GEOS SDK Documentation

This is the documentation for the GEOS Software Development Kit. It describes the GEOS API and software development toolset.

Welcome

If you're new to GEOS Programming, select this link for a list of introductory material.

Indexes

Index:
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X
Messages
A list of messages, organized by class. Classes include MetaClass, GenClass, and VisClass.

Topics

Select one of the topics below to see a list of links to chapters about that topic

Introduction
Information to new GEOS programmers or new Nokia 9000i Communicator programmers

C API Reference
Alphabetic listing of Routines and Structures with descriptions

Language Reference
Alphabetic listing of Goc and GP file keywords with descriptions

Nokia 9000/9000i Communicator
Topics specific to the Nokia 9000/9000i Communicator device

Communication
APIs that let the device communicate with the outside world

Data Management
High-level memory management APIs

Memory Management
Low-level APIs for managing memory

Files
Working with files and directories, File-related UI

Graphics
Graphics and drawing

Kernel
Functions and services provided by the GEOS kernel

Functions, object classes, and services provided by libraries

GEOS Programming
Differences between GEOS programming and plain C programming

UI Object Classes
Classes and mechanisms by which programmers may build application UI

System Object Classes
Low-level object classes providing basic system services

Tools
Compilation tools and utilities useful to GEOS programmers


Welcome to GEOS Programming

GEOS SDK Documentation: Welcome to GEOS Programming

| Up: GEOS SDK TechDocs

If you're new to the world of GEOS programming, the following documents will help you to find your way.

Installing the SDK
Installing the GEOS SDK, brief tour of the SDK, troubleshooting installation.
GEOS Programming Tutorial
Step-by-step creation of a simple application.
GEOS Architecture Overview
Summary descriptions of the pieces of GEOS.
Nokia 9000i Communicator UI
Descriptions of object classes to use when creating UI for Nokia 9000i Communicator applications.
Nokia 9000i Communicator Topics
Miscellaneous short topics which Nokia 9000i Communicator programmers should keep in mind.
"Hello World" Sample Application
Discussion of a sample application.
GEOS Programming Syntax
The syntax of Goc programming, everything you want to know about the GEOS object model and beyond.
UI Overview
Overview of the GEOS UI Classes along with discussion of the TicTac sample application.
| Up: GEOS SDK TechDocs


Hello World

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Application Structure
| One-page Version of this Chapter for Easy Printing

This chapter provides you with an application shell to which you can add as you learn more about GEOS programming. It describes each portion of the introductory (and compulsory) "Hello World" sample program. All programmers new to GEOS should read this chapter for a firm understanding of the basics of a GEOS application. The source code for Hello World is given in this chapter in pieces. It is also available as the Hello3 sample application.

1 Application Structure
2 Hello World
2.1 Features of Hello World
2.2 Strategy and Internals
2.3 Naming Conventions
3 Geode Parameters File
4 The Source File and Source Code
4.1 Inclusions and Global Variables
4.2 The Process Object
4.3 UI Objects
4.4 Code and Message Handlers
5 Exercises and Suggestions

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Application Structure
| One-page Version of this Chapter for Easy Printing


Application Structure

Hello World: 1 Application Structure

| Up: GEOS SDK TechDocs | Up | Prev: Hello World | Next: 2 Hello World

Every GEOS application and library will have at least two files: The Geode Parameters file provides the Glue linker with necessary information about the geode being compiled and linked. The source file contains the source code. Source code files may be split up into modules of source and definition files--this allows the programmer to keep large applications in a series of smaller files for easy organization and easier debugging.

Essentially there are four different types of files your application may use:

  • Parameters File ( .gp )
    The Parameters file, as stated above, provides the Glue linker with necessary information. It details specifics about the geode being compiled that will be necessary for dynamically linking at runtime. Each geode may have only one Parameters file. The Parameters file for Hello World is detailed in Geode Parameters File . Full reference information for this file type can be found in the Routines reference book.
  • Source File ( .goc )
    The Source file contains a combination of GEOS UI code and standard C code. Many source code files can be used for a single geode; this can help you organize your application's functionality into manageable groups of routines and objects. An introduction to the basics of the source file is given in The Source File and Source Code .
  • C Header File ( .h )
    C Header files may be used to hold definitions of data structures, classes, macros, routines, and other items. There are several standard GEOS header files you must include in your geode for it to compile properly. These are outlined in The Source File and Source Code .
  • GEOS Header File ( .goh )
    This file is essentially the same as the C header files described above. It can contain class and routine definitions as well as constants and variables. The primary difference between these files and the C header files is that .goh files must be included before the geode is run through the Goc preprocessor. C header files do not have to go through the Goc preprocessor. Simple geodes might have none of their own header files. These files are also described in The Source File and Source Code .
| Up: GEOS SDK TechDocs | Up | Prev: Hello World | Next: 2 Hello World


Hello World

Hello World: 2 Hello World

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Application Structure | Next: 3 Geode Parameters File

The Hello World sample application (Hello3) is very simple and yet accomplishes a great deal. With just a few simple steps, this program does the following:

  • Creates a complete primary window
  • Creates a scrollable window and scroller objects
  • Draws 48-point text into the scrollable window and redraws the text when the user changes a parameter
  • Handles scrolling and window resizes
  • Uses a pinnable menu to bring up a dialog box
  • Uses a dialog box to allow the user to change colors of the text
  • Uses keyboard mnemonics for both the menu and dialog box commands

The Hello World program uses some concepts and constructs with which you may not be familiar yet. This section will describe each of those constructs and will refer to other sections of the documentation for complete information.

Features of Hello World

Strategy and Internals

Naming Conventions

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Application Structure | Next: 3 Geode Parameters File


Features of Hello World

Hello World: 2.1 Hello World: Features of Hello World

| Up: GEOS SDK TechDocs | Up | Prev: 2 Hello World | Next: 2.2 Strategy and Internals

To the user, the Hello World program is very simple. It consists of a primary window with a single menu and a scrollable window within the primary. In the scrollable window is a phrase of text: "Welcome to GEOS!"

The menu has a single item, "Color," which brings up a dialog box with two buttons in it. One button is labeled "Blue" and turns the text blue, and the other is labeled "Gold" and turns the text yellow. The menu is pinnable, like all GEOS menus, and the dialog box may be moved around the screen and closed like any other independently-displayable dialog box.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Hello World | Next: 2.2 Strategy and Internals


Strategy and Internals

Hello World: 2.2 Hello World: Strategy and Internals

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Features of Hello World | Next: 2.3 Naming Conventions

The code for Hello World, as you will see, is quite simple. It consists mainly of User Interface gadgetry and uses just a few message handlers.

The main component of the application is the Process object, an instance of HelloProcessClass (a subclass of GenProcessClass ). This object makes all drawing and color changes by handling messages sent from the window manager and the triggers in the dialog box. The Process object basically manages the application, keeping track of the relevant data and interacting with the UI gadgetry.

The Process object is event-driven, meaning it has no main() routine run when the program is launched. Instead, the program does nothing until an event occurs (such as the view window sending MSG_META_EXPOSED when first opened). When the event (message) is received, the Process object responds to it and then waits until the next event occurs. Note, however, that the vast majority of events a Process object will receive and handle are not generated by your code but by the kernel and the UI.

The Menu and Dialog Box

Both the menu and the dialog box, once defined in the source code as objects, are implemented automatically by the system software. The application does not have to draw or otherwise manage these objects; they will interact directly with the UI to do the proper thing.

The triggers within the dialog, however, request actions that must be carried out by the application (changing the color to blue and gold, respectively). Although the application does not have to instantiate, draw, or otherwise modify the trigger objects, it must handle messages sent out by them when they are pressed by the user. This is discussed below, under "Changing the Text Color."

The Scrolling View and Drawing the Text

Almost everything is handled automatically by the User Interface for the Hello World application. This includes implementation of the system menus for the primary window and the scrolling functionality of the scrollable View window.

The view object (of GenViewClass ) is powerful and provides a lot of what most applications need. It automatically handles all window resizes and scrolls, and it will cause proper redrawing when another object (such as a pinned menu) is moved across it. The only thing it does not do is actually draw the application's images.

When the view senses that some portion of its window has become invalid (through scrolling or when the view window is first opened, for example), it will send a MSG_META_EXPOSED message to the Hello World application's Process object. The Process object will respond by drawing the text appropriately--it does not, however, have to worry about what portion of the text is visible or what portion of the screen the view window occupies. The view will automatically clip the text properly and display it within the window's bounds.

Changing the Text Color

In all, the Process object can handle six events specific to this application: MSG_META_CONTENT_VIEW_WIN_OPENED (sent by the view when it first creates its window), MSG_META_CONTENT_VIEW_WIN_CLOSED (sent by the view when its window is being destroyed), MSG_META_EXPOSED (described above), MSG_HELLO_CHANGE_TO_BLUE (sent by the Blue trigger), MSG_HELLO_CHANGE_TO_GOLD (sent by the Gold trigger), and MSG_HELLO_REDRAW_DOCUMENT (sent by the Process object to itself).

The Process object maintains two global variables: helloTextColor contains the current color of the text, and winHan contains the window handle of the view's window. When it draws the text, the Process object checks helloTextColor before drawing. Therefore, the handlers for the change-color messages change the value of helloTextColor .

However, changing the text's color is not quite that easy. Because the view window does not have any way of knowing that the Process object has changed the text, the application must inform the UI of the change. Otherwise, the change will be made in the document but will not appear on the screen until the view window is moved or resized by the user.

Therefore, we must force a window invalidation when we change the color. This will cause the View window to generate a new MSG_META_EXPOSED that will force the redrawing of the text in the new color. The window handle is cached in winHan for just this purpose; when the text color changes, we must invalidate the window so the UI will redraw its contents. We invalidate the window by calling the special graphics routine GrInvalRect() , passing the window's handle. We get the window handle when the view first creates the window--it will send out a MSG_META_CONTENT_VIEW_WIN_OPENED . When the window closes, the view will send MSG_META_CONTENT_VIEW_WIN_CLOSED in which Hello World destroys the cached window handle.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Features of Hello World | Next: 2.3 Naming Conventions


Naming Conventions

Hello World: 2.3 Hello World: Naming Conventions

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Strategy and Internals | Next: 3 Geode Parameters File

You might have noticed some of the names of variables, messages, and routines and seen a pattern of naming. Geoworks has developed a few naming conventions for various different items in the system. You don't have to follow these conventions, of course; it may help, however, if you keep to our conventions until you're familiar with most of the system. Some of the items that have special naming conventions are

Variables
Typically, variables begin with a lower case letter and have each subword in the name capitalized. The variable helloTextColor is a good example. Variables with just one word in the name are all lower case.
Classes
Class names should always have the suffix Class . Typically, the first portion of a class' name will describe the particular application, library, or module it is associated with. The second portion of the name should reflect the class' superclass. Each portion of the class name should be capitalized. Thus, HelloProcessClass conforms because it begins with the application's name (Hello), then has the superclass' name (Process), and finally has the suffix Class.
Constants
Constants are typically all upper case with underscores to delineate the portions of the name. C_BLUE is a good example; it is a set value that does not change. The structure of the name should reflect the use of the constant. (In this case, the enumerated type Color is reflected in the C_ portion of the constant's name.)
Routines
Kernel and UI routines, as well as method names, should have each portion capitalized. They should begin with some abbreviation relating to the module they belong to. For example, GrCreateState() is a graphics system routine, as is GrInvalRect() .
Messages
Message names should be all uppercase and begin with MSG_. Each portion of the name should be separated with underscores, and the first portion after MSG_ should reflect the class in which the message is defined. For example, MSG_HELLO_CHANGE_TO_BLUE follows all these conventions.
Objects
Object names are typically capitalized in the same manner as routines and classes. The object's name should reflect both the object's module and its class (or its function if not its class). HelloView is a good example.
Instance Data
Instance data fields typically have as their first portion an all-caps acronym for the class name with an I tacked on for "instance." The second portion is like a variable name, and the two portions are separated by an underscore. For example, GI_visMoniker is a field of GenClass (hence the GI_ ), and visMoniker is the variable name of the field.
| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Strategy and Internals | Next: 3 Geode Parameters File


Geode Parameters File

Hello World: 3 Geode Parameters File

| Up: GEOS SDK TechDocs | Up | Prev: 2 Hello World | Next: 4 The Source File and Source Code

The Hello World Parameters File shows the Geode Parameters ( hello3.gp ) file for the Hello World sample application. Each of the components of the parameters file is described in detail in the C Reference book.

Code Display 2-1 The Hello World Parameters File

##############################################################################
#
#	     Copyright (c) GeoWorks 1991, 1993-- All Rights Reserved
#
# PROJECT:		GEOS V2.0
# MODULE:		Hello World (Sample GEOS application)
# FILE:		hello3.gp (Hello World Application Geode Parameters File)
#
# DESCRIPTION:		This file contains Geode definitions for the "Hello World" sample
#		application. This file is read by the Glue linker to
#		build this application.
#
##############################################################################
#
#	Permanent name: This is required by Glue to set the permanent name
#	and extension of the geode. The permanent name of a library is what
#	goes in the imported library table of a client geode (along with the
#	protocol number). It is also what Swat uses to name the patient.
#
name hello3.app
#
#	Long filename: This name can be displayed by GeoManager.
#
longname "Hello World"
#
#	Specify geode type: This geode is an application, will have its own
#	process (thread), and is not multi-launchable.
#
type	appl, process, single
#
#	Specify the class name of the application Process object: Messages
#	sent to the application's Process object will be handled by
#	HelloProcessClass, which is defined in hello3.goc.
#
class	HelloProcessClass
#
#	Specify the application object: This is the object in the
#	application's generic UI tree which serves as the top-level
#	UI object for the application. See hello3.goc.
#
appobj	HelloApp
#
#	Token: This four-letter name is used by GeoManager to locate the
#	icon for this application in the token database. The tokenid
#	number corresponds to the manufacturer ID of the program's author
#	for uniqueness of the token. Eight is Geoworks' manufacturer ID for
# 	sample applications.
#
tokenchars "HELO"
tokenid 8
#
#	stack: This field designates the number of bytes to set aside for
#	the process' stack. (The type of the geode must be process, above.)
#	The default stack size is 2000 bytes.
#
stack 1500
#
# Heapspace: This is roughly the non-discardable memory usage (in words)
# of the application and any transient libraries that it depends on,
# plus an additional amount for thread activity. To find the heapspace
# for an application, use the Swat "heapspace" command.
#
heapspace 3644

#
#	Resources: List all resource blocks which are used by the application.
#	(Standard discardable code resources do not need to be mentioned.)
#
resource APPRESOURCE ui-object
resource INTERFACE ui-object
resource MENURESOURCE ui-object
#
#	Libraries: List which libraries are used by the application.
#
library	geos
library	ui
#
#	User Notes: This field allows the geode to fill its usernotes field
#	(available to the user through GeoManager's File/Get Info function)
#	with meaningful text.
#
usernotes "Sample application for GEOS version 2.0."
| Up: GEOS SDK TechDocs | Up | Prev: 2 Hello World | Next: 4 The Source File and Source Code


The Source File and Source Code

Hello World: 4 The Source File and Source Code

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Geode Parameters File | Next: 5 Exercises and Suggestions

The Hello World program's source code resides in a single file, hello3.goc . Portions of this file are presented throughout this section.

This sample application demonstrates the basics of all GEOS programs, and it's likely you will refer to it often until you become proficient with both the system and the reference documentation. Among the concepts and implementations it demonstrates are the following:

  • Declaration and use of resources
  • Standard inclusion files
  • Basic UI construction
  • Creation of the Primary window
  • Creation of a scrolling view and drawing to the view
  • Creation of a menu
  • Creation of a simple dialog box
  • Declaration of subclasses and messages
  • Definition of message handlers
  • Sending messages to another object

This discussion assumes you have a solid understanding of general programming concepts and C constructions. If you don't, you should most likely get to know the C programming language before continuing.

Inclusions and Global Variables

The Process Object

UI Objects

Code and Message Handlers

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Geode Parameters File | Next: 5 Exercises and Suggestions


Inclusions and Global Variables

Hello World: 4.1 The Source File and Source Code: Inclusions and Global Variables

| Up: GEOS SDK TechDocs | Up | Prev: 4 The Source File and Source Code | Next: 4.2 The Process Object

As Inclusion Files and Global Variables shows, the first thing in a .goc file is a list of other files and libraries that must be included. These are designated in the standard C protocol, with one exception (described below). All the inclusions and libraries a basic application will need are shown in the sample, and only the following inclusions is required of every GEOS application:

@include <stdapp.goh>

Other inclusions may be required for more complex applications and libraries, and these inclusions will be listed in the topics that require them. Goc accepts both the standard C #include for .h files and the GEOS @include for .goh files. The difference between them is that .h files may not include Goc constructs (e.g. @object ) whereas .goh files can.

After the inclusions are listed, you should declare any global variables used throughout your application. Be aware that global variables, though accessible by any object, are owned by the application's Process object and by the application's primary thread. Objects running in other threads (such as UI objects) should not access these global variables directly because this can cause synchronization problems between threads.

Whenever possible, you should avoid using too many global variables. Global variables are typically put in a fixed-block resource, and having too many can bog down a low-memory machine.

The Hello World application uses only two global variables. The first, helloTextColor , holds the value of the currently-displayed text color and is initialized to the value C_BLUE (dark blue). The second, winHan , contains the window handle of the scrollable view into which we draw our text. How these variables are used will be shown later.

Code Display 2-2 Inclusion Files and Global Variables

This is the first portion of the hello3.goc file.

/***********************************************************************
 *	     Copyright (c) GeoWorks 1991, 1993-- All Rights Reserved
 *
 * PROJECT:		GEOS
 * MODULE:		Hello World (Sample GEOS application)
 * FILE:		hello3.goc (Code file for Hello World Sample Application)
 *
 * DESCRIPTION:
 *	This file contains the source code for the Hello World application.
 *	This code will be processed by the goc C preprocessor and then
 *	compiled by a C compiler. After compilation, it will be linked
 *	by the Glue linker to produce a runnable .geo application file.
 *
 ***********************************************************************/
/***************************************************************************
 * Include files
 *	These files are the standard inclusion files to use the basics of
 *	the GEOS system and libraries. All applications should include
 *	at least the following files. Note that all inclusion files
 *	have the suffix .h or .goh indicating they are header files.
 ***************************************************************************/
@include <stdapp.goh>				/* Standard GEOS inclusion file */
/***********************************************************************
 * Global Variables
 ***********************************************************************/
word		helloTextColor = C_BLUE;
WindowHandle		winHan;
| Up: GEOS SDK TechDocs | Up | Prev: 4 The Source File and Source Code | Next: 4.2 The Process Object


The Process Object

Hello World: 4.2 The Source File and Source Code: The Process Object

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Inclusions and Global Variables | Next: 4.3 UI Objects

Every GEOS application has an object called the Process object. This object is run by the application's primary thread and is an instance of a subclass of GenProcessClass . Because the Process is an event-driven object, there is no main() routine that is executed when the program is launched. Instead, the object will wait until it receives messages (events), at which time it will execute the proper methods.

Applications can be of two basic models: The procedural model puts the entire functionality of the application within the Process object, not using any other objects in the application's thread (UI objects are run by the UI thread). All messages sent to the application are handled by the Process object, and most OOP issues can be avoided. The object-oriented model allows for other objects to be run by the application's primary thread; each of these objects will have its own instance data and be located in an object block (a resource) associated with its own message queue.

There is no command or directive that determines which model is used--the distinction is inherent within the format of message handlers and therefore can be virtually ignored. Hello World, for example, is simple enough to use the procedural model, handling all functionality with the Process object.

The Process object of Hello World is shown in Hello World's Process Object .

Code Display 2-3 Hello World's Process Object

This code display is part of hello3.goc and follows the previous display directly. */

/***********************************************************************
 *			Class & Method Definitions
 * This section contains the definition of the application's Process
 * class and its methods. Other classes can also be defined here,
 * along with the message each handles.
 ***********************************************************************/
/*
 * Here we define "HelloProcessClass" as a subclass of the system-provided
 * "GenProcessClass". As this application is launched, an instance of this class
 * will be created automatically to handle application-related events (messages).
 * The application thread will be responsible for running this object,
 * meaning that whenever this object handles a message, it will be executing
 * in the application's thread.
 */
/* You will find no object in this file declared to be of this class. Instead,
 * this class is bound to the application thread in hello3.gp.
@class HelloProcessClass, GenProcessClass;
/* The messages HelloProcessClass objects can handle that are not
 * system-defined are enumerated here. Each of these messages is sent
 * by one of the triggers in the dialog box. This is where class-
 * specific messages for this application (not system-defined messages)
 * are defined. */
@message void MSG_HELLO_CHANGE_TO_BLUE(); /* sent by Blue trigger */
@message void MSG_HELLO_CHANGE_TO_GOLD(); /* sent by Gold trigger */
@message void MSG_HELLO_REDRAW_DOCUMENT(); /* sent by Process to itself */
@endc /* signifies end of class definition */
/* Because this class definition must be stored in memory at runtime,
 * we must declare it here along with a flag indicating how it should
 * be loaded. The "neverSaved" flag is used because Process classes
 * are never saved to state files and therefore no relocation tables
 * need be built. */
@classdecl HelloProcessClass, neverSaved;
| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Inclusions and Global Variables | Next: 4.3 UI Objects


UI Objects

Hello World: 4.3 The Source File and Source Code: UI Objects

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 The Process Object | Next: 4.4 Code and Message Handlers

As stated earlier, the bulk of the Hello World application consists of User Interface objects. These objects are defined just after the Process object in your application's code, and they are given certain attributes and instance data. Once they have been defined, in general you will not have to bother with them again.

UI objects are arranged into resources. Each resource is allocated a block on the global heap when the program is launched; therefore, resources should be kept to reasonable sizes (2K-6K) whenever possible.

The application object resides within its own resource so the application takes up very little memory when iconified (minimized). Menus for complex applications are usually put in a menu resource. Most other UI gadgetry is put in a resource called "Interface" (though this name is not required).

The Application Object

Every application must have an application object, an instance of the class GenApplicationClass . The application object handles and manages many application-related things such as dispatching input sent by the input manager to the application. The application object must be the top-level generic object in every application. The name of the application object is also stated in the geode parameters file line appobj (see The Hello World Parameters File ).

Hello World's Application Object shows Hello World's application object definition. The comments in the code are extensive and explain the purpose of each line.

Code Display 2-4 Hello World's Application Object

This display is part of hello3.goc and follows the previous display directly.

/*
 *			Application Object
 * The very top-level generic object of an application MUST be a GenApplication
 * object. The hello3.gp file contains an "appobj" statement which indicates
 * that this "HelloApp" object is in fact the top-level UI object.
 * This object should be in its own resource so it takes up very little memory
 * when minimized. Note that the name of the resource may be whatever you choose;
 * it does not have to be AppResource.
 */
@start	AppResource;			/* Begin definition of objects in AppResource. */
@object GenApplicationClass HelloApp = {
    GI_comp = @HelloPrimary;
            /* The GI_comp attribute lists the generic children
             * of the object. The HelloApp object has just one
             * child, the primary window of the application. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = @HelloPrimary;
            /* This GCN list determines which of the application's
             * window objects must be made visible when the
             * application first starts up. */
}
@end	AppResource			/* End definition of objects in AppResource. */

The Primary Window and the View Window

Every application must have a primary window object of class GenPrimaryClass . This object will draw and manage the primary window and will work with the UI and the geometry manager to arrange all the children of the window properly. It will also automatically put up the system-controlled gadgets (such as the system window menu, the minimize/maximize buttons, and the Express menu).

The Hello World primary window has only two children, one of which is a menu. The other is the view object, which occupies the remaining space within the primary. The view object, as stated earlier, automatically handles all scrolling and clipping of documents. Its scrollable area is eight and a half inches by eleven inches. A description of what the view window does and how it interacts with the Process object to draw the text is given in The Scrolling View and Drawing the Text .

Hello World's Primary and View Objects shows the definition and attributes of each of these two objects. See the illustration above for a diagram of the two objects implemented under the OSF/Motif specification.

Code Display 2-5 Hello World's Primary and View Objects

This display is part of hello3.goc and directly follows the previous display.

@start	Interface;		/* This resource is for miscellaneous UI objects. */
@object GenPrimaryClass HelloPrimary = {
    GI_visMoniker = "Hello World Sample Application";
            /* This title will appear at the top of the primary
             * window as the name of the application. */
    GI_comp = @HelloView, @HelloMenu;
            /* This window has two children, the GenView object
             * and the GenInteraction menu object. */
    /* For simplicity, this application is not given an icon. Therefore, we must
     * prevent the user from being able to minimize the application. This is
     * done by applying the following attribute to the GenPrimary object. Note that
     * GenPrimaryClass is a subclass of GenDisplayClass, in which this attribute
     * is actually defined. */
    ATTR_GEN_DISPLAY_NOT_MINIMIZABLE;
    /* The following hint allows the primary window object to size itself
     * according to its children (the view object). */
    HINT_SIZE_WINDOW_AS_DESIRED;
   /*
    * When the specific UI permits, let's not show the menu bar on
    * startup. Some applications would find the extra space this leaves
    * helpful, in particular on the small screens of pen-based devices,
    * though for this simple application it doesn't really matter.
    */
    ATTR_GEN_DISPLAY_MENU_BAR_POPPED_OUT;
    HINT_DISPLAY_MENU_BAR_HIDDEN_ON_STARTUP;
}
/*		GenView object
 * This GenView object creates a window where the application can display portions
 * of a document as necessary. We want it to be scrollable, so the specific UI
 * (OSF/Motif) will create scroll bars which the user can interact with. Whenever a
 * portion of the window needs to be redrawn, the GenView object will invalidate
 * a portion of the window, causing a MSG_META_EXPOSED to be sent to the
 * application. The application will draw the document into the window. The
 * window keeps track of a mask which is used to clip the application's
 * drawing operations so only the invalid portion of the window is drawn. */
@object GenViewClass HelloView = {
    GVI_horizAttrs = @default | GVDA_SCROLLABLE | GVDA_NO_LARGER_THAN_CONTENT;
                /* This makes the View scrollable in the
                 * horizontal dimension and keeps it from
                 * growing larger than our document. */
    GVI_vertAttrs = @default | GVDA_SCROLLABLE | GVDA_NO_LARGER_THAN_CONTENT;
                /* This makes the View scrollable in the
                 * vertical dimension and keeps it from
                 * growing larger than our document. */
    GVI_docBounds = { 0, 0, 72*17/2, 72*11 };
                /* This sets the document size (scrollable
                 * bounds) of the GenView. */
    GVI_content = process;				/* This sets the output of the View--where it will
                 * send its MSG_META_EXPOSEDs--to be the
                 * application's Process object. */
 /*
  * This view will not take text input, so specify that no floating
  * keyboard should come up. Otherwise, we would get a floating
  * keyboard by default on pen-based systems.
  */
 ATTR_GEN_VIEW_DOES_NOT_ACCEPT_TEXT_INPUT;
}
@end	Interface		/* End definition of objects in this resource. */

The Hello World Menu

The Hello World program has one menu, called "Menu", located in the primary window's menu bar. Menus are instances of GenInteractionClass with the GIV_POPUP attribute set in the GII_visibility field. The moniker of the menu object appears on the menu bar (see The Hello World Menu for the definition of Hello World's menu).

The menu should have one child for each of its entries. In Hello World, the only child is the dialog box, whose moniker appears as the text of the menu item that brings up the box.

Code Display 2-6 The Hello World Menu

/This display is part of hello3.goc and follows the previous display directly.

/*			HelloMenu Menu
 * Menus are of GenInteractionClass. The moniker of a menu is used to show the menu
 * on the primary's menu bar (thus, "Menu" will show up in Hello World's menu bar).
 * Each of the menu's children (in the GI_comp field) will be an entry or a
 * collection of entries in the menu. To separate a menu from a dialog
 * box GenInteraction, you must apply the visibility GIV_POPUP. A dialog box will
 * have the visibility GIV_DIALOG. */
@start MenuResource;
@object GenInteractionClass HelloMenu = {
    GI_visMoniker = 'M', "Menu";				/* The moniker of the menu is used in
                 * the primary window's menu bar. */
    GI_comp = @HelloColorBox;				/* The only child of the menu (the only
                 * item in the menu) is the dialog box. */
    GII_visibility = GIV_POPUP;				/* This attribute designates the GenInteraction
                 * as a menu or a submenu. */
}
@end MenuResource;

The Dialog Box and Its Triggers

The Hello World Dialog Box and Its Triggers shows the code for the dialog box and its triggers.

Dialog boxes in GEOS may be of GenInteractionClass , or dialogs may be called up and instantiated during execution with kernel routines. Hello World uses a GenInteraction in order to have a "floating" dialog box that may be retained and moved around the screen.

When a dialog box is brought up by a menu item, the moniker of the dialog box object will be used as the text of the menu item. Thus, the word "Color" will appear both at the top of the dialog box and in the menu.

Children of a dialog box are arranged by the geometry manager. In general, children will be arranged top to bottom (or left to right, depending on the specific UI) as they are defined. Thus, the two triggers will appear "Blue" first and "Gold" second (on bottom). The dialog box will automatically size itself to fit all its children properly. Dialog boxes, unlike windows, are generally not resizable.

Each of the triggers will appear as a simple button big enough to hold its moniker. Each trigger, when pressed, sends a specified message to a specified object. The Blue trigger sends MSG_HELLO_CHANGE_TO_BLUE to the application's Process object, and the Gold trigger sends the message MSG_HELLO_CHANGE_TO_GOLD to the Process object. These messages are placed in the Process object's message queue and require no return values.

Code Display 2-7 The Hello World Dialog Box and Its Triggers

This display is part of hello3.goc and follows the previous display directly.

/*			HelloColorBox Dialog Box
 * Dialog boxes are of GenInteractionClass. The moniker of the dialog box will
 * appear at the top as the box's title. Additionally, if the GenInteraction
 * is a child of a menu interaction, the moniker will also serve as the text that
 * appears in the menu item that brings up the dialog box. The dialog will
 * automatically size itself to be large enough to hold all its children. Thus,
 * this dialog will be small (with just two children, each a trigger with a short
 * moniker). */
@object GenInteractionClass HelloColorBox = {
    GI_visMoniker = 'C', "Color";				/* The moniker will be displayed both as the
                 * dialog's title and as the menu item that
                 * brings the dialog up. */
    GI_comp = @HelloBlueTrigger, @HelloGoldTrigger;
                /* The two triggers are the only gadgets
                 * in the dialog box. */
    GII_visibility = GIV_DIALOG;				/* In order for this interaction to be
                 * a dialog box, this attribute must be set. */
}
/*			GenTriggers
 * Buttons are implemented by GenTriggerClass. When a trigger is pushed by the user
 * (clicked on with the mouse), it will send the specified message to the specified
 * output object. In both cases below, the trigger will send an application-defined
 * message to the application's Process object. A trigger's moniker is displayed
 * within the trigger. In this case, both are text, but any graphics could be used
 * instead to create graphical triggers easily. (E.g. a blue flag and a gold flag
 * rather than the words "Blue" and "Gold.") */
@object GenTriggerClass HelloBlueTrigger = {
    GI_visMoniker = 'B', "Blue";				/* The 'B' indicates the keyboard navigation
                 * character for this trigger. */
    GTI_destination = process;				/* Send the message to the Process object. */
    GTI_actionMsg = MSG_HELLO_CHANGE_TO_BLUE;	/* Send this message. */
}
@object GenTriggerClass HelloGoldTrigger = {
    GI_visMoniker = 'G', "Gold";				/* The 'G' indicates the keyboard navigation
                 * character for this trigger. */
    GTI_destination = process;				/* Send the message to the Process object. */
    GTI_actionMsg = MSG_HELLO_CHANGE_TO_GOLD;	/* Send this message. */
}
| Up: GEOS SDK TechDocs | Up | Prev: 4.2 The Process Object | Next: 4.4 Code and Message Handlers


Code and Message Handlers

Hello World: 4.4 The Source File and Source Code: Code and Message Handlers

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 UI Objects | Next: 5 Exercises and Suggestions

One of the first things a C programmer might notice when looking at the Hello World program is that it has no main() routine. This illustrates the primary distinction between an object-oriented system and a system that emulates object-orientedness.

Applications in GEOS consist of a Process object and, optionally, other objects in the same or different threads. The Process object of an application is event-driven . This means that until it receives a message, it does nothing; when it receives a message, however, it will automatically be woken up with the instruction pointer pointing at the proper routine's entry point.

GenProcessClass , the superclass of every Process object, handles many messages that most applications may never need to know about. For example, when the program is first launched, the Process object will receive a series of messages from the UI and the kernel telling it how it should start up. It automatically responds by setting up the proper message queues and executing the proper code. These are things you, as the programmer, do not need to know about to create a working GEOS application (though they are documented in the Geodes chapter).

Throughout the program's life, then, the Process object will receive and respond to messages as they are received. Each message has at most one corresponding method; if no method exists for a message, the message is ignored.

The Hello World Process object can handle six different messages, each of which is sent by a UI object. It also uses one routine defined internally and not available for use by other objects. Of the six messages it handles, three are specific to Hello World and three are universal to all applications using a GenView object: MSG_META_EXPOSED , MSG_META_CONTENT_VIEW_WIN_OPENED , and MSG_META_CONTENT_VIEW_WIN_CLOSED are sent by the view, and MSG_HELLO_CHANGE_TO_BLUE , MSG_HELLO_CHANGE_TO_GOLD , and MSG_HELLO_REDRAW_DOCUMENT are defined specific to HelloProcessClass and are sent by the triggers.

The function HelloDrawText() is internal to Hello World and is called by the MSG_META_EXPOSED handler. It is declared before the handler to satisfy normal C constraints.

Additionally, two constants are defined to determine the document size. These constants, along with the declaration of HelloDrawText() , are shown in Constant and Routine Definition .

Code Display 2-8 Constant and Routine Definition

This display is part of hello3.goc and follows the previous display directly.

/*************************************************************************
 *			Code for HelloProcessClass
 * Now that all the UI gadgetry has been defined, we must provide the
 * methods and routines used by the application. For simplicity, all
 * messages will be handled by the HelloProcessClass object.
 *************************************************************************/
/* Define constants used by the color-setting methods. Each of these
 * is a document size parameter in points. Therefore, the document is
 * 8.5 inches wide by 11 inches tall (one point is 1/72 of an inch). */
#define HORIZ_DOC_SIZE			(72*17/2)
#define VERT_DOC_SIZE			(72*11)
/* Declare that we will use the function HelloDrawText(), and define its
 * return and parameter values. It has no return value and has one parameter:
 * a graphics state handle called "gstate." */
void HelloDrawText(GStateHandle gstate);
/* The following constants are used by HelloDrawText(). */
#define TEXT_POINT_SIZE			 48	/* point size */
#define TEXT_X_POSITION			 30	/* x position, in document coords. */
#define TEXT_Y_POSITION			100	/* y position, in document coords. */

Handling the Window Messages

As stated earlier, the winHan global variable contains the window handle of the view's window. To set the variable, Hello World must intercept and handle the message MSG_META_CONTENT_VIEW_WIN_OPENED . This message passes the window handle, which HelloProcessClass simply stuffs into its winHan variable.

When the view window is destroyed, the application must make sure it forgets its window handle. Otherwise, we could try to draw to a nonexistent window, which is an error. This will not be a problem for Hello World because the only time the view can be destroyed is when the application is being shut down. For completeness, however, Hello World handles MSG_META_CONTENT_VIEW_WIN_CLOSED and sets winHan to zero.

Both of the methods for the above messages are shown in Messages from the View .

Code Display 2-9 Messages from the View

This display is part of hello3.goc and follows the previous display directly.

/* NOTE:
 * Because these are simple methods, the structure and syntax of methods are not
 * handled here. See the handler for MSG_META_EXPOSED, later in this chapter. */
/***********************************************************************
 * MSG_META_CONTENT_VIEW_WIN_OPENED for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Record the handle of the view window when the view
 *		creates it. This allows us to more-easily update the
 *		document when the user changes the text color.
 * PARAMETERS:		void (	word viewWidth,
 *			word viewHeight,
 *			WindowHandle viewWin)
 * SIDE EFFECTS: winHan is set to viewWindow
 ***********************************************************************/
@method HelloProcessClass, MSG_META_CONTENT_VIEW_WIN_OPENED {
    /* Get the window handle of the View. We need this handle in order to
     * force a window invalidation, causing the View to send a MSG_META_EXPOSED
     * to the Process object and thereby forcing a redraw of the window. */
    winHan = viewWindow;
}
/***********************************************************************
 * MSG_META_CONTENT_VIEW_WIN_CLOSED for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Take note that the view is now closed, so we don't
 *		try and draw to it or invalidate it any more (at
 *		least until it reopens)
 * PARAMETERS:		void (WindowHandle viewWindow)
 * SIDE EFFECTS:		winHan is set to 0
 *
 ***********************************************************************/
@method HelloProcessClass, MSG_META_CONTENT_VIEW_WIN_CLOSED {
    /* Set our window handle variable to zero. */
    winHan = 0;
}

Handling MSG_META_EXPOSED

As discussed in our HelloView object declaration (see Hello World's Primary and View Objects ), the view will send a MSG_META_EXPOSED to the Hello World Process object. Receipt of this message indicates that part of the scrollable view window has become invalid and must be redrawn.

Therefore, the class of the Hello World Process object ( HelloProcessClass ) must know how to draw the document in response to this message. Note that this message did not have to be defined specifically in the earlier definition of HelloProcessClass --this is because the message is already defined for MetaClass , the superclass of all GEOS classes.

MSG_META_EXPOSED Handler shows the method that handles MSG_META_EXPOSED for HelloProcessClass . Notice that the method calls the HelloDrawText() routine rather than drawing the text directly. While this may appear inefficient at first (and is for such a small, simple application), there are two main reasons why this is done:

First, it takes advantage of the GEOS single imaging model. The method simply creates the proper GState for drawing to the view window, then calls the drawing routine. A similar message for printing (i.e. when the user clicks on a "Print" trigger, a print message may be sent to the Process object) can use the same drawing routine--its handler would simply set up a GState for drawing to a Spool file and then call the drawing routine. Thus, one function is used for two purposes, cutting down code size.

Second, it allows more modularity in the testing of your code. If you need to make sure, for example, that the message is being received and handled, but you don't (yet) care if the drawing is done properly, you can set up HelloDrawText() as a dummy function. This would allow you to ensure the message is handled properly without having to debug all the drawing code.

Code Display 2-10 MSG_META_EXPOSED Handler

This display is part of hello3.goc and follows the previous display directly.

/***********************************************************************
 * MSG_META_EXPOSED for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Redraw the recently-exposed portion of the View
 * PARAMETERS:		void (WindowHandle win)
 * SIDE EFFECTS:		The invalid region of the window is cleared out
 *
 * STRATEGY:		This message is sent by the windowing system when a
 *		portion of the GenView has become invalid, either
 *		because a window that was obscuring it has been moved,
 *		or because some called GrInvalRect.
 *
 *		We redraw the entire document, after telling the
 *		graphics system we're drawing to the invalid portion
 *		of the window.
 *
 ***********************************************************************/
/* The method is declared with the goc keyword @method. This is followed by
 * the name of the class that knows how to handle the message (in this case,
 * the class is HelloProcessClass). Finally, the name of the message that
 * invokes this method is specified. Other items may also be specified (such
 * as a routine name that can be used instead of a message), but these are not
 * required. */
@method HelloProcessClass, MSG_META_EXPOSED {
    /* The local variable gstate will hold a GState handle. We will do
     * our drawing to this gstate. */
    GStateHandle gstate;
    /* Get a new, default graphics state that we can use while drawing.
     * We must allocate a new graphics state for the View window using
     * the kernel routine GrCreateState(). We pass the window handle of
     * the View window, which we received in a parameter called "win". */
    gstate = GrCreateState(win);
    /* Next, start a window update. This tells the windowing system that
     * we are in the process of drawing to this window. This is very
     * important--it ensures the window will be in a consistent state while
     * we're drawing. Specifically, it locks in the invalidated region to
     * which we'll be drawing; this makes sure that other threads drawing
     * to this window will not have any effect on our GState. A window
     * update is started by calling the kernel routine GrBeginUpdate()
     * with the GState handle. */
    GrBeginUpdate(gstate);
    /* If we had background graphics to draw, we could call the appropriate
     * graphics routines now. But we don't. */
    /* Draw our text into the window (pass the GState). This is done here
     * by calling the function HelloDrawText(), which knows how to draw
     * the appropriate document. (See below.) */
    HelloDrawText(gstate);				/* Special Hello World routine (below). */
    /* Now end the window update (unlock the GState and its window)
     * with the routine GrEndUpdate(), and free the GState handle by calling
     * the kernel routine GrDestroyState(). */
    GrEndUpdate(gstate);				/* Signal that we are done with
                 * the window update. */
    GrDestroyState(gstate);				/* Destroy the temporary GState. */
}
/***********************************************************************
 * MSG_HELLO_REDRAW_DOCUMENT for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Force the document to be redrawn by marking the
 *		entire document area in the view as invalid.
 * PARAMETERS:		void (void)
 * SIDE EFFECTS:		Any drawing to the document area between this
 *		message and the MSG_META_EXPOSED that it generates
 *		will not show up, as the entire window will be invalid.
 *
 ***********************************************************************/
@method HelloProcessClass, MSG_HELLO_REDRAW_DOCUMENT
{
    /* Now create a temporary GState to use for window invalidation
     * if the window handle is valid, then redraw the window. */
    if (winHan != 0) {
    GStateHandle gstate = GrCreateState(winHan);
    /* Call GrInvalRect using the GState. Invalidate the entire
     * document. This will cause the View to redraw itself and send
     * a MSG_META_EXPOSED to the Process object. */
    GrInvalRect(gstate, 0, 0, HORIZ_DOC_SIZE, VERT_DOC_SIZE);
    GrDestroyState(gstate);				/* Free the GState. */
    }
}
/***********************************************************************
 * HelloDrawText
 ***********************************************************************
 * SYNOPSIS:		Draw a single line of text onto the document. Note
 *		that it has no concept of the screen or the view --
 *		it is given a graphics state and draws through it.
 * CALLED BY:		(INTERNAL) HelloProcess::MSG_META_EXPOSED
 * RETURN:		nothing
 * SIDE EFFECTS:		attributes in the gstate are altered
 *
 * STRATEGY:		We separate the drawing from the exposure handler
 *		so the same routine can be used for both window
 *		refresh and for printing.
 *
 *		Set the font, point size and color, then draw the text.
 *
 ***********************************************************************/
/* Functions are declared as they would be in C. Parameters are defined using
 * the ANSI calling convention: The type of the parameter is given, followed
 * by the parameter name. Multiple parameters are separated by commas. This
 * function has a single parameter. */
void HelloDrawText(GStateHandle gstate) {
    /* First change some of the default GState values such as the font
     * and point size. This is done with the routine GrSetFont(). */
    GrSetFont(gstate, FID_DTC_URW_ROMAN, MakeWWFixed(TEXT_POINT_SIZE));
    /* Set the text color to the value in helloTextColor by calling the
     * graphics routine GrSetTextColor(). */
    GrSetTextColor(gstate, CF_INDEX, helloTextColor, 0, 0);
    /* Draw the text onto the document by using the GrDrawText() routine. */
    GrDrawText(gstate, TEXT_X_POSITION, TEXT_Y_POSITION,
            "Welcome to GEOS!", 0);
}

Handling Messages from the Triggers

When the user clicks on one of the two triggers in the Color dialog box, the pressed trigger sends off a message to the Hello World Process object. The Blue trigger sends MSG_HELLO_CHANGE_TO_BLUE , and the Gold trigger sends MSG_HELLO_CHANGE_TO_GOLD . The Process object must be able to handle both of these messages.

The methods that handle these messages are similar. Each sets the global variable helloTextColor , and each forces the view window to redraw itself (by sending MSG_HELLO_REDRAW_DOCUMENT to the Process) so the color is changed on the screen as well as in our variable. Handlers for MSG_HELLO_ shows the code and comments of both these methods.

Code Display 2-11 Handlers for MSG_HELLO_...

This display is part of hello3.goc and follows the previous display directly.

/***********************************************************************
 * MSG_HELLO_CHANGE_TO_BLUE for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Set the text color to dark blue and redraw the text.
 * PARAMETERS:		void (void)
 * SIDE EFFECTS:		helloTextColor is set to C_BLUE
 *
 ***********************************************************************/
@method HelloProcessClass, MSG_HELLO_CHANGE_TO_BLUE {
    helloTextColor = C_BLUE;				/* Set the helloTextColor variable to blue. */
    @call self::MSG_HELLO_REDRAW_DOCUMENT();
}
/***********************************************************************
 * MSG_HELLO_CHANGE_TO_GOLD for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Set the text color to yellow and redraw the text.
 * PARAMETERS:		void (void)
 * SIDE EFFECTS:		helloTextColor is set to C_YELLOW
 *
 ***********************************************************************/
@method HelloProcessClass, MSG_HELLO_CHANGE_TO_GOLD
{
    helloTextColor = C_YELLOW; /* Set the helloTextColor variable to gold. */
    @call self::MSG_HELLO_REDRAW_DOCUMENT();
}
| Up: GEOS SDK TechDocs | Up | Prev: 4.3 UI Objects | Next: 5 Exercises and Suggestions


Exercises and Suggestions

Hello World: 5 Exercises and Suggestions

| Up: GEOS SDK TechDocs | Up | Prev: 4 The Source File and Source Code

After studying the Hello World sample application and reading the System Architecture chapter, you may be ready to try some exercises before embarking on your application. It is strongly suggested that you spend some time modifying the Hello World program to get used to the programming environment and the Goc UI definition syntax. Some things you might try are outlined below--some are extremely easy; some are more difficult. Some will require you to read other chapters in this and the User Interface manual. In any case, you should go on to read the GEOS Programming chapter to understand the various keywords and constructs of the Goc programming language.

Some simple UI exercises you can incorporate into the Hello World program include the following suggestions:

  • Add a new menu item that brings up a dialog box letting the user select a point size for the text. In the dialog box you would use a GenValue object or perhaps a list of point sizes (or even one or more triggers) to take the user's input. The GenValue sends out a message with a new number when the user sets it, and your Process object would handle this message and set the point size. Your HelloDrawText() routine would then have to set the point size of the text as well as the color.
  • Add a feature that lets the user set the position and/or rotation of the text. This exercise will help you get used to the GEOS coordinate space.
  • Make HelloView scalable. To do this, you will create a new menu or dialog box that lets the user specify the scale. Most likely you will create a number of GenTriggers that apply a pre-specified scale; each of these triggers will send MSG_GEN_VIEW_SET_SCALE_FACTOR to HelloView. You may even try using a GenViewControlClass object.
  • Add background graphics to get used to the coordinate system. Perhaps draw a colored rectangle behind the text that changes colors to be the inverse of the text (i.e. when the text turns blue, the rectangle turns yellow).
  • Add printing. This is not nearly as difficult as you might think. To do this, you should read the Printing chapter. This exercise essentially consists of adding a trigger to invoke printing and a message handler for the printing message. This handler will create a printing GState and call the drawing routine. Instead of drawing to the screen, it draws to the Spooler's input.

Some of the exercises may have been implemented in the sample programs provided. You should look through the samples for insight into how the exercises may be completed. You are also encouraged to make up your own exercises to build on Hello World.

If you are new to GEOS programming, you should work through the Tutorial; otherwise, you will likely be overwhelmed by the complexity and depth of the system and not know where to begin.

| Up: GEOS SDK TechDocs | Up | Prev: 4 The Source File and Source Code


Installing the SDK

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 System Requirements
| One-page Version of this Chapter for Easy Printing

Welcome to GEOS development. This document guides you through the CD-ROM installation procedure; it also describes your environment configuration, shows how to set up multiple NT machines, and lists the most common setup difficulties in a short troubleshooting guide.

If you are familiar with GEOS development, please review the SDK changes described in Compiling GEOS Applications , and Directory Structure , before you begin compiling and debugging geodes.

You can find complete information on the tools mentioned here (Swat, pmake, etc.) in the Tools book.

1 System Requirements
2 Installation and Setup
2.1 Installation Details
2.2 NT Configuration
2.3 Environment Configuration
2.4 Registry Configuration
3 Launching NT GEOS and Swat
4 Compiling GEOS Applications
5 Directory Structure
5.1 Branches
5.2 Main Directories
5.3 Target Directories
5.4 Developing In Your Working Branch
5.5 Multi-Developer Projects
6 Troubleshooting

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 System Requirements
| One-page Version of this Chapter for Easy Printing


System Requirements

Installing the SDK: 1 System Requirements

| Up: GEOS SDK TechDocs | Up | Prev: Installing the SDK | Next: 2 Installation and Setup

The GEOS SDK runs on a Windows NT workstation (version 3.51 or later). The SDK also requires a perl interpreter (a shareware copy is provided on the CD-ROM) and the Borland C/C++ compiler, version 4.0 or later. (We recommend versions 4.X over 5.0; see Compiling GEOS Applications .)

| Up: GEOS SDK TechDocs | Up | Prev: Installing the SDK | Next: 2 Installation and Setup


Installation and Setup

Installing the SDK: 2 Installation and Setup

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 System Requirements | Next: 3 Launching NT GEOS and Swat

You can install the GEOS NT SDK as either a standalone SDK for just one computer or as a server/client SDK on a network drive. The former is suited for single developers while the latter allows multiple developers to work on different aspects of the same project simultaneously. In either case, the steps for installation are similar. In summary,

  • Install the Borland C/C++ compiler (v4.5 recommended).
  • Run setup from the GEOS NT SDK CD-ROM.
    • Specify the type of installation: Server, or Client, or Standalone.
  • Install perl (if not already present) by clicking on the "Perl Install" icon in the GEOS SDK Program Group.

Some additional steps are required for server-client installations.

  • Run the installer, specifying the "Server" option.
  • Set the network directory for the network (shared) branch of the SDK.
  • On each NT development machine, run the NT GEOS SDK installer with a "Client" install.
    • Specify where the network (shared) SDK branch resides.
The following sections explain each of these steps in greater detail and show how the SDK modifies the system environment.

Installation Details

NT Configuration

Environment Configuration

Registry Configuration

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 System Requirements | Next: 3 Launching NT GEOS and Swat


Installation Details

Installing the SDK: 2.1 Installation and Setup: Installation Details

| Up: GEOS SDK TechDocs | Up | Prev: 2 Installation and Setup | Next: 2.2 NT Configuration

Steps for installing the GEOS NT SDK:

Install the Borland C/C++ compiler (v4.5 recommended).
Installing the Borland C++ compiler prior to the GEOS NT SDK allows the SDK installer to automatically set the environment variable pointing to the directory where it finds the compiler. Be sure to install the 32-bit Windows compiler; it is part of the "typical" installation.
Run setup from the GEOS NT SDK CD-ROM.
Install the GEOS NT SDK by launching setup.exe, found in the CD-ROM's topmost directory. The installer will prompt you to choose from one of three installation options: Server, Client, or Standalone.

A server installation installs the shared components of the SDK on a network server so that it accessed from multiple client workstations.

A client installation:

  • creates a local (client) working directory.
  • sets your environment variables and updates your NT configuration.
  • sets defaults in the variable registry.

A standalone installation:

  • installs both the client and server portions on one machine.
  • creates a private demo (target) tree.
  • sets your environment variables and updates your NT configuration.
  • sets defaults in the variable registry.

Note that the root for the client and server portions must be the same. For example, if the server SDK is in f:/prog/sdks/pcgeos, then the local working directory must look like c:/pcgeos/jdoe, where the two italicized components match. This applies to both standalone and client/server development. Typically, this root directory is "pcgeos", e.g. S:\PCGEOS. (For more information, see Directory Structure.)

Install perl.
If you do not have a perl interpreter, install the shareware copy provided by running pcgeos\bin\Pw32i302.exe or by clicking the "Perl Install" GEOS SDK Program Group icon. Leave checked the box to run install.bat after unzipping; reply "y" (the default) to the install options.
Verify that environment and registry variables have been properly set.
The modified environment variables are listed in Environment Configuration. You can modify registry variables with GeosSetup.exe, as described in Registry Configuration.
| Up: GEOS SDK TechDocs | Up | Prev: 2 Installation and Setup | Next: 2.2 NT Configuration


NT Configuration

Installing the SDK: 2.2 Installation and Setup: NT Configuration

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Installation Details | Next: 2.3 Environment Configuration

The installer completes the following for you automatically; they are included here for reference only.

  • Task Priorities
    Foreground and background task are set to run at the same priority. This is needed for Swat to run properly. This setting is found in the System module of the NT Control Panel.
  • Open File Count
    files = 100 (or more) specified in %SystemRoot%\system32\config.nt .
  • File System Driver
    fs = os2{ec}.geo specified under the [system] category in your demo's .ini file.
| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Installation Details | Next: 2.3 Environment Configuration


Environment Configuration

Installing the SDK: 2.3 Installation and Setup: Environment Configuration

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 NT Configuration | Next: 2.4 Registry Configuration

The compiling and linking tools use the following environment variables to find executables and include files. These variables are set from the system module of the control panel, not an autoexec file.

setup sets these environment variables for you; they are included here for reference only.

ROOT_DIR
The root of the SDK installation, typically C:\PCGEOS (local development) or S:\PCGEOS (network development, substituting your particular network drive for S: ).
GOC_COMPILER_DIR
The root of the compiler directory, such as C:\BC45.
PATH
Your PATH must include entries for the bin\ directories of the Borland C++ compiler, PCGEOS, and your copy of perl. (More information on Borland C++ and perl is found in Compiling GEOS Applications .)
CCOM
Alternate compilation command for pmake . This must be set to @dosfront bcc for versions of the Borland compiler prior to 5.0.
| Up: GEOS SDK TechDocs | Up | Prev: 2.2 NT Configuration | Next: 2.4 Registry Configuration


Registry Configuration

Installing the SDK: 2.4 Installation and Setup: Registry Configuration

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Environment Configuration | Next: 3 Launching NT GEOS and Swat

To check your registry settings, run GeosSetup.exe from the GEOS SDK Program Group. (Note: do not confuse GeosSetup.exe for setup.exe, which is the installer program.)

Swat (here and on its command line) prefers forward slashes in directory paths; backslashes are sometimes inadvertently interpreted as special characters (\tools as <TAB> ools, for instance).

SDK Root
C:/PCGEOS (local) or S:/PCGEOS (network).
Can be any path, but the last element is typically PCGEOS. This is the directory where you installed the SDK.
Local Root
S:/PCGEOS/username , for example.
Where Swat stores .symdir files; also the head of its search for an engineer's own .sym files. For pmake to run properly the penultimate element of Local Root must have the same name as the last element of SDK Root . (It need not be the actual SDK Root directory, however.) The last element may be named anything, but user names are typically used.
Branch
N9000V20
 or other platform as appropriate. Leave blank if your development tree does not have platform branches.
Object Source
Installed
 Directory under SDKRoot/Branch heading the search for shared or SDK-supplied .sym files.
Tcl Directory
Tools/swat/lib.new
 Location of Swat's .tcl files under SDK Root .
Tcl Override Directory
Location of additional .tcl files; typically blank.
Swat Init file
Any init file you would like Swat to execute upon startup. Typically blank or C:/PCGEOS/SWAT.RC .
Fast Video
Checked
 NT GEOS video mode selector. Fast Video artificially reduces the number of screen updates used by NT GEOS; therefore, you will see less flicker and faster execution response than on the actual device. Disable this option if you are testing animations or assessing relative flicker rates.

The GEOS Setup window also includes a selector for the communication mode. Choose Local (NT native) to instruct Swat to attach to your NT GEOS window, or choose Remote (Serial) to instruct Swat to attach to an external device, with appropriate Speed and Port settings.

You may store and recall a number of different setups by using the Current Setup selector. The Setup names have no intrinsic meaning.

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Environment Configuration | Next: 3 Launching NT GEOS and Swat


Launching NT GEOS and Swat

Installing the SDK: 3 Launching NT GEOS and Swat

| Up: GEOS SDK TechDocs | Up | Prev: 2 Installation and Setup | Next: 4 Compiling GEOS Applications

You will find example shortcuts for launching the NT GEOS demo in the GEOS SDK Program Group. You can define additional shortcuts to launch NT GEOS by right-clicking on the root window (NT 4.0) or selecting the menu item File -> New on the Program Manager (NT 3.51). Specify a command such as
c:\pcgeos\target\n9000.nc\swat.exe /s loader.exe (modified as desired); you must also specify a working directory (under target\ ) corresponding to the simulation you wish this icon to run. For command examples, refer to the batch files described below.

To launch a target device window from the command line, change-directory to your target area (for example, \pcgeos\target\n9000.ec\ ) and type start s at the prompt. ( s.bat is supplied in the target directory templates, and start tells NT to run the command in a new window.) s.bat is a batch file which executes the Swat stub; ss.bat forces the demo to wait for a host Swat debugger to attach. Execute start s (or ss ) in your non-EC directory to run the non-EC version of the Swat stub. To run the non-Swat demo, use start geos , which executes loader{ec}.exe without the Swat stub.

In summary, each of these demos is available in both the EC and non-EC target directories:

ss.bat		Demo with Swat stub, wait for host Swat
s.bat		Demo with Swat stub, do not wait
geos.bat		Demo without Swat

Launch the host Swat debugger from an icon in the GEOS SDK Program Group or from the command line with swat (or start swat ). (Be careful not to run a Swat stub if your current directory is a target directory; you want to run swat.exe from pcgeos\bin\ .) Debugging with Swat in the NT environment is the same as that in the old DOS one; use ^ C and c to interrupt and continue execution, att and det to attach and detach, and so forth.

The Swat window size may be increased by modifying the window-size property of your command windows in general or your Swat shortcut in particular. Any change made to a window after Swat is launched will not be recognized.

You can instruct Swat to attach to an external Communicator device or PC emulator by running GEOS Setup prior to launching Swat and selecting the serial Communication Mode and an appropriate Comm port .

You can stop NT GEOS by entering detach or quit when Swat is attached. Otherwise, you can stop the demo by clicking the kill (X) button on the right of the titlebar, double-clicking the top-left titlebar menu, or using NT's task manager.

.swat: Automating Swat

If you have a file called . swat in the root of your private directory and run the GEOS Setup program to change the "Swat Init File" field to point to that file, Swat will execute Swat commands in that directory when it starts up. Below is a short example of a .swat file:

srcwin 15
sd hello

The srcwin command sets up a source-code window at the bottom of the screen. The sd command changes the symbolic default to be the "hello" thread. Any time Swat does not recognize a name in the current thread, it will see if the name has meaning in the hello thread.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Installation and Setup | Next: 4 Compiling GEOS Applications


Compiling GEOS Applications

Installing the SDK: 4 Compiling GEOS Applications

| Up: GEOS SDK TechDocs | Up | Prev: 3 Launching NT GEOS and Swat | Next: 5 Directory Structure

To compile GEOS C code, you need the following third-party software installed on your system:

  • Borland C/C++ Compiler
  • Perl Interpreter

Your PATH variable must include the Borland compiler's bin directory and the directory containing the perl interpreter. If you installed Borland C++ after the NT SDK or if you set up a network environment, you may need to set the environment variable GOC_COMPILER_DIR to reflect the compiler's location. Refer to Environment Configuration .

Borland C++ is a separate product on its own CD-ROM. We strongly recommend that you install version 4.5 of the compiler, included with most distributions of version 5.0. A change in the compiler's handling of #pragma options creates errors (not reported at compile time) in geodes compiled with version 5.0. You must install the 32-bit Windows compiler, a part of the "typical" installation.

A shareware version of the perl installer has been included with the SDK; refer to Installation and Setup for installation instructions.

All of the tools needed to compile geodes (goc, glue, etc.) have been included in the \pcgeos\bin directory. Use mkmf and pmake to compile, as described in the chapter "Using Tools" of the Tools book. ( pmake depend is not needed in the NT environment.)

After compiling, copy resulting geodes directly to your target tree from the command line or use Swat's send command after attaching. world\extrapps is the appropriate demo directory for extras on the Nokia 9000i Communicator.

Changes From the DOS SDK

If you are familiar with GEOS development in the DOS environment, note the following changes:

  • You need not run pmake depend after mkmf ; use it only to re-make dependencies when you change your include-file statements.
  • You may use DOS copy to put compiled geodes in your target tree, although Swat's send command works as before.
  • Subdirectories under the main include-file directories must be explicitly stated, as in #include <Ansi/string.h> .
  • The "pause" and "break" keys do not stop the demo; kill the GEOS window using the menubar or use Swat's detach or quit command.
  • The development directories have been modified; see Directory Structure.
| Up: GEOS SDK TechDocs | Up | Prev: 3 Launching NT GEOS and Swat | Next: 5 Directory Structure


Directory Structure

Installing the SDK: 5 Directory Structure

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Compiling GEOS Applications | Next: 6 Troubleshooting

The directory structure in the NT SDK differs significantly from that of the DOS SDK. The change reflects standardization across platforms and facilitates multi-engineer development in a network environment.

Branches

Main Directories

Target Directories

Developing In Your Working Branch

Multi-Developer Projects

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Compiling GEOS Applications | Next: 6 Troubleshooting


Branches

Installing the SDK: 5.1 Directory Structure: Branches

| Up: GEOS SDK TechDocs | Up | Prev: 5 Directory Structure | Next: 5.2 Main Directories

Every device that runs GEOS contains a specific version of the kernel, applications, and various ancillary libraries and drivers. The directory path that contains the specific version of the device software is called a Branch. (The "Trunk" is a collection of all the files from all the different branches. As a developer, you will never need to utilize any files on the Trunk.)

Branch directories are located directly under the PCGEOS directory ( i.e. , the main SDK directory) and were developed as a way to "lock in" a device-specific image of the development system. For example, the N9000V20 branch contains all the header and symbol files for Version 2.0 of the Nokia 9000 SDK. When you install an SDK for another GEOS device, you will find a new branch directory specific to that device under the PCGEOS directory.

The figure below shows the directory layout of the Nokia 9000i SDK (Version 1.1). This may appear slightly different on newer SDKs. This directory tree contains two Branch directories: N9000V11 and {Working_Branch} . ({ Working_Branch } is your personal development area; see Developing In Your Working Branch for a complete explanation of Working Branches)

If multiple developers are working on a project, then the branches should be separated into the local branches which reside on each engineer's machine and the shared network branch which all the project developers will access each time they compile. When performing a server install, the above directories marked "shared/network" are installed onto the network machine, and the client install places the directories marked "local" on each developer's machine. (See 5.5 Multi-Developer Projects.)

| Up: GEOS SDK TechDocs | Up | Prev: 5 Directory Structure | Next: 5.2 Main Directories


Main Directories

Installing the SDK: 5.2 Directory Structure: Main Directories

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Branches | Next: 5.3 Target Directories

Each Branch contains the following directories:

CInclude
Contains all the *.H and *.GOH header files you need to compile GEOS applications written in GOC.
Include
Contains all the *.DEF and *.UIH header files you need to compile GEOS applications written in ESP (assembly).
Installed
Contains all the *.LDF ( i.e. , link definition files) files (used by Glue, our linker) and symbol files of all the GEOS applications, libraries, and drivers (used by Swat, our debugger).
Appl
Contains a subdirectory holding your application's source code; for instance, your CardGame application might reside in: appl\cardgame\cardgame.goc

Also contains the following subdirectories:

SDK_C
which holds general purpose sample applications; for example, the huge array sample application lives in: appl\sdk_c\harray\harray.goc
SDK_*
which holds device-specific sample applications (where * refers to the specific device); for example, the dialog box sample application for the Nokia 9000i Communicator lives in: appl\sdk_9000\dialogs\dialogs.goc
Library
Contains a subdirectory holding your library's source code; for example, the new utility library you're writing would live in: library\utility\utility.goc
Driver
Contains a subdirectory holding your driver's source code; for example, the new Zippy driver you're writing would live in: driver\zippy\zippy.goc
| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Branches | Next: 5.3 Target Directories


Target Directories

Installing the SDK: 5.3 Directory Structure: Target Directories

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Main Directories | Next: 5.4 Developing In Your Working Branch

In addition to those directories listed above, your Working Branch contains a Target directory. This directory contains the executable and data files comprising an emulation (or "demo") of the actual GEOS device. The Target subtree mirrors the file system layout of the actual device.

Network installations can make use of shared geode libraries if desired. When geodes are invoked during emulation, the current working (target) directory tree will be searched first, followed by trees specified by the top keyword under [paths] in your .ini file. For example, all of the geodes under the system subdirectory could be stored in a central, shared location in order to save disk space or allow easy updates. You can supersede shared geodes in your demo by putting custom versions (of the same name) in your private system subdirectory.

Each target directory has a geos{ec}.ini file. It can refer to files of standard .ini settings under its [paths] category; it also contains superseding information necessary for running NT GEOS. When editing .ini files, take care to end each line with a CR-LF pair or use an editor (such as DOS edit ) that does so transparently. More information on .ini files in general is found in the Tools book.

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Main Directories | Next: 5.4 Developing In Your Working Branch


Developing In Your Working Branch

Installing the SDK: 5.4 Directory Structure: Developing In Your Working Branch

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Target Directories | Next: 5.5 Multi-Developer Projects

The Working Branch is your private development area. By doing all your work in your Working Branch (also called your Local Root ), the Branch files can remain untouched.

You specify the directory for your Working Branch during the Local SDK Install program. The default is your login name. For example, if your login is jdoe, then your Working Branch directory is c:\pcgeos\jdoe\.

Within your Working directory is a file called BRANCH. This file specifies which Branch directory the tools will use when compiling and linking your source code. The tools will first look for files in your Working Branch. Whatever files it does not find in your Working Branch, it will look for in the Branch specified by the BRANCH file. You need only keep the file or files that you are working on in your Working Branch and system files (such as header files) can remain on the device Branch. So, for example, a typical development sequence might look like this:

  • You want to add another feature to the Zippy driver so you copy (or "check out" if you have a version control system) the zippy.goc file from the Branch to your Working Branch.
  • You edit the file and make your changes.
  • You compile the modified file.
  • You find and fix all the bugs.
  • You "install" the modified file by using version control or by simply copying it back to the Branch.

To specify which Branch the compiler and linker should use, place the name of the Branch directory in the BRANCH file. (Currently, setting the Branch field in GEOS Setup only affects which branch Swat uses; you must edit the actual BRANCH file yourself.) Initially, your BRANCH file contains N9000V20.

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Target Directories | Next: 5.5 Multi-Developer Projects


Multi-Developer Projects

Installing the SDK: 5.5 Directory Structure: Multi-Developer Projects

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Developing In Your Working Branch | Next: 6 Troubleshooting

The system of Branch files is most helpful when there are multiple developers working on the same project. Each developer would maintain her personal working files in her own Working Branch while shared files would remain on the device Branch. As development progresses, each developer can "install" files to the Branch directory as she reaches stable milestones thereby allowing her co-workers to use the "installed" versions of her files. (Note: "installing" can be as simple as copying the files by hand or can be handled by a version control system.)

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Developing In Your Working Branch | Next: 6 Troubleshooting


Troubleshooting

Installing the SDK: 6 Troubleshooting

| Up: GEOS SDK TechDocs | Up | Prev: 5 Directory Structure

If you are having trouble starting the simulation or attaching Swat, consult the table below. The most common known difficulties are listed.

GEOS window dies immediately.
Run the EC version and attach Swat. Does it die with the error LS_CANNOT_LOCATE_KERNEL?
The working directory of the NT GEOS window must be a target directory with an .ini file and device subdirectories.

Does Swat report FSHOF_SET_FILE_NAME_PASSED_TO_SKELETON_DRIVER, ATTEMPTED_TO_PERFORM_UNSUPPORTED_FUNCTION_
BEFORE_APPROPRIATE_IFS_DRIVER_WAS_LOADED, or TOO_MANY_PRIMARY_FSDs?
There is a problem with the file system driver. Check .ini file settings and setver listing, referring to NT Configuration .

GEOS is not in a window.
It should have a title bar and look like a window.
Verify that you have a text loader ( loader{ec}.exe ).
Verify that geosvdd.dll is in your path.
Verify that you are using an NT video driver under [screen 0] in your .ini file (probably simp4bi{t/e} ).
GEOS cannot launch, reporting: Can't open named pipe
Are you already running NT GEOS?
Only one copy can run at a time.
Swat cannot connect, reporting: Can't contact GEOS using the Named Pipe: \\.\pipe\swatpipe
Are you running the demo with a Swat stub ( s.bat or ss.bat )? Did you immediately try to attach?
If you are running the correct demo, try to attach again. You may have attempted to attach before the Swat stub was ready.
The keyboard doesn't work.
Is the NT GEOS window active? It should have a blue title bar.
Click in the window to make it active.
GEOS isn't running a window.
Check your .ini file (and [paths] referents) for correct keyboard setting (or pini keyboard in swat); you must use the gdikbd keyboard driver.
The keyboard seems to fail suddenly.
Ctrl or Alt keys may be "stuck" in GEOS . Hit Ctrl, Alt, Caps Lock, and Shift a few times to clear them.
Extended-character-set dialog will not come up.
Verify that altGr = true under category [GDI keyboard] is set in your .ini file (or referent).
Swat's srcwin command fails, reporting: Window too small
The default NT command-window size is too small for the default srcwin. There are two fixes:
(1) Reduce the srcwin buffer size to match your window size with the Swat command srcwin 20.
 (2) Restart Swat from a larger window. (Size changes during a session are not recognized by Swat.) Your default window size can be increased by changing the window-size property of your command windows, or just your Swat shortcut.
Swat does not set mouse-click breakpoints.
Make sure quick-edit mode is off in your Swat window.
GEOS hangs until Swat window selected.
Have foreground/background task priorities been set equal?
Check under the System module of the Control Panel.
GEOS hangs or .ini file corrupt.
Run the EC version and attach Swat. Is the .ini file corrupt?
Open and re-save it using NT or DOS edit to restore CR-LF 's.
GEOS does not emulate sounds.
To test sounds in your application, you must use an actual device. (Sound has been disabled entirely to avoid the spurious sounds produced by the PC - DOS driver, in your . INI files, SOUND = OFF under [ UI ].)
pmake fails, reporting: Couldn't find geos.mk.
Set environment variable ROOT_DIR (to C:\PCGEOS , e.g.).
pmake fails, reporting: Couldn't find stddef.h
Set environment variable GOC_COMPILER_DIR (to C:\BC45 , e.g.).
| Up: GEOS SDK TechDocs | Up | Prev: 5 Directory Structure


Overview

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GEOS Overview
| One-page Version of this Chapter for Easy Printing

This chapter describes the structure of GEOS and the various system components and services used by applications. Nearly all programmers will want at least to browse this chapter before continuing with GEOS application development. The structures and architectures of the 8088 and 80x86 processors are reviewed in Appendix A.

1 GEOS Overview
2 The System Architecture
2.1 Applications
2.2 Libraries
2.3 The Kernel
2.4 Device Drivers
2.5 The User Interface
3 Object-Oriented Programming
3.1 Objects, Messages, and Methods
3.2 Classes and Inheritance
4 Multitasking and Multithreading
5 The GEOS User Interface
5.1 The Generic User Interface
5.2 The Scalable User Interface
5.3 Windows and Window Management
5.4 Input
5.5 Menus and Dialog Boxes
5.6 Scrolling Views
5.7 Visible Object Classes
5.8 Geometry Manager
5.9 Lists
5.10 Other Gadgets
5.11 Managing Documents and Files
5.12 Multiple Document Interface
5.13 Clipboard and Quick-Transfer
5.14 General Change Notification
5.15 Help Object
6 System Services
6.1 Memory
6.2 Virtual Memory
6.3 Local Memory and Object Blocks
6.4 Item Databases
6.5 Graphics System
6.6 Text
6.7 Print Spooler and Printing
6.8 Timers
6.9 Communications
6.10 Math support
6.11 International Support
7 Libraries
8 Device Drivers

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GEOS Overview
| One-page Version of this Chapter for Easy Printing


GEOS Overview

Overview: 1 GEOS Overview

| Up: GEOS SDK TechDocs | Up | Prev: Overview | Next: 2 The System Architecture

GEOS is a state-of-the-art graphical operating system combining the latest software technology with numerous innovations. Its tightly coded assembly language and object-oriented system turns the humblest 8088-based PCs with as little as 512 K of RAM into graphical workstations. On higher-end machines, GEOS is in a class by itself--performance on 80286, 80386, and 80486 machines with extended or expanded memory is exceptional.

Besides being coded entirely in object-oriented assembly language (a concept and language developed by Geoworks), GEOS is built on one of the most sophisticated operating system architectures available for PCs. Most of the GEOS kernel and system is composed of dynamically-linkable libraries; in addition, developers can create their own libraries to support entire generations of applications.

GEOS also includes true pre-emptive multitasking, multiple threads of execution within single applications, built-in outline font technology, and drivers for various devices such as printers, mice, video cards, file systems, keyboards, parallel and serial ports, task switchers, power management systems, and font engines. Development for GEOS is easy and quick; much code traditionally required by applications has been provided within the system software, thereby reducing time spent by application programmers on coding and debugging.

In addition, GEOS employs a breakthrough generic user interface. Applications specify their user interface needs, and the system dynamically chooses the proper manifestation of the UI at run-time through the use of specific UI libraries. Complex drawing and geometry are handled automatically by the UI library. The generic UI allows applications to work with various UI specifications without being recompiled. Users can choose the look and feel they want without burdening the application programmer with extra coding.

Among the other advanced features included in GEOS are a single imaging model providing true WYSIWYG (a single image is used both for drawing to the screen and for printing); automatic scaling and scrolling of displayed data; dynamic memory management and virtual memory; an item database manager; a graphic object library including support for resize, move and scale operations; complex and complete text formatting and editing; specialized generic controller objects providing standard user interface and functionality; and full network support. Several other mechanisms and libraries not mentioned here are also built into the system.

The following sections of this chapter describe many of these concepts and implementations, providing a quick-view of the GEOS system as a whole. More information may be found on individual topics elsewhere in this developer's kit. This chapter provides the background information necessary, however, to take full advantage of all that GEOS has to offer the application developer.

| Up: GEOS SDK TechDocs | Up | Prev: Overview | Next: 2 The System Architecture


The System Architecture

Overview: 2 The System Architecture

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 GEOS Overview | Next: 3 Object-Oriented Programming

The GEOS system as a whole consists of several layers. The topmost layer contains applications. Users interact directly with these applications, which use dynamic libraries on the next layer. One of the major libraries is the User Interface Library, commonly known as the UI. Both the libraries and applications interact with the kernel, the heart of the operating system. The kernel communicates with device drivers, which function as the final GEOS interface to physical devices such as video screens, printers, and file systems.

Applications

Libraries

The Kernel

Device Drivers

The User Interface

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 GEOS Overview | Next: 3 Object-Oriented Programming


Applications

Overview: 2.1 The System Architecture: Applications

| Up: GEOS SDK TechDocs | Up | Prev: 2 The System Architecture | Next: 2.2 Libraries

A GEOS application can take advantage of the various kernel and UI mechanisms to provide some set of functionality to the user. An application is made up primarily of two parts: The first part defines the application's user interface using objects and functions provided by the User Interface Library. The second part consists of the code of the application including procedures, functions, and object-specific code.

| Up: GEOS SDK TechDocs | Up | Prev: 2 The System Architecture | Next: 2.2 Libraries


Libraries

Overview: 2.2 The System Architecture: Libraries

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Applications | Next: 2.3 The Kernel

GEOS libraries are bodies of code or collections of objects that are dynamically loaded into memory when called for by applications or by other libraries. The use of libraries reduces the amount of code within application resources and increases application performance. In addition, library routines and objects may be shared by several different applications at once, reducing duplicated code in memory.

Many standard libraries are provided with GEOS and are available to all developers. Among these are a text object library, a graphic object library, a an import/export library and others. In addition, developers can easily create their own libraries with the developer kit tools.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Applications | Next: 2.3 The Kernel


The Kernel

Overview: 2.3 The System Architecture: The Kernel

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Libraries | Next: 2.4 Device Drivers

The GEOS kernel is the core of the operating system, providing all the basic services needed by the system. It provides multitasking, memory management, object creation and manipulation, windowing, process management, file system access, inter-application communication, message dispatching, and scores of basic graphic primitives. The kernel provides nearly 1000 routines usable by applications and libraries, though most applications will use only a portion of them.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Libraries | Next: 2.4 Device Drivers


Device Drivers

Overview: 2.4 The System Architecture: Device Drivers

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 The Kernel | Next: 2.5 The User Interface

GEOS device drivers isolate the system and applications from the specific needs and foibles of a user's hardware. Drivers translate the generic actions of an application (such as opening a file) into the specific needs of the hardware or system in use (such as the specific call to MS-DOS to open the file). By isolating the system from hardware specifics, drivers allow GEOS to be expanded in the future to use new devices and engines without recompiling applications.

Drivers are provided with the system for printers, video cards, mice, keyboards, serial and parallel ports, file systems, task switchers, power management hardware, and font engines. In the future, developers will be able to develop their own drivers for new hardware and file systems.

Most applications and libraries will access drivers through kernel calls. Drivers are also accessible to applications and libraries directly.

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 The Kernel | Next: 2.5 The User Interface


The User Interface

Overview: 2.5 The System Architecture: The User Interface

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Device Drivers | Next: 3 Object-Oriented Programming

The GEOS User Interface (UI) is a special entity. While it consists primarily of dynamic libraries, it can be thought of as a user interface driver.

Applications define their UI needs in a high-level language (called Goc and developed by Geoworks) interpreted by a special C preprocessor. Applications in general do not have to manifest windows, scrollbars, buttons, menus, or dialog boxes; instead, they use generic objects from the provided UI object library. These generic objects have no inherent visual representation; instead, they have a strictly-defined functionality and API.

A Specific User Interface library, at run-time, determines the visual representation each object takes depending on its context. For example, a GenInteraction generic object may appear as a menu, a dialog box, or a merely a grouping of other generic UI gadgets. Additionally, a menu GenInteraction may be represented as a vertical menu in one Specific UI but as a horizontal bar of choices in another.

Although such a system may appear to take some control of the application's user interface away from the programmer, in actuality quite the opposite is true. The use of a generic UI frees the programmer to concentrate on other issues and features without worrying about whether the application conforms to one or more specifications. The programmer does not have to worry about window positioning or clipping areas (unless he or she wants to), and menus and dialog boxes are almost entirely automatic.

Additionally, the programmer can set hints for individual generic objects; hints can help the specific UI decide how the object should appear or be arranged on the screen. Not all hints are allowed in all specific UIs, so a particular specific UI may ignore certain hints while implementing others. For example, a dialog box that has three buttons within it may have the hint HINT_ORIENT_CHILDREN_VERTICALLY to indicate the buttons should appear in a column rather than a row.

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Device Drivers | Next: 3 Object-Oriented Programming


Object-Oriented Programming

Overview: 3 Object-Oriented Programming

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 The System Architecture | Next: 4 Multitasking and Multithreading

Object-Oriented Programming (OOP) is a popular way of organizing programs, especially programs using graphical user interfaces. GEOS is an object-oriented system, programmed entirely in Object Assembly, a Geoworks innovation. This section describes the concepts behind OOP and how it is implemented in GEOS. If you are familiar with OOP concepts, you may skip this section; because terminology can differ from system to system, however, you will probably want to at least skim the rest of the section. (A full discussion of GEOS messaging and object manipulation can be found in the GEOS Programming chapter.)

Object-Oriented Programming is simply a way to organize code and data differently from traditional procedural programming. Anything done with procedural programming can be done in an object-oriented way and vice versa. However, OOP offers several significant advantages over procedural programming:

  • Modularity
    Especially in large systems, modularity can lead to cleaner, more manageable, and more easily expandable programs. OOP provides tight modularity with each component ( object ) having a strict API. This modularity also makes OOP systems ideal for the multithreaded environment of GEOS.
  • Simplicity
    People interact with things individually. Each object in our environment has a certain set of characteristics and certain behaviors. OOP systems use this point of view as a foundation for programs that are easy to understand.
  • Efficiency
    The efficient design of objects in an OOP system can lead to less repetition of reusable code. Several objects working independently may use the same code, thus reducing the impact on memory usage.
  • Reduction of code
    Because functionality (functions, routines, etc.) is inherited by object types from other object types (known as classes), it doesn't have to implemented again and again. Instead, one version of the code can exist in a particular class and be inherited by other classes, thus reducing coding and debugging time.

Objects, Messages, and Methods

Classes and Inheritance

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 The System Architecture | Next: 4 Multitasking and Multithreading


Objects, Messages, and Methods

Overview: 3.1 Object-Oriented Programming: Objects, Messages, and Methods

| Up: GEOS SDK TechDocs | Up | Prev: 3 Object-Oriented Programming | Next: 3.2 Classes and Inheritance

Procedural programming uses routines that act with globally-accessible or locally-defined data. Those routines must know about other routines in the program and must understand the data structure and organization used by other routines. Although some amount of isolation is applied to each routine, in essence they are all part of the greater whole of the program.

Objects, in their simplest sense, consist of data ( instance data ) and routines ( methods ) that act on that data. Objects may interact with each other but may not alter other objects' instance data; this rule leads to strict modularity and cleaner program design. Objects do not need to understand how other objects work or how another object's instance data is arranged; instead, only the interface to the other object is required.

Objects interact with each other via messages . Messages may indicate status, provide notification, pass data, or instruct the recipient to perform an action. When an object receives a message, it executes an appropriate method . A method may change the object's instance data, send messages to other objects, call kernel routines, or perform calculations--anything that can be done in a normal program procedure can be done in a method. (Note that occasionally the term message handler is used for method .)

Every object is represented by an Object Pointer (optr), a data structure that uniquely identifies the object's location in the system. This data structure is a combination of two special memory handles that provide abstraction of the object's location--this allows the object to be moved in memory or swapped to disk, or even saved to and retrieved from files. Object pointers are used to identify objects in many situations, the most common being when a message is sent. The intended recipient of the message is indicated by its optr.

Objects may interact with each other even if they are in different threads of execution. This is made possible by message queues and the kernel's message dispatcher . When a message is sent, it is first passed to the kernel's dispatcher with the recipient's optr. The dispatcher then puts the message in the recipient object's message queue. If other messages have been sent to the recipient but not handled yet, then the message will wait in the queue until the others have been handled. Otherwise, the message will be handled immediately. (For timing-critical messages, the sender can indicate that the message must be handled immediately; this is important in a multithreaded system.)

A thread is a single entity that runs a certain set of code and maintains a certain set of data. Only one thread may be using the processor at any given time; when a context switch occurs, one thread loses the processor and another takes it over. Each thread may run code for many objects, and an application can have several threads.

As an example, the figure below shows the conceptual model of a Counter object. The Counter maintains a single instance data field, currentCount . It also has three predefined actions:

  • CounterIncrementCount()
     This method handles the message MSG_COUNTER_INCREMENT . Its purpose is to increment the currentCount instance data field. When the count reaches the maximum (in this case, 100), then the counter resets itself.
  • CounterReturnCur()
     This method handles the message MSG_COUNTER_RETURN_CUR . It returns the value of the currentCount instance data field.
  • CounterReset()
     This method handles the message MSG_COUNTER_RESET . It sets the currentCount instance data field to zero.

Messages can carry data (parameters) with them and can also have return values. For example, if the Counter in the example could be set to an arbitrary value (rather than reset to zero), the object sending the set message would have to indicate the value to be set. This would be passed in the same way as a normal C parameter.

If a message is supposed to return a value to the sender, the sender must make sure the message is handled immediately. In this case, the message acts like a remote routine call, blocking the thread of the caller until the message has been handled and the value returned.

Objects can also share data when necessary. Rather than share instance data, however, each object can have an instance data field containing the handle of a sharable block of memory holding the subject data. Then each object could access the shared block through the normal memory manager. Handles are offsets into a table maintained by the kernel; this table contains pointers to the actual memory. This abstraction of the pointer allows the kernel to move the memory block around without forcing everything else to update their pointers. Handles can also be passed as message parameters, allowing large blocks of data to be passed along from object to object.

The figure above shows an example of two objects interacting via messages. In this example, the Requestor object requires the Calculator object to perform a calculation given two numbers. However, since the timing of the calculation is not critical, the result is not returned but is instead sent back by the Calculator object in a subsequent message. This allows the Requestor object to continue with other tasks until the result is ready. (Note that if the Calculator object could be used by more than one requestor, it would have to also be passed the requestor's object pointer. Otherwise, it would have no idea where to send the return message. This example assumes the Calculator object is used by only the one requestor and inherently knows the requestor's optr.)

| Up: GEOS SDK TechDocs | Up | Prev: 3 Object-Oriented Programming | Next: 3.2 Classes and Inheritance


Classes and Inheritance

Overview: 3.2 Object-Oriented Programming: Classes and Inheritance

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Objects, Messages, and Methods | Next: 4 Multitasking and Multithreading

If every object had to be coded and debugged once for each time it was used, OOP would provide few benefits over standard procedural programming. Many objects share similar functionality and could easily make use of the same code over and over again. The concepts of class and inheritance allow objects with similar data structures and methods to use common code.

The class is actually where the functionality of objects is defined. Every object is simply an instance of a class, a manifestation of the instance data and methods defined for the class. For example, the Counter object of Objects, Messages, and Methods could be an instance of a class called CounterClass . Other counters sharing the same characteristics would also be instances of CounterClass , each having its own value in its own instance data field.

A main benefit of the implementation of classes in GEOS is that objects can be created and destroyed during execution. Class definitions are stored separately from an individual instance's data; each instance knows where its class is located. Therefore, if two instances of a particular class exist, they both point to a single copy of the class definition. Since the code is stored in the class definition, the instances can use up only as much memory as their data fields require; each instance does not need to store its own method code.

Another, more powerful, benefit is gained through the implementation of inheritance. In many cases, objects will be similar but not identical. This means that their classes are also similar but not identical. Inheritance overcomes this problem through the use of superclasses and subclasses .

A class provides a certain set of instance data and methods. A subclass inherits all the instance data structures and methods of its superclass, modifying or enhancing them to provide some different functionality.

Take, for example, the class CounterClass . An instance of this class would provide a counter with certain features: The counter goes from zero to 100, it can be reset manually, it resets automatically when it reaches 100, and its value is retrieved with the use of a certain message. However, suppose we need a counter that does all those things and allows the counter to be set to an arbitrary value with the use of a new message.

Without inheritance, we would have to write all the code for CounterClass over again as a new class, adding the new method. However, by creating a subclass of CounterClass (let's call it CounterSetClass ), we can inherit all the methods and data structures of CounterClass without recoding them. Instead, we simply add the new message and the code for the new method. (It is also possible to modify inherited methods in a subclass.) This example of CounterClass and CounterSetClass is shown in the figure above.

The inheritance implemented in GEOS is actually much more complex and sophisticated than this simple example shows. You may never need to know more than the above concepts, however.

GEOS classes may be of a special type known as a variant . Variant classes do not initially know what their superclass is; instead, the superclass is determined by context for each instance of the variant class. This is a complicated topic that is discussed later in the documentation.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Objects, Messages, and Methods | Next: 4 Multitasking and Multithreading


Multitasking and Multithreading

Overview: 4 Multitasking and Multithreading

| Up: GEOS SDK TechDocs | Up | Prev: 3 Object-Oriented Programming | Next: 5 The GEOS User Interface

Multitasking is the ability to have multiple processes running simultaneously on a single system. In actuality, PCs have only one processor and only one task can run at any given moment; the operating system must manage the processor and allocate time to all the different programs running at once.

A thread is a single entity that runs in the GEOS system. Threads may be event-driven or procedural. An event-driven thread runs objects and has a message queue (also called an event queue). The thread receives messages for its objects and is only active when the objects are handling the messages. If an event-driven thread never receives a message, it will never use the processor. Procedural threads execute sequential code such as functions or procedures in C. Procedural threads do not have a message queue and do not run objects.

Cooperative multitasking , used in some operating systems, requires all processes running to cooperate with the operating system. The system has a routine which each process must call periodically; this routine (called a Context Switch routine) checks if any other processes are waiting to run. If other processes are waiting, priorities are checked and the process with the highest priority takes over the processor.

GEOS uses preemptive multitasking , in which threads are given the illusion that they have nonstop access to the processor. A preemptive system periodically interrupts the thread running and checks for other waiting threads; if there are any, the system will switch context automatically. Programs do not have to call a context switch routine, a cumbersome requirement in many cases.

GEOS also maintains a knowledge of thread priority . Every thread (including the kernel and user interface) has a base priority and a current priority; the base priority rarely changes, while the current priority decreases with recent processor usage. GEOS maintains these priorities intelligently, making sure that no thread ever uses the CPU more than its share of the time.

Any application running in GEOS may have multiple threads. This is useful, for example, in a spreadsheet application, where complex and time-consuming calculations may be done by a background thread that wakes up only when calculations need to be made. Use of multiple threads can make the application appear to be extraordinarily fast; the user can continue to navigate menus and use other UI gadgetry while the application is also doing something else.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Object-Oriented Programming | Next: 5 The GEOS User Interface


The GEOS User Interface

Overview: 5 The GEOS User Interface

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Multitasking and Multithreading | Next: 6 System Services

The GEOS User Interface (UI) maintains the interaction between each application and the user. It provides several necessary and many extra services for applications, relieving programmers from building much of the basic UI functionality into their applications. The structure, components, and services of the GEOS UI are outlined below.

The Generic User Interface

The Scalable User Interface

Windows and Window Management

Input

Menus and Dialog Boxes

Scrolling Views

Visible Object Classes

Geometry Manager

Lists

Other Gadgets

Managing Documents and Files

Multiple Document Interface

Clipboard and Quick-Transfer

General Change Notification

Help Object

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Multitasking and Multithreading | Next: 6 System Services


The Generic User Interface

Overview: 5.1 The GEOS User Interface: The Generic User Interface

| Up: GEOS SDK TechDocs | Up | Prev: 5 The GEOS User Interface | Next: 5.2 The Scalable User Interface

A User Interface (UI) specification is, essentially, a set of rules and conventions used to determine the interaction between a user and an application. Over the years, UI specifications have evolved from basic command-line shells to the standard graphical and pen-based systems in use today. No doubt you have come across several graphical UI specifications currently in use--Macintosh, Open Look, Presentation Manager, and OSF/Motif are a few examples.

Each of these specifications represents a particular implementation of the basic functionality required for application-user interaction. The basic functions are similar; only the implementation is different. Therefore, each of these specifications is referred to as a Specific UI .

GEOS employs a Generic UI --an implementation-independent way of defining an application's user interface. To the user, this means a single application executable can be used in any of several specific UI implementations. To the developer, this means relief from many of the UI definition tasks normally associated with graphical user interfaces.

The Generic UI can be thought of as a UI driver--a layer of abstraction between the application and the UI implementation. The programmer defines his needs in terms of basic functionality and user involvement, leaving the actual implementation (decisions about color, shape, menu structure, etc.) up to the system at run-time.

The application defines its UI by selecting certain objects from the Generic UI Library. Each of these objects has certain attributes that determine its specific implementation. In addition, each generic object may be assigned hints . Hints help the Specific UI Library determine the proper context and subjective information required to fully flesh out the proper manifestation of each generic object (for example, a GenPrimary window object can have special sizing restrictions suggested with various hints such as HINT_FIXED_SIZE and HINT_INITIAL_SIZE ).

Attributes

Attributes represent objective information which does not change with the specific UI in use. In general, attributes determine the basic functionality of the object regardless of its manifestation on the screen. For example, a GenView may have its scrollable attribute set; this means that in all Specific UIs, the View will have some sort of scrollers. However, one Specific UI might implement the scrollers as bars and another might implement them as dials or cranks.

Attributes are always allocated space in an object's instance data regardless of whether the attribute it set. For example, all GenView objects have a bit specifying whether it is scrollable or not. Attributes are used, therefore, primarily for determining the object's functionality rather than its appearance.

Hints

Hints represent subjective or contextual information about objects. Hints are different from attributes in two main aspects: First, hints may be ignored by certain Specific UIs. Suppose, for example, that a given Specific UI only allowed horizontal ordering of components within dialog boxes. This specific UI would ignore the hint HINT_ORIENT_CHILDREN_VERTICALLY applied to any GenInteraction object implemented as a dialog box.

Second, hints are dynamically added to each object's instance data in a special format known as variable data . Variable data fields are not allocated within the object's instance data unless they are used. Thus, objects with fewer hints use less memory. Attributes, on the other hand, must exist in the object's instance data whether they are used or not, and they therefore require a given amount of memory.

The Generic UI Classes

The Generic UI Library contains a number of object classes that implement nearly all the UI functions an application will ever need. For a full description of the API of each of these classes, see the Objects Book. Some of the more common implementations (e.g. menus, dialog boxes, and scrolling views) of these objects are outlined in the sections below.

| Up: GEOS SDK TechDocs | Up | Prev: 5 The GEOS User Interface | Next: 5.2 The Scalable User Interface


The Scalable User Interface

Overview: 5.2 The GEOS User Interface: The Scalable User Interface

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 The Generic User Interface | Next: 5.3 Windows and Window Management

As application technology advances, applications gain more and more features. Despite recent improvements in user-friendliness, however, over-featured applications can sometimes intimidate and frustrate users.

GEOS therefore implements a Scalable User Interface , a system in which a single application's source code can be compiled into different versions of the application, each version at a different level of complexity. Scalability of applications is shown in Geoworks Pro--the appliances are all created from exactly the same source code as the advanced versions of those applications.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 The Generic User Interface | Next: 5.3 Windows and Window Management


Windows and Window Management

Overview: 5.3 The GEOS User Interface: Windows and Window Management

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 The Scalable User Interface | Next: 5.4 Input

A window is an object that defines an area on the screen in which drawing and user interaction can occur. Windows may be resizable and movable, and automatically clip any drawing so it does not appear outside the window's edges.

The GEOS Window Manager maintains information about the size, shape, and visibility of all the windows in the system. In general, applications do not access the Window Manager directly; instead, generic UI objects and the kernel handle all window manipulation. Several different types of generic objects are "window" objects: GenPrimary is used as an application's primary window, GenInteraction is used for dialog boxes and menus, GenDisplay displays documents in a scrollable window, and GenView is used to create a scrollable graphics window into which an application can draw.

The Window Manager is closely tied to the GEOS Graphic System. These two parts of the system handle all window resizing, reshaping, drawing, and clipping automatically. They also interact with the User Interface's input mechanism to ensure that the proper windows receive the proper input. Applications do not need to know the shape, position, or size of a window when drawing inside it.

Applications can also allocate one or more windows in which they can present data (text, graphics, or other information). Each of these windows is called a View and is an instance of the GenView object (see below). The View manages all scrolling, scaling, sizing, and clipping automatically for the application; programmers don't have to worry about managing scrollbars or window size in order to ensure the proper portion of the data is drawn. Additionally, applications can modify any of the scrolling, scaling, or sizing behavior with a little effort to provide custom functionality.

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 The Scalable User Interface | Next: 5.4 Input


Input

Overview: 5.4 The GEOS User Interface: Input

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Windows and Window Management | Next: 5.5 Menus and Dialog Boxes

GEOS has an input manager that tracks the mouse, gets keyboard input, and passes the input events on to the proper windows and objects on the screen. The input manager is part of the UI.

The UI keeps track of three different active objects in the system: the Focus, the Target, and the Model. The focus is the active object that should receive all keyboard input. The target is the currently selected object with which the user can interact. The model represents a non-visible "selection" that can be maintained by the application. These three active objects can be accessed directly by other objects (e.g. menu items); for example, a "change color" menu item may want to work on whatever object is currently active--it would want to change the color of the target object, and by contacting the target directly, it does not have to know what object is the target.

Any object in the system can grab mouse or keyboard events. When an object has been granted a mouse grab or keyboard grab, it will receive the events until it relinquishes the grab. This fact is most useful when dealing with visible object trees inside a View (see GenView, below)--generic objects handle input automatically (including the Text objects).

Applications may or may not need to handle input directly; complex applications likely will want to filter certain input or handle special cases. Simple applications or utilities will probably not have to deal with input (keyboard or mouse events) directly at all.

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Windows and Window Management | Next: 5.5 Menus and Dialog Boxes


Menus and Dialog Boxes

Overview: 5.5 The GEOS User Interface: Menus and Dialog Boxes

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Input | Next: 5.6 Scrolling Views

Among the generic objects of the GEOS UI library is GenInteraction. This object can be used to implement menus, dialog boxes, and error boxes, and it can also be used for grouping and arranging other generic objects (such as triggers, lists, etc.). It provides extreme flexibility and functionality, and experienced programmers can usually get a menu and dialog structure up on the screen quickly just by setting a few attributes.

Remember that because the Interaction is a generic object, the Generic UI and Specific UI libraries will translate it into its proper implementation at run-time. The Interaction serves the primary purpose of grouping objects; the grouping may be implemented in various ways (e.g. a menu or a dialog box) depending on the attributes and hints applied.

The sections below outline some of the practical functionality of these objects; for a full description of what these do and how they can be used, see the Object Reference Book.

Menus

Menus in GEOS are subject to the rules and conventions of the specific UI in use by the user. However, several basic concepts are supported.

  • Standard menus
    Some specific UIs have differing menu structures. Therefore, several standard generic menus (e.g. the File menu) can be implemented by giving the menu a standard set of attributes. The UI will automatically build and manage this menu according to the UI specification in use at the time.
  • Cascading or nested menus
    Because Interactions may be nested, cascading (or nested) menus are easy to implement. Cascading menus are useful if an application has many menu items that may logically be grouped into different areas of functionality.
  • Keyboard accelerators
    Because the UI manages both input and menus, you can easily specify accelerator keystrokes for each menu item. When the user hits the proper keystrokes, the same action is performed as if the user had clicked on the associated menu item. This happens automatically without additional application code.
  • Keyboard menu navigation
    Every menu item may have a character that the user may press during keyboard navigation. This is important because it allows users to use your entire application without having a mouse.
  • Lists and checkboxes within menus
    Menus may contain not only triggers but also lists of items. Lists are implemented with the GenItemGroup, GenItem, GenBooleanGroup, and GenBoolean generic objects. Lists may be exclusive or not, and each entry may have a checkbox indicating its on/off status. Again, all these functions are determined by the attributes set for the interaction and item objects.
  • Menu items that bring up dialog boxes
    By setting certain attributes in an interaction, you can create menu items that automatically bring up dialog boxes. The UI will put a trigger in the menu representing the dialog box, and when the trigger is activated, the UI will put up the box.
  • Pin-up menus
    In the OSF/Motif implementation of GEOS, all menus are automatically given the ability to be pinned in place and moved around the screen. Because this is a function of the specific UI, applications are completely unaware of the fact.

Dialog Boxes

Dialog boxes are standard ways of having an application interact with the user. For example, a dialog box may contain a number of controls that determine how the application displays its data.

Dialog boxes, like menus, are implemented through the use of GenInteraction objects. Applications may also call a kernel routine that will put up standard dialogs in certain situations (e.g. errors or warnings).

Several features of dialog boxes are implemented automatically with very little additional code in the application:

  • Automatic geometry management
    When a dialog box contains generic objects (e.g. triggers, lists, and text fields), all geometry is handled automatically. By using various hints, you can modify the normal organization, but sizing and placement of the generic objects and the dialog box are entirely automatic.
  • Standard response triggers and reply bars
    Just as there are standard menu types, UI specifications often include rules for dialog box response triggers (e.g., the "OK" button on the left and a "Cancel" button on the right). Standard response triggers normally appear in an area of the dialog box known as the "reply bar." You can set up a reply bar with standard attributes that will automatically be implemented properly by the UI. As an alternative, you can even use pre-defined dialog box types that have their own reply bars.
  • Modality
    Sometimes, dialog boxes require a user action before the application or system can continue with other tasks. Dialog boxes can have various modality states: system-modal, which does not allow the user to interact with anything but the dialog box; application-modal, which allows the user to switch to other applications but disallows interaction within the dialog's application (except for the dialog box); and non-modal, which allows the user to switch to any other part of the system.
  • Standard dialog types
    To increase the amount of standardization across all applications, GEOS includes several standard dialog box types accessible through the use of UI routines. These include errors, warnings, and questions. Also, you can call up custom dialog boxes with special kernel routines.
| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Input | Next: 5.6 Scrolling Views


Scrolling Views

Overview: 5.6 The GEOS User Interface: Scrolling Views

| Up: GEOS SDK TechDocs | Up | Prev: 5.5 Menus and Dialog Boxes | Next: 5.7 Visible Object Classes

Although the basic generic objects provided by GEOS are powerful and useful in many situations, applications still need a way to display their own specific data. In most systems, this consists of allocating a window for the application's use and then implementing additional functionality for that window--scrolling, scaling, and document management.

The GenView generic object does all these things for an application. The application can allocate any number of views for its own use, and each view can display anything from regular graphics to hierarchies of visible objects.

Most applications will use at least one view. Its power and flexibility give programmers the opportunity to concentrate on their own application's functionality without having to worry about display issues such as scaling, clipping, and scrolling--they are all handled automatically. Some of the features of a view are listed below (see the GenView chapter for more detailed information):

  • Automatic clipping and updating
    When an application uses a GenView, the programmer can forget the worries involved in figuring out which portions of his document need to be redrawn when. The GEOS Window Manager will automatically clip the document to the View's boundaries and will notify the application whenever a portion of the View has become invalid (whether by scrolling, sizing, moving, or being uncovered by movement of another object). The application simply must draw the document whenever notified that a portion has become invalid; it does not have to do any calculations regarding what is visible on the screen (though it might if the document is especially complex).
  • Automatic scrolling
    By setting two attributes in a GenView's definition, an application can automatically make its view scrollable. The UI will automatically create scroller objects that will interact directly with the view--the application never has to know how far or in what direction the view has been scrolled. It simply will receive a message saying that the window has been invalidated and needs to be redrawn.
  • Automatic scaling
    The view provides automatic scaling of the contents of its window. The application may request scalings or include a standard controller to let the user control this transparently to the application.
  • Automatic sizing
    The Geometry Manager will automatically size the view to fit within its parent window. When the parent is resized, the view will follow. This process is transparent to applications. However, applications can customize this behavior by setting a fixed or desired size or by altering the sizing behavior of the view. They can also override this to make the parent window follow the view's size.
  • Comprehensive input management
    Many applications will display objects within a view. In many cases, these objects will require input (e.g. the cards in Solitaire). The view gets input events from the User Interface and can pass them on to the proper objects in the hierarchy.
  • Flexibility
    All the functions described above may be customized. Scrolling, for example, can be tracked and altered. Custom scroller objects can be defined in place of the automatically-generated scrollers. Sizing behavior can be adjusted. If necessary, you could get the view's bounds in order to draw only the visible portion of your document (though this often does not increase drawing performance significantly).
  • Customizable background color
    Each view has a default background color determined by the specific UI. This can, however, be set to any RGB value or to any GEOS color index. This is used, for example, in Solitaire to create the green background (so the application does not have to draw green under all the other objects).
| Up: GEOS SDK TechDocs | Up | Prev: 5.5 Menus and Dialog Boxes | Next: 5.7 Visible Object Classes


Visible Object Classes

Overview: 5.7 The GEOS User Interface: Visible Object Classes

| Up: GEOS SDK TechDocs | Up | Prev: 5.6 Scrolling Views | Next: 5.8 Geometry Manager

Many applications will need to create objects that draw themselves and interact directly with the user. Rather than forcing the programmer to create each of his objects from scratch, GEOS offers several visible object classes that already understand several system services and constructs. When an application needs a visible object, it simply defines a subclass of one of these visible classes, thus ensuring that his object will handle all the UI messages that may be sent to it.

| Up: GEOS SDK TechDocs | Up | Prev: 5.6 Scrolling Views | Next: 5.8 Geometry Manager


Geometry Manager

Overview: 5.8 The GEOS User Interface: Geometry Manager

| Up: GEOS SDK TechDocs | Up | Prev: 5.7 Visible Object Classes | Next: 5.9 Lists

The GEOS User Interface includes a sophisticated Geometry Manager that does all the calculations for organization of objects on the screen. The Geometry Manager manages the positions of generic UI objects in dialog boxes and windows, for example, and it can be used to automatically position visible objects within a view.

| Up: GEOS SDK TechDocs | Up | Prev: 5.7 Visible Object Classes | Next: 5.9 Lists


Lists

Overview: 5.9 The GEOS User Interface: Lists

| Up: GEOS SDK TechDocs | Up | Prev: 5.8 Geometry Manager | Next: 5.10 Other Gadgets

The GenItem, GenItemGroup, GenBoolean, GenBooleanGroup, and GenDynamicList objects can be used to provide several different types and styles of lists. Lists may appear within menus or dialog boxes. The actual visual implementation of a list depends on the specific UI in use; however, there are several basic types of lists available (for complete information, see the Lists chapter):

  • Dynamic lists
    Lists can be either static (a set number of elements) or dynamic (a varying number of elements).
  • Scrolling lists
    Scrolling lists are usually implemented for long lists or dynamic lists.
  • Exclusive lists
    Exclusive lists have one and only one element chosen at all times.
  • Non-exclusive lists
    Non-exclusive lists may have any number of elements set at a time. Each element is set or reset independently of the others.
  • Non-exclusive lists with override
    Non-exclusive lists with an override have a non-exclusive list with an additional element that overrides all the others (an example is the text styles menu--the "Plain" option overrides each of the others).
| Up: GEOS SDK TechDocs | Up | Prev: 5.8 Geometry Manager | Next: 5.10 Other Gadgets


Other Gadgets

Overview: 5.10 The GEOS User Interface: Other Gadgets

| Up: GEOS SDK TechDocs | Up | Prev: 5.9 Lists | Next: 5.11 Managing Documents and Files

Besides the objects listed above, the generic UI supplies a number of other gadgets. These include

  • Text Display and Edit Objects
    The GenText object provides a full-featured word processing object. It can be used for every text function from displaying a word to implementing a simple yet full-featured word processor. It supports multiple styles and paragraph attributes, character and paragraph spacing, and all the other features supported in the Text Library (see Text .)
  • Triggers
    The GenTrigger object implements all the functionality of a simple push-button. When a trigger is pressed, it sends a message to a pre-specified object.
  • Value
    The GenValue object allows the user to set a value within a given range.
  • Controllers and Tool Boxes
    The GenControl and GenToolControl objects allow an application to set up controllers that provide standard menu entries and their associated features. For example, the GenEditControl object creates the standard Edit menu with Cut, Copy, and Paste triggers. The GenToolControl object allows the user to specify which items in which controllers may be displayed within a floating tool box.
  • File Selector
    The GenFileSelector provides all the features of a file selector dialog box that allows the user to traverse their file system's directories and select a file.
| Up: GEOS SDK TechDocs | Up | Prev: 5.9 Lists | Next: 5.11 Managing Documents and Files


Managing Documents and Files

Overview: 5.11 The GEOS User Interface: Managing Documents and Files

| Up: GEOS SDK TechDocs | Up | Prev: 5.10 Other Gadgets | Next: 5.12 Multiple Document Interface

Most applications will save data files. GEOS provides a convenient storage format for data files and an even more convenient mechanism for managing the files.

The Document Control objects interact with the GEOS file system and virtual memory system to implement the Open, Close, Save, Save-As, and Revert functions of the File menu as well as many other common document-related features. In fact, the Document Control also controls user interaction for these basic functions, creating and updating the File menu and putting up the proper dialog boxes for each operation.

The Document Control objects, like GenView and other components of GEOS, take basic functionality common to most applications and implement it in the system software. Using a Document Control for your data file management can save weeks of coding and debugging time.

| Up: GEOS SDK TechDocs | Up | Prev: 5.10 Other Gadgets | Next: 5.12 Multiple Document Interface


Multiple Document Interface

Overview: 5.12 The GEOS User Interface: Multiple Document Interface

| Up: GEOS SDK TechDocs | Up | Prev: 5.11 Managing Documents and Files | Next: 5.13 Clipboard and Quick-Transfer

Some applications may benefit from allowing the user to have multiple data files open at any given time. GeoWrite and GeoDraw, for example, allow the user to work with multiple documents at once. Each document appears within its own window; each of these windows is a GenDisplay object within a Display Control object.

The Display Control objects, like the Document Control objects, allow applications to use multiple data files with a minimum of extra coding. The Display Control works closely with the Document Control to ensure that the proper data files are being operated on at all times. The Display Control also creates and maintains the Window menu, just as the Document Control creates and maintains the File menu.

| Up: GEOS SDK TechDocs | Up | Prev: 5.11 Managing Documents and Files | Next: 5.13 Clipboard and Quick-Transfer


Clipboard and Quick-Transfer

Overview: 5.13 The GEOS User Interface: Clipboard and Quick-Transfer

| Up: GEOS SDK TechDocs | Up | Prev: 5.12 Multiple Document Interface | Next: 5.14 General Change Notification

A common feature of GUIs is the Clipboard. Users can copy or cut data out of documents for pasting into other documents (or even the same document) later. When data is cut or copied, it is placed in a transfer item, a special data structure used by the Clipboard. All applications can access the Clipboard via Cut, Copy, and Paste; in addition, the Edit menu can be implemented automatically with the use of a GenEditControl object.

GEOS also offers another method of transferring data--the quick-transfer mechanism. In OSF/Motif, the user clicks the move/copy button (in most cases the right mouse button) over some selected data. He can then drag to another area and drop the selected data, either moving or copying it depending on certain contextual factors. Applications may or may not support the quick-transfer mechanism, though supporting it is not difficult and is strongly encouraged.

Both the Clipboard and the quick-transfer mechanism are supported automatically by all text edit and display objects. Applications may define their own custom data formats (transfer items) for use with both the Clipboard and the quick-transfer mechanism.

| Up: GEOS SDK TechDocs | Up | Prev: 5.12 Multiple Document Interface | Next: 5.14 General Change Notification


General Change Notification

Overview: 5.14 The GEOS User Interface: General Change Notification

| Up: GEOS SDK TechDocs | Up | Prev: 5.13 Clipboard and Quick-Transfer | Next: 5.15 Help Object

In a multithreaded system, one thread may change some information that another thread depends on. In this case, the thread that makes the change must notify all other users of the information that it has been changed and that they must update their status appropriately.

GEOS provides a mechanism for allowing all threads or objects to register for notification of certain events. For example, GeoManager is notified of all file system changes so it can update its display in real time. Although notification is automatically sent for certain system changes, applications can set up an automatic notification of custom changes. For example, if a multiuser network game depended on each user knowing a certain status flag, automatic notification could be set up to notify all the users whenever any user changed it.

| Up: GEOS SDK TechDocs | Up | Prev: 5.13 Clipboard and Quick-Transfer | Next: 5.15 Help Object


Help Object

Overview: 5.15 The GEOS User Interface: Help Object

| Up: GEOS SDK TechDocs | Up | Prev: 5.14 General Change Notification | Next: 6 System Services

On-line help is a convenient and powerful way to provide documentation to your users. Because graphical user interfaces are easy to understand, many users do not read documentation and prefer to discover an application's features on their own. On-line help can speed their learning process up.

GEOS provides an object that you can include in your User Interface that will automatically create a "Help" icon or menu item and display help text when that item is invoked. The help text displayed will be context-sensitive as defined by the application. The application simply has to provide the help text and a few other attributes of the help object, and the system will take care of displaying the help when the user requests it.

| Up: GEOS SDK TechDocs | Up | Prev: 5.14 General Change Notification | Next: 6 System Services


System Services

Overview: 6 System Services

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 The GEOS User Interface | Next: 7 Libraries

GEOS provides a number of services useful or necessary to applications. These services range from dynamic memory management to an item database to a sophisticated graphics system and a print spooler. Many of the most useful or integral services are described in this section.

Memory

Virtual Memory

Local Memory and Object Blocks

Item Databases

Graphics System

Text

Print Spooler and Printing

Timers

Communications

Math support

International Support

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 The GEOS User Interface | Next: 7 Libraries


Memory

Overview: 6.1 System Services: Memory

| Up: GEOS SDK TechDocs | Up | Prev: 6 System Services | Next: 6.2 Virtual Memory

GEOS uses all RAM available to the system, even expanded and extended memory. Memory is managed by the kernel and is accessed in the segmented scheme implemented by the 8086 processor. (The protected mode of the more advanced processors is not supported in the Version 2.0 system software but is expected to be in future releases.)

The GEOS Memory Manager is sophisticated. It uses dynamic allocation and access of blocks on a global heap to provide high performance, optimized to run very efficiently even on systems with only 640 K.

Handles

GEOS maintains control of all the memory, objects, and other entities in the system through the Handle Table . The Handle Table is a section of memory set aside for kernel use, and it contains a number of entries, each 16 bytes that can contain various information about many different items in the system. These entries are accessed by handles , 16-bit offsets into the Handle Table. Applications and libraries may use handles but may not access the contents of a handle table entry. The data structure is opaque to all but the kernel.

Handles are used for many different things. They can reference threads, memory blocks, VM blocks, or files; they can represent data structures such as timers, queues, semaphores, or events (messages); and they can be used by the kernel for optimization (such as when several words of data are passed with a message).

Through the Preferences application, a user can set the number of handles in the Handle Table. However, the size of the table does not change during a single execution of GEOS. Applications and libraries may allocate handles dynamically for most of the above-mentioned purposes (e.g. memory and file reference).

The Global Heap

A certain amount of memory is set aside for the file system (DOS, typically), the GEOS kernel (approximately 64 K), and any TSRs the user may have running on his system. Above this is the GEOS handle table. All RAM left (up to 1 megabyte) after these requirements are satisfied is used for the global heap (RAM over 1 megabyte is used as preferred swap space). The global heap is the space available to all the applications running in the system.

Applications allocate dynamic blocks of memory on the heap. Blocks may be of arbitrary size between 16 bytes and 64 K. Each block is designated a handle, an offset into the handle table where information about the block (such as size and location) is stored. Because blocks may be shuffled in the heap by the Memory Manager, applications must use handles as indirect references to the blocks; applications should not save pointers to specific locations in memory.

GEOS uses four basic types of blocks:

  • Fixed
    A fixed block will never move or swap out to disk. Having many fixed blocks can seriously degrade system performance. Fixed blocks are generally used for an application's global variables and primary code.
  • Moveable
    Moveable blocks may be shuffled around the heap. Moveable blocks may also be designated discardable and/or swapable (below). These blocks must be locked into memory before they can be accessed (see below).
  • Discardable
    Discardable blocks may be flushed whenever the Memory Manager requires more memory.
  • Swapable
    Swapable blocks may be swapped at any time by the Memory Manager. When a subsequent access is made to a swapped block, the Memory Manager will automatically read the block back into memory.

Allocating Memory

The GEOS kernel provides several routines to allocate memory. Some memory is allocated automatically, such as memory for code resources as they are loaded in. Other memory, however, must be allocated as it is needed during execution--for example, as the user types more and more text into a word processor. There are three basic ways to allocate memory on the heap:

  • MemAlloc()
     This routine allocates a new block on the heap for an application's use.
  • MemReAlloc()
     This routine reallocates a given block; this is useful for adding memory to a block already allocated.
  • malloc()
     Although use of malloc() may help in porting previous C code to GEOS, it is discouraged. The malloc() routine will allocate small amounts of memory within a fixed resource. Extensive use of malloc() leads to large, fixed blocks on the heap, degrading system performance.

When an application is done with a memory block, it can free the block with the routine MemFree() . This will allow the Memory Manager to free up that memory space and re-use the block's handle if required.

Accessing Memory

If a block is allocated as fixed, an application can use a far pointer to access any byte within the block. However, because fixed blocks are not always allocated in the same portion of memory each time an application is loaded, applications should not save pointers as state information.

Non-fixed blocks, however, can not be accessed by far pointers without locking them into their position in memory. MemLock() will take a block's handle and lock the block, thereby assuring that the Memory Manager will not move it in the middle of an access. MemLock() provides its caller with a far pointer to the block. When access to the block is finished, the thread that locked the block must call MemUnlock() , which marks the block as unlocked so it may once again be moved or swapped.

| Up: GEOS SDK TechDocs | Up | Prev: 6 System Services | Next: 6.2 Virtual Memory


Virtual Memory

Overview: 6.2 System Services: Virtual Memory

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 Memory | Next: 6.3 Local Memory and Object Blocks

GEOS implements a powerful virtual memory concept to accomplish both memory block swapping and data file storage. Virtual memory (VM) can be thought of as a disk-based heap--a VM file is segmented into blocks, each of which is designated a VM handle analogous to a global memory handle. When a VM block is required by an application, it is locked into memory from the file with a call to the kernel routine VMLock() , which loads the VM block into memory and locks it on the global heap.

VM is extremely useful for data file storage; indeed, the Document Control objects use it extensively.

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 Memory | Next: 6.3 Local Memory and Object Blocks


Local Memory and Object Blocks

Overview: 6.3 System Services: Local Memory and Object Blocks

| Up: GEOS SDK TechDocs | Up | Prev: 6.2 Virtual Memory | Next: 6.4 Item Databases

Often applications will want to store large numbers of very small data items. In these cases, it's cumbersome and inefficient to allocate a global handle and a block on the global heap. For these situations, GEOS offers the concept of local memory (LMem).

Local memory can be thought of as a mini-heap within a memory block. Each individual unit allocated in this mini-heap is known as a chunk , and each chunk is designated a chunk handle . Thus, to access any piece of information within an LMem block, you need to have the block's global handle and the item's chunk handle.

Chunks may be allocated or freed, and any chunk may be moved within an LMem heap at any time (e.g. when the LMem heap is compacted). Locked LMem heaps may have chunks shuffled only when space is allocated within the heap. Access to chunks is gained by locking the LMem block and dereferencing the chunk handle. Chunks may be resized, and bytes may be added to or removed from chunks.

LMem blocks are used primarily for two purposes: Database files and object blocks. Database files are managed by the Item Database Library (see below). Object blocks are used extensively by the UI and applications; object blocks store objects in memory and often are attached to VM files.

Each object in an object block occupies one chunk. Thus, the combination of global handle and chunk handle of an object is known as the Object Pointer (optr) of the object. The optr is used to identify the object uniquely in the system for all types of access.

| Up: GEOS SDK TechDocs | Up | Prev: 6.2 Virtual Memory | Next: 6.4 Item Databases


Item Databases

Overview: 6.4 System Services: Item Databases

| Up: GEOS SDK TechDocs | Up | Prev: 6.3 Local Memory and Object Blocks | Next: 6.5 Graphics System

The GEOS database routines allows applications to use LMem heaps and VM files together easily and cleanly. The database routines allows applications to add a layer of abstraction to their data files.

An application may allocate database files, logical groups of items within those files, and individual items within the groups. The database library automatically manages the LMem and VM blocks that implement the database, keeping them desirable sizes and moving them into and out of memory as needed.

Additionally, items may be allocated as "ungrouped," in which case the database manager will intelligently distribute these items among the various VM blocks in the database file for efficient access.

| Up: GEOS SDK TechDocs | Up | Prev: 6.3 Local Memory and Object Blocks | Next: 6.5 Graphics System


Graphics System

Overview: 6.5 System Services: Graphics System

| Up: GEOS SDK TechDocs | Up | Prev: 6.4 Item Databases | Next: 6.6 Text

The graphics system provided in the GEOS kernel is extremely powerful and is designed to make creating graphics simple and fast for applications. It includes some advanced concepts and state-of-the-art technology that make GEOS rise well above most other GUIs.

The Coordinate Space

The GEOS graphics system uses a single imaging model like that used by PostScript. Applications and libraries draw their graphics on a generic rectangular grid with a resolution of 72 points per inch. When drawing to the screen or a printer, the graphics system will translate the graphics commands to the highest resolution of the output device, ensuring true WYSIWYG output. This system allows programmers to specify their graphics in real-world coordinates without worrying about the size of various resolution devices.

Normal graphics coordinates are represented by signed, 16-bit integers. Normal documents can be up to about nine feet on a side. Additionally, the graphics system allows applications to use an extended coordinate system (called the large document model ) of signed, 32-bit integers. Large documents can be up to 900 miles on a side! Large documents, however, can incur a certain amount of additional overhead; if you do not need to use large documents, you probably should not.

Graphic States

To simplify complex drawing, the graphics system maintains a graphics state , or GState . GStates can be created or destroyed dynamically to allow several different GStates for a given window (only one may be active at any given moment, however).

The GState is essentially a data structure that contains all the relevant information about the current state of a window. The GState's information includes current color, drawing position, angle of rotation, scale, translation in the coordinate space, line and fill attributes, clipping paths, text attributes, and other items.

Graphics commands are issued relative to a particular GState. For example, the command GrDrawEllipse() must be passed a GState so the kernel knows exactly how to draw the ellipse; if the current GState is rotated 45 degrees and has green as its current line color, a green-outlined ellipse will be drawn rotated 45 degrees.

Programs may apply transformations to GStates--rotation, translation, or scaling. You can also define custom transformation matrixes to apply to your GStates if complex operations (e.g. shearing) are required.

Graphic Primitives and Graphic Objects

GEOS provides a complete set of graphics drawing primitives including lines, arcs, Bézier curves, splines, outline-defined text, rectangles, ellipses, polylines, polygons, and bitmaps.

Paths

A graphics path in GEOS is a continuous trail that defines the outline of an area. A path is an outline description of an arbitrarily shaped area, useful when an application must define a mathematically-precise shape. Paths may also be filled, combined with other paths, and scaled to any size without loss of resolution.

One example of the powerful application of paths is creation of arbitrary clipping regions. It is possible, for example, to clip drawings to an ellipse, to a bézier curve, or even to text.

Regions

Regions perform essentially the same function as paths--definition of an arbitrarily shaped area or clipping region. However, because regions are defined as resolution-dependent, they are typically used only for optimized drawing of UI gadgetry.

Graphics Strings

A graphics string (or GString ) is a collection of graphics commands; GStrings are useful for saving complex graphic operations and playing them back later. GStrings are created by calling a special routine and are then filled by executing graphics commands just as if you were drawing to the screen. GStrings may be pre-defined in source code as data resources to be played during execution.

GStrings are extremely useful for sharing graphic data between processes. For example, applications use GStrings to define their program icons, which may then be displayed by File Manager applications. The Clipboard supports the GString data format for cut, copy, and paste operations. GStrings are also flexible--they may contain comments and may be set up to be executed with parameters.

Bitmaps

Bitmaps are pixel-by-pixel images defined at a specific resolution; they are automatically scaled to match the resolution of the display. Bitmaps are used in general to define complicated pictures, usually with many colors.

GEOS allows the creation of bitmaps with specified resolutions. Bitmaps can be created off-screen and may be edited with standard graphics system commands. Bitmaps are used mainly as monikers of generic objects and as icons for applications and data files.

Color

GEOS allows applications to specify colors in two ways: with the system palette and with RGB values. The palette contains 256 entries, each of which is given a standard RGB value. If an application only uses 256 colors, it can modify the palette so it has the colors it needs. If an application uses more than 256 colors, it can specify any RGB value for any given graphics operation. The palette may also be expanded to support other color models. If the system palette is not sufficient, each application may create its own.

| Up: GEOS SDK TechDocs | Up | Prev: 6.4 Item Databases | Next: 6.6 Text


Text

Overview: 6.6 System Services: Text

| Up: GEOS SDK TechDocs | Up | Prev: 6.5 Graphics System | Next: 6.7 Print Spooler and Printing

Text in GEOS is handled by the Text Object, a sophisticated and powerful object that is the base for both the GenText and the VisText classes. The Text Object handles more situations than most applications will ever face; only powerful word processors and text-formatting programs may require more functionality. Those that require additional features can subclass the text object and add those features.

The Text Object implements text editing, manipulation, and display for GEOS. It represents a major portion of the system software and as such should be used by all applications that plan on providing any text-editing features. Among its many features are

  • Display and Text Wrapping Within Editable Bounds
    The Text Object is aware of its visible bounds and will wrap text appropriately to fit within the bounds. If the Text Object is given a set width, it will wrap text appropriately to that width.
  • "Editable" and "Selectable" Attributes
    The Text Object allows users to select and edit text. It can also act as a display-only object or a display-with-select object.
  • Character and Paragraph Formatting
    Sophisticated and extensive formatting options are included with the Text Object such as: Font selection, text styles (underline, bold, italic, etc.), text size (from four to 792 points), adjustable track kerning, text color, justification, adjustable margins, tabs with settable features (such as leaders, lines, and justification), paragraph borders, paragraph background colors, and paragraph spacing. All of these formatting options are settable and changeable.
  • Full Keyboard Editing
    The Text Object implements all the features necessary for editing text with the keyboard.
  • Embedded Graphics
    The Text Object can hold embedded graphics and graphics strings and display them properly.
  • Support for the Clipboard and Quick-Transfer
    The Text Object automatically supports the quick-transfer mechanism of the UI and handles the cut, copy, and paste commands of the Edit menu.
  • Many other powerful features
    For full feature listings and how to use the text object, see the Text chapter.
| Up: GEOS SDK TechDocs | Up | Prev: 6.5 Graphics System | Next: 6.7 Print Spooler and Printing


Print Spooler and Printing

Overview: 6.7 System Services: Print Spooler and Printing

| Up: GEOS SDK TechDocs | Up | Prev: 6.6 Text | Next: 6.8 Timers

GEOS contains a print spooler that manages printing for all applications in the system. The spooler executes in its own threads and manages a print output queue for each printer connected to the system. The spooler also provides standard user interface components such as the print dialog box. Applications can customize the print dialog box by adding their own gadgetry for certain types of printing (such as the customizations in GeoDex and GeoPlanner).

For the application, printing is essentially the same task as drawing to the screen; all the same graphics commands are used. The only difference is that the output is sent to a spool file rather than to the screen.

If the paper size in a user's printer is different from the size of the document being printed, the spooler will do all the calculations and operations necessary to print the document properly. For example, a document eleven inches by seventeen inches will be rotated and printed on two standard-sized sheets of paper.

| Up: GEOS SDK TechDocs | Up | Prev: 6.6 Text | Next: 6.8 Timers


Timers

Overview: 6.8 System Services: Timers

| Up: GEOS SDK TechDocs | Up | Prev: 6.7 Print Spooler and Printing | Next: 6.9 Communications

The GEOS kernel allows applications and libraries to set up different types of timers that can be used for various purposes. When a timer ticks, it calls a routine specified by the geode that initiated it. The different types of timers that can be set up include

one-shot
A timer that executes the specified routine once.
continual
A timer that calls the routine once every given time period.
message
Either a one-shot or continual timer that sends a specified message rather than call a specified routine.
millisecond
A one-shot timer with millisecond accuracy.
sleep
A timer that puts the calling thread to sleep for a given length of time.
semaphore
A timer that provides a "time-out" for a given semaphore queue (if the thread does not gain the semaphore in the given length of time, it is removed from the queue).
| Up: GEOS SDK TechDocs | Up | Prev: 6.7 Print Spooler and Printing | Next: 6.9 Communications


Communications

Overview: 6.9 System Services: Communications

| Up: GEOS SDK TechDocs | Up | Prev: 6.8 Timers | Next: 6.10 Math support

Communications is becoming an increasingly important area of system services. GEOS provides programs with a number of means of communication, whether communicating with peripheral devices, other computers, or with another GEOS program.

  • IACP, the Inter-Application Communication Protocol provides a way to send highly-structured messages between applications. It is quite versatile, but not simple.
  • The PCCom library allows a GEOS device to exchange data and files with another device over a serial link. The other device must also have access to PCCom--as of this writing, this means that the device must be running either DOS or GEOS.
  • Streams are like one-way pipes that allow a thread to send data to or receive data from another thread or I/O port. Typically, streams are used when dealing with serial or parallel ports; however, they may also be used for communication between two threads. GEOS has a Stream Driver that manages input to and output from streams. Additionally, a Parallel Driver manages output to a parallel port, and a Serial Driver manages input from and output to a serial port.
  • The Socket library, not present on all GEOS systems, provides a socket communication API--an API analogous to WinSock or Unix sockets. It provides a simple interface for communicating between machines or between programs running on the same machine.
| Up: GEOS SDK TechDocs | Up | Prev: 6.8 Timers | Next: 6.10 Math support


Math support

Overview: 6.10 System Services: Math support

| Up: GEOS SDK TechDocs | Up | Prev: 6.9 Communications | Next: 6.11 International Support

GEOS has a number of routines that accomplish complex mathematical calculations. There are also routines that do matrix manipulation and complicated linear algebra operations. In addition, the system provides a library of routines to implement simple fixed-point math and complicated floating-point operations.

| Up: GEOS SDK TechDocs | Up | Prev: 6.9 Communications | Next: 6.11 International Support


International Support

Overview: 6.11 System Services: International Support

| Up: GEOS SDK TechDocs | Up | Prev: 6.10 Math support | Next: 7 Libraries

International markets are extremely important to Geoworks due to the large number of PCs in use in countries other than the United States. Therefore, localization of applications was made a priority when designing the system software.

GEOS provides a number of tools for localizing your code. The most important is the GEOS application ResEdit, which allows a programmer to edit data resources. When an application is programmed correctly, translation can easily be accomplished by a support team.

The GEOS kernel also contains a number of routines that allow applications to access different DOS character sets. These routines ensure that an English-language program will work on machines with different DOS character sets.

| Up: GEOS SDK TechDocs | Up | Prev: 6.10 Math support | Next: 7 Libraries


Libraries

Overview: 7 Libraries

| Up: GEOS SDK TechDocs | Up | Prev: 6 System Services | Next: 8 Device Drivers

The use of dynamically-linkable libraries is key to the efficiency of GEOS applications. Several libraries are included in addition to the libraries provided by the kernel (such as the Graphic Object Library and the Database Library, discussed earlier):

  • Import/Export Library
    The Impex Library ("impex") contains a number of utilities to convert standard data files into GEOS data files and vice versa.
  • Sound Library
    GEOS provides a number of standard sounds and a small library ("sound") to play individual notes on the PC's internal speaker.
  • Ruler Object Library
    The Ruler library ("ruler") provides a ruler that applications can use along with a View. It can be set to different measurement units and scales and scrolls itself along with the scale and position of the View.
  • Cell Library
    The Cell Library ("cell") provides the lowest-level data manipulation and access routines required by a typical database or spreadsheet application.
  • Parse Library
    The Parse Library ("parse") provides a parser for mathematical expressions.
  • CD ROM Library
    The CD ROM Library ("cdrom") provides all the routines necessary to interface with standard Microsoft CD ROM extensions.
| Up: GEOS SDK TechDocs | Up | Prev: 6 System Services | Next: 8 Device Drivers


Device Drivers

Overview: 8 Device Drivers

| Up: GEOS SDK TechDocs | Up | Prev: 7 Libraries

GEOS does as much as possible to isolate applications from the hardware in use. Not only does this allow applications to run with thousands of different system configurations, but it also allows them to work properly with any new technology not yet developed--when a new device is introduced and a driver written for it, applications will automatically work with it (in most cases).

Device drivers are used by the kernel for all of the following:

  • Video Cards
    Most video cards with graphics are supported including HGC, CGA, EGA, VGA, and Super VGA.
  • Keyboards
    International keyboards are supported for most languages using available character sets.
  • Pen Input Devices
    New input devices such as digitizers, touch screens, and other types of devices are or can be easily supported by drivers that translate the input into standard mouse-and-keyboard style input.
  • Printers
    Standard 9-pin and 24-pin dot matrix printers as well as most laser printers are supported. PostScript output is also supported.
  • Mice
    Dozens of different mice are supported.
  • Serial and Parallel Ports
    Serial and parallel ports are driven by special types of Stream Drivers and can be used to create fax and other peripheral drivers.
  • File Systems
    File systems (MS-DOS and DR DOS, for example) are supported through file system drivers. Future releases of file systems and alternatives will be supported through the addition of new file system drivers.
  • Task Switchers
    GEOS interacts with DOS task-switchers through task switch drivers.
  • Power Management Hardware
    Many portable computers (notebooks and palmtops) use power management hardware to extend the life of their batteries. GEOS works with the power management systems through a power management system driver.
  • Font Rasterizers
    GEOS uses a combination of outline and bitmap fonts. Different font engines are supported through font rasterizers.
| Up: GEOS SDK TechDocs | Up | Prev: 7 Libraries


Tutorial

| Up: GEOS SDK TechDocs | Up | Down | Next: Tutorial Part 1: Compiling
| One-page Version of this Chapter for Easy Printing

This chapter shows how you might get started writing a simple GEOS program, and it explains several fundamental GEOS programming concepts as it goes along. Although this tutorial explicitly refers to the Nokia 9000 SDK version 1.1, it is also applies to the Nokia 9000i SDK version 2.0.

| Up: GEOS SDK TechDocs | Up | Down | Next: Tutorial Part 1: Compiling
| One-page Version of this Chapter for Easy Printing


Tutorial Part 1: Compiling

| Up: GEOS SDK TechDocs | Up | Next: Tutorial Part 2: A New Application

Before jumping into writing your own application, it might be useful to compile and run an existing sample application. This chapter will take you through the generic steps of checking out a source file, compiling it, then running the compiled application on the emulator with the Swat debugger attached. The next two chapters explore GEOS development in greater detail by stepping through every step in creating the application "Tutorial". We recommend that you step through this chapter to acquaint yourself with the generic process of compiling an application, then carefully follow the tutorial to get a flavor for GOC, Swat debugging, and Geode Parameter files.

Typically, your development sequence will follow these steps:

  1. Check out a file by copying it to your Working Branch. This is your private development area. By doing your work here, the Branch files remain untouched (see "Developing In Your Working Branch" for more information);
  2. Create a Makefile using the mkmf utility;
  3. Compile your application using the pmake utility;
  4. Run the application on the error-checking version of the target emulator;
  5. Debug your application using the Swat debugger; and
  6. Edit your source code and go back to Step 3.

The next section follows this sequence using the Dialogs sample application, though of course the same steps apply for any sample application. (Refer to the release notes in the README.TXT file or the Troubleshooting guide in the "Installing the SDK" chapter if you encounter problems.)

In the directory paths below, prepend your correct drive letter or path to PCGEOS and replace "workingdir" as appropriate. Also note that if you are using the Nokia 9000i SDK version 2.0, the directory labled here as N9000v11 will be N9000v20 in your development branch.

  1. Install the SDK by following the instructions in the "Installing the SDK" chapter;
  2. Start a command window.
  3. Go to the \APPL\ subdirectory of your Working Directory:
    4. CD PCGEOS\workingdir\APPL
  4. Create and go to the subdirectory \SDK_9000\DIALOGS\:
    5. MD SDK_9000\DIALOGS
CD SDK_9000\DIALOGS
  5. Copy the file DIALOGS.GOC from the N9000v11 Branch to this directory:
    6. COPY \PCGEOS\N9000V11\APPL\SDK_9000\DIALOGS\DIALOGS.GOC
  6. Type mkmf to create the makefile.
  7. Type pmake to compile and link the executable. This will create an error-checking version called DIALOGSEC.GEO .
  8. Launch a wait-for-swat EC demo. You can do this from the GEOS SDK program menu if the shortcuts were installed; otherwise, CD to \PCGEOS\workingdir\TARGET\N9000.EC, then type start ss .
  9. Launch Swat. You can also do this from the GEOS SDK program menu; otherwise, type start pcgeos\bin\swat . (The full path is only necessary if there is also a Swat stub in your current directory, which you do not want to run.)
    • If Swat pauses with a top-level prompt (=>), type c to continue.
    • If Swat cannot file a symbol file, press Ctrl-W to clear the "Where is it" default; then type i (lower-case) to ignore that file.
    • If Swat is searching a very long time for a particular symbol file (Looking for "such-and-such"...), you can abort the search by typing Ctrl-C , then using the Ctrl-W , i sequence described above to ignore the file.
  10. When the emulator has finished loading (usually after "serial" exits), press Ctrl-C in the Swat window to bring command control back to Swat.
  11. Copy your geode (your compiled executable) to the target emulator by typing send dialogs .
  12. Type c to continue Swat.
  13. Click inside the emulator window to regain focus in that window. Press Ctrl-F12 to launch the Extras folder.
  14. Use the arrow keys to highlight the EC Dialogs application. Press F1 to launch it. (The keys F1 - F4 correspond to the menu list on the right of the emulator window.)
  15. Play with the application.
  16. Select the Swat window, press Ctrl-C to bring command control back to Swat, then type quit to exit the emulator.

In this section, you learned how to check out a source code file by copying it from the Branch directory to your Working Directory, and then compile and run it on the emulator. Notice that it was not necessary to copy all the files from the \DIALOGS\ subdirectory in the N9000v11 Branch. When compiling and linking, the tools are smart enough to look for files in your Working Directory first and then look for whatever other files it needs in the Branch directory. This allows you to copy only the file or files you need to work on; shared files can remain in the Branch directory.

Now that you've gone through the basic steps of compiling and running a GEOS application, you can go on to the next chapter to create your own application from scratch.

| Up: GEOS SDK TechDocs | Up | Next: Tutorial Part 2: A New Application


Tutorial Part 2: A New Application

| Up: GEOS SDK TechDocs | Up | Up | Prev: Tutorial Part 1: Compiling | Next: Tutorial Part 3: Adding Procedural Code

In this tutorial, you will create a simple GEOS application that draws text and demonstrates the basic components of a GEOS application. You'll learn how to compile and link the application and explore its source code using the Swat debugger.

1 Creating "Tutorial"
1.1 Setting Up Your Working Directory
1.2 Compiling Your Source Code
1.3 Starting the Emulator and Swat
2 Learning GOC
2.1 TUTORIAL.GP: Geode Parameters File
2.2 TUTORIAL.GOC: Source Code
3 Exploring With Swat
4 Source Code

1: Creating "Tutorial"

The application that you'll be working on throughout this tutorial is called, appropriately enough, "Tutorial." In this section you'll create two files: TUTORIAL.GP and TUTORIAL.GOC. The .GP (or "geode parameters") file tells the linker how the application is organized; the .GOC file contains the actual source code. All GEOS applications require these two files.

1.1: Creating "Tutorial": Setting Up Your Working Directory

The first step in creating your application is to set up your Working Directory where you will do all your private development. Because your application is called "Tutorial," you need to create a \TUTORIAL\ subdirectory in your Working Directory to hold the source files (see "Developing In Your Working Branch" for more information).

  1. Create the \TUTORIAL\ subdirectory under \APPL\ in your Working Directory; for example: C:\PCGEOS\workingdir\APPL\TUTORIAL\ where "workingdir" refers to your personal working directory (usually the same as your login name).
  2. Type in the contents of TUTORIAL.GP and TUTORIAL.GOC. Put these files into the ...\TUTORIAL\ directory you just created. Typing source code by hand is often a good way to learn the feel for a language's grammar. If you don't want to type the source code by hand, follow the above links and copy the text into the files TUTORIAL.GOC and TUTORIAL.GP, as indicated.

1.2: Creating "Tutorial": Compiling Your Source Code

Now that you've created some source code, it's time to compile your application. First, you will need to create a Makefile and then run the "make" or compilation utility.

  1. Start up a command window and make sure you are in your Working Directory for this application; i.e., C:\PCGEOS\workingdir\APPL\TUTORIAL\.
  2. Type mkmf at the prompt; this utility creates a "makefile" which tells the pmake utility the steps with which to construct the application. It also creates a dependencies file, which tells pmake which files need to be remade whenever a source file has been modified.
  3. Type pmake to invoke the preprocessor, compiler, and linker to create the GEOS executable.

When you have completed these steps, your directory should contain some new files, including TUTORIALEC.GEO and your newly created GEOS executable or "geode." The "EC" in the name indicates that you've compiled the error-checking version of your application. The GEOS SDK tools have been set up with some optional compilation features to allow for error-checking code which is useful during development.

1.3: Creating "Tutorial": Starting the Emulator and Swat

Once you've successfully compiled your code, it's time to start up the GEOS emulator and attach Swat, the debugger.

  1. From the GEOS SDK program group, start the error-checking version of the emulator with Swat-wait.
  2. From the GEOS SDK program group, launch Swat (or type "swat" from a command prompt). The following should appear in the Swat window:
    3. Swat version 2.1 (May 28 1997 11:43:32).
Using the N9000v20 version of GEOS.
SDK version: ntsdk30
Looking for "loader"...c:/pcgeos/N9000v20/Installed/Loader/Text/loaderec.sym
Sourcing swat.tcl...Warning: Couldn't find the init file: c:\pcgeos\.swat
done
GEOS Attached
Stopped in LoadGeos, address 0a50h:0007h
LoadGeos:            CLD                        ;DF=0

(loader:0) 1 =>

    At this point, Swat is telling you that it has successfully established communications with the target emulator, has started the process of loading the GEOS kernel, and is now waiting for you to type in a command. Type c and press the <Enter> key to continue loading the GEOS emulator. The following will appear:

    (loader:0) 2 => c
Looking for "geos    Eker"...c:/pcgeos/N9000v20/Installed/Library/Kernel/N9000DE
MO_WIN32/geosec.sym
Looking for "os2     Eifs"...c:/pcgeos/N9000v20/Installed/Driver/IFS/DOS/OS2/N90
00/os2ec.sym
WARNING(os2::OS2Init): NOT_MONITORING_JFT
Looking for "netware Eifs"...c:/pcgeos/N9000v20/Installed/Driver/IFS/DOS/NetWare
/netwareec.sym
Looking for "respondrElib"...c:/pcgeos/N9000v20/Installed/Library/Respondr/N9000
DEMO/respondrec.sym
Looking for "rspwr   Edrv"...c:/pcgeos/N9000v20/Installed/Driver/Power/Rspwr/N90
00DEMO/rspwrec.sym
Thread 1 created for patient geos
Thread 2 created for patient geos
Looking for "vidmem  Edrv"...c:/pcgeos/N9000v20/Installed/Driver/Video/Dumb/VidM
em/vidmemec.sym
Looking for "swap    Elib"...c:/pcgeos/N9000v20/Installed/Library/Swap/swapec.sy
m
Looking for "disk    Edrv"...c:/pcgeos/N9000v20/Installed/Driver/Swap/Disk/diske
c.sym
Looking for "xms     Edrv"...c:/pcgeos/N9000v20/Installed/Driver/Swap/XMS/xmsec.
sym
Looking for "gdi     Elib"...c:/pcgeos/N9000v20/Installed/Library/GDI/GenPC/N900
0DEMO_WIN32/gdiec.sym
Looking for "gdiKbd  Edrv"...c:/pcgeos/N9000v20/Installed/Driver/Keyboard/GDI/gd
iKbdec.sym
Looking for "nimbus  Edrv"...c:/pcgeos/N9000v20/Installed/Driver/Font/Nimbus/nim
busec.sym
Looking for "stream  Edrv"...c:/pcgeos/N9000v20/Installed/Driver/Stream/streamec
.sym
Looking for "sound   Elib"...c:/pcgeos/N9000v20/Installed/Library/Sound/N9000/so
undec.sym
Looking for "standarddrvr"...c:/pcgeos/N9000v20/Installed/Driver/Sound/Standard/
N9000DEMO/standard.sym
Looking for "ui      Elib"...c:/pcgeos/N9000v20/Installed/Library/User/N9000/uie
c.sym
Thread 0 created for patient ui
Looking for "styles  Elib"...c:/pcgeos/N9000v20/Installed/Library/Styles/N9000/s
tylesec.sym
Looking for "color   Elib"...c:/pcgeos/N9000v20/Installed/Library/Color/N9000/co
lorec.sym
Looking for "ruler   Elib"...c:/pcgeos/N9000v20/Installed/Library/Ruler/N9000/ru
lerec.sym
Looking for "text    Elib"...c:/pcgeos/N9000v20/Installed/Library/Text/N9000/tex
tec.sym
Looking for "rtcm    Elib"...c:/pcgeos/N9000v20/Installed/Library/RTCM/rtcmec.sy
m
Looking for "ansic   Elib"...c:/pcgeos/N9000v20/Installed/Library/AnsiC/ansicec.
sym
Looking for "vp      Elib"...c:/pcgeos/N9000v20/Installed/Library/Foam/OEM/vp_st
b/vpec.sym
Looking for "foam    Elib"...c:/pcgeos/N9000v20/Installed/Library/Foam/Foam/foam
ec.sym
Looking for "foamdb  Elib"...c:/pcgeos/N9000v20/Installed/Library/Foam/DB/foamdb
ec.sym
Looking for "contdb  Elib"...c:/pcgeos/N9000v20/Installed/Library/Foam/Contdb/co
ntdbec.sym
Looking for "securitylib "...c:/pcgeos/N9000v20/Installed/Library/Foam/Security/
security.sym
Looking for "rudy    Espu"...c:/pcgeos/N9000v20/Installed/Library/SpecUI/Rudy/ru
dyec.sym
Looking for "simp4bitEdrv"...c:/pcgeos/N9000v20/Installed/Driver/Video/Dumb/Simp
4Bit/WIN32/simp4bitec.sym
WARNING(rudy::OLAppEnsureIndicatorCorrect): RUDY_INDICATOR_NOT_FOUND_IS_GEOS_BOO
TING_OR_SHUTTING_DOWN
Looking for "spool   Elib"...c:/pcgeos/N9000v20/Installed/Library/Spool/N9000/sp
oolec.sym
Thread 0 created for patient spool
Looking for "mailbox Elib"...c:/pcgeos/N9000v20/Installed/Library/Mailbox/N9000/
mailboxec.sym
Thread 0 created for patient mailbox
Looking for "nonts   Edrv"...c:/pcgeos/N9000v20/Installed/Driver/Task/NonTS/nont
sec.sym
Looking for "contlog Elib"...c:/pcgeos/N9000v20/Installed/Library/Foam/ContLog/c
ontlogec.sym
Looking for "indicatoEapp"...c:/pcgeos/N9000v20/Installed/Appl/FApps/OEM/indicat
o/indicate.sym
Thread 0 created for patient indicato
Looking for "faxfile Elib"...c:/pcgeos/N9000v20/Installed/Library/Fax/File/N9000
/faxfileec.sym
Looking for "viewer  Elib"...c:/pcgeos/N9000v20/Installed/Library/Foam/Viewer/vi
ewerec.sym
Looking for "clrfax  Egeo"...(cached)...c:/pcgeos/N9000v20/Installed/Appl/FApps/
ClrFax/clrfaxec.sym
Thread 0 created for patient clrfax
Looking for "math    Elib"...c:/pcgeos/N9000v20/Installed/Library/Math/mathec.sy
m
Looking for "ssset   Elib"...c:/pcgeos/N9000v20/Installed/Library/Foam/OEM/ssset
/sssetec.sym
Looking for "scm     Elib"...c:/pcgeos/N9000v20/Installed/Library/Foam/OEM/scm/s
cmec.sym
Thread 0 created for patient scm
Looking for "ota     Elib"...c:/pcgeos/N9000v20/Installed/Library/Foam/OEM/ota/o
taec.sym
Looking for "compose Elib"...c:/pcgeos/N9000v20/Installed/Library/Foam/OEM/compo
se/composee.sym
Looking for "accpnt  Elib"...c:/pcgeos/N9000v20/Installed/Library/AccPnt/N9000/a
ccpntec.sym
Looking for "borlandcElib"...c:/pcgeos/N9000v20/Installed/Library/Math/Compiler/
BorlandC/borlandcec.sym
Looking for "phone   Eapp"...c:/pcgeos/N9000v20/Installed/Appl/FApps/OEM/phone/p
honeec.sym
Thread 0 created for patient phone
Thread 1 created for patient indicato
viewer exited.
faxfile exited.
clrfax exited.
Thread 0 of clrfax exited 0
Thread 1 of indicato exited 0
Looking for "serial  Edrv"...c:/pcgeos/N9000v20/Installed/Driver/Stream/Serial/s
erialec.sym
serial exited.

    Each line that begins "Looking for..." indicates that the system is loading a geode (a GEOS executable) and that Swat is looking for its symbol information. Swat also tells you whenever it creates (or exits) a thread for any geode.

    Now that the emulator is running with Swat attached, you need to send your source code to the emulator to run it.

  3. Type Ctrl-C in the Swat window. At this point, the Swat prompt should appear:
    4. GEOS Halted
Stopped in TimerInterrupt, address 15a1h:c618h
TimerInterrupt+274:  STI
(geos:0) 2 =>
  4. Type send tutorial to copy the application to the emulator:
    5. (geos:0) 2 => send tutorial
Looking for EC version of tutorial...
(cached)...Sending file c:\pcgeos\janice\Appl\tutorial\TUTORIALEC.GEO to WORLD
\EXTRAPPS\TUTORIALEC.GEO
Bytes transfered: 1788/1788 (100%)
Send complete.
(geos:0) 3 =>
  5. Type run tutorial to run the application:
    6. (geos:0) 3 => run tutorial
Looking for application...
Allocating AppLaunchBlock
Force-queueing MSG_USER_LAUNCH_APPLICATION
Waiting for message...
Loading EXTRAPPS\TUTORIAL.GEO...
Looking for "tutorialEapp"...c:/pcgeos/janice/Appl/Tutorial/tutorialec.SYM
Thread 0 created for patient tutorial
Spawning tutorial...
(tutorial:0) 4 =>
  6. Type c to allow execution to continue on the emulator.

After you've followed these steps, your emulator should look like the screen pictured below.

Figure 1: The "Tutorial" application screen.

This is how your application will look after you type in the first pieces of code. Notice that it consists only of a blank viewing window and a "Close" button; later you will add code that displays text in this window.

As you can see, your application consists of an empty window. In the next chapter, you will add procedural code that draws a text string in this window.

2: Learning GOC

By typing in the contents of the source files (both the .GOC and .GP files), you've probably become a little familiar with GOC syntax. GOC is a proprietary superset of standard ANSI C. It supports C routines and functionality and adds its own set of keywords and special syntax. This section will cover basic GOC syntax, including the usage and meaning of various GOC keywords, by giving a line-by-line explanation of the code you entered.

2.1: Learning GOC: TUTORIAL.GP: Geode Parameters File

The .GP file tells the Glue linker about an application's general organization. (You can get complete information about all of the possible fields in a .GP file by reading the GP File Keywords reference.) Let's take a look at the code in TUTORIAL.GP to find out what each line means.

name tutorial.app

The name field specifies the "patient" name by which Swat will identify the application. Swat thinks of each geode as a patient, so you'll see phrases in Swat like "patient died".

longname "Tutorial Sample Application"

The longname field is the name the system displays for this application. For example, if you press Ctrl-F12 on the emulator (to list the applications in the "Extras" folder), you will see the longname for this application displayed. GEOS supports names up to 32 characters in length.

tokenchars "TUTO"
tokenid 8

Each application has its own unique identifier to the system. This identifier is a special GEOS data type called a GeodeToken. A GeodeToken is a data structure comprised of two fields:

tokenchars
A string of four characters (normally taken from the name of the application); and
tokenid
The Manufacturer ID for your company; this value is unique to your company and ensures that your applications' GeodeTokens do not conflict with those of other manufacturers. All sample applications, including this tutorial application, use a tokenid of 8; for your "real" applications, you should use your company's assigned Manufacturer ID. (Send email to orders@geoworks.com to receive a Manufacturer ID.)
type appl, process, single

The type line tells Glue whether your geode is an application, a driver or a library. Tutorial, like most GEOS applications, uses the following keywords:

  • appl which tells Glue that this geode is an application;
  • process which specifies that the geode should be run in its own thread;
  • single which specifies that only one copy of the program may run at a time.

Because GEOS is a multi-tasking or multi-threaded operating system, it allows more than one thread of execution to run at a time. This enables programs to run in the background. By specifying that your application has a process, you are requesting that it have its own thread of execution. If you were writing a library, you probably wouldn't want to give it a process, figuring that other geodes would be executing the library's code.

class TutorialProcessClass

The class line specifies which class will run the process thread. In this case, the TutorialProcessClass will be in charge of the process thread. (You will declare TutorialProcessClass in the TUTORIAL.GOC file.)

appobj TutorialApp

The appobj line specifies the "application object" for this application. The application object is the interface between your geode and the rest of the system. (You will declare this object in your source file as well.)

platform n9000v20

Glue will use the platform line to make sure that your application doesn't rely on any geodes that aren't present on the GEOS device.

heapspace 3K

The heapspace line specifies the maximum amount of memory the application requires on the global memory heap. Note that the value 3K is a wild guess. Once your application is closer to completion, you can use Swat's heapspace command to find out how much heap space the application really needs.

library geos
library ui

The library lines tell Glue which libraries to load for your application. All GEOS libraries are dynamically linked and loaded.

resource APPRESOURCE ui-object
resource INTERFACE ui-object

The resource lines specify the blocks of memory that your application uses for objects. Breaking up an application into resources allows it to run more efficiently because the system will only load those resources it needs.

The ui-object keyword signals that the UI thread should run the specified resource. Specifying a UI thread ensures that your application will have a UI thread and thus be dual-threaded: the process will run in one thread and UI objects will run in the UI thread. If you don't want the application to have two threads, use the keyword object instead of ui-object .

2.2: Learning GOC: TUTORIAL.GOC: Source Code

Now let's take a look at the source code in TUTORIAL.GOC . (For more information about GOC syntax, see the GEOS Programming chapter.)

@include <stdapp.goh>
@include <foam.goh>

The first lines of code contain the @include directive. Note the '@' symbol. This symbol is used to denote GOC keywords and distinguish them from regular C syntax. @include behaves the same way as #include, except that the inclusion will take place when the GOC preprocessor is processing the source code. Generally, you @include .GOH files and #include regular .H files.

@class TutorialProcessClass, GenProcessClass;
@endc;

The @class and @endc keywords mark the beginning and end of a new class definition. Here, you're defining TutorialProcessClass as a subclass of GenProcessClass , which means that TutorialProcessClass will inherit all the behavior of its parent, GenProcessClass .

@classdecl TutorialProcessClass, neverSaved;

Once you define the class, you need to declare it so that the system will know how to build objects of that class. To declare the class, you use the @classdecl keyword and because this is the process object, (a special object with no instance data), you also use the neverSaved flag. This flag tells the system that no instance data need be saved when the system shuts down.

 @start AppResource;

The @start keyword defines a resource or block of memory. In this case, you're defining the APPRESOURCE resource. In the . GP file, you may recall that this resource holds objects, and that those objects will be run by the UI thread.

@object FoamSubApplicationClass TutorialApp = {

The first object to be declared in the APPRESOURCE resource is the application object, TutorialApp . In the .GP file, you specified TutorialApp as the application object in the appobj line. The application object is responsible for receiving and handling all sorts of system messages and notifications. This is a nontrivial task so the object will be of a class specifically set up to handle this task: FoamSubApplicationClass . (Note, on GEOS systems other than the Nokia 9000i Communicator, you would use a GenApplicationClass; FoamSubApplicationClass is a specialized subclass of GenApplicationClass.)

Because the application object provides important information to the system, it needs to live in its own resource; that way, the system can query the application object by loading only that resource which contains it.

The next set of fields contains the application object's instance data. Instance data are characteristics of an object, as defined by the object's class. (This should make more sense as you read on.)

     GI_visMoniker = list{ @TutorialTextMoniker };

The GI_visMoniker field specifies the object's label or name that appears to the user (not to be confused with TutorialApp, the name of the object). The "visMoniker" part of the field name is short for "visual moniker." In GEOS parlance, a "visual moniker" is a text string or graphic used to identify an object to the user. This field is commonly set to a "list" of monikers; the system uses the moniker list to determine which text or graphic to display for your application.

      GI_comp = @TutorialPrimary;

The GI_comp field specifies the children of a generic UI object. In this case, the only child of the TutorialApp object is the TutorialPrimary object (which you declare later). (For a detailed description of how objects keep track of their children, see the GEOS Programming chapter.)

Notice that both instance data field names begin with "GI_." The "GI_" stands for "Gen Instance." Because GenApplicationClass is a subclass of GenClass , it inherits the GI_visMoniker field from its parent. By Geoworks' naming conventions, instance data field names begin with the initials of their associated class followed by an "I" for "instance." Thus, any GenApplicationClass instance field names would begin "GAI_."

You may be wondering about the syntax for @TutorialPrimary. @TutorialPrimary is actually a reference to the TutorialPrimary object. Object references have their own GEOS-specified type called an optr.

    gcnList( MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS ) =@TutorialPrimary;
}

This line puts the TutorialPrimary object,which is the primary window of your application, on a General Change Notification (GCN) list so that it will receive notification when a system-level change has occurred. (For more information about GCN lists, see the gcnList entry in the GEOS Programming chapter.)

This completes the declaration of the TutorialApp object.

@visMoniker TutorialTextMoniker = "My Tutorial";

This line contains the string to which the TutorialApp 's GI_visMoniker field refers. The @visMoniker keyword stores the string in a GEOS data structure set up strictly for monikers.

@localize "Program Title";

This @localize statement doesn't actually affect the program; it simply acts as a hint for someone translating the program to a foreign language. The ResEdit program displays the information in @localize statements when it prompts the translator to translate a moniker or string chunk. In this case, for example, when ResEdit prompts the translator to translate the string, "My Tutorial," it will display the string "Program Title" to assist with the translation.

@end AppResource

This line signals the end of the APPRESOURCE declaration.

@start Interface;

Here begins the next resource definition. This resource is called INTERFACE and will hold the basic UI gadgetry for your application.

@object GenPrimaryClass TutorialPrimary = {

The first object to be declared in this resource is TutorialPrimary , your application's primary window and an object of GenPrimaryClass .

    GI_comp = @TutorialView, @TutorialCloseTrigger;
}

The first instance data field is GI_comp , which specifies the children of TutorialPrimary .

@object GenViewClass TutorialView = {

The next object is a GenViewClass object called TutorialView ; this object provides space on the screen for objects to draw themselves.

    GVI_horizAttrs = @default |
        GVDA_NO_LARGER_THAN_CONTENT;
    GVI_vertAttrs  = @default |
        GVDA_NO_LARGER_THAN_CONTENT;
    GVI_content = process;
}

The GVI_content field specifies the top-level object which will appear in the view's drawing space. The GVI_horizAttrs and GVI_vertAttrs fields specify how the view should size itself in relation to the content object's size. Setting the flag GVDA_NO_LARGER_THAN_CONTENT ensures that the view will be the same size as its content object.

Notice that the GVI_...Attrs fields also use the @default keyword. @default tells the system to expand to the default values for this field (as defined by GenViewClass ); additional flags are ORed in with the single pipe ("|").

@object ComplexMonikerClass TutorialCloseTrigger = {
    ComplexMoniker = GenTriggerClass;
    CMI_topText = CMT_CLOSE;

The last object declared in the INTERFACE resource is TutorialCloseTrigger, an object of ComplexMonikerClass. TutorialCloseTrigger provides the "Close" button for your application.

The ComplexMoniker instance data field allows a sort of "multiple inheritance" in GEOS. ComplexMonikerClass defines behavior useful for many Communicator UI object types. GenTriggerClass defines behavior useful for buttons. GEOS multiple inheritance is complicated (you can learn more about it in the Goc syntax chapter); fortunately, it's easy to use it to set up this button--we just set the ComplexMoniker field as shown. The CMI_topText field supplies the text by which the button will identify itself to the user: "Close".

    GTI_destination = @TutorialApp;
    GTI_actionMsg = MSG_FSA_RETURN_TO_LAUNCHER;

The GTI_destination and GTI_actionMsg fields are GenTrigger instance data fields. GTI_actionMsg specifies the message that gets sent when the user presses the "Close" button; GTI_destination specifies the object who will handle the message. (We will discuss messages in more detail in the next chapter; suffice it to say that a message is a signal which may be handled by a set of procedural code defined for a class.)

    HINT_SEEK_MENU_BAR;
    HINT_SEEK_REPLY_BAR;
    HINT_SEEK_SLOT = 3;
}

These hints ensure that the button is placed on the right side of the screen so that it aligns with the buttons on the Nokia 9000i Communicator.

@end Interface;

This line signals the end of the INTERFACE resource definition.

3: Exploring With Swat

Now that you've successfully compiled your application, you can use Swat to "debug" it. In the Swat window, press Ctrl-C to halt the emulator and gain control back in the Swat window. Something similar to the following should appear in your Swat window:

GEOS Halted
Stopped in DOSIdleHook, address 277eh:1136h
DosIdleHook+17		MOVE	AX, 5760 (1680h)
(geos:0) 5 =>

"GEOS Halted" indicates that Swat has frozen the emulator. The next two lines of Swat output indicate the address of the current internal routine that is executing. If Swat stopped in a public routine, the address would have been given in relation to the routine name and would have been more readable.

The last line indicates the thread that was executing (on the emulator) when you hit Ctrl-C . In this example, the zeroth thread associated with the GEOS geode (the kernel) was executing. To examine the thread associated with your application, type "tutorial." (Recall that this is the value you set in the name field of your TUTORIAL.GP file [without the suffix].) This tells Swat which thread to switch to.

(geos:0) 5 => tutorial
[tutorial:0] 6 =>

The prompt indicates that you are now in the process thread of the tutorial geode. If you were in the UI thread, your prompt would read "[tutorial:1]." To change threads within a geode, simply type :0 or :1 .

Now try the where command to find out what this thread was doing when you stopped the system.

[tutorial:0] 6 => where
* 1: near BlockOnLongQueue(), 153dh:c22ah
2:  far QueueGetMessage(), 153dh:133ch
------------------------------------------------------------------------------
The event queue for "tutorial:0" is empty
==============================================================================
[tutorial:0] 7 =>

These lines tells you that the tutorial thread is idling; it's waiting for a message or something to happen.

Next, use the gentree command to examine your application's generic UI object tree.

[tutorial:0] 7 => gentree *TutorialPrimary
*TutorialPrimary::GenPrimary (@1, ^l5230h:001eh) "My Tutorial"
    *TutorialView::GenViewClass (@2, ^l5230h:0020h)
    ^143b0h:002ah(GenValueClass) (@3, ^15230h:002ah)
    *TutorialCloseTrigger(CM/GenTriggerClass) (@4, ^15230h:0024h) "Close"
[tutorial:0] 8=>

The gentree command prints out a list of all the objects in the tree, along with their class, object references, and monikers. For an example, let's look at the information for the TutorialView object:

  • GenViewClass is its class;
  • @2 is a shortcut for its object reference; and
  • ^l5230h:0020h is its object reference.

You can refer to an object by a shortcut that Swat provides, namely, the "@number." To look at an object's instance data, use the Swat command pobj followed by the "@number" designation for the particular object. Try the pobj command with the TutorialView object.

[tutorial:0] 8 => pobj @2
*TutorialView{TutorialViewClass} (@5, ^l53e0h:0020h)
master part: Gen_offset(131) -- TutorialViewInstance
@6: {TutorialViewInstance (^h21472:426)+131} = {
    MetaBase Gen = {
        ClassStruct _far *MB_class = 3a57h:183ah (rudy::CommonUIClassStructures:
:OLPaneClass)
    }
    LinkPart GI_link = {
        dword LP_next = 53e0h:0022h
    }
    CompPart GI_comp = {
        dword CP_firstChild = 53e0h:0028h
    }
    word GI_visMoniker = 0h
    word GI_kbdAccelerator = 0h
    byte GI_attrs = 2h
    byte GI_states = c0h
    PointDWFixed GVI_origin = {
        DWFixed PDF_x = {0.000000}
        DWFixed PDF_y = {0.000000}
    }
    RectDWord GVI_docBounds = {
        long RD_left = 0
        long RD_top = 0
        long RD_right = +1000
        long RD_bottom = +1000
    }
    PointDWord GVI_increment = {
        long PD_x = +20
        long PD_y = +15
    }
    PointWWFixed GVI_scaleFactor = {
        WWFixed PF_x = {1.000000}
        WWFixed PF_y = {1.000000}
    }
    ColorQuad GVI_color = {
        CQ_redOrIndex = fh, CQ_info = 0h, CQ_green = 0h, CQ_blue = 0h
    }
    word GVI_attrs = 810h
    byte GVI_horizAttrs = 98h
    byte GVI_vertAttrs = 88h
    byte GVI_inkType = 0h
    dword GVI_content = 4e60h:0000h
    dword GVI_horizLink = 0000h:0000h
    dword GVI_vertLink = 0000h:0000h
}
Variable Data:
         *** No Variable Data ***
[tutorial:0] 9 =>

We certainly got a lot of data from that command!

*TutorialView{TutorialViewClass} (@5, ^l5230h:0020h)

The first line gives the object's class, shortcut and pointer information, similar to the output from gentree. The rest of the output consists of the object's instance data. For example:

GenViewDimensionAttrs GVI_horizAttrs = 810h

This line show which flags (of type GenViewDimensionAttrs) are set for the object's GVI_horizAttrs instance data field.

You may recognize some of the instance data fields from your source code. Other fields you may not recognize because you didn't specify values for these fields, allowing the system to use default values instead.

With pobj, you took advantage of another Swat feature: command completion. There is no "pobj" Swat command; the command's name is really "pobject." Because "pobject" was the only Swat command that begins with p-o-b-j, Swat realized what you meant when you used "pobj."

Next let's try another exploratory Swat command:

[tutorial:0] 9 => classes
TutorialProcessClass (@10, 42b8h:0040h), off ui::UserClassStructures::GenProcessClass
[tutorial:0] 10 =>

The classes command lists all classes created by the application. To view the class hierarchy for a particular class, use the cup ("class up") command.

[tutorial:0] 10 => cup @10
dgroup::TutorialProcessClass (@11, 42b8h:0040h)
ui::UserClassStructures::GenProcessClass (@12, 30c4h:11e6h)
geos::kcode::ProcessClass (@13, 153dh:a9d5h)
geos::kcode::MetaClass (@14, 153dh:a8edh)
[tutorial:0] 11 =>

As shown above, you can use the shortcut reference (the "@number") with the cup command or you can use the class name or reference address, e.g., cup TutorialProcessClass or cup 42b8h:0040h.

Some other Swat commands you might want to try out:

help
The help command provides documentation on Swat commands. Try help stop .
apropos
The apropos command allows you to search the Swat documentation by keyword. Try apropos instance .
heapspace
The heapspace command tells you the amount of memory your application requires; you may recall that this value goes in your . GP file on the heapspace line. Because this value should reflect a worse-case scenario, it's important to run this command several times towards the end of development so that the figure is accurate.
handles tutorial
The handles command scans the entire memory heap and returns information about all handles used by the application.

In the next chapter, you'll begin by editing the source code, so prepare to switch over to your editor window.

| Up: GEOS SDK TechDocs | Up | Up | Prev: Tutorial Part 1: Compiling | Next: Tutorial Part 3: Adding Procedural Code


Tutorial Part 2 Source Code

| Up: GEOS SDK TechDocs | Up | Prev: Tutorial Part 2: A New Application | Prev: Tutorial Part 3: Adding Procedural Code

Here is the source code for the application discussed in this chapter. The source code is in two files:

  • TUTORIAL.GP, the Glue Parameters file
  • TUTORIAL.GOC, the Goc code

TUTORIAL.GP

# Permanent name: This is required by Glue to set the permanent name
# and extension of the geode. The permanent name of a library is what
# goes in the imported library table of a client geode (along with the
# protocol number). It is also what Swat uses to name the patient.
#
name     tutorial.app
# Long filename: this name can displayed by the system. "EC " is
# prepended to this when the error-checking version is linked by Glue.
#
longname "Tutorial Sample Application"
# Token: The four-letter name is used by the system to locate the
# icon for this application in the token database. The tokenid
# number corresponds to the manufacturer ID of the program's author
# for uniqueness of the token. Since this is a sample application, we
# use the manufacturer ID for the SDK, which is 8.
#
tokenchars "TUTO"
tokenid    8
# Specify geode type: This geode is an application, and will have
# its own process (thread).
#
type   appl, process, single
# Specify class name for application thread. Messages sent to the
# application thread (a.k.a. "process" when specified as the output
# of a UI object) will be handled by this process class.
#
class  TutorialProcessClass
# Specify application object. This is the object that serves as
# the top-level UI object in the application.
#
appobj TutorialApp
# Specify for which platform this application will be compiled.
# Glue uses this to make sure that the libraries we are using
# (listed below) are available for this platform.
# In this case, the specified platform is the Nokia 9000i Communicator
#
platform n9000v20
# Heapspace: This is roughly the non-discardable memory usage
# (in paragraphs (16 bytes/paragraph)) of the application and any
# transient libraries that it depends on, plus an additional
# amount for thread activity. To find the heapspace for an
# application, use the Swat "heapspace" command.
#
heapspace 3K
# Libraries: list which libraries are used by the application.
# The foam library is for the Nokia 9000i Communicator only.
#
library geos
library ui
library foam
# Resources: list all resource blocks which are used by the
# application whose allocation flags can't be inferred by Glue.
# Usually this is needed only for object blocks, fixed code
# resources, or data resources that are read-only. Standard
# discardable code resources do not need to be mentioned.
#
resource APPRESOURCE ui-object
resource INTERFACE   ui-object

TUTORIAL.GOC

/********************************************************************
 *		Include Files
 *******************************************************************/

@include <stdapp.goh>
@include <foam.goh>
/********************************************************************
 *		Class Definitions
 *******************************************************************/
    /*
     * Here we define "TutorialProcessClass" as a subclass of the
     * system provided "GenProcessClass". As this application is
     * launched, an instance of this class will be created, and
     * will handle all application-related events (messages). The
     * application thread will be responsible for running this
     * object, meaning that whenever this object handles a message,
     * we will be executing in the application thread.
     *
     * You will find no object in this file declared to be of this
     * class. Instead, it is specified as the class for the
     * application thread in "tutorial.gp".
     */

@class TutorialProcessClass, GenProcessClass;
    /*
     * Define messages for this class here. Most commonly, UI
     * objects will send these messages to the application thread
     * when they've been activated by the user.
     */

@endc; /* end of class definition */
/********************************************************************
 *		Class Declarations
 *******************************************************************/
    /*
     * Each object class requires a "class record" be stored in
     * fixed memory for it. The class record is used by the GEOS
     * message system for the delivery of messages sent to an
     * object of the class. The class record needs to be defined
     * in the file where the objects of that class are defined.
     * The @classdecl GOC directive tells GOC to create the class
     * record here.
     *
     * The neverSaved flag is necessary because ProcessClass
     * objects are hybrid objects. It tells the system that no
     * object of this class will ever be loaded from or stored to
     * a state file.
     */

@classdecl TutorialProcessClass, neverSaved;
/********************************************************************
 *		UI Object Resources
 *******************************************************************/
/********************************************************************
 *		AppResource Resource
 *******************************************************************/

@start AppResource;
/*
    *    Application Object
    *
    * The very top-level generic object of an application MUST be
    * a GenApplication object. The tutorial.gp file contains the "appobj"
    * statement which indicates that this "TutorialApp" object is in
    * fact the top-level UI object.
    *
    * This object should be in its own resource, as it is all that
    * need be in memory when the application is iconified. As such,
    * its resource should be as small as possible.
    *
    * FoamSubApplicationClass is for the Nokia 9000i Communicator
    * only. It adds special behavior for that platform.
    */

@object FoamSubApplicationClass TutorialApp = {

        /*
         * While the application object isn't shown on the screen,
         * it has a list of VisMonikers for three purposes:
         *   1) The GenPrimary (see below) inherits the moniker
         *      when it comes up on screen.
         *   2) When GeoManager needs to display an icon for the
         *      application, and there's none for it in the Token
         *      Database, it launches the app and sends a message
         *      to the application object to install its list of
         *      monikers in the token database under the
         *      application's token.
         *   3) When the GenPrimary is minimized, it uses the most
         *      appropriate graphical moniker from this list for the
         *      application's icon. If there's an abbreviated-text
         *      moniker in the list, that's used as the icon title.
         *      Else any regular-text moniker is used.
         */
    GI_visMoniker = list { @TutorialTextMoniker };
        /*
         * The GenApplication object has at least one child:
         * the main application window.
         */
    GI_comp = @TutorialPrimary;
        /*
         * The main window should appear on-screen when the application
         * is launched, so place it on the GAGCNLT_WINDOWS GCN list.
         * This causes the application object to bring it on-screen at
         * the appropriate time.
         */
    gcnList( MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS ) = @TutorialPrimary;
}
@visMoniker TutorialTextMoniker = "My Tutorial";
@localize "Program Title";
@end AppResource;
/********************************************************************
 *		Interface Resource
 * This resource is for miscellaneous UI objects.
 *******************************************************************/
@start Interface;
    /*
     *    Primary window
     *
     * Every application has at least one GenPrimary object.
     * This object serves as the main window of the application.
     * Inside this window, various UI components, such as menus
     * or lists, can be placed.
     */

@object GenPrimaryClass TutorialPrimary  = {
        /*
         * This window has two children:
         * the GenView object and the Close button (see below).
         */
    GI_comp = @TutorialView, @TutorialCloseTrigger;
}
    /*
     *    GenView object
     *
     * This GenView object creates a window where the application can
     * display portions of the document as necessary. As we want this
     * window to be scrollable, the specific UI will create scroll
     * bars with which the user can interact. Whenever a portion of
     * the window needs to be redrawn, the GenView object will
     * invalidate a portion of the window, causing a MSG_META_EXPOSED
     * to be sent to the application. The application will draw the
     * document into the window as requested. The window keeps track
     * of a mask, which is used to clip the application's drawing
     * operations, so that only the invalid portion of the window is
     * drawn to.
     */
@object GenViewClass TutorialView = {

        /*
         * Make sure the view never gets larger
         * than the thing being displayed within it.
         */
    GVI_horizAttrs = @default | GVDA_NO_LARGER_THAN_CONTENT;
    GVI_vertAttrs = @default | GVDA_NO_LARGER_THAN_CONTENT;

        /*
         * When the contents of this view (i.e. the document) must be
         * updated, the UI should send a MSG_META_EXPOSED to the
         * TutorialProcessClass object.
         */
    GVI_content = process;
};
    /*
     *    Close trigger
     *
     * On certain systems we should provide a way to exit the
     * application. On the Nokia 9000i Communicator we have a
     * close trigger like this one.
     */
@object ComplexMonikerClass TutorialCloseTrigger = {

    ComplexMoniker = GenTriggerClass;
    CMI_topText = CMT_CLOSE;

        /*
         * The next two instance fields are part of the
         * GenTriggerClass. They specify the recipient object
         * and the message that will be sent to that object
         * when this trigger is activated.
         */
    GTI_destination = @TutorialApp;
    GTI_actionMsg = MSG_FSA_RETURN_TO_LAUNCHER;

        /*
         * The specific UI uses this combination of hints
         * to place this trigger on the right side of the
         * Nokia 9000i Communicator's screen.
         */
    HINT_SEEK_MENU_BAR;
    HINT_SEEK_REPLY_BAR;
    HINT_SEEK_SLOT = 3;
}
@end Interface;
| Up: GEOS SDK TechDocs | Up | Prev: Tutorial Part 2: A New Application | Prev: Tutorial Part 3: Adding Code and Debugging with Swat


Tutorial Part 3: Adding Procedural Code

| Up: GEOS SDK TechDocs | Up | Down | Prev: Tutorial Part 2: A New Application | Next: Source Code With Changes

In this chapter, you'll add some procedural code to your Tutorial application which will draw a string of text to the viewing window. You'll explore the application's new code using Swat and learn how to set breakpoints and step through code.

1 Adding Code
2 TUTORIAL.GP Changes
3 TUTORIAL.GOC Changes
3.1 Subclassing the View
3.2 Adding Scrolling Behavior
3.3 Drawing the Content
4 Swat: Setting Breakpoints
5 Swat: Backtraces
6 Exercises Left for the Reader
7 Source Code

1: Adding Code

At the end of this chapter you will find a complete code listing for this application. Pieces of code which have been added or changed since the previous chapter are in bold. Follow these steps to edit, re-compile and run your new application.

  1. Add the new pieces of code using your favorite text editor. If you don't want to type it the source code by hand, follow the links to the revised Tutorial.gp and Tutorial.goc, then copy and paste the new code into tutorial.gp and tutorial.goc.
  2. In your Swat window, type exit tutorial to exit the earlier version of the application; (remember to use Ctrl+C to gain control back in your Swat window).
  3. In your command window, type pmake to compile the application (make sure you are still in your proper working directory). You may recall that in the previous chapter, you also had to use mkmf to create a makefile and a dependencies file. Those files will still work; we need only re-compile using pmake .
  4. Once you've successfully compiled, type send tutorial in your Swat window to send the newly compiled version to the emulator.
  5. Type run tutorial in the Swat window to run the new version of the application on the emulator.

Your code now lets your application have graphics in its view and allows the user to press the scroll buttons to scroll the view (see the image below). You have the beginnings of a typical GEOS application which displays its contents in a GenView object. (To learn more about views and displaying visual objects, see the \PCGEOS\APPL\SDK_C\VIEW\ and \PCGEOS\APPL\SDK_C\VIS\ sample applications.)

Image of N9000 screen with text running across screen.
Figure 1: "Tutorial" application with added code.

This is how your application will look after you type in the additional pieces of code. Notice that the main window is no longer blank; it now contains a string of text and a scrolling bar.

The next sections take a closer look at the code you've just added.

2: TUTORIAL.GP Changes

You added one line to your TUTORIAL.GP file.

export TutorialViewClass

TutorialViewClass is a new class that you'll define in your TUTORIAL.GOC file. Whenever you define a new class (with the exception of your process class), you'll need to export it in the .GP file so that Glue knows about the class when it builds the application.

3: TUTORIAL.GOC Changes

The changes to TUTORIAL.GOC include:

  • adding a new subclass of GenViewClass called TutorialViewClass so that you can add scrolling behavior to the view window;
  • intercepting the message MSG_META_KBD_CHAR in TutorialViewClass so that the user can scroll the window by pressing the arrow and PageUp/Down keys; and
  • intercepting MSG_META_EXPOSED so that the system knows how to redraw the window's contents whenever the contents change due to the user pressing the scroll button.

Before we look at the new pieces of code, let's discuss what we mean by messages. A message is simply a set of procedural code that gets called when a specific event occurs. For example, when the user presses a button, the system sends out a specific message to a specific object; by sending out a message, the system is calling the handler or method for that message.

A new class inherits the behavior and characteristics (messages and instance data) of its parent class. To add custom behavior to objects of your new class, either define your own message(s) for your new class or intercept inherited messages (which is what you're doing with your Tutorial application). To intercept a message, you simply write your own handler for it and the system will call that piece of code that when the message is sent.

Note that by Geoworks' naming conventions, all messages begin with "MSG_" followed by the name of the message's class. For example, MSG_META_EXPOSED and MSG_META_KBD_CHAR are both defined for MetaClass.

3.1: TUTORIAL.GOC Changes: Subclassing the View

The first step is to create a new subclass of GenViewClass.

@class TutorialViewClass, GenViewClass;
@endc;
@classdecl TutorialViewClass;

You use the @class and @endc keywords to define your new class and the @classdecl keyword to declare it.

@object TutorialViewClass TutorialView = { ...

Next, you declare TutorialView as an object of your new subclass, TutorialViewClass (instead of GenViewClass).

    GVI_horizAttrs  = @default |
        GVDA_SCROLLABLE |
        GVDA_NO_LARGER_THAN_CONTENT;
    GVI_vertAttrs  = @default |
        GVDA_SCROLLABLE |
        GVDA_NO_LARGER_THAN_CONTENT;

Setting the GVDA_SCROLLABLE flag in the view's GVI_...Attrs fields mkaes the content scrollable.

    GVI_docBounds = { 0, 0, 1000, 1000 };

The GVI_docBounds field sets the dimensions of the scrollable area. The first two coordinates are the top and left bounds; the last two coordinates are the right and bottom bounds.

3.2: TUTORIAL.GOC Changes: Adding Scrolling Behavior

To add scrolling behavior, TutorialViewClass needs to intercept MSG_META_KBD_CHAR.

@method TutorialProcessClass, MSG_META_KBD_CHAR {
...
}

The method for this message is straightforward. It tests to make sure the key pressed is either an arrow or PageUp or PageDown key. It then takes the key press value and handles it through a switch statement.

@send self::MSG_GEN_VIEW_SCROLL...();

Notice the syntax for sending a message programmatically. The @send keyword causes the system to place the message in the recipient's event queue. In this case, the recipient object is the calling object. In other words, TutorialView is sending a message to itself. The keyword for sending a message to yourself is, appropriately enough, self; if you were sending the message to another object, you would use the name of the recipient object instead. (@send is used when the message does not return a value; if the message returns a value or needs immediate handling, you would use the keyword @call. For more information on sending messages, see the GEOS Programming chapter.)

3.3: TUTORIAL.GOC Changes: Drawing the Content

To specify how the system should draw the content in your view, you need to intercept MSG_META_EXPOSED.

@method TutorialProcessClass, MSG_META_EXPOSED {

According to its documentation, MSG_META_EXPOSED takes one argument: the window handle of the window to draw to. In this case, that window is the view.

When you draw the contents of a view, you need to create a graphics data structure called a GState. A GState contains information about a window's graphics state or "gstate;" for example, its current drawing color, its current drawing font, etc.

    gstate = GrCreateState( win );

Once you've created a GState, you can begin drawing. The following code begins updating the view, then calls TutorialDraw() to draw some text to the view, then ends the update and destroys the GState :

    GrBeginUpdate( gstate );
    TutorialDraw( gstate );
    GrEndUpdate( gstate );
    GrDestroyState( gstate );

All drawing routines (those starting with Gr..()) take a GStateHandle as an argument.

4: Swat: Setting Breakpoints

Now that your application has some procedural code, you can use Swat to set breakpoints and step through the code line by line. By now, you should have Tutorial running on the emulator. Type Ctrl-C to regain control in the Swat window. After halting the target emulator you should see the following:

GEOS Halted
Stopped in DOSIdleHook, address 2522h:109dh
DOSIdleHook+16: INT 40 (28h)
(geos:0) 5 =>

Before setting a breakpoint in your code, you need to switch to your application's thread so that Swat will recognize your application's variable, class and function names. To do this, enter the name of your application's thread: tutorial .

(geos:0) 5 => tutorial
[tutorial:0] 6 =>

Next, you're going to set a breakpoint in the method for TutorialProcessClass' MSG_META_EXPOSED. The easiest way to set a breakpoint in a method is to first use the methods command which lists all methods for a class; the -p argument specifies the process class.

[tutorial:0] 6 => methods -p
  MSG_GEN_PROCESS_CLOSE_APPLICATION     (@2), TUTORIALPROCESSGEN_PROCESS_CLOSE_A
PPLICATION
                     MSG_META_EXPOSED     (@4), TUTORIALPROCESSMETA_EXPOSED

Now that you have a list of all the methods for the process class, you can refer to a particular message using the shortcuts Swat gives you. To set a breakpoint, you use the commands stop or brk.

[tutorial:0] 7 => stop @4
brk5
[tutorial:0] 8 =>

"brk5" indicates that you've sucessfully set the breakpoint. Swat should now halt whenever this routine is called. The number "5" indicates that this is your fifth breakpoint; you will be able to manipulate this breakpoint later by referring to it by this number. (Swat automatically sets breakpoints 1 through 4; we'll look at those later.)

To test the breakpoint, continue by typing c in the Swat window, then scroll the view on the target emulator. The system will tell the process object that it needs to redraw its content, and this should trigger your breakpoint:

[tutorial:0] 8 => c
Breakpoint 5
Stopped in TUTORIALPROCESSMETA_EXPOSED, line 382, "c:/pcgeos/Appl/Tutorial/TUTORIAL.
GOC"
    gstate = GrCreateState( win );
(tutorial:0) 9 =>

Swat tells you it broke at Breakpoint 5. (This information is useful when you've set several breakpoints.) Swat also shows you which statement in the program is about to be executed: gstate = GrCreateState( win ) .

Next, you'll use the srcwin command to display your source code.

(tutorial:0) 9 => srcwin 15
(tutorial:0) 10 =>

At this point, the bottom fifteen lines of your Swat window should display the source code; the highlighted line is the line about to be executed on the emulator. To the left side of the source code are line numbers.

To move around the source code file, use the Page Up , Page Down , left arrow , and right arrow keys. You can even set (and unset) breakpoints by clicking on the line numbers with the mouse.

Next you'll use the source-stepper command sstep to step through your source code line by line. At each line of code, you can step to the next line, step into a routine being called, or finish the routine.

(tutorial:0) 10 => sstep
Stepping in c:/pcgeos/Appl/Tutorial/TUTORIAL.GOC...
  382:    gstate = GrCreateState( win );

You are about to execute the GrCreateState() command. Press the n key.

  391:    GrBeginUpdate( gstate );

Notice that the highlight in the srcwin has moved to GrBeginUpdate() , the next line to be executed. Let's continue for another few steps and then get out of sstep mode. Press n three times to step through three more lines of code, then press q or the space bar to quit out of sstep mode.

  399:    TutorialDraw( gstate );
  405:    GrEndUpdate( gstate );
  406:    GrDestroyState( gstate );
(tutorial:0) 11 =>

To display a list of the variables local to a routine, use the locals command. To get a continuous display of local variables, use the localwin command (for example, localwin 15). You can use the print command to learn the value of any variable, and you can change the value of a variable using the assign command.

5: Swat: Backtraces

Backtraces are useful when you're figuring out where your program crashed. Though your program hasn't crashed, it's useful to test the where Swat command.

(tutorial:0) 11 => where
* 1:  far TUTORIALPROCESSMETA_EXPOSED(win = 52d0h), TUTORIAL.GOC:406
  3: call tutorial:0{TutorialProcessClass}MSG_META_EXPOSED(52d0h 0000h 0000h 0000h) (@
5, ^l52f0h:0000h)
------------------------------------------------------------------------------
The event queue for "tutorial:0" currently holds:
send tutorial:0{TutorialProcessClass}MSG_META_MOUSE_PTR(00d9h 0095h 0100h 0000h) (@6,
^l5370h:0000h)
send tutorial:0{TutorialProcessClass}MSG_META_KBD_CHAR(ff91h 0001h 5000h 0000h) (@7, ^
l5370h:0000h)
===================================================================================

From the first line, you can see that this thread is executing TUTORIALPROCESSMETA_EXPOSED() . The next line tells you that it is doing so to handle a MSG_META_EXPOSED which was sent to the process object.

The asterisk at line 1 indicates the current frame that Swat is examining. To examine another frame of the backtrace, use the frame command.

(tutorial:0) 12 => frame 3
CallCHandler+4: MOV BX, WORD SS:[12h]
(tutorial:0) 13 =>

At this point, the srcwin goes blank because Swat has switched to another level of examination for which you don't have source code. Note that frame doesn't change which code the target is executing, just which level Swat is looking at. If you use the where command again, you can see that the asterisk has moved to the second line. Type c to allow the program to continue; execution will then halt again since you still have a breakpoint set in MSG_META_EXPOSED.

(tutorial:0) 16 => c
Breakpoint 5
Stopped in TUTORIALPROCESSMETA_EXPOSED, line 382, "c:/pcgeos/Appl/SDK_C/TUTORIAL/TUTORIAL.
GOC"
    gstate = GrCreateState( win );
(tutorial:0) 17 =>

It would be nice to disable this breakpoint temporarily since you don't need it right now. Before you do that, use the brk list command to get a list of the current set of breakpoints:

(tutorial:0) 17 => brk list
Num S Address                        Patient    Command/Condition
1   E loader::kcode::LoaderError     all        echo Loader death due to [penum
LoaderStrings [read-reg ax]]
                                                  expr 1
2   E geos::kcode::FatalError        all
                                                  why
                                                  assign {word
 [address-kernel-internal errorFlag]} 0
                                                  expr 1
3   E geos::kcode::WarningNotice     all        why-warning
4   E geos::kcode::CWARNINGNOTICE    all        why-warning
5   E <T::TUTORIALPROCESSMETA_EXPOSED+4 all        halt
(tutorial:0) 18 =>

For each listed breakpoint, Swat displays where they break and what Swat commands which will be executed when the breakpoint is hit. The first breakpoint prints out an error message whenever you enter a routine called LoaderError() . The second breakpoint stops when execution has entered the GEOS kernel routine FatalError() and executes the why Swat command. Why returns an exiting error code and a string that pertains to the cause of the crash. Breakpoints 3 and 4 won't halt execution but will execute the why-warning Swat command, which echoes a warning message to the Swat screen. Swat sets these four breakpoints automatically. The fifth breakpoint is the one you set. It causes the system to halt when execution is at the specified address.

You can delete and disable breakpoints by using the brk delete and brk disable commands.

(tutorial:0) 18 => brk disable 5

Once you've disabled breakpoint 5, you can re-enable it by typing en 5 ("en" being short for "enable").

Other Swat commands you might want to try:

objwatch
shows you all messages sent to an object.
help
presents an interactive menu for retrieving help on all of the Swat commands.
alias
allows you to set up aliases for simple Swat commands.
save
saves the last 1000 lines of your Swat session to a text file. If you're encountering a crash bug in your program and enter a question to one of the GEOS development forums, you will often be asked to provide a backtrace of the crash. Use the where command to generate the backtrace and the save command to save it out.

6: Exercises Left for the Reader

The Tutorial sample application that you created in this Tutorial is very basic but should help introduce you to the basics of GOC and the SDK Tools. To continue learning more about the language and the tools, we encourage you to compile, run and Swat through any of the other sample applications on the SDK and/or try some of the following exercises:

  • Add a trigger that changes the size of the font or the angle of the text.
  • Change the content object from the process object to a VisContent object. Do this by creating a separate resource for the VisContent object and have the GenView object point to this new object.(See the \PCGEOS\APPL\SDK_C\VIS\ sample applications for reference.) Then intercept MSG_VIS_DRAW for the new content object and perform the drawing there.
  • Make the text string a VisClass object and add it as a child of the VisContent you created in the previous step. Have this Vis object intercept MSG_VIS_DRAW and perform the drawing there.

This tutorial and the exercises mentioned above should give you an introduction to the power and flexibility of GEOS. As you continue exploring the API and the tools, don't forget to take advantage of the many resources at hand: the SDK comes with a complete set of documentation and our web site features a searchable knowledgebase, an FAQ, and other self-help resources. Check out the Geoworks Developer Relations home page.


Tutorial Part 3 Source Code

| Up: GEOS SDK TechDocs | Up | Next Tutorial Part 3: Adding Procedural Code

Here is the source code for the application discussed in this chapter. Pieces of the code which have been added or changed from that in the previous chapter are called out in bold.

  • TUTORIAL.GP
  • TUTORIAL.GOC

TUTORIAL.GP

# Permanent name: This is required by Glue to set the permanent name
# and extension of the geode. The permanent name of a library is what
# goes in the imported library table of a client geode (along with the
# protocol number). It is also what Swat uses to name the patient.
#
name     tutorial.app
# Long filename: this name can displayed by the system. "EC " is
# prepended to this when the error-checking version is linked by Glue.
#
longname "Tutorial Sample Application"
# Token: The four-letter name is used by the system to locate the
# icon for this application in the token database. The tokenid
# number corresponds to the manufacturer ID of the program's author
# for uniqueness of the token. Since this is a sample application, we
# use the manufacturer ID for the SDK, which is 8.
#
tokenchars "TUTO"
tokenid    8
# Specify geode type: This geode is an application, and will have
# its own process (thread).
#
type   appl, process, single
# Specify class name for application thread. Messages sent to the
# application thread (a.k.a. "process" when specified as the output
# of a UI object) will be handled by this process class.
#
class  TutorialProcessClass
# Specify application object. This is the object that serves as
# the top-level UI object in the application.
#
appobj TutorialApp
# Specify for which platform this application will be compiled.
# Glue uses this to make sure that the libraries we are using
# (listed below) are available for this platform.
# In this case, the specified platform is the Nokia 9000i Communicator
#
platform n9000v20
# Heapspace: This is roughly the non-discardable memory usage
# (in paragraphs (16 bytes/paragraph)) of the application and any
# transient libraries that it depends on, plus an additional
# amount for thread activity. To find the heapspace for an
# application, use the Swat "heapspace" command.
#
heapspace 3K
# Libraries: list which libraries are used by the application.
# The foam library is for the Nokia 9000i Communicator only.
#
library geos
library ui
library foam
# Resources: list all resource blocks which are used by the
# application whose allocation flags can't be inferred by Glue.
# Usually this is needed only for object blocks, fixed code
# resources, or data resources that are read-only. Standard
# discardable code resources do not need to be mentioned.
#
resource APPRESOURCE ui-object
resource INTERFACE   ui-object
#
# Any classes that we define in our application must be exported
# here for glue to process them.
#
export TutorialViewClass

TUTORIAL.GOC

/********************************************************************
 *		Include Files
 *******************************************************************/
    @include <stdapp.goh>
    @include <foam.goh>
/********************************************************************
 *		Constants
 *******************************************************************/
      /*
       * These constants are used in the TutorialDraw function.
       */
    #define TEXT_POINT_SIZE  48.0 /* point size */
    #define TEXT_ROTATION   -15   /* angle of rotation (degrees) */
    #define TEXT_X_POSITION  30   /* x position, in document coords */
    #define TEXT_Y_POSITION   0   /* y position, in document coords */
/********************************************************************
 *		Class Definitions
 *******************************************************************/
      /*
       * Here we define "TutorialProcessClass" as a subclass of the
       * system provided "GenProcessClass". As this application is
       * launched, an instance of this class will be created, and
       * will handle all application-related events (messages). The
       * application thread will be responsible for running this
       * object, meaning that whenever this object handles a message,
       * we will be executing in the application thread.
       *
       * You will find no object in this file declared to be of this
       * class. Instead, it is specified as the class for the
       * application thread in "tutorial.gp".
       */
    @class TutorialProcessClass, GenProcessClass;
      /*
       * Define messages for this class here. Most commonly, UI
       * objects will send these messages to the application thread
       * when they've been activated by the user.
       */
    @endc; /* end of class definition */

      /*
       * We subclass GenViewClass here because we need to
       * intercept a message for our GenView object.
       */
    @class TutorialViewClass, GenViewClass;
    @endc; /* end of class definition */
/********************************************************************
 *		Class Declarations
 *******************************************************************/
      /*
       * Each object class requires a "class record" be stored in
       * fixed memory for it. The class record is used by the GEOS
       * message system for the delivery of messages sent to an
       * object of the class. The class record needs to be defined
       * in the file where the objects of that class are defined.
       * The @classdecl GOC directive tells GOC to create the class
       * record here.
       *
       * The neverSaved flag is necessary because ProcessClass
       * objects are hybrid objects. It tells the system that no
       * object of this class will ever be loaded from or stored to
       * a state file.
       */
    @classdecl TutorialProcessClass, neverSaved;
    @classdecl TutorialViewClass;
/********************************************************************
 *		UI Object Resources
 *******************************************************************/

/********************************************************************
 *		AppResource Resource
 *******************************************************************/
@start AppResource;

  /*
   *    Application Object
   *
   * The very top-level generic object of an application MUST be
   * a GenApplication object. The tutorial.gp file contains the "appobj"
   * statement which indicates that this "TutorialApp" object is in
   * fact the top-level UI object.
   *
   * This object should be in its own resource, as it is all that
   * need be in memory when the application is iconified. As such,
   * its resource should be as small as possible.
   *
   * FoamSubApplicationClass is for the Nokia 9000i Communicator
   * only. It adds special behavior for that platform.
   */
@object FoamSubApplicationClass TutorialApp = {
      /*
       * While the application object isn't shown on the screen,
       * it has a list of VisMonikers for three purposes:
       *   1) The GenPrimary (see below) inherits the moniker
       *      when it comes up on screen.
       *   2) When GeoManager needs to display an icon for the
       *      application, and there's none for it in the Token
       *      Database, it launches the app and sends a message
       *      to the application object to install its list of
       *      monikers in the token database under the
       *      application's token.
       *   3) When the GenPrimary is minimized, it uses the most
       *      appropriate graphical moniker from this list for the
       *      application's icon. If there's an abbreviated-text
       *      moniker in the list, that's used as the icon title.
       *      Else any regular-text moniker is used.
       */
    GI_visMoniker = list { @TutorialTextMoniker };

      /*
       * The GenApplication object has at least one child:
       * the main application window.
       */
    GI_comp = @TutorialPrimary;

      /*
       * The main window should appear on-screen when the application
       * is launched, so place it on the GAGCNLT_WINDOWS GCN list.
       * This causes the application object to bring it on-screen at
       * the appropriate time.
       */
    gcnList( MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS ) = @TutorialPrimary;
}

@visMoniker TutorialTextMoniker = "C Tutorial";
@localize "Program Title";

@end AppResource;
/********************************************************************
 *		Interface Resource
 * This resource is for miscellaneous UI objects.
 *******************************************************************/
@start Interface;

  /*
   *    Primary window
   *
   * Every application has at least one GenPrimary object.
   * This object serves as the main window of the application.
   * Inside this window, various UI components, such as menus
   * or lists, can be placed.
   */
@object GenPrimaryClass TutorialPrimary  = {
        /*
         * This window has two children:
         * the GenView object and the Close button (see below).
         */
    GI_comp = @TutorialView, @TutorialCloseTrigger;
}

  /*
   *    GenView object
   *
   * This GenView object creates a window where the application can
   * display portions of the document as necessary. As we want this
   * window to be scrollable, the specific UI will create scroll
   * bars with which the user can interact. Whenever a portion of
   * the window needs to be redrawn, the GenView object will
   * invalidate a portion of the window, causing a MSG_META_EXPOSED
   * to be sent to the application. The application will draw the
   * document into the window as requested. The window keeps track
   * of a mask, which is used to clip the application's drawing
   * operations, so that only the invalid portion of the window is
   * drawn to.
   */
@object TutorialViewClass TutorialView = {
      /*
       * Make view scrollable in X and Y and ensure that it never
       * gets larger than the thing being displayed within it.
       */
    GVI_horizAttrs = @default | GVDA_SCROLLABLE
                              | GVDA_NO_LARGER_THAN_CONTENT;
    GVI_vertAttrs  = @default | GVDA_SCROLLABLE
                              | GVDA_NO_LARGER_THAN_CONTENT;

      /*
       * When the contents of this view (i.e. the document) must be
       * updated, the UI should send a MSG_META_EXPOSED to the
       * TutorialProcessClass object.
       */
    GVI_content = process;

      /*
       * Specify the dimensions of the document being displayed
       * within the view.
       */

    GVI_docBounds = {
        0,
        0,
        1000,
        1000
    };
}
    /*
     *    Close trigger
     *
     * On certain systems we should provide a way to exit the
     * application. On the Nokia 9000i Communicator we have a
     * close trigger like this one.
     */
@object ComplexMonikerClass TutorialCloseTrigger = {
    ComplexMoniker = GenTriggerClass;
    CMI_topText = CMT_CLOSE;

        /*
         * The next two instance fields are part of the
         * GenTriggerClass. They specify the recipient object
         * and the message that will be sent to that object
         * when this trigger is activated.
         */
    GTI_destination = @TutorialApp;
    GTI_actionMsg = MSG_FSA_RETURN_TO_LAUNCHER;

        /*
         * The specific UI uses this combination of hints
         * to place this trigger on the right side of the
         * Nokia 9000i Communicator's screen.
         */
    HINT_SEEK_MENU_BAR;
    HINT_SEEK_REPLY_BAR;
    HINT_SEEK_SLOT = 3;
}
@end Interface;
/********************************************************************
 *		Function Definitions
 *******************************************************************/

/********************************************************************
 *              TutorialDraw
 ********************************************************************
 * SYNOPSIS:	 Redraws the entire document, given a graphics state
 *		 through which to draw.
 * CALLED BY:	 (INTERNAL) TutorialProcess::MSG_META_EXPOSED
 * RETURNS:      void
 * SIDE EFFECTS: Various aspects of the gstate are altered.
 * STRATEGY:     First set the font used by this gstate. Then
 *               set the text color. Using the constant angle lets
 *               rotate the gstate. To finish up, draw the text
 *               text to the gstate.
 *******************************************************************/
void

TutorialDraw( GStateHandle gstate )
  /*
   * gstate - GState to draw to.
   */
{

      /*
       * First change some of the default GState values,
       * such as font and point size.
       */
    GrSetFont( gstate, FID_DTC_URW_SANS, MakeWWFixed( TEXT_POINT_SIZE ) );

      /*
       * Apply a rotation to the transformation matrix,
       * so the text will be drawn at an angle.
       */
    GrApplyRotation( gstate, MakeWWFixed( TEXT_ROTATION ) );

      /*
       * Draw some text onto the document.
       */
    GrDrawText( gstate, TEXT_X_POSITION, TEXT_Y_POSITION,
                "The quick brown fox jumps over the lazy dog.",
                0 );
} /* TutorialDraw */
/********************************************************************
 *		Code for TutorialProcessClass
 *******************************************************************/

/********************************************************************
 *              MSG_META_EXPOSED
 ********************************************************************
 * SYNOPSIS:	 Redraw the recently-exposed portion of the View.
 *               See the Objects Reference book for more information
 *               on this message and its parameters.
 * PARAMETERS:	 WindowHandle win
 * RETURNS:      void
 * SIDE EFFECTS: The invalid region of the window is cleared out.
 * STRATEGY:	 This message is sent by the windowing system when a
 *	    	 portion of the GenView has become invalid, either
 *	    	 because a window that was obscuring it has been moved,
 *	    	 or because someone called GrInvalRect. We redraw the
 *               entire document, after telling the graphics system
 *               we're drawing to the invalid portion of the window.
 *
 *******************************************************************/

@method TutorialProcessClass, MSG_META_EXPOSED
{
      /*
       * gstate - This is a handle to the graphics state
       *          that we'll use to draw to the view.
       */
    GStateHandle gstate;

      /*
       * Get a default graphics state that we can use while drawing.
       */
    gstate = GrCreateState( win );

      /*
       * Start a window update. This tells the windowing system
       * that we are in the process of drawing to the invalid portion
       * of this window. GrBeginUpdate/GrEndUpdate are necessary only
       * when handling MSG_META_EXPOSED. For drawing at other times,
       * all you need do is create a graphics state and draw through it.
       */
    GrBeginUpdate( gstate );

      /*
       * Draw the pattern into the window (pass the GState). Breaking
       * the exposure and general drawing into two routines allows the
       * display to be updated by other routines without having to
       * invalidate the window.
       */
    TutorialDraw( gstate );

      /*
       * Now indicate that we are done drawing to the invalid area
       * and free the gstate.
       */
    GrEndUpdate( gstate );
    GrDestroyState( gstate );
} /* MSG_META_EXPOSED */

/* This is here to work around a bug in the Borland C 5.0 compiler. */
@method TutorialProcessClass, MSG_GEN_PROCESS_CLOSE_APPLICATION
{

    return( @callsuper() );
}
/********************************************************************
 *		Code for TutorialViewClass
 *******************************************************************/

/********************************************************************
 *              MSG_META_KBD_CHAR
 ********************************************************************
 * SYNOPSIS:     We intercept this message to add the scrolling
 *               behavior to the GenView. Normally this would work
 *               in GEOS but the Nokia 9000i Communicator has the
 *               scrolling disabled in GenViewClass.
 * PARAMETERS:   word character
 *               word flags
 *               word state
 * RETURNS:      void
 * SIDE EFFECTS: none
 * STRATEGY:     We first check the parameters to see if this
 *               keypress is one we are interested in. It has to be
 *               a "control" key, and must not be a key release
 *               (i.e. the user released the key after holding it).
 *               If both of these conditions are true we can then
 *               use a switch statement to see which key is being
 *               pressed and send the appropriate message to the
 *               GenView object.
 *******************************************************************/

@method TutorialViewClass, MSG_META_KBD_CHAR
{
      /*
       * theChar - A copy of the character with only the
       *           low byte. The high byte of character
       *           has some extra flags we don't need here.
       */
    byte        theChar;

      /*
       * Arrow keys and scroll buttons are control characters.
       * We handle first press and repeat presses of those keys.
       */
    if ( ( ( character >> 8 ) == CS_CONTROL ) &&
         ( flags & ( CF_FIRST_PRESS | CF_REPEAT_PRESS ) ) ) {
        theChar = character & 0xFF;

        switch ( theChar ) {

        case VC_PREVIOUS:
              /* This handles the Page Up key. */
            @send self::MSG_GEN_VIEW_SCROLL_PAGE_UP();
            break;

        case VC_UP:
              /* This handles the up arrow key. */
            @send self::MSG_GEN_VIEW_SCROLL_UP();
            break;

        case VC_NEXT:
              /* This handles the down arrow key. */
            @send self::MSG_GEN_VIEW_SCROLL_PAGE_DOWN();
            break;

        case VC_DOWN:
              /* This handles the up arrow key. */
            @send self::MSG_GEN_VIEW_SCROLL_DOWN();
            break;

        case VC_RIGHT:
              /* This handles the right arrow key. */
            @send self::MSG_GEN_VIEW_SCROLL_RIGHT();
            break;

        case VC_LEFT:
              /* This handles the left arrow key. */
            @send self::MSG_GEN_VIEW_SCROLL_LEFT();
            break;

        default:

              /*
               * Pass on other control chars to default handler.
               */
            @callsuper();
        } /* switch */
    } else {
          /*
           * Pass on all other chars to default handler.
           */
        @callsuper();
    }
} /* MSG_META_KBD_CHAR */
| Up: GEOS SDK TechDocs | Up | Next: Tutorial Part 3: Adding Procedural Code


C API Reference

GEOS SDK Documentation: C API Reference

| Up: GEOS SDK TechDocs

The following documents hold information about the GEOS C API.

Routines C API Reference
Alphabetical listing of many routines with declarations and descriptions.
Structures C API Reference
Alphabetical listing of many structures with declarations and descriptions.
GOC Language Keyword Reference
Alphabetical listing of Goc keywords with syntax and descriptions.
| Up: GEOS SDK TechDocs


GEOS C Routine Reference

| Up: GEOS SDK TechDocs | Up | Down | Next: AccessPointCommit() ...
| One-page Version of this Chapter for Easy Printing

GEOS C Routines Reference

This document contains reference information for most of the GEOS system routines. You may look up information in this and other GEOS SDK documentation by consulting the index. If, instead, you wish to browse this reference, you may choose one of the links below; the text of each is the name of the first routine documented on its page of the document.

AccessPointCommit() ...
AccessPointGetStringPropertyBlock() ...
ArrayQuickSort() ...
CCB() ...
CFatalError() ...
ChunkArrayDelete() ...
ChunkArrayElementToPtr() ...
ChunkArrayGetCountHandles() ...
ChunkArraySort() ...
ClipboardClearQuickTransferNotification() ...
ClipboardQueryItem() ...
ClipboardSetQuickTransferFeedback() ...
ConstructOptr() ...
ContactGetDBHandle() ...
ContactMatchNumber() ...
DataStoreCreate() ...
DataStoreDiscardRecord() ...
DataStoreGetFieldChunk() ...
DataStoreGetRecordCount() ...
DataStoreNewRecord() ...
DataStoreSetField() ...
DBAlloc() ...
DBDeref() ...
DBLock() ...
DiskCheckInUse() ...
DiskForEach() ...
DiskGetDrive() ...
DiskSave() ...
DriveGetDefaultMedia() ...
EC() ...
ECCheckHugeArray() ...
ECCheckProcessHandle() ...
ECVMCheckVMBlockHandle() ...
ElementArrayDelete() ...
ElementArrayRemoveReference() ...
FatalError() ...
FileCreate() ...
FileDeleteDir() ...
FileEnum() ...
FileEnumLocateAttr() ...
FileGetDiskHandle() ...
FileOpen() ...
FileRename() ...
FileSetHandleExtAttributes() ...
FileTruncate() ...
FloatFloatToAscii() ...
FoamCancelIfNoFreeSpace() ...
FoamDBAddFieldToRecord() ...
FoamDBGetCurrentRecordID() ...
FoamDBResumeUpdates() ...
FoamDisplayDeleteWarning() ...
FoamDocCheckProtocol() ...
FoamDocCreate() ...
FoamDocSetBlockHandle() ...
FoamGenerateUniqueFilename() ...
FoamStandardDialogOptr() ...
FoamStartSoundSequence() ...
FormatIDFromManufacturerAndType ...
GCNListAdd() ...
GCNListSend() ...
GenCopyChunk() ...
GeodeAllocQueue() ...
GeodeGetAppObject() ...
GeodeLoadDGroup ...
GeoFree() ...
GrApplyTranslation() ...
GrClearBitmap() ...
GrDestroyBitmap() ...
GrDrawCurve() ...
GrDrawLine() ...
GrDrawRect() ...
GrDrawRoundRect() ...
GrEndGString() ...
GrFillEllipse() ...
GrGetAreaColor() ...
GrGetDefFontID() ...
GrGetLineMask() ...
GrGetPtrRegBounds() ...
GrInitDefaultTransform() ...
GrNewPage() ...
GrSDivDWFByWWF() ...
GrSetClipPath() ...
GrSetLineAttr() ...
GrSetPalette() ...
GrSetTextMode() ...
GrTestPointInPath() ...
GrUDivWWFixed() ...
GSCloseSubPath() ...
GSDrawEllipse() ...
GSDrawSpline() ...
GSFillBitmap() ...
GSMoveTo() ...
GSSetCustomLineMask() ...
GSSetLineEnd() ...
GSSetSubscriptAttr() ...
HAL_COUNT() ...
HandleV() ...
HugeArrayDestroy() ...
HugeArrayPrev() ...
IACPProcessMessage() ...
ImpexExportToMetafile() ...
InitFileEnumStringSection() ...
InitFileReadStringBlock() ...
InitFileRevert() ...
InkDBGetDisplayInfo() ...
InkFolderGetNumChildren() ...
InkNoteGetCreationDate() ...
InkNoteSetModificationDate() ...
isalnum() ...
LMemAlloc() ...
LMemGetChunkSize() ...
LMemReAlloc() ...
LocalAsciiToFixed() ...
LocalCustomParseDateTime() ...
LocalGeosToCodePage() ...
LocalLexicalValue() ...
MailboxAcknowledgeMessageReceipt() ...
MailboxGetBodyFormat() ...
MailboxGetVMFile() ...
MakeWWFixed() ...
MemAlloc() ...
MemDowngradeExclLock() ...
MemLock() ...
MemModifyFlags() ...
MemThreadGrab() ...
MemUnlock() ...
ModemAnswerCall() ...
ModemSetMessageDataNotify() ...
NameArrayAdd() ...
NameArrayFind() ...
ObjCompFindChildByOptr() ...
ObjCompProcessChildren() ...
ObjDeref() ...
ObjDoRelocation() ...
ObjFreeChunk() ...
ObjInitDetach() ...
ObjIsClassADescendant() ...
ObjResizeMaster() ...
ObjVarAddData() ...
ObjVarDeleteDataAt() ...
ObjVarScanData() ...
ParallelClose() ...
PCCOMABORT() ...
qsort ...
realloc() ...
SerialGetFormat() ...
SGC_MACHINE ...
SocketBind() ...
SocketClose() ...
SocketGetAddressController() ...
SocketInterrupt() ...
SocketRemoveLoadOnMsg() ...
SoundAllocMusic() ...
SoundFreeMusic() ...
SoundPlayToMusicStream() ...
SpeedDialGetNumber() ...
SpoolGetNumPaperSizes() ...
SpoolModifyPriority() ...
StreamOpen() ...
StreamWrite() ...
SysGetPenMode() ...
SysShutdown() ...
TextSearchInString() ...
ThreadAttachToQueue() ...
ThreadGetError() ...
ThreadPSem() ...
TimerSleep() ...
TocNameArrayFind() ...
TokenDefineToken() ...
TokenLoadMonikerBuffer() ...
TokenLoadTokenChunk() ...
UserAllocObjBlock ...
UserRemoveAutoExec() ...
UtilAsciiToHex32() ...
VirtualSerialSetFormat() ...
VMClose() ...
VMGetMapBlock() ...
VMPreserveBlocksHandle() ...
WinAckUpdate() ...
WinGeodeSetInputObj() ...
WinScroll() ...

| Up: GEOS SDK TechDocs | Up | Down | Next: AccessPointCommit() ...
| One-page Version of this Chapter for Easy Printing


GEOS C Routines: AccessPointCommit() ...

| Up: GEOS SDK TechDocs | Up | Prev: GEOS C Routines: ... | Next: AccessPointGetStringPropertyBlock() ...

AccessPointCommit()

void AccessPointCommit();

This routine commits any access point data changes to permanent storage. The AccPnt library normally calls this routine when the device shuts down; applications thus need not call it. If you're trying to track down a bug which is likely to cause the system to crash before the changes can be committed, you may find this routine useful.

Include: accpnt.goh

AccessPointCompareStandardProperty()

void AccessPointCompareStandardProperty(
        AccessPointStandardProperty 					prop,
        char 					*str);

This routine compares the passed string to the name of the passed property.

Include: accpnt.goh

AccessPointCreateEntry()

word AccessPointCreateEntry(
        word 		loc,
        AccessPointType 		apt);

This routine creates a new access point.

After calling this routine, you will probably want to use the AccessPointSet...Property() routines to provide some information about the new access point.

Pass:

loc
The access point before which to insert the new access point. Pass zero to append the new access point to the end of the list.
apt
The AccessPointType of the new access point. APT_INTERNET for an ISP, APT_TERMINAL for a dialup, etc.

Return: ID number of the new access point.

Include: accpnt.goh

See Also: AccessPointSetStringProperty(), AccessPointSetIntegerProperty().

AccessPointDestroyEntry()

Boolean AccessPointDestroyEntry(
        word id);

This routine deletes an access point's information. If it returns non-zero, there was an error. (Perhaps the access point was locked or did not exist.)

Include: accpnt.goh

AccessPointDestroyProperty()

Boolean AccessPointDestroyProperty(
        word id,
        char *prop);

This routine destroys one of an access point's properties.

Pass:

id
The access point's ID number.
prop
Either an AccessPointStandardProperty value or a pointer to a buffer containing the property's name.

Return: Zero if there was no error, true if there was (no such access point, no such property, access point locked).

Include: accpnt.goh

See Also: AccessPointSetStringProperty(), AccessPointSetIntegerProperty().

AccessPointGetEntries()

ChunkHandle AccessPointGetEntries(
        MemHandle 		mh,
        ChunkHandle 		chunk,
        AccessPointType 		apt);

This routine gets a chunk array of all the access point IDs corresponding to access points of a given AccessPointType . Each element of the arraay is word-sized.

Pass:

mh
The memory block in which the chunk array shall reside.
chunk
Either the ChunkHandle of a chunk array, or zero to request that the library allocate a chunk array.
apt
The AccessPointType we're interested in: APT_INTERNET, APT_TERMINAL, etc.

Return: The ChunkHandle of the chunk array.

Include: accpnt.goh

AccessPointGetIntegerProperty()

Boolean AccessPointGetIntegerProperty(
        word 	id,
        char 	*prop,
        int 	*val);

This routine retrieves the value of one of an access point's property, representing the value as an integer.

Pass:

id
The access point's ID number.
prop
Either an AccessPointStandardProperty value or a pointer to a buffer containing the property's name.
val
Pointer to an integer; the routine will fill in this integer with the value of the property.

Return: Zero if there was no error, true if there was (no such access point, no such property).

Include: accpnt.goh

See Also: AccessPointGetStringPropertyBuffer(), AccessPointGetStringPropertyBlock().

| Up: GEOS SDK TechDocs | Up | Prev: GEOS C Routines: ... | Next: AccessPointGetStringPropertyBlock() ...


GEOS C Routines: AccessPointGetStringPropertyBlock() ...

| Up: GEOS SDK TechDocs | Up | Prev: AccessPointCommit() ... | Next: ArrayQuickSort() ...

AccessPointGetStringPropertyBlock()

BooleanAccessPointGetStringPropertyBlock(
        word 		id,
        char 		*prop,
        MemHandle 		*data,
        int 		*datalen);

This routine retrieves the value of one of an access point's property, returning the value as a string in a block of memory.

Pass:

id
The access point's ID number.
prop
Either an AccessPointStandardProperty value or a pointer to a buffer containing the property's name.
data
MemHandle of block to fill with value (pass zero to request that the block be allocated).
dataLen
Pointer to integer; the routine will fill in this integer with the length of the value string, excluding the trailing NULL.

Return: Zero if there was no error, true if there was (no such access point, no such property).

Include: accpnt.goh

See Also: AccessPointGetStringPropertyBuffer(), AccessPointGetIntegerProperty().

AccessPointGetStringPropertyBuffer()

Boolean AccessPointGetStringPropertyBuffer(
        word 	id,
        char 	*prop,
        char 	*buf,
        int 	*datalen);

This routine retrieves the value of one of an access point's property, returning the value as a string in a buffer of memory.

Pass:

id
The access point's ID number.
prop
Either an AccessPointStandardProperty value or a pointer to a buffer containing the property's name.
buf
Pointer to buffer to fill with value.
dataLen
Pointer to integer; pass the size of buf. The routine will fill in this integer with the length of the value string.

Return: Zero if there was no error, true if there was (no such access point, no such property).

Include: accpnt.goh

See Also: AccessPointGetStringPropertyBlock(), AccessPointGetIntegerProperty().

AccessPointGetType()

AccessPointType AccessPointCommit(
        word id);

This routine returns the type of an access point: APT_INTERNET, APT_TELNET, etc.

Include: accpnt.goh

AccessPointInUse()

Boolean   AccessPointInUse (word id);

If the access point with the ID id is being used by a connection, then this routine returns TRUE (non-zero); otherwise, it returns FALSE (zero).

Include: accpnt.goh

AccessPointIsEntryValid()

Boolean AccessPointIsEntryValid(
        word id);

This routine returns zero if the passed access point does not exist; it returns non-zero if it does exist.

Include: accpnt.goh

AccessPointLock()

void AccessPointLock(
        word id);

This routine locks an access point, preventing changes to it.

Include: accpnt.goh

See Also: AccessPointUnlock().

AccessPointSetIntegerProperty()

Boolean AccessPointSetIntegerProperty(
        word 	id,
        char+ 	*prop,
        int 	val);

This routine sets the value of one of an access point's properties to an integer.

Pass:

id
The access point's ID number.
prop
Either an AccessPointStandardProperty value or a pointer to a buffer containing the property's name.
val
The new value for the property.

Return: Zero if there was no error, true if there was (no such access point, access point locked).

Include: accpnt.goh

See Also: AccessPointSetStringProperty().

AccessPointSetStringProperty()

Boolean AccessPointSetStringProperty(
        word id,
        char *prop,
        char *val);

This routine sets the value of one of an access point's properties to a string.

Pass:

id
The access point's ID number.
prop
Either an AccessPointStandardProperty value or a pointer to a buffer containing the property's name.
val
The new value for the property.

Return: Zero if there was no error, true if there was (no such access point, access point locked).

Include: accpnt.goh

See Also: AccessPointSetIntegerProperty().

AccessPointUnlock()

void AccessPointUnlock(
        word id);

This routine unlocks an access point that was previously locked.

Include: accpnt.goh

See Also: AccessPointLock().

| Up: GEOS SDK TechDocs | Up | Prev: AccessPointCommit() ... | Next: ArrayQuickSort() ...


GEOS C Routines: ArrayQuickSort() ...

| Up: GEOS SDK TechDocs | Up | Prev: AccessPointGetStringPropertyBlock() ... | Next: CCB() ...

ArrayQuickSort()

void	ArrayQuickSort(
        void	*array,	/* Pointer to start of array */
        word	count,	/* Number of elements in array */
        word	elementSize,	/* Size of each element (in bytes) */
        word	valueForCallback, 		/* Passed to callback routine */
        QuickSortParameters *parameters);

This routine sorts an array of uniform-sized elements. It uses a modified QuickSort algorithm, using an insertion sort for subarrays below a certain size. The routine calls a callback routine to actually compare elements.

ArrayQuickSort() is passed five arguments: A pointer to the first element of the array, the number of elements in the array, the size of each element in bytes, a word of data (which is passed to all callback routines), and a pointer to a QuickSortParameters structure.

Before ArrayQuickSort() examines or changes any element, it calls a locking routine specified by the QuickSortParameters structure. This routine locks the element, if necessary, and takes any necessary prepatory steps. Similarly, after ArrayQuickSort() is finished with a routine, it calls an unlocking routine in the QuickSortParameters . Each of these routines is passed a pointer to the element and the word of callback data which was passed to ArrayQuickSort() .

The sort routine does not compare elements. Rather, it calls a comparison callback routine specified by the QuickSortParameters . This callback routine should be declared _pascal. Whenever ArrayQuickSort() needs to compare two elements, it calls the callback routine, passing the addresses of the elements and the valueForCallback word which was passed to ChunkArraySort() . The callback routine's return value determines which element will come first in the sorted array:

  • If element el1 ought to come before el2 in the sorted array, the callback routine should return a negative integer.
  • If element el1 ought to come after el2 in the sorted array, the callback routine should return a positive integer.
  • If it doesn't matter whether el1 comes before or after el2 in the array, the callback routine should return zero.

Include: chunkarr.h

Tips and Tricks: You may need to sort an array based on different criteria at different times. The simplest way to do this is to write one general-purpose callback routine and have the valueForCallback word determine how the sort is done. For example, the same callback routine could sort the array in ascending or descending order, depending on the valueForCallback .

Be Sure To: Lock the array on the global heap (unless it is in fixed memory).

Warnings: Do not have the callback routine do anything which might invalidate pointers to the array. For example, if the array is in a chunk, do not resize the chunks or allocate other chunks in the same LMem heap.

See Also: QuickSortParameters, ChunkArraySort().

BlockFromTransferBlockID

VMBlockHandle BlockFromTransferBlockID(id);
        TransferBlockID	 	id;

This macro extracts the VMBlockHandle from a TransferBlockID .

BlockIDFromFileAndBlock

TransferBlockID BlockIDFromFileAndBlock(file, block);
        VMFileHandle 		file;
        VMBlockHandle 		block;

This macro creates the dword type TransferBlockID from a VMFileHandle and a VMBlockHandle.

bsearch()

extern void *_pascal bsearch(
        const void 		*key,
        const void 		*array,
        word 		count,
        word 		elementSize,
        PCB(int, compare, (const void *, const void *)));

This is a standard binary search routine. The callback routine must be declared _pascal.

calloc()

void *	calloc(
        word		n,			/* number of structures to allocate */
        size_t		size);			/* size of each structure in bytes */

The malloc() family of routines is provided for Standard C compatibility. If a geode needs a small amount of fixed memory, it can call one of the routines. The kernel will allocate a fixed block to satisfy the geode's malloc() requests; it will allocate memory from this block. When the block is filled, it will allocate another fixed malloc-block. When all the memory in the block is freed, the memory manager will automatically free the block.

When a geode calls calloc() , it will be allocated a contiguous section of memory large enough for the specified number of structures of the specified size. The memory will be allocated out of its malloc-block, and the address of the start of the memory will be returned. The memory will be zero-initialized. If the request cannot be satisfied, calloc() will return a null pointer. The memory is guaranteed not to be moved until it is freed (with free() ) or resized (with realloc() ). When GEOS shuts down, all fixed blocks are freed, and any memory allocated with calloc() is lost.

Tips and Tricks: You can allocate memory in another geode's malloc-block by calling GeoMalloc() . However, that block will be freed when the other geode exits.

Be Sure To: Request a size small enough to fit in a malloc-block; that is, the size of the structure times the number of structures requested must be somewhat smaller than 64K.

Warnings: All memory allocated with calloc() is freed when GEOS shuts down.

See Also: malloc(),
free(),
GeoMalloc(),
realloc().

| Up: GEOS SDK TechDocs | Up | Prev: AccessPointGetStringPropertyBlock() ... | Next: CCB() ...


GEOS C Routines: CCB() ...

| Up: GEOS SDK TechDocs | Up | Prev: ArrayQuickSort() ... | Next: CFatalError() ...

CCB()

#define CCB(return_type, pointer_name, args) \
        return_type _cdecl (*pointer_name) args

This macro is useful for declaring pointers to functions that use the C calling conventions. For example, to declare a pointer to a function which is passed two strings and returns an integer, one could write

CCB(int, func_ptr, (const char *, const char *));

which would be expanded to

int _cdecl (*func_ptr) (const char *, const char *);

Different compilers have different syntax for the _cdecl keyword. Using this macro makes your callback compiler-independent.

See Also: PCB().

CellDeref()

void *	CellDeref(
        optr		CellRef);

This routine translates an optr to a cell into the cell's address. The routine is simply a synonym for LMemDeref() .

Include: cell.h

CellDirty()

void	CellDirty(
        void *		ptr);		/* pointer to anywhere in locked cell */

This routine marks a cell as "dirty"; i.e., the cell will have to be copied from memory back to the disk.

Include: cell.h

Tips and Tricks: All the cells in an item block are marked dirty at once; thus, you can call this routine just once for several cells in the same item block. Only the segment portion of the pointer is significant; thus, you can pass a pointer to anywhere in the cell. This is useful if you have incremented the pointer to the cell.

CellGetDBItem()

DBGroupAndItem 	CellGetDBItem(
        CellFunctionParameters *			cfp,
        word		row,			/* Get handles of cell in this row */
        byte		column);			/*...and this column */

All cells are stored as ungrouped DB items. If you wish to manipulate the cells with standard DB routines, you will need to know their handles. The routine is passed the address of the CellFunctionParameters and the row and column indices of the desired cell. It returns the DBGroupAndItem value for the specified cell. If there is no cell at the specified coordinates, it returns a null DBGroupAndItem . The routine does not lock the cell or change it in any way.

Include: cell.h

See Also: DBGroupAndItem.

CellGetExtent()

void	CellGetExtent(
        CellFunctionParameters *cfp,
        RangeEnumParams 		*rep); /* write boundaries in REP_bounds field */

This routine returns the boundaries of the utilized portion of the cell file. The routine is passed the address of the cell file's CellFunctionParameters structure.) It writes the results into the REP_bounds field of the passed RangeEnumParams structure. The index of the first row to contain cells is written into REP_bounds.R_top ; the index of the last occupied row is written to REP_bounds.R_bottom ; the index of the first occupied column is written to REP_bounds.R_left ; and the index of the last occupied row is written to REP_bounds.R_right . If the cell file contains no cells, all four fields will be set to -1..

Include: cell.h

CellLock()

void *	CellLock(
        CellFunctionParameters*			cfp,
        word			row,			/* Lock cell in this row... */
        word			column);			/* ... and this column */

This routine is passed the address of the CellFunctionParameters of a cell file, and the row and column indices of a cell. It locks the cell and returns a pointer to it.

Include: cell.h

See Also: CellLockGetRef().

CellLockGetRef()

void *	CellLockGetRef(
        CellFunctionParameters*			cfp,
        word			row,			/* Lock cell in this row... */
        word			column,			/* ... and this column */
        optr *			ref);			/* Write handles here */

This routine is passed the address of the CellFunctionParameters of a cell file, and the row and column indices of a cell. It locks the cell and returns a pointer to it. It also writes the locked cell's item-block and chunk handles to the optr. If the cell moves (e.g. because another cell is allocated), you can translate the optr structure into a pointer by passing it to CellDeref() .

Include: cell.h

Warnings: The optr becomes invalid when the cell is unlocked.

See Also: CellGetDBItem()., CellLock().

CellReplace()

void	CellReplace{
        CellFunctionParameters *			cfp,
        word		row,		/* Insert/replace cell at this row... */
        word		column,			/* ... and this column */
        const void *		cellData,			/* Copy this data into the new cell */
        word		size);			/* Size of new cell (in bytes) */

This routine is used for creating, deleting, and replacing cells in a cell file. To create or replace a cell, set cellData to point to the data to copy into the new cell, and set size to the length of the cell in bytes, and row and column the cell's coordinates. (As usual, cfp is a pointer to the cell file's CellFunctionParameters structure.) Any pre-existing cell at the specified coordinates will automatically be freed, and a new cell will be created.

To delete a cell, pass a size of zero. If there is a cell at the specified coordinates, it will be freed. (The cellData argument is ignored.)

Include: cell.h

Warnings: If a cell is allocated or replaced, pointers to all ungrouped items (including cells) in that VM file may be invalidated. The CellFunctionParameters structure must not move during the call; for this reason, it may not be in an ungrouped DB item. Never replace or free a locked cell; if you do, the cell's item block will not have its lock count decremented, which will prevent the block from being unlocked.

CellUnlock()

void	CellUnlock(
        void *	ptr); /* pointer to anywhere in locked cell */

This routine unlocks the cell pointed to by ptr . Note that a cell may be locked several times. When all locks on all cells in an item-block have been released, the block can be swapped back to the disk.

Include: cell.h

Tips and Tricks: The DB manager does not keep track of locks on individual items; instead, it keeps a count of the total number of locks on all the items in an item-block. For this reason, only the segment address of the cell is significant; thus, you can pass a pointer to somewhere within (or immediately after) a cell to unlock it. This is useful if you have incremented the pointer to the cell.

Be Sure To: If you change the cell, dirty it (with CellDirty() ) before you unlock it.

| Up: GEOS SDK TechDocs | Up | Prev: ArrayQuickSort() ... | Next: CFatalError() ...


GEOS C Routines: CFatalError() ...

| Up: GEOS SDK TechDocs | Up | Prev: CCB() ... | Next: ChunkArrayDelete() ...

CFatalError()

void	CFatalError(
        word	code)

This routine generates a fatal error. It stores an error code passed for use by the debugger.

ChunkArrayAppend()

void *	ChunkArrayAppend(
        optr	array,				/* optr to chunk array */
        word	elementSize)				/* Size of new element (ignored if
                         * elements are uniform-sized) */

This routine adds a new element to the end of a chunk array. It automatically expands the chunk to make room for the element and updates the ChunkArrayHeader . It returns a pointer to the new element.

One of the arguments is the size of the new element. This argument is significant if the array contains variable-sized elements. If the elements are uniform-sized, this argument is ignored. The array is specified with an optr.

Include: chunkarr.h

Be Sure To: Lock the block on the global heap (if it is not fixed).

Warnings: This routine resizes the chunk, which means it can cause heap compaction or resizing. Therefore, all existing pointers to within the LMem heap are invalidated.

See Also: ChunkArrayInsertAt(), ChunkArrayDelete().

ChunkArrayAppendHandles()

void *	ChunkArrayAppendHandles(
        MemHandle		mh,				/* Handle of LMem heap's block */
        ChunkHandle		ch,				/* Handle of chunk array */
        word		size)				/* Size of new element (ignored if
                             * elements are uniform-sized) */

This routine is exactly like ChunkArrayAppend() , except that the chunk array is specified by its global and local handles instead of by an optr.

Include: chunkarr.h

Be Sure To: Lock the block on the global heap (if it is not fixed).

Warnings: This routine resizes the chunk, which means it can cause heap compaction or resizing. Therefore, all existing pointers to within the LMem heap are invalidated.

See Also: ChunkArrayInsertAt(), ChunkArrayDelete().

ChunkArrayCreate()

ChunkHandle	 ChunkArrayCreate(
        MemHandle 	mh,	/* Handle of LMem heap's block */
        word	elementSize,	/* Size of each element (or zero if elements are
             * variable-sized) */
        word	headerSize,	/* Amount of chunk to use for header (or zero for
              * default size) */
        ObjChunkFlags ocf);

This routine sets up a chunk array in the specified LMem heap. The heap must have already been initialized normally. The routine allocates a chunk and sets up a chunk array in it. It returns the chunk's handle. If it cannot create the chunk array, it returns a null handle.

If the chunk array will have uniform-size elements, you must specify the element size when you create the chunk array. You will not be able to change this. If the array will have variable-sized elements, pass an element size of zero.

The chunk array always begins with a ChunkArrayHeader . You can specify the total header size; this is useful if you want to begin the chunk array with a special header containing some extra data. However, the header must be large enough to accommodate a ChunkArrayHeader , which will begin the chunk. If you define a header structure, make sure that its first element is a ChunkArrayHeader . Only the chunk array code should access the actual ChunkArrayHeader . If you pass a headerSize of zero, the default header size will be used (namely, sizeof(ChunkArrayHeader) ). If you pass a non-zero headerSize , any space between the ChunkArrayHeader and the heap will be zero-initialized.

To free a chunk array, call LMemFree() as you would for any chunk.

Include: chunkarr.h

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

Warnings: Results are unpredictable if you pass a non-zero headerSize argument which is smaller than sizeof(ChunkArrayHeader) . Since the routine allocates a chunk, it can cause heap compaction or resizing; all pointers to within the block are invalidated.

ChunkArrayCreateAt()

ChunkHandle 	ChunkArrayCreateAt(
        optr	array,	/* Create chunk array in this chunk */
        word	elementSize,	/* Size of each element (or zero if elements are
             * variable-sized) */
        word	headerSize,	/* Amount of chunk to use for header (or zero for
             * default size) */
        ObjChunkFlags ocf);

This routine is exactly like ChunkArrayCreate() , except that you specify the chunk which will be made into a chunk array. The chunk is specified with an optr. Note that any data already existing in the chunk will be overwritten.

Warnings: The chunk may be resized, which invalidates all pointers to within the LMem heap.

Include: chunkarr.h

ChunkArrayCreateAtHandles()

ChunkHandle 	ChunkArrayCreateAtHandles(
        MemHandle 		mh,
        ChunkHandle 		ch,
        word		elementSize,
        word		headerSize,
        ObjChunkFlags		ocf);

This routine is exactly like ChunkArrayCreate() , except that the chunk is specified with its global and chunk handles instead of with an optr.

Tips and Tricks: If you pass a null chunk handle, a new chunk will be allocated.

Warnings: The chunk may be resized, which would invalidate all pointers to within the LMem heap.

Include: chunkarr.h

| Up: GEOS SDK TechDocs | Up | Prev: CCB() ... | Next: ChunkArrayDelete() ...


GEOS C Routines: ChunkArrayDelete() ...

| Up: GEOS SDK TechDocs | Up | Prev: CFatalError() ... | Next: ChunkArrayElementToPtr() ...

ChunkArrayDelete()

void	ChunkArrayDelete(
        optr	array,				/* optr to chunk array */
        void *	element);				/* Address of element to delete */

This routine deletes an element from a chunk array. It is passed the address of that element, as well as the optr of the array.

Since the chunk is being decreased in size, the routine is guaranteed not to cause heap compaction or resizing.

Include: chunkarr.h

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

Tips and Tricks: Only the chunk handle portion of the optr is significant; the memory block is determined from the pointer to the element.

Warnings: The addresses of all elements after the deleted one will change. No other addresses in the block will be affected. If the address passed is not the address of an element in the array, results are undefined.

See Also: ChunkArrayAppend(),
ChunkArrayInsertAt(),
ChunkArrayZero().

ChunkArrayDeleteHandle()

void	ChunkArrayDeleteHandle(
        ChunkHandle		ch,				/* Handle of chunk array */
        void *		el);				/* Address of element to delete */

This routine is exactly like ChunkArrayDelete() , except that the chunk array is specified with its chunk handle instead of with an optr. The global memory handle is not needed, as the memory block is implicit in the pointer to the element.

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

Include: chunkarr.h

ChunkArrayDeleteRange()

void	ChunkArrayDeleteRange(
        optr	array,		/* optr to chunk array */
        word	firstElement,		/* index of first element to delete */
        word	count);		/* # of elements to delete (-1 to delete to
                 * end of array) */

This routine deletes several consecutive elements from a chunk array. The routine is passed the optr of the chunk array, the index of the first element to delete, and the number of elements to delete. The routine is guaranteed not to cause heap compaction or resizing; thus, pointers to other elements in the array will remain valid.

ChunkArrayElementResize()

void	ChunkArrayElementResize(
        optr	array,				/* optr to chunk array */
        word	element,				/* Index of element to resize */
        word	newSize);				/* New size of element, in bytes */

This routine resizes an element in a chunk array. The chunk array must have variable-sized elements. The routine is passed an optr to the chunk array (which must be locked on the global heap), as well as the index of the element to resize and the new size (in bytes). It does not return anything.

If the new size is larger than the old, null bytes will be added to the end of the element. If the new size is smaller than the old, bytes will be removed from the end to truncate the element to the new size.

Warnings: If the element is resized larger, the chunk array may move within the LMem heap, and the heap itself may move on the global heap; thus, all pointers to within the LMem heap will be invalidated.

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

Include: chunkarr.h

ChunkArrayElementResizeHandles()

void	ChunkArrayElementResizeHandles(
        Memhandle		mh,				/* Global handle of LMem heap */
        ChunkHandle		ch,				/* Chunk handle of chunk array */
        word		el,				/* Index of element to resize */
        word		ns);				/* New size of element, in bytes */

This routine is exactly like ChunkArrayElementResize() except that the chunk array is specified with its global and chunk handles, instead of with its optr.

Warnings: If the element is resized to larger than the old, the chunk array may move within the LMem heap, and the heap itself may move on the global heap; thus, all pointers to within the LMem heap will be invalidated.

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

Include: chunkarr.h

| Up: GEOS SDK TechDocs | Up | Prev: CFatalError() ... | Next: ChunkArrayElementToPtr() ...


GEOS C Routines: ChunkArrayElementToPtr() ...

ChunkArrayElementToPtr()

void *  ChunkArrayElementToPtr(
        optr           array,                                  /* optr to chunk array */
        word           elementNumber,                                  /* Element to get address of */
        void *         elementSize);                                   /* Write element's size here */

This routine translates the index of an element into the element's address. The routine is passed an optr to the chunk array, the index of the element in question, and a pointer to a word-sized variable. It returns a pointer to the element. If the elements in the array are of variable size, it writes the size of the element to the variable pointed to by the elementSize pointer. If the elements are of uniform size, it does not do this.

If the array index is out of bounds, the routine returns a pointer to the last element in the array. The routine will also do this if you pass the constant CA_LAST_ELEMENT.

Include: chunkarr.h

Tips and Tricks: If you are not interested in the element's size, pass a null pointer as the third argument.

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

Warnings: The error-checking version fatal-errors if passed the index CA_NULL_ELEMENT (i.e. 0xffff, or -1).

ChunkArrayElementToPtrHandles()

void *  ChunkArrayElementToPtrHandles(
        Memhandle              mh,                                     /* Handle of LMem heap's block */
        ChunkHandle            chunk,                                  /* Handle of chunk array */
        word           elementNumber,                                  /* Element to get address of */
        void *         elementSize);                                   /* Write element's size here */

This routine is just like ChunkArrayElementToPtr() , except that the chunk array is specified with its global and chunk handles, instead of with an optr.

Include: chunkarr.h

Tips and Tricks: If you are not interested in the element's size, pass a null pointer as the fourth argument.

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

See Also: ChunkArrayPtrToElement().

Warnings: The error-checking version fatal-errors if passed the index CA_NULL_ELEMENT (i.e. 0xffff, or -1).

ChunkArrayEnum()

Boolean ChunkArrayEnum(
        optr           array,                  /* optr to chunk array */
        void *         enumData,                       /* This is passed to callback routine */
        Boolean _pascal (*callback) (void *element, void *enumData));
        /* callback called for each element; returns TRUE to stop */

This routine lets you apply a procedure to every element in a chunk array. The routine is passed an optr to the chunk array, a pointer (which is passed to the callback routine), and a pointer to a Boolean callback routine. The callback routine, in turn, is called once for each element in the array, and is passed two arguments: a pointer to an element and the pointer which was passed to ChunkArrayEnum() . If the callback routine ever returns true for an element, ChunkArrayEnum() will stop with that element and return true . If it enumerates every element without being aborted, it returns false.

The callback routine can call such routines as ChunkArrayAppend(), ChunkArrayInsertAt(), and ChunkArrayDelete() . ChunkArrayEnum() will see to it that every element is enumerated exactly once. The callback routine can even make a nested call to ChunkArrayEnum() ; the nested call will be completed for every element before the outer call goes to the next element. The callback routine should be declared _pascal.

You can get into trouble if more than one thread of execution is accessing a single ChunkArray and one or both of these threads is using one of the ChunkArrayEnum...() routines. The error-checking version of the kernel detects this situation and generates a CHUNK_ARRAY_ENUM_INSERT_OR_DELETE_RUN_BY_MULTIPLE_THREADS warning if it detects such. If your code is getting this warning, you should add some synchronization between threads. While one thread is enumerating through chunks in the chunk array, the other should do nothing to enumerate the same array, nor alter the array in any way.

Include: chunkarr.h

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

ChunkArrayEnumHandles()

Boolean ChunkArrayEnumHandles(
        MemHandle              mh,                     /* Handle of LMem heap's block */
        ChunkHandle            ch,                     /* Handle of chunk array */
        void *         enumData,                       /* Buffer used by callback routine */
        Boolean _pascal (*callback) (void *element, void *enumData));
        /* callback called for each element; returns TRUE to stop */

This routine is exactly like ChunkArrayEnum() , except that the chunk array is specified by its global and chunk handles (instead of with an optr).

You can get into trouble if more than one thread of execution is accessing a single ChunkArray and one or both of these threads is using one of the ChunkArrayEnum...() routines. The error-checking version of the kernel detects this situation and generates a CHUNK_ARRAY_ENUM_INSERT_OR_DELETE_RUN_BY_MULTIPLE_THREADS warning if it detects such. If your code is getting this warning, you should add some synchronization between threads. While one thread is enumerating through chunks in the chunk array, the other should do nothing to enumerate the same array, nor alter the array in any way.

Include: chunkarr.h

ChunkArrayEnumRange()

Boolean         ChunkArrayEnumRange(
        optr   array,  /* optr to chunk array */
        word   startElement,   /* Start enumeration with this element */
        word   count,  /* Process this many elements */
        void * enumData,       /* This is passed to the callback routine */
        Boolean _pascal (*callback)            /* Return TRUE to halt enumeration */
               (void *element, void *enumData));

This routine is exactly like ChunkArrayEnum() (described above), except that it acts on a limited portion of the array. It is passed two additional arguments: the index of the starting element, and the number of elements to process. It will begin the enumeration with the element specified (remember, the first element in a chunk array has an index of zero). If the count passed would take the enumeration past the end of the array, ChunkArrayEnumRange() will automatically stop with the last element. You can instruct ChunkArrayEnumRange() to process all elements by passing a count of CA_LAST_ELEMENT.

You can get into trouble if more than one thread of execution is accessing a single ChunkArray and one or both of these threads is using one of the ChunkArrayEnum...() routines. The error-checking version of the kernel detects this situation and generates a CHUNK_ARRAY_ENUM_INSERT_OR_DELETE_RUN_BY_MULTIPLE_THREADS warning if it detects such. If your code is getting this warning, you should add some synchronization between threads. While one thread is enumerating through chunks in the chunk array, the other should do nothing to enumerate the same array, nor alter the array in any way.

Include: chunkarr.h

Warnings: The start element must be within the bounds of the array.

See Also: ChunkArrayEnum().

ChunkArrayEnumRangeHandles()

Boolean         ChunkArrayEnumRangeHandles(
        MemHandle      mh,     /* Handle of LMem heap's block */
        ChunkHandle    ch,     /* Handle of chunk array */
        word   startElement,   /* Start enumeration with this element */
        word   count,  /* Process this many elements */
        void * enumData,       /* This is passed to the callback routine */
        Boolean _pascal (*callback)            /* Return TRUE to halt enumeration */
               (void *element, void *enumData));

This routine is exactly like ChunkArrayEnumRange() , except that the chunk array is specified by its global and chunk handles (instead of with an optr).

You can get into trouble if more than one thread of execution is accessing a single ChunkArray and one or both of these threads is using one of the ChunkArrayEnum...() routines. The error-checking version of the kernel detects this situation and generates a CHUNK_ARRAY_ENUM_INSERT_OR_DELETE_RUN_BY_MULTIPLE_THREADS warning if it detects such. If your code is getting this warning, you should add some synchronization between threads. While one thread is enumerating through chunks in the chunk array, the other should do nothing to enumerate the same array, nor alter the array in any way.

ChunkArrayGetCount()

word    ChunkArrayGetCount(
        optr   array);                         /* optr of chunk array */

This routine returns the number of elements in the specified chunk array.

Include: chunkarr.h

Tips and Tricks: It is usually faster to examine the CAH _count field of the ChunkArrayHeader . This field is the first word of the ChunkArrayHeader (and therefore of the chunk). It contains the number of elements in the chunk array.

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

See Also: ChunkArrayHeader.


GEOS C Routines: ChunkArrayGetCountHandles() ...

| Up: GEOS SDK TechDocs | Up | Prev: ChunkArrayElementToPtr() ... | Next: ChunkArraySort() ...

ChunkArrayGetCountHandles()

word	ChunkArrayGetCountHandles(
        MemHandle		mh,				/* Handle of LMem heap's block */
        ChunkHandle		ch);				/* Handle of chunk array */

This routine is just like ChunkArrayGetCount() , except that the chunk array is specified by its global and local handles (instead of with an optr).

Include: chunkarr.h

ChunkArrayGetElement()

void	ChunkArrayGetElement(
        optr	array,					/* optr to chunk array */
        word	elementNumber,					/* Index of element to copy */
        void *	buffer);					/* Address to copy element to */

This routine copies an element in a chunk array into the passed buffer. It is your responsibility to make sure the buffer is large enough to hold the element.

Include: chunkarr.h

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed). Make sure the buffer is large enough to hold the element.

See Also: ChunkArrayPtrToElement(), ChunkArrayElementToPtr().

ChunkArrayGetElementHandles()

void	ChunkArrayGetElementHandles(
        Memhandle		mh,					/* Handle of LMem heap's block */
        ChunkHandle		array,					/* Handle of chunk array */
        word		elementNumber,					/* Index of element to copy */
        void *		buffer);					/* Address to copy element to */

This routine is just like ChunkArrayGetElement() , except that the chunk array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed). Make sure the buffer is large enough to hold the element.

See Also: ChunkArrayPtrToElement(), ChunkArrayElementToPtr().

ChunkArrayInsertAt()

void *	ChunkArrayInsertAt(
        optr		array,				/* Handle of chunk array */
        void *		insertPointer,				/* Address at which to insert
                             * element */
        word		elementSize);				/* Size of new element (ignored
                             * if elements are uniform-sized) */

This routine inserts a new element in a chunk array. You specify the location by passing a pointer to an element. A new element will be allocated at that location; thus, the element which was pointed to will be shifted, so it ends up immediately after the new element. The new element will be zero-initialized.

The routine is passed three arguments: the optr of the array, the address where the new element should be inserted, and the size of the new element. (If the array is of uniform-size elements, the size argument will be ignored.)

Include: chunkarr.h

Tips and Tricks: Only the chunk-handle portion of the optr is significant; the memory block is implicit in the pointer to the element.

Be Sure To: Lock the block on the global heap (if it is not fixed).

Warnings: If the address passed is not the address of an element already in the chunk array, results are undefined. The routine may cause heap compaction or resizing; all pointers within the block are invalidated.

See Also: ChunkArrayAppend(), ChunkArrayDelete().

ChunkArrayInsertAtHandle()

void *	ChunkArrayInsertAtHandle(
        ChunkHandle		chunk,				/* Handle of chunk array */
        void *		insertPointer,				/* Address at which to insert
                             * element */
        word		elementSize);				/* Size of new element (ignored
                             * if elements are uniform-sized) */

This routine is just like ChunkArrayInsertAt() , except that the chunk array is specified by its chunk handle. (The global block is implicit in the pointer passed.)

Include: chunkarr.h

ChunkArrayPtrToElement()

word	ChunkArrayPtrToElement(
        optr	array,						/* Handle of chunk array */
        void *	element);						/* Address of element */

This routine takes the address of an element in a chunk array, as well as an optr to the array. It returns the element's zero-based index.

Include: chunkarr.h

Tips and Tricks: Only the chunk-handle portion of the optr is significant; the memory block is implicit in the pointer to the element.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Warnings: If the address passed is not the address of the beginning of an element, results are unpredictable.

See Also: ChunkArrayElementToPtr().

ChunkArrayPtrToElementHandle()

word 	ChunkArrayPtrToElementHandle(
        ChunkHandle		array,			/* chunk handle of chunk array */
        void *		element);			/* Pointer to element to delete */

This routine is exactly like ChunkArrayPtrToElement() , except that the chunk array is indicated by its chunk handle. (The global block is implicit in the pointer passed.)

| Up: GEOS SDK TechDocs | Up | Prev: ChunkArrayElementToPtr() ... | Next: ChunkArraySort() ...


GEOS C Routines: ChunkArraySort() ...

| Up: GEOS SDK TechDocs | Up | Prev: ChunkArrayGetCountHandles() ... | Next: ClipboardClearQuickTransferNotification() ...

ChunkArraySort()

void	ChunkArraySort(
        optr		array,					/* optr to chunk array */
        word		valueForCallback,					/* Passed to callback routine */
        sword _pascal (*callback)				(void *el1,
                 void * el2,
                 word valueForCallback))
    /* Sign of return value decides order of elements */

This is a general-purpose sort routine for chunk arrays. It does a modified Quicksort on the array, using an insertion sort for subarrays below a certain size.

The sort routine does not compare elements. Rather, it calls a comparison callback routine passed in the callback parameter. Whenever it needs to compare two elements, it calls the callback routine, passing the addresses of the elements and the valueForCallback word which was passed to ChunkArraySort() . The callback routine should be declared _pascal. The callback routine's return value determines which element will come first in the sorted array:

  • If element el1 ought to come before el2 in the sorted array, the callback routine should return a negative integer.
  • If element el1 ought to come after el2 in the sorted array, the callback routine should return a positive integer.
  • If it doesn't matter whether el1 comes before or after el2 in the sorted array, the callback routine should return zero.

Include: chunkarr.h

Tips and Tricks: You may need to sort an array based on different criteria at different times. The simplest way to do this is to write one general-purpose callback routine and have the valueForCallback word determine how the sort is done. For example, the same callback routine could sort the array in ascending or descending order, depending on the valueForCallback .

Be Sure To: Lock the block on the global heap (unless it is fixed).

Warnings: Do not have the callback routine do anything which might invalidate pointers to the array (such as allocate a new chunk or element).

See Also: ArrayQuickSort().

ChunkArraySortHandles()

void	ChunkArraySortHandles(
        MemHandle		memHandle,					/* Handle of LMem heap's block */
        ChunkHandle		chunkHandle,					/* Handle chunk array */
        word		valueForCallback,					/* Passed to callback routine */
        sword _pascal(*callback)(void *el1, void * el2, word valueForCallback)
    /* Sign of return value decides order of elements */

This routine is exactly like ChunkArraySort() above, except that the chunk array is specified by its global and chunk handles (instead of by an optr).

Include: chunkarr.h

ChunkArrayZero()

void	ChunkArrayZero(
        optr	array);		/* optr to chunk array */

This routine destroys all the elements in an array. It does not affect the extra-space area between the ChunkArrayHeader and the elements. It is guaranteed not to cause heap compaction or resizing; thus, pointers to other chunks remain valid.

Include: chunkarr.h

Be Sure To: Lock the block on the global heap (unless it is fixed).

See Also: ChunkArrayDelete().

ChunkArrayZeroHandles()

void	ChunkArrayZeroHandles(
        MemHandle		mh		/* Global handle of LMem heap */
        ChunkHandle		ch);		/* Chunk handle of chunk array */

This routine is exactly like ChunkArrayZero() above, except that the chunk array is specified by its global and chunk handles (instead of by an optr).

Include: chunkarr.h

ClipboardAbortQuickTransfer()

void	ClipboardAbortQuickTransfer(void);

This routine cancels a quick-transfer operation in progress. It is typically used when an object involved in a quick-transfer is shutting down or when an error occurs in a quick-transfer. This routine is usually used only by the object or Process which initiated the quick-transfer.

Include: clipbrd.goh

ClipboardAddToNotificationList()

void	ClipboardAddToNotificationList(
        optr	notificationOD);

This routine registers the passed object or process for quick-transfer notification. This routine is typically called from within an object's MSG_META_INITIALIZE handler or within a Process object's MSG_GEN_PROCESS_OPEN_APPLICATION handler. Pass the optr of the object or the geode handle if the Process object should be registered.

Include: clipbrd.goh

See Also: ClipboardRemoveFromNotificationList().

| Up: GEOS SDK TechDocs | Up | Prev: ChunkArrayGetCountHandles() ... | Next: ClipboardClearQuickTransferNotification() ...


GEOS C Routines: ClipboardClearQuickTransferNotification() ...

| Up: GEOS SDK TechDocs | Up | Prev: ChunkArraySort() ... | Next: ClipboardQueryItem() ...

ClipboardClearQuickTransferNotification()

void	ClipboardClearQuickTransferNotification(
        optr	notificationOD);

This routine removes an object or process from quick-transfer notification. It is typically used in the object's MSG_META_DETACH handler or in the Process object's MSG_GEN_PROCESS_CLOSE_APPLICATION to ensure that it is not notified after it has already detached.

Pass the optr of the object specified to receive notification in ClipboardStartQuickTransfer() (or the geode handle if a process).

Note that an object may also want to check if a quick-transfer is in progress when detaching and possibly abort it if there is one.

Include: clipbrd.goh

ClipboardDoneWithItem()

void	ClipboardDoneWithItem(
        TransferBlockID header);

This routine is called when an object or Process is done using a transfer item. It relinquishes exclusive access to the item's transfer VM file after the caller had previously called ClipboardQueryItem() .

Include: clipbrd.goh

ClipboardEndQuickTransfer()

void	ClipboardEndQuickTransfer(
        ClipboardQuickNotifyFlags 				flags);

This routine ends a quick-transfer operation by resetting the pointer image, clearing any quick-transfer region, clearing the quick-transfer item, and sending out any needed notification of the completed transfer.

Pass this routine a record of ClipboardQuickNotifyFlags . Pass the value CQNF_MOVE if the operation was completed and was a move; pass CQNF_COPY if the operation was completed and was a copy. If the operation could not be completed (e.g. incompatible data types), pass CQNF_NO_OPERATION or CQNF_ERROR.

The notification sent out by the UI will be in the form of the message MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED . This message notifies the originator of the transfer item of the type of operation; the originator can then respond if necessary.

Include: clipbrd.goh

ClipboardEnumItemFormats()

word	ClipboardEnumItemFormats(
        TransferBlockID 		header,
        word 		maxNumFormats,
        ClipboardFormatID *		buffer);

This routine returns a list of all the formats supported by the current transfer item. To see whether a particular format is supported, you can use ClipboardTestItemFormat() instead.

Pass this routine the following:

header
The transfer item header as returned by ClipboardQueryItem() .
maxNumFormats
The maximum number of formats that should be returned. You should set your return buffer (see below) large enough to support this size.
buffer
A pointer to a locked or fixed buffer into which the formats will be copied. Upon return, the buffer will contain the proper number of ClipboardFormatID structures, one for each format available. This buffer should be at least large enough to support the number of formats requested in maxNumFormats .

The word return value is the total number of formats returned. This number will be equal to or less than the number passed in maxNumFormats . The routine will also return the passed buffer filled with that number of ClipboardFormatID structures.

Include: clipbrd.goh

See Also: ClipboardTestItemFormat().

ClipboardGetClipboardFile()

VMFileHandle ClipboardGetClipboardFile(void);

This routine returns the VM file handle of the current default transfer VM file.

Include: clipbrd.goh

ClipboardGetItemInfo()

optr	ClipboardGetItemInfo(
        TransferBlockID header);

This routine returns the source identifier (CIH _sourceID ) of the current transfer item. Pass the transfer item's header returned by ClipboardQueryItem() .

Include: clipbrd.goh

ClipboardGetNormalItemInfo()

TransferBlockID ClipboardGetNormalItemInfo(void);

This routine returns information about the normal transfer item. It returns a TransferBlockID dword which contains the VM file handle of the transfer file and the VM block handle of the transfer item's header block.

To extract the file handle from the return value, use the macro FileFromTransferBlockID() . To extract the block handle, use the macro BlockFromTransferBlockID() .

Include: clipbrd.goh

ClipboardGetQuickItemInfo()

TransferBlockID ClipboardGetQuickItemInfo(void);

This routine returns information about the quick-transfer transfer item. It returns a TransferBlockID dword which contains the VM file handle of the transfer file and the VM block handle of the transfer item's header block.

To extract the file handle from the return value, use the macro FileFromTransferBlockID() . To extract the block handle, use the macro BlockFromTransferBlockID() .

Include: clipbrd.goh

ClipboardGetQuickTransferStatus()

Boolean	ClipboardGetQuickTransferStatus(void);

This routine returns true if a quick-transfer operation is in progress, false otherwise. It is often called when objects or Processes are shutting down in order to abort any quick-transfers originated by the caller.

Include: clipbrd.goh

ClipboardGetUndoItemInfo()

TransferBlockID ClipboardGetUndoItemInfo(void);

This routine returns information about the undo transfer item. It returns a TransferBlockID dword which contains the VM file handle of the transfer file and the VM block handle of the transfer item's header block.

To extract the file handle from the return value, use the macro FileFromTransferBlockID() . To extract the block handle, use the macro BlockFromTransferBlockID() .

Include: clipbrd.goh

| Up: GEOS SDK TechDocs | Up | Prev: ChunkArraySort() ... | Next: ClipboardQueryItem() ...


GEOS C Routines: ClipboardQueryItem() ...

| Up: GEOS SDK TechDocs | Up | Prev: ClipboardClearQuickTransferNotification() ... | Next: ClipboardSetQuickTransferFeedback() ...

ClipboardQueryItem()

void	ClipboardQueryItem(
        ClipboardItemFlags 			flags,
        ClipboardQueryArgs *			retValues);

This routine locks the transfer item for the caller's exclusive access and returns information about the current transfer item. You should call this routine when beginning any paste or clipboard query operation. For operations in which you will change the clipboard's contents, you should instead use the routine ClipboardRegisterItem() .

Pass the following values:

flags
A record of ClipboardItemFlags indicating the transfer item you want to query. Use CIF_QUICK to get information on the quick transfer item, and pass zero (or TIF_NORMAL) to get information on the normal transfer item.
retValues A pointer to an empty ClipboardQueryArgs structure into which return information about the transfer item will be passed. This structure is defined as follows:
typedef struct {
    word			CQA_numFormats;
    optr			CQA_owner;
    TransferBlockID			CQA_header;
} ClipboardQueryArgs;

The CQA_header field of ClipboardQueryArgs is used as a pass value to several other clipboard routines. It contains the VM file handle of the transfer VM file and the VM block handle of the transfer item's header block. The CQA_owner field is the optr of the object that originated the transfer item. The CQA_numFormats field specifies the total number of formats available for this transfer item. To see if a particular format is supported by the transfer item, call the routine ClipboardTestItemFormat() .

Be Sure To: You must call ClipboardDoneWithItem() when you are done accessing the transfer item. This routine relinquishes your exclusive access to the transfer VM file.

Include: clipbrd.goh

See Also: ClipboardRequestItemFormat(), ClipboardDoneWithItem().

ClipboardRegisterItem()

Boolean	ClipboardRegisterItem(
        TransferBlockID header,
        ClipboardItemFlags flags);

This routine completes a change to the transfer item. You should use this routine whenever copying or cutting something into the clipboard or whenever attaching something as the quick-transfer item.

This routine puts the item specified by header into the transfer VM file. It frees any transfer item that may already be in the file. Pass this routine the following:

header
Header information for the item, consisting of the transfer VM file handle and the VM block handle of the block containing the new transfer item. Create the TransferBlockID structure using the macro BlockIDFromFileAndBlock() .
flags
A record of ClipboardItemFlags indicating whether you're registering a clipboard item or a quick-transfer item. The flag CIF_QUICK indicates the item is a quick-transfer item; zero (or TIF_NORMAL) indicates the item is a normal clipboard item.

Include: clipbrd.goh

See Also: ClipboardRequestItemFormat().

ClipboardRemoveFromNotificationList()

Boolean	ClipboardRemoveFromNotificationList(
        optr	notificationOD);

This routine removes an object or Process from the clipboard's change notification list. It is typically called when the object or Process is being detached or destroyed. Pass it the same optr that was added to the notification list with ClipboardAddToNotificationList ().

This routine returns an error flag: The flag will be true if the object could not be found in the notification list, false if the object was successfully removed from the list.

Include: clipbrd.goh

See Also: ClipboardAddToNotificationList().

ClipboardRequestItemFormat()

void	ClipboardRequestItemFormat(
        ClipboardItemFormatID 			format,
        TransferBlockID 			header,
        ClipboardRequestArgs *			retValue);

This routine returns specific information about a particular transfer item. Because some of the passed information must be retrieved with ClipboardQueryItem() , you must call ClipboardQueryItem() before calling this routine.

Pass this routine the following:

format
The manufacturer ID and format type of the new transfer item being put into the transfer VM file. Create the ClipboardItemFormatID value with the macro FormatIDFromManufacturerAndType() .
header
Header information for the item, consisting of the transfer VM file handle and the VM block handle of the block containing the new transfer item. Create the TransferBlockID structure using the macro BlockIDFromFileAndBlock() using returned information from ClipboardQueryItem() .
retValue A pointer to an empty ClipboardRequestArgs structure that will be filled by the routine. This structure is defined as follows:
typedef struct {
    VMFileHandle		CRA_file;
    VMChain		CRA_data;
    word		CRA_extra1;
    word		CRA_extra2;
} ClipboardRequestArgs;

Upon return, the CRA_file field will contain the transfer VM file's VM file handle and the CRA_data field will contain the VM block handle of the transfer item's header block. If there is no transfer item, CRA_data will be zero.

Include: clipbrd.goh

See Also: ClipboardRegisterItem(), ClipboardQueryItem().

| Up: GEOS SDK TechDocs | Up | Prev: ClipboardClearQuickTransferNotification() ... | Next: ClipboardSetQuickTransferFeedback() ...


GEOS C Routines: ClipboardSetQuickTransferFeedback() ...

| Up: GEOS SDK TechDocs | Up | Prev: ClipboardQueryItem() ... | Next: ConstructOptr() ...

ClipboardSetQuickTransferFeedback()

void	ClipboardSetQuickTransferFeedback(
        ClipboardQuickTransferFeedback 						cursor,
        UIFunctionsActive 						buttonFlags);

This routine sets the image of the mouse pointer during a quick-transfer operation. Use this routine to provide visual feedback to the user during the quick-transfer. For example, an object that could not accept the quick-transfer item would set the "no operation" cursor while the mouse pointer was over its bounds.

Pass the two following values:

cursor
A value of ClipboardQuickTransferFeedback type indicating the type of cursor to set. The possible values are listed below.
buttonFlags
A record of UIFunctionsActive flags. These flags are defined in the Input Manager section and deal with user override of the move/copy behavior.

The cursor parameter contains a value of ClipboardQuickTransferFeedback . This is an enumerated type that defines the cursor to be set, and it has the following values:

CQTF_MOVE
This sets the cursor to the specific UI's move cursor.
CQTF_COPY
This sets the cursor to the specific UI's copy cursor.
CQTF_CLEAR
This clears the cursor and sets it to the specific UI's modal "no operation" cursor.

Include: clipbrd.goh

ClipboardStartQuickTransfer()

Boolean	ClipboardStartQuickTransfer(
        ClipboardQuickTransferFlags 							flags,
        ClipboardQuickTransferFeedback							initialCursor,
        word							mouseXPos,
        word							mouseYPos,
        ClipboardQuickTransferRegionInfo *							regionParams,
        optr							notificationOD);

This routine signals the beginning of a quick-transfer operation. Typically, an object or process will call this routine in its MSG_META_START_MOVE_COPY handler.

Pass it the following parameters:

flags
A record of ClipboardQuickTransferFlags indicating whether an addition graphic region will be attached to the cursor and whether the caller wants notification of transfer completion. The flags allowed are listed below, after the parameter list.
initialCursor
The initial cursor to use for visual feedback to the user. It is a value of ClipboardQuickTransferFeedback , either CQTF_MOVE or CQTF_COPY. If -1 is passed in this parameter, the initial cursor will be the default no-operation cursor (i.e. the transfer source may not also act as the transfer destination).
mouseXPos
This field is used only if CQTF_USE_REGION is passed in flags . It is the horizontal position of the mouse in screen coordinates.
mouseYPos
This field is used only if CQTF_USE_REGION is passed in flags . It is the vertical position of the mouse in screen coordinates.
regionParams
A pointer to a ClipboardQuickTransferRegionInfo structure defining the graphical region to be attached to the cursor during the transfer operation. This structure is only required if CQTF_USE_REGION is passed in flags . It is defined below.
notificationOD
The optr of the object to be notified upon transfer completion. The object specified will receive the notification messages MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED and MSG_..._FEEDBACK .

The allowed ClipboardQuickTransferFlags are listed below:

CQTF_COPY_ONLY
Source supports copying only (not cutting).
CQTF_USE_REGION
Source has passed the definition of a graphical region which will be attached to the tail of the quick-transfer cursor.
CQTF_NOTIFICATION
Source requires notification of completion of the transfer in order to cut original data or provide other feedback.

If a graphical region is to be attached to the quick-transfer cursor, you must pass a pointer to a ClipboardQuickTransferRegionInfo in the regionParams parameter. This structure is defined below.

typedef struct {
    word	CQTRI_paramAX;
    word	CQTRI_paramBX;
    word	CQTRI_paramCX;
    word	CQTRI_paramDX;
    Point	CQTRI_regionPos;
    dword	CQTRI_strategy;
    dword	CQTRI_region;
} ClipboardQuickTransferRegionInfo;

This structure is passed on the stack to the routine. The first four fields represent the region's definition parameters. CQTRI_regionPos is a Point structure indicating where (in screen coordinates) the region is to be located. CQTRI_strategy is a pointer to the region strategy routine. CQTRI_strategy should be a video driver strategy. To find out the strategy of the video driver associated with your window, send your object a MSG_VIS_VUP_QUERY with VUQ_VIDEO_DRIVER. Pass the handle thus gained to GeodeInfoDriver() , which will return the strategy.

This routine returns an error flag: If a quick-transfer is already in progress, the return will be true . If the quick-transfer is successfully begun, the error flag will be false .

Include: clipbrd.goh

ClipboardTestItemFormat()

Boolean	ClipboardTestItemFormat(
        TransferBlockID		header,
        ClipboardFormatID 		format);

This routine tests whether the given format is supported by the specified transfer item. It returns true if the format is supported, false if the format is not supported. Pass the following values:

header
A TransferBlockID specifying the VM file handle and VM block handle of the transfer item to be checked. This is returned by the routines ClipboardGetNormalItemInfo() , ClipboardGetQuickItemInfo() , ClipboardGetUndoItemInfo() , and ClipboardQueryItem() . Most often the proper routine to use is ClipboardQueryItem() .
format
A ClipboardFormatID specifying the type and manufacturer ID of the format to be checked. It is most appropriate to create this parameter from its individual parts using the macro FormatIDFromManufacturerAndType() .

Include: clipbrd.goh

ClipboardUnregisterItem()

void	ClipboardUnregisterItem(
        optr	owner);

This routine restores the transfer item to what it was before the last ClipboardRegisterItem() . Pass it the optr of the caller.

Only the object that last registered a transfer item is allowed to unregister it. If the transfer item is owned by a different object, or if there is no transfer item, nothing will be done. If the transfer item is owned by the caller, the transfer item will be unregistered and the clipboard will be restored to its previous state.

Include: clipbrd.goh

| Up: GEOS SDK TechDocs | Up | Prev: ClipboardQueryItem() ... | Next: ConstructOptr() ...


GEOS C Routines: ConstructOptr() ...

| Up: GEOS SDK TechDocs | Up | Prev: ClipboardSetQuickTransferFeedback() ... | Next: ContactGetDBHandle() ...

ConstructOptr()

optr	ConstructOptr(
        Handle 		han,
        ChunkHandle 		ch);

This macro constructs an optr type from the given handle (typically a MemHandle) and chunk handle.

See Also: HandleToOptr(),
OptrToHandle(),
OptrToChunk().

ContactCompareNumbers()

Boolean 	ContactCompareNumbers(
        TCHAR *number1,
        TCHAR *number2);

This function compares two phone number strings for equivalence. It ignores extraneous characters such as spaces. It returns zero if they are the same number, otherwise it returns a non-zero value.

Include: contdb.goh

ContactCreateRecordFromTemplate()

MemHandle	 ContactCreateRecordFromTemplate( void );

This function creates a new Contact record in the Contacts database. It returns the handle of the new record. Make any desired changes to the record and then call ContactSaveRecord() to save the record or FoamDBDiscardRecord() to discard changes.

Include: contdb.goh

See Also: ContactSaveRecord(), ContactSetTemplateRecord().

ContactEnsureEmptyField()

FieldID	ContactEnsureEmptyField(
        MemHandle 		record,
        optr 		fieldName,
        ContdbFieldType		fieldType);

This function creates a field in a record, and clears the dat from the existing field of that name if one already existed. This function takes the following arguemnts:

record
The handle of the record, as returned by FoamDBGetRecordFromID() or ContactCreateRecordFromTemplate() .
fieldName
optr of a chunk containing the null-terminated name string, or NullOptr to use the default name for the passed ContdbFieldType .
fieldType
The type of field to find. One of CFT_NAME, CFT_COMPANY CFT_TITLE, CFT_ADDRESS, CFT_PHONE, CFT_FAX, CFT_EMAIL, CFT_DTMF, CFT_NOTES.

Include: contdb.goh

See Also: ContactEnsureField(), FoamDBSetFieldData().

ContactEnsureField()

FieldID	ContactEnsureField(
        MemHandle 		record,
        optr 		fieldName,
        ContdbFieldType		fieldType);

This functionreturns the FieldID of a field within a record, or creates the field if it doesn't find it. This function takes the following arguemnts:

record
The handle of the record, as returned by FoamDBGetRecordFromID() or ContactCreateRecordFromTemplate() .
fieldName
optr of a chunk containing the null-terminated name string, or NullOptr to use the default name for the passed ContdbFieldType .
fieldType
The type of field to find. One of CFT_NAME, CFT_COMPANY CFT_TITLE, CFT_ADDRESS, CFT_PHONE, CFT_FAX, CFT_EMAIL, CFT_DTMF, CFT_NOTES.

Include: contdb.goh

See Also: ContactEnsureEmptyField(), FoamDBSetFieldData().

ContactExportDBToTextFile()

ContactExportStatus 	ContactExportDBToTextFile(
        hptr 	fileExport,
        dword 	startElement,
        dword 	numOfRecords);

This function exports the Contact database's data as an ASCII file. On success, the function returns CES_NO_ERROR, otherwise it returns CES_FILE_ERROR. This function takes the following arguments:

fileExport
The handle of the opened file to hold the exported text.
startElement
The RecordID of the first record to export.
numOfRecords
This argument determines the range of records to export. All records with RecordID at least startElement but less than startElement+numOfRecords will be exported.

To export all the records, pass a startElement of zero and a numOfRecords of CONTACT_EXPORT_ALL_RECORDS.

Include: contdb.goh

See Also: ContactExportDBToTextFile().

| Up: GEOS SDK TechDocs | Up | Prev: ClipboardSetQuickTransferFeedback() ... | Next: ContactGetDBHandle() ...


GEOS C Routines: ContactGetDBHandle() ...

| Up: GEOS SDK TechDocs | Up | Prev: ConstructOptr() ... | Next: ContactMatchNumber() ...

ContactGetDBHandle()

VMFileHandle 	ContactGetDBHandle( void );

This function returns the handle of the Contact database, the database that contains all of the "address book" data used by the contdb library; it also lets the system know that you're using the database. The database handle is necessary for various operations with the library. When you're done with the database, remember to call ContactReleaseDBHandle() .

Include: contdb.goh

See Also: ContactGetDBHandleNoBlock(), ContactReleaseDBHandle().

ContactGetDBHandleNoBlock()

VMFileHandle 	ContactGetDBHandleNoBlock( void );

This function behaves in the same manner as ContactGetDBHandle() , except that if it cannot get the handle, it returns zero instead of waiting until the handle is available.When you're done with the handle, remember to call ContactReleaseDBHandle() .

Include: contdb.goh

See Also: ContactGetDBHandle().

ContactGetName()

Boolean	ContactGetName(
        MemHandle 		record,
        TCHAR 		*dest);

This function gets the name associated with the record, filling in the dest buffer. If there was no name in the record, this function returns non-zero; otherwise, it returns zero.

Include: contdb.goh

See Also: ContactGetDBHandle().

ContactGetTruncatedName()

Boolean	ContactGetTrunactedName(
        MemHandle 		record,
        TCHAR 		*dest,
        word		destSize);				/* Size of dest buffer */

This function gets the name associated with the record, filling in the dest buffer. It will truncate the name, as necessary to make it fit inside the buffer. Remember to leave space for the null terminator when determining how big a buffer you'll need. If there was no name in the record, this function returns non-zero; otherwise, it returns zero.

Include: contdb.goh

See Also: ContactGetDBHandle().

ContactGetUserDataID()

RecordID	ContactGetUserDataID( void );

This functionreturns the RecordID of the special Contacts database record containing the user's own contact data.

Include: contdb.goh

See Also: FoamDBGetRecordFromID().

ContactImportDBFromTextFile()

void 	ContactImportDBFromTextFile(
        hptr 		fileImport);					/* file's handle */

This function imports contact database information from an ASCII text file. This file's format is described below. The function will compare imported records to those already in the Contacts database. Any identical records will be merged.

A short sample file is shown below.

[RECORD]
[FIELD]
Name
[FIELD-NAME]
[FIELD-DATA]
Hudson W.
[FIELD-END]
[FIELD]
Company
[FIELD-NAME]
[FIELD-DATA]
Space Marines
[FIELD-END]
[FIELD]
Title
[FIELD-NAME]
[FIELD-DATA]
Private
[FIELD-END]
[RECORD-END]
[RECORD]
[FIELD]
Name
[FIELD-NAME]
[FIELD-DATA]
Adams, John Quincy
[FIELD-END]
[FIELD]
Tel
[FIELD-NAME]
[FIELD-DATA]
1 617 438 1776
[FIELD-END]
[FIELD]
[FIELD-NAME]
Company
[FIELD-DATA]
[FIELD-END]
USA Government
[FIELD]
Notes
[FIELD-NAME]
Note(Birthday)
[FIELD-DATA]
July 11, 1767
Gift ideas: flowers
[FIELD-END]
[RECORD-END]

The ASCII file follows these rules:

  • The page is a DOS text file, using the "Latin 1" code page (number 819).
  • Each line of text may ends with a newline; the newline may be preceded by an optional carriage return.
  • The data corresponding to each record is delimited by a line reading [RECORD] at the start and another line reading [RECORD-END] at the end. Any text that appears outside of these delimiters is lost.
  • Each record consists of one or more fields. Each field is delimited by a line reading [FIELD] and a line reading [FIELD-END].
  • For each field, the first line after the [FIELD] line will consist of one of the following strings, specifying the field's type:
    Name, Company, Title, Address, Tel, Fax, Mail, DTMF, Note.
  • There may be a line of text between the [FIELD-NAME] and [FIELD-DATA] lines. If there is no such line of text, then the field's name is the default name for its data type. If there is such a line of text, then you may form the field name by placing the text of this line between parentheses and preceding it with the default name for the field. In the "Birthday" example, above, the full field name would be "Note(Birthday)".
    The exception to this rule is "GSM/PCN" in phone number fields. If the line of text is "GSM/PCN", treat it as if it were "GSM". The full field name will probably be "Tel(GSM)" or "Fax(GSM)".
  • The line(s) of text between the [FIELD-DATA] line and the [FIELD-END] line contain the field's data, if any. If there are no lines of text, that means that the field contains no data. There may be more than one line of text.

Include: contdb.goh

See Also: ContactExportDBToTextFile().

ContactMatchName()

RecordID	ContactMatchName(
        TCHAR		name,
        ContdbFieldType		type,
        Boolean		stopEarly,
        FieldID		*field,
        word		*numMatches );

This function takes aname, searches all fields of the specified type in all records of the Contact database, returning the RecordID and FieldID of the matching field, if any; if there was no match, it returns -1.

The comparison of names does not consider case.

This function takes the following arguments:

name
String containing the name to match.
type
The type of phone number to match; one of CFT_NAME, CFT_COMPANY CFT_TITLE, CFT_ADDRESS, CFT_PHONE, CFT_FAX, CFT_EMAIL, CFT_DTMF, CFT_NOTES.
stopEarly
Set this flag true to stop searching after finding the first matching name; set it false to find out the number of matching names.
field
Pointer to a FieldID buffer. The function will fill this buffer in with the FieldID of the matching field, if any.
numMatches
Pointer to a word buffer. The function will fill this buffer in with the number of matching records found. This number is only meaningful if the stopEarly flag is false .

Include: contdb.goh

See Also: ContactEnsureEmptyField(), FoamDBSetFieldData().

| Up: GEOS SDK TechDocs | Up | Prev: ConstructOptr() ... | Next: ContactMatchNumber() ...


GEOS C Routines: ContactMatchNumber() ...

| Up: GEOS SDK TechDocs | Up | Prev: ContactGetDBHandle() ... | Next: DataStoreCreate() ...

ContactMatchNumber()

RecordID	ContactMatchNumber(
        TCHAR		number,
        ContdbCallType		type,
        FieldID		*field,
        word		*numMatches );

This function takes a phone number, searches all fields of the specified type in all records of the Contact database, returning the RecordID and FieldID of the matching field, if any; if there was no match, it returns -1.

This function takes the following arguments:

number
String containing the phone number to match.
type
The type of phone number to match; one of CCT_PHONE, CCT_SMS, CCT_FAX, CCT_EMAIL, CCT_GSM_FAX, CCT_ANY.
field
Pointer to a FieldID buffer. The function will fill this buffer in with the FieldID of the matching field, if any.
numMatches
Pointer to a word buffer. The function will fill this buffer in with the number of matching records found.

Include: contdb.goh

See Also: ContactEnsureEmptyField(), FoamDBSetFieldData().

ContactRecreateFieldLabel()

void ContdbRecreateFieldLabel(
        TCHAR 		*subLabel,
        TCHAR 		*destination,
        int 		fieldType);

This utility function takes a field sub-label (e.g., "GSM/PCN") and field type and creates a label from them (e.g. "Tel(GSM)". It will translate standard labels from English to the user's language. (Some field sublabels are standard, such as the "GSM" in "Tel(GSM)" and will be translated; the user's custom sublabels will not be translated.) This function takes the following arguments:

subLabel
The sublabel string.
destination
String buffer to hold the field name.
fieldType
The ContdbFieldType value of the field.

Include: contdb.goh

See Also: ContactImportDBFromTextFile().

ContactReleaseDBHandle()

void	ContactReleaseDBHandle( void );

This function releases the Contact database's handle, signalling that the application is done with the database for now.

Include: contdb.goh

See Also: ContactGetDBHandle(), ContactReleaseDBHandle().

ContactSaveRecord()

dword	ContactSaveRecord(
        MemHandle 		record);

This functionsaves the passed record in the database, inserting it in the proper sort order. It returns the record's RecordID number.

Include: contdb.goh

See Also: FoamDBDiscardRecord(), FoamDBDeleteRecord().

ContactSetTemplateRecord()

void	ContactSetTemplateRecord(
        MemHandle 		record);

This functionmakes the passed record the Contact database's template record. It also frees that contact's handle.

Include: contdb.goh

See Also: ContactCreateRecordFromTemplate().

ContdbTranslateSubFieldLabelToEnglish()

void	ContdbTranslateSubFieldLabelToEnglish(
        TCHAR 		*subLabel,
        TCHAR 		*destination);

This function translates a foreign sub-label to its english equivalent. This function only recognizes certain "standard" sublabels; the user's custom labels will not be translated. This function takes two arguments: a buffer containing the string to translate and a string buffer to hold the translation.

See Also: DBAllocUngrouped().

CreateVisMonikerLine()

optr	CreateVisMonikerLine(
    word			pointSize,
    FontID			typeFace,
    word			numberOfFields,
    VisMonikerColumn *	vmc );

Creates a visMoniker that separates each element into a separate column. This is useful if you've got a GenDynamicList and you want to display columns of data. It uses an array of VisMonikerColumn structs, referred to as fields. All fields will have the same point size, but TextStyle can vary between fields.

You can also have "adaptable columns", which are sequential text columns in the VisMonikerColumn array whose VMC_just fields are set to J_FULL. Rather than the text being padded or clipped to fit within a fixed field width, the text of each column immediately follows the previous column. All of the adaptable columns are left-justified in an overall field width, specified by the VMC_width of the first J_FULL VisMonikerColumn structure (the others are set to 0 width). Adaptable fields are still clipped to fit within the overall field width. A group of adaptable fields ends at the first VisMonikerColumn structure that is not J_FULL or has non-zero width.

Example: Say you want to create a moniker that looks like:

    Field1    SEARCHable text   Field2
    |         |                 |     |
        01234567890123456789012345678901234
Where "SEARCH" is supposed to be underlined and bold. Here is the VisMonikerColumn array you would pass:
elt#  VMC_width  VMC_ptr       VMC_just  VMC_style
--------------------------------------------------
0     10         "Field1"      J_LEFT    0
1     18         "SEARCH"      J_FULL    TS_BOLD | TS_UNDERLINE
2     0          "able text"   J_FULL    0
3     7          "Field2"      J_LEFT    0
elements 1 and 2 are placed together in an overall field width of 18.

Pass:

pointSize
Point size to use when generating the text moniker.
typeFace
Font identifier for the font to use.
numberOfFields
The number of columns in the vmc array.
vmc
An array of VisMonikerColumn structs that contain the column information for building the visMoniker. Each VisMonikerColumn struct defines an element in the line.
typedef struct {
  byte           VMC_width;   /* Width of text in chars, 0 if bitmap */
  const void * 	 VMC_ptr;     /* Pointer to text or bitmap;
                               * if bitmap, points to Bitmap structure */
  Justification  VMC_just;    /* How to justify the column text */
  TextStyle      VMC_style;   /* Column text styles */
  ColumnBorder   VMC_border;  /* Draw vertical border */
} VisMonikerColumn;

Return: Optr to the newly created visMoniker.

Warning: Because the new visMoniker is created in its own new memory block, be sure to free the block when you're done with it.

Include: foam.goh

DataStoreAddField()

DataStoreStructureError	DataStoreAddField(
        word		dsToken,
        FieldDescriptor	*fieldDescPtr,
        FieldID		*fieldIDPtr);

Adds a field to the datastore and writes the new field's FieldID to fieldIDPtr. Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful). Takes the following arguments:

dsToken
Session token to the opened datastore.
fieldDescPtr
Pointer to a FieldDescriptor structure. Set FD _flags to FF_PRIMARY_KEY if the field is part of the key.
        typedef struct {
            FieldData		FD_data;
            TCHAR		*FD_name;
        } FieldDescriptor;
        typedef struct {
            FieldType		FD_type;
            FieldCategory		FD_category;
            FieldFlags		FD_flags;
        } FieldData;
fieldIDPtr
Pointer to the FieldID of the added field.

Include: datastor.h

DataStoreBuildIndex()

DataStoreStructureError	DataStoreBuildIndex(
        word					dsToken,
        MemHandle					*indexBlockHan,
        word					dataSize,
        DataStoreIndexCallbackParams					*paramsPtr,
        sword _pascal (*CallbackRoutine) (
            word	dsToken,
            DataStoreIndexCallbackParams	*paramsPtr));

Builds an index of RecordNum s (low word only) based on either a specified field or a callback function. Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful). Takes the following arguments:

dsToken
Session token to the opened datastore.
indexBlockHan
Pointer to the handle of the index block.
dataSize
Number of bytes of data to write to the index block.
paramsPtr
Pointer to a DataStoreIndexCallbackParams structure. To build an index on a field, specify the field in DSICP _indexField . Set DSICP _sortOrder to SO_ASCENDING or SO_DESCENDING. If you specify a callback, the caller will pass DSICP _cbData , DSICP _rec1 and DSICP _rec2 to the callback.
        typedef struct {
            FieldID		DSICP_indexField;
            SortOrder		DSICP_sortOrder;
            void		*DSICP_cbData;
            RecordHeader		*DSICP_rec1;
            RecordHeader		*DSICP_rec1;
        } DataStoreIndexCallbackParams;
CallbackRoutine
Subroutine to determine which of two passed records comes before the other. The function should return:

-1 if DSICP_rec1 comes before DSICP_rec2
 1 if DSICP_rec1 comes after DSICP_rec2

Warnings: Cannot call this routine on datastores that contain more than 4,000 records (will return DSSE_INDEX_RECORD_NUMBER_LIMIT_EXCEEDED).

Include: datastor.h

DataStoreClose()

DataStoreError	DataStoreClose(
        word		dsToken);	/* session token to opened datastore */

Closes the datastore if no other application has it open; otherwise, closes the "session" associated with dsToken . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Warnings: Returns DSE_CLOSE_WITH_LOCKED_RECORD if the calling session has a record locked.

Include: datastor.h

| Up: GEOS SDK TechDocs | Up | Prev: ContactGetDBHandle() ... | Next: DataStoreCreate() ...


GEOS C Routines: DataStoreCreate() ...

| Up: GEOS SDK TechDocs | Up | Prev: ContactMatchNumber() ... | Next: DataStoreDiscardRecord() ...

DataStoreCreate()

DataStoreError	DataStoreCreate(
        DataStoreCreateParams			*paramsPtr,
        word			dsToken);

Creates a new datastore, opens a session to it and writes the session token to dsToken . Returns a DataStoreError value (DSE_NO_ERROR if successful). Takes the following arguments:

paramsPtr
Pointer to a DataStoreCreateParams structure. DSCP_name contains the name of the datastore. You can pass DSF_PRIVATE, DSF_TIMESTAMP and/or DSF_NO_PRIMARY_KEY to DSCP_flags. DSCP_keyList and DSCP_keyCount contain the list and number of key field(s), respectively. Specify the object to receive notification of datastore changes in DSCP_notifObject .
        typedef struct {
            TCHAR			*DSCP_name;
            DataStoreFlags			DSCP_flags;
            FieldDescriptor			*DSCP_keyList;
            word			DSCP_keyCount;
            optr			DSCP_notifObject;
        } DataStoreCreateParams;
dsToken
Session token to the opened datastore.

Include: datastor.h

DataStoreDelete()

DataStoreError	DataStoreDelete(
        TCHAR		*dsNamePtr);

Deletes the named datastore if no other application(s) has(have) it open. Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreDeleteField()

DataStoreStructureError	DataStoreDeleteField(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		fieldID);

Deletes the passed field from the datastore. Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful).

You can reference a field by its name or FieldID . If referencing by name, pass "0" in fieldID ; if referencing by FieldID , pass NULL in fieldNamePtr .

Warnings: Cannot delete key fields.

Cannot delete a field if an application has locked any record in the datastore (will return DSSE_ACCESS_DENIED).

Include: datastor.h

See Also: DataStoreRemoveFieldFromRecord().

DataStoreDeleteRecord()

DataStoreDataError	DataStoreDeleteRecord(
        word		dsToken,	/* session token to opened datastore */
        RecordID		recordID);

Deletes the passed record (referenced by its RecordID ) and flushes it from the record buffer if it is the current record. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: Cannot delete a record that has been locked or loaded into memory (will return DSDE_RECORD_IN_USE).

Include: datastor.h

See Also: DataStoreDeleteRecordNum().

DataStoreDeleteRecordNum()

DataStoreDataError	DataStoreDeleteRecordNum(
        word		dsToken,	/* session token to opened datastore */
        RecordNum		recordNum);

Deletes the passed record (referenced by its RecordNum ) and flushes it from the record buffer if it is the current record. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: Cannot delete a record that has been locked or loaded into memory (will return DSDE_RECORD_IN_USE).

Include: datastor.h

See Also: DataStoreDeleteRecord().

| Up: GEOS SDK TechDocs | Up | Prev: ContactMatchNumber() ... | Next: DataStoreDiscardRecord() ...


GEOS C Routines: DataStoreDiscardRecord() ...

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreCreate() ... | Next: DataStoreGetFieldChunk() ...

DataStoreDiscardRecord()

DataStoreDataError	DataStoreDiscardRecord(
        word		dsToken);	/* session token to opened datastore */

Flushes the current record from the record buffer (without saving any changes). Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Include: datastor.h

See Also: DataStoreSaveRecord().

DataStoreFieldEnum()

DataStoreError	DataStoreFieldEnum(
        word		dsToken,
        RecordHeader		*recordPtr,
        void		*enumDataPtr,
        Boolean _pascal (*CallbackRoutine) (
            void		*fieldDataPtr,
            word		fieldDataSize,
            FieldType		fieldType,
            FieldCategory		fieldCat,
            FieldID		fieldID,
            FieldFlags		flags,
            void		*enumDataPtr));

Enumerates through the fields in the passed record. When the callback returns TRUE, stops enumeration and returns DSE_NO_ERROR. Takes the following arguments:recordPtr

dsToken
Session token to the opened datastore.
recordPtr
Pointer to a RecordHeader structure.
        typedef struct {
            RecordID		RH_id;
            word		RH_size;
            byte		RH_fieldCount;
        } RecordHeader;
enumDataPtr
Pointer to data that caller passes to the callback.
CallbackRoutine
Boolean subroutine to determine when enumeration should end.

Be Sure To: Lock the record first with DataStoreLockRecord() before calling this routine.

Warnings: Callback can modify data passed in enumDataPtr but cannot modify any actual field data.

Include: datastor.h

See Also: DataStoreRecordEnum().

DataStoreFieldIDToName()

DataStoreStructureError	DataStoreFieldIDToName(
        word		dsToken,	/* session token to opened datastore */
        FieldID		fieldID,
        TCHAR		*fieldNamePtr,
        word		*nameSizePtr);

Writes the field name corresponding to the passed FieldID ( fieldID ) to fieldNamePtr . Copies no more than nameSizePtr bytes and overwrites this same parameter with the number of bytes actually copied. Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful).

Include: datastor.h

See Also: DataStoreFieldNameToID().

DataStoreFieldNameToID()

DataStoreStructureError	DataStoreFieldNameToID(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		*fieldIDPtr);

Writes the FieldID corresponding to the passed field name (fieldNamePtr ) to fieldIDPtr . Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful).

Include: datastor.h

See Also: DataStoreFieldIDToName().

DataStoreGetExtraData()

DataStoreError	DataStoreGetExtraData(
        word		dsToken,	/* session token to opened datastore */
        void		*dataPtr, /* data read from file header */
        word		*dataSizePtr);

Writes any extra data for this datastore to dataPtr . Writes the number of bytes copied to dataSizePtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetField()

DataStoreDataError	DataStoreGetField(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		fieldID,
        void		**blockPtrPtr,
        word		*blockSizePtr,
        MemHandle		*blockHanPtr);

Writes contents of the passed field to a block. If blockPtrPtr is NULL, allocates a block and writes its handle to blockHanPtr and its size to blockSizePtr . If blockPtrPtr is not NULL and field data exceeds block size, copies blockSizePtr bytes to the block and returns DSDE_EXCEEDS_BUFFER_SIZE. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

If referencing field by name, pass "0" in fieldID ; if referencing field by FieldID , pass NULL in fieldNamePtr .

Warnings: If the passed field is not present, writes "0" to blockSizePtr and returns DSDE_NO_ERROR.

Include: datastor.h

See Also: DataStoreGetFieldChunk().

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreCreate() ... | Next: DataStoreGetFieldChunk() ...


GEOS C Routines: DataStoreGetFieldChunk() ...

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreDiscardRecord() ... | Next: DataStoreGetRecordCount() ...

DataStoreGetFieldChunk()

DataStoreDataError	DataStoreGetFieldChunk(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		fieldID,
        MemHandle		blockHan,
        ChunkHandle		*chunkHanPtr,
        word		*dataSizePtr);

Writes contents of the passed field to a chunk in the passed block. If chunkHanPtr is NULL, allocates a chunk and writes its handle to chunkHanPtr . If chunkHanPtr is not NULL dataSizePtrand field data exceeds chunk size, resizes the chunk and writes the number of bytes copied to dataSizePtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

If referencing field by name, pass "0" in fieldID ; if referencing field by FieldID , pass NULL in fieldNamePtr .

Warnings: If the passed field is not present, writes "0" to dataSizePtr and returns DSDE_NO_ERROR.

Include: datastor.h

See Also: DataStoreGetField().

DataStoreGetFieldCount()

DataStoreError	DataStoreGetFieldCount(
        word		dsToken,	/* session token to opened datastore */
        word		*fieldCountPtr);

Writes the number of fields defined for this datastore to fieldCountPtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

See Also: DataStoreGetRecordCount().

DataStoreGetFieldInfo()

DataStoreStructureError	DataStoreGetFieldInfo(
        word		dsToken,	/* session token to opened datastore */
        FieldDescriptor		*fieldDescPtr,
        FieldID		fieldID,
        word		fieldNameSize);

Writes FieldDescriptor for passed field to fieldDescPtr. For FD_name , writes fieldNameSize bytes only; pass "0" (and set FD_name to NULL) if you don't need the field name. Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetFieldPtr()

DataStoreDataError	DataStoreGetFieldPtr(
        word		dsToken,	/* session token to opened datastore */
        RecordHeader		*recordPtr,
        FieldID		fieldID,
        void		**fieldContentPtrPtr,
        FieldType		*fieldTypePtr,
        word		*fieldSizePtr);

Gets a pointer to the field specified by fieldID . Writes the field's content to fieldContentPtrPtr , its FieldType to fieldTypePtr , and its size to fieldSizePtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetFieldSize()

DataStoreDataError	DataStoreGetFieldSize(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		fieldID,
        word		*fieldSizePtr);

Writes the number of bytes in the passed field to fieldSizePtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

If referencing field by name, pass "0" in fieldID ; if referencing field by FieldID , pass NULL in fieldNamePtr .

Warnings: If the field is not present, writes "0" to fieldSizePtr and returns DSDE_NO_ERROR.

Include: datastor.h

DataStoreGetFlags()

DataStoreError	DataStoreGetFlags(
        word		dsToken,	/* session token to opened datastore */
        DataStoreFlags		*FlagPtr);

Writes the DataStoreFlags set for this datastore to flagPtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetNextRecordID()

DataStoreError	DataStoreGetNextRecordID(
        word		dsToken,	/* session token to opened datastore */
        RecordID		*nextIDPtr);

Writes the next RecordID to be assigned to nextIDPtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetNumFields()

DataStoreDataError	DataStoreGetNumFields(
        word		dsToken,	/* session token to opened datastore */
        word		*fieldCountPtr);

Writes the number of fields in the current record to fieldCountPtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetOwner()

DataStoreError	DataStoreGetOwner(
        word		dsToken,	/* session token to opened datastore */
        GeodeToken		*tokenPtr);

Writes the GeodeToken of the application that created the datastore to tokenPtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreDiscardRecord() ... | Next: DataStoreGetRecordCount() ...


GEOS C Routines: DataStoreGetRecordCount() ...

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreGetFieldChunk() ... | Next: DataStoreNewRecord() ...

DataStoreGetRecordCount()

DataStoreError	DataStoreGetRecordCount(
        word		dsToken,	/* session token to opened datastore */
        dword		*RecordCountPtr);

Writes the number of records in the datastore to recordCountPtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Warnings: Does not count new records that have not yet been saved.

Include: datastor.h

See Also: DataStoreGetFieldCount().

DataStoreGetRecordID()

DataStoreDataError	DataStoreGetRecordID(
        word		dsToken,	/* session token to opened datastore */
        RecordID		*recordIDPtr);

Writes the RecordID of the current record to recordIDPtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: If no record is loaded, returns DSDE_RECORD_BUFFER_EMPTY.

Include: datastor.h

DataStoreGetTimeStamp()

DataStoreError	DataStoreGetTimeStamp(
        word		dsToken,	/* session token to opened datastore */
        FileDateAndTime		*timestampPtr);

Writes the date and time that the datastore was last modified to timestampPtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetVersion()

DataStoreError	DataStoreGetVersion(
        word		dsToken,	/* session token to opened datastore */
        ProtocolNumber		*versionPtr);

Writes the protocol number of the datastore to versionPtr . (Protocol numbers are useful for keeping track of different versions of a datastore.) Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreLoadRecord()

DataStoreDataError	DataStoreLoadRecord(
        word		dsToken,	/* session token to opened datastore */
        RecordID		recordID,
        RecordNum		*recordNumPtr);

Loads the record referenced by recordID into memory and makes it the current record. Writes the RecordNum of the loaded record to recordNumPtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: Returns DSDE_RECORD_BUFFER_NOT_EMPTY if there is already a record loaded in the session's record buffer.

Returns DSDE_INVALID_RECORD_NUMBER if there is no such record.

Returns DSDE_RECORD_IN_USE if another session has already locked the record.

Include: datastor.h

See Also: DataStoreLoadRecordNum().

DataStoreLoadRecordNum()

DataStoreDataError	DataStoreLoadRecordNum(
        word		dsToken,	/* session token to opened datastore */
        RecordNum		recordNum,
        RecordID		*recordIDPtr);

Loads the record referenced by recordNum into memory and makes it the current record. Writes the RecordID of the loaded record to recordIDPtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: Returns DSDE_RECORD_BUFFER_NOT_EMPTY if there is already a record loaded in the session's record buffer.

Returns DSDE_INVALID_RECORD_NUMBER if there is no such record.

Returns DSDE_RECORD_IN_USE if another session has already locked the record.

Include: datastor.h

See Also: DataStoreLoadRecord().

DataStoreLockRecord()

DataStoreDataError	DataStoreLockRecord(
        word		dsToken,	/* session token to opened datastore */
        RecordHeader		**recordPtrPtr);

Locks the current record. Used primarily when calling DataStoreFieldEnum() . Also used when calling DataStoreGetFieldPtr() . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Be Sure To: Unlock the locked record with DataStoreUnlockRecord() when you are through with the record.

Warnings: Returns DSDE_RECORD_BUFFER_EMPTY if there is no record loaded in the buffer.

Include: datastor.h

See Also: DataStoreUnlockRecord().

DataStoreMapRecordNumToID()

DataStoreDataError	DataStoreMapRecordNumToID(
        word		dsToken,	/* session token to opened datastore */
        RecordNum		recordNum,
        RecordID		*recordIDPtr);

Writes the RecordID corresponding to the passed RecordNum to recordIDPtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Include: datastor.h

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreGetFieldChunk() ... | Next: DataStoreNewRecord() ...


GEOS C Routines: DataStoreNewRecord() ...

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreGetRecordCount() ... | Next: DataStoreSetField() ...

DataStoreNewRecord()

DataStoreDataError	DataStoreNewRecord(
        word		dsToken);	/* session token to opened datastore */

Creates a new (empty) record and makes it the current record. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: A new record becomes a permanent part of the datatstore only after it is saved (with DataStoreSaveRecord() ).

Include: datastor.h

DataStoreOpen()

DataStoreError	DataStoreOpen(
        TCHAR		*dsNamePtr,
        optr		notifyObject,
        DataStoreOpenFlags		openFlag,
        word		*dsTokenPtr);

Opens the datastore referenced by dsNamePtr and writes the session token to dsTokenPtr . Passing DSOF_EXCLUSIVE in openFlag opens the datastore with access restricted to the caller; if another application already has the datastore open, returns DSE_ACCESS_DENIED. Passing NullOptr in notifyObject means no object will receive GWNT_DATASTORE_CHANGE notifications. Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreRecordEnum()

DataStoreError	DataStoreRecordEnum(
        word				dsToken,
        RecordNum				*startRecordPtr,
        DataStoreRecordEnumFlags				flags,
        void				*enumDataPtr,
        Boolean _pascal (*CallbackRoutine) (
            RecordHeader				record,
            void				*enumDataPtr));

Enumerates through the records of a datastore in storage order. When CallbackRoutine returns TRUE, stops enumeration, updates startRecordPtr with the RecordNum of the last record examined, and returns DSE_NO_ERROR. (To continue enumeration, increment startRecordPtr .) Returns DSE_NO_MORE_RECORDS if first or last record is reached.

Takes the following arguments:

dsToken
Session token to the opened datastore.
startRecordPtr
Pointer to the RecordNum at which to start enumeration (unless DSREF_START_AT_END is passed [in flags] in which case this argument is ignored).
flags
DataStoreRecordEnumFlags to pass. Passing DSREF_BACKWARDS causes enumeration to go backwards. Passing DSREF_START_AT_END with DSREF_BACKWARDS causes enumeration to go backwards starting at the last record. (Passing DSREF_START_AT_END without DSREF_BACKWARDS causes enumeration to start at the first record, ignoring whatever value is in startRecordPtr.)
enumDataPtr
Pointer to data that caller passes to the callback routine.
CallbackRoutine
Boolean subroutine. Caller passes in the RecordHeader of the current record and enumDataPtr. Returning TRUE ends enumeration.

Warnings: Callback can modify data passed in enumDataPtr but cannot modify any actual data in the record.

Datastore cannot be modified during enumeration.

Include: datastor.h

See Also: DataStoreFieldEnum(), DataStoreStringSearch().

DataStoreRemoveFieldFromRecord()

DataStoreDataError	DataStoreRemoveFieldFromRecord(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		fieldID);

Deletes the passed field from the current record. Returns a DataStoreError value (DSE_NO_ERROR if successful).

If referencing field by name, pass "0" in fieldID ; if referencing field by FieldID , pass NULL in fieldNamePtr .

Include: datastor.h

See Also: DataStoreDeleteField().

DataStoreRename()

DataStoreError	DataStoreRename(
        TCHAR		*oldNamePtr,
        TCHAR		*newNamePtr);

Changes the name of a datastore from oldNamePtr to newNamePtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreRenameField()

DataStoreStructureError	DataStoreRenameField(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*newNamePtr,
        TCHAR		*oldNamePtr,
        FieldID		fieldID);

Changes the name of a field from oldNamePtr to newNamePtr . Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful).

If referencing field by name, pass "0" in fieldID ; if referencing field by FieldID , pass NULL in oldNamePtr .

Warnings: Returns DSSE_FIELD_NAME_TOO_LONG if new name exceeds MAX_FIELD_NAME_LENGTH ( i.e. , 40 * sizeof(TCHAR)).

Include: datastor.h

DataStoreSaveRecord()

DataStoreDataError	DataStoreSaveRecord(
        word		dsToken,
        void		*dataPtr,
        sword _pascal (*CallbackRoutine) (
            RecordHeader		*Rec1Ptr,
            RecordHeader		*Rec2Ptr,
            word		dsToken,
            void		*dataPtr),
        RecordNum		*recordNumPtr,
        RecordID		*recordIDPtr);

Writes the current record to the datastore (saving any changes made to the record) and flushes the record from the buffer. If CallbackRoutine is "0", inserts saved record to the key (if a key exists) or appends the record to the end of the datastore if no key exists. Writes the RecordNum and RecordID of the saved record to recordNumPtr and recordIDPtr, respectively. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful). Takes the following arguments:

dsToken
Session token to the opened datastore.
dataPtr
Pointer to data that caller passes to the callback.
CallbackRoutine
Subroutine to determine where to insert saved record. Caller passes RecordHeader s of two records to compare. Caller can also pass extra data with dataPtr. The function should return:

-1if Rec1Ptr comes before Rec2Ptr
0 if Rec1Ptr equals Rec2Ptr
1 if Rec1Ptr comes after Rec2Ptr
recordNumPtr
Pointer to RecordNum of saved record.
recordIDPtr
Pointer to RecordID of saved record.

Include: datastor.h

See Also: DataStoreDiscardRecord().

DataStoreSetExtraData()

DataStoreError	DataStoreSetExtraData(
        word		dsToken,	/* session token to opened datastore */
        void		*extraDataPtr, /* data to write to file header */
        word		extraDataSize);

Writes extraDataSize bytes of extra data to the datastore file header, replacing any existing extra data. Passing "0" in extraDataSize deletes any existing extra data. Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreGetRecordCount() ... | Next: DataStoreSetField() ...


GEOS C Routines: DataStoreSetField() ...

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreNewRecord() ... | Next: DBAlloc() ...

DataStoreSetField()

DataStoreDataError	DataStoreSetField(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		fieldID,
        void		*dataPtr,
        word		dataSize);

Writes dataSize bytes of data (dataPtr) to the passed field. Passing "0" in dataSize deletes the field from the record. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

If referencing field by name, pass "0" in fieldID ; if referencing field by FieldID , pass NULL in fieldNamePtr .

Include: datastor.hblockHan

DataStoreSetNextRecordID()

DataStoreError	DataStoreSetNextRecordID(
        word		dsToken,	/* session token to opened datastore */
        RecordID		nextID);

Sets the next RecordID to be assigned to nextID. Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

See Also: DataStoreSetRecordID().

DataStoreSetRecordID()

DataStoreDataError	DataStoreSetRecordID(
        word		dsToken,	/* session token to opened datastore */
        RecordID		recordID);

Sets the RecordID of the current record to recordID; datastore must be opened for exclusive access. If the passed RecordID is greater than the next RecordID to be assigned, updates the latter with the passed RecordID + 1. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: Overwrites any existing record with the same RecordID as the passed value.

Include: datastor.h

See Also: DataStoreSetNextRecordID().

DataStoreSetTimeStamp()

DataStoreError	DataStoreSetTimeStamp(
        word		dsToken,	/* session token to opened datastore */
        FileDateAndTime		timestamp);

Sets date and time that datastore was last modified to timestamp. Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreSetVersion()

DataStoreError	DataStoreSetVersion(
        word		dsToken,	/* session token to opened datastore */
        ProtocolNumber		version);

Sets the datastore's protocol number; this is useful for keeping track of different versions of a datastore. Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreStringSearch()

DataStoreError	DataStoreStringSearch(
        word		dsToken,	/* session token to opened datastore */
        SearchParams		*paramsPtr);

Performs a string search on the datastore. If it finds a match, updates SP _startRecord with the RecordNum of the matching record and returns DSE_NO_ERROR. If it reaches the first or last record without finding a match, returns DSE_NO_MORE_RECORDS. Takes the following arguments:

dsToken
Session token to the opened datastore.
paramsPtr
Pointer to a SearchParams structure.
        typedef struct {
            SearchType		SP_searchType;
            RecordNum		SP_startRecord;
            dword		SP_maxRecords;
            FieldID		SP_startField;
            FieldCategory	SP_category;
            TCHAR		*SP_searchString;
            SearchFlags		SP_flags;
        } SearchParams;

Warnings: Returns DSSE_INDEX_RECORD_NUMBER_LIMIT_EXCEEDED if you call this routine on a datastore with more than 4,000 records.

Include: datastor.h

See Also: DataStoreRecordEnum(), DataStoreFieldEnum().

DataStoreUnlockRecord()

void	DataStoreUnlockRecord(
        word		dsToken);	/* session token to opened datastore */

Unlocks the current record. If you lock a record with DataStoreLock() , you must unlock it with this routine.

Include: datastor.h

See Also: DataStoreLockRecord().

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreNewRecord() ... | Next: DBAlloc() ...


GEOS C Routines: DBAlloc() ...

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreSetField() ... | Next: DBDeref() ...

DBAlloc()

DBItem	DBAlloc(
        VMFileHandle		file,
        DBGroup		group,
        word		size);

This routine allocates an item in the specified file and group. It is passed the handles for the file and group which will contain the new item. It returns the new item's item-handle.

Warnings: All pointers to items in the group may be invalidated.

Include: dbase.h

See Also: DBAllocUngrouped().

DBAllocUngrouped()

DBGroupAndItem 	DBAllocUngrouped(
        VMFileHandle		file,
        word		size);

This routine allocates an ungrouped item in the specified file. It is passed the handle of the file which will contain the new item. It returns the item's DBGroupAndItem value.

Warnings: All pointers to ungrouped items may be invalidated.

Include: dbase.h

See Also: DBAlloc().

DBCombineGroupAndItem()

DBGroupAndItem 	DBCombineGroupAndItem(
        DBGroup 		group,
        DBItem 		item);

This macro combines group and item handles into a dword-sized DBGroupAndItem value.

Include: dbase.h

See Also: DBGroupFromGroupAndItem(), DBItemFromGroupAndItem().

DBCopyDBItem()

DBItem 	DBCopyDBItem(
        VMFileHandle		srcFile,
        DBGroup		srcGroup,
        DBItem		srcItem,
        VMFileHandle		destFile,
        DBGroup		destGroup);

This routine makes a duplicate of a DB item in the specified DB file and group. It is passed the file handle, group handle, and item handle of the source item, as well as the file handle and group handle of the destination group. It makes a copy of the DB item and returns its DBItem handle.

Warnings: All pointers to items in the destination group may be invalidated.

Include: dbase.h

See Also: VMCopyVMChain().

DBCopyDBItemUngrouped()

DBGroupAndItem 	DBCopyDBItemUngrouped(
        VMFileHandle		srcFile,
        DBGroupAndItem		srcID,				/* source item */
        VMFileHandle		destFile);

This routine makes a duplicate of a specified DB item. It is passed the file handle and DBGroupAndItem value specifying the source item, and the file handle of the destination file. It allocates the item as an ungrouped item in the specified file and returns its DBGroupAndItem value.

Tips and Tricks: If the source item is not ungrouped, you can combine the group and item handles into a DBGroupAndItem value by calling the macro DBCombineGroupAndItem() .

Warnings: All pointers to ungrouped items in the destination file may be invalidated.

Include: dbase.h

See Also: VMCopyVMChain().

DBDeleteAt()

void	DBDeleteAt(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item,
        word		deleteOffset,
        word		deleteCount);

This routine deletes a sequence of bytes from within an item. It does not invalidate pointers to other items. The routine is passed the file, group, and item handles specifying the item, as well as an offset within the item and a number of bytes to delete. It will delete the specified number of bytes from within the item, starting with the byte at the specified offset.

Include: dbase.h

DBDeleteAtUngrouped()

void	DBDeleteAtUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id,
        word		deleteOffset,
        word		deleteCount);

This routine is just like DBDeleteAt() , except it is passed a DBGroupAndItem value instead of separate group and item handles. It does not invalidate pointers to other items.

Include: dbase.h

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreSetField() ... | Next: DBDeref() ...


GEOS C Routines: DBDeref() ...

| Up: GEOS SDK TechDocs | Up | Prev: DBAlloc() ... | Next: DBLock() ...

DBDeref()

void *	DBDeref(
        optr		*ref);

This routine is passed an optr to a locked DB item. The routine returns the address of the item.

Warnings: The optr becomes invalid when the DB item is unlocked.

Include: dbase.h

DBDirty()

void	DBUnlock(
        const void *		ptr);

This routine marks a DB item as dirty; this insures that the VM manager will copy its item-block back to the disk before freeing its memory. The routine is passed a pointer to anywhere within the item.

Tips and Tricks: All the items in an item block are marked dirty at once; thus, you can call this routine just once for several items in the same item block. Only the segment portion of the pointer is significant; thus, you can pass a pointer to anywhere in the item. This is useful if you have incremented the pointer to the item.

Include: dbase.h

DBFree()

void	DBFree(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This routine frees the specified item. It does not invalidate pointers to other items in the group. It is passed the file, group, and item handles specifying the item; it does not return anything.

Never Use Situations:
Never call DBFree() on a locked item. If you do, the item-block's lock count will not be decremented, which will prevent the item block from ever being properly unlocked.

Include: dbase.h

See Also: DBFreeUngrouped().

DBFreeUngrouped()

void	DBFreeUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id);

This routine frees the specified item. It does not invalidate pointers to other ungrouped items. It is passed the file handle and DBGroupAndItem value specifying the item; it does not return anything.

Never Use Situations: Never call DBFreeUngrouped() on a locked item. If you do, the item-block's lock count will not be decremented, which will prevent the item block from ever being properly unlocked.

Include: dbase.h

See Also: DBFree().

DBGetMap()

DBGroupAndItem 	DBGetmap(
        VMFileHandle		file);

This routine returns the DBGroupAndItem structure for the passed file's map item. If there is no map item, it returns a null handle.

Include: dbase.h

See Also: DBSetMap(), DBLockMap().

DBGroupAlloc()

DBGroup	DBGroupAlloc(
        VMFileHandle		file);

This routine allocates a new DB group in the specified file and returns its handle. If the group cannot be allocated, DBGroupAlloc() returns a null handle.

Include: dbase.h

DBGroupFree()

void	DBGroupFree(
        VMFileHandle		file,
        DBGroup		group);

This routine frees the specified group. This deletes all items and item-blocks associated with the group. It is passed the file and group handle specifying the group. Note that you can free a group even if some of its items are locked; those locked items will also be freed.

Include: dbase.h

DBGroupFromGroupAndItem()

DBGroup	DBGroupFromGroupAndItem(
        DBGroupAndItem		id);

This macro returns the DBGroup portion of a DBGroupAndItem value.

Include: dbase.h

DBInsertAt()

void	DBInsertAt(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item,
        word		insertOffset,
        word		insertCount);

This routine inserts bytes at a specified offset within a DB item. The bytes are zero-initialized. It is passed the file, group, and item handles specifying a DB item, as well as an offset within the cell and a number of bytes to insert. It inserts the specified number of bytes beginning at the specified offset; the data which was at the passed offset will end up immediately after the inserted bytes.

Warnings: This routine invalidates pointers to other items in the same group.

Include: dbase.h

DBInsertAtUngrouped()

void	DBInsertAtUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id,
        word		insertOffset,
        word		insertCount);

This routine is just like DBInsertAt() , except it is passed a DBGroupAndItem value instead of separate group and item handles.

Warnings: This routine invalidates pointers to other ungrouped items.

Include: dbase.h

DBItemFromGroupAndItem()

DBItem	DBItemFromGroupAndItem(
        DBGroupAndItem		id);

This macro returns the DBItem portion of a DBGroupAndItem value.

Include: dbase.h

| Up: GEOS SDK TechDocs | Up | Prev: DBAlloc() ... | Next: DBLock() ...


GEOS C Routines: DBLock() ...

| Up: GEOS SDK TechDocs | Up | Prev: DBDeref() ... | Next: DiskCheckInUse() ...

DBLock()

void *	DBLock(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This routine locks the specified item and returns a pointer to it. It is passed the file, group, and item handles specifying a DB item. If it fails, it returns a null pointer.

Include: dbase.h

See Also: DBLockGetRef(), DBLockUngrouped().

DBLockGetRef()

void *	DBLockGetRef(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item,
        optr *		ref);

This routine is just like DBLock() , except that it writes the item's optr to the passed address.

Include: dbase.h

Warnings: The optr is only valid until the DB item is unlocked.

DBLockGetRefUngrouped()

void *	DBLockGetRefUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id,
        optr *		ref);

This routine is the same as DBLockGetRef() , except that it is passed a DBGroupAndItem value.

Include: dbase.h

DBLockMap()

void *	DBLockMap(
        VMFileHandle		file);

This routine locks the specified file's map item and returns its address. To unlock the map item, call DBUnlock() normally.

Include: dbase.h

See Also: DBLockMap().

DBLockUngrouped()

void *	DBLockUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id);

This routine is the same as DBLock() , except that it is passed a DBGroupAndItem value.

Include: dbase.h

DBReAlloc()

void	DBReAlloc(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item,
        word		size);

This routine changes the size of a DB item. It is passed the file, group, and item handles specifying the DB item, and a new size for the item (in bytes). If the new size is larger than the old, space will be added to the end of the item; if the new size is smaller than the old, the item will be truncated to fit.

Warnings: If the new size is larger than the old, all pointers to items in the group are invalidated. Space added is not zero-initialized.

Include: dbase.h

DBReAllocUngrouped()

void	DBReAllocUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id,
        word		size);

This routine is just like DBReAlloc() , except it is passed a DBGroupAndItem value instead of separate group and item handles.

Warnings: If the new size is larger than the old, all pointers to ungrouped items are invalidated. Space added is not zero-initialized.

Include: dbase.h

DBSetMap()

void	DBSetMap(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This routine sets the DB map item. You can later retrieve a DBGroupAndItem structure identifying this item by calling DBGetMap() . The routine is passed the file, group, and item handles specifying the new map item; it does not return anything.

Include: dbase.h

DBSetMapUngrouped()

void	DBSetMapUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id);

This routine is just like DBSetMap() , except it is passed a DBGroupAndItem value instead of separate group and item handles.

Include: dbase.h

DBUnlock()

void	DBUnlock(
        void *	ptr); /* address of item to unlock */

This routine unlocks the DB item whose address is passed.

Tips and Tricks: Only the segment address of the pointer is significant. Thus, you can pass a pointer to somewhere within an item (or immediately after it) to unlock it.

Be Sure To: If the item has been changed, make sure you call DBDirty() before you unlock it.

Include: dbase.h

| Up: GEOS SDK TechDocs | Up | Prev: DBDeref() ... | Next: DiskCheckInUse() ...


GEOS C Routines: DiskCheckInUse() ...

| Up: GEOS SDK TechDocs | Up | Prev: DBLock() ... | Next: DiskForEach() ...

DiskCheckInUse()

Boolean	DiskCheckInUse(
        DiskHandle		disk);

This routine checks if a registered disk is being used. If a file on that disk is open, or if a path on that disk is on some thread's directory stack, the routine will return true (i.e. non-zero); otherwise it will return false (i.e. zero). Note that a disk may be "in use" even if it is not currently in any drive.

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file (which is guaranteed to be in use).

Include: disk.h

DiskCheckUnnamed()

Boolean	DiskCheckUnnamed( /* returns true if disk is unnamed */
        DiskHandle		disk);

This routine checks if a registered disk has a permanent name. If the disk does not have a name, the routine returns true (i.e. non-zero); otherwise it returns false . Note that GEOS assigns a temporary name to unnamed disks when they are registered. To find out a disk's temporary or permanent name, call DiskGetVolumeName() .

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file.

See Also: DiskGetVolumeName().

Include: disk.h

DiskCheckWritable()

Boolean	DiskCheckWritable(
        DiskHandle		disk);

DiskCheckWritable() checks if a disk is currently writable. It returns false (i.e. zero) if the disk is not writable, whether by nature (e.g. a CD-ROM disk) or because the write-protect tab is on; otherwise it returns true (i.e. non-zero).

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file.

Include: disk.h

DiskCopy()

DiskCopyError 	DiskCopy(
        word		source,
        word		dest,
        Boolean _pascal (*callback)
        (DiskCopyCallback					code,
         DiskHandle					disk,
         word					param));

This routine copies one disk onto another. The destination disk must be formattable to be the same type as the source disk. The first two arguments specify the source and destination drive. These drives may or may not be the same. If they are different, they must take compatible disks.

A disk copy requires frequent interaction with the user. For example, the copy routine must prompt the user to swap disks when necessary. For this reason, DiskCopy() is passed a pointer to a callback routine. This routine handles all interaction with the user. It must be declared _pascal. Each time it is called, it is passed three arguments. The first is a member of the DiskCopyCallback enumerated type; this argument specifies what the callback routine should do. The second argument is a disk handle; its significance depends on the value of the DiskCopyCallback argument. The third argument is a word-sized piece of data whose significance depends on the value of the DiskCopyCallback argument. Note that either of these arguments may be null values, depending on the value of the DiskCopyCallback argument.

The callback routine can abort the copy by returning true (i.e. non-zero); otherwise, it should return false (i.e. zero). The callback routine is called for several situations, identified by the value of DiskCopyCallback associated with them:

CALLBACK_GET_SOURCE_DISK
The callback routine should prompt the user to insert the source disk into the appropriate drive. The second argument is meaningless for this call. The third argument is the number identifying the drive; use DriveGetName() to find the name for this drive.
CALLBACK_GET_DEST_DISK
The callback routine should prompt the user to insert the destination disk into the appropriate drive. The second argument is meaningless for this call. The third argument is the number identifying the drive.
CALLBACK_REPORT_NUM_SWAPS
The second argument is meaningless for this call. The third argument is the number of disk swaps that will be necessary. The callback routine may wish to report this number to the user and ask for confirmation.
CALLBACK_VERIFY_DEST_DESTRUCTION
If the destination disk has already been formatted, the callback routine will be called with this parameter. The callback routine may wish to remind the user that the destination disk will be erased. The second argument is the handle of the destination disk; this is useful if, for example, you want to report the disk's name. The third argument is the destination drive's number. If the callback routine aborts the copy at this time by returning non-zero, the destination disk will not be harmed.
CALLBACK_REPORT_FORMAT_PCT
If the destination disk needs to be formatted, DiskCopy() will periodically call the callback routine with this parameter. The callback routine may wish to notify the user how the format is progressing. In this case, the second argument will be meaningless; the third parameter will be the percentage of the destination disk which has been formatted. The callback routine may wish to notify the user how the format is progressing.
CALLBACK_REPORT_COPY_PCT
While the copy is taking place, DiskCopy() will periodically call the callback routine with this parameter. The callback routine may wish to notify the user how the copy is progressing. In this case, the second parameter will be meaningless; the third parameter will be the percentage of the copy which has been completed.

If the copy was successful, DiskCopy() returns zero. Otherwise, it returns a member of the DiskCopyError enumerated type. That type has the following members:

ERR_DISKCOPY_INSUFFICIENT_MEM
This is returned if the routine was unable to get adequate memory.
ERR_CANT_COPY_FIXED_DISKS
ERR_CANT_READ_FROM_SOURCE
ERR_CANT_WRITE_TO_DEST
ERR_INCOMPATIBLE_FORMATS
The destination drive must be able to write disks in exactly the same format as the source disk. Note that the source and destination drives may be the same.
ERR_OPERATION_CANCELLED
This is returned if the callback routine ever returned a non-zero value, thus aborting the copy.
ERR_CANT_FORMAT_DEST

Include: disk.h

DiskFind()

DiskHandle 	DiskFind(
        const char *		fname,			/* Null-terminated volume name */
        DiskFindResult *		code);			/* DiskFindResult written here */

This routine returns the handle of the disk with the specified name. If there is no registered disk with the specified name, DiskFind() returns a null handle. Note that while disk handles are unique, volume names are not; therefore, there may be several registered disks with identical volume names. For this reason, DiskFind() writes a member of the DiskFindResults enumerated type (described below) into the space pointed to by the code pointer.

Structures: DiskFind() uses the DiskFindResult enumerated type, which has the following values:

DFR_UNIQUE
There is exactly one registered disk with the specified name; its handle was returned.
DFR_NOT_UNIQUE
There are two or more registered disks with the specified name; the handle of an arbitrary one of these disks was returned.
DFR_NOT_FOUND
There are no registered disks with the specified name; a null disk handle was returned.

Tips and Tricks: If you want to find all the disks with a given volume name, call DiskForEach() and have the callback routine check each disk's name with DiskGetVolumeName() .

See Also: DiskRegisterDisk().

Include: disk.h

| Up: GEOS SDK TechDocs | Up | Prev: DBLock() ... | Next: DiskForEach() ...


GEOS C Routines: DiskForEach() ...

| Up: GEOS SDK TechDocs | Up | Prev: DiskCheckInUse() ... | Next: DiskGetDrive() ...

DiskForEach()

DiskHandle 	DiskForEach(
        Boolean _pascal (* callback)			(DiskHandle				disk)) 		/* callback returns true
                                     * to cancel */

This routine lets you perform an action on every registered disk. It calls the callback routine once for each disk, passing the disk's handle. The callback routine must be declared _pascal. The callback routine can force an early termination by returning true (i.e. non-zero). If the callback routine ever returns true , DiskForEach() terminates and returns the handle of the last disk passed to the callback routine. If the callback routine examines every disk without returning true , DiskForEach() returns a null handle.

Tips and Tricks: DiskForEach() is commonly used to look for a specific disk. The callback routine checks each disk to see if it's the one; if it finds a match, the callback routine simply returns true , and DiskForEach() returns the disk's handle.

Include: disk.h

DiskFormat()

FormatError 	DiskFormat(
        word 		driveNumber,
        MediaType	media,			/* Format to this size */
        DiskFormatFlags	flags,			/* See flags below */
        dword		*goodClusters,		/* These are filled in at the */
        dword		*badClusters,		/* of the format */
    DiskHandle 	disk,			/* handle of disk, or
                         * 0 if disk unformatted, or
                         * 1 if drive state unknown */
    char 		*volumeName,		/* new name of disk */
        Boolean _pascal (*callback)
            (word percentDone));	/* Return true to cancel */

This routine formats a disk to the specified size. When it is finished, it fills in the passed pointers to contain the number of good and bad clusters on the disk. (To find out the size of each cluster, call DiskGetVolumeInfo() .) The routine returns a member of the FormatError enumerated type (whose members are described below).

DiskFormat() can be instructed to call a callback routine periodically. This allows the application to keep the user informed about how the format is progressing. The callback routine is passed either the percent of the disk which has been formatted, or the cylinder and head currently being formatted. The callback routine must be declared _pascal. The callback routine can cancel the format by returning true (i.e. non-zero); otherwise, it should return false (i.e. zero).

The third argument passed is a word-length flag field. Currently, only three flags are defined:

DFF_CALLBACK_PERCENT_DONE
A callback routine should be called periodically. The callback routine should be passed a single argument, namely the percentage of the format which has been done.
DFF_CALLBACK_CYL_HEAD
A callback routine should be called periodically. The callback routine should be passed a single argument, namely the cylinder head being formatted. If both DFF_CALLBACK_PERCENT_DONE and DFF_CALLBACK_CYL_HEAD are passed, results are undefined. If neither flag is set, the callback routine will never be called; a null function pointer may be passed.
DFF_FORCE_ERASE
A "hard format" should be done, i.e. the sectors should be rewritten and initialized to zeros. If this flag is not set, DiskFormat() will do a "soft format" if possible; it will check the sectors and write a blank file allocation table, but it will not necessarily erase the data from the disk.

DiskFormat() returns a member of the FormatError enumerated type. If the format was successful, it will return the constant FMT_DONE (which is guaranteed to equal zero). Otherwise, it will return one of the following constants:

FMT_DRIVE_NOT_READY
FMT_ERROR_WRITING_BOOT
FMT_ERROR_WRITING_ROOT_DIR
FMT_ERROR_WRITING_FAT
FMT_ABORTED
FMT_SET_VOLUME_NAME_ERROR
FMT_CANNOT_FORMAT_FIXED_DISKS_IN_CUR_RELEASE
FMT_BAD_PARTITION_TABLE
FMT_ERR_NO_PARTITION_FOUND
FMT_ERR_CANNOT_ALLOC_SECTOR_BUFFER
FMT_ERR_DISK_IS_IN_USE
FMT_ERR_WRITE_PROTECTED
FMT_ERR_DRIVE_CANNOT_SUPPORT_GIVEN_FORMAT
FMT_ERR_INVALID_DRIVE_SPECIFIED
FMT_ERR_DRIVE_CANNOT_BE_FORMATTED
FMT_ERR_DISK_UNAVAILABLE

Include: disk.h

| Up: GEOS SDK TechDocs | Up | Prev: DiskCheckInUse() ... | Next: DiskGetDrive() ...


GEOS C Routines: DiskGetDrive() ...

| Up: GEOS SDK TechDocs | Up | Prev: DiskForEach() ... | Next: DiskSave() ...

DiskGetDrive()

word	DiskGetDrive(
        DiskHandle		dh);

This routine returns the drive number associated with a registered disk. Note that it will do this even if the drive is no longer usable (e.g. if a network drive has been unmapped).

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file.

See Also: DiskFind(), DiskRegisterDisk().

Include: disk.h

DiskGetVolumeFreeSpace()

dword	DiskGetVolumeFreeSpace(
        DiskHandle		dh);

This routine returns the amount of free space (measured in bytes) on the specified disk. If the disk is, by nature, not writable (e.g. a CD-ROM disk), DiskGetVolumeFreeSpace() returns zero and clears the thread's error value. If an error condition exists, DiskGetVolumeFreeSpace() returns zero and sets the thread's error value.

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file.

See Also: DiskGetVolumeInfo().

Include: disk.h

DiskGetVolumeInfo()

word	DiskGetVolumeInfo(  /* Returns 0 if successful */
        DiskHandle		dh,
        DiskInfoStruct		*info);			/* Routine fills this structure */

This routine returns general information about a disk. It returns the following four pieces of information:

  • The size of each disk block in bytes. When space is allocated, it is rounded up to the nearest whole block.
  • The number of free bytes on the disk.
  • The total number of bytes on the disk; this is the total of free and used space.
  • The disk's volume name. If the volume is unnamed, the current temporary name will be returned.

The information is written to the passed DiskInfoStruct . If an error condition occurs, DiskGetVolumeInfo() will return the error code and set the thread's error value; otherwise, it will return zero.

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file.

Include: disk.h

DiskGetVolumeName()

void	DiskGetVolumeName(
        DiskHandle		dh,
        char *		buffer);			/* Must be VOLUME_NAME_LENGTH_ZT bytes
                         * long */

This routine copies the disk's volume name (as a null-terminated string) to the passed buffer. If an error occurs, it sets the thread's error value. If the volume has no name, the routine returns the current temporary name.

Warnings: DiskGetVolumeName() does not check the size of the buffer passed. If the buffer is not at least VOLUME_NAME_LENGTH_ZT bytes long, the routine may write beyond its boundaries.

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file.

See Also: DiskGetVolumeInfo(), DiskSetVolumeName().

DiskRegisterDisk()

DiskHandle 	DiskRegisterDisk(
        word	driveNumber);

This routine registers a disk in the specified drive and assigns it a disk handle. (The disk handle persists only to the end of the current session of GEOS.) If the disk already has a handle, DiskRegisterDisk() will return it. If the disk does not have a name, GEOS will assign it a temporary name (such as "UNNAMED1") and display an alert box telling the user what the temporary name is. (This is done only the first time the disk is registered in each session.) Note that the temporary name is not written to the disk; thus, it persists only until the end of the current session of GEOS.

If this routine returns a disk handle, there's a disk in the drive; if it doesn't, there may still be a disk in the drive, but the disk is unformatted.

Tips and Tricks: There is no harm in registering the same disk several times. Thus, if you want to get the disk handle for the disk in a specific drive, you can simply call DiskRegisterDisk() .

See Also: DiskRegisterDiskSilently().

Include: disk.h

DiskRegisterDiskSilently()

DiskHandle 	DiskRegisterDiskSilently(
        word		driveNumber);

This routine is almost identical to DiskRegisterDisk() (described immediately above). There is only one difference: If GEOS assigns a temporary name to the disk, it will not present an alert box to the user.

See Also: DiskRegisterDisk().

Include: disk.h

DiskRestore()

DiskHandle 	DiskRestore(
        void *		buffer,			/* buffer written by DiskSave() */
        DiskRestoreError _pascal (*callback)
            (const char 						*driveName,
             const char 						*diskName,
             void 						**bufferPtr,
                 DiskRestoreError 						error);

DiskRestore() examines a buffer written by DiskSave() and returns the handle of the disk described by that buffer. If that disk is already registered, DiskRestore() will simply return its handle. If the disk is not registered and is not in the drive, DiskRestore() will call the specified callback routine. The callback routine should be declared _pascal. The callback routine is passed four arguments:

  • A null-terminated string containing the name of the drive for the disk.
  • A null-terminated string containing the disk's volume label.
  • A pointer to a variable in the DiskRestore() routine. This variable is itself a pointer to the opaque data structure provided by DiskSave() . If the callback routine takes any action which causes that structure to move (e.g. if it causes the global or local heap containing the buffer to be shuffled), it should update the pointer in DiskRestore() .
  • A member of the DiskRestoreError enumerated type. This is the error which DiskRestore() would have returned if there had not been a callback routine. This is usually DRE_REMOVABLE_DRIVE_DOESNT_HOLD_DISK.

The callback routine should prompt the user to insert a disk. If the callback routine was successful, it should return DRE_DISK_IN_DRIVE (which is guaranteed to be equal to zero). Otherwise, it should return a member of the DiskRestoreError enumerated type; usually it will return DRE_USER_CANCELLED_RESTORE. Note that the callback routine will not generally know if the user has inserted a disk; it generally just displays an alert box and returns when the user clicks "OK." After the callback routine returns, DiskRestore() registers the disk and makes sure that it's the correct one; if it is not, it calls the callback routine again.

You can pass a null function pointer to DiskRestore() instead of providing a callback routine. In this case, DiskRestore() will fail if the disk has not been registered and is not currently in the drive.

DiskRestore() returns the handle of the disk. If it fails for any reason, it returns a null handle and sets the thread's error value to a member of the DiskRestorenError enumerated type. This type has the following members:

DRE_DISK_IN_DRIVE
This is returned by the callback routine. This is guaranteed to equal zero.
DRE_DRIVE_NO_LONGER_EXISTS
The disk is associated with a drive which is no longer attached to the system.
DRE_REMOVABLE_DRIVE_DOESNT_CONTAIN_DISK
The disk is unregistered, and it is not currently in the drive associated with it. If a callback routine was provided, DiskRestore() will call it under these circumstances.
DRE_USER_CANCELLED_RESTORE
This is returned by the callback routine if the user cancels the restore.
DRE_COULDNT_CREATE_NEW_DISK_HANDLE
DiskRestore() was unable to register the disk in the appropriate drive because it couldn't create a new disk handle.
DRE_REMOVABLE_DRIVE_IS_BUSY
The appropriate drive is busy with a time-consuming operation (e.g. a disk format).
DRE_NOT_ATTACHED_TO_SERVER
The disk was from a network server to which we are not logged in.
DRE_PERMISSION_DENIED
The disk was on a network which is now denying access to it.
DRE_ALL_DRIVES_USED
The disk was on a network volume that isn't mounted, but there is no drive left to which it can be mapped.

See Also: DiskSave().

Include: disk.h

| Up: GEOS SDK TechDocs | Up | Prev: DiskForEach() ... | Next: DiskSave() ...


GEOS C Routines: DiskSave() ...

| Up: GEOS SDK TechDocs | Up | Prev: DiskGetDrive() ... | Next: DriveGetDefaultMedia() ...

DiskSave()

Boolean	DiskSave(
        DiskHandle		disk,
        void *		buffer,				/* data will be written here */
        word *		bufferSize);				/* Size of buffer (in bytes) */

This routine writes information about a disk in the specified buffer. DiskRestore() can use this information to return the disk handle, even in another session of GEOS. The bufferSize argument should point to a word containing the size of the buffer (in bytes). If the buffer is large enough, DiskSave() will write an opaque data structure into the buffer, and change the value of *bufferSize to the actual size of the data structure; any extra buffer space can be freed or otherwise used. In this case, DiskSave() will return true (i.e. non-zero). If the buffer was too small, DiskSave() will return false (i.e. zero) and write the size needed into *bufferSize . Simply call DiskSave() again with a large enough buffer. If DiskSave() failed for some other reason, it will return false and set *bufferSize to zero.

See Also: DiskRestore().

Include: disk.h

DiskSetVolumeName()

word	DiskSetVolumeName(
        DiskHandle		dh,
        const char *		name);			/* Change the name to this */

This routine changes the disk's volume label. If it is successful, it returns zero; otherwise it returns an error code. It also sets or clears the thread's error value appropriately. The following error codes may be returned:

ERROR_INVALID_VOLUME
An invalid disk handle was passed to the routine.
ERROR_ACCESS_DENIED
For some reason, the volume's name could not be changed. For example, the volume might not be writable.
ERROR_DISK_STALE
The drive containing that disk has been deleted. This usually only happens with network drives.

Include: disk.h

DosExec()

word	DosExec(
        const char *		prog,
        DiskHandle 		progDisk,
        const char *		arguments,
        const char *		execDir,
        DiskHandle 		execDisk,
        DosExecFlags 		flags);

This routine shuts down GEOS to run a DOS program. It returns an error code if an error occurs or zero if successful. Its parameters are listed below:

prog
A pointer to a null-terminated character string representing the path of the program to be run. If a null string (not a null pointer), the system's DOS command interpreter will be run. The path string should not contain the drive name.
progDisk
A disk handle indicating the disk on which the program to be executed sits. If zero is passed, the disk on which GEOS resides will be used.
arguments
A pointer to a locked or fixed buffer containing arguments to be passed to the program being run.
execDir
A pointer to a null-terminated character string representing the path in which the program is to be run. The string should not contain the drive name. If a null pointer is passed and execDisk is zero, the program will be run in the directory in which GEOS was first started.
execDisk
The disk handle of the disk containing the directory in execDir .
flags
A record of DosExecFlags indicating whether the DOS program will give a prompt to the user to return to GEOS. The possible flags are DEF_PROMPT, DEF_FORCED_SHUTDOWN, and DEF_INTERACTIVE. For more information, see the entry for DosExecFlags .

If there was no error, DosExec() will return zero. Otherwise it will return one of the following error values: ERROR_FILE_NOT_FOUND, ERROR_DOS_EXEC_IN_PROGRESS, ERROR_INSUFFICIENT_MEMORY, or ERROR_ARGS_TOO_LONG.

Include: system.h

| Up: GEOS SDK TechDocs | Up | Prev: DiskGetDrive() ... | Next: DriveGetDefaultMedia() ...


GEOS C Routines: DriveGetDefaultMedia() ...

| Up: GEOS SDK TechDocs | Up | Prev: DiskSave() ... | Next: EC() ...

DriveGetDefaultMedia()

MediaType 	DriveGetDefaultMedia(
        word		driveNumber);

This routine returns the default media type for the specified drive. It returns a member of the MediaType enumerated type (described in the Data Structures reference). Note that a drive can be used for media types other than the default. For example, a high-density 3.5-inch drive will have a default media type of MEDIA_1M44, but it can read from, write to, and format 3.5-inch disks with size MEDIA_720K.

See Also: DriveTestMediaSupport().

Include: drive.h

DriveGetExtStatus()

word	DriveGetExtStatus(
        word		driveNumber);

This routine is much like DriveGetStatus() (described immediately below). However, in addition to returning all of the flags set by DriveGetStatus() , it also sets additional flags in the upper byte of the return value. It returns the following additional flags:

DES_LOCAL_ONLY
This flag is set if the device cannot be viewed over a network.
DES_READ_ONLY
This flag is set if the device is read only, i.e. no data can ever be written to a volume mounted on it (e.g., a CD-ROM drive).
DES_FORMATTABLE
This flag is set if disks can be formatted in the drive.
DES_ALIAS
This flag is set if the drive is actually an alias for a path on another drive.
DES_BUSY
This flag is set if the drive will be busy for an extended period of time (e.g., if a disk is being formatted).

If an error condition exists, DriveGetExtStatus() returns zero.

See Also: DriveGetStatus().

Include: drive.h

DriveGetName()

char *	DriveGetName(
        word	driveNumber,	/* Get name of this drive */
        char *	buffer,	/* Write name to this buffer */
        word	bufferSize);	/* Size of buffer (in bytes) */

This routine finds the name of a specified drive. You should use this name when prompting the user to take any action regarding this drive (e.g. to insert a disk). The routine writes the name, as a null terminated string, to the buffer passed. It returns a pointer to the trailing null. If the drive does not exist, or the buffer is too small, DriveGetName() returns a null pointer.

Include: drive.h

DriveGetStatus()

word	DriveGetStatus(
        word	driveNumber);

This routine returns the current status of a drive. The drive is specified by its drive number. The routine returns a word of DriveStatus flags. These flags are listed below:

DS_PRESENT
This flag is set if the physical drive exists, regardless of whether the drive contains a disk.
DS_MEDIA_REMOVABLE
This flag is set if the disk can be removed from the drive.
DS_NETWORK
This flag is set if the drive is accessed over a network (or via network protocols), which means the drive cannot be formatted or copied.
DS_TYPE
This is a mask for the lowest four bits of the field. These bits contain a member of the DriveType enumerated type.

If an error condition exists, DriveGetStatus() returns zero.

See Also: DriveGetExtStatus().

Include: drive.h

DriveTestMediaSupport()

Boolean	DriveTestMediaSupport(
        word		DriveNumber,
        MediaType		media);				/* Desired disk size */

This routine checks whether the specified drive can support disks in the specified size. It returns true (i.e., non-zero) if the drive supports the size.

See Also: DriveGetDefaultMedia().

Include: drive.h

| Up: GEOS SDK TechDocs | Up | Prev: DiskSave() ... | Next: EC() ...


GEOS C Routines: EC() ...

| Up: GEOS SDK TechDocs | Up | Prev: DriveGetDefaultMedia() ... | Next: ECCheckHugeArray() ...

EC()

void	EC(line);

This macro defines a line of code that will only be compiled into the error-checking version of the geode. The line parameter of the macro is the actual line of code. When the EC version of the program is compiled, the line will be treated as a normal line of code; when the non-EC version is compiled, the line will be ignored.

See Also: NEC().

EC_BOUNDS()

void	EC_BOUNDS(addr);

This macro adds an address check to the error-checking version of a program. When the EC version of the program is compiled, the address check will be included; when the non-EC version is compiled, the address check will be left out. The addr parameter is the address or pointer to be checked.

The macro expands to a call to ECCheckBounds() on the specified address or pointer. If the address is out of bounds, the program will stop with a call to FatalError() .

See Also: ECCheckBounds().

EC_ERROR()

void	EC_ERROR(code);

This macro inserts a call to FatalError() in the error-checking version of the program and does nothing to the non-EC version. When the program gets to this point, it will halt and put up an error message corresponding to the specified error code . If a condition should be checked before calling FatalError() , you can use EC_ERROR_IF() instead.

EC_ERROR_IF()

void	EC_ERROR_IF(test, code);

This macro inserts a conditional call to FatalError() in the error-checking version of a program; it does nothing for the non-EC version. The test parameter is a Boolean value that, if true , will cause the FatalError() call to be made. If test is false , FatalError() will not be called.

EC_WARNING()

EC_WARNING(word warningCode);

This macro generates a warning for the debugger when executed by error-checking code; it has no effect when in non-EC code.

Include: ec.h

EC_WARNING_IF()

EC_WARNING_IF(<expr>, word warningCode)

When this macro is executed in error-checking code, it tests <expr>; if <expr> is non-zero, it generates a warning with code warningCode for the debugger.

In non-EC code, the macro has no effect (and <expr> is not evaluated).

Include: ec.h

ECCheckBounds()

void	ECCheckBounds(
        void	*address);

This routine checks to see if the given pointer is within bounds of the block into which it points. If assertions fail, a fatal error will occur. If the "lmem" EC flag is turned on and the pointer points within an LMem block, then this routine will also check that the pointer points to a valid chunk handle or a chunk within the LMem block.

Include: ec.h

ECCheckChunkArray()

void	ECCheckChunkArray(
        optr	o);

This routine checks the validity of the specified chunk array. If the assertions fail, a fatal error will occur. The block must be locked before calling this routine.

Include: ec.h

ECCheckChunkArrayHandles()

void	ECCheckChunkArrayHandles(
        MemHandle mh,
        ChunkHandle ch);

This routine checks the validity of the specified chunk array. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckClass()

void	ECCheckClass(
        ClassStruct *class);

This routine checks that the given pointer actually references a class definition. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckDriverHandle()

void	ECCheckDriverHandle(
        GeodeHandle gh);

This routine checks that the passed handle actually references a driver. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckEventHandle()

void	ECCheckEventHandle(
        EventHandle eh);

This routine checks that the passed handle actually references a stored message. If the assertions fail, a fatal error will occur.

ECCheckFileHandle()

void	ECCheckFileHandle(
        FileHandle file);

This routine checks that the passed handle actually is a file handle and references a file. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckGeodeHandle()

void	ECCheckGeodeHandle(
        GeodeHandle gh);

This routine checks that the passed handle references a loaded geode. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckGStateHandle()

void	ECCheckGStateHandle(
        GStateHandle gsh);

This routine checks that the passed handle references a GState. If the assertions fail, a fatal error will occur.

Include: ec.h

| Up: GEOS SDK TechDocs | Up | Prev: DriveGetDefaultMedia() ... | Next: ECCheckHugeArray() ...


GEOS C Routines: ECCheckHugeArray() ...

| Up: GEOS SDK TechDocs | Up | Prev: EC() ... | Next: ECCheckProcessHandle() ...

ECCheckHugeArray()

void	ECCheckHugeArray(
        VMFileHandle		vmFile,
        VMBlockHandle		vmBlock);

This routine checks the validity of the passed Huge Array. If the block passed is not the directory block of a Huge Array, the routine fails.

Include: ec.h

ECCheckLibraryHandle()

void	ECCheckLibraryHandle(
        GeodeHandle gh);

This routine checks that the passed handle references a library. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemChunk()

void	ECCheckLMemChunk(
        void * chunkPtr);

This routine checks the validity of the chunk pointed to by chunkPtr . If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemHandle()

void	ECCheckLMemHandle(
        MemHandle mh);

This routine checks that the passed handle is a memory handle and actually references a local memory block. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemHandleNS()

void	ECCheckLMemHandleNS(
        MemHandle mh);

This routine checks that the passed handle is a local memory handle; unlike ECCheckLMemHandle() , however, it does not check sharing violations (when threads are illegally using non-sharable memory). If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemObject()

void	ECCheckLMemObject(
        optr	obj); 		/* Object must be locked down. */

This routine checks the validity of an object to ensure that it is an object stored in an object block. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemObjectHandles()

void	ECCheckLMemObjectHandles(
        MemHandle mh,
        ChunkHandle ch);

This routine checks the validity of an object to ensure that it is an object stored in an object block. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemOD()

void	ECCheckLMemOD(
        optr	o);

This routine checks the validity of the given local-memory-based object. If assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemODHandles()

void	ECCheckLMemODHandles(
        MemHandle objHan,
        ChunkHandle objCh);

This routine checks the validity of the given local-memory-based object. If assertions fail, a fatal error will occur.

Include: ec.h

ECCheckMemHandle()

void	ECCheckMemHandle(
        MemHandle mh);

This routine checks that the passed handle is a memory handle that references a memory block. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckMemHandleNS()

void	ECCheckMemHandleNS(
        MemHandle mh);

This routine checks that the passed handle references a memory block; unlike ECCheckMemHandle() , however, it will not check for sharing violations (when a thread illegally accesses a non-sharable block). If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckObject()

void	ECCheckObject(
        optr	obj);

This routine checks the validity of the given locked object. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckObjectHandles()

void	ECCheckObjectHandles(
        Memhandle mh,
        ChunkHandle ch);

This routine checks the validity of the given locked object. If the assertions fail, a fatal error will occur.

ECCheckOD()

void	ECCheckOD(
        optr	obj);

This routine checks the validity of the given object. Unlike ECCheckLMemObject() , however, it allows optrs of Process objects to be specified. If assertions fail, a fatal error will occur.

ECCheckODHandles()

void	ECCheckODHandles(
        MemHandle objHan,
        ChunkHandle objCh);

This routine checks the validity of the given object. Unlike ECCheckLMemObjectHandles() , however, it allows processes to be specified. If assertions fail, a fatal error will occur.

Include: ec.h

| Up: GEOS SDK TechDocs | Up | Prev: EC() ... | Next: ECCheckProcessHandle() ...


GEOS C Routines: ECCheckProcessHandle() ...

| Up: GEOS SDK TechDocs | Up | Prev: ECCheckHugeArray() ... | Next: ECVMCheckVMBlockHandle() ...

ECCheckProcessHandle()

void	ECCheckProcessHandle(
        GeodeHandle gh);

This routine checks that the passed handle actually references a process. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckQueueHandle()

void	ECCheckQueueHandle(
        QueueHandle qh);

This routine ensures the passed handle references an event queue. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckResourceHandle()

void	ECCheckResourceHandle(
        MemHandle mh);

This routine ensures that the passed handle references a geode resource. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckStack()

void	ECCheckStack();

This routine checks to make sure the current stack has not overflown (and is not about to). This routine also enforces a 100-byte gap between the stack bottom and the stack pointer. If assertions fail, a fatal error will occur.

Include: ec.h

ECCheckThreadHandle()

void	ECCheckThreadHandle(
        ThreadHandle th);

This routine checks that the passed handle actually references a thread. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckWindowHandle()

void	ECCheckWindowHandle(
        WindowHandle wh);

This routine checks that the passed handle actually references a window. If the assertions fail, a fatal error will occur.

Include: ec.h

ECLMemExists()

void	ECLMemExists(
        optr	o);

This routine checks to see if the specified chunk exists. This routine should be called by applications to check the chunk handle's validity. If the assertions fail, a fatal error will occur.

Include: ec.h

ECLMemExistsHandles()

void	ECLMemExistsHandles(
        MemHandle mh,
        ChunkHandle ch);

This routine checks to see if the specified chunk exists. This routine should be called by applications to check the chunk handle's validity. If the assertions fail, a fatal error will occur.

Include: ec.h

ECLMemValidateHandle()

void	ECLMemValidateHandle(
        optr	o);

This routine checks that the passed optr points to a local memory chunk. If the assertions fail, a fatal error will occur.

Include: ec.h

ECLMemValidateHandleHandles()

void	ECLMemValidateHandleHandles(
        MemHandle mh,
        ChunkHandle ch);

This routine checks that the passed memory and chunk handles actually reference a local memory chunk. If the assertions fail, a fatal error will occur.

Include: ec.h

ECLMemValidateHeap()

void	ECLMemValidateHeap(
        MemHandle mh);

This routine does a complete error-check of the LMem heap. It is used internally and should not be needed by application programmers.

Include: ec.h

ECMemVerifyHeap()

void	ECMemVerifyHeap()

This routine makes sure the global heap is in a consistent state. If the assertions fail, a fatal error will occur. This routine should likely not be called by anything other than the EC kernel.

Include: ec.h

ECVMCheckMemHandle()

void	ECVMCheckMemHandle(
        MemHandle han);

This routine checks that the given memory handle is actually linked to a VM block handle. If assertions fail, a fatal error will occur.

Include: ec.h

| Up: GEOS SDK TechDocs | Up | Prev: ECCheckHugeArray() ... | Next: ECVMCheckVMBlockHandle() ...


GEOS C Routines: ECVMCheckVMBlockHandle() ...

| Up: GEOS SDK TechDocs | Up | Prev: ECCheckProcessHandle() ... | Next: ElementArrayDelete() ...

ECVMCheckVMBlockHandle()

void	ECVMCheckVMBlockHandle(
        VMFileHandle file,
        VMBlockHandle block);

This routine checks the validity of the given VM file and block handles. If assertions fail, a fatal error will occur.

Include: ec.h

ECVMCheckVMFile()

void	ECVMCheckVMFile(
        VMFileHandle file);

This routine checks the validity of the given VM file handle. If assertions fail, a fatal error will occur.

Include: ec.h

ElementArrayAddElement ()

word	ElementArrayAddElement(
        optr	arr,		/* Handle of element array */
        void *	element,		/* Element to add (if necessary) */
        dword	callBackData,		/* This is passed to the Callback routine */
        Boolean _pascal (*callback) (void *elementToAdd,
            void *elementFromArray, 			dword valueForCallback));

This routine is used to add elements to an array. It is passed the address of a potential element. It compares the element with each member of an element array. If there are no matches, it adds the element to the array and sets the reference count to one. If there is a match, it increments the reference count of the matching element in the array and returns; it does not add the new element. When you pass the address of an element, make sure you pass the address of the data portion of the element (not the reference-count header).

You can pass a callback routine to ElementArrayAddElement() . ElementArrayAddElement() will call the callback routine to compare elements and see if they match. The callback routine should be declared _pascal. ElementArrayAddElement() passes the callback routine the address of the element you passed it, as well as the address of the data-portion of the element in the array (the part after the RefElementHeader structure). If the two elements match (by whatever criteria you use), return true ; otherwise, return false . If you pass a null function pointer, the default comparison routine will be called, which checks to see if every data byte matches.

Include: chunkarr.h

Tips and Tricks: If you know the element is already in the array, you can increment its reference count by calling ElementArrayAddReference() .

Be Sure To: Lock the block on the global heap before calling (unless it is fixed).

See Also: ElementArrayAddReference().

ElementArrayAddElementHandles()

word	ElementArrayAddElementHandles(
        MemHandle		mh,				/* Global handle of LMem heap */
        ChunkHandle		chunk				/* Chunk handle of element array */
        void *		element,				/* Element to add */
        dword		callBackData,		/* Passed to the Callback routine */
        Boolean _pascal (*callback) (void *elementToAdd,
            void *elementFromArray, 			dword valueForCallback));

This routine is exactly like ElementArrayAddElement() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

Tips and Tricks: If you know the element is already in the array, you can increment its reference count by calling ElementArrayAddReference() .

Be Sure To: Lock the block on the global heap before calling (unless it is fixed).

See Also: ElementArrayAddReference().

ElementArrayAddReference()

void	ElementArrayAddReference(
        optr	arr,				/* optr to element array */
        word	token);				/* Index number of element */

This routine increments the reference count of a member of an element array.

Be Sure To: Lock the block on the global heap before calling (unless it is fixed).

See Also: ElementArrayAddElement().

ElementArrayAddReferenceHandles()

void	ElementArrayAddReferenceHandles(
        MemHandle		mh,				/* Handle of LMem heap's block */
        ChunkHandle		ch,				/* Handle of element array */
        word		token);				/* Index number of element */

This routine is exactly like ElementArrayAddReference() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

ElementArrayCreate()

ChunkHandle ElementArrayCreate(
        MemHandle		mh,				/* Handle of LMem heap's block */
        word		elementSize,				/* Size of each element, or zero
                             * for variable-sized */
        word		headerSize);				/* Header size (zero for default) */

This routine creates an element array in the indicated LMem heap. It creates an ElementArrayHeader structure at the head of the chunk. If you want to leave extra space before the start of the array, you can pass a larger header size; if you want to use the standard header, pass a header size of zero.

You can specify the size of each element. Remember that the first three bytes of every element in an element array are the element's RefElementHeader ; structure, which contains the reference count; leave room for this when you choose a size. For arrays with variable-sized elements, pass a size of zero.

Include: chunkarr.h

Tips and Tricks: You may want to declare a structure for array elements; the first component should be a RefElementHeader . You can pass the size of this structure to ElementArrayCreate() .

If you want extra space after the ElementArrayHeader , you may want to create your own header structure, the first element of which is an ElementArrayHeader . You can pass the size of this header to ElementArrayCreate() , and access the data in your header via the structure.

Be Sure To: Lock the block on the global heap before calling this routine (unless it is fixed). If you pass a header size, make sure it is larger than sizeof(ElementArrayHeader) .

ElementArrayCreateAt()

ChunkHandle 	ElementArrayCreateAt(
        optr	arr,				/* optr of chunk for array */
        word	elementSize,				/* Size of each element, or zero
                         * for variable-sized */
        word	headerSize);				/* Header size (zero for default) */

This routine is just like ElementArrayCreate() above, except that the element array is created in a pre-existing chunk. The contents of that chunk will be overwritten.

Include: chunkarr.h

Warnings: If the chunk isn't large enough, it will be resized. This will invalidate all pointers to chunks in that block.

ElementArrayCreateAtHandles()

ChunkHandle 	ElementArrayCreateAtHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		ch				/* Create array in this chunk */
        word		elementSize,				/* Size of each element, or zero
                         * for variable-sized */
        word		headerSize);				/* Header size (zero for default) */

This routine is exactly like ElementArrayCreateAt() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

Warnings: If the chunk isn't large enough, it will be resized. This will invalidate all pointers to chunks in that block.

| Up: GEOS SDK TechDocs | Up | Prev: ECCheckProcessHandle() ... | Next: ElementArrayDelete() ...


GEOS C Routines: ElementArrayDelete() ...

| Up: GEOS SDK TechDocs | Up | Prev: ECVMCheckVMBlockHandle() ... | Next: ElementArrayRemoveReference() ...

ElementArrayDelete()

void	ElementArrayDelete(
        optr	arr,					/* optr to element array */
        word	token);					/* index of element to delete */

This routine deletes an element from an element array regardless of its reference count. The routine is passed the element array's optr and the token for the element to delete.

Note that when an element is removed, it is actually resized down to zero size and added to a list of free elements. That way the index numbers of later elements are preserved.

Include: chunkarr.h

Be Sure To: Lock the block on the global heap before calling (unless it is fixed).

See Also: ElementArrayRemoveReference().

ElementArrayDeleteHandles()

void	ElementArrayDeleteHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		ch,				/* Chunk handle of element array */
        word		token);				/* Index of element delete */

This routine is exactly like ElementArrayDelete() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

Be Sure To: Lock the block on the global heap before calling (unless it is fixed).

See Also: ElementArrayRemoveReference().

ElementArrayElementChanged()

void	ElementArrayElementChanged(
        optr	arr,				/* optr to element array */
        word	token,				/* Index number of element */
        dword	callbackData,				/* This is passed along to callback */
        Boolean _pascal (*callback)			/* Returns true if elements identical */
                (void *			elementChanged,
                 void *			elementToCompare,
                 dword			valueForCallback));

This routine checks to see if an element is identical to any other elements in the same element array. This is used after an element has changed to see if it now matches another element. If the element matches another, it will be deleted, and the other element will have its reference count incremented.

The routine is passed an optr to the element array, the token of the element which is being checked, a dword of data (which is passed to the callback routine), and a pointer to a callback comparison routine. The callback routine itself is passed pointers to two elements and the callbackData argument passed to ElementArrayElementChanged() . The callback routine should be declared _pascal. If the two elements are identical, the callback should return true (i.e. non-zero); otherwise, it should return false .

If you pass a null function pointer, ElementArrayElementChanged() will do a bytewise comparison of the elements.

Include: chunkarr.h

ElementArrayElementChangedHandles()

void	ElementArrayElementChangedHandles(
        MemHandle		memHandle,				/* Handle of LMem heap's block */
        ChunkHandle		chunkHandle,				/* Chunk handle of element array */
        word		token,				/* Index number of element */
        dword		callbackData,					/* This is passed along to
                             * callback */
        Boolean _pascal (*callback)			/* Returns true if elements identical */
                (void *			elementChanged,
                 void *			elementToCompare,
                 dword			valueForCallback));

This routine is exactly like ElementArrayElementChanged() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

ElementArrayGetUsedCount()

word	ElementArrayGetUsedCount(
        optr	arr,			/* optr to element array */
        dword	callbackData,			/* This is passed to callback routine */
        Boolean _pascal (*callback)				/* return true to count this element */
                (void * element, dword cbData));

This routine counts the number of active elements in an element array; that is, elements which have a reference count of one or greater. It can be instructed to count every element, or every element which matches certain criteria. The routine is passed three parameters: the optr of the chunk array, a dword which is passed to the callback routine, and a callback routine which determines whether the element should be counted. The callback routine,which should be declared _pascal, is passed the dword an a pointer to an element. It should return true if the element should be counted; otherwise, it should return false . To count every element, pass a null callback pointer.

Include: chunkarr.h

See Also: ElementArrayTokenToUsedIndex(), ElementArrayUsedIndexToToken().

ElementArrayGetUsedCountHandles()

void	ElementArrayGetUsedCountHandles(
        MemHandle 		mh,			/* Handle of LMem heap's block */
        ChunkHandle		ch,			/* Chunk handle of element array */
        dword	callbackData,			/* This is passed to callback routine */
        Boolean _pascal (*callback)				/* return true to count this element */
                (void * element, dword cbData));

This routine is exactly like ElementArrayGetUsedCount() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

| Up: GEOS SDK TechDocs | Up | Prev: ECVMCheckVMBlockHandle() ... | Next: ElementArrayRemoveReference() ...


GEOS C Routines: ElementArrayRemoveReference() ...

| Up: GEOS SDK TechDocs | Up | Prev: ElementArrayDelete() ... | Next: FatalError() ...

ElementArrayRemoveReference()

void	ElementArrayRemoveReference(
        optr		arr,				/* optr of element array */
        word		token,				/* Index of element to
                         * unreference */
        dword		callbackData,				/* Passed to callback routine */
        void _pascal (*callback)		(void *element, dword valueForCallback));
/* Routine is called if element is actually removed */

This routine decrements the reference count of the specified element. If the reference count drops to zero, the element will be removed. If an element is to be removed, ElementArrayRemoveReference() calls the callback routine on that element. The callback routine should perform any cleanup necessary; it is passed a pointer to the element and the callbackData argument. If you pass a null function pointer, no callback routine will be called.

Note that when an element is removed, it is actually resized down to zero size and added to a list of free elements. That way the index numbers of later elements are preserved.

Be Sure To: Lock the block on the global heap before calling (unless it is fixed).

See Also: ElementArrayDelete().

Include: chunkarr.h

ElementArrayRemoveReferenceHandles()

void	ElementArrayRemoveReferenceHandles(
        MemHandle		mh,			/* Handle of LMem heap */
        ChunkHandle		ch,			/* Chunk handle of element array */
        word		token,			/* Index of element to unreference */
        dword		callbackData,				/* Passed to callback routine */
        void _pascal (*callback)		(void *element, dword valueForCallback));
/* Routine is called if element is actually removed */

This routine is exactly like ElementArrayRemoveReference() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

ElementArrayTokenToUsedIndex()

word	ElementArrayTokenToUsedIndex(
        optr	arr,	/* Handle of element array */
        word	token,	/* Index of element to unreference */
        dword	callbackData,	/* Data passed to callback routine */
        Boolean 	_pascal (*callback)	/* Return true to count this element */
                (void *element, dword cbData));

This routine is passed the token of an element array. It translates the token into an index from some non-standard indexing scheme. The indexing scheme can either number the elements from zero, counting only those elements in use (i.e. those with a reference count greater than zero); or it can use a more restrictive scheme. If a callback routine is passed, the callback routine will be called for every used element; it should be declared _pascal and return true if the element should be counted. If a null callback pointer is passed, every used element will be counted.

Include: chunkarr.h

ElementArrayTokenToUsedIndexHandles()

word	ElementArrayTokenToUsedIndexHandles(
        MemHandle 		mh,		/* Handle of LMem heap */
        ChunkHandle 		ch,		/* Chunk handle of element array */
        word		token,		/* Index of element to unreference */
        dword		callbackData, /* Data passed to the
                         * callback routine */
        Boolean 	_pascal (*callback)	/* Return true to count this element */
                (void *element, dword cbData));

This routine is exactly like ElementArrayTokenToUsedIndex() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

ElementArrayUsedIndexToToken()

word	ElementArrayUsedIndexToToken(
        optr	arr,		/* optr to element array */
        word	index,		/* Find token of element with this index */
        dword	callbackData,		/* This is passed to the callback routine */
        Boolean _pascal (*callback)			/* Return true to count this element */
                (void *element, dword cbData));

This routine takes an index into an element array from some non-standard indexing scheme. The routine finds the element specified and returns the element's token. The indexing scheme can either number the elements from zero, counting only those elements in use (i.e. those with a reference count greater than zero); or it can use a more restrictive scheme. If a callback routine is passed, the callback routine will be called for every used element; it should should be declared _pascal return true if the element should be counted. If a null callback pointer is passed, every used element will be counted.

If no matching element is found, ElementArrayUsedIndexToToken() returns CA_NULL_ELEMENT.

Include: chunkarr.h

ElementArrayUsedIndexToTokenHandles()

word	ElementArrayUsedIndexToTokenHandles(
        MemHandle 		mh,		/* Handle of LMem heap's block */
        ChunkHandle 		ch,		/* Handle of element array */
        word		index,		/* Find token of element with this index */
        dword		callbackData, /* Data passed to the
                               * callback routine */
        Boolean 	_pascal (*callback)	/* Return true to count this element */
                (void *element, dword cbData));

This routine is exactly like ElementArrayUsedIndexToToken() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

| Up: GEOS SDK TechDocs | Up | Prev: ElementArrayDelete() ... | Next: FatalError() ...


GEOS C Routines: FatalError() ...

| Up: GEOS SDK TechDocs | Up | Prev: ElementArrayRemoveReference() ... | Next: FileCreate() ...

FatalError()

void	FatalError(
        word errorCode);

This routine causes a fatal error, leaving errorCode for the debugger.

FileClose()

word 	FileClose( /* returns error */
        FileHandle		fh,					/* File to close */
        Boolean		noErrorFlag);					/* Set if app. can't handle
                                 * errors */

This routine closes an open byte file. If the routine succeeds, it returns zero. If the routine fails and noErrorFlag is false (i.e., zero), FileClose() returns a member of the FileError enumerated type. If the routine fails and noErrorFlag is true (i.e., non-zero), the routine will fatal-error.

Warnings: The noErrorFlag parameter should be true only during debugging.

Include: file.h

FileCommit()

word	FileCommit( /* returns error */
        FileHandle		fh,
        Boolean		noErrorFlag);				/* set if can't handle errors */

FileCommit() forces the file system to write any cached information about a file to the disk immediately. If it is successful, it returns zero. If it fails, it returns an error code. If the routine fails and noErrorFlag is true (i.e. non-zero), the routine will fatal-error.

Warnings: The noErrorFlag parameter should be true only during debugging.

Include: file.h

FileConstructFullPath()

DiskHandle 	FileConstructFullPath(
        char		* * buffer,				/* Path string is written here */
        word		bufSize,			/* Length of buffer (in bytes) */
        DiskHandle		disk,			/* Disk or standard path; null for
                         * current path */
        const char		* tail,			/* Path relative to handle */
        Boolean		addDriveLetter);					/* Should path begin with drive
                                 * name? */

This routine translates a GEOS directory specification into a complete path string. It writes the string into the passed buffer. The directory is specified by two arguments: The first, disk , is the handle of a disk; this may also be a standard path constant. (If a null handle is passed, the current working directory is used.) The second, tail , is a pointer to the character string representing the tail end of the path. FileConstructFullPath() appends this relative path to the location indicated by the disk handle. It then constructs a full path string, beginning with that disk's root directory, and writes it to the buffer passed. If addDriveName is true (i.e. non-zero), the path string will begin with the drive's name and a colon. The pointer pointed to by buffer will be updated to point to the end of the constructed string.

Examples: The following call to FileConstructFullPath() might yield these results:

Sample call to FileConstructFullPath()

/* Here we find out the full path of a subdirectory of the DOCUMENT directory */
    DiskHandle		documentDisk;
    char		pathBuffer[256];			/* long enough for most paths */
    char		*pB = &pathBuffer;
    documentDisk = FileConstructFullPath(&pB,						/* pointer to pointer */
                    256, 		/* Length of buffer */
                    SP_DOCUMENT,		/* This can be a disk or
                             * standard path */
                    "MEMOS\\JANUARY", /* In C strings, the
                             * backslash must be
                             * doubled */
                    TRUE);		/* Prepend drive name */
/* If the standard paths are set up in the default configuration, "documentDisk"
 * would be the handle of the main hard drive, and pathBuffer would contain a
 * string like "C:\GEOWORKS\DOCUMENT\MEMOS\JANUARY" */

See Also: FileParseStandardPath().

Include: file.h

FileCopy()

word	FileCopy( /* returns error */
        const char		* source,				/* Source path and file name */
        const char		* dest,				/* Destination path and file name */
        DiskHandle		sourceDisk,				/* These handles may be Standard */
        DiskHandle		destDisk);				/* Path constants, or null to indi-
                         * cate current working directory */

This routine makes a copy of a file. The source and destination are specified with path strings. Each string specifies a path relative to the location specified by the corresponding disk handle. If the handle is a disk handle, the path is relative to that disk's root. If the disk handle is a standard path constant, the path string is relative to that standard path. If the disk handle is null, the path is relative to the current working directory.

If FileCopy() is successful, it returns zero. Otherwise, it returns one of the following error codes:

ERROR_FILE_NOT_FOUND
No such source file exists in the specified directory.
ERROR_PATH_NOT_FOUND
An invalid source or destination path string was passed.
ERROR_ACCESS_DENIED
You do not have permission to delete the existing copy of the destination file, or the destination disk or directory is not writable.
ERROR_FILE_IN_USE
Some geode has the existing destination file open.
ERROR_SHORT_READ_WRITE
There was not enough room on the destination disk.

See Also: FileMove().

Include: file.h

| Up: GEOS SDK TechDocs | Up | Prev: ElementArrayRemoveReference() ... | Next: FileCreate() ...


GEOS C Routines: FileCreate() ...

| Up: GEOS SDK TechDocs | Up | Prev: FatalError() ... | Next: FileDeleteDir() ...

FileCreate()

FileHandle 	FileCreate( /* sets thread's error value */
        const char		* name,				/* relative to working directory */
        FileCreateFlags		flags,				/* see below */
        FileAttrs		attributes);				/* FileAttrs of new file */

This routine creates a byte file. The file may be a DOS file or a GEOS byte file. If the file is successfully opened, FileCreate() will return the file's handle; otherwise, it will return a null handle and set the thread's error value.

The second parameter is a word-length FileCreateFlags record. The third parameter, attributes , describes the FileAttrs record to be set for the new file.

If successful, FileCreate() returns the file's handle. If it is unsuccessful, it returns a null handle and sets the thread's error value (accessible via ThreadGetError() ). The following error values are commonly returned:

ERROR_PATH_NOT_FOUND
A relative or absolute path was passed, and the path included a directory which did not exist.
ERROR_TOO_MANY_OPEN_FILES
There is a limit to how many files may be open at once. If this limit is reached, FileCreate() will fail until a file is closed.
ERROR_ACCESS_DENIED
Either the caller requested access which could not be granted (e.g. it requested write access when another geode had already opened the file with FILE_DENY_W), or the caller tried to deny access when that access had already been granted to another geode (e.g. it tried to open the file with FILE_DENY_W when another geode already had it open for write-access).
ERROR_WRITE_PROTECTED
The caller requested write or read-write access to a file in a write-protected volume.
ERROR_FILE_EXISTS
Returned if FileCreate() was called with FILE_CREATE_ONLY and a file with the specified name already exists.
ERROR_FILE_FORMAT_MISMATCH
Returned if FileCreate() was called with FILE_CREATE_TRUNCATE or FILE_CREATE_NO_TRUNCATE and a file exists in a different format than desired; i.e. you passed FCF_NATIVE and the file already exists in the GEOS format, or vice versa.

Examples: An example of usage is shown below.

Example of FileCreate() usage

/* Here we create a DOS file in the current working directory. If the file already
 * exists, we open the existing file and truncate it.
 */
    FileHandle		newFile;
    newFile = 		FileCreate("NEWFILE.TXT",
                    ( (FILE_CREATE_TRUNCATE | FCF_NATIVE)
                     | (FILE_ACCESS_RW | FILE_DENY_RW)),
                    0); /* set no attribute bits */

See Also: FileCreateTempFile(), FileOpen().

Include: file.h

FileCreateDir()

word	FileCreateDir( /* Returns error & sets thread's error value */
        const char * name);				/* Relative path of new directory */

This routine creates a new directory. The parameter is a path string; the path is relative to the current directory. The last element of the path string must be the directory to create.

If FileCreateDir() is successful, it returns zero and clears the thread's error value. Otherwise, it returns an error code and sets the thread's error value (accessible via ThreadGetError() ). The following errors are returned:

ERROR_PATH_TOO_LONG
The path string was longer than is permitted by the file system for that device.
ERROR_FILE_EXISTS
A file or directory with the specified name already exists at the specified location.
ERROR_INVALID_NAME
The name passed was inappropriate for directories on that device.
ERROR_DISK_STALE
The drive that disk was on has been removed.
ERROR_DISK_UNAVAILABLE
The validation of the disk in that drive was aborted by the user.
ERROR_PATH_NOT_FOUND
The path string was in some way invalid; for example, it might have instructed FileCreateDir() to create the directory within a directory which does not exist.
ERROR_ACCESS_DENIED
The thread is not able to create directories in the specified location, or a directory with the specified name already exists.
ERROR_WRITE_PROTECTED
The volume is write-protected.

See Also: FileDeleteDir().

Include: file.h

FileCreateTempFile()

FileHandle FileCreateTempFile( /* Sets thread's error value */
        char		* dir,		/* directory, relative to working dir.;
                     * file name replaces 14 trailing null
                     * characters upon return */
        FileCreateFlags		flags,
        FileAttrs		attributes);

This routine creates and opens a temporary file in the directory specified. The routine automatically selects a name for the temporary file. No creation flags are needed, since the file will definitely be created anew and will be used only by this geode. The directory string must end with fourteen null bytes (enough to be replaced by the new file's name).

If FileCreateTempFile() is successful, it returns the file's handle as well as the string passed in dir , with the trailing null characters replaced by the file name. If it is unsuccessful, it returns a null handle and sets the thread's error value to a member of the FileError enumerated type. (This error value is accessible via ThreadGetError() .)

Tips and Tricks: Temporary files are usually created in a subdirectory of SP_PRIVATE_DATA.

See Also: FileCreate().

Include: file.h

FileDelete()

word	FileDelete( /* returns error */
        const char * name);			/* path relative to working directory */

This routine deletes a file. If it is successful, it returns zero; otherwise, it returns a FileError . Common errors include:

ERROR_FILE_NOT_FOUND
No such file exists in the specified directory.
ERROR_WRITE_PROTECTED
The volume is write-protected.
ERROR_PATH_NOT_FOUND
An invalid path string was passed.
ERROR_ACCESS_DENIED
You do not have permission to delete that file.
ERROR_FILE_IN_USE
Some geode has that file open.

Include: file.h

| Up: GEOS SDK TechDocs | Up | Prev: FatalError() ... | Next: FileDeleteDir() ...


GEOS C Routines: FileDeleteDir() ...

| Up: GEOS SDK TechDocs | Up | Prev: FileCreate() ... | Next: FileEnum() ...

FileDeleteDir()

word	FileDeleteDir( /* Returns error & sets thread's error value */
        const char * name);			/* Relative path of directory to delete */

This argument deletes an existing directory. The parameter is a string which specifies the directory's position relative to the current working directory. The last element of the path string must be the name of the directory to delete.

If FileDeleteDir() is successful, it returns zero and clears the thread's error value. Otherwise, it returns an error code and sets the thread's error value (accessible via ThreadGetError() ). The following errors are returned:

ERROR_PATH_NOT_FOUND
The directory specified could not be found or does not exist.
ERROR_IS_CURRENT_DIRECTORY
This directory is some thread's current directory, or else it is on some thread's directory stack.
ERROR_ACCESS_DENIED
The thread does not have permission to delete the directory.
ERROR_WRITE_PROTECTED
The volume is write-protected.
ERROR_DIRECTORY_NOT_EMPTY
The directory specified is not empty. A directory must be empty before it can be deleted.

See Also: FileCreateDir().

Include: file.h

FileDuplicateHandle()

FileHandle FileDuplicateHandle( /* Sets thread's error value */
        FileHandle fh);

This routine duplicates the handle of an open file and returns the duplicate handle. The duplicate handle has the same read/write position as the original. Both handles will have to be closed for the file to be closed. If there is an error, FileDuplicateHandle() returns a null handle and sets the thread's error value (accessible via ThreadGetError() ).

Include: file.h

| Up: GEOS SDK TechDocs | Up | Prev: FileCreate() ... | Next: FileEnum() ...


GEOS C Routines: FileEnum() ...

| Up: GEOS SDK TechDocs | Up | Prev: FileDeleteDir() ... | Next: FileEnumLocateAttr() ...

FileEnum()

word	FileEnum( /* returns number of files returned */
        FileEnumParams		* params,			/* described below */
        MemHandle		* bufCreated,			/* FileEnum will allocate a return-
                         * buffer block & write its handle
                         * here */
        word		* numNoFit);				/* Number of files not handled is
                         * written here */

This routine is used to examine all the files in a directory. The routine can filter the files by whether they have certain extended attributes. It creates a buffer and writes information about the files in this buffer. This routine can be called in many different ways; full details are available.

Structures: FileEnum() uses several structures and enumerated types. They are shown below; the detailed description of the structures follows.

            /* Types, values, and structures passed
 * to the FileEnum() routine: */
        typedef enum /* word */ {
FESRT_COUNT_ONLY,
FESRT_DOS_INFO,
FESRT_NAME,
FESRT_NAME_AND_ATTR
        } FileEnumStandardReturnType;
        typedef enum /* word */ {
FESC_WILDCARD
        } FileEnumStandardCallback;
            /* Types, values, and structures returned
 * by the FileEnum() routine: */
        typedef struct {
FileAttrs 			DFIS_attributes;
FileDateAndTime 			DFIS_modTimeDate;
dword 			DFIS_fileSize;
FileLongName 			DFIS_name;
DirPathInfo 			DFIS_pathInfo;
        } FEDosInfo;
        typedef struct _FileEnumCallbackData {
FileExtAttrDesc 			FECD_attrs[1];
        } FileEnumCallbackData;
        typedef struct _FileEnumParams {
FileEnumSearchFlags 				FEP_searchFlags;
FileExtAttrDesc *				FEP_returnAttrs;
word 				FEP_returnSize;
FileExtAttrDesc *				FEP_matchAttrs;
word 				FEP_bufSize;
word 				FEP_skipCount;
word _pascal (*FEP_callback) 						(struct _FileEnumParams *params,
                         FileEnumCallbackData *fecd,
                         word frame);
FileExtAttrDesc *				FEP_callbackAttrs;
dword 			FEP_cbData1;
dword 			FEP_cbData2;
word 			FEP_headerSize;
        } FileEnumParams;

Most of the information passed to FileEnum() is contained in a FileEnumParams structure. The fields of the structure are as follows:

FEP_searchFlags
This is a byte-length flag field. The flags are of type FileEnumSearchFlags (described below). These flags specify which files at the current location will be examined by FileEnum() . They also specify such things as whether a callback routine should be used.
FEP_returnAttrs
This is a pointer to an array of FileExtAttrDesc structures. The last structure should have its FEA_attr field set to FEA_END_OF_LIST. The array specifies what information will be returned by FileEnum() . The FileExtAttrDesc structure is used in a slightly different way than usual. Every file will have an entry in the return buffer; this entry will contain all the extended attribute information requested. Each FileExtAttrDesc structure will specify where in that entry its information should be written. The FEAD_value field should contain only an offset value; the extended attribute will be written at that offset into the entry. (You can specify an offset by casting an integer value to type void * .) The FEAD_size value specifies how long the return value can be. You can also request certain return values by setting FEP_returnAttrs to equal a member of the FileEnumStandardReturnType (again, by casting the FileEnumStandardReturnType value to type void * ). The FileEnumStandardReturnType enumerated type is described later in this section.
FEP_returnSize
This is the size of each entry in the returned buffer. If a standard return type or an array of FileExtAttrDesc structures was passed, each entry in the returned buffer will contain all the extended attribute information requested for that file.
FEP_matchAttrs
This is a pointer to an array of FileExtAttrDesc structures. The last structure should have its FEA_attr field set to FEA_END_OF_LIST. FileEnum() will automatically filter out and ignore all files whose attributes do not match the ones specified by this array. For attributes that are word-sized records, FEAD_value.offset holds the bits that must be set, and FEAD_value.segment holds the bits that must be clear. For byte-sized flags, FEAD_value.offset.low contains the flags that must be set, and FEAD_value.offset.high contains flags that must be clear. Byte- and word-sized non-flag values are stored in FEAD_value.offset . For all other values, FEAD_value holds a pointer to the exact value to match, and FEAD_size specifies the length of that value (in bytes). If you do not want to filter out any files in the working directory, or if you will use the callback routine to filter the files, pass a null pointer in this field.
FEP_bufS
ize
 This specifies the maximum number of entries to be returned in the buffer. If you do not want to set a limit, pass the constant FE_BUFSIZE_UNLIMITED. The buffer will be grown as necessary.
FEP_skipCount
This contains the number of matching files to be ignored before the first one is processed. It is often used in conjunction with FEP_bufSize to examine many files a few at a time. For example, if you only wanted to examine ten files at a time, you would set FEP_bufSize to ten and FEP_skipCount to zero. FileEnum() would return the data for the first ten files which match the search criteria. After processing the returned data, if there were any files left over, you could call FileEnum() again, this time with FEP_skipCount set to ten; FileEnum() would handle the next ten matching files and return the data about them. In this way you could walk through all the matching files in the directory. Note that if the FileEnumSearchFlags bit FESF_REAL_SKIP is set (in FEP _searchFlags ), the first files in the directory will be skipped before they are tested to see if they match. This is faster, since the match condition won't have to be checked for the first files in the directory.
FEP_callback
This holds a pointer to a Boolean callback routine. The callback routine can check to see if the file matches some other arbitrary criteria. The callback routine is called for any files which match all the above criteria. It should be declared _pascal. It is passed three arguments: a pointer to the FileEnumParams structure, a pointer to the current stack frame (which is used by some assembly callback routines), and a pointer to an array of FileExtAttrDesc structures. These structures are all the attributes required either for return, matching, or callback (see FEP _callbackAttrs below), with the information for the current file filled in; you can search through them directly for the information you want, or you can call FileEnumLocateAttr() to search through this array. If the file should be accepted by FileEnum() , the callback should return true ; otherwise it should return false . You can also instruct FileEnum() to use one of the standard callback routines by passing a member of the FileEnumStandardCallback enumerated type. In this case, FEP_callbackAttrs is ignored; FileEnum() will automatically pass the appropriate information to the callback routine. (Note that if the FESF_CALLBACK bit of the FEP_searchFlags field is not set, the FEP_callback field is ignored.)
FEP_callbackAttrs
This is a pointer to an array of FileExtAttrDesc structures. The last structure should have its FEA_attr field set to FEA_END_OF_LIST. The array will be filled in with the appropriate information for each file before the callback routine is called. Note that if the FESF_CALLBACK bit of the FEP_searchFlags is not set, the FEP_callbackAttrs is ignored. If you do not need any attributes passed to the callback routine, set this field to be a null pointer.
FEP_cbData1 , FEP_cbData2
These are dword-length fields. Their contents are ignored by FileEnum() ; they are used to pass information to the callback routine. If you do not call a standard callback routine, you may use these fields any way you wish.
FEP_headerSize
If the flag FESF_LEAVE_HEADER is set, FileEnum() will leave an empty header space at the beginning of the return buffer. The size of the header is specified by this field. If FESF_LEAVE_HEADER is clear, this field is ignored.

The first field of the FileEnumParams structure, FEP_searchFlags , is a word-length record containing FileEnumSearchFlags . The following flags are available:

FESF_DIRS
Directories should be examined by FileEnum() .
FESF_NON_GEOS
Non-GEOS files should be examined by FileEnum() .
FESF_GEOS_EXECS
GEOS executable files should be examined by FileEnum() .
FESF_GEOS_NON_EXECS
GEOS non-executable files (e.g., VM files) should be examined by FileEnum() .
FESF_REAL_SKIP
If a skip count of n is specified, the first n files will be skipped regardless of whether they matched the attributes passed. In this case, FileEnum() will return the number of files passed through in order to get enough files to fill the buffer; the return value can thus be the real-skip count for the next pass.
FESF_CALLBACK
FileEnum() should call a callback routine to determine whether a file should be accepted.
FESF_LOCK_CB_DATA
This flag indicates that the FileEnumParams fields FEP_callback1 and FEP_callback2 are far pointers to movable memory that must be locked before FileEnum() is called.
FESF_LEAVE_HEADER
If set, FileEnum() should leave an empty header space at the start of the return buffer. The size of this buffer is specified by the FEP_headerSize field.

The FileEnumStandardReturnType enumerated type has the following values; they are used in conjunction with the FEP_returnAttrs field of the FileEnumParams structure.

FESRT_COUNT_ONLY
FileEnum() will not allocate any memory and will not return data about files; instead, it will simply return the number of files which match the specified criteria.
FESRT_DOS_INFO
FileEnum() will return an array of FEDosInfo structures. These structures contain basic information about the file: its virtual name, size, modification date, DOS attributes, and path information (as a DirPathInfo record).
FESRT_NAME
FileEnum() will return an array of FileLongName strings, each one of which is FILE_LONGNAME_BUFFER_SIZE characters long; every one of these will contain a file's virtual name followed by a null terminator.
FESRT_NAME_AND_ATTR
FileEnum() will return an array of FENameAndAttr structures, each one of which contains a file's DOS attributes and virtual name.

The FEDosInfo structure includes a word-sized record ( DFIS_pathInfo ) which describes the file's position relative to the standard paths. It contains the following fields:

DPI_EXISTS_LOCALLY
This bit is set if the file exists in a directory under the primary tree.
DPI_ENTRY_NUMBER_IN_PATH
This is the mask for a seven-bit field whose offset is DPI_ENTRY_NUMBER_IN_PATH_OFFSET.
DPI_STD_PATH
This is the mask for an eight-bit field whose offset is DPI_STD_PATH_OFFSET. If the file is in a standard path, this field will contain a StandardPath constant for a standard path containing the file. This need not be the "closest" standard path; for example, if the file is in the "World" directory, this constant might nevertheless be SP_TOP.

See Also: FileEnumLocateAttr(), FileEnumWildcard().

Include: fileEnum.h

| Up: GEOS SDK TechDocs | Up | Prev: FileDeleteDir() ... | Next: FileEnumLocateAttr() ...


GEOS C Routines: FileEnumLocateAttr() ...

| Up: GEOS SDK TechDocs | Up | Prev: FileEnum() ... | Next: FileGetDiskHandle() ...

FileEnumLocateAttr()

void *	FileEnumLocateAttr( /* returns NULL if attr not found */
        FileEnumCallbackData*			fecd,		/* Passed to callback routine */
        FileExtendedAttribute			attr,		/* Search for this attribute */
        const char *			* name);			/* Attribute name (if second
                         * argument is FEA_CUSTOM) */

FileEnum() can be instructed to call a callback routine to decide which files to filter out. This callback routine is passed an array of FileExtAttrDesc structures. To find a particular extended attribute in this array, call FileEnumLocateAttr() . This routine will find the address of the value of the attribute desired, and return that address. If the attribute is not in the array, FileEnumLocateAttr() will return a null pointer.

Include: fileEnum.h

FileEnumWildcard()

Boolean	FileEnumWildcard(
        FileEnumCallbackData			* fecd,			/* Passed to callback routine */
        word			frame);			/* Inherited stack frame */

This routine is a utility used by FileEnum() and is rarely used by applications. It checks to see if the virtual name of the current file (the file currently being evaluated by FileEnum() ) matches the pattern in the FEP_cbData1 field of the FileEnumParams structure.

The fecd parameter is a pointer to the callback data of the FileEnum() routine. The frame parameter is a pointer to the FileEnum() stack frame: The first dword is the FEP_cbData1 field, and the second is the FEP_cbData2 field.

This routine returns true (non-zero) if the file name and pattern match. Otherwise, it returns false .

Include: fileEnum.h

FileFromTransferBlockID()

VMFileHandle	 FileFromTransferBlockID(id);
        TransferBlockID id;

This macro extracts a VMFileHandle from a value of type TransferBlockID .

FileGetAttributes()

FileAttrs 	FileGetAttributes( /* Sets thread's error value */
        const char * path);			/* file's path relative to current
             * working directory */

This routine returns the standard FileAttrs attributes for a file. The file may be a GEOS file or a plain DOS file. Note that you can also get a file's attributes by getting the file's FEA_FILE_ATTR extended attribute. If an error occurs, this routine sets the thread's error (accessible via ThreadGetError() ).

See Also: FileAttrs, FileSetAttributes().

Include: file.h

FileGetCurrentPath()

DiskHandle FileGetCurrentPath(
        char *	buffer,			/* Path string is written here */
        word	bufferSize);			/* Size of buffer in bytes */

This routine writes the current path string (without drive specifier) to the buffer provided. If the buffer is too small, it truncates the path to fit. It returns the handle of the disk containing the current path. If the current path was declared relative to a standard path, the standard path constant will be returned.

Include: file.h

FileGetDateAndTime()

FileDateAndTime 	FileGetDateAndTime( /* sets thread's error value */
        FileHandle fh);

This routine finds out the time a file was last modified. This routine can be called on GEOS or non-GEOS files. Note that you can also find out the modification time of a file by checking the extended attribute FEA_MODIFICATION. If unsuccessful, it sets the thread's error value (accessible via ThreadGetError() ).

See Also: FileDateAndTime, FileSetDateAndTime().

Include: file.h

| Up: GEOS SDK TechDocs | Up | Prev: FileEnum() ... | Next: FileGetDiskHandle() ...


GEOS C Routines: FileGetDiskHandle() ...

| Up: GEOS SDK TechDocs | Up | Prev: FileEnumLocateAttr() ... | Next: FileOpen() ...

FileGetDiskHandle()

DiskHandle FileGetDiskHandle( /* sets thread's error value */
        FileHandle fh);

This routine returns the handle of the disk containing an open file. If unsuccessful, it sets the thread's error value (accessible via ThreadGetError() ).

Include: file.h

FileGetHandleExtAttributes()

word	FileGetHandleExtAttributes(
        FileHandle			fh,				/* open file's handle */
        FileExtendedAttribute			attr,				/* attribute to get */
        void			* buffer,				/* attribute is written here */
        word			bufSize);				/* length of buffer in bytes */

This routine gets one or more extended attributes of an open file. (To get the attributes of a file without opening it, call FileGetPathExtAttributes() .) If a single attribute is requested, the attribute will be written in the buffer passed. If several attributes are requested, attr should be set to FEA_MULTIPLE, and buffer should point to an array of FileExtAttrDesc structures. In this case, bufSize should be the number of structures in the buffer, not the length of the buffer.

If FileGetHandleExtAttributes() is successful, it returns zero. Otherwise, it returns one of the following error codes:

ERROR_ATTR_NOT_SUPPORTED
The file system does not recognize the attribute constant passed.
ERROR_ATTR_SIZE_MISMATCH
The buffer passed was too small for the attribute requested.
ERROR_ATTR_NOT_FOUND
The file does not have a value set for that attribute.
ERROR_ACCESS_DENIED
You do not have read-access to the file.

Tips and Tricks: Note that the only way to recover a custom attribute is by passing FEA_MULTIPLE, and using a FileExtAttrDesc to describe the attribute.

See Also: FileGetPathExtAttributes().

Include: file.h

FileGetPathExtAttributes()

word	FileGetPathExtAttributes(
        const char			* path,				/* path relative to current
                             * working directory */
        FileExtendedAttribute			attr,				/* attribute to get */
        void			* buffer,				/* attribute is written here */
        word			bufSize);				/* length of buffer in bytes */

This routine gets one or more extended attributes of a GEOS file. If a single attribute is requested, the attribute will be written in the buffer passed. If several attributes are requested, attr should be set to FEA_MULTIPLE, and buffer should point to an array of FileExtAttrDesc structures. In this case, bufSize should be the number of structures in the buffer, not the length of the buffer.

If FileGetPathExtAttributes() is successful, it returns zero. Otherwise, it returns one of the following error codes:

ERROR_ATTR_NOT_SUPPORTED
The file system does not recognize the attribute constant passed.
ERROR_ATTR_SIZE_MISMATCH
The buffer passed was too small for the attribute requested.
ERROR_ATTR_NOT_FOUND
The file does not have a value set for that attribute.
ERROR_ACCESS_DENIED
You do not have read-access to the file.

Tips and Tricks: Note that the only way to recover a custom attribute is by passing FEA_MULTIPLE, and using a FileExtAttrDesc to describe the attribute.

See Also: FileGetHandleExtAttributes().

Include: file.h

FileLockRecord()

word	FileLockRecord( /* returns error */
        FileHandle		fh,
        dword		filePos,			/* lock starting at this position... */
        dword		regLength);				/* lock this many bytes */

This routine puts a lock on a part of a byte file. It first checks to make sure that there are no locks that overlap the region specified; if there are, it will fail and return ERROR_ALREADY_LOCKED. If there are no locks, it will place a lock on the region specified and return zero.

Warnings: Locking a region only prevents threads from locking part of the same region; it does not prevent them from reading from or writing to the region. If applications use this mechanism, they have to make sure to call FileLockRecord before trying to access a part of a file.

See Also: FileUnlockRecord(), HandleP().

FileMove()

word	FileMove( /* Returns error */
        const char		* source,				/* source path and file name */
        const char		* dest,				/* destination path and file name */
        DiskHandle		sourceDisk,				/* These handles may be Standard */
        DiskHandle		destDisk);				/* Path constants, or null to indi-
                         * cate current working directory */

This routine moves a file from one location to another. The source and destination are specified with path strings. Each string specifies a path relative to the location specified by the corresponding disk handle. If the handle is a disk handle, the path is relative to that disk's root. If the disk handle is a standard path constant, the path string is relative to that standard path. If the disk handle is null, the path is relative to the current working directory.

If FileMove() is successful, it returns zero. Otherwise, it returns one of the following error codes and sets the thread's error value.

ERROR_FILE_NOT_FOUND
No such source file exists in the specified directory.
ERROR_PATH_NOT_FOUND
An invalid source or destination path string was passed.
ERROR_ACCESS_DENIED
You do not have permission to delete the source file, or there is already a file with the same name as the destination file (and you do not have permission to delete it), or the destination disk or directory is not writable.
ERROR_FILE_IN_USE
Either the source file is in use, or there is already a file with the same name as the destination file, and it is in use.
ERROR_SHORT_READ_WRITE
There was not enough room on the destination disk.

See Also: FileCopy().

Include: file.h

| Up: GEOS SDK TechDocs | Up | Prev: FileEnumLocateAttr() ... | Next: FileOpen() ...


GEOS C Routines: FileOpen() ...

| Up: GEOS SDK TechDocs | Up | Prev: FileGetDiskHandle() ... | Next: FileRename() ...

FileOpen()

FileHandle FileOpen( /* sets thread's error value */
        const char		* name,			/* relative to working dir */
        FileAccessFlags		flags);			/* Permissions/exclusions */

This routine opens a file for bytewise access. The file may be a DOS file or a GEOS byte file. If the file is successfully opened, FileOpen() will return the file's handle; otherwise, it will return a null handle and set the thread's error value (accessible via ThreadGetError() ). Errors typically set by this routine are listed below:

ERROR_FILE_NOT_FOUND
No file with the specified name could be found in the appropriate directory.
ERROR_PATH_NOT_FOUND
A relative or absolute path had been passed, and the path included a directory which did not exist.
ERROR_TOO_MANY_OPEN_FILES
There is a limit to how many files may be open at once. If this limit is reached, FileOpen() will fail until a file is closed.
ERROR_ACCESS_DENIED
Either the caller requested access which could not be granted (e.g. it requested write access when another geode had already opened the file with FILE_DENY_W), or the caller tried to deny access when that access had already been granted to another geode (e.g. it tried to open the file with FILE_DENY_W when another geode already had it open for write-access).
ERROR_WRITE_PROTECTED
The caller requested write or read-write access to a file in a write-protected volume.

See Also: FileCreate(), FileAccessFlags.

Include: file.h

FileParseStandardPath()

StandardPath FileParseStandardPath(
        DiskHandle		disk,
        const char		** path);

This routine is passed a full path (relative to the passed disk or a standard path, if the disk handle is null) and finds the standard path which most closely contains that path. It updates the pointer whose address is passed so that it points to the trailing portion of the path string. For example, if you pass the path string "\GEOWORKS\DOCUMENT\MEMOS\APRIL", the pointer would be updated to point to the "\MEMOS\APRIL" portion, and the StandardPath SP_DOCUMENT would be returned. If the path passed does not belong to a standard path, the constant SP_NOT_STANDARD_PATH will be returned, and the pointer will not be changed.

Include: file.h

FilePopDir()

void	FilePopDir();

FilePopDir() pops the top directory off the thread's directory stack and makes it the current working directory.

See Also: FilePushDir().

Include: file.h

FilePos()

dword	FilePos( /* Sets thread's error value */
        FileHandle		fh,
        dword		posOrOffset,
        FilePosMode		mode);

This routine changes the current file position. The position can be specified in three ways, depending on the value of the mode argument:

FILE_POS_START
The file position is set to a specified number of bytes after the start of the file. Passing this mode with an offset of zero will set the file position to the start of the file.
FILE_POS_RELATIVE
The file position is incremented by a specified number of bytes; this number may be negative.
FILE_POS_END
The file position is set to a specified number of bytes after the end of the file; it is usually passed with a negative number of bytes. Passing this mode with an offset of zero will set the file position to the end of the file.

FilePos() returns a 32-bit integer. This integer specifies the absolute file position after the move (relative to the start of the file). On an error, it sets the thread's error value (accessible via ThreadGetError() ).

Tips and Tricks: To find out the current file position without changing it, call FilePos() with mode FILE_POS_RELATIVE and offset zero.

Include: file.h

FilePushDir()

void	FilePushDir();

FilePushDir() pushes the current working directory onto the thread's directory stack. It does not change the current working directory.

See Also: FilePopDir().

Include: file.h

FileRead()

word	FileRead(
        FileHandle		fh,			/* handle of open file */
        void		* buf,			/* copy data to this buffer */
        word		count,			/* Length of buffer (in bytes) */
        Boolean		noErrorFlag);					/* Set if app can't
                             * handle errors */

This routine copies data from a file into memory. It starts copying from the current position in the file. If possible, it will copy enough data to fill the buffer. If FileRead() is successful, it returns the number of bytes copied. If an error occurs, FileRead() returns -1 and sets the thread's error value (usually to ERROR_ACCESS_DENIED). The current file position will be changed to the first byte after the ones which were read.

In C, there is no way to determine whether an ERROR_SHORT_READ_WRITE error occurs. To check whether all of the data was actually copied into memory, you must compare the number of bytes actually read with the number requested to be read. If your read operation requires multiple FileRead() operations, you may should read until zero bytes are returned.

To retrieve the thread error value, use ThreadGetError() .

If the argument noErrorFlag is set to true (i.e. non-zero), FileRead() will fatal-error if an error occurs (including an ERROR_SHORT_READ_WRITE).

Warnings: Pass noErrorFlag true only during debugging.

Include: file.h

| Up: GEOS SDK TechDocs | Up | Prev: FileGetDiskHandle() ... | Next: FileRename() ...


GEOS C Routines: FileRename() ...

| Up: GEOS SDK TechDocs | Up | Prev: FileOpen() ... | Next: FileSetHandleExtAttributes() ...

FileRename()

word	FileRename(
        const char * oldName,				/* Relative to working directory */
        const char * newName);				/* Name only, without path */

This routine changes a file's name. It cannot move a file to a different directory; to do that, call FileMove() . If the routine is successful, it returns zero; otherwise, it returns a FileError . Common errors include

ERROR_FILE_NOT_FOUND
No such file exists in the specified directory.
ERROR_PATH_NOT_FOUND
An invalid path string was passed.
ERROR_ACCESS_DENIED
You do not have permission to delete that file, or it exists on a read-only volume.
ERROR_FILE_IN_USE
Some geode has that file open.
ERROR_INVALID_NAME
The name was not a valid GEOS name; or the file is a non-GEOS file, and the name was not an appropriate native name.

See Also: FileMove().

Include: file.h

FileResolveStandardPath()

DiskHandle FileResolveStandardPath(
        char		** buffer,				/* Write path here; update pointer
                         * to point to end of path */
        word		bufSize,				/* Size of buffer (in bytes) */
        const char *		path,				/* Relative path of file */
        FileResolveStandardPathFlags flags,							/* Flags are described below */
        FileAttrs *attrsPtr);							/* Empty buffer, will be filled with attrs of passed file, if any */

This routine finds a file relative to the current location, then writes the full path to the file, starting at the root of the disk ( not at a standard path). It writes the path to the passed buffer, updating the pointer to point to the null at the end of the path string; it also returns the handle of the disk. If it cannot find the file it returns a null path.

Structures: A record of FileResolveStandardPathFlags is passed to FileResolveStandardPath() . The following flags are available:

FRSPF_ADD_DRIVE_NAME
The path string written to the buffer should begin with the drive name (e.g., "C:\GEOWORKS\DOCUMENT\MEMOS").
FRSPF_RETURN_FIRST_DIR
FileResolveStandardPath() should not check whether the passed path actually exists; instead, it should assume that the path exists in the first directory comprising the standard path, and return accordingly.

Include: file.h

FileSetAttributes()

word	FileSetAttributes( /* returns error value */
        const char		* path,			/* file's path relative to current
                     * working directory */
        FileAttrs		attr);			/* new attributes for the file */

This routine changes the standard DOS attributes of a DOS or GEOS file. Note that you can also change the attributes of a file by setting the extended attribute FEA_FILE_ATTR.

See Also: FileAttrs, FileGetAttributes().

Include: file.h

FileSetCurrentPath()

DiskHandle FileSetCurrentPath(
        DiskHandle		disk,			/* May be a standard path constant */
        const char		* path);			/* path string, null-terminated */

This routine changes the current path. It is passed two parameters: The first is the handle of the disk containing the new current path (this may be a standard path constant). The second is a null-terminated path string. It is specified with normal DOS conventions: directories are separated by backslashes; a period (".") indicates the current directory; and a pair of periods ("..") indicates the parent of the current directory. The string may not contain wildcard characters.

If disk is a disk handle, the path is relative to the root directory of that disk; if disk is a standard path constant, the path is relative to the standard path; if it is null, the path is relative to the current working directory. FileSetCurrentPath() returns the disk handle associated with the new current path; this may be a standard path constant. If FileSetCurrentPath() fails, it returns a null handle.

See Also: FileSetStandardPath().

Include: file.h

FileSetDateAndTime()

word	FileSetDateAndTime( /* returns error */
        FileHandle		fh,					/* handle of open file */
        FileDateAndTime		dateAndTime);					/* new modification time */

This routine changes a file's last-modification time-stamp. This routine can be called on GEOS or non-GEOS files. Note that you can also change the modification time of a file by changing the extended attribute FEA_MODIFICATION. If unsuccessful, this routine returns an error and sets the thread's error value (accessible via ThreadGetError() ).

See Also: FileDateAndTime, FileGetDateAndTime().

Include: file.h

| Up: GEOS SDK TechDocs | Up | Prev: FileOpen() ... | Next: FileSetHandleExtAttributes() ...


GEOS C Routines: FileSetHandleExtAttributes() ...

| Up: GEOS SDK TechDocs | Up | Prev: FileRename() ... | Next: FileTruncate() ...

FileSetHandleExtAttributes()

word	FileGetPathExtAttributes( /* returns error */
        FileHandle			fh,			/* handle of open file */
        FileExtendedAttribute		 	attr,			/* attribute to get */
        const void			* buffer,			/* attribute is read from here */
        word			bufSize);			/* length of buffer in bytes */

This routine sets one or more extended attributes of an open GEOS file. (To set the attributes of a file without opening it, call FileSetPathExtAttributes() .) If a single attribute is specified, the attribute's new value will be read from the buffer passed. If several attributes are to be changed, attr should be set to FEA_MULTIPLE, and buffer should point to an array of FileExtAttrDesc structures. In this case, bufSize should be the number of structures in the buffer, not the length of the buffer.

If FileSetHandleExtAttributes() is successful, it returns zero. Otherwise, it sets the thread's error value (accessible via ThreadGetError() ) and returns one of the following error codes:

ERROR_ATTR_NOT_SUPPORTED
The file system does not recognize the attribute constant passed.
ERROR_ATTR_SIZE_MISMATCH
The buffer passed was the wrong size for the attribute specified.
ERROR_ACCESS_DENIED
The caller does not have write-access to the file.
ERROR_CANNOT_BE_SET
The extended attribute cannot be changed. Such attributes as FEA_SIZE and FEA_NAME cannot be changed with the FileSet...() routines.

Tips and Tricks: Note that the only way to create or change a custom attribute is by passing FEA_MULTIPLE, and using a FileExtAttrDesc to describe the attribute.

See Also: FileSetPathExtAttributes().

Include: file.h

FileSetPathExtAttributes()

word	FileSetPathExtAttributes(
        const char			* path,			/* path relative to current
                         * working directory */
        FileExtendedAttribute			 attr,			/* attribute to get */
        const void			* buffer,			/* attribute is read from here */
        word			bufSize);			/* length of buffer in bytes */

This routine sets one or more extended attributes of a file. If a single attribute is specified, the attribute will be written in the buffer passed. If several attributes are to be changed, attr should be set to FEA_MULTIPLE and buffer should point to an array of FileExtAttrDesc structures. In this case, bufSize should be the number of structures in the buffer, not the length of the buffer.

If FileSetPathExtAttributes() is successful, it returns zero. Otherwise, it sets the thread's error value (accessible via ThreadGetError() ) and returns one of the following error codes:

ERROR_ATTR_NOT_SUPPORTED
The file system does not recognize the attribute constant passed.
ERROR_ATTR_SIZE_MISMATCH
The buffer passed was the wrong size for the attribute specified.
ERROR_ACCESS_DENIED
FileSetPathExtAttributes() returns this if any geode (including the caller) has the file open with "deny-write" exclusive access, or if the file is not writable.
ERROR_CANNOT_BE_SET
The extended attribute cannot be changed. Such attributes as FEA_SIZE and FEA_NAME cannot be changed with the FileSet...ExtAttributes() routines.

Tips and Tricks: Note that the only way to create or change a custom attribute is by passing FEA_MULTIPLE, and using a FileExtAttrDesc to describe the attribute.

See Also: FileSetHandleExtAttributes().

Include: file.h

FileSetStandardPath()

void	FileSetStandardPath(
        StandardPath path);			/* StandardPath to set */

This routine changes the current working directory to one of the system's StandardPath directories. Pass a standard path.

Include: file.h

FileSize()

dword	FileSize(
        FileHandle fh);		/* handle of open file */

This routine returns the size of the open file specified.

Include: file.h

| Up: GEOS SDK TechDocs | Up | Prev: FileRename() ... | Next: FileTruncate() ...


GEOS C Routines: FileTruncate() ...

| Up: GEOS SDK TechDocs | Up | Prev: FileSetHandleExtAttributes() ... | Next: FloatFloatToAscii() ...

FileTruncate()

word	FileTruncate(
        FileHandle		fh,			/* handle of open file */
        dword		offset,			/* offset at which to truncate */
        Boolean		noErrorFlag);

This routine truncates the specified file at the passed offset. The offset parameter can also be thought of as the desired file size. If the high bit of noErrorFlag is one, then no errors will be returned.

Include: file.h

FileUnlockRecord()

word	FileUnlockRecord( /* returns error */
        FileHandle		fh,				/* handle of open file
        dword		filePos,				/* Release lock that starts here */
        dword		regLength);				/* and is this long */

This routine releases a lock on a part of a byte-file. The lock must have been previously placed with FileLockRecord() .

See Also: FileLockRecord(), HandleV().

Include: file.h

FileWrite()

word	FileWrite( /* sets thread's error value */
        FileHandle		fh,				/* handle of open file */
        const void		* buf,				/* Copy from here into file */
        word		count,				/* # of bytes to copy */
        Boolean		noErrorFlag);				/* Set if can't handle errors */

This routine copies a specified number of bytes from a buffer to the file. The bytes are written starting with the current position in the file; any data already at that location will be overwritten. FileWrite() returns the number of bytes written. If FileWrite() could not write all the data (e.g. if the disk ran out of space), it will set the thread's error value (accessible via ThreadGetError() ) to ERROR_SHORT_READ_WRITE and return the number of bytes that were written. If it could not write the data to the file at all (e.g. if you do not have write-access to the file), it will return -1 and set the thread's error value to ERROR_ACCESS_DENIED. In any event, the file position will be changed to the first byte after the ones written.

If the argument noErrorFlag is set to true (i.e. non-zero), FileWrite() will fatal-error if an error occurs.

Warnings: Pass noErrorFlag true only during debugging.

Include: file.h

FloatAsciiToFloat()

Boolean	FloatAsciiToFloat( /* returns FALSE on success */
        word floatAtoFflags,
        word stringLength,
        void *string,
        void *resultLocation);

This routine is used to convert an ASCII string to a floating point number. It takes the following arguments:

floatAtoFflags
This FloatAsciiToFloatFlags field determines what will be done with the generated floating point number. At most one flag may be set. If FAF_PUSH_RESULT is set, then the number will be pushed on the floating-point stack. If the FAF_STORE_NUMBER flag is set, the number will be returned at the resultLocation address.
stringLength
This is the length of the buffer passed in string.
string
This buffer contains the ASCII string to convert. The string should be of the format "[|+|-] dddd.dddd [|E|e] [|+|-] dddd". The string is assumed to be legal. There may be at most a single decimal point. Spaces and thousands separators are ignored.
resultLocation
If you pass the FAF_STORE_NUMBER flag, the buffer pointed to by resultLocation will be filled with the floating point number. This buffer should be at least five words long.
| Up: GEOS SDK TechDocs | Up | Prev: FileSetHandleExtAttributes() ... | Next: FloatFloatToAscii() ...


GEOS C Routines: FloatFloatToAscii() ...

| Up: GEOS SDK TechDocs | Up | Prev: FileTruncate() ... | Next: FoamCancelIfNoFreeSpace() ...

FloatFloatToAscii()

word	FloatFloatToAscii( /* returns # of chars in ASCII string */
        FFA_stackFrame 		*stackFrame,
        char 		*resultString,
        FloatNum 		*number);

This routine converts a floating point number to an ASCII string. It uses a complicated data structure, FFA_stackFrame . To convert floating point numbers to ASCII without filling in this structure, use the FloatFloatToAscii_StdFormat() routine instead. Unless a special flag is passed, this routine will convert the top number of the floating point stack and pop it. FloatFloatToAscii() takes the following arguments:

stackFrame
This is an FFA_stackFrame structure. This structure contains a structure of parameters to FloatFloatToAscii() ; also, the routine will return some information in this structure. The structure is discussed in detail below.
resultString
Pointer to a buffer which will hold the generated ASCII string. This buffer must be either FLOAT_TO_ASCII_NORMAL_BUF_LEN or FLOAT_TO_ASCII_HUGE_BUF_LEN bytes, depending on the type of number (see below).
number
Specifies the format of the floating number. If it is FFAF_DONT_USE_SCIENTIFIC then the buffer resultString must be FLOAT_TO_ASCII_HUGE_BUF_LEN bytes long; otherwise, it must be FLOAT_TO_ASCII_NORMAL_BUF_LEN bytes.

If you pass the FFAF_FROM_ADDR flag, then instead of converting the number at the head of the floating-point stack, the routine will convert this number.

The routine explicitly returns the number of characters in the generated string, not counting the null terminator. It will return zero if an error occurred.

Structures:

typedef union {
    FloatFloatToAsciiData  	 				FFA_float;
        FloatFloatToDateTimeData				FFA_dateTime;
} FFA_stackFrame;

        typedef struct {
            FloatFloatToAsciiParams FFA_params;
            word                    FFA_startNumber;
            word                    FFA_decimalPoint;
            word                    FFA_endNumber;
            word                    FFA_numChars;
            word                    FFA_startExponent;
            word                    FFA_bufSize;
            word                    FFA_saveDI;
            word                    FFA_numSign;
            byte                    FFA_startSigCount;
            byte                    FFA_sigCount;
            byte                    FFA_noMoreSigInfo;
            byte                    FFA_startDecCount;
            byte                    FFA_decCount;
            word                    FFA_decExponent;
            word                    FFA_curExponent;
            byte                    FFA_useCommas;
            byte                    FFA_charsToComma;
            char                    FFA_commaChar;
            char                    FFA_decimalChar;
        } FloatFloatToAsciiData;

If you're passing a FloatFloatToAsciiData structure, you must fill in the FFA_params field. When FloatFloatToAscii() returns, some of the other fields will have been filled in with potentially useful information:

FFA_startNumber
This is an offset into the returned string, pointing to the start of the numeric characters.
FFA_decimalPoint
This is an offset into the returned string, pointing to the decimal point. If no decimal point appears in the string, this field will be zero.
FFA_endNumber
This is an offset into the returned string, pointing to the end of the numeric characters.
FFA_numChars
This is the number of characters in the returned string, or zero if there was an error. This is the same as FloatFloatToAscii()'s return value.
FFA_startExponent
This is an offset into the returned string, pointing to the "E" character. If there is no such character in the string, this field will be zero.
The other FloatFloatToAsciiData fields are internal.
        typedef struct {
            FloatFloatToAsciiFormatFlags 						formatFlags;
            byte    						decimalOffset;
            byte    						totalDigits;
            byte    						decimalLimit;
            char    						preNegative[SIGN_STR_LEN+1];
            char    						postNegative[SIGN_STR_LEN+1];
            char    						prePositive[SIGN_STR_LEN+1];
            char    						postPositive[SIGN_STR_LEN+1];
            char    						header[PAD_STR_LEN+1];
            char    						trailer[PAD_STR_LEN+1];
            byte    						FFTAP_unused;
        } FloatFloatToAsciiParams;

The FloatFloatToAsciiParams structure determines how FloatFloatToAscii() should format its generated string. It contains the following fields:

formatFlags
A FloatFloatToAsciiFormatFlags structure containing flags used to fine-tune the formatting.
decimalOffset
The number of decimal places to offset the decimal point. E.g. an offset of -6 will display numbers in terms of millions.
totalDigits
Maximum number of digits. This is normally MAX_DIGITS_FOR_NORMAL_NUMBERS. If you're using scientific notation, then this is probably MAX_DIGITS_FOR_HUGE_NUMBERS.
decimalLimit
Maximum number of decimal digits; the number will be rounded to meet this limit.
preNegative
The characters which precede a negative number. The string should be null-terminated. Thypical values are "-" and "(".
postNegative
The characters used to terminate a negative number. The string should be null-terminated. Typical values include the empty string and ")".
prePositive
The characters used to precede a postive number. The string should be null-terminated. Typical values include the null string and "+".
postPositive
The character used to terminate a positive number. The string is expected to be null terminated. Typically, this is the empty string.
header
The characters that precede the number. This string should be null terminated. This string might appear before or after the sign character, depending on whether the FFAF_SIGN_CHAR_TO_FOLLOW_HEADER flag is set. Typically, this string is empty; if it isn't, then set the FFAF_HEADER_PRESENT flag.
trailer
The characters that follow the number. This string should be null terminated. Whether this string follows or precedes the sign is determined by the FFAF_SIGN_CHAR_TO_PRECEDE_TRAILER flag. Typically, this string is empty; if it isn't, then set the FFAF_TRAILER_PRESENT flag.
        typedef WordFlags FloatFloatToAsciiFormatFlags;
        #define FFAF_FLOAT_RESERVED 									0x8000
        #define FFAF_FROM_ADDR 									0x4000
        #define FFAF_DONT_USE_SCIENTIFIC 									0x0200
        #define FFAF_SCIENTIFIC 									0x0100
        #define FFAF_PERCENT 									0x0080
        #define FFAF_USE_COMMAS 									0x0040
        #define FFAF_NO_TRAIL_ZEROS 									0x0020
        #define FFAF_NO_LEAD_ZERO 									0x0010
        #define FFAF_HEADER_PRESENT 									0x0008
        #define FFAF_TRAILER_PRESENT 									0x0004
        #define FFAF_SIGN_CHAR_TO_FOLLOW_HEADER 									0x0002
        #define FFAF_SIGN_CHAR_TO_PRECEDE_TRAILER									0x0001

The FloatFloatToAsciiFormatFlags field contains the following flags:

FFAF_FLOAT_RESERVED
This flag must be zero.
FFAF_FROM_ADDR
Normally, FloatFloatToAscii() will convert the number at the top of the floating point stack. By passing this flag, you ask that the routine instead convert the number pointed to by its number argument.
FFAF_DONT_USE_SCIENTIFIC
If set, this flag discourages the use of scientific notation. Zero padding will be used if possible.
FFAF_SCIENTIFIC
Set this flag to enforce scientific notation.
FFAF_PERCENT
Set this flag to display number as a percentage.
FFAF_USE_COMMAS
Set this flag to use comma separators.
FFAF_NO_TRAIL_ZEROS
Set this flag to pad the number with trailing zeros.
FFAF_NO_LEAD_ZERO
Set this flag to make a leading zero appear before fractional values. E.g. ".53" would appear as "0.53" instead.
FFAF_HEADER_PRESENT
If there is no header string, leave this flag clear--this results in a speed optimization.
FFAF_TRAILER_PRESENT
If there is no trailer string, leave this flag clear--this results in a speed optimization.
FFAF_SIGN_CHAR_TO_FOLLOW_HEADER
This flag determines the relative positions of the sign and header strings.
FFAF_SIGN_CHAR_TO_PRECEDE_TRAILER
This flag determines the relative positions of the sign and trailer strings.
        typedef struct {
            FloatFloatToDateTimeParams      FFA_dateTimeParams;
    } FloatFloatToDateTimeData;

        typedef struct {
                FloatFloatToDateTimeFlags 							FFA_dateTimeFlags;
                word    							FFA_year;
                byte 							FFA_month;
                byte 							FFA_day;
                byte 							FFA_weekday;
                byte 							FFA_hours;
                byte 							FFA_minutes;
                byte 							FFA_seconds;
        } FloatFloatToDateTimeParams;

The FloatFloatToDateTime structure is used to pass and return information for the formatting of date and time strings.

FFA_dateTimeFlags
This FloatFloatToDateTimeFlags field fine-tunes the way in which to format the date string.
FFA_year
FFA_month
FFA_day
FFA_weekday
FFA_hours
FFA_minutes
FFA_seconds
These fields will be used to return the various parts of the date/time.
        typedef WordFlags FloatFloatToDateTimeFlags;
        #define FFDT_DATE_TIME_OP 				0x8000
        #define FFDT_FROM_ADDR 				0x4000
        #define FFDT_FORMAT 				0x3fff

The FloatFloatToDateTimeFlags field determines how the date/time should be formated.

FFDTT_DATE_TIME_OP
Set this flag.
FFDTT_FROM_ADDR
Set this flag to convert the number passed as the number argument instead of the top number on the floating point stack.
FFDTT_FORMAT
This is a DateTimeFormat value.

See Also: FloatFloatToAscii_StdFormat().

FloatFloatToAscii_StdFormat()

word FloatFloatToAscii_StdFormat(
        char 					*string,
        FloatNum 					*number,
        FloatFloatToAsciiFormatFlags 					format,
        word 					numDigits,
        word 					numFractionalDigits)

This routine formats a floating point number as an ASCII string. It is similar in purpose to FloatFloatToAscii() . However, this routine does not require the use of the daunting FFA_stackFrame data structure. It has the following arguments:

string
Buffer to hold the converted string. This buffer must be at least FLOAT_TO_ASCII_NORMAL_BUF_LEN or FLOAT_TO_ASCII_HUGE_BUF_LEN.
number
If you pass FFAF_FROM_ADDR, this should be a pointer to a number to convert; otherwise the top number on the floating point stack will be popped and converted.
format
This is a flags field of type FloatFloatToAsciiFormatFlags . This data type is discussed under FloatFloatToAscii() . The following flags are permitted: FFAF_FROM_ADDR, FFAF_SCIENTIFIC, FFAFF_PERCENT, FFAF_USE_COMMAS, FFAF_NO_TRAIL_ZEROS.
numDigits
The number of significant digits desired.
numFranctionalDigits
The number of digits which may follow the decimal sign.

The routine returns the number of characters in the resulting string, or zero if there was an error.

See Also: FloatFloatToAscii().

| Up: GEOS SDK TechDocs | Up | Prev: FileTruncate() ... | Next: FoamCancelIfNoFreeSpace() ...


GEOS C Routines: FoamCancelIfNoFreeSpace()

 | Up: GEOS SDK TechDocs | Up | Prev: FloatFloatToAscii() ... | Next: FoamDBAddFieldToRecord() ...

FoamCancelIfNoFreeSpace()

Boolean FoamCancelIfNoFreeSpace();

Puts up an error dialog box if free disk space is below "full" level. This should be used beforeany editable view is opened (or before any operation that will consume disk space).

Pass: None.

Return: TRUE if out of disk space. FALSE if free space is below full level.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamCheckIfOutOfSpace()

FoamCheckIfOutOfSpace()

FoamDiskSpaceStatus    FoamCheckIfOutOfSpace();

Determines what level the free disk space has reached. Routines which call this can silently reject an operation if the level of disk space is insufficient.

Pass: None

Return: A FoamDiskSpaceStatus value that indicates how much free disk space is left. These values may be:

  • FDSS_NOT_FULL (free space > 50K)
  • FDSS_NEARLY_FULL (25K < free space < 50K)
  • FDSS_FULL (free space < 25K)

Include: foam.goh

See Also: FoamCancelIfNoFreeSpace()

| Up: GEOS SDK TechDocs | Up | Prev: FloatFloatToAscii() ... | Next: FoamDBAddFieldToRecord() ...


GEOS C Routines: FoamDBAddFieldToRecord()...

| Up: GEOS SDK TechDocs | Up | Prev: FoamCancelIfNoFreeSpace() ... | Next: FoamDBGetCurrentRecordID() ...

FoamDBAddFieldToRecord()

 FieldID FoamDBAddFieldToRecord(
        VMFileHandle       file,
        MemHandle       record,
        byte       *fieldName,
        word       fieldType);

Adds a field to the record record and gives it the name fieldName. A unique FieldID is assigned to the new field and returned. The Foam Database uses the fieldType to determine the sort order of the records (i.e., a field of type "3" will be inserted after all the existing fields of type "3", but before the fields of type "4"). If you are using this routine within the context of a ContDB, then fieldType should be one of the ContdbFieldType values (see below). Otherwise, you may set your own field type.

    /*
     * The standard field types in the contact database
     */
    typedef ByteEnum ContdbFieldType;
    #define CFT_NAME        0
    #define CFT_COMPANY     1
    #define CFT_TITLE       2
    #define CFT_ADDRESS     3
    #define CFT_PHONE       4
    #define CFT_FAX         5
    #define CFT_EMAIL       6
    #define CFT_DTMF        7
    #define CFT_NOTES       8
    /* Only available in Communicator products after Feb 1997: */
    #define CFT_URL         9
    #define CFT_PASSWORD    10
    #define CFT_TEL_FAX     11
    #define CFT_TEL_DATA    12

Include: foamdb.h

FoamDBBinarySearch()

RecordID   FoamDBBinarySearch(
        VMFileHandle file,
        MemHandle record,
        PCB(int, callback,
            (void * passedRecord,	 /* Callback routine */
            void * curRecord )));

This routine performs a binary search of the Foam database file to determine the index of record. The database is sorted according to an application-defined callback routine, so FoamDBBinarySearch() can find record in a divide-and-conquer method using a callback routine based on the same sorting scheme. If this routine finds record, it returns the RecordID of this record in the database; otherwise, it returns where this record would belong in the database.

(PCB is a GEOS macro that defines a callback routine.) The prototype for this callback routine should be of the form:

      int  _pascal  MyCallBackRoutine(void * passedRecord, void * curRecord);
The callback routine should return +1 if the record passedRecord comes after curRecord, -1 if passedRecord comes before curRecord, and 0 if the items match.

Include: foamdb.h

FoamDBClose()

word FoamDBClose(
        VMFileHandle    file);

Closes the file. It is the same as VMClose(). If successful, returns non-zero; if an error occurred, then it returns FALSE and the error value must be retrieved by ThreadGetError().

Include: foamdb.h

FoamDBCreateEmptyRecord()

MemHandle FoamDBCreateEmptyRecord(
        VMFileHandle    file);

Creates a new record in the database, gives it a unique ID, and returns the handle of the record data. This record will contain no fields or data. This record does not get saved to the database unless FoamDBSaveRecord() is called.

Warning: If you want to free the memory associated with a record, do not use MemFree(); instead call FoamDBDiscardRecord() to discard any record changes or FoamDBSaveRecord() to commit them to the the database.

Include: foamdb.h

FoamDBDeleteFieldFromRecord()

void FoamDBDeleteFieldFromRecord(
        VMFileHandle    file,
        MemHandle       record,
        FieldID            id);     /* Field to delete */

Deletes field ID id from the record of the database file. id is the ID of the field as returned from FoamDBAddFieldToRecord(). While it's not a pretty solution, you can also get the FieldID of a field from the FH_id part of the FieldHeader struct.

Include: foamdb.h

FoamDBDeleteRecord()

Boolean FoamDBDeleteRecord(
    VMFileHandle 	file,
    MemHandle 	record);    /* Record to terminated */

This routine frees up the record data in record, and, if the record exists in the database, deletes it from the database as well.

Returns non-zero if the record did not exist in the database.

Include: foamdb.h

FoamDBDiscardRecord()

void FoamDBDiscardRecord(
    VMFileHandle 	file,
    MemHandle 	record);

This routine discards any changes to the passed record record and frees up the record data stored in the passed handle, but does nothing to the data stored in the database file.

Include: foamdb.h

FoamDBDuplicateRecord()

MemHandle FoamDBDuplicateRecord(
        VMFileHandle    file,
        MemHandle       record);

Duplicates an existing record record of FoamDB file, assigns a new ID to it, and returns the handle of the duplicate.

Warning: As with FoamDBCreateEmptyRecord(), the record must eventually be saved or destroyed by calling FoamDBDiscardRecord() or FoamDBSaveRecord().

Include: foamdb.h

FoamDBFieldEnum()

Boolean FoamDBFieldEnum(
        MemHandle record,
        void *enumData,
        PCB(Boolean, callback,  /* Non-zero to stop enum */
                (FieldHeader *field,  void *callbackEnumData)));      /* Callback routine */

This routine essentially "walks" through each field in the specified record and invokes the specified callback routine for each field. enumData can point to the initial value of an application-defined buffer or data structure if one is used by the callback routine, otherwise it can be ignored. This pointer is passed to the callback routine.

The callback routine accepts two parameters; a pointer to the current field field, and some application-defined data callbackEnumData. callbackEnumData may be used to store information between different calls to the callback routine such as storing the longest string yet found or performing complex filtering on the record (in which case callbackEnumData could point to an array of field IDs gathered from the field pointer). To end the enumeration before reaching the last field in the record, return TRUE (or any non-zero value) from the callback routine.

To use the callback routine:

/*
 * Prototype of callback routine
 */
Boolean _pascal ExampleCallback (FieldHeader *field, void *callbackEnumData);
...
/*
 * Call FoamDBFieldEnum()
 */
ok = FoamDBFieldEnum(recordHandle, &someDataStruct, ExampleCallback);

See Also: FoamDBLockedRecordEnum().

Include: foamdb.h

| Up: GEOS SDK TechDocs | Up | Prev: FoamCancelIfNoFreeSpace() ... | Next: FoamDBGetCurrentRecordID() ...


GEOS C Routines: FoamDBGetCurrentRecordID() ...

| Up: GEOS SDK TechDocs | Up | Prev: FoamDBAddFieldToRecord() ... | Next: FoamDBResumeUpdates() ...

FoamDBGetCurrentRecordID()

RecordID FoamDBGetCurrentRecordID(
    VMFileHandle 	file);

Returns the RecordID that would be set for the next record in the database file.

Include: foamdb.h

FoamDBGetFieldData()

 word FoamDBGetFieldData(
        VMFileHandle    file,
        MemHandle       record,
        FieldID             id,
        char            *dest,      /* Buffer to store data */
        word            maxBytesToGet);      /* # bytes of data */

Copies the data from the specified field of record into the buffer dest. maxBytesToGet specifies the maximum number of bytes to copy into the buffer.

Returns the number of bytes actually copied.

Warning: The standard and most efficient way to store string data is without a null terminator, so when working with string data be sure to use the returned data size.

Include: foamdb.h

FoamDBGetFieldName()

Boolean FoamDBGetFieldName(
    VMFileHandle    file,
    MemHandle       record,
    FieldID            id,
    TCHAR   *dest,     /* Buffer to put name (null-terminated) */
    word            maxBytesToGet);     /* Number of characters to get */

Copies the name of the field ID id into the buffer dest.

Returns zero if field exists, non-zero if it doesn't.

Warning: Field names are null-terminated.

Include: foamdb.h

FoamDBGetFieldType()

 Boolean FoamDBGetFieldType(
        VMFileHandle    file,
        MemHandle       record,
        FieldID            id,
        byte            *type);

Gets the type of field ID id and puts it into type. Note that this type is a ContdbFieldType.

Returns zero if field exists, non-zero if it does not.

Include: foamdb.h

FoamDBGetNextPrevRecord()

dword FoamDBGetNextPrevRecord(
    VMFileHandle 	file,
    RecordID 	record,
    word 		count);

This routine returns the index into the huge array of a record count records away from the record with the RecordID record. count can be negative or positive, so this routine lets you move forwards or backwards through the database. Should the requested record be invalid, either because there is no record with RecordID record or the requested index is beyond the bounds of the database, this routine will return record.

Include: foamdb.h

FoamDBGetNumVisibleRecords()

dword	FoamDBGetNumVisibleRecords(
    VMFileHandle 	file);

This routine returns the number of visible records in the database file. This is often used when providing a list of records to a user.

Include: foamdb.h

FoamDBGetRecordFromID()

MemHandle FoamDBGetRecordFromID(
    VMFileHandle 	file,
    RecordID 	id);

This routine looks for a record in the database with the ID id, copies it into memory, and returns the handle. If no record in the database had the passed ID, this routine returns NullHandle.

Warning:
The handle returned should be freed using
FoamDBDeleteRecord(),
FoamDBDiscardRecord(), or
FoamDBSaveRecord().

Include: foamdb.h

FoamDBGetRecordID()

RecordID FoamDBGetRecordID(
        MemHandle       record);

Given a record handle, returns the RecordID of the record.

Include: foamdb.h

FoamDBGetVisibleRecord()

MemHandle FoamDBGetVisibleRecord(
    VMFileHandle 	file,
    dword 	index);

This routine finds the visible record with the index of index, copies the data into memory, and returns the handle of the block to the caller. If index is out of bounds, FoamDBGetVisibleRecord() returns NullHandle.

Warning:
The handle returned should be freed using
FoamDBDeleteRecord(),
FoamDBDiscardRecord(), or
FoamDBSaveRecord().

Include: foamdb.h

FoamDBLockedRecordEnum()

Boolean FoamDBLockedRecordEnum(
        MemHandle record,
        void *enumData,
        PCB(Boolean, callback,  /* Non-zero to stop enum */
                (FieldHeader *field,  void *callbackEnumData)));      /* Callback routine */

This routine calls a callback routine for each field in a locked down record. The callback routine is used the same way as in FoamDBFieldEnum(). It should be of the prototype

/*
 * Prototype of callback routine
 */
Boolean _pascal ExampleCallback (FieldHeader *field, void *callbackEnumData);

Include: foamdb.h

FoamDBMapNameToToken()

Boolean FoamDBMapNameToToken(
    VMFileHandle 	file,
    TCHAR 	*nameToLookFor,
    word 	*token);

This routine looks up the field named nameToLookFor in the database file and checks to see if any fields have that name.

Returns TRUE (-1) if found, FALSE (0) if not found. token will be the index to the element within the FieldName element array. This is not something that will normally be used by an application.

Include: foamdb.h

FoamDBMapTokenToName()

Boolean FoamDBMapTokenToName(
    VMFileHandle file,
    word nameToken,
    word maxBytesToCopy,
    TCHAR *dest);

This routine finds the field name associated with the token nameToken in the database file file, and copies at maximum maxBytesToCopy bytes of that name into the buffer dest. The routine returns non-zero if the token was out of bounds.

Warning: Field names are null-terminated.

Include: foamdb.h

FoamDBOpen()

VMFileHandle  FoamDBOpen(
        char *filename,
        word mapBlockSize,
        word majorProtocol,    /* Existing file protocol must match major and minor protocols */
        word minorProtocol);

Opens an existing database file filename, or creates a new one if it does not currently exist. Returns the handle of the database file or NullHandle if the file could not be opened. ThreadGetError() can be called to return the error type, which is the error returned from VMOpen(), or -1 if there was a protocol mismatch.

mapBlockSize is used to specify the size of the map block for the database. The default (minimum) size is sizeof(FoamDBMap), but more space can be allocated if the application needs to store extra data in the map block.

When creating a new file, the new file will be assigned the protocol number passed in majorProtocol and minorProtocol. When opening an existing file, the protocol number of the file is compared against the protocol number passed in majorProtocol and minorProtocol, and if they do not match, an error of -1 is returned.

Include: foamdb.h

FoamDBResortDatabase()

void FoamDBResortDatabase(
    VMFileHandle file,
    PCB(sword, callback,
        (RecordHeader *record1, RecordHeader *record2)));   /* Callback Routine */

This routine resorts the database file using the application-defined callback function to determine record order. As with the routine FoamDBSaveRecord(), the callback routine should return -1 if record1 should come before record2, or +1 if record1 should come after record2 in the database. The prototype for the callback routine should look like:

sword _pascal MyCallBackRoutine(RecordHeader *record1, RecordHeader *record2);

Include: foamdb.h

FoamDBResumeNotifications()

void FoamDBResumeNotifications(
    VMFileHandle 	file);

This routine resumes the generation of notifications for the database file. These notifications are FoamDBActionTypes.

See Also: FoamDBSuspendNotifications().

Include: foamdb.h

| Up: GEOS SDK TechDocs | Up | Prev: FoamDBAddFieldToRecord() ... | Next: FoamDBResumeUpdates() ...


GEOS C Routines: FoamDBResumeUpdates() ...

| Up: GEOS SDK TechDocs | Up | Prev: FoamDBGetCurrentRecordID() ... | Next: FoamDocCheckProtocol() ...

FoamDBResumeUpdates()

void FoamDBResumeUpdates(
    VMFileHandle 	file);

This routine resumes the generation of updates for the database file.

Include: foamdb.h

FoamDBSaveRecord()

dword FoamDBSaveRecord(
    VMFileHandle file,
    MemHandle record,
    PCB(sword, callback,
        (RecordHeader *record1,  RecordHeader *record2)));    /* Callback routine */

This routine saves the record record in the database file file, and frees up the passed memory block containing the record data. The callback routine determines where in the database the record should be stored. The Contdb library provides a front-end to this routine, ContactSaveRecord(), which should be used when saving records to a contact database.

PCB() is a GEOS macro which signifies that one of the parameters for FoamDBSaveRecord() will be a callback routine that will return a sword and accept two RecordHeaders. This callback routine should be declared as:

sword _pascal MyCallBackRoutine(RecordHeader *record1, RecordHeader *record2);

It should return -1 if record1 should come before record2, or +1 if record1 should come after record2 in the database.

Include: foamdb.h

FoamDBSetCurrentRecordID()

void FoamDBSetCurrentRecordID(
    VMFileHandle 	file,
    RecordID 	id);

Sets the record ID that will be used for the next created record.

Include: foamdb.h

FoamDBSetFieldData()

 void FoamDBSetFieldData(
        VMFileHandle    file,
        MemHandle       record,
        FieldID            id,
        char            *data,          /* Data to be stored */
        word            dataSize);      /* How many bytes of data */

Stores dataSize bytes of data in the field ID id of record.

Warning: When storing ASCII data (strings), the strings should not be null terminated. For example, if you want to store the string "ABCD", then let data be a pointer to the string and let dataSize equal 4.

Include: foamdb.h

FoamDBSetFieldName()

void FoamDBSetFieldName(
        VMFileHandle    file,
        MemHandle       record,
        FieldID            id,
        char            *name);

Changes the name of the field with the ID id of record to name.

Warning: name is a null-terminated string.

Include: foamdb.h

FoamDBSetNameForToken()

void FoamDBSetNameForToken(
    VMFileHandle 	file,
    word 	nameToken,
    TCHAR 	*name);

Sets the name for the field identified by nameToken token in the database's FieldName element array.

Warning: Field names are null-terminated.

Include: foamdb.h

FoamDBSuspendNotifications()

void FoamDBSuspendNotifications(
    VMFileHandle 	file);

This routine stops the generation of notifications to the database file. Calls to this routine must be matched by calls to FoamDBResumeNotifications(),

Include: foamdb.h

FoamDBSuspendUpdates()

void FoamDBSuspendUpdates(
    VMFileHandle	 file);

This routine stops the generation of updates for the database file. Calls to this routine must be matched by calls to FoamDBResumeUpdates().

Warning: It is dangerous to prevent updates.

Include: foamdb.h

FoamDBVisibleRecordEnum()

Boolean FoamDBVisibleRecordEnum(
    VMFileHandle file,
    void *enumData,
    PCB(Boolean, callback,     /* Non-zero to stop enum */
          (RecordHeader *record, void *callbackEnumData)));     /* Callback Routine */
This routine calls an application-specified callback routine for each visible record in the database. The callback routine can return non-zero to stop the enumeration. enumData is a pointer to any kind of data that your application needs to pass to the callback routine and that the callback routine can modify. The callback routine can return non-zero to stop the enumeration.

For a more complete explanation of using callback routines in a FoamDB, refer to the explanation given for the similar routine FoamDBFieldEnum(). The callback routine should be declared as

/*
 * Prototype of callback routine
 */
Boolean _pascal myCallBackRoutine(RecordHeader *record, void *callbackEnumData);

Include: foamdb.h

FoamDBVisibleRecordEnumWithRange()

 Boolean FoamDBVisibleRecordEnumWithRange(
    VMFileHandle file,
    void *enumData,
    dword startElement,
    dword numOfRecords,
    PCB(Boolean, callback,      /* Non-zero to stop enum */
        (RecordHeader *record, void *callbackEnumData)));     /* Callback routine */

This routine calls the application-specified callback routine for some visible records in the database, starting with the record number startElement, and continuing for numOfRecords records. enumData is a pointer to any kind of data that your application needs to pass to the callback routine and that the callback routine can modify. The callback routine can return non-zero to stop the enumeration.

For a more complete explanation of using callback routines in a FoamDB, refer to the explanation given for the similar routine FoamDBFieldEnum(). The callback routine should be declared as

/*
 * Prototype of callback routine
 */
Boolean _pascal myCallBackRoutine(RecordHeader *record, void *callbackEnumData);

Include: foamdb.h

| Up: GEOS SDK TechDocs | Up | Prev: FoamDBGetCurrentRecordID() ... | Next: FoamDocCheckProtocol() ...


GEOS C Routines: FoamDisplayDeleteWarning()

 | Up: GEOS SDK TechDocs | Up | Prev: FoamDBResumeUpdates() ... | Next: FoamDocCheckProtocol() ...

FoamDisplayDeleteWarning()

InteractionCommand    FoamDisplayDeleteWarning(
   optr        preText,
   TCHAR *     midText,
   optr        postText );

Displays a notification box that warns the user that a file is about to be deleted.

Pass:

preText
Optr of the text chunk to display before the file name; NullOptr if no text is to be displayed.
midText
Pointer to the name of the file being deleted.
postText
Optr of the text chunk to display after the file name; NullOptr if no text is to be displayed.

Return: Input from the user: IC_YES if okay to delete, IC_NULL if not okay or if system couldn't create the dialog.

Warning: This routine may resize LMem and/or object blocks, moving themon the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayDeleteWarningNoBlock()

FoamDisplayDeleteWarningNoBlock()

void FoamDisplayDeleteWarningNoBlock(
   optr  bodyText,
   word  msg,
   optr  dest );

Displays a notification box that gives the user a delete warning. This routine differs from FoamDisplayDeleteWarning() because it does not block the caller. Instead, it displays the dialog, and upon closure by the user it will send the specified message to the given object.

Pass:

bodyText
Optr to text chunk to display in dialog.
msg
The message to send when the user selects the "Delete" option.
dest
The object to send the message specified in msg when the user selects the "Delete" option.

Return: None.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayDeleteWarning()

FoamDisplayError()

void   FoamDisplayError(
     optr    errorText );

Displays an error dialog box, with a single "Ok" trigger, displaying the text pointed to by errorText. Use FoamDisplayErrorNoBlock() if you do not want to block the calling thread.

Pass:

errorText
Optr to the text chunk to display in the dialog box.

Return: None.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayErrorNoBlock()

FoamDisplayErrorNoBlock()

void FoamDisplayErrorNoBlock(
      optr     errorText );

Same as the FoamDisplayError() routine (listed above) except this one does not block the calling thread.

Pass:

errorText
Optr to the text chunk to display in the dialog box.

Return: None.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayError()

FoamDisplayNote()

void FoamDisplayNote(
     optr    noteText);

Displays a "Note" dialog box, with a single "Ok" trigger. Use FoamDisplayNoteNoBlock() to not block the calling thread.

Pass:

noteText
Optr to text chunk of the note to display.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayNoteNoBlock()

FoamDisplayNoteNoBlock()

void FoamDisplayNoteNoBlock(
     optr  noteText );

Same as the FoamDisplayNote() routine (listed above) except this one does not block the calling thread.

Pass:

noteText
Optr to text chunk of the note to display.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayNote()

FoamDisplayOkCancelWarning()

InteractionCommand   FoamDisplayOkCancelWarning(
   optr  warningText );

Displays a warning dialog that allows the user to cancel out of an action. The dialog will have both an "Ok" and a "Cancel" trigger.

Pass:

warningText
Optr to the text chunk containg a message to display in the dialog. This will usually be a question like "Delete your data?"

Return: The user's input: IC_APPLY if user selected "Accept". IC_NULL if dialog could not be displayed.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayQuestion(), FoamDisplayWarning(), FoamDisplayWarningNoBlock().

FoamDisplayQuestion()

InteractionCommand   FoamDisplayQuestion(
   optr  questionText );

Ask the user a Yes/No question, such as "Save changes to 'foo' before quitting?". The associated text should normally end in a question mark.

Pass:

questionText
Optr to the text chunk of the question being asked.

Return: The user's input: IC_YES if user selected "Yes". IC_NULL if dialog could not be displayed.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also:
FoamDisplayOkCancelWarning(),
FoamDisplayWarning(),
FoamDisplayWarningNoBlock().

FoamDisplayWarning()

void    FoamDisplayWarning(
   optr  warningText );

Displays a notification box that will warn the user about an action. It will not allow the user to cancel the action. Use FoamDisplayWarningNoBlock() to not block the calling thread.

Pass:

warningText
Optr to the text chunk containing the warning message.

Return: None.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also:
FoamDisplayOkCancelWarning(),
FoamDisplayQuestion(),
FoamDisplayWarningNoBlock().

FoamDisplayWarningNoBlock()

void    FoamDisplayWarningNoBlock(
   optr  warningText );

Same as the FoamDisplayWarning() routine (above) except this routine does not block the calling thread.

Pass:

warningText
Optr to the text chunk containing the warning message.

Return: None.

Include: foam.goh

See Also:
FoamDisplayOkCancelWarning(),
FoamDisplayQuestion(),
FoamDisplayWarning().

| Up: GEOS SDK TechDocs | Up | Prev: FoamDBResumeUpdates() ... | Next: FoamDocCheckProtocol() ...


GEOS C Routines: FoamDocCheckProtocol()

 | Up: GEOS SDK TechDocs | Up | Prev: FoamDBResumeUpdates()... | Next: FoamDocCreate()...

FoamDocCheckProtocol()

Boolean FoamDocCheckProtocol(
   DocumentHandle  docHandle,
   word            numCreatorChars,
   word            majorProtocol,
   word            minorProtocol );

Checks a document file for the correct protocol and creator token.

Pass:

docHandle
Document whose protocol will be checked.
numCreatorChars
Number of FEA_CREATOR token chars to check. You can pass two if you only want to check the first two characters.
majorProtocol
Major protocol number to check.
minorProtocol
Minor protocol number to check (pass -1 if you don't want to check the minor protocol).

Return: TRUE if the check failed, FALSE otherwise.

Include: foam.goh

See Also: None.

FoamDocClose()

void FoamDocClose(
   DocumentHandle  docHandle );

Closes a Foam document that was opened using FoamDocOpen().

Pass:

docHandle
Handle of the document to close, usually the one returned by FoamDocOpen().

Return: None.

Include: foam.goh

See Also:
FoamDocCreate(),
FoamDocDelete(),
FoamDocOpen().

FoamDocConvertFromDosAppend()

void FoamDocConvertFromDosAppend(
   optr     object,
   TCHAR *  buffer,
   word     bufferLength );

Append a buffer of DOS text to the temporary text object. The DOS text is converted to the GEOS character set; carriage return and Ctrl-Z are removed.

Pass:

object
Temporary text object to use for the conversion process. This is usually the optr returned by FoamDocConvertFromDosInit().
buffer
Pointer to the buffer of text to append.
bufferLength
Number of characters to append. Zero if buffer is null-terminated.

Return: None.

Include: foam.goh

See Also:
FoamDocConvertFromDosDone(),
FoamDocConvertFromDosInit(),
FoamDocConvertToDosDone(),
FoamDocConvertToDosInit(),
FoamDocConvertToDosRange(),
FoamDocGetDosTextSmall().

FoamDocConvertFromDosDone()

void   FoamDocConvertFromDosDone(
   DocumentHandle  docHandle,
   optr            object );

This function is called when you are done appending to the temporary text object. A text transfer item is created from the text object and stored in the specified document. The transfer item is stored in a VM block in the document, and its VMBlockHandle is stored in the document header (the document header is the VM file's map block; see FoamDocMapBlock in foam.goh for the structure of this map block) at the FDH_TEXT_TR_ITEM slot of the FoamDocHeader (see foam.goh for the definition of that enum).

The text object specified in "object" is then destroyed once the transfer is complete.

Pass:

docHandle
Temporary text object used in the conversion process. This is usually the optr returned by FoamDocConvertFromDosInit().

Return: None

Include: foam.goh

See Also:
FoamDocConvertFromDosAppend(),
FoamDocConvertFromDosInit(),
FoamDocConvertToDosDone(),
FoamDocConvertToDosInit(),
FoamDocConvertToDosRange(),
FoamDocGetDosTextSmall().

FoamDocConvertFromDosInit()

optr FoamDocConvertFromDosInit();

Initializes the DOS text file to Foam text document conversion. The function returns an optr of a temporary text object used in the conversion. The next step would be to use FoamDocConvertFromDosAppend to convert DOS text to GEOS text, then use FoamDocConvertFromDosDone to finish the conversion process.

Pass:

None

Return: Optr of the temporary text object to be used in the conversion process.

Include: foam.goh

See Also:
FoamDocConvertFromDosAppend(),
FoamDocConvertFromDosDone(),
FoamDocConvertToDosDone(),
FoamDocConvertToDosInit(),
FoamDocConvertToDosRange(),
FoamDocGetDosTextSmall().

FoamDocConvertToDosDone()

void    FoamDocConvertToDosDone(
   optr  object );

This function is called when you're done retrieving text from the temporary text object.

Pass:

object
Optr of the temporary text object used in the conversion process. This is usually the optr returned by FoamDocConvertToDosInit().

Return: None

Include: foam.goh

See Also: FoamDocConvertFromDosAppend(), FoamDocConvertFromDosDone, FoamDocConvertFromDosInit, FoamDocConvertToDosInit, FoamDocConvertToDosRange.

FoamDocConvertToDosInit()

optr FoamDocConvertToDosInit(
   DocumentHandle  docHandle );

Initializes the Document to Dos conversion, returning an optr of a temporary text object used in the conversion. All calls to FoamDocConvertToDosInit() must be offset by a call to FoamDocConvertToDosDone().

Pass:

docHandle
Handle of document that will be used in the conversion.

Return: Optr of a temporary text object to use in conversion routines.

Include: foam.goh

See Also: FoamDocConvertFromDosAppend, FoamDocConvertFromDosDone, FoamDocConvertFromDosInit, FoamDocConvertToDosDone, FoamDocConvertToDosRange.

FoamDocConvertToDosRange()

word FoamDocConvertToDosRange(
   optr     object,
   TCHAR *  buffer,
   dword    startPos,
   dword    endPos );

Retrieve a range of text from the document into a specified buffer. The buffer must be able to hold the number of bytes requested. The caller is responsible for making sure this is the case.

Pass:

object
Optr to the temporary text object used in the conversion process. This is usually the optr returned by FoamDocConvertToDosInit().
buffer
Pointer to a buffer that will contain the DOS text. This buffer must be twice the size of the text you're requesting as the routine uses the extra space for doing work. For example, if you request 512 characters to be converted, this buffer must be no less than 1024 bytes long.
startPos
Character offset into the text object at which to begin conversion.
endPos
Character offset into the text object at which to end conversion. You can use the constant TEXT_ADDRESS_PAST_END to specify the end of the text in the object.

Return: The number of characters converted.

Include: foam.goh

See Also: FoamDocConvertFromDosAppend, FoamDocConvertFromDosDone, FoamDocConvertFromDosInit, FoamDocConvertToDosDone, FoamDocConvertToDosInit.

| Up: GEOS SDK TechDocs | Up | Prev: FoamDBResumeUpdates()... | Next: FoamDocCreate()...


GEOS C Routines: FoamDocCreate()

 | Up: GEOS SDK TechDocs | Up | Prev: FoamDocCheckProtocol() | Next: FoamDocSetBlockHandle()

FoamDocCreate()

word FoamDocCreate(
   DocumentInfo *    docInfo,
   FileAttrInfo *    fileAttr,
   DocumentPrefix *  prefix );

This creates a Foam document file. It is simply created and then closed. You'll need to call FoamDocOpen() after using FoamDocCreate() in order to access the file.

The document that is created will have a default creator token of "RDOC", MANUFACTURER_ID_GEOWORKS. To change the document's creator token, call FoamDocSetAttrInfo() with FIA_token set to your application's GeodeToken.

Pass:

docInfo
Pointer to a DocumentInfo structure which contains the name of the document and the subdirectory it should go in under SP_DOCUMENT. Don't need DI_docName if prefix points to a name prefix since FoamDocCreate() will then make a default document name.
fileAttr
GeodeToken and ProtocolNumber to use for the document.
prefix
The prefix added to a document name. If null, no default name is made and name must be supplied in docInfo parameter; e.g. "NOTE 12.12.94 12:12 PM"

Return: Error value if something went wrong. This word is of type VMStatus, defined in vm.goh.

Include: foam.goh

See Also:
FoamDocClose(),
FoamDocDelete(),
FoamDocOpen().

FoamDocDelete()

Boolean FoamDocDelete(
   DocumentInfo *  docInfo );

This will delete a Foam document file, given a DocumentInfo struct. The document must be closed before you can delete it.

Pass:

docInfo
Pointer to a DocumentInfo struct, which specifies the file to be deleted.

typedef	struct {
    FileLongName DI_docName;  /* Document name */
    FDocumentDir DI_docDir;   /* Document directory */
} DocumentInfo;

typedef enum {
    FDD_PRIVATE,
    FDD_DOCUMENT,
    FDD_NONE,
    FDD_CREATED_TEXTS,
    FDD_RECEIVED_FAXES,
    FDD_RECEIVED_SM,
    FDD_RECEIVED_MAILS,
    FDD_MESSAGE_TEMPLATES,
    FDD_BUSINESS_CARDS,
    FDD_DOWNLOADED_FILES,
    FDD_WWW_HOTLIST,
    FDD_BROADCAST_MESSAGES,
    FDD_INTERNET,
    FDD_TONES
} FDocumentDir;

Return: TRUE if an error occurred, FALSE otherwise.

Include: foam.goh

See Also:
FoamDocClose(),
FoamDocCreate(),
FoamDocOpen().

FoamDocGetDosTextSmall()

word FoamDocGetDosTextSmall(
   DocumentHandle  docHandle,
   void *          buffer,
   word            count );

Returns the text stored in the text transfer item of the document in a character string format.

The text returned is converted from the GEOS character set to the native character set. Lines end with LF's and a NULL marks the end of text.

Pass:

docHandle
Handle of the Foam document from which to grab the text. This document must contain a text transfer item, similar to what is returned by FoamDocConvertFromDosDone().
buffer
Pointer to the buffer in which to put the DOS text.
count
The number of characters to convert. Use the FoamDocGetTextLength() routine to find the size of the Foam document.

Return: The number of characters converted.

Warning: Use this routine only if you know that the text in the document is less than a block in size (~10k).

Include: foam.goh

See Also:
FoamDocConvertFromDosAppend(),
FoamDocConvertFromDosDone(),
FoamDocConvertFromDosInit().

FoamDocGetHeaderData()

word FoamDocGetHeaderData(
   DocumentHandle  docHandle,
   FoamDocHeader   docHeader );

Retrieves the specified word of data from the Foam document's VM map block ("doc header"). The document header is simply a VMem map block. See the Concepts book for more information on Virtual Memory.

Pass:

docHandle
Handle of the Foam document from which to grab the header data.
docHeader
Specifies which word of data you want from the document header.
typedef enum {
    FDH_TEXT_TR_ITEM,
    FDH_ADDRESS_INFO,
    FDH_HANDLE1,
    FDH_HANDLE2,
    FDH_HANDLE3,
    FDH_HANDLE4,
    FDH_HANDLE5
} FoamDocHeader;

Return: The word of data from the document header.

Include: foam.goh

See Also: None.

FoamDocGetTextLength()

dword FoamDocGetTextLength(
   DocumentHandle  docHandle );

Returns the number of characters of text in the document.

Pass:

docHandle
Handle of the Foam document containing the text clipboard item.

Return: The length of the text in characters.

Include: foam.goh

See Also:
FoamDocGetDosSmall(),
FoamDocGetHeaderData(),
FoamDocGetTextLengthFromField().

FoamDocGetTextLengthFromField()

dword FoamDocGetTextLengthFromField(
   char *  field );

Returns the length of the text from a pointer to a field (User Notes). The pointer must be pointing to the "User notes" field of the file, as that is where the length of the text in the Foam document is stored.

Pass:

field
Pointer to the beginning of the "User notes" field.

Return: The length of the text in characters.

Include: foam.goh

See Also:
FoamDocGetDosSmall(),
FoamDocGetHeaderData(),
FoamDocGetTextLength().

FoamDocIsTextFormatted()

Boolean FoamDocIsTextFormatted(
   DocumentHandle      docHandle,
   DocumentFontInfo *  fontInfo );

This routine retrieves the FoamDocLengthAndFormatted structure from FEA_USER_NOTES and returns if the text of the document is formatted or not.

Pass:

docHandle
Handle of the Foam document containing a text clipboard item.
fontInfo
ointer to a DocumentFontInfo structure, which will be filled in with the font and point size of the unformatted text. Note that this structure is filled in only if the text is of a single point size and font id (i.e. FoamDocIsTextFormatted returns FALSE).
typedef struct {
    FontID	DFI_font;
    word	DFI_pointSize;
} DocumentFontInfo;

Return: TRUE if the text is formatted, FALSE otherwise. If FALSE, then fontInfo struture will be filled in with point size and font id of the text.

Include: foam.goh

See Also: FoamIsTextFormatted().

FoamDocOpen()

DocumentHandle FoamDocOpen(
   DocumentInfo *  docInfo,
   FDocAccess      docAccess );

Opens a Foam document and returns a file handle. If the return value is NullHandle, use ThreadGetError() to check for an error value.

Pass:

docInfo
Pointer to a DocumentInfo structure which contains the name of the document and the subdirectory it lives in under SP_DOCUMENT. The structure for DocumentInfo is defined as follows:
typedef	struct {
    FileLongName DI_docName;  /* Document name */
    FDocumentDir DI_docDir;   /* Document directory */
} DocumentInfo;

typedef enum {
    FDD_PRIVATE,
    FDD_DOCUMENT,
    FDD_NONE,
    FDD_CREATED_TEXTS,
    FDD_RECEIVED_FAXES,
    FDD_RECEIVED_SM,
    FDD_RECEIVED_MAILS,
    FDD_MESSAGE_TEMPLATES,
    FDD_BUSINESS_CARDS,
    FDD_DOWNLOADED_FILES,
    FDD_WWW_HOTLIST,
    FDD_BROADCAST_MESSAGES,
    FDD_INTERNET,
    FDD_TONES
} FDocumentDir;
docAccess
Indicates how to open the file - read only or read/write. Possible values are:
  • FDA_READ_WRITE
  • FDA_READ_ONLY
  • FDA_READ_ONLY_DENY_NONE

Return: Handle of the newly open document, or NullHandle if an error occurred.

Include: foam.goh

See Also:
FoamDocClose(),
FoamDocCreate(),
FoamDocDelete().

FoamDocSetAttrInfo

void FoamDocSetAttrInfo(
   DocumentHandle  docHandle,
   FileAttrInfo *  fileAttrInfo );

Initialize a file's extended attributes. This is done to allow detection of when a file that is being opened is actually a specific file type or not. Also, when major changes are made to the file structure we can change the protocol number to see if the file that we opened is compatible.

Pass:

docHandle
Handle of the FoamDocument being initialized.
fileAttrInfo
Pointer to a FileAttrInfo structure that will be used to set the files attributes. The structure definition is as follows:
typedef struct {
    GeodeToken	   FAI_token;     /* token of file */
    ProtocolNumber FAI_protocol;  /* protocol number of file */
} FileAttrInfo;

Return: None.

Include: foam.goh

See Also:
FoamDocSetBlockHandle(),
FoamDocSetHeaderData(),
FoamDocSetTextFormatted(),
FoamDocSetTextLength().

| Up: GEOS SDK TechDocs | Up | Prev: FoamDocCheckProtocol() | Next: FoamDocSetBlockHandle()


GEOS C Routines: FoamDocSetBlockHandle()

 | Up: GEOS SDK TechDocs | Up | Prev: FoamDocCreate() | Next: FoamGenerateUniqueFilename()

FoamDocSetBlockHandle()

void FoamDocSetBlockHandle(
   DocumentHandle  docHandle,
   VMBlockHandle   block,
   FoamDocHeader   docHeader );

Stores the handle of block in a field within the document's header block. The field is specified by docHeader. If the field already contains a VMBlock handle, that block will be freed by the system and your block handle will be placed in the field.

If the data you want to store in the document header (which is a FoamDocMapBlock structure, defined in foam.goh) isn't a VM handle, then use FoamDocSetHeaderData() instead.

Pass:

docHandle
Handle of the Foam document file which is being changed.
block
The VMBlockHandle being saved to the document header.
docHeader
Field within the document header where the block handle will be stored. FoamDocHeader is defined as:
typedef enum {
    FDH_TEXT_TR_ITEM,
    FDH_ADDRESS_INFO,
    FDH_HANDLE1,
    FDH_HANDLE2,
    FDH_HANDLE3,
    FDH_HANDLE4,
    FDH_HANDLE5
} FoamDocHeader;

Return: None.

Warning: Do not set docHeader to FDH_TEXT_TR_ITEM or FDH_ADDRESS_INFO as these two fields are used by the system.

Include: foam.goh

See Also:
FoamDocSetAttrInfo(),
FoamDocSetHeaderData(),
FoamDocSetTextFormatted(),
FoamDocSetTextLength().

FoamDocSetHeaderData()

void FoamDocSetHeaderData(
   DocumentHandle  docHandle,
   word            data,
   FoamDocHeader   docHeader );

Stores a word of data in a field within the specified document's header block. The field is specified by docHeader.

If the data you want to store in the document header is a VMBlock handle, use FoamDocSetBlockHandle() instead.

Pass:

docHandle
Handle of the Foam document that is being changed.
data
The word of data to store in the document header.
docHeader
Field within the document header where the "block" handle will be stored. FoamDocHeader is defined as follows.
typedef enum {
    FDH_TEXT_TR_ITEM,
    FDH_ADDRESS_INFO,
    FDH_HANDLE1,
    FDH_HANDLE2,
    FDH_HANDLE3,
    FDH_HANDLE4,
    FDH_HANDLE5
} FoamDocHeader;

Return: None

Warning: Do not set docHeader to FDH_TEXT_TR_ITEM or FDH_ADDRESS_INFO as these two fields are used by the system.

Include: foam.goh

See Also:
FoamDocSetAttrInfo(),
FoamDocSetBlockHandle(), FoamDocSetTextFormatted(),
FoamDocSetTextLength().

FoamDocSetTextFormatted()

void FoamDocSetTextFormatted(
   DocumentHandle        docHandle,
   FoamDocTextFormatted  fdtf,
   DocumentFontInfo *    fontInfo );

Stores information on the document text in the GeosFileHeader of the Document. This information can be retrieved by calling FoamDocIsTextFormatted().

Pass:

docHandle
Handle of the Foam document which is being changed.
fdtf
Set this to FDTF_FORMATTED if the text is formatted, otherwise set to FDTF_NOT_FORMATTED.
fontInfo
Pointer to a DocumentFontInfo structure. If the text does not contain multiple formats, then pass the font and point size of the text. See the description for FoamDocIsTextFormatted() for the definition of DocumentFontInfo.

Return: None.

Include: foam.goh

See Also:
FoamDocIsTextFormatted(),
FoamDocSetAttrInfo(),
FoamDocSetBlockHandle(),
FoamDocSetHeaderData(),
FoamDocSetTextLength().

FoamDocSetTextLength()

void FoamDocSetTextLength(
   DocumentHandle  docHandle,
   dword           length );

Sets the document's text length. This is usually used internally and you won't need to call this in most cases. This information is stored in the Foam document header and does not affect the text stored in the document.

Pass:

docHandle
Handle of the Foam document which is being changed.
length
Length that you want to store in the document header.

Return: None.

Include: foam.goh

See Also:
FoamDocSetAttrInfo(),
FoamDocSetBlockHandle(),
FoamDocSetHeaderData(),
FoamDocSetTextFormatted().

FoamDocStoreDosTextSmall()

void FoamDocStoreDosTextSmall(
   DocumentHandle  docHandle,
   void *          buffer,
   word            bufferLen );

Stores a character string in the document FDH_TEXT_TR_ITEM block. The text is converted from the native character set to the GEOS character set. Carriage return and Ctrl-Z are removed from the string.

Pass:

docHandle
Handle of the Foam document which is being changed.
buffer
Pointer to the string of text to store. Must be null-terminated.
bufferLen
Number of characters in the string pointed to by buffer.

Return: None.

Warning: Use this only if the text you are storing is less than a block in size (~10k). If the DOS text you want to store is larger than 10k, use the FoamDocConvertFromDos() routines listed below.

Include: foam.goh

See Also:
FoamDocConvertFromDosAppend(),
FoamDocConvertFromDosDone(),
FoamDocConvertFromDosInit(),
FoamDocStoreTextAsVMChain().

FoamDocStoreTextAsVMChain()

word FoamDocStoreTextAsVMChain(
   DocumentHandle  docHandle,
   optr            textObject );

This routine copies the text of a text object into a chain of text transfer items, each about 4000 bytes in size. The starting block of the chain is saved in FDH_TEXT_TR_ITEM of the document header. The next transfer item block handle is saved in the TTBH_reservedVM field of the TextTransferBlockHeader.

When it's time to read in the chain of text transfer items, first use the block in FDH_TEXT_TR_ITEM with MSG_VIS_TEXT_REPLACE_WITH_TEXT_TRANSFER_FORMAT then loop until the high word of TTBH_reservedVM is zero. To get at TTBH_reservedVM use VMLock() passing the the VMBlockHandle you used when calling MSG_VIS_TEXT_REPLACE_WITH_TEXT_TRANSFER_FORMAT.

Pass:

docHandle
Handle of the Foam document which is being changed.
textObject
Optr to the text object containing the text to store in the Foam document.

Return: VMBlockHandle of the first text transfer item. (This handle is also stored in FDH_TEXT_TR_ITEM.)

Include: foam.goh

See Also: FoamDocStoreDosTextSmall().

| Up: GEOS SDK TechDocs | Up | Prev: FoamDocCreate() | Next: FoamGenerateUniqueFilename()


GEOS C Routines: FoamGenerateUniqueFilename()

 | Up: GEOS SDK TechDocs | Up | Prev: FoamDocSetBlockHandle() | Next: FoamStandardDialogOptr()

FoamGenerateUniqueFilename()

void FoamGenerateUniqueFilename(
   FileLongName *nameBuffer);

This routine will return a data file name that is unique. If you specify a prefix in nameBuffer, this routine will add the next available number to the prefix and return the document name in the form: "[prefix] #[number]".

Pass:

nameBuffer
Buffer into which the file name will be written.

Return: None.

Warning: Occasionally an invalid file name can be returned. If you try to create a file with the returned name and the create fails, call this routine again to get a new name.

Include: foam.goh

See Also: FoamDocCreate().

FoamGetDocumentDir()

FDocumentDir FoamGetDocumentDir();

Returns the FDocumentDir value that corresponds to the current path. If the current path is not one of the FDocumentDir, then FDD_NONE is returned. FDocumentDir is a list of standard subdirectories under the Document directory.

Pass: None.

Return: The appropriate FDocumentDir value.

Include: foam.goh

See Also: FoamGetDocumentDirName(), FoamSetDocumentDir().

FoamGetDocumentDirName()

void FoamGetDocumentDirName(
   FDocumentDir fDocDir,
   FileLongName *buffer);

Fills in the buffer with the directory name corresponding to the specified FDocumentDir. FDocumentDir is a list of standard subdirectories under the Document directory.

Pass:

fDocDir
The FDocumentDir enum whose name you want.
buffer
A buffer that will hold the name of the directory.

Return: None; *buffer is filled in.

Include: foam.goh

See Also: FoamGetDocumentDir(), FoamSetDocumentDir().

FoamGetFileType()

GeosFileType FoamGetFileType(
   TCHAR *filename);

Get the GeosFileType of the named file.

Pass:

filename
Name of the file whose GeosFileType you want to retrieve. The buffer need not be longer than FILE_LONGNAME_BUFFER_SIZE bytes.

Return: A GeosFileType will be returned. The value will be

  • GFT_VM (standard GEOS VM file)
  • GFT_DATA (a non-VM data file)
  • GFT_EXECUTABLE (file is a application, library or driver)
  • GFT_DIRECTORY (if the name was a directory rather than a file)
  • GFT_NOT_GEOS_FILE (the file was most likely created by DOS)

Include: foam.goh

See Also: FoamGetHandleFileType().

FoamGetFreeDiskSpace()

dword FoamGetFreeDiskSpace();

This routine returns the number of bytes available on the disk.

Pass: None.

Return: The number of free bytes on the disk.

Warning: Since Geos is multi-threaded, the amount of free disk space may change rapidly as other threads use the disk..

Include: foam.goh

See Also:
FoamRoundUpAsMultipleOfClusterSize(),
FoamCheckIfOutOfSpace(),
FoamWarnIfOutOfSpace(),
FoamWarnForSaving(),
FoamCancelIfNoFreeSpace(),
FoamWarnIfNotEnoughSpace(),
FoamWarnSpaceAfterOperation().

FoamGetHandleFileType()

GeosFileType FoamGetHandleFileType(
   DocumentHandle docHandle);

Get the GeosFileType of the file specified by docHandle.

Pass:

docHandle
DocumentHandle of the file whose GeosFileType you want to retrieve.

Return: A GeosFileType will be returned. The value will be

  • GFT_VM (standard GEOS VM file)
  • GFT_DATA (a non-VM data file)
  • GFT_EXECUTABLE (file is a application, library or driver)
  • GFT_DIRECTORY (if the name was a directory rather than a file)
  • GFT_NOT_GEOS_FILE (the file was most likely created by DOS)

Include: foam.goh

See Also: FoamGetFileType().

FoamGetLexicalOrder()

byte FoamGetLexicalOrder(
   char theChar);

Gets the "lexical order" of a character, for use in optimized case and accent-insensitive searching (all versions of a character, accented, unaccented, upper and lower case will have the same lexical order).

Pass:

theChar
The character whose lexical equivalent you want.

Return: A LexicalOrder for the character is returned. See LexicalOrder enumeration in Include/Internal/lexical.def.

Include: foam.goh

See Also: None.

FoamGetLowSoundFlag()

word FoamGetLowSoundFlag();

Returns the status of the low sound mode.

Low sound mode specifies that UserStandardSound() will ignore the standard sound type it is told to play. Instead it plays a low, monotone note.

Pass: None.

Return: Non-zero (TRUE) if the flag is set; zero (FALSE) if the flag is not set.

Include: foam.goh

See Also: UserStandardSound(), FoamSetLowSoundFlag().

FoamIsTextFormatted()

Boolean FoamIsTextFormatted(
   optr             textObject,
   DocumentFontInfo *fontInfo);

Determines if text in a text object is formatted (does it have multiple character attributes). If the text is not formatted the function will return the font and the point size of the text in *fontInfo.

Pass:

textObject
optr to the text object to examine.
fontInfo
buffer that will be filled in if the text isn't in multiple formats.

Return: TRUE if the text is formatted, FALSE if text is all in a single format. If FALSE, then *fontInfo will be filled in.

Include: foam.goh

See Also: FoamDocIsTextFormatted().

FoamLaunchApplication()

void FoamLaunchApplication(
   FApplication applicationConstant);

Launch the application designated by applicationConstant. This is intended to be used by applications launched by System, Extras, Services so that when they quit (rather, lower to bottom), the original launcher appears.

When this routine is called and the designated application launched, the current application is lowered to bottom.

Pass:

applicationConstant
One of the following FApplication types:
typedef enum {
    FA_PHONE_APP,
    FA_FAX_APP,
    FA_SMS_APP,
    FA_SERVICES_APP,
    FA_CONTACT_APP,
    FA_NOTEBOOK_APP,
    FA_CALENDAR_APP,
    FA_SYSTEM_APP,
    FA_EXTRAS_APP,
    FA_CALCULATOR_APP,
    FA_CLOCK_APP,
    FA_USER_DATA_APP,
    FA_WWW_INSTALL_APP,
} FApplication;

Return: None.

Include: foam.goh

See Also: UserLoadApplication(), IACPConnect().

FoamRoundUpAsMultipleOfClusterSize()

dword FoamRoundUpAsMultipleOfClusterSize(
   dword space);

Since files require a full cluster on the disk, whether they actually fill the entire cluster or not, this routine allows you to calculate the actual disk space a file will require. Simply submit the requested space and the routine will return a size that is a multiple of cluster sizes. This returned size will never be less than the requested space.

FoamWarnForSaving() and FoamWarnIfNotEnoughSpace() use this routine.

Pass:

space
The number of bytes of disk space you want.

Return: The number of bytes of disk space that will actually be used by a file of the specified size.

Include: foam.goh

See Also: None.

FoamSetDocumentDir()

void FoamSetDocumentDir(
   FDocumentDir fDocDir);

Sets the current path to one of the standard FDocumentDir directories.

Pass:

fDocDir
The desired FDocumentDir enum.

Return: None.

Include: foam.goh

See Also:
FileSetStandardPath(),
FileSetCurrentPath(),
FoamGetDocumentDir(),
FoamGetDocumentDirName().

FoamSetLowSoundFlag()

void FoamSetLowSoundFlag(
   word lowSndFlag);

This routine changes the sound played by UserStandardSound(). If the lowSndFlag is set to TRUE, UserStandardSound() will play one plain note no matter what StandardSoundType is passed.

Pass:

lowSndFlag
Set to TRUE to enable low sound mode. Set to FALSE to disable low sound mode (i.e., revert to standard sound mode).

Return: None.

Include: foam.goh

See Also: UserStandardSound(), FoamGetLowSoundFlag().

| Up: GEOS SDK TechDocs | Up | Prev: FoamDocSetBlockHandle() | Next: FoamStandardDialogOptr()


GEOS C Routines: FoamStandardDialogOptr()

 | Up: GEOS SDK TechDocs | Up | Prev: FoamGenerateUniqueFilename() | Next: FoamStartSoundSequence()

FoamStandardDialogOptr()

InteractionCommand FoamStandardDialogOptr(
   void                     *helpFile,
   void                     *helpContext,
   LayerPriority            layerPriority,
   word                     rejectTriggerMessage,
   optr                     rejectTriggerDestination,
   word                     acceptTriggerMessage,
   optr                     acceptTriggerDestination,
   optr                     triggerTopText,
   TCHAR                    *arg2,
   TCHAR                    *arg1,
   FoamCustomDialogBoxFlags dialogFlags,
   optr                     bodyText,
   optr                     titleText,
   optr                     titleIconBitmap);

This routine puts up a custom dialog box. It does not return until the user responds to the dialog box. The dialog is destroyed by the system when the routine returns.

Pass:

helpFile
The name of the helpfile to associate with this dialog. The helpfile must be in the Userdata\Help directory on the device.
helpContext
The context within help file to associate with this dialog.
layerPriority
Custom layer priority to use. Set to zero to give standard layer priority to the dialog.
rejectTriggerMessage
This parameter is not used.
rejectTriggerDestination
This parameter is not used.
acceptTriggerMessage
This parameter is not used.
acceptTriggerDestination
This parameter is not used.
triggerTopText
This text will be used in the standard affirmative response trigger instead of the default text. If triggerTopText is NULL the default text will be used. Setting triggerTopText is only valid for GIT_PROPERTIES and GIT_NOTIFICATION type dialogs. Note that the trigger will still return IC_APPLY for GIT_PROPERTIES and IC_OK or GIT_NOTIFICAITON.
arg2
Second string argument. Each '\002' (0x02) in the bodyText string will be replaced by *arg2.
arg1
First string argument. Each '\001' (0x01) in the bodyText string will be replaced by *arg1.
dialogFlags
Defines various functionality of the dialog box. Here are the valid flags.
  • FCDBF_SYSTEM_MODAL: Set if the dialog box should be modal.
  • FCDBF_DIALOG_TYPE: One of the CustomDialogTypes. This allows you to define the default title icon and text of the dialog as a CDT_QUESTION, CDT_NOTIFICATION, CDT_WARNING, or CDT_ERROR. The offset of FCDBF_DIALOG_TYPE is defined as FCDBF_DIALOG_TYPE_OFFSET. This allows you to easily set the dialog type like this:
             (customDialogType << FCDBF_DIALOG_TYPE_OFFSET)
  • FCDBF_INTERACTION_TYPE: Defines the standard response trigger to give to this dialog. The following GenInteractionTypes may be used: GIT_PROPERTIES, GIT_PROGRESS, GIT_COMMAND, GIT_NOTIFICATION, or GIT_AFFIRMATION. Example of setting the FCDBF_INTERACTION_TYPE flag:
             (genIntType << FCDBF_INTERACTION_TYPE_OFFSET)
  • FCDBF_DESTRUCTIVE_ACTION: This flag signals that the affirmative response trigger (IC_YES) cannot have the default focus. Usually, this is used for dialogs that have some destructive action on an affirmative response.
bodyText
An optr to a null-terminated string containing the body of the main text in the dialog. This optr cannot be NULL; it must reference an actual block of text.
titleText
An optr to a null-terminated string containing the text for the title bar. If titleText is NULL, the system will choose a standard text title depending on the CustomDialogType specified in the FCDBF_DIALOG_TYPE field of the dialogFlags parameter.
titleIconBitmap
An optr to a Bitmap struct (see graphics.h) that contains the icon for the dialogs' title bar. If titleIconBitmap is NULL, then the system will choose a standard dialog icon depending on the CustomDialogType specified in FCDBF_DIALOG_TYPE field of the dialogFlags parameter.

Return: The response trigger chosen by the user. This value is in the form of an InteractionCommand corresponding to that trigger.

Warning: This routine may resize LMem- and/or object blocks, moving them on the heap and invalidating pointers to those blocks.

Include: foam.goh

See Also:
FoamStandardDialogOptrNoBlock(),
FoamDisplayDeleteWarning(),
FoamDisplayDeleteWarningNoBlock(),
FoamDisplayError(),
FoamDisplayErrorNoBlock(),
FoamDisplayNote(),
FoamDisplayNoteNoBlock(),
FoamDisplayOkCancelWarning(),
FoamDisplayQuestion(),
FoamDisplayWarning(),
FoamDisplayWarningNoBlock().

FoamStandardDialogOptrNoBlock()

optr FoamStandardDialogOptrNoBlock(
   void                     *helpFile,
   void                     *helpContext,
   LayerPriority            layerPriority,
   word                     rejectTriggerMessage,
   optr                     rejectTriggerDestination,
   word                     acceptTriggerMessage,
   optr                     acceptTriggerDestination,
   optr                     triggerTopText,
   TCHAR                    *arg2,
   TCHAR                    *arg1,
   FoamCustomDialogBoxFlags dialogFlags,
   optr                     bodyText,
   optr                     titleText,
   optr                     titleIconBitmap);

This routine puts up a custom dialog box and returns so the thread is not blocked. The dialog is destroyed by the system after the user responds to it.

Pass:

helpFile
The name of the helpfile to associate with this dialog. The helpfile must be in the Userdata\Help directory on the device.
helpContext
The context within help file to associate with this dialog.
layerPriority
Custom layer priority to use. Set to zero to give standard layer priority to the dialog.
rejectTriggerMessage
Extra message to send when a "reject" trigger (IC_DISMISS, IC_STOP, IC_NO) is pressed. Make sure to set rejectTriggerDestination to a valid object.
rejectTriggerDestination
Optr of object to send rejectTriggerMessage to when a "reject" trigger is pressed. Make sure to set rejectTriggerMessage to a valid Message.
acceptTriggerMessage
Extra message to send when an "accept" trigger (IC_OK, IC_YES, IC_APPLY) is pressed. Make sure to set acceptTriggerDestination to a valid object.
acceptTriggerDestination
Optr of object to send acceptTriggerMessage to when an "accept" trigger is pressed. Make sure to set acceptTriggerMessage to a valid Message.
triggerTopText
This text will be used in the standard affirmative response trigger instead of the default text. If triggerTopText is NULL the default text will be used. Setting triggerTopText is only valid for GIT_PROPERTIES and GIT_NOTIFICATION type dialogs.
arg2
Second string argument. Each '\002' (0x02) in the bodyText string will be replaced by *arg2.
arg1
First string argument. Each '\001' (0x01) in the bodyText string will be replaced by *arg1.
dialogFlags
Defines various functionality of the dialog box. Here are the valid flags.
  • FCDBF_SYSTEM_MODAL: Set if the dialog box should be modal.
  • FCDBF_DIALOG_TYPE: One of the CustomDialogTypes. This allows you to define the default title icon and text of the dialog as a CDT_QUESTION, CDT_NOTIFICATION, CDT_WARNING, or CDT_ERROR. The offset of FCDBF_DIALOG_TYPE is defined as FCDBF_DIALOG_TYPE_OFFSET. This allows you to easily set the dialog type like this:
             (customDialogType << FCDBF_DIALOG_TYPE_OFFSET)
  • FCDBF_INTERACTION_TYPE: Defines the standard response trigger to give to this dialog. The following GenInteractionTypes may be used: GIT_PROPERTIES, GIT_PROGRESS, GIT_COMMAND, GIT_NOTIFICATION, or GIT_AFFIRMATION. Example of setting the FCDBF_INTERACTION_TYPE flag:
             (genIntType << FCDBF_INTERACTION_TYPE_OFFSET)
  • FCDBF_DESTRUCTIVE_ACTION: This flag signals that the affirmative response trigger (IC_YES) cannot have the default focus. Usually, this is used for dialogs that have some destructive action on an affirmative response.
bodyText
An optr to a null-terminated string containing the body of the main text in the dialog. This optr cannot be NULL; it must be a valid optr.
titleText
An optr to a null-terminated string containing the text for the title bar. If titleText is NULL, the system will choose a standard text title depending on the CustomDialogType specified in the FCDBF_DIALOG_TYPE field of the dialogFlags parameter.
titleIconBitmap
An optr to a Bitmap struct (see graphics.h) that contains the icon for the dialogs' title bar. If titleIconBitmap is NULL, then the system will choose a standard dialog icon depending on the CustomDialogType specified in FCDBF_DIALOG_TYPE field of the dialogFlags parameter.

Return: The optr to the dialog box. This optr is only valid while the dialog box is up. Once the user responds (by hitting a response trigger), the dialog is destroyed, making the optr invalid.

Warning: This routine may resize LMem- and/or object blocks, moving them on the heap and invalidating pointers to those blocks.

Include: foam.goh

See Also:
FoamStandardDialogOptr(),
FoamDisplayDeleteWarning(),
FoamDisplayDeleteWarningNoBlock(),
FoamDisplayError(),
FoamDisplayErrorNoBlock(),
FoamDisplayNote(),
FoamDisplayNoteNoBlock(),
FoamDisplayOkCancelWarning(),
FoamDisplayQuestion(),
FoamDisplayWarning(),
FoamDisplayWarningNoBlock().

| Up: GEOS SDK TechDocs | Up | Prev: FoamGenerateUniqueFilename() | Next: FoamStartSoundSequence()


GEOS C Routines: FoamStartSoundSequence()

 | Up: GEOS SDK TechDocs | Up | Prev: FoamStandardDialogOptr() ... | Next: FormatIDFromManufacturerAndType() ...

FoamStartSoundSequence()

void FoamStartSoundSequence(
   StandardSoundType sound,
   word              duration,
   word              interval);

This routine is typically used to play an alarm type sound. It starts a sequence of sound that plays every interval ticks for duration ticks. If a sequence is currently sounding, the routine will not play your sequence; it will continue playing the current sequence.

Pass:

sound
The standard sound type to play. FOAM_FCLOCK_ALARM_SOUND may be used to play the clock alarm.
duration
The number of ticks at which to stop sounding the alarm.
interval
The number of ticks to pause between playing the sound.

Return: None.

Warning:

  • If another alarm is already playing, your alarm will not sound.
  • If low sound mode has been set, your alarm will play the low level sound instead of the StandardSoundType.
  • If you call FoamStartSoundSequence() and bring up a dialog, you should intercept MSG_META_KBD_CHAR for the dialog and call FoamStopSoundSequence() unless you use FoamStandardDialogOptr(), FoamStandardDialogOptrNoBlock(), or StandardDialogClass.

Include: foam.goh

See Also: FoamStopSoundSequence().

FoamStopSoundSequence()

void FoamStopSoundSequence();

Stop any alarm that was started by FoamStartSoundSequence().

Pass: None.

Return: None.

Include: foam.goh

See Also: FoamStartSoundSequence().

FoamUTOffset()

signed int FoamUTOffset();

Return the difference between the current home city (in the .ini file) and GMT (or universal time).

Pass: None.

Return: The number of minutes from GMT.

Include: foam.goh

See Also: None.

FoamWarnForSaving()

FoamSaveAction FoamWarnForSaving(
   dword  space );

Puts up a dialog box to warn the user of low disk space, but only if the disk space is approaching full. This routine should be used by any editable view (e.g., Contact Card, Notes editor).

Pass:

space
Disk space required, in bytes.

Return: The action you should take. Possible values are:

  • FSA_SAVE_DATA
  • FSA_THROW_AWAY_CHANGES
  • FSA_DO_NOTHING

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored segment pointers to them.

Include: foam.goh

See Also:
FoamWarnIfNotEnoughSpace(),
FoamWarnIfOutOfSpace(),
FoamWarnSpaceAfterOperation().

FoamWarnIfNotEnoughSpace()

FoamDiskSpaceStatus FoamWarnIfNotEnoughSpace(
   dword  space );

Puts up the appropiate dialogs depending on the disk space available. Should be called before performing a specific operation that may consume disk space. (e.g., receiving a fax, receiving mail, reading/fetching mail, downloading WWW files, printing).

Pass:

space
Disk space required, in bytes.

Return: How close to full the disk space will be. Possible values are:

  • FDSS_NOT_FULL (disk space > 50K)
  • FDSS_NEARLY_FULL (25K < disk space < 50K)
  • FDSS_FULL (disk space < 25K)

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored segment pointers to them.

Include: foam.goh

See Also:
FoamWarnForSaving(),
FoamWarnIfOutOfSpace(),
FoamWarnSpaceAfterOperation().

FoamWarnIfOutOfSpace()

Boolean FoamWarnIfOutOfSpace();

Puts up a warning dialog box depending on how much free disk space is available. This routine is used mainly by the system to check space after system booted/cover opened/system unlocked. This routine calls FoamCheckIfOutOfSpace() to determine the amount of space left.

Pass: None.

Return: FALSE if space is okay, TRUE if out of space.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored segment pointers to them.

Include: foam.goh

See Also:
FoamWarnForSaving(),
FoamWarnIfNotEnoughSpace(),
FoamWarnSpaceAfterOperation().

FoamWarnSpaceAfterOperation()

FoamDiskSpaceStatus FoamWarnSpaceAfterOperation();

Returns the available disk space status level. This should be called after executing an operation that has consumed disk space.

Pass: None.

Return: Status indicating the amount of available disk space. Possible values are:

  • FDSS_NOT_FULL (disk space > 50K)
  • FDSS_NEARLY_FULL (25K < disk space < 50K)
  • FDSS_FULL (disk space < 25K)

Include: foam.goh

See Also:
FoamWarnForSaving(),
FoamWarnIfNotEnoughSpace(),
FoamWarnIfOutOfSpace().

| Up: GEOS SDK TechDocs | Up | Prev: FoamStandardDialogOptr() ... | Next: FormatIDFromManufacturerAndType() ...


GEOS C Routines: FormatIDFromManufacturerAndType() ...

| Up: GEOS SDK TechDocs | Up | Prev: FoamStartSoundSequence() ... | Next: GCNListAdd() ...

FormatIDFromManufacturerAndType

dword	FormatIDFromManufacturerAndType(mfr, type);
        ManufacturerIDs 			mfr;
        ClipboardItemFormat 			type;

This macro takes a manufacturer ID and a format type (e.g. CIF_TEXT) and combines them into a dword argument of the type ClipboardItemFormatID .

free()

void 	free(
        void * blockPtr);				/* address of memory to free */

The malloc() family of routines is provided for Standard C compatibility. The kernel will allocate a fixed block to satisfy the geode's malloc() requests; it will allocate memory from this block. When the block is filled, it will allocate another fixed malloc-block. When all the memory in the block is freed, the memory manager will automatically free the block.

When a geode is finished with some memory it requested from malloc() , it should free the memory. That makes it easier for malloc() to satisfy memory request. It can free the memory by passing the address which was returned by malloc() (or calloc() or realloc() ) when the memory was allocated. All of the memory will be freed.

The memory must be in a malloc-block assigned to the geode calling free() . If you want to free memory in another geode's malloc-block, call GeoFree() .

Include: stdlib.h

Warnings: Pass exactly the same address as the one returned to you when you allocated the memory. If you pass a different address, free() will take unpredictable actions, including possibly erasing other memory or crashing the system.

See Also: calloc(),
malloc(),
GeoFree(),
realloc().

FractionOf()

word	FractionOf(
        WWFixedAsDWord		wwf);

This macro returns the fractional portion of a WWFixedAsDWord value.

Include: geos.h

| Up: GEOS SDK TechDocs | Up | Prev: FoamStartSoundSequence() ... | Next: GCNListAdd() ...


GEOS C Routines: GCNListAdd() ...

| Up: GEOS SDK TechDocs | Up | Prev: FloatFloatToAscii_StdFormat() ... | Next: GCNListSend() ...

GCNListAdd()

Boolean	GCNListAdd(
        optr		OD,				/* optr to add to list */
        ManufacturerID		manufID,				/* manufacturer ID of list */
        word		listType);				/* list type */

This routine adds an object pointer (optr) to a GCN list interested in a particular change. The routine must be passed the optr to add, along with the manufID and the type of the list to add it to. If no list of the specified manufacturer and type currently exists, a new list will be created.

This routine will return true if the optr was successfully added to the GCN list and false if the optr could not be added. An optr cannot be added to a GCN list if it currently exists on that list.

Include: gcnlist.goh

GCNListAddHandles()

Boolean	GCNListAddHandles(
        MemHandle		mh,				/* handle of object to add */
        ChunkHandle		ch,				/* chunk of object to add */
        ManufacturerIDs		manufID,				/* manufacturer ID of list */
        word		listType);				/* list type */

This routine is exactly the same as GCNListAdd() , except it takes the memory and chunk handles of the object rather than a complete optr.

Include: gcnlist.goh

GCNListAddToBlock()

Boolean	GCNListAddToBlock(
        optr		OD,				/* optr of list to add */
        ManufacturerID		manufID,				/* manufacturer ID of list */
        word		listType,				/* list type */
        MemHandle		mh,				/* handle of block holding list */
        ChunkHandle		listOfLists);				/* chunk of list of lists
                             * in block */

This routine adds a new GCN list to a block containing the GCN lists. Pass it the optr of the chunk containing the new GCN list as well as the list's type and manufacturer ID. Pass also the memory handle and chunk handle of the chunk containing the GCN "list of lists" which will manage the new list.

This routine returns true of the new optr is added to the GCN mechanism, false if it could not be added (if it was already there).

Warnings: This routine may resize chunks in the block, so you should dereference any pointers after calling this routine.

Include: gcnlist.goh

GCNListCreateBlock()

ChunkHandle GCNListCreateBlock(
        MemHandle mh);			/* handle of the locked LMem block */

This routine creates a list of lists for the GCN mechanism. It is rarely, if ever, called by applications. Pass it the handle of the locked LMem block in which the list should be created.

Include: gcnlist.goh

GCNListDestroyBlock()

void	GCNListDestroyBlock(
        MemHandle		mh,					/* handle of locked block to
                             * be destroyed */
        ChunkHandle		listOfLists);					/* chunk of list of lists */

This routine destroys a GCN list of lists and all the GCN lists associated with it. Pass it the handle of the locked LMem block containing the lists as well as the chunk handle of the chunk containing the list of lists.

Include: gcnlist.goh

GCNListDestroyList()

void	GCNListDestroyList(
        optr	list);		/* optr of the GCN list to be destroyed */

This routine destroys the specified GCN list.

Include: gcnlist.goh

GCNListRelocateBlock()

void	GCNListRelocateBlock(
        MemHandle		mh,					/* handle of locked LMem block
                             * containing GCN lists */
        ChunkHandle		listOfLists,					/* chunk of list of lists */
        MemHandle		relocBlock);					/* handle of block containing
                             * relocation information */

This routine relocates the GCN list of lists in the specified block, updating all the optrs stored therein.

Warnings: This routine can resize and/or move the LMem block, so you should dereference pointers after calling it.

Include: gcnlist.goh

GCNListRemove()

Boolean	GCNListRemove(
        optr		OD,				/* the optr to be removed */
        ManufacturerID		manufID,				/* manufacturer ID of the list */
        word		listType);				/* list type */

This routine removes the passed optr from the specified GCN list. The routine must be passed the optr to remove along with the manufacturer ID and list type of the list to remove it from.

This routine will return true if the optr was successfully removed from the GCN list and false if the optr could not be found on the GCN list and therefore could not be removed.

Include: gcnlist.goh

GCNListRemoveFromBlock()

Boolean	GCNListRemoveFromBlock(
        optr		OD,				/* optr of GCN list to remove */
        ManufacturerID		manufID,				/* manufacturer of list to remove */
        word		listType,				/* type of list being removed */
        MemHandle		mh,				/* handle of locked LMem block
                         * containing the list of lists */
        ChunkHandle		listOfLists);				/* chunk of list of lists */

This routine removes a GCN list from a GCN list block and from the list of lists therein.

Include: gcnlist.goh

GCNListRemoveHandles()

Boolean	GCNListRemoveHandles(
        MemHandle		mh,
        ChunkHandle		ch,
        ManufacturerID		manufID,
        word		listType);

This routine is exactly the same as GCNListRemove() , except it specifies the object to be removed via handles rather than an optr.

Include: gcnlist.goh

See Also: GCNListRemove().

| Up: GEOS SDK TechDocs | Up | Prev: FloatFloatToAscii_StdFormat() ... | Next: GCNListSend() ...


GEOS C Routines: GCNListSend() ...

| Up: GEOS SDK TechDocs | Up | Prev: GCNListAdd() ... | Next: GenCopyChunk() ...

GCNListSend()

word	GCNListSend(
        ManufacturerID		manufID,						/* manufacturer of list */
        word		listType,						/* notification type */
        EventHandle		event,						/* event to be sent to list */
        MemHandle		dataBlock,						/* data block, if any */
        word		gcnListSendFlags);						/* GCNListSendFlags */

This routine sends a message to all objects in the specified GCN list. The message is specified in event , and the list is specified in manufID and listType . The message will be sent asynchronously (some time after the change has occurred) by the message queue.

The dataBlock parameter contains the memory handle of an extra data block to be sent with the notification, if any; this block should also be specified in the classed event. If no data block is required, pass a NullHandle. If a data block with a reference cound is used, increment the reference count by one before calling this routine; this routine decrements the count and frees the block if the count reaches zero.

The gcnListSendFlags parameter is of type GCNListSendFlags , which has only one meaningful flag for this routine:

GCNLSF_SET_STATUS
Causes the message sent to the GCN list to be set as the lists "status." The list's status message is then sent to any object adding itself to the list at a later time. If this flag is set, the event handle in event will be returned by the routine. If this flag is not set, the return value will be the number of messages sent out.

Include: gcnlist.goh

GCNListSendToBlock()

word	GCNListSendToBlock(
        ManufacturerID		manufID,				/* manufacturer id of list */
        word		listType,				/* notification type */
        EventHandle		event,				/* event to be sent to list */
        MemHandle		dataBlock,				/* data block, if any */
        MemHandle		mh,				/* handle of locked LMem block
                         * containing GCN list of lists */
        ChunkHandle		listOfLists,				/* chunk of list of lists */
        GCNListSendFlags		flags);				/* GCNListSendFlags */

This routine sends the specified event to the specified list, just as GCNListSend() . GCNListSendToBlock() , however, specifies a particular instance of the GCN list by specifying the appropriate list of lists in mh and listOfLists . Other parameters and return values are identical to GCNListSend() .

See Also: GCNListSend().

Include: gcnlist.goh

GCNListSendToList()

void	GCNListSendToList(
        optr		list,				/* optr of GCN list */
        EventHandle		event,				/* event to send to list */
        MemHandle		dataBlock,				/* handle of data block, if any */
        GCNListSendFlags		flags);				/* GCNListSendFlags */

This routine sends the specified event to the specified GCN list . The list is specified explicitly by optr as opposed to by manufacturer ID and type. The event will be sent via the proper queues to all objects registered on the list. After the notification is handled by all notified objects, the event will be freed, as will the data block passed. (If no data block, pass NullHandle in dataBlock )

The flags parameter can have one flag, GCNLSF_SET_STATUS. If this flag is set, the event passed will be set as the list's status message.

Include: gcnlist.goh

See Also: GCNListSend().

GCNListSendToListHandles()

void	GCNListSendToListHandles(
        MemHandle		mh,				/* handle of list's block */
        ChunkHandle		ch,				/* chunk of list */
        EventHandle		event,				/* event to send to list */
        MemHandle		dataBlock,				/* handle of data block, if any */
        GCNListSendFlags		flags);				/* GCNListSendFlags */

This routine is exactly the same as GCNListSendToList() ; the list is specified not by optr, however, but by a combination of its global and chunk handles.

See Also: GCNListSendToList().

Include: gcnlist.goh

GCNListUnRelocateBlock()

Boolean	GCNListUnRelocateBlock(
        MemHandle		mh,				/* handle of the locked lmem block
                         * containing the list of lists */
        ChunkHandle		listOfLists,				/* chunk of the list of lists */
        MemHandle		relocBlock);				/* handle of block containing
                         * relocation/unrelocation info */

This routine unrelocates the specified list of lists, updating all the optrs according to the information in relocBlock . This routine is rarely, if ever, used by applications; it is used primarily by the UI when shutting down to a state file.

It returns true if the specified list of lists has no lists saved to state and therefore is simply destroyed. The return value is false if the list of lists is saved to the state file normally.

Include: gcnlist.goh

| Up: GEOS SDK TechDocs | Up | Prev: GCNListAdd() ... | Next: GenCopyChunk() ...


GEOS C Routines: GenCopyChunk() ...

| Up: GEOS SDK TechDocs | Up | Prev: GCNListSend() ... | Next: GeodeAllocQueue() ...

GenCopyChunk()

word	GenCopyChunk(
        MemHandle		destBlock,			/* handle of locked LMem block into
                     * which chunk will be copied */
        MemHandle		blk,			/* handle of locked source LMem block */
        ChunkHandle		chnk,			/* chunk handle of chunk to be copied */
        word		flags);			/* CompChildFlags */

This is a utility routine that copies one LMem chunk into a newly created chunk. The routine will allocate the new chunk in the block passed in destBlock and will return the chunk handle of the new chunk. It is used primarily by the UI to duplicate generic object chunks.

The source chunk is specified by the global handle blk and the chunk handle chnk . The flags parameter contains a record of CompChildFlags , of which only the CCF_MARK_DIRTY flag is meaningful. If this flag is set, the new chunk will be marked dirty.

Warnings: This routine may resize and/or move chunks and blocks, so you must dereference pointers after calling it.

Include: genC.goh

GenFindObjectInTree()

optr	GenFindObjectInTree(
        optr	startObject,		/* optr of object at which to start search */
        dword	childTable);		/* pointer to table of bytes, each indicating
             * the position of the child at the given
             * level; -1 is the end of the table */

This utility routine finds the object having the optr startObject in the generic tree. Applications will not likely need this routine.

The childTable parameter points to a table of bytes, each byte representing the child number to be found at each level. The first byte indicates the child of startObject to get; the second byte indicates the child to get at the next level; the third byte indicates the child to get at the next level, and so on. A byte of -1 indicates the end of the table. The object found will be returned.

Include: genC.goh

GenInsertChild()

void	GenInsertChild(
        MemHandle		mh,					/* handle of parent */
        ChunkHandle		chnk,					/* chunk of parent */
        optr		childToAdd,					/* optr of new child */
        optr		referenceChild,					/* optr of reference child */
        word		flags);					/* CompChildFlags */

This utility routine adds a child object to a composite object. It is used almost exclusively by the UI for generic objects--applications will typically use MSG_GEN_ADD_CHILD .

See Also: MSG_GEN_ADD_CHILD.

Warnings: This routine may move or resize chunks and/or object blocks; therefore, you must dereference pointers after calling it.

Include: genC.goh

GenProcessAction()

This is an Esp-only routine. In most cases, Goc programmers will use MSG_GEN_OUTPUT_ACTION instead.

GenProcessGenAttrsAfterAction()

void	GenProcessGenAttrsAfterAction(
        MemHandle		mh,		/* handle of object calling the routine */
        ChunkHandle		chnk);		/* chunk of object calling the routine */

This utility routine processes various attributes for a generic object after the object's action message has been sent. It is used almost exclusively by the generic UI after MSG_GEN_OUTPUT_ACTION or GenProcessAction() .

Warnings: This routine may move or resize chunks and/or object blocks; therefore, you must dereference pointers after calling it.

Include: genC.goh

GenProcessGenAttrsBeforeAction()

void	GenProcessGenAttrsBeforeAction(
        MemHandle		mh,		/* handle of object calling the routine */
        ChunkHandle		chnk);		/* chunk of object calling the routine */

This utility routine processes various attributes for a generic object before the object's action message has been sent. It is used almost exclusively by the generic UI before MSG_GEN_OUTPUT_ACTION or GenProcessAction() .

Warnings: This routine may move or resize chunks and/or object blocks; therefore, you must dereference pointers after calling it.

Include: genC.goh

GenProcessUndoGetFile()

VMFileHandle GenProcessUndoGetFile();

This routine returns the handle of the file that holds the process' undo information.

Include: Objects/gProcC.goh

GenProcessUndoCheckIfIgnoring()

Boolean GenProcessUndoCheckIfIgnoring();

This routine returns true if the process is currently ignoring actions.

Include: Objects/gProcC.goh

GenRemoveDownwardLink()

void	GenRemoveDownwardLink(
        MemHandle		mh,			/* handle of calling object */
        ChunkHandle		chnk,			/* chunk of calling object */
        word		flags);			/* CompChildFlags */

This utility routine removes a child from the generic tree, preserving the child's upward link and usability flags. It is called primarily by the generic UI and is rarely used by applications. The flags parameter specifies whether the object linkage should be marked dirty by passing the CCF_MARK_DIRTY flag.

Warnings: This routine may move or resize chunks and/or object blocks; therefore, you must dereference pointers after calling it.

Include: genC.goh

GenSetUpwardLink()

void	GenSetUpwardLink(
        MemHandle		mh,			/* handle of calling object */
        ChunkHandle		chnk,			/* chunk of calling object */
        optr		parent);			/* optr of calling object's parent */

This utility routine converts the child/parent link to an upward-only link. Pass the handle and chunk of the locked child object and the optr of the parent composite.

Include: genC.goh

| Up: GEOS SDK TechDocs | Up | Prev: GCNListSend() ... | Next: GeodeAllocQueue() ...


GEOS C Routines: GeodeAllocQueue() ...

| Up: GEOS SDK TechDocs | Up | Prev: GenCopyChunk() ... | Next: GeodeGetAppObject() ...

GeodeAllocQueue()

QueueHandle GeodeAllocQueue();

This routine allocates an event queue which can then be attached to a thread with ThreadAttachToQueue() . It returns the queue's handle if one is allocated; it will return zero otherwise. This routine is used outside the kernel only in exceptional circumstances.

Be Sure To: You must free the queue when you are done with it; use GeodeFreeQueue() .

Include: geode.h

GeodeDuplicateResource()

MemHandle GeodeDuplicateResource(
        MemHandle mh);		/* handle of geode resource to duplicate */

This routine reads a resource from a geode into a newly-allocated block (allocated by this routine). Any relocations on the resource to itself are adjusted to be the duplicated block. The handle of the duplicated block is returned.

Include: resource.h

GeodeFind()

GeodeHandle GeodeFind(
        const char	* name,			/* geode's permanent name */
        word		numChars,		/* number of characters to match:
                          * 8 for name, 12 for name.ext */
    GeodeAttrs	attrMatch,		/* GeodeAttrs that must be set */
        GeodeAttrs	attrNoMatch);		/* GeodeAttrs that must be off */

This routine finds a geode given its permanent name, returning the geode handle if found. If the geode can not be found, a null handle will be returned. Pass it the following:

name
A pointer to the null-terminated permanent name of the geode.
numChars
The number of characters to match before returning. Pass GEODE_NAME_SIZE to match the permanent name, (GEODE_NAME_SIZE + GEODE_NAME_EXT_SIZE) to match the name and extension.
attrMatch
A record of GeodeAttrs the subject geode must have set for a positive match.
attrNoMatch
A record of GeodeAttrs the subject geode must have cleared for a positive match.

Include: geode.h

GeodeFindResource()

word	GeodeFindResource(
        FileHandle	file,			/* geode's executable file */
        word		resNum,			/* resource number to find */
        word		resOffset,		/* offset to resource */
        dword		* base);		/* pointer to second return value */

This routine locates a resource within a geode's executable ( .geo ) file. It returns the size of the resource as well as the base position of the first byte of the resource in the file (pointed to by base ). Pass the following:

file
The file handle of the geode's executable file.
resNum
The number of the resource to be found.
resOffset
The offset within the resource at which to position the file's read/write position.
base
A pointer to a dword value to be filled in by the routine. This value will be the base offset from the beginning of the file to the first byte of the resource.

Structures: A geode's executable file is laid out as shown below.

0:	Geode file header
1:	Imported Library Table
2:	Exported Routine Table
3:	Resource Size Table
4:	Resource Position Table
5:	Relocation Table Size Table
6:	Allocation Flags Table
7+:	application resources

Include: geode.h

GeodeFlushQueue()

void	GeodeFlushQueue(
        QueueHandle	source,			/* source queue to flush */
        QueueHandle	dest,			/* queue to hold flushed events */
        optr		obj			/* object to handle flushed events */
        MessageFlags	flags);			/* MF_INSERT_AT_FRONT or zero */

This routine flushes all events from one event queue into another, synchronously. Pass it the following:

source
The queue handle of the source queue (the one to be emptied).
dest
The queue handle of the destination queue that will receive the flushed events.
obj
The object that will handle flushed events that were destined for the process owning the source queue. If the process owning the destination queue should be used, pass the destination queue handle in the handle portion of the optr and a null chunk handle.
flags
A record of MessageFlags . The only meaningful flag for this routine is MF_INSERT_AT_FRONT, which should be set to flush source queue's events to the front of the destination queue. If this flag is not passed, events will be appended to the queue.

Include: geode.h

GeodeFreeDriver()

void	GeodeFreeDriver(
        GeodeHandle gh);			/* handle of the driver */

This routine frees a driver geode that had been loaded with GeodeUseDriver() . Pass it the geode handle of the driver as returned by that routine.

Include: driver.h

GeodeFreeLibrary()

void	GeodeFreeLibrary(
        GeodeHandle gh);			/* handle of the library */

This routine frees a library geode that had been loaded with GeodeUseLibrary() . Pass it the geode handle of the library.

Include: library.h

GeodeFreeQueue()

void	GeodeFreeQueue(
        QueueHandle qh);			/* handle of queue being freed */

This routine frees an event queue allocated with GeodeAllocQueue() . Any events still on the queue will be flushed as with GeodeFlushQueue() . You must pass the handle of the queue to be freed.

Include: geode.h

| Up: GEOS SDK TechDocs | Up | Prev: GenCopyChunk() ... | Next: GeodeGetAppObject() ...


GEOS C Routines: GeodeGetAppObject() ...

| Up: GEOS SDK TechDocs | Up | Prev: GeodeAllocQueue() ... | Next: GeodeLoadDGroup ...

GeodeGetAppObject()

optr	GeodeGetAppObject(
        GeodeHandle gh);			/* handle of the application geode */

This routine returns the optr of the specified geode's GenApplication object. The geode should be an application. Pass zero to get the optr of the caller's application object.

Include: geode.h

GeodeGetCodeProcessHandle()

GeodeHandle GeodeGetCodeProcessHandle();

This routine returns the geode handle of the geode that owns the block in which the code which calls this routine resides.

Include: geode.h

GeodeGetDefaultDriver()

GeodeHandle GeodeGetDefaultDriver(
        GeodeDefaultDriverType type);		/* type of default driver to get */

This routine returns the default driver's geode handle for the type passed. The type must be one of the values of GeodeDefaultDriverType .

Include: driver.h

GeodeGetInfo()

word	GeodeGetInfo(
        GeodeHandle	  gh,			/* handle of the subject geode */
        GeodeGetInfoType  info,			/* type of information to return */
        void	 	* buf);			/* buffer to contain returned info */

This routine returns information about the specified geode. The geode must be loaded already. The meaning of the returned word depends on the value passed in info ; the GeodeGetInfoType is shown below. Pass the following:

gh
The geode handle of the geode. If gh is null then the routine will return the information of the current geode.
info
The type of information requested; this should be one of the values listed below.
buf
A pointer to a locked or fixed buffer which will contain returned information for various types requested.

GeodeGetInfoType has the following enumerations (only one may be requested at a time):

GGIT_ATTRIBUTES
Get the geode's attributes. The return value will be a record of GeodeAttrs corresponding to those attributes set for the geode. Pass a null buffer pointer.
GGIT_TYPE
Get the type of the geode. The returned value will be a value of GeosFileType indicating the type of file storing the geode. Pass a null buffer pointer.
GGIT_GEODE_RELEASE
Get the release number of the geode. The returned word will be the size of the buffer pointed to by buf , and the buffer will contain the ReleaseNumber structure of the geode.
GGIT_GEODE_PROTOCOL
Get the protocol level of the geode. The returned word will be the size of the buffer pointed to by buf , and the buffer will contain the ProtocolNumber structure of the geode.
GGIT_TOKEN_ID
Get the token identifier of the geode. The returned word will be the size of the buffer pointed to by buf , and the buffer will contain a GeodeToken structure containing the token characters and token ID of the geode's token.
GGIT_PERM_NAME_AND_EXT
Get the permanent name of the geode, with the extension characters. The returned word will be the size of the buffer pointed to by buf , and the buffer will contain a null-terminated character string representing the geode's permanent name (as set in its geode parameters file). Note that the buffer must be at least 13 bytes.
GGIT_PERM_NAME_ONLY
Get the permanent name of the geode without the extension characters. The returned word will be the size of the buffer pointed to by buf, and the buffer will contain the null-terminated character string representing the geode's permanent name. The buffer must be at least nine bytes.

Include: geode.h

GeodeGetOptrNS()

optr	GeodeGetOptrNS(
        optr	obj);

This routine unrelocates an optr, changing the virtual-segment handle to an actual global handle.

Include: resource.h

GeodeGetProcessHandle()

GeodeHandle GeodeGetProcessHandle();

This routine returns the geode handle of the current executing process (i.e. the owner of the current running thread). Use it when you need to pass your application's geode handle or Process object's handle to a routine or message.

Include: geode.h

GeodeGetUIData()

word	GeodeGetUIData(
        GeodeHandle		gh);

Include: geode.h

GeodeInfoDriver()

DriverInfoStruct  * GeodeInfoDriver(
        GeodeHandle gh);	 /* handle of the driver to get information about */

This routine returns information about the specified driver geode. Pass the geode handle of the driver as returned by GeodeUseDriver() . It returns a pointer to a DriverInfoStruct structure, shown below.

typedef struct {
    void			(*DIS_strategy)();
    DriverAttrs			DIS_driverAttributes;
    DriverType			DIS_driverType;
} DriverInfoStruct;

For full information on this structure, see the DriverInfoStruct reference entry.

Include: driver.h

GeodeInfoQueue()

word	GeodeInfoQueue(
        QueueHandle qh);			/* queue to query */

This routine returns information about a specific event queue. Pass the handle of the queue; for information about the current process' queue, pass a null handle. This routine returns the number of events (or messages) currently in the queue.

Include: geode.h

GeodeLoad()

GeodeHandle GeodeLoad(
        const char *		name,				/* file name of geode */
        GeodeAttrs		attrMatch,				/* GeodeAttrs that must be set */
        GeodeAttrs		attrNoMatch,				/* GeodeAttrs that must be clear */
        word		priority,				/* priority of the loaded geode */
        dword		appInfo,				/* special load information */
        GeodeLoadError *		err);				/* returned error value */

This routine loads the specified geode from the given file and then executes the geode based on its type. It returns the geode handle of the loaded geode if successful; if unsuccessful, the returned value will be NullHandle and the err pointer will point to an error value. Pass this routine the following:

name
A pointer to the name of the geode's file. This is a null-terminated character string that represents the full path of the file (or a path relative to the current working directory).
attrMatch
A record of GeodeAttrs that must be set in the specified geode for the load to be successful.
attrNoMatch
A record of GeodeAttrs that must be cleared in the specified geode for the load to be successful. (That is, each bit which is set in attrNoMatch must be clear in the geode's GeodeAttrs field.)
priority
If the subject geode is a process, this is the priority at which its process thread will run.
appInfo
Two words of data to be passed directly to the loaded geode. For libraries and drivers, this should be a far pointer to a null-terminated string of parameters.
err
A pointer to an empty GeodeLoadError which will hold any returned error values.

Warnings: If you load a geode dynamically with GeodeLoad() , you must be sure to free it when you are done with GeodeFreeDriver() or GeodeFreeLibrary() .

Include: geode.h

See Also: UserLoadApplication().

| Up: GEOS SDK TechDocs | Up | Prev: GeodeAllocQueue() ... | Next: GeodeLoadDGroup ...


GEOS C Routines: GeodeLoadDGroup ...

| Up: GEOS SDK TechDocs | Up | Prev: GeodeGetAppObject() ... | Next: GeoFree() ...

GeodeLoadDGroup

void	GeodeLoadDGroup(
        MemHandle		mh);

This routine forces the dgroup segment into the data-segment register. This routine expects the MemHandle of the code resource, which can be retrieved with GeodeGetCodeProcessHandle() .

Include: resource.h

GeodePrivAlloc()

word	GeodePrivAlloc(
        GeodeHandle		gh,				/* handle of the owner of the
                         * newly-allocated private data */
        word		numWords);				/* number of words to allocate */

This routine allocates a string of contiguous words in all geodes' private data areas; each set of words will be owned by the geode specified in gh . The data allocated can be accessed with GeodePrivWrite() and GeodePrivRead() and must be freed with GeodePrivFree() . The return value will be the offset to the start of the allocated range, or zero if the routine could not allocate the space.

Each geode has a block of private data the is accessed using the GeodePriv...() routines. A specific geode's private data block is expanded only when a valid GeodePrivWrite() is performed for the geode. Space is "allocated" in the data blocks of all geodes (loaded or yet-to-be loaded) simultaneously via a call to GeodePrivAlloc() . Data that have never been written are returned as all zeros.

Include: geode.h

GeodePrivFree()

void	GeodePrivFree(
        word	offset,		/* offset returned by GeodePrivAlloc() */
        word	numWords);		/* number of words to free */

This routine frees a group of contiguous words from all geodes' private data areas. The space must previously have been allocated with GeodePrivAlloc() . Pass the offset to the words as returned by GeodePrivAlloc() as well as the number of words to be freed.

Include: geode.h

GeodePrivRead()

void	GeodePrivRead(
        GeodeHandle		gh,			/* handle of owner of private data */
        word		offset,			/* offset returned by
                     * GeodePrivAlloc() */
        word		numWords,			/* number of words to read */
        word		* dest);			/* pointer to buffer into which data
                     * will be copied */

This routine reads a number of words from the geode's private data area. Pass the following:

gh
The geode handle of the owner of the private data to be read.
offset
The offset to the private data as returned by GeodePrivAlloc() .
numWords
The number of words to read.
dest
A pointer to a locked or fixed buffer into which the words should be read. It must be at least numWords words long.

Include: geode.h

GeodePrivWrite()

void	GeodePrivWrite(
        GeodeHandle		gh,			/* handle of owner of private data */
        word		offset,			/* offset returned by
                         * GeodePrivAlloc() */
        word		numWords,			/* number of words to be written */
        word		* src);			/* buffer containing data */

This routine writes a number of words into a geode's private data area. The area being written must have been allocated previously with GeodePrivAlloc() . Pass the following:

gh
The geode handle of the owner of the private data space.
offset
The offset to begin writing to, as returned by GeodePrivAlloc() .
numWords
The number of words to be written. This should be no more than had been previously allocated.
src
A pointer to the locked or fixed buffer containing the data to be written.

Include: geode.h

GeodeSetDefaultDriver()

void	GeodeSetDefaultDriver(
        GeodeDefaultDriverType			type,		/* type of default driver to set */
        GeodeHandle			gh);		/* driver to set as the default */

This routine sets the default driver for the indicated driver type. Pass the type of default driver in type and the handle of the driver in gh . The type must be a value of GeodeDefaultDriverType .

Include: driver.h

GeodeSetUIData()

void	GeodeSetUIData(
        GeodeHandle		gh,
        word		data)

GeodeUseDriver()

GeodeHandle GeodeUseDriver(
        const	char	* name,				/* file name of driver to load */
        word		protoMajor,				/* expected major protocol */
        word		protoMinor,				/* expected minor protocol */
        GeodeLoadError		* err);				/* pointer to returned error */

This routine dynamically loads a driver geode given the driver's file name. It returns the geode handle of the driver if successful; if unsuccessful, it returns an error code of type GeodeLoadError pointed to by err . Pass this routine the following:

name
A pointer to the driver's null-terminated full path and file name.
protoMajor
The expected major protocol of the driver. If zero, any protocol is acceptable.
protoMinor
The expected minor protocol of the driver.
err
A pointer to a GeodeLoadError in which any error values will be returned.

Tips and Tricks: It is much easier to automatically load the drivers you need by noting them in your geode parameters file.

Be Sure To: If you use GeodeUseDriver() to dynamically load a driver, you must also use GeodeFreeDriver() to free it when you are done using it.

Include: driver.h

GeodeUseLibrary()

GeodeHandle GeodeUseLibrary(
        const char	 *	name,				/* file name of library to load */
        word		protoMajor,				/* expected major protocol */
        word		protoMinor,				/* expected minor protocol */
        GeodeLoadError *		err);				/* pointer to returned error */

This routine dynamically loads a library geode when given the library's file name. (The library must be in the thread's working directory.) It returns the geode handle of the loaded library if successful; if unsuccessful, it returns an error code ( GeodeLoadError ) pointed to by err . Pass this routine the following parameters:

name
A pointer to the library's null-terminated file name.
protoMajor
The expected major protocol of the library. If zero, any protocol is acceptable.
protoMinor
The expected minor protocol of the library.
err
A pointer to a GeodeLoadError which will contain any returned error values.

Be Sure To: If you dynamically load a library with GeodeUseLibrary() , you must manually free it when finished, with GeodeFreeLibrary() .

Include: library.h

| Up: GEOS SDK TechDocs | Up | Prev: GeodeGetAppObject() ... | Next: GeoFree() ...


GEOS C Routines: GeoFree() ...

| Up: GEOS SDK TechDocs | Up | Prev: GeodeLoadDGroup ... | Next: GrApplyTranslation() ...

GeoFree()

void	* GeoFree(
        void		* blockPtr,				/* address of memory to free */
        GeodeHandle		geodeHan);				/* owner of block to be used */

The routine malloc() can free only memory in the malloc-block belonging to the calling geode. If you want to free memory in another geode's malloc-block, call GeoFree() . Passing a null GeodeHandle will make GeoMalloc() act on memory in the calling geode's malloc-block.

Include: geode.h

Warnings: Pass exactly the same address as the one returned to you when you allocated the memory. If you pass a different address, GeoFree() will take unpredictable actions, including possibly erasing other memory or crashing the system.

See Also: free().

GeoMalloc()

void	* GeoMalloc(
        size_t		blockSize,				/* # of bytes to allocate*/
        GeodeHandle		geodeHan,				/* Owner of block to be used */
        word		zeroInit);				/* Zero-initialize memory? */

The routine malloc() automatically allocates memory in the malloc-block belonging to the calling geode. It does not zero-initialize the memory. If you want to zero-initialize the memory, or want to allocate it in another geode's malloc-block, call GeoMalloc() . Pass true (i.e., non-zero) in zeroInit to zero-initialize the memory.

Passing a null GeodeHandle will make GeoMalloc() allocate the memory in the calling geode's malloc-block. If "zeroInit" is true, the memory will be initialized to null bytes; otherwise, the memory will be left uninitialized.

Include: geode.h

Warnings: All memory allocated with malloc() is freed when GEOS shuts down.

See Also: malloc().

GeoReAlloc()

void	* GeoReAlloc(
        void		* blockPtr,				/* address of memory to resize */
        size_t		newSize,				/* New size in bytes */
        GeodeHandle		geodeHan);				/* Owner of block to be used */

The routine realloc() can resize only memory in the malloc-block belonging to the calling geode. If you want to resize memory in another geode's malloc-block, call GeoReAlloc() . Passing a null GeodeHandle will make GeoReAlloc() act on memory in the calling geode's malloc-block.

If the block is resized larger, the new memory will not be zero-initialized. Resizing a block smaller will never fail. If GeoReAlloc() fails, it will return a null pointer (zero). If you pass a newSize of zero, the passed block pointer is freed and the return pointer is a null pointer.

Include: geode.h

Warnings: Pass exactly the same address as the one returned to you when you allocated the memory. If you pass a different address, GeoReAlloc() will take unpredictable actions, including possibly erasing other memory or crashing the system.

See Also: realloc().

GrApplyRotation()

void	GrApplyRotation(
        GStateHandle		gstate,			/* GState to alter */
        WWFixedAsDWord		angle); 			/* degrees counterclockwise */

Apply a rotation to the GState's transformation matrix.

Include: graphics.h

GrApplyScale()

void	GrApplyScale(
        GStateHandle		gstate,			/* GState to alter */
        WWFixedAsDWord		xScale,			/* new x scale factor */
        WWFixedAsDWord		yScale);			/* new y scale factor */

Apply a scale factor to the GState's transformation matrix.

Include: graphics.h

GrApplyTransform()

void	GrApplyTransform(
        GStateHandle		gstate,			/* GState to draw to */
        const TransMatrix		*tm);			/* transformation matrix to apply */

Apply a transformation, expressed as a transformation matrix, to a GState's coordinate system.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GeodeLoadDGroup ... | Next: GrApplyTranslation() ...


GEOS C Routines: GrApplyTranslation() ...

| Up: GEOS SDK TechDocs | Up | Prev: GeoFree() ... | Next: GrClearBitmap() ...

GrApplyTranslation()

void	GrApplyTranslation(
        GStateHandle		gstate,			/* GState to alter */
        WWFixedAsDWord		xTrans,			/* translation in x */
        WWFixedAsDWord		yTrans);			/* translation in y */

Apply a translation to the GState.

Include: graphics.h

GrApplyTranslationDWord()

void	GrApplyTranslationDWord(
        GStateHandle		gstate,			/* GState to alter */
        sdword		xTrans,			/* extended translation in x */
        sdword		yTrans);			/* extended translation in y */

Apply a 32-bit integer extended translation to the GState.

Include: graphics.h

GrBeginPath()

void	GrBeginPath(
        GStateHandle		gstate,			/* GState to alter */
        PathCombineType		params);			/* path parameters */

Starts or alters the path associated with a GState. All graphics operations that are executed until GrEndPath() is called become part of the path.

Depending on the value of the params field, the new path may replace the old path, or may be combined with the old path by intersection or union.

Include: graphics.h

GrBeginUpdate()

void 	GrBeginUpdate(
        GStateHandle gstate);			/* GState to draw to */

Called by an application to signal that it is about to begin updating the exposed region. This routine is normally called as part of a MSG_META_EXPOSED handler. Blanks out the invalid area.

Include: win.h

GrBitBlt()

void	GrBitBlt(
        GStateHandle		gstate,				/* GState to draw to */
        sword		sourceX,				/* original x origin */
        sword		sourceY,				/* original y origin */
        sword		destX,				/* new x origin */
        sword		destY,				/* new y origin */
        word		width,				/* width of area */
        word		height,				/* height of area */
        BLTMode		mode);				/* draw mode (see below) */

Transfer a bit-boundary block of pixels between two locations in video memory. This routine is useful for animation and other applications which involve moving a drawing around the screen.

Structures:

            typedef enum /* word */ {
    BLTM_COPY, 				/* Leave source region alone */
    BLTM_MOVE, 				/* Clear & invalidate source rect */
    BLTM_CLEAR				/* Clear source rectangle */
} BLTMode;

Include: graphics.h

GrBrushPolyline()

void	GrBrushPolyline(
        GStateHandle		gstate,			/* GState to draw to */
        const Point		* points,			/* array of Point structures to draw */
        word		numPoints,			/* number of points in array */
        word		brushH,			/* brush height */
        word		brushW);			/* brush width */

Draw a brushed connected polyline. Note that this routine ignores the GState's line width, and instead uses a brush height and width, measured in pixels.

Include: graphics.h

GrCharMetrics()

dword	GrCharMetrics(
        GStatehandle		gstate,			/* GState to get metrics for */
        GCM_info		info,			/* information to return */
        word		ch);			/* character of type Chars */

Returns metric information for a single character of a font. This information is used to determine the drawing bounds for a character. To find out how wide a character is (how much space to leave for it if drawing a line of text character-by-character), use GrCharWidth() instead.

Structures:

            typedef enum {
    GCMI_MIN_X, 						/* return = value << 16 */
    GCMI_MIN_X_ROUNDED,					 	/* return = value */
    GCMI_MIN_Y, 						/* return = value << 16 */
    GCMI_MIN_Y_ROUNDED,				 		/* return = value << 16 */
    GCMI_MAX_X, 						/* return = value << 16 */
    GCMI_MAX_X_ROUNDED, 						/* return = value << 16 */
    GCMI_MAX_Y, 						/* return = value << 16 */
    GCMI_MAX_Y_ROUNDED 						/* return = value << 16 */
} GCM_Info;

See Also: GrCharWidth().

Include: font.h

GrCharWidth()

dword	GrCharWidth( /* Returns width << 16 */
        GStateHandle		gstate,			/* GState to query */
        word		ch);			/* character of type Chars */

Return the width of a single character. Note that this routine does not take into account track kerning, pairwise kerning, space padding, or other attributes that apply to multiple characters.

Include: graphics.h

GrCheckFontAvailID()

FontID 	GrCheckFontAvailID(
        FontEnumFlags 		flags,
        word 		family,
        FontID 		id);

See if font (identified by ID) exists.

Include: graphics.h

GrCheckFontAvailName()

FontID 	GrCheckFontAvailName(
        FontEnumFlags 		flags,
        word 		family,
        const char 		* name);

See if font (identified by name) exists.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GeoFree() ... | Next: GrClearBitmap() ...


GEOS C Routines: GrClearBitmap() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrApplyTranslation() ... | Next: GrDestroyBitmap() ...

GrClearBitmap()

void	GrClearBitmap(
        GStateHandle 	gstate);			/* GState to affect */

Clear out the content of a bitmap. Note that the part of the bitmap actually cleared depends on the bitmap mode. For the normal mode, the data portion of the bitmap is cleared. If the bitmap is in BM_EDIT_MASK mode, then the mask is cleared and the data portion is left alone.

Include: graphics.h

GrCloseSubPath()

void	GrCloseSubPath(
        GStateHandle gstate);			/* GState to affect */

Geometrically closes the currently open path segment. Note that you must still call GrEndPath() to end the path definition.

Include: graphics.h

GrComment()

void	GrComment(
        GStateHandle		gstate,			/* GState to affect */
        const void  		* data,			/* comment string */
        word		size);			/* Size of data, in bytes */

Write a comment out to a graphics string.

Include: graphics.h

GrCopyGString()

GSRetType GrCopyGString(
        GStateHandle		source,			/* GState from which to get GString */
        GStateHandle		dest,			/* GState to which to copy GString */
        GSControl 		flags);			/* flags for the copy */

Copy all or part of a Graphics String. The GSControl record can have the following flags:

 GSC_ONE			/* just do one element */
 GSC_MISC			/* return on MISC opcode */
 GSC_LABEL			/* return on GR_LABEL opcode */
 GSC_ESCAPE			/* return on GR_ESCAPE opcode */
 GSC_NEW_PAGE			/* return when we get to a NEW_PAGE */
 GSC_XFORM			/* return on TRANSFORMATIONopcode */
 GSC_OUTPUT:			/* return on OUTPUT opcode */
 GSC_ATTR			/* return on ATTRIBUTE opcode */
 GSC_PATH			/* return on PATH opcode */

The return value can be any one of GSRetType , a byte-size field:

GSRT_COMPLETE
GSRT_ONE
GSRT_MISC
GSRT_LABEL
GSRT_ESCAPE
GSRT_NEW_PAGE
GSRT_XFORM
GSRT_OUTPUT
GSRT_ATTR
GSRT_PATH
GSRT_FAULT

Include: gstring.h

GrCreateBitmap()

VMBlockHandle GrCreateBitmap(
        BMFormat 		initFormat,				/* color fomat of bitmap */
        word		initWidth,				/* initial width of bitmap */
        word		initHeight,				/* initial height of bitmap */
        VMFileHandle 		vmFile,				/* VM file to hold bitmap's data*/
        optr 		exposureOD,				/* optr to get MSG_META_EXPOSED */
        GStateHandle		* bmgs);				/* Draws to this GState
                             * will draw to the bitmap */

This routine allocates memory for a bitmap and creates an off-screen window in which to hold the bitmap. This routine takes the following arguments:

initFormat
The depth of the bitmap's color.
initWidth
Bitmap's width.
initHeight
Bitmap's height.
vmFile
File to hold the bitmap data; the routine will allocate a block within this file.
exposureOD Object which will receive the "exposed" message when the bitmap's window is invalidated. If this argument is zero, then no exposed message will be sent.
Remember that an off-screen window is created to house the bitmap. When this window is first created, it will be invalid, and it is conceivable that later actions could cause it to become invalid again. On these occasions, the object specified by this argument will receive a MSG_META_EXPOSED.
bmgs
The GStateHandle pointed to by this argument can start out as null; the routine will use it to return the GState by which the bitmap can be drawn to. Any graphics routines which draw to this returned GState will be carried out upon the bitmap.

The routine returns a VMBlockHandle , the handle of the block within the passed VM file which contains the bitmap's data. The block will be set up as the first block of a HugeArray. Its header area will be filled with the following:

Complex Bitmap Header
This is a CBitmap structure which contains some basic information about the bitmap.
Editing Mode
These flags can change how the bitmap is being edited.
Device Information Block
This internal structure contains information about and used by the video driver. (Don't worry that you don't know the size of this structure; remember that the CBitmap structure contains the offsets of the bitmap and palette data areas.)
Pallette Information (optional)
If the bitmap has its own pallette, this is where the palette data will be stored; it will consist of an array of 3-byte entries. Depending on how many colors the bitmap supports, there may be 16 or 256 entries in this array.

The bitmap's raw data is in the VM block, but outside of the header area.

Include: graphics.h

GrCreateGString()

GStateHandle GrCreateGString(
        Handle		han,			/* memory, stream, or VM file handle */
        GStringType		hanType,			/* type of handle in han parameter */
        word		* gsBlock);				/* returned for GST_MEMORY and
                         * GST_VMEM types only */

Open a graphics string and start redirecting graphics orders to the string. The hanType parameter must be GST_MEMORY, GST_STREAM, or GST_VMEM.

Include: gstring.h

GrCreatePalette()

word	GrCreatePalette( /* Returns # of entries in color table
            * or 0 for monochrome or 24-bit */
        GStateHandle gstate);

Create a color mapping table and associate it with the current window. Initialize the table entries to the default palette for the device.

Include: graphics.h

GrCreateState()

GStateHandle GrCreateState(
        WindowHandle win);			/* Window in which GState will be active */

Create a graphics state (GState) block containg default GState information.

If zero is passed, then the GState created will have no window associated with it.

Include: graphics.h

GrDeleteGStringElement()

void	GrDeleteGStringElement(
        GStateHandle		gstate,			/* GState containing GString */
        word		count);			/* number of elements to delete */

Delete a range of GString elements from the GString in the passed GState.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrApplyTranslation() ... | Next: GrDestroyBitmap() ...


GEOS C Routines: GrDestroyBitmap() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrClearBitmap() ... | Next: GrDrawCurve() ...

GrDestroyBitmap()

void	GrDestroyBitmap(
        GStateHandle		gstate,			/* GState containing bitmap */
        BMDestroy 		flags);			/* flags for removing data */

Free the bitmap and disassociate it with its window. Depending on the passed flag, the bitmap's data may be freed or preserved. Thus, it is possible to remove the GString used to edit the bitmap while maintaining the bitmap in a drawable state.

Structures:

typedef ByteEnum BMDestroy;
/* 	BMD_KILL_DATA,
     BMD_LEAVE_DATA */

Include: graphics.h

GrDestroyGString()

void	GrDestroyGString(
        Handle		gstring,			/* Handle of GString */
        GStateHandle		gstate,			/* NULL, or handle of another
                     * gstate to free*/
        GStringKillType		type);			/* Kill type for data removal */

Destroys a GString. Depending on the GStringKillType argument, this either constitutes removing the GState from the GString data; or freeing both the GState and the GString's data. If you have been drawing the GString to a GState, you should pass the GState's handle as gstate , and this routine will do some cleaning up.

Structures:

typedef ByteEnum GStringKillType;
/* 	GSKT_KILL_DATA,
    GSKT_LEAVE_DATA */

Include: gstring.h

GrDestroyPalette()

void	GrDestroyPalette(
        GStateHandle gstate);			/* GState of palette to destroy */

Free any custom palette associated with the current window.

Include: graphics.h

GrDestroyState()

void	GrDestroyState(
        GStateHandle gstate);			/* GState to be destroyed */

Free a graphics state block.

Include: graphics.h

GrDrawArc()

void	GrDrawArc(
        GStateHandle		gstate,				/* GState to draw to */
        sword		left,				/* bounds of box outlining arc */
        sword		top,
        sword		right,
        sword		bottom,
        word		startAngle,				/* angles in degrees
        word		endAngle,				 * counter-clockwise */
        ArcCloseType		arcType);				/* how the arc is closed */

Draw an arc along the ellipse that is specified by a bounding box, from the starting angle to the ending angle.

Include: graphics.h

GrDrawArc3Point()

void	GrDrawArc3Point(
        GStateHandle		gstate,			/* GState to draw to */
        const ThreePointArcParams 	*params);

Draw a circular arc, given three points along the arc; both endpoints and any other point on the arc.

Include: graphics.h

GrDrawArc3PointTo()

void	GrDrawArc3PointTo(
        GStateHandle		gstate,			/* GState to draw to */
        const ThreePointArcToParams *params);

As GrDrawArc3Point() , above, except that the current position is automatically used as one of the endpoints.

Include: graphics.h

GrDrawBitmap()

void	GrDrawBitmap(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x,				/* x starting point */
        sword		y,				/* y starting point */
        const	Bitmap	* bm,				/* pointer to the bitmap */
        Bitmap * _pascal (*callback) (Bitmap *bm));	/* NULL for no callback */

Draw a bitmap. Note that if the bitmap takes up a great deal of memory, it is necessary to manage its memory when drawing. If the bitmap resides in a HugeArray (true of any bitmap created using GrCreateBitmap() ), then calling GrDrawHugeBitmap() will automatically take care of memory management. Otherwise, you may wish to provide a suitable callback routine. This routine should be declared _pascal and is passed a pointer into the passed bitmap and is expected to return a pointer to the next slice. This allows the bitmap to be drawn in horizontal bands, or swaths. The callback should signal it is done returning blocks by returning a null pointer.

Include: graphics.h

GrDrawBitmapAtCP()

void	GrDrawBitmapAtCP(
        GStateHandle		gstate,			/* GState to draw to */
        const	Bitmap	* bm,				/* pointer to the bitmap */
        Bitmap * _pascal (*callback) (Bitmap *bm));	/* NULL for no callback */

This routine is the same as GrDrawBitmap() , above, except that the bitmap is drawn at the current position.

Include: graphics.h

GrDrawChar()

void	GrDrawChar(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x,			/* x position at which to draw */
        sword		y,			/* y position at which to draw */
        word		ch);			/* character of type Chars */

Draw a character at the given position with the current text drawing attributes.

Include: graphics.h

GrDrawCharAtCP()

void	GrDrawCharAtCP(
        GStateHandle		gstate,			/* GState to draw to */
        word		ch);			/* character of type Chars */

Draw a character at the current position with the current text drawing attributes.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrClearBitmap() ... | Next: GrDrawCurve() ...


GEOS C Routines: GrDrawCurve() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrDestroyBitmap() ... | Next: GrDrawLine() ...

GrDrawCurve()

void	GrDrawCurve(
        GStateHandle		gstate,				/* GState to draw to */
        const	Point	*points);				/* array of four Points */

Draw a Bezier curve.

Include: graphics.h

GrDrawCurveTo()

void	GrDrawCurveTo(
        GStateHandle		gstate,				/* GState to draw to */
        const	Point	*points);			/* array of three Points */

Draw a Bezier curve, using the current postion as the first point.

Include: graphics.h

GrDrawEllipse()

void	GrDrawEllipse(
        GStateHandle		gstate,			/* GState to draw to */
        sword		left,			/* bounding box bounds */
        sword		top,
        sword		right,
        sword		bottom);

Draw an ellipse, defined by its bounding box.

Include: graphics.h

GrDrawGString()

GSRetType GrDrawGString(
        GStateHandle		gstate,					/* GState to draw to */
        Handle		gstringToDraw,					/* GString to draw */
        sword		x,					/* point at which to draw */
        sword		y,
        GSControl 		flags,					/* GSControl record */
        GStringElement		* lastElement);					/* pointer to empty structure */

Draw a graphics string. The passed control flag allows drawing to stop upon encountering certain kinds of drawing elements. If this causes the drawing to stop in mid-string, then the routine will provide a pointer to the next GStringElement to be played.

  • You must provide a GState to draw to. You may wish to call GrSaveState() on the GState before drawing the GString (and call GrRestoreState() afterwards). If you will draw anything else to this GState after the GString, you must call GrDestroyGString() on the GString, and pass this GState's handle as the gstate argument so that GrDestroyGString() can clean up the GState.
  • You must provide a GString to draw. The GString must be properly loaded (probably by means of GrLoadGString() ).
  • You can provide a pair of coordinates at which to draw the GString. The graphics system will translate the coordinate system by these coordinates before carrying out the graphics commands stored in the GString.
  • You can provide a GSControl argument which requests that the system stop drawing the GString when it encounters a certain type of GString element. If the GString interpreter encounters one of these elements, it will immediately stop drawing. The GString will remember where it stopped drawing. If you call GrDrawGString() with that same GString, it will continue drawing where you left off.
  • You must provide a pointer to an empty GStringElement structure. GrDrawGString() will return a value here when it is finished drawing. If the GString has stopped drawing partway through due to a passed GSControl , the returned GStringElement value will tell you what sort of command was responsible for halting drawing. For instance, if you had instructed GrDrawGString() to halt on an `output' element (GrDraw...() or GrFill...() commands), then when GrDrawGString() returns, you would check the value returned to see what sort of output element was present.

Include: gstring.h

GrDrawGStringAtCP()

GSRetType GrDrawGStringAtCP(
        GStateHandle		gstate,					/* GState to draw to */
        GStringeHandle		gstringToDraw,					/* GString to draw */
        GSControl 		flags,					/* GSControl flags */
        GStringElement 		* lastElement);					/* last element to draw */

Draw a graphics string as GrDrawGString() , above, except that drawing takes place at the current position.

  • You must provide a GState to draw to. You may wish to call GrSaveState() on the GState before drawing the GString (and call GrRestoreState() afterwards). If you will draw anything else to this GState after the GString, you must call GrDestroyGString() on the GString, and pass this GState's handle as the gstate argument so that GrDestroyGString() can clean up the GState.
  • You must provide a GString to draw. The GString must be properly loaded (probably by means of GrLoadGString() ).
  • You can provide a GSControl argument which requests that the system stop drawing the GString when it encounters a certain type of GString element. If the GString interpreter encounters one of these elements, it will immediately stop drawing. The GString will remember where it stopped drawing. If you call GrDrawGString() with that same GString, it will continue drawing where you left off.
  • You must provide a pointer to an empty GStringElement structure. GrDrawGString() will return a value here when it is finished drawing. If the GString has stopped drawing partway through due to a passed GSControl , the returned GStringElement value will tell you what sort of command was responsible for halting drawing. For instance, if you had instructed GrDrawGString() to halt on an `output' element (GrDraw...() or GrFill...() commands), then when GrDrawGString() returns, you would check the value returned to see what sort of output element was present.

Include: gstring.h

GrDrawHLine()

void	GrDrawHLine(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x1,			/* first horizontal coordinate */
        sword		y,			/* vertical position of line */
        sword		x2);			/* second horizontal coordinate */

Draw a horizontal line.

Include: graphics.h

GrDrawHLineTo()

void	GrDrawHLineTo(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x);			/* ending horizontal coordinate */

Draw a horizontal line starting from the current position.

Include: graphics.h

GrDrawHugeBitmap()

void	GrDrawHugeBitmap(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x			/* Point at which to draw */
        sword		y,
        VMFileHandle 		vmFile,			/* VM File holding HugeArray */
        VMBlockHandle 		vmBlk);			/* VM block of HugeArray */

Draw a bitmap that resides in a HugeArray.

Include: graphics.h

See Also: GrDrawBitmap(),
GrDrawHugeBitmapAtCP(),
GrDrawHugeImage().

GrDrawHugeBitmapAtCP()

void	GrDrawHugeBitmapAtCP(
        GStateHandle		gstate,			/* GState to draw to */
        VMFileHandle	 	vmFile,			/* VM file containing HugeArray */
        VMBlockHandle 		vmBlk);			/* VM block containing HugeArray */

As GrDrawHugeBitmap() , above, except that the bitmap is drawn at the current position.

Include: graphics.h

See Also: GrDrawBitmapAtCP(), GrDrawHugeBitmap().

. Note that the bitmap will be

GrDrawHugeImage()

void	GrDrawHugeImage(
        GStateHandle 		gstate,			/* GState to draw to */
        sword		x			/* point at which to draw */
        sword		y,
        ImageFlags 		flags,
        VMFileHandle 		vmFile,			/* VM file holding HugeArray */
        VMBlockHandle 		vmBlk);			/* VM block holding HugeArray */

Draw a bitmap that resides in a HugeArray drawn on an assumption of one device pixel per bitmap pixel. The bitmap will not draw rotated or scaled. Depending on the value of the flags argument, the bitmap may be expanded so that a square of device pixels displays each bitmap pixel.

Include: graphics.h

See Also: GrDrawImage(), GrDrawHugeBitmapAtCP().

GrDrawImage()

void	GrDrawImage(
        GStateHandle 		gstate,			/* GState to draw to */
        sword		x			/* point at which to draw */
        sword		y,
        ImageFlags 		flags,
        const Bitmap 		* bm);			/* pointer to bitmap */

Draw a bitmap. Note that the bitmap will be drawn on an assumption of one device pixel per bitmap pixel. The bitmap will not draw rotated or scaled. Depending on the value of the flags argument, the bitmap may be expanded so that a square of device pixels displays each bitmap pixel.

Include: graphics.h

See Also: GrDrawHugeImage(), GrDrawBitmap().

| Up: GEOS SDK TechDocs | Up | Prev: GrDestroyBitmap() ... | Next: GrDrawLine() ...


GEOS C Routines: GrDrawLine() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrDrawCurve() ... | Next: GrDrawRect() ...

GrDrawLine()

void	GrDrawLine(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x1,			/* First coordinate of line */
        sword		y1,
        sword		x2,			/* Second coordinate of line */
        sword		y2);

Draw a line.

Include: graphics.h

See Also: GrDrawLineTo(),
GrDrawHLine(),
GrDrawVLine().

GrDrawLineTo()

void	GrDrawLineTo(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x,			/* Second coordinate of line */
        sword		y);

Draw a line starting from the current position.

Include: graphics.h

See Also: GrDrawLine(),
GrDrawHLineTo(),
GrDrawVLineTo().

GrDrawPath()

void	GrDrawPath(
        GStateHandle gstate);			/* GState to draw to */

Draws the stroked version of the current path, using the current graphic line attributes.

Include: graphics.h

GrDrawPoint()

void	GrDrawPoint(
        GStateHandle		gstate,			/* GState to draw to */
        sword 		x,			/* Coordinates of point to draw */
        sword 		y);

Draw a pixel.

Include: graphics.h

GrDrawPointAtCP()

void	GrDrawPointAtCP(
        GStateHandle gstate);			/* GState to draw to */

Draw a pixel.

Include: graphics.h

GrDrawPolygon()

void	GrDrawPolygon(
        GStateHandle		gstate,				/* GState to draw to */
        const	Point 	* points,				/* array of points in polygon */
        word		numPoints);				/* number of points in array */

Draws a connected polygon.

Include: graphics.h

GrDrawPolyline()

void	GrDrawPolyline(
        GStateHandle		gstate,				/* GState to draw to */
        const	Point	* points,				/* array of points in polyline */
        word		numPoints);				/* number of points in array */

Draws a simple polyline.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrDrawCurve() ... | Next: GrDrawRect() ...


GEOS C Routines: GrDrawRect() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrDrawLine() ... | Next: GrDrawRoundRect() ...

GrDrawRect()

void	GrDrawRect(
        GStateHandle		gstate,			/* GState to draw to */
        sword		left,			/* bounds of rectangle to draw */
        sword		top,
        sword		right,
        sword		bottom);

Draws the outline of a rectangle.

Include: graphics.h

GrDrawRectTo()

void	GrDrawRectTo(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x,			/* opposite corner of rectangle */
        sword		y);

Draws the outline of a rectangle, with one corner defined by the current position.

Include: graphics.h

GrDrawRegion()

void	GrDrawRegion(
        GStateHandle		gstate,			/* GState to draw to */
        sword		xPos,			/* Position at which to draw */
        sword		yPos,
        const	Region	* reg,			/* Region definition */
        sword		param0,			/* value to use with
                     * parameterized coordinates */
        sword		param1);			/* value to use with
                     * parameterized coordinates */

Draw a region. The area will be rendered filled with the GState's area attributes.

Include: graphics.h

GrDrawRegionAtCP()

void	GrDrawRegionAtCP(
        GStateHandle		gstate,			/* GState to draw to */
        const	Region 	* reg,			/* region definition */
        sword	param0,	/* Value to use with parameterized coordinates */
        sword	param1,	/* Value to use with parameterized coordinates */
        sword	param2,	/* Value to use with parameterized coordinates */
        sword	param3);	/* Value to use with parameterized coordinates */

Draw a region at the current pen position. The area will be rendered filled with the GState's area attributes.

Include: graphics.h

GrDrawRelArc3PointTo()

void 	GrDrawRelArc3PointTo(
        const ThreePointRelArcToParams *params);

Draw a circular arc relative to the current point given two additional points: the other endpoint and any other point on the arc, both described in relative coordinates.

Include: graphics.h

GrDrawRelLineTo()

void 	GrDrawRelLineTo(
        GStateHandle 		gstate,			/* GState to draw to */
        WWFixedAsDWord 		x,			/* horizontal offset of second point */
        WWFixedAsDWord 		y);			/* vertical offset of second point */

Draw a line from the current pen position, given a displacement from the current pen position to draw to.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrDrawLine() ... | Next: GrDrawRoundRect() ...


GEOS C Routines: GrDrawRoundRect() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrDrawRect() ... | Next: GrEndGString() ...

GrDrawRoundRect()

void	GrDrawRoundRect(
        GStateHandle		gstate,					/* GState to draw to */
        sword		left,					/* bounds of rectangle */
        sword		top,
        sword		right,
        sword		bottom,
        word		cornerRadius);					/* radius of corner rounding */

Draw the outline of a rounded rectangle.

Include: graphics.h

GrDrawRoundRectTo()

void	GrDrawRoundRectTo(
        GStateHandle		gstate,					/* GState to draw to */
        sword		x,					/* opposite corner of bounds */
        sword		y,
        word		cornerRadius);					/* radius of corner rounding */

Draw the outline of a rounded rectangle, where one corner of the bounding rectangle is the current position.

Include: graphics.h

GrDrawSpline()

void	GrDrawSpline(
        GStateHandle		gstate,				/* GState to draw to */
        const	Point	* points,				/* array of points */
        word		numPoints,); 				/* number of points in array */

Draw a Bézier spline.

Include: graphics.h

See Also: GrDrawCurve().

GrDrawSplineTo()

void	GrDrawSplineTo(
        GStateHandle		gstate,				/* GState to draw to */
        const	Point	*points,				/* array of points */
        word		numPoints);				/* number of points in array */

Draw a Bezier spline, using the current position as one endpoint.

Include: graphics.h

See Also: GrDrawCurveTo().

GrDrawText()

void	GrDrawText(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x,			/* point at which to draw */
        sword		y,
        const	Chars	* str,			/* pointer to character string */
        word		size);			/* length of string */

Draw a string of text. The string is represented as an array of characters. Note that the text will be drawn using the GState's font drawing attributes and that this routine does not accept any style run arguments.

If the passed size argument is zero, the string is assumed to be null-terminated.

Include: graphics.h

GrDrawTextAtCP()

void	GrDrawTextAtCP(
        GStateHandle		gstate,			/* GState to draw to */
        const	Chars	* str,			/* pointer to character string */
        word		size);			/* length of string */

As GrDrawText() , above, except that the text is drawn at the current position.

If the passed size argument is zero, the string is assumed to be null-terminated.

Include: graphics.h

GrDrawVLine()

void	GrDrawVLine(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x,			/* horizontal position of line */
        sword		y1,			/* first vertical coordinate */
        sword		y2);			/* second vertical coordinate */

Draw a vertical line.

Include: graphics.h

GrDrawVLineTo()

void	GrDrawVLineTo(
        GStateHandle		gstate,			/* GState to draw to */
        sword		y);			/* second vertical position */

Draw a vertical line starting from the current position.

Include: graphics.h

GrEditBitmap()

GStateHandle GrEditBitmap(
        VMFileHandle 		vmFile,				/* VM file of bitmap */
        VMBlockHandle 		vmBlock,				/* VM block of bitmap */
        optr 		exposureOD);				/* optr to get MSG_META_EXPOSED */

This routine attaches a GState to the passed bitmap so that new drawings may be be sent to the bitmap.

Include: graphics.h

GrEditGString()

GStateHandle GrEditGString(
        Handle	vmFile,	/* VM file containing the GString */
        word	vmBlock);	/* VM block containing the GString */

This routine takes the location of a GString data block stored in a VM file. It will associate a GState with this GString data and returns the handle of this GState. Any graphics commands issued using this GStateHandle will be appended to the GString.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrDrawRect() ... | Next: GrEndGString() ...


GEOS C Routines: GrEndGString() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrDrawRoundRect() ... | Next: GrFillEllipse() ...

GrEndGString()

GStringErrorType GrEndGString(
        GStateHandle gstate);			/* GState to draw to */

Finish the definition of a graphics string.

Structures:

            typedef enum {
        GSET_NO_ERROR,
        GSET_DISK_FULL
} GStringErrorType;

Include: graphics.h

GrEndPath()

void	GrEndPath(
        GStateHandle gstate);			/* GState to draw to */

Finish definition of a path. Further graphics commands will draw to the display, as normal.

Include: graphics.h

GrEndUpdate()

void	GrEndUpdate(
        GStateHandle gstate);			/* GState to draw to */

Unlocks window from an update.

Include: win.h

GrEnumFonts()

word	GrEnumFonts( /* Return value = number of fonts found */
        FontEnumStruct		* buffer,			/* buffer for returned values */
        word		size,			/* number of structures to return */
        FontEnumFlags		flags,			/* FontEnumFlags */
        word		family);			/* FontFamily */

Generate a list of available fonts. The font information includes both the font's ID and a string name.

Structures:

            typedef struct {
        FontID FES_ID;
        char FES_name[FID_NAME_LEN];
} FontEnumStruct;

Include: font.h

GrEscape()

void	GrEscape(
        GStateHandle		gstate,			/* GState to draw to */
        word		code,			/* escape code */
        const	void	* data,			/* pointer to the data */
        word		size);			/* Size of data, in bytes */

Write an escape code to a graphics string.

Include: graphics.h

GrFillArc()

void	GrFillArc(
        GStateHandle		gstate,				/* GState to draw to */
        sword		left,				/* bounding rectangle */
        sword		top,
        sword		right,
        sword		bottom,
        word		startAngle,				/* angles in degrees
        word		endAngle				 * counter-clockwise */
        ArcCloseType 		closeType);				/* OPEN, CHORD, or PIE */

Fill an elliptical arc. The arc is defined by the bounding rectangle of the base ellipse and two angles. Depending on how the arc is closed, this will result in either a wedge or a chord fill.

Include: graphics.h

GrFillArc3Point()

void	GrFillArc3Point(
        GStateHandle		gstate,				/* GState to draw to */
        const ThreePointParams *params);

Fill an arc. Depending on how the arc is closed, this will result in either a wedge or a chord fill. The arc is defined in terms of its endpoints and one other point, all of which must lie on the arc.

Include: graphics.h

GrFillArc3PointTo()

void	GrFillArc3PointTo(
        GStateHandle		gstate,				/* GState to draw to */
        const ThreePointArcParams 	*params);

As GrFillArc3Point() , above, except that one endpoint of the arc is defined by the current position.

Include: graphics.h

GrFillBitmap()

void 	GrFillBitmap (
        GStateHandle 		gstate,				/* GState to draw to */
        sword 		x,				/* point at which to draw */
        sword 		y,
        const Bitmap 		* bm,				/* pointer to bitmap */
        Bitmap * _pascal (*callback) (Bitmap *bm));

Fill a monochrome bitmap with the current area attributes. The arguments to this routine are the same as those for GrDrawBitmap() .

Include: graphics.h

GrFillBitmapAtCP()

void 	GrFillBitmapAtCP (
        GStateHandle 		gstate,					/* GState to draw to */
        const Bitmap 		* bm,					/* pointer to bitmap */
        Bitmap * _pascal (*callback) (Bitmap *bm));

Fill a monochrome bitmap with the current area attributes. The bitmap will be drawn at the current position. The arguments to this routine are the same as those for GrDrawBitmapAtCP() .

Include: graphics.h

GrFillHugeBitmap()

void 	GrFillHugeBitmap(
        GStateHandle 		gstate,
        sword 		x,
        sword 		y,
        VMFileHandle 		vmFile,
        VMBlockHandle 		vmBlk);

This routine fills a monochrom bitmap where said bitmap is stored in a huge array data structure. The bitmap is specified by means of the vmFile and vmBlk arguments which should reference the huge array.

Include: graphics.h

GrFillHugeBitmapAtCP()

void 	GrFillHugeBitmapAtCP(
        GStateHandle 		gstate,
        VMFileHandle 		vmFile,
        VMBlockHandle 		vmBlk);

This routine fills a monochrom bitmap where said bitmap is stored in a huge array data structure. It will draw at the GState's current position. The bitmap is specified by means of the vmFile and vmBlk arguments which should reference the huge array.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrDrawRoundRect() ... | Next: GrFillEllipse() ...


GEOS C Routines: GrFillEllipse() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrEndGString() ... | Next: GrGetAreaColor() ...

GrFillEllipse()

void	GrFillEllipse(
        GStateHandle		gstate,				/* GState to draw to */
        sword		left,				/* Bounds of bounding rectangle */
        sword		top,
        sword		right,
        sword		bottom);

Draw a filled ellipse. The ellipse's dimensions are defined by its bounding box.

Include: graphics.h

GrFillPath()

void	GrFillPath(
        GStateHandle		gstate,				/* GState to draw to */
        RegionFillRule		rule);				/* ODD_EVEN or WINDING */

Fill an area whose outline is defined by the GState's path.

Include: graphics.h

GrFillPolygon()

void	GrFillPolygon(
        GStateHandle		gstate,					/* GState to draw to */
        RegionFillRule		windingRule,					/* ODD_EVEN or WINDING */
        const	Point	* points,					/* array of points in polygon */
        word		numPoints);					/* number of points in array */

This routine draws a filled polygon. The polygon is defined by the passed array of points.

Include: graphics.h

GrFillRect()

void	GrFillRect(
        GStateHandle		gstate,				/* GState to draw to */
        sword		left,				/* bounds of rectangle */
        sword		top,
        sword		right,
        sword		bottom);

Draw a filled rectangle.

Include: graphics.h

GrFillRectTo()

void	GrFillRectTo(
        GStateHandle		gstate,				/* GState to draw to */
        sword		x,				/* opposite corner of rectangle */
        sword		y);

Draw a filled rectangle. The current position will define one of the corners.

Include: graphics.h

GrFillRoundRect()

void	GrFillRoundRect(
        GStateHandle		gstate,					/* GState to draw to */
        sword		left,					/* bounds of rectangle */
        sword		top,
        sword		right,
        sword		bottom
        word 		cornerRadius);					/* radius of corner rounding */

Draw a filled rounded rectangle.

Include: graphics.h

GrFillRoundRectTo()

void	GrFillRoundRectTo(
        GStateHandle		gstate,				/* GState to draw to */
        sword		x,				/* opposite corner of rectangle */
        sword		y
        word 		cornerRadius);					/* radius of corner roundings */

Draw a filled rounded rectangle, using the current position to define one corner of the bounding rectangle.

Include: graphics.h

GrFindNearestPointsize()

Boolean	GrFindNearestPointsize( /* If false, then FontID invalid */
        FontID 		id,					/* fond ID */
        dword		sizeSHL16,					/* point size */
        TextStyle 		styles,					/* style */
        TextStyle 		* styleFound,						/* buffer for style */
        dword		* sizeFoundSHL16);						/* buffer for size */

Find the nearest available point size for a font. If the font passed in id exists, then styleFound will point to the styles available and sizeFoundSHL16 will point to the nearest point size to that passed. If the font is not found, the return valued will be true .

Include: font.h

GrFontMetrics()

dword	GrFontMetrics(
        GStateHandle		gstate,				/* subject GState */
        GFM_info		info);				/* Type of information to return */

Get metrics information about a font. It returns the requested information based on the info parameter.

Structures:

typedef enum /* word */ {
    GFMI_HEIGHT,				/* return = val << 16 */
    GFMI_MEAN,				/* return = val << 16 */
    GFMI_DESCENT,				/* return = val << 16 */
    GFMI_BASELINE,				/* return = val << 16 */
    GFMI_LEADING,				/* return = val << 16 */
    GFMI_AVERAGE_WIDTH,				/* return = val << 16 */
    GFMI_ASCENT,				/* return = val << 16 */
    GFMI_MAX_WIDTH,				/* return = val << 16 */
    GFMI_MAX_ADJUSTED_HEIGHT,				/* return = val << 16 */
    GFMI_UNDER_POS,				/* return = val << 16 */
    GFMI_UNDER_THICKNESS, 				/* return = val << 16 */
    GFMI_ABOVE_BOX,				/* return = val << 16 */
    GFMI_ACCENT,				/* return = val << 16 */
    GFMI_MANUFACTURER,				/* return = val */
    GFMI_KERN_COUNT, 				/* return = Char */
    GFMI_FIRST_CHAR, 				/* return = Char */
    GFMI_LAST_CHAR, 				/* return = FontMaker */
    GFMI_DEFAULT_CHAR,				/* return = Char */
    GFMI_STRIKE_POS,				/* return = Char */
    GFMI_BELOW_BOX, 				/* return = Char */
    GFMI_HEIGHT_ROUNDED				/* return = Char */
    GFMI_DESCENT_ROUNDED, 				/* return = Char */
    GFMI_BASELINE_ROUNDED, 				/* return = Char */
    GFMI_LEADING_ROUNDED, 				/* return = Char */
    GFMI_AVERAGE_WIDTH_ROUNDED,				/* return = Char */
    GFMI_ASCENT_ROUNDED, 				/* return = Char */
    GFMI_MAX_WIDTH_ROUNDED, 				/* return = Char */
    GFMI_MAX_ADJUSTED_HEIGHT_ROUNDED, /* ret = Char */
    GFMI_UNDER_POS_ROUNDED, 				/* return = Char */
    GFMI_UNDER_THICKNESS_ROUNDED, /* return = Char */
    GFMI_ABOVE_BOX_ROUNDED, 				/* return = Char */
    GFMI_ACCENT_ROUNDED=, 				/* return = Char */
    GFMI_STRIKE_POS_ROUNDED,				/* return = Char */
    GFMI_BELOW_BOX_ROUNDED				/* return = Char */
} GFM_info;

Include: font.h

| Up: GEOS SDK TechDocs | Up | Prev: GrEndGString() ... | Next: GrGetAreaColor() ...


GEOS C Routines: GrGetAreaColor() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrFillEllipse() ... | Next: GrGetDefFontID() ...

GrGetAreaColor()

RGBColorAsDWord 	GrGetAreaColor(
        GStateHandle gstate);			/* GState of which to get color */

Get the color which is being used to fill areas.

Include: graphics.h

GrGetAreaColorMap()

ColorMapMode GrGetAreaColorMap(
        GStateHandle gstate);				/* GState of which to get area color map */

Get the mapping mode used for filling areas with unavailable colors.

Include: graphics.h

GrGetAreaMask()

SysDrawMask GrGetAreaMask(
        GStateHandle		gstate,			/* GState of which to get mask */
        DrawMask		* dm);			/* buffer for returned mask */

Get the draw mask used when filling areas. The dm argument should point to a buffer capable of holding at least eight bytes to get the bit-pattern of the mask; otherwise dm should be NULL. The returned buffer is the 8x8 bit pattern: each byte represents a row of the pattern, and the bytes are ordered from top row to bottom.

Include: graphics.h

GrGetAreaPattern()

GraphicPattern 	GrGetAreaPattern(
        GStateHandle 		gstate,					/* GState of area pattern */
        const MemHandle		* customPattern,					/* pointer to handle of block for
                              * returned custom pattern */
        word 		* customSize);					/* pointer to size of returned
                             * buffer */

Get the area pattern used when filling areas.

Include: graphics.h

GrGetBitmap()

MemHandle GrGetBitmap(
        GStateHandle		gstate,					/* GState containing bitmap */
        sword		x,					/* bitmap origin */
        sword		y,
        word		width,					/* bitmap width and height */
        word		height,
        XYSize	 	* sizeCopied);					/* buffer for returned size */

Dump an area of the display to a bitmap. The handle of a block containing the bitmap is returned; the sizeCopied pointer points to the actual size of the bitmap successfully copied.

Include: graphics.h

GrGetBitmapMode()

BitmapMode 	GrGetBitmapMode(
        GStateHandle gstate);				/* GState containing bitmap */

Get mode bits for an editable bitmap.

Include: graphics.h

GrGetBitmapRes()

XYValueAsDWord GrGetBitmapRes(
        const Bitmap		* bm);				/* pointer to the bitmap */

Get the resolution of a bitmap.

Include: graphics.h

GrGetBitmapSize()

XYValueAsDWord GrGetBitmapSize(
        const	Bitmap	* bm);				/* pointer to the bitmap */

Get the dimensions, in points, of a bitmap.

Include: graphics.h

GrGetClipRegion()

MemHandle GrGetClipRegion(
        GStateHandle		gstate,			/* subject GState */
        RegionFillRule		rule);			/* ODD_EVEN or WINDING */

Get the current clip region. A null handle (zero) will be returned if no clip paths are se for the GState.

Include: graphics.h

GrGetCurPos()

XYValueAsDWord GrGetCurPos(
        GStateHandle gstate);				/* subject GState */

Get the current pen position.

Include: graphics.h

GrGetCurPosWWFixed()

void GrGetCurPosWWFixed(
        GStateHandle gstate,				/* subject GState */
        PointWWFixed 	*cp);			/* buffer in which to return cur. pos. */

Get the current pen position.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrFillEllipse() ... | Next: GrGetDefFontID() ...


GEOS C Routines: GrGetDefFontID() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrGetAreaColor() ... | Next: GrGetLineMask() ...

GrGetDefFontID()

FontID	GrGetDefFontID(
        dword	* sizeSHL16);		/* pointer to buffer for returned size */

Get the system default font (including size).

Include: font.h

GrGetExclusive()

GStateHandle 	GrGetExclusive(
        GeodeHandle videoDriver);

Use this routine to find out which GState, if any, has grabbed the video exclusive.

Include: graphics.h

GrGetFont()

FontID	GrGetFont(
        GStateHandle		gstate,				/* subject GState */
        WWFixedAsDWord		* pointSize);				/* pointer to buffer for
                         * returned point size */

Get the passed GState's current font, including point size.

Include: graphics.h

GrGetFontName()

FontID 	GrGetFontName(
        FontID 		id,			/* ID of font */
        const char 		* name);			/* buffer for returned name string */

Get the string name of a font. Note that if the returned FontID is zero, then the font was not found. The name string buffer should be a least FID_NAME_LEN in size.

Include: font.h

GrGetFontWeight()

FontWeight GrGetFontWeight(
        GStateHandle gstate);				/* GState containing the font */

Get the current font weight set for the passed GState.

Include: font.h

GrGetFontWidth()

FontWidth GrGetFontWidth(
        GStateHandle gstate);				/* GState containing the font */

Get the current font width set for the passed GState.

Include: font.h

GrGetGStringBounds()

void	GrGetGStringBounds(
        GStringHandle		source,				/* GString to be checked */
        GStateHandle		dest,				/* handle of GState to use */
        GSControl		flags,				/* GSControl flags */
        Rectangle		* bounds);				/* returned bounds of GState */

This routine returns the coordinate bounds of the source GString drawn at the current position in the GString. The dest GState will be used if passed; to have no GState restrictions, pass a null handle. The bounds of the smallest containing rectangle will be returned in the structure pointed to by bounds .

Include: gstring.h

GrGetGStringBoundsDWord

void	GrGetGStringBoundsDWord(
        Handle		gstring,				/* GString to be checked */
        GStateHandle		gstate,				/* handle of GState to use */
        GSControl		flags,				/* GSControl flags */
        RectDWord		* bounds);				/* returned bounds of GState */

This routine behaves as GrGetGStringBounds() , but has been alterred to work with 32-bit graphics spaces.

This routine returns the coordinate bounds of aGString drawn at the current position in the GString. The gstate GState will be used if passed; to have no GState restrictions, pass a null handle. The bounds of the smallest containing rectangle will be returned in the structure pointed to by bounds .

Include: gstring.h

GrGetGStringElement()

GStringElement GrGetGStringElement(
        GStateHandle		gstate,					/* handle of GString's GState */
        void 		* buffer,					/* pointer to return buffer */
        word 		bufSize,					/* size of return buffer */
        word 		* elementSize,					/* size of GString element */
        void		** pointerAfterData);							/* pointer to pointer to
                             * next element in GString */

Extract the next element from a graphics string. The opcode is returned explicitly. The routine's data can be returned in a buffer.

Include: gstring.h

GrGetHugeBitmapSize()

XYValueAsDWord GrGetHugeBitmapSize(
        VMFileHandle		vmFile,
        VMBlockHandle 		vmBlk);

Returns the dimensions (horizontal and vertical) of the passed huge bitmap, as referenced by a VM block and file.

Include: graphics.h

GrGetInfo()

void	GrGetInfo(
        GStateHandle		gstate,			/* GState to get information about */
        GrInfoTypes		type,			/* type of information to get */
        void	 	* data);			/* buffer for returned information */

Get the private data, window handle, or pen position associated with the GState.

Structures:

typedef enum {
    GIT_PRIVATE_DATA,
    GIT_WINDOW,
    GIT_PEN_POS
} GrInfoType

Include: graphics.h

GrGetLineColor()

RGBColorAsDWord GrGetLineColor(
        GStateHandle gstate);				/* subject GState */

Get the color used when drawing lines.

Include: graphics.h

GrGetLineColorMap()

ColorMapMode GrGetLineColorMap(
        GStateHandle gstate);				/* subject GState */

Get the mode used when drawing lines in an unavailable color.

Include: graphics.h

GrGetLineEnd()

LineEnd	GrGetLineEnd(
        GStateHandle gstate);				/* subject GState */

Get the end used when drawing lines.

Include: graphics.h

GrGetLineJoin()

LineJoin GrGetLineJoin(
        GStateHandle gstate);				/* subject GState */

Get the join used when drawing corners.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrGetAreaColor() ... | Next: GrGetLineMask() ...


GEOS C Routines: GrGetLineMask() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrGetDefFontID() ... | Next: GrGetPtrRegBounds() ...

GrGetLineMask()

SysDrawMask GrGetLineMask(
        GStateHandle		gstate,			/* subject GState */
        DrawMask		* dm);			/* buffer for returned custom mask */

Get the drawing mask used when drawing lines. The dm argument should point to a buffer capable of holding at least eight bytes to get the bit-pattern of the mask; otherwise dm should be NULL. The returned buffer is the 8x8 bit pattern: each byte represents a row of the pattern, and the bytes are ordered from top row to bottom.

Include: graphics.h

GrGetLineStyle()

LineStyle GrGetLineStyle(
        GStateHandle gstate);				/* subject GState */

Get the style, or "dottedness," used when drawing lines.

Include: graphics.h

GrGetLineWidth()

WWFixedAsDWord 	GrGetLineWidth(
        GStateHandle gstate);				/* subject GState */

Get the current line width.

Include: graphics.h

GrGetMaskBounds()

void	GrGetMaskBounds(
        GStateHandle		gstate,				/* subject GState */
        Rectangle		* bounds);				/* buffer for returned bounds */

Get the 16-bit bounds of the current clip rectangle.

Include: graphics.h

GrGetMaskBoundsDWord()

void	GrGetMaskBoundsDWord(
        GStateHandle		gstate,				/* subject GState */
        RectDWord		* bounds);				/* buffer for returned bounds */

Get the 16-bit bounds of the current clip rectangle, accurate to a fraction of a point.

Include: graphics.h

GrGetMiterLimit()

WWFixedAsDWord GrGetMiterLimit(
        GStateHandle gstate);						/* subject GState */

Get the miter limit to use when drawing mitered corners.

Include: graphics.h

GrGetMixMode()

MixMode GrGetMixMode(
        GStateHandle gstate);					/* subject GState */

Get the current mixing mode.

Include: graphics.h

GrGetPalette()

MemHandle GrGetPalette(
        GStateHandle		gstate,					/* subject GState */
        GetPalType		flag,					/* GPT_ACTIVE, GPT_CUSTOM, or
                             * GPT_DEFAULT */
        word	 	* numEntries);					/* number of entries in block */

Return all or part of the window's color lookup table. This routine returns the handle of a block containing all the returned palette entries.

Include: graphics.h

GrGetPath()

MemHandle 	GrGetPath(
        GStateHandle		gstate,				/* subject GState */
        GetPathType 		ptype);				/* Which path to retrieve */

Returns handle to block containing path data. Either the current path, the clipping path, or the window clipping path may be retrieved.

Include: graphics.h

GrGetPathBounds()

Boolean	GrGetPathBounds(
        GStateHandle		gstate,				/* subject GState */
        GetPathType 		ptype,
        Rectangle		* bounds);				/* buffer for returned bounds */

Returns the rectangular bounds that encompass the current path as it would be filled. A true return value indicates an error occurred or there was no path for the GState.

Include: graphics.h

GrGetPathBoundsDWord()

Boolean	GrGetPathBoundsDWord(
        GStateHandle		gstate,				/* subject GState */
        GetPathType		ptype,
        RectDWord		* bounds);				/* buffer for returned bounds */

Returns the rectangular bounds that encompass the current path as it would be filled. A true return value indicates an error occurred or there was no path for the GState.

Include: graphics.h

GrGetPathPoints()

MemHandle 	GrGetPathPoints(
        GStateHandle		gstate,					/* subject GState */
        word		resolution);					/* dots per inch */

Returns a series of points that fall along the current path. The returned points are in document coordinates.

Include: graphics.h

GrGetPathRegion()

MemHandle GrGetPathRegion(
        GStateHandle		gstate,				/* subject GState */
        RegionFillRule		rule);				/* ODD_EVEN or WINDING */

Get the region enclosed by a path.

Include: graphics.h

GrGetPoint()

RGBColorAsDWord GrGetPoint(
        GStateHandle		gstate,			/* subject GState */
        sword		x,			/* coordinates of pixel */
        sword		y);

Get the color of the pixel corresponding to the specified coordinates.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrGetDefFontID() ... | Next: GrGetPtrRegBounds() ...


GEOS C Routines: GrGetPtrRegBounds() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrGetLineMask() ... | Next: GrInitDefaultTransform() ...

GrGetPtrRegBounds()

word	GrGetPtrRegBounds( /* Returns size of Region data struct. */
        const	Region	* reg,				/* pointer to region */
        Rectangle		* bounds);				/* returned bounds of region */

Get the bounds of the passed region.

Include: graphics.h

GrGetSubscriptAttr()

ScriptAttrAsWord GrGetSubscriptAttr(
        GStateHandle gstate);					/* subject GState */

Get the GState's subscript drawing attributes. The high byte of the return value is the percentage of the font size for the subscript; the low byte is the percentage of the font size from the top at which the character gets drawn.

Include: font.h

GrGetSuperscriptAttr()

ScriptAttrAsWord GrGetSuperscriptAttr(
        GStateHandle gstate);					/* subject GState */

Get the GState's superscript drawing attributes. The high byte of the return value is the percentage of the font size for the superscript; the low byte is the percentage of the font size from the bottom at which the character gets drawn.

Include: font.h

GrGetTextBounds()

Boolean	GrGetTextBounds(
        GStateHandle		gstate,			/* subject GState */
        const char		* str,			/* text string */
        word 		xpos,			/* position where text would be drawn */
        word 		ypos,
        word 		count,			/* max number of characters to check */
        Rectangle 		* bounds);				/* returned bounding rectangle */

Get the bounds required to draw the passed text. If the passed count argument is zero, the string is assumed to be null-terminated.

Include: graphics.h

GrGetTextColor()

RGBColorAsDWord GrGetTextColor(
        GStateHandle gstate);				/* subject GState */

Get the color used when drawing text.

Include: graphics.h

GrGetTextColorMap()

ColorMapMode 	GrGetTextColorMap(
        GStateHandle gstate);				/* subject GState */

Get the mode used when drawing text in an unavailable color.

Include: graphics.h

GrGetTextMask()

SystemDrawMask 	GrGetTextMask(
        GStateHandle		gstate,			/* subject GState */
        DrawMask		* dm);			/* returned custom mask, if any */

Get the draw mask used when drawing text.The dm argument should point to a buffer capable of holding at least eight bytes to get the bit-pattern of the mask; otherwise dm should be NULL. The returned buffer is the 8x8 bit pattern: each byte represents a row of the pattern, and the bytes are ordered from top row to bottom.

Include: graphics.h

GrGetTextMode()

TextMode	GrGetTextMode(
        GStateHandle gstate);				/* subject GState */

Get the text mode, including information about the vertical offset used when drawing text.

Include: graphics.h

GrGetTextPattern()

GraphicPattern 	GrGetTextPattern(
        GStateHandle	 	gstate,						/* subject GState */
        const MemHandle		* customPattern,						/* pointer to returned handle
                                 * of block containing the
                                 * returned pattern */
        word		* customSize);						/* size of returned block */

Get the graphics pattern used when drawing text.

Include: graphics.h

GrGetTextSpacePad()

WWFixedAsDWord GrGetTextSpacePad(
        GStateHandle gstate);				/* subject GState */

Get the space pad used when drawing strings of text.

Include: graphics.h

GrGetTextStyle()

TextStyle 	GrGetTextStyle(
        GStateHandle gstate);					/* subject GState */

Get the style used when drawing text.

Include: graphics.h

GrGetTrackKern()

word 	GrGetTrackKern(
        GStateHandle 	gstate);				/* subject GState */

Get the track kerning used when drawing strings of text.

Include: graphics.h

GrGetTransform()

void	GrGetTransform(
        GStateHandle		gstate,			/* subject GState */
        TransMatrix		* tm);			/* pointer to returned TransMatrix */

Get the current coordinate transformation, expressed as a matrix.

Include: graphics.h

GrGetWinBounds()

void	GrGetWinBounds(
        GStateHandle		gstate,				/* subject GState */
        Rectangle		* bounds);				/* returned window bounds */

Get the bounds of the GState's associated window.

Include: graphics.h

GrGetWinBoundsDWord()

void	GrGetWinBoundsDWord(
        GStateHandle		gstate,				/* subject GState */
        RectDWord		* bounds);				/* returned window bounds */

Get the bounds of the GState's associated window, accurate to a fraction of a point.

Include: graphics.h

GrGetWinHandle()

WindowHandle GrGetWinHandle(
        GStateHandle gstate);					/* subject GState */

Get the handle of the GState's associated window.

Include: graphics.h

GrGrabExclusive()

GStateHandle GrGrabExclusive(
        GeodeHandle		videoDriver,				/* NULL for default */
        GStateHandle		gstate);				/* subject GState */

Start drawing exclusively to a video driver.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrGetLineMask() ... | Next: GrInitDefaultTransform() ...


GEOS C Routines: GrInitDefaultTransform() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrGetPtrRegBounds() ... | Next: GrNewPage() ...

GrInitDefaultTransform()

void	GrInitDefaultTransform(
        GStateHandle gstate);					/* subject GState */

Initialize the GState's default transformation to hold the value of the current transformation.

Include: graphics.h

GrInvalRect()

void	GrInvalRect(
        GStateHandle		gstate,				/* subject GState */
        sword		left,				/* bounds to be invalidated */
        sword		top,
        sword		right,
        sword		bottom);

Invalidate the passed rectangular area. This area will be redrawn.

Include: graphics.h

GrInvalRectDWord()

void	GrInvalRectDWord(
        GStateHandle		gstate,				/* subject GState */
        const	RectDWord	* bounds);				/* bounds to be invalidated */

Invalidate the passed rectangular area. This area will be redrawn.

Include: graphics.h

GrLabel()

void	GrLabel(
        GStringHandle		gstate,			/* subject GState */
        word			label);			/* label to write to GString */

Write the passed label into the passed GString.

Include: gstring.h

GrLoadGString()

Handle GrLoadGString(
        Handle		han,				/* handle of GString source */
        GStringType	hanType,			/* handle type */
        word		vmBlock);			/* if VM file, handle of VM block */

Load a graphics string from a file. Used with stream, VM, and pointer addressed GStrings.

When done with the GString, you may free it via GrDestroyGString().

Include: gstring.h

GrMapColorIndex()

RGBColorAsDWord GrMapColorIndex(
        GStateHandle		gstate,			/* GState to use for mapping */
        Color		c);			/* source color to be mapped */

Map a color index to its RGB equivalent using the color mapping scheme of the passed GState.

Include: graphics.h

GrMapColorRGB()

RGBColorAsDWord GrMapColorRGB(
        GStateHandle		gstate,			/* GState to use for mapping */
        word		red,			/* RGB values to map */
        word		green,
        word		blue);

Map an RGB color to an index.

Include: graphics.h

GrMoveReg()

void	GrMoveReg(
        Region	* reg,	/* pointer to region */
        sword	xOffset,	/* amount to shift horizontally */
        sword	yOffset);	/* amount to shift vertically */

Moves a region a given amount. Note that this operation affects only the region's data structure. The region must be redrawn or used in some other way for the changes to have any visible effect.

Include: graphics.h

GrMoveTo()

void	GrMoveTo(
        GStateHandle		gstate,			/* subject GState */
        sword		x,			/* new absolute pen position */
        sword		y);

Change the pen position.

Include: graphics.h

GrMoveToWWFixed()

void GrMoveToWWFixed(
        GStateHandle gstate,
        WWFixedAsDWord x,
        WWFixedAsDWord y);

This routine changes the pen position very precisely.

GrMulDWFixed()

void	GrMulDWFixed(
        const	DWFixed	* i,				/* first number */
        const	DWFixed	* j,				/* second number */
        DWFixed		* result);				/* pointer to returned result */

Multiply two fixed point numbers.

Include: graphics.h

GrMulWWFixed()

WWFixedAsDWord GrMulWWFixed(
        WWFixedAsDWord i,			/* first number */
        WWFixedAsDWord j);			/* second number */

Multiply two fixed point numbers.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrGetPtrRegBounds() ... | Next: GrNewPage() ...


GEOS C Routines: GrNewPage() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrInitDefaultTransform() ... | Next: GrSDivDWFByWWF() ...

GrNewPage()

void	GrNewPage(
        GStateHandle 		gstate,
        PageEndCommand 		pageEndCommand);

Begin drawing a new page. Normally used when printing documents.

Include: graphics.h

GrNullOp()

void	GrNullOp(
        GStateHandle gstate);				/* subject GState */

Write a null operation element to a GString.

Include: graphics.h

GrParseGString()

void	GrParseGString(
        GStateHandle gstate,
        GStateHandle gstringToDraw,
        GSControl flags,
        Boolean (*callback) /* TRUE to stop */ (void *element));

This routine traverses a GString, calling a callback function on each GStringElement encountered.

Include: gstring.h

GrQuickArcSine()

WWFixedAsDWord GrQuickArcSine(
        WWFixedAsDWord		deltaYDivDistance,						/* delta y / distance */
        word		origDeltaX);						/* original delta x */

Compute a fixed point arcsine. Angles are given in degrees counterclockwise of the positive x axis.

Include: graphics.h

GrQuickCosine()

WWFixedAsDWord GrQuickCosine(
        WWFixedAsDWord angle);				/* angle to cosine */

Compute a fixed point cosine. Angles are given in degrees counterclockwise of the positive x axis.

Include: graphics.h

GrQuickSine()

WWFixedAsDWord GrQuickSine(
        WWFixedAsDWord angle);				/* angle to sine */

Compute a fixed point sine. Angles are given in degrees counterclockwise of the positive x axis.

Include: graphics.h

GrQuickTangent()

WWFixedAsDWord GrQuickTangent(
        WWFixedAsDWord angle);				/* angle to tangent */

Compute a fixed point tangent. Angles are given in degrees counterclockwise of the positive x axis.

Include: graphics.h

GrReleaseExclusive()

void 	GrReleaseExclusive( /* TRUE if system had to force a redraw */
        GeodeHandle		videoDriver,				/* handle of video driver */
        GStateHandle		gstate,				/* GState that was drawing */
        Rectangle 		*bounds);				/* Bounds of aborted drawings */

Stop drawing exclusively to a video driver.

Include: graphics.h

GrRelMoveTo()

void	GrRelMoveTo(
        GStateHandle		gstate,			/* subject GState */
        WWFixedAsDWord 		x,			/* offsets to new pen position */
        WWFixedAsDWord 		y);

Change the pen position to coordinate expressed relative to the current position.

Include: graphics.h

GrRestoreState()

void	GrRestoreState(
        GStateHandle gstate);				/* subject GState */

Restore the values of a saved GState.

Include: graphics.h

GrSaveState()

void	GrSaveState(
        GStateHandle gstate);				/* subject GState */

Save the values of a GState, so that they may be restored by GrRestoreState() .

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrInitDefaultTransform() ... | Next: GrSDivDWFByWWF() ...


GEOS C Routines: GrSDivDWFByWWF() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrNewPage() ... | Next: GrSetClipPath() ...

GrSDivDWFByWWF()

void GrSDivDWFByWWF(
        const DWFixed 		* dividend,
        const WWFixed 		* divisor,
        DWFixed 		* quotient)				/* returned value */

Divide two fixed point numbers.

Include: graphics.h

GrSDivWWFixed()

WWFixedAsDWord GrSDivWWFixed(
        WWFixedAsDWord dividend,
        WWFixedAsDWord divisor)

Divide two fixed point numbers.

Include: graphics.h

GrSetAreaAttr()

void	GrSetAreaAttr(
        GStateHandle		gstate,			/* subject GState */
        const AreaAttr 		* aa);			/* AreaAttr structure */

Set all of the attributes used when filling areas.

Include: graphics.h

GrSetAreaColor()

void	GrSetAreaColor(
        GStateHandle 		gstate,				/* GState to set color for */
        ColorFlag 		flag,				/* flag of how to set color */
        word		redOrIndex,				/* color index or red RGB value */
        word		green,				/* green RGB value or zero */
        word		blue);				/* blue RGB value or zero */

Set the color to use when filling areas. The flag parameter may be CF_RGB (to set RGB values), CF_INDEX (to set a palette index), CF_GRAY, or CF_SAME.

Include: graphics.h

GrSetAreaColorMap()

void	GrSetAreaColorMap(
        GStateHandle 		gstate,				/* subject GState */
        ColorMapMode 		colorMap);				/* color mapping mode */

Set mode to use when trying to fill an area with an unavailable color.

Include: graphics.h

GrSetAreaMaskCustom()

void	GrSetAreaMaskCustom(
        GStateHandle 		gstate,				/* subject GState */
        const	DrawMask  	* dm);				/* pointer to new custom mask */

Set the drawing mask to use when filling areas.

Include: graphics.h

GrSetAreaMaskSys()

void	GrSetAreaMaskSys(
        GStateHandle 		gstate,				/* subject GState */
        SystemDrawMask 		sysDM);				/* new system area mask */

Set the drawing mask to use when filling areas.

Include: graphics.h

GrSetAreaPattern()

void 	GrSetAreaPattern(
        GStateHandle 		gstate,				/* subject GState */
        GraphicPattern 		pattern);				/* new pattern */

Set the graphics pattern to use when filling areas.

Include: graphics.h

GrSetBitmapMode()

void	GrSetBitmapMode(
        GStateHandle  		gstate,				/* subject GState */
        word 		flags,		 /* BM_EDIT_MASK or BM_CLUSTERED_DITHER */
        MemHandle 		colorCorr);				/* handle of ColorTransfer */

Set the bitmap editing mode. This allows the editing of a bitmap's mask, or turning on clustered dithering.

Include: graphics.h

GrSetBitmapRes()

Boolean	GrSetBitmapRes(
        GStateHandle		gstate,				/* subject GState */
        word		xRes,				/* new resolutions */
        word		yRes);

Set a complex bitmap's resolution.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrNewPage() ... | Next: GrSetClipPath() ...


GEOS C Routines: GrSetClipPath() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrSDivDWFByWWF() ... | Next: GrSetLineAttr() ...

GrSetClipPath()

void	GrSetClipPath(
        GStateHandle 		gstate,				/* subject GState */
        PathCombineType 		params,				/* how paths should be combined */
        RegionFillRule 		rule);				/* ODD_EVEN or WINDING */

Restrict the clipping region by intersecting it with the passed path.

Include: graphics.h

GrSetClipRect()

void	GrSetClipRect(
        GStateHandle 		gstate,				/* subject GState */
        PathCombineType		flags,				/* how paths should be combined */
        sword		left,				/* bounds of clipping rectangle */
        sword		top,
        sword		right,
        sword		bottom);

Restrict the clipping region by intersecting it with the passed rectangle.

Include: graphics.h

GrSetCustomAreaPattern()

void 	GrSetCustomAreaPattern(
        GStateHandle 		gstate,				/* subject GState */
        GraphicPattern 		pattern,				/* new area pattern */
        const void 	*	patternData,				/* pointer to pattern data */
        word		patternSize); /* size of pattern data buffer */

Set the graphics pattern to use when filling areas.

Include: graphics.h

GrSetCustomTextPattern()

void 	GrSetCustomTextPattern(
        GStateHandle 		gstate,					/* subject GState */
        GraphicPattern		pattern,					/* new pattern */
        const void 		* patternData		);			/* pointer to pattern data */

Set the graphic pattern used when drawing text.

Include: graphics.h

GrSetDefaultTransform()

void	GrSetDefaultTransform(
        GStateHandle gstate);				/* subject GState */

Replace the current coordinate transformation with the default transformation.

Include: graphics.h

GrSetFont()

void	GrSetFont(
        GStateHandle 		gstate,				/* subject GState */
        FontID 		id,				/* new font ID */
        WWFixedAsDWord 		pointSize);				/* new point size */

Set the font to use when drawing text.

Include: graphics.h

GrSetFontWeight()

void	GrSetFontWeight(
        GStateHandle 		gstate,				/* subject GState */
        FontWeight 		weight);				/* new font weight */

Set the font weight to use when drawing text.

Include: font.h

GrSetFontWidth()

void	GrSetFontWidth(
        GStateHandle 		gstate,				/* subject GState */
        FontWidth 		width);				/* new font width */

Set the font width to use when drawing text.

Include: font.h

GrSetGStringBounds()

void	GrSetGStringBounds(
        Handle		gstate,				/* GState or GString handle */
        sword		left,				/* new bounds of GString */
        sword		top,
        sword		right,
        sword		bottom);

Optimization routine which allows you to set bounds values for a GString. This bounds information will be returned by GrGetGStringBounds() whenever that routine is called upon the affected GString.

Include: graphics.h

GrSetGStringPos()

void	GrSetGStringPos(
        GStateHandle		gstate,				/* subject GState */
        GStringSetPosType		type,				/* how to set position */
        word		skip);				/* number of elements to skip */

Set a graphics strings' "playing position." Using this routine, it is possible to draw only selected elements of a GString.

Structures:

typedef ByteEnum GStringSetPosType;
/*	GSSPT_SKIP,
    GSSPT_RELATIVE,
    GSSPT_BEGINNING,
    GSSPT_END		*/

Include: gstring.h

| Up: GEOS SDK TechDocs | Up | Prev: GrSDivDWFByWWF() ... | Next: GrSetLineAttr() ...


GEOS C Routines: GrSetLineAttr() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrSetClipPath() ... | Next: GrSetPalette() ...

GrSetLineAttr()

void	GrSetLineAttr(
        GStateHandle 		gstate,			/* subject GState */
        const LineAttr 		* la);			/* new line attributes */

Set all attributes to use when drawing lines and corners.

Include: graphics.h

GrSetLineColor()

void	GrSetLineColor(
        GStateHandle 		gstate,				/* subject GState */
        ColorFlag 		flag,				/* color flag */
        word		redOrIndex,				/* new index or red RGB value */
        word		green,				/* new green RGB value or zero */
        word		blue);				/* new blue RGB value or zero */

Set the color to use when drawing lines.

Include: graphics.h

GrSetLineColorMap()

void	GrSetLineColorMap(
        GStateHandle gstate,				/* subject GState */
        ColorMapMode colorMap);				/* new color map mode for lines */

Set the mode to use when trying to draw lines in an unavailable color.

Include: graphics.h

GrSetLineEnd()

void	GrSetLineEnd(
        GStateHandle 		gstate,				/* subject GState */
        LineEnd 		end);				/* new line end specification */

Set the end to use when drawing lines.

Include: graphics.h

GrSetLineJoin()

void	GrSetLineJoin(
        GStateHandle 		gstate,				/* subject GState */
        LineJoin 		join);				/* new line join specification */

Set the line join to use when drawing corners.

Include: graphics.h

GrSetLineMaskCustom()

void	GrSetLineMaskCustom(
        GStateHandle 		gstate,				/* subject GState */
        const	DrawMask  	* dm);				/* new line draw mask */

Set the drawing mask used when drawing lines.

Include: graphics.h

GrSetLineMaskSys()

void	GrSetLineMaskSys(
        GStateHandle 		gstate,				/* subject GState */
        SystemDrawMask 		sysDM);				/* the new system line mask */

Set the drawing mask used when drawing lines.

Include: graphics.h

GrSetLineStyle()

void	GrSetLineStyle(
        GStateHandle	 	gstate,					/* subject GState */
        LineStyle 		style,					/* new line style */
        word		skipDistance,					/* skip distance to first pair */
        const DashPairArray		* dpa,					/* dash definition */
        word		numPairs);					/* number of pairs */

Set the style, or "dottedness," to use when drawing lines.

Include: graphics.h

GrSetLineWidth()

void	GrSetLineWidth(
        GStateHandle 		gstate,				/* subject GState */
        WWFixedAsDWord 		width);				/* new line width */

Set the line width to use when drawing lines.

Include: graphics.h

GrSetMiterLimit()

void	GrSetMiterLimit(
        GStateHandle 		gstate,				/* subject GState */
        WWFixedAsDWord 		limit);				/* new miter limit */

Set the miter limit to use when drawing mitered corners.

Include: graphics.h

GrSetMixMode()

void	GrSetMixMode(
        GStateHandle 		gstate,				/* subject GState */
        MixMode 		mode);				/* new mix mode */

Set the GState's mix mode, used to determine what happens when something is drawn on top of an existing drawing.

Include: graphics.h

GrSetNullTransform()

void	GrSetNullTransform(
        GStateHandle gstate);				/* subject GState */

Clear the coordinate transformation. Most applications will actually want to replace the coordinate transformation with the default transformation using GrSetDefaultTransform() .

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrSetClipPath() ... | Next: GrSetPalette() ...


GEOS C Routines: GrSetPalette() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrSetLineAttr() ... | Next: GrSetTextMode() ...

GrSetPalette()

void	GrSetPalette(
        GStateHandle 		gstate,				/* subject GState */
        SetPalType 		type,				/* SPT_DEFAULT or SPT_CUSTOM */
        const RGBValue 		*buffer,				/* array of palette entries */
        word		index, 				/* First element to change */
        word		numEntries);				/* number of entries in array */

Set one or more entries in a palette, a window's color lookup table.

Include: graphics.h

GrSetPaletteEntry()

void	GrSetPaletteEntry(
        GStateHandle 		gstate,				/* subject GState */
        word		index,				/* index in palette to set */
        word		red,				/* new RGB color values for entry */
        word		green,
        word		blue);

Set one entry in a palette, a GState's color lookup table.

Include: graphics.h

GrSetPrivateData()

void	GrSetPrivateData(
        GStateHandle		gstate,				/* subject GState */
        word		dataAX,				/* data to set */
        word		dataBX,
        word		dataCX,
        word		dataDX);

Set the private data for a GState.

Include: graphics.h

GrSetStrokePath()

void	GrSetStrokePath(
        GStateHandle gstate);				/* subject GState */

Replace a GState's path with the path resulting from stroking the original path. Note that this stroked path may be drawn, but may not be used for clipping.

Include: graphics.h

GrSetSubscriptAttr()

void 	GrSetSubscriptAttr(
        GStateHandle 		gstate,				/* subject GState */
        ScriptAttrAsWord 		attrs);				/* new subscript percentages */

Get the attributes used when drawing subscript characters.

Include: font.h

GrSetSuperscriptAttr()

void 	GrSetSuperscriptAttr(
        GStateHandle 		gstate,				/* subject GState */
        ScriptAttrAsWord 		attrs);				/* new superscript percentages */

Get the attributes used when drawing superscript characters.

Include: font.h

GrSetTextAttr()

void	GrSetTextAttr(
        GStateHandle		gstate,				/* subject GState */
        const	TextAttr	* ta);				/* pointer to text attributes */

Set all attributes used when drawing characters and text strings.

Include: graphics.h

GrSetTextColor()

void	GrSetTextColor(
        GStateHandle		gstate,				/* subject GState */
        ColorFlag		flag,				/* color flag */
        word		redOrIndex,				/* palette index or red RGB value */
        word		green,				/* green RGB value or zero */
        word		blue);				/* blue RGB value or zero */

Set the color used when drawing text.

Include: graphics.h

GrSetTextColorMap()

void	GrSetTextColorMap(
        GStateHandle		gstate,				/* subject GState */
        ColorMapMode 		colorMap);				/* new color mapping mode */

Set the mode used when trying to draw text in an unavailable color.

Include: graphics.h

GrSetTextMaskCustom()

void	GrSetTextMaskCustom(
        GStateHandle		gstate,				/* subject GState */
        const	DrawMask	* dm);				/* pointer to custom mask */

Set the drawing mask used when drawing text.

Include: graphics.h

GrSetTextMaskSys()

void	GrSetTextMaskSys(
        GStateHandle		gstate,				/* subject GState */
        SystemDrawMask 		sysDM);				/* new system draw mask */

Set the drawing mask used when drawing text.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrSetLineAttr() ... | Next: GrSetTextMode() ...


GEOS C Routines: GrSetTextMode() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrSetPalette() ... | Next: GrTestPointInPath() ...

GrSetTextMode()

void	GrSetTextMode(
        GStateHandle		gstate,					/* subject GState */
        TextMode 		bitsToSet,					/* TextMode flags to set */
        TextMode 		bitsToClear);					/* TextMode flags to clear */

Set the text mode associated with a GState. Using this routine, it is possible to change the vertical offset used when drawing text.

Include: graphics.h

GrSetTextPattern()

void 	GrSetTextPattern(
        GStateHandle 		gstate,				/* subject GState */
        GraphicPattern 		pattern);				/* new graphic pattern for text */

Set the graphic pattern used when drawing text.

Include: graphics.h

GrSetTextSpacePad()

void	GrSetTextSpacePad(
        GStateHandle		gstate,				/* subject GState */
        WWFixedAsDWord		padding);				/* new space padding */

Set the space pad used when drawing text strings.

Include: graphics.h

GrSetTextStyle()

void	GrSetTextStyle(
        GStateHandle		gstate,				/* subject GState */
        TextStyle 		bitsToSet,				/* TextStyle flags to set */
        TextStyle		bitsToClear);				/* TextStyle flags to clear */

Set the style to use when drawing text.

Include: graphics.h

GrSetTrackKern()

void	GrSetTrackKern(
        GStateHandle		gstate,				/* subject GState */
        word		tk);				/* degree of track kerning */

Set the track kerning to use when drawing text strings.

Include: graphics.h

GrSetTransform()

void	GrSetTransform(
        GStateHandle		gstate,				/* subject GState */
        const TransMatrix 		* tm);				/* new transformation matrix */

Set the GState's coordinate transformation.

Include: graphics.h

Update the VM file associated with a GState (this may apply when working

GrSetVMFile()

void	GrSetVMFile(
        GStateHandle		gstate,				/* subject GState */
        VMFileHandle 		vmFile);				/* new transformation matrix */

with certain kinds of bitmaps and GStrings).

Include: graphics.h

GrSetWinClipPath()

void	GrSetWinClipPath(
        GStateHandle		gstate,				/* subject GState */
        PathCombineType		params,				/* how paths are combined */
        RegionFillRule		rule);				/* ODD_EVEN or WINDING */

Restrict the window's clipping region by intersecting it with the passed path.

Include: graphics.h

GrSetWinClipRect()

void	GrSetWinClipRect(
        GStateHandle		gstate,				/* subject GState */
        PathCombineType		flags,				/* how paths are combined */
        sword		left,				/* new clipping rectangle bounds */
        sword		top,
        sword		right,
        sword		bottom);

Restrict the window's clipping region by intersecting it with the passed rectangle.

Include: graphics.h

GrSqrRootWWFixed()

WWFixedAsDWord GrSqrRootWWFixed(
        WWFixedAsDWord i);			/* number to get the square root of */

Compute the square root of a fixed point number.

Include: graphics.h

GrTestPath()

Boolean	GrTestPath(
        GStateHandle		gstate,				/* subject GState */
        GetPathType		ptype);				/* Type of path to check for */

Determine whether the GState has a path of the specified type.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrSetPalette() ... | Next: GrTestPointInPath() ...


GEOS C Routines: GrTestPointInPath() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrSetTextMode() ... | Next: GrUDivWWFixed() ...

GrTestPointInPath()

Boolean	GrTestPointInPath(
        GStateHandle		gstate,				/* subject GState */
        word		xPos,				/* point to test */
        word		yPos,
        RegionFillRule		rule);				/* ODD_EVEN or WINDING */

Determine whether the passed point falls in the interior of the GState's path.

Include: graphics.h

GrTestPointInPolygon()

Boolean	GrTestPointInPolygon(
        GStateHandle		gstate,				/* subject GState */
        RegionFillRule		rule,				/* ODD_EVEN or WINDING */
        Point		* list,				/* array of points in polygon */
        word		numPoints,				/* number of points in array */
        sword		xCoord,				/* coordinates of point to test */
        sword		yCoord);

Determine whether the passed point lies in the interior of the passed polygon.

Include: graphics.h

GrTestPointInReg()

Boolean 	GrTestPointInReg(
        const	Region	* reg,				/* pointer to region */
        sword 		x,				/* coordinates of point to test */
        sword 		y,
        Rectangle		*boundingRect);					/* returned bounding rectangle,
                             * if point in region */

Determine whether a point lies within the passed region. If the point is not in the region, the return value is true .

Include: graphics.h

GrTestRectInMask()

TestRectReturnType GrTestRectInMask(
        GStateHandle 		gstate,
        sword 		left,
        sword 		top,
        sword 		right,
        sword 		bottom);

This routine determines whether the passed rectangular area falls within the clipping region.

Structures:

        typedef ByteEnum TestRectReturnType;
TRRT_OUT,			/* rectangle completely out of region */
TRRT_PARTIAL,			/* rectangle partially in region */
TRRT_IN 			/* rectangle completely in region */

Include: graphics.h

GrTestRectInReg()

TestRectReturnType GrTestRectInReg(
        const Region		* reg			/* pointer to region */
        sword		left,			/* bounds of rectangle to be tested */
        sword		top,
        sword		right,
        sword		bottom);

Determine whether a rectangle lies within the passed region.

Structures:

        typedef ByteEnum TestRectReturnType;
TRRT_OUT,			/* rectangle completely out of region */
TRRT_PARTIAL,			/* rectangle partially in region */
TRRT_IN 			/* rectangle completely in region */

Include: graphics.h

GrTextWidth()

word	GrTextWidth(
        GStateHandle		gstate,				/* subject GState */
        const	Chars	* str,				/* text string to check */
        word		size);				/* maximum number of
                         * characters to check */

Compute the space the passed text string would require in a line of text. Use GrGetTextBounds() to determine the area necessary to render the text.

Include: graphics.h

GrTextWidthWWFixed()

WWFixedAsDWord 	GrTextWidthWWFixed( /* returns width << 16 */
        GStateHandle		gstate,				/* subject GState */
        const	Chars	* str,				/* text string to check */
        word		size)				/* maximum number of
                         * characters to check */

Compute the spacing the passed text string would require in a line of text, accurate to a fraction of a point. Use GrGetTextBounds() to determine the area necessary to render the text.

Include: graphics.h

GrTransform()

XYValueAsDWord 	GrTransform(
        GStateHandle		gstate,				/* subject GState */
        sword 		xCoord,				/* coordinates to transform */
        sword 		yCoord);

Apply the device's transformation to the passed point.

Include: graphics.h

GrTransformDWFixed()

void	GrTransformDWFixed(
        GStateHandle		gstate,				/* subject GState */
        PointDWFixed		* coord);				/* coordinates to transform */

Apply the device's transformation to the passed point.

Include: graphics.h

GrTransformDWord()

void 	GrTransformDWord(
        GStateHandle		gstate,				/* subject GState */
        sdword		xCoord,				/* coordinates to transform */
        sdword		yCoord,
        PointDWord		* deviceCoordinates);
            /* pointer to returned devide coordinates */

Apply the device's transormation to the passed point.

Include: graphics.h

GrTransformWWFixed()

void	GrTransformWWFixed(
        GStateHandle		gstate,				/* subject GState */
        WWFixedAsDWord		xPos,				/* coordinates to transform */
        WWFixedAsDWord		yPos,
        PointWWFixed		* deviceCoordinates);
                    /* pointer to returned devide coordinates */

Apply the device's transormation to the passed point.

Include: graphics.h

| Up: GEOS SDK TechDocs | Up | Prev: GrSetTextMode() ... | Next: GrUDivWWFixed() ...


GEOS C Routines: GrUDivWWFixed() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrTestPointInPath() ... | Next: GSCloseSubPath() ...

GrUDivWWFixed()

WWFixedAsDWord GrUDivWWFixed(
        WWFixedAsDWord		dividend,
        WWFixedAsDWord		divisor);

Compute an unsigned division of two fixed point numbers.

Include: graphics.h

GrUncompactBitmap()

VMBlockHandle GrUncompactBitmap(
        VMFileHandle 		srcFile,
        VMBlockHandle 		srcBlock,
        VMFileHandle 		destFile);

This routine uncompacts the data of a huge bitmap.

GrUntransform()

XYValueAsDWord GrUnTransformCoord(
        GStateHandle		gstate,				/* subject GState */
        sword		xCoord,				/* coordinates to untransform */
        sword		yCoord);

Apply the reverse of the device's transformation to the passed point.

Include: graphics.h

GrUntransformDWFixed()

void	GrUnTransCoordDWFixed(
        GStateHandle		gstate,				/* subject GState */
        PointDWFixed		* coord);				/* coordinates to untransform */

Apply the reverse of the device's transformation to the passed point.

Include: graphics.h

GrUntransformDWord()

void	GrUnTransformExtCoord(
        GStateHandle		gstate,				/* subject GState */
        sdword		xCoord,				/* coordinates to untransform */
        sdword		yCoord,
        PointDWord		* documentCoordinates);
                    /* pointer to returned devide coordinates *

Apply the reverse of the device's transformation to the passed point.

Include: graphics.h

GrUntransformWWFixed()

void	GrUnTransCoordWWFixed(
        GStateHandle		gstate,				/* subject GState */
        WWFixedAsDWord		xPos,				/* coordinates to untransform */
        WWFixedAsDWord		yPos,
        PointWWFixed		* documentCoordinates);
                    /* pointer to returned devide coordinates *

Apply the reverse of the device's transformation to the passed point.

Include: graphics.h

GSApplyRotation()

#define GSApplyRotation(angle_f) \
        GR_APPLY_ROTATION,GOC_WWF(angle_f

This GString creation macro applies a passed WWFixed value angle to the coordinate transformation.

Include: gstring.h

GSApplyScale()

#define GSApplyScale(x_f,y_f) \
        GR_APPLY_SCALE,GOC_WWF(x_f),GOC_WWF(y_f)

This GString creation macro applies the passed scale (defined in terms of two WWFixed numbers, one each for the x and y scale factors) to the coordinate transformation.

Include: gstring.h

GSApplyTransform()

#define GSApplyTransform(e11_f,e12_f,e21_f,e22_f,e31_df,e32_df) \
        GR_APPLY_TRANSFORM,GOC_WWF(e11_f),GOC_WWF(e12_f), \
        GOC_WWF(e21_f),GOC_WWF(e22_f), \
        GOC_DWF(e31_df), GOC_DWF(e32_df)

This GString creation macro applies the passed translation matrix to the coordinate transformation. The translation matrix is determined in terms of six of its elements, four defined by WWFixed values and two by DWFixed values.

Include: gstring.h

GSApplyTranslation()

#define GSApplyTranslation(x_f,y_f) \
        GR_APPLY_TRANSLATION,GOC_WWF(x_f),GOC_WWF(y_f)

This GString creation macro applies the passed translation (defined in terms of two WWFixed numbers, one each for the x and y translations) to the coordinate transformation.

Include: gstring.h

GSApplyTranslationDWord()

#define GSApplyTranslationDWord(x_sdw,y_sdw) \
        GR_APPLY_TRANSLATION_DWORD,GOC_SDW(x_sdw), GOC_SDW(y_sdw)

This GString creation macro applies a large translation (defined in terms of two dword-sized values, one each for the x and y translations) to the transformation.

Include: gstring.h

GSBeginPath()

#define GSBeginPath(flags) GR_BEGIN_PATH,(flags)

This GString creation macro signals that the drawing commands that follow, up until the next GSEndPath() , describe a path. It takes one argument, a PathCombineType .

Include: gstring.h

GSBrushPolyline()

#define GSBrushPolyline(width_b,height_b,pts_b) \
        GR_BRUSH_POLYLINE, GOC_WORD(pts_b), (width_b), (height_b)

This GString creation macro brushes a polyline. It takes three arguments: a word containing the number of points in the polyline and the byte-size width and height of the brush rectangle. Following this macro should appear the points of the polyline, each point passed via a GOC_POINT macro.

Include: gstring.h

| Up: GEOS SDK TechDocs | Up | Prev: GrTestPointInPath() ... | Next: GSCloseSubPath() ...


GEOS C Routines: GSCloseSubPath() ...

| Up: GEOS SDK TechDocs | Up | Prev: GrUDivWWFixed() ... | Next: GSDrawEllipse() ...

GSCloseSubPath()

#define GSCloseSubPath() GR_CLOSE_SUB_PATH

This GString creation macro geometrically closes the currently open path. You must still use GSEndPath() to signal that you are done defining the path.

Include: gstring.h

GSComment()

#define GSComment(size_b) \
        GR_COMMENT, GOC_WORD(size_b

This GString creation macro signals the start of a comment within the GString. It takes one argument, the length of the comment in bytes. Following this macro you should place the comment data.

Include: gstring.h

GSCreatePalette()

#define GSCreatePalette() GR_CREATE_PALETTE

This GString creation macro creates a custom palette.

Include: gstring.h

GSDestroyPalette()

#define GSDestroyPalette() GR_DESTROY_PALETTE

This GString creation macro destroys the custom palette, if any.

Include: gstring.h

GSDrawArc()

#define GSDrawArc(close_enum,x1_w,y1_w,x2_w,y2_w,ang1_w,ang2_w) \
        GR_DRAW_ARC,GOC_WORD(close_enum), \
GOC_SW(x1_w), GOC_SW(y1_w), \
GOC_SW(x2_w), GOC_SW(y2_w), \
GOC_SW(ang1_w),GOC_SW(ang2_w)

This GString creation macro draws an arc. It takes several arguments. First it takes an ArcCloseType value. Next it takes the boundaries of the arc's base ellipse. Finally, it takes starting and ending angles for the arc.

Include: gstring.h

GSDrawArc3Point()

#define GSDrawArc3Point(close, x1, y1, x2, y2, x3, y3) \
        GR_DRAW_ARC_3POINT,GOC_WORD(close),GOC_WWF(x1), \
GOC_WWF(y1), GOC_WWF(x2), GOC_WWF(y2), \
GOC_WWF(x3), GOC_WWF(y3)

This GString creation macro draws a three-point arc. It takes several arguments: an ArcCloseType and the coordinates of the three points defining the arc, each expressed as a WWFixed value.

Include: gstring.h

GSDrawArc3PointTo()

#define GSDrawArc3Point(close, x1, y1, x2, y2, x3, y3) \
        GR_DRAW_ARC_3POINT,GOC_WORD(close),GOC_WWF(x1), \
GOC_WWF(y1), GOC_WWF(x2), GOC_WWF(y2), \
GOC_WWF(x3), GOC_WWF(y3)

This GString creation macro draws a three-point arc with the current position acting as one of the endpoints. It takes several arguments: an ArcCloseType and the coordinates of the remaining defining points of the arc, each expressed as a WWFixed value.

Include: gstring.h

GSDrawBitmap()

#define GSDrawBitmap(x_w,y_w,w_w) \
        GR_DRAW_BITMAP, GOC_SW(x_w), \
GOC_SW(y_w), GOC_WORD(w_w)

This GString creation macro draws a bitmap. It takes three arguments: the coordinates at which to draw the bitmap and the size of the bitmap's data. This macro should be followed by the bitmap's data: a Bitmap structure and the mask and color data for the bitmap.

Include: gstring.h

GSDrawBitmapAtCP()

#define GSDrawBitmapAtCP(bsize_w) \
        GR_DRAW_BITMAP_CP, GOC_WORD(bsize_w)

This GString creation macro draws a bitmap at the current position. It takes one argument, the size of the bitmap data in bytes. This macro should be followed by the bitmap's data: a Bitmap structure and the mask and color data for the bitmap.

Include: gstring.h

GSDrawBitmapOptr()

#define GSDrawBitmapOptr(x_w,y_w,o_ptr) \
        GR_DRAW_BITMAP_OPTR,GOC_SW(x_w), \
GOC_SW(y_w),GOC_OPTR(o_ptr)

This GString creation macro draws a bitmap at the passed coordinates; the data for said bitmap should be stored in a chunk referenced by the passed optr.

Include: gstring.h

GSDrawCBitmap()

#define GSDrawCBitmap(x_w,y_w,slice1size_w,totalSize) \
        GR_DRAW_BITMAP,GOC_SW(x_w), GOC_SW(y_w), \
GOC_WORD(slice1size_w)

This GString creation macro draws a complex bitmap. It takes four arguments: the x and y coordinates at which to draw the bitmap, the size of one slice of the bitmap's mask and color data, and the total size of the bitmap's data. This macro should be followed by the bitmap's data: a CBitmap structure followed by the mask, color, and any other associated data.

Include: gstring.h

GSDrawChar()

#define GSDrawChar(c,x1_w,y1_w) \
        GR_DRAW_CHAR,(c),GOC_SW(x1_w),GOC_SW(y1_w)

This GString creation macro draws a text character. It takes three arguments: the Chars value of the character and the x and y coordinates at which to draw the character.

Include: gstring.h

GSDrawCharAtCP()

#define GSDrawCharAtCP(c) \
        GR_DRAW_CHAR_CP,(c)

This GString creation macro draws a single character at the current position. It takes one argument, the Chars value of the character to draw.

Include: gstring.h

GSDrawCurve()

#define GSDrawCurve(x1_sw,y1_sw,x2_sw,y2_sw,x3_sw,y3_sw,x4_sw,y4_sw) \
        GR_DRAW_CURVE,GOC_SW(x1_sw),GOC_SW(y1_sw), \
GOC_SW(x2_sw),GOC_SW(y2_sw), \
GOC_SW(x3_sw),GOC_SW(y3_sw),GOC_SW(x4_sw),GOC_SW(y4_sw)

This GString creation macro draws a Bezier curve. It takes eight arguments consisting of the coordinates for the curve's anchor and control points.

Include: gstring.h

GSDrawCurveTo()

#define GSDrawCurveTo(x2_sw,y2_sw,x3_sw,y3_sw,x4_sw,y4_sw) \
        GR_DRAW_CURVE_TO,GOC_SW(x2_sw),GOC_SW(y2_sw), \
GOC_SW(x3_sw),GOC_SW(y3_sw), \
GOC_SW(x4_sw),GOC_SW(y4_sw)

This GString creation macro draws a Bezier curve, using the current position as one endpoint. It takes six arugments, the coordinates of the remaining anchor and control points.

Include: gstring.h

| Up: GEOS SDK TechDocs | Up | Prev: GrUDivWWFixed() ... | Next: GSDrawEllipse() ...


GEOS C Routines: GSDrawEllipse() ...

| Up: GEOS SDK TechDocs | Up | Prev: GSCloseSubPath() ... | Next: GSDrawSpline() ...

GSDrawEllipse()

#define GSDrawEllipse(x1_w,y1_w,x2_w,y2_w) \
        GR_DRAW_ELLIPSE,GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws an ellipse with bounds defined by the passed coordinates.

Include: gstring.h

GSDrawHLine()

#define GSDrawHLine(x1_w,y1_w,x2_w) \
        GR_DRAW_HLINE,GOC_SW(x1_w), \
GOC_SW(y1_w),GOC_SW(x2_w)

This GString creation macro draws a horizontal line. It takes three word-length arguments: the coordinates of the first endpoint and the x coordinate of the second endpoint.

Include: gstring.h

GSDrawHLineTo()

#define GSDrawHLineTo(x2_w) \
        GR_DRAW_HLINE_TO,GOC_SW(x2_w)

This GString creation macro draws a horizontal line from the current position to the passed x coordinate.

Include: gstring.h

GSDrawLine()

#define GSDrawLine(x1_w,y1_w,x2_w,y2_w) \
        GR_DRAW_LINE,GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a line between the two passed points. The points are defined in terms of their x and y coordinates, passed as word values.

Include: gstring.h

GSDrawLineTo()

#define GSDrawLineTo(x2_w,y2_w) \
        GR_DRAW_LINE_TO,GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a line from the current postion to the passed point. The endpoint of the line is passed via two words describing its x and y coordinates.

Include: gstring.h

GSDrawPath()

#define GSDrawPath() GR_DRAW_PATH

This GString creation macro draws the current path.

Include: gstring.h

GSDrawPoint()

#define GSDrawPoint(x1_w,y1_w) \
        GR_DRAW_POINT,GOC_SW(x1_w),GOC_SW(y1_w)

This GString creation macro draws a dot at the passed coordinates.

Include: gstring.h

GSDrawPointAtCP()

#define GSDrawPointAtCP() \
                GR_DRAW_POINT_CP

This GString creation macro draws a dot at the current drawing position.

Include: gstring.h

GSDrawPolygon()

#define GSDrawPolygon(count_w) \
        GR_DRAW_POLYGON,GOC_WORD(count_w)

This GString creation macro draws the outline of a polygon. It takes one argument, the number of points in the polygon. Follow the macro with the points of the polygon, each passed via a GOC_POINT macro.

Include: gstring.h

GSDrawPolyline()

#define GSDrawPolyline(count_w) \
        GR_DRAW_POLYLINE,GOC_WORD(count_w)

This GString creation macro draws a polyline--a spline with corners. The macro takes one argument: the number of points in the polyline. The point data should follow the macro, each point passed via a GOC_POINT macro.

Include: gstring.h

GSDrawRect()

#define GSDrawRect(x1_w,y1_w,x2_w,y2_w) \
        GR_DRAW_RECT,GOC_SW(x1_w),GOC_SW(y1_w), \
        GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a rectangle with bounds defined by the passed coordinates, each coordinate a word-length value.

Include: gstring.h

GSDrawRectTo()

#define GSDrawRectTo(x2_w,y2_w) \
        GR_DRAW_RECT_TO,GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a rectangle with one corner at the current position and the other corner at the point defined by the passed coordinates.

Include: gstring.h

GSDrawRelArc3PointTo()

#define GSDrawRelArc3PointTo(close, x2, y2, x3, y3) \
        GR_DRAW_REL_ARC_3POINT_TO,GOC_WORD(close), \
GOC_WWF(x2), GOC_WWF(y2), GOC_WWF(x3), GOC_WWF(y3)

This GString creation macro draws a three-point arc with the current position acting as one endpoint and the other points coordinates specified as offsets from the coordinates of the current position. In addition to these offsets, this macro has one more argument: an ArcCloseType .

Include: gstring.h

GSDrawRelCurveTo()

#define GSDrawRelCurveTo(x2_sw,y2_sw,x3_sw,y3_sw,x4_sw,y4_sw) \
        GR_DRAW_REL_CURVE_TO,GOC_SW(x2_sw),GOC_SW(y2_sw), \
GOC_SW(x3_sw),GOC_SW(y3_sw), \
GOC_SW(x4_sw),GOC_SW(y4_sw)

This GString creation macro draws a Bezier curve. It uses the current position as one endpoint of the curve; it takes six arguments: the coordinates of the other points of the curve expressed as offsets from the coordinates of the current position.

Include: gstring.h

GSDrawRoundRect()

#define GSDrawRoundRect(x1_w,y1_w,x2_w,y2_w,r_w) \
        GR_DRAW_ROUND_RECT,GOC_WORD(r_w), \
GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a rounded rectangle with bounds specified by its first four arguments and corner radius specified by its final argument. All arguments are word-length values except the radius, which is a word.

Include: gstring.h

GSDrawRoundRectTo()

#define GSDrawRoundRectTo(x2_w,y2_w,r_w) \
        GR_DRAW_ROUND_RECT_TO,GOC_WORD(r_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a rounded rect with bounds defined by the coordinates of the current position and the coordinates of the passed point; the corner radius is specified by the macro's last argument.

Include: gstring.h

| Up: GEOS SDK TechDocs | Up | Prev: GSCloseSubPath() ... | Next: GSDrawSpline() ...


GEOS C Routines: GSDrawSpline() ...

| Up: GEOS SDK TechDocs | Up | Prev: GSDrawEllipse() ... | Next: GSFillBitmap() ...

GSDrawSpline()

#define GSDrawSpline(count_w) \
        GR_DRAW_SPLINE,GOC_WORD(count_w)

This GString creation macro draws a spline. It takes one argument, the number of points in the spline. Follow this macro with the list of points for the spline; each point should be defined by means of a GOC_POINT macro.

Include: gstring.h

GSDrawSplineTo()

#define GSDrawSplineTo(count_w) \
                GR_DRAW_SPLINE_TO,GOC_WORD(count_w)

This GString creation macro draws a spline using the current postion as one endpoint. It takes one argument, the number of points in the spline. Follow this macro with the list of points for the spline; each point should be defined by means of a GOC_POINT macro.

Include: gstring.h

GSDrawText()

#define GSDrawText(x1_w,y1_w) \
        GR_DRAW_TEXT,GOC_SW(x1_w),GOC_SW(y1_w)

This GString creation macro draws a string of text. Follow the macro with the string of text like so:

GSDrawText(10,10),"foo",

Include: gstring.h

GSDrawTextAtCP()

#define GSDrawTextAtCP() GR_DRAW_TEXT_CP

This GString creation macro draws a string of text at the current position. Follow the macro with the text to draw like so:

GSDrawTextAtCP(),"bar",

Include: gstring.h

GSDrawTextOptr()

#define GSDrawTextOptr(x_w,y_w,o_ptr) \
        GR_DRAW_TEXT_OPTR, GOC_SW(x_w), \
GOC_SW(y_w),GOC_OPTR(o_ptr)

This GString creation macro draws a string of text. It takes three arguments: the x and y coordinates at which to start drawing the text and an optr to the chunk in which the text is stored.

Include: gstring.h

GSDrawVLine()

#define GSDrawVLine(x1_w,y1_w,y2_w) \
        GR_DRAW_VLINE,GOC_SW(x1_w), \
GOC_SW(y1_w),GOC_SW(y2_w)

This GString creation macro draws a vertical line. It takes three arguments: the coordinates of the first endpoint and the y coordinate of the second endpoint.

Include: gstring.h

GSDrawVLineTo()

#define GSDrawVLineTo(y2_w) \
        GR_DRAW_VLINE_TO,GOC_SW(y2_w)

This GString creation macro draws a vertical line from the current position to the passed y coordinate.

Include: gstring.h

GSEndPath()

#define GSEndPath()                             GR_END_PATH

This GString creation macro signals the end of path-defining commands; from here on, drawing commands will draw instead of specifying path strokes.

Include: gstring.h

GSEndString()

#define GSEndString() \
        GR_END_GSTRING

This GString definition macro marks the end of a graphics string.

Include: gstring.h

GSEscape()

#define GSEscape(esc_w,size_w) \
        GR_ESCAPE,GOC_WORD(esc_w),GOC_WORD(size_w)

This GString creation macro inserts an escape code into the GString. The macro's first argument is a word-length escape code. The second argument is the size of any additional escape code data which follows, in bytes. Following this macro, you may place any amount of additional data to associate with the escape code.

Include: gstring.h

GSFillArc()

#define GSFillArc(close_enum,x1_w,y1_w,x2_w,y2_w,ang1_w,ang2_w) \
        GR_FILL_ARC,GOC_WORD(close_enum), \
GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w),GOC_SW(ang1_w), \
GOC_SW(ang2_w)

This GString creation macro draws a filled arc. It takes several arguments: an ArcCloseType value, the bounding coordinates of the arc's base ellipse, and the arc's starting and ending angles.

Include: gstring.h

GSFillArc3Point()

#define GSFillArc3Point(close, x1, y1, x2, y2, x3, y3) \
        GR_FILL_ARC_3POINT,GOC_WORD(close),GOC_WWF(x1), GOC_WWF(y1),\
GOC_WWF(x2), GOC_WWF(y2), GOC_WWF(x3), GOC_WWF(y3)

This GString creation macro fills a three-point arc. It takes several arguments: an ArcCloseType and the coordinates of the points defining the arc.

Include: gstring.h

GSFillArc3PointTo()

#define GSFillArc3PointTo(close, x2, y2, x3, y3) \
        GR_FILL_ARC_3POINT_TO,GOC_WORD(close), \
GOC_WWF(x2), GOC_WWF(y2), GOC_WWF(x3), GOC_WWF(y3)

This GString creation macro fills a three-point arc. The current position acts as one of the arc's endpoints, with the macro's arguments specifying the coordinates of the other points. The macro has one other argument: an ArcCloseType .

Include: gstring.h

| Up: GEOS SDK TechDocs | Up | Prev: GSDrawEllipse() ... | Next: GSFillBitmap() ...


GEOS C Routines: GSFillBitmap() ...

| Up: GEOS SDK TechDocs | Up | Prev: GSDrawSpline() ... | Next: GSMoveTo() ...

GSFillBitmap()

#define GSFillBitmap(x_w,y_w,bsize_w) \
        GR_FILL_BITMAP,GOC_SW(x_w), \
GOC_SW(y_w),GOC_WORD(bsize_w)

This GString creation macro draws a bitmap using the area-filling color. It will draw the bitmap at the passed coordinates; the macro's final argument is the size of the bitmap's data. Following the macro you should place the bitmap's data: first a Bitmap structure, then a number of bytes containing the mask and color values for the bitmap.

Include: gstring.h

GSFillBitmapAtCP()

#define GSFillBitmapAtCP(bsize_w) \
        GR_FILL_BITMAP_CP, GOC_WORD(bsize_w)

This GString creation macro draws a bitmap using the area-filling color at the current position. It takes one argument, the size of the bitmap's data. Following the macro you should place the bitmap's data: first a Bitmap structure, then a number of bytes containing the mask and color values for the bitmap.

Include: gstring.h

GSFillBitmapOptr()

#define GSFillBitmapOptr(x_w,y_w,o_ptr) \
        GR_FILL_BITMAP_OPTR,GOC_SW(x_w),GOC_SW(y_w),GOC_OPTR(o_ptr)

This GString creation macro draws a bitmap using the area-filling color. It does so at the passed coordinates; the bitmap's data should be stored in the chunk referenced by the passed optr.

Include: gstring.h

GSFillEllipse()

#define GSFillEllipse(x1_w,y1_w,x2_w,y2_w) \
        GR_FILL_ELLIPSE,GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a filled ellipse. It takes four arguments, the bounding coordinates of the ellipse.

Include: gstring.h

GSFillPath()

#define GSFillPath(region_fill_rule) \
        GR_FILL_PATH,(region_fill_rule)

This GString creation macro fills the current path using the passed RegionFillRule value.

Include: gstring.h

GSFillPolygon()

#define GSFillPolygon(count_w,fillrule_enum) \
        GR_FILL_POLYGON,GOC_WORD(count_w),(fillrule_enum)

This GString creation macro draws a filled polygon. It takes two arguments, the number of points in the polygon and a RegionFillRule value. This macro should be followed by the points of the polygon, each passed via a GOC_POINT macro.

Include: gstring.h

GSFillRect()

#define GSFillRect(x1_w,y1_w,x2_w,y2_w) \
        GR_FILL_RECT,GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a filled rectangle. The macro takes four arguments, the bounds of the rectangle.

Include: gstring.h

GSFillRectTo()

#define GSFillRectTo(x2_w,y2_w) \
        GR_FILL_RECT_TO ,GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a filled rectangle with the current position defining one corner and the opposite corner defined by the passed coordinates.

Include: gstring.h

GSFillRoundRect()

#define GSFillRoundRect(x1_w,y1_w,x2_w,y2_w,r_w) \
        GR_FILL_ROUND_RECT,GOC_WORD(r_w),GOC_SW(x1_w), \
GOC_SW(y1_w),GOC_SW(x2_w), \
GOC_SW(y2_w)

This GString creation macro draws a filled rounded rectangle. It takes five arguments; the first four give the bounding coordinates of the rectangle; the final argument is the corner radius.

Include: gstring.h

GSFillRoundRectTo()

#define GSFillRoundRectTo(x2_w,y2_w,r_w) \
        GR_FILL_ROUND_RECT_TO,GOC_WORD(r_w),GOC_SW(x2_w), GOC_SW(y2_w)

This GString creation macro draws a filled rounded rectangle. The rectangle's bounding coordinates are defined by the the current position and the two passed coordinates; its corner radius is defined by the macro's final argument.

Include: gstring.h

GSInitDefaultTransform()

#define GSInitDefaultTransform() \
        GR_INIT_DEFAULT_TRANSFORM

This GString creation macro initializes the GState's default transformation to hold the value of the current coordinate transformation.

Include: gstring.h

GSLabel()

#define GSLabel(w) \
        GR_LABEL, GOC_WORD(w)

This GString definition macro creates a label in the GString. This macro takes one argument, a word which acts as the label's token. By passing GSC_LABEL to GrDrawGString() or another GString-traversing command, you may use this label as something analogous to the label used by a goto command.

Include: gstring.h

| Up: GEOS SDK TechDocs | Up | Prev: GSDrawSpline() ... | Next: GSMoveTo() ...


GEOS C Routines: GSMoveTo() ...

| Up: GEOS SDK TechDocs | Up | Prev: GSFillBitmap() ... | Next: GSSetCustomLineMask() ...

GSMoveTo()

#define GSMoveTo(x1_w,y1_w) \
        GR_MOVE_TO, GOC_SW(x1_w), GOC_SW(y1_w)

This GString creation macro updates the current position to the passed coordinates.

Include: gstring.h

GSNewPage()

#define GSNewPage(pageEndCommand) \
        GR_NEW_PAGE,(pageEndCommand)

This GString creation macro signals the end of a page. The pageEndCommand argument should be a PageEndCommand value.

Include: gstring.h

GSNullOp()

#define GSNullOp()
        GR_NULL_OP

This GString creation macro inserts a null-operation element into the GString.

Include: gstring.h

GSRelMoveTo()

#define GSRelMoveTo(x1_wwf,y1_wwf) \
        GR_REL_MOVE_TO, GOC_WWF(x1_wwf),GOC_WWF(y1_wwf)

This GString creation macro updates the current position to a set of coordinates specified as offsets from those of the old current position.

Include: gstring.h

GSRestoreState()

#define GSRestoreState() \
        GR_RESTORE_STATE

This GString creation macro causes a GState saved by GSSaveState() to be restored.

Include: gstring.h

GSRestoreTransform()

#define GSRestoreTransform() \
        GR_RESTORE_TRANSFORM

This GString creation macro restores a transformation matrix previously saved via GSSaveTransform() .

Include: gstring.h

GSSaveState()

#define GSSaveState()\
        GR_SAVE_STATE

This GString creation macro causes the GState to be saved so that it may be restored by a subsequent GSRestoreState() .

Include: gstring.h

GSSaveTransform()

#define GSSaveTransform() \
        GR_SAVE_TRANSFORM

This GString creation macro saves the current tranformation matrix which may be retrieved by means of a GSRestoreTransform() .

Include: gstring.h

GSSetAreaAttr()

#define GSSetAreaAttr(r,g,b,mode,mask) \
                GR_SET_AREA_ATTR,(CF_RGB),(r),(g),(b),(mask),(mode)

This GString creation macro sets the AreaAttr value to use when filling areas. It takes several arguments: the red, green, and blue components of the color; the ColorMapMode to use when trying to draw in an unavailable color, and the mask to use.

Include: gstring.h

GSSetAreaColor()

#define GSSetAreaColor(r,g,b) GR_SET_AREA_COLOR,(r),(g),(b)

This GString creation macro specifies a new area-filling color in terms of its red, green, and blue components.

Include: gstring.h

GSSetAreaColorIndex()

#define GSSetAreaColorIndex(index) GR_SET_AREA_COLOR_INDEX,(index)

This GString creation macro specifies the new area-filling color by means of its index in the palette.

Include: gstring.h

GSSetAreaColorMap()

#define GSSetAreaColorMap(mode) GR_SET_AREA_COLOR_MAP,(mode)

This GString creation macro sets the ColorMapMode to use when trying to fill an area using an unavailable color.

Include: gstring.h

GSSetAreaMask()

#define GSSetAreaMask(index) GR_SET_AREA_MASK, (index)

This GString creation macro sets the mask to use when filling areas.

Include: gstring.h

GSSetAreaPattern()

#define GSSetAreaPattern(pattern) \
        GR_SET_AREA_PATTERN,GOC_PATTERN(pattern)

This GString creation macro sets the GraphicPattern to use when filling areas.

Include: gstring.h

GSSetClipPath()

#define GSSetClipPath(flags) GR_SET_CLIP_PATH,(flags)

This GString creation macro combines the current path with the clipping path using the passed PathCombineType .

Include: gstring.h

GSSetClipRect()

#define GSSetClipRect(path,rl_sw,rt_sw,rr_sw,rb_sw) \
        GR_SET_CLIP_RECT,GOC_SW(rl_sw), \
GOC_SW(rt_sw),GOC_SW(rr_sw),GOC_SW(rb_sw)

This GString creation macro sets a clipping rectangle. It takes five arguments: a PathCombineType and the bounding coordinates of the rectangle.

Include: gstring.h

GSSetCustomAreaMask()

#define GSSetCustomAreaMask(m1,m2,m3,m4,m5,m6,m7,m8) \
        GR_SET_CUSTOM_AREA_MASK, (m1), (m2), (m3), (m4), (m5), (m6), (m7),
(m8)

This GString creation macro specifies a custom bitmask to use when filling areas. It takes eight arguments, each a byte representing the pixels of one row.

Include: gstring.h

GSSetCustomAreaPattern()

#define GSSetCustomAreaPattern(pattern,size) \
        GR_SET_CUSTOM_AREA_PATTERN, GOC_PATTERN(pattern),GOC_WORD(size)

This GString creation macro sets up a custom pattern to use when filling areas. It takes two arguments: a GraphicPattern value and the size of the pattern's data. Following this macro should appear the pattern's data.

Include: gstring.h

| Up: GEOS SDK TechDocs | Up | Prev: GSFillBitmap() ... | Next: GSSetCustomLineMask() ...


GEOS C Routines: GSSetCustomLineMask() ...

| Up: GEOS SDK TechDocs | Up | Prev: GSMoveTo() ... | Next: GSSetLineEnd() ...

GSSetCustomLineMask()

#define GSSetCustomLineMask(m1,m2,m3,m4,m5,m6,m7,m8) \
        GR_SET_CUSTOM_LINE_MASK, (m1), (m2), (m3), (m4), (m5), (m6), (m7), \
(m8)

This GString creation macro specifies a new bitmap to use as the mask when drawing lines. It takes eight arguments, each a byte representing the pixels one one row.

Include: gstring.h

GSSetCustomLineStyle()

#define GSSetCustomLineStyle(index,count) \
        GR_SET_CUSTOM_LINE_STYLE,(index),(count)

This GString creation macro sets a custom style, or "dottedness," to use when drawing lines. It takes two arguments: the first is the index into the pattern to use, the second is the number of dash pairs in the line style. Following this macro should appear a DashPairArray containing the line style's data.

Include: gstring.h

GSSetCustomTextMask()

#define GSSetCustomTextMask(m1,m2,m3,m4,m5,m6,m7,m8) \
        GR_SET_CUSTOM_TEXT_MASK, (m1), (m2), (m3), (m4), (m5), (m6), (m7),
(m8)

This GString creation macro specifies a custom bitmask to use when rendering text. It takes eight arguments, each a byte representing one row of pixel data.

Include: gstring.h

GSSetCustomTextPattern()

#define GSSetCustomTextPattern(pattern,size) \
        GR_SET_CUSTOM_TEXT_PATTERN,GOC_PATTERN(pattern), GOC_WORD(size)

This GString creation macro sets a custom pattern to use when rendering text. It takes two arguments: a GraphicPattern value and the size of the pattern's data. Following this macro should appear the pattern's data.

Include: gstring.h

GSSetDefaultTransformation()

#define GSSetDefaultTransform() \
        GR_SET_DEFAULT_TRANSFORM

This GString creation macro replaces the current coordinate transformation with the default coordinate transformation.

Include: gstring.h

GSSetFont()

#define GSSetFont(id,size_wbf) \
                GR_SET_FONT,GOC_WBF(size_wbf),GOC_WORD(id)

This GString creation macro sets the font to use when rendering text. It takes two arguments: a FontID and a pointsize, expressed as a WBFixed number.

Include: gstring.h

GSSetFontWeight()

#define GSSetFontWeight(weight) GR_SET_FONT_WEIGHT,GOC_WORD(weight)

This GString creation macro sets the FontWeight to use when rendering text.

Include: gstring.h

GSSetFontWidth()

#define GSSetFontWidth(width) GR_SET_FONT_WIDTH,GOC_WORD(width)

This GString creation macro sets the FontWidth to use when rendering text.

Include: gstring.h

GSSetGStringBounds()

#define GSSetGStringBounds(x1_w,y1_w,x2_w,y2_w) \
        GR_SET_GSTRING_BOUNDS,GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro is useful for optimizations. When drawing your GString , the graphics system will take time to traverse your GString and determine its drawing bounds. If you compute those bounds ahead of time, you may pass them as arguments to this macro--if the graphics system encounters this GString element, it won't bother to traverse the rest of the GString , but will use the pre-computed values. Thus, if you use this GString element, you should place it at the beginning of your GString . The macro takes four word-length arguments, the coordinates of the bounds of the GString .

Include: gstring.h

GSSetLineAttr()

#define GSSetLineAttr(r,g,b,mode,mask,width,end,join,style) \
        GR_SET_LINE_ATTR,(CF_RGB),(r),(g),(b),(mask),(mode),(end),(join), \
(style) ,GOC_WWF(width)

This GString creation macro sets the LineAttr to use when drawing lines. It takes several arguments: the red, green, and blue components of the color to use; the ColorMapMode to use when trying to draw in an unavailable color, the drawing mask to use, the width, the LineEnd , the LineJoin , and the LineStyle .

Include: gstring.h

GSSetLineColor()

#define GSSetLineColor(r,g,b) GR_SET_LINE_COLOR,(r),(g),(b)

This GString creation macro sets the line-drawing color in terms of its red, green, and blue components.

Include: gstring.h

GSSetLineColorIndex()

#define GSSetLineColorIndex(color) GR_SET_LINE_COLOR_INDEX, (color)

This GString creation macro sets the line-drawing color by specifying which entry in the palette to use.

Include: gstring.h

GSSetLineColorMap()

#define GSSetLineColorMap(mode) GR_SET_LINE_COLOR_MAP,(mode)

This GString creation macro sets the ColorMapMode to use when trying to draw lines with an unavailable color.

Include: gstring.h

| Up: GEOS SDK TechDocs | Up | Prev: GSMoveTo() ... | Next: GSSetLineEnd() ...


GEOS C Routines: GSSetLineEnd() ...

| Up: GEOS SDK TechDocs | Up | Prev: GSSetCustomLineMask() ... | Next: GSSetSubscriptAttr() ...

GSSetLineEnd()

#define GSSetLineEnd(end) \
        GR_SET_LINE_END,(end)

This GString creation macro sets the LineEnd value to use when drawing endpoints of lines.

Include: gstring.h

GSSetLineJoin()

#define GSSetLineJoin(join) \
                GR_SET_LINE_JOIN,(join)

This GString creation macro sets the LineJoin value to use when drawing corners in polylines, polygons, and rectangles.

Include: gstring.h

GSSetLineMask()

#define GSSetLineMask(index) GR_SET_LINE_MASK, (index)

This GString creation macro sets the mask to use when drawing lines.

Include: gstring.h

GSSetLineStyle()

#define GSSetLineStyle(style,index) \
                GR_SET_LINE_STYLE,(style),(index)

This GString creation macro sets the style, or "dottedness," to use when drawing lines.

Include: gstring.h

GSSetLineWidth()

#define GSSetLineWidth(width_wwf) \
                GR_SET_LINE_WIDTH, GOC_WWF(width_wwf)

This GString creation macro sets the width to use when drawing lines. It takes a single argument, the width expressed as a WWFixed number.

Include: gstring.h

GSSetMiterLimit()

#define GSSetMiterLimit(limit_wwf) \
        GR_SET_MITER_LIMIT, GOC_WWF(limit_wwf)

This GString creation macro sets the miter limit to use when drawing mitered corners of rectangles, polylines, and polygons. This miter limit is expressed as a WWFixed value.

Include: gstring.h

GSSetMixMode()

#define GSSetMixMode(mode) GR_SET_MIX_MODE, (mode)

This GString creation macro sets the mix mode to use when drawing on top of existing drawings.

Include: gstring.h

GSSetNullPath()

#define GSSetNullPath() GR_SET_NULL_PATH

This GString creation macro clears the current path.

Include: gstring.h

GSSetNullTransform()

#define GSSetNullTransform() \
        GR_SET_NULL_TRANSFORM

This GString creation macro nullifies the coordinate transformation.

Include: gstring.h

GSSetPalette()

#define GSSetPalette(entry_count) GR_SET_PALETTE,(entry),(count)

This GString creation macro allows you to change several palette colors at once. It takes two arguments: the index of the first color entry to change, and the number of entries to change. Follow this macro with trios of bytes, each triplet specifying the red, green, and blue components of the colors.

GSSetPaletteEntry()

#define GSSetPaletteEntry(entry,r,g,b) \
        GR_SET_PALETTE_ENTRY GOC_WORD(entry),(r), (g),(b)

This GString creation macro changes a single entry in a custom palette. It takes four arguments: the index number of the entry and the red, green, and blue components of the new color for this entry.

Include: gstring.h

GSSetStrokePath()

#define GSSetStrokePath() GR_SET_STROKE_PATH

This GString creation macro replaces the current path with the path which results from stroking the current path with the current line attributes. This new path may be drawn, but may not be used for clipping.

Include: gstring.h

| Up: GEOS SDK TechDocs | Up | Prev: GSSetCustomLineMask() ... | Next: GSSetSubscriptAttr() ...


GEOS C Routines: GSSetSubscriptAttr() ...

| Up: GEOS SDK TechDocs | Up | Prev: GSSetLineEnd() ... | Next: HAL_COUNT() ...

GSSetSubscriptAttr()

#define GSSetSubscriptAttr(pos,size) \
        GR_SET_SUBSCRIPT_ATTR,(pos),(size)

This GString creation macro sets the vertical offset and size to use when rendering subscript text.

Include: gstring.h

GSSetSuperscriptAttr()

#define GSSetSuperscriptAttr(pos,size) \
        GR_SET_SUPERSCRIPT_ATTR,(pos),(size)

This GString creation macro sets the vertical offset and size to use when rendering superscript text.

Include: gstring.h

GSSetTextAttr()

#define GSSetTextAttr(cflag,r,g,b,mask,sSet,sReset,mSet,mReset,
sPad,id,pSize,trk Krn,fWeight,fWidth) \
        GR_SET_TEXT_ATTR,(cflag),(r),(g),(b),(mask),(sSet), \
(sReset),(mSet),(mReset),GOC_WWF(sPad), \
GOC_WORD(id), GOC_WWF(pSize),GOC_SW(trkKrn),(fWeight),(fWidth)

This GString creation macro sets the TextAttr structure to use when rendering text. It takes several arguments: the four components of a ColorQuad , a drawing mask, TextStyle flags to set and clear, TextMode flags to set and clear, a spacing pad, a FontID , a pointsize expressed as a WWFixed number, a track kerning value, a FontWeight , and a FontWidth .

Include: gstring.h

GSSetTextColor()

#define GSSetTextColor(r,g,b) GR_SET_TEXT_COLOR,(r),(g),(b)

This GString creation macro sets the text-rendering color by means of red, green, and blue component values.

Include: gstring.h

GSSetTextColorIndex()

#define GSSetTextColorIndex(index) GR_SET_TEXT_COLOR_INDEX,(index)

This GString creation macro specifies the text-rendering color as an index of the palette.

Include: gstring.h

GSSetTextColorMap()

#define GSSetTextColorMap(mode) GR_SET_TEXT_COLOR_MAP,(mode)

This GString creation macro sets the ColorMapMode to use when trying to render text in an unavailable color.

Include: gstring.h

GSSetTextMask()

#define GSSetTextMask(index) GR_SET_TEXT_MASK, (index)

This GString creation macro specifies the mask to use when rendering text.

Include: gstring.h

GSSetTextMode()

#define GSSetTextMode(set,reset) \
                GR_SET_TEXT_MODE,(set),(reset)

This GString creation macro sets and clears TextMode flags. It takes two arguments: the flags to set and the flags to clear.

Include: gstring.h

GSSetTextPattern()

#define GSSetTextPattern(pattern) \
        GR_SET_TEXT_PATTERN,GOC_PATTERN(pattern)

This GString creation macro sets the GraphicPattern to use when rendering text.

Include: gstring.h

GSSetTextSpacePad()

#define GSSetTextSpacePad(pad_wbf) \
                GR_SET_TEXT_SPACE_PAD, GOC_WBF(pad_wbf)

This GString creation macro specifies the space pad to use when rendering text. It takes one argument: the space padding value, expressed as a WBFixed number.

Include: gstring.h

GSSetTextStyle()

#define GSSetTextStyle(set,reset) \
                GR_SET_TEXT_STYLE,(set),(reset)

This GString creation macro changes the TextStyle flags to use when drawing text. It takes two arguments, the flags to set and the flags to clear.

Include: gstring.h

GSSetTrackKern()

#define GSSetTrackKern(degree) GR_SET_TRACK_KERN, GOC_SW(degree)

This GString creation macro sets the track kerning value to use when rendering text strings.

Include: gstring.h

GSSetTransform()

#define GSSetTransform(e11_f,e12_f,e21_f,e22_f,e31_df,e32_df) \
        GR_SET_TRANSFORM,GOC_WWF(e11_f), \
GOC_WWF(e12_f),GOC_WWF(e21_f), \
GOC_WWF(e22_f),GOC_DWF(e31_df),GOC_DWF(e32_df)

This GString creation macro sets the coordinate transformation. The new transformation is defined in terms of the elements of a transformation matrix, four WWFixed values and two DWFixed values.

Include: gstring.h

GSSetWinClipPath()

#define GSSetWinClipPath(flags) GR_SET_WIN_CLIP_PATH,(flags)

This GString creation macro combines the current path with the window clipping path, using the passed PathCombineType .

Include: gstring.h

GSSetWinClipRect()

#define GSSetWinClipRect(path,rl_sw,rt_sw,rr_sw,rb_sw) \
        GR_SET_WIN_CLIP_RECT,GOC_SW(rl_sw), \
GOC_SW(rt_sw),GOC_SW(rr_sw),GOC_SW(rb_sw)

This GString creation macro combines a rectangle with the existing window clipping path. It takes five arguments: a PathCombineType and the bounding coordinates of the rectangle.

Include: gstring.h

| Up: GEOS SDK TechDocs | Up | Prev: GSSetLineEnd() ... | Next: HAL_COUNT() ...


GEOS C Routines: HAL_COUNT() ...

| Up: GEOS SDK TechDocs | Up | Prev: GSSetSubscriptAttr() ... | Next: HandleV() ...

HAL_COUNT()

word	HAL_COUNT(
        dword	val);

This macro is provided for use with HugeArrayLock() . It extracts the lower word of the HugeArrayLock() return value. This is the number of elements in the Huge Array block after the locked one (counting that locked one).

HAL_PREV

word	HAL_PREV(
        dword	val);

This macro is provided for use with HugeArrayLock() . It extracts the upper word of the HugeArrayLock() return value. This is the number of elements in the Huge Array block before the locked one (counting that locked one).

HandleModifyOwner()

void	HandleModifyOwner(
        MemHandle		mh,			/* Handle of block to modify */
        GeodeHandle		owner);			/* Handle of block's new owner */

This routine changes the owner of the indicated global memory block. Note that this routine can be called only by a thread belonging to the block's original owner; that is, you can only use this routine to transfer ownership of a block from yourself to some other geode.

Include: heap.h

Never Use Situations:
Never use this unless the block already belongs to you and you are giving up ownership.

See Also: MemGetInfo(),
MemModifyFlags(),
MemModifyOtherInfo().

HandleP()

void	HandleP(
        MemHandle		mh);		/* Handle of block to grab */

If several different threads will be accessing the same global memory block, they need to make sure their activities will not conflict. The way they do that is to use synchronization routines to get control of a block. HandleP() is part of one set of synchronization routines.

If the threads are using this family of routines, then whenever a thread needs access to the block in question, it can call HandleP() . This routine checks whether any thread has grabbed the block with HandleP() (or MemPLock() ). If no thread has the block, it grabs the block for the calling thread and returns (it does not lock the block on the global heap). If a thread has the block, HandleP() puts the thread on a priority queue and sleeps. When the block is free for it to take, it awakens, grabs the block, and returns.When the thread is done with the block, it should release it with MemUnlockV() or HandleV() .

Include: heap.h

Tips and Tricks: If you will be locking the block after you grab it, use the routine MemPLock() (which calls HandleP() and then locks the block with MemLock() ). You can find out if the block is being accessed by looking at the HM_otherInfo word (with MemGetInfo() ). If HM_otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all threads accessing the block use HandleP() and/or MemPLock() to access the block. The routines use the HM_otherInfo field of the handle table entry; do not alter this field. Release the block with HandleV() or MemUnlockV() when you are done with it.

Warnings: If a thread calls HandleP() when it already has control of the block, it will deadlock; HandleP() will put the thread to sleep until the thread releases the block, but the thread will not be able to release the block because it's sleeping. MemThreadGrab() avoids this conflict. If you try to grab a non-sharable block owned by another thread, HandleP() will fatal-error.

See Also: MemThreadGrab(),
MemThreadGrabNB(),
MemThreadRelease(),
HandleV(),
HandleP(),
MemPLock(),
MemUnlockV().

HandleToOptr()

optr	HandleToOptr(
        Handle 	han;

This macro casts any handle to an optr, leaving the chunk handle portion of the resultant optr to be zero.

See Also: ConstructOptr(),
OptrToHandle(),
OptrToChunk().

| Up: GEOS SDK TechDocs | Up | Prev: GSSetSubscriptAttr() ... | Next: HandleV() ...


GEOS C Routines: HandleV() ...

| Up: GEOS SDK TechDocs | Up | Prev: HAL_COUNT() ... | Next: HugeArrayDestroy() ...

HandleV()

void	HandleV(
        MemHandle		mh);		/* Handle of block to grab */

HandleV() is part of a set of synchronization routines. If several different threads will be accessing the same global memory block, they need to make sure their activities will not conflict. The way they do that is to use synchronization routines to get control of a block. HandleV() is part of one set of synchronization routines.

If a block is being accessed via these synchronization routines, then a thread will not access a block until it has "grabbed" it with HandleP() or MemPLock() . When a thread is done with the block, it can release it for use by the other threads by calling HandleV() . Note that HandleV() does not unlock the block; it just changes the block's semaphore so other threads can grab it.

Include: heap.h

Tips and Tricks: If you need to unlock the thread just before releasing it, use the routine MemUnlockV() , which first unlocks the thread, and then calls HandleV() to release it. You can find out if the block is being accessed by looking at the HM_otherInfo word (with MemGetInfo() ). If HM_otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all threads accessing the block use HandleP() or MemPLock() to access the thread. The routines use the HM_otherInfo field of the handle table entry; do not alter this field.

Warnings: Do not use this on a block unless you have grabbed it. The routine does not check to see that you have grabbed the thread; it just clears the semaphore and returns.

See Also: MemThreadGrab(),
MemThreadGrabNB(),
MemThreadRelease(),
HandleV(),
HandleP(),
MemPLock(),
MemUnlockV().

HelpSendHelpNotification()

void 	HelpSendHelpNotification(
        word 		HelpType,
        const char 		*contextname,
        const char 		*filename);

Use this routine to bring up on-line help on the fly or to dynamically change the context presently showing in on-line help.

HugeArrayAppend()

dword	HugeArrayAppend(
        VMFileHandle		file,
        VMBlockhandle		vmBlock,				/* Handle of directory block */
        word		numElem,				/* # of elements to add to end of
                         * array */
        const void *		initData);				/* Copy into new element(s) */

This routine appends one or more elements to a Huge Array. If initData is a null pointer, the elements will be uninitialized.

If the Huge Array contains variable sized elements, this routine will append a single element; this element will be numElem bytes long. The single element will be initialized to hold initData .

If the Huge Array contains constant-sized elements, then initData will be treated as an array of element-sized structures. initData [0] will be the initial value of the first element, initData [1] will be the initial value of the second, and so on.

The return value is the element number of the element added. If more than one element was added; this will be the number of the first element added.

Include: hugearr.h

HugeArrayCompressBlocks()

void	HugeArrayCompressBlocks(
        VMFileHandle		vmFile,			/* File containing Huge Array */
        VMBlockHandle		vmBlock);			/* handle of directory block */

This routine compacts a Huge Array, resizing every block to be just as large as necessary to accommodate its elements. It does not change any of the data in the Huge Array.

Include: hugearr.h

HugeArrayContract()

word	HugeArrayContract(
        void **		elemPtr,				/* **elemPtr is first element to
                          * delete */
        word		numElem);				/* # of elements to delete */

Delete a number of elements starting at an address in a Huge Array. The routine will fix up the pointer so it points to the first element after the deleted elements. The routine automatically locks and unlocks Huge Array blocks as necessary.

Include: hugearr.h

HugeArrayCreate()

VMBlockhandle 	HugeArrayCreate(
        VMFileHandle		vmFile,				/* Create in this VM file */
        word		elemSize,				/* Pass zero for variable-size
                         * elements */
        word		headerSize);				/* Pass zero for default header */

This routine creates and initializes a Huge Array in the specified file. It returns the handle of the Huge Array's directory block.

Include: hugearr.h

HugeArrayDelete()

void	HugeArrayDelete(
        VMFileHandle		vmFile,
        VMBlockHandle		vmBlock,			/* handle of directory block */
        word		numElem,			/* # of elements to delete */
        dword		elemNum);			/* Index of first element to delete */

This routine deletes one or more elements from a Huge Array. It contracts and frees blocks as necessary.

Include: hugearr.h

HugeArrayDirty()

void	HugeArrayDirty(
        const void *		elemPtr);				/* Element in dirty block */

This routine marks a block in a Huge Array as dirty. The routine is passed a pointer to anywhere in a dirty element; that element's block will be dirtied.

Include: hugearr.h

Warnings: Be sure to call this routine before you unlock the element; otherwise, the block may be discarded before you can dirty it.

| Up: GEOS SDK TechDocs | Up | Prev: HAL_COUNT() ... | Next: HugeArrayDestroy() ...


GEOS C Routines: HugeArrayDestroy() ...

| Up: GEOS SDK TechDocs | Up | Prev: HandleV() ... | Next: HugeArrayPrev() ...

HugeArrayDestroy()

void	HugeArrayDestroy(
        VMFileHandle		vmFile,
        VMBlockHandle		vmBlock);			/* Handle of directory block */

This routine destroys a HugeArray by freeing all of its blocks.

Include: hugearr.h

HugeArrayEnum()

Boolean	HugeArrayEnum(
        VMFileHandle	vmFile,			/* Handle of Huge Array's VM file */
        VMBlockHandle	vmBlock,		/* Handle of the Huge Array's
                         * directory block */
        Boolean	 _pascal 	(*callback) (	/* return true to stop */
               void *			element,	/* element to examine */
               void * 			enumData),	/* pointer to data */
        dword		startElement,		/* first element to examine */
        dword		count,			/* examine this many elements */
        void *		enumData;		/* this pointer is passed to
                         * callback routine */

This routine lets you examine a sequence of elements in a Huge Array. HugeArrayEnum() is passed six arguments. The first two are a file handle and block handle; these specify the Huge Array to be examined. The third is a pointer to a Boolean callback routine. The fourth argument is the index of the first element to be examined (remember, the first element in the Huge Array has an index of zero). The fifth argument is the number of elements to examine, or -1 to examine through the last element. The sixth argument is a pointer which is passed unchanged to the callback routine; you can use this to pass data to the callback routine, or to keep track of a scratch space.

The callback routine, which must be declared _pascal, itself takes two arguments. The first is a pointer to an element in the huge array. The callback routine will be called once for each element in the specified range; each time, the first argument will point to the element being examined. The second argument is the pointer that was passed as the final argument to HugeArrayEnum() . The callback routine can make HugeArrayEnum() abort by returning true ; this is useful if you need to search for a specific element. Otherwise, the callback routine should return false . If the callback routine aborts the enumeration, HugeArrayEnum() returns true ; otherwise, it returns false .

HugeArrayEnum() is guaranteed to examine the elements in numerical order, beginning with startElement . The routine will automatically stop with the last element, even if count elements have not been enumerated. However, the starting element must be the index of an element in the array.

Include: hugearr.h

Warnings: The callback routine may not allocate, free, or resize any elements in the Huge Array. All it should do is examine or change ( without resizing) a single element.

The starting element must be an element in the array. If you pass a starting index which is out-of-bounds, the results are undefined.

HugeArrayExpand()

word	HugeArrayExpand(
        void **		elemPtr,				/* **elemPtr is element at location
                         * where new elements will be
                         * created */
        word		numElem,				/* # of elements to insert */
        const void *		initData);				/* Copy this into each new
                         * element */

This routine inserts a number of elements at a specified location in a HugeArray. The element pointed to will be shifted so it comes after the newly-created elements. The pointer will be fixed up to point to the first new element. The data pointed to by initData will be copied into each new element. If initData is null, the new elements will be uninitialized.

If the elements are of variable size, this routine will insert a single element; this element will be numElem bytes long.

Include: hugearr.h

HugeArrayGetCount()

dword	HugeArrayGetCount(
        VMFileHandle		vmFile,
        VMBlockHandle		vmBlock);			/* Handle of directory block */

This routine returns the number of elements in a Huge Array.

Include: hugearr.h

HugeArrayInsert()

void	HugeArrayInsert(
        VMFileHandle		vmFile,
        VMBlockHandle		vmBlock,			/* Handle of directory block */
        word		numElem,			/* # of elements to insert */
        dword		elemNum,			/* Index of first new element */
        const void *		initData);			/* Copy this into each new element */

This routine inserts one or more elements in the midst of a Huge Array. The first new element will have index elemNum ; thus, the element which previously had that index will now come after the new elements. The data pointed to by initData will be copied into each new element. If initData is null, the new elements will be uninitialized.

If the elements are of variable size, this routine will insert a single element; this element will be numElem bytes long.

Include: heap.h

HugeArrayLock()

dword	HugeArrayLock(
        VMFileHandle		vmFile,
        VMBlockhandle		vmBlock,			/* Handle of directory block */
        dword		elemNum,			/* Element to lock */
        void **		elemPtr,			/* Pointer to element is written here */
        word *		elemSize);

This routine locks an element in a Huge Array. It writes the element's address to *elemPtr . The dword returned indicates how many elements come before and after the element in that blok The upper word indicates how many elements come before the locked one, counting the locked element; the lower word indicates how many elements come after the locked element, again counting the locked one. If an error occured, then the lower word of the return value will be zero.

The routine also writes the size of the locked element (in bytes) to *elemSize . You may examine or change all the other elements in the block without making further calls to HugeArrayLock() .

Include: heap.h

See Also: HAL_COUNT(), HAL_PREV().

HugeArrayNext()

word	HugeArrayNext(
        void **		elemPtr,
        word *		size);

This routine increments a pointer to an element in a HugeArray to point to the next element. If the element was the last element in its block, HugeArrayNext() will unlock its block and lock the next one. The routine writes the pointer to *elemPtr; it returns the number of elements which come after the newly-locked one in its block, counting the newly-locked element. If this routine is passed a pointer to the last element in a HugeArray, it returns zero and elemPtr will point to the last element.

If the elements are variable-sized, then the word pointed to by size will be updated to reflect the size of the newly-locked element; otherwise, the value will be undefined.

Include: heap.h

Warnings: This routine may unlock the block containing the passed element. Therefore, if you need to mark the block as dirty, do so before making this call.

| Up: GEOS SDK TechDocs | Up | Prev: HandleV() ... | Next: HugeArrayPrev() ...


GEOS C Routines: HugeArrayPrev() ...

| Up: GEOS SDK TechDocs | Up | Prev: HugeArrayDestroy() ... | Next: IACPProcessMessage() ...

HugeArrayPrev()

word	HugeArrayPrev(
        void **		elemPtr1,				/* indicates current element */
        void **		elemPtr2,
        word * 		size);

This routine decrements a pointer to an element in a HugeArray to point to the previous element. If the element was the first element in its block, HugeArrayPrev() will unlock its block and lock the previous one. The routine writes the pointer to *elemPtr1, and writes a pointer to the first element in the block in *elemPtr2. It returns the number of elements which come before the newly-locked one in its block, counting the newly-locked element. If this routine is passed a pointer to the first element in a HugeArray, it returns zero and *elemPtr1 will point to the first element.

If the elements are variable-sized, then the word pointed to by size will be updated to reflect the size of the newly-locked element; otherwise, the value will be undefined.

Include: hugearr.h

Warnings: This routine may unlock the block containing the passed element. Therefore, if you need to mark the block as dirty, do so before making this call.

HugeArrayReplace()

void	HugeArrayReplace(
        VMFileHandle		file,
        VMBlockHandle		vmblock,				/* Handle of directory block */
        word		numElem,				/* # of elements to replace */
        dword		elemNum,				/* First element to replace */
        const void *		initData);				/* Copy this into each element

This routine replaces one or more elements with copies of the passed data. If initData is null, the elements will be filled with null bytes.

If the elements are of variable size, a single element will be resized; its new size will be numElem bytes long.

Include: hugearr.h

See Also: HugeArrayResize().

HugeArrayResize()

void	HugeArrayResize(
        VMFileHandle		vmFile,
        VMBlockHandle		vmBlock,			/* Handle of directory block */
        dword		elemNum,			/* Resize this element */
        word		newSize);			/* New size in bytes */

This routine resizes an element in a Huge Array. The array must contain variable-sized elements. If the new size is larger than the old, the extra space will be zero-initialized. If it is smaller, the element will be truncated.

Include: hugearr.h

HugeArrayUnlock()

void	HugeArrayUnlock(
        void *		elemPtr);

This routine unlocks the block of a HugeArray which contains the passed element.

Include: hugearr.h

Warnings: If you have changed any of the elements in the block, be sure to call HugeArrayDirty() before you unlock the block; otherwise the block might be discarded.

IACPConnect()

IACPConnection IACPConnect(
        GeodeToken 		*list,
        IACPConnectFlags 		flags,
        MemHandle 		appLaunchBlock,
        optr 		client,
        word 		*numServers);

This routine establishes a connection between a client object (by default the calling thread's application object) and one or more servers registered with the indicated list.

The client argument should be NullOptr unless the IACPCF_CLIENT_OD_SPECIFIED flag is set in the flags parameter.

Include: iacp.goh

IACPCreateDefaultLaunchBlock()

MemHandle IACPCreateDefaultLaunchBlock(
        word 		appMode);

This routine creates a memory block holding an AppLaunchBlock structure suitable for passing to IACPConnect() . The two valid values to pass in appMode are MSG_GEN_PROCESS_OPEN_APPLICATION and MSG_GEN_PROCESS_OPEN_ENGINE .

Include: iacp.goh

IACPFinishConnect()

void	IACPFinishConnect(
        IACPConnection		connection,
        optr		server);

Finishes a connection made to a server which had to change from non-interactible to interactible.

Include: iacp.goh

IACPGetServerNumber()

word	IACPGetServerNumber(
        IACPConnection		connection,
        optr		server);

This routine retrieves the server number for a particular server in a particular connection. It returns the server number.

Include: iacp.goh

IACPLostConnection()

void IACPLostConnection(
        optr 		oself,
        IACPConnection 		connection);

This routine is called by IACP server objects to handle when a client closes a connection.

Include: iacp.goh

| Up: GEOS SDK TechDocs | Up | Prev: HugeArrayDestroy() ... | Next: IACPProcessMessage() ...


GEOS C Routines: IACPProcessMessage() ...

| Up: GEOS SDK TechDocs| Up | Prev: HugeArrayPrev() ... | Next: ImpexExportToMetafile() ...

IACPProcessMessage()

void IACPProcessMessage(
        optr           oself,
        EventHandle            msgToSend,
        TravelOption           topt,
        EventHandle            completionMsg);

This is a utility routine to dispatch an encapsulated message handed to an object by an IACP connection.

Include: iacp.goh

IACPRegisterDocument()

void IACPRegisterDocument(
        optr   server,
        word   disk,
        dword  fileID);

This routine registers an open document and the server object for it.

This routine is to be used only by servers, not by clients, and should only be used by the creator of the document. There is no provision for using IACP to connect to a server that is not the creator of the document in question.

Include: iacp.goh

IACPRegisterServer()

void    IACPRegisterServer(
        GeodeToken             *list,
        optr           server,
        IACPServerMode                 mode,
        IACPServerFlags                flags);

This routine registers an object as a server for the IACP server list specified by the passed token.

Include: iacp.goh

IACPSendMessage()

word IACPSendMessage(
    IACPConnection 	connection,
    EventHandle 		msgToSend,
    TravelOption 		topt,
    EventHandle 		completionMsg,
    IACPSide 		side);

This routine sends the message event via IACP to all the objects on the other side of an IACP connection.

Include: iacp.goh

IACPSendMessageAndWait()

word IACPSendMessageAndWait(
        IACPConnection                 connection,
        EventHandle            msgToSend,
        TravelOption           topt,
        IACPSide     side);

This routine sends a recorded message to all the objects on the other side of an IACP connection, and waits until the other application responds that it has received the message via IACP. This does not mean the message has been handled by the other application, only that the other application was given the EventHandle and will try to deal with it. This is useful when passing volatile data.

Include: iacp.goh

IACPSendMessageToServer()

word IACPSendMessageToServer(
        IACPConnection                 connection,
        EventHandle            msgToSend,
        TravelOption           topt,
        EventHandle            completionMsg,
        word           serverNum);

This routine sends a message to a specific server on the other side of an IACP connection.

Include: iacp.goh

IACPShutdown()

void IACPShutdown(
        IACPConnection                 connection,
        optr           serverOD);

This routine removes a server or client from an IACP connection.

Include: iacp.goh

IACPShutdownAll()

void IACPShutdownAll(
        optr obj);

This calls IACPShutdown() for all connections to which the passed object is a party. It's primarily used by GenApplicationClass when the application is exiting.

Include: iacp.goh

IACPUnregisterDocument()

void IACPUnregisterDocument(
        optr   server,
        word   disk,
        dword  fileID);

This routine unregisters an open document and the server object for it.

Include: iacp.goh

IACPUnregisterServer()

void IACPUnregisterServer(
        GeodeToken     *list,
        optr           server);

This removes the specified server object from the indicated IACP server list.

Include: iacp.goh

ImpexCreateTempFile()

TransError ImpexCreateTempFile(
        char *         buffer,
        word           fileType,
        FileHandle *           file,
        MemHandle *            errorString);

This routine creates and opens a unique temporary file to be used by translation libraries for file importing and exporting. The routine is called only by translation libraries.

The routine is passed the following arguments:

buffer
The file name will be written to the buffer pointed to by this argument. The buffer should be at least FILE_LONGNAME_BUFFER_SIZE bytes long.
fileType
This specifies what kind of temporary file should be created. If IMPEX_TEMP_VM_FILE is passed, a GEOS VM file will be created. If IMPEX_TEMP_NATIVE_FILE is passed, a temporary file in the native format will be created.
file
This is a pointer to a FileHandle variable. The temporary file's handle will be written to *file .
errString
If ImpexCreateTempFile fails with error condition TE_CUSTOM it will allocate a block containing an error string. It will write the block's handle to *errString . It is the caller's responsibility to free this block when it's done with it.

If ImpexCreateTempFile is successful, it returns TE_NO_ERROR (which equals zero). If it fails, it returns a member of the TransferErrors enumerated type (usually TE_METAFILE_CREATION_ERROR). When you're done with the temporary file, call FileClose() .

Include: impex.goh

Warnings: If you close this file, the system may delete it at any time. Ordinarily you should close it with ImpexDeleteTempFile() , which deletes the file immediately.

If the routine does not fail with condition TE_CUSTOM, *errString may contain a random value. Do not use *errString if the routine did not return TE_CUSTOM.

ImpexDeleteTempFile()

TransError ImpexDeleteTempFile(
        const char *           buffer,
        FileHandle             tempFile,
        word           fileType);

This routine closes, then deletes, a temporary file which was created by ImpexCreateTempFile() . It is passed the following arguments:

buffer
This is a pointer to a character buffer containing the name of the temporary file. You can just pass the address of the buffer which was filled by ImpexCreateTempFile() .
tempFile
This is the handle of the temporary file.
fileType
This specifies what type of file is being deleted. If the temporary file is a GEOS VM file, this will be IMPEX_TEMP_VM_FILE. If it is a native-format file, it will be IMPEX_TEMP_NATIVE_FILE.
errString
If ImpexDeleteTempFile fails with error condition TE_CUSTOM it will allocate a block containing an error string. It will write the block's handle to *errString . It is the caller's responsibility to free this block when it's done with it.

ImpexDeleteTempFile() closes the specified file, then deletes it. If it is successful, it returns TE_NO_ERROR (i.e. zero); otherwise, it returns an appropriate member of the TransError enumerated type.

Include: impex.goh

Warnings: If the routine does not fail with condition TE_CUSTOM, *errString may contain a random value. Do not use *errString if the routine did not return TE_CUSTOM.


GEOS C Routines: ImpexExportToMetafile() ...

| Up: GEOS SDK TechDocs | Up | Prev: IACPProcessMessage() ... | Next: InitFileEnumStringSection() ...

ImpexExportToMetafile()

TransError 	ImpexExportToMetafile(
        Handle		xlatLib,
        VMFileHandle		xferFile,
        FileHandle		metafile,
        dword		xferFormat,
        word		arg1,
        word		arg2,
        MemHandle *		errString);

This routine is used by translation libraries. The routine calls an intermediate translation library to finish translating a given file into the GEOS Metafile format.

Include: impex.goh

Warnings: If the routine does not fail with condition TE_CUSTOM, *errString may contain a random value. Do not use *errString if the routine did not return TE_CUSTOM.

ImpexImportExportCompleted()

void 	ImpexImportExportCompleted(
        ImpexTranslationParams *			itParams);

The application should send this message when it is finished importing or exporting data. The routine will send an appropriate acknowledgment message to the ImportControl or ExportControl object, depending on the settings of ITP_impexOD and ITP _returnMsg .

If the application has just finished an import, it should not have changed the ImpexTranslationParams structure. If it had just finished preparing data for export, it should have set the ITP _transferVMChain field to contain the handle of the head of the VM chain.

Warnings: This routine, in essence, informs the ImportControl or ExportControl object that the application is finished with the transfer file. The ImportControl will respond by destroying the transfer file; the ExportControl will call the appropriate translation library to produce an output file. Therefore, an application should not call this routine until it is absolutely finished with the transfer file.

ImpexImportFromMetafile()

TransError 	ImpexExportToMetafile(
        Handle		xlatLib,
        VMFileHandle		xferFile,
        FileHandle		metafile,
        dword *		xferFormat,
        word		arg1,
        word		arg2,
        MemHandle *		errString);

This routine is used by translation libraries. The routine calls an intermediate translation library to translate a given file from the GEOS Metafile format to an intermediate format.

Include: impex.goh

Warnings: If the routine does not fail with condition TE_CUSTOM, *errString may contain a random value. Do not use *errString if the routine did not return TE_CUSTOM.

InitFileCommit()

void	InitFileCommit(void);

This routine commits any changes to the GEOS.INI file, removing and replacing its stored backup. It ensures that no other threads are working on the file during the commit operation.

Include: initfile.h

InitFileDeleteCategory()

void	InitFileDeleteCategory(
        const char *category);

This routine deletes the specified category, along with all its entries, from the GEOS.INI file. Pass it the following:

category
A pointer to the null-terminated string representing the category to be deleted. This string ignores white space and is case-insensitive.

Include: initfile.h

InitFileDeleteEntry()

void	InitFileDeleteEntry(
        const char *category,
        const char *key);

This routine deletes an entry in the GEOS.INI file. Pass it the following:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be deleted.

Include: initfile.h

InitFileDeleteStringSection()

void	InitFileDeleteStringSection(
        const char *		category,
        const char *		key,
        word		stringNum);

This routine deletes the specified string section from the given blob in the GEOS.INI file. Pass it the following:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be edited.
stringNum
The zero-based string section number.

Include: initfile.h

| Up: GEOS SDK TechDocs | Up | Prev: IACPProcessMessage() ... | Next: InitFileEnumStringSection() ...


GEOS C Routines: InitFileEnumStringSection() ...

| Up: GEOS SDK TechDocs | Up | Prev: ImpexExportToMetafile() ... | Next: InitFileReadStringBlock() ...

InitFileEnumStringSection()

Boolean	InitFileEnumStringSection(
        const char *		category,
        const char *		key,
        InitFileReadFlags		flags,
        Boolean _pascal (*callback)		(const char *				stringSection,
                 word 				sectionNum,
                 void *				enumData),
        void *		enumdata);

This routine enumerates a particular blob, allowing a callback routine to process each of the string sections in it. The routine will stop processing either after the last string section or when the callback routine returns true .

Pass this routine the following:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be enumerated.
flags
A record of InitFileReadFlags indicating the method of character conversion upon reading (upcase all, downcase all, do not change).
callback
A pointer to a Boolean callback routine. The callback routine is described below.
enumData
This pointer is passed unchanged to the callback routine. InitFileEnumStringSection() does not use it.

This routine returns a Boolean value. It returns true if the callback routine halted the enumeration by returning true ; otherwise, it returns false .

Callback Routine:

The callback routine may do anything it wants with the string section it receives. It must be declared _pascal. It must return a Boolean value: If it returns true , InitFileEnumStringSection() will stop processing the blob. If it returns false , processing will continue to the next string section, if any. The callback will receive the following parameters:

stringSection
A pointer to the null-terminated string section to be processed.
sectionNum
The zero-based number of the string section currently being processed.
enumData
A pointer passed through from the caller of InitFileEnumStringSection() .

Include: initfile.h

InitFileGetTimeLastModified()

dword	InitFileGetTimeLastModified(void);

This routine returns the time when the GEOS.INI file was last modified. The returned time is the value of the system counter when the file was last written.

Include: initfile.h

InitFileMakeCanonicKeyCategory()

void       InitFileMakeCanonicKeyCategory(
    char 		*keyCat,
    const TCHAR 	*src);

Converts a SBCS/DBCS (depending on GEOS version) string to an ASCIIX string without special INI characters. This is used to convert any type of string to one suitable for use as an INI file key or category.

Pass:

keyCat
The unconverted SBCS/DBCS string.
src
The buffer for the converted SBCS ASCII hex string. If keyCat is SBCS, then src should be the same length as keyCat (including the NULL terminator); otherwise, if keyCat is DBCS then src should point to a buffer which is twice as long as keyCat (including its NULL terminator).

Include: initfile.h

Version: This routine is available on builds 4 or greater of the Nokia 9000i Communicator (see the Versions chapter for more information).

InitFileReadBoolean()

Boolean	InitFileReadBoolean(
        const char *		category,
        const char *		key,
        Boolean *		bool);

This routine reads a Boolean entry in the GEOS.INI file, copying it into a passed buffer. It returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteBoolean() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
bool
A pointer to a Boolean variable in which the Boolean value will be returned.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Warnings: The return value of this function is not the Boolean stored in the GEOS.INI file. That value is returned in the Boolean pointed to by bool .

Include: initfile.h

InitFileReadDataBlock()

Boolean	InitFileReadDataBlock(
        const char *		category,
        const char *		key,
        MemHandle *		block,
        word *		dataSize);

This routine reads an entry in the GEOS.INI file, allocating a new block and copying the data into it. The routine returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteData() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
block
A pointer to a null memory handle. This pointer will point to the newly-allocated block handle upon return. The data read will be in the new block. It is your responsibility to free this block when you're done with it.
dataSize
The size of the read data. All the data will be read; the block will be as large as necessary.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

InitFileReadDataBuffer()

Boolean	InitFileReadDataBuffer(
        const char *		category,
        const char *		key,
        void *		buffer,
        word		bufSize,
        word *		dataSize);

This routine reads an entry in the GEOS.INI file, copying it into a passed buffer. It returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteData() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
buffer
A pointer to the buffer in which the data will be returned. This buffer must be in locked or fixed memory.
bufSize
The size of the passed buffer in bytes. If you are not sure what the data's size will be, you may want to use the (slightly less efficient) InitFileReadDataBlock() .
dataSize
A pointer to a word; on return, the word pointed to will contain the size (in bytes) of the data returned.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

InitFileReadInteger()

Boolean	InitFileReadInteger(
        const char *		category,
        const char *		key,
        word *		i);

This routine reads an integer entry in the GEOS.INI file, copying it into the passed variable. It returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteInteger() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
i
A pointer to a word in which the integer will be returned.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

| Up: GEOS SDK TechDocs | Up | Prev: ImpexExportToMetafile() ... | Next: InitFileReadStringBlock() ...


GEOS C Routines: InitFileReadStringBlock() ...

| Up: GEOS SDK TechDocs | Up | Prev: InitFileEnumStringSection() ... | Next: InitFileRevert() ...

InitFileReadStringBlock()

Boolean	InitFileReadStringBlock(
        const char *		category,
        const char *		key,
        MemHandle *		block,
        InitFileReadFlags		flags,
        word *		dataSize);

This routine reads a string entry in the GEOS.INI file, allocates a new block on the global heap, and copies the read string into the new block. It returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteString() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
block
A pointer to a memory block handle variable. Upon return, this variable will contain the handle of the newly allocated block; the block will contain the string read from the file. It is your responsibility to free this block when you're done with it.
flags
A record of InitFileReadFlags indicating the method of character conversion upon reading (upcase all, downcase all, do not change).
dataSize
A pointer to a word which, upon return, will contain the size of the string (in bytes) actually read from the file.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

InitFileReadStringBuffer()

Boolean	InitFileReadStringBuffer(
        const char *		category,
        const char *		key,
        char *		buffer,
        InitFileReadFlags		flags,
        word *		dataSize);

This routine reads a string entry in the GEOS.INI file, copying it into a passed, locked buffer. It returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteString() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
buffer
A pointer to a buffer into which the returned string will be written. This buffer must be in locked or fixed memory. If you don't know the approximate size of the data, you may want to use the (slightly less efficient) InitFileReadStringBlock() .
flags
A record of InitFileReadFlags indicating the size of the passed buffer as well as the method of character conversion upon reading (upcase all, downcase all, do not change).
dataSize
A pointer to a word which, upon return, will contain the size of the string (in bytes) actually read from the file.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

InitFileReadStringSectionBlock()

Boolean	InitFileReadStringSectionBlock(
        const char *		category,
        const char *		key,
        word		section,
        MemHandle *		block,
        InitFileReadFlags		flags,
        word *		dataSize);

This routine reads a string section from the specified entry in the GEOS.INI file, allocates a new block on the global heap, and copies the read string section into the new block. It returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteString() or InitFileWriteStringSection() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
section
The zero-based number of the string section to retrieved.
block
A pointer to a memory block handle. Upon return, this pointer will point to the handle of the newly allocated block; the block will contain the string section read from the file.
flags
A record of InitFileReadFlags indicating the method of character conversion upon reading (upcase all, downcase all, do not change).
dataSize
A pointer to a word which, upon return, will contain the size of the string section (in bytes) actually read from the file.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

InitFileReadStringSectionBuffer()

Boolean	InitFileReadStringSectionBuffer(
        const char *		category,
        const char *		key,
        word		section,
        char *		buffer,
        InitFileReadFlags		flags,
        word *		dataSize);

This routine reads a string section from the specified entry in the GEOS.INI file, copying it into a passed, locked buffer. It returns the indicated section in the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteStringSection() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
section
The zero-based number of the string section to be retrieved.
buffer
A pointer to a buffer into which the returned string section will be written. This buffer must be in locked or fixed memory. If you don't know the approximate size of the string section, you may want to use the (slightly less efficient) InitFileReadStringSectionBlock() .
flags
A record of InitFileReadFlags indicating the size of the passed buffer as well as the method of character conversion upon reading (upcase all, downcase all, do not change).
dataSize
A pointer to a word which, upon return, will contain the size of the string section (in bytes) actually read from the file.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

| Up: GEOS SDK TechDocs | Up | Prev: InitFileEnumStringSection() ... | Next: InitFileRevert() ...


GEOS C Routines: InitFileRevert() ...

| Up: GEOS SDK TechDocs | Up | Prev: InitFileReadStringBlock() ... | Next: InkDBGetDisplayInfo() ...

InitFileRevert()

Boolean	InitFileRevert(void);

This routine restores the GEOS.INI file from its saved backup version. It ensures that no other thread is operating on the file while it is being restored. This function returns an error flag: true represents an error in restoring the file; false indicates success.

Include: initfile.h

InitFileSave()

Boolean	InitFileSave(void);

This routine saves a backup copy of the GEOS.INI, synchronously updating the backup file (privdata\ini.bak) to be the current version. ( InitFileCommit() overwrites the GEOS.INI file itself.) It ensures that no other thread is operating on the file while it is being written out. This function returns an error flag: true represents an error in trying to save the file; false indicates success.

Include: initfile.h

InitFileWriteBoolean()

void	InitFileWriteBoolean(
        const char *		category,
        const char *		key,
        Boolean 		bool);

This integer writes a Boolean value into the specified category and key of the local GEOS.INI file. The Boolean will appear as "true" or "false" if the user looks at GEOS.INI with a text editor, but it will be an actual Boolean value to GEOS. Pass this routine the following:

category
A pointer to the null-terminated character string representing the INI category into which the data should be written.
key
A pointer to the null-terminated character string representing the INI key within category into which the data should be written.
bool
The Boolean value to be written.

Once written, the Boolean value can be read with InitFileReadBoolean() .

Include: initfile.h

InitFileWriteData()

void	InitFileWriteData(
        const char 		*category,
        const char 		*key,
        const void 		*buffer,
        word		bufSize);

This routine writes a given piece of data to the local GEOS.INI file. Pass it the following:

category
A pointer to the null-terminated character string representing the INI category into which the data should be written.
key
A pointer to the null-terminated character string representing the INI key within category into which the data should be written.
buffer
A pointer to a locked or fixed buffer containing the data to be written.
bufSize
The size of the buffer in bytes.

Once data has been written to the INI file, it can be read with InitFileReadDataBlock() or InitFileReadDataBuffer() .

Include: initfile.h

InitFileWriteInteger()

void	InitFileWriteInteger(
        const char 		*category,
        const char 		*key,
        word		value);

This routine writes an integer into the category and key specified for the local GEOS.INI file. Pass the following:

category
A pointer to the null-terminated character string representing the INI category into which the data should be written.
key
A pointer to the null-terminated character string representing the INI key within category into which the data should be written.
value
The integer to be written.

The integer, once written, can be read with InitFileReadInteger() .

Include: initfile.h

InitFileWriteString()

void	InitFileWriteString(
        const char *category,
        const char *key,
        const char *str);

This routine writes an entire string into the category and key specified for the local GEOS.INI file. Pass it the following:

category
A pointer to the null-terminated character string representing the INI category into which the data should be written.
key
A pointer to the null-terminated character string representing the INI key within category into which the data should be written.
str
A pointer to the null-terminated string to be written. If the string contains line feeds or carriage returns, it will automatically be parsed into string segments and be put within curly braces; if it contains curly braces, all closing braces will automatically have a backslash inserted before them.

To read a string written with this routine, use InitFileReadStringBlock() or InitFileReadStringBuffer() .

Include: initfile.h

InitFileWriteStringSection()

void	InitFileWriteStringSection(
        const char *category,
        const char *key,
        const char *string);

This routine appends a string section onto the blob specified by the category and key parameters. The string section will become part of the blob and will be its last section. The section may not contain any carriage returns or line feeds. Pass this routine the following:

category
A pointer to the null-terminated character string representing the INI category into which the data should be written.
key
A pointer to the null-terminated character string representing the INI key within category into which the data should be written.
string
A pointer to the string section to be written.

Once written, the segment may be read with InitFileReadStringSectionBlock() or InitFileReadStringSectionBuffer() .

Include: initfile.h

| Up: GEOS SDK TechDocs | Up | Prev: InitFileReadStringBlock() ... | Next: InkDBGetDisplayInfo() ...


GEOS C Routines: InkDBGetDisplayInfo() ...

| Up: GEOS SDK TechDocs | Up | Prev: InitFileRevert() ... | Next: InkFolderGetNumChildren() ...

InkDBGetDisplayInfo()

void 	InkDBGetDisplayInfo(
        InkDBDisplayInfo *		retVal,
        VMFileHandle 		fh);

This routine returns the dword ID of the note or folder which is presently being displayed by the Ink Database. It also returns the ID of the parent folder, and the page number, if applicable.

Structures: It returns this information by filling in an InkDBDisplayInfo structure:

typedef struct {
    dword 	IDBDI_currentDisplay;
    dword 	IDBDI_parentFolder;
    word 	IDBDI_pageNumber;
} InkDBDisplayInfo;

Include: pen.goh

InkDBGetHeadFolder()

dword 	InkDBGetHeadFolder(
        VMFileHandle 		fh);

This routine returns the dword ID of the head folder of an Ink Database file.

Include: pen.goh

InkDBInit()

void 	InkDBInit(
        VMFileHandle 		fh);

This routine takes a new Ink Database file. It initializes the file for use, creating all needed maps and a top-level folder.

Include: pen.goh

InkDBSetDisplayInfo()

void 	InkDBSetDisplayInfo(
        VMFileHandle 		fh,
        dword 		ofh,		/* Parent Folder dword ID# */
        dword		note,		/* ID# of note or folder to display */
        word		page); 		/* If displaying note, page # to display*/

This routine sets the display information for an Ink Database file. This routine sets the user's location in the database. The caller must supply the dword ID number of the note or folder to display, the parent folder (0 if displaying the top level folder), and the page number to display if displaying a note.

Include: pen.goh

InkFolderCreateSubFolder()

dword 	InkFolderCreateSubFolder(
        dword 		tag, 	/* ID# of parent folder (0 for top-level) */
        VMFileHandle 		fh); 	/* Handle of Ink DB file */

This routine creates a subfolder within the passed folder. The new folder is automatically added to it's parent's chunk array. The return value is new folder's dword ID number.

Include: pen.goh

InkFolderDelete()

void 	InkFolderDelete(
        dword 		tag,		/* ID# of folder */
        VMFileHandle 		fh);		/* Handle of Ink DB file */

This routine removes an Ink Database folder.

Include: pen.goh

InkFolderDepthFirstTraverse()

word 	InkFolderDepthFirstTraverse(
        dword 		rfldr,		/* ID# of folder at root of search tree */
        VMFileHandle		fh, 		/* Handle of Ink DB file */
        Boolean 	_pascal	(*callback)( /* far ptr to callback routine */
            dword				fldr,
            VMFileHandle 				fh,
            word *				info),
        word *		info);		/* Extra data to pass to callback */

This routine does a depth-first traversal of a folder tree. The callback routine, which must be declared _pascal, can halt the search by returning true , in which case the search routine will immediately return true ; otherwise the search will return false .

Include: pen.goh

InkFolderDisplayChildInList()

void 	InkFolderDisplayChildInList(
        dword 		fldr, 		/* ID# of folder */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        optr 		list, 		/* GenDynamicList */
        word		entry, 		/* entry number of child to display */
        Boolean		displayFolders); /* Include monikers in count,
                            * return their monikers */

This routine requests that a dynamic list display the name of one of a folder's children. It is normally called in an applications GDLI_queryMsg handler.

Include: pen.goh

InkFolderGetChildInfo()

Boolean 	InkFolderDisplayChildInfo( /* true if folder; else note */
        dword 		fldr, 		/* ID# of folder */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        word		entry, 		/* entry number of child */
        dword *		childID);	/* Pointer to returned child ID # */

This routine returns information about one of a folder's children. The explicit return value will be true if the child is a folder, false if the child is a note. In addition, the passed dword pointer will point to the child's dword ID number.

Include: pen.goh

InkFolderGetChildNumber()

word 	InkFolderDisplayChildInList(
        dword 		fldr, 		/* ID# of folder */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        dword		note); 		/* ID# of child note or folder */

This routine returns the passed note or folder's entry number within its passed parent folder.

Include: pen.goh

InkFolderGetContents()

DBGroupAndItem 	InkFolderGetContents(
        dword 		tag, 				/* ID# of folder */
        VMFileHandle 		fh,				/* Handle of Ink DB file */
        DBGroupAndItem *		subFolders); 		/* pointer to return value */);

This routine returns the contents of a folder. It returns two chunk arrays, each of which is filled with dword ID numbers of the folder's children. The explicitly returned array holds the numbers of the folder's child notes. The routine also fills in a pointer with a DB item holding a chunk array with the ID numbers of the subfolders.

Include: pen.goh

| Up: GEOS SDK TechDocs | Up | Prev: InitFileRevert() ... | Next: InkFolderGetNumChildren() ...


GEOS C Routines: InkFolderGetNumChildren() ...

| Up: GEOS SDK TechDocs | Up | Prev: InkDBGetDisplayInfo() ... | Next: InkNoteGetCreationDate() ...

InkFolderGetNumChildren()

dword 	InkFolderGetNumChildren( /* Subfolders:Notes */
        dword 		fldr, 		/* ID# of folder */
        VMFileHandle 		fh);		/* Handle of Ink DB file */

This message returns the number of children the Ink Database folder has. The high word of the return value holds the number of sub folders; the low word holds the number of notes.

Include: pen.goh

InkFolderMove()

void 	InkFolderMove(
        dword 		fldr, 		/* ID# of folder to move */
        dword 		pfldr);/* ID# of new parent folder */

This routine moves an Ink Database folder to a new location in the folder tree.

Include: pen.goh

InkFolderSetTitle()

void 	InkFolderSetTitle(
        dword 		tag, 		/* ID# of folder */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        const char *		name); 		/* Text object */);

This routine renames an Ink Database folder. The passed name should be null-terminated.

Include: pen.goh

InkFolderSetTitleFromTextObject()

void 	InkFolderSetTitleFromTextObject(
        dword 		tag, 		/* ID# of folder */
        FileHandle 		fh,		/* Handle of Ink DB file */
        optr		text); 		/* Text object */);

This routine sets the name of the passed Ink Database folder from the contents of the passed VisText object.

Include: pen.goh

InkGetDocPageInfo()

void 	InkGetDocPageInfo(
        PageSizeReport *		psr, 		/* Structure to fill with return value */
        VMFileHandle 		fh);

This routine returns the dword ID of the head folder of an Ink Database file.

Include: pen.goh

InkGetDocCustomGString()

GStateHandle 	InkGetDocCustomGString(
        VMFileHandle 		dbfh);

This routine returns the custom GString associated with the passed Ink Database file. Note that this custom background will only be used if the document's basic InkBackgroundType is IBT_CUSTOM. (This may be determined using the InkSetDocGString() routine.

Include: pen.goh

InkGetDocGString()

InkBackgroundType 	InkGetDocGString(
        VMFileHandle 		dbfh);

This routine returns the standard GString to use as a background picture with the passed Ink Database file. If the returned background type is custom, be sure to also call InkGetDocCustomGString() .

Include: pen.goh

InkGetParentFolder()

dword 	InkGetParentFolder(
        dword 		tag, 		/* ID# of folder or note */
        VMFileHandle 		fh);		/* Handle of Ink DB file */

This message returns the dword ID of the passed Ink Database note or folder.

Include: pen.goh

InkGetTitle()

word 	InkGetTitle(
        dword 		tag, 		/* ID# of folder or note */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        char *		dest); 		/* should be INK_DB_MAX_TITLE_SIZE +1 */);

This message fills the passed text buffer with the folder's or note's title, a null-terminated string. The routine's explicit return value is the length of the string (including the terminator).

Include: pen.goh

InkNoteCopyMoniker()

dword 	InkNoteCopyMoniker(
        dword 	title,	/* ID# of parent folder */
        optr 	list, 	/* Output list */
        word 	type, 	/* 1: text note
             * 0: ink note
             * -1: folder */
        word	entry);	/* Handle of Ink DB file */

This routine copies the icon nd title into the VisMoniker.

Include: pen.goh

InkNoteCreate()

dword 	InkNoteCreate(
        dword 		tag,		/* ID# of parent folder */
        VMFileHandle		fh);		/* Handle of Ink DB file */

This routine creates a note and adds it to the passed folder's child list. The new note's dword ID is returned.

Include: pen.goh

InkNoteCreatePage()

word 	InkNoteCreatePage(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh,		/* Handle of Ink DB file */
        word 		page); /* Page number to insert before,
                 * CA_NULL_ELEMENT to append */

This routine creates a new page within a note. It returns the new page number.

Include: pen.goh

InkNoteDelete()

void 	InkNoteDelete(
        dword 		tag, 		/* ID# of note */
        VMFileHandle 		fh);		/* Handle of Ink DB file */

This message deletes the passed note. All references to the note are deleted.

Include: pen.goh

InkNoteFindByKeywords()

ChunkHandle 	InkNoteFindByKeywords(
/* Return value is chunk array with elements:
 *  FindNoteHeader
 *  -dword tag-
 *  -dword tag-
 *   etc... */
        VMFileHandle 		fh,
        char *		strings,		/* strings to match (separated by
                    * whitespace or commas), can contain
                    * C_WILDCARD or C_SINGLE_WILDCARD */
        word 		opt,		/* true to match all keywords;
                 * false to match at least one keyword */

This routine returns a chunk array containing the dword ID numbers of all notes whose keywords match the passed search string, preceded by the number of matching notes. If no such notes are found, then the returned handle will be NULL.

Note that this routine will only return about 20K notes; if there are more that match, only the first 20K will be returned.

Include: pen.goh

InkNoteFindByTitle()

ChunkHandle 	InkNoteFindByTitle(
/* Return value is chunk array with elements:
 *  FindNoteHeader
 *  -dword tag-
 *  -dword tag-
 *   etc... */
        const char *		string,		/* string to match (can contain C_WILDCARD
                  * or C_SINGLE_WILDCARD */
        SearchOptions 		opt,		/* Search options */
        Boolean 		Body, 		/* true if you want to look in the body
                 * of text notes */
        VMFileHandle		fh);		/* Handle of Ink DB file */

This routine returns a chunk array containing the dword ID numbers of all notes whose titles match the passed search string, preceded by the number of matching notes. If no such notes are found, then the returned handle will be NULL.

Note that this routine will only return about 20K notes; if there are more that match, only the first 20K will be returned.

Include: pen.goh

| Up: GEOS SDK TechDocs | Up | Prev: InkDBGetDisplayInfo() ... | Next: InkNoteGetCreationDate() ...


GEOS C Routines: InkNoteGetCreationDate() ...

| Up: GEOS SDK TechDocs | Up | Prev: InkFolderGetNumChildren() ... | Next: InkNoteSetModificationDate() ...

InkNoteGetCreationDate()

dword 	InkNoteGetCreationDate(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh);		/* Handle of Ink DB file */

This routine gets a note's creation date.

Include: pen.goh

InkNoteGetKeywords()

void 	InkNoteGetKeywords(
        dword 		tag, 		/* ID# of note */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        char *		text); 		/* String to hold return value */);

This routine fills the passed buffer with the note's keywords. The target buffer should be of atleast length INK_DB_MAX_NOTE_KEYWORDS_SIZE +1. The string will be null-terminated.

Include: pen.goh

InkNoteGetModificationDate()

dword 	InkNoteGetModificationDate(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh);		/* Handle of Ink DB file */

This routine gets a note's modification date.

Include: pen.goh

InkNoteGetNoteType()

NoteType 	InkNoteGetNoteType( /* 0: Ink, 1: Text */
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh);		/* Handle of Ink DB file */

This routine gets a note's NoteType : NT_INK or NT_TEXT.

Include: pen.goh

InkNoteGetNumPages()

word 	InkNoteGetNumPages(
        dword 		tag);		/* ID# of note */

This routine returns the number of pages within the passed note.

Include: pen.goh

InkNoteGetPages()

DBGroupAndItem 	InkNoteGetPages(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh);		/* Handle of Ink DB file */

This routine returns a DB group and item containing a chunk array. The chunk array contains the page information of the note, either compressed pen data or text. Each array element holds one page of data.

Include: pen.goh

InkNoteLoadPage()

void 	InkNoteLoadPage(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh,		/* Handle of Ink DB file */
        word 		page, 		/* Page number */
        optr 		obj, 		/* an Ink or VisText object */
        word 		type);		/* note type 0: ink, 1: text */

This routine loads a visual object (Ink or Text) with the contents of the passed Ink Database page. Be sure to load only the correct type of data into an object.

Include: pen.goh

InkNoteMove()

void 	InkNoteMove(
        dword 		tag, 			/* ID# of note */
        dword 		pfolder, 			/* ID# of new parent folder */
        VMFileHandle 		fh);			/* Handle of Ink DB file */

This message moves the passed note to a new location. All references to the note are suitably altered.

Include: pen.goh

InkNoteSavePage()

void 	InkNoteSavePage(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh,		/* Handle of Ink DB file */
        word 		page, 		/* Page number */
        optr 		obj, 		/* an Ink or VisText object */
        word 		type);		/* note type 0: ink, 1: text */

This routine saves the contents of a visual object (Ink or Text) to the passed Ink Database page.

Include: pen.goh

InkNoteSendKeywordsToTextObject()

void 	InkNoteSendKeywordsToTextObject(
        dword 		tag, 		/* ID# of note */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        optr		text); 		/* Text object to set */);

This message replaces the passed VisText object's text with the keywords from the passed folder or note of an Ink Database file.

Include: pen.goh

InkNoteSetKeywords()

void 	InkNoteSetKeywords(
        dword 		tag, 			/* ID# of note */
        VMFileHandle 		fh,			/* Handle of Ink DB file */
        const char *		text); 			/* Keyword string */);

This message sets an Ink Database note's keywords. The passed string should be null-terminated.

Include: pen.goh

InkNoteSetKeywordsFromTextObject()

void 	InkNoteSetKeywordsFromTextObject(
        dword 		tag, 			/* ID# of note */
        VMFileHandle 		fh,			/* Handle of Ink DB file */
        optr *		text); 			/* Text object */);

This message sets an Ink Database note's keywords by copying them from the passed text object.

Include: pen.goh

| Up: GEOS SDK TechDocs | Up | Prev: InkFolderGetNumChildren() ... | Next: InkNoteSetModificationDate() ...


GEOS C Routines: InkNoteSetModificationDate() ...

| Up: GEOS SDK TechDocs | Up | Prev: InkNoteGetCreationDate() ... | Next: isalnum() ...

InkNoteSetModificationDate()

void 	InkNoteSetModificationDate(
        word		tdft1, 		/* First two words of */
        word		tdft2,		/*   TimerDateAndTime structure */
        dword 		note,		/* ID# of note */
        FileHandle		fh);		/* Handle of Ink DB file */

This routine sets a note's modification date.

Include: pen.goh

InkNoteSetNoteType()

void 	InkNoteSetNoteType(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh,		/* Handle of Ink DB file */
        NoteType 		nt);		/* NT_INK or NT_TEXT */

This routine sets a note's type: text or ink.

Include: pen.goh

InkNoteSetTitle()

void 	InkNoteSetTitle(
        dword 		tag, 			/* ID# of note */
        VMFileHandle 		fh,			/* Handle of Ink DB file */
        const char *		name); 			/* Text object */);

This message renames an Ink Database note. The passed name should be null-terminated. The string may be up to INK_DB_MAX_NOTE_KEYWORDS_SIZE +1 in length.

Include: pen.goh

InkNoteSetTitleFromTextObject()

void 	InkNoteSetTitleFromTextObject(
        dword 		tag, 		/* ID# of note */
        FileHandle 		fh,		/* Handle of Ink DB file */
        optr		text); 		/* Text object */);

This message sets the name of the passed Ink Database note from the contents of the passed VisText object.

Include: pen.goh

InkSendTitleToTextObject()

void 	InkSendTitleToTextObject(
        dword 		tag, 		/* ID# of folder or note */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        optr		to); 		/* Text object to set */);

This message replaces the passed VisText object's text with the name from the passed folder or note of an Ink Database file.

Include: pen.goh

InkSetDocCustomGString()

void	InkSetDocCustomGString(
        VMFileHandle 		dbfh,
        Handle		gstring);

This routine sets the custom GString to use as a background for the passed Ink Database file. Note that this custom background will only be used if the document's basic InkBackgroundType is IBT_CUSTOM. (Set this using the InkSetDocGString() routine.)

Include: pen.goh

InkSetDocGString()

void	InkSetDocGString(
        VMFileHandle 		dbfh,
        InkBackgroundType		type);

This routine sets the standard GString to use as a background picture with the passed Ink Database file. If the passed background type is custom, be sure to also call InkSetDocCustomGString() .

Include: pen.goh

InkSetDocPageInfo()

void 	InkSetDocPageInfo(
        PageSizeReport *		psr,
        VMFileHandle 		fh);

Set the page information for an Ink Database file.

Include: pen.goh

IntegerOf()

word	IntegerOf(
        WWFixedAsDWord		wwf)

This macro returns the integral portion of a WWFixedAsDWord value.

Include: geos.h

| Up: GEOS SDK TechDocs | Up | Prev: InkNoteGetCreationDate() ... | Next: isalnum() ...


GEOS C Routines: isalnum() ...

| Up: GEOS SDK TechDocs | Up | Prev: InkNoteSetModificationDate() ... | Next: LMemAlloc() ...

isalnum()

int	LocalIsAlphaNumeric(
        int	__C);

This routine returns true if the passed character is alphanumeric.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isalpha()

int	isalpha(
        int	__C);

This routine returns true if the passed character is alphabetic.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

iscntrl()

int	iscntrl(
        int	__C);

This routine returns true if the passed character is a control character.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isdigit()

int	isdigit(
        int	__c);

This routine returns true if the passed character is a decimal digit.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isgraph()

int	isgraph(
        wint	__C);

This routine returns true if the passed character is displayable.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

islower()

int	islower(
        int	__C);

This routine returns true if the passed character is a lower case alphabetic character.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isprint()

int	isprint(
        int	__C);

This routine returns true if the passed character is printable (i.e. takes up a space when printing).

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

ispunct()

int	ispunct(
        int	__c);

This routine returns true if the passed character is a punctuation mark.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isspace()

int	isspace(
        int	__c);

This routine returns true if the passed character is whitespace.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isupper()

int	isupper(
        int	__c);

This routine returns true if the passed character is an upper case alphabetic character.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isxdigit()

int	isxdigit(
        int	__C);

This routine returns true if the passed character is a hexadecimal digit.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

| Up: GEOS SDK TechDocs | Up | Prev: InkNoteSetModificationDate() ... | Next: LMemAlloc() ...


GEOS C Routines: LMemAlloc() ...

| Up: GEOS SDK TechDocs | Up | Prev: isalnum() ... | Next: LMemGetChunkSize() ...

LMemAlloc()

ChunkHandle	 LMemAlloc(
        MemHandle		mh,			/* Handle of block containing heap */
        word		chunkSize);				/* Size of new chunk in bytes */

This routine allocates a new chunk in the LMem heap. The heap must be locked or fixed. It allocates a chunk, expanding the chunk table if enccessary, and returns the chunk's handle. The chunk is not zero-initialized. If the chunk could not be allocated, it returns a null handle. Chunks are dword-aligned, so the chunk's actual size may be slightly larger than you request.

Include: lmem.h

Be Sure To: Lock the block on the global heap (unless the block is fixed).

Warnings: The heap may be compacted; thus, all pointers to chunks are invalidated. If LMF_NO_EXPAND is not set, the heap may be resized (and thus moved), thus invalidating all pointers to that block. Even fixed blocks can be resized and moved.

See Also: LMemDeref(), LMemReAlloc().

LMemContract()

void	LMemContract(
        MemHandle		mh);		/* Handle of LMem heap */

This routine contracts an LMem heap; that is, it deletes all the free chunks, moves all the used chunks to the beginning of the heap (right after the chunk handle table), and resizes the block to free the unused space at the end. It's a good idea to call this routine if you have just freed a lot of chunks, since that will free up some of the global heap. The LMem heap is guaranteed not to move; however, all pointers to chunks will be invalidated.

Be Sure To: Lock the block on the global heap (if it isn't fixed).

Include: lmem.h

LMemDeleteAt()

void	LMemDeleteAt(
        optr	chunk,				/* Chunk to resize */
        word	deleteOffset,				/* Offset within chunk of first
                     * byte to be deleted */
        word	deleteCount);				/* # of bytes to delete */

This routine deletes a specified number of bytes from inside a chunk. It is guaranteed not to cause the heap to be resized or compacted; thus, pointers to other chunks remain valid.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Warnings: The bytes you delete must all be in the chunk. If deleteOffset and deleteCount indicate bytes that are not in the chunk, results are undefined.

Include: lmem.h

See Also: LMemReAlloc(),
LMemInsertAt(),
LMemDeleteAtHandles().

LMemDeleteAtHandles()

void	LMemDeleteAtHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		ch,				/* Handle of chunk to resize */
        word		deleteOffset,				/* Offset within chunk of first
                         * byte to be deleted */
        word		deleteCount);				/* # of bytes to delete */

This routine is exactly like LMemDeleteAt() above, except that the chunk is specified by its global and chunk handles.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Warnings: The bytes you delete must all be in the chunk. If deleteOffset and deleteCount indicate bytes that are not in the chunk, results are undefined.

Include: lmem.h

LMemDeref()

void *	LMemDeref(
        optr	chunk);	/* optr to chunk to dereference */

This routine translates an optr into the address of the chunk. The LMem heap must be locked or fixed on the global heap. Chunk addresses can be invalidated by many LMem routines, forcing you to dereference the optr again.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Include: lmem.h

See Also: LMemDerefHandles().

LMemDerefHandles()

void *	LMemDerefHandles(
        MemHandle		mh,				/* Handle of LMem heap's block */
        ChunkHandle		chunk);				/* Handle of chunk to dereference */

This routine is exactly like LMemDeref() above, except that the chunk is specified by its global and chunk handles.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Include: lmem.h

See Also: LMemDeref().

LMemFree()

void	LMemFree(
        optr	chunk);				/*optr of chunk to free */

This routine frees a chunk from an LMem heap. The chunk is added to the heap's free list. The routine is guaranteed not to compact or resize the heap; thus, all pointers within the block remain valid (except for pointers to data in the freed chunk, of course).

Be Sure To: Lock the block on the global heap (unless it is fixed).

Include: lmem.h

See Also: LMemFreeHandles().

LMemFreeHandles()

void	LMemFreeHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		chunk);				/* Handle of chunk to free */

This routine is just like LMemFree() above, except that the chunk is specified by its global and chunk handles (instead of by an optr).

Be Sure To: Lock the block on the global heap (unless it is fixed).

Include: lmem.h

| Up: GEOS SDK TechDocs | Up | Prev: isalnum() ... | Next: LMemGetChunkSize() ...


GEOS C Routines: LMemGetChunkSize() ...

| Up: GEOS SDK TechDocs | Up | Prev: LMemAlloc() ... | Next: LMemReAlloc() ...

LMemGetChunkSize()

word	LMemGetChunkSize(
        optr	chunk);					/* optr of subject chunk */

This routine returns the size (in bytes) of a chunk in an LMem heap. Since LMem chunks are dword-aligned, the chunk's size may be slightly larger than the size specified when it was allocated. The routine is guaranteed not to compact or resize the heap; thus, all pointers within the block remain valid.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Include: lmem.h

See Also: LMemGetChunkSizeHandles().

LMemGetChunkSizeHandles()

word	Routine(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		chunk);				/* Handle of chunk in question */

This routine is just like LMemGetChunkSize() above, except that the chunk is specified by its global and chunk handles (instead of by an optr).

Be Sure To: Lock the block on the global heap (unless it is fixed).

Include: lmem.h

See Also: LMemGetChunkSize().

LMemInitHeap()

void	LMemInitHeap(
        MemHandle		mh,				/* Handle of (locked or fixed)
                         * block which will contain heap 	*/
        LMemType		type,				/* Type of heap to create */
        LocalMemoryFlags		flags,				/* Record of LocalMemoryFlags */
        word		lmemOffset,				/* Offset of first chunk in heap */
        word		numHandles,				/* Size of starter handle table */
        word		freeSpace);				/* Size of first free chunk
                         * created */

This routine creates an LMem heap in a global memory block. The block must be locked or fixed in memory. The routine initializes the LMemBlockHeader , creates a handle table, allocates a single free chunk, and turns on the HF_LMEM flag for the block. The block will be reallocated if necessary to make room for the heap. The routine takes six arguments:

mh
The memory block's handle
type
A member of the LMemType enumerated type, specifying the kind of block to create. For most applications, this will be LMEM_TYPE_GENERAL.
flags
A record of LocalMemoryFlags , specifying certain properties of the heap. Most applications will pass a null record.
lmemOffset
The offset within the block at which to start the heap. This must be larger than the size of the LMemBlockHeader structure which begins every heap block. Any space between the LMemBlockHeader and the heap is left untouched by all LMem routines. Usually you can just pass sizeof( LMemBlockHeader ) as this argument, or sizeof( ObjLMemBlockHeader ) for object blocks.
numHandles
The number of entries to create in the block's chunk handle table. The chunk handle table will grow automatically when all entries have been used up. Applications should generally pass the constant STD_INIT_HANDLES; they should definitely pass a positive number.
freeSpace
The amount of space to allocate to the first free chunk. Applications should generally pass the constant STD_INIT_HEAP; they should definitely pass a positive number.

To destroy an LMem heap, call MemFree() to free the block containing the heap.

Structures: There are two special data types used by this routine: LMemType and LocalMemoryFlags .

LMem heaps are created for many different purposes. Some of these purposes require the heap to have special functionality. For this reason, you must pass a member of the LMemType enumerated type to specify the kind of heap to create. The following types can be used; other types exist but should not be used with LMemInitHeap() .

LMEM_TYPE_GENERAL
Ordinary heap. Most application LMem heaps will be of this type.
LMEM_TYPE_OBJ_BLOCK
The heap will contain object instance chunks.

When an LMem heap is created, you must pass a record of flags to LMemInitHeap() to indicate how the heap should be treated. Most of the LocalMemoryFlags are only passed by system routines; the flags available for this routine are: LMF_HAS_FLAGS, LMF_DETACHABLE, LMF_NO_ENLARGE, LMF_RETURN_ERRORS. The flags can be read by examining the LMemBlockHeader structure at the beginning of the block. Ordinarily, general LMem heaps will have all flags cleared.

Tips and Tricks: If you want a fixed data space after the header, declare a structure whose first element is an LMemBlockHeader and whose other fields are for the data you will store in the fixed data space. Pass the size of this structure as the LMemOffset argument. You can now access the fixed data area by using the fields of the structure.

Be Sure To: Pass an offset at least as large as sizeof(LMemBlockHeader) . If you pass an offset that is too small, the results are undefined. Lock the block on the global heap before calling this routine (unless the block is fixed).

Warnings: The block may be relocated, if its initial size is too small to accommodate the heap. This is true even for fixed blocks. If the flag LMF_NO_ENLARGE is set, the block will never be relocated; however, you must make sure it starts out large enough to accommodate the entire heap.

Include: lmem.h

See Also: LMemBlockHeader, LMemType, LocalMemoryFlags, MemAlloc(), MemAllocLMem(), MemFree(), VMAllocLMem().

LMemInsertAt()

void	LMemInsertAt(
        optr	chunk,				/* optr of chunk to resize */
        word	insertOffset,				/* Offset within chunk of first byte
                     * to be added */
        word	insertCount);				/* # of bytes to add */

This routine inserts space in the middle of a chunk and zero-initializes the new space. The first new byte will be at the specified offset within the chunk.

Be Sure To: Lock the block on the global heap (unless it is fixed). Make sure the offset is within the specified chunk.

Warnings: This routine may resize or compact the heap; thus, all pointers to data within the block are invalidated.

You must pass an insertOffset that is actually within the chunk; if the offset is out-of-bounds, results are undefined.

Include: lmem.h

See Also: LMemReAlloc(),
LMemDeleteAt(),
LMemInsertAtHandles().

LMemInsertAtHandles()

void	LMemInsertAtHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		chunk,				/* Chunk to resize */
        word		insertOffset,				/* Offset within chunk of first byte
                         * to be added */
        word		insertCount);				/* # of bytes to add */

This routine is just like LMemInsertAt() above, except that the chunk is specified by its global and chunk handles (instead of by an optr).

Be Sure To: Lock the block on the global heap (unless it is fixed). Make sure the offset is within the specified chunk.

Warnings: This routine may resize or compact the heap; thus, all pointers to data within the block are invalidated.

You must pass an insertOffset that is actually within the chunk; if the offset is out-of-bounds, results are undefined.

Include: lmem.h

| Up: GEOS SDK TechDocs | Up | Prev: LMemAlloc() ... | Next: LMemReAlloc() ...


GEOS C Routines: LMemReAlloc() ...

| Up: GEOS SDK TechDocs | Up | Prev: LMemGetChunkSize() ... | Next: LocalAsciiToFixed() ...

LMemReAlloc()

Boolean	LMemReAlloc(
        optr	chunk,				/* optr of chunk to resize */
        word	chunkSize);				/* New size of chunk in bytes */

This routine resizes a chunk in an LMem heap. The heap must be in a locked or fixed block. If the routine succeeds, it returns zero. If it fails (because the heap ran out of space and could not be expanded), it returns non-zero.

If the new size is larger than the original size, extra bytes will be added to the end of the chunk. These bytes will not be zero-initialized. The heap may have to be compacted or resized to accommodate the request; thus, all pointers to data within the block are invalidated.

If the new size is smaller than the old, the chunk will be truncated. The request is guaranteed to succeed, and the chunk will not be moved; neither will the heap be compacted or resized. Thus, all pointers to other chunks remain valid. Reallocating a chunk to zero bytes is the same as freeing it.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Warnings: As noted, if the new size is larger than the old, the heap may be compacted or resized, invalidating pointers.

Include: lmem.h

See Also: LMemReAllocHandles(),
LMemInsertAt(),
LMemDeleteAt().

LMemReAllocHandles()

Boolean	LMemReAllocHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		chunk,				/* Handle of chunk to resize */
        word		chunkSize);				/* New size of chunk in bytes */

This routine is just like LMemReAlloc() above, except that the chunk is specified by its global and chunk handles (instead of by an optr).

Be Sure To: Lock the block on the global heap (unless it is fixed).

Warnings: As noted, if the new size is larger than the old, the heap may be compacted or resized, invalidating pointers.

Include: lmem.h

| Up: GEOS SDK TechDocs | Up | Prev: LMemGetChunkSize() ... | Next: LocalAsciiToFixed() ...


GEOS C Routines: LocalAsciiToFixed() ...

| Up: GEOS SDK TechDocs | Up | Prev: LMemReAlloc() ... | Next: LocalCustomParseDateTime() ...

LocalAsciiToFixed()

WWFixedAsDWord LocalAsciiToFixed(
        const char *		buffer,
        char **		parseEnd);

This routines converts a string like "12.345" to a fixed point number. The pointer pointed to by parseEnd will be returned pointing into the buffer after the last character parsed.

Include: localize.h

LocalCalcDaysInMonth()

word 	LocalCalcDaysInMonth(
        word 	year,
        word 	month);

This routine computes the number of days in a given month.

LocalCmpStrings()

sword	LocalCmpStrings(
        const char *		str1,
        const char *		str2,
        word		strSize);

This routine compares two strings to determine which comes first in a lexical (i.e. alphabetic) ordering. If the return value is negative, then str1 is earlier than str2 . If the return value is positive, then str1 is later than str2 . If the return value is zero, then the strings appear at the same place in alphabetical order.

Include: localize.h

LocalCmpStringsDosToGeos()

sword	LocalCmpStringsDosToGeos(
        const char *					str1,
        const char *					str2,
        word					strSize,
        word					defaultChar,
        LocalCmpStringsDosToGeosFlags 					flags);

This routine compares two strings to determine which comes first in lexical ordering. Either or both of these strings may be a DOS string. If the return value is negative, then str1 is earlier than str2 . If the return value is positive, then str1 is later than str2 . If the return value is zero, then the strings appear at the same place in alphabetical order.

Structures:

        typedef ByteFlags LocalCmpStringsDosToGeosFlags;
/* The following flags may be combined using | and &:
 * 		LCSDTG_NO_CONVERT_STRING_2,
 * 		LCSDTG_NO_CONVERT_STRING_1 */

Include: localize.h

LocalCmpStringsNoCase()

sword	LocalCmpStringsNoCase(
        const char *		str1,
        const char *		str2,
        word		strSize);

This routine compares two strings to determine which comes first in a lexical (i.e. alphabetic) ordering. The comparison used is not case-sensitive. If the return value is negative, then str1 is earlier than str2 . If the return value is positive, then str1 is later than str2 . If the return value is zero, then the strings appear at the same place in alphabetical order.

Include: localize.h

LocalCmpStringsNoSpace()

sword 	LocalCmpStringsNoSpace(
        const char 		*str1,
        const char 		*str2,
        word 		strSize);

This routine compares two strings to determine which comes first in a lexical (i.e. alphabetic) ordering. The comparison ignores spaces. If the return value is negative, then str1 is earlier than str2 . If the return value is positive, then str1 is later than str2 . If the return value is zero, then the strings appear at the same place in alphabetical order.

LocalCmpStringsNoSpaceCase()

sword 	LocalCmpStringsNoSpaceCase(
        const char 		*str1,
        const char 		*str2,
        word 		strSize);

This routine compares two strings to determine which comes first in a lexical (i.e. alphabetic) ordering. The comparison ignores spaces and is case-insensitive. If the return value is negative, then str1 is earlier than str2 . If the return value is positive, then str1 is later than str2 . If the return value is zero, then the strings appear at the same place in alphabetical order.

LocalCodePageToGeos()

Boolean	LocalCodePageToGeos(
        char *		str,
        word		strSize,			/* Size of the string, in bytes */
        DosCodePage 		codePage,
        word		defaultChar);

This routine converts a DOS string to standard GEOS text using a specified code page. Any characters for which there is no GEOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalCodePageToGeosChar()

word	LocalCodePageToGeosChar(
        word	ch,
        DosCodePage codePage,
        word	defaultChar);

This routine converts a DOS character to standard GEOS text using a specified code page. Any character for which there is no GEOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalCustomFormatDateTime()

word	LocalCustomFormatDateTime(
        char *	str,			/* Buffer to save formatted text in */
        const char *	format,			/* Format string */
        const	TimerDateAndTime *dateTime);

This routine takes a date or time and constructs a string using a custom format.

Include: localize.h

| Up: GEOS SDK TechDocs | Up | Prev: LMemReAlloc() ... | Next: LocalCustomParseDateTime() ...


GEOS C Routines: LocalCustomParseDateTime() ...

| Up: GEOS SDK TechDocs | Up | Prev: LocalAsciiToFixed() ... | Next: LocalGeosToCodePage() ...

LocalCustomParseDateTime()

word	LocalCustomParseDateTime(
        const char *		str,
        DateTimeFormat		format,
        TimerDateAndTime *		dateTime);

This routine parses a date and time string by comparing it with the passed DateTimeFormat . It fills in the fields of the TimerDateAndTime structure. Any fields which are not specified in the format string will be filled with -1.

If the string parses correctly, LocalCustomParseDateTime() returns -1. Otherwise it reutrns the offset to the start of the text which did not parse correctly.

Include: localize.h

LocalDistanceFromAscii()

WWFixedAsDword 	LocalDistanceFromAscii(
        const char *		buffer,
        DistanceUnit 		distanceUnits,
        MeasurementTypes		measurementType);

This routine takes a function like "72 pt" and returns a number representing the distance. The returned answer represents the measure in points, inches, centimeters, or some other measure as specified by the passed unit.

Include: localize.h

LocalDistanceToAscii()

word	LocalDistanceToAscii( /* Length of string, including NULL */
        char *		buffer,			/*Buffer to save formatted text in */
        word 		value,
        DistanceUnit 		distanceUnits,
        MeasurementType 		measurementType);

This routine takes a distance and a set of units and returns a string containing a properly formatted distance.

Include: localize.h

LocalDosToGeos()

Boolean	LocalDosToGeos(
        char *	str,
        word	strSize,
        word	defaultChar);

Convert a DOS string to GEOS text. Any characters for which there is no GEOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalDosToGeosChar()

word	LocalDosToGeosChar(
        word	ch,
        word	defaultChar);

Convert a DOS character to GEOS text. Any characters for which there is no GEOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalDowncaseChar()

word	LocalDowncaseChar(
        word	ch);

Return the lower case equivalent, if any, of the passed character.

Include: localize.h

LocalDowncaseString()

void	LocalDowncaseString(
        char *	str,
        word	size);		/* Size of string, in bytes */

Convert the passed string to its all lower case equivalent.

Include: localize.h

LocalFixedToAscii()

void	LocalFixedToAscii(
        char *	buffer,
        WWFixedAsDWord value,
        word	fracDigits);

This routine returns the ASCII expression of a fixed point number.

Include: localize.h

LocalFormatDateTime()

word	LocalFormatDateTime( /* Length of returned string */
        char *			str,
        DateTimeFormat 			format,
        const TimerDateAndTime *	dateTime);

This routine returns the string (e.g. "9:37") corresponding to the passed DateAndTime.

Include: localize.h

LocalFormatFileDateTime()

word 	LocalFormatFileDateTime(
        char 			*str,
        DateTimeFormat 			format,
        const FileDateAndTime 			*dateTime);

This utility routine provides a simple way to present a FileDateAndTime structure such as used with file-related operations.

Structures:

        typedef struct {
            byte        numberFormatFlags; /* NumberFormatFlags value */
            byte        decimalDigits;
            wchar       thousandsSeparator;
            wchar       decimalSeparator;
            wchar       listSeparator;
        } LocalNumericFormat;

The first byte is a NumberFormatFlags field specifying whether or not to use a leading zero. The second byte is the number of digits to show after the decimal point.

Include: localize.h

| Up: GEOS SDK TechDocs | Up | Prev: LocalAsciiToFixed() ... | Next: LocalGeosToCodePage() ...


GEOS C Routines: LocalGeosToCodePage() ...

| Up: GEOS SDK TechDocs | Up | Prev: LocalCustomParseDateTime() ... | Next: LocalLexicalValue() ...

LocalGeosToCodePage()

Boolean	LocalGeosToCodePage(
        char *		str,
        word		strSize,
        DosCodePage 		codePage,
        word		defaultChar);

Convert a GEOS string to DOS text, using the specified code page. Any characters for which there is no DOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalGeosToCodePageChar()

word	LocalGeosToCodePageChar(
        word		ch,
        DosCodePage 		codePage,
        word		defaultChar);

Convert a GEOS character to DOS text, using the specified code page. Any character for which there is no DOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalGeosToDos()

Boolean	LocalGeosToDos(
        char *	str,
        word	strSize,
        word	defaultChar);

Convert a GEOS string to DOS text. Any characters for which there is no DOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalGeosToDosChar()

word	LocalGeosToDosChar(
        word	ch,
        word	defaultChar);

Convert a GEOS character to DOS text. Any character for which there is no DOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalGetCodePage()

DosCodePage LocalGetCodePage(void);

This routine returns the current code page, used by DOS to handle international character sets.

Include: localize.h

LocalGetCurrencyFormat()

void	LocalGetCurrencyFormat(
        LocalCurrencyFormat *			buf,
        char *			symbol);

This routine returns the current currency format and symbol.

Include: localize.h

LocalGetDateTimeFormat()

void	LocalGetDateTimeFormat(
        char *		str,
        DateTimeFormat 		format);

This routine returns the user's preferred time and date formats.

Include: localize.h

LocalGetLanguage()

word 	LocalGetLanguage(void); /* Returns a StandardLanguage value */

This routine returns the user machine's language.

LocalGetMeasurementType()

MeasurementTypes LocalGetMeasurementType(void);

This routine returns the user preference between US and metric measurement systems.

Include: localize.h

LocalGetNumericFormat()

void	LocalGetNumericFormat(
        LocalNumericFormat *		buf);

This routine returns the user's preferred format for numbers.

Include: localize.h

LocalGetQuotes()

void	LocalGetQuotes(
        LocalQuotes *		quotes);

This routine returns the user's preferred quote marks.

Include: localize.h

LocalIsDateChar()

Boolean	LocalIsDateChar(
        word	ch);

This routine returns true if the passed character could be part of a date or time.

Include: localize.h

LocalIsDosChar()

Boolean	LocalIsDosChar(
        word	ch);

This routine returns true if the passed character is part of the DOS character set.

Include: localize.h

LocalIsNumChar()

Boolean	LocalIsNumChar(
        word	ch);

This routine returns true if the passed character is a number or part of the number format.

Include: localize.h

LocalIsSymbol()

Boolean	LocalIsSymbol(
        word	ch);

This routine returns true if the passed character is a symbol.

Include: localize.h

LocalIsTimeChar()

Boolean	LocalIsTimeChar(
        word	ch);

This routine returns true if the passed character is a number or part of the user's time format.

Include: localize.h

| Up: GEOS SDK TechDocs | Up | Prev: LocalCustomParseDateTime() ... | Next: LocalLexicalValue() ...


GEOS C Routines: LocalLexicalValue() ...

| Up: GEOS SDK TechDocs | Up | Prev: LocalGeosToCodePage() ... | Next: MailboxAcknowledgeMessageReceipt() ...

LocalLexicalValue()

word	LocalLexicalValue(
        word	ch);

This routine returns the passed character's lexical value, useful when trying to sort strings alphabetically.

Include: localize.h

LocalLexicalValueNoCase()

word	LocalLexicalValueNoCase(
        word	ch);

This routine returns the passed character's case-insensitive lexical value, useful when trying to sort strings alphabetically.

Include: localize.h

LocalParseDateTime()

Boolean	LocalParseDateTime(
        const char *		str,
        DateTimeFormat 		format,
        TimerDateAndTime *	dateTime);

This routine takes a string describing a date or time (e.g. "9:37") and parses it using the passed format. It returns TRUE (-1) on success. On failure it returns the offset to the start of the text that didn't match.

Include: localize.h

LocalSetCurrencyFormat()

void	LocalSetCurrencyFormat(
        const LocalCurrencyFormat *				buf,
        const char *				symbol);

This routine changes the stored preferred currency format.

Include: localize.h

LocalSetDateTimeFormat()

void	LocalSetDateTimeFormat(
        const char *		str,
        DateTimeFormat 		format);

This routine changes the stored preferred time and date format.

Include: localize.h

LocalSetMeasurementType()

void	LocalSetMeasurementType(
        MeasurementTypes meas);

This routine changes the stored preferred measurement type.

Include: localize.h

LocalSetNumericFormat()

void	LocalSetNumericFormat(
        const LocalNumericFormat *				buf);

This routine changes the stored preferred number format.

Include: localize.h

LocalSetQuotes()

void	LocalSetQuotes(
        const LocalQuotes *		quotes);

This routine changes the stored preferred quote marks.

Include: localize.h

LocalStringLength()

word	LocalStringLength(
        const char *		str);

This routine returns the length (in characters) of a null-terminated string (not counting the null), even for multibyte character sets.

Include: localize.h

LocalStringSize()

word	LocalStringSize(
        const char *		str);

This routine returns the size (in bytes) of a null-terminated string.

Include: localize.h

LocalUpcaseChar()

word	LocalUpcaseChar(
        word	ch);

This routine returns the upper case equivalent, if any, of the passed character.

Include: localize.h

LocalUpcaseString()

void	LocalUpcaseString(
        char *	str,
        word	size);

This routine converts the passed string to its all upper case equivalent.

Include: localize.h

LogAddEntry()

void 	LogAddEntry(
        LogEntry 		*logEntry);

This function adds an entry to the Contact log. (The Contact log keeps track of what calls the user has sent to and received from each of their contacts; this is the function that logs a call.) The function takes on argument, a LogEntry structure.

typedef struct {
  NameOrNumber 			LE_number;
  dword 			LE_contactID;
  LogEntryType 			LE_type;
  LogEntryDirection 			LE_direction;
  dword 			LE_duration;
  DateAndTime 			LE_datetime;
  LogEntryFlags 			LE_flags;
} LogEntry;

Normally, this function is invoked twice for each call--once at the start of the call, and again when the call is done.

When invlking the function at the start of the call, set up the LogEntry structure. Fill in the LE_number and/or LE_contactID fields if you know the contact's name, number, or RecordID number, Fill in the LE_type field with one of LET_CALL, LET_FAX, LET_SMS, LET_DATA_MAILBOX, LET_DATA_WWW, or LET_DATA_TELNET. Fill in the LE_direction field with one of LED_SENT, LED_RECEIVED, or LED_MISSED. The LE_duration should be zero. Fill in the LE_datetime field with the current date and time. (The TimerGetDateAndTime() function returns this information.) Leave the LE_flags field alone.

Keep the structure around after you call the routine; it will fill in some of the fields. Then when the call is complete, fill in the LE_duration field and invoke the function again. The LE_duration field should contain the call's duration in seconds. One way to compute this time is to call TimerGetCount() twice: once at the start of the call and once at the end. By subtracting the start time from the end time and dividing by 60, one can compute the number of seconds that passed.

Having invoked the function a second time, the program may safely free the LogEntry structure.

Include: contlog.goh

See Also: TimerGetDateAndTime(), TimerGetCount().

LogDeleteAllEntries()

void 	LogDeleteAllEntries();

This function deletes all entries in the contact log.

Include: contlog.goh

LogDestroy()

void 	LogDestroy();

This function deletes the contact log's log file.

Include: contlog.goh

| Up: GEOS SDK TechDocs | Up | Prev: LocalGeosToCodePage() ... | Next: MailboxAcknowledgeMessageReceipt() ...


GEOS C Routines: MailboxAcknowledgeMessageReceipt() ...

| Up: GEOS C Reference | Up | Prev: LocalLexicalValue()... | Next: MailboxGetBodyFormat() ...

MailboxAcknowledgeMessageReceipt()

 void 	MailboxAcknowledgeMessageReceipt(
    MailboxMessage 		msg);

This routine tells the Mailbox library that the application has taken control of the message msg. msg will be visually removed from the system inbox only when this function is called.

Warning: When the mailbox receives a message targeted for an application, it sends that application the message MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE. Should the application not acknowledge the message with MailboxAcknowledgeMessageReceipt() and the application exits and restarts, the application will again be notified of the message's availability.

Include: mailbox.h

MailboxBodyReformatted()

 MailboxError 	MailboxBodyReformatted(
    MailboxMessage 	msg,
    MailboxDataFormat 	newFormat,
    MailboxMessageFlags 	newBodyFlags);

This routine changes the format indication of the message msg to newFormat and sets the MailboxMessageFlags newBodyFlags to either MMF_BODY_DATA_VOLATILE or MMF_DELETE_BODY_AFTER_TRANSMISSION. This routine does not alter the contents of msg msg.If msg is invalid then a MailboxError will be generated.

The MailboxDataFormat structure is formatted as:

    typedef struct {		        word		MDF_id;     /*GeoworksMailboxDataFormatID*/        ManufacturerID	MDF_manuf;    } MailboxDataFormat;

Warning: MailboxBodyReformatted() is used to change the format of msg in-place. To create a copy of a message in a new format, use MailboxChangeBodyFormat() .

Include: mailbox.h

MailboxChangeBodyFormat()

 MailboxError	MailboxChangeBodyFormat(
    MailboxMessage 	msg,
    const MailboxChangeBodyFormatArgs 	*mcbfArgs);

This routine replaces the format and the body of the message msg with mcbfArgs. If msg is invalid or the body is in use, then a MailboxError will be generated.

Warning: MailboxChangeBodyFormat() should be used when creating a copy of a message in a new format. To reformat the message in-place, use MailboxBodyReformatted() .

Include: mailbox.h

MailboxChangeToMailboxDir()

void	MailboxChangeToMailboxDir(void);

This routine changes the application's current working directoryto the directory where the Mailbox library holds its message bodies and administrative files.

Warning: This is primarily intended for use by data and transport drivers.

Include: mailbox.h

MailboxCheckMediumAvailable()

Boolean  MailboxCheckMediumAvailable(
    MediumType 	mediumType,
    word 	unitNum,
    MediumUnitType 	unitType);

If the transmission medium mediumType exists, then this routine returns TRUE (non-zero); otherwise, it returns FALSE (zero). Since the Mailbox library cannot control the comings and goings of the different media, there is no guarantee that a medium which exists now will exist later.

For example, if a modem PCMCIA card is inserted, then the DATA_MODEM medium will be available (and the unit number will be the SERIAL_COMx constant by which the modem can be reached). When the PCMCIA card is removed, then that medium will no longer be available and MailboxCheckMediumAvailable() will return FALSE.

Warning: If unitType is a MUT_MEM_BLOCK (a handle to other data), it means that the Mailbox library will take responsibility for freeing the memory block. In this case, the application should no longer reference the handle it passed in as the unit number.

Include: mailbox.h

MailboxCheckMediumConnected()

Boolean 	MailboxCheckMediumConnected(
    MediumType 	mediumType,
    word 	unitNum,
    MediumUnitType 	unitType);

Sees if the Mailbox library has been told whether a connection over the specified transmission medium exists. The medium is not guaranteed to remain connected once this call returns, as the Mailbox library has no control over connections, usually. This routine returns non-zero if, while the call was in-progress, the medium using MediumType mediumType, unit number unitNum (ignored if unitType is set to MUT_NONE), and MediumUnitType unitType were connected; otherwise, the routine returns zero.

Warning: If unitType is a MUT_MEM_BLOCK (a handle to other data), it means that the Mailbox library will take responsibility for freeing the memory block. In this case, the application should no longer reference the handle it passed in as the unit number.

Include: mailbox.h

MailboxDeleteMessage()

 void 	MailboxDeleteMessage(MailboxMessage msg);

This routine lets the Mailbox library know that it is free to delete the message msg. This routine should only be called after the application has called MailboxAcknowledgeMessageReceipt() to acknowledge that it has finished handling the message.

Include: mailbox.h

MailboxDoneWithBody()

 void 	MailboxDoneWithBody(
    MailboxMessage	msg,
    const void 	*appRef,
    word 	appRefSize);

This routine indicates that the body reference appRef returned by an earlier call to MailboxGetBodyRef() will no longer be used, so the data driver is free to close the file, etc. appRef points to the body of the message msg, and appRefSize refers to the size of this body, in bytes.

Warning: After calling this routine, no further use of the body via appRef may be made.

Include: mailbox.h

MailboxDoneWithVMFile()

 void 	MailboxDoneWithVMFile(	VMFileHandle file);

Lets the Mailbox know that the application is done using the file file, a parameter which was returned from MailboxGetVMFile() . The Mailbox library is free to close file after this routine is called.

Include: mailbox.h

MailboxFreeDriver()

void	MailboxFreeDriver(
    GeodeHandle 	driverHandle);

This routine unloads the driver driverHandle previously loaded by the Mailbox library. Note that a call to MailboxLoad...Driver() should be balanced with a call to MailboxFreeDriver() .

See Also: MailboxLoadDataDriver()
MailboxLoadDataDriverWithError()
MailboxLoadTransportDriver()

Include: mailbox.h

MailboxGetAdminFile()

VMFileHandle	MailboxGetAdminFile(void);

Returns the handle of the main administrative VM file that is maintained by the Mailbox library.

Warning: The returned file should NOT be used for general storage of data. This routine is intended to allow applications that wish to register a message without the aid of a MailboxSendControl to create the transData argument required by the transport driver they've chosen, should the driver expect something to be allocated within the admin file.

Include: mailbox.h

| Up: GEOS C Reference | Up | Prev: LocalLexicalValue()... | Next: MailboxGetBodyFormat()...

This document is in Beta. Ride at your own risk. Mail us your comments and suggestions


GEOS C Routines: MailboxGetBodyFormat() ...

| Up: GEOS C Reference | Up | Prev: MailboxAcknowledgeMessageReceipt() ... | Next: MailboxGetVMFile() ...

MailboxGetBodyFormat()

 MailboxError	MailboxGetBodyFormat(
    MailboxMessage 	msg,
    MailboxDataFormat 	*dataFormat);

This routine gets the MailboxDataFormat token of the message msg and copies it into dataFormat. It returns a MailboxError if the routine is invalid.

Include: mailbox.h

MailboxGetBodyMboxRefBlock()

 MailboxError	MailboxGetBodyMboxRefBlock(
    MailboxMessage 	msg,
    MemHandle 	*mboxRefHandle);

Fetches the opaque mailbox reference to the message body of message msg and stores it in the block mboxRefHandle. This returned block contains a mailbox reference structure specific to the Data Driver. Examples are:

    /* for mailbox data drivers that use VMChains */
    typedef {
        dword VMTMR_vmChain;
        TCHAR VMTMR_filename[];
    } VMTreeMboxRef;

    /* for mailbox data drivers that use raw data files */
    typedef {
        Boolean FMR_deleteAfterTransmit;
        word FMR_diskDataOffset;
        word FMR_diskDataLen;
        word FMR_filenameLen;
        byte FMR_filenameAndDiskData[];
            /* start of null-terminated filename,
             * followed by diskData (at
             * FMR_diskDataOffset). */
    } FileDDMboxRef;
The routine returns ME_INVALID_MESSAGE if the message is invalid, or ME_NOT_ENOUGH_MEMORY if the block of data could not be created.

Note: This routine is normally used by the mailbox drivers, not by applications.

Include: mailbox.h

MailboxGetBodyRef()

 MailboxError	MailboxGetBodyRef(
    MailboxMessage 	msg,
    void 	*appRefBuf,
    word 	*appRefBufSize);

This routine retrieves the address of the message body of the message msg in the format understood and used by applications registering a message with the Mailbox library, the app-reference. The definitions of app-reference structures are defined in the documentation for each of the various communication methods, e.g. the SMS chapter. This reference to the body is copied into the buffer appRefBuf. appRefBufSize initially points to the size of this buffer in bytes, and then it is set to the number of bytes that were copied into the buffer. In the case of an error (msg is invalid, the library is unable to load the driver, there is insufficient memory, the app-ref buffer too small, there is no message body available) a MailboxError will be generated.

Warning: Each call to this routine must be matched by a call to MailboxDoneWithBody() .

Include: mailbox.h

MailboxGetDestApp()

 MailboxError	MailboxGetDestApp(
    MailboxMessage 	msg,
    GeodeToken 	*tokenBuf);

Gets the GeodeToken for the destination application from the message msg and copies it into tokenBuf. This routine returns ME_INVALID_MESSAGE if the passed MailboxMessage is invalid.

Include: mailbox.h

MailboxGetEndBound()

 MailboxError	MailboxGetEndBound(
    MailboxMessage 	msg,
    FileDateAndTime 	*dateTime);

Fetches the end bound of the message msg and copies it into dateTime. If the message is invalid, this routine returns ME_INVALID_MESSAGE.

See Also: MailboxGetStartBound()

Include: mailbox.h

MailboxGetFirstMediumUnit()

 word	 MailboxGetFirstMediumUnit(
    MediumType 	mediumType,
    MediumUnitType 	*unitType);

Puts the first available unit of the indicated MediumType mediumType into unitType. This should be used when a transport driver just wants to use a particular medium but doesn't care which one. This routine returns the unit number.

Warning: Do not call this for a medium that doesn't use unit numbers. You will generate a fatal-error in the error-checking version, and won't get any additional information in the non-error-checking version (since the return will be MUT_NONE whether there's an available unit or not). Use MailboxCheckMediumAvailable() instead. Also remember, if unitType is a handle to other memory because it is a MUT_MEM_BLOCK, it will be your responsibility to free the associated memory block.

Include: mailbox.h

MailboxGetMessageFlags()

 MailboxError	MailboxGetMessageFlags(
    MailboxMessage 	msg,
    MailboxMessageFlags 	*flags);

Retrieves the MailboxMessageFlags for the message msg and copies them into flags. If the message is invalid, ME_INVALID_MESSAGE is returned.

Include: mailbox.h

MailboxGetNumTransAddrs()

 MailboxError	MailboxGetNumTransAddrs(
    MailboxMessage 		msg,
    word 	*numAddresses);

Retrieves the number of transport addresses bound to the message msg and stores that number in numAddresses. If msg is in the inbox, then numAddressess is set to zero. If the number of addresses can't be obtained, then the routine returns ME_INVALID_MESSAGE.

See Also: MailboxGetTransAddr()

Include: mailbox.h

MailboxGetStartBound()

 MailboxError	MailboxGetStartBound(
    MailboxMessage 	msg,
    FileDateAndTime	*dateTime);

Fetches the start bound of the message msg and copies it into dateTime. If the message is invalid, this routine returns ME_INVALID_MESSAGE.

See Also: MailboxGetEndBound()

Include: mailbox.h

MailboxGetStorageType()

 MailboxError 	MailboxGetStorageType(
    MailboxMessage 	msg,
    MailboxStorage 	*storage);

This routine gets the MailboxStorage token for the message msg and copies it into storage. If the message is invalid, ME_INVALID_MESSAGE is returned.

Include: mailbox.h

MailboxGetSubjectBlock()

 MailboxError	MailboxGetSubjectBlock(
    MailboxMessage 	msg,
    MemHandle 	*subjHandle);

Retrieves the null-terminated subject/summary of the message msg and copies it into a global memory block, then puts the associated MemHandle into subjHandle. Returns ME_INVALID_MESSAGE if msg is invalid or ME_NOT_ENOUGH_MEMORY if there isn't sufficient memory to create the block.

Include: mailbox.h

MailboxGetSubjectLMem()

 MailboxError	MailboxGetSubjectLMem(
    MailboxMessage 	msg,
    MemHandle 	lmemBlock,
    ChunkHandle 	*subjectChunk);

Retrieves the subject/summary of the message msg and copies it into a local memory block lmemBlock, and puts the ChunkHandle for this null-terminated subject into subjectChunk. Returns ME_INVALID_MESSAGE if msg is invalid or ME_NOT_ENOUGH_MEMORY if there isn't sufficient memory to create the block.

Include: mailbox.h

MailboxGetTransAddr()

 Boolean	MailboxGetTransAddr(
    MailboxMessage 	msg,
    word 	addrNumber,
    void 	*addressBuffer,
    word 	*bufSizePtr);

This routine retrieves the addrNumber-th transport address from the message msg and copies the address into addressBuffer. bufSizePtr should initially point to the size of the buffer in bytes and, if the routine works successfully, it will be set to the number of bytes that were actually copied into addressBuffer. If the addrNumber is invalid because it is either too large or the message doesn't exist, then bufSizePtr will be set to zero. If the buffer isn't large enough to store the address, then bufSizePtr is set to the number of bytes required. The routine returns zero (FALSE) if an error occurred, otherwise it returns non-zero.

See Also: MailboxGetNumTransAddrs()
MailboxGetUserTransAddrLMem()
MailboxSetTransAddr()

Include: mailbox.h

MailboxGetTransData()

 MailboxError 	MailboxGetTransData(
    MailboxMessage 	msg,
    dword 	*transData);

Puts the 32-bit transport data registered with the message msg into transData. If msg is an invalid message then the routine returns ME_INVALID_MESSAGE.

See Also: MailboxSetTransData()

Include: mailbox.h

MailboxGetUserTransAddrLMem()

 MailboxError 	MailboxGetUserTransAddrLMem(
    MailboxMessage 	msg,
    word 	addrNumber,
    MemHandle 	lmemBlock,
    ChunkHandle 	*addrChunk);

Retrieves a user-readable transport address from a message. The input parameters are the message msg, the address number addrNumber, and the handle of the lmem block lmemBlock in which to place the string. The routine copies the null-terminated string into this block, and sets addrChunk to point to the string's chuck. There are three errors that could occur: if addrNumber is not valid, ME_ADDRESS_INVALID is returned; if msg is not valid, ME_INVALID_MESSAGE is returned; and if there is insufficient memory to create the chunk, ME_NOT_ENOUGH_MEMORY is returned;

See Also:MailboxGetTransAddr()

Include: mailbox.h

| Up: GEOS C Reference | Up | Prev: MailboxAcknowledgeMessageReceipt() ... | Next: MailboxGetVMFile() ...

This document is in Beta. Ride at your own risk. Mail us your comments and suggestions


GEOS C Routines: MailboxGetVMFile() ...

| Up: GEOS C Reference | Up | Prev: MailboxGetBodyFormat() ... | Next: MakeWWFixed() ...

MailboxGetVMFile()

 VMFileHandle 	MailboxGetVMFile(
    word 	expectedNumBlocks,
    word 	*vmStatusp);

This routine obtains the handle of a writable VM file in which data can be stored. The file is managed by the Mailbox library, so the data stored in it can be passed off to the library without being marked as MMF_BODY_DATA_VOLATILE.

Parameters:

expectedNumBlocks
This parameter is your estimate of your file's size, in blocks. The library uses this estimate to rationally apportion data among various VM files without getting block sizes within one file so large that the system is in danger of running out of conventional memory. Poor estimates will usually not cause fatal problems since the library is rather cautious when deciding whether to start a new file. Still, you should do your best to come up with accurate information on your needs. You should request 1 block for every 4Kb of data to store. For example, SMS message would only require 1 block. If expectedNumBlocks is set to zero, then a system-assumed value will be used.
vmStatusp
This is a pointer to a word that, in the case of an error, will be set to the error type.

Returns: If successful, this routine returns a VMFileHandle to a writable VM File. If it fails, it returns NullHandle and sets vmStatusp.

Include: mailbox.h

MailboxGetVMFileName()

void 	MailboxGetVMFileName(
    VMFileHandle 	file,
    FileLongName 	*namep);

Gets the name of a VM file file which was previously opened by the Mailbox library. This file resides in the Mailbox library's spool directory. namep is a buffer in which to place the file's FileLongName, a null-terminated file name.

Warning: MailboxGetVMFileName() is used primarily by data storage drivers to obtain a persistent reference to the body of a message since file handles aren't preserved across shutdowns or file closes.

Include: mailbox.h

MailboxLoadDataDriver()

 GeodeHandle	MailboxLoadDataDriver(
    MailboxStorage 	storage,
    GeodeLoadError 	*error);

MailboxLoadDataDriver() loads the specified data storage driver.

Parameters:

storage is a MailboxStorage token of the requested data storage driver.
error is a buffer to store a GeodeLoadError should the driver not load properly.

Return: If this routine is successful, it returns the handle of the loaded driver. Otherwise, the error is put into error.

This driver should be unloaded using MailboxFreeDriver() .

Include: mailbox.h

MailboxLoadDataDriverWithError()

 GeodeHandle	MailboxLoadDataDriverWithError(
    MailboxStorage 	storage,
    GeodeLoadError 	*error);

MailboxLoadDataDriverWithError() loads the specified data storage driver. If the driver can't be loaded then the error message error will be used to prompt the user to make the driver available, with the option to retry the load.

Parameters:

storage is a MailboxStorage token of the requested data storage driver.
error should point to a notification string that the user will see if the driver cannot load properly. If error points to NULL, then this routine behaves just like MailboxLoadDataDriver() .

Return: If this routine is successful, it returns the handle of the loaded driver. Otherwise, the error is put into error.

This driver should be unloaded using MailboxFreeDriver().

Include: mailbox.h

MailboxLoadTransportDriver()

 GeodeHandle	MailboxLoadTransportDriver(
    MailboxTransport 	transport,
    GeodeLoadError 	*error);

MailboxLoadTransportDriver() loads the transport driver with the MailboxTransport token transport and returns the handle of the driver. If the driver can't be loaded, then the GeodeLoadError is put into error.

The driver should be unloaded using MailboxFreeDriver() .

Include: mailbox.h

MailboxOpenVMFile()

 VMFileHandle 	MailboxOpenVMFile(
    FileLongName 	*namep,
    word 	*vmStatusp);

Reopens a VM file that was previously returned by MailboxGetVMFileName() , and whose name namep was obtained through MailboxGetVMFileName. If successful, the routine returns a VMFileHandle to the VMFile. If there is an error, this routine returns NullHandle with vmStatusp filled in with the appropriate VMStatus value.

Warning: Any call to this routine must be matched with a call to MailboxDoneWithVMFile() .

Include: mailbox.h

MailboxPushToMailboxDir()

void 	MailboxPushToMailboxDir();

This routine performs a FilePushDir() followed by a MailboxChangeToMailboxDir() .

Include: mailbox.h

MailboxRegisterMessage()

MailboxError MailboxRegisterMessage(
    MailboxRegisterMessageArgs 	*mrmArgs,
    MailboxMessage 		*msg);

The routine registers a message with the system. A message consists of the information the system needs to properly process that message, the MailboxRegisterMessageArgs mrmArgs, and the body data of the message itself, msg. The body data are copied, if necessary, or simply taken-possession-of by the system.

You can find examples of filling in MailboxRegisterMessageArgs in the chapters that discuss the various communication methods, such as the SMS chapter.

Include: mailbox.h

MailboxSetTransAddr()

 MailboxError	MailboxSetTransAddr(
    MailboxMessage msg,
    word addrNumber,
    const void *newAddress,
    word addrSize);

Sets the opaque transport address of the addrNumber-th address for the message msg to newAddress, replacing the existing one. addrSzie is the size of the new address buffer, in bytes. This routine returns ME_INVALID_MESSAGE if msg is invalid or ME_NOT_ENOUGH_MEMORY if there is not enough memory.

Warning: The new address may not differ from the old address in its significant address bytes. This is not intended to allow arbitrary redirection of a message, but simply for transport drivers to record their progress for a particular address in the insignificant portion of the address.

See Also: MailboxGetTransAddr()

Include: mailbox.h

MailboxSetTransData()

 MailboxError 	MailboxGetTransData(
    MailboxMessage 	msg,
    dword 	transData);

This routine replaces the 32-bit transport data registered with the message msg with transData. If the message is invalid then the routine returns ME_INVALID_MESSAGE; if successful, it returns ME_SUCCESS.

Warning: The caller is responsible for freeing any resources referred to by the previous dword transport data since the Mailbox library places absolutely no interpretation on the transData, and thus cannot know what needs to be freed when one transData dword replaces another.

See Also: MailboxGetTransAddr()

Include: mailbox.h

MailboxStealBody()

 MailboxError	MailboxStealBody(
    MailboxMessage 	msg,
    void 	*appRefBuf,
    word 	*appRefBufSize);

This routine lets an application take possession of the body of the message msg. It puts the app-reference to the body in appRefBuf. The app-reference structure defined for each communication mode is defined in the documentation for that communication mode, e.g. the SMS chapter. The application is responsible for destroying the data associated with this app-reference. If the message body is within a VM file, the application is responsible for calling MailboxDoneWithVMFile() after freeing the data that make up the body. The parameters for this routine are the message msg, a pointer appRefBuf to an app-reference, and a word appRefBufSize that points to the size of appRefBuf, in bytes. appRefBufSize is then filled with the number of bytes that are put into appRefBuf.

The possible errors are ME_INVALID_MESSAGE, ME_NOT_ENOUGH_MEMORY, ME_CANNOT_LOAD_DATA_DRIVER, ME_CANNOT_LOAD_TRANSPORT_DRIVER, ME_APP_REF_BUF_TOO_SMALL, ME_CANNOT_OPEN_MESSAGE_FILE, and ME_CANNOT_READ_MESSAGE_FILE.

Include: mailbox.h

| Up: GEOS C Reference | Up | Prev: MailboxGetBodyFormat() ... | Next: MakeWWFixed() ...


GEOS C Routines: MakeWWFixed() ...

| Up: GEOS SDK TechDocs | Up | Prev: MailboxGetVMFile() ... | Next: MemAlloc() ...

MakeWWFixed()

WWFixed MakeWWFixed(number);

This macro casts a floating-point or integer number to a WWFixed value.

Include: geos.h

malloc()

void	* malloc(
        size_t		blockSize);				/* # of bytes to allocate*/

The malloc() family of routines is provided for Standard C compatibility. If a geode needs a small amount of fixed memory, it can call one of the routines. The kernel will allocate a fixed block to satisfy the geode's malloc() requests; it will allocate memory from this block. When the block is filled, it will allocate another fixed malloc-block. When all the memory in the block is freed, the memory manager will automatically free the block.

When a geode calls malloc() , a section of memory of the size specified will be allocated out of its malloc-block, and the address of the start of the memory will be returned. The memory will not be zero-initialized. If the request cannot be satisfied, malloc will return a null pointer. The memory is guaranteed not to be moved until it is freed (with free() ) or resized (with realloc() ). When GEOS shuts down, all fixed blocks are freed, and any memory allocated with malloc() is lost.

Using too many fixed blocks degrades the memory manager's performance, slowing the whole system. For this reason, applications should not use malloc -family routines if they can possibly be avoided. They are provided only to simplify porting of existing programs; however, applications should make every effort to use the GEOS memory management and LMem routines instead. If you must use the malloc -family routines, use them sparingly, and free the memory as quickly as possible.

Tips and Tricks: You can allocate memory in another geode's malloc-block by calling GeoMalloc() . However, that block will be freed when the other geode exits.

Warnings: All memory allocated with malloc() is freed when GEOS shuts down.

Include: stdlib.h

See Also: calloc(),
free(),
GeoMalloc(),
realloc().

ManufacturerFromFormatID

word	ManufacturerFromFormatID(id);
        ClipboardItemFormatID id;

This macro extracts the word-sized manufacturer ID (of type ManufacturerIDs ) from a ClipboardInfoFormatID argument.

| Up: GEOS SDK TechDocs | Up | Prev: MailboxGetVMFile() ... | Next: MemAlloc() ...


GEOS C Routines: MemAlloc() ...

| Up: GEOS SDK TechDocs | Up | Prev: MakeWWFixed() ... | Next: MemDowngradeExclLock() ...

MemAlloc()

MemHandle MemAlloc(
        word		byteSize,				/* Size of block in bytes */
        HeapFlags		hfFlags,				/* Type of block */
        HeapAllocFlags		haFlags);				/* How to allocate block */

This routine allocates a global memory block and creates an entry for it in the global handle table. The properties of the block are determined by the HeapFlags record passed; the way the block should be allocated is determined by the HeapAllocFlags record. Both sets of flags are described below. The routine returns the block's handle. If it could not allocate the block, it returns a null handle. The block allocated may be larger than the size requested, as the block size is rounded up to the next even paragraph (one paragraph equals sixteen bytes).

HeapFlags are stored in the block's handle table entry. They can be retrieved with the routine MemGetInfo() ; some of them can be changed with the routine MemModifyFlags() . The following flags are available:

HF_FIXED
The block will not move from its place in the global heap until it is freed. If this flag is off, the memory manager may move the block while it is unlocked. If the flag is on, the block may not be locked, and HF_DISCARDABLE and HF_SWAPABLE must be off.
HF_SHARABLE
The block may be locked by threads belonging to geodes other than the block's owner.
HF_DISCARDABLE
The block may be discarded when unlocked.
HF_SWAPABLE
The block may be swapped to extended/expanded memory or to the disk swap space when it is unlocked.
HF_LMEM
The block contains a local memory heap. This flag is set automatically by LMemInitHeap() and VMAllocLMem() ; applications should not need to set this flag.
HF_DISCARDED
The memory manager turns this bit on when it discards a block. The bit is turned off when the block is reallocated.
HF_SWAPPED
The memory manager turns this bit on when it swaps a block to extended/expanded memory or to the disk swap space. It turns the bit off when it swaps the block back into the global heap.

HeapAllocFlags indicate how the block should be allocated and initialized. They are not stored and can not be retrieved. Some of the flags can be passed with MemReAlloc() . The following flags are available:

HAF_ZERO_INIT
The memory manager should initialize the block to null bytes. This flag may be passed to MemReAlloc() to cause new memory to be zero-initialized.
HAF_LOCK
The memory manager should lock the block after allocating it. To get the block's address, call MemDeref() . This flag may be passed to MemReAlloc() .
HAF_NO_ERR
The memory manager should not return errors. If it cannot allocate block, GEOS will crash. Use of this flag is strongly discouraged. This flag may be passed to MemReAlloc() .
HAF_UI
If both HAF_OBJECT_RESOURCE and HAF_UI are set, this block will be run by the application's UI thread.
HAF_READ_ONLY
The block's data will not be modified. Useful for the debugger.
HAF_OBJECT_RESOURCE
This block will be an object block.
HAF_CODE
This block contains executable code.
HAF_CONFORMING
If the block contains code, the code may be run by a less privileged entity. If the block contains data, the data may be accessed or altered by a less privileged entity.

If necessary, this routine will automatically compact the heap to find enough space to allocated the desired sized block.

Include: heap.h

See Also: MemAllocSetOwner(),
MemReAlloc(),
MemDeref().

MemAllocLMem()

MemHandle MemAllocLMem(
        LMemType		type,				/* type of LMem block */
        word		headerSize);				/* size of header structure */

This routine allocates and initializes a local memory block; it can be used to simplify this procedure from the two-step process of MemAlloc() followed by LMemInitHeap() . Pass an LMem type indicating what will be stored in the block, along with the size of the header structure to use. If the block is to have the standard header, pass zero in headerSize .

This routine returns the handle of the unlocked, newly allocated block. The block will contain two LMem handles and 64 bytes allocated for the LMem heap.

Include: lmem.h

See Also: LMemInitHeap().

MemAllocSetOwner()

MemHandle MemAllocSetOwner(
        GeodeHandle		owner,				/* Handle of block's owner */
        word		byteSize,				/* Size of block in bytes */
        HeapFlags		hfFlags,				/* Type of block */
        HeapAllocFlags		haFlags);				/* How to allocate block */

This routine is the same as MemAlloc() except that you can specify the owner of the global memory block created.

Include: heap.h

See Also: MemAlloc().

MemDecRefCount()

void	MemDecRefCount(
        MemHandle		mh);				/* handle of affected block */

This routine decrements the reference count of a global memory block (the reference count is stored in HM _otherInfo ). If the reference count reaches zero, MemDecRefCount() will free the block.

Warnings: This routine assumes that a reference count is stored in HM _otherInfo . You may only use this routine if the block has had a reference count set up with MemInitRefCount() .

Include: heap.h

MemDeref()

void	* MemDeref(
        MemHandle		mh);		/* Handle of locked block to dereference */

This routine takes one argument, the handle of a global memory block; it returns the address of the block on the global heap. If the block has been discarded, or if the handle is not a memory handle, it returns a null pointer. It gets this information by reading the block's handle table entry; it does not need to actually access the block.

Note that if the handle is of an unlocked, moveable block, MemDeref() will return the block's address with out any warning; however, the address will be unreliable, since the memory manager can move the block at any time.

Include: heap.h

Tips and Tricks: This is very useful when you allocate a fixed or locked block, and need to get the block's address without calling MemLock() .

Warnings: This routine, if given an unlocked, moveable block, will return the pointer without a warning, even though that block may move at any time.

See Also: MemGetInfo(), MemModifyFlags().

| Up: GEOS SDK TechDocs | Up | Prev: MakeWWFixed() ... | Next: MemDowngradeExclLock() ...


GEOS C Routines: MemDowngradeExclLock() ...

| Up: GEOS SDK TechDocs | Up | Prev: MemAlloc() ... | Next: MemLock() ...

MemDowngradeExclLock()

void	MemDowngradeExclLock(
        MemHandle		mh);			/* handle of affected block */

An application that has an exclusive lock on a block may downgrade it to a shared lock with this routine. It does not otherwise affect the block.

Include: heap.h

MemFree()

void	MemFree(
        MemHandle		mh);			/* handle of block to be freed */

This routine frees a global memory block. The block can be locked or unlocked.

Include: heap.h

Warnings: The routine does not care whether other threads have locked the block. If you try to free a bad handle, routine may fatal-error.

MemGetInfo()

word	MemGetInfo( /* return value depends on flag passed */
        MemHandle		mh, 			/* Handle of block to get info about */
        MemGetInfoType		info);			/* Type of information to get */

MemGetInfo() is a general-purpose routine for getting information about a global memory block. It gets the information by looking in the block's handle table entry; it does not need to access the actual block. It returns a single word of data; the meaning of that word depends on the value of the MemGetInfoType variable passed. The following types are available:

MGIT_SIZE
Return value is size of the block in bytes. This may be larger than the size originally requested, as blocks are allocated along paragraph boundaries.
MGIT_FLAGS_AND_LOCK_COUNT
The upper byte of the return value is the current HeapFlags record for the block. The lower byte is the number of locks currently on the block.
MGIT_OWNER_OR_VM_FILE_HANDLE
If the block is part of a VM file, return value is the VM file handle. Otherwise, return value is the GeodeHandle of the owning thread.
MGIT_ADDRESS
Return value is block's segment address on the global heap, or zero if block has been discarded. If block is unlocked and moveable, address may change without warning. Ordinarily, MemDeref() is preferable.
MGIT_OTHER_INFO
Returns the value of the HM_otherInfo word. This word is used in different ways for different types of handles.
MGIT_EXEC_THREAD
Returns the ThreadHandle of the thread executing this block, if any.

Include: heap.h

Warnings: If the handle is not a global memory block handle, results are unpredictable (the routine will read inappropriate data from the handle table entry).

See Also: MemDeref(),
MemModifyFlags(),
MemModifyOtherInfo(),
HandleModifyOwner().

MemIncRefCount()

void	MemIncRefCount(
        MemHandle		mh);			/* handle of affected block */

This routine increments the reference count of a global memory block (the reference count is stored in HM _otherInfo ).

Warnings: This routine assumes that a reference count is stored in HM _otherInfo . You may only use this routine if the block has had a reference count set up with MemInitRefCount() .

Include: heap.h

MemInitRefCount()

void	MemInitRefCount(
        MemHandle		mh,				/* handle of affected block */
        word		count);				/* initial reference count */

This routine sets up a reference count for the specified global memory block. The passed count is stored in the HM _otherInfo field of the block's handle-table entry.

Warnings: This routine overwrites the HM _otherInfo field. Since the semaphore routines ( HandleP() and HandleV() and the routines which use them) use this field, you cannot use both the semaphore routines and the reference count routines on the same block.

Include: heap.h

| Up: GEOS SDK TechDocs | Up | Prev: MemAlloc() ... | Next: MemLock() ...


GEOS C Routines: MemLock() ...

| Up: GEOS SDK TechDocs | Up | Prev: MemDowngradeExclLock() ... | Next: MemModifyFlags() ...

MemLock()

void	* MemLock(
        MemHandle		mh);			/* Handle of block to lock */

This routine locks a global memory block on the global heap. If the block is swapped, the memory manager swaps it back into the global heap; it then increments the lock count (up to a maximum of 255). The block will not be moved, swapped, or discarded until the lock count reaches zero. This routine returns a pointer to the start of the block, or a null pointer if block has been discarded. To get the address of a block without locking it, use MemDeref() .

Include: heap.h

Warnings: If you try to lock a bad handle, the routine may fatal-error. This routine does not check for synchronization problems; if the block is used by several threads, you should use the synchronization routines.

Never Use Situations:
Never lock a fixed block. Never use MemLock() to lock a block containing objects. Instead use ObjLockObjBlock() and then use MemUnlock() to unlock the block.

See Also: MemDeref().

MemLockExcl()

void	* MemLockExcl(
        MemHandle		mh);			/* Handle of block to grab */

If several different threads will be accessing the same global memory block, they should use data-access synchronization routines. MemLockExcl() belongs to one such set of routines. Often, several threads will need access to the same block; however, most of the time, they will not need to change the block. There is no synchronization problem if several threads read the same block at once, as long as none of them alters the block (by resizing it, writing to it, etc.) However, if a thread needs to change a block, no other thread should have access at that time.

The routines MemLockExcl() , MemLockShared() , MemUnlockShared() , and MemUnlockExcl() take advantage of this situation. They maintain a queue of threads requesting access to a block. When the block is not being used, they awaken the highest priority thread on the queue. If that thread requested exclusive access, the other threads sleep until it relinquishes access (via MemUnlockExcl() ). If it requested shared access, the routines awaken every other thread which requested shared access; the other threads on the queue will sleep until every active thread relinquishes access (via MemUnlockShared() ).

MemLockExcl() requests exclusive access to the block. If the block is not being accessed, the routine will grab exclusive access for the block, lock the block, and return the block's address. If the block is being accessed, the routine will sleep on the queue until it can get access; it will then awaken, lock the block, and return its address.

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all routines accessing the block get access with MemLockExcl() or MemLockShared() . The routines use the block's HM_otherInfo word; you must not alter it. When you are done accessing the block, make sure to relinquish access by calling MemUnlockExcl() .

Warnings: If a thread calls MemLockExcl() when it already has shared or exclusive access, it will deadlock; it will sleep until access is relinquished, but it cannot relinquish access while it is sleeping. If you try to grab a block which is owned by a different geode and is non-sharable, the routine will fatal-error.

Never Use Situations:
Never use MemLockExcl() or MemLockShared() on a fixed block. It will attempt to lock the block, and fixed blocks cannot be locked. Instead, use the HandleP() and HandleV() routines.

See Also: MemLockShared(),
MemUnlockExcl(),
MemUnlockShared().

MemLockFixedOrMovable()

void	* MemLockFixedOrMovable(
        void	* ptr);		/* virtual segment */

Given a virtual segment, this routine locks it (if it was movable). A virtual segment is an opaque pointer to a block that an application views as locked or fixed--the memory manager can actually swap locked or fixed blocks and will designate them as virtual segments.

Include: heap.h

MemLockShared()

void	* MemLockShared(
        MemHandle		mh);			/* Handle of block to grab */

MemLockShared() requests shared access to the passed block. If the block is not being accessed, or if it is held for shared access and the queue is empty, the routine gets access, locks the block, and returns the block's address. Otherwise it sleeps on the queue until the shared requests are awakened; it then locks the block and returns the block's address.

Include: heap.h

Be Sure To: Make sure that all routines accessing the block get access with MemLockExcl() or MemLockShared() . The routines use the block's HM _otherInfo word; you must not alter it. When you are done accessing the block, make sure to relinquish access by calling MemUnlockExcl() .

Warnings: If a thread calls MemLockShared() when it already has exclusive access, it will deadlock; it will sleep until access is relinquished, but it cannot relinquish access while it is sleeping. The thread must be careful not to take actions which could change the block, such as resizing it or writing to it. The routine will not enforce this. If you try to grab a block which is owned by a different geode and is non-sharable, the routine will fatal-error.

Never Use Situations:
Never use MemLockExcl() or MemLockShared() on a fixed block. It will attempt to lock the block, and fixed blocks cannot be locked. Instead, use the HandleP() and HandleV() routines.

See Also: MemLockExcl(),
MemUnlockExcl(),
MemUnlockShared().

| Up: GEOS SDK TechDocs | Up | Prev: MemDowngradeExclLock() ... | Next: MemModifyFlags() ...


GEOS C Routines: MemModifyFlags() ...

| Up: GEOS SDK TechDocs | Up | Prev: MemLock() ... | Next: MemThreadGrab() ...

MemModifyFlags()

void	MemModifyFlags(
        MemHandle		mh,					/* Handle of block to modify */
        HeapFlags		bitsToSet,					/* HeapFlags to turn on */
        HeapFlags		bitsToClear);					/* HeapFlags to turn off */

MemModifyFlags() changes the HeapFlags record of the global memory block specified. Not all flags can be changed after the block is created; only the flags HF_SHARABLE, HF_DISCARDABLE, HF_SWAPABLE, and HF_LMEM can be changed.

The routine uses the handle table entry of the block specified; it does not need to look at the actual block. The routine performs normally whether or not the block is locked, fixed, or discarded.

Include: heap.h

Warnings: If the handle is not a global memory handle, results are unpredictable; the routine will change inappropriate bits of the handle table entry.

See Also: MemGetInfo(),
HandleModifyOwner(),
MemModifyOtherInfo().

MemModifyOtherInfo()

void	MemModifyOtherInfo(
        MemHandle		mh,				/* Handle of block to modify */
        word		otherInfo);				/* New value of HM_otherInfo word */

Use this routine to change the value of the global memory block's HM _otherInfo word. Some blocks need this word left alone; for example, data-access synchronization routines use this word. Object blocks use this space to store the handle of the burden thread.

Include: heap.h

See Also: MemGetInfo(),
MemModifyFlags(),
HandleModifyOwner().

MemOwner()

GeodeHandle MemOwner(
        MemHandle		mh);			/* handle of block queried */

This routine returns the owning geode of the passed block. If the block belongs to a VM file, the owner of the VM file will be returned (unlike MemGetInfo() , which returns the VM file handle).

Include: heap.h

MemPLock()

void	* MemPLock(
        MemHandle		mh);		/* Handle of block to lock */

If several different threads will be accessing the same global memory block, they need to make sure their activities will not conflict. The way they do that is to use synchronization routines to get control of a block. MemPLock() is part of one set of synchronization routines.

If the threads are using the MemPLock() family, then whenever a thread needs access to the block in question, it can call MemPLock() . This routine calls HandleP() to get control of the block; it then locks the block and returns its address. If the block has been discarded, it grabs the block and returns a null pointer; you can then reallocate the block. When the thread is done with the block, it should release it with MemUnlockV() .

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all threads accessing the block use MemPLock() and/or HandleP() to grab the block. These routines use the HM _otherInfo field of the block's handle table entry; do not alter this field. Release the block with HandleV() or MemUnlockV() when you are done with it.

Warnings: If a thread calls MemPLock() when it already has control of the block, it will deadlock. MemThreadGrab() avoids this conflict. If you try to grab a non-sharable block owned by another thread, MemPLock() will fatal-error.

Never Use Situations:
Never use MemPLock() with a fixed block. It will try to lock the block, and fixed blocks cannot be locked. Instead, use HandleP() .

See Also: HandleP(),
MemUnlockV(),
HandleV().

MemPtrToHandle()

MemHandle MemPtrToHandle(
        void	* ptr);		/* pointer to locked block */

This routine returns the global handle of the locked block.

Include: heap.h

MemReAlloc()

MemHandle 	MemReAlloc(
        MemHandle		mh,					/* Handle of block */
        word		byteSize,					/* New size of the block */
        HeapAllocFlags		heapAllocFlags);					/* How to reallocate block */

This routine reallocates a global memory block. It can be used to resize a block; it can also be used to reallocate memory for a discarded block. Locked and fixed blocks can be reallocated; however, they may move on the global heap, so all pointers within the block must be adjusted. Note, however, that if the new size is smaller than the old size, the block is guaranteed not to move. The reallocated block may be larger than the size requested, as the block size is rounded up to the next even paragraph (one paragraph equals sixteen bytes). If you request that the block be expanded, but this is impossible, then the routine will return NullHandle.

The routine is passed a record of HeapAllocFlags . Only the flags HAF_ZERO_INIT, HAF_LOCK, and HAF_NO_ERR may be passed.

If necessary, the routine will automatically compact the heap.

Include: heap.h

Warnings: If HAF_LOCK is passed, the lock count will be incremented even if the block is already locked by this thread. The routine does not care whether the block has been locked by another thread (possibly belonging to another geode); thus, if the block is being used by more than one thread, it is important to use the synchronization routines.

See Also: MemAlloc(), MemDeref().

| Up: GEOS SDK TechDocs | Up | Prev: MemLock() ... | Next: MemThreadGrab() ...


GEOS C Routines: MemThreadGrab() ...

| Up: GEOS SDK TechDocs | Up | Prev: MemModifyFlags() ... | Next: MemUnlock() ...

MemThreadGrab()

void	* MemThreadGrab(
        MemHandle		mh);			/* Handle of block to grab */

MemThreadGrab() is used in conjunction with MemThreadGrabNB() and MemThreadRelease() to maintain data-access synchronization. If several threads will all have access to the same global memory block, they should use data-acess synchronization routines to make sure that their activities do not conflict. If a thread uses MemThreadGrab() and no other thread has grabbed the block in question, the routine will increment the "grab count," lock the block, and return its address. It can do this even if the calling thread has already grabbed the block. If another thread has grabbed the block, MemThreadGrab() will put the calling thread in a queue to get the block; the thread will sleep until it gets the block, then MemThreadGrab() will grab the block, lock it, and return its address.

If the block has been discarded, MemThreadGrab() grabs the block and returns a null pointer; you can then reallocate memory for the block.

Include: heap.h

Be Sure To: Make sure that all threads using the block use the MemThread...() routines to access it (not other data-acess synchronization routines). Do not change the HM _otherInfo word of the block's handle table entry (the routines use that word as a semaphore).

Warnings: If you try to grab a block which is owned by a different geode and is non-sharable, the routine will fatal-error.

Never Use Situations:
Never use MemThreadGrab() with a fixed block. It will try to lock the block, and fixed blocks cannot be locked. If you need data-access synchronization for a fixed block, use the HandleP() and HandleV() routines.

See Also: MemThreadGrabNB(), MemThreadRelease().

MemThreadGrabNB()

void	* MemThreadGrabNB(
        MemHandle		mh); /* handle of block to grab */

This is a data-synchronization routine to be used in conjunction with MemThreadGrab() and MemThreadRelease() . It is exactly the same as MemThreadGrab() , except that if it cannot grab the global memory block because another thread has it, the routine returns an error instead of blocking.

If successful, MemThreadGrabNB() returns a pointer to the block. If the block has been discarded, it grabs the block and returns a null pointer; you can then reallocate memory for the block. If the block has been grabbed by another thread, MemThreadGrab() returns the constant BLOCK_GRABBED.

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all threads using the block use the MemThread...() routines to access the block (not other data-access synchronization routines). Do not change the HM _otherInfo word of the block's handle table entry (the routines use that word as a semaphore).

Warnings: If you try to grab a block that is owned by a different geode and is non-sharable, the routine will fatal-error.

Never Use Situations:
Never use MemThreadGrabNB() with a fixed block. It will try to lock the block, and fixed blocks cannot be locked. If you need synchronization for a fixed block, use the HandleP() and HandleV() routines.

See Also: MemThreadGrab(), MemThreadRelease().

MemThreadRelease()

void	MemThreadRelease(
        MemHandle		mh); /* handle of locked block to release */

Use this routine to release a global memory block which you have grabbed with MemThreadGrab() or MemThreadGrabNB() . The routine decrements the grab count; if the grab count reaches zero, the routine unlocks the block.

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all threads using the block use the MemThread...() routines to access the block (not other data-access synchronization routines). Do not change the HM _otherInfo word of the block's handle table entry (the routines use that word as a semaphore). Make sure to release the block once for every time you grab it; the block is not unlocked until each of your grabs is released.

Warnings: If you try to release a block that you have not successfully grabbed, the routine will fatal-error.

See Also: MemThreadGrab(), MemThreadGrabNB().

| Up: GEOS SDK TechDocs | Up | Prev: MemModifyFlags() ... | Next: MemUnlock() ...


GEOS C Routines: MemUnlock() ...

| Up: GEOS SDK TechDocs | Up | Prev: MemThreadGrab() ... | Next: ModemAnswerCall() ...

MemUnlock()

void	MemUnlock(
        MemHandle		mh);		/* Handle of block to unlock */

This routine decrements the lock count of the indicated block. If the lock count reaches zero, the block becomes unlocked (it can be moved, swapped, or discarded). Do not try to unlock a block that has not been locked.

Include: heap.h

MemUnlockExcl()

void	MemUnlockExcl(
        memHandle		mh);			/* Handle of block to release */

If a thread has gained access to a block with MemLockExcl() , it should release the block as soon as it can. Until it does, no other thread can access the block for either shared or exclusive access. It can release the block by calling MemUnlockExcl() . This routine unlocks the block and releases the thread's access to it. If there is a queue for this block, the highest-priority thread waiting will be awakened, as described in MemLockExcl() .

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all routines accessing the block get access with MemLockExcl() or MemLockShared() . The routines use the block's HM _otherInfo word; you must not alter it. Call this routine while the block is still locked; it will call MemUnlock() to unlock the block.

Warnings: If you call this routine on a block which you have not gained access to, it may fatal-error.

See Also: MemLockExcl(),
MemLockShared(),
MemUnlockShared().

MemUnlockFixedOrMovable()

void	MemUnlockFixedOrMovable(
        void	* ptr);		/* virtual segment */

This routine unlocks a previously locked, movable virtual segment. Do not call this routine with normal locked or fixed blocks; only call it for those blocks locked with MemLockFixedOrMovable() .

Include: heap.h

MemUnlockShared()

void	MemUnlockShared(
        MemHandle		mh);		/* Handle of block to release */

If a thread has gained access to a block with MemLockShared() , it should release the block as soon as it can. Until it does, no thread can be awakened from the queue. It can release the block by calling MemUnlockShared() . This routine calls MemUnlock() , decrementing the block's lock count; it then releases the thread's access to it. If no other thread is accessing the block and there is a queue for this block, the highest-priority thread waiting will be awakened, as described in MemLockExcl() .

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all routines accessing the block get access with MemLockExcl() or MemLockShared() . These routines use the block's HM _otherInfo word; you must not alter it. Call this routine while the block is still locked; it will call MemUnlock() to unlock the block.

Warnings: If you call this routine on a block which you have not gained access to, it may fatal-error.

See Also: MemLockExcl(),
MemLockShared(),
MemUnlockExcl().

MemUnlockV()

void	MemUnlockV(
        MemHandle		mh);			/* Handle of block to release */

This routine unlocks a block with MemUnlock() , then releases its semaphore with HandleV() . Do not use this routine unless the block's semaphore was grabbed and the block locked (typically with the MemPLock() routine).

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all threads accessing the block use HandleP() or MemPLock() to access the thread. These routines use the HM _otherInfo field of the handle table entry; do not alter this field.

Warnings: Do not use this on a block unless you have grabbed it. The routine does not check to see that you have grabbed the thread; it just clears the semaphore and returns.

Never Use Situations:
Never use this routine to release a fixed block. It will try to unlock the block; fixed blocks cannot be locked or unlocked. Instead, call HandleV() directly.

See Also: MemPLock(),
HandleP(),
HandleV().

MemUpgradeSharedLock()

void	* MemUpgradeSharedLock(
        MemHandle		mh);			/* handle of locked block */

This routine upgrades a shared lock on the block to an exclusive lock, as if the caller had used MemLockExcl(). If other threads have access to the block, the caller will sleep in the access queue until it can gain exclusive access.

This routine returns the pointer of the locked block because, if the caller sleeps in the queue, the memory block could move between the call and the granting of access.

Include: heap.h

See Also: MemLockExcl(),
MemLockShared(),
MemDowngradeExclLock().

MessageSetDestination()

void	MessageSetDestination(
        EventHandle		event,			/* handle of the event to be modified */
        optr		dest);			/* new destination for the event */

This routine sets the destination of an event to the optr passed.

Include: object.h

| Up: GEOS SDK TechDocs | Up | Prev: MemThreadGrab() ... | Next: ModemAnswerCall() ...


GEOS C Routines: ModemAnswerCall() ...

| Up: GEOS SDK TechDocs | Up | Prev: MemUnlock() ... | Next: ModemSetMessageDataNotify() ...

ModemAnswerCall()

Boolean	ModemAnswerCall(
        SerialPortNum		port,			/* serial port number */
        ModemResultCode		*result);

Answers an incoming data call. Returns [???] if a connection has been established (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

See Also: ModemAutoAnswer().

ModemAutoAnswer()

Boolean	ModemAutoAnswer(
        SerialPortNum		port,		/* serial port number */
        word		number_of_rings,
        ModemResultCode		*result);

Sets modem to answer calls automatically. Does not wait for a call but returns [???] after successfully setting modem to auto-answer (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

Warnings: Modem must be in command mode when calling this routine.

See Also: ModemAnswerCall().

ModemClose()

void	ModemClose(
        SerialPortNum		port,			/* serial port number */
        StreamLingerMode		close_mode); /* manner in which to close stream */

Closes the serial port being used for a modem connection. Possible values for close_mode include STREAM_DISCARD or STREAM_LINGER.

Include: modemC.goh

Warnings: Use ModemHangup() to hang up an existing call.

See Also: ModemHangup().

ModemDial()

Boolean	ModemDial(
        SerialPortNum		port,			/* serial port number */
        word		dial_string_length,
        const char		*dial_string,	/* phone number to dial */
        ModemResultCode		*result);

Sends the modem's dial command to dial the specified number. Returns [???] if a connection has been established (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

See Also: ModemOpen().

ModemFactoryReset()

Boolean	ModemFactoryReset(
        SerialPortNum		port,			/* serial port number */
        ModemResultCode		*result);

Restores factory settings of modem. Returns [???] after successfully resetting (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

Warnings: Modem must be in command mode when calling this routine.

ModemHangup()

Boolean	ModemHangup(
        SerialPortNum		port,			/* serial port number */
        ModemResultCode		*result);

Hangs up an existing data call. Returns [???] after successfully hanging up (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

See Also: ModemClose().

ModemInitModem()

Boolean	ModemInitModem(
        SerialPortNum		port,			/* serial port number */
        word		init_string_length,
        const char		*init_string,	/* initialization string */
        ModemResultCode		*result);

Initializes modem with modem command string. A lead "AT" need not be supplied. Returns [???] after successfully initializing (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

Warnings: Modem must be in command mode when calling this routine.

ModemOpen()

Boolean	ModemOpen(
        GeodeHandle		drHandle,			/* handle of serial driver */
        SerialPortNum		port,			/* serial port number */
        StreamOpenFlags		flags,			/* manner in which to open stream */
        word		input_buffer_size,
        word		output_buffer_size,
        word		timeout);			/* timeout if SOF_TIMEOUT */

Opens the serial port to be used for a modem connection and places modem in command mode. Possible values for flags are SOF_TIMEOUT and SOF_NOBLOCK.

Include: modemC.goh

See Also: ModemDial().

ModemReset()

Boolean	ModemReset(
        SerialPortNum		port,			/* serial port number */
        ModemResultCode		*result);

Switches to command mode and resets the modem. Returns [???] after successfully resetting (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

See Also: ModemFactoryReset().

| Up: GEOS SDK TechDocs | Up | Prev: MemUnlock() ... | Next: ModemSetMessageDataNotify() ...


GEOS C Routines: ModemSetMessageDataNotify() ...

| Up: GEOS SDK TechDocs | Up | Prev: ModemAnswerCall() ... | Next: NameArrayAdd() ...

ModemSetMessageDataNotify()

void	ModemSetMessageDataNotify(
        SerialPortNum		port,		/* serial port number */
        Message		msg,		/* msg to be sent upon notification */
        optr		destination);	/* object to receive msg */

Registers a message to be sent when when data is received from the modem during an active call. As with the serial driver's data notification, further notification will not be sent until the serial port has been read. [MEANING UNTIL THE STREAM HAS BEEN READ???]

Be Sure To: Use the prototype for the message you are registering.

@prototype void MODEM_DATA_NOTIFICATION();

Include: modemC.goh

See Also: ModemSetRoutineDataNotify().

ModemSetMessageEndCallNotify()

void	ModemSetMessageEndCallNotify(
        SerialPortNum		port,		/* serial port number */
        Message		msg,		/* msg to be sent upon notification */
        optr		destination);	/* object to receive msg */

Registers a message to be sent when a call has ended for any reason. To "un-register," pass NullOptr in destination.

Be Sure To: Use the prototype for the message you are registering.

@prototype void MODEM_END_CALL_NOTIFICATION();

Warnings: All platforms may not support this feature.

Include: modemC.goh

ModemSetMessageResponseNotify()

void	ModemSetMessageResponseNotify(
        SerialPortNum		port,		/* serial port number */
        Message		msg,		/* msg to be sent upon notification */
        optr		destination);	/* object to receive msg */

Registers a message to be sent when when a response is received from the modem (while the modem is in command mode). Response is passed in a memory block which the handler should free.

Be Sure To: Use the prototype for the message you are registering.

@prototype void MODEM_RESPONSE_NOTIFICATION();

Include: modemC.goh

See Also: ModemSetRoutineResponseNotify().

ModemSetRoutineDataNotify()

void	ModemSetRoutineDataNotify(
        SerialPortNum		port,		/* serial port number */
        word		data,		/* data passed to callback */
        void _pascal (*NotifyRoutine) (word data));

Registers a routine to be called when data is received from the modem during an active call. As with the serial driver's data notification, further notification will not be sent until the serial port has been read. [MEANING UNTIL THE STREAM HAS BEEN READ???]

Include: modemC.goh

See Also: ModemSetMessageDataNotify().

ModemSetRoutineResponseNotify()

void	ModemSetRoutineResponseNotify(
        SerialPortNum		port,		/* serial port number */
        word		data,		/* data passed to callback */
        void _pascal (*NotifyRoutine) (
                word		data,
                word		responseSize,
                char		*response));

Registers a routine to be called when a response is received from the modem (while modem is in command mode).

Include: modemC.goh

See Also: ModemSetMessageResponseNotify().

| Up: GEOS SDK TechDocs | Up | Prev: ModemAnswerCall() ... | Next: NameArrayAdd() ...


GEOS C Routines: NameArrayAdd() ...

| Up: GEOS SDK TechDocs | Up | Prev: ModemSetMessageDataNotify() ... | Next: NameArrayFind() ...

NameArrayAdd()

word	NameArrayAdd(
        optr 		arr,				/* optr of name array */
        const char		* nameToAdd,				/* Name of new element */
        word		nameLength,				/* Length of name; pass zero if
                         * name string is null-terminated */
        NameArrayAddFlags		flags,				/* Described below */
        const	void	* data);				/* Copy this data to new element */

This routine creates a new element in a name array, copying the passed name and data into the new element. If no element with the passed name exists, NameArrayAdd() will create the element and return its token. If an element with the same name already exists, the existing element's reference count will be incremented and its token will be returned. The routine takes the following arguments:

array
The optr of the name array.
nameToAdd
The name of the new element. This string may contain nulls.
nameLength
The length of the name string, in bytes. If you pass zero, NameArrayAdd() will assume the string is null-terminated.
flags
A record of NameArrayAddFlags , described below.
data
The data to copy into the new element.

Structures: The argument is passed a set of NameArrayAddFlags . Only one flag is currently defined:

NAAF_SET_DATA_ON_REPLACE
If an element with the specified name exists and this flag is set, the data passed will be copied into the data area of the existing element. If this flag is not set, the existing element will not be changed.

Warnings: This routine may resize the name array; therefore, all pointers to the LMem heap are invalidated.

Include: chunkarr.h

NameArrayAddHandles()

dword	NameArrayAddHandles(
        MemHandle 		mh,				/* Handle of LMem heap */
        ChunkHandle 		arr,				/* Chunk handle of name array */
        const char * 		nameToAdd,				/* Name of new element */
        word		nameLength,				/* Length of name; pass zero if
                         * name string is null-terminated */
        NameArrayAddFlags		flags,				/* Described below */
        const	void *	data);				/* Copy this data to new element */

This routine is exactly like NameArrayAdd() above, except that the name array is specified by its global and chunk handles (instead of with an optr).

Warnings: This routine may resize the name array; therefore, all pointers to within the LMem heap are invalidated.

Include: chunkarr.h

NameArrayChangeName()

void	NameArrayChangeName(
        optr 		array,			/* optr of name array */
        word		token,			/* Token of element to be changed */
        const char * 		newName,			/* New name for element */
        word		nameLength);				/* Length of name in bytes; pass
                         * zero if name string is
                         * null-terminated */

This routine changes the name of an element in a name array.

Warnings: If the new name is longer than the old, the chunk will be resized, invalidating all pointers to within the LMem heap.

Include: chunkarr.h

NameArrayChangeNameHandles()

dword	NameArrayChangeNameHandles(
        MemHandle 		mh,				/* Handle of LMem heap */
        ChunkHandle 		array,				/* Chunk handle of name array */
        word		token,				/* Token of element to be changed */
        const char * 		newName,				/* New name for element */
        word		nameLength);				/* Length of name in bytes; pass
                         * zero if name string is
                         * null-terminated */

This routine is exactly like NameArrayChangeName() above, except that the name array is specified by its global and chunk handles (instead of with an optr).

Warnings: If the new name is longer than the old, the chunk will be resized, invalidating all pointers to within the LMem heap.

Include: chunkarr.h

NameArrayCreate()

ChunkHandle 	NameArrayCreate(
        MemHandle 		mh,				/* Global handle of LMem heap */
        word		dataSize,				/* Size of data section for
                         * each element */
        word		headerSize);				/* Size of header; pass
                         * zero for default header */

This routine creates a name array in the indicated LMem heap. It creates a NameArrayHeader structure at the head of a new chunk. If you want to leave extra space before the start of the array, you can pass a larger header size; if you want to use the standard header, pass a header size of zero.

You must specify the size of the data portion of each element when you create the array. The name portion will be variable sized.

Include: chunkarr.h

Tips and Tricks: If you want extra space after the NameArrayHeader , you may want to create your own header structure, the first element of which is a NameArrayHeader . You can pass the size of this header to NameArrayCreate() and access the data in your header via the structure fields.

Be Sure To: Lock the block on the global heap before calling this routine (unless it is fixed). If you pass a header size, make sure it is larger than sizeof(NameArrayHeader) .

Include: chunkarr.h

NameArrayCreateAt()

ChunkHandle 	NameArrayCreateAt(
        optr	array,	/* Turn this chunk into a name array */
        word	dataSize,	/* Size of data section of each element */
        word	headerSize); 	/* Size of header; pass zero for default header */

This routine is just like NameArrayCreate() above, except that the element array is created in a pre-existing chunk. The contents of that chunk will be destroyed.

Include: chunkarr.h

Warnings: If the chunk isn't large enough, it will be resized. This will invalidate all pointers to chunks in that block.

Include: chunkarr.h

NameArrayCreateAtHandles()

ChunkHandle 	NameArrayCreateAtHandles(
        MemHandle 		mh,				/* Global handle of LMem heap */
        ChunkHandle 		chunk,				/* the chunk for the array */
        word		dataSize,				/* Size of data for each element */
        word		headerSize);				/* Size of header; pass
                         * zero for default header */

This routine is exactly like NameArrayCreateAt() above, except that the name array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

Warnings: If the chunk isn't large enough, it will be resized. This will invalidate all pointers to chunks in that block.

Include: chunkarr.h

| Up: GEOS SDK TechDocs | Up | Prev: ModemSetMessageDataNotify() ... | Next: NameArrayFind() ...


GEOS C Routines: NameArrayFind() ...

| Up: GEOS SDK TechDocs | Up | Prev: NameArrayAdd() ... | Next: ObjCompFindChildByOptr() ...

NameArrayFind()

word	NameArrayFind(
        optr 		array,				/* optr to name array */
        const char		* nameToFind,				/* Find element with this name */
        word		nameLength,				/* Pass zero if name string is
                         * null-terminated */
        void *		returnData);				/* Copy data section into this
                         * buffer */

This routine locates the element with the specified name. It returns the token of the element and copies its data section into the passed buffer. If there is no element with the specified name, the routine will return CA_NULL_ELEMENT. The routine takes the following arguments:

array
The optr of the name array.
nameToAdd
The name of the element to find. This string may contain nulls.
nameLength
The length of the name string, in bytes. If you pass zero, NameArrayFind() will assume the string is null-terminated.
returnData
The data section of the element is written to this buffer.

Include: chunkarr.h

Warnings: You must make sure the returnData buffer is large enough to hold an element's data portion; otherwise, data after the buffer will be overwritten.

NameArrayFindHandles()

word	NameArrayFindHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle 		array,				/* Handle of name array */
        const char *		nameToFind,				/* Find element with this name */
        word		nameLength,				/* Pass zero if name string is
                         * null-terminated */
        void *		returnData);				/* Copy data section into this
                         * buffer */

This routine is exactly like NameArrayFind() above, except that the name array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

NEC()

NEC(line)

This macro defines a line of code that will only be compiled into the non -error-checking version of the geode. The line parameter of the macro is the actual line of code. When the non-EC version of the program is compiled, the line will be treated as a normal line of code; when the EC version is compiled, the line will be ignored.

See Also: EC().

Include: ec.h

ObjBlockGetOutput()

optr	ObjBlockGetOutput(
        MemHandle mh);			/* handle of the subject object block */

This routine returns the optr of the output destination object set for the specified object block. The output object is stored in the object block's header in the OLMBH_output field. If the block has no output set, NullOptr will be returned.

Include: object.h

See Also: ObjLMemBlockHeader.

ObjBlockSetOutput()

void	ObjBlockSetOutput(
        MemHandle		mh,			/* handle of the subject object block */
        optr		o);			/* optr of the new output object */

This routine sets the OLMBH_output field in the specified object block's header. The optr passed in o will be set as the block's output.

Include: object.h

See Also: ObjLMemBlockHeader.

ObjCompAddChild()

void	ObjCompAddChild(
        optr	obj,		/* optr of parent composite */
        optr	objToAdd,		/* optr of new child */
        word	flags,		/* CompChildFlags */
        word	masterOffset,		/* offset to master part */
        word	compOffset,		/* offset to comp field in master part */
        word	linkOffset);		/* offset to link field in master part */

This routine adds the given object to an object tree as the child of another specified object. It returns nothing. You will not likely want to use this routine; instead, you will probably use the messages listed below under "See Also." The parameters of this routine are

obj
The optr of the parent composite object. The parent must be a composite; if it is not, an error will result.
objToAdd
The optr of the child object. The child must have a link instance field (defined with @link ).
flags
A record of CompChildFlags . These flags indicate whether the object should initially be marked dirty as well as where in the parent's child list the new child should be placed (first, second, last, etc.).
masterOffset
The offset within the parent's instance chunk to the master group's offset. (The value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure.)
compOffset
The offset within the parent's instance chunk to the composite field.
linkOffset
The offset within the parent's instance chunk to the link field.

Warnings: This routine may resize and move LMem and Object blocks on the heap, thereby invalidating all segment addresses and pointers.

Include: metaC.goh

See Also: MSG_VIS_ADD_CHILD, MSG_GEN_ADD_CHILD.

ObjCompFindChildByNumber()

optr	ObjCompFindChildByNumber(
        optr	obj,		/* parent's optr */
        word	childToFind,		/* zero-based child number */
        word	masterOffset,		/* offset to master part */
        word	compOffset,		/* offset to comp field in master part */
        word	linkOffset);		/* offset to link field in master part */

This routine returns the optr of the passed object's child; the child is specified based on its position in the object's child list. You will not often use this routine, but you will probably use the messages listed under "See Also" instead. The routine's parameters are listed below:

obj
The optr of the parent object.
childToFind
The zero-based number of the child to be found. For example, to return the first child's optr, pass zero or CCO_FIRST; to return the last child's optr, pass CCO_LAST.
masterOffset
The offset within the parent's instance chunk to the master group's offset. (The value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure.)
compOffset
The offset within the parent's instance chunk to the composite field.
linkOffset
The offset within the parent's instance chunk to the link field.

Include: metaC.goh

See Also: MSG_GEN_FIND_CHILD, MSG_VIS_FIND_CHILD.

| Up: GEOS SDK TechDocs | Up | Prev: NameArrayAdd() ... | Next: ObjCompFindChildByOptr() ...


GEOS C Routines: ObjCompFindChildByOptr() ...

| Up: GEOS SDK TechDocs | Up | Prev: NameArrayFind() ... | Next: ObjCompProcessChildren() ...

ObjCompFindChildByOptr()

word	ObjCompFindChildByOptr(
        optr	obj,		/* parent's optr */
        optr	childToFind,		/* optr of child to find */
        word	masterOffset,		/* offset to master part */
        word	compOffset,		/* offset to comp field in master part */
        word	linkOffset);		/* offset to link field in master part */

This routine returns the zero-based child number of an object given its optr and its parent's optr. The returned number represents the child's position in its parent's child list. For example, a return value of zero indicates the object is the parent's first child. You will not likely use this routine; instead, you will probably use the messages shown below under "See Also."

The parameters for this routine are listed below:

obj
The optr of the parent object.
childToFind
The optr of the child whose number is to be returned. If the child is not found, the routine will return -1.
masterOffset
The offset within the parent's instance chunk to the master group's offset. (The value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure.)
compOffset
The offset within the parent's instance chunk to the composite field.
linkOffset
The offset within the parent's instance chunk to the link field.

Include: metaC.goh

See Also: MSG_GEN_FIND_CHILD, MSG_VIS_FIND_CHILD.

ObjCompMoveChild()

void	ObjCompMoveChild(
        optr	obj,		/* parent's optr */
        optr	objToMove,		/* optr of child to move */
        word	flags,		/* CompChildFlags */
        word	masterOffset,		/* offset to master part */
        word	compOffset,		/* offset to comp field in master part */
        word	linkOffset);		/* offset to link field in master part */

This routine moves the specified child within its parent's child list. This routine will not move a child from one parent to another, but it can reorganize a parent's children. You will not likely use this routine, but you may often use the messages listed under "See Also" below.

The parameters of this routine are shown below:

obj
The optr of the parent object.
objToMove
The optr of the child to be moved. If the optr does not point to a valid child, behavior is undefined and an error is likely.
flags
A record of CompChildFlags indicating the new position of the child and whether the link should be marked dirty.
masterOffset
The offset within the parent's instance chunk to the master group's offset. (The value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure.)
compOffset
The offset within the parent's instance chunk to the composite field.
linkOffset
The offset within the parent's instance chunk to the link field.

Warnings: This routine may cause LMem and/or Object Blocks to move or to shuffle their chunks, thereby invalidating any segment addresses or pointers.

Include: metaC.goh

See Also: MSG_GEN_MOVE_CHILD, MSG_VIS_MOVE_CHILD.

| Up: GEOS SDK TechDocs | Up | Prev: NameArrayFind() ... | Next: ObjCompProcessChildren() ...


GEOS C Routines: ObjCompProcessChildren() ...

| Up: GEOS SDK TechDocs | Up | Prev: ObjCompFindChildByOptr() ... | Next: ObjDeref() ...

ObjCompProcessChildren()

Boolean	ObjCompProcessChildren(
        optr		obj,				/* parent's optr */
        optr		firstChild,				/* optr of first child to process */
        ObjCompCallType		stdCallback,				/* standard callback type */
        void 		* cbData,				/* data passed to callback */
        word		masterOffset,				/* offset to master part */
        word		compOffset,				/* offset to comp field */
        word		linkOffset);				/* offset to link field */
        Boolean _pascal (*callback) (optr parent, optr child, void *cbData));

This routine performs a specific set of actions on all or some of an object's children. It is very rare that you will use this routine; typically, you should send a message to all of the parent's children. If, however, you use this routine, you must also write a callback routine that will be executed once for each affected child.

ObjCompProcessChildren() returns true (nonzero) only if it was stopped before all children had been processed. The only two ways this could be returned is if an error occurs or if your callback returns true when called.

The parameters for this routine are

obj
The optr of the composite whose children are to be processed.
firstChild
The optr of the first child to be processed. This routine will begin with the passed child and continue with all subsequent children. Pass the optr of the composite's first child--retrieved with the routine ObjCompFindChildByNumber() --to process all children.
stdCallback
A value of ObjCompCallType indicating how the data in the buffer pointed to by cbData will be passed to your callback routine. These values are detailed below.
cbData
A pointer to a buffer in which data can be passed to your callback routine. This buffer can be altered by your callback.
masterOffset
The offset within the parent's instance chunk to the master group's offset. (The value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure.)
compOffset
The offset within the parent's instance chunk to the composite field.
linkOffset
The offset within the parent's instance chunk to the link field.
callback
A pointer to the actual callback routine that will be executed once for each child. The callback should be in your geode's fixed memory. The parameters and return values for the callback routine are given below.

The callback routine takes three parameters and returns a boolean value. It must be declared _pascal. The three parameters to the callback are listed below:

parent
The optr of the parent composite.
child
The optr of the current child being processed.
cbData
A pointer to the buffer passed by the original caller of ObjCompProcessChildren() . What is actually in this buffer may depend on the value in the original sdtCallback parameter; if the buffer is not saved and restored by ObjCompProcessChildren() between children, each child may receive data altered by the previous child.

The callback routine can access and alter the buffer pointed to by cbData , or it can query the child or do anything else with the exception of destroying the child. It should return a Boolean value: true if ObjCompProcessChildren() should be aborted, false if it should not.

The values you can pass to ObjCompProcessChildren() in stdCallback are of type ObjCompCallType . You can use one of the following values to specify how the buffer in cbData will be passed on to the next child's callback routine:

OCCT_SAVE_PARAMS_TEST_ABORT
Save the buffer passed in cbData before calling each child; abort the routine if the callback returns true .
OCCT_SAVE_PARAMS_DONT_TEST_ABORT
Save the buffer passed in cbData before calling each child; do not check the return value of the callback before proceeding to the next child.
OCCT_DONT_SAVE_PARAMS_TEST_ABORT
Do not save the buffer in cbData , and abort if the callback routine returns true .
OCCT_DONT_SAVE_PARAMS_DONT_TEST_ABORT
Do not save the buffer in cbData , and do not check the callback routine's return value.
OCCT_ABORT_AFTER_FIRST
Abort the processing after only one child (typically used to call the nth child).
OCCT_COUNT_CHILDREN
Counts the number of children rather than calling each.

Include: metaC.goh

See Also: @send, @call, MSG_META_SEND_CLASSED_EVENT.

ObjCompRemoveChild()

void	ObjCompRemoveChild(
        optr	obj,		/* parent's optr */
        optr	objToRemove		/* optr of child to be removed */
        word	flags,		/* CompChildFlags */
        word	masterOffset,		/* offset to master part */
        word	compOffset,		/* offset to comp field in master part */
        word	linkOffset);		/* offset to link field in master part */

This routine removes the given child from the specified parent composite. The child will be removed entirely from the object tree, but it will not be detached or freed. The parameters of this routine are listed below:

obj
The optr of the parent composite.
objToRemove
The optr of the child to be removed.
flags
A record of CompChildFlags indicating whether the parent and child should be marked dirty after the operation.
masterOffset
The offset within the parent's instance chunk to the master group's offset. (The value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure.)
compOffset
The offset within the parent's instance chunk to the composite field.
linkOffset
The offset within the parent's instance chunk to the link field.

Include: metaC.goh

ObjDecInteractibleCount()

void	ObjDecInteractibleCount(
        MemHandle mh);			/* subject object block */

This routine decrements the given object block's interactable count. Do not decrement the interactable count without first incrementing it with ObjIncInteractibleCount() . Visible objects automatically decrement the interactable count in their MSG_VIS_CLOSE handlers.

Include: object.h

See Also: ObjIncInteractibleCount(), MSG_VIS_CLOSE, ObjLMemBlockHeader.

ObjDecInUseCount()

void	ObjDecInUseCount(
        MemHandle mh);		/* subject object block */

This routine decrements the given object block's in-use count. When the in-use count reaches zero, the block may safely be freed. You should not decrement the in-use count of a block without first incrementing it at some point with ObjIncInUseCount() .

Warnings: Do not decrement the in-use count without incrementing it first. An error will result.

Include: object.h

See Also: ObjIncInUseCount(), ObjDecInteractibleCount(), ObjLMemBlockHeader.

| Up: GEOS SDK TechDocs | Up | Prev: ObjCompFindChildByOptr() ... | Next: ObjDeref() ...


GEOS C Routines: ObjDeref() ...

| Up: GEOS SDK TechDocs | Up | Prev: ObjCompProcessChildren() ... | Next: ObjDoRelocation() ...

ObjDeref()

void	* ObjDeref(
        optr	obj			/* optr to dereference */
        word	masterLevel);			/* specific master level to dereference */

This routine dereferences the given optr and master level to reset the message parameter pself . Because many routines and messages may cause the calling object's instance chunk to move, the pself parameter may become invalid. The two parameters to ObjDeref() are

obj
The optr of the object to be dereferenced; nearly always you will want to pass oself .
masterLevel
The master level of the part to be dereferenced. This is the offset into the instance chunk where the offset to the master part is stored. Since pself points to the first byte of a master part, you must specify which master part you are dereferencing.

For example, a visible object dereferencing its VisClass instance data would call this routine as follows:

pself = ObjDeref(oself, 4);

Note, however, the ObjDeref1() and ObjDerefVis() exist to dereference the Vis master part, and ObjDeref2() and ObjDerefGen() exist to dereference the Gen master part.

Include: object.h

See Also: ObjDeref1(), ObjDeref2().

ObjDerefHandles()

void	* ObjDerefHandles(
        MemHandle		mh,					/* handle portion of optr */
        ChunkHandle		ch,					/* chunk portion of optr */
        word		masterLevel);					/* master level to dereference */

This routine is exactly the same as ObjDeref() , above, except that the optr is specified as its separate handles.

Include: object.h

ObjDeref1()

void	* ObjDeref1(
        optr obj);			/* optr of object to be dereferenced */

This routine is a special version of ObjDeref() which dereferences the first master part of an object. Visible objects should use this routine or ObjDerefVis() instead of ObjDeref() .

Include: object.h

See Also: ObjDeref(), ObjDeref2().

ObjDeref1Handles()

void	*ObjDeref1Handles(
        MemHandle		mh,			/* handle portion of optr */
        ChunkHandle		ch,);			/* chunk handle portion of optr */

This routine is exactly like ObjDeref1() , above, except that the optr is specified as its separate handles.

Include: object.h

ObjDeref2()

void	* ObjDeref2(
        optr	obj);		/* optr of object to be dereferenced */

This routine is a specialized version of ObjDeref() which dereferences the second master part of an object. Generic objects should use this routine or ObjDerefGen() instead of ObjDeref() .

Include: object.h

See Also: ObjDeref(), ObjDeref1().

ObjDeref2Handles()

void	* ObjDeref2Handles(
        MemHandle		mh,/			/* handle portion of optr */
        ChunkHandle		ch);			/* chunk portion of optr */

This routine is exactly like ObjDeref2() , above, except that the optr is specified as its separate handles.

Include: object.h

ObjDerefGen()

void	* ObjDerefGen(
        optr	obj);			/* generic object to be dereferenced */

This routine is exactly the same as ObjDeref2() and dereferences the Gen master part of a generic object.

Include: object.h

See Also: ObjDeref(), ObjDeref2().

ObjDerefVis()

void	* ObjDerefVis(
        optr	obj);			/* visible object to be dereferenced */

This routine is exactly the same as ObjDeref1() and dereferences the Vis master part of a visible object or a visibly-realized generic object.

Include: object.h

See Also: ObjDeref(), ObjDeref1().

| Up: GEOS SDK TechDocs | Up | Prev: ObjCompProcessChildren() ... | Next: ObjDoRelocation() ...


GEOS C Routines: ObjDoRelocation() ...

| Up: GEOS SDK TechDocs | Up | Prev: ObjDeref() ... | Next: ObjFreeChunk() ...

ObjDoRelocation()

Boolean	ObjDoRelocation( /* returns true if error */
        ObjRelocationType		type,				/* type of relocation */
        MemHandle		block,				/* handle of info block */
        void		* sourceData,				/* source of relocation */
        void		* destData);				/* relocated value */

This routine relocates a given word or dword argument and is used for resolving handles and pointers to movable objects. Most often, relocation and unrelocation occur when resources are loaded, swapped, or saved, and this is in most cases taken care of by the kernel.

ObjDoRelocation() takes four parameters:

type
The type of relocation to be performed ( RelocationType ). This can be one of the three values shown below.
block
The handle of the block containing the relocation.
sourceData
A pointer to the source of the relocation; this pointer should be cast to the proper type (word or dword) when calling the routine.
destData
A pointer to the value to be returned; this pointer should be cast appropriately when the routine is called. The exact type of return value depends on sourceData and type , above.

The type of relocation to be done affects the type of data passed in sourceData and destData . The relocation type is passed in the type parameter and must be one of the following enumerations of RelocationType :

RELOC_RELOC_HANDLE
The relocation will be from a resource ID to a handle. The sourceData pointer should be cast to type word, and the destData pointer should be cast to type Handle.
RELOC_RELOC_SEGMENT
The relocation will be from a resource ID to a segment address. The sourceData pointer should be cast to type word, and the destData pointer should be cast to type Segment.
RELOC_RELOC_ENTRY_POINT
The relocation will be from either a resource ID or an entry number to an entry point. Both the sourceData pointer and the destData pointer should be cast to type dword.

ObjDoRelocation() returns an error flag that will be true if an error occurs, false otherwise.

The relocation done by this routine can be undone with ObjDoUnRelocation() .

Include: object.h

ObjDoUnRelocation()

Boolean	ObjDoUnRelocation( /* returns true if error */
        ObjRelocationType		type,				/* type of relocation */
        MemHandle		block,				/* handle of info block */
        void		* sourceData,				/* source of relocation */
        void		* destData);				/* relocated value */

This routine unrelocates a given word or dword. It translates a handle, a segment address, or an entry point back into a resource ID. The translation done is the exact reverse of that done by ObjDoRelocation() . See that routine (above) for more information.

ObjDoUnRelocation() returns an error flag that will be true if an error occurs and false if the unrelocation is successful. The unrelocated resource ID will be returned pointed to by the destData pointer.

Include: object.h

See Also: ObjDoRelocation().

ObjDuplicateMessage()

EventHandle ObjDuplicateMessage(
        EventHandle msg);				/* event to duplicate */

This routine duplicates a prerecorded event, returning the event handle of the new event. Pass the handle of the event to be duplicated. You can then change information about the event with MessageSetDestination() .

Include: object.h

ObjDuplicateResource()

MemHandle ObjDuplicateResource(
        MemHandle		blockToDup,					/* handle of resource; must
                                 * not be loaded */
        GeodeHandle		owner,					/* owner of duplicate */
        ThreadHandle		burdenThread);					/* burden thread of duplicate */

This routine duplicates an entire object resource block. The new block will be put on the "saved blocks" list so it gets saved to the geode's state file. Usually this is used by the UI to make editable copies of an application's UI resources to ensure the proper state information gets saved. This routine takes three parameters:

blockToDup
The handle of the block to be duplicated. The block must not be resident in memory when ObjDuplicateResource() is called. Also, it can only be a "template" resource--a resource that does not get used by the UI or the application directly, but only gets copied via this routine.
owner
The owner geode of the new block. This should be the geode handle of the owning geode or zero to have the calling geode own it. If you pass an owner of -1, the new block will be owned by the same geode that owns the original.
burdenThread
The thread that will run the block and handle its messages. This should be a thread handle or zero to have the calling thread run the block. Passing a burdenThread of -1 makes the new resource have the same burden thread as the original.

ObjDuplicateResource() returns the handle of the newly created block, which will be unlocked and may or may not be resident in memory.

Include: object.h

See Also: ObjFreeDuplicate(), MSG_META_BLOCK_FREE, ObjLockObjBlock().

ObjEnableDetach()

void	ObjEnableDetach(
        optr	obj);		/* object calling this routine */

This routine acts as an object's handler for MSG_META_ACK . This handler decrements the acknowledgment count (incremented with ObjIncDetach() ) and, if the count is zero, enables the detach mechanism so the object can be fully detached. Because the detach mechanism is implemented in MetaClass , it is highly unlikely you will ever call this routine.

The lone parameter of this routine is the optr of the calling object (or, in the case of MSG_META_ACK , the object sending acknowledgment).

Warnings: This routine may resize and/or move chunks and object blocks, thereby invalidating all pointers and segment addresses.

Include: metaC.goh

See Also: MSG_META_DETACH, ObjInitDetach(), ObjIncDetach(), MSG_META_ACK.

| Up: GEOS SDK TechDocs | Up | Prev: ObjDeref() ... | Next: ObjFreeChunk() ...


GEOS C Routines: ObjFreeChunk() ...

| Up: GEOS SDK TechDocs | Up | Prev: ObjDoRelocation() ... | Next: ObjInitDetach() ...

ObjFreeChunk()

void	ObjFreeChunk(
        optr	o);		/* optr of chunk to be freed */

This routine frees the passed object's instance chunk. If the object came from a loaded resource, however, the object is resized to zero and marked dirty rather than actually freed.

Warnings: The object must be fully detached, and its message queues must be empty before it can safely be freed. All this is handled by MSG_META_DETACH and MSG_META_OBJ_FREE .

Include: object.h

See Also: MSG_META_DETACH, MSG_META_OBJ_FREE, ObjInstantiate().

ObjFreeChunkHandles()

void	ObjFreeChunkHandles(
        MemHandle		mh,			/* handle portion of optr */
        ChunkHandle		ch);			/* chunk portion of optr */

This routine is the same as ObjFreeChunk(); the chunk is specified by its handles rather than by an optr.

Include: object.h

ObjFreeDuplicate()

void	ObjFreeDuplicate(
        MemHandle mh);			/* handle of duplicate block to be freed */

This routine frees a block that had been saved with ObjSaveBlock() or created with ObjDuplicateResource() . It must be passed the memory handle of the duplicated resource.

Warnings: All objects in the duplicated resource must be properly detached to ensure that nothing tries to send messages to the objects in the block. Additionally, the block's in-use count and interactable count should be zero.

Include: object.h

See Also: ObjDuplicateResource(), ObjSaveBlock(), ObjLMemBlockHeader.

ObjFreeMessage()

void	ObjFreeMessage(
        EventHandle event);				/* event to be freed */

This routine frees an event handle and its associated event. This is rarely, if ever, used by anything other than the kernel. The kernel uses this routine to free events after they have been handled.

Include: object.h

ObjFreeObjBlock()

void	ObjFreeObjBlock(
        MemHandle block);			/* handle of the object block to be freed */

This routine frees the specified object block. It first checks the block's in-use count to see if any external references to the block are being kept. If the in-use count is nonzero, ObjFreeObjBlock() simply sets the block's auto-free bit and returns; the block will be freed the first time the in-use count reaches zero. If the in-use count is zero (no external references), the block will be freed immediately.

If the object block passed is not run by the calling thread, the operation will be handled by a remote call in the object block's thread.

Include: object.h

See Also: ObjFreeDuplicate(), MSG_META_BLOCK_FREE.

ObjGetFlags()

ObjChunkFlags ObjGetFlags(
        optr	o);		/* optr of subject object */

This routine returns the object flags associated with a given object. The object is specified by the passed optr, and the flags are stored in the object's ObjChunkFlags record.

Include: object.h

See Also: ObjSetFlags(), ObjChunkFlags.

ObjGetFlagsHandles()

ObjChunkFlags ObjGetFlagsHandles(
        Memhandle		mh,			/* handle portion of optr */
        ChunkHandle		ch);			/* chunk portion of optr */

This routine is the same as ObjGetFlags() , but the object is specified with its handles rather than with its optr.

Include: object.h

ObjGetMessageInfo()

Message	ObjGetMessageInfo(
        EventHandle		event,				/* event to be queried */
        optr	 	* dest);				/* buffer for destination optr */

This routine gets information about the specified event . The return value is the message number of the event. The dest parameter is a pointer to an optr. This routine will return with the optr represinting the event's destination object.

Include: object.h

ObjIncDetach()

void	ObjIncDetach(
        optr	obj);		/* optr of calling object */

This routine increments the number of detach acknowledgments an object must receive before it can safely be detached. Each time the detaching object sends notification of its detachment, it must call ObjIncDetach() to indicate that it must receive a corresponding detach acknowledgment ( MSG_META_ACK ).

The calling object must have previously called ObjInitDetach() . Since the detach mechanism is implemented in MetaClass , it is highly unlikely you will ever need to call this routine. ObjIncDetach() takes a single parameter: the optr of the calling object.

Include: metaC.goh

See Also: MSG_META_DETACH, ObjInitDetach(), ObjEnableDetach(), MSG_META_ACK.

ObjIncInteractibleCount()

void	ObjIncInteractibleCount(
        MemHandle mh);			/* handle of object block */

This routine increments the interactable count of the given object block. The interactable count maintains the number of objects currently visible to the user or about to be acted on by the user (e.g. via keyboard accelerator). The interactable count is maintained by the UI; only in extremely special cases may you need to increment or decrement the count. To decrement the count, use ObjDecInteractibleCount() .

Visible objects increment the interactable count in their MSG_VIS_OPEN handlers and decrement it in their MSG_VIS_CLOSE handlers. This is built into VisClass .

Include: object.h

See Also: ObjDecInteractibleCount(), MSG_VIS_OPEN, MSG_VIS_CLOSE, ObjLMemBlockHeader.

ObjIncInUseCount()

void	ObjIncInUseCount(
        MemHandle mh);			/* handle of object block */

This routine increments the given object block's in-use count. The in-use count maintains the number of outside references to this object block which are stored elsewhere and which need to be removed before the block can safely be freed. If you store an optr to an object block, you should increment the in-use count of the block.

When the reference to the block is removed, the in-use count should be decremented with ObjDecInUseCount() .

Include: object.h

See Also: ObjDecInUseCount(), ObjIncInteractibleCount(), ObjLMemBlockHeader.

| Up: GEOS SDK TechDocs | Up | Prev: ObjDoRelocation() ... | Next: ObjInitDetach() ...


GEOS C Routines: ObjInitDetach() ...

| Up: GEOS SDK TechDocs | Up | Prev: ObjFreeChunk() ... | Next: ObjIsClassADescendant() ...

ObjInitDetach()

void	ObjInitDetach(
        MetaMessages 		msg,
        optr		obj			/* object being detached */
        word		callerID,			/* an identifier token for the caller */
        optr		ackOD);			/* object to which ack is sent */

Initialize the detach sequence for the specified object. The detach sequence severs all ties between the system and the object, allowing it to be destroyed without other objects or geodes trying to contact it. It is highly unlikely you will ever call this routine; typically, you will instead use MSG_META_DETACH or one of the generic or visible object messages, which will call this routine. The parameters for this routine are

msg
The detach message.
obj
The optr of the object to be detached.
callerID
The caller object's ID.
ackOD
The optr of the caller object or another object which is to receive acknowledgment notification of the detach.

Include: metaC.goh

See Also: MSG_META_DETACH, MSG_GEN_DESTROY, MSG_VIS_REMOVE, ObjIncDetach(), ObjEnableDetach(), MSG_META_ACK.

ObjInitializeMaster()

void	ObjInitializeMaster(
        optr 		obj,				/* object to be initialized */
        ClassStruct		* class);				/* class in master group */

This routine initializes the appropriate master part of the passed object, resizing the instance chunk if necessary. It takes two parameters:

obj
The optr of the object whose master part is to be initialized.
class
A pointer to the class definition of a class in the appropriate master group. This does not have to be the master class; it must only be a class in the master goup.

Warnings: This routine may resize and/or move chunks or object blocks, thereby invalidating pointers and segment addresses.

Include: object.h

See Also: ObjResizeMaster(), ObjInitializePart(), ClassStruct.

ObjInitializeMasterHandles()

void	ObjInitializeMasterHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        ClassStruct		* class);				/* class in master group */

This routine is the same as ObjInitializeMaster() except it specifies the object via its handles rather than its optr.

Include: object.h

ObjInitializePart()

void	ObjInitializePart(
        optr 	obj,			/* object to have a part initialized */
        word	masterOffset);			/* offset to master offset in chunk */

This routine initializes all master parts of the given object down to and including the master part specified in masterOffset . It will resize the chunk if necessary and even resolve variant classes above the master group specified, if necessary. This routine takes two parameters:

obj
The optr of the object to be initialized.
masterOffset
The offset within the parent's instance chunk to the master group's offset (the value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure).

Warnings: This routine may move and/or resize chunks or object blocks, thereby invalidating pointers and segment addresses.

Include: object.h

See Also: ObjResizeMaster(), ObjInitializeMaster(), MSG_META_RESOLVE_VARIANT_SUPERCLASS.

ObjInitializePartHandles()

void	ObjInitializePartHandles(
        Memhandle		mh,					/* handle portion of optr */
        ChunkHandle		ch,					/* chunk portion of optr */
        word		masterOffset);					/* master group offset */

This routine is the same as ObjInitializePart() except that it specifies the object via its handles rather than an optr.

Include: object.h

ObjInstantiate()

optr	ObjInstantiate(
        MemHandle		block,				/* block in which new object
                         * will be instantiated */
        ClassStruct		* class);				/* class of new object */

This routine instantiates a new object, allocating the proper size instance chunk. It returns the optr of the new object; this optr can then be used to send setup messages or other messages (such as adding the object to an object tree, setting it usable, etc.).

The new object's instance data will be initialized to all zeroes if it has no master parts (is a direct descendant of MetaClass ). If it is a member of some master group, only enough space for the base structure (the master offsets and the class pointer) will be allocated. In either case, initialization of the instance data will occur at a later time.

ObjInstantiate() takes two parameters:

block
The memory handle of an object block in which the object's instance chunk will be allocated. This block must be an object block, though it need not be run by the caller's thread. If the block is run by another thread, the routine will be executed as a remote call.
class
A pointer to the ClassStruct structure of the class of the new object. This pointer will be set in the object's class pointer (the first four bytes of the instance chunk).

Warnings: This routine, because it allocates a new chunk, may cause LMem and Object blocks to move or resize, thereby invalidating any pointers and segment addresses. Be sure to dereference pointers after calls to this routine.

Include: object.h

See Also: ObjInstantiateForThread().

ObjInstantiateForThread()

optr	ObjInstantiateForThread(
        ThreadHandle		thread,				/* thread to run new object */
        ClassStruct		*class);				/* class of new object */

This routine is similar to ObjInstantiate() except you pass the thread to run the new object rather than the memory handle to an object block (to store the new object). To specify the current thread, pass "0" in thread. This routine automatically allocates memory blocks as needed. It returns the optr to the new object.

Include: object.h

See Also: ObjInstantiate().

| Up: GEOS SDK TechDocs | Up | Prev: ObjFreeChunk() ... | Next: ObjIsClassADescendant() ...


GEOS C Routines: ObjIsClassADescendant() ...

| Up: GEOS SDK TechDocs | Up | Prev: ObjInitDetach() ... | Next: ObjResizeMaster() ...

ObjIsClassADescendant()

Boolean	ObjIsClassADescendant(
        ClassStruct		* class1,				/* proposed ancestor */
        ClassStruct		* class2);				/* proposed descendant */

This routine checks if class2 is a descendand of class1 and returns true if it is.

Include: object.h

ObjIsObjectInClass()

Boolean	ObjIsObjectInClass(
        optr		obj,				/* object to check */
        ClassStruct	* class);			/* proposed class */

This routine checks to see if the passed object is a member of the specified class. It checks superclasses as well, but if an unresolved variant class is encountered, the variant will not be resolved. If you want to search past variant class links, you should send MSG_META_DUMMY to the object first. The two parameters for this routine are

obj
The optr of the object to be checked. Its object block must be locked down before this routine is called. (If the object's block might not be locked down, perhaps you should send MSG_META_IS_OBJECT_IN_CLASS to the object instead of calling this routine.)
class
A pointer to the subject class' ClassStruct definition.

ObjIsObjectInClass() returns true if the object is in the class, false (zero) if it is not.

Include: object.h

ObjIsObjectInClassHandles()

Boolean	ObjIsObjectInClassHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        ClassStruct		* class);				/* proposed class */

This routine is just like ObjIsObjectInClass() except the object is specified via its handles rather than its optr.

Include: object.h

ObjLinkFindParent()

optr	ObjLinkFindParent(
        optr	obj,		/* child's optr */
        word	masterOffset,		/* offset to master part with link field */
        word	linkOffset);		/* offset in master part to link field */

This routine returns the optr of the specified object's parent. It must be passed the following:

obj
The optr of the object whose parent is to be found.
masterOffset
The offset within the object's instance chunk to its master group's offset (the value that would appear in the Class_masterOffset field in its ClassStruct structure).
linkOffset
The offset within the object's instance chunk to the link field.

Include: metaC.goh

See Also: MSG_VIS_FIND_PARENT, MSG_GEN_FIND_PARENT.

ObjLockObjBlock()

void	* ObjLockObjBlock(
        MemHandle mh);			/* handle of object block */

This routine locks an object block, loading in the block if necessary. It must be passed the handle of the block, and it returns the locked block's segment address. When the caller is done using the block, it should unlock it with MemUnlock() .

Be Sure To: Always unlock the block when you are done with it, with MemUnlock() .

Include: object.h

See Also: MemLock(), MemUnlock().

ObjMapSavedToState()

VMBlockHandle ObjMapSavedToState(
        MemHandle mh);			/* handle of object block */

This routine returns the VM block handle of the state file block corresponding to the passed object block. If the specified object block has no state file equivalent, a null handle is returned.

Include: object.h

ObjMapStateToSaved()

MemHandle ObjMapStateToSaved(
        VMBlockHandle		vmbh,			/* VM block handle of state block */
        GeodeHandle		gh);			/* handle of geode owning block */

This routine takes a VM block handle and a geode handle and returns the memory block corresponding to the VM block, if any. The two parameters are

vmbh
The VM block handle of the VM block to be mapped.
gh
The geode handle of the owner of the block, or zero to use the calling geode's handle.

If the block is found, ObjMapStateToSaved() returns its handle. If the block is not found, it returns a null handle.

Include: object.h

ObjMarkDirty()

void	ObjMarkDirty(
        optr 	o);	/* object to be marked dirty */

This routine marks an object dirty, indicating that changes to it should be saved when its object block is saved. If you want changes to objects saved, you should mark the object dirty.

Tips and Tricks: Often you do not need this routine because parameters or flags to other routines will set the object dirty automatically. If there is any doubt, however, you should use this routine.

Include: object.h

See Also: ObjChunkFlags, ObjSetFlags().

ObjMarkDirtyHandles()

void	ObjMarkDirtyHandles(
        MemHandle		mh,			/* handle portion of optr */
        ChunkHandle		ch);			/* chunk portion of optr */

This routine is the same as ObjMarkDirty() except that it specifies the object via handles rather than an optr.

ObjProcBroadcastMessage()

void	ObjProcBroadcastMessage(
        EventHandle event);			/* the event to be broadcast */

This routine broadcasts the given event to all threads which have message queues. It must be passed an encapsulated event (usually recorded with @record ) and returns nothing. This is typically used for notification purposes.

Include: metaC.goh

ObjRelocateEntryPoint()

void *	ObjRelocateEntryPoint(
        EntryPointRelocation *			relocData);

ObjRelocOrUnRelocSuper()

void	ObjRelocOrUnRelocSuper(
        optr 		oself,
        ClassStruct		*class,
        word		frame);

Call this routine to relocate an object's superclass.

Include: object.h

| Up: GEOS SDK TechDocs | Up | Prev: ObjInitDetach() ... | Next: ObjResizeMaster() ...


GEOS C Routines: ObjResizeMaster() ...

| Up: GEOS SDK TechDocs | Up | Prev: ObjIsClassADescendant() ... | Next: ObjVarAddData() ...

ObjResizeMaster()

void	ObjResizeMaster(
        optr	obj,		/* object to have its master part resized */
        word	masterOffset,		/* master offset of proper master part */
        word	newSize);		/* new size for the master part */

This routine resizes a master part of an object's instance chunk. It is typically used to allocate space for a master part or to resize the master part to zero (as when the Vis part of a visible object is removed in MSG_VIS_CLOSE ). This routine must be passed the following three parameters:

obj
The optr of the object whose master part is to be resized.
masterOffset
The offset into the object's instance chunk where the offset to the master part is kept (this is the same offset held in the master class' Class_masterOffset field).
newSize
The new size of the master part. This can be found in the master class' Class_instanceSize field.

Warnings: This routine may resize and/or move chunks or object blocks, thereby invalidating stored segment addresses and pointers.

Include: object.h

See Also: ClassStruct, ObjInitializeMaster(), ObjInitializePart().

ObjResizeMasterHandles()

void	ObjResizeMasterHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        word		masterOffset,				/* offset to master part */
        word		newSize);				/* new size of master part */

This routine is the same as ObjResizeMaster() except that the object is specified with its handles rather than its optr.

Include: object.h

ObjSaveBlock()

void	ObjSaveBlock(
        MemHandle mh);			/*handle of block to be marked for saving */

This routine sets up an object or LMem block to be saved to its owner's state file. The block's handle must be passed in mh . The block must be an object block.

Include: object.h

See Also: ObjMapSavedToState(), ObjMapStateToSaved().

ObjSetFlags()

void	ObjSetFlags(
        optr		o,				/* object whose flags will be set */
        ObjChunkFlags 		bitsToSet,				/* flags to set */
        ObjChunkFlags		bitsToClear);				/* flags to clear */

This routine sets the chunk flags for the specified object. Flags that should be set are passed in bitsToSet , and flags that should be cleared are passed in bitsToClear . Typically, applications will not use this routine but will rather let the kernel maintain the object's flags.

Include: object.h

See Also: ObjGetFlags(), ObjChunkFlags.

ObjSetFlagsHandles()

void	ObjSetFlagsHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        ObjChunkFlags		bitsToSet,				/* flags to set */
        ObjChunkFlags		bitsToClear);				/* flags to clear */

This routine is the same as ObjSetFlags() except that the object is specified via its handles rather than its optr.

Include: object.h

ObjTestIfObjBlockRunByCurThread()

Boolean	ObjTestIfObjBlockRunByCurThread(
        MemHandle mh);			/* handle of object block */

This routine checks if the calling thread is running the specified object block. This routine can be used to determine if calls to objects in the block are across threads or internal to the calling thread. Pass this routine the handle of the object block to be checked--if the object block is a VM block, the thread for the VM file is checked rather than that for the block.

If the block is run by the calling thread, the return value is true . If a different thread runs the block, the return is false (zero).

Include: object.h

ObjUnrelocateEntryPoint()

void	ObjUnrelocateEntryPoint(
        EntryPointRelocation *			relocData,
        void *			entryPoint);
| Up: GEOS SDK TechDocs | Up | Prev: ObjIsClassADescendant() ... | Next: ObjVarAddData() ...


GEOS C Routines: ObjVarAddData() ...

| Up: GEOS SDK TechDocs | Up | Prev: ObjResizeMaster() ... | Next: ObjVarDeleteDataAt() ...

ObjVarAddData()

void	* ObjVarAddData(
        optr		obj,				/* object to add vardata to */
        VardataKey		dataType,				/* vardata type */
        word		dataSize);				/* vardata data size, if any */

This routine adds or alters a variable data entry for the specified object. If the data type does not currently exist in the instance chunk, it will be allocated and added to the chunk. If it does exist, the extra data of the entry will be re-initialized to all zeroes.

This routine returns a pointer to the extra data of the new or modified entry; if the entry has no extra data, an opaque pointer to the entry is passed, and you can use this pointer with ObjVarDeleteDataAt() . In either case, the object will be marked dirty.

If it is possible that you are adding the object's first/only field of variable data, set the object's OCF_VARDATA_RELOC flag by means of the ObjSetFlags() routine or MSG_META_SET_FLAGS.

The parameters of this routine are

obj
The optr of the object affected. This should be the caller's optr.
dataType
The VardataKey word declaring the data type and its flags. The VDF_SAVE_TO_STATE flag must be properly set; the VDF_EXTRA_DATA flag is ignored, however, as the routine will set it properly.
dataSize
The size of the extra data for this type. If the type has no extra data, pass zero.

Include: object.h

Warnings: This routine should be called only by the object whose vardata is affected. To operate on other objects' vardata remotely, use messages provided by MetaClass (see below under "See Also").

See Also: MSG_META_ADD_VAR_DATA, ObjVarDeleteDataAt().

ObjVarAddDataHandles()

void	* ObjVarAddDataHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        VardataKey		dataType,				/* vardata type */
        word		dataSize);				/* vardata data size, if any */

This routine is the same as ObjVarAddData() except that the object is specified via its handles rather than its optr.

Include: object.h

ObjVarCopyDataRange()

void	ObjVarCopyDataRange(
        optr	source,	/* the optr of the source object */
        optr	dest,	/* the optr of the destination (calling) object */
        word	rangeStart,	/* the smallest data type value to be copied */
        word	rangeEnd);	/* the largest data type value to be copied */

This routine copies all the vardata entries within the specified range from the source object to the dest object. The range to be copied is specified by data types and is between rangeStart and rangeEnd , inclusive. If any data entries are copied, the destination object will be marked dirty.

Warnings: This routine should be called only by the destination object; it is against OOP doctrine for one object to alter another's instance data.

Include: object.h

ObjVarDeleteData()

Boolean	ObjVarDeleteData(
        optr		obj,				/* object to delete from */
        VardataKey		dataType);				/* data type to delete */

This routine deletes a vardata entry from the specified object's instance chunk, if the entry exists. The entry is specified by its data type; to delete an entry specified by a pointer to it, use ObjVarDeleteDataAt() , below. It returns an error flag: true if the entry was not found, false if the entry was successfully deleted. The object will also be marked dirty by the routine.

The parameters for this routine are

obj
The optr of the object affected. This should be the caller's optr.
dataType
The VardataKey word declaring the data type and its flags. Both the VDF_SAVE_TO_STATE flag and the VDF_EXTRA_DATA flag are ignored.

Warnings: This routine should be called only by the object whose vardata is affected. To operate on other objects' vardata remotely, use messages provided by MetaClass (see below under "See Also").

Include: object.h

See Also: MSG_META_DELETE_VAR_DATA, ObjVarDeleteDataAt().

ObjVarDeleteDataHandles()

Boolean	ObjVarDeleteDataHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        VardataKey		dataType);				/* data type to delete */

This routine is the same as ObjVarDeleteData() except that the object is specified via its handles rather than its optr.

Include: object.h

| Up: GEOS SDK TechDocs | Up | Prev: ObjResizeMaster() ... | Next: ObjVarDeleteDataAt() ...


GEOS C Routines: ObjVarDeleteDataAt() ...

| Up: GEOS SDK TechDocs | Up | Prev: ObjVarAddData() ... | Next: ObjVarScanData() ...

ObjVarDeleteDataAt()

void	ObjVarDeleteDataAt(
        optr	obj,			/* object to delete from */
        word	extraDataOffset);			/* offset to extra data to delete */

This routine deletes the specified vardata entry from the given object's instance chunk. The vardata entry is specified by its pointer as returned by ObjVarAddData() , ObjVarFindData() , and ObjVarDerefData() . To delete an entry specified by its data type, use ObjVarDeleteData() , above.

Warnings: This routine should be called only by the object whose vardata is affected. To operate on other objects' vardata remotely, use messages provided by MetaClass (see below under "See Also").

Include: object.h

See Also: MSG_META_DELETE_VAR_DATA, ObjVarDeleteData().

ObjVarDeleteDataAtHandles()

void	ObjVarDeleteDataAtHandles(
        MemHandle		mh,			/* handle portion of optr */
        ChunkHandle		ch,			/* chunk portion of optr */
        word	extraDataOffset);				/* offset to extra data to delete */

This routine is the same as ObjVarDeleteDataAt() except that the object is specified via its handles rather than its optr.

Include: object.h

ObjVarDeleteDataRange()

void	ObjVarDeleteDataRange(
        optr 		obj,					/* object to delete from */
        word		rangeStart,					/* start of range */
        word		rangeEnd,					/* end of range */
        Boolean		useStateFlag);					/* save to state flag */

This routine deletes all data entries within a given range for the passed object. The range is specified by beginning and ending data types and is inclusive. The four parameters to this routine are

obj
The optr of the object whose data entries are to be deleted.
rangeStart
The lowest number data type to be deleted. Both the VDF_SAVE_TO_STATE flag and the VDF_EXTRA_DATA flag are ignored.
rangeEnd
The highest number data type to be deleted. Both the VDF_SAVE_TO_STATE flag and the VDF_EXTRA_DATA flag are ignored.
useStateFlag
A flag indicating whether entries with their VDF_SAVE_TO_STATE flags should be deleted. Pass true (nonzero) to take the state flag into account; pass false (zero) to delete all entries in the range.

Warnings: This routine should be called only by the object whose vardata is affected. To operate on other objects' vardata remotely, use messages provided by MetaClass (see below under "See Also").

Include: object.h

See Also: MSG_META_DELETE_VAR_DATA.

ObjVarDeleteDataRangeHandles()

void	ObjVarDeleteDataRangeHandles(
        MemHandle		mh,					/* handle portion of optr */
        ChunkHandle		ch,					/* chunk portion of optr */
        word		rangeStart,					/* start of range */
        word		rangeEnd,					/* end of range */
        Boolean		useStateFlag);					/* save to state flag */

This routine is the same as ObjVarDeleteDataRange() except that the object is specified via its handles rather than its optr.

Include: object.h

ObjVarDerefData()

void	* ObjVarDerefData(
        optr		obj,				/* object having data type */
        VardataKey		dataType);				/* data type to dereference */

This routine is exactly like ObjVarFindData() , below, except that it does not return a null pointer if the data type is not found. Do not use this routine unless you are absolutely sure the data type is in the object; otherwise, results are unpredictable.

Include: object.h

See Also: ObjVarFindData().

ObjVarDerefDataHandles()

void	* ObjVarDerefDataHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        VardataKey		dataType);				/* data type to dereference */

This routine is the same as ObjVarDerefData() except that the object is specified via its handles rather than its optr.

Include: object.h

ObjVarFindData()

void	* ObjVarFindData(
        optr		obj,				/* object to be checked */
        VardataKey		dataType);				/* data type to find */

This routine searches an object's variable data for a given data type. If the type is found, ObjVarFindData() returns a pointer to the entry's extra data; if the entry has no extra data, an opaque pointer is returned which may be used with ObjVarDeleteDataAt() . If the entry is not found, a null pointer is returned. The pointer returned by this routine must be used before any subsequent operations on the object's block; the pointer may be invalidated by other LMem or object operations.

The two parameters of this routine are

obj
The optr of the object affected. This should be the caller's optr.
dataType
The VardataKey word declaring the data type and its flags. Both the VDF_SAVE_TO_STATE flag and the VDF_EXTRA_DATA flag are ignored.

Warnings: This routine should be called only by the object whose vardata is affected. To operate on other objects' vardata remotely, use messages provided by MetaClass (see below under "See Also").

Include: object.h

See Also: MSG_META_GET_VAR_DATA.

ObjVarFindDataHandles()

void	* ObjVarFindDataHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        VardataKey		dataType);				/* data type to find */

This routine is the same as ObjVarFindData() except that the object is specified via its handles rather than its optr.

Include: object.h

| Up: GEOS SDK TechDocs | Up | Prev: ObjVarAddData() ... | Next: ObjVarScanData() ...


GEOS C Routines: ObjVarScanData() ...

| Up: GEOS SDK TechDocs | Up | Prev: ObjVarDeleteDataAt() ... | Next: ParallelClose() ...

ObjVarScanData()

void	ObjVarScanData(
        optr		obj,					/* object to be scanned */
        word		numHandlers,					/* number of handlers in table */
        VarDataCHandler		* handlerTable,					/* pointer to handler table */
        void		* handlerData);					/* pointer to handler data */

This routine scans an object's vardata and calls all the vardata handlers specified in the passed handler table. Pass it the following parameters:

obj
The optr of the object whose variable data table is to be scanned.
numHandlers
The number of handlers specified in the passed handler table.
handlerTable
A pointer to a list of VarDataCHandler structures. Each of these structures contains a vardata data type and a pointer to the routine that is to handle it. All the handler routines must be in the same segment as the handler table.
handlerData
A pointer to a buffer that is passed on to the handlers. This can contain any information of specific interest to the application or handlers.

Vardata Handler Format:
 A vardata handler routine must have the following format:

void _pascal (MemHandle mh, ChunkHandle chnk,
    VarDataEntry *extraData, word dataType,
    void *handlerData)

The handler should not free the object chunk or destroy the object; it can do anything else it pleases. The handler returns nothing and takes the following parameters:

mh:chnk
The memory handle and chunk handle of the object being referenced. Together, these comprise the optr of the object.
extraData
A pointer to the data type's extra data, if it has any. This pointer may be parsed with the macros VarDataTypePtr() , VarDataFlagsPtr() , and VarDataSizePtr() .
dataType
The data type of the data entry being handled. This is a record of type VardataKey .
handlerData
A pointer to a buffer passed through by ObjVarScanData() . This buffer may be used for passing additional data to the handlers.

Structures: The VarDataCHandler structure contains two elements:

typedef struct {
    word		VDCH_dataType;
    void	_pascal 	(*VDCH_handler) (
            MemHandle		mh,
            ChunkHandle		chnk,
            VarDataEntry		* extraData,
            word		dataType
            void		* handlerData);
} VarDataCHandler;

The first element is the data type, a record containing the data type and the vardata flags. The second element is a far pointer to the handler routine for the type.

Include: object.h

ObjVarScanDataHandles()

void	ObjVarScanDataHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        word		numHandlers,					/* number of handlers in table */
        VarDataCHandler		* handlerTable,					/* pointer to handler table */
        void		* handlerData);					/* pointer to handler data */

This routine is the same as ObjVarScanData() except that the object is specified via its handles rather than its optr.

Include: object.h

offsetof()

word	offsetof(struc, field);

This macro returns the offset of the specified field within the specified structure.

OptrToChunk()

ChunkHandle OptrToChunk(op);
        optr	op;

This macro extracts the chunk handle portion of the given optr.

See Also: ConstructOptr(), OptrToHandle().

OptrToHandle()

MemHandle OptrToHandle(op);
        optr	op;

This macro extracts the MemHandle portion of the given optr.

See Also: ConstructOptr(), OptrToChunk().

| Up: GEOS SDK TechDocs | Up | Prev: ObjVarDeleteDataAt() ... | Next: ParallelClose() ...


GEOS C Routines: ParallelClose() ...

| Up: GEOS SDK TechDocs | Up | Prev: ObjVarScanData() ... | Next: PCCOMABORT() ...

ParallelClose()

StreamError ParallelClose(
        GeodeHandle 		driver,
        ParallelUnit 		unit,
        Boolean 		linger);

Close the stream to a parallel port.

Include: streamC.h

ParallelGetError()

StreamError	 	ParallelGetError (
        Handle 		driver,
        ParallelUnit		unit,
        StreamRoles 		roles,
        ParallelError *		errorCode);

This routine returns any parallel-driver-specific error codes set by the last call to the parallel-driver.

ParallelOpen()

StreamError ParallelOpen(
        GeodeHandle 		driver,
        ParallelUnit 		unit,
        StreamOpenFlags 		flags,
        word		outBuffSize,
        word 		timeout);

This routine opens a stream to the specified parallel port. It is passed the following arguments:

driver
The GeodeToken of the parallel driver.
unit
The parallel port to open.
flags
This specifies whether the call should fail if the port is busy, or wait for a time to see if it will become free.
outBuffSize
The size of the stream buffer used for output to the parallel port.
timeout
The number of clock ticks to wait for the port to become free. (This argument is ignored if flags is not STREAM_OPEN_TIMEOUT.)

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

Include: streamC.h

ParallelSetError()

StreamError ParallelSetError(
        Handle 		driver,
        ParallelUnit 		unit,
        StreamRoles		roles,
        ParallelError *		errorCode);

This routine sets the device-specific error code for the specified parallel port.

ParallelWrite()

StreamError ParallelWrite(
        GeodeHandle 		driver,
        ParallelUnit 		unit,
        StreamBlocker 		blocker,
        word 		buffSize,
        const byte *		buffer,
        word *		numBytesWritten);

Write data to a parallel port.

Include: streamC.h

ParallelWriteByte()

StreamError ParallelWrite(
        GeodeHandle 		driver,
        ParallelUnit 		unit,
        StreamBlocker 		blocker,
        word 		buffSize,
        byte		dataByte);

Write one byte of data to a parallel port.

Include: streamC.h

ParserParseString()

ParserScannerEvaluatorError ParserParseString(
        char		*textBuffer,
        byte		*tokenBuffer,
        CParserStruct		*parserParams,
        CParserReturnStruct		*retVal);

Parse a string into a sequence of tokens.

Include: parse.h

ParserEvalExpression()

ParserScannerEvaluatorError ParserEvalExpression(
        byte		*scratchBuffer,
        byte		*resultBuffer,
        word		bufSize,
        CEvalStruct		*evalParams);

Evaluate a parsed expression.

Include: parse.h

ParserFormatExpression()

ParserScannerEvaluatorError ParserFormatExpression(
        byte		*tokenBuffer,
        char		*textBuffer,
        CFormatStruct		*formatParams);

Format an expression from parsed data.

Include: parse.h

PCB()

#define PCB(return_type, pointer_name, args) \
        return_type _pascal (*pointer_name) args

This macro is useful for declaring pointers to functions that use the pascal calling conventions. For example, to declare a pointer to a function which is passed two strings and returns an integer, one could write

PCB(int, func_ptr, (const char *, const char *));

which would be expanded to

int _pascal (*func_ptr) (const char *, const char *);

See Also: CCB().

| Up: GEOS SDK TechDocs | Up | Prev: ObjVarScanData() ... | Next: PCCOMABORT() ...


GEOS C Routines: PCCOMABORT() ...

| Up: GEOS SDK TechDocs | Up | Prev: ParallelClose() ... | Next: qsort ...

PCCOMABORT()

void PCCOMABORT(void);

This routine aborts the current file transfer operation being carried out by the PCCom library. It is the third entry point in the PCCom library.

Include: pccom.goh

PCCOMEXIT()

PCComReturnType PCCOMEXIT();

This routine kills a pccom thread such as those started by PCCOMINIT(). It is the second entry point in the PCCom library.

Structures:

        typedef ByteEnum PCComReturnType;
        #define PCCRT_NO_ERROR 0
        #define PCCRT_CANNOT_LOAD_SERIAL_DRIVER 1
        #define PCCRT_CANNOT_CREATE_THREAD 2
        #define PCCRT_CANNOT_ALLOC_STREAM 3
        #define PCCRT_ALREADY_INITIALIZED 4

Include: pccom.goh

PCCOMINIT()

PCComReturnType PCCOMINIT(
        SerialPortNum 		port,
        SerialBaud 		baud,
        word 		timeout,
        optr 		callbackOptr,
        PCComInitFlags 		flags);

This entry point of the PCCom library spawns a new thread which monitors a serial port and acts as a passive pccom terminal. This routine is the first entry point in the PCCom library.

This routine takes the following arguments:

port
A SerialPortNum value specifying which serial port to use for the pccom connection. Pass -1 for the system default value: com1 for the Zoomer, com2 for the desktop product.
baud
A SerialBaud value specifying what speed to use. Pass -1 for the system default value: 19200 baud for the Zoomer, 38400 baud for the desktop product.
timeout
Number of clock ticks (one tick is 1/60 second) to allow for connection.
callbackOptr
An object which will receive notification messages of certain events. A value of zero means no notification will be sent.
flags
If an object will be receiving notification messages, these flags determine what sort of notifications will be sent.

Structures:

        typedef ByteEnum PCComReturnType;
        #define PCCRT_NO_ERROR 0
        #define PCCRT_CANNOT_LOAD_SERIAL_DRIVER 1
        #define PCCRT_CANNOT_CREATE_THREAD 2
        #define PCCRT_CANNOT_ALLOC_STREAM 3
        #define PCCRT_ALREADY_INITIALIZED 4

        typedef WordFlags PCComInitFlags;
            /* send notifications when text is available for display */
        #define PCCIF_NOTIFY_OUTPUT 0x8000
            /* send notification when the remote machine shuts down the
 * serial line */
        #define PCCIF_NOTIFY_EXIT 0x4000

Include: pccom.goh

ProcCallFixedOrMovable_cdecl()

dword	ProcCallFixedOrMovable_cdecl(
        void 	(*routine),
        ...)

This routine calls the routine pointed to, passing the other arguments through to the called routine. The called routine must use C calling conventions.

Include: resource.h

ProcCallFixedOrMovable_pascal()

dword	ProcCallFixedOrMovable_pascal(
        ...,
        void 	(*routine))

This routine calls the routine pointed to, passing the other arguments through to the called routine. The called routine must use Pascal calling conventions.

Include: resource.h

ProcGetLibraryEntry()

void *	ProcGetLibraryEntry(
        GeodeHandle		library,
        word		entryNumber)

This routine returns the pointer to a library's entry-point.

Include: resource.h

ProcInfo()

ThreadHandle ProcInfo(
        GeodeHandle gh);		/* handle of geode to check */

This routine returns the first thread of the process geode specified. If the geode is not a process, the routine will return a null handle.

Include: geode.h

PtrToOffset()

word	PtrToOffset(ptr);
        dword	ptr;

This macro returns just the lower 16 bits of the given dword. It is most useful for extracting the offset portion of a far pointer.

PtrToSegment()

word	PtrToSegment(ptr);
        dword	ptr;

This macro returns just the upper 16 bits of the given dword. It is most useful for extracting the segment address of a far pointer.

PutUpDialogViaUIThread()

optr PutUpDialogViaUIThread(
    optr	dialogBoxTemplate );

This routine displays a dialog using a template that you supply, and runs it on the system's UI thread. It sends MSG_GEN_INTERACTION_INITIATE to bring the dialog on screen. See the SDK_9000/Dialogs sample application for an example of how to use this routine.

Use the routine TakeDownDialogViaUIThread() to remove the duplicate dialog from the screen and from memory.

Pass:

dialogBoxTemplate
Optr of the dialog you want to display. The dialog must be set to GIA_SYS_MODAL and start off as not GS_USABLE.

Return: Optr of the duplicated dialog object. Pass this to TakeDownDialogViaUIThread() when you want to remove the dialog.

Include: foam.goh

See Also: TakeDownDialogViaUIThread(), UserCreateDialog()

| Up: GEOS SDK TechDocs | Up | Prev: ParallelClose() ... | Next: qsort ...


GEOS C Routines: qsort ...

| Up: GEOS SDK TechDocs | Up | Prev: PCCOMABORT() ... | Next: realloc() ...

qsort

extern void _pascal qsort(
        void *array,
        word count,
        word elementSize,
        PCB(int, compare, (const void *, const void *)));

This is a standard quicksort routine. The callback routine must be decared _pascal.

QueueGetMessage()

EventHandle QueueGetMessage(
        QueueHandle qh);			/* queue to query */

This routine returns the next message on the given queue, blocking if the queue is empty. When a new message is added to the empty queue, this routine will unblock the thread and return the message. This routine is used almost exclusively by the kernel.

Include: geode.h

QueuePostMessage()

void	QueuePostMessage(
        QueueHandle		qh,			/* queue to add event to */
        EventHandle		event,			/* event to be added to queue */
        MessageFlags		flags);			/* MF_INSERT_AT_FRONT or zero */

This routine adds the specified event to the passed queue . The only valid flag for this routine is MF_INSERT_AT_FRONT, which will put the event in the first spot of the queue.

Include: geode.h

RangeEnum()

Boolean	RangeEnum(
        CellFunctionParameters			* cfp,				/* cell function parameters */
        RangeEnumParams			* params);				/* special other parameters */

This routine calls a callback routine for each cell in a specified range. This routine is passed pointers to two structures, both of which are shown below. It returns false if all the cells were processed, true if any of the cells caused the routine to abort before the end of the range was reached.

Callback Parameters:

The callback routine, which must be declared _pascal, receives a RangeEnumCallbackParams structure, which has the following definition:

typedef struct {
    RangeEnumParams			*RECP_params;		/* see below */
   /* current row, column, and cell data of cell */
    word			RECP_row;
    word			RECP_column;
    word			RECP_cellData;
} RangeEnumCallbackParams;

The callback routine can do anything with the cell information. It should return false after successfully processing the cell; if an error occurs, or if it wants to abort the RangeEnum() , it should return true .

Include: cell.h

RangeExists()

Boolean	RangeExists( /* returns non-zero if there are cells in range */
        CellFunctionParameters			* cfp,				/* see RangeEnum() */
        word			firstRow,				/* range delimiters */
        byte			firstColumn,
        word			lastRow,
        byte			lastColumn);

This routine returns true if there are any cells in the specified range. It is passed a pointer to the CellFunctionParameters structure for the cell file, as well as the indices of the first and last row, and the first and last column, of the range to check.

Include: cell.h

RangeInsert()

void	RangeInsert(
        CellFunctionParameters			* cfp,				/* see RangeEnum() */
        RangeInsertParams			* rep);				/* parameters structure */

This routine shifts existing cells to make room for new ones. (It does not actually create new cells.) Which cells are shifted, and in what direction, is specified by the RangeInsertParams structure:

RIP_bounds
A Rectangle structure which specifies which cells should be shifted. The cells currently in this range will be shifted across or down, depending on the value of RIP_delta ; the shifted cells displace more cells, and so on, to the edge of the visible portion of the cell file. To insert an entire row (which is much faster than inserting a partial row), set RIP _bounds. R _left = 0 and RIP _bounds. R _right = LARGEST_COLUMN .
RIP_delta
A Point structure which specifies how far the cells should be shifted and in which direction. If the range of cells is to be shifted horizontally, RIP_delta. P _x should specify how far the cells should be shifted over, and RIP_delta. P _y should be zero. If the cells are to be shifted vertically, RIP_delta. P _y should specify how far the cells should be shifted over, and RIP_delta. P _x should be zero.
RIP_cfp
This is the address of the CellFunctionParameters structure. You don't have to initialize this; the routine will do so automatically.

Include: cell.h

Warnings: If cells are shifted off the "visible" portion of the cell file, you will be unable to access them by row or column numbers; but they will not be deleted. For this reason, you should free all such cells before calling RangeInsert() . (You can find out if there are any cells at the edges by calling RangeExists() .) For an explanation of the "visible" and "scratch-pad" portions of a cell file, see the Cell library documentation.

| Up: GEOS SDK TechDocs | Up | Prev: PCCOMABORT() ... | Next: realloc() ...


GEOS C Routines: realloc() ...

| Up: GEOS SDK TechDocs | Up | Prev: qsort ... | Next: SerialGetFormat() ...

realloc()

void *	realloc(
        void *		blockPtr,				/* address of memory to resize */
        size_t		newSize);				/* New size of memory in bytes */

The malloc() family of routines is provided for Standard C compatibility. If a geode needs a small amount of fixed memory, it can call one of the routines. The kernel will allocate a fixed block to satisfy the geode's malloc() requests; it will allocate memory from this block. When the block is filled, it will allocate another fixed malloc-block. When all the memory in the block is freed, the memory manager will automatically free the block.

If a geode needs to change the size of a section of memory assigned to it by the malloc() family of routines, it should use realloc() . realloc() resizes the piece of memory specified and returns the memory's new base address.

If the new size is smaller then the previous size, bytes will be cut off from the end. The request is guaranteed to succeed. Furthermore, the memory will not be moved; the address returned will be the same as the address passed.

If the new size is larger than the previous size, realloc() may move the data to accommodate the request. If so, it will return the new address. The new memory added will not be zero-initialized. If realloc() cannot fulfill the request, it will return a null pointer, and the memory will not be altered.

Resizing a stretch of memory down to zero bytes is exactly the same as freeing it with free() . If you pass a null address to realloc() , it will allocate the memory the same way malloc() does.

The memory must be in a malloc-block assigned to the geode calling realloc() . If you want to resize memory in another geode's malloc-block, call GeoReAlloc() .

Warnings: Pass exactly the same address as the one returned to you when you allocated the memory. If you pass a different address, the results are undefined.

See Also: calloc(),
free(),
malloc(),
GeoReAlloc().

RecentContactsEraseStack()

BooleanRecentContactsEraseStack(
        RecentContactsType 			rcType);

This function erases all log entries of a certain type from the log. It returns zero if there was no error, non-zero otherwise. Pass one of RC_SENT_CALLS, RC_RECEIVED_CALLS, RC_MISSED_CALLS, RC_SENT_FAXES, RC_RECEIVED_FAXES, RC_SENT_SMS, or RC_RECEIVED_SMS.

Include: contlog.goh

RecentContactsGetCount()

word 	RecentContactsGetCount(
        RecentContactsType 			rcType);

This function returns the number of contacts of some type that have been made recently. Pass one of RC_SENT_CALLS, RC_RECEIVED_CALLS, RC_MISSED_CALLS, RC_SENT_FAXES, RC_RECEIVED_FAXES, RC_SENT_SMS, or RC_RECEIVED_SMS.

Include: contlog.goh

RecentContactsGetEntry()

word 	RecentContactsGetEntry(
        RecentContactsType 			rcType,
        int 			item,
        LogEntry 			*buffer);

This function retrieves the entry log for a call. The call is identified by its call type and an index number. The call type is one of RC_SENT_CALLS, RC_RECEIVED_CALLS, RC_MISSED_CALLS, RC_SENT_FAXES, RC_RECEIVED_FAXES, RC_SENT_SMS, or RC_RECEIVED_SMS. The index number should be at least zero and less than the number returned by RecentContactsGetCount() . The function fills in the passed LogEntry buffer with the entry's information. If there was an error, the function returns non-zero; otherwise, it returns zero.

Include: contlog.goh

RecentContactsGetMissedCallsTotalCount()

word	RecentContactsResetMissedCallsTotalCount();

This function returns the number of calls missed since the last time RecentContactsGetMissedCallsTotalCount() was called.

Include: contlog.goh

RecentContactsGetNewCount()

word RecentContactsGetNewCount(
        RecentContactsType 			rcType);

This function returns the number of contacts logged since the last time RecentContactsResetNewCount() was called.

Include: contlog.goh

RecentContactsResetMissedCallsTotalCount()

voidRecentContactsResetMissedCallTotalsCount( );

This function resets the number of missed calls as used by the RecentContactsGetMissedCallsTotalCount() function.

Include: contlog.goh

RecentContactsResetNewCount()

voidRecentContactsResetNewCount(
        RecentContactsType 			rcType);

This function resets the number of "new" contacts logged as used by the RecentContactsGetNewCount() function.

Include: contlog.goh

RespGetPDAHardwareInfo()

PDAHardwareInfo    RespGetPDAHardwareInfo(void);

This routine gets information about the current PDA hardware, specifically the type of PDA and whether or not it has a backlight. Use the following bit masks to extract the desired information from the PDAHardwareInfo bitfield:

typedef ByteFlags PDAHardwareInfo;
/* 5 bits unused */
#define PDAHI_BACKLIGHT_AVAILABLE       (0x04)
#define PDAHI_PDA_TYPE                  (0x02 | 0x01)    /* PDAType */

The PDAType is enumerated as:

typedef ByteEnum PDAType;
#define PDAT_PC_EMULATOR        0x0
#define PDAT_N9000              0x1
#define PDAT_N9000i             0x2

This routine is only available on builds 4 or newer of the Nokia 9000i Communicator (see Versions for more information).

Include: respondr.goh

SerialClose()

StreamError SerialClose(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        Boolean 		linger);

Close the stream to a serial port.

SerialCloseWithoutReset()

StreamError SerialClose(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        Boolean 		linger);

Close the stream to a serial port, without actually resetting the port.

SerialFlush()

StreamError SerialFlush(
        GeodeHandle 		driver,
        SerialUnit		unit,
        StreamRoles 		roles);

Flush all data pending in a serial port's input or output buffer (depending on the value of roles ).

SerialGetError()

StreamError	 	SerialGetError (
        Handle 		driver,
        SerialUnit		unit,
        StreamRoles 		roles,
        SerialError *		errorCode);

This routine returns error information pertaining to the last call to a serial driver. The SerialError codes returned may contain serial-driver-specific error codes not defined in the SerialError enumerated type.

| Up: GEOS SDK TechDocs | Up | Prev: qsort ... | Next: SerialGetFormat() ...


GEOS C Routines: SerialGetFormat() ...

| Up: GEOS SDK TechDocs | Up | Prev: realloc() ... | Next: SGC_MACHINE ...

SerialGetFormat()

StreamError SerialGetFormat(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        SerialFormat *		format,
        SerialMode *		mode,
        SerialBaud *		baud);

Get the format of a stream to a specified serial port.

SerialGetModem()

StreamError SerialGetModem(
        GeodeHandle 		driver,
        SerialUnit		unit,
        SerialModem *		modem);

Read a modem's hardware flow control bits.

SerialOpen()

StreamError SerialOpen(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        StreamOpenFlags 		flags,
        word 		inBuffSize,
        word		outBuffSize,
        word 		timeout);

This routine opens a stream to the specified serial port. It is passed the following arguments:

driver
The GeodeToken of the serial driver.
unit
The serial port to open.
flags
This specifies whether the call should fail if the port is busy, or wait for a time to see if it will become free.
inBuffSize
The size of the stream buffer used for input from the serial port.
outBuffSize
The size of the stream buffer used for output to the serial port.
timeout
The number of clock ticks to wait for the port to become free. (This argument is ignored if flags is not STREAM_OPEN_TIMEOUT.)

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

SerialQuery()

StreamError SerialQuery(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        StreamRoles role,
        word *		bytesAvailable);

Find out how much space is available in a serial buffer, or how much data is waiting to be read.

SerialRead()

StreamError SerialRead (
        Handle		driver,
        SerialUnit 		unit,
        StreamBlocker 		blocker,
        word 		buffSize,
        byte *		buffer,
        word *		numBytesRead);

Read data from a serial port and write it to a passed buffer.

SerialReadByte()

StreamError SerialReadByte (
        Handle		driver,
        SerialUnit 		unit,
        StreamBlocker 		blocker,
        byte		*dataByte);

Read a byte of data from a serial port and write it to a passed variable.

SerialSetError()

StreamError SerialSetError (
        Handle 		driver,
        SerialUnit 		unit,
        StreamRoles 		roles,
        SerialError 		errorCode);

This routine sets the serial-driver-specific error code for recovery with SerialGetError() .

SerialSetFlowControl()

StreamError 	SerialSetFlowControl (
        Handle 		driver,
        SerialUnit 		unit,
        SerialFlowControl 		flow,
        SerialModem 		modem,
        SerialModemStatus 		status);

This routine enables or disables software flow control. The flow argument has two flags, SFC_HARDWARE and SFC_SOFTWARE.

SerialSetFormat()

StreamError SerialSetFormat(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        SerialFormat 		format,
        SerialMode		mode,
        SerialBaud 		baud);

Set the format for a stream to a specified serial port.

SerialSetModem()

StreamError SerialSetModem(
        GeodeHandle 		driver,
        SerialUnit		unit,
        SerialModem 		modem);

Set a modem's hardware flow control bits.

SerialWrite()

StreamError SerialWrite(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        StreamBlocker 		blocker,
        word 		buffSize,
        const byte *		buffer,
        word *		numBytesWritten);

Write data to a serial port.

SerialWriteByte()

StreamError SerialWrite(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        StreamBlocker 		blocker,
        word 		buffSize,
        byte		dataByte);

Write one byte of data to a serial port.

| Up: GEOS SDK TechDocs | Up | Prev: realloc() ... | Next: SGC_MACHINE ...


GEOS C Routines: SGC_MACHINE ...

| Up: GEOS SDK TechDocs | Up | Prev: SerialGetFormat() ... | Next: SocketBind() ...

SGC_MACHINE

byte	SGC_MACHINE(val);
        dword	val;

This macro is used to extract the machine type from a SysGetConfig() return value.

Include: system.goh

SGC_PROCESSOR

byte	SGC_PROCESSOR(val);
        dword	val;

This macro is used to extract the processor type from a SysGetConfig() return value.

Include: system.goh

SocketAccept()

Socket SocketAccept(
        Socket 		s,
        int 		timeout);

To make a socket wait for and accept a connection, call SocketAccept() . The socket must be a listening socket (processed by SocketListen() ). Because the thread will block until a connection is made or SocketAccept() times out, programs will normally not call this routine in their main execution thread. Instead, a separate thread handles accepting a connection and receiving data from the connection.

If there are no connections to accept, SocketAccept() will set the SE_LISTEN_QUEUE_EMPTY error.

This routine takes the following arguments:

s
A listening socket (processed by SocketListen() ).
timeout
Time to wait for success, in 1/60 second ticks, or SOCKET_NO_TIMEOUT to try as long as possible.

It returns a socket which should be used to send and receive data over the connection. If the return value is zero, an error occured. Call ThreadGetError() to find out what the error value was.

See Also: SocketListen(),
SocketConnect(),
ThreadGetError().

Include: socket.goh

SocketAddLoadOnMsg()

SocketError SocketAddLoadOnMsg(
        SocketPort 		p,
        SocketLoadType 		slt,
        word 		disk,
        TCHAR 		*path);

To make the Socket library load a program when receiving packets on a particular port, call SocketAddLoadOnMsg() . The Socket library will write out an entry to the GEOS.INI file in the [socket] category and with key "LoadOnMsg". The Socket library consults these GEOS.INI file entries when it starts up and will act accordingly when it receives packets.

This routine takes the following arguments:

p
The port to monitor.
slt
How to load the application--either by UserLoadApplication() or GeodeLoad() .
disk
DiskHandle of application to load. This may be a StandardPath value.
path
Buffer holding path of application to load.

If successful, the routine will return SE_NORMAL (i.e., zero). Otherwise, it will return some other SocketError value.

See Also: SocketAddLoadOnMsgInDomain(),
SocketRemoveLoadOnMsg(),
UserLoadApplication(),
GeodeLoad().

Include: socket.goh

SocketAddLoadOnMsgInDomain()

SocketError SocketAddLoadOnMsgInDomain(
        SocketPort 		p,
        SocketLoadType 		slt,
        word 		disk,
        TCHAR 		*path,
        TCHAR 		*domain);

To make the Socket library load a program when receiving packets on a particular port within a particular domain, call SocketAddLoadOnMsgInDomain() . The Socket library will write out an entry to the GEOS.INI file in the [socket] category and with key "LoadOnMsg". The Socket library consults these GEOS.INI file entries when it starts up and will act accordingly when it receives packets.

This routine takes the following arguments:

p
The port to monitor.
slt
How to load the application--either by UserLoadApplication() or GeodeLoad() .
disk
DiskHandle of application to load. This may be a StandardPath value.
path
Buffer holding path of application to load.
domain
Buffer holding name of the domain to monitor.

If successful, the routine will return SE_NORMAL (i.e., zero). Otherwise, it will return some other SocketError value.

See Also: SocketAddLoadOnMsg(),
SocketRemoveLoadOnMsgInDomain(),
UserLoadApplication(),
GeodeLoad().

Include: socket.goh

| Up: GEOS SDK TechDocs | Up | Prev: SerialGetFormat() ... | Next: SocketBind() ...


GEOS C Routines: SocketBind() ...

| Up: GEOS SDK TechDocs | Up | Prev: SGC_MACHINE ... | Next: SocketClose() ...

SocketBind()

SocketError SocketBind(
        Socket 		s,
        SocketPort 		p,
        SocketBindFlags 		flags);

Use the SocketBind() routine to associate a socket with a port number on this side of the connection.

For any given domain, normally only one socket on the machine may be bound to any port number. If you have used SocketBind() to bind some other socket to this port, then you may only bind another socket to the port by setting the SBF_REUSE_PORT in the flags argument. If you have used SocketBindInDomain() to bind some other socket to this port, then you may not use SocketBind() to bind another socket to the port.

A given socket may only be bound to one port.

This routine takes the following arguments:

s
The socket to bind.
p
The port to monitor.
flags
Set the SBF_REUSE_PORT to bind the socket to the port even if another socket is bound to it. (You may not use this flag with datagram sockets.)

If successful, the routine will return SE_NORMAL (i.e., zero). Otherwise, it will return some other SocketError value. Be on the lookout for SE_SOCKET_ALREADY_BOUND, SE_PORT_IN_USE, or SE_BIND_CONFLICT.

See Also: SocketBindInDomain().

Include: socket.goh

SocketBindInDomain()

SocketError SocketBindInDomain(
        Socket 		s,
        SocketPort 		p,
        SocketBindFlags 		flags,
        TCHAR 		*domain);

Use the SocketBindInDomain() routine to associate a socket with a port number and domain on this side of the connection.

For any given domain, normally only one socket on the machine may be bound to any port number in any given domain. If you have used SocketBindInDomain() to bind some other socket to this port and domain, then you may only bind another socket to the port and domain by setting the SBF_REUSE_PORT in the flags argument. If you have used SocketBind() to bind some other socket to this port, then you may not use SocketBindInDomain() to bind another socket to the port.

A given socket may only be bound to one port.

This routine takes the following arguments:

s
The socket to bind
p
The port to monitor
flags
Set the SBF_REUSE_PORT to bind the socket to the port even if another socket is bound to it.
domain
The domain to monitor

If successful, the routine will return SE_NORMAL (i.e., zero). Otherwise, it will return some other SocketError value. Be on the lookout for SE_SOCKET_ALREADY_BOUND, SE_PORT_IN_USE, or SE_BIND_CONFLICT.

See Also: SocketBind().

Include: socket.goh

SocketCheckListen()

int SocketCheckListen(
        SocketPort 		p,
        TCHAR 		*domain,
        int 		bufsize);

Use the SocketCheckListen() command on a port to find out the domain for the first connect request on a listening socket--that is, the first connection request which has not yet been accepted. You might think of this as a limited form of "caller ID"--a chance to find out a bit about the incoming connection before you accept it. If SocketCheckListen() returns zero then you can call ThreadGetError() to get the SocketError value. If it is SE_PORT_NOT_LISTENING, it means that you have not passed it a port to which a socket is listening.

This routine takes the following arguments:

p
The port to check
domain
Buffer to hold the domain name
bufsize
The size of the domain buffer

It returns the length of the domain name. If the return value is zero, there was an error. Look out for SE_PORT_NOT_LISTENING, which signals that there is no socket bound to the port in question which is listening.

See Also: SocketBind(),
SocketBindInDomain(),
SocketListen().

Include: socket.goh

SocketCheckReady()

int SocketCheckReady(
        SocketCheckRequest 		*requests,
        int 		numRequests,
        int 		timeout);

To check one or more sockets to see if they have received data, received connection requests, or are ready to write, call SocketCheckReady() . It can check connections for incoming data or just incoming urgent data.

For programs which need to poll many sockets, SocketCheckReady() provides a tidy means to do this without spawning a thread for each socket.

The SocketCheckReady() routine takes an array of SocketCheckRequest structures. Each one of these structures contains a socket and a condition. SocketCheckReady() looks at each SocketCheckRequest structure in the array and returns the index of the first structure whose socket meets the condition.

Thus, to determine if a given socket is properly set-up so that you can send data through it, you would pass SocketCheckReady() a one-element array (represented in pseudo-code):

{ mySocket, SC_WRITE, 0 }

To check several sockets to see whether any of them had received any connection requests, pass SocketCheckReady() an array of the form (represented in pseudo-code):

{ socket1, SC_ACCEPT, 0 },
{ socket2, SC_ACCEPT, 0 },
{ socket3, SC_ACCEPT, 0 }

For any socket, you may check for one or more of the following conditions:

SC_ACCEPT
If a socket is listening for a connection, this condition indicates that another socket is in fact trying to connect to the listening socket.
SC_READ
If a socket is connected, this condition indicates that a packet of data has come in and is ready to be read.
SC_EXCEPTION
If a socket is connected, this condition indicates that the socket has figured out that something has gone wrong with its connection.
SC_URGENT
If a socket is connected, this condition indicates that it has received a packet of data that was marked urgent.
SC_WRITE
This condition indicates that data may be sent through the socket.

If you query a socket about a condition that does not apply to its current state (e.g., ask a non-listening socket if it is ready to accept), then SocketCheckReady() will return SE_IMPROPER_CONDITION.

This routine takes the following arguments:

requests
The array of SocketCheckRequests structures, as described above.
numRequests
Number of elements in the requests array.
timeout
Time to wait for success, in 1/60 second ticks, or SOCKET_NO_TIMEOUT to try as long as possible.

The routine returns the index of the first SocketCheckRequest to meet its conditions.

Include: socket.goh

| Up: GEOS SDK TechDocs | Up | Prev: SGC_MACHINE ... | Next: SocketClose() ...


GEOS C Routines: SocketClose() ...

| Up: GEOS SDK TechDocs | Up | Prev: SocketBind() ... | Next: SocketGetAddressController() ...

SocketClose()

SocketError SocketClose(
        Socket 		s);

The SocketClose() routine closes a socket and frees it. Use it to close and free a datagram socket. It is the routine to call when responding to a SE_CONNECTION_CLOSED exception. SocketClose() may be used on a socket in any state, will shut down both outgoing and incoming data, cleans up, and frees the socket.

See Also: SocketCloseSend().

Include: socket.goh

SocketCloseDomainMedium()

SocketError 	SocketCloseDomainMedium(
        char 		*domain,
        MediumAndUnit 		*mau,
        Boolean		force);

This routine "hangs up" the hardware aspect of a socket connection. You may request that it only do so if there are no socket connections using the medium. If you force the medium closed with socket connections using the medium, the connections will receive no notification (they will just get SE_CONNECTION_RESET errors); therefore, you should force the medium closed only in extreme situations.

This routine takes the following arguments:

domain
Pointer to a string holding the domain name.
mau
Pointer to a MediumAndUnit structure specifying which medium to "hang up".
force
Zero to leave the medium alone if there are active connections using it; non-zero to force the medium closed even with active connections.

Include: socket.goh

SocketCloseSend()

SocketError SocketCloseSend(
        Socket 		s)

The SocketCloseSend() routine only works with connected sockets. (You should always use SocketClose() to close a datagram socket.) It partially shuts down the socket, so that the socket can no longer send data. SocketSend() will return SE_SOCKET_CLOSED if passed such a partially closed socket. However, you can still receive data via the socket--among other things, this means that you can use SocketRecv() to check for an SE_CONNECTION_CLOSED exception. SocketCloseSend() sends an SE_CONNECTION_CLOSED exception packet to the other side of the connection.

Include: socket.goh

SocketConnect()

SocketError SocketConnect(
        Socket 		s,
        SocketAddress 		*addr,
        int 		timeout);

To make a connection, call SocketConnect() . You will pass the socket created via SocketCreate() , the address to connect to, and a timeout value.

You cannot form a true connection using a datagram socket. However, you can use SocketConnect() with a datagram socket to specify a default destination address to use when sending packets.

This routine takes the following arguments:

s
The socket making the connection.
addr
The address to connect to. This must be a primitive address returned by SocketResolve() .
timeout
Time to wait for success, in 1/60 second ticks, or SOCKET_NO_TIMEOUT to try as long as possible.

The SocketConnect() routine returns a SocketError value. If the routine returns SE_NORMAL, then the connection was established successfully. If it returns SE_CONNECTION_REFUSED, the remote site wasn't prepared to accept the connection. If it returns SE_CONNECTION_ERROR, the connection didn't go through for some miscellaneous reason. If it returns SE_NON_UNIQUE_CONNECTION, then you're trying to create a connection that is identical to another connection: same source address, source port, destination address, destination port, and delivery type. There would be no way to tell these connections apart. If you try to open the same connection from both sides simultaneously, they may both fail with SE_NON_UNIQUE_CONNECTION.

Include: socket.goh

SocketCreate()

Socket SocketCreate(
        SocketDeliveryType 			delivery);

To create a new socket, call SocketCreate() . You will need to specify the socket's delivery type: SDT_DATAGRAM, SDT_SEQ_PACKET (sequential packets), or SDT_STREAM. If it returns zero, there was an error: call ThreadGetError() to get the error value, a SocketError .

Include: socket.goh

| Up: GEOS SDK TechDocs | Up | Prev: SocketBind() ... | Next: SocketGetAddressController() ...


GEOS C Routines: SocketGetAddressController() ...

| Up: GEOS SDK TechDocs | Up | Prev: SocketClose() ... | Next: SocketInterrupt() ...

SocketGetAddressController()

ClassStruct * SocketGetAddressController(
        char 			*domain)

Given a domain name, you can create some UI gadgetry for choosing addresses within that domain. Call SocketGetAddressController() to get the class of an appropriate controller.

This routine takes a buffer containing the name of the domain for which to create a controller. It returns a ClassStruct pointer which may be passed to ObjInstantiate() .

For API information about address controllers, see XXX XREF Socket chapter XXX.

If the routine returns zero, there was an error. Call ThreadGetError() to find out what the error was (it will be a SocketError value). Look out for SE_UNKNOWN_DOMAIN.

See Also: SocketGetDomains(), ObjInstantiate().

Include: socket.goh

SocketGetAddressMedium()

SocketError SocketGetAddressMedium(
        SocketAddress 		*sa,
        MediumAndUnit 		*mau);

To find out which port would be used to connect to a given address, call SocketGetAddressMedium() . The socket need not actually be connected. Depending on the medium to be used, you might try to connect to the other site via some other domain or to not make the connection at all.

Include: socket.goh

SocketGetAddressSize()

int SocketGetAddressSize(
        char 		*domain);

To find out the maximum address data size for a given domain, call SocketGetAddressSize() . This can be helpful when allocating memory large enough to handle a full address.

This routine returns zero on error. Call ThreadGetError() to find out what the error was--if ThreadGetError() returns zero, then there wasn't really an error; this domain just doesn't require an address buffer.

Include: socket.goh

SocketGetDomainMedia()

ChunkHandle SocketGetDomainMedia(
        char 		*domain,
        optr 		mediaList);

If you're ever curious to know which sort of hardware ports the user's machine uses to connect up to a given domain, call SocketGetDomainMedia() . This returns a chunk array of MediumType structures. If you have a choice of domains to make a given connection, you might consider which medium would be used to make the connection.

Include: socket.goh

SocketGetDomains()

ChunkHandle SocketGetDomains(
        optr 	domainList);

The SocketGetDomains() routine takes the optr of a chunk array and fills in said array with elements, each of which contains a string, each string being the name of a supported domain. This chunk array has a standard ChunkArrayHeader header and variable sized elements.

If the return value is zero, there was an error. Call ThreadGetError() to find out what it was.

Include: socket.goh

SocketGetMediumAddress()

SocketError SocketGetMediumAddress(
        char 		*domain,
        bye 		*buffer,
        int 		*bufsize,
        MediumAndUnit 		*mau);

This routine determines the local address of the connection using a particular medium. If another machine were to connect to this machine via that medium, this is the address it would use.

This routine takes the following arguments:

domain
The domain name (a null-terminated string).
buffer
Buffer to hold the address information.
bufsize
Size of buffer, in bytes.
mau
The medium.

If the routine returns SE_NORMAL, then the address was computed successfully. The buffer buffer will be filled with as much of the address data as fit; the bufsize number will have changed to the total size of the address data--if this was larger than the size of buffer, then the address was truncated.

SocketGetPeerName()

SocketError SocketGetPeerName(
        Socket 		s,
        SocketAddress 		*addr);

If a socket is connected, call SocketGetPeerName() to get the address of the socket at the other end of the connection.

This routine takes the following arguments:

s
The connected socket to examine.
addr
Buffer holding hollow SocketAddress structure. The SA_domain , SA_domainSize , and SA_addressSize fields should be initialized. The buffer should be large enough to hold a SocketAddress structure and address data.

It returns SE_NORMAL (i.e., zero) if there was no error; otherwise it will return some SocketError value. The addr buffer will be filled in with address information. If either the domain name or address buffer isn't large enough to hold its string, the resulting truncated string will not be null-terminated.

Include: socket.goh

SocketGetSocketName()

SocketError SocketGetSocketName(
        Socket 		s,
        SocketAddress 		*addr);

To find out the address used to represent the socket's own side of the connection, call SocketGetSocketName() .

This routine takes the following arguments:

s
The connected socket to examine.
addr
Buffer holding hollow SocketAddress structure. The SA_domain , SA_domainSize , and SA_addressSize fields should be initialized. The buffer should be large enough to hold a SocketAddress structure and address data.

It returns SE_NORMAL (i.e., zero) if there was no error; otherwise it will return some SocketError value. The addr buffer will be filled in with address information. If either the domain name or address buffer isn't large enough to hold its string, the resulting truncated string will not be null-terminated.

Include: socket.goh

SocketGetIntSocketOption()

int 	SocketGetIntSocketOption(
        Socket		skt,
        SocketOption		opt);

This routine checks one of a socket's options, returning the current setting for that option. The return value's meaning depends upon the passed

This routine takes the following arguments:

skt
The socket to check.
opt
Which option to check. The following options are available:
SO_RECV_BUF: the maximum size of the socket's buffer for incoming data. A value of -1 means no limit.
SO_SEND_BUF: the maximum size of the socket's buffer for outgoing data. A value of -1 means no limit.
SO_INLINE: Non-zero if urgent data is forced into the same queue as regular data; zero to treat urgent data specially.
SO_NODELAY: Non-zero if domain driver should send all packets immediately; zero to allow the driver to save up packets to send as larger blocks.
SO_OWNER: Get the handle of the Socket's owning thread.
| Up: GEOS SDK TechDocs | Up | Prev: SocketClose() ... | Next: SocketInterrupt() ...


GEOS C Routines: SocketInterrupt() ...

| Up: GEOS SDK TechDocs | Up | Prev: SocketGetAddressController() ... | Next: SocketRemoveLoadOnMsg() ...

SocketInterrupt()

SocketError SocketInterrupt(
        Socket 		s);

This routine attempts to interrupt a SocketAccept() or a SocketRecv() pending on the passed socket s.

SocketInterruptResolve()

SocketError SocketInterruptResolve(
        TCHAR 		*domain,
        byte 		*address,
        int  		addrSize);

This routine attempts to interrupt the resolution of the specified address.

This routine takes the following arguments:

domain
The name of the domain.
address
The address which is being resolved.
addrSize
The size of the address buffer.

Warnings: This function is unreliable. It may not interrupt the desired resolve call,if it executes before the resolve call has begun to be processed. If may also cause other, parallel resolve in the same domain to be interrupted.

SocketListen()

SocketError SocketListen(
        Socket 		s,
        int 		qSize);

To listen for incoming sequenced packet or stream connections, call SocketListen() . The socket must be bound to a port before it can listen. You may allow more than one connection through the port-specify the maximum number of connections as an argument to SocketListen() .

This routine takes the following arguments:

s
The Socket which should listen for incoming connections. The socket should be bound to a port.
qSize
The number of incoming connections which may be accepted via the socket.

The SocketListen() routine causes the passed socket to "listen" for accepting connections. If another socket is already listening at the port, the SocketListen() routine will return SE_PORT_ALREADY_LISTENING.

See Also: SocketBind(),
SocketAccept(),
SocketCheckReady().

Include: socket.goh

SocketOpenDomainMedium()

SocketError SocketOpenDomainMedium(
        SocketAddress 		*addr,
        int 		timeout);

This routine opens a "raw" connection using the specified medium. Exactly what is meant by a "raw" connection depends upon the domain involved. In the case of TCP, this routine will dial the modem and create a PPP connection to the PPP server--but will not send any TCP packets.

If there is already a "raw" connection, this routine will return SE_NORMAL, just as if it had just opened the connection.

If the device's phone is busy, this routine will return SE_MEDIUM_BUSY.

This routine takes the following arguments:

addr
The address to connect to.
timeout
How many 1/60 second ticks to wait for a connection. Pass SOCKET_NO_TIMEOUT to wait forever.

When done with the connection, close it with SocketCloseDomainMedium() .

See Also: SocketCloseDomainMedium().

Include: socket.goh

SocketRecv()

int SocketRecv(
        Socket 		s,
        void 		*buffer,
        int 		bufSize,
        int 		timeout,
        SocketRecvFlags 		flags,
        SocketAddress 		*addr);

The SocketRecv() routine receives data from the socket. After filling the passed buffer with data, the socket will discard its copy of the received data to make room for the next piece of incoming data.

To "peek" at the next packet of incoming data, call SocketRecv() and pass the SRF_PEEK flag. This allows you to get the size of the next packet of incoming data and even to non-destructively look at the contents of that packet.

When using packet-based delivery type sockets (sequenced packet or datagram delivery sockets), you can only receive whole packets-if you grab data from the socket, but don't grab all the data in the packet, the rest of the data will be lost. Thus, it's a good idea to establish a maximum packet size for sockets using these delivery types.If you're not sure how much room you'll need to receive a packet, call SocketRecv() with the SRF_PEEK flag-this allows you to "peek" at the incoming data without causing the socket to discard its copy.

The SocketRecv() routine takes the following arguments:

s
The socket from which to grab the data.
buffer
Buffer to fill with data.
bufSize
Size of the data buffer. If this smaller than the amount of data, and the socket delivery type is packet-based, the data that doesn't fit in the buffer will be lost; if the socket is a stream socket, then socket will retain the remaining data.
timeout
How many 1/60 second ticks to wait for incoming data. Pass SOCKET_NO_TIMEOUT to wait forever.
flags
This flags field allows you to specify some options. Set the SRF_ADDRESS flag if you want the address of the sending machine in addr . Set the SRF_URGENT flag to only receive a packet marked urgent. Set the SRF_PEEK flag to "peek" at the incoming packet, leaving it in the socket.
addr
If you pass the SRF_ADDRESS_FLAG, then pass an empty SocketAddress buffer. Make sure the buffer has room for the address data after the SocketAddress structure. The structure's SA_domain , SA_domainSize , and SA_addressSize fields should be initialized. If either the domain or address buffer isn't long enough to hold its string, the resulting truncated string will not be null-terminated.

The SocketRecv() routine returns the size of the received buffer. If this size is zero, there may be an error in the connection.

Include: socket.goh

| Up: GEOS SDK TechDocs | Up | Prev: SocketGetAddressController() ... | Next: SocketRemoveLoadOnMsg() ...


GEOS C Routines: SocketRemoveLoadOnMsg() ...

| Up: GEOS SDK TechDocs | Up | Prev: SocketInterrupt() ... | Next: SoundAllocMusic() ...

SocketRemoveLoadOnMsg()

SocketError SocketRemoveLoadOnMsg(
        SocketPort p);

If you've requested the Socket library to load a program when it senses packets coming to a port, you can undo that request by calling SocketRemoveLoadOnMsg() .

See Also: SocketAddLoadOnMsg().

Include: socket.goh

SocketRemoveLoadOnMsgInDomain()

SocketError SocketRemoveLoadOnMsgInDomain(
        SocketPort 		p,
        TCHAR 		*domain);

If you've requested the Socket library to load a program when it senses packets coming to a port in a given domain, you can undo that request by calling SocketRemoveLoadOnMsgInDomain() .

See Also: SocketAddLoadOnMsgInDomain().

Include: socket.goh

SocketResolve()

int SocketResolve(
        char 		*domainName,
        byte 		*rawAddr,
        int 		addrSize,
        byte 		*result,
        int 		resultBuffSize );

You cannot use an address with other Socket library routines unless that address is in its most primitive form. This primitive form is created by taking a higher-level form of the address and passing it to the SocketResolve() routine. You will recall that addresses returned by the address controller will not be in their most primitive form. The primitive form of an address can be volatile: feel free to re-use them if making several connections over a short period of time, but re-resolve the address if it's been a long time since it was last resolved. Note that there can be a large overhead when it comes to resolving addresses.

This routine takes the following arguments:

domainName
Buffer containing null-terminated domain name string.
rawAddr
Buffer containing the unresolved form of the address. Normally, this will be the buffer at the end of a SACAddress returned by a SocketAddressControl in response to MSG_SOCKET_ADDRESS_CONTROL_GET_ADDRESSES .
If you aren't resolving an address from a SocketAddressControl, but in fact need to construct a raw address from scratch, see the documentation for SACAddress .
addrSize
Size of the rawAddr buffer, normally the SACA_opaqueSize field from a SACAddress returned by a SocketAddressControl in response to MSG_SOCKET_ADDRESS_CONTROL_GET_ADDRESSES .
result
Buffer to hold the resolved address. This will normally be a buffer trailing a SocketAddress structure.
resultBufferSize
Size of the result buffer.

It returns the size of the resolved address. If it returns zero, there was an error. Call ThreadGetError() to find out what the error was. Typical errors to look out for are SE_BUFFER_TOO_SMALL, SE_DESTINATION_UNREACHABLE, and SE_TEMPORARY_ERROR.

Include: socket.goh

SocketSend()

SocketError SocketSend(
        Socket 		s,
        void 		*buffer,
        int 		bufSize,
        SocketSendFlags 		flags,
        SocketAddress 		*addr);

The SocketSend() routine sends data over a socket. It takes several arguments:

s
The socket through which to send the data
buffer
A buffer containing the data to send
bufSize
The size of the data buffer.
flags
Options to use when sending. Set SSF_ADDRESS if you're passing an address in addr . Set SSF_URGENT to mark the packet urgent.
addr
Address to which to send data.
If the socket is a sequenced packet or stream delivery type socket, then this optional address will be ignored.
If the socket is a datagram delivery type socket, specify the destination address of the packet here unless the socket has a default destination address as described below.
Datagram sockets can use the SocketConnect() routine to specify a default destination address. If they have no default address, then you must specify the destination address for the packet here. If there is a default destination address and you pass this optional address and those addresses do not match, then the Socket library will generate a fatal error.

Normally, SocketSend() will return SE_NORMAL. If it detects an error, it may return some other SocketError value.

Include: socket.goh

SocketSetIntSocketOption()

void	SocketSetIntSocketOption(
        Socket		skt,
        SocketOption		opt,
        int		newval);

This routine changes the setting for one of a socket's options.

This routine takes the following arguments:

skt
The socket.
opt
Which option to change. The following options are available:
SO_RECV_BUF: the maximum size of the socket's buffer for incoming data. A value of -1 means no limit.
SO_SEND_BUF: the maximum size of the socket's buffer for outgoing data. A value of -1 means no limit.
SO_INLINE: Non-zero if urgent data is forced into the same queue as regular data; zero to treat urgent data specially.
SO_NODELAY: Non-zero if domain driver should send all packets immediately; zero to allow the driver to save up packets to send as larger blocks.
SO_OWNER: Specify a new owning thread for the Socket.
newval
The new setting for the option. The meaning of this value depends on which option you are changing.

Include: socket.goh

| Up: GEOS SDK TechDocs | Up | Prev: SocketInterrupt() ... | Next: SoundAllocMusic() ...


GEOS C Routines: SoundAllocMusic() ...

| Up: GEOS SDK TechDocs | Up | Prev: SocketRemoveLoadOnMsg() ... | Next: SoundFreeMusic() ...

SoundAllocMusic()

word	SoundAllocMusic( /* Returns a SOUND_ERROR_ value */
        const word 		*song,
        word 		voices,
        MemHandle		*control );

This routine takes a pointer to a fixed buffer of music and allocates a MemHandle which may then be passed to SoundPlayMusic() to play the music. The handle is returned in the address specified by control . SoundAllocMusic() returns a non-zero value if it encounters an error trying to allocate the music block.

If the music buffer is in a movable resource, you must initialize it using SoundInitMusic() instead of SoundAllocMusic() . The voices argument is the number of voices in the buffer.

SoundAllocMusicNote()

word SoundAllocMusicNote( /* Returns a SOUND_ERROR_... value. */
        InstrumentPatch				instrument,
        word				instTable,
        word 				frequency,
        word 				volume,
        SoundStreamDeltaTimeType				DeltaType,
        word 				duration,
        MemHandle				*control);

This routine allocates a MemHandle which may be passed to SoundPlayMusicNote() . You must provide all information about the note: its frequency, volume, and duration.

To play the note using a standard instrument, pass the instrument's value in instrument and zero in instTable . To use a custom instrument, instTable : instrument should be a pointer to the instrument.

Specify the frequency in Hertz or use one of the constants such as MIDDLE_C_b to specify a standard note frequency. Volume ranges from zero to 0xffff--you may wish to use a constant value such as DYNAMIC_FFF if you want help trying to choose a loudness. The note's duration is determined by its delta type, one of SSDTT_MSEC, SSDTT_TICKS, and SSDTT_TEMPO. If you pass SSDTT_MSEC or SSDTT_TICKS, the duration is measured in milliseconds or ticks (each tick is one sixtieth of a second). If you pass SSDTT_TEMPO, you may set the size of your time unit when you call SoundPlayMusicNote() . The duration determines how many time units the note should play. If the delta type is SSDTT_TICKS and duration is 30, then the note will sound for half a second.

The handle of the newly allocated note will be stored at the location specified by control . SoundAllocMusicNote() returns non-zero if there is an error allocating the note.

SoundAllocMusicStream()

word	SoundAllocMusicStream( /* Returns a SOUND_ERROR_... value */
        word 		streamType,
        word 		priority,
        word 		voices,
        word 		tempo,
        MemHandle		*control);

This routine allocates a handle suitable for passing to SoundPlayToMusicStream() . The handle will be returned at the address specified by control. SoundAllocMusicStream() returns a non-zero value if there is an error allocating the handle.

It is passed several arguments. The SoundStreamType determines how much space to allocate for the stream and will determine how much data can be written to the stream at one time. If you pass SST_ONE_SHOT, it indicates that the stream will not be explicitly destroyed, and that your stream should destroy the stream when the song is done. You must specify how many voices there are in the music buffer. You must also pass a starting tempo for the music stream.

SoundAllocSampleStream()

word SoundAllocSampleStream( /* Returns a SOUND_ERROR_... value */
        MemHandle		*control);

This routine allocates a sample stream handle. If the returned handle is null , the library was unavailable (i.e. some other thread has grabbed exclusive access).

SoundDisableSampleStream()

void 	SoundDisableSampleStream(
        MemHandle 		mh);

This routine disassociates the DAC player from the passed sample handle. Before you play more sounds using the handle, you will have to call SoundEnableSampleStream() again.

SoundEnableSampleStream()

word	SoundEnableSampleStream( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh,
        SoundPriority		priority,
        word 		rate,
        word 		manufacturerID,
        DACSampleFormat		format);

This routine associates a DAC player with the allocated sample handle. You must pass the sound handle, as returned by SoundAllocSampleStream() . You must also pass certain pieces of information about the sound you will be playing on the DAC device: the priority with which to grab the DAC player (e.g. SP_STANDARD), the sampling rate, and the format of the sample (as identified by a manufacturerID and a DACSampleFormat value).

| Up: GEOS SDK TechDocs | Up | Prev: SocketRemoveLoadOnMsg() ... | Next: SoundFreeMusic() ...


GEOS C Routines: SoundFreeMusic() ...

| Up: GEOS SDK TechDocs | Up | Prev: SoundAllocMusic() ... | Next: SoundPlayToMusicStream() ...

SoundFreeMusic()

void 	SoundFreeMusic(
        MemHandle 		mh);

This routine frees up a music handle. The music must not be playing; call SoundStopMusic() if you are not sure. You may not use the music handle after calling this routine on it.

SoundFreeMusicNote()

void SoundFreeMusicNote(
        MemHandle 		mh);

This routine frees up the passed note handle. The note must not be playing when you call this routine; call SoundStopMusicNote() if you are not sure. You should not try to use the note's handle after freeing it.

SoundFreeMusicStream()

void SoundFreeMusicStream(
        MemHandle 		mh);

This routine frees up the music stream's token. No music must be playing via the stream; call SoundStopMusicStream() if you are not sure. Do not try to use the stream after calling this routine on it.

SoundFreeSampleStream()

void 	SoundFreeSampleStream(
        MemHandle 		mh);

This routine frees the passed sampled sound handle. You must not try to use this handle after calling this routine on it.

SoundGetExclusive()

void	SoundGetExclusive(void);

This routine grabs the exclusive semaphore for the sound library; if another thread has already grabbed the exclusive, this routine will wait until the exclusive is released. Sounds which are playing now will be permitted to finish, but from now on, only the thread calling this routine will be allowed to play new sounds. When done with the sound library exclusive, call SoundReleaseExclusive() .

SoundGetExclusiveNB()

Boolean	SoundGetExclusiveNB(void);

This routine grabs the exclusive semaphore for the sound library, doing so even if some other thread has already grabbed the exclusive. Sounds which are playing now will be permitted to finish, but from now on, only the thread calling this routine will be allowed to play new sounds. This routine will return true if another thread already has exclusive access.

When done with the sound library exclusive, call SoundReleaseExclusive() .

SoundInitMusic()

void SoundInitMusic(
        MemHandle 		mh,
        byte 		voices);

This routine initializes a pre-defined simple music buffer structure. If the music buffer is stored in a fixed block, you can call SoundAllocMusic() instead. This allows a music buffer stored in a block referenced by a pointer to be playable using SoundPlayMusic() .

SoundPlayMusic()

word	SoundPlayMusic( /* Returns SOUND_ERROR_... value */
        MemHandle 		mh,
        word		priority,
        word 		tempo,
        EndOfSongFlags		flags);

This routine plays a buffer of music previously initialized by SoundInitMusic() or allocated by SoundAllocMusic() . The priority value will determine whether your sound will play if other sounds are already occupying the voices--pass a value such as SP_STANDARD. The tempo value will be used to determine the length of a 1/128th note. If your music buffer contained any notes whose lengths were measured by SSDTT_TEMPO delta type, then you should set this value accordingly. The flags argument determines whether the music's handle should be automatically freed when the sound is done playing. You may pass either or both of the flags UNLOCK_ON_EOS or DESTROY_ON_EOS.

Include: sound.h

SoundPlayMusicNote()

word 	SoundPlayMusicNote( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh,				/* handle of note */
        word 		priority,
        word		tempo,
        EndOfSongFlags		flags);

This routine plays a buffer of music previously allocated by SoundAllocMusicNote() --the return value of that function is passed as mh . The priority value will determine whether your sound will play if other sounds are already occupying the voices--pass a value such as SP_STANDARD. The tempo value will be used to determine the length of a 1/128th note. If your note's delta type is SSDTT_TEMPO, then you should set this value accordingly. The flags argument determines whether the notes's handle should be automatically freed when the note is done playing. You may pass either or both of the flags UNLOCK_ON_EOS or DESTROY_ON_EOS.

This routine returns true if the library was unavailable (i.e. if some other thread had grabbed the sound exclusive).

Include: sound.h

| Up: GEOS SDK TechDocs | Up | Prev: SoundAllocMusic() ... | Next: SoundPlayToMusicStream() ...


GEOS C Routines: SoundPlayToMusicStream() ...

| Up: GEOS SDK TechDocs | Up | Prev: SoundFreeMusic() ... | Next: SpeedDialGetNumber() ...

SoundPlayToMusicStream()

word	SoundPlayToMusicStream( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh,
        const word		* song,
        word 		size);

This routine plays a music buffer to a stream. Specify which stream to play to by means of the token returned by SoundAllocMusicStream() . To play music to the buffer, pass the size of the buffer you are playing and a pointer to the start of the piece. This piece of buffer must be made up of whole events--it should not start or end in the middle of an event (e.g. you can't specify that you want to play a note but not give its frequency, even if you plan to play another buffer to the stream that might begin with a frequency).

If you don't know the size of the buffer, it may be all right--any data in the buffer after the GE_END_OF_SONG will be ignored.

SoundPlayToMusicStreamNB()

word	SoundPlayToMusicStreamNB( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh,
        const word		* song,
        word 		size,
        word		*bytesWritten);

This routine acts as SoundPlayToMusicStream() , except in the case where the routine would block. If the music stream fills up, SoundPlayToMusicStream() blocks, waiting until there is more room in the stream; SoundPlayToMusicStreamNB() will instead exit, returning the number of bytes successfully written to the stream.

SoundPlayToSampleStream()

word SoundPlayToSampleStream( /* Returns a SOUND_ERROR_... value */
        MemHandle 				mh,
        word _far				* sample,
        word 				size,
        SampleFormatDescription				* format);

This routine passes sampled sound data to a DAC player. You must pass a sample sound handle to this routine--to acquire such a handle, call SoundAllocSampleStream() . The sample sound handle must be associated with a DAC player--to so associate the handle, call SoundEnableSampleStream() . You must pass a pointer to the sample data, along with the size of the sample as measured in bytes. You may change the format information which will determine how the DAC player handles the data.

SoundReallocMusic()

word 	SoundReallocMusic( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh,
        word _far 		* song);

This routine allows you to associate a new music buffer with an existing music handle. The new music buffer must not have more voices than was originally requested with SoundAllocMusic() . Do not call this routine with the handle of a sound that may be playing; call SoundStopMusic() on the handle if you are not sure.

SoundReallocMusicNote()

word 	SoundReallocMusicNote( /* Returns a SOUND_ERROR_... value */
        MemHandle 				mh,
        word 				freq,
        word 				vol,
        SoundStreamDeltaTimeType				timer,
        word 				durat,
        word				instrum,
        word				instTable);

This routine allows you to associate new note values with an existing note handle. Do not call this routine with the handle of a note that may be playing; call SoundStopMusicNote() on the handle if you are not sure.

SoundReleaseExclusive()

void	SoundReleaseExclusive(void);

This routine releases the sound library exclusive semaphore. You will not need to call this routine unless your code calls SoundGetExclusive() or SoundGetExclusiveNB() . This routine allows other threads to play sounds. If another thread called SoundGetExclusive() while your thread had the exclusive, it will now grab the exclusive.

SoundSampleDriverInfo()

void 	SoundSampleDriverInfo(
        word _far 					*voices,
        SoundDriverCapability _far 					*capability);

This routine determines the machine's ability to play recorded sounds.

SoundStopMusic()

word 	SoundStopMusic( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh); 		/* Handle of music buffer */

This routine stops the playing of a simple music buffer. It returns true if the library was unavailable (i.e. some other thread has grabbed the exclusive).

SoundStopMusicNote()

word 	SoundStopMusicNote( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh);

This routine stops a note that is playing. Pass the handle of the note, as was returned by SoundAllocMusicNote() . This routine returns true if the sound library was unavailable (i.e. some other thread has grabbed the exclusive).

SoundStopMusicStream()

word 	SoundStopMusicStream( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh);

This routine stops any music being played to the stream. All sounds are flushed from the stream. It takes one argument, the token of the sound stream, as returned by SoundAllocMusicStream() .

SoundSynthDriverInfo()

void 	SoundSynthDriverInfo(
        word _far 					*voices,
        SupportedEnvelopeFormat _far 					*format,
        SoundDriverCapability  _far 					*capability)

This routine describes the machine's music playing ability.

| Up: GEOS SDK TechDocs | Up | Prev: SoundFreeMusic() ... | Next: SpeedDialGetNumber() ...


GEOS C Routines: SpeedDialGetNumber() ...

| Up: GEOS SDK TechDocs | Up | Prev: SoundPlayToMusicStream() ... | Next: SpoolGetNumPaperSizes() ...

SpeedDialGetNumber()

int 	SpeedDialGetNumber(
        int		slot,
        TCHAR		*buf);

This function gets the phone number associated with a Speed Dial number. The function fills in the passed buf string with the phone number. If there is no such Speed Dial number, the function returns -1.

Include: contdb.goh

SpeedDialGetRecord()

int 	SpeedDialGetRecord(
        int		slot,
        RecordID 		*rid,
        FieldID		*fid);

This function returns the Contact and field corresponding to the passed Speed Dial number. If there is no such Speed Dial number, the function returns -1. This function takes the following arguments:

slot
The Speed Dial number.
rid
Pointer to a RecordID buffer. The function will fill this in with the RecordID of the Contact record.
fid
Pointer to a FieldID buffer. The function will fill this buffer in with the FieldID of the contact's field with the selected phone number.

Include: contdb.goh

SpeedDialGetSlot()

int 	SpeedDialGetSlot(
        RecordID 		rid,
        FieldID		fid);

This function gets the Speed Dial number for the Contact phone number represented by the passed RecordID and FieldID numbers. If there is no such Speed Dial number, the function returns -1.

Include: contdb.goh

SpeedDialSetRecord()

int 	SpeedDialSetSlot(
        int 		slot,
        RecordID 		rid,
        FieldID		fid);

This function sets the Speed Dial number for the Contact phone number represented by the passed RecordID and FieldID numbers. If that Speed Dial slot was not previously empty, the function returns -1.

Include: contdb.goh

SpoolConvertPaperSize()

word 	SpoolConvertPaperSize(
        word		width,			/* width of paper */
        word		height,			/* height of paper */
        PageType 		pageType);			/* type of page */

This routine converts a width and height into a page size number.

Include: spool.goh

SpoolCreatePaperSize()

word	SpoolCreatePaperSize( /* Returns zero if failed */
        char	*paperSizeString,		/* descriptive name MAX_PAPER_STRING_LENGTH */
        word	width,		/* width of paper */
        word	height,		/* length of paper */
        PageLayout laytout,			/* page layout */
        PageType pageType);			/* page type */

This routine defines and stores a new paper size for later use by the user. If successful, it will return a number representing the new paper size, a value between 128 and 255; if not successful, it will return zero.

Include: spool.goh

SpoolCreatePrinter()

word	SpoolCreatePrinter( 	/* Returns printer number, or -1 if
                  there was an error */
        char 		*printerName,				/* name of printer */
        PrinterDriverType 		type); 				/* device type */

Adds the printer to the list of currently installed printers and returns the new printer number. This routine is normally called from within the Preferences manager. If the routine returns 0xffff, then the printer already exists and thus could not be created.

Include: spool.goh

SpoolDelayJob()

SpoolOpStatus 	SpoolDelayJob(
        word 	JobID);

This routine moves a print job to the end of the print queue.

SpoolDeletePaperSize()

Boolean 	SpoolDeletePaperSize(
        word 	paperSizeNum,		/* size number to delete */
        PageType pageType);			/* page type */

This routine deletes a user-defined paper size. It will return true (i.e., non-zero) if there was an error.

Include: spool.goh

SpoolDeletePrinter()

void 	SpoolDeletePrinter(
        int 	prtrNum);		/* printer number to delete */

Deletes the requested printer from the system.

Include: spool.goh

SpoolDelJob()

SpoolOpStatus 	SpoolDelJob(
        word jobID);

This routine deletes a printer job.

SpoolGetDefaultPageSizeInfo()

void	SpoolGetDefaultPageSizeInfo(
        PageSizeReport		*psr);

This routine gets the dimensions of the system default page size.

Include: spool.goh

SpoolGetDefaultPrinter()

int 	SpoolGetDefaultPrinter(); /* Returns printer number */

Returns the system-default printer, which is used (for example) by the PrintControlClass as the default printer to print to.

Include: spool.goh

| Up: GEOS SDK TechDocs | Up | Prev: SoundPlayToMusicStream() ... | Next: SpoolGetNumPaperSizes() ...


GEOS C Routines: SpoolGetNumPaperSizes() ...

| Up: GEOS SDK TechDocs | Up | Prev: SpeedDialGetNumber() ... | Next: SpoolModifyPriority() ...

SpoolGetNumPaperSizes()

void	SpoolGetNumPaperSizes(
        NumPaperSizesInfo		*sizesInfo,
        PageType 		type);

Use this routine to find the number of paper sizes, both pre-defined and user-defined, that should appear in a paper size list. The number of paper sizes, along with the index of the default paper size will be returned in a Num

Structures:

        typedef struct {
                word    NPSI_numSizes;
                word    NPSI_defaultSize;
        } NumPaperSizesInfo;

Include: spool.goh

SpoolGetNumPrinters()

word	SpoolGetNumPrinters(
        PrinterDriverType		type,				   /* driver type */
        byte		localOnlyFlag); /* non-zero if only
                                * locally-defined printers should
                                * be counted. */

This routine returns the number of installed printers with the given type.

Include: spool.goh

SpoolGetPaperSize()

void 	SpoolGetPaperSize(
        PaperSizeInfo		sizeInfo, 			/* This must be between 0 and the return
                          * value of SpoolGetNumPaperSizes() */
        word		paperSizeNum,	 		/* a paper size number between
                                0 and SpoolGetNumPaperSizes() */
        PageType 		*pageType);			/* page type*/

Use this routine to determine the dimensions of a paper size.

Structures:

        typedef struct {
            word        PSI_width;          /* width, in points */
            word        PSI_height;         /* height, in points */
            PageLayout  PSI_defaultLayout;
        } PaperSizeInfo;

Include: spool.goh

SpoolGetPaperSizeOrder()

void	SpoolGetPaperSizeOrder(
        byte 		*order, 			/* buffer of size MAX_PAPER_SIZES */
                        /* On return, this buffer will be
                         * filled with the page size numbers
                         * arranged in the order
                         * corresponding to their display */
        byte 		*userSizes,		 	/* buffer of size MAX_PAPER_SIZES */
                        /* On return, will hold ordered
                         * array of user paper sizes. */
        PaperSizeOrderInfo		*numBuf,			/* empty PaperSizeOrderInfo struct */
        PageType                    pageType);  /* What sort of paper we're interested in */

This routine retrieves the paper size order array.

Structures:

        typedef struct {
            word    PSOI_numOrdered;    /* number of ordered sizes */
            word    PSOI_numUnused;     /* number of unused sizes */
        } PaperSizeOrderInfo;

Include: spool.goh

SpoolGetPaperString()

word	SpoolGetPaperString( /* true if error*/
        char	   *stringBuf,		/* empty buffer MAX_PAPER_STRING_LENGTH */
        word	   paperSizeNum,		/* returned name string */
        PageType	  prtrNum);		/* printer number */

Use this routine to determine the string to be displayed for a specific paper size. Upon return, stringBuf will have been filled with a null-terminated string describing the paper. The routine's return value is the length of the paper string, not including the null terminator.

Include: spool.goh

SpoolGetPrinterString()

Boolean	SpoolGetPrinterString( /* Returns true if error */
        PrinterStringInfo	   *retInfo,					/* pass empty structure */
        word	  	printerNum);				/* printer number */

This routine fills a buffer with the requested null-terminated printer name string. If the printer could not be found, the return value will be true (set for error).

Include: spool.goh

SpoolHurryJob()

SpoolOpStatus 	SpoolHurryJob(
        word 	JobID);

This routine moves a print job to the head of the print queue.

SpoolInfo()

SpoolOpStatus 	SpoolInfo(
        SpoolInfoType 		infoType,
        SpoolInfoParams 		*params);

This routine returns various types of information about the print queue's status. You may request information about the queue or about any job on the queue.

Structures:

        typedef enum /* word */ {
            SIT_JOB_INFO    = 0,
            SIT_QUEUE_INFO  = 2
        } SpoolInfoType;

The passed SpoolInfoType specifies whether you seek information about the print queue or a single job on the queue.

        typedef union {
            SpoolInfoQueueParams    SIP_queueParams;
            SpoolInfoJobParams      SIP_jobParams;
        } SpoolInfoParams;

The type of parameters used depend on the passed SpoolInfoType .

        typedef struct {
            Boolean 			SIQP_activityQuery;
            PrintPortInfo 			*SIQP_portInfo;
            MemHandle 			SIQP_retBlock;
            word 			SIQP_retNumJobs;
        } SpoolInfoQueueParams;

This structure contains information about the print queue and the SIQP_retBlock field points to a block of JobStatus structures.

        typedef struct {
            word        SIJP_jobID;
            MemHandle   SIJP_retBlock;
        } SpoolInfoJobParams;

This structure is used to pass the ID of the job to describe. The passed block will hold la JobStatus on return.

        typedef struct {
            char JS_fname[13]; /* std DOS (8.3) spool filename */
            char JS_parent[FILE_LONGNAME_LENGTH+1]; /* parent app's name */
            char JS_documentName[FILE_LONGNAME_LENGTH+1]; /* document name */
            word JS_numPages;                   /* # pages in document */
            SpoolTimeStruct JS_time;            /* time spooled */
            byte JS_printing;                   /* TRUE/FALSE if we are printing */
        } JobStatus;

Each JobStatus structure describes a single print job.

| Up: GEOS SDK TechDocs | Up | Prev: SpeedDialGetNumber() ... | Next: SpoolModifyPriority() ...


GEOS C Routines: SpoolModifyPriority() ...

| Up: GEOS SDK TechDocs | Up | Prev: SpoolGetNumPaperSizes() ... | Next: StreamOpen() ...

SpoolModifyPriority()

SpoolOpStatus 	SpoolModifyPriority(
        word 		jobID,
        ThreadPriority 		priority);

Use this routine to modify the priority of the thread that processes a print job's print queue.

SpoolSetDefaultPageSizeInfo()

void	SpoolSetDefaultPageSizeInfo(
        PageSizeReport		*psr);

Set the dimensions of the default system page.

Include: spool.goh

SpoolSetDefaultPrinter()

void 	SpoolSetDefaultPrinter(
        int prtrNum);		/* printer number */

Sets the system-default printer, used (for example) by PrintControlClass as the default printer. This routine is normally called from within the Preferences manager.

Include: spool.goh

SpoolSetDocSize()

void 	SpoolSetDocSize(
        Boolean 		open;			/* false if document is closed */
        PageSizeInfo 		* psr);			/* NULL if document is closed */

This routine tells the application's PageSizeControl object the document's size.

Include: spool.goh

SpoolSetPaperSizeOrder()

void 	SpoolSetPaperSizeOrder(
        byte	*orderArray, 		/* Array of paper siaes. Each element is
                 * a byte signifying:
                 * 0-127  : a pre-defined paper size, or
                 * 128-255: a user-defined paper size. */
        word 	number, 		/* number of entries in array */
        PageType pageType);			/* page type (paper, envelope, label) */

This routine resets the order in which paper sizes are displayed to the user.

Include: spool.goh

SpoolVerifyPrinterPort()

SpoolOpStatus SpoolVerifyPrinterPort(
        PrintPortInfo *portInfo);

Use this routine to check for the existence of a printer port

SpreadsheetInitFile()

VMBlockHandle SpreadsheetInitFile(
        const SpreadsheetInitFileData					* ifd);

This routine initializes a VM file for use by the spreadsheet object. It allocates a spreadsheet map block in the file and initializes this block. The routine returns the map block's handle; applications will need to remember this handle. It does not change any existing blocks in the VM file.

The ifd parameter is pointer to a SpreadsheetInitFileData structure containing the file handle and the number of rows and columns to allocate.

Structures: The SpreadsheetInitFileData structure is defined as follows:

typedef struct {
    word				SIFD_file;
    word				SIFD_numRows;
    word 				SIFD_numCols;
    SpreadsheetDrawFlags				SIFD_drawFlags;
} SpreadsheetInitFileData;
/* SpreadsheetDrawFlags:
 * SDF_DRAW_GRAPHICS
 * SDF_DRAW_NOTE_BUTTON
 * SDF_DRAW_HEADER_FOOTER_BUTTON
 * SDF_DRAW_GRID				*/

Include: ssheet.goh

StreamClose()

StreamError StreamClose (
        GeodeHandle 		driver,
        StreamToken 		stream,
        Boolean 		linger);

This routine shuts down a stream. It is passed the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
linger
Set true (i.e., non-zero) if the data currently in the stream should be kept until it's read; set false to flush the data immediately.

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

StreamFlush()

StreamError StreamFlush (
        GeodeHandle 		driver,
        StreamToken 		stream);

This routine flushes all the data pending in a stream. It is passed the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

StreamGetError()

StreamError	 	StreamGetError (
        Handle 		driver,
        StreamToken 		stream,
        StreamRoles 		roles,
        word *		errorCode);

This routine returns any driver-specific error codes set by the last call to a stream routine. It takes the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
role
If this is STREAM_ROLES_WRITER, the routine will return the last error from a write operation. If it is STREAM_ROLES_READER, it will return the last error from a read operation.
errorCode
A pointer to a word-sized variable. The device's error code is written to this variable.

If the routine is successful, it returns zero and writes the device's last error code into the errorCode variable. If it is unsuccessful, it returns a member of the StreamError enumerated type.

| Up: GEOS SDK TechDocs | Up | Prev: SpoolGetNumPaperSizes() ... | Next: StreamOpen() ...


GEOS C Routines: StreamOpen() ...

| Up: GEOS SDK TechDocs | Up | Prev: SpoolModifyPriority() ... | Next: StreamWrite() ...

StreamOpen()

StreamError	 StreamOpen (
        GeodeHandle 		driver,
        word 		buffSize,
        GeodeHandle		owner,
        HeapFlags 		heapFlags,
        StreamToken *		stream);

This routine opens a stream. It is passed the following:

driver
The GeodeToken of the stream driver.
buffSize
The size of the stream buffer, in bytes.
owner
The geode which will own the stream.
heapFlags
The flags for the creation of the buffer block.
*stream
The stream token will be written here.

If StreamOpen() is successful, it returns zero and writes the stream's token to *stream . If it is unsuccessful, it returns a member of the StreamError enumerated type.

StreamQuery()

StreamError StreamQuery (
        GeodeHandle 		driver,
        StreamToken 		stream,
        StreamRoles 		role,
        word *		bytesAvailable);

This routine finds out either how much free space is available in a stream's buffer, or how much data is waiting to be read. It is passed the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
role
If this is STREAM_ROLES_WRITER, the routine will return the amount of free space available in the stream buffer. If it is STREAM_ROLES_READER, it will return the amount of data waiting to be read.
*bytesAvailable
The routine will write the number of bytes available (for writing or reading) to this variable.

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

StreamRead()

StreamError StreamRead (
        GeodeHandle 		driver,
        StreamToken 		stream,
        StreamBlocker 		blocker,
        word 		buffSize,
        byte *		buffer,
        word *		numBytesRead);

This routine reads data from a stream. The routine takes the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
blocker
Specify whether to block if there is not enough data waiting to be read.
buffsize
Size of passed buffer (i.e. amount of data to read from stream).
buffer
Pointer to buffer where data from stream will be written.
*numBytesReadRead
StreamRead() will write to this variable the number of bytes actually read from the stream.

If StreamRead() is successful, it returns zero. If it is unsuccessful, or could not read all the data requested from the stream, it returns a member of the StreamError enumerated type.

StreamReadByte()

StreamError StreamWriteByte (
        GeodeHandle 		driver,
        StreamToken 		stream,
        StreamBlocker 		blocker,
        byte *		dataByte);

This routine reads a single byte from a stream. It takes the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
blocker
Specify whether to block if there is not enough room to write the data.
*dataByte
Read a byte from the stream, and write it to this variable.

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

StreamSetError()

StreamError StreamSetError(
        Handle 		driver,
        StreamToken 		stream,
        StreamRoles 		roles,
        word 		errorCode);

This routine sets the device-specific error code for a stream; this code can later be recovered by StreamGetError() . It takes the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
role
Either STREAM_ROLES_WRITER or STREAM_ROLES_READER.
errorCode
A 16-bit device-specific error code.

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

| Up: GEOS SDK TechDocs | Up | Prev: SpoolModifyPriority() ... | Next: StreamWrite() ...


GEOS C Routines: StreamWrite() ...

| Up: GEOS SDK TechDocs | Up | Prev: StreamOpen() ... | Next: SysGetPenMode() ...

StreamWrite()

StreamError StreamWrite (
        GeodeHandle 		driver,
        StreamToken 		stream,
        StreamBlocker 		blocker,
        word 		buffSize,
        const byte *		buffer,
        word *		numBytesWritten);

This routine writes data to a stream. The routine takes the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
blocker
Specify whether to block if there is not enough room to write all the data.
buffsize
Size of passed data buffer (i.e. amount of data to write to stream).
buffer
Pointer to data to write to stream.
*numBytesWritten
StreamWrite() will write to this variable the number of bytes actually written to the stream.

If StreamWrite() is successful, it returns zero. If it is unsuccessful, or could not write all the data to the stream, it returns a member of the StreamError enumerated type.

StreamWriteByte()

StreamError StreamWriteByte (
        GeodeHandle 		driver,
        StreamToken 		stream,
        StreamBlocker 		blocker,
        byte 		dataByte);

This routine writes a single byte to a stream. It takes the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
blocker
Specify whether to block if there is not enough room to write the data.
dataByte
Write this byte to the stream.

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

SysGetConfig()

dword	SysGetConfig();

This routine returns a set of values defining the system configuration. The returned dword contains four byte values, listed below from least significant byte to most significant byte:

configuration flags
This byte contains a record of SysConfigFlags reflecting the system status. This record includes information on how the system was started, whether Swat is running it, whether the system was restarted, etc.
reserved byte
This byte contains reserved information unusable by applications.
processor type
This byte contains a value reflecting the processor type of the machine running GEOS. This is of type SysProcessorType and is one of SPT_8088, SPT_8086, SPT_80186, SPT_80286, SPT_80386, or SPT_80486. Use the macro SGC_PROCESSOR() to extract this value from the returned dword.
machine type
This byte contains a value of SysMachineType indicating the type of the machine running GEOS. It may be one of the following values: SMT_UNKNOWN, SMT_PC, SMT_PC_CONV, SMT_PC_JR, SMT_PC_XT, SMT_PC_XT_286, SMT_PC_AT, SMT_PS2_30, SMT_PS2_50, SMT_PS2_60, SMT_PS2_80, or SMT_PS1. Use the macro SGC_MACHINE() to extract this value from the returned dword.

Include: system.h

SysGetDosEnvironment()

Boolean	SysGetDosEnvironment( /* true if error (not found) */
        const char		* variable,				/* environment variable */
        char		* buffer,				/* buffer for return value */
        word		bufSize);				/* maximum return string length */

This routine looks up a specified DOS environment variable in the environment buffer. It takes three parameters:

variable
A pointer to the null-terminated character string representing the name of the variable to be searched for.
buffer
A pointer to a locked or fixed buffer in which the variable's value will be returned.
bufSize
The size of the passed buffer in bytes (the maximum number of characters that can be returned including the terminating null character).

If the variable is not found, the error flag returned will be true .

Include: system.h

SysGetECLevel()

ErrorCheckingFlags SysGetECLevel(
        MemHandle * checksumBlock);

This routine checks the current error-checking level of the system. The returned record of ErrorCheckingFlags describes which levels of error checking are turned on and which are off. If checksum error checking (ECF_BLOCK_CHECKSUM) is on, pass a pointer to the handle of a block on which the checksum will be done.

Include: ec.h

SysGetInfo()

dword	SysGetInfo(
        SysGetInfoType info);			/* type of information to retrieve */

This routine returns general system information. Pass the type of information to be returned; the value returned depends on the type passed in info . Note that the largest returned value is a dword; many different return values should be cast to the appropriate type when calling SysGetInfo() .

The info parameter (of SysGetInfoType ) can have one of the following values:

SGIT_TOTAL_HANDLES
Returns the total number of handles in the kernel's handle table.
SGIT_HEAP_SIZE
Returns the total heap size in bytes.
SGIT_LARGEST_FREE_BLOCK
Returns the size (in bytes) of the largest possible block that may be allocated at the moment.
SGIT_TOTAL_COUNT
Returns the total number of clock ticks since the current session of GEOS started (subtracts the initial system clock value from the current time).
SGIT_NUMBER_OF_VOLUMES
Returns the total number of volumes registered with the system.
SGIT_TOTAL_GEODES
Returns the total number of geodes currently loaded.
SGIT_NUMBER_OF_PROCESSES
Returns the total number of processes currently loaded.
SGIT_NUMBER_OF_LIBRARIES
Returns the total number of libraries currently loaded.
SGIT_NUMBER_OF_DRIVERS
Returns the total number of drivers currently loaded.
SGIT_CPU_SPEED
Returns the CPU speed of the processor. The value returned will be ten times the ratio of the CPU speed relative to a base XT processor.
SGIT_SYSTEM_DISK
Returns the disk handle of the disk on which GEOS (the GEOS.INI file) resides.
SGIT_UI_PROCESS

Include: sysstats.h

| Up: GEOS SDK TechDocs | Up | Prev: StreamOpen() ... | Next: SysGetPenMode() ...


GEOS C Routines: SysGetPenMode() ...

| Up: GEOS SDK TechDocs | Up | Prev: StreamWrite() ... | Next: SysShutdown() ...

SysGetPenMode()

Boolean	SysGetPenMode();

This routine returns true if GEOS is running on a pen-based system, false if it is not.

Include: system.h

SysLocateFileInDosPath()

DiskHandle SysLocateFileInDosPath( /* sets thread's error value */
        const char		* fname,				/* file name */
        char		* buffer);				/* returned path of file */

This routine searches for a specified file along the search path specified in the DOS environment variable PATH. The parameters are

fname
A pointer to the null-terminated file name to search for.
buffer
A pointer to a locked or fixed buffer into which the full path of the file will be placed.

This routine returns the disk handle of the disk on which the file resides as well as the file's full path (with drive name) in the buffer pointed to by buffer . The path returned is a null-terminated character string. If the file could not be found, a null disk handle will be returned. The error value can be retrieved with ThreadGetError() .

Include: system.h

SysNotify()

word	SysNotify(
        SysNotifyFlags		flags,				/* options to offer user */
        const char 		* string1,				/* first string to display */
        const char 		* string2);				/* second string to display */

This routine causes the kernel to put up a standard notification dialog box on the screen. This dialog box is white with a black border and is used nearly exclusively for error notification by the kernel. Pass this routine the following parameters:

flags
A record of SysNotifyFlags indicating the options the dialog presents to the user. These flags are shown below.
string1
A pointer to a null-terminated character string put up in the dialog box (may be a null pointer).
string2
A pointer to a second null-terminated string presented in the dialog box (may be a null pointer).

The returned word is the user's response, based on the SysNotifyFlags passed (see below).

Structures: SysNotifyFlags is a record of several flags; none, any, or all of the flags may be set at a time. The five flags are

SNF_RETRY
Allow the user to retry the operation that brought up the notification box. If the user selects this option, it will be returned by the routine.
SNF_EXIT
Allow the user to exit GEOS entirely. If the user selects this option, it will be returned by the routine after an SST_CLEAN_FORCED shutdown has been initiated.
SNF_ABORT
Allow the user to abort the operation that brought up the notification box. If the user selects this option, it will be returned by the routine.
SNF_CONTINUE
Allow the user to continue the operation. If the user selects this option, it will be returned by the routine.
SNF_REBOOT
Allow the user to shut down and reboot GEOS directly. If the user selects this option, the routine will not return.

Include: system.h

SysRegisterScreen()

void	SysRegisterScreen(
        GeodeHandle		driver,
        WindowHandle		root);

SysSetECLevel()

void	SysSetECLevel(
        ErrorCheckingFlags		flags,					/* level of error checking */
        MemHandle		checksumBlock);					/* block to check, if any */

This routine sets the error-checking level of the software. Pass it a record of ErrorCheckingFlags indicating which levels of error checking should be employed. If checksum checking (ECF_BLOCK_CHECKSUM) is turned on, also pass the handle of a block on which the checksum will be performed.

Include: ec.h

SysSetExitFlags()

word	SysGetExitFlags(
        ExitFlags		bitsToSet,
        ExitFlags		bitsToClear);

| Up: GEOS SDK TechDocs | Up | Prev: StreamWrite() ... | Next: SysShutdown() ...


GEOS C Routines: SysShutdown() ...

| Up: GEOS SDK TechDocs | Up | Prev: SysGetPenMode() ... | Next: TextSearchInString() ...

SysShutdown()

Boolean	SysShutdown(
        SysShutdownType type,
        ...);

This routine causes the system to shut down, exiting to the native operating system (typically DOS). It takes variable parameters depending on the first parameter. The first parameter is the type of shutdown requested, and it determines the calling format of the routine. SysShutdown() returns a Boolean value dependent on the type of shutdown.

The parameters and calling format for this routine depend on the value in the type parameter. The possible values ( SysShutdownType ) are listed below with the associated parameter and return information.

SST_CLEAN
Shut down all applications cleanly, allowing any that wish to to abort the shutdown. The routine will return true if a system shutdown is already in progress at the time of the call. This type of shutdown will send MSG_META_CONFIRM_SHUTDOWN to all objects registered on the MANUFACTURER_ID_GEOWORKS:GCNSLT_SHUTDOWN_CONTROL GCN list (but only if the shutdown is not cancelled). Each object on that list must return an acknowledgment of the shutdown. The parameter format and parameters are
Boolean SysShutdown(
    SysShutdownType			type,
    optr			notificationOD,
    Message			msg);
notificationOD
The optr of an object which will receive the message passed in msg after the shutdown has been acknowledged. Pass a null optr to use the default notification ( MSG_META_DETACH sent to the UI).
msg
The message to be sent to the object in notificationOD .
SST_CLEAN_FORCED
Shut down all applications cleanly without the possibility of cancellation. This type takes no additional parameters and does not allow other geodes to abort the shutdown. It will return, but the return value will be meaningless.
SST_DIRTY
Attempt to exit device drivers and close all files without shutting down applications. Does not return. The parameters of this type are
Boolean SysShutdown{
    SysShutdownType			type,		/* SST_DIRTY */
    const char			* reason);
The reason parameter is a pointer to a text string presented to the user as a reason for the dirty shutdown. The string is null-terminated. Pass -1 if no reason is to be given.
SST_PANIC
Exit system device drivers (GA_SYSTEM) without exiting applications or closing files. This can be bad for the system and should be used only in emergency situations. This type of shutdown takes no additional parameters and does not return.
SST_REBOOT
This is used by GEOS when the user hits Ctrl-Alt-Del . Applications should not call it.
SST_RESTART
This is like SST_CLEAN_FORCED above, but it reloads GEOS after shutting down rather than exit completely. It takes no additional parameters; it will return TRUE if the system could not be restarted, FALSE if the shutdown has been initiated.
SST_FINAL
Perform the final phase of a shutdown. This routine is called only by the UI when the SST_CLEAN_FORCED shutdown is complete. This type does not return, and it takes one additional parameter. The calling format and parameters of this type are
Boolean SysShutdown(
    SysShutdownType			type,
    const char			* reason);
The reason parameter is a character string explaining the reason (typically an error) for the final shutdown.
SST_SUSPEND
Suspend system operation in preparation for task switching, and broadcast MSG_META_CONFIRM_SHUTDOWN to all objects on the MANUFACTURER_ID_GEOWORKS:GCNSLT_SHUTDOWN_CONTROL GCN list (see MetaClass ). All notified objects must return acknowledgment of the shutdown. This type of SysShutdown() returns true if another system shutdown is already in progress. It takes two additional parameters:
Boolean SysShutdown(
    SysShutdownType			type,
    optr			notificationOD,
    Message			msg);
notificationOD
The optr of an object which will receive the message passed in msg after the shutdown has been acknowledged. Pass a null optr to use the default notification ( MSG_META_DETACH sent to the UI), though this is not usually the intent of the call.
msg
The message to be sent to the object in notificationOD .
SST_CONFIRM_START
Called by the recipient of MSG_META_CONFIRM_SHUTDOWN ; this allows shutdown confirmation dialog boxes to be presented in order to the user. The caller of this type will be blocked until all previous callers have finished their confirmation procedure. When SysShutdown() returns, the caller may present its confirmation dialog and continue or abort the shutdown. If SysShutdown() returns true from a call with this type, the caller should not present the confirmation dialog to the user and need not call SysShutdown() with SST_CONFIRM_END; another thread has already cancelled the shutdown. This type takes no additional parameters.
SST_CONFIRM_END
The counterpart of SST_CONFIRM_START, this ends the confirmation sequence in an object's MSG_META_CONFIRM_SHUTDOWN handler. It takes one additional parameter and returns nothing. The calling format is shown below:
void	SysShutdown(
    SysShutdownType			type,
    Boolean			confirm);
The confirm parameter should be TRUE if the shutdown is to be continued, FALSE if the shutdown should be aborted.

Include: system.h

Warnings: Most applications should not call SysShutdown() . Any that do should do so with extreme care.

SysStatistics()

void	SysStatistics(
        SysStats * stats);			/* returned statistics */

This routine returns system performance statistics. Pass it a pointer to an empty SysStats structure; the routine will fill in the appropriate fields. SysStats has the following structure:

typedef struct {
    dword			SS_idleCount;
    SysSwapInfo			SS_swapOuts;
    SysSwapInfo			SS_swapIns;
    word			SS_contextSwitches;
    word			SS_interrupts;
    word			SS_runQueue;
} SysStats;

Include: sysstats.h

SysUnlockBIOS()

void	SysUnlockBIOS(void);

TakeDownDialogViaUIThread()

void	TakeDownDialogViaUIThread(
    optr		dialogBox );

This will dismiss and destroy a dialog created with the routine PutUpDialogViaUIThread(). It dismisses the dialog by sending it MSG_GEN_GUP_INTERACTION_COMMAND with the parameter IC_DISMISS.

Pass:

dialogBox
Optr of the duplicate object to be dismissed and destroyed. This is returned by the routine PutUpDialogViaUIThread().

Include: foam.goh

See Also: PutUpDialogViaUIThread(), UserDestroyDialog()

| Up: GEOS SDK TechDocs | Up | Prev: SysGetPenMode() ... | Next: TextSearchInString() ...


GEOS C Routines: TextSearchInString() ...

| Up: GEOS SDK TechDocs | Up | Prev: SysShutdown() ... | Next: ThreadAttachToQueue() ...

TextSearchInString()

char *	TextSearchInSTring(
        const char		*str1,
        conat char		*startPtr,
        const char		*endPtr,
        word		strSize,
        const char		*str2,
        word		str2Size,
        word		searchOptions,
        word		*matchLen);

This routine searches in a single text chunk for a passed text string. If a match is found, a pointer to that match (and the length of the match) are returned in passed buffers.

str1 is a pointer to the main string you will be searching in.

startPtr and endPtr are pointers to locations within str1 to begin and end the search.

strSize stores the size of str1 , or zero if null-terminated.

str2 stores the match string, which may include wildcards (type WildCard ).

str2Size stores the size of str2 , or zero if null-terminated.

searchOptions stores the SearchOptions to use by the search mechanism. The high byte should be zeroed.

matchLen stores a buffer to store the size of the matched word. (The matched word itself is returned by the routine.)

Include: Objects/vTextC.goh

TextSearchInHugeArray()

dword	TextSearchInSTring(
        char		*str2,
        word		str2Size,
        dword		str1Size,
        dword		curOffset,
        dword		endOffset,
        FileHandle		hugeArrayFile,
        VMBlockHandle		hugeArrayBlock,
        word		searchOptions,
        word		*matchLen);

This routine searches in a huge array for a passed text string. If a match is found, a dword offset to the match (and the length of the match) are returned in passed buffers.

str2 stores the match string, which may include wildcards (type WildCard ).

str2Size stores the size of str2 , or zero if null-terminated.

str1Size stores the total length of the string being searched.

curOffset stores the offset from the start of str1 to the first character to check.

endOffset stores the offset from the start of str1 to the last character to check.

hugeArrayFile stores the file handle of the huge array.

hugeArrayBlock stores the VM block handle of the huge array.

searchOptions stores the SearchOptions to use by the search mechanism. The high byte should be zeroed.

matchLen stores a buffer to store the size of the matched word. (The matched word itself is returned by the routine.)

Include: Objects/vTextC.goh

TGI_PRIORITY()

byte	TGI_PRIORITY(val);
        word	val;

This macro extracts the thread priority from the value returned by ThreadGetInfo() .

TGI_RECENT_CPU_USAGE()

byte	TGI_RECENT_CPU_USAGE(val);
        word	val;

This macro extracts the recent CPU usage from the value returned by ThreadGetInfo() .

ThreadAllocSem()

SemaphoreHandle ThreadAllocSem(
        word	value);			/* allowable locks on the semaphore */

This routine allocates and initializes a new semaphore for private use by a multithreaded application. Pass the value with which to initialize the semaphore; this value represents the number of threads that can grab the semaphore before other grab attempts will block. Typically, the passed value will be one. The routine returns the handle of the new semaphore.

Include: sem.h

See Also: ThreadPSem(),
ThreadVSem(),
ThreadFreeSem(),
ThreadPTimedSem().

ThreadAllocThreadLock()

ThreadLockHandle ThreadAllocThreadLock();

This routine allocates a special semaphore called a thread lock. With a normal semaphore, a thread that grabs the semaphore twice without releasing it will deadlock; with a thread lock, a thread can grab it more than once in succession. The thread has to release it once for each time it grabs the thread lock, however.

In all other aspects, however, the thread lock resembles a normal semaphore. ThreadAllocThreadLock() returns the handle of the new thread lock.

Include: sem.h

See Also: ThreadGrabThreadLock(), ThreadReleaseThreadLock().

| Up: GEOS SDK TechDocs | Up | Prev: SysShutdown() ... | Next: ThreadAttachToQueue() ...


GEOS C Routines: ThreadAttachToQueue() ...

| Up: GEOS SDK TechDocs | Up | Prev: TextSearchInString() ... | Next: ThreadGetError() ...

ThreadAttachToQueue()

void	ThreadAttachToQueue(
        QueueHandle		qh,				/* queue to attach */
        ClassStruct		* class);				/* primary class of thread */

This routine attaches the calling thread to the passed event queue. This is used only for event-driven threads. Typically, this routine is called when a thread is created; attaching to queues is automatic in nearly all cases, and you will rarely need this routine.

Pass the handle of the queue in qh and a class pointer in class . The class will be attached to the event queue and will handle all messages sent directly to the thread. This class should nearly always be a subclass of ProcessClass .

If a queue handle of zero is passed, the thread wants to "reattach" to the current queue. This is used typically during shutdown of event-driven threads, and it is nearly always taken care of automatically by ProcessClass .

Include: thread.h

ThreadCreate()

ThreadHandle ThreadCreate(
        word	priority,			/* Initial base priority of new thread */
        word	valueToPass,			/* Optional data to pass to new thread */
        word	(*startRoutine)(word valuePassed),
                /* Pointer to entry routine */
        word	stackSize,			/* Size of the stack for the new thread */
        GeodeHandle owner);				/* Geode that will own the new thread */

This routine creates a new procedural thread for a process. If you need a new event-driven thread, send MSG_PROCESS_CREATE_EVENT_THREAD to your process object instead.

Pass the following parameters to this routine:

priority
The priority of the new thread. Typically this will be one of the standard thread priorities (see below).
valueToPass
A word of optional data to be passed to the entry routine of the new thread. This can be used, for example, to indicate the thread's initial context or for initializing thread variables.
startRoutine
A pointer to the entry routine to be executed immediately for the thread. This may be in either fixed or movable memory. The segment must be a virtual segment. Note that if the routine is in movable memory, it may degrade heap performance for the life of the thread (its movable block will remain locked for extended stretches of time). The routine may return the thread's exit code or may call ThreadDestroy() directly.
stackSize
The stack size allocated for the thread. 512 bytes is typically enough for threads doing neither UI nor file system work; threads working with the file system will require 1 K. Threads working with UI objects will require 3 K.
owner
The geode handle of the geode that will own the thread. If the calling thread's geode will own the new thread, it can call GeodeGetProcessHandle() prior to calling ThreadCreate() .

ThreadCreate() returns the thread handle of the new thread. If an error occurs, the calling thread's error code will be set and a null handle returned; you should likely call ThreadGetError() to retrieve the error code after creating the new thread. A return of NO_ERROR_RETURNED from ThreadGetError() means no error occurred.

The standard thread priorities that may be passed in the priority parameter are listed below:

PRIORITY_TIME_CRITICAL
The highest priority of all; you should not use this in general because it will pre-empt nearly all other threads. (It may be useful, however, during debugging.)
PRIORITY_HIGH
A high priority; generally only used for highly important threads.
PRIORITY_UI
Another high priority; this is used for User Interface threads to provide quick response to user actions.
PRIORITY_FOCUS
A medium-level priority; this is used for whatever thread has the current input focus (whichever thread the user is currently working with).
PRIORITY_STANDARD
The standard application thread priority; you should typically use this when creating new threads.
PRIORITY_LOW
A low priority for tasks that can be done in the background.
PRIORITY_LOWEST
The lowest standard priority; it is used for threads that can take any amount of time to complete.

Include: thread.h

ThreadDestroy()

void	ThreadDestroy(
        word	errorCode,	/* Error code to indicate cause of destruction */
        optr	ackObject,	/* Object to receive destruction acknowledgment */
        word	ackData);	/* Additional word of data to pass (as the low
             * word of optr for source of MSG_META_ACK) */

This routine causes the current (calling) thread to exit and then destroy itself. The thread is responsible for ensuring that it has no leftover resources allocated or semaphores locked.

Pass it an error code or exit code meaningful to the application and the other threads in the application. This error code will be used by the debugger to determine the cause of the thread's exit; a null error code usually indicates successful completion of the thread's task.

Pass also the optr of the object to receive acknowledgement of the thread's destruction. The object specified will receive MSG_META_ACK after the calling thread is completely destroyed.

Be Sure To: Always clean up before exiting a thread. Unlock locked resources, free allocated memory, etc. You do not have to do these things for the application's primary thread; the process object (the primary thread) will automatically clean up after itself.

Include: thread.h

ThreadFreeSem()

void	ThreadFreeSem(
        SemaphoreHandle sem);			/* semaphore to be freed */

This routine frees the specified semaphore that had been allocated with ThreadAllocSem() . You must be sure that no threads are using the semaphore or will use it after it has been freed. Subsequent access attempts could cause illegal handle errors or worse.

Include: sem.h

See Also: ThreadAllocSem(),
ThreadPSem(),
ThreadVSem(),
ThreadPTimedSem().

ThreadFreeThreadLock()

void	ThreadFreeThreadLock(
        ThreadLockHandle sem);				/* thread lock to be freed */

This routine frees the specified thread lock that had been allocated with ThreadAllocThreadLock() . You must be sure that no threads are using or will use the thread lock after it has been freed. Subsequent attempts to grab or release the thread lock could cause illegal handle errors.

Include: sem.h

| Up: GEOS SDK TechDocs | Up | Prev: TextSearchInString() ... | Next: ThreadGetError() ...


GEOS C Routines: ThreadGetError() ...

| Up: GEOS SDK TechDocs | Up | Prev: ThreadAttachToQueue() ... | Next: ThreadPSem() ...

ThreadGetError()

word	ThreadGetError(void)

This routine returns the thread's current error value.

ThreadGetInfo()

word	ThreadGetInfo(
        ThreadHandle		th,			/* thread to get information about */
        ThreadGetInfoType 		info);			/* type of information to get */

This routine gets information about the specified thread. The information desired is specified in the info parameter; the subject thread is specified in the th parameter. If the thread handle passed is zero or a null handle, the routine will return information about the calling thread.

The info parameter is one of the following values of ThreadGetInfoType , specifying the type of information to be returned by ThreadGetInfo() :

TGIT_PRIORITY_AND_USAGE
The returned word will contain both the thread's priority and the thread's recent CPU usage. To extract the priority of the thread, use the macro TGI_PRIORITY; to extract the recent CPU usage, use the macro TGI_RECENT_CPU_USAGE.
TGIT_THREAD_HANDLE
Useful only when the th parameter is zero, this will return the thread handle of the subject thread. If th is zero, the handle of the calling thread will be returned.
TGIT_QUEUE_HANDLE
The returned word will contain the queue handle of the event-driven thread specified in th . If the thread specified is not event-driven, a null queue handle will be returned.

Include: thread.h

ThreadGrabThreadLock()

void	ThreadGrabThreadLock(
        ThreadLockHandle sem);				/* thread lock to grab */

This routine attempts to grab the thread lock for the calling thread. If the thread lock is currently held by another thread, the caller will block until the lock becomes available. If the caller already has the thread lock, it will grab the lock again and continue executing.

Be Sure To: Thread locks must be released with ThreadReleaseThreadLock() once for each time they are grabbed.

Warnings: This routine provides no deadlock protection for multiple threads. If multiple threads will be grabbing multiple thread locks, the locks should always be grabbed in the same order to minimize the potential for deadlock.

Include: sem.h

See Also: ThreadAllocThreadLock(), ThreadReleaseThreadLock().

ThreadHandleException()

void	ThreadHandleException(
        ThreadHandle		th,				/* thread to handle the exception */
        ThreadExceptions		exception,				/* exception to handle */
        void	(*handler)	());				/* pointer to handler */

This routine allows a thread to set up a handler for a processor exception. This can be useful for debugging purposes. Pass the following three parameters:

th
The handle of the thread to handle the exception. Pass zero for the current thread.
exception
A ThreadException type (see below).
handler
A pointer to a handler in fixed or locked memory. Pass a null pointer to use the GEOS default exception handler.

Structures: The ThreadException type has the following values:

TE_DIVIDE_BY_ZERO
TE_OVERFLOW
TE_BOUND
TE_FPU_EXCEPTION
TE_SINGLE_STEP
TE_BREAKPOINT

Include: thread.h

ThreadModify()

void	ThreadModify(
        ThreadHandle		th,					/* thread to modify */
        word		newBasePriority,					/* thread's new base priority */
        ThreadModifyFlags		flags);					/* flags (see below) */

This routine modifies the priority of the specified thread. Use it to either set the base priority of the thread or reset the current CPU usage to zero. The parameters should have the following values:

th
The thread handle; pass zero to change the priority of the calling thread.
newBasePriority
The new base priority of the thread. Use one of the standard priorities--see ThreadCreate() --or use a value between zero and 255.
flags
A record of ThreadModifyFlags ; pass TMF_BASE_PRIO to change the thread's base priority or TMF_ZERO_USAGE to reset the thread's recent CPU usage to zero.

Warnings: Unless the thread is timing-critical, you should not set the base priority to zero.

Include: thread.h

ThreadPrivAlloc()

word	ThreadPrivAlloc(
        word		wordsRequested,					/* number of words to allocate */
        GeodeHandle		owner);					/* handle of geode to own data */

This routine allocates a number of contiguous words in the private data of all geodes (loaded and yet-to-be loaded). It is exactly the same as GeodePrivAlloc() ; see the entry for that routine.

Include: thread.h

See Also: GeodePrivAlloc().

ThreadPrivFree()

void	ThreadPrivFree(
        word	range,			/* offset to first word to be freed */
        word	wordsRequested);			/* number of words to free */

This routine frees a number of contiguous private-data words previously allocated with ThreadPrivAlloc() . It is similar to GeodePrivFree() ; see the entry for that routine for full information.

Include: thread.h

See Also: GeodePrivFree().

| Up: GEOS SDK TechDocs | Up | Prev: ThreadAttachToQueue() ... | Next: ThreadPSem() ...


GEOS C Routines: ThreadPSem() ...

| Up: GEOS SDK TechDocs | Up | Prev: ThreadGetError() ... | Next: TimerSleep() ...

ThreadPSem()

SemaphoreError ThreadPSem(
        SemaphoreHandle sem);				/* semaphore to grab */

This routine attempts to grab the passed semaphore via a "P" operation. If the semaphore has already been grabbed, the thread will block until the semaphore becomes available, even if it was grabbed by the same thread.

ThreadPSem() returns an error code of type SemaphoreError . The error code is intended to indicate abnormal return by the previous thread; if the semaphore never becomes available, the thread will block indefinitely and the routine will not return.

Be Sure To: When the thread no longer needs the semaphore, it should release it with ThreadVSem() .

Warnings: This routine provides no deadlock protection. If threads will grab multiple common semaphores, they should always grab/release them in the same order, minimizing the chances for deadlock.

A thread may not try to grab a particular semaphore twice without releasing it in between grabs. The thread will block on itself and will deadlock. If a thread may need to grab the semaphore twice in a row, it should use a thread lock instead (see ThreadAllocThreadLock() for more information).

Include: sem.h

See Also: ThreadAllocSem(),
ThreadVSem(),
ThreadFreeSem(),
ThreadPTimedSem().

ThreadPTimedSem()

SemaphoreError ThreadPTimedSem(
        SemaphoreHandle		sem,				/* semaphore to grab */
        word		timeout);				/* ticks before timeout */

This routine attempts to grab the passed semaphore via a "P" operation. If the semaphore has already been grabbed, the thread will block for at most the number of ticks specified in timeout .

Often timeout is passed as zero to indicate that if the semaphore isn't available right now, the thread will go on with some other action.

Be Sure To: When the thread no longer needs the semaphore, it should release it with ThreadVSem() .

Warnings: This routine provides no deadlock protection. If threads will grab multiple common semaphores, they should always grab/release them in the same order, minimizing the chances for deadlock.

A thread may not try to grab a particular semaphore twice without releasing it in between grabs. The thread will block on itself and will deadlock. If a thread may need to grab the semaphore twice in a row, it should use a thread lock instead, though there is no timeout equivalent for thread locks (see ThreadAllocThreadLock() for more information).

Include: sem.h

See Also: ThreadAllocSem(),
ThreadPSem(),
ThreadVSem(),
ThreadFreeSem().

ThreadReleaseThreadLock()

void	ThreadReleaseThreadLock(
        ThreadLockHandle sem);				/* threadlock to release */

This routine releases the specified thread lock previously grabbed with ThreadGrabThreadLock() . Pass the handle of the thread lock as returned by ThreadAllocThreadLock() .

Do not try to release a thread lock that has not previously been grabbed. The results are unpredictable.

Include: sem.h

See Also: ThreadAllocThreadLock(), ThreadGrabThreadLock().

ThreadVSem()

void	ThreadVSem(
        SemaphoreHandle sem);				/* semaphore to release */

This routine releases a semaphore that was grabbed with ThreadPSem() or ThreadPTimedSem() . Pass the handle of the semaphore as returned by ThreadAllocSem() .

Do not try to release a semaphore that has not previously been grabbed with one of the above routines. The results are unpredictable.

Include: sem.h

See Also: ThreadAllocSem(),
ThreadPSem(),
ThreadFreeSem(),
ThreadPTimedSem().

TimerGetCount()

dword	TimerGetCount();

This routine returns the value of the system counter. The returned value is the number of ticks since GEOS started.

Include: timer.h

TimerGetDateAndTime()

void	TimerGetDateAndTime(
        TimerDateAndTime * dateAndTime);						/* buffer for returned values */

This routine returns the current time and date. Pass it a pointer to an empty TimerDateAndTime structure to be filled in by the routine.

Include: timedate.h

TimerGetFileDateTime()

TimerFileDateTime TimerGetFileDateTime();

This routine returns the current time and date in a structure equivalent to a FileDateAndTime . As such, it is useful for constructing time stamps for files.

TimerSetDateAndTime()

void	TimerSetDateAndTime(
        word			flags,					/* which item to set */
        const TimerDateAndTime			* dateAndTime);					/* new values */

This routine sets the current date and/or time of the system. Pass it the following:

flags
A word of flags. Pass TIME_SET_DATE to set the day, month, and year; pass TIME_SET_TIME to set the hour, minute, and second. Pass both to set both.
dateAndTime
A pointer to a TimerDateAndTime structure containing the information to be set.

Include: timedate.h

| Up: GEOS SDK TechDocs | Up | Prev: ThreadGetError() ... | Next: TimerSleep() ...


GEOS C Routines: TimerSleep() ...

| Up: GEOS SDK TechDocs | Up | Prev: ThreadPSem() ... | Next: TocNameArrayFind() ...

TimerSleep()

void	TimerSleep(
        word	ticks);		/* number of ticks the thread should sleep */

This routine invokes a "sleep timer" that will put the calling thread to sleep for the given number of ticks. At the end of the time, the thread will continue executing with the next instruction.

Warnings: Do not use sleep timers as a substitute for semaphores for thread synchronization.

Include: timer.h

:

TimerStart()

TimerHandle TimerStart(
        TimerType		timerType,				/* type of timer to start */
        optr		destObject,				/* object to receive notification
                         * message when timer expires */
        word		ticks,				/* amount of time to run */
        Message		msg,				/* notification message */
        word		interval,				/* interval for continual timers */
        word		* id);				/* buffer for returned timer ID */

This routine starts a timer of any type. The timer will run for the specified number of ticks and then will send the given message to the destination object. The message is sent with the flags MF_FORCE_QUEUE, MF_CHECK_DUPLICATE and MF_REPLACE, so it will always be put in the recipient's queue and will always replace any duplicates already in the queue. Pass this routine the following:

timerType
A value of TimerType indicating the type of timer to start.
destObject
The optr of the object that will be sent the specified message when the time is up.
ticks
The number of ticks for the timer to run. (Sixty ticks equals one second.) For continual timers, this is the amount of time before the first timeout.
msg
The message to be sent to the destination object when time is up.
interval
For continual timers, the interval (number of ticks) at which to send out the message to the destination object. The timer will send the message once at the end of each interval. The first message will be sent ticks ticks after the timer is started. The second message will be sent interval ticks after that.
id
A pointer to a word in which the timer's ID will be returned. You will need this ID for TimerStop() .

This routine returns the handle of the timer as well as an ID pointed to by the id parameter. You will need the handle and the ID for TimerStop() .

Structures:

TimerType The TimerType enumerated type defines what type of timer should be initiated. It has the following values:

TIMER_ROUTINE_ONE_SHOT
Start a timer that will call a routine and then free itself when the time is expired. This type is supported in assembly but not in C.
TIMER_ROUTINE_CONTINUAL
Start a timer that will call a routine once per time interval (starting after ticks ticks) until TimerStop() is called.
TIMER_EVENT_ONE_SHOT
Start a timer that will send a message to a given object, then free itself when time is expired.
TIMER_EVENT_CONTINUAL
Start a timer that will send a message to a given object once per time interval (starting after ticks ticks) until TimerStop() is called.
TIMER_MS_ROUTINE_ONE_SHOT
Start a timer that has millisecond accuracy. For this timer, the number of ticks will actually be the number of milliseconds. The timer will call a specified routine and then free itself when time is expired. This type is supported in assembly but not in C.
TIMER_EVENT_REAL_TIME
Start a timer that will call a routine at some particular date and time. On devices that support such a timer, this event will wake a sleeping machine.

Include: timer.h

TimerStop()

Boolean	TimerStop(
        TimerHandle		th,			/* handle of timer to be stopped */
        word		id);			/* timer ID (returned by TimerStart() */

This routine stops and destroys a timer that had been started with TimerStart() . Pass it the timer handle and the ID as returned by that routine (the ID of a continual timer will always be zero).

The returned error flag will be true if the timer could not be found.

Warnings: If you call TimerStop() to stop a continual timer that sends its message across threads, there may be timer events left in the recipient's event queue. It is unsafe in this situation to assume that all timer events have been handled. To ensure the timer message has been handled, you can send the destination an "all-safe" message with the MF_FORCE_QUEUE flag.

Include: timer.h

TocDBLock()

void * TocDBLock(
        DBGroupAndItem 		thing);

Use this routine to lock a name array maintained by a PrefTocList object.

Include: config.goh

TocDBLockGetRef()

void * TocDBLockGetRef(
        DBGroupAndItem 		thing,
        optr		*refPtr);

This routine locks a name array maintained by a PrefTocList object, returning the item's pointer and optr.

Include: config.goh

TocFindCategory()

Boolean TocFindCategory(
        TocCategoryStruct 		*cat);

This routine searches a PrefTocList object's name lists for a given token.

Structures:

        typedef struct {
            TokenChars 		TCS_tokenChars;
            DBGroupAndItem 		TCS_files; 					/* file name array */
            DBGroupAndItem 		TCS_devices; 					/* device name array--only if
                                         * TCF_EXTENDED_DEVICE_DRIVERS
                                         * is set. */
        } TocCategoryStruct;

Include: config.goh

TocGetFileHandle()

word TocGetFileHandle();

Use this routine to get the handle of the file used by PrefTocLists to store their name array data.

Include: config.goh

TocNameArrayAdd()

word TocNameArrayAdd(
        DBGroupAndItem 		array,
        const char 		*nameToFind,
        const void 		*data);

Use this routine to add a name to a name array maintained by a PrefTocList object.

Include: config.h

| Up: GEOS SDK TechDocs | Up | Prev: ThreadPSem() ... | Next: TocNameArrayFind() ...


GEOS C Routines: TocNameArrayFind() ...

| Up: GEOS SDK TechDocs | Up | Prev: TimerSleep() ... | Next: TokenDefineToken() ...

TocNameArrayFind()

word TocNameArrayGetElement(
        DBGroupAndItem 		array,
        word 		element,
        void 		*buffer);

Use this routine to find a name in the name list maintained by a PrefTocList object.

Include: config.goh

TocNameArrayGetElement()

word TocNameArrayGetElement(
        DBGroupAndItem 		array,
        word 		element,
        void 		*buffer);

Use this routine to retrieve a given element from a name array maintained by a PrefTocList object.

Include: config.goh

TocSortedNameArrayAdd()

word TocSortedNameArrayAdd(
        word 		arr,
        const char 		*nameToAdd,
        NameArrayAddFlags 		flags,
        const void 		*data);

This routine adds a name to a sorted name array associated with a PrefTocList object.

Structures:

        typedef WordFlags NameArrayAddFlags;
        #define NAAF_SET_DATA_ON_REPLACE 0x8000

Include: config.goh

TocSortedNameArrayFind()

Boolean TocSortedNameArrayFind(
        word 				arr,
        const char 				*nameToFind,
        SortedNameArrayFindFlags 				flags,
        void 				*buffer,
        word 				*elementNum);

This routine looks up a name in a sorted name array associated with a PrefTocList object.

Structures:

        typedef WordFlags SortedNameArrayFindFlags;
        #define SNAFF_IGNORE_CASE 0x0080

Include: config.goh

TocUpdateCategory()

void TocUpdateCategory(
        TocUpdateCategoryParams *params);

Use this routine to update a PrefTocList object based upon the files in a given directory with a given token.

Structures:

        typedef struct {
            TocUpdateCategoryFlags 				TUCP_flags;
            TokenChars 				TUCP_tokenChars;
            byte 				TUCP_fileArrayElementSize;

            TocUpdateAddCallback 				*TUCP_addCallback;
            byte 				TUCP_pad; /* Wants to be word-aligned */
        } TocUpdateCategoryParams;

        typedef word _pascal TocUpdateAddCallback(
            const char *filename,
            optr chunkArray);
        /* Return 0 if add aborted, else return offset of new element within
         * block */

Include: config.goh

TOKEN_CHARS()

dword	TOKEN_CHARS(a, b, c, d)

This macro creates a single dword value from four given characters. This is useful when creating a token characters value for a specific token. The first two characters passed may be chars, the second two should be cast to dwords. A simple example:

TOKEN_CHARS(`S', `A', (dword) `M', (dword) `P');

TokenCloseLocalTokenDB()

void	TokenCloseLocalTokenDB()

This routine closes the local token database.

| Up: GEOS SDK TechDocs | Up | Prev: TimerSleep() ... | Next: TokenDefineToken() ...


GEOS C Routines: TokenDefineToken() ...

| Up: GEOS SDK TechDocs | Up | Prev: TocNameArrayFind() ... | Next: TokenLoadMonikerBuffer() ...

TokenDefineToken()

word	TokenDefineToken(
        dword		tokenChars,					/* four token characters */
        ManufacturerID		manufacturerID,					/* manufacturer ID for token */
        optr		monikerList,					/* optr of moniker list */
        TokenFlags		flags);					/* token flags */

This routine adds a new token and moniker list to the token database. If the token already exists in the token DB, the old will be replaced with the new. This routine must only be called by a thread that can lock the block in which the passed moniker or moniker list resides. This routine must be passed the following parameters:

tokenChars
The four token characters that identify this moniker or moniker list in the token database. Create this dword value from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
monikerList
The optr of the moniker list to be added to the token database.
flags
A record of TokenFlags indicating the relocation status of the moniker list.

Warnings: This routine may legally move locked LMem blocks (token database items), thereby invalidating all pointers to token database items.

Include: token.h

TokenGetTokenInfo()

Boolean	TokenGetTokenInfo(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        TokenFlags		* flags);					/* returned token flags */

This routine returns information about a specified token. Pass it the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
flags
A pointer to an empty flags record; the flags set (if any) for the specified token (if it exists) will be returned here.

This routine returns a non-zero value if the token was found in the token database. It returns zero if the token was not found.

Include: token.h

TokenListTokens()

dword	TokenListTokens(
        TokenRangeFlags 		tokenRangeFlags,
        word 		headerSize,
        ManufacturerID 		manufacturerID));

This routine lists all the tokens in the token database. It allocates a new block on the global heap and writes in it an array of GeodeToken structures. This routine returns the actual tokens, not the token groups.

The returned dword consists of two values: The high word represents the number of tokens in the returned block and may be extracted with the macro TokenListTokensCountFromDWord() . The low word represents the handle of the newly-allocated block and can be extracted with the macro TokenListTokensHandleFromDWord() .

Include: token.h

TokenListTokensCountFromDWord()

word	TokenListTokensCountFromDWord(d);
        dword	d;

This macro extracts the number of tokens from the value returned by TokenListTokens() .

TokenListTokensHandleFromDWord()

word	TokenListTokensHandleFromDWord(d);
        dword	d;

This routine extracts the MemHandle from the value returned by TokenListTokens() .

TokenLoadMonikerBlock()

Boolean	TokenLoadMonikerBlock(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        DisplayType		displayType,					/* type of display for token */
        VisMonikerSearchFlags searchFlags,							/* flags for finding token */
        word		* blockSize,					/* returned block size */
        MemHandle		* blockHandle);					/* returned block handle */

This routine loads a specified token's moniker, allocating a new global memory block for the moniker. The returned Boolean will be false if the moniker was found, true otherwise. Information about the moniker is returned in the values pointed to by blockSize (the size of the newly allocated block) and blockHandle (the handle of the new block). If the moniker is not found, both return pointers will be NULL and no block will be allocated.

Pass this routine the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
displayType
A value of DisplayType indicating the size of the display (used to indicate small-screen devices, primarily).
searchFlags
A record of VisMonikerSearchFlags indicating what type of moniker is being requested.
blockSize
A pointer to a word in which the new block's size will be returned.
blockHandle
A pointer to a handle in which the new block's handle will be returned.

Include: token.h

| Up: GEOS SDK TechDocs | Up | Prev: TocNameArrayFind() ... | Next: TokenLoadMonikerBuffer() ...


GEOS C Routines: TokenLoadMonikerBuffer() ...

| Up: GEOS SDK TechDocs | Up | Prev: TokenDefineToken() ... | Next: TokenLoadTokenChunk() ...

TokenLoadMonikerBuffer()

Boolean	TokenLoadMonikerBuffer(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        DisplayType		displayType,					/* type of display for token */
        VisMonikerSearchFlags searchFlags,							/* flags for finding token */
        void		* buffer,					/* pointer to buffer for token */
        word		bufSize,					/* size of passed buffer */
        word		* bytesReturned);						/* number of bytes returned */

This routine loads a specified token's moniker into a provided buffer. The return value will be false if the moniker was found, true otherwise. The size of the returned moniker will be returned in the word pointed to by the bytesReturned parameter.

Pass this routine the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
displayType
A value of DisplayType indicating the size of the display (used to indicate small-screen devices, primarily).
searchFlags
A record of VisMonikerSearchFlags indicating what type of moniker is being requested.
buffer
A pointer to a locked or fixed buffer into which the moniker will be copied.
bufSize
The size of the passed buffer; also the maximum size of the moniker that may be returned.
bytesReturned
The size of the moniker actually returned in the buffer.

Include: token.h

TokenLoadMonikerChunk()

Boolean	TokenLoadMonikerChunk(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        DisplayType		displayType,					/* type of display for token */
        VisMonikerSearchFlags searchFlags,							/* flags for finding token */
        MemHandle		lmemBlock,					/* locked block for new chunk */
        word		* chunkSize,					/* returned new chunk size */
        ChunkHandle		* chunkHandle);					/* returned new chunk handle */

This routine loads a specified token's moniker, allocating a new chunk in a local memory block for the moniker. The returned error flag will be true if the moniker was not found, false otherwise.

Pass this routine the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
displayType
A value of DisplayType indicating the size of the display (used to indicate small-screen devices, primarily).
searchFlags
A record of VisMonikerSearchFlags indicating what type of moniker is being requested.
lmemBlock
The MemHandle of the local memory block in which the new chunk will be allocated. If the block is locked, you must dereference the global handle after calling this routine.
chunkSize
A pointer to a word in which the size of the allocated chunk will be returned.
chunkhandle
A pointer to a chunk handle in which the handle of the newly allocated chunk will be returned.

Warnings: This routine can move chunks in the passed block, thereby invalidating pointers to any chunk in the block.

Include: token.h

TokenLoadTokenBlock()

Boolean	TokenLoadTokenBlock(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        word		* blockSize,					/* returned size of new block */
        MemHandle		* blockHandle);					/* returned handle of block */

This routine loads the specified token's TokenEntry structure into a newly-allocated global memory block. If the token is not found, the returned error flag will be true ; otherwise, it will be false .

Pass this routine the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
blockSize
A pointer to a word in which the size of the newly-allocated block will be returned.
blockHandle
A pointer to a global handle in which the handle of the newly-allocated block will be returned.

Include: token.h

TokenLoadTokenBuffer()

Boolean	TokenLoadTokenBuffer(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        TokenEntry		* buffer);					/* buffer for returned token */

This routine loads the specified token's TokenEntry structure into a passed buffer. The returned error flag will be true if the token was not found, false otherwise. Pass this routine the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
buffer
A pointer to a locked or fixed buffer into which the token entry will be copied.

Include: token.h

| Up: GEOS SDK TechDocs | Up | Prev: TokenDefineToken() ... | Next: TokenLoadTokenChunk() ...


GEOS C Routines: TokenLoadTokenChunk() ...

| Up: GEOS SDK TechDocs | Up | Prev: TokenLoadMonikerBuffer() ... | Next: UserAllocObjBlock ...

TokenLoadTokenChunk()

Boolean	TokenLoadTokenChunk(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        MemHandle		lmemBlock,					/* handle of block for chunk */
        word		* chunkSize,					/* returned size of new chunk */
        ChunkHandle		* chunkHandle);					/* returned chunk handle */

This routine loads the specified token's TokenEntry structure into a newly-allocated chunk. The returned error flag will be true if the token could not be found, false otherwise.

Pass this routine the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
lmemBlock
The MemHandle of the local memory block in which the new chunk will be allocated. If the block is locked, you must manually dereference this handle after the routine call.
chunksize
A pointer to a word in which the size of the newly-allocated chunk will be returned.
chunkHandle
A pointer to a chunk handle in which the handle of the newly-allocated chunk will be returned.

Warnings: This routine can move chunks in the passed block, thereby invalidating pointers to any chunk in the block.

Include: token.h

TokenLockTokenMoniker()

void	* TokenLockTokenMoniker(
        TokenMonikerInfo		tokenMonikerInfo);				/* The DB group and item numbers
                         * as returned by TokenLookupMoniker() */

This routine locks a token's moniker so it may be drawn; it returns a pointer to the locked chunk containing the moniker information. Pass it the structure returned by TokenLookupMoniker() .

Be Sure To: Unlock the moniker with TokenUnlockTokenMoniker() after you have finished drawing it.

Include: token.h

TokenLookupMoniker()

Boolean	TokenLookupMoniker(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        DisplayType		displayType,					/* display type of token */
        VisMonikerSearchFlags searchFlags,							/* flags for finding token */
        TokenMonikerInfo *		tokenMonikerInfo);					/* DB group and item of token */

This routine finds and retrieves a pointer to the specific moniker for the specified token, given also the token's display type and other attributes. Pass the following:

tokenChars
The four token characters that identify this moniker or moniker list in the token database. Create this dword value from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
displayType
A value of DisplayType indicating the size of the display (used to indicate small-screen devices, primarily).
searchFlags
A record of VisMonikerSearchFlags indicating what type of moniker is being requested.
tokenDBItem
A pointer to an empty TokenMonikerInfo structure, in which the token's group and item numbers will be returned.

The return value is an error flag: it will be true if the item could not be found in the token database, false otherwise.

Include: token.h

TokenOpenLocalTokenDB()

word	TokenOpenLocalTokenDB()

This routine opens the local token database. It returns zero on success, and a VMStatus error code on failure.

Include: token.h

TokenRemoveToken

Boolean	TokenRemoveToken(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */

This routine removes the specified token and its moniker list from the token database. It returns an error flag: if the token could not be found, the returned flag is true ; otherwise it is false . Pass the following:

tokenChars
The four token characters that identify this moniker or moniker list in the token database. Create this dword value from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.

Include: token.h

TokenUnlockTokenMoniker()

void	TokenUnlockTokenMoniker(
        void * moniker);

This routine unlocks a moniker that had been locked with TokenLockTokenMoniker() . Pass a pointer to the locked moniker, as returned by the locking routine.

Include: token.h

TypeFromFormatID()

word	TypeFromFormatID(id);
        ClipboardItemFormatID id;

This macro extracts the word-sized format ID (of type ClipboardItemFormat ) from a ClipboardFormatID argument.

| Up: GEOS SDK TechDocs | Up | Prev: TokenLoadMonikerBuffer() ... | Next: UserAllocObjBlock ...


GEOS C Routines: UserAllocObjBlock ...

| Up: GEOS SDK TechDocs | Up | Prev: TokenLoadTokenChunk() ... | Next: UserRemoveAutoExec() ...

UserAllocObjBlock

MemHandle 	UserAllocObjBlock(
        ThreadHandle 		threadHandle);

This routine allocates a block of memory on the heap to be used for holding UI objects. It returns the handle of the block. It is passed one argument:

threadHandle
Handle of the thread which is to manage the block. Pass zero to use the current thread.

Include: ui.goh

UserAddAutoExec()

void	UserAddAutoExec(
        const char *		appName);

This routine adds an application to the list of those, like Welcome, that are automatically started by the UI when it loads. The application name will be added to the appropriate place in the GEOS.INI file. It is passed one argument:

appName
This is a pointer to a null-terminated string containing the name of the application. The application must be in SP_APPLICATION or SP_SYS_APPLICATION.

Include: ui.goh

UserCreateDialog()

optr	UserCreateDialog(
        optr	dialogBox);

This routine duplicates a template dialog box, attaches the dialog box to an application object, and sets it fully GS_USABLE so that it may be called with UserDoDialog() . Dialog boxes created in such a manner should be removed and destroyed with UserDestroyDialog() when no longer needed.

dialogBox
Optr to template dialog box (within a template object block). The block must be sharable, read-only and the top GenInteraction called with this routine must not be linked into any generic tree. The optr returned is a created, fully-usable dialog box.

See Also: UserDestroyDialog().

UserCreateInkDestinationInfo()

MemHandle 	UserCreateInkDestinationInfo(
        optr 		dest,
        GStateHandle 		gs,
        word		brushSize,
        GestureCallback		*callback);

This routine creates an InkDestinationInfo structure to be returned with MSG_META_QUERY_IF_PRESS_IS_INK. The callback routine must be declared _pascal.

Include: ui.goh

Structures:

        typedef Boolean _pascal GestureCallback (
            Point *arrayOfInkPoints,
            word numPoints,
            word numStrokes);

UserDestroyDialog()

void	UserDestroyDialog(
        optr	dialogBox);

This routine destroys the passed dialog box, usually created with UserCreateDialog() . This routine may only be used to destroy dialog boxes occupying a single block; the block must also hold nothing other than the dialog box to be destroyed. It is for this reason that it is wise to only use this routine to destroy dialogs created with UserCreateDialog() .

See Also: UserCreateDialog().

UserDoDialog()

InteractionCommand UserDoDialog(
        optr	dialogBox);

UserDoDialog() brings a pre-instantiated dialog box on-screen, blocking the calling thread until the user responds to the dialog. You must pass the optr of a GIV_DIALOG Interaction that is set both GIA_INITIATED_VIA_USER_DO_DIALOG and GIA_MODAL.

This routine returns the InteractionCommand of the particular response trigger selected by the user. This InteractionCommand may be either a predefined type (such as IC_YES) or a custom one defined using IC_CUSTOM_START.

The pre-defined InteractionCommand s are:

IC_NULL
IC_DISMISS
IC_APPLY
IC_RESET
IC_OK
IC_YES
IC_NO
IC_STOP
IC_EXIT
IC_HELP
IC_INTERACTION_COMPLETE

This routine may return IC_NULL for those cases in which a system shutdown causes the dialog to be dismissed before the user has entered a response.

Warnings: This routine blocks the calling thread until the dialog box receives a MSG_GEN_GUP_INTERACTION_COMMAND. Since the application thread is blocked, it cannot be responsible for sending this message or for handling messages from the response triggers.

See Also: UserStandardDialog(), UserStandardDialogOptr().

UserGetInterfaceLevel()

UIInterfaceLevel UserGetInterfaceLevel(void)

This routine returns the current UIInterfaceLevel . This is a word-sized enumerated type. It has the following values:

UIIL_NOVICE
UIIL_BEGINNING_INTERMEDIATE
UIIL_ADVANCED_INTERMEDIATE
UIIL_ADVANCED
UIIL_GURU

Include: ui.goh

UserLoadApplication()

extern GeodeHandle UserLoadApplication(
        AppLaunchFlags 		alf,
        Message 		attachMethod,
        MemHandle 		appLaunchBlock,
        char 		*filename,
        StandardPath 		sPath,
        GeodeLoadError 		*err);

Loads an application. Changes to standard application directory before attempting GeodeLoad on filename passed. Stores the filename being launched into the AppLaunchBlock , so that information needed to restore this application instance will be around later if needed.

| Up: GEOS SDK TechDocs | Up | Prev: TokenLoadTokenChunk() ... | Next: UserRemoveAutoExec() ...


GEOS C Routines: UserRemoveAutoExec() ...

| Up: GEOS SDK TechDocs | Up | Prev: UserAllocObjBlock ... | Next: UtilAsciiToHex32() ...

UserRemoveAutoExec()

void	UserRemoveAutoExec(
        const char *		appName);

This routine removes an application from the list of those to be launched on start-up. It is passed one argument:

appName
This is a pointer to a null-terminated string containing the name of the application.

Include: ui.goh

UserStandardDialog()

word	UserStandardDialog(
        char *			helpContext,
        char *			customTriggers,
        char *			arg2,
        char *			arg1,
        char *			string,
        CustomDialogBoxFlags 	dialogFlags);

UserStandardDialog() creates and displays either a custom dialog box or one of several pre-defined standard dialog boxes.

Most often, you will use this routine to create a custom dialog box that conforms to a standardized dialog. In this case, pass the CustomDialogType of SDBT_CUSTOM as the routine's final argument. You must then supply other parameters to create the custom dialog box.

If instead you wish to use one of the pre-defined CustomDialogType types, you should pass that type as the final argument to this routine. Some of these standard types require you to pass string parameters. Other arguments should be passed as null.

For custom dialog boxes you must pass a CustomDialogType (CDT_WARNING, CDT_NOTIFICATION, CDT_QUESTION, or CDT_ERROR). This chooses the proper icon glyph to display within the dialog box. (For example, a CDT_WARNING dialog might contain a large exclamation-point glyph.) Make sure that you use CDBF_DIALOG_TYPE_OFFSET to pass this value. E.g.:

(GIT_AFFIRMATION << CDBF_INTERACTION_TYPE_OFFSET) | (CDT_WARNING << CDBF_DIALOG_TYPE_OFFSET)

You should also pass a valid GenInteractionType . In most cases, this will be either GIT_NOTIFICATION, GIT_AFFIRMATION, or GIT_MULTIPLE_RESPONSE. Make sure that you use CDBF_INTERACTION_TYPE_OFFSET to pass this value. E.g.:

(GIT_AFFIRMATION << CDBF_INTERACTION_TYPE_OFFSET) | (CDT_WARNING << CDBF_DIALOG_TYPE_OFFSET)

Also pass the routine a string to display to the user. This string may be either text or graphics based. The arg1 and arg2 strings may be used in conjunction with this string; if string contains any ctrl-A or ctrl-B characters (ASCII value 1 and 2, respectively), then the string pointed to by arg1 or arg2 will be inserted in that place.

If the CustomDialogType is GIT_MULTIPLE_RESPONSE, you must also set up a Response Trigger Table with several trigger parameters. The customTrigger argument should be a pointer to a structure containing these parameters. Which type of structure depends on how many triggers there are: StandardDialog1ResponseTriggerTable , StandardDialog2ResponseTriggerTable , StandardDialog3ResponseTriggerTable , StandardDialog4ResponseTriggerTable . In each case, you will need to fill in fields specifying how many triggers there are and a moniker and response value for each trigger.

The helpContext value is used by the help system; this is the "context" name of the page of the geode's help to go to when the user asks for help with this dialog. A null pointer signals that the default help context should be used.

This routine returns an InteractionCommand value. It will be IC_NULL if the interaction was terminated by the system (such as in a system shutdown).

UserStandardDialogOptr()

word	UserStandardDialogOptr(
        char *		helpContext,
        void *		customTriggers,
        optr		arg2,
        optr		arg1,
        optr		string
        CustomDialogBoxFlags		dialogFlags);

UserStandardDialogOptr() provides the same functionality as UserStandardDialog() except that optrs to strings and string parameters are passed instead of fptrs. This is useful for localized strings in resource blocks.

See Also: UserStandardDialog(), UserDoDialog().

UserStandardSound()

word	UserStandardSound(
        StandardSoundType		type,
        ...);

This routine plays a simple sequence of notes. It can be used to play a standard system sound, a single custom tone, or a sequence of tones.

The routine takes a variable number of arguments. The first argument is a member of the StandardSoundType enumerated type. This argument specifies what kind of tone or tones will be played. Depending on the StandardSoundType passed, zero, one, or two additional arguments may be needed. StandardSoundType contains the following members:

SST_ERROR
This is the sound played when an "Error" dialog comes up. No further arguments are needed.
SST_WARNING
This is a general warning sound. No further arguments are needed.
SST_NOTIFY
This is a general notification sound. No further arguments are needed.
SST_NO_INPUT
This is the sound played when a user's input is not going anywhere (e.g. when he clicks the mouse outside a modal dialog box).
SST_KEY_CLICK
This is the sound produced when the keyboard is pressed, or when the user clicks on a floating keyboard. No further arguments are required.
SST_CUSTOM_SOUND
Play a custom sampled sound. This requires one more argument, the memory handle of the sound to be played.
SST_CUSTOM_BUFFER
Play a custom buffer of instrumental sound. This requires one further argument, a pointer to the memory block containing the sound buffer. Note that the "tempo" value used to play this buffer will be one tick per thirty-second note, probably much faster than you would otherwise expect.
SST_CUSTOM_NOTE
By passing this argument, you can have a single custom note played. You must provide one further argument, the handle of the note (such as returned by SoundAllocMusicNote() ).

The function returns a word-length token value. This value is useful for stopping sounds with the UserStopStandardSound() routine.

UserStopStandardSound()

word	UserStandardSound(
        StandardSoundType		type,
        word				countID);

This routine stops a sound that was started by UserStandardSound.

The sound to stop is identified by its type and by the word-length token value returned by UserStandardSound.

This function will not stop SST_CUSTOM_... sounds.

| Up: GEOS SDK TechDocs | Up | Prev: UserAllocObjBlock ... | Next: UtilAsciiToHex32() ...


GEOS C Routines: UtilAsciiToHex32() ...

| Up: GEOS SDK TechDocs | Up | Prev: UserRemoveAutoExec() ... | Next: VirtualSerialSetFormat() ...

UtilAsciiToHex32()

Boolean	UtilAsciiToHex32(
        const char *		string,
        dword *		value);

This routine converts a null-terminated ASCII string into a 32-bit integer. The string may begin with a hyphen, indicating a negative number. Aside from that, the string may contain nothing but numerals until the null termination. It may not contain whitespace.

If the routine is successful, it will return false and write an equivalent signed long integer to *value . If it fails, it will return true and write a member of the UtilAsciiToHexError enumerated type to *value . This type contains the following members:

UATH_NON_NUMERIC_DIGIT_IN_STRING
This string contained a non-numeric character before the trailing null (other than the allowed leading hyphen).
UATH_CONVERT_OVERFLOW
The string specified a number to large to be expressed as a signed 32-bit integer.

Include: system.h

UtilHex32ToAscii()

word	UtilHex32ToAscii(
        char *		buffer,
        dword		value,
        UtilHexToAsciiFlags 		flags);

This routine converts a 32-bit unsigned integer to its ASCII representation and writes it to the specified buffer. It returns the length of the string (not counting the nulll termination, if any). The routine is passed the following arguments:

buffer
This is a pointer to a character buffer. The buffer must be long enough to accommodate the largest string; that is, there must be ten bytes for the characters, plus one for the trailing null (if necessary).
value
This is the value to convert to ASCII.
flags
This is a record of UtilHexToAscii flags. The following flags are available:
UHTAF_INCLUDE_LEADING_ZEROS
Pad the string with leading zeros to a length of ten total characters.
UHTAF_NULL_TERMINATE
Add a null to the end of the string. If this flag is set, the buffer must be at least 11 bytes long. If it is clear, the buffer may be ten bytes long.

Include: system.h

VarDataFlagsPtr()

VarDataFlags 	VarDataFlagsPtr(
        void *	ptr);

This macro fetches the flags of a variable data type when given a pointer to the extra data for the type. The flags are stored in a VarDataFlags record. Only the flags VDF_EXTRA_DATA and/or VDF_SAVE_TO_STATE will be returned.

Include: object.h

Warnings: You must pass a pointer to the beginning of the vardata entry's extra data space.

VarDataSizePtr()

word	VarDataSizePtr(
        void *	ptr);

This macro fetches the size of a variable data entry when given a pointer to the extra data for the type.

Include: object.h

Warnings: You must pass a pointer to the beginning of the vardata entry's extra data space.

VarDataTypePtr()

word	VarDataTypePtr(
        void *	ptr);

This macro fetches the type of a variable data entry when given a pointer to the extra data of the entry. The type is stored in a VarDataFlags record. All flags outside the VDF_TYPE section will be cleared.

Include: object.h

Warning: You must pass a pointer to the beginning of the vardata entry's extra data space.

VirtualSerialClose()

VirtualSerialStatus 	VirtualSerialClose ( void  );

Waits until all outgoing data is sent to remote unit, then flushes incoming data buffer and closes connection to remote unit.

Returns VS_OK if data was successfully written.

Returns VS_CLOSED if stream was already closed.

Warning: This is a low-level routine whose use is discouraged in favor of ModemClose(). Do not call any Virtual Serial function except VirtualSerialOpen() after calling this function.

Include: vserlib.h

See Also: VirtualSerialOpen().

VirtualSerialFlush()

VirtualSerialStatus 	VirtualSerialFlush(
     word 		flushMode);

Flushes data from incoming buffer. The flushMode sets the operation direction. It may be:

STREAM_READ
Flushes the incoming data buffer.
STREAM_WRITE
Is ignored.
STREAM_BOTH
Same as STREAM_READ.

Returns VS_OK if the stream successfully flushed.

Returns VS_CLOSED if the stream is not open.

Include: vserlib.h

VirtualSerialOpen()

VirtualSerialStatus	 VirtualSerialOpen(
    VSOpenFlags 		stream_open_mode,    /* Fail or wait if stream is busy */
    word 		input_buffer_size,    /* # of bytes 1...4096 of input buffer */
    word 		output_buffer_size,    /* Ignore. Automatically set to 4352 bytes. */
    word 		timeout);    /* # of ticks to wait and retry if the stream is busy. */

This routine lets the client open a stream to the phone. Should the Virtual Phone data resource be in use and if stream_open_mode is set to SOF_NOBLOCK then the function fails immediately; otherwise, if stream_open_mode is set to SOF_TIMEOUT, the function will try again until it is either successful or timeout number of ticks (1 ... 32768) elapse. input_buffer_size is the size (in bytes 1...4096) of the input buffer. The output_buffer_size should be ignored, as the output buffer is set to 4352 bytes.

Returns VS_OK if successful.

Returns VS_DATA_RESOURCE_RESERVED if VP data resource is already in use.

Returns VS_STREAM_ALREADY_OPEN if stream has already been opened by another client.

Returns VS_CANNOT_ALLOC if cannot allocate memory for the streams buffer.

Warning: This is a low-level routine whose use is discouraged in favor of ModemOpen(). This routine must return VS_OK before client can start using other Virtual Serial functions. Outgoing data is not buffered in VirtualSerial library, but sent directly from calling client's buffer.

Include: vserlib.h

See Also: VirtualSerialClose().

VirtualSerialRead()

VirtualSerialStatus 	VirtualSerialRead(
        VSBlockingMode		blocking_mode, /* to block or not to block */
        word		data_size, /* # of bytes of data to be read */
        byte		*ptr_to_buffer, /* buffer to hold data */
        word		*bytes_read); /* # of bytes actually read */

Copies incoming data to buffer. If there is less than data_size number of bytes in the buffer and STREAM_BLOCK was specified in blocking_mode, will block until enough data becomes available; if there is less than data_size number of bytes in the buffer and STREAM_NO_BLOCK was specified, returns immediately with VS_SHORT_READ_WRITE.

Returns VS_OK if data was successfully written.

Returns VS_CLOSED if stream is not open.

Include: vserlib.h

See Also: VirtualSerialReadByte().

VirtualSerialReadByte()

VirtualSerialStatus 	VirtualSerialReadByte(
        VSBlockingMode		blockingMode, /* to block or not to block */
        byte		*dataByte); /* buffer to hold read data */

This routine reads a byte from the stream into dataByte. If the stream is empty and blockingMode is set to STREAM_BLOCK then the routine will wait for the request to be filled, or if STREAM_NO_BLOCK is specified the routine will immediately return an error.

Returns VS_OK if data was successfully written to dataByte.

Returns VS_SHORT_READ_WRITE if the data was not available and STREAM_NO_BLOCK was specified.

Returns VS_CLOSED if the stream is not open.

Warning: STREAM_BLOCK mode is dangerous to use: if the opposite end does not send data this function will block forever.

Include: vserlib.h

See Also: VirtualSerialRead(), VirtualSerialWriteByte().

VirtualSerialSendDTR()

VirtualSerialStatus 	VirtualSendDTR(
        byte		DTR_status);

Sends Data Terminal Ready status to modem.

Include: vserlib.h

VirtualSerialSetFlowControl()

VirtualSerialStatus 	VirtualSerialSetFlowControl(
        VSFlowControl		flow_control_mode); /* software or hardware */

Selects the flow control method used between the modem and the VirtualSerial Library. flow_control_mode can be set to either VSFC_SOFTWARE or VSFC_HARDWARE.

Returns VS_OK if successfully set or VS_CLOSED if stream is not open.

Include: vserlib.h

| Up: GEOS SDK TechDocs | Up | Prev: UserRemoveAutoExec() ... | Next: VirtualSerialSetFormat() ...


GEOS C Routines: VirtualSerialSetFormat() ...

| Up: GEOS SDK TechDocs | Up | Prev: UtilAsciiToHex32() ... | Next: VMClose() ...

VirtualSerialSetFormat()

VirtualSerialStatus 	VirtualSetFormat(
        VSSerialFormat		format,/* data format */
        VSSerialBaud		baud); /* baud rate */

Sets the modem's data format and speed. Returns VS_OK if successfully set or VS_CLOSED if stream is not open.

Include: vserlib.h

VirtualSerialSetNotify()

VirtualSerialStatus 	VirtualSerialSetNotify(
    VSNotifyType		notifyTpe,   /* Which events, and how they are received */
    optr 		destination,   /* Destination object */
    word 		method);   /* Message to pass to destination object */

This routine registers a notifier for any of the possible relevant events. The parameter notifyType is set to specify which events should result in the object destination being sent the message method. The notifyType should be set by OR-ing one VSNT_, one VSNE_, and one VSNM_ parameter.

The set of these parameters is:

VSNT_READER
notify reader
VSNE_DATA
data/space available notification
VSNE_MODEM
notify when modem input signals change
VSNM_NONE
disable notifier
VSNM_MESSAGE
notify by sending a message cx:dx
For example, notifyType = VSNT_READER | VSNE_DATA | VSNM_MESSAGE: This will notify the reader for data available using messaging.

This routine returns VS_OK if the notification is successfully registered.

Returns VS_NOT_SUPPORTED if the notifier is not supported.

Returns VS_CLOSED if the stream is not open.

Warning: This is a low-level routine whose use is discouraged in favor of ModemSetRoutineDataNotify(),
    ModemSetMessageDataNotify(),
    ModemSetRoutineResponseNotify(),
    ModemSetMessageResponseNotify(), and
    ModemSetMessageEndCallNotify().

Include: vserlib.h

VirtualSerialWrite()

VirtualSerialStatus 	VirtualSerialWrite(
        VSBlockingMode		blocking_mode, /* to block or not to block */
        word		buffer_size, /* # of bytes of data to write */
        const byte		*ptr_to_buffer, /* data to write to stream */
        word		*bytes_written); /* # of bytes actually written */

Writes buffer_size number of bytes of data from the buffer to stream. If there is not enough space in the stream and STREAM_BLOCK was specified in blocking_mode, will block and wait until more space becomes available; if there is not enough space in the stream and STREAM_NO_BLOCK was specified, returns immediately with VS_SHORT_READ_WRITE.

Returns VS_OK if data was successfully written.

Returns VS_CLOSED if stream is not open.

Warning: STREAM_BLOCK is dangerous to use: if the opposite end does not read its data, stream may be constantly full and routine will continue to block for a maximum of two minutes. After two minutes, routine returns VS_SHORT_READ_WRITE and discards the data.

Include: vserlib.h

VirtualSerialWriteByte()

VirtualSerialStatus 	VirtualSerialWriteByte(
        VSBlockingMode		blockingMode, /* To block or not to block... */
        byte		dataByte); /* Data to write */

This routine writes the byte dataByte into the stream. If the stream is full and blockingMode is set to STREAM_BLOCK then the routine will wait for the request to be filled, or if STREAM_NO_BLOCK is specified the routine will immediately return an error.

Returns VS_OK if data was successfully written.

Returns VS_SHORT_READ_WRITE if the space was not available and STREAM_NO_BLOCK was specified.

Returns VS_CLOSED if the stream is not open.

Warning: STREAM_BLOCK mode is dangerous to use: if the opposite end does not read its data, the stream may be constantly full and this function will block for maximum of two minutes. After two minutes the routine returns VS_SHORT_READ_WRITE and discards the given data.

Include: vserlib.h

See Also: VirtualSerialWrite(), VirtualSerialReadByte().

VisObjectHandlesInkReply()

void	VisObjectHandlesInkReply(void);

VisTextGraphicCompressGraphic()

extern VMChain VisTextGraphicCompressGraphic(
        VisTextGraphic 		*graphic,
        FileHandle 		sourceFile,
        FileHandle 		destFile,
        BMFormat format,
        word xRes,
        word yRes);

This routine compresses the bitmaps in a VisTextGraphic.

VMAlloc()

VMBlockHandle 	VMAlloc(
        VMFileHandle		file,
        word		size,			/* Size of a file in bytes */
        word		userID);			/* ID # to associate with block */

This routine creates a VM block. The block is not initialized. Before you use the block, you must lock it with VMLock() . If you pass a size of zero bytes, the VM block will be given an entry in the VM handle table, but no space in memory or in the file will be used; a global memory block will have to be assigned with VMAttach() .

Include: vm.h

See Also: VMAllocLMem(), VMAttach().

VMAllocLMem()

VMBlockHandle 	VMAllocLmem(
        VMFileHandle		file,
        LMemType		ltype,				/* Type of LMem heap to create */
        word		headerSize);				/* Size to leave for LMem header...
                         * pass zero for standard header */

This routine allocates a VM block and initializes it to contain an LMem heap. You must pass the type of LMem heap to create. If you want a fixed data space, you must pass the total size to leave for a header (including the LMemBlockHeader ); otherwise, pass a zero header size, indicating that only enough space for an LMemBlockHeader should be left. You do not need to specify a block size, since the heap will automatically expand to accommodate chunk allocations.

The block's user ID number is undefined. You will need to lock the block with VMLock() before accessing the chunks.

Include: vm.h

Warning: When you access chunks, remember to pass the block's global memory handle to the LMem routines (not the block's VM handle).

See Also: LMemInitHeap(),
VMAlloc(),
VMAttach().

VMAttach()

VMBlockHandle 	VMAttach(
        VMFileHandle		file,
        VMBlockHandle		vmBlock,
        MemHandle		mh);

This routine attaches an existing global memory block to a VM block. It is passed the following arguments:

file
The file's VMFileHandle.
vmBlock
The handle of the VM block to which the memory block should be attached. Any data associated with that block will be lost. If you pass a null VMBlockHandle , a new VM block will be allocated.
mh
The handle of the global memory block to attach.

The routine returns the handle of the VM block to which the memory block was attached.

If you attach to a pre-existing VM block, its user ID will be preserved. If you create a new block (by passing a null vmBlock argument), the user ID will be undefined.

The block is automatically marked dirty.

Include: vm.h

VMCheckForModifications()

Boolean	VMCheckForModifications(
        VMFileHandle		file);

This routine returns true if the VM file has been dirtied or updated since the last full save.

Include: vm.h

| Up: GEOS SDK TechDocs | Up | Prev: UtilAsciiToHex32() ... | Next: VMClose() ...


GEOS C Routines: VMClose() ...

| Up: GEOS SDK TechDocs | Up | Prev: VirtualSerialSetFormat() ... | Next: VMGetMapBlock() ...

VMClose()

word	VMClose(
        VMFileHandle	file,
        Boolean		noErrorFlag);

This routine updates and closes a VM file. If it is successful, it returns zero. If it fails, it returns a VMStatus error code. Note that the routine closes the file even if it could not successfully update the file; in this case, any changes since the last update will be lost. For this reason, it is safest to call VMUpdate() first, then (after the file has been successfully updated) call VMClose().

If noErrorFlag is TRUE, VMClose() will fatal-error if it could not succesfully update and close the file.

Include: vm.h

VMCompareVMChains()

Boolean	VMCompareVMChains(
        VMFileHandle		sourceFile,
        VMChain		sourceChain,
        VMFileHandle		destFile,
        VMChain		destChain);

This routine compares two VM chains or DB items. It returns true if the two are identical; otherwise it returns false .

Include: vm.h

VMCopyVMBlock()

VMBlockHandle 	VMCopyVMBlock(
        VMFileHandle		sourceFile,
        VMBlockHandle		sourceBlock,
        VMFileHandle		destFile);

This routine creates a duplicate of a VM block in the specified destination file (which may be the same as the source file). It returns the duplicate block's handle. The duplicate will have the same user ID as the original block.

Include: vm.h

VMCopyVMChain()

VMChain 	VMCopyVMChain(
        VMFileHandle		sourceFile,
        VMChain		sourceChain,
        VMFileHandle		destFile);

This routine creates a duplicate of a VM chain (or DB item) in the specified destination file (which may be the same as the source file). It returns the duplicate's VMChain structure. All blocks in the duplicate will have the same user ID numbers as the corresponding original blocks.

Include: vm.h

VMDetach()

MemHandle 	VMDetach(
        VMFileHandle		file,
        VMBlockHandle		block,
        GeodeHandle		owner);			/* Pass zero to have block owned by
                     * current thread's owner */

This routine detaches a global memory block from a VM block. If the VM block is not currently in memory, VMDetach() allocates a memory block and copies the VM block into it. If the VM block is dirty, VMDetach() will update the block to the file before detaching it.

Include: vm.h

VMDirty()

void	VMDirty(
        MemHandle		mh);

This routine marks a locked VM block as dirty.

Include: vm.h

VMFind()

VMBlockHandle 	VMFind(
        VMFileHandle		file,
        VMBlockHandle		startBlock,
        word		userID);

This routine finds a VM block with the specified user ID number. If the second argument is NullHandle the routine will return the matching block with the lowest handle. If the second argument is non-null, it will return the first matching block whose handle is larger than the one passed (in numerical order).

Include: vm.h

VMFree()

void	VMFree(
        VMFileHandle		file,
        VMBlockHandle		block);

This routine frees the specified VM block. If a global memory block is currently attached to the VM block, it is freed too.

Include: vm.h

VMFreeVMChain()

void	VMFreeVMChain(
        VMFileHandle		file,
        VMChain		chain);

This routine frees the specified VM chain or DB item. If a chain is specified, all blocks in the chain will be freed.

Include: vm.h

VMGetAttributes()

word	VMGetAttributes(
        VMFileHandle		file);

Each VM file contains a set of VMAttributes flags. These determine how the VM manager will treat the file. This routine returns the current flags.

Include: vm.h

Tips and Tricks: When the Document Control objects create files, they automatically initialize the attributes appropriately.

See Also: VMSetAttributes().

VMGetDirtyState()

word	VMGetDirtyState(
        VMFileHandle		file);

This routine finds out if a file has been dirtied. It returns a word-sized value. The upper byte of the return value is non-zero if the file has not been dirtied since the last save, auto-save, or update; the lower byte is non-zero if the file has not been dirtied since the last save. Thus, if the return value is zero, the file must be updated.

Include: vm.h

Tips and Tricks: VMUpdate() is optimized for updating clean files. For this reason, it is faster to call VMUpdate() then it is to first check the dirty state, then call VMUpdate() only if the file is dirty.

| Up: GEOS SDK TechDocs | Up | Prev: VirtualSerialSetFormat() ... | Next: VMGetMapBlock() ...


GEOS C Routines: VMGetMapBlock() ...

| Up: GEOS SDK TechDocs | Up | Prev: VMClose() ... | Next: VMPreserveBlocksHandle() ...

VMGetMapBlock()

VMBlockHandle 	VMGetMapBlock(
        VMFIleHandle		file);

This routine returns the VM block handle of the file's map block.

Include: vm.h

VMGrabExclusive()

VMStartExclusiveReturnValue VMGrabExclusive(
        VMFileHandle		file,
        word		timeout,
        VMOperation		operation,
        VMOperation *		currentOperation);

This routine gets exclusive access to a VM file for this thread.

Include: vm.h

VMInfo()

Boolean	VMInfo(
        VMFileHandle		file,
        VMBlockHandle		block,
        VMInfoStruct *		info

This routine writes the memory handle, block size, and user ID number of the block. It returns non-zero if the handle is invalid or free.

Include: vm.h

VMLock()

void *	VMLock(
        VMFileHandle		file,
        VMBlockHandle		block,
        MemHandle*		mh);

This routine locks a VM block into the global heap. It returns the block's base address.

Include: vm.h

VMMemBlockToVMBlock()

VMBlockHandle 	VMMemBlockToVMBlock(
        MemHandle		mh,
        VMFileHandle*		file);

This routine gets the VM block and file handles for a specified memory block. It returns the VM block handle and copies the VM file handle into *file .

The memory handle passed must be the handle of a block which is attached to a VM file. If it is not, the results are undefined.

Include: vm.h

VMModifyUserID()

void	VMModifyUserID(
        VMFileHandle		file,
        VMBlockHandle		block,
        word		userID);

This routine changes a VM block's user ID number.

Include: vm.h

VMOpen()

VMFileHandle 	VMOpen(
        char *		name,				/* Name of file to open/create */
        VMAccessFlags		flags,
        VMOpenType		mode,
        word		compression);				/* Compaction threshold percentage
                         * passed as an integer */

This routine opens or creates a VM file. It returns the handle of the opened file. If it is unable to open the file, it returns a null VMFileHandle and sets a VMStatus error code which may be retrieved with ThreadGetError() . VMOpen() looks for the file in the thread's working directory (unless a temporary file is being created, as described below). The routine takes four arguments:

name
A pointer to a string containing the name of the file to open. The file will be opened in the thread's current working directory. If a temporary file is being opened, this buffer should contain the full path of the directory in which to create the file, followed by fourteen null bytes (counting the string-ending null). VMOpen() will write the name of the temporary file in those trailing nulls.
flags
This specifies what kind of access to the file you need. The flags are described in the C Structures Reference.
mode
This specifies how the file should be opened. The types are described in the C Structures Reference.
compression
The compaction threshold percentage, passed as an integer. For example, to set a compaction threshold of 50%, pass the integer 50. When the percentage of used space in the file drops below the compaction threshold, the VM manager will automatically compact the file. To use the system default threshold, pass a threshold of zero. The compaction threshold is set only when the file is created; this argument is ignored if an existing file is opened.

You must also specify how the file should be opened. To do this, you pass a member of the VMOpenType enumerated type.

If for any reason VMOpen() is unable to open the requested file, it will returns a null file handle. It will also set the error value for ThreadGetError() . The possible error conditions are:

VM_FILE_EXISTS
VMOpen() was passed VMO_CREATE_ONLY, but the file already exists.
VM_FILE_NOT_FOUND
VMOpen() was passed VMO_OPEN, but the file does not exist.
VM_SHARING_DENIED
The file was opened by another geode, and access was denied.
VM_OPEN_INVALID_VM_FILE
VMOpen() was instructed to open an invalid VM file (or a non-VM file).
VM_CANNOT_CREATE
VMOpen() cannot create the file (but it does not already exist).
VM_TRUNCATE_FAILED
VMOpen() was passed VMO_CREATE_TRUNCATE; the file exists, but could not be truncated.
VM_WRITE_PROTECTED
VMOpen() was passed VMAF_FORCE_READ_WRITE, but the file was write-protected.
VM_CANNOT_OPEN_SHARED_MULTIPLE
VMOpen() attempted to open a file marked GFHF_SHARED_MULTIPLE while passing the VMAF_DISALLOW_SHARED_MULTIPLE flag.
VM_FILE_FORMAT_MISMATCH
VMOpen() attempted to create a VM file using a name that already belongs to a DOS file.

Include: vm.h

Tips and Tricks: If you use the document control objects, they will take care of opening files as necessary; you will not need to call VMOpen() .

See Also: FileOpen().

| Up: GEOS SDK TechDocs | Up | Prev: VMClose() ... | Next: VMPreserveBlocksHandle() ...


GEOS C Routines: VMPreserveBlocksHandle() ...

| Up: GEOS SDK TechDocs | Up | Prev: VMGetMapBlock() ... | Next: WinAckUpdate() ...

VMPreserveBlocksHandle()

void	VMPreserveBlocksHandle(
        VMFileHandle		file,
        VMBlockHandle		block);

Keep the same global memory block with this VM block until the block is explicitly detached or the VM block is freed.

Include: vm.h

VMReleaseExclusive()

void VMReleaseExclusive(
        VMFileHandle 		file);

This routine releases a thread's exclusive access to a VM file.

Include: vm.h

VMRevert()

void	VMRevert(
        VMFileHandle		file,);

This routine reverts a file to its last-saved state.

Include: vm.h

VMSave()

Boolean	VMSave(
        VMFileHandle		file);

This routine updates and saves a file, freeing all backup blocks. If it encounters an error in the save operation, it will return non-zero .

VMSaveAs()

VMFileHandle VMSaveAs(
        VMFileHandle		file,
        const char		*name,
        VMAccessFlags		flags.
        VMOpenType		mode,
        word		compression); /* compaction threshold */

This routine saves a file under a new name. The old file is reverted to its last-saved condition. If it encounters an error in the save operation, it will return a null VMFileHandle and set a VMStatus error code which may be retrieved with ThreadGetError() .

Include: vm.h

VMSetAttributes()

word	VMSetAttributes(
        VMFileHandle		file,
        VMAttributes		attrToSet,				/* Turn these flags on... */
        VMAttributes		attrToClear);				/* after turning these flags off */

This routine changes a VM file's VMAttributes settings. The routine returns the new attribute settings.

Include: vm.h

Tips and Tricks: When the Document Control objects create files, they automatically initialize the attributes appropriately.

Warnings: If you turn off VMA_BACKUP, make sure you do it right after a save or revert (when there are no backup blocks).

See Also: VMGetAttributes().

VMSetExecThread()

void	VMSetExecThread(
        VMFileHandle		file,
        ThreadHandle		thread);

Set which thread will execute methods of all objects in the file.

Include: vm.h

VMSetMapBlock()

void	VMSetMapBlock(
        VMFileHandle		file,
        VMBlockHandle		block);

This routine sets the map block for a VM file.

Include: vm.h

VMSetReloc()

void	VMSetReloc(
        VMFileHandle		file,
        void (*reloc) 		(VMFileHandle					file,
                 VMBlockHandle					block,
                 MemHandle					mh,
                 void					*data,
                 VMRelocTypes					type));

This routine sets a data-relocation routine for the VM file.

Include: vm.h

VMUnlock()

void	VMUnlock(
        MemHandle		mh);

This routine unlocks a locked VM block. Note that the block's global memory handle is passed (not its VM handle).

Include: vm.h

VMUpdate()

word	VMUpdate(
        VMFileHandle		file);

This routine updates dirty blocks to the disk.

Include: vm.h

Tips and Tricks: VMUpdate() is optimized for updating clean files to the disk. Therefore, it is faster to call VMUpdate() whenever you think it might be necessary, than it is to check the dirty state and then call VMUpdate() only if the file is actually dirty.

VMVMBlockToMemBlock()

MemHandle 	VMVMBlockToMemBlock(
        VMFileHandle		file,
        VmBlockHandle		block);

This routine returns the global handle of the memory block attached to a specified VM block. If no global block is currently attached, it will allocate and attach one.

Include: vm.h

| Up: GEOS SDK TechDocs | Up | Prev: VMGetMapBlock() ... | Next: WinAckUpdate() ...


GEOS C Routines: WinAckUpdate() ...

| Up: GEOS SDK TechDocs | Up | Prev: VMPreserveBlocksHandle() ... | Next: WinGeodeSetInputObj() ...

WinAckUpdate()

void	WinAckUpdate(
        WindowHandle		win);

This routine acknowledges that the application has received MSG_META_EXPOSED for the specified window, but chooses not to do any updating.

Include: win.h

WinApplyRotation()

void	WinApplyRotation(
        WindowHandle		win,
        WWFixedAsDWord		angle,
        WinInvalFlag		flag);

This routine applies the specified rotation to the window's transformation matrix.

Include: win.h

WinApplyScale()

void	WinApplyScale(
        WindowHandle		win,
        WWFixedAsDWord		xScale,
        WWFixedAsDWord		yScale,
        WinInvalFlag		flag);

This routine applies the specified scale factor to the window's transformation matrix.

Include: win.h

WinApplyTransform()

void	WinApplyTransform(
        WindowHandle		win,
        const TransMatrix *		tm,
        WinInvalFlag		flag);

This routine concatenates the passed transformation matrix with the window's transformation matrix. The result will be the window's new transformation matrix.

Include: win.h

WinApplyTranslation()

void	WinApplyTranslation(
        WindowHandle		win,
        WWFixedAsDWord		xTrans,
        WWFixedAsDword		yTrans,
        WinInvalFlag		flag);

This routine applies the specified translation to the window's transformation matrix.

Include: win.h

WinApplyTranslationDWord()

void	WinApplyExtTranslation(
        WindowHandle		win,
        sdword		xTrans,
        sdword		yTrans,
        WinInvalFlag		flag);

This routine applies the specified translation to the window's transformation matrix. The translations are specified as 32-bit integers.

Include: win.h

WinChangeAck()

WindowHandle WinChangeAck(
        WindowHandle		win,
        sword		x,
        sword		y,
        optr *		winOD);

Include: win.h

WinChangePriority()

void	WinChangePriority(
        WindowHandle		win,
        WinPassFlags		flags,
        word		layerID);

This routine changes the priority for the specified window.

Include: win.h

WinClose()

void	WinClose(
        WindowHandle		win);

This routine closes and frees the specified window.

Include: win.h

WinDecRefCount()

void	WinDecRefCount(
        WindowHandle		win);

This routine is part of the window closing mechanism.

WinEnsureChangeNotification()

void	WinEnsureChangeNotification(void);

Include: win.h

WinGeodeGetInputObj()

optr	WinGeodeGetInputObj(
        GeodeHandle		obj);

This routine fetches the optr of the input object for the specified geode. If there is no such object, it returns a null optr.

Include: win.h

WinGeodeGetParentObj()

optr	WinGeodeGetParentObj(
        GeodeHandle		obj);

This routine fetches the optr of the parent object of the specified geode. If there is no such object, it returns a null optr.

Include: win.h

WinGeodeSetActiveWin()

void	WinGeodeSetActiveWin(
        GeodeHandle		gh,
        WindowHandle		win);

This routine sets the active window for the specified geode.

Include: win.h

| Up: GEOS SDK TechDocs | Up | Prev: VMPreserveBlocksHandle() ... | Next: WinGeodeSetInputObj() ...


GEOS C Routines: WinGeodeSetInputObj() ...

| Up: GEOS SDK TechDocs | Up | Prev: WinAckUpdate() ... | Next: WinScroll() ...

WinGeodeSetInputObj()

void	WinGeodeSetInputObj(
        GeodeHandle		gh,
        optr		iObj);

This routine sets the input object for the specified geode.

Include: win.h

WinGeodeSetParentObj()

void	WinGeodeSetParentObj(
        GeodeHandle		gh,
        optr		pObj);

This routine sets the parent object for the specified geode.

Include: win.h

WinGeodeSetPtrImage()

void	WinGeodeSetPtrImage(
        GeodeHandle		gh,
        optr		ptrCh);

This routine sets the pointer image for the specified geode.

Include: win.h

WinGetInfo()

dword	WinGetInfo(
        WindowHandle		win,
        WinInfoTypes		type,
        void *		data);

This routine retrieves the private data from a GState.

Include: win.h

WinGetTransform()

void	WinGetTransform(
        WindowHandle		win,
        TransMatrix *		tm);

This routine retrieves the transformation matrix for the specified window. It writes the matrix to *tm .

Include: win.h

WinGetWinScreenBounds()

void	WinGetWinScreenBounds(
        WindowHandle		win,
        Rectangle *		bounds);

This routine returns the bounds of the on-screen portion of a window (specified in screen co-ordinates). It writes the bounds to *bounds .

Include: win.h

WinGrabChange()

Boolean	WinGrabChange(
        WindowHandle		win,
        optr		newObj);

This routine allows an object to grab pointer events. It returns zero if it was successful; otherwise it returns non-zero.

Include: win.h

WinInvalReg()

void	WinInvalReg(
        WindowHandle		win,
        const Region *		reg,
        word		axParam,
        word		bxParam,
        word		cxParam,
        word		dxParam);

This routine invalidates the specified region or rectangle.

Include: win.h

WinMove()

void	WinMove(
        WindowHandle		win,
        sword		xMove,
        sword		yMove,
        WinPassFlags		flags);

This routine moves a window. If the WPF_ABS bit of flags is set, the window's new position is specified relative to its parent's position. If it is clear, the window's new position is specified relative to its current position.

Include: win.h

WinOpen()

WindowHandle WinOpen(
        Handle		parentWinOrVidDr,
        optr		inputRecipient,
        optr		exposureRecipient,
        WinColorFlags		colorFlags,
        word		redOrIndex,
        word		green,
        word		blue,
        word		flags,
        word		layerID,
        GeodeHandle		owner,
        const Region *		winReg,
        word		axParam,
        word		bxParam,
        word		cxParam,
        word		dxParam);

This routine allocates and initializes a window and (optionally) an associated GState.

Include: win.h

WinReleaseChange()

void	WinReleaseChange(
        WindowHandle		win,
        optr		obj);

This routine releases an object's grab on the change OD.

Include: win.h

WinResize()

void	WinResize(
        WindowHandle		win,
        const Region *		reg,
        word		axParam,
        word		bxParam,
        word		cxParam,
        WinPassFlags		flags);

This routine resizes a window. It can move it as well.

Include: win.h

| Up: GEOS SDK TechDocs | Up | Prev: WinAckUpdate() ... | Next: WinScroll() ...


GEOS C Routines: WinScroll() ...

| Up: GEOS SDK TechDocs | Up | Prev: WinGeodeSetInputObj() ...

WinScroll()

void	WinScroll(
        WindowHandle		win,
        WWFixedAsDWord		xMove,
        WWFixedAsSWord		yMove,
        PointWWFixed *		scrollAmt);

This routine scrolls a window.

Include: win.h

WinSetInfo()

void	WinSetInfo(
        WindowHandle		win,
        WinInfoType		type,
        dword		data);

This routine sets some data for the specified window.

Include: win.h

WinSetNullTransform()

void	WinSetNullTransform(
        WindowHandle		win,
        WinInvalFlag		flag);

This routine changes a window's transformation matrix to the null (or identity) matrix.

Include: win.h

WinSetPtrImage()

void	WinSetPtrImage(
        WindowHandle		win,
        WinSetPtrImageLevel		ptrLevel,
        optr		ptrCh);

This routine sets the pointer image within the range handled by the specified window.

Include: win.h

WinSetTransform()

void	WinSetTransform(
        WindowHandle		win,
        const TransMatrix *		tm,
        WinInvalFlag		flag);

This routine replaces the window's transformation matrix with the one passed in *tm .

Include: win.h

WinSuspendUpdate()

void	WinSuspendUpdate(
        WindowHandle		win);

This routine suspends the sending of update messages to the window. The messages will be sent when WinUnSuspendUpdate() is called.

Include: win.h

WinTransform()

XYValueAsDWord 	WinTransform(
        WindowHandle		win,
        sword		x,
        sword		y);

This routine translates the passed document coordinates into screen coordinates.

Include: win.h

WinTransformDWord()

void	WinTransformDWord(
        WindowHandle		win,
        sdword		xCoord,
        sdword		yCoord,
        PointDWord *		screenCoordinates);

This routine translates the passed document coordinates into screen coordinates. The translated coordinates are written to *screenCoordinates .

Include: win.h

WinUnSuspendUpdate()

void	WinUnSuspendUpdate(
        WindowHandle		win);

This routine cancels a previous WinSuspendUpdate() call.

Include: win.h

WinUntransform

XYValueAsDWord 	WinUntransform(
        WindowHandle		win,
        sword		x,
        sword		y);

This routine translates the passed screen coordinates into document coordinates.

Include: win.h

WinUntransformDWord()

void	WinUntransformDWord(
        WindowHandle		win,
        sdword		xCoord,
        sdword		yCoord,
        PointDWord *		documentCoordinates);

This routine translates the passed screen coordinates into document coordinates. The translated coordinates are written to *documentCoordinates .

Include: win.h

WWFixedToFrac

word	WWFixedToFrac(WWFixed wwf)

This macro lets you address the fractional portion of a WWFixed value. It is legal to use this to assign a value to the fractional protion; that is,

WWFixedToFrac(myWWFixed) = 5;

is perfectly legal.

Include: geos.h

WWFixedToInt

word	WWFixedToInt(WWFixed wwf)

This macro lets you address the intetgral portion of a WWFixed value. It is legal to use this to assign a value to the integral protion; that is,

WWFixedToInt(myWWFixed) = 5;

is perfectly legal.

Include: geos.h

| Up: GEOS SDK TechDocs | Up | Prev: WinGeodeSetInputObj() ...


GEOS C Structs Reference

This document contains reference information for most of the GEOS system structs. You may look up information in this and other GEOS SDK documentation by consulting the index. If, instead, you wish to browse this reference, you may choose one of the links below; the text of each is the name of the first struct documented on its page of the document.

AccessPointStandardProperty ...
AppLaunchFlags ...
BitmapMode ...
CellFunctionParameterFlags ...
Chars ...
ChunkArrayHeader ...
ClipboardItemHeader ...
Color ...
ColorFlag ...
CompSizeHintArgs ...
CustomDialogBoxFlags ...
DataStoreIndexCallbackParams ...
DateTimeFormat ...
DiskFindResult ...
DosNoDotFileName ...
EndOfSongFlags ...
EvalErrorData ...
FALSE ...
FileAttrs ...
FileDateAndTime ...
FileFromTransferBlockID ...
FontFamily ...
FontID ...
FontMaker ...
GCM_info ...
GCNStandardListType ...
GeodeToken ...
GeoworksMediumID ...
GeoWorksVisContentGCNListType ...
GStringElement ...
GStringErrorType ...
HeapCongestion ...
IMCFeatures ...
InkControlFeatures ...
InstrumentPatch ...
InstrumentTable ...
Language ...
LMemType ...
LocalNumericFormat ...
MediumUnitType ...
MixMode ...
NULL ...
OperatorType ...
ParserFlags ...
ParserTokenData ...
Point ...
ProtocolNumber ...
RangeSortFlags ...
ReleaseNumber ...
ScannerTokenCellData ...
SerialBaud ...
Socket ...
SocketError ...
SocketLoadType ...
SoundPlayFlags ...
SpecWidth ...
SpoolInfoType ...
StandardDialog4ResponseTriggerTable ...
StyleElementFlags ...
SystemDrawMask ...
TextMode ...
TextReferenceType ...
TimerHandle ...
TravelOption ...
UndoActionStruct ...
VChar ...
VisRulerType ...
VMOpenType ...

| Up: GEOS SDK TechDocs | Up | Down | Next: AccessPointStandardProperty ...
| One-page Version of this Chapter for Easy Printing


GEOS C Structs: AccessPointStandardProperty ...

| Up: GEOS SDK TechDocs | Up | Prev: GEOS C Structs: ... | Next: AppLaunchFlags ...

AccessPointStandardProperty

typedef enum {
        APSP_NAME = 0x0,
        APSP_PHONE = 0x2,
        APSP_USER = 0x4,
        APSP_SECRET = 0x6,
        APSP_ADDRESS = 0x8,
        APSP_MASK = 0xa,
        APSP_GATEWAY = 0xc,
        APSP_DNS1 = 0xe,
        APSP_DNS2 = 0x10,
        APSP_DATA_BITS = 0x12,
        APSP_STOP_BITS = 0x14,
        APSP_PARITY = 0x16,
        APSP_DUPLEX = 0x18,
        APSP_MODEM_INIT = 0x1a,
        APSP_BS = 0x1c,
        APSP_HOSTNAME = 0x1e,
        APSP_INTERNET_ACCPNT = 0x20,
        APSP_PROMPT_SECRET = 0x22,					/* This property is an integer */
        APSP_AUTOMATIC = 0x4000,
        APSP_UNDEFINED = 0xffff,

             /* The following are only available on build 4 or more recent
                of the Nokia 9000i Communicator. */

        APSP_USE_LOGIN_APP = 0x24,
        APSP_LOGIN_APP_NAME = 0x26,
        APSP_SCRIPT_NAME = 0x28,
        APSP_CCARD_NAME = 0x2a,
        APSP_CCARD_ACCESS = 0x2c,
        APSP_CCARD_ID = 0x2e,
        APSP_CCARD_PREFIX = 0x30,
        APSP_CCARD_SEQUENCE = 0x32,
        APSP_COMPRESSION = 0x34,
} AccessPointStandardProperty;

All of the property data associated with these standard properties are strings, except where noted.

Any AccessPointStandardProperty value can be combined (bitwise-OR'd) with APSP_AUTOMATIC to make an alternative form of the name. This library puts no special interpretation on automatic names, but they are intended to represent values which are obtained from automatic configuration and not from the user.

Include: accpnt.goh

AccessPointType

typedef enum {
    APT_INTERNET = 0x1,
    APT_TERMINAL,
    APT_TELNET,
    APT_APP_LOCAL,
} AccessPointType;
#define APT_ALL (0)

Include: accpnt.goh

AddUndoActionFlags

typedef WordFlags AddUndoActionFlags;
#define AUAF_NOTIFY_BEFORE_FREEING											 		  			0x8000
#define AUAF_NOTIFY_IF_FREED_WITHOUT_BEING_PLAYED_BACK		 0x4000

AddUndoActionStruct

typedef struct {
UndoActionStruct 				AUAS_data;
optr 				AUAS_output;
AddUndoActionFlags 				AUAS_flags;
} AddUndoActionStruct;

The "undo" structures work together to provide information vital to processes which will be working with undo events.

AppAttachFlags

typedef WordFlags AppAttachFlags;
#define AAF_RESTORING_FROM_STATE						0x8000
#define AAF_STATE_FILE_PASSED						0x4000
#define AAF_DATA_FILE_PASSED						0x2000

These flags are passed to the process when the application is launching or being restored from a state file. The flags indicate whether the application is being launched from a state file, has a state file, and/or has a data file.

Note that if AAF_RESTORING_FROM_STATE is set, then AAF_STATE_FILE_PASSED will also be set.

AppInstanceReference

typedef struct {
/* AIR_fileName:
 * Application being launched. Pathname is relative to application
 * directory (which, of course, may be overriden with a direct path
 * to the application). */
PathName 				AIR_fileName;
/* AIR_stateFile:
 * State filename. File is assumed to be in standard directory for
 * GEOS state files. If the first byte is "0", then there is no
 * state file for this application. This structure is copied into the
 * field as an aid in restarting applications, and if it comes across
 * one with this byte as 0, it will not restart it. */
FileLongName 				AIR_stateFile;
/* AIR_diskHandle:
 * Disk handle for app (passed in) IF 0, use System disk, if -1, use
 * AIR_diskName. In the field, if this is a placeholder structure, this
 * word is the handle of the application object we are waiting to detach. */
DiskHandle 				AIR_diskHandle;
/* AIR_savedDiskData:
 * Start of data stored by DiskSave when instance is saved to state file. */
byte 				AIR_savedDiskData[1];
} AppInstanceReference;

AppLaunchBlock

typedef struct {
/* ALB_appRef:
 * Instance reference. Contains full pathname to application, as
 * referenced from app directory, plus the name of a state file.
 * Is enough info to launch application again, restored. (State file
 * need not be passed to GeodeLoad) */
AppInstanceReference 				ALB_appRef;
/* ALB_appMode:
 * Application attach mode method. Should be one of the following:
 * MSG_GEN_PROCESS_RESTORE_FROM_STATE:
 * 	State file must be passed; no data file should be passed.
 * MSG_GEN_PROCESS_OPEN_APPLICATION:
 * 	State file normally should not be passed, although one could be to
 * 	accomplish ui templates. A data file may be passed into the
 * 	application as well.
 * MSG_GEN_PROCESS_OPEN_ENGINE:
 * 	State file normally should not be passed. The data file on which the
 * 	engine will operate must be passed. If zero, the default data file
 * 	should be used (enforced by app, not GenProcessClass).*/
Message 				ALB_appMode;
/* ALB_launchFlags:
 * Miscellaneous flags to specify desired application launch type. */
AppLaunchFlags 				ALB_launchFlags;
/* ALB_diskHandle:
 * Disk handle for data path. (Set as application's current path in
 * GenProcess' MSG_META_ATTACH handler.) */
MemHandle 				ALB_diskHandle;
/* ALB_path:
 * Data path for application to use as initial path. (Usually this is
 * a directory of any data file passed.) (Set as application current
 * path in GenProcess' MSG_META_ATTACH handler.)
char 				ALB_path[PATH_BUFFER_SIZE];
/* ALB_dataFile:
 * Name of data file passed in to be opened (0 if none). Pathname is
 * relative to above path. */
char 				ALB_dataFile[PATH_BUFFER_SIZE];
/* ALB_genParent:
 * Generic parent for new application (0 to put on default field). (Should
 * be passed NULL to MSG_GEN_FIELD_LAUNCH_APPLICATION).
optr 				ALB_genParent;
/* ALB_userLoadAckOutput, ALB_userLoadAckMessage:
 * Together, these form an Action Descriptor which will be activated when
 * the application has been launched (used in conjunction with
 * ALF_SEND_LAUNCH_REQUEST_TO_UI_TO_HANDLE). (Set to NULL/0 if you don't
 * want to send anything).
 * The acknowledgement will come with three arguments: the GeodeHandle
 * (non-NULL if successful), a word value which will be zero if there was
 * an error, and the word value set in ALB_userLoackAckID (below).*/
optr 				ALB_userLoadAckOutput;
Message 				ALB_userLoadAckMessage;
/* ALB_userLoadAckID:
 * ID sent out via above action descriptor, if any. */
word 				ALB_userLoadAckID;
/* ALB_extraData:
 * Extra data to send to process (possibly a handle to
 * block containing arguments). */
word 				ALB_extraData;
} AppLaunchBlock;

This structure is used when an application is first starting up. It is an argument of various messages which will be intercepted by system classes. The first fields ( ALB_appRef , ALB_appMode , ALB_launchFlags , and ALB_uiLevel ) are preserved in the application's state file. The other information must be set correctly on launch.

| Up: GEOS SDK TechDocs | Up | Prev: GEOS C Structs: ... | Next: AppLaunchFlags ...


GEOS C Structs: AppLaunchFlags ...

| Up: GEOS SDK TechDocs | Up | Prev: AccessPointStandardProperty ... | Next: BitmapMode ...

AppLaunchFlags

typedef ByteFlags AppLaunchFlags;
#define ALF_SEND_LAUNCH_REQUEST_TO_UI_TO_HANDLE											0x80
#define ALF_OPEN_IN_BACK											0x40

ApplicationStates

typedef ByteFlags ApplicationStates;
#define AS_QUITTING								0x80
#define AS_DETACHING								0x40
#define AS_FOCUSABLE 								0x20
#define AS_MODELABLE 								0x10
#define AS_NOT_USER_INTERACTABLE 								0x08
#define AS_RECEIVED_APP_OBJECT_DETACH 								0x04
#define AS_ATTACHED_TO_STATE_FILE 								0x02
#define AS_ATTACHING 								0x01

ArcCloseType

typedef enum /* word */ {
ACT_OPEN,
ACT_CHORD,
ACT_PIE
} ArcCloseType;

This structure is used when filling arcs.

AreaAttr

typedef struct {
byte 		AA_colorFlag;
RGBValue 		AA_color;
SysDrawMask 		AA_mask;
ColorMapMode		AA_mapMode;
} AreaAttr;

ArgumentStackElement

typedef struct {
EvalStackArgumentType ASE_type;
EvalStackArgumentData ASE_data;
} ArgumentStackElement;

BBFixed

typedef struct {
byte BBF_frac;
byte BBF_int;
} BBFixed;

This structure represents an 8.8 fixed point number.

BBFixedAsWord

typedef word BBFixedAsWord;

This structure represents an 8.8 fixed point number.

Bitmap

typedef struct {
word	B_width;				/* In bitmap pixels */
word	B_height;				/* In bitmap pixels */
byte	B_compact;				/* A BMCompact value */
byte	B_type;				/* A BMFormat | BMType value */
} Bitmap;

This data structure provides some information about a simple graphics bitmap. It normally acts as the header for a set of bitmap data.

The bitmap data itself is organized into scan lines. If the bitmap has a mask (if the BMT_MASK bit is set in the B_type field), the first information for the scan line will be its mask information. There will be one bit of mask information for each pixel in the scan line (i.e. a number of bits equal to the bitmap width). The actual bitmap data for the scan line starts at the next byte boundary. For each pixel there will be a number of bits of color data, said number depending on the BMFormat value in the B_type field. The data for the next scan line will begin at the next byte boundary.

Thus, a 7x7 bitmap depicting an inverse "x" might appear:

(Bitmap)		{7, 7, BMC_UNCOMPACTED, BMF_MONO };
(byte)[]		{0x82, 		/* 10000010 */
         0x44, 		/* 01000100 */
         0x28, 		/* 00101000 */
         0x10, 		/* 00010000 */
         0x28, 		/* 00101000 */
         0x44, 		/* 01000100 */
         0x82 }; 		/* 10000010 */

A 3x3 color "-" shape with a a "+" shaped mask might appear:

(Bitmap)		{ 3, 3, BMC_UNCOMPACTED,
         (BMF_4BIT | BMT_MASK)};
(byte) []		{/* scan line 1: */
         0x40, 		/* mask: 010 */
         0, 0		/* data: 000 */

         /* scan line 2: */
         0xE0, 		/* mask: 111 */
         0x43, 0x20, 		/* data: 432 */

         /* scan line 3: */
         0x40, 		/* mask: 010 */
         0, 0 };		/* data: 000 */

If standard BMC_PACKBITS compression is used, then the mask (if any) and color data for the bitmap is compressed using the Macintosh PackBits standard. Under this system, to uncompress the data for a scan line, follow the loop:

  1. Read a byte.
  2. If the byte read in step (1) is between -1 and -127, read the next byte and copy it into the target buffer from +2 to +128 times.
  3. If the byte read in step (1) is between +1 and +127, read the next 1 to 127 bytes and copy them into the target buffer.
  4. If the byte read in step (1) is -128, ignore it.
  5. You're ready to read in the next batch of data; go back to step (1).

Thus a 16x4 color "=" with a matching mask would appear:

(Bitmap) 	{15, 3, BMC_PACKBITS, BMF_4BIT | BMT_MASK } ;
(byte) []		{/* scan line 1: */
            /* mask: 2 repetitions of 0xff */
         0xff, 0xff,
            /* data: 16 repetitions of 0x14 */
         0xf0, 0x14,
        /* scan line 2: */
            /* mask: 2 repetitions of 0x00 */
            /* data: 16 repetitions of 0x00 */
            /* total: 18 repetitions of 0x00 */
         0xee, 0x00,
        /* scan line 3: */
            /* mask: 2 repetitions of 0x00 */
            /* data: 16 repetitions of 0x00 */
            /* total: 18 repetitions of 0x00 */
         0xee, 0x00,
        /* scan line 4: */
            /* mask: 2 repetitions of 0xff */
         0xff, 0xff,
            /* data: 16 repetitions of 0x14 */
         0xf0, 0x14};

See Also: CBitmap.

| Up: GEOS SDK TechDocs | Up | Prev: AccessPointStandardProperty ... | Next: BitmapMode ...


GEOS C Structs: BitmapMode ...

| Up: GEOS SDK TechDocs | Up | Prev: AppLaunchFlags ... | Next: CellFunctionParameterFlags ...

BitmapMode

typedef WordFlags BitmapMode;
#define BM_EDIT_MASK					0x0002
#define BM_CLUSTERED_DITHER					0x0001

BlockFromTransferBlockID

VMBlockHandle BlockFromTransferBlockID(id);
        TransferBlockID		id;

This macro extracts the block handle from the given TransferBlockID value.

BlockIDFromFileAndBlock

TransferBlockID BlockIDFromFileAndBlock(f, b);
        VMFileHandle		f;
        VMBlockHandle		b;

This macro creates a TransferBlockID value from the given file and block handles.

BLTMode

See GrBitBlt() .

BMCompact

typedef ByteEnum ByteCompact;
#define BMC_UNCOMPACTED				0
#define BMC_PACKBITS				1
#define BMC_USER_DEFINED				0x80

This data structure is used to specify what sort of compaction is used to store a graphics bitmap.

BMDestroy

See entry for GrDestroyBitmap() .

BMFormat

typedef ByteEnum BMFormat
#define BMF_MONO 0
#define BMF_4BIT 1
#define BMF_8BIT 2
#define BMF_24BIT 3
#define BMF_4CMYK 4

This enumerated type determines a graphics bitmap's depth.

BMType

typedef ByteFlags BMType;
#define BMT_PALETTE				0x40
#define BMT_HUGE 				0x20
#define BMT_MASK 				0x10
#define BMT_COMPLEX 				0x08
#define BMT_FORMAT 				0x07

This structure is used to store various facts about a graphics bitmap.

Boolean

typedef word Boolean;

Booleans represent true/false values. If the Boolean is false , it will evaluate to zero; otherwise, it will be non-zero.

Button

typedef ByteEnum Button;
#define BUTTON_0				0
#define BUTTON_1				1
#define BUTTON_2				2
#define BUTTON_3				3

ButtonInfo

typedef ByteFlags ButtonInfo;
#define BI_PRESS				0x80
#define BI_DOUBLE_PRESS 				0x40
#define BI_B3_DOWN 				0x20
#define BI_B2_DOWN 				0x10
#define BI_B1_DOWN 				0x08
#define BI_B0_DOWN 				0x04
#define BI_BUTTON 				0x03

This structure contains the state of a mouse's buttons.

byte

typedef unsigned char byte;

ByteEnum

typedef byte ByteEnum;

ByteFlags

typedef byte ByteFlags;

CallbackType

typedef ByteEnum CallbackType;
#define CT_FUNCTION_TO_TOKEN					0
#define CT_NAME_TO_TOKEN 					1
#define CT_CHECK_NAME_EXISTS 					2
#define CT_CHECK_NAME_SPACE 					3
#define CT_EVAL_FUNCTION 					4
#define CT_LOCK_NAME 					5
#define CT_UNLOCK 					6
#define CT_FORMAT_FUNCTION 					7
#define CT_FORMAT_NAME 					8
#define CT_CREATE_CELL					9
#define CT_EMPTY_CELL 					10
#define CT_NAME_TO_CELL 					11
#define CT_FUNCTION_TO_CELL 					12
#define CT_DEREF_CELL 					13
#define CT_SPECIAL_FUNCTION 					14

CBitmap

typedef struct {
Bitmap 	CB_simple;
word 	CB_startScan;
word 	CB_numScans;
word 	CB_devInfo;
word 	CB_data;
word 	CB_palette;
word 	CB_xres;
word	CB_yres;
} CBitmap;

The CBitmap structure contains the information for a "complex" bitmap. Use the CBitmap structure to hold bitmaps which need to keep track of resolution information, a palette, or a mask.

| Up: GEOS SDK TechDocs | Up | Prev: AppLaunchFlags ... | Next: CellFunctionParameterFlags ...


GEOS C Structs: CellFunctionParameterFlags ...

| Up: GEOS SDK TechDocs | Up | Prev: BitmapMode ... | Next: Chars ...

CellFunctionParameterFlags

typedef ByteFlags CellFunctionParameterFlags;
#define CFPF_DIRTY 0x80 /* apps may read or change this. */
#define CFPF_NO_FREE_COUNT 0x07

CellFunctionParameters

typedef struct {
CellFunctionParameterFlags				CFP_flags;
VMFileHandle				CFP_file;						/* File containing cells */
VMBlockHandle				CFP_rowBlocks[N_ROW_BLOCKS];
} CellFunctionParameters;

This structure is used to pass specifics about a cell file to the cell library routines. Some of the data in the CellFunctionParameters structure is opaque to the application; others may be examined or changed by the application. The CellFunctionParameters structure contains the following fields:

CFP_flags
The cell library uses this byte for miscellaneous bookkeeping. When you create the structure, initialize this field to zero. There is only one flag which you should check or change; that is the flag CFPF_dirty . The cell library routines set this bit whenever they change the CellFunctionParameters structure, thus indicating that the structure ought to be resaved. After you save it, you may clear this bit.
CFP_file
This field must contain the VM file handle of the cell file. This field must be set each time you open the file.
CFP_rowBlocks
This field is an array of VM block handles, one for every existing or potential row block. The length of this array is N_ROW_BLOCKS (defined in cell.h ). When you create a cell file, initialize all of these handles to zero; do not access or change this field thereafter.

Include: cell.h

Warnings: The cell library expects the CellFunctionParameters structure to remain motionless for the duration of a call. Therefore, if you allocate it as a DB item in the cell file, you must not have the structure be an ungrouped item.

CellRange

typedef struct {
CellReference			CR_start;
CellReference			CR_end;
} CellRange;

CellReference

typedef struct {
CellRowColumn			CR_row;
CellRowColumn			CR_column;
} CellReference;

CellRowColumn

typedef WordFlags CellRowColumn;
#define CRC_ABSOLUTE				0x8000
#define CRC_VALUE				0x7fff

CharacterSet

typedef ByteEnum CharacterSet;
#define CS_BSW				0
#define CS_CONTROL 				0xff
#define CS_UI_FUNCS 				0xfe
#define VC_ISANSI 				CS_BSW
#define VC_ISCTRL 				CS_CONTROL
#define VC_ISUI 				CS_UI_FUNCS

CharFlags

typedef ByteFlags CharFlags;
#define CF_STATE_KEY				0x80
#define CF_EXTENDED 				0x10
#define CF_TEMP_ACCENT 				0x08
#define CF_FIRST_PRESS 				0x04
#define CF_REPEAT_PRESS 				0x02
#define CF_RELEASE 				0x01

| Up: GEOS SDK TechDocs | Up | Prev: BitmapMode ... | Next: Chars ...


GEOS C Structs: Chars ...

| Up: GEOS SDK TechDocs | Up | Prev: CellFunctionParameterFlags ... | Next: ChunkArrayHeader ...

Chars

typedef ByteEnum Chars;
#define C_NULL				0x0 /* NULL */
#define C_CTRL_A				0x1 /* <ctrl>-A */
#define C_CTRL_B				0x2 /* <ctrl>-B */
#define C_CTRL_C				0x3 /* <ctrl>-C */
#define C_CTRL_D				0x4 /* <ctrl>-D */
#define C_CTRL_E				0x5 /* <ctrl>-E */
#define C_CTRL_F				0x6 /* <ctrl>-F */
#define C_CTRL_G				0x7 /* <ctrl>-G */
#define C_CTRL_H				0x8 /* <ctrl>-H */
#define C_TAB				0x9 /* TAB */
#define C_LINEFEED				0xa /* LINE FEED */
#define C_CTRL_K				0xb /* <ctrl>-K */
#define C_CTRL_L				0xc /* <ctrl>-L */
#define C_ENTER				0xd /* ENTER or CR */
#define C_SHIFT_OUT				0xe /* <ctrl>-N */
#define C_SHIFT_IN				0xf /* <ctrl>-O */
#define C_CTRL_P				0x10 /* <ctrl>-P */
#define C_CTRL_Q				0x11 /* <ctrl>-Q */
#define C_CTRL_R				0x12 /* <ctrl>-R */
#define C_CTRL_S				0x13 /* <ctrl>-S */
#define C_CTRL_T				0x14 /* <ctrl>-T */
#define C_CTRL_U				0x15 /* <ctrl>-U */
#define C_CTRL_V				0x16 /* <ctrl>-V */
#define C_CTRL_W				0x17 /* <ctrl>-W */
#define C_CTRL_X				0x18 /* <ctrl>-X */
#define C_CTRL_Y				0x19 /* <ctrl>-Y */
#define C_CTRL_Z				0x1a /* <ctrl>-Z */
#define C_ESCAPE				0x1b /* ESC */
#define C_NULL_WIDTH				0x19 /* null width character */
#define C_GRAPHIC				0x1a /* Graphic in text. */
#define C_THINSPACE				0x1b /* 1/4 width space */
#define C_ENSPACE				0x1c /* En-space, fixed width */
#define C_EMSPACE				0x1d /* Em-space, fixed width. */
#define C_NONBRKHYPHEN				0x1e /* Non breaking hyphen. */
#define C_OPTHYPHEN				0x1f /* Optional hyphen, only drawn at eol */
#define C_SPACE				` '
#define C_EXCLAMATION				`!'
#define C_QUOTE				`"'
#define C_NUMBER_SIGN				`#'
#define C_DOLLAR_SIGN				`$'
#define C_PERCENT				`%'
#define C_AMPERSAND				`&'
#define C_SNG_QUOTE				0x27
#define C_LEFT_PAREN				`('
#define C_RIGHT_PAREN				`)'
#define C_ASTERISK				`*'
#define C_PLUS				`+'
#define C_COMMA				`,'
#define C_MINUS				`-'
#define C_PERIOD				 '.'
#define C_SLASH				`/'
#define C_ZERO				`0'
#define C_ONE				`1'
#define C_TWO				`2'
#define C_THREE 				`3'
#define C_FOUR 				`4'
#define C_FIVE 				`5'
#define C_SIX 				`6'
#define C_SEVEN 				`7'
#define C_EIGHT 				`8'
#define C_NINE 				`9'
#define C_COLON 				`:'
#define C_SEMICOLON 				`;'
#define C_LESS_THAN 				`<'
#define C_EQUAL 				`='
#define C_GREATER_THAN 				`>'
#define C_QUESTION_MARK 				`?'
#define C_AT_SIGN				0x40
#define C_CAP_A 				`A'
#define C_CAP_B 				`B'
#define C_CAP_C 				`C'
#define C_CAP_D 				`D'
#define C_CAP_E 				`E'
#define C_CAP_F 				`F'
#define C_CAP_G 				`G'
#define C_CAP_H 				`H'
#define C_CAP_I 				`I'
#define C_CAP_J 				`J'
#define C_CAP_K 				`K'
#define C_CAP_L 				`L'
#define C_CAP_M 				`M'
#define C_CAP_N 				`N'
#define C_CAP_O 				`O'
#define C_CAP_P 				`P'
#define C_CAP_Q 				`Q'
#define C_CAP_R 				`R'
#define C_CAP_S 				`S'
#define C_CAP_T 				`T'
#define C_CAP_U 				`U'
#define C_CAP_V 				`V'
#define C_CAP_W 				`W'
#define C_CAP_X 				`X'
#define C_CAP_Y 				`Y'
#define C_CAP_Z 				`Z'
#define C_LEFT_BRACKET				`['
#define C_BACKSLASH				0x5c
#define C_RIGHT_BRACKET				`]'
#define C_ASCII_CIRCUMFLEX				`^'
#define C_UNDERSCORE				`_'
#define C_BACKQUOTE				``'
#define C_SMALL_A				`a'
#define C_SMALL_B				`b'
#define C_SMALL_C				`c'
#define C_SMALL_D 				`d'
#define C_SMALL_E 				`e'
#define C_SMALL_F 				`f'
#define C_SMALL_G 				`g'
#define C_SMALL_H 				`h'
#define C_SMALL_I 				`i'
#define C_SMALL_J 				`j'
#define C_SMALL_K 				`k'
#define C_SMALL_L 				`l'
#define C_SMALL_M 				`m'
#define C_SMALL_N 				`n'
#define C_SMALL_O 				`o'
#define C_SMALL_P 				`p'
#define C_SMALL_Q 				`q'
#define C_SMALL_R 				`r'
#define C_SMALL_S 				`s'
#define C_SMALL_T 				`t'
#define C_SMALL_U 				`u'
#define C_SMALL_V 				`v'
#define C_SMALL_W 				`w'
#define C_SMALL_X 				`x'
#define C_SMALL_Y 				`y'
#define C_SMALL_Z 				`z'
#define C_LEFT_BRACE 				`{'
#define C_VERTICAL_BAR 				`|'
#define C_RIGHT_BRACE 				`}'
#define C_ASCII_TILDE 				`~'
#define C_DELETE				0x7f
#define C_UA_DIERESIS				0x80
#define C_UA_RING				0x81
#define C_UC_CEDILLA				0x82
#define C_UE_ACUTE				0x83
#define C_UN_TILDE				0x84
#define C_UO_DIERESIS				0x85
#define C_UU_DIERESIS				0x86
#define C_LA_ACUTE				0x87
#define C_LA_GRAVE				0x88
#define C_LA_CIRCUMFLEX				0x89
#define C_LA_DIERESIS				0x8a
#define C_LA_TILDE				0x8b
#define C_LA_RING				0x8c
#define C_LC_CEDILLA				0x8d
#define C_LE_ACUTE				0x8e
#define C_LE_GRAVE				0x8f
#define C_LE_CIRCUMFLEX				0x90
#define C_LE_DIERESIS				0x91
#define C_LI_ACUTE				0x92
#define C_LI_GRAVE				0x93
#define C_LI_CIRCUMFLEX				0x94
#define C_LI_DIERESIS				0x95
#define C_LN_TILDE				0x96
#define C_LO_ACUTE				0x97
#define C_LO_GRAVE				0x98
#define C_LO_CIRCUMFLEX				0x99
#define C_LO_DIERESIS				0x9a
#define C_LO_TILDE				0x9b
#define C_LU_ACUTE				0x9c
#define C_LU_GRAVE				0x9d
#define C_LU_CIRCUMFLEX				0x9e
#define C_LU_DIERESIS				0x9f
#define C_DAGGER				0xa0
#define C_DEGREE				0xa1
#define C_CENT				0xa2
#define C_STERLING				0xa3
#define C_SECTION				0xa4
#define C_BULLET				0xa5
#define C_PARAGRAPH				0xa6
#define C_GERMANDBLS				0xa7
#define C_REGISTERED				0xa8
#define C_COPYRIGHT				0xa9
#define C_TRADEMARK				0xaa
#define C_ACUTE				0xab
#define C_DIERESIS				0xac
#define C_NOTEQUAL				0xad
#define C_U_AE				0xae
#define C_UO_SLASH				0xaf
#define C_INFINITY				0xb0
#define C_PLUSMINUS				0xb1
#define C_LESSEQUAL				0xb2
#define C_GREATEREQUAL				0xb3
#define C_YEN				0xb4
#define C_L_MU				0xb5
#define C_L_DELTA				0xb6
#define C_U_SIGMA				0xb7
#define C_U_PI				0xb8
#define C_L_PI				0xb9
#define C_INTEGRAL				0xba
#define C_ORDFEMININE				0xbb
#define C_ORDMASCULINE				0xbc
#define C_U_OMEGA				0xbd
#define C_L_AE				0xbe
#define C_LO_SLASH				0xbf
#define C_QUESTIONDOWN				0xc0
#define C_EXCLAMDOWN				0xc1
#define C_LOGICAL_NOT				0xc2
#define C_ROOT				0xc3
#define C_FLORIN				0xc4
#define C_APPROX_EQUAL				0xc5
#define C_U_DELTA				0xc6
#define C_GUILLEDBLLEFT				0xc7
#define C_GUILLEDBLRIGHT				0xc8
#define C_ELLIPSIS				0xc9
#define C_NONBRKSPACE				0xca
#define C_UA_GRAVE				0xcb
#define C_UA_TILDE				0xcc
#define C_UO_TILDE				0xcd
#define C_U_OE				0xce
#define C_L_OE				0xcf
#define C_ENDASH				0xd0
#define C_EMDASH				0xd1
#define C_QUOTEDBLLEFT				0xd2
#define C_QUOTEDBLRIGHT				0xd3
#define C_QUOTESNGLEFT				0xd4
#define C_QUOTESNGRIGHT				0xd5
#define C_DIVISION				0xd6
#define C_DIAMONDBULLET				0xd7
#define C_LY_DIERESIS				0xd8
#define C_UY_DIERESIS				0xd9
#define C_FRACTION				0xda
#define C_CURRENCY				0xdb
#define C_GUILSNGLEFT				0xdc
#define C_GUILSNGRIGHT				0xdd
#define C_LY_ACUTE				0xde
#define C_UY_ACUTE				0xdf
#define C_DBLDAGGER				0xe0
#define C_CNTR_DOT				0xe1
#define C_SNGQUOTELOW				0xe2
#define C_DBLQUOTELOW				0xe3
#define C_PERTHOUSAND				0xe4
#define C_UA_CIRCUMFLEX				0xe5
#define C_UE_CIRCUMFLEX				0xe6
#define C_UA_ACUTE				0xe7
#define C_UE_DIERESIS				0xe8
#define C_UE_GRAVE				0xe9
#define C_UI_ACUTE				0xea
#define C_UI_CIRCUMFLEX				0xeb
#define C_UI_DIERESIS				0xec
#define C_UI_GRAVE				0xed
#define C_UO_ACUTE				0xee
#define C_UO_CIRCUMFLEX				0xef
#define C_LOGO				0xf0
#define C_UO_GRAVE				0xf1
#define C_UU_ACUTE				0xf2
#define C_UU_CIRCUMFLEX				0xf3
#define C_UU_GRAVE				0xf4
#define C_LI_DOTLESS				0xf5
#define C_CIRCUMFLEX				0xf6
#define C_TILDE				0xf7
#define C_MACRON				0xf8
#define C_BREVE				0xf9
#define C_DOTACCENT				0xfa
#define C_RING				0xfb
#define C_CEDILLA				0xfc
#define C_HUNGARUMLAT				0xfd
#define C_OGONEK				0xfe
#define C_CARON				0xff
/*
 * common shortcuts for low 32 codes
 */
#define C_NUL				C_NULL
#define C_STX				C_CTRL_B
#define C_ETX				C_CTRL_C
#define C_BEL				C_CTRL_G
#define C_BS				C_CTRL_H
#define C_HT				C_CTRL_I
#define C_VT				C_CTRL_K
#define C_FF				C_CTRL_L
#define C_SO				C_CTRL_N
#define C_SI				C_CTRL_O
#define C_DC1				C_CTRL_Q
#define C_DC2				C_CTRL_R
#define C_DC3				C_CTRL_S
#define C_DC4				C_CTRL_T
#define C_CAN				C_CTRL_X
#define C_EM				C_CTRL_Y
#define C_ESC				C_ESCAPE
/*
 * Some alternative names
 */
#define C_CR				C_ENTER
#define C_CTRL_M				C_ENTER
#define C_CTRL_I				C_TAB
#define C_CTRL_J				C_LINEFEED
#define C_LF				C_LINEFEED
#define C_CTRL_N				C_SHIFT_OUT
#define C_CTRL_O				C_SHIFT_IN
#define C_FS				C_ENSPACE
#define C_FIELD_SEP				C_FS
#define C_HYPHEN				C_MINUS
#define C_GRAVE				C_BACKQUOTE
#define C_PARTIAL_DIFF				C_L_DELTA
#define C_SUM				C_U_SIGMA
#define C_PRODUCT				C_U_PI
#define C_RADICAL				C_ROOT
#define C_LOZENGE				C_DIAMONDBULLET

Text characters may be represented by the standard C type char or by the GEOS type Chars. The difference shows up in debugging. If printing the value of a string as char, then the debugger will output ASCII text. If the string is treated as Chars, then the debugger will print out the constant names.

Include: char.h

| Up: GEOS SDK TechDocs | Up | Prev: CellFunctionParameterFlags ... | Next: ChunkArrayHeader ...


GEOS C Structs: ChunkArrayHeader ...

| Up: GEOS SDK TechDocs | Up | Prev: Chars ... | Next: ClipboardItemHeader ...

ChunkArrayHeader

typedef struct {
word	CAH_count;			/* # of elements in chunk array */
word	CAH_elementSize;			/* Size of each element (in bytes) */
word	CAH_curOffset;			/* For internal use only */
word	CAH_offset;			/* Offset from start of chunk to first element */
} ChunkArrayHeader;

Every chunk array begins with a ChunkArrayHeader . This structure contains information about the chunk array. Applications should never change the contents of the ChunkArrayHeader ; only the chunk array routines should do this. However, applications can examine the header if they wish.

Contents: There are four word-length fields in the ChunkArrayHeader:

CAH _count
This word contains the number of elements in the chunk array.
CAH _elementSize
This word contains the size of each element (in bytes). If the elements are variable-sized, CAH_elementSize will be zero.
CAH _curOffset
This word is used by ChunkArrayEnum() for bookkeeping.
CAH _offset
This is the offset from the start of the chunk to the first element in the array.
Chunk handles are offsets into a local memory heap. To find the current

ChunkHandle

typedef word ChunkHandle;

location of a chunk in an LMem heap, combine the segment address of the heap with the chunk handle. From this location you can read the current offset of the chunk itself.

See Also: optr, LMemDeref().

ChunkMapList

typedef struct {
word	CML_source;
word	CML_dest;
} ChunkMapList;

ClassFlags

typedef ByteFlags ClassFlags;
#define CLASSF_HAS_DEFAULT						0x80
#define CLASSF_MASTER_CLASS 						0x40
#define CLASSF_VARIANT_CLASS 						0x20
#define CLASSF_DISCARD_ON_SAVE 						0x10
#define CLASSF_NEVER_SAVED						0x08
#define CLASSF_HAS_RELOC 						0x04
#define CLASSF_C_HANDLERS 						0x02

This record is stored in the ClassStruct structure's Class_flags field. These flags are internal and may not be set or retrieved directly. See the entry on @class for more information about these flags.

ClassStruct

typedef	struct	_ClassStruct {
struct _ClassStruct *Class_superClass;		/* superclass pointer */
word		Class_masterOffset;					/* offset to master offset in chunk */
word 		Class_methodCount;					/* number of methods in this class */
word 		Class_instanceSize;					/* size of entire master group */
word 		Class_vdRelocTable;					/* offset to vardata relocation table */
word 		Class_relocTable;					/* offset to relocation table */
ClassFlags 		Class_flags;					/* a record of ClassFlags */
byte 		Class_masterMessages;					/* internal flags for optimization */
} ClassStruct;

This is the structure that defines a class. It is internal and used only very rarely by anything other than the kernel and the UI.

ClipboardItemFlags

typedef WordFlags ClipboardItemFlags;
#define CIF_QUICK				0x4000
#define TIF_NORMAL				0x0000

ClipboardItemFormat

typedef enum /* word */ {
CIF_TEXT,
CIF_GRAPHICS_STRING,
CIF_FILES,
CIF_SPREADSHEET,
CIF_INK,
CIF_GROBJ,
CIF_GEODEX,
CIF_BITMAP,
CIF_SOUND_SYNTH,
CIF_SOUND_SAMPLE
} ClipboardItemFormat;

ClipboardItemFormatID

typedef dword ClipboardItemFormatID;

ClipboardItemFormatInfo

typedef struct {
ClipboardItemFormatID				CIFI_format;
word				CIFI_extra1;
word				CIFI_extra2;
VMChain				CIFI_vmChain;
GeodeToken				CIFI_renderer;
} ClipboardItemFormatInfo;

| Up: GEOS SDK TechDocs | Up | Prev: Chars ... | Next: ClipboardItemHeader ...


GEOS C Structs: ClipboardItemHeader ...

| Up: GEOS SDK TechDocs | Up | Prev: ChunkArrayHeader ... | Next: Color ...

ClipboardItemHeader

typedef struct {
optr				CIH_owner;
ClipboardItemFlags				CIH_flags;
ClipboardItemNameBuffer				CIH_name;
word				CIH_formatCount;
optr				CIH_sourceID;
FormatArray				CIH_formats;
dword				CIH_reserved;
} ClipboardItemHeader;

ClipboardItemNameBuffer

typedef char ClipboardItemNameBuffer[CLIPBOARD_ITEM_NAME_LENGTH+1];

ClipboardQueryArgs

See ClipboardQueryItem() .

ClipboardQuickNotifyFlags

typedef WordFlags ClipboardQuickNotifyFlags;
#define CQNF_ERROR						0x8000
#define CQNF_SOURCE_EQUAL_DEST 						0x4000
#define CQNF_MOVE 						0x2000
#define CQNF_COPY 						0x1000
#define CQNF_NO_OPERATION 						0x0800
#define CQNF_UNUSED 						0x04ff

These flags give information about the success or failure of a quick transfer operation.

ClipboardQuickTransferFeedback

typedef enum {
CQTF_SET_DEFAULT,
CQTF_CLEAR_DEFAULT,
CQTF_MOVE,
CQTF_COPY,
CQTF_CLEAR
} ClipboardQuickTransferFeedback;

ClipboardQuickTransferFlags

See ClipboardStartQuickTransfer() .

ClipboardQuickTransferRegionInfo

See ClipboardStartQuickTransfer() .

ClipboardRequestArgs

See entry for ClipboardRequestItemFormat() .

CMYKTransfer

typedef struct {
byte	CMYKT_cyan[256];
byte	CMYKT_magenta[256];
byte	CMYKT_yellow[256];
byte	CMYKT_black[256];
} CMYKTransfer;

| Up: GEOS SDK TechDocs | Up | Prev: ChunkArrayHeader ... | Next: Color ...


GEOS C Structs: Color ...

| Up: GEOS SDK TechDocs | Up | Prev: ClipboardItemHeader ... | Next: ColorFlag ...

Color

typedef ByteEnum Color;
#define C_BLACK				0
#define C_BLUE 				1
#define C_GREEN 				2
#define C_CYAN 				3
#define C_RED 				4
#define C_VIOLET 				5
#define C_BROWN 				6
#define C_LIGHT_GRAY 				7
#define C_DARK_GRAY 				8
#define C_LIGHT_BLUE 				9
#define C_LIGHT_GREEN 				10
#define C_LIGHT_CYAN 				11
#define C_LIGHT_RED 				12
#define C_LIGHT_VIOLET 				13
#define C_YELLOW 				14
#define C_WHITE 				15

#define C_GRAY_0 				0x10
#define C_GRAY_7 				0x11
#define C_GRAY_13				0x12
#define C_GRAY_20				0x13
#define C_GRAY_27 				0x14
#define C_GRAY_33 				0x15
#define C_GRAY_40 				0x16
#define C_GRAY_47 				0x17
#define C_GRAY_53 				0x18
#define C_GRAY_60 				0x19
#define C_GRAY_68 				0x1a
#define C_GRAY_73 				0x1b
#define C_GRAY_80 				0x1c
#define C_GRAY_88 				0x1d
#define C_GRAY_93 				0x1e
#define C_GRAY_100 				0x1f

#define C_UNUSED_0 				0x20
#define C_UNUSED_1 				0x21
#define C_UNUSED_2 				0x22
#define C_UNUSED_3 				0x23
#define C_UNUSED_4 				0x24
#define C_UNUSED_5 				0x25
#define C_UNUSED_6 				0x26
#define C_UNUSED_7 				0x27

#define C_R0_G0_B0 				0x28
#define C_R0_G0_B1 				0x29
#define C_R0_G0_B2 				0x2a
#define C_R0_G0_B3 				0x2b
#define C_R0_G0_B4 				0x2c
#define C_R0_G0_B5 				0x2d
#define C_R0_G1_B0 				0x2e
#define C_R0_G1_B1 				0x2f
#define C_R0_G1_B2 				0x30
#define C_R0_G1_B3 				0x31
#define C_R0_G1_B4 				0x32
#define C_R0_G1_B5 				0x33
#define C_R0_G2_B0 				0x34
#define C_R0_G2_B1 				0x35
#define C_R0_G2_B2 				0x36
#define C_R0_G2_B3 				0x37
#define C_R0_G2_B4 				0x38
#define C_R0_G2_B5 				0x39

#define C_R0_G3_B0 				0x3a
#define C_R0_G3_B1 				0x3b
#define C_R0_G3_B2 				0x3c
#define C_R0_G3_B3 				0x3d
#define C_R0_G3_B4 				0x3e
#define C_R0_G3_B5 				0x3f
#define C_R0_G4_B0 				0x40
#define C_R0_G4_B1 				0x41
#define C_R0_G4_B2 				0x42
#define C_R0_G4_B3 				0x43
#define C_R0_G4_B4 				0x44
#define C_R0_G4_B5 				0x45
#define C_R0_G5_B0 				0x46
#define C_R0_G5_B1 				0x47
#define C_R0_G5_B2 				0x48
#define C_R0_G5_B3 				0x49
#define C_R0_G5_B4 				0x4a
#define C_R0_G5_B5 				0x4b

#define C_R1_G0_B0 				0x4c
#define C_R1_G0_B1 				0x4d
#define C_R1_G0_B2 				0x4e
#define C_R1_G0_B3 				0x4f
#define C_R1_G0_B4 				0x50
#define C_R1_G0_B5 				0x51
#define C_R1_G1_B0 				0x52
#define C_R1_G1_B1 				0x53
#define C_R1_G1_B2 				0x54
#define C_R1_G1_B3 				0x55
#define C_R1_G1_B4 				0x56
#define C_R1_G1_B5 				0x57
#define C_R1_G2_B0 				0x58
#define C_R1_G2_B1 				0x59
#define C_R1_G2_B2 				0x5a
#define C_R1_G2_B3 				0x5b
#define C_R1_G2_B4 				0x5c
#define C_R1_G2_B5 				0x5d

#define C_R1_G3_B0 				0x5e
#define C_R1_G3_B1 				0x5f
#define C_R1_G3_B2 				0x60
#define C_R1_G3_B3 				0x61
#define C_R1_G3_B4 				0x62
#define C_R1_G3_B5 				0x63
#define C_R1_G4_B0 				0x64
#define C_R1_G4_B1 				0x65
#define C_R1_G4_B2 				0x66
#define C_R1_G4_B3 				0x67
#define C_R1_G4_B4 				0x68
#define C_R1_G4_B5 				0x69
#define C_R1_G5_B0 				0x6a
#define C_R1_G5_B1 				0x6b
#define C_R1_G5_B2 				0x6c
#define C_R1_G5_B3 				0x6d
#define C_R1_G5_B4 				0x6e
#define C_R1_G5_B5 				0x6f

#define C_R2_G0_B0 				0x70
#define C_R2_G0_B1 				0x71
#define C_R2_G0_B2 				0x72
#define C_R2_G0_B3 				0x73
#define C_R2_G0_B4 				0x74
#define C_R2_G0_B5 				0x75
#define C_R2_G1_B0 				0x76
#define C_R2_G1_B1 				0x77
#define C_R2_G1_B2 				0x78
#define C_R2_G1_B3 				0x79
#define C_R2_G1_B4 				0x7a
#define C_R2_G1_B5 				0x7b
#define C_R2_G2_B0 				0x7c
#define C_R2_G2_B1 				0x7d
#define C_R2_G2_B2 				0x7e
#define C_R2_G2_B3 				0x7f
#define C_R2_G2_B4 				0x80
#define C_R2_G2_B5 				0x81

#define C_R2_G3_B0 				0x82
#define C_R2_G3_B1 				0x83
#define C_R2_G3_B2 				0x84
#define C_R2_G3_B3 				0x85
#define C_R2_G3_B4 				0x86
#define C_R2_G3_B5 				0x87
#define C_R2_G4_B0 				0x88
#define C_R2_G4_B1 				0x89
#define C_R2_G4_B2 				0x8a
#define C_R2_G4_B3 				0x8b
#define C_R2_G4_B4 				0x8c
#define C_R2_G4_B5 				0x8d
#define C_R2_G5_B0 				0x8e
#define C_R2_G5_B1 				0x8f
#define C_R2_G5_B2 				0x90
#define C_R2_G5_B3 				0x91
#define C_R2_G5_B4 				0x92
#define C_R2_G5_B5 				0x93

#define C_R3_G0_B0 				0x94
#define C_R3_G0_B1 				0x95
#define C_R3_G0_B2 				0x96
#define C_R3_G0_B3 				0x97
#define C_R3_G0_B4 				0x98
#define C_R3_G0_B5 				0x99
#define C_R3_G1_B0 				0x9a
#define C_R3_G1_B1 				0x9b
#define C_R3_G1_B2 				0x9c
#define C_R3_G1_B3 				0x9d
#define C_R3_G1_B4 				0x9e
#define C_R3_G1_B5 				0x9f
#define C_R3_G2_B0 				0xa0
#define C_R3_G2_B1 				0xa1
#define C_R3_G2_B2 				0xa2
#define C_R3_G2_B3 				0xa3
#define C_R3_G2_B4 				0xa4
#define C_R3_G2_B5 				0xa5

#define C_R3_G3_B0 				0xa6
#define C_R3_G3_B1 				0xa7
#define C_R3_G3_B2 				0xa8
#define C_R3_G3_B3 				0xa9
#define C_R3_G3_B4 				0xaa
#define C_R3_G3_B5 				0xab
#define C_R3_G4_B0 				0xac
#define C_R3_G4_B1 				0xad
#define C_R3_G4_B2 				0xae
#define C_R3_G4_B3 				0xaf
#define C_R3_G4_B4 				0xb0
#define C_R3_G4_B5 				0xb1
#define C_R3_G5_B0 				0xb2
#define C_R3_G5_B1 				0xb3
#define C_R3_G5_B2 				0xb4
#define C_R3_G5_B3 				0xb5
#define C_R3_G5_B4 				0xb6
#define C_R3_G5_B5 				0xb7

#define C_R4_G0_B0 				0xb8
#define C_R4_G0_B1 				0xb9
#define C_R4_G0_B2 				0xba
#define C_R4_G0_B3 				0xbb
#define C_R4_G0_B4 				0xbc
#define C_R4_G0_B5 				0xbd
#define C_R4_G1_B0 				0xbe
#define C_R4_G1_B1 				0xbf
#define C_R4_G1_B2 				0xc0
#define C_R4_G1_B3 				0xc1
#define C_R4_G1_B4 				0xc2
#define C_R4_G1_B5 				0xc3
#define C_R4_G2_B0 				0xc4
#define C_R4_G2_B1 				0xc5
#define C_R4_G2_B2 				0xc6
#define C_R4_G2_B3 				0xc7
#define C_R4_G2_B4 				0xc8
#define C_R4_G2_B5 				0xc9

#define C_R4_G3_B0 				0xca
#define C_R4_G3_B1 				0xcb
#define C_R4_G3_B2 				0xcc
#define C_R4_G3_B3 				0xcd
#define C_R4_G3_B4 				0xce
#define C_R4_G3_B5 				0xcf
#define C_R4_G4_B0 				0xd0
#define C_R4_G4_B1 				0xd1
#define C_R4_G4_B2 				0xd2
#define C_R4_G4_B3 				0xd3
#define C_R4_G4_B4 				0xd4
#define C_R4_G4_B5 				0xd5
#define C_R4_G5_B0 				0xd6
#define C_R4_G5_B1 				0xd7
#define C_R4_G5_B2 				0xd8
#define C_R4_G5_B3 				0xd9
#define C_R4_G5_B4 				0xda
#define C_R4_G5_B5 				0xdb

#define C_R5_G0_B0 				0xdc
#define C_R5_G0_B1 				0xdd
#define C_R5_G0_B2 				0xde
#define C_R5_G0_B3 				0xdf
#define C_R5_G0_B4 				0xe0
#define C_R5_G0_B5 				0xe1
#define C_R5_G1_B0 				0xe2
#define C_R5_G1_B1 				0xe3
#define C_R5_G1_B2 				0xe4
#define C_R5_G1_B3 				0xe5
#define C_R5_G1_B4 				0xe6
#define C_R5_G1_B5 				0xe7
#define C_R5_G2_B0 				0xe8
#define C_R5_G2_B1 				0xe9
#define C_R5_G2_B2 				0xea
#define C_R5_G2_B3 				0xeb
#define C_R5_G2_B4 				0xec
#define C_R5_G2_B5 				0xed
#define C_R5_G3_B0 				0xee
#define C_R5_G3_B1 				0xef
#define C_R5_G3_B2 				0xf0
#define C_R5_G3_B3 				0xf1
#define C_R5_G3_B4 				0xf2
#define C_R5_G3_B5 				0xf3
#define C_R5_G4_B0 				0xf4
#define C_R5_G4_B1 				0xf5
#define C_R5_G4_B2 				0xf6
#define C_R5_G4_B3 				0xf7
#define C_R5_G4_B4 				0xf8
#define C_R5_G4_B5 				0xf9
#define C_R5_G5_B0 				0xfa
#define C_R5_G5_B1 				0xfb
#define C_R5_G5_B2 				0xfc
#define C_R5_G5_B3 				0xfd
#define C_R5_G5_B4 				0xfe
#define C_R5_G5_B5 				0xff

#define C_LIGHT_GREY				C_LIGHT_GRAY
#define C_DARK_GREY				C_DARK_GRAY
#define C_BW_GREY				0x84

The Color enumerated type is used to encode color index values. Normally, the constant name will give a hint as to the color's composition. If an application has carried out sophisticated palette operations, however, then C_RED might actually correspond to, say, a shade of green.

Include: color.h

| Up: GEOS SDK TechDocs | Up | Prev: ClipboardItemHeader ... | Next: ColorFlag ...


GEOS C Structs: ColorFlag ...

| Up: GEOS SDK TechDocs | Up | Prev: Color ... | Next: CompSizeHintArgs ...

ColorFlag

typedef ByteEnum ColorFlag;
#define CF_INDEX			0
#define CF_GRAY			1
#define CF_SAME			2
#define CF_RGB			0x80

Several color-related commands accept colors in a variety of formats. The ColorFlag enumerated type is used to specify how the color is being described. The ColorFlag is normally used as part of a ColorQuad . See ColorQuad for information about how to interpret color specifications using ColorFlag s.

ColorMapMode

typedef ByteFlags ColorMapMode;
#define CMM_ON_BLACK 0x04				/* Set this bit if you're drawing on black */
#define CMM_MAP_TYPE 0x01				/* Either CMT_CLOSEST or CMT_DITHER) */
#define LAST_MAP_MODE				(CMM_MAP_TYPE | CMM_ON_BLACK)

This structure defines how the system will try to simulate colors not in the palette. If the map type is CMT_CLOSEST, the closest available color will be used. If the map type is CMT_DITHER, the system will mix together two or more close colors in a dithered pattern. If you will be drawing against a black background, you may wish to set the CMM_ON_BLACK flag.

ColorQuad

typedef struct {
byte 		CQ_redOrIndex;
ColorFlag	 	CQ_info;
byte 		CQ_green;
byte 		CQ_blue;
} ColorQuad;

This structure represents a color. The CQ_info field determines how the color is being described.

If the info field is CF_INDEX, then the color is being specified by its index, its place in the window's palette. The index is in the CQ_redOrIndex field; the the CQ_green and CQ_blue fields are meaningless for this specification.

If the info field is CF_RGB, then the color is specified by RGB (red, green, and blue) components. CQ_redOrIndex contains the color's red component, a number ranging from 0 to 255. The CQ_green and CQ_blue fields contain the color's green and blue components, respectively.

If the info field is CF_GRAY, then the color is being expressed as a grey scale. This is basically an optimized way of describing RGB colors where the red, green, and blue components are equal. The CQ_redOrIndex field contains the brightess, a number between 0 and 255. The CQ_green and CQ_blue fields are ignored.

When defining hatch patterns, it is possible have a CF_SAME info field. This means that the hatch lines should use the "same" color when drawing. That is, when hatching text, the text color will be used; when filling an area, the area color will be used. The CQ_redOrIndex , CQ_green , and CQ_blue fields are all ignored.

ColorQuadAsDWord

typedef dword ColorQuadAsDWord;

ColorTransfer

typedef struct {
RGBDelta				CT_data[125];
} ColorTransfer;

This structure consists of a 5x5x5 matrix of RGBDelta structures. This and be used to specify what sorts of adjustments to make to the color when displaying to a specific device. For instance, some color printers will wipe out certain colors if they try to use the amounts of ink suggested by the raw RGB values. The ColorTransfer structure thus serves to hold an array of "fudge factors" to tell the printer to use more or less ink than the raw RGB values would suggest.

ColorTransferData

typedef union {
MonoTransfer		CTD_mono;
RGBTransfer		CTD_rgb;
CMYKTransfer		CTD_cmyk;
} ColorTransferData;

ColorTransferType

typedef ByteEnum ColorTransferType;
#define CTT_MONO				 0
#define CTT_RGB 				 1
#define CTT_CMYK 				 2

CommonParameters

typedef struct {
word	CP_row;
word	CP_column;
word	CP_maxRow;
word	CP_maxColumn;
void	* CP_callback;
void	* CP_cellParams; /* ptr to an instance of SpreadsheetClass */
} CommonParameters;

CompChildFlags

typedef WordFlags CompChildFlags;
#define CCF_MARK_DIRTY				0x8000
#define CCF_REFERENCE				0x7fff
#define 		CCO_FIRST			0x0000
#define		CCO_LAST			0x7FFF
#define CCF_REFERENCE_OFFSET 0

A record used when adding, moving, or removing children in an object tree. The record has one flag and a value, as follows:

CCF_MARK_DIRTY
A flag indicating whether the object should be marked dirty at the end of the operation.
CCF_REFERENCE
A child number; when adding or moving a child, this is the child number after which the new object should be inserted. It can be any number less than 32768, or it can be either of the two constants shown above (CCO_FIRST or CCO_LAST).
| Up: GEOS SDK TechDocs | Up | Prev: Color ... | Next: CompSizeHintArgs ...


GEOS C Structs: CompSizeHintArgs ...

| Up: GEOS SDK TechDocs | Up | Prev: ColorFlag ... | Next: CustomDialogBoxFlags ...

CompSizeHintArgs

typedef struct {
            SpecWidth   CSHA_width;
            SpecHeight  CSHA_height;
            sword       CSHA_count;
} CompSizeHintArgs;
typedef WordFlags SpecWidth;
        #define SW_TYPE         0x8c00
        #define SW_DATA         0x03ff

typedef WordFlags SpecHeight;
        #define SH_TYPE         0x8c00
        #define SH_DATA         0x03ff
typedef ByteEnum SpecSizeType;
        #define SST_PIXELS                                0x0000
        #define SST_COUNT                                 0x0400
        #define SST_PCT_OF_FIELD_WIDTH    0x0800
        #define SST_PCT_OF_FIELD_HEIGHT   0x0c00
        #define SST_AVG_CHAR_WIDTHS       0x1000
        #define SST_WIDE_CHAR_WIDTHS      0x1400
        #define SST_LINES_OF_TEXT                 0x1800

        #define PCT_0   0x000
        #define PCT_5   0x033
        #define PCT_10  0x066
        #define PCT_15  0x099
        #define PCT_20  0x0cc
        #define PCT_25  0x100
        #define PCT_30  0x133
        #define PCT_35  0x166
        #define PCT_40  0x199
        #define PCT_45  0x1cc
        #define PCT_50  0x200
        #define PCT_55  0x233
        #define PCT_60  0x266
        #define PCT_65  0x299
        #define PCT_70  0x2cc
        #define PCT_75  0x300
        #define PCT_80  0x333
        #define PCT_85  0x366
        #define PCT_90  0x399
        #define PCT_95  0x3cc
        #define PCT_100 0x3ff

This structure is used to define the sizes of various UI gadgets. CSHA_width determines how the gadget's width should be calculated; CSHA_height determines how the height should be calculated; CSHA_count is the number of children in a particular line of the composite object (if that object is wrapping its children).

This example sets the initial size of the composite (probably a GenPrimary) to be half the screen's height and half the screen's width.

HINT_INITIAL_SIZE = {
    SST_PCT_OF_FIELD_WIDTH | PCT_50,
    SST_PCT_OF_FIELD_HEIGHT | PCT_50,
    0 };

This example sets the size of the composite to be 100 pixels high and 200 pixels wide.

HINT_FIXED_SIZE = {
    SST_PIXELS | 200,
    SST_PIXELS | 100,
    0 };

This example sets the composite's minimum size to be 10 average characters wide and 20 percent of the screen height tall.

HINT_MINIMUM_SIZE = {
    SST_AVG_CHAR_WIDTHS | 10,
    SST_PCT_OF_FIELD_HEIGHT | PCT_20,
    0 };

CountryType

typedef enum /* word */ {
CT_UNITED_STATES=1,
CT_CANADA,
CT_UNITED_KINGDOM,
CT_GERMANY,
CT_FRANCE,
CT_SPAIN,
CT_ITALY,
CT_DENMARK,
CT_NETHERLANDS,
} CountryType;

CRangeEnumParams

typedef struct {
            RangeEnumParams         CREP_params;
            void                    *CREP_locals;
            PCB(RANGE_ENUM_CALLBACK_RETURN_TYPE, CREP_callback,
(RangeEnumCallbackParams));
} CRangeEnumParams;

The CREP_callback routine should be declared _pascal.

CurrencyFormatFlags

typedef ByteFlags CurrencyFormatFlags;
#define CFF_LEADING_ZERO									0x20
#define CFF_SPACE_AROUND_SYMBOL 									0x10
#define CFF_USE_NEGATIVE_SIGN 									0x08
#define CFF_SYMBOL_BEFORE_NUMBER 									0x04
#define CFF_NEGATIVE_SIGN_BEFORE_NUMBER 									0x02
#define CFF_NEGATIVE_SIGN_BEFORE_SYMBOL 									0x01

| Up: GEOS SDK TechDocs | Up | Prev: ColorFlag ... | Next: CustomDialogBoxFlags ...


GEOS C Structs: CustomDialogBoxFlags ...

| Up: GEOS SDK TechDocs | Up | Prev: CompSizeHintArgs ... | Next: DataStoreIndexCallbackParams ...

CustomDialogBoxFlags

typedef WordFlags CustomDialogBoxFlags;
#define CDBF_SYSTEM_MODAL						0x8000
#define CDBF_DIALOG_TYPE						0x6000
#define CDBF_INTERACTION_TYPE						0x1e00
#define CDBF_DESTRUCTIVE_ACTION 0x0100
#define CDBF_DIALOG_TYPE_OFFSET         13
#define CDBF_INTERACTION_TYPE_OFFSET    9

CustomDialogType

typedef ByteEnum CustomDialogType;
#define CDT_QUESTION				0
#define CDT_WARNING				1
#define CDT_NOTIFICATION 				2
#define CDT_ERROR 				3

DACPlayFlags

typedef ByteFlags DACPlayFlags;
#define DACPF_CATENATE 0x80

DACReferenceByte

typedef enum {
 DACRB_NO_REFERENCE_BYTE,
 DACRB_WITH_REFERENCE_BYTE
} DACReferenceByte;

DACSampleFormat

typedef enum {
 DACSF_8_BIT_PCM,
 DACSF_2_TO_1_ADPCM,
 DACSF_3_TO_1_ADPCM,
 DACSF_4_TO_1_ADPCM
} DACSampleFormat;

This structure specifies what sort of sampling should be used when recording or playing a sampled sound.

DashPairArray

See: LineStyle

DataStoreCreateParams

typedef struct {
TCHAR				*DSCP_name;
DataStoreFlags				DSCP_flags;
FieldDescriptor				*DSCP_keyList;
word				DSCP_keyCount;
optr				DSCP_notifObject;
DataStoreOpenFlags				DSCP_openFlags;
} DataStoreCreateParams;

Attributes to specify when creating a new datastore.

DatatStoreDataError

typedef enum {
DSDE_NO_ERROR,
DSDE_INVALID_TOKEN,
DSDE_DATASTORE_LOCKED,
DSDE_ACCESS_DENIED,
DSDE_WRITE_ERROR,
DSDE_MEMORY_FULL,
DSDE_INVALID_RECORD_ID,
DSDE_INVALID_FIELD_ID,
DSDE_INVALID_FIELD_NAME,
DSDE_INVALID_FIELD_TYPE,
DSDE_INVALID_FIELD_CATEGORY,
DSDE_INVALID_FIELD_FLAGS,
DSDE_RECORD_BUFFER_NOT_EMPTY,
DSDE_RECORD_BUFFER_EMPTY,
DSDE_INVALID_FIELD_DATA,
DSDE_DATASTORE_READ_ONLY,
DSDE_INVALID_RECORD_NUMBER,
DSDE_INVALID_DATA_SIZE,
DSDE_FIELD_DOES_NOT_EXIST,
DSDE_RECORD_READ_ONLY,
DSDE_RECORD_NOT_FOUND,
DSDE_READ_ERROR,
DSDE_EXCEEDS_BUFFER_SIZE,
DSDE_TOO_MANY_FIELDS,
DSDE_RECORD_IN_USE,
DSDE_INVALID_BLOCK_HANDLE,
DSDE_INVALID_CHUNK_HANDLE,
DSDE_RECORD_LOCKED,
} DataStoreDataError;

Error values returned by various DataStore routines.

DatatStoreError

typedef enum {
DSE_NO_ERROR,
DSE_INVALID_TOKEN,
DSE_DATASTORE_LOCKED,
DSE_ACCESS_DENIED,
DSE_WRITE_ERROR,
DSE_MEMORY_FULL,
DSE_INVALID_RECORD_ID,
DSE_INVALID_NAME,
DSE_INVALID_KEY_LIST,
DSE_INVALID_FLAGS,
DSE_DUPLICATE_FIELD_NAME,	    /* duplicate field in create params */
DSE_CREATE_ERROR,
DSE_OPEN_ERROR,
DSE_CLOSE_ERROR,
DSE_UPDATE_ERROR,
DSE_DATASTORE_EXISTS,
DSE_DATASTORE_NOT_FOUND,
DSE_DATASTORE_ALREADY_OPEN,
DSE_PROTOCOL_ERROR,
DSE_CLOSE_WITH_LOCKED_RECORD,
DSE_INVALID_DATASTORE_FILE,
DSE_PRIVATE_DATASTORE,
DSE_BAD_SEARCH_PARAMS,
DSE_CANNOT_SET_NEXT_RECORD_ID_SMALLER,
DSE_NO_MATCH_FOUND,
DSE_NO_MORE_RECORDS,	    /* last record reached */
} DataStoreError;

Error values returned by various DataStore routines.

| Up: GEOS SDK TechDocs | Up | Prev: CompSizeHintArgs ... | Next: DataStoreIndexCallbackParams ...


GEOS C Structs: DataStoreIndexCallbackParams ...

| Up: GEOS SDK TechDocs | Up | Prev: CustomDialogBoxFlags ... | Next: DateTimeFormat ...

DataStoreIndexCallbackParams

typedef struct {
FieldID			DSICP_indexField;
SortOrder			DSICP_sortOrder;
void			*DSICP_cbData;
RecordHeader			*DSICP_rec1;
RecordHeader			*DSICP_rec2;
} DataStoreIndexCallbackParams;

Set of parameters caller passes to callback in DataStoreBuildIndex() .

DatatStoreStructureError

typedef enum {
DSSE_NO_ERROR,
DSSE_INVALID_TOKEN,
DSSE_DATASTORE_LOCKED,
DSSE_ACCESS_DENIED,
DSSE_WRITE_ERROR,
DSSE_MEMORY_FULL,
DSSE_TOO_MANY_FIELDS,
DSSE_INVALID_FIELD_ID,
DSSE_INVALID_FIELD_NAME,
DSSE_INVALID_FIELD_TYPE,
DSSE_INVALID_FIELD_CATEGORY,
DSSE_INVALID_FIELD_FLAGS,
DSSE_RECORD_BUFFER_NOT_EMPTY,
DSSE_FIELD_NAME_TOO_LONG,
DSSE_FIELD_NAME_EXISTS,
DSSE_FIELD_NOT_FOUND,
DSSE_TIME_STAMP_CANNOT_BE_ADDED,
DSSE_TIME_STAMP_CANNOT_BE_DELETED,
DSSE_INVALID_SORT_ORDER,
DSSE_NO_RECORDS_IN_DATASTORE,
DSSE_PARTIAL_INDEX_RETURNED,
DSSE_DELETE_PRIMARY_KEY,
DSSE_INDEX_RECORD_NUMBER_LIMIT_EXCEEDED,
} DataStoreStructureError;

Error values returned by various DataStore routines.

DataStoreFlags

typedef WordFlags DataStoreFlags;
#define DSF_PRIVATE				0x8000
#define DSF_TIMESTAMP				0x4000
#define DSF_BACKUP				0x2000
#define DSF_ARCHIVE				0x1000
#define DSF_NO_PRIMARY_KEY				0x1000

Flags passed when creating a datastore.

DataStoreChangeNotification

typedef struct {
TCHAR				DSCN_name[FILE_LONGNAME_BUFFER_SIZE];
DataStoreChangeType				DSCN_action;
RecordID				DSCN_record;
FieldID				DSCN_field;
} DataStoreChangeNotification;

Structure sent with MSG_META_NOTIFY_WITH_DATA_BLOCK when the datastore has been modified.

DataStoreChangeType

typedef enum {
DSCT_RECORD_ADDED,
DSCT_RECORD_CHANGED,
DSCT_RECORD_DELETED,
DSCT_FIELD_ADDED,
DSCT_FIELD_RENAMED,
DSCT_FIELD_DELETED,
DSCT_DATASTORE_CHANGED,
DSCT_NAME_CHANGED,
DSCT_NEXT_RECORD_ID_CHANGED,
} DataStoreChangeType;

Datastore change notifications; see DataStoreChangeNotification above.

DataStoreDate

typedef struct {
word	DSD_year;
byte	DSD_month;
byte	DSD_day;
} DataStoreDate;

Structure used to get and set data for date fields in a datastore.

DataStoreOpenFlags

typedef ByteFlags DataStoreOpenFlags;
#define DSOF_EXCLUSIVE				0x80

Flag passed when opening a datastore; gives exclusive access to the caller.

DataStoreRecordEnumFlags

typedef WordFlags DataStoreRecordEnumFlags;
#define DSREF_BACKWARDS				0x8000
#define DSREF_START_AT_END	0x4000

Flags passed in DataStoreRecordEnum() .

DataStoreTime

typedef struct {
byte	DST_hour;
byte	DST_minute;
byte	DST_second;
} DataStoreTime;

Structure used to get and set data for time fields in a datastore.

| Up: GEOS SDK TechDocs | Up | Prev: CustomDialogBoxFlags ... | Next: DateTimeFormat ...


GEOS C Structs: DateTimeFormat ...

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreIndexCallbackParams ... | Next: DiskFindResult ...

DateTimeFormat

typedef enum /* word */ {
DTF_LONG,			/* Thursday, 7th August, 1997 */
DTF_LONG_CONDENSED,		/* Thu, 7 Aug, 1997           */
DTF_LONG_NO_WEEKDAY,		/* 7th August, 1997           */
DTF_LONG_NO_WEEKDAY_CONDENSED,	/* 7 Aug, 1997                */
DTF_SHORT,			/* 7.8.97                     */
DTF_ZERO_PADDED_SHORT,		/* 07.08.97                   */
DTF_MD_LONG,			/* Thursday, 7th August       */
DTF_MD_LONG_NO_WEEKDAY,		/* 7th August                 */
DTF_MD_SHORT,			/* 7.8                        */
DTF_MY_LONG,			/* August 1997                */
DTF_MY_SHORT,			/* 8.97                       */
DTF_MONTH,			/* August                     */
DTF_WEEKDAY,			/* Thursday                   */
DTF_HMS,			/* 02:05:06                   */
DTF_HM,				/* 02:05                      */
DTF_H,				/* 02                         */
DTF_MS,				/* 5:06                       */
DTF_HMS_24HOUR,			/* 14:05:06                   */
DTF_HM_24HOUR,			/* 14:05                      */
} DateTimeFormat;

DayOfTheWeek

typedef enum {
DOTW_SUNDAY,
DOTW_MONDAY,
DOTW_TUESAY,
DOTW_WEDNESDAY,
DOTW_THURSDAY,
DOTW_FRIDAY,
DOTW_SATURDAY
} DayOfTheWeek;

This enumerated type is used in the TimerDateAndTime structure.

DBGroup

typedef word DBGroup;

This is the handle of a DB group. It is the VM handle of a DB group block. DB group handles do not change when a file is copied, or when it is closed and reopened.

DBGroupAndItem

typedef	dword DBGroupAndItem;

This is a dword which contains the group and item handles of a database item. The high word is the item's Group handle; the low word is the item's Item handle.

Macros are provided to create and parse the DBGroupAndItem :

DBCombineGroupAndItem()
Creates a DBGroupAndItem from given group and item handles.
DBCombineGroupAndItem(group, item);
DBExtractGroupFromGroupAndItem()
Extracts the DBGroup from a given DBGroupAndItem .
DBExtractGroupFromGroupAndItem(groupAndItem);
DBExtractItemFromGroupAndItem()
Extracts the DBItem from a given DBGroupAndItem .
DBExtractItemFromGroupAndItem(groupAndItem);

Include: geos.h

DBItem

typedef word DBItem;

This is the handle of a DB item. The DBItem and DBGroup together uniquely identify a DB item in a specified file.

DBReturn

typedef struct {
word	DBR_group;
word	DBR_item;
word	unused1;
word	unused2;
} DBReturn;

DefaultPrintSizes

typedef struct {
word	paperWidth;
word	paperHeight;
word	documentWidth;
word	documentHeight;
} DefaultPrintSizes;

DevicePresent

typedef enum /* word */ {
DP_NOT_PRESENT=0xffff,
DP_CANT_TELL=0,
DP_PRESENT=1,
DP_INVALID_DEVICE=0xfffe
} DevicePresent;

DirPathInfo

See FileEnum() .

DiskCopyCallback

See DiskCopy() .

DiskCopyError

typedef enum /* word */ {
ERR_DISKCOPY_INSUFFICIENT_MEM=0xd0,
ERR_CANT_COPY_FIXED_DISKS,
ERR_CANT_READ_FROM_SOURCE,
ERR_CANT_WRITE_TO_DEST,
ERR_INCOMPATIBLE_FORMATS,
ERR_OPERATION_CANCELLED,
ERR_CANT_FORMAT_DEST,
} DiskCopyError;

| Up: GEOS SDK TechDocs | Up | Prev: DataStoreIndexCallbackParams ... | Next: DiskFindResult ...


GEOS C Structs: DiskFindResult ...

| Up: GEOS SDK TechDocs | Up | Prev: DateTimeFormat ... | Next: DosNoDotFileName ...

DiskFindResult

See DiskFind() .

DiskHandle

typedef Handle DiskHandle;

DiskInfoStruct

typedef struct {
word		DIS_blockSize;
sdword		DIS_freeSpace;
sdword		DIS_totalSpace;
VolumeName		DIS_name;
} DiskInfoStruct;

DiskRestoreError

See DiskRestore() .

DisplayAspectRatio

typedef ByteEnum DisplayAspectRatio;
#define DAR_NORMAL				0
#define DAR_SQUISHED				1
#define DAR_VERY_SQUISHED				2

DisplayClass

typedef ByteEnum DisplayClass;
#define DC_TEXT				0
#define DC_GRAY_1 				1
#define DC_GRAY_2 				2
#define DC_GRAY_4 				3
#define DC_GRAY_8 				4
#define DC_COLOR_2 				5
#define DC_COLOR_4 				6
#define DC_COLOR_8                              7
#define DC_CF_RGB 				8

DisplaySize

typedef ByteEnum DisplaySize;
#define DS_TINY				0
#define DS_STANDARD				1
#define DS_LARGE				2
#define DS_HUGE				3

DisplayType

typedef ByteFlags DisplayType;
#define DT_DISP_SIZE					0xc0
#define DT_DISP_ASPECT_RATIO					0x30
#define DT_DISP_CLASS					0x0f

DistanceUnit

typedef ByteEnum DistanceUnit;
#define DU_POINTS							 0
#define DU_INCHES 							 1
#define DU_CENTIMETERS							 2
#define DU_MILLIMETERS 							 3
#define DU_PICAS 							 4
#define DU_EUR_POINTS 							 5
#define DU_CICEROS							 6
#define DU_POINTS_OR_MILLIMETERS 							 7
#define DU_INCHES_OR_CENTIMETERS 							 8
#define LOCAL_DISTANCE_BUFFER_SIZE 							32

DocQuitStatus

typedef enum /* word */ {
 DQS_OK,
 DQS_CANCEL,
 DQS_DELAYED,
 DQS_SAVE_ERROR
} DocQuitStatus;

DocumentSize

typedef struct {
int	leftMargin;
int	topMargin;
int	width;
int	height;
} DocumentSize;

DosCodePage

typedef enum /* word */ {
CODE_PAGE_US=437,
CODE_PAGE_MULTILINGUAL=850,
CODE_PAGE_PORTUGUESE=860,
CODE_PAGE_CANADIAN_FRENCH=863,
CODE_PAGE_NORDIC=865
} DosCodePage;

DosDotFileName

typedef char DosDotFileName[DOS_DOT_DOS_FILE_NAME_SIZE];

DosExecFlags

typedef ByteFlags DosExecFlags;
#define DEF_PROMPT					0x80			/* prompt user to return to GEOS */
#define DEF_FORCED_SHUTDOWN					0x40			/* force shutdown; no abort */
#define DEF_INTERACTIVE					0x20			/* program is interactive shell */

Flags used with DosExec() . DosExec() executes a DOS program based on these flags.

DosFileInfoStruct

typedef struct {
 byte 	DFIS_attributes;
 dword 	DFIS_modTimeDate;
 dword 	DFIS_fileSize;
 char 	DFIS_name[DOS_DOT_FILE_NAME_LENGTH_ZT];
 word 	DFIS_pathInfo;
} DosFileInfoStruct;

| Up: GEOS SDK TechDocs | Up | Prev: DateTimeFormat ... | Next: DosNoDotFileName ...


GEOS C Structs: DosNoDotFileName ...

| Up: GEOS SDK TechDocs | Up | Prev: DiskFindResult ... | Next: EndOfSongFlags ...

DosNoDotFileName

typedef char DosNoDotFileName[DOS_NO_DOT_DOS_FILE_NAME_SIZE];

DrawMask

typedef byte DrawMask[8];

The graphics system uses this structure for defining custom draw masks.

DriveType

typedef ByteEnum DriveType;
#define DRIVE_5_25				0
#define DRIVE_3_5				1
#define DRIVE_FIXED				2
#define DRIVE_RAM				3
#define DRIVE_CD_ROM				4
#define DRIVE_8				5
#define DRIVE_UNKNOWN				0xf
} DriveType;

Several routines (in particular, DriveGetStatus() ) provide information about drives used by the computer running GEOS. These routines return a member of the DriveTypes enumerated type. Note that while the type is byte-length, all of the values are guaranteed to fit in four bits; thus, routines like DriveGetStatus() can return a DriveTypes value in the low four bits and other flags in the high four bits of a single byte.

DriverAttrs

typedef WordFlags DriverAttrs;
#define DA_FILE_SYSTEM						0x8000
#define DA_CHARACTER						0x4000
#define DA_HAS_EXTENDED_INFO						0x2000

This record contains flags that indicate a given driver's attributes. This record is stored in the driver's DriverInfoStruct structure.

DriverExtendedInfoStruct

typedef struct {
DriverInfoStruct			DEIS_common;			/* The base driver info structure */
MemHandle			DEIS_resource;			/* Handle of driver's DriverExtendedInfo
                         * table. */
} DriverExtendedInfoStruct;

This structure is used by Preferences to locate the names of devices supported by a particular driver.

DriverExtendedInfoTable

typedef struct {
LMemBlockHeader			DEIT_common;
word			DEIT_numDevices;
ChunkHandle			DEIT_ChunkHandle;
word			DEIT_infoTable;
} DriverExtendedInfoTable;

DriverInfoStruct

typedef struct {
 void (*DIS_strategy)();							/* Pointer to strategy routine */
 DriverAttrs		DIS_driverAttributes;					/* driver's attribute flags */
 DriverType		DIS_driverType;					/* driver's type */
} DriverInfoStruct;

This structure defines the characteristics of a particular driver. In general, applications will not need to access this structure unless they use a driver directly.

DriverType

typedef enum {
DRIVER_TYPE_VIDEO = 1,					/* Video drivers */
DRIVER_TYPE_INPUT,					/* Input (keyboard, mouse) drivers */
DRIVER_TYPE_MASS_STORAGE,					/* Disk/Drive drivers */
DRIVER_TYPE_STREAM,					/* Stream and port drivers */
DRIVER_TYPE_FONT,					/* Font drivers */
DRIVER_TYPE_OUTPUT,					/* Output (not video and printer) drivers */
DRIVER_TYPE_LOCALIZATION,					/* Localization drivers */
DRIVER_TYPE_FILE_SYSTEM,					/* File system drivers */
DRIVER_TYPE_PRINTER,					/* Printer drivers */
DRIVER_TYPE_SWAP,					/* Swap drivers */
DRIVER_TYPE_POWER_MANAGEMENT,					/* Power management drivers */
DRIVER_TYPE_TASK_SWITCH,					/* Task switch drivers */
DRIVER_TYPE_NETWORK					/* Network file system drivers */
} DriverType;

This enumerated type has one value for each type of driver in the system. It is used primarily with GeodeUseDriver() and its associated routines. Each driver stores its type in its DriverInfoStruct structure.

DWFixed

typedef struct {
word WWF_frac;
dword WWF_int;
} DWFixed;

dword

typedef unsigned long dword;

DWordFlags

typedef dword DWordFlags;

ElementArrayHeader

typedef struct {
ChunkArrayHeader			EAH_meta;			/* chunk array header structure */
word			EAH_freePtr;			/* First free element */
} ElementArrayHeader;

Every element array must begin with an ElementArrayHeader . Since element arrays are special kinds of chunk arrays, the ElementArrayHeader must itself begin with a ChunkArrayHeader . The structure contains one additional field, EAH _freePtr . This is used to keep track of the freed elements in the element array. Applications should not examine or change this field.

| Up: GEOS SDK TechDocs | Up | Prev: DiskFindResult ... | Next: EndOfSongFlags ...


GEOS C Structs: EndOfSongFlags ...

| Up: GEOS SDK TechDocs | Up | Prev: DosNoDotFileName ... | Next: EvalErrorData ...

EndOfSongFlags

typedef ByteFlags EndOfSongFlags;
        #define EOSF_UNLOCK 0x0080 /* unlock block at EOS ? */
        #define EOSF_DESTROY 0x0040 /* destroy block at EOS ? */

        #define UNLOCK_ON_EOS EOSF_UNLOCK
        #define DESTROY_ON_EOS EOSF_DESTROY

These flags determine what should be done with the memory block which stores a song structure after the song is done playing.

EntryPointRelocation

typedef struct {
char	EPR_geodeName[GEODE_NAME_SIZE];
word	EPR_entryNumber;
} EntryPointRelocation;

EnvelopeOrientation

typedef ByteEnum EnvelopeOrientation;
#define EO_PORTAIT_LEFT				0x00
#define EO_PORTAIT_RIGHT				0x01
#define EO_LANDSCAPE_UP				0x02
#define EO_LANDSCAPE_DOWN				0x03

EnvelopePath

typedef ByteEnum EnvelopePath;
#define EP_LEFT				0x00
#define EP_CENTER				0x01
#define EP_RIGHT				0x02

Errors

#define ERROR_UNSUPPORTED_FUNCTION										1
#define ERROR_FILE_NOT_FOUND 										2
#define ERROR_PATH_NOT_FOUND 										3
#define ERROR_TOO_MANY_OPEN_FILES 										4
#define ERROR_ACCESS_DENIED 										5
#define ERROR_INSUFFICIENT_MEMORY 										8
#define ERROR_INVALID_VOLUME 										15
#define ERROR_IS_CURRENT_DIRECTORY 										16
#define ERROR_DIFFERENT_DEVICE 										17
#define ERROR_NO_MORE_FILES 										18
#define ERROR_WRITE_PROTECTED 										19
#define ERROR_UNKNOWN_VOLUME 										20
#define ERROR_DRIVE_NOT_READY 										21
#define ERROR_CRC_ERROR 										23
#define ERROR_SEEK_ERROR 										25
#define ERROR_UNKNOWN_MEDIA 										26
#define ERROR_SECTOR_NOT_FOUND 										27
#define ERROR_WRITE_FAULT 										29
#define ERROR_READ_FAULT 										30
#define ERROR_GENERAL_FAILURE 										31
#define ERROR_SHARING_VIOLATION 										32
#define ERROR_ALREADY_LOCKED 										33
#define ERROR_SHARING_OVERFLOW 										36
#define ERROR_SHORT_READ_WRITE 										128
#define ERROR_INVALID_LONGNAME 										129
#define ERROR_FILE_EXISTS 										130
#define ERROR_DOS_EXEC_IN_PROGRESS 										131
#define ERROR_FILE_IN_USE 										132
#define ERROR_ARGS_TOO_LONG 										133
#define ERROR_DISK_UNAVAILABLE 										134
#define ERROR_DISK_STALE 										135
#define ERROR_FILE_FORMAT_MISMATCH 										136
#define ERROR_CANNOT_MAP_NAME 										137
#define ERROR_DIRECTORY_NOT_EMPTY 										138
#define ERROR_ATTR_NOT_SUPPORTED 										139
#define ERROR_ATTR_NOT_FOUND 										140
#define ERROR_ATTR_SIZE_MISMATCH 										141
#define ERROR_ATTR_CANNOT_BE_SET 										142
#define ERROR_CANNOT_MOVE_DIRECTORY 										143
#define ERROR_PATH_TOO_LONG 										144
#define ERROR_ARGS_INVALID 										145
#define ERROR_CANNOT_FIND_COMMAND_INTERPRETER 										146
#define ERROR_NO_TASK_DRIVER_LOADED 										147

ErrorCheckingFlags

typedef WordFlags ErrorCheckingFlags;
#define ECF_REGION					0x8000
#define ECF_HEAP_FREE_BLOCKS 					0x4000
#define ECF_LMEM_INTERNAL 					0x2000
#define ECF_LMEM_FREE_AREAS 					0x1000
#define ECF_LMEM_OBJECT 					0x0800
#define ECF_BLOCK_CHECKSUM 					0x0400
#define ECF_GRAPHICS 					0x0200
#define ECF_SEGMENT 					0x0100
#define ECF_NORMAL 					0x0080
#define ECF_VMEM 					0x0040
#define ECF_APP 					0x0020
#define ECF_LMEM_MOVE 					0x0010
#define ECF_UNLOCK_MOVE 					0x0008
#define ECF_VMEM_DISCARD 					0x0004

Error checking flags are used when setting the system's error-checking level with SysSetECLevel() . The flags above may be individually set or cleared. It is important to use error checking when debugging; it can help catch obscure bugs that might otherwise go unnoticed until after a product ships.

| Up: GEOS SDK TechDocs | Up | Prev: DosNoDotFileName ... | Next: EvalErrorData ...


GEOS C Structs: EvalErrorData ...

| Up: GEOS SDK TechDocs | Up | Prev: EndOfSongFlags ... | Next: FALSE ...

EvalErrorData

typedef struct {
byte		EED_errorCode;				/* ParserScannerEvaluatorError */
} EvalErrorData;

EvalFlags

typedef ByteFlags EvalFlags;
#define EF_MAKE_DEPENDENCIES						0x80
#define EF_ONLY_NAMES 						0x40
#define EF_KEEP_LAST_CELL 						0x20
#define EF_NO_NAMES 						0x10
#define EF_ERROR_PUSHED 						0x08
#define EVAL_MAX_NESTED_LEVELS 32

EvalFunctionData

typedef struct {
FunctionID		EFD_functionID;
word		EFD_nArgs;
} EvalFunctionData;

EvalNameData

typedef struct {
word 	END_name;
} EvalNameData;

EvalOperatorData

typedef struct {
OperatorType		EOD_opType;
} EvalOperatorData;

EvalParameters

typedef struct {
CommonParameters				EP_common;
EvalFlags				EP_flags;
word				EP_fpStack;
word				EP_depHandle;
word				EP_nestedLevel;
dword				EP_nestedAddresses[EVAL_MAX_NESTED_LEVELS];
} EvalParameters;

EvalRangeData

typedef struct {
CellReference			ERD_firstCell;
CellReference			ERD_lastCell;
} EvalRangeData;

EvalStackArgumentData

typedef union {
EvalStringData			ESAD_string;
EvalRangeData			ESAD_range;
EvalErrorData			ESAD_error;
} EvalStackArgumentData;

EvalStackArgumentType

typedef ByteFlags EvalStackArgumentType;
#define ESAT_EMPTY					0x80
#define ESAT_ERROR 					0x40
#define ESAT_RANGE 					0x20
#define ESAT_STRING 					0x10
#define ESAT_NUMBER 					0x08
#define ESAT_NUM_TYPE 					0x03
#define ESAT_TOP_OF_STACK 					0
#define ESAT_NAME 					(ESAT_RANGE | ESAT_STRING)
#define ESAT_FUNCTION 					(ESAT_NUMBER | ESAT_STRING)

EvalStackOperatorData

typedef union {
EvalOperatorData			ESOD_operator;
EvalFunctionData			ESOD_function;
} EvalStackOperatorData;

EvalStackOperatorType

typedef ByteEnum EvalStackOperatorType;
#define ESOT_OPERATOR				0
#define ESOT_FUNCTION 				1
#define ESOT_OPEN_PAREN 				2
#define ESOT_TOP_OF_STACK 				3

EvalStringData

typedef struct {
word	ESD_length;
} EvalStringData;

EventHandle

typedef Handle		EventHandle;

ExitFlags

typedef ByteFlags ExitFlags;
#define EF_PANIC				0x80
#define EF_RUN_DOS				0x40
#define EF_OLD_EXIT				0x20
#define EF_RESET				0x10
#define EF_RESTART				0x08

ExportControlFeatures

typedef ByteFlags ExportControlFeatures;
#define EXPORTCF_BASIC						0x01

ExportControlToolboxFeatures

typedef ByteFlags ExportControlToolboxFeatures;
#define EXPORTCTF_DIALOG_BOX						0x01

| Up: GEOS SDK TechDocs | Up | Prev: EndOfSongFlags ... | Next: FALSE ...


GEOS C Structs: FALSE ...

| Up: GEOS SDK TechDocs | Up | Prev: EvalErrorData ... | Next: FileAttrs ...

FALSE

#define FALSE		 0
#define TRUE		(~0)	/* use as return value, not for comparisons */

FieldCategory

typedef ByteEnum FieldCategory
#define FC_NONE				0x0
#define FC_NAME				0x1
#define FC_DATE				0x2
#define FC_TELEPHONE				0x3
#define FC_ADDRESS				0x4
#define FC_EMAIL				0x5

When you add a field to a datatstore, you define its FieldCategory . FieldCategory is the type of information the data represents (not to be confused with FieldType ; see below).

FieldData

typedef struct {
FieldType			FD_type;
FieldCategory			FD_category;
FieldFlags			FD_flags;
} FieldData;

Structure used to get and set field information (see FieldDescriptor below).

FieldDescriptor

typedef struct {
FieldData			FD_data;
TCHAR			*FD_name;
} FieldDescriptor;

Structure used to get and set field information (see FieldData above).

FieldHeader

typedef struct {
FieldID		FH_id;
word		FH_size;
} FieldHeader;

Structure containing metadata about a variable-sized field (such as a string field); field data follows this header.

FieldHeaderFixed

typedef struct {
FieldID		FHF_id;
} FieldHeaderFixed;

Structure containing metadata about a fixed-sized field; field data follows this header.

FieldType

typedef ByteEnum FieldType
#define DSFT_FLOAT				0x0
#define DSFT_SHORT				0x1
#define DSFT_LONG				0x2
#define DSFT_TIMESTAMP				0x3
#define DSFT_DATE				0x4
#define DSFT_TIME				0x5
#define DSFT_STRING				0x6
#define DSFT_BINARY				0x7
#define DSFT_GRAPHICS				0x8
#define DSFT_INK				0x9

When you add a field to a datatstore, you define its FieldType . FieldType is the type of data the field contains (not to be confused with FieldCategory ; see above). Note that fields of type DSFT_FLOAT expect data of type FloatNum .

FileAccess

typedef ByteEnum FileAccess
#define FA_READ_ONLY				0
#define FA_WRITE_ONLY				1
#define FA_READ_WRITE				2

FileAccessFlags

typedef ByteFlags FileAccessFlags;
#define FILE_DENY_RW 0x10
#define FILE_DENY_W 0x20
#define FILE_DENY_R 0x30
#define FILE_DENY_NONE 0x40
#define FILE_ACCESS_R 0x00
#define FILE_ACCESS_W 0x01
#define FILE_ACCESS_RW 0x02
#define FILE_NO_ERRORS 0x80

When you open a file for bytewise access, you must pass a record of FileAccessFlags . The FileAccessFlags record specifies two things: what kind of access the caller wants, and what type of access is permitted to other geodes. A set of FileAccessFlags is thus a bit-wise "or" of two different values. The first specifies what kind of access the calling geode wants and has the following values:

FILE_ACCESS_R
The geode will only be reading from the file.
FILE_ACCESS_W
The geode will write to the file but will not read from it.
FILE_ACCESS_RW
The geode will read from and write to the file.

The second part specifies what kind of access other geodes may have. Note that if you try to deny a permission which has already been given to another geode (e.g. you open a file with FILE_DENY_W when another geode has the file open for write-access), the call will fail. It has the following values:

FILE_DENY_RW
No geode may open the file for any kind of access, whether read, write, or read/write.
FILE_DENY_R
No geode may open the file for read or read/write access.
FILE_DENY_W
No geode may open the file for write or read/write access.
FILE_DENY_NONE
Other geodes may open the file for any kind of access.

Two flags, one from each of these sets of values, are combined to make up a proper FileAccessFlags value. For example, to open the file for read-only access while prohibiting other geodes from writing to the file, you would pass the flags "(FILE_ACCESS_R | FILE_DENY_W)".

FileAccessRights

typedef char FileAccessRights[FILE_RIGHTS_SIZE];

| Up: GEOS SDK TechDocs | Up | Prev: EvalErrorData ... | Next: FileAttrs ...


GEOS C Structs: FileAttrs ...

| Up: GEOS SDK TechDocs | Up | Prev: FALSE ... | Next: FileDate ...

FileAttrs

typedef ByteFlags FileAttrs;
#define FA_ARCHIVE						0x20
#define FA_SUBDIR 						0x10
#define FA_VOLUME 						0x8
#define FA_SYSTEM	 					0x4
#define FA_HIDDEN 						0x2
#define FA_RDONLY 						0x1
#define FILE_ATTR_NORMAL 						0
#define FILE_ATTR_READ_ONLY	 					FA_RDONLY
#define FILE_ATTR_HIDDEN 						FA_HIDDEN
#define FILE_ATTR_SYSTEM 						FA_SYSTEM
#define FILE_ATTR_VOLUME_LABEL 						FA_VOLUME

Every DOS or GEOS file has certain attributes. These attributes mark such things as whether the file is read-only. With GEOS files, the attributes can be accessed by using the extended attribute FEA_FILE_ATTR. You can also access any file's standard attributes with the routines FileGetAttributes() and FileSetAttributes() ; these routines work for both GEOS files and plain DOS files.

The FileAttrs field contains the following bits:

FA_ARCHIVE
This flag is set if the file requires backup. Backup programs typically clear this bit.
FA_SUBDIR
This flag is set if the "file" is actually a directory. Geodes may not change this flag.
FA_VOLUME
This flag is set if the "file" is actually the volume label. This flag will be off for all files a geode will ever see. Geodes may not change this flag.
FA_SYSTEM
This flag is set if the file is a system file. Geodes should not change this bit.
FA_HIDDEN
This flag is set if the file is hidden.
FA_RDONLY
This flag is set if the file is read-only.

See Also: FileGetAttributes(), FileSetAttributes().

Include: file.h

FileChangeNotificationData

typedef struct {
word    	FCND_disk; 	/* Handle for disk on which change occurred */
FileID  	FCND_id;   	/* ID of affected file or containing dir,
                 * as appropriate to notification type */
FileLongName    FCND_name;	/* Name of file, if appropriate (FCNT_CREATE,
                 * FCNT_RENAME, and FCNT_OPEN only) */
} FileChangeNotificationData;

FileChangeType

typedef ByteEnum FileChangeType;
#define FCT_CREATE				0
#define FCT_DELETE 				1
#define FCT_RENAME 				2
#define FCT_CONTENTS 				3
#define FCT_DISK_FORMAT 				4

FileCopyrightNotice

typedef char FileCopyrightNotice[GFH_NOTICE_SIZE];

FileCreateFlags

typedef WordFlags FileCreateFlags;
#define FCF_NATIVE				0x8000
#define FCF_MODE				0x0300 /* Filled with FILE_CREATE_* constant */
#define FCF_ACCESS				0x00ff /* Filled with FileAccessFlags */

The lower byte of this field is a FileAccessFlags record. This specifies the file's permissions and exclusions. Note that you must request write or read/write permission when you create a file. The upper byte specifies how the file should be created. It contains the following possible values:

FILE_CREATE_TRUNCATE
If a file with the given name exists, it should be opened and truncated; that is, all data should be deleted.
FILE_CREATE_NO_TRUNCATE
If the file exists, it should be opened without being truncated.
FILE_CREATE_ONLY
If the file exists, the routine should fail and set the thread's error value to ERROR_FILE_EXISTS.
FCF_NATIVE
This flag is combined with one of the above flags if the file should be created in the device's native format; e.g. if it should be a DOS file instead of a GEOS file. The name passed must be an acceptable native file name. If a GEOS file with the specified name already exists, FileCreate() will fail with error condition ERROR_FILE_FORMAT_MISMATCH. Similarly, if the flag isn't set and a non-GEOS file with this name exists, FileCreate() will fail and return this error.
| Up: GEOS SDK TechDocs | Up | Prev: FALSE ... | Next: FileDate ...


GEOS C Structs: FileDate ...

| Up: GEOS SDK TechDocs | Up | Prev: FileAttrs ... | Next: FileFromTransferBlockID ...

FileDate

typedef WordFlags FileDate;
#define FD_YEAR                 0xfe00
#define FD_MONTH                0x01e0
#define FD_DAY                  0x001f
#define FD_YEAR_OFFSET          9
#define FD_MONTH_OFFSET         5
#define FD_DAY_OFFSET           0

A file's date stamp is stored in a 16-bit bitfield. This field contains entries for the year, month, and day. Each field is identified by a mask and an offset. To access a field, simply clear all bits except those in the mask, then shift the bits to the right by the number of the offset. (Macros are provided to do this; they are described below.) FileDate contains the following fields, identified by their masks:

FD_YEAR
This field records the year counting from the base year of 1980. It is offset by FD_YEAR_OFFSET bits.
FD_MONTH
This field records the months as integers, with January as 1. It is offset by FD_MONTH_OFFSET bits.
FD_DAY
This field records the day of the month. It is offset by FD_DAY_OFFSET bits.

Include: file.h

See Also: FileTime

FileDateAndTime

typedef DWordFlags FileDateAndTime;
#define FDAT_HOUR						0xf8000000
#define FDAT_MINUTE						0x07e00000
#define FDAT_2SECOND						0x001f0000
#define FDAT_YEAR 						0x0000fe00
#define FDAT_MONTH 						0x000001e0
#define FDAT_DAY 						0x0000001f
#define FDAT_HOUR_OFFSET					27
#define FDAT_MINUTE_OFFSET					21
#define FDAT_2SECOND_OFFSET					16
#define FDAT_YEAR_OFFSET 					9
#define FDAT_MONTH_OFFSET 					5
#define FDAT_DAY_OFFSET 					0
#define FDAT_BASE_YEAR 					1980

Every GEOS file has two date and time stamps. One of them records the time the file was created, and one records the time the file was last modified. These stamps are recorded with the file's extended attributes; they are labeled FEA_CREATION and FEA_MODIFICATION, respectively. Non-GEOS files have a single date/time stamp, which records the time the file was last modified.

The date/time stamps are stored in a 32-bit bitfield. This field contains entries for the year, month, day, hour, minute, and second. Each field is identified by a mask and an offset. To access a field, simply clear all bits except those in the mask, then shift the bits to the right by the number of the offset. (Macros are provided to do this; they are described below.) FileDateAndTime contains the following fields, identified by their masks:

FDAT_YEAR
This field records the year, counting from a base year of 1980. (The constant FDAT_BASE_YEAR is defined as 1980.) This field is at an offset of FDAT_YEAR_OFFSET bits from the low end of the value.
FDAT_MONTH
This field records the month as an integer, with January being one. It is located at an offset of FDAT_MONTH_OFFSET.
FDAT_DAY
This field records the day of the month. It is located at an offset of FDAT_DAY_OFFSET.
FDAT_HOUR
This field records the hour on a 24-hour clock, with zero being the hour after midnight. It is located at an offset of FDAT_HOUR_OFFSET.
FDAT_MINUTE
This field records the minute. It is located at an offset of FDAT_MINUTE_OFFSET.
FDAT_2SECOND
This field records the second, divided by two; that is, a field value of 15 indicates the 30th second. (It is represented this way to let the second fit into 5 bits, thus letting the entire value fit into 32 bits.) It is located at an offset of FDAT_2SECOND_OFFSET.

Macros are provided to extract values from each of the fields of a FileDateAndTime structure. The macros are listed below:

byte FDATExtractYear( /* returns year field, counted from 1980*/
        FileDateAndTime fdat);
word FDATExtractYearAD( /* returns year field + base year */
        FileDateAndTime fdat);
byte FDATExtractMonth( /* returns month field (1 = January, etc.) */
        FileDateAndTime fdat);
byte FDATExtractDay( /* returns day field */
        FileDateAndTime fdat);
byte FDATExtractHour( /* returns hour field */
        FileDateAndTime fdat);
byte FDATExtractMinute( /* returns minute field */
        FileDateAndTime fdat);
byte FDATExtract2Second( /* returns 2Second field */
        FileDateAndTime fdat);
byte FDATExtractSecond( /* returns number of seconds (2 * 2Second) */
        FileDateAndTime fdat);

Include: file.h

FileDesktopInfo

typedef char FileDesktopInfo[FILE_DESKTOP_INFO_SIZE];

FileDirID

typedef dword FileDirID;

FileExclude

typedef ByteEnum FileExclude;
#define FE_EXCLUSIVE				1
#define FE_DENY_WRITE 				2
#define FE_DENY_READ 				3
#define FE_NONE 				4

FileExtAttrDesc

typedef struct {
FileExtendedAttribute				FEAD_attr;			/* Attribute to get or set */
void			*FEAD_value;				/* Pointer to buffer/new value */
word			FEAD_size;				/* length of buffer/new value */
chr			*FEAD_name;				/* If FEAD_attr == FEA_CUSTOM,
                             * this points to null-
                             * terminated ASCII string with
                             * attribute's name; otherwise,
                             * this is ignored. */
} FileExtendedAttrDesc;

The routines to get and set extended attributes can be passed the attribute FEA_MULTIPLE. In this case, they will also be passed the address of an array of FileExtAttrDesc structures and the number of elements of the array. They will go through the array and read or write the appropriate information.

FileEnum() can also be passed arrays of FileExtAttrDesc structures. In this case, the number of elements in the array is not passed. Instead, each array ends with a FileExtAttrDesc with a FEAD _attr field set to FEA_END_OF_LIST.

See Also: FileExtendedAttribute.

Include: file.h

FileExtendedAttribute

typedef enum /* word */ {
FEA_MODIFICATION,
FEA_FILE_ATTR,
FEA_SIZE,
FEA_FILE_TYPE,
FEA_FLAGS,
FEA_RELEASE,
FEA_PROTOCOL,
FEA_TOKEN,
FEA_CREATOR,
FEA_USER_NOTES,
FEA_NOTICE,
FEA_CREATION,
FEA_PASSWORD,
FEA_CUSTOM,
FEA_NAME,
FEA_GEODE_ATTR,
FEA_PATH_INFO,
FEA_FILE_ID,
FEA_DESKTOP_INFO,
FEA_DRIVE_STATUS,
FEA_DOS_NAME,
FEA_OWNER,
FEA_RIGHTS,
FEA_MULTIPLE = 0xfffe,
FEA_END_OF_LIST = 0xffff,
} FileExtendedAttribute;

Every GEOS file has a set of extended attributes. These attributes can be recovered with FileGetPathExtAttributes() or FileGetHandleExtAttributes() . You can also use FileEnum() to search a directory for files with specified extended attributes.

The above extended attributes have been implemented. More may be added with future releases of GEOS. The attributes are discussed at length in the File System chapter.

See Also: FileExtAttrDesc.

Include: file.h

| Up: GEOS SDK TechDocs | Up | Prev: FileAttrs ... | Next: FileFromTransferBlockID ...


GEOS C Structs: FileFromTransferBlockID ...

| Up: GEOS SDK TechDocs | Up | Prev: FileDate ... | Next: FontFamily ...

FileFromTransferBlockID

VMFileHandle FileFromTransferBlockID(id);
        TransferBlockID		id;

This macro extracts the file handle from the given TransferBlockID value.

FileHandle

typedef Handle FileHandle;

FileID

typedef dword FileID;

FileLongName

typedef char FileLongName[FILE_LONGNAME_BUFFER_SIZE];

FileOwnerName

typedef char FileOwnerName[FILE_OWNER_NAME_SIZE];

FilePassword

typedef char FilePassword[FILE_PASSWORD_SIZE];

FilePosMode

typedef ByteEnum FilePosMode;
#define FILE_POS_START				0
#define FILE_POS_RELATIVE				1
#define FILE_POS_END				2

FileTime

typedef WordFlags FileTime;
#define FT_HOUR                 0xf800
#define FT_MINUTE               0x07e0
#define FT_2SECOND              0x001f
#define FT_HOUR_OFFSET          11
#define FT_MINUTE_OFFSET        5
#define FT_2SECOND_OFFSET       0

A file's time stamp is stored in a 16-bit bitfield. This field contains entries for the hour, minute, and second. Each field is identified by a mask and an offset. To access a field, simply clear all bits except those in the mask, then shift the bits to the right by the number of the offset. FileTime contains the following fields, identified by their masks:

FT_HOUR
This field records the hour on a 24-hour clock, with zero being the hour after midnight.. It is offset by FT_HOUR_OFFSET bits.
FT_MINUTE
This field identifies the minutes. It is offset by FT_MINUTE_OFFSET bits.
FT_2SECOND
This field records the second, divided by two; that is, a field value of 15 indicates the 30th second. (It is represented this way to let the second fit into 5 bits.) It is located at an offset of FT_2SECOND_OFFSET bits.

Include: file.h

See Also: FileDate

FileUserNotes

typedef char FileUserNotes[GFH_USER_NOTES_BUFFER_SIZE];

FindNoteHeader

typedef struct {
word 	FNH_count;			/* The number of matching notes we've found */
} FindNoteHeader;

FloatExponent

typedef WordFlags FloatExponent;
#define FE_SIGN         0x8000
#define FE_EXPONENT     0x7fff

FloatNum

typedef struct {
        word    				F_mantissa_wd0;
        word    				F_mantissa_wd1;
        word    				F_mantissa_wd2;
        word    				F_mantissa_wd3;
        FloatExponent   				F_exponent;
} FloatNum;

FontAttrs

typedef ByteFlags FontAttrs;
#define FA_FIXED_WIDTH 				0x40
#define FA_ORIENT 				0x20
#define FA_OUTLINE 				0x10
#define FA_FAMILY 				0x0f
#define FA_FAMILY_OFFSET				0

Include: font.h

FontEnumFlags

typedef ByteFlags FontEnumFlags;
#define FEF_ALPHABETIZE				0x80		/* Alphabetize returned list of fonts */
#define FEF_FIXED_WIDTH				0x20		/* Return only fixed-width fonts */
#define FEF_FAMILY 				0x10
#define FEF_STRING 				0x08
#define FEF_DOWNCASE 				0x04		/* Returned font names will be lowercase */
#define FEF_BITMAPS 				0x02		/* Interested in bitmap fonts */
#define FEF_OUTLINES 				0x01		/* Interested in outline fonts */

Include: font.h

FontEnumStruct

typedef struct {
 FontIDs	 	FES_ID;
 char 		FES_name[FID_NAME_LEN];
} FontEnumStruct;

Include: font.h

| Up: GEOS SDK TechDocs | Up | Prev: FileDate... | Next: FontFamily ...


GEOS C Structs: FontFamily ...

| Up: GEOS SDK TechDocs | Up | Prev: FileFromTransferBlockID ... | Next: FontID ...

FontFamily

typedef byte FontFamily;
#define FF_NON_PORTABLE					0x0007
#define FF_SPECIAL					0x0006
#define FF_MONO					0x0005
#define FF_SYMBOL					0x0004
#define FF_ORNAMENT					0x0003
#define FF_SCRIPT					0x0002
#define FF_SANS_SERIF					0x0001
#define FF_SERIF					0x0000

Include: fontID.h

FontGroup

typedef enum /* word */ {
#define FG_NON_PORTABLE					0x0e00
#define FG_SPECIAL					0x0c00
#define FG_MONO					0x0a00
#define FG_SYMBOL					0x0800
#define FG_ORNAMENT					0x0600
#define FG_SCRIPT					0x0400
#define FG_SANS_SERIF					0x0200
#define FG_SERIF					0x0000
} FontGroup;

Include: fontID.h

FontIDRecord

typedef WordFlags FontIDRecord;
#define FIDR_maker				0xf000
#define FIDR_ID				0x0fff
#define FIDR_maker_OFFSET					12
#define FIDR_ID_OFFSET					 0

Include: font.h

| Up: GEOS SDK TechDocs | Up | Prev: FileFromTransferBlockID ... | Next: FontID ...


GEOS C Structs: FontID ...

| Up: GEOS SDK TechDocs | Up | Prev: FontFamily ... | Next: FontMaker ...

FontID

typedef word FontID;
#define FID_PRINTER_20CPI										0xfa05
#define FID_PRINTER_17CPI										0xfa04
#define FID_PRINTER_16CPI										0xfa03
#define FID_PRINTER_15CPI										0xfa02
#define FID_PRINTER_12CPI										0xfa01
#define FID_PRINTER_10CPI										0xfa00
#define FID_PRINTER_PROP_SANS										0xf200
#define FID_PRINTER_PROP_SERIF										0xf000
#define FID_BITSTREAM_LETTER_GOTHIC										0x3a03
#define FID_PS_LETTER_GOTHIC										0x2a03
#define FID_DTC_LETTER_GOTHIC										0x1a03
#define FID_BITSTREAM_PRESTIGE_ELITE										0x3a02
#define FID_PS_PRESTIGE_ELITE										0x2a02
#define FID_DTC_PRESTIGE_ELITE										0x1a02
#define FID_BITSTREAM_AMERICAN_TYPEWRITER										0x3a01
#define FID_PS_AMERICAN_TYPEWRITER										0x2a01
#define FID_DTC_AMERICAN_TYPEWRITER										0x1a01
#define FID_BITSTREAM_URW_MONO										0x3a00
#define FID_PS_COURIER										0x2a00
#define FID_DTC_URW_MONO										0x1a00
#define FID_BITSTREAM_FUN_DINGBATS										0x380d
#define FID_PS_FUN_DINGBATS										0x280d
#define FID_DTC_FUN_DINGBATS										0x180d
#define FID_BITSTREAM_CHEQ										0x380c
#define FID_PS_CHEQ										0x280c
#define FID_DTC_CHEQ										0x180c
#define FID_BITSTREAM_BUNDESBAHN_PI_3										0x380b
#define FID_PS_BUNDESBAHN_PI_3										0x280b
#define FID_DTC_BUNDESBAHN_PI_3										0x180b
#define FID_BITSTREAM_BUNDESBAHN_PI_2										0x380a
#define FID_PS_BUNDESBAHN_PI_2										0x280a
#define FID_DTC_BUNDESBAHN_PI_2										0x180a
#define FID_BITSTREAM_BUNDESBAHN_PI_1										0x3809
#define FID_PS_BUNDESBAHN_PI_1										0x2809
#define FID_DTC_BUNDESBAHN_PI_1										0x1809
#define FID_BITSTREAM_U_GREEK_MATH_PI										0x3808
#define FID_PS_U_GREEK_MATH_PI										0x2808
#define FID_DTC_U_GREEK_MATH_PI										0x1808
#define FID_BITSTREAM_U_NEWS_COMM_PI										0x3807
#define FID_PS_U_NEWS_COMM_PI										0x2807
#define FID_DTC_U_NEWS_COMM_PI										0x1807
#define FID_BITSTREAM_ACE_I										0x3806
#define FID_PS_ACE_I										0x2806
#define FID_DTC_ACE_I										0x1806
#define FID_BITSTREAM_SONATA										0x3805
#define FID_PS_SONATA										0x2805
#define FID_DTC_SONATA										0x1805
#define FID_BITSTREAM_CARTA										0x3804
#define FID_PS_CARTA										0x2804
#define FID_DTC_CARTA										0x1804
#define FID_BITSTREAM_MICR										0x3803
#define FID_PS_MICR										0x2803
#define FID_DTC_MICR										0x1803
#define FID_BITSTREAM_ZAPF_DINGBATS										0x3802
#define FID_PS_ZAPF_DINGBATS										0x2802
#define FID_DTC_ZAPF_DINGBATS										0x1802
#define FID_BITSTREAM_DINGBATS										0x3801
#define FID_PS_DINGBATS										0x2801
#define FID_DTC_DINGBATS										0x1801
#define FID_BITSTREAM_URW_SYMBOLPS										0x3800
#define FID_PS_SYMBOL										0x2800
#define FID_DTC_URW_SYMBOLPS										0x1800
#define FID_BITSTREAM_JUNIPER										0x367f
#define FID_PS_JUNIPER										0x267f
#define FID_DTC_JUNIPER										0x167f
#define FID_BITSTREAM_COTTONWOOD										0x367e
#define FID_PS_COTTONWOOD										0x267e
#define FID_DTC_COTTONWOOD										0x167e
#define FID_BITSTREAM_BANCO										0x367d
#define FID_PS_BANCO										0x267d
#define FID_DTC_BANCO										0x167d
#define FID_BITSTREAM_ARCADIA										0x367c
#define FID_PS_ARCADIA										0x267c
#define FID_DTC_ARCADIA										0x167c
#define FID_BITSTREAM_ZIPPER										0x367b
#define FID_PS_ZIPPER										0x267b
#define FID_DTC_ZIPPER										0x167b
#define FID_BITSTREAM_WEIFZ_RUNDGOTIFCH										0x367a
#define FID_PS_WEIFZ_RUNDGOTIFCH										0x267a
#define FID_DTC_WEIFZ_RUNDGOTIFCH										0x167a
#define FID_BITSTREAM_WASHINGTON										0x3679
#define FID_PS_WASHINGTON										0x2679
#define FID_DTC_WASHINGTON										0x1679
#define FID_BITSTREAM_VICTORIAN										0x3678
#define FID_PS_VICTORIAN										0x2678
#define FID_DTC_VICTORIAN										0x1678
#define FID_BITSTREAM_VEGAS										0x3677
#define FID_PS_VEGAS										0x2677
#define FID_DTC_VEGAS										0x1677
#define FID_BITSTREAM_VARIO										0x3676
#define FID_PS_VARIO										0x2676
#define FID_DTC_VARIO										0x1676
#define FID_BITSTREAM_VAG_RUNDSCHRIFT										0x3675
#define FID_PS_VAG_RUNDSCHRIFT										0x2675
#define FID_DTC_VAG_RUNDSCHRIFT										0x1675
#define FID_BITSTREAM_TRAJANUS										0x3674
#define FID_PS_TRAJANUS										0x2674
#define FID_DTC_TRAJANUS										0x1674
#define FID_BITSTREAM_TITUS										0x3673
#define FID_PS_TITUS										0x2673
#define FID_DTC_TITUS										0x1673
#define FID_BITSTREAM_TIME_SCRIPT										0x3672
#define FID_PS_TIME_SCRIPT										0x2672
#define FID_DTC_TIME_SCRIPT										0x1672
#define FID_BITSTREAM_THUNDERBIRD										0x3671
#define FID_PS_THUNDERBIRD										0x2671
#define FID_DTC_THUNDERBIRD										0x1671
#define FID_BITSTREAM_THOROWGOOD										0x3670
#define FID_PS_THOROWGOOD										0x2670
#define FID_DTC_THOROWGOOD										0x1670
#define FID_BITSTREAM_TARRAGON										0x366f
#define FID_PS_TARRAGON										0x266f
#define FID_DTC_TARRAGON										0x166f
#define FID_BITSTREAM_TANGO										0x366e
#define FID_PS_TANGO										0x266e
#define FID_DTC_TANGO										0x166e
#define FID_BITSTREAM_SYNCHRO										0x366d
#define FID_PS_SYNCHRO										0x266d
#define FID_DTC_SYNCHRO										0x166d
#define FID_BITSTREAM_SUPERSTAR										0x366c
#define FID_PS_SUPERSTAR										0x266c
#define FID_DTC_SUPERSTAR										0x166c
#define FID_BITSTREAM_STOP										0x366b
#define FID_PS_STOP										0x266b
#define FID_DTC_STOP										0x166b
#define FID_BITSTREAM_STILLA_CAPS										0x366a
#define FID_PS_STILLA_CAPS										0x266a
#define FID_DTC_STILLA_CAPS										0x166a
#define FID_BITSTREAM_STILLA										0x3669
#define FID_PS_STILLA										0x2669
#define FID_DTC_STILLA										0x1669
#define FID_BITSTREAM_STENTOR										0x3668
#define FID_PS_STENTOR										0x2668
#define FID_DTC_STENTOR										0x1668
#define FID_BITSTREAM_SQUIRE										0x3667
#define FID_PS_SQUIRE										0x2667
#define FID_DTC_SQUIRE										0x1667
#define FID_BITSTREAM_SPRINGFIELD										0x3666
#define FID_PS_SPRINGFIELD										0x2666
#define FID_DTC_SPRINGFIELD										0x1666
#define FID_BITSTREAM_SLIPSTREAM										0x3665
#define FID_PS_SLIPSTREAM										0x2665
#define FID_DTC_SLIPSTREAM										0x1665
#define FID_BITSTREAM_SINALOA										0x3664
#define FID_PS_SINALOA										0x2664
#define FID_DTC_SINALOA										0x1664
#define FID_BITSTREAM_SHELLEY										0x3663
#define FID_PS_SHELLEY										0x2663
#define FID_DTC_SHELLEY										0x1663
#define FID_BITSTREAM_SERPENTINE										0x3662
#define FID_PS_SERPENTINE										0x2662
#define FID_DTC_SERPENTINE										0x1662
#define FID_BITSTREAM_RUBBER_STAMP										0x3661
#define FID_PS_RUBBER_STAMP										0x2661
#define FID_DTC_RUBBER_STAMP										0x1661
#define FID_BITSTREAM_ROMIC										0x3660
#define FID_PS_ROMIC										0x2660
#define FID_DTC_ROMIC										0x1660
#define FID_BITSTREAM_RIALTO										0x365f
#define FID_PS_RIALTO										0x265f
#define FID_DTC_RIALTO										0x165f
#define FID_BITSTREAM_REVUE										0x365e
#define FID_PS_REVUE										0x265e
#define FID_DTC_REVUE										0x165e
#define FID_BITSTREAM_QUENTIN										0x365d
#define FID_PS_QUENTIN										0x265d
#define FID_DTC_QUENTIN										0x165d
#define FID_BITSTREAM_PRO_ARTE										0x365c
#define FID_PS_PRO_ARTE										0x265c
#define FID_DTC_PRO_ARTE										0x165c
#define FID_BITSTREAM_PRINCETOWN										0x365b
#define FID_PS_PRINCETOWN										0x265b
#define FID_DTC_PRINCETOWN										0x165b
#define FID_BITSTREAM_PRESIDENT										0x365a
#define FID_PS_PRESIDENT										0x265a
#define FID_DTC_PRESIDENT										0x165a
#define FID_BITSTREAM_PREMIER										0x3659
#define FID_PS_PREMIER										0x2659
#define FID_DTC_PREMIER										0x1659
#define FID_BITSTREAM_POST_ANTIQUA										0x3658
#define FID_PS_POST_ANTIQUA										0x2658
#define FID_DTC_POST_ANTIQUA										0x1658
#define FID_BITSTREAM_PLAZA										0x3657
#define FID_PS_PLAZA										0x2657
#define FID_DTC_PLAZA										0x1657
#define FID_BITSTREAM_PLAYBILL										0x3656
#define FID_PS_PLAYBILL										0x2656
#define FID_DTC_PLAYBILL										0x1656
#define FID_BITSTREAM_PICCADILLY										0x3655
#define FID_PS_PICCADILLY										0x2655
#define FID_DTC_PICCADILLY										0x1655
#define FID_BITSTREAM_PEIGNOT										0x3654
#define FID_PS_PEIGNOT										0x2654
#define FID_DTC_PEIGNOT										0x1654
#define FID_BITSTREAM_PAPYRUS										0x3653
#define FID_PS_PAPYRUS										0x2653
#define FID_DTC_PAPYRUS										0x1653
#define FID_BITSTREAM_PADDINGTION										0x3652
#define FID_PS_PADDINGTION										0x2652
#define FID_DTC_PADDINGTION										0x1652
#define FID_BITSTREAM_OKAY										0x3651
#define FID_PS_OKAY										0x2651
#define FID_DTC_OKAY										0x1651
#define FID_BITSTREAM_ODIN										0x3650
#define FID_PS_ODIN										0x2650
#define FID_DTC_ODIN										0x1650
#define FID_BITSTREAM_OCTOPUSS										0x364f
#define FID_PS_OCTOPUSS										0x264f
#define FID_DTC_OCTOPUSS										0x164f
#define FID_BITSTREAM_MOTTER_FEMINA										0x364e
#define FID_PS_MOTTER_FEMINA										0x264e
#define FID_DTC_MOTTER_FEMINA										0x164e
#define FID_BITSTREAM_MICROGRAMMA										0x364d
#define FID_PS_MICROGRAMMA										0x264d
#define FID_DTC_MICROGRAMMA										0x164d
#define FID_BITSTREAM_MACHINE										0x364c
#define FID_PS_MACHINE										0x264c
#define FID_DTC_MACHINE										0x164c
#define FID_BITSTREAM_LINOTEXT										0x364b
#define FID_PS_LINOTEXT										0x264b
#define FID_DTC_LINOTEXT										0x164b
#define FID_BITSTREAM_LIBERTY										0x364a
#define FID_PS_LIBERTY										0x264a
#define FID_DTC_LIBERTY										0x164a
#define FID_BITSTREAM_LAZYBONES										0x3649
#define FID_PS_LAZYBONES										0x2649
#define FID_DTC_LAZYBONES										0x1649
#define FID_BITSTREAM_LATIN_WIDE										0x3648
#define FID_PS_LATIN_WIDE										0x2648
#define FID_DTC_LATIN_WIDE										0x1648
#define FID_BITSTREAM_KNIGHTSBRIDGE										0x3647
#define FID_PS_KNIGHTSBRIDGE										0x2647
#define FID_DTC_KNIGHTSBRIDGE										0x1647
#define FID_BITSTREAM_KAPITELLIA										0x3646
#define FID_PS_KAPITELLIA										0x2646
#define FID_DTC_KAPITELLIA										0x1646
#define FID_BITSTREAM_KALLIGRAPHIA										0x3645
#define FID_PS_KALLIGRAPHIA										0x2645
#define FID_DTC_KALLIGRAPHIA										0x1645
#define FID_BITSTREAM_ICE_AGE										0x3644
#define FID_PS_ICE_AGE										0x2644
#define FID_DTC_ICE_AGE										0x1644
#define FID_BITSTREAM_ICONE										0x3643
#define FID_PS_ICONE										0x2643
#define FID_DTC_ICONE										0x1643
#define FID_BITSTREAM_HORNDON										0x3642
#define FID_PS_HORNDON										0x2642
#define FID_DTC_HORNDON										0x1642
#define FID_BITSTREAM_HORATIO										0x3641
#define FID_PS_HORATIO										0x2641
#define FID_DTC_HORATIO										0x1641
#define FID_BITSTREAM_HIGHLIGHT										0x3640
#define FID_PS_HIGHLIGHT										0x2640
#define FID_DTC_HIGHLIGHT										0x1640
#define FID_BITSTREAM_HADFIELD										0x363f
#define FID_PS_HADFIELD										0x263f
#define FID_DTC_HADFIELD										0x163f
#define FID_BITSTREAM_GLASER_STENCIL										0x363e
#define FID_PS_GLASER_STENCIL										0x263e
#define FID_DTC_GLASER_STENCIL										0x163e
#define FID_BITSTREAM_GILL_KAYO										0x363d
#define FID_PS_GILL_KAYO										0x263d
#define FID_DTC_GILL_KAYO										0x163d
#define FID_BITSTREAM_GALADRIEL										0x363c
#define FID_PS_GALADRIEL										0x263c
#define FID_DTC_GALADRIEL										0x163c
#define FID_BITSTREAM_FUTURA_DISPLAY										0x363b
#define FID_PS_FUTURA_DISPLAY										0x263b
#define FID_DTC_FUTURA_DISPLAY										0x163b
#define FID_BITSTREAM_FUTURA_C_BLACK										0x363a
#define FID_PS_FUTURA_C_BLACK										0x263a
#define FID_DTC_FUTURA_C_BLACK										0x163a
#define FID_BITSTREAM_FRANKFURTER										0x3639
#define FID_PS_FRANKFURTER										0x2639
#define FID_DTC_FRANKFURTER										0x1639
#define FID_BITSTREAM_FLORA										0x3638
#define FID_PS_FLORA										0x2638
#define FID_DTC_FLORA										0x1638
#define FID_BITSTREAM_FLANGE										0x3637
#define FID_PS_FLANGE										0x2637
#define FID_DTC_FLANGE										0x1637
#define FID_BITSTREAM_FLASH										0x3636
#define FID_PS_FLASH										0x2636
#define FID_DTC_FLASH										0x1636
#define FID_BITSTREAM_FLAMENCO										0x3635
#define FID_PS_FLAMENCO										0x2635
#define FID_DTC_FLAMENCO										0x1635
#define FID_BITSTREAM_FETTE_GOTILCH										0x3634
#define FID_PS_FETTE_GOTILCH										0x2634
#define FID_DTC_FETTE_GOTILCH										0x1634
#define FID_BITSTREAM_FETTE_FRAKTUR										0x3633
#define FID_PS_FETTE_FRAKTUR										0x2633
#define FID_DTC_FETTE_FRAKTUR										0x1633
#define FID_BITSTREAM_ENVIRO										0x3632
#define FID_PS_ENVIRO										0x2632
#define FID_DTC_ENVIRO										0x1632
#define FID_BITSTREAM_EINHORN										0x3631
#define FID_PS_EINHORN										0x2631
#define FID_DTC_EINHORN										0x1631
#define FID_BITSTREAM_ECKMANN										0x3630
#define FID_PS_ECKMANN										0x2630
#define FID_DTC_ECKMANN										0x1630
#define FID_BITSTREAM_DYNAMO										0x362f
#define FID_PS_DYNAMO										0x262f
#define FID_DTC_DYNAMO										0x162f
#define FID_BITSTREAM_DOM_CASUAL										0x362e
#define FID_PS_DOM_CASUAL										0x262e
#define FID_DTC_DOM_CASUAL										0x162e
#define FID_BITSTREAM_DAVIDA										0x362d
#define FID_PS_DAVIDA										0x262d
#define FID_DTC_DAVIDA										0x162d
#define FID_BITSTREAM_CROISSANT										0x362c
#define FID_PS_CROISSANT										0x262c
#define FID_DTC_CROISSANT										0x162c
#define FID_BITSTREAM_CRILLEE										0x362b
#define FID_PS_CRILLEE										0x262b
#define FID_DTC_CRILLEE										0x162b
#define FID_BITSTREAM_COUNTDOWN										0x362a
#define FID_PS_COUNTDOWN										0x262a
#define FID_DTC_COUNTDOWN										0x162a
#define FID_BITSTREAM_CORTEZ										0x3629
#define FID_PS_CORTEZ										0x2629
#define FID_DTC_CORTEZ										0x1629
#define FID_BITSTREAM_CONFERENCE										0x3628
#define FID_PS_CONFERENCE										0x2628
#define FID_DTC_CONFERENCE										0x1628
#define FID_BITSTREAM_COMPANY										0x3627
#define FID_PS_COMPANY										0x2627
#define FID_DTC_COMPANY										0x1627
#define FID_BITSTREAM_COLUMNA_SOLID										0x3626
#define FID_PS_COLUMNA_SOLID										0x2626
#define FID_DTC_COLUMNA_SOLID										0x1626
#define FID_BITSTREAM_CITY										0x3625
#define FID_PS_CITY										0x2625
#define FID_DTC_CITY										0x1625
#define FID_BITSTREAM_CIRKULUS										0x3624
#define FID_PS_CIRKULUS										0x2624
#define FID_DTC_CIRKULUS										0x1624
#define FID_BITSTREAM_CHURCHWARD_BRUSH										0x3623
#define FID_PS_CHURCHWARD_BRUSH										0x2623
#define FID_DTC_CHURCHWARD_BRUSH										0x1623
#define FID_BITSTREAM_CHROMIUM_ONE										0x3622
#define FID_PS_CHROMIUM_ONE										0x2622
#define FID_DTC_CHROMIUM_ONE										0x1622
#define FID_BITSTREAM_CHOC										0x3621
#define FID_PS_CHOC										0x2621
#define FID_DTC_CHOC										0x1621
#define FID_BITSTREAM_CHISEL										0x3620
#define FID_PS_CHISEL										0x2620
#define FID_DTC_CHISEL										0x1620
#define FID_BITSTREAM_CHESTERFIELD										0x361f
#define FID_PS_CHESTERFIELD										0x261f
#define FID_DTC_CHESTERFIELD										0x161f
#define FID_BITSTREAM_CAROUSEL										0x361e
#define FID_PS_CAROUSEL										0x261e
#define FID_DTC_CAROUSEL										0x161e
#define FID_BITSTREAM_CAMELLIA										0x361d
#define FID_PS_CAMELLIA										0x261d
#define FID_DTC_CAMELLIA										0x161d
#define FID_BITSTREAM_CABARET										0x361c
#define FID_PS_CABARET										0x261c
#define FID_DTC_CABARET										0x161c
#define FID_BITSTREAM_BUXOM										0x361b
#define FID_PS_BUXOM										0x261b
#define FID_DTC_BUXOM										0x161b
#define FID_BITSTREAM_BUSTER										0x361a
#define FID_PS_BUSTER										0x261a
#define FID_DTC_BUSTER										0x161a
#define FID_BITSTREAM_BOTTLENECK										0x3619
#define FID_PS_BOTTLENECK										0x2619
#define FID_DTC_BOTTLENECK										0x1619
#define FID_BITSTREAM_BLOCK										0x3618
#define FID_PS_BLOCK										0x2618
#define FID_DTC_BLOCK										0x1618
#define FID_BITSTREAM_BINNER										0x3617
#define FID_PS_BINNER										0x2617
#define FID_DTC_BINNER										0x1617
#define FID_BITSTREAM_BERNHARD_ANTIQUE										0x3616
#define FID_PS_BERNHARD_ANTIQUE										0x2616
#define FID_DTC_BERNHARD_ANTIQUE										0x1616
#define FID_BITSTREAM_BELSHAW										0x3615
#define FID_PS_BELSHAW										0x2615
#define FID_DTC_BELSHAW										0x1615
#define FID_BITSTREAM_BARCELONA										0x3614
#define FID_PS_BARCELONA										0x2614
#define FID_DTC_BARCELONA										0x1614
#define FID_BITSTREAM_BAUHAUS										0x3613
#define FID_PS_BAUHAUS										0x2613
#define FID_DTC_BAUHAUS										0x1613
#define FID_BITSTREAM_AUGUSTEA_OPEN										0x3612
#define FID_PS_AUGUSTEA_OPEN										0x2612
#define FID_DTC_AUGUSTEA_OPEN										0x1612
#define FID_BITSTREAM_AMERICAN_UNCIAL										0x3611
#define FID_PS_AMERICAN_UNCIAL										0x2611
#define FID_DTC_AMERICAN_UNCIAL										0x1611
#define FID_BITSTREAM_ULTE_SCHWABACHER										0x3610
#define FID_PS_ULTE_SCHWABACHER										0x2610
#define FID_DTC_ULTE_SCHWABACHER										0x1610
#define FID_BITSTREAM_ARNOLD_BOCKLIN										0x360f
#define FID_PS_ARNOLD_BOCKLIN										0x260f
#define FID_DTC_ARNOLD_BOCKLIN										0x160f
#define FID_BITSTREAM_ALGERIAN										0x360e
#define FID_PS_ALGERIAN										0x260e
#define FID_DTC_ALGERIAN										0x160e
#define FID_BITSTREAM_PUMP										0x360d
#define FID_PS_PUMP										0x260d
#define FID_DTC_PUMP										0x160d
#define FID_BITSTREAM_MARIAGE										0x360c
#define FID_PS_MARIAGE										0x260c
#define FID_DTC_MARIAGE										0x160c
#define FID_BITSTREAM_OLD_TOWN										0x360b
#define FID_PS_OLD_TOWN										0x260b
#define FID_DTC_OLD_TOWN										0x160b
#define FID_BITSTREAM_HOBO										0x360a
#define FID_PS_HOBO										0x260a
#define FID_DTC_HOBO										0x160a
#define FID_BITSTREAM_GOUDY_HEAVYFACE										0x3609
#define FID_PS_GOUDY_HEAVYFACE										0x2609
#define FID_DTC_GOUDY_HEAVYFACE										0x1609
#define FID_BITSTREAM_DATA_70										0x3608
#define FID_PS_DATA_70										0x2608
#define FID_DTC_DATA_70										0x1608
#define FID_BITSTREAM_LCD										0x3607
#define FID_PS_LCD										0x2607
#define FID_DTC_LCD										0x1607
#define FID_BITSTREAM_BALLOON										0x3606
#define FID_PS_BALLOON										0x2606
#define FID_DTC_BALLOON										0x1606
#define FID_BITSTREAM_BLIPPO_C_BLACK										0x3605
#define FID_PS_BLIPPO_C_BLACK										0x2605
#define FID_DTC_BLIPPO_C_BLACK										0x1605
#define FID_BITSTREAM_COOPER_C_BLACK										0x3604
#define FID_PS_COOPER_C_BLACK										0x2604
#define FID_DTC_COOPER_C_BLACK										0x1604
#define FID_BITSTREAM_COPPERPLATE										0x3603
#define FID_PS_COPPERPLATE										0x2603
#define FID_DTC_COPPERPLATE										0x1603
#define FID_BITSTREAM_STENCIL										0x3602
#define FID_PS_STENCIL										0x2602
#define FID_DTC_STENCIL										0x1602
#define FID_BITSTREAM_OLD_ENGLISH										0x3601
#define FID_PS_OLD_ENGLISH										0x2601
#define FID_DTC_OLD_ENGLISH										0x1601
#define FID_BITSTREAM_BROADWAY										0x3600
#define FID_PS_BROADWAY										0x2600
#define FID_DTC_BROADWAY										0x1600
#define FID_BITSTREAM_NUPITAL_SCRIPT										0x3430
#define FID_PS_NUPITAL_SCRIPT										0x2430
#define FID_DTC_NUPITAL_SCRIPT										0x1430
#define FID_BITSTREAM_MEDICI_SCRIPT										0x342f
#define FID_PS_MEDICI_SCRIPT										0x242f
#define FID_DTC_MEDICI_SCRIPT										0x142f
#define FID_BITSTREAM_CHARME										0x342e
#define FID_PS_CHARME										0x242e
#define FID_DTC_CHARME										0x142e
#define FID_BITSTREAM_CASCADE_SCRIPT										0x342d
#define FID_PS_CASCADE_SCRIPT										0x242d
#define FID_DTC_CASCADE_SCRIPT										0x142d
#define FID_BITSTREAM_LITHOS										0x342c
#define FID_PS_LITHOS										0x242c
#define FID_DTC_LITHOS										0x142c
#define FID_BITSTREAM_TEKTON										0x342b
#define FID_PS_TEKTON										0x242b
#define FID_DTC_TEKTON										0x142b
#define FID_BITSTREAM_VLADIMIR_SCRIPT										0x342a
#define FID_PS_VLADIMIR_SCRIPT										0x242a
#define FID_DTC_VLADIMIR_SCRIPT										0x142a
#define FID_BITSTREAM_VAN_DIJK										0x3429
#define FID_PS_VAN_DIJK										0x2429
#define FID_DTC_VAN_DIJK										0x1429
#define FID_BITSTREAM_SLOGAN										0x3428
#define FID_PS_SLOGAN										0x2428
#define FID_DTC_SLOGAN										0x1428
#define FID_BITSTREAM_SHAMROCK										0x3427
#define FID_PS_SHAMROCK										0x2427
#define FID_DTC_SHAMROCK										0x1427
#define FID_BITSTREAM_ROMAN_SCRIPT										0x3426
#define FID_PS_ROMAN_SCRIPT										0x2426
#define FID_DTC_ROMAN_SCRIPT										0x1426
#define FID_BITSTREAM_RAGE										0x3425
#define FID_PS_RAGE										0x2425
#define FID_DTC_RAGE										0x1425
#define FID_BITSTREAM_PRESENT_SCRIPT										0x3424
#define FID_PS_PRESENT_SCRIPT										0x2424
#define FID_DTC_PRESENT_SCRIPT										0x1424
#define FID_BITSTREAM_PHYLLIS_INITIALS										0x3423
#define FID_PS_PHYLLIS_INITIALS										0x2423
#define FID_DTC_PHYLLIS_INITIALS										0x1423
#define FID_BITSTREAM_PHYLLIS										0x3422
#define FID_PS_PHYLLIS										0x2422
#define FID_DTC_PHYLLIS										0x1422
#define FID_BITSTREAM_PEPITA										0x3421
#define FID_PS_PEPITA										0x2421
#define FID_DTC_PEPITA										0x1421
#define FID_BITSTREAM_PENDRY_SCRIPT										0x3420
#define FID_PS_PENDRY_SCRIPT										0x2420
#define FID_DTC_PENDRY_SCRIPT										0x1420
#define FID_BITSTREAM_PALETTE										0x341f
#define FID_PS_PALETTE										0x241f
#define FID_DTC_PALETTE										0x141f
#define FID_BITSTREAM_PALACE_SCRIPT										0x341e
#define FID_PS_PALACE_SCRIPT										0x241e
#define FID_DTC_PALACE_SCRIPT										0x141e
#define FID_BITSTREAM_NEVISON_CASUAL										0x341d
#define FID_PS_NEVISON_CASUAL										0x241d
#define FID_DTC_NEVISON_CASUAL										0x141d
#define FID_BITSTREAM_HILL										0x341c
#define FID_PS_HILL										0x241c
#define FID_DTC_HILL										0x141c
#define FID_BITSTREAM_LINOSCRIPT										0x341b
#define FID_PS_LINOSCRIPT										0x241b
#define FID_DTC_LINOSCRIPT										0x141b
#define FID_BITSTREAM_LINDSAY										0x341a
#define FID_PS_LINDSAY										0x241a
#define FID_DTC_LINDSAY										0x141a
#define FID_BITSTREAM_LE_GRIFFE										0x3419
#define FID_PS_LE_GRIFFE										0x2419
#define FID_DTC_LE_GRIFFE										0x1419
#define FID_BITSTREAM_KUNSTLERSCHREIBSCHRIFT										0x3418
#define FID_PS_KUNSTLERSCHREIBSCHRIFT										0x2418
#define FID_DTC_KUNSTLERSCHREIBSCHRIFT										0x1418
#define FID_BITSTREAM_JULIA_SCRIPT										0x3417
#define FID_PS_JULIA_SCRIPT										0x2417
#define FID_DTC_JULIA_SCRIPT										0x1417
#define FID_BITSTREAM_ISBELL										0x3416
#define FID_PS_ISBELL										0x2416
#define FID_DTC_ISBELL										0x1416
#define FID_BITSTREAM_ISADORA										0x3415
#define FID_PS_ISADORA										0x2415
#define FID_DTC_ISADORA										0x1415
#define FID_BITSTREAM_HOGARTH_SCRIPT										0x3414
#define FID_PS_HOGARTH_SCRIPT										0x2414
#define FID_DTC_HOGARTH_SCRIPT										0x1414
#define FID_BITSTREAM_HARLOW										0x3413
#define FID_PS_HARLOW										0x2413
#define FID_DTC_HARLOW										0x1413
#define FID_BITSTREAM_GLASTONBURY										0x3412
#define FID_PS_GLASTONBURY										0x2412
#define FID_DTC_GLASTONBURY										0x1412
#define FID_BITSTREAM_GILLIES_GOTHIC										0x3411
#define FID_PS_GILLIES_GOTHIC										0x2411
#define FID_DTC_GILLIES_GOTHIC										0x1411
#define FID_BITSTREAM_FREESTYLE_SCRIPT										0x3410
#define FID_PS_FREESTYLE_SCRIPT										0x2410
#define FID_DTC_FREESTYLE_SCRIPT										0x1410
#define FID_BITSTREAM_ENGLISCHE_SCHREIBSCHRIFT										0x340f
#define FID_PS_ENGLISCHE_SCHREIBSCHRIFT										0x240f
#define FID_DTC_ENGLISCHE_SCHREIBSCHRIFT										0x140f
#define FID_BITSTREAM_DEMIAN										0x340e
#define FID_PS_DEMIAN										0x240e
#define FID_DTC_DEMIAN										0x140e
#define FID_BITSTREAM_CANDICE										0x340d
#define FID_PS_CANDICE										0x240d
#define FID_DTC_CANDICE										0x140d
#define FID_BITSTREAM_BRONX										0x340c
#define FID_PS_BRONX										0x240c
#define FID_DTC_BRONX										0x140x
#define FID_BITSTREAM_BRODY										0x340b
#define FID_PS_BRODY										0x240b
#define FID_DTC_BRODY										0x140b
#define FID_BITSTREAM_BIBLE_SCRIPT										0x340a
#define FID_PS_BIBLE_SCRIPT										0x240a
#define FID_DTC_BIBLE_SCRIPT										0x140a
#define FID_BITSTREAM_ARISTON										0x3409
#define FID_PS_ARISTON										0x2409
#define FID_DTC_ARISTON										0x1409
#define FID_BITSTREAM_ANGLIA										0x3408
#define FID_PS_ANGLIA										0x2408
#define FID_DTC_ANGLIA										0x1408
#define FID_BITSTREAM_MISTRAL										0x3407
#define FID_PS_MISTRAL										0x2407
#define FID_DTC_MISTRAL										0x1407
#define FID_BITSTREAM_BALMORAL										0x3406
#define FID_PS_BALMORAL										0x2406
#define FID_DTC_BALMORAL										0x1406
#define FID_BITSTREAM_COMMERCIAL_SCRIPT										0x3405
#define FID_PS_COMMERCIAL_SCRIPT										0x2405
#define FID_DTC_COMMERCIAL_SCRIPT										0x1405
#define FID_BITSTREAM_KAUFMANN										0x3404
#define FID_PS_KAUFMANN										0x2404
#define FID_DTC_KAUFMANN										0x1404
#define FID_BITSTREAM_PARK_AVENUE										0x3403
#define FID_PS_PARK_AVENUE										0x2403
#define FID_DTC_PARK_AVENUE										0x1403
#define FID_BITSTREAM_BRUSH_SCRIPT										0x3402
#define FID_PS_BRUSH_SCRIPT										0x2402
#define FID_DTC_BRUSH_SCRIPT										0x1402
#define FID_BITSTREAM_VIVALDI										0x3401
#define FID_PS_VIVALDI										0x2401
#define FID_DTC_VIVALDI										0x1401
#define FID_BITSTREAM_ZAPF_CHANCERY										0x3400
#define FID_PS_ZAPF_CHANCERY										0x2400
#define FID_DTC_ZAPF_CHANCERY										0x1400
#define FID_BITSTREAM_AVANTE_GARDE_CONDENSED										0x323d
#define FID_PS_AVANTE_GARDE_CONDENSED										0x223d
#define FID_DTC_AVANTE_GARDE_CONDENSED										0x123d
#define FID_BITSTREAM_INSIGNIA										0x323c
#define FID_PS_INSIGNIA										0x223c
#define FID_DTC_INSIGNIA										0x123c
#define FID_BITSTREAM_INDUSTRIA										0x323b
#define FID_PS_INDUSTRIA										0x223b
#define FID_DTC_INDUSTRIA										0x123b
#define FID_BITSTREAM_DORIC_BOLD										0x323a
#define FID_PS_DORIC_BOLD										0x223a
#define FID_DTC_DORIC_BOLD										0x123a
#define FID_BITSTREAM_AKZINDENZ_GROTESK										0x3239
#define FID_PS_AKZINDENZ_GROTESK										0x2239
#define FID_DTC_AKZINDENZ_GROTESK										0x1239
#define FID_BITSTREAM_GROTESK										0x3238
#define FID_PS_GROTESK										0x2238
#define FID_DTC_GROTESK										0x1238
#define FID_BITSTREAM_TEMPO										0x3237
#define FID_PS_TEMPO										0x2237
#define FID_DTC_TEMPO										0x1237
#define FID_BITSTREAM_SYNTAX										0x3236
#define FID_PS_SYNTAX										0x2236
#define FID_DTC_SYNTAX										0x1236
#define FID_BITSTREAM_STONE_SANS										0x3235
#define FID_PS_STONE_SANS										0x2235
#define FID_DTC_STONE_SANS										0x1235
#define FID_BITSTREAM_SERIF_GOTHIC										0x3234
#define FID_PS_SERIF_GOTHIC										0x2234
#define FID_DTC_SERIF_GOTHIC										0x1234
#define FID_BITSTREAM_PRIMUS_ANTIQUA										0x3233
#define FID_PS_PRIMUS_ANTIQUA										0x2233
#define FID_DTC_PRIMUS_ANTIQUA										0x1233
#define FID_BITSTREAM_PRIMUS										0x3232
#define FID_PS_PRIMUS										0x2232
#define FID_DTC_PRIMUS										0x1232
#define FID_BITSTREAM_PRAXIS										0x3231
#define FID_PS_PRAXIS										0x2231
#define FID_DTC_PRAXIS										0x1231
#define FID_BITSTREAM_PANACHE										0x3230
#define FID_PS_PANACHE										0x2230
#define FID_DTC_PANACHE										0x1230
#define FID_BITSTREAM_OCR_B										0x322f
#define FID_PS_OCR_B										0x222f
#define FID_DTC_OCR_B										0x122f
#define FID_BITSTREAM_OCR_A										0x322e
#define FID_PS_OCR_A										0x222e
#define FID_DTC_OCR_A										0x122e
#define FID_BITSTREAM_NEWTEXT										0x322d
#define FID_PS_NEWTEXT										0x222d
#define FID_DTC_NEWTEXT										0x122d
#define FID_BITSTREAM_NEWS_GOTHIC										0x322c
#define FID_PS_NEWS_GOTHIC										0x222c
#define FID_DTC_NEWS_GOTHIC										0x122c
#define FID_BITSTREAM_NEUZEIT_GROTESK										0x322b
#define FID_PS_NEUZEIT_GROTESK										0x222b
#define FID_DTC_NEUZEIT_GROTESK										0x122b
#define FID_BITSTREAM_MIXAGE										0x322a
#define FID_PS_MIXAGE										0x222a
#define FID_DTC_MIXAGE										0x122a
#define FID_BITSTREAM_MAXIMA										0x3229
#define FID_PS_MAXIMA										0x2229
#define FID_DTC_MAXIMA										0x1229
#define FID_BITSTREAM_LUCIDA_SANS										0x3228
#define FID_PS_LUCIDA_SANS										0x2228
#define FID_DTC_LUCIDA_SANS										0x1228
#define FID_BITSTREAM_LITERA										0x3227
#define FID_PS_LITERA										0x2227
#define FID_DTC_LITERA										0x1227
#define FID_BITSTREAM_KABEL										0x3226
#define FID_PS_KABEL										0x2226
#define FID_DTC_KABEL										0x1226
#define FID_BITSTREAM_HOLSATIA										0x3225
#define FID_PS_HOLSATIA										0x2225
#define FID_DTC_HOLSATIA										0x1225
#define FID_BITSTREAM_HELVETICA_INSERAT										0x3224
#define FID_PS_HELVETICA_INSERAT										0x2224
#define FID_DTC_HELVETICA_INSERAT										0x1224
#define FID_BITSTREAM_NEUE_HELVETICA										0x3223
#define FID_PS_NEUE_HELVETICA										0x2223
#define FID_DTC_NEUE_HELVETICA										0x1223
#define FID_BITSTREAM_HELVETICA										0x3222
#define FID_PS_HELVETICA										0x2222
#define FID_DTC_HELVETICA										0x1222
#define FID_BITSTREAM_HAAS_UNICA										0x3221
#define FID_PS_HAAS_UNICA										0x2221
#define FID_DTC_HAAS_UNICA										0x1221
#define FID_BITSTREAM_GOUDY_SANS										0x3220
#define FID_PS_GOUDY_SANS										0x2220
#define FID_DTC_GOUDY_SANS										0x1220
#define FID_BITSTREAM_GOTHIC										0x321f
#define FID_PS_GOTHIC										0x221f
#define FID_DTC_GOTHIC										0x121f
#define FID_BITSTREAM_GILL_SANS										0x321e
#define FID_PS_GILL_SANS										0x221e
#define FID_DTC_GILL_SANS										0x121e
#define FID_BITSTREAM_GILL										0x321d
#define FID_PS_GILL										0x221d
#define FID_DTC_GILL										0x121d
#define FID_BITSTREAM_FUTURA										0x321c
#define FID_PS_FUTURA										0x221c
#define FID_DTC_FUTURA										0x121c
#define FID_BITSTREAM_FOLIO										0x321b
#define FID_PS_FOLIO										0x221b
#define FID_DTC_FOLIO										0x121b
#define FID_BITSTREAM_FLYER										0x321a
#define FID_PS_FLYER										0x221a
#define FID_DTC_FLYER										0x121a
#define FID_BITSTREAM_FETTE_MIDSCHRIFT										0x3219
#define FID_PS_FETTE_MIDSCHRIFT										0x2219
#define FID_DTC_FETTE_MIDSCHRIFT										0x1219
#define FID_BITSTREAM_FETTE_ENGSCHRIFT										0x3218
#define FID_PS_FETTE_ENGSCHRIFT										0x2218
#define FID_DTC_FETTE_ENGSCHRIFT										0x1218
#define FID_BITSTREAM_ERAS										0x3217
#define FID_PS_ERAS										0x2217
#define FID_DTC_ERAS										0x1217
#define FID_BITSTREAM_DIGI_GROTESK										0x3216
#define FID_PS_DIGI_GROTESK										0x2216
#define FID_DTC_DIGI_GROTESK										0x1216
#define FID_BITSTREAM_CORINTHIAN										0x3215
#define FID_PS_CORINTHIAN										0x2215
#define FID_DTC_CORINTHIAN										0x1215
#define FID_BITSTREAM_COMPACTA										0x3214
#define FID_PS_COMPACTA										0x2214
#define FID_DTC_COMPACTA										0x1214
#define FID_BITSTREAM_CLEARFACE_GOTHIC										0x3213
#define FID_PS_CLEARFACE_GOTHIC										0x2213
#define FID_DTC_CLEARFACE_GOTHIC										0x1213
#define FID_BITSTREAM_OPTIMA										0x3212
#define FID_PS_OPTIMA										0x2212
#define FID_DTC_OPTIMA										0x1212
#define FID_BITSTREAM_CHELMSFORD										0x3211
#define FID_PS_CHELMSFORD										0x2211
#define FID_DTC_CHELMSFORD										0x1211
#define FID_BITSTREAM_CASTLE										0x3210
#define FID_PS_CASTLE										0x2210
#define FID_DTC_CASTLE										0x1210
#define FID_BITSTREAM_BRITANNIC										0x320f
#define FID_PS_BRITANNIC										0x220f
#define FID_DTC_BRITANNIC										0x120f
#define FID_BITSTREAM_BERLINER_GROTESK										0x320e
#define FID_PS_BERLINER_GROTESK										0x220e
#define FID_DTC_BERLINER_GROTESK										0x120e
#define FID_BITSTREAM_BENGUIAT_GOTHIC										0x320d
#define FID_PS_BENGUIAT_GOTHIC										0x220d
#define FID_DTC_BENGUIAT_GOTHIC										0x120d
#define FID_BITSTREAM_AVANTE_GARDE										0x320c
#define FID_PS_AVANTE_GARDE										0x220c
#define FID_DTC_AVANTE_GARDE										0x120c
#define FID_BITSTREAM_ANZEIGEN_GROTESK										0x320b
#define FID_PS_ANZEIGEN_GROTESK										0x220b
#define FID_DTC_ANZEIGEN_GROTESK										0x120b
#define FID_BITSTREAM_ANTIQUE_OLIVE										0x320a
#define FID_PS_ANTIQUE_OLIVE										0x220a
#define FID_DTC_ANTIQUE_OLIVE										0x120a
#define FID_BITSTREAM_ALTERNATE_GOTHIC										0x3209
#define FID_PS_ALTERNATE_GOTHIC										0x2209
#define FID_DTC_ALTERNATE_GOTHIC										0x1209
#define FID_BITSTREAM_AKZIDENZ_GROTESK_BUCH										0x3208
#define FID_PS_AKZIDENZ_GROTESK_BUCH										0x2208
#define FID_DTC_AKZIDENZ_GROTESK_BUCH										0x1208
#define FID_BITSTREAM_AKZIDENZ_GROTESK										0x3207
#define FID_PS_AKZIDENZ_GROTESK										0x2207
#define FID_DTC_AKZIDENZ_GROTESK										0x1207
#define FID_BITSTREAM_AVENIR										0x3206
#define FID_PS_AVENIR										0x2206
#define FID_DTC_AVENIR										0x1206
#define FID_BITSTREAM_UNIVERS										0x3205
#define FID_PS_UNIVERS										0x2205
#define FID_DTC_UNIVERS										0x1205
#define FID_BITSTREAM_FRANKLIN_GOTHIC										0x3204
#define FID_PS_FRANKLIN_GOTHIC										0x2204
#define FID_DTC_FRANKLIN_GOTHIC										0x1204
#define FID_BITSTREAM_ANGRO										0x3203
#define FID_PS_ANGRO										0x2203
#define FID_DTC_ANGRO										0x1203
#define FID_BITSTREAM_EUROSTILE										0x3202
#define FID_PS_EUROSTILE										0x2202
#define FID_DTC_EUROSTILE										0x1202
#define FID_BITSTREAM_FRUTIGER										0x3201
#define FID_PS_FRUTIGER										0x2201
#define FID_DTC_FRUTIGER										0x1201
#define FID_BITSTREAM_URW_SANS										0x3200
#define FID_PS_URW_SANS										0x2200
#define FID_DTC_URW_SANS										0x1200
#define FID_BITSTREAM_GALLIARD_ROMAN_ITALIC										0x307e
#define FID_PS_GALLIARD_ROMAN_ITALIC										0x207e
#define FID_DTC_GALLIARD_ROMAN_ITALIC										0x107e
#define FID_BITSTREAM_GRANJON										0x307d
#define FID_PS_GRANJON										0x207d
#define FID_DTC_GRANJON										0x107d
#define FID_BITSTREAM_GARTH_GRAPHIC										0x307c
#define FID_PS_GARTH_GRAPHIC										0x207c
#define FID_DTC_GARTH_GRAPHIC										0x107c
#define FID_BITSTREAM_BAUER_BODONI										0x307b
#define FID_PS_BAUER_BODONI										0x207b
#define FID_DTC_BAUER_BODONI										0x107b
#define FID_BITSTREAM_BELWE										0x307a
#define FID_PS_BELWE										0x207a
#define FID_DTC_BELWE										0x107a
#define FID_BITSTREAM_CHARLEMAGNE										0x3079
#define FID_PS_CHARLEMAGNE										0x2079
#define FID_DTC_CHARLEMAGNE										0x1079
#define FID_BITSTREAM_TRAJAN										0x3078
#define FID_PS_TRAJAN										0x2078
#define FID_DTC_TRAJAN										0x1078
#define FID_BITSTREAM_ADOBE_GARAMOND										0x3077
#define FID_PS_ADOBE_GARAMOND										0x2077
#define FID_DTC_ADOBE_GARAMOND										0x1077
#define FID_BITSTREAM_ZAPF_INTERNATIONAL										0x3076
#define FID_PS_ZAPF_INTERNATIONAL										0x2076
#define FID_DTC_ZAPF_INTERNATIONAL										0x1076
#define FID_BITSTREAM_ZAPF_BOOK										0x3075
#define FID_PS_ZAPF_BOOK										0x2075
#define FID_DTC_ZAPF_BOOK										0x1075
#define FID_BITSTREAM_WORCESTER_ROUND										0x3074
#define FID_PS_WORCESTER_ROUND										0x2074
#define FID_DTC_WORCESTER_ROUND										0x1074
#define FID_BITSTREAM_WINDSOR										0x3073
#define FID_PS_WINDSOR										0x2073
#define FID_DTC_WINDSOR										0x1073
#define FID_BITSTREAM_WEISS										0x3072
#define FID_PS_WEISS										0x2072
#define FID_DTC_WEISS										0x1072
#define FID_BITSTREAM_WEIDEMANN										0x3071
#define FID_PS_WEIDEMANN										0x2071
#define FID_DTC_WEIDEMANN										0x1071
#define FID_BITSTREAM_WALBAUM										0x3070
#define FID_PS_WALBAUM										0x2070
#define FID_DTC_WALBAUM										0x1070
#define FID_BITSTREAM_VOLTA										0x306f
#define FID_PS_VOLTA										0x206f
#define FID_DTC_VOLTA										0x106f
#define FID_BITSTREAM_VENDOME										0x306e
#define FID_PS_VENDOME										0x206e
#define FID_DTC_VENDOME										0x106e
#define FID_BITSTREAM_VELJOVIC										0x306d
#define FID_PS_VELJOVIC										0x206d
#define FID_DTC_VELJOVIC										0x106d
#define FID_BITSTREAM_ADOBE_UTOPIA										0x306c
#define FID_PS_ADOBE_UTOPIA										0x206c
#define FID_DTC_ADOBE_UTOPIA										0x106c
#define FID_BITSTREAM_USHERWOOD										0x306b
#define FID_PS_USHERWOOD										0x206b
#define FID_DTC_USHERWOOD										0x106b
#define FID_BITSTREAM_URW_ANTIQUA										0x306a
#define FID_PS_URW_ANTIQUA										0x206a
#define FID_DTC_URW_ANTIQUA										0x106a
#define FID_BITSTREAM_TIMES_NEW_ROMAN										0x3069
#define FID_PS_TIMES_NEW_ROMAN										0x2069
#define FID_DTC_TIMES_NEW_ROMAN										0x1069
#define FID_BITSTREAM_TIMELESS										0x3068
#define FID_PS_TIMELESS										0x2068
#define FID_DTC_TIMELESS										0x1068
#define FID_BITSTREAM_TIFFANY										0x3067
#define FID_PS_TIFFANY										0x2067
#define FID_DTC_TIFFANY										0x1067
#define FID_BITSTREAM_TIEPOLO										0x3066
#define FID_PS_TIEPOLO										0x2066
#define FID_DTC_TIEPOLO										0x1066
#define FID_BITSTREAM_SWIFT										0x3065
#define FID_PS_SWIFT										0x2065
#define FID_DTC_SWIFT										0x1065
#define FID_BITSTREAM_STYMIE										0x3064
#define FID_PS_STYMIE										0x2064
#define FID_DTC_STYMIE										0x1064
#define FID_BITSTREAM_STRATFORD										0x3063
#define FID_PS_STRATFORD										0x2063
#define FID_DTC_STRATFORD										0x1063
#define FID_BITSTREAM_STONE_SERIF										0x3062
#define FID_PS_STONE_SERIF										0x2062
#define FID_DTC_STONE_SERIF										0x1062
#define FID_BITSTREAM_STONE_INFORMAL										0x3061
#define FID_PS_STONE_INFORMAL										0x2061
#define FID_DTC_STONE_INFORMAL										0x1061
#define FID_BITSTREAM_STEMPEL_SCHNEIDLER										0x3060
#define FID_PS_STEMPEL_SCHNEIDLER										0x2060
#define FID_DTC_STEMPEL_SCHNEIDLER										0x1060
#define FID_BITSTREAM_SOUVENIR										0x305f
#define FID_PS_SOUVENIR										0x205f
#define FID_DTC_SOUVENIR										0x105f
#define FID_BITSTREAM_SLIMBACH										0x305e
#define FID_PS_SLIMBACH										0x205e
#define FID_DTC_SLIMBACH										0x105e
#define FID_BITSTREAM_SERIFA										0x305d
#define FID_PS_SERIFA										0x205d
#define FID_DTC_SERIFA										0x105d
#define FID_BITSTREAM_SABON_ANTIQUA										0x305c
#define FID_PS_SABON_ANTIQUA										0x205c
#define FID_DTC_SABON_ANTIQUA										0x105c
#define FID_BITSTREAM_SABON										0x305b
#define FID_PS_SABON										0x205b
#define FID_DTC_SABON										0x105b
#define FID_BITSTREAM_ROMANA										0x305a
#define FID_PS_ROMANA										0x205a
#define FID_DTC_ROMANA										0x105a
#define FID_BITSTREAM_ROCKWELL										0x3059
#define FID_PS_ROCKWELL										0x2059
#define FID_DTC_ROCKWELL										0x1059
#define FID_BITSTREAM_RENAULT										0x3058
#define FID_PS_RENAULT										0x2058
#define FID_DTC_RENAULT										0x1058
#define FID_BITSTREAM_RALEIGH										0x3057
#define FID_PS_RALEIGH										0x2057
#define FID_DTC_RALEIGH										0x1057
#define FID_BITSTREAM_QUORUM										0x3056
#define FID_PS_QUORUM										0x2056
#define FID_DTC_QUORUM										0x1056
#define FID_BITSTREAM_PROTEUS										0x3055
#define FID_PS_PROTEUS										0x2055
#define FID_DTC_PROTEUS										0x1055
#define FID_BITSTREAM_PLANTIN										0x3054
#define FID_PS_PLANTIN										0x2054
#define FID_DTC_PLANTIN										0x1054
#define FID_BITSTREAM_PERPETUA										0x3053
#define FID_PS_PERPETUA										0x2053
#define FID_DTC_PERPETUA										0x1053
#define FID_BITSTREAM_PACELLA										0x3052
#define FID_PS_PACELLA										0x2052
#define FID_DTC_PACELLA										0x1052
#define FID_BITSTREAM_NOVARESE										0x3051
#define FID_PS_NOVARESE										0x2051
#define FID_DTC_NOVARESE										0x1051
#define FID_BITSTREAM_NIMROD										0x3050
#define FID_PS_NIMROD										0x2050
#define FID_DTC_NIMROD										0x1050
#define FID_BITSTREAM_NIKIS										0x304f
#define FID_PS_NIKIS										0x204f
#define FID_DTC_NIKIS										0x104f
#define FID_BITSTREAM_NAPOLEAN										0x304e
#define FID_PS_NAPOLEAN										0x204e
#define FID_DTC_NAPOLEAN										0x104e
#define FID_BITSTREAM_MODERN_NO_216										0x304d
#define FID_PS_MODERN_NO_216										0x204d
#define FID_DTC_MODERN_NO_216										0x104d
#define FID_BITSTREAM_MODERN										0x304c
#define FID_PS_MODERN										0x204c
#define FID_DTC_MODERN										0x104c
#define FID_BITSTREAM_MINISTER										0x304b
#define FID_PS_MINISTER										0x204b
#define FID_DTC_MINISTER										0x104b
#define FID_BITSTREAM_MESSIDOR										0x304a
#define FID_PS_MESSIDOR										0x204a
#define FID_DTC_MESSIDOR										0x104a
#define FID_BITSTREAM_MERIDIEN										0x3049
#define FID_PS_MERIDIEN										0x2049
#define FID_DTC_MERIDIEN										0x1049
#define FID_BITSTREAM_MEMPHIS										0x3048
#define FID_PS_MEMPHIS										0x2048
#define FID_DTC_MEMPHIS										0x1048
#define FID_BITSTREAM_MELIOR										0x3047
#define FID_PS_MELIOR										0x2047
#define FID_DTC_MELIOR										0x1047
#define FID_BITSTREAM_MARCONI										0x3046
#define FID_PS_MARCONI										0x2046
#define FID_DTC_MARCONI										0x1046
#define FID_BITSTREAM_MAGNUS										0x3045
#define FID_PS_MAGNUS										0x2045
#define FID_DTC_MAGNUS										0x1045
#define FID_BITSTREAM_MAGNA										0x3044
#define FID_PS_MAGNA										0x2044
#define FID_DTC_MAGNA										0x1044
#define FID_BITSTREAM_MADISON										0x3043
#define FID_PS_MADISON										0x2043
#define FID_DTC_MADISON										0x1043
#define FID_BITSTREAM_LUCIDA										0x3042
#define FID_PS_LUCIDA										0x2042
#define FID_DTC_LUCIDA										0x1042
#define FID_BITSTREAM_LUBALIN_GRAPH										0x3041
#define FID_PS_LUBALIN_GRAPH										0x2041
#define FID_DTC_LUBALIN_GRAPH										0x1041
#define FID_BITSTREAM_LIFE										0x3040
#define FID_PS_LIFE										0x2040
#define FID_DTC_LIFE										0x1040
#define FID_BITSTREAM_LEAWOOD										0x303f
#define FID_PS_LEAWOOD										0x203f
#define FID_DTC_LEAWOOD										0x103f
#define FID_BITSTREAM_KORINNA										0x303e
#define FID_PS_KORINNA										0x203e
#define FID_DTC_KORINNA										0x103e
#define FID_BITSTREAM_JENSON_OLD_STYLE										0x303d
#define FID_PS_JENSON_OLD_STYLE										0x203d
#define FID_DTC_JENSON_OLD_STYLE										0x103d
#define FID_BITSTREAM_JANSON										0x303c
#define FID_PS_JANSON										0x203c
#define FID_DTC_JANSON										0x103c
#define FID_BITSTREAM_JAMILLE										0x303b
#define FID_PS_JAMILLE										0x203b
#define FID_DTC_JAMILLE										0x103b
#define FID_BITSTREAM_ITALIA										0x303a
#define FID_PS_ITALIA										0x203a
#define FID_DTC_ITALIA										0x103a
#define FID_BITSTREAM_IMPRESSUM										0x3039
#define FID_PS_IMPRESSUM										0x2039
#define FID_DTC_IMPRESSUM										0x1039
#define FID_BITSTREAM_HOLLANDER										0x3038
#define FID_PS_HOLLANDER										0x2038
#define FID_DTC_HOLLANDER										0x1038
#define FID_BITSTREAM_HIROSHIGE										0x3037
#define FID_PS_HIROSHIGE										0x2037
#define FID_DTC_HIROSHIGE										0x1037
#define FID_BITSTREAM_HAWTHORN										0x3036
#define FID_PS_HAWTHORN										0x2036
#define FID_DTC_HAWTHORN										0x1036
#define FID_BITSTREAM_GOUDY										0x3035
#define FID_PS_GOUDY										0x2035
#define FID_DTC_GOUDY										0x1035
#define FID_BITSTREAM_GAMMA										0x3034
#define FID_PS_GAMMA										0x2034
#define FID_DTC_GAMMA										0x1034
#define FID_BITSTREAM_GALLIARD										0x3033
#define FID_PS_GALLIARD										0x2033
#define FID_DTC_GALLIARD										0x1033
#define FID_BITSTREAM_FRIZ_QUADRATA										0x3032
#define FID_PS_FRIZ_QUADRATA										0x2032
#define FID_DTC_FRIZ_QUADRATA										0x1032
#define FID_BITSTREAM_FENICE										0x3031
#define FID_PS_FENICE										0x2031
#define FID_DTC_FENICE										0x1031
#define FID_BITSTREAM_EXCELSIOR										0x3030
#define FID_PS_EXCELSIOR										0x2030
#define FID_DTC_EXCELSIOR										0x1030
#define FID_BITSTREAM_ESPRIT										0x302f
#define FID_PS_ESPRIT										0x202f
#define FID_DTC_ESPRIT										0x102f
#define FID_BITSTREAM_ELAN										0x302e
#define FID_PS_ELAN										0x202e
#define FID_DTC_ELAN										0x102e
#define FID_BITSTREAM_EGYPTIENNE										0x302d
#define FID_PS_EGYPTIENNE										0x202d
#define FID_DTC_EGYPTIENNE										0x102d
#define FID_BITSTREAM_EGIZIO										0x302c
#define FID_PS_EGIZIO										0x202c
#define FID_DTC_EGIZIO										0x102c
#define FID_BITSTREAM_EDWARDIAN										0x302b
#define FID_PS_EDWARDIAN										0x202b
#define FID_DTC_EDWARDIAN										0x102b
#define FID_BITSTREAM_EDISON										0x302a
#define FID_PS_EDISON										0x202a
#define FID_DTC_EDISON										0x102a
#define FID_BITSTREAM_DIGI_ANTIQUA										0x3029
#define FID_PS_DIGI_ANTIQUA										0x2029
#define FID_DTC_DIGI_ANTIQUA										0x1029
#define FID_BITSTREAM_DEMOS										0x3028
#define FID_PS_DEMOS										0x2028
#define FID_DTC_DEMOS										0x1028
#define FID_BITSTREAM_CUSHING										0x3027
#define FID_PS_CUSHING										0x2027
#define FID_DTC_CUSHING										0x1027
#define FID_BITSTREAM_CORONA										0x3026
#define FID_PS_CORONA										0x2026
#define FID_DTC_CORONA										0x1026
#define FID_BITSTREAM_CONGRESS										0x3025
#define FID_PS_CONGRESS										0x2025
#define FID_DTC_CONGRESS										0x1025
#define FID_BITSTREAM_CONCORDE_NOVA										0x3024
#define FID_PS_CONCORDE_NOVA										0x2024
#define FID_DTC_CONCORDE_NOVA										0x1024
#define FID_BITSTREAM_CONCORDE										0x3023
#define FID_PS_CONCORDE										0x2023
#define FID_DTC_CONCORDE										0x1023
#define FID_BITSTREAM_CLEARFACE										0x3022
#define FID_PS_CLEARFACE										0x2022
#define FID_DTC_CLEARFACE										0x1022
#define FID_BITSTREAM_CLARENDON										0x3021
#define FID_PS_CLARENDON										0x2021
#define FID_DTC_CLARENDON										0x1021
#define FID_BITSTREAM_CHELTENHAM										0x3020
#define FID_PS_CHELTENHAM										0x2020
#define FID_DTC_CHELTENHAM										0x1020
#define FID_BITSTREAM_CENTURY_OLD_STYLE										0x301f
#define FID_PS_CENTURY_OLD_STYLE										0x201f
#define FID_DTC_CENTURY_OLD_STYLE										0x101f
#define FID_BITSTREAM_CENTURY										0x301e
#define FID_PS_CENTURY										0x201e
#define FID_DTC_CENTURY										0x101e
#define FID_BITSTREAM_CENTENNIAL										0x301d
#define FID_PS_CENTENNIAL										0x201d
#define FID_DTC_CENTENNIAL										0x101d
#define FID_BITSTREAM_CAXTON										0x301c
#define FID_PS_CAXTON										0x201c
#define FID_DTC_CAXTON										0x101c
#define FID_BITSTREAM_ADOBE_CASLON										0x301b
#define FID_PS_ADOBE_CASLON										0x201b
#define FID_DTC_ADOBE_CASLON										0x101b
#define FID_BITSTREAM_CASLON										0x301a
#define FID_PS_CASLON										0x201a
#define FID_DTC_CASLON										0x101a
#define FID_BITSTREAM_CANDIDA										0x3019
#define FID_PS_CANDIDA										0x2019
#define FID_DTC_CANDIDA										0x1019
#define FID_BITSTREAM_BOOKMAN										0x3018
#define FID_PS_BOOKMAN										0x2018
#define FID_DTC_BOOKMAN										0x1018
#define FID_BITSTREAM_BASKERVILLE_HANDCUT										0x3017
#define FID_PS_BASKERVILLE_HANDCUT										0x2017
#define FID_DTC_BASKERVILLE_HANDCUT										0x1017
#define FID_BITSTREAM_BASKERVILLE										0x3016
#define FID_PS_BASKERVILLE										0x2016
#define FID_DTC_BASKERVILLE										0x1016
#define FID_BITSTREAM_BASILIA										0x3015
#define FID_PS_BASILIA										0x2015
#define FID_DTC_BASILIA										0x1015
#define FID_BITSTREAM_BARBEDOR										0x3014
#define FID_PS_BARBEDOR										0x2014
#define FID_DTC_BARBEDOR										0x1014
#define FID_BITSTREAM_AUREALIA										0x3013
#define FID_PS_AUREALIA										0x2013
#define FID_DTC_AUREALIA										0x1013
#define FID_BITSTREAM_NEW_ASTER										0x3012
#define FID_PS_NEW_ASTER										0x2012
#define FID_DTC_NEW_ASTER										0x1012
#define FID_BITSTREAM_ASTER										0x3011
#define FID_PS_ASTER										0x2011
#define FID_DTC_ASTER										0x1011
#define FID_BITSTREAM_AMERICANA										0x3010
#define FID_PS_AMERICANA										0x2010
#define FID_DTC_AMERICANA										0x1010
#define FID_BITSTREAM_AACHEN										0x300f
#define FID_PS_AACHEN										0x200f
#define FID_DTC_AACHEN										0x100f
#define FID_BITSTREAM_NICOLAS_COCHIN										0x300e
#define FID_PS_NICOLAS_COCHIN										0x200e
#define FID_DTC_NICOLAS_COCHIN										0x100e
#define FID_BITSTREAM_COCHIN										0x300d
#define FID_PS_COCHIN										0x200d
#define FID_DTC_COCHIN										0x100d
#define FID_BITSTREAM_ALBERTUS										0x300c
#define FID_PS_ALBERTUS										0x200c
#define FID_DTC_ALBERTUS										0x100c
#define FID_BITSTREAM_ACCOLADE										0x300b
#define FID_PS_ACCOLADE										0x200b
#define FID_DTC_ACCOLADE										0x100b
#define FID_BITSTREAM_PALATINO										0x300a
#define FID_PS_PALATINO										0x200a
#define FID_DTC_PALATINO										0x100a
#define FID_BITSTREAM_GOUDY_OLD_STYLE										0x3009
#define FID_PS_GOUDY_OLD_STYLE										0x2009
#define FID_DTC_GOUDY_OLD_STYLE										0x1009
#define FID_BITSTREAM_BERKELEY_OLD_STYLE										0x3008
#define FID_PS_BERKELEY_OLD_STYLE										0x2008
#define FID_DTC_BERKELEY_OLD_STYLE										0x1008
#define FID_BITSTREAM_ARSIS										0x3007
#define FID_PS_ARSIS										0x2007
#define FID_DTC_ARSIS										0x1007
#define FID_BITSTREAM_UNIVERSITY_ROMAN										0x3006
#define FID_PS_UNIVERSITY_ROMAN										0x2006
#define FID_DTC_UNIVERSITY_ROMAN										0x1006
#define FID_BITSTREAM_BEMBO										0x3005
#define FID_PS_BEMBO										0x2005
#define FID_DTC_BEMBO										0x1005
#define FID_BITSTREAM_GARAMOND										0x3004
#define FID_PS_GARAMOND										0x2004
#define FID_DTC_GARAMOND										0x1004
#define FID_BITSTREAM_GLYPHA										0x3003
#define FID_PS_GLYPHA										0x2003
#define FID_DTC_GLYPHA										0x1003
#define FID_BITSTREAM_BODONI										0x3002
#define FID_PS_BODONI										0x2002
#define FID_DTC_BODONI										0x1002
#define FID_BITSTREAM_CENTURY_SCHOOLBOOK										0x3001
#define FID_PS_CENTURY_SCHOOLBOOK										0x2001
#define FID_DTC_CENTURY_SCHOOLBOOK										0x1001
#define FID_BITSTREAM_URW_ROMAN										0x3000
#define FID_PS_TIMES_ROMAN										0x2000
#define FID_DTC_URW_ROMAN										0x1000
#define FID_WINDOWS										0x0a01
#define FID_BISON										0x0a00
#define FID_LED										0x0600
#define FID_PMSYSTEM										0x0203
#define FID_BERKELEY										0x0202
#define FID_UNIVERSITY										0x0201
#define FID_CHICAGO										0x0200
#define FID_ROMA										0x0001
#define FID_INVALID										0x0000

Fonts are normally referenced by FontID.

Include: fontID.h

| Up: GEOS SDK TechDocs | Up | Prev: FontFamily ... | Next: FontMaker ...


GEOS C Structs: FontMaker ...

| Up: GEOS SDK TechDocs | Up | Prev: FontID ... | Next: GCM_info ...

FontMaker

typedef word FontMaker;
#define FM_PRINTER 				0xf000
#define FM_MICROLOGIC 				0xe000
#define FM_ATECH 				0xd000
#define FM_PUBLIC 				0xc000
#define FM_AGFA 				0x4000
#define FM_BITSTREAM 				0x3000
#define FM_ADOBE 				0x2000
#define FM_NIMBUSQ 				0x1000
#define FM_BITMAP 				0x0000

Include: fontID.h

FontMap

typedef byte FontMap;
#define FM_DONT_USE				0x00ff
#define FM_EXACT				0x0000

Include: fontID.h

FontWeight

typedef ByteEnum FontWeight;
#define FW_ULTRA_LIGHT					0
#define FW_EXTRA_LIGHT 					1
#define FW_LIGHT 					2
#define FW_BOOK 					3
#define FW_NORMAL 					4
#define FW_DEMI 					5
#define FW_BOLD 					6
#define FW_EXTRA_BOLD 					7
#define FW_ULTRA_BOLD 					8
#define FW_BLACK 					9

Include: font.h

FontWidth

typedef 	ByteEnum FontWidth;
#define FWI_NARROW 					0
#define FWI_CONDENSED 					1
#define FWI_MEDIUM 					2
#define FWI_WIDE 					3
#define FWI_EXPANDED 					4

Include: font.h

FormatArray

typedef ClipboardItemFormatInfo FormatArray[CLIPBOARD_MAX_FORMATS];

FormatError

typedef ByteEnum FormatError;
#define FMT_DONE												0
#define FMT_READY												1
#define FMT_RUNNING 												2
#define FMT_DRIVE_NOT_READY 												3
#define FMT_ERR_WRITING_BOOT 												4
#define FMT_ERR_WRITING_ROOT_DIR												5
#define FMT_ERR_WRITING_FAT 												6
#define FMT_ABORTED 												7
#define FMT_SET_VOLUME_NAME_ERR 												8
#define FMT_CANNOT_FORMAT_FIXED_DISKS_IN_CUR_RELEASE 												9
#define FMT_BAD_PARTITION_TABLE 												10
#define FMT_ERR_READING_PARTITION_TABLE 												11
#define FMT_ERR_NO_PARTITION_FOUND 												12
#define FMT_ERR_MULTIPLE_PRIMARY_PARTITIONS 												13
#define FMT_ERR_NO_EXTENDED_PARTITION_FOUND 												14
#define FMT_ERR_CANNOT_ALLOC_SECTOR_BUFFER 												15
#define FMT_ERR_DISK_IS_IN_USE 												16
#define FMT_ERR_WRITE_PROTECTED 												17
#define FMT_ERR_DRIVE_CANNOT_SUPPORT_GIVEN_FORMAT 												18
#define FMT_ERR_INVALID_DRIVE_SPECIFIED 												19
#define FMT_ERR_DRIVE_CANNOT_BE_FORMATTED 												20
#define FMT_ERR_DISK_UNAVAILABLE 												21

FormatIDFromManufacturerAndType

ClipboardItemFormatID FormatIDFromManufacturerAndType(man, typ)
        ManufacturerID		man;
        word		typ;

This macro creates a ClipboardItemFormatID dword value from the given manufacturer ID and format ID.

FunctionID

typedef enum /* word */ {
FUNCTION_ID_ABS,
FUNCTION_ID_ACOS,
FUNCTION_ID_ACOSH,
FUNCTION_ID_AND,
FUNCTION_ID_ASIN,
FUNCTION_ID_ASINH,
FUNCTION_ID_ATAN,
FUNCTION_ID_ATAN2,
FUNCTION_ID_ATANH,
FUNCTION_ID_AVG,
FUNCTION_ID_CHAR,
FUNCTION_ID_CHOOSE,
FUNCTION_ID_CLEAN,
FUNCTION_ID_CODE,
FUNCTION_ID_COLS,
FUNCTION_ID_COS,
FUNCTION_ID_COSH,
FUNCTION_ID_COUNT,
FUNCTION_ID_CTERM,
FUNCTION_ID_DATE,
FUNCTION_ID_DATEVALUE,
FUNCTION_ID_DAY,
FUNCTION_ID_DDB,
FUNCTION_ID_ERR,
FUNCTION_ID_EXACT,
FUNCTION_ID_EXP,
FUNCTION_ID_FACT,
FUNCTION_ID_FALSE,
FUNCTION_ID_FIND,
FUNCTION_ID_FV,
FUNCTION_ID_HLOOKUP,
FUNCTION_ID_HOUR,
FUNCTION_ID_IF,
FUNCTION_ID_INDEX,
FUNCTION_ID_INT,
FUNCTION_ID_IRR,
FUNCTION_ID_ISERR,
FUNCTION_ID_ISNUMBER,
FUNCTION_ID_ISSTRING,
FUNCTION_ID_LEFT,
FUNCTION_ID_LENGTH,
FUNCTION_ID_LN,
FUNCTION_ID_LOG,
FUNCTION_ID_LOWER,
FUNCTION_ID_MAX,
FUNCTION_ID_MID,
FUNCTION_ID_MIN,
FUNCTION_ID_MINUTE,
FUNCTION_ID_MOD,
FUNCTION_ID_MONTH,
FUNCTION_ID_N,
FUNCTION_ID_NA,
FUNCTION_ID_NOW,
FUNCTION_ID_NPV,
FUNCTION_ID_OR,
FUNCTION_ID_PI,
FUNCTION_ID_PMT,
FUNCTION_ID_PRODUCT,
FUNCTION_ID_PROPER,
FUNCTION_ID_PV,
FUNCTION_ID_RANDOM_N,
FUNCTION_ID_RANDOM,
FUNCTION_ID_RATE,
FUNCTION_ID_REPEAT,
FUNCTION_ID_REPLACE,
FUNCTION_ID_RIGHT,
FUNCTION_ID_ROUND,
FUNCTION_ID_ROWS,
FUNCTION_ID_SECOND,
FUNCTION_ID_SIN,
FUNCTION_ID_SINH,
FUNCTION_ID_SLN,
FUNCTION_ID_SQRT,
FUNCTION_ID_STD,
FUNCTION_ID_STDP,
FUNCTION_ID_STRING,
FUNCTION_ID_SUM,
FUNCTION_ID_SYD,
FUNCTION_ID_TAN,
FUNCTION_ID_TANH,
FUNCTION_ID_TERM,
FUNCTION_ID_TIME,
FUNCTION_ID_TIMEVALUE,
FUNCTION_ID_TODAY,
FUNCTION_ID_TRIM,
FUNCTION_ID_TRUE,
FUNCTION_ID_TRUNC,
FUNCTION_ID_UPPER,
FUNCTION_ID_VALUE,
FUNCTION_ID_VAR,
FUNCTION_ID_VARP,
FUNCTION_ID_VLOOKUP,
FUNCTION_ID_WEEKDAY,
FUNCTION_ID_YEAR,
FUNCTION_ID_FILENAME,
FUNCTION_ID_PAGE,
FUNCTION_ID_PAGES,
FUNCTION_ID_FIRST_EXTERNAL_FUNCTION=FUNCTION_ID_FIRST_EXTERNAL_FUNCTION_BASE
} FunctionID;

| Up: GEOS SDK TechDocs | Up | Prev: FontID ... | Next: GCM_info ...


GEOS C Structs: GCM_info ...

| Up: GEOS SDK TechDocs | Up | Prev: FontMaker ... | Next: GCNStandardListType ...

GCM_info

typedef enum /* word */ {
GCMI_MIN_X,
GCMI_MIN_X_ROUNDED,
GCMI_MIN_Y,
GCMI_MIN_Y_ROUNDED,
GCMI_MAX_X,
GCMI_MAX_X_ROUNDED,
GCMI_MAX_Y,
GCMI_MAX_Y_ROUNDED,
} GCM_info;

GCNDriveChangeNotificationType

typedef enum {
GCNDCNT_CREATED,
GCNDCNT_DESTROYED
} GCNDriveChangeNotificationType;

GCNExpressMenuNotificationType

typedef enum {
GCNEMNT_CREATED,
GCNEMNT_DESTROYED
} GCNExpressMenuNotificationType;

GCNListBlockHeader

typedef struct {
LMemBlockHeader			GCNLBH_lmemHeader;
ChunkHandle			GCNLBH_listOfLists;
} GCNListBlockHeader;

GCNListElement

typedef struct {
optr	GCNLE_item;
} GCNListElement;

GCNListHeader

typedef struct {
ChunkArrayHeader				GCNLH_meta;
word				GCNLH_statusEvent;
MemHandle				GCNLH_statusData;
word				GCNLH_statusCount;
/* Start of GCNListOfListElements */
} GCNListHeader;

GCNListOfListsElement

typedef struct {
GCNListType			GCNLOLE_ID;
ChunkHandle			GCNLOLE_list;
} GCNListOfListsElement;

GCNListOfListsHeader

typedef struct {
ChunkArrayHeader				GCNLOL_meta;
/* Start of GCNListOfListsElement's */
} GCNListOfListsHeader;

GCNListParams

typedef struct {
GCNListType		GCNLP_ID;
optr		GCNLP_optr;
} GCNListParams;

GCNListSendFlags

typedef WordFlags GCNListSendFlags;
#define GCNLSF_SET_STATUS										0x8000
#define GCNLSF_IGNORE_IF_STATUS_TRANSITIONING										0x4000

GCNListType

typedef struct {
word	GCNLT_manuf;
word	GCNLT_type;
} GCNListType;

GCNListTypeFlags

typedef WordFlags GCNListTypeFlags;
#define GCNLTF_SAVE_TO_STATE					0x8000

GCNShutdownControlType

typedef enum {
GCNSCT_SUSPEND,
GCNSCT_SHUTDOWN,
GCNSCT_UNSUSPEND
} GCNShutdownControlType;

| Up: GEOS SDK TechDocs | Up | Prev: FontMaker ... | Next: GCNStandardListType ...


GEOS C Structs: GCNStandardListType ...

| Up: GEOS SDK TechDocs | Up | Prev: GCM_info ... | Next: GeodeToken ...

GCNStandardListType

typedef enum {
GCNSLT_FILE_SYSTEM,
GCNSLT_APPLICATION,
GCNSLT_DATE_TIME,
GCNSLT_DICTIONARY,
GCNSLT_EXPRESS_MENU,
GCNSLT_SHUTDOWN_CONTROL
} GCNStandardListType;

GenAppGCNListTypes

In Goc, GenAppGCNListTypes values will be maintained other structures by other names. Standard GEOS list types are part of the GeoWorksGenAppGCNListType enumerated type.

GeneralEvent

typedef enum {
 GE_NO_EVENT=0, 				/* dummy event (NOP) */
 GE_END_OF_SONG=2, 				/* marks end of song */
 GE_SET_PRIORITY=4, 				/* changes sound priority */
 GE_SET_TEMPO=6, 				/* changes sound tempo */
 GE_SEND_NOTIFICATION=8, 				/* sends encoded message */
 GE_V_SEMAPHORE=10 				/* V's a specified semaphore*/
} GeneralEvent;

These represent some of the miscellaneous events which can make up a music buffer.

GenTravelOption

The GenClass defines some values meant to be used in the place of a TravelOption enumerated value. See TravelOption .

GeodeAttrs

typedef WordFlags GeodeAttrs;
#define GA_PROCESS 								0x8000
#define GA_LIBRARY 								0x4000
#define GA_DRIVER 								0x2000
#define GA_KEEP_FILE_OPEN 								0x1000
#define GA_SYSTEM 								0x0800
#define GA_MULTI_LAUNCHABLE 								0x0400
#define GA_APPLICATION 								0x0200
#define GA_DRIVER_INITIALIZED								0x0100
#define GA_LIBRARY_INITIALIZED 								0x0080
#define GA_GEODE_INITIALIZED 								0x0040
#define GA_USES_COPROC 								0x0020
#define GA_REQUIRES_COPROC 								0x0010
#define GA_HAS_GENERAL_CONSUMER_MODE								0x0008
#define GA_ENTRY_POINTS_IN_C 								0x0004

GeodeDefaultDriverType

typedef enum {
GDDT_FILE_SYSTEM = 0,					/* File system driver */
GDDT_KEYBOARD = 2,					/* Keyboard driver */
GDDT_MOUSE = 4,					/* Mouse driver */
GDDT_VIDEO = 6,					/* Video driver */
GDDT_MEMORY_VIDEO = 8,					/* Vidmem driver */
GDDT_POWER_MANAGEMENT = 10					/* Power management driver */
GDDT_TASK = 12					/* Task driver */
} GeodeDefaultDriverType;

The default driver type has one value for each default driver type in GEOS. This type is used with GeodeGetDefaultDriver() and GeodeSetDefaultDriver() .

GeodeGetInfoType

See GeodeGetInfo() .

GeodeHandle

typedef Handle GeodeHandle;

A standard handle that contains information about a loaded geode. When a geode has been loaded, it is referred to by its handle.

GeodeHeapVars

typedef struct {
 word		GHV_heapSpace;
} GeodeHeapVars;

GeodeLoadError

typedef enum {
GLE_PROTOCOL_IMPORTER_TOO_RECENT,
GLE_PROTOCOL_IMPORTER_TOO_OLD,
GLE_FILE_NOT_FOUND,
GLE_LIBRARY_NOT_FOUND,
GLE_FILE_READ_ERROR,
GLE_NOT_GEOS_FILE,
GLE_NOT_GEOS_EXECUTABLE_FILE,
GLE_ATTRIBUTE_MISMATCH,
GLE_MEMORY_ALLOCATION_ERROR,
GLE_NOT_MULTI_LAUNCHABLE,
GLE_LIBRARY_PROTOCOL_ERROR,
GLE_LIBRARY_LOAD_ERROR,
GLE_DRIVER_INIT_ERROR,
GLE_LIBRARY_INIT_ERROR,
GLE_DISK_TOO_FULL,
GLE_FIELD_DETACHING,
} GeodeLoadError;

These errors may be returned by routines that load geodes, including UserLoadApplication() , GeodeUseLibrary() , GeodeUseDriver() , and GeodeLoad() .

| Up: GEOS SDK TechDocs | Up | Prev: GCM_info ... | Next: GeodeToken ...


GEOS C Structs: GeodeToken ...

| Up: GEOS SDK TechDocs | Up | Prev: GCNStandardListType ... | Next: GeoworksMediumID ...

GeodeToken

typedef struct {
TokenChars			GT_chars;
ManufacturerID			GT_manufID;
} GeodeToken;

Defines a token identifier. The GT_chars field is four characters that identify the token; GT_manufID is the identifying number of the manufacturer of the item being referenced.

GeosFileHeaderFlags

typedef WordFlags GeosFileHeaderFlags;
#define GFHF_TEMPLATE						0x8000
#define GFHF_SHARED_MULTIPLE						0x4000
#define GFHF_SHARED_SINGLE						0x2000

GEOS

GeosFileType

typedef enum /* word */ {
GFT_NOT_GEOS_FILE,
GFT_EXECUTABLE,
GFT_VM,
GFT_DATA,
GFT_DIRECTORY,
GFT_LINK
} GeosFileType;

files are divided into several broad categories. You can find out a file's category by getting its FEA_FILE_TYPE extended attribute. This attribute is a member of the GeosFileType enumerated type. This type has the following values:

GFT_NOT_GEOS_FILE
The file is not a GEOS file. This constant is guaranteed to be equal to zero.
GFT_EXECUTABLE
The file is executable; in other words, it is some kind of geode.
GFT_VM
The file is a VM file.
GFT_DATA
The file is a GEOS byte file (see below).
GFT_DIRECTORY
The file is a GEOS directory (not yet implemented).
GFT_LINK
The file is a symbolic link (not yet implemented).

GeoWorksGenAppGCNListType

typedef enum /* word */ {
GAGCNLT_SELF_LOAD_OPTIONS = 0x6800,
GAGCNLT_GEN_CONTROL_NOTIFY_STATUS_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SELECT_STATE_CHANGE,
GAGCNLT_EDIT_CONTROL_NOTIFY_UNDO_STATE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_CHAR_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_PARA_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_TYPE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_SELECTION_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_COUNT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_STYLE_TEXT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_STYLE_SHEET_TEXT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_STYLE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FONT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_POINT_SIZE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FONT_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_JUSTIFICATION_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_FG_COLOR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_BG_COLOR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_PARA_COLOR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_BORDER_COLOR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SEARCH_SPELL_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SEARCH_REPLACE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_CHART_TYPE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_CHART_GROUP_FLAGS,
GAGCNLT_APP_TARGET_NOTIFY_CHART_AXIS_ATTRIBUTES,
GAGCNLT_APP_TARGET_NOTIFY_CHART_MARKER_SHAPE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_CURRENT_TOOL_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_BODY_SELECTION_STATE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_AREA_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_LINE_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_TEXT_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_STYLE_GROBJ_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_STYLE_SHEET_GROBJ_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_BODY_INSTRUCTION_FLAGS_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_GRADIENT_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_RULER_TYPE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_RULER_GRID_CHANGE,
GAGCNLT_TEXT_RULER_OBJECTS,
GAGCNLT_APP_TARGET_NOTIFY_BITMAP_CURRENT_TOOL_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_BITMAP_CURRENT_FORMAT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_FIELD_PROPERTIES_STATUS_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_FIELD_LIST_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_RCP_STATUS_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_FIELD_APPEARANCE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_DUMMY_CHANGE_2,
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_DUMMY_CHANGE_3,
GAGCNLT_APP_NOTIFY_DOC_SIZE_CHANGE,
GAGCNLT_APP_NOTIFY_PAPER_SIZE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_VIEW_STATE_CHANGE,
GAGCNLT_CONTROLLED_GEN_VIEW_OBJECTS,
GAGCNLT_APP_TARGET_NOTIFY_INK_STATE_CHANGE,
GAGCNLT_CONTROLLED_INK_OBJECTS,
GAGCNLT_APP_TARGET_NOTIFY_PAGE_STATE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_DOCUMENT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_DISPLAY_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_DISPLAY_LIST_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_MARKER_SHAPE,
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_POINT,
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_POLYLINE,
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_SMOOTHNESS,
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_OPEN_CLOSE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_ACTIVE_CELL_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_EDIT_BAR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_SELECTION_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_CELL_WIDTH_HEIGHT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_DOC_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_CELL_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_CELL_NOTES_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_DATA_RANGE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_NAME_CHANGE,
GAGCNLT_FLOAT_FORMAT_CHANGE,
GAGCNLT_DISPLAY_OBJECTS_WITH_RULERS,
GAGCNLT_APP_TARGET_NOTIFY_APP_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_LIBRARY_CHANGE,
GAGCNLT_WINDOWS,
GAGCNLT_STARTUP_LOAD_OPTIONS
} GeoWorksGenAppGCNListType;

| Up: GEOS SDK TechDocs | Up | Prev: GCNStandardListType ... | Next: GeoworksMediumID ...


GEOS C Structs: GeoworksMediumID ...

| Up: GEOS SDK TechDocs | Up | Prev: GeodeToken ... | Next: GeoWorksVisContentGCNListType ...

GeoworksMediumID

typedef enum {
            GMID_INVALID = 0x0,
            GMID_SERIAL_CABLE, 				/* unit is SerialPortNum */
            GMID_INFRARED, 				/* unit is SerialPortNum */
            GMID_DATA_MODEM, 				/* unit is SerialPortNum */
            GMID_FAX_MODEM, 				/* unit is SerialPortNum */
            GMID_PRINTER, 				/* unit is printer name in mem block */
            GMID_PARALLEL_PORT, 				/* unit is ParallelPortNum */
            GMID_NETWORK, 				/* unit is NetID */
            GMID_LOOPBACK, 				/* no units */
            GMID_SM 				/* Small Message--no units */
} GeoworksMediumID;

These values, together with MANUFACTURER_ID_GEOWORKS, form MediumType values representing certain standard communications media.

Include: medium.h

GeoWorksMetaGCNListType

typedef enum /* word */ {
MGCNLT_ACTIVE_LIST = 0x00,
MGCNLT_APP_STARTUP = 0x02
} GeoWorksMetaGCNListType;

GeoWorksNotificationType

typedef enum {
GWNT_INK,
GWNT_GEN_CONTROL_NOTIFY_STATUS_CHANGE,
GWNT_SELECT_STATE_CHANGE,
GWNT_UNDO_STATE_CHANGE,
GWNT_STYLE_CHANGE,
GWNT_STYLE_SHEET_CHANGE,
GWNT_TEXT_CHAR_ATTR_CHANGE,
GWNT_TEXT_PARA_ATTR_CHANGE,
GWNT_TEXT_TYPE_CHANGE,
GWNT_TEXT_SELECTION_CHANGE,
GWNT_TEXT_COUNT_CHANGE,
GWNT_TEXT_STYLE_CHANGE,
GWNT_FONT_CHANGE,
GWNT_POINT_SIZE_CHANGE,
GWNT_FONT_ATTR_CHANGE,
GWNT_JUSTIFICATION_CHANGE,
GWNT_TEXT_FG_COLOR_CHANGE,
GWNT_TEXT_BG_COLOR_CHANGE,
GWNT_TEXT_PARA_COLOR_CHANGE,
GWNT_TEXT_BORDER_COLOR_CHANGE,
GWNT_SEARCH_REPLACE_ENABLE_CHANGE,
GWNT_SPELL_ENABLE_CHANGE,
GWNT_CHART_TYPE_CHANGE,
GWNT_CHART_GROUP_FLAGS,
GWNT_CHART_AXIS_ATTRIBUTES,
GWNT_GROBJ_CURRENT_TOOL_CHANGE,
GWNT_GROBJ_BODY_SELECTION_STATE_CHANGE,
GWNT_GROBJ_AREA_ATTR_CHANGE,
GWNT_GROBJ_LINE_ATTR_CHANGE,
GWNT_GROBJ_TEXT_ATTR_CHANGE,
GWNT_GROBJ_BODY_INSTRUCTION_FLAGS_CHANGE,
GWNT_GROBJ_GRADIENT_ATTR_CHANGE,
GWNT_RULER_TYPE_CHANGE,
GWNT_RULER_GRID_CHANGE,
GWNT_RULER_GUIDE_CHANGE,
GWNT_BITMAP_CURRENT_TOOL_CHANGE,
GWNT_BITMAP_CURRENT_FORMAT_CHANGE,
GWNT_FLAT_FILE_FIELD_PROPERTIES_STATUS_CHANGE,
GWNT_FLAT_FILE_FIELD_LIST_CHANGE,
GWNT_FLAT_FILE_RCP_STATUS_CHANGE,
GWNT_FLAT_FIELD_APPEARANCE_CHANGE,
GWNT_FLAT_FILE_DUMMY_CHANGE_2,
GWNT_FLAT_FILE_DUMMY_CHANGE_3,
GWNT_SPOOL_DOC_OR_PAPER_SIZE,
GWNT_VIEW_STATE_CHANGE,
GWNT_INK_HAS_TARGET,
GWNT_PAGE_STATE_CHANGE,
GWNT_DOCUMENT_CHANGE,
GWNT_DISPLAY_CHANGE,
GWNT_DISPLAY_LIST_CHANGE,
GWNT_SPLINE_MARKER_SHAPE,
GWNT_SPLINE_POINT,
GWNT_SPLINE_POLYLINE,
GWNT_SPLINE_SMOOTHNESS,
GWNT_SPLINE_OPEN_CLOSE_CHANGE,
GWNT_UNUSED_1,
GWNT_SPREADSHEET_ACTIVE_CELL_CHANGE,
GWNT_SPREADSHEET_EDIT_BAR_CHANGE,
GWNT_SPREADSHEET_SELECTION_CHANGE,
GWNT_SPREADSHEET_CELL_WIDTH_HEIGHT_CHANGE,
GWNT_SPREADSHEET_DOC_ATTR_CHANGE,
GWNT_SPREADSHEET_CELL_ATTR_CHANGE,
GWNT_SPREADSHEET_CELL_NOTES_CHANGE,
GWNT_SPREADSHEET_DATA_RANGE_CHANGE,
GWNT_FLOAT_FORMAT_CHANGE,
GWNT_MAP_APP_CHANGE,
GWNT_MAP_LIBRARY_CHANGE,
GWNT_TEXT_NAME_CHANGE,
GWNT_CARD_BACK_CHANGE,
GWNT_TEXT_OBJECT_HAS_FOCUS,
GWNT_TEXT_CONTEXT,
GWNT_TEXT_REPLACE_WITH_HWR,
GWNT_HELP_CONTEXT_CHANGE,
GWNT_FLOAT_FORMAT_INIT,
GWNT_HARD_ICON_BAR_FUNCTION,
GWNT_STARTUP_INDEXED_APP,
GWNT_SPOOL_PRINTING_COMPLETE,
GWNT_MODAL_WIN_CHANGE,
GWNT_SPREADSHEET_NAME_CHANGE,
GWNT_DOCUMENT_OPEN_COMPLETE,
GWNT_EMAIL_SCAN_INBOX,
GWNT_FOCUS_WINDOW_KBD_STATUS,
GWNT_TAB_DOUBLE_CLICK,
GWNT_PAGE_INFO_STATE_CHANGE,
GWNT_CURSOR_POSITION_CHANGE,
GWNT_FAX_NEW_JOB_CREATED,
GWNT_FAX_NEW_JOB_COMPLETED,
GWNT_EMAIL_DATABASE_CHANGE,
GWNT_EMAIL_STATUS_CHANGE,
GWNT_EMAIL_PAGE_PANEL_UPDATE,
GWNT_PCCOM_DISPLAY_CHAR,
GWNT_PCCOM_DISPLAY_STRING,
GWNT_PCCOM_EXIT
} GeoWorksNotificationType;

| Up: GEOS SDK TechDocs | Up | Prev: GeodeToken ... | Next: GeoWorksVisContentGCNListType ...


GEOS C Structs: GeoWorksVisContentGCNListType ...

| Up: GEOS SDK TechDocs | Up | Prev: GeoworksMediumID ... | Next: GStringElement ...

GeoWorksVisContentGCNListType

typedef enum {
VCGCNLT_TARGET_NOTIFY_TEXT_PARA_ATTR_CHANGE = 0x4a00,
 PADDING_VCGCNLT_INVALID_ITEM_000
} GeoWorksVisContentGCNListType;

GetMaskType

typedef ByteEnum GetMaskType;
#define GMT_ENUM				0
#define GMT_BUFFER				1

GetPalType

typedef ByteEnum GetPalType;
#define GPT_ACTIVE				0
#define GPT_CUSTOM				1
#define GPT_DEFAULT				2

GFM_info

See GrFontMetrics() .

GOC_POINT macro

#define GOC_POINT(x,y) GOC_SW(x), GOC_SW(y)

This macro is useful when statically declaring GStrings. A GString is basically an array of bytes; this macro takes two word-length values and breaks them up into four comma-seperated bytes. Thus, they help to make GString commands with variable number of point data appear readable. For example:

...
GSDrawPolyline(3),
  GOC_POINT(0,6), GOC_POINT(10,6), GOC_POINT(10,1),
GSFillPolygon(4,ODD_EVEN),
  GOC_POINT(5,1), GOC_POINT(9,0),
  GOC_POINT(9,5), GOC_POINT(5,6),
...

GOC_SW(), GOC_WORD() macros

#define GOC_SW(a)       GOC_WORD(a)
#define GOC_WORD(a)     ((a)&0xff),(((a)&0xff00)>>8)

These macros are useful when you're statically defining a byte array which contains word-length values. Each macro breaks up a word-length value into two bytes, seperated by a comma.

GraphicPattern

typedef struct {
PatternType 		HP_type;
byte 		HP_data;
} GraphicPattern;

GSControl

typedef WordFlags GSControl;
#define GSC_PARTIAL 				0x0200
#define GSC_ONE 				0x0100
#define GSC_MISC 				0x0080
#define GSC_LABEL 				0x0040
#define GSC_ESCAPE 				0x0020
#define GSC_NEW_PAGE 				0x0010
#define GSC_XFORM 				0x0008
#define GSC_OUTPUT 				0x0004
#define GSC_ATTR 				0x0002
#define GSC_PATH 				0x0001

GSRetType

typedef ByteEnum GSRetType;
#define GSRT_COMPLETE				0
#define GSRT_FORM_FEED				1
#define GSRT_ONE 				2
#define GSRT_ESCAPE 				3
#define GSRT_OUTPUT 				4
#define GSRT_ELEMENT 				5
#define GSRT_FAULT 				0xff

GState

GStates are always referenced by means of GStateHandles, and are documented there.

GStateHandle

typedef Handle GStateHandle;

GStates, or graphics states, are used to interpret graphics commands. Any graphics command that draws anything takes a GStateHandle as an argument. Each GState is associated with a window, and the graphics system uses the GState to determine which window the command should affect.

The GState also holds considerable information determining how drawing commands will be carried out. For instance, it holds the line color. To draw a green line, first one routine set's the GState's line color to green. From then on (or until the line color is changed again), all lines drawn using that GState will be green. Thus, all commands that set color, pattern, or other drawing attributes take a GStateHandle argument.

GStateHandles are also used when creating bitmaps and graphics strings. In this case, the associated window is fake; all drawing commands passed a GStateHandle representing a bitmap or graphics string will affect the data structure instead of being drawn to screen.

GString

typedef void GString;

A GString (short for "Graphics Strings") represents a string of graphics commands. Each GString is made up of one or more GString elements, each of which corresponds to some standard graphics command.

GStrings may be created by means of drawing to a GStateHandle returned by GrCreateState() , but quite often GStrings are declared explicitly. The GString's data is often set up using macros like GSDrawLine() . These macros will output an opcode (of type GStringElement ) and format their macro arguments into data expected with the opcode.

For instance,

GSDrawLine(72, 144, 216, 288);

...would expand to the data:

(GStringElement)			GR_DRAW_LINE
GOC_SW(72), GOC_SW(144), GOC_SW(216), GOC_SW(288);

...where GOC_SW() is a macro that breaks up a word-length value into two bytes. Fundamentally, a GString is just an array of bytes.

Thus, these macros just represent data, though they look like normal kernel graphics commands.

| Up: GEOS SDK TechDocs | Up | Prev: GeoworksMediumID ... | Next: GStringElement ...


GEOS C Structs: GStringElement ...

| Up: GEOS SDK TechDocs | Up | Prev: GeoWorksVisContentGCNListType ... | Next: GStringErrorType ...

GStringElement

typedef ByteEnum GStringElement;
/* The following elements are defined :
    (Miscellaneous GString opcodes:)
GR_END_STRING,
GR_COMMENT,					(data: variable (word (length of code), code))
GR_NULL_OP,
GR_SET_GSTRING_BOUNDS,					(data: 8 bytes 	(4 swords))
GR_LABEL,					(data: 2 bytes 	(word))
GR_ESCAPE,					(data: variable (word (size of code), code))
GR_NEW_PAGE,
    (Coordinate Transform opcodes:)
GR_APPLY_ROTATION,					(data: 4 bytes 	(WWFixed))
GR_APPLY_SCALE,					(data: 8 bytes 	(2 WWFixed))
GR_APPLY_TRANSLATION,					(data: 8 bytes 	(2 WWFixed))
GR_APPLY_TRANSFORM,					(data: 26 bytes	(4 WWFixed, 2 DWFixed))
GR_APPLY_TRANSLATION_DWORD,					(data: 8 bytes 	(2 sdwords))
GR_SET_TRANSFORM,					(data: 26 bytes (4 WWFixed, 2 DWFixed))
GR_SET_NULL_TRANSFORM,
GR_SET_DEFAULT_TRANSFORM,
GR_INIT_DEFAULT_TRANSFORM,
GR_SAVE_TRANSFORM,
GR_RESTORE_TRANSFORM,
    (Output opcodes:)
GR_DRAW_LINE,					(data: 8 bytes 	(4 swords))
GR_DRAW_LINE_TO,					(data: 4 bytes 	(2 swords))
GR_DRAW_REL_LINE_TO					(data: 8 bytes 	(2 WWFixed))
GR_DRAW_HLINE,					(data: 6 bytes 	(3 swords))
GR_DRAW_HLINE_TO,					(data: 2 bytes 	(sword))
GR_DRAW_VLINE,					(data: 6 bytes 	(3 swords))
GR_DRAW_VLINE_TO,					(data: 2 bytes 	(sword))
GR_DRAW_POLYLINE,					(data: variable (word (# of points), points)
GR_DRAW_ARC,					(data: 14 bytes	(ArcCloseType, 6 swords))
GR_DRAW_ARC_3POINT, 					(data: 14 bytes	(ArcCloseType, 6 swords))
GR_DRAW_ARC_3POINT_TO, 					(data: 10 bytes	(ArcCloseType, 4 swords))
GR_DRAW_REL_ARC_3POINT_TO, 					(data: 18 bytes	(ArcCloseType, 4 WWFixed))
GR_DRAW_RECT,					(data: 8 bytes 	(4 swords))
GR_DRAW_RECT_TO,					(data: 4 bytes 	(2 swords))
GR_DRAW_ROUND_RECT,					(data: 10 bytes	(word, 4 swords))
GR_DRAW_ROUND_RECT_TO,					(data: 6 bytes 	(word, 2 swords))
GR_DRAW_SPLINE,					(data: variable (word (# of points), points))
GR_DRAW_SPLINE_TO,					(data: variable (word (# of points), points))
GR_DRAW_CURVE,					(data: 16 bytes	(8 swords))
GR_DRAW_CURVE_TO, 					(data: 12 bytes	(6 swords))
GR_DRAW_REL_CURVE_TO, 					(data: 24 bytes	(6 WWFixed))
GR_DRAW_ELLIPSE,					(data: 8 bytes 	(4 swords))
GR_DRAW_POLYGON,					(data: variable (word (# of points), points))
GR_DRAW_POINT,					(data: 4 bytes 	(2 words))
GR_DRAW_POINT_CP,
GR_BRUSH_POLYLINE,					(data: variable (word (# of points), 2 bytes,
                            points))
GR_DRAW_CHAR,					(data: 5 bytes) 	(Chars, 2 swords))
GR_DRAW_CHAR_CP,					(data: 1 byte) 	(Chars))
GR_DRAW_TEXT,					(data: variable (sword, sword,
                     word (length of string),
                            string (not null terminated)))
GR_DRAW_TEXT_CP,					(data: variable (word (length of string),
                             string (not null terminated)))
GR_DRAW_TEXT_PTR,					(data: 6 bytes 	(2 swords, (char *)))
GR_DRAW_TEXT_OPTR,					(data: 6 bytes 	(2 swords, optr))
GR_DRAW_PATH,
GR_FILL_RECT,					(data: 8 bytes 	(4 swords))
GR_FILL_RECT_TO,					(data: 4 bytes 	(2 swords))
GR_FILL_ROUND_RECT,					(data: 10 bytes	(4 swords, word))
GR_FILL_ROUND_RECT_TO,					(data: 6 bytes 	(2 swords, word))
GR_FILL_ARC,					(data: 14 bytes (ArcCloseType, 6 swords))
GR_FILL_POLYGON,					(data: variable (word (# of points),
                            RegionFillRule, points))
GR_FILL_ELLIPSE,					(data: 8 bytes 	(2 swords))
GR_FILL_PATH,					(data: 1 byte 	 (RegionFillRule))
GR_FILL_ARC_3POINT, 					(data: 14 bytes	(ArcCloseType, 6 swords))
GR_FILL_ARC_3POINT_TO 					(data: 10 bytes	(ArcCloseType, 4 swords))
GR_FILL_BITMAP, 					(data: 6 bytes 	(2 swords, word))
GR_FILL_BITMAP_CP, 					(data: 2 bytes 	(word))
GR_FILL_BITMAP_OPTR,
GR_DRAW_BITMAP,					(data: 6 bytes 	(2 swords, word))
GR_DRAW_BITMAP_CP,					(data: 2 bytes 	(word))
GR_DRAW_BITMAP_OPTR,					(data: 6 bytes 	(2 swords, optr))
GR_DRAW_BITMAP_PTR,					(data: 6 bytes 	(2 swords, *))
GSE_BITMAP_SLICE,					(data: variable)
    (Drawing Attribute opcodes:)
GR_SAVE_STATE,
GR_RESTORE_STATE,
GR_SET_MIX_MODE,					(data: 1 byte 	 (MixMode))
GR_MOVE_TO,					(data: 4 bytes 	(2 swords))
GR_REL_MOVE_TO,					(data: 8 bytes 	(2 WWFixed))
GR_CREATE_PALETTE,
GR_DESTROY_PALETTE,
GR_SET_PALETTE_ENTRY,					(data: 4 bytes 	(Color, 3 bytes))
GR_SET_PALETTE,					(data: variable (word (# of entries),
                            entries (3 bytes each)))
GR_SET_LINE_COLOR,					(data: 3 bytes 	(3 bytes))
GR_SET_LINE_MASK,					(data: 1 byte 	 (SysDrawMask))
GR_SET_LINE_COLOR_MAP,					(data: 1 byte 	 (ColorMapMode))
GR_SET_LINE_WIDTH,					(data: 4 bytes 	(WWFixed))
GR_SET_LINE_JOIN,					(data: 1 byte 	 (LineJoin))
GR_SET_LINE_END,					(data: 1 byte 	 (LineEnd))
GR_SET_LINE_ATTR,					(data: 9 bytes 	(CF_RGB, 3 bytes, SysDrawMask,
                        ColorMapMode, LineEnd, LineJoin, LineStyle)
GR_SET_MITER_LIMIT,					(data: 4 bytes 	(WWFixed))
GR_SET_LINE_STYLE,					(data: 2 bytes 	(LineStyle, index))
GR_SET_LINE_COLOR_INDEX,					(data: 1 byte 	 (Color))
GR_SET_CUSTOM_LINE_MASK,					(data: 8 bytes 	(8 bytes))
GR_SET_CUSTOM_LINE_STYLE,					(data: variable (word (index),
                            word (# of on-off dash pairs),
                            pairs (each pair is 2 bytes)))
GR_SET_AREA_COLOR,					(data: 3 bytes 	(3 bytes)
GR_SET_AREA_MASK,					(data: 1 byte 	 (SysDrawMask))
GR_SET_AREA_COLOR_MAP,					(data: 1 byte 	 (ColorMapMode))
GR_SET_AREA_ATTR,					(data: 6 bytes 	(CF_RGB, 3 bytes, SysDrawMask,
                            ColorMapMode))
GR_SET_AREA_COLOR_INDEX,					(data: 1 byte 	 (Color))
GR_SET_CUSTOM_AREA_MASK,					(data: 8 bytes 	(8 bytes))
GR_SET_AREA_PATTERN, 					(data: 2 bytes 	(GraphicPattern))
GR_SET_CUSTOM_AREA_PATTERN, 					(data: variable (GraphicPattern,
                            word (size of data)
                            pattern data))
GR_SET_TEXT_COLOR,					(data: 3 bytes 	(3 bytes))
GR_SET_TEXT_MASK,					(data: 1 byte 	 (SysDrawMask))
GR_SET_TEXT_COLOR_MAP,					(data: 1 byte 	 (ColorMapMode))
GR_SET_TEXT_STYLE,					(data: 2 bytes 	(2 TextStyles))
GR_SET_TEXT_MODE,					(data: 2 bytes 	(2 TextModes))
GR_SET_TEXT_SPACE_PAD,					(data: 3 bytes 	(WBFixed))
GR_SET_TEXT_ATTR,					(data: 20 bytes	(CF_RGB, 3 bytes, SysDrawMask,
                            ColorMapMode, 2 TextStyles,
                            2 TextModes, WBFixed, FontID, word))
GR_SET_FONT,					(data: 5 bytes 	(WBFixed, FontID))
GR_SET_TEXT_COLOR_INDEX,					(data: 1 byte 	 (Color))
GR_SET_CUSTOM_TEXT_MASK,					(data: 8 bytes 	()
GR_SET_TRACK_KERN,					(data: 2 bytes 	(sword))
GR_SET_FONT_WEIGHT, 					(data: 2 bytes 	(FontWeight))
GR_SET_FONT_WIDTH, 					(data: 2 bytes 	(FontWidth))
GR_SET_SUPERSCRIPT_ATTR, 					(data: 2 bytes 	(position, scale))
GR_SET_SUBSCRIPT_ATTR, 					(data: 2 bytes 	(position, scale))
GR_SET_TEXT_PATTERN, 					(data: 2 bytes 	(GraphicPattern))
GR_SET_CUSTOM_TEXT_PATTERN, 					(data: variable (GraphicPattern,
                            word (size of data),
                            pattern data))
    (Path opcodes:)
GR_BEGIN_PATH,					(data: 1 byte 	 (PathCombineParam))
GR_END_PATH,
GR_SET_CLIP_RECT,					(data: 8 bytes 	(4 swords))
GR_SET_WIN_CLIP_RECT,					(data: 8 bytes 	(4 swords))
GR_CLOSE_SUB_PATH,
GR_SET_CLIP_PATH,					(data: 1 byte 	 (flags))
GR_SET_WIN_CLIP_PATH,					(data: 1 byte 	 (flags))
GR_SET_STROKE_PATH												*/

Each Graphics String Element consists of a GStringElement value specifying the type of element and the data associated with the element's operation. For instance, GR_DRAW_CHAR operations need six bytes of data: two bytes holding the Chars value of the character to draw, and two signed words specifying the drawing coordinates.

A good place to find out about the data associated with a particular GStringElement type is the GS...() macros defined in gstring.h .

See Also: GOC_POINT, GOC_SW.

| Up: GEOS SDK TechDocs | Up | Prev: GeoWorksVisContentGCNListType ... | Next: GStringErrorType ...


GEOS C Structs: GStringErrorType ...

| Up: GEOS SDK TechDocs | Up | Prev: GStringElement ... | Next: HeapCongestion ...

GStringErrorType

typedef enum /* word */ {
GSET_NO_ERROR,
GSET_DISK_FULL
} GStringErrorType;

GStringKillType

See GrDestroyGString() .

GStringSetPosType

See GrSetGStringPos() .

GStringType

typedef ByteEnum GStringType;
#define GST_CHUNK				0
#define GST_STREAM 				1
#define GST_VMEM 				2
#define GST_PTR 				3
#define GST_PATH 				4

Handle

typedef word Handle;

HatchDash

typedef struct {
WWFixed		HD_on;
WWFixed		HD_off;
} HatchDash;

HatchLine

typedef struct {
PointWWFixed		HL_origin;
WWFixed		HL_deltaX;
WWFixed		HL_deltaY;
WWFixed		HL_angle;
ColorQuad		HL_color;
word		HL_numDashes;
    /* array of HatchDash structures follows here */
} HatchLine;

HatchPattern

typedef struct {
word HP_numLines;
/* array of HatchLine structures follows here */
} HatchPattern;

HeapAllocFlags

typedef ByteFlags HeapAllocFlags;
#define HAF_ZERO_INIT					0x80
#define HAF_LOCK 					0x40
#define HAF_NO_ERR 					0x20
#define HAF_UI 					0x10
#define HAF_READ_ONLY 					0x08
#define HAF_OBJECT_RESOURCE 					0x04
#define HAF_CODE 					0x02
#define HAF_CONFORMING 					0x01
#define HAF_STANDARD					(0)
#define HAF_STANDARD_NO_ERR					(HAF_NO_ERR)
#define HAF_STANDARD_LOCK					(HAF_LOCK)
#define HAF_STANDARD_NO_ERR_LOCK (HAF_NO_ERR | HAF_LOCK)

HeapAllocFlags indicate how the block should be allocated and initialized. They are not stored and can not be retrieved. Only some of the flags can be passed with MemReAlloc() ; these are noted below. The following flags are available:

HAF_ZERO_INIT
The memory manager should initialize the block to null bytes. This flag may be passed to MemReAlloc() to cause new memory to be zero-initialized.
HAF_LOCK
The memory manager should lock the block after allocating it. To get the block's address, call MemDeref() . This flag may be passed to MemReAlloc() .
HAF_NO_ERR
The memory manager should not return errors. If it cannot allocate block, GEOS will tell the user that there is no memory available and crash. Use of this flag is strongly discouraged. This flag may be passed to MemReAlloc() .
HAF_UI
If both HAF_OBJECT_RESOURCE and HAF_UI are set, this block will be run by the application's UI thread.
HAF_READ_ONLY
The block's data will not be modified. Useful for the debugger.
HAF_OBJECT_RESOURCE
This block will be an object block.
HAF_CODE
This block contains executable code.
HAF_CONFORMING
If the block contains code, the code may be run by a less privileged entity. If the block contains data, the data may be accessed or altered by a less privileged entity.
| Up: GEOS SDK TechDocs | Up | Prev: GStringElement ... | Next: HeapCongestion ...


GEOS C Structs: HeapCongestion ...

| Up: GEOS SDK TechDocs | Up | Prev: GStringErrorType ... | Next: IMCFeatures ...

HeapCongestion

typedef enum /* word */ {
HC_SCRUBBING,
HC_CONGESTED,
HC_DESPERATE
} HeapCongestion;

HeapFlags

typedef ByteFlags HeapFlags;
#define HF_FIXED				0x80
#define HF_SHARABLE 				0x40
#define HF_DISCARDABLE 				0x20
#define HF_SWAPABLE 				0x10
#define HF_LMEM 				0x08
#define HF_DISCARDED 				0x02
#define HF_SWAPPED 				0x01
#define HF_STATIC 				(HF_DISCARDABLE | HF_SWAPABLE)
#define HF_DYNAMIC 				HF_SWAPABLE

The following flags are available:

HF_FIXED
The block will not move from its place in the global heap until it is freed. If this flag is off, the memory manager may move the block while it is unlocked. If the flag is on, the block may not be locked, and HF_DISCARDABLE and HF_SWAPABLE must be off.
HF_SHARABLE
The block may be locked by threads belonging to geodes other than the block's owner.
HF_DISCARDABLE
The block may be discarded when unlocked.
HF_SWAPABLE
The block may be swapped to extended/expanded memory or to the disk swap space when it is unlocked.
HF_LMEM
The block contains a local memory heap. This flag is set automatically by LMemInitHeap() and VMAllocLMem() ; applications should not need to set this flag.
HF_DISCARDED
The memory manager turns this bit on when it discards a block. The bit is turned off when the block is reallocated.
HF_SWAPPED
The memory manager turns this bit on when it swaps a block to extended/expanded memory or to the disk swap space. It turns the bit off when it swaps the block back into the global heap.

HugeArrayDirectory

typedef struct {
LMemBlockHeader			HAD_header;
VMBlockHandle			HAD_data;
ChunkHandle			HAD_dir;
VMBlockHandle			HAD_xdir;
VMBlockHandle			HAD_self;
word			HAD_size;
} HugeArrayDirectory;

IACPConnectFlags

typedef WordFlags IACPConnectFlags;
#define IACPCF_OBEY_LAUNCH_MODEL 							0x0020
#define IACPCF_CLIENT_OD_SPECIFIED 							0x0010
#define IACPCF_FIRST_ONLY 							0x0008
#define IACPCF_SERVER_MODE 							0x0007

Include: iacp.goh

IACPServerFlags

typedef ByteFlags IACPServerFlags;
#define IACPSF_MULTIPLE_INSTANCES 						0x80

Include: iacp.goh

IACPServerMode

IACPServerMode;
#define IACPSM_NOT_USER_INTERACTIBLE 							0
#define IACPSM_IN_FLUX 							1
#define IACPSM_USER_INTERACTIBLE 							2

Include: iacp.goh

IACPSide

typedef enum {
IACPS_CLIENT,
IACPS_SERVER
} IACPSide;

Include: iacp.goh

ImageFlags

typedef ByteFlags ImageFlags;
#define IF_IGNORE_MASK 				0x10
#define IF_BORDER				0x08
#define IF_BITSIZE				0x07 /* Should hold an ImageBitSize */

#define IBS_1 			0
#define IBS_2 			1
#define IBS_4 			2
#define IBS_8 			3
#define IBS_16 			4

| Up: GEOS SDK TechDocs | Up | Prev: GStringErrorType ... | Next: IMCFeatures ...


GEOS C Structs: IMCFeatures ...

| Up: GEOS SDK TechDocs | Up | Prev: HeapCongestion ... | Next: InkControlFeatures ...

IMCFeatures

typedef ByteFlags IMCFeatures;
#define IMCF_MAP				0x01
#define IMC_DEFAULT_FEATURES							IMCF_MAP
#define IMC_DEFAULT_TOOLBOX_FEATURES							0
#define IMC_MAP_MONIKER_SIZE							1024

ImpexDataClasses

typedef WordFlags ImpexDataClasses;
#define IDC_TEXT				0x8000
#define IDC_GRAPHICS 				0x4000
#define IDC_SPREADSHEET 				0x2000
#define IDC_FONT 				0x1000

ImpexFileSelectionData

typedef struct {
FileLongName				IFSD_selection;
PathName				IFSD_path;
word				IFSD_disk;
GenFileSelectorEntryFlags				IFSD_type;
} ImpexFileSelectionData;

ImpexMapFlags

typedef ByteFlags ImpexMapFlags;
#define IMF_IMPORT				0x80
#define IMF_EXPORT				0x40

ImpexMapFileInfoHeader

typedef struct {
LMemBlockHeader				IMFIH_base;
word				IMFIH_fieldChunk;
word				IMFIH_numFields;
} ImpexMapFileInfoHeader;

ImpexTranslationParams

typedef struct {
optr		ITP_impexOD;
Message		ITP_returnMsg;
word 		ITP_dataClass;
FileHandle 		ITP_transferVMFile;
VMChain 		ITP_transferVMChain;
dword 		ITP_internal;
} ImpexTranslationParams;

ImportControlAttrs

typedef WordFlags ImportControlAttrs;
#define ICA_IGNORE_INPUT 0x8000						/* ignore input while import occurs */

ImportControlToolboxFeatures

typedef ByteFlags ImportControlToolboxFeatures;
#define IMPORTCTF_DIALOG_BOX					0x01

IndexArrayBlockHeader

typedef struct {
word	IALMBH_recCount;
word	IALMBH_array;
word	IALMBH_indexField;
word	IALMBH_sortOrder;
} IndexArrayBlockHeader;

Format of a block containing a datastore index of RecordNum s (low word only); see DataStoreBuildIndex() .

InitFileCharConvert

typedef ByteEnum InitFileCharConvert;
#define IFCC_INTACT				0		/* Leave all characters unchanged. */
#define IFCC_UPCASE				1		/* Make all characters upper case. */
#define IFCC_DOWNCASE				2		/* Make all characters lower case. */

This enumerated type describes how InitFileRead...() routines should handle incoming strings.

InitFileReadFlags

typedef WordFlags InitFileReadFlags;
#define IFRF_CHAR_CONVERT				0xc000			/* 2 bits: InitFileCharConvert type */
#define IFRF_READ_ALL	 			0x2000
#define IFRF_FIRST_ONLY	 			0x1000
#define IFRF_SIZE				0x0fff

This record is used with the InitFileRead...() routines. The IFRF_CHAR_CONVERT field is used to indicate whether strings being read should be upcased, downcased, or left unaltered--the type is designated by a value of InitFileCharConvert . The IFRF_SIZE field is used by routines that take a passed buffer; this field indicates the size of the buffer (the maximum number of bytes that can be returned by the routine).

When setting this record, make sure you shift the IFRF_CHAR_CONVERT value left an offset of IFRF_CHAR_CONVERT_OFFSET.

InkBackgroundType

typedef enum {
IBT_NO_BACKGROUND = 0,
IBT_NARROW_LINED_PAPER = 2,
IBT_MEDIUM_LINED_PAPER = 4,
IBT_WIDE_LINED_PAPER = 6,
IBT_NARROW_STENO_PAPER = 8,
IBT_MEDIUM_STENO_PAPER = 10,
IBT_WIDE_STENO_PAPER = 12,
IBT_SMALL_GRID = 14,
IBT_MEDIUM_GRID = 16,
IBT_LARGE_GRID = 18,
IBT_SMALL_CROSS_SECTION = 20,
IBT_MEDIUM_CROSS_SECTION = 22,
IBT_LARGE_CROSS_SECTION = 24,
IBT_TO_DO_LIST = 26,
IBT_PHONE_MESSAGE = 28,
IBT_CUSTOM_BACKGROUND = 30
} InkBackgrountType;

This enumerated type is a set of standard background pictures for use with the Ink Database routines.

| Up: GEOS SDK TechDocs | Up | Prev: HeapCongestion ... | Next: InkControlFeatures ...


GEOS C Structs: InkControlFeatures ...

| Up: GEOS SDK TechDocs | Up | Prev: IMCFeatures ... | Next: InstrumentPatch ...

InkControlFeatures

typedef ByteFlags InkControlFeatures;
#define ICF_PENCIL_TOOL				0x02
#define ICF_ERASER_TOOL				0x01

InkControlToolboxFeatures

typedef ByteFlags InkControlToolboxFeatures;
#define ICTF_PENCIL_TOOL				0x02
#define ICTF_ERASER_TOOL				0x01

InkDBDisplayInfo

See InkDBGetDisplayInfo() .

InkDBFrame

typedef struct {
Rectangle IDBF_bounds;							/* bounds of data to save or coord at
                             * which to load data */
VMFileHandle IDBF_VMFile;							/* VM File to write to/read from */
DBGroupAndItem IDBF_DBGroupAndItem;							/* DB item to save to/load from */
word IDBF_DBExtra;							/* space to skip at start of block */
} InkDBFrame;

InkFlags

typedef ByteFlags InkFlags;
#define IF_HAS_TARGET						0x20
#define IF_DIRTY 						0x10
#define IF_ONLY_CHILD_OF_CONTENT 						0x08
#define IF_CONTROLLED 						0x04
#define IF_INVALIDATE_ERASURES 						0x02
#define IF_HAS_UNDO 						0x01

InkReturnValue

typedef enum {
IRV_NO_REPLY,
/* VisComp objects use VisCallChildUnderPoint to send
 * MSG_META_QUERY_IF_PRESS_IS_INK to its children, and
 * VisCallChildUnderPoint returns this value (0) if there was not child
 * under the point. No object should actually return this value. */
IRV_NO_INK,
/* Return this if the object wants to treat incoming event as mouse data. */
IRV_INK_WITH_STANDARD_OVERRIDE,
/* Return this if the object normally wants ink (the text object does this),
 * but the user can force mouse events instead by pressing the pen and
 * holding for some user-adjustable amount of time. */
IRV_WAIT
/* Return this value if the object under the point is run by a different
 * thread and you want to hold up input (don't do anything with the incoming
 * MSG_META_START_SELECT) `til obj sends MSG_GEN_APPLICATION_INK_QUERY_REPLY
 * to the applicaiton object. */
} InkReturnValue;

This enumerated type is used by objects to let the system know whether incoming pointer events should be interpreted as mouse or pen data.

InsertChildFlags

typedef WordFlags InsertChildFlags
#define ICF_MARK_DIRTY				0x8000
#define ICF_OPTIONS				0x0003

This record specifies how children are to be added to an object tree.

InsertChildOption

typedef ByteEnum InsertChildOption
#define ICO_FIRST					0
#define ICO_LAST					1
#define ICO_BEFORE_REFERENCE					2
#define ICO_AFTER_REFERENCE					3

This enumerated type determines how a child is added and is used with the InsertChildFlags record. It has four enumerations, as shown above.

| Up: GEOS SDK TechDocs | Up | Prev: IMCFeatures ... | Next: InstrumentPatch ...


GEOS C Structs: InstrumentPatch ...

| Up: GEOS SDK TechDocs | Up | Prev: InkControlFeatures ... | Next: InstrumentTable ...

InstrumentPatch

typedef enum {
#define IP_ACOUSTIC_GRAND_PIANO 				0
#define IP_BRIGHT_ACOUSTIC_PIANO 				1
#define IP_ELECTRIC_GRAND_PIANO 				2
#define IP_HONKY_TONK_PIANO 				3
#define IP_ELECTRIC_PIANO_1 				4
#define IP_ELECTRIC_PIANO_2 				5
#define IP_HARPSICORD 				6
#define IP_CLAVICORD 				7
#define IP_CELESTA	 			8

#define IP_GLOCKENSPIEL 				9
#define IP_MUSIC_BOX 				10
#define IP_VIBRAPHONE 				11
#define IP_MARIMBA 				12
#define IP_XYLOPHONE 				13
#define IP_TUBULAR_BELLS 				14
#define IP_DULCIMER 				15

#define IP_DRAWBAR_ORGAN 				16
#define IP_PERCUSSIVE_ORGAN 				17
#define IP_ROCK_ORGAN 				18
#define IP_CHURCH_ORGAN 				19
#define IP_REED_ORGAN 				20
#define IP_ACCORDION 				21
#define IP_HARMONICA 				22
#define IP_TANGO_ACCORDION 				23

#define IP_ACOUSTIC_NYLON_GUITAR 				24
#define IP_ACOUSTIC_STEEL_GUITAR 				25
#define IP_ELECTRIC_JAZZ_GUITAR 				26
#define IP_ELECTRIC_CLEAN_GUITAR 				27
#define IP_ELECTRIC_MUTED_GUITAR 				28
#define IP_OVERDRIVEN_GUITAR 				29
#define IP_DISTORTION_GUITAR 				30
#define IP_GUITAR_HARMONICS 				31

#define IP_ACOUSTIC_BASS 				32
#define IP_ELECTRIC_FINGERED_BASS 33
#define IP_ELECTRIC_PICKED_BASS 34
#define IP_FRETLESS_BASS 				35
#define IP_SLAP_BASS_1 				36
#define IP_SLAP_BASS_2 				37
#define IP_SYNTH_BASS_1 				38
#define IP_SYNTH_BASS_2 				39

#define IP_VIOLIN 				40
#define IP_VIOLA 				41
#define IP_CELLO 				42
#define IP_CONTRABASS 				43
#define IP_TREMOLO_STRINGS 				44
#define IP_PIZZICATO_STRINGS 				45
#define IP_ORCHESTRAL_HARP 				46
#define IP_TIMPANI 				47

#define IP_STRING_ENSEMBLE_1 				48
#define IP_STRING_ENSEMBLE_2 				49
#define IP_SYNTH_STRINGS_1 				50
#define IP_SYNTH_STRINGS_2 				51
#define IP_CHIOR_AAHS 				52
#define IP_VOICE_OOHS 				53
#define IP_SYNTH_VOICE 				54
#define IP_ORCHESTRA_HIT 				55

#define IP_TRUMPET 				56
#define IP_TROMBONE 				57
#define IP_TUBA 				58
#define IP_MUTED_TRUMPET 				59
#define IP_FRENCH_HORN 				60
#define IP_BRASS_SECTION 				61
#define IP_SYNTH_BRASS_1 				62
#define IP_SYNTH_BRASS_2 				63

#define IP_SOPRANO_SAX 				64
#define IP_ALTO_SAX 				65
#define IP_TENOR_SAX 				66
#define IP_BARITONE_SAX 				67
#define IP_OBOE 				68
#define IP_ENGLISH_HORN 				69
#define IP_BASSOON 				70
#define IP_CLARINET 				71

#define IP_PICCOLO 				72
#define IP_FLUTE 				73
#define IP_RECORDER 				74
#define IP_PAN_FLUTE 				75
#define IP_BLOWN_BOTTLE 				76
#define IP_SHAKUHACHI 				77
#define IP_WHISTLE 				78
#define IP_OCARINA 				79

#define IP_LEAD_SQUARE 				80
#define IP_LEAD_SAWTOOTH 				81
#define IP_LEAD_CALLIOPE 				82
#define IP_LEAD_CHIFF 				83
#define IP_LEAD_CHARANG 				84
#define IP_LEAD_VOICE 				85
#define IP_LEAD_FIFTHS 				86
#define IP_LEAD_BASS_LEAD 				87

#define IP_PAD_NEW_AGE 				88
#define IP_PAD_WARM 				89
#define IP_PAD_POLYSYNTH 				90
#define IP_PAD_CHOIR 				91
#define IP_PAD_BOWED 				92
#define IP_PAD_METALLIC 				93
#define IP_PAD_HALO 				94
#define IP_PAD_SWEEP 				95

#define IP_FX_RAIN 				96
#define IP_FX_SOUNDTRACK 				97
#define IP_FX_CRYSTAL 				98
#define IP_FX_ATMOSPHERE 				99
#define IP_FX_BRIGHTNESS 				100
#define IP_FX_GOBLINS 				101
#define IP_FX_ECHOES 				102
#define IP_FX_SCI_FI 				103

#define IP_SITAR 				104
#define IP_BANJO 				105
#define IP_SHAMISEN 				106
#define IP_KOTO 				107
#define IP_KALIMBA 				108
#define IP_BAG_PIPE 				109
#define IP_FIDDLE 				110
#define IP_SHANAI 				111

#define IP_TINKLE_BELL 				112
#define IP_AGOGO 				113
#define IP_STEEL_DRUMS 				114
#define IP_WOODBLOCK 				115
#define IP_TAIKO_DRUM 				116
#define IP_MELODIC_TOM 				117
#define IP_SYNTH_DRUM 				118
#define IP_REVERSE_CYMBAL 				119

#define IP_GUITAR_FRET_NOISE 				120
#define IP_BREATH_NOISE 				121
#define IP_SEASHORE 				122
#define IP_BIRD_TWEET 				123
#define IP_TELEPHONE_RING 				124
#define IP_HELICOPTER 				125
#define IP_APPLAUSE 				126
#define IP_GUNSHOT 				127

#define IP_ACOUSTIC_BASS_DRUM 				128
#define IP_BASS_DRUM_1 				129
#define IP_SIDE_STICK 				130
#define IP_ACOUSTIC_SNARE 				131
#define IP_HAND_CLAP 				132
#define IP_ELECTRIC_SNARE 				133
#define IP_LOW_FLOOR_TOM 				134
#define IP_CLOSED_HI_HAT 				135

#define IP_HIGH_FLOOR_TOM 				136
#define IP_PEDAL_HI_HAT 				137
#define IP_LOW_TOM 				138
#define IP_OPEN_HI_HAT 				139
#define IP_LOW_MID_TOM 				140
#define IP_HI_MID_TOM 				141
#define IP_CRASH_CYMBAL_1 				142
#define IP_HIGH_TOM 				143

#define IP_RIDE_CYMBAL_1 				144
#define IP_CHINESE_CYMBAL 				145
#define IP_RIDE_BELL 				146
#define IP_TAMBOURINE 				147
#define IP_SPLASH_CYMBAL 				148
#define IP_COWBELL 				149
#define IP_CRASH_CYMBAL_2 				150
#define IP_VIBRASLAP 				151

#define IP_RIDE_CYMBAL_2 				152
#define IP_HI_BONGO 				153
#define IP_LOW_BONGO 				154
#define IP_MUTE_HI_CONGA 				155
#define IP_OPEN_HI_CONGA 				156
#define IP_LOW_CONGA 				157
#define IP_HI_TIMBALE 				158
#define IP_LOW_TIMBALE 				159

#define IP_HIGH_AGOGO 				160
#define IP_LOW_AGOGO 				161
#define IP_CABASA 				162
#define IP_MARACAS 				163
#define IP_SHORT_WHISTLE 				164
#define IP_LONG_WHISTLE 				165
#define IP_SHORT_GUIRO 				166
#define IP_LONG_GUIRO 				167

#define IP_CLAVES 				168
#define IP_HI_WOOD_BLOCK 				169
#define IP_LOW_WOOD_BLOCK 				170
#define IP_MUTE_CUICA 				171
#define IP_OPEN_CUICA 				172
#define IP_MUTE_TRIANGLE 				173
#define IP_OPEN_TRIANGLE 				174
} InstrumentPatch;

These are standard simulated instruments.

| Up: GEOS SDK TechDocs | Up | Prev: InkControlFeatures ... | Next: InstrumentTable ...


GEOS C Structs: InstrumentTable ...

| Up: GEOS SDK TechDocs | Up | Prev: InstrumentPatch ... | Next: Language ...

InstrumentTable

typedef enum {
         IT_STANDARD_TABLE=0 			/* default table */
} InstrumentTable;

The sound library uses this enumerated type to keep track of which table of simulated musical instruments to use.

JobStatus

typedef struct {
char			JS_fname[13];			/* std DOS (8.3) spool filename */
char			JS_parent[FILE_LONGNAME_LENGTH+1];
                        /* parent app's name */
char			JS_documentName[FILE_LONGNAME_LENGTH+1];
                        /* document name */
word			JS_numPages;			/* # pages in document */
SpoolTimeStruct			JS_time;			/* time spooled */
byte			JS_printing;			/* TRUE/FALSE if we are printing */
} JobStatus;

Justification

typedef ByteEnum Justification;
#define J_LEFT			0
#define J_RIGHT			1
#define J_CENTER			2
#define J_FULL			3

KeyboardShortcut

typedef ByteFlags KeyboardShortcut;
        #define KS_PHYSICAL     0x80    /* TRUE: match key, not character   */
        #define KS_ALT          0x40    /* TRUE: <ALT> must be pressed      */
        #define KS_CTRL         0x20    /* TRUE: <CTRL> must be pressed     */
        #define KS_SHIFT        0x10    /* TRUE: <SHIFT> must be pressed    */

        #define KS_CHAR_SET_PRINTABLE   (CS_BSW     & 0x0f)
        #define KS_CHAR_SET_CONTROL     (CS_CONTROL & 0x0f)

KeyboardType

typedef ByteEnum KeyboardType;
#define KT_NOT_EXTD				1
#define KT_EXTD				2
#define KT_BOTH				3

KeyMapType

typedef enum /* word */ {
KEYMAP_US_EXTD=1,
KEYMAP_US,
KEYMAP_UK_EXTD,
KEYMAP_UK,
KEYMAP_GERMANY_EXTD,
KEYMAP_GERMANY,
KEYMAP_SPAIN_EXTD,
KEYMAP_SPAIN,
KEYMAP_DENMARK_EXTD,
KEYMAP_DENMARK,
KEYMAP_BELGIUM_EXTD,
KEYMAP_BELGIUM,
KEYMAP_CANADA_EXTD,
KEYMAP_CANADA,
KEYMAP_ITALY_EXTD,
KEYMAP_ITALY,
KEYMAP_LATIN_AMERICA_EXTD,
KEYMAP_LATIN_AMERICA,
KEYMAP_NETHERLANDS,
KEYMAP_NETHERLANDS_EXTD,
KEYMAP_NORWAY_EXTD,
KEYMAP_NORWAY,
KEYMAP_PORTUGAL_EXTD,
KEYMAP_PORTUGAL,
KEYMAP_SWEDEN_EXTD,
KEYMAP_SWEDEN,
KEYMAP_SWISS_FRENCH_EXTD,
KEYMAP_SWISS_FRENCH,
KEYMAP_SWISS_GERMAN_EXTD,
KEYMAP_SWISS_GERMAN,
KEYMAP_FRANCE_EXTD,
KEYMAP_FRANCE,
} KeyMapType;

| Up: GEOS SDK TechDocs | Up | Prev: InstrumentPatch ... | Next: Language ...


GEOS C Structs: Language ...

| Up: GEOS SDK TechDocs | Up | Prev: InstrumentTable ... | Next: LMemType ...

Language

typedef ByteEnum Language;
#define L_DEFAULT				0
#define L_GRAPHIC 				0
#define L_ENGLISH 				1
#define L_GERMAN 				2
#define L_FRENCH 				3
#define L_SPANISH 				4
#define L_ITALIAN 				5
#define L_DANISH 				6
#define L_DUTCH 				7

LargeMouseData

typedef struct {
PointDWFixed				LMD_location;
byte				LMD_buttonInfo;
UIFunctionsActive				LMD_uiFunctionsActive;
} LargeMouseData;

LayerPriority

typedef ByteEnum LayerPriority;
#define LAYER_PRIO_MODAL					 6
#define LAYER_PRIO_ON_TOP					 8
#define LAYER_PRIO_STD					12
#define LAYER_PRIO_ON_BOTTOM					14

LibraryCallType

typedef enum /* word */ {
LCT_ATTACH,				/* The library was just loaded. */
LCT_DETACH,				/* The library is about to be unloaded. */
LCT_NEW_CLIENT,				/* A new client of the library was just loaded. */
LCT_NEW_CLIENT_THREAD,				/* A new thread was just created for a
                 * current client of the library. */
LCT_CLIENT_THREAD_EXIT,				/* A thread was just exited for a current
                 * client of the library. */
LCT_CLIENT_EXIT,				/* Library's client is about to be unloaded. */
} LibraryCallType

This type is used by library entry point routines. Library entry point routines take a value of this enumerated type to determine what, if anything, is to be done.

LineAttr

typedef struct {
byte 		LA_colorFlag;
RGBValue 		LA_color;
SysDrawMask 		LA_mask;
ColorMapMode 		LA_mapMode;
LineEnd 		LA_end;
LineJoin	 	LA_join;
LineStyle 		LA_style;
WWFixed 		LA_width;
} LineAttr;

LineEnd

typedef ByteEnum LineEnd;
#define LE_BUTTCAP				0
#define LE_ROUNDCAP				1
#define LE_SQUARECAP 				2
#define LAST_LINE_END_TYPE				LE_SQUARECAP

Line ends determine how the graphics system will draw the end of a line segment.

LineJoin

typedef ByteEnum LineJoin;
#define LJ_MITERED					0
#define LJ_ROUND					1
#define LJ_BEVELED					2
#define LAST_LINE_JOIN_TYPE					LJ_BEVELED

This enumerated type determines how the graphics system will draw corners of rectangles and polylines.

LineStyle

typedef ByteEnum LineStyle;
#define LS_SOLID					0
#define LS_DASHED 					1
#define LS_DOTTED 					2
#define LS_DASHDOT 					3
#define LS_DASHDDOT 					4
#define LS_CUSTOM 					5
#define MAX_DASH_ARRAY_PAIRS 					5

The LineStyle type describes a line's "dottedness." Lines using custom dashes will work with the DashPairArray structure:

typedef word    DashPairArray[MAX_DASH_ARRAY_PAIRS*2];

LMemBlockHeader

typedef struct {
MemHandle		LMBH_handle;
word		LMBH_offset;
word		LMBH_flags;
LMemTypes		LMBH_lmemType;
word		LMBH_blockSize;
word		LMBH_nHandles;
word		LMBH_freeList;
word		LMBH_totalFree;
} LMemBlockHeader;

This structure is found at the beginning of every block which contains an LMem heap. You can examine any of the fields by locking the block and casting its address to a *LMemBlockHeader . You should not, however, change any of the fields yourself; they are managed by the LMem routines.

Contents: The header has the following fields:

LMBH _handle
The handle of this block.
LMBH _offset
The offset from the beginning of the block to the beginning of the heap.
LMBH _flags
The LocalMemoryFlags currently set for the block. The flags are described in the entry for LMemInitHeap() .
LMBH _lmemType
The type of LMem heap in this block. This field is a member of the LMemType enumerated type, described in the entry for LMemInitHeap() .
LMBH _blockSize
The total size of this block. This size may change in either direction as a result of chunk allocation and heap compaction.
LMBH _nHandles
The number of handles available in the chunk handle table. Not all of these chunks are necessarily allocated as owned or free chunks. The table grows automatically when necessary.
LMBH _freeList
The chunk handle of the first free chunk in the linked list of free chunks.
LMBH _totalFree
The total amount of free space in the LMem heap.

Warnings: Do not change the settings of the LMemBlockHeader . They are automatically maintained by the LMem routines.

Include: lmem.h

See Also: LMemInitHeap().

| Up: GEOS SDK TechDocs | Up | Prev: InstrumentTable ... | Next: LMemType ...


GEOS C Structs: LMemType ...

| Up: GEOS SDK TechDocs | Up | Prev: Language ... | Next: LocalNumericFormat ...

LMemType

typdef enum {
LMEM_TYPE_GENERAL,
LMEM_TYPE_WINDOW,
LMEM_TYPE_OBJ_BLOCK,
LMEM_TYPE_GSTATE,
LMEM_TYPE_FONT_BLK,
LMEM_TYPE_GSTRING,
LMEM_TYPE_DB_ITEMS
} LMemType;

LMem heaps are created for many different purposes. Some of these purposes require the heap to have special functionality. For this reason, when you create an LMem heap, you must specify what it will be used for. The following types are available:

LMEM_TYPE_GENERAL
The LMem heap will be used for general data storage, possibly including a chunk, name, or element array. When an application creates an LMem heap, it will almost always be of type "General" or "Object."
LMEM_TYPE_OBJ_BLOCK
Objects are stored in object blocks, which are LMem heaps. An object block has some extra header information and contains one chunk which contains only flags. All the objects in the block are stored as chunks on the heap. Applications can directly create object blocks.
LMEM_TYPE_WINDOW
Windows are stored in memory as LMem heaps. The header contains information about the window; each region in the window is stored as a chunk. Applications will not directly create Window heaps.
LMEM_TYPE_GSTATE
A GState is an LMem heap. The GState information is in the header, and the application clip-rectangle is stored in a chunk. Applications do not directly create GState blocks; rather, they call a GState creation routine, which creates the block.
LMEM_TYPE_FONT_BLOCK
Font blocks are stored as LMem heaps. Applications do not create font blocks directly.
LMEM_TYPE_GSTRING
Whenever a GString is created or loaded, a GString LMem heap is created, and elements are added as chunks. The heap is created automatically by the GString routines; applications should not create GString blocks.
LMEM_TYPE_DB_ITEMS
The Virtual Memory mechanism provides routines to create and manage database items, short pieces of data which are dynamically allocated and are saved with the VM file. Applications do not directly allocate DB blocks; rather, they call DB routines, which see to it that the blocks are created.

Include: lmem.h

LocalDistanceFlags

typedef WordFlags LocalDistanceFlags;
#define LDF_FULL_NAMES							0x8000
#define LDF_PRINT_PLURAL_IF_NEEDED							0x4000

LocalCmpStringsDosToGeosFlags

See LocalCmpStringsDosToGeos() .

LocalCurrencyFormat

typedef struct {
byte	currencyFormatFlags;
byte	currencyDigits;
word	thousandsSeparator;
word	decimalSeparator;
word	listSeparator;
} LocalCurrencyFormat;

LocalMemoryFlags

typedef WordFlags LocalMemoryFlags;
#define LMF_HAS_FLAGS				0x8000
#define LMF_IN_RESOURCE 				0x4000
#define LMF_DETACHABLE 				0x2000
#define LMF_DUPLICATED 				0x1000
#define LMF_RELOCATED 				0x0800
#define LMF_AUTO_FREE 				0x0400
#define LMF_IN_LMEM_ALLOC 				0x0200
#define LMF_IS_VM				0x0100
#define LMF_NO_HANDLES 				0x0080
#define LMF_NO_ENLARGE 				0x0040
#define LMF_RETURN_ERRORS 				0x0020
#define LMF_DEATH_COUNT 				0x0007
#define STD_LMEM_OBJECT_FLAGS						(LMF_HAS_FLAGS | LMF_RELOCATED)

When an LMem heap is allocated, certain flags are passed to indicate properties the heap should have. Some of the flags are passed only for system-created heaps. The flags are stored in a word-length record ( LocalMemoryFlags ); the record also contains flags indicating the current state of the heap. The LocalMemoryFlags are listed below:

LMF_HAS_FLAGS
Set if the block has a chunk containing only flags. This flag is set for object blocks; it is usually cleared for general LMem heaps.
LMF_IN_RESOURCE
Set if the block has just been loaded from a resource and has not been changed since being loaded. This flag is set only for object blocks created by the compiler.
LMF_DETACHABLE
Set if the block is an object block which can be saved to a state file.
LMF_DUPLICATED
Set if block is an object block created by the ObjDuplicateResource() routine. This flag should not be set by applications.
LMF_RELOCATED
Set if all the objects in the block have been relocated. The object system sets this when it has relocated all the objects in the block.
LMF_AUTO_FREE
This flag is used by several object routines. It indicates that if the block's in-use count drops to zero, the block may be freed. This flag should not be set by applications.
LMF_IN_MEM_ALLOC
This flag is used in error-checking code to prevent the heap from being validated while a chunk is being allocated. For internal use only--do not modify .
LMF_IS_VM
Set if the LMem heap is in a VM block and the block should be marked dirty whenever a chunk is marked dirty. This flag is automatically set by the VM code when an LMem heap is created in or attached to a VM file. This flag should not be set by applications.
LMF_NO_HANDLES
Set if block does not use chunk handles. A block can be set to simulate the C malloc() routine; in this case, chunks are not relocated after being created, so chunk handles are not needed. Ordinarily, these blocks are created by the malloc() routine, not by applications.
LMF_NO_ENLARGE
Indicates that the local-memory routines should not enlarge this block to fulfill chunk requests. This guarantees that the block will not be moved by a chunk allocation request; however, it makes these requests more likely to fail.
LMF_RETURN_ERRORS
Set if local memory routines should return errors when allocation requests cannot be fulfilled. If the flag is not set, allocation routines will fatal-error if they cannot comply with requests. This flag is generally clear for expandable LMem blocks, since many system routines (such as ObjInstantiate() ) are optimized in such a way that they cannot deal with LMem allocation errors.
LMF_DEATH_COUNT
This field occupies the least significant three bits of the flag field. It means nothing if the value is zero. If it is non-zero, it indicates the number of remove-block messages left which must hit BlockDeathCommon before it will free the block. This flag is used by error-checking code in the kernel.
STD_LMEM_OBJ_FLAGS
This is a constant which combines the LMF_HAS_FLAGS and LMF_RELOCATED flags. These flags should be set for all object blocks.

Include: lmem.h

| Up: GEOS SDK TechDocs | Up | Prev: Language ... | Next: LocalNumericFormat ...


GEOS C Structs: LocalNumericFormat ...

| Up: GEOS SDK TechDocs | Up | Prev: LMemType ... | Next: MediumUnitType ...

LocalNumericFormat

typedef struct {
byte	numberFormatFlags;
byte	decimalDigits;
word	thousandsSeparator;
word	decimalSeparator;
word	listSeparator;
} LocalNumericFormat;

LocalQuotes

typedef struct {
word	frontSingle;
word	endSingle;
word	frontDouble;
word	endDouble;
} LocalQuotes;

ManufacturerFromFormatID

ManufacturerID ManufacturerFromFormatID(type);
        ClipboardItemFormatID			type;

This macro extracts the manufacturer ID from the given clipboard format ID and manufacturer value.

ManufacturerID

typedef word ManufacturerID
#define MANUFACTURER_ID_GEOWORKS									 0
#define MANUFACTURER_ID_APP_LOCAL									 1
#define MANUFACTURER_ID_DOS_LAUNCHER									 5
#define MANUFACTURER_ID_SDK									 8
#define MANUFACTURER_ID_GENERIC									10

While geode.h defines many other ManufacturerIDs for specific developers, the only one of these which is useful to know about when developing for the Nokia 9000i communicator is MANUFACTURER_ID_NOKIA.

Each company should have its own manufacturer ID which is unique to the company and ensures that its applications' GeodeTokens do not conflict with those of other manufacturers. If you don't have a manufacturer ID but think you should, contact Geoworks Developer Support.

MapColorToMono

typedef ByteEnum MapColorToMono;
#define CMT_CLOSEST				0
#define CMT_DITHER 				1

This type determines what the graphics system will do when trying to draw in an unavailable color. It will either draw in the closest color, or else mix two or more close colors to get as close as possible overall.

MapListBlockHeader

typedef struct {
LMemBlockHeader				MLBH_base;
word				MLBH_numDestFields;
word				MLBH_chunk1;
} MapListBlockHeader;

MarginDimensions

typedef struct {
int	leftMargin;
int	topMargin;
int	rightMargin;
int	bottomMargin;
} MarginDimensions;

MAX_FIELD_NAME_LENGTH

#define MAX_FIELD_NAME_LENGTH					40

Maximum size of a field name in a datastore. See DataStoreAddField() .

MeasurementType

typedef ByteEnum MeasurementType;
#define MEASURE_US				0
#define MEASURE_METRIC				1

MediaType

typedef enum /* byte */ {
#define MEDIA_NONEXISTENT				0
#define MEDIA_160K 				1
#define MEDIA_180K 				2
#define MEDIA_320K 				3
#define MEDIA_360K 				4
#define MEDIA_720K 				5
#define MEDIA_1M2 				6
#define MEDIA_1M44 				7
#define MEDIA_2M88 				8
#define MEDIA_FIXED_DISK 				9
#define MEDIA_CUSTOM 				10
} MediaType;

The MediaType enumerated type indicates how a disk is formatted. A member of this enumerated type is returned by some disk-information routines (e.g. DriveGetDefaultMedia() ). A MediaType value is also passed to DiskFormat() , indicating how the disk should be formatted.

MediumAndUnit

typedef struct {
            MediumType 		MU_medium;
            MediumUnitType 		MU_unitType;
            word 		MU_unit;
} MediumAndUnit;

This structure represents an instance of some communication medium-generally this corresponds to some hardware port or peripheral device.

Include: medium.h

| Up: GEOS SDK TechDocs | Up | Prev: LMemType ... | Next: MediumUnitType ...


GEOS C Structs: MediumUnitType ...

| Up: GEOS SDK TechDocs | Up | Prev: LocalNumericFormat ... | Next: MixMode ...

MediumUnitType

typedef ByteEnum MediumUnitType;
        #define MUT_NONE      0x0
        #define MUT_INT       0x1
        #define MUT_MEM_BLOCK 0x2
        #define MUT_ANY       0x3

Include: medium.h

MediumType

typedef struct {
            word            MET_id;
            ManufacturerID  MET_manuf;
} MediumType;

This structure represents a communication medium. Several standard media may be represented with a MET_manuf of MANUFACTURER_ID_GEOWORKS and a GeoworksMediumID in MET_id .

Include: medium.h

MemGetInfoType

See MemGetInfo() .

MemHandle

typedef Handle MemHandle;

Message

typedef word Message;

MessageError

typedef enum /* word */ {
 MESSAGE_NO_ERROR,				/* no error was encountered */
 MESSAGE_NO_HANDLES				/* no handle could be allocated
                 * and MF_CAN_DISCARD_IF_DESPARATE
                 * was passed */
} MessageErrors;

A MessageError is returned by the assembly routine ObjMessage() in assembly to indicate whether the message was successfully sent. This is not encountered by C applications.

MessageFlags

typedef WordFlags MessageFlags;
#define MF_CALL							0x8000			/* @call */
#define MF_FORCE_QUEUE 							0x4000
#define MF_STACK							0x2000			/* @stack */
#define MF_CHECK_DUPLICATE 							0x0800
#define MF_CHECK_LAST_ONLY 							0x0400
#define MF_REPLACE 							0x0200
#define MF_CUSTOM							0x0100
#define MF_FIXUP_DS 							0x0080
#define MF_FIXUP_ES 							0x0040
#define MF_DISCARD_IF_NO_MATCH 							0x0020
#define MF_MATCH_ALL 							0x0010
#define MF_INSERT_AT_FRONT 							0x0008			/* puts at front of queue */
#define MF_CAN_DISCARD_IF_DESPERATE 							0x0004
#define MF_RECORD 							0x0002			/* @record */
#define MF_DISPATCH_DONT_FREE 							0x0002

MessageFlags are specified in the assembly routine ObjMessage() . Most of these flags are set properly by Goc and the kernel in C. See the reference entries for the Goc keywords @call and @send .

MessageHandle

typedef Handle MessageHandle;

MessageMethod

typedef void	 MessageMethod();

Goc uses this type when building up method tables for classes.

MinIncrementType

typedef union {
MinUSMeasure				MIT_US;
MinMetricMeasure				MIT_METRIC;
MinPointMeasure				MIT_POINT;
MinPicaMeasure				MIT_PICA;
} MinIncrementType;

MinMetricMeasure

typedef ByteEnum MinMetricMeasure;
#define MMM_MILLIMETER					0
#define MMM_HALF_CENTIMETER					1
#define MMM_CENTIMETER 					2

MinPicaMeasure

typedef ByteEnum MinPicaMeasure;
#define MPM_PICA					0
#define MPM_INCH					1

MinPointMeasure

typedef ByteEnum MinPointMeasure;
#define MPM_25_POINT					0
#define MPM_50_POINT					1
#define MPM_100_POINT					2

MinUSMeasure

typedef ByteEnum MinUSMeasure;
#define MUSM_EIGHTH_INCH					0
#define MUSM_QUARTER_INCH					1
#define MUSM_HALF_INCH 					2
#define MUSM_ONE_INCH 					3

| Up: GEOS SDK TechDocs | Up | Prev: LocalNumericFormat ... | Next: MixMode ...


GEOS C Structs: MixMode ...

| Up: GEOS SDK TechDocs | Up | Prev: MediumUnitType ... | Next: NULL ...

MixMode

typedef ByteEnum MixMode;
#define MM_CLEAR			0	/* clear destination */
#define MM_COPY			1	/* new drawing is opaque */
#define MM_NOP 			2	/* no drawing */
#define MM_AND 			3	/* logical AND of new and old colors */
#define MM_INVERT 4				/* inverse of old color */
#define MM_XOR 			5	/* XOR of new and old colors */
#define MM_SET 			6	/* set destination black */
#define MM_OR 			7	/* logical OR of new and old colors */

The MixMode determines what the graphics system will do when drawing one thing on top of another.

ModemResultCode

typedef enum {
MRC_NOT_SUPPORTED = 1,
MRC_DRIVER_IN_USE,
MRC_TIMEOUT,
MRC_UNKNOWN_RESPONSE,
MRC_OK,
RC_ERROR,
MRC_BUSY,
MRC_NO_DIALTONE,
MRC_NO_ANSWER,
MRC_NO_CARRIER,
MRC_CONNECT,
MRC_CONNECT_1200,
MRC_CONNECT_2400,
MRC_CONNECT_4800,
MRC_CONNECT_9600,
MRC_BLACKLISTED,
MRC_DELAYED,
} ModemResultCode;

Error codes returned by modem routines.

MonoTransfer

typedef struct {
byte MT_gray[256];
} MonoTransfer;

MouseReturnFlags

typedef WordFlags MouseReturnFlags;
#define MRF_PROCESSED							0x8000
#define MRF_REPLAY 							0x4000
#define MRF_PREVENT_PASS_THROUGH 							0x2000
#define MRF_SET_POINTER_IMAGE 							0x1000
#define MRF_CLEAR_POINTER_IMAGE 							0x0800

These flags are used in various parts of the system that work with mouse input. Which values are appropriate to pass will vary based on context.

MouseReturnParams

typedef struct {
word 				unused;
MouseReturnFlags 				flags;
optr 				ptrImage;
} MouseReturnParams;

This structure is used in certain areas of the system which work with mouse input.

NameArrayAddFlags

typedef WordFlags NameArrayAddFlags;
#define NAAF_SET_DATA_ON_REPLACE 0x8000

NameArrayElement

typedef struct {
RefElementHeader NAE_meta;
} NameArrayElement;

NameArrayHeader

typedef struct{
ElementArrayHeader				NAH_meta;
word				NAH_dataSize;					/* Size of data section of
                                     * each element */
} NameArrayHeader;

Every name array must begin with a NameArrayHeader . Since name arrays are special kinds of element arrays, the NameArrayHeader must itself begin with an ElementArrayHeader . The structure contains one additional field, NAH_dataSize . This field specifies how long the data section of every element is. Applications may examine this field, but they must not change it.

NameArrayMaxElement

typedef struct {
RefElementHeader NAME_meta;
byte NAME_data[NAME_ARRAY_MAX_DATA_SIZE];
char NAME_name[NAME_ARRAY_MAX_NAME_SIZE];
} NameArrayMaxElement;

NO_ERROR_RETURNED

#define NO_ERROR_RETURNED				0

NoteType

typedef ByteEnum NoteType;
#define NT_INK			0
#define NT_TEXT			1

NotificationType

typedef struct {
ManufacturerID			NT_manuf;
word			NT_type;
} NotificationType;

NotifyInkHasTarget

typedef struct {
optr	NIHT_optr;
} NotifyInkHasTarget;

| Up: GEOS SDK TechDocs | Up | Prev: MediumUnitType ... | Next: NULL ...


GEOS C Structs: NULL ...

| Up: GEOS SDK TechDocs | Up | Prev: MixMode ... | Next: OperatorType ...

NULL

#undef NULL
#define NULL		0

NullChunk

#define NullChunk		((ChunkHandle) 0)

NullClass

#define NullClass		((ClassStruct *) 0)

NullHandle

#define NullHandle		((Handle) 0)

NullOptr

#define NullOptr		((optr) 0)

NumberFormatFlags

typedef ByteFlags NumberFormatFlags;
#define NFF_LEADING_ZERO				0x01

NumberType

typedef ByteEnum NumberType;
#define NT_VALUE				0
#define NT_BOOLEAN				1
#define NT_DATE_TIME				2

ObjChunkFlags

typedef ByteFlags ObjChunkFlags;
#define OCF_VARDATA_RELOC					0x10
#define OCF_DIRTY 					0x08
#define OCF_IGNORE_DIRTY 					0x04
#define OCF_IN_RESOURCE 					0x02
#define OCF_IS_OBJECT 					0x01

This record is stored at the beginning of each chunk and gives specific information about the chunk. The flags are internal.

ObjLMemBlockHeader

typedef struct {
LMemBlockHeader			OLMBH_header;					/* standard LMem block header */
word			OLMBH_inUseCount;
word			OLMBH_interactibleCount;
optr			OLMBH_output;
word			OLMBH_resourceSize;
} ObjLMemBlockHeader;

This is the standard Object Block header that begins every object block; you can set additional header fields with the @header Goc keyword. The fields of this structure are

OLMBH_header
The standard LMem block header. See the LMemBlockHeader structure type.
OLMBH_inUseCount
The "in use" count for the block. If not zero, then the block may not safely be freed.
OLMBH_interactibleCount
The "interactable" count for the block. If not zero, then one or more objects in the block are either visible to the user or about to be activated by the user (e.g. via keyboard shortcut). A block with a non-zero interactible count may not be swapped.
OLMBH_output
The optr of the object that will be notified about changes in resource status, such as in-use count changing to or from zero. Messages may also be sent to this output object via the TravelOption TO_OBJ_BLOCK_OUTPUT.
OLMBH_resourceSize
The size of the object block (resource).

ObjRelocation

typedef struct {
ObjRelocationType				OR_type;
word				OR_offset;
} ObjRelocation;

ObjRelocationSource

typedef ByteEnum ObjRelocationSource;
#define ORS_NULL								0
#define ORS_OWNING_GEODE 								1
#define ORS_KERNEL 								2
#define ORS_LIBRARY 								3
#define ORS_CURRENT_BLOCK 								4
#define ORS_VM_HANDLE 								5
#define ORS_OWNING_GEODE_ENTRY_POINT								6
#define ORS_NON_STATE_VM 								7
#define ORS_UNKNOWN_BLOCK 								8
#define ORS_EXTERNAL 								9
#define RID_SOURCE_OFFSET 								12

ObjRelocationType

typedef ByteEnum ObjRelocationType;
#define RELOC_END_OF_LIST						0
#define RELOC_RELOC_HANDLE						1
#define RELOC_RELOC_SEGMENT						2
#define RELOC_RELOC_ENTRY_POINT						3

OperatorStackElement

typedef struct {
EvalStackOperatorType OSE_type;
EvalStackOperatorType OSE_data;
} OperatorStackElement;

| Up: GEOS SDK TechDocs | Up | Prev: MixMode ... | Next: OperatorType ...


GEOS C Structs: OperatorType ...

| Up: GEOS SDK TechDocs | Up | Prev: NULL ... | Next: ParserFlags ...

OperatorType

typedef ByteEnum OperatorType;
#define OP_RANGE_SEPARATOR									0
#define OP_NEGATION 									1
#define OP_PERCENT 									2
#define OP_EXPONENTIATION 									3
#define OP_MULTIPLICATION 									4
#define OP_DIVISION 									5
#define OP_MODULO 									6
#define OP_ADDITION 									7
#define OP_SUBTRACTION 									8
#define OP_EQUAL 									9
#define OP_NOT_EQUAL 									10
#define OP_LESS_THAN 									11
#define OP_GREATER_THAN 									12
#define OP_LESS_THAN_OR_EQUAL 									13
#define OP_GREATER_THAN_OR_EQUAL 									14
#define OP_STRING_CONCAT 									15
#define OP_RANGE_INTERSECTION 									16
#define OP_NOT_EQUAL_GRAPHIC									17
#define OP_DIVISION_GRAPHIC 									18
#define OP_LESS_THAN_OR_EQUAL_GRAPHIC 									19
#define OP_GREATER_THAN_OR_EQUAL_GRAPHIC 									20
#define OP_PERCENT_MODULO 									21
#define OP_SUBTRACTION_NEGATION 									22

optr

typedef dword optr;

PageEndCommand

typedef ByteEnum PageEndCommand;
        #define PEC_FORM_FEED 0
        #define PEC_NO_FORM_FEED 1

This enumerated type is used to specify whether there should be a form feed at the end of a page. A form feed is almost always desired; however, a program such as a banner-printer might suppress the form feed.

PageLayout

typedef union {
PageLayoutPaper				PL_paper;
PageLayoutEnvelope				PL_envelope;
PageLayoutLabel				PL_label;
} PageLayout;

PageLayoutEnvelope

typedef WordFlags PageLayoutEnvelope;
#define PLE_PATH				0x0040
#define PLE_ORIENTATION				0x0010
#define PLE_TYPE				0x0004

PageLayoutLabel

typedef WordFlags PageLayoutLabel;
#define PLL_ROWS				0x7e00			/* labels down */
#define PLL_COLUMNS				0x01f8			/* labels across */
#define PLL_TYPE				0x0004			/* PT_LABEL */

PageLayoutPaper

typedef WordFlags PageLayoutPaper;
#define PLP_ORIENTATION				0x0008
#define PLP_TYPE				0x0004

PageSize

typedef struct {
word		unused;
word		PS_width;
word		PS_height;
PageLayout		PS_layout;
} PageSize;

PageSizeCtrlAttrs

typedef WordFlags PageSizeCtrlAttrs;
#define PZCA_ACT_LIKE_GADGET					0x8000
#define PZCA_PAPER_SIZE					0x4000
#define PZCA_INITIALIZE					0x2000

PageSizeCtrlFeatures

typedef ByteFlags PageSizeControlFeatures;
#define PSIZECF_MARGINS				0x04
#define PSIZECF_ALL 				0x02
#define PSIZECF_PAGE_TYPE				0x01

PageSizeReport

typedef struct {
dword 			PSR_width;
dword 			PSR_height;
PageLayout 			PSR_layout;
PCMarginParams			PSR_margins;
} PageSizeReport:

PageType

typedef enum {
PT_PAPER,
PT_ENVELOPE,
PT_LABEL
} PageType;

PaperOrientation

typedef ByteEnum PaperOrientation;
#define PO_PORTRAIT				0x00
#define PO_LANDSCAPE				0x01

ParallelUnit

typedef	enum
{
    PARALLEL_LPT1	= 0,
    PARALLEL_LPT2	= 2,
    PARALLEL_LPT3	= 4,
    PARALLEL_LPT4	= 6,
} ParallelUnit;

| Up: GEOS SDK TechDocs | Up | Prev: NULL ... | Next: ParserFlags ...


GEOS C Structs: ParserFlags ...

| Up: GEOS SDK TechDocs | Up | Prev: OperatorType ... | Next: ParserTokenData ...

ParserFlags

typedef ByteFlags ParserFlags;
#define PF_HAS_LOOKAHEAD						0x80
#define PF_CONTAINS_DISPLAY_FUNC 						0x40
#define PF_OPERATORS 						0x20
#define PF_NUMBERS 						0x10
#define PF_CELLS 						0x08
#define PF_FUNCTIONS 						0x04
#define PF_NAMES 						0x02
#define PF_NEW_NAMES 						0x01

ParserParameters

typedef struct {
CommonParameters			PP_common;
word			PP_parserBufferSize;
ParserFlags 			PP_flags;
dword			PP_textPtr;
ScannerToken			PP_currentToken;
ScannerToken			PP_lookAheadToken;
byte			PP_error;				/* ParserScannerEvaluatorError */
word			PP_tokenStart;
word			PP_tokenEnd;
} ParserParameters;

ParserScannerEvaluatorError

typedef ByteEnum ParserScannerEvaluatorError;
/*
 * Scanner errors
 */
#define PSEE_BAD_NUMBER									0
#define PSEE_BAD_CELL_REFERENCE 									1
#define PSEE_NO_CLOSE_QUOTE 									2
#define PSEE_COLUMN_TOO_LARGE 									3
#define PSEE_ROW_TOO_LARGE 									4
#define PSEE_ILLEGAL_TOKEN 									5
/*
 * Parser errors
 */
#define PSEE_GENERAL 									6
#define PSEE_TOO_MANY_TOKENS 									7
#define PSEE_EXPECTED_OPEN_PAREN 									8
#define PSEE_EXPECTED_CLOSE_PAREN 									9
#define PSEE_BAD_EXPRESSION 									10
#define PSEE_EXPECTED_END_OF_EXPRESSION 									11
#define PSEE_MISSING_CLOSE_PAREN 									12
#define PSEE_UNKNOWN_IDENTIFIER 									13
#define PSEE_NOT_ENOUGH_NAME_SPACE 									14
/*
 * Serious evaluator errors
 */
#define PSEE_OUT_OF_STACK_SPACE 									15
#define PSEE_NESTING_TOO_DEEP 									16
/*
 * Evaluator errors that are returned as the result of formulas.
 * These are returned on the argument stack.
 */
#define PSEE_ROW_OUT_OF_RANGE 									17
#define PSEE_COLUMN_OUT_OF_RANGE 									18
#define PSEE_FUNCTION_NO_LONGER_EXISTS 									19
#define PSEE_BAD_ARG_COUNT 									20
#define PSEE_WRONG_TYPE 									21
#define PSEE_DIVIDE_BY_ZERO 									22
#define PSEE_UNDEFINED_NAME 									23
#define PSEE_CIRCULAR_REF 									24
#define PSEE_CIRCULAR_DEP 									25
#define PSEE_CIRC_NAME_REF 									26
#define PSEE_NUMBER_OUT_OF_RANGE 									27
#define PSEE_GEN_ERR 									28
#define PSEE_NA 									29
/*
 * Dependency errors
 */
#define PSEE_TOO_MANY_DEPENDENCIES 									30
#define PSEE_SSHEET_BASE 								0xc0
#define PSEE_FLOAT_BASE 								250
#define PSEE_APP_BASE 								230
#define PSEE_FLOAT_POS_INFINITY 								PSEE_FLOAT_BASE
#define PSEE_FLOAT_NEG_INFINITY 								(PSEE_FLOAT_BASE + 1)
#define PSEE_FLOAT_GEN_ERR 								(PSEE_FLOAT_BASE + 2)

ParserToken

typedef struct {
ParserTokenType			PT_type;
ParserTokenData			PT_data;
} ParserToken;

ParserTokenCellData

typedef struct {
CellReference			PTCD_cellRef;
} ParserTokenCellData;

| Up: GEOS SDK TechDocs | Up | Prev: OperatorType ... | Next: ParserTokenData ...


GEOS C Structs: ParserTokenData ...

| Up: GEOS SDK TechDocs | Up | Prev: ParserFlags ... | Next: Point ...

ParserTokenData

typedef union {
ParserTokenNumberData 				PTD_number;
ParserTokenStringData 				PTD_string;
ParserTokenNameData 				PTD_name;
ParserTokenCellData 				PTD_cell;
ParserTokenFunctionData 				PTD_function;
ParserTokenOperatorData				PTD_operator;
} ParserTokenData;

ParserTokenFunctionData

typedef struct {
word		PTFD_functionID;
} ParserTokenFunctionData;

ParserTokenNameData

typedef struct {
word		PTND_name;
} ParserTokenNameData;

ParserTokenNumberData

typedef struct {
FloatNum		PTND_value;
} ParserTokenNumberData;

ParserTokenOperatorData

typedef struct {
OperatorType			PTOD_operatorID;
} ParserTokenOperatorData;

ParserTokenStringData

typedef struct {
word		PTSD_length;
} ParserTokenStringData;

ParserTokenType

typedef ByteEnum ParserTokenType;
#define PARSER_TOKEN_NUMBER 								0
#define PARSER_TOKEN_STRING 								1
#define PARSER_TOKEN_CELL 								2
#define PARSER_TOKEN_END_OF_EXPRESSION 								3
#define PARSER_TOKEN_OPEN_PAREN								4
#define PARSER_TOKEN_CLOSE_PAREN 								5
#define PARSER_TOKEN_NAME 								6
#define PARSER_TOKEN_FUNCTION 								7
#define PARSER_TOKEN_CLOSE_FUNCTION								8
#define PARSER_TOKEN_ARG_END 								9
#define PARSER_TOKEN_OPERATOR 								10

PathCombineType

typedef enum /* word */ {
PCT_NULL,				/* wipe out old path */
PCT_REPLACE,				/* replace old path with upcoming path */
PCT_UNION,				/* union old path with new */
PCT_INTERSECTION 				/* intersect old path with new */
} PathCombineType;

PathName

typedef char PathName[PATH_BUFFER_SIZE];

PatternType

typedef ByteEnum PatternType;
#define PT_SOLID				0
#define PT_SYSTEM_HATCH 				1
#define PT_SYSTEM_BITMAP 				2
#define PT_USER_HATCH 				3
#define PT_USER_BITMAP 				4
#define PT_CUSTOM_HATCH 				5
#define PT_CUSTOM_BITMAP 				6

PCDocSizeParams

typedef struct {
dword 	PCDSP_width;
dword 	PCDSP_height;
} PCDocSizeParams;

Use this structure to communicate document sizes to a Print Control.

PCMarginParams

typedef struct {
word 	PCMP_left; 			/* left margin */
word 	PCMP_top; 			/* top margin */
word 	PCMP_right; 			/* right margin */
word 	PCMP_bottom; 			/* bottom margin */
} PCMarginParams

This structure holds information about a document's or printer's margins.

PCProgressType

typedef enum {
PCPT_PAGE,
PCPT_PERCENT,
PCPT_TEXT
} PCProgressType;

PDAType

typedef ByteEnum PDAType;
#define PDAT_PC_EMULATOR        0x0
#define PDAT_N9000              0x1
#define PDAT_N9000i             0x2

These values correspond to the different Nokia 9000 Communicator hardware platforms. They are only available on builds 4 or newer of the device (see Versions for more information).

Include: respondr.goh

See Also: RespGetPDAHardwareInfo()

| Up: GEOS SDK TechDocs | Up | Prev: ParserFlags ... | Next: Point ...


GEOS C Structs: Point ...

| Up: GEOS SDK TechDocs | Up | Prev: ParserTokenData ... | Next: ProtocolNumber ...

Point

typedef struct {
sword P_x;
sword P_y;
} Point;

PointDWord

typedef struct {
sdword PD_x;
sdword PD_y;
} PointDWord;

PointDWFixed

typedef struct {
DWFixed PDF_x;
DWFixed PDF_y;
} PointDWFixed;

PointerDef32

typedef struct {
sbyte	PD_hotX;
sbyte	PD_hotY;
byte	PD_mask[CURSOR_IMAGE_SIZE_32];
byte	PD_image[CURSOR_IMAGE_SIZE_32];
} PointerDef32;
 STANDARD_CURSOR_IMAGE_SIZE = 32

This structure defines a mouse pointer.

PointWWFixed

typedef struct {
WWFixed 	PF_x;
WWFixed 	PF_y;
} PointWWFixed;

These structures are used to specify graphics point coordinates. Which point structure to use depends on size of the coordinate space and accuracy required.

PrintControlAttrs

typedef WordFlags PrintControlAttrs;
#define PCA_MARK_APP_BUSY					0x2000			/* mark busy while printing */
#define PCA_VERIFY_PRINT					0x1000			/* verify before printing */
#define PCA_SHOW_PROGRESS					0x0800			/* show print progress dialog box */
#define PCA_PROGRESS_PERCENT					0x0400			/* show progress by percentage */
#define PCA_PROGRESS_PAGE					0x0200			/* show progress by page */
#define PCA_FORCE_ROTATION					0x0100			/* Force rotation of output */
#define PCA_COPY_CONTROLS					0x0080			/* Copy controls are available */
#define PCA_PAGE_CONTROLS					0x0040			/* Page range controls available */
#define PCA_QUALITY_CONTROLS					0x0020			/* Quality controls available */
#define PCA_USES_DIALOG_BOX					0x0010			/* Dialog box should appear */
#define PCA_GRAPHICS_MODE					0x0008			/* Supports graphics mode output */
#define PCA_TEXT_MODE					0x0004			/* Supports text mode output */
#define PCA_DEFAULT_QUALITY					0x0002			/* default print quality */

PrintControlFeatures

typedef ByteFlags PrintControlFeatures;
#define PRINTCF_PRINT_TRIGGER					0x02			/* wants a print trigger */
#define PRINTCF_FAX_TRIGGER					0x01			/* wants a fax trigger */

PrintControlStatus

typedef enum {
PCS_PRINT_BOX_VISIBLE,
PCS_PRINT_BOX_NOT_VISIBLE
} PrintControlStatus;

PrintControlToolboxFeatures

typedef ByteFlags PrintControlToolboxFeatures;
#define PRINTCTF_PRINT_TRIGGER						0x02		/* wants a print tool trigger */
#define PRINTCTF_FAX_TRIGGER						0x01		/* wants a fax tool trigger */

PrinterDriverType

typedef enum PrinterDriverType;
PDT_PRINTER,
PDT_PLOTTER,
PDT_FACSIMILE,
PDT_CAMERA,
PDT_OTHER,
} PrinterDriverType;

This enumerated type indeicates the type of printer driver that we are dealing with.

PrinterOutputModes

typedef ByteFlags PrinterOutputModes;
#define POM_GRAPHICS_LOW					0x10
#define POM_GRAPHICS_MEDIUM					0x08
#define POM_GRAPHICS_HIGH					0x04
#define POM_TEXT_DRAFT					0x02
#define POM_TEXT_NLQ					0x01
#define PRINT_GRAPHICS =				(POM_GRAPHICS_LOW | POM_GRAPHICS_MEDIUM |
                 POM_GRAPHICS_HIGH )
#define PRINT_TEXT =				(POM_TEXT_DRAFT | POM_TEXT_NLQ)

PrintQualityEnum

typedef enum {
PQT_HIGH,
PQT_MEDIUM,
PQT_LOW
} PrintQualityEnum;

| Up: GEOS SDK TechDocs | Up | Prev: ParserTokenData ... | Next: ProtocolNumber ...


GEOS C Structs: ProtocolNumber ...

| Up: GEOS SDK TechDocs | Up | Prev: Point ... | Next: RangeSortFlags ...

ProtocolNumber

typedef struct {
    word	PN_major;
    word	PN_minor;
} ProtocolNumber;

Defines the protocol level of a file, geode, or document. PN_major represents significant compatibility comparisons, and PN_minor represents less significant differences. If the major protocol is different between to items, they are incompatible. If the minor protocol is different, they may or may not be incompatible.

QueueHandle

typedef Handle QueueHandle;

QuickSortParameters

typedef struct _QuickSortParameters {
    word _pascal (*QSP_compareCallback) (void *			el1,
                     void *			el2,
                      word 			valueForCallback));
    void _pascal (*QSP_lockCallback) (void *el, word valueForCallback));
    void _pascal (*QSP_unlockCallback) (void *el, word valueForCallback));
    word 		QSP_insertLimit;
    word 		QSP_medianLimit;

/* These are set internally by the quicksort algorithm and should not
 * be set by the caller: */
    word 		QSP_nLesser;
    word 		QSP_nGreater;
} QuickSortParameters;

This structure is passed to ArrayQuickSort(). The fields have the following meanings:

*QSP_compareCallback
This routine is called to compare elements. It should be declared _pascal. It should return a positive value if *el1 ought to come before *e2 in the sorted list; a negative value if *el1 ought to come after *e2 in the sorted list; and zero if it doesn't matter which comes first.
*QSP_lockCallback
This routine is called before ArrayQuickSort() examines or changes any element. It should be declared _pascal. You can pass a null function pointer, indicating that no locking callback routine should be called.
*QSP_unlockCallback
This routine is called after ArrayQuickSort() examines or changes any element. It should be declared _pascal. You can pass a null function pointer, indicating that no unlocking callback routine should be called.
QSP_insertLimit
If there are fewer than QSP_insertLimit elements in a sublist, ArrayQuickSort() will use an insertion sort for that sublist, rather than a QuickSort.
QSP_medianLimit
If there are fewer than QSP_medianLimit elements in a sublist, ArrayQuickSort() will use the first element as a partition, instead of searching for the median element.

RangeEnumCallbackParams

See RangeEnum() .

RangeEnumFlags

typedef ByteFlags RangeEnumFlags;
#define REF_ALL_CELLS							0x80
#define REF_NO_LOCK							0x40
#define REF_COLUMN_FLAGS							0x20
#define REP_MATCH_COLUMN_FLAGS							0x10
#define REF_CELL_ALLOCATED							0x08
#define REF_CELL_FREED							0x04
#define REF_OTHER_ALLOC_OR_FREE							0x02
#define REF_COLUMN_FLAGS_MODIFIED							0x01

These flags are used by RangeEnum() .

RangeEnumParams

typedef struct {
PCB(RANGE_ENUM_CALLBACK_RETURN_TYPE, REP_callback,
                                        (RangeEnumCallbackParams));
Rectangle				REP_bounds;
byte				REP_columnFlags;
word				*REP_columnFlagsArray;
CellFunctionParameters				*REP_cfp;
byte				REP_matchFlags;
word				*REP_locals;
} RangeEnumParams;

This structure is used by two routines, RangeEnum() and CellGetExtent() . When it is used by RangeEnum() , the structure specifies all the details about how RangeEnum() will function. CellGetExtent() is passed a blank RangeEnumParams structure; it fills in the REP_bounds field.

The callback routine, if any, should be declared _pascal.

Include: cell.h

RangeInsertParams

See RangeInsert() .:

RangeSortError

typedef enum /* word */ {
RSE_NO_ERROR,
RSE_UNABLE_TO_ALLOC,
} RangeSortError;

RangeSortCellExistFlags

typedef ByteFlags RangeSortCellExistsFlags;
#define RSCEF_SECOND_CELL_EXISTS 0x02
#define RSCEF_FIRST_CELL_EXISTS 0x01

| Up: GEOS SDK TechDocs | Up | Prev: Point ... | Next: RangeSortFlags ...


GEOS C Structs: RangeSortFlags ...

| Up: GEOS SDK TechDocs | Up | Prev: ProtocolNumber ... | Next: ReleaseNumber ...

RangeSortFlags

typedef ByteFlags RangeSortFlags;
#define RSF_SORT_ROWS						0x80
#define RSF_SORT_ASCENDING						0x40
#define RSF_IGNORE_CASE						0x20

RangeSortParams

typedef struct {
Rectangle		RSP_range;
Point		RSP_active;
dword		RSP_callback;
byte		RSP_flags; /* RangeSortFlags */
dword		RSP_cfp;
word		RSP_sourceChunk;
word		RSP_destChunk;
word		RSP_base;
dword		RSP_lockedEntry;
byte		RSP_cachedFlags;
} RangeSortParams;

RecordHeader

typedef struct {
RecordID		RH_id;
word		RH_size;
byte		RH_fieldCount;
} RecordHeader;

Structure containing metadata about a record; first FieldHeader follows.

RecordID

typedef dword RecordID;

Uniquely identifies a record in a datastore; this value stays constant and does not change when other records are added or deleted.

RecordNum

typedef dword RecordNum;

Specifies relative place in the datastore; this value may change when other records are added or deleted.

Rectangle

typedef struct {
sword	R_left;
sword	R_top;
sword	R_right;
sword	R_bottom;
} Rectangle;

This structure represents a graphics rectangle.

RectDWord

typedef struct {
sdword	RD_left;
sdword	RD_top;
sdword	RD_right;
sdword	RD_bottom;
} RectDWord;

This structure represents a graphics rectangle.

RectRegion

typedef struct {
word 	RR_y1M1;
word 	RR_eo1;	/* EOREGREC */
word 	RR_y2;
word 	RR_x1;
word 	RR_x2;
word 	RR_eo2;		/* EOREGREC */
word 	RR_eo3;		/* EOREGREC */
} RectRegion;

RefElementHeader

typedef struct {
 WordAndAHalf REH_refCount;
} RefElementHeader;

Region

typedef word Region;
#define EOREGREC				0x8000
#define EOREG_HIGH				 0x80

This structure represents a region of a graphics coordinate space.

Regions are described in terms of a rectangular array (thus the similarity to bitmaps). Instead of specifying an on/off value for each pixel, however, regions assume that the region will be fairly undetailed and that the data structure can thus be treated in the manner of a sparse array. Only the cells in which the color value of a row changes are recorded. The tricky part here is keeping in mind that when figuring out whether or not a row is the same as a previous row, the system works its way up from the bottom, so that you should compare each row with the row beneath it to determine whether it needs an entry.

The easiest region to describe is the null region, which is a special case described by a single word with the value EOREGREC (a constant whose name stands for E nd Of REG ion REC ord value). Describing a non-null region requires several numbers.

The first four numbers of the region description give the bounds of the region. Next come one or more series of numbers. Each series describes a row, specifying which pixels of that row are part of the region. The only rows which need to be described are those which are different from the row below. The first number of each row description is the row number, its y coordinate. The last number of each series is a special token, EOREGREC, which lets the kernel know that the next number of the description will be the start of another row. Between the row number and EOREGREC are the column numbers where the pixels toggle on and off. The first number after the row number corresponds to the first column in which the pixel is on; the next number is the first subsequent column in which the pixel is off; and so on.

RegionFillRule

typedef ByteEnum RegionFillRule;
#define ODD_EVEN				0
#define WINDING				1

This enumerated type determines how a path or region should be filled. Winding fill is more versatile, but requires that the path or polygon's edges run in the correct direction.

| Up: GEOS SDK TechDocs | Up | Prev: ProtocolNumber ... | Next: ReleaseNumber ...


GEOS C Structs: ReleaseNumber ...

| Up: GEOS SDK TechDocs | Up | Prev: RangeSortFlags ... | Next: ScannerTokenCellData ...

ReleaseNumber

typedef struct {
word	RN_major;
word	RN_minor;
word	RN_change;
word	RN_engineering;
} ReleaseNumber;

Used to record what version a file, document, or geode is. This represents the release level; the most significant numbers are RN_major and RN_minor . The other fields are typically used only internally to a manufacturer.

RGBColorAsDWord

typedef dword RGBColorAsDWord;
RGB_RED(val) ( val & 0xff)
RGB_GREEN(val) ( (val >> 8) & 0xff )
RGB_BLUE(val) ( (val >> 16) & 0xff )
RGB_INDEX(val) ( (val >> 24) & 0xff )

See the ColorQuad data structure to find out the meanings of the fields.

RGBDelta

typedef struct {
 byte RGBD_red;
 byte RGBD_green;
 byte RGBD_blue;
} RGBDelta;

RGBTransfer

typedef struct {
byte	RGBT_red[256];
byte	RGBT_green[256];
byte	RGBT_blue[256];
} RGBTransfer;

RGBValue

typedef struct {
byte	RGB_red;
byte	RGB_green;
byte	RGB_blue;
} RGBValue;

SACAddress

typedef struct {
        word                SACA_opaqueSize;
        /* label byte          SACA_opaque;    */
        /* user-readable form of address follows opaque address--use
         *   SACA_opaqueSize to compute the offset to this data. */
} SACAddress;

The SocketAddressControl uses SACAddress structures to provide information about addresses.

Most programs won't need to look at the innards of this structure: they will just pass the opaque address information to SocketResolve() to make sure that the address is valid and reduce it to its primitive form.

However, if you need to construct a "raw" address to pass to SocketResolve() , you'll need to know the structure of the opaque address information. It's an ExtendedSACAddress buffer followed by a null-terminated user-readable connection address string..

The ExtendedSACAddress buffer consists of:

  • A word giving the size of the optional link information (zero if there is no link information).
  • Link information, consisting of a LinkType value (one of LT_ADDR, LT_ID, or LT_DEFAULT), and parameters to identify the link. If the LinkType is LT_DEFAULT, then there are no further parameters. If the LinkType is LT_ID, then there is a word-length LinkID which acts as a permanent identifier for the link. If the LinkType is LT_ADDR, the parameter is a string which will be appended to the user-readable connection address string, surrounded by parentheses.

Include: sac.goh

SampleFormat

typedef struct {
         DACSampleFormat SMID_format:15;
         DACReferenceByte SMID_reference:1;
} SampleFormat;

SampleFormatDescription

typedef struct {
 word 	SFD_manufact;
 word 	SFD_format;
 word 	SFD_rate;
 word 	SFD_playFlags;
} SampleFormatDescription;

This structure acts as a header for a sampled sound, giving format information needed to properly interpret the sound data.

SansFace

typedef byte SansFace;
#define SF_A_CLOSED 0x0080
#define SF_A_OPEN 0x0000

sbyte

typedef char sbyte;

ScannerToken

typedef struct {
ScannerTokenType				ST_type;
ScannerTokenData				ST_data;
} ScannerToken;

| Up: GEOS SDK TechDocs | Up | Prev: RangeSortFlags ... | Next: ScannerTokenCellData ...


GEOS C Structs: ScannerTokenCellData ...

| Up: GEOS SDK TechDocs | Up | Prev: ReleaseNumber ... | Next: SerialBaud ...

ScannerTokenCellData

typedef struct {
CellReference			STCD_cellRef;
} ScannerTokenCellData;

ScannerTokenData

typedef union {
ScannerTokenNumberData					STD_number;
ScannerTokenStringData					STD_string;
ScannerTokenCellData					STD_cell;
ScannerTokenIdentifierData					STD_identifier;
ScannerTokenOperatorData					STD_operator;
} ScannerTokenData;

ScannerTokenIdentifierData

typedef struct {
word		STID_start;
} ScannerTokenIdentifierData;

ScannerTokenNumberData

typedef struct {
FloatNum		STND_value;
} ScannerTokenNumberData;

ScannerTokenOperatorData

typedef struct {
OperatorType			STOD_operatorID;
} ScannerTokenOperatorData;

ScannerTokenStringData

typedef struct {
word	STSD_start;
word	STSD_length;
} ScannerTokenStringData;

ScannerTokenType

typedef ByteEnum ScannerTokenType;
#define SCANNER_TOKEN_NUMBER 									0
#define SCANNER_TOKEN_STRING 									1
#define SCANNER_TOKEN_CELL 									2
#define SCANNER_TOKEN_END_OF_EXPRESSION 									3
#define SCANNER_TOKEN_OPEN_PAREN 									4
#define SCANNER_TOKEN_CLOSE_PAREN 									5
#define SCANNER_TOKEN_IDENTIFIER 									6
#define SCANNER_TOKEN_OPERATOR 									7
#define SCANNER_TOKEN_LIST_SEPARATOR									8

ScriptAttrAsWord

typedef word ScriptAttrAsWord;
/* 	High byte is a vertical offset, as a fraction of the font size.
    Low byte is a fractional scale to use.
    Thus, setting a subscript attr to 0x8020 would result in subscript
    characters being printed half a line down and at 1/4 normal size. */

This structure specifies the offset and scale factor with which sub- and superscript characters should draw.

ScriptFace

typedef byte ScriptFace;
#define SF_CURSIVE 0x0080
#define SF_CALLIGRAPHIC 0x0000

sdword

typedef long sdword;

SearchFlags

typedef ByteFlags SearchFlags;
#define	SF_BACKWARDS	 		0x80
#define	SF_START_AT_END	 		0x40
#define	SF_IGNORE_CASE	 		0x20
#define	SF_NO_WILDCARDS	 		0x10
#define	SF_PARTIAL_WORD	 		0x08

Flags to define search parameters; see SearchParams and DataStoreStringSearch() .

SearchParams

typedef struct {
SearchType		SP_searchType;
dword		SP_maxRecords;
FieldID		SP_startField;
FieldCategory	SP_category;
TCHAR		*SP_searchString;
SearchFlags		SP_flags;
} SearchParams;

Structure for specifying search conditions when calling DataStoreStringSearch() .

SearchType

typedef ByteEnum SearchType;
#define	ST_ALL	 		1	/* search all string fields */
#define	ST_FIELD			2	/* search by FieldID */
#define	ST_CATEGORY			3	/* search by FieldCategory */

Values to specify in search parameters; see SearchParams and DataStoreStringSearch() .

segment

typedef word segment;

SemaphoreError

typedef enum {
SE_NO_ERROR,					/* No error occurred */
SE_TIMEOUT,					/* The semaphore timed out before
                     * it could be grabbed by the thread */
SE_PREVIOUS_OWNER_DIED					/* The current holder of the semaphore
                     * exited abnormally */
} SemaphoreError;

Determines the error encountered by semaphore and threadlock routines such as ThreadPSem() and ThreadPTimedSem() .

SE_NO_ERROR
No error occurred and the semaphore was grabbed properly.
SE_TIMEOUT
The time elapsed and the semaphore was not grabbed. If this value is returned, the thread should not proceed with whatever protected operation was to happen. Instead, it should either attempt to grab the semaphore again or should proceed with other tasks.
SE_PREVIOUS_OWNER_DIED
The previous owner of the semaphore exited abnormally. If the thread currently holding the semaphore exited without releasing the semaphore, for example, this would be returned.
| Up: GEOS SDK TechDocs | Up | Prev: ReleaseNumber ... | Next: SerialBaud ...


GEOS C Structs: SerialBaud ...

| Up: GEOS SDK TechDocs | Up | Prev: ScannerTokenCellData ... | Next: Socket ...

SerialBaud

typedef	enum
{
    SERIAL_BAUD_115200			= 1,
    SERIAL_BAUD_57600			= 2,
    SERIAL_BAUD_38400			= 3,
    SERIAL_BAUD_19200			= 6,
    SERIAL_BAUD_14400			= 8,
    SERIAL_BAUD_9600			= 12,
    SERIAL_BAUD_7200			= 16,
    SERIAL_BAUD_4800			= 24,
    SERIAL_BAUD_3600			= 32,
    SERIAL_BAUD_2400			= 48,
    SERIAL_BAUD_2000			= 58,
    SERIAL_BAUD_1800			= 64,
    SERIAL_BAUD_1200			= 96,
    SERIAL_BAUD_600			= 192,
    SERIAL_BAUD_300			= 384
} SerialBaud;

SerialFormat

typedef	ByteFlags SerialFormat;
#define	SERIAL_FORMAT_DLAB_OFFSET						(7)
#define	SERIAL_FORMAT_DLAB						(0x01 << SERIAL_FORMAT_DLAB_OFFSET)

#define	SERIAL_FORMAT_BREAK_OFFSET						(6)
#define	SERIAL_FORMAT_BREAK						(0x01 << SERIAL_FORMAT_BREAK_OFFSET)

#define	SERIAL_FORMAT_PARITY_OFFSET						(3)
#define	SERIAL_FORMAT_PARITY						(0x07 << SERIAL_FORMAT_PARITY_OFFSET)

#define	SERIAL_FORMAT_EXTRA_STOP_OFFSET (2)
#define	SERIAL_FORMAT_EXTRA_STOP						(0x01 <<	\
                                SERIAL_FORMAT_EXTRA_STOP_OFFSET)

#define	SERIAL_FORMAT_LENGTH_OFFSET						(0)
#define	SERIAL_FORMAT_LENGTH						(0x03 << SERIAL_FORMAT_LENGTH_OFFSET)

SerialMode

typedef	enum {
    SM_RAW,
    SM_RARE,
    SM_COOKED
} SerialMode;

#define SERIAL_MODEM_RTS (0x01 << SERIAL_MODEM_RTS_OFFSET)

SerialModem

typedef	ByteFlags SerialModem;
#define    SMC_RTS      0x02    /* Request-To-Send (1 asserts it) */
#define    SMC_DTR      0x01    /* Data-Terminal Ready (1 asserts it) */

#define SERIAL_MODEM_RTS_OFFSET						(1)

#define SERIAL_MODEM_DTR_OFFSET						(0)
#define SERIAL_MODEM_DTR						(0x01 << SERIAL_MODEM_DTR_OFFSET)

SerialPortNum

typedef	enum
{
    SERIAL_COM1			= 0,
    SERIAL_COM2			= 2,
    SERIAL_COM3			= 4,
    SERIAL_COM4			= 6,
    SERIAL_COM5			= 8,
    SERIAL_COM6			= 10,
    SERIAL_COM7			= 12,
    SERIAL_COM8			= 14
} SerialPortNum;

SemaphoreHandle

typedef Handle SemaphoreHandle;

SerifFace

typedef byte SerifFace;
#define SF_SLAB 0x00c0
#define SF_MODERN 0x0080
#define SF_TRANS 0x0040
#define SF_OLD 0x0000

SetPalType

typedef ByteEnum SetPalType;
#define SPT_DEFAULT				0
#define SPT_CUSTOM				1

ShiftState

typedef ByteFlags ShiftState;
#define SS_LALT				0x80
#define SS_RALT 				0x40
#define SS_LCTRL 				0x20
#define SS_RCTRL 				0x10
#define SS_LSHIFT 				0x08
#define SS_RSHIFT 				0x04
#define SS_FIRE_BUTTON_1 				0x02
#define SS_FIRE_BUTTON_2				0x01

Modifiers which will be incorporated into input information. Corresponds to alt keys, control keys, shift keys, or special system modifiers. Note that these bits will only be set if not already accounted for; that is, if you are passed the character "E", the shift modifiers of this structure will not be marked.

| Up: GEOS SDK TechDocs | Up | Prev: ScannerTokenCellData ... | Next: Socket ...


GEOS C Structs: Socket ...

| Up: GEOS SDK TechDocs | Up | Prev: SerialBaud ... | Next: SocketError ...

Socket

typedef word Socket;

A Socket represents one side of a communication managed by the Socket library.

Include: socket.goh

SocketAddress

typedef struct {
        SocketPort  SA_port;
        word        SA_domainSize; 					/*  size in bytes of SA_domain buffer */
        char        *SA_domain; 					/*  null terminated domain name */
        word        SA_addressSize; 					/*  size in bytes of address data */
} SocketAddress;

Addresses are made up of a port number, a domain, and address data:

SA_port
Port numbers identify a particular line of communication within a machine. Port numbers may be 32-bit or 16-bit, depending on the domain. They are specified via the SocketPort structure.
SA_domain , SA_domainSize
The domain identifies the protocol of the network by which the addressed machine may be reached. The domain is specified by a string. SA_domain is a pointer to the null-terminated domain name string and SA_domainSize is the size of the buffer containing the domain name string.
Some routines are passed an empty SocketAddress structure. This means that you should have allocated memory for SA_domain, and initialized SA_domain and SA_domainSize .
SA_addressSize , Address Data
The format of the address data used to identify a machine within a domain depends on that domain. The SA_addressSize field contains the size of the address data. The buffer containing the address data should fall immediately after the SocketAddress structure.
Some routines are passed an empty SocketAddress structure. This means that you should leave room for address data after the structure and initialize the SA_addressSize field.

Include: socket.goh

SocketBindFlags

typedef WordFlags SocketBindFlags;
        #define SBF_REUSE_PORT  (0x8000)

These flags are used to specify options when binding a socket to a port.

There is only one flag: SBF_REUSE_PORT, requesting that the socket be bound to the port even if another socket is already bound to it. You may not use this flag with datagram sockets.

SocketCheckRequest

typedef struct {
        word 		SCR_socket;
        SocketCondition 		SCR_condition;
        byte 		SCR_reserved;
} SocketCheckRequest;
typedef ByteEnum SocketCondition;
        #define SC_READ 			0x0 			/*  data arrived */
        #define SC_WRITE 			0x1 			/*  data can be written */
        #define SC_ACCEPT 			0x2 			/*  connect request arrived */
        #define SC_EXCEPTION 			0x3 			/*  an exception occurred */
        #define SC_URGENT 			0x4 			/*  urgent data arrived */

A SocketCheckRequest is used to query a socket about whether it is ready to carry out some operation. You will need to set two fields:

SCR_socket
The socket to query.
SCR_condition
The operation to query about.

Include: socket.goh

SocketCondition

See SocketCheckRequest .

typedef ByteEnum

SocketDeliveryType

SocketDeliveryType;
        #define SDT_DATAGRAM 			0x0 		/*  unreliable packets */
        #define SDT_SEQ_PACKET 			0x1 		/*  reliable packets */
        #define SDT_STREAM 			0x2 		/*  reliable bytes */

Include: socket.goh

| Up: GEOS SDK TechDocs | Up | Prev: SerialBaud ... | Next: SocketError ...


GEOS C Structs: SocketError ...

| Up: GEOS SDK TechDocs | Up | Prev: Socket ... | Next: SocketLoadType ...

SocketError

typedef enum {
        SE_NORMAL = 0x0,
        /*  operation completed successfully */

        SE_OUT_OF_MEMORY,
        /*
         *  the net library was unable to allocate memory
         *  either there is no more system memory available, or a net-library
         *  specific upper bound on memory usage has been exceeded
         */

        SE_PORT_IN_USE,
        /*
         *  Another socket is already bound to this port, and the
         *  SBF_REUSE_PORT flag was not set.
         */

        SE_PORT_ALREADY_LISTENING,
        /*  Another socket is already listening on this port. */

        SE_SOCKET_IN_USE,
        /*
         *  The socket is already being used in a way inconsistent with the
         *  requested operation
         */

        SE_SOCKET_ALREADY_BOUND,
        /*  The socket is already bound to a port. */

        SE_SOCKET_NOT_BOUND,
        /*  The socket must be bound to a port for the requested operation. */

        SE_SOCKET_NOT_LISTENING,
        /*  The socket must be listening on a port for the requested operation. */

        SE_SOCKET_LISTENING,
        /*  A listening socket cannot be used for outgoing connections */

        SE_CONNECTION_REFUSED,
        /*  Nobody is listening on the remote port. */

        SE_CONNECTION_FAILED,
        /*  The connection is no longer valid, probably due to a
         *  link failure. */

        SE_CONNECTION_CLOSED,
        /*  The connection has been closed from the other end. */

        SE_NON_UNIQUE_CONNECTION,
        /*  Another connection with the same endpoint addresses and ports exists. */

        SE_TIMED_OUT,
        /*
         *  The timeout period expired before the operation completed.
         *  In some cases the protocol may enforce a shorter timeout then
         *  the user-specified value, so this is possible even where
         *  SOCKET_NO_TIMEOUT is used.
         */

        SE_CANT_LOAD_DRIVER,
        /*  A driver required to establish the connection could
         *  not be loaded. */

        SE_UNKNOWN_DOMAIN,
        /*  The requested domain is not known to the libarary. */

        SE_SYSTEM_SHUTDOWN,
        /*  The operation was aborted because GEOS is exiting */

        SE_SOCKET_NOT_CONNECTED,
        /*  The operation requires a connected socket */

        SE_SOCKET_ALREADY_CLOSED,
        /*  The send side of the socket is already closed */

        SE_SOCKET_BUSY,
        /*  Another thread is operating on the socket */

        SE_IMPROPER_CONDITION,
        /*
         *  The requested condition for SocketCheckReady is not valid
         *  for a socket in the current state
         */

        SE_INFO_NOT_AVAILABLE,
        /*  The driver could not provide the requested information */

        SE_DOMAIN_REQUIRES_16BIT_PORTS,
        /*
         *  The requested domain only allows 16 bit port numbers.  The
         *  SP_manuf field must be set to MANUFACTURER_ID_SOCKET_16BIT_PORT
         */

        SE_CONNECTION_ERROR,
        /*
         *  The connection was not made for some reason which does not have
         *  it's own specific SocketError
         */

            SE_MEDIUM_BUSY,
        /*  The medium required for the connection is not available */

            SE_DESTINATION_UNREACHABLE,
        /*  The requested destination is not available */

            SE_LINK_FAILED,
        /*
         *  The low level link used for this connection failed or could not be
         *  established
         */

            SE_INTERNAL_ERROR,
        /*
         *  The socket library received an impossible error code from the driver.
         *  Either the library or the driver has a bug.
         */

            SE_CLOSE_SEND_NOT_SUPPORTED,
        /*
         *  It is not possible to close only the send side of a socket in this
         *  domain.  Use SocketClose instead.
         */

        SE_BIND_CONFLICT,
        /*
         *  The same port cannot have both domain restricted and
         *  domain unrestricted bindings.
         */

            SE_ALL_PORTS_BUSY,
        /*
         *  SocketConnect attempted to implicitly bind a socket,
         *  but failed because all port numbers in the range used by
         *  implicit binds were in use
         */

            SE_SOCKET_NOT_INTERRUPTIBLE,
        /*
         *  An operation which cannot be interrupted is in in progress on the
         *  socket
         */

            SE_INTERRUPT,
        /*  The operation was interrupted with SocketInterrupt */

            SE_PORT_NOT_LISTENING,
        /* No listening socket or LoadOnMsg request exists for this port */

            SE_LISTEN_QUEUE_EMPTY,
        /* No connections are pending for this port */

            SE_SOCKET_CLOSED,
        /* The socket must be open for sending to call either
         *  SocketGetSocketName or SocketGetPeerName */

        SE_BUFFER_TOO_SMALL,
        /*  Returned by SocketResolve to mean that buffer for resolved
         *  address is too small */

        SE_TEMPORARY_ERROR,
        /* Returned by SocketResolve to indicate that address resolution
         * is temporarily unavailable for external reasons( such as
         * network down or server failure )*/

        SE_EXCEPTION,
        SE_URGENT = SE_EXCEPTION,
        /* Returned by SocketRecv to indicate that some urgent data
         *  arrived on a reliable socket or an exception was received
         * for a datagram socket
         */

        SE_NOT_IMPLEMENTED,
        /* Returned by API functions which are not yet implemented */

        SE_CONNECTION_RESET
        /* The connection was reset by the peer, by the local driver (perhaps
         * in response to SocketCloseDomainMedium()), or by the Socket library
         * because the Socket is freed when its owning thread exits.
         */
} SocketError;

Certain socket routines generate error values or generate thread errors under certain conditions. The error value will consist of a word-length value: a SocketError value in the low byte, and perhaps a SpecSocketDrError value in the high byte.

| Up: GEOS SDK TechDocs | Up | Prev: Socket ... | Next: SocketLoadType ...


GEOS C Structs: SocketLoadType ...

| Up: GEOS SDK TechDocs | Up | Prev: SocketError ... | Next: SoundPlayFlags ...

SocketLoadType

typedef enum {
        SLT_USER_LOAD_APPLICATION = 0x0,
        SLT_GEODE_LOAD,
} SocketLoadType;

The Socket library has the ability to load an application when it senses packet coming in to a port. It uses the SocketLoadType to determine how to load the application: via UserLoadApplication() or GeodeLoad() .

Include: socket.goh

SocketOption

typedef enum {
        SO_RECV_BUF = 0x0,                   /* receive buffer size     */
        SO_SEND_BUF = 0x2,                   /* send buffer size        */
        SO_INLINE = 0x4,                     /* inline urgent data      */
        SO_NODELAY = 0x6,                    /* send data immediately   */
        SO_OWNER = 0x8,                      /* which app owns socket   */
} SocketOption;

Various options which may be changed on a per-Socket basis by means of the SocketSetIntSocketOption() routine.

Include: socket.goh

SocketPort

typedef struct {
        word		SP_port;				/*  16-bit port number */
        ManufacturerID		SP_manuf;				/*  16-bit manufacturer's ID */
} SocketPort;

Port numbers identify a particular line of communication within a machine. Port numbers may be 32-bit or 16-bit, depending on the domain. They are specified via the SocketPort structure, made up of a 16-bit ManufacturerID and 16-bit token number. If the domain uses 16-bit socket numbers, then use MANUFACTURER_ID_SOCKET_16BIT_PORT as the ManufacturerID . If the domain allows 32-bit socket numbers, then you may use MANUFACTURER_ID_SOCKET_16BIT_PORT or some other ManufacturerID .

Include: socket.goh

SocketRecvFlags

typedef WordFlags SocketRecvFlags;
        #define SRF_ADDRESS (0x8000) 					/*  return source address of data */
        #define SRF_URGENT (0x4000) 					/*  return urgent data, if any */
        #define SRF_PEEK (0x2000) 					/*  leave data in socket */

These flags allow you to set options when receiving data from a socket. SRF_ADDRESS requests the address of the machine sending the data. SRF_URGENT requests that only urgent data be returned, if any. SRF_PEEK asks that the data be left on the socket.

Include: socket.goh

SocketSendFlags

typedef WordFlags SocketSendFlags;
        #define SSF_ADDRESS (0x8000) 						/*  an address is supplied */
        #define SSF_URGENT (0x4000) 						/*  this is urgent data */
        #define SSF_OPEN_LINK (0x2000)					 	/*  this datagram should open the
                                 *    link, if it is closed       */

Use this structure to specify options when sending data over a socket. You may signal that you are specifying an address and/or that the packet sent should be marked urgent.

Include: socket.goh

SortOrder

typedef ByteEnum SortOrder;
#define SO_ASCENDING				0
#define SO_DESCENDING				1

SoundDriverCapability

typedef WordFlags SoundDriverCapability;
#define SDC_NOISE 				0x8000
#define SDC_WAVEFORM 				0x6000
#define SDC_TIMBRE 				0x1800
#define SDC_ENVELOPE 				0x0600

typedef WordFlags SoundDriverNoiseCapability;
#define SDNC_NO_NOISE 				0x0000
#define SDNC_WHITE_NOISE 				0x8000

typedef WordFlags SoundDriverWaveFormCapability
#define SDWFC_NONE 				0x0000
#define SDWFC_SELECT 				0x2000
#define SDWFC_GENERATE 				0x4000

typedef WordFlags SoundDriverTimbreCapability;
#define SDTC_TONE_GENERATOR 0x0000
#define SDTC_ADDITIVE 				0x0800
#define SDTC_MODULATOR 				0x1000
#define SDTC_SELECTIVE 				0x1800

typedef WordFlags SoundDriverEnvelopeCapability;
#define SDEC_NONE 				0x0000
#define SDEC_ADSR 				0x0200
#define SDEC_DSP 				0x0400

These fields encode information about what the sound driver is capable of in terms of music synthesis.

| Up: GEOS SDK TechDocs | Up | Prev: SocketError ... | Next: SoundPlayFlags ...


GEOS C Structs: SoundPlayFlags ...

| Up: GEOS SDK TechDocs | Up | Prev: SocketLoadType ... | Next: SpecWidth ...

SoundPlayFlags

typedef WordFlags SoundPlayFlags;
#define SPF_HIGH_PRIORITY				0x8000

SoundPriority

typedef enum {
 SP_SYSTEM_LEVEL=10, 				/* most urgent */
 SP_ALARM=20,
 SP_STANDARD=30,
 SP_GAME=40,
 SP_BACKGROUND=50 				/* least urgent */
} SoundPriority;

#define SP_IMMEDIATE 			-1
#define SP_THEME 			+1

If the user's sound device can't play all requested sounds, it will use SoundPriority values to determine which sounds are the most important.

The highest priority sound you may construct using these values is (SP_SYSTME_LEVEL + SP_IMMEDIATE). The least priority sound would be (SP_BACKGROUND + SP_THEME).

SoundStreamDeltaTimeType

typedef enum {
 SSDTT_MSEC=8, 				/* wait for N mili seconds */
 SSDTT_TICKS=10, 				/* wait for N ticks */
 SSDTT_TEMPO=12, 				/* wait for N beats */
} SoundStreamDeltaTimeType;
/* The following macros may help when constructing music buffers */
#define DeltaTick(time)  				SSDTT_TICKS, time
#define DeltaMS(time)  				SSDTT_MSEC, time
#define DeltaTempo(time) 				SSDTT_TEMPO, time

These are the units by which you can specify a sound's duration: milliseconds, timer "ticks" (each 1/60 second), or by means of an independently supplied tempo.

SoundStreamEvents

typedef enum {
 SSE_VOICE_ON=0, 				/* turn on voice event */
 SSE_VOICE_OFF=2, 				/* turn off voice event */
 SSE_CHANGE=4, 				/* change instrument */
 SSE_GENERAL=6 				/* system-specific event */
} SoundStreamEvents;
/* The following macros may help when constructing music buffers */
#define General(command)  SSE_GENERAL, command

#define Rest(duration)  General(GE_NO_EVENT), DeltaTick(duration)

#define VoiceOn(voice,freq,attack)  SSE_VOICE_ON, voice, freq, attack
#define VoiceOff(voice) SSE_VOICE_OFF, voice
#define ChangeEnvelope(voice, instrument, table)  \
                    SSE_CHANGE, voice, instrument, table

#define SoundNote(voice,freq,duration,attack)  \
    VoiceOn(voice, freq, attack), DeltaTempo(duration), VoiceOff(voice)
#define Staccato(voice,freq,duration,attack) \
     VoiceOn(voice, freq, attack), DeltaTempo(((duration*0x03)/0x04)), \
     VoiceOff(voice), DeltaTempo((duration/0x4))
#define Natural(voice,freq,duration,attack) \
    VoiceOn(voice, freq, attack), DeltaTempo(((duration*0x07)/0x08)),
    VoiceOff(voice), DeltaTempo((duration/0x8))
#define Legato(voice,freq,duration,attack)  \
    SoundNote(voice, freq, duration, attack)

These are the "events" that make up a music buffer.

SoundStreamSize

typedef word SoundStreamSize;
#define SSS_ONE_SHOT 128 /* 128 bytes (very small) */
#define SSS_SMALL 256 /* 256 bytes */
#define SSS_MEDIUM 512 /* 512 bytes (nice size) */
#define SSS_LARGE 1024

SpecHeight

typedef WordFlags SpecHeight;
#define SH_TYPE				0x8c00
#define SH_DATA				0x03ff

SpecSizeSpec

typedef WordFlags SpecSizeSpec;
#define SSS_TYPE				0x8c00
#define SSS_DATA				0x03ff

SpecSizeType

typedef ByteEnum SpecSizeType;
#define SST_PIXELS						0x0000
#define SST_COUNT						0x0400
#define SST_PCT_OF_FIELD_WIDTH						0x0800
#define SST_PCT_OF_FIELD_HEIGHT						0x0c00
#define SST_AVG_CHAR_WIDTHS						0x1000
#define SST_WIDE_CHAR_WIDTHS						0x1400
#define SST_LINES_OF_TEXT						0x1800

SpecSocketDrError

typedef enum /*word*/ {
        SSDE_DEVICE_BUSY = 0x0100,      						/* serial port or modem
                               driver in use */
        SSDE_DIAL_ERROR = 0x0200,
        SSDE_LINE_BUSY = 0x0300,        /* modem got a busy signal */
        SSDE_NO_DIALTONE = 0x0400,
        SSDE_NO_ANSWER = 0x0500,
        SSDE_NO_CARRIER = 0x0600,
        SSDE_BLACKLISTED = 0x0700,      /* used in GSM network */
        SSDE_DELAYED = 0x0800,          /* used in GSM network */
        SSDE_CALL_FAILED = 0x0900, 						/* couldn't dial for some reason */
        SSDE_NEG_FAILED = 0x0a00,
        SSDE_AUTH_REFUSED = 0x0b00,
        SSDE_AUTH_FAILED = 0x0c00,
        SSDE_LQM_FAILURE = 0x0d00,
        SSDE_LOOPED_BACK = 0x0e00,
        SSDE_IDLE_TIMEOUT = 0x0f00,
        SSDE_DEVICE_NOT_FOUND = 0x1000,
        SSDE_DEVICE_TIMEOUT = 0x1100,
        SSDE_DEVICE_ERROR = 0x1200,
        SSDE_NO_USERNAME = 0x1300, 						/* no username and password
                               prompting used */
        SSDE_CANCEL = 0x1400,           /* user cancelled */
        SSDE_INVALID_ACCPNT = 0x1500    /* invalid access point */
} SpecSocketDrError;

Certain Socket library operations may return an error value as a SocketError value. If the root cause of the error was a low-level communications failure, then the high byte of the returned error word may contain a SpecSocketDrError value, with the SocketError value in the low byte.

| Up: GEOS SDK TechDocs | Up | Prev: SocketLoadType ... | Next: SpecWidth ...


GEOS C Structs: SpecWidth ...

| Up: GEOS SDK TechDocs | Up | Prev: SoundPlayFlags ... | Next: SpoolInfoType ...

SpecWidth

typedef WordFlags SpecWidth
#define SW_TYPE				0x8c00
#define SW_DATA				0x03ff

#define PCT_0				0x000
#define PCT_5				0x033
#define PCT_10				0x066
#define PCT_15				0x099
#define PCT_20				0x0cc
#define PCT_25				0x100
#define PCT_30				0x133
#define PCT_35				0x166
#define PCT_40				0x199
#define PCT_45				0x1cc
#define PCT_50				0x200
#define PCT_55				0x233
#define PCT_60				0x266
#define PCT_65				0x299
#define PCT_70				0x2cc
#define PCT_75				0x300
#define PCT_80				0x333
#define PCT_85				0x366
#define PCT_90				0x399
#define PCT_95				0x3cc
#define PCT_100				0x3ff

SpecWinSizePair

typedef struct {
SpecWinSizeSpec				SWSP_x;
SpecWinSizeSpec				SWSP_y;
} SpecWinSizePair;

SpecWinSizeSpec

typedef WordFlags SpecWinSizeSpec;
#define SWSS_RATIO				0x8000
#define SWSS_SIGN				0x4000
#define SWSS_MANTISSA				0x3c00
#define SWSS_FRACTION				0x03ff

SpecialFunctions

typedef enum /* word */ {
SF_FILENAME,
SF_PAGE,
SF_PAGES,
} SpecialFunctions;

SpoolError

typedef enum /* word */ {
SERROR_NO_SPOOL_FILE,
SERROR_NO_PRINT_DRIVER,
SERROR_NO_PORT_DRIVER,
SERROR_NO_PRINTERS,
SERROR_NO_MODE_AVAIL,
SERROR_CANT_ALLOC_BITMAP,
SERROR_NO_VIDMEM_DRIVER,
SERROR_MANUAL_PAPER_FEED,
SERROR_CANT_LOAD_PORT_DRIVER,
SERROR_PORT_BUSY,
SERROR_TEST_NO_PAPER,
SERROR_TEST_OFFLINE,
SERROR_TEST_PARALLEL_ERROR,
SERROR_MISSING_COM_PORT,
SERROR_PRINT_ON_STARTUP
} SpoolError;

SpoolFileName

typedef struct {
char	SFN_base[5];
char	SFN_num[3];
char	SFN_ext[5];
} SpoolFileName;

| Up: GEOS SDK TechDocs | Up | Prev: SoundPlayFlags ... | Next: SpoolInfoType ...


GEOS C Structs: SpoolInfoType ...

| Up: GEOS SDK TechDocs | Up | Prev: SpecWidth ... | Next: StandardDialog4ResponseTriggerTable ...

SpoolInfoType

typedef enum /* word */ {
SIT_JOB_INFO,
SIT_QUEUE_INFO
} SpoolInfoType;

SpoolOpStatus

typedef enum /* word */ {
SPOOL_OPERATION_SUCCESSFUL,
SPOOL_JOB_NOT_FOUND,
SPOOL_QUEUE_EMPTY,
SPOOL_QUEUE_NOT_EMPTY,
SPOOL_QUEUE_NOT_FOUND,
SPOOL_CANT_VERIFY_PORT,
SPOOL_OPERATION_FAILED
} SpoolOpStatus;

SpoolTimeStruct

typedef struct {
byte	STS_second;			/* second of the minute (0-59) */
byte	STS_minute;			/* minute of the hour (0-59) */
byte	STS_hour;			/* hour of the day (0-23) */
} SpoolTimeStruct;

SpoolVerifyAction

typedef enum {
SVA_NO_MESSAGE,
SVA_WARNING,
SVA_PRINTING
} SpoolVerifyAction;

StandardDialogBoxType

typedef enum {
SDBT_FILE_NEW_CANNOT_CREATE_TEMP_NAME,
SDBT_FILE_NEW_INSUFFICIENT_DISK_SPACE,
SDBT_FILE_NEW_ERROR,
SDBT_FILE_NEW_WRITE_PROTECTED,
SDBT_FILE_OPEN_SHARING_DENIED,
SDBT_FILE_OPEN_FILE_NOT_FOUND,
SDBT_FILE_OPEN_INVALID_VM_FILE,
SDBT_FILE_OPEN_INSUFFICIENT_DISK_SPACE,
SDBT_FILE_OPEN_ERROR,
SDBT_FILE_OPEN_READ_ONLY,
SDBT_FILE_OPEN_VM_DIRTY,
SDBT_FILE_OPEN_APP_MORE_RECENT_THAN_DOC,
SDBT_FILE_OPEN_DOC_MORE_RECENT_THAN_APP,
SDBT_FILE_SAVE_INSUFFICIENT_DISK_SPACE,
SDBT_FILE_SAVE_ERROR,
SDBT_FILE_SAVE_WRITE_PROTECTED,
SDBT_FILE_SAVE_AS_FILE_EXISTS,
SDBT_FILE_SAVE_AS_SHARING_DENIED,
SDBT_FILE_CLOSE_SAVE_CHANGES,
SDBT_FILE_CLOSE_ATTACH_DIRTY,
SDBT_FILE_REVERT_CONFIRM,
SDBT_FILE_REVERT_ERROR,
SDBT_FILE_ATTACH_DISK_NOT_FOUND,
SDBT_CANNOT_OPEN_VOLUME_SELECTED,
SDBT_QUERY_SAVE_AS_TEMPLATE,
SDBT_QUERY_SAVE_AS_EMPTY,
SDBT_QUERY_SAVE_AS_DEFAULT,
SDBT_QUERY_SAVE_AS_MULTI_USER,
SDBT_QUERY_SAVE_AS_PUBLIC,
SDBT_QUERY_RESET_EMPTY_FILE,
SDBT_QUERY_RESET_DEFAULT_FILE,
SDBT_CANNOT_OPEN_EMPTY_FILE
} StandardDialogBoxType;

StandardDialogParams

typedef struct {
word							 SDP_customFlags;
char							*SDP_customString;
char							*SDP_stringArg1;
char							*SDP_stringArg2;
StandardDialogResponseTriggerTable							*SDP_customTriggers;
} StandardDialogParams;

StandardDialogOptrParams

typedef struct {
word	SDOP_customFlags;
optr	SDOP_customString;
optr	SDOP_stringArg1;
optr	SDOP_stringArg2;
optr	SDOP_customTriggers;
} StandardDialogOptrParams;

StandardDialogResponseTriggerEntry

typedef struct {
optr	SDRTE_moniker;
word	SDRTE_responseValue;
} StandardDialogResponseTriggerEntry;

StandardDialog1ResponseTriggerTable

typedef struct {
 word							SD1RTT_numTriggers;
 StandardDialogResponseTriggerEntry							SD1RTT_trigger1;
} StandardDialog1ResponseTriggerTable;

StandardDialog2ResponseTriggerTable

typedef struct {
 word							SD2RTT_numTriggers;
 StandardDialogResponseTriggerEntry							SD2RTT_trigger1;
 StandardDialogResponseTriggerEntry							SD2RTT_trigger2;
} StandardDialog2ResponseTriggerTable;

StandardDialog3ResponseTriggerTable

typedef struct {
 word							SD3RTT_numTriggers;
 StandardDialogResponseTriggerEntry							SD3RTT_trigger1;
 StandardDialogResponseTriggerEntry							SD3RTT_trigger2;
 StandardDialogResponseTriggerEntry							SD3RTT_trigger3;
} StandardDialog3ResponseTriggerTable;

| Up: GEOS SDK TechDocs | Up | Prev: SpecWidth ... | Next: StandardDialog4ResponseTriggerTable ...


GEOS C Structs: StandardDialog4ResponseTriggerTable ...

| Up: GEOS SDK TechDocs | Up | Prev: SpoolInfoType ... | Next: StyleElementFlags ...

StandardDialog4ResponseTriggerTable

typedef struct {
 word								SD4RTT_numTriggers;
 StandardDialogResponseTriggerEntry 								SD4RTT_trigger1;
 StandardDialogResponseTriggerEntry 								SD4RTT_trigger2;
 StandardDialogResponseTriggerEntry 								SD4RTT_trigger3;
 StandardDialogResponseTriggerEntry 								SD4RTT_trigger4;
} StandardDialog4ResponseTriggerTable;

StandardPath

typedef enum /* word */ {
SP_NOT_STANDARD_PATH=0,
SP_TOP=1,
SP_APPLICATION=3,
SP_DOCUMENT=5,
SP_SYSTEM=7,
SP_PRIVATE_DATA=9,
SP_STATE=11,
SP_FONT=13,
SP_SPOOL=15,
SP_SYS_APPLICATION=17,
SP_PUBLIC_DATA=19,
SP_MOUSE_DRIVERS=21,
SP_PRINTER_DRIVERS=23,
SP_FILE_SYSTEM_DRIVERS=25,
SP_VIDEO_DRIVERS=27,
SP_SWAP_DRIVERS=29,
SP_KEYBOARD_DRIVERS=31,
SP_FONT_DRIVERS=33,
SP_IMPORT_EXPORT_DRIVERS=35,
SP_TASK_SWITCH_DRIVERS=37,
SP_HELP_FILES=39,
SP_TEMPLATE=41,
SP_POWER_DRIVERS=43,
SP_DOS_ROOM=45,
SP_HWR=47,
SP_WASTE_BASKET=49,
SP_BACKUP=51,
SP_PAGER_DRIVERS=53
SP_DUMMY=256
} StandardPath;

Most routines which are passed disk handles can also be passed members of the StandardPath enumerated type. Standard paths let applications access files in a disk-independent manner. Standard paths are usually arranged in a certain hierarchy; for example, the STATE directory usually belongs to the PRIVDATA directory. However, this is entirely at the user's discretion; applications may not make any assumption about how the standard paths are arranged.

StandardSoundType

See UserStandardSound() .

StreamBlocker

typedef	enum{
    STREAM_BLOCK			= 2,
    STREAM_NO_BLOCK			= 0
} StreamBlocker;

StreamError

typedef	enum{
    STREAM_NOT_IMPLEMENTED,
    STREAM_NO_ERROR,
    STREAM_WOULD_BLOCK,
    STREAM_CLOSING,
    STREAM_CANNOT_ALLOC,
    STREAM_BUFFER_TOO_LARGE,
    STREAM_CLOSED,
    STREAM_SHORT_READ_WRITE,
    STREAM_NO_DEVICE,
    STREAM_DEVICE_IN_USE,
    STREAM_POWER_ERROR
} StreamError;

StreamOpenFlags

typedef	enum {
    STREAM_OPEN_NO_BLOCK				= 0x01,
    STREAM_OPEN_TIMEOUT				= 0x02
} StreamOpenFlags

StreamToken

typedef Handleword StreamToken;

StreamRoles

typedef	enum {
    STREAM_ROLES_WRITER				= 0,
    STREAM_ROLES_READER				= -1,
    STREAM_ROLES_BOTH				= -2
} StreamRoles;

StyleChunkDesc

typedef struct {
VMFileHandle			SCD_vmFile;
word			SCD_vmBlockOrMemHandle;
ChunkHandle			SCD_chunk;
} StyleChunkDesc;

| Up: GEOS SDK TechDocs | Up | Prev: SpoolInfoType ... | Next: StyleElementFlags ...


GEOS C Structs: StyleElementFlags ...

| Up: GEOS SDK TechDocs | Up | Prev: StandardDialog4ResponseTriggerTable ... | Next: SystemDrawMask ...

StyleElementFlags

typedef WordFlags StyleElementFlags;
#define SEF_DISPLAY_IN_TOOLBOX						0x8000

StyleElementHeader

typedef struct {
NameArrayElement				SEH_meta;
word				SEH_baseStyle;
StyleElementFlags				SEH_flags;
dword				SEH_privateData;
} StyleElementHeader;

StyleSheetElementHeader

typedef struct {
 RefElementHeader				SSEH_meta;
 word				SSEH_style;
} StyleSheetElementHeader;

SupportedEnvelopeFormat

typedef enum {
 SEF_NO_FORMAT,
 SEF_SBI_FORMAT,
 SEF_CTI_FORMAT
} SupportedEnvelopeFormat;

These values specify how a sound device can simulate musical instruments, if it can at all.

sword

typedef signed short sword;

SysConfigFlags

typedef ByteFlags SysConfigFlags;
#define SCF_UNDER_SWAT				0x80
#define SCF_2ND_IC 				0x40
#define SCF_RTC 				0x20
#define SCF_COPROC 				0x10
#define SCF_RESTARTED 				0x08
#define SCF_CRASHED 				0x04
#define SCF_MCA 				0x02
#define SCF_LOGGING				0x01

The above flags indicate the system configuration. Any or all of these flags may be set at a time; if a flag is set, the description is true. These flags are used by the kernel and can be retrieved with SysGetConfig() .

SysDrawMask

typedef ByteFlags SysDrawMask;
#define SDM_INVERSE				0x80
#define SDM_MASK				0x7f

SysGetInfoType

See: SysGetInfo() .

SysMachineType

typedef ByteEnum SysMachineType;
#define SMT_UNKNOWN				0
#define SMT_PC 				1
#define SMT_PC_CONV 				2
#define SMT_PC_JR 				3
#define SMT_PC_XT 				4
#define SMT_PC_XT_286 				5
#define SMT_PC_AT 				6
#define SMT_PS2_30 				7
#define SMT_PS2_50 				8
#define SMT_PS2_60 				9
#define SMT_PS2_80 				10
#define SMT_PS1 				11

A byte-sized value indicating the type of machine running GEOS. This value can be retrieved with SysGetConfig() .

SysNotifyFlags

See: SysNotify() .

SysProcessorType

typedef ByteEnum SysProcessorType;
#define SPT_8088				0
#define SPT_8086 				0
#define SPT_80186 				1
#define SPT_80286 				2
#define SPT_80386 				3
#define SPT_80486 				4

This enumerated type is a byte that indicates the type of processor on the system running GEOS. It can be retrieved with SysGetConfig() .

SysShutdownType

See: SysShutdown() .

SysStats

typedef struct {
dword 		SS_idleCount;				/* Idle ticks in the last second. */
SysSwapInfo	 	SS_swapOuts;				/* Outward-bound swap activity. */
SysSwapInfo 		SS_swapIns;				/* Inward-bound swap actividy. */
word 		SS_contextSwitches;				/* Context switches in last second. */
word 		SS_interrupts;				/* Interrupts in the last second. */
word 		SS_runQueue;				/* Runnable threads at end of
                         * last second. */
} SysStats;

This structure is returned by SysStatistics() and represents the current performance statistics of GEOS.

SysSwapInfo

typedef struct {
word 	SSI_paragraphs;			/* Number of paragraphs swapped. */
word 	SSI_blocks;			/* Number of blocks swapped. */
} SysSwapInfo;

Structure used to represent current swap activity in SysStats structure.

| Up: GEOS SDK TechDocs | Up | Prev: StandardDialog4ResponseTriggerTable ... | Next: SystemDrawMask ...


GEOS C Structs: SystemDrawMask ...

| Up: GEOS SDK TechDocs | Up | Prev: StyleElementFlags ... | Next: TextMode ...

SystemDrawMask

typedef ByteEnum SystemDrawMask;
#define SDM_TILE					0
#define SDM_SHADED_BAR 					1
#define SDM_HORIZONTAL 					2
#define SDM_VERTICAL 					3
#define SDM_DIAG_NE 					4
#define SDM_DIAG_NW 					5
#define SDM_GRID 					6
#define SDM_BIG_GRID 					7
#define SDM_BRICK 					8
#define SDM_SLANT_BRICK 					9
#define SDM_0 					89
#define SDM_12_5 					81
#define SDM_25 					73
#define SDM_37_5 					65
#define SDM_50 					57
#define SDM_62_5 					49
#define SDM_75 					41
#define SDM_87_5 					33
#define SDM_100 					25
#define SDM_CUSTOM 					0x7f
#define SET_CUSTOM_PATTERN 					SDM_CUSTOM

SystemHatch

typedef ByteEnum SystemHatch;
#define SH_VERTICAL				0
#define SH_HORIZONTAL 				1
#define SH_45_DEGREE 				2
#define SH_135_DEGREE 				3
#define SH_BRICK 				4
#define SH_SLANTED_BRICK 				5

TargetLevel

typedef enum /* word */ {
TL_TARGET				= 0,
TL_CONTENT,
TL_GENERIC_OBJECTS				= 1000,
TL_GEN_SYSTEM,
TL_GEN_FIELD,
TL_GEN_APPLICATION,
TL_GEN_PRIMARY,
TL_GEN_DISPLAY_CTRL,
TL_GEN_DISPLAY,
TL_GEN_VIEW,
TL_LIBRARY_LEVELS				= 2000,
TL_APPLICATION_OBJECTS				= 3000,
} TargetLevel;

TcpAccPntExtendedAddress

typedef struct {
        word    TAPEA_linkSize;             /* 3 */
        byte    TAPEA_linkType;             /* LinkType (LT_ID) */
        word    TAPEA_accPntID;
} TcpAccPntExtendedAddress;

This is the "opaque" representation of a TCP address; specifically, the form used to identify a TCP address by means of its access point ID.

Set TAPEA_linkSize to 3; set TAPEA_linkType to LT_ID; set TAPEA_accPntID to the access point ID number.

TcpNonAccPntExtendedAddress

typedef struct {
   word    TNAPEA_linkSize;
   byte    TNAPEA_linkType;            /* LinkType (LT_ADDR) */
/* label byte TNAPEA_addr;     link address immed. followed by IP address */
} TcpNonAccPntExtendedAddress;

This is the "opaque" representation of a TCP address; specifically, the form used to identify a TCP address which is not a known access point--you will need to specify a phone number to dial in addition to an IP address.

Set TNAPEA_linkSize to the size of the phone number string. Set TNAPEA_linkType to LT_ADDR. Follow the structure witha null-terminated string consisting of the phone number to dial followed by the human-readable form of the IP address; for example "15105551234acme.com".

TcpOnlyExtendedAddress

typedef struct {
            word    TOEA_linkSize;              /* 0 */
        /*  label byte TOEA_ipAddr;             IP address  */
} TcpOnlyExtendedAddress;

This is the "opaque" representation of a TCP address; specifically, the form used to identify a TCP address when a link address is not required.

Set TOEA_linkSize to 0; follow the structure with the IP address, expressed either as a null-terminated string; for example "204.156.128.1" or "acme.com".

TestRectReturnType

See GrTestRectInReg() .

TextAttr

typedef struct {
byte 			TA_colorFlag;
RGBValue 			TA_color;
SysDrawMask 			TA_mask;
GraphicPattern 			TA_pattern;
TextStyle 			TA_styleSet;
TextStyle 			TA_styleClear;
TextMode 			TA_modeSet;
TextMode 			TA_modeClear;
WBFixed 			TA_spacePad;
FontID			TA_font;
WBFixed 			TA_size;
sword 			TA_trackKern;
} TextAttr;

| Up: GEOS SDK TechDocs | Up | Prev: StyleElementFlags ... | Next: TextMode ...


GEOS C Structs: TextMode ...

| Up: GEOS SDK TechDocs | Up | Prev: SystemDrawMask ... | Next: TextReferenceType ...

TextMode

typedef ByteFlags TextMode;
#define TM_TRACK_KERN						0x40
#define TM_PAIR_KERN 						0x20
#define TM_PAD_SPACES 						0x10
#define TM_DRAW_BASE 						0x08
#define TM_DRAW_BOTTOM 						0x04
#define TM_DRAW_ACCENT 						0x02
#define TM_DRAW_OPTIONAL_HYPHENS 						0x01

TextReference

typedef struct {
TR_type		TextReferenceType;
TR_ref		TextReferenceUnion;
} TextReference;

TextReferenceBlock

typedef struct {
MemHandle		TRB_handle;
} TextReferenceBlock;

This structure corresponds to a TextReferenceType of TRT_BLOCK. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRB_ handle stores the handle of the text buffer. No entries need to be filled in to allocate a destination buffer. The heap allocation request will be made with the HAF_NO_ERR flag. If VTGRF_RESIZE is passed then either the passed block or the allocated block will be resized to accommodate the text.

TextReferenceBlockChunk

typedef struct {
optr		TRBC_ref;
} TextReferenceBlockChunk;

This structure corresponds to a TextReferenceType of TRT_OPTR. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRBC_ ref stores the optr to a text buffer (a group of character)s. The handle field of TRBC _ref must be filled in.

It is assumed that the LMem heap will be able to accommodate this allocation. The caller is responsible for ensuring that this is the case.

If VTGRF_RESIZE is passed then either the passed block or the allocated block will be resized to accommodate the text.

TextReferenceDBItem

typedef struct {
FileHandle 		TRDBI_file;
DBItem		TRDBI_item;
DBGroup		TRDBI_group;
} TextReferenceDBItem;

This structure corresponds to a TextReferenceType of TRT_DB_ITEM. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRDBI__ file stores the VM file associated with this DB item.

TRDBI_ item stores the DB item itself.

TRDBI_ group stores the DB group the item belongs to.

Both TRDBI_ file and TRDBI_ group must be filled in if you want a VM block to be allocated.

If the TRDBI_ group field is set to DB_UNGROUPED then the item will be allocated ungrouped. TRDBI_group will hold the group in which the item was allocated on return.

If VTGRF_RESIZE is passed then either the passed block or the allocated block will be resized to accommodate the text.

TextReferenceHugeArray

typedef struct {
FileHandle		TRHA_file;
word		TRHA_array;
} TextReferenceHugeArray;

This structure corresponds to a TextReferenceType of TRT_HUGE_ARRAY. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRHA _file stores the VM file associated with this huge array.

TRHA_ array stores the Huge Array.

The TRHA_ file field must be set if you want a huge-array to be allocated.

If VTGRF_RESIZE is passed then either the passed block or the allocated block will be resized to accommodate the text.

TextReferencePointer

typedef struct {
char _far 		*TRP_pointer;
} TextReferencePointer;

This structure corresponds to a TextReferenceType of TRT_POINTER. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRP _pointer stores the pointer to the text. This field must be filled in.

VTGRF_RESIZE has no meaning with this sort of reference. VTGRF_ALLOCATE and VTGRF_ALLOCATE_ALWAYS are not valid flags to pass with this type of text reference.

This reference is the safest way to copy text out of a text object. Since the caller allocates the block it can also handle errors in the allocation.

TextReferenceSegmentChunk

typedef struct {
ChunkHandle			TRSC_chunk;
Segment			TRSC_segment;
} TextReferenceSegmentChunk;

This structure corresponds to a TextReferenceType of TRT_SEGMENT_CHUNK. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRSC_ segment stores the segment address of the text chunk. TRSC _chunk stores the chunk offset to the text.

It is assumed that the LMem heap will be able to accommodate this allocation. The caller is responsible for ensuring that this is the case.

If VTGRF_RESIZE is passed then either the passed block or the allocated block will be resized to accommodate the text.

| Up: GEOS SDK TechDocs | Up | Prev: SystemDrawMask ... | Next: TextReferenceType ...


GEOS C Structs: TextReferenceType ...

| Up: GEOS SDK TechDocs | Up | Prev: TextMode ... | Next: TimerHandle ...

TextReferenceType

typedef enum {
TRT_POINTER=0,				/* TextReferencePointer */
TRT_SEGMENT_CHUNK=2,				/* TextReferenceSegmentChunk */
TRT_OPTR=4,				/* TextReferenceBlockChunk */
TRT_BLOCK=6,				/* TextReferenceBlock */
TRT_VM_BLOCK=8,				/* TextReferenceVMBlock */
TRT_DB_ITEM=10,		/		* TextReferenceDBItem */
TRT_HUGE_ARRAY=12,				/* TextReferenceHugeArray */
} TextReferenceType;

TextReferenceUnion

typedef union {
TextReferencePointer 					TRU_pointer;
TextReferenceSegmentChunk				 	TRU_segChunk;
TextReferenceBlockChunk 					TRU_blockChunk;
TextReferenceBlock	 				TRU_block;
TextReferenceVMBlock					TRU_vmBlock;
TextReferenceDBItem	 				TRU_dbItem;
TextReferenceHugeArray					TRU_hugeArray;
} TextReferenceUnion;

TextReferenceVMBlock

typedef struct {
FileHandle 			TRVMB_file;
VMBlockHandle 			TRVMB_block;
} TextReferenceVMBlock;

This structure corresponds to a TextReferenceType of TRT_VM_BLOCK. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRVMB _file stores the VM file associated with this VM block. This entry must be filled in if you want a VM block to be allocated.

TRVMB _block stores the VM block itself.

If VTGRF_RESIZE is passed then either the passed block or the allocated block will be resized to accommodate the text.

TextStyle

typedef ByteFlags TextStyle;
#define TS_OUTLINE				0x40
#define TS_BOLD 				0x20
#define TS_ITALIC 				0x10
#define TS_SUPERSCRIPT 				0x08
#define TS_SUBSCRIPT 				0x04
#define TS_STRIKE_THRU 				0x02
#define TS_UNDERLINE 				0x01

ThreadException

typedef enum {
TE_DIVIDE_BY_ZERO=0,
TE_OVERFLOW=4,
TE_BOUND=8,
TE_FPU_EXCEPTION=12,
TE_SINGLE_STEP=16,
TE_BREAKPOINT=20
} ThreadException;

Processor exceptions used primarily for debugging, these are used with ThreadHandleException() .

ThreadGetInfoType

See ThreadGetInfo() .

ThreadHandle

typedef Handle ThreadHandle;

ThreadLockHandle

typedef Handle ThreadLockHandle;

, these flags determine what aspect of the thread

ThreadModifyFlags

typedef ByteFlags ThreadModifyFlags;
#define TMF_BASE_PRIO				0x80
#define TMF_ZERO_USAGE				0x40

Used with ThreadModify() is modified.

TimerCompressedDate

typedef WordFlags TimerCompressedDate;
#define TCD_YEAR 				0xfe00			/* years since 1980; e.g. 1988 is `8' */
#define TCD_MONTH				0x01e0			/* months (1 - 12) (0 illegal) */
#define TCD_DAY				0x001f			/* days (1-31) (0 illegal) */

TimerDateAndTime

typedef struct {
word			TDAT_year;			/* Year */
word			TDAT_month;			/* Number of month (1 through 12) */
word			TDAT_day;			/* Number of day in month (1 through 31) */
DaysOfTheWeek			TDAT_dayOfWeek;			/* DayOfTheWeek enumeration */
word			TDAT_hours;			/* Hour of the day (0 through 23) */
word			TDAT_minutes;			/* Minute in the hour (0 through 59) */
word			TDAT_seconds;			/* Second in the minute (0 through 59) */
} TimerDateAndTime;

This structure is used to keep track of the current time and date.

| Up: GEOS SDK TechDocs | Up | Prev: TextMode ... | Next: TimerHandle ...


GEOS C Structs: TimerHandle ...

| Up: GEOS SDK TechDocs | Up | Prev: TextReferenceType ... | Next: TravelOption ...

TimerHandle

typedef Handle TimerHandle;

TimerType

See: TimerStart() .

ToggleState

typedef ByteFlags ToggleState;
#define TS_CAPSLOCK				0x80
#define TS_NUMLOCK				0x40
#define TS_SCROLLLOCK				0x20

This structure describes the state of certain "toggles" which will affect how input is interpreted. These toggles correspond to the caps lock, num lock, and scroll lock keys.

TokenChars

typedef char TokenChars[TOKEN_CHARS_LENGTH]; /* TOKEN_CHARS_LENGTH=4 */

TokenDBItem

typedef DBGroupAndItem TokenDBItem;

TokenEntry

typedef struct {
GeodeToken			TE_token;			/* A GeodeToken structure for this file */
TokenDBItem			TE_monikerList;			/* A list of monikers for this token */
TokenFlags			TE_flags;			/* Flags indicating relocation status */
ReleaseNumber			TE_release;			/* Release number of the token DB */
ProtocolNumber			TE_protocol;			/* Protocol number of the toke DB */
} TokenEntry;

Used for the token entry in the map item of the token database, this structure identifies the structures and other information of each token. The TE_monikerList field points to a chunk containing the item numbers of the chunks of the token.

TokenFlags

typedef WordFlags TokenFlags;
#define TF_NEED_RELOCATION				0x8000

Used by token management routines, this flags record indicates whether the token has fields which must be relocated when the token is loaded or unloaded.

TokenMonikerInfo

typedef struct {
TokenDBItem 		TMI_moniker;
word 		TMI_fileFlag;			/* 0 if token is in shared token DB file;
                     * Non-0 if it's in local file */
} TokenMonikerInfo;

TokenRangeFlags

typedef WordFlags TokenRangeFlags;
#define TRF_ONLY_GSTRING	0x8000
#define TRF_ONLY_PASSED_MANUFID	0x4000
#define TRF_UNUSED		 0x3fff

TransError

typedef enum {
 TE_NO_ERROR, /* No error */
 TE_ERROR, /* General error */
 TE_INVALID_FORMAT, /* Format is invalid */
 TE_IMPORT_NOT_SUPPORTED, /* Format is not supported for export */
 TE_EXPORT_NOT_SUPPORTED, /* Format is not supported for export */
 TE_IMPORT_ERROR, /* General error during import */
 TE_EXPORT_ERROR, /* General error during export */
 TE_FILE_ERROR, /* Generic file error */
 TE_DISK_FULL, /* The disk is full */
 TE_FILE_OPEN, /* Error in opening a file */
 TE_FILE_READ, /* Error in reading from a file */
 TE_FILE_WRITE, /* Error in writing to a file */
 TE_FILE_TOO_LARGE, /* File is too large to process */
 TE_OUT_OF_MEMORY, /* Insufficient memory for import/export */
 TE_METAFILE_CREATION_ERROR, /* Error in creating the metafile */
 TE_EXPORT_FILE_EMPTY, /* File to be exported is empty */
 TE_CUSTOM /* Custom error message */
} TransError;

This enumerated type contains error values the impex library may wish to generate when translating.

TransErrorInfo

typedef struct {
 TransError transError;
 /* NOTE: customMsgHandle will be valid only if transError is TE_CUSTOM. */
word customMsgHandle;
} TransErrorInfo;

TransferBlockID

typedef dword TransferBlockID;
#define BlockIDFromFileAndBlock(f,b) 								(((dword)(f) << 16) | (b))
#define FileFromTransferBlockID(id) 								((VMFileHandle) ((id) >> 16))
#define BlockFromTransferBlockID(id) 								((VMBlockHandle) (id))

TransMatrix

typedef struct {
WWFixed 		TM_e11;
WWFixed 		TM_e12;
WWFixed 		TM_e21;
WWFixed 		TM_e22;
DWFixed 		TM_e31;
DWFixed 		TM_e32;
} TransMatrix;

The six variable elements of a coordinate transformation matrix.

| Up: GEOS SDK TechDocs | Up | Prev: TextReferenceType ... | Next: TravelOption ...


GEOS C Structs: TravelOption ...

| Up: GEOS SDK TechDocs | Up | Prev: TimerHandle ... | Next: UndoActionStruct ...

TravelOption

typedef enum {
 TO_NULL,
 TO_SELF,
 TO_OBJ_BLOCK_OUTPUT,
 TO_PROCESS
} TravelOption;
/* VisClass defines one other travel option: */
typedef enum {
 TO_VIS_PARENT=_FIRST_VisClass
} VisTravelOption;
/* GenClass defines some more travel options: */
typedef enum /* word */ {
 TO_GEN_PARENT=_FIRST_GenClass,
 TO_FOCUS,
 TO_TARGET,
 TO_MODEL,
 TO_APP_FOCUS,
 TO_APP_TARGET,
 TO_APP_MODEL,
 TO_SYS_FOCUS,
 TO_SYS_TARGET,
 TO_SYS_MODEL
} GenTravelOption;

This enumerated type can be used to specify the recipient of a message. Note that the values set up in the TravelOption , VisTravelOption , and GenTravelOption have been set up as descrete values.

TRUE

#define TRUE		-1	/* use as return value, not for comparisons */
#define FALSE		 0

TypeFromFormatID

word	TypeFromFormatID(type);
        ClipboardItemFormatID			type;

This macro extracts the format ID from the given clipboard format ID and manufacturer value.

UIFunctionsActive

typedef ByteFlags UIFunctionsActive;
#define UIFA_SELECT				0x80
#define UIFA_MOVE_COPY 				0x40
#define UIFA_FEATURES 				0x20
#define UIFA_CONSTRAIN 				0x10
#define UIFA_PREF_A 				0x08
#define UIFA_PREF_B 				0x04
#define UIFA_PREF_C 				0x02
#define UIFA_IN 				0x01
#define UIFA_ADJUST 				0x08
#define UIFA_EXTEND 				0x04
#define UIFA_MOVE 				0x08
#define UIFA_COPY 				0x04
#define UIFA_POPUP 				0x08
#define UIFA_PAN 				0x04

These flags describe the context of the user's input, providing some modal information.

UIInterfaceLevel

See UserGetInterfaceLevel() .

UndoActionDataFlags

typedef struct {
dword 		UADF_flags;
word 		UADF_extraflags;
} UndoActionDataFlags;

UndoActionDataPtr

typedef struct {
void 		*UADP_ptr;
word 		UADP_size;
} UndoActionDataPtr;

UndoActionDataType

typedef enum /* word */ {
UADT_FLAGS,
UADT_PTR,
UADT_VM_CHAIN,
} UndoActionDataType;

UndoActionDataUnion

typedef union {
/* To find out the type of data stored in this
 * union, check the value of the UndoActionStruct's
 * UAS_dataType field. */
UndoActionDataFlags 				UADU_flags;
UndoActionDataPtr 				UADU_ptr;
UndoActionDataVMChain 				UADU_vmChain;
} UndoActionDataUnion;
#define NULL_UNDO_CONTEXT 0

UndoActionDataVMChain

typedef struct {
/* This structure is filled in by the code for
 * MSG_META_UNDO. VMChains passed to
 * MSG_GEN_PROCESS_UNDO_ADD_ACTION should lie in the undo
 * file (which can be obtained by sending
 * MSG_GEN_PROCESS_UNDO_GET_FILE). */
VMChain 			UADVMC_vmChain;
VMFileHandle 			UADVMC_file;
} UndoActionDataVMChain;

| Up: GEOS SDK TechDocs | Up | Prev: TimerHandle ... | Next: UndoActionStruct ...


GEOS C Structs: UndoActionStruct ...

| Up: GEOS SDK TechDocs | Up | Prev: TravelOption ... | Next: VChar ...

UndoActionStruct

typedef struct {
UndoActionDataType 				UAS_dataType;
UndoActionDataUnion 				UAS_data;
dword 				UAS_appType;
} UndoActionStruct;

UtilAsciiToHexError

See UtilAsciiToHex32() .

UtilHexToAsciiFlags

typedef WordFlags UtilHexToAsciiFlags;
#define UHTAF_INCLUDE_LEADING_ZEROS							0x0002
#define UHTAF_NULL_TERMINATE							0x0001

VarDataCHandler

typedef	struct {
word	VDCH_dataType;
void	(*VDCH_handler) (MemHandle mh, ChunkHandle ch,
                VarDataEntry *extraData,
                word dataType, void *handlerData);
} VarDataCHandler;

See ObjVarScanData() .

VarDataEntry

typedef struct {
word	VDE_dataType;			/* vardata data type */
word	VDE_entrySize;			/* size of extra data; this field only exists
                 * if the type has extra data. */
} VarDataEntry;
#define VDE_extraData				sizeof(VarDataEntry);

Structure of a variable data entry. If the data type has no extra data, there will be no VDE_entrySize field. The extra data begins at offset VDE_extraData , defined above.

VarDataFlags

typedef WordFlags VarDataFlags;
#define VDF_TYPE				0xfffc				/* 14-bit data type */
#define VDF_EXTRA_DATA				0x0002				/* set if has extra data */
#define VDF_SAVE_TO_STATE				0x0001				/* set if type saved to state */

This is a word record containing three fields. This word is stored in the vardata structure's VDE_dataType field (see VarDataEntry , above).

VarDataKey

typedef word VardataKey;

VarObjRelocation

typedef struct {
VarDataFlags			VOR_type;			/* type and tag */
word			VOR_offset;
} VarObjRelocation;

| Up: GEOS SDK TechDocs | Up | Prev: TravelOption ... | Next: VChar ...


GEOS C Structs: VChar ...

| Up: GEOS SDK TechDocs | Up | Prev: UndoActionStruct ... | Next: VisRulerType ...

VChar

typedef ByteEnum VChar;
#define VC_NULL				0x0 /* NULL */
#define VC_CTRL_A 				0x1 /* <ctrl>-A */
#define VC_CTRL_B 				0x2 /* <ctrl>-B */
#define VC_CTRL_C 				0x3 /* <ctrl>-C */
#define VC_CTRL_D 				0x4 /* <ctrl>-D */
#define VC_CTRL_E 				0x5 /* <ctrl>-E */
#define VC_CTRL_F 				0x6 /* <ctrl>-F */
#define VC_CTRL_G 				0x7 /* <ctrl>-G */
#define VC_CTRL_H 				0x8 /* <ctrl>-H */
#define VC_CTRL_I 				0x9 /* <ctrl>-I */
#define VC_CTRL_J 				0xa /* <ctrl>-J */
#define VC_CTRL_K 				0xb /* <ctrl>-K */
#define VC_CTRL_L 				0xc /* <ctrl>-L */
#define VC_CTRL_M 				0xd /* <ctrl>-M */
#define VC_CTRL_N 				0xe /* <ctrl>-N */
#define VC_CTRL_O 				0xf /* <ctrl>-O */
#define VC_CTRL_P 				0x10 /* <ctrl>-P */
#define VC_CTRL_Q 				0x11 /* <ctrl>-Q */
#define VC_CTRL_R				0x12 /* <ctrl>-R */
#define VC_CTRL_S 				0x13 /* <ctrl>-S */
#define VC_CTRL_T 				0x14 /* <ctrl>-T */
#define VC_CTRL_U 				0x15 /* <ctrl>-U */
#define VC_CTRL_V 				0x16 /* <ctrl>-V */
#define VC_CTRL_W 				0x17 /* <ctrl>-W */
#define VC_CTRL_X 				0x18 /* <ctrl>-X */
#define VC_CTRL_Y 				0x19 /* <ctrl>-Y */
#define VC_CTRL_Z 				0x1a /* <ctrl>-Z */
#define VC_ESCAPE 				0x1b /* ESC */
#define VC_BLANK 				0x20 /* space */
/*
 * Numeric keypad keys
 */
#define VC_NUMPAD_ENTER 				0xd /* only on PS/2 keyboards */
#define VC_NUMPAD_DIV 				`/' /* only on PS/2 keyboards */
#define VC_NUMPAD_MULT 				`*'
#define VC_NUMPAD_PLUS 				`+'
#define VC_NUMPAD_MINUS 				`-'
#define VC_NUMPAD_PERIOD 				`.'
#define VC_NUMPAD_0 				`0'
#define VC_NUMPAD_1 				`1'
#define VC_NUMPAD_2 				`2'
#define VC_NUMPAD_3 				`3'
#define VC_NUMPAD_4 				`4'
#define VC_NUMPAD_5 				`5'
#define VC_NUMPAD_6 				`6'
#define VC_NUMPAD_7 				`7'
#define VC_NUMPAD_8				`8'
#define VC_NUMPAD_9				`9'
/*
 * Extended keyboard codes -- non-ASCII
 */
#define VC_F1 				0x80 /* Function keys */
#define VC_F2 				0x81
#define VC_F3 				0x82
#define VC_F4 				0x83
#define VC_F5				0x84
#define VC_F6 				0x85
#define VC_F7 				0x86
#define VC_F8 				0x87
#define VC_F9 				0x88
#define VC_F10 				0x89
#define VC_F11 				0x8a /* only on PS/2 keyboards */
#define VC_F12 				0x8b /* only on PS/2 keyboards */
#define VC_F13 				0x8c /* non-standard key */
#define VC_F14 				0x8d /* non-standard key */
#define VC_F15 				0x8e /* non-standard key */
#define VC_F16 				0x8f /* non-standard key */
#define VC_UP 				0x90 /* Cursor keys */
#define VC_DOWN 				0x91
#define VC_RIGHT 				0x92
#define VC_LEFT 				0x93
#define VC_HOME 				0x94 /* Scroll commands */
#define VC_END 				0x95
#define VC_PREVIOUS 				0x96
#define VC_NEXT 				0x97
#define VC_INS 				0x98 /* INS */
#define VC_DEL 				0x9a /* DEL */
#define VC_PRINTSCREEN				0x9b /* from <shift>-NUMPAD_MULT */
#define VC_PAUSE 				0x9c /* from <ctrl>-NUMLOCK */
#define VC_BREAK 				0x9e /* from <ctrl>- or <alt>-combo */
#define VC_SYSTEMRESET 				0x9f /* <ctrl>-<alt>-<del> combo */
/*
* Joystick control keys (0xa0 - 0xa9)
*/
#define VC_JOYSTICK_0 				0xa0        ; joystick 0 degrees
#define VC_JOYSTICK_45 	 			0xa1        ; joystick 45 degrees
#define VC_JOYSTICK_90 				0xa2        ; joystick 90 degrees
#define VC_JOYSTICK_135 				0xa3        ; joystick 135 degrees
#define VC_JOYSTICK_180 				0xa4        ; joystick 180 degrees
#define VC_JOYSTICK_225 				0xa5        ; joystick 225 degrees
#define VC_JOYSTICK_270 				0xa6        ; joystick 270 degrees
#define VC_JOYSTICK_315 				0xa7        ; joystick 315 degrees
#define VC_FIRE_BUTTON_1				0xa8        ; fire button #1
#define VC_FIRE_BUTTON_2 				0xa9        ; fire button #2

/*
 * Shift Keys			(0xe0 - 0xe7)
 */
#define VC_LALT 				0xe0
#define VC_RALT 				0xe1
#define VC_LCTRL 				0xe2
#define VC_RCTRL 				0xe3
#define VC_LSHIFT 				0xe4
#define VC_RSHIFT 				0xe5
#define VC_SYSREQ 				0xe6 /* Not on base PC keyboard */
#define VC_ALT_GR 				0xe7
/*
 * Toggle state keys (0xe8 - 0xef)
 */
#define VC_CAPSLOCK 				0xe8
#define VC_NUMLOCK 				0xe9
#define VC_SCROLLLOCK 				0xea
/*
 * Extended state keys (0xf0 - 0xf7)
 */
#define VC_INVALID_KEY 				0xff
#define VC_BACKSPACE 				VC_CTRL_H
#define VC_TAB 				VC_CTRL_I
#define VC_LF 				VC_CTRL_J
#define VC_ENTER 				VC_CTRL_M

| Up: GEOS SDK TechDocs | Up | Prev: UndoActionStruct ... | Next: VisRulerType ...


GEOS C Structs: VisRulerType ...

| Up: GEOS SDK TechDocs | Up | Prev: VChar ... | Next: VMOpenType ...

VisRulerType

typedef ByteEnum VisRulerType;
#define VRT_INCHES				0
#define VRT_CENTIMETERS				1
#define VRT_POINTS				2
#define VRT_PICAS				3
#define VRT_CUSTOM				CUSTOM_RULER_DEFINITION
#define VRT_NONE				NO_RULERS
#define VRT_DEFAULT				SYSTEM_DEFAULT

VisTextVariableType

typedef enum {
 VTVT_PAGE_NUMBER,
 VTVT_PAGE_NUMBER_IN_SECTION,
 VTVT_NUMBER_OF_PAGES,
 VTVT_NUMBER_OF_PAGES_IN_SECTION,
 VTVT_SECTION_NUMBER,
 VTVT_NUMBER_OF_SECTIONS,
 VTVT_CREATION_DATE_TIME,
 VTVT_MODIFICATION_DATE_TIME,
 VTVT_CURRENT_DATE_TIME,
 VTVT_STORED_DATE_TIME,
} VisTextVariableType;

VisTravelOption

The VisClass defines an enumerated value to be used in the place of a standard TravelOption . See the entry for TravelOption to see all possible values.

VisUpdateMode

typedef ByteEnum VisUpdateMode;
#define VUM_MANUAL 							0
#define VUM_NOW 							1
#define VUM_DELAYED_VIA_UI_QUEUE 							2
#define VUM_DELAYED_VIA_APP_QUEUE							3

VMAccessFlags

typedef ByteFlags VMAccessFlags;
#define VMAF_FORCE_READ_ONLY										0x80
#define VMAF_FORCE_READ_WRITE 										0x40
#define VMAF_ALLOW_SHARED_MEMORY 										0x20
#define VMAF_FORCE_DENY_WRITE 										0x10
#define VMAF_DISALLOW_SHARED_MULTIPLE 										0x08
#define VMAF_USE_BLOCK_LEVEL_SYNCHRONIZATION 										0x04

The VMAccessFlags specify what kind of access to the file the caller wants. The following flags are available:

VMAF_FORCE_READ_ONLY
If set, the file will be opened read-only, even if the default would be to open the file read/write. Blocks in read-only files cannot be dirtied, and changes in memory blocks will not be updated to the disk VM blocks.
VMAF_FORCE_READ_WRITE
If set, the file will be opened for read/write access, even if the default would be to open the file for read-only access.
VMAF_SHARED_MEMORY
If set, the VM manager should try to use shared memory when locking VM blocks; that is, the same memory block will be used for a given VM block no matter which thread locks the block.
VMAF_FORCE_DENY_WRITE
If set, then open the file deny-write; that is, no other threads will be allowed to open the file for read/write access.
VMAF_DISALLOW_SHARED_MULTIPLE
If this flag is set, files with the file attribute GFHF_SHARED_MULTIPLE cannot be opened.
VMAF_USE_BLOCK_LEVEL_SYNCHRONIZATION
If set, the block-level synchronization mechanism of the VM manager is assumed to be sufficient; the more restrictive StartExclusive/EndExclusive mechanism is not used. This is primarily intended for system software.

VMAttributes

typedef ByteFlags VMAttributes;
#define VMA_SYNC_UPDATE								0x80
#define VMA_BACKUP 								0x40
#define VMA_OBJECT_RELOC 								0x20
#define VMA_PRESERVE_HANDLES 								0x10
#define VMA_NOTIFY_DIRTY 								0x08
#define VMA_NO_DISCARD_IF_IN_USE		 						0x04
#define VMA_COMPACT_OBJ_BLOCK 								0x02
#define VMA_SINGLE_THREAD_ACCESS		 						0x01
/*
 * Attributes that must be set for object blocks: */
#define VMA_OBJECT_ATTRS				(VMA_OBJECT_RELOC | VMA_PRESERVE_HANDLES |
                 VMA_NO_DISCARD_IF_IN_USE |
                 VMA_SINGLE_THREAD_ACCESS)

VMBlockHandle

typedef word VMBlockHandle;

VMChain

typedef dword VMChain;

VMChainLink

typedef struct {
VMBlockHandle			VMC_next;
} VMChainLink;

VMChainTree

typedef struct {
VMChainLink		VMCT_meta;
word		VMCT_offset;
word		VMCT_count;
} VMChainTree;

VMFileHandle

typedef Handle VMFileHandle;

VMInfoStruct

typedef struct {
MemHandle		mh;
word		size;
word		userId;
} VMInfoStruct;

| Up: GEOS SDK TechDocs | Up | Prev: VChar ... | Next: VMOpenType ...


GEOS C Structs: VMOpenType ...

| Up: GEOS SDK TechDocs | Up | Prev: VisRulerType ...

VMOpenType

typedef ByteEnum VMOpenType;
#define VMO_OPEN							0
#define VMO_TEMP_FILE							1
#define VMO_CREATE							2
#define VMO_CREATE_ONLY							3
#define VMO_CREATE_TRUNCATE							4
#define VMO_NATIVE_WITH_EXT_ATTRS							0x80

The following types are available:

VMO_TEMP_FILE
If this is passed, the file will be a temporary data file. When you create a temporary file, you pass a directory path, not a file name. The path should be followed by fourteen null bytes, including the string's terminating null. The system will choose an appropriate file name and add it to the path string.
VMO_CREATE_ONLY
If this is passed, the document will be created. If a document with the specified name already exists in the working directory, VMOpen() will return an error condition.
VMO_CREATE
If this is passed, the file will be created if it does not already exist; otherwise it will be opened.
VMO_CREATE_TRUNCATE
If this is passed, the file will be created if it does not already exist; otherwise, it will be opened and truncated (all data blocks will be freed).
VMO_OPEN
Open existing file. If file does not exist, return an error condition.

VMOperation

typedef enum {
VMO_READ,
VMO_INTERNAL,
VMO_SAVE,
VMO_SAVE_AS,
VMO_REVERT,
VMO_UPDATE,
VMO_WRITE
} VMOperation;

VMRelocType

typedef enum {
VMRT_UNRELOCATE_BEFORE_WRITE,
VMRT_RELOCATE_AFTER_READ,
VMRT_RELOCATE_AFTER_WRITE,
VMRT_RELOCATE_FROM_RESOURCE,
VMRT_UNRELOCATE_FROM_RESOURCE,
} VMRelocType;

VMStartExclusiveReturnValue

typedef enum {
 VMSERV_NO_CHANGES,
 VMSERV_CHANGES,
 VMSERV_TIMEOUT
} VMStartExclusiveReturnValue;

VMGrabExclusive() returns a member of this enumerated type. It may have one of the following values:

VMSERV_NO_CHANGES
No other thread has changed this file since the last time this thread had access to the file.
VMSERV_CHANGES
The file may have been altered since the last time this thread had access to it; the thread should take appropriate actions (such as re-reading any cached data).
VMSERV_TIMEOUT
This call to VMGrabExclusive() failed and timed out without getting access to the file.

VolumeName

typedef char VolumeName[VOLUME_BUFFER_SIZE];

WBFixed

typedef struct {
byte WBF_frac;
word WBF_int;
} WBFixed;

wchar

typedef unsigned int wchar;

WindowHandle

typedef Handle WindowHandle;

WinInfoType

typedef enum /* word */ {
WIT_PRIVATE_DATA =0,
WIT_COLOR =2,
WIT_INPUT_OBJ =4,
WIT_EXPOSURE_OBJ =6,
WIT_STRATEGY =8,
WIT_FLAGS =10,
WIT_LAYER_ID =12,
WIT_PARENT_WIN =14,
WIT_FIRST_CHILD_WIN =16,
WIT_LAST_CHILD_WIN =18,
WIT_PREV_SIBLING_WIN =20,
WIT_NEXT_SIBLING_WIN =22,
WIT_PRIORITY=24,
} WinInfoType;

WinInvalFlag

typedef ByteEnum WinInvalFlag;
#define WIF_INVALIDATE					0
#define WIF_DONT_INVALIDATE					1

WinPassFlags

typedef WordFlags WinPassFlags;
#define WPF_CREATE_GSTATE						0x8000
#define WPF_ROOT 						0x4000
#define WPF_SAVE_UNDER 						0x2000
#define WPF_INIT_EXCLUDED 						0x1000
#define WPF_PLACE_BEHIND 						0x0800
#define WPF_PLACE_LAYER_BEHIND 						0x0400
#define WPF_LAYER 						0x0200
#define WPF_ABS 						0x0100
#define WPF_PRIORITY 						0x00ff

WinPriority

typedef ByteEnum WinPriority;
#define WIN_PRIO_POPUP					 4
#define WIN_PRIO_MODAL 					 6
#define WIN_PRIO_ON_TOP 					 8
#define WIN_PRIO_COMMAND 					10
#define WIN_PRIO_STD 					12
#define WIN_PRIO_ON_BOTTOM 					14

word

typedef unsigned int word;

WordAndAHalf

typedef struct {
word 	WAAH_low;
byte 	WAAH_high;
} WordAndAHalf;

WordFlags

typedef word WordFlags;

WWFixed

typedef struct {
word WWF_frac;
word WWF_int;
} WWFixed;

WWFixedAsDWord

typedef dword WWFixedAsDWord

XYOffset

typedef struct {
sword XYO_x;
sword XYO_y;
} XYOffset;

A graphics coordinate offset.

XYSize

typedef struct {
word XYS_width;
word XYS_height;
} XYSize;

A graphics size, in two dimensions.

XYValueAsDWord

typedef dword XYValueAsDWord;

A graphics size, in two dimensions, expressed as a DWord.

| Up: GEOS SDK TechDocs | Up | Prev: VisRulerType ...


Welcome to GEOS Programming

GEOS SDK Documentation: Language References

| Up: GEOS SDK TechDocs

The following documents are language references.

Goc Keyword Reference
Syntax for those @keywords used in GEOS/Goc programs.
GP File Keyword Reference
Syntax for .gp files.
| Up: GEOS SDK TechDocs


GOC Keywords

@alias

@alias(<protoMsg>) <messageDef>;

The @alias keyword is used for messages which take conditional parameters in an assembly handler. For example, if the assembly handler takes a word parameter normally and a dword only if a certain flag is set, you will need to have two C messages with the two different parameters. The @alias keyword allows this. Its arguments are shown below:

protoMsg
The name of the existing message that the alias will reference.
messageDef
The new message definition. This is a standard message definition as would follow the @message keyword.
@message void MSG_MY_MSG(word par);
@alias(MSG_MY_MSG) void MSG_MY_SECOND_MSG(dword par);

See Also: @message

@call

<ret> = @call [,<flags>] [{<cast_ret>}] \
<obj>::[{<cast_par>}]<msg>(<param>*);

The @call keyword sends the specified message to the specified object and makes the caller wait until the message is processed before continuing. The arguments of @call are shown below:

ret
A variable for receiving the return value of the message as defined by @message. This has the same usage as a typical function return value.
flags
Flags that determine how the message affects the recipient's event queue. The allowable flags are shown below. (The comma is required before each flag.)
cast_ret
A message to cast the message return value to. When Goc determines what type of value should be returned, it uses the return value of the cast_ret message if this field is used. The curly braces are required around this field.
obj
The name of the recipient object, or a variable representing the optr of the recipient.
cast_par
A message to cast the message parameters to. When Goc determines what type of values should be passed to the message, should be returned, it uses the parameters of the cast_par message if this field is used. The curly braces are required around this field.
msg
The name of the message to be sent, or an expression representing the message number. If an expression is used, you must cast the message to a certain type with the cast parameter.
param
Expressions or constants passed to the message. Parameters passed to messages are specified in the same way as if they were being passed directly to a function or routine in C.

The flags allowed with @call are shown below:

forceQueue
This flag will cause the message to be placed in the recipient's event queue, even if it could have been handled by a direct call.
checkDuplicate
This flag makes the kernel check if a message of the same name is already in the recipient's event queue. For this flag to work, forceQueue must also be passed. Note that due to implementation constraints, events will be checked from last to first rather than from first to last.
checkLastOnly
This flag works like checkDuplicate , above, except that it checks only the last message in the event queue.
replace
This flag modifies checkDuplicate and checkLastOnly by superseding the duplicate (old) event with the new one. The new event will be put in the duplicate's position in the event queue. If a duplicate is found but the replace flag is not passed, the duplicate will be dropped and the new event will be put at the end of the queue.
insertAtFront
This puts the message at the front of the recipient's event queue. Note that this flag will supersede the replace flag.
canDiscardIfDesperate
This flag indicates that this event may be discarded if the system is running extremely low on handles and requires more space immediately.

Additionally, @call alows the use of several special expressions in place of the recipient:

self
Send the message to the object issuing the @call command. e.g.
@call self::MSG_VIS_DRAW(flags, gstate);
process
Send the message to the object's associated Process object. e.g.
@call process::MSG_HELLO_RESPOND();
application
Send the message to the object's associated GenApplication object.
attr = @call application::MSG_GEN_GET_ATTRIBUTES();
@genParent
Send the message to the object's generic parent in a generic object tree.
@visParent
Send the message to the object's visible parent in a visible object tree.

If you need to send a message to an object's superclass, use the @callsuper keyword rather than @call.

gstate = @call myObj::MSG_META_CUT();
retVal = @call {MSG_MY_MSG} myObj::MSG_OTHER_MSG();
retVal = @call myObj::MSG_MY_MSG(10, param1);

See Also: @send, @callsuper, @message, @method, @object

@callsuper

<ret> = @callsuper [{<cast_ret>}] \
<obj>::<class>::[{<cast_par>}]<msg>(<param>*) [<flags>]+;
@callsuper;

The @callsuper keyword does two things: The most useful is to pass a received message on to the superclass to ensure default behavior is preserved; the second, and less used, acts just like @call but sends the message to the recipient's superclass rather than the recipient's class. This is rarely used but can be used if only default behavior is required of the message. Its arguments are shown below:

ret
Same as @call.
obj
Same as @call.
cast_ret
Same as @call .
class
The name of the object's superclass that should receive the message. It is possible to send the message to the highest superclass.
cast_par
Same as @call .
msg
Same as @call.
param
Same as @call.
flags
Same as @call.
(void) @callsuper myObj::MySupClass::MSG_MY_MSG();

See Also: @call, @send, @message, @method

@chunk

@chunk	<type> <name> [= <init>];

The @chunk keyword declares a resource chunk containing data of some kind. Data can be of any GEOS or C data type or structure, including a string of characters. The chunk must be declared between the resource delimiters @start and @end. Its arguments are described below:

type
The data type or structure type of the chunk.
name
The name of the chunk--how it will be referenced by other entities.
init
Initializer data, if any, to initialize the chunk to.

If you will need to access the chunk from another executable file, you must declare it in the other file with @extern. Objects are declared with @object.

typedef struct {
    int     a;
    int     b;
} MyStruct;
typedef char    MyString[13];
@start MyDataResource, data;
@chunk word			MyWordChunk;
@chunk MyStruct			MyMSChunk = {5, 10};
@chunk MyString			MyStringChunk = "My string";
@end MyDataResource;

See Also: @start, @end, @object, @extern

@chunkArray

@chunkArray 	<stype> <aname> [= {<init>}];

The @chunkArray keyword declares a Chunk Array, a special kind of chunk. Only uniform-element-size chunk arrays may be declared with this keyword. It has the following arguments:

stype
This is the type of each element in the Chunk Array. It may be any standard C or Goc type, or any derived type.
aname
This is the name of the Chunk Array.
init
You may declare the initializer values for a chunk array. If you do not set any initial values, the Chunk Array will be created with no elements.
@chunkArray int			someints;
@chunkArray dword			somedwords = {123456789,
                    6021023,
                    31415926};

See Also: @chunk, @elementArray

@class

@class	<cname>, <super> [, master [, variant]];

The @class keyword begins a class definition. All instance data and messages for the class are declared between @class and @endc. The arguments of @class are listed below:

cname
Name of the class being declared.
super
Name of the superclass.
master
Use of this term designates this class as a master class.
variant
Use of this term designates this class as a variant class.
@class MyTriggerClass, GenTriggerClass;
@endc
@class MyMasterVarClass, MetaClass, master, variant;
@endc

See Also: @endc, @classdecl

@classdecl

@classdecl <cname> [, neverSaved];

The @classdecl keyword defines a given class structure in memory. Every new class that will be used by an application must appear in an @classdecl declaration. The arguments for this keyword are shown below:

cname
The name of the class being declared.
neverSaved
Using this term indicates that objects of this class are never saved along with state information.
@classdecl MyTriggerClass;
@classdecl MyProcessClass, neverSaved;

See Also: @class

@composite

@instance @composite <iname> = <linkFieldName>;

The @composite keyword appears as a subcommand of @instance. It is a type of instance data--it indicates that an object of this class can have several children and that the @composite instance data field will be an optr to the first of its children. The arguments of the @composite keyword are given below:

iname
The name of the instance data field.
linkFieldName
The name of the @link instance data field for this class.
@class GenTrigWithKidsClass, GenTriggerClass;
    /* GI_link is the GenClass sibling link field. */
    @instance @composite GTWKI_comp = GI_link;
@endc

See Also: @instance, @link

@default

<fname> = @default [<op> [~]<attr>]*; /* to use default value of
                          instance data field */
@default <varRoot> = <super>; /* to specify default superclass of
                 a variant class */
@default <fname> = <value>; /* to specify a default value for an instance
data field defined by a superclass. */
<fname> = @default;

The @default keyword can be used in several ways: to specify the default value of an instance data field, to represent the default value of an object's instance data field, or to specify the default superclass of a variant class. It may also be used when defining a class to specify a default value to use with an instance data field defined by a superclass.

The @default keyword is most commonly used when modifying default instance data values of bitfield-type fields. The defaults are set in the @class definition and may be modified in the @object declaration. The arguments of @default are shown below:

fname
The name of the instance data field. Typically, this is a record.
op
A bitwise operator character. If setting certain bits, use the OR operator (|); if removing certain bits, use the AND operator (&).
attr
The name of the attribute bit to set or remove. If removing attribute bits, place the logical NOT character (~) in front of the attribute.
@object GenPrimaryClass MyPrimary {
    GI_states = @default & ~GENS_MAXIMIZED;
    GI_attrs = @default | GENA_TARGETABLE;
}

The @default keyword can also be used to specify the default superclass of a variant class. In this case, it has the following arguments:

varRoot
The name of the variant class, with the word "Class" removed. (e.g. the root of "MyVariantClass" is "MyVariant".)
super
The default superclass for this variant class.

To specify a class' default value for an instance data field when that instance data field is defined by a superclass, @default has the following arguments:

fname
The name of the instance data field.
value
The class' default value for the field.

To represent an object's default value for an instance data field, @default has the following arguments:

fname
The name of the instance data field.

See Also: @object, @instance, @class

@define

@define <mname>[(<pdef>)] <macro>

The @define directive defines a Goc macro. You can define C macros with the #define directive; macros that use Goc operators, keywords, or code must be defined with @define. Similarly, macros defined with @define must be later used with the "@" marker preceeding them; otherwise, the Goc processor will scan over the macro and will not evaluate it. The arguments of @define are listed below:

mname
The macro name. This can be used later as @ mname to invoke the macro.
pdef
The optional parameter definition, as with C macros.
macro
The macro.
@define MyChunk(a) @chunk char[] a = "text";
@define MyText(a,b) @chunk char[] a = "b";
/* You can later use these macros as follows: */
@MyChunk(Text1)
@MyText(Text2, newText)
/* This will evaluate to the following: */
@chunk char[] Text1 = "text";
@chunk char[] Text2 = "newText";

@deflib

@deflib <libName>

Most Goc libraries will have a .goh header file. This file should begin with a @deflib directive. This will see to it that no library header file is included more than once in a given compilation. The file must end with an @endlib directive. The @deflib directive takes the following argument:

libName
This is the name of the header file, with the .goh extension stripped off. For example, if the library's header file is hellolib.goh , the file would begin with
@deflib		hellolib

See Also: @endlib

@dispatch

@dispatch <event>;

The @dispatch keyword sends a previously-encapsulated message. This keyword is analogous to @send; use @dispatchcall if the event must be processed immediately. The encapsulated event must have been defined with @record. The arguments of @dispatch are given below:

event
The name of the encapsulated event, defined earlier with @record. This must not be a classed event.
@dispatch myEvent;
To change the recorded destination of the message, use the MessageSetDestination() routine.

See Also: @record, @send, @dispatchcall

@dispatchcall

<ret> = @dispatchcall (<nMsg>) <event>;

The @dispatchcall keyword sends a previously-encapsulated message. This keyword is analogous to @call; use @dispatch if the event can be sent with no return values. The encapsulated event must have been defined with @record. The arguments of @dispatchcall are given below:

nMsg
An override message to be sent. Encapsulated messages can store the message number to be sent; this will override the stored value.
event
The name of the encapsulated event, defined earlier with @record.
retVal = @dispatchcall (myMsg) myEvent;
To change the recorded destination of the message, use the MessageSetDestination() routine.

See Also: @record, @send, @dispatch

@elementArray

@elementArray 	<stype> <aname> [= {<init>}];

The @elementArray keyword declares a Element Array, a special kind of Chunk Array. It has the following arguments:

stype
This is the type of each element in the Element Array. It may be any standard C or Goc type, or any derived type.
aname
This is the name of the Element Array.
init
You may declare the initializer values for a chunk array. If you do not set any initial values, the Element Array will be created with no elements.

See Also: @chunk, @chunkArray

@end

@end	<segname>

The @end keyword denotes the end of a resource block definition that had been started with @start. Its one argument is the name of the resource segment.

@start MenuResource;
@end MenuResource;

See Also: @start, @header, @object, @chunk

@endc

@endc

The @endc keyword denotes the end of a class definition begun with @class. It has no arguments.

See Also: @class

@endif

@endif

The @endif directive denotes the end of a block of conditionally-compiled code. It is used with @if, @ifdef, and @ifndef.

See Also: @if, @ifdef, @ifndef

@endlib

@endlib

Most Goc libraries will have a .goh header file. This file should end with an @endlib directive. This will see to it that no library header file is included more than once in a given compilation. The file must begin with an @deflib directive.

See Also: @deflib

@exportMessages

@exportMessages <expname>, <num>;

The @exportMessages keyword sets aside a number of message spots so the messages may be declared elsewhere. This allows users of the class to declare messages that are guaranteed to be unique across all subclasses. Exported messages are declared with the @importMessage keyword. The arguments of @exportMessages are shown below:

expname
Name of the range being exported.
num
Number of message spots to be exported.
@exportMessages MetaUIMessages, 50;
@exportMessages MyExportedMessages, 12;

See Also: @importMessage, @reserveMessages, @message

@extern

@extern	<type> <name>;
@extern	method <cname>, <manme>+

The @extern keyword allows code in a given compilation session to access objects, chunks, monikers, and methods defined in another compilation session. The compiler will assume the element exists and will be linked by the Glue linker. If Glue is unable to locate and link the external resource element, it will respond with an error. The arguments of @extern are given below:

type
The type of resource element being referenced. This must be one of object , chunk , visMoniker , or method .
name
The name of the element being referenced.
@extern chunk MyChunk;
@extern object MyBlueTrigger;
@extern visMoniker GAL_visMoniker;

If @extern is being used to declare a method which is in a different file from the class declaration, it has the following arguments:

cname
The name of the class for which the method is defined.
mname+
The name of the message which invokes the method. As with normal method declarations, there must be at least one message which invokes the method.

Some confusion has arisen about when to use @extern . The following notes will hopefully prove useful.

Classes

Your class' definition should not be broken up over files. If you wish to keep your class definition in a file separate from your other code, this file should be a .goh file.

If your class is declared (@classdecl) in a file other than where it is defined (@class), then the declaring file should @include the defining file.

Normally the declaring file contains all method definitions for the class. If any method definitions are in another file, then both files will need an @extern keyword like so:

In file containing class declaration:

 @extern method MyClass, MSG_MY_DO_SOMETHING;

In file containing method code:

@extern method MyClass,
            MSG_MY_DO_SOMETHING(word myArg)
{ /* Method code here */ }
Object Trees

All objects declared in a static tree (e.g., your application's generic tree) should be in the same source file. If they are in different files, then they may be joined into a single tree only by dynamically adding objects from one file as children to objects of the other.

Note that if one file contains a tree of objects, then you may incorporate the whole tree by simply dynamically adding the top object in the file to the main tree. You won't have to add each object individually.

If an object declared in one source file will send a message to an object in another source file, you must include an @extern line in the source file containing the sending object:

@extern object ReceivingObjectName;

The message itself should be sent in the following manner (with variations possible if you will be using @call , passing arguments, or what have you):

optr ROOptr;
ROOptr = GeodeGetOptrNS(@ReceivingObjectName);
@send ROOptr::MSG_DO_SOMETHING(0, 0);

See Also: @chunk, @object, @visMoniker

gcnList

gcnList(<manufID>,<lname>) = <oname> [, <oname>]*;

The gcnList keyword, which does not have the keyword marker @ preceeding it, puts the listed objects onto the specified notification list. GCN lists are specified by both manufacturer ID and list type. The arguments of the gcnList keyword are given below:

manufID
The manufacturer ID number of the GCN list type. Often this will be MANUFACTURER_ID_GEOWORKS.
lname
The list type, or list name, of the GCN list.
oname
A listing of all the objects that will be included on the GCN list. Separate objects with commas.
@object GenApplicationClass HelloApp = {
    GI_comp = HelloPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) =
                        HelloPrimary;
}

See Also: @object

@genChildren

@send @genChildren::<msg>(<params>);

Any composite object in a generic object tree (therefore a subclass of GenClass ) can send a message that will be dispatched at once to all of its children. Note that any message sent with @genChildren as the destination must be dispatched with the @send keyword and therefore can have no return value and can not pass pointers in its parameters.

@genParent

[@send | @call] @genParent::<msg>(<params>);

Any composite object in a generic object tree (therefore a subclass of GenClass ) can use the @genParent address to send a message to its generic parent. This can be used with either @send or @call.

@gstring

@gstring <gsname> = {[<command> [, <command>]+]}

The @gstring keyword lets you declare a chunk containing GString data in Goc source code.

gsname
The name of the chunk which will contain the GString.
command
This may be any command which could be put in a GString. This is actually a series of bytes. Normally, the GS...() macros are used to specify the bytes.

An example:

@gstring MultPrimContentOneGraphic = {
    GSSetGStringBounds(10,10,48,64),
    GSSetTextColorIndex(C_VIOLET),
    GSSetFont(FID_DTC_URW_SANS, 54.0),
    GSSetTextStyle(TS_ITALIC, ~TS_ITALIC),
    GSDrawText(10, 10), "1",
    GSEndString()
};

The GSSetGStringBounds() command has been set up to optimize use of this GString: the programmer has pre-computed the drawing bounds of the GString. The graphics system will use the pre-computed bounds to speed up clipping.

The GSEndString() command signals that the GString is done. In all but the rarest cases, all GStrings should end with an end-gstring command.

@header

@header	<type> [= <init>];

The @header keyword sets the header of an object or data resource segment to a custom structure. The structure must begin with an LMemBlockHeader or ObjLMemBlockHeader. The arguments of @header are given below:

type
The name of the structure set as the new header type.
init
Any initializer data for the fields added to your structure.
typedef struct {
    LMemBlockHeader  meta;
    int     a;
    int     b;
} MyLMemBlockHeader;
@start MyDataResource, data;
@header MyLMemBlockHeader = 10, 12;
@end MyDataResource;

See Also: @start, @end, @object, @chunk

@if

@if (<cond>)

The @if directive denotes the beginning of a conditionally-compiled block of code. If the expression detailed in cond equates to true , then the code between the @if directive and the first corresponding @endif directive will be compiled with the rest of the code.

cond
The expression determining whether the code is to be compiled or not. This expression is based on numerical values, names of macros, and Boolean operators (|| and &&).
@if 0
    /* code not compiled */
@endif
@if MyMacro
    /* code compiled if MyMacro is defined */
@endif
@if defined(MyMacro) || MY_CONDITION
    /* code compiled either if MyMacro is defined or
     * if MY_CONDITION is non-zero */
@endif

See Also: @ifdef, @ifndef, @endif

@ifdef

@ifdef <item>

The @ifdef directive is similar to the @if directive in use, except the condition it evaluates is based solely on whether the item is defined or not (if item is defined, the following code is compiled).

See Also: @if, @ifndef, @endif

@ifndef

@ifndef <item>

The @ifndef directive is similar to the @ifdef directive in use, except the condition it evaluates is based solely on whether item is not defined (if item is not defined, the following code is compiled).

See Also: @if, @ifdef, @endif

@importMessage

@importMessage <expname>, <messageDef>;

The @importMessage keyword declares a message with a reserved message number set aside earlier by @exportMessages. The arguments of this keyword are given below:

expname
Name of the range exported with @exportMessages.
messageDef
Standard message definition--exactly the same as would follow the @message keyword for message declaration.
@importMessage MyExportedMessages, word MSG_MY_MSG(
                    byte param1, byte param2);

See Also: @exportMessages, @reserveMessages, @message

@include

@include <fname>

The @include directive is used to include Goc files into a code file. It is similar to the #include directive in C. Its only argument is a file name ( fname ) enclosed in either angled brackets or quotation marks. If you use quotation marks, the compiler will look first in the file's own directory; if you use angled brackets, it will look first in the standard include directories.

@include <stdapp.goh>
@include "Art/mkrGenDoc"

See Also: @extern

@instance

@instance <insType> <iname> = <default>;

The @instance keyword declares an instance data field for a class. This keyword will appear between the class delimeters @class and @endc. Its arguments are shown below:

insType
The data type of the instance data field. Must be a valid C data type or data structure. (Note--special types may also be used; see discussion below.)
iname
The name of the instance data field.
default
The default value of the field if it is not declared explicitly in the instance of the class.

The Goc preprocessor allows the use of several special types of instance data fields. To use these special types, insert the proper keyword (type name) in place of the insType argument above and do not include a default value for the field ( default ). The possible special types and their meanings are given in the list below (see the individual keyword entries for more detail):

@composite
This field will point to the first child in an object hierarchy. Note that this keyword has a special format. Rather than being allowed a default value, set the default argument in the declaration to be the same as the name of the corresponding @link field. This is important; otherwise, your program will not compile properly.
@link
This field will point to the next sibling object in an object hierarchy or will point to the parent.
@visMoniker
This field will contain a visual moniker or a pointer to a visual moniker resource chunk.
@kbdAccelerator
This field will contain a keyboard accelerator character.

Note that if you want to declare instance data fields for variable-sized data, you should use the @vardata keyword rather than @instance.

@instance int				myInteger = 10;
typedef struc {
    int     a;
    int     b;
} MyStruc;
@instance MyStruc				strucField = {7, 11};
@instance @visMoniker 				GI_moniker;
@instance @link				VI_link;
@instance @composite 				VCI_comp = VI_link;
@instance @kbdAccelerator GI_kbdAcc;

See Also: @vardata, @visMoniker, @link, @composite, @kbdAccelerator

@kbdAccelerator

@instance @kbdAccelerator <iname>;

The @kbdAccelerator keyword follows @instance to create an instance data field that will contain a keyboard accelerator. The iname argument is the name of the instance data field.

@instance @kbdAccelerator GI_kbdAcc;

See Also: @instance

@link

@instance @link <iname>;

The @link keyword follows @instance to define a link instance data field pointing to the object's next sibling in the object hierarchy. The iname argument is the name of the instance data field. Note that the name of the link field must be set as the default value of the corresponding @composite field.

@instance @link GI_link;
@instance @composite GI_comp = GI_link;

See Also: @instance, @composite

@localize

@localize { <string> <min>-<max> };
@localize { <string> <length> };
@localize { <string> };
@localize <string>;
@localize not;

This keyword is used to specify instructions for translators. When appearing under a @visMoniker or @chunk construction, this keyword specifies a string which the ResEdit program will show to translators localizing the geode.

For example:

@visMoniker FakeItemMoniker = "Data:";
@localize "This string will appear at the head of the list";

The arguments of this keyword are as follows:

string
The explanatory string.
min
The minimum length of the moniker or chunk's text.
max
The maximum length of the moniker or chunk's text.
length
The length of the moniker or chunk's text.
not
Keyword specifying that the moniker or chunk can not be localized.

@message

@message	<retType> <mname>([@stack] <param>*);

The @message keyword defines a message and its parameters and return values. This keyword will appear within a class definition (i.e., between @class and @endc). The message defined with @message will automatically be valid for the class for which it is defined as well as for subclasses of that class. The arguments of this keyword are shown below:

retType
The data type of the value returned by this message. This must be a standard C or GEOS data type or pointer.
mname
The name of the message. Typically, this will be the prefix "MSG_" followed by a shortened version of the class name, followed by a short name for the message.
@stack
This keyword may be used if the message might be sent from assembly language code instead of Goc. It indicates that the arguments will be passed on the stack; the handler will pop them off the stack in reverse order from the way they are listed in the declaration.
param*
The parameters for this message, of which there may be none or several. All the parameters must appear inside the parentheses. Parameters are defined in a similar manner as for functions and routines; each one consists of a data type followed by the name of the parameter of that type.
@message void MSG_TRIGGER_PUSHED(int push1);
@message word MSG_MY_MSG(byte firstParam, word secParam,
                    long thirdParam);

See Also: @method, @reserveMessages, @exportMessages, @importMessage, @record

@method

@method	[<hname>,] <cname>, <mname>+ [{<code>}];

The @method keyword begins definition of a method (message handler). Its arguments are listed below:

hname
The method name, if any. If no method name is given, one will be created by removing "Class" from the class name and "MSG_" from the message name and concatenating the two.
cname
The name of the class to which the method belongs. Each method belongs to only one class.
mname +
The name(s) of the message(s) handled by this method. There must be at least one message which invokes this method. There may be more than one, as long as they all have the same parameters.
code
Goc procedural code to handle the message. If there is no code, hname is assumed to be the name of an existing routine which should be used as the method.
@method   MyClass, MSG_MY_MSG {
    /* method code goes here */
}
@method   MyClassMethod, MyClass, MSG_MY_MSG {
    /* method code goes here */
}

See Also: @message

@noreloc

@noreloc <iname>;

The @noreloc keyword specifies that an instance data field (defined in the previous program statement) is not relocatable. Normally optr fields are assumed to be relocatable and will be automatically relocated by the system when shutting down and coming back from a shutdown; by means of the @noreloc, this automatic behavior can be turned off for a given field.

@object

@object	<class> <name> <flags>* = {
    [<fieldName> = <init>;]*
    [<varName> [= <init>];]*
}

The @object keyword defines an object in an object resource block. It must appear between @start and @end. Its arguments are defined below:

class
The name of the class of the object.
name
The name of the object.
flags
Flags associated with the object; currently only ignoreDirty is supported. When set, this flag indicates that changes to the object should not be saved to a state file.
fieldName
The name of an instance data field defined with @instance. Any number of such fields may be specified.
varName
The name of an instance data field defined with @vardata. Any number of such fields may be specified.
init
Initializer data for a normal instance data field or for the extra data of a variable data field. If a variable data field has no extra data, no initializer should be specified.

Many fields may be specified in the object declaration. Each field reference must be defined in a class in the object's class ancestry. Additionally, not all fields must be set. If a field is not specified within the @object declaration, the field will be set to its default value as defined by the class.

@start MyObjectResource;
@object GenTriggerClass MyTrigger ignoreDirty = {
    GI_visMoniker = "MyTrigger's Moniker";
}
@object GenApplicationClass MyApp = {
    GI_comp = MyPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) =
                            MyPrimary;
}
@object GenPrimaryClass MyPrimary = {
    GI_comp = MyMenu, MyInteraction, MyView;
    GI_visMoniker = "My Primary's Moniker";
}
@object MyClass NewObject = {
    NO_instance1 = 1;
    NO_instance6 = `C';
}
@end MyObjectResource;

See Also: @start, @end, @extern, @class, @instance, @vardata

@optimize

@optimize

This directive may be placed at the top of a .goh file. The directive instructs Goc to generate a specially processed .poh file which contains all the information of the .goh file, but is somewhat faster to compile. This .poh file is automatically regenerated if the corresponding .goh file has been changed since the last compilation.

@protominor

@protominor <prototypeName>

When creating a new version of an existing library, use the @protominor keyword to declare new messages and variable data fields for a class. Suppose your original class declaration looked like so:

@class MyClass, SuperClass;
    @message void MSG_M_DO_THIS(void);
    @vardata void TEMP_M_DONE_FLAG;
@endc

Having released this version of your class, you wished to release another version in which this class handled another message. You wanted to specify that this new message would only work with this new version of the library. This would be set up like so:

@class MyClass, SuperClass;
    @message void MSG_M_DO_THIS(void);
    @vardata void TEMP_M_DONE_FLAG;
    @protominor MyVersion20
    @message void MSG_M_DO_THAT(void);
@endc

You would also need to add an "incminor" line to the end of your .gp file:

incminor 	MyVersion20

To do the equivalent version control with routines, use the incminor .gp file directive.

See Also: @protoreset

@protoreset

@protoreset

The @protoreset keyword signals that the rest of a file should ignore any previous @protominor statements.

@class MyFirstClass, SuperClass;
    @message void MSG_M1_DO_THIS(void);
    @vardata void TEMP_M1_DONE_FLAG;
@endc
    @protominor MyVersion20
    @message void MSG_M1_DO_THAT(void);
    @protoreset
@class MySecondClass, SuperClass;
    @instance word M2I_token;
    @vardata void ATTR_M2_IGNORE_TOKEN;
    @protominor MyVersion20
    @vardata word ATTR_M2_ALTERNATE_TOKEN;
@endc

@prototype

@prototype <messageDef>;

The @prototype keyword allows multiple messages to have the same pass and return parameters. Use @prototype to define the pass and return values, then use @message to declare the messages that have these parameters. The messages defined with @message will have different message numbers and will invoke different methods. The messageDef argument is a standard message definition.

@prototype word MSG_MY_PROTO(byte param1);
@message(MSG_MY_PROTO) MSG_MY_MSG;
@message(MSG_MY_PROTO) MSG_MY_SECOND_MSG;

See Also: @alias, @message

@record

<event> = @record <obj>::<msg>(<param>*);

The @record keyword encapsulates an event for later use with @dispatch or @dispatchcall. The arguments of @record are as follows:

event
The name of the event. This name will be used with @dispatch and @dispatchcall later.
obj
The name of the object, or an expression representing the object that will receive the message. This may be set to null to indicate that the recipient will be determined when the message is sent.
msg
The name of the message, or an expression representing the message that will be sent. This may be set to null to indicate that the message will be determined when it it sent.
param
This is a list of parameters that will be sent with the message when it is dispatched.
myEvent = @record myObj::MSG_VIS_VUP_CREATE_GSTATE();

See Also: @dispatch, @dispatchcall, @call, @send

@reloc

@reloc	<iname>, [(<count>, <struct>)] <ptrType>;
@reloc	<iname>, <fn>, [(<count>, <struct>)] <ptrType>;

The @reloc keyword designates an instance data field that contains data requiring relocation on startup. Note that this does not include instance fields declared with the @composite and @link fields, but it does include any handle or pointer fields you may have. Note that there are two different formats for the use of @reloc. The first represents a normal instance field; the second represents a variable data instance field (see @vardata). This is not used with @instance or @vardata but stands alone.

The arguments of @reloc are shown below:

iname
The name of the relocatable instance data field.
count
If the instance variable is an array of relocatable data or structures containing relocatable fields, this is the number of elements in the array.
struct
If the relocatable data is an array of structures, this represents the name of the field within each structure that requires relocation.
ptrType
This is the type of pointer in the relocatable field. This must be one of optr (object pointer), ptr (far pointer), or handle .
fn
This is the name of the field within the extra data of the variable data. If no extra data will be associated with this relocatable field, then put a zero (0) rather than a field name.
@reloc MO_myHandle, handle;
@reloc MO_myVarHandle, 0, handle;
@reloc MO_myTable, (10, MyStruct), ptr;

See Also: @instance, @vardata

_reloc

@method [<hname>,] <cname>, _reloc { <code>};

The _reloc keyword is used to write relocation handlers for classes, if you need to relocate-unrelocate instance data when it's either read in or saved to state.

The arguments of _reloc are show below:

code
Code to execute when the object block is loaded in or saved out to state. , in which case instance data may need to be relocated or unrelocated by hand.

@reserveMessages

@reserveMessages <number>;

The @reserveMessages keyword reserves the given number of message spots. Messages are numbered sequentially according to the order of their declaration; this keyword allows one or more numbers to be skipped in the numbering process, allowing application upgrades without making earlier versions obsolete. The single argument is the number of message spots to skip.

@reserveMessages 25;

See Also: @exportMessages, @importMessage, @message

@send

@send	[<flags>+] [(<cast_ret>)] <obj>::[{<cast_par>}]<msg>(<param>*);

The @send keyword sends a given message to the specified object. The message will be sent and the sender's thread will continue executing without waiting for a response. If return values or synchronization is important, use the @call keyword. The parameters of @send are shown below:

flags
Flags that determine how the message affects the recipient's event queue. The allowable flags are shown below. (The comma is required before each flag.)
cast_ret
A message to cast the message return value to. When Goc determines what type of value should be returned, it uses the return value of the cast_ret message if this field is used. The curly braces are required around this field.
obj
The name of the recipient object, or an optr to the object.
cast_par
A message to cast the message parameters to. When Goc determines what type of values should be passed to the message, should be returned, it uses the parameters of the cast_par message if this field is used. The curly braces are required around this field.
msg
The name of the message to be sent, or an expression representing the message number. If an expression is used, you must cast the message to a certain type with the cast argument .
param
Expressions or constants passed to the message. Parameters passed to messages are specified in the same way as if they were being passed directly to a function or routine in C. Note that pointers may not be passed with @send but handles may; if you must pass a pointer, use @call.

The flags allowed with @send are shown below:

forceQueue
This flag will cause the message to be placed in the recipient's event queue, even if it could have been handled by a direct call.
checkDuplicate
This flag makes the kernel check if a message of the same name is already in the recipient's event queue. For this flag to work, forceQueue must also be passed. Note that due to implementation constraints, events will be checked from last to first rather than from first to last.
checkLastOnly
This flag works like checkDuplicate , above, except that it checks only the last message in the event queue.
replace
This flag modifies checkDuplicate and checkLastOnly by superseding the duplicate (old) event with the new one. The new event will be put in the duplicate's position in the event queue. If a duplicate is found but the replace flag is not passed, the duplicate will be dropped and the new event will be put at the end of the queue.
insertAtFront
This puts the message at the front of the recipient's event queue. Note that this flag will supersede the replace flag.
canDiscardIfDesperate
This flag indicates that this event may be discarded if the system is running extremely low on handles and requires more space immediately.
@send, forceQueue MyObj::MSG_MY_MSG(10, x);
@send MyObj::MSG_SET_ATTR(attributesParam);

See Also: @call, @callsuper, @message, @method

@specificUI

<fname>	= [@specificUI] <mod>* <key>;

The @specificUI keyword is used when setting a keyboard accelerator instance field in an object declaration. It tells the UI to allow the use of the keystrokes specified, even if they are normally reserved for the specific UI. The keyword itself takes no arguments; those shown are for the GenClass instance data field GI_kbdAccelerator . These are

fname
The name of the instance data field defined with @instance.
mod
Modifier keys; must be one or more of control , ctrl , shift , alt .
key
The accelerator character. Must be either a numeric value of a keyboard key or a letter enclosed in single quotation marks.
@object MyClass MyObject {
    GI_kbdAccelerator = ctrl shift `k';
}

See Also: GenClass , @kbdAccelerator, @instance

@stack

@message	<retType> <mname>([@stack] <param>*);

This keyword may be used if the message might be sent from assembly language code instead of Goc. It indicates that the arguments will be passed on the stack; the handler will pop them off the stack in reverse order from the way they are listed in the declaration.

See Also: @message

@start

@start	<segname> [, <flag>];

The @start keyword indicates the beginning of a resource block. The end of the block is denoted by the keyword @end. The arguments of @start are listed below:

segname
The name of the resource segment.
flags
Optional flag. The flag data , when set, indicates the block will be a data resource rather than an object resource. The flag notDetachable , when set, indicates the block should not be saved to a state file.
@start MenuResource;
@end MenuResource;
@start MyDialogResource, notDetachable;
@end MenuResource;
@start MyStringResource, data;
@end MyStringResource;

See Also: @end, @header, @object, @chunk

@uses

@uses <class>;

If you know that a variant class will always be resolved to be a subclass of some particular class, you can declare this with the @uses keyword. This will let the variant class define handlers for the "used" superclass. The keyword uses the following argument:

class
A class which will always be a superclass of the defined variant class.

Warnings: You must make sure that the variant class's inheritance is always resolved such that the used class is one of its ancestor classes.

See Also: @class

@vardata

@vardata	<type> <vname>;

The @vardata keyword creates a vardata data type for a class. Each type created with @vardata can be simply the name of the type, or it can have additional data (a single structure). The arguments of @vardata are given @defaultbelow:

type
This is the data type or structure type of the data field. If no extra data is to be associated with this field, then put the word void in place of a type.
vname
This is the name of the variable data instance field.
@vardata		dword		MY_FIRST_VAR_DATA;
typedef struc {
    int     a;
    int     b;
} MyStruc;
@vardata		MyStruc		MY_SECOND_VAR_DATA;
@vardata		void		MY_THIRD_VAR_DATA;

See Also: @vardataAlias, @instance

@vardataAlias

@vardataAlias (<origName>) <newType> <newName>;

The @vardataAlias keyword allows you to set up variable data fields with varying amounts of extra data. That is, a single variable data field in the instance chunk could have two different sizes and two different names. The arguments of @vardataAlias are listed below:

origName
The name of the original variable data field defined with @vardata.
newType
The new type or structure associated with this variable data field. If no extra data is to be associated with this alias, then put the word void instead of a type.
newName
The new name of the variable data field that uses the new type.
/* defined in GenTriggerClass */
@vardata word ATTR_GEN_TRIGGER_ACTION_DATA;
/* A special GenTrigger that uses a different data
 * type is defined in the application: */
@object GenTriggerClass MyTrigger = {
    GTI_actionMsg = MSG_MY_APPS_MESSAGE;
    GTI_destination = process;
    @vardataAlias (ATTR_GEN_TRIGGER_ACTION_DATA)
            dword ATTR_MY_TRIGGER_SPECIAL_DATA;

See Also: @vardata, @instance

@visChildren

@send @visChildren::<msg>(<params>);

Any composite object in a visible object tree (therefore a subclass of VisCompClass ) can send a message that will be dispatched at once to all of its children. Note that any message sent with @visChildren as the destination must be dispatched with the @send keyword and therefore can have no return value.

@visMoniker

@instance @visMoniker <iname>;

The @visMoniker keyword follows @instance to create an instance data field for a visual moniker. The iname argument is the name of the instance data field.

@instance @visMoniker GI_visMoniker;

See Also: @instance, GenClass

@visParent

@send @visParent::<msg>(<params>);

Any object in a visible tree can use @visParent as the destination of an @call command. The message will be sent to the object's parent in the visible object tree. The remainder of the command is the same as a normal @call .

| Up: GEOS SDK TechDocs | Up


Parameters File Keywords

appobj

appobj	<name>

The appobj field indicates the name of the application object. All geodes with appl set under type (see above) must have an appobj entry. The name argument should be the name of the object of GenApplicationClass specified in the application's .goc file.

class

class	<name>

The class field specifies the name of the object class to be bound to the geode's process thread. This field has significance only if process is specified in the geode's type field (see below). This should be the same as the ProcessClass object designated in the .goc file (see the Hello World sample for an example of this connection). Note that this class binding will only be for the geode's first (primary) thread.

driver

driver	<name> [noload]

This field specifies another driver that is used by this geode. The noload flag indicates that the used driver does not need to be loaded when the geode is first launched. Most applications and libraries will not use exported routines from drivers, so few geodes will use this field. (Notable exceptions are those geodes that access serial and parallel ports--those geodes will include the serial or parallel driver.)

else

ifdef <VARIABLE>
        <glue statements>
else
        <other glue statements>
endif

The else statement is part of Glue's conditional linking support. If the variable named in the ifdef statement is false, then any commands between an else and an endif will be used when linking.

endif

ifdef <VARIABLE>
        <glue statements>
else
        <other glue statements>
endif

The endif statement is part of Glue's conditional linking support. The endif keyword signals the end of the conditional commands which were begun with a previous ifdef command.

entry

entry	<name>

This field is used by library geodes. The name argument is the name of the library routine to be called by the kernel when the library is loaded or unloaded and when a program using the library is loaded or unloaded.

exempt

exempt	<library-name>

If you wish to exempt a certain library from Glue's platform checking, call it out with the exempt keyword. Glue will not complain if you then use parts of the library not normally available with platforms named in your platform statement.

export

export	<name> [as <name2>]

This field identifies routines usable by geodes other than the one being compiled; these routines are "exported" for use by other programs. Both forms create entry point symbols for the routines. The first name argument must be the actual name of the routine. If the second, optional, name2 argument is included, then other programs will call that routine using the second name rather than the original. This allows a routine to have a different global name than that used by its creator geode.

This field is also used to export classes defined in a .goc or .goh file. See the Benoit sample application for an example.

heapspace

heapspace <space>

Specify the memory requirements for the geode--how much space it requires on the heap. This parameter is required for geodes to run efficiently on certain platforms, such as the Casio Z-7000. To find out what number to enter in this field, run your geode on the device in question with Swat attached. Use the "heapspace" Swat command to find out how much memory your geode requires. For more information about the "heapspace" Swat command, see its entry in the Swat commands reference section of the Tools manual.

The space may be specified in paragraphs, or in k of memory:

heapspace 250
heapspace 4k

ifdef

ifdef <VARIABLE>
        <glue statements>
endif

ifdef <VARIABLE>
        <glue statements>
else
        <other glue statements>
endif

The ifdef keyword is used to implement conditional linking. If the named VARIABLE is defined (via the -D command line argument to Glue--see the Using Tools chapter to find out how), then the commands between the ifdef and the next else or endif command will be used when linking. Otherwise, they will be ignored. If the VARIABLE is not defined, then any commands appearing between the optional else keyword and the endif keyword will be used.

It is possible to nest these statements:

ifdef FOO
    ifdef BAR
        library foobar
    else
        library fooonly
    endif
else
    ifdef BAR
        library baronly
    else
        library nada
        endif
endif

incminor

incminor [<name>]

The incminor directive is used at the end of a library's .gp file before new routines are added (after a release of the library has already been made). After this release, new export and publish directives will be put after this incminor directive. The incminor directive causes two things: First, the geode's minor protocol number gets incremented by one. Second, any geode that uses your library will depend only on the higher minor protocol number if it actually uses one or more of the entry points exported after the incminor directive.

Any number of incminor directives may be used in a given .gp file. The major and the base minor numbers still come from a .rev file, if one exists.

The name argument is optional; it may be used in conjunction with the protominor compiler directive. Glue will know that the structures marked with the protominor label should be associated with the revision represented by the incminor directive.

library

library	<name> [noload]

This field specifies another library that is used by this geode. The noload flag indicates that the used library does not need to be loaded when the geode is first launched (though symbolic information will be loaded in any case). Note that every geode must have the line

library geos

included in the .gp file. Most will also have the following line:

library ui

Any number of used libraries may be specified.

load

load	<name> ["<class>"] as [<name2>] [<align>] [<combine>]\
 ["<class2>"]

The load field is used when you want to alter the way a segment is linked for your geode. This is especially useful, for example, when integrating another company's runtime routines into your application or library; their segments may correspond to specifications other than yours.

Every segment read in has a given name, class, alignment, and combination type. These are described below (the load parameters appear after):

name
This is the actual name of the segment being loaded in. Segments with the same name are treated as one continuous segment.
class
Segments with the same class name are always loaded together into memory regardless of their order in the geode's source code. Class names in the load directive must always be enclosed in quotation marks.
align
This specifies the alignment type of the segment--on what type of address the segment can start. Possible alignment settings are byte, word, double word, paragraph, and page.
combine
Segments with the same name may appear in different code modules. The combine parameter specifies how these segments are to be combined when loaded. The combine type may be one of the following (see your assembly reference manual for more information): COMMON, PRIVATE, PUBLIC, STACK, or RESOURCE.

The parameters for load are listed below. Only the first is necessary, to inform Glue which segment is to undergo the alterations. For an example of using the load statement, see below.

name
This represents the actual original name of the segment. It is a necessary parameter so Glue knows which segment's linkage is to be altered.
class
This is the original class name of the segment. It must be enclosed in quotation marks if given. If you do not need to change the class, this parameter is unnecessary.
name2
This is the new name of the segment, if any.
align
This specifies the new align type of the segment, if any.
combine
This specifies the new combine type of the segment, if any.
class2
This specifies a new class name for the segment, if any is required. If you do not need to change the class, this parameter is unnecessary. The new class must be in quotation marks.

Examples:

load _NAME_ "CODE" as CODE word public
load _NAME_ "CODE" as DATASEG para common "DATA"

longname

longname "<string>"

The longname field designates a 32-character name for the geode. This name will be displayed with the geode's icon by GeoManager; all geodes should be given a long name.

name

name	<pname>.<ext>

The name field in the parameters file gives the geode a permanent name which will be used by both the Glue linker and the Swat debugger. Every geode must have a permanent name. Note that the pname argument must be no more than eight characters, and the ext argument must be no more than four. Additionally, the ext argument may not be "appl," as that is reserved.

When Glue is linking an error-checking geode, it drops the fourth character of ext and adds "ec" to the end of pname .

nosort

nosort

This keyword should appear before the list of resources. Normally glue will sort the geode's resources to optimize their arrangement. This keyword turns off that sorting. If you will generate .GYM (generic symbol) files for your geode, you should use the nosort option, as it will be important that all versions of your geode order their resources in the same way. If you won't generate .GYM files, you probably don't want to use this option.

platform

platform <name>

The platform directive specifies that the Geode is compatible with the named system. This gives a sign of how backwards-compatible the application is. If multiple platforms are specified, Glue will make sure that the major protocol numbers for each of the libraries it finds within the platforms match. Having done that, it will use the smallest minor protocol number it can find for each library to ensure compatibility across all platforms.

If a reference is ever made to an entry point in a library that would cause the executable to depend upon a later version of the library than specified in the platform file, glue will complain. For example, if the specified platoform used GrObj version 534.1 and glue found a reference to an entry point that didn't exist until GrObj 534.3 (ie., an entry point exported following 3 'incminor's in grobj.gp), glue will spit out an error message like:

error: file "somegeode.gp", line 59: Usage of NewGrObjRoutine requires grobj minor protocol 3, but platform files only allow minor protocol 1

If the new routine happens to be a "published" routine, glue will copy it into the geode in an effort to avoid the error.

Common platforms include:

zoomer
Casio Z-7000, Tandy Z-PDA
bullet
Sharp PT-9000
geos20
GEOS 2.0 Desktop environment

For a list of all predefined platforms, go to the the Include directory of your SDK installation and list all *.PLT files.

publish

publish <name>

Normally, if a geode is required to run (via platform specifications) with a version of a library that doesn't contain one of the entry points required by the geode, glue will notify the user of the inconsistency, and the link will fail. However, if that entry point happens to be a published routine, glue will actually copy the routine into the geode and switch the call over to the newly copied routine to remove the dependency on the library routine. Glue does this by copying any routines marked "publish" in a library's .gp file into the .ldf file, then copying them out into whatever other geodes needs when those geodes are linked. Routines are marked "publish" by replacing the word "export" with the word "publish" in the .gp file, like so:

publish PublishedRoutine

The published routines appear in .ldf files in individual segments named after the routine (e.g. _PUBLISHED_PublishedRoutine), each containing a routine, also named after the published routine (e.g., _PUBLISHED__PUBLISHED_PublishedRoutine) You'll know one of these routines has been linked into your geode by examining the resource summary output by glue:

Resource 					Size # Relocs
-------------------------------------------------
CoreBlock 					0 	0
dgroup 					240 	8
_PUBLISHED_GROBJCALCCORNERS 					53 	1
_PUBLISHED_GROBJBODYPROCESSALLGR 					94 	2
TEST2_E 					478 	27
INTERFACE 					652 	1
CHANGETEXTDIALOG 					232 	1
APPRESOURCE 					416 	1

resource

resource <name> (read-only|preload|discardable|fixed|conforming|shared|\
 code|data|lmem|discard-only|swap-only|ui-object|object|\
 no-swap|no-discard)+

The resource

field indicates to Glue that the geode uses the named resource. Not all resources used by a geode must be declared here, however. (Resources are described in more detail in the GEOS Programming chapter.) Resources must be designated with the proper attributes, all of which are listed below:

(none)
If no attribute is specified, the resource named becomes a private data resource for the geode.
read-only
The resource block may not be modified by the program.
preload
The resource block should be loaded when the geode is first launched.
discardable
The resource block may be discarded from memory if necessary.
fixed
The resource block should reside in fixed memory.
conforming
The resource block, if containing code, may be called from a lower privilege level. If containing data, it may be accessed from a lower privilege level. (This applies only in protected mode and is not currently implemented.)
shared
The resource block may be used by other geodes. (Note: It is an error to specify code and shared without read-only .)
code
The resource block contains executable code.
data
The resource block contains data only. If a data resource is designated read-only and not fixed, it is assumed to be discardable.
lmem
The resource block consists of a local memory heap. This implies the attribute data (above), though not the condition pertaining to being discardable.
discard-only
The resource block should not be swapped but may be discarded. This is useful for initialization code.
swap-only
The resource block should not be discarded but may be swapped.
ui-object
The resource block contains objects to be run by the UI. This implies lmem , shared , and no-discard . All blocks for a geode designated ui-object will be run in a UI thread created specifically for the geode's UI objects.
object
The resource block contains objects that are to be run by the application's process thread rather than by the UI. This implies lmem and no-discard .
no-swap
The resource block will not be swapable.
no-discard
The resource block will not be discardable.

Because most resources are code resources, standard code does not have to be declared in the parameters file. Code resources default to code , read-only , and shared . However, if the resource is named in the .gp file, the default is overridden in favor of the settings presented. This fact is useful primarily when programming in assembly--in C, code resources are not declared explicitly.

The Hello World sample application uses only standard code resources (undeclared) and UI resources (designated ui-object ). Some other examples are listed below:

  • Shared data
resource <name> data shared
  • Initialization code
resource <nm> code shared read-only preload no-swap
  • Common code used by several geodes (this is the default)
resource <name> code shared read-only
  • Self-modifying code (strongly discouraged)
resource <name> code

stack

stack	<number>

The stack field designates the size of the application's stack in bytes. The default stack size is 2000 bytes. This field is not necessary for geodes unless they require a different size stack (the Hello World sample uses a slightly smaller stack size for example only). The stack field is valid only for geodes with a process aspect.

tokenchars

tokenchars "<string>"

This is one of two fields that identifies a unique token in GeoManager's token database file (see tokenid , below). The tokenchars field must be a string of four characters that identifies the geode's token. Note that these characters also appear in the geode file's extended attributes.

tokenid

tokenid	<number>

This is the other of two fields that identifies a unique token in GeoManager's token database file (see tokenchars , above). It must be a number corresponding to the programmer's manufacturer ID number. Note that this number also appears in the geode file's extended attributes.

type

type	(process|driver|appl|library)+ [single] [system] [uses-coproc]\
[needs-coproc] [has-gcm] [c-api]

The type field in the parameters file designates certain characteristics of the geode being compiled. These attributes correspond to the GeodeAttrs type and determine how the Glue linker will put the geode together. The attributes are as follows:

process
This attribute indicates the geode has its own thread. Applications should always have process specified in the type field.
driver
This attribute indicates the geode has a driver aspect.
appl
This attribute indicates the geode has an application aspect.
library
This attribute indicates the geode has a library aspect.
single
This geode may only have one copy running at a time. Some applications may allow multiple copies to be running at once; they should not specify single as a type attribute.
system
This attribute is set for drivers that must be exited specially and must always be exited. For example, a swap driver has special exit conditions that must always be met and is therefore a system driver.
uses-coproc
This attribute is set if the geode will make use of a math coprocessor if one is available. Note that if the geode with this attribute set is a library, all applications that use the library will inherit the property. This attribute is used to indicate that the coprocessor's state must be saved during a context switch.
needs-coproc
This attribute indicates that the geode must have a math coprocessor to run. (This implies uses-coproc , above).
has-gcm
This attribute indicates that the application being compiled has a GCM (appliance) version. This information is used by Welcome to locate all GCM applications.
c-api
This attribute indicates the library entry points are written in C so the kernel must call them with C calling conventions.

usernotes

usernotes "<string>"

This field specifies text to be put in the .geo file's usernotes field. The text must be within quotation marks and can be up to 100 characters long. It must contain no line breaks. This can be useful for containing copyright notices in the executable files. The user can read the text in the usernotes by using GeoManager's File/Get Info command.

| Up: GEOS SDK TechDocs | Up


Nokia 9000i/9110 Communicator

GEOS SDK Documentation: Nokia 9000/9000i/9110 Communicator

| Up: GEOS SDK TechDocs

The following documents contain information specific to the Nokia 9000/9000i Communicator device.

Tutorial
Shows the step-by-step creation of a simple Nokia 9000i Communicator application.
Nokia 9000/9000i/9110 Communicator Fact Sheet
One-stop reference guide to the the Nokia 9000/9000i/9110 Communicator.
Versions
Detecting the software version running on the user's device at run time, and some of the differences between versions.
Communicator UI
Nokia 9000i/9110 Communicator-specific object classes useful for creating application UI.
Distributing Software
How to package up your finished Nokia 9000i Communicator application to make it easy for users to load it onto their devices.
Memory Levels
Routines which detect and warn of low memory levels.
SMS
Using the Short Message Service.
Modem Connections
Using the phone as a virtual modem.
Contacts
Accessing the user's contact information--phone numbers, addresses, names, and so on.
Viewer-Compatible Documents
Creating a new document type that applications such as Notes can open.
Calendar API
How to insert appointments and events to the user's calendar.
Memory Card
How to use the N9110 memory card.
| Up: GEOS SDK TechDocs


Nokia 9000/9000i/9110 Communicator Fact Sheet

  • Hardware
    • Device Dimensions
    • Processor
    • Memory
    • Memory Cards
    • Screen Display
    • Keyboard
    • Communication
    • Battery Life
  • Text
  • Icons
  • Mapping Colors to Grayscale
    • Icons
    • Other Graphics
  • Disk Space Warnings
  • UI Conventions: Triggers
    • Trigger Usage
    • Menu Trigger
  • Zoom Levels
  • Emulator (U.S.) Keyboard Mapping
  • Sounds
  • The Installer
  • To Clear RAM
  • Version Information
  • Lid-Closing Event
  • Debugging on an Actual Communicator

Hardware

The device dimensions are: 9000/9000i Communicator
  • 17 cm long
  • 6 cm wide
  • 4 cm thick
  • 400 grams
9110 Communicator
  • 15.8 cm long
  • 5.6 cm wide
  • 2.7 cm thick (218 cc)
  • 249 grams

Processor

  • The phone functions are based on a Nokia 2110i.
  • 9000/9000i Communicators run on a 24 MHz 80386 chip; the 9110 Communicator uses an AMD 486 processor.

Memory

The Nokia 9000i Communicator has 8 megabytes of Flash RAM in it. It is partitioned as follows:

4 MB for "ROM" for the OS and applications
2 MB for application execution and heap space
2 MB for user data storage (essentially a RAM disk)
Memory Cards

The Nokia 9110 Communicator has a memory card slot. To insert the postage-stamp size memory card, you must turn off the device, remove the battery, and insert the card behind the battery. A memory card can provide up to 4 MB of additional Flash RAM or ROM, but future cards could provide even more memory. Since memory cards use a 32 bit address, they have a theoretical 4 GB upper limit.

Screen display

The display is a grayscale LCD screen 640x200 pixels, measuring 115 by 36 millimeters. The screen is neither touch-sensitive nor pen operable. The left status pane is 80 pixels wide and the command buttons at the right have a variable width, so an application's usable screen dimensions will be less than or equal to 560x200 pixels. For instance, the web browser has 540x200 pixels of usable display for web pages.

Nokia 9000i Communicators support 4 levels of gray, and the newer Nokia 9110 Communicators support 4-bit color (displays 16 levels of gray). Even though the 4-bit color devices default to 4 levels of gray, applications may change modes on-the-fly.

Keyboard

The keyboard is a standard QWERTY keyboard. English, French German and Scandinavian versions are currently available.

Communication

  • Cellular (voice and data)
    • Communication is through the GSM digital network, which can transmit cellular voice, data (and fax) and SMS alpha-numeric pages.
      • GSM's Short Message Service (SMS) allows mobile devices to send short, 160 character messages as a single packet of information. Nokia 9000i Communicators can send and receive these messages, and route them to specified target applications.
    • E-mail conforms to the SMTP and business card standards.
    • Internet protocols: TCP/IP and PPP.
    • The maximum baud rate for data calls is 9600 baud (14,400 baud on 9110 Communicators).
  • The Nokia 9000i supports two COM ports:
    • COM1 is the serial port
    • COM3 is the infrared port
    • (COM2 used internally so it is not available to applications.)
  • Infrared communication with outside devices via IrDA
    • The maximum baud rate for IrDA is 57,600 baud.
  • Serial line communication via a RS-232 compatible connection.
    • The maximum baud rate for the serial line is 19,200 baud.
  • NServer and device debugging at 19,200 baud

Battery Life

The Nokia 9110 Communicator uses a 1100 mAh Li-ion Battery that provides 3-6 hours of talk/data/fax, 60-200 hours of standby, up to 400 hours of standby when the phone is off, and takes 2 hours to charge.

Text

The following is a list of all the available fonts, their sizes, styles, and comments.

Font: | Available Size: | Styles: | Comments:
FID_RESPONDER | 14 | Bold | --
 | 16 | Plain, Bold | --
 | 17 | Plain | --
 | 18 | Plain, Bold, Italic, Bold-Italic | FID_RESPONDER 18 is the default font
 | 20 | Bold, Bold-Italic | --
FID_TERMINAL | 8 | Plain | Tiny, 7 pixel wide font need to display 80 characters per line, for Telnet and the like
 | 16 | Plain | Also 7 pixels wide, but vertically stretched for legibility; you can only display 12 lines of text on the screen using this font.
FID_TERMINAL_GRAPHIC | 8 | Plain | 7 pixel wide font need to display 80 characters per line. Has VT100 graphics above 80h..
 | 16 | Plain | 7 pixels wide, but vertically stretched for legibility; you can only display 12 lines of text in this font. Has VT100 graphics above 80h.
FID_DTC_URW_SANS | Any Size | -- | Outline font.
FID_DTC_URW_MONO | Any Size | -- | Outline font.
FID_DTC_URW_ROMAN | Any Size | -- | Outline font.
FID_BISON | -- | -- | This font has only one character, '*'. This is used for passwords.

Icons

There are two types of icons: Status Pane Icons and File List Icons.

When creating a status pane icon using the Icon Editor, use the following settings:

Width: 64
Height: 50
Color Scheme: 16-color
Apsect Ratio: VGA
Format Type: Icon
Display Size: Standard

When creating a file list icon using the Icon Editor, use the following settings:

Width: 25
Height: 18
Color Scheme: 16-color
Apsect Ratio: VGA
Format Type: Icon
Display Size: Tiny

Communicator Grays

Nokia 9000i Communicators support 4 levels of gray and the newer Nokia 9110 Communicators support 4-bit color, even though the display is grayscale (16 shades of gray). There are two different grayscale schemes used, one for visMonikers and icons, and the other for normal GEOS drawing routines.

Icons can make use of 4 levels of grey. When drawing an icon, use the following mapping of colors to create the correct gray level in the icon: (if other color values are used, they will not show up on 9000i devices).

GEOS Color | Icon Level of Gray
C_WHITE | White
C_LIGHT_VIOLET | Light Gray
C_GREEN | Dark Gray
C_BLACK | Black

Other graphics can make use of the 16 GEOS enumerated color types to choose the appropriate gray. The device will correctly map the color to the correct level of gray, depending on number of screen grays available. The following table displays how different colors will map to gray on 4- and 16- level grayscale modes.

GEOS Color | 4 Levels of Gray | 16 Levels of Gray
C_BLACK, C_DARK_GRAY | Black | Black
C_BLUE, C_LIGHT_BLUE |  | Dark-dark gray
C_GREEN, C_LIGHT_GREEN | Dark gray | Mid-dark gray
C_CYAN, C_LIGHT_CYAN |  | Light-dark gray
C_RED, C_LIGHT_RED | Light gray | Dark-light gray
C_VIOLET, C_LIGHT_VIOLET |  | Mid-light gray
C_YELLOW, C_BROWN | White | Light-light gray
C_WHITE, C_LIGHT_GRAY |  | White

Disk Space Warnings

Often, it is useful to get a qualitative measure of the available free disk space to check whether the disk is dangerously full. The disk warning levels are:

The Warning Level is less than 50K.
The Critical Level is less than 25K

UI Conventions: Triggers

Trigger Usage

To maintain a consistent look-and-feel for all Nokia 9000i applications, it is important that developers follow the same set of UI conventions. The most important navigation tool on the Nokia 9000i are the four triggers at the right side (see below).

"Call", "Voice mailbox" and "Recent calls" are this application's triggers. "Call" is in slot 0, "Voice mailbox" is in slot 2, etc. This particular application does not use slot 3.

The application specifies what these triggers are and which slot (0, 1, 2, or 3) each of these triggers belongs in. Newer versions of the Communicator (software verions 4.8 or later) support a fifth trigger, the menu trigger (see below). In general, applications should use:

HINT_SEEK_SLOT = 0
The main action (such as Open or Send). Affirmative responses (such as OK).

HINT_SEEK_SLOT = 1
Secondary functions or Main function if slot 0 is taken.

HINT_SEEK_SLOT = 2:
Settings and similar buttons that bring up options. Apply trigger in dialogs.

HINT_SEEK_SLOT = 3:
Negative responses such as Cancel, Close, or Dismiss. Back triggers. (Back is always in slot 3. This is the trigger with ATTR_TLT_IS_BACK_TRIGGER set.) "OK" if it is for dismissing the dialog box or if there are no other buttons. Settings can go here is slot 2 is already taken.

Applications launched from "Extras" must have a close button that calls MSG_FSA_RETURN_TO_LAUNCHER

You can use up to 6 triggers by setting up two panes of triggers. If this is done, than the fourth trigger of each group must be either "More" or "Back" text monikers with the associated right and left pointing icons.

Menu Trigger

Software versions 4.8 or later of the Communicator allow for a fifth "Menu" trigger that behaves just like a regular trigger except that it is invisible and does not take up a slot at the right side of the screen. It is activated whenever the user pushes the "Menu" button present on newer versions of the Communicator.

A normal application, with a certain set of visible triggers...

...may preform a particular action when the "Menu" trigger is activated, even though this trigger does not take up a visible slot at the right side of the screen.

If the user pushes the "Menu" button and there is no trigger to catch this action, then a pop-up note informs the user that there is no menu specified for this application.

To create a menu trigger, follow the same steps for creating a normal trigger but do not designate a HINT_SEEK_SLOT = {0,...,3} and set CMI_topText = CMT_MENU;. This automatically distinguishes it as a menu trigger. For more information, refer to ComplexMoniker Usage.

Ctrl-F2 corresponds to the menu button on the emulator.

Zoom Levels

On versions 4.8.8 or later of the 9000i Communicator, the user can press an off-screen "Zoom" button to toggle between three text size settings -- "Zoom" levels -- and adjust the text size for comfortable reading.

Normal zoom level.

Medium zoom.

High zoom. Note that only some parts of the UI are enlarged as the user chooses a higher zoom level. It would be impossible to fit larger triggers into the four slots, or increase the size of the clock.

On the emulator, the "Zoom" button is mapped to Ctrl-F1.

Use HINT_MAXIMUM_ZOOM_LEVEL to determine the maximum size that a UI component may be zoomed. HINT_MAXIMUM_ZOOM_LEVEL = ZL_ZOOM_1; implies that the component will not be zoomed at all, whereas HINT_MAXIMUM_ZOOM_LEVEL = ZL_ZOOM_3; implies that a UI component can be zoomed at the normal, medium, or maximum zoom levels.

The higher the zoom level, the larger the font size, and the fewer the number of characters that will fit into a fixed pixel length field. Thus, if you use a CreateVisMoniker...() routine, you should use the routines FoomZoomGetLevel() and FoamConvertToZoomedSize() to take the zoom level into consideration and create a VisMoniker of appropriate size. (Refer to Zoom Level UI for more details.)

Nokia 9000i Emulator Keyboard Mapping

This only applies to US QWERTY keyboards

F1 - Button 1
F2 - Button 2
F3 - Button 3
F4 - Button 4

F5 - launch Phone application
F6 - launch Fax application
F7 - launch SMS application
F8 - launch Internet Services application
F9 - launch Contact manager application
F10 - launch Notebook application
F11 - launch Calendar application
F12 - launch System application
Ctrl F12 - launch Extras application

Left Alt - Chrset
Insert - Help
Page Up - Scroll up
Page Down - Scroll down
Pause - Shutdown demo

Ctrl F1 - Zoom button (software versions 4.8 or later)
Ctrl F2 - Menu button (software versions 4.8 or later)
Ctrl F3 - Detach application
Ctrl F4 - Close application

Sound

Communicator applications may play StandardSoundType sounds through the PDA-side speaker. If you write applications that use sounds, you will have to debug sounds on the actual device since the emulator does not provide sound support.

The Installer

The Nokia 9000i Communicator SDK comes with an installer application called NServer.exe. It uses an installation script that describes your application's name, version, and related files that will need to be transferred.

To Clear all RAM

To reset the Nokia 9000i Communicator and effectively erase every bit of data on the phone (except, of course, the factory settings and data) you must pull out the main battery, put it back in, then hold down the keys Shift-Esc-f. You will be prompted to format the RAM of the device and you can cancel here if you don't really want to erase the data.

Once the phone has been reset you must re-enter all of the user data again, including your home country, name, and address.

Version Information

You can get version information from the INI file on the Nokia 9000i Communicator. The information is stored as a string in [UI]swSerialNumber. The string is 27 characters long. This same string can be seen on the last line of About this product. The whole string looks like this:

      000000/00/2a0030808044001/0.

The interesting part is: 2a0030808044001. The three first characters identify hardware version (here 2.10), six following characters identify software version (here 03.08.08), three following characters identify the country code (44 = Great Britain) and the last three characters identify the keyboard type (here 001 = English).

Here is sample code that shows how to extract the version information:

                 @start StringRes;
                     @chunk TCHAR UICategory[] = "ui";
                     @chunk TCHAR SWSerialNumber[] = "swSerialNumber";
                 @end StringRes;

                 ...

                 @method TestProcessClass, MSG_TEST_PROCESS_GET_VERSION
                 {
                     TCHAR   version[28] = "";
                     TCHAR * category;
                     TCHAR * key;
                     word    value;

                     MemLock( OptrToHandle( @UICategory ) );
                     category = (TCHAR *) LMemDeref( @UICategory );
                     key      = (TCHAR *) LMemDeref( @SWSerialNumber );
                     InitFileReadStringBuffer( category,
                          key,
                          version,
                          IFRF_SIZE | 28,
                          &value );

                     /*
                      * Extract the information you want from version string.
                      */
                     MemUnlock( OptrToHandle( @UICategory ) );
                 }

Lid-closing Event

After the Nokia 9000i Communicator's lid is closed, the Communicator waits for 30 seconds and then turns off the CPU. Of course, the CPU will not turn off until all applications are idle.

To receive notification that the lid is closed, an application should register on the GCN list GCNSLT_RESPONDER_NOTIFICATIONS. It should watch for MSG_META_NOTIFY with notificationType == GWNT_RESPONDER_NOTIFICATION, manufID == MANUFACTURER_ID_GEOWORKS and data == RNT_LID_CLOSED. (ResponderNotificationType is defined in N9000v20\CInclude\Internal\respondr.goh)

Debugging an actual Nokia 9000i Communicator

To debug on an actual Nokia 9000i Communicator, you need to attach your host PC to the device's 6-pin serial port. Then run the GEOS Setup program and change the "Communication Mode" to Remote and set the "Comm Speed" to 38400. Which "COMM Port" you use depends on which port on your PC you have used to connect to the phone. Remove the battery so the phone shuts down. Then replace the battery and press:

SHIFT-D-O (that is 'oh' not zero) to boot and wait for swat to attach.
SHIFT-D-B to boot with swat stub loaded, but doesn't wait for swat to attach.

Note that IR connections and PC connectivity won't work while the Nokia 9000i Communicator is in debug mode. (IR shares a UART with the swat port, so you trying to open the port will lock the device.)

Also note that debugging on the device is limited because you cannot set breakpoints in any of the system routines (since they are in ROM). Mostly you can check backtraces and set breakpoints in your own application.


Calendar API

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Overview

The Nokia 9000i Communicator comes with a calendar application for scheduling appointments and keeping a to-do list. You can add appointments and to-do items with the API defined in calendar.goh; you can also retrieve, modify, and delete events that you have added.

You can search for the existence of any event within a particular time period (including events you did not add yourself); however, Calendar API is not intended for use in retrieving, modifying, or deleting pre-existing events in the user's calendar database.

Calendar API uses IACP (Inter-Application Communication Protocol); your application sends a message to the Calendar application object, which services your request and then returns a status code (and other requested information) with a callback message. Before reading this section, you may wish to review the material on IACP or look at code in the CalApi.goc sample application.

On the Nokia 9000i Communicator, only devices manufactured after February 1997 support this API; your application should check the Calendar application version. Also, only one-time events may be manipulated; this API does not support repeating events.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Overview


Overview

Calendar API: 1 Overview

| Up: GEOS SDK TechDocs | Up | Prev: Introduction | Next: 2 Examples

Functionality

MSG_CALENDAR_ADD_EVENT
MSG_CALENDAR_GET_EVENT_BY_ID
MSG_CALENDAR_MODIFY_EVENT_BY_ID
MSG_CALENDAR_DELETE_EVENT_BY_ID
MSG_CALENDAR_CHECK_IF_EVENT_EXISTS

These five messages correspond to the operations you can perform on the user's calendar database: add an event, get an event you added, modify an event you added, delete an event you added, and search for the existence of any event within a specified time interval.

Because these messages are sent via IACP to the Calendar application, they cannot directly return status values. In each case, you specify a callback message and an object to receive it; status values and requested information (if any) are processed by your callback method.

Callback Prototypes

CALENDAR_ADD_EVENT_CALLBACK_MSG
Prototype of the callback message for MSG_CALENDAR_ADD_EVENT; passes status value and the assigned event ID.

CALENDAR_GET_EVENT_CALLBACK_MSG
Prototype of the callback message for MSG_CALENDAR_GET_EVENT_BY_ID; passes status value and handle to a CalendarReturnedEventStruct.

CALENDAR_ACCESS_EVENT_COMMON_CALLBACK_MSG
Prototype of the callback message for MSG_CALENDAR_MODIFY_EVENT_BY_ID, MSG_CALENDAR_DELETE_EVENT_BY_ID, and MSG_CALENDAR_CHECK_IF_EVENT_EXISTS; passes status value.

Event Types

Ordinary
Ordinary events have a start date, start time, end date, end time, an optional alarm at or before the start time, and a text description.

Only the start date is mandatory; if no start time (CAL_NO_TIME) is specified, the event is listed as a daily reminder (before the time-ordered list for its day). If only a start date and time are specified (the end date and time are CAL_NO_DATE and CAL_NO_TIME), the event is listed at the start time without an associated interval. If an end time is also specified, the event is shown spanning a time interval on a single day; if both an end date and time are specified, the event is shown spanning the entire interval from the start date and time until the end date and time.

If you specify a start time and an end date, you must also specify an end time; if the end time (or date and time) is specified, it must be later than the start time.

If you specify a start date and no start time (a daily reminder), do not specify an end time; specify an end date if you wish to have a multi-day daily reminder. Do not use the CEPS_reserveWholeDay parameter to create a multi-day daily reminder; if you omit the start and end times, you will instead create a multi-day event (see below) with the default start and end times of 00:00 and 23:59.

Result of Specifying Start and End Parameters
Start Param Specified | End Param Specified | Result
Date | Time | Date | Time
Yes | No | No | No | Daily reminder, listed at beginning of day.
Yes | No | Yes | No | Multi-day daily reminder, listed at the beginning of each day.
YesNo*YesNot defined. Typically creates a daily reminder.
Yes | Yes | No | No | Event listed (at the start time) with no interval.
Yes | Yes | No | Yes | Event spanning a time interval on one day.
Yes | Yes | Yes | Yes | Event (possibly) spanning multiple days.
Yes | Yes | Yes | No | Not allowed.
* = Doesn't matter
"No" = CAL_NO_DATE or CAL_NO_TIME was specified

Multi-day
Multi-day events are ordinary events that have a start and end time for each day in their date range, as opposed to being one continuous event spanning multiple days. The start and end time is the same for each day. Specify a multi-day event by including a non-zero value (the number of days) for CEPS_reserveWholeDay in CalendarEventParamStruct; also specify the start and end time (which will apply to each day) and the start day. The end day value is ignored when CEPS_reserveWholeDay is non-zero. If you omit the start or end time (using CAL_NO_TIME), it defaults to 00:00 (start time) or 23:59 (end time).

To-do List
To-do list events all appear in one list; they are not tied to any specific day. Each to-do event has a status (high priority, normal, or completed) and a text description. Specify a to-do list event by setting the start date to CAL_NO_DATE and the start time to the event's status.

Repeating
Repeating events automatically recur according to a specified pattern (daily, monthly, etc.). This API currently does not support repeating events; the error CEE_EVENT_NOT_SUPPORTED will be returned if you specify a non-zero CEPS_repeatInfo field when adding or modifying event, or if you attempt to get, modify, or delete a repeating event. Also, MSG_CALENDAR_CHECK_IF_EVENT_EXISTS will not check for repeating events in the specified range.

Alarms

The Calendar application displays a note and (optionally) sounds a tone when an event's alarm is due; this occurs at or before the event's start time. (Daily reminders cannot have an associated alarm.) You can set an alarm for an event by specifying a non-zero CalendarAlarmStruct field in the event's parameter structure, CalendarEventParamStruct. CalendarAlarmStruct is actually a WordFlag that encodes whether an alarm is set and how long before the event's start time it should sound. On the Nokia 9000i Communicator, this time must be specified in minutes.

Alarms use the RTCM (Real-Time Clock Manager) library, which allows them to activate when Calendar is not running or when the device is sleeping.

Checking the Calendar Version

Only versions of the Nokia 9000i Communicator manufactured after February 1997 have the calendar geode that supports this API. Your application can check the software version by looking for the INI file settting:
[Calendar]
calApi = True

See GEOS .INI Files for more information on INI files; also see this example.

Data Types

The following types are defined in calendar.goh:

structures:

CalendarEventParamStruct
CalendarReturnedEventStruct
CalendarEventRepeatInfo
CalendarEventRepeatDurationData
WordFlags:
CalendarAlarmStruct
CalendarEventRepeatLength
ByteEnums:
CalendarEventDescType
CalendarAlarmIntervalType
CalendarEventRepeatDuration
CalendarEventRepeatLengthUnitType
CalendarEventRepeatWeekday
CalendarEventRepeatIntervalType
CalendarEventRepeatInterval
enums:
CalendarEventError
CalendarToDoItemStatus

Internal Messages

The following messages defined in calendar.goh are intended for internal use only. Do not use them in your applications.
MSG_CALENDAR_REQUEST_SEARCH
MSG_CALENDAR_DELETE_EVENTS_BEFORE_DATE
MSG_CALENDAR_GET_NEXT_EVENT_ID
MSG_CALENDAR_SET_NEXT_EVENT_ID
| Up: GEOS SDK TechDocs | Up | Prev: Introduction | Next: 2 Examples


Reference

Calendar API: 3 Reference

| Up: GEOS SDK TechDocs | Up | Prev: 2 Examples | Next: Appendix: Calendar File Format

Messages

MSG_CALENDAR_ADD_EVENT
MSG_CALENDAR_GET_EVENT_BY_ID
MSG_CALENDAR_MODIFY_EVENT_BY_ID
MSG_CALENDAR_DELETE_EVENT_BY_ID
MSG_CALENDAR_CHECK_IF_EVENT_EXISTS

Prototypes

CALENDAR_ADD_EVENT_CALLBACK_MSG
CALENDAR_GET_EVENT_CALLBACK_MSG
CALENDAR_ACCESS_EVENT_COMMON_CALLBACK_MSG

Structures

CalendarEventParamStruct
CalendarReturnedEventStruct
CalendarEventRepeatInfo
CalendarEventRepeatDurationData

WordFlags

CalendarAlarmStruct
CalendarEventRepeatLength

ByteEnums

CalendarEventDescType
CalendarAlarmIntervalType
CalendarEventRepeatDuration
CalendarEventRepeatLengthUnitType
CalendarEventRepeatWeekday
CalendarEventRepeatIntervalType
CalendarEventRepeatInterval

enums

CalendarEventError
CalendarToDoItemStatus

MSG_CALENDAR_ADD_EVENT

void        MSG_CALENDAR_ADD_EVENT(
        CalendarEventParamStruct
                         *params,
        optr             completionOptr,
        Message          completionMsg);

Adds an event to the calendar database. Only ordinary, multi-day, and to-do list events are supported; repeating events are not.

Pass:

*params
Pointer to a CalendarEventParamStruct structure holding the event you are adding.
completionOptr
Optr to the object in your application to receive the callback reply message; specify zero (0) for no callback.
completionMsg
Name of your callback-handling message.

Return: Nothing. Error status and event ID (if successful) are passed to your callback method, if specified.

Include: calendar.goh

See Also: CALENDAR_ADD_EVENT_CALLBACK_MSG prototype.

MSG_CALENDAR_GET_EVENT_BY_ID

void        MSG_CALENDAR_GET_EVENT_BY_ID(
        dword            eventID,
        GeodeHandle      owner,
        optr             completionOptr,
        Message          completionMsg);

Gets an event in the calendar database. Only ordinary, multi-day, and to-do list events are supported; repeating events are not.

Pass:

eventID
Unique event ID specifying the event you are requesting. This ID was returned when you added the event with MSG_CALENDAR_ADD_EVENT.
owner
Owner assigned to the created memory block holding the event data. It should correspond to the thread that will lock and read the block.
completionOptr
Optr to the object in your application to receive the callback reply message.
completionMsg
Name of your callback-handling message.

Return: Nothing. Error status and data block handle are passed to your callback method.

Include: calendar.goh

See Also: CALENDAR_GET_EVENT_CALLBACK_MSG prototype.

MSG_CALENDAR_MODIFY_EVENT_BY_ID

void       MSG_CALENDAR_MODIFY_EVENT_BY_ID(
        CalendarEventParamStruct
                         *params,
        dword            eventID,
        optr             completionOptr,
        Message          completionMsg);

Modifies an event in the calendar database. Only ordinary, multi-day, and to-do list events are supported; repeating events are not.

Pass:

*params
Pointer to a CalendarEventParamStruct structure holding the modified event, which will completely replace the existing event.
eventID
Unique event ID specifying the event you are modifying. This ID was returned when you added the event with MSG_CALENDAR_ADD_EVENT.
completionOptr
Optr to the object in your application to receive the callback reply message; specify zero (0) for no callback.
completionMsg
Name of your callback-handling message.

Return: Nothing. Error status is passed to your callback method, if specified.

Include: calendar.goh

See Also: CALENDAR_ACCESS_EVENT_COMMON_CALLBACK_MSG prototype.

MSG_CALENDAR_DELETE_EVENT_BY_ID

void        MSG_CALENDAR_DELETE_EVENT_BY_ID(
        dword            eventID,
        optr             completionOptr,
        Message          completionMsg);

Deletes an event in the calendar database. Only ordinary, multi-day, and to-do list events are supported; repeating events are not.

Pass:

eventID
Unique event ID specifying the event you are deleting. This ID was returned when you added the event with MSG_CALENDAR_ADD_EVENT.
completionOptr
Optr to the object in your application to receive the callback reply message; specify zero (0) for no callback.
completionMsg
Name of your callback-handling message.

Return: Nothing. Error status is passed to your callback method, if specified.

Include: calendar.goh

See Also: CALENDAR_ACCESS_EVENT_COMMON_CALLBACK_MSG prototype.

MSG_CALENDAR_CHECK_IF_EVENT_EXISTS

void        MSG_CALENDAR_CHECK_IF_EVENT_EXISTS(
        FileDateAndTime  startDateTime,
        FileDateAndTime  endDateTime,
        optr             completionOptr,
        Message          completionMsg);

Searches the calendar database to see whether any events occur during a specified interval. Only ordinary, multi-day, and to-do list events are checked; repeating events are not.

Pass:

startDateTime
Start date and time of search interval.
endDateTime
End date and time of search interval.
completionOptr
Optr to the object in your application to receive the callback reply message.
completionMsg
Name of your callback-handling message.

Return: Nothing; the search result is passed to your callback method. The successful results are:

  • CEE_NORMAL indicates at least one event in the database overlaps your search range.
  • CEE_EVENT_NOT_FOUND indicates that no events in the database overlap your search range.

Include: calendar.goh

See Also: CALENDAR_ACCESS_EVENT_COMMON_CALLBACK_MSG prototype.

CALENDAR_ADD_EVENT_CALLBACK_MSG

void        CALENDAR_ADD_EVENT_CALLBACK_MSG(
        CalendarEventError
                         error,
        dword            eventID);

Template for your MSG_CALENDAR_ADD_EVENT callback. Use this prototype to declare, in the appropriate class definition, the method that will handle the callback from adding an event to the calendar database. For example:

@message (CALENDAR_ADD_EVENT_CALLBACK_MSG) MSG_MYAPP_PROCESS_ADD_EVENT_CALLBACK;

Pass:

error
The error code of your "add event" request; CEE_NORMAL if successful.
eventID
The unique identifier of this event, if it was successfully added.

Return: Nothing.

Include: calendar.goh

See Also: MSG_CALENDAR_ADD_EVENT

CALENDAR_GET_EVENT_CALLBACK_MSG

void        CALENDAR_GET_EVENT_CALLBACK_MSG(
        CalendarEventError
                         error,
        MemHandle        eventBlock);

Template for your MSG_CALENDAR_GET_EVENT_BY_ID callback. Use this prototype to declare, in the appropriate class definition, the method that will handle the callback from getting an event in the calendar database. For example:

@message (CALENDAR_GET_EVENT_CALLBACK_MSG) MSG_MYAPP_PROCESS_GET_EVENT_CALLBACK;

Pass:

error
The error code of your "get event" request; CEE_NORMAL if successful.
eventBlock
Unlocked global memory block containing the returned CalendarReturnedEventStruct .

Return: Nothing.

Include: calendar.goh

See Also: MSG_CALENDAR_GET_EVENT_BY_ID

CALENDAR_ACCESS_EVENT_COMMON_CALLBACK_MSG

void        CALENDAR_ACCESS_EVENT_COMMON_CALLBACK_MSG(
        CalendarEventError
                         error);

Template for your

  • MSG_CALENDAR_MODIFY_EVENT_BY_ID,
  • MSG_CALENDAR_DELETE_EVENT_BY_ID, and
  • MSG_CALENDAR_CHECK_IF_EVENT_EXISTS
callback functions. Use this prototype to declare, in the appropriate class definition, the methods that will handle the callbacks from these requests. For example:

@message (CALENDAR_ACCESS_EVENT_COMMON_CALLBACK_MSG)
                 MSG_MYAPP_PROCESS_EVENT_EXISTS_CALLBACK;

Pass:

error
The error code of your request. CEE_NORMAL indicates successful deletion or modification; for the MSG_CALENDAR_CHECK_IF_EVENT_EXISTS callback, CEE_NORMAL is returned if any event overlaps with the specified range, CEE_EVENT_NOT_FOUND otherwise.

Return: Nothing.

Include: calendar.goh

See Also: MSG_CALENDAR_MODIFY_EVENT_BY_ID, MSG_CALENDAR_DELETE_EVENT_BY_ID, MSG_CALENDAR_CHECK_IF_EVENT_EXISTS.

CalendarEventParamStruct

typedef struct {
    FileDateAndTime      CEPS_startDateTime;
    /*  Event start date and time */

    FileDateAndTime      CEPS_endDateTime;
    /*  Event end date and time */

    word                CEPS_reserveWholeDay;
    /*  Number of whole day to reserve the event. The max is
     *  CALENDAR_MAX_RESERVE_WHOLE_DAYS. 0 if not applicable.
     *
     *    If this field is non-zero, the end date specified in
     *    CEPS_endDateTime is ignored.
     *
     *    If CEPS_startDateTime does not have time specified, it is assumed
     *    the start time is 00:00.
     *
     *    If CEPS_endDateTime does not have time specified, it is assumed
     *    the end time is 23:59
     */

    CalendarAlarmStruct   CEPS_alarm;
    /*  Alarm associated with the event */

    CalendarEventDescType CEPS_eventType;
    /*  Type of data of the event */

    CalendarEventRepeatInfo *CEPS_repeatInfo;
    /* Repeat event info. 0 if the event is not repeating */

    word                CEPS_dataLength;
    /*  The number of bytes of the data in CEPS_data. If
     *  CEPS_eventType is CEDT_GEOS_TEXT, CEPS_dataLength does not include
     *  NULL.
     */

    char*               CEPS_data;
    /*  The event data. If the CEPS_eventType is CEDT_GEOS_TEXT, the
     *  text passed in from this field must be null-terminated.
     */
} CalendarEventParamStruct;
typedef enum {
    CTDIS_HIGH_PRIORITY = 0x101,
    CTDIS_NORMAL_PRIORITY,
    CTDIS_COMPLETED,
} CalendarToDoItemStatus;

This structure fully specifies a calendar event.

Most of the fields are self-explanatory. If you wish to add a to-do list item, specify CAL_NO_DATE for the start date and a CalendarToDoItemStatus enum in place of the start time. To-do items do not have start or end times, nor do they repeat or have alarms.

CEPS_reserveWholeDay is used to specify a single event that spans multiple days. This is a special case of ordinary events; instead of creating one long event, the start and end time you specify will apply to each day the event lasts. For example, you could use this to record a conference that runs from 10:00 to 15:00 for three consecutive days.

When specifying FileDateAndTime values, cast words and constants to type (dword) and specify literal long values (such as "19L") before shifting their bits.

True repeating events are not yet supported.

CalendarAlarmStruct

typedef WordFlags CalendarAlarmStruct;

/* CalendarAlarmIntervalType */
#define CAS_TYPE            (0x8000 | 0x4000)
#define CAS_TYPE_OFFSET     (14)

typedef ByteEnum CalendarAlarmIntervalType;
#define CAIT_MINUTES	(0x0)    /* Must use CAIT_MINUTES on the Communicator */
#define CAIT_HOURS	(0x1)
#define CAIT_DAYS	(0x2)

/*  set if there is alarm */
#define CAS_HAS_ALARM       (0x2000)

/*  data according to CAS_TYPE */
#define CAS_INTERVAL        (0x1fff)
#define CAS_INTERVAL_OFFSET (0)

This structure indicates whether an alarm is set, and how long before the event it should activate itself.

The CAS_HAS_ALARM bit indicates whether the alarm is set for the associated event. If so, the number masked by CAS_INTERVAL indicates when the alarm should sound. If the interval is zero, the alarm activates at the start time of the event; otherwise, it activates CAS_INTERVAL time units before the event. In the latter case, the time units are specified by CAS_TYPE, although on the Nokia 9000i Communicator only CAS_TYPE = CAIT_MINUTES is valid. If another CAS_TYPE is specified, the alarm will default to the maximum number of minutes.

For example, an alarm can be set to go off five minutes before the start of an event by specifying CAS_TYPE = CAIT_MINUTES and CAS_INTERVAL = 5.

CalendarEventDescType

typedef ByteEnum CalendarEventDescType;

#define CEDT_GEOS_TEXT  (0x0)           /* Description is a null-terminated,
                                         * single-byte text string in the
                                         * GEOS character set */
#define CEDT_UNICODE    (0x1)           /* Description is a null-terminated,
                                         * double-byte text string in the
                                         * Unicode character set. */
#define CEDT_INK        (0x2)           /* Description is digital ink. Number
                                         * of points can be determined from
                                         * the dataLength field. *

This enum indicates the format of the event description. Only CEDT_GEOS_TEXT is currently supported.

CalendarReturnedEventStruct

typedef struct {
    dword               CRES_eventID;
    /*  Unique event ID */

    FileDateAndTime      CRES_startDateTime;
    /*  Event start date and time */

    FileDateAndTime      CRES_endDateTime;
    /*  Event end date and time */

    word                CRES_reserveWholeDay;
    /*  Number of whole day to reserve the event. The max is
     *  CALENDAR_MAX_RESERVE_WHOLE_DAYS. 0 if not applicable.
     *
     *    If this field is non-zero, the end date specified in
     *    CRES_reserveWholeDay is ignored.
     *
     *    If CRES_startDateTime does not have time specified, it is assumed
     *    the start time is 00:00.
     *
     *    If CRES_endDateTime does not have time specified, it is assumed
     *    the end time is 23:59
     */

    CalendarAlarmStruct CRES_alarm;
    /*  Alarm associated with the event */

    CalendarEventDescType CRES_eventType;
    /*  Type of data of the event */

    word                CRES_repeatInfo;
    /* Offset in this block that points to CalendarEventRepeatInfo repeat
     * event info. 0 if the event is not repeating. */

    word                CRES_dataLength;
    /*  The number of bytes of the data in CRES_data. If
     *  CRES_eventType is CEDT_GEOS_TEXT, CRES_dataLength does not include
     *  NULL.
     */

/*    label     char            CRES_data;*/
    /*  The event data. If the CRES_eventType is CEDT_GEOS_TEXT, the
     *  text passed in from this field must be null-terminated.
     */
} CalendarReturnedEventStruct;

The event data (CRES_data) immediately follow this structure in the returned block. If CRES_eventType = CEDT_GEOS_TEXT, the text must be null-terminated.

CRES_repeatInfo is not currently supported.

CalendarEventError

typedef enum {
    CEE_NORMAL = 0x0,
    CEE_GENERAL_ERROR,
    CEE_INTERNAL_ERROR,
    CEE_NOT_ENOUGH_MEMORY,
    CEE_NOT_ENOUGH_DISKSPACE,
    CEE_EVENT_NOT_FOUND,
    CEE_EVENT_TEXT_TOO_LONG,
    CEE_INVALID_DATE,
    CEE_INVALID_TIME,
    CEE_INVALID_TODO_ITEM_STATUS,
    CEE_INVALID_RESERVE_WHOLE_DAY,
    CEE_INVALID_ALARM,
    CEE_INVALID_EVENT_TYPE,
    CEE_START_DATE_LATER_THAN_END_DATE,
    CEE_START_TIME_LATER_THAN_END_TIME,
    CEE_MISSING_END_TIME_WHEN_START_TIME_AND_END_DATE_ARE_SET,
    CEE_EVENT_NOT_SUPPORTED,
    CEE_INVALID_TIME_RANGE,             /*  The time range specified
                                         *  is invalid: end date/time
                                         *  is earlier than start date/time.*/
    CEE_ACCESS_DENIED                   /*  The requested event is in use and
                                         *  your access is denied. */
} CalendarEventError;

Error codes returned by Calendar API.

CalendarEventRepeatInfo

typedef struct {
    CalendarEventRepeatInterval CERI_interval;
    /*  how long between events? */

    CalendarEventRepeatDuration CERI_duration;
    /*  how long shall we keep this up? */

    CalendarEventRepeatDurationData CERI_durationData;
    /*  more details of duration */

    word        CERI_numExceptions;
    /*  number of exception dates */

/*  label       FileDate        CERI_exceptions;*/
    /*  array of dates on which event is *not* to take place */

} CalendarEventRepeatInfo;

Included for completeness; repeating events are not supported in calendar.goh API.

This structure specifies the repeat information in a repeating event. An array of exception dates, if any, follows at the end of this structure.

CalendarEventRepeatInterval

typedef WordFlags CalendarEventRepeatInterval;

/* which week */
#define CERI_WEEK         (0x8000 | 0x4000 | 0x2000)
#define CERI_WEEK_OFFSET  (13)

/*  number of units between events. */
#define CERI_COUNT        (0x1000 | 0x0800 | 0x0400 | 0x0200 | 0x0100 | 0x0080 | 0x0040)
#define CERI_COUNT_OFFSET (6)

/*  CalendarEventRepeatWeekday  */
#define CERI_DAY          (0x0020 | 0x0010 | 0x0008)
#define CERI_DAY_OFFSET   (3)

/*  How frequently the event should be repeated */
/* CalendarEventRepeatIntervalType */
#define CERI_TYPE         (0x0004 | 0x0002 | 0x0001)
#define CERI_TYPE_OFFSET  (0)

typedef ByteEnum CalendarEventRepeatWeekday;
#define CERW_SUNDAY     (0x0)
#define CERW_MONDAY     (0x1)
#define CERW_TUESDAY    (0x2)
#define CERW_WEDNESDAY  (0x3)
#define CERW_THURSDAY   (0x4)
#define CERW_FRIDAY     (0x5)
#define CERW_SATURDAY   (0x6)

typedef ByteEnum CalendarEventRepeatIntervalType;
#define CERIT_DAILY           (0x0)
#define CERIT_WEEKLY          (0x1)
#define CERIT_MONTHLY_WEEKDAY (0x2)
#define CERIT_MONTHLY_DATE    (0x3)
#define CERIT_YEARLY_WEEKDAY  (0x4)
#define CERIT_YEARLY_DATE     (0x5)

Included for completeness; repeating events are not supported in calendar.goh API.

This member specifies the interval between occurrences of the event. The meaning of CERI_WEEK, CERI_DAY, and CERI_COUNT depends on the type of interval for the repeating event:

DAILY:
CERI_WEEK = unused
CERI_DAY = unused
CERI_COUNT = number of days between repeats (usually 1)

WEEKLY:
CERI_WEEK = unused
CERI_DAY = which day of week to repeat
CERI_COUNT = number of weeks between repeats (usually 1)

BI-WEEKLY:
CERI_WEEK = unused
CERI_DAY = which day of week to repeat
CERI_COUNT = number of weeks between repeats (2 in this case)

MONTHLY_WEEKDAY (e.g., the first Thursday of each month):
CERI_WEEK = which week in month
CERI_DAY = which day of week
CERI_COUNT = number of months between repeats (usually 1)

MONTH_DATE (e.g., the 15th of each month):
         CERI_WEEK = unused
CERI_DAY = unused
CERI_COUNT = number of months between repeats (the date is
                 taken from the appointment info itself, not the repeat info)

YEARLY_WEEKDAY (e.g., the first Thursday of November):
         CERI_WEEK = which week in month
CERI_DAY = which day of week
CERI_COUNT = number of years between repeats (month is taken from
                 the appointment info itself, not the repeat info)

YEARLY_DATE (e.g., birthdays):
         CERI_WEEK = unused
CERI_DAY = unused
CERI_COUNT = number of years between repeats (month and day are
                 taken from the appointment info itself, not the repeat info)

CalendarEventRepeatDuration

typedef ByteEnum CalendarEventRepeatDuration;
#define CERD_FOREVER    (0x0)           /*  no additional data */
#define CERD_UNTIL      (0x1)           /*  Repeat end date */
#define CERD_LENGTH     (0x2)           /*  CalendarEventRepeatLength */

Included for completeness; repeating events are not supported in calendar.goh API.

This member specifies how long before occurrences of the event terminate. Either the event is repeated indefinitely (CERD_FOREVER), or for a finite time; CERD_UNTIL and CERD_LENGTH indicate the meaning of the duration-length parameter.

CalendarEventRepeatDurationData

typedef union {
    FileDate                  CERDD_until;   /* Repeat until when */
    CalendarEventRepeatLength CERDD_length;  /* Repeat for how long */
} CalendarEventRepeatDurationData;

typedef WordFlags CalendarEventRepeatLength;
#define CERL_COUNT        (0xfffc)
#define CERL_COUNT_OFFSET (2)

/* CalendarEventRepeatLengthUnitType */
#define CERL_UNIT         (0x0002 | 0x0001)
#define CERL_UNIT_OFFSET  (0)

typedef ByteEnum CalendarEventRepeatLengthUnitType;
#define CERLUT_DAY      (0x0)
#define CERLUT_WEEK     (0x1)
#define CERLUT_MONTH    (0x2)
#define CERLUT_YEAR     (0x3)

typedef WordFlags FileDate;
#define FD_YEAR		        0xfe00  /* :7 year since FDAT_BASE_YEAR */
#define FD_MONTH		0x01e0  /* :4 month (1-12) */
#define FD_DAY                  0x001f  /* :5 day of the month (1-31) */
#define FD_YEAR_OFFSET	        9
#define FD_MONTH_OFFSET	        5
#define FD_DAY_OFFSET		0

#define FDAT_BASE_YEAR		1980

Included for completeness; repeating events are not supported in calendar.goh API.

This member specifies the duration of a repeating event that terminates. The repeat interval can be specified by its termination date or by its length.

FileDate is an absolute date in a format defined in file.h. CalendarEventRepeatLength is a word specifying a time unit (days, weeks, months, or years) and a count. The count includes the initial event day; for example, an event repeating every day for 3 days set for 12/15 would have events on 12/15, 12/16, and 12/17.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Examples | Next: Appendix: Calendar File Format


Examples

Calendar API: 2 Examples

| Up: GEOS SDK TechDocs | Up | Prev: 1 Overview | Next: 3 Reference
Most of these examples are from the CalApi sample application.

Checking the Version of Calendar

Calendar API routines are only supported by later versions of the Calendar application. These versions will have the following INI file setting:
[Calendar]
calApi = True
This setting can be checked as follows:
    #include <initfile.h>
    Boolean calapi;

    if ( InitFileReadBoolean( "Calendar", "calApi", &calapi ) ) {

/* entry was not found */

        FoamDisplayWarning( @CantRunCalApi );
        @send application::MSG_META_QUIT();
    }
    else {
        if ( calapi == FALSE ) {

/* entry has wrong value */

            FoamDisplayWarning( @NoCalApiSupport );
            @send application::MSG_META_QUIT();
        }
    }

Specifying an Ordinary Event

This is an example of filling a CalendarEventParamStruct with parameters to specify an event with a start date, start time, optional end date, optional end time, and an alarm.

@include <calendar.goh>
#include <timedate.h>      /* definition of TimerDateAndTime */

/* Declare event_g as a Calendar event param structure */

    CalendarEventParamStruct event_g;

/* Other variables in this example */

    TimerDateAndTime date, time;
    Boolean          boolEndDate, boolEndTime;
    word             alarmMinutes;
    TCHAR            *eventText_g;

/* Fill the start date and time.  (Assume the variables "date" and "time"
 * have already been filled.)  Variables are cast as dwords to prevent
 * bit-shifting off the left of a too-small word.
 */

    event_g.CEPS_startDateTime =
        ( (dword)date.TDAT_year    << FDAT_YEAR_OFFSET   ) |
        ( (dword)date.TDAT_month   << FDAT_MONTH_OFFSET  ) |
        ( (dword)date.TDAT_day     << FDAT_DAY_OFFSET    ) |
        ( (dword)time.TDAT_hours   << FDAT_HOUR_OFFSET   ) |
        ( (dword)time.TDAT_minutes << FDAT_MINUTE_OFFSET );

/* Fill the end date and time.  (Assume the variables "date", "time", and
 * "boolEndXxxx" have already been filled.)  Variables are cast as dwords to
 * prevent bit-shifting off the left of a too-small word.  Use CAL_NO_XXXX
 * if a value is not specified.
 */

    if (boolEndDate) {
        event_g.CEPS_endDateTime =
            ( (dword)date.TDAT_year  << FDAT_YEAR_OFFSET  ) |
            ( (dword)date.TDAT_month << FDAT_MONTH_OFFSET ) |
            ( (dword)date.TDAT_day   << FDAT_DAY_OFFSET   );
    }
    else {
        event_g.CEPS_endDateTime = (dword)CAL_NO_DATE << FDAT_DAY_OFFSET;
    }

    if (boolEndTime) {
        event_g.CEPS_endDateTime |=
            ( (dword)time.TDAT_hours   << FDAT_HOUR_OFFSET   ) |
            ( (dword)time.TDAT_minutes << FDAT_MINUTE_OFFSET );
    }
    else {
        event_g.CEPS_endDateTime |= (dword)CAL_NO_TIME << FDAT_2SECOND_OFFSET;
    }

/* The event will not be a multi-day event */

    event_g.CEPS_reserveWholeDay = 0;

/*
 * The alarm will go off "alarmMinutes" minutes before event time.
 * Mask with CAS_INTERVAL to ensure alarmMinutes is in range.
 */

    event_g.CEPS_alarm =
          CAS_HAS_ALARM |
        ( CAIT_MINUTES << CAS_TYPE_OFFSET ) |
        ( CAS_INTERVAL & ( alarmMinutes << CAS_INTERVAL_OFFSET ));

/* Event has a text description */

    event_g.CEPS_eventType = CEDT_GEOS_TEXT;

/* Event is not repeating */

    event_g.CEPS_repeatInfo = 0;

/* Assume event text is already pointed to by eventText_g */

    event_g.CEPS_dataLength = LocalStringSize( eventText_g );
    event_g.CEPS_data = eventText_g;

Specifying a To-do Event

To specify a to-do list event, the start date is set to CAL_NO_DATE and the to-do priority is set in the start time field.

@include <calendar.goh>

/* Declare event_g as a Calendar event param structure */

    CalendarEventParamStruct event_g;

/* Other variables in this example */

    word     myPriority;
    TCHAR    *eventText_g;

/* "myPriority" can be in the range [0-2] here, corresponding to
 * CTDIS_HIGH_PRIORITY, CTDIS_NORMAL_PRIORITY, and CTDIS_COMPLETED.
 */

    event_g.CEPS_startDateTime =
        ((dword)CAL_NO_DATE << FDAT_DAY_OFFSET) |
        ((dword)(myPriority + CTDIS_HIGH_PRIORITY)  << FDAT_2SECOND_OFFSET);

/* Assume the to-do text is already pointed to by eventText_g */

    event_g.CEPS_dataLength = LocalStringSize( eventText_g );
    event_g.CEPS_data = eventText_g;

/* This parameter must be zero, otherwise the error CEE_EVENT_NOT_SUPPORTED
 * will be generated.
 */
    event_g.CEPS_repeatInfo = 0;

Other parameters in CalendarEventParamStruct will be ignored.

Adding an Event

To add an event, send MSG_CALENDAR_ADD_EVENT to the Calendar application's process object via IACP. The error code and assigned event ID can be checked in your callback method (MSG_CALAPI_PROCESS_ADD_EVENT_CALLBACK here); see examples of callbacks below.

      /*
       * serverGeodeToken    - token of the calendar app
       * connectionFlags     - optional flags for connection
       * iacpConnectionToken - token for this IACP connection
       * serverCount         - the number of running servers
       * hMsgToSend          - handle of the recorded message
       * hLaunchBlock        - handle of launch block
       * pLaunchBlock        - pointer to launch block
       */
    GeodeToken	     serverGeodeToken = CALENDAR_TOKEN;
    IACPConnectFlags connectionFlags  = 0;
    IACPConnection   iacpConnectionToken;
    word	     serverCount;
    EventHandle      hMsgToSend;
    MemHandle        hLaunchBlock;
    AppLaunchBlock * pLaunchBlock;

      /*
       * Make a launch block so that server (calendar app) is
       * launched if not running already.
       */
    hLaunchBlock = IACPCreateDefaultLaunchBlock(
        MSG_GEN_PROCESS_OPEN_APPLICATION );

      /*
       * Make sure it does not bring up the calendar on top of us.
       * Otherwise, the user can interact with it and produce
       * unwanted side-effects.
       */
    pLaunchBlock = MemLock( hLaunchBlock );
    pLaunchBlock->ALB_launchFlags |=
    ( ALF_OPEN_IN_BACK | ALF_DO_NOT_OPEN_ON_TOP );
    MemUnlock( hLaunchBlock );

      /*
       * Connect to the Calendar IACP server.
       */
    iacpConnectionToken = IACPConnect( &serverGeodeToken,
                                       connectionFlags,
                                       hLaunchBlock,
                                       NullOptr,
                                       &serverCount );

    if ( iacpConnectionToken != IACP_NO_CONNECTION ) {

          /*
           * Record the ADD_EVENT message for transmittal via IACP.
           */

        hMsgToSend = @record null::MSG_CALENDAR_ADD_EVENT(
                &event_g;
                ConstructOptr( GeodeGetProcessHandle(), NullChunk ),
                MSG_CALAPI_PROCESS_ADD_EVENT_CALLBACK );

          /*
           * Send the recorded message to the Calendar process obj
           * using IACPSendMessage.
           */

        IACPSendMessage( iacpConnectionToken,
                         hMsgToSend,
                         TO_PROCESS,
                         NULL,
                         IACPS_CLIENT );
          /*
           * Shut down the IACP connection and return the error value.
           */

        IACPShutdown( iacpConnectionToken, NullOptr );
        return( CAE_NO_ERROR );
    }
    else {
          /*
           * Return an error as something went wrong
           * trying to connect to the calendar app.
           */
        return( CAE_CANNOT_CONNECT_CALENDAR );
    }

Getting an Event

To retrieve event parameters from an event you added (perhaps to check for any changes made by the user), send MSG_CALENDAR_GET_EVENT_BY_ID via IACP and inspect the returned memory block in your callback method. Send the IACP message as above, replacing the ADD_EVENT request with a GET_EVENT one; the eventID is the one returned to your ADD_EVENT callback.

        hMsgToSend = @record MSG_CALENDAR_GET_EVENT_BY_ID(
                eventID,
                GeodeGetProcessHandle(),
                ConstructOptr( GeodeGetProcessHandle(), NullChunk ),
                MSG_CALAPI_PROCESS_GET_EVENT_CALLBACK );

In your callback method (the last parameter above), extract the information you are interested in. See the sample application CalApi for the complete method; excerpts are shown below.

/* In your class declaration: */

    @message (CALENDAR_GET_EVENT_CALLBACK_MSG)
                         MSG_CALAPI_PROCESS_GET_EVENT_CALLBACK;;

/********************************************************************
 *		MSG_CALAPI_PROCESS_GET_EVENT_CALLBACK
 ********************************************************************
 * SYNOPSIS:	 Callback from Calendar IACP server on result of
 *               MSG_CALENDAR_GET_EVENT_BY_ID.
 * PARAMETERS:	 CalendarEventError error
 *               MemHandle          eventBlock
 * RETURN:       void
 *
 * STRATEGY:  Check status value; successful if CEE_NORMAL
 *            If successful,
 *               Lock and dereference returned global block
 *               Calculate pointer to event text (it follows
 *                   the returned event structure)
 *               Extract information from structure
 *               Free the global memory block!
 *
 *******************************************************************/
@method CalApiProcessClass, MSG_CALAPI_PROCESS_GET_EVENT_CALLBACK
{
      /*
       * eventData     - pointer to the returned event data
       * eventTextData - pointer to the event description
       * dateTime      - dateTime structure used to display event info
       * tempStr       - used to convert event info into strings
       * alarmType     - used to convert alarm info into alarm setting
       */
    CalendarReturnedEventStruct * eventData;
    TCHAR *                       eventTextData;
    TimerDateAndTime              dateTime;
    TCHAR                         tempStr[MAX_EVENT_TEXT_LENGTH];
    CalApiAlarmType               alarmType;

      /*
       * If there are no errors in getting the event,
       * we lock down the returned structure and dereference
       * the event text description.
       */
    if ( CEE_NORMAL == error ) {
    eventData = MemLock( eventBlock );
    eventTextData = (TCHAR*)((TCHAR*)eventData +
                        sizeof( CalendarReturnedEventStruct ) );
          /*
           * Convert the start date/time into strings.
           * First turn the FileDateAndTime into a TimerDateAndTime.
           * Then use LocalFormatDateTime to turn that into a string.
           * Don't forget to handle the case where the date or
           * time isn't specified (i.e. equals -1).
           */
        dateTime.TDAT_year = FDATExtractYear( eventData->CRES_startDateTime ) +
                             FDAT_BASE_YEAR;
        dateTime.TDAT_month = FDATExtractMonth( eventData->CRES_startDateTime );
        dateTime.TDAT_day = FDATExtractDay( eventData->CRES_startDateTime );
        dateTime.TDAT_hours = FDATExtractHour( eventData->CRES_startDateTime );
        dateTime.TDAT_minutes = FDATExtractMinute(
            eventData->CRES_startDateTime );
        if ( ( eventData->CRES_startDateTime & 0x0000ffff ) == CAL_NO_DATE ) {
            strcpy( tempStr, "NO_DATE" );
        }
        else {
            LocalFormatDateTime( tempStr, DTF_SHORT, &dateTime );
        }

        if ( ( eventData->CRES_startDateTime & 0xffff0000 ) ==
          (dword)CAL_NO_TIME << 16 ) {
            strcpy( tempStr, "NO_TIME" );
        }
        else {
            LocalFormatDateTime( tempStr, DTF_HM_24HOUR, &dateTime );
        }

     /* Perform any other processing; display information... */

          /*
           * Free the event data block.
           */
    MemFree( eventBlock );
    }
    else {
        /* Error handling for error != CEE_NORMAL ... */
    }
} /* MSG_CALAPI_PROCESS_GET_EVENT_CALLBACK */

Checking for Events

To check for events in a given time range, pass the start and end times with MSG_CALENDAR_CHECK_IF_EVENT_EXISTS via IACP (as in the example above). Use your callback to interpret the returned status code.
/* Check for events overlapping the range 12:15 PM June 9, 1997 until
   9:15 AM June 10, 1997  */

/* Note that constants are specified as Long literals. */

    FileDateAndTime          startDateTime_g =
        ( ( ( 1997L - FDAT_BASE_YEAR ) << FDAT_YEAR_OFFSET ) |
          ( 6L    << FDAT_MONTH_OFFSET  ) |
          ( 9L    << FDAT_DAY_OFFSET    ) |
          ( 12L   << FDAT_HOUR_OFFSET   ) |
          ( 15L   << FDAT_MINUTE_OFFSET ) );

    FileDateAndTime          endDateTime_g =
        ( ( ( 1997L - FDAT_BASE_YEAR ) << FDAT_YEAR_OFFSET ) |
          ( 6L    << FDAT_MONTH_OFFSET  ) |
          ( 10L   << FDAT_DAY_OFFSET    ) |
          ( 9L    << FDAT_HOUR_OFFSET   ) |
          ( 15L   << FDAT_MINUTE_OFFSET ) );

    hMsgToSend = @record null::MSG_CALENDAR_CHECK_IF_EVENT_EXISTS(
            startDateTime_g,
            endDateTime_g,
            ConstructOptr( GeodeGetProcessHandle(), NullChunk ),
            MSG_CALAPI_PROCESS_CHECK_IF_EVENT_EXISTS_CALLBACK );

/* In CalApiProcessClass, declare the method: */

    @message (CALENDAR_ACCESS_EVENT_COMMON_CALLBACK_MSG)
                  MSG_CALAPI_PROCESS_CHECK_IF_EVENT_EXISTS_CALLBACK;

/********************************************************************
 *            MSG_CALAPI_PROCESS_CHECK_IF_EVENT_EXISTS_CALLBACK
 ********************************************************************
 * SYNOPSIS:     Callback from Calendar IACP server on result of
 *               MSG_CALENDAR_CHECK_IF_EVENT_EXISTS.
 * PARAMETERS:   CalendarEventError error
 * RETURN:       void
 * STRATEGY:     If an event existed, CEE_NORMAL is returned;
 *               otherwise, we get CEE_EVENT_NOT_FOUND.
 *******************************************************************/
@method CalApiProcessClass, MSG_CALAPI_PROCESS_CHECK_IF_EVENT_EXISTS_CALLBACK
{
    if (error == CEE_NORMAL) {

  /* At least one event in search range... */

    } else if (error == CEE_EVENT_NOT_FOUND) {

  /* No events overlap search range...*/

    } else {

  /* A processing error occurred... */

    }
} /* MSG_CALAPI_PROCESS_CHECK_IF_EVENT_EXISTS_CALLBACK */

| Up: GEOS SDK TechDocs | Up | Prev: 1 Overview | Next: 3 Reference


Calendar File Format

| Up: GEOS SDK TechDocs | Up | Prev: 3 Reference
  • Normal events and To-do list items
  • Organization of normal events
  • Multiple Day Events
  • Organization of To-do list items
  • Repeat Events
  • Organization of repeat events
  • Memos
  • Unique ID info
  • Event ID array info
  • Event Sent-To info
  • Server Destination Info (N9110 Communicator)
  • Pending event info (N9110 Communicator)
  • Text file syntax update

Data for the events, to-do list items, and memos associated with events are stored in a file called "Calendar". Its DOS name is "calendar.000", and it is located in the privdata\calendar directory.

Normal events and To-do list items

Normal events refer to events which are not repeated. In other words, normal events only happen once. Repeated events occur at a regular frequency (e.g. daily, weekly). Normal events and To-do list items (as well as repeated events) are stored in database items. A DBGroup/DBItem pair uniquely identify an event or item, and Database library routines are used to manipulate or access these database items. The data structure for these events/items is called EventStruct, and it has the following structure:

EventStruct			struct
    ES_flags                  EventInfoFlags
    ES_parentMap              word	(?)
    ES_timeYear               word	(?)
    ES_timeDay                byte	(?)
    ES_timeMonth              byte	(?)
    ES_timeMinute             byte	(?)
    ES_timeHour               byte	(?)
    ES_endYear                word  (?)
    ES_endDay                 byte	(?)
    ES_endMonth               byte	(?)
    ES_reservedDays           word	(?)
    ES_endTimeMinute          byte	(?)
    ES_endTimeHour            byte	(?)
    ES_varFlags               VariableLengthFlags
    ES_memoToken              word	(?)
    ES_uniqueID               dword (?)
    ES_alarmYear              word	(?)
    ES_alarmDay               byte	(?)
    ES_alarmMonth             byte	(?)
    ES_alarmMinute            byte	(?)
    ES_alarmHour              byte	(?)
    ES_alarmPrevGr            word	(?)
    ES_alarmPrevIt            word	(?)
    ES_alarmNextGr            word	(?)
    ES_alarmNextIt            word	(?)
    ES_repeatID               word	(?)
    ES_sentToArrayBlock       word (?)
    ES_sentToArrayChunk       word (?)
    ES_nextBookID             word (?)
    ES_dataLength             word	(?)
    ES_data                   byte	(?)
EventStruct		ends

Normal events are distinguished from To-do list items in the EventInfoFlags. The EventInfoFlags are a byte record:

EventInfoFlags		record
    EIF_ALARM_ON:1		; most significant bit
    EIF_NORMAL:1
    EIF_HEADER:1
    EIF_REPEAT:1
    EIF_TODO:1
    EIF_INK:1
    EIF_ALARM_SOUND	AlarmSound:2
EventInfoFlags		end

EventInfoFlags are used in the DayEvent object (a UI object that is not in the database), so there is a flag for repeat events. But in the EventStruct, EIF_REPEAT will never be set since a different data structure is used for repeated events.

The member ES_parentMap is the DBItem number for the structure which organizes all events for a day. More on this in Organization of normal events.

ES_timeYear, ES_timeDay, ES_timeMonth, ES_timeMinute, and ES_timeHour all specify the date and time of the event. The date is NOT 0-based. For example, Jan 1st, 1996 would translate to:

        ES_timeYear		1996
        ES_timeDay		1
        ES_timeMonth	1

Time for an event can range from 00:00(midnight) to 23.59(11:59pm). For example 1:34pm would be:

        ES_timeMinute	34
        ES_timeHour		13

An event can be specified over an interval (e.g. 9:00-10:00). In order to specify this interval, an event has an end date and end time. The VariableLengthFlags in the EventStruct are used to specify if an event has an end time. Since the VariableLengthFlags record is used in the DayEvent object, it has two flags to denote if the start or end times have been modified.

VariableLengthFlags	record
   VLF_START_TIME:1		     ; does event have start time?
   VLF_END_TIME:1			; does event have an endtime?
   VLF_START_DIRTY:1		; start time/date dirty?
   VLF_END_DIRTY:1			; end time/date dirty?
   VLF_END_DATE:1			; does event have an enddate?
   VLF_WHOLE_DAY_RES:1		; whole day reservation
   VLF_REPEAT_UNTIL:1		; we have a repeat until date
   :1
VariableLengthFlags	end

The names of event memos are stored in an array. The members of this array are memo name/memo token pairs. The memo token in the EventStruct is used to find the memo name for an event memo. If an event has no memo, then this field is -1 (ffffh). Zero is a valid memo token. Memos will be discussed more in the section Memos.

All events have unique IDs associated with them (ES_uniqueID). See Unique ID info.

Each event(normal or repeat) can have an alarm associated with the event. The EIF_ALARM_ON flag in the EventInfoFlags tell if the alarm is set. The members ES_alarmYear, ES_alarmDay, ES_alarmMonth, ES_alarmMinute, and ES_alarmHour all tell the date and time of the alarm. The alarm date/time is the same format as the event date/time(i.e. NOT 0-based). If the alarm flag is not set, then this alarm time is the same as the event time. It is important that the alarm time be valid even if there is no alarm.

ES_sentToArrayBlock and ES_sentToArrayChunk is a chunk array block and chunk handle that identifies the Sent-To information of the event. ES_nextBookID is the ID to be assigned to the next booking appointment on the event. See Sent-To info.

All events are in a doubly-linked list, sorted by alarm time. The structure members ES_alarmPrevGr, ES_alarmPrevIt, ES_alarmNextGr, and ES_alarmNextIt are the "pointers" to the previous and next events respectively. As mentioned before, if an event doesn't have its alarm set, then its alarm time is the same as its event time. To-do list items are not linked in a list. ES_alarmPrevGr and ES_alarmPrevIt constitute the DBGroup/DBItem pair which identify the previous event. There are two dummy events in this list--one at the beginning and one at the end. The first dummy event has the following values:

    ES_flags			(?) - uninitialized
    ES_parentMap		0
    ES_timeYear		1900
    ES_timeDay			0
    ES_timeMonth		0
    ES_timeMinute 		0
    ES_timeHour		0
    ES_endYear			(?)
    ES_endDay			(?)
    ES_endMonth		(?)
    ES_reservedDays		(?)
    ES_endTimeMinute	(?)
    ES_endTimeHour		(?)
    ES_varFlags		(?)
    ES_memoToken		(?)
    ES_alarmYear		1900
    ES_alarmDay		0
    ES_alarmMonth		0
    ES_alarmMinute		0
    ES_alarmHour		0
    ES_alarmPrevGr		0
    ES_alarmPrevIt		0
    ES_alarmNextGr		depends on 1st event by alarm time
    ES_alarmNextIt		depends on 1st event by alarm time
    ES_repeatID		(?)
    ES_sentToArrayBlock	(?)
    ES_sentToArayChunk	(?)
    ES_nextBookID		(?)
    ES_dataLength		(?)
    ES_data			(?)

The values for the end dummy event are:

    ES_flags			(?) - uninitialized
    ES_parentMap		0
    ES_timeYear		9999
    ES_timeDay			33
    ES_timeMonth		12
    ES_timeMinute 		0
    ES_timeHour		0
    ES_endYear			(?)
    ES_endDay			(?)
    ES_endMonth		(?)
    ES_reservedDays		(?)
    ES_endTimeMinute	(?)
    ES_endTimeHour		(?)
    ES_varFlags		(?)
    ES_memoToken		(?)
    ES_alarmYear		1999
    ES_alarmDay		33
    ES_alarmMonth		12
    ES_alarmMinute		0
    ES_alarmHour		0
    ES_alarmPrevGr		depends on last event by alarm time
    ES_alarmPrevIt		depends on last event by alarm time
    ES_alarmNextGr		0
    ES_alarmNextIt		0
    ES_repeatID		(?)
    ES_sentToArrayBlock	(?)
    ES_sentToArayChunk	(?)
    ES_nextBookID		(?)
    ES_dataLength		(?)
    ES_dataLength		(?)
    ES_data			(?)

Organization of normal events

Please refer to figure-1 for graphical represenation of calendar file structure during the following discussion.

Figure 1. Normal Event Data Structures

At the top of the hierarchy for all Calendar data is the database map block. This map block contains the structures YearMapHeader followed directly by a YearMapStruct for each year in which there is an event. The map block looks like:

YearMapHeader		struct
    YMH_secure1			byte	(?)
    YMH_numYears			word	(?)
    YMH_nextAlarmGr			word	(?)
    YMH_nextAlarmIt			word	(?)
    YMH_repeatMapGr			word	(?)
    YMH_repeatMapIt			word	(?)
    YMH_multDayTableGr		word	(?)
    YMH_multDayTableIt		word	(?)
    YMH_undoBufGr			word	(?)
    YMH_undoBufIt			word	(?)
    YMH_undoRmGroup			byte	(?)
    YMH_secure2			byte	(?)
    YMH_toDoListGr			word (?)
    YMH_toDoListIt			word	(?)
    YMH_memoNameArrayBlock	word	(?)
    YMH_memoNameArrayChunk	word	(?)
    YMH_nextMemoToken		word	(?)
    YMH_RTCMAlarmGr			word	(?)
    YMH_RTCMAlarmIt			word	(?)
    YMH_alarmToken			word (?)
    YMH_alarmID			word (?)
    YMH_alarmYear			word	(?)
    YMH_alarmMonthDay		word	(?)
    YMH_alarmHourMinute		word	(?)
    YMH_nextEventID			dword (?)
    YMH_eventIDArray		word (?)
    YMH_pendingApptArrayBlock word (?)	; N9110 Communicator
    YMH_pendingApptArrayChunk word (?)	; N9110 Communicator
    YMH_serverDestinationArray word (?)	; N9110 Communicator
YearMapHeader		ends

The YearMapHeader has two bytes to ensure that it hasn't been corrupted. YMH_secure1 contains SECURITY1, while YMH_secure2 contains SECURITY2.

SECURITY1		equ		10111010b
SECURITY2		equ		01001101b

The number of years denotes the number of years that have events, and therefore, the number of YearMapStructs which follow the YearMapHeader in the map block.

The YMH_nextAlarmGr/It is a DBGroup/DBItem pair denoting the next event by alarm time. The naming is misleading because even though it says next alarm, this event may not have its alarm on. It is easier to think of this event as the next event in the linked list of events, since this list is sorted by alarm time. YMH_RTCMAlarmGr/It, however, is the next event whose alarm is on. These fields are zero if there is no event registered with the RTCM(Real-time clock) library.

YMH_repeatMapGr/It is the database item which organizes the repeated events. This will be detailed in Organization of repeat events.

  • YMH_undoBufGr/It and YMH_undoRmGroup are unused since the undo facility is not used in Responder Calendar.
  • YMH_toDoListGr/It is the database item which organizes the To-do list items. More on this in Organization of To-do list items.
  • YMH_memoNameArrayBlock and YMH_memoNameArrayChunk are the VMBlock and chunk handle for the ChunkArray containing the memo name/memo token pairs for event memos. More on this in Memos.
  • YMH_nextMemoToken is the next token to be assigned when the next memo is created. It must be larger than any memo token in existence.
  • YMH_alarmToken and YMH_alarmID are information given by the RTCM library to identify an alarm. They are created when an alarm is registered for an event with the RTCM library. We must use these when we want to specify our registered alarm (e.g. to unregister the alarm, to change the alarm time) to the RTCM library. When importing, these values should be zeroed.
  • YMH_alarmYear/MonthDay/HourMinute specify the date and time for the alarm registered with the RTCM library.
  • YMH_nextEventID specifies the next event ID to be used.
  • YMH_eventIDArray specifies a huge array in the same file that stores the mapping of unique IDs and DB Group:Item of the corresponding event. See Event ID array info for more details.
  • (N9110 Communicator) YMH_pendingApptArrayBlock and YMH_pendingApptArrayChunk are the VMBlock and chunk array handle storing the list of holding events with SMS number and versit text. More information can be found in Pending event info.
  • (N9110 Communicator) YMH_serverDestinationArray is the vm handle of the huge array holding the server destination (attendee) information for each UID used in remote booking.

Following the YearMapHeader are the YearMapStructs which lead us to the normal events for that year. The YearMapStruct looks like:

YearMapStruct		struct
    YMS_year		word	(?)		; the year
    YMS_group		word	(?)		; group for this year
    YMS_yearMap	word	(?)		; item # for year map
YearMapStruct		ends

The YMS_yearMap is a DBItem which holds an array of DBItem numbers for the days in the year where there are events. This "YearMap" is the following size:

    YearMapSize		equ (12 * 32 * 2)   	; twelve months, 32 days, 2 bytes

By translating the day into a position in this year map, we find the DBItem number for the database item which organizes all events for that day. Conceptually, it works as follows:

    year	->	DBGroup
    month/day  ->	DBItem		Event organizing block for a single day

The date is translated to an index into the array as follows:

    index =  ((month -1) * 64) + (2 * day)

For example:

    Jan 1 index = ((1-1) * 64) + (1 * 2) = 2
    March 5 index =  ((3-1) * 64)  +(5 * 2) = 138

The array would have some members not used (marked empty), while those days without events will have zeros. Those days with events will have the DBItem number for the event organizing block.

 | Jan 1 | Jan 2 | Jan 3 | … | Jan 31 |  | Feb 1 | … |
empty | 0 | 0 | 68h | … | 0 | empty | 36h | … | 0

The event organizing block contains two structures: EventMapHeader and EventMapStruct. The EventMapHeader looks like:

  • DBItem number for this block
  • Number of events for this day
  • Day
  • Month
    EventMapHeader		struct
        EMH_item		word
        EMH_numEvents	word
        EMH_day		byte
        EMH_month		byte
    EventMapHeader		ends

Directly following the EventMapHeader is an EventMapStruct for every event in that day. Consequently, EMH_numEvents tells you how many EventMapStructs there are following the EventMapHeader.

  • Time for event
  • DBItem number for event
      EventMapStruct		struct
        EMS_time		word
        EMS_event		word
    EventMapStruct		ends

Finally, we've reached the DBItem number for the event (EMS_event). Combining the DBGroup number for the year with the DBItem number in the EventMapStruct gives us the "pointer" to the event (EventStruct) itself.

Multiple Day Events

If VLF_END_DATE flag set in a normal event, it is a multiple day event and it has valid ES_endDay/Month/Year values. If VLF_WHOLE_DAY_RES flag is set in an event, it is a special multiple day event which reserves a certain number of days from the start date. ES_reservedDays field as well as ES_endDay/Month/Year are filled in this case. An extra data structure (MultipleDayTableHeader and MultipleDayTableEntry's ) is used to record all multiple day events. This table can be traversed to tell whether a given date is included within the range of a multiple day event (figure-2.

MultipleDayTableHeader	struct
   MDTH_available	word		; number of available entries
   MDTH_validCount	word		; number of valid entries
   MDTH_totalSize	word		; table block size in bytes
   MDTH_data		label	MultipleDayTableEntry
MultipleDayTableHeader	ends

MultipleDayTableEntry	struct
   MDTE_groupItem	dword	; group#:item# pair
                        ; 0 if invalid
   MDTE_startDate	dword	; high word - year
                        ; low word hi byte - month
                        ; low word low byte - day
   MDTE_endDate	dword	; high word - year
                        ; low word hi byte - month
                        ; low word low byte - day
   align word
MultipleDayTableEntry	ends
Figure 2. Multiple Day Event Table

In order to determine whether a date falls within the range of a multiple day event, one has to look through entire table comparing dates. MDTE_groupItem contains 0 if the entry is an empty entry. MDTH_validCount is provided so that you can stop traversing the table as soon as you've looked at all valid entries( that is, you should keep track of how many valid entries have been examined ). Valid entries tend to group at the front of the table.

Organization of To-do list items

As mentioned before, the YearMapHeader has the DBGroup/It numbers for the database item which organizes the To-do list. Conceptually, the To-do list is organized as one day. The DBGroup/It numbers for the To-do list point to a database item which has the same structures as that for organizing a day of events--EventMapHeader and EventMapStructs. The EMH_numEvents is actually the number of To-do list items, while the day and month are special values:

        TODO_DUMMY_MONTH_DAY 	equ	259.

Each EventMapStruct holds the DBItem number for the EventStruct which is the To-do list item. To-do items, however, are not sorted by time like events. Instead they are sorted by priority:

        TODO_DUMMY_HOUR		equ	1
        TODO_HIGH_PRIORITY		equ	1
        TODO_NORMAL_PRIORITY	equ	2
The priority information is stored in the minute field of the EventStruct (ES_timeMinute) , while all To-do items will have the same year/month/day/hour. The To-do items are also distinguished by their year (TODO_DUMMY_YEAR equ 1), which is an invalid year for normal events(HIGH_YEAR equ 9999, LOW_YEAR equ 1900). Finally, a To-do list item can be marked completed. This information is stored in the ES_alarmMinute field of EventStruct.

    TODO_COMPLETED	equ		3
    TODO_NOT_COMPLETED	equ		4

Repeat Events

Repeated events have a different structure than EventStruct. Their data structure is called RepeatStruct:

RepeatStruct		struct
    RES_flags				EventInfoFlags
    RES_type				RepeatEventType
    RES_ID				word	(?)
    RES_startYear			word	(?)
    RES_startDay			byte	(?)
    RES_startMonth			byte	(?)
    RES_endYear			word	(?)
    RES_endDay				byte	(?)
    RES_endMonth			byte	(?)
    RES_minute				byte	(?)
    RES_hour				byte	(?)
    RES_alarmPrecedeMinute	word 	(?)
    RES_memoToken			word 	(?)
    RES_endMinute			byte	(?)
    RES_endHour			byte	(?)
    RES_varFlags			VariableLengthFlags
    RES_padding			byte	(?)
    RES_uniqueID			dword (?)
    RES_sentToArrayBlock		word (?)
    RES_sentToArrayChunk		word (?)
    RES_nextBookID			word (?)
    RES_DOWFlags			DOWFlags (?)
    RES_day				byte	(?)
    RES_month				byte	(?)
    RES_DOW				byte	(?)
    RES_occur				byte	(?)
    RES_dataLength			word	(?)
    RES_data				byte	(?)
RepeatStruct		ends

RES_Flags is a byte record of EventInfoFlags. It is the same as ES_flags in EventStruct.

RES_type is of type RepeatEventType. RepeatEventType is an enumerated byte:

RepeatEventType		etype byte, 1
    RET_WEEKLY			enum RepeatEventType
    RET_MONTHLY_DATE	enum RepeatEventType
    RET_MONTHLY_DOW		enum RepeatEventType(unused)
    RET_YEARLY_DATE		enum RepeatEventType
    RET_YEARLY_DOW		enum RepeatEventType(unused)
    RET_BIWEEKLY		enum	RepeatEventType

The RepeatEventType field describes the frequency, or what kind of repeat event this is. For daily, we use RET_WEEKLY and then in the DOWFlags, we set every day. As for working days events, we use RET_WEEKLY and only set Mon-Fri in DOWFlags. (See below)

  • RES_ID is the RepeatID. This is the unique number which identifies each repeat event.
  • RES_startYear, RES_startDay, and RES_startMonth are the date that this repeat event should start.
  • RES_endYear, RES_endDay, and RES_endMonth are the date that this repeat event should end. When this field is valid, VLF_REPEAT_UNTIL bit is set in RES_varFlags.
  • RES_hour and RES_minute are the time for the repeat event.
  • RES_alarmPrecedeMinute is the number of minutes before the event that the alarm is scheduled to go off. The maximum allowed for Responder is 60 minutes. The normal events specify their alarm time explicitly (ES_alarmYear, ES_alarmHour, etc), but the repeated events cannot do that, since they represent many events. So if we had a daily event at 8am, and it has its alarm set to go off at 7:45am, then the RES_alarmPrecedeMinute for this event would be 15.
  • RES_memoToken is the same as ES_memoToken. More on this in Memos.
  • RES_endMinute and RES_endHour are the same as ES_endTimeMinute and ES_endTimeHour. RES_varFlags is a byte record of type VariableLengthFlags. This is the same as ES_varFlags.
  • RES_padding is a wasted byte.

Events have unique IDs associated with them (RES_uniqueID). See Unique ID info.

RES_sentToArrayBlock and RES_sentToArrayChunk is a chunk array block and chunk handle that identifies the Sent-To information of the event. RES_nextBookID is the ID to be assigned to the next booking appointment on the event. See Sent-To info.

RES_DOWFlags is a byte record of type DOWFlags.

DOWFlags		record
    DOWF_unused:1,
    DOWF_Sunday:1,
    DOWF_Saturday:1,
    DOWF_Friday:1,
    DOWF_Thursday:1,
    DOWF_Wednesday:1,
    DOWF_Tuesday:1,
    DOWF_Monday:1
DOWFlags		end

For weekly events, this record tells which day of the week we repeat on. For daily events, all the flags are set except DOWF_unused. For working day events, only DOWF_Monday, DOWF_Tuesday, DOWF_Wednesday, DOWF_Thursday and DOWF_Friday flags are set.

  • RES_day is used for events which repeat monthly or yearly. It is the number of the day (e.g. June 15th = 15).
  • RES_month is the number for the month if this event repeats yearly (e.g. Feb = 2).
  • RES_DOW and RES_occur are both unused in the 9000I communcator.
  • RES_dataLength is the length of the null-terminated string which is the event string.
  • RES_data is the first byte of the null-terminated string which is the event string.

RepeatEvents can also have exception dates. These are dates in which the repeated event is not displayed. For instance, a daily event could be displayed every day after April 15th except May 1st and May 2nd. May 1st and May 2nd would be the exception days. These dates come after the null-terminated string. These dates are of type RepeatDateException.

RepeatDateException		struct
    RDE_day		byte
    RDE_month		byte
    RDE_year		word
RepeatDateException

In order to find out how many of these exception dates there are, we need to find out the size of the DBItem and subtract the size of the RepeatStruct and the size of the string. Since RepeatDateException structures are four bytes, we divide the result by four to determine how many exception dates there are for this repeat event.

Organization of repeat events

Figure-3 represents repeat event data structure.

Figure 3. Repeat Event Data Structures

As mentioned before, the YearMapHeader has the DBGroup/It numbers for the repeat event organizing block. This repeat event organizing block has two structures in it: RepeatMapHeader followed by a RepeatMapStruct for each repeat event.

RepeatMapHeader		struct
     RMH_item		word
    RMH_size		word
    RMH_numItems	word
    RMH_nextValue	word
RepeatMapHeader		ends
  • RMH_item is the DBItem number for this organizing block.
  • RMH_size is the size of a repeat table created dynamically. It would not be needed for translating the file to the repeat events.
  • RMH_numItems is the number of repeat events, therefore, this is also the number of RepeatMapStruct structures which follow the RepeatMapHeader.
  • RMH_nextValue is the RepeatID given to the next created repeat event. It must be larger than any RepeatID currently in existence.
RepeatMapStruct		struct
    RMS_indexValue		word
    RMS_item			word
RepeatMapStruct		ends

RMS_indexValue is the unique RepeatID for this repeat event. If we're looking for a repeat event, we search for the RepeatMapStruct which matches our RepeatID. Once we match this RepeatID, we can get the DBItem number in the RMS_item field of the RepeatMapStruct. The DBGroup number comes from YMH_repeatMapGr.

Memos

Any normal or repeat event can have a memo attached to it. These memos are not kept in the Calendar file. Calendar keeps the names of memos in an array, and references these memos by their name. The YearMapHeader contains the VMBlock handle as well as the ChunkHandle within this VMBlock for the ChunkArray which contains the memo names. YMH_memoNameArrayBlock is the VMBlock handle, and YMH_memoNameArrayChunk is the ChunkHandle of the ChunkArray. This ChunkArray contains members of type MemoNameStruct.

    MemoNameStruct			MemoNameStruct	   struct
        MNS_memoName	FileLongName   ; Name of the memo
        MNS_memoToken	word    ; Token to identify the memo name
    MemoNameStruct	ends

Each event with a memo (either repeat event or normal event), has a memo token to reference the name of the memo. The memo name is of type FileLongName.

    FileLongName	type	FILE_LONGNAME_BUFFER_SIZE dup (char)
    FILE_LONGNAME_BUFFER_SIZE 	equ	36

In order to find the name of a memo for an event, then, we first get the block from the YearMapHeader with the array of memo name/memo token pairs. Then we search the chunk array for the matching memo token. Once found, we get the memo name from the MNS_memoName field of the MemoNameStruct in the array. The documents themselves are in the \document\own_text.000 directory. If a memo name is "Memo #5"in the array, then it corresponds to \document\own_text.000\memo_#5.000 .

Unique ID info

All events now have unique ID (dword) associated with them. The ID will not be reused on one device, and the first event ID is FIRST_EVENT_ID (00020001h). If the user uses data removal to remove all calendar event, the calendar app still will not reuse old IDs.

INVALID_EVENT_ID (00020000h) is a dummy value to represent invalid ID. It should not appear in the file.

The YearMapHeader has the next event ID to be used. (YMH_nextEventID). All events have their own unique ID (ES_uniqueID for normal events, RES_uniqueID for repeated events). All three fields should be exported and imported accordingly. See Text file syntax update.

If a text file from an old device is to be converted into a binary calendar file, all the events created should have unique ID, starting from FIRST_EVENT_ID.

Event ID array info

The YearMapHeader has a field called YMH_eventIDArray. It is a VMBlockHandle that points to a huge array in the same file. This huge array stores structures of event ID and corresponding event DB Group:Item of EventStruct. The purpose of this array is to allow easy look-up of event DB Group:Item given a unique ID, and then, one can dereference that Group:Item pair to access its EventStruct. The structure of each element is as follows:

EventIDArrayElemStruct		struct
    EIDAES_eventID			dword		; unique ID
    EIDAES_eventGr			word			; DB group
    EIDAES_eventIt			word			; DB item
EventIDArrayElemStruct		ends

These huge array elements are fixed-sized and they are unsorted. There should NOT be any elements with duplicate event IDs nor Group:Item at any time. These elements should always be kept updated to reflect the correct mapping of event ID and Group:Item of the corresponding event's EventStruct. So, whenever an event is inserted, deleted or when the EventStruct DB Group:Item is changed, this huge array has to be updated.

It is not necessary to export this huge array. However, it is important to reconstruct this huge array for import. So, during calendar import, one has to call HugeArrayCreate() to create the huge array and HugeArrayAppend() to insert a huge array element for each event.

Event Sent-To info

Some events might have Sent-To information. e.g. Alice creates an event in her calendar ("Lunch") and uses the app to send an appointment to Bob. Bob's name and SMS number is already entered in contact database, and Alice picks Bob's name from a contact directory.

Now the app has to keep track of a few things, because Bob may reply Alice to accept / deny the appointment. This struct contains one Sent-To information:

EventSentToStruct

    ESTS_bookID	word
                    ; the number that identifies the 						; current appointment from others 						; (for the same event)
    ESTS_yearSent	word
                    ; the date the appointment is sent
    ESTS_monthSent	byte
    ESTS_daySent	byte
    ESTS_hourSent	byte
                    ; the time the appointment is sent
    ESTS_minuteSent byte
    ESTS_name		SENT_TO_NAME_FIELD_MAX_LEN_WITH_NULL (byte)
                    ; name of recipient
    ESTS_smsNum	MAX_NUMBER_FIELD_DATA_LEN+1 (byte)
    ESTS_status	EventRecipientStatus
                    ; what does the recipient say?
    ESTS_remoteEventID dword
                    ; ID of created event in
                    ; recipient's device

where:

EventRecipientStatus:

    ERS_ILLEGAL	= 0	; illegal status
    ERS_ACCEPTED	= 2	; the appointment accepted
    ERS_DISCARDED	= 4	; denied
    ERS_NO_REPLY	= 6	; no reply yet
    ERS_FORCED	= 8	; forced
    ERS_FORCED_AND_ACCEPTED	= 10	; forced and accepted

SENT_TO_NAME_FIELD_MAX_LEN_WITH_NULL = 21
MAX_NUMBER_FIELD_DATA_LEN = 30

When an event has new sent-to information, a chunk array of EventSentToStruct is created for the event if none exists; then a new element of EventSentToStruct is created at the end of the array. The block and chunk of the chunk array is stored at ES_sentToArrayBlock / ES_sentToArrayChunk / RES_sentToArrayBlock / RES_sentToArrayChunk.

When an event is created (on Bob's calendar) through an SMS (from Alice), the event needs to store the sender's information in the event struct. This is so that we can do update on event (e.g. Alice decides there will be no lunch after all, and cancel the event.)

We decided to keep three pieces of information in the event. (SMS number, event ID and book ID) An appropriate place to store these would be the usual {Repeat|Event}Struct; however, that would increase the size of all the ordinary events. So we will keep them in the sent-to chunk array header.

    EventSentToHeader	struct
        ESTH_chunkArrayHd		ChunkArrayHeader	; chunk array header
            ESTH_senderSMS		SMSNumberType	; SMS number of sender
        ESTH_senderEventID	dword			; ID of sender's event in
                                    ;  sender's device
        ESTH_senderBookID	word			; Book ID of sender's event
    EventSentToHeader	ends

    SMSNumberType	TYPE 23 dup (char)

To summarize: {Repeat|Event}Struct stores the block handle and chunk handle of sent-to array. The array header type is EventSentToHeader struct; the array has elements of type EventSentToStruct. If the event is created from SMS, the header stores the sender information. (Otherwise, the ESTH_senderSMS is null; ESTH_senderEventID is INVALID_EVENT_ID; ESTH_senderBookID is 0) If the event has been sent to other recipients, each appointment would have a corresponding element (of type EventSentToStruct) in the chunk array.

To identify the first appointment Alice made to Bob from the second one, the (R)ES_bookID is used. The first booking of the event has ID = 0, the next one 1, and so on.

All of the fields in the EventSentToStruct have to be exported. Same goes for the (R)ES_sentToArrayBlock, (R)ES_sentToArrayChunk, and (R)ES_nextBookID. See Text file syntax update.

Server Destination Info (N9110 Communicator)

Remote calendar booking requires sending and receiving booking events through a centralized server. So, it requires a bit more information in addition to the sent-to information found in the EventSentToHeader. The YMH_serverDestinationArray is an unsorted huge array with variable sized elements. Each element holds a header followed by a number of Attendee fields. The header is defined by the SentToSubDestinationEntry structure:

    SentToSubDestinationEntry	struct
        STSDE_UID		dword			; unique id
        STSDE_count    word			; number of booking ids,
                                ; excluding sender
                                ; attendee field
        STSDE_reserved	word			; should be 0 if not used
        STSDE_null	word				; should always be 0
        STSDE_destinations label TCHAR
                                ;
                                ; followed by:
                                ;   1. Sender attendee field, null-terminated string
                                ;      (It can just be NULL).
                                ;   2. 0 or more attendee fields, null-terminated
                                ;      strings. (Each can be NULL by itself.)
                                ;
        SentToSubDestinationEntry	ends

STSDE_UID is the unique event ID specified in ES_uniqueID or RES_uniqueID. STSDE_count contains the number of booking ID's in STSDE_destinations, excluding send attendee field. STSDE_null is always 0 for searching purpose. STSDE_destinations is a series of null-terminated strings. The first such string is the sender Attendee field. This will be a null string if the event is not created by a booking request. After the sender field are the Attendee fields used to send booking requests for this event. If this event has been sent to 3 destinations, then STSDE_count will be 3, and three strings will follow the sender field. Any of the strings may be null. Note that STSDE_count does not include the sender field.

For example, for an event booked to other people, STSDE_destinations is "\0engr1@geoworks.com\0\0engr2@geoworks.com\07" and STSDE_count is 3 (excluding the first NULL character). (\0 denotes NULL character.) It has no sender Attendee field as the first character is NULL. The first recipient's ID is "engr1@geoworks.com/", the second has no ID (thus just NULL character) and the third is "engr2@geoworks.com".

For another example of an event booked by other people, STSDE_destinations is "server1@geoworks.com\0mgr1@geoworks.com\0mgr2@geoworks.com\0\0\0" and STSDE_count is 4. The sender attendee field becomes "server1@geoworks.com". The first recipient's ID is "mgr1@geoworks.com", the second "mgr2@geoworks.com", while the third and fourth ones have no ID's.

Please note that the strings in STSDE_destinations should not have any prevailing and trailing white space. White space is only allowed within the string.

The exported file syntax for the sub destinations is explained in Text file syntax update.

Pending event info (N9110 Communicator)

When Calendar receives booking requests, the user can postpone replies to sender whether to accept or deny the requests by selecting option "Hold". The events being held are stored in an unsorted chunk array referenced by VMBlock YMH_pendingApptArrayBlock and ChunkHandle YMH_pendingApptArrayChunk. Each chunk array element is a variable-size structure:

PendingEventStruct	struct
    PES_smsNum		SMSNumberType	; SMS number of recipient
    PES_versitText	label TCHAR	; start of versit text
PendingEventStruct	ends

where PES_smsNum is the sender SMS number and PES_versitText is the null-terminated versit text originally sent with the SMS message.

The exported file syntax for pending events is explained in Text file syntax update.

Text file syntax update

1. File protocol

File protocol has been changed from 2.0 to 3.0 so there will be a new field in each exported file.

    [FILE-PROTOCOL]
    3.0

In the N9110 Communicator, the file protocol is 4.0:

    [FILE-PROTOCOL]
    4.0

If the protocol is not specified, it is assumed the text file comes from a responder device (2.0)

2. Next Event ID

This is a new field in each exported file indicating the next event ID to be assigned to a new event. It is YMH_nextEventID of YearMapHeader. This ID has to be preserved during import and export. See Unique ID info for more details.

    [NEXT-EVENT-ID]
    131074

3. New fields for all events

A few important fields are added to the data file, and they must be exported / imported so that the user can continue to receive appointment reply if s/he exports and imports the data file.

    [EVENT-ID]		Unique ID of the event in the file
    138751

    [BOOK-ID]		Count of appointments made on a single event.
    3

    [SENDER-SMS]	If the event is created from smart SMS,
    +555-33545		the sms number of sender...

    [SENDER-EVENT-ID]	and event ID...
    150003

    [SENDER-BOOK-ID]	and book ID...
    6

4. Sent-to information

For each sent-to information, the exported fields look like this:

    [SENT-TO]
    Princess Lizzy		Name of recipient, at most 20 chars long
    +555-9898 			SMS of recipient
    13.02.1997 13:05		Date and time
    4				Book-id of this appointment for the
                    current event
    ACCEPTED | DISCARDED |
     FORCED | NO-REPLY |
     FORCED-AND-ACCEPTED
    100033			If the event is accepted: the event-ID
                    on the recipient's device

If there are multiple sent-to information for an event, they are listed one after the other.

5. Pending event information

Each pending event is independent of to-do event and other calendar events. The exported field of each pending event starts with [HOLD] , and then sender SMS on the next line, followed by multiple lines of pending event versit text, which looks like:

[HOLD]
+555-9898			Sender SMS
//SCKE4E4
BEGIN:VCALENDAR
VERSION:1.0
BEGIN:VEVENT
CATEGORIES:APPOINTMENT
STATUS:NEEDS ACTION
DTSTART:19970903T080000
SUMMARY:Event appointment
DESCRIPTION:test test
UID:0
END:VEVENT
END:VCALENDAR
[HOLD-END]

The end of versit text is marked by [HOLD-END] .

6. Server Destination Info

Each piece of server destination info is contained in an element SentToSubDestinationEntry of the huge array in YMH_serverDestinationArray. Its format in exported file looks like:

[SENT-TO-DEST]
138751	; Unique ID of the event
4	; count of booking IDs
0:server1@geoworks.com		; sender attendee info, always start
; with 0
1:mgr1@geoworks.com		; first recipient ID
3:mgr2@geoworks.com		; second recipient has no ID, but
; the third has ID
[SENT-TO-DEST-END]

To convert the above structure, STSDE_destinations will become "server1@geoworks.com\0mgr1@geoworks.com\0\0mgr2@geoworks.com\0\0". There are 4 booking IDs, but only 2 of them have real IDs. So the other two are simply empty null strings.

[SENT-TO-DEST]
138752	; Unique ID of the event
4	; count of booking IDs
2:mgr1@geoworks.com		; first recipient ID, no sender info
3:mgr2@geoworks.com		; second recipient has no ID, but
; the third has ID
[SENT-TO-DEST-END]

This example will yield STSDE_destinations as "\0\0mgr1@geoworks.com\0mgr2@geoworks.com\0\0". The first NULL indicates no sender attendee info, while the last NULL indicates that the fourth recipient has no booking ID.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Reference


Contacts

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Setting up a Contact List
| One-page Version of this Chapter for Easy Printing

The Nokia 9110i Communicator uses Contact databases to store "address book" information. Every time the user enters information about how a place may be reached by phone, fax, or e-mail that information is stored in a Contact database, accessed via the contdb library.

Whenever you prompt the user for information about where to send something, you'll probably use a ContactList. This will allow the user to choose which of their contacts should be the recipient.

There are two libraries which exist to keep track of Contact databases and provide an API for the applications which share access to it.

  • The contdb library keeps track of the contact information, and may be used to look up or edit contact information. It also provides a controller which allows the user to choose a contact from a list of existing contacts.
  • The contLog library provides an API for logging what sorts of communication has been made with the entities described in the Contact database. It also provides a controller which allows the user to select a recently contacted contact.

Contact database are Foam databases, and some operations upon the database will make use of Foam database commands. We will not explore the meanings of these commands closely here, but will show how they are used in the context of Contact databases.

Before reading this chapter, you should be familiar with controllers, Object-Oriented Programming, and the GEOS messaging system. It helps if you try out the "Contacts" built-in application, which demonstrates much of the functionality we'll discuss here. The appl/SDK_9000/Contact/ sample application gives full source for the most useful Contact database operations.

Most programmers first encounter the contdb and ContLog libraries when they need to intercept a message associated with a ContactList or perhaps a RecentContactsControl object. These object classes provide the user with a standard way to choose a contact.

There are other controllers which allow the user to choose an SMS contact. SMAddressControlClass allows the user to choose a recipient for a SMS message. This class should cover some applications' needs when it comes to allowing the user to choose a contact. However, if you need a custom control, you may wish to use a ContactList and/or a RecentContactsSMSControl.

A ContactList presents the user with a list of known contacts. The user may choose a name from the list. In Setting up a Contact List, we show how to set up such an object and get the phone number of the contact the user selects.

The RecentContactsSMSControl presents the user with a list containing names and numbers associated with contacts that have recently had SMS transactions with the user. The user may choose a name or number from the list. In Choosing Contacts From a Log, we show how to set up such an object and get the phone number of the contact the user selects.

1 Setting up a Contact List
1.1 Multiple-Selection ContactLists and Groups
1.2 Remembering the Search
1.3 Detecting Unknown Phone Numbers
1.4 Alternate Messages
2 Choosing Contacts From a Log
3 Using a Dedicated Contact
4 Logging Calls
5 To Get More Info on a Contact
6 The User's Own Data

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Setting up a Contact List


Setting up a Contact List

Contacts: 1 Setting up a Contact List

| Up: GEOS SDK TechDocs | Up | Down | Prev: Contacts | Next: 2 Choosing Contacts From a Log

ContactListClass, CONTACT_LIST_NOTIFY_CONTACT_SELECTED_MSG, ContactGetDBHandle(), FoamDBGetRecordFromID(), FoamDBGetFieldData(), FoamDBDiscardRecord(), ContactReleaseDBHandle()

To present the user with a list of Contacts, do the following:

Make sure that your .gp file contains lines requiring the foamdb and contdb libraries:

library contdb
library foamdb

In your source code, create a ContactList object. The following ContactList object declaration allows the user to pick a contact. If the contact has more than one GSM phone number, the controller will make the user choose one of the fields.

  • When the user chooses a contact, the ContactList will send a message to an object. The ContactList's GCI_output and ATTR_CONTACT_LIST_NOTIFY_CONTACT_SELECTED_MSG specify the object and message. In the example below, the application's process object will handle a message. We will discuss the message in more detail below.
  • To make the ContactList SMS-specific, set its ATTR_CONTACT_LIST_CALL_TYPE to CCT_SMS as shown below. This will gray out contacts which do not have SMS phone numbers.
  • To include the Select button, set the CLF_SELECT_CONTACT_NUMBER feature flag in ATTR_GEN_CONTROL_REQUIRE_UI.
  • To include the ContactList's Search bar, set the CLF_SEARCH feature flag in ATTR_GEN_CONTROL_REQUIRE_UI; to exclude the Search bar, exclude the UI.
  • Optionally, you can use the standard Contact List bitmap by making your ContactList a ComplexMoniker and setting its CMI_iconBitmap field to CMB_CONTACT_LIST.

Putting this together, we end up with the following:

@object ContactListClass MyContactList = {
  GI_visMoniker = "I want to talk to...";
  GCI_output = (TO_PROCESS);
  ATTR_CONTACT_LIST_CALL_TYPE = CCT_SMS;
  ATTR_GEN_CONTROL_REQUIRE_UI = CLF_SEARCH |
                       CLF_SELECT_CONTACT_NUMBER ;
  HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
  ATTR_CONTACT_LIST_NOTIFY_CONTACT_SELECTED_MSG =
                        MSG_MYPROCESS_SEND_MESSAGE ;
}

With this configuration, the ContactList will send a MSG_MYPROCESS_SEND_MESSAGE message to your process object when the user chooses a contact. A sample handler for this message is shown below (Handling the ContactList's "Selected" Message). The message will conform to the prototype:

@prototype void
    CONTACT_LIST_NOTIFY_CONTACT_SELECTED_MSG(
                                   dword recordID,
                                   word fieldID);

The message handler should carry out the following steps:

  1. Get the current Contact database's handle. To do this, call ContactGetDBHandle(). This routine takes no arguments, and returns the Contact database's handle.
  2. Get the handle of the record by calling FoamDBGetRecordFromID(). This routine takes the Contact's database handle and the record's RecordID value. It allocates a block of memory to hold the record's data and returns the record's handle.
  3. To extract the contact's GSM phone number, call FoamDBGetFieldData(). Pass the database handle, the record handle, the FieldID associated with the GSM number field (this number is supplied by the ContactList when it sends this message), a buffer to write the name to, and the size of that buffer. The function fills in the buffer with the phone number string, and returns the length of that string. The returned string might not be null-terminated; thus you will either need to keep track of the returned string length, or else terminate the string.
  4. Now that you're done with the record, let the database know by calling FoamDBDiscardRecord(). This routine takes the Contact's database handle and the record's handle as arguments.
  5. Now that you're done with the database, release its handle by calling ContactReleaseDBHandle().

The example below shows a way of carrying out the above set of steps.

Code Display Handling the ContactList's "Selected" Message

@class MyProcessClass, GenProcessClass;
    @message (CONTACT_LIST_NOTIFY_CONTACT_SELECTED_MSG)
                        MSG_MYPROCESS_SEND_MESSAGE;
@endc
@method MyProcessClass, MSG_MYPROCESS_SEND_MESSAGE {
    TCHAR           theNumber[31];
    TCHAR           theName[MAX_NAME_DATA_LEN+1];
    word            numLen;
    VMFileHandle    CDBHandle;
    MemHandle       theRecord;
        /* Get the Contact database's handle */
    CDBHandle = ContactGetDBHandle();
        /* Get the handle to the record we're interested in */
    theRecord = FoamDBGetRecordFromID( CDBHandle, recordID );
        /* Get the GSM phone number string; null-terminate it. */
    FoamDBGetFieldData( CDBHandle, theRecord, fieldID, theNumber, 30);
    theNumber[numLen] = `\000'; /* add null terminator */
        /* We'll copy the name to theName buffer. */
    ContactGetName(theRecord, theName);

        /* If you need more information about the contact
         * (e.g., business name, job title), this is a good place
         * to retrieve it. */

    /* Do what you like with theNumber and theName */
        /* We're done with this record. */
    FoamDBDiscardRecord(CDBHandle, theRecord );
        /* We're done with the database. */
    ContactReleaseDBHandle();
}

Multiple-Selection ContactLists and Groups

Remembering the Search

Detecting Unknown Phone Numbers

Alternate Selection Messages

| Up: GEOS SDK TechDocs | Up | Down | Prev: Contacts | Next: 2 Choosing Contacts From a Log


Multiple-Selection ContactLists

Contacts: 1.1 Setting up a Contact List: Multiple-Selection ContactLists

| Up: GEOS SDK TechDocs | Up | Prev: 1 Setting up a Contact List | Next: 1.2 Remembering the Search

Recent versions of the ContDB library support ContactLists which allow the user to select more than one contact at a time, either by selecting contacts one by one or by choosing contact groups. This feature is available on devices with version numbers "Responder Build 4...." and higher. See Software Version Number for information about finding out the software version of the user's device.)

To create such a ContactList, give it the hint HINT_CONTACT_LIST_NON_EXCLUSIVE_LIST. If your application is running on a device recent enough to support contact groups, then ATTR_CONTACT_LIST_ALLOW_GROUP_AS_CALL_TYPE will allow the user to select a group.

There are two ways to obtain the list of selected contacts:

  • Set the flag CLF_SELECT_CONTACT_NUMBER in ATTR_GEN_CONTROL_REQUIRE_UI, and set ATTR_CONTACT_LIST_NOTIFY_MULTIPLE_CONTACTS_SELECTED_MSG to the message which will be sent whenever one or more contacts are selected from the contact list. This message must be of the prototype:
    •         @prototype void
            CONTACT_LIST_NOTIFY_MULTIPLE_CONTACTS_SELECTED_MSG(
                MemHandle block = cx,
                word num = dx);

    The passed memory handle block will contain the set of ContactListSelectedEntrys, and num will contain the number of these entries. Your method must free the passed memory block.

  • To get the current selection without relying on the Select button, you will need to query the ContactList directly. Call MSG_CONTACT_LIST_GET_IDS_OF_SELECTED_RECORDS; the ContactList will return a ContactListSelectedRecordsData structure containing the number of selected entries and the handle to a memory block containing the IDs of the selected entries stored as ContactListSelectedEntry structures.
| Up: GEOS SDK TechDocs | Up | Prev: 1 Setting up a Contact List | Next: 1.2 Remembering the Search


Remembering the Search

Contacts: 1.2 Setting up a Contact List: Remembering the Search

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Multiple-Selection ContactLists | Next: 1.3 Detecting Unknown Phone Numbers

Normally, a ContactList will lose track of the user's current search if the focus moves to another object. To force the list to remember the current search, send it a MSG_CONTACT_LIST_PRESERVE_SEARCH_ON_NEXT_LOST_FOCUS before it loses the focus. (This message is available on devices with version numbers "Responder Build 4...." and higher. See Software Version Number for information about finding out the software version of the user's device.)

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Multiple-Selection ContactLists | Next: 1.3 Detecting Unknown Phone Numbers


Detecting Unknown Phone Numbers

Contacts: 1 Setting up a Contact List: 1.3 Detecting Unknown Phone Numbers

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Remembering the Search | Next: 1.4 Alternate Selection Messages

If the user enters a valid phone number in the search box of a ContactList, but that number is not found, an application may request that the ContactList send out a notification using a variable data field. (This variable data field is available on devices with version numbers "Responder Build 4...." and higher. See Software Version Number for information about finding out the software version of the user's device.)

Give the ContactList a ATTR_CONTACT_LIST_NOTIFY_NUMBER_ENTERED_MSG variable data field. This field's data is a message.

The message should have parameters:

  void MSG_MY_CL_NOTIFY( MemHandle phNumber = cx);

The passed block contains the phone number string. The message's recipient is responsible for freeing the passed block.

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Remembering the Search | Next: 1.4 Alternate Selection Messages


1.4 Alternate Selection Messages

Contacts: 1 Setting up a Contact List : 1.4 Alternate Selection Messages

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Detecting Unknown Phone Numbers | Next: 2: Choosing Contacts From a Log

Build 4.3.7 of the Responder (see versions for more information about software versions) supports two methods for selecting contacts from a CCT_PHONE type of contact controller list. If the user quickly presses the Select trigger (F1 on the PC emulator), then, depending on the circumstances, the message specified by ATTR_CONTACT_LIST_NOTIFY_CONTACT_SELECTED_MSG, ATTR_CONTACT_LIST_NOTIFY_NUMBER_ENTERED_MSG, or ATTR_CONTACT_LIST_NOTIFY_MULTIPLE_CONTACTS_SELECTED_MSG is sent. If the user holds the Select trigger for a longer time, an alternate type of message analogous to the normal selection message is sent. These alternate messages include:

ATTR_CONTACT_LIST_ALTERNATE_NOTIFY_CONTACT_SELECTED_MSG
Set this to the message which will be called when the user selects a contact from the list after holding the Select trigger for a long duration. If more than one contact was selected then this message returns the first selection. This message must follow the prototype:
@prototype void CONTACT_LIST_ALTERNATE_NOTIFY_CONTACT_SELECTED_MSG(
                                dword recordID = cx:dx,
                                word fieldID = bp);
fieldID is the ID of the field containing the selected GSM phone number of the record with the ID recordID. This is the same API as that of the normal selection message; you may use the same handler for both messages.
ATTR_CONTACT_LIST_ALTERNATE_NOTIFY_NUMBER_ENTERED_MSG
Set this to the message which should be sent when the user has entered a valid phone number in the search field which does not match any record in the contact list and the user has held the trigger for a long duration. For this message to be sent, CLF_SELECT_CONTACT_NUMBER must be set. The message must follow the prototype:
@prototype void CONTACT_LIST_ALTERNATE_NOTIFY_NUMBER_ENTERED_MSG(
                                MemHandle block = cx);
block will contain the null-terminated phone number.
ATTR_CONTACT_LIST_ALTERNATE_NOTIFY_MULTIPLE_CONTACTS_SELECTED_MSG
Set this to the message which will be sent when the user selects one or more contacts from the list and holds the Select trigger for a long duration. Note that CLF_SELECT_CONTACT_NUMBER and HINT_NON_EXCLUSIVE_LIST must be present for multiple selection contact lists. This message must follow the prototype:
@prototype void CONTACT_LIST_ALTERNATE_NOTIFY_MULTIPLE_CONTACTS_SELECTED_MSG(
                                MemHandle block = cx,
                                word num = dx);
block will contain the num ContactListSelectedEntry selected items.
| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Detecting Unknown Phone Numbers | Next: 2: Choosing Contacts From a Log


Choosing Contacts From a Log

Contacts: 2 Choosing Contacts From a Log

| Up: GEOS SDK TechDocs | Up | Prev: 1 Setting up a Contact List | Next: 3 Using a Dedicated Contact
RecentContactsSMSControlClass, CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG

For a list of recent SMS contacts, use a RecentContactsSMSController. You will need the following line in your .gp file:

library		contlog

The following setup would allow the user to pick a phone number from a list of recently called SMS numbers, sending a MSG_MYPROCESS_SEND_RECENT message to the application's process object:

    @object RecentContactsSMSControlClass MyLog = {
        GCI_output = ( TO_PROCESS );
        ATTR_RECENT_CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG =
            MSG_MYPROCESS_SEND_RECENT;
    }

With this setup, when the user chooses a phone number from the log, a MSG_MYPROCESS_SEND_RECENT message will be sent to the process object, which should handle the message. The message should conform to the CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG prototype.

@prototype void
    CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG(
                           RecentContactsData *data)
typedef struct {
    dword               RCD_contactID;
    NameOrNumber        RCD_number;
} RecentContactsData;

The data argument's RCD_number field contains the GSM phone number of the selected contact. The RCD_contactID contains either:

  • the ContactID of a record in the device's contact database (not of a database on a memory card), or
  • LECI_INVALID_CONTACT_ID

The code example below shows a handler that extract's the name associated with the selected contact. It makes use of functions described in Setting up a Contact List.

Code Display: Handling the RecentContacts "Selected" Message

@class MyProcessClass, GenProcessClass;
    @message (CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG)
                        MSG_MYPROCESS_SEND_RECENT;
@endc
@method MyProcessClass, MSG_MYPROCESS_SEND_RECENT {
        /* Note: the code in this example is only useful if you need
         * the contact's name. If all you need is the GSM phone number,
         * none of this code is necessary. The phone number string is in
         * data->RCD_number.
         *
         * If you do need the following code, you will need to make sure
         * that your .gp file includes the lines
                library contdb
                library foamdb
         */

        TCHAR           theName[MAX_NAME_DATA_LEN+1];
        VMFileHandle    CDBHandle;
        MemHandle       theRecord;
        word            oldOverride;

        /* If the user chose a phone number that doesn't have a contact
         * associated with it, the contact's RecordID value is
         * LECI_INVALID_CONTACT_ID, signalling an invalid contact. */

        if ( data->RCD_contactID != LECI_INVALID_CONTACT_ID) {
           oldOverride = ContactSetOverrideDB(CONTACT_DEVICE_DB_NAME);
           CDBHandle = ContactGetDBHandle();
           theRecord = FoamDBGetRecordFromID( CDBHandle,
                                              data->RCD_contactID );
           ContactGetName(theRecord, theName);

           /* If you need more information about the contact
            * (e.g., business name, job title), this is a good place
            * to retrieve it. */

         FoamDBDiscardRecord(CDBHandle, theRecord);
         ContactReleaseDBHandle();
         ContactRestoreOverrideDB(oldOverride);
       }

      /* do something useful with data->RCD_number and theName */
}

Recent versions of the Nokia 9000i Communicator allow fax and SMS contact lists to support multiple selections. Set the RCFCF_MULTIPLE_SELECTION or RCMCF_MULTIPLE_SELECTION bit to enable multiple selection for, respectively, a fax or SMS recent contact list and specify the message ATTR_RECENT_CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG which will be sent when the user chooses an item from the list.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Setting up a Contact List | Next: 3 Using a Dedicated Contact


Using a Dedicated Contact

Contacts: 3 Using a Dedicated Contact

| Up: GEOS SDK TechDocs | Up | Prev: 2 Choosing Contacts From a Log | Next: 4 Logging Calls
ContactMatchName(), ContactEnsureField()

Perhaps you're writing a special-purpose application that communicates with just one contact. Perhaps a carry-out restaurant chain has contracted you to write an application that the user can use to place an order over the phone. The program would only need to make contact with one place: the local restaurant's GSM phone number.

In this case, you wouldn't want to provide UI for choosing a contact--your program is only interested in one dedicated contact. Still, it's worthwhile to store the contact in the Contact database--if the restaurant's phone number changes, the user can enter the corrected number using the Contact Manager built-in application.

To find the Contact record for a particular contact, you will need to make sure that your .gp file contains the following lines:

library contdb
library foamdb

Finding the "Stuckey's Snack Shack" Contact, below, shows how you could find a dedicated contact in a Contact database and extract the phone number associated with that contact.

To locate a dedicated contact and extract its phone number, carry out the following steps:

  1. Which of the user's Contact databases do you wish to search? If the user has a memory card installed, there may be several databases to search. Perhaps you want to search all of them. Perhaps you want to ignore databases on the memory card, and only search the device's own database.
  2. For each database, to get the RecordID of the contact's record, call ContactMatchName(). This routine takes a search string (pass the contact's name), a ContdbFieldType (pass CFT_NAME, since we're searching by name), a flag (pass a non-zero value to specify that you're only interested in one matching record), a pointer to a FieldID buffer to fill in and a pointer to a word value to fill in. It returns the RecordID of the contact (or -1 if the contact wasn't found). It also fills in the FieldID buffer with the contact's name field FieldID and fills in the word value with the number of matches found; neither of these values is important to our purpose.

    3. If you're searching multiple databases and the current database doesn't contain the record, go on to the next database.

  3. Get the Contact database's handle. To do this, call ContactGetDBHandle(). This routine takes no arguments and returns the database's handle.
  4. Get the record's handle by calling FoamDBGetRecordFromID(). This routine takes the Contact database's handle and the record's RecordID. It returns the record's memory handle.
  5. Get the FieldID of the record's GSM phone number field. In the example below, we assume that the record has only one phone number field, and that this is the field we want. For a more rigorous approach, we could examine each field to make sure it was named "Tel (GSM)". For an example of how one might examine all the fields of a record, see To Get More Info on a Contact.

    6. For our shortcut, we use the ContactEnsureField() function. This function takes as arguments a record handle, a field name string (in our example, we pass a null optr to specify that we should use the default field name, and the ContdbFieldType of the field to find ( pass CFT_PHONE). The function returns a FieldID.

  6. To extract the phone number data, call FoamDBGetFieldData(). Pass the database handle, the record handle, the FieldID, a buffer to write the name to, and the size of that buffer. The function fills in the buffer with the phone number string, and returns the length of that string. The returned string might not be null-terminated; thus you will either need to keep track of the returned string length, or else terminate the string.
  7. Now that you're done with the record, let the database know by calling FoamDBDiscardRecord(). This routine takes the Contact's database handle and the record's handle as arguments.
  8. Now that you're done with the database, release its handle by calling ContactReleaseDBHandle().

If the contact is not found, there are a couple of approaches the application can take:

  • Create a new record with the proper name and a guess at the phone number. The code example below illustrates this approach.
  • Signal to the user that an error has occured and halt the operation.

Code Display: Finding the "Stuckey's Snack Shack" Contact

@method ContactProcessClass, MSG_CONTACT_PROCESS_SEND_TO_STUCKEYS
{

    TCHAR        nameStr[] = "Stuckey's Snack Shack";
    TCHAR        numStr[] = "+1-800-788-2539";
    VMFileHandle hCDB;
    RecordID     recordID;
    FieldID      fieldID;
    word         numMatches;
    dword        recordIndex;
    word         len;
    MemHandle    hRecord;
    int          dbOverride = TRUE;
    FileLongName dbName;
    int          numDBs;
    int          dbNumber;
    word         oldOverride;

    /*
     * Check the database on the device and the databases in the memory card.
     */
    /*
     * First try the db on the device.
     */
    oldOverride = ContactSetOverrideDB(CONTACT_DEVICE_DB_NAME);
    hCDB = ContactGetDBHandle();   /* device DB */
    recordID = ContactMatchName( nameStr, CFT_NAME, TRUE,
                                 &fieldID, &numMatches );
    ContactReleaseDBHandle();    /* device DB */

    if ( recordID == LECI_INVALID_CONTACT_ID) {
        /*
         * Now try each db on the memory card.
         */
        numDBs = ContactGetNumDBsOnMemoryCard();
        for (dbNumber = 0 ; dbNumber < numDBs; dbNumber++) {
            ContactGetDBName(dbNumber, dbName);
            ContactSetOverrideDB(dbName);
            hCDB = ContactGetDBHandle();
            recordID = ContactMatchName( nameStr, CFT_NAME, TRUE,
                                     &fieldID, &numMatches );
            ContactReleaseDBHandle();
            if ( recordID != LECI_INVALID_CONTACT_ID) {
                break;
            }
            /* We didn't find it, loop up and try again. */
        }
        if ( recordID == LECI_INVALID_CONTACT_ID) {
            ContactRestoreOverrideDB(oldOverride);
            dbOverride = FALSE;
        }
    }

    hCDB = ContactGetDBHandle();

    if ( LECI_INVALID_CONTACT_ID == recordID ) {
        /* No such record in contact database -- let's add it. */
        dbOverride = FALSE;
        hRecord = ContactCreateRecordFromTemplate();
        fieldID = ContactEnsureField( hRecord, NullOptr, CFT_NAME );
        FoamDBSetFieldData( hCDB, hRecord, fieldID,
                            nameStr, strlen( nameStr ) );
        fieldID = ContactEnsureField( hRecord, @TelGSMStr, CFT_PHONE );
        FoamDBSetFieldData( hCDB, hRecord, fieldID,
                            numStr, strlen( numStr ) );
        recordIndex = ContactSaveRecord( hRecord );
        hRecord = FoamDBGetVisibleRecord( hCDB, recordIndex );

    }
    else {
        /* Contact exists: get record from ID. */
        hRecord = FoamDBGetRecordFromID( hCDB, recordID );
    }

    /* Record was either found or created: get the GSM phone number. */
    fieldID = ContactEnsureField( hRecord, @TelGSMStr, CFT_PHONE );
    len = FoamDBGetFieldData( hCDB, hRecord, fieldID,
                              numStr, MAX_NUMBER_FIELD_DATA_LEN );
    numStr[len] = '\0';

      /*
       * Here, the application can do something with the contact's data...
       */

    ContactReleaseDBHandle();
    if (dbOverride ) {
        ContactRestoreOverrideDB(oldOverride);
    }

} /* MSG_CONTACT_PROCESS_SEND_TO_STUCKEYS */

To allow the user to edit the contact information for this single contact, use a ContactEdit object. To specify the contact that the user will work with, you will need its handle. Pass the handle as an argument to the MSG_CONTACT_EDIT_DISPLAY_RECORD message. (Don't call ContactSaveRecord(), FoamDBDeleteRecord(), or FoamDBDiscardRecord() when done with this handle; the controller will do so.)

| Up: GEOS SDK TechDocs | Up | Prev: 2 Choosing Contacts From a Log | Next: 4 Logging Calls


Logging Calls

Contacts: 4 Logging Calls

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using a Dedicated Contact | Next: 5 To Get More Info on a Contact
LogAddEntry()

If you wish to log incoming or outgoing messages, you will use the LogAddEntry() function, provided by the contlog library. Make sure that you your.gp file contains the line

library contlog

To log a call, carry out the following steps:

  1. Before making the call, set up a LogEntry structure and call LogAddEntry(). The code shown in Logging an SMS Call would be appropriate for logging an outgoing message; to log an incoming message, substitute LED_RECEIVED for LED_SENT.
  2. Make the call.
  3. When the call is done, call LogAddEntry() again, passing the same LogEntry structure as before, after writing the duration (in seconds) in the LogEntry's LE_duration field. (The previous invokation of LogAddEntry() will have filled in the LogEntry's LE_flags field with correct values.)

Code Display: Logging an SMS Call

LogEntry            MyEntry ;
dword               duration;
TimerDateAndTime    tdat;
TimerGetDateAndTime(&tdat);
duration = TimerGetCount();

strcpy( MyEntry->LE_number, theNumber);
MyEntry.LE_contactID = recordID; /* This is useful only if recordID
                                  * refers to a record in the device contact
                                  * databse--not a database on a memory card.
                                  * If this is not the case, then use
                                  * LECI_INVALID_CONTACT_ID*/
MyEntry.LE_type = LET_SMS;
MyEntry.LE_direction = LED_SENT;
MyEntry.LE_duration = 1;
MyEntry.LE_dateTime.DAT_year = tdat.TDAT_year;
MyEntry.LE_datetime.DAT_month = tdat.TDAT_month;
MyEntry.LE_datetime.DAT_day = tdat.TDAT_day;
MyEntry.LE_datetime.DAT_hour = tdat.TDAT_hours;
MyEntry.LE_datetime.DAT_minute = tdat.TDAT_minutes;

LogAddEntry( &MyEntry );

/* Send the message, and when done...*/

MyEntry.LE_duration = TimerGetCount() - duration;
LogAddEntry( &MyEntry );

When using the Contact Log library to log a contact, if you don't know the phone number, you can pass LEF_WILDCARD_NUMBER to LogAddEntry() to specify that this call's number should match any phone number. This is only allowed on devices with version numbers "Responder Build 4...." and higher. See Software Version Number for information about finding out the software version of the user's device.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using a Dedicated Contact | Next: 5 To Get More Info on a Contact


To Get More Info on a Contact

Contacts: 5 To Get More Info about a Contact

| Up: GEOS SDK TechDocs | Up | Prev: 4 Logging Calls | Next: 6 The User's Own Data
FoamDBFieldEnum(), FoamDBGetFieldType(), FoamDBGetFieldName()

In previous examples, we've shown how to get a selected contact's name and GSM phone number. It is possible to get more information about a contact given if you know which database it's in and its RecordID number.

In the examples shown in Handling the ContactList's "Selected" Message, Handling the RecentContacts "Selected" Message, and Finding the "Stuckey's Snack Shack" Contact there is a comment noting a good place to get more information about a contact. If, for some reason, you need to find out information about a Contact in a context other than shown in one of those examples,

  1. The database containing the contact must be the current contact database; in the contexts described above, this will be true. You will need the record's RecordID number.
    To query a ContactList object for the RecordID of its selected record, use the MSG_CONTACT_LIST_GET_ID_OF_SELECTED_RECORD message. This message takes no arguments, and returns a RecordID.
  2. Gain access to the current Contact database by calling ContactGetDBHandle(), storing the returned handle.
  3. To access the database record corresponding to the selected contact, call FoamDBGetRecordFromID(). This returns the record's handle.
  4. Get the contact information you need. Information about how you might do this is described in the paragraphs following this checklist.
  5. When you're done with the record handle, call FoamDBDiscardRecord() to free the handle.
  6. Be sure to call ContactReleaseDBHandle() to signal you're done with the database. You shouldn't keep the handle around for long; doing so prevents the file from being closed.

To find out any other sort of information about the contact, you'll need to access one of the fields of the record. Fields within a Foam DB record (the Contact DB is a Foam DB) are identified by a FieldID number.

The FoamDBFieldEnum() function provides a way to scan all the fields of a record. The following code snippet shows how you might use FoamDBFieldEnum() to search for all of a contact's voice phone numbers. FoamDBFieldEnum() calls a callback routine for each of the record's fields. FoamDBFieldEnum() takes as arguments a record handle, a pointer to a data buffer (this pointer will be passed to the callback routine; it gives you a way to pass data to the callback; in our example, we pass the record handle in the buffer), and a pointer to a callback function. The callback function is passed a FieldHeader structure and a pointer to the data buffer that was passed toFoamDBFieldEnum(). From the FieldHeader, it can get the FieldID from the FieldHeader's FH_id field. To get information about each field, use the FoamDBGetFieldType(), FoamDBGetFieldName(), and/or FoamDBGetFieldData() routines. Each of these routines takes the contact database handle, a record handle, a FieldID, and a pointer to a buffer to fill with a return value.

Code Display: Checking All Fields of a Contact

In this example, we check all fields of a contact and build up an array with the names and data for those fields that correspond to voice phone numbers. We know that the database containing the contact is the current contact database. We know the contact's RecordID value.

void BuildNumberList( RecordID recordID ) {
    /* Scan all fields of the Contact record */
    /* For each one that corresponds to a voice phone number, store
     * the field's name (e.g., "Phone") and data (e.g., "555-1212") in
     * the global Data array. */

    VMFileHandle 		CDBHandle;
    MemHandle		curRecord;
    CDBHandle = ContactGetDBHandle();
    curRecord = FoamDBGetRecordFromID( CDBHandle, recordID );
    NumberOfNumbers = 0			/* Global variable:
                    number of elements in Data array*/
    FoamDBFieldEnum( curRecord, &curRecord, &BuildNumberListCallback );
    FoamDBDiscardRecord( CDBhandle, curRecord) ; /* Done with the record */
    ContactReleaseDBHandle(); /* Done with the Contact DB for now */
}
Boolean _pascal BuildNumberListCallback( FieldHeader *field, void *enumData ) {
    VMFileHandle		CDBHandle;
    byte		fieldType;
    MemHandle		curRecord = *((MemHandle *) enumData );
    CDBandle = ContactGetDBHandle();
    FoamDBGetFieldType( CDBHandle, curRecord, field->FH_id, &fieldType);
    if (fieldType == CFT_PHONE ) { /* For each voice phone number */
                /* To look for something other than a phone
                 * number, we would use a different
                 * ContdbFieldType value. */
                    /* Grab the field name */
      FoamDBGetFieldName( CDBHandle, curRecord, field->FH_id,
            Data[NumberOfNumbers].fName, MAX_FIELD_LABEL_LEN);
                    /* Grab the field data */
                    /* If the data has zero length, func will
                     * return zero. */
      if (FoamDBGetFieldData( CDBHandle, curRecord, field->FH_id,
            Data[NumberOfNumbers].fData, MAX_FIELD_NUMBER_DATA_LEN)) {
                    /* If the data was not of zero length
                     * (wasn't just a blank field), then
                     * up our NumberOfNumbers. */
        NumberOfNumbers++;
      }
    }
    ContactReleaseDBHandle(); 	/* We're done with the database */
                /* if we return non-zero, we'll stop Enum'ing
                 * through the fields. Since we only have room
                 * for 10 fields in our array... */
    return( NumberOfNumbers > 10) ;
}

As shown above, to get the field data and field name associated with a field, use the FoamDBGetFieldData() and FoamDBFGetFieldName() functions. Many users will leave blank fields in their contact records; to test for a blank field, check FoamDBGetFieldData()'s return value; if it is zero, then the user has not filled in that field of the contact information.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Logging Calls | Next: 6 The User's Own Data


The User's Own Data

Contacts: 6 The User's Own Data

| Up: GEOS SDK TechDocs | Up | Prev: 5 To Get More Info on a Contact
ContactGetUserDataName(), ContactGetUserDataID()

Recent versions of the ContDB library provide routines which access the user's own contact information. These routines are only available on devices with version numbers "Responder Build 4...." and higher. See Software Version Number for information about finding out the software version of the user's device.

The ContDB library provides two routines useful for accessing the contact information the user has specified for themself. The ContactGetUserDataName() routine returns the user's name string. ContactGetUserDataID() returns the record ID number of a record which contains the user's contact information.

| Up: GEOS SDK TechDocs | Up | Prev: 5 To Get More Info on a Contact


Distributing Your Application

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Preparing Your Application
| One-page Version of this Chapter for Easy Printing

Once you've finished coding and testing your application, you'll want to distribute it to users. You'll need to take some time to figure out exactly what files make up your package--what files does the user need to use your product? Is it just an application geode? Perhaps an application geode plus a data file? If one or more of the files is an application, make sure it's really ready for distribution. The files that make up your software are known as a software package.

Once you've figured out what files make up your software package, there are two approaches to making your package available:

PC Download
If all users will have access to a 486 (or better) PC running MS Windows 3.1 or MS Windows 95 and a serial or IR connection, and if you have some way to get files onto that PC, you can have users install your software with the built-in Install/Remove software application.
You will provide a package description file (described in the Package Description File section, below), and will need to set up server software on the PC (described in Setting Up a PC Server ).
Web Site
You can make the files available on a web site. Users with the ability to browse the web can download your program easily, thanks to installation savvy in the built-in WWW application.
You will need to provide a package description file (described in the Package Description File section, below), and a bit more to get your server and files ready (described in Setting Up a Web Server ). You will bundle each software package into an Add-On Software file (a specially tagged archive).

No matter how you're making your software available, you'll need to create its Package Description File.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Preparing Your Application
| One-page Version of this Chapter for Easy Printing


Preparing Your Application

Distributing Your Application: 1 Preparing Your Application

| Up: GEOS SDK TechDocs | Up | Prev: Distributing Your Application | Next: 2 Package Description File

Before you package your files up for distribution, first make sure that any applications in the package are ready.

The applications must be ready to shut down when prompted by the system. If the application is running on the device, the user won't be able to un-install the application or to install a newer version. To make your application shut down as necessary, make sure that its FoamSubApplication object has HINT_APPLICATION_QUIT_ON_IACP_ALLOW_FILE_ACCESS in its declaration.

| Up: GEOS SDK TechDocs | Up | Prev: Distributing Your Application | Next: 2 Package Description File


Package Description File

Distributing Your Application: 2 Package Description File

| Up: GEOS SDK TechDocs | Up | Prev: 1 Preparing Your Application | Next: 3 Setting Up a PC Server

No matter how you're making your program available, you'll need to provide a "package description file," sometimes known as the . INS file. This is an ASCII text file which specifies important information about the files that make up your program:

  • Name of the package, which the Install/Remove software application displays to the user
  • Description of the package, which the Install/Remove software application displays to the user
  • Order in which files should be installed.
  • Names and locations of the files on the host machine
  • Where the files should be copied to on the client phone

This installation order is important in case of interrupted installation--if file A depends upon file B , but installation was interrupted after file A was brought over, but before file B was, then file A won't work, since it depends on an absent file. If another package is relying upon an older, successfully installed version of file A , then you've just broken that other file.

Use the following format when creating the package description file:

  • The file name must have a . INS suffix.
  • Whitespace at the end of a line is ignored; blank lines are ignored.
  • The first line must say "GEOS Package Description File v1.0"
  • The second line must contain the package name. This is the name by which the package will be identified to the user in the Install/Remove software application. The application doesn't have space for a long description, though--about 20 characters.
  • The third line must contain a description of the package. When the user views the package in the Install/Remove software application, they may read this description before deciding whether to download/install the package. The application doesn't have space for a long description, though--about 16 characters.
  • The fourth line must contain the size of the package, (in bytes). This is the combined size of all files in the package. This string must be at most 14 characters long. (Hopefully, this will be sufficient!) When computing the size, don't forget to include the size of the package description file itself. It's all right to over-estimate the size.
  • The fifth line must consist of a period (.). (This delimiter separates information about the package as a whole from information about the individual files.)
  • The rest of the file consists of pairs of lines:
    The first line of each pair contains the path of a file on the host machine (or within the Add-On Software file, which will be described later);
    The second line of each pair contains the destination of the file on the client phone.

    The host-machine location is relative to the location of the package description file. The destination location is relative to the base of the GEOS tree.

The line-pairs describing file locations must be ordered based upon the order in which the files should be installed. Libraries, fonts, etc. must be listed before the applications that depend on them.

Sample Package Description File

This example shows a package description file for the Santa package. On the host machine, the package description file is in the C:\NSERVER directory, and the files making up the package are in the C:\NSERVER\SANTA directory. The Santa application depends on the Sleigh library, and the Rudy font; the Sleigh library depends on the Donner and Blitzen libraries.

GEOS Package Description File v1.0

Santa v1.0
Gives gifts.
15769
.

santa\rudy.fnt
userdata\font\rudy.fnt
santa\donner.geo
system\donner.geo
santa\blitzen.geo
system\blitzen.geo
santa\sleigh.geo
system\sleigh.geo
santa\santa.geo
world\extrapps\santa.geo
| Up: GEOS SDK TechDocs | Up | Prev: 1 Preparing Your Application | Next: 3 Setting Up a PC Server


Setting Up a PC Server

Distributing Your Application: 3 Setting Up a PC Server

| Up: GEOS SDK TechDocs | Up | Prev: 2 Package Description File | Next: 4 Setting Up a Web Server

The Nokia 9000i Communicator comes with a disk containing the software by which a PC may be made to act as a server machine for the Install/Remove software application. The phone can make either a serial or IR connection with the PC. (For an IR connection, the PC must be running MS Windows 95 and the IR monitoring software which comes with it.)

  1. From MS Windows 3.1 or Windows 95, run the program called server on the floppy disk to install a program called NServer onto the PC.
  2. Run NServer to turn your PC into a server for the Nokia 9000i Communicator. This program monitors one of the PC's COM ports.
  3. Make sure that the NServer program is monitoring the right COM port--check its Settings.

If the user makes a serial or IR connection between a Nokia 9000i Communicator and a PC running the NServer, then runs the Install/Remove software application on the phone, they can install packages from the PC to their phone.

The Install/Remove software application needs to know which directory to scan for Package Definition files. By default, the Install/Remove software application will look in the PC's A:\NSERVER directory--allowing for easy installation directly from floppy. If the files reside on the PC's hard drive, they should probably reside in C:\NSERVER; if they don't, make sure that the user knows where to look for them. To avoid clutter, you might want to put each "package" of files into its own directory under the directory containing the package description files.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Package Description File | Next: 4 Setting Up a Web Server


Setting Up a Web Server

Distributing Your Application: 4 Setting Up a Web Server

| Up: GEOS SDK TechDocs | Up | Prev: 3 Setting Up a PC Server

To make Nokia 9000i Communicator software packages available on a web site, the web site administrator will do some set-up work so that the server knows how to tag Add-On Software files. Each software package must be bundled up into an Add-On Software file--a special kind of archive.

Standard Add-On Software files will have a . AOS suffix, and should be identified by the MIME-type application/x-nokia-9000-communicator-add-on-software . Exactly how this is set up depends upon your server software. NCSA's httpd daemon may use a file called mime.types , to which you would add a line reading

            application/x-nokia-9000-communicator-add-on-software 							aos

Once the server has been set up correctly, each software package needs to be bundled up into an Add-On Software file.

  1. Pack the package descriptor file and all files in the software package into one tar archive file. (This is done using the tar program. Tar is a standard Unix program; there is a public domain DOS tar program. The host-file-location lines in the package description file should describe locations within the tar file's directory structure.) The . INS file must be in the top level directory of the tar file so that it may be found. Read your tar documentation to find out how to create the archive. An appropriate invocation for one version of tar might be:
    2. tar cvf santa2.aos santa2.ins santa2/santa2.geo
  2. Give the tar file's filename the suffix . AOS so that it will be recognized as an Add-On Software file.
  3. Put the file up on your web server.

Users of the Nokia 9000i Communicator using the WWW built-in application will be able to use WWW to install the software package from your site. The WWW program will download the Add-On Software file, extract the files from the archive, and put them into the proper directories as directed by the package description file.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Setting Up a PC Server


Memory Levels

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Memory Levels
| One-page Version of this Chapter for Easy Printing

This chapter collects various utility routines and concepts that may not adequately be addressed within other chapters.

If you are writing an application that requires use of memory, you will want to keep track of how much memory is available on the Nokia 9000i/9110 Communicator's flash file system (FFS) or the N9110 Communicator's memory card.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Memory Levels
| One-page Version of this Chapter for Easy Printing


Memory Levels

Memory Levels: 1 Memory Levels

| Up: GEOS SDK TechDocs | Up | Prev: Memory Levels | Next: 2 Modifying Files on the FFS

Operations that require writing to the FFS should almost always check "disk space" both before and after the operation. There are three defined levels of available disk space on the device. These levels are:

  • Not full (more than 50kb available)
  • Nearly full (between 25 and 50kb available)
  • Full (less than 25 kb available)

You can check for these conditions by using the following constants:

  • FOAM_DISK_SPACE_NEARLY_FULL_LEVEL
  • FOAM_DISK_SPACE_FULL_LEVEL

FFS checking is usually needed at the following times:

  • Writing files to disk
  • Engaging in an operation that consumes memory
| Up: GEOS SDK TechDocs | Up | Prev: Memory Levels | Next: 2 Modifying Files on the FFS


Modifying Files on the FFS

Memory Levels: 2 Modifying Files on the FFS

| Up: GEOS SDK TechDocs | Up | Prev: 1 Memory Levels | Next: 3 Operations that Consume FFS Space
FoamWarnForSaving(), FoamSaveAction
On N9110: FoamWarnForSavingOnDisk()

Whenever you perform an operation that modifies a file on the FFS, you should make sure that you have enough space to save the file. For example, you may bring up a document, modify it, and then wish to close the app, saving the document as it now exists.

In those case, use FoamWarnForSaving() to make sure you can proceed with the operation. Pass this routine the number of bytes required for the operation. The routine checks if there is enough memory available for the operation. If there is, it returns FSA_SAVE_DATA. Otherwise, it initiates a dialog box that asks you whether you wish to either throw away the changes or cancel the operation. If you are using a N9110 Communicator and want to save to the memory card, use FoamWarnForSavingOnDisk() and pass the card's DiskHandle in addition to the number of required bytes.

FoamWarnForSaving() returns one of the following FoamSaveAction values:

FSA_SAVE_DATA
The operation is well within the limits.
FSA_THROW_AWAY_CHANGES
The operation could not proceed due to memory constraints, and the user chose to discard the changes. Proceed accordingly. (You might, for example, allow the app to close.)
FSA_DO_NOTHING
The operation could not proceed due to memory constraints, and the user chose to cancel the operation. Proceed accordingly. (You should not allow the app to close, for example.)
| Up: GEOS SDK TechDocs | Up | Prev: 1 Memory Levels | Next: 3 Operations that Consume FFS Space


Operations that Consume FFS Space

Memory Levels: 3 Operations that Consume FFS Space

| Up: GEOS SDK TechDocs | Up | Prev: 2 Modifying Files on the FFS | Next: 4 Utility Space Routines
FoamWarnIfNotEnoughSpace(), FoamCheckIfOutOfSpace(), FoamWarnSpaceAfterOperation(), FoamDiskSpaceStatus
On N9110: FoamWarnIfNotEnoughSpaceOnDisk(), FoamCheckIfOutOfSpaceOnDisk(), FoamWarnSpaceAfterOperationOnDisk()

Some operations may consume memory once they are begun, and it might be difficult to stop them once they start. For example, receiving a fax, reading or receiving e-mail, downloading something from the Web or printing a document may all consume FFS space.

FoamWarnIfNotEnoughSpace() takes a numeric value corresponding to the bytes required for the operation. If the operation would result in memory falling below the critical level, the routine will notify the user that it has insufficient memory to complete the operation (via a dialog box) and will return FDDS_FULL. Before writing to the N9110's memory card, use the analogous routine FoamWarnIfNotEnoughSpaceOnDisk().

If the operation can proceed otherwise, it will return the appropriate FoamDiskSpaceStatus value:

FDDS_NOT_FULL
FDDS_NEARLY_FULL
FDDS_FULL

FoamCheckIfOutOfSpace() also returns a FoamDiskSpaceStatus but does not initiate any dialog box (FoamCheckIfOutOfSpaceOnDisk() for memory card).

You should callFoamWarnSpaceAfterOperation() after any operation that consumes FFS space (FoamWarnSpaceAfterOperationOnDisk() for the memory card). If the operation resulted in an FDDS_NEARLY_FULL status, the routine brings up a dialog alerting the user that the device is nearly out of FFS space.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Modifying Files on the FFS | Next: 4 Utility Space Routines


Utility Space Routines

Memory Levels: 4 Utility Space Routines

| Up: GEOS SDK TechDocs | Up | Prev: 3 Operations that Consume FFS Space
FoamGetFreeDiskSpace(), FoamCheckIfOutOfSpace()
On N9110: FoamGetFreeDiskSpaceOnDisk(), FoamCheckIfOutOfSpaceOnDisk()

FoamGetFreeDiskSpace() returns the number of bytes of free space. Use FoamGetFreeDiskSpaceOnDisk() on a N9110 to get the number of free bytes on the disk (such as the memory card) with the passed DiskHandle.

FoamCheckIfOutOfSpace() also returns a FoamDiskSpaceStatus but does not initiate any dialog box (FoamCheckIfOutOfSpaceOnDisk() on N9110).

| Up: GEOS SDK TechDocs | Up | Prev: 3 Operations that Consume FFS Space


Memory Cards

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Emulating a Memory Card
| One-page Version of this Chapter for Easy Printing

The Nokia 9110 Communicator supports an optional memory card for extra data storage. The memory card is about the size of a postage stamp and provides up to 4 MB of Flash memory. To install a memory card, shut down the Communicator, remove the main battery, and insert the card behind the battery. For more information on memory card technology, refer to its homepage.

When a user starts the Communicator with a memory card, GEOS merges the directories on the card with the corresponding Communicator directories. For example, if the file temp were in the memory card's NOKIA\DOCUMENT\ directory, the user would see temp in the phone's document directory. As another example, if the application test.geo were in the card's NOKIA\WORLD\EXTRAPPS\ directory, then the user could run test.geo from the "Extra Applications" menu.

To develop geodes that use the memory card, you should become acquainted with special API that lets you deal with files and directories on a card. Emulate a memory card on the emulator by using a floppy disk.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Emulating a Memory Card


Emulating a Memory Card

Memory Cards: 1 Emulating a Memory Card

| Up: GEOS SDK TechDocs | Up | Prev: Memory Cards | Next: 2 Memory Card API

The memory card is removable media, like a floppy disk. The emulator uses the floppy disk (in the A: drive) to emulate a memory card.

To set up a floppy disk that emulates a memory card:

  1. Format a floppy disk using a FAT file system
  2. Create the following directory structure:
    A:\NOKIA\DOCUMENT\MMC
A:\NOKIA\PRIVDATA
  3. You can also create these optional directories to hold additional information:
    A:\NOKIA\WORLD\SYSAPPS (System Applications geodes)
A:\NOKIA\WORLD\EXTRAPPS (Extra Applications geodes)
A:\INSTALL (For installable packages)
A:\BACKUP (For backup sets)

Insert the disk into the floppy drive and launch the emulator. At startup, the emulator checks for a disk with the correct format (directories). If the emulator finds such a "memory card," it merges the memory card directories with the N9110 directories. For example, any geodes in the memory card's NOKIA\WORLD\SYSAPPS directory appear in the list of the N9110's "System Applications".

| Up: GEOS SDK TechDocs | Up | Prev: Memory Cards | Next: 2 Memory Card API


Memory Card API

Memory Cards: 2 Memory Card API

| Up: GEOS SDK TechDocs | Up | Prev: 1 Emulating a Memory Card

The Nokia 9110 Communicator features expanded file API for memory cards.

internal/respondr.goh

To use a memory card, first see if there is a memory card using RespGetMmcStatus() & MASK_MS_CARD. Then get its DiskHandle using RespGetMmcDisk(). Use this DiskHandle to specify paths to files and directories on the memory card.

The user interacts with the memory card as if it were a directory with the same name as the memory card. You can get this name with RespGetMmcName() and alter it with RespSetMmcName().

foam.goh

Use FoamPathFolderNameForDisplay() and FoamPathGetFullPathForDisplay() to display user-friendly paths to items on the memory card.

There are several new diskspace routines which extend the standard diskspace routines to apply the memory card. These routines have the same name as their counterparts but end in "...OnDisk" and take a DiskHandle parameter. For example, FoamCheckIfOutOfSpaceOnDisk() behaves like FoamCheckIfOutOfSpace() but can check the disk space on the Communicator or the memory card.

For code examples, refer to the MCUtils sample application (N9110\APPL\SDK_9000\MCUtils\).

| Up: GEOS SDK TechDocs | Up | Prev: 1 Emulating a Memory Card


Modem Connections

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Introduction
| One-page Version of this Chapter for Easy Printing

This chapter discusses how to set up your Nokia 9000i Communicator to emulate a Hayes-compatible modem to establish a direct serial connection.

On the Nokia 9000i Communicator, you connect through a "virtual" serial port and use a stream to read and write data to this port just as you would with an actual serial port. (For more information on streams, see the Streams chapter of the Concepts Book.) The modem driver handles data notifications from the port.

The ModemC Library provides an interface to the modem driver while the VirtualSerial Library provides a stream interface to the "virtual" serial port. In addition, the modem.goc module provides a set of basic routines for establishing a modem connection. You can drop this module into any application that uses a modem.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Introduction
| One-page Version of this Chapter for Easy Printing


Introduction

Modem Connections: 1 Introduction

| Up: GEOS SDK TechDocs | Up | Prev: Modem Connections | Next: 2 Connecting

Sending and receiving data over a serial line is similar to sending and receiving data through a socket connection. You configure your hardware, make the connection, open streams for sending and receiving data, and close the connection ( i.e. , hang up).

This chapter uses the MTalk sample application to illustrate how to set up and use a modem connection. MTalk establishes a simple "talk" exchange between two devices over a modem connection. It provides user interface for taking text input (which it sends to the other end of the connection) and for displaying data received from the other end of the connection.

(Note: Both mtalk.goc, the sample application discussed in this chapter, and modem.goc, the module that provides its basic modem connectivity, live in \APPL\SDK_9000\MTALK\.)

| Up: GEOS SDK TechDocs | Up | Prev: Modem Connections | Next: 2 Connecting


Connecting

Modem Connections: 2 Connecting

| Up: GEOS SDK TechDocs | Up | Prev: 1 Introduction | Next: 3 Sending Data

To establish a connection, MTalk does the following:

  • allocates a buffer to be used (later) for holding received data
  • prompts the user for the phone number to dial
  • calls ModemConnect() (defined in modem.goc) to make the connection

Code Display 9-1 Making A Serial Line Connection

    /*
     * phoneNum - Phone number to dial.
     * retVal - Return value of ModemConnect().
     */
    char        phoneNum[MAX_PHONE_NUM_LENGTH + 1];
    Boolean     retVal;
    /*
     * Allocate a global memory block to be used as a buffer to hold received data.
     * A local buffer variable in the read function would use up
     * precious stack space, and a global buffer variable
     * would increase the size of the application's fixed data segment,
     * which degrades overall memory manager performance.
     *
     * Allocating it to be DISCARDABLE has further advantages.
     * Because we don't care what happens to the contents after they
     * have been processed, we'll allow the memory manager to throw
     * the block out if it decides it needs the heap space. This
     * means a little extra work for us whenever we want to use the
     * block, since we have to check for this condition, and
     * reallocate if necessary.
     */
    recvBuffer = MemAlloc( INPUT_BUFFER_SIZE, HF_DISCARDABLE, 0 );
    /*
     * If the buffer was not successfully allocated, memory must be getting low
     * so shut down.
     */
    if ( recvBuffer == NullHandle )
    {
    @send application::MSG_META_QUIT();
    return;
    }
    /*
     * Get the phone number to dial.
     */
    @call MTalkPhoneNumberText::MSG_VIS_TEXT_GET_ALL_PTR( phoneNum );
    /*
     * Create the modem connection and register the object and messages
     * it will receive when data is received or when the call has ended.
     * Note: in this example, oself refers to the process object.
     * Remember that ModemConnect() returns FALSE if it successfully makes
     * the connection.
     */
    retVal = ModemConnect( phoneNum,
             oself,
             MSG_MTALK_PROCESS_READ_DATA,
             MSG_MTALK_PROCESS_CALL_ENDED );
    /*
     * If the connection failed, free the buffer and return. (Note: after freeing
     * any allocated memory, it is good practice to re-set handles and pointers to
     * Null so that you can test for a Null value later.)
     */
    if ( retVal )
    {
    MemFree( recvBuffer );
    recvBuffer = NullHandle;
    return;
    }

ModemConnect() takes care of the "housekeeping" details involved in making a connection. Specifically, this routine takes care of the following:

  • checks to make sure the phone is available for use
  • opens and configures the port (on the Nokia 9000i Communicator, the "virtual" serial port is COM1)
  • initializes the modem
  • sets up to receive notification when data arrives on the serial port or when the call ends
  • dials the phone number

ModemConnect() takes four parameters:

phoneNumber
Char pointer to the phone number to dial.
dataReceiver
Optr to the object that will receive data or call end notification.
dataReceiptMessage
Message sent when data arrives on the port.
endCallMessage
Message sent when the call has ended.

ModemConnect() returns FALSE if the attempt to connect is successful. If the attempt is unsuccessful, it displays a dialog box notifying the user of the error. Possible error messages include:

"Connectivity Problem:
Data calls cannot be made while another call is active."
"Connectivity Problem: Line is busy. Try again later."
"Connectivity Problem:
No answer. Check the phone number and try again later."
"Connectivity Problem: General Error."
"Connectivity Problem: Error initializing modem."
| Up: GEOS SDK TechDocs | Up | Prev: 1 Introduction | Next: 3 Sending Data


Sending Data

Modem Connections: 3 Sending Data

| Up: GEOS SDK TechDocs | Up | Prev: 2 Connecting | Next: 4 Receiving Data

To send data, MTalk does the following:

  • retrieves the text the user has typed in and puts it into a memory block
  • locks the block down (to get a pointer to the text)
  • gets the size of the text
  • calls ModemSend() (defined in modem.goc) to send the data
  • unlocks the memory block

Code Display 9-2 Sending Data Over A Serial Connection

    /*
     * textBlock - Handle of block containing text to send.
     * textPtr - Pointer to text to send.
     * textSize - Size of text to send.
     */
    MemHandle		textBlock;
    char *		textPtr;
    int		textSize;
    /*
     * Get text that user has typed and put it into a newly created memory block.
     * Lock the block down on the heap and obtain a pointer to it.
     */
    textBlock = @call MTalkSendText::MSG_VIS_TEXT_GET_ALL_BLOCK( NullHandle );
    textPtr = MemLock( textBlock );
    /*
     * Get the size of the text then send it.
     */
    textSize = LocalStringSize( textPtr );
    if ( ModemSend( textSize, (byte*)textPtr ) != STREAM_NO_ERROR )
    {
    /* handle the error */
    }
    /*
     * Once the text has been sent, free its block.
     */
    MemFree( textBlock );

ModemSend() calls the VirtualSerial Library routine, VirtualSerialWrite() , to send the passed data. VirtualSerialWrite() writes the passed data to stream and, if necessary, blocks until enough space in the stream becomes available. (If you don't want to block, pass STREAM_NO_BLOCK in VirtualSerialWrite() .)

ModemSend() takes two arguments:

dataLength
Length of the string (not counting the Null character).
data
Pointer to the data to be sent.

ModemSend() returns a StreamError value:

STREAM_NO_ERROR
if it successfully sends the data.
STREAM_CLOSED
if the stream is not open.
STREAM_SHORT_READ_WRITE
if the required amount of space was not available and STREAM_NO_BLOCK was specified.
| Up: GEOS SDK TechDocs | Up | Prev: 2 Connecting | Next: 4 Receiving Data


Receiving Data

Modem Connections: 4 Receiving Data

| Up: GEOS SDK TechDocs | Up | Prev: 3 Sending Data | Next: 5 Disconnecting

When the modem driver receives notification that data has arrived on the port, it sends a pre-registered message to a specified object. (You specify both the object and the message when you call ModemConnect() .)

In the MTalk example, the driver sends MSG_MTALK_PROCESS_READ_DATA to the process object when data has arrived on the port. The handler for this message is shown in Receiving Data Over A Serial Connection .

To receive data, MTalk does the following:

  • checks to make sure the connection is still open
  • locks the input buffer down (to get a pointer to the text)
  • calls ModemReceive() (defined in modem.goc) to read incoming data
  • displays read data
  • unlocks the block

Code Display 9-3 Receiving Data Over A Serial Connection

@method MTalkProcessClass, MSG_MTALK_PROCESS_READ_DATA
{
    /*
     * recvDataSize - Amout of data actually read in.
     * recvDataPtr - Pointer to data to read in.
     * retVal - Return value of ModemReceive().
     */
    word		revDataSize;
    char *		recvDataPtr;
    StreamError		retVal;
    /*
     * Check to see if connection is still open. Because of message queueing
     * delays, we may have received this data notification after the connection
     * has been closed.
     *
     * ConnectionOpen - global flag that keeps track of whether
     * the connection is open or closed.
     */
    if ( ConnectionOpen == FALSE )
    {
    return;
    }
    /*
     * Lock the input buffer down on the heap, and obtain a pointer to it.
     * (Note: This buffer is a memory block that was allocated in the
     * "Connecting" example.)
     */
    recvDataPtr = MemLock( recvBuffer );
    /*
     * A NULL pointer means that the memory manager has gone ahead
     * and discarded the buffer block since the last time it was
     * unlocked.  So we need to reallocate it here.  Note that the
     * MemHandle is still in use; it just isn't bound to any block of memory.
     */
    if ( recvDataPtr == NULL )
    {
        /*
         * Reallocate the block, locking it immediately,
         * so it doesn't get discarded again. If the block cannot
         * be re-allocated, memory must be low so close the connection.
         */
    if (( MemReAlloc( recvBuffer, RECV_BUFFER_SIZE, HAF_LOCK ) == NullHandle)
    {
        @send self::MSG_MTALK_PROCESS_CLOSE();
        return;
    }
        /*
         * After re-allocating the locked block, dereference its handle
         * to obtain a pointer to it.
         */
    recvDataPtr = MemDeref( recvBuffer );
    }
    /*
     * Read available data up to the size of our buffer then display it.
     */
    if ( ModemReceive( RECV_BUFFER_SIZE, (byte*)recvDataPtr, &recvDataSize )
                            == STREAM_NO_ERROR )
    {
        @call MTalkOutText::MSG_VIS_TEXT_APPEND_PTR( recvDataPtr,
                             recvDataSize );
    }
    /*
     * If the read data filled the entire buffer, there's a good
     * chance more data is available so send the message through the
     * process object's queue again.  (We do not want to keep reading
     * in a loop in case the other side dumped a very large stream
     * of data; this would keep the process object from handling
     * other messages in its queue.)
     */
    if ( RECV_BUFFER_SIZE == recvDataSize ) {
    @send ,forceQueue self::MSG_MTALK_PROCESS_READ_DATA();
    }
    /*
     * Once all the data has been read, unlock the buffer block.
     */
    MemUnlock( recvBuffer );
} /* end of MSG_MTALK_PROCESS_READ_DATA */

Like ModemSend() , ModemReceive() calls a VirtualSerial routine, VirtualSerialRead() , to read incoming data. Because no blocking is specified, VirtualSerialRead() reads only data that is currently available in the stream. (If you want the routine to block until there is enough data in the stream to fill the entire buffer, pass STREAM_BLOCK in VirtualSerialRead() .)

ModemReceive() takes three arguments:

recvBufLength
Size of allocated buffer (in bytes).
recvBuf
Byte pointer to the buffer.
bytesReceived
Word pointer. ModemReceive() writes the number of bytes actually read in to this parameter.

ModemReceive() returns a StreamError value:

STREAM_NO_ERROR
if it successfully reads the data.
STREAM_CLOSED
if the stream is not open.
STREAM_SHORT_READ_WRITE
if the required amount of data was not available and STREAM_NO_BLOCK was specified.
| Up: GEOS SDK TechDocs | Up | Prev: 3 Sending Data | Next: 5 Disconnecting


Disconnecting

Modem Connections: 5 Disconnecting

| Up: GEOS SDK TechDocs | Up | Prev: 4 Receiving Data

When a call has ended, the modem driver sends a pre-registered message to a specified object. (You specify both the message and the receiving object in ModemConnect() .)

In the MTalk example, the modem driver sends MSG_MTALK_PROCESS_CALL_ENDED to the process object when the call has ended for any reason. This message checks if the connection is still open and if it is, calls yet another message, MSG_MTALK_PROCESS_CLOSE, to close it. The handler for MSG_MTALK_PROCESS_CLOSE is shown in Ending A Serial Line Connection .

Code Display 9-4 Ending A Serial Line Connection

@method MTalkProcessClass, MSG_MTALK_PROCESS_CLOSE
{
    /* close the connection */
    ModemDisconnect();
    /*
     * Free the buffer and re-set the (global) "connection" flag
     * to indicate the connection is closed.
     */
    MemFree( recvBuffer );
    recvBuffer = NullHandle;
    ConnectionOpen = FALSE;
} /* end of MSG_MTALK_PROCESS_CLOSE */

ModemDisconnect() , defined in modem.goc, takes care of the following:

  • hangs up the phone
  • closes the "virtual" serial port

ModemDisconnect() takes no parameters and returns void.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Receiving Data


SMS Guide

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 SMS Overview
| One-page Version of this Chapter for Easy Printing

The Short Message Service (SMS) is a protocol designed to transmit short alpha-numeric messages via a dedicated cellular provider's "service center." SMS is built into the GSM (Global System for Mobile communications) standard that has gained general acceptance within Europe; SMS will also most likely be integrated into whatever digital cellular network becomes the de-facto standard within the United States.

The Nokia 9000i Communicator is compliant with the transmission and reception of SMS protocol messages according to the GSM standard. With SMS, it is possible to create applications and services that request and retrieve short text messages (up to 160 characters) to any server or user on the GSM network. The ability to send and receive short messages can enable a wide variety of services. For example, short messages can be used to retrieve stock quotes, place horse-racing bets at a paramutual facility, or play chess with another member of the GSM network. The possible uses for the SMS protocol are almost limitless. A service need only be connected to the GSM network.

Geoworks' Mailbox library includes built-in transmission and receipt of SMS messages. The elements of the Mailbox library that are of concern to SMS application developers have been excerpted and included in this chapter.You will not need to understand the inner workings of the Mailbox library to write an SMS application or service.

Before reading this chapter, you should be familiar with cellular technologies, the SMS standard and elementary GEOS programming--in particular, the use of GEOS Virtual Memory (VM) files.

1 SMS Overview
2 Using the Mailbox Library
2.1 Mailbox Overview
3 Sending an SMS Message
3.1 Sending and the Mailbox Library
3.2 Using the SMS Mailbox API
4 Receiving an SMS Message
4.1 SM Receive and the Mailbox Library
4.2 Receiving an SMS Message

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 SMS Overview
| One-page Version of this Chapter for Easy Printing


SMS Overview

SMS Guide: 1 SMS Overview

| Up: GEOS SDK TechDocs | Up | Prev: SMS Guide | Next: 2 Using the Mailbox Library

The Short Message Service protocol handles a "short message" of up to 160 characters. The SMS standard of 160 characters allows you to send a text message to any single node (address) on the GSM network by way of a dedicated cellular service provider.

The cellular service provider is responsible for managing incoming SMS messages and holding them until the recipient's phone becomes active. The length of time that an SMS message should be held can be specified by the sending party. This is known as a validity period .

The cellular provider may also elect to deliver the message in a format other than SMS. This is done at the server through a message conversion into the alternative format (e.g., as a fax message, as an email, as a page, etc.) This alternative method of delivery may also be specified by the sending party (though, of course, the recipient must have the means to handle such a format).

Because the SMS message is so short, it is guaranteed to be delivered as a single packet of information.

| Up: GEOS SDK TechDocs | Up | Prev: SMS Guide | Next: 2 Using the Mailbox Library


Using the Mailbox Library

SMS Guide: 2 Using the Mailbox Library

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 SMS Overview | Next: 3 Sending an SMS Message

The Nokia 9000i Communicator comes with built-in capabilities to send and receive SMS messages. This is enabled through use of Geoworks' Mailbox library. Because the Mailbox library includes all Inbox/Outbox functionality (email, faxing, etc.), it contains a large amount of information that is not relevant to an SMS service developer. This section will only detail general information about the Mailbox library and API specific to SMS application developers.

Mailbox Overview

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 SMS Overview | Next: 3 Sending an SMS Message


Mailbox Overview

SMS Guide: 2.1 Using the Mailbox Library: Mailbox Overview

| Up: GEOS SDK TechDocs | Up | Prev: 2 Using the Mailbox Library | Next: 3 Sending an SMS Message

The purpose of the Mailbox library is to let applications send and receive information to and from other computers, through a variety of communications media. The mailbox insulates the application as much as possible from the mechanics of transmitting and receiving the information.

In order to use the Mailbox library, an application must package information in a format acceptable to both a data driver (which stores the message) and a transport driver (which sends the message) -- in this case the SMS transport driver. The application then registers this message with the Mailbox library. During the registration process, you specify in which data format the message is stored.

The Mailbox library stores messages that have not yet been transmitted in its outbox . Messages are held in the outbox until the hardware connection necessary for transmission is available. An application can also specify that a message not be delivered until a specific time. In this case, the message will automatically remain in the outbox until that time. While a message is in the outbox, the device's user can examine information about the message and delay or even cancel its delivery.

When the time comes to send the message, the Mailbox library activates the appropriate transport driver. This activation is transparent to an application developer.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Using the Mailbox Library | Next: 3 Sending an SMS Message


Sending an SMS Message

SMS Guide: 3 Sending an SMS Message

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Using the Mailbox Library | Next: 4 Receiving an SMS Message

There are three pieces of information that you will need to know when you send an SMS message. These are rather self-evident:

  • The text message.
  • The recipient's GSM phone number.
  • The cellular provider's phone number. All SMS messages are routed through the cellular provider.

If you wish the SMS message to be sent to a particular application on the receiving Nokia 9000i Communicator, you will also need the following information:

  • The application token ( TokenChars ) of the receiving app.
  • The ManufacturerID of the receiving app.

In GEOS, these two pieces of information are referred to as a GeodeToken . If the application is your own, the GeodeToken is easy enough to determine (since you will have defined it yourself). If the application is not your own, you will need to contact that manufacturer or cellular provider for this information.

Sending and the Mailbox Library

Using the SMS Mailbox API

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Using the Mailbox Library | Next: 4 Receiving an SMS Message


Sending and the Mailbox Library

SMS Guide: 3.1 Sending an SMS Message: Sending and the Mailbox Library

| Up: GEOS SDK TechDocs | Up | Prev: 3 Sending an SMS Message | Next: 3.2 Using the SMS Mailbox API

Any SMS message that is sent out to the cellular provider is routed through the mailbox's outbox . There are three basic paradigms involved in packaging up information for use by the outbox. Although knowledge of how they work is not essential to sending an SMS message, it may be important when troubleshooting problems.

Apart from any parameters sent to the mailbox, the three basic pieces of information the outbox needs to know in order to send a message are:

  • In what data format the message is "wrapped." The mailbox can handle any data format as long as it is wrapped (contained) in a format it can understand. The mailbox calls an appropriate "data driver" when storing the message before it is sent.
  • The type of data being sent. (The mailbox, itself, does not need to know the actual data; it only needs to know how the data is wrapped.) Telling the mailbox what the data is provides a black box abstraction.
  • The transport driver to use when sending the message.

When a message is placed in the outbox, the mailbox calls the appropriate data driver to store the data. When a connection is made, the mailbox calls the appropriate transport driver and sends the message. Note that the mailbox itself does not need to know the message's contents, or even its format.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Sending an SMS Message | Next: 3.2 Using the SMS Mailbox API


Using the SMS Mailbox API

SMS Guide: 3.2 Sending an SMS Message: Using the SMS Mailbox API

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Sending and the Mailbox Library | Next: 4 Receiving an SMS Message
library mailbox
library foam
library ansic

To send an SMS message to the outside world, an application will need to perform the following steps:

  1. Set up the text message. In many cases, the text will reside in a simple text buffer. If you wish to address the SMS message to a particular application, you will need to prepend a twelve-character prefix to the text message. The message itself will then be, at most, 148 characters in length. Without a prefix, you may send a 160-character SMS message. (See Setting Up the Text Message .)
  2. Copy the text into a VM file. The Mailbox library may store an SMS text message in a variety of formats (indicated by type GeoworksMailboxDataFormatID ), but a VM file is the easiest to manipulate. This VM file will be accessed by a Mailbox data driver. (See Storing the Text Within a VM File .)
  3. Set up the SMSendingOptions parameters in a DB item. These will be used by the SMS transport driver when the message is delivered. (See Setting Up the SMSending Options .)
  4. Set up the MailboxRegisterMessageArgs structure. (See Setting Up the MailboxRegisterMessageArgs .)
  5. Call MailboxRegisterMessage() . (See Registering the Message .)
  6. Upon success, notify the Mailbox library that we are done with the VM file.

These steps are detailed in the sections following.

Setting Up the Text Message

foam.goh, Ansi/string.h

The Short Message Service protocol is able to handle a "short message" of up to 160 characters. The Foam library defines this character limit:

#define FOAM_MAX_SMS_TEXT_SIZE 160

The Nokia 9000i Communicator also allows you to further specify a particular application within the Nokia 9000i Communicator by adding a prefix to the SMS message (thus reducing the amount of useful text).

This prefix consists of two backslashes ("//"), a 4-byte GEOS application token ( TokenChars), a 5-byte GEOS ManufacturerID, and a carriage return ("/r", ASCII 13, or 0x0D hex). The Mailbox library will correctly read all characters up to the carriage return.

For example:

//SKAA11\r

is a valid prefix.

The application TokenChars and ManufacturerID should match those of the intended receiving application. If you define your own application, and only wish to communicate with its counterpart running on another device, you will define this string in the application's .gp file. The string is constructed from the tokenchars and tokenid lines of the .gp file.

You may also send a message to a different application, as long as you know its TokenChars and ManufacturerID .

This prefix should leave at least 148 characters to store the alpha-numeric text message. When concatenating two strings, be sure that you do not overflow the bounds of the array.

Code Display 3-1 Setting Up an SMS Text Message

/*
 * Declare the buffer to store the text message and write the prefix to the buffer.
 */
TCHAR smsMessage[FOAM_MAX_SMS_TEXT_SIZE + 1] = "//SKAA11\r";
TCHAR smsText[] = "Here's my test message. I better make sure it fits.";
/*
 * Add the body of the text to the buffer but first check the bounds.
 */
if ( strlen( smsMessage ) + strlen( smsText ) <= FOAM_MAX_SMS_TEXT_SIZE )
{
    strcat( smsMessage, smsText );
}

Storing the Text Within a VM File

mailbox.goh, Mailbox\vmtree.h, MailboxGetVMFile()

Text within an SMS message needs to be stored by the Mailbox until it connects with a cellular service provider. When you register a message with the Mailbox library, you indicate what storage format you have selected. The Mailbox uses a data driver to store and retrieve messages. There are a number of data drivers available, but we only need to know a few.

SMS messages may either be stored as VM (Virtual Memory) trees or as GEOS files, within the Mailbox itself. VM trees offer a lower overhead, and are simpler to use, so that is the format we have documented. (It is possible that future data drivers will become available as well.) If you are unfamiliar with GEOS VM trees, consult the Virtual Memory chapter of the Concepts book.

The mailbox data driver stores its data in what is called a VM tree. This tree is referenced by a VMTreeAppRef structure.

typedef struct {
    VMChain			VMTAR_vmChain;
    VMFileHandle			VMTAR_vmFile;
} VMTreeAppRef;
VMTAR_vmChain stores the VMChain to pass to the data driver. For SMS messages, simply use the VMCHAIN_MAKE_FROM_VM_BLOCK macro to construct a VMChain from a single VMBlockHandle .
VMTAR_ vmFile stores the VMFileHandle which contains the VM block.

The Mailbox stores these VM trees locally within an administation file that it maintains. To store the text within a Mailbox-compatible VM file, perform the following steps:

  1. Call MailboxGetVMFile() to retrieve a VM file handle within the Mailbox. This routine expects an argument indicating the expected number of blocks to be added to the file; because the SMS message can be contained in a single block, pass 1. Also pass a buffer to store the returned VMStatus.

    This routine returns a VMFileHandle.
  2. Allocate a VM block within this file, using VMAlloc().
  3. Lock the block down using VMLock(). This routine returns the segment address of the locked block. You will also receive a temporary memory handle that you can use to manipulate the block.
  4. Copy the text from your text buffer into the locked block starting at its segment address. (You will need to skip two bytes for the VMChainLink that begins the block.)
  5. Dirty the block using VMDirty().
  6. Unlock the block using VMUnlock().
  7. Store the VM file and VM block within a VMTreeAppRef structure. This format is understood by the mailbox data driver.

Code Display 3-2 Storing the Text In a VM File

VMFileHandle		vmFile;
VMBlockHandle		vmBlock;
TCHAR		*textPtr;
MemHandle		mh;
word		vmStatus;
VMTreeAppRef		vmRef;
/*
 * Obtain the handle of a writable VM file from the Mailbox library. We pass one
 * since we do not plan to add additional blocks to this file (because SMS messages
 * are so short).
 */
vmFile = MailboxGetVMFile(1, &vmStatus);
/*
 * Allocate a VM block and store the text within this block. The block will need
 * to fit a header (VMChainLink), the SMS message itself, and a null terminator.
 */
vmBlock = VMAlloc(vmFile, (sizeof(VMChainLink) + strlen(smsMessage) +1), 0);
textPtr = VMLock(vmFile, vmBlock, &mh);
/*
 * We advance past the VMChainLink contained at the beginning of the VM
 * block and copy the text to that location.
 */
strcpy(textPtr + sizeof(VMChainLink), smsMessage);
/*
 * Dirty the block and unlock it.
 */
VMDirty(mh);
VMUnlock(mh);
/*
 * Store the VM file and VM block within a VMTreeAppRef structure. This is the
 * format that the mailbox library understands. Essentially, it ia a VM "chain"
 * of a single VM file.
 */
vmRef.VMTAR_vmChain = VMCHAIN_MAKE_FROM_VM_BLOCK(vmBlock);
vmRef.VMTAR_vmFile = vmFile;

Setting Up the SMSending Options

internal\resp\smdefine.goh, MailboxGetAdminFile(), SMSendingOptions, ReadCSNumberFromIniFile()

When registering a mailbox message, the application must specify a data driver to use when storing the message. It will also need to specify which transport driver to use when the message is to be delivered. The GEOS SMS transport driver expects a number of parameters indicating how the SMS message should be sent. These parameters are passed as a SMSendingOptions structure within a GEOS DBItem.

To store these parameters within a compatible DB item, perform the following steps:

  1. Call MailboxGetAdminFile() to retrieve the VMFileHandle of the Mailbox administration file. This is where you will copy your SMS parameters.
  2. Allocate an ungrouped DB item within this file, using DBAllocUngrouped().
  3. Lock the DB item using DBLockUngrouped().
  4. Copy your basic SMSendingOptions parameters into this DB item.
  5. Read the service center number from the .INI file using InitFileReadStringBuffer(). Copy that number into the DB item.
  6. Dirty the block using DBDirty().
  7. Unlock the block using DBUnlock().

The SMSendingOptions structure

The SMSendingOptions structure is defined within smdefine.goh and is shown below:

typedef struct {
    SMReplyPath			SMSO_replyPath;
    SMValidityPeriod			SMSO_validityPeriod;
    SMMessageConversion			SMSO_messageConversion;
    TCHAR		SMSO_scAddress[MAX_ADDRESS_SIZE];
} SMSendingOptions;

#define MAX_ADDRESS_SIZE 22
SMSO_ replyPath contains an SMReplyPath value which indicates whether the recipient may reply to the message through the sender's service provider. Possible values are:
SMRP_NO
This flag is set by default and indicates that the recipient must reply through the recipient's service provider.
SMRP_YES
Setting this flag allows the recipient to reply through the sender's service provider.
SMSO _validityPeriod contains an SMValidityPeriod value which indicates the length of time that the message is considered valid for delivery. The cellular provider should interpret this value and if it is unable to connect with the recipient, drop the message and inform the sender that the message was not delivered. Possible values on pre-N9110 devices are:
SMVP_1_HOUR SMVP_6_HOURS
SMVP_24_HOURS
SMVP_1_WEEK
SMVP_MAX (The actual value of SMVP_MAX will vary by service provider.)

On the N9110 Communicator these values are:
SMVP_ONE_HOUR
SMVP_SIX_HOURS
SMVP_ONE_DAY
SMVP_ONE_WEEK
SMVP_MAX

SMSO_ messageConversion contains an SMMessageConversion value, indicating that you wish to have the SMS message delivered to the recipient in a different format than that of an SMS message (provided that the cellular provider offers this service). Possible values are:
SMMC_NORMAL
Deliver the message as a normal SMS message. This is the default value.
SMMC_FAX_GROUP_3
Deliver the message as a fax according to the fax group 3 standard.
SMMC_FAX_GROUP_4
Deliver the message as a fax according to the fax group 4 standard.
SMMC_VOICE
Deliver the message as a voice mail message.
SMMC_ERMES
Deliver the message according to the European Radio Messaging System standard.
SMMC_PAGING
Deliver the message as an alpha-numeric page.
SMMC_EMAIL
Deliver the message as an Internet e-mail message.
SMMC_X400
Deliver the mesage according to the X400 e-mail standard.
SMSO_ scAddress stores the address of the service center (cellular provider). This number is stored as an ASCII text string and is limited to 22 characters. This number should be retrieved from the Nokia 9000i Communicator's .INI file; the number is stored under the scNumber field within the SMS category. This "address" in this case is really just a numerical phone number.

Reading a String From the .INI File

initfile.h, InitFileReadStringBuffer()

To read a string from the .INI file, call InitFileReadStringBuffer() . Pass "SMS" as the category and "scNumber" as the key. The routine will read the entry into a buffer passed to the routine. It will also read the size of the string into another buffer.

Code Display 3-3 Setting the SMSendingOptions Parameters

SMSendingOptions			*ptrSendingOptions;
VMFileHandle			adminFile;
DBGroupAndItem			smsParams;
word			sizeOfBuffer;
adminFile = MailboxGetAdminFile();
smsParams = DBAllocUngrouped(adminFile, sizeof(SMSendingOptions));
ptrSendingOptions = (SMSendingOptions *) DBLockUngrouped(adminFile, smsParams);
/* set desired sending options */
ptrSendingOptions->SMSO_replyPath = SMRP_NO;
ptrSendingOptions->SMSO_validityPeriod = SMVP_ONE_HOUR;
ptrSendingOptions->SMSO_messageConversion = SMMC_NORMAL;
/* be sure to initialize these fields as well, otherwise the app will crash! */
ptrSendingOptions->SMSO_dataCodingScheme = 0;
ptrSendingOptions->SMSO_userDataLength = 0;
ptrSendingOptions->SMSO_userDataLength = 0;
InitFileReadStringBuffer("SMS",
            "scNumber",
            ptrSendingOptions->SMSO_scAddress,
            0,
            &sizeOfBuffer);
DBDirty(ptrSendingOptions);
DBUnlock(ptrSendingOptions);

Setting Up the MailboxRegisterMessageArgs

MailboxRegisterMessageArgs, MailboxTransAddr

By now, you have created a reference to the SMS message itself (within a VMTreeAppRef structure); you have also placed the SMS sending parameters within the mailbox administration file. You are now ready to load the MailboxRegisterMessageArgs structure with parameters. (Two of these parameters will be the location of the SMS message and its sending parameters.)

  1. Indicate the format in which you wish the SMS message stored while it resides in the outbox. (The mailbox will call the appropriate mailbox data driver when the message is registered.)
  2. Indicate the format of the actual message. This is where you tell the mailbox that this message is an SMS message.
  3. Indicate the VM file in which the SMS message is stored.
  4. Tell the mailbox to use the SMS transport driver when the mailbox sends the message.
  5. Give the mailbox the address of the recipient.
  6. Indicate the DB item where the SMSendingOptions are located.
  7. Tell the mailbox when to begin delivery of the message and when to give up delivery attempts if unsuccessful.

All of these steps are accomplished by simply filling in a MailboxRegisterMessageArgs structure. This structure is large, but it is not altogether complicated. Each field is described in the following sections, though only SMS relevant information is shown. It is shown below:

typedef struct {
    MailboxStorage				MRA_bodyStorage;
    MailboxDataFormat				MRA_bodyFormat;
    const void 				*MRA_bodyRef;
    word				MRA_bodyRefLen;
    MailboxTransport				MRA_transport;
    MailboxTransportOption				MRA_transOption;
    MailboxTransAddr				*MRA_transAddrs;
    word				MRA_numTransAddrs;
    dword				MRA_transData;
    MailboxMessageFlags				MRA_flags;
    const char				*MRA_summary;
    GeodeToken				MRA_destApp;
    FileDateAndTime				MRA_startBound;
    FileDateAndTime				MRA_endBound;
} MailboxRegisterMessageArgs;

Data Format for Storage

As we mentioned, the Mailbox will need to store a message in a format that it can understand. Rather than have the Mailbox understand every data format that is (or will ever be) developed, it is easier to "package" the message in a small number of standard formats. You specify in what format the message will be wrapped in the MRA _bodyStorage field.

MRA_ bodyStorage indicates under what format (of type MailboxStorage ) the message will be stored in the internal Mailbox administration file. MailboxStorage is defined as follows:
typedef struct {
    word			MS_id;
    ManufacturerID			MS_manuf;
} MailboxStorage;
MS_id
...stores a word of data indicating the type of data driver that will be used to store the message. For SMS messages, we will use a Geoworks data driver referenced by a GeoworksMailboxStorageID. The following types are valid:
    GMSID_FILE
    GMSID_VM_TREE
As we indicated earlier, SMS messages should be stored in VM trees. You do have the option of storing them as GEOS files as well, but VM trees are easier to manipulate.
MS_manuf
...stores the ManufacturerID of the data driver being used. Since we will be using Geoworks VM trees, we will store MANUFACTURER_ID_GEOWORKS in this location.

Data Format of the Message

MRA_bodyFormat
...indicates the format of the actual data being stored (in its original source). This field takes an argument of type MailboxDataFormat . This is defined below:
typedef struct {
    word			MDF_id;
    ManufacturerID			MDF_manuf;
} MailboxDataFormat;
MDF_id
...stores a word of data indicating the data stored in the message. We will use a Geoworks data format referenced by a GeoworksMailboxDataFormatID. Pass GMDFID_SHORT_MESSAGE in this field to indicate that the message is a Geoworks SMS message.
MDF_manuf
...stores the ManufacturerID of the format. Since we will be using Geoworks SMS , we will store MANUFACTURER_ID_GEOWORKS in this location.

References to the VM Tree Structure

MRA _bodyRef stores a pointer to the reference of the SMS message. Since we will be storing the message in a VM tree, we will reference that message with a VMAppTreeRef .
MRA_ bodyRefLen stores the length of the reference indicated in MRA_ bodyRef . In this case, we will store the size of the VMAppTreeRef structure.

The Transport Driver

The Mailbox uses an applicable transport driver when it wants to deliver a message to the outside world. You specify this transport driver in the MRA_ transport field.
MRA _transport takes an argument of type MailboxTransport . This is defined below:
typedef struct {
    word			MT_id;
    ManufacturerID			MT_manuf;
} MailboxTransport;
MT_id
...stores a word of data indicating the type of transport driver that will be used to deliver the message. For SMS messages, we will use a Geoworks transport driver referenced by a GeoworksMailboxTransportID.
To send an SMS message, we will use the SM transport driver referenced by GMTID_SM.
MT_manuf
...stores the ManufacturerID of the transport driver being used. Since we will be using Geoworks SM transport driver, we will store MANUFACTURER_ID_GEOWORKS in this location.
MRA_transOption
...stores an unsigned value that the the transport driver uses. For SMS, set this to zero.
MRA_transAddrs
stores the address of the eventual recipient. This address is stored as a pointer to a MailboxTransAddr structure; it is only important that the transport driver understand the format for this address.

If there are multiple recipients, this pointer references multiple MailboxTransAddr structures. This structure is defined below:
typedef struct {
    const void			*MTA_transAddr;
    unsigned			MTA_transAddrLen;
    const char			*MTA_userTransAddr;
} MailboxTransAddr;
MTA_transAddr
...stores a pointer to the recipient address, understood by the transport driver. (For SMS this is a simple ASCII text string of the recipient's phone number.)
MTA_transAddrLen
...stores the length of the address stored in MTA_transAddr.
MTA_userTransAddr
...stores a user-readable version of the address, as a null-terminated text string. Because the SMS address is already stored as an ASCII text value, this field will be the same as MTA_transAddr.
MRA_numTransAddrs
...stores the number of recipient addresses stored in MRA_transAddrs.
MRA_transData
...stores data understood by the transport driver. As mentioned in Setting Up the SMSending Options , the SMS transport driver expects a DBItem containing a structure of type SMSendingOptions .
MRA_flags
...stores a field of MailboxMessageFlags.
For SMS messages, the only flag you may care about is MMF_DELETE_BODY_AFTER_TRANSMISSION, which will delete the VM file containing the SMS message after it is delivered. Do not set any other flags.
MRA_summary
stores a text string that the Outbox displays to indicate the type of message being delivered. For example, if you are sending a chess move for a game in progress, you might set this field to display "Chess Move."
MRA_destApp
...stores the GeodeToken of the destination application.
MRA_startBound
...indicates a time and date (of type FileDateAndTime ) to begin transmission of the message. You may also pass the value MAILBOX_NOW (0) for immediate delivery.
MRA_endBound
...indicates a time and date (of type FileDateAndTime ) to end attempts to transmit the message to the service provider. You may also pass the value MAILBOX_ETERNITY (-1) to indicate attempt until successful.

Code Display 3-4 Setting Up the Mailbox Registration Arguments

MailboxRegisterMessageArgs				registerArgs;
MailboxTransAddr				addressList;
TCHAR				number[MAX_ADDRESS_SIZE];
/*
 * We tell the mailbox that the data is stored within a VMTreeAppRef.
 */
registerArgs.MRA_bodyStorage.MS_id = GMSID_VM_TREE;
registerArgs.MRA_bodyStorage.MS_manuf = MANUFACTURER_ID_GEOWORKS;
/*
 * We tell the mailbox that this is an SMS message.
 */
registerArgs.MRA_bodyFormat.MDF_id = GMDFID_SHORT_MESSAGE;
registerArgs.MRA_bodyFormat.MDF_manuf = MANUFACTURER_ID_GEOWORKS;
/*
 * We tell the mailbox where the body text is stored.
 */
registerArgs.MRA_bodyRef = &vmRef;
registerArgs.MRA_bodyRefLen = sizeof(VMTreeAppRef);
/*
 * We tell the mailbox to use the SM transport driver when sending the message.
 */
registerArgs.MRA_transport.MT_id = GMTID_SM;
registerArgs.MRA_transport.MT_manuf = MANUFACTURER_ID_GEOWORKS;
registerArgs.MRA_transOption = 0;
/*
 * Normally, we would retrieve the phone number from an outside source (such as
 * the contact database). Here, we just set it to a specific value.
 */
strcpy(number, "13125551212");
/*
 * We need to stuff each address (in this case, a phone number) into a
 * MailboxTransAddr structure.
 */
addressList.MTA_transAddr = &number;
addressList.MTA_transAddrLen = strlen(number);
addressList.MTA_userTransAddr = &number;
registerArgs.MRA_transAddrs = &addressList;
registerArgs.MRA_numTransAddrs = 1;
/*
 * We pass the previously set up SMSendingOptions in the DBGroupItem to the
 * transport driver.
 */
registerArgs.MRA_transData = smsParams;
registerArgs.MRA_flags = MMF_DELETE_BODY_AFTER_TRANSMISSION;
registerArgs.MRA_summary = "Chess Move";
strcpy(registerArgs.MRA_destApp.GT_chars, "SKAA");
registerArgs.MRA_destApp.GT_manufID = MANUFACTURER_ID_NOKIA;
registerArgs.MRA_startBound = MAILBOX_NOW;
registerArgs.MRA_endBound = MAILBOX_ETERNITY;

Registering the Message

MailboxRegisterMessage(), MailboxDoneWithVMFile()

Finally, you have everything set up. To send the message, all you need to do is register the message with the mailbox library.

MailboxRegisterMessage() takes an address (of type MailboxMessage ) as a second parameter. The routine will write a token value into this address so that future mailbox routines can refer to this message by its token.

Do not confuse this token with the actual message (which is sent through the MailboxRegisterMessageArg ).

After you have successfully registered the message, call MailboxDoneWithVMFile() to tell the mailbox to get rid of the temporary VM file it had created.

Code Display 3-5 Using MailboxRegisterMessage

MailboxMessage				msgToken;
MailboxError				mailboxError;
/* registerArgs and vmFile have been declared previously as:
 *    MailboxRegisterMessageArgs		registerArgs;
 *    VMFileHandle				vmFile;
 */
mailboxError = MailboxRegisterMessage(&registerArgs, &msgToken);
if (mailboxError == ME_SUCCESS) {
    /* If we want to do anything special upon success, here it would reside ... */
};
MailboxDoneWithVMFile(vmFile);

There is a possibility that MailboxRegisterMessage() may return an error (of type MailboxError ) if something is not working correctly (or if the message was not set up correctly). The routine returns ME_SUCCESS if the registration was successful.

The following are valid mailbox error codes:

ME_SUCCESS = 0
ME_NOT_ENOUGH_MEMORY
ME_CANNOT_LOAD_DATA_DRIVER
ME_CANNOT_LOAD_TRANSPORT_DRIVER
ME_UNABLE_TO_CREATE_TRANSMIT_THREAD
ME_CANNOT_ENQUEUE_MESSAGE
ME_USER_CANCELED
ME_LOST_CONNECTION
ME_CANNOT_CREATE_MESSAGE_FILE
ME_CANNOT_RESIZE_MBOX_REF
ME_CANNOT_SAVE_MESSAGE_FILE
ME_CANCELLED_BY_RECEIVER
ME_MESSAGE_BLOCKS_ARE_MISATCHED
ME_DATA_DRIVER_CANNOT_STORE_MESSAGE_BODY
ME_INSUFFICIENT_DISK_SPACE
ME_CANNOT_CONNECT

The following are unrecoverable MailboxError error codes. (The high bit is set if this is the case.)

#define ME_UNRECOVERABLE 0x8000
ME_ADDRESS_INVALID
ME_DATA_DRIVER_CANNOT_ACCESS_MESSAGE_BODY
ME_UNSUPPORTED_BODY_FORMAT
ME_MESSAGE_BODY_INVALID
ME_INVALID_MESSAGE
ME_REPLY_ADDRESS_NOT_AVAILABLE
ME_DESTINATION_APPLICATION_UNKNOWN
ME_UNKNOWN_DISK_ERROR
ME_APP_REF_BUF_TOO_SMALL
ME_CANNOT_OPEN_MESSAGE_FILE
ME_CANNOT_READ_MESSAGE_FILE
ME_CAN_NEVER_CONNECT
| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Sending and the Mailbox Library | Next: 4 Receiving an SMS Message


Receiving an SMS Message

SMS Guide: 4 Receiving an SMS Message

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Sending an SMS Message | Next: 4.1 SM Receive and the Mailbox Library

There are two ways the Nokia 9000i Communicator may receive an SMS message: standard, or targeted.

As a developer, you do not need to concern yourself with standard messages since a "standard" message is handled by the Nokia 9000i Communicator's built-in SMS application. This application handles standard messages that are either sent without an SMS prefix or that are sent specifically to the SM Receive application.

Targeted messages are those messages sent using the twelve character prefix. These messages may not necessarily be sent using another Nokia 9000i Communicator; any device that uses the correct SMS prefix can contact a specific application if it so desires. In any case, the twelve character prefix (containing the GeodeToken ) will specify the application that is meant to receive the message.

SM Receive and the Mailbox Library

Receiving an SMS Message

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Sending an SMS Message | Next: 4.1 SM Receive and the Mailbox Library


SM Receive and the Mailbox Library

SMS Guide: 4.1 Receiving an SMS Message: SM Receive and the Mailbox Library

| Up: GEOS SDK TechDocs | Up | Prev: 4 Receiving an SMS Message | Next: 4.2 Receiving an SMS Message

When the GSM network informs the unit that is has an SMS message pending for receipt, it launches the SM Receive application. This application retrieves the SMS message(s). If the message is a standard SMS message, it routes the message to the application's "Received messages" folder.

If the message is targeted to a specific application, the SM Receive application contacts the mailbox and places the message in the mailbox's inbox . The mailbox then contacts the application by sending it a message ( MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE ). If the mailbox cannot contact the application, it re-routes the message back to the SM Receive application.

You do not need to know how the SM Receive application works, but it is useful to know the process which occurs when an SMS message is received.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Receiving an SMS Message | Next: 4.2 Receiving an SMS Message


Receiving an SMS Message

SMS Guide: 4.2 Receiving an SMS Message: Receiving an SMS Message

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 SM Receive and the Mailbox Library

When the inbox receives a message that it determines is destined for a specific application, the mailbox will contact that application's process class with MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE . This message passes a token identifer of the SMS message.

If you wish your application to handle an SMS message, you will need to write a message handler for MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE . It should perform the following steps:

  1. Call MailboxAcknowledgeReceipt() to inform the mailbox that we're handling the message. Pass the token identifer of the SMS message that was passed with MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE . (See Acknowledging Receipt of the Message .)
  2. Retrieve the VM reference to the message with MailboxGetBodyRef() . (See Retrieving the Text from the VM Block .)
  3. Lock down the VM block and retrieve a pointer to the text.
  4. Copy the text to a local buffer.
  5. Unlock the VM block.
  6. Call MailboxDoneWithBody() to instruct the mailbox to release the VM reference to the text. (See Cleaning Up .)
  7. Call Mailbox DeleteMessage() , passing the token referring to this message.

These specific steps are described in more detail below:

Acknowledging Receipt of the Message

MailboxAcknowledgeMessageReceipt()

When your application receives an SMS message with MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE , your first order of business should be informing the mailbox that you have the message. To do this, send the mailbox MailboxAcknowledgeMessageReceipt() , passing it the message token.

Code Display 3-6 Acknowledging Receipt of an SMS Message

/*
 * Each application which wishes to receive SMS messages should create a
 * MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE handler in the app's main prcoess
 * class.
 */
@method MyProcessClass, MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE {
    /*
     * First we inform the mailbox that we've received the message. We pass the
     * `msg' token that was passed to this message in the first place.
     */
    MailboxAcknowledgeMessageReceipt(msg);
    /* To be continued ... */

Retrieving the Text from the VM Block

MailboxGetBodyRef()

As we indicated in Storing the Text Within a VM File , it is convenient for the SMS message itself to be wrapped in a small VMChain structure. In the following section, we assume that is indeed what was done on the sending end.

Use MailboxGetBodyRef() to retrieve this reference to the actual text. Pass the message token, a pointer to a buffer to store the body reference, and the length of that buffer.

Code Display 3-7 Retrieving the Text

    MailboxError 			errorCode;
    VMTreeAppRef			vmRef;
    word 			refSize;
    MemHandle			handleToText;
    TCHAR			smsText[FOAM_MAX_SMS_TEXT_SIZE+1];
    char			*ptrToText;
    /*
     * First, retrieve the reference to the body of the SMS text from the mailbox
     * library. Place that reference into our vmRef variable.
     */
    refSize = sizeof(VMTreeAppRef);
    errorCode = MailboxGetBodyRef(msg, &vmRef, &refSize);
    if (errorCode == ME_SUCCESS) {
    /*
     * Lock the block and retrieve a pointer to the SMS text.
     */
    ptrToText = (char *) VMLock(vmRef.VMTAR_vmFile,
                 VMCHAIN_GET_VM_BLOCK(vmRef.VMTAR_vmChain),
                 &handleToText);
    /*
     * Advance the pointer beyond the carriage return to get beyond the
     * prefix.
     */
    while ( *ptrToText != '\r')
        ptrToText++;
    /* Advance over the '\r' ...*/
        ptrToText++;
    strcpy(smsText, ptrToText);
    VMUnlock(handleToText);
    /* To be continued ... */

Cleaning Up

MailboxDoneWithBody(), MailboxDeleteMessage()

Finally, we need to notify the mailbox that it can delete both the text of the message (and the reference to that text) and the message itself. To perform each of these steps, call MailboxDoneWithBody() and MailboxDeleteMessage() .

Code Display 3-8 Cleaning Up the Mailbox

    /* Now remove the external text and the reference to that text. */
    MailboxDoneWithBody(msg, &vmRef, refSize);
    /*
     * Finally, delete the "message" including the message token and any
     * information included with it.
     */
    MailboxDeleteMessage(msg);
    }
}
| Up: GEOS SDK TechDocs | Up | Prev: 4.1 SM Receive and the Mailbox Library


Communicator UI

| Up: GEOS SDK TechDocs | Up | Down | Next: 0 Foam UI Classes Overview
| One-page Version of this Chapter for Easy Printing

The Nokia 9000i contains has a very specific look and feel which is created through the use of a specific user interface developed for the device. The specific UI contains not only visual representations for GEOS objects, but also additional objects that are particularly tailored for the Nokia 9000i device.

Most of these objects have been grouped into the "Foam" library. This library is a special collection of classes, objects and routines specifically applicable to the Nokia 9000i device. Because of its multi-faceted nature, the Foam library is not a coherent library so much as a "toolbox" of useful widgets.

We will not attempt to cover every piece of the Foam library within this chapter. Instead, we provide a brief overview of the foam UI classes, then delve into the objects which are particularly useful for creating your application.

These objects are:

  • FoamSubApplicationClass
  • ComplexMonikerClass
  • FlashingNoteClass
  • RepeatingGenGlyphClass
  • Special Foam dialog box routines
  • Special routines for presenting lists

0 Foam UI Classes Overview
1 FoamSubApplicationClass
1.1 FoamSubApplicationClass Instance Data
1.2 Using a FoamSubApplication Object
2 ComplexMonikerClass
2.1 ComplexMoniker Instance Data
2.2 ComplexMoniker Usage
2.3 ComplexMoniker Triggers
2.4 ComplexMoniker Messages
3 Convenient Dialog Routines
4 FlashingNoteClass
4.1 FlashingNote Instance Data
4.2 Setting Up the Template
4.3 Initiating and Destroying the Flashing Note
5 FoamProgressDialog
5.1 FoamProgressDialog Instance Data
5.2 Setting Up the Template
5.3 Using a FoamProgressDialog
6 Routines for Lists
7 Zoom Levels UI
8 FilterList, FilterText
8.1 FilterList and FilterText Usage
8.2 FilterList Instance Data
8.3 FilterList Messages
8.4 FilterText Instance Data
8.5 FilterText Messages

| Up: GEOS SDK TechDocs | Up | Down | Next: 0 Foam UI Classes Overview
| One-page Version of this Chapter for Easy Printing


Foam UI Classes Overview

Communicator UI: 0 Foam UI Classes Overview

| Up: GEOS SDK TechDocs | Up | Prev: Communicator UI | Next: 1 FoamSubApplicationClass

The foam library includes many new UI classes. This page lists these classes, briefly describes each class' utility, and displays an annotated snapshot of each UI class.

  • FFileSelectorClass
  • ExtendedFileSelectorClass
  • ComplexMonikerClass
  • TwoLevelTriggerClass
  • FoamSubApplicationClass
  • GreyFrameClass
  • ListWithUserDefinedClass
  • RepeatingGenGlyphClass
  • Dialogs
    • FlashingNoteClass
    • SelfDestroyingDialogClass
    • FoamProgressDialogClass
  • Indicators
    • GreyGenInteractionClass
    • IndicatorClass
    • IconIndicatorClass
    • LevelIndicatorClass
    • IndicatorInteractionClass
  • Filter List
    • FilterListClass
    • FilterTextClass
  • Underlined Text
    • UnderlinedGenTextClass
    • UnderlinedVisTextClass
  • Text Style Controllers
    • FoamTextStyleControlClass
    • FoamFontControlClass
    • FoamPointSizeControlClass
    • FoamJustificationControlClass

FFileSelectorClass

Inherits: GenFileSelectorClass

Use a FFileSelectorClass when you do not need a controller and the associated ComplexMoniker UI to select a file but only want to display a list of files (if you do want a full controller with complete UI, use FileOpenControlClass). FFileSelectorClass objects allow you to get and set the directory path using the FDocumentDir enum values for the directories which are specific to the Nokia 9000i Communicator.

An example of a FFileSelectorClass file selector.

Include: foam.goh

Reference: FileOpenControlClass, FFileSelectorClass.

ExtendedFileSelectorClass

Inherits: GenFileSelectorClass, but is a variant class.

This class is not available on build 4.8.8 or earlier versions of the Communicator.

The ExtendedFileSelectorClass expands the functionality of any subclass of GenFileSelectorClass, usually FFileSelectorClass. It allows multiple files to be selected, optionally provides the user with a search box that narrows the list of files to match the text typed in the search box, and can find files in any directory or subdirectory, not just the directories represented by the enumerated type FDocumentDir.

Include: foam.goh

Reference: ExtendedFileSelectorClass, FFileSelectorClass.

ComplexMonikerClass

Inherits: GenClass, but is a variant class.

The ComplexMonikerClass allows developers to create a moniker that contains both text and a bitmap icon. Since the text and icon are referred to generically, it is easy to localize the moniker. A complex moniker is a variant class - its parent class is dynamically created at runtime - so it can assume a variety of forms. The superclass of a ComplexMonikerClass object may be any child of GenClass. This allows complex monikers to be:

  • Triggers
  • Dialog boxs
  • Lists (items, grouped items, boolean items, contacts, or a dynamic list)
While each of the above items behaves quite differently, they all share the ComplexMonikerClass behavior of displaying a text and icon moniker.

Include: foam.goh

Reference: ComplexMonikerClass, GenTriggerClass, GenInteractionClass, GenItemGroupClass, GenItemClass, GenBooleanGroupClass, GenDynamicListClass, or ContactListClass.

TwoLevelTriggerClass

Inherits: ComplexMonikerClass

Applications that require more than four triggers may establish two panes of triggers. The user can toggle between these two panes using two TwoLevelTriggerClass trigger objects which usually have the monikers "More" or "Back". They are typically put in the fouth slot (slot 3). Since the "More" and "Back" triggers take up two slots, an application which uses two panes of triggers may define up to six of its own triggers.

Include: foam.goh

Reference: TwoLevelTriggerClass, ComplexMonikerClass, Nokia 9000i Communicator Topics: UI Conventions

FoamSubApplicationClass

Inherits: GenApplicationClass

Since the Nokia 9000i Communicator does not have a file manager per se, applications added to the device must be launched through a currently existing application. The FoamSubApplicationClass specifies in which pre-existing application an add-on application should reside.

Include: foam.goh

Reference: FoamSubApplicationClass, GenApplicationClass.

GreyFrameClass

Inherits: GenInteractionClass

Draws a gray beveled box around its children. It can be used with the ComplexMoniker variant class to add a moniker label (see FilterListClassexample).

Include: foam.goh

Reference: GreyFrameClass, GenInteractionClass.

ListWithUserDefinedClass

Inherits: GenItemGroupClass

GenItemClass and GenItemGroupClass work together to display a list of static monikers that the user may choose from. ListWithUserDefinedClass expands this to allow the user to navigate to an object and input information. The additional item may be a UnderlinedTextWithListClass, TextWithListClass, or GenValueWithListClass.

Include: foam.goh

Reference: ListWithUserDefinedClass, GenItemGroupClass.

RepeatingGenGlyphClass

Inherits: GenGlyphClass

RepeatingGenGlyphClass is used to create animations. It works be replacing the visMoniker of GenGlyphClass with the application-defined sequence of bitmapped icons, which it then cycles through. One common use of RepeatingGenGlyphClass is in dialog boxes which are opened with PutUpDialogViaUIThread(). This is done so that the animation is processed by another thread and thus doesn't burden the application.

Include: foam.goh

Reference: RepeatingGenGlyphClass, FoamProgressDialogClass.

Dialogs

FlashingNoteClass

Inherits: GenInteractionClass

The FlashingNoteClass is used to flash a note on the screen for a set length of time. The application specifies the minimum and maximum time the note will remain on the screen. If the user presses a key after the note has been on screen for the minimum duration, the note dialog box is dismissed. This dialog runs on its own thread and takes the responsibility for dismissing and destroying itself.

Include: foam.goh

Reference: FlashingNoteClass, GenInteractionClass.

SelfDestroyingDialogClass

Inherits: GenInteractionClass

The SelfDestroyingDialogClass is used to put up a dialog box that self-destructs upon dismissal.

Include: foam.goh

Reference: SelfDestroyingDialogClass, GenInteractionClass.

FoamProgressDialogClass

Inherits: GenControlClass

When an application starts a process that may take a while, it is good practice to put up a FoamProgressDialogClass dialog which notifies the user that the application is working and which indicates the status of the process. This dialog is put up on its own thread. The process is indicated by an animation of gears grinding away and by the status text which the application may append. If the user chooses the provided "Cancel" trigger a message is sent which indicates that the process should be terminated.

Include: foam.goh

Reference: FoamProgressDialogClass, GenControlClass.

Indicator Classes

The indicator classes exist in the foam library to facilitate drawing the status indicator at the left of the Nokia 9000i Communicator screen. As such, they are usually not as useful as Gen classes. Below is a brief listing of these classes followed by a screen shot of several UI objects which instantiate these classes.

GreyGenInteractionClass

Inherits: GenPrimaryClass

This class creates a primary window with a gray background. It is an inappropriate base for most applications because it only draws properly when placed against the left edge of the screen and it puts a gray background behind the trigger menu, thus making them look different from all other Communicator applications.

Include: foam.goh

Reference: GreyGenInteractionClass, GenPrimaryClass .

IndicatorClass

Inherits: GenGlyphClass

An IndicatorClass object is a GenGlyph that draws an etched border around itself.

Include: foam.goh

Reference: IndicatorClass, GenGlyphClass .

IconIndicatorClass

Inherits: IndicatorClass

This class is a GenGlyph that can draw one or two icons within etched borders from a list of bitmaps. The displayed icons can be changed dynamically.

Include: foam.goh

Reference: IconIndicatorClass, IndicatorClass.

LevelIndicatorClass

Inherits: IndicatorClass

The LevelIndicatorClass draws a bitmap followed by a sequence of bitmaps. This is used for displaying a level indicator such as the battery charge. The number of levels or the bitmap label can be changed dynamically.

Include: foam.goh

Reference: LevelIndicatorClass, IndicatorClass.

IndicatorInteractionClass

Inherits: IndicatorClass

This makes a GenInteraction with an etched border.

Include: foam.goh

Reference: IndicatorInteractionClass, GenInteractionClass.

Filter Lists

FilterListClass

Inherits: GenDynamicListClass

See below for description.

FilterTextClass

Inherits: TextWithListClass, which in turn inherits GenTextClass.

FilterListClass and FilterTextClass objects work together to provide the appropriate UI for interactive searches.

Include: foam.goh

Reference: FilterListClass, FilterTextClass, TextWithListClass, GenTextClass.

Underlined Text

UnderlinedGenTextClass

Inherits: GenTextClass

See below for description.

UnderlinedVisTextClass

Inherits: VisTextClass

UnderlinedGenTextClass and UnderlinedVisTextClass objects behave like the associated TextClass objects except that the text area has a dotted underline. These classes are used extensively for text-entry fields on the Nokia 9000i Communicator.

Include: foam.goh

Reference: UnderlinedGenTextClass, UnderlinedVisTextClass, GenTextClass, VisTextClass.

Text Style Controllers

FoamTextStyleControlClass

Inherits: GenControlClass

See below for description.

FoamFontControlClass

Inherits: GenControlClass

See below for description.

FoamPointSizeControlClass

Inherits: GenControlClass

See below for description.

FoamJustificationControlClass

Inherits: GenControlClass

The controllers FoamTextStyleControlClass, FoamFontControlClass, FoamPointSizeControlClass, and/or FoamJustificationControlClass attach to a text object and allow the user to change the displayed text styles. These controllers are functionally equivalent to the standard text object controllers (TextStyleControl, FontControl, etc.), but they have UI and features customized for the Nokia 9000i Communicator.

Include: foam.goh

Reference: FoamTextStyleControlClass, FoamFontControlClass, FoamPointSizeControlClass, FoamJustificationControlClass, GenControlClass.

| Up: GEOS SDK TechDocs | Up | Prev: Communicator UI | Next: 1 FoamSubApplicationClass


FoamSubApplicationClass

Communicator UI: 1 FoamSubApplicationClass

| Up: GEOS SDK TechDocs | Up | Down | Prev: 0 Foam UI Classes Overview | Next: 2 ComplexMonikerClass

Applications may be written to be incorporated into the Nokia 9000i Communicator as part of a special release. The Nokia 9000i Communicator does not have a file manager, per se, to enable launching these applications. The applications must be launched through a currently existing application. FoamSubApplicationClass allows you to indicate under which pre-existing application the new application should reside.

"Add-on" applications for the Nokia 9000i Communicator device, whether included on the device as part of a value-added release, or developed as an aftermarket product, will fall into one of three categories:

  • Service applications that communicate to the outside world.
  • System applications that perform a system function. (PC connectivity tools fall under this category, for example.)
  • "Extra" applications that don't fall into either of the two prior cases.

For each of these cases, there is an application already on the device that launches the sub-application. Service applications are launched using the SVCAPPS launcher; system applications use the SYSAPPS launcher; extra applications use the EXTRAPPS launcher.

Add-on applications are declared using the FoamSubApplication object, and an instance data field is set within the object indicating the "main" application it should be tied with.

Because a sub-application is not a separate application, special care needs to be taken when the sub-application is closed. This is discussed in Using a FoamSubApplication Object .

FoamSubApplicationClass Instance Data

Using a FoamSubApplication Object

| Up: GEOS SDK TechDocs | Up | Down | Prev: 0 Foam UI Classes Overview | Next: 2 ComplexMonikerClass


FoamSubApplicationClass Instance Data

Communicator UI: 1.1 FoamSubApplicationClass: FoamSubApplicationClass Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 1 FoamSubApplicationClass | Next: 1.2 Using a FoamSubApplication Object

There is only one instance data field in a FoamSubApplicationClass object. This field is noted below.

Code Display 2-1 FoamSubApplicationClass Instance Data

@instance FApplication 	FSAI_launcherApp;

FSAI _launcherApp stores an FApplication type referring to the application it should reside under.

For FoamSubApplication objects, the following FApplication values are the only values that are relevant:

FA_SERVICES_APP
FA_SYSTEM_APP
FA_EXTRAS_APP
| Up: GEOS SDK TechDocs | Up | Prev: 1 FoamSubApplicationClass | Next: 1.2 Using a FoamSubApplication Object


Using a FoamSubApplication Object

Communicator UI: 1.2 FoamSubApplicationClass: Using a FoamSubApplication Object

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 FoamSubApplicationClass Instance Data | Next: 2 ComplexMonikerClass
MSG_FSA_RETURN_TO_LAUNCHER

FoamSubApplication objects do not close like other applications. Because of this, they need to provide their own special "Close" trigger. However, FoamSubApplication objects should not simply "close" when the user presses this trigger. Instead, they should send MSG_FSA_RETURN_TO_LAUNCHER to this FoamSubApplication object.

This message changes the [hardIconBar] entry of the .INI file for the sub-application to be the launcher application (stored in FSAI_ launcherApp ). This allows the sub-application to seemingly close without closing the parent application. It is the responsibility of the application to provide a "Close" trigger that sends this message to the FoamSubApplicationClass object.

The "Close" trigger is declared as a ComplexMonikerClass object of type CMT_CLOSE. The ComplexMoniker is discussed more fully in ComplexMonikerClass .

Code Display 2-2 Using the FoamSubApplicationClass Object

/*
 * Declare the FoamSubApplication object as you would a normal GenApplication
 * object. This sub-app will be launched from the Extras application.
 */
@start   AppResource
@object FoamSubApplicationClass MyApp = {
    GI_comp = @MyPrimary;
    FSAI_launcherApp = FA_EXTRAS_APP;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = @MyPrimary;
    GI_visMoniker = list { @MyTextMoniker };
}
@visMoniker MyTextMoniker = "Nokia UI\rSample App";
@end     AppResource;
/*
 * Somewhere in your sub-app, create a CMT_CLOSE trigger.
 */
@object ComplexMonikerClass CloseTrigger = {
    ComplexMoniker = GenTriggerClass;
    CMI_topText = CMT_CLOSE;
    GTI_actionMsg = MSG_FSA_RETURN_TO_LAUNCHER;
    GTI_destination = @TemplateApp;
    HINT_SEEK_MENU_BAR;
    HINT_SEEK_REPLY_BAR;
    /*
     * Close Command triggers should always be in the fourth position (3 because
     * HINT_SEEK_SLOT refers to zero-based slot positions).
     */
    HINT_SEEK_SLOT = 3;
}
| Up: GEOS SDK TechDocs | Up | Prev: 1.1 FoamSubApplicationClass Instance Data | Next: 2 ComplexMonikerClass


ComplexMonikerClass

Communicator UI: 2 ComplexMonikerClass

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 FoamSubApplicationClass | Next: 3 Convenient Dialog Routines

ComplexMonikerClass is a versatile class that provides powerful behavior with little overhead. A complex moniker serves two purposes. First, it allows a developer to easily create a moniker containing both graphics and text. Second, it allows both text and graphics to be easily localized by referring to both items "generically."

A complex moniker may take a variety of forms. ComplexMonikerClass is what's known in GEOS as a "variant" class, which means that the object's parent class is dynamically assigned when the object is visually built.

A complex moniker may contain both a text string and a bitmap. The text string may be either a single line or two lines (separated by a `\r' character in the text buffer). If the text is two lines, it will be right-justified; this allows text strings in the command bar to align properly.

ComplexMoniker Instance Data

ComplexMoniker Usage

ComplexMoniker Triggers

ComplexMoniker Messages

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 FoamSubApplicationClass | Next: 3 Convenient Dialog Routines


ComplexMoniker Instance Data

Communicator UI: 2.1 ComplexMonikerClass: ComplexMoniker Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 2 ComplexMonikerClass | Next: 2.2 ComplexMoniker Usage

ComplexMonikerClass contains the following instance data:

Code Display 2-3 ComplexMonikerClass Instance Data

ComplexMoniker			ClassName
@instance TextStyle			CMI_textStyle = (TS_BOLD);
@instance word 			CMI_topText;
@instance word 			CMI_iconBitmap;
@instance byte 			CMI_fontSize;

The ComplexMoniker Keyword

Because ComplexMonikerClass is a variant class, it needs to have its superclass assigned when it is built. The ComplexMoniker keyword represents the parent class of the complex moniker. By setting this keyword to a class name the parent class is changed from the default superclass GenClass to the specified class. Any child of GenClass may be a complex moniker superclass. The following are commonly used superclasses:

GenTriggerClass
GenInteractionClass
GenItemGroupClass
GenItemClass
GenBooleanGroupClass
GenDynamicListClass
ContactListClass

Each of these classes will behave quite differently, but all will exhibit the same characteristics in displaying their text and icon monikers that are gained through using ComplexMonikerClass. Note that using a ComplexMoniker object will cause the specific UI to ignore any moniker stored in the GI_ visMoniker instance data field in the above classes.

The ComplexMoniker Text

CMI _topText stores the "text" to display. This may be either a Chunkhandle to a text string, or a ComplexMonikerText identifier. If the value is a ComplexMonikerText identifier, the specific UI will determine what exact text is displayed.

As noted, the text may contain two lines. The top text is limited to COMPLEX_MONIKER_MAX_TOP_TEXT_LENGTH while the bottom is limited to COMPLEX_MONIKER_MAX_BOTTOM_TEXT_LENGTH.

Below is a complete list of types; in most cases the (English) text on the Nokia 9000i device is similar, if not identical, to the text alluded to by the identifier's name. Those cases where the actual text is noticeably different are noted. Using such an identifier is beneficial for localization purposes.

CMT_NULL
Never set a moniker to this value. It exists purely as a placeholder.
Basic strings:
CMT_OK
CMT_CLOSE
CMT_CANCEL
CMT_YES
CMT_NO
Command strings:
CMT_CLEAR
CMT_CLEAR_ALL
CMT_CHANGE
CMT_RENAME
CMT_COPY
CMT_SETTINGS
CMT_OPEN
CMT_CREATE_NEW "New"
CMT_DELETE
CMT_ROTATE
CMT_SELECT
CMT_START
CMT_GO
CMT_INSTALL
CMT_DEINSTALL "Remove"
CMT_NEXT
CMT_PREVIOUS
CMT_USER_DEFINED "User defined:"
CMT_ZOOM
CMT_ZOOM_IN
CMT_ZOOM_OUT
CMT_VIEWING_TOOLS
CMT_OPTIONS
Communication strings:
CMT_PHONE "On"
CMT_CALL
CMT_CALL_LOWERCASE "Call"
CMT_SEND
CMT_SEND_OPTIONS
CMT_CONNECT
CMT_CALLS
CMT_FAXES
CMT_DATA_CALLS
CMT_MESSAGES
CMT_RECENT_CALLS
CMT_RECENT_FAXES "Recent numbers"
CMT_RECENT_MESSAGES "Recent numbers"
CMT_RECEIVED_CALLS
CMT_RECEIVED_FAXES "Received fax numbers"
CMT_RECEIVED_MESSAGES "Received message numbers"
CMT_MISSED_CALLS
CMT_LAST_DIALED_CALLS "Dialed calls"
CMT_TO_COLON "To:"
CMT_FROM_COLON "From:"
CMT_TEXT_SEND "Send text"
CMT_TEXT_CAPTURE "Capture text"
CMT_CAPTURE
CMT_STOP_CAPTURE
CMT_FORWARD_FAX "Forward"
CMT_SELECT_RECEIVER "Recipient"
CMT_HANGUP
CMT_FORWARD
CMT_REPLY
Miscellaneous commands:
CMT_CLEAR_LIST
CMT_NO_CONTACT_INFORMATION "No log information"
CMT_CONTACT_CARD
CMT_NONE
CMT_CLEAR_ALL_COMMAND
CMT_ACCEPT
CMT_RESET
CMT_STOP
CMT_ACTIVATE
CMT_DISABLE
CMT_HELP
CMT_BACK
CMT_SPECIAL_KEYS_COMMAND "Keys"
CMT_SPECIAL_KEYS
CMT_TEXT
CMT_TEXTS
CMT_ZOOM_PLUS "Zoom in"
CMT_ZOOM_MINUS "Zoom out"
CMT_INSERT
CMT_OFF
CMT_ON
CMT_MENU /* Not available on build 4.8.8 or earlier versions of the Communicator */
CMT_SEARCH /* Not available on build 4.8.8 or earlier versions of the Communicator */

Code Display 2-4 Setting a Complex Moniker's Text

/*
 * The text in a complex moniker is stored within the CMI_topText instance field.
 * This text may either be referenced by a ChunkHandle ...
 */
@object ComplexMonikerClass MyMoniker = {
    ComplexMoniker = GenTriggerClass;
    CMI_topText = @MyText;
}
/* Use the TCHAR type to ensure the text is DBCS-compliant. */
@chunk TCHAR MyText[] = "Sample Text";
/*
 * ... or CMI_topText can store a ComplexMonikerText type which indicates the text
 * generically.
 */
@object ComplexMonikerClass MyClearTrigger = {
    ComplexMoniker = GenTriggerClass;
    CMI_topText = CMT_CLEAR;
}

The ComplexMoniker Icon Bitmap

CMI_ iconBitmap stores the bitmap to display within this moniker. This may either be a ChunkHandle to a Bitmap structure or a member of the ComplexMonikerBitmap types. These types are enumerated below:

CMB_LEFT_FINGER
CMB_RIGHT_FINGER
CMB_CONTACT_CARD
CMB_CONTACT_LIST
CMB_SPEED_DIAL
CMB_SETTINGS_TITLE_ICON
CMB_SERVICES_VIEW
CMB_SERVICES_DEFINE
CMB_QUESTION_ICON
CMB_OPEN_FOLDER
CMB_DRAWER
CMB_RENAME_COPY
CMB_FOLDER
CMB_SECURITY_SUCCESS
CMB_SECURITY_ERROR
CMB_PC_ICON
CMB_INPUT_TITLE_ICON
CMB_WARNING_TITLE_ICON
CMB_SEARCH /* Not available on build 4.8.8 or earlier versions of the Communicator */
CMB_SMS_CARD_ICON /* Not available on build 4.8.8 or earlier versions of the Communicator */

Again, using a generic identifier eases localization.

You can create your own bitmaps by using the GEOS Icon Editor. To do so, perform the following steps:

  • Start GEOS (C:\PCGEOS\GEOS2XNC\LOADER.EXE
  • Launch the Icon Editor application.
  • Select either the 5th format (32x20 monochrome) or the 1st format (48x30 color). The first is used to create a monochrome bitmap; the second is used to create a 4-bit grayscale bitmap.
  • Edit the bitmap as desired.
  • The Nokia 9000i Communicator is a grayscale device. It is capable of handling 4-bit grayscale bitmaps ( BMFormat of BMF_4BIT). You may also create simple monochrome bitmaps ( BMFormat of BMF_MONO).
  • The GEOS Icon Editor only creates color bitmaps. The following colors in the Icon Editor will be mapped to the matching grey-scale shades on the device to create a grayscale bitmap:

    Icon Editor Color

    Nokia 9000i

    WHITE

    WHITE

    BLACK

    BLACK

    LIGHT_VIOLET

    LIGHT GRAY

    DARK_GREEN

    DARK GRAY
    (For more information about mapping colors to Nokia 9000i Communicator grays, refer to the N9000i Topics Sheet.)
  • Write the file out as filename .goh using the following options:
    Current Format
    Vismoniker source code
    Compacted
    Write mask (default)
  • Edit the first line of the filename .goh file and replace this line:
@visMoniker Moniker ={
  • with:
@chunk byte YourIconName[] = {
  • Delete the following lines from the beginning of the file:
size = <...>;
style = <...>;
color = <...>;
aspectRatio = <...>;
cachedSize = <...,...>;
gstring { GSFillBitmapAtCP(<...>),
  • Delete the following lines from the end of the file:
GSEndString()
}
  • Finally, add a semi-colon to the end of the file.

Your resulting bitmap, ready for inclusion in your app, should look like this:

@chunk byte MyIconName[] = {
    Bitmap (32,20,BMC_PACKBITS,
            (BMT_MASK|BMF_4BIT)),
    0xfd, 0xff, 0x03, 0xbf, ...
};

Text Styles

CMI_ textStyle stores a GEOS TextStyle , defined in graphics.h . By default, text is shown as TS_BOLD. The full list of styles appears below:

TS_BOLD (default)
TS_OUTLINE
TS_ITALIC
TS_SUPERSCRIPT
TS_SUBSCRIPT
TS_STRIKE_THRU
TS_UNDERLINE

Font Sizes

CMI _fontSize stores a font size (in points). There are four pre-established font sizes that you should choose from:

#define FOAM_LARGE_FONT_SIZE					20
#define FOAM_NORMAL_FONT_SIZE					18
#define FOAM_LIGHT_FONT_SIZE					17
#define FOAM_SMALL_FONT_SIZE					16

The large font size is best reserved for commands and title bar monikers. In most other cases, use the normal font size.

The default font can be obtained by those routines or objects that demand a font name by using the VisTextDefaultFont value VTDF_RESPONDER.

ComplexMoniker Vardata

There are several vardata attributes that you may attach to the Complex Moniker. These are noted below:

Code Display 2-5 ComplexMoniker Vardata

@vardata		void	ATTR_COMPLEX_MONIKER_PLACE_BITMAP_AT_LEFT;
@vardata		void	HINT_COMPLEX_MONIKER_DRAW_SEPARATOR;
@vardata		void	HINT_COMPELX_MONIKER_DONT_DRAW_SEPARATOR;

ATTR_COMPLEX_MONIKER_PLACE_BITMAP_AT_LEFT will position the icon bitmap (if any) to the left of the text.

HINT_COMPLEX_MONIKER_DRAW_SEPARATOR draws a single-line separator below the complex moniker.

HINT_COMPLEX_MONIKER_DONT_DRAW_SEPARATOR prevents the drawing of a single-line separator below the complex moniker.

| Up: GEOS SDK TechDocs | Up | Prev: 2 ComplexMonikerClass | Next: 2.2 ComplexMoniker Usage


ComplexMoniker Usage

Communicator UI: 2.2 ComplexMonikerClass: ComplexMoniker Usage

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 ComplexMoniker Instance Data | Next: 2.3 ComplexMoniker Triggers
MSG_COMPLEX_MONIKER_GET_MONIKER, MSG_COMPLEX_MONIKER_REPLACE_MONIKER

ComplexMonikers are used for constructing lists, dialog boxes, and command triggers. Below are examples of a dialog box and a dynamic list. The next section will discuss command triggers. If you are not creating a command trigger, set CMI_fontSize to something other than FOAM_LARGE_FONT_SIZE.

Code Display 2-6 Complex Moniker Example (GenInteraction)

@object ComplexMonikerClass AboutBox = {
/* This object will behave as a GenInteration (grouping) object */
    ComplexMoniker = GenInteractionClass;
    CMI_topText = @AboutBoxTitle;
    CMI_iconBitmap = @AboutIcon;
    GI_comp = @AboutText;
/* Geometry Hints */
    HINT_DRAW_IN_BOX;
    HINT_DRAW_SHADOW;
    HINT_PLACE_MONIKER_TO_LEFT;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
/* ComplexMoniker Hints */
    HINT_COMPLEX_MONIKER_DRAW_SEPARATOR;
    ATTR_COMPLEX_MONIKER_PLACE_BITMAP_AT_LEFT;
}
@chunk TCHAR AboutBoxTitle[] = "CmplxMon Sample Application";

Code Display 2-7 ComplexMoniker Example (GenDynamicList)

@object ComplexMonikerClass CMTextsList = {
    ComplexMoniker = GenDynamicListClass;
    CMI_topText = @CMTextsListTitle;
    CMI_fontSize = FOAM_NORMAL_FONT_SIZE;
    CMI_iconBitmap = CMB_OPEN_FOLDER;
    GIGI_destination = process;
    GIGI_applyMsg = MSG_CMPLXMON_PROCESS_SHOW_CURRENT_CMTEXT;
    GDLI_queryMsg = MSG_CMPLXMON_PROCESS_GET_CMTEXT;
/* GDLI_numItems will be set at app-launch time. */
    HINT_DRAW_IN_BOX;
    HINT_DRAW_SHADOW;
    HINT_PLACE_MONIKER_ABOVE;
    HINT_PLACE_MONIKER_TO_LEFT;
    HINT_COMPLEX_MONIKER_DRAW_SEPARATOR;
    HINT_MINIMIZE_CHILD_SPACING;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
    HINT_ITEM_GROUP_SCROLLABLE;
    ATTR_COMPLEX_MONIKER_PLACE_BITMAP_AT_LEFT;
    ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED;
    ATTR_GEN_ITEM_GROUP_STATUS_MSG =
    MSG_CMPLXMON_PROCESS_SHOW_CURRENT_CMTEXT;
    }
}
| Up: GEOS SDK TechDocs | Up | Prev: 2.1 ComplexMoniker Instance Data | Next: 2.3 ComplexMoniker Triggers


ComplexMoniker Triggers

Communicator UI: 2.3 ComplexMonikerClass: ComplexMoniker Triggers

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 ComplexMoniker Usage | Next: 2.4 ComplexMoniker Messages

The four command triggers on the Nokia 9000 appear on the right side of the screen and correspond with the four off-screen buttons. Use a ComplexMoniker to construct a trigger. Recent versions of the 9000i Communicator (software version 4.8.8 or later) allow for a fifth "Menu" Command trigger that does not appear along the right edge of the screen and is activiated by a special off-screen "Menu" button.

"Call", "Voice mailbox" and "Recent calls" are this application's triggers. "Call" is in slot 0, "Voice mailbox" is in slot 2, etc. This particular application does not use slot 3, but does have a "Menu" trigger that does not appear in the side bar.

For a discussion of Command trigger usage conventions (e.g., "Where should I put the Close trigger?"), refer to the Communicator Fact Sheet.

To create a ComplexMoniker command trigger:

  • Set the ComplexMoniker keyword to GenTriggerClass .
  • Include the hint HINT_SEEK_MENU_BAR and HINT_SEEK_REPLY_BAR in the object declaration.
  • Set the HINT_SEEK_SLOT hint to 0,1,2, or 3, corresponding to the (zero-based) slot you wish to place the command trigger within. The top slot is 0, the bottom slot is 3.

If the Command trigger is the "Menu" trigger, then include the hints for a Command trigger but specify that CMI_topText = CMT_MENU; and leave out the HINT_SEEK_SLOT hint. This causes the "Menu" trigger to not appear at the right side of the screen, but still be active. If you are supporting an older version of the 9000i Communicator that does not support the menu trigger (before version 4.8), then this trigger will be automatically placed in an available command trigger slot. Applications that will support older devices should check the version number and make sure that there will be an available slot for the "Menu" trigger.

Code Display 2-8 ComplexMoniker Example (Command Trigger)

@object ComplexMonikerClass MyCommandTrigger = {
    ComplexMoniker = GenTriggerClass;
    CMI_topText = @CommandTextTitle;
    GTI_actionMsg = MSG_GEN_INTERACTION_INITIATE;
    GTI_destination = @DialogThatThisTriggerBringsUp;
    HINT_SEEK_MENU_BAR;
    HINT_SEEK_REPLY_BAR;
    HINT_SEEK_SLOT = 0;
}
@chunk byte CommandTextTitle[] = "I Command Thee!";

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 ComplexMoniker Usage | Next: 2.4 ComplexMoniker Messages


ComplexMoniker Messages

Communicator UI: 2.4 ComplexMonikerClass: ComplexMoniker Messages

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 ComplexMoniker Triggers | Next: 3 Convenient Dialog Routines
MSG_COMPLEX_MONIKER_GET_MONIKER, MSG_COMPLEX_MONIKER_REPLACE_MONIKER

To retrieve a complex moniker, send the ComplexMoniker MSG_COMPLEX_MONIKER_GET_MONIKER . This message takes a single argument, a pointer to a structure of type GetComplexMoniker . The structure will be filled in with the relevant information by the message.

typedef struct {
    TextStyle				GCM_textStyle;
    byte				GCM_fontSize;
    ComplexMonikerText			GCM_topText;
    ComplexMonikerBitmap		GCM_iconBitmap;
} GetComplexMoniker;

Send MSG_COMPLEX_MONIKER_REPLACE_MONIKER to replace a complex moniker with a new moniker.

This message takes two arguments: a ReplaceComplexMoniker structure and a ReplaceComplexMonikerChunkHandles structure.

The ReplaceComplexMoniker structure should hold the parameters for the new moniker.

typedef struct {
    TextStyle				RCM_textStyleSet;
    TextStyle				RCM_textStyleClear;
    word				RCM_fontSize;
    dword				RCM_topTextSource;
    dword				RCM_iconBitmapSource;
    ComplexMonikerSourceType		RCM_topTextSourceType;
    ComplexMonikerSourceType		RCM_iconBitmapSourceType;
    word				RCM_iconBitmapSize;
    RCMOverwrite			RCM_overwrite;
} ReplaceComplexMoniker;

RCM_textStyleSet stores a TextStyle record of styles that should be set (masked) on top of any currently existing styles.

RCM_textStyleClear stores a TextStyle record of styles that should be cleared if they are set among the currently existing styles. Note: Currently these bits are inverted rather than cleared.

RCM_fontSize stores the new font size. Pass 0 to indicate no change.

RCM_topTextSource stores a dword value containing, or pointing to, the source for the new text. The type of value stored here is indicated in RCM_topTextSourceType.

RCM_iconBitmapSource stores a dword value containing, or pointing to, the source for the new bitmap. The type of value stored here is indicated in RCM_iconBitmapSourceType.

RCM_topTextSourceType stores the type of text reference contained in RCM_topTextSource.

RCM_iconBitmapSourceType stores the type of bitmap reference contained in RCM_iconBitmapSource.

RCM_iconBitmapSize stores the size of the icon bitmap, in bytes.

RCM_overwrite indicates whether the existing complex moniker chunks should be overwritten. Pass TRUE if you wish this occur.

MSG_COMPLEX_MONIKER_REPLACE_MONIKER also passes a ReplaceComplexMonikerChunkHandles structure. This structure is used to place returned chunk handles of the text and bitmap.

typedef struct {
    ChunkHandle			RCMCH_topText;
    word			RCMCH_unusedCX;
    ChunkHandle			RCMCH_iconBitmap;
    word			RCMCH_unusedBP;
} ReplaceComplexMonikerChunkHandles;

RCMCH_topText contains the ChunkHandle of the text portion of the new complex moniker.

RCMCH_iconBitmap contains the ChunkHandle of the bitmap (graphics) portion of the new complex moniker.

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 ComplexMoniker Triggers | Next: 3 Convenient Dialog Routines


Convenient Dialog Routines

Communicator UI: 3 Convenient Dialog Routines

| Up: GEOS SDK TechDocs | Up | Prev: 2 ComplexMonikerClass | Next: 4 FlashingNoteClass

The Foam library provides a number of routines useful for displaying simple dialog boxes to the user. Each of these displays a dialog box. The dialog box will have one or more buttons. Most of the functions have a return value; the function's return value will be an InteractionCommand value corresponding to which button the user pressed to dismiss the dialog. The FoamDisplay...NoBlock() routines don't have an explicit return value; they send a message if the user presses the OK button.

The following routines are available:

FoamDisplayWarning() , FoamDisplayWarningNoBlock()
These routines present a dialog box notifying the user about an action. The user may dismiss the dialog, but may not cancel the action. The function takes one argument, the optr of a string buffer to display. This function has no return value.
FoamDisplayError() , FoamDisplayErrorNoBlock()
These routines present a dialog box notifying the user that an error has occurred. The user may dismiss the dialog via the OK button. The function takes one argument: the optr of the string buffer containing the text to display. The function has no return value.
FoamDisplayOkCancelWarning()
This function presents the user with a warning in an OK/Cancel dialog. The function takes one argument: the optr of the string buffer to display. This function returns an InteractionCommand value; if the user presses the OK button, the function returns IC_APPLY.
FoamDisplayQuestion()
This routine presents a Yes/No dialog. It takes one argument, the optr of the text buffer containing the string to display. If the user presses the Yes button, the function will return IC_YES.
FoamDisplayNote() , FoamDisplayNoteNoBlock()
These functions display an OK dialog box displaying a text message to the user. The function takes one argument, the optr to a string buffer with the text to display.
FoamDisplayDeleteWarning()
This routine presents an OK/Cancel dialog box asking for a file deletion confirmation. This function takes three arguments: a pointer to a string buffer containing the file name, and optrs to two string chunks: one to display before the file name, and one to display after. If the function returns IC_YES, the user has confirmed that the file should be deleted.
FoamDisplayDeleteWarningNoBlock()
This routine presents an OK/Cancel dialog box asking for a file deletion confirmation. This function takes three arguments: the optr of the text string asking the user for confirmation, the message to send if the Delete button is pressed, and the object to send that message to. This function has no explicit return value. If the user Cancels the dialog, it won't send any message.

For dialog box types not provided by these functions, you will have to set up your UI, using GenInteraction objects in the usual way--or you may use the FlashingNoteClass or FoamProcessDialogClass , described later in this chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 2 ComplexMonikerClass | Next: 4 FlashingNoteClass


FlashingNoteClass

Communicator UI: 4 FlashingNoteClass

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Convenient Dialog Routines | Next: 5 FoamProgressDialog

Sometimes, you may want to send a notification to a user without requiring the user to respond. Ideally, you would like a dialog box to appear on screen for a few seconds, and then go away, or automatically disappear once any key is pressed. You can get this behavior with a FlashingNoteClass object.

You can provide the following parameters to a Flashing Note object:

  • A minimum duration to stay on-screen. After the minimum duration, the dialog can be dismissed by pressing any key.
  • A maximum duration to stay on-screen. (The dialog will dismiss itself if this time is reached.)

A Flashing Note should be placed as the root of an unattached tree within its own UI block (resource). In this manner, it will act as a template. To initiate the flashing note, call the routine PutUpDialogViaUIThread(), passing the optr of the dialog box template. This routine will copy the template resource, attach it to the application tree, and manage its thread of execution. The dialog will be run within the specific UI thread to avoid blocking the application thread.

FlashingNote Instance Data

Setting Up the Template

Initiating and Destroying the Flashing Note

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Convenient Dialog Routines | Next: 5 FoamProgressDialog


FlashingNote Instance Data

Communicator UI: 4.1 FlashingNoteClass: FlashingNote Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 4 FlashingNoteClass | Next: 4.2 Setting Up the Template

FlashingNoteClass only contains one piece of instance data, but it also changes the defaults for its superclass, GenInteractionClass .

Code Display 2-9 FlashingNoteClass Instance Data

#define FLASHING_NOTE_DEFAULT_DURATION						(3*60) /* 3 seconds */
#define FLASHING_NOTE_DEFAULT_MINIMUM_DURATION						(1*60) /* 1 second */
@instance 	word		FNI_duration;
@default GII_attrs =				GIA_NOT_USER_INITIATABLE | GIA_SYS_MODAL);
@default GII_visibility =				GIV_DIALOG;
@default GII_type = 				GIT_MULTIPLE_RESPONSE;
@default FNI_duration = 				FLASHING_NOTE_DEFAULT_DURATION;
@vardata word		HINT_FLASHING_NOTE_DESTROY_SELF_AFTER_DISMISS;
@vardata word		ATTR_FLASHING_NOTE_MINIMUM_DURATION;

FNI _duration stores the maximum duration (in ticks -- 1/60ths of a second) for the dialog to remain on-screen. Do not set this to zero; doing so will make the dialog box "permanent" and incapable of dismissal.

| Up: GEOS SDK TechDocs | Up | Prev: 4 FlashingNoteClass | Next: 4.2 Setting Up the Template


Setting Up the Template

Communicator UI: 4.2 FlashingNoteClass: Setting Up the Template

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 FlashingNote Instance Data | Next: 4.3 Initiating and Destroying the Flashing Note

Usually, a FlashingNoteClass object (and its children) should be contained within their own resource. The resource should remain unattached to the application tree. The routine PutUpDialogViaUIThread() will take care of copying and attaching the group of objects.

To set up your template, perform the following steps:

  • Declare the template's resource and make it notDetachable .
  • Define the root object as a FlashingNoteClass object.
  • Set the root object not usable (~GS_USABLE).
  • Set the FNI_ duration instance field to the number of ticks (1/60ths of a second) you wish to have the dialog box on-screen. By default, this is 180 ticks (3 seconds).
  • If you want a minimum time for the dialog box to remain on-screen, add ATTR_FLASHING_NOTE_MINIMUM_DURATION and set it to the number of ticks (1/60ths of a second) to remain on-screen. By default, this is 60 ticks (one second).
  • Add HINT_FLASHING_NOTE_DESTROY_SELF_AFTER_DISMISS if you don't want to manually destroy it.
  • Add the hints HINT_WINDOW_NOT_TITLE_BAR and HINT_DRAW_IN_BOX .

Code Display 2-10 FlashingNote Template

@start FlashingNoteTemplateResource, notDetachable;
@object FlashingNoteClass FlashingNoteTemplate = {
    GI_states = @default & ~GS_USABLE;
    GI_comp = @DialogBoxTemplate;
    FNI_duration = 600;						/* 10 seconds */
    HINT_DRAW_IN_BOX;
    HINT_FLASHING_NOTE_DESTROY_SELF_AFTER_DISMISS;
    HINT_WINDOW_NO_TITLE_BAR;
    ATTR_FLASHING_NOTE_MINIMUM_DURATION = 300;						/* 5 seconds */
}
/*
 * We want a separator, so we will create a sub-group interaction as a complex
 * moniker.
 */
@chunk TCHAR DialogTemplateText[] = "Flashing Note";
@object ComplexMonikerClass DialogBoxTemplate = {
    ComplexMoniker = GenInteractionClass;
    CMI_topText = @DialogTemplateText;
    CMI_fontSize = FOAM_NORMAL_FONT_SIZE;
    GI_comp = @DialogText;
    HINT_PLACE_MONIKER_ABOVE;
    HINT_COMPLEX_MONIKER_DRAW_SEPARATOR;
    HINT_FIXED_SIZE = { SST_PIXELS | FOAM_DEFAULT_BUBBLE_WIDTH, 0 };
}
@chunk TCHAR DialogTextText[] = "This particular flashing note will stay up a
                minimum \r
                of 5 seconds, and will dismiss itself if you
                press any key after that.\r\r\
                It will dismiss itself automatically after 10
                seconds.\r";
@object GenTextClass DialogText = {
    GTXI_text = @DialogTextText;
    GI_attrs = @default | GA_READ_ONLY;
}
@end FlashingNoteTemplateResource;
| Up: GEOS SDK TechDocs | Up | Prev: 4.1 FlashingNote Instance Data | Next: 4.3 Initiating and Destroying the Flashing Note


Initiating and Destroying the Flashing Note

Communicator UI: 4.3 FlashingNoteClass: Initiating and Destroying the Flashing Note

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Setting Up the Template | Next: 5 FoamProgressDialog

Initiating the flashing note is simple. Simply call PutUpDialogViaUIThread() , passing the optr of the root Flashing Note object.

If that object contains HINT_FLASHING_NOTE_DESTROY_AFTER_DISMISS , the dialog will be automatically detached and destroyed after it disappears from view (though the template block will remain untouched, of course).

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Setting Up the Template | Next: 5 FoamProgressDialog


FoamProgressDialog

Communicator UI: 5 FoamProgressDialog

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 FlashingNoteClass | Next: 6 Routines for Lists

When performing a time-consuming task, it is wise to prevent user frustration and use a FoamProcessDialogClass which informs the user of the task's progress and offers much-appreciated feedback.

The FoamProgress dialog provides the following functionality:

  • A default animation of gears in motion, informing the user that an action is taking place.
  • A "status indicator" consisting of a series of five dots. These five dots get displayed as the task gets closer to completion. Next to this status indicator, you may include status text, if desired.
  • Text which describes the task.
  • A default Cancel trigger.

As with a Flashing Note, a FoamProgress dialog should be placed as the root of an unattached tree within its own UI block (resource). To initiate the Foam Progress dialog, call the routine PutUpDialogViaUIThread(), passing the optr of the dialog box template. This routine will copy the template resource, attach it to the application tree, and manage its thread of execution. The dialog will be run within the specific UI thread, avoiding blocking the application thread.

The dialog can be taken down with TakeDownDialogViaUIThread() or by placing HINT_FOAM_PROGRESS_DIALOG_DESTROY_SELF_AFTER_DISMISS on the object.

FoamProgressDialog Instance Data

Setting Up the Template

Using a FoamProgressDialog

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 FlashingNoteClass | Next: 6 Routines for Lists


FoamProgressDialog Instance Data

Communicator UI: 5.1 FoamProgressDialog: FoamProgressDialog Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 5 FoamProgressDialog | Next: 5.2 Setting Up the Template

FoamProgressDialogClass contains several pieces of instance data, and also alters the defaults of its superclasses, GenControlClass and GenInteractionClass .

Code Display 2-11 FoamProgressDialogClass Instance Data

@instance		ChunkHandle		FPDI_descriptionText			= NullChunk;
@instance		ChunkHandle		FPDI_statusText			= NullChunk;
@instance		optr		FPDI_cancelDestination			= NullOptr;
@instance		word		FPDI_cancelActionMsg			= 0;
@vardata void		HINT_FOAM_PROGRESS_DIALOG_DESTROY_SELF_AFTER_DISMISS;
@vardata void		ATTR_FOAM_PROGRESS_DIALOG_DONT_DISMISS_ON_CANCEL;
@default		GII_visibility		= GIV_POPUP;
@default		GII_type		= GIT_MULTIPLE_RESPONSE;
@default		GII_attrs		= (GIA_NOT_USER_INITIATABLE | GIA_MODAL);
@default		GI_attrs		= GA_SIGNAL_INTERACTION_COMPLETE;
@default		GI_states		= (@default | GS_ENABLED);

FPDI _descriptionText contains text to display to the user, informing them of the task being performed.
FPDI_ statusText contains text to display next to the status indicator.
FPDI _cancelDestination contains the object that should receive notification (via FPDI _cancelActionMsg ) if the action is cancelled by the user. The object must be capable of handling cancellation in a clean manner.
FPDI_ cancelActionMsg stores the message to send if the user cancels the operation. This message will be sent to the FPDI_ cancelDestination , where it should be handled appropriately.
HINT_FOAM_PROGRESS_DIALOG_DESTROY_SELF_AFTER_DISMISS indicates that the resource block containing this dialog should be destroyed after the dialog is dismissed. This hint indirectly calls UserDestroyDialog(); do not use this hint if you will use TakeDownDialogViaUIThread() instead.
ATTR_FOAM_PROGRESS_DIALOG_DONT_DISMISS_ON_CANCEL indicates that the dialog will not be dismissed if the user presses "Cancel." The application is responsible for handling this situation, and eventually dismissing the dialog.
| Up: GEOS SDK TechDocs | Up | Prev: 5 FoamProgressDialog | Next: 5.2 Setting Up the Template


Setting Up the Template

Communicator UI: 5.2 FoamProgressDialog: Setting Up the Template

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 FoamProgressDialog Instance Data | Next: 5.3 Using a FoamProgressDialog

A FoamProgressDialogClass object (and its children) should be contained within its own resource. The resource should remain unattached to the application tree. The routine PutUpDialogViaUIThread() will take care of copying and attaching the group of objects.

To set up your template, perform the following steps:

  • Declare the template's resource and make it notDetachable .
  • Define the root object as a FoamProgressDialogClass object.
  • Set the root object not usable (~GS_USABLE).
  • Set the object GIA_SYS_MODAL (and make sure to turn off its default GIA_MODAL flag).
  • Set the FPDI_ descriptionText instance field with text describing the action.
  • Set FPDI_ statusText with text to reside along the status indicator.
  • Set HINT_DRAW_IN_BOX.

Code Display 2-12 FoamProgressDialog Template

@start FoamProgressDialogTemplate, notDetachable;
@chunk TCHAR DescriptionText[] = "While, we're waiting a series of dots will appear below."
@chunk TCHAR StatusText[] = "Here are the dots: ";
@object FoamProgressDialogClass ProgressSample = {
    FPDI_descriptionText = @DescriptionText;
    FPDI_statusText = @StatusText;
    GII_attrs = (@default | GIA_SYS_MODAL) & ~GIA_MODAL;
    GI_states = (@default) & ~GS_USABLE;
    HINT_DRAW_IN_BOX;
}
@end FoamProgressDialogTemplate;
| Up: GEOS SDK TechDocs | Up | Prev: 5.1 FoamProgressDialog Instance Data | Next: 5.3 Using a FoamProgressDialog


Using a FoamProgressDialog

Communicator UI: 5.3 FoamProgressDialog: Using a FoamProgressDialog

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Setting Up the Template | Next: 6 Routines for Lists

MSG_FOAM_PROGRESS_DIALOG_APPEND_STATUS_TEXT

Code Display 2-13 Using a FoamProgressDialog

static ShowProgressDialog ()
{
    optr			progressDialog;
    word			i;
    SemaphoreError			semResult;
    TCHAR			statusAppendStr[] = " . ";
    progressDialog = PutUpDialogViaUIThread(@FoamProgressDialogTemplate);
    for ( i=0; i<5; i++) {
    semResult = ThreadPTimedSem(cancelSemaphore, 0);
    if (semResult == SE_TIMEOUT) {
        @send ProgressDialog::MSG_MY_PROGRESS_DIALOG_CANCEL_RECEIVED();
    break;
    }
    else (
        ThreadVSem(cancelSempahore);
        TimerSkeep(30);
        @call progressDialog::MSG_FOAM_PROGRESS_DIALOG_APPEND_STATUS_TEXT(
                statusAAppendStr);
    }
    }
    TimerSleep(30);
    TakeDownDialogViaUIThread(progressDialog);
}
/* Handler for MSG_MY_PROGRESS_DIALOG_CANCEL_TRIGGERED */
@method MyProgressDialogClass, MSG_MY_PROGRESS_DIALOG_CANCEL_TRIGGERED
{
    SemaphoreError			semResult;
    semResult = ThreadPSem(cancelSemaphore);
    EC_ERROR_IF(
    (semResult == SE_TIMEOUT), CANNOT_GRAB_SEMAPHORE_CANCELPROGDIALOG);
}
/* Handler for MSG_MY_PROGRESS_DIALOG_CANCEL_RECEIVED */
@method MyProgressDialogClass, MSG_MY_PROGRESS_DIALOG_CANCEL_RECEIVED
{
    ThreadVSem(cancelSemaphore);
}
| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Setting Up the Template | Next: 6 Routines for Lists


Routines for Lists

Communicator UI: 6 Routines for Lists

| Up: GEOS SDK TechDocs | Up | Prev: 5 FoamProgressDialog | Next: 7 Zoom Levels UI

There are two routines useful for presenting the user with a list that is alphabetically sorted and/or presents has multi-column items.

The FoamGetLexicalOrder() routine is used for alphabetically ordering strings. It takes a char value and returns a byte-size number. This number represents that character's position in a sorted multi-language "alphabet". If you are presenting the user with a sorted list of strings, this is the routine to use to sort the list.

The CreateVisMonikerLine() routine puts together a multi-column visual moniker. It puts the moniker together from an array of strings and/or bitmaps you supply. It returns the optr of the new moniker. It takes the following arguments:

  • a font point size: All text will use this point size.
  • a font: All text will use this font.
  • the number of columns
  • an array of VisMonikerColumn structures: This data contains the data which will be combined into the multi-column moniker.

The VisMonikerColumn structure is defined:

typedef struct {
    byte            VMC_width; /* 0 for bitmaps */
    const void *    VMC_ptr;
    Justification   VMC_just;
    TextStyle       VMC_style;
    ColumnBorder    VMC_border;
} VisMonikerColumn;

For each field, pass a VMC_width to specify the number of pixels to allow to draw the text, or 0 to signal that this column is a bitmap. VMC_ptr should point to a string buffer if the column contains text; it should point to a simple bitmap if the column contains a bitmap.

When you are done using the multi-column moniker, it is up to you to free its memory. Get the memory block handle from the optr and pass that handle to MemFree() .

| Up: GEOS SDK TechDocs | Up | Prev: 5 FoamProgressDialog | Next: 7 Zoom Levels UI


Zoom Levels UI

Communicator UI: 7 Zoom Level

| Up: GEOS SDK TechDocs | Up | Prev: 6 Routines for Lists | Next: 8. FilterList, FilterText

Versions 4.8.8 or later of the 9000i Communicator allow the user to press an off-screen "Zoom" button to toggle between three text size settings -- "Zoom" levels -- and adjust the text size for more comfortable reading.

Normal zoom level.

Medium zoom.

High zoom.

Note that only some parts of the UI are enlarged as the user chooses a higher zoom level. This is done because it would be impossible to increase the size of some UI components, i.e. larger triggers would not fit into four slots. You should determine how you want your UI components to zoom, then specify the maximum zoom level (size) allowed for each UI component with the hint HINT_MAXIMUM_ZOOM_LEVEL = ... The three zoom levels are of the enumerated type ZoomLevel, which is defined as

typedef ByteEnum ZoomLevel;
#define ZL_ZOOM_1	0
#define ZL_ZOOM_2	1
#define ZL_ZOOM_3	2

HINT_MAXIMUM_ZOOM_LEVEL = ZL_ZOOM_1;, the default setting, means that a UI component cannot be zoomed larger than the normal size -- that is, it cannot be zoomed at all. HINT_MAXIMUM_ZOOM_LEVEL = ZL_ZOOM_3; implies that a UI component can be zoomed at the normal, medium, or maximum zoom levels.

The higher the zoom level, the larger the font size, and the fewer the number of characters that will fit into a fixed pixel length field. Thus, if you use a CreateVisMoniker...() routine, you will need to know how to size the VisMoniker so that it holds the same number of characters at any zoom level. To do this, first check the version number and make sure that it is software version 4.8.8 or later. If it is, then determine the new size of your font as follows:

Code Display 2-14 Determining the Point Size of a Zoomed Font

        /* Assume we have a word originalPointSize that is the original point size
           of the font. */

        /* The point size of the font, taking zoom level into consideration. */
        word zoomedSize;

        /* How 'zoomed in' we are. */
        ZoomLevel zoomLevel;

        zoomLevel  = FoamZoomGetLevel();
        zoomedSize = FoamConvertToZoomedSize( zoomLevel, originalPointSize );

        /* Now create the VisMoniker using zoomedSize as the font size. */

Ctrl-F1 is mapped to the "Zoom" button on the emulator.

| Up: GEOS SDK TechDocs | Up | Prev: 6 Routines for Lists | Next: 8. FilterList, FilterText


FilterList, FilterText

Communicator UI: 8. FilterList, FilterText

| Up: GEOS SDK TechDocs | Up | Prev: 7. Zoom Level | Next: 8.1. FilterList and FilterText Usage

FilterListClass and FilterTextClass objects provide the UI for interactive searches on lists of objects. When the user types a string into a FilterTextClass object, the FilterTextClass object calls a method that uses this text to create a filter for the FilterListClass object. This filter controls which monikers the FilterList displays, and in what order.

For example, the Composer application has a FilterListClass object that displays a long list of songs, and a FilterTextClass "Search" field. When the user types a string in the "Search" field, the FilterList displays only items that start with that string.

Composer Application FilterList, FilterTest.

A filter is an array of unique words (16-bit numbers) that controls which application item identifiers (the input to GDLI_queryMsg) are used at a given position on the displayed list (physical item identifiers). Filters let you map the internal list of monikers to any list configuration you want, including a list with multiple entries of the same moniker, an empty list, or a list in a new order.

For example, suppose we have a list of six colors (red, yellow, blue, green, gray, and orange) and a search box that narrows down the list of displayed colors. When the search field is empty, there is no filter and the list is displayed normally.

FilterList, FilterText Example: No Filter.

When we type 'g' into the search field, we only want to display colors that start with 'g'; green and gray. Since 'green' and 'gray' are the 3rd and 4th items (counting from zero) of the unfiltered list and we want to display them in the 0th and 1st entries of the filter list, we create the following filter, a word array with two entries:

Filter for FilterText 'g':
Entry # | Value
0 | 3
1 | 4

This filter is then passed to the filter list.

FilterList, FilterText Example: Filter for 'g'.

Here is a diagram of how filters map the application item identifier to the physical item identifier in the previous examples:

FilterList, FilterText: Filter Diagram.

If the search text filters out all items in the list then the moniker FLI_noMatchMoniker, which is optionally specified by the FilterListClass object, is shown.

FilterList, FilterText Example: No match for 'mauve'.

FilterList and FilterText Usage

FilterList Instance Data

FilterList Messages

FilterText Instance Data

FilterText Messages

| Up: GEOS SDK TechDocs | Up | Prev: 7. Zoom Level | Next: 8.1. FilterList and FilterText Usage


FilterList, FilterText Usage

Communicator UI: 8.1 FilterList and FilterText: FilterList, FilterText Usage

| Up: GEOS SDK TechDocs | Up | Prev: 8. FilterList, FilterText | Next: 8.2. FilterList Messages

The following example shows:

  • A FilterListClass object.
  • A FilterTextClass object.
  • A query message handler that determines the moniker for a given application item.
  • A filter message handler that uses the text of the FilterTextClass to create a filter for the FilterListClass.

Create a FilterListClass object as you would create a GenDynamicList. Fill in the necessary GenItemGroup and GenDynamicClass data and specify a query message handler GDLI_queryMsg that will get the moniker corresponding to an application item identifier (a word). You can also specify the monikers to show when the list is empty either because it has no items (FLI_noItemsMoniker) or because it filters out all items (FLI_noMatchMoniker).

Code Display: Creating a FilterList object

/* In the definition of your destination object's class, define a message based
 * on the prototype GEN_DYNAMIC_LIST_QUERY. This prototype passes two arguments:
 * list (the optr of the list sending info) and item (the application item
 * identifier). */

    @message (GEN_DYNAMIC_LIST_QUERY_MSG) MSG_QUERY_COLOR_MONIKER;

/* Set up the selections, just as you would in a normal GenItemGroup. */

typedef enum {
    MC_RED = 0, MC_YELLOW, MC_BLUE, MC_GREEN, MC_GRAY, MC_ORANGE
} MyColors;
#define NUM_COLORS MC_ORANGE+1

/* In the object declaration, include normal instance data for a GenItemGroup and
 * GenDynamicList, making sure to also fill in GDLI_numItems and GDLI_queryMsg with
 * the proper information. The list will retrieve and display monikers automatically
 * through the query message as they are needed, just like a GenDynamicList.
 * The visMoniker FLI_noItemsMoniker is displayed if GDLI_numItems is zero.
 * FLI_noMatchMoniker is displayed when GDLI_numItems is not zero
 * but the filter eliminates all items from the list.  */

@visMoniker ListTestNoItems = "< No Colors >";
@visMoniker ListTestNoMatch = "< No Matching Colors >";
@object FilterListClass ListTestFilterList = {
    GIGI_destination = process;
    GIGI_selection = MC_BLUE;
    GIGI_applyMsg = MSG_NOTIFY_COLOR_CHANGE;
    GIGI_numSelections = 1;
    GDLI_numItems = NUM_COLORS;
    GDLI_queryMsg = MSG_QUERY_COLOR_MONIKER;
    GIGI_behaviorType = GIGBT_NON_EXCLUSIVE;

    FLI_noItemsMoniker = @ListTestNoItems;
    FLI_noMatchMoniker = @ListTestNoMatch;

    HINT_ITEM_GROUP_SCROLLABLE;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
    HINT_ITEM_GROUP_SHOW_SELECTION_EVEN_WHEN_NOT_FOCUS;
}

Create a FilterTextClass object, a text field that behaves like TextWithListClass (its parent class) except that when the user types in the text field, this object sends out a filter message FTI_filterMsg after an adjustable timout period FTI_filterTimeout. It is common practice to put FilterListClass objects in a gray box with a "Search" moniker and search icon.

Code Display: Creating a FilterText object

/* Make a gray complex moniker box with the text "Search" and a search icon that holds
 * the FilterTextClass object. */

@object ComplexMonikerClass ListTestSearchBarGroup = {
    ComplexMoniker = GrayFrameClass;
    CMI_iconBitmap = CMB_SEARCH;
    CMI_topText = CMT_SEARCH;
    CMI_textStyle = @default & ~TS_BOLD;
    CMI_fontSize = FOAM_NORMAL_FONT_SIZE;
    GI_comp = @ListTestSearchBarTextEdit;

    HINT_CENTER_CHILDREN_VERTICALLY;
    HINT_CENTER_MONIKER;
    ATTR_COMPLEX_MONIKER_PLACE_BITMAP_AT_LEFT;
    HINT_PLACE_MONIKER_TO_RIGHT;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
}

/* In the FilterText object declaration, be sure to set TWLC_listObj to the
 * FilterList object, set FTI_filterMsg to the filter message, and
 * give a timeout FTI_filterTimeout between when the user enters text
 * and the filter message is send. */

@object FilterTextClass ListTestSearchBarTextEdit = {
    GTXI_maxLength = 30;
    GTXI_attrs = @default | GTA_SINGLE_LINE_TEXT;
    GTXI_destination = process;
    TWLC_listObj = @ListTestFilterList;
    FTI_filterMsg = MSG_LTSBG_SEARCH_COLORS;
    FTI_filterTimeout = 0;                   /* Use a small timeout because
                                                  * the search is so fast. */

    HINT_DEFAULT_FOCUS;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
}

Create a query message handler for the filter list. This handler is passed the application item identifier (position) of the item to supply a moniker for. Write it as you would write a query message handler for a GenDynamicList, but substitute for the following functions:

  • MSG_FILTER_LIST_REPLACE_ITEM_MONIKER instead of MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER
  • MSG_FILTER_LIST_REPLACE_ITEM_MONIKER_OPTR instead of MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR
  • MSG_FILTER_LIST_REPLACE_ITEM_TEXT instead of MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT

For more information on the query handler message, refer to DynamicList Basics.

Code Display: Creating a FilterList query message handler

/* Create the list of data in the form of text strings. */

@start ItemText, data;

@chunk char *listMonikers[NUM_COLORS] = {
    "red", "yellow", "blue", "green", "gray", "orange"
};

@end ItemText

/* Create the query message handler. Each time the FilterList needs to display
 * an item, it will call this querying message with the application item
 * identifier of the item it needs to display.
 * DEFINITION: void MSG_QUERY_COLOR_MONIKER(optr list, word item) */

@method ListTestProcessClass, MSG_QUERY_COLOR_MONIKER {
     char ** listMonikersDeref;

     MemLock(HandleOf(@listMonikers));
     listMonikersDeref= LMemDeref(@listMonikers);
     @call ListTestFilterList::MSG_FILTER_LIST_REPLACE_ITEM_TEXT(item, listMonikersDeref[item]);
     MemUnlock(HandleOf(@listMonikers));
}

The filter message takes no arguments and returns nothing. It is called when the user types something into the FilterTextClass object. A standard filter message retrieves the filter text (for example, with MSG_VIS_TEXT_GET_ALL_PTR), performs some search, and then sends MSG_FILTER_LIST_INITIALIZE to the FilterList, passing the number of items in the filtered list (a word) and a filter (an array of unique words).

Code Display: Creating a FilterText filter message handler

    #include <Ansi/string.h>    /* To use strcmp() in MSG_LTSBG_SEARCH_COLORS.
                                 * To access the ANSI library, be sure to include
                                 * the line "library ansic" in the .gp file.  */

/* Define a message that has no parameters and returns nothing. It will
 * be sent by the FilterText object to search the FilterList. */

    @message void MSG_LTSBG_SEARCH_COLORS();

/* Create the filter message handler. MSG_LTSBG_SEARCH_COLORS
 * filters out all color monikers from the list that do not
 * start with the text of text of ListTestSearchBarTextEdit.
 * DEFINITION: void MSG_LTSBG_SEARCH_COLORS(void) */

@method ListTestProcessClass, MSG_LTSBG_SEARCH_COLORS
{
    word filter[NUM_COLORS];      /* The filter is a word array */
    word filterListSize = 0;      /* Number of items in filtered list */
    char searchBuffer[30];        /* Stores ListTestSearchBarTextEdit text */
    word searchBufferLength;      /* Size of ListTestSearchBarTextEdit text */
    char ** listMonikersDeref;    /* Stores array of color monikers */
    word item = 0;                /* Counter for loop through list items */

        /* Put text of ListTestSearchBarTextEdit into searchBuffer */
    @call ListTestSearchBarTextEdit::MSG_VIS_TEXT_GET_ALL_PTR(searchBuffer);

    searchBufferLength = strlen(searchBuffer);

    /* If the search text is empty, then pass a NULL filter to the filter list */
    if(searchBufferLength == 0) {
        @call ListTestFilterList::MSG_FILTER_LIST_INITIALIZE(NUM_COLORS, NULL);
        return;
    }

    MemLock(HandleOf(@listMonikers));
    listMonikersDeref = LMemDeref(@listMonikers);

        /* Loop to check whether the filter should include each list item */
    for (item = 0; item < NUM_COLORS; item++)  {
        if (strncmp(searchBuffer, listMonikersDeref[item], searchBufferLength) == 0) {
                filter[filterListSize] = item;
                filterListSize++;
        }
    }
    MemUnlock(HandleOf(@listMonikers));

      @call ListTestFilterList::MSG_FILTER_LIST_INITIALIZE(filterListSize, filter);
}

Although filter lists and filter texts are usually used to search on a list and narrow the results, you can use them for many other tasks, such as re-ordering the display of a list or displaying an item multiple times in a list.

You can store more information in a filter than just an application item identifier. If you want the query to highlight the part of a moniker that matches the search text, you could store the item number in the lower byte of the word and the position of the match in the upper byte. If you use unused bits to encode extra information about the item, this information is significant to the list because the entire word is part of the item ID. When the application wants to pass an item ID to the list, for example with MSG_FILTER_LIST_SET_SINGLE_SELECTION, it must pass the entire word including whatever extra information you encoded into the identifier.

Note that a FilterList does not require a corresponding FilterText. An application could generate a filter rather than waiting to be prompted by a FilterText object. But in most situations you will use a FilterList object together with a FilterText object.

| Up: GEOS SDK TechDocs | Up | Prev: 8. FilterList, FilterText | Next: 8.2. FilterList Messages


FilterList Instance Data

Communicator UI: 8.2 FilterList, FilterText: FilterList Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 8.1 FilterList and FilterText Usage | Next: 8.3 FilterList Messages

A FilterList could have an empty list for two reasons: the unfiltered list might be empty, or the filter might not match any list items. FilterListClass instance data specifies the monikers to show when the list object is empty for either of these conditions. These monikers are optional. If they are not specified then the list will display nothing under the respective conditions.

Code Display: FilterListClass Instance Data

@instance @visMoniker FLI_noItemsMoniker;
@instance @visMoniker FLI_noMatchMoniker;
FLI_noItemsMoniker is the moniker to display when the list is empty and there is no filter.
FLI_noMatchMoniker is the moniker to display when the list is empty and there is a filter.
| Up: GEOS SDK TechDocs | Up | Prev: 8.1 FilterList and FilterText Usage | Next: 8.3 FilterList Messages


FilterList Messages

Communicator UI: 8.3 FilterList, FilterText: FilterList Messages

| Up: GEOS SDK TechDocs | Up | Prev: 8.2. FilterList Instance Data | Next: 8.4 FilterText Instance Data

A FilterListClass object behaves like a GenDynamicList (its parent class) except that the application can supply a filter (a word array) that the FilterListClass object uses to map physical item identifiers to application item identifiers.

Changing the Filter:

MSG_FILTER_LIST_INITIALIZE

 void   MSG_FILTER_LIST_INITIALIZE(
        word numItems,
        word *filter );

Use the message MSG_FILTER_LIST_INITIALIZE whenever you change the filter or the number of items in a FilterList. This message corresponds to the GenDynamicListClass message MSG_GEN_DYNAMIC_LIST_INITIALIZE, but should be used instead of MSG_GEN_DYNAMIC_LIST_INITIALIZE. This message notifies the dynamic list to display a new list of items. If the list is already usable, it invalidates and deselects all the current items and re-requests all the monikers. The list will not scroll back to the top explicitly -- to do so, send a MSG_FILTER_LIST_MAKE_ITEM_VISIBLE message to get to the top of the list.

Source: Unrestricted.

Destination: Any FilterList object.

Parameters:

numItems
The new number of displayed items.
filter
The new list filter. filter is an array of application item identifiers to display in the list. If filter is NULL, then physical IDs and application IDs are the same, and no mapping between them takes place. If the filter is not null and numItems is 0, then there are no matching items, and FLI_noMatchMoniker will be displayed.

Return: Nothing.

Other Messages:

FilterListClass defines several messages to deal with application item identifiers that are analogous to several GenItemGroupClass and GenDynamicListClass messages:

  • MSG_FILTER_LIST_SET_SINGLE_SELECTION
  • MSG_FILTER_LIST_SET_MULTIPLE_SELECTIONS
  • MSG_FILTER_LIST_GET_SELECTION
  • MSG_FILTER_LIST_GET_MULTIPLE_SELECTIONS
  • MSG_FILTER_LIST_GET_ITEM_OPTR
  • MSG_FILTER_LIST_SET_ITEM_STATE
  • MSG_FILTER_LIST_IS_ITEM_SELECTED
  • MSG_FILTER_LIST_MAKE_ITEM_VISIBLE
  • MSG_FILTER_LIST_REPLACE_ITEM_MONIKER
  • MSG_FILTER_LIST_REPLACE_ITEM_MONIKER_OPTR
  • MSG_FILTER_LIST_REPLACE_ITEM_TEXT
  • MSG_FILTER_LIST_GET_FOCUS_ITEM
  • MSG_FILTER_LIST_SET_FOCUS_ITEM

The application must use the FilterListClass version of the message instead of the GenItemGroupClass or GenDynamicListClass messages to query and set item states. The semantics and parameters of each message are the same as the GenItemGroup/GenDynamicList counterpart, except where noted.

In method descriptions the word "identifier" is taken to mean application item identifier.

MSG_FILTER_LIST_SET_SINGLE_SELECTION

void    MSG_FILTER_LIST_SET_SINGLE_SELECTION(
        word identifier,
        Boolean indeterminate );

Corresponds to the GenItemGroupClass method MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION. This method sets exactly one selection for a list.

Parameters:

identifier
The identifier of the item to select.

indeterminate
Pass TRUE if the group should be marked indeterminate.

Return: Nothing.

MSG_FILTER_LIST_SET_MULTIPLE_SELECTIONS

 void   MSG_FILTER_LIST_SET_MULTIPLE_SELECTIONS(
        word *selectionList,
        word numSelections );

Corresponds to the GenItemGroupClass method MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS. This message sets multiple selections for a list. The caller must pass a pointer to a list of identifiers that should be selected, along with the number of selections to set. .

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters:

selectionList
A pointer to the buffer containing a list of item identifiers.
numSelections
The number of selections in the passed list.

Return: Nothing.

MSG_FILTER_LIST_GET_SELECTION

 word   MSG_FILTER_LIST_GET_SELECTION(void);

Corresponds to the GenItemGroupClass method MSG_GEN_ITEM_GROUP_GET_SELECTION. This message returns the current selection identifier for the list object, or the first selection in the item group if there are multiple selections in a non-exclusive or extended-selection-exclusive item group.

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters: None.

Return: The current selection stored in the FilterListClass's GIGI_selection instance field (or GIGS_NONE if there are no selections).

MSG_FILTER_LIST_GET_MULTIPLE_SELECTIONS

 word   MSG_FILTER_LIST_GET_MULTIPLE_SELECTIONS(
       word *selectionList,
       word maxSelections );

Corresponds to the GenItemGroupClass method MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS. This message returns the current list of selections for non-exclusive and extended-selection lists. The caller must allocate a buffer for the entries and pass the size of that buffer. If there is insufficient space in the passed buffer, no entries will be filled in. You should call MSG_FILTER_LIST_GET_NUM_SELECTIONS beforehand in order to allocate the correct maximum size for the buffer.

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters:

selectionList
Buffer to hold the selections.
maxSelections
Maximum number of selections to place in the buffer.

Return: Number of selections. selectionList is filled in with the identifiers of the selections (the list will not be null-terminated).

MSG_FILTER_LIST_GET_ITEM_OPTR

 optr   MSG_FILTER_LIST_GET_ITEM_OPTR(
        word identifier );

Corresponds to the GenItemGroupClass method MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR. This message returns the optr of a GenItem within a FilterList.

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters:

identifier
The identifier of the item to search for.

Return: The optr of the GenItem with the requested identifier (or a null optr if none is found).

MSG_FILTER_LIST_SET_ITEM_STATE

 void   MSG_FILTER_LIST_SET_ITEM_STATE(
        word identifier,
        Boolean state );

Corresponds to the GenItemGroupClass message MSG_GEN_ITEM_GROUP_SET_ITEM_STATE. This message sets the state (selected or unselected) of a particular GenItem within a FilterList.

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters:

identifier
The identifier of the item to change state.
state
TRUE to mark item selected, FALSE to mark item unselected.

Return: Nothing.

MSG_FILTER_LIST_IS_ITEM_SELECTED

 Boolean   MSG_FILTER_LIST_IS_ITEM_SELECTED(
        word identifier );

Corresponds to the GenItemGroupClass message MSG_GEN_ITEM_GROUP_IS_ITEM_SELECTED. This message checks whether an item within a FilterList is selected, even if that item is not usable, not enabled, or not within the FilterList. The message returns TRUE if the item is selected, FALSE if it is not selected.

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters:

identifier
The identifier of the item being queried.

Return: TRUE (non-zero) if the item identifier is selected.

MSG_FILTER_LIST_MAKE_ITEM_VISIBLE

 void   MSG_FILTER_LIST_MAKE_ITEM_VISIBLE(
        word identifier );

Corresponds to the GenItemGroupClass method MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE. This message ensures that an item within a scrolling FilterList is visible, by scrolling the list if necessary.

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters:

identifier
The identifier of the item to be made visible.

Return: Nothing.

MSG_FILTER_LIST_REPLACE_ITEM_MONIKER

 void   MSG_FILTER_LIST_REPLACE_ITEM_MONIKER(
        word item,
        ReplaceItemMonikerFlags flags,
        word height,
        word width,
        word length,
        VisMonikerDataType dataType,
        VisMonikerSourceType sourceType,
        dword source );

Corresponds to the GenDynamicListClass message MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER. This message sends a moniker to use for a particular list item.

Source: The object handling a FilterList's query message..

Destination: The FilterList specified in the query message.

Parameters:

item
The position of the item needing a moniker.
flags
RIMF_NOT_ENABLED if the item's moniker should be shown disabled.
height
If the item is a GString, the height in points.
width
If the item is a GString, the width in points.
length
Size of moniker data, in bytes. This value is ignored for VMST_OPTR. If the dataType is VMDT_TEXT and length is zero, the text moniker is assumed to be null-terminated. If the dataType is VMDT_GSTRING and the length is zero, the GString is assumed to end with a GR_END_GSTRING element.
dataType
The VisMonikerDataType of the actual moniker.
sourceType
The type of pointer referencing the moniker.
source
A pointer to the moniker data.

Return: Nothing.

MSG_FILTER_LIST_REPLACE_ITEM_MONIKER_OPTR

 void   MSG_FILTER_LIST_REPLACE_ITEM_MONIKER_OPTR(
        word item,
        optr moniker );

Corresponds to the GenDynamicListClass message MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR. This message is a simplified version of MSG_FILTER_LIST_REPLACE_ITEM_MONIKER. The moniker must be of type VisMoniker and referenced by an optr.

Source: The object handling a dynamic list's query message.

Destination: The FilterList object specified by the query message.

Parameters:

item
The position of the item to display.
optr
The optr of the VisMoniker to use.

Return: Nothing.

MSG_FILTER_LIST_REPLACE_ITEM_TEXT

 void   MSG_FILTER_LIST_REPLACE_ITEM_TEXT(
        word item,
        char *text );

Corresponds to the GenDynamicListClass message MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT. This message is a simplified version of MSG_FILTER_LIST_REPLACE_ITEM_MONIKER.

Source: The object handling a dynamic list's query message.

Destination: The FilterList object specified by the query message.

Parameters:

item
Identifier of item to replace.
text
Far pointer to a null-terminated text string.

Return: Nothing.

MSG_FILTER_LIST_GET_FOCUS_ITEM

 word   MSG_FILTER_LIST_GET_FOCUS_ITEM(void);

Corresponds to the GenItemGroupClass message MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM. This message gets the focus item of a FilterList.

Source: Anywhere.

Destination: Any FilterList.

Parameters: None.

Return: The identifier of the item which currently has the focus, or GIGS_NONE if none.

MSG_FILTER_LIST_SET_FOCUS_ITEM

 void   MSG_FILTER_LIST_SET_FOCUS_ITEM(
        word identifier );

Corresponds to the GenItemGroupClass message MSG_GEN_ITEM_GROUP_SET_FOCUS_ITEM. This message scrolls the FilterList list so that the specified item will be the visible focus.

Source: Anywhere.

Destination: Any FilterList.

Parameters:

identifier
The ID of the item which will be the focus of the item group.

Return: Nothing.

MSG_FILTER_LIST_MAP_PHYSICAL_TO_APP

word    MSG_FILTER_LIST_MAP_PHYSICAL_TO_APP(
        word    id);

Use MSG_FILTER_LIST_MAP_PHYSICAL_TO_APP to get the application ID that corresponds to a physical item location id on a FilterList from the filter.

Source: While this message can be called at any time, it is normally not necessary because the other FilterList methods automatically handle this mapping task.

Parameters:

id
The physical identifier of a list item.

Return: Returns the application item ID that corresponds to the physical item ID, or GIGS_NONE if the current filter doesn't contain the specified item.

| Up: GEOS SDK TechDocs | Up | Prev: 8.2. FilterList Instance Data | Next: 8.4 FilterText Instance Data


FilterText Instance Data

Communicator UI: 8.4 FilterList, FilterText: FilterText Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 8.3. FilterList Messages | Next: 8.5. FilterText Messages

Code Display: FilterTextClass Instance Data

@default GTXI_attrs = (@default | GTA_SINGLE_LINE_TEXT);

@instance word FTI_filterTimeout = 30;
@instance Message FTI_filterMsg;

FilterTextClass inheirits from TextWithListClass. When the user modifies the text in the text box, the user-defined message FTI_filterMsg is sent to the FilterTextClass's GIGI_destination. This message takes no arguments and returns nothing. This message's handler should use the text in the FilterText object to filter a FilterList object.

The user can specify a timeout delay FTI_filterTimeout (in ticks) between the time the text is modified and the time the message is sent. If your filter message will take a long time to process, then give a longer timeout to avoid long, pausing searches before the user is done typing in the full filter text. On the other hand, if your filter message is very fast then give a small timeout to make the UI very responsive. The default delay is half a second (30 ticks).

| Up: GEOS SDK TechDocs | Up | Prev: 8.3. FilterList Messages | Next: 8.5. FilterText Messages


FilterText Messages

Communicator UI: 8.5 FilterList, FilterText: FilterText Messages

| Up: GEOS SDK TechDocs | Up | Prev: 8.4. FilterText Instance Data

FilterTextClass behaves like its parent class TextWithListClass, but sends out the message FTI_filterMsg to filter a FilterList whenever text is entered into it.

MSG_FILTER_TEXT_SEND_FILTER_MSG

 void   MSG_FILTER_TEXT_SEND_FILTER_MSG(void);

This message causes the filter timeout to be reset and the FTI_filterMsg message to be sent. It is sent internally by the FilterText's timeout or by the application to cause an immediate filter.

Source: Unrestricted.

Destination: Any FilterTextClass object.

Parameters: None.

Return: Nothing.

MSG_FILTER_TEXT_RESET

 void   MSG_FILTER_TEXT_RESET(void);

Clears the text currently in the FilterText box and immediately sends out FTI_filterMsg, causing the destination to clear the search. This is the equivalent of sending MSG_VIS_TEXT_DELETE_ALL followed by MSG_FILTER_TEXT_SEND_FILTER_MSG.

Source: Unrestricted.

Destination: Any FilterTextClass object.

Parameters: None.

Return: Nothing.

MSG_FILTER_TEXT_RESET_TEXT_IF_NOT_FOCUSED

 void   MSG_FILTER_TEXT_RESET_TEXT_IF_NOT_FOCUSED(void);

This message erases the text in the FilterText box if the focus is lost.

Source: Unrestricted.

Destination: Any FilterTextClass object.

Parameters: None.

Return: Nothing.

| Up: GEOS SDK TechDocs | Up | Prev: 8.4. FilterText Instance Data


Versions

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Software Version Number
| One-page Version of this Chapter for Easy Printing

This chapter discusses various new API features that have been added in recent versions of the Nokia 9000i and Nokia 9110 Communicators.

1 Software Version Number
2 User's Name
3 IMEI Code Number
4 New SMS Header
5 New Standard Monikers, Bitmaps
6 Non-exclusive Lists
7 Filter Lists/Filter Text
8 Incremental Searches
9 Contact's URL and Password
10 ContactList Searches
11 Unknown Phone Numbers
12 Detecting Hardware Type
13 Menu Trigger
14 Zoom Level
15 Memory Cards

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Software Version Number
| One-page Version of this Chapter for Easy Printing


Software Version Number

Versions: 1 Software Version Number

| Up: GEOS SDK TechDocs | Up | Prev: Versions | Next: 2 User's Name

The INI file contains the version number. The [ui] category's productName field will be one of the following strings:

  • Responder Build 3.8.8 (original release, does not support features described in this document)
  • Responder Build 4.3.7 (update)
  • Responder Build 4.8.? (later update)

There is also a software serial number, in the swSerialNumber field. This field has the format: 000000/00/AAABBCCDDLLLFKK/0

AAA : hardware version
1st digit: hardware major protocol (2)
2nd digit: hardware minor protocol (a)
3rd digit: hardware variant number (0)
This is not the actual hardware version, but rather
which version the software was compiled for. All
released images are for 2.10.0 ("2a0" hex).
BB :
build major number (03,04)
CC :
build minor number (08,03)
DD : build variant number (08,07)
Same as for productName, but padded with extra zeros.
LLL :
localization country code (044 = English, 033 = French, 049 = German)
F :
0=all current releases 1=certain future releases
KK :
keyboard mapping version (01 = English, 02 = French, 03 = German, 04 = Scandinavian)

Use one of the the IniFileReadString...() routines to find out the value of these INI file fields.

Code Display 13-1 Getting the Software Version Number

@start StringRes;
  @chunk TCHAR UICategory[] = "ui";
  @chunk TCHAR SWSerialNumber[] = "swSerialNumber";
@end StringRes;
...
@method TestProcessClass, MSG_TEST_PROCESS_GET_VERSION
{
    TCHAR version[28] = "";
    TCHAR *category;
    TCHAR *key;
    word  value;
    MemLock( OptrToHandle( @UICategory ) );
    category = (TCHAR *) LMemDeref( @UICategory );
    key = (TCHAR *) LMemDeref( @SWSerialNumber );
    /*
     * Use one of the InitFileReadString...() routines.
     * Extract the information you want from version string.
     */
    MemUnlock( OptrToHandle( @UICategory ) );
}

Further complicating matters, the swSerialNumber does not follow the format described above in the PC Emulator. On the PC emulator, the version string will look something like "Build X.X", where the Xs are numbers. These correspond to the build's major number.

| Up: GEOS SDK TechDocs | Up | Prev: Versions | Next: 2 User's Name


User's Name

Versions: 2 User's Name

| Up: GEOS SDK TechDocs | Up | Prev: 1 Software Version Number | Next: 3 IMEI Code Number

The ContDB library has a new function, ContactGetUserName() , which returns the user's name.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Software Version Number | Next: 3 IMEI Code Number


IMEI Code Number

Versions: 3 IMEI Code Number

| Up: GEOS SDK TechDocs | Up | Prev: 2 User's Name | Next: 4 New SMS Header

To read the phone's IMEI number, you will need to use the EciImeiGet() function, provided by the eci library. This function takes one parameter, a pointer to a buffer to fill with the code number string. The buffer must be at least 17 bytes long, to hold the null-terminated 16-character code string. The function returns zero if the IMEI code was read succesfully, non-zero otherwise.

Code Display 13-2 Reading the IMEI Code

@include <eci.goh>
...
@method TestProcessClass, MSG_TEST_PROCESS_GET_IMEI
{
    TCHAR imei[17] = "";
    if ( EciGetImei( imei) == 0)
    {
      /*
       * We got IMEI code, do any check you want.
       */
    }
    else
    {
      /*
       * For some reason query failed.
       */
    }
}
| Up: GEOS SDK TechDocs | Up | Prev: 2 User's Name | Next: 4 New SMS Header


New SMS Header

Versions: 4 New SMS Header

| Up: GEOS SDK TechDocs | Up | Prev: 3 IMEI Code Number | Next: 5 New Standard Monikers, Bitmaps

In addition to the old way to send messages to application using their GeodeToken in front of the actual data, a new user data header is now introduced. Otherwise sending and receiving messages has not changed. The new header adopts the concept from Narrow Band Socket specification.

The new header format is //SCKddoo or //SCKLddddoooo where dd (and dddd) represent "destination port" and oo (and oooo) represent "originator port". Both these headers are terminated with space character.These ports are ascii coded hexadecimal numbers.

Short header covers ports from 0 to 255 (00 to FF in hex) and the longer form from 0 to 65535 (0000 to FFFF in hex).

The range of port numbers is divided to reserved port address range and to dynamic and/or private port address range. Usage of port address range 0 to 49151 is restricted and assignment of a port in this range requires contacting port address assignment authority. The reserved port address range is further divided to ports for "well-known protocols" (range 0 to 239 and 256 to 1024) and to registered ports (range 1025 to 49151). Nokia has reserved ports 5500 to 5599 for company-specific protocols. Port addresses in the dynamic address range 240 to 255 or in the dynamic and/or private address range 49152 to 65535 may be used freely by any vendor.

In order to receive these messages application must register themselves to SMS receive application. This can be done using smreg library. This library offers functions for registering and unregistering.

SMRegRegisterApplication() takes there parameters: GeodeToken of your application, a pointer to buffer containing "port numbers" you register for and number of messages you are registering. Port numbers must be ASCII coded hexadecimal numbers.

Code Display 13-3 Registering to receive "destination port" SMS Messages

@method TestProcessClass,  MSG_TEST_PROCESS_REGISTER
{
    GeodeToken appl;
    TCHAR messageProtocol[5];
    word protocol;
/* We register ourselves to listen port 43962 that is ABBA in hex.  */
    protocol = 43962;
    appl.GT_chars[0] = `T';
    appl.GT_chars[1] = `E';
    appl.GT_chars[2] = `S';
    appl.GT_chars[3] = `T';
    appl.GT_manufID = 11;
/* Let's ask smreg library to convert 43962 to ascii coded hex for us. */
    SMRegIntegerToHex( protocol, messageProtocol,
                         SMRITH_NULL_TERMINATE |SMRITH_WORD_LENGTH);
/* Now we have messageProtocol = "ABBA" */
    SMRegRegisterApplication( appl, messageProtocol, 1);
}

You can register more than one port by calling the function:

SMRegRegisterApplication( appl, "ABBADADA0055", 3);

Then you will receive messages ABBA, DADA and 55.

The function returns a SMRegResult value: SMRR_OK if your registration was succesful; otherwise it will be SMRR_FAIL.

SMRegIntegerToHex() function converts passed decimal integers to ascii coded hexadecimal strings. You pass a word containing integer to be converted, a character pointer where the result is stored and flags how the conversion is done. You can convert either byte length integers i.e. 0 to 255 and the result is two characters to word length i.e. 0 to 65535 and the result is four characters. By default the destination string is not null terminated, but if you pass flag SMRITH_NULL_TERMINATE null is added at the end of the string.

If you want to stop receiving messages you can unregister yourself. This can be done using SMRegUnregisterApplication() function. The parameters are identical to SMRegRegisterApplication. In addition SMMR_OK and SMRR_FAIL, this unregister function may return SMRR_APPLICATION_NOT_REGISTERED to indicate that you were not registered to listen this message.

The message routing table is stored to .INI file. You may freely modify it yourself, but it is not recommended. More than one application may want to listen same messages, so writing .INI file direct may cause problems to other applications. The category is SMSRouteTable and port numbers represent keys underneath.

Code Display 13-4 INI file: SMSRouteTable example

Here TEST application wants to receive messages 00F0 and ABBA and TES2 wants to receive message ABBA.

[SMSRouteTable]
00F0 = "TEST",11
ABBA = { "TEST",11
          "TES2",11 }
| Up: GEOS SDK TechDocs | Up | Prev: 3 IMEI Code Number | Next: 5 New Standard Monikers, Bitmaps


ComplexMoniker additions: Menu trigger, Monikers

Versions: 5 New Standard Monikers, Bitmaps

| Up: GEOS SDK TechDocs | Up | Prev: 4 New SMS Header | Next: 6 Non-exclusive Lists

There are new ComplexMonikerType values and a new ComplexMonikerBitmap:

  • CMT_MENU Useful for "Menu" buttons.
  • CMT_SEARCH
  • CMB_SEARCH
| Up: GEOS SDK TechDocs | Up | Prev: 4 New SMS Header | Next: 6 Non-exclusive Lists


Non-exclusive Lists

Versions: 6 Non-exclusive Lists

| Up: GEOS SDK TechDocs | Up | Prev: 5 New Standard Monikers, Bitmaps | Next: 7 FilterList/FilterText

GenItemGroups may now be of type GIGBT_NON_EXCLUSIVE, allowing the user to select more than one item at a time (available in software versions 4.8.8 or later). For a complete discussion of non-exclusive lists, refer to GenItemGroup basics.

GenItemGroupClass now supports two messages which allow you to get and set which item has the "focus"--which single item the user currently has highlighted, as opposed to the selected items:

  • MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM
  • MSG_GEN_ITEM_GROUP_SET_FOCUS_ITEM

Code Display 13-5 Using MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM

@chunk TCHAR NoteSanFrancisoFocus[] = "San Franciso has the city list focus.";
@method TestProcessClass, MSG_TEST_PROCESS_GET_CITY_GROUP_FOCUS
{
    word city_selected;
    city_selected = @call CityGroup::MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM();
    if (city_selected == CG_san_francisco)
    {
        FoamDisplayNote(@NoteSanFrancisoFocus);
    }
}

ContactLists:

If you are using a ContactList (from the Contact DB library), you may set it up for multiple selections by giving it a ATTR_CONTACT_LIST_NON_EXCLUSIVE_LIST variable data field. To find out which records are selected in such a list, use MSG_CONTACT_LIST_GET_IDS_OF_SELECTED_RECORDS.

| Up: GEOS SDK TechDocs | Up | Prev: 5 New Standard Monikers, Bitmaps | Next: 7 FilterList/FilterText


Filter List/Filter Text

Versions: 7 Filter List/Filter Text

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Non-exclusive Lists | Next: 8 Incremental Searches

Responder Versions 4.8.8 and later support Filter List and Filter Text objects which together provide the UI for interactive searches on lists of objects. The Filer List object is a modified GenDynamicList and the filter text object is a modified TextWithList. When the user types a string into a FilterTextClass object, the FilterTextClass object calls a method that uses this text to create a filter for the FilterListClass object. This filter controls which monikers the FilterList displays, and in what order.

For example, the Composer application has a FilterListClass object that displays a long list of songs, and a FilterTextClass "Search" field. When the user types a string in the "Search" field, the FilterList displays only items that start with that string.

To create FilterList/FilterText pair:

  • Create a FilterListClass object.
  • Create a query message handler for the filter list. The Filter List calls this message to retrieve every item on the list.
  • Create a FilterTextClass object.
  • Create a Filter Text message handler that uses the text in the Filter Text object to create a filter. This filter determines which items in the Filter List will be displayed.

For more details, refer to the Filter List, Filter Text section of the Communicator UI chapter.

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Non-exclusive Lists | Next: 8 Incremental Searches


Incremental Searches

Versions: 8 Incremental Searches

| Up: GEOS SDK TechDocs | Up | Prev: 7 FilterList/FilterText | Next: 9 Contact's URL and Password

To allow the user to narrow the choices shown in a list when they type into a text entry, use two new classes in the Foam library: FilterListClass and FilterTextClass. Their API may be found in the include file foam.goh.

These classes are only available after Build 4. Check the " F " field of the version number, as described above.

| Up: GEOS SDK TechDocs | Up | Prev: 7 FilterList/FilterText | Next: 9 Contact's URL and Password


Contact's URL and Password

Versions: 9 Contact's URL and Password

| Up: GEOS SDK TechDocs | Up | Prev: 8 Incremental Searches | Next: 10 ContactList Searches

The Contact DB Library has four new ContactFieldType values: CFT_TEL_FAX, CFT_TEL_DATA, CFT_URL and CFT_PASSWORD.

These values are only available after Build 4. Check the " F " field of the version number, as described above.

| Up: GEOS SDK TechDocs | Up | Prev: 8 Incremental Searches | Next: 10 ContactList Searches


ContactList Searches

Versions: 10 ContactList Searches

| Up: GEOS SDK TechDocs | Up | Prev: 9 Contact's URL and Password | Next: 11 Unknown Phone Numbers

Normally, a ContactList will lose track of the user's current search if the focus moves to another object. To force the list to remember the current search, send it a MSG_CONTACT_LIST_PRESERVE_SEARCH_ON_NEXT_LOST_FOCUS before it loses the focus.

This message is only available after Build 4. Check the " F " field of the version number, as described above.

| Up: GEOS SDK TechDocs | Up | Prev: 9 Contact's URL and Password | Next: 11 Unknown Phone Numbers


Unknown Phone Numbers

Versions: 11 Unknown Phone Numbers

| Up: GEOS SDK TechDocs | Up | Prev: 10 ContactList Searches | Next: 12 Detecting Hardware Type

If the user enters a valid phone number in the search box of aContactList, but that number is not found, an application mayrequest that the ContactList send out a notification using a new variable data field.

Give the ContactList a ATTR_CONTACT_LIST_NOTIFY_NUMBER_ENTERED_MSG variable data field. This field's data is a message.

The message should have parameters:

  void MSG_MY_CL_NOTIFY( MemHandle phNumber = cx);

The passed block contains the phone number string. The message's recipient is responsible for freeing the passed block.

When using the Contact Log library to log a contact, if you don't know the phone number, you can pass LEF_WILDCARD_NUMBER to LogAddEntry() to specify that this call's number should match any phone number.

| Up: GEOS SDK TechDocs | Up | Prev: 10 ContactList Searches | Next: 12 Detecting Hardware Type


Detecting Hardware Type

Versions: 12 Detecting Hardware Type

| Up: GEOS SDK TechDocs | Up | Down | Prev: 11 Unknown Phone Numbers | Next: 13 Menu Trigger

The Responder Library has a new routine RespGetPDAHardware() that lets an application determine the type of hardware it is running on, specifically the type of PDA and whether or not it has a backlight. This routine returns a PDAHardwareInfo byte flag. The bit PDAHI_BACKLIGHT_AVAILABLE (0x04) is zero if there is no backlight, or one if there is. The bits PDAHI_PDA_TYPE (0x02 | 0x01) are set to the type of PDA. These enumerated values may be PDAT_PC_EMULATOR (0x0), PDAT_N9000 (0x1), or PDAT_N9000i (0x2), corresponding to, respectively, the PC emulator, the Nokia 9000 released in 1996, and the Nokia 9000 released in the second half of 1997.

| Up: GEOS SDK TechDocs | Up | Down | Prev: 11 Unknown Phone Numbers | Next: 13 Menu Trigger


Menu Trigger

Versions: 13 Menu Trigger

| Up: GEOS SDK TechDocs | Up | Prev: 12 Detecting Hardware Type | Next: 14 Zoom Level

Software versions 4.8 or later of the Communicator allow for a fifth "Menu" trigger that behaves just like a regular trigger except that it is invisible and does not take up a slot at the right side of the screen. It is activated whenever the user pushes the "Menu" button present on newer versions of the Communicator.

A "Menu" command trigger is defined just like any other command trigger, except it lacks a HINT_SEEK_SLOT and you must set CMI_topText = CMT_MENU;. The "Menu" button on the Communicator maps to Ctrl-F2 on the emulator.

For more information, refer to the Communicator Fact Sheet and ComplexMoniker Triggers.

| Up: GEOS SDK TechDocs | Up | Prev: 12 Detecting Hardware Type | Next: 14 Zoom Level


Zoom Level

Versions: 14 Zoom Level

| Up: GEOS SDK TechDocs | Up | Prev: 13 Menu Trigger | Next: 15 Memory Cards

Versions 4.8.8 or later of the 9000i Communicator allow the user to press an off-screen "Zoom" button to toggle between three text size settings -- "Zoom levels" -- and adjust the text size for more comfortable reading.

If you want a UI component to increase font size when the user increases the zoom level, then pass it HINT_MAXIMUM_ZOOM_LEVEL = ZL_ZOOM_3; (or ...ZL_ZOOM_2; if you want it be limited to medium-size).

The higher the zoom level, the larger the font size, and the fewer the number of characters that will fit into a fixed pixel length field. Thus, if you use a CreateVisMoniker...() routine, you will need to know how to size the VisMoniker appropriately for the current zoom level. For more details, refer to the Zoom Level UI chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 13 Menu Trigger | Next: 15 Memory Cards


Memory Cards

Versions: 15 Memory Cards

| Up: GEOS SDK TechDocs | Up | Prev: 14 Zoom Level

The Nokia 9110 Communicator has a slot for an optional memory card. The card is about the size of a postage stamp and provides up to 4 MB of extra Flash memory or ROM.

New API lets you read and write to a memory card. For more details, refer to the Memory Cards chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 14 Zoom Level


Viewer-Compatible Documents

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Document Browser Application
| One-page Version of this Chapter for Easy Printing

The user of a Nokia 9000i Communicator can create various kinds of documents, including mail, SMS messages, and notes. Most of these documents can be shared between applications. The SMS application can open short Mail documents, the Fax program can send text from Notes, the Notes application can read and edit many kinds of documents.

The Viewer library makes this document-sharing possible. An application can instantiate an object of FileOpenControlClass, a class defined by the Viewer library, which allows the user to select a document. The Viewer library will then start up another library which will allow the user to view and edit the document--a VILFile library exists for each document type. If you are defining a new kind of document to be compatible with the Viewer library, you will need to write a corresponding VILFile library.

Before embarking on this task, consider if it's appropriate. Do you really need to make your documents Viewer Library-compatible? Consider the "Tones" files created and edited by means of the Composer application. These are documents, but it seems unlikely that the user would want to view or edit these files from other applications--users probably don't want to send their Tones via Fax. Instead of creating a VILFile library to work with these documents, the programmers wrote a specialized application to work with the documents; this task was more straightforward than writing a VILFile library.

To define a new type of Viewer-library compatible document, you will need to do the following things:

Choose a four-character token.
When the Viewer library determines which VILFile library to use to view a document, it identifies the document's type by means of a four-character token. In later examples we will use the token 'TABV'.
Create a document browser application with a FileOpenControl.
Write an application that lets the user create documents of your type. To allow this application to work with the Viewer library, add a FileOpenControl object to the application, and configure the controller appropriately.
Associate the token with your VILFile Library.
The Viewer library uses a table in the device's INI file to figure out which VILFile library to use when viewing a document. Before the user can view one of your files, you set up the proper entry in the INI file. Assuming that you're writing a document browser application that creates these documents, this application could add the appropriate entry to the INI file.
Create a VILFile Library.
Set up the framework of your VILFile Library. There should be a UI resource containing a ViewerDialog and four entry point routines.
Decide on your document's file format.
Document data is stored in VM files. When the user creates or opens a document, the Viewer library will tell your VILFile library the file's directory and name. It's then up to your code to store data in and retrieve data from the file.
Decide on your UI's state block format.
If the device is turned off or is running low on memory, your VILFile library will have to store its state in a heap block; later on, it will have to restore its state from the data in that block. Your code should be able to store the name and location of the current document, along with the current state of UI gadgetry.
Customize the UI that displays and edits the document.
A ViewerDialog object is a a dialog which provides some standard triggers and behavior. To display the document's data, create a ViewerDialog and then place other objects within it.
Write the four entry-point routines for the library.
The Viewer library will call routines of your VILFile library when it's time to open a document, close a document, save to state, or switch between documents. You will need to write code for these four routines.
Install your geodes in the proper place.
Your application(s) probably belong in the device's WORLD\EXTRAPPS directory. Your VILFile library goes in the SYSTEM directory.

The rest of the chapter discusses how to carry out these tasks.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Browser Application
| One-page Version of this Chapter for Easy Printing


Document Browser Application

Viewer-Compatible Documents: 1 Document Browser Application

| Up: GEOS SDK TechDocs | Up | Prev: Viewer-compatible documents | Next: 2 VILFile Library

You'll probably want to write some sort of document browser application to handle two tasks involved with supporting a viewer-compatible document type. (It's possible to carry out these tasks in other ways; in most cases, it's simplest to write an application.)

  • Associate the VILFile library with the document type token
  • Allow the user to create documents of the document type

Associating the VILFile Library with the Document Type Token

The system will recognize your document's type by its four-byte token. As soon as possible, you will need to tell the system which VILFile library to use to work with files of your type. If your software package includes some setup or install application, it can set up the association by editing the device's GEOS.INI file. If your package contains no such set-up application, but does contain some other application, make that application edit the INI file; warn users that the system will only recognize the document type after they've run the application.

The following code snippet shows how a GEOS program could edit the GEOS.INI to associate the token "TABV" with the tablev library. A program using this snippet would write a line to the vilFileTable section of the INI file reading "TABV = tablev.geo" ; the system will look for this line.

#include <initfile.h> /* header for InitFileWriteString() */

#define TV_FILE_TOKEN     "TABV"

@method MyProcessClass, MSG_GEN_PROCESS_OPEN_APPLICATION
{
  @callsuper();
  NEC( InitFileWriteString("vilFileTable", TV_FILE_TOKEN, "tablev.geo"); )
  EC(  InitFileWriteString("vilFileTable", TV_FILE_TOKEN, "tablevec.geo"); )
} /* MSG_GEN_PROCESS_OPEN_APPLICATION */

The application's process object will receive MSG_GEN_PROCESS_OPEN_APPLICATION soon after the application is run.

When we're testing on the error-checking version of the system, we'll use the error-checking version of the tablev library. The filename of the library differs between the normal and error-checking versions; we use the EC() and NEC() conditional compilation macros to make sure that we write out the correct file name.

Allowing the User To Create Documents of Your Type

The common way to allow the user to create Viewer-compatible documents of a given type is by means of a subclassed FileOpenControl object. Let's take a look at how one might configure such an object, then see how to integrate it into an application's UI.

[FileOpenControl screen shot]

The following code snippet shows a fairly feature-rich instance of a FileOpenControl object set up to create documents of the "TABV" type.

@include <viewer.goh> /* header for the Viewer library */

#define TV_FILE_TOKEN     "TABV"
#define TV_MAJOR_PROTOCOL 0
#define TV_MINOR_PROTOCOL 0

@class MyFileOpenControlClass, FileOpenControlClass;
@endc;

@classdecl MyFileOpenControlClass;

@object MyFileOpenControlClass MyFileOpen = {
  CMI_topText = @FCMoniker;
  ATTR_GEN_CONTROL_REQUIRE_UI = { FOF_MOVE | FOF_CREATE_FOLDER |
                                  FOF_COPY | FOF_OPEN |
                                  FOF_DELETE | FOF_RENAME_COPY |
                                  FOF_RENAME | FOF_CLOSE };

  ATTR_FILE_OPEN_CREATE_TRIGGER_MONIKER = @CreateTriggerMoniker;
  HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
  ATTR_FILE_OPEN_DEFAULT_FILE_NAME_PREFIX = @VFFPrefix;
  ATTR_FILE_OPEN_FILE_ATTR_INFO = {
    TV_FILE_TOKEN, 26, TV_MAJOR_PROTOCOL, TV_MINOR_PROTOCOL
  };
}
@chunk TCHAR FCMoniker[] = "CalcuWriter";
@chunk TCHAR CreateTriggerMonikerText[] ="New\rCalc";
@chunk FileOpenCreateNewMoniker CreateTriggerMoniker = {
  @CreateTriggerMonikerText,
  0
} ;

@method MyFileOpenControlClass, MSG_FILE_OPEN_CONTROL_GET_VIEWER_LIBRARY_NAME {
    GeodeToken           theToken;
    ViewerInterfaceError error;

    if ( error = ViewerGetFileToken( docInfo, &theToken ) ) {
      return( error );
    }

    if ( theToken.GT_chars[0] ) { /* check for null token */
      return( @callsuper() );
    }
    else {
      return( VIE_NO_VIEWER_LIBRARY );
    }

}

The sizing hint HINT_EXPAND_HEIGHT_TO_FIT_PARENT is fairly important. Without any sizing hint, the object is likely to change size over its lifetime.

The ATTR_FILE_OPEN_CREATE_TRIGGER_MONIKER variable data field allows you to specify a custom moniker for the control's "Create" trigger, if one exists. As shown in this example, this field should hold the chunk handle of a FileOpenCreateNewMoniker structure in the same resource as the FileOpenControl.

ATTR_FILE_OPEN_DEFAULT_FILE_NAME_PREFIX specifies a default file name for documents created with this controller (if it supports the feature of creating documents). This field should hold the chunk handle of a string in the same resource as the FileOpenControl.

If the FileOpenControl is to create new documents, specify the document type of those documents with ATTR_FILE_OPEN_FILE_ATTR_INFO. This field should contain a GeodeToken structure holding the document type's token, your ManufacturerID, and a protocol number. The ManufacturerID and token identify the type; the protocol number allows you to specify different versions of the type.

The ATTR_GEN_CONTROL_REQUIRE_UI (not used in this example) variable data field allow you to turn on some of the FileOpenControl's features. (Use ATTR_GEN_CONTROL_PROHIBIT_UI, not shown in this example, to turn off some of these features.)

FOF_RENAME
Allows the user to rename a file.
FOF_COPY
Allows the user to copy a file to a different directory.
FOF_RENAME_COPY
Allows the user to copy a file to another directory, specifying a new name for the copy.
FOF_DELETE
Allows the user to delete a file.
FOF_CREATE_NEW
Allows the user to create a new file. You will need to make sure that the FileOpenControl knows what kind of document to create, using the ATTR_FILE_OPEN_FILE_ATTR_INFO field.
FOF_OPEN
Allows the user to open an existing file.
FOF_CLOSE
Allows the user to close a subdirectory.
FOF_CREATE_FOLDER
Allows the user to create a subdirectory.
FOF_MOVE
Allows the user to move a file to a different directory.
FOF_COPY_TO_SOUNDS
Allows the user to copy the file to the Tones directory. A FileOpenControl does not normally display this directory.

The method for MSG_FILE_OPEN_CONTROL_GET_VIEWER_LIBRARY_NAME is a work-around for a bug. Without this work-around, the system will crash if the user uses the FileOpenControl to open a file that has a null token.

Fitting the FileOpenControl into your Application

In addition to configuring the application's FileOpenControl, you will need to do some things to allow the FileOpenControl to coexist with the rest of your application's objects.

  • Make sure the FileOpenControl object is on the MGCNLT_ACTIVE_LIST and GAGCNLT_VIEWER_NOTIFICATIONS notification lists. The FileOpenControl, like most controllers needs to be on these lists to get certain system notifications.
  • Add FileOpenControl to the application's UI tree. Typically, the FileOpenControl is the child of the application's primary window.
  • Fix overloaded button slots. Usually, a FileOpenControl will show additional triggers when it's displaying a subdirectory. For instance, it brings up a "Close" trigger.

    • An application might have some triggers in the same window that contains the FileOpenControl. For instance, there might be a "Close" trigger to exit the application. This can lead to overlaps when the FileOpenControl tries to put its own triggers in the occupied slots. To work around this, intercept MSG_FILE_OPEN_CONTROL_SUB_DIR_OPEN and MSG_FILE_OPEN_CONTROL_SUB_DIR_CLOSE and set the extra triggers not-usable or usable.

[FileOpenControl screen shot]

Figure: The "Close" trigger is covering another trigger in the same slot.

The following code snippets show how an application might carry out these tasks.

/* Put the FileOpenControl on the necessary notification lists */
@object FoamSubApplicationClass VFApp = {
  ...
  gcnList( MANUFACTURER_ID_GEOWORKS, MGCNLT_ACTIVE_LIST ) =
        @MyFileOpen;
  gcnList( MANUFACTURER_ID_GEOWORKS, GAGCNLT_VIEWER_NOTIFICATIONS ) =
    @MyFileOpen;
}

/* Make the controller a child of the primary window. */
@object GenPrimaryClass VFPrimary = {
  GI_comp = ... , @MyFileOpen ;
}

/* Disable and re-enable triggers whose slots overlap
   the FileOpenControl's when it views a subdirectory. */
@method MyFileOpenControlClass, MSG_FILE_OPEN_CONTROL_SUB_DIR_OPEN {
  @send VFCloseTrigger::MSG_GEN_SET_NOT_USABLE( VUM_DELAYED_VIA_APP_QUEUE );
}

@method MyFileOpenControlClass, MSG_FILE_OPEN_CONTROL_SUB_DIR_CLOSE {
  @send VFCloseTrigger::MSG_GEN_SET_USABLE( VUM_DELAYED_VIA_APP_QUEUE );
}
| Up: GEOS SDK TechDocs | Up | Prev: Viewer-compatible documents | Next: 2 VILFile Library


VILFile Library

Viewer-Compatible Documents: 2 VILFile Library

| Up: GEOS SDK TechDocs | Up | Prev: 1 Document Browser Application

Each viewer-compatible document type needs a VILFile library responsible for reading, writing, and displaying documents of that type. Writing such a library requires some UI work, VM file operations, and an understanding of the actions which the Viewer library requests of VILFile libraries.

Each VILFile library must have at least four entry points (exported routines); the Viewer library will call these routines when it needs the VILFile library to carry out certain tasks:

  1. Open:
    Purpose: The Viewer library needs the VILFile document to open a document.
    The user may have just now selected the document;
    the user may have just now created the document;
    the user may have opened the document a while ago, the application may have been shut down to state (and the document closed), and the application may now be opening again, so that the document must be re-opened.

    2. Action: Typically, the Open routine displays UI and caches information that will be needed later, including the document file's path. If the document is new, the routine is responsible for initializing the document.

  2. Detach (Quit to State):
    Purpose: The device must close the document though the user hasn't explicitly closed it. Perhaps the device is running low on memory and must shut down the viewing application, or perhaps the device is powering down.

    3. Action: The routine is responsible for collecting all state information and encoding it into a block of memory which it returns to the Viewer library. If the document file is open, this routine should close it. This routine is normally responsible destroy the UI which displays the document.

  3. Change Document (Switch Documents):
    Purpose: The user is jumping from document to document. The Viewer library needs the VILFile library to close the current document and open another.

    4. Action: The routine should close the current document and attempt to open the new document. This document's location is passed as an argument. It may update the displayed UI. It will probably need to update cached information useful for other tasks.

  4. Close Document:
    Purpose: The user is done with the current document. The Viewer library needs to close the document.

    5. Action: The routine is responsible for closing the current document. Typically, it will also destroy the UI used to display the document.

Below, we'll see source code for a simple VILFile library which carries out these duties.

GP File

Before discussing the VILFile library's procedural code, it's worthwhile to note directives needed in the library's GP file.

  • You will need to link the viewer library.
  • The first four exported routines correspond to the four routines which each VILFile library must support.
  • Typically, your library will create UI by making a copy of a template resource. This template resource should be marked read-only and shared in the GP file. We'll discuss this UI template in greater detail below.
  • Your library will almost certainly have to declare a subclass of ViewerDialogClass; export this class.

Code Display: Typical .GP file for a VILFile library

library viewer
library ansic    # Not all VILFile libraries will use ansic, but
                 # our example code does.  (It uses strncpy())

resource THEVIEWERUI           object read-only shared

export TVOPEN
export TVDETACH
export TVCHANGEDOCUMENT
export TVCLOSE

export MyViewerDialogClass

Declaring UI

The typical VILFile library will use UI gadgetry to display the contents of a document. The top object of this gadgetry should be a ViewerDialog object. Because it's an easy way to take care of some VILFile library duties, libraries often use a subclass of ViewerDialogClass.

Typically, the VILFile library creates the document display gadgetry by making a copy of a template UI resource. This resource should be marked as read-only and shared in the GP file. It should be declared notDetachable.

When it comes time to discard the UI, we'll send a MSG_VD_CLOSE to the ViewerDialog object. It will do some clean-up work before closing down.

[ViewerDialog screen shot]
@include <viewer.goh>

@class MyViewerDialogClass, ViewerDialogClass;

/* The following messages are utilities; we added them to help out
   with some VILFile library duties */
  @message word			MSG_MVD_OPEN_DOCUMENT( DocumentInfo *doc );
  @message void                 MSG_MVD_CLOSE_DOCUMENT();
  @message word			MSG_MVD_RESTORE_STATE( MemHandle stateBlock );
  @message MemHandle            MSG_MVD_SAVE_STATE();

/* In our dialog UI, we added a "Close" button.   It sends this message. */
  @message void 		MSG_MVD_CLOSE();

/* The following instance data fields contain cached information.
   The information is cached when the document is opened; the information
   is useful for accessing the document file and for closing it. */
  @instance VMFileHandle	MVDI_fileStash;
  @instance DocumentInfo        MVDI_docStash;

/* The dialog is a handy place to cache information.  This example
   displays the sum of two user-entered numbers.  It caches the3
   value of this sum in an instance data field;
   no doubt your library will have various data best cached in the dialog. */
  @instance WWFixedAsDWord 	MVDI_sumNum;
@endc;

@classdecl MyViewerDialogClass;

/* Here is the template UI block; the library will make a copy
   of this template whenever it needs to display a document. */
@start TheViewerUI, notDetachable;

/* At the top of the template's UI tree is a subclassed ViewerDialog */
@object MyViewerDialogClass TVDialog = {
  GI_comp = @TVLabel, @TVCloseTrigger,
            @TVAddend0, @TVAddend1, @TVSum ;
  GI_states = @default & ~GS_USABLE;
}

/* A GenGlyph to display the document name. */
@object GenGlyphClass TVLabel = {
  GI_visMoniker = "docName";
  @localize not;
}

/* In this example, the document's data consists of two numbers,
   which the program adds together.  These two GenValues show
   the numbers, and allow the user to change them. */
@object GenValueClass TVAddend0 = {
  GI_visMoniker = "add";
  GVLI_maximum = MakeWWFixed( 16000 );
}

@object GenValueClass TVAddend1 = {
  GI_visMoniker = "to";
  GVLI_maximum = MakeWWFixed( 16000 );
}

/* This GenGlyph displays the sum. */
@object GenGlyphClass TVSum = {
  GI_visMoniker = "0";
}

/* This is the Close trigger for the dialog. */
@object ComplexMonikerClass TVCloseTrigger = {
  ComplexMoniker = GenTriggerClass;
  CMI_topText = CMT_CLOSE;
  CMI_textStyle = TS_BOLD;

  GTI_destination = @TVDialog;
  GTI_actionMsg = MSG_MVD_CLOSE;

  HINT_SEEK_REPLY_BAR;
  HINT_SEEK_SLOT = 3;
}

@end TheViewerUI;

Four Entry Points

Your VILFile library must provide four entry points (exported routines). The order in which these routines are exported (in the GP file) is important; they should correspond to the values of the ViewerLibraryRoutine enumerated type (defined in viewer.goh). You will need to write these routines.

Open
This routine is responsible for opening the document or restoring from state. There is a number of other tasks which typically take place here.
  • Create UI to display the document. This is normally done by means of a call to UserCreateDialog(), passing the optr of a subclassed ViewerDialog object in a template resource. However you go about creating the UI, follow up by sending a MSG_VD_SET_VIEWER_PARAMS to the dialog.
  • The new UI is going to take up memory. The document browser application will probably need more heap space to allow this. The ViewerDialogClass has a mechanism to help with this. If you call GeodeRequestSpace() and pass the returned token to MSG_VD_SET_HEAPSPACE_TOKEN, the ViewerDialog knows to release this heapspace when it is dismissed. The upshot of all this: now is a good time to call GeodeRequestSpace().
  • Do some correctness checking on the document. Check its protocol number. If your document format has some "magic number" or checksum, this would be a good time to check it.
  • If there's a problem, alert the user and discard the UI by sending a MSG_VD_CLOSE to the ViewerDialog.

The routine is passed a ViewerParams structure which provides either state information or information about the document to be opened. The routine should return an optr (null if there was a problem). The Viewer library will pass this optr as an argument to subsequent routines.

If you're restoring from state, you'll be passed a block of memory which you previously filled with state information.

Detach (Save to State)
This routine is responsible for allocating and filling a block of memory with state information--information which may later be used to restore from state. This routine should also close the document file and discard the document-display UI.
Change Document (Switch Documents)
This routine is responsible for switching between two documents by closing the current document and opening another. The routine should check the new document for correctness (as described for "Open," above). It should update the document-display UI so that it corresponds to the new document. If there's a problem, it should discard the document-display UI.
Close
This routine is responsible for closing the current document. It should close the document file and discard the document-display UI.

It's a good idea to keep the document file open as long as the the document is open--this marks the file as "in use" so that the user won't try to open it and change it in another application.

Code Display: Sample handlers for the four entry points

/* VLR_OPEN       optr( ViewerParams *vp ) */
optr _pascal TVOpen( ViewerParams *vp ) {
  optr   		theTableUI;
  ReservationHandle     moreSpace;

  /* We want to request more heapspace for the app to hold all
     the memory we're going to need. */

  moreSpace = GeodeRequestSpace( 14, GeodeGetProcessHandle() );
    /* We got that 14 by using the 'heapspace' Swat command on an
       app that uses this library, trying to figure out how many
       more k the application needed when it was using this library. */

  if ( moreSpace == 0 ) {
    /* We should alert the user that we're out of memory,
       perhaps with a SysNotify().  But I won't do that for
       now because I'm lazy.  */

    return( NullHandle );
  }

  /* Create the UI to display the document by copying the template resource */
  theTableUI = UserCreateDialog( @TVDialog );

  /* Tell the ViewerDialog about the heapspace we reserved.  The dialog will
     free it when closed. */
  @call theTableUI::MSG_VD_SET_HEAPSPACE_TOKEN( moreSpace );

  @call theTableUI::MSG_VD_SET_VIEWER_PARAMS( vp );

  /* Are we restoring from state or just opening a document? */
  if (vp->VP_state == FALSE ) {

    /* We're opening a document.  Is it new?  */
    if ( vp->VP_attr & VA_NEW_DOCUMENT ) {

      /* Initialize the new document -- we'll see this code later */

    }
    /* Call the code we wrote that opens and displays the document */
    if ( 0 == @call theTableUI::MSG_MVD_OPEN_DOCUMENT( &vp->VP_documentInfo )) {
      /* If there was a problem, discard the UI. */
      @send theTableUI::MSG_VD_CLOSE(GeodeGetCodeProcessHandle(), VCS_CLOSED );
      return( NullHandle );
    }
  }

  else {  /* We're restoring from state. */
      /* Call the code we wrote that restores from state. */
      if (0 == @call theTableUI::MSG_MVD_RESTORE_STATE( vp->VP_state )) {
        /* If there was a problem, discard the UI. */
        @send theTableUI::MSG_VD_CLOSE( GeodeGetCodeProcessHandle(), VCS_CLOSED );
        return( NullHandle );
      }
  }

  /* Whether we're opening a document or restoring from state, we've
     initialized the UI correctly.  Bring up the dialog box. */
  @call theTableUI::MSG_GEN_INTERACTION_INITIATE();

  return( theTableUI );
}

/* VLR_DETACH hptr( optr optrUI );  */
hptr _pascal TVDetach( optr optrUI ) {
  /* We will save our data to state, close the document, and discard UI */
  MemHandle theState;

  theState = @call optrUI::MSG_MVD_SAVE_STATE();
  @call optrUI::MSG_MVD_CLOSE_DOCUMENT();
  @call optrUI::MSG_VD_CLOSE( GeodeGetCodeProcessHandle(), VCS_CLOSED );
  return( theState );
}

/* VLR_CHANGE_DOCUMENT   void( ViewerChangeDocumentStruct *vcds) */
void _pascal TVChangeDocument( ViewerChangeDocumentStruct *vcds) {
  /* Switch between documents--close the current document and open
     the passed document. */
  @call vcds->VCD_viewerOD::MSG_MVD_CLOSE_DOCUMENT();
  if ( 0 == @call vcds->VCD_viewerOD::MSG_MVD_OPEN_DOCUMENT( &vcds->VCD_documentInfo )) {
    @send vcds->VCD_viewerOD::MSG_VD_CLOSE(GeodeGetCodeProcessHandle(), VCS_CLOSED );
  }
}

/* VLR_CLOSE  void (optr optrUI) */
void _pascal TVClose( optr optrUI ) {
  /* Close the current document and discard UI */
  @call optrUI::MSG_MVD_CLOSE_DOCUMENT();
  @call optrUI::MSG_VD_CLOSE( GeodeGetCodeProcessHandle(), VCS_CLOSED );
}

Supporting Other Document UI

Not all document-manipulation requests need come in via the four entry points described above. For example, you might want to add a "Close" trigger to the document display dialog. Pressing the button corresponding to this trigger might result in closing the document and discarding the document display UI. The following code snippet shows how your program might handle a Close trigger's action.

@method Close, MyViewerDialogClass, MSG_MVD_CLOSE {
  /* The user pressed the "Close" button.  Close the document
     file and discard the document display UI */
  @call self::MSG_MVD_CLOSE_DOCUMENT();
  @send self::MSG_VD_CLOSE( GeodeGetCodeProcessHandle(), VCS_CLOSED );
}

Creating, Opening, and Saving Document Files

As discussed, VILFile will, in response to requests from the system or requests generated internally, want to act upon the document file. We haven't talked about how a program goes about doing so.

When the Viewer library requests that the VILFile library open the document file, it will pass the file's path, encoded as a FoamPathHandle, which is equivalent to a FDocumentDir. It will also pass the document file's name. Using a combination of Foam and kernel library routines, your library can open the file and manipulate its contents as shown in the code examples below. The file is a normal GEOS VM File; you may use normal VM routines with it.

Because the Viewer library only supplies a reference to the document file when that file is to be opened, your library will probably need to cache a reference to the file for future operations. It's a good idea to keep the file open while the user is working with the document, so that the system's file-sharing protection will keep other programs from trying to change the file.

Code Display: Manipulating the Document file

#include <Ansi/string.h>

  /* This snippet of code for creating new documents is taken
     from the VLR_OPEN handler. */
  /* This code makes use of "vp", the ViewerParameters structure passed
     to the Open routine */

      VMFileHandle      theFile;
      VMBlockHandle 	theBlock;
      WWFixedAsDWord 	*theBuffer;
      MemHandle         dummyHandle;

      /* Open the document */
      /* Save the current directory */
      FilePushDir();
      FoamSetDocumentDir( vp->VP_documentInfo.DI_docDir );
      theFile = VMOpen( vp->VP_documentInfo.DI_docName,
                        VMAF_FORCE_READ_WRITE | VMAF_FORCE_DENY_WRITE,
                        VMO_OPEN, 0 );

      /* Now that we've opened the file, initialize its data.
         In our example, document files are pretty simple,
         containing two WWWFixedAsDWord values. */
      theBlock = VMAlloc( theFile, 2*sizeof(WWFixedAsDWord), 0 );
      VMSetMapBlock( theFile, theBlock );
      theBuffer = VMLock( theFile, theBlock, &dummyHandle );
      theBuffer[0] = 0;
      theBuffer[1] = 0;

      /* We've written the data.  Mark the file dirty and close it. */
      VMDirty( dummyHandle );
      VMUnlock( dummyHandle );
      VMClose( theFile, FALSE );

      FilePopDir();
  /* So ends the snippet from the VLR_OPEN handler */

  ---------

/* word	MSG_MVD_OPEN_DOCUMENT( DocumentInfo *doc ); */
@method OpenDocument, MyViewerDialogClass, MSG_MVD_OPEN_DOCUMENT {
  /* The entry point routines send this message when they want to open a
     document.
     The handler checks the document's version (to make sure it wasn't
     created by a later, incompatible version of this library) opens the
     document file, reads its contents, and updates the document
     display UI. */

  VMBlockHandle 	theBlock;
  WWFixedAsDWord 	*theBuffer;
  MemHandle         	dummyHandle;
  optr   		someObject;
  ProtocolNumber 	protocol;

  /* Cache the document file's path. */
  pself->MVDI_docStash.DI_docDir = doc->DI_docDir;
  strncpy( pself->MVDI_docStash.DI_docName, doc->DI_docName, FILE_LONGNAME_BUFFER_SIZE );

  /* Open the document file. */
  FilePushDir();  /* Save the old directory */
  FoamSetDocumentDir( doc->DI_docDir );
  pself->MVDI_fileStash = VMOpen( doc->DI_docName,
                                  VMAF_FORCE_READ_WRITE | VMAF_FORCE_DENY_WRITE,
                                  VMO_OPEN, 0 );

  /* Check to see if something went wrong. */
  if ( NullHandle == pself->MVDI_fileStash ) {

    /* Something went wrong--we couldn't open the file. */
    /* We should put in some code here to alert the user
       that something went wrong when we tried to open
       the file.  Maybe someone else has it open. */
     /* For this simple example, we just return an error.
        The code which called us will make the dialog
        go away.  Which is fine.  But it would be better
        if we let the user know what was going on. */
    return( 0 ) ;
  }

  /* We successfully opened the file.  */
  /* Now we check its protocol number.  Perhaps this document was
     created by another, later version of this same library--and
     it might not be compatible with this library.  Normally,
     incompatible versions use different major protocol numbers. */
  /* If we encounter a document with the wrong protocol number,
     we reject it and return an error.  Of course, if we encountered
     an old-version document, we could take measures to extract its
     information and save it out again in the new format.  We would
     then use FileSetHandleExtAttributes() to mark the file with
     its new protocol number. */
  FileGetHandleExtAttributes( pself->MVDI_fileStash,
                              FEA_PROTOCOL,
                              &protocol,
                              sizeof( ProtocolNumber ) );
  if ( protocol.PN_major != 0 ) {
     /* We should put in some code here to alert the user
        that this document is older or newer than we
        know how to deal with. */
     /* For this simple example, we just return an error.
        The code which called us will make the dialog
        go away.  Which is fine.  But it would be better
        if we let the user know what was going on. */
    VMClose( pself->MVDI_fileStash, FALSE );
    return( 0 ) ;
  }

  FilePopDir();

  /* We've successfully opened the file and it looks correct.
     Read its data out and update the document display UI. */
  theBlock = VMGetMapBlock( pself->MVDI_fileStash );
  theBuffer = VMLock( pself->MVDI_fileStash, theBlock, &dummyHandle );

  /* We reuse the "someObject" variable to refer to each of the
     document display objects in turn. */
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend0 ) );
  @call someObject::MSG_GEN_VALUE_SET_VALUE( theBuffer[0], FALSE );
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend1 ) );
  @call someObject::MSG_GEN_VALUE_SET_VALUE( theBuffer[1], FALSE );
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVLabel ) );
  @call someObject::MSG_GEN_REPLACE_VIS_MONIKER_TEXT( doc->DI_docName,
                                                      VUM_NOW );

  /* We're done with the data.  Unlock the data block, but keep the
     file open to discourage other programs from opening it. */
  VMUnlock( dummyHandle );

  /* Return success */
  return( 1 ) ;
}

/* Save and Close the file */
@method CloseDocument, MyViewerDialogClass, MSG_MVD_CLOSE_DOCUMENT {
  /* Our code calls this message when it wants to close a document.  */
  /* The handler gets the current data from the document display UI,
     saves this data to the document file, and closes the document file. */
  WWFixedAsDWord *theBuffer;
  VMBlockHandle  theBlock;
  MemHandle      dummyHandle;
  optr           someObject;

  /* We've previously stashed the handle of the document file. */
  theBlock = VMGetMapBlock( pself->MVDI_fileStash );
  theBuffer = VMLock( pself->MVDI_fileStash, theBlock, &dummyHandle );

  /* Retrieve the data from the document display UI.
     We re-use the "someObject" variable to reference
     each appropriate display object in turn.  */
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend0 ) );
  theBuffer[0] = @call someObject::MSG_GEN_VALUE_GET_VALUE( );
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend1 ) );
  theBuffer[1] = @call someObject::MSG_GEN_VALUE_GET_VALUE( );

  /* We've written the data.  Mark the file dirty and close it. */
  VMDirty( dummyHandle );
  VMUnlock( dummyHandle );
  VMClose( pself->MVDI_fileStash, FALSE );
}

Saving and Restoring State

Sometimes the document viewer might have to shut down even though the user hasn't explicitly closed the document. The user might power down the device, the device might run low on power, or the device might run low on memory while the viewer application is in the background and decide to shut down the viewer application.

Under these circumstances, the VILFile library should save the information needed to restore the document display UI state. As shown below, there is likely to be more state information than there was information in the document itself. The state information will almost certainly need to include the path and filename information for the document file.

#include <Ansi/string.h>

typedef struct {
  WWFixedAsDWord TVS_addend0;
  WWFixedAsDWord TVS_addend1;
  WWFixedAsDWord TVS_sumNum;
  DocumentInfo   TVS_doc;
} TVState;

@method RestoreState, MyViewerDialogClass, MSG_MVD_RESTORE_STATE {
  /* Our code calls this message when it wants to restore from state */
  /* The handler reads data from the passed memory block and updates
     the dialog's instance data and the document display UI's data.
     It also opens the document file, though it ignores the file's
     contents. */
  TVState *theBuffer;
  optr    someObject;
  char	  sumString[LOCAL_DISTANCE_BUFFER_SIZE];

  /* Lock down the passed block */
  theBuffer = MemLock( stateBlock );

  /* Update the document display UI and the data we cache in the
     dialog's instance data. */
  /* We re-use the "someObject" variable to refer to each of the
     document display UI objects in turn. */
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend0 ) );
  @call someObject::MSG_GEN_VALUE_SET_VALUE( theBuffer->TVS_addend0, FALSE );
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend1 ) );
  @call someObject::MSG_GEN_VALUE_SET_VALUE( theBuffer->TVS_addend1, FALSE );
  pself->MVDI_docStash.DI_docDir = theBuffer->TVS_doc.DI_docDir;
  strncpy( pself->MVDI_docStash.DI_docName,
           theBuffer->TVS_doc.DI_docName,
           FILE_LONGNAME_BUFFER_SIZE );

  /* Open the document file. */
  FilePushDir();
  FoamSetDocumentDir( theBuffer->TVS_doc.DI_docDir );
  pself->MVDI_fileStash = VMOpen( theBuffer->TVS_doc.DI_docName,
                                  VMAF_FORCE_READ_WRITE | VMAF_FORCE_DENY_WRITE,
                                  VMO_OPEN, 0 );

  if ( NullHandle == pself->MVDI_fileStash ) {
    /* Something went wrong when we attempted to open the file.
       In the time after we shut down to state, any of a number
       of things could have happened.  The file may have been
       deleted, or it may have been opened by another application.
       We should get the error and do something with it. */

     /* In this simple example, we just return an error.
        The code which called us will make the dialog
        go away.  Which is fine.  But it would be better
        if we let the user know what was going on. */
    return( 0 );
  }

  FilePopDir();

  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVLabel ) );
  @call someObject::MSG_GEN_REPLACE_VIS_MONIKER_TEXT( theBuffer->TVS_doc.DI_docName,
                                                      VUM_NOW );
  pself->MVDI_sumNum = theBuffer->TVS_sumNum;
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVSum ) );
  LocalFixedToAscii( sumString, pself->MVDI_sumNum, 0 );
  @call someObject::MSG_GEN_REPLACE_VIS_MONIKER_TEXT( sumString, VUM_NOW );

  return( 1 ) ;
}

@method SaveState, MyViewerDialogClass, MSG_MVD_SAVE_STATE {
/* Our code sends this message when it needs to shut down to state.
   The handler allocates a memory block and fills it with the
   information we'll need to restore to state later--data from
   the document display UI objects and cached information from
   the dialog. */
  MemHandle 	stateBlock;
  TVState   	*theBuffer;
  optr 		someObject;

  /* Allocate the block */
  stateBlock = MemAlloc( sizeof( TVState ),
                         (HF_DYNAMIC | HF_SHARABLE) ,
                         HAF_LOCK );
  theBuffer = MemDeref( stateBlock );

  /* Retrieve data from objects and write it to the block */
  theBuffer->TVS_doc.DI_docDir = pself->MVDI_docStash.DI_docDir;
  strncpy( theBuffer->TVS_doc.DI_docName,
           pself->MVDI_docStash.DI_docName,
           FILE_LONGNAME_BUFFER_SIZE );
  theBuffer->TVS_sumNum = pself->MVDI_sumNum;
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend0 ) );
  theBuffer->TVS_addend0 = @call someObject::MSG_GEN_VALUE_GET_VALUE( );
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend1 ) );
  theBuffer->TVS_addend1 = @call someObject::MSG_GEN_VALUE_GET_VALUE( );

  /* We're done.  Unlock the block and return it. */
  MemUnlock( stateBlock );
  return( stateBlock );
}
| Up: GEOS SDK TechDocs | Up | Prev: 1 Document Browser Application


Communications

GEOS SDK Documentation: Communications

| Up: GEOS SDK TechDocs

The following documents contain information about communicating.

Access Point Library
Repository of information about the user's ISPs and other providers of communication media.
Contacts Database
The Nokia 9000i Communicator has a database which acts as a repository for contact information, including phone numbers.
Socket Library
The GEOS Socket API, TCP/IP and PPP connections.
Modem Connections
The Nokia 9000i Communicator supports a sort of "virtual modem" which you may use to make a modem connection.
SMS Guide
The Nokia 9000i Communicator has specialized classes for working with SMS (the Short Message System).
Printing
Creating printed hardcopy output.
Mailbox Library
The Mailbox Library underlies inbox/outbox functions. Most programmers won't do much with Mailbox directly, using just a few functions depending on what and how they're communicating.
Sending Faxes
How to send a fax: prompting the user for a phone number, drawing the contents.
Distributing Your Application
The Nokia 9000i Communicator has protocols set up for the distribution of software packages.
PCCom Library
Low-level API used to communicate with another device over a serial connection.
Using Streams
Low-level stream-based communication API.
| Up: GEOS SDK TechDocs


Access Point

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Access Points
| One-page Version of this Chapter for Easy Printing

The Access Point library acts as a sort of special address book--it keeps track of phone numbers and other information about Internet Service Providers. It can also store information about Terminal and Telnet access points. The library provides UI gadgetry by which the user may choose and edit access point entries; the library also provides routines by which programs may do the same.

1 Access Points
2 Letting The User Choose
2.1 Multiple-Selection Access Point Controllers
3 Searching For an Access Point
4 Locking and Unlocking
5 Adding an Access Point
6 Deleting an Access Point


Access Points

Access Point: 1 Access Points

| Up: GEOS SDK TechDocs | Up | Prev: Access Point | Next: 2 Letting The User Choose

Access points, loosely defined, are collections of data useful for making communications connections. The Access Point library keeps track of access points by storing their information in the .INI file.

The library defines a few kinds of access points, identified by the AccessPointType enumerated type:

APT_INTERNET
This access point holds data useful for connecting to an ISP. Applications making a PPP connection can use this information to make the connection.
APT_TERMINAL
This access point holds data useful for connecting to a dialup terminal.
APT_TELNET
This access point holds data useful for making a telnet connection.

For each access point, the AccessPoint library may keep track of any number of properties. While you may store all sorts of information about any access point, there are a number of standard properties, enumerated as AccessPointStandardProperty values, including name, phone number, user ID, address, and password.

Access points are referenced by word-length ID numbers.

| Up: GEOS SDK TechDocs | Up | Prev: Access Point | Next: 2 Letting The User Choose


Letting The User Choose

Access Point: 2 Letting The User Choose

| Up: GEOS SDK TechDocs | Up | Up | Prev: 1 Access Points | Next: 3 Searching For an Access Point

The Talk sample application shows how an application can use an AccpntControl object to provide UI for picking an access point. This example is discussed in the Socket library documentation. The application also shows how to use the selected access point (in this case, an ISP) to establish a PPP connection; this part of the application is discussed further in the Socket chapter.

In this example, the controller is declared:

            @chunk char accpntMkr[] = "Access List";
            @object AccessPointControlClass AccpntControl = {
                GI_states = GS_USABLE | GS_ENABLED;
                ATTR_ACCESS_POINT_CONTROL_LIST_MONIKER = @accpntMkr;
                HINT_ACCESS_POINT_CONTROL_MINIMIZE_SIZE;
            }

To present the user with a different type of access points (telnet connections, for example) set the type in the APCI_type instance data field:

@object AccessPointControlClass AccpntControl = {
    ...
    APCI_type = APT_TELNET;
}

You may wish to set up your program's UI such that there is a gadget which will not be enabled (will be grayed out) if there are no access points of the proper type defined. To do this, turn off the object's GS_ENABLED flag in its declaration, and use the object's optr as the value of the APCI_enableDisable instance field:

@object ... MyObject = {
    ...
    GI_states = @default & ~GS_ENABLED;
}
@object AccessPointControlClass AccpntControl = {
    ...
    APCI_enableDisable = @MyObject;
}

To find out the ID number of the controller's currently selected access point, use MSG_ACCESS_POINT_CONTROL_GET_SELECTION .

To find out when the user has selected an access point (changed their selection), you have to be a bit trickier.

  • Subclass AccessPointControlClass .
  • Write a method for your subclass, intercepting MSG_ACCESS_POINT_CONTROL_UPDATE_SELECTION. Be sure to use @callsuper to invoke the default handler for this message.
  • Use an object of your subclass instead of a regular AccessPointControl object.

While the application is using the selected access point, it can "lock" the access point to prevent the access point's parameters from being modified while it's in use. (See Locking and Unlocking .)

Multiple-Selection Access Point Controllers

| Up: GEOS SDK TechDocs | Up | Up | Prev: 1 Access Points | Next: 3 Searching For an Access Point


Multiple-Selection Access Point Controllers

Access Point: 2 Letting The User Choose: 2.1 Multiple-Selection Access Point Controllers

| Up: GEOS SDK TechDocs | Up | Prev: 2 Letting The User Choose | Next: 3 Searching For an Access Point

Recent versions of the Access Point library support controllers which allow the user to select more than one access point at a time. This feature is available on devices with version numbers "Responder Build 4...." and higher. See Software Version Number for information about finding out the software version of the user's device.)

To create such an AccpntControl object, give it the hint HINT_ACCESS_POINT_CONTROL_MULTISELECTABLE. To get a list of the currently selected access points, use MSG_ACCESS_POINT_CONTROL_GET_MULTIPLE_SELECTIONS. The prototype for this message is:

@message word MSG_ACCESS_POINT_CONTROL_GET_MULTIPLE_SELECTIONS(
                          word    *buffer,
                          word     maxIDs);

buffer
A buffer in which to store the IDs of the currently selected access point entries.
maxIDs
The number of selections to place in buffer. Use MSG_ACCESS_POINT_CONTROL_GET_NUM_SELECTIONS to get the number of currently selected entries.
| Up: GEOS SDK TechDocs | Up | Prev: 2 Letting The User Choose | Next: 3 Searching For an Access Point


Searching For an Access Point

Access Point: 3 Searching For an Access Point

| Up: GEOS SDK TechDocs | Up | Prev: 2 Letting The User Choose | Next: 4 Locking and Unlocking

Perhaps instead of allowing the user to choose an access point, the program should use a known access point. The following code example shows how a program might search for an access point, given its type and name.

Code Display 23-1 Searching for an Access Point

@start  TemplateStringsResource, data;
    @chunk  TCHAR TargetName[] = "Stuckeys Sales Server";
@end    TemplateStringsResource;
@method TemplateProcessClass, MSG_TP_SEND {
    MemHandle     aBlock;
    ChunkHandle   theList;  /* Chunk Array of entry IDs of access points */
    word          theID = ACCESS_POINT_INVALID_ID ;
    aBlock = MemAllocLMem( LMEM_TYPE_GENERAL, 0 );
    MemLock( aBlock );
    theList = AccessPointGetEntries( aBlock , 0, APT_INTERNET );
    ChunkArrayEnumHandles( aBlock, theList, &theID, FunkyCallback);
    MemUnlock( aBlock );
    MemFree(aBlock);
    if (theID == ACCESS_POINT_INVALID_ID ) {
      /* didn't find the access point--deal with it */
    } else {
      /* found the access point--use it */
    }
}
Boolean _pascal FunkyCallback( void *el, void *retVal ) {
    char nomen[128];
    int  nomenSize;
    int  cmpResult;
    char *accessPointName;
    AccessPointGetStringPropertyBuffer( *((word *)el), APSP_NAME,
                                           nomen, &nomenSize );
    MemLock( OptrToHandle(@TargetName) );
    cmpResult = LocalCmpStrings( LMemDeref( @TargetName ) , nomen, 0);
    MemUnlock( OptrToHandle(@TargetName) );
    if (cmpResult) return(FALSE); 					/* if they don't match, return FALSE so
                       we can keep searching. */

    *(word *)retVal = *(word *)el;					/* We found a match. grab its ID */
    return(TRUE);				 	/* ...and we can stop searching now. */
}

The AccessPointGetEntries() routine returns a chunk array containing a list of access point IDs.

We then use the ChunkArrayEnumHandles() routine to call a callback routine to examine each ID in turn. There are many other ways we could have cycled through these entries--see the documentation of chunk arrays in the Local Memory chapter to find out which is best for your purpose.

Given an access point's ID number, we find out the name associated with that ID by calling AccessPointGetStringPropertyBuffer() . To specify that we're interested in the name, we pass APSP_NAME; we could also have passed some other AccessPointStandardProperty value, or even a pointer to a string to search for the value of some custom property.

While the application is using the selected access point, it can "lock" the access point to prevent the access point's parameters from being modified while it's in use. (See Locking and Unlocking .)

| Up: GEOS SDK TechDocs | Up | Prev: 2 Letting The User Choose | Next: 4 Locking and Unlocking


Locking and Unlocking

Access Point: 4 Locking and Unlocking

| Up: GEOS SDK TechDocs | Up | Prev: 3 Searching For an Access Point | Next: 5 Adding an Access Point

If something were to alter an access point's parameters while your application was using that access point, bad things might happen. To prevent anything from changing the access point's parameters, use the AccessPointLock() routine. When done using the access point, use the AccessPointUnlock() routine to unlock it. To check whether or not an access point is being used for a connection, use the routine AccessPointInUse().

| Up: GEOS SDK TechDocs | Up | Prev: 3 Searching For an Access Point | Next: 5 Adding an Access Point


Adding an Access Point

Access Point: 5 Adding an Access Point

| Up: GEOS SDK TechDocs | Up | Prev: 4 Locking and Unlocking | Next: 6 Deleting an Access Point

To add a new access point:

  • Use the AccessPointCreateEntry() routine to create the access point. To create an internet access point, use the syntax:
newID = AccessPointCreateEntry( 0, APT_INTERNET );
  • Using the ID returned by AccessPointCreateEntry() , set the appropriate properties for the access point.
| Up: GEOS SDK TechDocs | Up | Prev: 4 Locking and Unlocking | Next: 6 Deleting an Access Point


Deleting an Access Point

Access Point: 6 Deleting an Access Point

| Up: GEOS SDK TechDocs | Up | Prev: 5 Adding an Access Point

To delete an existing access point:

  • Call AccessPointDestroyEntry() .

| Up: GEOS SDK TechDocs | Up | Prev: 5 Adding an Access Point


Fax Sending

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Destination Phone Number
| One-page Version of this Chapter for Easy Printing

This chapter describes how an application can use the fax libraries and Mailbox library to compose and send a fax. Most GEOS devices, including the Nokia 9000i Communicator, come with an application that composes and sends simple fax documents; this chapter will show you how you can write a program that composes a custom fax using graphics commands.

This chapter assumes that you know the basics of GEOS programming. It also assumes that you know how to draw the image that you want to fax, given a GState to draw to. (Most visible objects draw themselves when sent a MSG_VIS_DRAW ; documentation of kernel drawing commands may be found in

A PrintControl object provides the API for drawing the contents of the fax; while this chapter will tell you what you need to know for simple faxes, more information about the PrintControl is available in .

Sending a fax may be broken down into the following tasks:

  • Finding out where to send the fax; and
  • Drawing the contents of the fax.

Ideally, these tasks are carried out using standard object classes that conform to the device's look and feel.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Destination Phone Number
| One-page Version of this Chapter for Easy Printing


Destination Phone Number

Fax Sending: 1 Destination Phone Number

| Up: GEOS SDK TechDocs | Up | Prev: Fax Sending | Next: 2 Drawing the Contents

To prompt the user for the fax's destination phone number while employing the device's standard look and feel, we will use the Mailbox library and its fax adjuncts. Specifically, we will use a MailboxSendControl object, configured for use with fax.

Make sure the following lines appear in the application's .gp file:

library mailbox   # Defines MailboxSendControlClass
library spool     # Defines PrintControlClass

We will use a MailboxSendControl object to provide the UI that prompts the user for a phone number. When it comes time to draw the contents of our fax, we'll also need a PrintControl object.

We make sure that these controller objects are added to the correct GCN lists in the declaration of our application's Application object:

Code Display 4-1 Adding the Fax Controls to the Proper GCN Lists

In the declaration of the application's Application object, we make sure that the MailboxSendControl and PrintControl objects are placed on the proper GCN lists.

@object ... MyApp = {
    ...
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) =
                ... @MyPrintControl, @MySendControl;
    gcnList(MANUFACTURER_ID_GEOWORKS, MGCNLT_ACTIVE_LIST) = ... @MySendControl;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_MAILBOX_SEND_CONTROL) =
                            ... @MySendControl;
    ATTR_GEN_APPLICATION_PRINT_CONTROL = @MyPrintControl;
}

We make sure that the MailboxSendControl will be visible to the user; the straightforward way to do this is to add it to the application's generic UI tree. For example:

@object GenPrimaryClass MyPrimary = {
    GI_comp  = ..., @MySendControl;
}

Finally, we're ready to declare the MailboxSendControl object itself:

Code Display 4-2 MailboxSendControl Object Ready for Fax

@object MailboxSendControlClass MySendControl = {
    GI_visMoniker = "Send";
    GII_visibility = GIV_POPUP;
    GCI_output = process;
    MSCI_defBodyType = MOT_SELECTION;
    ATTR_MAILBOX_SEND_CONTROL_SEND_SELECTION;
    ATTR_GEN_INIT_FILE_CATEGORY = "fax" ;
    ATTR_MAILBOX_SEND_CONTROL_SINGLE_TRANSPORT = {
    {GMID_CELL_MODEM, MANUFACTURER_ID_GEOWORKS},
    {GMTID_FAX_SEND, MANUFACTURER_ID_GEOWORKS},0};
    ATTR_MAILBOX_SEND_CONTROL_TRANSPORT_HINT_OPTR =  {
      {GMTID_FAX_SEND, MANUFACTURER_ID_GEOWORKS}, 0,
      @MyPrintControl};
}

(If your application is to run on the Nokia 9000i Communicator, use ComplexMonikerClass to give your MailboxSendControl the standard Nokia 9000i Communicator look and feel. In the example above, you would replace

@object MailboxSendControlClass MySendControl = {
    GI_visMoniker = "Send";

...with...

@object ComplexMonikerClass MySendControl = {
    ComplexMoniker = MailboxSendControlClass;
    CMI_topText = CMT_SELECT_RECEIVER;
    HINT_SEEK_REPLY_BAR; /* make this button 0 */
    HINT_SEEK_SLOT = 0;

)

ATTR_MAILBOX_SEND_CONTROL_TRANSPORT_HINT_OPTR tells the MailboxSendControl that when it comes time to send a fax, it should look to the MyPrintControl object to provide the data. The FaxSendTD Mailbox transport driver knows how to interact with PrintControl objects; we will add code to our program to work with the PrintControl to draw our fax contents.

ATTR_GEN_INIT_FILE_CATEGORY tells the send control where to find the user's faxing preferences within the INI file--the "fax" category.

ATTR_MAILBOX_SEND_CONTROL_SINGLE_TRANSPORT specifies that this MailboxSendControl only sends data by fax. To allow your application to send data in additional ways, get rid of this ATTR.

The MailboxSendControl would ask the user what data they wish to send: the current selection, current page, all pages. In most fax programs, we don't want to give the user that choice. In our example, we use the ATTR_MAILBOX_SEND_CONTROL_SEND_SELECTION vardata field to prevent the controller from presenting the user with this choice--the MailboxSendControl will always try to send the "current selection," though our program ignores any idea of selection.

| Up: GEOS SDK TechDocs | Up | Prev: Fax Sending | Next: 2 Drawing the Contents


Drawing the Contents

Fax Sending: 2 Drawing the Contents

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Destination Phone Number | Next: 2.1 Declaring the Objects

We have configured the MailboxSendControl so that when the user has specified a recipient for the fax, the control will query a PrintControl object for the fax's contents.

Declaring the Objects

Supplying the Document's Name

Supplying the Fax's Contents

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Destination Phone Number | Next: 2.1 Declaring the Objects


Declaring the Objects

Fax Sending: 2.1 Drawing the Contents: Declaring the Objects

| Up: GEOS SDK TechDocs | Up | Prev: 2 Drawing the Contents | Next: 2.2 Supplying the Document's Name

Again, make sure the following line is in the application's .gp file:

library spool    # Defines PrintControlClass

In the previous section, we saw that the PrintControl object needed to appear on certain GCN lists and in the MailboxSendControl 's ATTR_MAILBOX_SEND_CONTROL_TRANSPORT_HINT_OPTR field. (See Adding the Fax Controls to the Proper GCN Lists and MailboxSendControl Object Ready for Fax .)

Assuming that the PrintControl won't be used for anything else, the following declaration will suffice:

@object PrintControlClass MyPrintControl = {
    GI_states = @default & ~GS_ENABLED;
    PCI_docNameOutput = process;
    PCI_output = process;
}

Here we have turned off the GS_ENABLED flag because the PrintControl 's UI will never appear in the application; it isn't part of the application's generic UI tree. (To include the PrintControl's UI in your application, leave it enabled, add it to the generic UI tree, and see the Printing chapter for more information.)

The PCI_output line specifies that the application's process object will be responsible for drawing the fax's contents. The PCI_docNameOutput line specifies that the application's process will be responsible for providing the fax's name.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Drawing the Contents | Next: 2.2 Supplying the Document's Name


Supplying the Document's Name

Fax Sending: 2.2 Drawing the Contents: Supplying the Document's Name

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Declaring the Objects | Next: 2.3 Supplying the Fax's Contents

The process object (or whatever object is specified in the PrintControl 's PCI_docNameOutput field) must supply the fax's name when queried. (On the Nokia 9000i Communicator, this name is not especially important; this is just a required part of the PrintControl 's API.) The name need not be unique.

To fulfill its responsibility, the object must handle MSG_PRINT_GET_DOC_NAME , responding by sending MSG_PRINT_CONTROL_SET_DOC_NAME . If the message is not handled in this manner, the fax will never be sent.

The following is an acceptable handler:

Code Display 4-3 Handling MSG_PRINT_CONTROL_SET_DOC_NAME for faxing

The printCtrlOD value is an argument to the message.

@method MyProcessClass, MSG_PRINT_GET_DOC_NAME {
    @send printCtrlOD::MSG_PRINT_CONTROL_SET_DOC_NAME("Good Morning");
}
| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Declaring the Objects | Next: 2.3 Supplying the Fax's Contents


Supplying the Fax's Contents

Fax Sending: 2.3 Drawing the Contents: Supplying the Fax's Contents

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Supplying the Document's Name

The process object (or whatever object is specified in the PrintControl 's PCI_output field) describes the fax's contents in the method for the imported message MSG_PRINT_START_PRINTING . It will receive this message after the user has specified a recipient for the fax. This message is defined:

void MSG_PRINT_START_PRINTING( \
            optr 			printCtrlOD , \
            GStateHandle 			gstate );

There are some things that the handler for this message must do:

  • It should draw the fax's contents.
  • After drawing the fax's contents, it should call GrNewPage() to generate a form feed. (It should also call GrNewPage() between the pages of a multi-page fax, of course.)
  • It should send the PrintControl MSG_PRINT_CONTROL_PRINTING_COMPLETED .

There are many things that the handler for this message may do. For information about some of them, see thePrinting chapter. (If you read that chapter, you might think it's important that the print job's page range is also determined; this is not important for faxing.)

The following example shows a handler that draws a fax's contents to contain some text and a rectangle:

Code Display 4-4 Handling MSG_PRINT_START_PRINTING when faxing

@method MyProcessClass, MSG_PRINT_START_PRINTING {
/* We draw the contents of the fax. */
    GrDrawText(gstate, 20,40, "Good Morning", 0);
    GrDrawRect(gstate, 10,30, 110,55);
/* Make sure that our last drawing command is a form-feed */
    GrNewPage(gstate, PEC_FORM_FEED);
/* Let the PrintControl know we're done */
    @send printCtrlOD::MSG_PRINT_CONTROL_PRINTING_COMPLETED();
}

In the example, we draw the fax's contents with a couple of kernel graphics commands. We might just have easily passed gstate as the graphic state argument to some Visible object's MSG_VIS_DRAW message (also passing the DrawFlag DF_PRINT).

We also need to tell the PrintControl how many pages we're printing. The first page that we draw on is page one. If there is a separate cover page, that will be page zero. To specify how many pages there are to print, we call MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE, and then MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE, passing the number of pages to print as that message's second argument.

It is possible that the fax cover page will appear on the same page on which drawing starts. To find out how much vertical space this cover page will take up on the first page, call the PrintControl with MSG_PRINT_GET_FIRST_PAGE_OFFSET ; the return value will be the height of the cover page. (To properly follow naming conventions, this message should be called MSG_PRINT_CONTROL_GET_FIRST_PAGE_OFFSET ; as of this writing it has not yet had its name fixed.)

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Supplying the Document's Name


The Mailbox Library

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing

One of the most important functions of a portable computing device is to exchange information with other devices. A portable computer might use a number of different physical means to transmit and receive information; it might also use a variety of communication protocols (some of which may not be invented yet!).

GEOS helps to insulate an application from the physical details of transmitting and receiving information. It does this with its Mailbox library, and associated drivers. The library lets each application transmit and receive information in more or less the same way, no matter how it uses the information, and no matter what it does with the information once it receives it (or how it assembles the information before transmitting it).

The Mailbox API relies heavily on a basic understanding of VMChain structures and routines. Please consult the Virtual Memory chapter of the Concepts book for more information.

NOTE: This is a preliminary document. Please consult the Mailbox definition files ( mailbox.def or mailbox.goh ) for current and complete information.

1 Design Philosophy
1.1 Mailbox Overview
1.2 Transport Driver Overview
2 Inbox/Outbox Structure
3 Mailbox Structures & Utilities
4 Data Formats
4.1 GMDFID_STREAM_GSTRING
4.2 GMDFID_VM_GSTRING
4.3 GMDFID_TRANSFER_ITEM
4.4 GMDFID_TEXT_CHAIN
4.5 GMDFID_TEXT_FILE
4.6 GMDFID_DOCUMENT
4.7 GMDFID_INK
4.8 GMDFID_VM_COMPOSITE
5 Data Drivers
6 Application Responsibilities
6.1 Providing a User-Interface
6.2 Sending a Message
6.3 Receiving and Processing Messages
7 Examples of Mailbox Usage
7.1 An Incoming Pager Email Message
7.2 Sending and Receiving an Address Book Page

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing


Design Philosophy

The Mailbox Library: 1 Design Philosophy

| Up: GEOS SDK TechDocs | Up | Down | Prev: The Mailbox Library | Next: 2 Inbox/Outbox Structure

The purpose of the Mailbox library is to let applications send and receive information to and from other computers, through a variety of communications media. It insulates the application as much as possible from the mechanics of transmitting and receiving the information.

An application might need to transmit information over several different kinds of connection. For example, it might send information over a modem or through a direct connection. It might need to use the same device in very different ways; for example, it might fax a document through a modem, or copy a file to a server through that same modem.

The Mailbox library helps to insulate applications from the details of the hardware and communication protocols. It does this by dividing up the communications tasks.

Mailbox Overview

Transport Driver Overview

| Up: GEOS SDK TechDocs | Up | Down | Prev: The Mailbox Library | Next: 2 Inbox/Outbox Structure


Mailbox Overview

The Mailbox Library: 1.1 Design Philosophy: Mailbox Overview

| Up: GEOS SDK TechDocs | Up | Prev: 1 Design Philosophy | Next: 1.2 Transport Driver Overview

The Mailbox library implements an Inbox/Outbox system. This lets the user select an appropriate device through which to transport the information; it also lets the user select a delivery time (though most applications will always designate messages for immediate delivery, so it is possible that the first release of the Inbox/Outbox system will not support applications setting an early-bound for delivery time). (Most applications will do this by including the Mailbox Library's MailboxSendControl device, which the user can use to create an outgoing message.)

The application must, when requested, package up information in a format acceptable to a transport driver being used. (Most applications will only need to support one or a few different data formats, such as GStrings or raw text in a VM Chain.) It then registers this message with the Mailbox library.

The Mailbox library stores messages that have not yet been transmitted in its outbox . Messages are held in the outbox until the hardware connection necessary for transmission is available. For example, if the transport driver needed a direct serial link, the message would stay in the outbox until that link was available. An application can also specify that a message not be delivered until a specific time. In this case, the message will automatically remain in the outbox until that time. While a message is in the outbox, the device's user can examine information about the message, and (if he or she chooses) delay or cancel its delivery.

When the time comes to send the message, the Mailbox library activates the appropriate data and transport drivers. This activation is transparent to an application developer. It is helpful to understand how a transport driver operates, however.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Design Philosophy | Next: 1.2 Transport Driver Overview


Transport Driver Overview

The Mailbox Library: 1.2 Design Philosophy: Transport Driver Overview

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Mailbox Overview | Next: 2 Inbox/Outbox Structure

The transport driver handles actually sending a message of the device. It interacts with the actual hardware used for sending the message. This document does not attempt to discuss creating transport drivers in detail.

Messages are received much the way they are delivered. The transport driver receives a message, registers it with the Mailbox library, and passes its data, block by block, to the appropriate data driver. The data driver translates this message into its own data format. The Mailbox library stores the message in its inbox until it can deliver the message. When the appropriate time for delivery is reached, the Mailbox library starts up an appropriate application (if it is not already running) and passes the message to the application. The application must notify the Mailbox library when it has finished with the message.

A user or application can address a message to the local machine. In this case, the message will not be sent to the transport driver; the Mailbox library will deliver the message simply by moving it from the machine's outbox to its inbox.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Mailbox Overview | Next: 2 Inbox/Outbox Structure


Inbox/Outbox Structure

The Mailbox Library: 2 Inbox/Outbox Structure

| Up: GEOS SDK TechDocs | Up | Prev: 1 Design Philosophy | Next: 3 Mailbox Structures & Utilities

The Mailbox library interacts directly with applications. It also manages drivers, which provide information about data formats, and also interact with the hardware links to the outside world. Thus, to support a new hardware device or data format, you just need to write a new driver.

The responsibilities of the Inbox/Outbox system are divided among four different agents:

  • The application packages outgoing data, and handles incoming data addressed to that application. It provides data in a format acceptable to the transport driver, and receives information in such a format. It generally uses the MailboxSendControl controller to let the user control the sending of messages.
  • The data driver describes how the information is stored; it thus lets the application know (indirectly) both how to assemble data for transport. (It is only concerned with the gross structure of the message, e.g. whether it's stored in a VM chain or a plain-text file. The structure above that level is opaque to the data driver.) It stores the message until the message is ready for transport. It also translates outgoing messages into a format appropriate for the transport driver, and translates incoming messages from such a format into its own internal format.
  • The transport driver interacts with the actual hardware device used to transmit or receive the data. It receives outgoingmessage data from the data driver, and sends it over the device; it receives incoming messages, and sends them to the appropriate data driver. In some cases, it may translate the message's data; for example, a fax transport driver might read a message containing a GString, and translate that into a standard fax format readable by a (non-GEOS) fax machine.
  • The Mailbox library manages the interaction between the other agents. It keeps track of messages that have been designated for transport at another time, and messages that have been received by the computer but not yet delivered to any application. It loads the appropriate drivers and applications when they are needed. It also provides UI, letting the user set options about how to handle outgoing and incoming messages.
| Up: GEOS SDK TechDocs | Up | Prev: 1 Design Philosophy | Next: 3 Mailbox Structures & Utilities


Mailbox Structures & Utilities

The Mailbox Library: 3 Mailbox Structures & Utilities

| Up: GEOS SDK TechDocs | Up | Prev: 2 Inbox/Outbox Structure | Next: 4 Data Formats
MailboxGetVMFile(), MailboxMessageFlags, MailboxMessagePriority, MailboxDeliveryVerb

There are a few data types that are used at various parts of the message-transmission and reception process. There are also a few concepts related to these types that need to be discussed first.

First of all, we need to distinguish between two basic data types: data format types , and data storage types .

Every message has two data tokens associated with it. One is the data format type, and the other is the data storage type. The data storage type describes the gross structure of the message data. Every data driver can accept one data storage type. For example, the GEOS system includes data drivers for messages whose bodies are stored in VM trees, and messages whose bodies are stored in byte-files. The data driver is not concerned with the message on a lower level. If you send a message in a VM tree, the data drivers at both machines will make sure that the message arives in a valid VM tree (which may mean fixing up the VM handles that make up the links in the tree), but they won't care about the data inside the blocks of the tree.

On a higherlevel, every message has its own data format type. This type describes how the data is actually stored in the underlying structure. For example, a message might be stored as a GString in a VM chain. In this case, the message's data storage type would be "VM tree" (a VM chain is a special kind of VM tree), and the data format type would be "VM Gstring".

Every data format type implies a particular data storage type; all messages with data format type a will necessarily use data storage type x . The converse, however, is not necessarily true. For example, all messages with data format type "VM GString" have data storage type "VM tree"; as do all messages with data format "VM composite".

When an application passes a message to a data driver, it may designate the message body as either "volatile" or "non-volatile". If the data is marked "volatile", the application may alter or destroy the data as soon as it has registered the message. That is, when the application registers the message, the appropriate Data Driver copies all the message's information out of the passed message before execution returns to the application. You should pass "volatile" if, for example, you are passing a pointer to a VM file used by the application.

If you designate the data as non-volatile , you are not allowed to alter or delete the data in the message. If you ask it to, the data driver will take care of freeing the storage used for the message when it is done with it. Every data driver has its own conventions about what applications should do with non-volatile messages after sending them.

For example, if you send a message through the VM tree data driver, you should write non-volatile messages to a special VM file created by the routine MailboxGetVMFile() . This routine is passed a single argument, the number of blocks you expect to allocate in the VM block. The routine returns the handle of a VM file. You can write the message in this file, and register the message (designated "non-volatile"). The data driver will then take care of freeing the file's storage when the message has been safely delivered.

Each message is associated with a set of MailboxMessageFlags . These are a byte-length record. This record has the following flags:

MMF_BODY_DATA_VOLATILE
As discussed above, this flag indicates that the data in the message must be copied immediately, as it is subject to change or freeing without further notice.
MMF_SEND_WITHOUT_QUERY
This flag indicates that the message should be sent without asking the user for permission.
MMF_PRIORITY
This is a member of the MailboxMessagePriority enumerated type, discussed below.
MMF_VERB
This is a member of the MailboxDeliveryVerb enumerated type, discussed below.
MMF_DELETE_BODY_AFTER_TRANSMISSION
Set if the data driver should delete the message body after transmitting. Note that if MMF_BODY_DATA_VOLATILE is set, then the data driver will always delete the copy of the body it made at the time the message was registered, and leave the body of the original app-ref alone, regardless of how MMF_DELETE_BODY_AFTER_TRANSMISSION is set. If there is an error during transmission, the message body will still be deleted.

Every message can be assigned a priority. This priority is represented by a member of the MailboxMessagePriority enumerated type. The priority affects how the Mailbox library will handle the message at both the sending and the receiving end. This type has the following values:

MMP_EMERGENCY
The recipient user should be notified immediately, and not allowed to do anything before he or she receives the message.
MMP_URGENT
The user should be notified immediately, and told there is an urgent message awaiting. The user does not, however, have to read the message right away.
MMP_FIRST_CLASS
The user should be notified on the next periodic check of the Inbox. There are no other constraints on when the user should read the message.
MMP_THIRD_CLASS
The Inbox should not notify the user of the message's arrival; instead, the appropriate application should notify the user, if appropriate. For outgoing messages, the user must explicitly request that the message be sent; it will not happen automatically once a connection is made.

Every message has a "delivery verb" associated with it. This is useful in notifying the user of the message's arrival. Each delivery verb is represented by a member of the MailboxDeliveryVerb enumerated type. This type has the following members:

MDV_DELIVER
This is the default verb.
MDV_VIEW
The message is some sort of image that can be viewed, e.g. a received fax.
MDV_PLAY
The message is some kind of continuous message with a definite starting point and ending point, e.g. a voice recording or MPEG movie.
MDV_ACCEPT
Another generic verb.
MDV_READ
The message is mainly textual, and can be read (e.g. an old-fashioned EMail message).
MDV_FILE
The message is, or contains, one or more units that should be stored in a document that can contain many such units (e.g. an address-book card).
| Up: GEOS SDK TechDocs | Up | Prev: 2 Inbox/Outbox Structure | Next: 4 Data Formats


Data Formats

The Mailbox Library: 4 Data Formats

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Mailbox Structures & Utilities | Next: 5 Data Drivers

An application is largely insulated from the details of transmitting a message. It needs merely to write the message's data in a format which the chosen data driver can understand; the data driver will then translate this into an appropriate format for transmission.

GEOS provides two data drivers. These drivers use the data transport formats "File Data" and "VM tree data". Each data-format type provided by GEOS is built on top of one of these formats.

There are a few concerns in common to each data driver.

Every data driver will have its own format for accepting the data. In each case, the data driver is passed a pointer to a "data block" which tells it where the rest of the message is. Thus, every data driver is called in the same way: it is passed a single far pointer to some data block. How that data block stores the location of the message will vary from one data storage format to another.

GEOS will almost certainly define the following different data format types. An application should, if possible, support as many of these data types as is feasible. Each format is uniquely identified by a member of the GeoworksMailboxDataFormatID enumerated type. (The following sections will be expanded when the Inbox/Outbox suite is completed.)

GMDFID_STREAM_GSTRING

GMDFID_VM_GSTRING

GMDFID_TRANSFER_ITEM

GMDFID_TEXT_CHAIN

GMDFID_TEXT_FILE

GMDFID_DOCUMENT

GMDFID_INK

GMDFID_VM_COMPOSITE

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Mailbox Structures & Utilities | Next: 5 Data Drivers


GMDFID_STREAM_GSTRING

The Mailbox Library: 4.1 Data Formats: GMDFID_STREAM_GSTRING

| Up: GEOS SDK TechDocs | Up | Prev: 4 Data Formats | Next: 4.2 GMDFID_VM_GSTRING

In this data type, the message is stored as a GString written to a byte file. This is useful for printing and faxing messages. Applications should make every effort to support this data type. These messages are handled by the GEOS file data driver.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Data Formats | Next: 4.2 GMDFID_VM_GSTRING


GMDFID_VM_GSTRING

The Mailbox Library: 4.2 Data Formats: GMDFID_VM_GSTRING

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 GMDFID_STREAM_GSTRING | Next: 4.3 GMDFID_TRANSFER_ITEM

This is much like STREAM_GSTRING, except that the message is stored in a GString written to a VM Chain. These messages are handled by the GEOS VM-tree data driver.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 GMDFID_STREAM_GSTRING | Next: 4.3 GMDFID_TRANSFER_ITEM


GMDFID_TRANSFER_ITEM

The Mailbox Library: 4.3 Data Formats: GMDFID_TRANSFER_ITEM

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 GMDFID_VM_GSTRING | Next: 4.4 GMDFID_TEXT_CHAIN

This format is used when the user wishes to send the contents of the clipboard as a message. These messages are handled by the GEOS VM-tree data driver.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 GMDFID_VM_GSTRING | Next: 4.4 GMDFID_TEXT_CHAIN


GMDFID_TEXT_CHAIN

The Mailbox Library: 4.4 Data Formats: GMDFID_TEXT_CHAIN

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 GMDFID_TRANSFER_ITEM | Next: 4.5 GMDFID_TEXT_FILE

The message is plain text, written to a VM chain. These messages are handled by the GEOS VM-tree data driver.

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 GMDFID_TRANSFER_ITEM | Next: 4.5 GMDFID_TEXT_FILE


GMDFID_TEXT_FILE

The Mailbox Library: 4.5 Data Formats: GMDFID_TEXT_FILE

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 GMDFID_TEXT_CHAIN | Next: 4.6 GMDFID_DOCUMENT

The message is plain text, written to a byte file. These messages are handled by the GEOS file data driver.

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 GMDFID_TEXT_CHAIN | Next: 4.6 GMDFID_DOCUMENT


GMDFID_DOCUMENT

The Mailbox Library: 4.6 Data Formats: GMDFID_DOCUMENT

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 GMDFID_TEXT_FILE | Next: 4.7 GMDFID_INK

The message is written to some kind of GEOS document with extended attributes and a full GEOS filename. The document's internal format is opaque to the data and transport drivers. These messages are handled by the GEOS file data driver.

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 GMDFID_TEXT_FILE | Next: 4.7 GMDFID_INK


GMDFID_INK

The Mailbox Library: 4.7 Data Formats: GMDFID_INK

| Up: GEOS SDK TechDocs | Up | Prev: 4.6 GMDFID_DOCUMENT | Next: 4.8 GMDFID_VM_COMPOSITE

The message is stored as GEOS ink. These messages are handled by the GEOS VM-tree data driver.

| Up: GEOS SDK TechDocs | Up | Prev: 4.6 GMDFID_DOCUMENT | Next: 4.8 GMDFID_VM_COMPOSITE


GMDFID_VM_COMPOSITE

The Mailbox Library: 4.8 Data Formats: GMDFID_VM_COMPOSITE

| Up: GEOS SDK TechDocs | Up | Prev: 4.7 GMDFID_INK | Next: 5 Data Drivers

The message is passed as a VM tree. The first block of the tree holds an array of pointers to blocks that head up other VM-based data formats, followed by an array of MailboxDataFormat tokens which identifies the format of each chain. These messages are handled by the GEOS VM-tree data driver.

| Up: GEOS SDK TechDocs | Up | Prev: 4.7 GMDFID_INK | Next: 5 Data Drivers


Data Drivers

The Mailbox Library: 5 Data Drivers

| Up: GEOS SDK TechDocs | Up | Prev: 4 Data Formats | Next: 6 Application Responsibilities

A data driver specifies how the data in a transmitted message is arranged. An application that sends a message must provide it in the format required by the data driver. The data driver then translates the message into an appropriate format for the transport driver. When the transport driver receives a message, it determines which data driver can interpret the message, and then calls theMailbox library.

There are a number of default GEOS data types, and developers may write other data drivers of their own to support new data types.

A data driver has to perform a few different tasks:

  • When an application sends a message, the Mailbox library hands the message to the data driver. If the message-data is volatile, the data driver must copy the message's data to a safe place, so the application can free the storage it used to create the message. It gives the Mailbox library an opaque token to identify the message; the mailbox library will use this token when it requests the message for delivery.
  • When the Mailbox library requests it, it must provide message data (one "block" at a time) in a format appropriate for the transport driver.
  • When the Mailbox library receives a message in the driver's format, it must be able to handle data, one "block" at a time, provided by the transport driver in that driver's format; it must be able to assemble this into a message in the data driver's format. (As when transmitting a message, the data driver will give the Mailbox library an opaque token to identify this message.)
  • When it delivers a message, it must provide a reference to the received message in a format the receiving application will be able to understand.
  • The data driver must be able to provide, on request, the size of any message (outgoing or incoming) it's holding.
  • When the Mailbox library informs the data driver that a message has been handled (i.e. an outgoing message has been transmitted, or an incoming message has been deleted by the destination application), the data driver must free its copy of the message, reclaiming the space that message was using.
| Up: GEOS SDK TechDocs | Up | Prev: 4 Data Formats | Next: 6 Application Responsibilities


Application Responsibilities

The Mailbox Library: 6 Application Responsibilities

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 Data Drivers | Next: 7 Examples of Mailbox Usage

The application is insulated from most of the mundane responsibilities of transmitting and receiving data. Its behavior is event-driven. It has to know how to respond to a variety of requests from the Mailbox library; however, it generally does not have to initiate action on its own.

There are a number of specific tasks an application must perform:

  • It must provide the right UI (usually in the form of controllers) for using the Inbox/Outbox functionality. It can do this by including the appropriate controllers.
  • It must package data into an acceptable format. When the user selects data to transmit (which could be a small message or an entire document), it must format the data appropriately.
  • It must be able to handle incoming messages. This consists of notifying the user that new data has been received, incorporating the data into the document (or adding a new document containing the data), delete the message it's been sent, and notify the Mailbox library that it has received the data.

Providing a User-Interface

Sending a Message

Receiving and Processing Messages

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 Data Drivers | Next: 7 Examples of Mailbox Usage


Providing a User-Interface

The Mailbox Library: 6.1 Application Responsibilities: Providing a User-Interface

| Up: GEOS SDK TechDocs | Up | Prev: 6 Application Responsibilities | Next: 6.2 Sending a Message

To use the Mailbox library, an application will usually want to include a MailboxSendControl object in its UI. (Of course, you can create your own UI, but the MailboxSendControl is a convenient package.) Although most applications will use the UI provided by this controller other may want to use the

The settings of the MailboxSendControl will determine what formats the application can use when it sends data. There are a number of standard data types specified by the MailboxSendControl. If you wish to support a new format for transporting data, you must subclass the MailboxSendControl object and add any tools necessary for that new data type. If you wish to provide extra, application specific functionality, you must add tools to the controller yourself. For example, if you are writing a form-collection application, you might wish to add an icon that selects the certain fields of each form, assembles them into a message, and transmits them.

| Up: GEOS SDK TechDocs | Up | Prev: 6 Application Responsibilities | Next: 6.2 Sending a Message


Sending a Message

The Mailbox Library: 6.2 Application Responsibilities: Sending a Message

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 Providing a User-Interface | Next: 6.3 Receiving and Processing Messages

A user will generally send a message by using the application's MailboxSendControl object. This object lets the user send the selected material in the application. It lets the user select such things as the message's destination, transmission time, subject line, etc.

Once the user has selected the message, the MailboxSendControl sends the message MSG_META_MAILBOX_CREATE_MESSAGE to its output object (generally the application object). This message instructs the object to create an appropriate message.

When the application has been notified that it should create a message, it should then determine what data type the message will need. It does this by sending MSG_MAILBOX_SEND_CONTROL_CHOOSE_FORMAT to its MailboxSendControl. It can find out other information about the message to be sent by sending other messages to the MailboxSendControl.

The MailboxSendControl informs the application what data type it should use. The application must determine what data to send (generally the current selection); it then assembles this data into the format requested by the send control object. Finally, it registers the message by sending MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE to its MailboxSendControl. This message instructs the MailboxSendControl to register the message with the Mailbox library.

MSG_META_MAILBOX_CREATE_MESSAGE()

MetaMailboxMessage void MSG_META_MAILBOX_CREATE_MESSAGE(
        optr		sendControl,
        word		transHandle);

This message informs the output of a MailboxSendControl that it should create the body of the message. It is the obligation of that object to notify the controller that it has accomplished this task with MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE.

MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE

MailboxMessage 	MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE(
        word		TransID,
        MailboxStorage		bodyStorage,
        MailboxDataFormat		bodyFormat,
        const void *		bodyRef,
        unsigned word		bodyRefLen,
        MailboxMessageFlags		flags,
        const char *		subject,
        const GeodeToken *		destApp)

When an application wishes to send a message, it does this by registering a message with the Mailbox library. The Mailbox library then loads the appropriate data and transport drivers to actually deliver the message. The application registers a message by sending MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE to its MailboxSendControl object.

Source: The recipient of MSG_META_MAILBOX_CREATE_MESSAGE. Generally sent by the Application object.

Destination: The application's MailboxSendControl.

Parameters: TransID This is the token the MailboxSendControl passed with its MSG_META_MAILBOX_CREATE_MESSAGE .

bodyStorage
The 32-bit token (of type MailboxStorage ) identifying the data driver which should be used. (The message will be passed in a format appropriate to that data driver.)
bodyFormat
The 32-bit token (of type MailboxDataFormat ) identifying the kind of data in the message body. Each format token implies a particular storage type.
bodyRef
A pointer to the beginning of a data block. This block instructs the data driver where the message body is. (Each data driver may have its own conventions for interpreting the data in this block.)
bodyRefLen
The number of bytes at *bodyRef .
flags
A word of MailboxMessageFlags .
subject
A pointer to a null-terminated string containing the subject or summary line describing the message. (The string may contain newline characters.)
destApp
the GeodeToken of the application which will receive the message (on whichever machine receives the message). If this is a generic token, the recipient machine's Mailbox library will automatically convert it to the token of whichever application will actually receive the message.

Interception: If you define a subclass of the MailboxSendControl, you may wish to intercept this message to add functionality.

MSG_META_MAILBOX_MESSAGE_REGISTERED

MetaMailboxMessage void MSG_META_MAILBOX_MESSAGE_REGISTERED(
        MemHamdle		args);

This message is sent by the MailboxSendControl once it has attempted (via MailboxRegisterMessage() ) to register the message. The controller may call MailboxDoneWithVMFile() (or any other cleanup procedures) in response to this message. <

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 Providing a User-Interface | Next: 6.3 Receiving and Processing Messages


Receiving and Processing Messages

The Mailbox Library: 6.3 Application Responsibilities: Receiving and Processing Messages

| Up: GEOS SDK TechDocs | Up | Prev: 6.2 Sending a Message | Next: 7 Examples of Mailbox Usage
MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE, MailboxAcknowledgeMessageReceipt(), MailboxGetSubjectBlock(), MailboxGetSubjectLMem(), MailboxGetMessageFlags(), MailboxGetBodyFormat(), MailboxGetBodyRef(), MailboxDoneWithBody(), MailboxStealBody(), MailboxDeleteMessage()

An application that uses the Inbox/Outbox technology must be able to handle incoming messages, as well as generate outgoing ones.

The Mailbox holds incoming messages in its Inbox until an appropriate time to deliver them. For example, it may choose to wait until the user starts up the application, or until the delivery deadline is reached. In any event, when the Inbox library needs to deliver the message it sends MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE to the application object of an appropriate application. This message has a single argument, a 32-bit MailboxMessage token which uniquely identifies the message.

If the application is going to handle the message, it must call the routine MailboxAcknowledgeMessageReceipt() . The routine is passed the MailboxMessage token for the message. (If you do not acknowledge the receipt of the message, your application will be bothered by repeated notifications.)

Once the application has been notified of an incoming message, it can call a number of routines to find out about the message. To find out the message's subject line, it should call either MailboxGetSubjectLMem() or MailboxGetSubjectBlock() . Both of these routines return the subject text for the message.

MailboxGetSubjectBlock() is passed a single parameter, namely the MailboxMessage token for the message. The routine allocates a global memory block, writes the subject line to that block (as a null-terminated string), and returns the handle of the block. If it fails, it returns zero and sets the thread's error value. (See ThreadGetError() .)

MailboxGetSubjectLMem() is very similar. It is passed two arguments: the MailboxMessage token for the message, and the global memory handle of an LMem heap. The routine allocates a chunk in that LMem heap, writes the subject line to that chunk (again, as a null-terminated string), and returns the chunk handle of that chunk. Again, if the routine is unable to comply, it returns zero and sets the thread's error value.

MailboxGetMessageFlags() is passed the MailboxMessage token for the message. It returns the MailboxMessageFlags for that message. It is often used to find the message's priority.

MailboxGetBodyFormat() is passed the MailboxMessage token for the message. It returns the MailboxDataFormat value for the message's body. The application can use this to decide if the message is written in a format the application can understand.

MailboxGetBodyRef() is used to actually retrieve the data for the message. The routine is passed the following arguments:

  • The MailboxMessage token for the message.
  • A pointer to a data buffer.
  • The size of the buffer, in bytes.

If the routine is successful, it returns true and writes a reference to the message body in the passed buffer. This reference is in the same format that would be passed to MailboxRegisterMessage() , i.e. it varies according to the data driver. If the routine is unsuccessful, it returns false and sets the thread's error value to an appropriate MailboxError value.

For every time the application successfully calls MailboxGetBodyRef() , it must call MailboxDoneWithBody() . This routine is passed three arguments:

  • The MailboxMessage token for the message.
  • A pointer to the data buffer passed to MailboxGetBodyRef() .
  • The size of the buffer, in bytes.

The routine notifies the Mailbox library that the application is done with the message body for the time being. The mailbox library is thus free to close the file holding the data, or take some similar action. (The message body will not be destroyed; it will still be available for future calls to MailboxGetBodyRef() .)

An application can also get the message body by calling MailboxStealBody() . This is passed the same arguments, and returns the same values, as MailboxGetBodyRef() . However, if the routine is successful, the storage area containing the message body will be transferred to the ownership of the application, and the data driver will free any other storage it used to hold the message body. After a successful call to MailboxStealBody() , any future calls to MailboxGetBodyRef() or MailboxStealBody() will fail; as far as the Mailbox library is concerned, the message has no body any more. When the application is finished with the message, it must free the storage used for the message. In particular, if the data reside in a VM file, the application must call MailboxDoneWithVMFile() to notify the Mailbox library to free that file.

When an application is done processing a message, it must call MailboxDeleteMessage() . This routine is passed the MailboxMessage token for the message. It instructs the mailbox library to delete the message and any associated data. The MailboxMessage token becomes invalid after this routine returns.

| Up: GEOS SDK TechDocs | Up | Prev: 6.2 Sending a Message | Next: 7 Examples of Mailbox Usage


Examples of Mailbox Usage

The Mailbox Library: 7 Examples of Mailbox Usage

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Application Responsibilities | Next: 7.1 An Incoming Pager Email Message

It may be helpful to go through a step-by-step example of how a message might be sent, illustrating the function of the transport driver, and how much is done for you. The Mailbox library can be used for a great many different applications, so it's hard to describe a "typical" mailbox transaction; but there are certain elements most mailbox transactions have in common.

We present several different examples of how the Mailbox library might be used. Note that in the following discussion, the behavior of the transport driver (which is opaque to an application developer) is discussed. Unless you are writing a transport driver yourself, you will not need to worry about this.

An Incoming Pager Email Message

Sending and Receiving an Address Book Page

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Application Responsibilities | Next: 7.1 An Incoming Pager Email Message


An Incoming Pager Email Message

The Mailbox Library: 7.1 Examples of Mailbox Usage: An Incoming Pager Email Message

| Up: GEOS SDK TechDocs | Up | Prev: 7 Examples of Mailbox Usage | Next: 7.2 Sending and Receiving an Address Book Page

In this example, we consider a device that's receiving an Email message. The message was generated by some central server, which need not have been running any variety of GEOS, as long as the message conformed to the transport driver's expectations for the message format.

Arrival of The Message

The message is received via a wireless pager card that's plugged into the device. The transport driver for the card, which was loaded when the card was inserted, periodically polls the card to see if new messages have arrived. On the next poll, it finds that there is indeed a new message. It extracts enough of the message to determine it's something for the Email application to see.

Armed with that, it calls the Mailbox library to register a message, specifying:

  • The destination is the Email application (whose token it knows, in the same way it knows the message is for the Email application).
  • The transport mechanism is LOCAL (i.e. the message is destined for an application on this machine).
  • The user should be notified of the arrival of the message, when appropriate. (The priority may be extracted from the pager card's data structure, allowing some Email to be treated as urgent, while other is treated as junk mail)
  • The user should not be asked whether the message should be delivered when the connection is available (i.e. when the application is loaded). That is, when the application is loaded, the message will automatically be presented.
  • The delivery-verb of choice is "Read"; this is used when presenting the message to the user.
  • The storage format token is that for the data driver located on the pager card's file-system.
  • The data format is one the Email application expects. Note that in this case, the pager card data driver will end up converting from the format in which the message is stored on the card to a VM- or file-based format of some sort. In effect, the data driver is pretending the data are stored in the format appropriate to the data format token the pager driver has claimed they're in, when in fact they're in a very different format within the card's own memory.
  • The address of the data (a reference to the data on the card). This address is specific to the passed data driver. When registering a message, you must know the type of address the data driver you're specifying expects (and, of course, provide an address of data in that format).
  • The message data are not volatile, as far as the pager driver is concerned. (Data are volatile if they are in danger of deletion as soon as the Mailbox library returns from the store-message call. The indication of volatility is passed to the data driver so it knows if it should copy the data to a safer location.)
  • The subject line, which it has extracted from the message.
  • A starting time-bound of the current time. This allows the message to be delivered immediately, if the Email application is currently loaded.
  • An ending time-bound of one day from now. When the ending bound time is reached, the application will automatically be loaded and the message presented. The choice of an ending bound is peculiar to the card and what the driver writer thinks is a reasonable deadline for the user to view the message. It may also be intuited from something in the Email message itself, such as a special header in the message.

If the card will delete the message after a certain time, the pager driver will want to set an ending time-bound well before that deletion is scheduled to occur. If the card may delete the message after a while to free up space, as happens in the EMBARC card, the driver may well want to set an ending time-bound of, say, one day from when the message was received. The driver could also set the ending bound to match the starting bound, thus forcing the Email application to be loaded. The final choice, of course, is to set no ending bound.

For its part, the Mailbox library will load the data driver and call it, passing the data address the library was given and allowing the driver to copy the data (if required) and return its own version of the "opaque" data. (The message body is "opaque" to the Mailbox library, in that it never looks into this data for information; instead, it passes the data to the various drivers and applications, which may interpret and manipulate the data.) For example, the file data driver might well copy the file, whose name was passed, into a spool directory for safekeeping, returning the shorter name back to the Mailbox library as the opaque data to store with the message. The rest of the data will be copied into the message descriptor, after checking to make sure it's reasonable, and the descriptor will be placed at the head of the inbox queue.

Assuming the message is given standard First-Class priority and that the Email application isn't currently running in the foreground, the Mailbox library will catch the existence of the new message at its next (user-settable) check interval and will notify the user of its arrival. If the message has a higher priority, the user may be notified immediately, and may be forced to deal with the message immediately, as well. If the Email application is the foreground application, and has told the system it will take care of notifications itself, the message is immediately given to the application, which will presumably notify the user in its own way.

Delivery of The Message

When the delivery box is presented to the user, the user can decide how to proceed with the message.

If the user says to send it, the Mailbox library will establish an IACP connection to the application that is to receive the message and send it an appropriate notification. When the application acknowledges receipt of the message, the message is removed from the Mailbox library's "inbox" and any list displaying the message is updated appropriately.

In essence, the Mailbox library will use IACP to send a MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE to the application object, passing the token for the message.

Using the message token, the application calls the Mailbox library to acknowledge receipt of the message. This acknowledgment will remove the message from the inbox, but will not delete the message descriptor.

The application will then call the Mailbox library back to fetch the body of the message, its subject, time stamp, etc. When the user deletes the message, the Email application will notify the library that it is done with the message. This call will cause the message data and the message descriptor to both be deleted.

| Up: GEOS SDK TechDocs | Up | Prev: 7 Examples of Mailbox Usage | Next: 7.2 Sending and Receiving an Address Book Page


Sending and Receiving an Address Book Page

The Mailbox Library: 7.2 Examples of Mailbox Usage: Sending and Receiving an Address Book Page

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 An Incoming Pager Email Message

This transmission is about as complex as it gets. In it, a user on one GEOS machine transmits an address book page to another user, on another GEOS machine. The transaction is broken into four parts, involving both machines.

Message Set-Up

First, the user selects the page he or she wishes to send. The user selects the means of transport from a Send menu or set of send tools (depending on the system and how the application is set up). The act of going to the page causes the application to notify the message-send controller of the context in which the application finds itself. It is this context that determines what type of data will be placed, by default, in the message body.

The message controller will bring up a dialog displaying the UI for setting transport options, including the address to which the message is to be sent. This UI is obtained by calling the transport driver.

When the user has chosen the address, the controller will send off a MSG_META_MAILBOX_CREATE_MESSAGE to its output, including the 16-bit token bound to the "Current Page" string in the message controller's table.

Having received the message, it becomes the application's responsibility to determine in what formats it can provide the data to be sent. It calls the message controller back, passing an array of data format descriptors that the controller then passes to the transport driver, to see which format it prefers. There will be transport drivers, like the one that uses the Socket library, where any data format will do, so long as the data driver supports the READ_NEXT_BLOCK and WRITE_NEXT_BLOCK calls. Other transport drivers will be more closely coupled with a data driver. For example, the Fax transport driver will require additional information about the fax being sent and will insist on a particular data driver being used.

The transport driver will select one (or none) of the formats and return it to the controller, which will return it to the application. If the transport driver indicates none of the formats is acceptable, the message controller will inform the user that the selected object (page, document, whatever) cannot be sent via the selected medium. The transaction will then be at an end.

Each data format descriptor consists of two 32-bit tokens (composed of the driver's manufacturer ID combined with a 16-bit constant). One token indicates the underlying storage format and refers to a single well-defined data driver. The other token defines the way data are stored in that underlying storage format. From an application's point of view, each data format token can only be used with one particular storage type token. Passing both allows things like the pager driver, described earlier, to be more flexible in how a message is stored. Such flexibility is not needed for the application; as far as it is concerned, a particular data driver is always associated with a particular data format.

If the Mailbox library is asked to load a data driver using a storage type descriptor with which it is not familiar, it will search the appropriate directory, loading each driver it's not seen before and asking it for its storage type token. This allows the work for a non-standard driver to be delayed as long as possible.

For argument's sake, let's say the transport driver indicated that its preferred format is that of a HugeArray with variable-sized elements. The application will ask the Mailbox library for a VM file to use, then convert the page into such an array, presumably placing each field of the address page into its own array element. Any references between the pieces are stored as indices into the array. For example, if the page is made up of the individual entries within the address card, plus a header that contains the names for each entry and a pointer to the entry data, the header must store the array index of the entry data, not a VM block + offset or even a DB group & item. Avoiding reference to individual VM blocks allows the data to be transmitted and stored in arbitrary VM blocks of a data file on the remote side.

The application would then pass a data format descriptor for an address book page stored in a HugeArray, and a storage type descriptor of a VM tree (HugeArrays are based on VM trees).

(If the data are just a stream, without internal pointers to different parts of it, the data can be put into a straight VM chain, rather than a HugeArray. For example, an application that sends just text across would have no need of a HugeArray, but could simply use a VM chain of the text blocks. It would then pass a data format descriptor indicating the thing is an address book page in a VM chain, but the storage type descriptor would still be for a VM tree.)

The matter of which VM file should be used bears a little bit of discussion, as well. If the data that make up the body of a message are already present in the application's document, by all means pass the address to them and mark the body data as volatile (you may not be about to free them, but you also can't guarantee the Mailbox library that they'll be around when the message needs to be sent). If you need to perform any conversion, however, you should use the Mailbox library's utility routines to obtain an appropriate VM file. Then you don't need to mark the body as volatile and the Mailbox library is saved the trouble and time of copying them into its own VM file for safekeeping.

Registering the Message

Having created the appropriate HugeArray, the application then calls the message controller back to finally register the message with the Mailbox library.

The controller will determine the time bounds, and the transport driver. It will call the address controller to fetch the block of data to be stored in the transport descriptor. The application provides the following information in its call to the message controller:

  • The destination is another copy of the same application (thus, it passes its own token).
  • The user should be notified of the arrival of the message, when appropriate. (The message is travelling First-Class.)
  • The storage format is a VM tree.
  • The data format is an address book page in a HugeArray.
  • The location of the data (the VM file & block handle of the HugeArray).
  • The message data are not volatile, as they are stored in a VM file provided by the Mailbox library.
  • The subject line, which is something like "Address book entry for name ", where name is the Name field of the entry.

As for any registered message, the Mailbox library will call the data driver, passing it the HugeArray's address and the data-is-volatile flag. The VM tree data driver will copy the array to a private VM file and return that file's name & block handle for storing in the message descriptor.

After the controller method returns, the application must remember to call the Mailbox library to tell it the application is done using the VM file in which it stored the message body. It must wait until after the message is registered to do this.

Transmitting the Message

Once the message is in the Mailbox library's Outbox, it still needs to be sent to the destination computer. When the message is safely in the outbox, the Mailbox library will put up a confirmation box that allows the user to send the message now or later. Assuming the user chooses to delay the message, the user will (at some point in the future) bring up the outbox control panel and choose to see the messages destined for InfraRed (if InfraRed is the only transport with messages stored for it, InfraRed will be brought up automatically). The user will then click the Beam All trigger in the main control panel. This will prompt the Mailbox library to load the InfraRed transport driver and tell it to create a connection to the address stored in the message.

If the connection is successful, the Mailbox library will instruct the transport driver to send each message in turn, passing in the message descriptor. The transport driver, having connected to the well-known mailbox port on the remote machine, will send across the message descriptor, indicating the data format and the destination application, followed by the blocks of the message. The data blocks are obtained by calling the data driver for the message.

On the receiving side, the receipt of the first bytes of the connection request will cause the socket driver for the InfraRed medium to be loaded. When the link is established and an attempt is made to connect to the mailbox socket, the InfraRed transport driver will be loaded to handle the connection. The transport driver will receive each block from the Socket library. The first block will allow it to load the appropriate data driver, which it will use to store the subsequent blocks. If it is unable to load the requisite data driver, it will reject the message and the sending user will be told why.

When all the data blocks have been received, the data driver will be told the message is complete, at which point it will attempt to write the data to disk (it may also have been writing it to disk block by block, or in batches of blocks if the message is particularly large; the driver is allowed to return an error from any block write call and the transport driver will call the data driver to delete what it had received so far and will discard any remaining blocks in this message as they are received, eventually returning an error to the sending transport driver). If this write fails (due to lack of disk space, for example), the receiving transport driver will return an error, which will be returned back to the Mailbox library on the sending machine. The user will be notified and the message will remain in the outbox.

Following the commitment of the message data, the destination machine's transport driver will store the message in the inbox. Again, if the descriptor cannot be committed to disk, an error will propagate back to the sending Mailbox library. Only when the message descriptor is safely stored will the remote transport driver indicate the message has been successfully transmitted.

When the receiving machine has accepted the message, the sending machine will delete the message from its outbox, freeing the message data, etc.

Delivering the Message

Once the message is stored in the inbox, it progresses exactly as for the Email message, except it's the Address Book application that will receive the data.

Depending on the priority of the message, the Mailbox library will, sooner or later, notify the user that the message has arrived. If the user chooses not to store the card right then, he will be asked again when the Address Book is next launched or another message arrives for it.

When the user gives the go-ahead for delivering the message, the application is notified, via IACP. When the application has acknowledged receipt of the message, the descriptor is removed from the inbox (though not from the file).

The application will call the Mailbox library to obtain the data format of the message body. If it can accept that format, the application will call the library again, telling it to ask the data driver for the appropriate reference to the data.

Armed with this reference, the application can then act on the message. The message body should be treated as read-only and not be modified by the application in any way.

When it's done with the message, it calls the library again to say it's done with the message (this call is passed on to the data driver to do with as it sees fit).

If the application has no further use for the message descriptor, it will call the Mailbox library to say the message should be deleted.

If the application is unable to understand the data format of the message body, it is responsible for informing the user and deleting the message.

In this case, the Address Book will learn the data are a HugeArray-based address book page, which it understands. It will ask for and receive the VM file and block handle of the HugeArray, extract the relevant pieces and merge them into the address book. In all likelihood, the application will want to confirm with the user that she really wants to do this (especially if it will be overwriting an existing entry) and to do it in the current document. If the user indicates she would rather place the record in another document, the application is free to open the other document; the Mailbox library sets no time limits on how long an application may keep a delivered message active.

Having applied the change (or not), the Address Book will tell the library it's done with the data and to delete the message.

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 An Incoming Pager Email Message


PCCom Library

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 PCCom Library Abilities
| One-page Version of this Chapter for Easy Printing

The PCCom library provides a simple way to allow a geode to provide and monitor a PCCom connection. If you are familiar with the SDK, you probably think of pccom as a tool which allows the target machine to receive commands from the host machine. While the target machine runs the pccom tool, the host machine can upload and download files and otherwise manipulate the target machine.

The PCCom library allows a geode to start up a PCCom thread monitoring a serial port for purposes other than debugging. For instance, it allows the GEOS machine to receive files sent over a serial line by another machine running pcsend or another program using pccom's file transfer protocol. This allows file transfers and other pccom operations to go on in the background while the user continues to interact with GEOS.

If you're not familiar with the pccom tool, you should probably read the pccom section of the Using Tools chapter--perhaps not the whole section, but at least enough to understand basic usage.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 PCCom Library Abilities
| One-page Version of this Chapter for Easy Printing


PCCom Library Abilities

PCCom Library: 1 PCCom Library Abilities

| Up: GEOS SDK TechDocs | Up | Prev: PCCom Library | Next: 2 What To Do

Your geode needn't do much to support PCCom. The PCCom library acts as a passive pccom machine--it will only accept orders from a remote machine.

All you need to do to support PCCom is start up a PCCom process, ideally freeing it when done. Geodes using the PCCom library have the option of receiving notification when pccom would display text.

| Up: GEOS SDK TechDocs | Up | Prev: PCCom Library | Next: 2 What To Do


What To Do

PCCom Library: 2 What To Do

| Up: GEOS SDK TechDocs | Up | Prev: 1 PCCom Library Abilities | Next: 3 Staying Informed
PCCOMINIT(), PCCOMEXIT(), PCCOMABORT()

When you are ready to start monitoring a serial port for pccom-style communications, call PCCOMINIT(). This routine is an entry point for the library, accessible via ProcGetLibraryEntry () as the first entry point in the library, and it may also be called as a normal routine. PCCOMINIT() starts up a new thread which will monitor the passed serial port. If it cannot make the connection, it will return an error.

When you are done with PCCom, call PCCOMEXIT(), invocable as a library entry point or as a normal routine, which closes down the monitor thread and frees the serial port for other uses. After calling this routine, you must call PCCOMINIT() again if you wish to re-establish the pccom connection.

You may call the PCCOMABORT() routine at any time; this routine aborts any pccom file transfer that may be in progress, but leaves the PCCom connection intact.

| Up: GEOS SDK TechDocs | Up | Prev: 1 PCCom Library Abilities | Next: 3 Staying Informed


Staying Informed

PCCom Library: 3 Staying Informed

| Up: GEOS SDK TechDocs | Up | Prev: 2 What To Do

The sections above tell you everything you need to let your geode interact with pccom. It is possible to do more: your geode can receive notification whenever the pccom tool would display some text. The pccom tool displays text to show the user what's going on. Text signals the successful or unsuccessful completion of certain operations; a spinning baton shows that a file transfer is in progress. Your geode can also find out if the machine on the other side of the pccom link has quit pccom.

When calling PCCOMINIT(), your geode can specify an object which should receive notification when pccom has text to display or senses that the other side of the pccom link has quit. If an object is so specified, it will receive notification messages at these times. You must also set the PCComInitFlags argument to PCCOMINIT() such that the appropriate kinds of notification will be sent; there is one flag which asks for display text notification and another flag which asks for notification when the other side of the pccom connection exits.

Notification will come in the form of MSG_META_NOTIFY or MSG_META_NOTIFY_WITH_DATA_BLOCK . There are three possible forms of notification:

MSG_META_NOTIFY: GWNT_PCCOM_DISPLAY_CHAR
If the passed notification type is GWNT_PCCOM_DISPLAY_CHAR, then the notification's data word contains a character that pccom would display.
MSG_META_NOTIFY_WITH_DATA_BLOCK : GWNT_PCCOM_DISPLAY_STRING
If the passed notification type is GWNT_PCCOM_DISPLAY_STRING, then the data in the notification's data block is a string of characters.
MSG_META_NOTIFY : GWNT_PCCOM_EXIT_PCCOM
If the passed notification type is GWNT_PCCOM_EXIT_PCCOM, then the pccom process on the other side of the pccom connection has exited; the notification's data word contains a PCComReturnType indicating the success the other side had in exiting. The other side of the connection signals that it is exiting by sending an EX escape code over the pccom connection. A data value of PCCRT_NO_ERROR is a sign of a successful exit; other return values might signal that the exit was the result of an error.
| Up: GEOS SDK TechDocs | Up | Prev: 2 What To Do


The Spool Library

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Introduction to Printing
| One-page Version of this Chapter for Easy Printing

Any geode can work with printers, fax machines, and other print devices by including the spool library and instantiating a PrintControl object to handle system interactions. The PrintControlClass provides the printing interface. It includes both user interface and an interface between the geode and the printing thread. There is also a PageSizeControlClass which provides UI concerning page layout.

Before reading this chapter, you should be familiar with the graphics system.

1 Introduction to Printing
2 Simple Printing Example
3 How Jobs Get Printed
3.1 Printing System Components
3.2 Chronology
4 Print Control Instance Data
4.1 Alerting the GenApplication
4.2 Attributes
4.3 Page Range Information
4.4 Document Size
4.5 Print Output Object
4.6 Document Name Output
4.7 The Default Printer
4.8 Adding UI Gadgetry
5 Print Control Messages
5.1 Common Response Messages
5.2 Flow of Control Messages
5.3 Working with Instance Data
6 Page Size Control
7 Other Printing Components
7.1 Spooler and Scheduling
7.2 Printer Drivers
7.3 Page Size Related Routines
8 Debugging Tips

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Introduction to Printing
| One-page Version of this Chapter for Easy Printing


Introduction to Printing

The Spool Library: 1 Introduction to Printing

| Up: GEOS SDK TechDocs | Up | Prev: The Spool Library | Next: 2 Simple Printing Example

By including a PrintControl object, an application gains access to the powers of the GEOS printing system. Below are several of the features built into GEOS:

  • Single Imaging Model
    You'll use the same commands to describe print jobs as you did for screen drawings. These commands work for all supported printers. Thus, anything you can draw can be printed, and the application doesn't have to worry about what model of printer is being used.
  • Background Printing
    Background printing allows users to continue working while their documents print. This is an example of multitasking at work. A process known as the Spooler spawns a separate thread for each active printer. Since each printer's thread priority is low, printing will take place in the background, interfering little with the user's interactions. If you want printing to take place in the foreground, your application can boost the priority of a printer thread.
  • Standard UI Components
    The PrintControl is a full-featured control object and it provides a UI mechanism for finding out what a user wants to print. For user choices which depend on the printer (for example, some printers have no low-quality mode), the PrintControl will take printer abilities into consideration.
  • Control Over Scheduling
    The Spooler normally handles jobs in FIFO (first in, first out) order. Geodes can exercise a great deal of control over the Spooler's scheduling of jobs. Any scheduling options the user might request using the Printer Control Panel, the application can request using spool routines.
  • Managing the Printer Yourself
    If you can't or won't use standard GEOS commands to describe a print job, you can use Raw Mode to send a packet of commands in the printer's own language. For example, you could send escape codes or commands from a page description language. Since this is a standard printing mode, you still benefit from all the usual spool scheduling features.
  • Management of Multiple Printers
    The user may have multiple printing devices hooked up and installed at one time. The system handles this situation intelligently: the Spooler maintains a separate queue for each serial or parallel port. Thus, if two printers are installed on two different ports, both may print at once. If two or more printers are installed on the same port then they share a queue. Thus, jobs for one printer won't try to print at the same time as those for another on the same port. To make sure the job prints to the correct printer, the Spooler will put up a dialog box advising the user to put the correct device on line.
| Up: GEOS SDK TechDocs | Up | Prev: The Spool Library | Next: 2 Simple Printing Example


Simple Printing Example

The Spool Library: 2 Simple Printing Example

| Up: GEOS SDK TechDocs | Up | Prev: 1 Introduction to Printing | Next: 3 How Jobs Get Printed

Adding printing capability to an application is fairly simple, especially if it will be printing WYSIWYG. Somewhere in the application there is probably a message handler which is in charge of drawing the document. By including a Print Control object and writing handlers to some standard messages, it is a simple manner to redirect the document drawing commands to a printer.

Code Display 14-1 Printing Example

@include <spool.goh>
@object GenApplicationClass MyApp = {
    /* ...Much other GenApplication instance data omitted... */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) =
         @MyPrintControl;
    ATTR_GEN_APPLICATION_PRINT_CONTROL = @MyPrintControl;
}
@object PrintControlClass MyPrintControl = {
/* The PrintControlClass is a subclass of GenControlClass. Its "features"
 * correspond to the "Print" and "Fax" triggers in the File menu. Its instance
 * data contains information about the document to be printed, and links to
 * objects which will provide information. */
/* Single page documents would leave out page range UI from the Print dialog:
    PCI_attrs = 		(@default & ~(PCA_PAGE_CONTROLS| PCA_VERIFY_PRINT));
    PCI_startUserPage = 1;
    PCI_endUserPage = 1 */
/* The PCI_output, or Print Output object, is in charge of supplying the graphics
 * commands describing the print job whenever the user wants to print. This object
 * is expected to handle a MSG_PRINT_CONTROL_START_PRINTING. */
 * The print output object is normally either the model, target or process. */
    PCI_output = 		( TO_APP_TARGET ) ;
/* The PCI_docNameOutput object is supposed to provide the name of the document
 * on demand. This name is used to identify the document to the user in the Print
 * Control Panel. If this object is a GenDocumentGroup, it automatically does this.
 * If the object is not a GenDocumentGroup object, then * it must have a handler
 * for MSG_PRINT_GET_DOC_NAME that sends a nmae to the PrintControl via
 * MSG_PRINT_CONTROL_SET_DOC_NAME. */
    PCI_docNameOutput = MyGenDocumentGroup;
/* The PCI_docSize field specifies the size of the document to print (not to
 * be confused with the size of the printer's paper). */
/* Many simple applications only support one document size,
 * and may specify it in the PrintControl and never bother with it again.
 * We're setting the margins to all zero because they'll be updated via
 * MSG_PRINT_CONTROL_GET_MARGINS. */
    PCI_docSizeInfo = {			(15/2*72),
                (19/2*72),
                0,
                {0, 0, 0, 0}}; */
/* Many simple applications do not support multiple documents. These applications
 * should set the GS_ENABLED flag of the PrintControl's GI_states field. */
}
@method MVTStartPrinting, MyVisTargetedClass, MSG_PRINT_START_PRINTING
/* We've set up our Print Control to send this message to the Target, so whatever
 * object will have the target when the user wants to print needs a handler for
 * this message. We could have easily have put the Model or process in charge
 * of printing, changing PCI_output accordingly. It is often convenient to use
 * the same object to handle drawing to screen and to the printer. */
/* Arguments:		optr 		printControlOD,
        GStateHandle		gstate */
{
    PCMarginParams 	margins;
/*
 * 	Applications which allow the user to change the document size may handle
 *	the situation in more than one way. If the user has changed the page setup
 *	by working with a PageSizeControl, then the application has probably
 * 	already been alerted to the page size. If the application has its own
 *	way of computing document size, it should use it. Such applications should
 * 	send MSG_PRINT_CONTROL_SET_DOC_SIZE and MSG_PRINT_CONTROL_SET_DOC_MARGINS
 * 	to the PrintControl, either in this handler or else whenever the page size
 *	changes. Either time is fine, just so long as the document size is set
 * 	correctly by the time this MSG_PRINT_START_PRINTING handler is finished.
 *	Applications	 supporting only a single document size should probably set
 *	the size (and margins) in the PrintControl's instance data, as shown
 *	above. It is also possible to send a MSG_PRINT_CONTROL_SET_DOC_SIZE on
 *	every print, but if the size never changes, this is a bit wasteful.
 *	This application supports only one size of document. If the document
 *	doesn't fit on the page, the spooler will tile it onto multiple pages
 *	as necessary. It uses the printer's margins as the document margins: */
    @call 	MyPrintControl::MSG_PRINT_CONTROL_GET_PRINTER_MARGINS(
        &margins, 		/* Fill in structure with printer's margins */
        TRUE);		/* ...and automatically use printer margins
                 * as document margins */
    @call 	self::MSG_VIS_DRAW(DF_PRINT, gstate);
    /* If the MSG_VIS_DRAW handler didn't end with a form feed, put one in:
    GrNewPage(gstate, PEC_FORM_FEED); */
    @send 	MyPrintControl::MSG_PRINT_CONTROL_PRINTING_COMPLETED();
}
@method MVTDraw, MyVisTargetClass, MSG_VIS_DRAW
/* Chances are, whatever object this is has some sort of draw handler already.
 * However, if you're building up this class from scratch, you'll be glad to
 * know that commands of the following form work just as well drawing in
 * response to a print request as they do drawing anything else: */
{	GrSetLineColor(gstate, CF_INDEX, C_RED, 0, 0);
    GrDrawLine(gstate, 144, 144, 288, 288);
    GrNewPage(gstate, PEC_FORM_FEED);
}
| Up: GEOS SDK TechDocs | Up | Prev: 1 Introduction to Printing | Next: 3 How Jobs Get Printed


How Jobs Get Printed

The Spool Library: 3 How Jobs Get Printed

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Simple Printing Example | Next: 4 Print Control Instance Data

You may wonder how and when these various features get implemented. Simply stated, the application describes the print job, which gets placed onto a queue where it remains until it is fed to a printer. For many programmers, that's enough to know. If you want a more complete explanation, read on.

Printing System Components

Chronology

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Simple Printing Example | Next: 4 Print Control Instance Data


Printing System Components

The Spool Library: 3.1 How Jobs Get Printed: Printing System Components

| Up: GEOS SDK TechDocs | Up | Prev: 3 How Jobs Get Printed | Next: 3.2 Chronology

Printing involves the concerted effort of several objects running in different threads. The most important pieces of the printing system are

PrintControl
The printing system is big and would be difficult to interact with if not for the Print Control, which acts as a sort of intermediary. This object is a member of the PrintControlClass , a GenControl subclass provided by the spool library. It handles the UI, communication with the spool thread, and most other printing functions. Every geode which is going to print includes its own Print Control, placing the control object in its generic tree.
Print Output
The Print Output is an object (often the process object) chosen by the geode when creating the Print Control. This object is in charge of building print jobs and must be prepared to provide page descriptions whenever it receives a MSG_PRINT_START_PRINTING from the Print Control.
Spooler
The Spooler handles most of the important "behind the scenes" work of printing. As mentioned, it handles the scheduling of jobs and manages the operations by which generic print jobs are translated for specific printers.
Printer Driver
GEOS printer drivers handle the back end of printing. GEOS has many printer drivers, each of which serves one or more models of printer. The driver supplies the Spooler with information about the printer and makes some final adjustments to the translated print job to get it ready for the specific printer.
| Up: GEOS SDK TechDocs | Up | Prev: 3 How Jobs Get Printed | Next: 3.2 Chronology


Chronology

The Spool Library: 3.2 How Jobs Get Printed: Chronology

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Printing System Components | Next: 4 Print Control Instance Data

Now that you're somewhat familiar with these components, you're ready for the detailed account of how they work together to print a job. Note that the application is only involved at one point. The printing system takes care of the rest automatically.

  1. User Activates Trigger
     Printing normally begins when the user activates the User Print Trigger provided by the Print Control. This trigger sends a message to the Print Control telling it to put up the Print Dialog box.
  • User Interacts with the Print Dialog Box
     The Print Control responds to the Print Trigger's message by presenting a dialog box in which the user selects such things as page range and print quality (see. The user then either initiates printing or cancels. If the user wants to print, he'll confirm this by clicking on the appropriate trigger. The application will then get a chance to veto the user's choices. The Print Control asks the dialog box to remove itself, and it then gets ready for the upcoming print job.
  • Print Control Prepares for New Job
     Soon the Print Control is going to ask for the page descriptions, but first it needs a place to store the data. It creates a spool file in which to store the upcoming graphics commands. It also allocates a GString handle by which the Print Output can transmit the commands. It then sends a message to the application's Print Output, signalling that the Print Output should supply the job.
  • Application Supplies Job
     Having received the Print Control's message, the Print Output supplies graphics routines to the provided GString handle, using the same commands as when printing to any other GState. This string of graphics commands ends with a message saying the job is completed. The Print Control supplies some extra information to the spooler, and the print job is ready to enter the queue.
  • Job Traverses Queue
     At this point, the spool library places the print job on a printer queue where the job waits to be printed. (Actually, the Spooler uses a trick to save on overhead. If there are no jobs for a printer, the Spooler doesn't maintain a queue for that printer. When a new job comes in for a printer that has no queue so far, a new queue is prepared for that printer and the job placed on the new queue. When the last job of a queue is printed and there are no more jobs forthcoming, the queue is removed.) The Spooler's various scheduling powers, if exercised, affect jobs in the queue.
  • Printing
     Eventually, the job reaches the head of the queue. It is now ready to print. The Spooler works with the Printer Driver to transform the print job into a form that the printer can work with. This may involve building a bitmap depicting the job, or it might involve translating the GString's commands into the commands of some other page description language, such as PostScript.
  • Hard Copy
     The printer driver sends the appropriate printer commands to the printer. If the printer doesn't have sufficient memory to hold the whole job, the driver sends only part of the job at a time.
| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Printing System Components | Next: 4 Print Control Instance Data


Print Control Instance Data

The Spool Library: 4 Print Control Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 How Jobs Get Printed | Next: 5 Print Control Messages

The PrintControl is a powerful and adaptable subclass of GenControl and contains many instance fields to fit the needs of different types of applications. Each field is described in greater detail later in this section; all are shown in Print Control Instance Data and Features .

Code Display 14-2 Print Control Instance Data and Features

        /* The following bitfield contains the PrintControl's attributes */
    @instance PrintControlAttrs 		PCI_attrs =
                (PCA_COPY_CONTROLS | PCA_PAGE_CONTROLS |
                 PCA_QUALITY_CONTROLS | PCA_USES_DIALOG_BOX |
                 PCA_GRAPHICS_MODE | PCA_TEXT_MODE );
        /* Possible PCI_attrs flags (may be combined using | and &):
         * PCA_MARK_APP_BUSY, 				PCA_VERIFY_PRINT,
         * PCA_SHOW_PROGRESS, 				PCA_PROGRESS_PERCENT,
         * PCA_PROGRESS_PAGE, 				PCA_FORCE_ROTATION
         * PCA_COPY_CONTROLS,				PCA_PAGE_CONTROLS,
         * PCA_QUALITY_CONTROLS, 				PCA_USES_DIALOG_BOX,
         * PCA_GRAPHICS_MODE,				PCA_TEXT_MODE
         * PCA_DEFAULT_QUALITY						*/
        /* The fields below are the complete and requested page ranges */
    @instance word 		PCI_startPage = 1;
    @instance word 		PCI_endPage = 1;
    @instance word 		PCI_startUserPage = 0;
    @instance word 		PCI_endUserPage = 0x7fff;
        /* The following field contains the default printer number */
    @instance word 		PCI_defPrinter = -1;
        /* The following fields deal with document dimensions */
    @instance PageSizeReport 		PCI_docSizeInfo = 0;
        /* Pointers to objects receiving vital messages */
    @instance optr 		PCI_output;
    @instance optr 		PCI_docNameOutput;
        /* The PrintControl's features determine whether to use standard
         * print and fax triggers to bring up the Print Dialog box. */
      typedef ByteFlags PrintControlFeatures;
    /* The following flags may be combined with | and &:
         PRINTCF_PRINT_TRIGGER,
         PRINTCF_FAX_TRIGGER */
    /* To provide a non-standard print trigger, use the GenControl vardata
     * field ATTR_GEN_CONTROL_APP_UI. */
    typedef ByteFlags PrintControlToolboxFeatures;
    /* The following flags may be combined with | and &:
        PRINTCTF_PRINT_TRIGGER
        PRINTCTF_FAX_TRIGGER */
    /* To include application-specific UI in the print dialog box. */
    @vardata optr ATTR_PRINT_CONTROL_APP_UI;
    /* This piece of temporary vardata is internal: */
    @vardata TempPrintCtrlInstance  TEMP_PRINT_CONTROL_INSTANCE;
    /* Its structures are defined as follows:
    typedef struct {
     optr 			TPCI_currentSummons; ( currently active summons )
     optr 			TPCI_progressBox; ( OD of progress dialog box */
     ChunkHandle 			TPCI_jobParHandle; ( handle to JobParamters )
     word 			TPCI_fileHandle; ( file handle (if printing) )
     word 			TPCI_gstringHandle; ( gstring handle if printing )
     word 			TPCI_printBlockHan; ( the printer block handle )
     PrintControlAttrs 			TPCI_attrs;
     PrintStatusFlags 			TPCI_status;
     byte 			 TPCI_holdUpCompletionCount;
    }  TempPrintCtrlInstance;
    typedef ByteFlags  PrintStatusFlags;
    #define  PSF_FAX_AVAILABLE 0x80 ( set if a fax driver is available )
    #define PSF_ABORT 0x08 ( user wants to abort printing )
    #define PSF_RECEIVED_COMPLETED 0x04 ( MSG_..._PRINTING_COMPLETED received )
    #define PSF_RECEIVED_NAME 0x02 ( MSG_PC_SET_DOC_NAME received )
    #define PSF_VERIFIED 0x01 ( PSG_PC_VERIFY_? received ) */
    @vardata TempPrintCompletionEventData  TEMP_PRINT_COMPLETION_EVENT;
    /* The TempPrintCompletionEventData structure is defined:
        typedef struct {
         MemHandle 		 TPCED_event;
         MessageFlags 		TPCED_messageFlags;
        }  TempPrintCompletionEventData;

Alerting the GenApplication

Attributes

Page Range Information

Document Size

Print Output Object

Document Name Output

The Default Printer

Adding UI Gadgetry

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 How Jobs Get Printed | Next: 5 Print Control Messages


Alerting the GenApplication

The Spool Library: 4.1 Print Control Instance Data: Alerting the GenApplication

| Up: GEOS SDK TechDocs | Up | Prev: 4 Print Control Instance Data | Next: 4.2 Attributes

PrintControl objects should be placed on the GenApplication's MANUFACTURER_ID_GEOWORKS/GAGCNLT_SELF_LOAD_OPTIONS general change notification list. Also, if the application is to allow printing of documents from the file from the file manager, then the PrintControl's optr should be specified in the GenApplication's ATTR_GEN_APPLICATION_PRINT_CONTROL field.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Print Control Instance Data | Next: 4.2 Attributes


Attributes

The Spool Library: 4.2 Print Control Instance Data: Attributes

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Alerting the GenApplication | Next: 4.3 Page Range Information
PCI_attrs, MSG_PRINT_CONTROL_SET_ATTRS, MSG_PRINT_CONTROL_GET_ATTRS

The Print Control includes several attributes which are grouped together into a record of type PrintControlAttrs . These represent some choices made when instantiating the Print Control.

PCA_MARK_APP_BUSY
Describing large print jobs can take a fair amount of time. If this bit is set, the application will reassure the user that it is busy (by showing the busy cursor) while spooling long print jobs.
PCA_VERIFY_PRINT
If this bit is set, the Print Control will ask the application for confirmation before printing anything. This request will come in the form of a MSG_PRINT_VERIFY_PRINT_REQUEST sent to the PCI_output object. The Print Control sends this message after the user has finished interacting with the Print dialog box so that the application can make sure that the user has made valid choices.
PCA_SHOW_PROGRESS, PCA_PROGRESS_PERCENT, PCA_PROGRESS_PAGE
When describing a large print job, it is considerate to let the user know when the application is making some kind of progress. If the first of these bits is set, the Print Control will display a progress dialog box when spooling a print job. The next two bits determine whether progress will be reported as a percentage, number of pages completed, both, or neither. Regardless of whether percents or page number progress is reported, the progress box can display an application-supplied string.
PCA_FORCE_ROTATION
If this bit is set, the output will automatically be printed rotated on the page. Normally, this bit is not set so the Spooler will make a choice to make the job fit on the minimum number of pages. The Banner program sets this bit to make sure that fanfold paper printers will create continuous banners; if it didn't print sideways, then users would have to use a lot of tape to put their banners together (they have to anyhow, if using non continuous-feed paper).
PCA_COPY_CONTROLS
This flag determines whether the Print Control will allow the user to request that multiple copies be printed. If your application uses this option, then it is vital that the print control be updated whenever the total page count of the document changes.
PCA_PAGE_CONTROLS
If this bit is set, the user will be allowed to select specific page ranges to print. Even if it seems like users would have little reason to print short ranges, it's still nice to include this option: if the printer messes up one page, it's usually irritating to have to reprint the whole document.
PCA_QUALITY_CONTROLS
If this flag is on, the user will be allowed to select which quality to print with. Note that not all printers can print at all qualities, but most support more than one mode.
PCA_USES_DIALOG_BOX
This bit determines whether the Print Control will display a dialog box containing printing choices for the user. Specialty geodes and GCM applications might want to turn this flag off; it is set by default.
PCA_GRAPHICS_MODE, PCA_TEXT_MODE
These bits determine whether the Control should support graphic mode and/or text mode output.
PCA_DEFAULT_QUALITY
These two bits contain the default print quality to use. The enumerated type PrintQualityEnum defines the values available: PQT_HIGH, PQT_MEDIUM, and PQT_LOW.

Those attributes which are on by default are PCA_COPY_CONTROLS, PCA_PAGE_CONTROLS, PCA_QUALITY_CONTROLS, PCA_USES_DIALOG_BOX, PCA_GRAPHICS_OUTPUT, and PCA_TEXT_OUTPUT.

There are messages to get and set these attributes.

MSG_PRINT_CONTROL_SET_ATTRS

void	MSG_PRINT_CONTROL_SET_ATTRS(
        PrintControlAttrs 	attributes);

Use this message to change the values stored in the PCI_attrs structure. Pass a record of type PrintControlAttrs containing the desired values.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: attributes The new PrintControlAttrs .

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_ATTRS

PrintControlAttrs 	MSG_PRINT_CONTROL_GET_ATTRS();

Use this message to retrieve the values stored in the PCI_attrs structure. It will return a record of type PrintControlAttrs containing the desired values.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: The present print control attributes.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Alerting the GenApplication | Next: 4.3 Page Range Information


Page Range Information

The Spool Library: 4.3 Print Control Instance Data: Page Range Information

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Attributes | Next: 4.4 Document Size
PCI_startPage, PCI_endPage, PCI_startUserPage, PCI_endUserPage, MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE, MSG_PRINT_CONTROL_GET_TOTAL_PAGE_RANGE, MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE, MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE

The PCI_startPage and PCI_endPage fields should contain the page numbers of the first and last pages possible to print, known as the total page range. The PCI_startUserPage and PCI_endUserPage contain the beginning and ending page numbers of the range the user wants to print, often called the user page range.

For documents whose page length may change (such as word processor documents), the page ranges should be updated whenever the Print dialog box goes up, at which time the application will be receiving a MSG_PRINT_NOTIFY_PRINT_DB . Normally, the user page range will start out being the same as the total page range. For most documents the first page of the total page range is page one, but applications which will include cover sheets might want the cover sheet to be page zero.

MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE

void	MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE(
        int 	firstPage,
        int	lastPage);

The application should send this message to set the first and last page numbers of the document. You might want to send this message every time the document length changes or just every time the Print dialog box is put up. The Print Control warns the PCI_output object of the occurrence of the latter event with a MSG_PRINT_NOTIFY_PRINT_DB .

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: firstPage The page number of the document's first page.

lastPage
The page number of the document's last page.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_TOTAL_PAGE_RANGE

dword	MSG_PRINT_CONTROL_GET_TOTAL_PAGE_RANGE();

This message returns two integers. These integers are the numbers of the first and last pages of the range of possible pages. These values aren't necessarily the first and last pages of the range the user wants to print. Use MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE for that information.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: A double word. The high word is the first page; the low word is the last page.

Interception: Unlikely.

MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE

void	MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE(
        int 	firstPage,
        int	lastPage);

This message takes the passed values and uses them as the first and last pages that the user wants to print.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: firstPage The page number of the first page to print.

lastPage
The page number of the last page to print.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE

dword	MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE();

This message returns the user's selected range of pages to print.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: A double word. The high word is the first page which the user wishes to print; the low word is the last page of this range.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Attributes | Next: 4.4 Document Size


Document Size

The Spool Library: 4.4 Print Control Instance Data: Document Size

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Page Range Information | Next: 4.5 Print Output Object
 PCI_docSizeInfo, MSG_PRINT_CONTROL_SET_DOC_SIZE, MSG_PRINT_CONTROL_GET_DOC_SIZE, MSG_PRINT_CONTROL_SET_DOC_MARGINS, MSG_PRINT_CONTROL_GET_DOC_MARGINS, MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE, MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE, MSG_PRINT_CONTROL_SET_DOC_SIZE_INFO, MSG_PRINT_CONTROL_GET_DOC_SIZE_INFO

It is possible to specify the size of the document when creating the Print Control. Note that this is the size of the document, not the size of the piece of paper. Ideally, the document should fit on the paper, though obviously in the case of huge documents like some spreadsheets, this may not be the case. The document size includes the margin size; it is possible to set the margin size as well.

The document size must be set correctly before the document is finished printing. If all documents the application produces have the same dimensions (or if you want some size to be the default), you may specify dimensions for the document when instantiating the print control. You may also set up document margins at this time.

The messages listed above get and set the document and margin sizes. You must use MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE and MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE when working with the dimensions of 32-bit extended documents.

MSG_PRINT_CONTROL_SET_DOC_SIZE

void	MSG_PRINT_CONTROL_SET_DOC_SIZE(
        int 	width,
        int	height);

This message changes the values of the document size. It takes two integers, representing the new width and height for the document to use. It can only use 16-bit values, so use MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE when working with 32-bit extended graphics spaces.

Remember that the document size includes the document margins.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: width The document's width, in points.

height
The document's height, in points.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_DOC_SIZE

dword	MSG_PRINT_CONTROL_GET_DOC_SIZE();

Use this message to retrieve the present document size. The size is returned as a width and height, each expressed in points.

Note that if the size might be a 32-bit value (which might happen if the document uses an extended graphics space), you must use the MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE . If either dimension is a 32-bit number, using a regular MSG_PRINT_CONTROL_GET_DOC_SIZE will result in an error.

Remember that the document size includes the document margins.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: A double word. The high word is the width, the low word is the height.

Interception: Unlikely.

MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE

void	MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE(
        PCDocSizeParams 	*ptr);

This message changes the values of the document size using the two passed double integers as the new width and height to use. When working with normal 16 bit graphics spaces, use MSG_PRINT_CONTROL_SET_DOC_SIZE instead.

Remember that the document size includes the document margins.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to a PCDocSizeParams structure containing the document size.

Return: Nothing.

Interception: Unlikely.

Structures: The PCDocSizeParams structure has the following definition:

typedef struct {
    dword		PCDSP_width;
    dword		PCDSP_height;
} PCDocSizeParams;

MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE

void	MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE(
        PCDocSizeParams 	*ptr);

Use this message to retrieve the present document size. It returns two double integers representing the width and height, expressed in points. If the size is a 16 bit value, you can use MSG_PRINT_CONTROL_GET_DOC_SIZE instead.

Remember that the document size includes the document margins.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to a PCDocSizeParams structure to hold document parameters.

Return: Nothing is returned explicitly.

ptr
The structure is filled with document size.

Interception: Unlikely.

Structures: The PCDocSizeParams structure has the following definition:

typedef struct {
    dword		PCDSP_width;
    dword		PCDSP_height;
} PCDocSizeParams;

MSG_PRINT_CONTROL_SET_DOC_MARGINS

void	MSG_PRINT_CONTROL_SET_DOC_MARGINS(
        PCMarginParams 	*ptr);

Use this message to set new values for the document margins. It takes four arguments, the point values to use for the left, top, right, and bottom margins.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to a PCMarginParams structure with new document margins.

Return: Nothing.

Interception: Unlikely.

Structures: The PCMarginParams structure has the following definition:

typedef struct {
    word		 PCMP_left;
    word		PCMP_top;
    word		PCMP_right;
    word		PCMP_bottom;
} PCMarginParams;

MSG_PRINT_CONTROL_GET_DOC_MARGINS

void	MSG_PRINT_CONTROL_GET_DOC_MARGINS(
        PCMarginParams 	*ptr);

Use this message to get the present values for the document margins. It returns four integers. These integers represent the left, top, right, and bottom margins, expressed in typographer's points.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to a PCMarginParams structure which will hold return value.

Return: Nothing returned explicitly.

ptr
Structure filled in with document margins.

Interception: Unlikely.

Structures: The PCMarginParams structure has the following definition:

typedef struct {
    word		PCMP_left;
    word		PCMP_top;
    word		PCMP_right;
    word		PCMP_bottom;
} PCMarginParams;

MSG_PRINT_CONTROL_SET_DOC_SIZE_INFO

void	MSG_PRINT_CONTROL_SET_DOC_SIZE_INFO(
        PageSizeReport 	*ptr);

Use this message to set all of the information about the document size and orientation.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to a PageSizeReport structure.

Return: Nothing.

Interception: Unlikely.

Structures: The PageSizeReport structure has the following definition:

typedef struct {
    dword			PSR_width;
    dword			PSR_height;
    PageLayout			PSR_layout;
    PCMarginParams 			PSR_margins;
} PageSizeReport;

MSG_PRINT_CONTROL_GET_DOC_SIZE_INFO

void	MSG_PRINT_CONTROL_GET_DOC_SIZE_INFO(
        PageSizeReport 	*ptr);

Use this message to set all of the information about the document size and orientation.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to an empty PageSizeReport structure which the message handler will fill in.

Return: Nothing.

Interception: Unlikely.

Structures: The PageSizeReport structure has the following definition:

typedef struct {
    dword			PSR_width;
    dword			PSR_height;
    PageLayout			PSR_layout;
    PCMarginParams 			PSR_margins;
} PageSizeReport;
| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Page Range Information | Next: 4.5 Print Output Object


Print Output Object

The Spool Library: 4.5 Print Control Instance Data: Print Output Object

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Document Size | Next: 4.6 Document Name Output
PCI_output, MSG_PRINT_START_PRINTING, MSG_PRINT_VERIFY_PRINT_REQUEST, MSG_PRINT_NOTIFY_PRINT_DB

Some object is going to describe the print jobs to the Print Control. When instantiating the Print Control, set the chosen object's name in the PCI_output field:

PCI_output = <yourObject>;

There are three messages the Print Control may send to its output: MSG_PRINT_START_PRINTING, MSG_PRINT_VERIFY_PRINT_REQUEST, and MSG_PRINT_NOTIFY_PRINT_DB . The Print Output must respond to MSG_PRINT_START_PRINTING , which is the signal that it's time to describe a print job. The Print Output also must respond to MSG_PRINT_VERIFY_PRINTING if the PrintControl's PCA_VERIFY_PRINT bit has been set. Otherwise, it need not handle this message. Finally, the Print Output may have a handler for the MSG_PRINT_NOTIFY_DB , a message the PrintControl will send whenever the Print dialog box comes up or goes away.

The Print Method

Assuming your geode doesn't try to do anything fancy with its own printing UI or weird scheduling, probably the most complicated thing you'll have to do when adding printing capability to your geode is write a Print Method.

This message must be handled by the Print Output object, though the handler probably won't be too complicated. In its simplest form, a MSG_PRINT_START_PRINTING handler could just call some graphics commands and finish off by sending MSG_PRINT_CONTROL_PRINTING_COMPLETED to the PrintControl.

The message is accompanied by a pointer back to the Print Control and a GString handle. Any graphics commands drawn to the GString handle will be retained and will become part of the print job.

Before looking at examples, be warned that there are some requirements that every Print Method must meet:

  • Be sure the document size has been set correctly. Before the job is finished and you send MSG_PRINT_CONTROL_PRINTING_COMPLETED , you must be certain the document's size and margins have been set somewhere. This may have been set using the PCI_docSizeInfo field when defining the Print Control. If you haven't already let the Print Control know about the document size elsewhere, then you must send a MSG_PRINT_CONTROL_SET_DOC_SIZE , probably either in the MSG_PRINT_START_PRINTING or wherever your geode sets up document information in general.
  • Be certain the number of pages to be printed has been correctly set. If your application hasn't already set the page range (when the Print Control was created or when the Print dialog box goes up are popular times for this), send a MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE to your Print Control. Make sure you do it before sending in the MSG_PRINT_CONTROL_PRINTING_COMPLETED .
  • Be sure the document name has been set. If you use a GenDocumentGroup as your PCI_docNameOutput , this will be taken care of for you. Otherwise, some time before the MSG_PRINT_CONTROL_PRINTING_COMPLETED is sent, your PCI_docNameOutput object is going to be asked for the document name, and the job won't be spooled until your PCI_docNameOutput sends the correct name back to the Print Control.
  • The Print Method should end each page with a GrNewPage() ; the last thing drawn before the print job ends is a GrNewPage() .
  • Finally, when the Print Method is finished describing the print job, it must end with a MSG_PRINT_CONTROL_PRINTING_COMPLETED . Otherwise, the Print Control has no way of knowing the job is ready.

When the Print Control receives the message MSG_PRINT_CONTROL_PRINTING_COMPLETED , it responds by cleaning up and sending the print job to the spooler.

Your handler can send a MSG_PRINT_CONTROL_REPORT_PROGRESS to the Print Control if it is spooling a large job and wants to reassure the user. If something goes wrong, or if MSG_PRINT_CONTROL_REPORT_PROGRESS returns a signal indicating the user wishes to cancel, MSG_PRINT_START_PRINTING should send a MSG_PRINT_CONTROL_PRINTING_CANCELLED instead.

MSG_PRINT_START_PRINTING

void	MSG_PRINT_START_PRINTING(
        optr		printControlOD,
        GStateHandle		gstate);

The handler for this message should call a number of graphics routines, using the passed GState. When done with graphics routines, the handler should send a MSG_PRINT_CONTROL_PRINTING_COMPLETED (or MSG_PRINT_CONTROL_PRINTING_CANCELED ) to the Print Control. The handler may have to accomplish other tasks; see the above bulleted list (the one marked as "the most used and useful section of this chapter").

Source: PrintControl object.

Destination: The object specified in PCI_output .

Parameters: printControlOD The optr of the PrintControl object.

gstate
The GState handle to draw to.

Return: Nothing.

Interception: The Print output object must intercept this message, build the print job by drawing to the passed GState, then send MSG_PRINT_CONTROL_PRINTING_COMPLETED to the object in printControlOD .

Verifying User Choices

You may write a handler for MSG_PRINT_VERIFY_PRINT_REQUEST , which will be sent to the Print Output if the PCA_VERIFY_PRINT bit has been set. The message will be sent after the user has dismissed the Print dialog box, after the system has had a chance to make sure the document fits on the paper but before the document is actually spooled.

This message gives the application a chance to check out the state of the UI and make sure that the user's choices are valid. Note that the PrintControl does its own checking to make sure that the document will fit on the page. After examining the UI, the handler must send back a MSG_PRINT_CONTROL_VERIFY_COMPLETED , passing the argument to say whether it's okay to print.

MSG_PRINT_VERIFY_PRINT_REQUEST

void	MSG_PRINT_VERIFY_PRINT_REQUEST(
        optr 	printControlOD);

The handler for this message should make whatever checks are necessary to make sure that the user has made valid choices with the printing UI. The handler should then send back an indication of whether the user's choices are all right by means of a MSG_PRINT_CONTROL_VERIFY_COMPLETED.

Source: PrintControl object.

Destination: The object specified in PCI_output .

Parameters: printControlOD The optr of the PrintControl object.

Return: Nothing explicitly. However, the handler for this message must send a MSG_PRINT_CONTROL_VERIFY_COMPLETED back to the print control.

Interception: If you've set the PCA_VERIFY_PRINT flag, you must intercept this message correctly or else the print job will never start.

Dialog Box Notification

The Print Control will send the Print Output a MSG_PRINT_NOTIFY_DB every time the Print dialog box comes up or goes away. Note that this message allows the application to update the page range and print group's UI at only those times that updates are needed.

This message will arrive with one argument, a pointer back to the PrintControl object that sent it. The handler for this message might wish to update the page range information using MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE and MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE .

MSG_PRINT_NOTIFY_PRINT_DB

void	MSG_PRINT_NOTIFY_PRINT_DB(
        optr		printControlOD,
        PrintControlStatus		pcs);

The handler for this message can do any almost anything; this message signals that the Print dialog box has just come up or gone away.

Source: PrintControl object.

Destination: The object specified in PCI_output .

Parameters: printControlOD The optr of the PrintControl object.

pcs
The status of the print control; either PCS_PRINT_BOX_VISIBLE or PCS_PRINT_BOX_NOT_VISIBLE.

Return: Nothing.

Interception: Some Print Output objects will intercept this message to set page ranges.

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Document Size | Next: 4.6 Document Name Output


Document Name Output

The Spool Library: 4.6 Print Control Instance Data: Document Name Output

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Print Output Object | Next: 4.7 The Default Printer
PCI_docNameOutput, MSG_PRINT_GET_DOC_NAME

The PCI_docNameOutput field must contain the optr of an object which can tell the Spooler the document's name. The document name is displayed on the Printer Control Panel and is how the user can figure out which job is which on the queue. If a GenDocumentGroup is set as the Print Control's PCI_docNameOutput , then that object will automatically do the right thing. If you use some other type of object, it must be prepared to supply document names on demand. This demand will come in the form of a MSG_PRINT_GET_DOC_NAME .

This message comes with a pointer back to the Print Control. The handler should respond by sending a MSG_PRINT_CONTROL_SET_DOC_NAME with a string containing the name.

MSG_PRINT_GET_DOC_NAME

void	MSG_PRINT_GET_DOC_NAME(
        optr	printControlOD);

If your PCI_docNameOutput object is not a GenDocumentGroup, then it must have a handler for this message.

Source: PrintControl object.

Destination: The object specified in PCI_docNameOutput .

Parameters: printControlOD The optr of the PrintControl object.

Return: Nothing returned explicitly, but should send a MSG_PRINT_CONTROL_SET_DOC_NAME back to the Print Control.

Interception: If receiving object is a GenDocument, unlikely. Otherwise, interception is necessary.

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Print Output Object | Next: 4.7 The Default Printer


The Default Printer

The Spool Library: 4.7 Print Control Instance Data: The Default Printer

| Up: GEOS SDK TechDocs | Up | Prev: 4.6 Document Name Output | Next: 4.8 Adding UI Gadgetry
PCI_defPrinter, MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER, MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER

As this documentation has already stressed, one of the printing system's more important features is that it is device independent. However, certain special-purpose applications could conceivably depend on the user's printer type, and those applications may have use for the Print Control's PCI_defPrinter field. When the Print Control first appears, it will normally have the system default printer selected. Using the PCI_defPrinter option, another printer might be selected. The user may of course override the default printer and select another.

You can set or retrieve the value in PCI_defPrinter with the messages listed above. For information on them, see Working with Instance Data .

There are utility routines to get information about the system default printer. There are commands to retrieve information about each of the installed printers so that the application may find out whether the desired type of printer is installed and, if so, what its printer number is. These routines are described in Printer Information and Manipulation .

| Up: GEOS SDK TechDocs | Up | Prev: 4.6 Document Name Output | Next: 4.8 Adding UI Gadgetry


Adding UI Gadgetry

The Spool Library: 4.8 Print Control Instance Data: Adding UI Gadgetry

| Up: GEOS SDK TechDocs | Up | Prev: 4.7 The Default Printer | Next: 5 Print Control Messages
ATTR_PRINT_CONTROL_APP_UI

If the Print Control supplies UI that meets your geode's needs, feel free to skip this section. On the other hand, if you've decided you want a non-standard print trigger or want some specialized gadgetry to appear in the application's Print dialog box, it is possible to create objects and tell the Print Control to work with them. Make sure that any objects you want to use in this fashion aren't part of a generic or visual tree already. Also, these objects must be set not usable (~ GS_USABLE).

Normally the user has two ways to print up a Print dialog box: the User Print Trigger or the User Fax Trigger, both appearing in the File menu. You may specify a special trigger to be used in place of the standard trigger provided by the Print Control. Normally the default trigger is not only sufficient but preferable, if only because it will automatically display with the correct appearance for the specific UI. To define your own trigger object, create the object(s), setting the correct instance data. Then, when instantiating your Print Control, include an ATTR_GEN_CONTROL_APP_UI:

ATTR_GEN_CONTROL_APP_UI = {yourTrigger};

When this option is used at all, normally the trigger object is a simple trigger. The action descriptor of this application-defined trigger should be

GTI_output = <your_PrintControl>;
GTI_method = MSG_PRINT_CONTROL_INITIATE_PRINT;

By working with the control's features, you may remove either or both of the default triggers.

The Print Control's vardata field ATTR_PRINT_CONTROL_APP_UI allows application-specific gadgetry to appear in the Print dialog box. Create the object(s) to be included and set their instance data, then use the ATTR_PRINT_CONTROL_APP_UI field to signal that the Print Control should include the gadgetry.

ATTR_PRINT_CONTROL_APP_UI = {yourPrintGroup};

When this option is used at all, usually a GenInteraction serves as the print group object, with children appropriate to the task at hand. This generic tree must not be destroyed unless the vardata field has been removed.

| Up: GEOS SDK TechDocs | Up | Prev: 4.7 The Default Printer | Next: 5 Print Control Messages


Print Control Messages

The Spool Library: 5 Print Control Messages

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Print Control Instance Data | Next: 6 Page Size Control

Now you have a rather good idea of what's required to set up a Print Control. Once it's been set up, your geode may send the following messages to it.

Common Response Messages

Flow of Control Messages

Working with Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Print Control Instance Data | Next: 6 Page Size Control


Common Response Messages

The Spool Library: 5.1 Print Control Messages: Common Response Messages

| Up: GEOS SDK TechDocs | Up | Prev: 5 Print Control Messages | Next: 5.2 Flow of Control Messages
MSG_PRINT_CONTROL_VERIFY_COMPLETED, MSG_PRINT_CONTROL_SET_DOC_NAME

These messages are normally used only to respond to messages sent out by the Print Control.

MSG_PRINT_CONTROL_VERIFY_COMPLETED

void	MSG_PRINT_CONTROL_VERIFY_COMPLETED(
        Boolean 	continue);

If you have decided to verify the user's Print dialog box choices, you must send this message back to the Print Control in response to MSG_PRINT_VERIFY_PRINT_REQUEST . If true is passed, the Print Control will continue preparing the job for printing. If the value passed is false , the print will be cancelled. It is up to the application to explain the problem to the user.

Source: Unrestricted, normally the PCI_output object.

Destination: Any PrintControl object.

Parameters: continue Flag signalling whether printing should continue or be cancelled.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_SET_DOC_NAME

void	MSG_PRINT_CONTROL_SET_DOC_NAME(
        char	* string);

This message must be sent by the PCI_docNameOutput before the print job can be spooled. It is sent in response to a MSG_PRINT_GET_DOC_NAME from the Print Control. The string argument should be something that will allow the user to recognize the print job in the Printer Control Panel.

Source: Unrestricted--normally the PCI_docNameOutput object.

Destination: Any PrintControl object.

Parameters: string The document's name, as a null-terminated string. Maximum length of this string is FILE_LONGNAME_LENGTH+1.

Return: Nothing.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Print Control Messages | Next: 5.2 Flow of Control Messages


Flow of Control Messages

The Spool Library: 5.2 Print Control Messages: Flow of Control Messages

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Common Response Messages | Next: 5.3 Working with Instance Data
MSG_PRINT_CONTROL_PRINTING_COMPLETED, MSG_PRINT_CONTROL_REPORT_PROGRESS, MSG_PRINT_CONTROL_PRINTING_CANCELLED, MSG_PRINT_CONTROL_REPORT_STRING, MSG_PRINT_CONTROL_INITIATE_PRINT, MSG_PRINT_CONTROL_PRINT, MSG_PRINT_CONTROL_INITIATE_OUTPUT_UI

Sending these messages to the Print Control signals that a new stage of printing is ready to begin.

MSG_PRINT_CONTROL_PRINTING_COMPLETED

void	MSG_PRINT_CONTROL_PRINTING_COMPLETED();

This message signals the Print Control that the application is finished describing the print job. The application must send this message (or a MSG_PRINT_CONTROL_PRINTING_CANCELLED ) some time after receiving a MSG_PRINT_START_PRINTING .

Source: The object that received MGS_PRINT_START_PRINTING .

Destination: Any PrintControl object.

Parameters: None.

Return: Nothing.

Interception: Unlikely, unadvised.

MSG_PRINT_CONTROL_REPORT_PROGRESS

Boolean	MSG_PRINT_CONTROL_REPORT_PROGRESS(
        PCProgressType 	progress,
        int 	pageOrPercent);

This message may be sent between the time the Print Output receives a MSG_PRINT_START_PRINTING and the time it sends a MSG_PRINT_CONTROL_PRINTING_COMPLETED . Its arguments include a progress type (page number or percentage) and a number representing the progress. It returns a Boolean value, which will normally be true but will be false if the user wishes to cancel printing.

The application should send out this message periodically over the course of a long print job, probably about once per page. Progress may be reported as a page number, a percentage of the job completed, or as a null-terminated string.

If the return value is false , the application should stop drawing to the print GString immediately and send the Print Control a MSG_PRINT_CONTROL_PRINTING_CANCELLED . The return value may be safely ignored, but if the user wishes to cancel printing, it's polite to cancel immediately instead of making him wait until the MSG_PRINT_START_PRINTING has finished describing the entire job.

Source: The object that received MGS_PRINT_START_PRINTING when building a time-consuming job.

Destination: The PrintControl object specified by MSG_PRINT_START_PRINTING .

Parameters: progress How progress is being reported, by page (PCPT_PAGE) or percent (PCPT_PERCENT).

pageOrPercent
How much progress has been made.

Return: Flag signalling that printing should continue. If the flag is false (i.e. zero), then the user wants to cancel printing, and it would be polite to stop.

Interception: Unlikely.

Structures: The PCProgressType enumeration is defined:

 typedef enum {
 PCPT_PAGE,
 PCPT_UNUSED1, /* Unused type */
 PCPT_PERCENT,
 PCPT_UNUSED2, /* Unused type */
 PCPT_TEXT
} PCProgressType;

MSG_PRINT_CONTROL_REPORT_PROGRESS_STRING

@alias(MSG_PRINT_CONTROL_REPORT_PROGRESS) \
Boolean	MSG_PRINT_CONTROL_REPORT_PROGRESS_STRING(
        PCProgressType 	progress,
        Chars 	*progressString);

This message may be sent between the time the Print Output receives a MSG_PRINT_START_PRINTING and the time it sends a MSG_PRINT_CONTROL_PRINTING_COMPLETED . Its arguments include a text string giving some indication of progress. This message returns a Boolean value, which will normally be true but will be false if the user wishes to cancel printing.

The application should send out this message periodically over the course of a long print job, probably about once per page. Progress may be reported as a page number, a percentage of the job completed, or as a null-terminated string.

If the return value is false , the application should stop drawing to the print GString immediately and send the Print Control a MSG_PRINT_CONTROL_PRINTING_CANCELLED . The return value may be safely ignored, but if the user wishes to cancel printing, it's polite to cancel immediately instead of making him wait until the MSG_PRINT_START_PRINTING has finished describing the entire job.

Source: The object that received MGS_PRINT_START_PRINTING when building a time-consuming job.

Destination: The PrintControl object specified by MSG_PRINT_START_PRINTING .

Parameters: progress This must be PCPT_TEXT. The reason that this parameter has only one possible value is that this message is actually an alias of MSG_PRINT_CONTROL_REPORT_PROGRESS , handled by the same assembly routine.

progressString
A string of text, giving an indication of progress.

Return: Flag signalling that printing should continue. If the flag is false (i.e. zero), then the user wants to cancel printing, and it would be polite to stop.

Interception: Unlikely.

Structures: The PCProgressType enumeration is defined:

typedef enum {
 PCPT_PAGE,
 PCPT_UNUSED1, /* Unused type */
 PCPT_PERCENT,
 PCPT_UNUSED2, /* Unused type */
 PCPT_TEXT
} PCProgressType;

MSG_PRINT_CONTROL_INITIATE_OUTPUT_UI

void MSG_PRINT_CONTROL_INITIATE_OUTPUT_UI(
        PrinterDriverType type);

Display the output dialog box.

Source: Anything.

Destination: PrintControl object.

Parameters: type PrinterDriverType value specifying what sort of printer device.

Return: Nothing.

Interception: Rare. This messages is generally used internally only, as the PrintControl normally provides all the UI neededfor printing.

MSG_PRINT_CONTROL_PRINTING_CANCELLED

void	MSG_PRINT_CONTROL_PRINTING_CANCELLED();

The application may send this message to the PrintControl after receiving a MSG_PRINT_START_PRINTING . Do not send both this message and a MSG_PRINT_CONTROL_PRINTING_COMPLETED for a single print job.

The application may send this message to cancel a document while describing a print job, before it would send the MSG_PRINT_CONTROL_PRINTING_COMPLETED .

Source: Unrestricted, probably the PCI_output .

Destination: The PrintControl object specified by MSG_PRINT_START_PRINTING .

Parameters: None.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_INITIATE_PRINT

void	MSG_PRINT_CONTROL_INITIATE_PRINT();

This message, normally sent by the Print trigger in the File menu, is the signal that the Print Control should display the Print dialog box. Geodes with custom print triggers should make sure that those triggers send this message to the Print Control. Geodes using the Print Control's provided print trigger need not send this message.

Source: Unrestricted--typically the user print trigger (or fax trigger).

Destination: Any PrintControl object.

Parameters: None.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_PRINT

void	MSG_PRINT_CONTROL_PRINT();

This message, normally sent by the Print trigger in the Print dialog box, is the signal that the user has made his printing choices and is ready for the Print Control to verify those choices and spool the job.

Source: Unrestricted--typically the Print trigger in the Print Dialog Box.

Destination: Any PrintControl object.

Parameters: None.

Return: Nothing.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Common Response Messages | Next: 5.3 Working with Instance Data


Working with Instance Data

The Spool Library: 5.3 Print Control Messages: Working with Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Flow of Control Messages | Next: 6 Page Size Control
MSG_PRINT_CONTROL_SET_OUTPUT, MSG_PRINT_CONTROL_GET_OUTPUT, MSG_PRINT_CONTROL_SET_DOC_NAME_OUTPUT, MSG_PRINT_CONTROL_GET_DOC_NAME_OUTPUT, MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER, MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER, MSG_PRINT_CONTROL_GET_PRINT_MODE, MSG_PRINT_CONTROL_GET_PAPER_SIZE, MSG_PRINT_CONTROL_GET_PRINTER_MARGINS, MSG_PRINT_CONTROL_CALC_DOC_DIMENSIONS, MSG_PRINT_CONTROL_VERIFY_DOC_MARGINS, MSG_PRINT_CONTROL_VERIFY_DOC_SIZE

The Print Control handles many messages which allow the geode to retrieve and alter the values of the instance data.

MSG_PRINT_CONTROL_SET_OUTPUT

void 	MSG_PRINT_CONTROL_SET_OUTPUT(
        optr 	objectPtr);

This message, used very rarely, specifies that a different object should become the Print Output. Pass the optr of the object to use.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: objectPtr The optr of the new PCI_output .

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_OUTPUT

optr 	MSG_PRINT_CONTROL_GET_OUTPUT();

This message returns the optr of the Print Output.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: The optr of the current PCI_output object.

Interception: Unlikely.

MSG_PRINT_CONTROL_SET_DOC_NAME_OUTPUT

void 	MSG_PRINT_CONTROL_SET_DOC_NAME_OUTPUT(
        optr 	document);

This message, used very rarely, specifies that a different object should become the Document Name Output object. Pass the optr of the object to use.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: objectPtr The optr of the new PCI_docNameOutput .

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_DOC_NAME_OUTPUT

optr 	MSG_PRINT_CONTROL_GET_DOC_NAME_OUTPUT();

This message returns the optr of the Document Name Output object.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: The optr of the Document Name output object.

Interception: Unlikely.

MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER

void 	MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER(
        int 	printerNum);		/* -1 for system's default printer */

This message sets the application default printer, to be used the next time the print dialog box appears. Pass the printer number of the printer to use. Passing a value of -1 means that the system default printer should be used.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: printerNum Number of the new default printer.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER

int 	MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER();

Use this message to retrieve the number of the present application default printer. You may not assume that this printer is the one the user is printing on, since the user may have changed to another printer. A return value of -1 means that the application will use the system default printer.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: The number of the default printer.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_PRINT_MODE

byte 	MSG_PRINT_CONTROL_GET_PRINT_MODE();

This message retrieves the current user-selected print mode, if any. Its possible return values include zero, meaning no mode has been selected; PM_GRAPHICS_LOW_RES; PM_GRAPHICS_MED_RES; PM_GRAPHICS_HI_RES; PM_TEXT_DRAFT; and PM_TEXT_NLQ.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: A PrinterOutputModes signalling the printer's quality.

Interception: Unlikely.

Structures: The PrinterOutputModes structure has the following definition:

typedef ByteFlags PrinterOutputModes;
#define  POM_GRAPHICS_LOW 0x10
#define POM_GRAPHICS_MEDIUM 0x08
#define POM_GRAPHICS_HIGH 0x04
#define POM_TEXT_DRAFT 0x02
#define POM_TEXT_NLQ 0x01

Each flag indicates the specified quality is available. Two useful masks which have been set up are PRINT_GRAPHICS and PRINT_TEXT.

MSG_PRINT_CONTROL_GET_PAPER_SIZE

void 	MSG_PRINT_CONTROL_GET_PAPER_SIZE(
        PCMargineParams 		*retVal);

Use this message to retrieve the Print Control's present paper size.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: retVal An empty PCMarginParams structure which the message handler will fill.

Return: Nothing returned explicitly; the retVal structure will be filled.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_PAPER_SIZE_INFO

void	MSG_PRINT_CONTROL_GET_PAPER_SIZE_INFO(
        PageSizeReport 	*ptr);

Use this message to retrieve all of the information about the document size and orientation.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to an empty PageSizeReport structure which the message handler will fill in.

Return: Nothing.

Interception: Unlikely.

Structures: The PageSizeReport structure has the following definition:

typedef struct {
    dword			PSR_width;
    dword			PSR_height;
    PageLayout			PSR_layout;
    PCMarginParams 			PSR_margins;
} PageSizeReport;

MSG_PRINT_CONTROL_GET_PRINTER_MARGINS

void 	MSG_PRINT_CONTROL_GET_PRINTER_MARGINS(
        MarginDimensions 		*retVal,
        Boolean 		setMargins);

This message returns the margins enforced by the requested printer. If the boolean argument is true , the document margins will be set to be the same as the printer margins.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: setMargins Pass TRUE to set the document's margins equal to the printer's margins, FALSE to leave the margins as they are.

retVal
A pointer to an empty MarginDimensions structure to be filled in by the handler.

Return: The printer's enforced margins.

Structures: The following structure keeps track of all four of a document's margins.

 typedef struct {
    int 	leftMargin;		/* measured in points */
    int 	topMargin;
    int 	rightMargin;
    int 	bottomMargin;
} MarginDimensions;

Interception: Unlikely.

MSG_PRINT_CONTROL_CALC_DOC_DIMENSIONS

void 	MSG_PRINT_CONTROL_CALC_DOC_DIMENSIONS(
        PageSizeReport 	*ptr);

This message calculates the maximum printable area allowed by the user-selected paper size and printer-mandated margins. This message automatically resets the document size and margins to hold these values.

Note that this message correctly handles rotated pages.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr A pointer to a PageSizeReport structure to hold return value.

Return: Nothing returned explicitly.

ptr
The pointer to the filled PageSizeReport structure.

Interception: Unlikely.

MSG_PRINT_CONTROL_CHECK_IF_DOC_WILL_FIT

Boolean 	MSG_PRINT_CONTROL_CHECK_IF_DOC_WILL_FIT(
        Boolean 	warning);

This message returns true if the passed document margins will fit on the selected printer.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: warning If set, the PrintControl will deliver a warning to the user.

Return: Indication of whether the printer can work with the current margins ( true if so, false if not).

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Flow of Control Messages | Next: 6 Page Size Control


Page Size Control

The Spool Library: 6 Page Size Control

| Up: GEOS SDK TechDocs | Up | Prev: 5 Print Control Messages | Next: 7 Other Printing Components
MSG_PRINT_REPORT_PAGE_SIZE, SpoolSetDocSize(), MSG_PZC_GET_PAGE_SIZE, MSG_PZC_SET_PAGE_SIZE,

Thanks to the Spooler, applications don't need to worry about what page size the user is working with. If a document is bigger than a printer's paper, then the Spooler will automatically tile the job onto as may sheets of paper as necessary. However, some applications will need to make the user decide on a page size. Page layout programs need to know what size page the user is working with. Spreadsheet programs need to make sure that cells aren't split in half by a page break.

Applications which ask the user to select a page size should incorporate a PageSizeControl into their generic tree. This generic control object provides a dialog box containing page setup choices for the user.

This dialog box contains UI allowing the user to specify what sort of paper the document is to be printed to: envelope, labels, or regular paper. The user may then specify a set of dimensions within that type; paper would have choices including letter sized, legal sized, and A4. The user can even set up a set of custom page dimensions.

Most applications which include a PageSizeControl will probably keep track of their own page size. In this case, use SpoolSetDocSize() to update the PageSizeControl's UI when changing the page size. When the user changes the paper size in the PageSizeControl, the control's output will receive a MSG_PRINT_REPORT_PAGE_SIZE .

Applications which do not keep track of the page size but want to include a PageSizeControl may do so. However, these applications will probably need to find out the page size at a specific time, not just receive notification every time the user changes the page size. This doesn't really fit the normal controller model, and in fact such applications are asking the PageSizeControl to act not like a controller, but like an ordinary piece of UI gadgetry. Setting the PZCA_ACT_LIKE_GADGET flag in the PageSizeControl's PZCI_attrs instance field will make the PageSizeControl act like a gadget.

If the PageSizeControl is to act as a controller, it must appear on the GenApplication's GAGCNLT_SELF_LOAD_OPTIONS GCN list, as does the PrintControl.

No matter how an application interacts with its PageSizeControl, it will work with the PageSizeReport structure.

  typedef struct {
    dword 			PSR_width;
    dword 			PSR_height;
    PageLayout 			PSR_layout;
    PCMarginParams 			PSR_margins;
} PageSizeReport;
 typedef WordFlags PageLayout;
  typedef enum {
    PT_PAPER,
    PT_UNUSED1, /* Unused type */
    PT_ENVELOPE,
    PT_UNUSED2, /* Unused type */
    PT_LABEL
} PageType;
/* Specifying a PageLayout is accomplished by
 * OR-ing together the parts of the layout.
 * Exactly what those parts are depends on the
 * PageType: */
/* Paper Layouts:
 * 	(PT_PAPER | (PO_orient << 3) ) */
  typedef ByteEnum 				PaperOrientation;
#define PO_PORTRAIT 					0x00
#define PO_LANDSCAPE 					0x01
  typedef WordFlags 				PageLayoutPaper;
#define PLP_ORIENTATION 					0x0008
#define PLP_TYPE 					0x0004 /* PT_PAPER */
/* Envelope Layouts:
 * (PT_ENVELOPE | (EP_path << 5) | (EO_ori << 3)) */
  typedef ByteEnum 				EnvelopePath;
#define EP_LEFT 					0x00
#define EP_CENTER 					0x01
#define EP_RIGHT 					0x02
  typedef ByteEnum 				EnvelopeOrientation;
#define EO_PORTAIT_LEFT 					0x00
#define EO_PORTAIT_RIGHT 					0x01
#define EO_LANDSCAPE_UP 					0x02
#define EO_LANDSCAPE_DOWN 					0x03
  typedef WordFlags 				PageLayoutEnvelope;
#define PLE_PATH 					0x0040
#define PLE_ORIENTATION 					0x0010
#define PLE_TYPE 					0x0004 /*PT_ENVELOPE*/
/* Label Layouts:
 * 	(PT_LABEL | (rows <<8) | (cols << 3)) */
  typedef WordFlags 				PageLayoutLabel;
#define PLL_ROWS 					0x7e00 /* # rows */
#define PLL_COLUMNS 					0x01f8 /* # cols */
#define 	PLL_TYPE 				0x0004 /* PT_LABEL */

Code Display 14-3 PageSizeControl Features

  typedef ByteFlags 	PageSizeControlFeatures;
/* The following flags may be combined with | and &:
    PSIZECF_MARGINS, 			( Set margin sizes )
    PSIZECF_CUSTOM_SIZE,			( Custom dimension ranges )
    PSIZECF_LAYOUT,
    PSIZECF_SIZE_LIST,
    PSIZECF_PAGE_TYPE 			( Choice of standard dimensions ) */
#define PSIZEC_DEFAULT_FEATURES (PSIZECF_PAGE_TYPE | PSIZECF_SIZE_LIST | \
                 PSIZECF_LAYOUT | PSIZECF_CUSTOM_SIZE)
MSG_PRINT_REPORT_PAGE_SIZE
void	MSG_PRINT_REPORT_PAGE_SIZE(
        PageSizeReport *psr);

The page size control's output object should have a handler for this message. The control will send this message whenever the user has changed the page size. The handler may wish to change the bounds of some appropriate object, store the new bounds somewhere, or take some other action.

Source: PageSizeControl object.

Destination: The GCI_output object.

Parameters: psr A pointer to a PageSizeReport structure containing the page type and size.

Return: Nothing.

Interception: The handler for this message should store the page size information. It may set the bounds of one or more visual objects.

In addition to the standard set of generic Control instance data, the Page Size control includes some of its own. This data, as you might expect, is largely concerned with paper size and setup. The fields are listed in Page Size Control Instance Data .

PageSizeControls use the PageLayout data structure to hold and pass layout information such as page orientation. One part of this structure gives the type of page being described: paper, envelope, or label. The other parts of the structure have different meanings depending on the type of page. For paper, the layout keeps track of the orientation. For envelopes, the structure contains both path and orientation information. For labels, this structure keeps track of how many labels are on the sheet in each direction; these numbers are bounded by MAXIMUM_LABELS_ACROSS and MAXIMUM_LABELS_DOWN.

Code Display 14-4 Page Size Control Instance Data

@instance PageSizeControlAttrs				PZCI_attrs = 0;
    /* Possible attributes:
     * PZCA_ACT_LIKE_GADGET
     * PZCA_PAPER_SIZE
     * PZCA_INITIALIZE				*/
    /* Current page dimensions and type/layout */
@instance dword 			PZCI_width = 0;
@instance dword 			PZCI_height = 0;
@instance PageLayout 			PZCI_layout = 0;
@instance PCMarginParams			PZCI_margins = { 0, 0, 0, 0 };
/* The following vardata field is internal: */
@vardata  PageSizeControlMaxDimensions  TEMP_PAGE_SIZE_CONTROL_MAX_DIMENSIONS;
/* Its structure is defined:
typedef struct {
     dword  PZCMD_width; ( maximum width )
     dword PZCMD_height; ( maximum height )
} PageSizeControlMaxDimensions; */
/* Attribute to allow applications to be made aware of every change made
 * to gadgetry in the PageSizeControl. This is especially useful for applications
 * that want to add UI that will be dependent upon the state of this
 * controller. No effort is made to eliminate the redundant output of data. */
@vardata  PageSizeControlChanges  ATTR_PAGE_SIZE_CONTROL_UI_CHANGES;
/* This field has structure:
    typedef struct {
     optr  PSCC_destination; ( destination for message )
     Message PSCC_message; ( message to be sent )
    }  PageSizeControlChanges; */
The message should follow the prototype: */
@prototype void PAGE_SIZE_UI_CHANGES_MSG(PageSizeReport _far *psr )
PZCI_width , PZCI_height
These integers represent the current page dimensions, measured in points. Note that these values may never go outside the bounds described by the constants MINIMUM_PAGE_WIDTH_VALUE , MINIMUM_PAGE_HEIGHT_VALUE, MAXIMUM_PAGE_WIDTH_VALUE , and MAXIMUM_PAGE_HEIGHT_VALUE.
PZCI_layout
This field contains a PageLayout structure describing the current page layout settings.
PZCI_margins
This field contains the page's margins.

The following messages allow applications to work with a PageSizeControl directly, like a regular UI gadget. There is one message which the PageSizeControl will send to its output, and several that any object may send to the controller.

Note that these messages are only useful if the PageSizeControl is operating in gadget mode (i.e. if its PZCA_ACT_LIKE_GADGET bit is set). Otherwise, the control's default behavior would override that requested by these messages.

The PageSizeControlAttrs record has the following flags:

PZCA_ACT_LIKE_GADGET
The PageSizeControl object will act like any other generic gadget and will not respond to normal controller notifications.
PZCA_PAPER_SIZE
The PageSizeControl object will display paper sizes instead of document sizes. Setting this would be very rare for applications.
PZCA_INITIALIZE
The PageSizeControl object will be initialized to system default values if PZCA_ACT_LIKE_GADGET is also set.
PZCA_LOAD_SAVE_OPTIONS
Allow the controller to load and save the user's options.
MSG_PZC_GET_PAGE_SIZE
void	MSG_PZC_GET_PAGE_SIZE(
        PageSizeReport		*psr);

This message returns the present page size in terms of width, height, and layout, stored in a PageSizeReport . Note that the PageSizeControl will be sending out a MSG_PRINT_REPORT_PAGE_SIZE every time the user applies changes to these values.

Only send this message if the PageSizeControl is operating in gadget mode.

Source: Unrestricted, as long as the PageSizeControl is in gadget mode.

Destination: PageSizeControl object.

Parameters: psr A pointer to a PageSizeReport structure in which the page size will be returned.

Return: The PageSizeReport structure pointed to by psr will be filled with the page information.

Interception: Unlikely.

MSG_PZC_SET_PAGE_SIZE
void 	MSG_PZC_SET_PAGE_SIZE(
        PageSizeReport *	psr);

This message changes the current page size. It takes a PageSizeReport data structure containing the new dimensions and layout. The Apply trigger of a Page Size Control normally sends this message, passing the present user page size as arguments.

Only send this message if the PageSizeControl is operating in gadget mode.

Source: Unrestricted, as long as the PageSizeControl is in gadget mode.

Destination: PageSizeControl object.

Parameters: psr A pointer to a PageSizeReport structure containing page information.

Return: Nothing.

Interception: Unlikely.

MSG_PZC_SET_MAXIMUM_HEIGHT
void MSG_PZC_SET_MAXIMUM_HEIGHT(
        dword height);

Set the maximum height for a page.

Source: Anything.

Destination: PageSizeControl object.

Parameters: height Height, in points.

Return: Nothing.

Interception: Generally should not be intercepted.

MSG_PZC_SET_MAXIMUM_WIDTH
void MSG_PZC_SET_MAXIMUM_WIDTH(
        dword width);

Set the maximum width for a page.

Source: Anything.

Destination: PageSizeControl object.

Parameters: width Width, in points.

Return: Nothing.

Interception: Generally should not be intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Print Control Messages | Next: 7 Other Printing Components


Other Printing Components

The Spool Library: 7 Other Printing Components

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Page Size Control | Next: 8 Debugging Tips

Most geodes can fulfill all their drawing needs by working with their Print Control. The system has two other main parts that work with printing: the first is the spool library, which controls scheduling; the second is a collection of printer drivers, which handle interactions between the printing devices and the rest of the system.

Spooler and Scheduling

Printer Drivers

Page Size Related Routines

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Page Size Control | Next: 8 Debugging Tips


Spooler and Scheduling

The Spool Library: 7.1 Other Printing Components: Spooler and Scheduling

| Up: GEOS SDK TechDocs | Up | Prev: 7 Other Printing Components | Next: 7.2 Printer Drivers
SpoolInfo(), SpoolHurryJob(), SpoolDelayJob(), SpoolModifyPriority(), SpoolVerifyPrinterPort(), SpoolCreateSpoolFile(), SpoolDelJob(), MSG_PRINT_NOTIFY_PRINT_JOB_CREATED

Geodes may invoke certain routines that affect the Spooler, but for the most part they should avoid this practice. These routines allow the geode to take advantage of some of the Spooler's scheduling capabilities, so the geode can hurry some jobs and delay others. Most applications should not be concerned with the scheduling of printer jobs. If the user thinks that some job should be given a higher priority, he can work with the Printer Control Panel. Many of the following routines require the print job's ID. Applications that will use these routines should have a handler for MSG_PRINT_NOTIFY_PRINT_JOB_CREATED , which has reference material at the end of this section.

For those applications that will be scheduling, the SpoolHurryJob() and SpoolDelayJob() routines may come in handy. Passed a job ID, these routines move the associated job to the front or back of the queue. SpoolModifyPriority() changes the priority of a spool thread so that it may print in the foreground or be moved further into the background. Each function returns a code describing the Spool's Operating Status, alerting the geode if the job wasn't found, the queue was empty, the port couldn't be verified, or even if the operation was successful.

The SpoolInfo() routine returns the list of jobs on a queue or information about any individual job. The other kind of information application writers might be interested in is the printer port status. It's sometimes unwise to spool a job when the printer is having problems, and SpoolVerifyPrinterPort() can give some warning about problems.

While the above routines are used only rarely in applications, there are others that are used even less often. SpoolCreateSpoolFile() creates a spool file, which may be used to create a pre-generated job for the printer kept handy. Such a file would only be useful to applications that can only print one thing and don't mind leaving around an extra spool file. SpoolDelJob() deletes jobs from the queue.

MSG_PRINT_NOTIFY_PRINT_JOB_CREATED

void 	MSG_PRINT_NOTIFY_PRINT_JOB_CREATED(
        optr	printControlOD,
        word 	jobID);

The PrintControl sends this message when it has sent the job to the print queue. Applications may intercept this message to retrieve the new job's ID number or for any other reason.

Source: PrintControl object.

Destination: GCI_output object.

Parameters: printControlOD The optr of the PrintControl object.

jobID
Print job's ID.

Return: Nothing.

Interception: Applications that want to know the ID of created jobs should intercept this message. Note that this behavior is not encouraged, and most applications will want to leave meddling with a print job's status up to the user's discretion.

| Up: GEOS SDK TechDocs | Up | Prev: 7 Other Printing Components | Next: 7.2 Printer Drivers


Printer Drivers

The Spool Library: 7.2 Other Printing Components: Printer Drivers

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 Spooler and Scheduling | Next: 7.3 Page Size Related Routines

Printer Drivers are very important but at the same time not something that any geode you write is going to interact with. Printer Drivers take care of translating the device-independent graphics commands into forms that individual models of printers can work with. Drivers also provide the Spooler with information about the printer and provide printer-specific UI to the Print Control.

As long as a geode uses only device-independent graphics commands, that geode need not worry about printer drivers. In fact, the only geodes that require knowledge of printer drivers are those that use Raw Mode (see below) printing and access the printer drivers directly. To incorporate Raw Mode printing into a geode (normally a bad idea), keep reading. Whether or not the geode you're writing will interact with Printer Drivers, you might want to continue reading this section to find out what printer drivers do.

Dumb Printers

When dealing with a printer that has no page description language, the Spooler and Printer Driver have to be clever. First the Spooler allocates an offscreen bitmap and plays back the contents of the spool file into the bitmap. It does this through the vidmem driver, which has the same interface as a video driver but draws to offscreen bitmaps instead of to a device. Once the bitmap is complete, the Spooler starts feeding the bitmap to the Printer Driver, which translates the bitmap into a series of commands to the printer. If (as is often the case) the printer doesn't have enough memory to absorb a whole page's worth of data at a time, the Printer Driver feeds in one horizontal band, or "swath" of the bitmap at a time. This also allows for better backgrounding: The driver can feed in a swath, then block and allow other threads to complete other tasks until the printer is ready for the next swath.

Smart Printers

When dealing with an "intelligent" printer, a printer that has its own page description language, the Spooler bypasses most of the work it has to do when dealing with dumb printers. Since an intelligent printer presumably has some commands with GEOS equivalents, the Spooler doesn't bother with creating a bitmap but just passes the GString on to the Printer Driver. The Printer Driver then builds a page description in the printer's native language based on the contents of the GString.

Raw Mode

If you want to use some printer-specific command that doesn't have an equivalent in the GEOS graphics system, you will use raw mode. Raw Mode printing expects that you know what sorts of commands the printer will be expecting. If you try to send PostScript commands to a printer that doesn't understand PostScript, probably the results won't be the desired output.

Printer Information and Manipulation

SpoolGetNumPrinters(), SpoolGetPrinterString(), SpoolGetPrinterInfo(), SpoolCreatePrinter(), SpoolDeletePrinter(), SpoolGetDefaultPrinter(), SpoolSetDefaultPrinter()

If you want to find out something about a printer without accessing the printer driver directly, the spool library provides a number of utility routines that work with printer drivers.

Most of these functions specify which of a user's printers they wish to work with by passing its printer number. The first installed printer will be printer number zero, the second printer will be number one, and so on. Use the SpoolGetNumPrinters() routine to find out how many printers have been installed. Once you have this number, you have an upper bound for printer numbers to test. There will always be fewer than MAXIMUM_NUMBER_OF_PRINTERS printers.

The SpoolGetPrinterString() and SpoolGetPrinterInfo() routines return information about the printer associated with the passed printer number. SpoolGetPrinterString() returns the a string containing the name of the printer, such as "HP DeskJet on COM1." The printer string will be of length at most MAXIMUM_PRINTER_NAME_LENGTH. SpoolGetPrinterInfo() returns various pieces of information helpful to geodes that work with printers directly.

The SpoolCreatePrinter() and SpoolDeletePrinter() routines can install and uninstall printers, tasks probably best left to the Preferences Manager. The SpoolDeletePrinter() routine uninstalls the printer corresponding to the passed printer number. It is easy to use but not something that most geodes should be doing. SpoolCreatePrinter() installs a new printer, returning the new printer's printer number. Note that any geode calling this latter function is going to be expected to provide considerable information about the printer being installed. Again, this is not a routine that many geodes should be using.

SpoolGetDefaultPrinter() returns the number of the system-default printer. SpoolSetDefaultPrinter() makes the printer associated with the passed printer number the new system-default printer. Note that these routines are concerned with the system-default printer as opposed to the application-default printer. Most applications won't specify an application specific default printer. For these applications, the system-default printer will be the default. Of course, the user will still be allowed to select any printer, ignoring the default.

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 Spooler and Scheduling | Next: 7.3 Page Size Related Routines


Page Size Related Routines

The Spool Library: 7.3 Other Printing Components: Page Size Related Routines

| Up: GEOS SDK TechDocs | Up | Prev: 7.2 Printer Drivers | Next: 8 Debugging Tips
SpoolGetNumPaperSizes(), SpoolGetPaperString(), SpoolConvertPaperSize(), SpoolCreatePaperSize(), SpoolDeletePaperSize(), SpoolGetPaperSizeOrder(), SpoolSetPaperSizeOrder(), SpoolGetDefaultPageSizeInfo(), SpoolSetDefaultPageSizeInfo()

Most geodes won't be too concerned with what sort of page size choice the user has made. If the document fits on the page, all's well. If the document doesn't fit, the Spooler will tile the job using only as many pieces of paper as necessary. If the document size should be the same as the page size, the geode can send a MSG_PRINT_CONTROL_CALC_DOC_DIMENSIONS to the print control.

There are several routines which deal with page sizes. Most applications have no reason to call these functions, and we will not discuss them in detail here. Some general notes are in order for those programmers who might work with these functions, though.

It takes two indexes to access a page size. The first is the PageType : paper, envelope, or label. Within each type, there are up to MAX_PAPER_SIZES page sizes. To find out the number of page sizes presently defined for a given type, use the SpoolGetNumPaperSizes() routine. To find the number of a paper size associated with a PageType and a given set of dimensions, use the SpoolConvertPaperSize() routine. For the text string describing the size of the page, call SpoolGetPaperString() (the buffer to hold the string should be of size MAX_PAPER_STRING_LENGTH.

To find out the order in which the page sizes of a given type are stored, call SpoolGetPaperSizeOrder() . You may give a new order by calling SpoolSetPaperSizeOrder() . This will affect the order in which page sizes are presented in a page size control.

To create a new page size, call SpoolCreatePaperSize() . This routine takes a page type, a set of dimensions, and a string to describe those dimensions to the user. It returns a new page size number. To destroy a page size, call SpoolDeletePaperSize() .

To find out or change the default page size, call SpoolGetDefaultPageSizeInfo() or SpoolSetDefaultPageSizeInfo() .

| Up: GEOS SDK TechDocs | Up | Prev: 7.2 Printer Drivers | Next: 8 Debugging Tips


Debugging Tips

The Spool Library: 8 Debugging Tips

| Up: GEOS SDK TechDocs | Up | Prev: 7 Other Printing Components

Though GEOS provides an easy-to-use printer interface, mistakes are sure to happen. You may find the following information useful in catching your mistakes or for testing rigorously.

Under Swat, you can see the various threads the Spooler works with. There is always one spool thread running in the background. When the first job is queued on a printer, you can see that a new thread is created for that printer. When the last job for a printer is completed, the thread exits again.

Here are the most common printing mistakes:

  • Forgetting to add the Print Control object to the application's Active List (the GAGCNLT_SELF_LOAD_OPTIONS GCN list).
  • Not setting the document name (see Document Name Output ).
  • Setting the number of pages (page range) incorrectly.
| Up: GEOS SDK TechDocs | Up | Prev: 7 Other Printing Components


Socket

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Introduction
| One-page Version of this Chapter for Easy Printing

This chapter describes interactions with the Socket library, used for managing communications between threads -- threads on different machines or two GEOS threads on the same machine (via the "loopback" communication domain). It provides an API which is mostly independent of the medium used for inter-machine communication.

It is common to spawn a separate thread to poll a socket for incoming data; See the Multithreading chapter to learn about managing threads. Sample applications are located in APPL\SDK_C\MultThrd. Some socket operations require knowledge of the communications network used.

1 Introduction
2 Simple Example
3 Addresses
3.1 Letting the User Choose an Address
3.2 Managing Addresses
3.3 Hardware Ports
4 Making a Connection
5 Accepting a Connection
6 Loading on Connection
7 Sending and Receiving Data
7.1 Urgent Data
7.2 SocketSend() and SocketRecv()
7.3 Options
8 Closing the Connection
9 Checking the Connection
10 Domain-Specific Information
10.1 TCP/IP--GEOS Specific
10.2 TCP/IP--Standard
10.3 Loopback

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Introduction
| One-page Version of this Chapter for Easy Printing


Introduction

Socket: 1 Introduction

| Up: GEOS SDK TechDocs | Up | Prev: Socket | Next: 2 Simple Example

The Socket library manages communications both between machines and between threads on the same machine. It provides a level of abstraction above individual communication protocols ( e.g. , IrLMP, TCP/IP). Programs using the Socket library can send and receive information packets without knowing much about the communications protocol by which those packets are being transmitted. Also, programs using the Socket library need not know about the hardware communication ports used -- whether communication is being carried out over an IR serial port, a modem, or a network connection -- the API is the same.

The Socket library supports different "delivery types"-- different levels of reliability in transmitting data. There is a trade-off: the more reliable delivery types require more set-up work by the programs using them. The following delivery types are available:

Datagram
Unreliable transmission of discrete packets. Packets may arrive out of order or not at all. It is up to the data sender to determine if re-transmission is necessary. This is the least demanding delivery type to set up, because it doesn't require any two-way connection.
Sequenced Packet
Sequenced Packet delivery means reliable transmission of discrete packets. The packets arrive in the order sent. The Socket library manages error checking and re-transmissions, as necessary. The program using a sequenced packet socket is responsible for establishing a two-way communications connection.
Stream
Stream delivery means the reliable transmission of a stream of bytes, as opposed to other delivery types which require that data be broken up into packets. The Socket library manages a stream of bytes, breaking it up into packets, transmitting the packets, and reconstructing the byte stream from packets on the other side. The Socket library also manages error-checking and re-transmission, as necessary. The program using a stream socket is responsible for establishing a two-way communications connection.

Addresses

Each side of the connection is represented by an address. An address is made up of several parts:

Domain
The network over which the connection is being made. Domains include TCP/IP, IRLAP, and "loopback" (used for connections between two threads on the same machine).
Address Data
This data identifies a machine on a network domain. The format of the address depends on the domain. In the case of TCP/IP, a sample address might be the string "geoworks.com". IrLAP addresses are 32-bit numbers. If the domain is "loopback", then no address information is necessary -- the sending and receiving machines are one and the same.
Port
This token identifies one side of a connection within one machine. Ports allow multiple data sockets to operate over one connection. Thus, connecting to "geoworks.com", one might send mail messages using port 25 and file-transfer commands via port 21. Port numbers are either 16-bit or 32-bit, depending on the domain.

The amount of overhead associated with sending and receiving data depends upon the devices involved, the domain, and the delivery type. When testing, experiment with different domains (if available), delivery types, and packet sizes to find a good balance of reliability and rapidity.

For any domain, there may be more than one address format. For instance, the TCP/IP address "geoworks.com" is conveniently human-readable, but cannot be used at the socket level to actually make a connection. Instead, you must resolve "geoworks.com" to a 32-bit address number. All socket operations require "primitive" addresses -- addresses resolved to their simplest form. To resolve an address, call SocketResolve() . We will discuss the details of working with addresses in more detail below.

Errors

Many of the Socket library routines return error values. Sometimes these error values are return values of routines; if you call a routine that can return an error value, be sure to check the return value and react appropriately to errors. Some routines don't return an error value directly, instead returning a flag to indicate that something has gone wrong. To find out exactly what went wrong, call ThreadGetError() .

Those Socket library routines that return error values do not set the thread error value. Calling ThreadGetError() after calling such a routine would not detect any error encountered by that routine.

ThreadGetError() returns a word-sized value. The low byte of this word is a SocketError value; the high byte is a SpecSocketDrError value. If the returned word is zero, that means that there was no error.

In general, a SpecSocketDrError value indicates a low-level error, and a SocketError value indicates a high-level error. In many cases, there will be a SocketError in the low byte, but the high byte will be clear -- there was no problem with the low-level communication medium, but a protocol exception may have occurred.

In general, only the low byte (the SocketError value) is useful in determining how to handle the exception. The high byte might affect what error message is displayed to the user. For example, it probably doesn't matter to your program whether the high byte was SSDE_LINE_BUSY or SSDE_NO_DIALTONE. In either case, it just means that communication could not be established. However, users might want to know the nature of the problem so that they can figure out how fix it.

Some of the more common SocketError values are listed below. In addition, certain routines may make use of other SocketError values, which will be discussed with those routines.

SE_NORMAL
There is no error. SE_NORMAL is guaranteed to equal zero.
SE_OUT_OF_MEMORY
The Socket library ran out of memory. Perhaps it was trying to receive or send a data packet that was too large.
SE_SOCKET_IN_USE
The socket was busy doing something else. Perhaps you tried to connect it, but it was already connected.
SE_SOCKET_BUSY
The socket was temporarily busy doing something else. Perhaps you should try again a few times.
SE_MEDIUM_BUSY
The hardware port which would be used to carry out the requested operation was busy. You might wait for the hardware port to become free; you could call SocketCheckMediumConnection() to see if the port is being used for a socket connection.
SE_ALL_PORTS_BUSY
To carry out the requested operation, the Socket library tried to use a port number; however all port numbers were already busy.
SE_SOCKET_NOT_BOUND
The socket was not bound ( i.e. , associated with a port number), but needed to be bound to carry out the requested operation. To bind a socket, call SocketBind() .
SE_SOCKET_NOT_LISTENING
The socket wasn't listening for incoming connections, and needed to be in this state to carry out the requested operation. To make a socket listen, call SocketListen() .
SE_SOCKET_LISTENING
The socket was busy listening for incoming connections when you asked it to carry out another operation. Remember that you often want separate sockets to handle listening and data transfer.
SE_SOCKET_NOT_CONNECTED
The socket was not connected, but a connection is required for the operation you requested. To connect the socket, call SocketConnect() .
SE_SOCKET_CLOSED
The socket was closed, making the requested operation impossible.
SE_CONNECTION_CLOSED
The other side of your socket's connection has closed that connection; to close up your side of the connection, call SocketClose() .
SE_CONNECTION_FAILED
The connection was lost, probably due to some low-level link failure. There may have been a problem with the physical connection or with the domain-specific software.
SE_CONNECTION_RESET
The connection was lost, probably due to this side of the connection intentially making a low-level disconnection; e.g. , the sort of disconnection that results from SocketCloseDomainMedium() or the Socket's owning thread exiting.
SE_TIMED_OUT
The Socket library was unable to carry out the requested operation before the time-out time was reached. Beware: Depending on the domain, for certain phases of certain operations, there may be low-level time-outs which can occur even if you have specified a longer or an infinite time-out.
SE_CANT_LOAD_DRIVER
The Socket library was unable to load a driver necessary to carry out the requested operation.
SE_UNKNOWN_DOMAIN
The Socket library didn't recognize the domain name.
SE_DESTINATION_UNREACHABLE
The requested destination address was unreachable over the network.
SE_LINK_FAILED
The low-level link used for this connection failed or could not be established. There may have been a problem with the physical connection or with the domain-specific software.
SE_SYSTEM_SHUTDOWN
This error signals that GEOS is shutting down. Your program should probably either end its connections gracefully or attempt to delay the shutdown. Note that it is possible for a shutdown to occur without generating this signal.
SE_INFO_NOT_AVAILABLE
The requested information was not available.
SE_DOMAIN_REQUIRES_16BIT_PORTS
You tried to use a 32-bit port number in a domain that does not support them. Use MANUFACTURER_ID_SOCKET_16BIT_PORT as the ManufacturerID of your port number.
SE_INTERRUPT
The operation was stymied by SocketInterrupt() .
SE_INTERNAL_ERROR
The Socket library did something wrong. You should take advantage of whatever developer support to which you have access. They will want a back-trace and other information necessary to duplicate the error.
| Up: GEOS SDK TechDocs | Up | Prev: Socket | Next: 2 Simple Example


Simple Example

Socket: 2 Simple Example

| Up: GEOS SDK TechDocs | Up | Prev: 1 Introduction | Next: 3 Addresses

The sample application Appl/SDK_C/Talk provides an example of a simple application using the Socket library.

To understand how the program works, you should, of course, look at its source code. The following is a high-level overview of what the Talk example illustrates: a simple client-server connection in which the server awaits connections, the client connects, and then the two exchange data. Note: this application assumes that the host device (the client) has already connected to the server. Remote connections are usually made through PPP or Point-to-Point Protocol. See SocketOpenDomainMedium() discussed in Hardware Ports .

The example performs the following steps:

  1. Allocate memory in which to store data.
     When sending or receiving data, you need to provide a pointer to a buffer containing the data to be sent or received. Thus, the buffer should be in a block which can either be locked down as needed or is in fixed memory. Programs often spawn a separate thread to receive data from the socket; if this thread needs to lock down a memory block in which to store the data, then make sure that the thread can lock down the memory block. See the Multithreading chapter to learn about managing threads.
    (If you follow the multi-threaded approach, be aware that when the Socket's owning thread exits, it frees the Socket. To allow a Socket to survive the exit of its thread, use SocketSetIntSocketOption() to change its owning thread.)
    For this simple example, we're using a small buffer to handle the receipt of data, so we declare it as a global variable so it resides in the application's fixed memory:
char recvBuf[16];
  • Client Side: Make Sure Connected to Domain
     Depending on the nature of the device and application, you may need to make sure that the device is connected to the communication domain. In the Talk sample application, we make sure that the device is dialed in to an ISP: an AccessPointControl object prompts the user to choose an ISP, and a call to SocketOpenDomainMedium() opens a PPP connection to that ISP.
  • Client Side: Get Address For Other Side of Connection.
     The Talk application allows the user to connect with another machine by specifying that machine's address. The Talk application creates a controller gadget by which the user can specify an address. SocketGetAddressController() returns the controller class' pointer; ObjInstantiate() creates an object of the class.
    The following code is called when the application opens. It creates the controller gadget, initializes its data, and places it in a dialog box.
ctrlClass =
  SocketGetAddressController(theAddress.SA_domain));
ctrlOb =
  ObjInstantiate(
    HandleOf(GeodeGetOptrNS(@AddressDialog)),
    ctrlClass);
@call ctrlOb::MSG_SOCKET_ADDRESS_CONTROL_SET_ACTION(
  ConstructOptr(0,TO_PROCESS), MSG_CTP_VALID);
@call
  GeodeGetOptrNS(@AddressDialog)::MSG_GEN_ADD_CHILD(
    ctrlOb, CCO_FIRST);
@call ctrlOb::MSG_GEN_SET_USABLE(VUM_NOW);

When the user enters an address, the following code is called. It gets the selected address from the address controller and packages it up into a form the Socket library can use.

MSG_SOCKET_ADDRESS_CONTROL_GET_ADDRESSES returns a chunk with raw address data based on the address entered by the user. SocketResolve() reduces this raw address data to a primitive form that can be used for making connections.

adArray = @call ctrlOb::MSG_SOCKET_ADDRESS_CONTROL_GET_ADDRESSES();
address1 = ChunkArrayElementToPtrHandles(
  HandleOf(ctrlOb), adArray, 0,0);
theAddress.SA_addressSize =
  SocketResolve(
    theAddress.SA_domain, (byte *)&address1[1],
    address1->SACA_opaqueSize, addressBuffer,
    MAX_ADDRESS_SIZE);
LMemFreeHandles(HandleOf(ctrlOb), adArray);
  • Client Side: Create and Connect Socket.
     The Client side now creates a socket for its side of the connection and attempts to connect to the address extracted from the address controller. The socket will be of the stream delivery type.
dataSocket = SocketCreate(SDT_STREAM);
SocketConnect(
  dataSocket, (SocketAddress *) &theAddress,
  SOCKET_NO_TIMEOUT);
  • Server Side: Create Socket and Accept Connection(s)
     On the server side, the application spawns a separate thread to handle incoming connections.

There are two "sockets" created on this, the server side of the connection. The first socket is bound to a particular port, and "listens" for incoming connections. It is possible to accept more than one connection over the listening socket--each accepted connection is represented by another socket to represent this side of the new particular connection.

listenSocket = SocketCreate(SDT_STREAM);
SocketBind(listenSocket, theAddress.SA_port, 0);
SocketListen(listenSocket, 5);
dataSocket = SocketAccept(
  listenSocket, SOCKET_NO_TIMEOUT);
  • Either Side: Send Data
     In this example, the data to send is taken from a text object. The program loads the text into a block of memory, locks the block down to get a pointer to it, then transmits the data.
textBlock = @call
  GeodeGetOptrNS(@InText)::
  MSG_VIS_TEXT_GET_ALL_BLOCK(NullHandle);
textPtr = MemLock(textBlock);
textSize = LocalStringSize(textPtr);
SocketSend(
  dataSocket, textPtr, textSize, 0,
  (SocketAddress *)0);
MemFree(textBlock);
  • Either Side: Wait For and Receive Data
     The application's listening thread waits for data to arrive and stores it away in a buffer when it does arrive. It processes the incoming data and gets ready for the next piece of data.
while ((
  datasize = SocketRecv(
    dataSocket, recvBuf, sizeof recvBuf,
    SOCKET_NO_TIMEOUT, 0, (SocketAddress *)0))
  != 0)
{ @call
  GeodeGetOptrNS(@OutText)::MSG_VIS_TEXT_APPEND_PTR(
    recvBuf, datasize); }
  • Either Side: Close Connection
     The application has a Close button. When the user presses this button, the application shuts down its side of the connection. The SocketCloseSend() routine partially shuts down the socket so that it can receive data, but cannot send it; also, the routine sends a signal to the other side that the connection is closing.
if (listenSocket) {
  SocketClose(listenSocket);
  listenSocket = 0; }
SocketCloseSend(dataSocket);
  • Either Side: Detect Closed Connection and Close Socket
     When receiving data, if either side seems to have received a zero-length packet, it checks to see if an exception has occurred, specifically to see if the connection has closed. (When SocketRecv() detects an error, it sets the thread error value, which may be retrieved via ThreadGetError() .) If the connection has closed, then Talk closes the sockets on this side of the connection.
if (ThreadGetError() == SE_CONNECTION_CLOSED) {
  if (listenSocket) {
    SocketClose(listenSocket);
    listenSocket = 0; }
  SocketClose(dataSocket); }
  • Both Sides: Sockets Freed When Thread Exits
     Lest you are worried about these Sockets you have created but not freed, know that they will be freed automatically when the owning thread exits. (To allow a Socket to survive its thread's exit, you must assign the Socket to a new thread.)
| Up: GEOS SDK TechDocs | Up | Prev: 1 Introduction | Next: 3 Addresses


Addresses

Socket: 3 Addresses

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Simple Example | Next: 4 Making a Connection

In the example above, the application queried the user for a connection address. This is a fairly common case. However, some programs need to use hard-wired addresses. Perhaps you want to allow connections over all available domains. Thus, a few words about addresses may be in order.

Addresses are usually represented via the SocketAddress structure:

typedef struct {
    SocketPort 		SA_port;
    word 		SA_domainSize;
    char *		SA_domain;
    word 		SA_addressSize;
} SocketAddress;
/* domain-specific address data here */
SA_port
Port numbers identify a particular line of communication within a machine. Port numbers may be 32-bit or 16-bit, depending on the domain. They are specified via the SocketPort structure, made up of a 16-bit ManufacturerID and 16-bit arbitrarily-chosen token number. If the domain uses 16-bit socket numbers, then use MANUFACTURER_ID_SOCKET_16BIT_PORT as the ManufacturerID . If the domain allows 32-bit socket numbers, then you may use MANUFACTURER_ID_SOCKET_16BIT_PORT or some other ManufacturerID , probably your own ID or that of some standard service provider. This numbering scheme helps to avoid overlapping port numbers.
SA_domain , SA_domainSize
The domain identifies the protocol of the network by which the addressed machine may be reached. The domain is specified by a string. In a SocketAddress structure, SA_domain is a pointer to the null-terminated domain name string and SA_domainSize is the size of the buffer containing the domain name string.
Many communications protocols are hard-wired to work with a particular domain. To find out all domains available to the user's device, call SocketGetDomain(). (See Letting the User Choose an Address .)
SA_addressSize , Address Data
The format of the address data used to identify a machine within a domain depends on that domain. The SA_addressSize field of the SocketAddress structure contains the size of the address data. The buffer containing the address data should fall immediately after the SocketAddress structure. Read the documentation for a given domain to find out the format for its addresses.
You'll want to make sure that space for this data is, in fact, allocated at the end of the structure. If you declare a socket address, you will define a struct, probably based on SocketAddress , that has the fields of SocketAddress , but also has a buffer to hold the resolved address data. More likely than not, you won't need to declare or define such a structure -- you're more likely to use SocketCreateResolvedAddress() or bypass the creation of this structure altogether.

Before using an address, make sure that it is in its primitive form. For instance, an IP address like "geoworks.com" is not in its primitive form -- the IP address must be translated into a four-byte number before it can be used. Use SocketResolve() (described below) to transform an address into its primitive form. Note that the address passed to SocketResolve() is not in a SocketAddress structure.

Letting the User Choose an Address

Managing Addresses

Hardware Ports

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Simple Example | Next: 4 Making a Connection


Letting the User Choose an Address

Socket: 3.1 Addresses: Letting the User Choose an Address

| Up: GEOS SDK TechDocs | Up | Prev: 3 Addresses | Next: 3.2 Managing Addresses
SocketAddressControllerClass, SocketGetDomains(), SocketGetAddressController(), SocketCreateResolvedAddress(), SocketResolve(), SocketInterruptResolve()

Given a domain, you can request the Socket library to provide UI gadgetry so that the user can choose an address within that domain. Many programs only support socket connections over one domain. However, if you need a list of all domains supported in the user's environment, call SocketGetDomains() .

The SocketGetDomains() routine takes the optr of a chunk array and fills in that array with elements. Each element contains a (non-null-terminated) string which is the name of a supported domain. This chunk array has a standard ChunkArrayHeader header and variable-sized elements.

If you want the routine to allocate the chunk array for you, pass an optr that contains a NullChunk ( i.e. , a null ChunkHandle):

MemHandle	myHandle;
Optr	myOptr;
myHandle = OptrToHandle(myHandle);
myOptr = ConstructOptr(myHandle, NULL);

For more information about working with chunk arrays, see the Local Memory chapter. In short, to manage the chunk array of domain names:

  1. Lock down the local memory heap with MemLock() .
  2. Use LMemDerefHandles() to get a pointer to the chunk array.
  3. To get the number of domains, call ChunkArrayGetCount() .
  4. To get a pointer to the nth string in the chunk array, call ChunkArrayElementToPtr() or ChunkArrayElementToPtrHandles() .
  5. When finished reading domain strings, call MemUnlock() on the local memory heap.

Given a domain name, you can create some UI gadgetry for choosing addresses within that domain. As shown in the Appl/SDK_C/Talk example, call SocketGetAddressController() to get the class of an appropriate controller. This class is guaranteed to support the messages shown in SocketAddressControllerClass .

Code Display 23-1 SocketAddressControllerClass

/* Domain-specific subclasses of SocketAddressControllerClass all support
 * the following messages. MSG_SOCKET_ADDRESS_CONTROL_GET_ADDRESSES is probably
 * the most commonly used message. */
/*
 * Build the block of data that holds the addresses selected by the user
 * to pass to the transport driver. If OK, returns ChunkArray of SACAddress
 * structures in same block as controller. Else, returns 0.
 * As of this writing, all address controllers return at most one address
 * in the list of addresses.
 * The returned address(es) are not in its primitive form; use
 * SocketResolve() to transform the address into something usable.
 */
 @message word MSG_SOCKET_ADDRESS_CONTROL_GET_ADDRESSES();
  typedef struct {
    word 		SACA_opaqueSize;
    /* label byte 		SACA_opaque;    Pass this to SocketResolve */
    /* after the opaque address, there is a null-terminated
     *  human-readable form of the address. */
} SACAddress;
/*
 * Inform the subclass of the type of medium selected by the user
 * so it can customize its appearance appropriately, should the controller
 * handle media that have different ways of addressing.
 */
@message void MSG_SOCKET_ADDRESS_CONTROL_SET_MEDIUM (
                MediumType mediumType);
/*
 * Set the current user-editable address to the passed address.
 */
@message void MSG_SOCKET_ADDRESS_CONTROL_SET_ADDRESSES(optr addr);
/*
 * Inform the controller of the message and destination object it should use to
 * notify the invoker that a valid address exists.
 */
@message void MSG_SOCKET_ADDRESS_CONTROL_SET_ACTION (
                optr dest,
                word actionMsg);
/*
 * Let the containing dialog know whether the address we've got is a
 * valid one, so it can decide whether to allow the user to send the
 * message.
 */
@message void MSG_SOCKET_ADDRESS_CONTROL_SET_VALID_STATE(Boolean valid);

You cannot use an address with Socket library routines unless that address is in its primitive form. This primitive form is created by taking a higher-level form of the address and passing it to the SocketCreateResolvedAddress() routine or the SocketResolve() routine. Exactly how the Socket library resolves the address depends upon the domain.

Recall that addresses returned by the address controller are not in their primitive form. The primitive form of an address can be volatile: feel free to re-use them if making several connections over a short period of time, but re-resolve the address if it's been a long time since it was last resolved. Note that there can be a large overhead when it comes to resolving addresses. Also note that IRLAP numeric addresses are especially volatile, changing every time the protocol connection starts, i.e., every time the IRLAP driver is loaded, either for the first time or after more than a minute of inactivity.

The SocketCreateResolvedAddress() routine takes a pointer to a domain name and raw address data and returns a buffer with a SocketAddress structure and the address data. Depending on what you're going to use the address for, you may need to fill in the structure's SA_port field.

SocketResolve() is the same as SocketCreateResolvedAddress() except that SocketResolve() takes a buffer to hold the resolved address data. If the buffer isn't big enough, SocketResolve() returns SE_BUFFER_TOO_SMALL. Depending on the domain, calling SocketResolve() on an address already in its primitive form may have undesired results.

The resolution process may or may not require network access, depending on the domain and the input format. Depending on the domain, the addressing system may be volatile. Under these conditions, put off resolving the address as long as possible, until just before using the address; there will be less time for the address to go bad. If the network goes down, the Socket library might not be able to resolve addresses. In this case, SocketCreateResolvedAddress() or SocketResolve() returns SE_TEMPORARY_ERROR.

If you wish to interrupt the resolution process (perhaps you allow the user to cancel the operation), use the SocketInterruptResolve() function. This function attempts to halt the resolution process. It is, however, unreliable. It may not succeed in interrupting the resolution process; it may also interrupt other resolves happening at the same time.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Addresses | Next: 3.2 Managing Addresses


Managing Addresses

Socket: 3.2 Addresses: Managing Addresses

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Letting the User Choose an Address | Next: 3.3 Hardware Ports
SocketGetAddressSize(), SocketGetSocketName(), SocketGetPeerName()

Most programs blindly use addresses supplied by the user or use only hard-wired addresses. Only a few programs need to access and manipulate addresses. The Socket library provides a few utility routines for this purpose.

To find out the maximum address data size for a given domain, call SocketGetAddressSize() . This can be helpful when allocating memory to handle a full address, such as that returned by the SocketGetSocketName() and SocketGetPeerName() functions.

If a socket is connected, call SocketGetPeerName() to get the address of the socket at the other end of the connection. To find out the address used to represent the socket's own side of the connection, call SocketGetSocketName() .

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Letting the User Choose an Address | Next: 3.3 Hardware Ports


Hardware Ports

Socket: 3.3 Addresses: Hardware Ports

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Managing Addresses | Next: 4 Making a Connection
SocketGetDomainMedia(), SocketGetAddressMedium(), SocketCheckMediumConnection(), SocketGetMediumAddress(), SocketOpenDomainMedium(), SocketCloseDomainMedium()

Most socket connections involve the use of some hardware "port" -- some physical connection to the outside world. To determine which sort of hardware ports the user's machine uses to connect to a given domain, call SocketGetDomainMedia() . This returns a chunk array of MediumType structures. Given a choice of domains to make a given connection, consider which medium would be used to make the connection.

To find out which port the Socket library would use to connect to a given address, call SocketGetAddressMedium() . Depending on the medium, you might try to connect to the other site via some other domain or decide not to make the connection at all.

To check a given hardware port is being used for an active connection, call SocketCheckMediumConnection() . If a point-to-point connection exists, this routine returns the address of the remote site. If there is a connection, but not a specific address, then the routine returns a null address. If there is no connection through the hardware port, the routine returns a SocketError value.

To find out the address of the local machine on a connection over a particular medium, use the SocketGetMediumAddress() routine. This is the address another machine would use to connect to this machine via that medium.

The SocketOpenDomainMedium() routine opens a "raw" connection using the specified medium. Exactly what is meant by a "raw" connection depends upon the domain involved. In the case of TCP, this routine will dial the modem and create a PPP connection to the PPP server but will not send any TCP packets.

It is possible to "hang up" a hardware port. Call SocketCloseDomainMedium() to force a domain to close a medium. You may request that the medium only be closed if it is not being used ( i.e. , there are no active connections using the medium). If any socket connections are using the port, they will receive the error SE_CONNECTION_RESET. Later, we will discuss orderly ways to close a connection, being careful to send proper notification to the other side of the connection.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Managing Addresses | Next: 4 Making a Connection


Making a Connection

Socket: 4 Making a Connection

| Up: GEOS SDK TechDocs | Up | Prev: 3 Addresses | Next: 5 Accepting a Connection
SocketCreate(), SocketConnect(), SocketInterrupt()

To make a connection, a program creates a socket and specifies an address to which to connect that socket.

To create a new socket, call SocketCreate() . You must specify the socket's delivery type: datagram, sequential packet, or stream.

Before sending or receiving data over a sequential packet or stream socket, you must "connect" it to the place it will be sending the data to or receiving it from.

Depending on the medium and device, you may need to make sure that the device is connected to a network. For example, for a Nokia 9000i Communicator to make a TCP/IP connection, it must first have made a PPP connection. Use the SocketOpenDomainMedium() routine to make the "raw" network connection.

To make a connection, call SocketConnect() . Pass the socket created via SocketCreate() , the address to connect to, and a time-out value. If it returns SE_NORMAL, then the connection was established successfully. If it returns SE_CONNECTION_REFUSED, the remote site wasn't prepared to accept the connection. If it returns SE_CONNECTION_ERROR, the connection didn't go through for some miscellaneous reason. If it returns SE_NON_UNIQUE_CONNECTION, then the attempted connection would have been identical to another connection: same source address, source port, destination address, destination port, and delivery type. There would be no way to tell these connections apart. If you try to open the same connection from both sides simultaneously, they may both fail with SE_NON_UNIQUE_CONNECTION.

To cancel the connection operation, call SocketInterrupt() .

Datagram sockets cannot form a true connection -- they are unreliable. Sockets must be able to send and receive datagram packets without relying on a permanent connection. However, using SocketConnect() with a datagram socket specifies a default destination address to use when sending packets.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Addresses | Next: 5 Accepting a Connection


Accepting a Connection

Socket: 5 Accepting a Connection

| Up: GEOS SDK TechDocs | Up | Prev: 4 Making a Connection | Next: 6 Loading on Connection
SocketBind(), SocketBindInDomain(), SocketListen(), SocketAccept()

There's a bit of work involved in setting up a program to receive connections from other sockets. The program must create a socket and associate it with a port. If there is to be a sequenced-packet or stream delivery connection, then the program needs to do some more work to listen for and accept these connections.

If a program creates a socket that accepts connections, the program should probably spawn a separate thread for each accepted connection. If this is not practical, then see SocketCheckReady() for a way to monitor several sockets with a single thread.

Call SocketCreate() to create a socket for this side of the connection. If the socket is to receive datagram packets, then it must be of type datagram. If the other socket in the connection will be a sequenced packet or stream delivery type, then the socket accepting the connection must be either sequenced packet or stream delivery type; however, the two sockets need not be of the same type.

(If you follow the multi-threaded approach, be aware that when the Socket's owning thread exits, it frees the Socket. To allow a Socket to survive the exit of its thread, use SocketSetIntSocketOption() to change its owning thread.)

The other side of the connection attempts to hook up to a port number on this side of the connection. If a socket is to intercept connections to a certain port, it must somehow associate itself with that port. Use the SocketBind() or SocketBindInDomain() routines to associate a socket with a port number. SocketBind() associates the socket with a port number. SocketBindInDomain() associates the socket with a port number but only within a specific domain.

For any given domain, normally only one socket on the machine may be bound to any port number. Thus, if you call SocketBindInDomain() to bind socket A to port 3 in the TCP/IP domain, then:

  • You could use SocketBindInDomain() to bind socket B to port 3 in the IRLAP domain, but
  • You couldn't use SocketBindInDomain() to bind socket C to port 3 in the TCP/IP domain, and
  • You couldn't use SocketBind() to bind socket D to port 3.

There are some ways around this restriction:

  • To force SocketBind() or SocketBindInDomain() to re-use some port that another socket has bound, then pass the SBF_REUSE_PORT flag. Otherwise, be prepared to receive SE_PORT_IN_USE or SE_BIND_CONFLICT. Even the SBF_REUSE_PORT flag won't allow you to use both SocketBind() and SocketBindInDomain() on a single port. E.g., in the example above, you could use SocketBindInDomain() with SBF_REUSE_PORT to bind socket C to port 3 in the TCP/IP domain, but you could not use SocketBind() with SBF_REUSE_PORT to bind socket D to port 3. You may not use SBF_REUSE_PORT with datagram-based sockets.
  • If there are only non-datagram-based sockets bound to a port, you may bind a datagram-based socket to that port without setting SBF_REUSE_PORT. The reverse is also true. Recall that only a datagram socket can connect with another datagram socket; non-datagram sockets connect only with other non-datagram sockets.
  • A given socket may only be bound to one port. Trying to bind it to another port results in SE_SOCKET_ALREADY_BOUND. Create another socket to bind to the other port.

Always check for bind conflicts. Bind conflicts may occur even though a program is well-behaved: other programs may have bound ports to a socket. For reasons described in Loading on Connection , sometimes the Socket library itself may monitor a port.

When setting up a sequenced packet or stream delivery connection, a bit more set-up work is necessary. These delivery types require a "connection."

To listen for incoming sequenced packet or stream connections, call SocketListen() . The socket must be bound to a port before it can listen. To allow more than one connection through the port, specify the maximum number of connections as an argument to SocketListen() . The SocketListen() routine causes a socket to listen for incoming connections. If another socket is listening at the port, SocketListen() returns SE_PORT_ALREADY_LISTENING.

To make a socket wait for and accept a connection, call SocketAccept() . The socket must be listening for connections (processed by SocketListen() ). Because the thread blocks until a connection is made or SocketAccept() times out, programs normally don't call this routine in their main execution thread. As in the Appl/SDK_C/Talk sample application, a separate thread handles accepting a connection and receiving data from the connection.

If a connection is made, SocketAccept() returns a Socket used to represent the connection. Do not confuse this connected socket with the listening socket. You might be able to accept more connections from the listening socket.

If SocketAccept() returns SE_LISTEN_QUEUE_EMPTY, then there were no connections waiting to be accepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Making a Connection | Next: 6 Loading on Connection


Loading on Connection

Socket: 6 Loading on Connection

| Up: GEOS SDK TechDocs | Up | Prev: 5 Accepting a Connection | Next: 7 Sending and Receiving Data
SocketAddLoadOnMsg(), SocketAddLoadOnMsgInDomain(), SocketRemoveLoadOnMsg(), SocketRemoveLoadOnMsgInDomain()

It's all very well that you can tell a running program to accept packets and connections over sockets. However, it seems unreasonable to ask the user to run these programs by hand any time there might be a pending connection.

You can tell the Socket library to load a particular geode when it receives data on a particular port, or on a particular port via a particular domain. For an example, see the Appl/SDK_C/Talk sample application: if you run Talk and press the Autoload button, the Socket library knows to start up the Talk program because it senses that it is receiving an incoming Talk connection.

When the program starts up, it should probably create a socket, bind it to the appropriate port, and make that socket listen. If the listening socket is then destroyed, then any unaccepted connections are canceled.

The Socket library writes out an entry to the GEOS.INI file in the [socket] category and with key "LoadOnMsg". The Socket library consults these GEOS.INI file entries when it starts up and will act accordingly when it receives packets.

To make the Socket library load a program when receiving packets on a particular port, call SocketAddLoadOnMsg() . Specify the program by means of its path. To load a program when receiving packets on a particular port on a particular domain, call SocketAddLoadOnMsgInDomain() .

To remove the entries from the GEOS.INI file and tell the Socket library not to try to load the program when receiving packets, call either SocketRemoveLoadOnMsg() or SocketRemoveLoadOnMsgInDomain() .

To listen for incoming data in this manner, the Socket library effectively binds a socket to monitor the port. Thus

  • SocketAddLoadOnMsg() and SocketAddLoadOnMsgInDomain() can run into bind conflicts just as SocketBind() and SocketBindInDomain() do.
  • SocketAddLoadOnMsg() interferes with SocketAddLoadOnMsgInDomain() and SocketBindInDomain() on the same port.
  • SocketAddLoadOnMsgInDomain() interferes with SocketAddLoadOnMsg() and SocketBind() on the same port.
| Up: GEOS SDK TechDocs | Up | Prev: 5 Accepting a Connection | Next: 7 Sending and Receiving Data


Sending and Receiving Data

Socket: 7 Sending and Receiving Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Loading on Connection | Next: 8 Closing the Connection
SocketSend(), SocketRecv(), SocketSetIntSocketOption(), SocketGetIntSocketOption(), SocketSetSocketOption(), SocketGetSocketOption()

Once both sides of a socket communication are set up, they're ready to send data back and forth. The SocketSend() and SocketRecv() routines, described below, allow a socket to transmit and receive data, respectively.

The amount of overhead associated with sending and receiving data depends on the devices involved, the domain, and the delivery type. Be certain that if just one byte of data is sent at a time, time performance will suffer.

Whenever possible, the sender should gather up small pieces of data and send them in one large packet. The optimal size of a packet depends on the device, domain, and delivery type. When designing a communications protocol, it would be worthwhile to figure out which sorts of data may be combined into larger blocks. Note that in some domains, the driver will try to do this gathering of small blocks into large packets for you.

Urgent Data

SocketSend() and SocketRecv()

Options

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Loading on Connection | Next: 8 Closing the Connection


Urgent Data

Socket: 7.1 Sending and Receiving Data: Urgent Data

| Up: GEOS SDK TechDocs | Up | Prev: 7 Sending and Receiving Data | Next: 7.2 SocketSend() and SocketRecv()

The Socket library supports the notion of urgent data. This allows a short packet of data to cut in front of other packets. To send a packet marked urgent, pass the SSF_URGENT flag to SocketSend() .

In some domains, only one byte of data in a packet can be marked urgent. In such a domain, passing SSF_URGENT with a 32-byte packet to SocketSend() results in two packets being sent: a 1-byte urgent packet and a 31-byte non-urgent packet. The data of the 1-byte urgent packet is the last byte of the original 32-byte packet. Check your domain documentation for size limits on urgent packets.

Urgent data may be delivered out of order. It may arrive out of sync with non-urgent packets; it may arrive out of order with other urgent packets. There is no queue for urgent data. Each urgent data packet received causes the previous urgent data to be discarded.

The SocketRecv() routine, in charge of receiving incoming packets, may be instructed to receive only urgent data or only non-urgent data. If there is pending urgent data and SocketRecv() is told to poll for normal data, it will return an error value, SE_URGENT, signalling that the program should call SocketRecv() again, this time to receive urgent data. (If, instead, you call SocketRecv() telling it to receive normal data again, it will return any pending normal data; it will not generate the SE_URGENT exception for the pending piece of urgent data -- unless another piece of urgent data has arrived in the meantime.)

Datagram connections do not support urgent data; SocketRecv() generates an SE_URGENT error on a datagram connection, it's really generating a SE_EXCEPTION value. If you receive such an error, it is a sign that the network is having troubles and packets may not be getting through.

You may force incoming urgent data to be treated in the same manner as normal data by setting the socket's "in-line" option. See SO_INLINE: In-line urgent data To force urgent data to be treated the same as normal data. This allows you to poll for both urgent and normal data with a single SocketRecv() command. Of course, setting this option means that you will not receive for information about socket options.

| Up: GEOS SDK TechDocs | Up | Prev: 7 Sending and Receiving Data | Next: 7.2 SocketSend() and SocketRecv()


SocketSend() and SocketRecv()

Socket: 7.2 Sending and Receiving Data: SocketSend() and SocketRecv()

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 Urgent Data | Next: 7.3 Options

The SocketSend() routine sends data over a socket. It takes several arguments:

Socket
The socket through which to send data
Buffer
A buffer containing the data to send
Buffer Size
The size of the data buffer.
Optional Address
Address to which to send data. If you pass an address here, make sure that you set the Address flag in the Flags field. If you don't set the Address flag, then this Optional Address argument may be a null pointer.
For sequenced packet or stream delivery type sockets, optional address is ignored.
For datagram delivery type sockets, specify the destination address of the packet in this parameter unless the socket has a default destination address as described below.
Datagram sockets can use the SocketConnect() routine to specify a default destination address. If they have no default address, you must specify the destination address for the packet. If there is a default destination address and you pass this optional address, then the optional address overrides the default for this send.
The following flags are available:
SSF_ADDRESS
To pass an optional address.
SSF_URGENT
To mark the data packet as urgent.
SSF_OPEN_LINK
To open the link if it is closed or to close the current link if it is idle and open it to a different location. If you do not set this flag, the datagram will only be sent if the link is opened to the desired remote link address.

Note that in some domains, the driver may delay before sending small packets of data because it is waiting for more packets to combine into one large packet. In many cases, this leads to more efficient communication. You may turn off this behavior by setting the "no delay" option. See SO_NODELAY: No Delay for small packets To force a driver to send small packets without waiting for other packets to bundle together. Drivers often delay sending small packets, waiting for other packets to bundle together in one larger packet. Th for information about setting socket options.

The SocketRecv() routine receives data from the socket. After filling the passed buffer with data, the socket discards its copy of the received data to make room for the next piece of incoming data.

Sequenced packet or datagram delivery sockets can only receive whole packets. If you grab data from the socket but don't grab all the data in the packet, the remaining data is lost. Thus, it's a good idea to establish a maximum packet size for sockets using these delivery types. If you're not sure how much room you'll need to receive a packet, call SocketRecv() with the SRF_PEEK flag. This allows you to "peek" at the incoming data without causing the socket to discard its copy.

The SocketRecv() routine takes the following arguments:

Socket
The socket from which to grab the data.
Buffer
Buffer to fill with data.
Buffer Size
Size of the data buffer. A packet-based socket will not retain data that does not fit in the buffer. A stream socket, however, will.
Time-out
The number of 1/60 second ticks to wait for incoming data. Pass SOCKET_NO_TIMEOUT to wait forever.
Optional Address
If you pass the Address flag of the Flags argument, then pass an empty SocketAddress buffer. (The buffer's SA_domain , SA_domainSize , and SA_addressSize fields must be initialized.) Make sure the buffer has room for the address data after the SocketAddress structure. Note that if either the domain name buffer or the address buffer isn't long enough to hold its string, the resulting truncated string isn't null-terminated.
If you do not set the Address flag, this Optional Address argument may be a null pointer.
The following flags are available:
SRF_ADDRESS
Return the source address in the Optional Address buffer.
SRF_URGENT
Return the first packet which has been marked urgent. (See Urgent Data .)
SRF_PEEK
Force the socket to not discard the data received.

The SocketRecv() routine returns the size of the received buffer. If this size is zero, there may be an error in the connection. Use ThreadGetError() to check for an error. If ThreadGetError() returns SE_NORMAL, then there was no error and you received a zero-length packet. For a datagram connection, an SE_EXCEPTION error indicates that the network is having problems and some datagrams may not be getting through.

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 Urgent Data | Next: 7.3 Options


Options

Socket: 7.3 Sending and Receiving Data: Options

| Up: GEOS SDK TechDocs | Up | Prev: 7.2 SocketSend() and SocketRecv() | Next: 8 Closing the Connection

To customize a socket's sending and receiving behavior, use SocketSetIntSocketOption() (or SocketSetSocketOption() if you are using Assembly language). The following options are available:

SO_RECV_BUF : Receive Buffer Size
To change the size of the buffer used to hold incoming data.
SO_SEND_BUF : Send Buffer Size
To change the size of the buffer used to hold outgoing data.
SO_INLINE : In-line urgent data
To force urgent data to be treated the same as normal data. This allows you to poll for both urgent and normal data with a single SocketRecv() command. Of course, setting this option means that you will not receive urgent data as soon as possible.
SO_NODELAY : No Delay for small packets
To force a driver to send small packets without waiting for other packets to bundle together. Drivers often delay sending small packets, waiting for other packets to bundle together in one larger packet. This is oftentimes more efficient.
SO_OWNER : Owning Thread for socket
To specify a new owning thread for a socket. When the owning thread detaches from the Socket library (this happens automatically when the thread exits), the socket will be freed; any attempts to use the freed socket will result in errors. To make a socket survive its thread, you must assign it to a new thread. This option does not actually affect how the socket sends or receives data.

You may find out the current options for a socket by calling SocketGetIntSocketOption() ( SocketGetSocketOption() if you're using Assembly language).

| Up: GEOS SDK TechDocs | Up | Prev: 7.2 SocketSend() and SocketRecv() | Next: 8 Closing the Connection


Closing the Connection

Socket: 8 Closing the Connection

| Up: GEOS SDK TechDocs | Up | Prev: 7 Sending and Receiving Data | Next: 9 Checking the Connection
SocketInterrupt(), SocketClose(), SocketCloseSend(), SocketReset(), SocketCloseDomainMedium()

To halt either an accept or receive operation in progress, call SocketInterrupt() . If no such operation is in progress but the socket is listening or connected, the next such operation will be interrupted. Look out for the error SE_SOCKET_NOT_INTERRUPTIBLE; this signals that a non-interruptible operation is taking place on the socket.

When it comes time to close the connection and/or free a socket, call SocketClose() or SocketCloseSend() .

SocketCloseSend() works only with connected sockets. It partially shuts down the socket so that it can no longer send data; it can, however, still receive data. (Passing a partially closed socket to SocketSend() results in an SE_SOCKET_CLOSED error.) SocketCloseSend() sends an SE_CONNECTION_CLOSED exception packet to the other side of the connection to let it know that it's starting to shut itself down. Upon receipt of this exception packet, the other side of the connection will probably want to begin closing itself down as well.

Not all domains support closing the "send" part of a socket. In this case, the routine will return SE_CLOSE_SEND_NOT_SUPPORTED and you must use SocketClose() instead to close the connection and free the socket.

SocketClose() closes the socket (shutting down both incoming and outgoing data) and frees it. This routine may be used on a socket in any state. You must use SocketClose() to close (and free) a datagram socket.

To close a connection with extreme prejudice, call SocketReset() . Use this routine with caution as it bypasses all normal protocol-level handshaking and unilaterally kills the connection. When using this routine, you should still call SocketClose() to free the socket data structures and flush any unread data.

The SocketCloseDomainMedium() routine can close all connections over a given medium without sending any notifications. It effectively hangs up the connection. You pass a flag to specify whether the routine should only hang up if there are no active connections through the medium, or hang regardless. For more information about socket media and this routine, see Hardware Ports .

| Up: GEOS SDK TechDocs | Up | Prev: 7 Sending and Receiving Data | Next: 9 Checking the Connection


Checking the Connection

Socket: 9 Checking the Connection

| Up: GEOS SDK TechDocs | Up | Prev: 8 Closing the Connection | Next: 10 Domain-Specific Information
SocketCheckListen(), SocketCheckReady()

The Socket library provides utility routines that query the status of a connection. This can be helpful when figuring out why a connection may not be working or confirming that a connection is working.

Use SocketCheckListen() on a port to find out the domain and medium for the first connection request on the port; that is, the first connection request which has not yet been accepted. Think of this as a limited form of "caller ID" -- a chance to find out about the incoming connection before accepting it. If SocketCheckListen() returns SE_PORT_NOT_LISTENING, it means there is no socket that is bound to the port and listening.

To "peek" at the next packet of incoming data, call SocketRecv() and pass the SRF_PEEK flag. This allows you to get the size of the next packet of incoming data and to look at its contents without destroying it.

To check one or more sockets to see if they have received data, received connection requests, or are ready to write, call SocketCheckReady() . It can check connections for incoming data or just incoming urgent data.

For programs which need to poll many sockets, SocketCheckReady() provides a tidy means to do this without spawning a thread for each socket.

The SocketCheckReady() routine takes an array of SocketCheckRequest structures. Each one of these structures contains a socket and a condition. SocketCheckReady() looks at each SocketCheckRequest structure in the array and returns the index of the first structure whose socket meets the condition.

Thus, to determine if a given socket is properly set up so that you can send data through it, pass SocketCheckReady() a one-element array (represented in pseudo-code):

{ mySocket, SC_WRITE, 0 }

To check several sockets to see whether any of them had received any connection requests, pass to SocketCheckReady() an array of the form (represented in pseudo-code):

{ socket1, SC_ACCEPT, 0 },
{ socket2, SC_ACCEPT, 0 },
{ socket3, SC_ACCEPT, 0 }

For any socket, you may check for one or more of the following conditions, as represented by SocketCondition values:

SC_ACCEPT
If a socket is listening for a connection, this condition indicates that another socket is trying to connect to the listening socket.
SC_READ
If a socket is connected, this condition indicates that a packet of data has come in and is ready to be read.
SC_EXCEPTION
If a socket is connected, this condition indicates that the socket has discovered something wrong with its connection.
SC_URGENT
If a socket is connected, this condition indicates that it has received a packet of data that was marked urgent.
SC_WRITE
This condition indicates that data may be sent through the socket.

If you query a socket about a condition that does not apply to its current state ( e.g. , ask a non-listening socket if it is ready to accept connections), then SocketCheckReady() returns SE_IMPROPER_CONDITION.

| Up: GEOS SDK TechDocs | Up | Prev: 8 Closing the Connection | Next: 10 Domain-Specific Information


Domain-Specific Information

Socket: 10 Domain-Specific Information

| Up: GEOS SDK TechDocs | Up | Down | Prev: 9 Checking the Connection | Next: 10.1 TCP/IP--GEOS Specific

Some portions of the Socket library API are domain specific; depending on which domain you're using, the domain name, address formats, port numbering schemes, etc. may differ.The following sections describe information specific to particular domains. Not all domains are available on any given device.

TCP/IP--GEOS Specific

TCP/IP--Standard

Loopback

| Up: GEOS SDK TechDocs | Up | Down | Prev: 9 Checking the Connection | Next: 10.1 TCP/IP--GEOS Specific


TCP/IP--GEOS Specific

Socket: 10.1 Domain-Specific Information: TCP/IP--GEOS Specific

| Up: GEOS SDK TechDocs | Up | Prev: 10 Domain-Specific Information | Next: 10.2 TCP/IP--Standard

The TCP/IP domain is a popular standard used for internet communications. The GEOS-specific version supports 32-bit port numbers. GEOS TCP/IP data may be transmitted via a regular TCP/IP network, though both the sending and receiving machines must support GEOS TCP/IP.

Domain Name:
"TCPIP_GEOS "
Port Numbers:
32-bit values.
Opaque Address Format:
This address will be either a TcpAccPntExtendedAddress ,a TcpOnlyExtendedAddress , or a TcpNonAccPntExtendedAddress .
TcpAccPntExtendedAddress: Use this structure when referring to an address identified by an access point ID number.
TcpOnlyExtendedAddress: Use this structure to identify a TCP address by its address instead of by its ID.
TcpNonAccPntExtendedAddress: Use this structure to identify a TCP address that is not a known access point. You will need to specify a link address (a phone number) as well as an IP address.
Notes:
  • On some devices (including the Nokia 9000i Communicator), you cannot make a TCP/IP connection or send packets until the device has a "raw" connection to the network--a PPP connection. Calling SocketOpenDomainMedium() with the TCP medium creates a PPP connection. The modem will dial, and the machine will connect to the PPP server, but no TCP level packets will be sent. If the PPP connection is already made, this routine will return SE_NORMAL, just as if the connection had been just now made. If the modem is already in use, an SE_MEDIUM_BUSY error will be returned. Use the SocketCloseDomaninMedium() to hang up the phone when done.
  • The Talk sample application shows how a device can make a raw PPP connection with the Internet Service Provider (ISP), getting the necessary information about the ISP from the built-in Access Point library. (Notice how the object AccpntControl is declared; notice the sample's TalkAddressClass handler for MSG_GEN_GUP_INTERACTION_COMMAND , which queries the AccessPointControl for the selected ISP and extracts the useful information about that ISP; finally, the MSG_CTP_CONNECT handler dials the phone to make the PPP connection.)

Code Display 23-2 Making the Raw TCP/IP Connection

Here we see three snippets of code from the Talk sample application
The AccessPointControl allows the user to choose an ISP:

@chunk char accpntMkr[] = "Access List";
@object AccessPointControlClass AccpntControl = {
    GI_states = GS_USABLE|GS_ENABLED;
    HINT_ACCESS_POINT_CONTROL_MINIMIZE_SIZE;
    ATTR_ACCESS_POINT_CONTROL_LIST_MONIKER = @accpntMkr; }

In @method TalkAddressClass, MSG_GEN_GUP_INTERACTION_COMMAND, we get information we'll need about the ISP

point = @call \
GeodeGetOptrNS(@AccpntControl)::MSG_ACCESS_POINT_CONTROL_GET_SELECTION();
/* store link info into address buffer */
rawAddress.UTA_link.TAPEA_linkSize = 3;
rawAddress.UTA_link.TAPEA_linkType = LT_ID;
rawAddress.UTA_link.TAPEA_accPntID = point;
/* the text of the address follows the link info */
alen = @call GeodeGetOptrNS(@IPText)::MSG_VIS_TEXT_GET_ALL_PTR(
                        (char *)&(rawAddress.UTA_ip[0]));
if (alen > MAX_IP_ADDR_STRING_LENGTH) FatalError(0);  /* too much text */
/* resolve the raw address into a SocketAddress */
theAddress.SA_addressSize = SocketResolve(theAddress.SA_domain,
                    (byte *)(&rawAddress),
                    sizeof(TcpAccPntExtendedAddress)+alen,
                    (byte *)(&addressBuffer),
                    MAX_ADDRESS_SIZE);

In MSG_CTP_CONNECT's handler, we make the PPP connection:

rval = SocketOpenDomainMedium((SocketAddress *) &theAddress, SOCKET_NO_TIMEOUT);
  • TCP/IP only supports one byte of urgent data in a packet. If you send a multi-byte packet marked urgent, it will be divided into two packets (on the receiving side). For example, a 32-byte packet marked urgent would be broken up into a 31-byte normal packet and a one-byte urgent packet.

    • If you set the SSF_URGENT flag to SocketSend() when sending a multi-byte packet, the last byte of the packet will be marked urgent.

    Recall that you can set the in-line option to specify that urgent data should be treated as normal data. In this case, urgent data will be treated as a normal 1-byte packet.

  • If you're not familiar with TCP/IP, TCP/IP illustrated by W. Richard Stevens provides a good introduction.

    The TCP/IP standard is defined in a number of RFCs. The RFCs listed below may be of interest. If you research these documents, note that the first is an index to the others.

    1720 J. Postel, I. Architecture Board (IAB), "INTERNET OFFICIAL PROTOCOL STANDARDS", 11/23/1994. (Obsoletes RFC1610) (Obsoleted by RFC1780) (STD 1)

    1700 J. Reynolds, J. Postel, "ASSIGNED NUMBERS", 10/20/1994. (Obsoletes RFC1340)

    1122 R. Braden, "Requirements for Internet hosts - communication layers", 10/01/1989.

    1123 R. Braden, "Requirements for Internet hosts - application and support", 10/01/1989.

    0791 J. Postel, "Internet Protocol", 09/01/1981. (Obsoletes RFC0760)

    0950 J. Mogul, J. Postel, "Internet standard subnetting procedure", 08/01/1985.

    0919 J. Mogul, "Broadcasting Internet datagrams", 10/01/1984.

    0922 J. Mogul, "Broadcasting Internet datagrams in the presence of subnets", 10/01/1984.

    0792 J. Postel, "Internet Control Message Protocol", 09/01/1981. (Obsoletes RFC0777)

    0768 J. Postel, "User Datagram Protocol", 08/28/1980.

    0793 J. Postel, "Transmission Control Protocol", 09/01/1981. (Updates RFC0761)

    1034 P. Mockapetris, "Domain names - concepts and facilities", 11/01/1987. (Obsoletes RFC0973) (Updated by RFC1101)

    1035 P. Mockapetris, "Domain names - implementation and specification", 11/01/1987. (Obsoletes RFC0973) (Updated by RFC1348)

    0974 C. Partridge, "Mail routing and the domain system", 01/01/1986.

    1531 R. Droms, "Dynamic Host Configuration Protocol", 10/07/1993. (Obsoleted by RFC1541)

    1533 S. Alexander, R. Droms, "DHCP Options and BOOTP Vendor Extensions", 10/08/1993. (Obsoletes RFC1497)

    1534 R. Droms, "Interoperation Between DHCP and BOOTP", 10/08/1993.

    1144 V. Jacobson, "Compressing TCP/IP headers for low-speed serial links", 02/01/1990.

    1547 D. Perkins, "Requirements for an Internet Standard Point-to-Point Protocol", 12/09/1993.

    1662 W. Simpson, "PPP in HDLC-like Framing", 07/21/1994. (Obsoletes RFC1549)

    1334 B. Lloyd, W. Simpson, "PPP Authentication Protocols", 10/20/1992.

    1661 W. Simpson, "The Point-to-Point Protocol (PPP)", 07/21/1994. (Obsoletes RFC1548)

    1570 W. Simpson, "PPP LCP Extensions", 01/11/1994. (Updates RFC1548)

    1663 D. Rand, "PPP Reliable Transmission", 07/21/1994.

    1332 PS G. McGregor, "The PPP Internet Protocol Control Protocol (IPCP)", 05/26/1992. (Obsoletes RFC1172)
  • Some (16-bit) constants (defined in sockmisc.h ) are available with the port numbers for common TCP services:
#define  ECHO            7  /* TCP/UDP */
#define  DISCARD         9  /* TCP/UDP */
#define  FTP_DATA        20 /* TCP */
#define  FTP             21 /* TCP */
#define  TELNET_SERVER   23 /* TCP */
#define  NAME_SERVER     42 /* UDP */
#define  WHOIS           43 /* TCP */
#define  DOMAIN_SERVER   53 /* TCP/UDP */
#define  FINGER          79 /* TCP */
  • The address data information with a SocketAddress structure takes the following form for the TCP domain:
    • typedef struct {
    word     TOEA_linkSize; 				/* 0 */
    byte    TOEA_ipAddr[4]; /* IP address */
}  TcpOnlyResolvedAddress;

    A variation on this structure is used for storing the address information for a TCP access point address. Along with the address information, there are three bytes of information about the nature of the link: the first of these bytes should have value LT_ID, so that the driver will know that the next two bytes represent a link ID; the other two bytes should be the access point's ID.

    typedef struct {
    word	TAPRA_linkSize; /* 3 */
    byte	TAPRA_linkType; /* LinkType (LT_ID) */
    word	TAPRA_accPntID;
    byte	  TAPRA_ipAddr[4]; /* IP address */
}  TcpAccPntResolvedAddress;
| Up: GEOS SDK TechDocs | Up | Prev: 10 Domain-Specific Information | Next: 10.2 TCP/IP--Standard


TCP/IP--Standard

Socket: 10.2 Domain-Specific Information: TCP/IP--Standard

| Up: GEOS SDK TechDocs | Up | Prev: 10.1 TCP/IP--GEOS Specific | Next: 10.3 Loopback

This is the standard TCP/IP domain, popular protocol for internet communications.

Domain Name:
"TCPIP"
Port Numbers:
16-bit values.
Opaque Address Format:
This address will be either a TcpAccPntExtendedAddress ,a TcpOnlyExtendedAddress , or a TcpNonAccPntExtendedAddress .
TcpAccPntExtendedAddress: Use this structure when referring to an address identified by an access point ID number.
TcpOnlyExtendedAddress: Use this structure to identify a TCP address by its address instead of by its ID.
TcpNonAccPntExtendedAddress: Use this structure to identify a TCP address that is not a known access point. You will need to specify a link address (a phone number) as well as an IP address.
Notes:
  • The notes for GEOS TCP/IP (starting on If you're not familiar with TCP/IP, TCP/IP illustrated by W. Richard Stevens provides a good introduction. The TCP/IP standard is defined in a number of RFCs. The RFCs listed below may be of interest. If you research these documents, note that t ) apply to the TCP/IP standard as well.
  • You cannot use the sequential packets delivery type with standard TCP/IP.
| Up: GEOS SDK TechDocs | Up | Prev: 10.1 TCP/IP--GEOS Specific | Next: 10.3 Loopback


Loopback

Socket: 10.3 Domain-Specific Information: Loopback

| Up: GEOS SDK TechDocs | Up | Prev: 10.2 TCP/IP--Standard

The loopback domain can only be used for communications between sockets on the same machine.

Domain Name:
"loopback"
Port Numbers:
32-bit values.
Opaque Address Format:
There is no address data: The loopback domain is used for intra-machine communication.
| Up: GEOS SDK TechDocs | Up | Prev: 10.2 TCP/IP--Standard


Using Streams

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Using Streams: The Basics
| One-page Version of this Chapter for Easy Printing

It is often useful for an application to be able to write out data in an orderly manner to be read by another program or sent to a device such as a printer or modem. GEOS provides a mechanism called a stream to allow an orderly flow of data between two programs, between two threads of a multi-threaded program, or between a program and a device such as a serial or parallel port. The stream interface includes various ways to notify a program that bytes are available to read, or that there is room to write additional data.

The GEOS parallel and serial port drivers use the stream mechanism, so programs that will use these ports must do so via this mechanism; however, libraries exist to handle some of the more common uses for port communications. Programs do not need to access the serial or parallel ports in order to print because the spooler does it for them. For more information about the spooler and printing, see .

This chapter may be read with only a general understanding of GEOS and Object Assembly. Messaging is used heavily in informing the application using a stream of the stream's status, so a good understanding of object-oriented programming will be helpful. You should also be familiar with the GEOS memory manager. The chapter is divided into three main sections:

  • A general description of a GEOS stream and how the two sides (writers and readers) access it.
  • A description of the specific routines and conventions for using streams to send data to and receive data from a serial port.
  • A description of the specific routines and conventions for using a stream to send data to a parallel port.

1 Using Streams: The Basics
1.1 Initializing a Stream
1.2 Blocking on Read or Write
1.3 Writing Data to a Stream
1.4 Reading Data from a Stream
1.5 Shutting Down a Stream
1.6 Miscellaneous Functions
2 Using the Serial Ports
2.1 Initializing a Serial Port
2.2 Communicating
2.3 Closing a Serial Port
3 Using the Parallel Ports
3.1 Initializing a Parallel Port
3.2 Communicating
3.3 Closing a Parallel Port

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Using Streams: The Basics
| One-page Version of this Chapter for Easy Printing


Using Streams: The Basics

Using Streams: 1 Using Streams: The Basics

| Up: GEOS SDK TechDocs | Up | Down | Prev: Using Streams | Next: 2 Using the Serial Ports

A stream is a path along which information flows in one direction. At one end of the stream is a writer , who puts data into the stream. At the opposite end of the stream is a reader , who receives data from the stream. The writer and reader can be GEOS programs or device drivers. Data flow in both directions can be achieved by using two streams, one in each direction; this is how the serial port driver is implemented.

Even though the stream driver is loaded automatically when a stream is created, you will need to initialize, configure, and destroy any streams you use. The specific steps involved in this process are

  1. Get the handle of the stream driver.
    You will need to get this handle to use most of the stream-library routines. You can get this handle by calling GeodeUseDriver() .
  2. Create the stream.
    You must create each stream you plan on using. When a stream is initialized, it is designated a token that is used when calling the stream driver's strategy routine.
  3. Configure the stream.
    Arrange how your geode will be notified by the stream driver when certain situations (error received or buffer full/empty) arise, and make sure that all geodes accessing the stream have been given the stream's token.
  4. Use the stream.
  5. Shut down the stream.
    Not a trivial task, shutting down a stream can involve several synchronization issues.

Streams are created and managed by the Stream Driver . Programs written in Object-Assembly can call the driver directly. Goc programs cannot do this; instead, they make calls to the Stream Library , which in turn calls the Stream Driver, and passes back any return values.

Initializing a Stream

Blocking on Read or Write

Writing Data to a Stream

Reading Data from a Stream

Shutting Down a Stream

Miscellaneous Functions

| Up: GEOS SDK TechDocs | Up | Down | Prev: Using Streams | Next: 2 Using the Serial Ports


Initializing a Stream

Using Streams: 1.1 Using Streams: The Basics: Initializing a Stream

| Up: GEOS SDK TechDocs | Up | Prev: 1 Using Streams: The Basics | Next: 1.2 Blocking on Read or Write

A stream is essentially a first-in-first-out data buffer, in which the writer is different from the reader. When the writer writes data to the stream, the kernel stores it in the buffer; when the reader requests information from the stream, the kernel retrieves the oldest data not yet read. The data is stored in a memory block; this block may be either fixed or movable. If it is movable, both the reader and the writer must lock the block before calling any stream routines.

Note that the kernel does not enforce who is the reader or writer to a stream. Any geode may call the appropriate stream library routine, passing in the token for a stream, and read or write data. However, in practice, only those threads with a legitimate interest in a stream will know the stream's token.

The serial and parallel drivers are built on top of the stream driver. There are separate routines to access the serial and parallel ports; these routines are discussed in Using the Serial Ports for the serial driver and Using the Parallel Ports for the parallel driver.

Creating a Stream

StreamOpen()

To create and initialize a new stream, call the routine StreamOpen() . This routine takes five arguments:

  • The handle of the stream driver.
  • The size of the stream buffer, in bytes. This may not be larger than 32767.
  • The GeodeHandle of the geode that will own this stream. When this geode exits, the stream will be freed; however, you should call StreamClose() before this happens.
  • A set of HeapFlags . The routine will have to allocate a block to hold the stream. The HeapFlags specify whether that block will be fixed or movable. If it is fixed, this argument should contain the flag HF_FIXED; otherwise it should be blank.
  • The pointer to a StreamToken variable. StreamOpen() will create the stream and write its token to this variable. You will need this token whenever you access the stream, for reading or writing.

If the creation is successful, StreamOpen() will return zero and store the stream's token in the StreamToken variable. You must see to it that both the reader and the writer have this token. If the stream cannot be created, the strategy routine will set an error flag and return either STREAM_CANNOT_ALLOC (if the memory for the stream's buffer cannot be allocated) or STREAM_BUFFER_TOO_LARGE (if the requested stream size was greater than 32767).

Assigning Readers and Writers

Once a stream is created, you must make sure that both ends will be managed--a stream that has only a writer or only a reader is not a useful stream.

When communicating with a device such as a serial or parallel port, the port is considered to be the entity on the other end. However, if two threads are communicating via a stream, you must make sure the other thread can gain access to the stream. The best way to do this is to set up a message that will be sent by the creator to the other geode. This message should contain as an argument the token of the stream and probably the direction of the stream (whether the creator will be reading or writing).

Once both geodes have the stream's token, each can access the stream normally. The next several sections explain how to access a stream for writing and reading.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Using Streams: The Basics | Next: 1.2 Blocking on Read or Write


Blocking on Read or Write

Using Streams: 1.2 Using Streams: The Basics: Blocking on Read or Write

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Initializing a Stream | Next: 1.3 Writing Data to a Stream
StreamBlocker, StreamError, StreamGetError(), StreamSetError()

A stream is a data buffer of limited size. When a thread writes to the stream, there is a chance it could run out of space. Similarly, when a thread reads from the stream, there is a possibility that it will try to read more data than is available; for example, it might try to read 500 bytes, when only 250 bytes of data are sitting in the stream.

There are two ways you can deal with these situations. One way is, you can instruct the thread to block. For example, if you try to write 500 bytes to a stream and there is only 200 bytes of space available, the driver will write the first 200 bytes to that space, then have the writing thread block until more space is available (i.e. until the reading thread has read some data). The writing thread will not resume execution until all the data has been written. Similarly, a reading thread could block until the stream provided all the data it requested.

The other approach is to have the stream driver write or read all it can, then return an appropriate error code. This requires a little more work by the calling thread, as it cannot assume that all the data is always read or written; however, it avoids the risk of deadlock.

All read and write routines are passed a member of the StreamBlocker enumerated type. This type has two members: STREAM_BLOCK, indicating that the calling thread should block in the situations described above; and STREAM_NO_BLOCK, indicating that the routine should immediately return with an error if enough space is not available. A single thread may, if it wishes, pass STREAM_BLOCK sometimes and STREAM_NO_BLOCK sometimes.

If a stream routine returns an error, the error will be a member of the StreamError enumerated type. The possible error values are described in the section for each routine.

Sometimes the device or application at the other end of the stream will need to return a special error value. For example, it may have its own error enumerated type, and may wish to signal an error to the application on the other end of the stream. It can do this by calling StreamSetError() . This routine is passed four arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamRoles enumerated type. The only appropriate values here are STREAM_ROLES_WRITER or STREAM_ROLES_READER.
  • A word-sized error code.

If the routine is succesful, it returns zero. Otherwise, it returns a member of the StreamError enumerated type.

To read a an error value set by the other side of a stream, call StreamGetError() . This routine is passed four arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamRoles enumerated type. The only appropriate values here are STREAM_ROLES_WRITER or STREAM_ROLES_READER.
  • A pointer to a word-sized variable.

If successful, the routine returns zero, and writes the other side's error value to the passed word-sized argument. If it is unsuccessful, the routine returns a StreamError value.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Initializing a Stream | Next: 1.3 Writing Data to a Stream


Writing Data to a Stream

Using Streams: 1.3 Using Streams: The Basics: Writing Data to a Stream

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Blocking on Read or Write | Next: 1.4 Reading Data from a Stream
StreamWrite(), StreamWriteByte()

To write data into a stream, call the routine StreamWrite() . This routine takes six arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamBlocker enumerated type, as described in Blocking on Read or Write .
  • The amount of data to be written, in bytes.
  • A pointer to a data buffer; the data will be copied from that buffer to the stream.
  • A pointer to an integer. StreamWrite() will write the number of bytes actually copied to that integer.

If all the data was written successfully, StreamWrite() will return zero and write the number of bytes written (i.e. the size of the data buffer passed) to the integer pointed to by the sixth argument. If it could not successfully write all the data, it will return one of the following StreamError values:

STREAM_WOULD_BLOCK
STREAM_NO_BLOCK had been passed, and there was no room to write any data to the stream. The sixth argument will be set to zero.
STREAM_SHORT_READ_WRITE
If STREAM_NOBLOCK had been passed, this means there was not enough room to write all the data. If STREAM_BLOCK had been passed, this means the stream was closed before all the data could be written. The sixth argument will contain the number of bytes actually written to the stream.
STREAM_CLOSING
The stream is in the process of being closed; no writing is permitted while this is happening. The sixth argument will be set to zero.
STREAM_CLOSED
The stream has already been closed. The sixth argument will be set to zero.

You may often want to write a single byte to the stream. There is a special routine to do this, StreamWriteByte() . This routine takes four arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamBlocker enumerated type, as described in Blocking on Read or Write .
  • The byte to be written.

If the byte is written successfully, StreamWriteByte() will return zero. Otherwise, it will return one of the following error values:

STREAM_WOULD_BLOCK
STREAM_BLOCK had been passed, and there was no room to write any data to the stream.
STREAM_CLOSING
The stream is in the process of being closed; no writing is permitted while this is happening.
STREAM_CLOSED
The stream has already been closed.
| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Blocking on Read or Write | Next: 1.4 Reading Data from a Stream


Reading Data from a Stream

Using Streams: 1.4 Using Streams: The Basics: Reading Data from a Stream

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Writing Data to a Stream | Next: 1.5 Shutting Down a Stream
StreamRead(), StreamReadByte()

To read data in a stream, call the routine StreamRead() . This routine takes six arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamBlocker enumerated type, as described in Blocking on Read or Write .
  • The amount of data to be read, in bytes.
  • A pointer to a data buffer; the data will be read from the stream into that buffer.
  • A pointer to an integer. StreamRead() will write the number of bytes actually read to that integer.

If the requested amount of data was read successfully, StreamRead() will return zero and write the number of bytes read (i.e. the size of the data buffer passed) to the integer pointed to by the sixth argument. If it could not successfully read the requested amount of data, it will return one of the following StreamError values:

STREAM_WOULD_BLOCK
STREAM_BLOCK had been passed, and there was no data waiting in the stream. The sixth argument will be set to zero.
STREAM_SHORT_READ_WRITE
If STREAM_NOBLOCK had been passed, this means the stream did not have the amount of data requested. If STREAM_BLOCK had been passed, this means the stream was closed before the requested amount of data could be read. The sixth argument will contain the number of bytes actually read from the stream.
STREAM_CLOSING
The stream is in the process of being closed; no reading is permitted while this is happening. The sixth argument will be set to zero.

You may often want to read a single byte from the stream. There is a special routine to do this, StreamReadByte() . This routine takes four arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamBlocker enumerated type, as described in Blocking on Read or Write .
  • A pointer to a byte-sized variable; the data byte read will be written to this variable.

If the byte is written successfully, StreamReadByte() will return zero. Otherwise, it will return one of the following error values:

STREAM_WOULD_BLOCK
STREAM_BLOCK had been passed, and there was no data waiting in the stream.
STREAM_CLOSING
The stream is in the process of being closed; no reading is permitted while this is happening.
| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Writing Data to a Stream | Next: 1.5 Shutting Down a Stream


Shutting Down a Stream

Using Streams: 1.5 Using Streams: The Basics: Shutting Down a Stream

| Up: GEOS SDK TechDocs | Up | Prev: 1.4 Reading Data from a Stream | Next: 1.6 Miscellaneous Functions
StreamClose()

Either the writer or the reader can instigate stream shutdown by calling StreamClose() . When one of the two calls this routine, the shut-down process is started; it will not be completed until the other calls the routine.

If the writer calls StreamClose() , it may specify that the data already in the buffer be flushed (immediately cleared), or that it linger . If you specify that the data should linger, the data will be preserved as long as the reader has the stream open. The reader can continue to read data normally until it runs out of data. The last read-operation will most likely return STREAM_SHORT_READ_WRITE; after that, all attempts to read data will generate the error STREAM_CLOSING. At that point, the reader should call StreamClose() . (If the data was flushed by the writer, the next read attempt will return STREAM_CLOSING.)

To shut down the stream, call the routine StreamClose() . This routine is passed the following arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A Boolean value saying whether the data in the stream should be kept until it's read; false (i.e. zero) indicates it should be flushed.

If you are using the Serial or Parallel drivers (described later in this chapter), you do not have to coordinate the closure of a stream.

| Up: GEOS SDK TechDocs | Up | Prev: 1.4 Reading Data from a Stream | Next: 1.6 Miscellaneous Functions


Miscellaneous Functions

Using Streams: 1.6 Using Streams: The Basics: Miscellaneous Functions

| Up: GEOS SDK TechDocs | Up | Prev: 1.5 Shutting Down a Stream | Next: 2 Using the Serial Ports
StreamFlush(), StreamQuery()

To flush all the pending (written but unread) data from a stream, call the routine StreamFlush() . This routine is passed two arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.

To find out how much free space is available in a stream, or how much data is waiting to be read, call StreamQuery() . This routine is passed four arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamRoles enumerated type. The only appropriate values here are STREAM_ROLES_WRITER (to find the amount of free space available for writing) or STREAM_ROLES_READER (to find the amount of data waiting to be read).
  • A pointer to an integer variable.

If the call is successful, StreamQuery() returns zero and writes its return value to the fourth argument. If you pass STREAM_ROLES_WRITER, StreamQuery() writes the number of bytes of free space available in the stream buffer. If you pass STREAM_ROLES_READER, StreamQuery() returns the number of bytes of data waiting to be read. If the call is unsuccessful, StreamQuery() returns a StreamError .

| Up: GEOS SDK TechDocs | Up | Prev: 1.5 Shutting Down a Stream | Next: 2 Using the Serial Ports


Using the Serial Ports

Using Streams: 2 Using the Serial Ports

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Using Streams: The Basics | Next: 3 Using the Parallel Ports

The serial driver uses streams to control the flow of data to and from serial ports. The kernel automatically copies data from the serial port into one stream for reading, and sends data from another stream into the serial port. An application which wishes to use the serial port simply reads and writes data from those streams.

Initializing a Serial Port

Communicating

Closing a Serial Port

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Using Streams: The Basics | Next: 3 Using the Parallel Ports


Initializing a Serial Port

Using Streams: 2.1 Using the Serial Ports: Initializing a Serial Port

| Up: GEOS SDK TechDocs | Up | Prev: 2 Using the Serial Ports | Next: 2.2 Communicating

Like the stream driver, the serial driver is not accessed directly from Goc code. Instead, a Goc application makes calls to the Stream Library, which passes the requests through to the Serial Driver's strategy routine. Each serial-port command must be passed the GeodeHandle of the Serial Library; again, you can find this handle by calling GeodeGetInfo() .

The serial driver uses two streams, one for data going out to the serial port (outgoing) and one for data coming in from the serial port (incoming). Your program is the writer of the outgoing and the reader of the incoming. (In both cases, the port acts as the opposite user.)

Opening a Serial Port

SerialOpen()

To open a serial port, call the routine SerialOpen() . This routine is passed the following arguments:

  • The GeodeHandle of the serial-port driver.
  • A member of the SerialUnit enumerated type; this specifies which serial port is being opened. The type's members are SERIAL_COM1, SERIAL_COM2, and so on up to SERIAL_COM8.
  • A member of the StreamOpenFlags enumerated type, indicating what to do if the requested serial port is busy (either STREAM_OPEN_NO_BLOCK, indicating that the routine should return an error immediately; or STREAM_OPEN_TIMEOUT, indicating that the routine should wait a specified number of clock ticks to see if the port will free up).
  • The total size of the stream to be used as an input buffer, in bytes.
  • The total size of the stream to be used as an output buffer, in bytes.
  • The maximum number of ticks to wait for the serial port to become available (if STREAM_OPEN_TIMEOUT was passed).

A flag is returned to indicate whether the serial port could be opened; if not, a value of type StreamError will be returned to indicate the reason. Possible stream error values are STREAM_BUFFER_TOO_LARGE and STREAM_CANNOT_CREATE, and the additional values STREAM_NO_DEVICE (if the serial port does not exist) or STREAM_DEVICE_IN_USE (if the device is busy and the StreamOpenFlags passed indicate not to wait).

Note that when using the serial driver, you do not identify the stream by a stream token but rather by the serial port number, known as a unit number . When accessing a serial port, you simply pass the port's unit number along with either STREAM_READ (if reading from the stream) or STREAM_WRITE (if writing to the stream); because each port has two streams associated with it, you must specify both parameters. The serial driver will understand which stream you are accessing.

Configuring a Serial Port

SerialSetFormat(), SerialGetFormat(),SerialSetModem(), SerialGetModem(), SerialSetFlowControl()

Communication using a serial port requires that parity, speed, and flow control be properly set. To control these settings, call SerialSetFormat() , passing the following arguments:

  • The GeodeHandle of the serial-port driver.
  • The member of the SerialUnit enumerated type.
  • A one-byte record of type SerialFormat , specifying the parity, word length, and number of stop bits to be used on the serial line; this record is described below.
  • A member of the SerialMode enumerated type, set to indicate the level of flow control: SM_COOKED to indicate XON/XOFF flow control with characters stripped to seven bits, SM_RARE to indicate XON/XOFF flow control but incoming characters left alone, or SM_RAW to indicate no flow control.
  • The baud rate to use, a member of the enumerated type SerialBauds , which has the following members:
typedef	enum
{
    SERIAL_BAUD_115200				= 1,
    SERIAL_BAUD_57600				= 2,
    SERIAL_BAUD_38400				= 3,
    SERIAL_BAUD_19200				= 6,
    SERIAL_BAUD_14400				= 8,
    SERIAL_BAUD_9600				= 12,
    SERIAL_BAUD_7200				= 16,
    SERIAL_BAUD_4800				= 24,
    SERIAL_BAUD_3600				= 32,
    SERIAL_BAUD_2400				= 48,
    SERIAL_BAUD_2000				= 58,
    SERIAL_BAUD_1800				= 64,
    SERIAL_BAUD_1200				= 96,
    SERIAL_BAUD_600				= 192,
    SERIAL_BAUD_300				= 384
} SerialBaud;

SerialFormat is a byte-sized record that specifies the parity, word-length, and number of stop bits for the serial line. The record has the following fields:

SERIAL_FORMAT_DLAB
This is for internal use only; it must be set to zero.
SERIAL_FORMAT_BREAK
If set, this causes a BREAK condition to be asserted on the line. You must explicitly clear this bit again to resume normal operation.
SERIAL_FORMAT_PARITY
This three-bit field holds the parity to expect on receive and use on transmit. It uses the SerialParity enumerated type, which has the following members:
typedef		enum {
    SERIAL_PARITY_NONE				= 0,
    SERIAL_PARITY_ODD				= 1,
    SERIAL_PARITY_EVEN				= 3,
    SERIAL_PARITY_ONE				= 5,
    SERIAL_PARITY_MARK				= 5,
    SERIAL_PARITY_ZERO				= 7,
    SERIAL_PARITY_SPACE				= 7
} SerialParity;
SERIAL_FORMAT_EXTRA_STOP
If this is set, extra stop-bits will be sent. One stop bit is always sent. However, if you set this flag, an extra 1/2 stop bit will be sent if the word-length is 5 bits long; an extra 1 stop bit will be sent if the frame is 6, 7, or 8 bits long.
SERIAL_FORMAT_LENGTH
This two-bit field holds the length of each data word, minus five (i.e. a five-bit word is represented with a zero, a six-bit word with a one).

To find out the current settings of a serial port, call SerialGetFormat() . This routine is passed five arguments:

  • The GeodeHandle of the serial-port driver.
  • The member of the SerialUnit enumerated type.
  • A pointer to a SerialFormat variable. SerialGetFormat() will write the format data to this variable.
  • A pointer to a SerialMode variable. SerialGetFormat() will write the appropriate mode constant (SM_COOKED, XON/XOFF, or SM_RARE) to this variable.
  • A pointer to a SerialBaud variable. SerialFormat() will write the appropriate constant to this variable.

As with other serial port routines, if the routine is successful, it will return zero; if it is unsuccessful, it will return an error code.

If you are using a modem's hardware flow control, you will have to configure the modem appropriately. You can do this by calling SerialSetModem() . This routine is passed three arguments:

  • The GeodeHandle of the serial-port driver.
  • The member of the SerialUnit enumerated type.
  • A record of type SerialModem . This record has four fields: SERIAL_MODEM_OUT2, SERIAL_MODEM_OUT1, SERIAL_MODEM_RTS, and SERIAL_MODEM_DTR. Set these fields to indicate how the control-bits should be set.

To find out what flow control is being used, call SerialGetModem() . This routine is passed three arguments:

  • The GeodeHandle of the serial-port driver.
  • The member of the SerialUnit enumerated type.
  • A pointer to a record of type SerialModem . SerialGetModem() will set this record's SERIAL_MODEM_OUT2, SERIAL_MODEM_OUT1, SERIAL_MODEM_RTS, and SERIAL_MODEM_DTR bits appropriately.

You can also set the flow control without setting the other format options. Do this by calling SerialSetFlowControl() . This routine is passed the following arguments:

  • The GeodeHandle of the serial-port driver.
  • The member of the SerialUnit enumerated type.
  • A record of type SerialModem . This record has four fields: SERIAL_MODEM_OUT2, SERIAL_MODEM_OUT1, SERIAL_MODEM_RTS, and SERIAL_MODEM_DTR. Set these fields to indicate how the control-bits should be set.
  • A member of the SerialMode enumerated type, set to indicate the level of flow control: SM_COOKED to indicate XON/XOFF flow control with characters stripped to seven bits, SM_RARE to indicate XON/XOFF flow control but incoming characters left alone, or SM_RAW to indicate no flow control.
  • A record of type SerialModemStatus to indicate which lines (chosen from DCD, DSR, and CTS) should be used to control outgoing data (if hardware flow control is selected). When one of the selected lines is de-asserted by the remote system, the serial driver will not transmit any more data until the state changes.
| Up: GEOS SDK TechDocs | Up | Prev: 2 Using the Serial Ports | Next: 2.2 Communicating


Communicating

Using Streams: 2.2 Using the Serial Ports: Communicating

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Initializing a Serial Port | Next: 2.3 Closing a Serial Port
SerialRead(), SerialReadByte(), SerialWrite(), SerialWriteByte(), SerialQuery(), SerialFlush(), SerialGetError(), SerialSetError(), SerialError

Communicating with a serial port is very much like using any other stream. Special versions of the stream routines are provided, but they function just like their stream counterparts.

To read data from a serial port, call SerialRead() or SerialReadByte() . These routines take the same arguments as their Stream...() counterparts, except that each one must be passed the handle of the Serial Driver, not the Stream Driver, and each routine is passed the SerialUnit for the appropriate port, instead of being passed a stream token. These routines behave exactly like their Stream...() counterparts.

To write data to a serial port, call SerialWrite() or SerialWriteByte() . Again, these routines behave like their Stream...() counterparts, and take similar arguments.

To find out if you can read or write data to the port, call SerialQuery() . Again, this routine behaves like its Stream...() equivalent. To flush any data from the input or output stream, call SerialFlush() .

To send an error to the other side of a serial connection, call SerialSetError() . This routine behaves much like StreamSetError() . The only difference is that the error code passed must be a member of the SerialError enumerated type. This type has the following members:

SE_BREAK
Break condition detected on line.
SE_FRAME
Framing error (e.g. improper number of stop bits).
SE_PARITY
Parity error in received character.
SE_OVERRUN
A new byte was received before the previous byte was read; the previous byte was discarded.

To read an error condition passed by the other side of a serial connection, call SerialGetError() . This routine behaves just like StreamGetError() . The error code returned will be a member of the SerialError enumerated type.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Initializing a Serial Port | Next: 2.3 Closing a Serial Port


Closing a Serial Port

Using Streams: 2.3 Using the Serial Ports: Closing a Serial Port

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Communicating | Next: 3 Using the Parallel Ports
SerialClose(), SerialCloseWithoutReset()

To close a serial port, call the routine SerialClose() . This routine is passed three arguments:

  • The GeodeHandle of the serial-port driver.
  • The member of the SerialUnit enumerated type.
  • Either STREAM_LINGER (to instruct the kernel to close the port after all outgoing data in the buffer has been sent), or STREAM_DISCARD (to instruct the kernel to close the port right away and discard all buffered data).

This function returns immediately whether the port was closed right away or not. However, if STREAM_LINGER is specified, the port may not be re-opened until all the data in the Serial Port's buffer has been dealt with.

You can also instruct the serial driver to close the stream to a port, without actually resetting the port. Do this by calling SerialCloseWithoutReset() . This routine is passed the same arguments as SerialClose() .

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Communicating | Next: 3 Using the Parallel Ports


Using the Parallel Ports

Using Streams: 3 Using the Parallel Ports

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Using the Serial Ports | Next: 3.1 Initializing a Parallel Port

Using a parallel port is simpler than using a serial port since data goes in only one direction. GEOS does not currently support reading data from a parallel port.

Parallel ports are used primarily for printing, which is handled by the Spool Object Library. The information in this section is useful only to programmers whose applications will need to send data out through the parallel port without using the spooler. Most applications, however, will use the spooler for any and all parallel port use.

Initializing a Parallel Port

Communicating

Closing a Parallel Port

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Using the Serial Ports | Next: 3.1 Initializing a Parallel Port


Initializing a Parallel Port

Using Streams: 3.1 Using the Parallel Ports: Initializing a Parallel Port

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using the Parallel Ports | Next: 3.2 Communicating
ParallelOpen()

To open a parallel port, call the routine ParallelOpen() . This routine is passed the following arguments:

  • The GeodeHandle of the parallel-port driver.
  • A member of the ParallelUnit enumerated type; this specifies which parallel port is being opened. The type's members are PARALLEL_LPT1, PARALLEL_LPT2, PARALLEL_LPT3, and PARALLEL_COM4.
  • A member of the StreamOpenFlags enumerated type, indicating what to do if the requested parallel port is busy (either STREAM_OPEN_NO_BLOCK, indicating that the routine should return an error immediately; or STREAM_OPEN_TIMEOUT, indicating that the routine should wait a specified number of clock ticks to see if the port will free up).
  • The unit number of the parallel port in question--this is a value of type ParallelPortNums .
  • The total size of the stream to be used as an output buffer, in bytes.
  • The maximum number of ticks to wait for the parallel port to become available (if STREAM_OPEN_TIMEOUT was passed).

A flag is returned to indicate whether the parallel port could be opened; if not, a value of type StreamError will be returned to indicate the reason. Possible stream error values are STREAM_BUFFER_TOO_LARGE and STREAM_CANNOT_CREATE, and the additional values STREAM_NO_DEVICE (if the parallel port does not exist) or STREAM_DEVICE_IN_USE (if the device is busy and the StreamOpenFlags passed indicate not to wait (or not to wait any longer)).

Note that when using the parallel driver, you do not identify the stream by a stream token but rather by the parallel port number, known as a unit number . When accessing a parallel port, you simply pass the port's unit number along with either STREAM_READ (if reading from the stream) or STREAM_WRITE (if writing to the stream); because each port has two streams associated with it, you must specify both parameters. The parallel driver will understand which stream you are accessing.

Once the port is selected, the PC will assert the SLCTIN signal, which usually will place the device on-line.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using the Parallel Ports | Next: 3.2 Communicating


Communicating

Using Streams: 3.2 Using the Parallel Ports: Communicating

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Initializing a Parallel Port | Next: 3.3 Closing a Parallel Port
ParallelWrite(), ParallelWriteByte()

Writing to a parallel port is much like writing to any other stream. To write data, call ParallelWrite() or ParallelWriteByte() . These routines take the same arguments as their Stream...() components, except that each one must be passed the handle of the Parallel Driver, not the Stream Driver, and each routine is passed the ParallelUnit for the appropriate port, instead of being passed a stream token. These routines behave exactly like their Stream...() counterparts.

To send an error to the other side of a serial connection, call ParallelSetError() . (This routine is generally not called by applications.) This routine behaves much like StreamSetError() . It is passed four arguments:

  • The GeodeHandle of the parallel-port driver.
  • The appropriate member of the ParallelUnit enumerated type.
  • The appropriate member of the StreamRoles enumerated type.
  • A ParallelError record. This word-sized record has the following flags:
PE_FATAL
Set if the driver encountered an error that means it can't continue.
PE_TIMEOUT
Set if there is no response from the printer, but no other (non-masked) error is present.
PE_NOPAPER
Set if the device is out of paper.
PE_OFFLINE
Set if the device is off-line.
PE_ERROR
Indicates a general error.

To read an error condition passed by the other side of a serial connection, call ParallelGetError() . This routine behaves much like StreamGetError() . The error code returned will be a member of the ParallelError enumerated type.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Initializing a Parallel Port | Next: 3.3 Closing a Parallel Port


Closing a Parallel Port

Using Streams: 3.3 Using the Parallel Ports: Closing a Parallel Port

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Communicating
ParallelClose()

To close a parallel port, ParallelClose() . This routine takes the following arguments:

  • The GeodeHandle of the parallel-port driver.
  • The member of the ParallelUnit enumerated type.
  • Either STREAM_LINGER (to instruct the kernel to close the port after all outgoing data in the buffer has been sent), or STREAM_DISCARD (to instruct the kernel to close the port right away and discard all buffered data).

This function returns immediately whether the port was closed right away or not. However, if STREAM_LINGER is specified, the port may not be re-opened until all the data in the parallel port's buffer has been dealt with.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Communicating


Data Management Topics

GEOS SDK Documentation: Data Management Topics

| Up: GEOS SDK TechDocs

The following documents describe API for certain specialized data-storage mechanisms.

DataStore library
Stores a database in a file. Databases consist of records, each record containing several fields of data.
Cell Library
Stores "cells" of data in a two-dimensional array in a VM file.
Impex Library
Translates GEOS data files into common formats used on PCs.
Contacts Database
The Nokia 9000i Communicator has a database dedicated to storing Contact information such as names, addresses, and phone numbers.
| Up: GEOS SDK TechDocs


Cell Library

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Structure and Design
| One-page Version of this Chapter for Easy Printing

For some applications, it is natural to organize data in a two-dimensional array. The classic example of this is the spreadsheet, in which every entry (or cell ) can be uniquely identified by two integers: the cell's row and its column.

The cell library lets you arrange data this way. The cell library saves the cells as DB items in a VM file. It insulates the application from the actual DB mechanism, letting the application behave as if its data is stored in a two-dimensional array. The library also provides routines to sort the cells by row or by column and to apply a routine to every cell in a range of rows and/or columns.

A collection of cells arranged into rows and columns is termed a cell file . Every cell file is contained in a VM file. There is often a one-to-one correspondence between cell files and the VM files which contain them. However, this correspondence is optional. There is nothing to stop an application from maintaining several distinct cell files in a single VM file.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Structure and Design
| One-page Version of this Chapter for Easy Printing


Structure and Design

Cell Library: 1 Structure and Design

| Up: GEOS SDK TechDocs | Up | Prev: Cell Library | Next: 2 Using the Cell Library

Most of the internal structure of a cell file is transparent to the geode which uses it. A geode can, for example, lock a cell with CellLock() , specifying the cell's row and column. The cell library will find the appropriate DB item and lock it, returning the locked item's address. For most operations, the geode does not need to know anything about the internal structure of the cell file. However, the internal structure does matter for some purposes. For this reason, we present a quick overview of the structure of a cell file.

A cell file can contain up to 16,512 rows, numbered from zero to 16,511. Of these 16,512 rows, the last 128 are "scratch-pad" rows. They are intended to be used for holding information or formulae that will not be displayed or associated with a specific cell. The scratch-pad rows are never shifted; if you create a cell in the first scratch-pad row, it will always stay in that row. All other rows are called "visible" rows. Visible rows can be shifted when rows are created or deleted. For example, if you insert a new row 10, all the cells in the old row 10 will now be in row 11, and so on. The first scratch-pad row will be unchanged. Be aware that the database will not delete cells from rows that are shifted off the spreadsheet. For example, if you insert a new row, the last visible row will be shifted off the spreadsheet; the references to cells in that row will be removed, but the cells themselves will stay as DB items in the file. This is not generally a problem, since few cell files will need to use the last visible rows. If you add a row that will cause cells to be shifted off, you should delete those cells first.

The first row has an index number of zero. The last visible row has an index equal to the preprocessor constant LARGEST_VISIBLE_ROW . The first scratch-pad row has an index equal to (LARGEST_VISIBLE_ROW + 1). The last scratch-pad row has an index equal to LARGEST_ROW (which equals (LARGEST_VISIBLE_ROW + 128) or 16,511). The constants are all defined in cell.h .

The basic data unit in a cell file is the cell . The cell library treats cells as opaque data structures; their internal structure is entirely up to the geode using them. Cells are stored as ungrouped DB items. This restricts cell size to the size of a DB item; that is, a cell can theoretically be as large as 64K, but in practice should not grow larger than around 8K (and ideally should be under a kilobyte in size). Remember, whenever a DB item is created or resized, pointers to all other items in the group are invalidated. Since cells are ungrouped items, whenever you create or resize a cell, you invalidate any pointers to all other ungrouped items in that VM file. In particular, you invalidate pointers to all other cells in that VM file (even if the cells belong to another cell file in the VM file).

Cells are grouped into rows . A row can have up to 256 cells, numbered from zero to 255. Within a row, cells are identified by their column index. The column index can fit into an unsigned byte variable. The cell library creates a column array for every row which contains cells. The column array contains one entry for each cell in the row. A row often contains just a few widely scattered elements. For this reason, the column array is implemented as a sparse array . Each cell in the row has an entry consisting of two parts, namely the cell's column number and its DBGroupAndItem structure. The advantage of this arrangement is that the column array need only contain entries for those cells which exist in the row (instead of maintaining entries for the blank spaces between cells). The disadvantage is that when you lock a cell, the cell manager has to make a search through the column array to find its reference; however, this is generally a small cost.

The column arrays themselves belong to row blocks . Each row block is an LMem heap stored in the VM file, and each of its column arrays is a chunk in that heap. Row blocks contain up to 32 rows. These rows are sequential; that is, any existing rows from row zero to row 31 will always belong to the same row block, and none of them will ever be in the same row block as row 32. Since the row blocks and column arrays are not kept in DB items, they can be accessed and altered without causing any locked items to move. To keep track of the row blocks, you must have a CellFunctionParameters structure for each cell file. That structure need not be kept in the VM file (although it often is); rather, you must pass the address of the structure to any cell library routine you call.

Owing to the structure of a cell file, some actions are faster than others. The essential thing to remember is that cells are grouped together in rows, which are themselves grouped together to form a cell file. This means that you can access several cells belonging to the same row faster than you could access cells belonging to different rows. Similarly, if you insert a cell, it is much more efficient to shift the rest of the row over (which involves accessing only that one row) than to shift the rest of the column down (which involves accessing every visible row). Similarly, you can access groups of cells faster if they belong to the same row block.

| Up: GEOS SDK TechDocs | Up | Prev: Cell Library | Next: 2 Using the Cell Library


Using the Cell Library

Cell Library: 2 Using the Cell Library

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Structure and Design | Next: 2.1 The CellFunctionParameters Structure

The cell library is versatile. The basic cell access routines are very simple, but more advanced utilities give you a wide range of actions. This section will explain the techniques used to set up and use a cell file, as well as the more advanced techniques available.

The CellFunctionParameters Structure

Basic Cell Array Routines

Actions on a Range of Cells

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Structure and Design | Next: 2.1 The CellFunctionParameters Structure


The CellFunctionParameters Structure

Cell Library: 2.1 Using the Cell Library: The CellFunctionParameters Structure

| Up: GEOS SDK TechDocs | Up | Prev: 2 Using the Cell Library | Next: 2.2 Basic Cell Array Routines

The cell library needs to have certain information about any cell file on which it acts; for example, it needs to know the handles of the VM file and of the row blocks. That information is kept in a CellFunctionParameters structure. The geode which uses a cell file is responsible for creating a CellFunctionParameters structure. The C definition of the structure is shown below.

Code Display 19-1 CellFunctionParameters

typedef	struct {
    CellFunctionParameterFlags
            CFP_flags;		/* Initialize this to zero. */
    VMFileHandle		CFP_file;		/* The handle of the VM file containing
                     * the cell file. Reinitialize this each
                     * time you open the file. */
    VMBlockHandle		CFP_rowBlocks[N_ROW_BLOCKS];			/* Initialize these to zero. */
} CellFunctionParameters;

In order to create a cell file, you must create a CellFunctionParameters structure. Simply allocate the space for the structure and initialize the data fields. When you call a cell library routine, lock the structure on the global heap and pass its address. Geodes will usually allocate a VM block in the same file as the cell file, and use this block to hold the CellFunctionParameters structure; this ensures that the structure will be saved along with the cell file. They may often declare this to be the map block, making it easy to locate (see the VM chapter). However, this is entirely at the programmer's discretion. All that the cell library requires is that the structure be locked or fixed in memory every time a cell library routine is called.

The CellFunctionParameters structure contains the following fields:

CFP_flags
The cell library uses this byte for miscellaneous bookkeeping. When you create the structure, initialize this field to zero. There is only one flag which you should check or change; that is the flag CFPF_DIRTY. The cell library routines set this bit whenever they change the CellFunctionParameters structure, thus indicating that the structure ought to be resaved. After you save it, you may clear this bit.
CFP_file
This field must contain the file handle of the VM file containing the cell file. A VM file can have a new file handle every time it is opened; thus, you must reinitialize this field every time you open the file.
CFP_rowBlocks
This field is an array of VM block handles, one for every existing or potential row block. If a row block exists in the cell file, its handle is recorded here. If it does not exist, a null handle is kept in the appropriate place. The length of this array is a number of words equal to the constant N_ROW_BLOCKS (defined in cell.h ). When you create a cell file, initialize all of these handles to zero; do not access or change this field thereafter.

One warning: The cell library expects the CellFunctionParameters structure to remain motionless for the duration of a call. Therefore, if you allocate it as a DB item in the same VM file as the cell file, you must not have the structure be an ungrouped item. Remember, all the cells are ungrouped DB items; allocating or resizing a cell can potentially move any or all of the ungrouped DB items in that file.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Using the Cell Library | Next: 2.2 Basic Cell Array Routines


Basic Cell Array Routines

Cell Library: 2.2 Using the Cell Library: Basic Cell Array Routines

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 The CellFunctionParameters Structure | Next: 2.3 Actions on a Range of Cells
CellReplace(), CellLock(), CellLockGetRef(), CellDirty(), CellGetDBItem(), CellGetExtent()

The basic cell routines are simple to use. One argument taken by all of them is the address of the CellFunctionParameters structure. As noted, this structure must be locked or fixed in memory for the duration of a function call. You can also access cells in any of the ways you would access a DB item; for example, you can resize a cell with DBReAlloc() .

All of the routines use the VM file handle specified in the CellFunctionParameters structure.

To create, replace, or free a cell, call the routine CellReplace() . This routine takes five arguments:

  • The address of the CellFunctionParameters structure.
  • The element's row.
  • The element's column.
  • The address of the data to copy into the new cell. This must not move during the allocation; therefore, it must not be in an ungrouped DB item in the same VM file as the cell file. In particular, it must not be another cell. The data will not be changed.
  • The size of the new cell. If you pass a size of zero, the cell will be deleted if it already exists; otherwise, nothing will happen.

If the cell file already contains a cell with the specified coordinates, CellReplace() will free it. CellReplace() will then allocate a new cell and copy the specified data into it. The routine invalidates any existing pointers to ungrouped DB items in the file.

Once you have created a cell, you can lock it with CellLock() . This routine takes three arguments: the address of the CellFunctionParameters structure, the cell's row, and the cell's column. It locks the cell and returns its address (the assembly version returns the cell's segment address and chunk handle). Remember, the cell is an ungrouped DB item, so its address may change the next time another ungrouped DB item is allocated or resized, even if the cell is locked.

Like all DB items, cells can (under certain circumstances) be moved even while locked. For this reason, a special locking routine is provided, namely CellLockGetRef() . This routine is just like CellLock() except that it takes one additional argument, namely the address of an optr. CellLockGetRef writes the locked item's global memory handle and chunk handle into the optr. You can translate an optr to a cell into a pointer by calling CellDeref() ; this is another synonym for LMemDeref() , and is identical to it in all respects.

If you change a cell, you must mark it dirty to insure that it will be updated on the disk. To do this, call the routine CellDirty() . This routine takes two arguments, namely the address of the CellFunctionParameters structure and the address of the (locked) cell. The routine marks the cell's item block as dirty.

Sometimes you may need to get the DB handles for a cell. For example, you may want to use a DB utility to resize the cell; to do this, you need to know its handles. For these situations, call the routine CellGetDBItem() . The routine takes three arguments: the address of the CellFunctionParameters structure, the cell's row, and the cell's column. It returns the cell's DBGroupAndItem value. You can pass this value to any of the DB...Ungrouped() routines, or you can break this value into its component handles by calling DBGroupFromGroupAndItem() or DBItemFromGroupAndItem() .

If you want to find out the bounds of an existing cell file, call the routine CellGetExtent() . This routine takes two arguments: the address of the CellFunctionParameters , and the address of a RangeEnumParams structure. For the purposes of this routine, only one of its fields matters, namely the field REP_bounds . This field is itself a structure of type Rectangle , whose structure is shown below in Rectangle . CellGetExtent() writes the bounds of the utilized section of the cell file in the REP_bounds field. The index of the first row which contains a cell will be written in the rectangle's R_top field; the index of the last row will be written in R_bottom ; the index of the first column will be written in R_left ; and the index of the last column will be written in R_right . If the cell file contains no cells, all four fields will be set to -1.

Code Display 19-2 Rectangle

typedef	struct {
    sword		R_left;		/* Index of first column written here. */
    sword		R_top;		/* Index of first row written here. */
    sword		R_right;		/* Index of last column written here. */
    sword		R_bottom;		/* Index of last row written here. */
} Rectangle;
| Up: GEOS SDK TechDocs | Up | Prev: 2.1 The CellFunctionParameters Structure | Next: 2.3 Actions on a Range of Cells


Actions on a Range of Cells

Cell Library: 2.3 Using the Cell Library: Actions on a Range of Cells

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Basic Cell Array Routines
RangeExists(), RangeInsert(), RangeEnum(), RangeSort(), RangeInsertParams

The cell library provides a number of routines which act on a range of cells. All of these routines take the address of a CellFunctionParameters structure as an argument. Many of these routines also take the address of a special parameter structure; for example, RangeInsert() takes the address of a RangeInsertParams structure. In these cases, the structure should be in locked or fixed memory. If the routine might allocate or resize cells, the structure must not be in an ungrouped DB item.

You may want to find out if there are any cells in a specified section of the cell file. To do this, call the routine RangeExists() . This routine takes five arguments:

  • The address of the locked CellFunctionParameters structure
  • The index of the first row in the section to be checked
  • The index of the first column in the section to be checked
  • The index of the last row in the section to be checked
  • The index of the last column in the section to be checked

If any cells exist in that section, the routine returns true (i.e. non-zero). Otherwise, it returns false .

You may wish to insert several cells at once. For this reason, the cell library provides the routine RangeInsert() . This routine does not actually allocate cells; instead, it shifts existing cells to make room for new ones. You specify a section of the cell file to shift. Any cells in that section will be shifted over; the caller specifies whether they should be shifted horizontally or vertically. The routine takes two arguments, namely the address of the CellFunctionParameters and the address of a RangeInsertParams structure. It does not return anything. The definition of the RangeInsertParams structure is shown in The RangeInsertParams and Point structures . The calling geode should allocate it and initialize it before calling RangeInsert() .

Code Display 19-3 The RangeInsertParams and Point structures

typedef	struct {		/* defined in cell.h */
    Rectangle			RIP_bounds;		/* Range of cells to shift */
    Point			RIP_delta;		/* Specify which way to shift */
    CellFunctionParameters			*RIP_cfp;
} RangeInsertParams;
typedef	struct {		/* defined in graphics.h */
    sword		P_x;	/* Distance to shift horizontally */
    sword		P_y;	/* Distance to shift vertically */
} Point;

The RangeInsertParams structure has three fields. The calling geode should initialize the fields to determine the behavior of RangeInsert() :

RIP _bounds
This field specifies which cells should be shifted. The cells currently in this range will be shifted across or down, depending on the value of RIP_delta ; this shifts more cells, and so on, to the edge of the visible portion of the cell file. The field is a Rectangle structure. To insert an entire row (which is much faster than inserting a partial row), set RIP_bounds.R_left = 0 and RIP_bounds.R_right = LARGEST_COLUMN .
RIP _delta
This field specifies how far the cells should be shifted and in which direction. The field is a Point structure (see The RangeInsertParams and Point structures ). If the range of cells is to be shifted horizontally, RIP_delta.P_x should specify how far the cells should be shifted to the right, and RIP_delta.P_y should be zero. If the cells are to be shifted vertically, RIP_delta.P_y should specify how far the cells should be shifted down, and RIP_delta.P_x should be zero.
RIP _cfp
This is the address of the CellFunctionParameters structure. You don't have to initialize this; the routine will do so automatically.

You may need to perform a certain function on every one of a range of cells. For this purpose, the cell library provides the routine RangeEnum() . This routine lets you specify a range of cells and a callback routine; the routine will be called on each cell in that range.

You can sort a range of cells, by row or by column, based on any criteria you choose. Use the routine RangeSort() . This routine uses a QuickSort algorithm to sort the cells specified. You supply a pointer to a callback routine which is used to compare cells.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Basic Cell Array Routines


DataStore Library

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Introduction
| One-page Version of this Chapter for Easy Printing

Personal Information Manager (PIM) applications such as address books and appointment calendars need fast, efficient data storage. Because these applications often share the same data, the data should be maintained on the system level and not by each application individually.

The DataStore Library provides a simple way for applications to store and access data in a centralized database. It shields programmers from low-level implementation of data storage.

To understand how to use the DataStore Library, you need only a basic understanding of GEOS programming.

1 Introduction
2 Creating a DataStore
2.1 Adding Fields
2.2 Adding Records
2.3 Deleting Records
2.4 Deleting Fields
3 Deleting a DataStore
4 Building an Index
5 String Search
6 Enumeration
7 Timestamping
8 Synchronization

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Introduction
| One-page Version of this Chapter for Easy Printing


Introduction

DataStore Library: 1 Introduction

| Up: GEOS SDK TechDocs | Up | Prev: DataStore Library | Next: 2 Creating a DataStore

The DataStore Library provides a system for storing and manipulating collections of structured data. Data reside in numbered records with type-defined fields; each collection of records is called a datastore. An application can access different datastores simultaneously; similarly, multiple applications can access a single datastore concurrently.

The DataStore Manager manages synchronization by issuing "session tokens" whenever an application opens (or creates) a datastore. An application can have multiple sessions, each identified by a unique token.

The DataStore Library contains routines for creating and accessing data on the file level, record level, and field level. All the routines return an error value which is useful for error checking. For a full list of routines and their parameter lists, see the C Reference Book (routines are listed alphabetically and all DataStore routines begin with "DataStore").

| Up: GEOS SDK TechDocs | Up | Prev: DataStore Library | Next: 2 Creating a DataStore


Creating a DataStore

DataStore Library: 2 Creating a DataStore

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Introduction | Next: 3 Deleting a DataStore
DataStoreCreate()

To create a new datastore, specify its attributes in a DataStoreCreateParams structure and call DataStoreCreate() .

typedef struct {
    TCHAR	*DSCP_name;
    DataStoreFlags	DSCP_flags;
    FieldDescriptor	*DSCP_keyList;
    word 	DSCP_keyCount;
    optr	DSCP_notifObject;
    DataStoreOpenFlags	DSCP_openFlags;
} DataStoreCreateParams;
DSCP _name
Name of the datastore. Used when opening, deleting or renaming a datastore. Any legal filename is acceptable.
DSCP _flags
Only the following flags may be passed:
  • DSF_PRIVATE
    Restricts access to the datastore to the owner only ( i.e. , the application that created the datastore).
  • DSF_TIMESTAMP
    Creates a timestamp field and makes it the first field of the datastore. This field is updated whenver a record is modified.
  • DSF_NO_PRIMARY_KEY
    Specifies that the datastore will contain no key field. When passing this flag, you must set DSCP _keyCount to zero.
DSCP _keyList
Pointer to an array of fields that make up the key. The term "key field" is not used in the relational database sense. In a datastore, the key field does not uniquely identify a record; it simply determines storage order of the records if no callback is passed in DataStoreSaveRecord() (see "Adding Records," Adding Records for a complete discussion of storage order).
    typedef struct {
        FieldData		FD_data;
        TCHAR		*FD_name;
    } FieldDescriptor;
    typedef struct {
        FieldType			FD_type;
        FieldCategory			FD_category;
        FieldFlags			CFD_flags;
    } FieldData;
Fields of FieldType DSFT_TIMESTAMP and DSFT_BINARY may not be part of the key and the only FieldFlag which may be passed is FF_DESCENDING (default sort order is ascending).
Once you create a datastore, you cannot redefine the key later (by adding, deleting or changing the key field(s)) nor can you add a key to a datastore that was created without one.
DSCP _keyCount
Number of fields that make up the primary key.If you pass zero, then you must pass DSF_NO_PRIMARY_KEY in DSCP_flags .
DSCP _notifyObject
Object to be notified when changes to the datastore occur. This object will receive MSG_META_NOTIFY_WITH_DATA_BLOCK which will pass a DataStoreChangeNotification structure. See the GCN chapter.

Passing NullOptr means no object will receive notification.
DSCP _openFlags
Passing zero allows multiple applications to open the datastore simultaneously. Passing DSOF_EXCLUSIVE gives exclusive access to the calling application.

Adding Fields

Adding Records

Deleting Records

Deleting Fields

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Introduction | Next: 3 Deleting a DataStore


Adding Fields

DataStore Library: 2.1 Creating a DataStore: Adding Fields

| Up: GEOS SDK TechDocs | Up | Prev: 2 Creating a DataStore | Next: 2.2 Adding Records
DataStoreAddField()

Once you've created a new datastore, you can add (non-key) fields at any time with DataStoreAddField() . When adding fields, keep in mind:

  • The maximum size of a field name is MAX_FIELD_NAME_LENGTH or 40.
  • When defining fields, note the difference between FieldType and FieldCategory . FieldType specifies the type of data stored in the field, such as DSFT_STRING; FieldCategory defines what type of information that data represents, such as a phone number (FC_TELEPHONE). See the C Reference Book for a full list of FieldTypes and FieldCategories .

The following code sample shows how to create a datastore that contains three fields, one of which is the key.

Code Display 9-1 Creating a New DataStore

    /*
     * When a new datastore is created, the DataStore Manager
     * opens a "session" and returns a "session" token
     */
    word	dsToken;
    /*
     * DataStoreCreateParams contains information about
     * the new datastore, such as key field(s) and access level
     */
    DataStoreCreateParams	params;
    /*
     * FieldDescriptor contains information about
     * a field, such as its name and type
     */
    FieldDescriptor	field;
    /*
     * This example will be an "Exchange Rate" datastore with three fields
     */
    static TCHAR dsName[] = "Exchange Rates";
    static TCHAR field1[] = "country";
    static TCHAR field2[] = "currency";
    static TCHAR field3[] = "exchange rate";
    /*
     * Define the "country" field; this will become the key field
     */
    field.FD_name = field1;
    field.FD_data.FD_type = DSFT_STRING;
    field.FD_data.FD_category = FC_NAME;
    field.FD_data.FD_flags = 0;	/* use ascending sort order */
    /*
     * Set the parameters for the new DataStore file:
     *   - add a timestamp field (this becomes the first field)
     *   - define the key field (the key cannot be changed later)
     *   - designate which object is to receive notifications
     * 	when the DataStore is changed
     */
    params.DSCP_name = dsName;
    params.DSCP_flags = DSF_TIMESTAMP;
    params.DSCP_keyList = &field; /* "country" field defined above */
    params.DSCP_keyCount = 1;
    params.DSCP_notifObject = oself; /* "oself" refers to the object handling
            this message; in this case, it is the process object */
    params.DSCP_openFlags = 0; /* make the datastore sharable between apps */
    /*
     * Now create the new datastore file. If it is successfully created,
     * add the additional fields.
     */
    if(DataStoreCreate(&params, &dsToken) == DSE_NO_ERROR)
    {
        /*
         * DataStoreAddField() returns the FieldID of the newly
         * created field to the passed FieldID variable (i.e., fid).
         */
        FieldID		fid;
        /* add currency field */
        field.FD_name = field2;
        field.FD_data.FD_type = DSFT_STRING;
        field.FD_data.FD_category = FC_NONE;
        field.FD_data.FD_flags = 0;	/* use ascending sort order */
        DataStoreAddField(dsToken, &field, &fid);
        /* add exchange rate field */
        field.FD_name = field3;
        field.FD_data.FD_type = DSFT_FLOAT;
        field.FD_data.FD_category = FC_NONE;
        field.FD_data.FD_flags = 0;	/* use ascending sort order */
        DataStoreAddField(dsToken, &field, &fid);
    }

Below is a diagram of the "Exchange Rates" datastore created in the above code example. The following section discusses how to add records to the datastore.

Note that fields of type DSFT_FLOAT expect data of type FloatNum (a special GEOS data type that differs from the standard C float type; see the Math chapterfor additional information on FloatNum s).

| Up: GEOS SDK TechDocs | Up | Prev: 2 Creating a DataStore | Next: 2.2 Adding Records


Adding Records

DataStore Library: 2.2 Creating a DataStore: Adding Records

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Adding Fields | Next: 2.3 Deleting Records
DataStoreOpen(), DataStoreNewRecord(), DataStoreSetField(), DataStoreSaveRecord()

To manage synchronization between applications accessing the same datastore, the DataStore Manager creates a "session" each time an application opens a datastore with DataStoreOpen() . Each session has its own "record buffer;" to modify a record, you must load it into the record buffer first. The loaded record is called the "current record." There can only be one record loaded in the buffer at a time. DataStoreNewRecord() adds a new record and makes that record the "current record."

To write to a record, call DataStoreSetField() . When you write to a record, you are actually writing to a copy of the record loaded in the record buffer. Any changes made to this copy do not become permanent until you call DataStoreSaveRecord() . To cancel changes, call DataStoreDiscardChanges() . Both routines flush the current record from the buffer.

Most routines that access fields (such as DataStoreSetField() ) take both a FieldID and a field name as parameters. You can reference the field by either parameter. To reference a field by its name, pass its name in the name parameter and zero in the FieldID parameter. To reference a field by its FieldID , pass its FieldID in the FieldID parameter andNULL in the name parameter. Fields are numbered from zero. (There are routines for obtaining the FieldID corresponding to a given field name and vice versa; see DataStoreFieldNameToID() and DataStoreFieldIDToName() in the C Reference Book.)

The following code example shows how to open an existing datastore, add a new record, and write data to two of its fields.

Code Display 9-2 Adding a Record to a DataStore

    /* Opening a datastore returns a token. */
    word			dsToken;
    /*
     * Saving changes to a record returns both the record's RecordNum
     * and RecordID
     */
    RecordID			recordID;
    RecordNum			recordNum;
    /*
     * Open the "Exchange Rates" datastore with sharable access
     * (i.e., no flag passed). "Oself" refers to the object which is to
     * receive change notifications; in this case, assume oself refers to
     * the process class.
     */
    if(DataStoreOpen("Exchange Rates", oself, 0, &dsToken) == DSE_NO_ERROR)
    {
        /* Add a new record. */
        if(DataStoreNewRecord(dsToken) == DSDE_NO_ERROR)
        {
            /*
             * Write data to the country and exchange rate fields.
             * You can refer to a field by its name or FieldID;
             * the examples below show both methods.
             */
            TCHAR			countryBuffer[] = "Albania";
            /* refer to a field by its name */
            DataStoreSetField(dsToken, "country", 0, countryBuffer,
                            strlen(countryBuffer));
            FloatNum			rateBuffer = .9234;
            /* refer to a field by its FieldID */
            DataStoreSetField(dsToken, NULL, 3, &rateBuffer,
                            sizeof(rateBuffer));
            /* Save the record. */
            DataStoreSaveRecord(dsToken, 0, 0, &recordNum, &recordID);
        }
        /* Close the datastore. */
        DataStoreClose(dsToken);
    }

DataStoreSaveRecord() writes the RecordNum and RecordID of the saved record to the passed variables. A record's RecordNum is its relative place in the datastore; this value may change when records are added or deleted. A record's RecordID is its unique identifier and does not change. RecordNums are numbered from zero; RecordIDs are numbered from one.

You can use a callback function with DataStoreSaveRecord() to specify where in the datastore the record is to be saved. The calling routine passes the record to be inserted (rec1) and a record from the datastore (rec2) to the callback; the callback decides which of the two records comes before the other. (The callback cannot modify the records, however.)

The callback should return a value greater than zero if rec1 comes before rec2; otherwise, a value less than zero. Declaration of Callback Function in DataStoreSaveRecord() shows the declaration of the callback.

Code Display 9-3 Declaration of Callback Function in DataStoreSaveRecord()

sword SortCallback(RecordHeader *rec1, RecordHeader *rec2,
                        word dsToken, void *cbData);

The actual data in the record follows the RecordHeader . Use DataStoreGetFieldPtr() or DataStoreFieldEnum() to access fields within the records.

typedef struct {
    RecordID	RH_uniqueID;
    byte	RH_fieldCount; /* # of fields */
    word	RH_size; /* # of bytes */
} RecordHeader;

If you do not specify a callback routine in DataStoreSaveRecord() , it will insert the record according to values in the key field(s). If two records have matching values in one key field, they will be inserted according to the first non-matching key field value. Records with matching key field(s) values are stored in the order they are added. Records with empty key fields are inserted at the beginning of the file.

If there is no callback or key, records are added to the end of the file.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Adding Fields | Next: 2.3 Deleting Records


Deleting Records

DataStore Library: 2.3 Creating a DataStore: Deleting Records

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Adding Records | Next: 2.4 Deleting Fields
DataStoreDeleteRecord(), DataStoreDeleteRecordNum()

To delete a record by its RecordID , call DataStoreDeleteRecord() . To delete a record by its RecordNum , call DataStoreDeleteRecordNum() . When deleting records, remember that RecordID s number from one and stay constant but that RecordNum s (like array elements) number from zero and change (whenever records are added or deleted). Be sure to use the correct value with the appropriate routine.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Adding Records | Next: 2.4 Deleting Fields


Deleting Fields

DataStore Library: 2.4 Creating a DataStore: Deleting Fields

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Deleting Records | Next: 3 Deleting a DataStore
DataStoreGetNumFields(), DataStoreRemoveFieldFromRecord(), DataStoreDeleteField()

To optimize storage, empty fields are not stored; thus, it is possible for a datastore to be defined with four fields but contain records with fewer than four fields.

DataStoreGetNumFields() returns the number of fields in the current record. DataStoreRemoveFieldFromRecord() deletes a field from the current record. (Note that passing zero in the size parameter in DataStoreSetField() will also cause the field to be removed from the record.)

To delete a field from the datastore itself, call DataStoreDeleteField() .

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Deleting Records | Next: 3 Deleting a DataStore


Deleting a DataStore

DataStore Library: 3 Deleting a DataStore

| Up: GEOS SDK TechDocs | Up | Prev: 2 Creating a DataStore | Next: 4 Building an Index
DataStoreDelete()

To delete a datastore, call DataStoreDelete() . This routine will delete the entire file or return DSE_DATASTORE_IN_USE if the datastore is in use or DSE_DATASTORE_NOT_FOUND if the datastore does not exist.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Creating a DataStore | Next: 4 Building an Index


Building an Index

DataStore Library: 4 Building an Index

| Up: GEOS SDK TechDocs | Up | Prev: 3 Deleting a DataStore | Next: 5 String Search
DataStoreBuildIndex()

You can create a secondary index ( i.e. , an index based on a non-key field) by calling DataStoreBuildIndex() . This routine builds an index based on a single field or on sort criteria specified in a callback function.

DataStoreBuildIndex() creates an array of RecordNum s (low word only) and stores the array in an LMem (local memory) block. The block contains an IndexArrayBlockHeader which holds data about the index (the number of records in the index, the offset to the beginning of the array, etc.). Following the block header is space for writing your own data, which is followed by the index itself. There is no limit to how much data you can write but the amount of data you write naturally decreases the amount of space available for the index. If the LMem block is too big to be allocated, the routine will return DSSE_MEMORY_FULL.

The following example shows how to build an index on a field and write data to the index's block header.

Code Display 9-4 Building a Secondary Index

    /* index parameters */
    DataStoreIndexCallbackParams	params;
    /* Building an index returns the handle of the allocated block */
    MemHandle	indexHandle;
    /* sample data to be added to the index block */
    TCHAR	indexData[]  = "Index created 7/1/96.";
    /*  Set up index parameters. */
    params.DSICP_indexField = 2; /* field on which to build index */
    params.DSICP_sortOrder = SO_DESCENDING; /* sort direction */
    params.DSICP_cbData = NULL; /* data to be passed to the callback
                    function if a callback is being used */
    /*
     * Open the datastore. (In this example, assume that dsToken
     * has been declared as a global variable.)
     */
    if(DataStoreOpen("Exchange Rates", oself, 0, &dsToken) == DSE_NO_ERROR)
    {
        /* Build the index. */
        if(DataStoreBuildIndex(dsToken, &indexHandle,
                sizeof(IndexArrayBlockHeader)+
                LocalStringSize(indexData)+sizeof(TCHAR),
                &params, NULL) == DSSE_NO_ERROR)
        {
            /* get a pointer to the block header */
            IndexArrayBlockHeader *pIndex;
            /* lock the block down */
            MemLock(indexHandle);
            /*
             * dereference the handle to get a pointer
             * to the block header
             */
            pIndex = MemDeref(indexHandle);
            /* increment the pointer past the block header */
            pIndex++;
            /* now copy the sample data into the block */
            strcpy((TCHAR *) pIndex,  indexData);
            /* Now that we're through with the block,unlock it. */
            MemUnlock(indexHandle);
        }
        /* Close the datastore. */
        DataStoreClose(dsToken);
    }

You can also build an index based on a custom callback routine. The calling routine passes the DataStoreCallbackParams to the callback; the callback decides which of the two records ( DSICP_rec1 or DSICP_rec2 ) should go first. (If you use a callback, DataStoreBuildIndex() will ignore DSICP _indexField and DSICP _sortOrder .)

The callback should return:
-1 if DSICP_rec1 comes before DSICP_rec2
 1 if DSICP_rec1 comes after DSICP_rec2

Code Display 9-5 Declaration of Callback Function in DataStoreBuildIndex()

sword SortCallback(word dsToken, DataStoreIndexCallbackParams *params);

DataStoreBuildIndex() works on datastores of 4,000 records or less. If you call this routine on a datastore larger than 4,000 records, the routine will return DSSE_INDEX_RECORD_NUMBER_LIMIT_EXCEEDED.

The application owns this index and is responsible for freeing the block. The DataStore Manager does not maintain the index in any way. Applications can synchronize a secondary index by rebuilding it whenever the application receives notification of a change that would affect the index.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Deleting a DataStore | Next: 5 String Search


String Search

DataStore Library: 5 String Search

| Up: GEOS SDK TechDocs | Up | Prev: 4 Building an Index | Next: 6 Enumeration
DataStoreStringSearch()

To do a simple string search (on a specified field or field category), use DataStoreSearchString() . Starting at a specified record number, this routine searches through each record until it finds a match or until it runs out of records to search.

DataStoreSearchString() uses the following parameters:

typedef struct {
    SearchType	SP_searchType;
    RecordNum	SP_startRecord;
    dword	SP_maxRecords;
    FieldID	SP_startField;
    FieldCategory	SP_category;
    TCHAR	*SP_searchString;
    SearchFlags	SP_flags;
} SearchParams;
SP _searchType
Possible flags:
  • ST_ALL
    Search all fields of FieldType DFST_STRING.
  • ST_FIELD
    Search by FieldID . (Specify the FieldID in SP_startField , explained below.)
  • ST_CATEGORY
    Search by FieldCategory . (Specify the FieldCategory in SP_category , explained below.)
SP _startRecord
RecordNum of record to begin search. This routine updates this field with the RecordNum of the last record examined.
SP _maxRecords
Maximum number of records to search. Allows the program to break out of the search if no matches are found. (The search can be continued by incrementing the value of SP_startRecord .) Passing -1 causes the routine to search all records.
SP _startField
FieldID of field to search if SP_searchType is set to ST_FIELD. If you specify a non-string field, the routine will return DSE_BAD_SEARCH_PARAMS.

FieldID of the field to begin the search if SP_searchType is set to ST_CATEGORY. If you specify a field that is not of the specified category, the routine will start with the next field it finds of the specified category.
SP _category
FieldCategory to search if SP_searchTyp e is set to ST_CATEGORY (otherwise this parameter is ignored).
SP _searchString
Null-terminated string to search.
SP _flags
Possible flags:
  • SF_BACKWARDS
    Search backwards through the records.
  • SF_START_AT_END
    Begin search from first record (going forward) or from last record if SF_BACKWARDS is also passed. If set, SP_startRecord is ignored.
  • SF_IGNORE_CASE
    Ignore case when searching.
  • SF_NO_WILDCARDS
    Ignore wild card characters.

    Many libraries including DataStore support the use of wild card characters for searching. See the Text object chapter for a discussion of wild card searching; also, see /Appl/Datastore/datastore.goc for a code example.
  • SF_PARTIAL_WORD
    Treat search string as a substring. For example, if search string is "den," return "gardenia," "dentist," and "garden."

The following example shows how to set up a simple string search on a specified field.

Code Display 9-6 Searching a DataStore

    /* search conditions */
    SearchParams	params;
    /*  Specify search parameters. */
    params.SP_searchType = ST_FIELD; /* search by FieldID */
    params.SP_startRecord = 0; /* start search at first record */
    params.SP_maxRecords = -1; /* search all records until a match is found or
                    there are no more records to search */
    params.SP_startField = 1; /* search "country" field */
    params.SP_searchString = "Albania"; /* string to search for */
    params.SP_flags = SF_IGNORE_CASE; /* ignore case when searching */
    /* Open the datastore. */
    if(DataStoreOpen("Exchange Rates", oself, 0, &dsToken) == DSE_NO_ERROR)
    {
        /* Do the search. */
        if(DataStoreStringSearch(dsToken, &params) == DSDE_NO_ERROR)
        {
             /*
              * If a match is found, load the record and get the data
              * from the "exchange rate" field.
              * Note:  DataStoreStringSearch() returns the record
              * number of the last examined record in SP_startRecord.
              */
            if(DataStoreLoadRecordNum(dsToken, params.SP_startRecord,
                            &recordID) == DSDE_NO_ERROR)
            {
                /* variables used for retrieving field data */
                FloatNum		rateBuffer, *pRateBuffer;
                RecordID		recordID;
                MemHandle		dummy;
                word		size;
                pRateBuffer = &rateBuffer;
                size = sizeof(rateBuffer);
                DataStoreGetField(dsToken, "exchange rate", 0,
                    (void **)&pRateBuffer, &size, &dummy);
                /*
                  * Do something with the data then
                  * flush the record from the buffer.
                  */
                DataStoreDiscardRecord(dsToken);
            }
        DataStoreClose(dsToken);
        }
    }

If the routine returns DSE_NO_MORE_RECORDS, it has reached the last record in the file (either the first or last record depending on the direction of the search). If the routine returns DSE_NO_MATCH_FOUND, it did not find a match within the set of records it searched. If it returns DSE_NO_ERROR, it writes the RecordNum of the matching record in SP_startRecord .

The DataStore Library does not implement global searches ( i.e. , searches through multiple datastores), though it is possible to implement this type of search at the application level by opening each datastore file and calling DataStoreStringSearch() on each one.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Building an Index | Next: 6 Enumeration


Enumeration

DataStore Library: 6 Enumeration

| Up: GEOS SDK TechDocs | Up | Prev: 5 String Search | Next: 7 Timestamping
DataStoreFieldEnum(), DataStoreRecordEnum()

DataStoreFieldEnum() enumerates through fields of a record. This routine uses a Boolean callback to determine whether to continue enumeration. If the callback returns TRUE, enumeration stops.

DataStoreRecordEnum() enumerates through records of a datastore in storage order, starting at the specified RecordNum in the specified direction. This routine uses a Boolean callback routine to determine whether to continue enumeration. If the callback returns TRUE, enumeration ends; if FALSE, enumeration continues until the callback returns TRUE or until the routine reaches the last record.

There are two DataStoreRecordEnumFlags that can be passed in this routine:

  • DSREF_BACKWARDS
    Enumerate backwards through the datastore.
  • DSREF_START_AT_END
    Enumerate from first record (going forward) or from last record if DSREF_BACKWARDS is also passed.

The following example enumerates through the a datastore looking for the maximum value in a particular field.

Code Display 9-7 Enumerating Through a Datastore

    /* data to be passed to the callback routine */
    FloatNum	enumData = 0;
    /* record at which to start enumeration */
    RecordNum	rec = 0;
    /*
     * Open the datastore. For this example, assume dsToken
     * is a global variable.
     */
    if(DataStoreOpen("Exchange Rates", oself, 0, &dsToken) == DSE_NO_ERROR)
    {
        /*
         * Enumerate through the datastore starting at the first record
         * (so pass zero in the flags parameter);
         * find the maximum value of the "exchange rate" field.
         */
        if(DataStoreRecordEnum(dsToken, &rec, 0, &enumData, EnumCallback)
                            == DSE_NO_MORE_RECORDS)
        {
            /* do something with the value */
        }
        DataStoreClose(dsToken);
    }
    /*
     * The callback compares the data in the "exchange rate" field to the value
     * passed in with enumData. If the field data is greater than that of
     * enumData, copy the field's data to enumData.
     */
    Boolean EnumCallback(RecordHeader *record, void *enumData)
    {
        /* parameters for getting field data */
        FloatNum	rateBuffer, *pRateBuffer;
        FieldType	type;
        word	size;
        if(DataStoreGetFieldPtr(dsToken, record, 3, (void **)&pRateBuffer,
                        &type, &size) == DSDE_NO_ERROR)
        {
            if(*pRateBuffer > *((FloatNum *)enumData))
            {
                *((FloatNum *)enumData) = *pRateBuffer;
            }
        }
        return FALSE; /* FALSE to continue enumeration */
    }

If the routine returns DSE_NO_MORE_RECORDS, it has reached the last record in the file. If it returns DSE_NO_ERROR, it writes the RecordNum of the last record examined in the startRecord parameter.

| Up: GEOS SDK TechDocs | Up | Prev: 5 String Search | Next: 7 Timestamping


Timestamping

DataStore Library: 7 Timestamping

| Up: GEOS SDK TechDocs | Up | Prev: 6 Enumeration | Next: 8 Synchronization
DataStoreGetTimeStamp(), DataStoreSetTimeStamp()

Record timestamping is important for synchronization and reconciliation of data between devices; between a portable device and a desktop PC, for example. Passing the FT_TIMESTAMP flag when creating a new datastore makes the first field a timestamp field. The DataStore Manager updates this field when the record has been modified.

You can read the data in the timestamp field by calling DataStoreGetField() and passing zero for the FieldID parameter and NULL for field name.

To retrieve the time and date a datastore was last changed, call DataStoreGetTimeStamp() . To modify a datastore's timestamp manually, call DataStoreSetTimeStamp() .

| Up: GEOS SDK TechDocs | Up | Prev: 6 Enumeration | Next: 8 Synchronization


Synchronization

DataStore Library: 8 Synchronization

| Up: GEOS SDK TechDocs | Up | Prev: 7 Timestamping

The DataStore Manager ensures synchronization between applications sharing the same datastore file by issuing a series of read and write locks. Routines that modify data request write locks while non-modifying routines request read locks. If the DataStore Manager cannot grant a lock because another session has locked the datastore, the routine will return DSE_DATASTORE_LOCKED.There can be up to thirty-two readers per datastore.

| Up: GEOS SDK TechDocs | Up | Prev: 7 Timestamping


The Import/Export Library

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Impex Basics
| One-page Version of this Chapter for Easy Printing

Most applications which write data files should be compatible with other applications. For example, applications will want to be able to import data, i.e. open a file written by another application and translate its data into a format the application can use. However, writing code to open another application's file and translate it into your own application's format can be difficult. Any application that wanted to properly support file-importing would have to provide a great many of these utilities. Furthermore, there would be a lot of duplicated effort; one application's routine for importing a particular file format would be much like another's. Applications would have similar problems exporting data, i.e. writing their data in another application's format.

The Impex (Import/Export) Library, along with specific translation libraries, solves these problems. They automatically translate files from non-GEOS formats into the Metafile (i.e. Clipboard) format and back. Applications just need to include the Import and Export objects; once they do this, importing and exporting data is just like using the Clipboard. Furthermore, extending the Impex Library to handle new formats is easy. All you have to do is write a new translation library for that one format and put the library in the SP_IMPORT_EXPORT_DRIVERS standard path. All applications which use the Impex objects will then be able to use that new format. Developers can write their own translation libraries; however, few will need to do this, since GEOS provides libraries for the most popular formats.

The Impex library currently supports import and export of text and graphic files. Geoworks is planning on extending it to allow the import and export of spreadsheet and font files as well. When these capabilities are available, they will be just like the already-supported text and graphic import and export.

1 Impex Basics
1.1 The Impex Objects
1.2 How the Impex Objects Work
2 Using Impex
2.1 Common Impex Concepts
2.2 The ImportControl Object
2.3 The ExportControl Object
3 Writing Translation Libraries
3.1 How Translation Libraries Work
3.2 Intermediate Formats

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Impex Basics
| One-page Version of this Chapter for Easy Printing


Impex Basics

The Import/Export Library: 1 Impex Basics

| Up: GEOS SDK TechDocs | Up | Down | Prev: The Import/Export Library | Next: 2 Using Impex

The Impex library provides a uniform way of importing data from files written by non-GEOS applications, and of exporting data to files written in these formats. It also saves coding time. Every outside format needs a single translation library, which translates between the Metafile format and the format for that specific library. The Impex objects provide all the necessary user interface (though the libraries and applications can add more), and also do all necessary interaction with the translation libraries.

In order to import data, an application will need to have an ImportControl object. Similarly, in order to export data, an application will need an ExportControl. Most applications that produce data files will want to use both of these objects.

Some applications will create a new GEOS file for imported data; others will add it into the current document, as if they were pasting data from the clipboard. Similarly, some applications will export the entire current document; others will export only the current selection, if any. This is left entirely to the discretion of the application.

The Impex Objects

How the Impex Objects Work

| Up: GEOS SDK TechDocs | Up | Down | Prev: The Import/Export Library | Next: 2 Using Impex


The Impex Objects

The Import/Export Library: 1.1 Impex Basics: The Impex Objects

| Up: GEOS SDK TechDocs | Up | Prev: 1 Impex Basics | Next: 1.2 How the Impex Objects Work

There are two Impex objects which applications need to know about: ImportControl and ExportControl. These objects are both subclassed off of ImportExportClass , which provides some functionality needed for both objects; however, all this functionality is internal, so you can ignore this class. (No one should ever create an ImportExport object, and it has no instance data which applications may set.) ImportExportClass is itself subclassed from GenControlClass ; therefore, ImportControl and ExportControl objects inherit all the functionality of controllers.

Applications will generally have one ImportControl and one ExportControl object. Applications may put these in different places; for example, some will place both of these on the File menu, while others may put them in the "Open" or "Save As" dialog boxes. Applications should decide this based on how they think the user will be using the import and export capabilities.

The ImportControl and ExportControl objects communicate with the application by sending messages. The ImportControl object sends a message to its recipient near the end of the Import process, when it has translated a file into a VM chain for the application to copy. The ExportControl object sends a message near the beginning of the export process, instructing the application to create a VM chain for the application to export. In both cases the message sent and the recipient are set by the application. Most applications will have the messages sent to their process arguments; however, applications which use the object model of document control may want to have the messages sent to the target object.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Impex Basics | Next: 1.2 How the Impex Objects Work


How the Impex Objects Work

The Import/Export Library: 1.2 Impex Basics: How the Impex Objects Work

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 The Impex Objects | Next: 2 Using Impex

The Impex objects manage the user interface for importing and exporting data. They also open and create the appropriate files when necessary. If a user wants to import a file, the Import object lets the user select a source file and format. The import object will then call an appropriate library to translate the source file into a VM chain in the clipboard (i.e. MetaFile) format. At this point, it sends a message to the application, passing the handle of the VM chain. Similarly, when the user wants to export data, the Export object first lets the user select a name and format for the destination file. The Export object then sends a message to the application requesting a source VM chain in the Metafile format. The Export object then calls the appropriate translation library, which translates the MetaFile into a corresponding DOS file in the specified format.

It may help to give a blow-by-blow example. Let's suppose someone is using FooPaint, a graphics program for GEOS. FooPaint uses both the ImportControl and ExportControl objects to let the user import and export graphics. The user wants to import a graphic file written by BazDraw, a non-GEOS application. The user activates the "Import" trigger. The following things happen:

  1. The ImportControl checks what translation libraries are available. This tells it what formats can be imported.
  2. The ImportControl presents a dialog box to the user. This box contains a list of available formats. It also contains a file selector, which the user uses to select a file of the appropriate format. Assume the user chooses the BazDraw format, and the file FISH.BAZ.
  3. The ImportControl opens FISH.BAZ and a temporary VM file. It then starts up the BazDraw-to-Metafile library. It then sends a message to this library, passing both files' handles with the message.
  4. The library reads FISH.BAZ and writes an equivalent Metafile sequence in a VM chain in the temporary file. When it is done, it returns the VM chain handle to the ImportControl.
  5. The ImportControl sends a message to its destination object (often the application's Process object). This is the application's first direct involvement in the import. The message includes such information as the handles of the VM chain, the type of data being imported (in this case, a graphics metafile), and the message to send on completion.
  6. The application copies the information from the VM chain. This is almost identical to pasting data from the clipboard. FooPaint will probably respond by adding the graphic to the target document.
  7. When the application is finished copying the data, it calls the ImpexImportExportCompleted() utility routine. This routine sends an appropriate acknowledgment message to the ImportControl object. The ImportControl destroys the temporary VM file.

Note that almost all of this takes place without any action by the application. The only thing the application has to do is copy data from the VM chain to its own file, translating from the Metafile format to its own format. This is exactly what the application does whenever it pastes data from the clipboard; thus, applications which use the clipboard already have almost everything they need to use the Impex objects.

Exporting data is almost the same as importing it. The application is sent a message at the beginning of the operation, asking the application to write a VM chain in the Metafile format. The ExportControl will already have created a temporary VM file for the application to use; the application just has to allocate a chain in it and fill it with the appropriate information. The ExportControl presents a dialog box to the user, letting the user select a name, format, and location for the exported file; it then starts up the appropriate translation library to write the file.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 The Impex Objects | Next: 2 Using Impex


Using Impex

The Import/Export Library: 2 Using Impex

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Impex Basics | Next: 3 Writing Translation Libraries

The Impex objects are easy to use. Essentially, an application has to declare two objects and handle a single message from each of them. Applications which can use the clipboard already have most of the code they will need to use the Impex objects.

Common Impex Concepts

The ImportControl Object

The ExportControl Object

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Impex Basics | Next: 3 Writing Translation Libraries


Common Impex Concepts

The Import/Export Library: 2.1 Using Impex: Common Impex Concepts

| Up: GEOS SDK TechDocs | Up | Prev: 2 Using Impex | Next: 2.2 The ImportControl Object

There are certain concepts and structures which are used by both of the Impex objects. Applications which use the Impex objects will have to be familiar with them.

Metafile Formats

ImpexDataClasses

The Impex objects are designed to translate into a few specific formats. These formats are known collectively as the Metafile formats. These formats (except for the "font" format) are identical to the standard "Clipboard Item Formats" described in the Clipboard chapter. When an application uses the Impex objects, it has to specify what types of data it is prepared to import or export. It does this by setting an ImpexDataClasses record in both the ImportControl and the ExportControl objects. ImpexDataClasses has the following fields:

IDC_TEXT
The data is in the Metafile Text format. This is identical to the Clipboard's CIF_TEXT format. In addition to text, this format records information about fonts, spacing, embedded graphics, etc.
IDC_GRAPHICS
The data is in the Metafile Graphics format. This is simply a GString in a VM chain. This format is identical to the Clipboard's CIF_GRAPHICS_STRING format.
IDC_SPREADSHEET
The data is in the Metafile Spreadsheet format. This corresponds to the Clipboard's CIF_SPREADSHEET format.
IDC_FONT
The data is in the Metafile Font format. This format is used to translate fonts between representations.

Every translation library translates a DOS file into a VM chain in one of these four formats, and vice versa. When an application declares an Impex object, it specifies what formats should be supported for that application. The user will be allowed to choose translation libraries which translate to or from a supported Metafile format.

ImpexTranslationParams

ImpexTranslationParams

The Import and Export objects have to pass information to the application and the translation libraries. The same sort of information gets passed in many situations; for example, the objects often have to pass the VMFileHandle and VMBlockHandle which specify the VM chain. For simplicity, the Impex objects just pass a pointer to a ImpexTranslationParams structure when they send messages to the translation libraries or the application. The library or application takes the appropriate action, changing the ImpexTranslationParams structure as necessary; it then sends a response message, which takes a pointer to the same ImpexTranslationParams . ImpexTranslationParams has the following structure:

typedef struct {
     optr			ITP_impexOD;
    Message			ITP_returnMsg;
    ImpexDataClasses			ITP_dataClass;
    VMFileHandle			ITP_transferVMFile;
    VMBlockHandle			ITP_transferVMChain;
    dword			ITP_internal;
    ManufacturerID			ITP_manufacturerID;
    ClipboardFormat			ITP_clipboardFormat;
} ImpexTranslationParams;
ITP_impexOD
This field holds the optr of whatever Impex object sent the message. The response message should be addressed to this optr.
ITP _returnMsg
This field holds the message which the library or application should send to the Impex object when it is finished. This message always takes a single argument, namely a pointer to the ImpexTranslationParams . The application should just pass the pointer to the ImpexTranslationParams to the routine ImpexImportExportCompleted() ; this routine will send the appropriate notification message.
ITP _dataClass
This is a ImpexDataClasses record. Exactly one of the flags will be set, indicating what sort of Metafile format is being used.
ITP_transferVMFile
This is the VMFileHandle of the transfer file. The transfer file is automatically created and destroyed by an Impex object.
ITP _transferVMChain
This is the VMBlockHandle of the first block in the transfer VM chain. In Import operations, the translation library creates the VM chain in the transfer file; in Export operations, the application creates it. The chain will be freed when the Impex object destroys the transfer VM file. For information about creating VM chains, see the VM chapter.

ITP_internal
 This field is for internal use by the Impex objects. You should not change it.

ITP_manufacturerID
 This field contains the manufacturer ID which should be used for the Metafile data; see the Clipboard chapter.

ITP_clipboardFormat

This field specifies what format should be used for the Metafile data; see the Clipboard chapter.

The meaning of each field can change, depending on the circumstances. For example, when the ImportControl sends its message to the application, the ITP _transferVMChain field will contain the handle of a VM chain containing the imported data. In contrast, when the ExportControl sends its message, ITP _transferVMChain contains a null handle; the application should allocate a VM chain, fill it with data, and write the VMBlockHandle of the chain to this field. When a field has a special meaning, the documentation will explain it.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Using Impex | Next: 2.2 The ImportControl Object


The ImportControl Object

The Import/Export Library: 2.2 Using Impex: The ImportControl Object

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Common Impex Concepts | Next: 2.3 The ExportControl Object

Applications which use the Impex library will generally have a single ImportControl object. This object is usually a child of the File menu; however, applications may put it wherever they want. They should also place it on the application object's GAGCNLT_SELF_LOAD_OPTIONS General Change Notification list. Applications should disable this object (with MSG_GEN_SET_NOT_ENABLED) whenever they are not prepared to accept imported data; for example, some applications will choose to disable file importing whenever they are unable to open a new document.

The ImportControl object is subclassed from GenControlClass (by way of ImportExportClass , as noted above). It thus has all the functionality of that class. It also has a few instance data fields of its own; they are shown in ImportControlClass Instance Data .

Code Display 16-1 ImportControlClass Instance Data

/* ICI_attrs is a word-length record which stores attribute information for the
 * ImportControl object. It has only one flag, ICA_IGNORE_INPUT. */
    @instance ImportControlAttrs					ICI_attrs = 0;
/* ICI_dataClasses is a word-length record which indicates what Metafile formats
 * are supported by the application. The application must set this field. */
    @instance ImpexDataClasses					ICI_dataClasses = 0;
/* ICI_destination and ICI_message indicate what message should be sent when the
 * ImportControl object has finished importing a file. The application must set
 * these fields. The message must take a single argument, namely a pointer to an
 * ImpexTranslationParams structure in ss:bp (on the stack). */
    @instance optr					ICI_destination;
    @instance word					ICI_message;
/* Applications may wish to add their own UI objects to the Import dialog box.
 * They can do so by defining a generic tree (the top object of which must be not
 * "usable"), and putting an optr to the top object in ATTR_IMPORT_CONTROL_APP_UI.
 */
    @vardata optr 					ATTR_IMPORT_CONTROL_APP_UI;
/* Controller features flags */
typedef ByteFlags 			ImportControlFeatures;
#define IMPORTCF_BASIC			0x01
typedef	ByteFlags 			ImportControlToolboxFeatures;
#define IMPORTCTF_DIALOG_BOX				0x01

ICI_attrs

ImportControlAttrs, MSG_IMPORT_CONTROL_GET_ATTRS, MSG_IMPORT_CONTROL_SET_ATTRS

ICI _attrs is a word-length record of type ImportControlAttrs . This record contains only one flag:

ICA_IGNORE_INPUT
If this flag is on, the ImportControl will consume all input to the application while the import occurs. By default, this flag is off.

To find out the current setting of this field, send MSG_IMPORT_CONTROL_GET_ATTRS to the ImportControl. To change this field, send MSG_IMPORT_CONTROL_SET_ATTRS to the ImportControl.

MSG_IMPORT_CONTROL_GET_ATTRS

ImportControlAttrs	 MSG_IMPORT_CONTROL_GET_ATTRS();

This message retrieves the current setting of the ImportControl's ICI _attrs field.

Source: Unrestricted.

Destination: Any ImportControl object.

Return: The ImportControl's ICI _attrs field.

Interception: This message should not be intercepted.

MSG_IMPORT_CONTROL_SET_ATTRS

void	MSG_IMPORT_CONTROL_SET_ATTRS(
        ImportControlAttrs		attrs);

This message changes the current settings of an ImportControl's ICI _attrs field.

Source: Unrestricted.

Destination: Any ImportControl object.

Parameters: attrs The new settings for the ICI _attrs field.

Interception: This message should not be intercepted.

ICI_dataClasses

MSG_IMPORT_CONTROL_GET_DATA_CLASSES, MSG_IMPORT_CONTROL_SET_DATA_CLASSES

When you declare an Import object, you must specify what kind of Metafiles your application is prepared to accept. You do this by setting the value of the ICI _dataClasses field. This field is a word-length record of type ImpexDataClasses (described in ImpexTranslationParams ). If (for example) only the IDC_TEXT bit is set, the ImportControl will use only those import libraries which produce text Metafile output. More than one bit may be set; when the ImportControl sends its notification, it will tell the application what type of data is being imported.

To find out the current settings of the ICI _dataClasses field, send MSG_IMPORT_CONTROL_GET_DATA_CLASSES. To change the settings of this field, send MSG_IMPORT_CONTROL_SET_DATA_CLASSES.

MSG_IMPORT_CONTROL_GET_DATA_CLASSES

ImpexDataClasses	 MSG_IMPORT_CONTROL_GET_DATA_CLASSES();

This message retrieves the current setting of the ImportControl's ICI _dataClasses field. This tells you what kind of data can be imported.

Source: Unrestricted.

Destination: Any ImportControl object.

Return: The ImportControl's ICI _dataClasses field.

Interception: This message should not be intercepted.

MSG_IMPORT_CONTROL_SET_DATA_CLASSES

void	MSG_IMPORT_CONTROL_SET_DATA_CLASSES(
        ImpexDataClasses 		dataClass);

This message changes the current settings of an ImportControl's ICI _dataClasses field.

Source: Unrestricted.

Destination: Any ImportControl object.

Parameters: dataClass The new settings for the ICI _dataClasses field.

Interception: This message should not be intercepted.

The ImportControl Action

MSG_IMPORT_CONTROL_GET_ACTION, MSG_IMPORT_CONTROL_SET_ACTION, ImpexImportExportCompleted()

The ImportControl does most of its work transparently to the application. It interacts with the rest of the application only when the user has selected a file to import and the appropriate translation library has produced a VM chain. At this point the ImportControl sends a notification message to the application. The application responds by copying the data from the VM chain and sending back an acknowledgment message. The ImportControl can then destroy the temporary VM transfer file.

The application determines what message will be sent, and to what object, by setting the ICI _destination and ICI _message fields. Whatever object will receive the message should define an appropriate message. The ImportControl will send this message with a single parameter: itp , a pointer to an ImpexTranslationParams structure (see ImpexTranslationParams ). The fields of the structure have the following meanings in this case:

ITP_impexOD
The object to which the application should send its acknowledgment message. In this case, it is the optr of the ImportControl.
ITP _returnMsg
The acknowledgment message to send when the import has been completed. In this case, it is MSG_IMPORT_CONTROL_IMPORT_COMPLETE.
ITP _dataClass
An ImpexDataClasses record with one flag set. This flag indicates what type of Metafile has been prepared.
ITP _transferVMFile
The VMFileHandle of the temporary transfer file.
ITP _transferVMChain
The VMBlockHandle of the lead block in the VM chain containing the imported data.
ITP _internal
For use by the ImportControl and should not be changed by the application.

The recipient of the message should take any appropriate action; usually this entails copying the data from the VM chain, as if it were pasting data from the Clipboard. When the application is finished, it should call ImpexImportExportCompleted() . This routine takes one parameter, namely the itp pointer which was passed to the object. (The ImpexTranslationParams structure should not have been changed.) ImpexImportExportCompleted() reads the appropriate message and destination from the ImpexTranslationParams and sends the proper acknowledgment message (which in this case is MSG_IMPORT_CONTROL_IMPORT_COMPLETE ).

Applications which use the object model of document control will often set ICI _destination to TO_APP_TARGET; this will make it send its messages to the target object. The application can find out the ImportControl's action by sending it MSG_IMPORT_CONTROL_GET_ACTION . The application can change the ImportControl's action by sending it MSG_IMPORT_CONTROL_SET_ACTION .

MSG_IMPORT_CONTROL_GET_ACTION

void	MSG_IMPORT_CONTROL_GET_ACTION(
        ImpexAction *		retValue);

This message retrieves the values of an ImportControl's ICI _destination and ICI _message fields. These fields indicate what action the ImportControl will take when it is finished preparing a file for import.

Source: Unrestricted.

Destination: Any ImportControl object.

Parameters: retValue A pointer to an ImpexAction structure.

Return: The value of ICI _message (i.e. the message sent by the ImportControl).

recipient
A pointer to an ImpexAction structure describing the message sent.

Interception: This message should not be intercepted.

Structures: The message and recipient are written to an ImpexAction structure:

typedef struct {
    word	message;		/* message sent */
    word	unused;
    optr	destOD;		/* Destination of message */
} ImpexAction;

MSG_IMPORT_CONTROL_SET_ACTION

void	MSG_IMPORT_CONTROL_SET_ACTION(
        optr	destOD,	/* Send messages to this object. */
        word	ICImsg);	/* Send this message to the above recipient. */

This message changes the values of an ImportControl's ICI _destination and ICI _message fields. These fields indicate what action the ImportControl will take when it is finished preparing a file for import.

Source: Unrestricted.

Destination: Any ImportControl object.

Parameters: destOD Set ICI _destination to this value.

ICImsg
Set ICI _message to this value.

Interception: This message should not be intercepted.

Adding to the Import Dialog Box

When the user selects the "Import" trigger or tool, the Import controller brings up a dialog box. The application can, if it wishes, add UI objects to this box. It does so by defining a tree of generic objects (the top object of which must be set "not usable"). It must place an optr to the top object in the tree in ATTR_IMPORT_CONTROL_APP_UI. When the ImportControl builds the dialog box, it will add that optr as one of the children in the tree and set it "usable".

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Common Impex Concepts | Next: 2.3 The ExportControl Object


The ExportControl Object

The Import/Export Library: 2.3 Using Impex: The ExportControl Object

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 The ImportControl Object | Next: 3 Writing Translation Libraries

Applications which use the Impex library will generally have a single ExportControl object. This object is usually a child of the File menu; however, applications may put it wherever they want. They should also place it on the application object's GAGCNLT_SELF_LOAD_OPTIONS GCN list. Applications should disable this object (with MSG_GEN_SET_NOT_ENABLED) whenever they are not able to prepare data for export; for example, some applications will choose to disable file exporting whenever the "Cut" and "Copy" functions are disabled.

The ExportControl object is subclassed from GenControlClass (by way of ImportExportClass , as noted above). It thus has all the functionality of that class. It also has a few instance data fields of its own; they are shown in ExportControlClass Instance Data .

Code Display 16-2 ExportControlClass Instance Data

/* ECI_attrs is a word-length record which stores attribute information for the
 * ImportControl object. It has only one flag, ECA_IGNORE_INPUT. */
    @instance ExportControlAttrs					ECI_attrs = 0;
/* ECI_dataClasses is a word-length record which indicates what Metafile formats
 * are supported by the application. The application must set this field. */
    @instance ImpexDataClasses					ECI_dataClasses = 0;
/* ECI_destination and ECI_message indicate what message should be sent when the
 * ExportControl object is preparing to export a file. The application must set
 * these fields. The message must take a single argument, namely a pointer to an
 * ImpexTranslationParams structure in ss:bp (on the stack). */
    @instance optr					ECI_destination;
    @instance word					ECI_message;
/* Applications may wish to add their own UI objects to the Export dialog box.
 * They can do so by defining a generic tree (the top object of which must be not
 * "usable"), and putting an optr to the top object in ATTR_EXPORT_CONTROL_APP_UI.
 */
    @vardata optr 					ATTR_EXPORT_CONTROL_APP_UI;
/* Controller features flags */
typedef ByteFlags 			ExportControlFeatures;
#define EXPORTCF_BASIC			0x01
typedef	ByteFlags 			ExportControlToolboxFeatures;
#define EXPORTCTF_DIALOG_BOX				0x01

ECI_attrs

ExportControlAttrs, MSG_EXPORT_CONTROL_GET_ATTRS, MSG_EXPORT_CONTROL_SET_ATTRS

ECI _attrs is a word-length record of type ExportControlAttrs . This record contains only one flag:

ECA_IGNORE_INPUT
If this flag is on, the ExportControl will consume all input to the application while the import occurs. By default, this flag is off.

To find out the current setting of this field, send MSG_EXPORT_CONTROL_GET_ATTRS to the ImportControl. To change this field, send MSG_EXPORT_CONTROL_SET_ATTRS to the ImportControl.

MSG_EXPORT_CONTROL_GET_ATTRS

ExportControlAttrs	 MSG_EXPORT_CONTROL_GET_ATTRS();

This message retrieves the current setting of the ExportControl's ECI _attrs field.

Source: Unrestricted.

Destination: Any ExportControl object.

Return: The ExportControl's ECI _attrs field.

Interception: This message should not be intercepted.

MSG_EXPORT_CONTROL_SET_ATTRS

void	MSG_EXPORT_CONTROL_SET_ATTRS(
        ExportControlAttrs		attrs);

This message changes the current settings of an ExportControl's ECI _attrs field.

Source: Unrestricted.

Destination: Any ExportControl object.

Parameters: attrs The new settings for the ECI _attrs field.

Interception: This message should not be intercepted.

ECI_dataClasses

MSG_EXPORT_CONTROL_GET_DATA_CLASSES, MSG_EXPORT_CONTROL_SET_DATA_CLASSES

When you declare an Export object, you must specify what kind of Metafiles your application is able to create. You do this by setting the value of the ECI _dataClasses field. This field is a word-length record of type ImpexDataClasses (described in ImpexTranslationParams ). If (for example) only the IDC_TEXT bit is set, the ExportControl will use only those export libraries which expect text Metafile input. More than one bit may be set; when the ExportControl sends its notification, it will tell the application what type of data it expects to export.

To find out the current settings of the ECI _dataClasses field, send MSG_EXPORT_CONTROL_GET_DATA_CLASSES. To change the settings of this field, send MSG_EXPORT_CONTROL_SET_DATA_CLASSES.

MSG_EXPORT_CONTROL_GET_DATA_CLASSES

ImpexDataClasses	 MSG_EXPORT_CONTROL_GET_DATA_CLASSES();

This message retrieves the current setting of the ExportControl's ECI _dataClasses field. This tells you what kind of data can be exported.

Source: Unrestricted.

Destination: Any ExportControl object.

Return: The ExportControl's ECI _dataClasses field.

Interception: This message should not be intercepted.

MSG_EXPORT_CONTROL_SET_DATA_CLASSES

void	MSG_EXPORT_CONTROL_SET_DATA_CLASSES(
        ImpexDataClasses 		dataClasses);

This message changes the current settings of an ExportControl's ECI _dataClasses field.

Source: Unrestricted.

Destination: Any ExportControl object.

Parameters: dataClasses The new settings for the ECI _dataClasses field.

Interception: This message should not be intercepted.

The ExportControl Action

MSG_EXPORT_CONTROL_GET_ACTION, MSG_EXPORT_CONTROL_SET_ACTION

The ExportControl does most of its work transparently to the application. It interacts with the rest of the application after the user selects the name, location, and format of the exported file. At this point the ExportControl creates a temporary transfer file and sends a notification message to the application; the notification message passes the file handle and the format expected. The application responds by creating a VM chain in the transfer file and filling it with the data to export, formatted in the appropriate Metafile format. The ExportControl can then call the translation library to create the output file.

The application determines what notification message will be sent, and to what object, by setting the ECI _destination and ECI _message fields. Whatever object will receive the message should define an appropriate message. The ExportControl will send this message with a single parameter: itp , a pointer to an ImpexTranslationParams structure. The fields of the structure have the following meanings in this situation:

ITP_impexOD
The object to which the application should send its acknowledgment message. In this case, it is the optr of the ExportControl.
ITP _returnMsg
The acknowledgment message to send when the export has been completed. In this case, it is MSG_EXPORT_CONTROL_EXPORT_COMPLETE.
ITP _dataClass
An ImpexDataClasses record with one flag set. This flag indicates what type of Metafile should be prepared.
ITP _transferVMFile
The VMFileHandle of the temporary transfer file.
ITP _transferVMChain
A null handle. When the application has created the transfer VM chain, it should write the VMBlockHandle of the head of the chain to this field. If the application fails for any reason, it should leave this field as a null handle.
ITP _internal
This field is for use by the ExportControl and should not be changed by the application.

The recipient of the message should take any appropriate action; usually this entails translating the current selection into the Metafile format and writing it to a VM chain. When the application is finished, it should call ImpexImportExportCompleted() . This routine will send the appropriate acknowledgment message to the ExportControl object (in this case, MSG_EXPORT_CONTROL_EXPORT_COMPLETE ). This routine takes one parameter, namely the itp pointer which was passed to the object. The ITP _transferVMChain field of the ImpexTranslationParams structure should be set to the handle of the head block in the VM chain. If the application was unable to prepare the data for export, it should clear this field.

The ExportControl object will have created a temporary file for the application to use. This file will be entirely empty when the application gets it. The ExportControl will ignore everything in the file except for the VM chain indicated by ITP _transferVMChain ; thus, an application can feel free to allocate blocks in the VM file for scratch space. The ExportControl will destroy the file when the translation library has finished preparing the output file.

Applications which use the object model of document control will often set ECI _destination to TO_APP_TARGET; this will make it send its messages to the target object. The application can find out the ExportControl's action by sending it MSG_EXPORT_CONTROL_GET_ACTION . The application can change the ExportControl's action by sending it MSG_EXPORT_CONTROL_SET_ACTION .

MSG_EXPORT_CONTROL_GET_ACTION

void	MSG_EXPORT_CONTROL_GET_ACTION(
        ObjectState *		retValue);

This message retrieves the values of an ExportControl's ECI _destination and ECI _message fields. These fields indicate what action the ExportControl will take when it needs to have data prepared for export.

Source: Unrestricted.

Destination: Any ImportControl object.

Parameters: retValue A pointer to an ObjectState structure.

Return: The value of ICI _message (i.e. the message sent by the ExportControl).

recipient
A pointer to an ObjectState structure describing the message sent.

Interception: This message should not be intercepted.

Structures: The message and recipient are written to an ObjectState structure:

typedef struct {
    int	notUsed;
    word	message;		/* Message sent */
    optr	destOD;		/* Destination of message */
} ObjectState;

MSG_EXPORT_CONTROL_SET_ACTION

void	MSG_EXPORT_CONTROL_SET_ACTION(
        optr	destOD,	/* Send messages to this object. */
        word	ECImsg);	/* Send this message to the above recipient. */

This message changes the values of an ExportControl's ECI _destination and ECI _message fields. These fields indicate what action the ExportControl will take when it is finished preparing a file for export.

Source: Unrestricted.

Destination: Any ExportControl object.

Parameters: recipient Set ECI _destination to this value.

message
Set ECI _message to this value.

Interception: This message should not be intercepted.

Adding to the Import Dialog Box

When the user selects the "Export" trigger or tool, the Export controller brings up a dialog box. The application can, if it wishes, add UI objects to this box. It does so by defining a tree of generic objects (the top object of which must be set "not usable"). It must place an optr to the top object in the tree in ATTR_EXPORT_CONTROL_APP_UI. When the ExportControl builds the dialog box, it will add that optr as one of the children in the tree and set it "usable".

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 The ImportControl Object | Next: 3 Writing Translation Libraries


Writing Translation Libraries

The Import/Export Library: 3 Writing Translation Libraries

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Using Impex | Next: 3.1 How Translation Libraries Work

GEOS comes with many translation libraries, and more are being added all the time. Geoworks is continually adding new translation libraries for popular formats. Nevertheless, we cannot guarantee to support every format. Developers may decide to write their own translation libraries. This section describes how the libraries work in enough detail that developers will be able to do preliminary work in writing the libraries.

Most applications will find the provided translation libraries sufficient for their needs. Therefore, most developers can skip this section. You should read this if you are planning on writing translation libraries, or just if you want more understanding of the importing and exporting process.

Remember: once a translation library has been written, all a user has to do to install it is copy it to the appropriate directory. All existing applications which use Impex will then automatically be able to import and export that format. Users will be able to buy translation library collections from third-party vendors the way they buy font collections now.

How Translation Libraries Work

Intermediate Formats

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Using Impex | Next: 3.1 How Translation Libraries Work


How Translation Libraries Work

The Import/Export Library: 3.1 Writing Translation Libraries: How Translation Libraries Work

| Up: GEOS SDK TechDocs | Up | Prev: 3 Writing Translation Libraries | Next: 3.2 Intermediate Formats

A translation library's task is easily stated. It has to do two things: read a native-format file and produce a Metafile translation, and read data in a Metafile and write corresponding data in a native-format file. How easy this is to do depends on the formats involved.

Every translation library specifies what format of Metafile it expects to work with. For example, the FooWrite translation library would translate FooWrite files into IDC_TEXT Metafiles and vice versa. When a user activates the "Import" trigger, he will be presented with a list of formats to use; those formats will correspond to all the libraries which can translate files into formats the application can accept. For example, if the application specified that it could accept text or graphics Metafiles, the user's choice of format would depend on which of the installed libraries could generate text or graphics Metafiles.

The translation library can also suggest a file mask. For example, the FooWrite translation library might specify that FooWrite data files meet the pattern "*.FOO". By default, the Import file selector will show only the files that match the library's mask. However, the user can override this mask, setting a different one or no mask at all.

When a user decides to import a file, the ImportControl opens the source file and creates a temporary transfer VM file. The ImportControl then starts up the appropriate translation library and passes the two file handles to it. The translation library should read the entire source file, translate it to the appropriate Metafile format, and write it to a VM chain in the transfer file. It then returns the VMBlockHandle of the head of the VMChain to the ImportControl. If it was unable to translate the file, it should return a null handle; the ImportControl then displays an appropriate error message. The ImportControl will close the source file automatically.

When a user decides to export a file, the ExportControl creates a temporary transfer file and opens an empty native-format file for the output. The ExportControl then calls the translation library. The library is passed the handles of the two files, as well as the handle of the Metafile VM chain. The library reads the Metafile and writes an appropriate data file. When it is finished, it notifies the ExportControl, which automatically closes the destination file and destroys the temporary transfer file.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Writing Translation Libraries | Next: 3.2 Intermediate Formats


Intermediate Formats

The Import/Export Library: 3.2 Writing Translation Libraries: Intermediate Formats

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 How Translation Libraries Work

Many libraries will want to make use of intermediate formats. For example, a company may have defined its own transfer format for its applications. The simplest way for it to translate files into the GEOS Metafile might be to translate the file into its own transfer format, then translate from this format into the Metafile format. GEOS supports this with its use of intermediate translation libraries.

An intermediate library is much like an ordinary translation library. Like other libraries, it must translate from its own format into the Metafile format. The only difference is that intermediate libraries are not called by the Impex objects; instead, they are called by other translation libraries.

For example, suppose FooWare, Inc., has a line of graphic FooApps which includes FooDraw, FooPaint, FooSketch, and FooScribble. FooWare has developed its own file-transfer format, FooInterchangeFormat (FIF); it has code written to translate any FooApp's files into FIF and back.

FooWare now wants to write translation libraries for GEOS. The first thing FooWare does is write an intermediate translation library which takes a DOS file containing FIF data and produces a VM chain containing a GEOS graphic Metafile, and vice versa. Once this is written, FooWare has an easy time writing the actual translation libraries. For example, the FooPaint translation library imports files by converting a FooPaint data file into the analogous FIF file. Since FooWare already has routines to do this translation, it just has to port existing code to the GEOS library. The FooPaint translation library then calls the FIF-to-Metafile intermediate translation library and gets the finished translation. Similarly, the FooPaint translation library exports data by calling the FIF-to-Metafile library to produce a FIF version of the data; it can then use ported code to produce an actual FooPaint file.

Note that any ordinary translation library can also be used as an intermediate translation library. For example, let's suppose that FooWare has code written to translate FIF files into PostScript files. This makes translating the documents even easier. The FIF-to-Metafile library can just use ported code to produce a PostScript version of the data; it can then call the PostScript-to-Metafile translation library, which is provided with GEOS. GEOS comes with translation libraries for many popular file-interchange formats; thus, many developers will be able to write translation libraries just by porting code from their pre-existing translators, then calling one of the GEOS translation libraries.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 How Translation Libraries Work


Welcome to GEOS Programming

GEOS SDK Documentation: Memory Management

| Up: GEOS SDK TechDocs

The following documents are language references.

Memory Management
Allocating and managing heap memory blocks.
Local Memory
Subdividing a memory block into smaller chunks.
Memory Levels
The Nokia 9000i Communicator's Foam library provides API for detecting and warning of low memory levels.
GEOS Programming
@chunk and other keywords allow a program to statically declare chunks of memory within resources.
Virtual Memory
The VM routines allow you to use disk memory as you would a heap block; these routines are used for file manipulation.
DB Routines
Provide an API for storing many small pieces of data in a VM file.
Handles
Low-level API for grabbing and releasing handles.
Machine Architecture
The x86 segmented memory architecture.
| Up: GEOS SDK TechDocs


Database

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing

Some applications need to keep track of many small pieces of data. For example, a database might use thousands of items of data, each of them only a paragraph long; in a spreadsheet, the data size might be only a few bytes. GEOS provides a Database (DB) library to make it easy to keep track of such data and store them conveniently in a GEOS Virtual Memory file.

The DB library manages Local Memory heaps in a VM file and uses these heaps to store items. It lets the geode associate items into groups; these groups can grow indefinitely, unlike LMem heaps.

1 Design Philosophy
2 Database Structure
2.1 DB Items
2.2 DB Groups
2.3 Allocating Groups and Items
2.4 Ungrouped DB Items
2.5 The DB Map Item
3 Using Database Routines
3.1 General Rules to Follow
3.2 Allocating and Freeing Groups
3.3 Allocating and Freeing Items
3.4 Accessing DB Items
3.5 Resizing DB Items
3.6 Setting and Using the Map Item
3.7 Routines for Ungrouped Items
3.8 Other DB Utilities

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing


Design Philosophy

Database: 1 Design Philosophy

| Up: GEOS SDK TechDocs | Up | Prev: Database | Next: 2 Database Structure

A database manager should be flexible, allowing applications to store a variety of data items. It should be efficient, with minimal overhead in data-access time as well as in memory usage (whether in main memory or in disk space). Ideally, it ought to insulate applications from the details of memory allocation and data referencing. The GEOS database manager meets all of these requirements and several more:

  • Flexible Data Formats
    The DB Manager does not care about the content of a DB item. Consequently, a DB item can be anything that can fit in an LMem chunk. An application can use one file to store many different sizes of database item.
  • Speed and Efficiency
    The GEOS DB library uses the powerful GEOS memory management, Virtual Memory, and Local Memory routines. These enable it to store and access many database items with a minimal overhead in access time and storage space.
  • Uniform Data-Access Format
    Database items are stored in standard GEOS Virtual Memory files. All of the file-access utilities (such as the document control objects) can work unchanged with database files. Furthermore, VM files can contain both ordinary VM blocks and DB items in any combination.
  • Full Group Management
    Applications can divide DB items into groups. Access time is improved when items from the same group are accessed in succession. As an alternative, applications can let the DB manager create and assign groups for the items.
  • Sharable Data
    Since DB items are stored in VM files, the files can be shared between applications. All of the standard VM synchronization routines work for DB files.
| Up: GEOS SDK TechDocs | Up | Prev: Database | Next: 2 Database Structure


Database Structure

Database: 2 Database Structure

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Design Philosophy | Next: 3 Using Database Routines

The Database routines use a Database Manager to access and create DB items. These items are stored in a standard VM file. This chapter will sometimes refer to a "Database File"; this simply means a VM file which contains DB items.

DB Items

DB Groups

Allocating Groups and Items

Ungrouped DB Items

The DB Map Item

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Design Philosophy | Next: 3 Using Database Routines


DB Items

Database: 2.1 Database Structure: DB Items

| Up: GEOS SDK TechDocs | Up | Prev: 2 Database Structure | Next: 2.2 DB Groups

The basic unit of data is the item . Items are simply chunks in special LMem heaps which are managed by the DB Manager; these heaps are called item blocks . You will not need to use any of the LMem routines; the DB manager will create and destroy LMem heaps as necessary and will call the appropriate routines to lock DB items when needed.

Each DB item in a DB file is uniquely identified by the combination of a group-handle and an item-handle . Note that these handles are not the same as the item's LMem Heap handle and its chunk handle. You will not generally need to use the item's heap and chunk handles; the DB routines store and use these automatically. However, you can retrieve them if necessary (for example, if you want to use an LMem utility on a DB item).

The DB Manager does not keep track of allocated items. Once you allocate an item, you must store the group- and item-handles. If you lose them, the item will remain in the file, but you will not be able to find it again.

Since DB items are chunks, their addresses are somewhat volatile. If you allocate an item in a group, other items in that group may move even if they are locked. (See the Local Memory chapter.)

| Up: GEOS SDK TechDocs | Up | Prev: 2 Database Structure | Next: 2.2 DB Groups


DB Groups

Database: 2.2 Database Structure: DB Groups

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 DB Items | Next: 2.3 Allocating Groups and Items

Each DB item is a member of a DB group. The DB group is a collection of VM blocks; the group comprises a single group block and zero or more item blocks.

The group block contains information about each item block and each item in the group. For each item block, it records the VM handle of the block and the number of DB items in the block. For each DB item, it records the VM handle of the item block in which the item is stored and the item's chunk handle within that item block. The item blocks are simply LMem heaps with a little extra information in the headers.

The item's group-handle is simply the VM handle of the group block for that group. The item's item-handle is an offset into the group block; the information about the item is stored at that offset. When you lock an item, the DB manager looks in that location in the group block and reads the handles of the item block and the chunk associated with that item; it then locks the item block and returns the address of the chunk. (In assembly code, it returns the segment address and the chunk handle.) The relationship between the different blocks and handles is shown in the figure below.

Whenever you access a DB item, the DB manager has to lock the block. If you access several items in a row, the overall access time is better if they belong to the same group since only one group block will need to be swapped in to memory. The items may also be in the same item-block since each item block contains items from only one group; again, this improves access time. Thus, it is a good idea to distribute items in groups according to the way they will be accessed; for example, an address-book database might group entries according to the first letter of the last name, thus speeding up alphabetical access. If you have no logical way to group items, see Ungrouped DB Items .

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 DB Items | Next: 2.3 Allocating Groups and Items


Allocating Groups and Items

Database: 2.3 Database Structure: Allocating Groups and Items

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 DB Groups | Next: 2.4 Ungrouped DB Items

When you need a new DB group, call the DB routine DBGroupAlloc() (see Allocating and Freeing Groups ). This routine creates and initializes a DB group block.

When you allocate a DB item, you specify which group the item will go in. The DB manager sets up an entry for the item in the group block. It then decides which item block to put the item in. It tries to keep all the item blocks at the right size to optimize speed. If all of the group's item blocks are too full, it allocates a new item block and allocates the new item in that block. In either case, it returns the new item's item-handle.

Once an item has been allocated, it will stay in the same item block (and have the same chunk handle) until it is freed or resized. If it is resized to a larger size, it may be moved to a different item block belonging to the same group.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 DB Groups | Next: 2.4 Ungrouped DB Items


Ungrouped DB Items

Database: 2.4 Database Structure: Ungrouped DB Items

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Allocating Groups and Items | Next: 2.5 The DB Map Item

Sometimes there is no natural way to group DB items. For these situations, the DB manager allows you to allocate ungrouped items. These items actually belong to special groups which are automatically allocated by the DB manager. The DB manager tries to keep these groups at the right size for optimum efficiency.

When you allocate an ungrouped item, the DB manager allocates an item in one of its "ungrouped groups." If there are no such groups or if all of these groups have too many items already, the DB manager allocates a new "ungrouped" group.

For practical purposes, ungrouped DB items have a single, dword-sized handle. This "handle" is of type DBGroupAndItem . The upper word of this is the handle of the ungrouped group for this item; the lower word is the item's item-handle within that group. There are special versions of most database routines for use with ungrouped items. These routines take a DBGroupAndItem argument instead of separate group-handle and item-handle arguments. These routines are discussed in Routines for Ungrouped Items . This section also describes macros which combine a group-handle and item-handle into a DBGroupAndItem and which break a DBGroupAndItem into its constituent parts.

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Allocating Groups and Items | Next: 2.5 The DB Map Item


The DB Map Item

Database: 2.5 Database Structure: The DB Map Item

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Ungrouped DB Items | Next: 3 Using Database Routines

You can designate a "map item" for a VM file with the routine DBSetMap() . You can recover the map item's group and handle at will by calling DBGetMap() . This is entirely separate from the file's map block; indeed, a VM file can have both a map block and a map item, and they may be set, locked, and changed independently.

The map routines are described in detail in Setting and Using the Map Item .

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Ungrouped DB Items | Next: 3 Using Database Routines


Using Database Routines

Database: 3 Using Database Routines

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Database Structure | Next: 3.1 General Rules to Follow

GEOS provides a wide range of routines for working with databases. The routines all require that the calling thread have the VM file open. Most routines have to be passed the VMFileHandle of the appropriate VM file.

Almost all DB routines come in two forms. The standard form takes, among its arguments, the group-handle and the item-handle of an item to be affected. The other form is designed for use with "ungrouped" items. This form takes, as an argument, the item's DBGroupAndItem structure.

In addition to the routines listed here, all of the VM chain routines can work on DB items. Simply cast the DBGroupAndItem structure to type VMChain , and pass it in place of the chain argument(s). ( VMCopyVMChain() will allocate the duplicate item as "ungrouped.") For more information about VMChain routines, see the VM chapter.

General Rules to Follow

Allocating and Freeing Groups

Allocating and Freeing Items

Accessing DB Items

Resizing DB Items

Setting and Using the Map Item

Routines for Ungrouped Items

Other DB Utilities

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Database Structure | Next: 3.1 General Rules to Follow


General Rules to Follow

Database: 3.1 Using Database Routines: General Rules to Follow

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Database Routines | Next: 3.2 Allocating and Freeing Groups

There are certain rules of "memory etiquette" you should follow when using DB files. For the most part, these rules are the same as the general rules of memory etiquette.

First and foremost, try to keep as few blocks locked as possible, and keep them locked for as short a time as possible. You should not usually need to keep more than one item locked at a time. If you need another item, unlock the first one first, even if they're in the same item block. (This will cost very little time since the item block is unlikely to be swapped to disk right away.) The main reason you should have two or more items open at once is if you are directly comparing them or copying data from one to another. In this case, you should unlock each item as soon as you're done with it.

Remember that items are implemented as chunks in LMem heaps. This means, for example, that when you allocate an item (or expand an existing one), the heap it resides in (i.e. the item block) may be compacted or moved on the global heap (even if it is locked). This will invalidate all pointers to items in that item block. As a general rule, you should not allocate (or expand) items if you have any items from that group locked. Do not allocate "ungrouped" items if you have any items from any of the "ungrouped" groups locked. If you must keep an item locked, keep track of the item's memory block and chunk handle so you can use DBDeref() to get the address again.

Finally, try to keep the blocks small. Most of this is done for you. When you allocate an item, the DB manager will put it in an uncrowded item block. If all item blocks are too large, it will allocate a new one. However, you should keep items from getting too large. If individual items get into the multi-kilobyte range, you should consider storing them a different way; for example, you could make each f the larger items a VM block or a VM chain.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Database Routines | Next: 3.2 Allocating and Freeing Groups


Allocating and Freeing Groups

Database: 3.2 Using Database Routines: Allocating and Freeing Groups

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 General Rules to Follow | Next: 3.3 Allocating and Freeing Items
DBGroupAlloc(), DBGroupFree()

You can improve DB access time by assigning items to groups such that items from the same group will generally be accessed together. This will cut down on the number of times group and item blocks will have to be swapped into memory.

To allocate a group, call DBGroupAlloc() . This routine takes one argument, namely the handle of the VM file in which to create the group. It allocates the group and returns the group-handle (i.e., the VM handle of the group block). If it is unable to allocate the group, it will return a null handle.

If you are done with a DB group, call DBGroupFree() . This routine frees the group's group block and all of its item blocks. Any attached global memory blocks will also be freed. Naturally, all items in the group will be freed as well. You can free a group even if some of its items are locked; those items will be freed immediately.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 General Rules to Follow | Next: 3.3 Allocating and Freeing Items


Allocating and Freeing Items

Database: 3.3 Using Database Routines: Allocating and Freeing Items

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Allocating and Freeing Groups | Next: 3.4 Accessing DB Items
DBAlloc(), DBFree()

To allocate a DB item, call DBAlloc() . This routine takes three arguments: the handle of the VM file, the DB Group in which to allocate the item, and the size of the item (in bytes). The routine will allocate an item in one of that group's item blocks (allocating a new item block if necessary); it returns the new item's item-handle.

Remember that when you allocate a DB item, the DB manager allocates a chunk in an LMem heap (the item block). This can cause the item block to be compacted or resized; this will invalidate all pointers to items in that block. For this reason, you should not allocate items in a group while other items in that group are locked. Similarly, you should not allocate "ungrouped" items while any "ungrouped" items are locked. Instead, unlock the items, allocate the new one, and then lock the items again.

When you are done with an item, free it with DBFree() . This routine takes three arguments: the file handle, the group-handle, and the item-handle. It frees the item, making appropriate changes in the group block. If the item was the only one in its item block, that item block will be freed as well. DBFree() does not return anything. Note that you should never free a locked item since the item-block's reference-count will not be decremented (and the block will never be unlocked). Always unlock an item before freeing it. (You need not, however, unlock items before freeing their group ; when a group is freed, all of its items are automatically freed, whether they are locked or not.)

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Allocating and Freeing Groups | Next: 3.4 Accessing DB Items


Accessing DB Items

Database: 3.4 Using Database Routines: Accessing DB Items

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Allocating and Freeing Items | Next: 3.5 Resizing DB Items
DBLock(), DBLockGetRef(), DBDeref(), DBUnlock(), DBDirty()

To access a database item, lock it with DBLock() . This routine takes three arguments: the handle of the VM file, the item's group-handle, and the item's item-handle. The routine locks the item-block on the global heap and returns the address of the element. If the block is already locked (generally because another item in the item-block is locked), it increments the lock count.

In some circumstances it might be useful to know the global handle of the locked item-block and the chunk handle of the item. For example, if you want to set up an item as a chunk array, you will need this information. For this reason, the library provides the routine DBLockGetRef() . This routine is just like DBLock() , except that it takes one additional argument: the address of a variable of type optr. DBLockItemGetRef() writes global and chunk handles to the optr and returns the address of the locked DB item. You can now use any of the LMem routines on the item, simply by passing the optr.

Note that the memory block attached to the item block may change each time the block is locked unless you have instructed the VM manager to preserve the handle (see the VM chapter). The chunk handle will not change, even if the file is closed and reopened, unless the chunk is resized larger. (When an item is resized larger, the DB manager may choose to move the item to a different item-block, thus changing its chunk handle.) In general, if you will need this information you should get it each time you lock the item instead of trying to preserve it from one lock to the next.

If you have an optr to a locked DB item, you can translate it to an address with the routine DBDeref() . This is useful if you have to keep one item locked while allocating or expanding another item in that group. Since the locked item might move as a result of the allocation, you can get the new address with DBDeref() . In general, however, you should unlock all items in a group before allocating or resizing one there. Note that DBDeref is simply a synonym for LMemDeref() ; the two routines are exactly the same.

When you are done accessing an item, call DBUnlock() . This routine takes one argument, the address of a locked item. The routine decrements the reference count of the item's item-block. If the reference count reaches zero, the routine unlocks the block. Thus, if you lock two different items in an item block, you should unlock each item separately. As noted above, you should always unlock an item before freeing it.

If you change a DB item, you should mark the item's block as dirty by calling DBDirty() . This ensures that the changes will be copied to the disk the next time the file is saved or updated. The routine takes one argument, a pointer to an address in an item block (generally the address of an item). It will dirty the item-block containing that item. As with VM blocks, you must dirty the item before you unlock it, as the memory manager can discard any clean block from memory as soon as it is unlocked.

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Allocating and Freeing Items | Next: 3.5 Resizing DB Items


Resizing DB Items

Database: 3.5 Using Database Routines: Resizing DB Items

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Accessing DB Items | Next: 3.6 Setting and Using the Map Item
DBReAlloc(), DBInsertAt(), DBDeleteAt()

Database items may be resized after allocation. They may be expanded either by having bytes added to the end or by having bytes inserted at a specified offset within the item. Similarly, items may be contracted by having bytes truncated or by having bytes deleted from the middle of the item. When an item is resized, the DB manager automatically dirties the item block (or blocks) affected.

As noted above, when an item is expanded, its item block can be compacted or moved on the item heap (even if the item is locked). Thus, pointers to all items in that item block may be invalidated, even if they are locked. For that reason, you should unlock all items in the group before expanding any of them. If you must leave an item locked, be sure to get its new address with DBDeref() . If you decrease an item's size, the item-block is guaranteed not to move or be compacted. Thus, you can safely contract locked items (or items in the same block as locked items).

To set a new size for an item, call DBReAlloc() . This routine takes four arguments: the file handle, the group-handle, the item-handle, and the new size (in bytes). If the new size is smaller than the old, bytes will be truncated from the end of the item. If the new size is larger than the old, bytes will be added to the end of the item; these bytes will not be zero-initialized.

To insert bytes in the middle of an item, call the routine DBInsertAt() . This routine takes five arguments: the file handle, the group-handle, the item-handle, the offset (within the item) at which to insert the bytes, and the number of bytes to insert. The new bytes will be inserted beginning at that offset; they will be zero-initialized. Thus, if you insert ten bytes beginning at offset 35, the new bytes will be at offsets 35-44; the byte which had been at offset 35 will be moved to offset 45. To insert bytes at the beginning of an item, pass an offset of zero.

To delete bytes from the middle of an item, call DBDeleteAt() . This routine takes five arguments: the file handle, the group-handle, the item-handle, the offset (within the item) of the first byte to delete, and the number of bytes to delete. The routine does not return anything.

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Accessing DB Items | Next: 3.6 Setting and Using the Map Item


Setting and Using the Map Item

Database: 3.6 Using Database Routines: Setting and Using the Map Item

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Resizing DB Items | Next: 3.7 Routines for Ungrouped Items
DBSetMap(), DBGetMap(), DBLockMap()

A VM file can have a map block and a map item . The map can be retrieved with a special-purpose routine, even if you don't know its handle (or handles); thus, the map usually keeps track of the handles for the rest of the file. The map can be retrieved even if the file is closed and re-opened. To set a map block, use the routine VMSetMap() (see the VM chapter). To set a map item , use the routine DBSetMap() . DBSetMap() takes three arguments: the file handle, the item's group-handle, and the item's item-handle. The routine sets the file's map item to the DB item specified. A VM file can have both a map block and a map item; these are set independently.

Once you have set a map item, you can retrieve its handles with the command DBGetMap() . This routine takes one argument, namely the file's handle. It returns a DBGroupAndItem value containing the map item's handles. You can break this value into its constituent handles with DBGroupFromGroupAndItem() and DBItemFromGroupAndItem() (see Routines for Ungrouped Items ). You can also lock the map directly without knowing its handles by calling the routine DBLockMap() . This routine takes one argument, namely the file handle. It locks the map item and returns the map's address. When you are done with the map item, unlock it normally with a call to DBUnlock() .

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Resizing DB Items | Next: 3.7 Routines for Ungrouped Items


Routines for Ungrouped Items

Database: 3.7 Using Database Routines: Routines for Ungrouped Items

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Setting and Using the Map Item | Next: 3.8 Other DB Utilities
DBAllocUngrouped(), DBFreeUngrouped(), DBLockUngrouped(), DBLockGetRefUngrouped(), DBReAllocUngrouped(), DBInsertAtUngrouped(), DBDeleteAtUngrouped(), DBSetMapUngrouped()

Special routines are provided for working with ungrouped items. These routines are very similar to their standard counterparts. The routine DBAllocUngrouped() allocates an ungrouped item. It takes two arguments, the file handle and the size of the item to allocate. The DB manager allocates the item in one of the "ungrouped" groups and returns a DBGroupAndItem value containing the group-handle and item-handle. You can break this value into its components by calling the macros described in Routines for Ungrouped Items , or you can pass this value directly to the other "ungrouped" routines.

The rest of the routines listed above are exactly the same as their counterparts with one exception: whereas their counterparts take, among their arguments, the item's group-handle and item-handle, the ungrouped routines take a DBGroupAndItem value. Each routine's other arguments are unchanged, as is the return value.

These routines are provided as a convenience. If you allocate an ungrouped item, you are perfectly free to break the DBGroupAndItem value into its component handles, and pass those handles to the standard DB routines. Conversely, if you allocate a normal "grouped" item, you are free to combine the two handles into a DBGroupAndItem token and pass that token to the "ungrouped" routines.

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Setting and Using the Map Item | Next: 3.8 Other DB Utilities


Other DB Utilities

Database: 3.8 Using Database Routines: Other DB Utilities

| Up: GEOS SDK TechDocs | Up | Prev: 3.7 Routines for Ungrouped Items
DBCopyDBItem(), DBCopyDBItemUngrouped(), DBGroupFromGroupAndItem(), DBItemFromGroupAndItem(), DBCombineGroupAndItem()

You can duplicate a DB item with the routine DBCopyDBItem() . This routine takes five arguments: the handle of the source file, the source item's group-handle, the source item's item-handle, the handle of the destination file (which may be the same as the source file), and the handle of the destination group. The routine will allocate a new item in the specified file and group. It will then lock both items and copy the data from the source item to the destination. Finally, it will unlock both items and return the item-handle of the duplicate item.

The routine DBCopyDBItemUngrouped() is the same as DBCopyDBItem() , except that it allocates an ungrouped item in the specified file. It is passed the source file handle, the DBGroupAndItem value for the source item, and the destination file handle. It allocates an ungrouped item and returns its DBGroupAndItem value.

Remember, if you are allocating the duplicate in the same group as the source, you should only call this routine when the source item is unlocked (since its item-block may be compacted when the new item is allocated). If the destination is in another block, the source item may be locked or unlocked at your preference. If it is locked when you call DBCopyDBItem() , it will be locked when the routine returns.

All of the VM chain utilities work on DB items as well as VM chains. The routines are described in the VM chapter. To use a VM chain routine, pass the item's DBGroupAndItem value. For example, VMCopyVMChain() will allocate an "ungrouped" duplicate item in the specified file and return its DBGroupAndItem value.

To build a DBGroupAndItem value from the group-handle and item-handle, use the macro DBCombineGroupAndItem() . This macro takes the two handles and returns a DBGroupAndItem value. To extract the component handles from a DBGroupAndItem value, use the macros DBGroupFromGroupAndItem() and DBItemFromGroupAndItem() . These macros are passed a DBGroupAndItem value and return the appropriate component.

| Up: GEOS SDK TechDocs | Up | Prev: 3.7 Routines for Ungrouped Items


Handles

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing

One of the most important data types in GEOS is the handle . There are many different types of handles serving many different purposes. However, they all have one or both of these traits: they provide an unchanging reference to a changing (or moving) thing, and they allow geodes to access and manipulate indirectly certain structures which are maintained as internal to the kernel.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing


Design Philosophy

Handles: 1 Design Philosophy

| Up: GEOS SDK TechDocs | Up | Prev: Handles | Next: 2 The Global Handle Table

GEOS handles play many different roles. The most fundamental of these is memory access. GEOS moves memory in the global heap and swaps memory to the disk. This makes memory available as needed and means that the total memory being used by geodes can be many times larger than the physical memory in the computer; however, it means also that geodes need some way to keep track of their memory. In addition, the system needs to perform memory "housekeeping," doing such things as freeing memory when the geode which owns it exits, making sure that memory is only locked by appropriate geodes, noting which memory sections can be swapped to the hard disk, etc. This means that the kernel needs to maintain a data area for each block of memory. Although the memory itself may move around, the data about the memory should remain in the same place so the kernel can access it.

The solution to both difficulties is the handle table . The table contains entries for each block of memory. These entries keep track of the block, noting such information as where the block is in the global heap, whether it has been swapped to the disk, who its owner is, etc. The handle table does not move in memory. Each entry is referenced by a handle , which is simply the address of the table entry. When a geode needs access to a block, it can call a memory-management routine and pass the handle associated with that block. The memory manager dereferences the handle to find the data about the block and then takes any appropriate steps.

The handle table is useful for many other things. Often a geode will need to perform an action on something which is managed by the kernel. For example, the kernel keeps track of the different disk volumes it has seen. A geode may need to access a disk but not want to keep track of the volume name; or it might want to find out details (such as the size available) of a given disk. Similarly, it might want to find out how large a file which it has opened has grown. All of this information is managed by the kernel, but applications may need to access it or change it. Therefore, each of these things is given an entry in the same handle table as the memory blocks; geodes can reference these things by their handles, the same way they do memory blocks.

The handle mechanism is useful in other places. Sometimes a geode will want to subdivide a block of memory into smaller parcels (or chunks ). GEOS provides a local memory library which implements this functionality. These chunks can be moved around within a block, so applications access them by handles. The handle in this case is an offset to a handle table within that block; dereferencing the handle gives the address of the chunk. Similar techniques are used to divide Virtual Memory files. The principle is the same as with global memory handles: an unchanging handle is used to find the address of a movable thing.

There are certain things all handles have in common. They are all sixteen bits long, allowing them to fit in an 80x86 register. They also all specify addresses. The address is of an entry in a handle table; this entry contains information about the thing whose handle this is. However, handles can be divided into two basic groups:

  • Those in the global handle table.
    The global handle table is kept in main memory below the global heap. The table is never accessed directly by geodes. Instead, geodes pass the handle as an argument to kernel routines that access the handle table.
  • Those in local handle tables.
    Such handles are offsets into handle tables which may be stored in memory blocks or VM files. These handles persist as long as the entity containing the handle table does.
| Up: GEOS SDK TechDocs | Up | Prev: Handles | Next: 2 The Global Handle Table


The Global Handle Table

Handles: 2 The Global Handle Table

| Up: GEOS SDK TechDocs | Up | Prev: 1 Design Philosophy | Next: 3 Local Handles

Many handles refer to things which are managed by the kernel. These things include global memory blocks, disk volumes, files, and many other things. Each of these things has an entry in the global handle table. Only the GEOS kernel may directly access the global handle table. If a geode wants to access one of these things, it must call a system routine and pass the thing's handle.

Global handles may not be saved across sessions of GEOS. The global handle table has to be rebuilt each time GEOS is launched. For example, when GEOS shuts down, all open files are closed. When an application restores from state, it may choose to reopen a file; it will then be given the file's handle, which will almost certainly be different from the file's handle during the previous session.

Sometimes several threads will be using the same global handle, and they will need to synchronize their access to it. They can arrange this by using the HandleP() and HandleV() routines. These routines are most often used to synchronize access to a global memory block; for this reason, they are documented in the Memory Management chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Design Philosophy | Next: 3 Local Handles


Local Handles

Handles: 3 Local Handles

| Up: GEOS SDK TechDocs | Up | Prev: 2 The Global Handle Table

Many handles are not kept in the global handle table. For example, a block of memory may be declared as a "local-memory (LMem) heap." An LMem heap can contain many different chunks of data; the LMem manager moves these chunks around to make room for new ones. Each chunk is referenced via a chunk handle . (For more details about LMem heaps, see the Local Memory chapter.)

Local handle tables are useful when a handle needs to persist across GEOS shutdowns. For example, data in VM files is accessed by its block handle. These handles therefore need to remain the same every time the file is opened.

Some local handle tables are intended to be directly accessible to geodes. For example, LMem Chunk handles are simply offsets into the block containing the LMem heap; the address specified by that offset contains another offset value, which is the offset to the chunk. This scheme is part of the LMem API. Geodes which are written in assembly generally look up chunk handles themselves, instead of calling a system routine to translate the chunk handle into an address. (Goc code generally dereferences a chunk handle with a call to LMemDeref() .) Some other local handle tables are intended to be opaque to geodes. For example, applications treat VM block handles as opaque 16-bit tokens; to dereference a VM handle, a geode must pass it to an appropriate system routine (such as VMLock() ).

Local handles are used by many different libraries; among them are the LMem, VM, Database, and Cell libraries. The details differ with each library; for more information, see the appropriate chapter. An object-descriptor is a combination of two handles: the handle of the global memory block containing the object, and the chunk handle of the object itself. For more information, see the GEOS Programming chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 2 The Global Handle Table


Machine Architecture

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 History of the 80x86
| One-page Version of this Chapter for Easy Printing

The GEOS operating system is state-of-the art technology based on the Intel 80x86 microprocessor. This chapter discusses some of the elements of the 80x86 design and history.

Those unfamiliar with the 8086 architecture would do well not only to read this overview but also to pick up a book about the Intel chip series. There are dozens of good books about the 80x86 chips, and this section provides only a brief review.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 History of the 80x86
| One-page Version of this Chapter for Easy Printing


History of the 80x86

Machine Architecture: 1 History of the 80x86

| Up: GEOS SDK TechDocs | Up | Prev: Machine Architecture | Next: 2 8086 Architecture Overview

The commercial microcomputer essentially began with Intel's introduction of the 8008 chip in 1972. This was an 8-bit machine that eventually led to the 8080 (in 1974), which was the precursor to the processors that now provide the power of today's PCs.

The 8080 was an 8-bit processor that used seven general-purpose registers and an external 8-bit bus. It addressed 64K of memory; such a small memory space necessitated optimization of each instruction in an attempt to keep as many instructions as possible under one byte in length. This optimization resulted in many instructions being forced to work on specific registers; for example, most arithmetic and logical operations used the accumulator as the destination register.

In 1978, the 8086 followed, bringing 16-bit architecture to microcomputers. The designers of this new chip, however, wanted to aid in quick software development, so they gave the 8086 an instruction set and architecture reminiscent of the 8080--this allowed software based on the earlier chip to be ported to the new hardware quickly and cheaply. The 8086 offered significant advances, most notably the ability to access up to one megabyte of memory in 64K segments.

A year later, in 1979, the 8088 was introduced. This chip had all the advanced features of the 16-bit 8086 except one: Rather than using a 16-bit external data bus, the 8088 used the same 8-bit bus used in the 8080. This step backwards made it possible for systems and peripherals designed for use with the 8080 to be used with a faster, more powerful chip. The 8088 was built into IBM's personal computers for just this reason.

Both the 8088 and the 8086 can run the same software; they use the same instruction set. However, the power of the data processing offered by the 8086 eventually won out as systems and software became more complex.

| Up: GEOS SDK TechDocs | Up | Prev: Machine Architecture | Next: 2 8086 Architecture Overview


8086 Architecture Overview

Machine Architecture: 2 8086 Architecture Overview

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 History of the 80x86 | Next: 2.1 Memory

The 8086 uses a 16-bit architecture but can handle 8-bit data as well. It accesses up to one megabyte (1024K) of memory, sixteen times more than the 8-bit 8080 could. It has thirteen registers (each 16 bits) plus a status register containing nine flags.

Memory

Registers

The Prefetch Queue

Inherent Optimizations

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 History of the 80x86 | Next: 2.1 Memory


Memory

Machine Architecture: 2.1 8086 Architecture Overview: Memory

| Up: GEOS SDK TechDocs | Up | Prev: 2 8086 Architecture Overview | Next: 2.2 Registers

The memory of the 8086 begins at hexadecimal address 0x00000 and continues (each increment representing one byte) to hexadecimal address 0xFFFFF. Any two consecutive bytes constitute a word. The address of a word is the same as the address of its low byte.

Both the 8088 and 8086 have instructions that access and manipulate words, and both have instructions that access and manipulate bytes. The 8088, however, always fetches a single byte from memory due to its 8-bit bus. The 8086 always fetches two bytes, or a word; if the instruction only operates on a byte, the excess 8 bits will be ignored. This information may be useful to assembly programmers who wish to optimize the performance of their code, but C programmers can ignore it.

As stated earlier, the 8086 can access up to one megabyte of memory. This translates to 2 20 bytes, or 2 20 addresses. However, because the 8086 is designed to do 16-bit arithmetic, it can not directly access all that memory. Instead, an additional mechanism, known as segmentation , is employed.

A segment is a contiguous set of bytes no larger than 64K (2 16 bytes). It must begin on a paragraph boundary (a paragraph is a contiguous block of 16 bytes, the first of which has an address divisible by 16--that is, its address must have zeros for its four least significant bits). This allows the processor to use just 16 bits (leaving off the four zero bits) to access the first byte of a given segment.

To access bytes further into a segment, instructions use not only the 16-bit segment pointer but also a 16-bit offset. Combined, the segment pointer and offset can specify any byte in memory.

Segments may be any size up to 64K. There is no way to specify the exact size of a segment; the processor does not police memory access. Segments may obviously overlap; it is possible, for example, to have two different segments begin 16 bytes apart. Since segments can be any size up to 64K, the two in this example may well overlap.

The addressing mode described above is used in the 8088 and 8086, a large portion of the GEOS market. However, later processors such as the 80386 also employ a protected mode of memory access, in which each process running is relegated a given portion of memory and memory access is policed by the processor. These later processors also can use the segmented mode of the 8086; however, because GEOS applications should be able to run on an 8086 or 8088 machine, they should adhere to the 8086 rules.

| Up: GEOS SDK TechDocs | Up | Prev: 2 8086 Architecture Overview | Next: 2.2 Registers


Registers

Machine Architecture: 2.2 8086 Architecture Overview: Registers

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Memory | Next: 2.3 The Prefetch Queue

The Intel 80x86 processors have thirteen registers plus one register containing machine status flags. The thirteen registers are separated into four logical groups by their use (they are all 16 bits):

  • Instruction Pointer
    This single register maintains the address of the current instruction being executed. This is not accessed by applications.
  • Segment Registers
    These four registers contain segment pointers.
  • Index and Pointer Registers
    These four registers contain offsets into segments.
  • General registers
    These four registers can contain any general data. They may be operated on as words or bytes.

The Segment and Index registers are used in conjunction to access memory. A program may have four segments pointed to at once: A code segment (CS), a data segment (DS), a stack segment (SS), and an extra segment (ES). These segments have various uses and purposes described in most 8088/8086 books.

The four Index registers are used as offsets into the segments pointed to in the Segment registers. They are the Stack Pointer (SP), the Base Pointer (BP), the Source Index (SI), and the Destination Index (DI). Each of these index registers has special applications with certain instructions.

The General registers are four 16-bit registers that may be used for any purpose. However, due to the early restrictions of the 8080 that carried over into the later processors, some instructions place their results or take their source data from specific registers.

All four general registers may be accessed as a word or as two separate bytes. The four registers are AX (the accumulator), BX (the base register), CX (the counter), and DX (the data register). The high byte of any of these may be accessed by substituting "H" for "X," and the low byte may be accessed by substituting "L" for "X." (The "H" stands for "high" and the "L" for "low.")

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Memory | Next: 2.3 The Prefetch Queue


The Prefetch Queue

Machine Architecture: 2.3 8086 Architecture Overview: The Prefetch Queue

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Registers | Next: 2.4 Inherent Optimizations

Programmers who code in assembly language should be familiar with how the 8086 fetches data and instructions from memory. Good programmers can take advantage of the more efficient instructions to cut down on processor time for given operations.

The 8086 takes four clock cycles to fetch a single word from memory. To speed up instruction processing, the 8086 has a prefetch queue , a buffer of six bytes into which pending instructions are put. The 8086 is also broken into two separate processing units: The Execution Unit executes instructions while the Bus Interface Unit (BIU) fetches pending instructions and stuffs them into the prefetch queue.

The main goal of this separation is to make as much use of the bus as possible, even when an instruction that does not access memory is being executed. For example, if an instruction takes eight cycles to execute and does not access memory, the BIU could meanwhile fill four instructions into the prefetch queue. Therefore, while slow instructions are still slow, the instructions after them appear to be quicker.

However, jump and branch instructions negate this prefetch effect. When a branch or jump is executed, the prefetch queue is flushed and must again be filled.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Registers | Next: 2.4 Inherent Optimizations


Inherent Optimizations

Machine Architecture: 2.4 8086 Architecture Overview: Inherent Optimizations

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 The Prefetch Queue

The 8088 and 8086 instruction set was designed with many instructions manifested in two different forms: one that allowed a wide range of possible arguments, and another that worked with one of the arguments prespecified. Since the second form did not have to load both arguments from memory, those instructions were shorter than their counterparts.

For example, the instruction and has these two forms: "And with anything," which assembles into three bytes of machine code, and "And with AX or AL," which assembles into only two bytes of machine code. Thus, the instruction and al, ffh is more efficient than and bl, ffh .

Another optimization occurs in the set of string instructions. A string is simply a set of consecutive bytes or words in memory. To repeat an operation on each element of the string could normally take a long time due to branching and checking for final conditions (branching clears out the prefetch queue). However, the string operations act as single, nonbranching instructions, so the prefetch queue is not affected by them. This speeds up string operations considerably.

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 The Prefetch Queue


Memory Management

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing

Managing memory in a multitasking system is complex because many different entities--the kernel, libraries, and applications--are all trying to use the same memory space. To ensure that each gets all the memory it needs, without having to worry about what other memory users are doing, GEOS provides a memory manager.

1 Design Philosophy
2 The Structure of Memory
2.1 Expanded/Extended Memory
2.2 Main Memory
3 Using Global Memory
3.1 Memory Etiquette
3.2 Requesting Memory
3.3 Freeing Memory
3.4 Accessing Data in a Block
3.5 Accessing Data: An Example
3.6 Data-Access Synchronization
3.7 Retrieving Block Information
3.8 The Reference Count
4 malloc()

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing


Design Philosophy

Memory Management: 1 Design Philosophy

| Up: GEOS SDK TechDocs | Up | Prev: Memory Management | Next: 2 The Structure of Memory

Some systems make each application maintain and manage its own memory. In a multitasking system, this is not only difficult and time-consuming but also potentially dangerous, as applications can easily start trashing memory used by other processes. GEOS protects and relieves applications by providing comprehensive memory management.

The GEOS memory management system is designed to meet rigorous demands. Some of the requirements are

  • Independence of memory location
    Ideally, an application should not have to keep track of the address of each of its data items; rather, the memory management system should allow the application to reference its memory virtually.
  • Hardware independence
    Applications are much easier to write and maintain if they can ignore hardware specifics. An application should be able to specify its memory requirements in generic terms ("I need a package this big which behaves in this way") and let the memory manager worry about where it comes from.
  • Efficient use of memory
    A good operating system should be able to rearrange memory in order to gather as much space as possible. It should also be able to discard certain data or copy it to mass-storage devices (like a hard disk) if more memory is needed.
  • Management of Shared Data
    Applications should be able to share common data or code. In a multitasking system, proper synchronization of shared resources is essential to maintain data integrity.

The GEOS memory manager meets all of these needs. Applications often take advantage of the memory manager without trying; for example, the memory manager swaps methods into memory when messages need to be handled. Applications can also request memory at run-time, either by requesting raw memory from the memory manager, or by creating Virtual Memory files through the VM library (see the VM chapter).

| Up: GEOS SDK TechDocs | Up | Prev: Memory Management | Next: 2 The Structure of Memory


The Structure of Memory

Memory Management: 2 The Structure of Memory

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Design Philosophy | Next: 3 Using Global Memory

The GEOS memory manager currently uses real mode memory addressing, and is designed to run on PCs with as little as 512K RAM. Typically, between 30K and 160K of that is occupied by DOS. The remaining RAM is used for the global handle table and the global heap (described below). The GEOS kernel is kept in the global heap.

Because of the constraints of real-mode memory addressing, GEOS uses memory in segments of at most 64K. Each segment may be subdivided and parceled out to fill memory requests from applications. Sometimes an application will need a seemingly contiguous section of memory which is larger than 64K; in these situations, it should use the HugeArray routines (see the VM chapter).

Expanded/Extended Memory

Main Memory

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Design Philosophy | Next: 3 Using Global Memory


Expanded/Extended Memory

Memory Management: 2.1 The Structure of Memory: Expanded/Extended Memory

| Up: GEOS SDK TechDocs | Up | Prev: 2 The Structure of Memory | Next: 2.2 Main Memory

While GEOS is designed to run on a standard system, it makes efficient use of expanded and extended memory, if it is available. GEOS treats extended memory as a fast and convenient RAM disk, swapping blocks into the extended memory rather than out to a slower disk drive. This incurs none of the usual overhead of a RAM disk such as a directory because it is treated as normal memory. Expanded memory is used in a similar manner.

| Up: GEOS SDK TechDocs | Up | Prev: 2 The Structure of Memory | Next: 2.2 Main Memory


Main Memory

Memory Management: 2.2 The Structure of Memory: Main Memory

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Expanded/Extended Memory | Next: 3 Using Global Memory

Memory available to applications is organized in a data structure called the Global Heap. The size of the global heap may vary from machine to machine and can change from session to session, but during a single execution of GEOS, the heap size remains constant. Usually, the global heap occupies approximately 450K bytes of a 640K system.

When an application requests memory, it is allocated a "block" on the heap. Blocks may be of almost any size but may not be larger than 64K. (However, the heap is most efficient when blocks are 2K-6K in size; see Memory Etiquette .) Every block is allocated a unique handle (described below) from the Handle Table; by calling a memory manager routine, the application can translate a handle to a conventional pointer.

When GEOS shuts down, all the blocks on the global heap are freed, even if they are locked or fixed. If an application will need to store the data over a shutdown, it should make the block part of a VM file, which it can then reopen when it restores from state. The GEOS kernel attaches object blocks to system VM files and takes care of storing them to state and restoring them when GEOS restarts.

Blocks and Handles

GEOS segments memory into blocks. A block is simply a number of contiguous bytes on the global heap in which code or data may be stored. Any given block may be of any size but must be less than 64K, due to the segmented addressing scheme used by the 80x86 processors. A block's size is determined when it is allocated--the process that requests the memory must specify the desired size.

To facilitate efficient memory usage and to present all applications with enough memory to function, blocks are dynamic in nature. This means that unless a block is fixed or has been locked (see Accessing Data in a Block ), there is no telling its precise address on the heap, or indeed whether it is on the heap at all. For this reason, applications are not given the address of an allocated block; rather, they are given the block's handle.

Memory handles are indexes into the global handle table. Geodes may not access the table directly. When they want to access the memory indicated by a handle, they pass the handle to the memory manager and are returned the segment address of the block's current location on the heap.

In addition to storing the actual address of the block, the handle table entry records the block's attributes, such as whether the block is discardable, swapable, or fixed. The memory manager uses all these attributes to manipulate the block.

Enabling Block Access

Dynamic memory, while providing significant benefits, poses one major problem: What happens if a block is accessed while being moved, swapped, or discarded? GEOS responds to this problem with the implementation of a system for locking and unlocking blocks. When a block is locked on the heap, the Memory Manager may not move, swap, or discard it until it is unlocked. This locking mechanism allows applications to gain control over their memory during periods of access and relinquish it when it is not in active use. Applications, however, should not leave blocks locked for extended periods as this may interfere with heap compaction.

When a process wants to use a block, it instructs the memory manager to lock the block by calling MemLock() (see MemLock() locks a block on the heap. It is passed the handle of the block; it returns a pointer to the start of the block on the heap. If the block has been discarded, MemLock() returns a null pointer. ). The application passes the handle of the block, and the memory manager locks the block and returns a pointer to the block's area in the global heap. While a block is unlocked, the memory manager can, depending on the block's category, move the block on the heap, swap it to disk or extended/expanded memory, or discard it altogether.

Types of Blocks

When a geode requests memory, it may specify how that memory is to be treated by the memory manager. The memory request includes a set of HeapFlags (see HF_FIXED The block will not move from its place in the global heap until it is freed. If this flag is off, the memory manager may move the block when it is unlocked. If it is on, the block may not be locked. This flag cannot be changed after the ) which specifies how and when the block can be moved. Broadly speaking, memory blocks can be divided into four categories:

  • Fixed
    A fixed block has the flag HF_FIXED set to one, and HF_DISCARDABLE and HF_SWAPABLE set to zero. A fixed block is allocated on the global heap and stays exactly where it was created until it is freed. An application can therefore reference data within a fixed block with normal pointers; the application does not need to use the memory manager to translate a handle into a pointer. Accessing fixed blocks is very quick since the memory never needs to be read from disk and the handle does not need to be dereferenced. However, since the memory manager cannot relocate fixed blocks, they tend to degrade the memory manger's performance; therefore, applications should generally use small amounts of fixed memory. Note that if a fixed block is resized, it may be moved on the global heap; therefore, after resizing a fixed block, a geode should reset its pointers within the block by dereferencing the block handle.
  • Moveable
    A moveable block has the flag HF_FIXED set to zero. When a moveable block is not in use (i.e. unlocked), the memory manager may move it within the global heap. This lets the memory manager keep the global heap compacted (see Maximizing Free Space in Memory ). Accessing moveable blocks is slightly slower than accessing fixed blocks since the application needs to call the memory manager to lock the block and translate the memory handle to a pointer. In addition to being moveable, a block may also be swapable and/or discardable (as described below).
  • Swapable
    A swapable block has the flag HF_FIXED set to zero and HF_SWAPABLE set to one. If a block is marked "swapable," the memory manager has the option of swapping it out of main memory (either to the hard disk, or to extended or expanded memory) when it is unlocked. This keeps space on the global heap free for other requests. If an application tries to lock a block which has been swapped out of main memory, the memory manager will copy the entire block back to the global heap, then lock it normally. By using swapable memory, geodes can potentially use much more memory than the roughly 450K available in the global heap. However, accessing an unlocked swapable block can potentially be much slower than accessing a non-swapable block, as the block may have to be copied in from the disk.
  • Discardable
    A discardable block has HF_FIXED set to zero and HF_DISCARDABLE set to one. Sometimes it doesn't make sense to copy a block to the disk. For example, many memory blocks contain code or unchanging data which is read in from the disk. It would make no sense to swap these blocks back to disk, since the data is already there. Instead, one can mark a block "discardable." This indicates that when the block is unlocked, the memory manager can, at will, discard the block. If a process attempts to lock a block which has been discarded, the memory manager will return an error. The application can then "re-allocate" memory for that block (see MemReAlloc() on To change the size of a block, call the routine MemReAlloc(). This routine is also used to allocate memory for a block that has been discarded. The routine is passed the memory handle, the new size, and the HeapAllocFlags; it returns the block's ) and copy the data back from the disk. (The system takes care of reloading discarded code resources as necessary.) A block can be both discardable and swapable (indeed, discardable blocks are usually swapable). If a block has both HF_DISCARDABLE and HF_SWAPABLE set, the memory manager can either swap the block to extended/expanded memory or discard it; it will not swap the block to the disk.

Fixed blocks must be declared as such when they are allocated, and they remain so until they are freed. However, non-fixed blocks may become or cease to be discardable or swapable after they are created. To enable or disable these characteristics, call the routine MemModifyFlags() (see MemModifyFlags() is used to change a block's HeapFlags record. It takes three arguments: The handle of the block, the HeapFlags to turn on, and the HeapFlags to clear. It returns nothing. Not all HeapFlags can be changed after a block is created ).

Maximizing Free Space in Memory

Moveable, swapable, and discardable blocks are allocated from the top of the heap using a first-fit method. Fixed blocks are allocated from the bottom of the heap. If there is not enough contiguous free memory to satisfy an allocation request, the memory manager attempts to shuffle moveable blocks in order to place all free memory together in one large mass.

This shuffling is called heap compaction. If the free space resulting from compaction still is not enough, blocks are discarded or swapped to liberate more free space, and the heap is again compacted. Because of the multitasking nature of GEOS, compaction occurs in the background and is invisible to both the user and applications. The memory manager will also periodically compact memory during periods of low activity; this helps insure that there will be memory instantly available for a sudden large demand (e.g. when an application is launched).

The compaction is not arbitrary. The kernel decides which blocks to swap or discard based on recent usage patterns. This means, for example, that if you haven't used routines in a code resource for a while, that resource is more likely to be discarded than the resources you've accessed recently. (For this reason, geodes normally isolate their initialization code in one resource, which can be discarded later.)

A block left locked for extended periods could interfere with heap compaction. Suppose, for example, that the moveable locked block in the middle of the heap were left locked during an application's entire execution. Essentially, this would cause the heap to be fractured into two subheaps, making compaction more difficult and possibly slowing the system down.

All compaction, swapping, and discarding are functions of the Memory Manager. Applications need only indicate how much space is needed and when space can be freed. Applications may also resize blocks at will; if necessary, the memory manager will compact the heap to accommodate the request.

Block Attributes

HeapAllocFlags, HeapFlags

Blocks are allocated with certain flags that help the Memory Manager manipulate memory efficiently. These flags can be found in the GEOS file heap.h , which should be included in all applications that plan to allocate memory dynamically with the memory manager routines.

The flags fall into two categories: those used when the block is allocated (stored in a record called HeapAllocFlags ) and those used to describe the block as it is manipulated (stored in a record called HeapFlags ).

The HeapAllocFlags record is used to determine what qualities the memory manager should give the block when it is first allocated. Some of these flags are also relevant when memory is being reallocated. These qualities include:

HAF_ZERO_INIT
Upon allocation, initialize data in block to zeros.
HAF_LOCK
Upon allocation, the block should be locked on the global heap. Use MemDeref() ( MemDeref() is passed the handle of a block on the global heap; it returns the block's address on the global heap. As noted above, this routine is useful when you allocate a fixed or locked block. If the block has been discarded, it returns a nul ) to get a pointer to the block.
HAF_NO_ERR
Do not return error codes; system error if block cannot be allocated. Use of this flag is strongly discouraged.
HAF_OBJECT_RESOURCE
This block is an object-block. This is set by the system only .
HAF_UI
If both HAF_OBJECT_RESOURCE and HAF_UI are set, the memory manager will set the block to allow the application's UI thread to manipulate objects in the block. This is set by the system only .
HAF_READ_ONLY
This block's data will not be modified.
HAF_CODE
This block contains executable code.
HAF_CONFORMING
If this block contains code, the code may be run by a less privileged entity. If the block contains data, the data may be accessed or altered by a less privileged entity.

Once a block is allocated, it has certain properties that govern how the Memory Manager manipulates it. These properties are determined by the HeapFlags . The HeapFlags also contain data about whether the block has been swapped or discarded. These flags are stored in the block's handle-table entry, so they can be retrieved without locking the block. To retrieve the flags, call the routine MemGetInfo() with the flag MGIT_FLAGS_AND_LOCK_COUNT. (See MemGetInfo() on MemGetInfo() is a general-purpose block information routine. It is passed two arguments: the handle of the block, and a member of the MemGetInfoType enumerated type. The return value is always word-length; however, its significance depends on th .) Some of the flags can be changed after the block has been allocated; for details, see MemModifyFlags() on MemModifyFlags() is used to change a block's HeapFlags record. It takes three arguments: The handle of the block, the HeapFlags to turn on, and the HeapFlags to clear. It returns nothing. Not all HeapFlags can be changed after a block is created . The flags include

HF_FIXED
The block will not move from its place in the global heap until it is freed. If this flag is off , the memory manager may move the block when it is unlocked. If it is on , the block may not be locked. This flag cannot be changed after the block has been allocated.
HF_SHARABLE
The block may be locked by geodes other than the owner. This flag can be changed with MemModifyFlags() .
HF_DISCARDABLE
If the block is unlocked and space is needed, the memory manager may discard it. This flag can be changed with MemModifyFlags() .
HF_SWAPABLE
If the block is unlocked and space is needed, it may be swapped to expanded or extended memory or to the hard disk. This flag can be changed with MemModifyFlags() .
HF_LMEM
The block is a local-memory block, managed by the LMem module (see the Local Memory chapter). The flag is set automatically by LMemInitHeap() . It can be changed with MemModifyFlags() ; however, an application should not change this flag.
HF_DISCARDED
The block has been discarded by the memory manager. Only the system can set or clear this flag.
HF_SWAPPED
The block has been swapped to extended or expanded memory or to the hard disk. Only the system can set or clear this flag.
|  Up: GEOS SDK TechDocs | Up | Prev: 2.1 Expanded/Extended Memory | Next: 3 Using Global Memory


Using Global Memory

Memory Management: 3 Using Global Memory

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 The Structure of Memory | Next: 4 malloc()

When an application needs more raw memory at run-time, it can use the memory manager kernel routines to allocate, use, and free new blocks. An application can also create a Virtual Memory file to get memory with more advanced functionality, such as the ability to save to a disk file (see the VM chapter). However, one must understand the raw memory routines in order to use VM files.

If you will be storing small pieces of information in a block, you should create a local memory heap (a special kind of memory block). LMem heaps are also useful for storing arrays of information or for storing objects. For more information, see the Local Memory chapter.

Memory Etiquette

Requesting Memory

Freeing Memory

Accessing Data in a Block

Accessing Data: An Example

Data-Access Synchronization

Retrieving Block Information

The Reference Count

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 The Structure of Memory | Next: 4 malloc()


Memory Etiquette

Memory Management: 3.1 Using Global Memory: Memory Etiquette

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Global Memory | Next: 3.2 Requesting Memory

The GEOS memory manager tries to fulfill every memory request. It does not enforce many rules on how the geodes should use memory. This gives each geode maximum flexibility, but it also means that the geodes have to take responsibility for their behavior; they must see to it that their memory usage does not degrade the system's performance.

First and foremost, applications should never use memory which has not been assigned to them by the memory manager. Unlike some operating systems (e.g. UNIX), GEOS does not enforce divisions between memory spaces. This allows GEOS to run on PCs which do not provide protected-mode access. It means, however, that the system will not stop an application from loading a bad address into a pointer, accessing that address, and thus writing to another memory space. An application should access only that memory which the memory manager assigns to it directly (via calls to memory routines or data-space assigned at startup) or indirectly (through other libraries, such as the VM library). This also means that you should not save pointers to movable memory after unlocking the memory block. If you are not careful, you can accidently access memory which no longer contains your data, with sometimes-fatal results.

Another rule is to minimize the amount of memory you keep locked at a time. When memory is locked, the memory manager cannot move it on the global heap or swap it out of memory. This can result in the heap becoming fragmented, which in turn makes it more likely that memory requests will fail. Keep memory unlocked as much as possible. It's worthwhile to unlock memory even if you'll be locking it again very soon; this gives the memory manager a chance to compact the heap. If you have some data which you will need locked for long periods of time, it is best to put it in a fixed block, since fixed blocks are allocated from the bottom of the global heap and thus cause less fragmentation. Of course, you should free the fixed block as soon as possible.

Try to have as few blocks locked at a time as possible. Every locked block increases the danger that the heap will be unable to comply with a memory request. Try to organize your use of memory to minimize the number of blocks you will have to keep locked at a time.

In the same vein, try to keep the amount of memory you have on the global heap to a minimum. You should declare all of your non-fixed memory as swappable and/or discardable. Remember, you can use MemModifyFlags() to change these characteristics; during a timing-critical period, you could have a block set as non-swappable, and then make it swappable again as soon as timing becomes less important. If you use Virtual Memory files, the VM Manager does much of this for you.

Try to keep your memory blocks small. Although memory blocks can, in principle, grow to 64K, the memory manager is best suited for dealing with blocks in the 2K-6K range. If you need a contiguous data space which grows beyond 8K in size, you should use the Huge Array mechanism (see the VM chapter), which automatically (and almost transparently) divides a large data space across several smaller memory blocks. If you use many small data items, you should use the Database library, which automatically distributes data items among different memory blocks, keeping each block near the optimum size. (See the DB chapter).

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Global Memory | Next: 3.2 Requesting Memory


Requesting Memory

Memory Management: 3.2 Using Global Memory: Requesting Memory

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Memory Etiquette | Next: 3.3 Freeing Memory
MemAlloc(), MemAllocSetOwner(), MemReAlloc()

When you need a block of raw memory, you must use one of the kernel's memory allocation routines. You also must use kernel memory routines to change the size of a block or to reallocate space for a discarded block.

MemAlloc() creates a block and assigns a handle to it. The routine must be passed the size (in bytes) of the block to be allocated, along with the HeapAllocFlags and HeapFlags for that block. MemAlloc() will set the block's owner as the owner of the thread that called it. It will return the handle of the newly-allocated block.

MemAllocSetOwner() is the same as MemAlloc() , except that the caller explicitly sets the owner of the new block by passing the handle of the owning geode. Like MemAlloc() , it returns the handle of the new block. This is commonly used by drivers and shared libraries, which allocate memory owned by the geode which calls them. When the block's owner exits, the block will be freed, even if the block's creator is still running.

If you request a fixed block or pass the flag HAF_LOCK, the block will be allocated locked on the heap. However, the routine will still return just the memory handle. To translate this handle to a pointer, call the routine MemDeref() . MemDeref() is passed a block's handle and returns a pointer to the block (or a null pointer if the block has been discarded).

To change the size of a block, call the routine MemReAlloc() . This routine is also used to allocate memory for a block that has been discarded. The routine is passed the memory handle, the new size, and the HeapAllocFlags ; it returns the block's memory handle. You can reallocate a fixed or locked block; however, the block may be moved on the global heap to satisfy the request. (This is the only way a fixed block can move.) As with MemAlloc() , you can request that the memory manager lock the block after reallocating it; you will then have to call MemDeref() to get the address of the block. Note that if the new size is smaller than the original size, the routine is guaranteed to succeed, and the block will not move from its current position. Reallocating a block to zero bytes discards the block but preserves its handle; the block can then be reallocated later.

If the memory manager is unable to accommodate a request, it will return an error condition. The requestor can prevent error messages by passing the flag HAF_NO_ERR; this will result in a system error if the memory cannot be allocated. Passing HAF_NO_ERR is therefore strongly discouraged.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Memory Etiquette | Next: 3.3 Freeing Memory


Freeing Memory

Memory Management: 3.3 Using Global Memory: Freeing Memory

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Requesting Memory | Next: 3.4 Accessing Data in a Block
MemFree()

When you are done using a block, you should free it. Every block takes up space in the global handle table; if blocks are not freed, the handle table may fill up, causing a system error. Furthermore, non-swapable, non-discardable blocks take up space in the global heap until they are freed.

To free a block, call MemFree() and pass the handle of the block to be freed. The block will be freed even if it is locked. Therefore, if the block can be used by other threads, you should make sure no other thread has locked the block before you free it.

You can also set a reference count for a block. When a block's reference count drops to zero, the memory manager will automatically free it. This is useful if several threads will be accessing the same block. For more information, see The Reference Count .

When a geode exits, all blocks owned by it are automatically freed.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Requesting Memory | Next: 3.4 Accessing Data in a Block


Accessing Data in a Block

Memory Management: 3.4 Using Global Memory: Accessing Data in a Block

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Freeing Memory | Next: 3.5 Accessing Data: An Example
MemLock(), MemUnlock()

Because the memory manager is constantly reorganizing the global heap, applications must have a way of making sure a block stays put when they want to use it. Applications must also have a way of recalling swapped blocks when they are needed.

These requirements are met by the memory manager's locking scheme. Whenever you need to access data in a non-fixed block, you must first lock it. This will cause the memory manager to copy the block back into the global heap if it had been swapped; the memory manager will not move, swap, or discard a block while the block is locked.

Any block may be locked several times. Each lock increments the block's lock count (to a maximum of 255 locks per block), and each unlock decrements it. The memory manager can only move the block when the lock count is zero.

One warning about locking blocks: Do not try to lock a block which was allocated as fixed. Attempting to do so will result in a system error. If you need to translate a fixed-block handle to a pointer, call MemDeref() .

MemLock() locks a block on the heap. It is passed the handle of the block; it returns a pointer to the start of the block on the heap. If the block has been discarded, MemLock() returns a null pointer.

Immediately after you are done using a block, you should unlock it by calling MemUnlock() . It is better to lock and unlock the same block several times than to retain control of it for an extended period, as locked blocks degrade the performance of the heap compaction mechanism. To unlock a block, call MemUnlock() , passing the handle of the block to be unlocked. MemUnlock() decrements the lock count.

A block may be locked by any of the threads run by its creator; if the block is sharable, it may be run by any other thread as well. There is nothing in the MemLock() routine to prevent different threads from locking a block at the same time, causing potential synchronization problems. For this reason, if threads will be sharing a block, they should use the synchronization routines (see Data-Access Synchronization ).

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Freeing Memory | Next: 3.5 Accessing Data: An Example


Accessing Data: An Example

Memory Management: 3.5 Using Global Memory: Accessing Data: An Example

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Accessing Data in a Block | Next: 3.6 Data-Access Synchronization

Allocating and Using a Block shows how to allocate a block, lock it, access a word of data in the block, and unlock the block. This example shows the basic principles of using blocks.

Code Display 8-1 Allocating and Using a Block

/*
 * Variable Declarations
 */
MemHandle 	myBlockHandle;
char 	charArray[50], *blockBaseAddress;
/* First, we allocate a block of the desired size. Since we'll use the block right
 * away, we allocate the block already locked.
 */
myBlockhandle = MemAlloc(					/* MemAlloc returns the block handle */
            2048,		/* Allocate 2K of memory */
            HF_SWAPABLE,		/* HeapFlags: Make block swapable */
            HAF_ZERO_INIT|HAF_LOCK);			/* HeapAllocFlags: Initialize
                         * the memory to zero & lock it */

/* The block is already locked on the global heap. However, we do not have the
 * block's address; we just have its handle. Therefore, we need to call a routine
 * to dereference the handle. */
blockBaseAddress = (char *) MemDeref(myBlockHandle);						/* Returns a ptr to base of
                         * block */
/* Enter some data in the block */
strcpy(blockBaseAddress,
    "I can resist anything except temptation.\n   --Wilde"
/* We're done with the block for the moment, so we unlock it. */
MemUnlock(myBlockHandle); /* blockBaseAddress is now meaningless */
/* Here we do some other stuff . . . */
/* Now we want to use the block again. First we have to lock it. */
blockBaseAddress = (byte *) MemLock(myBlockHandle);						/* Returns a ptr to locked
                         * block */
/* Read a string from the block: */
strcpy(charArray, blockBaseAddress);
/* We're done with the block, so we free it. Note that we can free the block
 * without unlocking it first.
 */
MemFree(myBlockhandle); /* myBlockHandle is now meaningless */
| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Accessing Data in a Block | Next: 3.6 Data-Access Synchronization


Data-Access Synchronization

Memory Management: 3.6 Using Global Memory: Data-Access Synchronization

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Accessing Data: An Example | Next: 3.7 Retrieving Block Information
MemThreadGrab(), MemThreadGrabNB(), MemThreadRelease(), MemLockShared(), MemUnlockShared(), MemLockExcl(), MemDowngradeExclLock(), MemUpgradeSharedLock(), MemUnlockExcl(), HandleP(), HandleV(), MemPLock(), MemUnlockV()

Blocks can have the property of being sharable--that is, the same block may be locked by threads owned by several different geodes. However, this can cause data synchronization problems; one application can be changing data while another application is trying to use it. To prevent this, GEOS provides semaphore routines. Only one thread can have the block's semaphore at a time. When an application wants to use a shared block, it should call a routine to set the semaphore. Once the routine returns, the application can use the block; when it is done, it should release the block's semaphore so other applications can use the block.

Note that use of semaphores is entirely voluntary by each application. Even if thread A has the semaphore on a block, thread B can call MemLock() on the block and start changing it. However, all threads using shared blocks ought to use the semaphore routines to prevent confusion.

There are several different sets of routines which can be used to control a block's semaphore. The different sets of routines make different trade-offs between faster operation and increased flexibility. Any one block should be accessed with only one set of routines; different threads should all be using the same routines to access a given block, and a thread should not switch from one set of routines to another for a particular block. If this rule isn't followed, results are undefined. All of these routines access the HM _otherInfo word of the handle table entry; if the block will be locked by any of these routines, you must not alter that word. None of these routines is used to access object blocks; instead, special object-block locking routines are provided.

Most geodes should use MemThreadGrab() , MemThreadGrabNB() , and MemThreadRelease() to access sharable blocks. These routines provide the maximum protection against deadlock in exchange for a slightly slower execution.

MemThreadGrab() gives the thread the semaphore for the block in question and locks the block. It is passed the handle of the block and returns the block's address on the global heap. If no thread has the block's semaphore, it gives the semaphore to the calling thread. If the calling thread already has the semaphore, a "semaphore count" is incremented; the thread will not release the semaphore until it has been released as many times as it has been grabbed. (For example, two different objects run by the same thread could each grab the semaphore; the semaphore would not be released until each object called MemThreadRelease() .) If another thread has the semaphore, MemThreadGrab() blocks until it can get the semaphore; it then increments the semaphore, locks the block, and returns the address.

MemThreadGrabNB() is the same as MemThreadGrab() , except that it never blocks. If you call MemThreadGrabNB() while another thread has the semaphore, the routine will immediately return an error. MemThreadGrabNB() takes the handle of the block; it increments the semaphore, locks the block, and returns the block's address on the heap.

MemThreadRelease() releases a block grabbed by either MemThreadGrab() or MemThreadGrabNB() . It is passed the block's handle. It unlocks the block and decrements the block's semaphore.

One common situation is that several threads may need to read a block but only once in a while will an application need to write to the block. In this case, there is no synchronization problem in having several readers at once; however, if any thread is writing, no other thread should be reading or writing. For this situation, GEOS provides this set of MemLock routines: MemLockShared() , MemUnlockShared() , MemLockExcl() , and MemUnlockExcl() .

These routines, like the others, maintain a queue of threads which have requested thread access. The difference is that any number of readers can have access at once. When a thread wants read access, it calls MemLockShared() . If the queue is empty and the block is unlocked or locked for reading, the routine returns and the thread is given shared access; otherwise, the thread is blocked, and the request goes on the queue. When a routine is finished reading the block, it should call MemUnlockShared() .

When a routine needs to write to a block, it should call MemLockExcl() . If nobody has locked the block (and thus the queue is empty), the thread will immediately be given exclusive access; otherwise, the thread will block, and the request will go on the queue. When the thread no longer needs write access, it should call MemUnlockExcl() .

When all threads with access to a block have released their locks, the queued thread with the highest priority will be awakened and given the lock on the block. If that thread had requested shared access, all other threads on the queue that had requested shared access will also be awakened and given locks.

A thread can change its lock from shared to exclusive or vice versa. If a thread has an exclusive lock on a block, it can change the lock to shared by calling MemDowngradeExclLock() . This routine takes one argument, namely the block's global handle. It changes the lock to "shared" and wakes up all sleeping threads which are waiting for shared access. For convenience, MemDowngradeExclLock() returns the address of the block; however, the block is guaranteed not to move.

If a thread has shared access and wants exclusive access, it can call MemUpgradeSharedLock() . If the thread has the only lock on the block, its lock will be changed to "exclusive" (even if there are writers on the queue). If any other threads have the block locked, the routine will release the thread's lock and put the thread on the queue. When the thread comes to the head of the queue, the routine will wake the thread and give it an exclusive lock. The routine returns the block's address on the global heap. Note that the block may be altered or moved during this call if the call blocked.

Once a thread has been given a shared lock, there is nothing to prevent it from altering (or even freeing) the block. The routines rely on good citizenship by the threads using them.Also, if a thread isn't careful, there is a great danger of deadlock. If (for example) a thread requests exclusive access to a block when it already has access, the thread will deadlock: it will block until the threads with access all release the block, but it can't release its own lock because it is blocked. If you may need to have multiple locks on a block, use the MemThread routines, which check for these situations.

There are other sets of routines which can be used to access a block's semaphore. As noted, a block should be accessed via just one set of routines. These routines provide less protection against deadlock than the MemThread routines do; however, they have a slightly faster response time.

A more primitive group of routines is HandleP() , HandleV() , MemPLock() , and MemUnlockV() . These routines function much like the MemThread routines. HandleP() grabs the block's semaphore and returns; it does not lock the block. This makes it very useful for working with fixed blocks (which cannot be locked). HandleV() releases the block's semaphore and returns; it does not unlock the block. Note, however, that HandleP() will block if any thread controls the semaphore, even the thread that called HandleP() . If a thread calls HandleP() while it controls the semaphore, it will block until the semaphore is released, but it can't release the semaphore because it has blocked. Thus, the thread will deadlock, and no other thread will be able to get the semaphore. Therefore, a thread should use HandleP() only if it is very confident that it will never try to double-set the semaphore.

Usually, when a thread grabs a block's semaphore, it needs to have the block locked on the heap. For this reason, GEOS provides the routines MemPLock() and MemUnlockV() . MemPLock() simply calls HandleP() and then calls MemLock() . MemUnlockV() , correspondingly, calls MemUnlock() and then calls HandleV() . These routines are completely compatible with HandleP() and HandleV() ; for example, a thread could grab and lock a block with MemPLock() , then unlock it with MemUnlock() and release it with HandleV() .

HandleP() and HandleV() are general-purpose handle routines. They can be called on any type of global handle. For example, if two threads need to synchronize their access to a file, they can lock and unlock the file handle with HandleP() and HandleV() . However, they are most commonly used with memory blocks.

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Accessing Data: An Example | Next: 3.7 Retrieving Block Information


Retrieving Block Information

Memory Management: 3.7 Using Global Memory: Retrieving Block Information

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Data-Access Synchronization | Next: 3.8 The Reference Count
MemDeref(), MemPtrToHandle(), MemGetInfo(), MemOwner(), MemModifyFlags(), HandleModifyOwner(), MemModifyOtherInfo()

GEOS offers several routines to retrieve and change information about a block. Each of these routines has a full entry in the Routine Reference Book.

MemDeref() is passed the handle of a block on the global heap; it returns the block's address on the global heap. As noted above, this routine is useful when you allocate a fixed or locked block. If the block has been discarded, it returns a null pointer.

MemPtrToHandle() , passed a pointer to a locked block, finds the handle of that memory block.

MemGetInfo() is a general-purpose block information routine. It is passed two arguments: the handle of the block, and a member of the MemGetInfoType enumerated type. The return value is always word-length; however, its significance depends on the MemGetInfoType value passed:

MGIT_SIZE
Returns the size of the memory block (in bytes).
MGIT_FLAGS_AND_LOCK_COUNT
Upper byte is the number of locks on the block; lower eight bits are the block's HeapFlags record (see HF_FIXED The block will not move from its place in the global heap until it is freed. If this flag is off, the memory manager may move the block when it is unlocked. If it is on, the block may not be locked. This flag cannot be changed after the ).
MGIT_OWNER_OR_VM_FILE_HANDLE
If the block is attached to a GEOS Virtual Memory file, MemGetInfo() returns the VM file handle. Otherwise, it returns the GeodeHandle of the owning thread.
The MemOwner() routine also returns the owning geode of the passed block; however, if the block belongs to a VM file, the file's owner will be returned instead of the file handle.
MGIT_ADDRESS
Returns the block's segment address, if it is on the global heap; otherwise, it returns zero. If the block is resized or is not locked, the address may change without warning. Note that the segment address is returned as a word, not as a pointer; this is of limited utility in C.
MGIT_OTHER_INFO
Returns the HM_otherInfo word from the block's handle table entry. The usage of this word varies depending on the block's nature; for example, object blocks store the handle of their burden thread here.
MGIT_EXEC_THREAD
This is useful for object blocks only. Returns the handle of the thread executing methods for objects in the block.

MemModifyFlags() is used to change a block's HeapFlags record. It takes three arguments: The handle of the block, the HeapFlags to turn on, and the HeapFlags to clear. It returns nothing. Not all HeapFlags can be changed after a block is created; only HF_SHARABLE, HF_DISCARDABLE, HF_SWAPABLE, and HF_LMEM can be so changed.

HandleModifyOwner() changes the geode owning a given block. It takes two arguments, namely the handle of the block and the handle of the new owner. It returns nothing. If the block is not sharable, only the owner of a block can change the block's owner.

MemModifyOtherInfo() changes the HM_otherInfo word of the block's handle table entry. It takes two arguments: The handle of the block, and one word of data to store in the HM_otherInfo field. It returns nothing.

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Data-Access Synchronization | Next: 3.8 The Reference Count


The Reference Count

Memory Management: 3.8 Using Global Memory: The Reference Count

| Up: GEOS SDK TechDocs | Up | Prev: 3.7 Retrieving Block Information | Next: 4 malloc()
MemInitRefCount(), MemIncRefCount(), MemDecRefCount()

Sometimes several different threads will need to look at the same block of memory. For example, a single thread might need to send the same information to objects in several different threads. The simplest way to do that would be to write the information in a global memory block and pass the block's handle to the objects. However, it's a bad idea to allocate global blocks for longer than necessary, since this uses up handles. It therefore becomes important to free these blocks when everyone's done with them.

GEOS provides a simple mechanism for managing this. Every block can have a reference count . When a reference count for a block reaches zero, the block is automatically freed. That way, for example, if an object needed to send the same information to five objects, it could write the data to a block, give that data block a reference count of five, and send the block's handle to the objects. Each object, when it finished accessing the data, would decrement the data block's reference count. When all five objects had decremented the reference count, the block would be freed.

You must be careful about a few things while using the reference count mechanism. First of all, the reference count is kept in the HM _otherInfo field of the block's handle table entry. For this reason, you must not use the reference count routines if you will be using HM _otherInfo for any other purpose. In particular:

  • The block may not be an object block; the HM_otherInfo field for an object block stores the handle of the burden thread for that block.
  • You may not use any of the data-access synchronization routines described in Data-Access Synchronization , since all of those routines store the semaphore in HM _otherInfo . You should generally use the reference count only for blocks that will not be changed until they are freed, so that data synchronization will not be an issue.

Once the reference count is decremented to zero, the block is immediately freed. Once a block is freed, its handle may be used for something else. If you try to increment or decrement the reference count of the block, the results are undefined. For this reason, you should make sure that the reference count does not reach zero until all threads are done with the block. One way to arrange for this is to have a single thread do all the decrementing. For example, an object might set the reference count to five, and send the handle to five other objects. Each of these objects, when finished with the block, would send a message back to the first object, which would decrement the reference count. As an alternative, you could have each of the objects decrement the count itself when it was finished. In this case, the first object would have to assume that the block was freed as soon as it sent out all of the messages, since it would have no way of knowing when the other objects would be finished with the block.

Finally, since the reference count is stored in HM _otherInfo , it has a maximum value of (216-1). If you try to increment the reference count past this value, the results are undefined. This will not be a problem for most applications.

To set up a reference count for a block, call MemInitRefCount() . This routine takes two arguments: the handle of a global memory block, and the reference count for that block. The reference count must be greater than zero. MemInitRefCount() sets the block's HM _otherInfo field to the specified reference count. MemInitRefCount() does not return anything.

To increment the reference count, call MemIncRefCount() . This routine is passed a single argument, namely the handle of the global memory block. The routine simply increments HM _otherInfo . It does not return anything.

To decrement the reference count, call MemDecRefCount() . This routine is passed the handle of a global memory block. It decrements the block's HM _otherInfo field. If the field reaches zero, MemDecRefCount() will immediately free the block. The routine does not return anything.

| Up: GEOS SDK TechDocs | Up | Prev: 3.7 Retrieving Block Information | Next: 4 malloc()


malloc()

Memory Management: 4 malloc()

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Global Memory
malloc(), calloc(), realloc(), free()

GEOS provides support for the Standard C memory allocation routines. However, support is limited by the nature of the 80x86 and the GEOS memory management system.

A geode can request memory with malloc() or calloc() . When a geode does this for the first time, the memory manager will allocate a fixed block and return a pointer to within the fixed block. (This block is actually a special kind of LMem heap.) Because the memory is in a fixed block, the geode does not need to access it with handles; it can use the pointer directly. If the block fills up, the manager can allocate another fixed block for these requests.

However, there are some problems with this. The main problem is that fixed blocks degrade the memory manager's performance. The more a geode uses malloc() , the more memory is tied up in fixed blocks. And, as always, contiguous memory is limited to 64K by the 80x86 segmented addressing scheme.

Most of the time, geodes should use other types of memory allocation. For allocating small chunks of data, applications should use LMem routines or techniques built on top of them (database items, chunk arrays, etc.); for larger chunks of memory, applications should use memory manager routines or HugeArrays. However, to help writers port C code to GEOS, malloc() and its relatives are available.

To get a stretch of contiguous memory, use the routines malloc() or calloc() . malloc() takes one argument, a size in bytes; it returns a void pointer to that many bytes of fixed memory. calloc() takes two arguments: a number of structures, and the size of each such structure. It allocates enough memory for that many structures and returns a void pointer to the memory. Both malloc() and calloc() zero-initialize the memory when they allocate it.

If a routine wants to change the size of memory allocated with malloc() or calloc() it can use realloc() . realloc() takes two arguments: a pointer to a piece of memory allocated with malloc() or calloc() , and a new size in bytes. It returns a void pointer to the memory, which may have been moved to satisfy the request. If it could not satisfy the request, it returns a null pointer, and the original memory is untouched. Note that the pointer you pass realloc() must be the same pointer that was returned by malloc/calloc ; if (for example) you allocate 100 bytes and are returned 008Bh:30h, and try to resize it by passing 008Bh:40h to realloc() , inappropriate memory will be affected, and the results are undefined.

If you decrease the size of a memory section with realloc() , the routine is guaranteed to succeed. If you increase the size, it may fail; if it does succeed, the new memory will not be zero-initialized. Reallocating a block down to zero memory is the same as freeing it. You can pass a null pointer to realloc() along with the size; this makes realloc() function like malloc() .

When you are done with memory allocated by malloc -family routines, you should call free() to free the memory for other malloc() calls. As with realloc() , you must pass the same pointer that you were originally given.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Global Memory


Local Memory

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing

The GEOS memory manager is well suited to dealing with blocks of memory in the 2K-6K range. However, for small amounts of memory, the manager's overhead becomes significant. For this reason, GEOS provides local memory (or LMem ) routines. These routines let an application allocate a block of memory and designate it as a local memory heap . The application can then request small amounts of memory from this heap. The LMem library automatically reorganizes the heap when necessary to make space available.

The local memory routines are also used to manipulate objects. All objects are special LMem chunks which are stored in a special type of LMem heap (called an object block ). Most of the routines which work on chunks can also be used on objects.

1 Design Philosophy
2 Structure of a Local Memory Heap
2.1 The Local Heap
2.2 Chunks and Chunk Handles
2.3 Types of LMem Heaps
3 Using Local Memory Heaps
3.1 Creating a Local Heap
3.2 Using Chunks
3.3 Contracting the LMem Heap
3.4 Example of LMem Usage
4 Special LMem Uses
4.1 Chunk Arrays
4.2 Element Arrays
4.3 Name Arrays

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing


Design Philosophy

Local Memory: 1 Design Philosophy

| Up: GEOS SDK TechDocs | Up | Prev: Local Memory | Next: 2 Structure of a Local Memory Heap

The GEOS memory manager is designed to deal with blocks of memory which are measured in kilobytes. Every memory block needs an entry in the global handle table; each such entry takes up 16 bytes. This memory overhead is insignificant for a 10K code block but very significant for, for example, a 100 byte database entry. Furthermore, GEOS allows a limited number of global handles. If an application were to use global blocks for small amounts of data, it could use up too many handles.

On the other hand, if an application were to store many small pieces of data in a single global block, it might have to write elaborate memory-management routines. It would have to make sure that it could resize a piece of data at will and shuffle other data to make room. This could force programmers to spend a lot of their time writing support code.

For these reasons, GEOS provides local memory routines. Applications can designate a block of memory as a local-memory heap and use LMem routines to request small amounts ( chunks ) of data. The local memory library automatically shuffles chunks in that heap to make room for new requests. Applications can change the size of a chunk at will. The cost of using the LMem routines is one added layer of indirection and a small amount of memory overhead for each chunk.

The LMem routines have another advantage. They provide a uniform way of managing small pieces of memory. This lets GEOS add functionality which applications can use in a variety of ways. For example, GEOS implements an array-management scheme based on LMem chunks; this scheme comes complete with a modified Quicksort routine. Similarly, all GEOS objects are stored in object blocks, which are a special kind of local-memory heap. This makes it easy to add or delete objects dynamically.

| Up: GEOS SDK TechDocs | Up | Prev: Local Memory | Next: 2 Structure of a Local Memory Heap


Structure of a Local Memory Heap

Local Memory: 2 Structure of a Local Memory Heap

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Design Philosophy | Next: 3 Using Local Memory Heaps

A local memory heap looks and acts much like the global heap. However, it is contained entirely within a single memory block. This block is initialized with a 16-byte LMemBlockHeader (described on The offset within the block where the local heap's handle table will begin. The local heap will fill the space to the end of the block; any data between the LMemBlockHeader and the indicated offset will be zero-initialized. If an application wil ), a local memory handle table, and a local memory heap. Optionally, a space for data may be allocated between the header and the handle table.

Each allocated section of memory within a local heap is called a chunk , and the handles of these chunks are called chunk handles .

Chunks comprise a chunk of data preceded by a word that contains the length of the data (in bytes). When the heap is created, a certain number of chunk handles will be allocated. If a chunk is requested after all of these chunks have been given out, the local memory routines will enlarge the LMem handle table, relocating chunks as necessary. Unused chunks are stored in a linked list.

The Local Heap

Chunks and Chunk Handles

Types of LMem Heaps

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Design Philosophy | Next: 3 Using Local Memory Heaps


The Local Heap

Local Memory: 2.1 Structure of a Local Memory Heap: The Local Heap

| Up: GEOS SDK TechDocs | Up | Prev: 2 Structure of a Local Memory Heap | Next: 2.2 Chunks and Chunk Handles

An application may designate any block as a local memory heap. The block may or may not be fixed, swapable, or discardable, as the application desires.

A block may have other data besides the local heap. When a local heap is created, an offset into the block can be specified. The heap will put its header structure at the beginning of the block and the handle table at the specified offset; everything in between will be left untouched by the LMem routines. The offset must be larger than the standard header or else may be zero, indicating that the default offset should be used.

Before performing any operations on a local memory heap, an application must lock the heap the way it would any other block. Some local memory routines may need to resize the block; this may cause the block to be moved on the global heap. Therefore, you should assume that these routines may invalidate all pointers to the relevant block. The descriptions of routines which may behave this way will contain a warning to that effect. When you initialize an LMem heap, you can specify that it should never be resized; this is advisable if the heap is in a fixed block.

A virtual-memory file block may contain an LMem heap. For details on this, see the Virtual Memory chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Structure of a Local Memory Heap | Next: 2.2 Chunks and Chunk Handles


Chunks and Chunk Handles

Local Memory: 2.2 Structure of a Local Memory Heap: Chunks and Chunk Handles

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 The Local Heap | Next: 2.3 Types of LMem Heaps

Just as blocks on the local heap are accessed with handles, chunks are accessed via chunk handles. Each chunk handle is an offset into the block containing the local memory heap; thus, the segment address of the locked heap, combined with the chunk handle, make up a pointer to a location within the local memory heap's chunk handle table. That location contains another offset which, when combined with the segment address of the block, composes a far-pointer to the actual chunk.

Chunks are movable within the local heap; whenever a chunk is created or resized, the local memory manager may move any chunks in that heap. There is no locking mechanism for chunks; thus, creating or resizing a chunk can potentially invalidate pointers to all the other chunks, forcing the application to again dereference the handles for these chunks. Be warned that many message handlers can cause heap compaction. As a general rule, you should not save chunk addresses around message sends; instead, dereference the chunk handles to get the current address.

Chunks are aligned along dwords. This speeds up chunk moves and similar operations. This means that when you request a chunk, its size may be slightly larger than you request.

Objects are special kinds of LMem chunks. An optr is simply the global memory handle of an LMem heap followed by the chunk handle of an object. For this reason, many LMem routines come in two formats: one which is passed an optr, and one which is passed the global and chunk handles. There is also a macro, ConstructOptr() , which is passed a memory handle and a chunk handle and returns an optr constructed from the two.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 The Local Heap | Next: 2.3 Types of LMem Heaps


Types of LMem Heaps

Local Memory: 2.3 Structure of a Local Memory Heap: Types of LMem Heaps

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Chunks and Chunk Handles | Next: 3 Using Local Memory Heaps
LMemType, LMemFlags

In addition to being used for general memory needs, local memory heaps perform many specific functions in the GEOS system. When an LMem heap is created, a member of the LMemType enumerated type is passed, indicating to what use the LMem heap will be put. Several types are available:

LMEM_TYPE_GENERAL
The LMem heap will be used for general data storage, possibly including a chunk, name, or element array. When an application creates an LMem heap, it will almost always be of type "General" or "Object."
LMEM_TYPE_WINDOW
Windows are stored in memory as LMem heaps. The header contains information about the window; each region in the window is stored as a chunk. Applications will not directly create Window heaps.
LMEM_TYPE_OBJ_BLOCK
Objects are stored in object blocks, which are LMem heaps. An object block has some extra header information and contains one chunk which contains only flags. All the objects in the block are stored as chunks on the heap. Applications can directly create object blocks; for more information, see the GEOS Programming chapter.

LMEM_TYPE_GSTATE
A GState is an LMem heap. The GState information is in the header, and the application clip-rectangle is stored in a chunk. Applications do not directly create GState blocks; rather, they call a GState creation routine, which creates the block. (See the Graphics Environment chapter.)

LMEM_TYPE_FONT_BLOCK
Font blocks are stored as LMem heaps. Applications do not create font blocks directly.
LMEM_TYPE_GSTRING
Whenever a GString is created or loaded, a GString LMem heap is created, and elements are added as chunks. The heap is created automatically by the GString routines; applications should not create GString blocks. (See the Graphics Environment chapter.)

LMEM_TYPE_DB_ITEMS
The Virtual Memory mechanism provides routines to create and manage database items

, short pieces of data which are dynamically allocated and are saved with the VM file. These items are stored in special database LMem heaps, which are created in special database blocks in the VM file. Applications do not directly allocate DB blocks; rather, they call DB routines, which see to it that the blocks are created. (See the Database chapter.)

When an LMem heap is allocated, certain flags are passed to indicate properties the heap should have. Some of these flags are passed only for system-created heaps. The flags are stored in a word-length record ( LocalMemoryFlags ); the record also contains flags indicating the current state of the heap. The LocalMemoryFlags are listed below:

LMF_HAS_FLAGS
Set if the block has a chunk containing only flags. This flag is set for object blocks; it is usually cleared for general LMem heaps.
LMF_IN_RESOURCE
Set if the block has just been loaded from a resource and has not been changed since being loaded. This flag is set only for object blocks created by the compiler.
LMF_DETACHABLE
Set if the block is an object block which can be saved to a state file.
LMF_DUPLICATED
Set if block is an object block created by the ObjDuplicateResource() routine. This flag should not be set by applications.
LMF_RELOCATED
Set if all the objects in the block have been relocated. The object system sets this when it has relocated all the objects in the block.
LMF_AUTO_FREE
This flag is used by several object routines. It indicates that if the block's in-use count drops to zero, the block may be freed. This flag should not be set by applications.
LMF_IN_MEM_ALLOC
This flag is used in error-checking code to prevent the heap from being validated while a chunk is being allocated. For internal use only--do not modify .
LMF_IS_VM
Set if LMem heap is in a VM block and the block should be marked dirty whenever a chunk is marked dirty. This flag is automatically set by the VM code when an LMem heap is created in or attached to a VM file. This flag should not be set by applications.
LMF_NO_HANDLES
Set if block does not use chunk handles. A block can be set to simulate the C malloc() routine; in this case, chunks are not relocated after being created, so chunk handles are not needed. Ordinarily, these blocks are created by the malloc() routine, not by applications. (See the discussion of malloc() in the Memory Management chapter.)

LMF_NO_ENLARGE
Indicates that the local-memory routines should not enlarge this block to fulfill chunk requests. This guarantees that the block will not be moved by a chunk allocation request; however, it makes these requests more likely to fail.

LMF_RETURN_ERRORS
Set if local memory routines should return errors when allocation requests cannot be fulfilled. If the flag is not set, allocation routines will fatal-error if they cannot comply with requests. This flag is generally clear for expandable LMem blocks, since many system routines (such as ObjInstantiate() ) are optimized in such a way that they cannot deal with LMem allocation errors.
LMF_DEATH_COUNT
This field occupies the least significant three bits of the flag field. It means nothing if the value is zero. If it is non-zero, it indicates the number of remove-block messages left which must hit BlockDeathCommon before it will free the block. This flag is used some error-checking code in the kernel.
STD_LMEM_OBJECT_FLAGS
This is a constant which combines the LMF_HAS_FLAGS and LMF_RELOCATED flags. These flags should be set for all object blocks.
| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Chunks and Chunk Handles | Next: 3 Using Local Memory Heaps


Using Local Memory Heaps

Local Memory: 3 Using Local Memory Heaps

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Structure of a Local Memory Heap | Next: 4 Special LMem Uses

Local memory heaps are much like the global heap and are accessed in much the same way. Local heaps are simple to create and manage.

Many applications will not need to use local heaps directly; instead, they will use more advanced data-management mechanisms based on local heaps, such as chunk arrays or database blocks. However, even if you use these mechanisms, you should be familiar with this section; this will help you understand how the other mechanisms work.

Remember that every local memory heap resides in a global memory block. All the rules for using memory blocks apply. (See the Memory Management chapter.)

Creating a Local Heap

Using Chunks

Contracting the LMem Heap

Example of LMem Usage

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Structure of a Local Memory Heap | Next: 4 Special LMem Uses


Creating a Local Heap

Local Memory: 3.1 Using Local Memory Heaps: Creating a Local Heap

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Local Memory Heaps | Next: 3.2 Using Chunks
LMemInitHeap(), MemAllocLMem()

Before you create a local heap, you must first allocate and lock a block on the global heap using MemAlloc() and MemLock() . (Alternatively, you can allocate a fixed block.) Then, you must call LMemInitHeap() . (If you are creating a local heap to hold objects, you should use the UserAllocObjBlock() routine instead; it's much simpler to use.)

LMemInitHeap() creates and initializes a local memory heap. It must be passed several arguments:

  • The handle of a locked or fixed block which will contain the local memory heap. It may be moveable, discardable, both, or neither; it should not have the flag HF_LMEM set. If the LMem heap will be resizable, the block may be of any size; the LMem routines will enlarge it if necessary. If the heap will not be resizable (i.e. LMF_NO_ENLARGE is passed), it must be created large enough to accommodate the heap.
  • The offset within the block where the local heap's handle table will begin. The local heap will fill the space to the end of the block; any data between the LMemBlockHeader and the indicated offset will be zero-initialized. If an application will not need a fixed data space, it should specify an offset of zero; the handle table will then be put immediately after the LMemBlockHeader . If the created block will be an object block, then it will need room for an ObjLMemBlockHeader structure. Often, when an application needs a fixed data space, it will define a special structure, the first element of which is an LMemBlockHeader , and will pass the size of that structure as the offset. It can then access the fixed data by using the fields of the structure. If the offset specified is less than the size of an LMemBlockHeader , LMemInitHeap() will return an error.
  • A member of the LMemType enumerated type, specifying the type of block to be created (see In addition to being used for general memory needs, local memory heaps perform many specific functions in the GEOS system. When an LMem heap is created, a member of the LMemType enumerated type is passed, indicating to what use the LMem heap wil ).
  • A word of LocalMemoryFlags for the heap. (See When an LMem heap is allocated, certain flags are passed to indicate properties the heap should have. Some of these flags are passed only for system-created heaps. The flags are stored in a word-length record (LocalMemoryFlags); the record also .)
  • A word specifying the number of chunk handles to leave room for in the starter handle table. When these chunks have all been allocated, the local memory manager will expand the LMem handle table to create more chunk handles. No matter how big the starter handle table is, the heap will initially contain just one chunk (whose size is specified by the next argument). Applications should generally pass the constant STD_INIT_HANDLES.
  • A word specifying the amount of space to be assigned to the chunk created when the heap is initialized. When more space is needed, the chunks will expand to the end of the block, and (if necessary) the block itself will be expanded. Applications should generally pass the constant STD_INIT_HEAP.

LMemInitHeap() creates the LMemBlockHeader and the chunk handle table. It also creates a single free chunk; more chunks will automatically be created as needed. It may resize the block passed (unless the flag LMF_NO_ENLARGE is passed); therefore, any pointers to the block may become invalid. It does not return anything.

If you want to create a memory block and initialize it as an LMem heap in one operation, call MemAllocLMem() . This routine takes two arguments: a member of the LMemType enumerated type, and the amount of space to leave for the header (again, a zero size indicates that the default header size should be used). MemAllocLMem() allocates a movable, swapable block in the global heap, then initializes an LMem heap in that block. If you specify an LMemType of LMEM_TYPE_OBJ_BLOCK, MemAllocLMem() will pass the STD_LMEM_OBJECT_FLAGS flags; otherwise, it will pass a clear LocalMemoryFlags record.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Local Memory Heaps | Next: 3.2 Using Chunks


Using Chunks

Local Memory: 3.2 Using Local Memory Heaps: Using Chunks

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Creating a Local Heap | Next: 3.3 Contracting the LMem Heap
LMemAlloc(), LMemDeref(), LMemFree(), LMemGetChunkSize(), LMemReAlloc(), LMemInsertAt(), LMemDeleteAt(), LMemDerefHandles(), LMemFreeHandles(), LMemGetChunkSizeHandles(), LMemReAllocHandles(), LMemInsertAtHandles(), LMemDeleteAtHandles()

Once a local heap has been initialized, you can allocate, use, and free chunks at will. Chunks can only be manipulated while the block containing the LMem heap is fixed or locked on the global heap.

LMemAlloc() allocates a new chunk on the local heap. It is passed the handle of the block containing the heap and the size of the chunk needed. LMemAlloc() returns the handle of the new chunk (which must then be dereferenced before the chunk is used). The size requested will be rounded up as necessary to ensure that the chunks are dword-aligned. An additional two bytes will be allocated to store the size of the chunk; these bytes will be before the data. This routine may compact the chunks on the local heap, so all pointers to that heap will be invalidated; they will have to be dereferenced by their chunk handles. Furthermore, the block itself may be moved (if LMF_NO_ENLARGE is not set). Even fixed blocks may be moved if they need to expand to accommodate new chunks.

All of the following routines come in two forms. As noted, an optr is simply the handle of an object block, followed by the object's chunk handle. For this reason, most LMem routines come in two slightly different formats: one where the chunk is specified with an optr, and one where it is specified with the two handles. In all other ways, the two versions of each routine are identical. Indeed, in assembly there is only a single version of each routine; the only difference is in how the C routines take their parameters.

Once you have allocated a chunk, you must dereference its chunk handle in order to use it. You can do this with LMemDeref() . This routine takes a singe parameter, namely the optr. It returns a pointer to the data portion of the chunk (after the size word). This pointer will remain valid until the block is unlocked or until a routine is called which can cause block resizing or heap compaction (e.g. LMemAlloc() ). Since these routines can invalidate chunk-pointers, it is important that data-synchronization routines be used if more than one thread is accessing the heap; otherwise, one thread may cause the heap to be shuffled while another thread is trying to read from it. The version which takes handles is named LMemDerefHandles() .

When you are done using a chunk of memory, you should free it with LMemFree() . This routine is passed an optr; it does not return anything. It does not resize the block or shuffle chunks; therefore, pointers to other chunks will not be invalidated by LMemFree() . The version which takes handles is named LMemFreeHandles() .

You can find out the size of any chunk by calling the routine LMemGetChunkSize() . This routine is passed an optr; it returns the size of the chunk in bytes (not counting the chunk's size word). The version which takes handles is named LMemGetChunkSizeHandles() .

Chunks can be resized after creation. The Boolean routine LMemReAlloc() takes two arguments, namely an optr and the new size of the chunk. If the new size is larger than the old one, bytes will be added to the end of the chunk; chunks may be shuffled and the block may be resized, so all pointers to chunks will be invalidated. The new bytes will not be zero-initialized. If the new chunk size is smaller than the old one, the chunk will be truncated; pointers to chunks will not be invalidated. This routine will fail only if the LMem heap ran out of space and could not be resized. In this case, it will return non-zero without changing the chunk. If it succeeds, it returns zero. The version which takes handles is called LMemReAllocHandles() .

You can add bytes inside a chunk with the Boolean routine LMemInsertAt() . This routine takes three arguments: the optr, an offset within the chunk, and the number of bytes to add. The new space is added beginning at the specified offset; it is initialized to zeros. This may cause chunks to be shuffled and/or the block to be expanded; pointers to chunks are therefore invalidated. Note that it is your responsibility to make sure that the offset within the chunk really is in the chunk; otherwise, results are undefined. If LMemInsertAt() fails (because the LMem heap ran out of space and could not be expanded), it returns non-zero without changing the chunk; otherwise it returns zero. The version which takes handles is named LMemInsertAtHandles() .

You can delete bytes within a chunk with the routine LMemDeleteAt() . This routine takes three arguments: the optr, the offset within the chunk of the first byte to be deleted, and the number of bytes to delete. This routine does not invalidate pointers to chunks. The routine does not return anything. Note that it is your responsibility to make sure that all the bytes to be deleted are within the chunk, i.e. that the offset and number of bytes passed do not specify bytes that are beyond the end of the chunk. If you fail to do this, results are undefined. The version which takes handles is named LMemDeleteAtHandles() .

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Creating a Local Heap | Next: 3.3 Contracting the LMem Heap


Contracting the LMem Heap

Local Memory: 3.3 Using Local Memory Heaps: Contracting the LMem Heap

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Using Chunks | Next: 3.4 Example of LMem Usage
LMemContract()

The local memory manager routines ordinarily take care of heap compaction. However, you can also order compaction at will.

The routine LMemContract() compacts the heap and then frees all the unused heap space (by truncating the block with the LMem heap). The routine takes one argument, namely the handle of the (locked or fixed) block containing the LMem heap. It shuffles all the chunks, thus invalidating pointers to chunks; however, it is guaranteed not to move the block on the global heap.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Using Chunks | Next: 3.4 Example of LMem Usage


Example of LMem Usage

Local Memory: 3.4 Using Local Memory Heaps: Example of LMem Usage

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Contracting the LMem Heap | Next: 4 Special LMem Uses

At first, the local memory techniques can seem tricky. This section contains an example of LMem usage in Goc. The example shows the basic principles of LMem usage.

Code Display 16-1 LMem Usage in GOC

/*
 * Declarations
 */
/* We'll want to have a fixed data area at the start of the block. That area will
 * have to start with an LMemBlockHeader, but after that, we can put whatever we
 * want. To make it easy to access the fixed data, we define a structure.
 */
typedef struct {
    LMemBlockHeader		MLMBH_standardHeader;
    float		MLMBH_someData;
    float		MLMBH_someMoreData;
    char		MLMBH_someChars[10];
} MyLMemBlockHeader;
MyLMemBlockHeader 			*thisHeapsHeader;
MemHandle			thisHeapsHandle;
ChunkHandle			firstChH, secondChH;
char 			*firstChPtr, *secondChPtr;
int			i;
/*
 * Code
 */
/* We have to create the LMem heap. First, we create the block: */
thisHeapsHandle = MemAlloc(					/* MemAlloc returns the block handle */
            2048,		/* Allocate 2K; can grow as necessary */
            HF_SWAPABLE,		/* Make block swapable. LMemInitHeap()
                     * will add the flag HF_LMEM. */
            HAF_ZERO_INIT | HAF_LOCK); 				/* Zero & lock the block
                             * upon allocation */
LMemInitHeap(thisHeapsHandle,					/* Pass handle of locked block */
        LMEM_TYPE_GENERAL,			/* Allocate a general heap */
        0,			/* Don't pass any flags */
        sizeof(MyLMemBlockHeader), 	/* Offset to leave room for header */
        STD_INIT_HANDLES,			/* Standard # of starter handles */
        STD_INIT_HEAP);		/* Allocate standard amt. of empty heap */
/* The block is still locked; we can initialize the fixed data section. */
thisHeapsHeader = (MyLMemBlockHeader *) MemDeref(thisHeapsHandle);
thisHeapsHeader->MLMBH_someData = 3.1415926;
/* Now, we allocate some chunks. This invalidates pointers to this heap (such as
 * thisHeapsHeader), since chunk allocation may cause the heap to be resized (and
 * thus moved). The block must be locked when we do this.
 */
firstChH = LMemAlloc(					/* LMemAlloc returns a chunk handle */
        thisHeapsHandle,			/* Pass handle of block . . . */
        100);			/* . . . and number of bytes in chunk */
secondChH = LMemAlloc(thisHeapsHandle, 50);
/* Now, we start writing data to a chunk: */
firstChPtr = (char *) LMemDerefHandles(thisHeapsHandle, firstChH);
for(i = 0; i <= 30; i++)
   firstChPtr[i] = 'x';
/* We can insert 10 bytes into the middle of the second chunk. This may cause the
 * chunks or blocks to be shuffled; all pointers are invalidated
 */
LMemInsertAtHandles(thisHeapsHandle, secondChH, 						/* Block & chunk handles */
        20,				/* Insert after 20th byte */
        30);				/* Insert 30 null bytes */
/* If we want to access the first chunk, we need to dereference its handle again:
 */
firstChPtr = (char *) LMemDeref(thisHeapsHandle, firstChH);
for(i = 1; i <= 15; i++)
    firstChPtr[(i<<1)] = 'o';
/* When we're done with a chunk, we should free it. This does not invalidate
 * any pointers to other chunks.
 */
LMemFreeHandles(thisHeapsHandle, firstChH);
/* If we won't be using an LMem heap for a while, we should unlock it the way we
 * would any block: */
MemUnlock(thisHeapsHandle);
/* When we're done with the LMem heap, we can just free it. */
MemFree(thisHeapsHandle);
| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Contracting the LMem Heap | Next: 4 Special LMem Uses


Special LMem Uses

Local Memory: 4 Special LMem Uses

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Using Local Memory Heaps | Next: 4.1 Chunk Arrays

Local memory heaps are used for many purposes in GEOS. Objects are stored in special LMem heaps, called object blocks ; windows, GStrings, and many other things are implemented as LMem heaps; and LMem heaps can be used to manage arrays of data. Most of these things are done by the system, transparent to the user; however, some of these things can be called on directly by applications.

Most of these techniques are described in their own chapters. One use of LMem heaps will be described in this chapter; namely, the use of LMem chunks to store special arrays of data via the Chunk Array routines. The chapter will also describe special purpose variants of the Chunk Array: the Element Array and the Name Array.

Chunk Arrays

Element Arrays

Name Arrays

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Using Local Memory Heaps | Next: 4.1 Chunk Arrays


Chunk Arrays

Local Memory: 4.1 Special LMem Uses: Chunk Arrays

| Up: GEOS SDK TechDocs | Up | Prev: 4 Special LMem Uses | Next: 4.2 Element Arrays

Very often an application will need to keep track of many different pieces of data and access them by an index. An application can do this in the traditional way, i.e., allocate a block of memory and set it up as an array. However, GEOS provides a mechanism which is often more suitable: the chunk array . The chunk array routines let you dynamically insert or delete elements in the middle of an array; you can get a pointer to an arbitrary element (specified by its index number); you can sort the array based on any arbitrary criterion. The array can be specified as "uniform-size" (all elements the same size, specified when the chunk array is created), or "variable-size" (each element can be created at an arbitrary size and can be resized at will). Note that either type of array can grow or shrink dynamically; while the elements may be of a fixed size, the array need not be.

The chunk array is implemented on top of the LMem routines. The entire array is a single chunk in a local memory heap (hence the name). It therefore has a maximum total size of somewhat less than 64K, and memory efficiency drops significantly if it is larger than roughly 6K. If you need a larger array, you should use a Huge Array (see the VM chapter). If you will be using the chunk array routines, you should include chunkarr.h .

Structure of the Chunk Array

A chunk array is contained in a single chunk in an LMem heap. It begins with a special header structure which specifies certain characteristics of the chunk array: the number of elements in the array, the size of each element (or zero for variable sized arrays), the offset from the start of the chunk to the first element, and the offset from the first element to the end of the array. The header is a structure of type ChunkArrayHeader ; an application can examine the fields of this structure directly or by using chunk array routines. The creating application can request that the chunk array contain some blank space between the header and the first element; it can use that space however it likes.

Elements can be referenced by index number. The first element has index number zero. You can translate element numbers to pointers, and vice versa, by calling ChunkArrayElementToPtr() and ChunkArrayPtrToElement() (see Adding, Removing, and Accessing Elements ).

A uniform-size chunk array has a simple structure. After the header (and the extra space, if any) come the elements. They follow one after another, with no free space between them. An application can request a pointer to any element, specified by index; the chunk array routine multiplies the index by the size of each element and adds the product to the address of the first element, producing a pointer to the requested element. An application can also proceed through the array, going from one element to the next, without specifically requesting pointers.

The structure of a variable-size chunk array is a little bit more complicated. After the header (and extra space) is a lookup-table of two-byte entries, each containing the offset from the start of the chunk to the appropriate element. The chunk array routines maintain this table automatically. When an application wants to reference a given element, the chunk array routine doubles the index number and adds it to the offset to the start of this table; this produces the address of a table entry, which itself contains the offset to the actual element. Effectively, there are two arrays with the same number of elements; the first is a uniform-sized array of offsets to the entries in the second (variable-sized) array. However, this is transparent to the application, which merely requests a pointer to element n and is returned that pointer. It does not need to know or care about the extra table constructed by the chunk array routines.

Creating a Chunk Array

ChunkArrayCreate(), ChunkArrayCreateAt(), ChunkArrayCreateAtHandles(), ChunkArrayHeader

Chunk arrays are created in local memory heaps. An LMem heap can have several chunk arrays, and it can have other types of chunks besides chunk arrays; however, since an LMem heap is limited to 64K in total size, the more you put in the heap, the less room there is for a chunk array to grow.

The first step in creating a chunk array is to create an LMem heap. Create the heap the same way you would any general LMem heap. The heap should probably be left resizable, since that way it will be able to grow to accommodate the chunk array.

Once you have created the heap, use the routine ChunkArrayCreate() to create the chunk array. This routine will allocate a chunk for the chunk array, initialize the array, and return the chunk handle. Since the routine allocates a chunk, it can cause chunk shuffling or heap resizing; thus, all pointers to the heap are invalidated.

ChunkArrayCreate() takes four arguments:

  • The handle of a locked block containing the LMem heap;
  • The size of each element, or zero if the elements are variable-size; and
  • The size of the header for the chunk array. If you pass zero, the routine will automatically leave enough space for a ChunkArrayHeader structure; if you pass a non-zero argument, it must be larger than the size of a ChunkArrayHeader . If you will need a data space at the start of the array, it is a good idea to define a structure, the first element of which is a ChunkArrayHeader , and use that structure to access the fixed data area; you can then pass the size of that structure as this argument.
  • A field of ObjChunkFlags which act as those passed to LMemAlloc() .

There is another version of this routine which creates a chunk array in an existing chunk. This routine, ChunkArrayCreateAt() , takes three arguments, namely an optr indicating the chunk, the size of each element, and the size of the header. It allocates a chunk array in that chunk, resizing it if necessary, and returns the chunk's handle. Any data in the chunk may be overwritten (except for whatever data falls in the header area after the ChunkArrayHeader ). The version which takes handles is called ChunkArrayCreateAtHandles() .

When you are done with a chunk array, you can free it with LMemFree() the way you would any other chunk.

Adding, Removing, and Accessing Elements

ChunkArrayAppend(), ChunkArrayAppendHandles(), ChunkArrayInsertAt(), ChunkArrayInsertAtHandle(), ChunkArrayDelete(), ChunkArrayDeleteHandle(), ChunkArrayDeleteRange(), ChunkArrayDeleteRangeHandles(), ChunkArrayElementResize(), ChunkArrayElementResizeHandles(), ChunkArrayElementToPtr(), ChunkArrayElementToPtrHandles(), ChunkArrayPtrToElement(), ChunkArrayPtrToElementHandle(), ChunkArrayGetElement(), ChunkArrayGetElementHandles()

The chunk array library provides a high-level interface for working with the arrays. The application specifies a request in general terms (e.g., "Insert a new element before element five," "Give me a pointer to element 20"). The routines take care of the low-level memory management: inserting bytes, swapping chunks to make room, etc. These routines depend on the LMem routines; therefore, the block containing the LMem heap must be locked or fixed when you use these routines (or any other chunk array routines).

When you call a chunk array routine, you must pass the handles specifying the chunk. As with other routines which act on a chunk, these routines come in two formats: one in which the chunk is specified with an optr, and one in which it is specified with the global memory handle and the chunk handle.

Adding elements to a chunk array is easy. To add an element to the end of a chunk array, use the routine ChunkArrayAppend() . This routine automatically updates the ChunkArrayHeader (and the lookup-table, if elements are variable-sized). The routine takes two arguments, namely the optr and the size of the new element. The size must be greater than zero. If the array elements are uniform-sized, the size argument is ignored. The routine will resize the chunk to accommodate the new element, update its header table (and lookup table if necessary), and return a pointer to the element. Since the chunk is resized, all other chunk pointers (and pointers within the chunk array) are invalidated. The version which takes handles is named ChunkArrayAppendHandles() .

You can also add an element within the middle of an array. The routine ChunkArrayInsertAt() takes three arguments, namely the optr, a pointer to the location at which to insert the element, and the size of the element (ignored for uniform-sized arrays). The size must be greather than zero. The routine will insert the appropriate number of bytes at that location in the chunk, update the header and lookup-table, and return a pointer to the new element. Pointers to chunks are invalidated. The version which takes the chunk handle, ChunkArrayInsertAtHandle() , is slightly unusual in that it is passed the chunk handle but not the global memory handle; the routine gets the segment address of the chunk from the passed pointers.

When you are done with an element, free it with ChunkArrayDelete() . This routine takes two arguments, namely the optr and a pointer to the element to be deleted. It shrinks the chunk; thus, it is guaranteed not to shuffle chunks, so chunk pointers remain valid (though pointers to elements within the chunk array will be invalidated if the elements come after the deleted element). Again, the handle version, ChunkArrayDeleteHandle() , is passed the chunk handle but not the global handle.

If you need to delete several consecutive elements, call ChunkArrayDeleteRange() . This routine takes three arguments: the optr to the chunk array, the index of the first element to delete, and the number of elements to delete. The specified elements will be deleted. As with ChunkArrayDelete() , the global and local heaps will not be shuffled. The handle version, ChunkArrayDeleteRangeHandles() , is passed the global handle of the LMem heap and the chunk handle of the chunk array instead of the optr to the chunk array.

Elements in variable-sized arrays can be resized after creation with the routine ChunkArrayElementResize() . This routine takes three arguments: the optr, the element number, and the new size. The new size must be greater than zero. The routine resizes the element and updates the lookup table. If the new size is larger than the old, null bytes will be added to the end of the element; chunks may be shuffled, so all chunk pointers are invalidated. If the new size is smaller than the old, the element will be truncated. This is guaranteed not to shuffle chunks, so pointers to chunks remain valid, though pointers within the array may be invalidated. The version which takes handles, ChunkArrayElementResizeHandles() , is passed both the global memory handle and the chunk handle.

If you have the index of an element and you want to access that element, use the routine ChunkArrayElementToPtr() . It takes three arguments: the optr, the element number, and a pointer to a word-length variable. The routine writes the size of the element in the variable and returns a pointer to the element. (If you are not interested in the element's size, pass a null pointer.) It does not change the chunk in any way, so no pointers are invalidated. If you pass an index which is out-of-bounds, ChunkArrayElementToPtr() will treat it as the index of the last element. (The constant CA_LAST_ELEMENT is often used for this purpose.) However, the error-checking version will always fatal-error if passed the index CA_NULL_ELEMENT (i.e. 0xffff). The version which takes handles is named ChunkArrayElementToPtrHandles() .

If you know the address of an element and you need to find out its index, use the routine ChunkArrayPtrToElement() . This routine takes two arguments, namely the optr and a pointer to the element. It returns the index number of the element. The version which takes the chunk handle, ChunkArrayPtrToElementHandle() , is passed the chunk handle and the pointer but not the global memory handle.

You can copy an element to a specified location with the routine ChunkArrayGetElement() . This routine takes three arguments: the optr, the element number, and a pointer to a buffer big enough to hold the entire element. The routine will copy the element to the specified buffer. The version which takes handles is called ChunkArrayGetElementHandles() .

Chunk Array Utilities

ChunkArrayGetCount(), ChunkArrayGetCountHandles(), ChunkArrayZero(), ChunkArrayZeroHandles(), ChunkArrayEnum(), ChunkArrayEnumHandles(), ChunkArrayEnumRange(), ChunkArrayEnumRangeHandles(), ChunkArraySort(), ArrayQuickSort(), bsearch()

To find out how many elements are in a chunk array, use the routine ChunkArrayGetCount() . This routine takes one argument, namely the optr. It returns the number of elements in the array. It does not change the array; no pointers are invalidated. The version which takes handles is named ChunkArrayGetCountHandles() .

If you want to delete all elements in the array but you don't want to free the array itself, use the routine ChunkArrayZero() . This routine takes one argument, namely the optr. It does not return anything. This routine deletes all the elements in the array, updates the header and lookup tables, and resizes the chunk. Since the chunk is truncated, no chunks are swapped, so no chunk pointers are invalidated (though pointers to elements are, naturally, invalidated). The version which takes handles is named ChunkArrayZeroHandles() .

If you want to apply a function to every element in the array, use ChunkArrayEnum() . This routine is passed three arguments:

  • The optr.
  • A pointer to a Boolean callback routine. This routine will be called for each element in succession. This routine must be declared _pascal. If the callback routine ever returns true , ChunkArrayEnum() will immediately return with value true (without checking any more elements). If the callback routine returns false for every element, ChunkArrayEnum() will return with value false.
  • A pointer which is passed to the callback routine.

The callback routine should be written to take two arguments:

  • A pointer to the start of an element
  • The pointer passed to ChunkArrayEnum()

ChunkArrayEnum() can be used for many different purposes, depending on the nature of the callback routine. For example, it can perform some action on every element (in which case it ought always to return false ); it can analyze the data in the various elements; it can check to see if any element meets some criterion. If it needs to write its results, it might do so at the location indicated by the pointer. ChunkArrayEnum() will not cause heap shuffling unless the callback routine causes it; thus, if the callback routine avoids shuffling the heap, it can (for example) be passed a pointer to a chunk in the same LMem heap as the chunk array. The version which is passed handles is named ChunkArrayEnumHandles() .

There is another version of ChunkArrayEnum() which acts on a range of elements. This routine is called ChunkArrayEnumRange() . This routine takes the same arguments as ChunkArrayEnum() , plus two more: a start index, and a number of elements to enumerate. ChunkArrayEnumRange() calls the callback routine for the element with the specified index, and for every element thereafter until it has processed the specified number of elements. You can have it enumerate to the end of the chunk array by passing a count of 0xffff. There is a version which takes handles called ChunkArrayEnumRangeHandles() .

You can get into trouble if more than one thread of execution is accessing a single ChunkArray and one or both of these threads is using one of the ChunkArrayEnum...() routines. The error-checking version of the kernel detects this situation and generates a CHUNK_ARRAY_ENUM_INSERT_OR_DELETE_RUN_BY_MULTIPLE_THREADS warning if it detects such. If your code is getting this warning, you should add some synchronization between threads. While one thread is enumerating through chunks in the chunk array, the other should do nothing to enumerate the same array, nor alter the array in any way.

The chunk array library also provides a sorting routine, ChunkArraySort() . This routine performs a modified Quicksort on the array, using insertion sorts for subarrays below a certain size. The sort routine takes three arguments: the chunk array's optr, a pointer to a comparison function, and a word of data which is passed to the comparison function.

Whenever the sort routine needs to decide which of two elements comes first, it calls the comparison routine. The comparison routine takes two arguments, namely the optr and the word of data passed to ChunkArraySort() . It returns a signed word with the following significance: If the first of the elements should come first in the sorted array, it returns a negative number; if the first element ought to come after the second, it should return a positive number; and if it doesn't matter which comes first, it should return zero. You can write a general-purpose comparison routine which can compare based on any of several parts of the element, and you can use the data word to instruct it which part to sort on; or you can use the data word to tell it to sort in ascending or descending order. ChunkArraySort() does not cause heap shuffling as long as the comparison routine does not. The routine which takes handles is called ChunkArraySortHandles() .

ChunkArraySort() is based on a more general array sorting routine, ArrayQuickSort() . ChunkArraySort() reads data about the array from the array header and passes the information to ArrayQuickSort() . You can call ArrayQuickSort() directly for arrays which are not chunk arrays, provided all elements are of uniform size. ArrayQuickSort() takes five arguments: a pointer to an array (which should be locked or fixed in memory), the number of elements in the array, the size of each element, a pointer to a callback routine (which has exactly the same format as the ChunkArraySort() callback routine), and a data word to pass to that callback routine. It does not return anything.

Note that ChunkArraySort() is currently implemented only for chunk arrays with fixed-sized elements.

The bsearch() routine conducts a binary search on an a sorted array.

Example of Chunk Array Usage

This section contains an example of how a chunk array might be used. It shows several common chunk array actions, including sorting the array.

Code Display 16-2 Example of Chunk Array Usage

/*
 *	Declarations (not in any routine)
 */
/* We want to store some data right after the chunk array header, so we define our
 * own header structure. Data in this structure (except for the ChunkArrayHeader
 * proper) will not be affected by the chunk array routines. */
typedef struct {
    ChunkArrayHeader			standardChunkArrayHeader;
    int			someData;
    float			someMoreData;
} MyChunkArrayHeader;
/* For simplicity, we define a structure which will be used for each element in the
 * array. (This is entirely optional.) */
typedef struct {
    char	someText[80];
    int	anInteger;
    float	aFloat;
} MyElementStructure;
/* We define some values to pass to the sort routine. The routine will sort by a
 * different field depending on what value it's passed. */
#define	SORT_ARRAY_BY_STRING				0
#define	SORT_ARRAY_ASCENDING_BY_INT				1
#define	SORT_ARRAY_DESCENDING_BY_FLOAT				2
/* This is the routine we will use to sort the array. We pass the address of this
 * routine to ChunkArraySort(), which will call this routine to compare elements.
 * It returns a negative number if the first element should come before the second
 * in the sorted array, and a positive integer if the second should come before the
 * first. If the elements can be in either order, it returns zero. */
sword _pascal MyElementCompareRoutine(
    MyElementStructure 			*e1,	/* Address of first element */
    MyElementStructure 			*e2,	/* Address of second element */
    word		valueForCallback) 		/* Datum passed in to ChunkArraySort() */
{
    /* We sort differently depending on what the value of valueForCallback is.
     * That way, we can use this one routine for all our sorting.
     */
    switch(valueForCallback) {
        case SORT_ARRAY_ASCENDING_BY_INT:
        /* Compare the elements based on their integer fields. Smaller int
         * comes first.*/
        if (e1->anInteger < e2->anInteger)
            return(-1);
        else if (e1->anInteger > e2->anInteger)
            return(1);
        else return(0);
        break;
        case SORT_ARRAY_DESCENDING_BY_FLOAT:
        /* Compare the elements based on their float fields. Larger float
         * comes first.*/
        if (e1->aFloat > e2->aFloat)
            return(-1);
        else if (e2->aFloat < e2->aFloat)
            return(1);
        else return(0);
        break;
        case SORT_ARRAY_BY_STRING:
        /* In this case, we call the localization routine to compare the
         * two strings. The localization routine has the same return
         * conventions as this routine, so we return its result directly.
         */
            return(LocalCmpStrings(e1->someText, e2->someText, 40));
            break;
        default:
        /* If we get here, we were passed a bad callback word. The callback
         * routine therefore does not express a preference in ordering the
         * two elements; it shows this by returning zero.
         */
    } /* end of switch */
}
/* All of the above appears in some declaration section. The code below might
 * appear in any routine which creates a chunk array. First, the declarations:
 */
MemHandle			blockWithHeap;
chunkHandle			myChunkArray;
MyChunkArrayHeader			*chunkArrayAddress;
MyElementStructure			*currentElement;
/* Now the code. Here, blockWithHeap has already been set to hold the block handle
 * of an LMem heap.
 */
MemLock(blockWithHeap); /* Always lock LMem heap before acting on it */
myChunkArray = ChunkArrayCreate(blockWithHeap,
            sizeof(MyElementStructure), /* Size of each element */
            sizeof(MyChunkArrayHeader) /* Size of header */
            0);
/* Let's write some data into our part of the header. We need the array's address: */
chunkArrayAddress = LMemDerefHandles(blockWithHeap, myChunkArray);
chunkArrayAddress->someData = 42;						/* This data won't be affected */
chunkArrayAddress->someMoreData = 2.7182818;						/* by chunk array actions */
/* Now, let's create an element: */
currentElement = ChunkArrayAppendHandles(blockWithHeap, myChunkArray, 0);
    /* That invalidates chunkArrayAddress */
currentElement->anInteger = 1999;
currentElement->aFloat = 1.4142135;
strcpy(currentElement->someText, 				"Work is the curse of the drinking class.\n" \
                "  --Oscar Wilde")
/* We're done with the array for the moment, so we unlock it: */
MemUnlock(blockWithHeap);
/* Let's assume that several other elements are created now. */
/* . . . */
/* Now we need to sort the array: */
MemLock(blockWithHeap);
ChunkArraySortHandles(blockWithHeap, myChunkArray,
        SORT_ARRAY_ASCENDING_BY_INT, /* this is passed to comp. routine */
        MyElementCompareRoutine);
/* Array is now sorted! */
| Up: GEOS SDK TechDocs | Up | Prev: 4 Special LMem Uses | Next: 4.2 Element Arrays


Element Arrays

Local Memory: 4.2 Special LMem Uses: Element Arrays

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Chunk Arrays | Next: 4.3 Name Arrays

Sometimes an application will create an array with a high duplication rate; that is, the array may contain many identical elements. This can be inefficient if the duplication rate is very high or elements are very large. For this reason, GEOS provides a special variant of the chunk array known as the element array . Every element in an element array has a reference count. When you insert an element, the insertion routine checks whether an identical element already exists in the array. If it does, the routine does not add another copy; instead, it increments the reference count of the element already in the array and returns its element index. If no such element exists, the routine copies the new element into the array, gives it a reference count of 1, and returns its element number. The application may specify a comparison routine which determines whether an element already exists in the array; or it may instruct the insertion routine to do a byte-level comparison.

Note that elements in an element array may be of fixed, uniform size, or they may be of variable size (just as with chunk arrays). When you create an element array, you must specify the size of each element; specifying a size of zero indicates that the elements are of variable size.

Members of an element array keep their index numbers until they are freed. If an element is deleted, the element array routines actually just resize the element to zero and add it to a free list. This means that an element with index 12 might not be the thirteenth element in the array, as it would in a chunk array (remember, indexes start with zero); there might be freed elements before it. For this reason, we speak of an element in an element array having a "token" instead of an index; you should generally consider a token to be an opaque value. Nevertheless, in most situations, element array tokens behave just like chunk array indexes.

When you delete a reference to an element, its reference count is decremented. If the reference count reaches zero, the routine calls an application-specified callback routine to delete the element itself.

Note that adding an element to an element array requires a linear search through the existing elements; thus, element arrays are inefficient for large numbers of elements, if elements will be continually added. Accessing elements, however, takes constant time, since the element array routines can quickly translate an element's token into the offset to that element. Thus, it takes no longer to access an element in an element array than it does to access one in a chunk array.

Creating an Element Array

ElementArrayCreate(), ElementArrayCreateAt(), ElementArrayCreateAtHandles(), ElementArrayHeader

To create an element array, call the routine ElementArrayCreate() . Like ChunkArrayCreate() , it takes three arguments: the LMem heap's handle, the size of each element (or 0 for variable-sized elements), and the size to leave for the array header. The routine allocates a chunk in the LMem heap and initializes it as an element array. There is one significant difference: Element arrays begin with an ElementArrayHeader , a structure whose first component is a ChunkArrayHeader . If you are allocating free space between the header and the array, make sure to leave enough room for an ElementArrayHeader . If you do not need to allocate free space, you can pass a header size of zero, as with ChunkArrayCreate() .

Note that when you specify the size of each element in the element array, you must include the size of each element's header. Thus, if each element's body were six bytes long, you would tell ElementArrayCreate() that the size of each element is "6 + sizeof( RefElementHeader )".

There is another version of this routine, ElementArrayCreateAt() , which creates the element array in a pre-existing chunk. This routine takes three arguments: an optr indicating the chunk, the size of each element, and the size of the header. It creates the element array in the specified chunk, resizing it if necessary. Any data in the chunk may be overwritten (except for whatever data falls in the header area after the ElementArrayHeader ). There is also a version which takes handles instead of an optr; it is called ElementArrayCreateAtHandles() .

The routine returns the handle of the newly-created element array. It can cause heap compaction or resizing; therefore, all pointers to the heap are invalidated.

Adding an Element

ElementArrayAddElement(), ElementArrayAddElementHandles(), ElementArrayAddReference(), ElementArrayAddReferenceHandles()

Adding an element to an element array is somewhat different from adding one to a chunk array. To add an element to a chunk array, you merely call the append routine, then write the element into the allocated space. If you want to add an element to an element array, you must first write out the data for the element in a buffer. You then pass the address of this data to ElementArrayAddElement() , which compares your new element with the elements already in the array, and copies it into the array if necessary.

ElementArrayAddElement() takes four arguments:

  • An optr indicating the element array;
  • The address of the element to copy into the array;
  • A pointer to a callback comparison routine (see below), or a null pointer to do a byte-wise comparison;
  • A dword of data to pass to the comparison routine.

You may have your own criteria for deciding whether an element should be copied into an array. For example, elements in the array may have three data fields; perhaps you count two elements as matching if the first two data fields match. For this reason, ElementArrayAddElement() lets you specify your own comparison routine. The callback routine should be a Boolean routine, declared _pascal, which takes three arguments:

  • The address of the element to add;
  • The address of an element in the array to compare the new element to;
  • The callback data dword passed to ElementArrayAddElement() .

ElementArrayAddElement() calls the callback routine to compare the new element to each element in the array. If the callback routine ever returns true , ElementArrayAddElement() has found a matching element in the array; it will increment that element's reference count and return its index. If the callback routine returns false for every element, ElementArrayAddElement() copies the new element into the array and gives it a reference count of 1. It returns the element's index; the element will keep that index until it is freed. Note that there is no way to specify where in an element array a new element should be added. If there are free spaces in the array, the new element will be created in the first free space; otherwise, it will be appended to the end of the array.

If you want to do a bytewise comparison, pass in a null pointer as the callback routine. ElementArrayAddElement() will then do a bytewise comparison of the elements, treating two elements as equal only if every byte in the element bodies matches. (It's okay if the two elements have different headers and reference counts; the bytewise comparison routine only compares the element bodies.) The bytewise comparison is implemented as a machine-language string instruction; it is therefore very fast.

If you know that the element you want to add is already in the array, call ElementArrayAddReference() . This routine simply increments the reference count of a specified element; it does no comparisons. It is therefore much faster than ElementArrayAddElement() .

Both of these routines have counterparts which are passed handles instead of an optr; these counterparts are named ElementArrayAddElementHandles() and ElementArrayAddReferenceHandles() .

Accessing Elements in an Element Array

ElementArrayElementChanged(), ElementArrayElementChangedHandles()

Elements in element arrays are accessed in almost the same way as elements in chunk arrays. There is one major difference. Each element in an element array begins with a RefElementHeader structure, which contains the element's reference count. For this reason, it is a good idea to declare special structures for your elements and have the first component of that structure be the RefElementHeader structure (as in the code sample below).

Code Display 16-3 Structure for Element Array Elements

/* We need to declare two different structures. One will have all the data fields
 * in the body of the element; it will be called "MyElementBody". The other
 * structure begins with the "RefElementHeader" structure, which must begin every
 * element in an element array; the other field of the structure is a
 * "MyElementBody" structure. When you create an element, you pass the address of
 * the MyElement structure to ElementArrayCreateElement().
 */
typedef struct {
    word		amount;		/* This has the element's data fields */
    float		interestRate;
    char		description[20];
} MyElementBody;
typedef struct {
    RefElementHeader			header; 	/* We won't use this--it holds ref count */
    MyElementBody		body;
} MyElement;

Note that if you change an element, this may make it identical to another element in the element array; in this case, the two could be combined into one. To check for this situation, call ElementArrayElementChanged() . This routine takes four arguments: the optr to the element array, the token for the element changed, a callback comparison routine, and a dword of data which is passed to the callback routine. ElementArrayElementChanged() checks to see if the element is identical to any other element in the array. It calls the comparison routine to compare elements. (You can force a bytewise comparison by passing a null function pointer; this comparison will make sure that all bytes in the element body match, though it's okay if the elements have different values in their RefElementHeader structures.) If it matches another element, the two elements will be combined; i.e., the element passed will be deleted, and the matching element will have its reference count increased appropriately. The matching element's token will be returned; you will have to change any references to the old element appropriately. If no match is found, the token which was passed will be returned. The version which takes handles is called ElementArrayElementChangedHandles() .

Removing An Element From An Element Array

ElementArrayRemoveReference(), ElementArrayRemoveReferenceHandles(), ElementArrayDelete(), ElementArrayDeleteHandles()

When you want to remove an element from an element array, you should ordinarily call ElementArrayRemoveReference() . This routine decrements the element's reference count; it does not, however, delete the element unless the reference count reaches zero (i.e. the last reference to the element has been deleted).

This routine takes four arguments: the optr of the array, the index of the element, a pointer to a callback routine, and a dword-sized constant to be passed to it. There may be certain bookkeeping tasks you want to perform when an element is actually being deleted but not when it is just having its reference count decremented. In this case, you can pass the address of a callback routine, which will be called on any element to be deleted just before the deletion occurs. After the callback routine returns, the element will be removed. If you do not need to have a callback routine called, pass a null function pointer. As noted, when an element is removed, it is actually just resized to zero; that way the index numbers of following elements are preserved.

If you want to delete an element regardless of its reference count, call ElementArrayDelete() . This routine takes two arguments, namely the optr indicating the array and the index of the element to be deleted. It does not take a callback routine; perform any necessary bookkeeping before you call it.

Both of these routines have counterparts which take handles; these counterparts are named ElementArrayRemoveReferenceHandles() and ElementArrayDeleteHandles() .

The "Used Index" and Other Index Systems

ElementArrayGetUsedCount(), ElementArrayGetUsedCountHandles(), ElementArrayUsedIndexToToken(), ElementArrayUsedIndexToTokenHandles(), ElementArrayTokenToUsedIndex(), ElementArrayTokenToUsedIndexHandles()

Sometimes its useful to have a special index system for element arrays. Perhaps you would like the used elements to be numbered sequentially, that is, the first "used" element would be element "zero," even if there were free elements before it. This would require setting up a second index system, besides the one used by the element array routines. GEOS provides routines with this functionality.

To find out the number of elements in an element array, call ElementArrayGetUsedCount() . This routine can return either the number of elements in use or the number of "in use" elements which satisfy any arbitrary criteria. The routine takes three arguments: the optr to the element array, a dword of data which is passed to a callback routine, and a pointer to a Boolean callback routine. That callback routine should itself take two arguments: a pointer to an element, and the dword passed to ElementArrayGetUsedCount() . The callback routine is called once for each "in use" element. The callback should return true if the element should be counted; otherwise, it should return false . For example, the callback routine might return true if the element is longer than 10 bytes; in this case, ElementArrayGetUsedCount() would return the number of elements which are longer than 10 bytes. To have every used element counted, pass a null function pointer. The version of this routine which takes handles is called ElementArrayGetUsedCountHandles() .

If you use a different indexing scheme, you will need a way to translate the index into the normal element array token. To do this, call the routine ElementArrayUsedIndexToToken() . This routine takes four arguments: the optr of the element array, the index count, a dword (which is passed to the callback routine), and a callback routine. The callback routine is of the same format as the callback routine passed to ElementArrayGetUsedCount() ; it should return true if the element meets some criterion. ElementArrayUsedIndexToToken() translates the index passed into the element array's token for that element. For example, if the callback routine returns true for elements which are longer than 10 bytes, and you pass an index of five, ElementArrayUsedIndexToToken() will return the token for the sixth element in the element array which is longer than 10 bytes. (Remember, all indexes are zero-based.) Again, passing a null function pointer makes the routine count all "in-use" elements. The version which takes the element array's handles is called ElementArrayUsedIndexToTokenHandles() .

To translate a token back into this kind of index, call ElementArrayTokenToUsedIndex() . This routine takes four arguments: the optr to the element array, an element token, a callback routine (as with the other routines in this section), and a dword which is passed along to the callback routine. The routine finds the element whose token was passed and returns the index it would have under the indexing system defined by the callback routine. Again, passing a null function pointer makes the routine count every "in-use" element. The routine which takes handles is called ElementArrayTokenToUsedIndexHandles() .

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Chunk Arrays | Next: 4.3 Name Arrays


Name Arrays

Local Memory: 4.3 Special LMem Uses: Name Arrays

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Element Arrays

Applications can build on chunk arrays and element arrays in many ways. The chunk array library includes one example of an elaboration on these structures: the name array . The name array is a special kind of element array in which elements can be accessed by a "name" label as well as by a token. Elements in a name array are of variable size. Each element is divided into three sections: The first is the RefElementHeader ; every element in an element array must begin with one of these (and the name array is a kind of element array). The second is the data section. The data section is the same size for every element in a given name array; this size may be anything from zero bytes up to a maximum of NAME_ARRAY_MAX_DATA_SIZE (64 bytes). The data section is followed by a "name" section. This section contains a sequence of bytes of any length up to a maximum of NAME_ARRAY_MAX_NAME_SIZE (256 bytes). The name may contain nulls and need not be null terminated. You can translate a name into the element's token by calling NameArrayFind() (described below).

Note that creating elements in a name array, as in any element array, requires a search through all elements; it thus takes linear time. Furthermore, translating a name into a token also requires a linear search through the elements, and thus also takes linear time. Name arrays thus become slow if they grow too large. Accessing an element by token, however, still takes constant time.

Creating a Name Array

NameArrayCreate(), NameArrayCreateAt(), NameArrayCreateAtHandles(), NameArrayAdd(), NameArrayAddHandles(), NameArrayHeader, NameArrayAddFlags

Creating a name array is much like creating an element array. Every name array must begin with a NameArrayHeader . This structure has the following definition:

typedef struct {
    ElementArrayHeader				NAH_meta;
    word				NAH_dataSize;
} NameArrayHeader;

This structure contains one new field, namely NAH _datasize . This field specifies the size of the data area of each element; the name area is of variable size. You may examine this field at will, but you may not change it. You may set up a fixed data area between the NameArrayHeader and the elements. The usual way to do this is to define a structure whose first element is a NameArrayHeader structure.

To create a name array, call the routine NameArrayCreate() . This routine is passed three arguments:

  • The global handle of an LMem heap. The name array will be created in this block.
  • The size of the data area in each element. The total size of the element will vary, depending on the size of the name. Remember, there is a three byte RefElementHeader at the start of every element (before the data section).
  • The size of the header structure for the name array. If you will not need a fixed data area, you can pass a size of zero, and enough space will automatically be left for a NameArrayHeader .

The routine allocates a chunk in the specified heap, initializes a name array in that chunk, and returns the chunk's handle. If it fails for any reason, it returns a null chunk handle. Since the routine allocates a chunk, all pointers to the LMem heap are invalidated.

If you want to create a name array in a specific chunk, call NameArrayCreateAt() . This routine is almost the same as NameArrayCreate() . However, instead of being passed a memory handle, NameArrayCreateAt() is passed an optr to a chunk. The name array will be created in that chunk. Any data in that chunk (outside of the fixed data area) will be destroyed. Note that if the chunk is too small for the name array, NameArrayCreateAt() will resize it; thus, pointers to the LMem heap may be invalidated. There is a version of this routine which takes the chunk's global and chunk handles instead of its optr; this routine is called NameArrayCreateAtHandles() .

To create an element, call NameArrayAdd() . This routine creates an element and copies the data and name into it. The routine takes five arguments:

  • The optr to the name array.
  • A pointer to an array of characters containing the element's name.
  • The length of the name, in bytes. This many characters will be copied into the name. If you pass a length of zero, bytes will be copied until a null byte is reached (the null will not be copied).
  • A word-length set of NameArrayAddFlags . Only one flag is currently defined, namely NAAF_SET_DATA_ON_REPLACE. This flag is described below.
  • A pointer to the data section. The data will be copied into the new element. (The length of the data portion was specified when the name array was created.)

NameArrayAdd() allocates the element, copies in the data and name, and returns the element's token. If an element with the specified name already exists, NameArrayAdd() will not create a duplicate. Instead, if the flag NAAF_SET_DATA_ON_REPLACE was passed, NameArrayAdd() will copy the new data section into the existing element; if the flag was not passed, it will leave the existing element unchanged. In either case, it will return the existing element's token and increment its reference count. If an element is added, the name array may have to be resized; therefore, pointers into the chunk array will be invalidated. There is a version in which the name array is specified by its global and chunk handles; this version is called NameArrayAddHandles() .

Code Display 16-4 Allocating a Name Array

/* We want a fixed data space, so we define our own header structure. */
typedef	struct {
    NameArrayHeader		MNAH_meta;		/* Must begin with a NameArrayHeader!!! */
    char *		MNAH_comments[32];
} MyNameArrayHeader;
/* The data section of the name array will be this structure: */
typedef struct {
    double		MDSS_myDataFloat;
    int		MDSS_myDataInts[20];
} MyDataSectionStruct;
/* Every element in the name array will have this structure: */
typedef struct {
    RefElementHeader			MES_header;		/* For internal use */
    MyDataSectionStruct			MES_data;
    char			MES_name[]; 		/* We don't know how long this
                         * will actually be */
} MyElementStruct;
MemHandle		myLMemHeap;		/* Assume this is initialized */
ChunkHandle		myNameArray;
/* Sample call to NameArrayCreate() */
myNameArray = NameArrayCreate(myLMemHeap, sizeof(MyDataSectionStruct),
                sizeof(MyNameArrayHeader));

Accessing Elements in a Name Array

NameArrayFind(), NameArrayFindHandles(), NameArrayChangeName(), NameArrayChangeNameHandles()

Name array routines can be accessed with all the routines used for accessing element arrays. However, a few special purpose routines are also provided.

If you know the name of an element and want a copy of its data, call NameArrayFind() . This routine is passed four arguments:

  • The optr to the name array.
  • A pointer to a character buffer. The buffer should contain the name of the element sought.
  • The length of the name. If a length of zero is passed, the name is considered to be null terminated (the trailing null is not part of the name).
  • A pointer to a return buffer. The data portion of the element will be copied to this location.

NameArrayFind() will do a linear search through the elements. If it finds one with the name specified, it will return that element's token and copy the data portion into the return buffer. If there is no element with the specified name, NameArrayFind() will return the constant CA_NULL_ELEMENT. The routine NameArrayFindHandles() is identical, except that the name array is specified by its global and chunk handles.

To change an element's name, call NameArrayChangeName() . This routine is passed four arguments:

  • The optr to the name array.
  • The token of the element whose name will be changed.
  • A pointer to a character buffer containing the new name for the element.
  • The length of the new name. If a length of zero is passed, the name is considered to be null terminated (the trailing null is not part of the name).

NameArrayChangeName() changes the element's name. If the new name is longer than the old, the element will have to be resized; this will invalidate pointers within that block. NameArrayChangeNameHandles() is identical, except that the name array is specified by its global and chunk handles.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Element Arrays


Virtual Memory

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing

Most disk-operating systems provide minimal file functionality. A file is simply treated as a sequence of bytes; applications can copy data from files to memory or vice versa, but nothing more elaborate. GEOS provides much more elaborate functionality with its Virtual Memory files.

Virtual Memory (VM) is very useful for two reasons. First, it is often impractical to read an entire disk file into contiguous memory; indeed, it is impossible to do so if the file is larger than 64K. The use of virtual memory allows each file to grow arbitrarily large. Second, each disk file is one long, cumbersome stream of data. By using virtual memory files, applications can break files down into smaller, more manageable blocks which the memory manager can handle more efficiently.

1 Design Philosophy
2 VM Structure
2.1 The VM Manager
2.2 VM Handles
2.3 Virtual Memory Blocks
2.4 VM File Attributes
3 Using Virtual Memory
3.1 How to Use VM
3.2 Opening or Creating a VM File
3.3 Changing VM File Attributes
3.4 Creating and Freeing Blocks
3.5 Attaching Memory Blocks
3.6 Accessing and Altering VM Blocks
3.7 VM Block Information
3.8 Updating and Saving Files
3.9 Closing Files
3.10 The VM File's Map Block
3.11 File-Access Synchronization
3.12 Other VM Utilities
4 VM Chains
4.1 Structure of a VM Chain
4.2 VM Chain Utilities
5 Huge Arrays
5.1 Structure of a Huge Array
5.2 Basic Huge Array Routines
5.3 Huge Array Utilities

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing


Design Philosophy

Virtual Memory: 1 Design Philosophy

| Up: GEOS SDK TechDocs | Up | Prev: Virtual Memory | Next: 2 VM Structure

The GEOS Virtual Memory file management system is designed to provide features not easily available with standard file systems. Designed primarily for use by applications for long-term data storage, it is also used by the system for many other purposes, such as to provide state saving (in the UI). The main benefits it provides to applications include the following:

  • Convenient file structure
    GEOS divides VM files into VM blocks, each of which is accessed via a VM block handle. This structure is much like a "disk-based heap," and is analogous to (and compatible with) the memory heap. VM blocks are accessed the same way whether they are on disk or resident in memory. They can be independently resized and locked.
  • Ease of file manipulation
    Many file manipulation techniques are much simpler with VM files. For example, geodes do not have to keep track of which blocks they have changed; instead, when they change a block, they mark it as dirty, and when they update the file, the virtual memory routines will automatically copy just the changed blocks to the disk.
  • Sharing of Data Files
    GEOS maintains information about each VM file and knows when more than one thread is using it. This allows the system to notify all users when one thread has modified a file. The system provides data synchronization for individual blocks.
  • Integrity of Data Files
    GEOS provides several features to protect the integrity of VM files. For example, you can request that GEOS maintain a backup that can be used to restore the file to its last-saved state. In addition, you can have GEOS do all writing to the file synchronously to ensure that all the data in the file stays consistent even if GEOS is somehow interrupted.
  • Uniform File Structure
    Because all GEOS data files are based on VM files, utilities can be written which work for all VM files. For example, the Document Control objects can take care of opening, closing, and saving all data files which are based on VM files. Furthermore, data structures can be designed which can be added at will into any VM file.
| Up: GEOS SDK TechDocs | Up | Prev: Virtual Memory | Next: 2 VM Structure


VM Structure

Virtual Memory: 2 VM Structure

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Design Philosophy | Next: 3 Using Virtual Memory

Virtual memory can be thought of as a heap stored in a disk file. Like the global heap, it is divided into blocks which are 64K or smaller; each block is accessed via a handle. Blocks can be locked, which brings them into main memory. If blocks are modified while in memory, they are copied back to the file when the file is updated.

The primary component of virtual memory is the VM file. The VM file consists of a VM File Header, a collection of VM blocks, and a special structure called a VM Header. The VM File Header is a standard GEOS file header, containing the file's extended attributes and system bookkeeping information. Geodes may not access it directly; instead, they can make calls to the extended attributes routines to access data in the header. The VM blocks and the VM Header do not occupy fixed places in the file. In particular, the VM Header does not necessarily come before all the blocks. Instead, the VM File Header stores the offset within the file to the VM Header, and the VM Header stores information about the blocks (such as their locations in the file). Furthermore, the blocks in a VM file are arranged in no particular order; they are not necessarily arranged in the order they were created, or in any other sequence.

The VM Header maintains all the bookkeeping information about the VM file. For example, it contains the VM Block Table. The block table is much like the global handle table. Block handles are offsets into the block handle table; a block's entry in the table contains information about the block, such as the block's location in the file. Usually, the Block Table contains entries for blocks that haven't been created yet; when all of these handles have been used, the VM Manager expands the block table. For details, see Virtual Memory Blocks .

The VM Manager

VM Handles

Virtual Memory Blocks

VM File Attributes

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Design Philosophy | Next: 3 Using Virtual Memory


The VM Manager

Virtual Memory: 2.1 VM Structure: The VM Manager

| Up: GEOS SDK TechDocs | Up | Prev: 2 VM Structure | Next: 2.2 VM Handles

The VM manager can be thought of as a memory manager for a disk-based heap, providing all the services a memory manager would and more. The VM manager provides for allocation, use, and deallocation of virtual memory blocks. It manages the Block Table, enlarging it as necessary when more VM block handles are needed. It keeps track of which VM blocks have been loaded into memory, which are currently in use, and which have been "dirtied" (modified) since they were read from the disk; it also keeps track of how many threads are accessing a given VM file at any time. The VM manager also accomplishes all swapping and read/write operations involving VM files.

| Up: GEOS SDK TechDocs | Up | Prev: 2 VM Structure | Next: 2.2 VM Handles


VM Handles

Virtual Memory: 2.2 VM Structure: VM Handles

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 The VM Manager | Next: 2.3 Virtual Memory Blocks

There are several different types of handles associated with VM files. It is important not to get them confused.

  • File Handles
    When you open or create a VM file, it is assigned a file handle, as discussed in the File System chapter. Whenever you call a VM routine, you must specify the VM file by passing this handle. The file handle can change each time the file is opened. Furthermore, if two different network users have the same VM file open at the same time, each user might have a different file handle for the same file.
  • VM Block Handles
    The VM file has a block table in the VM Header. The block table contains the locations of blocks in the file. A given block has the same block handle every time the file is opened. If a file is duplicated, blocks in the new file will have the same VM handles as the corresponding blocks in the old file. In this chapter, references to "block handles" or "VM handles" are referring to VM block handles unless otherwise noted.
  • Memory Block Handles
    When a VM block is locked, it is copied into a memory block. This memory block has an entry in the global handle table. The memory block may persist even after the VM block has been unlocked. Ordinarily, you can refer to a VM block by its VM block handle whether or not it is resident in memory; however, in some cases, you may want to use the memory block handle instead of the VM block handle. This saves time when you know the block is resident in memory, because the VM manager doesn't have to translate the VM block handle into the corresponding global handle. It is also necessary if you need to resize or dirty a VM block. You can instruct the VM manager to use the same global handle each time a given VM block is locked until the file is closed; otherwise, the memory handle for a VM block may change each time the block is locked.
  • Chunk Handles and Database/Cell Handles
    A VM block can contain a local memory heap. If so, that block will have its own chunk handle table. Also, the Database and Cell libraries have been designed to let the VM file efficiently manipulate small pieces of data. These libraries are based on the LMem library. Each item of data has its own DB handle as well as a VM Block handle. Database items are discussed in depth in the Database chapter.
| Up: GEOS SDK TechDocs | Up | Prev: 2.1 The VM Manager | Next: 2.3 Virtual Memory Blocks


Virtual Memory Blocks

Virtual Memory: 2.3 VM Structure: Virtual Memory Blocks

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 VM Handles | Next: 2.4 VM File Attributes

Most file systems treat files as a string of bytes. A user can read a file sequentially or can start reading at a specified distance into the file. This makes it difficult to work with large files. Reading an entire large file into memory may be impractical, and it may be difficult to access different parts of the file at once.

For this reason, GEOS divides its Virtual Memory files into VM blocks. This division is entirely internal to GEOS. When the file is written to disk, it is still a sequence of bytes, and other operating systems can copy the file normally. However, GEOS geodes can access the file much more conveniently by specifying the blocks they wish to access.

The Nature of VM Blocks

A VM block is a sequence of bytes in a VM file. It must be small enough to fit in a global memory block (i.e. 64K or less, preferably 2K-6K). It may move about in the file; for this reason, it is accessed by a VM block handle. Blocks are either free or used . A used block has an entry in the block table and also a space allocated in the file. (This could be a block of free space, which will be freed the next time the file is compacted.) A free block has a slot in the file's handle table but no space in the file; it is available if a thread needs to allocate a block.

Blocks persist after a file has been closed and GEOS has been shut down. A given block is always accessed by the same block handle. There are utilities to copy blocks within a VM file or between files. Blocks in a VM file are in no particular order. If an application wants to set up a sequence of blocks, it can create a VM Chain, in which the first word of each block is the handle of the next block in the chain. However, even chained blocks will probably not be in order in the actual file.

Each VM block can be assigned a "user ID" number. You can request the handles of the VM blocks with any given ID number. You do not have to assign ID numbers to blocks, but it is sometimes convenient. The ID numbers are stored in the handles' entries in the block table, not in the blocks themselves; this makes it easy to find a block with a specified user ID. User IDs can be changed at will with the routine VMModifyUserID() . Note that all user IDs from 0xff00 to 0xffff are reserved for system use. You can find a block with a specific user ID by calling VMFind() ; see You can locate VM blocks by their user ID numbers. The routine VMFind() takes three arguments: the VM file handle, a VM block handle, and the user ID for which to look. The routine looks through the block table, starting with the handle after th .

Creating and Using VM Blocks

There are two ways you can create a VM block. The first is to request a VM block: You specify the size of the block, the block is created, and you are returned the handle. This is the method ordinarily used. The second method is to attach memory to a block: You create a global memory block, and instruct the VM manager to add it to the VM file. There are sometimes advantages to this technique; for example, you can create an LMem heap, and then attach it to the VM file; it will then be saved with the file. You can also attach a memory block to an existing VM block; this will destroy whatever used to be in the VM block and replace it with the contents of the new block.

You can dynamically resize a VM block by locking it into memory, resizing the memory block, and saving it back to the disk.

These techniques are described in detail in Creating and Freeing Blocks and Attaching Memory Blocks .

File Compaction

When a VM block is freed, the VM manager will note that there is empty space in the file. It will use that space when new blocks are allocated. However, since new blocks will probably not fit exactly in the spaces left by freed blocks, some space may be wasted.

In time, the percentage of wasted file space can grow unacceptably large. To prevent this, the Virtual Memory manager periodically compacts the files. When the ratio of data to free space drops below a certain threshold, the Virtual Memory manager copies the data in the file over the free space. While a file is being compacted, any requests for access to the file will block until compaction is finished. Note that the format of the data is not changed; the free space between data blocks is simply removed.

When a geode creates a VM file, it can specify a "compaction threshold." When the percentage of used space drops below this threshold, the VM manager will automatically compact the file without any attention from the application. The geode should take care in setting the threshold. If the threshold is too low, the file may grow unacceptably large before it is compacted; on the other hand, if the threshold is too high, the VM manager might spend too much time compacting the file for relatively low gains. The application can specify a threshold of zero; this will cause the system default threshold to be used.

Note that if a file is in "backup mode," the file will be compacted only on calls to VMSave() , VMSaveAs() , or VMRevert() . If the file is not in backup mode, it can be compacted on any call to VMUpdate() .

File Updating and Backup

When a block is locked into memory, the VM manager copies the data from the disk block to the global memory block. When the block is unlocked, the VM manager assumes that it can discard the memory block, since the data is already on the disk in the VM block.

If you alter the data in a block, you must notify the VM manager of this fact. You do this by marking the block as dirty . When a block has been marked dirty, the VM manager knows that the version in memory is more up-to-date than the version in the disk file. If the flag VMA_SYNC_UPDATE is off (the default), the block will be written back to the file as soon as possible. If the attribute is on , the block will not be copied back to the disk file until VMUpdate() is called; until then, the block will be copied to the disk swap space if memory is needed. The next time you lock the block, you will be given the new, changed version.

When you want to write the dirty blocks to the disk, you can instruct the VM manager to update the file. This copies all the dirtied blocks back to the disk and marks all blocks as clean . The VM manager also automatically updates the file when it is closed. The updating routines check if the file is currently clean; thus, very little time is lost if you try to update a clean file.

The VM manager can be instructed to notify all users of a file when the file changes from clean to dirty. This has two main uses: it helps maintain data synchronization if many geodes are using the same file, and it lets the document control objects know when to enable the "Save" and "Revert" triggers. (See the Documents Objects chapter.)

The VM manager can be instructed to maintain a backup of a file. If it is so instructed, it will not overwrite the original block when it updates it; instead, it will keep a copy of the original block as well as a copy of the new version. This is transparent to the application. When the VM Manager is instructed to save the file, it deletes all the backup blocks. If it is instructed to revert the file to its last-saved version, it replaces the new version of the changed blocks with the backup versions, thus restoring the file to its condition as of the last time it was saved. If the VM manager is instructed to "save-as" the file, it will create a new file, which does not contain the backup blocks; that is, it contains the file as it currently exists. It will then revert the original file to its last-saved version and close it.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 VM Handles | Next: 2.4 VM File Attributes


VM File Attributes

Virtual Memory: 2.4 VM Structure: VM File Attributes

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Virtual Memory Blocks | Next: 3 Using Virtual Memory
VMAttributes

Each VM file has a set of attributes which determine how the VM Manager treats the file. These attributes are specified by a set of VMAttributes flags. When a VM file is created, all of these attributes are off; after a file has been created, you can change the attributes with VMSetAttributes() (see Changing VM File Attributes ). The following flags are available:

VMA_SYNC_UPDATE
Allow synchronous updates only. Instructs VM Manager to update the file only when you call un updating routine ( VMUpdate() , VMSave() , etc.). This attribute is off by default (indicating that the VM manager should feel free to update blocks whenever they are unlocked). You should set this attribute if the file might not be in a consistent state every time a block is unlocked.
VMA_BACKUP
Maintain a backup copy of all data. The file can then be restored to its last stored state. This is described above.
VMA_OBJECT_RELOC
Use the built-in object relocation routines. This attribute must be set if the VM file contains object blocks.
VMA_NOTIFY_DIRTY
If this attribute is set, the VM Manager will notify all threads which have the VM file open when the file changes from clean to dirty. It notifies threads by sending a MSG_META_VM_FILE_DIRTY to each process that has the file open. (This message is defined for MetaClass , so any object can handle it.)
VMA_NO_DISCARD_IF_IN_USE
If this attribute is set, the VM manager will not discard LMem blocks of type LMEM_TYPE_OBJ_BLOCK if OLMBH_inUseCount is non-zero. This attribute must be set if the file contains object blocks. If this attribute is set, each object block will be kept in memory as long as any thread is using an object in the block.
VMA_COMPACT_OBJ_BLOCK
If set, the VM manager will unrelocate generic-object blocks before writing them. It does this by calling CompactObjBlock() . This allows a VM file to contain generic object blocks.
VMA_SINGLE_THREAD_ACCESS
Set this if only a single thread will be accessing the file. This allows optimizations in VMLock() . This attribute must be set if the VM file contains object blocks.
VMA_OBJECT_ATTRS
This is not, strictly speaking, a VM attribute. Rather, it is a mask which combines the flags VMA_OBJECT_RELOC, VMA_NO_DISCARD_IF_IN_USE, and VMA_SINGLE_THREAD_ACCESS. All of these attributes must be set if the file contains object blocks.
| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Virtual Memory Blocks | Next: 3 Using Virtual Memory


Using Virtual Memory

Virtual Memory: 3 Using Virtual Memory

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 VM Structure | Next: 4 VM Chains

The most common way applications will use Virtual Memory is for their data files. However, there are other uses; for example, VM provides a convenient way to maintain working memory. Some data structures (such as Huge Arrays) can only exist in VM files; an application may create a temporary VM file just for these structures.

How to Use VM

Opening or Creating a VM File

Changing VM File Attributes

Creating and Freeing Blocks

Attaching Memory Blocks

Accessing and Altering VM Blocks

VM Block Information

Updating and Saving Files

Closing Files

The VM File's Map Block

File-Access Synchronization

Other VM Utilities

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 VM Structure | Next: 4 VM Chains


How to Use VM

Virtual Memory: 3.1 Using Virtual Memory: How to Use VM

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Virtual Memory | Next: 3.2 Opening or Creating a VM File

There are five basic steps to using VM files. The steps are outlined below, and described in greater detail in the following sections.

  • Open (or create) the VM file
    Before you perform any actions on a VM file, you must open it with VMOpen() . This routine can be used to open an existing VM file or create a new one. If you use the document control objects, they will open and create files automatically. Once you have created a VM file, you may want to change its attributes with VMSetAttributes() .
  • Bring a VM block into the global memory heap
    After you open a VM file, you can bring blocks from the file into memory with VMLock() . You can also create new blocks with VMAlloc() and VMAttach() .
  • Access the data
    Once a VM block has been brought into memory, you can access it the way you would any other memory block. When you are done with the data, you should unlock it with VMUnlock() . If you change the memory, you should mark it dirty with VMDirty() before unlocking it. This ensures that the new version of the block will be written to the disk.
  • Update the VM file
    Use one of the several VM updating routines to copy the dirty blocks back to the disk. (If asynchronous update is allowed, the VM file manager will try to update blocks whenever they are unlocked.)
  • Close the VM file
    Use VMClose() when you are done with the file. It will update the file and close it.
| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Virtual Memory | Next: 3.2 Opening or Creating a VM File


Opening or Creating a VM File

Virtual Memory: 3.2 Using Virtual Memory: Opening or Creating a VM File

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 How to Use VM | Next: 3.3 Changing VM File Attributes
VMOpen(), VMOpenType, VMAccessFlags

To create or open a VM file, call the routine VMOpen() . You may not need to open and create files directly; if you use the document control objects, they automatically create and open files as the user requests. (See the Document Objects chapter.) VMOpen() looks for the file in the thread's working directory (unless a temporary file is being created, as described below). VMOpen() takes four arguments and returns the file handle. The arguments are:

  • File name
    This argument is a pointer to a string of characters. This string is a relative or absolute path specifying the file to open; if a temporary file is being created, the string is the path of the directory in which to place that file, followed by fourteen null bytes (counting the string-ending null). The name of the temporary file is appended to the path.
  • VMAccessFlags
     This argument is a set of flags which specifies how the file is accessed. The flags are described below.
  • VMOpenType enumerated type
    This argument specifies how the file should be opened. The VMOpenType are described below.
  • Compaction threshold
    This is the minimum percentage of a file which must be used for data at any given time. If the percentage drops below this threshold, the file is compacted. If you pass a threshold of zero, the system default threshold is used. The compaction threshold is set only when the file is created; this argument is ignored if an existing file is opened.

When you use VMOpen() , you must specify how the file should be opened. You do this by passing a member of the VMOpenType enumerated type. The types are as follows:

VMO_TEMP_FILE
If this is passed, the file will be a temporary data file. When you create a temporary file, you pass a directory path, not a file name. The path should be followed by fourteen null bytes, including the string's terminating null. The system will choose an appropriate file name and add it to the path string.
VMO_CREATE_ONLY
If this is passed, the document will be created. If a document with the specified name already exists in the working directory, VMOpen() will return an error condition.
VMO_CREATE
If this is passed, the file will be created if it does not already exist; otherwise it will be opened.
VMO_CREATE_TRUNCATE
If this is passed, the file will be created if it does not already exist; otherwise, it will be opened and truncated (all data blocks will be freed).
VMO_OPEN
Open an existing file. If the file does not exist, return an error condition.
VMO_NATIVE_WITH_EXT_ATTRS
The file will have a name compatible with the native filesystem, but it will have GEOS extended attributes. This flag can be combined with any of the other VMOpenType values with a bit-wise or .

You also have to specify what type of access to the file you would like. You do this by passing a record of VMAccessFlags . This is a byte-length bitfield. The following flags are available:

VMAF_FORCE_READ_ONLY
If set, the file will be opened read-only, even if the default would be to open the file read/write. Blocks in read-only files cannot be dirtied, and changes in memory blocks will not be updated to the disk VM blocks.
VMAF_FORCE_READ_WRITE
If set, the file will be opened for read/write access, even if the default would be to open the file for read-only access.
VMAF_SHARED_MEMORY
If set, the VM manager should try to use shared memory when locking VM blocks; that is, the same memory block will be used for a given VM block no matter which thread locks the block.
VMAF_FORCE_DENY_WRITE
If set, the file will be opened deny-write; that is, no other threads will be allowed to open the file for read/write access.
VMAF_DISALLOW_SHARED_MULTIPLE
If this flag is set, files with the file attribute GFHF_SHARED_MULTIPLE cannot be opened.
VMAF_USE_BLOCK_LEVEL_SYNCHRONIZATION
If set, the block-level synchronization mechanism of the VM manager is assumed to be sufficient; the more restrictive file-level synchronization is not used. This is primarily intended for system software. (See File-Access Synchronization .)

If you open a file with VMAF_FORCE_READ_ONLY, it's generally a good idea to also open it with VMAF_FORCE_DENY_WRITE. When you open a file VMAF_FORCE_READ_ONLY, if the file is writable, and is located on a writable device which can be used by other machines (e.g. a network drive), the kernel will load the entire file into memory and make the blocks non-discardable (even when they are clean); this keeps the file you see consistent, even if another user changes the version of the file on the disk. However, this can cause problems if the machine has limited swap space. If the file is opened with VMAF_FORCE_DENY_WRITE, no other device will be allowed to change the file while you have it open, which means the kernel can just load and discard blocks as necessary.

The routine VMOpen() returns the file handle. If it cannot satisfy the request, it returns a null handle and sets the thread error word. The error word can be recovered with the ThreadGetError() routine. The possible error conditions are:

VM_FILE_EXISTS
VMOpen() was passed VMO_CREATE_ONLY, but the file already exists.
VM_FILE_NOT_FOUND
VMOpen() was passed VMO_OPEN, but the file does not exist.
VM_SHARING_DENIED
The file was opened by another geode, and access was denied.
VM_OPEN_INVALID_VM_FILE
VMOpen() was instructed to open an invalid VM file (or a non-VM file).
VM_CANNOT_CREATE
VMOpen() cannot create the file (but it does not already exist).
VM_TRUNCATE_FAILED
VMOpen() was passed VMO_CREATE_TRUNCATE; the file exists but could not be truncated.
VM_WRITE_PROTECTED
VMOpen() was passed VMAF_FORCE_READ_WRITE, but the file or disk was write-protected.
|  Up: GEOS SDK TechDocs | Up | Prev: 3.1 How to Use VM | Next: 3.3 Changing VM File Attributes


Changing VM File Attributes

Virtual Memory: 3.3 Using Virtual Memory: Changing VM File Attributes

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Opening or Creating a VM File | Next: 3.4 Creating and Freeing Blocks
VMGetAttributes(), VMSetAttributes()

When a VM file is created, it is given a set of VMAttributes (see VMA_SYNC_UPDATE Allow synchronous updates only. Instructs VM Manager to update the file only when you call un updating routine (VMUpdate(), VMSave(), etc.). This attribute is off by default (indicating that the VM manager should feel free to upd ). These attributes can be examined with the routine VMGetAttributes() . The routine takes one argument, namely the handle of the VM file (which is overridden if a default VM file is set). It returns the VMAttributes flags.

You can change the attributes by calling VMSetAttributes() . This routine takes three arguments: the file handle (which may be overridden), a set of bits which should be turned on, and a set of bits which should be turned off. It returns the new VMAttributes flags.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Opening or Creating a VM File | Next: 3.4 Creating and Freeing Blocks


Creating and Freeing Blocks

Virtual Memory: 3.4 Using Virtual Memory: Creating and Freeing Blocks

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Changing VM File Attributes | Next: 3.5 Attaching Memory Blocks
VMAlloc(), VMAllocLMem(), VMFree()

Once you have created a VM file, you have to allocate blocks in order to write data to the file. The usual way to do this is with VMAlloc() . This routine takes three word-sized arguments:

  • The file handle
    This argument is overridden if a default VM file is set.
  • A user-ID number
    This can be any word of data the application wants to associate with the VM block. The application can locate blocks with a given user ID by using VMFind() (see You can locate VM blocks by their user ID numbers. The routine VMFind() takes three arguments: the VM file handle, a VM block handle, and the user ID for which to look. The routine looks through the block table, starting with the handle after th ).
  • The number of bytes in the block
    This may be zero, in which case no memory is allocated; a memory block must be specifically attached with VMAttach() (see Attaching Memory Blocks ).

The routine returns the handle of the VM block. Before you can use the block, you have to lock it with VMLock() . The block is marked dirty when it is allocated.

There is a routine to allocate a block and initialize it as an LMem heap. This is useful if you are storing object blocks in a VM file. The routine, VMAllocLMem() , takes three arguments:

  • The VM file handle
    This is overridden if a default VM file is set.
  • A member of the LMemTypes enumerated type
    This specifies what kind of heap the LMem heap will be. (See the Local Memory chapter.)
  • The size of the block header
    Use this if you want to store extra data in the LMem block header. To use the standard LMem header, pass an argument of zero. (See the Local Memory chapter.)

The routine creates a VM block and allocates a global memory block to go with it. It initializes the heap in the global block and marks the block as dirty. The LMem heap will begin with two LMem handles and a 64-byte heap; this will grow as necessary. The VM block will have a user ID of zero; you can change this if you wish. The routine returns the handle of the new VM block.

There are two other ways to create LMem blocks in a VM file; these ways give you more control of the block's initialization. You can allocate a VM block normally, lock that block, then get the handle of the associated memory block and initialize an LMem heap in it; or you can allocate an LMem heap normally, and attach that memory block to the VM file using VMAttach() . For more details on LMem heaps, see the Local Memory chapter.

To free a VM block, call VMFree() . This routine is passed two arguments: the VM file handle, and the VM block handle. The handle will immediately be freed, even if it is locked. Any associated memory will also be freed. If you want to keep the memory, detach the global memory block from the file (with VMDetach() ) before you free the block.

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Changing VM File Attributes | Next: 3.5 Attaching Memory Blocks


Attaching Memory Blocks

Virtual Memory: 3.5 Using Virtual Memory: Attaching Memory Blocks

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Creating and Freeing Blocks | Next: 3.6 Accessing and Altering VM Blocks
VMAttach(), VMDetach()

When you use VMAlloc() , the VM manager allocates a global memory block and attaches it to a VM block. However, sometimes you want to allocate the block yourself, or you may have an existing memory block which you want to copy into the VM file. To do this, you call the routine VMAttach() .

VMAttach() takes three arguments:

  • The VM file handle
    The handle of the file to attach.
  • The VM block handle
    If you pass a null handle, a free VM block will be allocated and attached to the global memory block. If you pass the handle of an existing block, the data in the VM block will be replaced with the contents of the global memory block.
  • The global memory handle
    The memory block must be swappable. After the block is attached, the VM manager may discard or free it, as with any other global blocks used by the VM file.

VMAttach() attaches the global memory block to the VM block. The VM Manager becomes the owner of the memory block. The next time the file is updated, the memory block will be copied to the file. VMAttach() returns the handle of the VM block. If it could not perform the attach, it returns a null handle and leaves the global memory block unchanged.

You can also detach the global memory block from a VM block. The routine VMDetach() disassociates a global memory block from its VM block. The routine takes three arguments: the VM file handle; the VM block handle; and the GeodeHandle of the geode which will be made the owner of the memory block. (Passing a null GeodeHandle will make the calling geode the block's owner.) The VM manager disassociates the memory block from the VM block, changes the memory block's owner, marks it "non-discardable," and returns its handle. If the VM block is not currently in memory, VMDetach() will automatically allocate a memory block, copy the VM block's data into it, and return the memory block's handle. If the VM block was dirty, the block will be updated to the file before it is detached. The next time the VM block is locked, a new global memory block will be allocated for it.

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Creating and Freeing Blocks | Next: 3.6 Accessing and Altering VM Blocks


Accessing and Altering VM Blocks

Virtual Memory: 3.6 Using Virtual Memory: Accessing and Altering VM Blocks

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Attaching Memory Blocks | Next: 3.7 VM Block Information
VMLock(), VMUnlock(), VMDirty(), VMFind(), VMModifyUserID(), VMPreserveBlocksHandle()

Once you have opened a VM file and allocated blocks, you will need to access blocks. The VM library provides many routines for doing this.

If you need to access the data in a VM file, you can use the routine VMLock() . This routine moves a VM block onto the global heap. It does this by allocating a global memory block (if the VM block is not already associated with a memory block), reallocating the global block if it had been discarded, locking the memory block on the global heap, and copying the VM block into the global block, if necessary. (It will copy the VM block to memory only if necessary, i.e. if the memory block is newly-allocated, or had been discarded and reallocated.) VMLock() takes three arguments: the handle of the VM file, the VMBlockHandle of the block to lock, and a pointer to a memHandle variable. It returns a pointer to the start of the block, and writes the global block's handle into the memHandle variable. You can now access the block the same way you would any other block, with one exception: When you are done with the block, you do not call MemUnlock() ; instead, call the routine VMUnlock() , passing it the handle of the global memory block (not the handle of the VM block). This will unlock the global block on the heap.

If you alter the global memory block, you will need to notify the VM manager of this so it will know to copy the changes back to the VM file. You do this by calling the routine VMDirty() . VMDirty() takes one argument, the handle of the global memory block ( not the VM block). It is important to dirty the block while it is still locked on the heap; as soon as you unlock a clean block, the VM manager may choose to discard it. Dirty blocks are copied back to the VM file when it is updated. Note that if an object in a VM block is marked dirty (via ObjMarkDirty() ), the block is automatically dirtied. Similarly, if you attach a global memory block to a VM block (via VMAttach() ), the VM block is automatically dirtied.

You can dynamically resize VM blocks. To do this, lock the VM block with VMLock() ; then resize the global memory block with MemReAlloc() . Be sure to mark the block dirty so the changes will be copied to the disk file. Note that although the global memory block will remain locked, it may move on the global heap when it is resized. You will therefore need to dereference the global memory handle (with MemDeref() ) before accessing the memory.

You can locate VM blocks by their user ID numbers. The routine VMFind() takes three arguments: the VM file handle, a VM block handle, and the user ID for which to look. The routine looks through the block table, starting with the handle after the one passed, until it finds a block with the specified user ID. If it does not find such a block, it returns a null handle; otherwise, it returns the block's VMBlockHandle. Thus, by passing in a block handle of zero, you will get the handle of the first block with the specified ID; by passing back in that block's handle, you will get the next block with that ID; and so on, until you get all the blocks (after which you will be returned a null handle).

You can change a block's user ID number by calling the routine VMModifyUserID() . This routine takes three arguments: the VM file handle, the VM block handle, and the new user ID number. Since user IDs are maintained in the block table, not in the blocks themselves, it doesn't matter whether the block is locked, or even whether it is associated with data in the file. (For example, a block allocated with a size of zero can have its user-ID changed.)

Ordinarily, the VM manager can free any unlocked, clean global block if the space is needed. However, you can instruct the VM manager not to free the global block associated with a specific block by calling the routine VMPreserveBlocksHandle() . The routine takes two arguments, namely the VM file handle and the VM block handle. It sees to it that the specified VM block will remain attached to the same global block until the VM block is specifically detached (or reattached).

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Attaching Memory Blocks | Next: 3.7 VM Block Information


VM Block Information

Virtual Memory: 3.7 Using Virtual Memory: VM Block Information

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Accessing and Altering VM Blocks | Next: 3.8 Updating and Saving Files
VMVMBlockToMemBlock(), VMMemBlockToVMBlock(), VMInfo()

Several utilities are provided to give you information about VM blocks.

If you know the handle of a VM block, you can find out the handle of the associated global block by calling the routine VMVMBlockToMemBlock() . This routine takes two arguments, namely the VM file handle and the VM block handle. It returns the global memory handle of the associated block; however, note the caveats regarding global handles in the above section. If the VM block is not currently associated with a global memory block, the routine will allocate a memory block, copy the VM block into it, and return its handle. If the VM handle is not associated with any data in the file and is not attached to a global memory block, VMVMBlockToMemBlock() returns a null handle.

Conversely, if you know the handle of a global memory block and want to find out the VM file and block to which it is attached, call the routine VMMemBlockToVMBlock() . This routine takes two arguments: the global memory handle, and a pointer to a VMFileHandle variable. It returns the VM block handle of the associated VM block, and writes the handle of the VM file to the address passed. If the global memory block is not attached to a VM file, it returns null handles.

The Boolean routine VMInfo() is an omnibus information routine. It takes three arguments: the handle of a VM file, the handle of a VM block, and a pointer to a VMInfoStruct structure (described below in VMInfoStruct ). If the VM block is free, out of range, or otherwise invalid, it returns false ; otherwise, it returns true (i.e. non-zero) and fills in the fields of the VMInfoStruct .

Code Display 18-1 VMInfoStruct

/* This is the definition of the VMInfoStruct. A pointer to a VMInfoStruct is
 * passed to the routine VMInfo(). The routine fills in the structure's fields.
 */
typedef struct {
    MemHandle		mh;	/* Null handle returned if no block is attached */
    word		size;	/* Size of VM block in bytes */
    word		userID;	/* User ID (or zero if no user ID was specified) */
} VMInfoStruct;
| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Accessing and Altering VM Blocks | Next: 3.8 Updating and Saving Files


Updating and Saving Files

Virtual Memory: 3.8 Using Virtual Memory: Updating and Saving Files

| Up: GEOS SDK TechDocs | Up | Prev: 3.7 VM Block Information | Next: 3.9 Closing Files
VMUpdate(), VMSave(), VMSaveAs(), VMRevert(), VMGetDirtyState() VMSave()

When you dirty a memory block, that action notifies the VM manager that the block will need to be written back to the file. If the attribute VMA_SYNC_UPDATE is off , the VM manager will try to update the block to the disk file as soon as the block is unlocked, and will then mark the block as clean . However, if the flag is on , the manager does not write the block until it is specifically told to update the file. At this point, it copies any dirty blocks back over their attached VM blocks, then marks all blocks as clean . If you use the document control objects, they will take care of updating and saving the file. However, you may need to call the updating routines specifically.

The routine VMUpdate() instructs the VM manager to write all dirty blocks to the disk. It takes one argument, the VM file handle (which is overridden if a thread file has been set). It returns zero if the update proceeded normally; otherwise, it returns either one of the FileErrors or one of the three VMUpdate() status codes:

VM_UPDATE_NOTHING_DIRTY
All blocks were clean, so the VM disk file was not changed.
VM_UPDATE_INSUFFICIENT_DISK_SPACE
The file has grown since the last update, and there is not enough room on the disk to accommodate it.
VM_UPDATE_BLOCK_WAS_LOCKED
Some of the VM blocks were locked by another thread, so they could not be updated to the disk.

VMUpdate() is optimized for updating clean files; thus, it costs very little time to call VMUpdate() when you are not sure if the file is dirty. If a file is auto-saved, VMUpdate() is used.

A VM file can maintain backup copies of updated blocks. If so, updating the file will write changes to the disk, but will not alter those backup blocks. To finalize the changes, call the routine VMSave() . This routine updates the file, then deletes all the backup blocks and compacts the file. If the file does not have backup capability, VMSave() acts the same as VMUpdate() .

If a file has the backup capability, you cannot directly access the backup blocks. However, you can instruct the VM manager to restore the file to its last-saved state. The command VMRevert() causes the VM manager to check the VM file for blocks which have backups. It then deletes the non-backup block, and changes the backup block into a regular block. It also discards all blocks in memory that were attached to the blocks which just reverted. The file will then be in its condition as of the last time it was saved. The routine may not be used on files which do not have the flag VMA_BACKUP set.

You can save a file under a new name with the routine VMSaveAs() . If the file has backup capability, the old file will be restored to its last-saved condition (as if VMRevert() had been called); otherwise, the old file will be left in the file's current state. The routine is passed the name of the new file. VMSaveAs() copies all the blocks from the old file to the new one. If a block has a backup copy, the more recent version is copied. The new file will thus have the file in its current state; block handles will be preserved. After the new file has been created, if the file has backup-capability, VMSaveAs() reverts the original file to its last-saved state. It then closes the old file and returns the handle of the new file.

If you manage VM files with the document control objects, you generally don't have to call the update or save routines. The document control objects will set up a file menu with appropriate commands ("Save," "Save As," etc.), and will call the appropriate routines whenever the user chooses a command.

If you need to find out whether a file is dirty, call the routine VMGetDirtyState() . This routine returns a two-byte value. The more significant byte is non-zero if any blocks have been dirtied since the last update or auto-save. The less significant byte is non-zero if any blocks have been dirtied since the last save, save-as, or revert action. If the file does not have backup-capability, both bytes will always be equal. Note that VMUpdate() is optimized for clean files, so it is generally faster to call VMUpdate() even if the file might be clean, rather than checking the dirty-state with VMGetDirtyState() .

| Up: GEOS SDK TechDocs | Up | Prev: 3.7 VM Block Information | Next: 3.9 Closing Files


Closing Files

Virtual Memory: 3.9 Using Virtual Memory: Closing Files

| Up: GEOS SDK TechDocs | Up | Prev: 3.8 Updating and Saving Files | Next: 3.10 The VM File's Map Block
VMClose()

When you are done with a VM file for the time being, you should close it with VMClose() . This routine updates all the dirty blocks, frees all the global memory blocks attached to the file, and closes the file (thus freeing its handle). The routine is passed two arguments. The first is the handle of the file to close. The second is a flag, noErrorFlag . If this flag is FILE_NO_ERROR, VMClose() will not return error conditions; if it could not successfully close the file, it will fatal-error.

If noErrorFlag is false (i.e., zero), VMClose() will update the file and close it. If the file could not be updated, it will return an error condition. Be warned, however, that if for some reason VMClose() could not finish updating a file (for example, because the disk ran out of space), VMClose() will return an error message, but will close the file and free the memory anyway. Thus, the most recent changes will be lost. For this reason, it is usually safer to first update the file (and handle any error messages returned) and then close it.

When GEOS shuts down, all files are closed. When it restarts, you can open the files manually.

If the file is backup-enabled, the backup blocks will be preserved until the file is next opened. That means, for example, that the next time you open the file, you can continue working on it normally, or you can immediately revert it to its condition as of the last time it was saved (in the earlier GEOS session).

| Up: GEOS SDK TechDocs | Up | Prev: 3.8 Updating and Saving Files | Next: 3.10 The VM File's Map Block


The VM File's Map Block

Virtual Memory: 3.10 Using Virtual Memory: The VM File's Map Block

| Up: GEOS SDK TechDocs | Up | Prev: 3.9 Closing Files | Next: 3.11 File-Access Synchronization
VMGetMapBlock(), VMSetMapBlock()

When they're created, the blocks of a VM file are in no particular order. You will need some way to keep track of VM block handles so you can find each block when you need it. The usual way to do this is with a map block .

A map block is just like any other VM block. Like other blocks, it can be a standard block, an LMem heap, etc. It is different in only one way: the VM manager keeps track of its handle. By calling the routine VMGetMapBlock() , you can get the VM handle of the map block. You can then look inside the map block to get information about the other blocks.

Note that the structure of the VM map block is entirely the concern of the creating geode. The VM manager neither requires nor specifies any internal structure or information content.

To create a map block, allocate a VM block through any of the normal techniques, then pass its VM handle as an argument to VMSetMapBlock() . That block will be the new map block. If there already was a map block, the old block will become an ordinary VM block.

In addition to setting a map block, you can set a map database item with the command DBSetMap() . For details, see the Database chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 3.9 Closing Files | Next: 3.11 File-Access Synchronization


File-Access Synchronization

Virtual Memory: 3.11 Using Virtual Memory: File-Access Synchronization

| Up: GEOS SDK TechDocs | Up | Prev: 3.10 The VM File's Map Block | Next: 3.12 Other VM Utilities
VMGrabExclusive(), VMReleaseExclusive()

Sometimes several different geodes will need access to the same VM file. Generally, these will be several different copies of the same application, perhaps running on different machines on a network. GEOS provides three different ways shared-access can be handled.

A VM file can be one of three different types: standard, "public," and "shared-multiple." By default, all new VM files are standard. The file's type is one of its extended attributes, and can be changed with the routine FileSetHandleExtendedAttributes() (see the File System chapter). The document control automatically lets the user select what kind of file to create, and changes its type accordingly. (See the Document Objects chapter.)

Only one geode may write to a standard GEOS VM file at a time. If a geode has the file open for read/write access, no other geode will be allowed to open that file. If a geode has the file open for read-only access, other geodes are allowed to open it for read-only access, but not for read-write access. If a file is opened for read-only access, blocks cannot be dirtied or updated. If a geode tries to open a file for writing when the file is already open, or if the geode tries to open it for reading when the file has already been opened for writing, VMOpen() will return an error.

In general, when a file is opened, it is by default opened for read-write access. For example, the document control objects present a dialog box which lets the file be opened for read-only access, but has this option initially turned off. However, some files are used mainly for reference and are infrequently changed. For example, a company might keep a client address book on a network drive. Most of the time, people would just read this file; the file would only occasionally be changed. For this reason, GEOS lets you declare VM file as "public." Public files are, by default, opened for read-only access. In all other respects the file is the same as a standard GEOS VM file; it can be opened by several readers at a time, but by only one geode at a time if the geode will be writing.

Sometimes several geodes will need to be able to write to a file at once. For example, a company might have a large customer database, and several users might be writing records to the database at the same time. For this reason, GEOS lets you create "shared-multiple" files. Several geodes can have a "shared-multiple" file open at once. However, a geode cannot access the file whenever it wants. Instead, it must get the file's semaphore to access the file's data. When it needs to access the file, it calls VMGrabExclusive() . This routine takes four arguments:

  • The handle of the VM file
  • A timeout value
    If a timeout value is passed, VMGrabExclusive() will give up trying to get the semaphore after a specified number of seconds has passed. If a timeout value of zero is passed, VMGrabExclusive() will block until it can get the file's semaphore.
  • A member of the VMOperation enumerated type
    This specifies the kind of operation to be performed on the locked file. The VMOperation values are described below.
  • A pointer to a word-length variable.
    If this call to VMGrabExclusive() fails and times out, the operation currently being performed will be written here.

The routine returns a member of the VMStartExclusiveReturnValue enumerated type. The following return values are possible:

VMSERV_NO_CHANGES
No other thread has changed this file since the last time this thread had access to the file.
VMSERV_CHANGES
The file may have been altered since the last time this thread had access to it; the thread should take appropriate actions (such as re-reading any cached data).
VMSERV_TIMEOUT
This call to VMGrabExclusive() failed and timed out without getting access to the file.

When a geode calls VMGrabExclusive() , it must pass a member of the VMOperation enumerated type. Most of the values are used internally by the system; while a geode should never pass these values, they may be returned by VMGrabExclusive() if the call times out. The following values are defined in vm.h :

VMO_READ
This indicates that the geode will not change the file during this access. This lets the kernel perform some optimizations.
VMO_INTERNAL
VMO_SAVE
VMO_SAVE_AS
VMO_REVERT
VMO_UPDATE
These values are set only by the kernel. Applications should never pass them.
VMO_WRITE
This indicates that the geode may write to the file during this access.

The application may also pass any value between VMO_FIRST_APP_CODE and 0xffff. The kernel treats all these values as synonymous with VMO_WRITE; however, the application may choose to associate meanings with numbers in this range (perhaps by defining an enumerated type whose starting value is VMO_FIRST_APP_CODE).

When a thread is done accessing a file, it should release its exclusive access by calling VMReleaseExclusive() . The routine takes one argument, namely the file handle. It does not return anything.

| Up: GEOS SDK TechDocs | Up | Prev: 3.10 The VM File's Map Block | Next: 3.12 Other VM Utilities


Other VM Utilities

Virtual Memory: 3.12 Using Virtual Memory: Other VM Utilities

| Up: GEOS SDK TechDocs | Up | Prev: 3.11 File-Access Synchronization | Next: 4 VM Chains
VMCopyVMBlock(), VMSetReloc()

If you would like to duplicate a VM block, or copy it to another file, call VMCopyVMBlock() . This routine is passed three arguments:

  • The VMFileHandle of the file containing the source block.
  • The VMBlockHandle of the source block.
  • The VMFileHandle of the destination file (which may be the same as the source file).

The routine makes a complete duplicate of the source block, copying it to the destination file. It returns the VMBlockHandle of the duplicate block. Note that the duplicate block will almost certainly have a different block handle than the source block. If the block contains a copy of its own handle, you should update it accordingly.

Sometimes you will need to perform special actions whenever loading a block into memory or writing it to the disk. For example, you may store data in a compressed format on the disk, and need to expand it when it's loaded into memory. For this reason, you can set a relocation routine. This routine will be called whenever the following situations occur:

  • A VM block has just been copied from the disk into memory (routine will be passed the flag VMRT_RELOCATE_AFTER_READ).
  • A block is about to be written from memory to the disk (routine is passed VMRT_UNRELOCATE_BEFORE_WRITE).
  • A block in memory has just been written to the disk, but is not being discarded (routine is passed VMRT_RELOCATE_AFTER_WRITE).
  • A VM block has just been loaded from a resource (routine is passed VMRT_RELOCATE_FROM_RESOURCE). This is called by the relocating object, not by the VM manager.
  • A VM block is about to be written to a resource (routine is passed VMRT_UNRELOCATE_FROM_RESOURCE). This is called by the unrelocating object, not by the VM manager.

Using the routine VMSetReloc() , you can instruct the VM manager to call your relocation routine whenever appropriate.

| Up: GEOS SDK TechDocs | Up | Prev: 3.11 File-Access Synchronization | Next: 4 VM Chains


VM Chains

Virtual Memory: 4 VM Chains

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Using Virtual Memory | Next: 5 Huge Arrays

A VM file is just a collection of blocks. These blocks are not kept in any particular order. If the application wants to keep the blocks in some kind of order, it must set up some mechanism to do this.

One common mechanism is the VM chain. This is simply a way of arranging blocks in a sequence, in which each block contains the handle of the next block. GEOS has a standard format for VM chains and provides utility routines which work with chains of this format.

In general usage, a "chain" is a simple tree in which each block has a link to at most one other block. However, the GEOS VM chain can also contain special "tree blocks," which may have links to any number of child blocks. By using these blocks, an application can set up VM trees of unlimited complexity.

Structure of a VM Chain

VM Chain Utilities

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Using Virtual Memory | Next: 5 Huge Arrays


Structure of a VM Chain

Virtual Memory: 4.1 VM Chains: Structure of a VM Chain

| Up: GEOS SDK TechDocs | Up | Prev: 4 VM Chains | Next: 4.2 VM Chain Utilities

A VM chain is composed of two kinds of blocks: chain blocks (which are linked to at most one other block), and tree blocks (which may be linked to any number of other blocks). One block is the head of the chain; chain utility routines can be passed the handle of this block, and they will act on all the blocks in the chain. If a block is a "leaf" block, it should contain a null handle. An example of a VM chain with tree blocks is shown in the figure below.

Be warned that a VM chain must not contain any circuits. That is, by following links, you should not be able to go from any block back to itself; and there should not be two different routes from any one block to any other. If you create such a VM chain and pass it to a chain utility, the results are undefined. It is your responsibility to make sure no loops occur.

A VM chain block is the same as any other VM block, with one exception: The block must begin with a VMChainLink structure. This structure contains a single data field, VMC_next , which is the handle of the next block in the chain. If the block is in a chain but has no next link, VMC_next is a null handle. This means, for example, that LMem heaps cannot belong to a VM chain (since LMem heaps must begin with an LMemHeader structure).

In addition to chain blocks, a VM chain may contain a tree block. A tree block may have several links to blocks. A tree block begins with a VMChainTree structure. This structure has three fields:

VMCT_meta
This is a VMChainLink structure. Every block in a VM chain, including a tree block, must begin with such a structure. However, to indicate that this is a tree block, the VMC_next field must be set to the special value VM_CHAIN_TREE.
VMCT_offset
This is the offset within the block to the first link. All data in the tree block must be placed between the VMChainTree structure and the first link. If you do not put data in this block, set this field to sizeof(VMChainTree) .
VMCT_count
This is the number of links in the tree block.

Any of the links may be a null handle. To delete the last link in the block, just decrement VMCT_count (and, if you wish, resize the block). To delete a link in the midst of a block, just change the link to a null handle without decrementing VMCT_count . To add a new link to a VM tree block, you can either add the handle after the last link and increment VMCT_count ; or you can replace a null handle (if there are any) with the new handle, without changing VMCT_count .

| Up: GEOS SDK TechDocs | Up | Prev: 4 VM Chains | Next: 4.2 VM Chain Utilities


VM Chain Utilities

Virtual Memory: 4.2 VM Chains: VM Chain Utilities

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Structure of a VM Chain | Next: 5 Huge Arrays
VMChainHandle, VMFreeVMChain(), VMCompareVMChains(), VMCopyVMChain(), VMCHAIN_IS_DBITEM(), VMCHAIN_GET_VM_BLOCK(), VMCHAIN_MAKE_FROM_VM_BLOCK()

Several utilities are provided for working with VM chains. They allow you to compare, free, or copy entire VM chains with a single command.

For convenience, all of these routines can work on either a VM chain or a database item. This is useful, because sometimes you will want to use the utility on a data structure without knowing in advance how large it will be. This way, if there is a small amount of data, you can store it in a DB item; if there is a lot, you can store it in a VM chain of any length. Whichever way you store the data, you can use the same chain utilities to manipulate it.

The routines all take, as an argument, a VMChain which is simply a dword. If the low word is null, the VMChain contains the VM block handle to a VM chain. If the low word is not null, the VMChain contains a DBGroupAndItem . Note that none of the blocks in the VM chain need be locked when the routine is called; the routine will lock the blocks as necessary, and unlock them when finished. Similarly, a DB item need not be locked before being passed to one of these routines. However, the VM file containing the structure must be open.

If you want to free an entire VM chain at once, call the routine VMFreeVMChain() . This routine takes two arguments, namely the VM file handle and a VMChain . It frees every block in the VM chain, and returns nothing.

You can compare two different VM chains, whether in the same or in different files, by calling VMCompareVMChains() . This Boolean routine takes four arguments, namely the handles of the two files (which may be the same) and the VMChain s ( i.e. , the block handles to the two chains). The geode must have both files open when it calls this routine. The routine returns true ( i.e. , non-zero) if the two chains are identical ( i.e. , the trees have the same structures and all data bytes are identical). Note that if the chains contain tree blocks which are identical except in the order of their links, the chains will not be considered identical and the routine will return false ( i.e. , zero).

You can make a copy of a VM chain with the routine VMCopyVMChain() . This routine copies the entire chain to a specified file, which may be the same as the source file. The blocks in the duplicate chain will have the same user ID numbers as the corresponding original blocks. The routine takes three arguments: the handle of the source file, the VMChain of the source chain or item, and the handle of the destination file. It copies the chain or item and returns the VMChain of the duplicate structure. As noted, if the low word of the VMChain is null, the high word will be the VM handle of the head of the chain. The geode must have both files open when it calls this routine.

Several utilities are provided for working with VMChain types. They are implemented as preprocessor macros, so they are very fast. The macro VMCHAIN_IS_DBITEM() is passed a VMChain and returns non-zero if the VMChain identifies a DB item ( i.e. , if the low word is non-zero) or zero if the VMChain identifies a VM chain. The macro VMCHAIN_GET_VM_BLOCK () is passed a VMChain containing the block handle to a VM chain. It returns the high word, i.e., the chain's VM block handle. Finally, the macro VMCHAIN_MAKE_FROM_VM_BLOCK() takes a VMBlockHandle value and casts it to type VMChain .

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Structure of a VM Chain | Next: 5 Huge Arrays


Huge Arrays

Virtual Memory: 5 Huge Arrays

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 VM Chains | Next: 5.1 Structure of a Huge Array

Sometimes a geode needs to work with an array that can get very large. Chunk arrays are very convenient, but they are limited to the size of an LMem heap, which is slightly less than 64K; furthermore, their performance begins to degrade when they get larger than 6K. Similarly, if a geode uses raw memory for an array, it is limited to the maximum size of a global memory block, again 64K.

For this reason, GEOS provides the Huge Array library. A Huge Array is stored in a VM file. All the elements are stored in VM blocks, as is the directory information. The application can specify an array element by its index, and the Huge Array routine will lock the block containing that element and return its address. Array indices are dword-sized, meaning a Huge Array can have up to 232 elements. Since elements are stored in VM blocks, each element has a maximum size of 64K; however, the size of the entire array is limited only by the amount of disk space available. The blocks in a Huge Array are linked together in a VM chain, so the VM chain utilities can be used to duplicate, free, and compare Huge Arrays.

There are a couple of disadvantages to using Huge Arrays. The main disadvantage is that it takes longer to access an element: the routine has to lock the directory block, look up the index to find out which block contains the element, lock that block, calculate the offset into that block for the element, and return its address. (However, elements are consecutive within blocks; thus, you can often make many array accesses with a single Huge Array lookup command.) There is also a certain amount of memory overhead for Huge Arrays. Thus, if you are confident that the array size will be small enough for a single block, you are generally better off with a Chunk Array (see the Local Memory chapter).

Huge arrays may have fixed-size or variable-sized elements. If elements are variable-sized, there is a slight increase in memory overhead, but no significant slowdown in data-access time.

Structure of a Huge Array

Basic Huge Array Routines

Huge Array Utilities

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 VM Chains | Next: 5.1 Structure of a Huge Array


Structure of a Huge Array

Virtual Memory: 5.1 Huge Arrays: Structure of a Huge Array

| Up: GEOS SDK TechDocs | Up | Prev: 5 Huge Arrays | Next: 5.2 Basic Huge Array Routines

The huge array has two different type of blocks: a single directory block, and zero or more data blocks. The blocks are linked together in a simple (i.e., non-branching) VM chain. The directory block is the head of the chain. A Huge Array is identified by the handles of the VM file containing the array and the directory block at the head of the array.

The directory block is a special kind of LMem block. It contains a header structure of type HugeArrayDirectory (which begins with an LMemBlockHeader structure), followed by an optional fixed data area, which is followed by a chunk array. The chunk array is an array of HugeArrayDirEntry structures. There is one such structure for each data block; the structure contains the handle of the data block, the size of the block, and the index number of the last element in the block.

Each data block is also a special LMem block. It contains a HugeArrayBlock structure (which begins with an LMemBlockHeader structure) and is followed by a chunk array of elements. If the Huge Array has variable-sized elements, so will each data block's chunk array.

When you want to look up a specific element, the Huge Array routines lock the directory block. They then read through the directory chunk array until they find the block which contains the specified element. At this point, the routine knows both which data block contains the element and which element it is in that block's chunk array. (For example, if you look up element 1,000, the Huge Array routine might find out that block x ends with element 950 and block y ends with element 1,020. The routine thus knows that element 1,000 in the Huge Array is in the chunk array in block y , and its element number is 50 in that block's array.)

The routine then unlocks the directory block, and locks the data block containing that element. It returns a pointer to that element. It also returns the number of elements occurring before and after that element in that chunk array. The calling geode can access all the elements in that block without further calls to Huge Array routines.

When you insert or delete an element, the Huge Array routines look up the element index as described above. They then call the appropriate chunk array routine to insert or delete the element in that data block. They then go through the directory and adjust the element numbers throughout. If inserting an element in a data block would bring the block's size above some system-defined threshold, the Huge Array routine automatically divides the data block.

When the VM routines resize an element block, they automatically make the block larger than necessary. This leaves extra space for future insertions in that block, so the block won't have to be resized the next time an element is added. This improves efficiency, since you may often be adding several elements to the same block. However, this also means that most Huge Arrays have a fair amount of unused space. If you won't be adding elements to a Huge Array for a while, you should compact the Huge Array with HugeArrayCompressBlocks (see Huge Array Utilities ).

Ordinarily, VM Chains may not contain LMem heaps. Huge Arrays are an exception. The reason LMem blocks cannot belong to VM chains is simple. Each block in a VM chain begins with the handle of the next block in the chain (or VM_CHAIN_TREE if it is a tree block). However, each LMem heap has to begin with an LMemBlockHeader structure, the first word of which is the global handle of the memory block. In order for these blocks to serve as both, special actions have to be taken. When a Huge Array block is unlocked, its first word is the handle of the next block in the chain. It is thus a VM chain block and not a valid LMem heap. When the Huge Array routine needs to access the block, it locks the block and copies the block's global handle into the first word, storing the chain link in another word. This makes the block a valid LMem heap, but it (temporarily) invalidates the VM chain.

Although VM chain utilities work on Huge Arrays, you must be sure that the Huge Array is a valid VM chain when you call the utility. In practice, this means you cannot use a VM chain utility when any block in the chain is locked or while any thread might be accessing the array. If more than one thread might be using the array, you should not use the chain utilities.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Huge Arrays | Next: 5.2 Basic Huge Array Routines


Basic Huge Array Routines

Virtual Memory: 5.2 Huge Arrays: Basic Huge Array Routines

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Structure of a Huge Array | Next: 5.3 Huge Array Utilities
HugeArrayCreate(), HugeArrayDestroy(), HugeArrayResize(), HugeArrayLock(), HugeArrayUnlock(), HugeArrayDirty(), HugeArrayAppend(), Huge ArrayInsert(), HugeArrayReplace(), HugeArrayDelete(), HugeArrayGetCount()

GEOS provides many routines for dealing with Huge Arrays. The basic routines are described in this section. Some additional routines which can help optimize your code are described in Huge Array Utilities .

Note that you should never have more than one block of a Huge Array locked at a time. Furthermore, when you call any routine in this section (except HugeArrayUnlock() , HugeArrayDirty() , and HugeArrayGetCount() ), you should not have any blocks locked. The next section contains several routines which may be used while a block is locked. Also, if you use any VM chain routines on a Huge Array, you should make sure that no blocks are locked.

To create a Huge Array, call HugeArrayCreate() . This routine allocates a directory block and initializes it. The routine takes three arguments:

  • The handle of the VM file in which to create the huge array. This argument is ignored if a default VM file has been set for this thread.
  • The size of each element. A size of zero indicates that arguments will be variable-sized.
  • The size to allocate for the directory block's header. If you want to have a fixed data area between the HugeArrayDirectory structure and the chunk array of directory entries, you can pass an argument here. The size must be at least as large as sizeof(HugeArrayDirectory) . Alternatively, you can pass an argument of zero, indicating that there will be no extra data area, and the default header size should be used.

HugeArrayCreate() returns the VM handle of the directory block. This is also the handle of the Huge Array itself; you will pass it as an argument to most of the other Huge Array routines.

When you are done with a Huge Array, destroy it by calling HugeArrayDestroy() . This routine frees all of the blocks in the Huge Array. It takes two arguments, namely the global handle of the VM file and the VM handle of the Huge Array. It does not return anything. You should make sure that none of the data blocks are locked when you call this since all of the VM chain links must be valid when this routine is called.

To access an element in the array, call HugeArrayLock() . This routine takes five arguments:

  • The global handle of the VM file which contains the Huge Array.
  • The VM handle of the Huge Array.
  • The (32-bit) index number of the element.
  • A pointer to a pointer to an element.
  • A pointer to a word-sized variable.

The routine figures out which block has the element specified (as described above). It then locks that block. It writes a pointer to that element in the location passed, and writes the size of the element in the variable pointed to by the other pointer (this is useful if the Huge Array has variable-sized elements). It returns a dword. The more significant word is the number of consecutive elements in that block, starting with the pointer returned; the less significant word is the number of elements in that block before (and including) the element specified. For example, suppose you lock element 1,000. Let's assume that this element is in block x ; block x has 50 elements, and element 1,000 in the huge array is the 30th element in block x . The routine would write a pointer to element 1,000 in the pointer passed; it would then return the dword 0x0015001e. The upper word (0x0015) indicates that element 1,000 is the first of the last 21 consecutive elements in the block; the lower word (0x001e) indicates that the element is the last of the first 30 consecutive elements. You thus know which other elements in the Huge Array are in this block and can be examined without further calls to HugeArrayLock() .

When you are done examining a block in the Huge Array, you should unlock the block with HugeArrayUnlock() . This routine takes only one argument, namely a pointer to any element in that block. It does not return anything. Note that you don't have to pass it the same pointer as the one you were given by HugeArrayLock() . Thus, you can get a pointer, increment it to work your way through the block, and unlock the block with whatever address you end up with.

Whenever you insert or delete an element, the Huge Array routines automatically mark the relevant blocks as dirty. However, if you change an element, you need to dirty the block yourself or the changes won't be saved to the disk. To do this, call the routine HugeArrayDirty() . This routine takes one argument, namely a pointer to an element in a Huge Array. It dirties the data block containing that element. Naturally, if you change several elements in a block, you only need to call this routine once.

If you want to add elements to the end of a Huge Array, call HugeArrayAppend() . If elements are of uniform size, you can add up to 216 elements with one call to this routine. You can also pass a pointer to a template element; it will copy the template into each new element it creates. This routine takes four arguments:

  • The global handle of the VM file containing the Huge Array. This argument is ignored if a default file has been set.
  • The VM handle of the Huge Array.
  • The number of elements to append, if the elements are of uniform size; or the size of the element to append, if elements are of variable size.
  • An optional pointer to a buffer containing initialization data for the new element(s).

The routine returns the index of the new element. If several elements were created, it returns the index of the first of the new elements. This index is a dword.

You can also insert one or more elements in the middle of a Huge Array. To do this, call the routine HugeArrayInsert() . As with HugeArrayAppend() , you can insert many uniform-sized elements at once, and you can pass a pointer to a template to initialize elements with. The routine takes five arguments:

  • The global handle of the VM file containing the Huge Array. This argument is ignored if a default file has been set.
  • The VM handle of the Huge Array.
  • The number of elements to insert, if the elements are of uniform size; or the size of the element to insert, if elements are of variable size.
  • The index of the first of the new elements (the element that currently has that index will follow the inserted elements).
  • A pointer to a template element to copy into each new element (pass a null pointer to leave the elements uninitialized).

It returns the index of the first of the new elements. Ordinarily, this will be the index you passed it; however, if you pass an index which is out of bounds, the new elements will be put at the end of the array, and the index returned will thus be different.

To delete elements in a Huge Array, call HugeArrayDelete() . You can delete many elements (whether uniform-sized or variable-sized) with one call to HugeArrayDelete() . The routine takes four arguments:

  • The global handle of the VM file containing the Huge Array. This argument is ignored if a default file has been set.
  • The VM handle of the Huge Array.
  • The number of elements to delete.
  • The index of the first element to delete.

You can resize one of the elements by calling HugeArrayResize() . If you make the element smaller, then data at the end will be truncated. If you make the element larger, then the new data will be zero-initialized.

You can erase or replace the data in one or more elements with a call to HugeArrayReplace() . This is also the only way to resize a variable-sized element. You can pass a pointer to a template to copy into the element or elements, or you can have the element(s) initialized with null bytes. The routine takes five arguments:

  • The global handle of the VM file containing the Huge Array. This argument is ignored if a default file has been set.
  • The VM handle of the Huge Array.
  • The number of elements to replace, if the elements are uniform-sized; or the new size for the element, if elements are variable-sized.
  • The index of the first of the elements to replace.
  • A pointer to a template element to copy into each new element (pass a null pointer to fill the elements with null bytes).

You can find out the number of elements in a Huge Array with a call to HugeArrayGetCount() . This routine takes two arguments, namely the file handle and the handle of the Huge Array. The routine returns the number of elements in the array. Since array indices begin at zero, if HugeArrayGetCount() returns x, the last element in the array has index x-1.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Structure of a Huge Array | Next: 5.3 Huge Array Utilities


Huge Array Utilities

Virtual Memory: 5.3 Huge Arrays: Huge Array Utilities

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Basic Huge Array Routines
HugeArrayNext(), HugeArrayPrev(), HugeArrayExpand(), HugeArrayContract(), HugeArrayEnum(), HugeArrayCompressBlocks(), ECCheckHugeArray()

The routines in the other section may be all you will need for using Huge Arrays. However, you can improve access time by taking advantage of the structure of a Huge Array. As noted above, you can use any of the VM Chain routines on a Huge Array, as long as none of the blocks are locked.

If you have been accessing an element in a Huge Array and you want to move on to the next one, you can call the routine HugeArrayNext(). The routine takes a pointer to a pointer to the element. It changes that pointer to point to the next element in the array, which may be in a different block. If the routine changes blocks, it will unlock the old block and lock the next one. It returns the number of consecutive elements starting with the element we just advanced to. If we were at the last element in the Huge Array, it returns zero and the pointer will point to the last element.

If you want to move to the previous element instead of the next one, call HugeArrayPrev(). It also takes a pointer to a pointer to an element. It changes that pointer to a pointer to the previous element; if this means switching blocks, it unlocks the current block and locks the previous one. It returns the number of elements which come before the newly-locked one in its block, counting the newly-locked element. If the pointer was pointing to the first element in the Huge Array, it returns zero and the pointer will point to the first element.

If you have a block of the Huge Array locked and you want to insert an element or elements at an address in that block, call the routine HugeArrayExpand() . It takes three arguments:

  • A pointer to a pointer to the location where we you want to insert the elements. This element must be in a locked block.
  • The number of elements to insert, if the elements are uniform-sized; or the size of the element to insert, if elements are variable-sized.
  • A pointer to a template to copy into each new element. If you pass a null pointer, elements will not be initialized.

The routine inserts the elements, dirtying any appropriate blocks. It writes a pointer to the first new element into the pointer passed. Since the data block may be resized or divided to accommodate the request, this address may be different from the one you pass. The routine returns the number of consecutive elements beginning with the one whose address was written. If the new element is in a different block from the address passed, the old block will be unlocked, and the new one will be locked.

If you have a block of a Huge Array locked and you want to delete one or more elements starting with one within the block, you can call HugeArrayContract() . This routine takes two arguments:

  • A pointer to the first element to be deleted. The block with that element must be locked.
  • The number of elements to delete. Not all of these elements need be in the same block as the first.

The routine deletes the elements, dirtying any appropriate blocks. It then changes the pointer to point to the first element after the deleted ones. If this element is in a different block, it will unlock the old block and lock the new one. It returns the number of consecutive elements following the one whose address was written.

You may wish to perform the same operation on a number of consecutive elements of a Huge Array. HugeArrayEnum() is a routine which lets you do this efficiently. HugeArrayEnum() takes six arguments:

  • The VMFileHandle of the VM file containing the Huge Array.
  • The VMBlockHandle of the Huge Array.
  • The index of the first element to be enumerated (remember, the first element has index zero).
  • The number of elements to enumerate, or -1 to enumerate to the end of the array.
  • A void pointer; this pointer will be passed to the callback routine.
  • A void pointer to a Boolean callback routine. This callback routine should take two arguments: a void pointer to an element and the void pointerthat was passed to HugeArrayEnum() . The callback routine can abort the enumeration by returning true ( i.e. , non-zero).

HugeArrayEnum() calls the callback routine for every element, in order, from the first element. It passes the callback a pointer to the element and the pointer passed to HugeArrayEnum() . The callback routine may not do anything which would invalidate any pointers to the Huge Array; for example, it may not allocate, delete, or resize any of the elements. The callback routine should restrict itself to examining elements and altering them ( without resizing them). The callback routine can abort the enumeration by returning true ( i.e. , non-zero); if it does so, HugeArrayEnum() will return true . If HugeArrayEnum() finishes the enumeration without aborting, it returns false ( i.e. , zero).

If the count is large enough to take HugeArrayEnum() past the end of the array, HugeArrayEnum() will simply enumerate up to the last element, then stop. For example, if you pass a start index of 9,000 and a count of 2,000, but the Huge Array has only 10,000 elements, HugeArrayEnum() will enumerate up through the last element (with index 9,999) then stop. However, the starting index must be the index of an element in the Huge Array. You can also pass a count of -1, indicatingg that HugeArrayEnum() should enumerate through the last element of the array. Therefore, to enumerate the entire array, pass a starting element of zero and a count of -1.

As noted above, most Huge Arrays contain a fair amount of unused space. This makes it much faster to add and remove elements, since blocks don't need to be resized very often. However, if you have a Huge Array that is not frequently changed, this is an inefficient use of space. You can free this space by calling HugeArrayCompressBlocks() . This routine is passed two arguments: the handle of the VM file, and the VMBlockHandle of the Huge Array. The routine does not change any element in the Huge Array; it simply resizes the directory and data blocks to be no larger than necessary to hold their elements. The routine does not return anything.

If you want to verify (in error-checking code) that a given VM block is the directory block of a Huge Array, you can call ECCheckHugeArray() . This routine is passed the VM file and block handles of the block in question. If the block is the directory block of a Huge Array, the routine returns normally; otherwise it causes a fatal error. The routine should not, therefore, be used in non-EC code.

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Basic Huge Array Routines


File Manipulation

GEOS SDK Documentation: Files

| Up: GEOS SDK TechDocs

The following documents hold information about GEOS file manipulations.

File System
Kernel routines used to examine and alter directories and files.
Virtual Memory
Use VM routines to write and organized data in files.
Nokia 9000i Communicator UI
The Nokia 9000i Communicator has specialized classes for presenting the user with directory and file choices. Before using a class described in one of the documents below, if you're writing a Communicator application, you should check this chapter.
Documents
Object classes used to handle document UI and synchronization.
GenFileSelectorClass
An object class which allows the user to browse the directory tree and choose a file.
| Up: GEOS SDK TechDocs


Documents

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Document Control Overview
| One-page Version of this Chapter for Easy Printing

The GEOS document control objects let the programmer ignore most of the details of opening, closing, and saving files. The programmer just specifies the characteristics the application will expect of its documents. The document control presents all dialog boxes and notices to the user, and it maintains the "Save," "Save As," "Open," "New," and "Revert" triggers in the File menu.

There are three parts to the document control: the GenDocumentControl object, which maintains the user interface; the GenDocuments, each of which manages a single document; and the GenDocumentGroup object, which creates and manages GenDocument objects as needed. These classes can all be subclassed to add functionality.

1 Document Control Overview
1.1 The Document Control Objects
1.2 Document Control Interaction
1.3 Document Control Models
2 Document Control Data Fields
2.1 GenDocumentControl Data
2.2 GenDocumentGroup Data
2.3 GenDocument Attributes
3 Basic DC Messages
3.1 Other Document Group Messages
3.2 From the Doc Control Objects
4 Advanced DC Usage
4.1 Document Protocols
4.2 Multiple Document Model
4.3 Working with DOS files
4.4 Special-Purpose Messages
4.5 Forcing Actions
4.6 Some Other Internal Messages

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Document Control Overview
| One-page Version of this Chapter for Easy Printing


Document Control Overview

Documents: 1 Document Control Overview

| Up: GEOS SDK TechDocs | Up | Down | Prev: Documents | Next: 2 Document Control Data Fields

A program that creates or uses files--in short, almost all applications--has to deal with many tasks. It has to present dialog boxes to the user so he can choose which file to open; it must give alert messages when the user tries to do something dangerous, like quit without saving; and it must notice when GEOS is being shut down and make sure it saves appropriate data.

The document control objects make these tasks much easier. They take care of some of these tasks by themselves; for example, they maintain the File menu commands and display dialog and alert boxes as needed. The Document Control objects make other application jobs much easier by sending messages at appropriate times. They can also manage several documents at once, making it much easier for applications to manage multiple documents.

Many different document control sample applications are provided with the SDK. In most cases, you should be able to get your document control by copying over code from the appropriate sample application, then customizing it.

The Document Control Objects

Document Control Interaction

Document Control Models

| Up: GEOS SDK TechDocs | Up | Down | Prev: Documents | Next: 2 Document Control Data Fields


The Document Control Objects

Documents: 1.1 Document Control Overview: The Document Control Objects

| Up: GEOS SDK TechDocs | Up | Prev: 1 Document Control Overview | Next: 1.2 Document Control Interaction

There are three different classes of objects which together constitute the document control. These are GenDocumentControlClass , GenDocumentGroupClass , and GenDocumentClass . A document will need at least one of each to use the document control technology. The relationship between the objects is diagramed below.

GenDocumentControlClass

An application will have one object of class GenDocumentControlClass . This object manages the user interface: It presents some dialog and alert boxes, it manages the File Selector, it creates appropriate entries for the File menu, and it updates the enabled/disabled states of these items (e.g. it disables the Save trigger after the file has been saved).

The GenDocumentControl is generally made a child of the file menu, which is itself a child (or descendant) of the GenPrimary. The GenDocumentControl is on the GenPrimary's active list. It has no children, but it does have an optr to the GenDocumentGroup object. By convention, the GenDocumentControl object is in the same data segment as the GenPrimary object. Certain attributes of the GenDocumentControl object will determine the characteristics of the File Selector and other UI gadgets.

GenDocumentControlClass is a subclass of GenControlClass . This means that you can set up toolboxes to perform the "Save," "Open," etc., actions. For more details, see the Controllers chapter.

GenDocumentGroupClass

An application will have one object of class GenDocumentGroupClass . This object creates and manages the document objects. Ordinarily, the document objects belong to GenDocumentClass . However, if the program wishes to alter the behavior of the document object, it can create a subclass of GenDocumentClass . In this case, the GenDocumentGroup object will contain a pointer to the class definition of the document subclass, and will create document objects of this class as needed.

The GenDocumentGroup object is a child of any object. It does not have any children when it is declared; however, it will dynamically give itself document-object children at run-time. It is in its own data segment. Certain of its attributes determine what the attributes of its document children will be.

GenDocumentClass

Each GenDocument object manages a single open file. It keeps track of the volume, path, and filename for the document, the dirty state of the document, and other document-related information. It opens and closes files and presents file-related dialog boxes (e.g., "Save changes before closing?"). Programs often define a subclass of the GenDocumentClass which has additional, application-related functionality. In this case, objects of the subclass are used instead of objects of GenDocumentClass .

GenDocumentClass is a subclass of GenContentClass . Therefore, document objects can receive the output of GenView objects. GenDocument objects have all the functionality of GenContent objects. GenContent objects are themselves subclassed from GenClass . Very few applications will use the GenContent objects directly; for that reason, they are documented in this chapter.

Document objects are not declared in the source code; they are created at run-time by the GenDocumentGroup object. The document currently active is called the target document .

| Up: GEOS SDK TechDocs | Up | Prev: 1 Document Control Overview | Next: 1.2 Document Control Interaction


Document Control Interaction

Documents: 1.2 Document Control Overview: Document Control Interaction

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 The Document Control Objects | Next: 1.3 Document Control Models

A simple user operation will usually involve all three types of objects. For example, suppose the user selects the Open trigger from the File menu. The following actions will be taken:

  1. The Open trigger sends a MSG_GEN_DOCUMENT_CONTROL_INITIATE_OPEN_DOC to the GenDocumentControl object. The GenDocumentControl object responds by displaying a file selector.
  2. When the user selects a file and clicks "OK," the GenDocumentControl object gets the selected file's path from the file selector object.
  3. The GenDocumentControl object sends a MSG_GEN_DOCUMENT_GROUP_OPEN_DOC to the GenDocumentGroup, passing the name and path of the file to open.
  4. The GenDocumentGroup creates a document object, either of class GenDocumentClass or of a programmer-specified class. The GenDocumentGroup object sends a MSG_GEN_DOCUMENT_OPEN to the document object, passing the file and path name to be opened.
  5. The document object opens the file specified and handles errors appropriately. It then sends messages to the application instructing it to create the UI and initialize the file.

Most of these steps are transparent to the application programmer. Typically, an application will intercept MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE ; the programmer needs only to write this handler, not all the code needed for the above steps.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 The Document Control Objects | Next: 1.3 Document Control Models


Document Control Models

Documents: 1.3 Document Control Overview: Document Control Models

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Document Control Interaction | Next: 2 Document Control Data Fields

GEOS allows two distinct models of document control, a Procedural model and an Object model. While the two models use the same objects, they embody different programming philosophies.

The Procedural model of document control is much like traditional, non-object-oriented programming. Under this model, whenever a situation arises that needs the application's attention, the document control objects will send a message to a single object (generally the Process object). This object handles all of these situations.

Under the Object model of document control, the application defines a subclass of GenDocumentClass . This new document class has methods to handle situations needing the application's attention. This model is based on the philosophy of object-oriented programming; each document object has code to handle situations arising for that document.

The main difference between the two models is where the messages are sent. Under the Procedural model, messages are sent to the Process object; under the Object model, messages are sent to the appropriate document object. Every message sent in the Procedural model corresponds to a message sent in the Object model.

The Procedural model is simpler to use; it does not require the application to subclass objects. It is thus well-suited for simple applications which will have only one file open at a time. It may also be an easier model for programmers who are new to object-oriented programming. The Object model, on the other hand, is best suited for applications which will have many documents open at once; the application can let every document object manage a single document without worrying about other open documents.

The Procedural Model

The Procedural model of document control is much like traditional, procedure-oriented programming. This model is simpler to implement than the object-model. It is well suited for simple applications which have only one document open at a time.

Under the Procedural model of document control, every time a situation arises which requires the application's attention, the document control objects will send an appropriate message to the GenDocumentGroup object's output. These messages are imported from MetaClass , so all objects can handle them. The application will generally use global variables for run-time data storage.

For example, when a new document needs to be initialized, the document control sends a MSG_META_DOC_OUTPUT_INITIALIZE_DOCUMENT_FILE to its output object. The output object takes any appropriate steps (e.g., storing the file handle, setting up the map block, etc.).

The Object Model

The Object model of document control is better suited to advanced applications and applications which will have more than one document open at a time. Under this model, the application defines a subclass of GenDocumentClass . This new document class has handlers for situations requiring the application's attention. It also has local variables (i.e., instance data fields) which store any information the application will need about this document.

Whenever a situation arises that needs the application's attention, the relevant document object will send a message to itself. This document object will then handle the message. For example, an application might define its own document class, MyAppDocumentClass (a subclass of GenDocumentClass) . Suppose a new document has been created and needs to be initialized. First, the GenDocumentGroup object will create a new document object by instantiating an object of MyAppDocumentClass . Next, the new document object will send itself a MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE. MyAppDocumentClass will have a handler for this message; the handler will initialize the file as well as the document object's data structures.

Messages Under the Two Models

The simplest way to show the difference between the two models is to see how a single event is handled. This section examines one specific case, in which a document needs to be initialized; other cases are handled analogously.

Suppose a situation arises needing the application's attention; for example, a document is created and needs to be initialized. First, the document object will send an appropriate message to itself. In this case, it would send itself the message MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE . If the application uses a subclass of GenDocumentClass and this subclass has a handler for MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE , the messager will call that method; otherwise, the messager will call the handler defined for this message by GenDocumentClass . The handler in GenDocumentClass will find out the GenDocumentGroup object's output optr. If this optr is non-null, the handler will send an appropriate message (in this case, MSG_META_DOC_OUTPUT_INITIALIZE_DOCUMENT_FILE ) to the output object.

Note that, under normal circumstances, the application will handle only one of the two messages. For example, if the application writes a handler for MSG_GEN_DOCUMENT_PHYSICAL_SAVE , the handler defined by GenDocumentClass will not be called; as a result, MSG_META_DOC_OUTPUT_PHYSICAL_SAVE will not be sent to the GenDocumentGroup object's output. This is not usually a problem, since the application will generally handle one message or the other. If, for some reason, it needs to have both messages sent, the handler for MSG_GEN_DOCUMENT_SAVE should contain a @callsuper instruction.

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Document Control Interaction | Next: 2 Document Control Data Fields


Document Control Data Fields

Documents: 2 Document Control Data Fields

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Document Control Overview | Next: 3 Basic DC Messages

This section describes the attributes of the two document control classes, GenDocumentControlClass and GenDocumentGroupClass , as well as of the GenDocumentClass . Note that GenDocument objects are created at run-time, and their attributes are initialized by the creating GenDocumentGroup object. However, their attributes can be changed with the appropriate messages.

Many of the messages to the GenDocumentControl have corresponding messages to the GenDocumentGroup. For example, to find out the GenDocumentControl's attributes, one can either send a MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS to the GenDocumentControl or send a MSG_GEN_DOCUMENT_GROUP_GET_UI_ATTRS to the GenDocumentGroup. In either case, the message will return the same result. It is sometimes more convenient to send a message to the GenDocumentGroup object; for example, a document object can do this with @call @genParent::<message> .

GenDocumentControl Data

GenDocumentGroup Data

GenDocument Attributes

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Document Control Overview | Next: 3 Basic DC Messages


GenDocumentControl Data

Documents: 2.1 Document Control Data Fields: GenDocumentControl Data

| Up: GEOS SDK TechDocs | Up | Prev: 2 Document Control Data Fields | Next: 2.2 GenDocumentGroup Data

The GenDocumentControl handles interaction between the document control and the user. It maintains the File menu entries and manages the file selector. Its attributes all relate to these duties. A complete list of the attributes follows in GenDocumentControl Instance Data along with comments and the default values.

Some of these data fields can be examined but not set by the application. Data fields for purely internal use (which are neither examined, nor set, by applications) are not listed.

Code Display 13-1 GenDocumentControl Instance Data

/* The GDCI_documentToken field specifies the token characters and token ID of
 * files managed by this document control. All files created by the document
 * control will have these token characters, and the File Selector object will be
 * set to allow only such files to be selected. This attribute is ignored if DOS
 * files are being opened. */
    @instance GeodeToken			GDCI_documentToken = {};
/* GDCI_selectorType determines which files will be displayed by the File Selector
 * generated by this object. Only one of the options may be set. By default, only
 * documents are visible. */
    @instance GenFileSelectorType				GDCI_selectorType = GFST_DOCUMENTS;
    /* Types available:
     *	GFST_DOCUMENTS, 			GFST_EXECUTABLES,
     * 	GFST_NON_GEOS_FILES, 			GFST_ALL_FILES */
/* GDCI_attrs specifies certain characteristics of the file to be opened. The
 * default setting is shown below. */
    @instance GenDocumentControlAttrs					GDCI_attrs =
            ((GDCM_SHARED_SINGLE << GDCA_MODE_OFFSET) |
             GDCA_VM_FILE |
             GDCA_SUPPORTS_SAVE_AS_REVERT |
             (GDCT_NEW << GDCA_CURRENT_TASK_OFFSET))
    /* Attributes available:
     * GDCA_MODE:
     *	GDCM_VIEWER, 			GDCM_SHARED_SINGLE,
     *	GDCM_SHARED_MULTIPLE
     * GDCA_CURRENT_TASK:
     * 	GDCT_NONE, 			GDCT_NEW,
     * 	GDCT_OPEN, 			GDCT_USE_TEMPLATE,
     *	GDCT_SAVE_AS,			GDCT_COPY_TO,
     *	GDCT_DIALOG,			GDCT_TYPE,
     *	GDCT_PASSWORD
     * Other fields:
     * 	GDCA_MULTIPLE_OPEN_FILES,				GDCA_DOS_FILE_DENY_WRITE,
     *	GDCA_VM_FILE, 				GDCA_NATIVE,
     * 	GDCA_SUPPORTS_SAVE_AS_REVERT,				GDCA_DOCUMENT_EXISTS,
     *	GDCA_DO_NOT_SAVE_FILES
     */
/* GDCI_features specifies certain extra features of the document control. The
 * default setting is shown below. */
    @instance GenDocumentControlFeatures					GDCI_features =
            (GDCF_READ_ONLY_SUPPORTS_SAVE_AS_REVERT |
             GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN |
             GDCF_SUPPORTS_TEMPLATES |
             GDCF_SUPPORTS_USER_SETTABLE_EMPTY_DOCUMENT |
             GDCF_SUPPORTS_USER_MAKING_SHARED_DOCUMENTS |
             GDCF_NAME_ON_PRIMARY);
    /* Flags available:
     *	GDCF_READ_ONLY_SUPPORTS_SAVE_AS_REVERT,
     *	GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN,
     * 	GDCF_SUPPORTS_TEMPLATES,
     *	GDCF_SUPPORTS_USER_SETTABLE_EMPTY_DOCUMENT,
     *	GDCF_SUPPORTS_USER_SETTABLE_DEFAULT_DOCUMENT,
     *	GDCF_SUPPORTS_USER_MAKING_SHARED_DOCUMENTS,
     *	GDCF_NAME_ON_PRIMARY */
/* GDCI_enableDisableList specifies objects which should be enabled whenever a
 * document is opened and disabled when all documents are closed. The field is the
 * handle of a chunk containing a list of optrs to the objects to be enabled and
 * disabled. The default value is a null handle.*/
    @instance ChunkHandle			 	GDCI_enableDisableList;
/* The GDCI_openGroup, GDCI_importGroup, GDCI_useTemplateGroup, GDCI_saveAsGroup,
 * GDCI_exportGroup, and GDCI_userLevelGroup attributes hold optrs to groups of UI
 * objects to be added to the "Open," "Import," "Use Template," "Save As,"
 * "Export," and "User Level", dialog boxes, respectively. The GDCI_dialogGroup
 * field holds an optr to objects to be added to the opening dialog box. The optr
 * is to the head of a tree of UI objects. The top object in the tree should be
 * set "not usable." Default values are all null optrs. */
    @instance optr				GDCI_openGroup;
    @instance optr				GDCI_importGroup;
    @instance optr				GDCI_useTemplateGroup;
    @instance optr				GDCI_saveAsGroup;
    @instance optr				GDCI_exportGroup;
    @instance optr				GDCI_dialogGroup;
    @instance optr				GDCI_userLevelGroup;
/* If the GDCI_features field includes "displayNameOnPrimary" but no document is
 * open, the Primary's moniker is set to the string pointed to by the
 * GDCI_noNameText attribute. The default value is a null chunk handle. */
    @instance ChunkHandle				GDCI_noNameText;
/* If the GDCA_currentTask section of the GDCI_attrs field is set to
 * GDCT_NONE on startup, then the file specified by GDCI_defaultFile will be
 * opened (and, if necessary, created). The file is specified by a chunk handle of
 * a null-terminated string; this string should specify the file's path relative
 * to the SP_DOCUMENT standard path.The default value is a null chunk handle,
 * indicating that if the startup value of GDCA_currentTask is GDCT_NONE, no
 * documents should be opened. */
    @instance ChunkHandle 				GDCI_defaultFile;
/* GDCI_templateDir is the chunk handle of a null-terminated text string which
 * specifies a directory to hold template documents. The directory is specified
 * relative to SP_TEMPLATE. If not set by you, this defaults to the SP_TEMPLATE
 * standard path. */
    @instance ChunkHandle				GDCI_templateDir;
/* GDCI_documentGroup is an optr to the GenDocumentGroup object. You must set
 * this field. */
    @instance optr			GDCI_documentGroup;
/* GDCI_targetDocName is a character array. It is set to contain the name of
 * the current target file. This field is automatically updated by the document
 * control. */
    @instance FileLongName				GDCI_targetDocName = "";
/* The Document Control automatically displays a big dialog box at startup which
 * lets the user choose to create, open, etc. a file. Each option has a button
 * (with a picture) and an explanatory text. You can override the default graphic
 * or text by setting any of the following fields:
 */
    @instance ChunkHandle				GDCI_dialogNewText;
    @instance ChunkHandle				GDCI_dialogTemplateText;
    @instance ChunkHandle				GDCI_dialogOpenDefaultText
    @instance ChunkHandle				GDCI_dialogImportText;
    @instance ChunkHandle				GDCI_dialogOpenText;
    @instance ChunkHandle				GDCI_dialogUserLevelText;
    @instance @visMoniker				GDCI_dialogNewMoniker;
    @instance @visMoniker				GDCI_dialogTemplateMoniker;
    @instance @visMoniker				GDCI_dialogOpenDefaultMoniker
    @instance @visMoniker				GDCI_dialogImportMoniker;
    @instance @visMoniker				GDCI_dialogOpenMoniker;
    @instance @visMoniker				GDCI_dialogUserLevelMoniker;

The Document Token

GDCI_documentToken, MSG_GEN_DOCUMENT_CONTROL_GET_TOKEN, MSG_GEN_DOCUMENT_GROUP_GET_TOKEN

The document control's file selector will display only those files whose document token matches the GenDocumentControl object's GDCI_documentToken attribute. All files created by the application will have the specified document tokens. There are no messages to alter the token attributes at run-time. (If the document control is used to manage DOS files, the file selector will show all non-GEOS files.)

A token is defined by a GeodeToken structure. The format of this structure is shown below. The first field, GT_chars , will vary for each document type. The second, GT_manufID , will be the same for the tokens of all applications and documents created by a given company.

typedef struct {
    char 		GT_chars[TOKEN_CHARS_LENGTH];
                /* TOKEN_CHARS_LENGTH = 4 */
    ManufacturerID 		GT_manufID;
                /* word-sized integer */
} GeodeToken;

The message MSG_GEN_DOCUMENT_CONTROL_GET_TOKEN instructs the GenDocumentControl object to write a copy of the document token to a specified address. The message has one argument: the address of a GeodeToken . MSG_GEN_DOCUMENT_GROUP_GET_TOKEN functions identically, but it is sent to the GenDocumentGroup object.

You can also find out the application's token by sending GEN_DOCUMENT_CONTROL_GET_CREATOR or GEN_DOCUMENT_GROUP_GET_CREATOR to the appropriate object. The application's token will be used as the "creator token" for any documents created by the document control.

MSG_GEN_DOCUMENT_CONTROL_GET_TOKEN

void 	MSG_GEN_DOCUMENT_CONTROL_GET_TOKEN(
        GeodeToken *		token); /* address to copy token to */

This message gets the document token values for all documents created by this document control.

Source: Unrestricted.

Destination: Any GenDocumentControl object.

Parameters: token A pointer to an empty GeodeToken structure.

Return: The document GeodeToken is written to the variable whose address is passed.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_GET_TOKEN

void 	MSG_GEN_DOCUMENT_GROUP_GET_TOKEN(
        GeodeToken *		token); /* address to copy token to */

This is the same as MSG_GEN_DOCUMENT_CONTROL_GET_TOKEN , except that it is sent to the GenDocumentGroup object instead of the GenDocumentControl object.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: token A pointer to an empty GeodeToken structure.

Return: The document GeodeToken is written to the variable whose address is passed.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_CONTROL_GET_CREATOR

void 	MSG_GEN_DOCUMENT_CONTROL_GET_CREATOR(
        GeodeToken *		token); /* address to copy token to */

This message gets the token for the application; this token is the "creator token" for all files created by the document control.

Source: Unrestricted.

Destination: Any GenDocumentControl object.

Parameters: token A pointer to an empty GeodeToken structure.

Return: The document GeodeToken is written to the variable whose address is passed.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_GET_CREATOR

void 	MSG_GEN_DOCUMENT_GROUP_GET_CREATOR(
        GeodeToken *		token); /* address to copy token to */

This is the same as MSG_GEN_DOCUMENT_CONTROL_GET_CREATOR , except that it is sent to the GenDocumentGroup object instead of the GenDocumentControl object.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: token A pointer to an empty GeodeToken structure.

Return: The document GeodeToken is written to the variable whose address is passed.

Interception: You should not subclass this message.

The GDCI_selectorType Field

GDCI_selectorType

The GDCI_selectorType field determines what files will be displayed by and can be opened with the file selector. The options are stored as a byte-sized enumerated type. The options are:

GFST_DOCUMENTS
This is the default option. The file selector will display those documents with the appropriate tokens.
GFST_EXECUTABLES
The file selector will display executable files as well as appropriate document files.
GFST_NON_GEOS_FILES
The file selector will display all non-GEOS files (and only non-GEOS files).
GFST_ALL_FILES
The file selector will display all files.

The GDCI_attrs Field

GDCI_attrs, MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS, MSG_GEN_DOCUMENT_GROUP_GET_UI_ATTRS

The GenDocumentControl object has eight attribute flags stored in the word-sized bitfield GDCI_attrs . They may be retrieved by sending MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS to the GenDocumentControl object or by sending MSG_GEN_DOCUMENT_GROUP_GET_UI_ATTRS to the GenDocumentGroup object. The attributes are set at coding time; there is no way for an application to change the attributes at run-time, although the GenDocumentControl will change some of the attributes to reflect its current state.

The attributes are

GDCA_MULTIPLE_OPEN_FILES
Allow several documents to be open at once. If this attribute is disabled, the "New" and "Open" triggers will be disabled when a document is open (however, see also the description of the flag GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN on GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN This feature is ignored if the attribute GDCA_MULTIPLE_OPEN_FILES is on. If GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN is on and GDCA_MULTIPLE_OPEN_FILES is off, the "New" and "Open" triggers are enabled when the doc ). This attribute defaults to off.
GDCA_MODE
This is a two-bit field. GDCA_MODE is a mask of all the bits in this field; the offset of this field is equal to the constant GDCA_MODE_OFFSET. The field has the following possible settings:
GDCM_VIEWER
All documents are opened in read-only mode; the New, Save, Save As, and Revert triggers are permanently disabled. Other applications can open the file for read/write access.
GDCM_SHARED_SINGLE
Documents are opened for reading and writing. When a document is open, it is marked "deny-write" so other applications can open the file only for read-only access. The user can mark a document as a "public" document, in which case the default is to open a file "read-only." The default GDCA_MODE setting is GDCM_SHARED_SINGLE.
GDCM_SHARED_MULTIPLE
This mode is designed for documents that can have multiple writers, such as multi-user databases. Documents are ordinarily opened as in GDCM_SHARED_SINGLE mode above; however, a user can designate a file as a "multi-user" file, which means that it can be opened by several applications at once for read/write access.
The default setting of the GDCA_MODE flag is GDCM_SHARED_SINGLE. If you want a different value, first clear the two-bit field, then set the new setting, like this:
GDCI_attrs = (@default & ~GDCA_MODE) \
    | (GDCM_VIEWER << GDCA_MODE_OFFSET);
Note that the GDCA_MODE attribute has a slightly different effect if the document control manages DOS files. For details, see GDCA_DOS_FILE_DENY_WRITE below.
GDCA_DOS_FILE_DENY_WRITE
This attribute does not matter for VM files. If a DOS file is opened while the GDCA_DOS_FILE_DENY_WRITE bit is set, no other application will be able to write to that file. This is true even if the DOS file was opened for read-only access; however, if the file is a multi-user document opened in "shared-multiple" mode, other applications will be able to write to it regardless of whether the GDCA_DOS_FILE_DENY_WRITE attribute is set. By default, GDCA_DOS_FILE_DENY_WRITE is off.
GDCA_VM_FILE
This attribute specifies whether the document control objects will open GEOS Virtual Memory files (if GDCA_VM_FILE is on), or DOS files (if GDCA_VM_FILE is off). The default value is on.
GDCA_NATIVE
If this bit is set and GDCA_VM_FILE is not set, documents will be stored in the format native to the file-system.
GDCA_SUPPORTS_SAVE_AS_REVERT
This attribute is ordinarily set only for GEOS files. If the attribute is on, the application will use the backup functionality of VM files to support "Save As" and "Revert" functionality. If the attribute is off, the file will be altered whenever it is updated to disk. The default value is on. DOS files do not normally support "Save As" and "Revert." Applications can implement "Save As" and "Revert" functionality for DOS files by defining a subclass of GenDocumentClass with handlers for MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS and MSG_GEN_DOCUMENT_PHYSICAL_REVERT , but this is not recommended. Ordinarily, this attribute should be off for DOS files.
GDCA_DOCUMENT_EXISTS
This attribute is set and maintained at run-time by the GenDocumentControl code. The attribute is on if at least one document is open.
GDCA_CURRENT_TASK
This three-bit attribute has a dual function: It determines the application's behavior at start-up, and it indicates what task the application is currently performing. The mask GDCA_CURRENT_TASK is a mask of all the bits in this field; the field's offset is equal to the constant GDCA_CURRENT_TASK_OFFSET. The possible settings are as follows:
GDCT_NONE
If a default file has been specified (see The GDCI_noNameText Field ), that file will be opened; otherwise, the application will start with no file opened.
GDCT_NEW
A new document will be created at startup. If the GDCF_DIALOG_BOX_FOR_NEW flag is set, a dialog box will be presented at startup.
GDCT_OPEN
The "Open File" dialog box will be presented at startup.
GDCT_USE_TEMPLATE
The "Use Template" dialog box will be presented at startup.
GDCT_SAVE_AS
This is not a valid initial setting. The GDCA_CURRENT_TASK field has this setting between when a user chooses the "Save As" command and when the document is saved.
GDCT_COPY_TO
This is not a valid initial setting. The GDCA_CURRENT_TASK field has this setting between when the user chooses the "Copy To" command and when the command has been fully executed.
GDCT_DIALOG
GDCT_TYPE
GDCT_PASSWORD
None of these are valid initial settings.

If a document is passed in to be opened at startup (as, for example, when a user launches an application by double-clicking a file created by the application), that file will be opened, and the initial setting of GDCA_CURRENT_TASK will be ignored. The document control automatically maintains this bitfield to correspond to whatever action the document control is currently taking. The application can find out what the document control is doing by reading the attributes and checking this field.

GDCA_DO_NOT_SAVE_FILES
If this bit is set, the application will not be able to save files. By setting this bit, you can turn your application into a fully-functioning demo.
GDCA_FORCE_DEMAND_PAGING
If this bit is set, the document will be paged with demand-paging, even if the system ordinarily forces all documents completely into memory.

MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS

GenDocumentControlAttrs 	MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS();

Use this message to find out what the GenDocumentControl object's GDCA_attrs flags are. The attributes cannot be changed by a message; they can only be read.

Source: Unrestricted.

Destination: Any GenDocumentControl object.

Parameters: None.

Return: Returns a word-length bitfield containing GDCA _attrs record.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_GET_UI_ATTRS

GenDocumentControlAttrs 	MSG_GEN_DOCUMENT_GROUP_GET_UI_ATTRS();

This message is the same as the MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS message (see above) except that it is sent to the GenDocumentGroup object.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: None.

Return: A word-length record containing the GDCA _attrs field.

Interception: You should not subclass this message.

The GDCI_features Flags

GDCI_features, MSG_GEN_DOCUMENT_CONTROL_GET_FEATURES, MSG_GEN_DOCUMENT_GROUP_GET_UI_FEATURES, MSG_GEN_CONTROL_CONFIGURE_FILE_SELECTOR

The GDCI_features attribute specifies whether certain optional functionality of the Document Control technology is enabled. The features are determined at coding time; there is no message to change features at run-time. To retrieve the features, send MSG_GEN_DOCUMENT_CONTROL_GET_FEATURES .

GDCF_READ_ONLY_SUPPORTS_SAVE_AS_REVERT
If this feature is on, the "Save As" and "Revert" triggers are enabled when read-only documents are opened. Once the user chooses "Save As," the new file will no longer be opened as "Read Only"; the "Save" trigger will be enabled. If this feature is off, "Save As" and "Revert" triggers are disabled for read-only files. By default, this feature is on.
GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN
This feature is ignored if the attribute GDCA_MULTIPLE_OPEN_FILES is on. If GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN is on and GDCA_MULTIPLE_OPEN_FILES is off, the "New" and "Open" triggers are enabled when the document is "clean" (i.e., the document has not been marked "dirty" since it was opened/created or saved); that is, "New" and "Open" are enabled whenever "Save" is disabled. If the user chooses "New" or "Open" when the document is "clean," the target document is closed and the new document is opened or created. If both GDCA_MULTIPLE_OPEN_FILES and GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN are off, the "New" and "Open" triggers are disabled whenever a document is open. By default, this attribute is on.
GDCF_SUPPORTS_TEMPLATES
If this feature is enabled, the user can save files as templates. If the user chooses the command "Use Template," a copy of the template is opened as a "new" document, and the template is left unchanged. If this feature is disabled, the application cannot create templates. By default, this feature is on.
GDCF_SUPPORTS_USER_SETTABLE_EMPTY_DOCUMENT
If this feature is enabled, the user can designate a file to be the model for all new documents. When the user chooses "New," this "model" document will be copied and the copy will be opened. By default, this feature is on.
GDCF_SUPPORTS_USER_SETTABLE_DEFAULT_DOCUMENT
If this attribute is on, the user can choose a default document (one which will automatically be opened when the application is launched). (See The GDCI_defaultFile Field .)
GDCF_SUPPORTS_USER_MAKING_SHARED_DOCUMENTS
If this attribute is on, the user can save a document as "shared," allowing several processes to access it at once. By default, the attribute is on.
GDCF_NAME_ON_PRIMARY
If this attribute is on, the name of the target document is displayed at the top of the Primary window. The GenDocumentControl object does this by changing the moniker of the GenPrimary object to the name of the target document. If there is no open document, the GenPrimary will display the string specified by the attribute GDCI_noNameText . By default, this attribute is on.

For an added degree of control, you can use MSG_GEN_DOCUMENT_CONTROL_CONFIGURE_FILE_SELECTOR to change the attributes of the document control's file selector.

MSG_GEN_DOCUMENT_CONTROL_GET_FEATURES

GenDocumentControlFeatures 	MSG_GEN_DOCUMENT_CONTROL_GET_FEATURES();

Use this message to retrieve the current GDCI_features flags. The flags cannot be changed at run-time.

Source: Unrestricted.

Destination: Any GenDocumentControl object.

Parameters: None.

Return: The GenDocumentControl's GDCI_features flags.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_GET_UI_FEATURES

GenDocumentControlFeatures 	MSG_GEN_DOCUMENT_GROUP_GET_UI_FEATURES();

Use this message to retrieve the current GDCI_features flags. The flags cannot be changed at run-time.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: None.

Return: The GenDocumentControl's GDCI_features flags.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_CONTROL_CONFIGURE_FILE_SELECTOR

void 	MSG_GEN_DOCUMENT_CONTROL_CONFIGURE_FILE_SELECTOR(
        optr 		fileSelector,
        word 		flags); /* GenDocumentControlAttrs */

Configure file selector. This message can be sub-classed to modify the behavior of the file selectors that the document control uses.

The GDCI_enableDisableList Field

GDCI_enableDisableList

The GenDocumentControl can be set to enable certain UI objects when documents are open. This is done using the GDCI_enableDisableList attribute. This attribute is the chunk handle of a list of object-pointers. Each of the referenced objects should start as disabled. Whenever a document is opened, a MSG_GEN_SET_ENABLED is sent to each object in the list. When the last document is closed, a MSG_GEN_SET_NOT_ENABLED is sent to each object in the list.

Adding to the Dialog Boxes

GDCI_openGroup, GDCI_importGroup, GDCI_useTemplateGroup, GDCI_saveAsGroup, GDCI_exportGroup, GDCI_dialogGroup, GDCI_userLevelGroup

The GenDocumentControl object manages the dialog boxes for many different user actions. The programmer can specify a tree of UI objects to be included with each of these dialog boxes. For example, to add a group of objects to the "Use Template" dialog box, the programmer should put them all in a tree (perhaps by making them all children of a GenInteraction object) and store an object-pointer to the head of the tree in the GDCI_useTemplateGroup attribute. The top object in the tree should be set "not usable."

The GDCI_noNameText Field

GDCI_noNameText

The GenDocumentControl object can be set to display the name of the current target document in the moniker of the GenPrimary window. If this feature is enabled, and no document is opened, the GenPrimary will have its moniker change to the string specified by GDCI_noNameText . If the feature GDCF_DISPLAY_NAME_ON_PRIMARY is disabled, GDCI_noNameText is ignored.

The GDCI_defaultFile Field

GDCI_defaultFile

If the attribute GDCA_CURRENT_TASK is initially set to GDCT_NONE and a default file is specified, the default file is automatically opened at startup. If GDCA_CURRENT_TASK is not initially set to GDCT_NONE, this attribute is ignored. This field holds the chunk handle of a null-terminated string. The string specifies the file's path relative to the SP_DOCUMENT. If the feature GDCF_SUPPORTS_USER_SETTABLE_DEFAULT_DOCUMENT is enabled, this attribute can be changed by the user at run-time. If the file specified does not exist, it is created as an empty document; if the document exists but cannot be opened, no file is opened at startup. If the named document exists but is inappropriate (e.g. it was created by another application), no document is opened at startup.

The GDCI_templateDir Field

GDCI_templateDir

If templates are supported, this is the default directory for opening and saving them. This string specifies a subdirectory to the standard path SP_TEMPLATE. If not explicitly set in the source code, the template directory will default to SP_TEMPLATE.

The GDCI_documentGroup Field

GDCI_documentGroup

The GenDocumentControl and the GenDocumentGroup communicate with each other via messages. To do this, each needs the optr of the other. GDCI _documentGroup is the optr of the GenDocumentGroup object for this application. It is set in the source code and may not be changed at run time.

The GDCI_targetDocName Field

GDCI_targetDocName

This attribute contains the name of the target document. The document control automatically sets and updates this field when necessary.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Document Control Data Fields | Next: 2.2 GenDocumentGroup Data


GenDocumentGroup Data

Documents: 2.2 Document Control Data Fields: GenDocumentGroup Data

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 GenDocumentControl Data | Next: 2.3 GenDocument Attributes

The GenDocumentGroup object creates and manages the document objects. In the "process" model of document control, it sends messages to the process object (or some other designated object) when the application needs to take some action. (It sends these messages even when the "object" model is being followed; however, the messages are ignored.)

A list of data fields for the GenDocumentGroup object follows in GenDocumentGroupClass instance data . Some of the data fields can be changed at run-time, and others cannot; a discussion of the data fields follows the listing. If a data field cannot be set or read by the application, it is not discussed.

Code Display 13-2 GenDocumentGroupClass instance data

/* GDGI_attrs is a record that specifies certain basic characteristics of the
 * documents to be managed. The attributes are set in the source code and are not
 * changed at run-time. The default settings are below. */
    @instance GenDocumentGroupAttrs				GDGI_attrs = (GDGA_VM_FILE |
                    GDGA_SUPPORTS_AUTO_SAVE |
                    GDGA_AUTOMATIC_CHANGE_NOTIFICATION |
                    GDGA_AUTOMATIC_DIRTY_NOTIFICATION |
                    GDGA_APPLICATION_THREAD |
                    GDGA_AUTOMATIC_UNDO_INTERACTION |
                    GDGA_CONTENT_DOES_NOT_MANAGE_CHILDREN);
    /* The following flags are available:
     * 	GDGA_VM_FILE,
     *	GDGA_NATIVE,
     *	GDGA_SUPPORTS_AUTO_SAVE,
     *	GDGA_AUTOMATIC_CHANGE_NOTIFICATION,
     * 	GDGA_AUTOMATIC_DIRTY_NOTIFICATION,
     * 	GDGA_APPLICATION_THREAD,
     *	GDGA_VM_FILE_CONTAINS_OBJECTS,
     *	GDGA_CONTENT_DOES_NOT_MANAGE_CHILDREN,
     * 	GDGA_LARGE_CONTENT,
     * 	GDGA_AUTOMATIC_UNDO_INTERACTION */
/* GDGI_untitledName is the name suggested when a new document is
 * first saved. */
    @instance ChunkHandle			GDGI_untitledName;
/* The GenDocumentGroup object creates a document object for each document
 * opened. The attribute GDGI_documentClass is a pointer to the class definition
 * which will be used for document objects. By default, it points to the
 * definition of GenDocumentClass, so document objects belong to GenDocumentClass.
 * If you use a subclass of GenDocumentClass, you must change this attribute to
 * point to the new class. */
    @instance ClassStruc *			GDGI_documentClass =
                    (ClassStruc *) &GenDocumentClass;
/* Ordinarily, the Document Group creates document objects by instantiating an
 * object of the class indicated by GDGI_documentClass. However, it can be
 * instructed instead to duplicate a specific document object for each new
 * document. To arrange this, set the GDGI_genDocument to point to the document
 * object to duplicate. */
    @instance optr 			GDGI_genDocument;
/* If the Procedural model is used, whenever the application needs to take an
 * action, messages will be sent to the output of the GenDocumentGroup.
 * Ordinarily, the output will be the process object. If the Object model is used,
 * this attribute is generally left as a null pointer. */
    @instance optr			GDGI_output;
/* The GenDocumentGroup object communicates with the GenDocumentControl
 * object through messages. To do this, each one needs an object-pointer to the
 * other. This is set in the source code. */
    @instance optr			GDGI_documentControl;
/* The GenDocument (or subclass) objects can behave as Content objects. The
 * document control can automatically connect GenDocument objects to the GenView
 * if told to do so. The GDGI_genView field is an object-pointer to a GenView
 * object. */
    @instance optr			GDGI_genView;
/* In a multiple-document model, the document control can be set up to work with
 * the display control. When this functionality is enabled, the
 * GenDocumentGroup will automatically duplicate a specified block (generally
 * one containing a GenDisplay object), attach the Display object to the specified
 * GenDisplayGroup object, and set the header for the GenDisplay to the name of
 * the document. When the document is closed, the block is freed. */
    @instance optr			GDGI_genDisplay;
/* GDGI_genDisplayGroup points to the GenDisplayGroup which manages the
 * GenDisplays. */
    @instance optr			GDGI_genDisplayGroup;
/* Each GEOS document has a protocol number, which identifies the version of
 * the application that created it. The GDGI_protocolMajor and
 * GDGI_protocolMinor attributes specify the protocol number to be assigned to
 * all documents created by the document control. */
    @instance word			GDGI_protocolMajor = 1;
    @instance word			GDGI_protocolMinor = 0;

The GDGI_attrs Field

GDGI_attrs, MSG_GEN_DOCUMENT_GROUP_GET_ATTRS

This attribute specifies certain characteristics of the documents to be opened. These attributes are generally set in the source code and can not be changed at run-time. They are stored in a word-sized bitfield.

GDGA_VM_FILE
This attribute is on if the documents to be opened are GEOS Virtual Memory files. By default, it is on.
GDGA_NATIVE
This attribute is on if the documents to be opened are in the file system's native format (i.e. they are not GEOS files). By default, it is off.
GDGA_SUPPORTS_AUTO_SAVE
If this attribute is on, the documents will be periodically auto-saved. It works only with VM files (unless you subclass GenDocument to handle MSG_GEN_DOCUMENT_PHYSICAL_UPDATE ; see Working with DOS files ). It works by periodically updating the file to disk. It should probably be turned off if "Save As" and "Revert" are disabled. By default, the attribute is on. The program can temporarily disable auto-save for a document by sending the document object MSG_GEN_DOCUMENT_DISABLE_AUTO_SAVE .
GDGA_AUTOMATIC_CHANGE_NOTIFICATION
If this attribute is on, the GenDocumentGroup object will periodically check all open documents to see if they have been changed by another process. If a document has changed, the document control will send MSG_META_DOC_OUTPUT_DOCUMENT_HAS_CHANGED to the application. This attribute is useful if the application may be reading multi-user files.
GDGA_AUTOMATIC_DIRTY_NOTIFICATION
This attribute is relevant only for GEOS files. If the attribute is on, whenever a file is marked "dirty," the file system will automatically notify the document control. The document control will then take appropriate actions (enable the "Save" trigger, etc.). The document control will also present a "Save changes before closing" dialog box if the document is closed before being saved. If GDGA_AUTOMATIC_DIRTY_NOTIFICATION is off, or if the documents are DOS files, the application will have to notify the document control when the document is dirty. It does this by sending a MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY to the GenDocumentGroup object (under the procedure model), or by sending a MSG_GEN_DOCUMENT_MARK_DIRTY to the document object (under the object model). By default, GDGA_AUTOMATIC_DIRTY_NOTIFICATION is on.
GDGA_APPLICATION_THREAD
If this attribute is on, the GenDocumentGroup object is run by the application thread, as are its (document-object) children. By default, it is on.
GDGA_VM_FILE_CONTAINS_OBJECTS
If the document control manages Virtual Memory Object files, this attribute should be set to on. By default, this attribute is off.
GDGA_CONTENT_DOES_NOT_MANAGE_CHILDREN
The application's main VisContent, if any, does not manage its children. By default, this attribute is on.
GDGA_LARGE_CONTENT
The application's main VisContent uses the large model. By default, this attribute is off.
GDGA_AUTOMATIC_UNDO_INTERACTION
The application sends out undo set-context messages automatically as necessary.

MSG_GEN_DOCUMENT_GROUP_GET_ATTRS

GenDocumentGroupAttrs 	MSG_GEN_DOCUMENT_GROUP_GET_ATTRS();

Use this message to find out the attributes of the GenDocumentGroup object. Note that the attributes cannot be changed at run-time; they can only be examined.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: None.

Return: Flags in GDGI _attrs bitfield.

Interception: You should not subclass this message.

The GDGI_untitledName Field

 GDGI_untitledName, MSG_GEN_DOCUMENT_GROUP_GET_DEFAULT_NAME

The first time a new document is saved, the document control presents a "Save As" dialog box. If the GDGI_untitledName field is set to point to a string, that string will be suggested as the name of the document. If the attribute is not set, no name will be suggested. The current default name can be retrieved by sending MSG_GEN_DOCUMENT_GROUP_GET_DEFAULT_NAME to the GenDocumentGroup object.

MSG_GEN_DOCUMENT_GROUP_GET_DEFAULT_NAME

GenDocumentGroupAttrs 	MSG_GEN_DOCUMENT_GROUP_GET_DEFAULT_NAME(
        char *buffer); /* Address to write default name */

This message instructs the GenDocumentGroup object to copy the GDGI_untitledName attribute to the specified address. In addition, the message will return the GDGI_attrs word of the GenDocumentGroup object. If you just want the attributes, use MSG_GEN_DOCUMENT_GROUP_GET_ATTRS .

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: buffer A pointer to a character buffer. This buffer should be of length FILE_LONGNAME_BUFFER_SIZE or greater.

Return: The record of flags stored in GDGI_attrs .

*buffer
Null-terminated name string.

Interception: You should not subclass this message.

Warnings: Make sure the buffer is long enough to hold any file name. Otherwise, the method may overwrite data after the buffer. The constant FILE_LONGNAME_BUFFER_SIZE, defined in file.def , is equal to the maximum file name length, counting the null terminator.

The GDGI_documentClass Field

GDGI_documentClass

Each time a document is opened, the GenDocumentGroup creates a document object. Ordinarily, the document object is a member of GenDocumentClass . However, sometimes the programmer will want to add functionality to the document objects, doing so by defining a subclass of GenDocumentClass . (For example, in the object model of document control, the program implements most functionality by defining new methods for the document class.) If this is the case, the programmer will have to make sure the GenDocumentGroup object creates document objects from the new class. One can do this by setting the GDGI_documentClass field to point to the class structure of the new document object class. By default, this field points to GenDocumentClass .

The GDGI_genDocument Field

GDGI_genDocument

Ordinarily, the document group creates new document objects by instantiating objects from the class specified in GDGI _documentClass . However, you can instead provide a document object for the document group to duplicate. To do this, set the GDGI _genDocument field to the optr of the "template" document object. This object should be marked "not usable."

The GDGI_output Field

GDGI_output, MSG_GEN_DOCUMENT_GROUP_GET_OUTPUT, MSG_GEN_DOCUMENT_GROUP_SET_OUTPUT

Every time something happens which needs to be handled by the application, the document control notifies the application in two ways: The relevant document object sends a message to itself, and the GenDocumentGroup object sends a message to its designated output object. In the Procedural model of document control, the document-object messages are ignored, and the GenDocumentGroup messages are sent to an object (usually the process object) which has handlers for the messages. In the Object model, the GDGI_output attribute is left as a null pointer, and GenDocumentClass is subclassed to handle the messages.

MSG_GEN_DOCUMENT_GROUP_GET_OUTPUT

optr	MSG_GEN_DOCUMENT_GROUP_GET_OUTPUT();

Under the Procedural model of document control, the GenDocumentGroup sends messages to a designated output object. To get the optr of that output object, send this message to the GenDocumentGroup object.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Return: Returns the optr of the document group's output object.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_SET_OUTPUT

void	MSG_GEN_DOCUMENT_GROUP_SET_OUTPUT(
        optr	output); /* The new recipient of the GenDocumentGroup's
        output messages */

Under the Procedural model of document control, the GenDocumentGroup object sends messages to a designated output object. Use this message to change the recipient of the output.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: output The optr of the object which will receive the output.

Interception: You should not subclass this message.

The GDGI_documentControl Field

GDGI_documentControl

The GenDocumentControl and the GenDocumentGroup communicate with each other via messages. To do this, each needs an optr to the other. GDCI_documentControl is an optr to the GenDocumentGroup object for this application. It is set in the source code and is not changed at run time.

Dynamically Creating Displays

GDGI_genDisplay, GDGI_genDisplayGroup, MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY, MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY_GROUP

In a multi-document application, each document will ordinarily have its own GenDisplay object and often many other UI objects as well. The document control can be instructed to dynamically create a number of objects for each new document and destroy these objects when the document is closed.

If an application is going to have the document control create and manage GenDisplay objects, it must declare a GenDisplayGroup object. The GenDisplayGroup should be declared normally; however, it should be given no children. In the source code, the GenDocumentGroup object's GDGI_genDisplayGroup data field should contain an optr to the GenDisplayGroup object.

The application should also declare a template resource. This resource should contain a single generic tree; the top object in this tree should be a GenDisplay object which is set "not usable." The GenDocumentGroup object's GDGI_genDisplay field should contain an optr to that GenDisplay.

When a new document object is created, the document control will automatically copy the resource containing the GenDisplay referenced by GDGI_genDisplay , make the new GenDisplay a child of the GenDisplayGroup referenced by GDGI_genDisplayGroup , and set the new GenDisplay as "usable." When the document object is destroyed (because the document is closed), the document control will automatically destroy that document's copy of the resource.

MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY

optr	MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY();

The GDGI_genDisplay field can be set to point to a GenDisplay object. If GDGI_genDisplay is not a null optr, then the document control will duplicate the resource containing the referenced GenDisplay whenever a new document object is created. The duplicate GenDisplay is made a child of the GenDisplayGroup object indicated by GDGI_genDisplayGroup . By using this message, you can get an optr to that "template" display object. Any changes made to that object will be copied whenever a new document object is created.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Return: Returns the optr of the "template" GenDisplay.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY_GROUP

optr	MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY_GROUP();

If a GenDisplayGroup object is used to manage GenDisplay objects, the GenDocumentGroup object will contain an optr to the GenDisplayGroup. By using this message, you can get an optr to the GenDisplayGroup object.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Return: Returns the optr of the GenDisplayGroup object.

Interception: You should not subclass this message.

Connecting Documents with a GenView

GDGI_genView, MSG_GEN_DOCUMENT_GROUP_GET_VIEW

The document control can be instructed to automatically connect the output of a GenView to the document object associated with the view. That way, the document object can handle all the messages relating to the view. This is naturally only done when the application is using the Object model; if it is using the Procedural model, a GenView will most likely send its messages to the Process object.

There are two ways to enable this functionality. One way is appropriate only to single-document applications; the other is appropriate to multi-document applications.

A single-document application using the Object model should declare the GenView normally as part of the generic tree. (It might well be placed on the GenDocumentControl object's GDCI_enableDisableList .) The source code should set the GDGI_genView field to be an optr to the GenView. When a document is opened, the document control will automatically set the GenView object to direct its output to the document object.

A multi-document application using the Object model should use the document control's ability to create and manage GenDisplay objects. The application will have a resource which is duplicated for each open document. This resource will contain a generic tree, at the head of which is a GenDisplay. To use a GenView, all the application has to do is put a GenView in the tree headed by that GenDisplay, and set GDGI_genView to point to that GenView. When a document object is created, the document control will automatically have the new GenView (in the duplicate resource) send its output to the new GenDocument.

MSG_GEN_DOCUMENT_GROUP_GET_VIEW

optr MSG_GEN_DOCUMENT_GROUP_GET_VIEW();

The GenDocumentGroup object can be set to automatically link document objects to GenView objects. Use this message to find out what the designated GenView is. If there is no such GenView , this message will return a null optr.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Return: The optr of the GenView object (specified in GDGI _genView).

Interception: You should not subclass this message.

Document Protocols

GDGI_protocolMajor, GDGI_protocolMinor, MSG_GEN_DOCUMENT_GROUP_GET_PROTOCOL

Every GEOS file (and each application that creates such files) has a protocol associated with it. Protocols are used to make sure an application knows if a given document was created by a different version of the application. They are stored in the file's FEA_PROTOCOL extended attributes (see the File System chapter). The protocol is of the form "MAJOR.MINOR," where both "MAJOR" and "MINOR" are 16-bit unsigned integers. The application's protocol is specified by the GDGI_protocolMajor and GDGI_protocolMinor attributes of the GenDocumentGroup object.

All documents created by an application will have the application's protocol number. If a document has the same major protocol number as the application but a lower minor protocol number, the document is compatible with the application. If the document has a lower major protocol number, the document is incompatible with the application; it can be opened only if a routine has been defined to upgrade the document. If the document has a higher protocol than the application (i.e. its major protocol number is higher, or it has the same major protocol number and a higher minor protocol number), the document control will not open the file; it will present an error message. By default, the GenDocumentGroup object has a GDGI_protocolMajor of one and a GDGI_protocolMinor of zero.

When the user opens an earlier but compatible document, the GenDocumentGroup opens the file and attaches it to a document object. Then, the (newly-created) document object sends itself MSG_GEN_DOCUMENT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT; the GenDocumentGroup then sends its output object the message MSG_META_DOC_OUTPUT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT . If neither of these messages is handled, the document will be opened as if it were of the current protocol (since it is compatible). Often an application will not handle these messages.

If the user tries to open an earlier and incompatible document, the GenDocumentGroup opens the file and attaches it to a document object. Then, the document object sends a MSG_GEN_DOCUMENT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT to itself, and the GenDocumentGroup sends a MSG_META_DOC_OUTPUT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT to its output. If neither message is handled, the GenDocumentGroup closes the file unchanged and removes the document object, and the GenDocumentControl presents an error message (since the document is incompatible). If either message is handled, the document will be opened normally after the handler exits.

Note that the document control will not automatically change the protocol number for a file after it has been updated. If you wish this done, you should have the handler for the message call one of the routines to change the FEA_PROTOCOL extended attribute.

MSG_GEN_DOCUMENT_GROUP_GET_PROTOCOL

dword	MSG_GEN_DOCUMENT_GROUP_GET_PROTOCOL();

Use this message to get the protocol number associated with the GenDocumentGroup. A protocol number is composed of two parts, a major component and a minor component. This message returns a double-word; the high word is the major component, and the low word is the minor component.

Source: Unrestricted.

Destination: GenDocumentGroupClass.

Return: Returns a dword-sized value; the high word contains the major protocol number, and the low word contains the minor protocol number.

Interception: You should not subclass this message.

|  Up: GEOS SDK TechDocs | Up | Prev: 2.1 GenDocumentControl Data | Next: 2.3 GenDocument Attributes


GenDocument Attributes

Documents: 2.3 Document Control Data Fields: GenDocument Attributes

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 GenDocumentGroup Data | Next: 3 Basic DC Messages

There are very few GenDocumentClass attributes that you will need to be concerned with. The GenDocumentGroup object creates and updates document objects as needed. Ordinarily, the application will not look at the GenDocumentClass instance data. If the program defines a subclass of GenDocumentClass , the subclass's methods should use only the subclass's instance data.

There is only one attribute which the program should change at run-time, and that is the GDA_PREVENT_AUTO_SAVE bit of the GDI_attrs field. This bit can be changed with the messages MSG_GEN_DOCUMENT_DISABLE_AUTO_SAVE and MSG_GEN_DOCUMENT_ENABLE_AUTO_SAVE .

GenDocumentClass is a subclass of GenContentClass and has all the functionality of that class. Since GenContent objects are rarely used directly, the class does not have its own chapter; instead, it is documented in GenContentClass . The main thing to know about the GenContent is that, like a VisContent, it is displayed in a GenView and can have visible children. It can also have generic children, though it may not have both visible and generic children at the same time.

The GDI_attrs Field

GDI_attrs, MSG_GEN_DOCUMENT_GET_ATTRS, MSG_GEN_DOCUMENT_ENABLE_AUTO_SAVE, MSG_GEN_DOCUMENT_DISABLE_AUTO_SAVE, MSG_GEN_DOCUMENT_AUTO_SAVE

The GDI_attrs word contains flags indicating the status of the document. The application can read or change any of these attributes; however, only the attribute GDA_PREVENT_AUTO_SAVE should actually be changed at run-time.

GDA_READ_ONLY
This attribute is set if the document in question is opened for read-only access.
GDA_READ_WRITE
This attribute is set if the document is opened for read/write access.
GDA_FORCE_DENY_WRITE
If this attribute is set, while the document is open, no other process will be allowed to open that document for read/write access.
GDA_SHARED_MULTIPLE
The document is opened in "shared multiple" mode.
GDA_SHARED_SINGLE
The document is opened in "shared single" mode.
GDA_UNTITLED
The document is newly-created and has not yet been saved; it is still untitled.
GDA_DIRTY
The document has been marked dirty since the last time it was saved.
GDA_CLOSING
The document is in the process of being closed.
GDA_ATTACH_TO_DIRTY_FILE
The document object is being attached to a dirty file (e.g., when restarting GEOS).
GDA_SAVE_FAILED
The user attempted to save the document, and it could not be saved (e.g., someone else denied write access, or the volume was no longer accessible).
GDA_OPENING
The document is in the process of being opened.
GDA_AUTO_SAVE_STOPPED
Auto-save was stopped while in progress.
GDA_PREVENT_AUTO_SAVE
This bit can be changed by the application at run-time. While the bit is on, auto-save is disabled.

MSG_GEN_DOCUMENT_GET_ATTRS

GenDocumentAttrs 	MSG_GEN_DOCUMENT_GET_ATTRS();

Use this message to get the GDI_attrs flags for a given document. These attribute flags give information about the document's permissions as well as about any operations currently in progress.

Source: Unrestricted--objects subclassed from GenDocumentClass often send this message to themselves.

Destination: Any GenDocument object.

Return: The object's word-sized GDI_attrs field.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_DISABLE_AUTO_SAVE

void	MSG_GEN_DOCUMENT_DISABLE_AUTO_SAVE();

Sometimes an application needs to temporarily disable auto-save for a specific document (for example, if it is in the middle of making elaborate changes to the file). It can do this by sending this message to the document object. The document's GDA_PREVENT_AUTO_SAVE bit will be turned on, and auto-save will be disabled until the document receives a MSG_GEN_DOCUMENT_ENABLE_AUTO_SAVE .

Source: Unrestricted--objects subclassed from GenDocumentClass often send this message to themselves.

Destination: Any GenDocument object.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_ENABLE_AUTO_SAVE

void	MSG_GEN_DOCUMENT_ENABLE_AUTO_SAVE();

This message turns off a document's GDA_PREVENT_AUTO_SAVE bit. If the bit is already off (i.e., auto-save is enabled), the message has no effect.

Source: Unrestricted--objects subclassed from GenDocumentClass often send this message to themselves.

Destination: Any GenDocument object.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_AUTO_SAVE

void	MSG_GEN_DOCUMENT_AUTO_SAVE();

This message forces the document object to immediately auto-save its file.

Source: Unrestricted. The document object may send this message to itself.

Destination: Any GenDocument object.

Interception: This message is not generally subclassed.

The GDI_operation Attribute

GDI_operation, MSG_GEN_DOCUMENT_GET_OPERATION

A single user action can result in many routines being called and many messages being sent out. To help keep track of what's going on, GenDocumentClass has a byte-length field, GDI_operation . If the document control is in the midst of handling a user action for a given document, it will set the GDI_operation byte accordingly. The current operation is a member of the GenDocumentOperation enumerated type. This type has the following possible values:

GDO_NORMAL
This is the usual setting. If the document is not currently handling a user action, this is the setting.
GDO_SAVE_AS
When the user chooses "Save As," the byte is set to this value. It remains at this value until the new document has been opened and saved.
GDO_REVERT
The setting from the time the user chooses "Revert" until the file has been restored to its last-saved state.
GDO_REVERT_QUICK
The setting from the time the user chooses "restore from backup" until the file has been restored.
GDO_ATTACH
The setting while the UI is being created for and attached to a given document. When a file document is created or opened, the GDI_operation field is set to this after GEOS has opened the file, and it remains at this setting until the application finishes attaching the UI.
GDO_DETACH
The setting while the UI is being detached from a given document, but before the actual file is closed.
GDO_NEW
The setting while a new file is being created. When the file is created and initialized, the GDI_operation will change to GDO_ATTACH .
GDO_OPEN
The setting while an existing file is being opened. When the file is created and initialized, the GDI_operation will change to GDO_ATTACH .
GDO_SAVE
The setting while a document is being saved.
GDO_CLOSE
After the UI has been detached, the GDI_operation byte is set to this value until the document object is destroyed.
GDO_AUTO_SAVE
The setting while a file is being updated (i.e. auto-saved) to disk.

MSG_GEN_DOCUMENT_GET_OPERATION

GenDocumentOperation 	MSG_GEN_DOCUMENT_GET_OPERATION();

Use this message to find out what user action a given document object is in the midst of processing. This is useful if you are handling some message and want to find out the context in which that message was sent. Note that although the message returns a word-length value, the GDI_operation enumerated type is byte-length; it is thus safe to cast the return value to a byte-length variable.

Source: Unrestricted--objects subclassed from GenDocumentClass often send this message to themselves.

Destination: Any GenDocument object.

Return: Returns a member of the GenDocumentOperation enumerated type corresponding to the document object's current operation.

Interception: You should not subclass this message.

File Information

GDI_fileHandle, GDI_diskHandle, GDI_volumeName, GDI_fileName, MSG_GEN_DOCUMENT_GET_FILE_NAME, MSG_GEN_DOCUMENT_GET_FILE_HANDLE

The document object stores certain data about the file associated with it. In particular, the instance data records the document's path, its full file name, and the handles of the file and the disk volume containing the file. This data can be retrieved by sending messages to the document object.

MSG_GEN_DOCUMENT_GET_FILE_NAME

void	MSG_GEN_DOCUMENT_GET_FILE_NAME(
        char *buffer); /* Address to write file name to */

This message instructs a GenDocument to write the name of its file (without the path) to the specified address.

Source: Unrestricted--objects subclassed from GenDocumentClass often send this message to themselves.

Destination: Any GenDocument object

Parameters: buffer Buffer of length FILE_LONGNAME_BUFFER_SIZE.

Return: Writes file's virtual name into the passed buffer as a null-terminated string.

Warnings: Make sure the buffer passed is of length FILE_LONGNAME_BUFFER_SIZE; otherwise the method might overwrite other data.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GET_FILE_HANDLE

FileHandle 	MSG_GEN_DOCUMENT_GET_FILE_HANDLE();

This message returns the handle of the file associated with a given GenDocument object.

Source: Unrestricted--objects subclassed from GenDocumentClass often send this message to themselves.

Destination: Any GenDocument object.

Return: Returns handle of file associated with that document object.

Interception: You should not subclass this message.

GenContentClass

The GenContent generic object is similar to the VisContent visible object in that it interacts directly with the GenView. While the VisContent allows an application to display a visible hierarchy of objects within the view, however, the GenContent allows either generic or visible object hierarchies to be displayed. This is the one case where you may ordinarily have visible objects be children of a generic object. Note that you should not have both visible and generic objects as children of the same GenContent; if you do so, results are undefined.

Any GenView whose content is a GenContent (or subclass) should have its GVA_GENERIC_CONTENTS attribute set.

GenContentClass is a subclass of GenClass and therefore inherits all the instance data, messages, and hints of all generic objects. The GenContent also has two other instance data fields, however; these are

@instance byte			GCI_attrs = 0;
@instance optr			GCI_genView;

The GCI_attrs field contains a record of VisContentAttrs and is used by document objects for visual updates and interaction with the GenView. This record may be retrieved with MSG_GEN_CONTENT_GET_ATTRS or set with MSG_GEN_CONTENT_SET_ATTRS .

The GCI_genView field contains the optr of the GenView object displaying the GenContent. This, too, is used by document objects to manage interaction with the GenView.

MSG_GEN_CONTENT_GET_ATTRS

byte	MSG_GEN_CONTENT_GET_ATTRS();

This message returns the record of VisContentAttrs set in the GenContent's GCI_attrs field.

Source: Unrestricted.

Destination: Any GenContent object

Parameters: None.

Return: The GCI_attrs settings.

Interception: Unlikely.

MSG_GEN_CONTENT_SET_ATTRS

void	MSG_GEN_CONTENT_SET_ATTRS(
        byte	attrsToSet,
        byte	attrsToClear);

This message sets the attributes in the GenContent's GCI_attrs record.

Source: Unrestricted.

Destination: Any GenContent object.

Parameters: attrsToSet A record of VisContentAttrs indicating which flags should be set. Those set in attrsToSet will be set in GCI_attrs .

attrsToClear
A record of VisContentAttrs indicating which flags should be cleared. Those cleared in attrsToClear will be cleared in GCI_attrs . Note that if a flag is set in both parameters, it will end up cleared.

Return: Nothing.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 GenDocumentGroup Data | Next: 3 Basic DC Messages


Basic DC Messages

Documents: 3 Basic DC Messages

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Document Control Data Fields | Next: 4 Advanced DC Usage

The document control objects use messages for many things. Since well over half a dozen classes of objects (counting file selectors, GenDisplayGroup objects, menu triggers, etc.) and far more actual objects are involved in intricate tasks, many messages are continually sent back and forth. Most of these messages are transparent to the programmer. The programmer need only know about them if the program subclasses a message to add functionality to it; this is an advanced technique which few programs will ever need to use.

There are two basic types of messages the programmer needs to know about. First, there are messages which are sent to document control objects; these objects may query information, toggle some functionality, or otherwise instruct the DC objects to take some action. Second, there are messages the DC objects send when the programmer's code needs to take some action. Each type of message is treated in a separate section.

Other Document Group Messages

From the Doc Control Objects

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Document Control Data Fields | Next: 4 Advanced DC Usage


Other Document Group Messages

Documents: 3.1 Basic DC Messages: Other Document Group Messages

| Up: GEOS SDK TechDocs | Up | Prev: 3 Basic DC Messages | Next: 3.2 From the Doc Control Objects

The following are the messages a program might ordinarily send to the GenDocumentGroup object. Many of these messages request information about the GenDocumentControl object or the target document; others request information about the GenDocumentGroup object or instruct it to take actions. Many of the messages require, as an argument, an optr to a document object; however, a null object-pointer can be passed, thus indicating the target document. This is especially useful under single-document models; the application doesn't need to keep track of the document object's optr, since it is always the target document.

MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY

void	MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY(
        optr	document); /* document to mark dirty */

This message notifies the GenDocumentGroup object that the specified document has been dirtied. The GenDocumentGroup will enable and disable file menu triggers as appropriate. If the argument is a null pointer, the target document will be marked dirty.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: document optr of document to mark dirty. If a null optr is passed, the target document will be dirtied.

Interception: You should not subclass this message.

Tips: If the document is a VM file and the GenDocumentGroup attribute GDGA_AUTOMATIC_DIRTY_NOTIFICATION is set, the VM routines will notify the GenDocumentGroup that the document has been dirtied whenever the VMDirty() (or DBDirty() , CellDirty() , etc.) routine is called. However, if you change a data cache without changing the actual file, you should send this message (or MSG_GEN_DOCUMENT_GROUP_MA RK_DIRTY_BY_FILE) to insure that the changes to the cache will be saved.

MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY_BY_FILE

void	MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY_BY_FILE(
        FileHandle 	file); /* file handle of document to mark dirty */

This message notifies the GenDocumentGroup that the file with the specified handle has been dirtied. The GenDocumentGroup will enable and disable file menu triggers as appropriate. The document is specified by file handle, not document optr.

Source: Unrestricted

Destination: Any GenDocumentGroup object.

Parameters: file The handle of the file to dirty.

Interception: You should not subclass this message.

Tips: If the document is a VM file and the GenDocumentGroup attribute GDGA_AUTOMATIC_DIRTY_NOTIFICATION is set, the VM routines will notify the GenDocumentGroup that the document has been dirtied whenever the VMDirty() (or DBDirty() , CellDirty() , etc.) routine is called. However, if you change a data cache without changing the actual file, you should send this message (or MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY ) to insure that the changes to the cache will be saved.

MSG_GEN_DOCUMENT_GROUP_GET_DOC_BY_FILE

optr	MSG_GEN_DOCUMENT_GROUP_GET_DOC_BY_FILE(
        FileHandle 	file);

Use this message if you know the file handle of a document and you need to get an object-pointer to the document object corresponding to the file. In the inverse situation (you know the object, and need to get the file handle), send MSG_GEN_DOCUMENT_GET_FILE_HANDLE directly to the document object.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: file The handle of file whose document object is needed.

Return: Returns the optr of document object

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_SAVE_AS_CANCELLED

void	MSG_GEN_DOCUMENT_GROUP_SAVE_AS_CANCELLED();

If you are in the midst of handling a "Save As" operation, and you need to cancel it, send this message to the GenDocumentGroup object.

Source: Unrestricted

Destination: Any GenDocumentGroup object.

Interception: This message is not generally subclassed.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Basic DC Messages | Next: 3.2 From the Doc Control Objects


From the Doc Control Objects

Documents: 3.2 Basic DC Messages: From the Doc Control Objects

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Other Document Group Messages | Next: 4 Advanced DC Usage

Often the document control will need to notify the application to take an action. For example, when a document is created, the application needs to be told to initialize the document and the user interface. There are two basic models for handling these situations: the "Procedure" model and the "Object" model. (The differences between these models are discussed at more length in the section Document Control Models .) Each model has its own way of messaging.

Under the Procedure model, every time the application needs to be notified, the document control sends a message to the GenDocumentGroup's output object, which is ordinarily the process object. Under the Object model, the affected document object will send a message to itself; this message has no handler under GenDocumentClass , so the application must use a subclass of GenDocumentClass with handlers for these messages.

A single user action can generate several messages. For example, when the user opens a document, four messages are sent: MSG_..._PHYSICAL_OPEN , MSG_..._READ_CACHED_DATA_FROM_FILE , MSG_..._CREATE_UI_FOR_DOCUMENT , and MSG_..._ATTACH_UI_TO_DOCUMENT . Furthermore, a given message might be sent as the result of several different user actions; for example, the message MSG_..._CREATE_UI_FOR_DOCUMENT is sent when the user creates a new file or opens an existing one. If a handler needs to know what user action precipitated a given message, it can send a MSG_GEN_DOCUMENT_GET_OPERATION to the document object.

Messages Handled under the Procedure Model

Whenever the application needs to be notified to take an action, a message will be sent to the GenDocumentGroup's output object. Two arguments accompany such messages: A pointer to the relevant document object and the handle of the file associated with that document. All of these messages are exported from MetaClass , so they can be handled by objects of any class.

Each of these messages corresponds to a document-model message, all of which are described in Messages Handled under the Object Model . These are just the basic messages; for more advanced functionality, see the message listings in the advanced section.

MSG_META_DOC_OUTPUT_INITIALIZE_DOCUMENT_FILE

void	MSG_META_DOC_OUTPUT_INITIALIZE_FILE(
        optr		document, /* document object to initialize */
        FileHandle		file); /* Handle of file to initialize */

The GenDocumentGroup object sends this message out when a new document has been created and needs to be initialized. Applications which use VM files will allocate the map block and initialize it. If an application maintains data caches for its files, it should initialize the caches at this point.

Note that the handler for this message should not take any UI-related actions. These should be left to the handlers for MSG_META_DOC_OUTPUT_CREATE_UI_FOR_DOCUMENT and MSG_META_DOC_OUTPUT_ATTACH_UI_FOR_DOCUMENT .

Source: The GenDocumentGroup object.

Destination: The output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the document object which has just been created.

file
The FileHandle of the file which has just been created or opened.

Interception: If you are using the Procedure model, you must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_META_DOC_OUTPUT_CREATE_UI_FOR_DOCUMENT

void	MSG_META_DOC_OUTPUT_CREATE_UI_FOR_DOCUMENT(
        optr		document, /* Pointer to document object */
        FileHandle		file); /* Handle of file associated with
                 * document object */

The GenDocumentGroup object sends this message after a document has been created or opened. Before this message is sent, the GenDocumentControl object will enable those objects on its GDCI_enableDisableList , and the GenDocumentGroup object will copy the GenDisplay resource for the document (if one is defined).

Applications that use dynamic UI objects will commonly respond to this message by creating the objects for the newly-opened document. Applications that use static UI objects will commonly respond to this message by enabling the objects.

Source: The GenDocumentGroup object.

Destination: The output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: If you are using the Procedure model, you must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_META_DOC_OUTPUT_ATTACH_UI_TO_DOCUMENT

void	MSG_META_DOC_OUTPUT_ATTACH_UI_TO_DOCUMENT(
        optr		document, /* optr of document object */
        FileHandle 		file); /* handle of file for this document */

The GenDocumentGroup object sends this message when the UI for a newly-opened document has been created. It also sends this message when re-opening a document as part of restoring GEOS from a state file. Applications may respond to this by attaching dynamic UI objects and setting the values of static UI objects.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the document object.

file
The FileHandle of the appropriate file.

MSG_META_DOC_OUTPUT_DETACH_UI_FROM_DOCUMENT

void 	MSG_META_DOC_OUTPUT_DETACH_UI_FROM_DOCUMENT(
        optr		document, /* pointer to document object */
        FileHandle 		file); /* handle of file for this document */

The GenDocumentGroup sends this message when a document is being closed, whether because a user closes the file or because the application is being closed. It also sends this message when GEOS is in the process of saving itself to a state file prior to shutting down. Applications generally respond to this by detaching dynamic UI objects. Note that the GenDocumentControl object will automatically disable any objects in its GDCI_enableDisableList .

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: If you are using the Procedure model, you must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_META_DOC_OUTPUT_DESTROY_UI_FOR_DOCUMENT

void	MSG_META_DOC_OUTPUT_DESTROY_UI_FOR_DOCUMENT(
        optr	document, /* pointer to document object */
        FileHandle 	file); /* handle of file for this document */

The GenDocumentGroup object sends this message out when a document is being closed, whether because a user closes the file or because the application is being closed. Applications will generally disable static display objects and delete dynamic display objects. Note that the GenDocumentControl object will automatically disable all objects in its GDCI_enableDisableList , and the GenDocumentGroup will delete the display block it created for a document, if any.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: If you are using the Procedure model, you must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_META_DOC_OUTPUT_ATTACH_FAILED

void	MSG_META_DOC_OUTPUT_ATTACH_FAILED(
        optr	document, /* pointer to document object */
        FileHandle 	File); /* (former) handle of file for this document */

When GEOS restores itself from state, the document control tries to reattach all documents which were attached when GEOS was shut down. If this is impossible (as for example if a document was deleted after GEOS shut down), the GenDocumentGroup object will send this message to its output.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: If you are using the Procedure model, you must write a handler for this message in whatever class will be receiving it (usually the process class).

Messages Handled under the Object Model

If an application uses the Object model of document control, it will generally not handle the messages to the GenDocumentGroup's output. Instead, it will define a subclass of GenDocumentClass ; this subclass will have methods for those situations which require the application's attention. Note that GenDocumentClass does not have handlers for any of these messages; if the application does not define a method for a given message, that message will have no effect.

MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE

Boolean	MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE();

When a new document is created, the document object sends this message to itself. VM file based applications will generally respond to this message by allocating and initializing the map block. DOS file based applications will commonly initialize data structures for a default file. If an application maintains data caches for its files, it should initialize the caches at this point.

The application should not take any UI-related actions; those should be postponed until the messages MSG_GEN_DOCUMENT_CREATE_UI_FOR_DOCUMENT and MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT are received.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Parameters: Nothing.

Return: If the handler could not initialize the file, it should return true ; the document control will then destroy the new file.

MSG_GEN_DOCUMENT_CREATE_UI_FOR_DOCUMENT

void	MSG_GEN_DOCUMENT_CREATE_UI_FOR_DOCUMENT();

The GenDocument object sends this message to itself after a document has been created or opened. Before this message is sent, the GenDocumentControl object will enable those objects on its GDCI_enableDisableList , and the GenDocumentGroup object will copy the GenDisplay block for the document (if one is defined).

Applications that use dynamic UI objects will commonly respond to this message by creating the objects for the newly-opened document. Applications that use static UI objects will commonly respond to this message by enabling the objects.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT

void	MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT();

The document object sends this message to itself when the UI for a newly-opened document has been created. It also sends this message when re-opening a document as part of restoring GEOS from a state file. Applications may respond to this by attaching dynamic UI objects and setting the values of static UI objects.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT

void	MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT();

The document object sends this message when a document is being closed, whether because a user closes the file or because the application is being closed. It also sends this message when GEOS is in the process of saving itself to a state file prior to shutting down. Applications generally respond to this by detaching dynamic UI objects. Note that the GenDocumentControl object will automatically disable any objects in its GDCI_enableDisableList .

Source: A GenDocument object.

Destination: The document object sends this message to itself.

MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT

void	MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT();

The GenDocumentGroup object sends this message out when a document is being closed, whether because a user closes the file or because the application is being closed. Applications will generally disable static display objects and delete dynamic display objects. Note that the GenDocumentControl object will automatically disable all objects in its GDCI_enableDisableList , and the GenDocumentGroup will delete the display block it created for a document, if any.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

MSG_GEN_DOCUMENT_ATTACH_FAILED

void	MSG_GEN_DOCUMENT_ATTACH_FAILED();

When GEOS restores itself from state, the document control tries to reattach all documents which were attached when GEOS was shut down. If this is impossible (as for example if a document was deleted after GEOS shut down), the document object will send this message to itself.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Messages Associated with Common User Actions

A single user action can precipitate several application-handled messages. This section lists the messages associated with each of several common user actions. Note that some messages are sent as the result of many user actions. If a handler needs to find out what user action caused a message to be sent, it should send MSG_GEN_DOCUMENT_GET_OPERATION to the document object.

If a message is not ordinarily handled, it is enclosed in [brackets] below. These messages are documented in the advanced usage section. Actions taken by the document control objects (other than messages sent) are listed in italics . The messages listed are sent by the appropriate GenDocument object to itself. If the message is not subclassed by the GenDocument object, it sends a corresponding procedural-model message (of the form MSG_META_DOC_OUTPUT...) to the GenDocumentGroup object's output. The one exception is MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS; as noted on MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE , this message does not have a corresponding MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS.

  • New document is created:
    [ MSG_GEN_DOCUMENT_PHYSICAL_CREATE ]
    new file is created
    VM files: initialize     VM attributes, token, protocol
     MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE
     if saveAs/Revert supported, save file so revert will return to this state
     [ MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE ]
    [ MSG_GEN_DOCUMENT_PHYSICAL_SAVE ]
    MSG_GEN_DOCUMENT_CREATE_UI_FOR_DOCUMENT
    MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT
  • Document is opened:
    [ MSG_GEN_DOCUMENT_PHYSICAL_OPEN ]
    [ MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE ]
    MSG_GEN_DOCUMENT_CREATE_UI_FOR_DOCUMENT
    MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT
  • Document is saved:
    [ MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE ]
    [ MSG_GEN_DOCUMENT_PHYSICAL_SAVE ]
    VM files: call made to VMSave
  • Document is "Saved As":
    [ MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE ]
    [ MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS ]
    [DOS files: MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE ]
    VM files: VMSaveAs called
     [ MSG_GEN_DOCUMENT_SAVE_AS_COMPLETED ]
  • Document is reverted to last-saved version:
    MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
     [ MSG_GEN_DOCUMENT_PHYSICAL_REVERT ]
    VM : VMRevert called
     [ MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE ]
    MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT
  • Document is about to be closed:
    [MSG_GEN_DOCUMENT_PHYSICAL_CHECK_FOR_MODIFICATIONS]
If document is modified & user wants to save changes:
[ MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE ]
[ MSG_GEN_DOCUMENT_PHYSICAL_SAVE ]
VM : VMSave called
 MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT
If document is modified and user does not want to save changes:
MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
 [ MSG_GEN_DOCUMENT_PHYSICAL_REVERT ]
VM : VMRevert called
 MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT
 [ MSG_GEN_DOCUMENT_PHYSICAL_CLOSE ]
If document is not modified:
MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT
 [ MSG_GEN_DOCUMENT_PHYSICAL_CLOSE ]
If document is not modified and untitled:
MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT
 [ MSG_GEN_DOCUMENT_PHYSICAL_CLOSE ]
[ MSG_GEN_DOCUMENT_PHYSICAL_REVERT ]
  • GEOS restoring from state, document being attached:
    [ MSG_GEN_DOCUMENT_PHYSICAL_OPEN ]
    [ MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE ]
    MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT
  • GEOS restoring from state, attach failed:
    MSG_GEN_DOCUMENT_ATTACH_FAILED
    MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT
  • GEOS shutting down, document being detached:
    [ MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT ]
    [ MSG_GEN_DOCUMENT_PHYSICAL_UPDATE ]
    VM files: VMUpdate called
     MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
     [ MSG_GEN_DOCUMENT_PHYSICAL_CLOSE ]
| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Other Document Group Messages | Next: 4 Advanced DC Usage


Advanced DC Usage

Documents: 4 Advanced DC Usage

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Basic DC Messages | Next: 4.1 Document Protocols

By now, you should know enough for most uses of the document control. For single-document applications which manage GEOS Virtual Memory files and use a generic interface, the above documentation should be sufficient. However, there are some needs which require more advanced techniques. This section details these techniques.

This section discusses the use of document protocols to smooth the process of upgrading software. It also discusses using the document control to manage multiple documents simultaneously and to manage DOS files. Finally, it discusses those messages which an application might need to handle but would not ordinarily need to know about.

Document Protocols

Multiple Document Model

Working with DOS files

Special-Purpose Messages

Forcing Actions

Some Other Internal Messages

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Basic DC Messages | Next: 4.1 Document Protocols


Document Protocols

Documents: 4.1 Advanced DC Usage: Document Protocols

| Up: GEOS SDK TechDocs | Up | Prev: 4 Advanced DC Usage | Next: 4.2 Multiple Document Model
MSG_META_DOC_OUTPUT_OPEN_EARLIER_COMPATIBLE_DOCUMENT, MSG_META_DOC_OUTPUT_OPEN_EARLIER_INCOMPATIBLE_DOCUMENT, MSG_GEN_DOCUMENT_OPEN_EARLIER_COMPATIBLE_DOCUMENT, MSG_GEN_DOCUMENT_OPEN_EARLIER_INCOMPATIBLE_DOCUMENT

One difficulty in upgrading software is that an obsolete program may have created many documents. If the new version can't read those documents, people who used the old version will be inconvenienced; however, if the new versions always use the same document formats as the old versions, options for improvement will be limited. Above all, if document formats change, the new version should detect this gracefully, without crashing or damaging the old file.

The header for a GEOS Virtual Memory file contains two words for protocol numbers. The document control objects use the protocol numbers to insure that a document is compatible with the version of the application which is opening it. There are two parts to the protocol number: the major protocol number, and the minor protocol number. (If a document has a major protocol number of 3 and a minor number of 11, it is referred to has having protocol 3.11.) By convention, versions of an application with entirely compatible document formats will have the same major protocol number; if a new version of an application cannot read older documents without converting them in some way, it will have a higher major protocol number, and the minor number will be reset to zero.

When the GenDocumentGroup object opens a file, it checks the major and minor protocol numbers. It will then take appropriate action:

  • If the document's major and minor protocol numbers match the protocol attributes of the GenDocumentGroup object, the document will be opened normally.
  • If the document has a higher protocol number than the GenDocumentGroup (i.e. either the document has a higher major protocol number, or the document and the GenDocumentGroup have the same major protocol number and the document has a higher minor protocol number), the document control will display an appropriate alert box, after which it will close the file and delete the document object. (It will do all of this automatically, without any attention from the application.)
  • If the document has lower major protocol number than the document control, the document control will send MSG_META_DOC_OUTPUT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT (and a corresponding MSG_GEN_DOCUMENT_... ). If neither message is handled, or if a handler returns an error, the document control will display an alert box and will close the file and delete the document object.
  • If the document has the same major but a lower minor protocol number than the GenDocumentGroup, the document control will send MSG_META_DOC_OUTPUT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT (and a corresponding MSG_GEN_DOCUMENT_... ). After this, it will proceed normally, whether the messages were handled or not (since the document is presumed to be compatible). If a handler returns an error, it will close the file and free the document object.

Note that the document control will not change the file under any of these circumstances. In particular, if it opens an earlier document, it will not change the document's protocol number. If the application wishes to do this, it should do it explicitly (generally in the handlers for the " UPDATE_..._DOCUMENT " messages). The protocol numbers are among a file's extended attributes. For information about changing extended attributes, see the File System chapter.

MSG_META_DOC_OUTPUT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT

Boolean	MSG_META_DOC_OUTPUT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT(
        word *		error, 			/* Return error code from FileError */
        optr 		document,			/* pointer to document object */
        FileHandle 		file); 			/* handle of file opened */

The GenDocumentGroup object sends this message to its output when the user tries to open a document with the same major protocol number as the document control and a lower minor protocol number. Applications will commonly respond to this message by changing the document's protocol number to bring it up-to-date. If the application can't use the document, it should return true and set *error . (File access error codes are members of the FileError enumerated type, defined in file.h .) With an error, the document control will close the document unchanged. If the application successfully updates the document, it should return zero and set *error to zero.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object)

Parameters: error A pointer to a word in which an error should be returned.

document
The optr of the appropriate document object.
file
The FileHandle of the appropriate file.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: You must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_GEN_DOCUMENT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT

Boolean	MSG_GEN_DOCUMENT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT(
        word *	error); /* Return error code from FileError type */

The document object sends this message when the user tries to open a document with the same major protocol number as the document control and a lower minor protocol number. Applications will commonly respond to this message by changing the document's protocol number to bring it up-to-date. If the application can't use the document, it should return true and put an error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .) With an error, the document control will close the document unchanged. If the application successfully updates the document, it should return zero and set *error to zero.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Parameters: error A pointer to a word in which an error should be returned.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

MSG_META_DOC_OUTPUT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT

Boolean	MSG_META_DOC_OUTPUT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT(
                                        /* Return true if error */
        word *error, /* Return error code from FileError enum. type */
        optr document, /* pointer to document object */
        FileHandle file); /* handle of file opened */

The GenDocumentGroup object sends this message to its output when the user tries to open a document with a lower major protocol number than the document control. Applications respond to the message by making any changes to the document necessary to make it compatible with the application. The application should also change the document's protocol numbers. If the application can't use the document, it should return true and put an error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .) With an error, the document control will close the document unchanged. If the application successfully updates the document, it should return zero and set *error to zero.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: error A pointer to a word in which an error should be returned.

document
The optr of the appropriate document object.
file
The FileHandle of the appropriate file.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: You must write a handler for this message in whatever class will be receiving it (usually the process class)

MSG_GEN_DOCUMENT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT

Boolean	MSG_GEN_DOCUMENT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT(
        word *	error); /* Return error code from FileError type */

The document object sends this message when the user tries to open a document with a lower major protocol number than the document control. Applications respond to this message by making any changes to the document necessary to make it compatible with the application. The application should also change the document's protocol numbers. If the application can't use the document, it should return true and put an error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .) With an error, the document control will close the document unchanged. If the application successfully updates the document, it should return zero and set *error to zero.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Parameters: error A pointer to a word in which an error should be returned.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).
| Up: GEOS SDK TechDocs | Up | Prev: 4 Advanced DC Usage | Next: 4.2 Multiple Document Model


Multiple Document Model

Documents: 4.2 Advanced DC Usage: Multiple Document Model

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Document Protocols | Next: 4.3 Working with DOS files

The Object model of document control makes it easy to manage several documents at once. Effectively, each document object acts as a special-purpose application dedicated to handling one file. The display control lets the user change documents at will. The document control and display control take care of most of the nuts and bolts of file switching, so the application doesn't have to worry about them.

Subclassing GenDocumentClass

Under the object model of document control, a document's functionality is implemented as methods in the document class. Much of the switching between documents is thus transparent to the application. When the target changes, all user actions will result in messages being sent to the new target document; whenever a document gets a message, it knows the message pertains to itself, not some other document (and thus knows that it is the target document).

In order to implement this functionality, the application must declare a subclass of GenDocumentClass . This subclass will have its own methods for application-handled messages.

Using the Display Group

The simplest way to manage multiple documents is to use the display control. The display control lets the user change documents transparently to the application. In order to do this, the application must define a resource of objects which should be copied each time a document is opened or created. The GenDocumentGroup object's GDGI_genDisplay attribute should be set to point to a GenDisplay object in that resource; the GenDisplay should be set "not usable" and should be the head of a tree of objects. Also, the application should define an object of GenDisplayGroupClass , and the GenDocumentGroup's GDGI_genDisplayGroup attribute should be set to point to it. The display control will then automatically switch displays whenever the user chooses an entry from the display control.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Document Protocols | Next: 4.3 Working with DOS files


Working with DOS files

Documents: 4.3 Advanced DC Usage: Working with DOS files

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Multiple Document Model | Next: 4.4 Special-Purpose Messages
MSG_META_DOC_OUTPUT_PHYSICAL_SAVE, MSG_GEN_DOCUMENT_PHYSICAL_SAVE, MSG_META_DOC_OUTPUT_PHYSICAL_UPDATE, MSG_GEN_DOCUMENT_PHYSICAL_UPDATE, MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE, MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE, MSG_META_DOC_OUTPUT_PHYSICAL_REVERT, MSG_GEN_DOCUMENT_PHYSICAL_REVERT

The document control can be used to handle DOS files. However, there are special issues to be aware of. When you use GEOS Virtual Memory files, the system takes care of swapping sections of the file in and out of memory as needed. You can use high-level commands to mark parts of the file as dirty, and when you need the document saved, only the dirty sections will be copied to the disk. The details of reading from and writing to the disk are transparent to the application.

When you use DOS files, on the other hand, you have to take care of all of these details yourself. It is usually impractical to keep all of a document in memory at one time, so you have to have some way of managing the data (perhaps by creating a temporary VM file and copying the DOS file into that).

For this reason, the document control sends out messages when it does many low-level things (such as save files). If the application needs to take special actions, it can define handlers for these messages. Most of these messages can be ignored if you are working with GEOS files.

If you want to implement "Save As" and "Revert" for DOS files, you will have to do most of it by hand. If you leave "Save As" and "Revert" enabled, the Document Control will do some of the work for you. For example, when the user chooses "Save As", the Document Control will first present a File Selector, letting the user choose a file name and location. The Document Control will then create the new file. After this it will send out MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE and MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE, passing the handle of the newly-created file. The application is responsible for writing the current version of the document to the new file, and reverting the original file to its last-saved state. The Document Control will automatically close the original file and update all Document Control instance data as necessary.

MSG_META_DOC_OUTPUT_PHYSICAL_SAVE

Boolean	MSG_META_DOC_OUTPUT_PHYSICAL_SAVE(
        word *		error,
        optr 		document,
        FileHandle 		file);

If you need to take special steps to save a file, you should have a handler for either this message or MSG_GEN_DOCUMENT_PHYSICAL_SAVE . The handler should write the file completely to the disk. If an error occurs, return true and write the error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .)

If, for example, you copy a DOS file into a temporary VM file while you work on it, you would probably respond to this message by copying the data from the temporary file back to the DOS file.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: error A pointer to a word in which an error should be returned.

document
The optr of the appropriate document object.
file
The FileHandle of the appropriate file.

Interception: DOS-based applications must handle either this message or MSG_GEN_DOCUMENT_PHYSICAL_SAVE . Applications which use GEOS data files will generally not intercept this message.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

MSG_GEN_DOCUMENT_PHYSICAL_SAVE

Boolean	MSG_GEN_DOCUMENT_PHYSICAL_SAVE(
        word *	error); 		/* Error code from FileError type */

This message is sent when the user saves a file. If you need to take special steps to save a file, you should have a handler for either this message or MSG_META_DOC_OUTPUT_PHYSICAL_SAVE . The handler should write the file completely to the disk. If an error occurs, return true and write the error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .)

If, for example, you copy a DOS file into a temporary VM file while you work on it, you would probably respond to this message by copying the data from the temporary file back to the DOS file.

Source: The GenDocument object.

Destination: The document object sends this message to itself.

Parameters: error A pointer to a word in which an error should be returned.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: DOS-based applications must handle either this message or MSG_META_DOC_OUTPUT_PHYSICAL_SAVE . Applications which use GEOS data files will generally not intercept this message.

MSG_META_DOC_OUTPUT_PHYSICAL_UPDATE

Boolean	MSG_META_DOC_OUTPUT_PHYSICAL_UPDATE(occurred */
        word *		error, 			/* Error code from FileError type */
        optr 		document,			/* Pointer to document object */
        FileHandle 		file);			/* Handle of DOS file */

This message is sent when the file is auto-saved (if this is enabled), and when the document is detached as part of a GEOS shutdown. If you need to take special steps to save a file, you should have a handler for either this message or MSG_GEN_DOCUMENT_PHYSICAL_UPDATE . The handler should write the file completely to the disk. If an error occurs, return true and write the error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .)

If, for example, you copy a DOS file into a temporary VM file while you work on it, you would probably respond to this message by copying the data from the temporary file back to the DOS file.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: error A pointer to a word in which an error should be returned.

document
The optr of the appropriate document object.
file
The FileHandle of the appropriate file.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: DOS-based applications which will have auto-save capability must handle either this message or MSG_GEN_DOCUMENT_PHYSICAL_UPDATE . Applications which use GEOS data files will generally not intercept this message.

MSG_GEN_DOCUMENT_PHYSICAL_UPDATE

Boolean	MSG_GEN_DOCUMENT_PHYSICAL_UPDATE(
        word *	error); 		/* Error code from FileError type */

This message is sent when the file is auto-saved (if this is enabled), and when the document is detached as part of a GEOS shutdown. If you need to take special steps to save a file, you should have a handler for either this message or MSG_META_DOC_OUTPUT_PHYSICAL_UPDATE . The handler should write the file completely to the disk. If an error occurs, return true and write the error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .)

If, for example, you copy a DOS file into a temporary VM file while you work on it, you would probably respond to this message by copying the data from the temporary file back to the DOS file.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Parameters: error A pointer to a word in which an error should be returned.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: DOS-based applications which will have auto-save capability must handle either this message or MSG_META_DOC_OUTPUT_PHYSICAL_UPDATE . Applications which use GEOS data files will generally not intercept this message.

MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE

Boolean	MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE(
        word *		error,
        optr		document,
        FileHandle		file);

This message is sent when the Document Control is ready to "save-as" a DOS file. The Document Control will have asked the user what the new file should be, and will have created an appropriate file. The handler for this message must write the current version of the document to the new file, and restore the original file to its last-saved state.

Source: The GenDocumentGroup object.

Destination: The output of GenDocumentGroup (usually the Process object).

Parameters: error A pointer to a word in which an error code should be returned.

document
The optr of the appropriate document object.
file
The handle of the newly-opened file. The current version of the document should be saved to this file.

Return: true if an error occurred.

*file
A member of the FileError enumerated type (if an error occurred).

Interception: DOS-file applications must intercept this message (or MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE) if they wish to implement save-as/revert functionality.

MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE

Boolean	MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE(
        word *		error,
        FileHandle		file);

This message is sent when the Document Control is ready to "save-as" a DOS file. The Document Control will have asked the user what the new file should be, and will have created an appropriate file. The handler for this message must write the current version of the document to the new file, and restore the original file to its last-saved state.

Source: A GenDocument.

Destination: The GenDocument object sends this message to itself.

Parameters: error A pointer to a word in which an error code should be returned.

file
The handle of the newly-opened file. The current version of the document should be saved to this file.

Return: true if an error occurred.

*error
A member of the FileError enumerated type (if an error occurred).

Interception: DOS-file applications must intercept this message (or MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE) if they wish to implement save-as/revert functionality.

MSG_META_DOC_OUTPUT_PHYSICAL_REVERT

Boolean	MSG_META_DOC_OUTPUT_PHYSICAL_REVERT(
        word *		error,
        optr		document,
        FileHandle		file);

The Document Control sends this message to revert a DOS file to its last-saved state. The handler must restore the file to its condition as of the last time it was saved.

Source: The GenDocumentGroup object.

Destination: The output of GenDocumentGroup (usually the Process object).

Parameters: error A pointer to a word in which an error should be returned.

document
The optr of the appropriate document object.
file
The FileHandle of the appropriate file.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: DOS-file applications must intercept this message (or MSG_GEN_DOCUMENT_PHYSICAL_REVERT) if they wish to implement save-as/revert functionality.

MSG_GEN_DOCUMENT_PHYSICAL_REVERT

Boolean	MSG_GEN_DOCUMENT_PHYSICAL_REVERT(
        word *		error,
        FileHandle		file);

The Document Control sends this message to revert a DOS file to its last-saved state. The handler must restore the file to its condition as of the last time it was saved.

Source: The GenDocumentGroup object.

Destination: The output of GenDocumentGroup (usually the Process object).

Parameters: error A pointer to a word in which an error should be returned.

file
The FileHandle of the appropriate file.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: DOS-file applications must intercept this message (or MSG_META_DOC_OUTPUT_PHYSICAL_REVERT) if they wish to implement save-as/revert functionality.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Multiple Document Model | Next: 4.4 Special-Purpose Messages


Special-Purpose Messages

Documents: 4.4 Advanced DC Usage: Special-Purpose Messages

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Working with DOS files | Next: 4.5 Forcing Actions

In addition to the basic messages discussed above, there are messages the document control sends out which do not ordinarily need to be handled. Some of these messages have been discussed above; most of the rest are described here.

Caching Data in Memory

MSG_META_DOC_OUTPUT_WRITE_CACHED_DATA_TO_FILE, MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE, MSG_META_DOC_OUTPUT_READ_CACHED_DATA_FROM_FILE, MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE

Sometimes an application will want to keep frequently-accessed data in memory. For example, if you are managing Virtual Memory files, you may want to copy the map block to a fixed memory block instead of locking the block every time you need to read or change it. This is known as caching data.

If you cache data, you must make sure that the application's version of the data is consistent with the disk file. The document control helps keep track of this. Whenever the file (or the state) is saved, the document control will first send a message instructing the application to write the cache to the file, then it will save the file. Similarly, when the file is opened or GEOS is restarted from state, the document control will send a message instructing the application to reload the cached data from the file.

There is one special concern. The user cannot save a file unless it has been marked dirty; also, the document control does not send MSG_..._WRITE_CACHED_DATA_TO_FILE to documents which are not dirty. Therefore, if you change the data cache without actually altering the file, you should send a MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY to the GenDocumentGroup.

MSG_META_DOC_OUTPUT_READ_CACHED_DATA_FROM_FILE

void	MSG_META_DOC_OUTPUT_READ_CACHED_DATA_FROM_FILE(
        optr 		document,			/* optr of document object */
        FileHandle 		file);			/* FileHandle of associated file */

The GenDocumentGroup sends this message when the document needs to read cached data. In particular, it sends this when a document is opened, when a document is reverted to its last-saved state, and when a document is re-opened as GEOS restores from state. If the application maintains a data cache, it should read the data from the file at this point. If the document does not cache data, it can ignore this message.

Note that if the document control notices that the file has changed on disk, it will not send this message; it will, however, send a MSG_META_DOC_OUTPUT_DOCUMENT_HAS_CHANGED . The handler for that message should reread the cache or call the handler for this message.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: You must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE

void	MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE();

The document object sends this message to itself when the document needs to read cached data. In particular, it sends this when a document is opened, when a document is reverted to its last-saved state, and when a document is re-opened as GEOS restores from state. If the application maintains a data cache, it should read the data from the file at this point. If the document does not cache data, it can ignore this message.

Note that if the document control notices that the file has changed on disk, it will not send this message; it will, however, send a MSG_GEN_DOCUMENT_DOCUMENT_HAS_CHANGED . The handler for that message should reread the cache or call the handler for this message.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

MSG_META_DOC_OUTPUT_WRITE_CACHED_DATA_TO_FILE

void	MSG_META_DOC_OUTPUT_WRITE_CACHED_DATA_TO_FILE(
        optr 		document,			/* optr of document object */
        FileHandle 		file);			/* FileHandle of associated file */

The GenDocumentGroup object sends this message when the document needs to write cached data back to the file. In particular, it sends this message just before a document is saved, auto-saved, or "Saved As," and before the document is closed as GEOS shuts down. The document should write its cached data back to the file. If the document does not cache data, it can ignore this message.

Warnings: This message will not be sent if the document is clean. Therefore, if you change the data cache without changing the file, you should send a MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY_BY_FILE to the GenDocumentGroup object.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object)

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: You must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE

void	MSG_META_DOC_OUTPUT_WRITE_CACHED_DATA_TO_FILE();

The document object sends this message to itself when the document needs to write cached data back to the file. In particular, it sends this message just before a document is saved, auto-saved, or "Saved As," and before the document is closed as GEOS shuts down. The document should write its cached data back to the file. If the document does not cache data, it can ignore this message.

Warnings: This message will not be sent if the document is clean. Therefore, if you change the data cache without changing the file, you should send a MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY_BY_FILE to the GenDocumentGroup object.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Other Messages To Know About

There are a few other messages which are worth knowing about. These messages alert the application to special situations. Most applications can ignore these messages; however, for a few, these messages should be handled.

MSG_META_DOC_OUTPUT_SAVE_AS_COMPLETED

void	MSG_META_DOC_OUTPUT_SAVE_AS_COMPLETED(
        optr 		document,			/* optr of document object */
        FileHandle 		file);			/* FileHandle of associated file */

The GenDocumentGroup object sends this message when a "Save As" operation has been successfully completed.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: You must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_GEN_DOCUMENT_SAVE_AS_COMPLETED

void	MSG_GEN_DOCUMENT_SAVE_AS_COMPLETED();

The document object sends this message to itself when a "Save As" operation has been successfully completed.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

MSG_META_DOC_OUTPUT_DOCUMENT_HAS_CHANGED

void	MSG_META_DOC_OUTPUT_DOCUMENT_HAS_CHANGED(
        optr 		document,			/* optr of document object */
        FileHandle 		file);			/* FileHandle of associated file */

If the GDGA_AUTOMATIC_CHANGE_NOTIFICATION attribute of the GenDocumentGroup object is set to on, a timer will periodically check to see if any open documents have been changed by another application. If they have, the GenDocumentGroup object will send this message out. The application should respond by redisplaying the data on the screen and rereading any cached data from the file.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: You must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_GEN_DOCUMENT_DOCUMENT_HAS_CHANGED

void	MSG_GEN_DOCUMENT_DOCUMENT_HAS_CHANGED();

If the GDGA_AUTOMATIC_CHANGE_NOTIFICATION attribute of the GenDocumentGroup object is set to on, a timer will periodically check to see if any open documents have been changed by another application. If they have, the document object will send this message out. The application should respond by redisplaying the data on the screen and rereading any cached data from the file.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Working with DOS files | Next: 4.5 Forcing Actions


Forcing Actions

Documents: 4.5 Advanced DC Usage: Forcing Actions

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Special-Purpose Messages | Next: 4.6 Some Other Internal Messages
MSG_GEN_DOCUMENT_CONTROL_DISPLAY_DIALOG, MSG_GEN_DOCUMENT_CONTROL_INITIATE_NEW_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_USE_TEMPLATE_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_USE_TEMPLATE_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_OPEN_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_IMPORT_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_AS_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_AS_TEMPLATE_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_MOVE_TO_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_COPY_TO_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_EXPORT_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_SET_TYPE_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_SET_PASSWORD_DOC

If you wish, you can force the document control to take certain actions as if the user had requested them. You do this by sending the message which would ordinarily trigger such an action. For example, when the user selects the "save" trigger, that trigger sends the message MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_DOC to the Document Control object. If you wish, you can force a save by sending this message manually; the document control will behave as if the user had selected that action.

MSG_GEN_DOCUMENT_CONTROL_DISPLAY_DIALOG

void	MSG_GEN_DOCUMENT_CONTROL_DISPLAY_DIALOG();

This message forces the document control to display the opening "New/Open" dialog box, as if the user had selected the "New/Open" trigger on the File menu.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_NEW_DOC

void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_NEW_DOC();

This message forces the document control to create a new file, exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_USE_TEMPLATE_DOC

void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_USE_TEMPLATE_DOC();

This message forces the document control to create a new file from a template (bringing up an appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_OPEN_DOC

void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_OPEN_DOC();

This message forces the document control to open a file (bringing up the appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_IMPORT_DOC

void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_IMPORT_DOC();

This message forces the document control to import a file (bringing up the appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_DOC

void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_DOC();

This message forces the document control to save the active file, exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_AS_DOC

void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_AS_DOC();

This message forces the document control to save the active file under a new name (bringing up the appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_AS_TEMPLATE_DOC

void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_AS_TEMPLATE_DOC();

This message forces the document control to save the active file as a template (bringing up the appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_MOVE_TO_DOC

void MSG_GEN_DOCUMENT_CONTROL_INITIATE_MOVE_TO_DOC();

This message forces the document control to move the active file to a new name and/or location (bringing up the appropriate file selector), exactly as if the user had requested it.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_COPY_TO_DOC

void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_COPY_TO_DOC();

This message forces the document control to copy the active file to a new name (bringing up the appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_EXPORT_DOC

void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_EXPORT_DOC();

This message forces the document control to export the active file (bringing up the appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_SET_TYPE_DOC

void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_SET_TYPE_DOC();

This message forces the document control to change the type (public, read-only, etc.) of the active file (bringing up the appropriate dialog box), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_SET_PASSWORD_DOC

void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_SET_PASSWORD_DOC();

This message forces the document control to set the password of the active file (bringing up the appropriate dialog box), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Special-Purpose Messages | Next: 4.6 Some Other Internal Messages


Some Other Internal Messages

Documents: 4.6 Advanced DC Usage: Some Other Internal Messages

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Forcing Actions
DocumentCommonParams, MSG_GEN_DOCUMENT_NEW, MSG_GEN_DOCUMENT_OPEN, MSG_GEN_DOCUMENT_CLOSE, MSG_GEN_DOCUMENT_QUICK_BACKUP, MSG_GEN_DOCUMENT_RECOVER_QUICK_BACKUP, MSG_GEN_DOCUMENT_SAVE, MSG_GEN_DOCUMENT_SAVE_AS, MSG_GEN_DOCUMENT_SAVE_AS_TEMPLATE, MSG_GEN_DOCUMENT_COPY_TO, MSG_GEN_DOCUMENT_REVERT, MSG_GEN_DOCUMENT_EDIT_USER_NOTES, MSG_GEN_DOCUMENT_CHANGE_TYPE, MSG_GEN_DOCUMENT_CHANGE_PASSWORD, MSG_GEN_DOCUMENT_RENAME, MSG_GEN_DOCUMENT_SET_EMPTY, MSG_GEN_DOCUMENT_CLEAR_EMPTY, MSG_GEN_DOCUMENT_SET_DEFAULT, MSG_GEN_DOCUMENT_CLEAR_DEFAULT, MSG_GEN_DOCUMENT_CLOSE_IF_CLEAN_UNNAMED, MSG_GEN_DOCUMENT_SEARCH_FOR_DOC, MSG_GEN_DOCUMENT_TEST_FOR_FILE, MSG_GEN_DOCUMENT_AUTO_SAVE, MSG_GEN_DOCUMENT_UPDATE, MSG_GEN_DOCUMENT_CHECK_FOR_MODIFICATIONS, MSG_GEN_DOCUMENT_CLOSE_FILE, MSG_GEN_DOCUMENT_REOPEN_FILE, MSG_GEN_DOCUMENT_GET_VARIABLE, MSG_GEN_DOCUMENT_REVERT_NO_PROMPT, MSG_GEN_DOCUMENT_FILE_CHANGED_REINITIALIZE_CREATED_UI, MSG_GEN_DOCUMENT_CLOSE_IF_OPEN_FOR_IACP_ONLY, MSG_GEN_DOCUMENT_MOVE_TO

The following messages are for internal use by the Document Control objects only. The GenDocumentControl object sends each of these messages to an appropriate GenDocument object to instruct it to perform a certain action; no intervention by the application is necessary, or appropriate. However, some applications may wish to subclass one or more of these messages, simply to find out when the message is being sent (and thus when a particular action is being performed). Any application which does this must make sure to call the superclass without changing any of the arguments.

Several of these messages pass, as an argument, a structure of type DocumentCommonParams . This structure contains information about the file associated with the document object. If you intercept any of these messages, you may wish to examine this structure. However, be sure that you do not alter any of its fields. The structure has the following definition:

 typedef struct {
    FileLongName			DCP_name;
    DiskHandle			DCP_diskHandle;
    PathName			DCP_path;
    GenDocumentAttrs			DCP_docAttrs;
    DocumentOpenFlags			DCP_flags;
    IACPConnection			DCP_connection;
} DocumentCommonParams;
DCP _name
The name of the document in question.
DCP _diskHandle
The dosk handle of the disk (or standard path) containing the document.
DCP _path
The path to the document (relative to the disk handle in DCP _diskHandle ).
DCP _docAttrs
The GenDocumentAttrs of the document in question.
DCP _flags
The DocumentOpenFlags of the document in question.
DCP _connection
If the document is opened for an IACP connection, this field specifies the IACP connection that requestged the document be opened. If the user opened the document directly, this field will be zero. (This field is only set when the structure is passed with MSG_GEN_DOCUMENT_OPEN or MSG_GEN_DOCUMENT_SEARCH_FOR_DOC; otherwise, the field's value is undefined.)

MSG_GEN_DOCUMENT_NEW

Boolean	MSG_GEN_DOCUMENT_NEW(
        word *			fileOrError,
        DocumentCommonParams *			params);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to create a new document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: params A pointer to a DocumentCommonParams structure. This structure specifies the name and location for the file.

Return: true (i.e., non-zero) if error.

*fileOrError
If return value is false , this will be the file handle of the new document. If return value is true , this will be a member of the FileError enumerated type.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

Structures: The DocumentCommonParams structure (described on typedef struct { FileLongName DCP_name; ).

MSG_GEN_DOCUMENT_OPEN

Boolean	MSG_GEN_DOCUMENT_OPEN(
        word *			fileOrError,
        DocumentCommonParams *			params);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to create a new document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: params A pointer to a DocumentCommonParams structure. This structure specifies the name and location for the file.

Return: true (i.e., non-zero) if error.

*fileOrError
If return value is false , this will be the file handle of the new document. If return value is true , this will be a member of the FileError enumerated type.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

Structures: The DocumentCommonParams structure (described on typedef struct { FileLongName DCP_name; ).

MSG_GEN_DOCUMENT_CLOSE

DocQuitStatus 				MSG_GEN_DOCUMENT_CLOSE(
        IACPConnection		connection);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to close a document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: IACPConnection The IACP connection requesting the close (or zero if the close was requested by a user).

Return: A member of the DocQuitStatus enumerated type (described below).

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

Structures: The return value is a member of the DocQuitStatus enumerated type. This type has the following members:

DQS_OK
The documentwas updated and closed successfully.
DQS_CANCEL
The close operation was canceled before it could be completed.
DQS_DELAYED
The close operation has been delayed.
DQS_SAVE_ERROR
The handler was unable to update the file to the disk.

MSG_GEN_DOCUMENT_QUICK_BACKUP

void	MSG_GEN_DOCUMENT_QUICK_BACKUP();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to make a quick backup of a document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_RECOVER_QUICK_BACKUP

void	MSG_GEN_DOCUMENT_RECOVER_QUICK_BACKUP();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to recover a document from a quick backup.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_SAVE

Boolean	MSG_GEN_DOCUMENT_SAVE();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to save a document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: Nothing.

Return: true if error.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_SAVE_AS

Boolean MSG_GEN_DOCUMENT_SAVE_AS(
        word *			fileOrError,
        DocumentCommonParams *			params);

The Document Control object sends this message to a Document object to instruct it to "Save As" its document. The DocumentCommonParams structure specifies the new name and location for the file.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: fileOrError A pointer to a word-sized variable.

params
A pointer to a DocumentCommonParams structure specifying the new name and location for the file.

Return: true if an error occurred.

*fileOrError
If the routine returned false , this contains the file handle of the new file. If it returned true , this contains a FileError code.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_SAVE_AS_TEMPLATE

Boolean MSG_GEN_DOCUMENT_SAVE_AS_TEMPLATE(
        word *			fileOrError,
        DocumentCommonParams *			params);

The Document Control object sends this message to a Document object to instruct it to save its document as a template. The DocumentCommonParams structure specifies the name and location for the template.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: fileOrError A pointer to a word-sized variable.

params
A pointer to a DocumentCommonParams structure specifying the new name and location for the file.

Return: true if an error occurred.

*fileOrError
If the routine returned false , this contains the file handle of the new file. If it returned true , this contains a FileError code.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_COPY_TO

Boolean	MSG_GEN_DOCUMENT_COPY_TO(
        DocumentCommonParams *			params);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to copy the current document to a new location, while keeping the original copy open. The DocumentCommonParams structure specifies the name and location for the copy.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: params A pointer to a DocumentCommonParams structure specifying the new name and location for the file.

Return: true if error.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_REVERT()

Boolean	MSG_GEN_DOCUMENT_REVERT();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to "Revert" its document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: true if error.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_EDIT_USER_NOTES

void	MSG_GEN_DOCUMENT_EDIT_USER_NOTES();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to perform an "Edit User Notes" operation.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CHANGE_TYPE

void	MSG_GEN_DOCUMENT_CHANGE_TYPE(
        GenDocumentType		type);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to change its document's type to the type specified in type .

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: type The new type of the document.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

Structures: A member of the GenDocumentType enumerated type. This type has the following members:

GDT_NORMAL
The document is a normal document.
GDT_READ_ONLY
The document is available for read-only access.
GDT_TEMPLATE
The document is a template.
GDT_READ_ONLY_TEMPLATE
The document is a template, and is opened read-only.
GDT_PUBLIC
The document is "public" (and thus is, by default, opened read-only).
GDT_MULTI_USER
The document can be read by several users at once.

MSG_GEN_DOCUMENT_CHANGE_PASSWORD

void	MSG_GEN_DOCUMENT_CHANGE_PASSWORD(
        char *		password);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to change the document's password.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: password A pointer to a null-terminated string containing the document's new password.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_RENAME

void	MSG_GEN_DOCUMENT_RENAME();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to rename its document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_SET_EMPTY

void	MSG_GEN_DOCUMENT_SET_EMPTY();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to make its document the new model for "empty" documents.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CLEAR_EMPTY

void	MSG_GEN_DOCUMENT_CLEAR_EMPTY();

The GenDocumentControl object sends this message to the GenDocument object to inform it that there is no longer a file to serve as the model for "empty" documents.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_SET_DEFAULT

void	MSG_GEN_DOCUMENT_SET_DEFAULT();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to make its document the default document to open on startup.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CLEAR_EMPTY

void	MSG_GEN_DOCUMENT_CLEAR_EMPTY();

The GenDocumentControl object sends this message to the GenDocument object to inform it that there is no longer a default file to open on startup.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CLOSE_IF_CLEAN_UNNAMED

void	MSG_GEN_DOCUMENT_CLOSE_IF_CLEAN_UNNAMED();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to close its document if the document has not been changed or saved since it was created.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_SEARCH_FOR_DOC

Boolean	MSG_GEN_DOCUMENT_SEARCH_FOR_DOC(
        DocumentCommonParams *			params);

The GenDocumentControl object sends this message to the GenDocument object to check if it has the specified document open, and bring it to the front if that document exists.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: params A DocumentCommonParams structure specifying the document sought. The DCP_connection field specifies the IACP connection looking for that document (or zero if the request came directly from the user).

Return: true if the Document object has that document open.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

Structures: The DocumentCommonParams structure (described on typedef struct { FileLongName DCP_name; ).

MSG_GEN_DOCUMENT_TEST_FOR_FILE

Boolean	MSG_GEN_DOCUMENT_TEST_FOR_FILE(
        FileHandle		file,
        optr		*docFound);

The GenDocumentControl object sends this message to the GenDocument object to ask if that Document has the file with the specified handle open. If it does, the GenDocument is to write its optr to the passed variable.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: file The FileHandle of the file being sought.

docFound
A pointer to an optr-sized variable.

Return: true if the Document has the specified file open.

*docFound
The optr of the Document object, if the Document has the specified file open; otherwise, this variable is left unchanged.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_AUTO_SAVE

void	MSG_GEN_DOCUMENT_

The GenDocumentControl object sends this message to the GenDocument object to instruct it to

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_UPDATE

Boolean	MSG_GEN_DOCUMENT_UPDATE(
        word *		error);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to write changes in its document to the disk.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: error A pointer to a word-sized variable.

Return: true if an error occurred.

*error
A FileError code, or zero if no error occurred.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CHECK_FOR_MODIFICATIONS

void	MSG_GEN_DOCUMENT_CHECK_FOR_MODIFICATIONS();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to check if its file has been modified on the disk.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CLOSE_FILE

void	MSG_GEN_DOCUMENT_CLOSE_FILE(
        IACPConnection		connection);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to close its file, without removing the document object itself. (This is done, e.g., when the Document performs a "Move To"; it must close the file, move it, and reopen it.)

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: connection The IACP connection requesting that the file be closed, or zero if the request came directly from the user.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_REOPEN_FILE

void	MSG_GEN_DOCUMENT_REOPEN_FILE();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to reopen its file (after having handled a MSG_GEN_DOCUMENT_CLOSE_FILE).

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_GET_VARIABLE

void	MSG_GEN_DOCUMENT_GET_VARIABLE(
        VisTextGraphic *		graphic,
        char *		buffer);

The GenDocumentControl object sends this message to the GenDocument object to get information about a variable graphic. (This message is used by the text object to implement variable graphics.)

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: graphic A pointer to a VisTextGraphic structure.

char
A pointer to a 100-byte buffer.

Return: Both buffers filled in appropriately.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_REVERT_NO_PROMPT

void	MSG_GEN_DOCUMENT_REVERT_NO_PROMPT();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to "Revert" its document without prompting the user.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: true if error.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_FILE_CHANGED_REINITIALIZE_CREATED_UI

void	MSG_GEN_DOCUMENT_FILE_CHANGED_REINITIALIZE_CREATED_UI();

The GenDocumentControl object sends this message to the GenDocument object to inform it that its associated file has changed, and it should reinitialize its UI and reload any cached data.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CLOSE_IF_OPEN_FOR_IACP_ONLY

void	MSG_GEN_DOCUMENT_CLOSE_IF_OPEN_FOR_IACP_ONLY()

The GenDocumentControl object sends this message to the GenDocument object to instruct it to close its document if the document is open only for an application-mode IACP connection.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_MOVE_TO

Boolean	MSG_GEN_DOCUMENT_MOVE_TO(
        DocumentCommonParams			params);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to perform a "Move To" operation. The DocumentCommonParams structure specifies the new name and location for the file.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: params A pointer to a DocumentCommonParams structure. This structure specifies the new name and location for the file.

Return: true (i.e. non-zero) if error.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

Structures: The DocumentCommonParams structure (described on typedef struct { FileLongName DCP_name; ).

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Forcing Actions


The File System

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing

Every operating system needs a way to interact with files. Files are used to hold both data and executable code. They are also the simplest way of transferring data from one computer to another.

GEOS provides powerful file-management functionality. It runs on top of a disk-operating system, and uses that DOS to read files from different media. Applications which run under GEOS need only interact with the GEOS file-management system; they are insulated from the differences between versions of DOS. They are also insulated from the differences between various file-storage media: CD-ROM drives, network file servers, and floppy and hard disks all present the same interface.

The GEOS file system provides functionality that many versions of DOS do not have. It allows the use of virtual directories, so (for example) the system's FONT directory could actually comprise several physical directories. GEOS files have functionality which DOS files lack. For example, GEOS provides support for file-sharing and data-access synchronization across networks. Nevertheless, GEOS lets applications access standard DOS files and directories when desired.

You may not need to use much of the file system directly. The document control and file-selector objects can let the user select and open files transparently to the application. Many applications will never need to negotiate the directory structure.

1 Design Philosophy
2 File System Overview
3 Disks and Drives
3.1 Accessing Drives
3.2 Accessing Disks
4 Directories and Paths
4.1 Standard Paths
4.2 Current Path and Directory Stack
4.3 Creating and Deleting Directories
5 Files
5.1 DOS Files and GEOS Files
5.2 Files and File Handles
5.3 GEOS Extended Attributes
5.4 File Utilities
5.5 FileEnum()
5.6 Bytewise File Operations

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing


Design Philosophy

The File System: 1 Design Philosophy

| Up: GEOS SDK TechDocs | Up | Prev: The File System | Next: 2 File System Overview

The GEOS file system was designed to meet two goals. First, the file system should insulate geodes from differences in hardware, making them device independent; and second, it should give geodes all the functionality they might plausibly want without being cumbersome.

One of the hallmarks of GEOS is its ability to insulate applications from differences in hardware. The GEOS file system plays a large role in this. Geodes which need access to files make their requests to the GEOS file system, which accesses the actual storage media. The virtues of this approach are several:

  • Geodes can rely on a consistent API for accessing media. When an application needs to open a file, it uses exactly the same techniques whether the file resides on a hard or floppy disk, a network drive, or some other medium.
  • GEOS can easily expand to make use of new technologies. To support a new storage medium, GEOS just needs a new device driver; all existing application binaries will automatically work with the new device.
  • Applications need not worry about what DOS the system might be running under. The GEOS file system does all interaction with the DOS and automatically takes advantages of each DOS' strengths. To support new versions of DOS, GEOS just needs new drivers.

GEOS is also flexible. Flexibility means more than just being able to use new technology in the same old ways; it means providing functionality to serve unforeseen purposes. GEOS provides this:

  • The file system allows such advanced functionality as virtual paths and directories. Files can thus be distributed across various media (for example, templates and fonts on CD-ROM drives, company files on a network drive, other documents on local hard disks) transparently to both user and application.
  • Other functionality can be added. For example, all GEOS files can have names up to 32 characters long, regardless of strictures on individual media. The file system maps the GEOS "virtual names" to device-dependent "native names" transparently to the user and application.

While GEOS provides this advanced functionality, it still lets geodes access the raw files. If a geode wants to access any file as a sequence of bytes (the DOS model), it may do so. Similarly, geodes may work with physical DOS directories if they wish to.

| Up: GEOS SDK TechDocs | Up | Prev: The File System | Next: 2 File System Overview


File System Overview

The File System: 2 File System Overview

| Up: GEOS SDK TechDocs | Up | Prev: 1 Design Philosophy | Next: 3 Disks and Drives

The GEOS file system manages all access to files on any type of storage. Whenever a geode needs to access a data file, it calls a file system routine. The file system makes any necessary calls to the computer's DOS. Like much of the GEOS system, it is driver-based; this makes it easy to expand GEOS to accommodate new technologies.

When a geode needs to access a file, it makes a call to the file system. The file system sends requests through the appropriate driver. For example, if the geode needs access to a file on a local hard or floppy disk drive, the file system will send commands through a DOS driver, which will in turn issue appropriate commands to the DOS itself. Similarly, if the file resides on a network drive, the file system will send commands through an appropriate network driver, which will translate them into corresponding commands to the network server.

Every storage device is known as a drive. A drive can be either fixed-media or movable-media. Fixed media drives are always associated with a specific storage medium; in effect, the drive is the same as the storage. The primary example of this is the conventional hard disk. Movable-media drives use a potentially unlimited number of storage objects. Examples include floppy disk drives, CD-ROM drives, and cartridge hard drives.

One way of organizing storage objects is to divide them into volumes . Ordinarily, every disk (floppy or hard) is a single volume; however, hard disks are sometimes divided into several volumes. Network storage devices are frequently divided into several volumes. If a single drive is partitioned into several volumes, DOS and GEOS treat each volume as a separate drive. In this chapter, the words volume and disk will be used interchangeably.

Individual volumes are organized with directories. A directory may contain several different files or directories. There is not usually a limit on the number of files a directory may contain; however, no two files or directories belonging to a given directory may have the same name.

Every volume is organized in a directory tree . The topmost directory is known as the root ; it is unnamed, since every volume contains exactly one root directory. The root may contain files or directories. Those directories may themselves contain files or directories, and so on. The total number of files is usually limited only by the size of the storage device.

To specify a file, you need to specify three things: The volume the file resides on, the directory to which the file belongs, and the file's name. In most DOS environments, a volume is generally specified with a letter followed by a colon (for example, the first hard disk is generally specified as "C:"). For non-fixed media (e.g. floppy disks), the letter actually specifies the drive; the volume is presumed to be in that drive. In GEOS, volumes and drives have different identifying systems. Every drive is identified by a drive number . This is a positive integer; the first drive has a number of zero, the second is drive one, and so on. Every volume is identified by a token (the disk handle ).

Specifying the directory is a little trickier. There may be many directories with the same name on a given volume. For this reason, the directory is specified with a path. The path begins with the root directory; the root is followed by the name of one of the directories belonging to the root; that directory is followed by the name of one of its subdirectories; and so on, until the desired directory is reached. Since all the directories belonging to a given directory must have unique names, the path is guaranteed to uniquely specify a directory on a volume. For example, you might specify a directory as "\GEOWORKS\DOCUMENT\MEMOS"; this would indicate a directory named "MEMOS," which belongs to a directory named "DOCUMENT," which in turn belongs to a directory named "GEOWORKS" at the root level.

The file itself is identified by its name. Since its directory has been uniquely specified by the volume and path, the name is guaranteed to specify at most one file in that directory. Each disk-operating system has its own conventions about how file names can be formed; for example, MS-DOS requires file names to have a "name" portion of at most eight letters, followed by an "extension" of at most three letters (known as the "FILENAME.EXT" convention).

For convenience, most operating systems let you specify a "working directory." This is a combination of a volume and a path. If you have a working directory, you can omit either the volume or the path from a file specification, and the volume and/or path of the working directory will be used. This is called a "relative path," i.e. a path that is relative to the working directory instead of the root directory. (Note that if you specify a disk handle for any operation, you must pass an absolute path which begins at the root of that disk or standard path.) GEOS allows every thread to have its own working directory; each thread can also save paths on a stack, letting the thread switch directories easily. For more details, see Current Path and Directory Stack .

Some disk-operating systems, such as MS-DOS, allow a working directory for each drive. GEOS does not allow this; you may have only one working directory at a time, regardless of how many drives you have. If you need to switch back and forth between directories on different drives, you can use the directory stack (see Current Path and Directory Stack ).

| Up: GEOS SDK TechDocs | Up | Prev: 1 Design Philosophy | Next: 3 Disks and Drives


Disks and Drives

The File System: 3 Disks and Drives

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 File System Overview | Next: 4 Directories and Paths

GEOS provides an easy interface to storage devices. Every drive (or analogous storage device) is identified by a token. Every volume is also identified by a token. This enables you to easily move from one volume to another.

You generally need only worry about disks and drives when you are opening a file. Once you have a file open, you can access the file by its file handle without paying attention to the disk it resides on. The GEOS file system will automatically prompt the user to change disks whenever necessary.

Note that if you use the document control object to open files, you will probably never have to worry about disks and drives. The document control automatically presents a File Selector dialog box to the user, letting the user navigate among disks and directories; when the user selects a file, the document control will automatically open the file and return its handle to the application.

Accessing Drives

Accessing Disks

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 File System Overview | Next: 4 Directories and Paths


Accessing Drives

The File System: 3.1 Disks and Drives: Accessing Drives

| Up: GEOS SDK TechDocs | Up | Prev: 3 Disks and Drives | Next: 3.2 Accessing Disks
DriveGetStatus(), DriveGetExtStatus(), DriveGetDefaultMedia(), DriveTestMediaSupport(), DriveGetName(), DriveStatus, DriveType, MediaType

Most systems running GEOS have access to a number of different drives. With the exception of network drives, the drives available will usually not change during an execution of GEOS, although the volumes mounted on the drives can change. Every drive is accessed by its drive number . This token is a byte-length integer value.

When you wish to open a file, you must specify its volume, not its drive. This is because the volume mounted on a drive can change frequently and without warning.

GEOS provides routines to get information about a drive. To get general information about a drive, call the routine DriveGetStatus() . This routine takes the drive number and returns a word-length set of DriveStatus flags (defined in drive.h ). If an error condition exists, such as the drive you request not existing, it returns zero. The following flags may be returned:

DS_PRESENT
This flag is set if the physical drive exists, regardless of whether the drive contains a disk.
DS_MEDIA_REMOVABLE
This flag is set if the disk can be removed from the drive.
DS_NETWORK
This flag is set if the drive is accessed over a network (or via network protocols), which means the drive cannot be formatted or copied.
DS_TYPE
This is a mask for the lowest four bits of the field. These bits contain a member of the DriveType enumerated type.

The lowest four bits of the word contains a member of the DriveType enumerated type. The field can be accessed by masking out all the bits except for those set in DS_TYPE. DriveType comprises the following values:

DRIVE_5_25
Drive uses 5.25-inch floppy disks.
DRIVE_3_5
Drive uses 3.5-inch floppy disks.
DRIVE_FIXED
Drive uses some kind of fixed disk (e.g. a hard drive).
DRIVE_RAM
Drive is chip-based (either RAM or ROM).
DRIVE_CD_ROM
Drive uses read-only optical disks.
DRIVE_8
Drive uses 8-inch floppy disks.
DRIVE_UNKNOWN
Drive type is unknown.

DriveGetStatus() returns the information most often needed about a drive. However, you may sometimes need more obscure information. For this reason, GEOS provides the routine DriveGetExtStatus() . Like DriveGetStatus() , it takes the drive number as an argument and returns a word of flags; however, it returns additional flags. The flags returned by DriveGetStatus() are set in the lower byte of the returned word; special additional flags are set in the upper byte. Like DriveGetStatus() , DriveGetExtStatus() returns zero if the drive specified is invalid. The following flags are defined for the upper byte:

DES_LOCAL_ONLY
This flag is set if the device cannot be viewed over a network.
DES_READ_ONLY
This flag is set if the device is read only, i.e. no data can ever be written to a volume mounted on it (e.g., a CD-ROM drive).
DES_FORMATTABLE
This flag is set if disks can be formatted in the drive.
DES_ALIAS
This flag is set if the drive is actually an alias for a path on another drive.
DES_BUSY
This flag is set if the drive will be busy for an extended period of time (e.g., if a disk is being formatted).

Many disk drives can take a variety of disks. For example, high-density 3.5-inch drives can read and write to either 720K disks or 1.44M disks. Every drive has a "default" media type. When you format a disk in that drive, it will, by default, be formatted to the default size. To find out the default disk type, call the routine DriveGetDefaultMedia() . This routine takes one argument, namely the drive number. It returns a member of the MediaType enumerated type. MediaType has the following possible values:

MEDIA_160K, MEDIA_180K, MEDIA_320K, MEDIA_360K
These are all sizes used by 5.25-inch disks.
MEDIA_720K
This is the size of a regular 3.5-inch disk.
MEDIA_1M2
This is the size of a high-density 5.25-inch disk.
MEDIA_1M44
This is the size of a high-density 3.5-inch disk.
MEDIA_2M88
This is the size of an ultra-high-density 3.5-inch disk.
MEDIA_FIXED_DISK
This is returned for all fixed disks.
MEDIA_CUSTOM
This is returned if none of the other values is appropriate. For example, it is returned for CD-ROM drives.
MEDIA_NONEXISTENT
This is returned if the drive specified does not contain a disk. This value is defined to be equal to zero.

If you want to find out if a drive can accommodate a certain kind of disk, call the routine DriveTestMediaSupport() . This Boolean routine takes two arguments: a drive number and a member of the MediaType enumerated type. If the drive supports that medium, the routine returns true (i.e. non-zero); otherwise, it returns false (i.e. zero).

To find out the name of a given drive, call DriveGetName() . This routine is passed three arguments: a drive number, a pointer to a character buffer, and the size of that buffer. DriveGetName() writes the drive's name to the buffer as a null-terminated string; it returns a pointer to that trailing null. If the buffer was not large enough, or the drive does not exist, it returns a null pointer.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Disks and Drives | Next: 3.2 Accessing Disks


Accessing Disks

The File System: 3.2 Disks and Drives: Accessing Disks

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Accessing Drives | Next: 4 Directories and Paths

Applications will work with disks more than they will work with drives. Once a geode knows a disk's handle, it can ignore such questions as whether the disk is in a drive; it need merely provide the disk's handle. If necessary, the system will prompt the user to insert the disk in the appropriate drive.

Registering Disks

DiskRegisterDisk(), DiskRegisterDiskSilently()

GEOS automatically keeps track of all disks used. The first time a disk is accessed in a session, it is registered . This means that it is assigned a disk handle .

The disk handle records certain information, such as the disk's volume name and whether the disk is writable. It also notes in which drive the disk was last inserted; if the system prompts the user to reinsert the disk, it will insist on that drive. A disk is automatically reregistered when certain actions are performed which might invalidate a disk's handle-table entry; for example, it is reregistered if it is formatted. It is also reregistered if someone tries to write a file to a disk which is marked read-only; the user may have ejected the disk and removed its write-protect tab. Note that reregistering a disk does not change its handle; it just brings GEOS's information about the disk up-to-date.

Note that the disk handle is not a reference to the global handle table; thus, Swat commands like phandle will not work with disk handles. Disk handles should always be treated as opaque 16-bit tokens.

You can specifically instruct the system to register a disk by calling the routine DiskRegisterDisk() . The routine is passed a single argument, namely the drive number. If the disk has an entry in the disk table, the routine will not reregister the disk; it will just return the disk's handle. If the disk has no entry in the table, the system will create an entry and register the disk. In this case, also, the routine will return the (new) disk handle. If the routine fails (for example, because there is no valid disk in the specified drive, or the drive itself does not exist), it returns a null handle.

When a disk is registered, the system notes the volume label. This label is used when the system has to prompt the user to insert a disk. If an unlabeled disk is inserted, the system will choose an arbitrary label for the volume (e.g. "UNNAMED1"). The system does not actually write this label to the disk; the label is used by the system and discarded when the session ends. Ordinarily, the system will present an alert box to inform the user about the temporary label. You can suppress this notification by calling the system routine DiskRegisterDiskSilently() . This routine has the same arguments and return values as DiskRegisterDisk() .

Getting Information about a Disk

DiskGetVolumeInfo(), DiskGetVolumeFreeSpace(), DiskGetDrive(), DiskGetVolumeName(), DiskFind(), DiskCheckWritable(), DiskCheckInUse(), DiskCheckUnnamed(), DiskForEach(), DiskInfoStruct, DiskFindResult

GEOS provides many routines to get information about disks. If geodes call disk routines at all, they are most likely to call these. Most of these routines are passed the handle of the disk. If you know the disk's volume label and need to find out its handle, call the routine DiskFind() (described below). If you know the disk is currently in a drive and you need to find out its handle, register the disk (see Registering Disks ). Note that any routine which is passed a disk handle can be passed a standard path constant; in this case, the routine will give you information about the disk containing the geos.ini file.

The basic disk-information routine is DiskGetVolumeInfo() . This returns information about the size of the disk and the amount of free space available. The routine is passed two arguments: the disk handle and a pointer to a DiskInfoStruct structure (shown below). The routine fills in the fields of the DiskInfoStruct structure and returns zero if it was successful. If it fails for any reason, it returns an error value and sets the thread's error value (which can be recovered with ThreadGetError() ). The usual error value returned is the constant ERROR_INVALID_VOLUME.

Code Display 17-1 The DiskInfoStruct Structure

typedef struct {
    word		DIS_blockSize;		/* # of bytes in a block; smallest size
                     * file system can allocate at once */
    sdword		DIS_freeSpace;		/* # of bytes free on disk */
    sdword		DIS_totalSpace;		/* Total size of the disk in bytes */
    char		DIS_name[VOLUME_BUFFER_SIZE];
                    /* Volume name; if disk is unnamed, this
                     * is the temporary name. String is
                     * null-terminated. */
} DiskInfoStruct;

If you just want to know a disk's name, call DiskGetVolumeName() . This routine takes two arguments: the disk handle and the address of a character buffer. (The buffer must be at least VOLUME_NAME_LENGTH_ZT characters long.) It writes the volume name to the buffer as a null-terminated string, and it returns the buffer's address. If the volume is unnamed, DiskGetVolumeName() writes the temporary volume name.

Note that all the routines which return a volume's name will return the temporary name if the volume is unnamed. For this reason, if you want to find out if a volume is unnamed, you must use a special purpose routine, namely DiskCheckUnnamed() . This Boolean routine is passed the disk's handle. If the volume does not have a permanent label, the routine returns true ; otherwise, it returns false .

If you want to know how much free space is available on a disk, call the routine DiskGetVolumeFreeSpace() . The routine is passed the disk handle; it returns (as a dword) the number of free bytes available. If the volume is currently read-only (e.g. a floppy disk with the write-protect tab set), it returns the amount of space that would be available if the volume were made read/write. If the volume is, by its nature, not writable (e.g. a CD-ROM disk), the routine will return zero. It will also return zero if an error condition occurs; in this case, it will also set the thread's error value.

If you want to know what drive a volume is associated with, call DiskGetDrive() . This routine takes one argument, namely the volume's disk handle. It returns the number of the drive which had that disk. Note that it will return this value even if that drive is no longer usable.

If you know the label of a volume which has been registered and you need to find out its handle, call the routine DiskFind() . The routine takes two arguments: the address of a null-terminated string containing the volume name and a pointer to a variable of the DiskFindResult enumerated type. It will return the disk's handle; if no disk with the specified name has been registered, it will return a null handle. DiskFindResult has the following possible values:

DFR_UNIQUE
Exactly one volume with the specified name has been registered. The handle of that volume was returned.
DFR_NOT_UNIQUE
Two or more volumes with the specified label have been registered. The handle of an arbitrary one of these volumes was returned. If you want to find the handles of all of these disks, call DiskForEach() , described below.
DFR_NOT_FOUND
No disk with the specified label has been registered. A null handle was returned.

To check if a volume is writable, call the Boolean routine DiskCheckWritable() . The routine takes one argument, the disk's handle. If the disk is writable, the routine returns true (i.e. non-zero). If the disk is non-writable, the routine returns false (i.e. zero).

To see if a disk is being used by any threads, call DiskCheckInUse() . The routine takes one argument: the disk's handle. It returns true (i.e. non-zero) if a file on the disk is open or if any thread has a directory on that disk in its directory stack (see Current Path and Directory Stack ). If neither condition applies, the routine returns false (i.e. zero).

If you want to perform an action on every disk, call DiskForEach() . This routine takes one argument, a pointer to a Boolean callback routine. The callback routine should take a single argument, the handle of a disk. DiskForEach() calls the callback routine once for every registered disk. It passes the disk handle to the callback routine, which can take any action it wants; for example, it could call one of the other disk-information routines. The callback routine can make DiskForEach() halt prematurely by returning a non-zero value. If the callback routine forced an early halt, DiskForEach() returns the last disk handle which had been passed to the callback routine; otherwise it returns a null handle. This routine is commonly called to look for a specific disk. To do this, simply have the callback routine check each disk to see if it is the one sought; if it is, simply return true , and DiskForEach() will return that disk's handle.

DiskForEach() does not need to examine the actual disks; it works from the information the file-system stores about all registered disks. This means that DiskForEach() will not have to prompt the user to insert any disks. Of course, the callback routine may need to examine the disks, in which case the user will be prompted when necessary.

Saving and Restoring a Disk Handle

DiskSave(), DiskRestore(), DiskRestoreError

A disk does not necessarily have the same handle from one execution of GEOS to another. This can pose a problem for an application which is restarting from a state file. In order to reopen a file, it has to know the file's location. If it knows the file's location relative to a standard path, there is no problem, since the application can use the standard path constant in the place of a disk handle. If the file is not in a standard path, the application will need some way of figuring out the disk's handle on restart.

For this reason, GEOS provides DiskSave() and DiskRestore() . DiskSave() saves information about a disk in an opaque data structure. DiskRestore() reads such a data buffer and returns the handle of the disk described; it even arranges to prompt the user if the disk has not been registered yet.

To save a disk handle, call DiskSave() . This routine takes three arguments:

  • The disk handle.
    This may be a standard path.
  • A pointer to a buffer.
    DiskSave() will write opaque data to that buffer; you will need to pass that data to DiskRestore() to restore the handle.
  • A pointer to an integer.
    When you call DiskSave() , that integer should contain the size of the buffer (in bytes). When DiskSave() exits, the integer will contain the size of the buffer needed or used (as described below).

If DiskSave() was successful, it will return true . The integer parameter will contain the size of the buffer actually needed; for example, if the buffer had been 100 bytes long and DiskSave() returns 60, you can safely free the last 40 bytes in the buffer. If DiskSave() failed, it will return false . If it failed because the buffer was too small, it will write the size needed into the integer passed; simply call DiskSave() again with a large enough buffer. If DiskSave() failed for some other reason (e.g. the disk belongs to a drive which no longer exists), it will write a zero value to the integer.

To restore a disk, call DiskRestore() . This routine takes two arguments:

  • A pointer to the opaque data structure written by DiskSave() .
  • A pointer to a callback routine. The callback routine is called if the user must be prompted to insert the disk. If you pass a null function pointer, DiskRestore() will fail in this situation.

If the disk in question has already been registered or is currently in its drive, DiskRestore() will return its handle. If the disk is not registered and is not in any drive, DiskRestore() will call the callback routine. The callback routine should accept the following four arguments:

  • A pointer to a null-terminated string containing the drive name, specified according to the DOS convention (e.g. "C:").
  • A pointer to a null-terminated string containing the disk volume label.
  • The DiskRestoreError (see below) which would be returned if the callback routine hadn't been called.
  • A pointer to a pointer to the opaque data structure. If the callback routine causes the data structure to move, it should fix up the pointer to point to the new location. Note that the two strings (the drive name and the disk label) are located within this data structure; thus, if you cause this structure to move, both of those pointers will be invalidated.

If the callback routine believes the user inserted the correct disk, it should return DRE_DISK_IN_DRIVE. Otherwise, it should return a DiskRestoreError constant. In this case, DiskRestore() will fail and set the thread's error value to the constant specified. If the callback routine returns an error, that error will generally be DRE_USER_CANCELLED_RESTORE.

If DiskRestore() is successful, it will return the disk handle; this may be different from the disk's handle in the previous execution. You may now free the data buffer, if you like. If DiskRestore() fails, it will return a null handle and set the thread's error value. There are several different DiskRestoreError values; they are listed below.

DRE_DRIVE_NO_LONGER_EXISTS
The disk had last been used in a drive which is no longer attached to the system (or the appropriate file-system driver is no longer present).
DRE_REMOVABLE_DRIVE_DOESNT_HOLD_DISK
The disk was in a removable-media drive, and that drive doesn't contain the disk.
DRE_USER_CANCELLED_RESTORE
A callback routine was called and returned this value to DiskRestore() .
DRE_COULDNT_CREATE_NEW_DISK_HANDLE
DiskRestore() found the disk in the drive but was for some reason unable to create the disk handle.
DRE_REMOVABLE_DRIVE_IS_BUSY
The appropriate drive exists but is unavailable due to some time-consuming operation (e.g. a disk format).

Other Disk Utilities

DiskSetVolumeName(), DiskFormat(), DiskCopy(), FormatError, DiskCopyCallback, DiskCopyError

GEOS provides several utilities for working with disks. These utilities allow geodes to copy disks, format them, and change their volume names. Most applications will never need to use these utilities; they can rely on the users to take care of disk formatting with an application like GeoManager. However, some applications will want to make use of them. For example, an archiving program might automatically format storage disks and give them appropriate labels.

Changing a Volume Name

If you want to set or change a volume's name, you should call DiskSetVolumeName() . This routine takes two arguments: the volume's handle and the address of a null-terminated string (containing the new volume name). If it is able to change the volume's name, it returns zero; otherwise, it returns an error code. It sets or clears the thread's error value appropriately. The following error codes may be returned:

ERROR_INVALID_VOLUME
An invalid disk handle was passed to the routine.
ERROR_ACCESS_DENIED
For some reason, the volume's name could not be changed. For example, the volume might not be writable.
ERROR_DISK_STALE
The drive containing that disk has been deleted. This usually only happens with network drives.

Formatting a Disk

If a geode needs to format a disk, it can call the routine DiskFormat() . This routine can do either low-level or high-level ("soft") formats. The routine does not interact with the user interface; instead, it calls a callback routine, which can arrange any such interaction. DiskFormat() takes seven arguments:

  • The number of the drive containing the disk to be formatted.
  • The address of a null-terminated string containing the disk's new volume name.
  • A member of the MediaType enumerated type (see MEDIA_160K, MEDIA_180K, MEDIA_320K, MEDIA_360K These are all sizes used by 5.25-inch disks. ).
  • Flags indicating how the format should be done. The following flags are available:
DFF_CALLBACK_PERCENT_DONE
A callback routine will be called periodically. The callback routine will be passed a single argument, namely the percentage of the format which has been done, expressed as an integer.
DFF_CALLBACK_CYL_HEAD
A callback routine will be called periodically. The callback routine will be passed a single argument, namely the cylinder head being formatted. If both DFF_CALLBACK_PERCENT_DONE and DFF_CALLBACK_CYL_HEAD are passed, results are undefined.
DFF_FORCE_ERASE
A "hard format" should be done; that is, the sectors should be rewritten and initialized to zeros. If this flag is not set, DiskFormat() will do a "soft format" if possible; it will check the sectors and write a blank file allocation table, but it will not necessarily erase the data from the disk.
  • A pointer to an unsigned integer variable; the number of good sectors on the disk will be written here.
  • A pointer to an unsigned integer variable; the number of bad sectors on the disk will be written here.
  • The address of a Boolean callback routine, as described above. The routine should be passed either the current cylinder and head or the percentage formatted, depending on the flag passed to DiskFormat() . It should return true to abort the format, or false (i.e. zero) to continue with the format. If neither DFF_CALLBACK_PERCENT_DONE nor DFF_CALLBACK_CYL_HEAD is passed, the callback routine will never be called, so this argument may be a null pointer.

DiskFormat() returns a member of the FormatError enumerated type. If the format was successful, it will return the constant FMT_DONE (which is guaranteed to equal zero). Otherwise, it will return one of the following constants:

FMT_DRIVE_NOT_READY
FMT_ERROR_WRITING_BOOT
FMT_ERROR_WRITING_ROOT_DIR
FMT_ERROR_WRITING_FAT
FMT_ABORTED
FMT_SET_VOLUME_NAME_ERROR
FMT_CANNOT_FORMAT_FIXED_DISKS_IN_CUR_RELEASE
FMT_BAD_PARTITION_TABLE,
FMT_ERR_READING_PARTITION_TABLE,
FMT_ERR_NO_PARTITION_FOUND,
FMT_ERR_MULTIPLE_PRIMARY_PARTITIONS,
FMT_ERR_NO_EXTENDED_PARTITION_FOUND
FMT_ERR_CANNOT_ALLOC_SECTOR_BUFFER
FMT_ERR_DISK_IS_IN_USE
FMT_ERR_WRITE_PROTECTED
FMT_ERR_DRIVE_CANNOT_SUPPORT_GIVEN_FORMAT
FMT_ERR_INVALID_DRIVE_SPECIFIED
FMT_ERR_DRIVE_CANNOT_BE_FORMATTED
FMT_ERR_DISK_UNAVAILABLE

Copying Disks

GEOS provides a routine for copying disks. This routine, DiskCopy() , maintains a balance between the two goals of limiting memory usage and minimizing disk swapping. It will reformat the destination disk if necessary. The routine does a sector-for-sector copy; therefore, the destination disk must either be of exactly the same type as the source disk (i.e., same medium and size), or it must be reformatable to be the same size. For this reason, neither the source nor the destination may be a fixed disk.

DiskCopy() does not interact with the user directly, even though the user may have to swap disks. Instead, it calls a callback routine whenever interaction with the user may be necessary. The routine takes the following arguments:

  • The drive number of the source drive.
  • The drive number of the destination drive. This may be the same as the source drive.
  • The address of a Boolean callback routine. The routine must take three arguments: a member of the DiskCopyCallback enumerated type (described below), a disk handle, and a word-sized parameter holding any other appropriate information. The routine should return non-zero to abort the copy; otherwise, it should return zero.

The callback routine is called under a variety of circumstances. When it is called, the first argument passed is a member of the DiskCopyCallback enumerated type, which specifies both why the callback routine was called and what the other arguments signify. DiskCopyCallback contains the following types:

CALLBACK_GET_SOURCE_DISK
The callback routine should prompt the user to insert the source disk. The second argument is meaningless for this call. The third argument is the number identifying the drive.
CALLBACK_GET_DEST_DISK
The callback routine should prompt the user to insert the destination disk. The second argument is meaningless for this call. The third argument is the number identifying the drive.
CALLBACK_REPORT_NUM_SWAPS
The second argument is meaningless for this call. The third argument is the number of disk swaps that will be necessary to copy the disk. The callback routine may wish to report this number to the user and ask for confirmation.
CALLBACK_VERIFY_DEST_DESTRUCTION
If the destination disk has already been formatted, the callback routine will be called with this parameter. The callback routine may wish to remind the user that the destination disk will be erased. The second argument is the handle of the destination disk; this is useful if, for example, you want to report the disk's name. The third argument is the destination drive's number. As in the other cases, the callback routine can abort the format by returning non-zero.
CALLBACK_REPORT_FORMAT_PERCT
If the destination disk needs to be formatted, DiskCopy() will periodically call the callback routine with this parameter. In this case, the second argument will be meaningless; the third parameter will be the percentage of the destination disk which has been formatted.
CALLBACK_REPORT_COPY_PERCT
While the copy is taking place, DiskCopy() will periodically call the callback routine with this parameter. In this case, the second parameter will be meaningless; the third parameter will be the percentage of the copy which has been completed.

If the copy was successful, DiskCopy() returns zero. Otherwise, it returns a member of the DiskCopyError enumerated type, which has the following members:

ERR_DISKCOPY_INSUFFICIENT_MEM
This is returned if the routine was unable to get adequate memory.
ERR_CANT_COPY_FIXED_DISKS
ERR_CANT_READ_FROM_SOURCE
ERR_CANT_WRITE_TO_DEST
ERR_INCOMPATIBLE_FORMATS
ERR_OPERATION_CANCELLED
This is returned if the callback routine ever returned a non-zero value, thus aborting the copy.
ERR_CANT_FORMAT_DEST
| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Accessing Drives | Next: 4 Directories and Paths


Directories and Paths

The File System: 4 Directories and Paths

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Disks and Drives | Next: 5 Files

Information, whether code or data, is always stored in files. However, storage volumes are not simply collections of files. Rather, they are organized into directories. There are two main reasons for this organization.

First, a large storage device can easily contain hundreds or thousands of files. Computer users need to be able to find a specific file quickly. If they were confronted with a list of all the files on a storage device, they would have a hard time finding the one they want. The directory structure solves this problem; users can navigate through the directory tree until they find the file they want.

The other reason for the directory structure is to expand the name-space for files. If every file on a volume were forced to have a unique name, users would find themselves severely restricted. Directories allow more flexibility; since a file is identified by both its name and its location, its name need only be unique within its directory.

A file can be uniquely specified by a full path . A full path specifies the volume containing the file as well as all the directories containing the file, starting with the root directory. The volume specification can usually be omitted, indicating that the file is on the same volume as the current working directory. Alternatively, the file can be specified with a relative path . A relative path specifies the file's position relative to the current working directory, instead of starting with the root.

In most implementations of DOS, there is a standard way of describing a path. These conventions are used in GEOS as well. A full path begins with a backslash, which represents the root directory. This may be followed by one or more directory specifications, separated by backslashes. The first directory listed would be a member of the root directory; each following directory would be a member of the preceding directory. A relative path is the same, except that it does not begin with a backslash. In either a full or a relative path, their are two special directory names. A single period (".") specifies the current directory; that is, the path "." indicates the current working directory, and the path "\GEOWORKS\DOCUMENT" is the same as the paths "\GEOWORKS\DOCUMENT\." and "\GEOWORKS\.\DOCUMENT". A double period ("..") indicates the parent of the current directory; thus, "\GEOWORKS\DOCUMENT\.." is equivalent to "\GEOWORKS".

The GEOS file management system allows each thread to have a working directory. Whenever a thread needs to open a file, it can rely on its working directory instead of passing a full path. GEOS provides an added mechanism: it defines certain system standard paths which can be reached with simple system commands. These paths provide a way to standardize directory usage; an application might, for example, keep a certain data file in the standard PRIVDATA directory, leaving the user to decide where that PRIVDATA directory may be. This is covered in detail in the following section.

Standard Paths

Current Path and Directory Stack

Creating and Deleting Directories

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Disks and Drives | Next: 5 Files


Standard Paths

The File System: 4.1 Directories and Paths: Standard Paths

| Up: GEOS SDK TechDocs | Up | Prev: 4 Directories and Paths | Next: 4.2 Current Path and Directory Stack

The GEOS system is designed to run on a wide variety of architectures. For example, it can be stored on a hard disk, recorded in ROM chips, or resident on a network server and run by members of the network. This presents a difficulty. The system uses many files, storing both code (such as libraries) and data (such as fonts). It can't assume that they are in any specific place. For example, a user might be running GEOS from a network server, but she might have several fonts for personal use on her own hard disk. The system has to be able to look in all the right places. Applications have a similar dilemma. An application needs to be able to choose an appropriate place to create data files, and needs to be able to find them later.

The solution to this is to use standard paths . There are a number of standard directories GEOS uses. Each one of these has a constant associated with it. The constants are treated as special disk handles. For example, the routine FileSetCurrentPath() (described in Current Path and Directory Stack ) takes two arguments, a disk handle and a path string. If the disk handle passed is the constant SP_DOCUMENT and the string passed is "Memos", the routine will look in the standard document path for a directory called "Memos" and will open it.

There are two advantages to standard paths. The first is that they give the system and the applications points of reference. An application does not need to worry where to write its internal data files; it can simply install them in the standard PRIVDATA directory.

The second advantage is that the standard paths do not need to correspond to any one specific directory. A standard path can actually be several different DOS directories. For example, the GEOS kernel looks for font files in the standard path SP_FONT. The user may define this path to be several directories; it might comprise a local directory for the most commonly used fonts, a network directory for some decorative fonts, etc. The file system would automatically locate all of these fonts. Similarly, the SP_DOCUMENT directory might comprise a directory on a ROM disk for read-only files, as well as a directory on a hard disk where files are written. A standard path is considered "read-only" if and only if all of the directories which make up the standard path are read-only. When you create a file directly in a standard path (as opposed to in a subdirectory of a standard path), the file system will write it to the directory on the disk containing the geos.ini file.

The standard paths have a default hierarchy. For example, the standard path SP_VIDEO_DRIVERS usually refers to a directory named VIDEO which belongs to the standard path SP_SYSTEM. However, the user is allowed to define each standard path however he or she wishes. For this reason, the programmer may not make any assumptions about where each standard path is located.

Below is a list of standard paths currently implemented. The paths are described as if they were single directories; remember, however, that each one of these may comprise several directories. Each path is accompanied by notes on how it is used and where it might commonly be found. The user can decide where each of these directories is, so make no assumptions about their locations. These paths will always be supported; future versions of GEOS may add new paths.

SP_TOP
The top level directory. This is the directory which contains GEOS.INI. This is often C:\GEOWORKS.
SP_APPLICATION
All non-system applications are in this directory or a directory belonging to it. This is often a WORLD directory under SP_TOP.
SP_DOCUMENT
All document files should go here. This is commonly a DOCUMENT directory under SP_TOP.
SP_SYSTEM
All libraries and drivers belong to this directory. All libraries must go in this directory, not in a subdirectory. Drivers are further grouped into subdirectories, one for each type of driver. This is commonly a SYSTEM directory under SP_TOP. Geodes should never need to directly access this directory, other than for installing drivers to it.
SP_PRIVATE_DATA
This contains data files which should not be accessed by users. By convention, each geode creates a subdirectory with its own data files. For example, the application HELLO.GEO would use a directory called HELLO in SP_PRIVATE_DATA. This is commonly a PRIVDATA directory under SP_TOP.
SP_DOS_ROOM
This directory contains the DOS-room launchers. It is commonly a DOSROOM directory under SP_TOP.
SP_STATE
This directory contains all state files. It is commonly a STATE directory under PRIVDATA.
SP_USER_DATA
This directory holds data files that the user may add to, delete, upgrade, or otherwise change. Each data file type should have its own subdirectory. This is commonly a USERDATA directory under SP_TOP.
SP_FONT
This directory contains all font data files, no matter what format they are. It is often USERDATA\FONT.
SP_SPOOL
This directory contains all application spooler files. It is commonly USERDATA\SPOOL.
SP_SYS_APPLICATION
This directory contains all system applications. For example, it contains the Geoworks Pro "Welcome" application. These applications should not be launched by the user from the desktop. This directory is commonly SYSTEM\SYSAPPL.
SP_MOUSE_DRIVERS
This directory contains all mouse drivers. It is commonly SYSTEM\MOUSE.
SP_PRINTER_DRIVERS
This contains all printer drivers. It is commonly SYSTEM\PRINTER.
SP_FILE_SYSTEM_DRIVERS
This directory contains drivers for file systems. It has both DOS and network drivers. It is commonly SYSTEM\FS.
SP_VIDEO_DRIVERS
This directory contains video drivers. It is commonly SYSTEM\VIDEO.
SP_SWAP_DRIVERS
This directory has all of the swap drivers. It is commonly SYSTEM\SWAP.
SP_KEYBOARD_DRIVERS
This directory has all of the keyboard drivers. It is commonly SYSTEM\KEYBOARD.
SP_FONT_DRIVERS
This directory has all of the font drivers. It is commonly SYSTEM\FONT.
SP_IMPORT_EXPORT_DRIVERS
This directory has all of the import/export libraries. It is commonly SYSTEM\IMPEX.
SP_TASK_SWITCH_DRIVERS
This directory contains all task-switching drivers. It is commonly SYSTEM\TASK.
SP_HELP_FILES
This directory contains all help files. It is commonly USERDATA\HELP.
SP_TEMPLATE
This directory contains document templates. It is commonly DOCUMENT\TEMPLATE.
SP_POWER_DRIVERS
This directory contains power-management drivers. It is commonly SYSTEM\POWER.
SP_HWR
This directory contains the handwriting recognition libraries. It is commonly SYSTEM\HWR.
SP_WASTE_BASKET
This directory contains files that have been dragged to the waste basket but have not yet been thrown out. All non-open files and directories in this path are periodically deleted. Temporary files should usually be created here; they will not be deleted until after they have been closed. This directory is commonly PRIVDATA\WASTE.
SP_BACKUP
This directory contains backup files made by the document control. It is commonly \PRIVDATA\BACKUP.
| Up: GEOS SDK TechDocs | Up | Prev: 4 Directories and Paths | Next: 4.2 Current Path and Directory Stack


Current Path and Directory Stack

The File System: 4.2 Directories and Paths: Current Path and Directory Stack

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Standard Paths | Next: 4.3 Creating and Deleting Directories
FileSetCurrentPath(), FileGetCurrentPath(), FileConstructFullPath(), FileParseStandardPath(), FileResolveStandardPath(), FilePushDir(), FilePopDir(), FileResolveStandardPathFlags

Every thread has a current path . When the thread opens a file, it can pass just the name of the file; the file system combines this name with the current path to find the file. The path is a combination of a disk handle and a directory sequence. To set the thread's current path, call the routine FileSetCurrentPath() , which takes two arguments: a disk handle and a pointer to a null-terminated string. The string should contain a sequence of directories specified in the normal DOS convention. To change to a standard path, pass the path constant as the disk handle and a null string (i.e. a pointer to a null byte). To change to a subdirectory of a standard path, pass the path constant as the disk handle and a pointer to a relative or absolute path specification; for example, to change to the HELLO directory in PRIVDATA, pass the disk handle constant SP_PRIVATE_DATA and a pointer to the string "HELLO". FileSetCurrentPath() returns the handle of the disk. If you change to a standard path, it returns the path constant; if you change to a directory within a standard path, it returns the constant of the closest standard path. In the event of error, it returns a null handle and sets the thread's error value. The error most commonly returned is ERROR_PATH_NOT_FOUND, indicating that the specified directory could not be found or does not exist. To set the current path equal to a standard path, use the FileSetStandardPath() routine.

To find out the current path, call the routine FileGetCurrentPath() . This routine takes two arguments: the address of a character buffer and the size of the buffer. It returns the handle of the current path's disk and writes the path (without drive specifier) to the buffer, truncating the path if the buffer is too small. If the directory is a standard path or a subdirectory of one, FileGetCurrentPath() will return the disk-handle constant for that path and will write an absolute path to the buffer. If you want a full path, use FileConstructFullPath() , described below.

To translate a standard path into a full path, call FileConstructFullPath() , which takes five arguments:

  • A Boolean value indicating whether the drive name should be prepended to the path string returned.
  • The handle of a disk. A null argument indicates that the path string passed is relative to the current working directory. The handle may be a standard path constant, indicating that the path string passed is relative to that standard path.
  • A pointer to a string containing a path relative to the location indicated by the previous argument. This may be an empty string.
  • A pointer to a pointer to a character buffer. The path will be written to this buffer. The routine will update this to be a pointer to a pointer to the null terminator.
  • The length of that buffer.

The routine writes the full path to the buffer and returns the disk handle. If it is unable to construct a full path, it returns a null handle. The pointer to the path buffer will have been changed to point to the end of the string; you may want to keep a copy of the old value of the pointer around when using this routine.

To find the standard path to a given location, call the routine FileParseStandardPath() . This routine is passed two arguments:

  • The handle of the disk. Passing a null disk handle indicates that the path string contains a drive specifier.
  • A pointer to a pointer to a path string. This path should begin at the root of the disk specified.

FileParseStandardPath() returns the standard path constant. It also updates the pointer to point to the remaining portion of the path. For example, if you pass a pointer to a pointer to the string "\GEOWORKS\DOCUMENT\MEMOS\APRIL", the pointer would be updated to point to the "\MEMOS\APRIL" portion, and the handle SP_DOCUMENT would be returned. If the path passed does not belong to a standard path, the constant SP_NOT_STANDARD_PATH will be returned, and the pointers will not be changed.

Because each standard path is made up of one or more directories (possibly on different devices), it can be hard to know just where a file is. For that reason, GEOS provides FileResolveStandardPath() . This routine is passed a relative path to a file; it then constructs the full path of the file, starting from the root of the disk ( not from a standard path); it also returns the handle of the actual disk containing the file. FileResolveStandardPath() is passed several arguments:

  • A pointer to a pointer to a character buffer. The full path will be written to that buffer, and the pointer will be updated to point to the null terminator.
  • The length of the buffer, in bytes.
  • A pointer to a relative path string. This path is relative to the current working directory, which is usually a standard path.
  • A set of FileResolveStandardPathFlags.
  • A pointer to an empty FileAttrs buffer; the routine will fill in this buffer with the passed file's attributes, if any.

FileResolveStandardPath() writes the full, absolute path to the buffer specified. It also returns the handle of the disk containing that file. If it cannot find the file specified, it returns a null handle. There are two FileResolveStandardPathFlags available:

FRSPF_ADD_DRIVE_NAME
The path string written to the buffer should begin with the drive name (e.g., "C:\GEOWORKS\DOCUMENT\MEMOS").
FRSPF_RETURN_FIRST_DIR
FileResolveStandardPath() should not check whether the passed path actually exists; instead, it should assume that the path exists in the first directory comprising the standard path, and return accordingly.

In addition to having a current path, every thread has a directory stack . The stack is used for switching paths quickly. You can at any time push the current path onto the stack by calling FilePushDir() . This routine pushes the directory on the directory stack and returns nothing. You can change the current path to the one on top of the directory stack by calling FilePopDir() . This pops the top directory off the stack and makes it the current path. (If the directory stack is empty, the result is undefined.) These routines are very useful when you write subroutines which may need to change the current working directory; they can push the old directory at the start of the routine and pop it at the end.

Files are often specified "by their paths." This simply means specifying them with a string containing the directory path and ending with the file name. This path may be either relative or absolute.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Standard Paths | Next: 4.3 Creating and Deleting Directories


Creating and Deleting Directories

The File System: 4.3 Directories and Paths: Creating and Deleting Directories

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Current Path and Directory Stack | Next: 5 Files
FileCreateDir(), FileDeleteDir()

You can create directories with FileCreateDir() . The routine takes a single argument, namely the address of a character string. If the string is simply a directory name, it will attempt to create that directory at the current location. If the string is a relative path, it will create the directory at the end of the path, if possible. For example, passing the string "Memos\September" will cause it to check if there is a directory called "Memos" at the current location. If there is, it will create the directory "September" inside the directory "Memos". If the string is an absolute path (i.e. there is a backslash before the first directory name), it will behave the same way, but it will start with the root directory. The routine returns zero if it was successful. If it was unsuccessful, it will return one of the following error codes:

ERROR_INVALID_NAME
The name passed was inappropriate for directories on that device.
ERROR_PATH_TOO_LONG
The path string was longer than is permitted by the file system for that device.
ERROR_FILE_EXISTS
A file or directory with the specified name already exists at the specified location.
ERROR_PATH_NOT_FOUND
The path string was in some way invalid; for example, it might have instructed FileCreateDir() to create the directory inside of a directory which does not exist.
ERROR_ACCESS_DENIED
The thread is not able to create directories in the specified location.
ERROR_DISK_STALE
The drive that disk was on has been removed.
ERROR_DISK_UNAVAILABLE
The validation of the disk in that drive was aborted by the user.
ERROR_WRITE_PROTECTED
The volume is write-protected.

Note that the directory name can be any acceptable GEOS file name; that is, it may be up to 32 characters long and can contain any characters other than backslashes, colons, asterisks, or question marks. For further information about GEOS file names, see DOS Files and GEOS Files .

You can delete directories with FileDeleteDir() . This routine takes a single argument, namely the address of a character string. This string can specify a relative or absolute path, as with FileCreateDir() . It attempts to delete the directory specified. (Note that you are not allowed to delete your current directory or a non-empty directory.) If it successfully removes the directory, it returns zero. Otherwise, it returns one of the following members of the FileError type:

ERROR_PATH_NOT_FOUND
The directory specified could not be found or does not exist.
ERROR_IS_CURRENT_DIRECTORY
This directory is some thread's current directory, or else it is on some thread's directory stack. Note that you cannot delete your own working directory.
ERROR_ACCESS_DENIED
The calling thread does not have permission to delete the directory. This is also returned if the directory was on a read-only device.
ERROR_DIRECTORY_NOT_EMPTY
The directory specified is not empty. Delete all the files before you attempt to delete the directory.
ERROR_DISK_STALE
The drive that disk was on has been removed.
ERROR_DISK_UNAVAILABLE
The validation of the disk in that drive was aborted by the user.
| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Current Path and Directory Stack | Next: 5 Files


Files

The File System: 5 Files

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Directories and Paths | Next: 5.1 DOS Files and GEOS Files

When data is not actually in a computer's memory, it needs to be grouped together in a manageable form. Most storage devices group data together into files . A file is a collection of information. It may be a program, either in machine language or in a higher-level language (as with batch files); or it may be data.

When an application is accessing a data file, it is said to have that file open . It may be open for read and write access, just for reading, or just for writing. If a file is open, it can not be moved or deleted (although it can be copied).

You will probably not have to read all of this section. Bytewise File Operations ( Bytewise File Operations ) is important only if you will be using DOS files or GEOS byte files. Most applications will work with GEOS Virtual Memory (VM) files.

DOS Files and GEOS Files

Files and File Handles

GEOS Extended Attributes

File Utilities

FileEnum()

Bytewise File Operations

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Directories and Paths | Next: 5.1 DOS Files and GEOS Files


DOS Files and GEOS Files

The File System: 5.1 Files: DOS Files and GEOS Files

| Up: GEOS SDK TechDocs | Up | Prev: 5 Files | Next: 5.2 Files and File Handles

Most file systems have a simple convention of what a file is. Applications treat files as if they were a sequence of bytes. They can read the bytes in order from the file, copying a sequence of bytes into memory. They have a "position" in the file, indicating the next byte to be read. An application can copy data from memory to a file, replacing the data there.

GEOS allows geodes to access any type of DOS file using the normal DOS conventions. However, it also provides its own file format, the GEOS file. GEOS files are stored on the disk as normal DOS files. This means that they can be copied from one disk to another by any normal DOS procedure; they can be uploaded or downloaded, compressed, archived, or transferred to different devices exactly as if they were ordinary DOS files. Nevertheless, when a system is running GEOS, the files have added functionality. GEOS data files have special extended attributes , which keep track of such things as the file's tokens, protocol numbers, etc. For more information about extended attributes, see GEOS Extended Attributes .

Most GEOS data files are Virtual Memory files. VM files can automatically back up data; they allow their users to insert data at any point in the file, and when the file's user needs access to data the VM manager automatically allocates memory and swaps the data into it. These files are created and accessed using special VM routines; for more information, see the VM chapter. Note that you can have a GEOS file hold raw data instead of having VM capability. Such a file is known as a "byte" file since it is treated as a sequence of bytes with no structure except what is specifically added by the file's creator. All of the routines for working with DOS files can be used with GEOS byte files.

One basic difference between GEOS files and DOS files is in naming conventions. Each file system and disk-operating system has its own convention for how files may be named. By far, the most common convention is the one used by MS-DOS: each file is identified by a name of up to eight characters, followed by an optional extension of up to three characters. There are further restrictions on which characters may be part of a name; for example, none of the characters may be a period.

GEOS provides more versatility. Each file has two different names. It has a native name; this is the name used by the device holding the file and must meet all of its restrictions. For example, if the file is kept on a usual DOS-compatible disk, the native name would be in the FILENAME.EXT format. The other name is the virtual file name. This may contain a number of characters equal to FILE_LONGNAME_LENGTH and may contain any characters other than backslashes ("\"), colons (":"), asterisks ("*"), and question marks ("?"). Any time a file's name is needed, either the virtual or the native file name may be used. When a file is created, GEOS automatically maps its virtual name into an appropriate native name.

Special GEOS information, such as the file's virtual name and extended attributes, is stored in the body of the file itself. Thus, while applications should not access this information directly, they can still be assured that it will be preserved when the file is copied by any normal DOS techniques. This information is stored in a special header which is transparent to geodes. If you use one of the bytewise file operations, you will not be able to affect the header.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Files | Next: 5.2 Files and File Handles


Files and File Handles

The File System: 5.2 Files: Files and File Handles

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 DOS Files and GEOS Files | Next: 5.3 GEOS Extended Attributes
FileDuplicateHandle()

In order to read or change a file, you must open it with an appropriate routine. When you open a file, the file system sees to it that the file will not be erased or moved until you close it. When a geode exits, all files it has open are automatically closed. When GEOS exits, all files are closed. If an application restarts from a saved state, it will have to reopen all files.

When you open a file, the GEOS file system returns a file handle . This is a global handle associated with that file. Whenever you need to perform an action on a file you have opened, you specify the file by passing its handle. Note that there may be several handles which all refer to the same file. If more than one thread will need to use the same handle, they may synchronize their access by using HandleP() and HandleV() ; for details, see the Memory Management chapter. The Virtual Memory routines include data-access synchronization routines for use with VM files; for details, see the VM chapter.

It is sometimes useful to have two different handles which indicate the same file. GEOS provides the routine FileDuplicateHandle() . This routine takes one argument, namely the handle of an open file. It creates and returns another handle which indicates the same file. You will have to close both handles to close the file. FileDuplicateHandle() works on any type of file handle; that is, it can be used on the handles of DOS files, GEOS byte files, or VM files. Note that the duplicate handle will have the same read/write position as the original.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 DOS Files and GEOS Files | Next: 5.3 GEOS Extended Attributes


GEOS Extended Attributes

The File System: 5.3 Files: GEOS Extended Attributes

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Files and File Handles | Next: 5.4 File Utilities
FileGetHandleExtAttributes(), FileGetPathExtAttributes(), FileSetHandleExtAttributes(), FileSetPathExtAttributes(), FileExtAttrDesc, FileDateAndTime, FileAttrs

All GEOS files, whether they contain code or data, have special extended attributes . Geodes cannot look at these directly; instead, they make calls to the file system when they want to examine or change the attributes. There are many different extended attributes; however, they are all accessed and changed in a uniform way. Some of the extended attributes are also supported for non-GEOS files. The following extended attributes are currently available:

FEA_MODIFICATION
FEA_FILE_ATTR
FEA_SIZE
FEA_FILE_TYPE
FEA_FLAGS
FEA_RELEASE
FEA_PROTOCOL
FEA_TOKEN
FEA_CREATOR
FEA_USER_NOTES
FEA_NOTICE
FEA_CREATION
FEA_PASSWORD
FEA_CUSTOM
FEA_NAME
FEA_GEODE_ATTR
FEA_PATH_INFO
FEA_FILE_ID
FEA_DESKTOP_INFO
FEA_DRIVE_STATUS
FEA_DISK
FEA_DOS_NAME
FEA_OWNER
FEA_RIGHTS

There are also two special constants, FEA_MULTIPLE and FEA_END_OF_LIST. These are also described below.

There are two different routines to read a file's extended attributes: FileGetHandleExtAttributes() and FileGetPathExtAttributes() . These routines are the same except in the way the file is specified: in one, the handle of an open file is passed, whereas in the other, the address of a path string is passed.

FileGetHandleExtAttributes() takes four arguments. The first is the handle of the file whose attributes are desired; this may be a VM file handle or a byte-file handle. The second is a constant specifying the attribute desired. All extended attributes which are currently supported are listed above; more may be added later. The third is a pointer to a buffer; the attribute's value will be written into that buffer. The fourth argument is the size of the buffer (in bytes). Before it returns, FileGetHandleExtAttributes() will write the value of the attribute into the buffer. If successful, it will return zero; otherwise, it will return one of the following error codes:

ERROR_ATTR_NOT_SUPPORTED
The file system does not recognize the attribute constant passed.
ERROR_ATTR_SIZE_MISMATCH
The buffer passed was too small for the attribute requested.
ERROR_ATTR_NOT_FOUND
The file does not have a value set for that attribute. This is returned if you try to get certain extended attributes of a non-GEOS file.
ERROR_ACCESS_DENIED
You do not have read-access to the file.

FileGetHandleExtAttrs() can also fetch several attributes at once. For details on this, see the section on FEA_MULTIPLE ( FEA_MULTIPLE ).

You can get a file's extended attributes without having it open by calling FileGetPathExtAttributes() . This routine takes a pointer to a null-terminated path string instead of a file handle. This makes it suitable for examining the attributes of an executable file or directory. Note that the file system will still have to open the file in order to get the attributes. If any geode (including the caller) has the file open with "deny-read" exclusive, the call will fail with error condition ERROR_ACCESS_DENIED. If it could not find the file specified, it will return ERROR_FILE_NOT_FOUND.

To change one of a file's extended attributes, make a call either to FileSetHandleExtAttributes() or to FileSetPathExtAttributes (). These routines take the same arguments as the FileGet...() routines above; however, they copy the data from the buffer into the attribute, instead of vice versa. These routines return zero if the operation was successful. Otherwise, they return one of the following error codes:

ERROR_ATTR_NOT_SUPPORTED
The file system does not recognize the attribute constant passed. This is returned if you try to set an extended attribute for a non-GEOS file.
ERROR_ATTR_SIZE_MISMATCH
The buffer passed was the wrong size for the attribute specified.
ERROR_ACCESS_DENIED
FileSetHandleExtAttributes() returns this if the caller does not have write-access to the file. FileSetPathExtAttributes() returns this if any geode (including the caller) has the file open with "deny-write" exclusive access, or if the file is not writable.
ERROR_CANNOT_BE_SET
The extended attribute cannot be changed. Such attributes as FEA_SIZE and FEA_NAME cannot be changed with the FileSet...() routines.

FEA_MULTIPLE

By passing this extended attribute, you can get or set several extended attributes at once. This is also the only way to get, set, or create a custom attribute. If you pass this, the other arguments are slightly different. The first argument is still the file specifier (handle or path), and the second argument is FEA_MULTIPLE. However, the third argument is the base address of an array of FileExtAttrDesc structures, and the fourth argument is the number of these structures in the array. The array has one element for each attribute you wish to get or set. Each FileExtAttrDesc structure has the following fields:

FEAD_attr
This is the numerical constant for the attribute to be read or set. If a custom attribute is being set, this should be FEA_CUSTOM.
FEAD_value
If the attribute is being set, this is the address of the new value. If the attribute is being read, this is the address of the buffer into which to copy the value.
FEAD_size
This is the size of the buffer or value pointed to by FEAD_value .
FEAD_name
If FEAD_attr is set to FEA_CUSTOM, this is the address of a null-terminated string containing the custom attribute's name. If FEAD_attr is set to anything else, this field is ignored.

FEA_CUSTOM

In addition to the system-defined extended attributes, any GEOS file may have any number of custom attributes. Each custom attribute is named by a null-terminated ASCII string. To create a custom attribute, call one of the FileSet...() routines, specifying the new attribute with a FileExtAttrDesc structure (as described immediately above). If you try to read a custom attribute which has not been defined for that file, the routine will fail with error condition ERROR_ATTR_NOT_FOUND.

Note that not all file systems support the use of custom extended attributes; therefore, you should write your applications so they can perform correctly without using them.

FEA_MODIFICATION

Every file has a "last modified" time. This is automatically updated whenever the file is written to. To find the modification time of a file, get the extended attribute FEA_MODIFICATION. The modification time is returned as a 32-bit FileDateAndTime value. The value has the following fields, each of which is small enough to fit in a signed-byte variable:

FDAT_YEAR
This field records the year, counting from a base year of 1980. (The constant FDAT_BASE_YEAR is defined as 1980.) This field is at an offset of FDAT_YEAR_OFFSET bits from the low end of the value.
FDAT_MONTH
This field records the month as an integer, with January being one. It is located at an offset of FDAT_MONTH_OFFSET.
FDAT_DAY
This field records the day of the month. It is located at an offset of FDAT_DAY_OFFSET.
FDAT_HOUR
This field records the hour on a 24-hour clock, with zero being the hour after midnight. It is located at an offset of FDAT_HOUR_OFFSET.
FDAT_MINUTE
This field records the minute. It is located at an offset of FDAT_MINUTE_OFFSET.
FDAT_2SECOND
This field records the second, divided by two; that is, a field value of 15 indicates the 30th second. (It is represented this way to let the second fit into 5 bits, thus letting the entire value fit into 32 bits.) It is located at an offset of FDAT_2SECOND_OFFSET.

The macros FDATExtractYear() , ...Month() , ...Day() , ...Hour() , ...Minute() , and ...2Second() all extract the specified field from a FileDateAndTime value. The macro FDATExtractSecond() extracts the FDAT_2SECOND field and doubles it before returning it. The FDATExtractYearAD() extracts the year field and adds the base year, thus producing a word-sized year value.

FEA_FILE_ATTR

There are certain attributes which all files have. These attributes specify such things as whether the file is hidden, whether it is read-only, and several other things. To get these attributes, call an extended attribute routine with argument FEA_FILE_ATTRIBUTES. The attributes are passed or returned in a FileAttrs record. This record has the following fields:

FA_ARCHIVE
This flag is set if the file requires backup. Backup programs typically clear this flag.
FA_SUBDIR
This flag is set if the "file" is actually a directory. Geodes may not change this flag.
FA_VOLUME
This flag is set if the "file" is actually the volume label. This flag will be off for all files a geode will ever see. Geodes may not change this flag.
FA_SYSTEM
This flag is set if the file is a system file. Geodes should not change this flag.
FA_HIDDEN
This flag is set if the file is hidden from normal directory searches. For example, a GenFileSelector, by default, does not list files that have this flag set.
FA_RDONLY
This flag is set if the file is read-only.

Many file systems (including DOS) require that files be closed when you set these attributes. For that reason, you cannot change these attributes with FileSetHandleExtAttributes() . You must use either FileSetPathExtAttributes() or FileSetAttributes() (described below in Getting and Setting Information about a Byte File ). If you try to set this field with FileSetHandleExtAttributes() , you will be returned ERROR_ATTR_CANNOT_BE_SET.

FEA_SIZE

This attribute is simply the size of the file in bytes. It is dword-sized (allowing for files as large as 4096 megabytes). The attribute can be read, but not directly changed.

FEA_FILE_TYPE

This attribute is a member of the GeosFileType enumerated type and should not be altered. The type has the following values:

GFT_NOT_GEOS_FILE
The file is not a GEOS file. This constant is guaranteed to be equal to zero.
GFT_EXECUTABLE
The file is executable; in other words, it is some kind of geode.
GFT_VM
The file is a VM file.
GFT_DATA
The file is a GEOS byte file (see below).
GFT_DIRECTORY
The file is a GEOS directory.
GFT_LINK
The file is a symbolic link (not yet implemented).

FEA_FLAGS

This attribute is a word-sized flag field, named GeosFileHeaderFlags . The following flags are implemented:

GFHF_TEMPLATE
The file is a document template.
GFHF_SHARED_SINGLE
The file can be opened for shared-single access.
GFHF_SHARED_MULTIPLE
The file can be opened for shared-multiple access.

Shared-single and shared-multiple access are described in the VM chapter. For more details, see the VM chapter.

FEA_RELEASE

This attribute is a ReleaseNumber structure. Generally, only geodes have release numbers. The structure has the following fields:

RN_major
The file's major release number. An increase in the major release number generally indicates a change which is not downward-compatible.
RN_minor
The file's minor release number. An increase in the minor release number generally indicates that the new version is compatible with previous versions.
RN _change
A field for use by individual manufacturers.
RN _engineering
A field for use by individual manufacturers.

FEA_PROTOCOL

This attribute contains the file's protocol numbers. A ProtocolNumber structure is returned. For a discussion of file protocols, see the Document Objects chapter.

FEA_TOKEN

This attribute is the file's token. It consists of a GeodeToken structure. For more information about tokens, see the Applications and Geodes chapter.

FEA_CREATOR

This attribute is the token of the document's creator. It consists of a GeodeToken structure. For more information about tokens, see the Applications and Geodes chapter.

FEA_USER_NOTES

This attribute is a null-terminated string. It is displayed in the file's "Info" box. Users can edit this string with GeoManager.

FEA_NOTICE

This attribute contains the file's copyright notice.

FEA_CREATION

This attribute is a FileDateAndTime structure. It contains the time when the file was created.

FEA_PASSWORD

This attribute contains the file's encrypted password, if any.

FEA_NAME

This attribute contains the file's virtual name. It is a null-terminated character string.

FEA_GEODE_ATTR

This attribute contains information about the geode. If the file is not a geode, this field's value will be zero. If it is a geode, it will contain a record of GeodeAttrs . This record has the following fields:

GA_PROCESS
This geode has a process thread.
GA_LIBRARY
This geode is a library.
GA_DRIVER
This geode is a driver.
GA_KEEP_FILE_OPEN
The GEOS kernel keeps this file open while GEOS is running.
GA_SYSTEM
This file is part of the kernel.
GA_MULTI_LAUNCHABLE
This geode can be loaded more than once simultaneously.
GA_DRIVER_INITIALIZED
The geode is a driver which has been opened, loaded and initialized.
GA_LIBRARY_INITIALIZED
The geode is a library which has been opened and loaded, and whose entry point has been called.
GA_GEODE_INITIALIZED
The geode has been opened and completely initialized.
GA_USES_COPROC
The geode uses a math coprocessor, if one is available.
GA_REQUIRES_COPROC
The geode can only run if a math coprocessor or emulator is present.
GA_HAS_GENERAL_CONSUMER_MODE
The geode is an application which can be run in GCM mode.
GA_HAS_ENTRY_POINTS_IN_C
This geode is a library or driver which can be called from C code.

FEA_PATH_INFO

This field contains information about the file's path. It is for internal use by the kernel.

FEA_FILE_ID

This field is for internal use by the kernel.

FEA_DESKTOP_INFO

This field is for use by the desktop manager.

FEA_DRIVE_STATUS

This attribute contains the DriveExtendedStatus word for the drive containing the file. The DriveExtendedStatus value is described in Accessing Drives .

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Files and File Handles | Next: 5.4 File Utilities


File Utilities

The File System: 5.4 Files: File Utilities

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 GEOS Extended Attributes | Next: 5.5 FileEnum()
FileDelete(), FileRename(), FileCopy(), FileMove(),FileGetDiskHandle()

Most of the time, such actions as copying, deleting, and renaming files are handled by desktop management programs like GeoManager. However, other geodes may need to perform these actions themselves. For example, if you use a temporary file, you may wish to delete it when you're done. The GEOS file system provides routines for these situations. One file utility, FileEnum() , is elaborate enough to be treated in its own section; for details, see FileEnum() .

To delete a file, call FileDelete() . This routine takes one argument, namely the address of a path string. If it can delete the file, it returns zero; otherwise, it returns an error code. Common error conditions include the following:

ERROR_FILE_NOT_FOUND
No such file exists in the specified directory.
ERROR_PATH_NOT_FOUND
An invalid path string was passed.
ERROR_ACCESS_DENIED
You do not have permission to delete that file, or it exists on a read-only volume.
ERROR_FILE_IN_USE
Some geode has that file open.

To change a file's name, call FileRename() . This routine takes two arguments: a pointer to the path string specifying the file, and a pointer to a string specifying the new name for the file. If successful, FileRename() returns zero; otherwise, it returns one of the above error codes.

To make a copy of a file, call FileCopy() . This routine takes four arguments: the handles of the source and destination disks (which may be the same), and the addresses of source and destination path strings. Passing a disk handle of zero indicates the current path's disk. Each string specifies a path relative to the location specified by the corresponding disk handle. If the handle is a disk handle, the path is relative to that disk's root. If the disk handle is a standard path constant, the path string is relative to that standard path. If the disk handle is null, the path is relative to the current working directory. FileCopy() will make a copy of the file in the specified location with the specified name. If a file with that name and location already exists, it will be overwritten. FileCopy() returns zero if successful. Otherwise it returns one of the following error codes:

ERROR_FILE_NOT_FOUND
No such source file exists in the specified directory.
ERROR_PATH_NOT_FOUND
An invalid source or destination path string was passed.
ERROR_ACCESS_DENIED
You do not have permission to delete the existing copy of the destination file, or the destination disk or directory is not writable.
ERROR_FILE_IN_USE
Some geode has the existing destination file open.
ERROR_SHORT_READ_WRITE
There was not enough room on the destination disk. The destination disk will be left unchanged (i.e. a partial copy of the file will not be made).

To move a file from one directory to another, either on the same disk or on different disks, call FileMove() . This routine takes four arguments: the handles of the source and destination disks (which may be the same), and pointers to source and destination path strings. Passing a null disk handle indicates the current working directory. Each string specifies a path relative to the location specified by the corresponding disk handle. If the handle is a disk handle, the path is relative to that disk's root. If the disk handle is a standard path constant, the path string is relative to that standard path. If the disk handle is null, the path is relative to the current working directory. If the copy is successful, FileMove() will return zero; otherwise, it will return one of the above error codes.

If you want to find out the handle of the disk containing an open file, call FileGetDiskHandle() . This routine is passed the file handle; it returns the disk handle. This is useful if the geode has to prepare for a shutdown; it can get the disk handle with FileGetDiskHandle() , then save that handle with DiskSave() (see Saving and Restoring a Disk Handle ). With this information (and the file name), the geode will be able to reopen the file when it restarts.

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 GEOS Extended Attributes | Next: 5.5 FileEnum()


FileEnum()

The File System: 5.5 Files: FileEnum()

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 File Utilities | Next: 5.6 Bytewise File Operations
FileEnum(), FileEnumLocateAttr(), FileEnumWildcard(), FileEnumAttrs, FileEnumSearchFlags, FileEnumStandardReturnType, FEDosInfo, FENameAndAttr, FileEnumCallbackData

You may sometimes need to perform an action on every file that matches certain criteria. For these situations, the file system provides the routine FileEnum() . FileEnum() can be called in two ways. The usual way is to have FileEnum() provide certain information about every file in a directory (or every file of a given type); when called, FileEnum() will allocate a buffer and fill it with data structures, one for each matching file. Less commonly, you can have FileEnum() call a callback routine for every file which matches certain criteria; this callback routine can take a broader range of actions.

You should not often need to use the FileEnum() routine. The most common time that applications would need this functionality is when they present a list of the files in a directory to the user. The GenFileSelector object provides this functionality, making all necessary calls to FileEnum() transparently to both the user and the geode. Note that the document control objects automatically create and manipulate a file selector when appropriate. For further information, see the FileSelector object chapter.

FileEnum() takes the following arguments:

  • A pointer to a FileEnumParams structure (see below). The data in this structure will tell FileEnum() what to do.
  • A pointer to a MemHandle variable. FileEnum() will allocate a memory block to hold information about the files, and will write the block's handle to this location.
  • A pointer to a word variable. If FileEnum() was unable to fit information about all the files into the block passed, it will write the number of files not handled into this variable.

FileEnum() returns the number of files which were returned in the buffer.

The FileEnumParams structure specifies how FileEnum() should perform. The structure is defined as follows. A summary of each field's role follows; for full details, see the reference entry for FileEnum() .

typedef struct _FileEnumParams {
    FileEnumSearchFlags 				FEP_searchFlags;
    FileExtAttrDesc *				FEP_returnAttrs;
    word 				FEP_returnSize;
    FileExtAttrDesc *				FEP_matchAttrs;
    word 				FEP_bufSize;
    word 				FEP_skipCount;
    word _pascal (*FEP_callback)
        (struct _FileEnumParams *					params,
         FileEnumCallbackData *					fecd,
         word 					frame);
    FileExtAttrDesc *				FEP_callbackAttrs;
    dword 				FEP_cbData1;
    dword 				FEP_cbData2;
    word 				FEP_headerSize;
} FileEnumParams;
FEP_searchFlags
This is a byte-length flag field. The flags are of type FileEnumSearchFlags (described below). These flags specify which files at the current location will be examined by FileEnum() . They also specify such things as whether a callback routine should be used.
FEP_returnAttrs
This field specifies what information is wanted about the files. It is a pointer to an array of FileExtAttrDesc structures (see FEA_MULTIPLE ). The attributes will be written to the return buffer; each file will have an entry, containing all the attributes requested for that file. You can also request certain return values by setting FEP_returnAttrs to equal a member of the FileEnumStandardReturnType (again, by casting the FileEnumStandardReturnType value to type void * ). The FileEnumStandardReturnType enumerated type is described later in this section.
FEP_returnSize
This is the size of each entry in the returned buffer. If a standard return type or an array of FileExtAttrDesc structures was passed, each entry in the returned buffer will contain all the extended attribute information requested for that file.
FEP_matchAttrs
This field specifies which files should be enumerated by FileEnum() . It is a pointer to an array of FileExtAttrDesc structures (see FEA_MULTIPLE ). These structures specify values for certain extended attributes. Only those files whose extended attributes match these will be enumerated. If you do not want to filter out any files in the working directory, or if you will use the callback routine to filter the files, pass a null pointer in this field.
FEP_bufsize
This field specifies the maximum number of entries to be returned in the buffer. If you do not want to set a limit, pass the constant FEP_BUFSIZE_UNLIMITED. The buffer will be grown as necessary.
FEP_skipCount
This field contains the number of matching files to be ignored before the first one is processed. It is often used in conjunction with FEP_bufSize to examine many files a few at a time. Each time FileEnum() is called, you can enumerate a certain number of files; by adjusting the skip count each time, you can start each enumeration where the previous one ended. In this way you could walk through all the matching files in the directory. Note that if the FileEnumSearchFlags bit FESF_REAL_SKIP is set (in FEP _searchFlags ), the first files in the directory will be skipped before they are tested to see if they match. This is faster, since the match condition won't have to be checked for the first files in the directory.
FEP_callback
This field holds a pointer to a Boolean callback routine. The callback routine can check to see if the file matches some other arbitrary criteria. The callback routine is called for any files which match all the above criteria. It can then decide whether to enumerate the file however it wishes. The callback routine should be declared _pascal . If the file should be accepted by FileEnum() , the callback should return true ; otherwise it should return false . You can also instruct FileEnum() to use one of the standard callback routines by passing a member of the FileEnumStandardCallback enumerated type. In this case, FEP_callbackAttrs is ignored; FileEnum() will automatically pass the appropriate information to the callback routine. (Note that if the FESF_CALLBACK bit of the FEP_searchFlags field is not set, the FEP_callback field is ignored.) The callback routine may not call any routines which are in movable memory at the time FileEnum() is called, except for routines which are in the same resource as the callback routine.
FEP_callbackAttrs
This field specifies what additional attributes should be read for the callback routine. This field is a pointer to an array of FileExtAttrDesc structures (see FEA_MULTIPLE ). The array will be filled in with the appropriate information for each file before the callback routine is called. Note that if the FESF_CALLBACK bit of the FEP_searchFlags is not set, the FEP_callbackAttrs is ignored. If you do not need any attributes passed to the callback routine, set this field to be a null pointer.
FEP_cbData1 , FEP_cbData2
These are dword-length fields. Their contents are ignored by FileEnum() ; they are used to pass information to the callback routine. If you do not call a standard callback routine, you may use these fields any way you wish.
FEP_headerSize
If the flag FESF_LEAVE_HEADER is set, FileEnum() will leave an empty header space at the beginning of the return buffer. The size of the header is specified by this field. If the flag FESF_LEAVE_HEADER is clear, this field is ignored.

The first field of the FileEnumParams structure, FEP_searchFlags , is a word-length record containing FileEnumSearchFlags . The following flags are available:

FESF_DIRS
Directories should be examined by FileEnum() .
FESF_NON_GEOS
Non-GEOS files should be examined by FileEnum() .
FESF_GEOS_EXECS
GEOS executable files should be examined by FileEnum() .
FESF_GEOS_NON_EXECS
GEOS non-executable files (e.g., VM files) should be examined by FileEnum() .
FESF_REAL_SKIP
If a skip count of n is specified, the first n files will be skipped regardless of whether they matched the attributes passed. In this case, FileEnum() will return the number of files passed through in order to get enough files to fill the buffer; the return value can thus be the real-skip count for the next pass.
FESF_CALLBACK
FileEnum() should call a callback routine to determine whether a file should be accepted.
FESF_LOCK_CB_DATA
This flag indicates that the FileEnumParams fields FEP_callback1 and FEP_callback2 are far pointers to movable memory that must be locked before FileEnum() is called.
FESF_LEAVE_HEADER
If set, FileEnum() should leave an empty header space at the start of the return buffer. The size of this buffer is specified by the FEP_headerSize field.

You can specify precisely which data you want about each file and in which arrangement it should be returned. However, for most purposes, you can get enough information by requesting one of the standard return types. If you pass a member of FileEnumStandardReturnType in FEP_returnAttrs , FileEnum() will write an array of structures to the return buffer; each file will have one such structure. The structures are shown below in Standard FileEnum() Return Structures . FileEnumStandardReturnType has the following values:

FESRT_COUNT_ONLY
FileEnum() will not allocate any memory or return data about files; instead, it will simply return the number of files which match the specified criteria.
FESRT_DOS_INFO
FileEnum() will return an array of FEDosInfo structures. These structures contain basic information about the file: its virtual name, size, modification date, DOS attributes, and path information (as a DirPathInfo record).
FESRT_NAME
FileEnum() will return an array of FileLongName strings, each one of which is FILE_LONGNAME_BUFFER_SIZE characters long; every one of these will contain a file's virtual name followed by a null terminator.
FESRT_NAME_AND_ATTR
FileEnum() will return an array of FENameAndAttr structures, each one of which contains a file's DOS attributes and virtual name.

The FEDosInfo structure includes a word-sized record which describes the file's position relative to the standard paths. It contains the following fields:

DPI_EXISTS_LOCALLY
This bit is set if the file exists in a directory under the primary tree.
DPI_ENTRY_NUMBER_IN_PATH
This is the mask for a seven-bit field whose offset is DPI_ENTRY_NUMBER_IN_PATH_OFFSET.
DPI_STD_PATH
This is the mask for an eight-bit field whose offset is DPI_STD_PATH_OFFSET. If the file is in a standard path, this field will contain a StandardPath constant for a standard path containing the file. This need not be the "closest" standard path; for example, if the file is in the "World" directory, this constant might nevertheless be SP_TOP.

Code Display 17-2 Standard FileEnum() Return Structures

/* These structures are the standard FileEnum() return types. You can also
 * instruct FileEnum() to return any arbitrary extended attribute information.
 */
typedef struct {			/* These are returned if you specify FESRT_DOS_INFO */
    FileAttrs 		FEDI_attributes;			/* File's DOS attributes;
                     * see  Getting and Setting Information about a Byte File */
    FileDateAndTime 		FEDI_modTimeDate			/* Last modification time; see  FEA_MODIFICATION */
    dword 		FEDI_fileSize;			/* Size of file (in bytes) */
    FileLongName		FEDI_name;			/* File's virtual name; null-terminated */
    DirPathInfo 		FEDI_pathInfo;			/* PathInfo structure, described above */
} FEDosInfo;
typedef struct{ 			/* These are returned if you specify FESRT_NAME_AND_ATTR */
    FileAttrs 		FENAA_attributes;			/* File's DOS attributes;
                     * see  Getting and Setting Information about a Byte File */
    FileLongName 		FENAA_name;			/* File's virtual name; null-terminated */
} FENameAndAttr;

There is currently one standard callback routine provided. This routine, FileEnumWildcard() , rejects all files whose names don't match a wildcard string. To call this routine, set FEP_callback as follows:

enumParams.FEP_callback = (void *) FESC_WILDCARD;

FEP_cbData1 should be a pointer to a null-terminated wildcard string. In this string, "*" denotes a sequence of zero or more of any character, and "?" denotes any one character; all other characters represent themselves. If a file's virtual name matches the wildcard string, information about it will be returned by FileEnum() . If the lowest byte of FEP_cbData2 is non-zero, the comparison will be case-insensitive; otherwise, it will be case-sensitive. (The other bytes of FEP_cbData2 are ignored.) This is different from standard DOS wildcarding; for example, the name "doc*." matches any file that begins with the letters "doc" and ends with a period, whereas a DOS wildcard string "doc*." would match only those files whose name start with "doc" and which have no extension.

If you're writing your own callback routine, you may wish to use the FileEnumLocateAttr() utility routine. This routine searches a FileExtAttrDesc array (as passed to FileEnum() callbacks), and will search the array for a file matching a particular attribute.

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 File Utilities | Next: 5.6 Bytewise File Operations


Bytewise File Operations

The File System: 5.6 Files: Bytewise File Operations

| Up: GEOS SDK TechDocs | Up | Prev: 5.5 FileEnum()

There are several routines designed for working with files as a string of bytes. These routines may be used to work with DOS files or with GEOS byte files. You can open any file (including an executable file or a VM file) for byte-level access. This may be useful for such things as file-compression routines; however, be aware that if you make any changes to such files, you could invalidate them. For this reason, if you open a VM or executable file for byte-level access, you should open it for read-only use.

Opening and Closing Files

FileOpen(), FileCreate(), FileCreateTempFile(), FileClose(), FileAccessFlags

The GEOS file system provides several routines for opening files for byte-level access. If you are working with GEOS Virtual Memory files, you should use the appropriate VM routines to open and close the files (see the VM chapter). You should use the byte-level routines only if you are working with DOS files or with GEOS byte files. You may occasionally want to read a VM file or an executable file as a string of bytes. In this rare case, you must use the routines in this section. Note, however, that you should not change the VM file with these routines; it is safe only to open it for read-only access.

To open a file, call FileOpen() . This routine takes two arguments: a set of FileAccessFlags and a pointer to a null-terminated string. The string should specify the name of the file (either the virtual name or the native name may be used). It may simply be a file name, or it may be a relative or absolute path. The FileAccessFlags record specifies two things: what kind of access the caller wants, and what type of access is permitted to other geodes. A set of FileAccessFlags is thus a bit-wise OR of two different values. The first specifies what kind of access the calling geode wants and has the following values:

FILE_ACCESS_R
The geode will only be reading from the file.
FILE_ACCESS_W
The geode will write to the file but will not read from it.
FILE_ACCESS_RW
The geode will read from and write to the file.

The second part specifies what kind of access other geodes may have. Note that if you try to deny a permission which has already been given to another geode (e.g. you open a file with FILE_DENY_W when another geode has the file open for write-access), the call will fail. The following permissions can be used:

FILE_DENY_RW
No geode may open the file for any kind of access, whether read, write, or read/write.
FILE_DENY_R
No geode may open the file for read or read/write access.
FILE_DENY_W
No geode may open the file for write or read/write access.
FILE_DENY_NONE
Other geodes may open the file for any kind of access.

Two flags, one from each of these sets of values, are combined to make up a proper FileAccessFlags value. For example, to open the file for read-only access while prohibiting other geodes from writing to the file, you would pass the flags FILE_ACCESS_R and FILE_DENY_W as follows:

myHandle = FileOpen("MyFile",
        (FILE_ACCESS_R | FILE_DENY_W));

If successful, FileOpen() returns the file's handle. If it is unsuccessful, it returns a null handle and sets the thread's error value. The following error values are commonly returned:

ERROR_FILE_NOT_FOUND
No file with the specified name could be found in the appropriate directory.
ERROR_PATH_NOT_FOUND
A relative or absolute path had been passed, and the path included a directory which did not exist.
ERROR_TOO_MANY_OPEN_FILES
There is a limit to how many files may be open at once. If this limit is reached, FileOpen() will fail until a file is closed.
ERROR_ACCESS_DENIED
Either the caller requested access which could not be granted (e.g. it requested write access when another geode had already opened the file with FILE_DENY_W), or the caller tried to deny access when that access had already been granted to another geode (e.g. it tried to open the file with FILE_DENY_W when another geode already had it open for write-access).
ERROR_WRITE_PROTECTED
The caller requested write or read-write access to a file in a write-protected volume.

Note that if you use the document control objects, they automatically make all appropriate calls to FileOpen() when the user requests it; you will automatically be passed the file's handle.

FileOpen() can only be called if the file already exists. In order to create a byte file, you must call FileCreate() . FileCreate() takes four arguments: a set of FileCreateFlags , a set of FileAccessFlags , a set of FileAttrs , and a pointer to a string containing a name for the file. As with FileOpen() , the name may be a name alone or a relative or absolute path. The FileCreateFlags specifies whether the file should be created if it already exists. The following flags are available:

FILE_CREATE_TRUNCATE
If a file with the given name exists, it should be opened and truncated; that is, all data should be deleted.
FILE_CREATE_NO_TRUNCATE
If the file exists, it should be opened without being truncated.
FILE_CREATE_ONLY
If the file exists, the routine should fail and set the thread's error value to ERROR_FILE_EXISTS.
FCF_NATIVE
This flag is combined with one of the above flags if the file should be created in the device's native format; e.g. if it should be a DOS file instead of a GEOS file. The name passed must be an acceptable native file name. If a GEOS file with the specified name already exists, FileCreate() will fail with error condition ERROR_FILE_FORMAT_MISMATCH.

The first three flags (FILE_CREATE_...) are mutually exclusive; exactly one of them must be passed to FileCreate() . That flag may or may not be combined with FCF_NATIVE.

The FileAccessFlags are the same as described in FileOpen() . Note, however, that you must request either write access or read/write access when you use FileCreate() .

Every file has a set of attributes. These record certain information about the file. If you create a file, you will need to specify values for these attributes. The attributes are described above in the section on FEA_FILE_ATTR .

If FileCreate() is successful, it will open the file and return its handle. If it fails, it will return a null handle and set the thread's error value. It may return any of the FileOpen() errors. It may also return the following errors:

ERROR_FILE_EXISTS
Returned if FileCreate() was called with FILE_CREATE_ONLY and a file with the specified name already exists.
ERROR_FORMAT_MISMATCH
Returned if FileCreate() was called with FILE_CREATE_TRUNCATE or FILE_CREATE_NO_TRUNCATE and a file exists in a different format than desired; i.e. you passed FCF_NATIVE and the file already exists in the GEOS format, or vice versa.

It is often useful to create temporary files which are not seen by the user. In these cases, you generally don't care about the file's name since you will most likely be deleting the file on exit. For these situations GEOS provides the routine FileCreateTempFile() . FileCreateTempFile() is passed a directory; it chooses a unique name for the file. This routine takes two arguments:

  • A set of FileAttrs , as described above.
  • A pointer to a null-terminated string specifying the path for the temporary file. This path may be relative or absolute. To create the temporary file in the current directory, pass the string "." This string should contain fourteen extra null bytes at the end, as FileCreateTempFile() will write the name of the temporary file at the end of the string. Temporary files are typically created in SP_WASTE_BASKET.

If successful, FileCreateTempFile() will open the temporary file and return its handle. It will also write the file's name to the end of the string passed. You will need to know the name to delete the file. The name is also useful if GEOS shuts down while a temporary file is open; the geode will need to know the temporary file's name in order to reopen it.

When you are done with a file, you should close it by calling FileClose() . This releases any restrictions you may have placed on the file and allows the file to be moved or deleted. It is passed two arguments: the file handle and a Boolean value which should be set to true (i.e. non-zero) if the geode cannot handle error messages; it will cause FileClose() to fatal-error if it cannot successfully close the file. (This should only be used during development; the flag should never be passed in a finished program.) The routine returns zero if successful; otherwise, it returns a FileError value.

Reading From and Writing To Files

FileRead(), FileWrite(), FilePos(), FileCommit()

There are a few specific operations you are allowed to perform on data in a byte-file. You can copy data from the file into memory; you can copy data from memory into the file, overwriting the file's contents; you can write data to the end of a file; and you can cut data from the end of the file. If you want to perform more elaborate manipulations on a byte-file, you may wish to create a temporary VM file and copy the data there (see the VM chapter).

Every file handle has a file position associated with it. All read and write operations begin at that position; they may also change the position. The first byte in a file is considered to be at position zero. If the file is a GEOS byte file, position zero is immediately after the GEOS header; thus, the header cannot be accessed or altered via the read and write operations.

To read data from a file, call FileRead() . This routine takes four arguments. The first is the file's handle. The second is the address of a buffer. FileRead() will copy the requested number of bytes from the file to the buffer. The third is the number of bytes to read. The fourth is a Boolean indicating whether the caller can handle errors. (This is true if the geode cannot handle error messages; it will cause FileRead() to fatal-error if it cannot successfully read the data. This should only be used during development; the flag should never be passed in a finished program.) FileRead() returns the number of bytes actually read. This may be less than the number requested, if the end of file is reached; in this case, the thread's error value will be set to ERROR_SHORT_READ_WRITE. If FileRead() was unable to gain access to the file, it will return -1 and set the thread's error value to ERROR_ACCESS_DENIED. In any event, the file position will be incremented by the number of bytes read; thus, it will point to the first byte after the data read.

To write data to a file, call FileWrite() . This routine takes four arguments. The first is the file's handle. The second is the address of a buffer in memory. The third is the number of bytes to write. The fourth is a Boolean indicating whether the caller can handle errors. FileWrite() will copy the specified number of bytes from the buffer to the file, starting at the current position and expanding the file as necessary. It will also increment the current position by the number of bytes written. If the current position is not at the end of the file, FileWrite() will overwrite the file's existing data. FileWrite() returns the number of bytes written. This may be less than the number requested, if the disk ran out of space; in this case, the thread's error value will be set to ERROR_SHORT_READ_WRITE. If FileWrite() could not get access to the file (as, for example, if the geode had read-only access to the file), it will return -1 and set the thread's error value to ERROR_ACCESS_DENIED.

If a file is on a removable disk, the kernel will make sure that the disk is in the appropriate drive before reading from or writing to it. If the disk is not in the drive, the kernel will prompt the user to insert it. The user will have the option of aborting the operation; this will result in the file-access routine failing with error condition ERROR_DISK_UNAVAILABLE.

When you write changes to a file, either the GEOS file system or the underlying DOS may choose to cache those changes to save time. All cached changes will be written to the disk when the file is closed. However, you can force the cached changes to be written immediately by calling FileCommit() . This routine takes two arguments. The first is the file's handle. The second is a Boolean indicating whether the caller can handle errors. The routine returns zero if the operation was successful; otherwise it returns an error code.

To change the current file position, call FilePos() . This routine takes three arguments. The first is the file handle. The second is a member of the FilePosMode enumerated type; this value indicates how the new position is specified. The third argument is a number of bytes; it specifies how far the file position will be moved. FilePosMode has the following possible values:

FILE_POS_START
The file position is set to a specified number of bytes after the start of the file. Passing this mode with an offset of zero will set the file position to the start of the file (i.e. immediately after the header information).
FILE_POS_RELATIVE
The file position is incremented or decremented by a specified number of bytes.
FILE_POS_END
The file position is set to a specified number of bytes before the end of the file. Passing this mode with an offset of zero will set the file position to the end of the file.

FilePos() returns a 32-bit integer. This integer specifies the file position after the move (relative to the start of the file). To find out the current file position without changing it, call FilePos() with mode FILE_POS_RELATIVE and offset zero.

Getting and Setting Information about a Byte File

FileGetDateAndTime(), FileSetDateAndTime(), FileGetAttributes(), FileSetAttributes()

GEOS provides several routines to get information about files. To get information about a GEOS file, you would ordinarily use one of the extended attributes routines (see GEOS Extended Attributes ). These routines are ordinarily used for non-GEOS files. Nevertheless, all of the following routines can be used on GEOS files.

FileGetDateAndTime() and FileSetDateAndTime() are used to get and set the file's modification time. To access a GEOS file's modification time, you would ordinarily call an extended attribute routine, passing FEA_MODIFICATION. However, special-purpose routines are provided specifically for changing a file's modification time. Note that these routines may be used for GEOS or non-GEOS files. Similarly, you can change the FEA_MODIFICATION attribute even for non-GEOS files. To find out the modification time, call FileGetDateAndTime() . This routine is passed the file's handle and returns a FileDateAndTime value (as described above on FEA_MODIFICATION ). To change the modification time, call FileSetDateAndTime() . This routine is passed the file's handle and a FileDateAndTime value. If successful, it returns zero; otherwise, it returns an error code. You must have write permission to change the modification time; otherwise, FileSetDateAndTime() will fail with condition ERROR_ACCESS_DENIED. The TimerGetFileDateTime() routine returns the current date and time in a FileDateAndTime structure.

To find out a DOS file's attributes, call FileGetAttributes() . This routine is passed a file's path. It returns the file's FileAttrs record (as described on FEA_FILE_ATTR ). To change the file's attributes, call FileSetAttributes() . This routine takes two arguments: the address of a null-terminated path string and a FileAttrs record. It returns zero if it was successful; otherwise, it returns an error condition. Note that a file's attributes cannot be changed if the file is open.

Data-Access Synchronization

FileLockRecord(), FileUnlockRecord()

GEOS provides routines to help threads synchronize file access. This functionality is very elaborate for VM files. For byte files it is less so. Several threads can synchronize their access to a single handle by using HandleP() and HandleV() , described in the Memory Management chapter. If they want to use the file at the same time, they should use FileLockRecord() and FileUnlockRecord() .

FileLockRecord() takes three arguments: the file handle, a dword specifying the start of the region to be locked, and a dword specifying the length (in bytes) of the region to be locked. If there are no locks on any part of that region, FileLockRecord() returns zero; otherwise, it returns the error code ERROR_ALREADY_LOCKED. Note that there is nothing to stop another thread or geode from reading or writing to that region. The lock simply prevents anyone from locking that region with FileLockRecord() . The file's users have to remember to lock any part of the file before accessing it.

To release a lock on a part of a file, call FileUnlockRecord() . This routine takes the same arguments as FileLockRecord() .

|  Up: GEOS SDK TechDocs | Up | Prev: 5.5 FileEnum()


The FileSelector Object

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 File Selector Overview
| One-page Version of this Chapter for Easy Printing

The GenFileSelector provides the user interface and functionality to allow a user to traverse directories and volumes (disks) and view the files contained therein. It can be customized to limit the displayed file names or to expand the listings to include special files.

This object is used by the GenDocumentControl object to provide most file dialog boxes. If your application uses the Document Control objects, you will probably not need to add a GenFileSelector.

1 File Selector Overview
2 File Selector Basics
2.1 Setting Up the File Selector
2.2 Supporting the File Selector
2.3 Messages to Handle
2.4 Some Common Customizations
3 File Selector Instance Data
3.1 The GFSI_attrs Field
3.2 The GFSI_fileCriteria Field
3.3 Matching a File's Token
3.4 Matching a File's Creator App
3.5 Matching a File's Geode Attributes
3.6 Masking File Names
3.7 Matching a File's File Attributes
3.8 Searching Via Callback Routine
3.9 Resetting a Filter
4 File Selector Use
4.1 When a User Selects a File
4.2 The Current Selection
4.3 Rescanning Directories
4.4 Setting Scalable UI Data

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 File Selector Overview
| One-page Version of this Chapter for Easy Printing


File Selector Overview

The FileSelector Object: 1 File Selector Overview

| Up: GEOS SDK TechDocs | Up | Prev: The FileSelector Object | Next: 2 File Selector Basics

When you are not using the Document Control objects but need to allow the user to traverse directories and disks in order to locate a given file, you should use a GenFileSelector. The File Selector is designed to give tremendous flexibility for many different types of file searches, providing both directory scanning and user interface. An OSF/Motif File Selector is shown below.

The File Selector allows users to browse all the directories and disks readable by DOS, including readable network drives. This browsing is completely internal to the File Selector; until the user selects a file from the list, your application does not need to know what the File Selector is doing.

You can customize the searches the File Selector makes in several different ways:

  • Setting paths, volumes, and selections
    You may direct the File Selector to show any given directory on any given volume, and you may also set the selected file displayed. Additionally, you can navigate directories with messages sent to the File Selector. See The Current Selection .
  • Limiting a directory scan to certain files
    The File Selector can limit its directory scans to show only the files you request. You may limit the scan based on file types, file attributes, or filename extensions. See File Selector Instance Data .
  • Searching for associated files
    You can limit the File Selector's searches to only those files that match a given creator or token. That is, the File Selector will display only files created by a particular geode. See Matching a File's Token .
  • Customizing the searches
    If the available search options don't fit your needs, you can create a customized search. If this type of search is employed, the File Selector will send a special message to itself--to take advantage of this, you can easily subclass the GenFileSelector and write a custom handler for this special message. See Searching Via Callback Routine .

All of the directory scanning and path navigation is internal to the GenFileSelector. When a selection is made (a user single- or double-clicks on an entry, for example), the File Selector will send out a message indicating the selection. You can, however, force the File Selector to change to any directory or search criteria at any time.

| Up: GEOS SDK TechDocs | Up | Prev: The FileSelector Object | Next: 2 File Selector Basics


File Selector Basics

The FileSelector Object: 2 File Selector Basics

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 File Selector Overview | Next: 3 File Selector Instance Data

The following sections describe how to set up and use the File Selector in a simple configuration and how to modify the configuration for common uses.

Most File Selectors will be put in dialog boxes and will be accompanied by at the least a Reply Bar. (For more information on dialog boxes and reply bars, see the Menus and Dialog Boxes chapter.) Typically, the dialog box will be brought up by a menu item such as Open... or Insert From Text File... .

The figure above shows a sample Insert From Text File dialog box. (Note that you would probably use the Impex object for this functionality; this is provided for example.) This box has three basic elements: The GenGlyphDisplay (the title at the top), the File Selector (the scrolling list of files and the disk and folder icons), and the Reply Bar. In nearly all cases, these three elements will be included with every File Selector in its dialog box.

Setting Up the File Selector

Supporting the File Selector

Messages to Handle

Some Common Customizations

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 File Selector Overview | Next: 3 File Selector Instance Data


Setting Up the File Selector

The FileSelector Object: 2.1 File Selector Basics: Setting Up the File Selector

| Up: GEOS SDK TechDocs | Up | Prev: 2 File Selector Basics | Next: 2.2 Supporting the File Selector

For a typical configuration, you will have to set up a GenInteraction as a dialog box. (For information dialog boxes, see the Menus and Dialog Boxes chapter.) The File Selector and other elements of the dialog should be placed as children of the Interaction.

A Basic File Selector shows the Goc code required to display and use a File Selector in this basic form. You can use this code to create a similar dialog box with the proper elements or modify it to gain the functionality you want. Specific permutations of this configuration will be described in Some Common Customizations ; however, the remainder of this section describes the basics of the File Selector.

Code Display 14-1 A Basic File Selector

/* Extra code for menus and other UI objects is left out. Only what is
 * required for these objects is included. */
/* This dialog box provides the organization needed to contain the File
 * Selector, the name of the box, and the Reply Bar. */
@object GenInteractionClass MyDialogBox = {
    GI_visMoniker = `I', "Insert From Text File";
    GI_comp = MyGlyph, MyFileSel, MyInsertTrigger;
    GII_visibility = (GIV_DIALOG);
    GII_type = (GIT_COMMAND);
};
/* The Glyph Display provides the label in the dialog box. Alternatively, the
 * moniker of the GenInteraction could be used for this functionality. */
@object GenGlyphClass MyGlyph = {
    GI_visMoniker = "Insert From Text File";
}
/* The File Selector is as basic as possible. */
@object GenFileSelectorClass MyFileSel = {
    GFSI_destination = process;					/* The object receiving notification. */
    GFSI_notificationMsg = MSG_MY_APP_FILE_SELECTED;							/* The message sent
                             * upon selection. */
};
@object GenTriggerClass MyInsertTrigger = {
    GI_visMoniker = "Insert";
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_APP_INSERT_TRIGGER_SELECTED;
    HINT_SEEK_REPLY_BAR;
};
/* Note that in most cases, this trigger will not be needed. Generally, the File
 * Selector will include an "OK" or "Use This File" trigger in its reply bar that
 * executes an IC_OK function. For your trigger above to demonstrate that
 * functionality, remove the GTI_... fields and add the line
 * ATTR_GEN_TRIGGER_INTERACTION_COMMAND = (IC_OK);
 */
| Up: GEOS SDK TechDocs | Up | Prev: 2 File Selector Basics | Next: 2.2 Supporting the File Selector


Supporting the File Selector

The FileSelector Object: 2.2 File Selector Basics: Supporting the File Selector

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Setting Up the File Selector | Next: 2.3 Messages to Handle

To use the File Selector as shown in A Basic File Selector , your application must have some object (which may be the Process object) that can handle all the cases that may arise from a user's interaction with the dialog box. All of these cases are listed below:

  • The user clicks once on an entry in the File Selector's list.
    When this happens, the File Selector highlights the selected entry and sends its notification message to its destination object. With the message will come a record of flags indicating the action was a single click.
  • The user double-clicks on an entry in the File Selector's list.
    This action causes the File Selector to send its notification message to its destination object. The default action for the dialog box containing the File Selector will also occur (e.g. the default action is what happens when the "Insert" trigger is pressed). If the current selection is a directory, the application does not need to do anything; the File Selector will open the directory automatically. If the current selection is a file, the application must deal with the file properly and then close the dialog box containing the File Selector.
  • The user clicks on the "Insert" trigger.
    This action causes the trigger to send its notification message to its destination object. In most cases, the trigger with be an IC_OK trigger that will cause the File Selector to act as if a double-click had occurred. If the current selected entry in the list is a directory, the application should instruct the File Selector to open the directory and display its file list. If the current selection is a file, the application is responsible for dealing properly with the file; additionally, the application should then close the dialog box containing the File Selector.

The above list describes the basics of File Selector support. Different File Selectors (such as SaveAs dialogs that allow the user to type in a file name) can easily be defined by adding other gadgetry to the controller. All other actions possible are internal to the file selector (e.g. the "Go To Document" button is a feature of the File Selector which is invisible to the application).

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Setting Up the File Selector | Next: 2.3 Messages to Handle


Messages to Handle

The FileSelector Object: 2.3 File Selector Basics: Messages to Handle

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Supporting the File Selector | Next: 2.4 Some Common Customizations

The list above shows two different messages that your File Selector's basic configuration: The first results from the user clicking on an entry and comes from the File Selector object, and the second is sent by the "Insert" trigger when the user clicks on it. The following descriptions show what you must do when handling these messages. (Note that in many cases only one message, the File Selector's action message, will be used; this occurs when the "Insert" trigger is actually an IC_OK or similar response trigger.)

Handling a File Selector Selection shows a sample handler for the message sent by the File Selector. Handling a File Selector "OK" Button shows a sample handler for the message sent by the Reply Bar's Insert trigger. Both these handlers are written to go with the code in A Basic File Selector , and both handle the simplest case. Where application-specific code is required, comments have been inserted.

You should not, in general, have to handle any more than the File Selector's notification message. You may add your own gadgetry to add other functionality, however.

The File Selector's notification message is the message you set in GFSI_notificationMsg . This message should have the same definition as the prototype GEN_FILE_SELECTOR_NOTIFICATION_MSG , which is defined below. You can either define it with the same parameters, or you can define it exactly on the prototype. (The latter is recommended.) The prototype carries with the notification a word of GenFileSelectorEntryFlags ; this record contains the following flag fields:

GFSEF_TYPE
This is a two-bit field (at offset GFSEF_TYPE_OFFSET) that describes the type of the selection. This may be one of GFSET_FILE (selection is a file), GFSET_SUBDIR (selection is a subdirectory), or GFSET_VOLUME (selection is a volume or disk). These are values of GenFileSelectorEntryType .
GFSEF_OPEN
Set if the selection action was an "open" (i.e. double-click).
GFSEF_NO_ENTRIES
Set if no entries are in the list.
GFSEF_ERROR
Set if there was an error opening the selected entry on a double-click or a MSG_GEN_FILE_SELECTOR_OPEN_ENTRY .
GFSEF_TEMPLATE
Set if the file is a template (i.e. has GFHF_TEMPLATE set).
GFSEF_SHARED_MULTIPLE
Set if the file is shared with multiple writers (i.e. has GFHF_SHARED_MULTIPLE).
GFSEF_SHARED_SINGLE
Set if the file is shared and has a single writer (i.e. has GFHF_SHARED_SINGLE).
GFSEF_READ_ONLY
Set if the file is read-only (i.e. has FA_RDONLY).
GFSEF_PARENT_DIR
Set if the current selection is the parent directory (i.e. the first entry in the file selector's list).

GEN_FILE_SELECTOR_NOTIFICATION_MSG

void	GEN_FILE_SELECTOR_NOTIFICATION_MSG(
        word				entryNum,
        GenFileSelectorEntryFlags				entryFlags);

This prototype should be used for all notification messages sent out by the File Selector; that is, any message you set in GFSI_notificationMsg should be based on this prototype.

Source: The GenFileSelector object.

Destination: The object specified in GFSI_destination .

Parameters: entryNum The entry number selected in the list.

entryFlags
A GenFileSelectorEntryFlags record describing the selection. To get the entry type from the flags record, use the macro GFS_GET_ENTRY_TYPE, described after this reference entry.

Return: Nothing.

Structures: GenFileSelectorEntryFlags is a record with the following flags:

typedef WordFlags GenFileSelectorEntryFlags;
#define GFSEF_TYPE						0xc000
#define GFSEF_OPEN						0x2000
#define GFSEF_NO_ENTRIES 						0x1000
#define GFSEF_ERROR 						0x0800
#define GFSEF_TEMPLATE 						0x0400
#define GFSEF_SHARED_MULTIPLE 						0x0200
#define GFSEF_SHARED_SINGLE 						0x0100
#define GFSEF_READ_ONLY 						0x0080
#define GFSEF_PARENT_DIR 						0x0040
#define GFSEF_TYPE_OFFSET 					14

The GFSEF_TYPE field is two bits defined by one of the following GenFileSelectorEntryType constants. These two bits define the type of entry selected.

typedef ByteEnum GenFileSelectorEntryType;
#define GFSET_FILE				0
#define GFSET_SUBDIR				1
#define GFSET_VOLUME				2

Interception: The notification message must be intercepted and handled for the selection of a file to have any effect in your application. There is no default behavior, except in the document control objects (which create their own File Selectors).

GFS_GET_ENTRY_TYPE

byte	GFS_GET_ENTRY_TYPE(flags)
        word	flags;

This macro extracts the high two bits from a GenFileSelectorEntryFlags record delivered as a parameter of a File Selector's notification message. Compare the value to GFSET_FILE, GFSET_SUBDIR, and GFSET_VOLUME.

Code Display 14-2 Handling a File Selector Selection

/*
 * C handler for MSG_MY_APP_FILE_SELECTED. The message is sent by the File Selector
 * object (MyFileSel) to the application's Process object (MyProcessClass) when the
 * user clicks on an entry in the displayed file list. This code display is per the
 * setup in  A Basic File Selector.
 */
/*
 * Format of the message:
 * void MSG_MY_APP_FILE_SELECTED(word entryNum,
 *					GenFileSelectorEntryFlags entryFlags)
 */
@method	MyProcessClass, MSG_MY_APP_FILE_SELECTED {
    /* First check if this is an OPEN operation (double-click). If so,
     * then test whether the selection is a file. If so, simulate the "Insert"
     * trigger by sending the trigger's message to ourselves. If it is not a file,
     * or if the operation is not OPEN, we need do nothing. */
    if (GFS_GET_ENTRY_FLAGS(entryFlags) & GFSEF_OPEN) {    /* Is the operation
                              * a double-click? */
    if (GFS_GET_ENTRY_TYPE(entryFlags) & GFSET_FILE) {
                        /* Is the selection a file? */
        /* Execute application-specific code here. */
    }
    }
}
/* Note that we do not necessarily have to handle double-click operations in this
 * way. Since a double-click automatically activates the GenInteraction's default
 * element (typically an "Ok" trigger), we can simply handle the press of the
 * "Ok" trigger as shown in  Handling a File Selector "OK" Button. */

Code Display 14-3 Handling a File Selector "OK" Button

/* C handler for MSG_MY_APP_INSERT_TRIGGER_SELECTED, the message sent by the Insert
 * trigger to the application's Process object when the user clicks on it.
 * Format of this message: void MSG_MY_APP_INSERT_TRIGGER_SELECTED().
 * This code display is per the setup shown in  A Basic File Selector. */
@method	MyProcessClass, MSG_MY_APP_INSERT_TRIGGER_SELECTED {
    /* Declare a local dword of flags and entry number. */
    dword selFlags;
    /* First, retrieve the selection number and flags from the File Selector.
     * For now, we can ignore the selection name and path. To retrieve this
     * information, send the message MSG_GEN_FILE_SELECTOR_GET_SELECTION to the
     * File Selector. This message will return a dword (selFlags) that contains the
     * selection's entry number and a GenFileSelectorEntryFlags record. (To ignore
     * the selection name, pass a null pointer with the message.) */
    selFlags = @call MyFileSel::MSG_GEN_FILE_SELECTOR_GET_SELECTION(NULL);
    /* Next, determine whether the selection is a file. To do this, check the
     * returned GenFileSelectorEntryFlags record against GFSET_FILE. If the result
     * is true, the selection is a file. If false, it is a directory or volume. If
     * the selection is a file, we will operate on it appropriately (this is
     * specific to each application). If not, we will direct the File Selector to
     * open the entry. Note that we can assume an OPEN operation is in progress
     * because the only two ways to get to this point are through a double-click
     * on a selection and through a click on the "Insert" trigger.
     */
    /* Note the use of the two macros GFS_GET_ENTRY_FLAGS and GFS_GET_ENTRY_NUMBER.
     * The first extracts the GenFileSelectorEntryFlags record from the dword
     * selFlags, and the second extracts the entry number from selFlags. */
    if (GFS_GET_ENTRY_FLAGS(selFlags) == GFSET_FILE) {	/* the selection is a file */
    /* Execute application-specific code here. */
    } else {				/* the selection is a volume or directory */
    /* Now direct the File Selector to open the entry. Use the message
     * MSG_GEN_FILE_SELECTOR_OPEN_ENTRY, which returns a flag; if the flag is
     * set, then an error occurred. If the flag is clear, the operation
     * succeeded. */
    if (@call MyFileSel::MSG_GEN_FILE_SELECTOR_OPEN_ENTRY(
                    GFS_GET_ENTRY_NUMBER(selFlags))) {
        /* Provide a proper error message or beep sound. */
    }
}
| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Supporting the File Selector | Next: 2.4 Some Common Customizations


Some Common Customizations

The FileSelector Object: 2.4 File Selector Basics: Some Common Customizations

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Messages to Handle | Next: 3 File Selector Instance Data

Although the basics are all covered under the previous sections and you need nothing more to add a File Selector to your application, you will probably want to customize it somewhat. Typical simple customizations include limiting the types of files displayed, restricting the search to a single directory or volume, limiting the display to directories only (no files), and limiting the search to only those files with a given filename extension.

Each of these is shown in the displays following this section. Notice that the only changes you need to make to implement any of these customizations is to the File Selector's Goc code. You will not need to handle any other messages or add other code to your application. These customizations are shown in Display Only Directories through Show Only Files with a Given Filename Extension and show how to do the following (you may apply any combination):

  • Show directories only.
    The File Selector will display no files, only directories.
  • Show all files in a single directory.
    The File Selector will show all files but will restrict directory navigation; the user can only select files in the current directory.
  • Show hidden files as well as normal files.
  • Show all files created by a given geode.
    The File Selector will restrict displayed files to only those that were created by the geode with the given tokenChars and tokenID .
  • Show all files with a given filename extension.
    The File Selector will restrict the files displayed to only those with the given filename extension.

These are not the only customizations you can make. Although they will satisfy the needs of the large majority of applications, you can change the File Selector's searches any way you want (you can even set up a callback routine to filter every single file or directory). The remainder of this chapter explains in detail the different things you can do to the File Selector.

Code Display 14-4 Display Only Directories

@object GenFileSelectorClass DirectorySelector = {
    GFSI_destination = process;
    GFSI_notificationMsg = MSG_MY_APP_DIRECTORY_SELECTED;
    GFSI_fileCriteria = FSFC_DIRS;
};

Code Display 14-5 Limit the Search to a Single Directory

@object GenFileSelectorClass SingleDirFileSelector = {
    GFSI_destination = process;
    GFSI_notificationMsg = MSG_MY_APP_FILE_SELECTED;
    GFSI_attrs = 			FSA_HAS_FILE_LIST;
    GFSI_fileCriteria =			FSFC_NON_GEOS_FILES | FSFC_GEOS_EXECUTABLES
            | FSFC_GEOS_NON_EXECUTABLES;
    ATTR_GEN_PATH_DATA = {0, DIRECTORY};
        /* Limit the display to this standard directory.
         * Note that paths may be given; relative paths are taken as
         * relative to the specified standard path. This attribute
         * is defined in GenClass. */
};

Code Display 14-6 Show Hidden Files As Well As Normal Files

@object GenFileSelectorClass HiddenFileSelector = {
    GFSI_destination = process;
    GFSI_notificationMsg = MSG_MY_APP_FILE_SELECTED;
    /* Specify that FA_SYSTEM files are the only ones not to be displayed. */
    ATTR_GEN_FILE_SELECTOR_FILE_ATTR = { 0, FA_SYSTEM};
};

Code Display 14-7 Show Only Files Created by a Given Geode

@object GenFileSelectorClass CreatorFileSelector = {
    GFSI_destination = process;
    GFSI_notificationMsg = MSG_MY_APP_FILE_SELECTED;
    ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH = {{"CRTR"}, 0};
};

Code Display 14-8 Show Only Files with a Given Filename Extension

@object GenFileSelectorClass ExtensionFileSelector = {
    GFSI_destination = process;
    GFSI_notificationMsg = MSG_MY_APP_FILE_SELECTED;
    ATTR_GEN_FILE_SELECTOR_NAME_MASK = {"*.BAT"};				/* Show files ending with BAT. */
    /* Note that the mask is case sensitive. This means that the mask must be
     * in upper case to match DOS files. */
};
| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Messages to Handle | Next: 3 File Selector Instance Data


File Selector Instance Data

The FileSelector Object: 3 File Selector Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 File Selector Basics | Next: 4 File Selector Use

The File Selector is diverse and flexible, and you can customize it in many different ways. Typically, you will only need to add or modify a few lines of Goc code to achieve the results you need; however, if you have changing needs, you can change the File Selector during execution by sending it various messages.

The File Selector has a number of attributes and variable data fields that determine its functionality. These are listed in File Selector Attributes , along with comments and the defaults that are set.

Code Display 14-9 File Selector Attributes

/* Following are several type definitions used in the instance data. */
 typedef FileLongName			GenFileSelectorSelection;
typedef WordFlags			 FileSelectorFileCriteria;
typedef WordFlags			 FileSelectorAttrs;
typedef FileLongName			GenFileSelectorMask;
typedef struct {
    FileAttrs		GFSFA_match;
    FileAttrs		GFSFA_mismatch;
}  GenFileSelectorFileAttrs;
typedef struct {
    GeodeAttrs		GFSGA_match;
    GeodeAttrs		GFSGA_mismatch;
}  GenFileSelectorGeodeAttrs;
     /* GFSI_selection
     * The selection field contains the name of the current selection being
     * displayed by the File Selector. */
    @instance GenFileSelectorSelection					 GFSI_selection = {0};
    /* GFSI_fileCriteria
     * The file criteria field contains flags that determine the search and
     * filter criteria used by the File Selector when scanning directories. */
    @instance FileSelectorFileCriteria					 GFSI_fileCriteria =
                        FSFC_DIRS | FSFC_NON_GEOS_FILES |
                        FSFC_GEOS_EXECUTABLES |
                        FSFC_GEOS_NON_EXECTUTABLES;
    /* Possible flags for GFSI_fileCriteria:
     * FSFC_DIRS				FSFC_NON_GEOS_FILES
     * FSFC_GEOS_EXECUTABLES				FSFC_GEOS_NON_EXECUTABLES
     * FSFC_MASK_CASE_INSENSITIVE				FSFC_FILE_FILTER
     * FSFC_FILTER_IS_C				FSFC_TOKEN_NO_ID
     * FSFC_USE_MASK_FOR_DIRS */
    /* GFSI_attrs
     * The attributes field determines what features of the File Selector are
     * to be implemented. */
    @instance FileSelectorAttrs				 GFSI_attrs =		FSA_ALLOW_CHANGE_DIRS |
                        FSA_HAS_CLOSE_DIR_BUTTON |
                        FSA_HAS_OPEN_DIR_BUTTON |
                        FSA_HAS_DOCUMENT_BUTTON |
                        FSA_HAS_CHANGE_DIRECTORY_LIST |
                        FSA_HAS_CHANGE_DRIVE_LIST |
                        FSA_HAS_FILE_LIST;
    /* Possible flags for  GFSI_attrs:
     * FSA_ALLOW_CHANGE_DIRS				FSA_SHOW_FIXED_DISKS_ONLY
     * FSA_SHOW_FILES_DISABLED				FSA_HAS_CLOSE_DIR_BUTTON
     * FSA_HAS_OPEN_DIR_BUTTON				FSA_HAS_DOCUMENT_BUTTON
     * FSA_HAS_CHANGE_DIRECTORY_LIST				FSA_HAS_CHANGE_DRIVE_LIST
     * FSA_HAS_FILE_LIST				FSA_USE_VIRTUAL_ROOT
    /* GFSI_destination and GFSI_notificationMsg
     * When a user selects a file, the File Selector sends the message
     * specified in GFSI_notificationMsg to the object specified in
     * GFSI_destination. */
    @instance optr				 GFSI_destination;
    @instance Message				 GFSI_notificationMsg;
    /* Token and creator matches
     * These two vardata fields filter files according to their tokens.
     * A file that has a token other than that set will not be displayed
     * by the File Selector. If you only want to match files with certain
     * token characters, you can set FSFC_TOKEN_NO_ID in GFSI_fileCriteria. */
    @vardata GeodeToken				 ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH;
    @vardata GeodeToken				 ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH;
    /* File attributes
     * This vardata field filters files according to the files' native
     * attributes. If a file does not have the attributes specified in
     * GFSFA_match, or if it has an attribute specified in GFSFA_mismatch,
     * it will not be displayed. */
    @vardata GenFileSelectorFileAttrs					 ATTR_GEN_FILE_SELECTOR_FILE_ATTR;
    /* Geode attributes
     * This vardata field filters files according to the files' geode
     * attributes. If a file does not have the attributes specified in
     * GFSGA_match, or if it has an attribute specified in GFSGA_mismatch,
     * it will not be displayed. */
    @vardata GenFileSelectorGeodeAttrs					 ATTR_GEN_FILE_SELECTOR_GEODE_ATTR;
    /* Mask
     * This vardata field filters files based on their names. The mask is
     * a text string matched against file names. Files which coincide with the
     * mask string are displayed; others are not. The mask string will also
     * be applied to volumes and directories if FSFC_USE_MASK_FOR_DIRS is
     * set in GFSI_fileCriteria. */
    @vardata GenFileSelectorMask				 ATTR_GEN_FILE_SELECTOR_NAME_MASK;
    /* Virtual Root
     * This vardata field defines a "virtual root" for the file selector. The
     * user will not be allowed to navigate above the "virtual root" except
     * with the Change Drives popup list, if available. If the Change Drives
     * list is used, the virtual root is afterwards ignored. */
     @vardata GenFilePath				 ATTR_GEN_FILE_SELECTOR_VIRTUAL_ROOT;
    /* Scalable UI Support
     * This hint is used to support scalable UI within the File Selector based
     * on the application's features (MSG_GEN_APPLICATION_GET_APP_FEATURES).
     * This hint takes an array of GenFileSelectorScalableUIEntry structures.
     * Typically, one element will have GFSSUIC_SET_FEATURES_IF_APP_FEATURE_ON,
     * and one element will have GFSSUIC_SET_FEATURES_IF_APP_FEATURE_OFF.
     * The structures are defined below. */
    @vardata GenFileSelectorScalableUIEntry  HINT_FILE_SELECTOR_SCALABLE_UI_DATA;
    /* The structures for the above hint are given below.
     * GenFileSelectorScalableUIEntry is a structure with three fields; each
     * element in the array is one of these structures.
     * GenFileSelectorScalableUICommand is the type of the first field.
     * If the data in GFSSUIE_appFeature is true according to the type in
     * GFSSUIE_command, then the features in GFSSUIE_fsFeatures will be set
     * accordingly. */
    typedef ByteEnum GenFileSelectorScalableUICommand;
    #define GFSSUIC_SET_FEATURES_IF_APP_FEATURE_ON							0
    #define GFSSUIC_SET_FEATURES_IF_APP_FEATURE_OFF							1
    #define GFSSUIC_ADD_FEATURES_IF_APP_FEATURE_ON							2
    #define GFSSUIC_SET_FEATURES_IF_APP_LEVEL							3
    #define GFSSUIC_ADD_FEATURES_IF_APP_LEVEL							4
typedef struct {
    GenFileSelectorScalableUICommand					GFSSUIE_command;
    WordFlags					GFSSUIE_appFeature;
    FileSelectorAttrs					GFSSUIE_fsFeatures;
} GenFileSelectorScalableUIEntry;
    /* Number of files
     * This hint determines the number of files that are visible at once. */
     @vardata word				HINT_FILE_SELECTOR_NUMBER_OF_FILES_TO_SHOW;
    /* File List Width
     * This hint defines the width of the file list. The data is a number of
     * average-width characters, up to a maximum of 255.
    @vardata word 				 HINT_FILE_SELECTOR_FILE_LIST_WIDTH;

The GFSI_attrs Field

The GFSI_fileCriteria Field

Matching a File's Token

Matching a File's Creator App

Matching a File's Geode Attributes

Masking File Names

Matching a File's File Attributes

Searching Via Callback Routine

Resetting a Filter

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 File Selector Basics | Next: 4 File Selector Use


The GFSI_attrs Field

The FileSelector Object: 3.1 File Selector Instance Data: The GFSI_attrs Field

| Up: GEOS SDK TechDocs | Up | Prev: 3 File Selector Instance Data | Next: 3.2 The GFSI_fileCriteria Field
GFSI_attrs, MSG_GEN_FILE_SELECTOR_GET_ATTRS, MSG_GEN_FILE_SELECTOR_SET_ATTRS

The GenFileSelector has ten attributes in GFSI_attrs , shown below. They are stored as a word-sized record of type FileSelectorAttrs ; they may be retrieved with MSG_GEN_FILE_SELECTOR_GET_ATTRS and set with MSG_GEN_FILE_SELECTOR_SET_ATTRS .

FSA_ALLOW_CHANGE_DIRS
This flag allows the user to change directories by double clicking; it is set by default.
FSA_SHOW_FIXED_DISKS_ONLY
This flag does not allow removable volumes (e.g. floppy disks) to be shown in the File Selector volume list. By default, this flag is off.
FSA_SHOW_FILES_DISABLED
This flag instructs the File Selector to display files as disabled. This is useful for SaveAs operations, for example, when files must be displayed but should not be selectable by the user. By default, this flag is off. Setting this flag will turn all other search criteria off, so all filenames will be displayed.
FSA_HAS_CLOSE_DIR_BUTTON
This flag causes the File Selector to include a "Close Directory" button.
FSA_HAS_OPEN_DIR_BUTTON
This flag causes the File Selector to include an "Open Directory" button.
FSA_HAS_DOCUMENT_BUTTON
This flag causes the File Selector to include a "Go to Document" button that displays the DOCUMENT directory.
FSA_HAS_CHANGE_DIRECTORY_LIST
This flag causes the File Selector to include a list that allows the user to change directories.
FSA_HAS_CHANGE_DRIVE_LIST
This flag causes the File Selector to include a list that allows the user to change drives.
FSA_HAS_FILE_LIST
This flag causes the File Selector to include a file list.
FSA_USE_VIRTUAL_ROOT
This flag causes the File Selector to use the information in the ATTR_GEN_FILE_SELECTOR_VIRTUAL_ROOT attribute. This allows changing the File Selector without altering the vardata.

MSG_GEN_FILE_SELECTOR_SET_ATTRS

void	MSG_GEN_FILE_SELECTOR_SET_ATTRS(
        FileSelectorAttrs attributes);

This message sets the File Selector's GFSI_attrs record to a new set of attributes. The File Selector will rescan the current directory using the new attributes. If the File Selector is not visible when it receives this message, it will set the attributes but will not rescan the directory.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: attributes A record of FileSelectorAttrs to set into the File Selector's GFSI_attrs record.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_GET_ATTRS

FileSelectorAttrs MSG_GEN_FILE_SELECTOR_GET_ATTRS();

This message returns the current attributes set in the File Selector's GFSI_attrs instance data.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: None.

Return: A record of FileSelectorAttrs representing the flags set in the File Selector's GFSI_attrs field.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3 File Selector Instance Data | Next: 3.2 The GFSI_fileCriteria Field


The GFSI_fileCriteria Field

The FileSelector Object: 3.2 File Selector Instance Data: The GFSI_fileCriteria Field

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 The GFSI_attrs Field | Next: 3.3 Matching a File's Token
GFSI_fileCriteria, MSG_GEN_FILE_SELECTOR_GET_FILE_CRITERIA, MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

Each File Selector can limit or extend its searches in several different ways. The File Selector's instance data contains information about which types of files to include in the display, which not to include, and other special criteria. (Note that if the proper GFSI_attrs are set, directories and volumes will be unaffected by these scan limitations.)

The attribute field GFSI_fileCriteria is a record that indicates which of several search limiters should be considered in a directory scan. Note that some limitations may be invoked without a corresponding GFSI_fileCriteria entry. There are several possible entries in GFSI_fileCriteria , and any or all may be turned on at once. Each flag in the field is shown below, along with the field that it affects:

FSFC_DIRS
This flag indicates that subdirectories should be displayed in the File Selector's directory scans. This flag is set by default.
FSFC_NON_GEOS_FILES
This flag indicates that non-GEOS files should be displayed. This flag is set by default.
FSFC_GEOS_EXECUTABLES
This flag indicates that GEOS executable files should be displayed. This flag is set by default.
FSFC_GEOS_NON_EXECUTABLES
This flag indicates that GEOS non-executable files (documents and other types) should be displayed. This flag is set by default.
FSFC_MASK_CASE_INSENSITIVE
This flag indicates that the filename mask (if any) should be case-insensitive. The mask applies only to files unless FSFC_USE_MASK_FOR_DIRS is also set (see below); the mask is set in ATTR_GEN_FILE_SELECTOR_NAME_MASK .
FSFC_FILE_FILTER
This flag makes the File Selector use an application-defined callback routine when scanning. The callback routine is used after all other filters have been applied. The callback routine must be defined in a subclass of GenFileSelectorClass .
FSFC_FILTER_IS_C
This flag indicates that the callback filter routine is written in C and follows the Pascal calling convention.
FSFC_TOKEN_NO_ID
This flag indicates that when using a token to match files, the File Selector should ignore the token ID. In other words, it will only match the token characters.
FSFC_USE_MASK_FOR_DIRS
This flag indicates that if a filename mask is used, the File Selector should apply the mask to directories as well as to files. By default, the mask is applied only to file names.

You should set the proper GFSI_fileCriteria attributes in your application's Goc code. However, you can change the GFSI_fileCriteria field at run-time with MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA . You can also get the flags with MSG_GEN_FILE_SELECTOR_GET_FILE_CRITERIA . Both of these messages are detailed below.

MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

void	MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA(
        FileSelectorFileCriteria fileCriteria);

This message sets the File Selector's GFSI_fileCriteria record to a new set of flags. If the File Selector is visible when it receives this message, it will rescan the current directory with the new attributes. Otherwise, it will set the GFSI_fileCriteria record but will not rescan.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: fileCriteria A record of FileSelectorFileCriteria to set into the File Selector's GFSI_fileCriteria record.

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS , MSG_GEN_FILE_SELECTOR_SET_TOKEN , MSG_GEN_FILE_SELECTOR_SET_CREATOR , MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS , MSG_GEN_FILE_SELECTOR_SET_MASK

MSG_GEN_FILE_SELECTOR_GET_FILE_CRITERIA

FileSelectorFileCriteria MSG_GEN_FILE_SELECTOR_GET_FILE_CRITERIA();

This message returns the File Selector's current GFSI_fileCriteria record.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: None.

Return: A record of FileSelectorFileCriteria representing the File Selector's GFSI_fileCriteria flags.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS , MSG_GEN_FILE_SELECTOR_SET_TOKEN , MSG_GEN_FILE_SELECTOR_SET_CREATOR , MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS , MSG_GEN_FILE_SELECTOR_SET_MASK

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 The GFSI_attrs Field | Next: 3.3 Matching a File's Token


Matching a File's Token

The FileSelector Object: 3.3 File Selector Instance Data: Matching a File's Token

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 The GFSI_fileCriteria Field | Next: 3.4 Matching a File's Creator App
ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH, MSG_GEN_FILE_SELECTOR_SET_TOKEN, MSG_GEN_FILE_SELECTOR_GET_TOKEN

Since every GEOS file has a token associated with it, you can limit directory scans to include only those files with a given token. (Note that directories and volumes are not affected by this limitation.) Alternatively, because the token is made up of two items, the token chars and the token ID, you can arrange a search based on just the token characters (you can not search on only the ID).

Both the match token ID and the match token characters are stored in the File Selector's ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH instance field. This field is of type GeodeToken , the structure of which is shown below:

  typedef struct {
   TokenChars			GT_chars[TOKEN_CHARS_LENGTH];
   ManufacturerID			GT_manufID;
} GeodeToken;

The GT_chars field is four characters as defined in the geode's geode parameters ( .gp ) file. The GT_manufID field is the manufacturer ID number allotted to the particular developer who created the geode.

If the GFSI_fileCriteria attribute FSFC_TOKEN_NO_ID is set, only the token characters will be matched. The ID portion of the token will be ignored by the File Selector (until FSFC_TOKEN_NO_ID is turned off). See The GFSI_fileCriteria Field for more information on GFSI_fileCriteria .

If no token information is set, no filter will be applied based on tokens. To set the token at run-time, send MSG_GEN_FILE_SELECTOR_SET_TOKEN to the File Selector. To retrieve the token match information, send MSG_GEN_FILE_SELECTOR_GET_TOKEN .

MSG_GEN_FILE_SELECTOR_SET_TOKEN

void	MSG_GEN_FILE_SELECTOR_SET_TOKEN(
        dword		tokenChars,
        ManufacturerID		manufacturerID);

This message sets the ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH field to the two passed values. The token set with this message will be used to filter out certain files; directories and volumes are not affected by this filter.

If the File Selector is visible when it receives this message, it will rescan the current directory using the new token information. Otherwise, the File Selector will store the passed token.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: tokenChars The token characters of the token to match files against. Use the macro SET_TOKEN_CHARS (below) to set this dword value from the four individual characters.

manufacturerID
The token ID of the token to match files against.

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA , MSG_GEN_FILE_SELECTOR_SET_CREATOR

MSG_GEN_FILE_SELECTOR_GET_TOKEN

void	MSG_GEN_FILE_SELECTOR_GET_TOKEN(
        GetTokenCreatorParams *retValue);

This message returns the current token match information set in the File Selector's ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH field.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: retValue A pointer to a GetTokenCreatorParams structure, shown below. The structure may be empty when passed.

Return: The GetTokenCreatorParams structure pointed to by retValue will contain the token in ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH .

Structures: The GetTokenCreatorParams structure consists of two elements: a GeodeToken structure containing the File Selector's token characters and manufacturer ID fields, and a reserved word. Its structure is shown below:

typedef struct {
    GeodeToken   GTP_Token;
    word         GTP_Unused;						/* reserved */
} GetTokenCreatorParams;

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA , MSG_GEN_FILE_SELECTOR_GET_CREATOR

SET_TOKEN_CHARS

dword	SET_TOKEN_CHARS(ch1, ch2, ch3, ch4);
        char	ch1, ch2, ch3, ch4;

This macro creates a single dword containing the four given characters. Use this macro to create the tokenChars parameter for MSG_GEN_FILE_SELECTOR_SET_TOKEN and MSG_GEN_FILE_SELECTOR_SET_CREATOR .

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 The GFSI_fileCriteria Field | Next: 3.4 Matching a File's Creator App


Matching a File's Creator App

The FileSelector Object: 3.4 File Selector Instance Data: Matching a File's Creator App

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Matching a File's Token | Next: 3.5 Matching a File's Geode Attributes
ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH, MSG_GEN_FILE_SELECTOR_SET_CREATOR, MSG_GEN_FILE_SELECTOR_GET_CREATOR

Most applications will store their documents in a special type of file; each of these files will include the token of the creator geode. (This is a function of the Document Control objects.)

As a result, you may limit the File Selector's searches to only those files created by a given geode. Set the token to be matched in the File Selector's ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH variable data instance field. This search is employed in the same manner as a token search (above), so it is also subject to the FSFC_TOKEN_NO_ID attribute of GFSI_fileCriteria .

The ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH field is a GeodeToken structure that contains both the match ID and the match characters of the creator token. The GeodeToken structure is shown in the previous section. You can set the creator information in the File Selector's Goc code, or you can set it at run-time with MSG_GEN_FILE_SELECTOR_SET_CREATOR . To retrieve the creator token, use MSG_GEN_FILE_SELECTOR_GET_CREATOR .

MSG_GEN_FILE_SELECTOR_SET_CREATOR

void	MSG_GEN_FILE_SELECTOR_SET_CREATOR(
        dword		tokenChars,
        ManufacturerID		manufacturerID);

This message sets the ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH field to the two passed values. This field is a vardata instance record of type GeodeToken . The token set with this message will be used to filter out certain files; directories and volumes are not affected by this filter. Only files which have their creator token the same as that of the File Selector will be displayed.

If the File Selector is visible when it receives this message, it will rescan the directory using the new token as a filter. Otherwise, the File Selector will store the passed token for later use.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: tokenChars The token characters of the creator token to match files against. Use the macro SET_TOKEN_CHARS to set this dword value from the four individual characters. This macro is shown in the previous section.

manufacturerID
The token ID of the creator token to match files against.

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA , MSG_GEN_FILE_SELECTOR_SET_TOKEN

MSG_GEN_FILE_SELECTOR_GET_CREATOR

void	MSG_GEN_FILE_SELECTOR_GET_CREATOR(
        GetTokenCreatorParams *retValue);

This message returns the current creator match information set in the File Selector's ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH field.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: retValue A pointer to a GetTokenCreatorParams structure, shown below. The structure may be empty when passed.

Return: The GetTokenCreatorParams structure pointed to by retValue will contain the token in ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH .

Structures: The GetTokenCreatorParams structure consists of two elements: a GeodeToken structure containing the File Selector's token characters and manufacturer ID fields, and a reserved word. Its structure is shown below:

typedef struct {
     GeodeToken   GTP_token;
    word         GTP_unused;						/* reserved */
} GetTokenCreatorParams;

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA, MSG_GEN_FILE_SELECTOR_GET_TOKEN

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Matching a File's Token | Next: 3.5 Matching a File's Geode Attributes


Matching a File's Geode Attributes

The FileSelector Object: 3.5 File Selector Instance Data: Matching a File's Geode Attributes

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Matching a File's Creator App | Next: 3.6 Masking File Names
ATTR_GEN_FILE_SELECTOR_GEODE_ATTR, MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS, MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS

Every geode in GEOS has a structure of type GeodeAttrs stored in its file. As a result, you can limit the File Selector's searches to include only a certain set of geodes--those with the attributes set in the File Selector's ATTR_GEN_FILE_SELECTOR_GEODE_ATTR vardata instance field.

ATTR_GEN_FILE_SELECTOR_GEODE_ATTR is a structure of type GenFileSelectorGeodeAttrs , which consists of two parts. GFSGA_match represents the geode attributes an acceptable file must have on, and GFSGA_mismatch represents the geode attributes an acceptable file must have off. The structure's definition is shown below:

typedef struct {
     GeodeAttrs    GFSGA_match;
    GeodeAttrs    GFSGA_mismatch;
} GenFileSelectorGeodeAttrs;

To retrieve the current geode match information, send the message MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS to the File Selector object. To set them, send MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS . If you do not explicitly set the geode match information, the File Selector will not apply the geode match filter to its directory scans.

The possible geode attributes on which you can match or mismatch are shown below. They are all flags of the GeodeAttrs record, a system type that is not specific to GenFileSelectorClass

. Note that none of these flags is set by default. (Geode attributes are described in detail in the Applications and Geodes chapter.)

GA_PROCESS
This flag indicates the geode is a process (has an initial thread).
GA_LIBRARY
This flag indicates the geode is a library (exports routines).
GA_DRIVER
This flag indicates the geode is a driver (has a Driver Table).
GA_KEEP_FILE_OPEN
This flag indicates the .geo file must stay open (e.g. is a resource that is designated as discardable).
GA_SYSTEM
This flag indicates the geode is a GEOS system geode.
GA_MULTI_LAUNCHABLE
This flag indicates the geode may be loaded more than once.
GA_APPLICATION
This flag indicates the geode is a user-launchable application.
GA_DRIVER_INITIALIZED
Set on the fly by GEOS if the geode's driver aspect has been initialized (should not be matched).
GA_LIBRARY_INITIALIZED
Set on the fly by GEOS if the geode's library aspect has been initialized (should not be matched).
GA_GEODE_INITIALIZED
Set on the fly by GEOS if the geode's process aspect has been initialized (should not be matched).
GA_USES_COPROC
This flag indicates that the geode uses a coprocessor if one is available.
GA_REQUIRES_COPROC
This flag indicates the geode requires a coprocessor or coprocessor emulator.
GA_HAS_GENERAL_CONSUMER_MODE
This flag indicates the geode can be run in GCM mode.
GA_ENTRY_POINTS_IN_C
This flag indicates that the geode has library/driver entry points in C.

MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS

void	MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS(
        GeodeAttrs		matchGeodeAttrs,
        GeodeAttrs		mismatchGeodeAttrs);

This message sets the File Selector's vardata instance field ATTR_GEN_FILE_SELECTOR_GEODE_ATTR to the two passed values.

If the File Selector is visible when it receives this message, it will automatically rescan the current directory, applying the new geode filter. Otherwise, the new geode match/mismatch records will be stored but no rescan will occur.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: matchGeodeAttrs A GeodeAttrs record indicating the flags that every acceptable file must have set. This record will be set into GFSGA_match .

mismatchGeodeAttrs
A GeodeAttrs record indicating the flags that every acceptable file must have clear. This record will be set into GFSGA_mismatch .

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS

dword	 MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS();

This message returns the File Selector's current geode attribute match information.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: None.

Return: The returned dword consists of the two GeodeAttrs records stored in the File Selector's ATTR_GEN_FILE_SELECTOR_GEODE_ATTR vardata field. To extract the match attributes ( GFSGA_match ), use the macro GET_MATCH_ATTRS . To extract the mismatch attributes ( GFSGA_mismatch ), use GET_MISMATCH_ATTRS.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

GET_MATCH_ATTRS

word	GET_MATCH_ATTRS(attr);
        dword	attr;

This macro extracts the GFSGA_match record ( GeodeAttrs ) from the given dword (returned by MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS ).

GET_MISMATCH_ATTRS

word	GET_MISMATCH_ATTRS(attr);
        dword	attr;

This macro extracts the GFSGA_match record ( GeodeAttrs ) from the given dword (returned by MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS ).

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Matching a File's Creator App | Next: 3.6 Masking File Names


Masking File Names

The FileSelector Object: 3.6 File Selector Instance Data: Masking File Names

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Matching a File's Geode Attributes | Next: 3.7 Matching a File's File Attributes
ATTR_GEN_FILE_SELECTOR_NAME_MASK, MSG_GEN_FILE_SELECTOR_SET_MASK, MSG_GEN_FILE_SELECTOR_GET_MASK

By setting up a filename mask, you can limit the File Selector's displays to only those files that conform to the mask characters. For example, to show only those files with the .BAT extender, you could set the mask to *.BAT.

Only one mask may be in use at any given time. If you need to mask for two different sets of characters (e.g. *.BAT and *.EXE), you will need to subclass the File Selector and modify its behavior. (See Searching Via Callback Routine for more information.)

The mask is stored in the File Selector's variable data instance attribute ATTR_GEN_FILE_SELECTOR_NAME_MASK and consists of a null-terminated character string. This field is defined as type GenFileSelectorMask , the same as FileLongName . This string may contain the DOS " * " and " ? " wildcard characters. The filter applied by the File Selector is the same as that applied by the FileEnum() routine detailed in the File System chapter. In short, the mask is taken as a character string matched to the entire file name (thus, a mask of *.* implies all files with a period in their names; the period separating a filename and an extension will not count as a period).

Normally, the mask is applied only to files and not to directories or volumes. However, if the GFSI_fileCriteria attribute FSFC_USE_MASK_FOR_DIRS is set, directories will also be subject to the mask. (Volumes are never subject to the mask filter.)

By default, the mask filter is case-sensitive. You can make the mask filter be case-insensitive, however, by setting FSFC_MASK_CASE_INSENSITIVE in the File Selector's GFSI_fileCriteria instance field.

You can set the mask in your application's Goc code or by sending the message MSG_GEN_FILE_SELECTOR_SET_MASK to the File Selector. To retrieve the current mask, use MSG_GEN_FILE_SELECTOR_GET_MASK . If you do not explicitly set a mask string, the File Selector will not make any filename mask comparisons.

MSG_GEN_FILE_SELECTOR_SET_MASK

void	MSG_GEN_FILE_SELECTOR_SET_MASK(
        char	*mask);

This message sets the ATTR_GEN_FILE_SELECTOR_NAME_MASK variable instance field of the File Selector. This field contains a null-terminated character string (of type GenFileSelectorMask ); during a directory scan, the File Selector checks all filenames against this mask, and only those files that contain the mask characters are displayed. The mask string may contain DOS wildcard characters (" * " and " ? ").

The mask works for both DOS and GEOS files. However, unless the FSFC_USE_MASK_FOR_DIRS flag is set in the GFSI_fileCriteria attribute, directories will be unaffected by the mask. Volumes are unaffected in any case.

If the File Selector is visible on the screen when it receives this message, it will automatically rescan the current directory with the new mask. Otherwise, the new mask will be stored for later use.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: mask A pointer to the mask string to be set. This is a null-terminated character string.

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

MSG_GEN_FILE_SELECTOR_GET_MASK

void	MSG_GEN_FILE_SELECTOR_GET_MASK(
        char	*mask);

This message returns the mask string in the File Selector's ATTR_GEN_FILE_SELECTOR_NAME_MASK field.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: mask A pointer to an empty character string. The string must be at least sizeof(GenFileSelectorMask) characters.

Return: The character string pointed to by mask will contain the mask string set in ATTR_GEN_FILE_SELECTOR_NAME_MASK .

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_MASK, MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Matching a File's Geode Attributes | Next: 3.7 Matching a File's File Attributes


Matching a File's File Attributes

The FileSelector Object: 3.7 File Selector Instance Data: Matching a File's File Attributes

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Masking File Names | Next: 3.8 Searching Via Callback Routine
ATTR_GEN_FILE_SELECTOR_FILE_ATTR, MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS, MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS

All files have a number of attributes stored in a record of type FileAttrs . (For full information on FileAttrs , see the File System chapter.) To filter files based on the FileAttrs record, set your File Selector's variable data instance field ATTR_GEN_FILE_SELECTOR_FILE_ATTR to reflect the attributes that must be on and those that must be off.

ATTR_GEN_FILE_SELECTOR_FILE_ATTR is a structure of type GenFileSelectorFileAttrs , shown below. This structure contains two fields: GFSFA_match represents the attributes an acceptable file has on, and GFSFA_mismatch represents the attributes an acceptable file has off.

typedef struct {
     FileAttrs    GFSFA_match;
    FileAttrs    GFSFA_mismatch;
} GenFileSelectorFileAttrs;

The allowable file attributes are listed below and are discussed fully in the File System chapter. If you don't explicitly set the file attribute limitations, the File Selector will automatically filter out all files with either FA_SYSTEM or FA_HIDDEN.

FA_ARCHIVE
This flag indicates that the file requires a backup.
FA_SYSTEM
This flag indicates that the file used by DOS.
FA_HIDDEN
This flag indicates that the file not seen by normal searches.
FA_RDONLY
This flag indicates that the file is read-only.

Note: For directories not to be filtered out when FSFC_DIRS isn't set, you must mismatch FA_SYSTEM and FA_HIDDEN. That is, to show only those subdirectories which match your filter criteria, ensure FSFC_DIRS is not set, and set mismatch attributes thus:

ATTR_GEN_FILE_SELECTOR_FILE_ATTR = {
                0,
                FA_HIDDEN | FA_SYSTEM
}

You may retrieve the current file attributes by sending the message MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS to the File Selector object. You may set new file attribute limitations by sending it the message MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS .

MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS

void	MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS(
        FileAttrs setAttrs,
        FileAttrs clearAttrs);

This message sets the ATTR_GEN_FILE_SELECTOR_FILE_ATTR vardata instance field of the File Selector. If the File Selector is visible on the screen when it receives this message, it will rescan the directory immediately with the new attributes.

Source: Unrestricted.

Destination: Any GenFileSelector object

Parameters: setAttrs A FileAttrs record to be set into GFSFA_match . Only files with these flags set will pass the filter.

clearAttrs
A FileAttrs record to be set into GFSFA_mismatch . Only files with these flags cleared will pass the filter.

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS

word	MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS();

This message returns the ATTR_GEN_FILE_SELECTOR_FILE_ATTR of the File Selector. This attribute contains two byte-sized fields which represent the match and mismatch attributes (see above).

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: None.

Return: A word value: The high byte represents the mismatch attributes, and the low byte represents the match attributes. To extract these two fields from the returned value, use the macros GET_MATCH_FILE_ATTRS and GET_MISMATCH_FILE_ATTRS (below).

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

GET_MATCH_FILE_ATTRS

byte	GET_MATCH_FILE_ATTRS(attr);
        word	attr;

This macro extracts the GFSFA_match portion of the word value returned by MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS . The extracted value is a record of FileAttrs .

GET_MISMATCH_FILE_ATTRS

byte	GET_MISMATCH_FILE_ATTRS(attr);
        word	attr;

This macro extracts the GFSFA_mismatch portion of the word value returned by MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS . The extracted value is a record of FileAttrs .

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Masking File Names | Next: 3.8 Searching Via Callback Routine


Searching Via Callback Routine

The FileSelector Object: 3.8 File Selector Instance Data: Searching Via Callback Routine

| Up: GEOS SDK TechDocs | Up | Prev: 3.7 Matching a File's File Attributes | Next: 3.9 Resetting a Filter
MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE, GenFileSelectorFilterRoutine

If the built-in search limitations do not provide the functionality you require, you can set up a callback routine that the File Selector will call for each file or subdirectory in a directory scan. This routine will serve as the final filter for each file; if a file passes all other filters, it will be subjected to your routine. This procedure adds overhead to any scan, of course, so you should try to use the built-in File Selector functions whenever possible.

To set the File Selector to use the callback functionality, you must set FSFC_FILTER_FILE in the GFSI_fileCriteria field. If your callback routine is written in C, you must also set FSFC_FILTER_IS_C; the routine must also follow the Pascal calling convention.

In order to take advantage of the callback functionality, you must first subclass GenFileSelectorClass to handle the message MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE , which the File Selector will send to itself before beginning the directory scan. Your handler should return the address of the callback routine along with a pointer to an array of attributes that will be passed to it.

The callback routine can assume that if it is being called, the file has passed all other filters--the callback is therefore the last word on acceptance or rejection of the file. In essence, the callback is asked whether the file should be rejected: It should return the constant TRUE if the file is to be rejected but FALSE if it is not to be rejected.

As mentioned above, you can designate an array of extended file attributes that will be passed to the callback routine. This array contains a number of FileExtAttrDesc structures, each of which defines a certain attribute and some extra data for the attribute. This structure is shown below:

typedef struct {
    FileExtendedAttribute FEAD_attr;
                /* file attribute to
                 * get or set */
    void		* FEAD_value;
                /* pointer to a buffer
                 * or new value */
    word		FEAD_size;		/* size of buffer or
                 * new value */
    char		* FEAD_name;
                /* if custom attribute,
                 * pointer to name */
} FileExtAttrDesc;

In the above context, FEAD_value is meaningless and FEAD_size is the number of bytes required to hold the value of the attribute. Note that this array is not passed to the callback routine; instead, it specifies the structure and composition of the values that will be.

An example of filtering files using a callback routine is shown in Filtering Files Via a Callback Routine . The FileExtAttrDesc structure is defined and detailed in the File System chapter.

Code Display 14-10 Filtering Files Via a Callback Routine

/*******************************************************************************
 * This is a sample handler for MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE.
 * You should be able to use this code in your subclass. Note that the routine
 * name "FilterFileSelectorRoutine" is application-specific; it can be whatever
 * you want. It must be declared as static Boolean, however. See below.
 * PARAMETERS:
 * void (GenFileSelectorGetFilterRoutineResults *filter)
 *******************************************************************************/
@method FilterFileSelectorClass, MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE {
    /* We need to return a virtual pointer to the filter routine so the
     * file selector can lock and unlock the routine's code resource as
     * appropriate. Unfortunately, the compiler optimizes the simple
     * implementation (just assigning filter->filterRoutine the address of
     * the routine) by storing CS for the segment, which is unhelpful. So
     * we need a static variable holding the routine's address instead. */
    static GenFileSelectorFilterRoutine *const filterRoutine =
                    &FilterFileSelectorRoutine;
    /* Specify the address of the routine to call. It need not be locked into
     * memory, as the GenFileSelector will do that for us. */
    filter->filterRoutine = filterRoutine;
    /* Specify the additional attributes we need to look at in our filter
     * routine. As with the filterRoutine, these need not be in fixed or
     * locked memory. */
    filter->filterAttrs = &filterFileSelectorFilterAttrs;
}
/*******************************************************************************
 * This is a sample File Selector callback routine. It may be called whatever
 * you want and must be declared static Boolean.
 * This routine is called once per file or directory that passes all other File
 * Selector filters. This routine is limited to examining the attributes of the
 * file. It may not do anything that could cause the File Selector's instance chunk
 * to move in memory.
 * Return: TRUE to reject the file, FALSE to accept the file.
 *******************************************************************************/
static Boolean FilterFileSelectorRoutine(optr oself,
                FileEnumCallbackData *fecd, word frame)
/* The code of the routine is not included here; to see it, look at the FSFilter
 * sample application. */
    /* Following is the array of attributes examined by our filter routine.
     * This array contains an arbitrary number of elements, the last of which
     * has FEA_END_OF_LIST as its FEAD_attr field. FEAD_value is unused in this
     * context. FEAD_size is set to the total number of bytes needed for each
     * attribute.
     * Each structure's fields are FEAD_attr, FEAD_value, FEAD_size,
     * and FEAD_name */
const FileExtAttrDesc filterFileSelectorFilterAttrs[] = {
    { FEA_NAME, 0, sizeof(FileLongName), NULL },
    { FEA_FILE_ATTR, 0, sizeof(FileAttrs), NULL },
    { FEA_FILE_TYPE, 0, sizeof(GeosFileType), NULL },
    { FEA_END_OF_LIST, 0, 0, NULL }
};

MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE

void	MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE(
        GenFileSelectorGetFilterRoutineResults *filter);

This message returns the address of the callback routine used by the File Selector as well as the array of FileExtAttrDesc structures specifying the callback's parameters.

For the callback function to be used, you must set FSFC_FILE_FILTER in the File Selector's GFSI_fileCriteria instance data field. If your callback routine is in C, you must also set FSFC_FILTER_IS_C. In addition, the callback routine must follow the Pascal calling convention.

Source: Sent by the File Selector object to itself before calling FileEnum() . Also may be used by other objects to retrieve the callback information for the File Selector.

Destination: Any GenFileSelector object.

Parameters: filter A pointer to an empty return value structure. This structure is detailed below.

Return: The structure pointed to by filter must be filled by this method.

Structures: The single parameter is a pointer to an empty structure of type GenFileSelectorGetFilterRoutineResults . This structure has two parts: filterRoutine is a pointer to your callback routine, and filterAttrs is a pointer to an array of FileExtAttrDesc structures. You must fill in this structure in your handler so the File Selector knows how to call your callback routine. The structure is shown below:

typedef struct {
    GenFileSelectorFilterRoutine						*filterRoutine;
    const FileExtAttrDesc						*filterAttrs;
} GenFileSelectorGetFilterRoutineResults;

Interception: If a callback routine is to be used, you must subclass and handle this message to return the proper structures.

Tips: If your callback has no requirements other than the file name, you can pass NULL in the filterAttrs field of filter .

Warnings: The handler for this message must return a virtual segment for the callback routine in filter 's filterRoutine field. This can cause problems with most compilers; to avoid these problems, set a static variable in your method as shown in Filtering Files Via a Callback Routine , below (taken from the FSFilter sample application).

See Also: FileEnum()

GenFileSelectorFilterRoutine()

Boolean	GenFileSelectorFilterRoutine(
        optr			oself,
        FileEnumCallbackData			*fecd,
        word			frame);

This routine is defined by your application and is called for each file in a directory scan. It may be of any name of your choice as shown in Filtering Files Via a Callback Routine . It serves as the final filter; when called, it can assume the subject file has passed all other filters applied. This routine is set up and executed in the same manner as the callback routine for FileEnum() ; see the File System chapterfor complete details.

Parameters: oself The optr of the File Selector object

fecd
A pointer to a FileEnumCallbackData structure.
frame
An inherited stack frame passed by FileEnum() to each of its helper routines.

Return: Your callback must return the constant FALSE if the file should be accepted, TRUE if the file should be rejected.

Warnings: Your routine must not do anything that may cause the File Selector or its object block to move in memory (adding variable data fields, for instance).

See Also: FileEnum()

| Up: GEOS SDK TechDocs | Up | Prev: 3.7 Matching a File's File Attributes | Next: 3.9 Resetting a Filter


Resetting a Filter

The FileSelector Object: 3.9 File Selector Instance Data: Resetting a Filter

| Up: GEOS SDK TechDocs | Up | Prev: 3.8 Searching Via Callback Routine | Next: 4 File Selector Use

Once a filter is set (such as a filename mask or a file attribute match/mismatch filter), you may wish to delete the filter. To remove a filter designated by a flag in an instance field, simply clear the flag with the appropriate message.

If the filter is applied via a variable data field, however, you will have to use MSG_META_DELETE_VARDATA . This message is detailed in MetaClass and must be passed the name of the variable data field to be removed. Removal of the field equates to removal of the filter.

| Up: GEOS SDK TechDocs | Up | Prev: 3.8 Searching Via Callback Routine | Next: 4 File Selector Use


File Selector Use

The FileSelector Object: 4 File Selector Use

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 File Selector Instance Data | Next: 4.1 When a User Selects a File

Besides setting the File Selector's attributes and dynamically manipulating its instance data, you can change its display and current file lists using several messages. You can also retrieve its current state with other messages. These functions are detailed in the following sections.

When a User Selects a File

The Current Selection

Rescanning Directories

Setting Scalable UI Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 File Selector Instance Data | Next: 4.1 When a User Selects a File


When a User Selects a File

The FileSelector Object: 4.1 File Selector Use: When a User Selects a File

| Up: GEOS SDK TechDocs | Up | Prev: 4 File Selector Use | Next: 4.2 The Current Selection
GFSI_destination, GFSI_notificationMsg, MSG_GEN_FILE_SELECTOR_GET_ACTION, MSG_GEN_FILE_SELECTOR_SET_ACTION

When a user selects an entry in the File Selector's displayed list, either by double-clicking or by clicking on an "Open" button, the File Selector will notify a predetermined object or process that a selection has been made. In addition, if the selection is a volume or a directory, the File Selector will automatically (on a double-click) open the selection and show the new file list.

When defining your GenFileSelector, you must designate an output object and a message that will be sent to it when the user makes a selection. The message is stored in the GFSI_notificationMsg instance field, and the object's optr is stored in the GFSI_destination instance field. Note that instead of an optr, you may use a TravelOption as the output destination (TO_TARGET, TO_FOCUS, etc.); you can also use ATTR_GEN_DESTINATION_CLASS to specify the class of the output object to ensure the notification message is only delivered to a class that can handle it.

When the user makes a selection, the File Selector will send the notification message to the destination object along with flags indicating whether the selection was a single or double click. The message is defined by the class that will receive it; for example, the File Selector definition in A Basic File Selector designates MSG_MY_APP_FILE_SELECTED as the notification message that will be sent to the application's process object (the destination object).

You must set both the notification message and the destination object in your File Selector's Goc code. If you don't, no message will be sent, and your application will ignore user input to the File Selector. (The File Selector will still allow the user to navigate throughout the file system, and your application can query the File Selector as to its current selection, however.)

You may retrieve or set the File Selector's notification message and destination object at run-time by sending the following messages:

MSG_GEN_FILE_SELECTOR_GET_ACTION
Returns the File Selector's current notification message and destination object.
MSG_GEN_FILE_SELECTOR_SET_ACTION
Sets the File Selector's GFSI_destination and GFSI_notificationMsg fields to new values.

MSG_GEN_FILE_SELECTOR_GET_ACTION

void	MSG_GEN_FILE_SELECTOR_GET_ACTION(
        GetActionParams *retValue);

This message returns the File Selector's output object and notification message.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: retValue A pointer to an empty GetActionParams structure (detailed below), which will be filled with the return data.

Return: The GetActionParams structure pointed to by retValue will contain the output optr and notification message set for the File Selector.

Structures: The GetActionParams structure is shown below:

  typedef struct {
    Message  GAP_message; /* GFSI_notificationMsg */
    word     GAP_unused;  /* Internal field */
    optr     GAP_output;  /* GFSI_destination */
} GetActionParams;

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_SET_ACTION

void	MSG_GEN_FILE_SELECTOR_SET_ACTION(
        optr		actionOD,
        Message		actionMessage);

This message sets the File Selector's GFSI_notificationMsg field to actionMessage and the GFSI_destination field to actionOD .

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: actionOD The optr of the new output object.

actionMessage
The new output notification message.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4 File Selector Use | Next: 4.2 The Current Selection


The Current Selection

The FileSelector Object: 4.2 File Selector Use: The Current Selection

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 When a User Selects a File | Next: 4.3 Rescanning Directories
GFSI_selection

Most directory and volume navigation is handled internally by the File Selector, and the application has no need to know what is happening until a file is actually selected.

The File Selector maintains the name of the current selection in its GFSI_selection instance data field. The current full path, volume name, and disk are stored in variable data entries defined by GenClass . The application can easily, at any time, retrieve or set the current selection, path, or volume. Volume names, paths, and file names are all stored as null-terminated character strings in the GEOS character set.

Current path and directory are supported by GenClass and are not specific to GenFileSelectorClass .

The File Selector's current path is stored in a variable data field called ATTR_GEN_PATH_DATA . This field is a GenFilePath structure, defined below. This structure stores both an absolute path and the handle of the disk on which the path resides. For the GenFileSelector, this structure represents the currently-displayed directory.

  typedef struct {
    DiskHandle  GFP_disk;
    PathName    GFP_path;
} GenFilePath;

To retrieve the current path, send the File Selector a MSG_GEN_PATH_GET . To set the path, send it MSG_GEN_PATH_SET . To retrieve just the disk handle, send MSG_GEN_PATH_GET_DISK_HANDLE . Each of these messages is detailed fully in the GenClass chapter.

Traversing the File System

MSG_GEN_FILE_SELECTOR_UP_DIRECTORY, MSG_GEN_FILE_SELECTOR_OPEN_ENTRY

To set the currently-displayed directory, you can send one of the following messages to the File Selector object (these are all, of course, subject to the File Selector's attributes and applied filters):

MSG_GEN_FILE_SELECTOR_UP_DIRECTORY
Cause the File Selector to go up one directory. If already at the root, switch to volume selection. Does nothing if FSA_ALLOW_CHANGE_DIRS is not set in GFSI_attrs .
MSG_GEN_FILE_SELECTOR_OPEN_ENTRY
Cause the File Selector to attempt to open the passed entry in the current directory. Nothing will be done if the passed entry is a file; this message opens only directories and volumes. Use this with MSG_GEN_FILE_SELECTOR_GET_SELECTION when a user clicks on an "Open" button (or something similar) in the File Selector's dialog box.

MSG_GEN_FILE_SELECTOR_UP_DIRECTORY

void	MSG_GEN_FILE_SELECTOR_UP_DIRECTORY();

This message causes the File Selector to go up one directory in the directory tree. If the File Selector is already in the root directory, it will switch to the volume list (if FSA_ALLOW_CHANGE_VOLUMES is set in GFSI_attrs ). This message is only valid when the File Selector is visible on the screen.

Source: Unrestricted.

Destination: Any GenFileSelector object that is on the screen.

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_OPEN_ENTRY

Boolean	MSG_GEN_FILE_SELECTOR_OPEN_ENTRY(
        word	entryNumber);

This message causes the File Selector to open and display the contents of the entry specified. It returns an error flag: if the entry opens successfully, the error flag is returned false ; if some error occurs, the flag is returned true .

The entry specified with this message must be in the current file list. First, retrieve the entry number from the File Selector with the message MSG_GEN_FILE_SELECTOR_GET_SELECTION . If the entry can be opened (i.e. it is in the current file list and it is a volume or directory), the File Selector will open it, scan the directory, and display the new file list. If the entry is not a directory or volume, the File Selector will do nothing. This message is only valid when the File Selector is visible on the screen.

Source: Unrestricted.

Destination: Any GenFileSelector object that is on the screen.

Parameters: entryNumber The number of the entry to be opened.

Return: An error flag: true if error, false if no error.

Interception: Generally not intercepted.

The Current Selection

MSG_GEN_FILE_SELECTOR_SET_SELECTION, MSG_GEN_FILE_SELECTOR_SET_FULL_SELECTION_PATH, MSG_GEN_FILE_SELECTOR_GET_SELECTION, MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH

The File Selector keeps track of which item is currently designated as the selection . The selection appears on the screen as the only highlighted entry in the current list (the highlighted entry may be scrolled out of the view but remains the selection). Additionally, the selection is the file or folder on which any operations (such as open) will be executed.

The GFSI_selection field of the GenFileSelector's instance data is a null-terminated character string representing the name of the selection. The format of the selection string depends on the context: If the File Selector is displaying the volume list and thus the selection is a volume label, the string will consist of a drive name followed by a colon. (For non-removable disks, the colon will be followed by [ volume name ].) If the File Selector is displaying a directory and the selection is a file or directory, the selection string will consist of the name of the file or directory selected. The selection does not contain any information about the current volume or directory.

By sending the following messages, you can retrieve or set the current selection in your application. These messages affect ATTR_GEN_PATH_DATA, managed by GenClass for the File Selector, as well as the GFSI_selection instance field.

MSG_GEN_FILE_SELECTOR_SET_SELECTION
Attempt to set the selection to the given volume, file, or directory name. The given file or subdirectory must be in the currently-displayed list.
MSG_GEN_FILE_SELECTOR_SET_FULL_SELECTION_PATH
Attempt to set the path and selection to the given volume, file, or directory name. The passed selection must be in the directory at the end of the given path.
MSG_GEN_FILE_SELECTOR_GET_SELECTION
Gives a null-terminated character string representing the name of the current selection. This may be a volume, directory, or file name.
MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH
Gives the disk handle as well as a null-terminated character string representing the full path name of the current selection, excluding the drive name.

The selection retrieval messages return two word values in a single dword argument. These two values are a record of GenFileSelectorEntryFlags , flags that indicate the type of selection and operation underway, and an integer that indicates the place of the selection in the current file list. Two macros allow you to extract these values from the dword argument:

GFS_GET_ENTRY_NUMBER
Extracts the entry number from the given dword.
GFS_GET_ENTRY_FLAGS
Extracts the selection's flags record.

GFS_GET_ENTRY_NUMBER

word	GFS_GET_ENTRY_NUMBER(arg);
        dword	arg;

This takes the return value of MSG_GEN_FILE_SELECTOR_GET_SELECTION and MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH and returns the entry number of the selection.

GFS_GET_ENTRY_FLAGS

word	GFS_GET_ENTRY_FLAGS(arg);
        dword	arg;

This takes the return value of MSG_GEN_FILE_SELECTOR_GET_SELECTION and MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH and returns the GenFileSelectorEntryFlags for the selection.

MSG_GEN_FILE_SELECTOR_SET_SELECTION

Boolean	MSG_GEN_FILE_SELECTOR_SET_SELECTION(
        char	*selection);

This message causes the File Selector to attempt to select a given file, directory, or volume from the currently-displayed file list. It sets the selection by setting the File Selector's GFSI_selection field to the passed string.

If the File Selector is not on the screen or is suspended when it receives this message, it will set the selection and hold it until it scans the directory. The validity of the selection is not determined until the directory is again scanned. If the directory is not scanned immediately, the message's return value will be true . If the File Selector is on the screen and is not suspended, it will determine the validity of the selection immediately and return a value appropriate to the file's validity.

If the selection is valid, the user will see it highlighted in the File Selector's display. The selection will not be opened by this message; it is only selected.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: selection A pointer to a character string giving the name of the file to be selected. The selection string is case-sensitive; if selecting a DOS file, it must be all upper case.

Return: An error flag: false if the selection is successfully made. The error condition can occur if the file is not found or if the File Selector can not currently be updated (it is not visible or is suspended).

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_GET_SELECTION

dword	MSG_GEN_FILE_SELECTOR_GET_SELECTION(
        char	*selection);

This message returns the name, the entry number, and the entry flags of the file or directory currently selected.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: selection A pointer to an empty character string at least sizeof(FileLongName) characters long. If just the entry number and entry flags are desired, pass a null pointer.

Return: The high word of the dword return value is the number of the selection in the File Selector's current file list. The low word is a record of type GenFileSelectorEntryFlags . Use GFS_GET_ENTRY_NUMBER to extract the entry number; use GFS_GET_ENTRY_FLAGS to extract the flags record.

selection
The character string pointed to will be the null-terminated name of the current selection.

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_SET_FULL_SELECTION_PATH

Boolean	MSG_GEN_FILE_SELECTOR_SET_FULL_SELECTION_PATH(
        char		*selection,
        DiskHandle		diskHandle);

This message causes the File Selector to set its path data and its GFSI_selection field according to the string passed in selection . The string's format is described below. The entry may be in any directory on any volume; the volume is specified by the passed disk handle.

If the File Selector is suspended or not currently on the screen when it receives this message, it will set the path and selection but will do nothing else until it becomes unsuspended or visible. Otherwise, the message will be handled immediately, and the File Selector will navigate to and scan the proper directory, setting the selection if possible.

If the path is valid but the selection is not, the File Selector will display the proper file list and set the selection to the first entry in the list. If any part of the path other than the selection is invalid, the File Selector will show the volume list.

Source: Unrestricted.

Destination: Any GenFileSelector object that is on the screen.

Parameters: selection A pointer to a character string containing the full path to be set as the File Selector's current path and selection. The path may be absolute or relative--if relative, it is considered relative to the File Selector's current directory and diskHandle will be ignored.

diskHandle
The disk handle specifying the volume on which the new selection resides. If zero, the File Selector's currently-displayed disk will be used. If a relative path is specified in selection , this parameter will be ignored.

Return: This message returns an error flag: If the selection is successfully made, the flag will be false . If an error occurs, the flag will be true .

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH

dword	MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH(
        char	*selection);

This message returns the disk handle and flags as well as the full pathname of the current selection.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: selection A pointer to a character string at least PATH_BUFFER_SIZE characters long. Upon return, this string will contain the full selection path.

Return: The returned dword contains two word-sized fields: The high word represents the File Selector's current disk handle, and the low word is a GenFileSelectorEntryFlags record with the selection's flags. Use the macro GFS_GET_FULL_SELECTION_PATH_DISK_HANDLE to extract the disk handle, and use GFS_GET_ENTRY_FLAGS to extract the flags record. If sent when the File Selector is not visible on the screen, the message will return the disk handle and path of the last valid selection.

selection
The character string pointed to will contain the full path and name of the current selection.

Interception: Generally not intercepted.

GFS_GET_FULL_SELECTION_PATH_DISK_HANDLE

DiskHandle GFS_GET_FULL_SELECTION_PATH_DISK_HANDLE(arg);
        dword	arg;

This macro extracts the disk handle (the high word) from a dword argument returned by MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH .

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 When a User Selects a File | Next: 4.3 Rescanning Directories


Rescanning Directories

The FileSelector Object: 4.3 File Selector Use: Rescanning Directories

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 The Current Selection | Next: 4.4 Setting Scalable UI Data
MSG_GEN_FILE_SELECTOR_RESCAN, MSG_GEN_FILE_SELECTOR_SUSPEND, MSG_GEN_FILE_SELECTOR_END_SUSPEND

Each time the File Selector changes directories, it scans that directory and displays only the files it is supposed to. It looks into each file briefly to check the file against each criterion set in the File Selector. You can force the GenFileSelector to rescan the current directory with the message MSG_GEN_FILE_SELECTOR_RESCAN .

Occasionally you will want to scan the current directory only once after changing many parameters instead of rescanning after each change (search limitations, current volume, current selection, etc.). To optimize redrawing and rescanning, you can temporarily keep the GenFileSelector from scanning each time a MSG_GEN_FILE_SELECTOR_SET_... message is sent. To do this, send the following messages (both must be used, though not at the same time):

MSG_GEN_FILE_SELECTOR_SUSPEND
Notifies the File Selector that several operations in a row are coming and that it should suspend scanning the disk until the counterpart to this message (below) is received.
MSG_GEN_FILE_SELECTOR_END_SUSPEND
Indicates to the File Selector that directories may now be rescanned and displayed.

MSG_GEN_FILE_SELECTOR_RESCAN

void	MSG_GEN_FILE_SELECTOR_RESCAN();

This message causes the File Selector to rescan its current directory. The File Selector will re-build and re-display its entire file list. This message will only work when the File Selector is visible on the screen.

Source: Unrestricted.

Destination: Any GenFileSelector object that is on the screen.

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_SUSPEND

Boolean	MSG_GEN_FILE_SELECTOR_SUSPEND();

This message causes the File Selector to suspend directory scans until a MSG_GEN_FILE_SELECTOR_END_SUSPEND is received. This provides the ability to avoid unnecessary directory rescans when several different attributes are being changed in succession (e.g. volume, mask, and file types).

No user action is affected by this message; only the following attribute-setting messages will be affected (their normal automatic rescan will not happen). Note that all messages have the prefix MSG_GEN_FILE_SELECTOR_ :

SET_SELECTION SET_FULL_SELECTION_PATH
SET_MASK SET_FILE_ATTRS
SET_TOKEN SET_CREATOR
SET_GEODE_ATTRS SET_ATTRS
SET_FILE_CRITERIA

In addition to the above messages, the GenClass message MSG_GEN_PATH_SET will also be suspended.

Source: Unrestricted.

Destination: Any non-suspended GenFileSelector object.

Parameters: None.

Return: An error flag: true if the File Selector is already suspended, false if the suspension is successful.

Interception: Generally not intercepted.

Warnings: After suspending a File Selector with this message, be sure to unsuspend it with MSG_GEN_FILE_SELECTOR_END_SUSPEND .

MSG_GEN_FILE_SELECTOR_END_SUSPEND

Boolean	MSG_GEN_FILE_SELECTOR_END_SUSPEND();

This message unsuspends a File Selector that had previously been suspended with MSG_GEN_FILE_SELECTOR_SUSPEND . This message automatically causes a directory rescan. Because multiple suspension attempts are disallowed, this message only needs to be sent once to unsuspend the File Selector.

Source: Unrestricted.

Destination: Any suspended GenFileSelector object.

Parameters: None.

Return: An error flag: true if the File Selector was not previously suspended, false if the File Selector is successfully unsuspended.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 The Current Selection | Next: 4.4 Setting Scalable UI Data


Setting Scalable UI Data

The FileSelector Object: 4.4 File Selector Use: Setting Scalable UI Data

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Rescanning Directories
HINT_FILE_SELECTOR_SCALABLE_UI_DATA

The File Selector lets you set which features you want based on the current application features, using HINT_FILE_SELECTOR_SCALABLE_UI_DATA . This hint will query the application object to get the current application features set, and it will set the File Selector's feature set ( GFSI_attrs ) accordingly.

The hint takes an array of GenFileSelectorScalableUIEntry structures, each of which has three fields:

GFSSUIE_command
This field is a GenFileSelectorScalableUICommand , which is an enumerated type describing the effect of this particular entry. The five commands are detailed below.
GFSSUIE_appFeature
This word represents the application's feature set or user level. The hint, when processed, will query the GenApplication object (the GAI_appFeatures value) and check those features against the features in this field. If they match, the hint will cause the features in GFSSUIE_fsFeatures to be set or cleared as appropriate.
GFSSUIE_fsFeatures
This field represents the File Selector attributes that should be turned on or off depending on the command and the application features (see both fields above). The commands are defined below.

Following are the GenFileSelectorScalableUICommand s. Each queries the GFSSUIE_appFeature field and then acts appropriately.

GFSSUIC_SET_FEATURES_IF_APP_FEATURE_ON
This command will set the features in GFSSUIE_fsFeatures if the feature(s) in GFSSUIE_appFeature are turned on.
GFSSUIC_SET_FEATURES_IF_APP_FEATURE_OFF
This command will set the features in GFSSUIE_fsFeatures if the feature(s) in GFSSUIE_appFeature are off.
GFSSUIC_ADD_FEATURES_IF_APP_FEATURE_ON
This command will turn on the additional features in GFSSUIE_fsFeatures if the features in GFSSUIE_appFeature are turned on.
GFSSUIC_SET_FEATURES_IF_APP_LEVEL
This command will set the features in GFSSUIE_fsFeatures if the application is at the user level specified in GFSSUIE_appFeature .
GFSSUIC_ADD_FEATURES_IF_APP_LEVEL
This command will add the additional features in GFSSUIE_fsFeatures if the application is at the user level specified in GFSSUIE_appFeature .
| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Rescanning Directories


Graphics

GEOS SDK Documentation: Graphics

| Up: GEOS SDK TechDocs

The following documents describe API for graphics and drawing functions.

GenGlyphClass
Perhaps the easiest way to draw to the screen is to use a GenGlyph object. It can draw a text label easily, or a graphic with a bit more effort.
Nokia 9000i Communicator UI
The Nokia 9000i Communicator UI has specialized bitmaps it uses to let the user know about the machine's current mode, etc. The best way to use these bitmaps is to use the standard Nokia 9000i Communicator UI object classes.
Hello World
The Hello World sample application uses kernel routines to draw angled text to a window.
UI Overview
Includes discussion of TicTac sample application, an example of visible objects drawing custom graphics in a window; it also describes many provided UI classes which draw themselves.
Graphics Environment
Manipulating graphics states, the GEOS coordinate space, and otherwise affecting the drawing environment.
Drawing Shapes
Kernel drawing routines to draw shapes, text, lines, curves; also to change colors, fonts, etc.
Printing
Drawing on paper is similar to drawing on the screen.
| Up: GEOS SDK TechDocs


Graphics Environment

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Graphics Road Map
| One-page Version of this Chapter for Easy Printing

The GEOS graphics system provides many powerful tools to enhance your geode's appearance and graphical capabilities.

The kernel automatically displays much of what appears on the screen. Generic and Visual objects display themselves, so if these objects can display everything your geode needs, you can probably skip this section for now. On the other hand, geodes with any kind of specialized display needs will need imaging code, as will any geodes that print.

This, the first chapter of the Imaging Section, explains the thinking behind the graphics system and the work you'll have to get done ahead of time to set up the space you're going to be drawing to. For actual drawing commands, see the Drawing Shapes chapter.

Before reading this chapter, you should be familiar with the basics of the generic UI and messaging. You may also want to review high school geometry.

1 Graphics Road Map
1.1 Chapter Structure
1.2 Vocabulary
2 Graphics Goals
3 Graphics Architecture
4 How To Use Graphics
5 Coordinate Space
5.1 Standard Coordinate Space
5.2 Coordinate Transformations
5.3 Precise Coordinates
5.4 Device Coordinates
5.5 Larger Document Spaces
5.6 Current Position
6 Graphics State
6.1 GState Contents
6.2 Working with GStates
7 Working With Bitmaps
8 Graphics Strings
8.1 Storage and Loading
8.2 Special Drawing Commands
8.3 Declaring a GString Statically
8.4 Creating GStrings Dynamically
8.5 Drawing and Scanning
8.6 Editing GStrings Dynamically
8.7 Parsing GStrings
9 Graphics Paths
10 Working With Video Drivers
10.1 Kernel Routines
10.2 Direct Calls to the Driver
11 Windowing and Clipping
11.1 Palettes
11.2 Clipping
11.3 Signalling Updates

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Graphics Road Map
| One-page Version of this Chapter for Easy Printing


Graphics Road Map

Graphics Environment: 1 Graphics Road Map

| Up: GEOS SDK TechDocs | Up | Down | Prev: Graphics Environment | Next: 2 Graphics Goals

Graphics is a big topic. It has many applications and thus many ways to apply it. If you will be writing graphics-intensive programs, you'll probably end up reading everything in these chapters. Chances are, however, that you'll only need to learn a few things about the graphics system.

This section is for those people who would like to skip around the GEOS imaging system. It contains an outline of the chapter as well as a list of definitions for terms that will be used throughout.

Chapter Structure

Vocabulary

| Up: GEOS SDK TechDocs | Up | Down | Prev: Graphics Environment | Next: 2 Graphics Goals


Chapter Structure

Graphics Environment: 1.1 Graphics Road Map: Chapter Structure

| Up: GEOS SDK TechDocs | Up | Prev: 1 Graphics Road Map | Next: 1.2 Vocabulary

This chapter is divided into sections. Which sections you'll want to read depend on what you're interested in.

1 Road Map
You're reading the road map now.
2 Goals
This section goes into the design philosophy behind the GEOS graphics system.
3 Architecture
The Graphics Architecture section describes the basics behind how the graphics system works.
4 How to Use Graphics
This section is for programmers new to GEOS who know what sort of graphics they want their geode to have but don't know how to get it. This section discusses the different contexts in which graphics appear and how you can work within each of those contexts. After going through the section, you may wish to return to this road map to find out where to read more about whatever sort of graphics you'll be working with.
5 Coordinate Space
The GEOS graphics system describes locations and distances using a rectangular grid coordinate system. This section explains how to work with and manipulate this space.
6 Graphics State
This section describes the GState, a data structure used in many contexts and for many purposes throughout the graphics system.
7 Bitmaps
It is possible to specify an image by using a data structure in which an array of color values is used to represent a rectangular area of the screen. This section describes how such a data structure can be manipulated.
8 Graphics Strings and Metafiles
This section describes how to create and draw Graphics Strings, also known as GStrings. (See the Vocabulary section, below, for a simple definition of GString.)
9 Graphics Paths
The graphics system allows you to specify an arbitrary area of the display which can be used in powerful graphics operations. This section explains how to set up a path, a data structure describing the outline of the area you want to specify.
10 Video Drivers
Most programmers can safely skip this section, but video game writers might want to read it. This section includes some advanced techniques for getting faster drawing rates.
11 Windowing and Clipping
This section explains some of the work the graphics system does to maintain windows in a Graphical User Interface. Included is an in-depth look at clipping, along with some commands by which you can change the way clipping works.
| Up: GEOS SDK TechDocs | Up | Prev: 1 Graphics Road Map | Next: 1.2 Vocabulary


Vocabulary

Graphics Environment: 1.2 Graphics Road Map: Vocabulary

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Chapter Structure | Next: 2 Graphics Goals

Several terms will crop up again and again in your dealings with graphics. Many of these terms will be familiar to programmers experienced with other graphics systems, but those not familiar with any of these terms are encouraged to find out what they mean.

GState, Graphics State
The graphics system maintains data structures called Graphics States, or GStates for short. GStates keep track of how a geode wants to draw things: the colors to use, current text font, scaling information, and so on. Most graphics routines, including all routines that actually draw anything, take a GState as one of their arguments. Thus to draw the outline of a red rectangle, you would first call the GrSetLineColor() routine to change the color used for drawing lines and outlines to red. Only after thus changing the GState would you call the GrDrawRect() command.
Standard Coordinate Space, Document Coordinate Space
Drawing commands in GEOS use the standard coordinate space, also known as the document coordinate space. This is a rectangular grid used for expressing locations and distances. This grid uses units of 1/72nd of an inch. Thus, drawing a point to (72, 72) will draw the point one inch to the right and one inch below the top left corner of an ordinary Content or document.
GString, Graphics String, Metafile
GString is short for Graphics String. A GString is a data structure representing a sequence of graphics commands. Graphics Strings are used many places in the system. GStrings are used to pass graphics through the clipboard. They serve as the descriptors of graphical monikers, which are in turn used as application icons. The printing system uses GStrings to describe images to be printed. A graphics Metafile is just a file containing a GString, taking advantage of this data structure to store a graphics image to disk.
Bitmap
A bitmap is a picture defined as a rectangular array of color values. Note that since display devices normally have their display area defined as a rectangular grid for which each pixel can have a different color value, it is relatively easy to draw a bitmap to the screen, and in fact the graphics system normally works by constructing a bitmap of what is to be displayed and displaying it. Bitmaps are sometimes known as "raster images."
Path
Paths provide a precise way to specify arbitrary areas of the display area. Like GStrings, they are described by a sequence of drawing commands. Instead of defining a picture, these commands describe a route across the graphics space. The enclosed area can be used in a variety of operations.
Region
Regions provide another approach to describing arbitrary display areas. Regions use a compressed scan-line data structure to represent a pixel-based shape on the display. Though lacking the mathematical precision of paths, region-based operations are very fast and thus ideally suited to certain simple tasks.
Palette, RGB Values
Many display devices can display a wide variety of colors. Of these, most cannot display all their colors at once. Typical configurations can show 16 or 256 colors at a time out of a possible 256K. A palette is one of these subsets of the set of possible colors. When drawing something in color, normally the color to draw with is specified by the palette index, that color's place in the table.
The value stored in each entry is an RGB value, a color described in terms of its red, green, and blue components. Each of these three values ranges between 0 and 255. Geodes can change the RGB values associated with palette entries and thus change the available colors.
Video Driver
These geodes stand between the graphics system and the actual display device. They maintain device independence and do a lot of the "behind the scenes" work of the graphics system.
Windowing, clipping, "marked invalid"
The windowing and graphics systems are heavily intertwined. The graphics system controls how windows should be drawn, while the windows system keeps track of which parts of various displays are visible. For the most part graphics programmers don't have to worry too much about the windowing system, but there are some terms worth knowing. Clipping is the process of keeping track of what things drawn to a window are actually visible. If a geode draws something beyond the edge of a window, the system can't just ignore the drawing, as the user might later reveal the drawing by scrolling onto that area. The graphics system "clips" the graphic, being sure to show only those parts that are in the visible part of a window. Further clipping takes place when a window is obscured behind another window. Any drawing on the lower window must not show up until the upper window is moved. The "clipping region" is that area of a window which is visible--anything drawn to the window outside the region will be clipped (see the figure below). Programs can reduce the clipping areas of any of their associated windows.
The windowing system is able to work much more quickly by assuming that not too many things are going to be changing at once. Normally it can assume that a given area of the screen will look like it did a short while ago. When it's time to change an area of the screen, that area is said to be "marked invalid," since whatever is presently being shown there is no longer valid. The normal example is that if a pull-down menu has been obscuring part of a document, when the menu goes away, the part of the document that becomes visible must be redrawn.
MSG_VIS_DRAW, MSG_META_EXPOSED
These messages are sent to visible objects to instruct them to redraw themselves; if you are using graphics to display a visible object then you are probably intercepting MSG_VIS_DRAW. MSG_META_EXPOSED lets an object know that it has been marked invalid; either it has to be drawn for the first time, or part or all of it has been exposed (hence the name). The UI controller sends this message to the top object in the Vis hierarchy. MSG_VIS_DRAW, on the other hand, specifically instructs an object to redraw itself. Generally, a Vis object will respond to MSG_META_EXPOSED by sending MSG_VIS_DRAW to itself; it responds to MSG_VIS_DRAW by issuing the appropriate drawing commands, then sending MSG_VIS_DRAW to each of its children.
Graphic Objects
Graphic Objects provide the user with an interface for working with graphics in a manner similar to GeoDraw's. They are useful for programs which allow the users to construct some sort of graphical document or provide a sort of graphical overlay for a spreadsheet.
| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Chapter Structure | Next: 2 Graphics Goals


Graphics Goals

Graphics Environment: 2 Graphics Goals

| Up: GEOS SDK TechDocs | Up | Prev: 1 Graphics Road Map | Next: 3 Graphics Architecture

The graphics system is in charge of displaying everything in GEOS. Thus, it is vital that the GEOS graphics system be both powerful and easy to use. Features such as outline font support and WYSIWYG printing, usually afterthoughts on other operating systems, have been incorporated into the GEOS kernel. The graphics system was designed to be state of the art and thus had to achieve several goals:

  • Fast Operation
    The GEOS graphics system is heavily optimized for common operations on low-end PCs. To allow a windowing system to run on an 8088 machine, it is vital that line drawing, clipping, and other common graphical operations run very quickly. They do.
  • Device Independence
    Applications are sheltered from the hardware. Coordinate system units are device independent so that all of your drawing commands use real-world measurements. These units are then translated into device coordinates by the kernel.
  • Complete Set of Drawing Primitives
    The graphics system must be able to draw a wide variety of shapes and objects to meet the needs of the UI and applications. It does.
  • Built In Support for Outline Fonts
    GEOS includes outline font technology supporting a variety of font formats. Outline fonts are text typefaces which are defined by their outline shape rather than by a bitmap representation. Outline fonts can be scaled with no apparent loss of smoothness. GEOS also supports bitmap-based fonts; however the very nature of these fonts makes them non-WYSIWYG.
  • Single Imaging Model for Screen and Hardcopy
    The graphics system uses the same high level graphics language for screen imaging as for printing. Because the GEOS system creates both screen and printed images through the same language, the screen can display a document just as it will be printed. Except for differences in resolution, what you see is what you get.
| Up: GEOS SDK TechDocs | Up | Prev: 1 Graphics Road Map | Next: 3 Graphics Architecture


Graphics Architecture

Graphics Environment: 3 Graphics Architecture

| Up: GEOS SDK TechDocs | Up | Prev: 2 Graphics Goals | Next: 4 How To Use Graphics

The graphics system involves many pieces of GEOS working together to turn an application's UI and graphics commands into drawings on a display.

The graphics portion of the kernel automatically makes any requested transformations to the coordinate space dealing with scaling, rotation, etc. It transforms complicated drawing commands into simpler ones supported by the video drivers.

The video driver can render these simplified commands directly on the device. The video driver checks for collisions, making sure that if whatever is being drawn coincides with the mouse pointer, the mouse pointer will be drawn on top. The video driver does clipping so a geode doesn't draw outside its window.

Commands and information flow in this manner from application to the Video Device, though information also flows in the opposite direction, so that the kernel can get information about the display device's resolution, size, and capabilities.

If the geode is going to draw text, the graphics system makes calls to the font manager. If the requested font isn't already in memory, the font manager loads it. The font manager then makes calls to the individual font driver for information about specific characters.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Graphics Goals | Next: 4 How To Use Graphics


How To Use Graphics

Graphics Environment: 4 How To Use Graphics

| Up: GEOS SDK TechDocs | Up | Prev: 3 Graphics Architecture | Next: 5 Coordinate Space

When looking at the source code of sample applications, it's usually not too hard to pick out the commands that do the actual drawing. Commands with names like GrDraw...() generally are self-explanatory. It's not so easy to pick out the commands that set up an area in which the drawing will take place. Part of the problem is that there are many ways to display graphics; each is well suited for different tasks. This section of the chapter provides some practical knowledge about the various ways to display graphics and which situations are appropriate for each.

When possible, the best way to learn how to perform a graphics action is to look at code which performs a similar action. The sample program presented in the Hello World chapter shows a simple graphics environment sufficient for many geodes. If you only need to change what is being displayed (as opposed to how it is displayed), you can work straight from the example, drawing different shapes using commands found in the Drawing Shapes chapter. Most basic graphics techniques are used in one sample program or another. By combining and adapting code from the sample programs, you can take care of most simple graphics needs.

If you can't find a sample geode to work from, there are several points to consider when deciding what sort of graphics environment to set up.

  • Sometimes the only graphics commands in a geode will be those used to define that geode's program icon. This is a common enough case that instructions for setting up your geode's icon are in the program topics section, .
  • Will existing generic UI gadgetry be sufficient for everything you want to display? If you're writing a utility, it might be. If you're writing an arcade game, it probably won't be.
  • If your geode will be displaying graphics, will the user ever interact directly with the graphics? A graphing program might draw a graph based on data the user types in. Such a program could draw the graph but might not actually allow the user to interact with the graph. On the other hand, an art program will probably expect the user to interact with the graphics directly.

Once you've figured out just what your geode's graphical needs are, you're ready to find out which pieces of graphics machinery are right for you.

For custom graphics that will appear in a view, the content object of the GenView must be prepared to issue graphics commands. A common tactic is to create a subclass of VisContentClass and let an object of this subclass act as the content for a view. The subclass would very likely have a specialized MSG_VIS_DRAW . The Process object is another popular choice for the view's output descriptor. In this case, the process must be prepared to intercept any messages the view is likely to send, with MSG_META_EXPOSED and MSG_VIS_DRAW of the most interest. Whichever object, process or content, is the content of a view can respond to MSG_META_EXPOSED or MSG_VIS_DRAW by calling kernel graphics routines. For more information on how to use these objects, see the Views chapter.

As you learn more advanced graphics concepts you may discover shortcuts. As you get deeper into graphics, you should keep a cardinal rule in mind. Any time the graphics space is obscured and then exposed, the geode must be able to draw correctly, no matter what changes have been made. If your geode only draws on a MSG_VIS_DRAW , it will automatically follow this rule. However, applications using shortcuts must take MSG_VIS_DRAW into account; it may be sent at any time, and what it draws may wipe out what was there before. An arcade game that moves a spaceship by blitting a bitmap may be fast; however, be sure that the spaceship will be drawn to the right place if the game's window is obscured and then exposed. Don't worry if this sounds confusing now, but keep these words in mind as you read on.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Graphics Architecture | Next: 5 Coordinate Space


Coordinate Space

Graphics Environment: 5 Coordinate Space

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 How To Use Graphics | Next: 6 Graphics State

The graphics system uses a rectangular coordinate grid to specify the size and position at which drawing commands will be carried out. This is a logical choice as most display devices use a rectangular grid of pixels. "Smart" devices with built in graphics routines tend to use coordinate grids to set place, size, and sometimes movement. "Dumb" devices, capable only of displaying bitmaps, always have these bitmaps described in terms of pixels on a square grid. The GEOS graphics system expects the geode to use the provided device-independent grid to describe graphics commands. The graphics system will then convert this information to device coordinates: coordinates set up for the specific display device, using a grid of pixels.

Standard Coordinate Space

Coordinate Transformations

Precise Coordinates

Device Coordinates

Larger Document Spaces

Current Position

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 How To Use Graphics | Next: 6 Graphics State


Standard Coordinate Space

Graphics Environment: 5.1 Coordinate Space: Standard Coordinate Space

| Up: GEOS SDK TechDocs | Up | Prev: 5 Coordinate Space | Next: 5.2 Coordinate Transformations

The standard coordinate space is what the application uses to describe where things are to be drawn. It is device independent, based on real world units: typographer's points, about 1/72nd of an inch. The origin of the coordinate system is normally the upper left corner of the document, but this can change with context and with changes made by the kernel or even your geode. The coordinate space normally extends from -16384 to 16383 horizontally and vertically. These constants may be referenced as MIN_COORD and MAX_COORD. Note that if you're going to be printing your document, you should restrict yourself to coordinates between -4096 to +4096 to account for scaling to draw on high resolution printers. It is possible to define a 32-bit coordinate space, which gives more room but costs speed and memory. For details about 32-bit spaces, see Larger Document Spaces .

Whenever you draw something, you must specify the coordinates where that thing will be drawn. The coordinates you pass specify where in the coordinate plane that thing will be drawn; the plane, in turn, may be translated, scaled, and/or rotated from the standard window coordinate system (see Coordinate Transformations ).

The system also maintains a device coordinate system, with a device pixel defined as the unit of the grid. This is the type of coordinate system most programmers are used to, but it is certainly not device independent. The graphics system will do all the worrying about device coordinates so your program doesn't have to. (Note, however, that GrDrawImage() , GrDrawHugeImage() , and GrBrushPolyline() are more device-dependant than most routines; see the Drawing Shapes chapter, bitmap section and polyline section for information on these routines).

Standard GEOS coordinates depart from the device model, taking an approach closer to a pure mathematical Cartesian plane. Programmers used to working with device-based coordinates are especially encouraged to read Device Coordinates to learn about some of the differences.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Coordinate Space | Next: 5.2 Coordinate Transformations


Coordinate Transformations

Graphics Environment: 5.2 Coordinate Space: Coordinate Transformations

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Standard Coordinate Space | Next: 5.3 Precise Coordinates

GEOS provides routines which "transform" the coordinate space. These commands can shift, magnify, or rotate the coordinate grid, or perform several of these changes at once. These transformations affect structures in the GState, and new transformations can be combined with or replace a GState's current transformation.

These transformations apply to the coordinate space, not to individual objects. As a result, if you apply a 200% scaling factor to a drawing not centered on the origin, not only will its size change, but its position will change as well. If you want these operations to affect an object but not its position, you should translate the coordinates so that the origin is at the desired center of scaling or rotation, apply the scaling or rotation, draw the object at the translated origin, then change the coordinates back.

Since they are stored in the GState, these transformations endure--they do not go away after you've drawn your next object. If you apply a 90 degree rotation, you will continue drawing rotated to 90 degrees until you either rotate to some other angle or use another Graphics State. Transformations are also cumulative. If you rotate your space 30°, then translate it up an inch, the result will be a rotated, translated coordinate space. If you want to nullify your last transformation, apply the opposite transformation.

When applying a new transformation to a space which has already been transformed, the old transformations will affect the new one. Be careful, therefore, of the order of your transformations when combining a translation with any other kind of transformation. If you make your transformations in the wrong order, you may not get what you expected.

Simple Transformations

GrApplyRotation(), GrApplyScale(), GrApplyTranslation(), GrSetDefaultTransform(), GrSetNullTransform(), GrInitDefaultTransform(), GrSaveTransform(), GrRestoreTransform()

If you find yourself using transformations at all, they will probably all be rotations, scalings, and translations. The GEOS graphics system includes commands to apply these kinds of transformations to your coordinate system, taking the form GrApplyTransformation() . These commands work with a transformation data structure associated with the Graphics State, so everything drawn in that Graphics State will be suitably transformed.

GrApplyRotation() rotates the coordinate space, turning it counterclockwise around the origin. All objects drawn after the rotation will appear as if someone had turned their drawing surface to a new angle. With a 90° rotation, a shape centered at (1, 1) would draw as if centered at (1, -1). Anything drawn centered at the origin would not change position but would be drawn with the new orientation.

GrApplyScale() resizes the coordinate space; this is useful for zooming in and out. After applying a scale to double the size of everything in the x and y directions, everything will be drawn twice as big, centered twice as far away from the origin. Applying a negative scale causes objects to be drawn with the scale suggested by the magnitude of the scaling factor but "flipped over" to the other side of the coordinate axes.

GrApplyTranslation() causes the coordinate system to be shifted over. After a translation, everything will be drawn at a new position, with no change in orientation or size.

To undo the effects of a transformation, you can apply the opposite transformation: rotate the other way, translate in the opposite direction, or scale with the inverse factor.

To undo the effects of all prior transformations, return to the default transformation matrix using the GrSetDefaultTransform() command. The routine GrSetNullTransform() sets the Graphics State transformation to the null transform--nullifying not only your transformations, but any the system may have imposed as well. For the most part, you should avoid using the GrSetNullTransform() command and use the GrSetDefaultTransform() instead. You can change the default transformation matrix using GrInitDefaultTransform() , but this is generally a bad idea since the windowing system works with the default transformation, and if a geode begins making capricious changes, this can produce strange images.

There are "push" and "pop" operations for transformations. To keep a record of the GState's current transformation, call GrSaveTransform() . To restore the saved transformation, call GrRestoreTransform() .

Complicated Transformations

GrApplyTransform(), GrSetTransform(), GrGetTransform(), GrTransformByMatrix(), GrUntransformByMatrix()

You may want to make some change to the coordinate system that has no simple correspondence to scaling, rotation, or translation. Perhaps you know some linear algebra and want to use your knowledge to combine several transformation functions into a single transformation (thus improving execution speed). All transformations on the coordinate system are expressed in the form of transformation matrices. A GEOS graphics system transformation consists of a matrix containing 6 variables and 3 constants (see figure, below). The six variables allow for standard coordinate transformations. The constants (0, 0, and 1 respectively) allow these transformation matrices to be composed. For example, multiplying a scaling matrix with a rotation matrix creates a matrix which represents a combined scaling and rotation. The six variable matrix elements are stored in a TransMatrix structure.

The GEOS system uses one matrix to store the Graphics State transformation and one to store the Window transformation. When told to apply a new transformation, the graphics system constructs a matrix to represent the requested transformation change and multiplies this matrix by the old transformation matrix. To combine these matrices, GEOS multiplies them together to get the cross-product

If you know that there's a particular combination of transformations you're going to be using a lot, you can do some math in advance to compute your own transformation matrix, then apply the raw matrix as a transformation using GrApplyTransform() . To replace the GState's current transformation matrix with the matrix of your choice, use GrSetTransform() . To find out the current transformation, call GrGetTransform() .

You could combine these steps to pre-compute a transformation that combined a translation and a scaling:

  1. Call GrApplyTranslation() to apply a translation to the GState's coordinate space.
  2. Call GrApplyScale() to apply the scale factor to the GState's coordinate space. The space is now both translated and scaled.
  3. Call GrGetTransform() to get the current GState transformation (stored in a TransMatrix structure).
  4. To apply this transformation to another GState, use the GrApplyTransform() command.

GrTransformByMatrix() returns the results of transforming a passed coordinate pair using an arbitrary transformation matrix. GrUntransformByMatrix() performs the reverse operation, returning the coordinates which would have transformed to the passed coordinates.

Sometimes you have to be careful about the order in which these transformations are supposed to occur. When multiplying transformation matrices together, the transformation that is applied later is the first in the multiplication pair. You can combine any number of rotations and scalings together without having to worry about order: the resulting matrices will be the same. When combining a translation with any other kind of operation, it makes a difference what order you make the transformations and thus makes a difference based on what order you multiply the matrices.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Standard Coordinate Space | Next: 5.3 Precise Coordinates


Precise Coordinates

Graphics Environment: 5.3 Coordinate Space: Precise Coordinates

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Coordinate Transformations | Next: 5.4 Device Coordinates

As has been previously stated, coordinates are normally given in typographer's points. Most graphics commands accept coordinates accurate to the nearest point. This should be more than sufficient for most geodes, but for those specialized programs, more precise drawing is possible.

For simple cases, it is possible to create precise drawings by scaling the drawings. To make drawings accurate to one fourth of a point, for example, scale by 25% and multiply all coordinates accordingly. However, this approach is limited and may result in confusing code.

Another way to make precise drawings is to use the graphics commands which have been specially set up to take more precise coordinates. These commands will not be described in detail here, but keep them in mind when planning ultra-high resolution applications. GrRelMoveTo() , GrDrawRelLineTo() , GrDrawRelCurveTo() , and GrDrawRelArc3PointTo() take WWFixed coordinates, and are thus accurate to a fraction of a point. To draw a precise outline, use these commands to draw the components of the outline. To fill an area, use the precise drawing commands to describe the path forming the outline of the area, then fill the path.

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Coordinate Transformations | Next: 5.4 Device Coordinates


Device Coordinates

Graphics Environment: 5.4 Coordinate Space: Device Coordinates

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Precise Coordinates | Next: 5.5 Larger Document Spaces

Most programmers can work quite well within the document space regardless of how coordinates will correspond to device coordinates. However, some programmers might need to know about the device coordinates as well. The system provides clever algorithms for going from document to device space for all programmers, as well as routines to get device coordinate information from the device driver.

What the System Draws on the Device

Consider a device whose pixels are exactly 1/72nd of an inch, such that no scaling is required to map document units to device units. The relationship of the coordinate systems is illustrated below. Note that a pixel falls between each pair of document units. This is a further demonstration of the concept that document coordinates specify a location in the document coordinate space, not a pixel.

Next consider a device that has a resolution of 108 dpi, which is 1.5 times greater than our default 72 dpi. That is, there are 1.5x1.5 pixels on the device for each square document unit. The basic problem here is that the coordinates that are specified in document space map to non-integer pixels in device space. The graphics system would like the pixels to be half-filled along two edges of the rectangle (see the figure below). Unfortunately, a pixel must be either filled or empty, so the system needs a set of rules to follow in this situation. These rules are

  • If the midpoint of a pixel (i.e., the device coordinate of that pixel) falls inside the area's boundary, that pixel is filled.
  • Conversely, if the midpoint of a pixel falls outside the area's border, the pixel is not filled.
  • If the midpoint of the pixel falls exactly on the border of the area to be filled, the following rule is used:
    Pixels on the left or the top are not filled;
    Pixels on the right or the bottom are filled;
    Pixels in the left-bottom and top-right corners are not filled.

These rules might seem a little odd: Why not just fill all the pixels that would be touched by the area? One of the problems with this approach is that areas that did not overlap in the document space would overlap on the device. Or more specifically, they would overlap only on some devices (depending on the resolution), which is even worse. The rules have the property that adjoining areas in document space will not overlap in any device space.

Our next set of potential problems comes with lines. Lines can be very thin and thus might be invisible on some low-resolution devices. If the graphics system used the rules for filled objects then some thin lines would be only partially drawn on low resolution devices. GEOS uses Bresenham's algorithm for drawing straight thin lines, ensuring that a continuous group of pixels will be turned on for a line (see the figure below). This continuity is insured due to the behavior of the algorithm:

  • If the line is more horizontal then vertical, exactly one pixel will be turned on in each column between the two endpoints.
  • If the line is more vertical than horizontal, exactly one pixel will be turned on in each row.
  • If the line is exactly 45 degrees, exactly one pixel will be turned on in each column and row.

Since ellipses and Bézier curves are drawn as polylines, Bresenham's algorithm will work with them.

Converting Between Doc and Device Coordinates

GrTransform(), GrUntransform(), GrTransformWWFixed(), GrUntransformWWFixed()

Given a coordinate pair, at times it's convenient to know the corresponding device coordinates. Sometimes the reverse is true. Use these functions to convert a coordinate pair to device coordinates or vice versa. GrTransform() takes a coordinate pair and returns device coordinates. GrUntransform() does the reverse. If you want to be able to get a more exact value for these coordinates you can use GrTransformWWFixed() and GrUntransformWWFixed() . These return fixed point values so you can do other math on them before rounding off to get a whole number that the graphics system can use.

To transform points through an arbitrary transformation instead of to device coordinates, use the GrTransformByMatrix() or GrUntransformByMatrix() routines, described previously.

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Precise Coordinates | Next: 5.5 Larger Document Spaces


Larger Document Spaces

Graphics Environment: 5.5 Coordinate Space: Larger Document Spaces

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Device Coordinates | Next: 5.6 Current Position
GrApplyTranslationDWord(), GrTransformDWord(), GrUntransformDWord(), GrTransformDWFixed(), GrUntransformDWFixed()

Some applications may need a graphics space larger than the 19 foot square of the standard coordinate space (10 foot square for printed documents). Some spreadsheets can take up a lot of room, as can wide banners. Geodes that will need a large drawing space can request a 32-bit graphics space. Most applications will be able to get by with standard 16-bit coordinate spaces, and their authors may safely skip this section. Note, however, that the Graphic and Spreadsheet objects use 32-bit graphics spaces for display.

If a geode will use a 32-bit space in a view, it will need a VisContent object with the VCNA_LARGE_DOCUMENT_MODEL attribute.

For the most part, 32-bit documents use the same graphics commands as regular 16-bit documents. No single drawing command can cover more than a standard document area; each has been optimized to draw quickly in 16 bits. To draw to the outlying reaches of your document space, apply a special translation that takes 32-bit coordinates, then use normal drawing routines in your new location.

A model for working with 32-bit graphics spaces commonly used with spreadsheets is to break the graphics space into sections. To draw anything, the geode first uses a translation to get to the proper section, then makes the appropriate graphics calls to draw within that section.

If you wish to display this 32-bit coordinate graphics space to the screen, you'll probably want to do so in a 32-bit content.

The standard graphics commands can only draw to one 16 bit space at a time. You will need to translate the coordinate space to choose which 16-bit portion of the 32-bit space you want to draw to. To do so you need to use some special translation functions. GrApplyTranslationDWord() corresponds to the GrApplyTranslation() function normally used.You can use this routine to make the jumps necessary to access far away portions of the graphics space. Since a coordinate can now be in a much larger area than before, all routines that deal with a point's position have 32-bit equivalents.

GrTransformDWord() and GrUntransformDWord() take the place of GrTransform() and GrUntransform() . GrTransformDWFixed() and GrUntransformDWFixed() take the place of GrTransformWWFixed() and GrUntransformWWFixed() , with two words in the integer part of the number instead of one.

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Device Coordinates | Next: 5.6 Current Position


Current Position

Graphics Environment: 5.6 Coordinate Space: Current Position

| Up: GEOS SDK TechDocs | Up | Prev: 5.5 Larger Document Spaces | Next: 6 Graphics State
GrMoveTo(), GrMoveToWWFixed(), GrRelMoveTo(), GrGetCurPos(), GrGetCurPosWWFixed()

The graphics system supports the notion of a current position, sometimes called a pen position. Note that the pen position metaphor predates "pen computing"; please don't confuse these concepts. The concept behind the pen position is that all graphics commands are executed by a pen that ends up at the last place drawn. So if the last command was to draw a curve ending at (20, 20), the pen will still be there. There are special drawing commands that work with the pen position, so you could then draw a line from the current position to (30, 30). The line would extend from (20, 20) to (30, 30), and the pen would then be at (30, 30).

Calling GrMoveTo() moves the pen position to coordinates of your choice. GrMoveToWWFixed() moves the pen position to coordinates which may be specified with a great deal of precision. GrRelMoveTo() moves the pen to a location relative to its present position. GrRelMoveTo() takes coordinates accurate to a fraction of a point, allowing for very precise placement. Call GrGetCurPos() to get the current position, GrGetCurPosWWFixed() to do so with greater accuracy.

There are some guidelines to follow when figuring out where your pen position is going to end up after a draw operation. If you call the GrDrawLineTo() procedure, the pen will end up at the point drawn to. If you call any draw/fill procedure that takes two points as arguments, the pen position usually ends up at the second point passed; if it does not, the reference entry will note where the pen position ends. When text draws at the current position, the current position is used to place the left side of the text. When done drawing the text, the current position has moved to the right side of the text.

| Up: GEOS SDK TechDocs | Up | Prev: 5.5 Larger Document Spaces | Next: 6 Graphics State


Graphics State

Graphics Environment: 6 Graphics State

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 Coordinate Space | Next: 7 Working With Bitmaps

The data structure known as the Graphics State, or GState, keeps track of changes your code makes about how it wants to draw things. If your geode changes its transformation, everything you draw from that point will be drawn transformed until you change the transformation again. If your geode sets the line width to two points, all lines to come will be drawn two points wide until the width is set to something else. Many graphics routines ask that you pass a GState along as one of the parameters so they know where and how to draw what you've requested.

GState Contents

Working with GStates

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 Coordinate Space | Next: 7 Working With Bitmaps


GState Contents

Graphics Environment: 6.1 Graphics State: GState Contents

| Up: GEOS SDK TechDocs | Up | Prev: 6 Graphics State | Next: 6.2 Working with GStates

Sometimes it's convenient to think of the GState as being analogous to the Properties boxes in GeoDraw. The GState keeps track of how the program wants to draw lines, text, and filled areas, just as the Line Properties, Text Properties, and Area Properties boxes keep track of how the GeoDraw user wants to draw these objects.

While handy, this analogy doesn't do the GState justice. The GState keeps track of many things:

  • Mix Mode
    The graphics system allows for different mix modes (sometimes known as copy modes). These drawing modes permit the geode to draw in such ways that it erases instead of drawing, draws using the inverse of whatever it's drawing on, or uses various other modes.
  • Current Position (also known as Pen Position)
    The graphics system keeps track of the position of the last thing drawn. If your geode wants to draw something else at this location, there are drawing commands that will draw at the current position.
  • Area Attributes
    The GState contains information that the graphics system will use when filling areas. This information includes the color and fill pattern to use.
  • Line attributes
    The graphics system uses information stored in the Graphics State to keep track of what color and pattern to use when drawing lines. The GState also keeps track of whether lines should be drawn as dotted, and if so what sort of dot-dash pattern to use. The GState contains the line width. It also contains line join information, which controls how lines will be drawn when they meet at an angle, as at the vertices of a polygon.
  • Text Attributes
    The Graphics State contains the ID of the current font, the font size, and the text style. It also contains a color and pattern to use, information about the font, and some esoteric text-drawing options.
  • Coordinate Space Transformations
    The GState keeps track of the current coordinate space transformation. If there is an associated window, the window's transformation matrix is maintained separately with the window's information.
  • Associated Window
    The GState knows the handle of the window associated with the GState. This is the window that will determine where drawings appear and how they are clipped.
  • Associated GString
    If the application is building a GString, the GState is aware of it. The GState contains a reference to the GString. When the graphics system turns graphics commands into GString elements, these elements will be appended to the referenced GString.
  • Associated Path
    If the application is building a path, the GState contains a pointer to the path along which new path elements are passed, similar to the way GString elements are passed. The GState also keeps track of whether the current path is to be combined with another path or should be used "as is."
  • Clipping Information
    The GState keeps track of clipping information in addition to that maintained by the window.

The function of many of these parts may be fairly intuitive to someone used to working with graphics programs. Some of the others may require additional explanation, especially when it comes to how to work with them.

| Up: GEOS SDK TechDocs | Up | Prev: 6 Graphics State | Next: 6.2 Working with GStates


Working with GStates

Graphics Environment: 6.2 Graphics State: Working with GStates

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 GState Contents | Next: 7 Working With Bitmaps
GrCreateState(), GrDestroyState(), GrSaveState(), GrRestoreState()

As has been mentioned, most graphics routines require that a GState handle be provided as an argument. Beginning programmers are often unclear on just where to get the GState to use.

Many drawing routines are called by MSG_VIS_DRAW . This message provides a GState, and all routines in the handlers for this message should use the provided GState. Creating a new GState under these circumstances is unnecessary and wasteful. However, sometimes you will need to create a GState. GrCreateState() creates a GState with the default characteristics. You must specify the window with which the GState will be associated.

Commands which change drawing attributes or the current position change the GState.

GrDestroyState() is used to get rid of a GState, freeing the memory to be used by other things. If GStates are created but not destroyed, eventually they will take too much memory. Normally, for each call to GrCreateState() there is a corresponding GrDestroyState() . MSG_VIS_DRAW handlers don't need to destroy the passed GState. Graphics states are cached so that GrCreateState() and GrDestroyState() don't normally need to call MemAlloc() or MemFree() . When GStates are freed, their space is added to the cache. When the memory manager needs to find space on the heap, it flushes the cache.

A geode is most likely to call GrCreateState() when about to draw a piece of geode-defined UI. Other than that, you'll probably be using GStates provided to you by the system. You might want to create a GState if you wanted to calculate something (perhaps the length, in inches, of a text string) when you had no appropriate GState.

GrSaveState() provides a sort of "push" operation that works with GStates. When you call certain functions, like GrSetAreaColor() , new values will wipe out the values of the old GState. But if you've previously called GrSaveState() , then any time you call GrRestoreState() on your saved state, it will come back and displace the current state. Your application can save a GState to save a commonly used clipping region, which could then be restored by restoring the state instead of repeating all the operations needed to duplicate the region. GrSaveTransform() and GrRestoreTransform() are optimizations of GrSaveState() and GrRestoreState() , but they only preserve the GState's transformation.

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 GState Contents | Next: 7 Working With Bitmaps


Working With Bitmaps

Graphics Environment: 7 Working With Bitmaps

| Up: GEOS SDK TechDocs | Up | Prev: 6 Graphics State | Next: 8 Graphics Strings
GrCreateBitmap(), GrDestroyBitmap(), GrEditBitmap() GrGetPoint(), GrSetBitmapMode(), GrGetBitmapMode(), GrSetBitmapRes(), GrGetBitmapRes(), GrClearBitmap(), GrGetBitmapSize(), GrCompactBitmap(), GrUncompactBitmap()

Bitmaps are useful for describing complicated pictures that don't have to be smooth at all resolutions. For example, coming up with all the lines and shapes necessary to describe a complicated photograph would be time-consuming and a waste of memory. It's much easier to set up a rectangular array of cells and to set a color value for each cell. Bitmaps are often used for defining program icons. GEOS includes a great deal of bitmap support. It has kernel routines to create, modify, and draw bitmaps.

There are three main ways to create a bitmap for an application to use. One often used is to embed the data of a desired bitmap directly into a graphics string. This is the way normally used for defining system icons. The other common way is to call the kernel graphics routine GrCreateBitmap() . Another way to create a bitmap, not used so often, is to manipulate memory directly: The formats used for describing bitmaps are public, and though it would be easier in most cases to work through GrCreateBitmap() , those with specialized needs might want to create their bitmap data structures from scratch.

The GrCreateBitmap() routine creates an offscreen bitmap and returns a Graphics State which can be drawn to; changes to this Graphics State become changes to the offscreen bitmap. For example, calling GrDrawLine() and passing the Graphics State provided with such a bitmap would result in a bitmap depicting a line. To display this bitmap, call the GrFillBitmap(), GrDrawHugeBitmap(), GrDrawImage(), or GrDrawHugeImage() commands in another graphics space (see the Drawing Shapes chapter).

When creating a bitmap, you must make choices about what sort of bitmap you want. Depending on your choices, GEOS will be able to use a variety of optimizations. For instance, it takes much less room to store a monochrome bitmap than a color bitmap the same size.

Whenever creating a bitmap, you must specify its dimensions and what sort of coloring it will use (monochrome, four bit, eight bit, or 24 bit).

You may store a mask with your bitmap. This mask works something like the mask used when drawing a view's mouse pointer. When the bitmap is drawn, blank areas of the bitmap will be drawn as black for those pixels where the mask is turned on. For pixels where the mask is turned off, whatever was underneath the bitmap will be allowed to show through.

By asking for a Complex bitmap, you can specify even more information. Complex bitmaps may include their own palette and may specify their own horizontal and vertical resolution. They are very useful for working with bitmaps that may have been captured on other systems or for working with display devices.

The GrDestroyBitmap() routine destroys some or all of the information associated with a bitmap. You may use this function to free all memory taken up by the bitmap. You may also use this function to free only the GState associated with a bitmap by GrCreateBitmap() , but leave the bitmap's data alone; the BMDestroy argument will determine exactly what is destroyed. This usage comes in handy for those times when a bitmap will not be changing, but will be drawn. Large bitmaps are stored in HugeArrays so they won't take up inordinate amounts of RAM; of course it's always wise to free the memory associated with a bitmap when that bitmap is no longer needed.

If you have freed the GState associated with a huge bitmap using GrDestroyBitmap() but want to make changes to the bitmap, all is not lost. Call GrEditBitmap() to associate a new GState with the bitmap. Be careful, however; the bitmap will not recall anything about the old GState, so you must set up colors, patterns, and other such information again. To update the VM file used to store a bitmap (if any), call GrSetVMFile() .

GrClearBitmap() clears the data from a bitmap.

GrGetPoint() can retrieve information from a bitmap, returning its color value for some location. It works with all sorts of display areas, not just bitmaps. It is mentioned here because of its usefulness for those who wish to be able to exercise effects on their bitmaps.

GrSetBitmapMode() gives you control over how drawing commands will affect the bitmap. If your bitmap has a mask, use this routine to switch between editing the mask and the bitmap itself; the BitmapMode argument will specify what is to be edited.

This routine also gives you control over how monochrome bitmaps should handle color. When you draw something in color to a monochrome bitmap, the system tries to approximate the color using dithering. It turns some pixels on and some pixels off in an attempt to simulate the color's brightness. A crimson area would appear with most pixels black, and thus rather dark. A pink area on the other hand would have mostly white pixels, and thus appear light. GrSetBitmapMode() can change which strategy GEOS will use when deciding which pixels to turn on. If you wish to use "dispersed" dithering, the pixels turned on will be spread out evenly, resulting in a smooth gray. However, some output devices (notably certain printers) have trouble drawing small, widely spaced dots accurately. Using "clustered" dithering causes the system to keep the pixels turned on close together, resulting in pictures reminiscent of newspaper photographs. Thus, devices that prefer a few big dots to lots of little dots will have an easier time with bitmaps so edited.

The bitmap mode information may also include a color transfer table. These tables are normally only used by printer drivers, but your geode is welcome to use them, if you can find a reason. Some models of printer have problems when mixing colors. Several printers have problems trying to mix dark colors, tending to end up with black. The table contains a byte value for each possible value of each color component. When mixing the colors, the graphics system will find the real color value to use in the table. Thus, if the reds in RGB bitmaps are looking somewhat washed out, you might set up a table so that reds would be boosted. RGBT_red[1] might be 16, RGBT_red[2] 23, so that the device would use more than the standard amount of the red component.

GrGetBitmapMode() returns the current bitmap editing mode.

GrSetBitmapRes() works with complex bitmaps, changing their resolution. Because simple bitmaps are assumed to be 72 dpi, their resolution cannot be changed unless they are turned into complex bitmaps. GrGetBitmapRes() returns the resolution associated with a complex bitmap.

GrGetBitmapSize() returns a bitmap's size in points. This function might be useful for quickly determining how much space to set aside when displaying the bitmap. GrGetHugeBitmapSize() retrieves the size of a bitmap stored in a HugeArray .

Use GrCompactBitmap() and GrUncompactBitmap() to compact and uncompact bitmaps. Compacted bitmaps take up less memory; uncompacted bitmaps draw more quickly. Note that the bitmap drawing routines can handle compacted and uncompacted bitmaps. These functions are here to aid programmers who wish more immediate control over their memory usage.

| Up: GEOS SDK TechDocs | Up | Prev: 6 Graphics State | Next: 8 Graphics Strings


Graphics Strings

Graphics Environment: 8 Graphics Strings

| Up: GEOS SDK TechDocs | Up | Down | Prev: 7 Working With Bitmaps | Next: 9 Graphics Paths

A GString is a data structure which represents a series of graphics commands. This structure may be stored in a chunk or VM file so that it may be played back later. An application may declare a GString statically or may create one dynamically using standard kernel drawing commands. They are used for describing application icons and printer jobs, among other things.

Storage and Loading

Special Drawing Commands

Declaring a GString Statically

Creating GStrings Dynamically

Drawing and Scanning

Editing GStrings Dynamically

Parsing GStrings

| Up: GEOS SDK TechDocs | Up | Down | Prev: 7 Working With Bitmaps | Next: 9 Graphics Paths


Storage and Loading

Graphics Environment: 8.1 Graphics Strings: Storage and Loading

| Up: GEOS SDK TechDocs | Up | Prev: 8 Graphics Strings | Next: 8.2 Special Drawing Commands
GrCreateGString(), GrDestroyGString(), GrLoadGString(), GrEditGString(), GrCopyGString(), GrGetGStringHandle(), GrSetVMFile()

GStrings may reside an a number of types of memory areas. Depending on the GString's storage, you will have to do different things to load it. One common case we have already discussed to some extent is when the GString is part of a visual moniker. In this case, the gstring will be stored in the gstring field of the @visMoniker 's implied structure. In this case the UI will do all loading and drawing of the GString.

The GString data itself consists of a string of byte-length number values. The graphics system knows how to parse these numbers to determine the intended drawing commands. You need not know the details of the format used--there are routines by which you may build and alter GStrings using common kernel graphics routines; however, macros and constants have been set up so that you may work with the data directly.

GString data may be stored in any of the following structures (corresponding to the values of the GStringType enumerated type):

Chunk
This is the storage structure of choice for GStrings which will be used as monikers.
VM Block
Virtual memory is normally used to store GStrings which may grow very large. GStrings residing in virtual memory may be dynamically edited.
Pointer-Referenced Memory
You may refer to GStrings by means of a pointer. However, this will only work for reading operations (i.e. you may not change the GString). This is the ideal way to reference a GString which is statically declared in a code resource.
Stream
Streams are not actually used to store data--they are used to transmit it between threads or devices. If you write a GString to a stream, it is assumed that some other application, perhaps on another device, will be reading the GString.

Note that a GString stored in a Stream, VM block, or in memory referenced only by a pointer is not quite ready to be drawn, only GStrings stored in a chunk may be drawn. Fortunately there is a routine which can load any type of GString into local memory so that it may be drawn.

If you are editing or creating the GString dynamically, it will have a GState associated with it. Any drawing commands made using this GState will be appended to the GString. This GState will not be stored with the GString; it is instead stored with the other GStates. You may destroy the GState when done editing, and hook up a new one if starting some other edit; this will not affect the GString's storage.

To dynamically create an empty GString, call the GrCreateGString() routine. You must decide where you want the GString to be stored--either in a chunk, a VM block, or a stream. If you wish to store the GString in a chunk or VM block, a memory unit of the appropriate type will be allocated for you. This routines will return the GState by which the GString may be edited and the chunk or VM block created.

The GrDestroyGString() routine allows you to free up the GState handle associated with your GString You may also destroy the GString's data if you wish; specify exactly what you want to destroy by means of the GStringKillType argument. In addition, you may destroy another GState. You must pass the global handle of the GString to destroy--this will be the handle returned by GrCreateGString() , GrEditGString() , or GrLoadGString() .

The GrLoadGString() command loads a GString into a global memory block so that it may be drawn. Actually, it doesn't load the entire GString into memory, but does initialize the data structure so that it may be referenced through the global memory handle which the routine returns.

The GrEditGString() command is very much like GrCreateGString() , except that instead of creating a new GString, it allows you to dynamically edit an existing GString. This command loads a VM-based GString into a special data structure. Like GrCreateGString() , it returns a GState to which you may make drawing commands. You may insert or delete drawing commands while in this mode, all using kernel drawing routines. For more information about using this routine, see Editing GStrings Dynamically .

The GrCopyGString() command copies the contents of one GString to another. At first you might think that you could do this by allocating the target GString with GrCreateGString() , then drawing the source GString to the provided GState. However, the GState may only have one GString associated with it, whether that GString is being used as a source or target.

To find the handle of the GString data associated with a GState, call GrGetGStringHandle() . To update the VM file associated with a GString (perhaps after calling VMSave() ), use GrSetVMFile() .

| Up: GEOS SDK TechDocs | Up | Prev: 8 Graphics Strings | Next: 8.2 Special Drawing Commands


Special Drawing Commands

Graphics Environment: 8.2 Graphics Strings: Special Drawing Commands

| Up: GEOS SDK TechDocs | Up | Prev: 8.1 Storage and Loading | Next: 8.3 Declaring a GString Statically
GrEndGString(), GrNewPage(), GrLabel(), GrComment(), GrNullOp(), GrEscape(), GrSetGStringBounds()

There are certain kernel graphics commands which, though they could be used when drawing to any graphics space, are normally only used when describing GStrings. Most of these commands have no visible effect, but only serve to provide the GString with certain control codes.

The most commonly used of these routines is GrEndGString() . This signals that you are done describing the GString, writing a GR_END_GSTRING to the GString. This routine will let you know if there was an error while creating the GString (if the storage device ran out of space). Its macro equivalent is GSEndString() .

The GrNewPage() routine signals that the GString is about to start describing another page. GStrings are used to describe things to be sent to the printer, and unsurprisingly, this routine is often used in that context. An example of a prototype multi-page printing loop is shown in Multi-Page Printing Loop . Whether or not it is drawing to a GString, GrNewPage() causes the GState to revert to the default. When calling this routine, specify whether or not a form-feed signal should be generated by means of the PageEndCommand argument.

Code Display 23-1 Multi-Page Printing Loop

/* The application this code fragment is taken from stores several pages of
 * graphics in one coordinate space. The pages are arranged each below the other.
 * To display page #X, we would scroll down X page lengths. */
for (curPage=0; curPage < numberOfPages; curPage++) {
    GrSaveState(gstate);
    GrApplyTranslation(gstate, 0,MakeWWFixed(curPage*pageHeight));
    /* ...Draw current page... */
    GrRestoreState(gstate);
    GrNewPage(gstate, PEC_NO_FORM_FEED );}

The GrLabel() routine inserts a special element into the GString. This element does not draw anything. However, these GString labels, as they are called, are often used like labels in code. By using GString drawing routines with a certain option (described below), you may "jump" to this label and start drawing from that point in the GString. Alternately, you could start at some other part of the GString and automatically draw until you encountered that label.

The GrComment() routine inserts an arbitrary-length string of bytes into the GString which the GString interpreter will ignore when drawing the GString. You might use this to store anything from custom drawing commands which only your geode has to be able to understand to a series of blank bytes which could act as a sort of placeholder in memory.

Code Display 23-2 GSComment Example

static const byte MyGString[] = {
    GSComment(20), 'C','o','p','y','l','e','f','t',' ','1','9','9','3',
    ' ','P','K','i','t','t','y',
    GSDrawEllipse(72, 72, 144, 108),
    GSEndString()};

The GrNullOp() routine draws nothing and does nothing other than take up space in the GString (a single-byte element of value GR_NOP). You might use it as a placeholder in the GString.

The GrEscape() writes an escape code with accompanying data to the GString. Most geodes do not use this functionality; you might use it to embed special information in a TransferItemFormat based on GStrings.

Depending on what the system will do with your GString, the bounds of the GString may be used for many purposes. Normally the system determines the bounds of a GString by traversing the whole GString and finding out how much space it needs to draw. The GrSetGStringBounds() allows you to optimize this, setting up a special GString element whose data contains the bounds of the GString. You should call this routine early on in your GString definition so that the system won't have to traverse very much of your GString to discover the special element.

| Up: GEOS SDK TechDocs | Up | Prev: 8.1 Storage and Loading | Next: 8.3 Declaring a GString Statically


Declaring a GString Statically

Graphics Environment: 8.3 Graphics Strings: Declaring a GString Statically

| Up: GEOS SDK TechDocs | Up | Prev: 8.2 Special Drawing Commands | Next: 8.4 Creating GStrings Dynamically

For most programmers, the first encounter with GStrings (often, in fact, their first encounter with any sort of graphics mechanism) is with a program icon. Often this program icon consists of one or more GStrings, each of which contains a bitmap. These monikers are often set up using the @visMoniker keyword. This automatically stores the GString to a chunk. For an example of a GString stored this way, see the appSMMoniker GString in GString in Visual Monikers .

Code Display 23-3 GString in Visual Monikers

@start AppResource;
@object GenApplicationClass AppIconApp = {
/* The visual moniker for this application (an icon) is created by selecting the
 * most appropriate moniker from the list below. Each moniker in this list is
 * suitable for a specific display type. The specific UI selects the moniker
 * according to the display type GEOS is running under. A text moniker is also
 * supplied if the specific UI desires a textual moniker. */
 GI_visMoniker = list {
    AppIconTextMoniker, /* simple text moniker */
    appLCMoniker, /* Large Color displays */
    appLMMoniker, /* Large Monochrome displays */
    appSCMoniker, /* Small Color displays */
    appSMMoniker, /* Small Monochrome displays */
    appLCGAMoniker, /* Large CGA displays */
    appSCGAMoniker /* Small CGA displays */ }
}
@visMoniker AppIconTextMoniker = "C AppIcon Sample Application";
@end AppResource
@start SAMPLEAPPICONAREASMMONIKERRESOURCE, data;
@visMoniker appSMMoniker = {
    style = icon;
/* Small monochrome icons use the standard size and 1 bit/pixel gray coloring. */
    size = standard; color = gray1; aspectRatio = normal; cachedSize = 48, 30;
/* The following lines hold GString data: */
  @gstring {
        /* GSFillBitmapAtCP:
         * This macro signals that we want to fill a bitmap. The next few
         * bytes should consist of information about the bitmap; the data
         * after that holds the bitmap data. The GString reader will know
         * when it's done reading the bitmap data based on the size stated
         * in the bitmap's info--it will thus know where to look for the
         * next command (in this case a GSEndString() macro. */
     GSFillBitmapAtCP(186),
        /* Bitmap():
         * This macro is used to write basic information about the bitmap
         * to the GString. In this case, that information consists of:
         * The bitmap is 48x30. It is compressed using PackBits. It is
         * monochrome. */
    Bitmap (48, 30, 0, BMF_MONO),
        /* 0x3f, 0xff, 0xff, ...:
         * These numbers are the bitmap data itself. */
    0x3f, 0xff, 0xff, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0,
    0x60, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x60,
    0x60, 0x00, 0x00, 0x80, 0x00, 0x60, 0x60, 0x00, 0x01, 0xc0, 0x00, 0x60,
    0x60, 0x00, 0x03, 0xe0, 0x00, 0x60, 0x60, 0x00, 0x07, 0xf0, 0x00, 0x60,
    0x60, 0x00, 0x03, 0xf8, 0x03, 0xfe, 0x60, 0x00, 0x01, 0xfc, 0x00, 0xf9,
    0x60, 0x06, 0x04, 0xfe, 0x01, 0xfd, 0x60, 0x38, 0x06, 0x1f, 0x01, 0xfd,
    0x60, 0x40, 0x03, 0x63, 0x81, 0x05, 0x60, 0x80, 0x01, 0xa3, 0xc1, 0x04,
    0x60, 0x40, 0x00, 0xc3, 0x81, 0x04, 0x60, 0x38, 0x00, 0x6f, 0x01, 0x04,
    0x60, 0x07, 0xfe, 0xf6, 0x00, 0x88, 0x60, 0x00, 0x01, 0xfc, 0x01, 0x8c,
    0x60, 0x00, 0x03, 0xfe, 0x03, 0xde, 0x60, 0x00, 0x07, 0xf5, 0x07, 0x77,
    0x60, 0x00, 0x03, 0xe2, 0x8f, 0xaf, 0x60, 0x00, 0x01, 0xc3, 0xdf, 0xdf,
    0x60, 0x00, 0x00, 0x81, 0xff, 0xff, 0x60, 0x00, 0x00, 0x00, 0xff, 0xff,
    0x60, 0x00, 0x00, 0x00, 0x7b, 0xff, 0x60, 0x00, 0x00, 0x00, 0x03, 0xff,
    0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff,
    0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff,
        /* GSEndString():
         * This macro lets the GString interpreter know we're done. */
    GSEndString() } }
@end SAMPLEAPPICONAREASMMONIKERRESOURCE;

Notice that this example uses macros to set up the data for the GString. We could have just written the GString data as a series of numbers, as shown in GString Declared Without Macros , but the macros are usually easier to read. Each macro's name, you will notice, is taken from the corresponding graphics command name. Thus the GSFillBitmap() macro corresponds to the GrFillBitmap() routine. There are no GSGet...() macros; GStrings have no return values or conditional statements, and thus have no use for retrieving this sort of information.

Code Display 23-4 GString Declared Without Macros

const char byte myGString[] = {
    GR_SET_LINE_STYLE,			/* First we set the line style. */
    LS_DASHED,			/* 	We want a dashed line. */
    0, 			/* 	Draw dashes starting with index 0 */
    GR_DRAW_RECT_TO,			/* Next we draw a rectangle from our current
                 * position to... */
    0, 72, 0, 72, 			/*	...the point (72, 72). (We need the zeroes
                 * 	because GStrings are arrays of bytes, but
                 * 	we need a word to describe each
                 * 	coordinate. */
    GR_END_GSTRING
}; /* myGString */

Just as all monikers are not GStrings, not all GStrings need be declared as monikers. See Statically Declared GString for an example of a statically declared GString taken from the Moniker sample application; here the declared GString is eventually used as a moniker, but could just as well have been passed to GrDrawGString() and drawn to an arbitrary graphics space.

Code Display 23-5 Statically Declared GString

static void _near			SetPreDefinedGStringMoniker(void)
{
/*
 * A predefined graphics string that draws a
 * MONIKER_WIDTH by MONIKER_HEIGHT * light-blue rectangle at 0, 0.
 */
static const byte gstring[] = {
    GSSetAreaColorIndex(C_LIGHT_BLUE),
    GSFillRect(0, 0, MONIKER_WIDTH, MONIKER_HEIGHT),
    GSEndString() };
@call CycleMonikerTrigger::MSG_GEN_REPLACE_VIS_MONIKER(
    VUM_NOW, 				/* Update appearance immediately */
    MONIKER_HEIGHT, MONIKER_WIDTH, 				/* Since source is a GString, we need to
                     * pass the height and width of the
                     * GString. */
    sizeof(gstring), 				/* Pass the size of sourceGString. */
     VMDT_GSTRING, 				/* Source is a gstring.... */
    VMST_FPTR, 				/* ...referenced by a far pointer */
    (dword) gstring); 				/* Pointer to gstring */
} /* End of SetPreDefinedGStringMoniker() */

For more examples of statically declared GStrings, see the Moniker, GSTest, and GSTest2 sample applications.

| Up: GEOS SDK TechDocs | Up | Prev: 8.2 Special Drawing Commands | Next: 8.4 Creating GStrings Dynamically


Creating GStrings Dynamically

Graphics Environment: 8.4 Graphics Strings: Creating GStrings Dynamically

| Up: GEOS SDK TechDocs | Up | Prev: 8.3 Declaring a GString Statically | Next: 8.5 Drawing and Scanning

Sometimes it comes in handy to be able to create GStrings "on the fly." To add elements to a GString, issue normal kernel drawing commands, but use a GState which is associated with the GString.

To create a new, empty GString ready for editing (i.e. with an attached GState), call GrCreateGString() . At this point, you may draw to the GString using normal drawing commands. For an example of creating a GString in this manner, see Creating a GString Dynamically .

Code Display 23-6 Creating a GString Dynamically

#define LABEL_BOX    2
#define LABEL_CIRCLE 3
gstate = GrCreateGString(file, GST_VMEM, &myVMBlock);
GrSetLineColor(gstate, CF_INDEX, C_BLUE, 0, 0);
GrDrawRect(gstate, 0, 0, 620, 500);
GrLabel(gstate, LABEL_BOX);
GrSetAreaColor(gstate, CF_INDEX, C_RED, 0, 0);
GrFillRect(gstate, 10, 130, 610, 490);
GrSetLineWidth(gstate, MakeWWFixed(2));
GrLabel(gstate, LABEL_CIRCLE);
GrSetAreaColor(gstate, CF_INDEX, C_RED, 0, 0);
GrFillEllipse(gstate, 130, 10, 490, 370);
GrDrawEllipse(gstate, 130, 10, 490, 370);
GrEndGString(gstate);
GrDestroyGString(gstate, 0, GSKT_LEAVE_DATA);

Drawing to a GString in this manner is almost exactly like drawing in any other GEOS graphics environment. However, there are some important rules to keep in mind.

  • The GString must end with a GR_END_GSTRING element; when the GString interpreter encounters this element, it knows to stop drawing. When creating a GString dynamically, the normal way to assure this is to call GrEndGString() . (Actually, this rule is not strictly true--when you learn more about drawing GStrings, you will see that it is possible to stop GString drawing based on other cues. However, it's always safest to end the GString with a GR_END_GSTRING in case some other application tries to draw the same GString.)
  • Remember that you are creating a data structure which will be used later. The only commands which will affect the GString's contents are the kernel graphics routines, and only those which actually draw something or change the drawing properties. When creating a GString, it is tempting to include constructions like the following:
if (redFlag)
    {GrSetAreaColor(
        gstate, C_RED, CF_INDEX, 0, 0);}
else  {GrSetAreaColor(
        gstate, C_BLUE,CF_INDEX, 0, 0);}
GrFillRect(gstate, 0, 0, 72, 72);
  • You might be surprised when you found out that the following code fragments both drew rectangles which were the same color:
redFlag = FALSE;
GrDrawGString(			screenGState,
            myGString,
            0, 0, 0, elem);
redFlag = TRUE;
GrDrawGString(			screenGState,
            myGString,
            0, 0, 0, elem);
  • The factor which would determine the color of the drawing in this example would be the value of redFlag when you were creating the GString, not when you were drawing it.
  • Think carefully before making coordinate space transformations in GStrings. If you want to remove all transformation effects, you should always call GrSetDefaultTransform() , instead of GrSetNullTransform() . By using GrSetDefaultTransform() , an application that is including your GString can apply some other type of transformation and make that the default; your application will then appear transformed as intended. However, if you call GrSetNullTransform() , you ignore that default transform and will appear in a strange way.
  • If you use GrInitDefaultTransform() , you should probably bracket its use with calls to GrSaveTransform() and GrRestoreTransform() . This save/restore pair will also save the current default transformation, if there is one. By adding the save and restore, you will be preserving whatever default transform the application including yours has set up.
  • If you think your Graphics String will never show up included in some other application, consider the following: The print-to-file feature creates a graphics string that can be imported into several other applications.
  • If you think you have to use GrSetTransform() , try replacing it with a GrSetDefaultTransform() / GrApplyTransform() pair. This will most likely have the same effect, but will be more palatable to another application using the GString.
  • If you are including some other externally-created Graphics String into your document, you probably want to bracket it with GrSaveState() and GrRestoreState() .
  • If you're creating a multi-page GString which might be printed, make sure that each page is independent. There should be nothing assumed about the GState at the beginning of any page--you should instead assume that each will begin with the default GState. This applies to transformations, drawing properties, the GState's path, and so on.
    Keep in mind that each page should be able to print by itself if extracted from a multi-page document.
    The following pseudocode is an example of a "bad" idea:
for(curPage=0;curPage < numberOfPages; curPage++) {
 /* { draw current page } */
 GrNewPage();
 GrApplyTranslation(0, pageHeight); }
  • The following pseudocode provides a "better" way to do the same thing:
for (curPage=0; curPage < numberOfPages; curPage++) { GrSaveState();
  GrApplyTranslation(0,curPage*pageHeight);
  /* {draw current page} */
 GrRestoreState();
 GrNewPage(); }

Probably the most important piece of advice is to think about how the Graphics String will be used. If it will be used only under certain well-controlled circumstances, the above concerns may not affect you.

| Up: GEOS SDK TechDocs | Up | Prev: 8.3 Declaring a GString Statically | Next: 8.5 Drawing and Scanning


Drawing and Scanning

Graphics Environment: 8.5 Graphics Strings: Drawing and Scanning

| Up: GEOS SDK TechDocs | Up | Prev: 8.4 Creating GStrings Dynamically | Next: 8.6 Editing GStrings Dynamically
GrDrawGString(), GrDrawGStringAtCP(), GrSetGStringPos(), GrGetGStringBounds(), GrGetGStringBoundsDWord()

There are several ways to use a GString. You've already seen how to use one as the visual moniker for a UI gadget. In that case, the UI is responsible for drawing the moniker. If you are learning about interfacing with printers, you probably know that you pass a GString to a PrintControl object to describe print jobs.

There is also a kernel graphics routine for drawing GStrings directly. The GrDrawGString() command draws a GString, or a part thereof. Remember that the GString must be loaded for drawing; you must call GrLoadGString() if you have not done so already (or if you have destroyed the GString since you last called GrLoadGString() ).

Code Display 23-7 GrDrawGString() in Action

@method StaticContentClass, MSG_VIS_DRAW {
    word 	lElem;
    static const byte gstringData[] = {
        GSSetAreaColorIndex(C_RED), GSFillRect(0, 0, 72, 72),
        GSEndString() };
    @callsuper();
    gstring = GrLoadGString 			(PtrToSegment(gstringData), GST_PTR,
                 PtrToOffset(gstringData));
    GrDrawGString(gstate, gstring, 0, 0, 0, &lElem); }
@method DynamicContentClass, MSG_VIS_DRAW{
    Handle 		gstring;
    VMFileHandle		file;
    char 		fileString[] = ".", 0;
    VMBlockHandle		gstringData;
    GStringElement		lElem;
    @callsuper();
    file = VMOpen(fileString,
           (VMAF_FORCE_READ_WRITE | VMAF_USE_BLOCK_LEVEL_SYNCHRONIZATION),
           VMO_CREATE_TRUNCATE,
           0);
    gstring = GrCreateGString(file, GST_VMEM, &gstringData);
    GrSetAreaColor(gstring, CF_INDEX, C_RED, 0, 0);
    GrFillRect(gstring, 0, 0, 72, 72);
    GrEndGString(gstring);
    GrDestroyGString(gstring, NULL, GSKT_LEAVE_DATA);
    gstring = GrLoadGString(file, GST_VMEM, gstringData);
    GrDrawGString(gstate, gstring, 0, 0, 0, &lElem);
    GrDestroyGString(gstring, NULL, GSKT_DESTROY_DATA);
    FileDelete(fileString);

The GrDrawGString() routine has several arguments. A simple usage of the routine is shown in GrDrawGString() in Action . To take advantage of some of the more powerful features of GrDrawGString() , you should know what the purpose of the arguments.

  • You must provide a GState to draw to. You may wish to call GrSaveState() on the GState before drawing the GString (and call GrRestoreState() afterwards). If you will draw anything else to this GState after the GString, you must call GrDestroyGString() on the GString, and pass this GState's handle as the gstate argument so that GrDestroyGString() can clean up the GState.
  • You must provide a GString to draw. The GString must be properly loaded (probably by means of GrLoadGString() ).
  • You can provide a pair of coordinates at which to draw the GString. The graphics system will translate the coordinate system by these coordinates before carrying out the graphics commands stored in the GString.
  • You can provide a GSControl argument which requests that the system stop drawing the GString when it encounters a certain type of GString element. If the GString interpreter encounters one of these elements, it will immediately stop drawing. The GString will remember where it stopped drawing. If you call GrDrawGString() with that same GString, it will continue drawing where you left off. Note that any time a GString-traversing function such as GrDrawGString() returns, it returns a GSRetType value which makes it clear exactly why it stopped traversing the GString.
  • You must provide a pointer to an empty GStringElement structure. GrDrawGString() will return a value here when it is finished drawing. If the GString has stopped drawing partway through due to a passed GSControl , the returned GStringElement value will tell you what sort of command was responsible for halting drawing. For instance, if you had instructed GrDrawGString() to halt on an `output' element (GrDraw...() or GrFill...() commands), then when GrDrawGString() returns, you would check the value returned to see what sort of output element was present.

Note that those last two arguments aren't very useful except when used in conjunction with some other GString routines which we will get to later.

The GrDrawGStringAtCP() routine functions in much the same way as GrDrawGString() , except that the current pen position will be used in the place of passed coordinate arguments.

The GrSetGStringPos() routine allows you to skip any number of GString elements, or go back to the beginning or end of the GString. You specify whether you want to skip to the beginning, end, or ahead by a few steps; this is specified by the GStringSetPosType argument. This routine is useful both when drawing and editing GStrings. Note that you may also use this routine to jump backwards in a GString, but this only works with VM-based GStrings. The GString must be loaded for drawing or editing, and you will pass in the GString's global handle, as supplied by GrLoadGString() or GrEditGString() .

Code Display 23-8 GrSetGStringPos() In Action

/* The following routine is used to allow creating an `overlay' effect. Normally,
 * multi-page GStrings will contain form feed elements that signal the break
 * between pages. Here we see a section of code which will filter out form feeds by
 * skipping those elements. The result will be a single page in which all the pages
 * of the original GString are drawn on top of each other. */
    for (	gsr = GrDrawGString(gstate, gstring, 0, 0, GSC_NEW_PAGE, gse);
        gsr == GSR_FORM_FEED;
        gsr = GrDrawGString(gstate, gstring, 0, 0, GSC_NEW_PAGE, gse) )
            {GrSetGStringPos(gstring, GSSPT_SKIP_1, 0); }

Because a GString remembers its place when you stop drawing partway through, if you wish to `reset' the GString position, you should use GrSetGStringPos() to set it back to the beginning.

Occasionally you may be curious to know how much space is necessary to draw a GString. The GrGetGStringBounds() routine determines this, returning the coordinates describing the GString's bounding rectangle. If the GString may have very large bounds, you should use the GrGetGStringBoundsDWord() routine instead.

| Up: GEOS SDK TechDocs | Up | Prev: 8.4 Creating GStrings Dynamically | Next: 8.6 Editing GStrings Dynamically


Editing GStrings Dynamically

Graphics Environment: 8.6 Graphics Strings: Editing GStrings Dynamically

| Up: GEOS SDK TechDocs | Up | Prev: 8.5 Drawing and Scanning | Next: 8.7 Parsing GStrings
GrEditGString(), GrDeleteGStringElement()

Applications may find cause to dynamically alter an existing GString. You might create a sort of template GString and want to fill in some parts at a later time. If you will draw several similar GStrings, it might be nice to use a single GString, but change only certain parts before drawing each time.

GrEditGString() allows you to edit an existing GString. It only works with VM-based GStrings. Calling GrEditGString() allocates a new GState and associates it with the GString. Any drawing commands to this GState will be appended to the GString. You may use GrDrawGString() (along with appropriate GSControl values) and GrSetGStringPos() to change position within the GString, allowing you to insert new commands into the middle of the GString.

The GrDeleteGStringElement() routine allows you to delete any number of GString elements. The elements deleted will be taken starting at your position in the GString. This command only works while editing the GString, and you must pass the GString's editing GState handle to this routine.

| Up: GEOS SDK TechDocs | Up | Prev: 8.5 Drawing and Scanning | Next: 8.7 Parsing GStrings


Parsing GStrings

Graphics Environment: 8.7 Graphics Strings: Parsing GStrings

| Up: GEOS SDK TechDocs | Up | Prev: 8.6 Editing GStrings Dynamically | Next: 9 Graphics Paths
GrGetGStringElement(), GrParseGString()

For complicated GString operations, you may find the following advanced routines helpful.

GrGetGStringElement() returns the raw data associated with the current GString element. To understand this stream of bytes, you must know what sorts of data are associated with each kind of GString element. For example GrGetGStringElement () might return GR_DRAW_RECT with the following buffer of bytes:

GR_DRAW_RECT, 0x00, 0x48, 0x00, 0x24,
0x00, 0x90, 0x00, 0x84

You must know enough about GString element structures to know that this will draw a rectangle with bounds {72, 36, 144, 108}. To find out this sort of information, examine the GS...() macros, or search gstring.h for macros containing the appropriate GStringElement value.

Code Display 23-9 GrGetGStringElement() In Action

/* Our application allows for a second kind of spline, a B-spline. This spline
 * looks similar to a regular Bézier spline, but is somewhat different and uses
 * a different mathematical formula. When this app creates a GString, it will
 * output Bézier splines in the normal way.
 * When outputting a B-spline to a GString, it outputs the GString element for a
 * regular spline. That way, other applications will be able to draw the GString
 * mostly correctly. However, all B-spline elements will be preceded by a
 * GString comment 'B''s''p''l'.:
 *	GSComment(4), 'B','s','p','l',
 *	GSDrawSpline(...), ...
 * The following snippet of code will be used when this application draws a
 * GString. It will look for the significant comments. When it finds them, it will
 * know that the following GR_DRAW_SPLINE element should actually be treated
 * differently. */
    for (	gsr = GrDrawGString(gstate, gstring, 0, 0, GSC_MISC, gse);
        gsr == GSRT_MISC;
        gsr = GrDrawGString(gstate, gstring, 0, 0, GSC_MISC, gse) )
            {byte canonicalBuffer[] = {GR_COMMENT,'B','s','p','l'};
             byte buffer[20];
             int eSize;
              GrGetGStringElement(gstate, gstring,
                sizeof(buffer),&buffer, &eSize);
             /* First check to see if this is the
              * comment we're looking for: */
            if (strncmp(buffer, canonicalBuffer, 5)) {
                 /* Skip ahead to the GrDrawSpline element */
                GrSetGStringPos(gstring, GSSPT_SKIP_1, 0);
                GrGetGStringElement(gstate, gstring,
                    sizeof(buffer), &buffer, &eSize);
                 /* Draw spline using our routine */
                MyDrawBSpline(gstate, buffer+3,
                    (eSize-3)/sizeof(Point))
                 /* Advance GString so kernel won't draw a
                  * Bézier spline over our B-spline. */
                GrSetGStringPos(gstring, GSSPT_SKIP_1, 0) }
        }

The GrParseGString() command calls a callback routine on all elements of a GString which match some criterion. The routine may save information about the elements, draw to a GState, or something completely different. GrParseGString() takes the following arguments:

  • GString to parse.
  • GState handle. GrParseGString() itself will do nothing with this handle, and passing a NULL handle is permitted. However, this GState will be passed to the callback routine. If your callback routine will draw, it is thus convenient to pass a properly initialized GState to GrParseGString() which the callback routine may then draw to.
  • A record of type GSControl . This will determine which elements will be passed on to the callback routine. If you set GSC_OUTPUT, the callback routine will be called only for those GString elements which draw something. If you set GSC_ONE, the callback routine will be called upon all of the GString elements.
  • Far pointer to the callback routine itself.

The callback routine is passed a pointer to the GString element and the handle of the GState that was passed to GrParseGString() .

| Up: GEOS SDK TechDocs | Up | Prev: 8.6 Editing GStrings Dynamically | Next: 9 Graphics Paths


Graphics Paths

Graphics Environment: 9 Graphics Paths

| Up: GEOS SDK TechDocs | Up | Prev: 8 Graphics Strings | Next: 10 Working With Video Drivers
GrBeginPath(), GrEndPath(), GrCloseSubPath(), GrSetStrokePath(), GrGetPathBounds(), GrTestPointInPath(), GrGetPathPoints(), GrGetPathRegion(), GrGetPath(), GrSetPath(), GrGetPathBoundsDWord(), GrTestPath()

A path is a data structure which describes a route across a graphics space. Normally, applications use the path to specify an arbitrary screen area, defining a path that describe's the area's borders. Like a GString, a path is created by calling an appropriate initiation routine followed by a series of drawing commands.

Paths don't have to be continuous. An unconnected path is said to be made up of more than one path element.

Paths can be created by taking the intersection or union of two or more paths. In this case, the paths which are combined to make the resulting path are called sub-paths .

Paths are normally used to describe regions. However, regions are basically bitmaps, whereas paths are described in terms of the standard coordinate space. Thus, while regions don't scale well at all, it is possible to scale a path perfectly, then use the transformed path to more correctly compute the desired region. If this comparison sounds similar to that between bitmap- and outline- based fonts, it should. The "outline" of an outline-based font character is, in fact, a specialized form of path.

Paths are also used to describe clipping regions. It is possible to combine any path with a window's clipping region to further restrict the clipping area. Thus it is possible to clip to an ellipse or to a Bézier curve, or even to use text as a clipping region.

When constructing a path, you should keep in mind what it will be used for. If the path is to be filled using the winding rule, it is important that all edges go the correct direction. The winding rule algorithm assumes that the region is described by edges that go around it counterclockwise. Edges going clockwise describe hollows. The odd/even fill rule will work independently of path direction. See the Drawing Shapes chapter for more information about winding rules.

Both the winding and odd/even fill rules demand closed path elements. If any path elements are not filled, the routine will treat them as if they were, connecting the ends of each open path element with a straight line.

It is also possible to alter an existing path by combining it with another. The new path thus formed preserves both paths. If drawn, both paths will appear. If the path is filled or used as a clipping region, the geode can specify how the regions described by the paths should be combined, whether the intersection or union should be taken. Any number of paths can be combined in this manner.

GrBeginPath() signals that the geode is about to start describing a path. All drawing commands directed at the GState will go into constructing the path until GrEndPath() is called. GrBeginPath() is also the routine used to combine a path with an existing path. Calling GrBeginPath() when already constructing a path signals that further graphics commands should describe a new path to be combined with the existing one. The new path can either replace the existing one or combine to find the intersection or the union.

GrCloseSubPath() closes the current path element, adding a straight line segment from the current position to the starting point if necessary.

GrGetPathBounds() returns the bounding coordinates of a rectangle that completely encloses the current path. GrTestPointInPath() tests whether a passed point falls within the current path. GrGetPathPoints() returns all the points along a path in the order visited. GrGetPathRegion() returns the region defined by the path. GrGetPathBoundsDWord() returns the bounds of the path and works in a 32-bit graphics space. If you just want to know whether or not a given path exists, then call GrTestPath() , passing the GetPathType GPT_CURRENT.

GrSetStrokePath() replaces the current path with the one that would result from "stroking" (drawing) the current path with the current line attributes. For example, if the current line style is a dotted line, the result will most likely be a set of many skinny regions. At this time, stroke paths cannot be used for clipping purposes. However, geodes can still draw and fill these paths.

Paths can be drawn or filled using the GrDrawPath() and GrFillPath() commands. For more information about these routines, see the Drawing Shapes chapter.

GrGetPath() retrieves the handle of a block containing the path's data. You may pass this handle to GrSetPath() and thus copy a path to another GState.

| Up: GEOS SDK TechDocs | Up | Prev: 8 Graphics Strings | Next: 10 Working With Video Drivers


Working With Video Drivers

Graphics Environment: 10 Working With Video Drivers

| Up: GEOS SDK TechDocs | Up | Down | Prev: 9 Graphics Paths | Next: 11 Windowing and Clipping

The main benefit of the device independence is that the geode writer can issue graphics commands without worrying about the device. Working with the video driver is left to the graphics system.

Kernel Routines

Direct Calls to the Driver

| Up: GEOS SDK TechDocs | Up | Down | Prev: 9 Graphics Paths | Next: 11 Windowing and Clipping


Kernel Routines

Graphics Environment: 10.1 Working With Video Drivers: Kernel Routines

| Up: GEOS SDK TechDocs | Up | Prev: 10 Working With Video Drivers | Next: 10.2 Direct Calls to the Driver
GrInvalRect(), GrInvalRectDWord(), GrGrabExclusive(), GrGetExclusive(), GrReleaseExclusive(), GrBitBlt(), GrGetBitmap(), GrGetWinHandle()

Sometimes the geode may want more power over the driver. It can see what device coordinates correspond to a set of standard GEOS coordinates or vice versa.

It is possible to update part of a drawing without exposing the whole window. Calling GrInvalRect() causes a passed rectangular area to be updated; the area outside the rectangle will be unaffected. GrInvalRectDWord() works the same way, but for large coordinate spaces.

Programs can also seize exclusive access to a video driver by calling the GrGrabExclusive() command, which allows only the passed GState to alter what is shown on the screen. This routine is useful for programs such as screen dumps who want to accomplish something concerning the screen without worrying that programs in other threads will change the screen in the meantime. GrReleaseExclusive() ends the exclusive access to the screen so that other GStates can update.

To find out if the video exclusive is presently grabbed, call GrGetExclusive() . This will return the handle of the GState presently in possession of the exclusive, or zero if there is no such GState.

GrBitBlt() is an advanced function used to quickly copy or move data within video memory. Effectively, it can copy or move a rectangular part of the document space to another part of that space--the passed BLTMode will determine whether the area is copied or moved. This might be used in an arcade game or animation program to move simple pictures around the screen very quickly. Pass this function the source and destination rectangles, and whether you are copying or moving the block. After calling GrBitBlt() , the changes have been made to video memory but have not actually been drawn to the screen. Make sure that the affected areas are redrawn by invalidating the appropriate area. If speed is a concern, and if you're using GrBitBlt() it probably is, you'll probably want to restrict the clipping area when the drawing is refreshed.

GrGetBitmap() basically returns a dump of an arbitrary display area. It is an advanced function and should be used with some caution. If asked to dump an area from a GState being displayed to screen, GrGetBitmap() won't check to see if the dumped area is obscured by another window, and so your dump might include a picture of that other window. However, if you're working with some sort of offscreen bitmap, this function provides a way to look at large portions of it a time. Note that to look at smaller areas, you might prefer to use GrGetPoint() , an optimized, easier to use function to find out the color of a pixel.

At some times, it may prove useful to know what window, if any, is associated with a GState. To find out, call GrGetWinHandle() .

| Up: GEOS SDK TechDocs | Up | Prev: 10 Working With Video Drivers | Next: 10.2 Direct Calls to the Driver


Direct Calls to the Driver

Graphics Environment: 10.2 Working With Video Drivers: Direct Calls to the Driver

| Up: GEOS SDK TechDocs | Up | Prev: 10.1 Kernel Routines | Next: 11 Windowing and Clipping

Rarely, a geode may wish to make direct calls to the video driver. In most cases, anything your code might want the video driver to do can be handled better by going through the appropriate graphics routine.

| Up: GEOS SDK TechDocs | Up | Prev: 10.1 Kernel Routines | Next: 11 Windowing and Clipping


Windowing and Clipping

Graphics Environment: 11 Windowing and Clipping

| Up: GEOS SDK TechDocs | Up | Down | Prev: 10 Working With Video Drivers | Next: 11.1 Palettes

Windows are the interface between the graphics commands and the GEOS user interface. In this section we will discuss some of the graphical mechanisms associated with windows in GEOS.

Palettes

Clipping

Signalling Updates

| Up: GEOS SDK TechDocs | Up | Down | Prev: 10 Working With Video Drivers | Next: 11.1 Palettes


Palettes

Graphics Environment: 11.1 Windowing and Clipping: Palettes

| Up: GEOS SDK TechDocs | Up | Prev: 11 Windowing and Clipping | Next: 11.2 Clipping

Each window has a color palette associated with it. For more information about manipulating palettes, see the Drawing Shapes chapter. The system will use the palette of whatever window is active. As a result, if two windows have different palettes, when one window is active, the other's colors will be distorted.

| Up: GEOS SDK TechDocs | Up | Prev: 11 Windowing and Clipping | Next: 11.2 Clipping


Clipping

Graphics Environment: 11.2 Windowing and Clipping: Clipping

| Up: GEOS SDK TechDocs | Up | Prev: 11.1 Palettes | Next: 11.3 Signalling Updates
GrSetClipPath(), GrSetClipRect(), GrGetClipRegion(), GrTestRectInMask(), GrSetWinClipRect(), GrGetMaskBounds(), GrGetMaskBoundsDWord(), GrGetWinBounds(), GrGetWinBoundsDWord()

The graphics system provides some routines which allow geodes to work together with the windowing system to control clipping. A window's clipping region, you will recall, is that area of the window that must be redrawn. It corresponds to that area of the graphics space which is visible inside the window and not obscured by another window. The area outside the clipping region is supposed to be that area that doesn't need to be redrawn. Geodes which are certain that part of their graphics space doesn't need to be (or shouldn't be) redrawn can restrict their clipping region to exclude this portion.

Restricting the clipping region can lead to quicker redraws, since the graphics system doesn't have as much to redraw. Arcade games in which most of the action only takes place in one or two areas of the display can restrict their clipping region to speed redraws in the smaller, active areas. Also, the clip region can be used as a sort of stencil.

The clip region is defined by a path which describes the boundaries of the region. A window typically begins with a rectangular clip region. If it is partially obscured by other windows, this rectangle will have "bites" taken out of it. This is done using normal path routines used for combining paths. The clipping path of a newly obscured window is determined by computing its old clipping path with that of the obscuring window.

Since any number of paths may be combined, it is a simple matter for the graphics system to combine a window's clipping path with a geode's path chosen to restrict the clipping region. Thus the geode can define its restricted clipping area without knowing anything about the window's present status.

GrSetClipPath() sets the geode-defined path to use when restricting the window's clip path. GrSetClipRect() is an optimization of GrSetClipPath() to handle the most common case in which the new path element is a rectangle.

GrGetClipRegion() returns the Region data structure corresponding to the current clip path. GrGetMaskBounds() and GrGetMaskBoundsDWord() return the bounds of the current clipping path.To find out whether there is a clip path at all, just call GrTestPath() and pass GPT_CLIP.

GrTestRectInMask() determines whether a rectangular area lies fully, partially, or not at all within the clipping area. This routine is useful for optimizing redraws.

GrSetWinClipRect() and GrSetWinClipPath() set the clipping path associated with the window; you should never have occasion to use it. GrGetWinBounds() and GrGetWinBoundsDWord() return the bounds of the window clipping path. To find out whether there is a window clipping path at all, call GrTestPath() and pass GPT_WIN_CLIP.

| Up: GEOS SDK TechDocs | Up | Prev: 11.1 Palettes | Next: 11.3 Signalling Updates


Signalling Updates

Graphics Environment: 11.3 Windowing and Clipping: Signalling Updates

| Up: GEOS SDK TechDocs | Up | Prev: 11.2 Clipping
GrBeginUpdate(), GrEndUpdate()

The kernel provides two messages by which the geode may signal that it is updating the contents of a window. When updating a region (as when handling a MSG_META_EXPOSED ), the geode should call GrBeginUpdate() after creating the GState, and call GrEndUpdate() before destroying the GState.

This causes the system to store the GState for future comparisons during clipping region calculation. If you don't call these functions, the clipping region is likely to be wrong for this and other updates. Default system MSG_META_EXPOSED handlers call these routines.

Note that you only need call these routines when performing a requested update; if you are drawing to a window without being asked to do so, you need not call these routines.

| Up: GEOS SDK TechDocs | Up | Prev: 11.2 Clipping


Drawing Graphics

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Drawing Goals
| One-page Version of this Chapter for Easy Printing

This chapter describes the commands used to draw things on a display. the Graphics Environment chapter, explains how to set up the display necessary to use these commands.

You must have read the previous chapter; in particular, you should be familiar with GStates. To use certain commands, you should also be familiar with the GEOS fixed point number formats.

1 Drawing Goals
2 Shapes
2.1 Dots
2.2 Lines
2.3 Rectangles
2.4 Ellipses
2.5 Elliptical Arcs
2.6 Three-Point Arcs
2.7 Rounded Rectangles
2.8 Polylines and Polygons
2.9 Bézier Curves and Splines
2.10 Drawing Bitmaps
2.11 Paths
2.12 Regions
2.13 Text
3 Shape Attributes
3.1 Color
3.2 Patterns and Hatching
3.3 Mix Mode
3.4 Masks
3.5 Line-Specific Attributes

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Drawing Goals
| One-page Version of this Chapter for Easy Printing


Drawing Goals

Drawing Graphics: 1 Drawing Goals

| Up: GEOS SDK TechDocs | Up | Prev: Drawing Graphics | Next: 2 Shapes

The graphics system has a set of graphic commands capable of describing anything that might ever be drawn; at the same time, the command set should not be overwhelming. Common routines must be heavily optimized: if the line or text drawing routines are slow, the system will be slowed by a similar amount. Commands for drawing the various shapes must exist for all the contexts discussed in the previous chapter. In GEOS, you use the same graphics commands whether you are drawing to a display, describing a GString (which can be sent to a printer), drawing to a bitmap, or describing a path. This simplifies graphics programming a great deal.

| Up: GEOS SDK TechDocs | Up | Prev: Drawing Graphics | Next: 2 Shapes


Shapes

Drawing Graphics: 2 Shapes

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Drawing Goals | Next: 3 Shape Attributes

Depending on how much experience you have with the GEOS graphics system, you may have some idea already about what sorts of shapes can be drawn and the ways to draw them. Most of these commands have names like GrDrawShape() or GrFillShape() (e.g. GrDrawRect() , GrFillEllipse() ). Normally, a command of the form GrDrawShape() draws the outline of a shape, while GrFillShape() fills in the interior of the shape. Commands with names like GrSetAttribute() (e.g. GrSetAreaColor() ) change the color, fill pattern, and other attributes of shapes to be drawn. Most of these commands are passed a GState. The drawing commands are also passed coordinates at which to draw.

For many of these commands, there are GString opcodes which represent the command in a GString. Also, the arguments used when drawing these shapes often correspond to instance data specific to the Graphic Object which draws that shape.

Most of these routines work with standard coordinates, measured in typographer's points. For geodes that need to make drawings which are precise to a fraction of a point, four routines have been set up to use WWFixed coordinates, and are thus accurate to a fraction of a point. These routines are GrDrawRelLineTo() , GrRelMoveTo() , GrDrawRelCurveTo() , and GrDrawRelArc3PointTo() . Geodes may use these routines to draw the outline of any conceivable two-dimensional shape. To create a precise, filled shape, use these routines to describe a path and then fill the path.

Dots

Lines

Rectangles

Ellipses

Elliptical Arcs

Three-Point Arcs

Rounded Rectangles

Polylines and Polygons

Bézier Curves and Splines

Drawing Bitmaps

Paths

Regions

Text

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Drawing Goals | Next: 3 Shape Attributes


Dots

Drawing Graphics: 2.1 Shapes: Dots

| Up: GEOS SDK TechDocs | Up | Prev: 2 Shapes | Next: 2.2 Lines
GrDrawPoint(), GrDrawPointAtCP()

A point is the smallest thing drawable; it will always appear as a single pixel. The point's position is defined by a standard coordinate pair. Points are drawn using line attributes. After drawing a point, the pen position will be at the point's coordinates.

GrDrawPoint() draws a point at the passed coordinate pair. GrDrawPointAtCP() draws a point at the current pen position.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Shapes | Next: 2.2 Lines


Lines

Drawing Graphics: 2.2 Shapes: Lines

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Dots | Next: 2.3 Rectangles
GrDrawLine(), GrDrawLineTo(), GrDrawRelLineTo(), GrDrawHLine(), GrDrawHLineTo(), GrDrawVLine(), GrDrawVLineTo()

A line is simply a line segment that connects two points. Lines are drawn using the current line attributes stored with the GState. The new pen position becomes the last passed coordinate pair.

GrDrawLine() draws a line between points. GrDrawLineTo() draws a line from the current pen position to the passed point. GrDrawHLine() draws a horizontal line between two points. These two points share the same y coordinate (thus the line is horizontal). GrDrawHLineTo() draws a horizontal line from the current pen position to a passed x coordinate. GrDrawVLine() draws a vertical line between two points which share a common x coordinate. GrDrawVLineTo() draws a vertical line from the current pen position to a passed y coordinate. The GrDrawH...() and GrDrawV...() routines save space compared to GrDrawLine() or GrDrawLineTo() , since fewer coordinates are necessary to define the line.

GrDrawRelLineTo() draws a line from the current pen position to the point at the specified x and y offset from the starting position. This routine takes very precise coordinates, and is useful for describing paths.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Dots | Next: 2.3 Rectangles


Rectangles

Drawing Graphics: 2.3 Shapes: Rectangles

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Lines | Next: 2.4 Ellipses
GrDrawRect(), GrDrawRectTo(), GrFillRect(), GrFillRectTo()

Rectangles are defined by four coordinates which can be thought of as either defining the rectangle's left, top, right, and bottom bounds or as specifying two opposite corners of the rectangle.

GrDrawRect() draws the outline of a rectangle using the passed coordinates. GrDrawRectTo() draws the outline of a rectangle using the current pen position as one of the defining points; the pen position is unchanged by this operation. These functions draw the rectangle outline using the current line attributes. GrFillRect() draws a filled rectangle defined by the passed coordinates. GrFillRectTo() fills a rectangle of which the pen position is one corner. GrFillRect() and GrFillRectTo() use the GState's area attributes. They do not draw a border around the rectangle; if you want a bordered rectangle, call GrFillRect() and follow it with GrDrawRect() . Note that if the order of these operations if reversed, the fill may obscure the draw.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Lines | Next: 2.4 Ellipses


Ellipses

Drawing Graphics: 2.4 Shapes: Ellipses

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Rectangles | Next: 2.5 Elliptical Arcs
GrDrawEllipse(), GrFillEllipse()

Ellipses are defined by their bounding rectangles. The pen position becomes the first coordinate pair passed. Circles are ellipses with heights equal to their widths.

GrDrawEllipse() draws the outline of an ellipse using the current line drawing attributes. GrFillEllipse() fills the ellipse's area using the current area attributes.

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Rectangles | Next: 2.5 Elliptical Arcs


Elliptical Arcs

Drawing Graphics: 2.5 Shapes: Elliptical Arcs

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Ellipses | Next: 2.6 Three-Point Arcs
GrDrawArc(), GrFillArc()

An arc is a partial ellipse. An arc is defined in terms of its base ellipse, the angle at which to start drawing the arc, and the angle at which to stop drawing. Angles are counted in degrees counter-clockwise with 0° corresponding to the positive x axis (i.e., "3 o'clock").

GrDrawArc() draws the outline of an elliptical arc, a curved line. It does so using the GState's current line attributes. GrFillArc() fills the arc. There are two ways to fill an arc: you can fill in the wedge described by the arc, or you can fill just the region between the arc and its chord; set the style with an ArcCloseType value.

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Ellipses | Next: 2.6 Three-Point Arcs


Three-Point Arcs

Drawing Graphics: 2.6 Shapes: Three-Point Arcs

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 Elliptical Arcs | Next: 2.7 Rounded Rectangles
GrDrawArc3Point(), GrDrawArc3PointTo(), GrFillArc3Point(), GrFillArc3PointTo(), GrDrawRelArc3PointTo()

The graphics system allows another way to specify arcs. Given two endpoints and one arbitrary point, there is a unique circular arc which has those endpoints and passes through that arbitrary point. The closer the arbitrary point is to the line connecting the endpoints, the shallower the described arc.

GrDrawArc3Point() draws the three-point arc corresponding to the passed points. The second endpoint passed becomes the new pen position. GrDrawArc3PointTo() draws a three-point arc using the present pen position as one of the endpoints; the other endpoint becomes the new pen position. GrFillArc3Point() fills a three-point arc. GrFillArc3PointTo() fills an arc that has the present pen position as an endpoint.

The GrDrawRelArc3PointTo() routine draws a three-point arc where the pen position is the first point and the other two points are specified as offsets from that position. This routine takes WWFixed coordinates for precision drawing.

One time when programmers might especially want to use three point arcs is in the construction of paths. An important consideration when constructing paths is making sure that the various segments of the path are connected; that is, that they share endpoints. When specifying elliptical arcs, the endpoints are never explicitly defined. Thus, it is ambiguous in some cases whether an arc is supposed to be connected to something else. Because three-point arcs include their endpoints with their definition, there is no such ambiguity.

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 Elliptical Arcs | Next: 2.7 Rounded Rectangles


Rounded Rectangles

Drawing Graphics: 2.7 Shapes: Rounded Rectangles

| Up: GEOS SDK TechDocs | Up | Prev: 2.6 Three-Point Arcs | Next: 2.8 Polylines and Polygons
GrDrawRoundRect(), GrDrawRoundRectTo(), GrFillRoundRect(), GrFillRoundRectTo()

Rounded rectangles are defined in terms of their bounding rectangle and the radius of the circle used to compute the rounded corners. The smaller the corner circle is, the sharper the rectangle's corners will be. A larger corner circle results in a more rounded corner with more area taken away.

GrDrawRoundRect() draws the outline of a rounded rectangle with the passed bounding rectangle and corner circle dimensions. The drawing position is set to the first passed coordinate pair. GrDrawRoundRectTo() draws the outline of the rounded rectangle for which the current position is one corner of the bounding rectangle. The current position is unaffected by this operation. GrDrawRoundRect() and GrDrawRoundRectTo() use the current line drawing attributes. GrFillRoundRect() fills a rounded rectangle with passed bounding rectangle and corner radius using the current area attributes. GrFillRoundRectTo() fills a rounded rectangle that has the current position as one corner of the bounding rectangle.

| Up: GEOS SDK TechDocs | Up | Prev: 2.6 Three-Point Arcs | Next: 2.8 Polylines and Polygons


Polylines and Polygons

Drawing Graphics: 2.8 Shapes: Polylines and Polygons

| Up: GEOS SDK TechDocs | Up | Prev: 2.7 Rounded Rectangles | Next: 2.9 Bézier Curves and Splines
GrDrawPolyline(), GrDrawPolygon(), GrFillPolygon(), GrBrushPolyline(), GrTestPointInPolygon()

Polylines and polygons are drawings made up of chains of connected lines. They are defined as lists of points, or corners. After drawing a polyline or polygon, the pen position will be at the last point of the shape.

GrDrawPolyline() draws a polyline using the current line attributes. GrDrawPolygon() draws a polygon using the current line attributes.

To draw a closed figure with GrDrawPolyline() , the first and last point must have the same coordinates. GrDrawPolygon() draws figures as closed automatically; having the same beginning and ending point is unnecessary.

GrFillPolygon() fills the interior of a polygon. It does so using the current area attributes. It also uses a fill rule, describing how the polygon should be filled. The odd/even fill rule decides whether a point is within a polygon by seeing how many times a ray drawn from the point to the exterior of the polygon crosses an edge of that polygon. If the result is odd, the point is considered to be in the interior and is filled. If the result is even, the point is outside the polygon and is left alone. The winding rule works in a similar manner, but whenever the ray crosses an edge, the rule looks at the direction of that edge: if it crosses the ray left to right, an accumulator is incremented; if it crosses the ray right to left, the accumulator is decremented. Those points for which the ray's accumulator is non-zero are considered inside the region.

GrBrushPolyline() is an optimized routine. It provides a fast, simple way to draw thick polylines. Instead of specifying a line width in points, the caller passes the dimensions of a rectangle of pixels. This rectangle will be used as a sort of brush, dragged along the course of the polyline. The result is a polyline drawn very quickly. However, this function is not display-independent, and is therefore not WYSIWYG. Because the rectangle dimensions are specified in pixels instead of points, the polyline will be thicker or thinner depending on the display's resolution. In the system software, this routine provides the "ink" feedback for pen-based systems, where speed is a top priority.

To find out whether a given point falls within a polygon, call GrTestPointInPolygon() .

| Up: GEOS SDK TechDocs | Up | Prev: 2.7 Rounded Rectangles | Next: 2.9 Bézier Curves and Splines


Bézier Curves and Splines

Drawing Graphics: 2.9 Shapes: Bézier Curves and Splines

| Up: GEOS SDK TechDocs | Up | Prev: 2.8 Polylines and Polygons | Next: 2.10 Drawing Bitmaps
GrDrawCurve(), GrDrawCurveTo(), GrDrawSpline(), GrDrawSplineTo(), GrDrawRelCurveTo()

Bézier curves are mathematical constructs which provide a cheap and easy way to define smooth curves in a manner that computers can understand. There are other ways to define curves to computers, but the Bézier was chosen for the GEOS kernel because it is used in many standard font description formats. Splines, as implemented in GEOS, are created by drawing curves in sequence.

Bézier curves are defined in terms of four points. Two of these points are the endpoints, known as anchor points. The other two points are known as control points, one associated with each anchor point. The curve extends from anchor point to anchor point. The line between an anchor point and its control point determines the slope, or derivative, of the curve at the anchor point. The further the control point is from the anchor point, the further the curve wants to go along the straight line before curving off towards the other anchor point. A control point at zero distance from its anchor point won't affect the curve; if both control points are at zero distance from their anchors, the result will be a straight line segment.

GrDrawCurve() draws a Bézier curve. It takes four points as arguments, using the first and last as anchor points and the middle two as control points. GrDrawCurveTo() draws a curve but uses the current pen position as the first anchor point, setting the pen position to the second anchor point after drawing.

It would be possible to draw splines by drawing a number of curves which had common endpoints, but the graphics system provides the GrDrawSpline() routine by which a spline with an arbitrary number of spline segments may be drawn with one call. GrDrawSplineTo() draws a spline with the current position as the first anchor point. The spline drawing routines require the application to set up an array of points. When calling GrDrawSpline() , these points should be in the order: anchor, control, control, anchor, control, control,..., anchor. The total number of points should be equal to 3n+1, where n is equal to the number of spline segments. Since GrDrawSplineTo() uses the current position as the first anchor point, for this function the array should start with the first control point, and there should be 3n points passed.

For most programmers, that's probably enough to know. Those programmers who want to know more and don't mind a bit of math may feel free to continue this section.

A curve is defined in terms of four points. There is a formula to determine the coordinates of all points on a spline in terms of these four points. The formula uses two parameterized cubic equations. These equations determine the x and y coordinates of a point on the curve. By finding the points corresponding to various parameters, it is possible to approximate the spline as closely as necessary. See below for the equations.

Splines may be created by drawing curves which share endpoints. Given an anchor point which two curves of a spline share, if the control point of one lies in the exact opposite direction of the other control point, the resulting spline will be smooth. If the control points are not only in the exact opposite directions but are also the same distance from the anchor point, then not only will the resulting spline be smooth, but its derivative will be smooth as well.

We call smooth splines with smooth derivatives "very smooth," and this condition is analogous to C' continuity in functions. Smooth splines with non-smooth derivatives are called "semi smooth", analogous to G' continuity.

| Up: GEOS SDK TechDocs | Up | Prev: 2.8 Polylines and Polygons | Next: 2.10 Drawing Bitmaps


Drawing Bitmaps

Drawing Graphics: 2.10 Shapes: Drawing Bitmaps

| Up: GEOS SDK TechDocs | Up | Prev: 2.9 Bézier Curves and Splines | Next: 2.11 Paths
GrDrawBitmap(), GrDrawBitmapAtCP, GrFillBitmap(), GrFillBitmapAtCP(), GrDrawHugeBitmap(), GrDrawHugeBitmapAtCP(), GrDrawImage()

In this section only drawing bitmaps will be discussed. For information on creating and modifying bitmaps, see the Graphics Environment chapter.

GrDrawBitmap() draws a bitmap. This routine is very versatile. It can draw simple and complex bitmaps. It can draw compacted or uncompacted bitmaps, can use a bitmap-specific palette, handles strange resolutions intelligently, and generally does the right thing. If you're working with a large bitmap and want to manage its storage, you may provide a routine to pass in part of the bitmap at a time. If the bitmap is stored in a huge array (true of all bitmaps created with GrCreateBitmap() ) use GrDrawHugeBitmap() instead of GrDrawBitmap() , and it will manage memory for you. GrDrawBitmapAtCP() draws a bitmap at the current position.

If you just want to draw a monochrome bitmap, consider using the GrFillBitmap() command. This routine treats the bitmap like a mask, coloring the "on" pixels with the present area color, and leaving the "off" pixels alone so that whatever is underneath the bitmap can show through. This routine is heavily optimized and very fast. GrFillBitmapAtCP() works the same way, filling the bitmap at the current position.

Use GrDrawHugeBitmap() to draw a bitmap that has been stored in a HugeArray data structure. Remember that any bitmaps created by GrCreateBitmap() are stored in a HugeArray . GrDrawHugeBitmap() will automatically take care of memory management. GrDrawHugeBitmapAtCP() works the same way, drawing the bitmap at the current position. GrFillHugeBitmap() and GrFillHugeBitmapAtCP() fill huge bitmaps.

GrDrawImage() is less adaptable but faster than GrDrawBitmap() . GrDrawImage() has its own kind of scaling which doesn't work in the standard GEOS fashion. This routine ignores the resolutions of both device and bitmap and displays the bitmap so that each pixel of the bitmap corresponds to one pixel of the display. If the coordinate system has been scaled or rotated, GrDrawImage() will ignore the scale and rotation when drawing the bitmap. The bitmap may be magnified, but this is not quite the same as normal scaling: The bitmap's resolution is still ignored, but each pixel of the bitmap will receive a square of pixels on the display. GrDrawHugeImage() draws the image of a bitmap stored in a HugeArray .

The image-drawing routines take an ImageFlags structure which holds a flag to specify whether borders should be drawn between the pixels of the bitmap and a bit size field which specifies the magnification to use.

| Up: GEOS SDK TechDocs | Up | Prev: 2.9 Bézier Curves and Splines | Next: 2.11 Paths


Paths

Drawing Graphics: 2.11 Shapes: Paths

| Up: GEOS SDK TechDocs | Up | Prev: 2.10 Drawing Bitmaps | Next: 2.12 Regions
GrDrawPath(), GrFillPath()

GrDrawPath() draws a path using the current line attributes. GrFillPath() fills a path using the current area attributes. GrFillPath() can fill the path using either an odd/even or winding fill rule. If the path is to be filled using the winding fill rule, the path must have been defined so that the segments forming the border of each region go around the region so that the interior of the region is to the left. That is, on convex parts of the border, edges should be in the counterclockwise direction. On concave parts of the border edges should go clockwise. For an illustration of a path following this rule, see the figure above. The fill rule is specified by means of a RegionFillRule value, which may be one of ODD_EVEN or WINDING.

When you define a path by combining two other paths, the result might not be exactly what you would expect.

| Up: GEOS SDK TechDocs | Up | Prev: 2.10 Drawing Bitmaps | Next: 2.12 Regions


Regions

Drawing Graphics: 2.12 Shapes: Regions

| Up: GEOS SDK TechDocs | Up | Prev: 2.11 Paths | Next: 2.13 Text
GrGetPathRegion(), GrDrawRegion(), GrDrawRegionAtCP(), GrMoveReg(), GrGetPtrRegBounds(), GrTestPointInReg(), GrTestRectInReg()

Sometimes it's useful to be able to describe an arbitrary area of a display. Regions provide a mechanism for doing so. Regions are ideal for delimiting large, blobby areas which are relatively free of detail. They are used by the system to describe many "filled shapes."

While it is possible to define a region directly, writers familiar with paths may define a path and then call the GrGetPathRegion() routine. To find out how to define a region directly, see below.

GrDrawRegion() draws a region at the passed position using the current area drawing attributes. GrDrawRegionAtCP() draws a region at the current pen position.

GrMoveReg() changes the coordinates stored within a region data structure by the specified x and y offsets.

GrGetPtrRegBounds() returns the coordinates of the passed region's bounding rectangle.

GrTestPointInReg() sees if the passed point falls within a region. GrTestRectInReg() tests to see whether the passed rectangle falls entirely or partially within a region; it returns a value of type TestRectReturnType specifying the degree of overlap. These two functions are very useful when using regions for clipping purposes; if a given point or rectangle is discovered to be outside the clipping region, there's no need to draw the point or rectangle.

Some application writers may wish to define regions directly without describing a path. Regions are described in terms of a rectangular array (thus the similarity to bitmaps). Instead of specifying an on/off value for each pixel, however, regions assume that the region will be fairly undetailed and that the data structure can thus be treated in the manner of a sparse array. Only the cells in which the color value of a row changes are recorded. The tricky part here is keeping in mind that when figuring out whether or not a row is the same as a previous row, the system works its way up from the bottom, so that you should compare each row with the row beneath it to determine whether it needs an entry.

The easiest region to describe is the null region, which is a special case described by a single word with the value EOREGREC (a constant whose name stands for E nd Of REG ion REC ord value). Describing a non-null region requires several numbers.

The first four numbers of the region description give the bounds of the region. (You may encounter certain visual objects which use region structures to describe their bounds. These regions do not contain any bounds numbers. The object's drawing bounds will be used instead.)

Next come one or more series of numbers. Each series describes a row, specifying which pixels of that row are part of the region. The only rows which need to be described are those which are different from the row below. The first number of each row description is the row number, its y coordinate. The last number of each series is a special token, EOREGREC, which lets the kernel know that the next number of the description will be the start of another row. Between the row number and EOREGREC are the column numbers where the pixels toggle on and off. The first number after the row number corresponds to the first column in which the pixel is on; the next number is the first subsequent column in which the pixel is off; and so on.

The figure below shows a simple region, along with the numbers used to define it. Those rows which are the same as the rows beneath them have no entry in this structure. Notice that rows four through seven, being the same as row eight, have no entries.

It is possible to customize a region by taking advantage of a feature of the coordinate system. Graphics routines take word-sized (16-bit) coordinate arguments. Normal coordinates only require 15 bits. When working with regions, the graphics system uses the extra bit to allow for coordinates that are described in terms of "parameters."

When you create a region you can specify coordinates as an offset from a parameter . When the region is initialized, up to four parameters may be defined. Coordinates may then be specified as 13-bit offsets from any one of these four parameters. When drawing the construct, new values may be passed for the parameters. In this way, it is possible to use a single region to describe a variety of shapes, just by changing the parameters.

Coordinates with the values shown below will be interpreted as offsets.

4000h-5FFFh: Offsets from parameter zero, with 5000h corresponding to the parameter exactly.
6000h-7FFFh: Offsets from parameter one, with 7000h corresponding to the parameter exactly.
8000h-9FFFh: Offsets from parameter two, with 9000h corresponding to the parameter exactly.
A000h-BFFFh: Offsets from parameter three, with B000h corresponding to the parameter exactly.

Thus, if a coordinate in a GString were 5000h, and the region were drawn with parameter zero equal to 72, then that drawing would take place at the coordinate 72. The coordinate 4FFFh would be interpreted as 71. Use the following constants to clarify parameterized coordinates:

/* Constants for DrawRegion */
#define PARAM_0 0x5000
#define PARAM_1 0x7000
#define PARAM_2 0x9000
#define PARAM_3 0xb000

Some or all coordinates of a region description may incorporate parameters. Note that the region code doesn't check regions for correctness. If the bounds of a region are set incorrectly, the rows are given out of order, or an incorrect (odd) number of on/off points is given for a row, the results are undefined.

| Up: GEOS SDK TechDocs | Up | Prev: 2.11 Paths | Next: 2.13 Text


Text

Drawing Graphics: 2.13 Shapes: Text

| Up: GEOS SDK TechDocs | Up | Prev: 2.12 Regions | Next: 3 Shape Attributes

Programs normally display text with UI gadgetry such as GenText, VisText, and GenGlyph objects. For those times when a geode will display text as part of a graphical display, sometimes it's best to display text using direct calls to the graphics system.

Displaying Text

GrDrawText(), GrDrawTextAtCP(), GrDrawChar(), GrDrawCharAtCP(), GrDrawTextField()

There are several functions that display text. The GrDrawText() routine displays a text string. The passed y position, adjusted by the text mode (see below), determines the vertical position. The passed x position determines where the text will start printing, as normal.

GrDrawText() draws a text string at the desired position using the GState's current text attributes. This text string should contain no carriage returns, line feeds, tabs, or other non-printing characters. GrDrawTextAtCP() draws a text string at the current position. GrDrawChar() and GrDrawCharAtCP() draw a single character, which should not be a non-printing character. GrDrawTextField() draws a field of text--however, this routine is only available in Assembly language.

Special Text Attributes

GrGetTextStyle(), GrSetTextStyle(), GrGetTextMode(), GrSetTextMode(), GrGetTextSpacePad(), GrSetTextSpacePad(), GrGetFont(), GrSetFont(), GrGetTrackKern() GrSetTrackKern(), GrGetFontWeight(), GrSetFontWeight(), GrGetFontWidth(), GrSetFontWidth(), GrGetSuperscriptAttr(), GrSetSuperScriptAttr(), GrGetSubscriptAttr(), GrGetSubscriptAttr()

Applications can display text in a number of ways. Thus the GState has many attributes it keeps track of solely for drawing text.

Text style is a collective set of attributes (bold, italic, etc.) that affects how the text is drawn by the graphics system. GrGetTextStyle() gets the current text style, and GrSetTextStyle() allows a new style to be specified. Styles are expressed as a TextStyle record. Note that some complicated styles which are offered by the text objects are not available here: these styles are available only from the text objects; if you wish to offer these styles without using a text object, you'll have to do the artwork yourself.

Depending on the text mode attribute, text may either be drawn from the bottom of the font box, top of the font box, baseline, or accent line. GrGetTextMode() gets the text mode, returning information about which offset to use when drawing text. GrSetTextMode() allows this information to be reset. The information is stored in a TextMode record. Note that if you will be drawing characters of more than one size or font, and if you want those characters to line up by baseline, you should use GrSetTextMode() to use the TM_DRAW_BASE text mode.

GrSetTextSpacePad()

 sets the special amount used to pad space characters;

GrGetTextSpacePad()
 retrieves the current space padding.

GrGetFont() returns the current font and type size. The font is identified by its FontID ; the default font has the ID DEFAULT_FONT_ID and size DEFAULT_FONT_SIZE ; these are the values which new GStates will start with. GrSetFont() sets a new font to use. The font's point size may be between MIN_POINT_SIZE and MAX_POINT_SIZE .

Track kerning adjusts the space between characters. A negative kerning value means that characters will be drawn closer together. A large negative kerning value can make characters draw overlapped. A positive kerning value causes characters to draw with more space between them. GrGetTrackKern() returns the present track kerning value. GrSetTrackKern() changes this value. The kerning value must be between MIN_TRACK_KERNING and MAX_TRACK_KERNING; values greater than MAX_TRACK_KERNING will be replaced by MAX_TRACK_KERNING, values less that MIN_TRACK_KERNING will result in MIN_TRACK_KERNING being used. The kerning value will be multiplied as a percentage by the font size to get a number of points to use for kerning; if this multiplied value is greater than the BBFixed (byte-byte fixed point) number MAX_KERN_VALUE or less than MIN_KERN_VALUE then it will be adjusted to fall at the end of this range.

A font's weight determines its boldness. For many fonts, there will be only two weights defined: plain and bold. However, some fonts allow for finer control of weight. To find out the current font weight, call GrGetFontWeight() . To use a different font weight, call GrSetFontWeight() . Some useful weight values are stored in the FontWeight enumerated type. The weight should definitely be between FW_MINIMUM and FW_MAXIMUM.

To make characters draw wider or narrower, adjust the font width. Some fonts come in wide or narrow versions. If the font does not support the requested width, GEOS will simulate the width as best it can. The GrGetFontWidth() routine retrieves the current width; GrSetFontWidth() changes it. Some helpful width values are stored in the FontWidth enumerated type. The width should definitely be between FWI_MINIMUM and FWI_MAXIMUM.

Geodes can control how text will be drawn in superscript and subscript styles. The super- and subscript attributes determine how to scale the characters and how far they should be displaced. There are several standard super- and subscript attributes available, including values for footnote numbers, and chemical inferiors. Typesetting enthusiasts who wish to adjust the width of these characters differently than the height (as in some standard super- and sub- script layouts) should work with the font width. Use GrGetSuperscriptAttr() and GrGetSubscriptAttr() to find out what the present values are. Use GrSetSuperscriptAttr() and GrSetSubscriptAttr() to change these values. Each of these routines works with a word sized value: the top byte is a percentage of the font size to offset sub- or superscript characters; the low byte is the percentage of font size to use for the sub- or superscript character 0x0064 (decimal 100) would be full-sized with no displacement.

Accessing Available Fonts

GrEnumFonts(), GrCheckFontAvail(), GrFindNearestPointsize(), GrGetDefFontID(), GrGetFontName()

To find out which fonts are available in the user's environment, use the GrEnumFonts() command. You specify what sorts of fonts you're interested in by setting a number of flags, and the routine will fill a buffer with the available fonts with their FontID s and names.

The FontEnumFlags record determines which fonts will be returned. At least one of the FEF_OUTLINES and FEF_BITMAPS flags must be set to determine whether outline, bitmap, or both kinds of fonts should be returned.Keep in mind that only outline fonts will result in true WYSIWYG printer output. The FEF_ALPHABETIZE flag will force the returned buffer of fonts to appear in lexical order. The FEF_DOWNCASE flag requests that the font names appear all in lowercase.

The FEF_FAMILY flag asks that the search be limited to a font family. To specify what sort of family you want, use the FontFamily enumerated type.

GrEnumFonts() ignores the FEF__STRING flag; other routines will use this flag to find out whether the font is specified by a FontID or its ASCII name.

The other flags narrow the search: if you set the FEF_FIXED_WIDTH flag, then only fixed-width fonts will be returned. If you set the FEF_USEFUL flag, only those fonts marked as "useful" will be returned.

The font information will be returned as an array of FontEnumStruct structures. Each of these structures will contain a FontID and the ASCII name of the associated font.

There may be up to MAX_FONTS available on the system. If you're not prepared to handle an array large enough to hold this many fonts, be sure to pass an argument specifying how large an array is being provided.

To find out if a given font is available in the user's environment, call either GrCheckFontAvailID() or GrCheckFontAvailName() , depending on whether the font is being identified by its FontID or its ASCII name. If programming in assembly language, use the GrCheckFontAvail() routine no matter how you're identifying the font. You may narrow the search by passing the appropriate FontEnumFlags . Make sure that the FEF_STRING bit is clear when searching by ID and set when searching by name.

Some fonts are available only in certain sizes and styles. Bitmap fonts are examples of this. The GrFindNearestPointsize() routine takes a typeface, size, and style set. It returns the closest available size and style (or returns FID_INVALID if the passed font isn't available).

To get the font which the Generic UI will use by default, call GrGetDefFontID() . This also returns the font size to use, useful if you wish to respect the user's choice of type size.

To find out the ASCII name of a string for which you have the FontID , call GrGetFontName() . If the font isn't available, this function will return false ; otherwise it will fill a passed buffer with the name of the font. The passed buffer should be FID_NAME_LEN bytes long.

Text Metrics

GrTextWidth(), GrTextWidthWWFixed(), GrTextWidthWBFixed(), GrCharWidth(), GrFontMetrics(), GrCharMetrics()

From time to time it may become important to know something about the size of a letter to be drawn. The graphics system has several routines for retrieving this information. GrTextWidth() returns the width, in points, of a string. GrTextWidthWWFixed() does the same thing, but returns a more accurate figure, including a fractional part; note that in assembly, this routine is called GrTextWidthWBFixed() . GrCharWidth() returns the width of a single character. Note that the width of a text string will normally be different than the sum of the widths of the component characters. GrTextWidth() takes track kerning and pairwise kerning into account.

GrFontMetrics() returns information pertaining to a font. The meanings of these metrics are listed below:

Height
The height of the "font box." This is how much vertical space should be allocated for a line of text. Note that when the text is drawn, some characters may go beyond these bounds. Though the height is still the vertical distance to set aside for a line of text, applications should be prepared for characters to have a vertical height equal to the maximum adjusted height, discussed below.
Maximum Adjusted Height
The maximum height required to draw the character. Some fonts contain characters that are meant to go beyond the normal vertical boundaries, perhaps extending into the space occupied by text on the next or previous line. This metric is the minimum height to guarantee being able to display such a character.
Above Box
Normally the top of the font box, the uppermost limit of a font, is 3/4 of the font's height above the baseline. For those fonts which do not follow this rule, the Above Box metric is the number of points by which the true font box extends beyond the normal font box.
Below Box
Normally, the bottom of the font box, the bottommost limit of a font, is 1/4 of the font's height below the baseline. For those fonts which do not follow this rule, the Below Box metric is the number of points by which the true font box extends below the normal font box.
Mean
The height of a typical lower case character. This metric is sometimes called the "x height," since this will be the height of a lower case "x."
Descent
How far descending characters (like "y" and "j") extend below the baseline.
Baseline
The vertical position of the bottoms of nondescending characters. The number referred to as the "baseline" is the distance between the baseline and the top of accent marks. This corresponds to the text mode TM_DRAW_BASE.
Accent
How much space to leave for accent marks. This distance is measured from the top of the accent mark area to the ascent height. This corresponds to the text mode TM_DRAW_ACCENT.
Ascent
Height of capital letters (and some tall lower-case letters, such as "f").
Underline Position
The distance from the top of the accent line to the top of the underline.
Underline Thickness
The width of the underline.
Strikethrough Position
The vertical position of the line used for "strikethrough" text.
Average Width
The average character width. This figure is normally computed as a weighted average width of lower-case characters, said average weighted based on the frequency distribution of the characters.
Maximum Width
Width of the font's widest character.
Leading
The height of the vertical gap between lines of text.
Kern Count
The number of specially kerned pairs in the font. Many fonts allow certain pairs of characters to have special spacing, known as "pair kerning." For example many fonts try to squish "To" so that the "T" and "o" are closer together than normal leading would dictate. Do not confuse kerned pairs with ligatures. A kerned pair just has strange spacing (e.g. "To" vs. "T o"); a ligature is a pair of characters which have been combined into one (e.g. "æ").
Driver
The font driver associated with the font, specified by a member of the FontMaker enumerated type.
First Character
The Char value of the first defined, drawable character.
Last Character
The Char value of the last defined, drawable character.
Default Character
The Char value of the drawable character which will be drawn as a substitute for any undrawable characters.

The GrCharMetrics() routine returns useful measurements for a character. For each of a font's characters, you may request the following information:

Width
While not a metric returned by the GrCharMetrics() routine, the character width is very important. It determines how much space to allow for the character. The metrics that follow don't affect how much space to allow; instead, they give the bounds of the area that will be drawn to. As shown, characters are free to draw beyond the area set aside for them by the width, into the space of the previous or next character.
Minimum X
The character's leftmost bound. If this value is positive, it means that the character will have some extra space before it. If the value is negative, the character may infringe on the space of the previous character. This metric is also called the "left side bearing."
Minimum Y
The character's bottommost bound. This is the character's "descent," the amount it hangs below the baseline.
Maximum X
The character's rightmost drawing bound. This value should be greater than the Minimum X. Do not confuse this metric with the character's width.
Maximum Y
The character's topmost bound. This is the distance the character extends above the baseline. It is sometimes called the character's "ascent."

The GrGetTextBounds() routine computes the drawing bounds for a string of text.

| Up: GEOS SDK TechDocs | Up | Prev: 2.12 Regions | Next: 3 Shape Attributes


Shape Attributes

Drawing Graphics: 3 Shape Attributes

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Shapes | Next: 3.1 Color
GrSetAreaAttr(), GrSetLineAttr(), GrSetTextAttr()

The graphics system keeps track of many attributes which affect how graphics will be drawn. By setting attributes in the graphics state, your geode can change the color, fill pattern, and other features that won't affect the shape of what you're drawing but will affect its appearance.

The following sections will explain how each of these attributes may be set individually. Note that if you wish to set all attributes to use when filling areas at once, call GrSetAreaAttr() . Call GrSetLineAttr() to set all attributes to use when drawing lines. Use GrSetTextAttr() to set all text-rendering attributes.

Color

Patterns and Hatching

Mix Mode

Masks

Line-Specific Attributes

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Shapes | Next: 3.1 Color


Color

Drawing Graphics: 3.1 Shape Attributes: Color

| Up: GEOS SDK TechDocs | Up | Prev: 3 Shape Attributes | Next: 3.2 Patterns and Hatching

If your geode displays graphics, you can probably make its graphical display more effective by using color. Your geode can have as much control over color as it wants, ranging from saying what colors certain objects should be to choosing which colors should be available.

Most color controllers work with the concept of a palette, a subset of the colors your video device is capable of displaying. Most controllers can only use some of these colors at a time; these colors make up the palette. You can refer to colors by their color index (i.e. their place in the palette). You can also refer to colors by their RGB values.

An RGB value specifies a color by mixing intensities of red, green, and blue. The more of each component color added, the more of that color will show up in the result. If all components are equal, the result will be a shade of gray. If all components are zero, the result will be black. If all components are at the maximum (255), the result will be white.

The data structure used to store color information is known as the ColorQuad , shown in Color Data Structures .

Code Display 24-1 Color Data Structures

typedef struct {
    /* The ColorQuad data structure is used to represents a color. There are
     * many ways to describe a color. The CQ_info field determines how the
     * color is being specified, and the other fields will be interpreted
     * based on CQ_info's value. Colors may be referenced by palette index,
     * RGB value, or grey scale value. */
        /* CQ_redOrIndex
         * If CQ_info is CF_INDEX, then this is the palette index.
         * If CQ_info is CF_RGB, then this is the Red component.
         * If CQ_info is CF_GRAY, then this is the Gray scale */
    byte 	CQ_redOrIndex;
        /* CQ_info:
         * This ColorFlag determines how the other three fields of the
         * ColorQuad will be interpreted. The ColorFlag type is shown
         * below. */
    ColorFlag 	CQ_info;
        /* CQ_green:
         * If CF_RGB, then these fields are the Green and Blue components.
         * Otherwise, these fields are ignored. */
    byte 	CQ_green;
    byte 	CQ_blue;
} ColorQuad;
 typedef dword ColorQuadAsDWord;
    typedef ByteEnum 	ColorFlag;
    CF_INDEX,
        /* Color specified by palette index. The values of the first
         * 16 entries of the system palette are specified by Color values */
    CF_GRAY,
        /* Color specified by gray value; this is like CF_RGB, but the
         * value in CQ_redOrIndex will be used for the Green and Blue
         * fields as well. */
    CF_SAME,
        /* Used with hatch patterns, if this flag is set, hatches will draw
         * using the default color (the one set using GrSetXXXXColor()) */
    CF_RGB
        /* Color Set using RGB values */
/* Sample Colors:
 * To use the system palette's light green:						{ C_LIGHT_GREEN, CF_INDEX, 0, 0}
 * To use the 40th color in the palette:						{ 40, CF_INDEX, 0, 0}
 * To use a custom brown:						{ 150, CF_RGB, 90, 0}
 * To use a 75% Gray (75% *256 = 192):						{ 192, CF_GRAY, 0, 0}
 */

Using Available Colors

GrGetAreaColor(), GrSetAreaColor(), GrGetLineColor(), GrSetLineColor(), GrGetTextColor(), GrSetTextColor()

If a geode is to draw something in color, it first calls a routine to set which color to use, then calls the appropriate drawing routine. The color can be specified in terms of its palette index or its RGB value. The data specifying which color to use is stored in the GState, with separate color fields to keep track of how to draw lines, areas, and text.

GrSetLineColor() changes the color used when drawing lines. The GrSetAreaColor() routine sets the color to be used when filling areas. GrSetTextColor() sets the color to be used when rendering text. GrGetLineColor() , GrGetAreaColor() , and GrGetTextColor() return the color values and flags stored with the GState.

The default system palette includes several colors whose indexes have been set up so they may be referenced by descriptive constant names. The constants are members of the enumerated type Color , the most common shown in the figure above. Thus, instead of having to remember that the index 02 means green, the constant C_GREEN can be passed to the appropriate color setting command.

Other Color values include a 16-shade gray scale (the C_GRAY_...entries), some "unused" entries (the C_UNUSED_... entries), and a number of entries which have been set up to allow you to specify a color by its red, green, and blue components on a zero-to-five scale (the C_R..._G..._B... entries).

Programmers should use care when using these constants in conjunction with palette manipulation, as it is possible to change the RGB color value associated with a palette entry. Since the constant names are associated with palette indexes instead of RGB values, it is possible to change the RGB value of palette entry 02 so that the C_GREEN constant actually refers to, for example, a shade of magenta.

When the Color Isn't in the Palette

GrSetLineColorMap(), GrSetAreaColorMap(), GrSetTextColorMap(), GrGetLineColorMap(), GrGetAreaColorMap(), GrGetTextColorMap(), GrMapColorIndex(), GrMapColorRGB()

Not all users have video devices which can display 256 colors at a time. Even fewer have printers capable of doing so. Therefore, the graphics system makes allowances for drawings which use unavailable colors. Exactly what the system will do with an unavailable color depends on the color mapping mode used. By default, the system will choose the two (or more) closest available colors and dither them together in an attempt to form a mixture which will appear to be the correct color overall when seen from a distance.

GrSetLineColorMap() , GrSetAreaColorMap() , and GrSetTextColorMap() set the color mapping to use when drawing with an unavailable color. GrGetLineColorMap() , GrGetAreaColorMap() , and GrGetTextColorMap() return the current color mapping modes. The geode may draw either in the closest available color, or using a dithering of the closest colors. Often it's a good idea to have text map to solid colors since its detailed nature doesn't go too well with dithering.

When a geode wants to learn more about which colors are available, it can use the GrMapColorIndex() routine to find the RGB value associated with an index. The GrMapColorRGB() routine returns the index and true RGB color of the palette entry that most closely matches the values passed.

Custom Palettes

GrCreatePalette(), GrDestroyPalette(), GrSetPaletteEntry(), GrSetPalette(), GrGetPalette()

If the system default palette does not meet a geode's needs, the geode can change its own palette. Some bitmaps (such as those in GIF) have palettes associated with them, and a geode displaying such bitmaps might wish to display the bitmap in its true colors without settling for the closest defaults. Specialized tasks such as photo processing, anti-aliasing, and color cycle animation tend to depend on the ability to manipulate palettes. A geode can create a custom palette associated with a window, then change the color values of entries in that palette.

Since the palette is associated with a window, there can be as many custom palettes as there are windows. The video driver uses the palette associated with the active window. This means that all the inactive windows (and the background) will also be drawn with the palette of the active window; thus, if you give a window a special palette, that may make inactive windows look different. To avoid this, try to avoid changing palette entries 0-15, as the UI uses these (These are the entries representing C_RED, etc.).

GrCreatePalette() creates a custom palette and associates it with your graphic state's associated window. The custom palette starts with all entries having their original RGB values from the default palette. GrSetPaletteEntry() takes a palette entry and sets it to a new RGB value. GrSetPalette() allows you to specify new values for several palette entries at a time. GrSetPalette() can also set an entry back to its default value. GrDestroyPalette() destroys the custom palette, freeing the memory used for it.

Given a choice of palette entries to change, you might choose one of the C_UNUSED_... entries.

Call GrGetPalette() to get a handle to a memory block containing the palette data. Depending on the GetPalType passed, you will either get the palette information for your current palette or the default palette. This routine returns the handle of a memory block. The memory block contains an array with 256 entries (one for each color), each entry consisting of three bytes. The first byte of each entry is the color's red component, the second is the color's green component, and the third is the color's blue component.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Shape Attributes | Next: 3.2 Patterns and Hatching


Patterns and Hatching

Drawing Graphics: 3.2 Shape Attributes: Patterns and Hatching

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Color | Next: 3.3 Mix Mode
GrSetAreaPattern(), GrSetAreaPatternCustom(), GrSetTextPattern(), GrSetTextPatternCustom(), GrGetAreaPattern(), GrGetTextPattern()

Fill patterns allow the application to tile an area with a repeating pattern of bits or lines helpful for suggesting textures. The graphics system supports two types of fill patterns. Bitmap patterns, familiar to most computer users, tile the filled area with a repeated bitmap. Hatch patterns fill the area with a repeated sequence of lines. Hatch patterns are defined in terms of families of parallel lines. Patterns are referenced by a PatternType and an index, stored in a GraphicPattern structure. The pattern types are

PT_SOLID
The lack of a pattern. Fills the area solid. This is the default.
PT_SYSTEM_HATCH
System-defined hatch pattern. These patterns are unchangeable and are available to all geodes.
PT_SYSTEM_BITMAP
System-defined tile bitmap pattern. These patterns are unchangeable and available to all geodes.
PT_USER_HATCH
User-defined hatch pattern. These patterns are available to all geodes.
PT_USER_BITMAP
User-defined tile bitmap pattern. These patterns are available to all geodes.
PT_CUSTOM_HATCH
Application-defined hatch pattern. These patterns are application-specific.
PT_CUSTOM_BITMAP
Application-defined tile bitmap pattern. These patterns are application-specific.

Use GrSetAreaPattern() and GrSetTextPattern() to use patterns defined outside the application (system- and user-defined patterns). To use the system's brick hatch pattern, for example, pass the PatternType PT_SYSTEM_HATCH and the SystemHatch SH_BRICK. To use a user-defined bitmap pattern, pass PatternType PT_USER_BITMAP and the number of the pattern. If you pass an invalid pattern (requesting a user hatch pattern when the user hasn't defined one, for instance), the area or text will be filled solid.

Applications may define their own patterns. Before adding custom patterns to an application, consider whether such an action is really necessary. Remember that the user may define his own patterns. The data associated with any application custom pattern may be at most 16Kbytes.

Hatch patterns are defined in terms of families of lines. The pattern designer specifies a series of families; each family consists of a set of equidistant parallel lines. Thus, by defining one family of lines, one could cover a surface with one set of parallel lines. By asking for two families, the region could be filled with a grid.

For each line family, the application must supply certain information. Your application will work with the HatchLine data type to specify

  • Origin
    Since each line family is drawn as a set of equidistant parallel lines, the origin might seem useless. Who cares where the lines start, since they fill the available space? In fact, the origin will only be meaningful if your hatch pattern contains more than one line family. The origin allows you to draw one line family at an offset from another. In the case of the example presented in the figure above, the third line family needed an origin offset from that of the first two families, since none of the lines of the third family pass through the point used as the origin by the first two families.
  • Delta offset between lines of the family
    Each family has a horizontal and vertical offset. When drawing the pattern, the first line will be drawn starting at the origin. The second line will be drawn at a perpendicular distance equal to the passed y offset. The second line may also be drawn at a parallel offset, but this will only affect dotted lines.
  • Angle at which to draw the lines
    Remember that angles are measured in degrees measured counterclockwise from the positive x axis.
  • Color
    You may draw the lines using the default color (area color if pattern is filling an area; text if rendering text), or you may draw using a specific color.
  • Dashes
    You may draw the lines using a custom dash pattern. See the Line Style attribute, below, to learn how to set up a dash pattern.

Custom bitmap patterns are defined in terms of simple bitmaps. To find out the structure of a bitmap, see the struct's documentation.

To use a custom pattern, call GrSetCustomAreaPattern() or GrSetCustomTextPattern() . Along with the usual information, you must include a pointer to a memory location which marks the beginning of some structures holding the pattern data. The commands and structures are detailed in the reference manual. For an example of some code using a custom hatch pattern, see Hatch Pattern Data .

Code Display 24-2 Hatch Pattern Data

/* This example shows how to implement the pattern illustrated in the figure above. */
    /* ... */
    GrSetPatternCustom(myGState, gp, hexHatchPatt);
    /* ... */
GraphicPattern 	gp = {PT_CUSTOM_HATCH, 0};
 static HatchPattern 	hexHatchPatt = { 3 };					/* Three HatchLine structures
                         * must follow */
static HatchLine line1 = { 			{MakeWWFixed(0) , MakeWWFixed(0)},			/* Origin */
             MakeWWFixed(0),			/* Delta X
                         *  dashes will be in alignment */
            MakeWWFixed(20.7846097),			/* Delta Y
                         *  lines will be 12*sqrt(3)
                         *  apart */
            MakeWWFixed(0),			/* Angle */
            (dword) (CF_SAME<<16),			/* Color
                         *  will use default color */
            1			/* Number of dashes
                         *  one HatchDash pattern
                         *  must follow */
        };
 static HatchDash dash1 = {			{12, 0},			/* On for 12 points */
            {24, 0}};			/* ...and Off for 24 points */
static HatchLine line2 = { 			{MakeWWFixed(0), MakeWWFixed(0)},			/* Origin */
             MakeWWFixed(0),			/* Delta X */
            MakeWWFixed(20.7846097),			/* Delta Y*/
            {120,0},			/* Angle */
            (dword) (CF_SAME<<16),			/* Color */
            1			/* Number of dashes */
        };
static HatchDash dash 2 = {			{12, 0},			/* On for 12 points */
            {24, 0}};			/* ...and Off for 24 points */
static HatchLine line3 = { 			{{12,0} , MakeWWFixed(0)},			/* Origin */
                         *  this line family will be at
                         *  a 12 pt. horizontal offset
                         *  from the other two families.
             MakeWWFixed(0),			/* Delta X */
            MakeWWFixed(20.7846097),			/* Delta Y*/
            {60,0},			/* Angle */
            (dword) (CF_SAME<<16),			/* Color */
            1			/* Number of dashes */
        };
static HatchDash dash3 = {			{12, 0},			/* On for 12 points */
            {24, 0}};			/* ...and Off for 24 points */

To find out the current area or text pattern, call GrGetAreaPattern() or GrGetTextPattern() .

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Color | Next: 3.3 Mix Mode


Mix Mode

Drawing Graphics: 3.3 Shape Attributes: Mix Mode

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Patterns and Hatching | Next: 3.4 Masks
GrGetMixMode(), GrSetMixMode()

The kernel supports several mix modes. These modes control what will happen when something is drawn on top of something else. Normally when this occurs the new drawing covers up the old one. This is known as MM_COPY, since the new drawing is copied directly onto the graphics space.

There are many other mix modes available.

MM_COPY
MM_COPY is the most common mix mode as well as the default. It draws the new drawing on top of the old as though the new drawing where on opaque paper--nothing of the old drawing shows through the region defined by the new drawing.
MM_CLEAR
The region defined by the new drawing is blanked out. The color of the new drawing does not matter.
MM_SET
The region defined by the new drawing is washed black. The color of the new drawing does not matter.
MM_INVERT
The region defined by the new drawing is inverted. The color value displayed will be the logical inverse of the old color. The color of the new drawing does not matter.
MM_NOP
The old drawing remains; nothing is drawn of the new drawing. Just about the only thing likely to change is that the pen position will be updated.
MM_XOR
The color value to display is calculated by taking the bitwise XOR of the new and old colors. If painting in white, this acts as a sort of reverse paint. Note that this bitwise XOR is applied to the index numbers of the colors, not to their RGB values.
MM_OR
The color value to display is calculated by taking the bitwise OR of the new and old colors. On a monochrome display, this corresponds to drawing on a clear plastic overlay. The OR operation is applied to the indexes of the colors as opposed to their RGB values.
MM_AND
The color value to display is calculated by taking the bitwise AND of the new and old colors. Where either drawing is blank, the result will be blank. On a monochrome display, this results in a "clipping" effect. The AND operation is applied to the indexes of the colors.

GrGetMixMode() returns the current drawing mode. GrSetMixMode() tells the system to use a new mode.

Note that the drawing modes that use logical operators to compute the color have some nice effects when used with the default system colors. Due to the values of the indexes of the gray colors, MM_OR and MM_AND used with the gray colors can be thought of as lightening and darkening operations. For instance, AND-ing C_LIGHT_GRAY together with C_LIGHT_RED color results in the darker C_RED.

When using drawing modes, it often pays to be aware of where a drawing command is drawing something as blank as opposed to where it isn't drawing at all. Filling a rectangular area of the screen with a black and white checkerboard pattern isn't exactly like drawing only the corresponding black squares. When using MM_COPY, the white areas of the checkerboard will overwrite whatever was underneath. When drawing a checkerboard with individual black squares, the background would show through no matter what background because there's nothing drawn between the squares.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Patterns and Hatching | Next: 3.4 Masks


Masks

Drawing Graphics: 3.4 Shape Attributes: Masks

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Mix Mode | Next: 3.5 Line-Specific Attributes
GrSetAreaMaskSys(), GrSetAreaMaskCustom(), GrGetAreaMask(), GrSetLineMaskSys(), GrSetLineMaskCustom(), GrGetLineMask(), GrSetTextMaskSys(), GrSetTextMaskCustom(), GrGetTextMask()

Masks are the pixellated equivalent of hatch patterns. Instead of specifying a set of dashed lines, the program uses an 8x8 bitmap array which will be tiled. The graphics system provides a number of standard mask patterns, including 65 percentage masks (for achieving, e.g., a 50% fill) and several shape patterns. It's simple to ask that the inverse of a system pattern be used instead of the pattern itself. The program may also specify and use custom draw masks by setting an 8 byte array.

The GrSetAreaMaskSys() routine specifies one of the standard draw masks to use when filling areas. GrSetAreaMaskCustom() allows the program to specify a custom mask by passing a pointer to an 8 byte. Each byte of the buffer represents one row of the mask; the rows are ordered from top to bottom. GrGetAreaMask() returns the current area mask.

There are similar routines which set the drawing mask to use when drawing lines: GrSetLineMaskSys() , GrSetLineMaskCustom() , and GrGetLineMask() . To work with the drawing mask used when rendering text, use GrSetTextMaskSys() , GrSetTextMaskCustom() , and GrGetTextMask() .

For the most part, it's probably not a good idea to use masks when drawing text, since the pixelated nature of the mask is likely to make the text less legible. However, when drawing text in very large point sizes, draw masks are probably fine.

Since several of the masks are associated with a fill percentage, some programmers may confuse masks with gray-scale fills. Grays should be implemented with colors, by drawing in a color with equal red, green, and blue values.

When you are building a document image, do not use draw masks just to achieve a lighter color. That is, if you are not making use of the "mask" property of draw masks (i.e., you don't care if you can see through it), then you should just specify a lighter color and let the kernel do the dithering. This is important especially because drawing masks in PostScript takes much longer than drawing a bitmap of the same size.

When constructing or interpreting system draw mask values, first remember that if the SDM_INVERSE flag is set, then the inverse of the mask will be used instead of the mask itself. The remaining bits should be a SystemDrawMask value, a value between zero and SDM_0. Draw mask values between zero and 24 are various graphic draw mask patterns. Values between 25 and 89 are percentage fill masks, 89 corresponding to the empty fill and 25 to a 100% fill. Several SDM_... constants have been set up with the more commonly used system draw patterns. For a list of constants, see the Routines manual.

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Mix Mode | Next: 3.5 Line-Specific Attributes


Line-Specific Attributes

Drawing Graphics: 3.5 Shape Attributes: Line-Specific Attributes

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Masks
GrGetLineWidth(), GrSetLineWidth(), GrGetLineJoin(), GrSetLineJoin(), GrGetLineEnd(), GrSetLineEnd(), GrSetMiterLimit(), GrGetLineStyle(), GrSetLineStyle(), GrSetLineAttr()

Line attributes determine characteristics of lines drawn with the GrDraw...() routines. They do not affect anything drawn with the GrFill...() routines.

You may work with the width used when drawing lines by means of the GrGetLineWidth() and GrSetLineWidth() routines.

The graphics system makes it easy to draw dotted lines. The line style attribute controls the "dottedness" with which lines should be drawn; it may be changed or retrieved with the GrSetLineStyle() and GrGetLineStyle() routines. Lines can be drawn using any of a number of standard system LineStyle s or by defining a custom dot pattern.

Custom dot patterns are defined in terms of arrays of pairs of bytes. The first byte of each pair gives the distance that the line should be drawn with the pen down, the second number the distance with the pen up. The array as a whole is stored in a DashPairArray . Dash lengths will scale with the line width.

Line joins govern the behavior of angles and corners. Using the appropriate line join style, a geode can specify that angles should be blunt, pointed, or rounded. GrGetLineJoin() returns the current line join drawing technique, while GrSetLineJoin() sets a new line join to use. The join is specified by a member of the LineJoin enumerated type. The miter limit governs the maximum length of mitererd joins; use GrSetMiterLimit() and GrGetMiterLimit() to work with this value.

The GrSetLineEnd() and GrGetLineEnd() routines set and retrieve the style with which the graphics system will draw the ends of line segments. The ending style is one of the LineEnd values. It may be round, square and even with the end of the line, or square and extending past the mathematical end of the line.

To set all of the line-drawing attributes at once, call GrSetLineAttr() .

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Masks


Kernel Functions

GEOS SDK Documentation: Kernel Functions

| Up: GEOS SDK TechDocs

The following documents hold information GEOS kernel services.

Localization
API for string manipulation routines and routines for localization/internationalization.
File System
Kernel routines to traverse and manipulate directories and files.
Graphics Environment
Kernel routines that manage graphics states, clipping, graphics coordinates.
Drawing Shapes
Kernel routines that draw shapes, lines, curves, and text with a variety of colors, patterns, and fonts.
Heap Memory
API for allocating and managing heap memory blocks.
Local Memory Functions
API for storing small pieces of information in a heap memory block.
Database Functions
API for storing many small pieces of information in a VM file.
General Change Notification
Provide a way to broadcast notification of events to applications which have registered to listen for these events. There are different kinds of events; applications choose which they want to listen for.
Geodes and Applications
Launching applications, application version numbers, application icon, INI file manipulation, timers and the clock, error-checking code, Inter-Application Communication Protocol.
Synchronizing Threads
Operations for synchronizing multi-threaded applications, including semaphores.
Handles
Low-level API for grabbing and releasing handles.
Libraries
Low-level API for the loading and freeing of libraries.
| Up: GEOS SDK TechDocs


General Change Notification

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Goals
| One-page Version of this Chapter for Easy Printing

In a multitasking environment, threads may need to know of condition changes that might affect them. In most cases where shared resources or multiple threads of execution exist, processes and objects must be sure of the integrity of data that they depend on and must be sent notice when that data changes.

In GEOS, this functionality is provided through the General Change Notification (GCN) mechanism. Although one could set up messages between processes and objects manually, the GCN mechanism eliminates the need to keep track of all processes that depend on the particular change and to keep track of all messages sent out to the various processes and objects.

1 Design Goals
2 The Mechanics of GCN
3 System Notification
3.1 Registering for System Notification
3.2 Handling System Notification
3.3 Removal from a System List
4 Application Local GCN Lists
4.1 Creating Types and Lists
4.2 Registering for Notification
4.3 Handling Application Notification
4.4 Removal from Application Lists

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Goals
| One-page Version of this Chapter for Easy Printing


Design Goals

General Change Notification: 1 Design Goals

| Up: GEOS SDK TechDocs | Up | Prev: General Change Notification | Next: 2 The Mechanics of GCN

General Change Notification allows you to keep track of both system and application events. Objects or processes interested in a particular change may request the GCN mechanism to notify them when that change occurs. That change may be system-wide (such as a file system change) or application-specific (such as a text style change within a word processor). The GCN mechanism allows objects or processes to sign up for such notification and intercept messages sent by the system (or the application) so that you may respond to different changes on a case by case basis.

The manners in which you sign up for these two types of notification differ although the functionality of the notification process is similar. The most straightforward way is to use a gcnList field in an application's application object; this is the usual approach used to add an application's GenPrimary object to the application's window list. We will assume that you know how to add an object to a GCN list in this manner, as it is shown in most if not all of the sample applications.

You can also sign up for system-wide notification through the use of certain routines and intercept system messages when the change occurs. Other objects may sign up for application-specific notification supported by GenApplicationClass . These application specific notifications should only be sent to the GenApplication object.

| Up: GEOS SDK TechDocs | Up | Prev: General Change Notification | Next: 2 The Mechanics of GCN


The Mechanics of GCN

General Change Notification: 2 The Mechanics of GCN

| Up: GEOS SDK TechDocs | Up | Prev: 1 Design Goals | Next: 3 System Notification

The basic GCN functionality manages lists of objects that are interested in specific changes. For each particular change that needs to be monitored, a separate list is needed. A completely separate "list of lists" containing an inventory of all GCN lists is also created. This will serve as the "search" table, while the particular GCN list will serve as the "messaging" list. When an event is detected, the GCN mechanism will search through the list of lists, seeing if a notification list is interested in a particular change, and send the appropriate messages if the objects do indeed wish notification of the event.

There are several reasons why you would want to use GCN:

  • Ease of use
    The GCN mechanism eliminates the need to monitor and dispatch messages relating to system changes.
  • Commonality
    The GCN mechanism provides a common platform for communication between applications in certain cases.
  • The system expects you to
    Many messages sent by the system expect a GCN mechanism to intercept them. Although you can intercept these messages manually, it is easier to take advantage of GCN's built-in functions.
| Up: GEOS SDK TechDocs | Up | Prev: 1 Design Goals | Next: 3 System Notification


System Notification

General Change Notification: 3 System Notification

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 The Mechanics of GCN | Next: 4 Application Local GCN Lists
GCNListAdd(), GCNListSend()

The system provides several lists for common system changes which might affect your application. After signing up on one of these lists (for example, the file change list) you will be notified by the kernel whenever the specified change occurs. In most cases, all you need to do is register for notification with GCNListAdd() and intercept the kernel's notification message.

The GCN mechanism performs its functions through a common series of steps. These steps are:

  1. The object registers for notification with GCNListAdd().
  2. The change occurs.
  3. The GCN mechanism is informed of the change by the acting party (in most cases this is the system itself, although a library may also send notifications). Applications do not send notifications at the system level.
  4. The GCN mechanism dispatches notification messages to all interested parties with GCNListSend() .
  5. The object is informed of the change.
    If you need to perform some work related to this change, you should have a message handler to intercept the system messages.

Registering for System Notification

Handling System Notification

Removal from a System List

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 The Mechanics of GCN | Next: 4 Application Local GCN Lists


Registering for System Notification

General Change Notification: 3.1 System Notification: Registering for System Notification

| Up: GEOS SDK TechDocs | Up | Prev: 3 System Notification | Next: 3.2 Handling System Notification

Whenever an object or process needs to be notified of some system change, it must call the routine GCNListAdd() to add itself to the list for that particular change. GCNListAdd () finds the appropriate general change notification list--creating a new one if none currently exists--and adds the optr of the new object to the end of that list. You may add the optr to the GCN list at any time during the process' or object's life, but it is usually convenient for a process to be added in its MSG_GEN_PROCESS_OPEN_APPLICATION or for an object that is on the active list to be added in its MSG_META_ATTACH handler.

Each optr in a GCN list should have a notification ID attached to it. (The combination of a manufacturer ID and a notification type comprises an element's specific notification ID.) GCNListAdd() must be passed the optr of the object to add, along with a notification ID. For each separate notification ID, a separate GCN list is needed and will be created automatically.

The GCN routines use a word of data, GCNStandardListType , to represent the notification type. The currently recognized GCNStandardListType types for MANUFACTURER_ID_GEOWORKS are

  • GCNSLT_FILE_SYSTEM
    This GCNStandardListType is used for notification of a file system change. Parties on this list will receive the system messages MSG_NOTIFY_FILE_CHANGE and MSG_NOTIFY_DRIVE_CHANGE .
  • GCNSLT_APPLICATION
    This GCNStandardListType is used for notification of a starting or exiting application. Parties on this list will receive the system messages MSG_NOTIFY_APP_STARTED and MSG_NOTIFY_APP_EXITED .
  • GCNSLT_DATE_TIME
    This GCNStandardListType is used for notification of a date/time change in the system's internal clock. Note that this will not tell you about timer ticks--the only time changes that will come up are those resulting from system restarts and time changes by the user. Parties on this list will receive the system message MSG_NOTIFY_DATE_TIME_CHANGE. This message does not pass any further information, so your message handler should be able to take care of any changes by itself (such as calling the internal clock for an updated value).
  • GCNSLT_DICTIONARY
    This GCNStandardListType is used for notification of a user dictionary change. Parties on this list will receive the system message MSG_NOTIFY_USER_DICT_CHANGE.
  • GCNSLT_KEYBOARD_OBJECTS
    This list is used for notification when the user has chosen a different keyboard layout. Parties on this list will receive the system message MSG_NOTIFY_KEYBOARD_LAYOUT_CHANGE.
  • GCNSLT_EXPRESS_MENU_CHANGE
    This GCNStandardListType notifies various system utilities that an express menu has been created or destroyed. The recipient receives the optr of the Express Menu Control. This list should be used in conjunction with the GCNSLT_EXPRESS_MENU_OBJECTS list. Objects on this list receive MSG_NOTIFY_EXPRESS_MENU_CHANGE, which itself passes a GCNExpressMenuNotificationType (either GCNEMNT_CREATED or GCNEMNT_DESTROYED) and the optr of the Express Menu Control affected.
  • GCNSLT_INSTALLED_PRINTERS
    This list notifies objects when a printer is either installed or removed. The recipient of MSG_PRINTER_INSTALLED_REMOVED might want to call SpoolGetNumPrinters() to learn if any printer or fax drivers are currently installed.
  • GCNSLT_SHUTDOWN_CONTROL
    This GCNStandardListType is used for system shutdown control. Parties on a list of this type will receive the system message MSG_META_CONFIRM_SHUTDOWN which itself passes a GCNShutdownControlType (either GCNSCT_SUSPEND, GCNSCT_SHUTDOWN, or GCNSCT_UNSUSPEND). Shutdown Control is documented in the Applications and Geodes chapter.
  • GCNSLT_TRANSFER_NOTIFICATION
    This list notifies objects that a transfer item within the clipboard has changed (or been freed). Parties on this list will receive the system message MSG_META_CLIPBOARD_
    NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED and MSG_META_CLIPBOARD_NOTIFY_TRANSFER_ITEM_FREED.
  • GCNSLT_EXPRESS_MENU_OBJECTS
    This list contains all Express Menu Control objects in the system. Typically this list is used to add a control panel item or a DOS task list item to all express menu Control objects. This can be done by sending MSG_EXPRESS_MENU_CONTROL_CREATE_ITEM to the GCN list with GCNListSend() .
  • GCNSLT_TRANSPARENT_DETACH
    This list contains all application objects that may be transparently detached if the system runs short of heap space, in least recently used (LRU) order. This list should only be used if transparent launch mode is in use.
  • GCNSLT_TRANSPARENT_DETACH_DA
    This list contains a list of transparently detachable desk accessories if the system runs short of heap space. This list should only be used if transparent launch mode is in use. Objects should not be detached unless all detachable, full-screen applications have been detached.
  • GCNSLT_REMOVABLE_DISK
    This list is used to store all application and document control objects that originate from a removable drive. If the disk they originate on is removed, they will be notified to shut themselves down with MSG_META_DISK_REMOVED.

These pre-defined notification types are intended only for use with MANUFACTURER_ID_GEOWORKS. Other manufacturers wishing to intercept their own system changes must define their own change types under their respective manufacturer IDs if they are unable to use MANUFACTURER_ID_GEOWORKS.

Code Display 9-1 Adding a Process Object to a GCN List

@method MyProcessClass, MSG_GEN_PROCESS_OPEN_APPLICATION {
    optr			myThread;
    @callsuper;			/* Do default MSG_GEN_PROCESS_OPEN_APPLICATION */
/* Casts the return value for the process handle into an optr */
    myThread = ConstructOptr(GeodeGetProcessHandle(), NullChunk);
/* myThread (the process) is added to notification of file changes */

    GCNListAdd(myThread, MANUFACTURER_ID_GEOWORKS, GCNSLT_FILE_SYSTEM);
}
| Up: GEOS SDK TechDocs | Up | Prev: 3 System Notification | Next: 3.2 Handling System Notification


Handling System Notification

General Change Notification: 3.2 System Notification: Handling System Notification

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Registering for System Notification | Next: 3.3 Removal from a System List
MSG_NOTIFY_FILE_CHANGE, MSG_NOTIFY_DRIVE_CHANGE, MSG_NOTIFY_APP_STARTED, MSG_NOTIFY_APP_EXITED, MSG_NOTIFY_USER_DICT_CHANGE, MSG_NOTIFY_EXPRESS_MENU_CHANGE

When an identified change occurs, either the system (or a library) will call the routine GCNListSend() , passing it the appropriate notification message. This routine scans the list of all GCN lists and dispatches notification to all appropriate objects that had requested knowledge of the specified change. If any additional information relating to the change cannot be included in the message, the system allows GCNListSend() to pass data in a global heap block. For example, additional information about a file change (name of file, etc.) must be passed in a global heap block.

The object or process originally requesting notification of the change should be required to handle the appropriate message. If additional data about the change is passed in a global heap block, the process should access that information with MemLock() and MemUnlock() . You should always call the process's superclass in your message handler to make sure that the global heap block will be automatically freed by MetaClass . Therefore, do not free a global heap block manually in a notification handler.

The system provides several messages which you may want to handle. These messages provide notification of file system changes, application start-up or shut-down, system clock changes, etc. These messages are mentioned with the list they correspond to in Registering for System Notification . Messages which require more detailed explanation are also mentioned below.

The kernel sends MSG_NOTIFY_FILE_CHANGE whenever a change to the file system occurs. All objects signed up on the GCN list GCNSLT_FILE_CHANGE will receive this message.

MSG_NOTIFY_FILE_CHANGE passes a FileChangeNotificationType specifying the change that occurred. Some types indicate the presence of a data block (of type FileChangeNotificationData) containing, if applicable, the name of the file changed, the disk handle of the file changed, and the ID of the affected file.

The notification type should be one of the following:

FCNT_CREATE
This type indicates that a file or directory was created. FCND_ id stores the ID of the containing directory; FCND_ name contains the name of the new file or directory that was created.
FCNT_RENAME
This type indicates that a file or directory was renamed. FCND_ id stores the ID of the file or directory that was renamed; FCND_ name contains its new name.
FCNT_OPEN
This type indicates that a file was opened. FCND_ id stores the ID of the file. FCND_ name is undefined, and may or may not be present. (You can check the size of the block to see if it is indeed present.) This notification type is generated after a call to FileEnableOpenCloseNotification() .
FCNT_DELETE
This type indicates that a file or directory was deleted. FCND_ id stores the ID of the file or directory that was deleted. FCND_ name is undefined and may or may not be present.
FCNT_CONTENTS
This type indicates that a file's contents have changed. FCND_ id stores the ID of the file. FCND_ name is undefined and may or may not be present. This notification type is generated after a call to FileCommit() or FileClose() that results in a file modification.
FCNT_ATTRIBUTES
This type indicates that a file's attributes have changed. FCND_ id stores the ID of the file. FCND_ name is undefined and may or may not be present. This notification type is generated upon completion of all changes in a FileSetAttributes() , FileSetHandleExtAttributes() , or FileSetPathExtAttributes() call.
FCNT_DISK_FORMAT
This type indicates that a disk has been formatted. Both FCND_ id and FCND_ name are undefined and may not be present.
FCNT_CLOSE
This type indicates that a file has been closed. FCND _id stores the identifier of the file. FCND _name is undefined and may not be present. This notification type is generated only after a call to FileEnableOpenCloseNotification() .
FCNT_BATCH
This type indicates that this file change notification is actually a group of notifications batched together. In this case, MSG_NOTIFY_FILE_CHANGE passes the MemHandle of a FileChangeBatchNotificationData block instead. This data block stores a batch of FileChangeBatchNotificationItem structures, each referring to an operation (with its own notification type, disk handle, file name, and file ID). Note that in this batched case, you must assume that all file names and file IDs that are optional (i.e. are undefined) are not present.
FCNT_ADD_SP_DIRECTORY
This type indicates that a directory has been added as a StandardPath . FCND_ disk contains the StandardPath that was added. This notification type is generated after a call to FileAddStandardPathDirectory() .
FCNT_DELETE_SP_DIRECTORY
This type indicates that a directory has been deleted as a StandardPath . FCND_ disk contains the StandardPath that was deleted. This notification type is generated after a call to FileDeleteStandardPathDirectory() .

You may access this data (after locking the block) and perform whatever actions you need within your message handler.

The kernel also sends MSG_NOTIFY_DRIVE_CHANGE to GCN lists of type GCNSLT_FILE_CHANGE. This message passes a GCNDriveChangeNotificationType specifying whether a drive is being created or destroyed and the ID of the affected drive.

The kernel sends MSG_NOTIFY_APP_STARTED whenever any application starts up within the system and MSG_NOTIFY_APP_EXITED whenever an application shuts down. All objects signed up on the GCN list GCNSLT_APPLICATION will receive these messages after the change occurs. MSG_NOTIFY_APP_STARTED passes the MemHandle of the application starting up, which you may access to perform any required actions. In a similar manner, MSG_NOTIFY_APP_EXITED passes the MemHandle of the application shutting down.

The kernel sends MSG_NOTIFY_USER_DICT_CHANGE whenever the system changes the current user dictionary in use. All objects signed up for the GCN list GCNSLT_USER_DICT_CHANGE will receive this message after the change occurs. MSG_NOTIFY_USER_DICT_CHANGED passes the MemHandle of the Spell Box causing the change and the MemHandle of the user dictionary being changed, both of which you may access in your message handler.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Registering for System Notification | Next: 3.3 Removal from a System List


Removal from a System List

General Change Notification: 3.3 System Notification: Removal from a System List

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Handling System Notification | Next: 4 Application Local GCN Lists

You should use GCNListRemove() to remove an object from a system GCN list. You must pass the notification ID ( GCNStandardListType and Manufacturer ID) and the optr of the object to be removed. The optr of the object in question will only be removed from the list of the particular change specified. If the optr is on several GCN lists, those other lists will remain unchanged.

An object or process in the course of dying must remove itself from all GCN lists that it is currently on. You should therefore keep track of all GCN lists you add a particular object to. It is usually convenient for a process to remove itself from these lists within its MSG_GEN_PROCESS_CLOSE_APPLICATION message handler or for an object to remove itself in its MSG_META_DETACH handler.

Code Display 9-2 Removing a Process from a GCN list

@method MyProcessClass, MSG_GEN_PROCESS_CLOSE_APPLICATION {
    optr 		myThread;
    myThread = ConstructOptr(GeodeGetProcessHandle(), NullChunk);
    GCNListRemove(myThread, MANUFACTURER_ID_GEOWORKS, GCNSLT_FILE_CHANGE);
    @callsuper;
}
| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Handling System Notification | Next: 4 Application Local GCN Lists


Application Local GCN Lists

General Change Notification: 4 Application Local GCN Lists

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 System Notification | Next: 4.1 Creating Types and Lists

The GCN mechanism not only allows you to keep track of system changes but also allows you to keep track of changes within a specific application. These application-specific GCN lists operate in slightly different manners than the system-wide application lists. There are an extensive number of pre-defined application lists for MANUFACTURER_ID_GEOWORKS. You may use these if you like, but in most cases you will want to create your own list and notification types for your application.

The GenControl objects make extensive use of these types of GCN lists when implementing changes. For a complete discussion of using these lists within the context of a GenControl, see the Controllers chapter.

If you will be creating custom GenControl objects or just wish to set up a notification system within your application, you will want to create your own application GCN lists when using this mechanism. To do this, you must follow a few preliminary steps:

  • Create a new list of type YourCompanyName GenAppGCNListTypes within an appropriate yourCompanyName.h file.
  • Create an GCN notification type of YourCompanyName NotificationTypes for the above list type within the yourCompanyName.h file.

The GCN mechanism in this case performs its functions through a common series of steps. These steps are similar to the steps needed for system-wide notification:

  1. The object registers for notification with MSG_META_GCN_LIST_ADD .
  2. The change occurs within your application and invokes your own custom method. Because the change occurs within your application, you are responsible for detecting the change and sending out notification yourself.
  3. Record the notification event with MSG_META_NOTIFY or MSG_META_NOTIFY_WITH_DATA_BLOCK , the notification list type to use, and the data block to pass (if applicable).
  4. Use MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST to pass the event. You may have to pass some GCNListSendFlags with this message. This message acts as a dispatch routine, sending all interested parties the recorded event MSG_META_NOTIFY .
  5. The object is informed of the change with MSG_META_NOTIFY or MSG_META_NOTIFY_WITH_DATA_BLOCK . If you need to perform some work related to this change, you should have a message handler to intercept these messages.

Creating Types and Lists

Registering for Notification

Handling Application Notification

Removal from Application Lists

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 System Notification | Next: 4.1 Creating Types and Lists


Creating Types and Lists

General Change Notification: 4.1 Application Local GCN Lists: Creating Types and Lists

| Up: GEOS SDK TechDocs | Up | Prev: 4 Application Local GCN Lists | Next: 4.2 Registering for Notification

It is a relatively simple matter to create your own notification types. Within an appropriate company-specific file merely create your own types and lists. (For example, all Geoworks application-local lists are within the file geoworks.h .)

Code Display 9-3 Creating New Notification Types and Lists

/* These types should be placed within an appropriate yourCompanyName.h file. */
/* First create a group of Notification types to use for your MANUFACTURER_ID. */
typedef enum {
    <yourCompanyName>_NT_CUSTOM_NOTIFICATION_NUMBER_ONE
    <yourCompanyName>_NT_CUSTOM_NOTIFICATION_NUMBER_TWO
    ...
} <yourCompanyName>NotificationTypes;
/* Then create whatever Notification list types you need. These list types
 * usually correspond one-to-one to the types enumerated above. It is possible,
 * however, for several lists to be interested in a single notification type. */
typedef enum {
    <yourCompanyName>_GAGCNLT_CUSTOM_LIST_TYPE_ONE
    <yourCompanyName>_GAGCNLT_CUSTOM_LIST_TYPE_TWO
    ...
} <yourCompanyName>GenAppGCNListTypes;
| Up: GEOS SDK TechDocs | Up | Prev: 4 Application Local GCN Lists | Next: 4.2 Registering for Notification


Registering for Notification

General Change Notification: 4.2 Application Local GCN Lists: Registering for Notification

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Creating Types and Lists | Next: 4.3 Handling Application Notification

MSG_META_GCN_LIST_ADD

Registering for application notification is simple once you have created your own custom notification lists. Whenever an object or process needs to be notified of an application change, you should call MSG_META_GCN_LIST_ADD to add that object or process to the list interested in that particular change. MSG_META_GCN_LIST_ADD finds the appropriate custom GCN list and adds the optr of the new object to the end of that list. (If no space for the list currently exists because it is empty, the message will allocate space for the list automatically.) You may add the interested optr at any time during the process' or object's life, but it is usually convenient for a process to be added in its MSG_GEN_PROCESS_OPEN_APPLICATION or for an object to be added in its MSG_META_ATTACH handler.

Each optr in a GCN list should have a notification ID attached to it. The combination of a manufacturer ID and a notification type comprises an element's specific notification ID. MSG_META_GCN_LIST_ADD must pass the optr of the object to add, along with a notification ID. For each separate notification ID, a separate GCN list is needed and will be created automatically.

Geoworks has several pre-defined GCN lists of type GeoWorksGenAppGCNListType for use by applications. You will probably have only limited use for these; these list types are used mostly by the UI controllers. For information on these types and how the various classes use them, see the Controllers chapter.

Code Display 9-4 Adding Yourself to a Custom GCN List

@method MyProcessClass, MSG_GEN_PROCESS_OPEN_APPLICATION {
    @callsuper;			/* Do default MSG_GEN_PROCESS_OPEN_APPLICATION */
    myThread = ConstructOptr(GeodeGetProcessHandle(), NullChunk);
/* myThread (the process) is added to notification of TYPE_ONE changes */

    @call MyApplication::MSG_META_GCN_LIST_ADD(myThread,
    yourCompanyName_GAGCNLT_CUSTOM_LIST_TYPE_ONE,
    MANUFACTURER_ID_yourCompanyName);
}
| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Creating Types and Lists | Next: 4.3 Handling Application Notification


Handling Application Notification

General Change Notification: 4.3 Application Local GCN Lists: Handling Application Notification

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Registering for Notification | Next: 4.4 Removal from Application Lists
MSG_META_NOTIFY, MSG_META_NOTIFY_WITH_DATA_BLOCK, MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST

When a change occurs in the application that needs to send out notification, you must set up the notification message and send it to the interested list. You may attach a data block to this notification for use by the objects on the notification list. To send out these notifications, you should use MSG_META_NOTIFY or MSG_META_NOTIFY_WITH_DATA_BLOCK (when passing data).

In the simplest case without the need to pass data, you should encapsulate MSG_META_NOTIFY with the particular Notification ID (notification type and Manufacturer ID) that should be notified. You should then send MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST to your application object with this event and the particular GCN list interested in this change. (Note that you will have to keep track of which lists are interested in which notification types.) Make sure that you perform a send (not a call) when using this message as the message may cross threads.

Code Display 9-5 Using MSG_META_NOTIFY

@method MyProcessClass, MSG_SEND_CUSTOM_NOTIFICATION {
    MessageHandle event;
/* First encapsulate the MSG_META_NOTIFY with the type of list and manufacturer ID
 * interested in the change. Since this message is being recorded for no class in
 * particular, use NullClass.*/
    event = @record (optr) NullClass::MSG_META_NOTIFY(
    MANUFACTURER_ID_yourCompanyName,
    yourCompanyName_NT_CUSTOM_TYPE_ONE,
    NULL );
/* Then send this MSG_META_NOTIFY using MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST.
 * You must make sure to pass the particular GCN list interested in the changes
 * encapsulated in the above message. */
    @send MyProcess::MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST (
    (word) 0, 		/* GCNListSendFlags */
    event,			/* Handle to MSG_NOTIFY event above. */
    0,			/* No data passed, so no data block. */
    /* Pass the list interested in NT_CUSTOM_TYPE_ONE notification types. */
    yourCompanyName_GAGCNLT_APP_CUSTOM_LIST_ONE,
    /* Pass your manufacturer ID. */
    MANUFACTURER_ID_yourCompanyName);
}

If instead you need to pass a data block along with the notification, you should use MSG_META_NOTIFY_WITH_DATA_BLOCK . You should set up the structure to pass beforehand. You must also make sure to add a reference count to the data block equal to the number of lists (not objects) you wish to send the notification. To do this, call MemInitRefCount() with the data block and the total number of lists you are sending the notification to. (In most cases, you will only send notification to one list, although, of course, that list may have several objects.)

Code Display 9-6 MSG_META_NOTIFY_WITH_DATA_BLOCK

@method MyProcessClass, MSG_SEND_CUSTOM_NOTIFICATION {
    typedef struct {
    int number;
    char letterToLookFor;
    } MyDataStructure;
    MemHandle myDataBlock;
    MyDataStructure *myDataPtr;
    MessageHandle event;
/* Allocate and lock down a block for the data structure. This will be passed
 * along with the notification. NOTE: data blocks must be sharable! */
    myDataBlock = MemAlloc(sizeof(MyDataStructure), (HF_DYNAMIC | HF_SHARABLE),
             HAF_STANDARD);
    myDataPtr = MemLock(myDataBlock);
/* Load up the structure with pertinent information. */
    myDataPtr->count = 200;
    myDataPtr->letterToLookFor = `z';
/* Unlock it and set its reference count to 1 as we are only sending this to one
 * list. */
    MemUnlock(myDataBlock);
    MemInitRefCount(myDataBlock, (word) 1);
/* Now encapsulate a MSG_META_NOTIFY_WITH_DATA_BLOCK message. Since it is being
 * recorded for no particular class, use NullClass as its class type. */
    event = @record (optr) NullClass::MSG_META_NOTIFY_WITH_DATA_BLOCK(
            MANUFACTURER_ID_yourCompanyName,				/* Manufacturer ID */
            NT_CUSTOM_TYPE_ONE,				/* List type. */
            myDataBlock);				/* handle of data block */
/* Finally, send the message off to our process. The GCNListSendFlags depend on
 * the situation. */
    @send MyProcess::MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST(
            (word) 0,			/* GCNListSendFlags */
            event,			/* Handle to message */
            myDataBlock,			/* Handle of data block */
    /* Pass the type of list interested in NT_CUSTOM_TYPE_ONE notification. */
            GAGCNLT_APP_CUSTOM_LIST_ONE,
            MANUFACTURER_ID_yourCompanyName);
/* All done! myDataBlock will be MemFree()'d automatically. */
}

The object or process originally requesting notification of the change will want to provide a handler for the MSG_META_NOTIFY or MSG_META_NOTIFY_WITH_DATA_BLOCK . If additional data about the change is passed in a data block, the process should access that information with MemLock() and MemUnlock() . You should always call the process's superclass in your message handler, to make sure that the global heap block will be automatically freed by MetaClass . Therefore, do not free a notification data block manually in a notification handler.

Code Display 9-7 Intercepting an Application Notification Change

/* Code to implement when MyObjectClass receives MSG_META_NOTIFY with a certain
 * notification type. */
@method MyObjectClass, MSG_META_NOTIFY {
    MyDataStructure myData;				/* Stores the passed data block. */
/* Lock the data structure. */
    myData = MemLock(data);
/* Check the notification type and implement the changes you wish to occur in
 * response to the previous event. */
    if ((notificationType == yourCompanyName_NT_CUSTOM_TYPE_ONE) &
    (manufID == MANUFACTURER_ID_yourCompanyName)){
    /* Code to implement for your object. */
    }
    MemUnlock(data);
    @callsuper;				/* Important! Frees data block. */
}
| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Registering for Notification | Next: 4.4 Removal from Application Lists


Removal from Application Lists

General Change Notification: 4.4 Application Local GCN Lists: Removal from Application Lists

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Handling Application Notification

You should use MSG_META_GCN_LIST_REMOVE to remove an object from an application GCN list. You must pass the routine the notification ID ( yourCompanyName AppGCNListTypes and Manufacturer ID) and the optr of the object to remove. Note that the optr of the object in question will only be removed from the list of the particular change specified. If the optr is on several GCN lists, those other lists will remain unchanged.

An object or process in the course of dying must remove itself from all GCN lists that it is currently on, both from the system and from an application. You should therefore keep track of all GCN lists you add a particular object to. It is usually convenient for a process to remove itself from these lists within its MSG_GEN_PROCESS_CLOSE_APPLICATION message handler or for an object to remove itself at MSG_META_DETACH time.

Code Display 9-8 Removing from an Application GCN List

@method MyProcessClass, MSG_GEN_PROCESS_CLOSE_APPLICATION {
    @send MyApplication::MSG_META_GCN_LIST_REMOVE(
            MyObject,			/* optr to remove from list. */
            yourCompanyName_NT_CUSTOM_LIST_ONE,
                    /* list to remove object from. */
        /* Manufacturer ID of list to remove object from. */
            MANUFACTURER_ID_yourCompanyName);
    @callsuper;
}
| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Handling Application Notification


Applications and Geodes

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Geodes
| One-page Version of this Chapter for Easy Printing

This chapter discusses the life of an application as well as several topics most application programmers will want to cover at one time or another. Before reading this chapter, you should have read each of the previous chapters; this chapter builds on the knowledge you gained in those chapters.

This chapter details the components and features of a geode , a GEOS executable. The chapter is presented in the following five sections:

  • Geodes
    The Geodes section describes how libraries, drivers, and applications are launched, used, and shut down. It also discusses the general sections of a geode and the geode's executable file.
  • Creating Icons
    The Creating Icons section describes the structure of the Token Database, how icons are stored, managed, and created.
  • Saving User Options
    The Saving Options section describes how to work with the GEOS.INI file.
  • General System Utilities
    The General System Utilities section describes several different mechanisms provided by GEOS that you may find useful in your application or other geode.
  • The Error-Checking System Software
    The Error-Checking System Software describes the differences between the debugging and standard versions of the system software. There are two versions of nearly every part of the system software including the kernel, the UI, and most libraries and drivers.
  • IACP: Inter-Application Communication Protocol
    The IACP section describes how applications can communicate with each other and pass data back and forth. This protocol works for applications that are not necessarily loaded and running; thus, an application can change another application's data dynamically--the recipient application is automatically started if it is not already running. (This is an advanced topic; most programmers will not need to read this section.)

1 Geodes
1.1 Geode Components and Structures
1.2 Launching an Application
1.3 Shutting Down an Application
1.4 Saving and Restoring State
1.5 Using Other Geodes
1.6 Writing Your Own Libraries
1.7 Working with Geodes
1.8 Geode Protocols and Release Levels
1.9 Temporary Geode Memory
2 Creating Icons
2.1 The Token Database
2.2 Managing the Token Database File
3 Saving User Options
3.1 Saving Generic Object Options
3.2 The GEOS.INI File
4 General System Utilities
4.1 System Clock
4.2 Using Timers
4.3 System Statistics and Utilities
4.4 Shutting the System Down
5 The Error-Checking Version
5.1 Adding EC Code to Your Program
5.2 Special EC Routines
6 Inter-Application Communication
6.1 IACP Overview
6.2 GenApplicationClass Behavior
6.3 Messages Across an IACP Link
6.4 Being a Client
6.5 Being a Server

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Geodes
| One-page Version of this Chapter for Easy Printing


Geodes

Applications and Geodes: 1 Geodes

| Up: GEOS SDK TechDocs | Up | Down | Prev: Applications and Geodes | Next: 2 Creating Icons

Geode is the term used to describe a GEOS executable. Just as DOS has executables (programs) that reside in files on a disk, so too does GEOS. GEOS executables normally have the filename extension .GEO.

Each geode may have up to three different aspects:

  • process
     Most of your geodes will have this aspect. A geode that is a process has an initial event-driven thread started for it by the kernel. All applications and some libraries will have this aspect.
  • library
     This aspect indicates that the geode exports entry points for other geodes to use. Typically, these entry points describe where either object classes or code for routines is within the geode. A library has a special routine (the library's entry point) that is called by the system when the library or one of its clients is loaded or in the process of being unloaded.
  • driver
     This aspect indicates that the geode hides the details of some device or similarly changeable thing from the system. A driver has a special structure that defines its type, and it has a single entry point called a strategy routine . All calls to the driver pass through this strategy routine.

A geode can have any combination of these aspects. For example, the print spooler is a process-library (and therefore provides routines for other geodes while also having a thread of its own), but the sound library is actually a library-driver since it manipulates the machine's sound hardware.

Library and driver geodes that do not have the process aspect do not initially have event-driven threads. Therefore, typically they will not contain objects, just procedural code. They can contain objects, however, as any geode is free to create an event-driven thread for itself at any time. In fact, the parallel port driver does just that when it is printing to a port through DOS or BIOS.

Geodes are loaded either with a call to the kernel routine GeodeLoad() or as a side effect of a library's client being loaded (in that case, the library geode will be loaded as well). The generic UI supplies the special routine UserLoadApplication() , which you may use to load an application--a geode which has both a process aspect and its process class subclassed off of GenProcessClass (and therefore can put generic UI objects on the screen).

Once a geode has been loaded, it is identified by its geode handle , which is the memory handle of the block that holds all the geode's system-administrative data. This block is called the core block and should not be accessed by anything other than the kernel. The geode handle is also used to determine the owner of a particular block in memory; when queried for the owner of a particular block, the kernel will return the geode handle of the geode that owns that block. A geode is the only entity that may own a system resource. If the geode is a process, the geode handle may also be known as a process handle.

When a geode is loaded, its core block is connected to a linked list of the core blocks of other geodes running in the system. This linked list is chronological, with the first entry belonging to the first geode loaded and the last entry belonging to the most recent geode loaded. Each core block contains an entry for the handle of the next core block in the list; the kernel can follow these links to locate any geode in the system. (Only the kernel may do this.)

After the core block is appended to the list, GEOS scans the list for other instances of the same core block. If the geode has been loaded more than once, it will have multiple instances in the list (one instance of the core block for each time the geode is loaded; each core block references the same copy of the geode, however). GEOS then copies the shared-resource handles from an existing core block (if found) into the new core block, thus reducing the amount of work required to load a particular geode multiple times (the shared resources do not need to be reloaded or recreated). Non-shared resource handles are not copied; the resources are loaded or constructed as necessary.

Each geode's core block contains a reference count for that particular geode. When the geode is first loaded, the reference count is set to one. If the geode is a process, the act of initializing the process thread increments the reference count. Each time the geode is loaded again, the new core block will get its own reference count. If the geode is loaded implicitly (as a library, with GeodeUseLibrary() , or with GeodeUseDriver() ), or if it spawns a new thread, it will receive yet another reference count.

The reference count is decremented when a thread owned by the geode exits. If a client of a library geode exits, the library's reference count goes down by one.

When a geode's reference count reaches zero, all the geode's non-sharable resources are freed along with all the file, event, and timer handles owned by the geode. If a sharable resource is co-owned by another instance of the geode, ownership is transferred to the geode's next-oldest instance. (Shared resources are always owned by the oldest instance of their geode.) Once the resources have been freed or transferred, the core block is removed from the linked list and is freed.

To make sure no synchronization problems occur while updating the core block list (e.g. a geode is being loaded while it has just been freed), GEOS maintains an internal semaphore. The geode loading and freeing routines automatically maintain this semaphore.

Geode Components and Structures

Launching an Application

Shutting Down an Application

Saving and Restoring State

Using Other Geodes

Writing Your Own Libraries

Working with Geodes

Geode Protocols and Release Levels

Temporary Geode Memory

| Up: GEOS SDK TechDocs | Up | Down | Prev: Applications and Geodes | Next: 2 Creating Icons


Geode Components and Structures

Applications and Geodes: 1.1 Geodes: Geode Components and Structures

| Up: GEOS SDK TechDocs | Up | Prev: 1 Geodes | Next: 1.2 Launching an Application

A geode is simply a special type of GEOS file. It has a special file header that gets loaded in as the geode's core block. This file header contains the geode's type, attributes, release and protocol levels, and many other pieces of information necessary for GEOS to work with the geode. You never will have to know the exact structure of this header as the kernel provides routines necessary to access important portions of it.

Several important items contained in the header are listed below.

  • Core Block Handle
    The core block contains its own memory handle, filled in when the geode is loaded into memory.
  • Geode Attributes
    Each geode has a record of type GeodeAttrs . The geode attributes are described below in Geode Attributes .
  • Release and Protocol Levels
    Each geode can have release and protocol levels associated with it to ensure compatibility between different versions. Release and protocol levels are discussed in Protocol Numbers .
  • Geode Name
    Each geode has a geode name and extension specified in the geode parameters ( .gp ) file.
  • Geode Token
    Every geode has a token associated with it in the token database. The token describes the geode's icon and is a structure of type GeodeToken . Tokens and icons are discussed in Creating Icons .
  • Geode Reference Count
    The geode's reference count is stored in the core block. See above for a discussion of the reference count and how it's managed.
  • Geode File Handle
    The file handle of the geode identifies the open GEO file so the kernel can locate, load, and access its various resources.
  • Geode Process Handle
    Each geode that has a parent process will have the handle of the process in its core block. This is the handle of the parent process' core block. (The parent process is the owner of the thread that loaded the geode. It is notified when the geode exits, if the exiting geode is a process.)
  • Handle of the Next Geode
    Because geode core blocks are stored in a linked list, each core block must contain a reference to the next geode in the list.
  • Handle of Private Data Space
    Each geode that is not the kernel can have a "private data space." This private data space is discussed in Temporary Geode Memory .
  • Resource, Library, and Driver Information
    Each geode that imports or exports library or driver information must keep the import and export specifics available. Additionally, each geode must keep track of the resources it owns. All this information is stored in tables referenced from within the core block.

Geode Attributes

Each geode has in its core block a record of type GeodeAttrs . This record defines several things about the geode, including which aspects it uses and which of its aspects have been initialized. The GeodeAttrs record contains one bit for each of the following attributes.

GA_PROCESS
This geode has a process aspect and therefore an initial event-driven thread.
GA_LIBRARY
This geode has a library aspect and therefore exports routines (and possibly object classes).
GA_DRIVER
This geode has a driver aspect and therefore has a driver table, in which the strategy routine is specified.
GA_KEEP_FILE_OPEN
This geode must have its .GEO file kept open because its resources may be discardable or are initially discarded.
GA_SYSTEM
This geode is a privileged geode and is almost certainly a system-used driver. These geodes have special exit requirements.
GA_MULTI_LAUNCHABLE
This geode may be loaded more than once and therefore may have more than one instance of its core block in memory.
GA_APPLICATION
This geode is a user-launchable application.
GA_DRIVER_INITIALIZED
This flag is set if the geode has had its driver aspect initialized (if the driver's strategy routine has been initialized). This flag will be set dynamically by the kernel.
GA_LIBRARY_INITIALIZED
This flag is set if the geode has had its library aspect initialized (if the library's entry routine has been called). This flag will be set dynamically by the kernel.
GA_GEODE_INITIALIZED
This flag is set if all aspects of the geode have been initialized.
GA_USES_COPROC
This geode uses a math coprocessor if one is available.
GA_REQUIRES_COPROC
This geode requires the presence of a math coprocessor or a coprocessor emulator.
GA_HAS_GENERAL_CONSUMER_MODE
This geode may be run in the General Consumer (appliance) Mode.
GA_ENTRY_POINTS_IN_C
This geode has its library entry routine in C rather than assembly language.

Geode Token

As stated above, every geode is associated with a token in the token database. This token is defined by the use of a GeodeToken structure. This structure and its uses are discussed in Creating Icons .

| Up: GEOS SDK TechDocs | Up | Prev: 1 Geodes | Next: 1.2 Launching an Application


Launching an Application

Applications and Geodes: 1.2 Geodes: Launching an Application

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Geode Components and Structures | Next: 1.3 Shutting Down an Application
GeodeLoad(), UserLoadApplication(), MSG_GEN_PROCESS_OPEN_APPLICATION

An application is a geode with its GA_APPLICATION attribute set. This type of geode may be launched by the user through GeoManager or some other means provided by the system or another application. For the most part, the system will invoke and carry out the launch; your responsibilities are limited.

An application may be loaded in essentially two ways: It may be launched, or it may be reloaded from a state file. In both cases, the kernel will load the proper resources and build out the UI properly according to the application.

Most of the procedure of launching is handled within GenProcessClass , a subclass of ProcessClass . An application should define its own subclass of GenProcessClass for its Process objects (event-driven threads not acting as Process objects should be subclassed from ProcessClass , not GenProcessClass ). The launch procedure may be invoked by any thread and by any geode in either of the following ways:

  • GeodeLoad()
     This loads a geode from a given file and begins executing it based on the geode's type. GeodeLoad() takes the name of the geode's file as well as a priority to set for the new geode's process thread, if it has one. GeodeLoad() first creates the process thread of the application, then sends this thread a message. The process thread (a subclass of GenProcessClass ) then creates a UI thread for the application.
  • UserLoadApplication()
     Used by most application launchers, this routine loads an application. (C programmers will generally use MSG_GEN_PROCESS_OPEN_APPLICATION instead.) This routine takes some additional parameters and can load a geode either in engine mode or from a state file as well as in the normal open mode (see below). The base functionality of opening and loading the geode is implemented in this routine by a call to GeodeLoad() . Note, however, that this routine may only open application geodes--geodes with the GA_APPLICATION attribute set.

Geodes may be launched in three modes:

  • Application mode launches the geode, loads all its active resources, builds its UI gadgetry, and sets it usable. The geode must be an application--that is, it must have its GA_APPLICATION attribute set.
  • Engine mode launches the geode but leaves its UI objects unusable (it never brings them on-screen). Engine mode is useful if you need to launch an application to grab information from it. GeoManager uses engine mode to launch an application, extract its icon, and put the icon in the token database. For efficiency, the application is never set usable, so its UI is never built.
  • Restore mode launches the geode from a saved state file, loading in resources and merging them with the default resources. This mode is invoked automatically by the UI if it is restoring the system or the geode from saved state. This mode is handled automatically by GenProcessClass (your Process object).

It is possible, however, for one application to launch another in a custom mode. If this is done, the application being launched is responsible for implementing the special mode.

When the launch process has been initiated with the above routines, a thread is created for the application and its Process object is loaded immediately. Also loaded is the application's GenApplication object. The Application and Process objects interact with the User Interface to load the objects on the application's active list and set them all usable, bringing them up on-screen.

Near the end of this procedure, just before the GenApplication is set usable, the Process object will receive a message based on the mode of launch. If the application must set up any special notification (such as for the quick-transfer mechanism) or must restore special state file data, it should intercept this message. Typically the message received will be MSG_GEN_PROCESS_OPEN_APPLICATION --two others are received (when restoring from state and when opening in engine mode), but they should not be intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Geode Components and Structures | Next: 1.3 Shutting Down an Application


Shutting Down an Application

Applications and Geodes: 1.3 Geodes: Shutting Down an Application

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Launching an Application | Next: 1.4 Saving and Restoring State
MSG_GEN_PROCESS_CLOSE_APPLICATION, MSG_GEN_PROCESS_CLOSE_ENGINE, MSG_GEN_PROCESS_CLOSE_CUSTOM, MSG_META_QUIT

Just as loading an application is handled almost entirely by the system and GEOS classes, application shutdown is also fairly automatic. If the application intercepted MSG_GEN_PROCESS_OPEN_APPLICATION for its own purposes on startup, it likely has to do a little cleanup; otherwise, it won't have to worry about shutting down. (See Saving and Restoring State for special information on using this message.)

Any object in the system may cause an application to shut down. Usually, shutdown occurs either when the system is being exited (when a user exits to DOS, for example) or when the user has closed the application. Therefore, the usual source of the shutdown directive is the User Interface.

An application begins shutting down when either its Process object or its Application object receives a MSG_META_DETACH . If you want to cause a shutdown manually, you should send MSG_META_QUIT to the application's GenApplication object; this will execute some default functions and then send the appropriate MSG_META_DETACH . Essentially, the same detach and destruction mechanisms used for any object are used for the entire application. The object receiving MSG_META_DETACH passes the message along to all of its children and to all the objects on its active list. (If a MSG_META_DETACH is used without MSG_META_QUIT , the application will create a state file.)

When they have all acknowledged the detach, the application acknowledges the detach and sets itself unusable. It automatically flushes its message queues before shutting down to avoid synchronization problems. You should not subclass the MSG_META_DETACH handler unless you have special needs for cleaning up or sending special detach messages to other objects or geodes. If you do subclass it, you must call the superclass at the end of your handler. Otherwise, the application will not finish detaching (see the GEOS Programming chapter).

Instead of intercepting MSG_META_DETACH , though, the application may intercept the mode-specific message it will also receive. Depending on the mode in which it was launched, the application will receive (via the Process object) either MSG_GEN_PROCESS_CLOSE_APPLICATION (for application mode) or MSG_GEN_PROCESS_CLOSE_ENGINE (for engine mode). There is no special shutdown message for shutting down to a state file; instead, MSG_GEN_PROCESS_CLOSE_APPLICATION is used.

When the system shuts down or task-switches, a different type of shutdown occurs. Applications (or other objects interested in this event) must register for notification on the notification list GCNSLT_SHUTDOWN_CONTROL (notification lists are described in the General Change Notification chapter). When the system shuts down or task-switches, the object will then receive a MSG_META_CONFIRM_SHUTDOWN , at which time the object must call SysShutdown() .

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Launching an Application | Next: 1.4 Saving and Restoring State


Saving and Restoring State

Applications and Geodes: 1.4 Geodes: Saving and Restoring State

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Shutting Down an Application | Next: 1.5 Using Other Geodes
ObjMarkDirty(), ObjSaveBlock()

Nearly all applications will save and restore their state so the user may shut down and return to precisely the same configuration he or she left. Saving of state is almost entirely contained within the system software; for the most part, only UI objects are saved to state files. You can, however, mark other object blocks and data for saving to a state file.

The state file for an application is a VM file containing object blocks. Only object blocks may be saved to the state file, though you can save LMem data by setting up object blocks with only data chunks in them. (Create the blocks with MemAllocLMem() , passing type LMEM_TYPE_OBJ_BLOCK, then simply use LMemAlloc() to allocate data chunks.) For more information about managing LMem and data chunks, see the Local Memory chapter. You can also save an extra data block to the state file using MSG_GEN_PROCESS_CLOSE_APPLICATION and MSG_GEN_PROCESS_OPEN_APPLICATION . In the close message, you can return the handle of an extra block to be saved to the state file; in the open message, the handle of the extra block is given to you, and you can restore this data as necessary. See the reference information for these messages under GenProcessClass for more information.

When a state file is saved, the system recognizes and saves only the dirty (modified) objects and chunks. Later, when state is restored, the system merges the changes in the state file with the original object blocks, resulting in the state that was saved.

For individual objects or entire object blocks to be saved to the state file, they must be marked dirty. Generic objects automatically mark themselves dirty at the appropriate times, so you don't have to worry about them. To mark other objects dirty, use the routine ObjMarkDirty() . Each object which has been marked dirty will be saved to a state file when appropriate. If you want to save an entire object block to the state file, you can call ObjSaveBlock() on the block; the system will save the entire block, not just the dirty chunks.

State files are dealt with at only two times: First, when the system starts up, it will check for the existence of application state files. If a state file exists, the system will attempt to load the application belonging to it; after loading the application's resources, it will merge the state changes with the default settings to restore the original state.

The second time state files are used is when the system shuts down. A simple shutdown (called a "detach") is invoked only by the UI and is not abortable. When a detach occurs, the system shuts down all geodes as cleanly and quietly as possible, saving them to state files. Only certain geodes will respond in extreme cases, offering the user the option of delaying the detach or cancelling an operation in progress. An example of this is the GEOS spooler; if one or more jobs are actively printing or queued for printing, the spooler will ask the user whether the job should continue and the detach be delayed, or whether the job should be aborted or delayed until the next startup. The spooler can not abort the detach in any case.

Another type of detach is called a "quit." Any geode may invoke a quit, which is actually a two-step detach. A quit will first notify all other geodes that the system will soon be detaching; other geodes then have the chance to abort the quit if they want. For example, if a terminal program were downloading a file and received a quit notification, it could ask the user whether she wanted to abort the quit or the download. If the user wanted to finish the download, she would abort the quit; if she wanted to quit, she would abort the download. The system would then either shut down via a normal detach or stop the quit sequence.

When a geode is first launched, no state file exists for it. The state file is not created until the geode is actually detached to a state file. If a geode is restored from a state file, the file will exist until the geode is detached again. A geode that gets closed (not detached to state) will remove any state file it may have created during a previous detach. A geode that is detached to state will create or modify its state file as appropriate.

The state of an application (how it was launched) is reflected in the GAI_states field of the GenApplication object. To retrieve the application's state, send it MSG_GEN_APPLICATION_GET_STATE . It will return a value of ApplicationStates . The most frequent use of this message is by applications that need to know whether a "quit" is underway when their process objects receive the MSG_GEN_PROCESS_CLOSE_APPLICATION message; the process object will query the GenApplication and see if it is in the AS_QUITTING state.

In addition to the above state-saving functionality, the kernel provides two routines that translate handles between the state file and memory. ObjMapSavedToState() takes the memory handle of an object block and returns its corresponding state file VM block handle. ObjMapStateToSaved() takes the state file VM block handle and returns the corresponding memory block handle, if any.

If your application's documents are VM files, it is a very simple matter to save document state. In fact, if you use the GenDocument and document control objects, they will take care of document state saving for you. Be sure that the VM file has the VMA_BACKUP flag set in its VMAttributes ; then you can simply call VMUpdate() on the document file. (Note--do not use VMSave() instead; it will erase the backup and lock in the user's changes to the document.) If you are not using GEOS VM files, it is up to you how and if you will save the document's state. For more information about GEOS VM files, see the VM chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Shutting Down an Application | Next: 1.5 Using Other Geodes


Using Other Geodes

Applications and Geodes: 1.5 Geodes: Using Other Geodes

| Up: GEOS SDK TechDocs | Up | Prev: 1.4 Saving and Restoring State | Next: 1.6 Writing Your Own Libraries

Often, geodes will have to use other geodes. For example, a communications program will use the Serial Driver, and a draw application will use the Graphic Object Library. Normally, this is taken care of by the compiler and the linker when you include a library or driver in your .goc and .gp files.

Other times, however, an application will have to load libraries or drivers on the fly and then free them some time later. This section describes how to load, use, and free libraries and drivers.

Using Libraries

GeodeUseLibrary(), GeodeFreeLibrary()

Libraries are always referenced by their file names or by their geode handles. It's easiest, however, to use the file name of the library when loading it--the system will locate the library for you. It's unusual to need to load a library for use with your geode; in almost all cases it's easiest to include the library in your .goc and .gp files and have the system load and link the library automatically. (To do this, include the library's interface definition file in your code file and list the library's geode name in your geode parameters file.)

If you need to load a library dynamically, though, use GeodeUseLibrary() . This routine takes the protocol numbers expected of the library (see Protocol Numbers ) and the library geode's filename. It will locate and load the library if not already loaded. If the library is already loaded, it will increment the library's reference count. When you are done using a library loaded with GeodeUseLibrary() , you must free the library's instance with GeodeFreeLibrary() .

Using Drivers

GeodeUseDriver(), GeodeInfoDriver(), GeodeGetDefaultDriver(), GeodeSetDefaultDriver(), GeodeFreeDriver()

Drivers are referenced by either their permanent names or their geode handles. Most drivers used by applications will be loaded automatically by the kernel; the application must have the driver's permanent name specified in its .gp file. Should an application need to use a driver not included in its parameters file, however, it can do so with the routines described below.

When you need to use a driver, the GeodeUseDriver() routine will locate and load it, adding it to the active geodes list. You must pass the desired driver geode's filename as well as the expected protocol levels of the driver. The routine will return the driver's geode handle. If you load a driver dynamically, you must free it with GeodeFreeDriver() when your geode shuts down or otherwise finishes using the driver.

If you know a driver's geode handle, you can easily retrieve information about it with the routine GeodeInfoDriver() . This returns a structure of type DriverInfoStruct , which contains the driver's type ( DriverType ), the driver's attributes, and a far pointer to the driver's strategy routine. Many driver types have an expanded information structure, of which DriverInfoStruct is just the first field. Video driver information structures, for example, also contain dimensions and color capabilities (among other things) of the particular devices they drive. The driver information structure is shown below.

typedef struct {
    void			(*DIS_strategy)();
    DriverAttrs			DIS_driverAttributes;
    DriverType			DIS_driverType;
} DriverInfoStruct;

The DIS_strategy field of the structure contains a pointer to the driver's strategy routine in fixed memory. After the driver has been loaded, its strategy routine is called directly with a driver function name.

The DIS_driverAttributes is an attribute record of type DriverAttrs , the flags of which are shown below:

DA_FILE_SYSTEM
This flag indicates that the driver is used for file access.
DA_CHARACTER
This flag indicates that the driver is used for a character-oriented device.
DA_HAS_EXTENDED_INFO
This flag indicates that the driver has a DriverExtendedInfo structure.

The DIS_driverType contains the type of driver described by the information structure. The types that may be specified in this field are listed below:

DRIVER_TYPE_VIDEO
This is used for video drivers.
DRIVER_TYPE_INPUT
This is used for input (mouse, keyboard, pen, etc.) drivers.
DRIVER_TYPE_MASS_STORAGE
This is used for storage device drivers.
DRIVER_TYPE_STREAM
This is used for stream and port (parallel, serial) drivers.
DRIVER_TYPE_FONT
This is used for font rasterizing drivers.
DRIVER_TYPE_OUTPUT
This is used for output drivers other than video and printer drivers.
DRIVER_TYPE_LOCALIZATION
This is used for drivers that facilitate internationalization.
DRIVER_TYPE_FILE_SYSTEM
This is used for file system drivers.
DRIVER_TYPE_PRINTER
This is used for printer drivers.
DRIVER_TYPE_SWAP
This is used for the system's memory-swapping drivers.
DRIVER_TYPE_POWER_MANAGEMENT
This is used for devices that have power management systems.
DRIVER_TYPE_TASK_SWITCH
This is used for devices or systems that have task switchers.
DRIVER_TYPE_NETWORK
This is used for special networks that require driver functionality.

When you want a driver to perform one of its functions, you must call its strategy routine. The strategy routine typically takes a number of parameters, one of which is the function the driver should perform. The DriverInfoStruct contains a far pointer to the strategy routine; your application should store this far pointer and call it directly any time one of the driver's functions is needed. However, because the driver may be put in a different location each time it's loaded, you should not save the pointer in a state file. Note that this scheme of accessing drivers directly can only be implemented in assembly language. Some drivers may provide library interfaces as well as their standard driver interface; this allows routines to be written in C.

GEOS maintains default drivers for the entire system. The types of default drivers are described by GeodeDefaultDriverType ; all the types are shown below. They are called default drivers because the default for each category of driver used by the system is stored in the GEOS.INI file. GEOS will, upon startup, load in the default driver of each category.

GDDT_FILE_SYSTEM
GEOS may use several file system drivers during execution. A file system driver allows GEOS to work on a given DOS (or substitute file access system). The primary driver is considered the default driver.
GDDT_KEYBOARD
The system may only use one keyboard driver during execution; typically, keyboards can differ from country to country. Not all systems will have a keyboard.
GDDT_MOUSE
The mouse should be usable when the system comes up on the screen; the user does not have to manually load a mouse driver with each execution of GEOS. Not all systems will have a mouse.
GDDT_VIDEO
GEOS must know what type of video driver to use prior to attempting to display itself.
GDDT_MEMORY_VIDEO
The Vidmem driver is a video driver that draws to memory (i.e. to bitmaps). It is used primarily for printing but also for editing bitmaps.
GDDT_POWER_MANAGEMENT
A few machines use hardware power management systems (most notably some palmtops and some notebook and laptop machines). In order for GEOS to handle the power management hardware properly, it must load the driver on startup. (Most machines will not use this type of driver.)
GDDT_TASK
Some systems will use a task switcher; in order for GEOS to be a task in one of these, it must have a default task driver.

To retrieve the default that GEOS is using, call the routine GeodeGetDefaultDriver() with the appropriate driver type (a member of the type GeodeDefaultDriverType ). This routine will return the geode handle of the default driver of that type. To set a new default driver for a specified driver type, use GeodeSetDefaultDriver() . This routine takes a geode handle and a driver type and sets the system default for that type. Typically, system defaults will be set only by the Preferences Manager application.

| Up: GEOS SDK TechDocs | Up | Prev: 1.4 Saving and Restoring State | Next: 1.6 Writing Your Own Libraries


Writing Your Own Libraries

Applications and Geodes: 1.6 Geodes: Writing Your Own Libraries

| Up: GEOS SDK TechDocs | Up | Prev: 1.5 Using Other Geodes | Next: 1.7 Working with Geodes

Creating a library geode is a simple step beyond creating a normal application. Libraries can have their own process threads or not; most libraries do not, though some will. To create a library geode, you have to do four things:

  • Declare the geode to be a library.
    In the geode parameters ( .gp ) file, you must declare the type of the geode to be library.
  • Define an entry point routine.
    Create a routine that initializes the library; this routine is called the "entry point routine" and is called each time the library is loaded.
  • Export the entry point routine.
    Add another line in the geode parameters ( .gp ) file to export the library's entry point routine.
  • Set the geode's permanent name properly.
    Most libraries have the extension of their permanent name be "lib." This helps identify it as a library when using Swat. The permanent name is defined in the geode parameters ( .gp ) file.

Defining a Library-the sound.gp File shows an example of the three changes you must make to the geode's parameters file to make it a library.

Code Display 7-1 Defining a Library--the sound.gp File

 # The Sound Library has the following three lines in its geode parameters file
# (sound.gp) different from how they might appear in an application.
#
# The permanent name of the geode might normally be sound.app if it is an
# application. A library typically has "lib" as its name extension.
#
name sound.lib
#
# Declare the type of the geode to be a library. Applications typically have a
# "process" aspect; many libraries do not. Libraries must also be declared to
# have a "library" aspect. (Note that the Sound Library is exceptional in that
# it has both a driver aspect and a library aspect.)
#
type driver, library, single
#
# Export the geode's entry point routine so the kernel may call it when the
# library is loaded. This routine must be defined somewhere in the geode's code.
# Note that it is exported with the "entry" line rather than the standard
# "export" line; this is to distinguish the exported routine as the entry
# point rather than a typical routine.
#
entry SoundEntry
| Up: GEOS SDK TechDocs | Up | Prev: 1.5 Using Other Geodes | Next: 1.7 Working with Geodes


Working with Geodes

Applications and Geodes: 1.7 Geodes: Working with Geodes

| Up: GEOS SDK TechDocs | Up | Prev: 1.6 Writing Your Own Libraries | Next: 1.8 Geode Protocols and Release Levels

The system provides a number of utility routines for getting information and setting attributes of geodes. These are loosely organized throughout the following sections.

Accessing the Application Object

GeodeGetAppObject()

GEOS offers a routine for retrieving the optr of an application's GenApplication object. GeodeGetAppObject() takes the process handle of the Process object of the application. It returns the optr of the application's GenApplication object.

General Geode Information

GeodeFind(), GeodeFindResource(), GeodeDuplicateResource(), GeodeGetInfo(), GeodeGetProcessHandle(), GeodeGetCodeProcessHandle(), ProcInfo()

GeodeFind() returns a geode's handle when given a permanent name and attributes to search on. GEOS will search the active geode list for any geode with the given name and the proper attributes set or clear.

GeodeFindResource() locates a given resource within a geode's file. It must be passed the file handle of the geode, the number of the resource in the file, and an offset within the resource at which the file read/write position should be placed. This routine may only be used on open geode files, and it returns the base offset and size of the resource. You will probably not need to use this routine. To load a resource into a new block of memory, you can call GeodeDuplicateResource() .

GeodeGetInfo() takes a geode handle, a GeodeGetInfoType parameter, and a buffer appropriate for the return information. It returns the appropriate values as specified in the GeodeGetInfoType parameter. (This parameter specifies what type of information is sought about the geode; the routine can return the geode's attributes, geode type, release level, protocol, token ID, or permanent name.) The possible values of GeodeGetInfoType are shown below:

GGIT_ATTRIBUTES
This indicates the geode's attributes should be returned.
GGIT_TYPE
This indicates the geode's type should be returned (type GeodeType ).
GGIT_GEODE_RELEASE
This indicates the geode's release numbers should be returned.
GGIT_GEODE_PROTOCOL
This indicates the geode's protocol numbers should be returned.
GGIT_TOKEN_ID
This indicates the geode's token information should be returned.
GGIT_PERM_NAME_AND_EXT
This indicates the geode's permanent name and extender should be returned.
GGIT_PERM_NAME_ONLY
This indicates the eight characters of the geode's permanent name only should be returned, without the extender characters.

GeodeGetProcessHandle() returns the geode handle of the current process (the owner of the current thread). Another routine, GeodeGetCodeProcessHandle() , returns the handle of the geode that owns the code block from which it was called.

ProcInfo() returns the thread handle of the first thread of a given process.

Managing Geode Event Queues

GeodeAllocQueue(), GeodeFreeQueue(), GeodeInfoQueue(), GeodeFlushQueue(), ObjDispatchMessage(), QueueGetMessage(), QueuePostMessage(), GeodeDispatchFromQueue()

The following routines allocate and manage event queues. These routines are rarely called by applications as event queues are automatically managed for each thread and application.

GeodeAllocQueue() allocates an event queue and returns its handle. GeodeInfoQueue() returns the number of events in a given event queue. GeodeFreeQueue() frees an event queue allocated with the routine GeodeAllocQueue() . It must be passed the handle of the queue to be freed (unhandled events still in the queue will be discarded).

GeodeFlushQueue() flushes all events from one queue and synchronously places them all in another queue (events may not simply be tossed out).

QueueGetMessage() combined with ObjDispatchMessage() removes the first event from the given event queue and handles it via a callback routine. A far pointer to the callback routine in memory must be passed. Typically these will be used only by the assembly ObjMessage() routine used by the kernel; some other applications of this routine may be used, though. For example, the sound driver uses a note queue unassociated with objects and messages. The callback routine therefore gets the "event" (note) and pretends it's handling a message.

QueuePostMessage() adds an event to the specified queue.

| Up: GEOS SDK TechDocs | Up | Prev: 1.6 Writing Your Own Libraries | Next: 1.8 Geode Protocols and Release Levels


Geode Protocols and Release Levels

Applications and Geodes: 1.8 Geodes: Geode Protocols and Release Levels

| Up: GEOS SDK TechDocs | Up | Prev: 1.7 Working with Geodes | Next: 1.9 Temporary Geode Memory

Every GEOS geode and VM file has both a release level and a protocol level as extended attributes of the file. These two items help ease the transitions for both programmer and user when changes are made to applications, libraries, drivers, system software, etc. To control release and protocol numbers, use the GREV tool and a REV file as described in the Grev section of the Using Tools chapter.

Release Numbers

The release number is a ReleaseNumber structure which consists of four components: The RN_major and RN_ minor numbers are the most significant. The RN_ change and RN_ engineering numbers are less significant and are used primarily to indicate non-released or running upgrade types of changes to the geode. The ReleaseNumber of a geode or VM file is stored in the file's FEA_RELEASE extended attribute, and its structure is shown below:

typedef struct {
    word    RN_major;
    word    RN_minor;
    word    RN_change;
    word    RN_engineering;
} ReleaseNumber;

The contents of the release number are up to the particular geode and are product-specific. Release numbers are not used by GEOS for compatibility checking or any other validation of files, though they are used during installation procedures.

To retrieve the release number of a given geode, use the routine GeodeGetInfo() . Release levels should be set at compile time and are not changeable at run-time.

Protocol Numbers

The protocol number is a structure of type ProtocolNumber stored in the file's FEA_PROTOCOL extended attribute. Each GEOS geode and data file has a protocol level associated with it. The protocol level is used for compatibility checking for both geodes and documents.

The ProtocolNumber structure consists of two parts, the major protocol and the minor protocol. This structure is shown below:

typedef struct {
    word    PN_major;
    word    PN_minor;
} ProtocolNumber;

Differences in protocol levels indicate incompatibilities between two geodes, between a geode and its document format, or between a geode and its state file format. If the major protocols are different, the two items are not compatible at all (unless special provisions are made). If the minor protocol is greater than expected, some incompatibility may exist but should not affect the program. You should increment a geode's or document's protocol whenever a change is made.

If a change to a library is upward-compatible, only the minor protocol needs to be incremented. For example, if a library acquires a new function but the library's entry points are undisturbed, the minor protocol should be incremented and the major protocol left as is. If the new function causes relocation of the entry point numbers, however, the major protocol must also be incremented.

An application's protocol must be increased whenever a change will affect the application's state files. If you make a change to an application, for example, and the user has old state files, either the changes in the application can be replaced with the old information, or the state file will cause an incompatibility with unpredictable results. If the change to the application is simply functional, increment the minor protocol. If the change is to any part of a UI resource or to any other item saved to a state file, increment the major protocol. State files will be loaded if minor protocols are different and will be ignored if major protocols are different.

If an application's document format changes, you should make sure the new documents are not loaded by old applications or vice versa (unless you take the necessary conversion steps). When opening a document, you can check its protocol by checking the document file's extended attribute FEA_PROTOCOL. If the protocol level needs to be changed (after conversions have been done, of course), you can change them by setting FEA_PROTOCOL. (See the File System chapter.)

A few examples of when minor and major protocols should be incremented follow. Keep in mind that this list is by no means exhaustive.

  • If you add a data structure to a document format and older versions of the application will still be able to open the document, increment the document's minor protocol only.
  • If you change the instance data structure of an object that gets saved to the document, increment the major protocol, as the new methods will be using the wrong offsets to access data in the old object.
  • If you add a new class to your application and don't disturb any of the other classes' entry points (i.e. add the class at the end), increment the minor protocol only.
  • If you add a new class, disturbing entry points, increment the major protocol.
  • If you add or delete a resource, increment the major protocol.
  • If you add a chunk or object to a resource that gets saved to state files, increment the major protocol.
  • If you change an object's flags (e.g. mark it ignore-dirty), increment the major protocol. Otherwise, the flags will be restored from the state file and will override the changes you made.
| Up: GEOS SDK TechDocs | Up | Prev: 1.7 Working with Geodes | Next: 1.9 Temporary Geode Memory


Temporary Geode Memory

Applications and Geodes: 1.9 Geodes: Temporary Geode Memory

| Up: GEOS SDK TechDocs | Up | Prev: 1.8 Geode Protocols and Release Levels | Next: 2 Creating Icons
GeodePrivAlloc(), GeodePrivFree(), GeodePrivWrite(), GeodePrivRead()

Every geode in the system has a "private data" area, a space set aside in its core block. This private data is used primarily by library geodes, when each of the library's clients uses its own copy of a particular data structure that gets manipulated by the library. The private data mechanism is used in the GEOS implementation of malloc() , for example (though you need not know this to use malloc() ).

Private memory may be allocated, written to, read from, and freed by the library. The library does not have to allocate a block for each geode and maintain its own handle table; the use of the GeodePriv...() routines automatically manages this.

GeodePrivAlloc() reserves a given number of contiguous words for the library within the private data of all geodes in the system. The memory space is reserved but is not actually allocated for a given geode until it is used (written to); this is done for optimization purposes. This routine will return a tag pointing to where the reserved words begin. This tag is used when reading, writing, or freeing the private data. If the memory could not be allocated, the routine will return zero.

GeodePrivWrite() and GeodePrivRead() write to and read from the private data space. They take similar parameters: a geode handle, the tag as returned by GeodePrivAlloc() , the total number of words to be written or read, and a pointer to a locked or fixed buffer. In GeodePrivWrite() , the buffer will be passed containing the words to be written; in GeodePrivRead() , the buffer will be passed empty and returned containing the words read.

Typically, the geode handle passed will be zero; this indicates that the current process (which will be the library's current client) will be the owner of the private data affected. Because the library code will be executing in the thread of a given application, the application geode will be the only one having its private data affected. Thus, a library can use the same code to store different data for each geode that uses it; neither the library nor the geode needs to know that other geodes are also using the same routines.

GeodePrivFree() frees a given number of words from all geodes' private data. It needs to be passed only the number of words to be freed and the tag as returned by GeodePrivAlloc() .

| Up: GEOS SDK TechDocs | Up | Prev: 1.8 Geode Protocols and Release Levels | Next: 2 Creating Icons


Creating Icons

Applications and Geodes: 2 Creating Icons

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Geodes | Next: 3 Saving User Options

Every geode can have an icon associated with it. Typically, only applications will have special icons; other geodes (libraries and drivers) generally use one of the system icons or the icon of the application they're primarily used by.

An application's icon is stored in two places: It is defined and stored within the application's extended attributes in its .GEO file. It is also stored in a database file maintained by the UI, called the token database .

The GEOS development kit includes an icon editor tool so you can easily create icons of various color and resolution characteristics and install them into your applications.

The Token Database

Managing the Token Database File

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Geodes | Next: 3 Saving User Options


The Token Database

Applications and Geodes: 2.1 Creating Icons: The Token Database

| Up: GEOS SDK TechDocs | Up | Prev: 2 Creating Icons | Next: 2.2 Managing the Token Database File
TokenOpenLocalTokenDB(), TokenCloseLocalTokenDB()

The token database is stored in its own file. Each entry represents a single icon or series of icons that can be used with any number of files. The token database file is managed by the UI and the kernel; you should have no reason to access its internals directly, and most applications will never need to use any of the token routines except to install their document icons.

Some systems may have shared token database files; this is controlled by the INI file key sharedTokenDatabase in the paths category. Most often a shared database file exists on a network drive and may be supplemented with a local token database. By default, if a shared database file exists, it will be opened read-only in addition to the read/write local file. You can open and close only the local database file, however, with the routines TokenOpenLocalTokenDB() and TokenCloseLocalTokenDB() .

Every GEOS file has a token. The token is an index into the token database. When GeoManager scans a directory, it grabs the token from each file and searches through the token database file for it. If a match is found, GeoManager selects the proper icon and displays it; if no match is found or if the file's token is invalid, GeoManager will launch the application in engine mode and request that it install its token in the database.

For non-GEOS files, GeoManager uses the three extension characters of the file's name as a pseudo-index. For each extension (e.g. .COM, .EXE, .DOC, .BAT, etc.), GeoManager uses a single icon. Which icon is used can be set in the GEOS.INI file if a user wishes, but GeoManager will normally select the default DOS icon (of which there are two: one for executables and one for non-executables).

The index into the token database consists of two parts and is of type GeodeToken . This structure contains four text characters as well as the manufacturer ID number of the geode's manufacturer. The structure's definition is shown below:

typedef struct {
    TokenChars				GT_chars;
    ManufacturerID				GT_manufID;
} GeodeToken;
typedef char TokenChars[TOKEN_CHARS_LENGTH];

This structure is created and filled automatically in the geode's .geo file header by the Glue linker, which takes the values from the geode's geode parameters ( .gp ) file. The two fields used from the .gp file are tokenchars for the four characters and tokenid for the manufacturer ID.

A GeodeToken structure in the token database file can also be filled in when GeoManager scans a directory. If the header of a particular application's .geo file does not have a recognized token, GeoManager will launch the application in "engine" mode, loading its GenApplication object. It will request that the application object then install its icon into the token database file ( GenApplicationClass knows how to do this).

The token database file contains one entry for each token that has been installed. Each time a new token and icon is encountered, a new entry is added by the UI. This happens automatically when GeoManager scans a directory.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Creating Icons | Next: 2.2 Managing the Token Database File


Managing the Token Database File

Applications and Geodes: 2.2 Creating Icons: Managing the Token Database File

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 The Token Database | Next: 3 Saving User Options
TokenDefineToken(), TokenGetTokenInfo(), TokenLookupMoniker(), TokenLoadMonikerBlock(), TokenLoadMonikerChunk(), TokenLoadMonikerBuffer(), TokenRemoveToken(), TokenLoadTokenBlock(), TokenLoadTokenChunk(), TokenLoadTokenBuffer(), TokenLockTokenMoniker(), TokenUnlockTokenMoniker(), TokenGetTokenStats(), TokenListTokens()

In nearly all cases, you will create your application's icon with the icon tool and not worry about it again. However, the following routines allow you to add, change, access, and remove entries from the token database.

TokenLoadTokenBlock() , TokenLoadTokenChunk() , and TokenLoadTokenBuffer() load a TokenEntry structure into memory (a newly allocated block, a newly allocated chunk, or a locked buffer). The TokenEntry structure contains information about the token, the geode's release number, and the geode's protocol number. This structure does not actually contain the monikers used for the icon.

TokenLookupMoniker() gets the specific moniker of a token entry given the display type (CGA, EGA, VGA, etc.), the entry's GeodeToken structure, and search flags. If the moniker is found, the entry identifier (database group and ID numbers) of the moniker are returned. You can use these return values to lock the moniker into memory (see below).

TokenLockTokenMoniker() locks a moniker into memory given its entry identifier. This routine returns a pointer to a locked block and the chunk handle of the chunk containing the locked moniker. A moniker should always be locked before it is drawn; this keeps it from moving in memory while it is being accessed. The routine TokenUnlockTokenMoniker() unlocks a previously locked moniker when given the moniker's segment address. This unlocks the entire block, not just the individual moniker.

TokenLoadMonikerBlock() , TokenLoadMonikerChunk() , and TokenLoadMonikerBuffer() load a specific moniker into memory from the token database file (into a newly allocated block, a newly allocated chunk, or a locked buffer). It takes the same parameters as TokenLookupMoniker() but returns the handle and chunk handle of the loaded moniker. If using this routine, simply lock the memory block rather than using TokenLockTokenMoniker() .

TokenGetTokenInfo() finds a token when passed its tokenchars and tokenid and returns the token's flags. If no token exists with the passed characteristics, it will return an error flag.

TokenDefineToken() adds a new token and its moniker list to the token database. If the given token already exists, the new one will replace the old one. The token identifier ( tokenchars , tokenid ), handle and chunk handle of the moniker list, and flags of the new token must be passed.

TokenRemoveToken() removes a given token and its moniker list from the token database file. It returns only a flag indicating whether the token was successfully removed or not.

TokenListTokens() returns a list of the tokens in the token database. It is passed three arguments:

  • A set of TokenRangeFlags , which specifies which tokens should be returned. The following flags are available:
TRF_ONLY_GSTRING
Return only those tokens which are defined with a GString.
TRF_ONLY_PASSED_MANUFID
Return only those tokens which match the passed manufacturer's ID.
  • A number of bytes to reserve for a header.
  • A manufacturer ID. This field is ignored if TRF_ONLY_PASSED_MANUFID was not passed.

TokenListTokens() allocates a global memory block and copies all the specified tokens into that block. It leaves a blank space at the beginning of the block; this space is the size specified by the second argument. The rest of the block is an array of GeodeToken structures. TokenListTokens() returns a dword. The lower word of the return value is the handle of the global memory block; the upper word is the number of GeodeToken structures in that block.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 The Token Database | Next: 3 Saving User Options


Saving User Options

Applications and Geodes: 3 Saving User Options

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Creating Icons | Next: 4 General System Utilities

Almost all users enjoy configuring their systems to their own tastes, whether it's setting the background bitmap or choosing a default font. Most applications will, therefore, want to provide a way for the user to choose and save various options for the application.

Generic objects have this capability built in. If you set them up for saving options, they will automatically set and maintain the saved options, making sure the options are set properly whenever the application is launched. However, sometimes you will want to set additional options not managed by UI objects. This is not difficult to do in GEOS: You can have your application save its options directly to the local initialization file, GEOS.INI.

Saving Generic Object Options

The GEOS.INI File

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Creating Icons | Next: 4 General System Utilities


Saving Generic Object Options

Applications and Geodes: 3.1 Saving User Options: Saving Generic Object Options

| Up: GEOS SDK TechDocs | Up | Prev: 3 Saving User Options | Next: 3.2 The GEOS.INI File

All appropriate generic UI objects have the ability to save their options. For example, a properties GenInteraction could save which of its options are on and which are off when the user presses a "save options" trigger.

To save generic object options, you have to do two basic things:

  • Create an Options menu
    Typically, you will create a menu for user options. This menu should be declared of type GIGT_OPTIONS_MENU as shown in Saving Generic Object Options .
  • Use a special GCN list
    All objects that want to save options must be put on a special GCN list in the application's GenApplication object. The list type should be either GAGCNLT_STARTUP_LOAD_OPTIONS (if the options should be loaded at the time of application startup) or GAGCNLT_SELF_LOAD_OPTIONS (if the options should be loaded when the object deems it necessary, typically when the object is first displayed).

Saving Generic Object Options shows an example of how objects should be declared for saving their options.

Code Display 7-2 Saving Generic Object Options

/* The GenApplication object must declare a GCN list of the type appropriate for
 * the options being saved (or both types if the application uses both types).
 * This GenApplication declares a list of objects whose options do not have to be
 * loaded at startup. */
@object GenApplicationClass SampleApp = {
    GI_visMoniker = "Sample Application";
    GI_comp = SamplePrimary;				/* Primary window object is the only child. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = SamplePrimary;
        /* The above list is to declare windowed objects that must appear
         * when the application is opened and made usable. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) =
                    SampleController;
        /* The above list is used for generic objects that save their
         * options but do not need their options loaded at startup. */
}
/* Some applications that have generic objects save their own options might not
 * have a special Options menu but may just have a trigger somewhere for saving
 * options. In any case, the "save options" trigger sends MSG_META_SAVE_OPTIONS to
 * its GenApplication object. If you use an Options menu with GIGT_OPTIONS_MENU
 * set, this will automatically be built into the menu.
 * The SampleOptionsMenu is a child of SamplePrimary, not shown. */
@object GenInteractionClass SampleOptionsMenu = {
    GI_comp = SampleToolbox, SampleToolControl, SampleSaveOptsTrigger;
    GII_visibility = GIV_POPUP;					/* Make it a menu. */
    ATTR_GEN_INTERACTION_GROUP_TYPE = (GIGT_OPTIONS_MENU);
}
/* The other objects (controllers) are not shown here. Just the "save options"
 * trigger, which sends MSG_META_SAVE_OPTIONS to the GenApplication object. */
@object GenTriggerClass SampleSaveOptsTrigger = {
    GI_visMoniker = `S', "Save Options";
    GTI_destination = SampleApp;
    GTI_actionMsg = MSG_META_SAVE_OPTIONS;
}
| Up: GEOS SDK TechDocs | Up | Prev: 3 Saving User Options | Next: 3.2 The GEOS.INI File


The GEOS.INI File

Applications and Geodes: 3.2 Saving User Options: The GEOS.INI File

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Saving Generic Object Options | Next: 4 General System Utilities

GEOS can use multiple initialization files in network situations, but only the local GEOS.INI is editable. The local GEOS.INI (referred to hereafter simply as GEOS.INI or "the INI file") is read first into a moveable, swappable buffer, and it contains the path names of any other INI files to be used. The other INI files are subsequently loaded into other buffers. When the kernel searches for a specific entry in one of the INI files, it looks first in the local INI's buffer and then in any others in the order they were loaded. When it reaches what it wants, it stops searching. Thus, multiple entries are allowed but are not used, and the local INI file has precedence over all others.

Configuration of the INI File

The GEOS.INI file has a very specific format and syntax. It is segmented into categories , each of which contains several keys that determine how GEOS will perform. (For an example of an INI file, see Example GEOS.INI File Entries .) Note that typically the GEOS.INI file should not be accessed directly because its format is subject to change.

A category is essentially a group of keys. Certain geodes will work with their own categories, and certain categories will be used by several geodes. For example, the system category is used by the kernel and the UI to determine several of the default system settings. Each category is set in GEOS.INI by putting its name within square brackets. The text within the brackets is case-insensitive and ignores white space, so [My category], [mycategory], and [MY CATEGORY] are all equivalent.

A key is any setting that the system or an application can recognize and assign a value to. A single key may exist in several different categories; since both category and key define an entry, the entries will be unique if either key or category is different. Keys may be text strings, integers, or Boolean values, and every key is identified by its name, which is an ASCII string. Data may also be read from and written to the file in binary form; the kernel will automatically convert this into ASCII hexadecimal for storage and will revert it to binary during retrieval.

Both category names and key fields are called "entries." Each entry may exist on a single line or may cover several lines. An entry that contains carriage returns is technically known as a "blob." Each blob will automatically be enclosed in curly braces when it is written into the file. Any blob that contains curly braces will automatically have backslashes inserted before the closing braces so GEOS doesn't mistake them for the blob delimiters.

Comments may be added to the INI file by putting a semicolon at the beginning of the line. The file has several standard categories and keys that can be set within them. These are detailed in the INI File chapter .

Code Display 7-3 Example GEOS.INI File Entries

; The category "system" is used by the kernel and the UI to set certain system
; defaults such as the number of handles, the default system font and size, and
; the types of memory drivers to be loaded.
[system]
; The handles key is assigned an integer that determines the number of handle
; spaces allocated for the system's Handle Table.
handles = 2500
; The font key is assigned a character string that represents a file name
; or a list of file names separated by spaces.
; The listed file(s) will be read in as the font driver geode.
font = nimbus.geo
; The memory key is assigned a blob of text containing all the names of the memory
; drivers available to the system.
memory = {
disk.geo
emm.geo
xms.geo
}
; The category "MyApp's Category" is set for example only. It is used by the MyApp
; application.
[My App's Category]
; The myappHiScore key is an integer key set by the MyApp application.
myappHiScore = 52
; The myappBoolean key is a Boolean value. Booleans are case-insensitive, so True,
; true, and TRUE are all equated to "true". This is actually a Boolean value and
; is translated by the read and write routines that work with Boolean values.
myappBoolean = true
; The myappHiName is a text string that, in this case, contains carriage returns,
; backslash characters, and curly brace characters. The original text looked like
; this:
;		this is a multi-line
;		blob of text with curly
;		brace ({,}) characters }} in it
; It is automatically given backslashes in front of the closing braces, and it
; is automatically surrounded with curly braces.
myappHiName = {this is a multi-line
blob of text with curly
brace ({,\}) characters \}\} in it}

Managing the INI File

InitFileSave(), InitFileRevert(), InitFileGetTimeLastModified(), InitFileCommit()

Because the INI file is common to all geodes and to all threads in the system, only one thread at a time may access it. This synchronization is handled by the kernel whenever a routine to read from or write to the INI file is used. Additionally, the INI file is loaded into a buffer when the system is first run; all operations on the INI file actually work on this buffer, and the buffer may be flushed to disk only by the kernel.

There are some general utility routines useful for managing the INI file. One saves a backup of the file, another reverts it from the last backup, the third checks the time the file was last modified, and the fourth commits any pending INI file changes to disk.

To update the backup copy of GEOS.INI, use the routine InitFileSave(). It requires no parameters and returns an error flag if the file could not be saved. InitFileRevert() reverts the GEOS.INI file to its state the last time it was backed up. This routine takes no parameters, and it returns an error flag if the revert can not be accomplished.

InitFileGetTimeLastModified() returns the system counter's value stored the last time GEOS.INI was modified.

InitFileCommit() takes all the changes made since the file was last modified and flushes them to disk. This commits all the changes and should be used only from the kernel. It should not be used by applications.

Writing Data to the INI File

InitFileWriteData(), InitFileWriteString(), InitFileWriteStringSection(),
InitFileWriteInteger(), InitFileWriteBoolean()

GEOS provides five routines to write to the INI file, one for each of the allowable data types. Each of these routines will first gain exclusive access to the local INI buffer, then locate the appropriate category for writing. After writing the key and its value, the routine will relinquish exclusive access to the buffer, allowing other threads to write into it. Writing a category or key that does not exist in the local INI file will add it to the file. If you use a routine to write keys or categories to the INI file, be sure to first convert the strings to the acceptable ASCII hex format using InitFileMakeCanonicKeyCategory().

Each of these routines takes at least three arguments: The category is specified as a null-terminated character string; a pointer to the string is passed. The key name is also specified as a null-terminated character string; again, a pointer to the string is passed. The third parameter is specific to the routine and contains the value to which the key will be set.

InitFileWriteData() writes a number of bytes into the INI buffer, and it takes four parameters. The additional parameter is the size of the data. The data will be converted into ASCII hexadecimal when the file is saved and will be converted back when the key is read.

InitFileWriteString() takes a pointer to the null-terminated character string to be written. If the character string contains carriage returns or line feeds, it will automatically be converted into a blob.

InitFileWriteStringSection() writes a new string section (a portion of a blob) into the specified entry. The specified entry must be a blob already, and the string section will be appended to the blob. A string section is a line of a blob delineated by line feeds or carriage returns.

InitFileWriteInteger() takes as its third argument the integer to be written.

InitFileWriteBoolean() takes a Boolean value. A zero value represents false, and any nonzero value represents true. When looking at the INI file with a text editor, the Boolean value will appear as a text string of either "true" or "false"; it will, however, be interpreted as a Boolean rather than a text string.

Getting Data from the INI File

InitFileReadDataBuffer(), InitFileReadDataBlock(), InitFileReadStringBuffer(), InitFileReadStringBlock(), InitFileEnumStringSection() InitFileReadStringSectionBuffer(), InitFileReadStringSectionBlock(), InitFileReadInteger(), InitFileReadBoolean()

When you want to check what a key is set to in the INI file, you should use one of the InitFileRead...() routines. These search the local INI file first and then each of the additional INI files in the order they were loaded. They will return the first occurrence of a given key, so if the key exists in both your local INI file and another INI file, these routines will return only the local value.

All of these routines take at least two parameters. The first is the category of the entry to be retrieved; this is stored as a null-terminated ASCII string, and a pointer to the string is passed. The second is the key of the entry. This, too, is stored as a null-terminated ASCII string, and a pointer to the string is passed.

InitFileReadBoolean() returns the Boolean value of the given key. If the key is set "false," a value of zero (FALSE) will be returned. If the key is set "true," a nonzero value will be returned (-1, the constant TRUE).

InitFileReadInteger() returns the integer value of the given key.

InitFileReadDataBuffer() and InitFileReadDataBlock() both return the data bytes stored in the given key. The first, however, takes the address of a buffer already allocated and puts the data into the buffer. The second allocates a new block on the heap and puts the data into it. If you don't know the size of the data, you should use InitFileReadDataBlock() .

InitFileReadStringBuffer() and InitFileReadStringBlock() both return the null-terminated string stored in the given key. In both cases, curly braces will be stripped off of blobs and backslash characters will be removed if appropriate. The first, however, takes the address of a buffer already allocated and puts the string in the buffer. The second allocates a new block on the heap and returns the string in it. If you don't know the approximate size of the string, use InitFileReadStringBlock() .

InitFileReadStringSectionBuffer() and its counterpart InitFileReadStringSectionBlock() both return a null-terminated section of the string stored in the given key. A string section is defined as any number of contiguous printable ASCII characters and is delimited by carriage returns or line feeds. These routines take the number of the string section desired and return the section (if it exists). InitFileReadStringSectionBuffer() takes the address of a buffer already allocated on the heap and returns the string section in the buffer. InitFileReadStringSectionBlock() allocates a new block on the heap and returns the string section in it. You should use this routine if you don't know the approximate size of the string section.

InitFileEnumStringSection() enumerates the specified blob, executing a specified callback routine on each string section within the blob.

Deleting Items from the INI File

InitFileDeleteEntry(), InitFileDeleteCategory(), InitFileDeleteStringSection()

Besides reading and writing data, you can delete categories and keys that were previously entered. InitFileDeleteEntry() takes pointers to both the null-terminated category name and the null-terminated key name and deletes the entry. InitFileDeleteCategory() takes only a pointer to the null-terminated category name and deletes the entire category, including all the keys stored under it.

In addition, you can delete a single string section from a specified blob. InitFileDeleteStringSection() takes the category and key names of the blob as well as the index of the string section, and it deletes the string section. If the string section does not exist or if either the key or category can not be found, the routine will return an error flag.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Saving Generic Object Options | Next: 4 General System Utilities


General System Utilities

Applications and Geodes: 4 General System Utilities

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Saving User Options | Next: 5 The Error-Checking Version

The kernel provides a number of routines that fulfill general needs for system utilities. These range from setting the system's date and time to retrieving the amount of swap memory used to shutting down the system in order to execute a DOS-based program.

System Clock

Using Timers

System Statistics and Utilities

Shutting the System Down

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Saving User Options | Next: 5 The Error-Checking Version


System Clock

Applications and Geodes: 4.1 General System Utilities: System Clock

| Up: GEOS SDK TechDocs | Up | Prev: 4 General System Utilities | Next: 4.2 Using Timers
TimerGetDateAndTime(), TimerSetDateAndTime(), TimerGetFileDateTime()

The system clock reflects the current date and time of day. TimerGetDateAndTime() takes a pointer to a TimerDateAndTime structure and fills it with the current year, month, day, day of week, hour, minute, and second. TimerGetFileDateTime() is a similar function--it gets the current date and time, returning them in structures used when working with files. TimerSetDateAndTime() sets the current date and time to the values in the passed structure. It is unusual for any application other than the Preferences Manager to change the system clock.

| Up: GEOS SDK TechDocs | Up | Prev: 4 General System Utilities | Next: 4.2 Using Timers


Using Timers

Applications and Geodes: 4.2 General System Utilities: Using Timers

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 System Clock | Next: 4.3 System Statistics and Utilities
TimerStart(), TimerStop(), TimerSleep(), TimerGetCount()

If your geode needs an action to happen on a timed basis, you will want to use a timer. GEOS lets you set up timers that will call a routine or send a message after a given interval has passed. GEOS offers four types of timers:

  • One-shot
    A one-shot timer counts a certain number of "ticks." (There are 60 ticks in a second.) It will call a routine or send a message after the specified number of ticks have been counted.
  • Continual
    A continual timer counts a certain number of ticks and then resets and counts again. Each time it reaches the specified number of ticks, it calls a routine or sends a message.
  • Millisecond
    A millisecond timer is a one-shot timer that counts in milliseconds rather than ticks and calls a routine when time is up (it does not send a message).
  • Sleep
    A sleep timer causes the thread that creates it to sleep for a certain number of ticks. The sleep timer is unlike the others in that it does not send a message or call a routine when time is up; it only awakens the sleeping thread.
  • Real-time
    A real-time timer is passed the number of days since 1980, the hour, and the minute of the event. On hardware platforms that support such a thing, GEOS will wake up when the real-time timer goes off. (All other timers are ignored under these conditions.)

All timers except sleep timers are created and started with the routine TimerStart() , and continual timers can be destroyed with TimerStop() . TimerStop() may also be used to prematurely stop a one-shot timer, though if the one-shot sent a message, the message could be in the recipient's queue even after TimerStop() was called. Sleep timers are created and started with TimerSleep() , and they do not need to be stopped.

An additional routine, TimerGetCount() , returns the current system counter. The system counter contains the number of ticks counted since GEOS was started.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 System Clock | Next: 4.3 System Statistics and Utilities


System Statistics and Utilities

Applications and Geodes: 4.3 General System Utilities: System Statistics and Utilities

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Using Timers | Next: 4.4 Shutting the System Down
SysStatistics(), SysGetInfo(), SysGetConfig(), SysGetPenMode(), SysGetDosEnvironment()

Occasionally, a geode will need to query the kernel about the state or configuration of the system. GEOS offers five routines for this:

SysStatistics() returns a structure of type SysStats . This structure contains information about how busy the CPU is, how much swap activity is going on, how many context switches occurred in the last second, how many interrupts occurred in the last second, and how many runnable threads exist. You can not set or otherwise alter this structure.

SysGetInfo() returns a particular statistic about the current system status. It can return a number of different statistics including the CPU speed, the total number of handles in the handle table, the total heap size, the total number of geodes running, and the size of the largest free block on the heap. Most of the information your application can request with this routine can be garnered from the Perf performance meter; it is easiest to use Perf when debugging, though a few geodes will need to know this information.

SysGetConfig() returns information about the current system's configuration including the processor type and machine type. It also returns flags about the particular session of GEOS including whether this session was started with Swat, whether a coprocessor exists, and other information.

SysGetPenMode() returns TRUE if GEOS is currently running on a pen-based machine.

SysGetDosEnvironment() returns the value of a DOS environment variable. It is passed a string representing the variable name and returns a string representing the value.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Using Timers | Next: 4.4 Shutting the System Down


Shutting the System Down

Applications and Geodes: 4.4 General System Utilities: Shutting the System Down

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 System Statistics and Utilities | Next: 5 The Error-Checking Version
DosExec(), SysShutdown()

There are two ways for a geode to shut down GEOS. The first, DosExec() , shuts the system down to run a program under DOS, returning after the DOS program has finished--unless a task-switch driver is in use, in which case the system will create a new task and cause the task-switcher to switch to the new task. The second, SysShutdown() , forces the system to shut itself down completely. Neither of these routines is commonly used by anything other than the kernel, GeoManager, special "launcher" programs, or the UI. Their use by other libraries or applications is discouraged unless absolutely necessary.

DosExec() takes several parameters including the pathname of the DOS program to be run, arguments for the program, an optional disk handle of the disk that contains the program to be run, the optional directory and disk handle in which the program should be executed, and a record of DosExecFlags . If the return value is nonzero, an error occurred in loading the DOS program, and you can use ThreadGetError() to check what error occurred. Note that DosExec() always returns. Applications should not rely on DosExec() shutting the system down; if a task switcher is present, GEOS will be swapped out rather than shut down.

SysShutdown() causes GEOS to exit in one of several ways. This routine should be passed a shutdown mode. If the mode is SST_CLEAN, SST_RESTART, SST_SUSPEND, or SST_CLEAN_FORCED, the routine will return; otherwise, it will not return and the shutdown will commence. If SST_CLEAN is passed, the shutdown may be aborted after SysShutdown() returns. You can have SysShutdown() cause GEOS to reboot itself after shutting down (as the Preferences Manager application does for certain preferences settings), but this starts GEOS fresh. This routine is very rarely used by anything other than the UI, the kernel, or the Preferences Manager application.

If something else (typically the UI or task switcher) shuts the system down, objects that register for shutdown notification will receive MSG_META_CONFIRM_SHUTDOWN . The application should call SysShutdown() with the mode SST_CONFIRM_START; this allows the object to have exclusive rights for asking the user to confirm the shutdown (when the object is finished with the user interaction, it can call SysShutdown() with SST_CONFIRM_END to release exclusive access). This is useful if your application or library has an ongoing operation and wants to verify the shutdown with the user.

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 System Statistics and Utilities | Next: 5 The Error-Checking Version


The Error-Checking Version

Applications and Geodes: 5 The Error-Checking Version

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 General System Utilities | Next: 6 Inter-Application Communication

GEOS has two versions of its system software. The normal version as shipped retail is the "non-error-checking" version. The other, used for debugging applications and other geodes, is called the "error-checking" version, or EC version . Nearly all components of the system software exist in both versions--the kernel, the UI, libraries, drivers, etc.

Together, Swat and the EC version provide extensive and superb debugging power. The EC version allows you to call special error-checking routines to check the integrity of handles, resources, memory, files, and other things. These error-checking routines all begin with EC; for more information, see their entries in the Routine Reference Book. In addition to the full error checking provided by the EC versions of the system geodes, you can add your own error-checking code to your programs.

Adding EC Code to Your Program

Special EC Routines

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 General System Utilities | Next: 6 Inter-Application Communication


Adding EC Code to Your Program

Applications and Geodes: 5.1 The Error-Checking Version: Adding EC Code to Your Program

| Up: GEOS SDK TechDocs | Up | Prev: 5 The Error-Checking Version | Next: 5.2 Special EC Routines

When compiling your code, you can include certain extra lines in either the EC version or the non-EC version of your program. For example, during debugging you want your program to check the validity of each memory handle before locking the memory block; for the final version of your program, however, you don't want this validity check because it slows down your program. You can easily add a few instructions that will be compiled only into the EC version.

GEOS provides five macros, listed below, for version-specific instructions. These macros must be treated as statements; they may not be used in expressions.

EC
This macro adds the specified line of code to the EC version only. When the non-EC version is compiled from the same code, the line will be left out.
EC_ERROR
This macro halts the execution of the EC version of a program by calling FatalError() with a specified error code. The call to FatalError() will not be included in the non-EC version.
EC_ERROR_IF
This macro works like EC_ERROR, above, but it also allows you to set a condition for whether FatalError() will be called. If the condition is met, FatalError() will be called.
NEC
This macro adds the specified line of code to the non-EC version but leaves it out of the EC version. (It is the converse of the EC macro.)
EC_BOUNDS
This macro checks the validity of a specified address (pointer) by calling the ECCheckBounds() routine. If the pointer is out of bounds, ECCheckBounds() will call FatalError() . This macro may only add the bounds check to the EC version.

An example of use of these macros is shown in EC Macros .

Code Display 7-4 EC Macros

/* This code display shows only the usage of these macros; assume that each
 * line shown below exists within a particular function or method. */
/* The EC macro adds a line of code to the EC version. Its format is
 *	EC(line)		where line is the line of code to be added.
 * Note that the NEC macro is similar. */
    EC( @call MyErrorDialogBox::MSG_MY_ERR_PRINT_ERROR(); )
    NEC( @call MyErrorDialogBox::MSG_MY_ERR_PRINT_NO_ERROR(); )
/* The EC_ERROR macro adds a call to FatalError() to the EC version. Its format is
 *	EC_ERROR(code)		where code is the error code to be called. */
    EC_ERROR(ERROR_ATTR_NOT_FOUND)
/* The EC_ERROR_If macro is similar to EC_ERROR but is conditional. Its format is
 *	EC_ERROR_IF(test, code)			where test is a Boolean value and code is the
 * error code to be called. */
    lockVariable = MyAppCheckIfLocked();					/* TRUE if inaccessible. */
    EC_ERROR_IF(lockVariable, ERROR_ACCESS_DENIED)						/* Error if inaccessible. */
/* The EC_BOUNDS macro adds a call to ECCheckBounds() to the EC version.
 * Its format is
 *	EC_BOUNDS(addr)		where addr is the address to be checked. */
    myPointer = MyAppGetMyPointer();
    EC_BOUNDS(myPointer)
| Up: GEOS SDK TechDocs | Up | Prev: 5 The Error-Checking Version | Next: 5.2 Special EC Routines


Special EC Routines

Applications and Geodes: 5.2 The Error-Checking Version: Special EC Routines

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Adding EC Code to Your Program | Next: 6 Inter-Application Communication
SysGetECLevel(), SysSetECLevel(), SysNotify(), EC...(), CFatalError(), CWarningNotice()

While using Swat and the EC version, you can set and retrieve the current level of error checking employed. The level is set with a record of flags, each of which determines whether a certain type of checking is turned on. This record is of type ErrorCheckingFlags . You can retrieve it with SysGetECLevel() and set it with SysSetECLevel() .

SysNotify() puts up the standard error dialog box--white with a black frame. This routine is available in both the EC and the non-EC versions of the kernel. This is the box used by the kernel to present unrecoverable errors to the user. You can also call it up and allow the user any of five options: retry, abort, continue, reboot, or exit. Usually, this dialog box is used only for errors, but it can be used for other user notification as well. (Note that with the "exit" and "reboot" options, this routine will not return. Note also that only certain combinations of the five options are supported.)

The CFatalError() and CWarningNotice() routines provide run-time and compile-time error or warning messages. The CFatalError() routine calls the kernel's FatalError() function, which puts up an error dialog box and, in Swat, causes Swat to hit a breakpoint so you can debug the error. CWarningNotice() may be put in error-checking code to make the compiler put up a compile-time warning note.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Adding EC Code to Your Program | Next: 6 Inter-Application Communication


Inter-Application Communication

Applications and Geodes: 6 Inter-Application Communication

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 The Error-Checking Version | Next: 6.1 IACP Overview

Applications do not usually need to communicate with each other. Most applications will interact only with the kernel and with libraries; such applications don't even notice if any other applications are running. However, a few applications will need to communicate with other applications.

For example, many desktop-managers provide a "print file" command; the user selects a file's icon, then chooses this command to print out the appropriate file. The desktop manager will have no way of knowing how to print the file; after all, the file could have been created by an application which hadn't even been written when the desktop manager was installed. The desktop manager therefore causes the appropriate application to be started. It then instructs the application to print the file.

Because GEOS is an object-oriented system, there is a natural model for inter-application communication. When one application needs to contact another, it can simply send a message; thus, sending information or instructions to another application is not, in principle, different from sending it from one object to another within an application.

The GEOS Inter-Application Communications Protocol (IACP) specifies how to open communications with another application, and how to send messages back and forth.

IACP Overview

GenApplicationClass Behavior

Messages Across an IACP Link

Being a Client

Being a Server

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 The Error-Checking Version | Next: 6.1 IACP Overview


IACP Overview

Applications and Geodes: 6.1 Inter-Application Communication: IACP Overview

| Up: GEOS SDK TechDocs | Up | Prev: 6 Inter-Application Communication | Next: 6.2 GenApplicationClass Behavior

There is a major difference between sending a message within an application, and sending one to a different application. When you send a message from one object to another within an application, you know that the recipient exists, and you know the optr of that recipient. This makes it easy to send messages.

When you send a message to another application, however, you do not (at first) know any optrs to that application. In fact, you may not even know that the application is running. Often, all you will know is something like, "I want to send a message to SpiffyWrite ".

GEOS uses a client-server model of inter-application communication. Every GeodeToken corresponds to a server. Whenever an application is launched, GEOS checks to see if there is a server-list corresponding to the application's token. If there is, GEOS adds the app's Application object to the server-list; if there is not, GEOS creates a server-list and adds the Application object to that list.

For example, suppose the user launches a single copy of SpiffyWrite ; this application has a manufacturer-ID of MANUFACTURER_ID_SPIFFYWARE, and the token characters "SWRI". GEOS will check if there's a server-list for that GeodeToken . Let us suppose there isn't such a list; GEOS will automatically create one, and add SpiffyWrite 's Application object to that list. The Application object is now said to be a server for the list.

Now let us suppose another application needs to contact SpiffyWrite ; for example, perhaps a desktop program needs to print a SpiffyWrite file. It tells the kernel that it would like to be a client on the list for the token "{MANUFACTURER_ID_SPIFFYWARE, "SWRI"}". GEOS will check to see if a server-list for that token exists. If so, it will add the client to that list; this will cause a notification message (MSG_META_IACP_NEW_CONNECTION) to be sent to every server for that list.

Once a client is linked to a server, it can send a message to the server list. It does this by encapsulating a message, then passing the encapsulated message to the server-list. GEOS will dispatch the message to every server for the list; the server objects will receive it just like any ordinary message. (It actually passes the encapsulated message to the server object as an argument to MSG_META_IACP_PROCESS_MESSAGE ; the server can then dispatch the message to the final recipient.)

This establishes the link between the applications. The client can pass an optr to the servers by putting it in the encapsulated message; a server object can then send messages straight to a particular object.

When a client no longer needs to communicate with a server, it unregisters itself from the server list. GEOS then sends a notification message to every server object.

Customarily, whoever allocates a global resource must also free it. For example, if a client might pass information to the server by allocating a global block, writing the data to the block, and passing the block to the server. The server should notify the client when the server is finished with that data; the client can then free the block. Similarly, if a server allocates a block to pass information to a client, the server should free the block.

There may be several servers for a given server list. For example, if three copies of SpiffyWrite were running at once, each of their Application objects would be a server for the same server-list. Furthermore, any object can make itself a server for any list. All servers will receive copies of every message sent to the server list. To distinguish between different servers for a list, every server for a list is assigned a distinct ServerNumber . If it chooses to, a client can specify that a message be sent only to the server with a specific number.

| Up: GEOS SDK TechDocs | Up | Prev: 6 Inter-Application Communication | Next: 6.2 GenApplicationClass Behavior


GenApplicationClass Behavior

Applications and Geodes: 6.2 Inter-Application Communication: GenApplicationClass Behavior

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 IACP Overview | Next: 6.3 Messages Across an IACP Link

GenApplicationClass is built to support IACP automatically. If a server or client object is subclassed from GenApplicationClass , most of the work of supporting IACP is done transparently to the application writer. The following capabilities are built in:

  • The Application Object automatically registers itself as a server for the appropriate list when it is launched in application mode. If it is launched in engine mode, it registers itself when it receives MSG_META_APP_STARTUP. If the last client-connection to the application is closed, and the Application is not currently running in application mode, the Application object will shut down automatically.
  • When the Application object registers itself as a server for its own list, it sends itself MSG_GEN_APPLICATION_IACP_REGISTER. An application may subclass this if it wants to take other action at this time (e.g. registering itself for other lists). Similarly, when an Application object unregisters itself from its own server list, it sends itself MSG_GEN_APPLICATION_IACP_UNREGISTER.
  • The Application object automatically handles the MSG_META_IACP... messages appropriately. In particular, when the kernel passes an encapsulated message to an Application object with MSG_META_IACP_PROCESS_MESSAGE , the Application object automatically dispatches the message to the appropriate location.
  • An Application object will refuse to quit as long as any client has an open IACP connection to it. (It can, however, be forcibly detached; this happens when the system is shut down, as noted below.). In such a case, the Application object will automatically call IACPShutdownAll() to shut down all IACP links it has open, whether it is a client or a server on those links.
  • When a link is closed, IACP automatically sends MSG_META_IACP_LOST_CONNECTION to all objects on the other side of the link. When an Application object receives this message, it waits until all remaining messages from the link have been handled; it then calls IACPShutdown() for that connection. It also forwards this message to all Document objects, so a Document object will know to close itself if the IACP connection was the only reference to it. Again, the Application object does this whether it is a client or a server.
  • If the Application object is forcibly detached, it sends itself MSG_GEN_APPLICATION_IACP_SHUTDOWN_ALL_CONNECTIONS. The default handler for this message will call IACPShutdownAll() to shut down all IACP links the Application object has open, whether it is a client or a server on those links. You can subclass this message if you need to take some additional action when the IACP connections are severed.
| Up: GEOS SDK TechDocs | Up | Prev: 6.1 IACP Overview | Next: 6.3 Messages Across an IACP Link


Messages Across an IACP Link

Applications and Geodes: 6.3 Inter-Application Communication: Messages Across an IACP Link

| Up: GEOS SDK TechDocs | Up | Prev: 6.2 GenApplicationClass Behavior | Next: 6.4 Being a Client
IACPSendMessage(), IACPSendMessageToServer()

Either a client or a server may send messages over an IACP link. Both clients and servers use the same technique. The message sender encapsulates a message, and passes the encapsulated message to IACPSendMessage() . IACPSendMessage() dispatches the message to every object on the other side of the link. For example, if a client passes a message to IACPSendMessage() , that message will be dispatched to every server object for the specified list.

IACPSendMessage() is passed five arguments:

  • The token for the IACP link.
  • The EventHandle of an encapsulated message.
  • The TravelOption for that message.
  • An encapsulated completion message; this will be dispatched once each time the first message has been successfully handled.
  • A member of the IACPSide enumerated type. This tells whether the message is being sent by a client or a server. If you pass the value IACPS_CLIENT, the message will be dispatched to all servers; if you pass IACPS_SERVER, the message will be dispatched to all clients.

The message will be dispatched to all geodes on the other side of a link. Note that a client need not send the message to the server object per se. It can use the travel options field to direct the message anywhere within the server object's geode. It can also specify the optr of the recipient when it encapsulates the message; in this case, it should pass a TravelOption of -1.

Every time the encapsulated message is successfully handled, the "completion message" will be dispatched. Typically, the completion message is addressed to the object that called IACPSendMessage() , instructing it to free global resources that had been allocated for the message.

The routine returns the number of messages that were dispatched. This lets the sender know how many completion messages to expect, and lets it properly initialize all reference counts to global resources.

A client may choose to send a message to a specific server. It can do this by calling IACPSendMessageToServer() . This takes almost the same arguments as IACPSendMessage() . However, instead of being passed an IACPSide value, it is passed a server number. GEOS will dispatch a single copy of the message to the specified server. IACPSendMessageToServer() returns the number of times the message was dispatched. This will ordinarily be one; however, if the specified server is no longer registered, it will be zero.

| Up: GEOS SDK TechDocs | Up | Prev: 6.2 GenApplicationClass Behavior | Next: 6.4 Being a Client


Being a Client

Applications and Geodes: 6.4 Inter-Application Communication: Being a Client

| Up: GEOS SDK TechDocs | Up | Prev: 6.3 Messages Across an IACP Link | Next: 6.5 Being a Server

Any object can register as a client for an IACP server list. When an object is a client, it can send messages to the server list, which will pass them along to the servers for that list.

Registering as a Client

IACPConnect(), IACPCreateDefaultLaunchBlock()

To register as a client for a list, call the routine IACPConnect() . When you call this routine, you specify which server list you are interested in. If there is no such server list running, you can instruct the kernel to start up the server list, as well as one of the default applications for that list.

IACPConnect() is passed five arguments:

  • The GeodeToken of the list for which you want to register.
  • A set of IACPConnectFlags . The following flags are available:
IACPCF_OBEY_LAUNCH_MODEL
This indicates that GEOS should follow the launch model, which will specify whether the user should be presented a dialog box, asking the user whether an existing application should be used as the server, or a new application launched. If you set this flag, you must pass an AppLaunchBlock , with the ALB _appMode field set to MSG_GEN_PROCESS_OPEN_APPLICATION; you must also set IACPCF_SERVER_MODE to IACPSM_USER_INTERACTIBLE.
IACPCF_CLIENT_OD_SPECIFIED
This flag indicates that you will specify what object will be the new client. If you do not set this flag, the sending geode's Application object will be registered as the client.
IACPCF_FIRST_ONLY
If you pass this flag, the client will be connected to only the first server on the server list. Any messages sent by the client to the server list will be passed only to that one server.
IACPCF_SERVER_MODE
This field is three bits wide; it holds a member of the IACPServerMode enumerated type. This type specifies how the client expects the server to behave. Currently, only two types are supported:
IACPSM_NOT_USER_INTERACTIBLE
This is equal to zero. It indicates that the server object need not interact with the user.
IACPSM_USER_INTERACTIBLE
This is equal to two. It indicates that the server should be able to interact with the user like any normal application.
  • The MemHandle of an AppLaunchBlock . If the server you specify is not running, GEOS will launch the application specified by the AppLaunchBlock . If you pass a null MemHandle , GEOS will return an error if no server is running.
  • The optr of the client object. This is ignored if IACPCF_CLIENT_OD_SPECIFIED was not passed.
  • A pointer to a word. IACPConnect() will write the number of servers on the list to that word.

IACPConnect() returns a word-sized IACPConnection token. You will need to pass that token when you call another IACP routine to use the connection. It will also return the number of server objects on the list; it returns this value by writing it to the address indicated by the pointer passed.

If the server list you indicate is not currently running, IACP may do one of two different things. If you pass a null handle as the third argument, IACPConnect() will fail. It will return the error value IACP_NO_CONNECTION, and indicate that there are no servers on the specified list.

If you pass an AppLaunchBlock , IACPConnect() will examine that launch block to see what application should be launched to act as a server. The AppLaunchBlock should specify the location and name of the application to open. If the ALB _appRef.AIR_diskHandle field is non-zero, IACPConnect() will look in the specified disk or standard path for an application with the right GeodeToken ; otherwise, it will look in the standard places for an application.

Note that if you pass a launch block to IACPConnect() , you may not alter or free it afterwards. If the application is created, the block you pass will be its launch block; if not, the kernel will free the block automatically. In any event, the caller no longer has access to the block.

If IACPConnect() launches an application, the caller will block until that application has been created and registers for the server list. If the application does not register for that list, the caller will never unblock. You must therefore make sure that you are launching the right application for the list. Note that every application object will automatically register for the server list which shares its token.

To create a launch block, you should call IACPCreateDefaultLaunchBlock() . This routine is passed a single argument, which specifies how the application will be opened. That argument must be MSG_GEN_PROCESS_OPEN_APPLICATION (the application will be opened as a standard, user-interactible application); MSG_GEN_PROCESS_OPEN_ENGINE (the application will be opened in engine mode, i.e. with no user interface); or MSG_GEN_PROCESS_OPEN_CUSTOM (which has an application-specified meaning).

IACPCreateDefaultLaunchBlock() allocates a launch block and sets up its fields appropriately. As created, the launch block will have the following characteristics:

  • The application's initial working directory (i.e. the launch block's ALB _diskHandle field) will be SP_DOCUMENT.
  • No application directory will be specified in the launch block (i.e. ALB _appRef. AIR _diskHandle will be zero); IACPConnect() will attempt to find the application on its own.
  • No initial data file will be specified (i.e. ALB _dataFile will be blank).
  • The application will determine its own generic parent (i.e. ALB _genParent will be null).
  • No extra data word will be passed to the application (i.e. ALB _extraData will be zero).
  • There will be no output descriptor (i.e. ALB _userLoadAckOutput and ALB _userLoadAckMessage will be null.)

IACPCreateDefaultLaunchBlock() returns the handle of the newly-created launch block. Once the block is created, you can alter any of its fields before passing the launch block to IACPConnect() . (Once you pass the launch block to IACPConnect() , you may not alter it any more.)

Often a client will want the server to open a specific document. For example, if a desktop-manager is implementing a "print-file" command, it will need to open a server application, instruct it to open the file to be printed, and then instruct it to print the file. To make the server open a document, pass the document's name in ALB _dataFile . The server will open the file when you register, and close it when you unregister.

Unregistering as a Client

IACPShutdown(), IACPShutdownAll()

If an application no longer needs to interact with a particular server list, it should call IACPShutdown() . This routine is also used by servers which wish to remove themselves from a server list. This section describes how the routine is used by clients; Being a Server describes how it is used by servers.

The routine is passed two arguments:

  • The IACPConnection token for the link which is being shut down.
  • An optr. This must be null if the routine is being called by a client.

IACPShutdown() sends MSG_META_IACP_LOST_CONNECTION to all objects on the other side of the link; that is, if a client calls IACPShutdown() , all servers on the list will be sent this message.

IACPShutdownAll() closes all IACP links for the application which calls it. The Application object automatically calls this routine when the application is exiting.

| Up: GEOS SDK TechDocs | Up | Prev: 6.3 Messages Across an IACP Link | Next: 6.5 Being a Server


Being a Server

Applications and Geodes: 6.5 Inter-Application Communication: Being a Server

| Up: GEOS SDK TechDocs | Up | Prev: 6.4 Being a Client

Every time an application is launched, its Application object automatically registers as a server for the server list that shares its GeodeToken . The Application class has default handlers for all the notification messages IACP sends to the server objects.

If you wish, you can have another object act as a server. However, if you do this, you will have to do more of the work yourself. While the notification messages are defined for MetaClass , and thus can be handled by any class of object, MetaClass does not come with handlers for these messages; if the server is not subclassed from GenApplicationClass , you will have to write the handlers yourself. This is discussed below.

Registering and Unregistering a Server

IACPRegisterServer(), IACPUnregisterServer(), IACPGetServerNumber()

You will not generally need to register and unregister a server object explicitly. As noted above, when an application is launched, the application object is automatically registered as a server for the list with its GeodeToken ; when the application exits, the Application object is automatically unregistered from that list.

However, you may wish to explicitly register an object as a server. For example, you might want your application object to be a server on a list with a different GeodeToken ; or you might want to register a non-Application object as a server. In this case, you will need to explicitly register and unregister the object.

To register an object as a server, call IACPRegisterServer() . This routine is passed the following arguments:

  • The GeodeToken of the list for which you are registering as a server.
  • The optr of the object which is registering as a server. This object must be able to handle the MSG_META_IACP... messages appropriately (this is built into GenApplicationClass ).
  • A member of the IACPServerMode enumerated type. This type specifies how the client expects the server to behave. Currently, only two types are supported:
IACPSM_NOT_USER_INTERACTIBLE
This is equal to zero. It indicates that the server object will not interact directly with users.
IACPSM_USER_INTERACTIBLE
This is equal to two. It indicates that the server will be able to interact with the user like any normal application.
  • A set of IACPServerFlags . Currently, only two flags are supported: IACPSF_MULTIPLE_INSTANCES indicates that multiple copies of the application might be running at once (Every multi-launchable application should set this flag.); IACPSF_MAILBOX_DONT_ASK_USER asks the Mailbox library not to notify the user of new messages for this application.

IACPRegisterServer() registers the object as a server for the appropriate list; it creates the server list if necessary.

To unregister an object as a server, call IACPUnregisterServer() . This routine is passed two arguments: the GeodeToken of the server list, and the optr of the server. The object will be removed immediately from the server list. Note, however, that the server list might have already dispatched some messages to the server being removed; these messages might be waiting on the server object's queue, and thus the server object might get some IACP messages even after it calls IACPUnregisterServer() . One way to deal with this is to have the server object send itself a message, via the queue, immediately after it calls IACPUnregisterServer() . When the object receives this message, it will know that it has no more IACP messages on its queue.

Every server object on a given server list has a unique server number . This server number will not change while the server is attached to the list. A server object can find out its server number by calling IACPGetServerNumber() . This routine takes two arguments: the IACPConnection and the optr to the server object. It returns the object's server number.

Non-Application Servers and Clients

MSG_META_IACP_PROCESS_MESSAGE, IACPProcessMessage(), MSG_META_IACP_NEW_CONNECTION, MSG_META_IACP_LOST_CONNECTION

Every server and client object must be able to handle certain messages. GenApplicationClass comes with handlers for these messages, so you need not write them yourself. However, if you will be using some other kind of object as the server, you must handle the messages yourself. You may also choose to have your application object subclass any of these messages; in that case, you should generally have your handler use @callsuper .

When a server or client sends an IACP message, the kernel passes the encapsulated message to each object on the other side of the link. It does this by sending the message MSG_META_IACP_PROCESS_MESSAGE to each object. This message comes with three arguments:

msgToSend
The EventHandle of the encapsulated message.
topt
The TravelOption for that message.
completionMsg
The EventHandle of any message to be sent after msgToSend has been dispatched. (This field may be set to zero, indicating that there is no completion message.)

The recipient of MSG_META_IACP_PROCESS_MESSAGE should call IACPProcessMessage() . This routine is passed four arguments: the optr of the object calling the routine, and the three arguments passed with MSG_META_IACP_PROCESS_MESSAGE. IACPProcessMessage() dispatches both encapsulated messages properly.

Remember, if the client or server is subclassed from GenApplicationClass , all of this is done for you. You need only write a handler for the message if the client or server object is not from a subclass of GenApplicationClass .

Whenever a client registers on an IACP list, the kernel sends MSG_META_IACP_NEW_CONNECTION to all servers on that list. This message comes with three arguments:

appLaunchBlock
This is the handle of the AppLaunchBlock which the server passed to IACPConnect() .
justLaunched
This is a Boolean value. If the server's application has just been launched specifically to be a server, this will be true (i.e. non-zero).
IACPConnection
This is the token for the IACP connection.

When an object (either client or server) removes itself from an IACP connection, the kernel sends MSG_META_IACP_LOST_CONNECTION to all objects on the other side of the link. This message has two parameters:

connection
The token for the IACP list.
serverNum
If a server removed itself from the list (and this message is being sent to clients), this field will have the number of the server that removed itself. If a client removed itself from the list, this field will be zero.

Whenever a new client is attached to a server list, MSG_META_IACP_NEW_CONNECTION is sent to every server object. This message comes with three arguments:

appLaunchBlock
This is the handle of the application's launch block. The default GenApplicationClass handler examines the launch block to see if the application should open a document.
justLaunched
If the application was just launched specifically to be a server, this field will be true (i.e. non-zero).
connection
This is the token for the IACP connection.

When a client is removed from a server list, every server object is sent MSG_META_IACP_LOST_CONNECTION . Similarly, when a server is removed, every client object is sent MSG_META_IACP_LOST_CONNECTION . The message comes with two arguments:

IACPConnection
This is the token for the IACP connection.
serverNum
If a server left the list (and the message is being sent to clients), this argument will hold its serverNum . If a client left the list (and the message is being sent to servers), this argument will be set to zero.
| Up: GEOS SDK TechDocs | Up | Prev: 6.4 Being a Client


Threads and Semaphores

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Multitasking Goals
| One-page Version of this Chapter for Easy Printing

One of the most impressive features of GEOS is its ability to perform several tasks simultaneously, even on the least powerful PCs. Of course, the PC has only one processor and can execute only one instruction at a time. GEOS keeps track of the various tasks, or threads , that are underway, and by switching from one thread to another many times per second creates the illusion that the PC is actually doing all the jobs at the same time.

This chapter covers

  • the basic concepts of multitasking,
  • the GEOS multitasking scheme,
  • the steps to creating a multi-threaded application, and
  • the use of semaphores to synchronize threads and avoid deadlock.

You will need to know the information in this appendix if you will be writing a multi-threaded application. The information in the appendix is not essential if your application will be single-threaded or if you will use the standard GEOS dual-thread architecture. For a dual-thread application, be careful not to send a message with @call from an object run by a user interface thread to an object run by any other thread of the application.

1 Multitasking Goals
2 Two Models of Multitasking
2.1 Cooperative Multitasking
2.2 Preemptive Multitasking
3 GEOS Multitasking
3.1 GEOS Threads
3.2 Context Switches
3.3 Thread Scheduling
3.4 Applications and Threads
4 Using Multiple Threads
4.1 How GEOS Threads Are Created
4.2 Managing Priority Values
4.3 Handling Errors in a Thread
4.4 When a Thread Is Finished
5 Synchronizing Threads
5.1 Semaphores: The Concept
5.2 Semaphores In GEOS

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Multitasking Goals
| One-page Version of this Chapter for Easy Printing


Multitasking Goals

Threads and Semaphores: 1 Multitasking Goals

| Up: GEOS SDK TechDocs | Up | Prev: Threads and Semaphores | Next: 2 Two Models of Multitasking

The GEOS multitasking system is one of the most sophisticated available for PCs today. It was designed with the latest available technology and was created to serve the following two primary goals:

  • Fast Response
    The GEOS multitasking system was designed with a strong emphasis on rapid response to user input. Prompt, visible reaction to user action is the single most important factor contributing to the perception of speed. For example, if a user changes an element in a spreadsheet (requiring the whole spreadsheet to be recomputed) and then pulls down a menu, he wants the menu to appear right away. If the menu does not appear until the computation is finished, the system will seem sluggish. If the computation takes a few seconds or longer, the user may wonder whether the system has crashed altogether.
  • Ease of Programming
    Making the programmer's job easy is another motive behind the design of GEOS multitasking. Ideally, the programmer should not need to be aware that his program will run in a multitasking environment. The program should proceed as though it were the only one running on the system. Besides certain "good citizen" rules, application programs are isolated from the multitasking environment. On the other hand, if a programmer wants to take advantage of the multitasking capability of GEOS (by designing a program to perform more than one task concurrently) the system is designed to make this as simple and efficient as possible.
| Up: GEOS SDK TechDocs | Up | Prev: Threads and Semaphores | Next: 2 Two Models of Multitasking


Two Models of Multitasking

Threads and Semaphores: 2 Two Models of Multitasking

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Multitasking Goals | Next: 3 GEOS Multitasking

Many operating systems provide the ability to carry out multiple tasks at the same time. While each operating system has its own unique way of managing this, there are two fundamental types of multitasking: cooperative (used by Microsoft Windows), and preemptive (used by GEOS).

Cooperative Multitasking

Preemptive Multitasking

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Multitasking Goals | Next: 3 GEOS Multitasking


Cooperative Multitasking

Threads and Semaphores: 2.1 Two Models of Multitasking: Cooperative Multitasking

| Up: GEOS SDK TechDocs | Up | Prev: 2 Two Models of Multitasking | Next: 2.2 Preemptive Multitasking

A cooperative multitasking system, as the name suggests, is one in which the various programs cooperate. They agree to share the system and its resources. Each program running under a system has complete control while it is actually running. Every so often, when it reaches a convenient place, it calls a special system routine (called a context-switch routine) to see if any other program has work do to. If so, that program takes control of the system until it in turn reaches a convenient stopping point and passes control on to the next waiting program. If several programs are ready to run, they wait in a queue so that each one gets a chance to run before the first program runs again. (It is also possible to implement a cooperative multitasking system where some programs have a higher priority than others. In this case, a more complicated algorithm might be used by the context-switch routine to determine which program gets to run next.)

Smooth operation of a cooperative multitasking system requires that all programs be written to call the context-switch routine frequently. When large calculations are being performed, programmers tend to find this requirement inconvenient. Writing well-behaved programs (i.e., programs that do not keep control of the processor for too long at a stretch) is especially difficult because most cooperative multitasking systems impose restrictions on when a context switch can take place.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Two Models of Multitasking | Next: 2.2 Preemptive Multitasking


Preemptive Multitasking

Threads and Semaphores: 2.2 Two Models of Multitasking: Preemptive Multitasking

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Cooperative Multitasking | Next: 3 GEOS Multitasking

In a preemptive multitasking system, programs do not have to relinquish control of the system voluntarily. Instead of calling a context-switch routine, the program is written as though it were going to run continuously from start to finish. The hardware generates a timer interrupt a number of times each second, and that interrupt triggers the kernel's context-switch mechanism.

The context switch can also be triggered by other interrupts. For example, if the user moves the mouse in GEOS, the mouse will generate an interrupt. GEOS responds by marking the input thread runnable; the thread will then run after the interrupt is complete. This is how GEOS achieves its extraordinary response times to user input.

With preemptive multitasking, each program can have the illusion that it is running continuously and has complete control of the system. It also enables the system to interact quickly with the user even when applications are busily computing new results.

For example, a spreadsheet program can keep running until the timer interrupt causes a context switch. Other programs, including the one responsible for drawing menus, then get their turns to run. If a user clicks on a pull-down menu, the menu will appear. When the spreadsheet program regains control of the system, it can carry on from where it was interrupted, blissfully unaware that any of this has taken place.

While preemptive multitasking makes most things simpler for the user and application programmer, there are a few important issues to consider in writing programs for a preemptive multitasking system such as GEOS. When the context switches are controlled by a timer interrupt, they can occur between any two instructions. If a program is interrupted while it is updating a data structure, that data structure may be left in an inconsistent state while another thread is running. If the data structure is not accessed by any other process running on the system, there is no problem: the update will be completed when the program resumes. However, some data structures (including system resources) may be accessed by more than one program. It is important that two updates to the same data do not happen at the same time.

This problem is analogous to one often experienced by network users. If a text file is being edited at the same time by two different users and they both save their changes to the file, whoever saves first will have his version overwritten by the other. Many systems have a means of locking a file while you are editing it; no one else can begin editing the file while you have it locked. A preemptive multitasking system must have a similar locking scheme to prevent two accesses to the same data structure from happening at the same time. The locking mechanism should be as transparent as possible to the programmer. For example, the locking and unlocking of system resources should happen automatically so that application programmers need not concern themselves with it.

This is exactly how GEOS coordinates its resources, as you shall see in the following sections.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Cooperative Multitasking | Next: 3 GEOS Multitasking


GEOS Multitasking

Threads and Semaphores: 3 GEOS Multitasking

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Two Models of Multitasking | Next: 4 Using Multiple Threads

GEOS implements a preemptive multitasking scheme. Application programs are required to follow certain "good citizen" rules and are otherwise given the illusion that they are alone on the system. The applications themselves are, for the most part, isolated from the multitasking environment.

GEOS Threads

Context Switches

Thread Scheduling

Applications and Threads

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Two Models of Multitasking | Next: 4 Using Multiple Threads


GEOS Threads

Threads and Semaphores: 3.1 GEOS Multitasking: GEOS Threads

| Up: GEOS SDK TechDocs | Up | Prev: 3 GEOS Multitasking | Next: 3.2 Context Switches

The various units that take turns running in the system are called "threads" in GEOS. Threads can have different priorities; a thread that has a higher priority (indicated by a lower priority number) will generally get more processor time.

Keeping Track of Threads

In order to switch among threads, the system needs to keep track of certain things about each one. For each thread, the system keeps track of priority, the most recent values of the registers, and flags. The priorities are used to determine which thread is going to run next. When the thread is run, the appropriate registers are reset to the values they had the last time the thread was stopped. This allows the thread to resume execution as though it had never been interrupted.

Like other things in GEOS, each thread has a handle, a sixteen-bit value which programs use to refer to the thread. When calling thread-related routines (e.g., to set the thread's priority), programs use the handle to specify the thread.

Event-Driven and Procedural Threads

GEOS uses two different types of threads. The two types differ only in the way they run when their turn comes and in the way they are created. The discussions about priority, context switches, and synchronization elsewhere in this chapter apply equally to both types.

The first type of GEOS thread is an "event-driven" thread. An event-driven thread normally executes code for one or more objects. Each event-driven thread has an event queue; when a message is sent to any object created by the thread, the message is placed in the thread's event queue. The thread processes each event in the order received by executing the appropriate message handler from the object's class definition.

Messages can also be sent to the thread itself rather than to an object created by the thread. When the thread is created, it is assigned a class--normally a subclass of ProcessClass (for non-application threads) or GenProcessClass (for application threads)--to determine the handlers to use when messages are sent directly to the thread. In this sense, the thread can be considered an instance of the given class.

The second type of thread is procedural. Rather than running handlers for messages in an object-oriented scheme, it simply executes procedural code.
The system does not provide an event queue for a procedural thread, and messages cannot be sent to such a thread.

| Up: GEOS SDK TechDocs | Up | Prev: 3 GEOS Multitasking | Next: 3.2 Context Switches


Context Switches

Threads and Semaphores: 3.2 GEOS Multitasking: Context Switches

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 GEOS Threads | Next: 3.3 Thread Scheduling

Context switches are triggered in two ways under GEOS. The first is a timer or other hardware interrupt. The second occurs when the thread reaches a point where it cannot continue right away, such as when the thread exits or when it attempts to access a locked resource.

The PC hardware generates a timer interrupt sixty times per second. The time between timer interrupts is called a tick . Each thread is allowed to run for a specified number of ticks before the timer interrupt routine will transfer control to a different thread. This number of ticks is the same for all threads; it is called a time slice .

When a thread begins its turn, GEOS sets a counter to the number of ticks in a time slice. At each timer interrupt, GEOS decrements the counter. If the counter has not reached zero, control is immediately returned to the running thread. When the counter reaches zero, GEOS checks to see if some other thread has reached a higher priority than the current thread. If so, the current thread is placed in the system's list of runnable threads (called the run queue ), and the highest priority thread begins running. Otherwise the current thread gets to run for another time slice.

Sometimes a thread will try to access a system resource (or shared data object) which is currently in use by another thread. When this happens, the thread must wait until the desired resource is available. The thread is placed on a queue, and a new thread is selected from the run queue. Every thread that is not currently running is either in the run queue (waiting to be executed) or in another queue waiting for a needed resource to become available. When the resource becomes available, the thread is moved to the run queue and is ready to be run again. This process is described in greater detail in Synchronizing Threads .

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 GEOS Threads | Next: 3.3 Thread Scheduling


Thread Scheduling

Threads and Semaphores: 3.3 GEOS Multitasking: Thread Scheduling

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Context Switches | Next: 3.4 Applications and Threads

When there is a context switch, GEOS must determine which thread will be executed next. It does this by examining the run queue and selecting the most important thread to run. A thread's importance is reflected in its priority: the lower the priority number, the more important the thread.

Base Priority

Each thread gets a base priority, a value from zero to 255. Applications generally have a priority between 128 and 191. Threads that are critical to quick system response (such as user input threads that manage such things as pull-down menus and dialog boxes) are given lower numbers. Higher numbers can be given to less time-critical threads such as those used for print spooling and other background activities. To provide faster response to the user, GEOS temporarily reduces an application's base priority by 32 (giving it a higher priority) when the user is interacting with it. When the user switches to interact with another application, the first application's base priority returns to normal, and the new one gets a reduced base priority number.

Recent CPU Usage

GEOS keeps track of a thread's recent CPU usage with a number that varies from zero to 60. Starting at zero, the number is incremented at every timer interrupt while the thread is running. Once each second, the recent CPU usage is halved, so that as a thread's CPU usage recedes into the past, its recent CPU usage number will diminish. The resulting number reflects how much time the thread has had control of the CPU and how long ago this time was.

Current Priority

Once each second, the current priority of each thread is recomputed by adding the base priority to the recent CPU usage. The resulting number is the one used in selecting a thread from the run queue.

When it is time for a context switch, GEOS selects the thread with the lowest current priority number from the run queue. If there is a tie, the selection is arbitrary. However, because recent CPU usage counts against a thread, two threads of equal priority will not stay that way. One will run, and its recent CPU usage (and thus its current priority number) will be increased. The other thread will therefore get its chance to run.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Context Switches | Next: 3.4 Applications and Threads


Applications and Threads

Threads and Semaphores: 3.4 GEOS Multitasking: Applications and Threads

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Thread Scheduling | Next: 4 Using Multiple Threads

There are two standard architectures for GEOS applications: single-thread and dual-thread. While the single-thread option is somewhat easier to program, there are distinct advantages to the dual-thread method.

In the dual-thread architecture, one thread manages the application's user interface while the other manages the rest of the application's functionality. Since both threads are event-driven, each has an event queue. Messages that are sent to user interface objects (resulting from mouse clicks, keyboard input, etc.) can be handled without waiting for other tasks in the application to be completed. This allows the application to respond to user input (by putting up menus, moving windows, and so on) without first completing the current non-user-interface task (which may involve a lot of computation).

The dual-thread architecture, however, poses a problem of synchronization: One thread can get ahead of the other. Threads that count on each other must keep track of each other's progress in order to avoid this; when potential problems are identified, use semaphores to keep the threads in line (see Synchronizing Threads ).

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Thread Scheduling | Next: 4 Using Multiple Threads


Using Multiple Threads

Threads and Semaphores: 4 Using Multiple Threads

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 GEOS Multitasking | Next: 5 Synchronizing Threads

It is possible for an application to create additional threads for a variety of purposes. For example, a terminal emulation program might have a thread whose sole purpose is to monitor the serial line for incoming characters. This might avert the danger of a serial input buffer or stream overflowing while the application is performing an involved task, such as loading a text file from disk, while not requiring a great deal of fixed memory for the serial driver's input buffer.

How GEOS Threads Are Created

Managing Priority Values

Handling Errors in a Thread

When a Thread Is Finished

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 GEOS Multitasking | Next: 5 Synchronizing Threads


How GEOS Threads Are Created

Threads and Semaphores: 4.1 Using Multiple Threads: How GEOS Threads Are Created

| Up: GEOS SDK TechDocs | Up | Prev: 4 Using Multiple Threads | Next: 4.2 Managing Priority Values

GEOS threads can be created in three different ways. The first thread (or pair of threads, in the dual-thread architecture) for each application is created automatically when the application is launched. By calling the appropriate routines, the application can create additional threads to handle messages sent to certain objects or to run procedural code.

The Application's Primary Thread

The application's primary thread is created automatically by GEOS when the application is launched. (See the Applications and Geodes chapterfor information on launching applications.) For example, if a user double-clicks on your application's icon in a GeoManager window, GeoManager calls the library routine UserLoadApplication() , specifying the geode file and certain other parameters. This calls the GeodeLoad() routine in the GEOS kernel.

If the program is written using the single-thread model, GEOS creates an event-driven thread to handle messages sent to any object in the program. If the program is written using the dual-thread model, GEOS creates one event-driven thread to handle messages sent to the program's user interface objects and another to handle messages sent to other objects in the program.

If the program requires more than two threads, the extra thread(s) must be allocated manually on startup and destroyed before the application exits completely.

Event-Driven Threads

ThreadAttachToQueue()

To create an event-driven thread (one that handles messages sent to certain objects), send a MSG_PROCESS_CREATE_EVENT_THREAD to your application's primary thread, passing as arguments the object class for the new thread (a sublass of ProcessClass ) and the stack size for the new thread (1 K bytes is usually a good value, or around 3 K bytes for threads that will handle keyboard navigation or manage a text object). This message is detailed in the Process Object chapter.

GEOS will create the new thread, give it an event queue, and send it a MSG_META_ATTACH . Initially, the thread will handle only messages sent to the thread itself. If the thread creates any new objects, however, it will handle messages sent to those objects as well. To control the behavior of the new thread, define a subclass of ProcessClass and a new handler for MSG_META_ATTACH . The new handler can create objects or perform whatever task is needed. Be sure to start your new handler with @callsuper() so that the predefined initializations are done as well.

If you have a thread that you want attached to a different event queue, you can use ThreadAttachToQueue() . This routine is not widely used except when applications are shutting down and objects need to continue handling messages while not returning anything. It's unlikely you will ever use this routine.

Threads That Run Procedural Code

ThreadCreate()

To create a thread to run procedural code, first load the initial function into fixed memory. Then call the system routine ThreadCreate() , passing the following arguments: The base priority for the new thread, an optional sixteen-bit argument to pass to the new thread, the entry point for the code, the amount of stack space GEOS should allocate for the new thread, and the owner of the new thread.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Using Multiple Threads | Next: 4.2 Managing Priority Values


Managing Priority Values

Threads and Semaphores: 4.2 Using Multiple Threads: Managing Priority Values

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 How GEOS Threads Are Created | Next: 4.3 Handling Errors in a Thread
ThreadGetInfo(), ThreadModify(), ThreadGetInfoType

You can ascertain and modify the priority of any thread in the system, given the thread's handle. The handle is provided by the routines that create threads, and it can be provided by one thread to another in a message. The following system routines relate to the priority of a thread:

ThreadGetInfo() returns information about a thread. When calling ThreadGetInfo() , pass the handle of the thread in question and a value of the type ThreadGetInfoType (see below). If zero is passed as the thread handle, ThreadGetInfo() returns information on whatever thread executed the call.

ThreadGetInfoType is an enumerated type with three possible values:

TGIT_PRIORITY_AND_USAGE
This requests the base priority and recent CPU usage of a thread. (To determine the current priority, simply add the base priority to the recent CPU usage.)
TGIT_THREAD_HANDLE
This requests the handle of the thread. Use this (with a thread handle of zero) to get the caller's own thread handle.
TGIT_QUEUE_HANDLE
This requests the handle of the event queue for an event-driven thread. It returns a zero handle if the thread is not event-driven.

T hreadModify() changes the priority of a thread. The arguments to pass include the handle of the thread to modify (zero for the thread executing the call), a new base priority for the thread, and two flags: One that indicates whether to change the thread's base priority and one that indicates whether to reset the thread's recent CPU usage to zero. If the flag to change the thread's base priority is not set, the new base priority argument is ignored. In general, you should only lower a thread's priority (i.e., raise its base priority number). Applications that raise their own priority damage the performance of the system as a whole. Keep in mind that GEOS already favors the thread with which the user is interacting.

There are several pre-defined priority levels you can use to set a thread's priority. You may wish to use these when debugging to raise the priority of a potentially buggy thread for efficient debugging. These are listed below, each a different constant.

  • PRIORITY_TIME_CRITICAL
    Threads should not be set time-critical unless they must own the processor exclusively for a certain amount of time. Excluding other threads can have undesirable side effects.
  • PRIORITY_HIGH
  • PRIORITY_UI
  • PRIORITY_FOCUS
  • PRIORITY_STANDARD
  • PRIORITY_LOW
  • PRIORITY_LOWEST
| Up: GEOS SDK TechDocs | Up | Prev: 4.1 How GEOS Threads Are Created | Next: 4.3 Handling Errors in a Thread


Handling Errors in a Thread

Threads and Semaphores: 4.3 Using Multiple Threads: Handling Errors in a Thread

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Managing Priority Values | Next: 4.4 When a Thread Is Finished
ThreadHandleException(), ThreadException

Some threads in GEOS will want to handle certain errors in special ways. The errors a particular thread can intercept and handle are listed in an enumerated type called ThreadException , the elements of which are shown below:

  • TE_DIVIDE_BY_ZERO
  • TE_OVERFLOW
  • TE_BOUND
  • TE_FPU_EXCEPTION
  • TE_SINGLE_STEP
  • TE_BREAKPOINT

A thread can handle a particular exception by setting up a special handler routine and calling ThreadHandleException() when one of these exceptions occurs. This is useful if a number of objects are run by the same thread and all should handle a particular exception in the same way; the routine can be thread-specific rather than object-specific. ThreadHandleException() must be passed the thread's handle, the exception type, and a pointer to the handler routine's entry point.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Managing Priority Values | Next: 4.4 When a Thread Is Finished


When a Thread Is Finished

Threads and Semaphores: 4.4 Using Multiple Threads: When a Thread Is Finished

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Handling Errors in a Thread | Next: 5 Synchronizing Threads
ThreadDestroy()

Whenever an application creates an additional thread with MSG_PROCESS_CREATE_EVENT_THREAD or ThreadCreate() , it must be sure that the thread exits when it is finished. Simply exiting the application may not eliminate any additional threads, and these threads can cause GEOS to hang when shutting down the system.

When a thread exits, it should first release any semaphores or thread locks it has locked and free any memory or other resources that are no longer needed. Resources in memory do not have to be freed in the same thread that allocated them, but you should be sure that they are freed before the application exits.

A procedural thread exits by calling ThreadDestroy() with three arguments: an error code, an optr, and a word of data. When the thread exits, it sends (as its last act) a MSG_PROCESS_NOTIFY_THREAD_EXIT to the application's primary thread and a MSG_META_ACK to the object descriptor passed. (ThreadDestroy()'s word of data argument will be passed as the lower word of MSG_META_ACK's optr argument.) Each message has the error code as an argument. In designing a multi-threaded application, you can create methods for MSG_PROCESS_NOTIFY_THREAD_EXIT (in your primary thread's class) or MSG_META_ACK (in any class) for communication among threads, and you may use the error code for any data you choose. The convention is that an error code of zero represents successful completion of a thread's task.

An event-driven thread should not call ThreadDestroy() directly because its event queue must be removed from the system cleanly. Instead, send a MSG_META_DETACH to the thread, passing the same arguments as for ThreadDestroy() . The handler for MSG_META_DETACH in MetaClass cleanly removes the event queue and terminates the thread, sending the same messages as described above. You may write a special handler for MSG_META_DETACH when you subclass ProcessClass , but be sure to end the handler with @callsuper() so the thread exits properly.

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Handling Errors in a Thread | Next: 5 Synchronizing Threads


Synchronizing Threads

Threads and Semaphores: 5 Synchronizing Threads

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Using Multiple Threads | Next: 5.1 Semaphores: The Concept

Because GEOS is a preemptive multitasking environment, it needs a way to prevent two threads from accessing system resources (or other shared data) simultaneously. Otherwise, data could be corrupted. GEOS uses semaphores to prevent to threads from performing conflicting operations at the same time.

Semaphores: The Concept

Semaphores In GEOS

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Using Multiple Threads | Next: 5.1 Semaphores: The Concept


Semaphores: The Concept

Threads and Semaphores: 5.1 Synchronizing Threads: Semaphores: The Concept

| Up: GEOS SDK TechDocs | Up | Prev: 5 Synchronizing Threads | Next: 5.2 Semaphores In GEOS

A semaphore is a data structure on which three basic operations are performed. These operations allow threads to avoid conflicting with other threads. Think of a semaphore as a flag which programs can set to indicate that some resource is locked. Anyone else who wants to use the resource must wait in line until whoever set the flag resets it. The three basic operations on a semaphore are initialize, set, and reset.

Initialize

The "initialize" operation creates a semaphore and gives it a name. In its initial state, the semaphore is "unlocked," meaning the first process that attempts to access it will succeed right away. A semaphore must be initialized before it can be used, although the initialization can be handled by the operating system so that it is transparent to the programmer.

Set (the "P" Operation)

The "P" operation is what a program performs in order to make sure it is allowed to proceed. For example, if the program is about to access shared data, it performs the "P" operation on the semaphore protecting that data to make sure no other program is accessing it.

If the semaphore is unlocked and a thread performs the "P" operation on it, the thread simply marks the semaphore locked and proceeds normally. If the semaphore is locked, a thread performing the "P" operation will block . This means the thread will stop running and will wait in the thread queue associated with the semaphore. When its turn to perform the protected operation arrives, the thread will proceed.

Reset (the "V" Operation)

When a thread has finished a protected operation, it performs the "V" operation to unlock the semaphore. If there are other threads in the queue for this semaphore, one of them is restarted and takes over, keeping the semaphore locked. Thus only one thread at a time runs the protected operation. If a thread performs the "V" operation on a semaphore and there are no other threads waiting in the semaphore's queue, the thread simply marks the semaphore unlocked and proceeds.

Programs must always reset a semaphore when they are done with it. If you fail to reset a semaphore, other threads may wait forever.

Because only one thread at a time is performing the protected operation and this thread is responsible for unlocking the semaphore, it is sometimes said to "have" the semaphore. The "P" and "V" operations are often referred to as "grabbing" and "releasing" a semaphore, respectively.

The Dreaded Deadlock Problem

When semaphores are not used carefully, they can cause programs to stop running entirely. Suppose Thread A tries to grab a semaphore which Thread B has locked. Thread A stops running until Thread B releases the semaphore. Thread B then tries to grab a second semaphore, which Thread A has previously locked. Thread B waits for Thread A to release the second semaphore, but Thread A is waiting for Thread B to release the first semaphore. Neither thread will ever wake up. This situation is called "deadlock."

To avoid deadlock, follow these guidelines:

  • When possible, avoid having a thread attempt to lock one semaphore while it already has another one locked.
  • When two or more semaphores may be locked by the same thread at the same time, they should always be used in the same order. Semaphores are often arranged in a hierarchy, with the coarsest (the one controlling access to the most resources) at the top and the finest at the bottom. Any thread grabbing multiple semaphores in this hierarchy must always grab from top to bottom; that is, no thread should grab a semaphore "above" one it already has locked.
  • In certain situations, a semaphore is used "between" two threads. One thread needs to wait until another performs a specific action. The first thread is said to "block" on the second. Of course, two threads must never block on each other. To ensure this situation never arises, only one of the threads should use the @call keyword when sending messages to the other; the other should always use @send and, when getting return information, have some sort of notification message sent in response.
  • When using the GEOS messaging system to send a message with @call to an object in another thread, the sending thread automatically blocks on the receiving thread. Since a number of user interface objects must be sent messages with @call , the application thread sometimes blocks on the user interface thread. To avoid deadlock, code that runs in the user interface thread must never send messages with @call to objects in the application thread. (This is a particular example of the above rule being implemented.)
| Up: GEOS SDK TechDocs | Up | Prev: 5 Synchronizing Threads | Next: 5.2 Semaphores In GEOS


Semaphores In GEOS

Threads and Semaphores: 5.2 Synchronizing Threads: Semaphores In GEOS

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Semaphores: The Concept

GEOS contains several system resources that are protected by semaphores. Since application programs can access these resources only through library routines, the programmer does not need to be aware of these semaphores; the required operations are performed by the library routines. For system resources (e.g. files, memory, handles), GEOS defines semaphores and provides special routines to set and reset them. The chapter that describes each system resource explains how to use the special semaphores that protect the resource.

The routines described in this section illustrate GEOS semaphores and can be used to create semaphores to protect resources defined within a multithreaded application. There are routines for each of the operations (initialization, P, and V), and there are special routines which simplify the use of a semaphore by multiple objects within the same thread.

Note that it is possible to create a semaphore with a starting value greater than one. That is, you can create a semaphore that will allow more than one thread to grab it at once. Typically, if only one thread may grab the semaphore, the thread is called a "mutual exclusion," or "mutex," semaphore, because it is normally used to make sure two threads don't mutually grab a particular resource.

Semaphores that can be grabbed by more than one thread are generally called "scheduling semaphores" because they allow easy manipulation of scheduled resources. The classic example of this is the "producer-consumer problem" wherein one thread produces buffers and another consumes them. Initially, no buffers exist, so the semaphore starts at zero. The consumer goes into a loop wherein it simply P's the semaphore (blocking until a buffer exists), takes the first buffer in the queue, processes the buffer, destroys the buffer, and then returns to the top of the loop. The producer, meanwhile, can produce any number of buffers, queue them, and V the semaphore once for each consumable buffer. The consumer thread will continue to process until all the buffers are consumed, and then it will block and wait for more buffers.

Operations on a Semaphore

ThreadAllocSem(), ThreadPSem(), ThreadPTimedSem(), ThreadVSem(), ThreadFreeSem()

To create a semaphore, simply call the routine ThreadAllocSem() , passing an initial value for the semaphore. This should normally be one to indicate the semaphore is unlocked. If you want the semaphore to be locked initially, pass an initial value of zero. In either case, the returned value will be the handle of the newly created semaphore. Use this handle with the routines described below.

Once a semaphore is created, a thread can lock it (i.e., perform the "P" operation) by calling the routine ThreadPSem() , passing the semaphore's handle as an argument. If the semaphore is unlocked, the thread locks it and proceeds; otherwise the thread waits in the semaphore's thread queue.

Another routine that performs the "P" operation is ThreadPTimedSem() . When calling this routine, pass as arguments the semaphore's handle and an integer representing a number of ticks. This integer is a time limit: If another thread has the semaphore locked and does not unlock it within the specified number of ticks, the routine will return with a flag indicating the lock was unsuccessful. Programs that use ThreadPTimedSem() must check this flag and must not perform the protected operation if it is set. The most common use of this routine is with a time limit of zero, meaning that the semaphore should be locked only if it is available right away--if it is not available, the thread will continue with some other action.

To release the semaphore (by performing the "V" operation), the thread calls ThreadVSem() , again passing the semaphore's handle. If there are other threads waiting for the semaphore, the one with the lowest current priority number takes over.

When a semaphore is no longer needed, it can be destroyed by calling ThreadFreeSem() with the semaphore's handle as an argument.

Operations on a Thread Lock

ThreadAllocThreadLock(), ThreadGrabThreadLock(), ThreadReleaseThreadLock(), ThreadFreeThreadLock()

At times it is convenient to have a program lock a semaphore that it has already locked. For example, one routine might lock a semaphore protecting a piece of memory and then call itself recursively. A thread lock is a semaphore that can be locked any number of times, as long as each lock is performed by the same thread. If another thread tries to grab the thread lock, it will wait until the first thread has performed the "V" operation once for each time it has run the "P" operation. It is possible to write reentrant routines using thread locks but not using regular semaphores.

A thread lock is initialized with the ThreadAllocThreadLock() routine, which takes no arguments. A thread lock is always created unlocked. To perform the "P" and "V" operation on a thread lock, use ThreadGrabThreadLock() and ThreadReleaseThreadLock() , respectively, and pass the semaphore's handle as an argument. These routines are analogous to ThreadPSem() and ThreadVSem() for semaphores. When a thread lock is no longer needed, it should be freed with a call to ThreadFreeThreadLock().

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Semaphores: The Concept


Libraries

GEOS SDK Documentation: Libraries

| Up: GEOS SDK TechDocs

The following documents hold information about a number of GEOS libraries.

UI
The UI library provides object classes used to build application UI.
AccPnt
Repository of information about "access points"--the user's internet service provider(s) and the like.
Socket
The Socket library provides a socket-style communication API. Through it, you can make TCP/IP and PPP connections.
Contacts
The Nokia 9000i Communicator libraries ContDB and ContLog keep track of the user's contact information.
Foam: Memory Levels
The Nokia 9000i Communicator's library Foam provides, among other things, routines to detect and warn of low memory level.
Foam: UI
The Nokia 9000i Communicator's library Foam provides, among other things, a number of object classes used to create application UI.
Mailbox
Underlies all inbox/outbox functionality.
Printing
The Spool library manages printing functionality.
Cell
Stores data in a two-dimensional array in a VM file.
DataStore
Manages databases consisting of records, each record consisting of several fields.
Using Streams
The StreamC library provides a low-level API for stream-style communication.
PCCom
Provides API for communicating with a PC over a serial connection, if that PC is running pccom (the engine underlying Swat communication and Nokia 9000i Communicator backup).
Impex
Translates some GEOS data files to formats used by popular PC programs.
Ruler
Provides object classes which draw tick marks and can scroll along with some other scrolling object. Also provides pointer movement constraints on devices which use mouse- or stylus-style pointers.
Config
Provides object classes useful for capturing user preferences to the INI file.
Parse
Provides API useful for parsing spreadsheet-style expressions or other types of expressions.
Using Libraries
This document doesn't describe any GEOS library in particular; it does show how a program can load or free a library "by hand."
| Up: GEOS SDK TechDocs


Config Library

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Providing the UI
| One-page Version of this Chapter for Easy Printing

Not all users are the same; not all computing environments are the same. Often some part of an application allows the user to tailor the UI to his wishes. The user may wish to specify some preferences at a system level rather than on an application level. To do this, the user will look to the Preferences Manager application. The config library provides mechanisms for writing Preferences modules.

Normally the purpose of a Preferences module's gadgetry is to set up some information in the user's .INI file. If you are encouraging other developers to take advantage of the configuration done by your module, be sure to document the .INI file categories and keys affected.

There are three main tasks involved in writing a Preferences module:

  • Providing a tree of UI gadgetry which will act as the module's dialog box.
  • Providing information Preferences will use to construct the module's button.
  • Providing any underlying functionality other than writing information to the .INI file.

The first two routines exported in the module's .gp file will be responsible for carrying out the first two of these tasks. The final task is difficult to define--it will depend on the nature of your module.

Preferences modules written in C will need one more thing. To indicate that the preferences module is compatible with the standard GEOS 2.0 Preferences Manager applications, the module will need a local.mk makefile for compilation, and that local.mk file should include a line that reads:

_PROTO = 2.0

For information about local.mk files, see the Using Tools chapter.

Before you try to write a Preference module, there are some things you should already know. Each module is a library, so be sure to read the Libraries chapter. to find out how to write a GEOS library. It will let you know, among other things, that you must place the "c-api" flag in your library's GP file if its entry points are in C. .

1 Providing the UI
1.1 Designing the UI Tree.
1.2 UI Fetch Routine
2 Module Information Routine
3 Important Messages
4 Object Class Reference
4.1 PrefClass
4.2 PrefValueClass
4.3 PrefItemGroupClass
4.4 PrefStringItemClass
4.5 PrefBooleanGroupClass
4.6 PrefDynamicListClass
4.7 TitledGlyphClass
4.8 PrefInteractionClass
4.9 PrefDialogClass
4.10 PrefTextClass
4.11 PrefControlClass
4.12 PrefTimeDateControlClass
4.13 PrefTriggerClass
4.14 PrefTocListClass

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Providing the UI
| One-page Version of this Chapter for Easy Printing


Providing the UI

Config Library: 1 Providing the UI

| Up: GEOS SDK TechDocs | Up | Down | Prev: Config Library | Next: 2 Module Information Routine

There are two steps to providing the UI for your preferences module. The first is to declare the objects you will use. The second is to write and export a routine which returns the head of the tree of UI gadgetry.

Designing the UI Tree.

UI Fetch Routine

| Up: GEOS SDK TechDocs | Up | Down | Prev: Config Library | Next: 2 Module Information Routine


Designing the UI Tree.

Config Library: 1.1 Providing the UI: Designing the UI Tree.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Providing the UI | Next: 1.2 UI Fetch Routine

Preferences modules present a dialog box to the user. You will need to design the dialog for your module. As you do so, you may find objects of the following classes useful. Each of the following classes is based upon one of the Generic UI classes.

GenClass provides mechanisms by which objects will read and write values to the .INI file. Pref- classes provide further functionality, such as storing an "original" value to revert to if the user decides to Cancel their changes.

The following Pref- classes are available:

PrefDialogClass
This class specializes in acting as the root of a Pref module's UI tree. It has been set up to provide "OK" and "Cancel" triggers by default. It has a mechanism to restart the system on an Apply if the module specifies that such a reset will be necessary to implement the user's changes.
Often a module will use a subclass of PrefDialogClass as the head of its UI tree--often the subclass intercepts MSG_GEN_APPLY to ensure valid user choices and to take other appropriate actions.
PrefValueClass
PrefValue objects act like objects of GenValueClass . However, these objects have the ability to reset their values to that originally stored in the .INI file (in case the user wants to cancel). They can handle only word-length values.
PrefItemGroupClass
Objects of this class act like GenItemGroups. In addition, this class has the ability to store an "original" value.
PrefStringItemClass
A PrefItemGroup normally writes data about selected items in the form of an integer. However, if its children are PrefStringItem objects, the PrefItemGroup can instead write out a special string associated with each PrefStringItem, resulting in a more readable .INI file.
PrefBooleanGroupClass
This class acts like GenBooleanGroupClass with the ability to store an "original" value.
PrefDynamicListClass
Use objects of this class where you might otherwise use a GenDynamicList. This class will not work together with PrefStringItem objects.
PrefTocListClass
This class presents an alphabetized list of files or driver names. It is used to provide lists of devices, such as the list of available printers. It can also be used to show a list of files with a given token in a given directory.
TitledGlyphClass
This specialized subclass of GenGlyph shows both an icon and a text moniker. It is used to present the icon and name at the top of a typical Preferences module dialog box.
PrefInteractionClass
This class acts like GenInteractionClass , but also will relay certain messages to its children which GenInteraction would not. Those objects which will need to work with the .INI file should receive these messages, so any Gen- objects which will work with the .INI file and any Pref objects should be grouped under PrefInteractions instead of GenInteractions.
PrefTextClass
This class behaves like GenTextClass , except that it will load and save its value based upon that stored in the .INI file.
PrefTriggerClass
This class acts as does GenTriggerClass, but has an extra action, so that two separate messages will be sent when the trigger is activated.
PrefControlClass
This class acts as a cross between PrefClass and GenControlClass .
PrefTimeDateControlClass
This class allows the user to set the system date and time.
PrefClass
PrefClass is something like GenClass --while never used directly, it is the superclass of all the other Pref- classes, and sets up several of the mechanisms which all will use.

There aren't any special restrictions on what sorts of objects appear in the UI tree of a Preferences module. However, the following rules will prove useful in constructing modules that correctly write their data to the .INI file and have a look and feel consistent with existing modules. For an example, see Pref Module UI Framework .

  • As a rule of thumb, the top object of the tree should be a PrefDialog.
  • For a consistent look, the upper portion of the dialog should contain the module's icon, name, and some brief help text. This is normally done using a TitledGlyph and a GenText object.
  • Generally the easiest way to specify the .INI file category and key to use is by means of ATTR_GEN_INIT_FILE_CATEGORY and ATTR_GEN_INIT_FILE_KEY. Assuming that the module will work with just one category, it is sufficient to declare an ATTR_GEN_INIT_FILE field for just the top object of the module's UI tree.
  • Because of the way messages are relayed, you should use PrefInteractions instead of GenInteractions if any objects under the interaction will be saving data to the .INI file.

Code Display 22-1 Pref Module UI Framework

@object MPMDialogClass MPMRoot = {
    GI_states = @default & ~GS_USABLE;
    GI_comp = @MPMTitleGroup, @MPMOtherStuff;
    HINT_INTERACTION_SINGLE_USAGE;
    HINT_INTERACTION_COMPLEX_PROPERTIES;
    HINT_ORIENT_CHILDREN_VERTICALLY;
    HINT_LEFT_JUSTIFY_CHILDREN;
    ATTR_GEN_HELP_CONTEXT = "myPrefModule";
    ATTR_GEN_INIT_FILE_CATEGORY = "myPref";
}
@object GenInteractionClass MPMTitleGroup = {
    GI_comp = @MPMTitle, @MPMHelp;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
}
@object TitledGlyphClass MPMTitle = {
    GI_visMoniker = list {
        @FontTextMoniker, @FontLCMoniker, @FontLMMoniker, @FontLCGAMoniker
     }
}
@object GenTextClass MPMHelp = {
    GI_attrs = @default | GA_READ_ONLY;
    GTXI_text = "Do such and such to configure your so and so.";
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    /* Might want HINT_MINIMUM_SIZE */
}
| Up: GEOS SDK TechDocs | Up | Prev: 1 Providing the UI | Next: 1.2 UI Fetch Routine


UI Fetch Routine

Config Library: 1.2 Providing the UI: UI Fetch Routine

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Designing the UI Tree. | Next: 2 Module Information Routine

Once you have constructed the UI tree, write a routine that returns the head of that tree. This should be the first routine exported in your module's .gp file. The Preferences Manager knows to call the first exported routine when it needs the UI tree for a module.

The routine should have the parameters

optr _pascal (void)

For an example of such a routine, see UI Fetch Routine .

Code Display 22-2 UI Fetch Routine

optr _pascal MPMGetPrefUITree(void) { return (@MPMRoot);}
| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Designing the UI Tree. | Next: 2 Module Information Routine


Module Information Routine

Config Library: 2 Module Information Routine

| Up: GEOS SDK TechDocs | Up | Prev: 1 Providing the UI | Next: 3 Important Messages

The Preferences application needs to know some information about each module to present the trigger which allows the user into said module. Preferences does this by calling the second routine exported by the module library. Preferences expects this routine to fill in a blank PrefModuleInfo structure.

By filling in this structure, the routine describes the button that Preferences will use to represent the module. Also, you may specify a User Level for the module, so it will be presented only to users with enough expertise to use it. You may also ask that only users with certain privileges be given access to the module; without the proper privileges, they won't see it.

The Routine should have the parameters

void _pascal (*PrefModuleInfo)

For an example of such a routine, see Module Information Routine .

Code Display 22-3 Module Information Routine

void _pascal MPMGetModuleInfo(PrefModuleInfo *info)
{	/* We'll set up this structure for both system and user information */
     moduleInfo->PMI_requiredFeatures = 0;
     moduleInfo->PMI_prohibitedFeatures = 0;
     moduleInfo->PMI_minLevel = UIIL_ADVANCED;
     moduleInfo->PMI_maxLevel = UIIL_MAX_LEVEL;
     moduleInfo->PMI_monikerList = @MPMMonikerList;
     moduleInfo->PMI_monikerToken = moduleToken;
}
const GeodeToken moduleToken = { "MyPf", MANUFACTURER_ID_MINE };

The following information about the PrefModuleInfo structure may prove useful when writing the routine:

PMI_requiredFeatures
This field allows you to restrict the display of your module so that it will only appear to users which have certain privileges.
PMF_HARDWARE
These settings are for a user who has permissions to actually change the configuration of the workstation. In a network environment where users log in to different machines at different times, normal users would be prevented from changing the mouse drivers, video drivers, etc.
PMF_SYSTEM
These changes are more complex and potentially more damaging than the basic "user" changes, therefore, some users may be prevented from using these settings.
PMF_NETWORK
These are network settings. Generally only the system administrator should see these settings, as they affect the entire network.
PMF_USER
These are basic user changes. These settings are the most basic and least dangerous, controlling user preferences such as background color and screen saver types.
PMI_prohibitedFeatures
This flag field allows you to restrict the display of your module so that it will only appear to users who don't have certain privileges. If you had two modules, advanced UI and simple UI, one might be for the sysop and the other for normal users--this would keep the sysop's Preferences from being "cluttered" by the plain user module. The flags available are the same as those for PMI_requiredFeatures .
PMI_minLevel
This field allows you to specify the minimum User UI Level in which your module should appear. If the module's UI is very complicated and will confuse novice users, use this field to hide it from them.
PMI_maxLevel ;
This field allows you to specify the maximum User UI Level in which your module should appear. If you have two versions of your module--one for advanced users and one for novice users--use this field to hide the novice module from the advanced users.
PMI_monikerList
Like the moniker list for an application, this will provide the icon shown on the main Preferences screen. Create this using the Icon editor. You should have icon formats 64x40 color, 64x40 monochrome, and 64x18 monochrome.
PMI_monikerToken
A four character token and manufacturer ID by which to recognize the module.
| Up: GEOS SDK TechDocs | Up | Prev: 1 Providing the UI | Next: 3 Important Messages


Important Messages

Config Library: 3 Important Messages

| Up: GEOS SDK TechDocs | Up | Prev: 2 Module Information Routine | Next: 4 Object Class Reference

If you're going to create a subclass for use in a Preferences module, the following messages will be of special interest, as they signal that the user is carrying out certain important actions.

MSG_PREF_INIT
When a dialog box containing Pref objects is brought up, each object will receive MSG_PREF_INIT. Developers can subclass MSG_PREF_INIT to perform basic initialization procedures.
MSG_META_LOAD_OPTIONS
Generic objects (and by inheritance, Pref- objects) respond to MSG_META_LOAD_OPTIONS by loading a value from the appropriate place in the .INI file. This place is determined by the object's ATTR_GEN_INIT_FILE_KEY field. If the object has an ATTR_GEN_INIT_FILE_CATEGORY field, that category will be used; otherwise the default handler will look up the Generic tree for an object with a ATTR_GEN_INIT_FILE_CATEGORY field defined and will use the value stored there.
Unlike a GenInteraction, a PrefInteraction will relay this message to its children. When it's time to load options from the .INI file, a MSG_META_LOAD_OPTIONS will be sent to the top level of the module's UI tree. This message should be propagated down the tree to reach all objects which should read their values from the .INI file. This is why it is suggested that you use PrefInteractions in place of GenInteractions if any Pref- objects will be underneath. If you create a new sort of object which should relay this message to other objects, be sure to do so.
MSG_META_SAVE_OPTIONS
Generic objects (and by inheritance, Pref- objects) respond to MSG_META_SAVE_OPTIONS by saving a value to the appropriate place in the .INI file. This place is determined by the object's ATTR_GEN_INIT_FILE_KEY field. If the object has an ATTR_GEN_INIT_FILE_CATEGORY field, that category will be used; otherwise the default handler will look up the Generic tree for an object with a ATTR_GEN_INIT_FILE_CATEGORY field defined and will use the value stored there.
Unlike a GenInteraction, a PrefInteraction will relay this message to its children. When it's time to save options to the .INI file, a MSG_META_SAVE_OPTIONS will be sent to the top level of the module's UI tree. This message should be propagated down the tree to reach all objects which should write their values to the .INI file. This is why it is suggested that you use PrefInteractions in place of GenInteractions if any Pref- objects will be underneath. If you create a new sort of object which should relay this message to other objects, be sure to do so.
Often, interceptors of this message will carry out whatever actions are necessary to carry out the user's wishes in those cases where writing something to the .INI file is not enough to bring the change about.
MSG_GEN_RESET
If the user clicks on a "Revert" or "Cancel" button, all values in UI gadgets should revert to those values originally stored in the .INI file. You will note that many Pref- objects have an extra field to store their "original" value. If you create your own kind of Preferences object, keep in mind that it will need some way to store the "original" value (probably initialized while handling MSG_META_LOAD_OPTIONS ) and should intercept MSG_GEN_RESET to replace its value with the "original" value.
MSG_GEN_LOAD_OPTIONS
Generic objects (and thus Pref- objects) respond to MSG_META_LOAD_OPTIONS by sending themselves this message. This message is only meant to be used for managing the .INI file; any other functionality should occur in the handling of MSG_META_LOAD_OPTIONS.
MSG_GEN_SAVE_OPTIONS
Generic objects (and thus Pref- objects) respond to MSG_META_SAVE_OPTIONS by sending themselves this message. This message is only meant to be used for managing the .INI file; any other functionality should occur in the handling of MSG_META_SAVE_OPTIONS .

If you are interested in detecting certain user actions, the following list may prove useful:

User Opens Dialog
Any objects which need to do some initialization when the user first opens the module's dialog box should intercept MSG_PREF_INIT . After receiving MSG_PREF_INIT , each object will receive a MSG_META_LOAD_OPTIONS .
User clicks Apply
Assuming the top object of the module's UI tree is a PrefDialog and that the dialog has been set up as a Properties dialog (as it is by default), then this object will receive a MSG_GEN_APPLY when the user activates the Apply trigger. The Pref dialog responds to this message by sending itself a MSG_META_SAVE_OPTIONS , which it will then pass on to its children.
User clicks Cancel or Reset
All Gen- and Pref- objects within the UI tree will receive a MSG_GEN_RESET .
| Up: GEOS SDK TechDocs | Up | Prev: 2 Module Information Routine | Next: 4 Object Class Reference


Object Class Reference

Config Library: 4 Object Class Reference

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Important Messages | Next: 4.1 PrefClass

If you will use any of the Pref- classes in your module, the following reference material may prove useful.

PrefClass

PrefValueClass

PrefItemGroupClass

PrefStringItemClass

PrefBooleanGroupClass

PrefDynamicListClass

TitledGlyphClass

PrefInteractionClass

PrefDialogClass

PrefTextClass

PrefControlClass

PrefTimeDateControlClass

PrefTriggerClass

PrefTocListClass

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Important Messages | Next: 4.1 PrefClass


PrefClass

Config Library: 4.1 Object Class Reference: PrefClass

| Up: GEOS SDK TechDocs | Up | Prev: 4 Object Class Reference | Next: 4.2 PrefValueClass

PrefClass defines several fields of instance data which will be used by all of its subclasses.

There are several variable data fields which make sure that a given Preference gadget will not be usable by those without the proper privileges; other fields make sure that the gadgets will not appear to those users whose UI levels indicate that they would be confused by the presence of such an object. Keep in mind that if all gadgets in your module demand some privilege or User level, you should reflect this in the routine which provide Preferences module information about your module.

Code Display 22-4 PrefClass Instance Data

@class PrefClass, GenClass, master, variant;
@instance PrefAttributes  PI_attrs = (PA_LOAD_IF_USABLE|PA_SAVE_IF_USABLE);
typedef ByteFlags  PrefAttributes;
#define  PA_REBOOT_IF_CHANGED 0x80
/* This bit signals that changes in the state of
 * this object requires a system reboot to take effect. */
#define PA_LOAD_IF_USABLE 0x40
 /* Load options only if this object is usable (this is ON by default). */
#define PA_SAVE_IF_USABLE 0x20
 /* Save options only if this object is usable (this is ON by default) */
#define PA_SAVE_IF_ENABLED 0x10
 /* Save options only if this object is enabled */
#define PA_SAVE_IF_CHANGED 0x08
 /* Save options only if this object has changed. */
@vardata optr  ATTR_PREF_REBOOT_STRING;
/* This is the string passed to MSG_PREF_MGR_DO_REBOOT_DIALOG -- the
 * string must be in a SHARABLE LMEM resource and is incorporated into the
 * middle of another sentence. In English, the sentence is "The system software
 * will shut down and restart to change the <reboot_string>. Do you wish
 * to proceed?"
 *
 * If an object has its PA_REBOOT_IF_CHANGED attribute set, it will scan for
 * this attribute, first in itself, then up the generic tree, until it
 * finds one. */
@reloc ATTR_PREF_REBOOT_STRING, 0, optr;
@vardata PrefMgrFeatures ATTR_PREF_REQUIRED_FEATURES;
/* features bits that must be set for this object to be usable. */
@vardata PrefMgrFeatures ATTR_PREF_PROHIBITED_FEATURES;
/* features bits that must not be set for this object to be usable. */
@vardata UIInterfaceLevel ATTR_PREF_MIN_LEVEL;
/* interface level below which this object will not be usable */
@vardata UIInterfaceLevel ATTR_PREF_MAX_LEVEL;
/* interface level above which this object will not be usable */

In addition to the instance data fields, there are messages which allow for the dynamic retrieval and updating of these fields.

MSG_PREF_SET_INIT_FILE_CATEGORY

void MSG_PREF_SET_INIT_FILE_CATEGORY(
        const char *category);

Pref objects automatically respond to this message by changing the value of the ATTR_GEN_INIT_FILE_CATEGORY variable data field. This is a utility message; similar functionality is available via MSG_META_ADD_VAR_DATA.

Source: Anywhere

Destination: Any PrefClass object.

Parameters: category Null-terminated category string.

Return: Nothing.

MSG_PREF_HAS_STATE_CHANGED

Boolean MSG_PREF_HAS_STATE_CHANGED();

This message determines whether the object or any of its children have changed since the last time options were loaded. This is used in conjunction with the PA_REBOOT_IF_CHANGED flag that can be set for an object--this flag signals that, if the object changes state, then GEOS should be restarted for the change to go into effect.

Source: PrefClass object sends this message to itself when handling MSG_PREF_GET_REBOOT_INFO .

Destination: Self.

Parameters: None.

Return: Should return true (i.e. non-zero) if state has changed, false (i.e. zero) otherwise.

Interception: New PrefClass subclasses will use this to compare the object's current state against the "original" state, assuming one was stored.

MSG_PREF_GET_REBOOT_INFO

optr MSG_PREF_GET_REBOOT_INFO();

This message asks whether the system will need to reboot as a result of changes. If a reboot is necessary, the dialog will then notify the user of the reboot, and give the user a chance to abort (since the system reset may be a lengthy process on some systems).

Source: PrefDialog sends this message to all children on an apply.

Destination: Any Pref- object.

Interception: Subclasses should check the PA_REBOOT_IF_CHANGED flag--if the flag is set, the object should compare its present value to its "original" value. If a reboot is never needed, then do nothing.
If a reboot is necessary, the method should return the string to use when rebooting. The standard way to do this is via MSG_PREF_GET_REBOOT_STRING .
If possible, developers should try to avoid situations where a system reset is necessary, and use mechanisms such as the General Change Notification instead.

Parameters: None.

Return: Zero if no reboot needed; otherwise the OD of string to insert in the confirm-shutdown dialog box. The string will be inserted into a sentence; the string should describe what is changing. You may wish to use the value of ATTR_PREF_REBOOT_STRING.

See Also: MSG_PREF_GET_REBOOT_STRING , ATTR_PREF_REBOOT_STRING.

MSG_PREF_GET_REBOOT_STRING

optr MSG_PREF_GET_REBOOT_STRING();

This message should return the optr of a string in a sharable local memory resource. This string will display in the dialog box which asks the user if it is all right to shut down. If a given object doesn't know of a good string to supply, it should forward the message on to its parent in the generic tree. The default PrefClass handler returns the value stored in ATTR_PREF_REBOOT_STRING.

Source: Pref- object sends this message to itself.

Destination: Any PrefClass object.

Parameters: Nothing.

Return: Object Pointer of string (or NULL if not found).

MSG_PREF_INIT

void MSG_PREF_INIT(
        PrefMgrFeatures 		features,
        UIInterfaceLevel 		level);

This message initializes the object. The object should perform any required set-up to appear on-screen. The PrefDialog object will send this object to all of its children when the dialog is first initiated.

Source: Generic Parent

Destination: Any Pref- object.

Parameters: features This structure tells you a bit what sorts of changes the user prepared to make.

level
The user's User Level.

Return: None.

Interception: Default behavior is to determine whether to make the object usable based on UI level and features (or will be, when implemented). Subclass should call superclass before handling, as subclass behavior may be different if the object is not usable.

MSG_PREF_NOTIFY_DIALOG_CHANGE

void MSG_PREF_NOTIFY_DIALOG_CHANGE(
        PrefDialogChangeType 			type);

This message goes out via PDGCNLT_DIALOG_CHANGE list bound to PrefDialog object telling interested parties of a change in the box's state. It is sent when the dialog box is being opened, closed, or destroyed.

Source: PrefDialog object.

Destination: Any Pref- object registered on PDCGCNLT_DIALOG_CHANGE.

Interception: This message has no default handler; it must be intercepted to do anything useful.

Structures:

typedef enum {
     PDCT_OPEN,
    PDCT_CLOSE,
    PDCT_DESTROY,
    PDCT_RESTART,
    PDCT_SHUTDOWN
}  PrefDialogChangeType;

MSG_PREF_SET_ORIGINAL_STATE

void MSG_PREF_SET_ORIGINAL_STATE();

This message copies the object's current state to the "original" value field, which will be used on a MSG_GEN_RESET .

Parameters: None.

Return: Nothing.

Interception: Subclasses should respond to this message by copying their current value to whatever instance field represents the "original" state of the object.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Object Class Reference | Next: 4.2 PrefValueClass


PrefValueClass

Config Library: 4.2 Object Class Reference: PrefValueClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 PrefClass | Next: 4.3 PrefItemGroupClass

PrefValue objects behave like GenValue objects except that they have the ability to store an "original" value in the PVI_originalValue instance data field. Unlike GenValue objects, PrefValues can only handle single word values.

Code Display 22-5 PrefValueClass Instance Data

@class PrefValueClass, PrefClass;
@instance word  PVI_originalValue = 0;
@vardata void  ATTR_PREF_VALUE_ORIG_IS_INDETERMINATE;
@vardata word ATTR_PREF_VALUE_ROUND;
/* Specifies an integer value to which the value will be rounded on .INI file
 * reads/writes. For instance, with a value for 10 here, the value would always
 * round off to the nearest 10. Note that the user can still type in an illegal
 * value, but this value will be rounded before saving to the .INI file.*/

MSG_PREF_VALUE_SET_ORIGINAL_VALUE

void MSG_PREF_VALUE_SET_ORIGINAL_VALUE(
        word 		orig,
        Boolean 		indeterminate);

This message sets the "original" value of the object--it also sets the value stored by the superclass.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 PrefClass | Next: 4.3 PrefItemGroupClass


PrefItemGroupClass

Config Library: 4.3 Object Class Reference: PrefItemGroupClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 PrefValueClass | Next: 4.4 PrefStringItemClass

This class acts like GenItemGroupClass , but has extended functionality for reading/writing strings to the .INI file. When the user triggers the dialog's Apply trigger (when the PrefItemGroup receives a MSG_META_SAVE_OPTIONS ), the PrefItemGroup will respond by writing out strings based upon which of its children items are presently selected. These strings can be taken from the items' monikers or from strings used with PrefStringItemClass objects.

When loading options, if either the PIFF_USE_ITEM_MONIKERS or PIFF_USE_ITEM_STRINGS is set for the item group, then it will select its children based on the strings stored in the .INI file.Otherwise, the item group will default to the GenItemGroupClass functionality, which uses the identifier of the items as a basis for selection.

Code Display 22-6 PrefItemGroupClass Instance Data

@instance PrefInitFileFlags  PIGI_initFileFlags = 0;
typedef ByteFlags  PrefInitFileFlags;
#define  PIFF_USE_ITEM_STRINGS 0x80
/*
 * If set, then the item group's children must be of class PrefStringItemClass,
 * and their strings will be used to interact with the .INI file. */
#define PIFF_USE_ITEM_MONIKERS 0x40
/* If set, the monikers of the items are used to interact with the .INI file. */
#define PIFF_APPEND_TO_KEY 0x20
/* If set, the strings in this list will be ADDED
 * to strings that may already exist for this key */
#define PIFF_ABSENT_KEY_OVERRIDES_DEFAULTS 0x10
/* If set, an absent key for the item group will cause it to
 * behave as if an empty key were in the .INI file, effectively
 * overriding any default values stored with the group when it was
 * compiled. Used primarily by those groups where one subclasses
 * MSG_PREF_STRING_ITEM_CHECK_IF_IN_INIT_FILE_KEY to determine the
 * initial setting, rather than looking in the .INI file. */
@instance word PIGI_originalSelection = 0;
@instance word PIGI_originalNumSelections = 0;
@instance byte PIGI_suspendCount = 0;
/* When suspend count is nonzero, the item group won't update text objects, nor
 * will it enable/disable objects when the selection changes. */
@vardata PrefEnableData ATTR_PREF_ITEM_GROUP_ENABLE;
/* allows setting up an object to be enabled/disabled when
 * settings are made in the item group */
typedef struct {
    word 		 PED_item; 		/* Identifier of the item that controls
                     * enabling/disabling of object. If this
                     * is GIGS_NONE, then the action will be
                     * performed if no items are selected. */
    ChunkHandle 		PED_lptr; 		/* Object to be enabled/disabled */
     PrefEnableFlags 		PED_flags;
}  PrefEnableData;
typedef ByteFlags  PrefEnableFlags;
#define  PEF_DISABLE_IF_SELECTED 0x80
/* Disable the object if the associated item is selected,
 * otherwise do the opposite. */
#define PEF_DISABLE_IF_NONE 0x40
/* If this flag is set, then the PED_item field is ignored.
 * Instead, the item group will disable the specified object if
 * no items are selected -- or if there are no items in the list. */
@vardata word  ATTR_PREF_ITEM_GROUP_OVERRIDE;
/* This attribute is used to specify an item which will act
 * as an OVERRIDE for all other items (in a non-exclusive
 * item group).
 *
 * When the specified item is SELECTED, then all other items are DE-SELECTED.
 * When any other item is SELECTED, the override item is DE-SELECTED.
 * If all items are DE-SELECTED, the override item becomes SELECTED */
@vardata ChunkHandle ATTR_PREF_ITEM_GROUP_TEXT_DISPLAY;
/* chunk handle of a GenText object that will be updated
 * with the moniker of the currently selected item on APPLY/RESET. */
@vardata ChunkHandle ATTR_PREF_ITEM_GROUP_STATUS_TEXT;
/* chunk handle of a GenText object that will be updated every
 * time this object receives a MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG. */
@vardata char ATTR_PREF_ITEM_GROUP_EXTRA_STRING_SECTION[];
/* If a string is given -- the string will always be written
 * out as the FIRST string section for the .INI file key. For
 * example, the "Memory" item group uses this to always insert
 * "disk.geo" whenever writing out the memory types. Note:
 * this item is only written if the item group has either the
 * PIFF_USE_ITEM_MONIKERS or PIFF_USE_ITEM_STRINGS set. */

MSG_PREF_ITEM_GROUP_SET_ORIGINAL_SELECTION

void 	MSG_PREF_ITEM_GROUP_SET_ORIGINAL_SELECTION(
word selection);

Set the "originalSelection" field of the item group. Also, sends MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION to superclass. This message can only be used with exclusive item groups--for other types, use the GenItemGroup messages, and then send the object MSG_PREF_SET_ORIGINAL_STATE .

Source: Anywhere.

Destination: An exclusive PrefItemGroup.

Parameters: selection Which item to select.

Return: Nothing.

Interception: Unlikely.

MSG_PREF_ITEM_GROUP_GET_SELECTED_ITEM_TEXT

word 	MSG_PREF_ITEM_GROUP_GET_SELECTED_ITEM_TEXT(
        char 	*buffer,
        word 	bufSize);

This message returns the text in the moniker of the selected item. The value returned is the number of characters in the string. If nothing is selected, the value returned is zero.

MSG_PREF_ITEM_GROUP_GET_ITEM_MONIKER

word MSG_PREF_ITEM_GROUP_GET_ITEM_MONIKER(@stack
        char 	*buffer,
        word 	bufSize,
        word 	identifier);

This message retrieves the text in the moniker of one of the items of a PrefItemGroup. This works for dynamic list subclasses as well. The message returns the number of characters in the returned text.

Parameters: buffer Buffer in which to return moniker text.

bufSize
Size of buffer.
identifier
Identifier of item whose moniker should be returned.

Return: Size of item's moniker.

MSG_PREF_ITEM_GROUP_UPDATE_TEXT

void MSG_PREF_ITEM_GROUP_UPDATE_TEXT(
        ChunkHandle 		textObject);

This message updates the specified text object with text of the currently selected item. The PrefItemGroup sends this to itself in conjunction with either ATTR_PREF_ITEM_GROUP_TEXT_DISPLAY or ATTR_PREF_ITEM_GROUP_STATUS_TEXT. By default, this message will also cause the text object to send out its status message.

Source: Self.

Destination: Self.

Interception: Default behavior is to fetch the moniker of the current selection, and sent that to the text. The subclasser can use different text, if desired.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 PrefValueClass | Next: 4.4 PrefStringItemClass


PrefStringItemClass

Config Library: 4.4 Object Class Reference: PrefStringItemClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 PrefItemGroupClass | Next: 4.5 PrefBooleanGroupClass

Objects of this class, used together with a PrefItemGroup with its PIFF_USE_ITEM_STRINGS flag set, allow for objects which act like GenItems but can write an arbitrary string out to the .INI file if the user selects them.

Code Display 22-7 PrefStringItemClass Instance Data

@class PrefStringItemClass, GenItemClass;
@instance ChunkHandle  PSII_initFileString = 0;

MSG_PREF_STRING_ITEM_SET_INIT_FILE_STRING

void MSG_PREF_STRING_ITEM_SET_INIT_FILE_STRING(
        const char 		*str);

This message sets the string that is read and written in the .INI file.

Interception: Unlikely but possible. Default behavior sets the string and then checks again with the .INI file to make sure item should be selected.

MSG_PREF_STRING_ITEM_CHECK_IF_IN_INIT_FILE_KEY

Boolean MSG_PREF_STRING_ITEM_CHECK_IF_IN_INIT_FILE_KEY(
        PrefItemGroupStringVars 				*vars);

This message checks the passed string to see if the string bound to this item is in one of its pieces. A PrefItemGroup will send this object when loading options if it has the PIFF_USE_ITEM_STRINGS flag set.

Source: PrefItemGroup object.

Destination: PrefStringItem object.

Interception: May be intercepted if the subclass needs to check other things to decide whether it should be selected. If you also wish to have the default behavior, you may call the superclass either before or after you've made your own decision, as appropriate.

Parameters: vars Information about the string.

Return: Returns true (i.e. non-zero) if bound string is within the string stored in the local variable.

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 PrefItemGroupClass | Next: 4.5 PrefBooleanGroupClass


PrefBooleanGroupClass

Config Library: 4.5 Object Class Reference: PrefBooleanGroupClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 PrefStringItemClass | Next: 4.6 PrefDynamicListClass

This class behaves as GenBooleanGroup, except that it saves its original state and handles MSG_GEN_RESET appropriately. It can also use item strings or monikers when loading and saving options.

Code Display 22-8 PrefBooleanGroupClass Instance Data

@class PrefBooleanGroupClass, PrefClass;
@instance PrefInitFileFlags 				 PBGI_initFileFlags = 0;
@instance word 				PBGI_originalState = 0;

MSG_PREF_BOOLEAN_GROUP_SET_ORIGINAL_STATE

void MSG_PREF_BOOLEAN_GROUP_SET_ORIGINAL_STATE(
        word state);

This message sets the "original" state of the Boolean group.

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 PrefStringItemClass | Next: 4.6 PrefDynamicListClass


PrefDynamicListClass

Config Library: 4.6 Object Class Reference: PrefDynamicListClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 PrefBooleanGroupClass | Next: 4.7 TitledGlyphClass

Objects of PrefDynamicListClass act like GenDynamicList objects, but with the intelligence to write their data to the .INI file. Developers will generally not use this class directory, but instead will subclass, intercepting the following messages:

MSG_PREF_DYNAMIC_LIST_BUILD_ARRAY
To build the data structures which will be used when later supplying monikers
MSG_PREF_ITEM_GROUP_GET_ITEM_MONIKER
To supply the moniker for a requested item on demand
MSG_PREF_DYNAMIC_LIST_FIND_ITEM
To find a given item when passed its moniker.

By handling these three messages, the developer will have a dynamic list that can properly load and save its selection and will scroll to the correct item in response to keyboard input.

The load and save mechanism is currently only configured for exclusive dynamic lists.

MSG_PREF_DYNAMIC_LIST_BUILD_ARRAY

void MSG_PREF_DYNAMIC_LIST_BUILD_ARRAY();

The PrefDynamicList sends itself this message in response to MSG_PREF_INIT.

Source: Self.

Destination: Self.

Interception: The subclasser should create an array of item strings, ideally sorted.

MSG_PREF_DYNAMIC_LIST_FIND_ITEM

 Boolean MSG_PREF_DYNAMIC_LIST_FIND_ITEM(
        word 		*itemPtr,
        const char 		*str,
        Boolean 		ignoreCase);

This message finds an item given a moniker. It is normally used in the context of loading options.

Source: Self.

Destination: Self.

Parameters: itemPtr Pointer to space in which to return a word.

str
String to search for.
ignoreCase
Flag requesting a case-insensitive search.

Return: Returns true (i.e. non-zero) if an item was found with a moniker matching the passed string; itmPtr will have been filled with the number of the matching item. If no such item was found, the message will return false (i.e. zero) and itmPtr will point to the first item after the requested item.

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 PrefBooleanGroupClass | Next: 4.7 TitledGlyphClass


TitledGlyphClass

Config Library: 4.7 Object Class Reference: TitledGlyphClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.6 PrefDynamicListClass | Next: 4.8 PrefInteractionClass

Each TitledGlyph displays two monikers, one icon and one text. TitledGlyphClass has no unique instance data or messages. You are expected to provide a list of monikers in the GI_visMoniker field, and this list should include several icons and a text moniker. Normally the same icon is used as by the routine providing Preferences module information.

| Up: GEOS SDK TechDocs | Up | Prev: 4.6 PrefDynamicListClass | Next: 4.8 PrefInteractionClass


PrefInteractionClass

Config Library: 4.8 Object Class Reference: PrefInteractionClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.7 TitledGlyphClass | Next: 4.9 PrefDialogClass

If you have any Preferences objects which write out their values to the .INI file, and if you would normally have those objects appear under a GenInteraction, then you should have them appear under a PrefInteraction instead.

Objects of the class will forward the following messages on to their children:

MSG_PREF_INIT , MSG_PREF_SET_ORIGINAL_STATE
Object will send these messages to every child that is a subclass of PrefClass .
MSG_META_LOAD_OPTIONS
Object will send this to each child that satisfies the appropriate criteria (based on the child's PA_LOAD_IF_USABLE flag).
MSG_META_SAVE_OPTIONS
Object will send this to each child that satisfies the appropriate criteria (based on the child's PA_SAVE_IF_CHANGED, PA_SAVE_IF_ENABLED, and PA_SAVE_IF_USABLE flags).
MSG_PREF_HAS_STATE_CHANGED , MSG_PREF_GET_REBOOT_INFO
Propagates these messages to children, returning the value from the first child that returns a non-null response.

Code Display 22-9 PrefInteractionClass Instance Data

@class PrefInteractionClass, PrefClass;
@instance PrefInteractionAttrs  PII_attrs = 0;
typedef ByteFlags  PrefInteractionAttrs;
#define  PIA_LOAD_OPTIONS_ON_INITIATE 0x80
/* If set, then the dialog will send MSG_PREF_INIT,
 * followed by MSG_META_LOAD_OPTIONS to itself when it
 * receives a MSG_GEN_INTERACTION_INITIATE */
#define PIA_SAVE_OPTIONS_ON_APPLY 0x40
/* This flag is normally OFF to allow non-dialog prefInteractions to reside inside
 * other interactions without duplicate SAVE_OPTIONS messages being sent. */
| Up: GEOS SDK TechDocs | Up | Prev: 4.7 TitledGlyphClass | Next: 4.9 PrefDialogClass


PrefDialogClass

Config Library: 4.9 Object Class Reference: PrefDialogClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.8 PrefInteractionClass | Next: 4.10 PrefTextClass

The top level object of any Preferences module UI tree should probably be a PrefDialog object. If the user's changes will necessitate a system reset, then this object will be responsible for rebooting the system.

On receiving MSG_GEN_APPLY , the dialog sends MSG_PREF_GET_REBOOT_INFO to itself (which is propagated to all children via PrefInteractionClass ). If any children require reboot, then the dialog sends MSG_PREF_DIALOG_CONFIRM_REBOOT to itself, which normally puts up a confirmation dialog box (this can be subclassed to provide other behavior). If user confirms reboot, then the dialog will send MSG_PREF_DIALOG_REBOOT to itself, and MSG_GEN_APPLY to its superclass to save the options (it being assumed that the PIA_SAVE_OPTIONS_ON_APPLY flag is set).

Code Display 22-10 PrefDialogClass Instance Data

@class PrefDialogClass, PrefInteractionClass;
@default GII_visibility = GIV_DIALOG;
@default GII_type = GIT_PROPERTIES;
@default GII_attrs = @default | GIA_NOT_USER_INITIATABLE | GIA_MODAL;
@default PII_attrs = @default | PIA_SAVE_OPTIONS_ON_APPLY;

MSG_PREF_DIALOG_CONFIRM_REBOOT

@message Boolean MSG_PREF_DIALOG_CONFIRM_REBOOT(
        optr 		string);

By default, the PrefDialog will respond to this message by putting up a dialog box asking the user if he wants to restart the system.

Parameters:

string
Object pointer of string which will be inserted in a sentence of the form "The system software will shut down and restart to change the string."

Return: Returns true (i.e. non-zero) to confirm the reboot; returns false (i.e. zero to deny the reboot.

MSG_PREF_DIALOG_REBOOT

void MSG_PREF_DIALOG_REBOOT();

This message causes GEOS to restart. This message does not return.

| Up: GEOS SDK TechDocs | Up | Prev: 4.8 PrefInteractionClass | Next: 4.10 PrefTextClass


PrefTextClass

Config Library: 4.10 Object Class Reference: PrefTextClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.9 PrefDialogClass | Next: 4.11 PrefControlClass

Objects of this class act like GenText objects, but have mechanisms to write their text string to the .INI file.

Code Display 22-11 PrefTextClass Instance Data

@class PrefTextClass, PrefClass;
@instance ChunkHandle  PTI_originalText = 0;
@vardata ChunkHandle  ATTR_PREF_TEXT_INIT_FILE_CATEGORY_TARGET;
/* PrefTextClass has the wonderful ability that it can set another
 * object's .INI. file category from its text. Category gets updated
 * whenever the text object receives MSG_GEN_TEXT_SEND_STATUS_MSG.
 * To make this happen, put the ChunkHandle of the object in this field. */
| Up: GEOS SDK TechDocs | Up | Prev: 4.9 PrefDialogClass | Next: 4.11 PrefControlClass


PrefControlClass

Config Library: 4.11 Object Class Reference: PrefControlClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.10 PrefTextClass | Next: 4.12 PrefTimeDateControlClass

This subclass of PrefInteractionClass acts like GenControl, but bypasses some GenControl functionality. This class is assumed not to be on notification lists. Objects of this class will pass MSG_GEN_RESET to their children as a normal GenInteraction would. This class has no special instance data or messages.

| Up: GEOS SDK TechDocs | Up | Prev: 4.10 PrefTextClass | Next: 4.12 PrefTimeDateControlClass


PrefTimeDateControlClass

Config Library: 4.12 Object Class Reference: PrefTimeDateControlClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.11 PrefControlClass | Next: 4.13 PrefTriggerClass

This controller allows the user to set the system time. This class' messages and instance data fields are internal. To get and set the date and time directly, use TimerGetDateAndTime() and TimerSetDateAndTime() .

| Up: GEOS SDK TechDocs | Up | Prev: 4.11 PrefControlClass | Next: 4.13 PrefTriggerClass


PrefTriggerClass

Config Library: 4.13 Object Class Reference: PrefTriggerClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.12 PrefTimeDateControlClass | Next: 4.14 PrefTocListClass

PrefTriggerClass is not a subclass of PrefClass ; it is a subclass of GenTriggerClass . It acts like a normal trigger except that when activated it will send a second message if it has been set up with a ATTR_PREF_TRIGGER_ACTION field.

Code Display 22-12 PrefTriggerClass Instance Data

@class PrefTriggerClass, GenTriggerClass;
@vardata PrefTriggerAction  ATTR_PREF_TRIGGER_ACTION;
typedef struct {
     word  PTA_message;
     optr PTA_dest;
}  PrefTriggerAction;
| Up: GEOS SDK TechDocs | Up | Prev: 4.12 PrefTimeDateControlClass | Next: 4.14 PrefTocListClass


PrefTocListClass

Config Library: 4.14 Object Class Reference: PrefTocListClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.13 PrefTriggerClass

PrefTocListClass allows users to choose a device driver by selecting the appropriate device name. It is assumed that each driver can control one or more types of devices. When you set up each driver, you will set up strings to describe which devices that driver can control.

This class is a fully functional subclass of PrefDynamicListClass (i.e. it handles those messages one must intercept to create a viable Preferences dynamic list class). It can be used to scan a list of files (not necessarily device drivers), as long as all of those files are in the same directory and have the same characters in their token.

Code Display 22-13 PrefTocListClass Instance Data

@class PrefTocListClass, PrefDynamicListClass;
@instance TocUpdateCategoryFlags  PTLI_flags = 0;
typedef WordFlags  TocUpdateCategoryFlags;
#define  TUCF_EXTENDED_DEVICE_DRIVERS 0x8000
/* Files being enumerated are assumed to be extended device drivers. */
#define TUCF_CUSTOM_FILES 0x4000
/* The TUCP_fileArrayElementSize field will be used when creating the files array.
 * Otherwise, each element of the files array will be of size TocFileStruct.
 * NOTE: If this flag is used, the data structure used for each file
 * element MUST contain TocFileStruct as its first element. */
#define TUCF_ADD_CALLBACK 0x2000
/* TUCP_addCallback contains a fptr to a callback
 * routine that will be called when a file is added to the files array. */
#define TUCF_DIRECTORY_NOT_FOUND 0x1000
/* Don't actually scan the directory, because it doesn't exist.
 * Just create the category, and leave it empty. */
@instance TocCategoryStruct PTLI_tocInfo = {"", 0, 0};
typedef struct {
    TokenChars 		 TCS_tokenChars;
    DBGroupAndItem 		TCS_files; 		/* file name array */
    DBGroupAndItem 		TCS_devices; 		/* device name array--only if
                     * TCF_EXTENDED_DEVICE_DRIVERS is set. */
}  TocCategoryStruct;
@vardata PrefTocExtraEntry  ATTR_PREF_TOC_LIST_EXTRA_ENTRY_1;
@vardata PrefTocExtraEntry ATTR_PREF_TOC_LIST_EXTRA_ENTRY_2;
typedef struct {
    ChunkHandle 		 PTEE_item;
    /* lptr of item name. For device lists, this is
     * the device. For others, this is the file name */
    ChunkHandle 		PTEE_driver;
    /* Driver name (for device lists ONLY) */
    word 		PTEE_info;
    /* Extra word of information */
}  PrefTocExtraEntry;
@vardata char ATTR_PREF_TOC_LIST_INFO_KEY[];
/* This key is used to write the "info" word for device lists */

Use ATTR_GEN_PATH_DATA to tell the PrefTocList object in which directory to look for the device drivers. The PrefTocList will store the list of device drivers and device names in DB items referenced in the PTLI_tocInfo field's TCS_files and TCS_devices slots--it will do this automatically, so if you wish the default behavior, leave these as zero when declaring the object. If you will write your own handlers for keeping track of driver information, you may wish to work with these fields as there are some routines which have been set up to work with them.

Code Display 22-14 PrefTocList Framework

@object PrefTocListClass MyPrefToc = {
    PTLI_flags		= @default | TUCF_EXTENDED_DEVICE_DRIVERS;
    PTLI_tocInfo		= {"TOKN", 0, 0};
    HINT_ITEM_GROUP_SCROLLABLE;
    ATTR_GEN_INIT_FILE_KEY = "keyName";
    ATTR_GEN_PATH_DATA = { file path containing drivers };
}

The following routines are rather esoteric. You should only use them if you wish to subclass PrefTocListClass ' behavior and wish to continue using its existing data structures.

TocSortedNameArrayAdd()
Use this routine to add a new name to a sorted array. The new name will be inserted in its correct place in the alphabetically sorted array.
TocSortedNameArrayFind()
This routine determines whether a given name is in the TOC List's sorted name array, and will return the position of the name's element if found.
TocFindCategory()
Use this routine to find a category in the Toc file. The TCS_tokenChars field of the passed TocCategoryStruct should already be filled in; this routine will fill in the other fields.
TocNameArrayFind()
Use this routine to get back the token associated with a name in the name array. If the name is not found, the routine will return CA_NULL_ELEMENT.
TocNameArrayGetElement()
This routine takes a name array and a token and returns information about the name array element with the given token.
TocUpdateCategory()
This complicated routine will update a category using a callback routine which you must write. It updates the file lists by scanning the current directory.
TocNameArrayAdd()
Use this routine to add an element to a Toc name array.
TocDBLock()
Use this routine to lock one of the name arrays.
TocDBLockGetRef()
This C-only routine does the same thing as TocDBLock() and also returns the DB item's pointer and optr.
TocGetFileHandle()
Use this routine to get the handle of the file in which all Toc device driver information is stored.

MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_INFO

Boolean MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_INFO(
        word 		*infoPtr);

This message returns the information word about the currently selected item. This message can only be used with PrefTocList objects that have the TCF_EXTENDED_DEVICE_DRIVERS flag set.

Parameters: infoPtr Pointer to a word in which to return the info word.

Return: Returns false (i.e. zero) if item was found, returns true (i.e. non-zero) if item was not found.

MSG_PREF_TOC_LIST_GET_SELECTED_DRIVER_NAME

word MSG_PREF_TOC_LIST_GET_SELECTED_DRIVER_NAME(
        char 		*buf,
        word 		bufSize);

This message returns the name of the driver for the selected device. This message may only be used if TCF_EXTENDED_DEVICE_DRIVERS is set.

Parameters: buf Pointer to buffer to fill with the driver name.

bufSize
The size of buf.

Return: If the device name fits in buf, then buf is filled and the message returns the size of the device name. If the device name doesn't fit, then the message will return zero.

MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_PATH

void MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_PATH(
        TocItemPath 		*data);

This message returns the absolute path of the selected driver or file.

Structures:

typedef struct {
  word 			 TIP_disk; /* disk handle */
  MemHandle 			TIP_pathBlock;
    /* Handle of block holding path (locked) */
  char 			*TIP_path;
    /* Locked null-terminated absolute path */
}  TocItemPath;

MSG_PREF_TOC_LIST_CHECK_DEVICE_AVAILABLE

Boolean MSG_PREF_TOC_LIST_CHECK_DEVICE_AVAILABLE(
        word 		*retvalPtr);

This message determines if the device selected by the list is available in the machine. This message may only be called for PrefTocList objects that have the TCF_EXTENDED_DEVICE_DRIVERS flag set.

Parameters: retValPtr Pointer to a word which will be filled with the return value.

Return: Returns true (i.e. non-zero if the device is available; if the driver is a video driver, *retValPtr is set to the device's DisplayType . If the device is not available, returns false (i.e. zero); retValPtr is zero of the device doesn't exist; otherwise it will be the value of the appropriate GeodeLoadError plus one.

| Up: GEOS SDK TechDocs | Up | Prev: 4.13 PrefTriggerClass


The Math Library

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Basic Math Functions
| One-page Version of this Chapter for Easy Printing

The Math Library allows high precision computations not available through standard integer operations. Floating Point (FP) numbers use decimal representation to express fractional quantities. This format also allows scientific notation to represent very large and very small numbers.

To enable floating point operations, an application must include a GEOS Math Library. There are two Math Libraries: math.h and math.goh . The file math.h includes what you need to get floating point numbers to work within your application. The object file, math.goh (which includes math.h ), includes the float-format controller, an object that allows the user to control how floating point numbers are formatted within an application.

In most cases, the mere inclusion of the Math Library will eliminate the need to directly call Math Library routines. (The C compiler will parse the mathematical expressions into appropriate floating point routines.) You may, however, wish to call some of these routines directly. Developers using Object Assembly must use this latter approach. C developers may also want to directly manipulate the FP stack for involved computations that they would rather not leave to the compiler. This approach is discussed in the latter half of this chapter.

Use of math.goh is optional, depending on the purpose of the application. A spreadsheet application, for example, might want to allow the user to format FP numbers. Other applications might not want to include this file.

1 Basic Math Functions
1.1 Algebraic Functions
1.2 Transcendental Functions
1.3 Random Number Generation
2 Conversions to Other Types
3 Float Formats
3.1 System-defined Formats
3.2 User-defined Formats
4 Direct FP Operations
4.1 Floating Point Numbers
4.2 The Floating Point Stack
4.3 Floating Point Math Routines

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Basic Math Functions
| One-page Version of this Chapter for Easy Printing


Basic Math Functions

The Math Library: 1 Basic Math Functions

| Up: GEOS SDK TechDocs | Up | Down | Prev: The Math Library | Next: 2 Conversions to Other Types

The Math Library includes many routines and structures that make the manipulation of FP numbers possible. Most of these are transparent to a C programmer.

C includes the following floating point types: float , double , and long double . A floating point number in GEOS is of type FloatNum , which uses the IEEE 80 bit standard (equivalent to a long double) to represent floating point values. FloatNum has overloaded the float, double, long double, and all basic math functions.

A FloatNum consists of a one bit sign, a 15 bit exponent, and a 64 bit mantissa. The maximum exponent allowed is 7FFEh. An exponent of 7FFFh (FP_NAN) signals an underflow or overflow. You may check the value of this exponent using the macro FLOAT_EXPONENT to extract the exponent of a FloatNum .

Code Display D-1 Extracting an Exponent

FloatNum myNum;
if (FLOAT_EXPONENT(&myNum) == FP_NAN)
{
     return(ERROR);
}

If you use any floats, doubles, or long doubles in your application, GEOS will convert these types into FloatNum values automatically and call the appropriate GEOS functions to manipulate the numbers. As this is the case, it is usually easier to declare any floating point variables in your application as FloatNum types (or long doubles). This cuts down on conversion time.

The Math Library provides all the familiar mathematical functions to manipulate these GEOS FP numbers (addition, multiplication, computation of logarithms, etc.). In C, most of these functions will automatically be called when their corresponding C operation involving FP numbers take place. (For example, when using the `+' binary operator to add two FP numbers, the Math Library will use the corresponding FloatAdd() routine.)

You may, under special circumstances, wish to use these math routines directly. If you do so, you will need to manipulate the floating point stack manually, pushing numbers on the stack and making sure numbers are in the proper stack location to perform each operation. In most cases, the FP math routines operate on numbers already in place on the FP stack; they take and return no arguments of their own.

Two functions you will need to use if you take this approach are FloatPushNumber() and FloatPopNumber() . FloatPushNumber() takes the address of a variable (of type FloatNum ) to push onto the FP stack; FloatPopNumber() takes the address of a buffer to place an FP number popped off the FP stack. Other routines (for example, FloatAdd() ) can then be called to operate on the FP stack. (See Adding Two FP Numbers for an example using this approach.)

There are many other routines that perform stack manipulation by shifting locations of FP numbers on the stack ( FloatRoll() , FloatDrop() , etc.). These routines are covered in detail in the latter part of this chapter because they are seldom needed in a C applications.

Algebraic Functions

Transcendental Functions

Random Number Generation

| Up: GEOS SDK TechDocs | Up | Down | Prev: The Math Library | Next: 2 Conversions to Other Types


Algebraic Functions

The Math Library: 1.1 Basic Math Functions: Algebraic Functions

| Up: GEOS SDK TechDocs | Up | Prev: 1 Basic Math Functions | Next: 1.2 Transcendental Functions

Algebraic routines perform algebraic operations on FP numbers. The Math Library provides all of the basic algebraic routines that operate on GEOS FP numbers (addition, subtraction, etc.).

If you wish to call these routines directly rather than rely on the C operations, you may manipulate the floating point stack directly. To add two numbers using FloatAdd() for example, you would use FloatPushNumber() twice to push the two values to add onto the FP stack, and then call FloatAdd() to operate on the FP stack. (See Adding Two FP Numbers .)

Code Display D-2 Adding Two FP Numbers

/*
 * The following two methods each add two FP numbers and return the result. The
 * first method is familiar C code. The second example uses the floating point
 * routines from math.h directly. Note that the C code will be assembled into code
 * that uses FloatAdd() also, but that this is transparent to the code.
 */
@method MyProcessClass, MSG_SUM_FLOATS {
    long double			number1, number2, number3;
    number1 = 1.0;
    number2 = 2.0;
    number3 = number1 + number2;
    return(number3);
}
@method MyProcessClass, MSG_SUM_FLOATS_MANUALLY {
    long double			number1, number2, number3;
    number1 = 1.0;
    number2 = 2.0;
    FloatPushNumber(&number1);				/* Push number1 onto the FP stack. */
    FloatPushNumber(&number2);				/* Push number2 onto the FP stack. */
    FloatAdd();				/* Add the top two numbers on the FP stack. The
                 * result will be placed on top of the FP stack.*/
    FloatPopNumber(&number3);				/* Pop the result into the number3 variable. */
    return(number3);
}

Several functions have no equivalent C operation. Consult Direct FP Operations for more details on using these functions.

Note that although direct comparisons between FP numbers are not allowed, you can use the FloatLt0(), FloatGt0(), and FloatEq0() functions to compare whether the the addition of two FP numbers is gretaer than, less than, or equal to zero.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Basic Math Functions | Next: 1.2 Transcendental Functions


Transcendental Functions

The Math Library: 1.2 Basic Math Functions: Transcendental Functions

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Algebraic Functions | Next: 1.3 Random Number Generation

The Math Library provides an array of transcendental routines that operate on GEOS FP numbers. A transcendental function is one which cannot be derived through algebraic means. Examples of transcendental functions include the trigonometric functions (sine, cosine, etc.) and the logarithmic functions (log, natural log, etc.).

The table above lists the function names and the operations they perform. Typical functions in C are listed alongside. (The basic language of C itself includes no such transcendental functions but almost all compilers include a C math library that does.) As can be seen in the table, several functions have no equivalent C operation. Consult Float Formats for more details on using these functions.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Algebraic Functions | Next: 1.3 Random Number Generation


Random Number Generation

The Math Library: 1.3 Basic Math Functions: Random Number Generation

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Transcendental Functions | Next: 2 Conversions to Other Types
 FloatRandomize(),  FloatRandom(),  FloatRandomN()

The Math Library also provides routines to create random numbers. Using any of these routines requires that you manually push and pop numbers on the FP stack.

FloatRandomize() primes the random number generator, in preparation for a call to FloatRandom() or FloatRandomN() . If FloatRandomize() is passed the flag RGIF_USE_SEED, the routine must also pass a developer supplied seed.

FloatRandom() returns a random value between 0 (inclusive) and 1 (exclusive). The number is placed on top of the FP stack. To assign that value to a variable, use FloatPopNumber() .

FloatRandomN() returns a random value between 0 (inclusive) and N (exclusive), where N is an integer. The integer value must be on top of the FP stack. The returned integer is pushed onto the FP stack.To assign that value to a variable, use FloatPopNumber() .

Code Display D-3 Creating a Random Number

/*
 * This method takes a passed seed (passedSeed) and returns a random integer
 * between 0 and 100.
 */
@method MyProcessClass, MSG_GET_RANDOM_FLOAT {
    long double			randomX;
    /* Prime the random number generator. */
    FloatRandomize(RGIF_USE_SEED, passedSeed);
    randomX = 100;
    FloatPushNumber(&randomX);
    FloatRandomN();
    FloatPopNumber(&randomX);
    return(randomX);
}
| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Transcendental Functions | Next: 2 Conversions to Other Types


Conversions to Other Types

The Math Library: 2 Conversions to Other Types

| Up: GEOS SDK TechDocs | Up | Prev: 1 Basic Math Functions | Next: 3 Float Formats

In many cases, FP numbers will need to be converted to different types for use in different parts of an application. For example, floating point numbers may be involved in a complex function that returns an integer. FP numbers may also need to appear to the user as ASCII text.

Converting Into Other Floats

There are several routines which convert GEOS FP numbers into other float formats, compatible with the C types float and double . Typically in C, this conversion is accomplished by casting the FP numbers into the other type. It is therefore done automatically for you.

If you are working in Assembly or you wish to directly pass floats or doubles to C stubs, consult Direct FP Operations .

Converting ASCII to FP Numbers

 FloatAsciiToFloat()

FloatAsciiToFloat() converts a number represented in an ASCII text format into a FP number. The routine recognizes two flags:

  • FAF_PUSH_RESULT
    Pushes the result onto the FP stack.
  • FAF_STORE_NUMBER
    Stores the result in a specified address.

The routine must also be passed a pointer to the string to convert, the number of characters in the string to convert (starting at the address) and the buffer to store the FP number if passing FAF_STORE_NUMBER.

Converting FP Numbers to ASCII

 FloatFloatToAscii(),  FloatFloatToAscii_StdFormat(),  FloatFloatIEEE64ToAscii_StdFormat()

FloatFloatToAscii() converts an FP number into ASCII text format. The routine must be passed a stack frame, which may be set up by declaring a local variable of type FFA_stackFrame and moving data into the appropriate fields. You should also pass the routine a pointer to a buffer to store the resultant string. This buffer must be declared with a length of either:

FLOAT_FLOAT_TO_ASCII_NORMAL_BUF_LEN
FLOAT_FLOAT_TO_ASCII_HUGE_BUF_LEN

The FFA_stackFrame is a union of two structures: FloatFloatAsciiData or FloatFloatToDateTimeData . You will want to use the FloatFloatToAsciiData structure in most cases; FloatFloatToAsciiDateTimeData is used to format a FP number (representing a date and time) into a date-time format passed in the structure. The routine checks a bit in the structure to see which structure is being passed.

Normal Conversions

The FloatFloatToAsciiData structure is used most often in formatting FP numbers into ASCII. The structure is rather large and cumbersome to set up. You may wish to use the routine FloatFloatToAscii_StdFormat() which sets up many of these entries automatically. ( FloatFloatToAsciiData Structure lists the entries of the FloatFloatToAsciiData structure.)

Code Display D-4 FloatFloatToAsciiData Structure

typedef struct {
/*
 * FFA_params stores the entries that the caller must set up.
 */
    FloatFloatToAsciiParams			FFA_params;
/*
 * These entries store information returned by FloatFloatToAscii() that may be
 * examined.
 */
    word			FFA_startNumber;
    word			FFA_decimalPoint;
    word			FFA_endNumber;
    word			FFA_numChars;
    word			FFA_startExponent;
/*
 * The rest of the entries are for internal use only.
 */
    word			FFA_bufSize;
    word			FFA_saveDI;
    word			FFA_numSign;
    byte			FFA_startSigCount;
    byte			FFA_sigCount;
    byte			FFA_noMoreSigInfo;
    byte			FFA_startDecCount;
    byte			FFA_decCount;
    word			FFA_decExponent;
    word			FFA_curExponent;
    byte			FFA_useCommas;
    byte			FFA_charsToComma;
    char			FFA_commaChar;
    char			FFA_decimalChar;
} FloatFloatToAsciiData;

FFA_ params is a structure that stores the following entries of its own:

formatFlags
Flags specifying the look and feel of the ASCII text format (see below for allowed flags).
decimalOffset
Integer which specifies the number of decimal places to shift the output. For example, to display a number in terms of millions, a decimalOffset of -6 shifts the decimal point six places to the left; to display in terms of tenths would require a decimalOffset of 1.
totalDigits
Integer which specifies the maximum number of decimal places (integer and decimal portions) that the FP number may exhibit. The ASCII string is truncated if the length of the string is greater than this number.
decimalLimit
Integer which specifies the maximum number of digits to the right of the decimal point. For example a decimalLimit of 2 would print out 123.456789 as 123.46.
preNegative
Characters used to precede a negative number, in the format of a null terminated text string.
postNegative
Characters used to follow a negative number, in the format of a null terminated text string.
prePositive
Characters used to precede a positive number, in the format of a null terminated text string.
postPositive
Characters used to follow a positive number, in the format of a null terminated text string.
header
Characters used to precede the number, in the format of a null terminated text string. For example, a typical header for a currency format might be "$".
trailer
Characters used to follow a FP number, in the format of a null terminated text string.

The formatFlags record is a record of type FloatFloatToAsciiFormatFlags and defines the format of the ASCII output. Set the appropriate flags to get the desired output.

  • FFAF_FROM_ADDR
    Set if routine should take the FP number from a specified address, rather than the FP stack. This pointer (*number) should be passed to FloatFloatToAscii() if this flag is set.
  • FFAF_DONT_USE_SCIENTIFIC
    Set if the result should not be expressed in scientific notation under any condition. If this is set, the number will always be formatted in fixed format.
  • FFAF_SCIENTIFIC
    Set if the result should be expressed in scientific notation even if the number can be expressed in fixed point format. For example, the number `2' will be expressed as "2 x 10 0 ."
  • FFAF_PERCENT
    Set if the result should be expressed as a percentage.
  • FFAF_USE_COMMAS
    Set if the result should use commas to separate thousands.
  • FFAF_NO_TRAIL_ZEROS
    Set if extraneous zeros to the right of the decimal point should be ignored. For example, "123.67000000" will be reduced to "123.67."
  • FFAF_NO_LEAD_ZEROS
    Set to ignore the lead zero for a number less than one. That is, "0.123" is reduced to ".123."
  • FFAF_HEADER_PRESENT
    Set if a header is present; this speeds up conversion.
  • FFAF_TRAILER_PRESENT
    Set if a trailer is present; this speeds up conversion.
  • FFAF_SIGN_CHAR_TO_FOLLOW_HEADER
    Set if sign character(s) should follow the header.
  • FFAF_SIGN_CHAR_TO_PRECEDE_TRAILER
    Set if sign character(s) should precede the trailer.

The rest of the entries in FloatFloatToAsciiData store information filled in by FloatFloatToAscii() . These entries are described below:

FFA_ startNumber stores the offset to the start of numeric characters in the ASCII buffer.
FFA_ decimalPoint stores the offset to the decimal point character or zero if no decimal point is present.
FFA_ endNumber stores the offset to the end of the numeric characters in the ASCII buffer.
FFA_ numChars stores the total number of characters in the ASCII buffer (excluding the null terminator). This entry is set to zero if an error is encountered.
FFA_ startExponent stores the offset to the "E" character in the ASCII buffer or zero if no exponent is present. Applications can check this to see if the number was expressed in scientific notation using the `E' format.

Date-Time Conversions

FFA_stackFrame may contain FloatFloatToAsciiDateTimeData if FloatFloatToAscii() is being used to convert a FP number into a date-time format. In that case FFA_stackFrame contains the structure FloatFloatToDateTimeData instead of FloatFloatToAsciiData . ( FFA_stackFrame is a union.)

FloatFloatToDateTimeData contains one entry, FFA_ dateTimeParams . This structure contains several flags which specify how the date-time should be formatted and a number of entries which break down the date-time into its respective parts (year, month, day etc.) If none of these entries are filled in, the date-time is taken from the top of the FP stack.

Date-times are represented by FP numbers in GEOS. The integer portion represents dates as integers counted from Jan 1, 1900, which is designated as 1. The highest date allowed is 73049 (December 31, 2099). The fractional portion represents a fraction of the day between midnight (0.000000) and 11:59:59 p.m. (0.999988). This fractional value is derived from the hour, minute and second of the day.

Code Display D-5 DateTime Parameters

typedef struct {
    FloatFloatToDateTimeFlags				FFA_dateTimeFlags;
    word				FFA_year;
    byte				FFA_month;
    byte				FFA_day;
    byte				FFA_weekday;
    byte				FFA_hours;
    byte				FFA_minutes;
    byte				FFA_seconds;
} FloatFloatToDateTimeParams;
/*
 *	FloatFloatToDateTimeFlags record
 */
typedef WordFlags FloatFloatToDateTimeFlags;
#define		FFDT_DATE_TIME_OP			0x8000
#define		FFDT_FROM_ADDR			0x4000
#define		FFDT_FORMAT			0x3fff

The flag FFDT_DATE_TIME_OP is set to notify the FloatFloatToAscii() routine that this operation is a date-time format, not a normal float to ASCII conversion. This flag must be set if you want to convert the FP number into a date-time format using FloatFloatToAscii() .

FFDT_FROM_ADDR is set if the date-time FP number should not be taken from the FP stack (or passed directly in the FloatFloatToDateTimeParams structure) but should instead be taken from the address passed in FloatFloatToAscii() .

FFDT_FORMAT stores the DateTimeFormat that the routine will use to format the number into a date-time string.

If the date-time is directly passed in, and not taken from an FP date-time number either at a passed address or the top of an FP stack, FloatFloatToAscii() looks at the other passed parameters.

FFA_ year specifies the year. The value must be between 1900 and 2099. This is not a one-based year, as it is when presented as a date-time number.

FFA_ month is the month of the year, a value between 1 and 12.

FFA_ day is the day of a month, a value between 1 and 31.

FFA_ hour specifies the hour of the day, a value between 0 and 23. Zero specifies midnight.

FFA_ minutes specifies the minute of the hour, a value between 0 and 59.

FFA_ seconds specifies the second of the minute, a value between 0 and 59.

Using Standard Formats

FloatFloatToAscii_StdFormat() uses a pre-set stack frame, eliminating the need to set up the variables of the FloatFloatToAsciiData structure manually. The only flags recognized are FFAF_FROM_ADDR, FFAF_SCIENTIFIC, FFAF_PERCENT, FFAF_USE_COMMAS, and FFAF_NO_TRAIL_ZEROS. The developer must pass the number of total digits and the number of decimal digits desired. If the flag FFAF_FROM_ADDR is used, a pointer to a FP number (not on the FP stack) must also be passed.

The standard format sets the following elements of the stack frame to zero: decimalOffset, header, trailer, postNegative, prePositive , and postPositive . The structure element preNegative is set to the minus sign ("-").

FloatFloatIEEE64ToAscii_StdFormat() performs the same operation as FloatFloatToAscii_StdFormat() except that the FP number is passed (in 64 bit format) and is not taken from the stack. The entire FP number (not just a pointer to it) must be passed. All criteria for FloatFloatToAscii_StdFormat() applies to this routine, except that the flag FFAF_FROM_ADDR is not used.

Date and Time Routines

 FloatGetDateNumber(),  FloatDateNumberGetYear(),  FloatDateNumberGetMonthAndDay(),  FloatDateNumberGetWeekday(),  FloatGetTimeNumber(),  FloatTimeNumberGetHour(),  FloatTimeNumberGetMinutes(),  FloatTimeNumberGetSeconds()

FloatGetDateNumber() , when passed the month, day, and year, converts the data into an FP "date number" representation. This format represents dates as integers counted from Jan 1, 1900, which is designated as 1. The highest date allowed is 73050 (December 31, 2099).

FloatDateNumberGetYear(), FloatDateNumberGetMonthAndDay() and FloatDateNumberGetWeekday() all return the appropriate data, either the year, month and day, or weekday, given an FP "date number" as defined above. All data are returned as integers, not as FP numbers, and the original FP "date number" is popped off of the stack. Years are returned as integers between 1900 and 2099. Month and Days are returned as integers between 1 and 12, and 1 and 31, respectively. Weekdays are returned as integers between 1 and 7, where 1 is Sunday, 2 is Monday, etc.

FloatGetTimeNumber() when passed hours, minutes, and seconds returns an FP decimal representation between midnight (0.000000) and 11:59:59 p.m. (0.999988).

FloatTimeNumberGetHour(), FloatTimeNumberGetMinutes() and FloatTimeNumberGetSeconds() return the appropriate data given an FP "time number" as defined above. The original FP "time number" is popped off of the stack.

Note that both "date numbers" and "time numbers" can be added to specify a specific point in time. For example, 73050.999988 would specify December 31, 2099, 11:59:59. Since these formats are in FP format, they can be operated on with all standard functions in the FP library.

Miscellaneous Date Time Routines

 FloatGetDaysInMonth(),  FloatGetNumDigitsInIntegerPart(),  FloatFormatNumber()

FloatGetDaysInMonth() returns the total number of days in a specific month, for a specific year. The routine must be passed the appropriate month and year.

FloatGetNumDigitsInIntegerPart() returns the number of digits in the integer portion of an FP number. Numbers between zero and one will return one as the number of digits.

FloatStringGetDateNumber(), FloatStringGetTimeNumber()
| Up: GEOS SDK TechDocs | Up | Prev: 1 Basic Math Functions | Next: 3 Float Formats


Float Formats

The Math Library: 3 Float Formats

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Conversions to Other Types | Next: 4 Direct FP Operations

FP numbers can be displayed in many ways. For example, as we have seen, an FP number may actually represent a date-time. When we display the FP number 366.0000, we may want to show it (in text) as "Jan 1, 1901." The Math Library has a number of system-defined formats for your use. You may also allow users to define their own formats with the Float Format controller.

The underlying structures and routines to use and create float format options are explained in the next section. In most cases, however, the simple inclusion of a Float Format controller (of FloatFormatClass ) provides all of the UI and functionality to create and apply formats to FP numbers within text objects.

System-defined Formats

User-defined Formats

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Conversions to Other Types | Next: 4 Direct FP Operations


System-defined Formats

The Math Library: 3.1 Float Formats: System-defined Formats

| Up: GEOS SDK TechDocs | Up | Prev: 3 Float Formats | Next: 3.2 User-defined Formats

A system-defined FP format is stored within a FormatParams structure. This structure defines whether the FP number is a number to be converted into numerical text or a date-time. These format parameters are stored within arrays managed by the format control code.

Code Display D-6 System-defined Float Formats

/*
 * System-defined float formats are stored in an array that is maintained and
 * accessed by the float controller code. Each element is made up of a
 * FormatParams structure.
 */
typedef struct {
    /*
     * The FloatFloatToAsciiParams_Union stores either a
     * FloatFloatToAsciiParams structure if the number is a `pure' FP number,
     * or a FloatFloatToDateTimeParams structure if the number is a date-time.
     * In this way, it is essentially the same as the FFA_stackFrame discussed
     * earlier.
     */
    FloatFloatToAsciiParams_Union				FP_params;
    /*
     * FP_formatName stores the name of this formatting option that will be
     * displayed in the float controller's dynamic list. This name is loaded
     * from the optr given in FP_nameHan and FP_nameOff.(The table where these
     * strings are kept is within a localizable resource and therefore will
     * have different text under different country setups.)
     */
    char				FP_formatName[FORMAT_NAME_LENGTH+1];
    word				FP_nameHan;		/* MemHandle */
    word				FP_nameOff;		/* ChunkHandle */
    /*
     * FP_listEntryNum stores the zero-based position of this FormatParams
     * entry within the table.
     */
    word				FP_listEntryNum;
    /*
     * FP_signature is an internal field used for error-checking.
     */
    word				FP_signature;
} FormatParams;

An application will never need to access this table of formats directly. GEOS contains several routines (in math.goh ) that can access this table and add, delete and modify table entries. Usually, it is easiest to include a Float Format controller in your application if you intend to allow the user to change float formats with these routines.

There are many system-defined float formatting options. These formats are identified by FormatIdType enumerations. Each type corresponds to a FormatParams structure.

Each FormatIdType enumeration is a direct offset into the float format lookup table. To distinguish between system-defined and user-defined formats, the high bit of a FormatIdType is set to indicate that the format is system-defined. Thus, 8000h refers to the first system-defined format, 8000h + (size( FormatParams )) refers to the second system-defined format, etc.

The format strings themselves are stored within a localizable resource, so that they may appear in a manner relevant to the particular country involved. For example, an FP number of 12.0 using the FormatIdType FORMAT_ID_CURRENCY might appear in the U.S. as $12.00, but will appear as £12.00 in Great Britain.

Code Display D-7 Float Format IDs

typedef	enum {
    FORMAT_ID_GENERAL	 	 	 		= 0x8000,
    FORMAT_ID_FIXED		 	 		= 0x8061,
    FORMAT_ID_FIXED_WITH_COMMAS	 	 			= 0x80c2,
    FORMAT_ID_FIXED_INTEGER	 				= 0x8123,
    FORMAT_ID_CURRENCY		 	 		= 0x8184,
    FORMAT_ID_CURRENCY_WITH_COMMAS 	 				= 0x81e5,
    FORMAT_ID_CURRENCY_INTEGER 	 				= 0x8246,
    FORMAT_ID_PERCENTAGE 	 	 			= 0x82a7,
    FORMAT_ID_PERCENTAGE_INTEGER 	 				= 0x8308,
    FORMAT_ID_THOUSANDS 	 	 			= 0x8369,
    FORMAT_ID_MILLIONS 	 	 			= 0x83ca,
    FORMAT_ID_SCIENTIFIC 	 	 			= 0x842b,
    FORMAT_ID_DATE_LONG 	 	 			= 0x848c,
    FORMAT_ID_DATE_LONG_CONDENSED 	 				= 0x84ed,
    FORMAT_ID_DATE_LONG_NO_WKDAY 					= 0x854e,
    FORMAT_ID_DATE_LONG_NO_WKDAY_CONDENSED 					= 0x85af,
    FORMAT_ID_DATE_SHORT 	 	 			= 0x8610,
    FORMAT_ID_DATE_SHORT_ZERO_PADDED 					= 0x8671,
    FORMAT_ID_DATE_LONG_MD 	 	 			= 0x86d2,
    FORMAT_ID_DATE_LONG_MD_NO_WKDAY 					= 0x8733,
    FORMAT_ID_DATE_SHORT_MD 					= 0x8794,
    FORMAT_ID_DATE_LONG_MY 	 	 			= 0x87f5,
    FORMAT_ID_DATE_SHORT_MY 	 				= 0x8856,
    FORMAT_ID_DATE_YEAR 	 	 			= 0x88b7,
    FORMAT_ID_DATE_MONTH 	 	 			= 0x8918,
    FORMAT_ID_DATE_DAY 	 				= 0x8979,
    FORMAT_ID_DATE_WEEKDAY 	 				= 0x89da,
    FORMAT_ID_TIME_HMS			 		= 0x8a3b,
    FORMAT_ID_TIME_HM					= 0x8a9c,
    FORMAT_ID_TIME_H					= 0x8afd,
    FORMAT_ID_TIME_MS			 		= 0x8b5e,
    FORMAT_ID_TIME_HMS_24HR		 			= 0x8bbf,
    FORMAT_ID_TIME_HM_24HR		 			= 0x8c20,
    FORMAT_ID_INDETERMINATE 	 				= 0xffff
} FormatIdType;
| Up: GEOS SDK TechDocs | Up | Prev: 3 Float Formats | Next: 3.2 User-defined Formats


User-defined Formats

The Math Library: 3.2 Float Formats: User-defined Formats

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 System-defined Formats | Next: 4 Direct FP Operations

Users may also design their own float formatting options. These user-defined formats are stored in FormatEntry structures. The array of these user-defined formats is kept separate from the system-defined formats. (user-defined formats are stored within their own VM block).

User-defined FormatIdType enums do not have the high bit (8000h) set in order to distinguish them from system-defined FormatIdType enums.

Code Display D-8 User-defined Formats

/*
 * User-defined formats are stored in a VM block that is created and maintained by
 * the float controller code. As each new format is added, space is made for a
 * FormatEntry structure. As formats are deleted, their entry's FE_used field is
 * set to zero to indicate that the entry is free for new formats to overwrite.
 */
typedef struct {
    /*
     * Each FormatEntry contains a corresponding FormatParams structure.
     */
    FormatParams 		FE_params;
    /*
     * FE_listEntryNumber is the zero-based position of the format counting
     * both previous user-defined formats and system-defined formats. For
     * example, if there are 10 system-defined formats, the first user-defined
     * format will have a FE_listEntryNumber of 10 (because positions are
     * zero-based).
     */
    word		FE_listEntryNumber;
    /*
     * FE_used, if non-zero, indicates that this entry within the user-defined
     * list is currently in use. If FE_used is zero, then this entry position
     * may be used to add a new user-defined entry without increasing the size
     * of the VM block.
     */
    byte		FE_used;
    /*
     * FE_sig is an error-checking field used internally.
     */
    word		FE_sig;
} FormatEntry;
| Up: GEOS SDK TechDocs | Up | Prev: 3.1 System-defined Formats | Next: 4 Direct FP Operations


Direct FP Operations

The Math Library: 4 Direct FP Operations

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Float Formats | Next: 4.1 Floating Point Numbers

The Math Library allows your application to use floating point (FP) numbers. C Developers will find little reason to make direct calls to functions in the Math Library as most of this functionality is taken care of for you in the C environment. (An exception is conversions from FP numbers to ASCII text characters and use of special date-time routines.) Assembly developers, however, will find direct use of the functions and structures in the Math Library essential.

The latter half of this chapter (from this point on) is provided for Assembly developers who need more complete information about the intricacies of the Math Library. Much of this information may also be useful for C developers who wish to make direct calls to the functions in this library for optimization purposes.

Floating Point Numbers

The Floating Point Stack

Floating Point Math Routines

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Float Formats | Next: 4.1 Floating Point Numbers


Floating Point Numbers

The Math Library: 4.1 Direct FP Operations: Floating Point Numbers

| Up: GEOS SDK TechDocs | Up | Prev: 4 Direct FP Operations | Next: 4.2 The Floating Point Stack

The Math Library defines floating point numbers by using binary point representation. In this format the bit in the zeros place is multiplied by 20, the bit in the minus-ones place is multiplied by 2-1, etc. The exponent is in base 2. For example, the Binary Point representation for 5 is:.

Floating Point Numbers in GEOS follow the IEEE 754 standard used by Intel. In this format a floating point number is represented in an 80 bit (5 word) format. (In C, this type is known as a long double .) This format specifies that the 80 bits contain:

  • a 1 bit sign (the most significant bit).
  • a 15 bit exponent (0000h to 7FFFh).
  • a 64 bit mantissa.

The 15 bit exponent is biased by 3FFFh, so that an exponent of 1 would be represented by 4000h, and an exponent of -1 would be represented by 3FFEh. This produces a hexadecimal range for the exponent of 4000h, or a decimal range of 4932.

Infinity is represented by 7FFFh in the exponent and 1 followed by 63 zeros in the mantissa. Therefore, all exponent bits are set to 1. (Negative infinity is the same except that the sign bit is also set to 1).

Zero is a special case in which all 80 bits-- sign, exponent, and mantissa--are set to zero.

The mantissa is normalized so that the most significant bit is always 1 (except when the FP number is zero). The binary point follows this 1 bit. This produces a precision of approximately 19 decimal places, which is adequate for most needs. This most significant bit is not "assumed away"; it is always present in the mantissa.

Note that in most uses, the use of binary point representation is transparent to the application.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Direct FP Operations | Next: 4.2 The Floating Point Stack


The Floating Point Stack

The Math Library: 4.2 Direct FP Operations: The Floating Point Stack

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Floating Point Numbers | Next: 4.3 Floating Point Math Routines

Floating Point (FP) numbers are placed and manipulated on an FP stack. Numbers can be rearranged, operated on, and removed from this stack through pushes, pops, and the use of special routines.

Initialization of the FP Stack

 FloatInit(),  FloatExit()

Before performing any floating point operations, a thread needs to call FloatInit() to create and initialize an FP stack. Each thread using floats must have its own unique FP stack. This call to FloatInit() is automatically performed by any application that includes the Math Library.

FloatInit() creates a swapable block of memory for the thread, initializes various stack pointers, and stores the handle for the block in the thread's data structure. FloatInit() must be passed the size of the stack to create (in bytes) and the type of stack ( FloatStackType ) to create.

The default FP stack holds 25 FP elements (250 bytes). An FP stack must be able to hold at least 5 FP elements.

The default floating point stack is FLOAT_STACK_GROW which instructs the system to increase the size of the stack whenever its bounds are reached. This is done automatically.

Other FloatStackType types are FLOAT_STACK_WRAP, which drops the FP numbers at the low end of the stack (effectively wrapping over that end) and FLOAT_STACK_ERROR which signals an error when the stack limit has been reached.

FloatExit() detaches the floating point stack for the current thread and frees its memory. FloatExit() only frees the FP stack associated with the current thread; other FP stacks in other threads remain unaffected. As is the case with FloatInit() , the call to FloatExit() is automatically performed by any application that includes the Math Library.

If FloatInit() is called twice before calling FloatExit() , the data on the original floating point stack will be lost.

Pushing and Popping on the FP Stack

 FloatPushNumber(),  FloatPopNumber(),  FloatDepth()

FloatPushNumber() pushes an FP number onto the top of the FP stack for the current thread from a passed buffer. The number must be already set up in 80 bit, FP format. The routine must be passed the pointer to the buffer storing the number.

Similarly, FloatPopNumber() pops an FP number from the top of the FP stack for the current thread into a passed buffer.

FloatDepth() returns the number of FP numbers currently in place on the stack.

Note: For clarity in diagrams within this chapter, stack locations will be numbered in order from the top position of the stack, S1 being first, S2 being second, etc. Variables will be numbered in the order they are pushed onto the stack, so that if X1, X2, and X3 are pushed onto the stack, the format illustrated below will result.

FP Stack Manipulation

 FloatRoll(),  FloatRollDown(),  FloatRot(),  FloatSwap()

Besides basic pushing and popping, the Float Library also includes many other routines which manage FP numbers on the stack. Unless otherwise specified, an operation that pushes, pops, or extracts an FP number on the stack affects all other FP numbers below the position of the operation by shifting their location in the stack either up or down, in standard stack fashion.

FloatRoll() pushes a selected FP number (S X ) onto the top of the stack (S1), removing it from location SX in the process. FloatRoll() passed with a value of 3 would move the FP number in S3 onto the top of the stack, pushing the stack in the process. All FP numbers below the extracted number remain unaffected by this routine.

FloatRollDown() performs the inverse operation of FloatRoll() . popping the top stack value (S1) into the specified location on the stack (S X ). FloatRollDown() passed with a value of 3 would move the FP number in S1 into location S3, shifting the stack in the process.

Both of these routines must be passed a stack location to move to or from.

FloatRot() rotates the top three numbers on the stack, placing S3 onto the top of the stack. This is equivalent to a FloatRoll() passed with a value of 3.

FloatSwap() exchanges S1 and S2.

Repetitious applications of these routines will return the stack to its former state.

Copying and Deleting Numbers on the FP Stack

 FloatPick(),  FloatOver(),  FloatDrop(),  FloatDup()

FloatPick() copies the contents of S X and pushes that value onto the FP stack. The entire stack is pushed in the process. FloatPick() passed with a value of 3 would copy the contents of S3 onto the FP stack.

FloatOver() copies S2 to the top of the stack, equivalent to a FloatPick() passed with a value of 2.

FloatDrop() drops (pops) the top number (S1) off the FP stack. This routine is different than FloatPopNumber() because the routine does not place the popped number into a memory address, and is therefore much faster.

FloatDup() duplicates the value at S1, pushing it onto the top of the stack. The stack is pushed in the process.

Comparing Numbers on the FP Stack

 FloatComp(),  FloatCompESDI(),  FloatCompAndDrop()

These routines essentially perform the same operation as the Assembly command cmp . FloatComp() performs a compare of the top two FP numbers (S1 and S2) and sets the appropriate flags in the flags register. The two FP numbers remain on the stack. FloatCompESDI() compares the contents of es:[di] with the value in S1 (and the FP number in S1 remains on the stack). FloatCompAndDrop() performs a compare of S1 and S2 and drops both from the FP stack.

Recovery of the FP Stack

 FloatGetStackPointer(),  FloatSetStackPointer()

FloatGetStackPointer() returns the current stack pointer value of the FP stack. FloatSetStackPointer() sets the stack pointer to a previous position. This routine must be passed a value that is greater than or equal to the current value of the stack pointer. (I.e. you must be throwing something, or nothing, away.)

These routines may be useful before the execution of involved routines that may push many numbers onto the stack. If an unrecoverable error is encountered, the programmer need not pop the intermediate values off the stack to return to the previous stack configuration. Only the stack pointer is saved; the state of the stack is not. If any numbers below the stack pointer are popped or altered, FloatSetStackPointer() will not recover the previous state of the stack.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Floating Point Numbers | Next: 4.3 Floating Point Math Routines


Floating Point Math Routines

The Math Library: 4.3 Direct FP Operations: Floating Point Math Routines

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 The Floating Point Stack

The Float Library provides a number of routines to modify data on the FP stack. These routines can be categorized in several major groups:

  • Constant Routines
  • Math Routines
  • Transcendental Routines
  • Conversion Routines
  • Date and Time Routines

Constant Routines

Float0(), FloatPoint5(), Float1(), FloatMinusPoint5(), FloatMinus1(),Float2(), Float5(), Float10(), Float3600(), Float16384(), Float86400()
FloatPi(), FloatPiDiv2(),FloatLg10(), FloatLn2(), FloatLn10(), FloatSqrt2()

The Constant Routines provide a means of quickly obtaining an often used number or an often used operation with a constant operand. Each of these functions pushes the constant FP value onto the top of the FP stack. (For example, FloatMinusPoint5() pushes -.5 onto the FP stack.)

FloatPi() , FloatPiDiv2() , FloatLg10() , FloatLn2() , FloatLn10() , and FloatSqrt2() each push the specified transcendental constant onto the FP stack: p,p/2, the log of 10, the natural log of 2, the natural log of 10,and the square root of 2, respectively. (See also Transcendental Routines.)

Constant Operands

FloatMultiply2(), FloatMultiply10(), FloatDivide2(), FloatDivide10(), Float10ToTheX()

FloatMultiply2(), FloatMultiply10(), FloatDivide2(), FloatDivide10() perform the specified operations on the contents of S1, either multiplying or dividing the contents of S1 by 2 or 10, and push the result onto the FP stack. The original value in S1 is popped off of the stack.

Float10ToTheX() pushes 10 to a passed exponent onto the FP stack.

Algebraic Routines

FloatAbs(), FloatAdd(), FloatSub(), FloatDivide(), FloatMultiply(), FloatDIV(), FloatMod(),FloatFactorial(), FloatNegate(), FloatInverse()

The Math Routines perform various operations on numbers already placed on the FP stack. All of the following routines pop the operated numbers off of the stack and push the result onto S1.

FloatAdd() adds the contents of S1 and S2, and pushes the result onto the top of the FP stack. The original values are popped off the stack.

FloatSub() subtracts the contents of S1 from the contents of S2, and pushes the result onto the top of the FP stack. The original values are popped off the stack.

Note that, in general, numbers will be pushed onto the stack in the order they would normally be operated on. For example, to obtain X1-X2, the programmer would first push X1, then X2, and call FloatSub , as in the following diagram:

since X1 would now be in location S2, while X2 is in location S1.

FloatMultiply() multiplies the contents of S1 and S2 and pushes the result onto the top of the FP stack. The original values are popped off the stack.

FloatDivide() divides the contents of S2 by the contents of S1 and pushes the result onto the top of the FP stack. The original values are popped off the stack.

FloatDIV() performs a FloatDivide() , truncating the fractional portion of the number and returning only the integer result. The original values are popped off the stack.

FloatMod() pushes S2 mod S1 (the remainder of FloatDivide() ) onto the top of the FP stack. The original values are popped off the stack.

FloatAbs() pushes the absolute value of S1 onto the top of the FP stack. The original value is popped off the stack.

FloatFactorial() pushes the factorial (x!) of the value in S1 onto the top of the FP stack. The original value is popped off the stack.

FloatNegate() negates the value in S1 and pushes the result onto the top of the FP stack. The original value is popped off the stack.

FloatInverse() pushes the inverse of the value in S1 (-S1) onto the top of the FP stack. The original value is popped off of the stack.

Comparison Routines

FloatMax(), FloatMin(), FloatLt0(), FloatEq0(), FloatGt0()

FloatMax() performs a compare of the FP numbers in S1 and S2 and, if necessary, swaps the greater number into S1.

FloatMin() performs a compare of the FP numbers in S1 and S2 and, if necessary, swaps the lesser number into S1.

FloatLt0(), FloatEq0(), FloatGt0() check whether the FP number in S1 is less than zero, equal to zero, or greater than zero. The carry bit is set to TRUE if the relationship is true. The original value is popped off of the stack.

Fractional and Integral Routines

FloatFrac(), FloatTrunc(), FloatInt(), FloatIntFrac(), FloatRound()

FloatFrac() pushes the fractional portion of S1 onto the FP stack. The original value is popped off of the stack.

FloatTrunc() pushes the integral portion of the contents of S1 onto the FP stack. This amounts to a rounding of the FP number toward zero, so that FloatTrunc() performed on -7.8 would return -7. The original value is popped off the stack.

FloatInt() rounds S1 down to its integral component, so that FloatInt() performed on -7.8 would return -8. Note that for negative numbers, this is different from FloatTrunc() . The original value is popped off the stack.

FloatIntFrac() splits a number into its fractional and integral parts, with the fractional part in S1 and the integral part in S2. The original value is popped off the stack.

FloatRound() rounds S1 to a given number of decimal places. FloatRound() passed with zero as an argument rounds S1 to the nearest integer, rounding up if greater than or equal to .5, rounding down if less than .5

Routines that Return Random Values

FloatRandom(), FloatRandomN(), FloatRandomize()

FloatRandom() pushes a random number between 0 (inclusive) and 1 (exclusive) onto the stack.

FloatRandomN() pushes a random integer between 0 (inclusive) and N (exclusive) onto the stack.

FloatRandomize() primes the random number generator. This routine expects a seed and some RandomGenInitFlags . If the flag RGIF_USE_SEED is passed, then a developer-supplied seed will be used. Otherwise, a seed based on the timer clock will be used.

FloatRandomize() should always be called before any of the FloatRandom() routines to ensure a high degree of randomness.

The random number generation method uses the linear congruential method, an algorithm which ensures a high degree of randomness in the computation method.

Transcendental Functions

Transcendental functions are functions that cannot be constructed using normal arithmetic routines. Care must be taken with the following routines to ensure that they are operating on a valid range of values. Otherwise, the routines will return an error.

Trigonometric Routines

FloatSin(), FloatCos(), FloatTan(), FloatArcSin(), FloatArcCos(), FloatArcTan(), FloatArcTan2(), FloatSinh(), FloatCosh(), FloatTanh(), FloatArcSinh(), FloatArcCosh(), FloatArcTanh()

FloatSin(), FloatCos() , and FloatTan() perform the given operation on the contents of S1, pushing the result onto the FP stack. S1 must be expressed in radians. The original value is popped off of the stack.

FloatArcSin(), FloatArcCos() , and FloatArcTan() perform the given inverse operation on S1, pushing the result onto the FP stack. The result is given in radians. The original value is popped off of the stack.

FloatArcTan2() calculates the arc tangent given cartesian coordinates x and y . The arctangent is calculated from the x-axis through the origin to the given point. The value returned is expressed in radians, between -p (exclusive) and +p (inclusive). The original values are popped off of the stack.

FloatSinh(), FloatCosh() , and FloatTanh() perform the given hyperbolic operations on S1, pushing the result onto the FP stack. The original value is popped off of the stack.

FloatArcSinh(), FloatArcCosh() , and FloatArcTanh() perform the given inverse hyperbolic operations on S1, pushing the result onto the FP stack. The original value is popped off of the stack.

Exponential Routines

FloatExp(), FloatExponential(), FloatLg(), FloatLog(), FloatLn(), FloatLn1plusX(), FloatSqr(), FloatSqrt()

FloatExp() performs the exponentiation of e to the power of S1, pushing the result onto the FP stack. The original value is popped off of the stack.

FloatExponential() performs the exponentiation of S2 to the power of S1, pushing the result onto the FP stack. The original values are popped off of the stack.

FloatLg() performs the logarithm to the base 2 on S1, pushing the result onto the FP stack. The original value is popped off of the stack.

FloatLog() performs the logarithm to the base 10 on S1. The original value is popped off the stack.

FloatLn() performs the natural logarithm (log base e ) on S1. The original value is popped off the stack.

FloatLn1plusX() performs the natural log of (1 + S1). The original value is popped off the stack.

FloatSqr() performs the square of S1, pushing the result onto the FP stack. The original value is popped off the stack.

FloatSqrt() performs the square root of S1, pushing the result onto the FP stack. The original value is popped off the stack.

Conversion Routines

At some point 80-bit FP numbers may need to be converted into different formats, or you may need to convert these differently formatted numbers into a floating point representation. The Math Library provides for this contingency.

Conversions Between Integers and FP Numbers

FloatDwordToFloat(), FloatWordToFloat(), FloatFloatToDword()

FloatDwordToFloat() converts a passed double-word signed integer into a floating point number, and pushes that number onto the FP stack.

FloatWordToFloat() converts a passed word signed integer into a floating point number, and pushes that number onto the FP stack.

FloatFloatToDword() converts the FP number in S1 into a double-word signed integer. The FP number is converted into an integer by rounding the FP number to zero decimal places.

You can convert a FP number into a word value by using FloatFloatToDword() and just using the low word.

Conversions Between 80 bit FP Numbers and Other Floats

FloatGeos80ToIEEE64(), FloatGeos80ToIEEE32(), FloatIEEE64ToGeos80(), FloatIEEE32ToGeos80()

FloatGeos80ToIEEE64() converts a GEOS FP number (80 bits) into a 64 bit floating point number (in C, a type of double ).

FloatGeos80ToIEEE32() converts a GEOS FP number (80 bits) into a 32 bit floating point number (in C, a type of float ).

FloatIEEE64ToGeos80() converts a 64 bit FP number into a GEOS 80 bit FP number (in C, a type of long double ).

FloatIEEE32ToGeos80() converts a 32 bit FP number into a GEOS 80 bit FP number (in C, a type of long double ).

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 The Floating Point Stack


Parse Library

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Parse Library Behavior
| One-page Version of this Chapter for Easy Printing

The Parse Library was originally created to provide a parser for a spreadsheet language. However, it will also fit the needs of a programmer who wants to implement a language based on mathematical expressions.

The Parse Library takes an expression as text, converts it to an expression using tokens, and evaluates the expression. When finished, it converts the result back into text and returns it. The Parse Library recognizes a special grammar and set of expressions that include an interface to the Cell Library's data structures. Therefore, you can use the Cell and Parse Libraries together to form the basic underlying engine of a spreadsheet application.

You may want to familiarize yourself with how compilers work before you read this section. In particular, you should understand how scanners use regular expressions to translate raw text into token streams; and you should be familiar with the parsing of context-free grammars. A good book to look at is Compilers: Principles, Techniques, and Tools by Aho, Sethi, and Ullman (a.k.a. "The Red Dragon Book").

1 Parse Library Behavior
1.1 The Scanner
1.2 The Parser
1.3 Evaluator
1.4 Formatter
2 Parser Functions
2.1 Internal Functions
2.2 External Functions
3 Coding with the Parse Library
3.1 Parsing a String
3.2 Evaluating a Token Sequence
3.3 Formatting a Token Sequence

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Parse Library Behavior
| One-page Version of this Chapter for Easy Printing


Parse Library Behavior

Parse Library: 1 Parse Library Behavior

| Up: GEOS SDK TechDocs | Up | Down | Prev: Parse Library | Next: 2 Parser Functions

The Parse Library takes a string of characters and evaluates it. In many ways, it acts like a compiler; it translates a string into tokens, evaluates the tokens, and returns the result. It can also reverse the process, translating a sequence of tokens into the equivalent text string. Finally, it can simplify a string of tokens, performing arithmetic simplifications and calling functions. The parse library provides many useful functions; furthermore, applications can define their own functions.

The different functions are separated into different parts of the parse library. The parse library contains the following basic sections:

  • Scanner
    The scanner reads a text string and converts it into a series of tokens. It does not keep track of the context of the tokens. Its behavior is partially determined by the localization settings; for example, it uses the localization setting to tell whether the decimal separator is a period, a comma, or some other character or string. It is called by the parser; it is not used independently.
  • Parser
    The parser interprets the stream of tokens returned by the scanner. It initializes the scanner and uses it to read tokens from the input strings; it also makes sure that the string of tokens is legally formatted. It does not do any type-checking.
  • Evaluator
    The evaluator simplifies a token string. It does this by replacing arithmetic expressions with their results, by making function calls, by reading current values of cells, and by replacing identifiers with their values. The result is another token string; usually this string consists of a single token (a number or string).
  • Formatter
    The formatter translates a token string into a text string. It is used to display the evaluator's output. Its behavior is influenced by the localization settings.

For example, suppose an application used the parse library to evaluate the string "(5*6)+SUM(A2:C6)". The following steps would be taken:

  1. The parser would parse the string. It would do this by calling the scanner to read tokens from the string. It would then parse the token sequence to see that it evaluated to a well-formed expression. (It would not do any simplifying or type-checking.)
  2. The evaluator would simplify the expression. It would reduce the token sequence for "(5*6)" to the single token for "30". It would then call the SUM function, passing it the specifier for the range of cells "A2:C6". The SUM function would check the type of its arguments, then perform the appropriate action (in this case, adding the values of the cells together). The SUM function would return a value (e.g., it might return 999.9). The evaluator would thus be able to simplify the entire token sequence to the single token for the number 1029.9.
  3. When the application needed to display the result, it would call the formatter. The formatter would check the localization settings, finding out what the thousands separator and decimal point character are. It would create the string "1,029.9".

Token strings are usually more compact than the corresponding text strings. There are several reasons for this; for example, cell references are much more compact, functions are specified by an ID number instead of a string, and white space is removed. When translated into a token string, it is only three bytes long: one token byte to specify that this is a number, and two data bytes to store the value of the number. For this reason, applications which use the parse library will generally not store the text entered by the user; instead, they can store the equivalent token string, and use the formatter to display the string when necessary.

The parse library routines often need to request information from the calling application or instruct it to perform a task. For example, when the Parser encounters a name, it needs to get a name ID from the calling application. For this reason, every Parse Library routine is passed a callback routine. The library routine calls this callback routine when necessary, passing a code indicating what action the callback routine should take. The beginning section will just describe this in general terms; for example, "the Evaluator uses the callback to find out the value of a cell." The advanced section provides a more detailed explanation.

The Scanner

The Parser

Evaluator

Formatter

| Up: GEOS SDK TechDocs | Up | Down | Prev: Parse Library | Next: 2 Parser Functions


The Scanner

Parse Library: 1.1 Parse Library Behavior: The Scanner

| Up: GEOS SDK TechDocs | Up | Prev: 1 Parse Library Behavior | Next: 1.2 The Parser

The scanner translates a text string into a sequence of tokens. The tokens can then be processed by the parser. Every token is associated with some data.

The scanner can be treated as a part of the parser. It is never used independently; instead, the parser is called on to parse a string, and the parser calls the scanner to translate the string into tokens.

The scanner does not keep track of tokens after it processes them. For this reason, it will not notice if, for example, parentheses are not balanced. It returns errors only if it is passed a string which does not scan as a sequence of tokens.

Scanner Tokens

The scanner recognizes the tokens listed below. Note that applications will never directly encounter the scanner tokens; the tokens translates them into parser tokens before returning them. A complete list of parser tokens (with their names) is given in Parser Tokens .

NUMBER
This is some kind of numerical constant. The format in the string is determined by the localization settings. The data section of the token is a floating-point number (even if the string contained an integer).
STRING
This is a sequence of characters surrounded by "double-quotes." All characters within double quotes are translated into their ASCII equivalents, with the exceptions noted below in Strings . The data section is a pointer to the ASCII string specified.
CELL
This is a reference to a cell in a database. The format is described in Cell References .
END_OF_EXPRESSION
The scanner returns this token when it has examined and translated an entire text string and reached its end.
OPEN_PAREN
This is simply a left parenthesis character, i.e. "(". There is no data section associated with this token.
CLOSE_PAREN
This is simply a right parenthesis character, i.e. ")". There is no data section associated with this token.
OPERATOR
This is a unary or binary operator. The operators are described in Operators . The data section specifies which operator was encountered.
LIST_SEPARATOR
This is a comma, i.e. ",". It is used to separate arguments to functions. There is no data section associated with this token.
IDENTIFIER
This is a sequence of characters, not in quotation marks, which does not match the format for cell references. Identifiers may be functions (built-in or application-defined) or variables; see Identifiers . The data section is a string containing the identifier.

Strings

The string passed to the scanner may, itself, contain strings. These inner strings are not further analyzed; rather, their contents are associated with the string token. Strings are delimited by double-quotes. All characters within the double-quotes are copied directly into the token's data, with the exception of the backslash, i.e. "\". This character signals that the character (or characters) which immediately follow it are to be interpreted literally. Backslash-codes include the following:

\"
This code represents a double-quote character (i.e. ASCII 0x22, or "); it indicates that the double-quote should be copied into the string, instead of read as a string delimiter.
\n
This code represents a newline control code (i.e. ASCII 0x0A, or control-J).
\t
This code represents a hard-tab control code (i.e. ASCII 0x09, or control-I).
\f
This code represents a form-feed control code (i.e. ASCII 0x0C, or control-L).
\b
This code represents a backspace control code (i.e. ASCII 0x10, or control-H).
\\
This code represents a backslash character (i.e. ASCII 0x5C, or "\").
\ nnn
This code is a literal octal value. The backslash must be followed by three digits, making up an octal integer in the range 0-177o (i.e. 0-255). The byte specified is inserted directly into the string. Thus, for example, "\134" is functionally identical to "\\".

Cell References

The parse library is often used in conjunction with cell files; for example, the spreadsheet objects use the two libraries together. For this reason, the scanner recognizes cell references. Cell references are described by the regular expression [A-Z]+[0-9]+; that is, one or more capital letters, followed by one or more digits. The capital letters indicate the cell's column. The first column (the column with index 0) is indicated by the letter A; column 1 is B, column 2 is C, and so on, up to column 25 (which is Z). Column 26 is AA, followed by AB, AC, and so on to AZ (column 51); this column is followed by BA, and so on, to the largest column, IV (column 255). The rows are indicated by number, with the first row having number 1.

The data portion of a cell reference token is a CellReference structure. This structure records the row and column indices of the cell; the scanner translates the cell reference to these indices. For more information about the cell library, see the Cell Library chapter.

When the evaluator needs to get the value of a cell, it calls a callback routine, passing the cell's CellReference structure. The application is responsible for looking up the cell's value and returning it to the evaluator. If you manage a cell file with a Spreadsheet object, this work is done for you; the Spreadsheet will be called by the evaluator, returning the values of cells as needed. (The spreadsheet returns zero for empty or unallocated cells.)

Note that while the cell library numbers both rows and columns starting from zero, the Parse library numbers rows starting from one. This is because historically, spreadsheets have had the first row be row number 1. Therefore, if the parser encounters a reference to cell A1, it will translate this into a cell reference which specifies row zero, column zero.

Operators

The scanner recognizes a number of built-in operators. Neither the scanner nor the parser does any simplification or evaluation of operator expressions; this is done by the evaluator. All operators are represented by the token SCANNER_TOKEN_OPERATOR. The token has a one-byte data section, which is a member of the enumerated type OperatorType ; this value specifies which operator was encountered. This section begins with a listing of currently supported operators in order of precedence, from highest precedence to lowest; this is followed by a detailed description of the operators. All operators listed here will always be supported; other operators may be added in the future.

Note that neither the scanner nor the parser does any evaluation of arguments. All type-checking is done at evaluation time. Thus, if parse the text "(3 * "HELLO")", the parser will not complain; the evaluator, however, will return a "bad argument type" error.

The figure below lists the operators in order of precedence. Highest-precedence operators are listed first. Operators with the same precedence are listed together; a blank line implies a drop in precedence. Operators of the same precedence level are grouped from left to right; that is, "1 - 2 - 3" is the same as "(1 - 2) - 3".

:
This is a range separator. The range separator is a binary infix operator. The parser recognizes expressions of the format Cell1 : Cell2 as describing a rectangular range of cells, with the two specified cells being diagonally opposite corners. The data portion of this token is the constant OP_RANGE_SEPARATOR.
...
This is another range separator. It is functionally identical to the colon operator. The data portion of this token is the constant OP_RANGE_SEPARATOR. (The formatter will turn this back into a colon.)
-
This can be either of two different operators. It can be a negation operator. This is a unary prefix operator which reverses the arithmetic sign of the operand. It can also be a subtraction operator. This is a binary infix operator. The parser determines which operator is represented. For example, in "(-1)", the hyphen is a negation operator; in "(1-2)", it is a subtraction operator. The data portion of this token is either OP_NEGATION or OP_SUBTRACTION; the scanner assigns the neutral OP_SUBTRACTION_NEGATION, and the parser decides (from context) which value is appropriate.
%
This can be either of two operators. It can be a percent operator. This is a unary postfix operator which divides its operand by 100; that is, "50%" evaluates to 0.5. It can also be a modulo arithmetic operator. This is a binary infix operator which returns the remainder when its first operand is divided by its second operand; that is, "11%4" evaluates to 3.0. The parser determines which operator is represented. The data portion of this token is either OP_PERCENT or OP_MODULO; the scanner assigns the neutral OP_PERCENT_MODULO, and the parser decides (from context) which constant is appropriate.
^
This is the exponentiation operator. It is a binary infix operator; it raises its first operand to the power of the second operand (e.g. "2^3" evaluates to 8.0). The data portion of this token is the constant OP_RANGE_EXPONENTIATION.
*
This is the multiplication operator. It is a binary infix operator. It multiplies the two operands. The data portion of this token is the constant OP_MULTIPLICATION.
/
This is the division operator. It is a binary infix operator. It divides the first operand by the second. The data portion of this token is the constant OP_ DIVISION. The constant OP_DIVISION_GRAPHIC is functionally equivalent; however, the formatter will display the operator as "".
+
This is the addition operator. It is a binary infix operator. It adds the two operands. The data portion of this token is the constant OP_RANGE_ADDITION.

Several Boolean operators are also provided. In every case, if a Boolean expression is true, it evaluates to 1.0; if it is false, it evaluates to 0.0. (There is no Boolean negation operator; however, there is a Boolean negation function, NOT, which returns 1.0 if its argument is zero, and otherwise returns zero.) Boolean operators may be used for numbers or strings. They work in the conventional way for numbers. Strings are "equal" if they are identical. One string is said to be "less than" another if it comes first in lexical order. The parse library uses localized string comparison routines to compare strings; thus, the local lexical ordering is automatically used. (For more information, see the Localization chapter.)

=
This is the equality operator. It is a binary infix operator. An expression evaluates to 1.0 if both operands evaluate to identical values. The data portion of this token is the constant OP_EQUAL.
<>
This is the inequality operator. It is a binary infix operator. An expression evaluates to 1.0 if the two operands evaluate to different values. The data portion of this token is OP_NOT_EQUAL. The constant OP_NOT_EQUAL_GRAPHIC is functionally equivalent; however, the formatter will display the operator as "".
>
This is the "greater-than" operator. It is a binary infix operator. It returns 1.0 if the first operand evaluates to a larger number than the second operand. The data portion of this token is OP_GREATER_THAN.
<
This is the "less-than" operator. It is a binary infix operator. It returns 1.0 if the first operand evaluates to a smaller number than the second operand. The data portion of this token is OP_LESS_THAN.
>=
This is the "greater-than-or-equal-to" operator. It is a binary infix operator. It returns 1.0 if the first operator evaluates to a number that is greater than or equal to the value of the second operand. The data portion of this token is OP_GREATER_THAN_OR_EQUAL. The constant OP_GREATER_THAN_OR_EQUAL_GRAPHIC is functionally equivalent; however, the formatter will display the operator as "".
<=
This is the "less-than-or-equal-to" operator. It is a binary infix operator. It returns 1.0 if the first operator evaluates to a number that is less than or equal to the value of the second operand. The data portion of this token is OP_LESS_THAN_OR_EQUAL. The constant OP_LESS_THAN_OR_EQUAL_GRAPHIC is functionally equivalent; however, the formatter will display the operator as "£".

Some special-purpose operators are also provided:

&
This is the "string-concatenation" operator. It is a binary infix operator. The arguments must be strings. The result is a single string, composed of all the characters of the first string (without its null terminator) followed by all the characters of the second string (with its null terminator); for example, ("Franklin" & "Poomm") evaluates to "FranklinPoomm". The data portion of this token is OP_STRING_CONCAT.
#
This is the "range-intersection" operator. It is a binary infix operator. Both arguments must be cell ranges. The result is the range of cells which falls in both of the operand cell ranges. Note that cell ranges must be rectangular; there is, therefore, no "range-union" operator. The data portion of this token is OP_RANGE_INTERSECTION.

Identifiers

Any unbroken alphanumeric character sequence which does not appear in quotes, and which is not in the format for a cell reference, is presumed to be an identifier. Identifiers serve two roles: they may be function names, or they may be labels.

The scanner merely notes that an identifier has been found; it does not take any other action. The parser will find out what the identifier signifies. If the identifier's position indicates that it is a function (but the name is not that of a built-in function), the parser will prompt its caller for a pointer to a callback routine which will perform this function. If its position indicates that it is an identifier, the parser will request the value associated with the identifier; this may be a string, a number, or a cell reference.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Parse Library Behavior | Next: 1.2 The Parser


The Parser

Parse Library: 1.2 Parse Library Behavior: The Parser

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 The Scanner | Next: 1.3 Evaluator

Applications will never call the scanner directly. Instead, if they access the parse library directly (instead of through the spreadsheet objects), they will call the parser and pass it a string, and the parser will in turn call the scanner to process the string into tokens. This section will not discuss how to call the parser, since few applications will need to do that; it will instead describe the general workings of the parser.

The parser translates a well-formed string into a sequence of tokens. It calls the scanner to read tokens from the string. It then uses a context-free grammar to make sure the string is well formed. The context-free grammar is described below. The scanner outputs a sequence of parser tokens. The parser tokens are almost identical to the scanner tokens, with a few exceptions; those exceptions are noted below.

The parser is passed a callback routine. The parser calls this routine when it needs information about a token; for example, if it encounters a function it does not recognize, it calls the callback to get a pointer to the function. The details of this are provided in the advanced section.

If the parser is not passed a well-formed expression, or if it is unable to successfully parse the string for some other reason, it returns an error code. The error codes are described at length in the advanced section.

The Parser's Grammar

The parser uses a context-free grammar to make sure the string is well-formed. The grammar is listed below. The basic units of the grammar are listed in ALL-CAPS; higher-level units are listed in italics . The string must parse to a well-formed expression .

expression :
'(' expression ') '
 NEG_OP expression
 IDENTIFIER '(' function_args ')'
base_item more_expression
more_expression :
<empty>
PERCENT_OP more_expression
 BINARY_OP expression
function_args :
<empty>
arg_list
arg_list :
expression
expression ',' arg_list
base_item : NUMBER
STRING
CELL_REF
IDENTIFIER

Parser Tokens

The parser does not return scanner tokens; instead, it returns a sequence of parser tokens. The parser tokens are almost directly analogous to the scanner tokens. However, a few additional token types are added.

The parser tokens have the same structure as the scanner tokens. The first field is a constant specifying what type of token this is. The second field contains specific information about the token; this field may be blank. The parser has the following types of tokens:

PARSER_TOKEN_NUMBER
This is the same as the scanner NUMBER token.
PARSER_TOKEN_STRING
This is the same as the scanner STRING token.
PARSER_TOKEN_CELL
This is the same as the scanner CELL token.
PARSER_TOKEN_END_OF_EXPRESSION
This is the same as the scanner END_OF_EXPRESSION token.
PARSER_TOKEN_OPEN_PAREN
This usually replaces the scanner OPEN_PAREN token. However, it is not used if the parenthesis is delimiting function arguments; it is only used if the parenthesis is changing the order of evaluation.
PARSER_TOKEN_CLOSE_PAREN
This usually replaces the scanner CLOSE_PAREN token. However, it is not used if the parenthesis is delimiting function arguments; it is only used if the parenthesis is changing the order of evaluation.
PARSER_TOKEN_NAME
This replaces some occurrences of the scanner IDENTIFIER token; specifically, those where the identifier is not a function name. The data portion is the number for that name.
PARSER_TOKEN_FUNCTION
This replaces some occurrences of the scanner IDENTIFIER token, specifically those in which the identifier is a function name. The data portion is the function ID number.
PARSER_TOKEN_CLOSE_FUNCTION
This replaces some occurrences of the scanner CLOSE_PAREN token; specifically, those where the closing parenthesis delimits function arguments.
PARSER_TOKEN_ARG_END
The parser inserts this token after every argument to a function call; thus, it replaces occurrences of SCANNER_TOKEN_LIST_SEPARATOR, and also occurs after the last argument to a function.
PARSER_TOKEN_OPERATOR
This is the same as the parser's OPERATOR token. The data section is an operator constant, as described above in Operators . Note the parser replaces occurrences of OP_PERCENT_MODULO with either OP_PERCENT or OP_MODULO, as appropriate; similarly, it replaces OP_SUBTRACTION_NEGATION with either OP_SUBTRACTION or OP_NEGATION.

When the parser encounters an identifier that is in the appropriate place for a function name (that is, an identifier followed by a parenthesized argument list), it does not write an identifier token. Instead, it writes a "function" token, which has a one-word data section. This section is the function ID (described in Parser Functions ). If the function's name is not one of a built-in function, it will call the application's callback routine to find out what the function's ID number is; the evaluator will pass this ID when it needs to have the function called.

When the parser encounters an identifier, it asks its caller for an ID number for the identifier. It can then store the ID number instead of the entire string. The evaluator will use this ID number when requesting the value of the identifier. The formatter will use the ID number when requesting the original identifier string associated with the ID number.

When the parser encounters a scanner parenthesis token, it does not necessarily translate it into a parser parenthesis token. This is because parentheses fulfill two separate roles: they specify the order of evaluation, and they delimit function arguments. When the parser encounters parenthesis tokens which specify order of evaluation, it translates them into parser parenthesis tokens. If, however, it encounters argument-delimiting parentheses, it does not need to translate them literally; after all, the presence of a function token implies that it will be followed by an argument list. Thus, the parser does not need to copy the parenthesis tokens. Instead, it copies the tokens of the argument list. When it reaches a list separator, it replaces that with an "end-of-argument" token; when it reaches the closing parenthesis for the function call, it replaces that with a "close-function" token.

An Example of Scanning and Parsing

Suppose that you call the parser on the text string "3 + SUM(6.5, 3 ^ (4 - 1), C5...F9)". The parser will evaluate the string, one token at a time. When it needs to process a token, it will call the scanner to return the next token in the string. It will then replace these tokens with parser tokens, and write out the sequence of tokens to its output buffer.

For simplicity, this example treats the scanner as if it scanned the entire text stream at once, and returned the entire sequence of tokens to the scanner. In this case, the scanner would translate the text into the following sequence of tokens:

Token		Data			Comment
NUMBER		3.0			All numbers are floats
OPERATOR		OP_ADDITION
IDENTIFIER		"SUM"
OPEN_PAREN					delimits function args
NUMBER		6.5
LIST_SEPARATOR
NUMBER		3.0
OPERATOR		OP_EXPONENTIATION
OPEN_PAREN
NUMBER		4.0
OPERATOR		OP_SUBTRACTION_NEGATION
                    Parser figures out
                    which operator this is
NUMBER		1.0
CLOSE_PAREN
LIST_SEPARATOR
CELL		C5			Actually stored as
                     "4,2"; row index 4,
                    column index 2
OPERATOR		OP_RANGE_SEPARATOR
CELL		F9
CLOSE_PAREN
END_OF_EXPRESSION

The parser reads these tokens, one at a time, and writes out an analogous sequence of parser tokens:

Token		Data			Comment
NUMBER		3.0			All numbers are floats
OPERATOR		OP_ADDITION
FUNCTION		FUNCTION_ID_SUM
NUMBER		6.5
END_OF_ARG
NUMBER		3.0
OPERATOR		OP_EXPONENTIATION
OPEN_PAREN
NUMBER		4.0
OPERATOR		OP_SUBTRACTION
NUMBER		1.0
CLOSE_PAREN
END_OF_ARG
CELL		C5			Actually stored as
                     "4,2"; row index 4,
                    column index 2
OPERATOR		OP_RANGE_SEPARATOR
CELL		F9
END_OF_ARG
CLOSE_FUNCTION
END_OF_EXPRESSION

The application does not need to save the original text string. Instead, it can save the buffer containing the parser tokens, and use the formatter to translate the token sequence back into a character string.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 The Scanner | Next: 1.3 Evaluator


Evaluator

Parse Library: 1.3 Parse Library Behavior: Evaluator

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 The Parser | Next: 1.4 Formatter

The evaluator simplifies a token string returned by the parser. If the input token sequence was well-formed (as are all token sequences generated by the Parser), the evaluator will produce a token sequence consisting of two tokens: a single "result" token (which may be an error token), followed by the "end-of-expression" token. It does this by doing two main things: simplifying arithmetic expressions, and making function calls.

The evaluator maintains two stacks, an Operator stack and an Argument stack. It reads the tokens from beginning to end. Each time it reads a token, it takes an action; this may involve pushing something onto a stack, or processing some of the tokens on the tops of the stacks.

If an error occurs, the parser may take two different actions. Some errors are pushed on the argument stack; these may be handled by functions. For example, if the result of an expression is too large to be represented, the evaluator will just push PSEE_FLOAT_POS_INFINITY on the argument stack. Any function or operator which is passed an error code as an argument can either handle the error, propagate the error, or return a different error. For example, if the division operator is passed PSEE_FLOAT_POS_INFINITY as the divisor, it will simply return zero.

The actual evaluation of tokens is straightforward. The evaluator pops the top token from the Operator stack. This is either a function or an operator. If the token is an operator, the evaluator pops either one or two arguments from the top of the argument stack, takes the appropriate action, and pushes the result on the argument stack. If the token is a function, the evaluator calls the function directly, passing it a pointer to the argument stack and the number of arguments to the function call. The function is responsible for popping off all of the arguments and pushing the return value on the argument stack.

Special actions have to be taken if an operand or argument is a cell reference. If the cell is an argument to a function, or an operand (and the operator is not a range-separator or range-intersection), the evaluator will call its callback routine to get the value contained by the cell; this value will be put on the argument stack in place of the cell reference. If the operand is a range-separator or range-intersection, the cells or ranges will be combined into a single range, which is pushed on the Argument stack.

The evaluator reads tokens, one at a time, from the buffer provided by the parser. For each token it takes an appropriate action:

OPEN_PAREN
Push an OPEN_PAREN token on the Operator stack.
CLOSE_PAREN
Evaluate tokens from the Operator stack until an OPEN_PAREN reaches the top of the operator stack; then pop the OPEN_PAREN off the stack.
OPERATOR
If the top token on the Operator stack is an OPERATOR of higher precedence than this OPERATOR, then evaluate top of Operator stack. Repeat until top of operator stack is either not an operator, or is an operator of lower precedence. Finally, push the operator token on the operator stack.
FUNCTION
Push FUNCTION token on the Operator stack. The evaluator FUNCTION token contains the function ID and the number of arguments to the function (starting at zero).
CLOSE_FUNCTION
Call function on top of Operator stack, passing it the pointer to the Argument stack and the number of arguments to the function call. (The arguments will be on the top of the argument stack.) The function should pop the arguments off the Argument stack, then push the return value (or error code) on the Argument stack.
ARG_END
Evaluate the Operator stack until a FUNCTION token is at the top of the Operator stack; then increment the argument count of that function.
NUMBER
Push number on Argument stack. (Actually, what is pushed is a reference to the thread's floating-point stack, which contains the number itself.)
STRING
Push string on Argument stack.
CELL_REF
Push the cell reference on the Argument stack.
NAME
Call the callback function to find the value associated with the name; act on the value appropriately.
END_OF_EXPRESSION
Evaluate the Operator stack until it is empty; the result will be on the top of the Argument stack.
| Up: GEOS SDK TechDocs | Up | Prev: 1.2 The Parser | Next: 1.4 Formatter


Formatter

Parse Library: 1.4 Parse Library Behavior: Formatter

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Evaluator | Next: 2 Parser Functions

In order to display a token sequence, you must call the Formatter. The formatter is very straightforward. It is passed a buffer containing a token sequence; it returns a character array containing the result. The formatter makes use of the localization routines to format the result according to the local language and the user's Preferences settings.

If the token sequence consists of an error token, the formatter will generate an appropriate error string.

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Evaluator | Next: 2 Parser Functions


Parser Functions

Parse Library: 2 Parser Functions

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Parse Library Behavior | Next: 3 Coding with the Parse Library

The Parse library provides many built-in functions. Furthermore, each application can define its own functions. Every function is associated with a function ID number. Built-in functions have ID numbers assigned to them in the library code; application-defined functions are given ID numbers by the application. ID numbers are word-sized unsigned integers. All built-in ("internal") functions have ID numbers which are less than the constant FUNCTION_ID_FIRST_EXTERNAL_FUNCTION_BASE; all application-defined ("external") functions have ID numbers which are greater than this constant.

When the Parser reads an identifier token whose position indicates that it is a function, it converts the identifier to a function token (containing a function ID). The parser first checks to see if the identifier is the name of a built-in function. If so, it looks up the function's ID number and stores it in the function token.

If the identifier is not the name of a built-in function, the Parser calls the application's callback routine to get the function's ID number. The application must assign each function a word-sized ID which is greater than or equal to the constant FUNCTION_ID_FIRST_EXTERNAL_FUNCTION_BASE. This constant is defined as 0x8000.

When the Evaluator needs to evaluate a function, it checks to see if the function is external or internal. If the function is internal, it looks up the functions address and calls it. If the function is external, it calls the application's callback routine and passes the function ID. In either case, it passes a pointer to the argument stack and the number of arguments. The function is responsible for popping all the arguments off the stack and pushing the result. It can also push an error message on the stack. All of this is discussed at length in the advanced section.

Internal Functions

External Functions

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Parse Library Behavior | Next: 3 Coding with the Parse Library


Internal Functions

Parse Library: 2.1 Parser Functions: Internal Functions

| Up: GEOS SDK TechDocs | Up | Prev: 2 Parser Functions | Next: 2.2 External Functions

The Parse library provides many internal functions. Any application which uses the parse library automatically makes use of these functions. Some of these functions take a single argument; others take a set number of arguments or a variable number.

A listing of currently available functions follows, along with a short description of each one, and a parenthetical description of the function's arguments.

 ABS		Absolute value (One numeric argument)
 ACOS		Arc-cosine (One numeric argument)
 ACOSH		Hyperbolic arc-cosine (One numeric
        argument)
 AND		Boolean AND (Either a list of numbers
        or a cell range--there must be at least
        two numbers)
 ASIN		Arc-sine (One numeric argument)
 ASINH		Hyperbolic arc-sine (One numeric
        argument)
 ATAN		Arc-tangent (One numeric argument)
 ATAN2		Four-quadrant arc-tangent (XXX)
 ATANH		Hyperbolic arc-tangent (One numeric
        argument)
 AVG		Average of arguments (Either a list of
        numbers 	or a cell range--there must be
        at least 	two numbers)
 CHAR		Translates character-set code into
        character (One Chars argument)
 CHOOSE		Finds value in list at specified offset
        (XXX)
 CLEAN		Removes control characters from a string
        (One string argument)
 CODE		Translates character into character-set
        code (One string argument (first
        character will be converted))
 COLS		Returns # of columns in range (XXX)
 COS		Cosine (One numeric argument)
 COSH		Hyperbolic cosine (One numeric argument)
COUNT		Returns number of items in list (Any
        number of arguments)
 CTERM		Returns time for an investment to reach
        a specified value (XXX)
 DDB		Depreciation over a period (XXX)
DEGREES		Converts radians to degrees (XXX)
 ERR		Returns error PSEE_GEN_ERR (XXX)
 EXACT		Tests if two strings match (Two string
        arguments)
 EXP		Exponentiation (One numeric argument)
 FACT		Factorial (XXX)
 FALSE		Returns false (0.0) (XXX)
 FIND		Returns position in string where
        substring first occurs (Three
        arguments: string to find, string to
        search, starting offset (0 to start
        with the first character))
 FV		Future value of investment (XXX)
 HLOOKUP		Finds a value in a horizontal lookup
        table (XXX)
 IF		IF(<cond>,x,y) = x if <cond> is true,
        else y (like C's "<cond> ? x : y") (XXX)
 INDEX		Finds value at specified offset in a
        range (XXX)
 INT		Rounds to next lowest integer (One
        numeric argument)
 IRR		Internal rate of return (XXX)
 ISERR		True if argument is error (XXX)
 ISNUMBER		True if argument is number (XXX)
 ISSTRING		True if argument is string (XXX)
 LEFT		Returns first characters in string (Two
        arguments: string, number of letters)
 LENGTH		Returns length of string (One string
        argument)
 LN		Natural log (One numeric argument)
 LOG		Log to base 10 (One numeric argument)
 LOWER		Converts string to all-lowercase (One
        string argument)
 MAX		Returns largest of arguments (XXX)
 MID		Returns characters from middle of
        string (Three arguments: string,
        starting offset (0 is first character),
        desired string length)
 MIN		Returns smallest of arguments (XXX)
 MOD		Modulo arithmetic (XXX)
 N		Returns value of first cell in range
        (XXX)
 NA		Returns error PSEE_NA (XXX)
 NPV		Returns net present value of future
        cash flows (XXX)
 OR		Boolean OR (XXX)
 PI		Returns 3.1415926... (XXX)
 PMT		Calculates # of payments to pay off a
        debt (XXX)
 PRODUCT		Returns product of arguments (XXX)
 PROPER		Converts string to "Proper
        Capitalization" (One string argument)
 PV		Calculates present value of an
        investment (XXX)
 RADIANS		Converts degrees to radians (XXX)
RANDOM		Generates random number between 0 and 1
        (XXX)
 RANDOMN		Generates random integer below a
        specified ceiling (XXX)
 RATE		Calculates interest rate needed for
        investment to reach specified value
        (XXX)
 REPEAT		Returns string made of repeated
        argument string (Two arguments: string
        to repeat, number of times to repeat)
 REPLACE		Replaces characters in a string (Four
        arguments: original string, offset of
        first character to replace (0 is the
        first character), number of characters
        to replace, replacement string)
 RIGHT		Returns last characters in a string
        (Two arguments: original string,
        desired length of returned string)
 ROUND		Rounds number to specified precision
        (XXX)
 ROWS		Returns number of rows in range (XXX)
 SIN		Sine (One numeric argument)
 SINH		Hyperbolic-sine (One numeric argument)
 SLN		Calculates straight-line depreciation
        (XXX)
 SQRT		Square-root (One numeric argument)
 STD		Calculates standard deviation (XXX)
 STDP		Standard deviation of entire population
        (XXX)
 STRING		Converts number into string (Two
        numeric arguments: number to convert,
        number of decimal places (between 0 and
        DECIMAL_PRECISION))
 SUM		Returns sum of arguments (XXX)
 SYD		Sum-of-years'-digits depreciation (XXX)
 TAN		Tangent (sine/cosine) (One numeric
        argument)
 TANH		Hyperbolic tangent (sinh/cosh) (One
        numeric argument)
 TERM		Returns number of payments needed to
        reach future value (XXX)
 TRIM		Removes leading, trailing, and
        consecutive spaces from a string (One
        string argument)
 TRUE		Returns TRUE (1.0) (XXX)
 TRUNC		Removes fractional part; rounds towards
        zero (One numeric argument)
 UPPER		Converts all letters in string to
        uppercase (One string argument)
 VALUE		Converts string to number (One string
        argument)
| Up: GEOS SDK TechDocs | Up | Prev: 2 Parser Functions | Next: 2.2 External Functions


External Functions

Parse Library: 2.2 Parser Functions: External Functions

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Internal Functions | Next: 3 Coding with the Parse Library

Applications which use the Parse library may write their own functions. Whenever the formatter encounters a function name which it does not recognize, it calls the application to get an ID for the function. When the evaluator needs to evaluate that function, it calls the application, passing the arguments and the function ID. The application should return a single value. If it cannot produce a value, it should return an error code. The error codes are described in Evaluating a Token Sequence .

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Internal Functions | Next: 3 Coding with the Parse Library


Coding with the Parse Library

Parse Library: 3 Coding with the Parse Library

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Parser Functions | Next: 3.1 Parsing a String

This section describes how to use the Parser directly, instead of using intermediaries (like the Spreadsheet library). Most applications will not need to use these routines.

Parsing a String

Evaluating a Token Sequence

Formatting a Token Sequence

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Parser Functions | Next: 3.1 Parsing a String


Parsing a String

Parse Library: 3.1 Coding with the Parse Library: Parsing a String

| Up: GEOS SDK TechDocs | Up | Prev: 3 Coding with the Parse Library | Next: 3.2 Evaluating a Token Sequence
ParserParseString()

To parse a string, call ParserParseString() . This routine takes four arguments:

  • a pointer to a null-terminated string
  • a pointer to a buffer
  • a pointer to a CParserStruct (This structure contains a pointer to a callback routine.)
  • a pointer to a CParserReturnStruct

ParserParseString() parses a string into a sequence of tokens and writes the tokens to the buffer. Whenever the parser encounters an identifier, it calls the callback routine and requests an ID number for the identifier. Similarly, when the parser encounters a function whose name it does not recognize, it calls the callback routine to get a function ID number. The ID numbers are stored in the token sequence. Note: it is best to parse strings that contain known function names and identifiers.

The Parser can return the following errors:

PSEE_BAD_NUMBER
The string contained a badly-formatted number.
PSEE_BAD_CELL_REFERENCE
The string contained a badly-formatted cell reference.
PSEE_NO_CLOSE_QUOTE
The string contained an opening quote with no matching closing quote.
PSEE_COLUMN_TOO_LARGE
The string contained a cell whose column index was out of bounds (greater than 255).
PSEE_ROW_TOO_LARGE
The string contained a cell whose row index was out of bounds.
PSEE_ILLEGAL_TOKEN
The string contained a character sequence which was not a legal token.
PSEE_TOO_MANY_TOKENS
The expression was too complex.
PSEE_EXPECTED_OPEN_PAREN
A function call lacked an open-parenthesis.
PSEE_EXPECTED_CLOSE_PAREN
A function call lacked a close-parenthesis.
PSEE_BAD_EXPRESSION
The string contained a badly-formed expression.
PSEE_EXPECTED_END_OF_EXPRESSION
An expression ended improperly.
PSEE_MISSING_CLOSE_PAREN
Parentheses were mismatched.
PSEE_UNKNOWN_IDENTIFIER
An identifier or external function name was encountered, and the callback routine would not provide an ID for it.
PSEE_GENERAL
General parser error.
| Up: GEOS SDK TechDocs | Up | Prev: 3 Coding with the Parse Library | Next: 3.2 Evaluating a Token Sequence


Evaluating a Token Sequence

Parse Library: 3.2 Coding with the Parse Library: Evaluating a Token Sequence

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Parsing a String | Next: 3.3 Formatting a Token Sequence
ParserEvalExpression()

To format an expression, call ParserEvalExpression() . This routine is passed a token sequence; it evaluates it and writes the resulting token sequence to a passed buffer. It calls a supplied callback routine to perform the following tasks:

  • Return the value of a specified cell
  • Return the value associated with a given identifier, specified by ID number
  • Evaluate an external function, given the arguments and the function ID number

The evaluator produces a sequence two tokens long, including the "end-of-expression" token. The first token might be an error token. Two errors are so serious that if they occur, the evaluation is immediately halted and the error is returned:

PSEE_OUT_OF_STACK_SPACE
The evaluator ran out of stack space. Evaluation was halted when this occurred.
PSEE_NESTING_TOO_DEEP
The nesting grew too deep for the evaluator. Evaluation was halted when this occurred.

The following errors may be propagated; that is, if an expression returns an error, that error would be passed, as a value, to outer expressions. For example, if the evaluator were evaluating "SUM(1, (PROD(1, 2, "F. T. Poomm"))", PROD would return PSEE_WRONG_TYPE, since it expects numeric arguments. SUM, in turn, would be passed two arguments: the number 1 and the error PSEE_WRONG_TYPE. That function might, in turn, propagate the error upward, return a different error, or return a non-error value. (SUM, as it happens, would propagate the error; that is, it would return PSEE_WRONG_TYPE.)

PSEE_ROW_OUT_OF_RANGE
A cell's row index was out of range.
PSEE_COLUMN_OUT_OF_RANGE
A cell's column index was out of range.
PSEE_FUNCTION_NO_LONGER_EXISTS
The callback routine did not recognize the function ID for an external function.
PSEE_BAD_ARG_COUNT
A function was passed the wrong number of arguments.
PSEE_WRONG_TYPE
A function was passed an argument of the wrong type.
PSEE_DIVIDE_BY_ZERO
A division by zero was attempted.
PSEE_UNDEFINED_NAME
The callback would not provide a value for an identifier ID.
PSEE_CIRCULAR_REF
A circular reference occurred. This error will only occur if it is returned by the callback routine.
PSEE_CIRCULAR_DEP
The value is dependant on a cell whose value is PSEE_CIRCULAR_REF.
PSEE_CIRC_NAME_REF
The expression uses a name which is defined circularly.
PSEE_NUMBER_OUT_OF_RANGE
The result was a number which could not be expressed as a float.
PSEE_GEN_ERR
General error; this is returned when no other error code is appropriate.
PSEE_NA
The value for a cell was not available.
PSEE_FLOAT_POS_INFINITY
A float routine returned the error FLOAT_POS_INFINITY.
PSEE_FLOAT_NEG_INFINITY
A float routine returned the error FLOAT_NEG_INFINITY.
PSEE_FLOAT_GEN_ERR
A float routine returned the error FLOAT_GEN_ERR.
PSEE_TOO_MANY_DEPENDENCIES
The formula contained too many levels of dependency. This is generally returned by the callback routine; the Parse library routines do not return this error, they merely propagate it.

The application may also define its own error codes, beginning with the constant PSEE_FIRST_APPLICATION_ERROR. All internal functions, and all operators, always propagate application-defined errors.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Parsing a String | Next: 3.3 Formatting a Token Sequence


Formatting a Token Sequence

Parse Library: 3.3 Coding with the Parse Library: Formatting a Token Sequence

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Evaluating a Token Sequence
ParserFormatExpression()

The routine ParserFormatExpression() is passed a token buffer; it returns a character string. The formatter uses the localization routines to format numbers. The formatter also formats error codes as appropriate error messages. These error messages are stored in a localizable resource, so the formatter library will produce error messages in the appropriate language.

If the formatter encounters a token ID or an external function ID, it will call the callback routine to find out what character sequence is associated with that ID number. If it encounters an application-defined error code, it will request an appropriate error string. Applications should store these error strings in localizable resources; this will simplify translating the application into another language.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Evaluating a Token Sequence


Ruler Object Library

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Ruler Features
| One-page Version of this Chapter for Easy Printing

The ruler library provides objects that act as rulers. These rulers may be linked up with a GenView object so that when the user scrolls around within the GenView, the rulers update accordingly. The rulers work with a variety of measurement systems and also provide grids and guidelines to help the user to figure out the spacial layout of a graphic space. To aid the user's mouse movement, the rulers can constrain the mouse's movement to certain lines.

To work with the basic ruler set-up, you don't need to know much about GEOS other than the basics of objects and messaging. However, for certain customizations, you will probably need to know something about GenViews and notifications they pass on to linked objects.

1 Ruler Features
2 Ruler Setup
3 VisRuler Instance Data
4 Managing Rulers
4.1 RulerShowControl
4.2 Mouse Tracking
4.3 Grid Spacing and Constraint
4.4 Guide Constraints and Guidelines
4.5 Other Mouse Constraints
4.6 Esoteric Messages

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Ruler Features
| One-page Version of this Chapter for Easy Printing


Ruler Features

Ruler Object Library: 1 Ruler Features

| Up: GEOS SDK TechDocs | Up | Prev: Ruler Object Library | Next: 2 Ruler Setup

Rulers are a "must have" feature for drawing programs or any geode concerned with layout-intensive tasks. By alerting the rulers to pointer events, the application may display marks in the ruler which track the mouse's position so that the user knows the exact mouse coordinates. The ruler library provides many more features to applications prepared to support those features:

  • Grids
    The ruler's tick marks are of course drawn inside the ruler. However, the ruler can draw a grid over the main view so that the user may see where major units fall on the document directly.
  • Guides
    While a regular grid is helpful, the user may want to set up guidelines to mark off areas important to a document's layout. These guides may appear both as marks on the ruler and lines across the main view itself.
  • Mouse Constraints
    The rulers can act as mouse constraint managers. The mouse location can snap to grid lines, guidelines, or lines at any angle through any point.
  • Large Document Support
    The rulers work with large (32-bit) documents. They respond correctly to large mouse and scrolling events. However, at this time there is no way using C to intercept the message by which the Ruler handles large pointer events.
  • Controller Support
    The ruler library includes a full suite of controller objects, including controllers for managing grids and guides.
| Up: GEOS SDK TechDocs | Up | Prev: Ruler Object Library | Next: 2 Ruler Setup


Ruler Setup

Ruler Object Library: 2 Ruler Setup

| Up: GEOS SDK TechDocs | Up | Prev: 1 Ruler Features | Next: 3 VisRuler Instance Data

For each ruler a geode displays, it will need to include three objects: an object of RulerViewClass to display the ruler, a RulerContentClass object to act as the top of the RulerView's visible tree, and a VisRulerClass object, the ruler itself. Also, the geode may wish to include some of the available ruler control objects.

The RulerViews should be alerted when the main GenView scrolls or scales, so set the GenView's GVI_horizLink and GVI_vertLinks to hold the optrs of the RulerViews. A RulerView to the side of the GenView should be linked by the horizontal link; a RulerView above or below the GenView should be linked vertically.

Each RulerView should have a RulerContent as its content. Set this up as you would a normal GenView/VisContent linkage. The RulerContent functions as a VisContent for most purposes; there is some subclassed behavior so that the VisRuler will be notified when the RulerView has detected a scroll or scale event.

A VisRuler should be the child of the RulerContent. If you have more than one VisRuler associated with a view, you should use the VRI_slave links to connect them. When one VisRuler receives certain messages, it will handle them and pass them on to whatever VisRuler has been designated in its VRI_slave field. If this slave ruler itself has a slave, then the message will be relayed again, and so on. Thus the application only has to send messages to one ruler, instead of to all of them. The ruler which is not the target of a VRI_slave link is the view's master ruler, and its VRA_MASTER bit should be set.

If the view only has one ruler associated with it, that ruler's VRA_MASTER bit should be set.

If you will use the ruler library's mouse constraint management support, then the VisRulers must be run in the same thread which manages the main content. The VisRuler messages which deal with mouse positions receive the mouse location parameters on the stack, and thus the handlers must be running in the same thread as the callers. This is not a major restriction: since mouse events are so common, sending them across threads to be processed would lead to slow, jerky responses.

Your application may also include other objects:

  • RulerTypeControl to allow the user to use different measurement units.
  • RulerGridControl to draw a grid on the main view to which the user may snap the mouse.
  • RulerGuideControl to allow the user to manage guidelines.
  • RulerShowControl to show or hide rulers. Because hidden rulers are set not usable, and thus do not work well with GCN, applications including a RulerShowControl need to do some extra work. This is not difficult, but exactly what needs to be done may vary between applications. For details, see RulerShowControl .
  • If you will be using any of the ruler controllers, you must provide a way to relay classed events to the VisRuler objects. Whichever object will have the target when the controllers send out their classed events must have a special handler for MSG_META_SEND_CLASSED_EVENT which determines if the message is intended for a member of VisRuler class, and if so passes it to the master VisRuler object. Note that if you are working with the grobj library, a targeted GrObjBody correctly relays classed events to rulers.

Finally, if you wish the VisRuler to take special action on certain mouse events (perhaps providing a mark on the ruler to track the mouse pointer's position), the GenView's content should intercept those mouse events and send messages to the VisRuler (again, the VisRuler at the top of the VRI_slave chain) with the requested action.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Ruler Features | Next: 3 VisRuler Instance Data


VisRuler Instance Data

Ruler Object Library: 3 VisRuler Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 2 Ruler Setup | Next: 4 Managing Rulers

The VisRuler's instance data fields are listed below. Depending on your setup, the management of most of these fields will probably be taken care of by the ruler itself or some of its associated controllers. However, many programs set values for the VRI_rulerAttrs , VRI_constrainStrategy , and VRI_slave fields when defining their rulers.

Code Display 19-1

@classVisRulerClass,VisClass;
@instance VisRulerAttributes VRI_rulerAttrs;
typedef ByteFlags VisRulerAttributes;
#define VRA_IGNORE_ORIGIN 				0x80
#define VRA_SHOW_GUIDES 				0x40
#define VRA_SHOW_GRID 				0x20
#define VRA_SHOW_MOUSE 				0x10
#define VRA_HORIZONTAL 				0x08
#define VRA_MASTER 				0x04
@instance VisRulerType VRI_type;
typedef ByteEnum VisRulerType;
  #define VRT_INCHES 			0x0
#define VRT_CENTIMETERS 			0x1
#define VRT_POINTS 			0x2
#define VRT_PICAS 			0x3
#define VRT_CUSTOM 			CUSTOM_RULER_DEFINITION
#define VRT_NONE 			NO_RULERS
#define VRT_DEFAULT 			SYSTEM_DEFAULT
@instance VisRulerConstrainStrategy VRI_constrainStrategy;
typedef WordFlags VisRulerConstrainStrategy;
#define VRCS_OVERRIDE 							0x8000
#define VRCS_SET_REFERENCE 							0x2000
#define VRCS_SNAP_TO_GRID_X_ABSOLUTE 							0x1000
#define VRCS_SNAP_TO_GRID_Y_ABSOLUTE 							0x0800
#define VRCS_SNAP_TO_GRID_X_RELATIVE 							0x0400
#define VRCS_SNAP_TO_GRID_Y_RELATIVE 							0x0200
#define VRCS_SNAP_TO_GUIDES_X 							0x0100
#define VRCS_SNAP_TO_GUIDES_Y 							0x0080
#define VRCS_CONSTRAIN_TO_HORIZONTAL_AXIS 							0x0040
#define VRCS_CONSTRAIN_TO_VERTICAL_AXIS 							0x0020
#define VRCS_CONSTRAIN_TO_UNITY_SLOPE_AXIS 							0x0010
#define VRCS_CONSTRAIN_TO_NEGATIVE_UNITY_SLOPE_AXIS 							0x0008
#define VRCS_CONSTRAIN_TO_VECTOR 							0x0004
#define VRCS_CONSTRAIN_TO_VECTOR_REFLECTION 							0x0002
#define VRCS_INTERNAL 							0x0001
@instance MinIncrementType 			VRI_minIncrement; /* minimum increment displayed */
  typedef union {
    MinUSMeasure 			MIT_US;
    MinMetricMeasure 			MIT_METRIC;
    MinPointMeasure 			MIT_POINT;
    MinPicaMeasure 			MIT_PICA;
} MinIncrementType;
typedef ByteEnum   MinUSMeasure;
#define MUSM_EIGHTH_INCH 				0x0
#define MUSM_QUARTER_INCH 				0x1
#define MUSM_HALF_INCH 				0x2
#define MUSM_ONE_INCH 				0x3
typedef ByteEnum   MinMetricMeasure;
#define MMM_MILLIMETER 				0x0
#define MMM_HALF_CENTIMETER 				0x1
#define MMM_CENTIMETER 				0x2
typedef ByteEnum   MinPointMeasure;
#define MPM_25_POINT 				0x0
#define MPM_50_POINT 				0x1
#define MPM_100_POINT 				0x2
typedef ByteEnum   MinPicaMeasure;
#define MPM_PICA 				0x0
#define MPM_INCH 				0x1
@instance WWFixed VRI_scale; /* scale factor */
@instance DWFixed VRI_origin; /* 0,0 of the ruler in document coordinates */
@instance PointDWFixed VRI_reference;
@instance sdword VRI_mouseMark;
@instance word VRI_window; /* actually a noreloc WindowHandle */
@instance optr VRI_slave;
@instance Grid VRI_grid;
@instance WWFixed VRI_vectorSlope; /* slope of vector */
@instance optr VRI_guideArray;
@instance word VRI_guideInfluence; /* influence of guides in pixels */
@instance word VRI_desiredSize;
@instance word VRI_reserved;
@instance dword VRI_invalOD; /* actually a noreloc optr */
@instance word VRI_transformGState; /* actually a noreloc GStateHandle */

The VRI_rulerAttrs field contains flags which determine whether the ruler will support given features. This field tends to vary between applications; put some thought into which flags to turn on.

VRA_IGNORE_ORIGIN
Using the VRI_origin field, it is possible to set an origin for the user to use based upon the coordinates of the graphic space within the main view. This can come in handy when the zero point for the ruler shouldn't correspond to the very left or top of the view; GeoWrite uses this when the user wants the ruler's origin to be based upon the right edge of a text object rather than the edge of the view. The VRA_IGNORE_ORIGIN flag effectively turns off this feature.
VRA_SHOW_GUIDES
This flag indicates that the user wants guidelines drawn over their main view.
VRA_SHOW_GRID
This flag indicates that the user wants gridlines drawn over the main view.
VRA_SHOW_MOUSE
This flag indicates that the ruler should draw a tick mark to show the mouse pointer's location.
VRA_HORIZONTAL
This flag indicates that the ruler is horizontal. Horizontal rulers should have this flag turned on; vertical rulers should not.
VRA_MASTER
If a given view has only one ruler associated with it, that ruler should have this flag set. If a given view has more than one ruler (perhaps having one horizontal and one vertical), one of these rulers should be set up as the master ruler. It should have the VRA_MASTER flag set and should have the second ruler's optr in its VRI_slave field. If there is a third ruler, the second ruler should have its optr in its (the second ruler's) VRI_slave field, and so on.

The VRI_constrainStrategy field contains the default mouse constraint strategy the ruler should use when constraining mouse movement.

VRCS_OVERRIDE
This flag isn't really meant to be part of the default mouse constraint; it will be ignored if in the VRI_constrainStrategy field. During some events other constrain strategies may be combined with the default mouse constrain strategy; if the other strategy's VRCS_OVERRIDE flag is set, then the default mouse constraint strategy field will be ignored.
VRCS_SET_REFERENCE
This flag signals that the next selection should signal that the user wishes the point clicked on to be the reference point, which is used with certain kinds of mouse constraint.
VRCS_SNAP_TO_GRID_X_ABSOLUTE
This flag signals that the mouse position should be snapped to the grid horizontally.
VRCS_SNAP_TO_GRID_Y_ABSOLUTE
This flag signals that the mouse position should be snapped to the grid vertically.
VRCS_SNAP_TO_GRID_X_RELATIVE
This flag signals that the mouse position should be horizontally snapped to a grid such as would be formed if the present grid spacing was used with the reference point as the grid's origin.
VRCS_SNAP_TO_GRID_Y_RELATIVE
This flag signals that the mouse position should be vertically snapped to a grid such as would be formed if the present grid spacing was used with the reference point as the grid's origin.
VRCS_SNAP_TO_GUIDES_X
This flag signals that the mouse positions should be horizontally snapped to guidelines. Note that guidelines are only effective within range of their "influence" in pixels.
VRCS_SNAP_TO_GUIDES_Y
This flag signals that the mouse positions should be vertically snapped to guidelines. Note that guidelines are only effective within range of their "influence" in pixels.
VRCS_CONSTRAIN_TO_HORIZONTAL_AXIS
This flag signals that the mouse position should be restrained to the horizontal line passing through the reference point.
VRCS_CONSTRAIN_TO_VERTICAL_AXIS
This flag signals that the mouse position should be restrained to the vertical line passing through the reference point.
VRCS_CONSTRAIN_TO_UNITY_SLOPE_AXIS
This flag signals that the mouse position should be restrained to the diagonal line with slope ( y = x ) passing through the reference point.
VRCS_CONSTRAIN_TO_NEGATIVE_UNITY_SLOPE_AXIS
This flag signals that the mouse position should be restrained to the diagonal line with slope ( y = -x ) passing through the reference point.
VRCS_CONSTRAIN_TO_VECTOR
This flag signals that the mouse position should be constrained to the line passing through the reference and vector points.
VRCS_CONSTRAIN_TO_VECTOR_REFLECTION
This flag signals that the mouse position should be constrained to the reflection of the line passing through the reference and vector points.

The VRI_type and VRI_minIncrement fields determine the measurement type and ruler increment of the ruler.Normally, the user will work with the ruler's type via a RulerTypeControl. RulerTypeControlClass 's features structures are shown below.

Code Display 19-2 RulerTypeControl Features

typedef WordFlags  RTCFeatures;
#define  RTCF_DEFAULT				(0x20)
#define RTCF_SPREADSHEET 				(0x10)
#define RTCF_INCHES 				(0x08)
#define RTCF_CENTIMETERS 				(0x04)
#define RTCF_POINTS 				(0x02)
#define RTCF_PICAS 				(0x01)
#define  RTC_DEFAULT_FEATURES \
    (RTCF_INCHES | RTCF_CENTIMETERS | RTCF_POINTS | RTCF_PICAS | RTCF_DEFAULT)
typedef WordFlags  RTCToolboxFeatures;
#define  RTCTF_DEFAULT 				(0x20)
#define RTCTF_SPREADSHEET 				(0x10)
#define RTCTF_INCHES 				(0x08)
#define RTCTF_CENTIMETERS 				(0x04)
#define RTCTF_POINTS 				(0x02)
#define RTCTF_PICAS 				(0x01)
#define  RTC_DEFAULT_TOOLBOX_FEATURES (RTCF_INCHES | RTCF_CENTIMETERS)

The RulerTypeControl uses a notification block with the following structure:

typedef struct RulerTypeNotificationBlock {
    VisRulerType  RTNB_type;
}  RulerTypeNotificationBlock;

The ruler must keep track of the main view's scale; if the main view has doubled in scale, the ruler should put its tick marks twice as far apart. This scale is stored in the VRI_scale field. The ruler automatically receives a MSG_VIS_RULER_VIEW_SCALE_FACTOR_CHANGED when the main view's scale changes. To set the scale independently of the main view scale, send a MSG_VIS_RULER_SET_SCALE .

The VRI_origin field allows you to set an origin for the ruler other than (0,0). The coordinates are given in terms of the main view's coordinate system.

The reference point, used for various types of mouse constraint, is stored in the VRI_reference field.

VRI_mouseMark contains the coordinate at which the mouse tick is currently being drawn.

The VRI_window field, initiated automatically, holds the window handle of the main view.

The VRI_slave field holds the ruler's slave ruler; see the documentation for VRA_MASTER, above, to learn about master and slave rulers.

The VRI_grid field contains the information used to maintain the grid associated with a ruler.

The VRI_vectorSlope defines the slope of a vector that may be used with vector mouse constraints.

The VRI_guideArray field keeps track of the location where guideline data is stored; this field is initiated automatically.

The VRI_guideInfluence contains the influence to use when constraining the mouse position to guidelines. Guidelines will only attract the mouse if it is within a certain distance of them--this distance is the "influence" of the guidelines. It is measured in pixels, not points; thus, the guide influence is independent of scale.

The VRI_desiredSize field contains the ruler's idea of its ideal thickness. By default, this is set so that the ruler will have room to draw its numbers and tick marks normally; if you will be subclassing the VisRuler to add more gadgetry, you may wish to use a different value here.

The VRI_invalOD field contains the object descriptor of the object which should be redrawn when the ruler needs to draw something differently to the main view; by the ruler's default behavior, this will be updated automatically via a MSG_VIS_RULER_GAINED_SELECTION .

The VRI_transformGState field contains the handle of a GState. This GState contains the transformation which is used to transform the coordinate system used with mouse constraints.

MSG_VIS_RULER_SET_TYPE
void	MSG_VIS_RULER_SET_TYPE(
        VisRulerType type);

This message sets the VRI_type field.

MSG_VIS_RULER_GET_TYPE
VisRulerType MSG_VIS_RULER_GET_TYPE();

This message returns the value in the VRI_type field.

MSG_VIS_RULER_SET_CONSTRAIN_STRATEGY
void MSG_VIS_RULER_SET_CONSTRAIN_STRATEGY(
        VisRulerConstrainStrategy 				setflags,
        VisRulerConstrainStrategy 				clearflags);

This message sets the default mouse constrain strategy for the ruler.

Parameters: setflags Flags to set.

clearflags
Flags to clear.

Return: Nothing.

MSG_VIS_RULER_GET_CONSTRAIN_STRATEGY
VisRulerConstrainStrategy
MSG_VIS_RULER_GET_CONSTRAIN_STRATEGY();

This message gets the current default constrain strategy for the ruler.

MSG_VIS_RULER_SET_IGNORE_ORIGIN
void MSG_VIS_RULER_SET_IGNORE_ORIGIN(
        Boolean 		ignore);

This message sets the "ignore origin" state.

Parameters: ignore Set true (i.e. non-zero) to ignore the origin. Set false (i.e. zero) not to ignore the origin.

Return: Nothing.

MSG_VIS_RULER_SET_ORIGIN
void MSG_VIS_RULER_SET_ORIGIN(
        sdword 		pointInt,
        word 		pointFrac);

Use this message to set the ruler's origin point.

MSG_VIS_RULER_GET_ORIGIN
void MSG_VIS_RULER_GET_ORIGIN(
        DWFixedReturn *retval);

Use this message to retrieve the ruler's origin point.

Structures:

typedef struct {
    word 		unused;
    word 		 DWFR_frac;
    sdword 		DWFR_int;
}  DWFixedReturn;
MSG_VIS_RULER_SET_REFERENCE
void MSG_VIS_RULER_SET_REFERENCE( @stack
        sdword 		yInt,
        word 		yFrac,
        sdword 		xInt,
        word 		xFrac);

Set the ruler's reference point.

MSG_VIS_RULER_SET_VECTOR
void MSG_VIS_RULER_SET_VECTOR( @stack
        sdword 		yInt,
        word 		yFrac,
        sdword 		xInt,
        word 		xFrac);

Set the point to use to define the "vector"--together with the reference point, this will form a slope which may be used for mouse constraints.

MSG_VIS_RULER_SET_MIN_INCREMENT
void	MSG_VIS_RULER_SET_MIN_INCREMENT(
        MinIncrementType min);

This message sets the VRI_minIncrement field to the passed value. It must be passed the proper MinIncrementType value.

MSG_VIS_RULER_SET_SCALE
void	MSG_VIS_RULER_SET_SCALE(
        WWFixedAsDWord scale);

This message sets the VRI_scale field.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Ruler Setup | Next: 4 Managing Rulers


Managing Rulers

Ruler Object Library: 4 Managing Rulers

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 VisRuler Instance Data | Next: 4.1 RulerShowControl

Most ruler functions will happen automatically. However, certain pieces of functionality require that the geode provide certain message handlers. Some geodes may have use for subclassed ruler objects, and subclassing will require further extra code. This section provides information for those who would learn more about how to manage rulers.

RulerShowControl

Mouse Tracking

Grid Spacing and Constraint

Guide Constraints and Guidelines

Other Mouse Constraints

Esoteric Messages

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 VisRuler Instance Data | Next: 4.1 RulerShowControl


RulerShowControl

Ruler Object Library: 4.1 Managing Rulers: RulerShowControl

| Up: GEOS SDK TechDocs | Up | Prev: 4 Managing Rulers | Next: 4.2 Mouse Tracking

The RulerShowControl allows the user to show or hide rulers. This control works by setting RulerViews not usable. However, objects which aren't usable don't work with GCN, and thus you will need to set up some other object to intercept the appropriate notifications. Don't try to include a RulerShowControl unless you know about GCN or are willing to learn.

When declaring your RulerShowControl, you may choose a GCN list which the control will work with. The default list is MANUFACTURER_ID_GEOWORKS:
GAGCNLT_DISPLAY_OBJECTS_WITH_RULERS. However, you may change this to be any list you like. You must also specify a message which some object will respond to.

Some object which will be usable when the user has access to the RulerShowControl should be on the control's GCN list. This object must be prepared to handle the message in the RSCI_message field. This message should take a RulerShowControlAttributes structure and set the ruler views usable or not usable accordingly. See Sample Handler for use with RulerShowControl for an example.

Code Display 19-3 RulerShowControl Instance Data and Features

typedef WordFlags  RSCCFeatures;
#define  RSCCF_SHOW_VERTICAL (0x04)
#define RSCCF_SHOW_HORIZONTAL (0x02)
#define RSCCF_SHOW_RULERS (0x01)
typedef WordFlags  RSCCToolboxFeatures;
/* There are no toolbox features. */
#define  RSCC_DEFAULT_FEATURES (RSCCF_SHOW_VERTICAL | RSCCF_SHOW_HORIZONTAL)
#define RSCC_DEFAULT_TOOLBOX_FEATURES (0)
@instance RulerShowControlAttributes 					RSCI_attrs;
typedef WordFlags  RulerShowControlAttributes;
#define  RSCA_SHOW_VERTICAL 				0x8000
#define RSCA_SHOW_HORIZONTAL 				0x4000
@instance GCNListType 					 RSCI_gcnList; /* object to notify */
@instance Message 					RSCI_message; /* message to notify with */

Code Display 19-4 Sample Handler for use with RulerShowControl

@method MyDisplayClass, MSG_MD_UPDATE_RULERS
    /* The RulerShowControl's RSCI_message field should be
     * MSG_MD_UPDATE_RULERS. The message should have been declared
     * using the RULER_SHOW_CONTROL_NOTIFY prototype. */
  if (attrs & RSCA_SHOW_VERTICAL) {
    @call LeftView::MSG_GEN_SET_USABLE(VUM_NOW);
  }
  else {
    @call LeftView::MSG_GEN_SET_NOT_USABLE(VUM_NOW);
  }
  if (attrs & RSCA_SHOW_HORIZONTAL) {
    @call TopView::MSG_GEN_SET_USABLE(VUM_NOW);
  }
  else {
    @call TopView::MSG_GEN_SET_NOT_USABLE(VUM_NOW);
  }
}
| Up: GEOS SDK TechDocs | Up | Prev: 4 Managing Rulers | Next: 4.2 Mouse Tracking


Mouse Tracking

Ruler Object Library: 4.2 Managing Rulers: Mouse Tracking

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 RulerShowControl | Next: 4.3 Grid Spacing and Constraint

The ruler has the ability to show a tick mark which tracks the mouse's movement. This can be a boon to users who wish to know the mouse's precise location.

MSG_VIS_RULER_SHOW_MOUSE

void MSG_VIS_RULER_SHOW_MOUSE();

Sets a flag such that the ruler will draw the mouse tick on pointer events.

MSG_VIS_RULER_HIDE_MOUSE

void MSG_VIS_RULER_HIDE_MOUSE();

Sets a flag such that the ruler won't draw the mouse tick on pointer events.

MSG_VIS_RULER_DRAW_MOUSE_TICK

void MSG_VIS_RULER_DRAW_MOUSE_TICK( @stack
        sdword 		yInt,
        word 		yFrac,
        sdword 		xInt,
        word 		xFrac);

This message indicates that the ruler should draw a line indicating the mouse position and informs the ruler of the mouse's new position.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 RulerShowControl | Next: 4.3 Grid Spacing and Constraint


Grid Spacing and Constraint

Ruler Object Library: 4.3 Managing Rulers: Grid Spacing and Constraint

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Mouse Tracking | Next: 4.4 Guide Constraints and Guidelines

Applications which provide rulers often do so to allow the user to work with the mouse more accurately. Such applications might also provide a grid. A grid helps locate the mouse pointer's exact location on the document without the need to watch the ruler. The ruler can also constrain mouse movement to the grid.

To allow the user to change the grid spacing, include a RulerGridControl. The features structures for the RulerGridControl are shown below.

Code Display 19-5 RulerGridControlClass Features

typedef WordFlags  RGCFeatures;
#define  RGCF_GRID_SPACING 				(0x04)
#define RGCF_SNAP_TO_GRID 				(0x02)
#define RGCF_SHOW_GRID 				(0x01)
#define R GC_DEFAULT_FEATURES \
            (RGCF_GRID_SPACING | RGCF_SNAP_TO_GRID | RGCF_SHOW_GRID)
#define RGC_DEFAULT_TOOLBOX_FEATURES (0)

MSG_VIS_RULER_SHOW_GRID

void MSG_VIS_RULER_SHOW_GRID();

Use this message to request that the grid be drawn to the main view, setting the VRA_SHOW_GRID flag.

MSG_VIS_RULER_HIDE_GRID

void MSG_VIS_RULER_HIDE_GRID();

Use this message to request that the grid not be drawn to the main view, clearing the VRA_SHOW_GRID flag.

MSG_VIS_RULER_DRAW_GRID

void MSG_VIS_RULER_DRAW_GRID(
        GStateHandle gstate);

This message will draw the grid to the passed GState if the VRA_SHOW_GRID_FLAG is set.

MSG_VIS_RULER_TURN_GRID_SNAPPING_ON

void MSG_VIS_RULER_TURN_GRID_SNAPPING_ON();

Set the default VisRulerConstrainStrategy to support grid snapping, setting the VRCS_SNAP_TO_GRID_X_ABSOLUTE and VRCS_SNAP_TO_GRID_Y_ABSOLUTE flags.

MSG_VIS_RULER_TURN_GRID_SNAPPING_OFF

void MSG_VIS_RULER_TURN_GRID_SNAPPING_OFF();

Set the default VisRulerConstrainStrategy to not include grid snapping, turning off the VRCS_SNAP_TO_GRID_X_ABSOLUTE and VRCS_SNAP_TO_GRID_Y_ABSOLUTE flags.

MSG_VIS_RULER_SET_GRID_SPACING

void MSG_VIS_RULER_SET_GRID_SPACING(
        WWFixedAsDWord spacing);

Set the grid's horizontal and vertical spacing, working with the VRI_grid field.

MSG_VIS_RULER_SET_HORIZONTAL_GRID_SPACING

void MSG_VIS_RULER_SET_HORIZONTAL_GRID_SPACING(
        WWFixedAsDWord spacing);

Set the horizontal grid spacing, leaving the vertical alone.

MSG_VIS_RULER_SET_VERTICAL_GRID_SPACING

void MSG_VIS_RULER_SET_VERTICAL_GRID_SPACING(
        WWFixedAsDWord spacing);

Set the vertical grid spacing, leaving the horizontal alone

MSG_VIS_RULER_GET_GRID_SPACING

void MSG_VIS_RULER_GET_GRID_SPACING(
        GridSpacing* gridspace);

This message returns the current grid spacing.

Structures: This message works with the GridSpacing structure. Do not confuse this structure with the Grid structure.

typedef struct {
    WWFixed GS_y;
    WWFixed GS_x;
} GridSpacing;

MSG_VIS_RULER_GET_STRATEGIC_GRID_SPACING

void MSG_VIS_RULER_GET_STRATEGIC_GRID_SPACING(
        GridSpacing* gridspace);

This message returns a grid spacing that will look nice on the screen at the current scale factor.

Structures: This message works with the GridSpacing structure. Do not confuse this structure with the Grid structure.

typedef struct {
    WWFixed GS_y;
    WWFixed GS_x;
}   GridSpacing;

MSG_VIS_RULER_SNAP_TO_GRID

void MSG_VIS_RULER_SNAP_TO_GRID(
        PointDWFixed _far* point);

This message snaps the passed coordinate to the grid.

Parameters: point Point to snap to grid.

Return: Nothing returned explicitly, however structure in point modified so that point is snapped to grid.

MSG_VIS_RULER_SNAP_TO_GRID_X

void MSG_VIS_RULER_SNAP_TO_GRID_X(
        PointDWFixed _far* point);

Snap the passed point's x coordinate to the grid.

Parameters: point Point to snap to grid.

Return: Nothing returned explicitly, however structure in point modified so that point is snapped to grid.

MSG_VIS_RULER_SNAP_TO_GRID_Y

void MSG_VIS_RULER_SNAP_TO_GRID_Y(
        PointDWFixed _far* point);

Snap the passed point's y coordinate to the grid.

Parameters: point Point to snap to grid.

Return: Nothing returned explicitly, however structure in point modified so that point is snapped to grid.

MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE

void MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE(
        PointDWFixed _far* point);

Returns the point closest to the passed point that is an integral number of grid spacings from the reference point.

Parameters: point Point to snap.

Return: Nothing returned explicitly, however structure in point modified so that point is snapped.

MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE_X

void MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE_X(
        PointDWFixed _far* point);

Returns the point horizontally closest to the passed point that is an integral number of grid spacings from the reference point.

Parameters: point Point to snap.

Return: Nothing returned explicitly, however structure in point modified so that point is snapped.

MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE_Y

void MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE_Y(
        PointDWFixed _far* point);

Returns the point vertically closest to the passed point that is an integral number of grid spacings from the reference point.

Parameters: point Point to snap.

Return: Nothing returned explicitly, however structure in point modified so that point is snapped.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Mouse Tracking | Next: 4.4 Guide Constraints and Guidelines


Guide Constraints and Guidelines

Ruler Object Library: 4.4 Managing Rulers: Guide Constraints and Guidelines

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Grid Spacing and Constraint | Next: 4.5 Other Mouse Constraints

Sometimes the user will want to have some guidelines that do not conform to a regular grid. The user may set up their own guidelines. Normally to allow the use of guidelines, your application should include a RulerGuideControl object. The features structures for this controller class are shown below.

Code Display 19-6 RulerGuideControlClass Features

typedef WordFlags  RulerGuideControlFeatures;
#define  RGCF_HV 			(0x08)
#define RGCF_LIST 			(0x04)
#define RGCF_POSITION 			(0x02)
#define RGCF_DELETE 			(0x01)
#define  RULER_GUIDE_CONTROL_DEFAULT_FEATURES \
            (RGCF_HV | RGCF_LIST | RGCF_POSITION | RGCF_DELETE)

The following messages allow for the use and manipulation of guidelines.

MSG_VIS_RULER_TURN_GUIDES_SNAPPING_ON

void MSG_VIS_RULER_TURN_GUIDES_SNAPPING_ON();

Set the default VisRulerConstrainStrategy to include guides snapping, turning on the VRCS_SNAP_TO_GUIDES_X and VRCS_SNAP_TO_GUIDES_Y flags.

MSG_VIS_RULER_TURN_GUIDES_SNAPPING_OFF

void MSG_VIS_RULER_TURN_GUIDES_SNAPPING_OFF();

Set the default VisRulerConstrainStrategy to not include guides snapping, turning off the VRCS_SNAP_TO_GUIDES_X and VRCS_SNAP_TO_GUIDES_Y flags.

MSG_VIS_RULER_CREATE_GUIDE_ARRAY

void MSG_VIS_RULER_CREATE_GUIDE_ARRAY();

By default, the VisRuler stores its guides in a chunk array, known as a guide array. If the ruler has no guides defined, it doesn't bother keeping this data structure around. When the first guide is created, the VisRuler will send itself this message, to which it will respond by setting up the data region.

Subclasses might intercept this message if they have data structures which need initializing when the first guide is created.

MSG_VIS_RULER_ADD_HORIZONTAL_GUIDE

void MSG_VIS_RULER_ADD_HORIZONTAL_GUIDE( @stack
        sdword 		dwfInt,
        word 		dwfFrac);

Use this message to add a horizontal guide.

MSG_VIS_RULER_ADD_VERTICAL_GUIDE

void MSG_VIS_RULER_ADD_VERTICAL_GUIDE( @stack
        sdword 		dwfInt,
        word 		dwfFrac);

Use this message to add a vertical guide.

MSG_VIS_RULER_DRAW_GUIDES

void MSG_VIS_RULER_DRAW_GUIDES(
        GStateHandle gstate);

Use this message to draw guidelines to a document. The default VisRuler handler for this message will draw the lines at the appropriate coordinates to the passed GState.

MSG_VIS_RULER_DRAW_GUIDE_INDICATORS

void MSG_VIS_RULER_DRAW_GUIDE_INDICATORS();

This message asks the VisRuler to draw tick-marks on itself to show the positions of guides. The default VisRuler has no handler for this message; subclasses are welcome to intersect it and draw indicators appropriately.

MSG_VIS_RULER_SNAP_TO_GUIDES

void MSG_VIS_RULER_SNAP_TO_GUIDES(
        PointDWFixed _far* point);

Use this message to snap the passed point to the guides.

MSG_VIS_RULER_SNAP_TO_GUIDES_X

void MSG_VIS_RULER_SNAP_TO_GUIDES_X(
        PointDWFixed _far* point);

Use this message to horizontally snap the passed point to the guides.

MSG_VIS_RULER_SNAP_TO_GUIDES_Y

void MSG_VIS_RULER_SNAP_TO_GUIDES_Y(
        PointDWFixed _far* point);

Use this message to vertically snap the passed point to the guides.

MSG_VIS_RULER_GET_GUIDE_INFLUENCE

word MSG_VIS_RULER_GET_GUIDE_INFLUENCE();

Guidelines will only affect the mouse if the mouse pointer lies within the guide's area of influence. This message returns the present influence value.

MSG_VIS_RULER_SET_GUIDE_INFLUENCE

void MSG_VIS_RULER_SET_GUIDE_INFLUENCE(
        word influence);

Guidelines will only affect the mouse if the mouse pointer lies within the guide's area of influence. This message sets the influence distance, measured in pixels.

MSG_VIS_RULER_SELECT_HORIZONTAL_GUIDE

void MSG_VIS_RULER_SELECT_HORIZONTAL_GUIDE( @stack
        sdword 		dwfInt,
        word 		dwfFrac);

Use this message to select a horizontal guide at the passed coordinates, if any. Note that only one horizontal guide may be selected at any time.

MSG_VIS_RULER_SELECT_VERTICAL_GUIDE

void MSG_VIS_RULER_SELECT_VERTICAL_GUIDE( @stack
        sdword 		dwfInt,
        word 		dwfFrac);

Use this message to select a vertical guide at the passed coordinates, if any. Note that only one vertical guide may be selected at any time.

MSG_VIS_RULER_DESELECT_ALL_HORIZONTAL_GUIDES

void MSG_VIS_RULER_DESELECT_ALL_HORIZONTAL_GUIDES();

Use this message to deselect all of the ruler's horizontal guides.

MSG_VIS_RULER_DESELECT_ALL_VERTICAL_GUIDES

void MSG_VIS_RULER_DESELECT_ALL_VERTICAL_GUIDES();

Use this message to deselect all of the ruler's vertical guides.

MSG_VIS_RULER_DELETE_HORIZONTAL_GUIDE

void MSG_VIS_RULER_DELETE_HORIZONTAL_GUIDE( @stack
        sdword 		dwfInt,
        word 		dwfFrac);

Use this message to delete the horizontal guide at the passed coordinate, if any.

MSG_VIS_RULER_DELETE_VERTICAL_GUIDE

void MSG_VIS_RULER_DELETE_VERTICAL_GUIDE( @stack
        sdword 		dwfInt,
        word 		dwfFrac);

Use this message to delete the vertical guide at the passed coordinate, if any.

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Grid Spacing and Constraint | Next: 4.5 Other Mouse Constraints


Other Mouse Constraints

Ruler Object Library: 4.5 Managing Rulers: Other Mouse Constraints

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Guide Constraints and Guidelines | Next: 4.6 Esoteric Messages

Up until now, we have discussed various ways to constrain mouse movement to grid- or guide-lines. However, other constraints are possible, and may be accessed through the following messages.

MSG_VIS_RULER_CONSTRAIN_TO_AXES

void MSG_VIS_RULER_CONSTRAIN_TO_AXES(
        PointDWFixed _far* point);

Use this message to constrain the mouse to the axes; that is to force it to be in line either horizontally or vertically with the reference point (the point stored in VRI_reference ).

MSG_VIS_RULER_CONSTRAIN_TO_HORIZONTAL_AXIS

void MSG_VIS_RULER_CONSTRAIN_TO_HORIZONTAL_AXIS(
        PointDWFixed _far* point);

Use this message to constrain the mouse horizontally, forcing it to be in line horizontally with the reference point (the point stored in VRI_reference ).

MSG_VIS_RULER_CONSTRAIN_TO_VERTICAL_AXIS

void MSG_VIS_RULER_CONSTRAIN_TO_VERTICAL_AXIS(
        PointDWFixed _far* point);

Use this message to constrain the mouse vertically, forcing it to be in line vertically with the reference point (the point stored in VRI_reference ).

MSG_VIS_RULER_CONSTRAIN_TO_DIAGONALS

void MSG_VIS_RULER_CONSTRAIN_TO_DIAGONALS(
        PointDWFixed _far* point);

Use this message to constrain the mouse to the diagonal axes; that is to force it to be on a line 45 degrees through the reference point (the point stored in VRI_reference ).

MSG_VIS_RULER_SET_CONSTRAIN_TRANSFORM

void MSG_VIS_RULER_SET_CONSTRAIN_TRANSFORM(
        TransMatrix* tm)

Use this message to impose a transformation on the coordinate system used to constrain the mouse. You can see this at work in GeoDraw when resizing an object which has been rotated or skewed.

MSG_VIS_RULER_CLEAR_CONSTRAIN_TRANSFORM

void MSG_VIS_RULER_CLEAR_CONSTRAIN_TRANSFORM();

Use this message to remove any transformation on the coordinate system used to constrain the mouse.

MSG_VIS_RULER_CONSTRAIN_X_TO_UNITY_SLOPE_AXIS

void MSG_VIS_RULER_CONSTRAIN_X_TO_UNITY_SLOPE_AXIS(
        PointDWFixed _far* point);

Use this message to determine the coordinates resulting from horizontally snapping a given point to the Northeast diagonal line passing through the reference point.

MSG_VIS_RULER_CONSTRAIN_Y_TO_UNITY_SLOPE_AXIS

void MSG_VIS_RULER_CONSTRAIN_Y_TO_UNITY_SLOPE_AXIS(
        PointDWFixed _far* point);

Use this message to determine the coordinates resulting from vertically snapping a given point to the Northeast diagonal line passing through the reference point.

MSG_VIS_RULER_CONSTRAIN_X_TO_NEGATIVE_SLOPE_AXIS

void MSG_VIS_RULER_CONSTRAIN_X_TO_NEGATIVE_SLOPE_AXIS(
        PointDWFixed _far* point);

Use this message to determine the coordinates resulting from horizontally snapping a given point to the Southeast diagonal line passing through the reference point.

MSG_VIS_RULER_CONSTRAIN_Y_TO_NEGATIVE_SLOPE_AXIS

void MSG_VIS_RULER_CONSTRAIN_Y_TO_NEGATIVE_SLOPE_AXIS(
        PointDWFixed _far* point);

Use this message to determine the coordinates resulting from horizontally snapping a given point to the Southeast diagonal line passing through the reference point.

MSG_VIS_RULER_CONSTRAIN_TO_VECTOR

@message void MSG_VIS_RULER_CONSTRAIN_TO_VECTOR(
        PointDWFixed _far* point);

This message computes the point resulting from constraining the passed point using vector constraint. This means that the mouse's constrained location will end up at a certain angle from the reference angle. This angle is determined by means of a point set up my MSG_VIS_RULER_SET_VECTOR . This constraint will constrain the point either to the vector line or to that line's reflection.

MSG_VIS_RULER_CONSTRAIN_X_TO_VECTOR

@message void MSG_VIS_RULER_CONSTRAIN_X_TO_VECTOR(
        PointDWFixed _far* point);

This message computes the point resulting from horizontally constraining the passed point to the line going through the reference point with the present vector slope.

MSG_VIS_RULER_CONSTRAIN_X_TO_VECTOR_REFLECTION

@message void MSG_VIS_RULER_CONSTRAIN_X_TO_VECTOR_REFLECTION(
        PointDWFixed _far* point);

This message computes the point resulting from horizontally constraining the passed point to the line going through the reference point with the negative of the present vector slope.

MSG_VIS_RULER_CONSTRAIN_Y_TO_VECTOR

@message void MSG_VIS_RULER_CONSTRAIN_Y_TO_VECTOR(
        PointDWFixed _far* point);

This message computes the point resulting from vertically constraining the passed point to the line going through the reference point with the present vector slope.

MSG_VIS_RULER_CONSTRAIN_Y_TO_VECTOR_REFLECTION

@message void MSG_VIS_RULER_CONSTRAIN_Y_TO_VECTOR_REFLECTION(
        PointDWFixed _far* point);

This message computes the point resulting from vertically constraining the passed point to the line going through the reference point with the negative of the present vector slope.

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Guide Constraints and Guidelines | Next: 4.6 Esoteric Messages


Esoteric Messages

Ruler Object Library: 4.6 Managing Rulers: Esoteric Messages

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Other Mouse Constraints

For the most part, the ruler will work together with the main view to coordinate updates. This is done by means of messages going to the ruler. Subclasses of VisRulerClass might conceivably intercept these messages to alter behavior.

MSG_VIS_RULER_GAINED_SELECTION

void MSG_VIS_RULER_GAINED_SELECTION(
        optr 		dest);

This message notifies the ruler that the ruled object (the object in the main view) is selected and the ruler should update its UI to reflect its own attributes.

Parameters: dest Object to send messages to whenever a change in ruler settings should result in the ruled object being redrawn (e.g. when the user wants to draw the grid).

Return: Nothing.

MSG_VIS_RULER_LOST_SELECTION

void MSG_VIS_RULER_LOST_SELECTION();

This message notifies the ruler that the ruled object is no longer selected.

MSG_VIS_RULER_UPDATE_CONTROLLERS

void MSG_VIS_RULER_UPDATE_CONTROLLERS();

This message signals that the ruler should update its associated controllers.

MSG_VIS_RULER_UPDATE_TYPE_CONTROLLER

void MSG_VIS_RULER_UPDATE_TYPE_CONTROLLER();

This message signals that the ruler should update its type controller, if any.

MSG_VIS_RULER_UPDATE_GRID_CONTROLLER

void MSG_VIS_RULER_UPDATE_GRID_CONTROLLER();

This message signals that the ruler should update its grid controller, if any.

MSG_VIS_RULER_UPDATE_GUIDE_CONTROLLER

void MSG_VIS_RULER_UPDATE_GUIDE_CONTROLLER();

This message signals that the ruler should update its guide controller, if any.

MSG_VIS_RULER_COMBINE_GUIDE_INFO

void MSG_VIS_RULER_COMBINE_GUIDE_INFO(
        MemHandle data); 		/* Handle of block containing
               VisRulerNotifyGuideChangeBlockHeader*/

This message allows the horizontal and vertical rulers to load their respective guide information into a single structure so that the information can be collated and passed on to a RulerGuideControl.

Structures:

typedef struct {
    LMemBlockHeader 			 VRNGCBH_header;
    word 			VRNGCBH_vertGuideArray;
    word 			VRNGCBH_horizGuideArray;
}  VisRulerNotifyGuideChangeBlockHeader;

MSG_VIS_RULER_VIEW_SCALE_FACTOR_CHANGED

void MSG_VIS_RULER_VIEW_SCALE_FACTOR_CHANGED( @stack
        WindowHandle 		viewWindow,
        WWFixedAsDWord 		yScaleFactor,
        WWFixedAsDWord 		xScaleFactor);

The ruler will receive this message when the main view's scale factor changes. Specifically, when the RulerContent `s scale is changing (which will happen because of the RulerView's link to the main view), its handler for MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED sends this message to the VisRuler.

MSG_VIS_RULER_GET_DESIRED_SIZE

@message word MSG_VIS_RULER_GET_DESIRED_SIZE();

This message returns how much space the ruler thinks it needs to draw the numbers and hatch marks.

Interception: This method should be subclassed if the subclassed ruler's size needs to vary with respect to scale factor, whether the ruler is horizontal or vertical, or whatever size seems appropriate.

MSG_VIS_RULER_INVALIDATE_WITH_SLAVES

void MSG_VIS_RULER_INVALIDATE_WITH_SLAVES();

The ruler's default behavior will result in it redrawing itself at appropriate times. Depending on what use you put your ruler to, you may have to force it to redraw itself. If so, you should probably use this message.

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Other Mouse Constraints


Welcome to GEOS Programming

GEOS SDK Documentation: Programming Topics

| Up: GEOS SDK TechDocs

The following documents cover some of the ins and outs of GEOS programming.

Tutorial
The Tutorial introduces some important programming topics.
GEOS Programming
The GEOS object model, Goc keywords.
Goc Keywords
Alphabetic listing of Goc keywords, with syntax and descriptions.
Localization
GEOS-specific string functions; Goc keywords to make your program more easily localizable.
Swat Introduction
Using Swat, the GEOS debugger.
| Up: GEOS SDK TechDocs


GEOS Programming

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Basic Data Types and Structures
| One-page Version of this Chapter for Easy Printing

Because GEOS implements its own messaging and object system, standard C programming must be supplemented with GEOS-specific programming. This chapter describes the syntax and commands available to GEOS programmers in the Goc preprocessor.

1 Basic Data Types and Structures
1.1 Records and Enumerated Types
1.2 Handles and Pointers
1.3 Fixed Point Structures
2 Goc and C
2.1 Goc File Types
2.2 Conditional Code in Goc
2.3 Macros in Goc
2.4 Using Routine Pointers in Goc
2.5 Esoteric Source-Code Management
3 The GEOS Object System
3.1 GEOS Terminology
3.2 Object Structures
3.3 The GEOS Message System
4 Using Classes and Objects
4.1 Defining a New Class or Subclass
4.2 Non-relocatable Data
4.3 Defining Methods
4.4 Declaring Objects
4.5 Sending Messages
4.6 Managing Objects

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Basic Data Types and Structures
| One-page Version of this Chapter for Easy Printing


Basic Data Types and Structures

GEOS Programming: 1 Basic Data Types and Structures

| Up: GEOS SDK TechDocs | Up | Down | Prev: GEOS Programming | Next: 2 Goc and C

In addition to the standard data types available in C, the Goc preprocessor handles several other types specific to GEOS. These are all defined in the file geos.h . Some of these types, shown below, were carried over from the world of assembly language.

The Boolean type behaves as most Boolean types--any nonzero value represents a true state, and zero represents the false state. Throughout the documentation, true and false are taken to be these meanings. Note that the constants TRUE and FALSE are defined and may be used as return values from your functions and methods. Do not compare Boolean variables, however, against these constants. A Boolean may be true without actually equaling the TRUE value.

Records and Enumerated Types

Handles and Pointers

Fixed Point Structures

| Up: GEOS SDK TechDocs | Up | Down | Prev: GEOS Programming | Next: 2 Goc and C


Records and Enumerated Types

GEOS Programming: 1.1 Basic Data Types and Structures: Records and Enumerated Types

| Up: GEOS SDK TechDocs | Up | Prev: 1 Basic Data Types and Structures | Next: 1.2 Handles and Pointers

GEOS objects and routines make extensive use of flag records and enumerated types. A flag record is a byte, word, or dword in which each bit represents the state (on or off) of a particular attribute or function. An enumerated type is a byte or word in which each enumeration has a unique constant value.

To define a flag record, you should use one of these types and then define the flags to be bits within the record. To set flags, OR them (bitwise OR) with the record; to clear them, AND their bitwise inverses (bitwise AND) with the record. Creating and working with flag records is shown in Flag Records and ByteEnums .

There are two basic enumerated types: The standard enumerated type supported by your C compiler uses word-sized values. GEOS also allows byte-sized enumerated types with the ByteEnum type. Use of this type is shown in Flag Records and ByteEnums .

Code Display 5-1 Flag Records and ByteEnums

/* Define flag records to be the optimized length for the number of flags. For
 * example, the sample type MyFlag has six flags and therefore should be a byte.
 * Flag values should be constants equivalent to having a single bit set in the
 * flag record. */
typedef ByteFlags MyFlag;
#define MF_FIRST_FLAG			0x01
#define MF_SECOND_FLAG			0x02
#define MF_THIRD_FLAG			0x04
#define MF_FOURTH_FLAG			0x08
#define MF_FIFTH_FLAG			0x10
#define MF_SIXTH_FLAG			0x20
/* In a section of code, to set a flag, bitwise OR it with the record. To clear the
 * flag, bitwise AND its inverse with the record. You can set any number of flags
 * at a time as shown in the following examples. */
    ...
    MyFlag		myFlagsRecord;		/* Set up a variable of the flag record type */
    /* Set the second and fourth flag. */
    myFlagsRecord = MF_SECOND_FLAG | MF_FOURTH_FLAG;
    /* Set the first flag and then clear the fifth and sixth flags. */
    myFlagsRecord = (myFlagsRecord | MF_FIRST_FLAG) & ~(MF_FIFTH_FLAG |
                            MF_SIXTH_FLAG);
/* The ByteEnum type can be used instead of the standard enumerated type, which
 * is implemented by most compilers as a word type. To define a ByteEnum, define
 * the type and then a unique constant value for each enumeration as in the
 * following example. */
typedef ByteEnum USCity;
#define USC_HARTFORD			0x00
#define USC_CHARLOTTE			0x01
#define USC_WICHITA			0x02
#define USC_PIERRE			0x03
#define USC_ORLANDO			0x04
| Up: GEOS SDK TechDocs | Up | Prev: 1 Basic Data Types and Structures | Next: 1.2 Handles and Pointers


Handles and Pointers

GEOS Programming: 1.2 Basic Data Types and Structures: Handles and Pointers

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Records and Enumerated Types | Next: 1.3 Fixed Point Structures

Handles and pointers are present everywhere in GEOS--they are the essential elements that make dynamic linking and efficient memory management possible.

GEOS pointers are all far pointers; that is, they are 32-bit addresses that reference specific locations in memory. They are normal C pointers and can be used as such. Two other pointer types are also used by GEOS: Object pointers (optrs) and segment pointers. Object pointers are described below; segment pointers are 16-bit addresses described in the Memory Management chapter.

Handles

Handles are 16-bit, unsigned values used for several purposes. They provide abstraction when the exact address of a data structure or other item is not known or is an inconsistent state. The kernel maintains a handle table to keep track of many of the handles in the system. Each entry in the handle table is 16 bytes that contains information about the item referenced by the handle; these 16 bytes are opaque to applications and libraries and can not be accessed or altered except by the kernel. Other handle types are managed in other tables by the kernel.

Handles are used for the following primary purposes. For a full description of how handles are used, see the Handles chapter.

  • Memory reference
    Entries for memory handles contain pointers to memory blocks; when the blocks move, the pointers are updated. However, the handle's integrity is preserved, and applications keep track of just the handle value.
  • Virtual Memory reference
    Entries for VM handles indirectly reference VM blocks similar to the way memory handles reference blocks of memory. VM handles may be used whether the VM block is resident in memory or not.
  • File and disk reference
    Entries for file and disk handles contain information about the location and status of the referenced file/disk. They provide indirect access to files and disks in a manner similar to memory handles.
  • Data structure implementation
    Certain frequently-used system data structures require a storage format that provides for quick and convenient access at all times. These data structures (for example, threads, event queues, messages, timers, and semaphores) are stored in Handle Table entries.
  • Optimization
    The kernel will, if space permits, sometimes use Handle Table entries for optimized temporary storage. (For example, when passing parameters on the stack with messages, the kernel will occasionally use handles for storing the parameters.)

The NullHandle value (zero) is used to indicate a null handle.

There are over a dozen different types of handles that can be used by any sort of geode. These are listed in the GEOS Programming chapter. All are 16-bit unsigned integers.

Chunk Handles and Object Pointers

Objects and small data structures are stored in small memory pieces called chunks. Chunks are stored in memory blocks known as local memory heaps, and each local memory heap can contain several chunks. Each chunk is referenced by a combination of two handles: The MemHandle handle locates the local memory heap, and the ChunkHandle locates the chunk within the block. A null chunk handle value is specified by NullChunk .

Objects are referenced in the same way as chunks, but the handle and chunk handle are combined into a single structure called an Object Pointer, or optr. Each optr uniquely identifies a particular object in the system. Note that optrs are often used to reference non-object chunks and data structures. A null value is specified by NullOptr .

GEOS provides several macros, all defined in geos.h , for creating and parsing optrs.

  • ConstructOptr()
     This macro constructs an optr from a MemHandle and a ChunkHandle.
  • OptrToHandle()
     This macro extracts the MemHandle portion of the given optr.
  • OptrToChunk()
     This macro extracts the chunk handle portion of a given optr.

Pointers

Pointers can be used normally as in C. All Goc-generated pointers are far pointers; that is, they are 32-bits long, composed of a 16-bit segment and a 16-bit offset.

GEOS provides macros for extracting the segment and handle portions of pointers.

  • PtrToSegment()
     This macro returns the segment portion of the given pointer (returned as type "word").
  • PtrToOffset()
     This macro returns the offset portion of the given pointer (returned as type "word").

GEOS automatically loads code resources when needed. However, when you call routines through pointers, you must take special measures to see to it that the routine is properly loaded into memory. This is discussed below in Using Routine Pointers in Goc .

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Records and Enumerated Types | Next: 1.3 Fixed Point Structures


Fixed Point Structures

GEOS Programming: 1.3 Basic Data Types and Structures: Fixed Point Structures

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Handles and Pointers | Next: 2 Goc and C

When you want to represent non-integral numbers (i.e., real numbers), you can use either the standard C floating-point format or the following special structures of GEOS for fixed point math. Note that fixed-point calculations are faster than the corresponding floating-point math, so if you want to optimize your code, you should use the GEOS fixed-point structures with the GEOS math routines.

Code Display 5-2 GEOS Data Structures

 /* Fixed-Point Structures
 *    The following structures are used to represent fixed-point numbers:
 *    numbers with a fractional portion and an integral portion. Notice that
 *    there are several formats of fixed-point numbers; each uses a different
 *    number of bits for the parts of the number. Choose whichever is most
 *    appropriate (for optimization). */
     /* BBFixed
     * One byte integer, one byte fraction */
typedef struct {
    byte		BBF_frac;		/* fractional portion */
    byte		BBF_int;		/* integral portion */
} BBFixed;
    /* BBFixedAsWord
     * Sometimes it is convenient to refer to a BBFixed value as type word.
     * The BBFixedAsWord type is used for this purpose. */
 typedef word		BBFixedAsWord;
    /* WBFixed
     * One word integer, one byte fraction */
 typedef struct {
    byte		WBF_frac;		/* fractional portion */
    word		WBF_int;		/* integral portion */
} WBFixed;
    /* WWFixed
     * One word integer, one word fraction */
 typedef struct {
    word		WWF_frac;		/* fractional portion */
    word		WWF_int;		/* integral portion */
} WWFixed;
    /* WWFixedAsDWord
     * Sometimes it is convenient to refer to a WWFixed value as type dword.
     * The WWFixedAsDWord type is used for this purpose. */
 typedef dword		WWFixedAsDWord;
    /* DWFixed
     * two words (one dword) integer, one word fraction */
typedef struct {
    word		DWF_frac;		/* fractional portion */
    sdword		DWF_int;		/* integral portion */
 } DWFixed;
/* Three-byte structure
 * The WordAndAHalf structure is used when you need a 24-bit value and you want to
 * optimize and avoid using a 32-bit value. */
typedef struct {
    word		WAAH_low;		/* the low 16 bits */
    byte		WAAH_high;		/* the high 8 bits */
} WordAndAHalf;

Three special macros are also available to work with the WWFixed type. These are listed below:

  • MakeWWFixed
     This macro creates a WWFixed structure from a given floating-point number or dword number.
  • WWFixedToFrac
     This macro produces the fractional portion of a WWFixed structure.
  • WWFixedToInt
     This macro produces the integral portion of a WWFixed structure.

Two other macros are provided for use with WWFixedAsDword structures:

  • IntegerOf()
     This macro returns the integral portion of a WWFixedAsDword structure.
  • FractionOf()
     This macro returns the fractional portion of a WWFixedAsDword structure.
| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Handles and Pointers | Next: 2 Goc and C


Goc and C

GEOS Programming: 2 Goc and C

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Basic Data Types and Structures | Next: 3 The GEOS Object System

Goc is a superset of the standard C programming language. Goc actually acts as a sort of preprocessor before the code is run through a standard C compiler. There are several differences you must be aware of, though. These differences are covered in the following sections as well as throughout the documentation.

Goc File Types

Conditional Code in Goc

Macros in Goc

Using Routine Pointers in Goc

Esoteric Source-Code Management

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Basic Data Types and Structures | Next: 3 The GEOS Object System


Goc File Types

GEOS Programming: 2.1 Goc and C: Goc File Types

| Up: GEOS SDK TechDocs | Up | Prev: 2 Goc and C | Next: 2.2 Conditional Code in Goc
@include, @optimize

When programming in Goc, you will use several different types of code files. Files ending in .goh are the Goc equivalent of C .h files--they contain routine headers, constants, and other included data and structures necessary for the program. Files ending in .goc are the Goc equivalent of C .c files--they contain code and data for the program. You should keep any Goc-specific code or header information strictly in the Goc files, and standard C code should be kept in C files. C code can also be put in .goc and .goh files, but for consistency, you should try to keep it separate.

Not all of your program's header files need be .goh files--if the header file contains only C constructions (structures, routine definitions, and so on), then you may leave it as a standard C .h file, included by means of the #include directive.

The rule of thumb is that if a header file contains any Goc code or includes a .goh file, then it must be a .goh file. Note also that .goh files are allowed to contain simple standard C code; if you are not sure, then, you can make all your header files .goh .

Standard C programs use the #include directive to include header ( .h ) files. When using Goc, you can use this directive in standard .c and .h files; when including .goh files in .goc files, though, you have to use the @include directive, which is Goc-specific. It has the same syntax as #include .

An example of using @include comes from the Hello World application, which includes the stdapp.goh file. (All GEOS applications will need to include this file to compile properly; it must be @include d before any standard C headers are #include d.) The line from Hello World is

@include <stdapp.goh>

The syntax of this directive, as stated above, is the same as for the C directive #include . One exception is that the @include directive will include a file just once, even if it is included by an included file--there is no need to conditionally include a file (checking first to make sure it hasn't already been included).

If you will be including a Goc file in many different applications, or if it is very long and elaborate, it is a good idea to put the keyword @optimize at the top of the file. This instructs the Goc preprocessor to generate a special stripped-down version of the file with a .poh suffix. The compiler will then automatically keep the .poh file up to date, and use it in compilations instead of the .goh file. The .poh file contains all the data of the .goh file, but is somewhat faster to compile into an application; thus, by using the @optimize keyword, you incur a longer compilation whenever you make a change to the .goh file, but a shorter compilation time when the .goh file is unchanged since the last compilation. You may choose to leave the @optimize directive out while the header is being developed, then put it in when the header is fairly stable.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Goc and C | Next: 2.2 Conditional Code in Goc


Conditional Code in Goc

GEOS Programming: 2.2 Goc and C: Conditional Code in Goc

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Goc File Types | Next: 2.3 Macros in Goc
@if, @ifdef, @ifndef, @endif

Many C programs use the directives #if , #ifdef , #ifndef , and #endif to define conditional code--code that should be compiled into the program only if certain conditions are met. When working with standard C code in your GEOS applications, you should still use these directives; when working with Goc code, however (in .goh and .goc files), you should use the Goc directives @if , @ifdef , @ifndef , and @endif .

Goc conditionals are more limited than C conditionals. Conditional expressions may be based on numbers, names of macros, and the Boolean operators OR ( || ) and AND ( && ). Some examples of Goc conditional expressions are shown below:

@ifdef (MyMacro)
    /* code compiled if MyMacro is defined */
@endif
@if 0
    /* code that will not be compiled at all */
@endif
@if defined(MyMacro) || MY_CONSTANT
    /* code compiled if either MyMacro is
     * defined or MY_CONSTANT is not zero */
@endif
@ifndef 0
    /* code always compiled */
@endif
| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Goc File Types | Next: 2.3 Macros in Goc


Macros in Goc

GEOS Programming: 2.3 Goc and C: Macros in Goc

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Conditional Code in Goc | Next: 2.4 Using Routine Pointers in Goc
@define

The C programming language allows definition and use of macros, and most programmers use macros extensively. You can use the #define directive in standard C code in your GEOS programs to define macros that use only standard C code.

Similarly, you can use the @define Goc directive to create macros in Goc. (Macros must be defined with @define ; otherwise, the Goc processor will skip the #define directive and process the macro as if it were standard code to be processed normally.)

Macros in Goc have a somewhat different syntax than standard C macros though they are very similar. Some examples of simple Goc macros follow below:

@define mlply(val1,val2)					val1 * val2
@define defChunk(a)				@chunk char a[] = "text"

When using Goc macros in your code, you must preface them with the " @ " Goc marker, indicating to the processor that it is a macro. If you do not preface the macro with " @ ", then Goc will pass over it and will not process it, leaving it up to the C compiler--which will likely give an error. For example, using the second macro defined above (defChunk), you could create a number of chunks easily:

@defChunk(firstText)
@defChunk(secondText)
@defChunk(thirdText)

The above would equate to the following:

@chunk char[] firstText = "text";
@chunk char[] secondText = "text";
@chunk char[] thirdText = "text";

Using "defChunk" without the " @ " marker would most likely result in a compilation error in the C compiler.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Conditional Code in Goc | Next: 2.4 Using Routine Pointers in Goc


Using Routine Pointers in Goc

GEOS Programming: 2.4 Goc and C: Using Routine Pointers in Goc

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Macros in Goc | Next: 2.5 Esoteric Source-Code Management
ProcCallFixedOrMovable_cdecl(), ProcCallFixedOrMovable_pascal()

Most GEOS code is kept in movable resources. If you call a routine explicitly from source code the Goc preprocessor generates appropriate directives to see to it that the resource is loaded into memory when it is called. However, if you call a routine with a routine-pointer, GEOS cannot take these precautions. Accordingly, when you are calling a routine with a pointer, you must either see to it that the resource is loaded, or use one of the two ProcCallFixedOrMovable routines to instruct the kernel to lock the appropriate resource.

If you know the routine is in a resource which is locked or fixed in memory, you can use the routine pointer exactly the way you would in standard C. This is usually because the calling routine is in the same resource as the routine or routines which may be called.

If you are not sure that the resource is loaded, you should call the routine with either ProcCallFixedOrMovable_cdecl() or ProcCallFixedOrMovable_pascal() . Each of these routines is passed the following arguments:

  • A pointer to the routine to be called
  • All the arguments passed to the routine, in exactly the order which the routine expects.

Both routines return whatever the called routine returns.

If the routine to be called was defined with standard C calling conventions (the default), you should use ProcCallFixedOrMovable_cdecl() . If the routine was declared with the keyword _pascal , it uses Pascal's calling conventions; you must then use the routine ProcCallFixedOrMovable_pascal() . Most kernel and system-library routines are declared with Pascal's calling conventions.

Code Display 5-3 Using ProcCallFixedOrMovable_cdecl()

extern int
SomeRoutineCalledViaAPointer(int anArg, int anotherArg, const char *someText);
int (*funcPtr) (int, int, const char *);					/* A function pointer */
funcPtr = SomeRoutineCalledViaAPointer;
/* We want to do
 *	SomeRoutineCalledViaAPointer(1, 2, "Franklin T. Poomm");
 * but we want to call it through the pointer, even though it's in another
 * resource:
 */
ProcCallFixedOrMovable_cdecl(funcPtr,					/* The pointer to the routine */
                1, 2, "Franklin T. Poomm");
| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Macros in Goc | Next: 2.5 Esoteric Source-Code Management


Esoteric Source-Code Management

GEOS Programming: 2.5 Goc and C: Esoteric Source-Code Management

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Using Routine Pointers in Goc | Next: 3 The GEOS Object System
@optimize, @protominor

There is a small number of Goc keywords which are used only to ease the burden of maintaining header files which will be used and shared often.

The @optimize keyword is used to speed compilation of Goc header files. It may be placed at the top of a .goh file. The directive instructs Goc to generate a specially processed .poh file which contains all the information of the .goh file, but is somewhat faster to compile. This .poh file is automatically regenerated if the corresponding .goh file has been changed since the last compilation. Its syntax is simple:

@optimize

The @protominor keyword is used so that a single Goc header file may serve multiple versions of a Goc library. When creating a new version of an existing library, use the @protominor keyword to declare new messages and variable data fields for a class. Suppose your original class declaration looked like so:

@class MyClass, SuperClass;
    @message void MSG_M_DO_THIS(void);
    @vardata void TEMP_M_DONE_FLAG;
@endc

Having released this version of your class, you wished to release another version in which this class handled another message. You wanted to specify that this new message would only work with this new version of the library. This would be set up like so:

@class MyClass, SuperClass;
    @message void MSG_M_DO_THIS(void);
    @vardata void TEMP_M_DONE_FLAG;

    @protominor MyVersion20
    @message void MSG_M_DO_THAT(void);
@endc

This keyword has the syntax:

@protominor <name>
name
Name of the minor protocol. This name should correspond to an incminor entry in the geode's .gp file.

To do the equivalent version control with routines, use the incminor .gp file directive.

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Using Routine Pointers in Goc | Next: 3 The GEOS Object System


The GEOS Object System

GEOS Programming: 3 The GEOS Object System

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Goc and C | Next: 4 Using Classes and Objects

GEOS is almost entirely object-oriented. Its object system supports true object-oriented principles such as encapsulation, inheritance, and message dispatching through the kernel.

The following section describes the class and object structures of GEOS, how to declare and define classes and objects, and how the messaging system and the kernel's message dispatcher work.

GEOS Terminology

Object Structures

The GEOS Message System

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Goc and C | Next: 4 Using Classes and Objects


GEOS Terminology

GEOS Programming: 3.1 The GEOS Object System: GEOS Terminology

| Up: GEOS SDK TechDocs | Up | Prev: 3 The GEOS Object System | Next: 3.2 Object Structures

Though you should be familiar with general object-oriented programming terms, there are quite a few for which the meaning is slightly different in GEOS, and there are others which are entirely new to GEOS. This section is divided into four categories: General Terms, Class Terms, Object Terms, and Messaging Terms.

General Terms

chunk
A chunk is a small section of memory located in a Local Memory Heap. Object instance data is stored in a chunk, one chunk per object. Local Memory and chunks are described fully in the Local Memory chapter.

fptr An fptr is a "far pointer"--a 32-bit pointer to a specific location of memory. It is a standard C pointer.

handle A handle is a 16-bit index into a Handle Table and is used to reference memory blocks. For more information, see the Handles chapter.

object block
 An object block is a specific type of Local Memory block that contains object chunks.

optr An optr is a unique identifier for an object and is therefore also referred to as an "Object Pointer." An optr is used to locate and access any object in the system, and because it is made up of handles and chunk handles, it will always stay the same even when the object moves in memory.
thread
A thread is a single executable entity that runs either procedural code or one or more objects. If a thread is "event-driven," it executes code for a given set of objects, receiving messages and dispatching them to the proper objects.

Class Terms

class
A class is the definition of a set of instance data structures and the methods that work on those structures. An object is called an "instance" of its class.
class tree
A class tree represents the hierarchy of inheritance from superclass to subclass. If a message is not handled by a given class, it will be automatically passed up the class tree until it is handled or the root of the tree is reached, after which the message is discarded.
inheritance
Inheritance is the term given to the way an object of a particular class has all the same instance variables and methods as an instance of the object's superclasses.
initialize
Initialization of an object is when a master part (or master group) of the object's instance data is filled out. This occurs whenever a class in the master group not yet initialized in the object receives its first message.
master
The term "master" is used in several cases, all related. A master class is generally considered the top class in a single class tree. Although the master class may have superclasses, it provides a conceptual break and creation of a new subtree.
master group
The section of an object's instance data belonging to a particular master class and all its subclasses is called a master group. A master group is initialized when the master class (or one of its subclasses) receives its first message.
resolve
Resolution of a variant class occurs when the variant's superclass is determined. Each instance of a variant class must be resolved individually.
subclass
The term "subclass" is used to show relationships between classes. A subclass is defined on another class, from which it inherits instance data and methods. This other class is known as a "superclass," below.
superclass
The term "superclass" is used to show relationships between classes. A superclass passes on its instance data and methods to all classes defined as subclasses of it.
variant
A variant class may have different superclasses. However, an instance of a variant class may have only one superclass at any given moment. The use of variant classes can provide much the same functionality as the multiple inheritance found in some other object systems.

Object Terms

child
A child object is one that sits below another object in an object tree. The terms child, parent, and sibling are used only to show relationships between objects.
composite
A composite object is one that can have children. The composite has a "composite link" (an optr) to its first child and a "sibling link" to its next sibling. If it has no next sibling, the sibling link instead points to the object's parent object.
instance
An instance is a particular manifestation of a class. This term is almost always interchangeable with "object," though sometimes it specifically refers to the chunk containing the object's instance data rather than to the object as a whole.
link
A link is typically an optr pointing to an object's next sibling in an object tree. It is also used more generally to refer to any optr linking two objects in an object tree (parent and child, or last sibling and parent).
object
An object is a specific manifestation of a class. Typically, this term is interchangeable with "instance"; however, sometimes the term "object" refers to the combination of an object's methods and instance data whereas the term "instance" refers to just the object's instance data chunk.
object tree
An object tree is a means of organizing objects into a hierarchy for display or organizational purposes. Do not confuse it with the "class tree," the structure which represents class relationships. An object tree is made up of composite objects, each of which may have children or be the child of another object. The topmost object in the tree is called the "root," and the bottommost objects are called the "leaves" or "nodes." Non-composite objects may be placed in the tree as leaves but may not have children.
parent
A parent object is one that has children in an object tree. The parent contains a composite link (an optr) to its first child and is pointed to by its last child.
state file
A state file is a Virtual Memory file used to store the state of objects. Typically, object blocks will be written to or extracted from the state file. Generic UI objects have this functionality built in automatically; other objects may manage their own state saving by managing the state file.

Messaging Terms

blocking
A thread "blocks" when it must wait for resources or return values from messages sent to objects in another thread. Specifically, a thread blocks when one of its objects sends a message to another thread with the "call" command; if the "send" command is used, the thread will continue executing normally.
call
A message sent with the call command causes the calling thread to block until the message is handled by the recipient. If the recipient is in the calling thread, the code will be executed immediately.
dispatcher
The GEOS dispatcher is internal to the kernel and passes messages on to their proper recipients. The dispatcher will dynamically locate the proper object and method and will invoke the method.
message
A message is a directive, query, or other instruction sent from one object to another. Messages may take parameters and may return information.
method
A method, also called a "message handler," is the code invoked by a message. A method may do anything a normal function or procedure may do, including alter instance data. It is poor style and highly discouraged for one object's method to alter another object's instance data directly.
send
A message sent with the send command will be placed in the recipient's event queue and will not cause the sender to block. Messages that return information or pass pointers should never be dispatched with the send command; use the call command in those cases.
| Up: GEOS SDK TechDocs | Up | Prev: 3 The GEOS Object System | Next: 3.2 Object Structures


Object Structures

GEOS Programming: 3.2 The GEOS Object System: Object Structures

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 GEOS Terminology | Next: 3.3 The GEOS Message System

You do not need to know what data structures are used to store objects and classes; understanding them can make programming GEOS much easier, however.

Each object is implemented in two parts: the instance data chunk and the class definition. Although both are integral parts of the object and they are interconnected, they are stored in different places.

An object's instance data is stored in an instance chunk. This instance chunk is sometimes referred to as the object itself, but this isn't quite accurate--the instance chunk contains only the object's data along with a pointer to its class structure. The structure of the instance chunk is given in Instance Chunk Structures .

An object's class structure contains all the code for the class. Since the class code may be accessed by many objects, the class definition resides in a geode's fixed memory resource. Every class (except the root, MetaClass ) has a pointer to its superclass so it can inherit that class' methods and structures.

All objects of a given class use the same code--the class' code--for their functions. They dynamically access this code so the code blocks need to be in memory only once, no matter how many objects are actively using them. Additionally, each class dynamically accesses its superclass' code, so any class may be accessed by all the objects of the subclasses as well. Class structures are shown in Class Structure and Class Trees .

Instance Chunk Structures

Each object's instance data is stored in a Local Memory chunk. Several chunks are stored in one memory block, called a local memory heap. (See the Local Memory chapter.) This local memory heap, containing objects, is known as an object block .

Each object block has a special header type that distinguishes it from normal local memory heaps. After the header in the block is the chunk handle table: a table containing offsets to each object in the block. Following the chunk handle table are the objects.

Each object is referenced by an object pointer, or optr. The optr contains two items: the global memory handle of the object block and the chunk handle of the object. Note that because the optr is made up of handles, an object must be locked before its instance data can be accessed. The GEOS kernel takes care of this automatically when executing methods.

Only the object's instance data is stored in the chunk pointed to by the optr; the method table and code used by the object are stored in the class' data structures, not the object's. To reconcile this separation of code and data, every object's first four bytes of instance data are a pointer to the object's class definition. This pointer is traversed by the kernel automatically and should not be altered or accessed by applications.

Included in an object's instance chunk are certain fields generated and filled by either Goc or the kernel. Following these fields is the object's instance data, grouped by master part. It's unlikely you'll ever have to know the actual structures used in the instance chunk because the kernel automatically calculates the proper offsets to individual instance data fields. However, understanding the underlying structures may help in understanding how the object system of GEOS works.

Instance data within an instance chunk is stored in "master parts" or "master groups." A master group is simply a number of instance data fields grouped according to their appropriate master class levels. Master classes are detailed in Master Classes .

A class designated as a master class resembles a normal class in all respects save one: it determines how instance data is grouped in a chunk. Each master class is the head of a class subtree; all the classes below it in the class tree (down to the next master class) are considered to be in that master class' group. Instance data for all classes in the master group are lumped together in the instance chunk; each master group's instance data within the chunk is accessed via a special offset stored within the chunk.

The first four bytes of an object's chunk contain a pointer to the object's class structure. The class structure (described in Class Structure and Class Trees ) resides in fixed memory. (A variant-class object has a slightly different structure; this is detailed in Variant Classes .)

An object that has no master classes in its class ancestry (unusual) has its instance data directly following its class pointer. Objects belonging to master classes or their subclasses, however, are somewhat more complex. This distinction can be mostly ignored by application and library programmers (with the exception of deciding which classes should be master classes and which should not).

Each master part of the chunk is located by an offset inserted directly after the object's class pointer in the chunk. The position of the word containing this offset is then stored in the master class structure so the class can find its instance data later. The combination of the class pointer and the various master offsets make up the object's "base structure." When a typical object is instantiated, the base structure is all that is created.

Each master part is left unallocated (for efficiency) until it is first accessed via a message sent to a class in the master group. When a class in the master group receives its first message, the entire master part of the chunk is allocated and initialized. This means that an object's chunk remains as small as possible until it absolutely must grow larger. Some classes even detect when a master part of the object will no longer be needed and actually remove (shrink to zero) the unwanted instance data from the chunk ( GenClass does this by shrinking the Vis master part to zero size when a gadget is taken off the screen).

Any object may have "variable data" instance data fields; these are fields that may be added or removed dynamically to keep from having unused space in the instance chunk. Generic UI hints are "variable data" (also called vardata )--if an object has the hint, it appears in its instance chunk, if the object does not have the hint, the chunk does not have unused space in it.

Vardata entries are stored all together at the end of the instance chunk, regardless of their master groups. An object with two master groups and three variable data fields, for example, would look like that shown in the figure below. Variable data and its use are discussed in full in Defining and Working With Variable Data Fields .

Master Classes

A master class provides a conceptual break between levels within a class tree. Each master class is the head of a class subtree, and all its subclasses are considered to be in its "master group."

The purpose of making a class a master class is to separate its instance data from that of its superclass. Each master group's instance data is lumped together in one section of the object's instance chunk and is not initialized until a class in the master group is accessed. The initialization (allocation of extra memory within the instance chunk) occurs automatically.

As shown in the figures below, an object of RookClass would have an instance chunk with two master groups, one for the PieceClass master class and one for the GamePcClass master class. The first of the two master parts represents the instance data for PieceClass only; the second master part represents the object's instance data for all of GamePcClass , ChessClass , and RookClass .

The functionality of master classes is required to implement GEOS variant classes (see Variant Classes ). A variant class allows a single class to have a version of "multiple inheritance" in that it can have different superclasses depending on the system context.

Class Structure and Class Trees

For the most part, you won't ever need or want to know the internal structure of a class as implemented in memory. The class structure is created and partially filled by the Goc preprocessor and Glue linker; the remainder is filled by the kernel when the class is loaded. It's unlikely you will need to know the actual class structures; you won't ever have to manually build a class unless your program dynamically creates it (not a common procedure for typical applications).

This section will describe how the class is implemented and how class trees are structured and managed. However, it will not discuss creating new classes during execution.

Classes are implemented with special data structures and code blocks. Each class is defined within and exported by a particular geode; when the geode is loaded the class definition and its code are loaded into the geode's fixed memory. All references to the class are then relocated by the kernel into pointers. For example, if a class is defined by a library, that library's "core block" (the special information kept about it by the kernel) contains an absolute pointer to the class' definition in a fixed memory resource owned by the library. Any applications then using that class load the library. The kernel examines the library's core block for the proper pointer and uses it each time the application references that class.

Because of this, each class is loaded into memory just once; all objects that use the class use the same class structure and code. Each object has a pointer in its instance chunk directly to the class structure; each class contains a pointer to its superclass' class structure. Using these pointers, the kernel can travel up an object's class tree to access any appropriate code.

A class is a combination of data structure and code. The data structure ( ClassStruct ) contains information about the class, its superclass, its methods, and the structure and size of its instance data. The code consists of methods (message handlers). A diagram of the data structure is given below.

Class_superClass

Every class has as its first four bytes a pointer to its superclass. This points to the superclass' ClassStruct structure in all cases except two: The root of any class tree has a null superclass pointer, indicating that the root has no superclass. Variant classes have the integer 1 (one) always, indicating that the superclass is determined in a special manner. For more information on variant classes, see Variant Classes .

Class trees are constructed when classes are defined; a new class is created as the subclass of some existing class, and its Class_superClass pointer is automatically set to point to the superclass. There is no need to point down the tree; messages are always passed to superclasses and never to subclasses.

Class_masterOffset

Class_masterOffset stores the offset indicating how far into the instance chunk the object's offset to this class' master part is. Note that use of this offset is entirely internal; individual classes do not have to figure out where their instance data is within the chunk (they may, however, have to know what master level each class is).

The master offset is used primarily because an object can have some of its master parts initialized and others uninitialized. If only one master part of the object had been initialized, the location of the instance data in the chunk may be different than if all master parts had been initialized.

Class_methodCount

Class_methodCount stores the total number of methods referenced in the class' method table. This is the total number of methods defined for this class only; other methods defined in other classes (even in the same master group) are stored in the method tables of those classes.

Class_instanceSize

Class_instanceSize holds the number of bytes to be allocated whenever an object of this class is instantiated. If the class is a master class or a subclass of a master class, this is the size of the master part. If the class has no master class above it, this is the number of bytes to allocate for the entire object (including superclass pointer).

Class_vdRelocTable

Class_vdRelocTable is a near pointer (16 bit offset) to the variable-data relocation information. The relocation information contains the type of relocation to be done for each data type. There is one entry in the variable data relocation table for each relocatable field in each particular variable-data type. Variable data (also called vardata ) is described in full in Defining and Working With Variable Data Fields .

Class_relocTable

Class_relocTable is a near pointer (16 bit offset) to the relocation information for the non-variable data instance fields of the class. The relocation information contains the type of relocation done for each relocatable instance field (other than variable-data entries). A relocatable instance field is one which must be updated when the object is loaded--pointers, offsets, etc. The entry in the relocation table is defined with the @reloc keyword, described on @reloc <iname>, [(<count>, <struct>)] <ptrType>; .

Class_flags

Class_flags contains seven flags (shown below) that determine the characteristics of the class. Declarers for these flags are used in the @classdecl declaration (see Defining a New Class or Subclass ).

CLASSF_HAS_DEFAULT
This flag indicates that the class has a special default method to handle unrecognized messages (typically, this handler simply passes the unrecognized message on to the superclass). This flag is not implemented in C. This flag is set by declaring the class as hasDefault .
CLASSF_MASTER_CLASS
This flag is set if the class is a master class. This flag is set by declaring the class as master .
CLASSF_VARIANT_CLASS
This flag is set if the class is a variant class. This flag is set by declaring the class as variant (all variants must also be declared as masters).
CLASSF_DISCARD_ON_SAVE
This flag indicates the instance data for the class can be discarded when the object is saved. This flag applies only to master classes and will be heeded only when the master group is immediately above a variant-master group. This flag is set by declaring the class discardOnSave .
CLASSF_NEVER_SAVED
This flag indicates objects of this class are never saved to a state file or loaded in from a resource. Goc will not build a relocation table for a class with this flag set. This flag is set by declaring the class neverSaved .
CLASSF_HAS_RELOC
This flag indicates that the class has a special routine for relocating and unrelocating objects of the class when they are loaded from or written to disk. This flag is set by declaring the class hasReloc .
CLASSF_C_HANDLERS
This flag indicates the class' methods are written in C rather than in assembly. This flag is set by the compiler and should not be set by applications.

Class_masterMessages

Class_masterMessages contains a number of flags set by Goc indicating whether this class has methods for messages defined within a given master level. It is used to optimize the search for a method to handle a message. These flags are internal and should not be used by programmers.

The Method Table

Every class has a method table, a table that indicates the location of the code to be executed when a certain message is received. The method table is in three parts and begins at the byte labelled Class_methodTable (this is simply a label, not a specific data field).

The first part of the method table is a list of messages the class can handle. Each entry in this list is two bytes and contains the message number of a message handled by the class.

The second part of the method table is a list of pointers to methods. Each entry in this list is a pointer (four bytes) which points to a specific routine in a code block. If the code is in a fixed block, the pointer will be a far pointer; if the code is in a moveable or discardable block, the pointer will be a far pointer containing a virtual segment . (A virtual segment, something you do not need to know about, is a handle shifted right four bits with the top four bits set. Since this represents an illegal segment address, GEOS recognizes it as a virtual segment and will take the necessary actions to lock the block into memory before access and unlock it after access. Manipulation of the bits in the virtual segment is completely internal to GEOS.)

The kernel searches the message list until it comes across the appropriate message number and notes the message's position in the table. It then looks at the corresponding position in the pointer list. If the pointer there is a virtual segment and offset, it will load the appropriate code block, lock it, and execute the code. If the pointer points to fixed memory, the code will be executed immediately. (If the message number is not found in the table, the kernel will either execute the class' default handler or pass the message on to the class' superclass.)

Variant Classes

A variant class is one which has no set superclass. The variant's superclass is determined at run-time based on context and other criteria. Note that objects may not be variant--only classes may be variant. An object always has a specific class to which it belongs, and its class pointer always points to that class' ClassStruct structure. In addition, every variant class must also be a master class.

A variant class, however, may have different superclasses at different times. This functionality provides a form of "multiple inheritance": the class may inherit the instance data and functions of different classes depending on its attributes and desired features. Note, however, that a variant class may have only one superclass at any given moment.

The most visible example of a variant class is GenClass and how a generic object is resolved into its specific UI's appropriate representation. Each generic object (for example, a GenTrigger), is a subclass of the master class GenClass . All the instance data belonging to GenTriggerClass and GenClass , therefore, is stored in the Gen master part of the instance chunk.

GenClass , however, is a variant class, meaning that it does not know its superclass when the object is instantiated. Each generic object's class will be linked directly to another class provided by the specific UI in use: the specific UI's class provides the visual representation while the generic UI class provides the object's functionality. In this way, the object can actually perform many of its generic functions without having a visual representation.

The resolution of the superclass comes when the generic object is displayed on the screen: the kernel sees that the object has no superclass and looks into its instance data and class structure. The kernel then determines what the appropriate specific UI class will be for the object's class and provides the superclass link necessary. It also then initializes the superclass' master part of the object (in this case, the master part belonging to VisClass ), updating all the master part offsets in the instance chunk's master offset fields.

You can see from the above discussion that GenClass must know at least something about its potential superclasses. In fact, all variant classes must know at least the topmost class of all its potential superclasses. The definition of GenClass is

@class GenClass, VisClass, master, variant;

The @class keyword declares the new class, GenClass . GenClass is to be a variant class and therefore must also be a master class. All the superclasses of GenClass will be related to VisClass ; this means that all specific UI classes which may act as Gen's superclass must be subclassed from VisClass . (Another way of looking at the definition is that GenClass is an eventual subclass of VisClass --you have no way of knowing beforehand how many class layers may be between the two, however.)

The variant must specify an eventual superclass so the kernel knows how many master offset fields it must allocate when an instance of the variant is created. For example, a GenTrigger has two master groups: that of GenClass , and that of VisClass . Because the GenClass master group is necessarily below the VisClass master group in the class hierarchy (after the superclass link has been resolved), the GenClass master offset in the instance chunk must be after the VisClass master offset. If the definition did not specify VisClass as an eventual superclass, no master offset field would be allocated for it, and the Class_masterOffset field of GenClass ' Class structure would not be able to hold any particular value.

As stated at the beginning of this section, there are no "variant objects." Every object belongs to a specific class, and the object's class can never change. All instances of a variant class, however, can be resolved to different superclasses due to the way the superclass of each variant is resolved. One example of this is the generic-to-specific mapping of the GenInteraction object.

All GenInteractions are of class GenInteractionClass ; this never changes. GenInteractionClass , however, is a subclass of GenClass , a variant class. This means that the class tree of the GenInteraction object is only partially completed; before the GenInteraction is resolved, it looks like the simplified diagram below.

The GenInteraction object may be resolved into one of several different specific UI classes. For example, the motif.geo library has several classes for GenInteractions; among them are the classes that implement menus, dialog boxes, and grouping interactions. These classes are all specialized subclasses of VisClass , the eventual superclass of GenClass .

The class tree of the GenInteraction is not complete. A class tree must have links all the way back to MetaClass for it to be complete; this only goes to GenClass . GenClass has a special value in its Class_superClass field, 0001h:0000h. This represents a reserved "pointer" that indicates to the kernel that the class is a master class.

The superclass of the variant can be different for every instance because the superclass pointer is actually stored in the object's instance chunk rather than in the class' ClassStruct structure. This also allows a class tree to have more than one variant class in its hierarchy; for example, one variant could be resolved to be the subclass of another variant. The tree must always be headed by MetaClass .

The superclass pointer for the variant is stored in the variant's master group instance data. Not all master groups have superclass pointers; only those for variant classes. After the GenInteraction is resolved, the pointer (the first four bytes of the Gen master part) points to the proper superclass for this object (in this case, OLMenuWinClass ). The object, with its full class tree, is shown in the figure above.

An In-Depth Example

This section gives an example of a GenTrigger object after its variant part has been resolved. This example provides in-depth diagrams of the class and instance structures for those programmers who wish to understand them. There is no need to know them, however; you will not likely ever need to access the internals of either a class structure or an instance structure.

The GenTrigger taken as an example is the "New Game" trigger of the TicTac sample application. This trigger is the only child of the Game menu GenInteraction; it is shown below. The code defining the trigger is given in TicTac's New Game Trigger .

Code Display 5-4 TicTac's New Game Trigger

/* The TicTacNewTrigger has a moniker and an output. All its other instance data
 * is set to the GenClass defaults. The content of these fields is not important
 * for this example, however. */
@object GenTriggerClass TicTacNewTrigger = {
    GI_visMoniker = "New Game";
    GTI_destination = TicTacBoard;					/* Send the action message to the
                     * TicTac game board object. */
    GTI_actionMsg = MSG_TICTAC_NEW_GAME; 	/* The action message. */
}

The GenTrigger's Instance Chunk

The GenTrigger object has two master parts, just like the GenInteraction object shown in Variant Classes : the Gen master part holds the instance data for GenClass and GenTriggerClass . The Vis master part holds the instance data for VisClass and OLButtonClass . The MetaClass instance data consists only of the object's class pointer and has no master part.

The trigger's instance chunk's basic structure consists of the class pointer (four bytes) followed by two words of offset into the chunk. The first offset gives the location of the Vis master part, and the second gives the location of the Gen master part. After the offsets are the master parts themselves, and if the trigger had any variable data, it would appear at the end of the chunk.

Each master part has the master class' instance fields first, followed by those of its subclasses. All the fields that belong to VisClass begin VI_..., all those that belong to OLButtonClass begin OLBI_..., etc.

Notice also the first four bytes of the Gen master part: they contain a pointer to the "superclass" of GenClass for the trigger. Although the trigger typically does not have different forms in any given specific UI (as the GenInteraction does), it will have a different class for each specific UI it encounters. For example, the OSF/Motif class is OLButtonClass ; another specific UI will use a different class for GenTriggers.

The GenTrigger's Full Class Tree

Since GenClass is a variant, it has a superclass pointer of 0001h:0000h. This special value (with an illegal segment address) indicates to the kernel that this object's GenClass superclass is stored in the instance chunk itself. The superclass is stored in the first four bytes of the Gen master part, as shown in the previous section.

GenTriggerClass' ClassStruct Structure

Because all classes have the same class structure, only GenTriggerClass will be examined here. The class structure and the instance chunk structure are closely linked in several ways.

As shown in the diagram, the instance chunk points directly to the class. The class points to its superclass, thereby providing inheritance of all the methods and structures of classes higher in the class tree such as GenClass .

The class structure contains some information about the instance chunk's format, specifically Class_masterOffset and Class_instanceSize . Class_masterOffset gives the offset into the instance chunk where the offset to the master part is stored. Class_instanceSize contains the size of the master part so the kernel can quickly allocate the needed space when the master part is initialized.

The method table resides at the end of the class, and it has entries for each message handled by the class. GenTriggerClass handles seven messages (stored in Class_methodCount ); any message received by this trigger and not recognized by GenTriggerClass is passed up the class tree for handling. Thus, a MSG_GEN_SET_NOT_ENABLED sent to the trigger will be passed on to GenClass and will be handled there.

The method table has two parts: The first part is a listing of message numbers, and the second part is a listing of pointers to the method code. When the object receives a message, the kernel scans the first part to see if the class handles the message. If it does, the kernel then scans the second part of the table to get the pointer to the code. It then executes the code there as if that code were a function called by the object's code.

How a Message Is Handled

Most aspects of messages and messaging are described in the following section. This section, however, describes how the kernel finds and executes the code when a message is sent to the GenTrigger. The message is MSG_GEN_SET_USABLE (handled by GenClass ).

Messages are sent directly to an object using its optr. That is, when you send a message to this particular GenTrigger, you send it directly to TicTacNewTrigger, not to some monolithic "case" statement run by your application. Since the object's optr uniquely identifies the location of the object's instance chunk in memory, the kernel can quickly access the code for the handler.

When MSG_GEN_SET_USABLE is sent to the TicTacNewTrigger, for example, the kernel looks in the object's instance chunk for its class pointer. It follows this pointer and then looks in GenTriggerClass ' ClassStruct structure. It scans the first part of the class' method table for MSG_GEN_SET_USABLE . If the message is not there (and it isn't), the kernel will then follow the class' Class_superClass pointer and look in GenClass ' ClassStruct structure. It then scans the first part of GenClass ' method table for the message. GenClass has an entry for MSG_GEN_SET_USABLE , and therefore the kernel checks the second part of the method table for the code pointer. It follows this pointer to the method's entry point and begins executing the code there.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 GEOS Terminology | Next: 3.3 The GEOS Message System


The GEOS Message System

GEOS Programming: 3.3 The GEOS Object System: The GEOS Message System

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Object Structures | Next: 4 Using Classes and Objects

Because objects are independent entities, they must have some means of communicating with other objects in the system. As shown in the example of the calculator and requestor objects in the Overview chapter, communication is implemented through the use of messages and methods.

The Messaging Process

When an object needs to notify another object of some event, retrieve data from another object, or send data to another object, it sends a message to that object. Sending a message is similar to calling a function in C in that the message can take parameters (including pointers) and give return values. However, messages are also quite different from function calls in the multithreaded environment of GEOS.

Each object block in the system is associated with a single thread of execution. Each thread that runs objects (some run only procedural code) has an event queue , a queue in which messages are stored until they can be handled. Every message sent to an object from another thread gets put in the object's thread's event queue. (Messages sent between objects within the same thread are not handled via the queue unless forced that way.) Thus, a single thread's event queue can have messages for many different objects. For most single-thread applications, the programmer will not have to worry about synchronization issues.

The sender of a message has to be aware of synchronization issues raised by having multiple threads in the system. Essentially, you can send a message two ways: The first, "calling" the message, allows the use of return values and acts almost exactly like a function call in C. This places the message in the recipient's event queue and then halts the sender until the return values are received. The sender "goes to sleep" until the message has been processed and is then awoken by the kernel, thus ensuring the message is handled before the sender executes another line of code. The call option should also be used when passing pointers; otherwise, the item pointed to may move before the message can be handled, invalidating the pointer.

The second, "sending" the message, is used primarily when synchronization is not an issue. For example, if the message merely notifies the recipient of some condition or event, or if it sends data with no expectation of a return value, the send option can be used. Essentially, the sender will send off the message and then forget about it, continuing on with its own business. (The exception to this is an object sending a message to another object in the same thread; then the message is handled immediately, as if it had been called.)

When an object sends a message, the message actually first gets passed to the kernel (this is all automatic). The kernel will put the message into the proper thread's event queue and, if necessary, put the sender's thread temporarily to sleep. When the recipient's thread is ready to handle the message, the kernel pulls it from the event queue.

The kernel then locates and loads the recipient object into memory (if necessary). The recipient's object block will be loaded and locked, and the object will be locked while processing the event. (Note, however, that it is possible for the object to move if the recipient makes a call to LMemAlloc() or does something else that can cause shuffling of the object chunks.) The kernel will follow the object's class pointer to its class and will scan the method table. If the class can handle the message, the proper method code will be executed. If the class can not handle the message, the kernel will follow the superclass pointer and check the method table there. The message will continue up the class tree like this until either it is handled or it reaches the root and returns unprocessed.

After the method code has been executed, the kernel collects any return values and wakes up the caller thread again if necessary. To the caller, it's as if the message were handled instantaneously (with the call option). Senders are never blocked; only messages called (with the call option) may block the caller's thread. If a message is sent to an object in the same thread, however, it will be executed as a call and will be handled immediately, unless the sender explicitly states that it should go through the message queue.

Be careful, though, if you are writing code in multiple threads (for example, if you subclass UI objects and write new method code for them). You have to make sure that two threads never call each other; this can lead to deadlock if the calls happen to overlap. The easiest way to deal with this is to have one thread always send a message requesting a return message with any needed return values. The other thread will then send off a return message with the data. For example, a UI object may require information from an application's object. The UI object sends MSG_REQUEST_INFORMATION (or something similar). The application's object then receives that message and responds with a MSG_RETURNING_REQUESTED_INFORMATION (or something similar). With this scheme, the application's object is free to use call whenever it wants, but the UI object must always use send.

Message Structures and Conventions

A message is simply a 16-bit number determined at compile time. Specifically, it is an enumerated type--this ensures that no two messages in the same class can have the same number.

An event is an opaque structure containing the message number and information about the recipient, the sender, parameters, and return values. When an object sends a message, the kernel automatically builds out the event structure (generally stored in the handle table for speed and efficiency). You will never have to know the structure of an event.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Object Structures | Next: 4 Using Classes and Objects


Using Classes and Objects

GEOS Programming: 4 Using Classes and Objects

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 The GEOS Object System | Next: 4.1 Defining a New Class or Subclass

The previous sections dealt with the internals of the GEOS object system. This section describes how you can create classes and objects and manage them during execution using Goc keywords and kernel routines. Almost all Goc keywords begin with " @ " (one notable exception is gcnList ).

All the most useful keywords available in Goc are shown in Goc Keywords . This display is for initial reference; all the keywords are detailed in depth in the following sections of this chapter and in the Routines Book.

Code Display 5-5 Goc Keywords

    /* Including .goh files */
@include <fname>;
    /* Defining New Classes and Subclasses */
@class	<cname>, <super> [, master [, variant]];
@endc
    /* Declaring a class */
@classdecl <cname> [, <cflags>];
    /* Defining messages for a class */
@message <retType> <mname>([@stack] <param>*);
@reserveMessages <num>;
@exportMessages <expName>, <num>;
@importMessage <expName>, <messageDef>;
@alias(<protoMsg>) <messageDef>;
@prototype <messageDef>;
    /* Defining instance data fields for a class */
@instance <insType> <iname> [ = <default>];
gcnList(<manufID>, <ltype>) = <oname> [, <oname>]*;
@instance @composite <iname> [ = <linkName>];
@instance @link <iname> [ = <default>];
@instance @visMoniker <iname> [ = <default>];
@instance @kbdAccelerator <iname> [ = <default>];
@reloc <iname>, [ (<count>, <struct>), ] <ptrType>;
@noreloc <iname>;
@default <iname> = <default>;
    /* Defining vardata fields for a class */
@vardata <type> <vname>;
@vardataAlias (<origName>) <newType> <newName>;
@reloc <vname>, <fn>, [ (<count>, <struct>), ] <ptrType>;
    /* Defining methods (message handlers) */
@method [ <hname>, ] <cname>[, <mname>+];
@method [ <hname>, ] <cname>, _reloc;
    /* Defining library code */
@optimize
@deflib <libname>
@endlib
    /* Defining resources */
@start <segname> [ , <flags> ];
@header <type> [ = <init> ];
@end <segname>
@chunk <type> <name> [ = <init> ];
@localize <string>;
@localize { <string> <length>};
@localize { <string> <minLength>-<maxLength> };
@localize not;
@chunkArray <stype> <aname> [ = {<init>} ];
@elementArray <stype> <aname> [ = {<init>} ];
@extern <type> <name>;
@gstring <name> = {[<command> [, <command>]+]} ;
    /* Declaring an object */
@object <class> <name> <flags>* = {
    [<fieldName> = <init>];*
    [<varName> [ = <init> ]]*;
}
    /* Accessing an object's instance data from one of its methods */
@self-><iname>
    /* Sending and calling messages */
@send [<flags>,+] <obj>::[{<cast>}] <msg>(<params>*);
<ret> = @call [<flags>,+] [{<cast>}] <obj>::[{<cast2>}]<msg>(<params>*);
@callsuper();
@callsuper <obj>::<class>::<msg>(<params>*) [<flags>,];
<event> = @record <obj>::<msg>(<params>*);
@dispatch [noFree] <nObj>::<nMsg>::<event>;
<ret> = @dispatchcall [noFree] [{<cast>}] <nobj>::<nMsg>::<event>;
    /* Using conditional code */
@if <cond>
@if defined(<item>)
@ifdef <cond>
@ifndef <cond>
    /* Creating Goc macros */
@define <mname> <macro>
    /* Esoteric Source-Code Management */
@optimize
@protominor <name>

Defining a New Class or Subclass

Non-relocatable Data

Defining Methods

Declaring Objects

Sending Messages

Managing Objects

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 The GEOS Object System | Next: 4.1 Defining a New Class or Subclass


Defining a New Class or Subclass

GEOS Programming: 4.1 Using Classes and Objects: Defining a New Class or Subclass

| Up: GEOS SDK TechDocs | Up | Prev: 4 Using Classes and Objects | Next: 4.2 Non-relocatable Data
@class, @classdecl, @endc, @default, @uses

You can create new classes in GEOS by using the Goc keywords @class and @endc . These frame the class definition as shown in Defining Classes ; the @endc keyword takes no parameters, but @class takes the following parameters:

@class    <cname>, <super> [, master [, variant]];
cname
This is the name of the new class.
super
This is the class name of the superclass.
master
When included, this word makes the new class a master class.
variant
When included, this word makes the new class a variant class. All variant classes must also be declared master classes.

Every class must have a class structure ( ClassStruct ) in memory. This is created and filled automatically by Goc and the kernel; however, you must use the @classdecl keyword to make sure the structure gets built. Only one @classdecl statement may be used for each class, however--Goc will give an error if the class is declared twice. This is also shown in Defining Classes , and its parameters are as follows:

@classdecl    <cname> [, <cflags>];
cname
This is the name of the class being declared.
cflags
These are optional flags, described below.

The optional flags that can be used with a class declaration determine how objects of the class get shut down (see Class_flags ). The flags you can use with @classdecl are

neverSaved
This flag indicates that objects of this class will neither be written to a state file nor be loaded in from a resource. This flag should only be used for classes whose objects will only be created at run-time (not declared in the .goc file) and for process classes.
discardOnSave
This flag applies only to master classes. Objects of this class will never be saved to a state file and must be re-initialized each time they are loaded. If you want an object simply to revert to its default configuration each time it is loaded, use the flag ignoreDirty instead in the object's declaration (see @object , in Declaring an Object ).

In addition, any variant class can have a default superclass. The variant is resolved at compile-time to have the default superclass as its superclass. To set a default superclass for a variant class, add a line with the following structure in the class definition:

@default <varRoot> = <super>;
varRoot
The name of the variant class with "Class" removed. (For example, GenClass would be specified as "Gen.")
super
The name of the superclass to set as the default.

Sometimes a variant class will know that it will be the subclass of a specific class, though it doesn't know (at compile time) just how that ancestry will be traced. You can use the @uses directive to let the compiler know this; that way, the variant class can define handlers for the "used" class. For example, if you know that variant class MyVariantClass will always be resolved as a descendant of MyAncestorClass , you can put the directive

@uses MyAncestorClass;

in the definition of MyVariantClass . The general format is

@uses <class>;
class
The class which will always be an ancestor to this class.

Code Display 5-6 Defining Classes

/* The @class keyword defines a new class or subclass. @endc ends the class
 * definition, and @classdecl must be put somewhere in the code to make sure Glue
 * will link the class structure into the compiled geode.*/
@class	MyNewClass, VisClass;
    /* Message declarations would go here. See @message.
     * Instance data field declarations would go here. See @instance
     * 						and @vardata. */
@endc
@classdecl MyNewClass, neverSaved;
@class	MyTriggerClass, GenTriggerClass;
    /* New messages for this subclass are defined here. */
    /* New instance data fields for this subclass are defined here. */
@endc
@classdecl MyTriggerClass;
/* When defining a variant class (which must also be a master class), you can
 * set a superclass for the variant at compile-time using @default. */
@class	MyNewVariantClass, MetaClass, master, variant;
    @default		MyNewVariant = VisClass;
@endc	MyNewVariantClass
@classdecl MyNewVariantClass;

Defining New Messages for a Class

@message, @stack, @reserveMessages, @exportMessages, @importMessage, @alias, @prototype

As discussed in The GEOS Message System , messages are simply 16-bit numbers allocated as an enumerated type. When a new class is defined, a constant is automatically created representing the first message number for the class. This constant is then used as the first number in the enumeration of messages.

The constant is built off the class' superclass. MetaClass has the first 16384 messages reserved for its use. Each master level gets 8192, and the first master class of a level gets 2048 of these. All other classes are allocated 512 message spots. Thus, a master class subclassed directly off MetaClass would have 2048 messages beginning with number #16384 (since the numbering is zero-based). A subclass of this would have 512 messages beginning with number #18432.

This numbering scheme ensures that no two classes at different levels in the class hierarchy will have the same message number. Specifically, a class will never have the same message number as one of its sub- or superclasses.

New messages must be defined in the class definition between the @class and @endc keywords (see above). They are defined much like normal function calls and follow the normal C calling conventions (see Defining Messages for examples). If your class uses messages from its superclass, you do not have to declare these messages in your class definition--they are inherited automatically. This is true even if you are subclassing the method to alter its functionality.

To define a new message, use the @message keyword. This keyword takes the following parameters:

@message   <retType> <mname>(<param>*);
retType
This is the data type of the return value of the message. It can be any standard C or GEOS data type (excluding structures), or a pointer to a structure. If this message has multiple return values, you must do as in C function calls and pass pointers to buffers for the return information.
mname
This is the name of the message. By convention, it will be MSG_ followed by a shortened version of the name of the class and then some useful name (e.g., MSG_META_INITIALIZE ).
param
This represents one or more parameters. Messages may have no parameters, one parameter, or several parameters. Parameter definition is essentially the same as definition of function parameters; see Defining Messages for examples.

NOTE: When defining a function with no parameters, it is best to declare it with "void" between the parentheses. This will make sure Goc gives an error if the function is called with arguments.

Messages for Use with Assembly Code

The @stack keyword indicates that parameters are passed on the stack; it is important to note that because of calling conventions, parameters passed on the stack must be listed in the message definition in reverse order from the way the handler pops them from the stack. This keyword is used only when the message may be handled by an assembly language method; its format is shown below:

@message <retType> <mname>(@stack <param>*);

All the parameters shown in the formats are the same as in the normal @message format.

Code Display 5-7 Defining Messages

    /* Each message is defined for a class within the class definition. */
@class	MyTriggerClass, GenTriggerClass;
    /* All the new messages MyTriggerClass can handle are defined here. */
@message void MSG_MYTRIG_SET_COLOR(colors colorIndex);
@message optr MSG_MYTRIG_RETURN_OPTR( void );
@message void MSG_MYTRIG_COLLECT_PARAMS(byte bParam, word wParam, char * string);
    /* Instance data fields would be defined here. */
@endc
@classdecl MyTriggerClass;

Exporting, Importing, and Reserving Message Ranges

As discussed above, message numbers are assigned based on the class' location in the class tree. No message number will ever conflict with messages defined in the class' superclasses. However, the assignment scheme opens up the possibility that classes on the same level in the class tree could have conflicting message numbers, as shown below.

Normally, this is not a problem. If subclasses are to receive the same message and handle it differently, the message can typically be defined in the superclass and simply be intercepted by the subclasses. Sometimes, however, different subclasses will need to have different definitions for the same messages. For example, a class supplied by a library may be used by several applications; if the applications each create a subclass, these subclasses can import particular messages that will be the same for all the subclasses in all the applications.

Goc therefore allows a class to export a range of message numbers which subclasses can import and create specific definitions for. This allows you greater control over what aspects of the class you can define.

To export a range of messages, use the @exportMessages keyword. This will set aside several message numbers which can then be imported by subclasses using the @importMessage keyword.

Another potential problem is upgrading your program from release to release. If you create classes that may grow in the future, you may want to reserve a number of message spots to ensure that those spots can be filled in later. Nothing is done with the spots; they are simply place holders for future upgrades. You can use the @reserveMessages keyword to reserve a range of any size. The parameters of these three keywords are shown below:

@reserveMessages    <num>;
@exportMessages     <expName>, <num>;
@importMessage	      <expName>, <messageDef>;
num
This is the number of messages in the exported range.
expName
This is the name of the exported range. This is used when importing messages to ensure that the proper numbers are used.
messageDef
This is a standard message definition line, the same as would be found with the @message keyword (though @message is left out).

Note that you do not need to reserve messages for upgrades; any class can always have messages tacked on to the end of its class definition. If you want to group the messages logically, however, you should reserve ranges where you expect additions to be made.

Aliasing Messages

The @alias keyword allows a single message to have more than one pass/return format. The @prototype keyword allows quick, clean, and convenient repetition of a single format for many different messages; it also allows a class to create a prototype so users of a message can have their own messages with the same format.

@alias is used when a single method takes conditional parameters. For example, a method may take a word value in a certain case and a dword value in another (dependent upon a passed flag). Each condition must be accounted for in its own message format. Rather than create a message and a method for each case, you can create a single assembly-language method for all the different pass/return formats; then, you can use @alias to make several messages refer to the same method, each using a different format.

@alias(<protoMsg>) <msgDef>;
protoMsg
The name of the original message. The new message may have different pass/return values but will invoke the same method code and will have the same message number.
msgDef
The new message definition. It follows the same format as messages defined with the @message keyword (with @message left off).

In addition, if you have a single pass/return format for many messages, you can use the @prototype keyword as coding shorthand. For example, if an object has ten messages that all take two parameters and return a single value, you can set up the format with the @prototype keyword and then use a simpler format for definition of your messages. An example is shown in Aliasing Messages , and the parameters of this keyword are shown below.

@prototype <msgDef>;
msgDef
This is the standard message definition. For the message name, use something like " MY_PROTOTYPE " that you can insert later into the definitions of your real messages. All other parts of the message definition are the same as would be declared with @message (with @message left off).

Code Display 5-8 Aliasing Messages

    /* MyClass in this example uses both prototype and aliased messages. */
@class MyClass, MetaClass;
    /* The following is a normal message declaration. The register that this
     * parameter uses is specified because the handler (method) is written in
     * assembly language. */
@message void MSG_MESSAGE_WITH_WORD(byte flag = cl, word value = dx);
    /* The following message invokes the same method as the alias above.
     * It has the same message number but passes a different sized parameter
     * in different registers. */
@alias(MSG_MESSAGE_WITH_WORD) void MSG_MESSAGE_WITH_DWORD(byte flag = cl,
                            dword value = dx:bp);
    /* The following message is not used. Its pass and return values can
     * be used elsewhere, however, to ensure that all handlers of this message
     * type are given the same format. */
@prototype int MSG_MYCLASS_PROTO(int a, int b);
    /* The following have the same return values and parameters
     * as the prototype above. */
@message(MSG_MYCLASS_PROTO) MSG_MY_CLASS_ADD;
@message(MSG_MYCLASS_PROTO) MSG_MY_CLASS_SUBTRACT;
@message(MSG_MYCLASS_PROTO) MSG_MY_CLASS_MULTIPLY;
@endc
@classdecl MyClass;

Defining Instance Data Fields

@instance, @composite, @link, @visMoniker, @kbdAccelerator

Instance data fields are all defined with the @instance keyword. Other keywords may be included in the @instance declaration for special types of data. All instance data definitions must appear between the class' @class and @endc keywords (see above under class definition).

The @instance keyword is used to define normal instance data. If you have data that must be added or removed dynamically (such as hints), use the @vardata keyword, described in Defining and Working With Variable Data Fields . Also, if you have data that requires relocation (such as pointers to fixed data) when the object is loaded, use the @reloc keyword.

The format of the @instance keyword is as follows:

@instance    <insType>    <iname> = <default>;
insType
A standard C or GEOS data or structure type representing the data type of the instance field.
iname
The name of the instance field.
default
The default value of the instance field if it is not filled in when an object of this class is instantiated. The value must, of course, be appropriate for the data type.

Goc has several special types of instance data fields that you can declare along with @instance to make object definition easier. The format for using one of the special types is shown below (with examples in Declaring Instance Data Fields ). Each of the types is also described below.

@instance    <specType> <iname>;
specType
This is the keyword (one of those shown in the list below) that defines the special type of this field.
iname
This is the name of the instance field.

The special types are given here:

@composite
This field is used when objects of the class being defined are allowed to have children. The @composite field will actually contain an optr to the first child object in an object tree. Since most objects in object trees are subclassed from VisClass or GenClass , you will most likely never use the @composite keyword. Both VisCompClass and GenClass have @composite fields predefined. The @composite type has a special format, shown below:
@instance  @composite <iname> = <linkName>;
where iname is the name of the instance field and linkName is the name of the field designated as @link (below). Note that there must be a @link field in every class that has a @composite field. See Managing Object Trees for more information on object trees and the composite and link fields.
@link
This field is used by objects that can be children in an object tree. Where the @composite field points to the first child, the @link field points to the next sibling. If there is no next sibling, this field will point back to the parent object. Since most objects in object trees are subclassed from VisClass or GenClass , you will most likely never use the @link keyword. Both VisClass and GenClass have @link fields predefined.
@visMoniker
This field is designated as holding a pointer to a visual moniker chunk for the object. It is used in GenClass --see the GenClass chapterfor information on the GI_visMoniker field. The moniker or moniker list must be in the same resource as the generic object using that moniker since only the chunk's handle is stored. A moniker list can store full object pointers to its monikers, so monikers referenced by a list need not be in the same resource as that list; thus if an object's moniker is specified via a list, then while the list must be in the same resource as the object, the monikers themselves need not be.
@kbdAccelerator
This field contains a character sequence that, when typed by the user, causes the object to execute its default operation. For example, a keyboard accelerator could invoke a trigger implemented as a menu item. It is used in GenClass only.

Code Display 5-9 Declaring Instance Data Fields

/* GenClass is a good example of many of the different types of fields. */
@class	GenClass, VisClass, master, variant;
    /* The GenClass messages are defined here. */
    @instance @link GI_link;
    @instance @composite GI_comp = GI_link;
    @instance @visMoniker GI_visMoniker;
    @instance @kbdAccelerator GI_kbdAccelerator;
    @instance byte GI_attrs = 0;
    @instance byte GI_states = (GS_USABLE|GS_ENABLED);
    /* Hints and other variable data fields are defined with @vardata. */
@endc

New Defaults for Subclassed Instance Data Fields

@default

Recall that when defining an instance data field you can set up a default value for that field. When creating a subclass, you may wish to specify that the subclass should have a different default value for a given field than the superclass does. Use the @default keyword to do this:

@default <iname> = <default>;
iname
The name of the instance field.
default
The new default value of the instance field if it is not filled in when an object of this class is instantiated. The value must, of course, be appropriate for the data type. You may use @default as part of this value; this @default will be treated as the value of the superclass. (If this seems confusing, try looking at the example.)

For example, a subclass of GenInteraction could set GIV_DIALOG as its default value for the GenInteraction instance field GII_visibility :

@default GII_visibility = GIV_DIALOG;

A generic class might want to have the same value for its GI_states field as its superclass, except with the GS_USABLE flag turned off:

@default GI_states = @default & ~GS_USABLE;

Defining and Working With Variable Data Fields

@vardata, @vardataAlias, ObjVarAddData(), ObjVarDeleteData(), ObjVarDeleteDataAt(), ObjVarScanData(), ObjVarFindData(), ObjVarDerefData(), ObjVarDeleteDataRange(), ObjVarCopyDataRange(), MSG_META_ADD_VAR_DATA, MSG_META_DELETE_VAR_DATA, MSG_META_INITIALIZE_VAR_DATA, MSG_META_GET_VAR_DATA

Most classes will have well-defined instance data fields; each object in the class will have the same data structures, and all the instance chunks will look relatively similar and will be the same size.

Many classes, however, will also use "variable data," or instance fields that may be added or removed dynamically. This allows objects within the same class to have more or less instance data than other objects in the class. One example of variable data is the use of hints in generic UI objects. Because each object in a given class may or may not have hints, the objects can actually have different instance sizes. Variable data instance fields are defined with the use of the @vardata keyword.

Using variable data, however, is somewhat more complex than using standard instance data. You must use special kernel routines or messages to get a pointer to the data; then you can use the pointer to access the field.

Variable data is stored together at the end of the instance chunk in "data entries." Each entry consists of a primary word and optional extra data. The primary word represents a data type defined by the keyword @vardata . This type is created automatically by Goc when the @vardata keyword is used.

Each data entry is associated with a master class level and is considered part of the instance data for that master level (despite being stored at the end of the instance chunk). Thus, when a master part of an object is destroyed, the variable data entries associated with that master class will also be destroyed. For example, when a UI object is set not usable (taken off the screen), its Vis master part is removed from the instance chunk; any variable data entries associated with VisClass will also be destroyed.

Variable data may also be tagged for saving to the state file. That is, you can set up individual data entries to be saved to a state file and to be reinstated when the object is loaded from the state file. For more information about state saving, see Saving Object State .

Variable data may be defined in an object's declaration in your .goc file or may be added and removed during execution. This gives the potential for using variable data as temporary storage in an object's instance chunk; however, temporary data used in this manner should be kept small to avoid slowing down the system--constantly resizing instance chunks to add and remove vardata fields makes more work for the memory manager.

To define a variable data type in a given class, use the @vardata keyword as follows (an example is given in Defining Variable Data ):

@vardata    <type> <vname>;
type
This is the data type of any extra data associated with the variable data. It must be a standard C or GEOS data type. If the type void is specified, no extra data will be added to the data entry when it is created. (An instance data field may be declared as an array, just as in standard C.)
vname
This is the name of the variable data type. This name is used whenever referring to the vardata entry. Note that no two variable data types should have the same name, even if they're in different classes. Doing so will cause a compilation error. It's a good practice to put the class name within the data type name.

Code Display 5-10 Examples of Instance Data Declarations

/* These are some data fields for MyDataClass.
 */
    @instance		ChunkHandle		MDI_aChunk;
    @instance		HelloInfoFlags		MDI_flags;
    @instance		byte		MDI_lotsOfNumbers[32];

Some vardata types may have varying amounts of extra data. For example, one type may have either a word or a dword of extra data. To allow this, you can set up an alias with the new type attached using the keyword @vardataAlias :

@vardataAlias  (<origName>) <newType> <newName>;
origName
This is the name of the original variable data field already defined with @vardata .
newType
This is the data type of the new variable data field, a standard C or GEOS data type.
newName
This is the name of the new variable data field. In essence, the original and new fields will have the same data type word but will have different extra data size.

As noted earlier, the data type field in the data entry has two flags associated with it. These flags are each one bit:

VDF_EXTRA_DATA
This flag indicates that this data type carries extra data.
VDF_SAVE_TO_STATE
This flag indicates that this particular data entry should be saved along with all the other object's instance data when the state is saved. It should likewise be restored when the object is restored from the state file. Unless set off explicitly, this flag will be set for every data type defined in a .goc or .goh file.

The bitmask VDF_TYPE is a bitwise OR of VDF_EXTRA_DATA and VDF_SAVE_TO_STATE. You can use it to mask out all but those bits.

Code Display 5-11 Defining Variable Data

/* Hints are defined with the @vardata command, as is shown in GenClass. Only a
 * small portion of the hints for GenClass are shown here. Those with structures
 * or data types (not "void") have extra data fields associated with them. */
@class GenClass, VisClass, master, variant;
    /* Messages are defined here. */
    /* Followed by instance data defined with @instance. */
    @vardata void HINT_CENTER_MONIKER;
    @vardata SpecSizeSpec HINT_CUSTOM_CHILD_SPACING;
    @vardata char[] ATTR_GEN_INIT_FILE_KEY;
    /* Relocatable instance fields (see the next section) are defined with
     * @reloc. This field contains an object pointer that must be resolved
     * when the GenClass object is loaded. */
    @instance @link GI_link;
    @reloc GI_link, optr;
    @vardata DestinationClassArgs ATTR_GEN_DESTINATION_CLASS;
    @reloc ATTR_GEN_DESTINATION_CLASS, 0, optr;
@endc

The kernel provides a number of routines an object may use to add, remove, and change its own vardata entries. Note that all these routines must be called from within the object containing the variable data entries; since variable data is instance data, it is against OOP doctrine for one object to alter another object's variable data directly.

Instead, MetaClass provides vardata messages that can be sent by one object to another to add, remove, change, or retrieve vardata entries of another object. The kernel routines and MetaClass messages are outlined below:

  • ObjVarAddData()
     This routine adds an new entry for the passed data type or replaces the extra data associated with a particular data type entry.
  • ObjVarDeleteData()
     This routine deletes the entry for a particular data type when passed the data type.
  • ObjVarDeleteDataAt()
     This routine deletes a particular data entry when passed the entry's pointer as returned by ObjVarAddData() , ObjVarFindData() , or ObjVarDerefData() .
  • ObjVarScanData()
     This routine causes the kernel to scan all data entries in an object's variable data and call any "handler routines" listed for them. This process is described below.
  • ObjVarFindData()
     This routine searches for and returns (if possible) a pointer to a data entry of the passed data type.
  • ObjVarDerefData()
     This routine returns a pointer to a data entry when passed the object's optr and the data type. If the entry does not exist, this routine will call on the object to create and initialize the entry. Such variable data then behaves much like instance data. The object containing the vardata is responsible for creating the entry and then initializing it upon receipt of a MSG_META_INITIALIZE_VAR_DATA , described below.
  • ObjVarDeleteDataRange()
     This routine deletes all data entries with types in the passed range.
  • ObjVarCopyDataRange()
     This routine copies all data entries within the passed range from one object's instance chunk to another`s. If any entries are copied, the destination object will be marked dirty for saving. This routine must be called by the destination object; it is bad policy for one object to alter another object's instance data. This routine is primarily for copying hints from one UI object to another and is not commonly used by applications.

The four messages (in MetaClass ) that can be used to add, delete, and alter variable data entries remotely are listed below. Classes will never need to intercept and subclass these messages because the proper functionality is implemented in MetaClass .

MSG_META_ADD_VAR_DATA
Adds a new vardata type to the recipient object. If the type already exists, the passed type replaces the old one.
MSG_META_DELETE_VAR_DATA
Deletes a vardata type from the recipient's instance data. If the type does not exist, nothing is done.
MSG_META_INITIALIZE_VAR_DATA
Used when something is trying to access an object's vardata field remotely but the field has not yet been added to the object or initialized. The object must create and/or initialize the vardata field at this point.
MSG_META_GET_VAR_DATA
Returns the extra data set for the passed data type.

In addition to supporting variable data structures, GEOS allows you to set up "handlers" for different variable data types. Handlers are routines that process a given data entry; for example, each generic UI object stores a number of hints. Specific UI classes, when attached to the generic object, have a specific routine to handle each hint supported. Some specific UIs do nothing with certain hints; these specific UIs do not have handlers for those hints.

Handlers are associated with data types through the use of a VarDataCHandler table. This is a table that you set up in your .goc file that contains pairings of routine names with @vardata field names. An example of the VarDataCHandler table is shown in Variable Data Handlers .

A handler is simply a normal C routine or function and is defined as such. The handler should be declared as an _pascal routine.The table pairs the handler with the @vardata data type, and when ObjVarScanData() is called, all handlers for all data types are called in order. This is true for the object's class and all its superclasses since variable data is inherited just as normal instance data is. The handler can do almost anything appropriate with the exception of destroying the object or adding or deleting variable data from the object.

Code Display 5-12 Variable Data Handlers

    /* This example is taken from the C VarData sample application. */
/* This is a VarDataCHandler. It is called by the ObjVarScanData() routine when
 * the data type corresponding to this routine in the VarDataCHandlerTable
 * is encountered. The parameters indicated are passed.
 *
 * This particular handler is actually used for several different data types
 * (see VarDataCHandlerTable below). The data type can be distinguished by
 * the `dataType' parameter.
 *
 * NOTE: VarDataInteractionHintHandler, like any handler used in a
 * VarDataCHandler structure, must be declared _pascal. */
void _pascal VarDataInteractionHintHandler(MemHandle mh, ChunkHandle chnk,
        void *data, word dataType, HandlerData *handlerData) {
    if (dataType == HINT_ORIENT_CHILDREN_HORIZONTALLY) {
    handlerData->HD_flags.has_horiz = 1;
    } else if (dataType == HINT_ORIENT_CHILDREN_VERTICALLY) {
    handlerData->HD_flags.has_vert = 1;
    } else if (dataType == HINT_ALLOW_CHILDREN_TO_WRAP) {
    handlerData->HD_flags.has_allow_wrap = 1;
    } else if ((dataType == HINT_WRAP_AFTER_CHILD_COUNT) &&
        (((WrapAfterChildCountData *) data)->WACCE_childCount == 2)) {
    handlerData->HD_flags.has_wrap_after = 1;
    }
}
/* This is the VarDataCHandler Table. It consists of data type/VarDataCHandler
 * pairs. The VarDataCHandlers are far routines. */
static VarDataCHandler varDataInteractionHandlerTable[] = {
    {HINT_ORIENT_CHILDREN_HORIZONTALLY, VarDataInteractionHintHandler},
    {HINT_ORIENT_CHILDREN_VERTICALLY, VarDataInteractionHintHandler},
    {HINT_ALLOW_CHILDREN_TO_WRAP, VarDataInteractionHintHandler},
    {HINT_WRAP_AFTER_CHILD_COUNT, VarDataInteractionHintHandler}
};

Defining Relocatable Data

@reloc

Some objects and classes may have instance data fields that must be resolved when the object is loaded and linked at run-time. For example, if the object contains an optr to another object, that optr must be updated when the object is loaded and resolved since the global memory handle can't be known at compile-time.

For some special instance fields, this happens automatically. For example, the @composite and @link fields as well as optrs are automatically resolved. However, if you add your own instance fields requiring relocation, you will have to set them up with the @reloc keyword. This is true for both static and variable data.

This keyword uses two formats. The first listed here is for normal, static instance data, and the second is used with variable data.

 @reloc    <iname>, [(<count>, <struct>)] <ptrType>;
iname
This is the name of the relocatable instance field.
count
If the instance variable is an array of relocatable data or structures containing relocatable fields, this is the number of elements in the array.
struct
If the relocatable data is an array of structures, this represents the name of the field within each structure that requires relocation.
ptrType
This is the type of relocatable data contained in the field. It may be one of optr , ptr , or handle .
@reloc <vname>, <fn>, [(<count>, <struct>)] ptrType;
vname
This is the name of the variable data type.
fn
This is the name of the field within the variable data's extra data. If there is no extra data with this data type, put a zero rather than a name.
count
If the instance variable is an array of relocatable data or structures containing relocatable fields, this is the number of elements in the array.
struct
If the relocatable data is an array of structures, this represents the name of the field within each structure that requires relocation.
ptrType
This is the type of relocatable data contained in the field. It may be one of optr , ptr , or handle .
| Up: GEOS SDK TechDocs | Up | Prev: 4 Using Classes and Objects | Next: 4.2 Non-relocatable Data


Non-relocatable Data

GEOS Programming: 4.2 Using Classes and Objects: Non-relocatable Data

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Defining a New Class or Subclass | Next: 4.3 Defining Methods
 @noreloc

To force an instance data field which would normally be relocatable (e.g., an optr) to not be relocatable, use the @noreloc keyword. Use this keyword together with the name of the field to be marked non-relocatable directly after defining the instance field itself as shown in Use of the @noreloc Keyword .

Code Display 5-13 Use of the @noreloc Keyword

    @instance optr MCI_ruler;				/* Normally MCI_ruler would be reloc... */
    @noreloc MCI_ruler;				/* ...but now it isn't. */
| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Defining a New Class or Subclass | Next: 4.3 Defining Methods


Defining Methods

GEOS Programming: 4.3 Using Classes and Objects: Defining Methods

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Non-relocatable Data | Next: 4.4 Declaring Objects
@method, @extern

Methods are the routines executed when an object receives a message. Each class understands a certain set of messages; each of these has a place in the class' method table and corresponds to one method.

Although methods are class-specific, they are not defined between the @class and @endc of class definition. Instead, their declaration line links them to a single class and to a specific message. Goc, Glue, and GEOS build each class' method table automatically; you do not have to create the table yourself.

To define a method, use the @method keyword. This has the following structure:

@method    [<hname>,] <cname>[, <mname>]+;
hname
The handler name, if any. If you wish to use the method as a function, it must have a handler name. If you do not provide a handler name, Goc will create one for you. This name is useful for setting breakpoints when debugging. If you do not provide a name, Goc constructs the name by concatenating the class name (with the -Class suffix, if any, removed) with the message name (with the MSG_- prefix, if any, removed). For example, Goc would call MyClass ' handler for MSG_DO_SOMETHING "MyDO_SOMETHING".
cname
The name of the class to which this method belongs. Each method may belong to only one class.
mname
The name of the message that invokes this method. The plus symbol indicates that one method may be invoked by more than one message as long as they all have the same parameters and return values. At least one message must be specified.
There is a special Goc keyword, _reloc , used in place of a message name when writing a method for MSG_META_RELOC and MSG_META_UNRELOC . A method handling _reloc will handle both MSG_META_RELOC and MSG_META_UNRELOC ; the method code can check the value of its message argument to find out which message is being handled.

Note that the name of the method (the handler name) is optional. Parameters and return values are not put in the method declaration--these are defined with @message as discussed in The GEOS Message System .

If you will wish to call the method as a routine occasionally, your compiler will probably require that you provide a prototype for the routine. If your @message declaration looks like

@message word MSG_MC_DO_SOMETHING(word thing);

and your @method declaration looks like

@method DoSomething, MyClass, MSG_MC_DO_SOMETHING {
/* Code Here */ }

Then your protoype should look like

extern word _pascal DoSomething(optr oself,
                      MyMessages message,
                      word thing);

The name of the type MyMessages is constructed automatically by taking the name of the class, removing the "Class" suffix, and replacing said suffix with "Messages".

Normally, all of a class' methods will be coded in the same code file in which the class is declared with @classdecl . If, however, you find you need to declare a class' methods in a different file, you can use the @extern keyword to indicate a method is defined and/or used in a different object file. Goc will give no error if @extern is used and the method exists nowhere; Glue, however, will give a linking error in such a case. There is no such requirement, however, if you are putting only the class definition (the definitions between @class and @endc ) in a different file. In this case, you can put the class definition in a .goh header file and the method code in the same .goc file as the @classdecl statement; you must @include the .goh file, but you won't need to use the @extern directive (as long as the method code is in the same file as the @classdecl directive).

The format for using @extern is as follows:

/* In the file in which the class is declared with  * @classdecl:
 */
    @extern method <cname>, <mname>+;
/* In the file containing the method code: */
    @extern method <cname>, <mname>+ {
        ...method code goes here...
    }
cname
The name of the class for which the method is defined.
mname
The name of the message which invokes the method. Note that external method definitions, like normal method definitions, can handle more than one message.

Three parameters are passed automatically with messages and do not have to be declared in the @message definition. They are important to know when writing methods, however, because they can greatly simplify your code. These are standard parameters for all classes except ProcessClass and its subclasses below:

pself
A far pointer to the object's instance data. pself points to the master group for the class for which the handler is defined. Note that this pointer may be invalidated by message calls so it is preferable to use the GOC directive, @self , instead. Unlike pself, @self is always valid.
oself
An optr to the object's instance data. It contains the global memory handle and chunk handle of the instance chunk. This can be used for routines that act on the object's instance data.
message
The message number of the message being handled.

As mentioned, ProcessClass is a special type of class. It has no true instance data because it uses the standard PC structure of an application (idata, udata, etc.). It only has one standard parameter to each of its methods: the message that was sent to it. This is because the "instance data" of ProcessClass includes all the global variables of your program. They are accessed automatically, no oself or @self is required.

Code Display 5-14 A Class Definition

/* The class ValClass defines four messages that invoke four different methods. The
 * entire class is shown in this example; it will function properly if coded this
 * way. Note that the methods have the class name in their declaration line
 * and thus do not appear within the class definition. */
@class ValClass, MetaClass;
@instance int value;			/* instance data value: an uninitialized integer */
    /* message declarations
     * All four messages will be handled by this class. They return
     * the types shown and take the parameters defined. */
@message int MSG_VAL_GET_VALUE();
@message void MSG_VAL_SET_VALUE(int newValue);
@message void MSG_VAL_NEGATE_VALUE();
@message Boolean MSG_VAL_IS_VALUE_BIGGER_THAN(int newValue);
@endc
@classdecl ValClass;			/* the class structure must be put in memory */
    /* Method Declarations
     * Each of the four methods is a single line of code. Note that the
     * parameters are automatically defined in the message definition and do
     * not need to be restated in the method definition. The same is true of
     * the return type. Note also that the class and message names appear in
     * the @method line. */
@method	ValGetValue, MyClass, MSG_VAL_GET_VALUE {
    return(@self->value);
}
@method	ValSetValue, MyClass, MSG_VAL_SET_VALUE {
    @self->value = newValue;
}
@method	ValNegateVal, MyClass, MSG_VAL_NEGATE_VALUE {
    @self->value *= -1;
}
@method	 ValClass, MSG_VAL_IS_VALUE_BIGGER_THAN {
    /* This handler's name will automatically be created to be
     * ValVAL_IS_VALUE_BIGGER_THAN. You can use this name as a
     * C function call from within the same thread. */
    return(@self->value > newValue);
}

You may sometimes wish to call a method with normal C call-and-return conventions, rather than by sending a message. To do so, you will have to declare the method as a routine as well as a method. The declaration should have the following format:

extern <type> _pascal <MethodName>(
    optr					oself,
    <TruncatedClassName>Messages					message,
    <type1>					<arg1>,
    <type2>					<arg2>)
type
This is the type returned by the method. It may be any data type.
MethodName
This is the name of the method. If you will be calling a method as a routine, you must give the method a name when you declare it (see To define a method, use the @method keyword. This has the following structure: ). Use the same name here.
TruncatedClassName
This is the name of the class, without the word "Class". The type of this argument is the truncated class name followed (with no space) by the word "Messages". Thus, for "HelloCounterClass", the truncated class name would be "HelloCounter", and the type of this field would be "HelloCounterMessages".
type n , arg n
Use these fields to declare each of the arguments passed to the message. Be sure to use exactly the same arguments, and in the same order, as in the message declaration.

Code Display 5-15 Declaring a Method As a Routine

@message int MSG_HELLO_COUNTER_RECALCULATE_VALUE( \
                HelloPriority 		priority, \
                word 		randomDatum, \
                char 		aLetter);
extern int _pascal HelloCounterRecalculateValue(
            optr			oself,
            HelloCounterMessages			message,
            HelloPriority			priority,
            word			randomDatum,
            char			aLetter);
@method	HelloCounterRecalculate, HelloCounterClass, \
        MSG_HELLO_COUNTER_RECALCULATE_VALUE {
    /* method code goes here... */
}
| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Non-relocatable Data | Next: 4.4 Declaring Objects


Declaring Objects

GEOS Programming: 4.4 Using Classes and Objects: Declaring Objects

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Defining Methods | Next: 4.5 Sending Messages

In GEOS programs, you can instantiate objects in two ways: You can declare them in your source code with the @object keyword, or you can instantiate them and fill in their instance data during execution. In most cases, you will probably do the former, especially with generic UI objects.

Additionally, you can create resources and chunks with the @start, @end , @header , and @chunk keywords. GEOS libraries also need an additional set of declarations in their definition ( .goh ) files; these declarations ( @deflib and @endlib ) indicate that the code contained between them is part of the specified library.

Defining Library Code

@deflib, @endlib

If your geode is a library, it will likely have a number of .goh files. Each of these files contains some portion of the library's code and is included by applications that use the library. The library code must be delimited by the two keywords @deflib and @endlib , which have the following formats:

@deflib <libname>
@endlib
libname
The permanent name of the library with the extender stripped off. For example, the UI library's name is ui.lib, and the format would then be
@deflib ui
    /* library code here */
@endlib

Note that these two keywords are only necessary in files that define classes in the library. Files that have just code or data used in the library do not require them (though they are allowed).

Declaring Segment Resources and Chunks

@start, @end, @header, @chunk, @gstring, @chunkArray, @elementArray, @extern

There are essentially three types of resources in GEOS: code resources containing routines and methods, object blocks containing object instance chunks (and often data chunks), and data resources containing only data chunks. Code resources are created automatically, and no declaration is required for them (unless you require special segments; then you should use the pragmas appropriate for your C compiler).

Object blocks or other LMem resources are declared with @start and @end . You can set a special header on a local memory resource with the @header keyword. These are described below, and an example of declaring the resource block is given in Declaring Data Resources . Note that the @header keyword must come between the @start and @end delimiters.

@start    <segname> [, <flags>];
@header   <type> [= <init>];
@end     <segname>
segname
This is the name of the resource segment.
flags
These are optional flags that determine two characteristics of the resource. If the flag data is set, the block will be set to a data resource--the default is an object block. If the flag notDetachable is set, the resource block will never be saved to a state file.
type
This is the name of a structure type that will act as the header structure for the resource block. It must be some permutation of either LMemBlockHeader (for non object blocks) or ObjLMemBlockHeader (for object blocks).
init
This is an initializer for the new header type. Typically, some data fields will be added on to the end of one of the standard LMem block headers. These fields may be filled in with initializer data with this optional argument.

The resource elements (objects or chunks, for example) are also declared within the @start and @end delimiters. The @chunk keyword declares a data chunk and is shown below. For the @object keyword, see the next section.

@chunk    <type> <name> [= <init>];
type
This is the data type that will be held in the chunk.
name
This is the name of the chunk. You may use this name as you would a variable name to reference the chunk.
init
This is initializer data in the standard C format. If initializing a structure, make sure you put the data within curly braces.

There is a special-case syntax for the declaration of a chunk which contains GString data. A GString is a stored series of graphics commands, discussed in detail in the Graphics Environment chapter. Its syntax may be summarized:

@gstring <name> = { <init> };
name
This is the name of the chunk. You may use this name as you would a variable name to reference the chunk.
init
The is the initializer data. It is a series of bytes. Normally, a GString's data set up in a @gstring chunk is set up by means of the GS...() macros.

Two other types of resource elements may also be defined, both of which are array types. The @chunkArray keyword declares a chunk array structure, and the @elementArray keyword declares an element array structure. See the Local Memory chapter, for information on the structure and usage of chunk and element arrays. These static arrays must not appear in a resource marked data; the resource may be marked notDetachable or may be unmarked. In the .gp file, the resource should be marked lmem, optionally read-only. The formats for the keywords are described below:

@chunkArray <stype> <aname> = {<init>};
@elementArray <stype> <aname> [ = {<init>} ];
stype
This is the type of structure stored in the array.
aname
This is the name of the array.
init
This is any initializer data. If there is no initializer data (i.e., you want to declare an empty chunk array), you'll want a code snippet like the following:
@chunk ChunkArrayHeader <aname> = {0,
    sizeof(<stype>), 0, sizeof(ChunkArrayHeader) };

Here is an example showing two statically declared chunk arrays:

#define CELL_DATA_LENGTH 15

typedef char CellDataStruct[CELL_DATA_LENGTH];

@chunkArray CellDataStruct headerDataArray = {
    "no", "Description",  "type", " $/lb"
};

@chunkArray CellDataStruct coffeeDataArray = {
    "0", "House Blend",  "PEET", "$ 6.95",
    "1", "Mocha-Java",   "PEET", "$10.75",
    "2", "Garuda Blend", "PEET", "$ 9.25",
    "3", "Italian Dark", "DARK", "$ 8.75",
    "4", "French Roast", "DARK", "$ 8.75",
    "5", "Sumatra",      "PACF", "$ 8.95",
    "6", "Sulawesi-Kal", "PACF", "$11.55",
    "7", "Java",         "PACF", "$ 8.75",
    "8", "Aged Java",    "PACF", "$ 9.75",
    "9", "Mexican",      "AMER", "$ 7.55"
};

One other keyword, @extern , allows you to use chunks from a different compilation session. The @extern keyword can be used to reference remote (external) chunks, objects, and visual monikers. If the item referenced by @extern is not actually defined anywhere, Glue will return an error when it tries to link the item. (Note also that Glue can not link through an external item; only one layer of external linkage is allowed. Thus, one object could not have an @extern to a chunk that had an @extern to a visMoniker.) Children in object trees may not be defined with @extern ; Goc will produce an error in this case. The format of this keyword is as follows:

@extern chunk <cname>;
@extern object <oname>;
@extern visMoniker <vmname>;
cname
This is the name of a chunk.
oname
This is the name of an object.
vmname
This is the name of a visMoniker chunk.

Code Display 5-16 Declaring Data Resources

/* This example declares a data block with three different chunks in it and
 * its own header type. */
typedef struct {
    LMemBlockHeader			MLMBH_meta;       /* basic header structure */
    int			MLMBH_numEntries;  /* the number of entries in the block */
} StudentBlockHeader;
@start StudentBlock, data;					/* data flag indicates LMem block */
@header StudentBlockHeader = 1;					/* initialize new header fields */
/* The three chunks are defined below. Each represents a single field associated
 * with a single student; that is why the header's MLMBH_numEntries field contains
 * one (rather than three). */
@chunk	char	firstName[] = "John";
@chunk	char	lastName[] = "Student";
@chunk	int	grade = 6;
@end StudentBlock					/* end of resource block */

Declaring an Object

@object, @default, @specificUI, gcnList

The @object keyword is much like the @chunk keyword in its use. It allocates a chunk in a resource segment and fills it with initializer data. However, it also does much more in that it creates an object of a specified class and fills it with default data if no initializer data is specified.

The @object keyword, like @chunk , must be used between the delimiters @start and @end (see above). However, it must be used in an object block (it can not be used in a data resource segment). Its format is below, and several examples are shown in Declaring Objects with @object .

@object   <class> <name> = <flags>* {
    <fieldName> = <init>;*
    <varName> [= <init>];*
};
class
The class of the object. It must be a valid class defined either by GEOS or by your own code.
name
The name of the object. The object's name will be used when sending messages to it.
flags
Currently only one flag is allowed for objects: ignoreDirty . When set, it will cause the object to ignore when changes have been made to its instance data; this means it will always be restored from a state file in the same way as it is defined in the code file. This flag should not be set for any generic objects.
fieldName
The name of an instance data field defined for the class.
varName
The name of a variable data instance field. If the variable data field has extra data, you can initialize it with the optional argument.
init
This is initializer data for the instance data field. If you want default values in the field, you can either leave the field out of the @object declaration or use the keyword @default for the initializer. When declaring variable data, though, be aware that if the extra data is a structure, the initializer must be enclosed in curly braces.

GEOS supports special instance data fields as described in Defining Instance Data Fields . These are declared as normal fields and are described below. Also, generic hints are implemented as variable data, so they are added to an object in the same way as other variable data types.

Object Trees

All objects declared in a static tree (e.g., your application's generic tree) should be in the same source file. If they are in different files, then they may be joined into a single tree only by dynamically adding objects from one file as children to objects of the other.

Note that if one file contains a tree of objects, then you may incorporate the whole tree by simply dynamically adding the top object in the file to the main tree. You won't have to add each object individually.

If an object declared in one source file will send a message to an object in another source file, you must include an @extern line in the source file containing the sending object:

@extern object ReceivingObjectName;

The message itself should be sent in the following manner (with variations possible if you will be using @call , passing arguments, or what have you):

optr ROOptr;
ROOptr = GeodeGetOptrNS(@ReceivingObjectName);
@send ROOptr::MSG_DO_SOMETHING(0, 0);

GeodeGetOptrNS() returns the object pointer for the object. In the case of a multiply-launchable program, Goc has no way of knowing which program's @ReceivingObjectName should receive the message--by using GeodeGetOptrNS() , you ensure that the object getting the message will be in the same geode as the object calling the routine.

Declaring Children

If an object is to be part of an object tree, its class (or one of its superclasses) must have at least an @link instance data field as does GenClass . If the object is to be allowed to have children, it must also have a field of type @composite . These allow Goc to automatically and correctly link the tree together.

As described in Defining Instance Data Fields , the @composite field points to the first child of an object, and the @link field points either to the object's next sibling or back to the parent if there is no next sibling. However, all child declaration is done completely in the composite field when using @object . The format is as follows:

<fname> = <childName> [, <childname>]* ;
fname
This is the name of the field defined with @composite in the class definition.
childName
This is the name of an object to be a child of this object. The star symbol indicates that one or more children may be included in the declaration line--they should be separated by commas, and each child must also be declared with an @object declaration.

There are many examples of this in the sample applications. Some simple examples are shown below.

GI_comp = @TicTacPrimary;
GI_comp = @TicTacView, @TicTacGameMenu;
VCI_comp = @TTX1, @TTX2, @TTX3, @TTX4, @TTX5, @TTO1,
         @TTO2, @TTO3, @TTO4, @TTO5;

Declaring Visual Monikers

For an object to have a visual moniker, it must have an instance data field of type @visMoniker as GenClass does (see Defining Instance Data Fields ). If you are in fact working with GenClass' GI_visMoniker field, you might want to consult its description in the GenClass chapter.

Visual monikers may take many forms, and the declaration line changes depending on which form you are using. The form ranges from a simple text field to a complex list of possible monikers based on video resolution. Each of these forms and the declaration line for it is given below.

The following form is used for simple text strings (shown with example):

<fname> = <string>;
GI_visMoniker = "One";
fname
The name of the moniker's instance data field.
string
A string of text enclosed in quotation marks. This string is the visual moniker for the object.

The following form is used for text strings that have a keyboard navigation character. A keyboard navigation character can be a character enclosed in single quotation marks, a numerical constant, or a text string representing some constant defined with #define . If it's a text string, the string is passed on for the C compiler to parse. The form is shown below with an example.

<fname> = <nav>, <string>;
GI_visMoniker = 'V', "View";
fname
The name of the moniker's instance data field.
nav
A keyboard navigation character represented as described above.
string
A text string enclosed in quotation marks.

The following form is used when a list of monikers is required. Most frequently, this form is used when defining an application's icons; one moniker for each potential screen resolution will be included in the list. The form is shown below, along with an example.

<fname> = list { <nameList> };
GI_visMoniker = list {
 @DocDirButtonSCMoniker,
 @DocDirButtonSMMoniker,
 @DocDirButtonSCGAMoniker
}
fname
This is the name of the moniker's instance data field.
nameList
This is a list of resource data chunk names separated by commas. Each chunk can be defined with the @chunk or @visMoniker keyword.

It is possible when declaring a list of visual monikers to have each moniker within a chunk or to declare each moniker with the @visMoniker keyword. If used on its own line, this keyword takes the form

@visMoniker <fname> = <init>;

The fields are the name of the moniker (same as that specified in the moniker list) and the moniker data. The visual moniker will be put in its own chunk just as if the moniker had been declared with the @chunk keyword, but using @visMoniker often is clearer and easier.

Visual monikers and strings presented by a program's UI are often accompanied by localization information. By placing a @localize keyword after a @visMoniker or @chunk construction, you can provide instructions for whoever uses the ResEdit program to localize your program (i.e., to translate its strings into a foreign language). This keyword can use any syntax from the following list:

@localize { <string> <min>-<max> };
@localize { <string> <length> };
@localize { <string> };
@localize <string>;
@localize not;
string
A string of explanatory text. It should describe the moniker or chunk's contents.
min
Minimum length of the moniker text or string.
max
Maximum length of the moniker text or string.
length
Length of the moniker text or string.
not
Keyword specifying that the moniker or chunk is not localizable.

Declaring GCN List Assignments

GEOS employs a "General Change Notification" (GCN) system which allows objects to register for notification of certain types of events. For example, some applications may want to notify for changes in the file system (file deletes, moves, copies, etc.); some objects may require notification each time the selection changes (e.g., a PointSizeControl object needs to know when the user changes a text selection). For further information, see the General Change Notification chapter.

While many objects will add themselves to and remove themselves from GCN lists dynamically, others will need to be placed on certain GCN lists in their definitions. For this, the gcnList keyword (the only one not preceded by the marker @ ) is used. Its format is shown below:

gcnList(<manufID>, <ltype>) = <oname>,+;
manufID
This is the manufacturer ID of the manufacturer responsible for creating the particular list type. It is used to differentiate different manufacturers who may use the same list type enumerator. In many cases, this will be the type MANUFACTURER_ID_GEOWORKS.
ltype
This is the name of the GCN list. Most that you will use are defined by GenApplication and begin GAGCNLT_.... All the list types are defined in the GenApplication chapter in the Objects book.
oname
This is a list of objects that should be on the GCN list. The objects are separated by commas.

Declaring Keyboard Accelerators

A keyboard accelerator acts as a "hot key," invoking the object's default action when pressed. The accelerator character is defined in an instance field declared with @kbdAccelerator as shown in Defining Instance Data Fields . The form of declaration follows.

<fname> = [@specificUI] <mod>* <key>;
fname
This is the name of the @kbdAccelerator instance data field.
@specificUI
This is a Goc keyword that, when used in this declaration, allows your program to use a key combination reserved for the specific UI. This can have undefined consequences and is strongly discouraged.
mod
This is a modifier key. You may put any or all of the modifiers here, separated by spaces. The four modifiers are alt , control , ctrl , and shift .
key
This is either a character enclosed in single quotes (e.g., `k') or a numeric key constant (e.g., C_CTRL_A or 0x57).

Using Default Values

Often an object will be declared with the default values of an instance data field. Other times, especially with generic UI object attributes, an object will have the default values with one or two modifications. In the first case, the instance data field does not need to be addressed at all; the default value will be used if no declaration is made.

In the second case, however, you must use the @default keyword to get the default values. If modifications are made and @default is not used, all default values will be lost. This is normally used only with bitfield-type attributes, and modifications are made with bitwise operators. The use of @default is shown below.

<fname> = @default [<op> [~]<attr>]*;
fname
This is the name of the instance data field.
op
This is a bitwise operator. If adding an attribute to the default, use the bitwise OR operator (|); if removing an attribute, use the bitwise AND operator (&) with the inverse of the attribute (see below). One operator is required for each attribute added or removed. The priorities of the operators are the same as in the standard C programming language.
~
This is the bitwise operator NOT. If removing an attribute using the bitwise AND operator, you should include the NOT symbol in front of the attribute.
attr
This is the name of the attribute being added or removed.

Code Display 5-17 Declaring Objects with @object

    /* This example shows the use of @start, @object, @visMoniker, and @end.
     * It is taken from the TicTac sample application. */
@start AppResource;
    /* The AppResource resource block contains the TicTacApp
     * object and its visual moniker chunk. */
@object GenApplicationClass TicTacApp = {
    GI_visMoniker = list { @TicTacTextMoniker };
    GI_comp = @TicTacPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS,GAGCNLT_WINDOWS) = @TicTacPrimary;
}
@visMoniker TicTacTextMoniker = "TicTacToe";
@end AppResource
@start Interface;
    /* The Interface resource declares TicTac's primary window and other UI
     * gadgetry. Only the GenView from this application is shown. */
@object GenViewClass TicTacView = {
    GVI_content = @TicTacBoard;					/* A relocatable optr field */
    GVI_docBounds = {0, 0, BOARD_WIDTH, BOARD_HEIGHT};
                    /* A Rectangle structure */
    GVI_color = { C_BLUE, 0, 0, 0 };					/* A ColorQuad structure */
    GVI_horizAttrs = @default | GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT
                & ~GVDA_SCROLLABLE;
        /* The NO_LARGER and NO_SMALLER attributes are set in the
         * field, and SCROLLABLE is cleared. The SCROLLABLE attribute
         * is not set by default for the GenView; it is shown here
         * for illustration. */
    GVI_vertAttrs = @default | GVDA_NO_LARGER_THAN_CONTENT
               | GVDA_NO_SMALLER_THAN_CONTENT;
}
@end Interface
| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Defining Methods | Next: 4.5 Sending Messages


Sending Messages

GEOS Programming: 4.5 Using Classes and Objects: Sending Messages

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Declaring Objects | Next: 4.6 Managing Objects
@send, @call, @callsuper, @record, @dispatch, @dispatchcall, TravelOption, ObjDuplicateMessage(), ObjFreeMessage(), ObjGetMessageInfo()

Often you will have to send messages to objects throughout the system. You can send messages in several ways, but the two most basic and most frequently used involve the keywords @call and @send .

If a message is being sent across threads, you must be aware of synchronization issues. If the message does not cross thread boundaries, the kernel will link the code directly as if it were a function call. (This is an implementation difference only; you do not have to specify anything different in your code.)

The @send keyword causes the kernel to put the specified message into the recipient's event queue. Messages sent with @send may not give return values and may not take pointers to locked memory as arguments. The sender then continues executing without ever knowing whether the message was properly handled or not.

The @call keyword is used when the message being sent must return information to the sender. It is also used when the message must be handled immediately, before the sender is allowed to continue executing. In essence, the sender is "put to sleep" until the message has been processed, at which time the sender is woken up and may continue executing. If the message sent with @call is not handled (passed up the recipient's class tree and still not handled), it will return as if it had been; no error message will be returned.

The formats for @send and @call are similar. Use them like function calls. Their format is given below:

@send [,<flags>]+ \
        <obj>::[{<cast2>}]<msg>(<params>*);
<ret> = @call [,<flags>]+ [{<cast>}] <obj>::\
            [{<cast2>}]<msg>(<params>*);
flags
This is a list of flags separated by the commas. The allowed flags are shown below.
obj
This is the name of the object to which the message will be sent. It can also be an optr variable.
msg
This is the name of the message being sent.
params
This is a list of parameters, built exactly as it would be for a standard C function call.
ret
This is a variable that will hold the return value, if any. Note that this is valid only with @call because @send does not return anything.
cast
If a message name is put here, Goc will automatically cast the return type to whatever type is returned by cast .
cast2
If a message name is put here, Goc will assume that the message is passed the same arguments as message cast2 .

The flags allowed for these keywords are listed below. They are rarely used but are available.

forceQueue
This flag will cause the message to be placed in the recipient's event queue, even if it could have been handled by a direct call. Do not use this flag with @call .
checkDuplicate
This flag makes the kernel check if a message of the same name is already in the recipient's event queue. For this flag to work, forceQueue must also be passed. Events are checked from first (next-to-be-processed) to last. If the message is a duplicate, it will be dropped; however the replace flag, described below, can change this behavior.
checkLastOnly
This flag works with checkDuplicate , causing it to check only the last message in the event queue.
replace
This flag modifies checkDuplicate and checkLastOnly by superseding the duplicate (old) event with the new one. The new event will be put in the duplicate's position in the event queue. If a duplicate is found but the replace flag is not passed, the duplicate will be dropped.
insertAtFront
This puts the message at the front of the recipient's event queue.
canDiscardIfDesperate
This flag indicates that this event may be discarded if the system is running extremely low on handles and requires more space immediately.

The @call command can also be used within an expression in the same way a function call could. For example, the following conditional expression is valid:

if (@call MyObj::MSG_MYOBJ_TEST()) {
    /* conditional code */
}

The result of the message call will be evaluated in the if statement. Note that this may not be done with @send because it returns nothing.

Nested Message Calls

Because of the way Goc processes message calls, it is impossible to nest messages on a single line. For example, this call is illegal:

@send Obj1::MSG_THATS_PASSED_AN_INT(\
        @call Obj2::MSG_THAT_RETURNS_INT());

Any such call will generate a compile-time error. Instead, you should use temporary variables to break this up into several lines, e.g.:

int i;
i = @call Obj2::MSG_THAT_RETURNS_INT();
@send Obj2::MSG_THATS_PASSED_AN_INT(i);

Sending a Message to an Object's Superclass

Often you may wish to send a message directly to an object's superclass to ensure that default behavior is implemented. Use the @callsuper keyword with the following format:

@callsuper <obj>::<class>::<msg>(<pars>*) [<flgs>+];
obj
This is the object to send the message to, as in @call and @send . The object block must already be locked, and must be run by the current thread of execution. (Usually an object uses @callsuper() to send a message to itself.)
class
This is the class whose superclass should receive the message.
msg
This is the message name.
pars
This is the parameter list, same as @call and @send .
flgs
This is the flags list, same as @call and @send .

When used on a line by itself (with no parameters or return values), @callsuper() passes a received message on to the superclass. This is used quite often when a subclass wants to alter existing behavior rather than replace it.

Encapsulating a Message

By encapsulating messages, you can set up events to be sent out at a later time. An encapsulated message can include the message to be sent, the object it should be sent to, and the parameters that should be passed. Using encapsulated messages can sometimes simplify coding.

Messages can be encapsulated with the @record keyword and later dispatched with @dispatch and @dispatchcall . (Though the use of @record does not necessitate a later @dispatch --there are other uses for an encapsulated event.) In addition, when the event is dispatched, you can override the values set in the encapsulated event to change the destination or the message. You can also cast the return value to another type if necessary. The formats of these three keywords are as follows:

<event> = @record <obj>::[{<cast>}]<msg>(<params>*);
event
The handle of the recorded event, of type EventHandle .
obj
The object set to receive the message when it is dispatched. This field may be set to null if the destination is determined when the message is dispatched.
cast
The name of a message; if this is present, the event will have the same argument types as the specified message.
msg
The message set to be sent when the event is dispatched. This field may be set to null if the message is determined when it is dispatched.
params
The parameter list (same as in @call and @send ) that will be sent with the dispatched message.

You may create a copy of a recorded message by means of the ObjDuplicateMessage() routine. To free it, call ObjFreeMessage() . To discover the Message and destination object associated with a recorded event, call ObjGetMessageInfo() .

The @dispatch keyword is used to dispatch an encapsulated event to its destination. This is similar to @send in that it can have no return values. If the event has return values, use @dispatchcall (below).

@dispatch [noFree] \
        <nObj>::[{<cast>}]<nMsg>::<event>;
noFree
This flag indicates that the event's handle should not be freed after the event has been dispatched. This is useful if the same encapsulated event is to be used more than once.
nObj
This is an override destination. If the destination in the encapsulated event is null, then an object must be set here. Any object set will override that in the encapsulated message. If no override object is desired, set this to null .
nMsg
This is an override message. If set, this message will be sent rather than that in the encapsulated event. If no override message is desired, set this to null . Any override will be sent with the same parameters as set in the encapsulated event.
event
This is the handle of the encapsulated event. This may not be a classed event.

The @dispatchcall keyword works exactly like the @dispatch keyword above except that it allows the use of return values. The sender will be "put to sleep" if necessary while the recipient processes the message and will be "woken up" when the message returns.

<ret> = @dispatchcall [noFree] [{<cast>}] <nObj>::\
                                    <nMsg>::<event>;
ret
This is a variable that will contain the return value of the message.
other parameters
All the other parameters are the same as those in @dispatch .

Using Expressions with Messages

All message-sending keywords described in the previous sections-- @call , @send , @record , @dispatch , and @dispatchcall --can take expressions in place of a destination object's name. Additionally, the @dispatch and @dispatchcall keywords can take expressions in place of the message name. However, if an expression is used for the message, you must use a cast type to make sure Goc knows the return and parameter types. Note, however, that casts in this case use curly braces rather than parentheses.

Casting Message Call and Return Types

Goc allows you to cast a message's pass and return values. This is best explained by example:

{
 int swapInt;
 char c;
 c = @call {MSG_1} object:: {MSG_2} MSG_X(swapInt);
}

In this case, MSG_2 takes an integer argument and MSG_1 returns a char. The casts tell Goc how MSG_X will receive parameters and return results. Goc needs the casts in those cases where MSG_X doesn't appear explicitly (perhaps it has been stored as an integer), and thus Goc would not be able to parse the parameters or return values.

When Goc tries to determine proper parameters and returns, it will look to the following sources when available. When trying to figure out parameters, it will look first for MSG_2, then MSG_X, and MSG_1 last. The first one Goc finds will determine the parameters.

@send and @record don't support return values, but on a @call , Goc will figure out return values by looking at MSG_1, MSG_X, and finally MSG_2.

In this case, Goc will pass to fn's method like MSG_CAST_2 but will return values as MSG_CAST_1 does:

Message fn = GetMessageToCall();
c = @call {MSG_CAST_1} myObj:: {MSG_CAST_2} fn(x);

Now we pass to MSG_B like MSG_CAST_2, but return like MSG_B:

 c = @call myObj:: {MSG_CAST_2} MSG_B(swapInt);

Classed Events and Travel Options

MSG_META_SEND_CLASSED_EVENT, TravelOption

You do not always need to specifically include an object as a destination for your message. In many cases, you may be able to generically address your message using a classed event . A classed event consists of a pre-defined TravelOption enumerated type and an EventHandle recorded using the @record Goc construct. The event in this case does not contain a specific object, but rather an object class.

The TravelOption acts as a navigator, determining along what path the message should be delivered. The object class within the classed event acts as a filter, determining if the object sent the message is an object of that class. If the object first encountered along the path dictated by the TravelOption is not a matching class, the classed event is passed to the next object on that path. When the class finally matches, the message will be handled by that object.

The classed event is originally dispathed from the object by sending MSG_META_SEND_CLASSED_EVENT. This message is usually sent by an object to itself. The travel options available depend on the class(es) of the object receiving the message. That is, the object receiving the message must be a sub-class of one of the following:

  • MetaClass (Usable by all GEOS object classes)
TO_NULL
Indicates that no object is to receive the message. The default case in this case is to destroy the classed event that is dispatched,
TO_SELF
Indicates that the message should be delivered to itself. If it is not handled there, it will be destroyed.
TO_OBJ_BLOCK_OUTPUT
Each GEOS object block contains a default output stored in the object block header.This TravelOption indicates that the message should be delivered to the object block's output optr.
TO_PROCESS
Indicates that the message should be delivered to the application's ProcessClass object.
  • GenClass (Usable by all generic objects)
    For a discussion of the Focus, Target and Model hierarchies, see the Input chapter.
TO_GEN_PARENT
Indicates that the message should be delivered the object's generic parent.
TO_FOCUS
Indicates that the message should be delivered along the object's focus hierarchy, following the focus links from child to child.
TO_TARGET
Indicates that the message should be delivered along the object's target hierarchy, following the target links from child to child.
TO_MODEL
Indicates that the message should be delivered along the object's model hierarchy, following the model links from child to child.
TO_APP_FOCUS
Indicates that the message should be delivered along the focus hierarchy, starting at the root GenApplication object and following the focus links from child to child.
TO_APP_TARGET
Indicates that the message should be delivered along the target hierarchy, starting at the root GenApplication object and following the target links from child to child.
TO_APP_MODEL
Indicates that the message should be delivered along the model hierarchy, starting at the root GenApplication object and following the model links from child to child.
TO_SYS_FOCUS
Indicates that the message should be delivered along the focus hierarchy, starting at the root GenSystem object and following the focus links from child to child.
TO_SYS_TARGET
Indicates that the message should be delivered along the target hierarchy, starting at the root GenSystem object and following the target links from child to child.
TO_SYS_MODEL
Indicates that the message should be delivered along the model hierarchy, starting at the root GenSystem object and following the model links from child to child.
  • VisClass (Usable by all visible objects)
TO_VIS_PARENT
Indicates that the message should be delivered to the object's visible parent.
  • GenApplicationClass (Usable by all Application objects)
TO_PRINT_CONTROL
Indicates that the object should be delivered to the application object's print control object.

To send a classed event, you must first record the event, specifying the message to send and the object class to handle the message. Then you must send MSG_META_SEND_CLASSED_EVENT--usually to yourself-- passing that handle and a TravelOption .

<event> = @record <objClass>::<msg>
@send self::MSG_META_SEND_CLASSED_EVENT(<event>,
                        <TravelOption>);

Code Display 5-18 Sending a Classed Event

/*
 * First record the Classed event. In this case, we want an object of class
 * `MyFooClass' to handle MSG_FOO. We will send this classed event along the
 * target hierarchy. We want to begin the path at the application object, so we
 * will use the TO_APP_TARGET TravelOption.
 */
event = @record MyFooClass::MSG_FOO;
/*
 * Then dispatch the classed event using MSG_META_SEND_CLASSED_EVENT.
 */
@send self::MSG_META_SEND_CLASSED_EVENT(event, TO_APP_TARGET);

Message Shortcuts

All messages, when received, contain three basic parameters: the message number ( message ), the optr of the recipient ( oself ), and a far pointer to the recipient's locked instance chunk ( pself ). This allows several shortcuts and shorthand formats for use within methods:

@callsuper;

When used in a method as above, the @callsuper keyword passes the received message up to the object's superclass. Use this whenever subclassing a message when the default functionality must be preserved.

 <ret> = @call self::<msg>(<params>*);

Any object can send a message to itself using @call and "self" as the destination. The remainder of the command is the same as a normal @call .

 <ret> = @call process::<msg>(<params>*);

Any object can send a message to its Process object with @call and "process" as the destination. (The Process object is the object of class ProcessClass .) The remainder of the command is the same as a normal @call .

 <ret> = @call application::<msg>(<params>*);

Any object can send a message to its Application object (of class GenApplicationClass ) with @call and "application" as the destination. The remainder of the command is the same as a normal @call .

 <ret> = @call @visParent::<msg>(<params>*);

Any object in a visible tree can use @visParent as the destination of an @call command. The message will be sent to the object's parent in the visible object tree. The remainder of the command is the same as a normal @call .

 <ret> = @call @genParent::<msg>(<params>*);

Any object in a generic tree can use @genParent as the destination of an @call command. The message will be sent to the object's parent in the generic object tree. The remainder of the command is the same as a normal @call .

 @send @visChildren::<msg>(<params>*);

Any composite object in a visible object tree (therefore a subclass of VisCompClass ) can send a message that will be dispatched at once to all of its children. Any message sent with @visChildren as the destination must be dispatched with the @send keyword and therefore can have no return value.

 @send @genChildren::<msg>(<params>*);

Any composite object in a generic object tree (therefore a subclass of GenClass ) can send a message that will be dispatched at once to all of its children. Any message sent with @genChildren as the destination must be dispatched with the @send keyword and therefore can have no return value.

In addition to the above shortcuts, you may also pass the optr of an object using @<obj> , where <obj> represents the name of the object. This syntax gets translated by Goc into (optr)&<obj> ; this is similar to using the ampersand (&) to pass a pointer.

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Declaring Objects | Next: 4.6 Managing Objects


Managing Objects

GEOS Programming: 4.6 Using Classes and Objects: Managing Objects

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Sending Messages

In addition to knowing how to declare objects and classes, you need to know how to manage objects during execution. This includes instantiating new objects, deleting objects, saving object state, and moving objects around object trees.

Both the kernel and MetaClass (the topmost class in any class hierarchy) have routines and methods to create, manage, and destroy objects. You will probably not have to or want to use all these routines and methods, but understanding what they do and how they work can help you understand the object system as a whole.

Creating New Objects

ObjDuplicateResource(), ObjInstantiate(), MSG_META_INITIALIZE, MSG_GEN_COPY_TREE

You can introduce objects to the system in four basic ways. Each of these has benefits and drawbacks, and each has an appropriate place and time for use. It is unlikely, however, that you will use all four different means.

Storing Objects in a Resource Block

This is a common and simple way to introduce objects to the system. The Hello World sample application uses this method of creating and loading objects. Resource blocks are contained in your geode's executable file (the .geo file) and are automatically loaded when accessed. These resources may also be tagged for saving to state files automatically.

Setting up an object resource is simply a matter of defining the resource and using the @object keyword to define each object in the resource. The object resource block is automatically created and put in your executable file. Each object defined with @object is allocated a chunk and a chunk handle within the resource block. Because both the chunk handle and the handle of the resource block are known, accessing individual objects in the resource is simple. In essence, when you set up an object resource, you don't need to worry about bringing the objects into the system or shutting them down.

Using a resource for objects has limitations. Objects loaded from resources are always loaded with the same characteristics. This can be a problem if you need to have several different copies of a certain set of objects, and each copy can be changed. In this case, you would duplicate the resource (see below) before accessing the objects within it.

For an example of objects defined by means of declaring them with the @object keyword within an object resource, see Declaring Objects with @object .

To define an object resource, you must know what objects you'll require before your geode is launched. Some complex programs will dynamically instantiate individual objects or entire trees without knowing previously what objects will be required. To do this, you'll need to use ObjInstantiate() (see below) for instantiating individual objects.

Duplicating an Object Block Resource

This is another common method employed by both the User Interface and applications. It provides the simplicity of the object resource model (above) while allowing you to have several different copies of the resource. Thus, it's perfect if you want to use templates for your object blocks (this is what the Document Control object does, as shown in the Tutorial application (see APPL\TUTORIAL\MCHRT4\MCHRT.GOC)).

First, you must set up an object resource in your code file with @start , @end , and @object . In the .gp file, mark the resource discard-only . The objects in such a "template" resource should not be linked to any object outside the block. Generic object branches created in this manner should have their topmost object marked as not usable (~GS_USABLE); this is because it is illegal for a generic object to be usable without having a generic parent. Instead of accessing these objects directly, you should duplicate the resource block. (A resource can not be both duplicated and used directly.) This is done with ObjDuplicateResource() , which allocates a new block on the heap, sets it up properly, and copies the resource directly into it.

You are returned a handle to the new object block, which you can then modify any way you like. Because all the chunk handles of all the objects will be the same as in the source block, you can easily access any object in the duplicate. Once copied, the duplicate objects may be added to your generic tree and then set GS_USABLE. And, by using ObjDuplicateResource() more than once on the same resource, you can have several different, possibly modified versions of the resource at once.

As with using resource blocks, however, you must know the configuration of all your template objects beforehand. You may still need to add new objects to the resource or dynamically create other objects. This is the primary drawback of this method.

Additionally, if you duplicate resource blocks, you should also free them when they're not needed any more. Generic objects in the block should be set not usable and then removed from the tree before the resource is freed. Freeing should be done by sending MSG_META_BLOCK_FREE to any object in the block or by calling ObjFreeObjBlock() . Use of the kernel routine ObjFreeDuplicate() is not recommended as it requires all objects in the block to be thoroughly clean of any ties to the system. ( MSG_META_BLOCK_FREE and ObjFreeObjBlock() ensure that the objects have had adequate time to relinquish these ties first.)

Instantiating an Individual Object

The most complex of these three options, this can provide the flexibility needed for all cases. The actual act of instantiating an object is not difficult or very complex. However, it is time and labor intensive and requires several steps that are not necessary when using object resources. In addition, cleaning up after objects created in this manner is more complex.

To create a new object on the fly, you first must set up a place to put it. To do this, allocate a memory block on the global heap (you can instead use an existing object block, of course) and set it up with the proper flags and header to be an object block. There is no need to lock the chosen block on the heap. The block is now set up to receive the new object.

To actually create the new object, call the kernel routine ObjInstantiate() . This will lock the block on the heap, create a chunk in the object block and zero the instance chunk. If the object is of a class with master parts, the instance chunk will remain uninitialized until the first time a class in the master group receives any message. If the class is a direct descendant of MetaClass , the instance chunk will be immediately initialized to default values with MSG_META_INITIALIZE . If you want to change or add to the default data of this type of object, subclass MSG_META_INITIALIZE ; be sure to call the superclass first. To initialize any master group of an object, send it a classed event that will be handled by a class in that master level.

After calling ObjInstantiate() , the block will be automatically unlocked. An example of instantiating a new object is shown in Instantiating an Object . Generic objects created this way may then be added to a generic tree and set usable. They may be destroyed using MSG_GEN_DESTROY .

Code Display 5-19 Instantiating an Object

/* This sample of code belongs to a sample GenInteraction object (the object would
 * have to be a subclass of GenInteractionClass). It does several things:
 *	1.	It instantiates a new GenTrigger object. The new
 *		GenTrigger will be added to the same object block
 *		containing the GenInteraction handling the message.
 *	2.	It adds the new GenTrigger as a child of the
 *		SampInteraction (the handling object).
 *	3.	It sets the GenTrigger usable and enabled. */
@method SampInteractionClass, MSG_SAMP_INTERACTION_CREATE_TRIGGER {
    optr 		newTrig;
    newTrig = ObjInstantiate(OptrToHandle(oself),
                (ClassStruct *)&GenTriggerClass);
    /* The two parameters are the handle of an object block and the
     * pointer to a class definition. The object block is the same
     * one containing the GenInteraction, whose optr is contained in
     * the standard oself parameter. The class structure pointer points
     * to the class definition of GenTriggerClass. */
    /* Now set the trigger as the last child of the GenInteraction. */
    @call self::MSG_GEN_ADD_CHILD(newTrig, (CCF_MARK_DIRTY | CCF_LAST);
    /* Now set the trigger usable and enabled. */
    @call newTrig::MSG_GEN_SET_USABLE(VUM_DELAYED_VIA_UI_QUEUE);
    @call newTrig::MSG_GEN_SET_ENABLED(VUM_NOW);
}

Copying a Generic Tree

The fourth way to create new objects is by using the message MSG_GEN_COPY_TREE . This, when sent to a generic object in a generic tree, copies an entire generic tree below and including the object into another, pre-existing object block.

This is an easy way to copy generic trees, one of the more common purposes of creating new objects. However, it only works with the generic objects (with a superclass GenClass ). Trees created using MSG_GEN_COPY_TREE can be destroyed with MSG_GEN_DESTROY .

For an example of MSG_GEN_COPY_TREE use, see the SDK_C\GENTREE sample application.

Working With Object Blocks

ObjIncInUseCount(), ObjDecInUseCount(), ObjLockObjBlock(), ObjFreeObjBlock(), ObjFreeDuplicate(), ObjTestIfObjBlockRunByCurThread(), ObjBlockSetOutput(), ObjBlockGetOutput()

Once you have an object block created, either with ObjDuplicateResource() or with the memory routines, there are several things you can do with it. It may be treated as a normal memory block, but there are also several routines for use specifically with object blocks:

ObjIncInUseCount() and ObjDecInUseCount() increment and decrement an object block's in-use count (used to ensure the block can't be freed while an object is still receiving messages). ObjLockObjBlock() locks the object block on the global heap. ObjFreeObjBlock() frees any object block. ObjFreeDuplicate() is the low-level routine which frees an object block created with ObjDuplicateResource() . ObjTestIfObjBlockRunByCurThread() returns a Boolean value indicating whether the calling thread runs a given object block. ObjBlockSetOutput() and ObjBlockGetOutput() set and return the optr of the object set to receive output messages (i.e., messages sent with travel option TO_OBJ_BLOCK_OUTPUT) from all the objects within the object block.

Working With Individual Objects

ObjIsObjectInClass(), ObjIsClassADescendant(), ObjGetFlags(), ObjGetFlagsHandles(), ObjSetFlags(), ObjSetFlagsHandles(), ObjDoRelocation(), ObjDoUnRelocation(), ObjResizeMaster(), ObjInitializeMaster(), ObjInitializePart()

The kernel supplies several routines for working with and modifying individual object chunks and object data. These are all described fully in the Routine Reference Book; most are not commonly used by applications.

ObjIsObjectInClass() takes a class and an optr and returns whether the object is a member of the class. A related message, ObjIsClassADescendant() determines whether a given class is a subclass of another. ObjGetFlags() returns the object flags for a given object instance chunk; ObjSetFlags() sets the flags to passed values. ObjGetFlagsHandles() and ObjSetFlagsHandles() refer to objects by means of handles instead of optrs. ObjDoRelocation() processes any passed instance data fields in the object declared as relocatable; ObjDoUnRelocation() returns the passed relocatable fields to their index values.

ObjInitializeMaster() causes the system to build out a particular master group's instance data for an object. ObjInitializePart() causes the system to build all master groups above and including the passed level. (This will also resolve variant classes.) ObjResizeMaster() resizes a given master part of the instance chunk, causing the chunk to be resized.

Managing Object Trees

ObjLinkFindParent(), ObjCompAddChild(), ObjCompRemoveChild(), ObjCompMoveChild(), ObjCompFindChildByOptr(), ObjCompFindChildByNumber(), ObjCompProcessChildren()

Many objects will be part of object trees. Nearly all generic UI and visible objects exist as members of trees for organizational purposes. Object trees can be useful, powerful, and convenient mechanisms for organizing your objects.

An object tree is made up of "composite" objects--objects which may or may not have children. The distinguishing characteristic of a composite object is that it has one instance data field declared with the @composite keyword and another declared with the @link keyword. The @composite field contains a pointer to the object's first child in the tree, and the @link field contains a pointer to the object's next sibling.

If you set up an object resource block containing composite objects, it's very easy to set up an object tree. Your generic UI objects are declared in a tree with the GenApplication object at its head. Additionally, it's easy to alter an object tree once it's been created. The kernel provides several routines, and MetaClass uses several messages for adding, removing, and moving objects to, from, and within trees.

  • ObjLinkFindParent()
     This routine finds the optr of the calling object's direct parent. The kernel traverses the link fields until it returns to the parent object.
  • ObjCompFindChildByOptr()
     This routine returns the number of the child (first, second, third, etc.) whose optr is passed. The child must exist and must be a child of the calling object.
  • ObjCompFindChildByNumber()
     This routine returns the optr of the child whose number (first, second, etc.) is passed.
  • ObjCompAddChild()
     This routine takes an object's optr and adds it to the caller's list of children. Depending on the flags passed, the child may be inserted in any child position (first, second, etc.).
  • ObjCompMoveChild()
     This routine takes a child object and moves it to a new position. However, it will still remain a child of the calling object. If you want to move the child to be a child of a different object, you must first remove it from the tree altogether and then add it to the other parent.
  • ObjCompRemoveChild()
     This routine removes a child object from the object tree.
  • ObjCompProcessChildren()
     This routine calls a callback routine for each child of the calling object in turn. The callback routine may do virtually anything (except destroy the object or free its chunk or something similar).

By using the above routines, you can fully manipulate any object tree and the objects within it.

Detaching and Destroying Objects

MSG_META_DETACH, MSG_META_DETACH_COMPLETE, MSG_META_ACK, MSG_META_OBJ_FLUSH_INPUT_QUEUE, MSG_META_OBJ_FREE, MSG_META_FINAL_OBJ_FREE, ObjInitDetach(), ObjIncDetach(), ObjEnableDetach(), ObjFreeChunk(), ObjFreeChunkHandles()

While creating objects is actually quite simple, detaching and destroying them can be quite involved. For this reason, GEOS does most of the work for you, and in most cases you won't have to worry about what happens when your application is shut down or saved to a state file. However, if you instantiate individual objects of your own classes, you should be very careful about how your objects are detached from the system and destroyed.

Detaching objects involves severing all of the object's ties with the rest of the system. Destruction of an object entails actually removing the object's instance chunk and related structures, making sure that it has handled all its waiting messages.

Throughout its life, an object is likely to become involved with a number of other things--other objects, drivers, files, streams, the memory manager, the kernel--and each of these things may send the object messages from time to time. The task, when detaching an object from the system, is to sever all the object's ties with the outside world, to make sure that no other entity will ever try to contact the object again.

To those unfamiliar with these problems, they can be overwhelming. However, GEOS takes care of them for you in most situations. All generic and visible objects, all objects in object trees, and all objects that maintain an "active list" will automatically (in nearly all cases) have the detach functionality built in by MetaClass .

The Detach Sequence

When an object receives MSG_META_DETACH , it begins the detach sequence to sever all its ties with other entities in the system. It must first notify all its children and all the objects on its various notification lists that it will be going away (most often, all its children, by association, will also be detached). It then must clear its message queues. Finally, it must acknowledge its detachment to the object that originally sent MSG_META_DETACH . Each of these phases is described in detail below and is implemented by MetaClass . You have to do none of this work unless your object is truly a special case.

Detaching in conjunction with destruction is somewhat intricate because not only must the object notify all other interested parties, but it must also receive acknowledgment of the notice. Otherwise, the notification and some other message could be sent at the same time, and the object could be destroyed before the notification can be handled. (Destruction is discussed in the following section.)

Because any object may be put in the position of being detached and then immediately destroyed, it must send out notification and then wait until all the notices have been acknowledged before continuing with other tasks. The kernel and MetaClass implement a mechanism for this using four kernel routines. Again, you do not need to do this since all classes have MetaClass as their root.

First the object being detached (in its MSG_META_DETACH handler) calls the routine ObjInitDetach() . This tells the kernel that the object is initiating a detach sequence and that the acknowledgment mechanism must be set up. The kernel will allocate a variable data entry to hold a count of the number of notices sent and acknowledgments received.

After this, the object must send a MSG_META_DETACH or its equivalent to each of its children and each of the objects on its "active list." With each notice sent, the object must call ObjIncDetach() , which increments the notice count.

After sending all the notices, the object then calls the kernel routine ObjEnableDetach() . This notifies the kernel that all the notices have been sent and that the object is waiting for the acknowledgments.

Acknowledgment comes in the form of MSG_META_ACK and is received by the object being detached. MSG_META_ACK is handled by MetaClass and will decrement the notice count, essentially saying there are one fewer notices left to be received. When the final MSG_META_ACK is received (setting the notice count to zero) and ObjEnableDetach() has also been called, the kernel will automatically send a MSG_META_DETACH_COMPLETE to the object. This assures the object that it will never receive another message from another entity in the system.

The final step in the detach sequence is acknowledging that the object has been detached. In its MSG_META_DETACH_COMPLETE handler, the object should send a MSG_META_ACK to the object that originated the detach sequence. This will allow that object to continue with its detach sequence if it was involved in one; without this step, only leaves of object trees could ever be detached. This final step is provided in default handlers in MetaClass and is inherited by all objects.

To free a chunk, perhaps a chunk used to store an object, use the ObjFreeChunk() routine. Normally this will free the chunk; however if the chunk is storing an object from a resource, the chunk will instead be dirtied and resized to zero. To free a chunk referenced by handles, call ObjFreeChunkHandles() instead.

The Destruction Sequence

The destruction sequence must be initiated from outside and will begin when the object receives a MSG_META_OBJ_FREE . Often, the MSG_META_OBJ_FREE will be sent by the object to itself.

The destruction sequence consists of three steps: First, the object must clear out its message queues; even though it is detached and can not receive new messages, there may be some left over in the queue (an error if it occurs). Second, it must finish executing its code and working with its instance data. Third, it must free its instance chunk. Each of these steps is described below.

Even though the object has notified the rest of the system that it is going away, it still must flush its message queues of any unhandled messages. These messages could have been received between the time the original MSG_META_OBJ_FREE was received and notification was sent out (due to interrupts or context switching). To clear its message queues, the object must send itself a MSG_META_OBJ_FLUSH_INPUT_QUEUE , which will ensure that any messages in the queues are handled appropriately before the object shuts down. This step is handled automatically by the MSG_META_OBJ_FREE handler in MetaClass . You should never have to send this message, and indeed its use is discouraged.

To the outside world, the second and third steps seem like a single step. However, MSG_META_OBJ_FREE can not simply free the instance chunk after the queues are cleared; it must be able to access the instance chunk until all the method code has been executed. So, MSG_META_OBJ_FREE sends the final message to the object, MSG_META_FINAL_OBJ_FREE , and then exits. MSG_META_FINAL_OBJ_FREE waits a short while and then frees the object's chunk. This ensures that MSG_META_OBJ_FREE has finished and the chunk is not being used by any code.

Possible Pitfalls

If you're not careful, you can cause the detach mechanism to fail by instantiating an object on the fly and saving that object's optr. If the object is then detached and you don't account for the saved optr, you could later send a message to a nonexistent object. This has undefined results and can be nearly impossible to track down.

Note that objects created within resources and by ObjDuplicateResource() will almost always automatically be taken care of by the detach mechanism. Objects you create with ObjInstantiate() are the ones to be careful with.

Saving Object State

ObjSaveBlock(), ObjMarkDirty(), ObjMapSavedToState(), ObjMapStateToSaved()

Object state saving is almost entirely contained within the system. For the most part, only UI objects are saved to state files; however, you can mark other object blocks for saving. State saving is described in full in the Applications and Geodes chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Sending Messages


Libraries

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing

If you write more than one application, you may find yourself repeating a lot of effort. You may end up writing the same routines several times, or defining very similar classes for different applications.

For this reason, GEOS lets you define libraries . Libraries are much like applications. However, they don't do anything on their own; instead, they must be loaded by other geodes. They provide applications with routines and object classes. This lets applications share code easily and efficiently.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Design Philosophy
| One-page Version of this Chapter for Easy Printing


Design Philosophy

Libraries: 1 Design Philosophy

| Up: GEOS SDK TechDocs | Up | Prev: Libraries | Next: 2 Library Basics

GEOS libraries are designed to make code-sharing simple and efficient. They allow several different applications to make use of the same routines and classes while using the minimum amount of space.

Conventional libraries are fully included in each application. The usual technique is to write a header file which contains the code for the library's routines. Any application which needs to use the routines can then include this library. This has one main advantage: The code can be written and tested once, and applications can then rely on it to work. However, there is a severe drawback to this approach: every application which uses the library will contain identical code. This is not a problem for non-multitasking environments; if only a single application can run at a time, then there will be only a single copy of each library. In a multithreaded environment like GEOS, however, this is a very inefficient use of resources.

GEOS libraries solve this problem. Each geode specifies which libraries it will use, either at compile-time (in the .gp file) or at run-time (with GeodeUseLibrary() ). The kernel will see to it that the library is loaded when necessary. This means that if a dozen applications are all using the same library, the code needs only be loaded once.

Conventional libraries contain only routines. GEOS libraries, on the other hand, may contain both routines and object classes. There are several advantages to defining a class in a library instead of in an application. First, there is the same code-sharing benefit that routines have. If a class is defined in a library, the heap will contain at most one copy of each of the class's methods, no matter how many applications use objects from that class. There is another advantage as well; all applications which use that class can be sure that they are using objects whose definitions are identical. This makes it possible for applications to send messages to objects owned by other geodes.

Writing a library is very much like writing an application. There are only a few differences, which are covered in this appendix. You should already be familiar with writing applications before you try to write a library.

| Up: GEOS SDK TechDocs | Up | Prev: Libraries | Next: 2 Library Basics


Library Basics

Libraries: 2 Library Basics

| Up: GEOS SDK TechDocs | Up | Prev: 1 Design Philosophy | Next: 3 The Library Entry Point

A library is a geode, much like an application geode. However, its behavior is slightly different. In particular, libraries do not have any threads of their own, unless they explicitly create them.

When a geode calls a routine which is exported from a library, the routine is run by the thread which made the call, not by the library's thread. This has several implications. First, it means that a library's response time is not dependent on the number of applications which use the library. An application which uses the library a lot will do so on its own time and may have its priority reduced accordingly. Indeed, a library with many users is likely to perform better than one with few users, since its code will be less likely to be swapped out of the global heap. Similarly, library routines will use the stack of the calling thread; this means that the same routine can be called by several different threads at once, with less danger of a synchronization problem.

Another consequence is that if a library routine allocates memory, that memory will belong to the owning geode. Thus, when the application exits, the memory will automatically be freed; on the other hand, if the library exits before the application does, the memory block will remain. If a library wants to have the block owned by the library geode, it must set the owner explicitly.

Geodes which use a library are said to be its "clients." A client may declare that it uses a library in its .gp file, or it may load the library at runtime by calling GeodeUseLibrary() . One library may be a client of another; in this case, when the first library is loaded, the second will be as well.

A library may have a single special routine, known as its entry point . The kernel calls this routine to inform the library when it is launched or freed, when it acquires a new client, or when a client is unloaded. The entry point routine is described more fully in The Library Entry Point .

A library may export object classes or routines. If a routine is exported, it may be called by any geode which uses the library. If an object class is exported, any geode which uses the library may instantiate objects of that class, and may define a subclass of it.

Every library should have a library header file. This header file contains declarations for all exported routines and classes, as well as definitions of any appropriate macros, constants, structures, etc. Every geode which uses the library will need to include this header file. If the library exports any object classes, the header file should be a Goc header file with the suffix .goh ; otherwise it should be a standard C header file with the suffix .h . The header file is described in more detail in Header Files .

| Up: GEOS SDK TechDocs | Up | Prev: 1 Design Philosophy | Next: 3 The Library Entry Point


The Library Entry Point

Libraries: 3 The Library Entry Point

| Up: GEOS SDK TechDocs | Up | Prev: 2 Library Basics | Next: 4 Exported Routines and Classes
LibraryEntry(), LibraryCallType

A library may need to do bookkeeping when it is launched, when a client is attached, or at other times. For this reason, some libraries will have an entry point routine. The entry point routine is called by the kernel; it should never be called by other geodes. Some of the calls are made in the kernel thread, while others are made by a geode's thread. All of the calls are made automatically by the kernel.

An entry point routine must take two arguments. The format of an entry point is shown in A Library Entry Point .

Code Display C-1 A Library Entry Point

Boolean _pascal
    LibraryEntry(LibraryCallType				ty,
            GeodeHandle		client);

When the kernel calls the entry point routine, it passes the following arguments:

  • A member of the LibraryCallType enumerated type. This specifies why the kernel is calling the routine. This type is described below.
  • A geode handle. This parameter is valid only if certain LibraryCallType values are passed.

The entry point should return true if an error occurs; otherwise it should return false (i.e. zero).

LibraryCallType contains the following members:

LCT_ATTACH
This is passed when the library has just been launched. The client parameter is undefined. The call is made in the application's thread.
LCT_DETACH
This is passed when the library is about to be unloaded. The client parameter is undefined. The call is made in the application's thread.
LCT_NEW_CLIENT
A thread has just called GeodeUseLibrary() , or a geode which depends on the library is being launched. The client parameter contains the GeodeHandle of the new client. The call is made in the kernel thread.
LCT_NEW_CLIENT_THREAD
A geode which depends on the library has just spawned a new thread. The client parameter contains the GeodeHandle of the thread's owner. The call is made in the new thread.
LCT_CLIENT_THREAD_EXIT
A thread which uses the library is being destroyed. The client parameter contains the GeodeHandle of the thread's owner. The call is made in the soon-to-be destroyed thread.
LCT_CLIENT_EXIT
A client loaded this library with GeodeUseLibrary() has just called GeodeFreeLibrary() . The client parameter contains the GeodeHandle of the former client. The call is made in the kernel thread.

Sometimes a single action can prompt several calls to the entry point, each with a different LibraryCallType value. For example, suppose FooWrite is launched. This application's .gp file specifies that it uses the BarObj library. At the time FooWrite is launched, BarObj has not been loaded. The kernel will automatically launch BarObj and immediately call the entry point with parameter LCT_ATTACH. The kernel will then call the entry point again with parameter LCT_NEW_CLIENT, passing FooWrite's GeodeHandle . It will then call the entry point once for each FooWrite thread, passing LCT_NEW_CLIENT_THREAD; it will make these calls as each thread is started.

Some libraries will not need to take any actions when the entry point is called; these libraries need not have an entry point routine. On the other hand, some libraries will need to do bookkeeping chores. This is left entirely to the library's discretion.

The entry point should take care not to perform any actions with side effects outside the library. If the entry point allocates memory, it should make sure to make the library's geode the block's owner. Similarly, the entry point should not change the working directory; instead, it should use FilePushDir() and FilePopDir() to make temporary changes to the working directory.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Library Basics | Next: 4 Exported Routines and Classes


Exported Routines and Classes

Libraries: 4 Exported Routines and Classes

| Up: GEOS SDK TechDocs | Up | Prev: 3 The Library Entry Point | Next: 5 Header Files

Writing routines for a library is very much like writing them for an application. Simply export the routine in the .gp file and any geode which uses the library will be able to call the routine.

It is important when writing routines for export to document the routines exhaustively. Remember that the library will probably be used by other programmers; they will rely on the routines to behave exactly as specified. Exported routines should also minimize side effects; for example, it is a bad idea for a library routine to change the working directory without changing it back, unless that is the routine's main purpose.

Most libraries will have a number of routines which are not for export, but are used by routines that are exported. These are simply written normally, and are not exported in the .gp or declared in the header file. Remember that programmers will not see these routines; their side effects should thus be fully documented with the exported routines which call them.

Some libraries will declare classes of objects. In this case, the library should specify in the .gp file that it uses whichever library defines the superclass of the object. For example, if a library defines a subclass of GenClass , it should specify that it uses the UI library. It should then export the new class.

Some libraries will declare classes that are not intended to be used by clients. For example, the Impex library declares ImportExportClass . This class is never instantiated; it contains code and instance data that are used by its subclasses ( ImportControlClass and ExportControlClass ). Such "hidden" classes need not be exported. However, the classes must be fully declared in the header files so the subclasses can be defined accurately and consistently.

| Up: GEOS SDK TechDocs | Up | Prev: 3 The Library Entry Point | Next: 5 Header Files


Header Files

Libraries: 5 Header Files

| Up: GEOS SDK TechDocs | Up | Prev: 4 Exported Routines and Classes | Next: 6 Compiler Directives

Every library should have at least one header file. This file contains declarations and definitions which are needed by each of the geodes which uses the library.

If a library exports routines but does not export object classes, its header will be a standard C header file. This file should contain declarations of every exported routine. It should also contain the definitions of any macros, constants, structures, etc., which clients might use.

If a library exports object classes, its header will be a Goc header with the suffix .goh . In addition to routine declarations, it must contain the complete declarations of each of the exported object classes, including all the message declarations. A .goh file should begin with the Goc directive " @deflib < libname>", and end with the directive " @endlib ". This ensures that the header will only be included once, even if the code tries to include it several times.

Large libraries may have several header files. For example, a library might declare several similar object classes. It is usually simpler to write a separate header for each class; a client can then include only the headers for classes which it will use. Note that the header for a class should use a Goc @include directive to include the header for that class's superclass.

It is of the utmost importance that the headers be kept in synchronization with the libraries they describe. As a rule, a library will include each one of those headers; that helps to keep all the files compatible. Nevertheless, you must be careful whenever changing a library.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Exported Routines and Classes | Next: 6 Compiler Directives


Compiler Directives

Libraries: 6 Compiler Directives

| Up: GEOS SDK TechDocs | Up | Prev: 5 Header Files

Libraries have to be compiled slightly differently from applications. Since library routines are run under application threads, they must treat global variables differently than applications do. You must therefore add pragmas to ensure that the library is compiled correctly.

There are several steps to take:

  • The compiler should not to expect the ss register to be the same as ds .
  • The compiler must generate code to load the dgroup segment address into ds at the start of exported routines.
  • The compiler must set up semaphores or other data-synchronization structures for global variables.

Most compiler manuals have a section on compiling dynamically-linked libraries (DLLs) for Microsoft Windows; this section will describe how to set up these conditions. Note that you need only do this if your library will have its own global or static variables. If the library's routines and methods use only local, automatic variables, you need not perform these actions.

When you compile a library, you must pass the argument "-L <libname>" to Goc. Edit your local.mk file to make it insert this flag. For more information about the local.mk file, see Using Tools chapter.

Finally, if your library is written in C or Goc (or if its API is in C, even if some internal routines are written in assembly), you must indicate this by putting the flag c-api on the "type" line of the library's .gp file. This instructs the kernel to pass arguments on the stack (instead of in registers) when calling the entry point routine.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Header Files


Localization

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Localization Goals
| One-page Version of this Chapter for Easy Printing

Localization is the means by which GEOS adapts to foreign environments. The kernel automatically accounts for such country-dependent items as currencies and keyboard layouts. Your geodes can work with foreign character sets and formatting preferences. Using the ResEdit tool, anyone can quickly translate an executable's text into other languages. You should look over this chapter whether or not you're planning on distributing your software internationally. The chapter includes documentation for some functions which you're likely to use regardless.

1 Localization Goals
2 How To Use Localization
3 Preparing for ResEdit
4 International Formats
4.1 Number and Measure
4.2 Currency
4.3 Quotation Marks
4.4 Dates and Times
4.5 Filters for Formats
5 Lexical Functions
5.1 Comparing Strings
5.2 String Length and Size
5.3 Casing
5.4 Character Categories
5.5 Lexical Values
5.6 DOS Text & Code Pages

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Localization Goals
| One-page Version of this Chapter for Easy Printing


Localization Goals

Localization: 1 Localization Goals

| Up: GEOS SDK TechDocs | Up | Prev: Localization | Next: 2 How To Use Localization

The benefit of Geoworks' experience with previous international products was designed into GEOS from the start. The kernel was designed with the following three goals of localization in mind:

  • Ease of use
    Localization should be easy to use. A cumbersome system would slow programmers and leave them reluctant to use the system.
  • Integration with the kernel
    Functions used by many geodes should be part of the kernel. This allows for shared code, using less memory and making developers' jobs easier.
  • Economy
    Anyone should be able to translate an executable's text without having to understand or access the source code.

If you plan to release your software in other languages, you should code it for easy conversion later. Even if you have no such plans, there are still good reasons for using the localization features of GEOS.

  • Stay Generic and Adaptable
    By using localization code, your programs can work in a variety of environments without modification of source code. Even if you don't translate your geode into another language, users can still use it despite different keyboards and character sets.
  • Respect user's preferences
    Much of localization deals with user preferences which differ from country to country. Regardless of country, users may make changes using the Preferences desk tool and will no doubt appreciate having their preferences maintained.
  • Conform to the system
    You're probably going to end up using localization since much of GEOS is already localized. If your geodes are going to read DOS text files or alphabetize lists, you're going to use localized code; GEOS takes foreign character sets into account. Fortunately, localization functions are easy to use. In those situations where you don't absolutely have to use localized code, usually it's easier to use the localized functions than to write your own.
| Up: GEOS SDK TechDocs | Up | Prev: Localization | Next: 2 How To Use Localization


How To Use Localization

Localization: 2 How To Use Localization

| Up: GEOS SDK TechDocs | Up | Prev: 1 Localization Goals | Next: 3 Preparing for ResEdit

Your localization workload will vary depending on whether you plan to release your software in other countries. If you don't plan on exporting your software, your only contact with localization will come when you use the common functions that are localized. If you plan on international distribution, you'll be using localization both when writing your geodes and later on, during the translation process, in the following steps:

  • Use provided routines.
    Several GEOS functions have been localized to handle foreign variations, and you should use these whether you plan international release or not. Most of these functions are concerned with "International formats" and character sets.
  • Plan ahead for ResEdit .
    You'll probably use the ResEdit tool if you translate your software into other languages. You'll have to be careful about how you write your programs if ResEdit is to work with them properly. Any text in your geode that the user may see will have to be translated. ResEdit looks only at chunk is in localizable resources. You should also be prepared for the translations of any string to be longer than the original. You may want to provide instructions for translators. You may include these instructions within the body of your source code, close to the strings that will be translated.
  • Use ResEdit .
    The ResEdit tool makes translation easy. It goes through an application's resources and checks all objects for strings and bitmaps, asking the translator to make any fitting changes. The translator never needs to see the source code and doesn't have to know how to program.
| Up: GEOS SDK TechDocs | Up | Prev: 1 Localization Goals | Next: 3 Preparing for ResEdit


Preparing for ResEdit

Localization: 3 Preparing for ResEdit

| Up: GEOS SDK TechDocs | Up | Prev: 2 How To Use Localization | Next: 4 International Formats

ResEdit is a GEOS program which speeds geode translations. This section won't tell you how to use the ResEdit tool but will explain how to write your code if you want ResEdit to work with it correctly.

The important thing to remember is to put all text and bitmaps to be localized in localizable resources. ResEdit looks for localizable objects only in non-code resources. The string or bitmap itself must be stored in a chunk, which you may insure by using either the @chunk or @visMoniker keywords. (If you're using assembly, you should use the LocalDefString macro when setting up localizable strings.) Thus, your application icons are probably stored in the following way:

@start APPSCMONIKERRESOURCE, data
/* Application moniker: */
@visMoniker MyAppSCMoniker = { /* ... */}
@end APPSCMONIKERRESOURCE

At first it might seem surprising that bitmaps should be localized, since pictures are supposed to be a sort of universal language. However, this attitude may seem less strange when you try to distribute software to a culture that finds pictures of yellow arrows offensive.

Your application could keep its strings in a resource in the style illustrated in Storing Strings in Localizable Resources .

Code Display 8-1 Storing Strings in Localizable Resources

@start ErrorStrings, data;
@chunk char NoMonkeyError[] =
    "FTPOOMM failed: No monkey present. Acquire a monkey and try again.";
@chunk char NoPeanutError[] =
    "FTPOOMM failed: No peanuts present. Please insert peanuts and try again.";
@end ErrorStrings;

When working with assembly language, this means that any object stored in a code resource, idata , or udata will be passed over in the search for localizable resources, and thus will not be localizable.

When you know you are storing your strings such that they are localizable, you can provide information which will be visible to the translator when they use the ResEdit tool. Use the @localize keyword ( localize in assembly) to set up this help text. The @localize directive should directly follow the chunk it applies to.

Remember the @localize syntax:

     @localize { <string> <min>-<max> };
     @localize { <string> <length> };
     @localize { <string> };
     @localize <string>;
 @localize not;

Code Display 8-2 Storing Strings in Localizable Resources

@start ErrorStrings, data
@chunk char NoMonkeyError[] =
    "FTPOOMM failed: No monkey present. Acquire a monkey and try again";
@localize "The phrase 'acquire a monkey' appears in another string. Both should \
be translated in the same way.";
@end ErrorStrings
@object GenGlyphClass BossMon = {
     GI_visMoniker = "Boss";
     @localize { "This means hide the game because a boss is coming" 3-6 }; }

Another thing to keep in mind is that when strings are translated, they are likely to grow 33% to 50%. You have to remember to leave room for larger strings, both in memory and in UI. If you use the usual generic UI gadgetry, the geometry manager should stretch the various gadgets to fit any larger names. You should be careful that it doesn't have to stretch so far that components get lost off the edge of the screen. If you decide to get around this problem by constraining the size of some gadgetry, keep in mind that if you don't allow it to stretch, your new strings may not fit.

| Up: GEOS SDK TechDocs | Up | Prev: 2 How To Use Localization | Next: 4 International Formats


International Formats

Localization: 4 International Formats

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Preparing for ResEdit | Next: 5 Lexical Functions

"International Formats" generally refers to formats which differ from country to country. In GEOS, it signifies those formats which the user can set in the International section of the Preferences desk tool. GEOS provides functions to work with International Formats.

Number and Measure

Currency

Quotation Marks

Dates and Times

Filters for Formats

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Preparing for ResEdit | Next: 5 Lexical Functions


Number and Measure

Localization: 4.1 International Formats: Number and Measure

| Up: GEOS SDK TechDocs | Up | Prev: 4 International Formats | Next: 4.2 Currency
LocalGetNumericFormat(), LocalSetNumericFormat()
LocalGetMeasurementType(), LocalSetMeasurementType(), LocalAsciiToFixed(), LocalFixedToAscii(), LocalDistanceToAscii(), LocalDistanceFromAscii()

In the USA, 3.142 is a little more than three. In some other countries, it's a little over three thousand. This discrepancy arises from the fact that the decimal and thousands separators are interchanged--in some countries the "." symbol takes the place of the "," symbol when expressing numbers. To allow for local number formats, GEOS provides functions for retrieving and setting the user's preferences.

The number format includes the thousands separator, decimal separator, list separator, and number of decimal digits, as shown in the figure below. The number format also contains a record, NumberFormatFlags , which holds one flag, NFF_LEADING_ZERO . This flag is on if the user wants a leading zero.

There is no overall automatic formatting command for numbers. GEOS supports many internal numeric formats such as fixed point and floating point. If the corresponding math library doesn't contain a formatting command for the number format you're using, you will need to work directly with the localization functions.

The LocalGetNumericFormat() routine returns the numeric format so you may use it to do your own formatting. LocalSetNumericFormat() allows you to reset the user's preferences, though this is inadvisable as it overrides and erases the user's original settings.

These routines use the LocalNumericFormat structure to store the number format data.

The LocalGetMeasurementType() and LocalSetMeasurementType() routines work with the enumerated type MeasurementType , of which there are two: MEASURE_US and MEASURE_METRIC. LocalGetMeasurementType() retrieves the user's current preferred measurement type; LocalSetMeasurementType() sets the user's preference to the passed value. As with any command that resets the user's preference, LocalSetMeasurementType() should never be used by most geodes.

The LocalAsciiToFixed() routine converts an ascii string (e.g. "12.7") to a fixed point number. LocalFixedToAscii() performs the reverse.

To convert between a string like "72 pt" and a number representing a distance, use the LocalDistanceFromAscii() routine. LocalDistanceToAscii() goes the other way, constructing the string corresponding to a given distance. These routines use the DistanceUnit enumerated type to specify the measurement units which are used as shown in the table below.

When allocating strings to use as the targets for Local...ToAscii() routines, allocating a buffer of size LOCAL_DISTANCE_BUFFER_SIZE will be sufficient.

Table 8-1 DistanceUnit types

DistanceUnit value # of Points Display Format Entry Format(s)

DU_POINTS 1.000 ###.### pt ###.### pt

DU_INCHES 72.000 ###.### in ###.### in ###.###"

DU_CENTIMETERS 28.346 ###.### cm ###.###cm

DU_MILLIMETERS 2.835 ###.### mm ###.###mm

DU_PICAS 12.000 ###.### pi ###.### pi

DU_EUR_POINTS 1.065 ###.### ep ###.### ep

DU_CICEROS 12.787 ###,### ci ###.### ci

DU_POINTS_OR_MILLIMETERS and DU_INCHES_OR_CENTIMETERS: special cases

| Up: GEOS SDK TechDocs | Up | Prev: 4 International Formats | Next: 4.2 Currency


Currency

Localization: 4.2 International Formats: Currency

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Number and Measure | Next: 4.3 Quotation Marks
LocalGetCurrencyFormat(), LocalSetCurrencyFormat()

These functions allow you to work with the currency format (see the figure below). This format consists of the currency symbol string, number of currency digits (which may be different from the number of digits for ordinary numbers), and the CurrencyFormatFlags record. The flags in this structure determine whether the currency format includes space around the symbol, a negative sign, or a leading zero. There are flags to determine the relative order of the negative sign, currency symbol, and number. The currency symbol string may be of up to length CURRENCY_SYMBOL_LENGTH including the null terminator. LocalGetCurrencyFormat() also returns the separator characters from the numeric formats, eliminating the need for a separate call to LocalGetNumericFormat() . LocalSetCurrencyFormat() allows the changing of the set preferences. Most parts of the currency format are stored in a LocalCurrencyFormat structure when passed to or returned by these functions.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Number and Measure | Next: 4.3 Quotation Marks


Quotation Marks

Localization: 4.3 International Formats: Quotation Marks

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Currency | Next: 4.4 Dates and Times
LocalGetQuotes(), LocalSetQuotes()

In different countries, people use different types of quotation marks. Germans, for example, might use the » and « characters. Although traditionally, the only computer-generated quotation marks available are " and ", GEOS allows all geodes to use native quotation marks without extraordinary effort.

If a user types in a quotation mark, the keyboard knows which character to print; thus, if you're using ResEdit on a string, it's easy to have your translator type in the correct character. In these cases your geode will have no use for LocalGetQuotes() or LocalSetQuotes() . On the other hand, if your program is going to construct a string which contains quotation marks, use the LocalGetQuotes() routine; it will return the correct characters to use. If you want to reset the preferences, use the LocalSetQuotes() routine.

These routines work with the LocalQuotes structure, which simply holds four characters to use as the four kinds of quotation mark.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Currency | Next: 4.4 Dates and Times


Dates and Times

Localization: 4.4 International Formats: Dates and Times

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Quotation Marks | Next: 4.5 Filters for Formats
LocalFormatDateTime(), LocalParseDateTime(),
LocalGetDateTimeFormat(), LocalSetDateTimeFormat(),
LocalCustomFormatDateTime(), LocalCustomParseDateTime(), LocalCalcDaysInMonth(), LocalFormatFileDateTime()

LocalFormatDateTime() and LocalParseDateTime() allow display and parsing of date and time strings in a variety of formats. Passed the appropriate data, LocalFormatDateTime() will return a string with the data appropriately formatted. LocalParseDateTime() performs the reverse function, reading a string and attempting to extract information from it. The strings these functions use should be long enough to hold a formatted date or time. The predefined constant DATE_TIME_BUFFER_SIZE is the minimum recommended number of characters for one of these strings.

Localization functions use DateTimeFormat s to keep track of what sort of format is desired. If told to parse "5:28" as hours and minutes, LocalParseDateTime() will (depending on the user's time format preferences) return 5 in the hours place and 28 in the minutes place. If told to parse "5/28" as hours and minutes, LocalParseDateTime() would return an error. There are a variety of DateTimeFormats() to choose from, and you should decide which format to use based on what information you want your application to display or read.

Each DateTimeFormat has a string associated with it that contains formatting information. This string consists of the characters of the format, with place-holding tokens for the fields of the date or time. These tokens are delimited by the special character "|". For example, the date/time format string "|HH|:|mm| |ap|" would correspond to the formatted string "10:37 am". The string for each DateTimeFormat can be accessed or altered with the LocalGetDateTimeFormat() and LocalSetDateTimeFormat() commands. The recommended minimum size of a date/time format string is the constant DATE_TIME_FORMAT_SIZE.

If you don't wish to work through one of the standard DateTimeFormat values, you can construct a string containing some date/time tokens and pass it as an argument to LocalCustomFormatDateTime() , along with the appropriate date and time information. To parse a date/time using a custom format, use LocalCustomParseDateTime() .

The LocalFormatFileDateTime() utility routine works in the same way as LocalFormatDateTime() , except it takes a FileDateAndTime argument. It is normally used to format the information returned by FileGetDateAndTime() or TimerGetFileDateTime() .

The LocalCalcDaysInMonth() utility routine takes a month number and returns the number of days in that month.

A number of constants have been set up to aid in the computation of the size of string necessary to hold a date/time or a date/time format string. These constants are DATE_TIME_BUFFER_SIZE, DATE_TIME_FORMAT_SIZE, MAX_MONTH_LENGTH , MAX_DAY_LENGTH, MAX_YEAR_LENGTH, MAX_WEEKDAY_LENGTH , MAX_SEPARATOR_LENGTH, and TOKEN_LENGTH .

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Quotation Marks | Next: 4.5 Filters for Formats


Filters for Formats

Localization: 4.5 International Formats: Filters for Formats

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Dates and Times | Next: 5 Lexical Functions
LocalIsDateChar(), LocalIsTimeChar(), LocalIsNumChar()

The generic UI allows for filters on text objects to keep users from typing inappropriate characters. For example, if the user were expected to type in a number, the "~" character would be ignored as that isn't part of the number format. If you want to use generic text objects, you can give them hints to accept only characters from certain formats. If you want to use the filtering functions for your own purposes, they are available globally. When passed a character, these functions will tell whether the character is a legal part of a format. LocalIsDateChar() returns TRUE if the passed character is part of the short date format, DTF_SHORT. LocalIsTimeChar() returns TRUE when passed a number or time separator character. LocalIsNumChar() returns TRUE when passed a number character, a separator, or negative sign.

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Dates and Times | Next: 5 Lexical Functions


Lexical Functions

Localization: 5 Lexical Functions

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 International Formats | Next: 5.1 Comparing Strings

Since different languages have different alphabets, GEOS has to allow for characters not in the standard English character set. You will find that character values are stored via the Chars enumerated type. Thus, many standard procedures have been localized. Most of these functions have to do with the lexical value of characters, their place in a lexical, or alphabetic, ordering. These lexical values take the place of ASCII standard character values that you may be used to.

Comparing Strings

String Length and Size

Casing

Character Categories

Lexical Values

DOS Text & Code Pages

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 International Formats | Next: 5.1 Comparing Strings


Comparing Strings

Localization: 5.1 Lexical Functions: Comparing Strings

| Up: GEOS SDK TechDocs | Up | Prev: 5 Lexical Functions | Next: 5.2 String Length and Size
LocalCmpStrings(), LocalCmpStringsNoCase(), LocalCmpStringsNoSpace(), LocalCmpStringsNoSpaceCase(), LocalGetLanguage(), LocalCmpChars(), LocalCmpCharsNoCase()

LocalCmpStrings() takes two strings as arguments and says which, if either, comes first alphabetically. LocalCmpStringsNoCase() does the same thing but is not case sensitive. LocalCmpStrings() does a better job of ordering strings than assembly language instructions such as cmps that just compare ASCII values. Since the ASCII value of "a" places it after "Z", it is advisable to use LocalCmpStrings() , which uses the localized lexical values. When comparing two strings for equality, assembly instructions like cmps may be used safely.

The LocalCmpStringsNoSpace() and LocalCmpStringsNoSpaceCase() routines are like the LocalCmpStrings() and LocalCmpStringsNoCase() routines, except that spaces and punctuation marks are ignored. Note that if you are comparing only a certain number of characters that spaces and punctuation marks will not be included in this number.

The sort order of the strings will depend on the language used when sorting them. To find out the current language used for sorting, call LocalGetLanguage() .

There are two assembly routines LocalCmpChars() and LocalCmpCharsNoCase() which allow for the quick lexical comparison of two characters.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Lexical Functions | Next: 5.2 String Length and Size


String Length and Size

Localization: 5.2 Lexical Functions: String Length and Size

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Comparing Strings | Next: 5.3 Casing
LocalStringLength(), LocalStringSize()

There are two routines which determine how long a string is. LocalStringLength() returns the number of characters making up the string, not including the null terminator, if any. LocalStringSize() returns the number of bytes in a string, again not counting any null terminators. Normally these two values will be the same, but any applications which want to support double byte character support will need separate functions to handle those characters that take more than one byte to represent.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Comparing Strings | Next: 5.3 Casing


Casing

Localization: 5.3 Lexical Functions: Casing

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 String Length and Size | Next: 5.4 Character Categories
 LocalUpcaseChar(),  LocalDowncaseChar(),  LocalUpcaseString(),  LocalDowncaseString()

These functions take a character or string and, if possible, transform it to upper or lower case. They work only on alphabetic characters; other characters will be returned unchanged. These functions can deal with all languages, understanding "È" is the upper case for "è". If you're used to using ASCII, you might be tempted to write your own version of these casing functions by doing arithmetic operations with the lexical or ASCII values of characters, but chances are these will not work with other character sets and should be avoided.

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 String Length and Size | Next: 5.4 Character Categories


Character Categories

Localization: 5.4 Lexical Functions: Character Categories

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Casing | Next: 5.5 Lexical Values
 isupper(), LocalIsUpper(),  islower(), LocalIsLower(), isalpha(),  LocalIsAlpha(),  ispunct(), LocalIsPunctuation(), isspace(),  LocalIsSpace(), isprint(),  LocalIsPrintable(),
iscntrl(),  LocalIsControl(), isdigit(),  LocalIsDigit(), isxdigit(),  LocalIsHexDigit(),  isgraph(), LocalIsGraphic(),
isalnum(),  LocalIsAlphaNumeric(),  LocalIsSymbol()

GEOS assembly provides some commonly used character predicates. C users use the standard C functions isupper() , islower() , etc. to access these predicates. Passed a character, each of these functions tells whether that character falls into a certain category. LocalIsUpper() returns true if the character is an uppercase alphabetic character; LocalIsLower() returns true if the character is lower case. LocalIsAlpha() approves any alphabetic character. LocalIsPunctuation() checks for punctuation marks. LocalIsSpace() looks for white space (including spaces, tabs, and carriage returns), and LocalIsSymbol() catches just about everything else. LocalIsControl() detects control characters (e.g. Control-A). LocalIsDigit() returns true for decimal digits. LocalIsHexDigit() approves hexadecimal digits (including the characters a-f, A-F). LocalIsAlphaNumeric() detects alphabetic characters and decimal digits. LocalIsPrintable() returns true when passed a character which takes up a space when printed, corresponding to the standard C function isprint() . LocalIsGraphic() checks for displayable characters, in the manner of the standard C function isgraphic() .

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Casing | Next: 5.5 Lexical Values


Lexical Values

Localization: 5.5 Lexical Functions: Lexical Values

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Character Categories | Next: 5.6 DOS Text & Code Pages
LocalLexicalValue(), LocalLexicalValueNoCase()

If for some reason you want to get the lexical value of a character, these functions will return it. LocalLexicalValue() , passed a character, returns its lexical value. LocalLexicalValueNoCase() does the same thing but ignores case. If the lexical value for one character is lower than another, that character comes first alphabetically. For instance, "a" would have a lower lexical value than "z." This ordering will be valid in any language using the same character set.

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Character Categories | Next: 5.6 DOS Text & Code Pages


DOS Text & Code Pages

Localization: 5.6 Lexical Functions: DOS Text & Code Pages

| Up: GEOS SDK TechDocs | Up | Prev: 5.5 Lexical Values
LocalDosToGeos(), LocalGeosToDos(), LocalDosToGeosChar(), LocalGeosToDosChar(), LocalCmpStringsDosToGeos(), LocalIsDosChar(), LocalCodePageToGeos(), LocalGeosToCodePage(), LocalCodePageToGeosChar(), LocalGeosToCodePageChar(), LocalGetCodePage(), LocalIsCodePageSupported()

There are several functions which work with DOS-format text, converting it to and from GEOS format. Normally, converting DOS text files to GeoWrite documents is handled by Import/Export routines; for those occasions where your code needs to convert DOS text to GEOS format or vice versa, however, these functions should be sufficient. They are localized because DOS has its own version of localization which it implements as "code pages." Each code page is a table of characters. Countries or regions with unusual characters have different code pages. If you haven't worked with code pages up to this point, you probably won't have to start now but can just rely on GEOS to use the native code page correctly. If your geode is going to allow computers from one country to communicate with those of another, you might need to use specialized functions.

Assuming you want to use the code page native to the user's machine, you can use LocalDosToGeos() and LocalGeosToDos() to convert strings from one text format to the other. LocalDosToGeosChar() and LocalGeosToDosChar() work similarly, converting a single character.

LocalCmpStringsDosToGeos() takes two strings, converts one or both of them to GEOS format, then compares them, returning the same values as the regular string comparing functions. Pass a LocalCmpStringsDosToGeosFlags to specify which strings to convert before comparing.

Each of these functions also takes a character as an argument. When a character in one format has no corresponding character in the format it's being converted to, the character argument will be substituted. At the same time a flag will be set to let your geode know that there were some characters that couldn't be converted nicely. LocalIsDosChar() checks a GEOS character to see if it maps into a DOS character. The MIN_MAP_CHAR constant is the smallest value which may need to be mapped. If a character's value is below this constant, then it will be the same under any code page.

If your geode is interested in which code page it's using, the LocalGetCodePage() instruction will tell you what the default code page is. If you want to use a code page other than the native one, the LocalCodePageToGeos() , LocalGeosToCodePage() , LocalCodePageToGeosChar() , and LocalGeosToCodePageChar() behave as their default code page equivalents, except that each takes a code page as an argument to be used in conversion. A number of support code page values are enumerated in the DosCodePage type. To find out whether the user's environment supports a specific code page, call LocalIsCodePageSupported() .

| Up: GEOS SDK TechDocs | Up | Prev: 5.5 Lexical Values


UI

GEOS SDK Documentation: UI

| Up: GEOS SDK TechDocs

The following documents describe mechanisms and object classes from which you may build up your application's UI.

Nokia 9000i Communicator UI
The Nokia 9000i Communicator defines specialized UI classes, some of which fill the role plain vanilla GEOS UI classes on the device.
UI Overview
Overview of the GEOS UI classes, including examples.
GenGlyph
Perhaps the simplest UI class, GenGlyph displays a moniker, takes up space, and does little else.
GenInteractionClass
The grouper class: acts as a logical grouper, a dialog box. On devices which support menus, GenInteractions can be menus.
GenTriggerClass
The button class.
Geometry Management
The User library spacially arranges UI objects, guided by hints attached to these objects.
List Classes
How to make lists of choices, scrolling and otherwise.
GenViewClass
A scrolling clipping view.
GenValueClass
The number spinner and scroller button class.
Online Help
How to provide on-line help to your application's users.
Text Objects
How to use some of the advanced features of text objects.
VisContentClass
Acts as the content of a GenView; has great control over how it manages its children.
VisClass
VisClass does little on its own, but is very customizable. It can draw itself and control how it handles user input. It can appear in VisComps or VisContents.
VisCompClass
Like VisClass, but can manage children.
Table Library
Draws a grid, and provides an easy API for drawing in cells of the grid.
Pen Library
If the Nokia 9000i Communicator supported ink input, you might use some of the functionality of this library. Other GEOS devices use its Ink-gathering object.
| Up: GEOS SDK TechDocs


GenGlyph

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenGlyph Features
| One-page Version of this Chapter for Easy Printing

The GenGlyph object allows you to easily display single lines of text without using a more complex GenText object. A GenGlyph can be thought of as a label in which you can display text but which offers no other functionality.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenGlyph Features
| One-page Version of this Chapter for Easy Printing


GenGlyph Features

GenGlyph: 1 GenGlyph Features

| Up: GEOS SDK TechDocs | Up | Prev: GenGlyph | Next: 2 GenGlyph Basics

Applications often display simple lines of text. Text might request the user to make a selection. Text can alert the user to a certain application condition. The GenGlyph offers a simple, easy to use text display object.

One other generic object, GenText, provides a powerful means to display text with functions such as word-wrapping, font scalability, and an arbitrarily large buffer size. If you do not need such a versatile and powerful text display capability, however, it is wiser and easier to use a GenGlyph. This object only displays text in the default font and style for your application.

| Up: GEOS SDK TechDocs | Up | Prev: GenGlyph | Next: 2 GenGlyph Basics


GenGlyph Basics

GenGlyph: 2 GenGlyph Basics

| Up: GEOS SDK TechDocs | Up | Prev: 1 GenGlyph Features | Next: 3 Modifying a GenGlyph

The GenGlyph object does not, by itself, offer any instance fields or messages. The functionality of a GenGlyph object is included entirely within the instance fields and messages of its superclass, GenClass .

Unlike text within a GenText, the text of a GenGlyph is taken solely from the object's GI_visMoniker . Therefore, the primary use of a GenGlyph lies in its visual moniker. Any GenClass messages dealing with an object's visual moniker will modify the appearance of a GenGlyph.

A GenGlyph does not allow word-wrapping; if your text may be longer than the object's horizontal constraints, either break up the lines of text into smaller, single-line GenGlyphs or use a GenText object.

Code Display 6-1 Using Hints to Manage GenGlyphs

/* This code will duplicate the display shown above */
@object GenInteractionClass DiskCopyBox = {
    GI_comp = @DiskCopyHeader, @SourceSelection, @DestinationSelection;
    GII_visibility = GIV_DIALOG;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
/* The DiskCopyHeader will be the line of instructional text. */
@object GenGlyphClass DiskCopyHeader = {
    GI_visMoniker = "Select source and destination for disk copy:";
    HINT_CENTER_MONIKER;			/* Centers the moniker horizontally. */
}
/* These objects will form the "Source" line. */
@object GenInteractionClass SourceSelection = {
    GI_comp = @SourceText, @SourceList;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_CENTER_MONIKER;
}
/* This group of objects will form the "Destination" line. */
@object GenInteractionClass DestinationSelection = {
    GI_comp = @DestinationText, @DestinationList;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_CENTER_MONIKER;
}
/* For simplicity, the List objects are not shown. */
@object GenGlyphClass SourceText = {
    GI_visMoniker = "Source:";
}
@object GenGlyphClass DestinationText = {
    GI_visMoniker = "Destination:";
}
| Up: GEOS SDK TechDocs | Up | Prev: 1 GenGlyph Features | Next: 3 Modifying a GenGlyph


Modifying a GenGlyph

GenGlyph: 3 Modifying a GenGlyph

| Up: GEOS SDK TechDocs | Up | Prev: 2 GenGlyph Basics

You may wish at some point to change the text in a GenGlyph. Because a GenGlyph's text is entirely within its visual moniker, it is a simple matter to use GenClass messages to change the moniker dynamically. Use MSG_GEN_GET_VIS_MONIKER to return a GenGlyph's current moniker; use MSG_GEN_USE_VIS_MONIKER (or MSG_GEN_REPLACE_VIS_MONIKER ) to set a new moniker for a GenGlyph. Doing so will ensure that the object is visually rebuilt with the new moniker.

Code Display 6-2 A Toggle On/Off Switch Dialog Box

@object GenInteractionClass MyDialogBox = {
    GI_comp = @DialogText, @DialogButton;
    GII_visibility = GIV_DIALOG;
}
/* Monikers for the Instructions (the GenGlyphs). */
@visMoniker OnTextMoniker = "Click the `ON' button";
@visMoniker OffTextMoniker = "Click the `OFF' button";
/* Monikers for the triggers. */
@visMoniker OnButtonMoniker = "ON";
@visMoniker OffButtonMoniker = "OFF";
/* The object begins in the "On" state. */
@object GenGlyphClass DialogText = {
    GI_vismoniker = @OnTextMoniker;
}
/* Whenever the button is pressed, MSG_FLIP_THE_SWITCH will change both the glyph
 * and the trigger monikers. */
@object GenTriggerClass DialogButton = {
    GI_visMoniker = @OnButtonMoniker;
    GTI_actionMsg = MSG_FLIP_THE_SWITCH;
    GTI_destination = process;
}
@method MyProcessClass, MSG_FLIP_THE_SWITCH {
    ChunkHandle testMoniker;				/* Stores the temporary moniker. */
    testMoniker = @call DialogButton::MSG_GEN_GET_VIS_MONIKER;
/* If the moniker is "ON", turn both it and the glyph to the Off monikers.
 * Otherwise (the moniker is "OFF"), turn both it and the glyph to the On
 * monikers. Both visual updates are delayed via the UI queue (and will therefore
 * be updated at the same time rather than separately) to avoid flashing. */
    if (testMoniker == "ON") {
    @call DialogButton::MSG_GEN_USE_VIS_MONIKER(OptrToChunk(@OffButtonMoniker),
                        VUM_DELAYED_VIA_UI_QUEUE);
    @call DialogText::MSG_GEN_USE_VIS_MONIKER(OptrToChunk(@OffTextMoniker),
                            VUM_DELAYED_VIA_UI_QUEUE);
    }
    else {
    @call DialogButton::MSG_GEN_USE_VIS_MONIKER(OptrToChunk(@OnButtonMoniker),
                VUM_DELAYED_VIA_UI_QUEUE);
    @call DialogText::MSG_GEN_USE_VIS_MONIKER(OptrToChunk(@OnTextMoniker),
                VUM_DELAYED_VIA_UI_QUEUE);
    }
    }
| Up: GEOS SDK TechDocs | Up | Prev: 2 GenGlyph Basics


Menus and Dialog Boxes

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenInteraction Features
| One-page Version of this Chapter for Easy Printing

GenInteraction is a high-level object which manages groups of other generic objects. These groups of objects can appear as part of a window, as their own menu or even as a separate dialog box. GenInteractionClass also provides the means to interpret actions of its children and adjust the behavior of the Interaction accordingly. Therefore, it is meaningless for an Interaction to appear without children to manage.

GenInteractionClass is a subclass of GenClass and therefore inherits all of its functionality. Of special interest for GenInteractions is geometry management with hints provided in GenClass . See the GenClass chapterand the Managing UI Geometry chapterfor more information.

You should be familiar with generic objects before reading this chapter. Please see the UI Overviewfor an summary of each generic object in the user interface. You should be particularly familiar with the instance data of and messages handled by GenClass

1 GenInteraction Features
1.1 Sub-Group Interactions
1.2 Popup Interactions (Menus)
1.3 Dialog Boxes
1.4 PopOuts
2 GenInteraction Instance Data
2.1 GenInteraction Visibility
2.2 Standard Interactions (Menus)
2.3 GenInteraction Types
2.4 GenInteraction Attributes
3 GenInteraction Usage
3.1 Visibilities
3.2 Types
4 Supplemental Usage
4.1 Initiating Interactions
4.2 Dismissing Interactions
4.3 Modality for Dialogs
4.4 Managing Input
4.5 Thread Blocking Routines
5 Interaction Commands
5.1 InteractionCommand Types
5.2 Dialog Control
5.3 Standard Response Triggers
5.4 Replacing Default Triggers
5.5 Triggers Completing Interactions

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenInteraction Features
| One-page Version of this Chapter for Easy Printing


GenInteraction Features

Menus and Dialog Boxes: 1 GenInteraction Features

| Up: GEOS SDK TechDocs | Up | Down | Prev: Menus and Dialog Boxes | Next: 2 GenInteraction Instance Data

Because of the nature of GenInteractionClass , objects of this class may appear in a variety of forms. The specific UI will have some control over the visual presentation of GenInteractions, but your application can usually specify in what manner the Interaction should appear. Your application may also add hints to the specific UI to modify the arrangement of objects within the Interaction.

GenInteraction provides three main visual implementations:

  • A sub-group, having no inherent visual representation at all. (The visual implementation depends on the Interaction's parent.)
  • A popup (typically a menu) remaining hidden from the application until activated, and then normally only remaining on-screen for the duration of the selection.
  • An independently displayable window (dialog box) coming on-screen whenever activated and going off-screen whenever instructed to.
  • Popout dialog boxes which can act as sub-group GenInteractions (for example a menu bar) that can be "popped out" into dialog boxes. Note that popouts are not related to pop-ups.

Specifically, a GenInteraction also provides the following features:

  • organization of the spacial arrangement of other objects.
  • the capability to apply properties within an Interaction either immediately (in immediate mode) or at a later time (in delayed mode).
  • the display of progress reports on the state of a process or object.
  • the ability to initiate commands.
  • several default dialog boxes for notification and affirmation responses.
  • modal dialog boxes that block other threads of execution until responded to.

Sub-Group Interactions

Popup Interactions (Menus)

Dialog Boxes

PopOuts

| Up: GEOS SDK TechDocs | Up | Down | Prev: Menus and Dialog Boxes | Next: 2 GenInteraction Instance Data


Sub-Group Interactions

Menus and Dialog Boxes: 1.1 GenInteraction Features: Sub-Group Interactions

| Up: GEOS SDK TechDocs | Up | Prev: 1 GenInteraction Features | Next: 1.2 Popup Interactions (Menus)

A sub-group Interaction is the simplest manifestation of a GenInteraction. A sub-group may only appear as part of a larger window. You will most often use sub-group Interactions to arrange and group other objects (the Interaction's children) within proper places in the UI. This grouping is also sometimes useful to perform geometry actions upon and otherwise manipulate a group of objects as a whole. Using Interactions eliminates the need to provide individual requirements to each child object.

For example, you can use the hint HINT_ORIENT_CHILDREN_VERTICALLY on a single GenInteraction object to display its children in a vertical row. The Interaction's only function in this case is to provide an object for the hint to act upon. Interactions of this type may contain any number and type of children. Because they are not independently displayable, however, their use becomes limited to the constraints of the larger window. If you need the Interaction's children to be hidden from view until needed, you should use a popup or dialog Interaction.

| Up: GEOS SDK TechDocs | Up | Prev: 1 GenInteraction Features | Next: 1.2 Popup Interactions (Menus)


Popup Interactions (Menus)

Menus and Dialog Boxes: 1.2 GenInteraction Features: Popup Interactions (Menus)

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Sub-Group Interactions | Next: 1.3 Dialog Boxes

A popup is an Interaction that you can use to contain commands or selections that should only be displayed when the popup itself is activated. A popup usually conceals its children from view until needed, therefore avoiding cluttering up the UI with dozens of objects at any given time. Popups are most often implemented as menus. Popups usually only remain on-screen until either a selection within the popup is made or the user activates some other object. Menus in OSF/Motif, for example, will only remain on-screen until an object in the menu is selected or until the user clicks somewhere else in the application. (GEOS' implementation of OSF/Motif does allow popups to be pinned, however.)

Popups are not independently displayable; they must appear as part of another window. The popup Interaction's visual moniker usually serves as the only visual cue to the existence of the menu until it is activated. These menu titles usually appear in an appropriate menu bar of the window.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Sub-Group Interactions | Next: 1.3 Dialog Boxes


Dialog Boxes

Menus and Dialog Boxes: 1.3 GenInteraction Features: Dialog Boxes

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Popup Interactions (Menus) | Next: 1.4 PopOuts

A dialog box is an Interaction that takes the form of an independently displayable window. Typically, a dialog box is a temporary window brought up by the application to request information from the user. Dialogs differ from Primary and Document windows in several ways, however. (For example, most dialogs are not resizable.)

A dialog box can be in either of two conditions: modal or non-modal. By default, dialog boxes are non-modal. A modal dialog box is one in which all other input to the application is blocked until the user responds to the dialog. You will usually need modal dialog boxes when an application must gain some further information before proceeding. For example, many of the System Preferences dialog boxes are modal to prevent the user from changing other aspects of the system while these dialogs are up.

Dialog boxes come on-screen either through direct user activation or through the application's instigation. By default, a dialog box will usually have an associated trigger object placed within the UI. This trigger's only function is to allow the user to bring the dialog box on-screen. You may remove this default activation trigger if your application code should bring up the dialog box instead.

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Popup Interactions (Menus) | Next: 1.4 PopOuts


PopOuts

Menus and Dialog Boxes: 1.4 GenInteraction Features: PopOuts

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Dialog Boxes | Next: 2 GenInteraction Instance Data

A popout GenInteraction is really just a grouping object that exists as either a simple sub-group or as a dialog box. Popouts generally begin life as sub-groups but can be "popped out" into dialog boxes that can be repositioned wherever the user wishes them. The dialog boxes can also be popped back into their sub-group arrangement.

In general, the GenInteraction is either popped-out or popped-in by double-clicking within the Interaction bounds.

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Dialog Boxes | Next: 2 GenInteraction Instance Data


GenInteraction Instance Data

Menus and Dialog Boxes: 2 GenInteraction Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 GenInteraction Features | Next: 3 GenInteraction Usage

GenInteraction has a set of instance fields; all are listed in GenInteraction Instance Fields . Any objects of GenInteractionClass or one of its subclasses will contain these instance fields, along with the instance fields of GenClass .

Code Display 7-1 GenInteraction Instance Fields

@instance GenInteractionType GII_type = GIT_ORGANIZATIONAL;
typedef ByteEnum GenInteractionType;
#define GIT_ORGANIZATIONAL 0
#define GIT_PROPERTIES 1
#define GIT_PROGRESS 2
#define GIT_COMMAND 3
#define GIT_NOTIFICATION 4
#define GIT_AFFIRMATION 5
#define GIT_MULTIPLE_RESPONSE 6
@instance GenInteractionVisibility GII_visibility = GIV_SUB_GROUP;
typedef ByteEnum GenInteractionVisibility;
#define GIV_NO_PREFERENCE 0
#define GIV_POPUP 1
#define GIV_SUB_GROUP 2
#define GIV_CONTROL_GROUP 3
#define GIV_DIALOG 4
#define GIV_POPOUT 5
@instance GenInteractionAttrs GII_attrs = 0;
typedef ByteFlags GenInteractionAttrs;
#define GIA_NOT_USER_INITIATABLE					0x80
#define GIA_INITIATED_VIA_USER_DO_DIALOG					0x40
#define GIA_MODAL					0x20
#define GIA_SYS_MODAL					0x10

GII_ type describes the function of the GenInteraction.

GII_ visibility describes the visual implementation of the GenInteraction. Typically, this affects where and how the object and its children are arranged within the UI. A GenInteraction's visibility may also affect the behavior of the children, though this is generally a function of the GII_ type field.

GII_ attrs define attributes that affect how a GenInteraction is initiated, and whether input to other parts of the application (or system) is affected.

Code Display 7-2 GenInteraction Hints

/*
 * Hints that affect Properties GenInteractions. These hints are described in the
 * GIT_PROPERTIES GenInteraction section.
 */
@vardata void HINT_INTERACTION_SINGLE_USAGE;
@vardata void HINT_INTERACTION_COMPLEX_PROPERTIES;
@vardata void HINT_INTERACTION_SIMPLE_PROPERTIES;
@vardata void HINT_INTERACTION_RELATED_PROPERTIES;
@vardata void HINT_INTERACTION_UNRELATED_PROPERTIES;
@vardata void HINT_INTERACTION_SLOW_RESPONSE_PROPERTIES;
@vardata void HINT_INTERACTION_FAST_RESPONSE_PROPERTIES;
@vardata void HINT_INTERACTION_REQUIRES_VALIDATION;
/* Hints that affect all types of GenInteractions. */
@vardata void HINT_INTERACTION_FREQUENT_USAGE;
@vardata void HINT_INTERACTION_INFREQUENT_USAGE;
@vardata void HINT_INTERACTION_MAKE_RESIZABLE;
@vardata void HINT_INTERACTION_CANNOT_BE_DEFAULT;
@vardata void HINT_INTERACTION_MODAL;
@vardata void HINT_INTERACTION_NO_DISTURB;
@vardata void HINT_INTERACTION_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD;
@vardata void HINT_CUSTOM_SYS_MENU;
@vardata void HINT_INTERACTION_MAXIMIZABLE;
@vardata void HINT_INTERACTION_POPOUT_HIDDEN_ON_STARTUP;
@vardata void HINT_INTERACTION_DISCARD_WHEN_CLOSED;

HINT_INTERACTION_FREQUENT_USAGE indicates that the GenInteraction is frequently used, and the specific UI may alter default functionality to reflect this. In most cases, this prevents dialog boxes from being automatically dismissed when the user is done interacting with them.

HINT_INTERACTION_INFREQUENT_USAGE indicates that the GenInteraction is infrequently used. In some specific UIs, this prevents sub-menus from automatically cascading.

HINT_INTERACTION_MAKE_RESIZABLE indicates that this GenInteraction may be resized even if it not normally allowed to do so. This hint usually only applies to dialog boxes.

HINT_INTERACTION_CANNOT_BE_DEFAULT indicates that this GenInteraction should not be activated as a system default. This hint prevents children of this GenInteraction from being activated as system defaults also. You should use this hint to mark groups of objects that may be potentially destructive; the user will be prevented from accidentally activating any objects in the GenInteraction.

HINT_INTERACTION_MODAL indicates that this GenInteraction (a dialog box) should be application modal. This hint should be used in place of GIA_MODAL in cases where modality is not needed for functionality, but is needed to present a cleaner UI to the user. Ideally, you should be able to go back and remove these hints by reworking the UI at some later time.

HINT_INTERACTION_NO_DISTURB indicates that this GenInteraction, if initiated, should be brought on-screen without disturbing the focus of the application. Usually, this hint is placed on notification type dialog boxes (such as new-mail notifications) so that the dialog box doesn't irritate the user by drawing away the focus.

HINT_INTERACTION_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD indicates that the default action for this Interaction group is to navigate to the next field. If something activates the GenInteraction's default action (for example by double-clicking within its confines) objects within the Interaction will not be activated; the focus will travel to the next field.

HINT_CUSTOM_SYS_MENU indicates that this GenInteraction menu is a custom system menu. Depending on the specific UI, the standard system menu will be added as a sub-menu of the custom system menu. This hint takes an integer value that specifies the child position to place the standard system menu (0 being the first position).

HINT_INTERACTION_MAXIMIZABLE indicates that this GenInteraction should be maximizable. This hint also allows the GenInteraction to become restorable to its non-maximizable state.

HINT_INTERACTION_POPOUT_HIDDEN_ON_STARTUP indicates that the GIV_POPOUT GenInteraction should be initially off-screen.

HINT_INTERACTION_DISCARD_WHEN_CLOSED indicates that the GenInteraction object (which must be a dialog box) can be discarded from memory when it is brought off-screen. This is an optimazation designed to free up memory. If the GenInteraction contains this hint, it must also be marked GIA_NOT_USER_INITIATABLE (to prevent it from being referenced), and contain a one-way upward generic link only. The dialog and all of its children must reside in their own block marked nonDetachable and discardable . (There should also be no other objects in that block, of course.)

Code Display 7-3 GenInteraction Optional Attributes

/* Optional Attributes. */
@vardata byte ATTR_GEN_INTERACTION_GROUP_TYPE;						/* GenInteractionGroupType */
typedef ByteEnum GenInteractionGroupType;
#define GIGT_FILE_MENU 0
#define GIGT_EDIT_MENU 1
#define GIGT_VIEW_MENU 2
#define GIGT_OPTIONS_MENU 3
#define GIGT_WINDOW_MENU 4
#define GIGT_HELP_MENU 5
#define GIGT_PRINT_GROUP 6
@vardata void ATTR_GEN_INTERACTION_OVERRIDE_INPUT_RESTRICTIONS;
@vardata void ATTR_GEN_INTERACTION_ABIDE_BY_INPUT_RESTRICTIONS;
@vardata void ATTR_GEN_INTERACTION_POPPED_OUT;
@vardata void ATTR_GEN_INTERACTION_POPOUT_NOT_CLOSABLE;

ATTR_GEN_INTERACTION_GROUP_TYPE indicates that this GenInteraction (typically a menu) is a special group that the specific UI should be aware of. This optional attribute takes a GenInteractionGroupType as its argument. For more information on these standard menus, see Standard Interactions (Menus) .

ATTR_GEN_INTERACTION_OVERRIDE_INPUT_RESTRICTIONS instructs the specific UI to override any input restrictions in place on a modal dialog box. Many dialog types (GIT_PROGRESS, GIT_NOTIFICATION, GIT_AFFIRMATION and GIT_MULTIPLE_RESPONSE) override these input restrictions by default anyway; this hint is for use in cases where this override is not provided.

ATTR_GEN_INTERACTION_ABIDE_BY_INPUT_RESTRICTIONS instructs the specific UI to abide by any input restrictions in place on a modal dialog box. In general, this changes the default behavior for many dialog types (GIT_PROGRESS, GIT_NOTIFICATION, GIT_AFFIRMATION and GIT_MULTIPLE_RESPONSE) which normally override any input restrictions be default.

ATTR_GEN_INTERACTION_POPPED_OUT indicates that a GIV_POPOUT GenInteraction is in its popped-out state. Absence of this optional attribute indicates that the popout is in its popped-in state. This attribute can be set initially, in which case the Popout will appear popped out. This attribute is also set internally by the UI whenever the state of the GIV_POPOUT changes.

ATTR_GEN_INTERACTION_POPOUT_NOT_CLOSABLE indicates that a GIV_POPOUT, when popped out in its dialog box state, will not be closable; you will only be able to pop it back into its sub-group state.

GenInteraction Visibility

Standard Interactions (Menus)

GenInteraction Types

GenInteraction Attributes

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 GenInteraction Features | Next: 3 GenInteraction Usage


GenInteraction Visibility

Menus and Dialog Boxes: 2.1 GenInteraction Instance Data: GenInteraction Visibility

| Up: GEOS SDK TechDocs | Up | Prev: 2 GenInteraction Instance Data | Next: 2.2 Standard Interactions (Menus)
GII_visibility, MSG_GEN_INTERACTION_GET_VISIBILITY, MSG_GEN_INTERACTION_SET_VISIBILITY

The GII _visibility instance field describes in what manner the user interface will display the Interaction. This is important for GenInteractions because of the wide variety of ways they may appear within an application. In most cases, this attribute will not directly affect the functionality of the children of the Interaction but only the visual implementation and specific UI activations of the objects.

Certain GII_types (see below) may only be meaningful under certain GII_ visibility types, however. For example, GIT_NOTIFICATION, GIT_AFFIRMATION, and GIT_MULTIPLE_RESPONSE gain most of their significant functionality within dialog boxes (visibility GIV_DIALOG). In these cases the visibility will affect the functionality of the Interaction.

Each of the following visibilities is an enumeration of type GenInteractionVisibility ; therefore, you may select one and only one of the following types for your Interaction. By default, a GenInteraction is GIV_SUB_GROUP.

GIV_SUB_GROUP
This specifies that the Interaction serves as a visual grouping of objects within a larger window. This visibility creates an Interaction within the parent window and is therefore not independently displayable. This visibility is the default case.
GIV_POPUP
This specifies that this Interaction is temporary and only appears on-screen for the duration of a single selection. In most specific UIs, the popup will stay up until either an object in the Interaction is activated or until the user clicks somewhere else. Some specific UIs allow a menu to be pinned, however. The most common manifestation of a popup is a menu. Popups typically appear within appropriate "menu bars" of an application but may be independently displayable under different specific UIs.
GIV_DIALOG
This specifies that this Interaction should appear as a dialog box. A dialog box is a temporary window used to display controls or request information from the user. A dialog box can typically contain any number or variety of other generic objects. A dialog box may be either modal or non-modal.
GIV_CONTROL_GROUP
This specifies that this Interaction contains controls and therefore should not appear as a popup Interaction. This prevents the Interaction from disappearing before the user can select an entry in the control group. A control group Interaction may appear as either a sub-group or dialog, depending on the specific UI and its position within the application's generic object tree.
GIV_POPOUT
This specifies that the GenInteraction may act as either a sub-group or as a dialog box, depending on its state. Usually, popouts act as normal sub-group Interactions until the user or the application "pops" them out into a dialog box. Popouts are normally "popped out" into their dialog box state by double-clicking within the confines of the GenInteraction. This popout state is reflected with either the presence or absence of the ATTR_GEN_INTERACTION_POPPED_OUT vardata entry.
GIV_NO_PREFERENCE
This specifies that there is no visual preference for this Interaction. The specific UI will create the Interaction based on hints, the types of children it contains, and its location in the generic tree.

You may also retrieve or set the visibility of any Interaction at run-time. To retrieve the GenInteractionVisibility stored in GII_visibility , send the Interaction a MSG_GEN_INTERACTION_GET_VISIBILITY . You can set the visibility of any Interaction by sending it a MSG_GEN_INTERACTION_SET_VISIBILITY . Make sure that any Interaction you set the visibility for is not currently GS_USABLE or an error will result.

MSG_GEN_INTERACTION_GET_VISIBILITY

byte	MSG_GEN_INTERACTION_GET_VISIBILITY();

This message retrieves the current GenInteractionVisibility stored in the GII_visibility instance field of an Interaction.

Source: Unrestricted.

Destination: Any GenInteraction object.

Parameters: None.

Return: GenInteractionVisibility of the Interaction object.

Interception: Generally not intercepted.

MSG_GEN_INTERACTION_SET_VISIBILITY

void	MSG_GEN_INTERACTION_SET_VISIBILITY(
        byte	visibility);

This message sets the GII_visibility instance data for the GenInteraction. This message must pass a valid GenInteractionVisibility type to the Interaction object. The Interaction must not be GS_USABLE when receiving this message. The new visibility will take effect when the Interaction is next made GS_USABLE.

Source: Unrestricted.

Destination: Any non-usable GenInteraction object.

Parameters: visibility GenInteractionVisibility for interaction.

Return: Nothing.

Interception: Generally not intercepted.

Warnings: Make sure that the object sent this message is not GS_USABLE.

| Up: GEOS SDK TechDocs | Up | Prev: 2 GenInteraction Instance Data | Next: 2.2 Standard Interactions (Menus)


Standard Interactions (Menus)

Menus and Dialog Boxes: 2.2 GenInteraction Instance Data: Standard Interactions (Menus)

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 GenInteraction Visibility | Next: 2.3 GenInteraction Types
ATTR_GEN_INTERACTION_GROUP_TYPE, GenInteractionGroupType

Often you will set up standard menus such as the File menu, the Window menu, the Edit menu, and the View menu. You can set these menus up very simply, with the attribute ATTR_GEN_INTERACTION_GROUP_TYPE .

This attribute specifies the type of menu the GenInteraction is via a value of type GenInteractionGroupType . This can be any of the following:

GIGT_FILE_MENU
Creates a standard File menu. Typically has a document control and print control as its children.
GIGT_EDIT_MENU
Creates a standard Edit menu with Cut, Copy, Paste, and Undo as appropriate. Typically has a GenEditControl as its child.
GIGT_VIEW_MENU
Creates a standard View menu; typically has a GenView controller as its child.
GIGT_OPTIONS_MENU
Creates a standard Options menu; typically, this menu has application-specific option entries as well as a GenToolControl as its children.
GIGT_WINDOW_MENU
Creates a standard Window menu; typically has a display controller as its child.
GIGT_HELP_MENU
Creates a standard Help menu; typically has the help objects as its children.
GIGT_PRINT_GROUP
Indicates that the GenInteraction is the print group. Typically contains a print controller and other print commands.

Each of the above types will set up the proper GenInteraction attributes and other generic objects to complete the interactions. Thus, by setting up a GenInteraction like this:

@object GenInteractionClass MyEditMenu = {
    GI_comp = MyEditControl;
    GII_visibility = GIV_POPUP;
    ATTR_GEN_INTERACTION_GROUP_TYPE =
            (GIGT_EDIT_MENU);
}

you can have a complete Edit menu in your program. (Of course, you must also supply the GenEditControl object that would be MyEditControl in the example.)

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 GenInteraction Visibility | Next: 2.3 GenInteraction Types


GenInteraction Types

Menus and Dialog Boxes: 2.3 GenInteraction Instance Data: GenInteraction Types

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Standard Interactions (Menus) | Next: 2.4 GenInteraction Attributes
GII_type, MSG_GEN_INTERACTION_GET_TYPE, MSG_GEN_INTERACTION_SET_TYPE

The GII_type instance field describes the contents of the Interaction. This attribute depends on the makeup of the children within the Interaction. (Note that it would be unusual for an Interaction to appear without children.) In most cases, this will directly affect the functionality of the particular Interaction. Every GenInteraction object should contain children that perform some function within the UI. That function is determined in part by this instance field.

Each of the following types is an enumeration of GenInteractionType ; therefore, you may select one and only one of the following types for your Interaction. By default, a GenInteraction is GIT_ORGANIZATIONAL.

GIT_ORGANIZATIONAL
This specifies that the Interaction is merely organizational. This allows the Interaction to provide geometry management (and not much else) to its children. Any objects within an organizational Interaction should be able to perform their actions independently. Organizational Interactions may appear in any visual form: sub-groups, menus, or dialog boxes.
GIT_PROPERTIES
This specifies that the Interaction contains properties (attributes) that the user can change. Typically, a properties Interaction will operate in one of two modes: immediate or delayed. If the GII_visibility attribute causes this Interaction to appear as a dialog box, the specific UI may create "Apply" and "Reset" triggers. In delayed mode, an apply trigger allows the Interaction to process changes made within the properties Interaction as a group rather than individually; the reset trigger allows the Interaction to reset the properties to their former state. If a popup is GIT_PROPERTIES, it will operate in immediate mode even if hints attempt to override this.
GIT_PROGRESS
This specifies that the Interaction displays a progress report for some operation. For example, a dialog box may appear showing a disk copy operation with the time remaining to its completion. If the GII_visibility attribute causes this Interaction to appear as a dialog box, the specific UI may create a "Stop" trigger that halts the operation in progress and the progress reporting. Your application is responsible for stopping the Interaction from communicating its progress report and may dismiss the Interaction if the specific UI desires.
GIT_COMMAND
This specifies that the Interaction contains commands that the application provides. If the GII_visibility attribute causes this Interaction to appear as a dialog box, the specific UI may create a "Close" trigger to dismiss the dialog. You will have to supply your own specific command triggers within a GIT_COMMAND dialog box.
GIT_NOTIFICATION
This specifies that the Interaction sends notification of some event. If the GII_visibility attribute causes this Interaction to appear as a dialog box, the specific UI may create an "OK" trigger that the user can press to acknowledge the notification.
GIT_AFFIRMATION
This specifies that the Interaction asks for confirmation of an operation. If the GII_visibility attribute causes this Interaction to appear as a dialog box, the specific UI may create "Yes" and "No" triggers. Depending upon the Specific UI, these triggers may dismiss the dialog box.
GIT_MULTIPLE_RESPONSE
This specifies that this Interaction may include multiple items that the user can respond to. (Your application should add custom response triggers using the GenTrigger's vardata ATTR_GEN_TRIGGER_INTERACTION_COMMAND .) If you wish these triggers to appear in a dialog box reply bar, use HINT_SEEK_REPLY_BAR .

You may retrieve or set the type of any Interaction at run-time. To retrieve the GenInteractionType stored in the GII_type instance field, send MSG_GEN_INTERACTION_GET_TYPE . You can set the type of any Interaction by sending it MSG_GEN_INTERACTION_SET_TYPE . Make sure that any Interaction you set the type for is not currently GS_USABLE or an error will result.

MSG_GEN_INTERACTION_GET_TYPE

byte	MSG_GEN_INTERACTION_GET_TYPE();

This message returns the current GenInteractionType stored in the GII_type instance field for the Interaction.

Source: Unrestricted.

Destination: Any GenInteraction object.

Parameters: None.

Return: GenInteractionType of the Interaction.

Interception: Generally not intercepted.

MSG_GEN_INTERACTION_SET_TYPE

void	MSG_GEN_INTERACTION_SET_TYPE(
        byte	type);

This message sets the GII_type instance data for the GenInteraction. This message must pass a valid GenInteractionType enumerated type to the Interaction object. The Interaction must not be GS_USABLE when receiving this message; the new type will take effect when the Interaction is next made GS_USABLE.

Source: Unrestricted.

Destination: Any non-usable GenInteraction object.

Parameters: type GenInteractionType for the Interaction.

Return: Nothing.

Interception: Generally not intercepted.

Warnings: Make sure that the object is not GS_USABLE when sending it this message.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Standard Interactions (Menus) | Next: 2.4 GenInteraction Attributes


GenInteraction Attributes

Menus and Dialog Boxes: 2.4 GenInteraction Instance Data: GenInteraction Attributes

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 GenInteraction Types | Next: 3 GenInteraction Usage
GII_attrs, MSG_GEN_INTERACTION_GET_ATTRS, MSG_GEN_INTERACTION_SET_ATTRS

The GII_attrs instance field describes how the Interaction behaves under various circumstances. These attributes only affect the behavior of dialog boxes. If the Interaction given these attributes is not a dialog box, the attributes will have no effect. This field specifies how a GIV_DIALOG Interaction may be initiated and whether input to other parts of the UI is allowed while the Interaction is active. The GII_attrs instance field is a bitfield and therefore any combination of these attributes may be set.

None of these attributes is set by default.

GIA_NOT_USER_INITIATABLE
This attribute specifies that the user cannot bring up the dialog directly. Unless this attribute is set, most specific UIs will place a default activation trigger within a proper place in the UI to initiate the dialog box. This attribute prevents this trigger from appearing. To bring up a dialog box set GIA_NOT_USER_INITIATABLE, your application must send the dialog a MSG_GEN_INTERACTION_INITIATE. Usually, these Interactions should be direct children of either a GenPrimary or GenApplication.
GIA_INITIATED_VIA_USER_DO_DIALOG
This attribute specifies that the application brings up this Interaction through UserDoDialog () . You should use this routine in special cases where you need the user to respond to a dialog box before continuing with your application thread. (This routine blocks the calling thread until the user responds to the dialog box.) Any dialog boxes marked with this attribute should not be initiated with MSG_GEN_INTERACTION_INITIATE. The UI won't include a default activation trigger for dialog boxes with this attribute.
GIA_MODAL
This attribute specifies that while this dialog is up, all other input to the application should be ignored. This attribute will create an application modal dialog box.
GIA_SYS_MODAL
This attribute specifies that while this dialog is up, all other input to the system (including other applications) should be ignored. This attribute will create a system modal dialog box.

You may retrieve or set the GII_attrs of any Interaction at run-time. To retrieve the GenInteractionAttrs , send the Interaction a MSG_GEN_INTERACTION_GET_ATTRS . You can set the GenInteractionAttrs of any Interaction by sending it a MSG_GEN_INTERACTION_SET_ATTRS . Make sure that any Interaction you set these attributes for is not currently GS_USABLE or an error will result.

If your application contains several dialog boxes sitting directly under a GenPrimary (typically with their GIA_NOT_USER_INITIATABLE attribute), the application may take longer to start up. This happens because the system will have to process each of the dialogs individually and determine at that point that a trigger should not be created for them.

You may instead wish to create an organizational Interaction to manage these non-user initiatable Interactions. To do this, create a single GIT_ORGANIZATIONAL GIA_NOT_USER_INITIATABLE dialog box under the GenPrimary. All "real" dialogs should then be placed as children of this "holding" Interaction. When the application starts up, it only needs to process this one Interaction.

Grouping dialogs in this way also allows easier initiation and dismissal of the dialogs all at once. Simply encapsulate MSG_GEN_INTERACTION_INITIATE or MSG_GEN_GUP_INTERACTION_COMMAND with IC_DISMISS and use MSG_GEN_SEND_TO_CHILDREN on the one Interaction.

MSG_GEN_INTERACTION_GET_ATTRS

byte	MSG_GEN_INTERACTION_GET_ATTRS();

This message retrieves the current GII_attrs instance field from the Interaction sent the message.

Source: Unrestricted.

Destination: Any GenInteraction object.

Parameters: None.

Return: GenInteractionAttrs of the Interaction.

Interception: Generally not intercepted.

MSG_GEN_INTERACTION_SET_ATTRS

void	MSG_GEN_INTERACTION_SET_ATTRS(
        byte	setAttrs,
        byte	clearAttrs);

This message sets the GII_attrs instance data for the GenInteraction sent the message. The Interaction must not be GS_USABLE when receiving this message. The new attributes will take effect when the Interaction is next made GS_USABLE.

Source: Unrestricted.

Destination: Any non-usable GenInteraction object.

Parameters: setAttrs GenInteractionAttrs to set.

clearAttrs
GenInteractionAttrs to clear.

Return: Nothing.

Interception: Generally not intercepted.

Warnings: Make sure that the object is not GS_USABLE when sending it this message.

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 GenInteraction Types | Next: 3 GenInteraction Usage


GenInteraction Usage

Menus and Dialog Boxes: 3 GenInteraction Usage

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 GenInteraction Instance Data | Next: 4 Supplemental Usage

You will probably need to create GenInteractions at various points within your application. The GenInteraction object is one of the most flexible generic objects within GEOS. Not only can an Interaction operate as a menu, dialog, or sub-group, but there also exist several pre-defined dialog boxes with default triggers for your use. These dialogs will make writing your application easier and faster. The simplest way to see the wide variety of options available to you is to examine the different types of Interactions available with different instance data values.

Visibilities

Types

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 GenInteraction Instance Data | Next: 4 Supplemental Usage


Visibilities

Menus and Dialog Boxes: 3.1 GenInteraction Usage: Visibilities

| Up: GEOS SDK TechDocs | Up | Prev: 3 GenInteraction Usage | Next: 3.2 Types

When first creating an Interaction, you must decide in what form to visually present your communication between the application and the user. This concern may be somewhat cosmetic in form but may also affect the functionality of the Interaction itself. For instance, more than one GenInteractionType will support different actions with different visibility types.

GenInteraction Sub-Groups (GIV_SUB_GROUP)

The simplest GenInteraction is one used to arrange the geometry of other objects. To do this, a GenInteraction should be GIV_SUB_GROUP (the default).

You can use hints provided in GenClass to arrange the appearance of objects within the GenInteraction. Interactions usually provide the greatest flexibility for geometry manipulation. For full information, see the Managing UI Geometry chapter.

Note that a GIV_SUB_GROUP Interaction may only appear within a parent window as it is not itself independently displayable.

Code Display 7-4 Arranging a Group of Children

/* This Interaction creates a typical number keypad arrangement. */
@object GenInteractionClass MyKeyPad = {
    /* The Interaction by default is GIV_SUB_GROUP and GIT_ORGANIZATIONAL,
     * so there is no need to set these types. */
    GI_comp = Row789, Row456, Row123;
    /* The Interaction's three children will be arranged vertically. The
     * UI will also draw a border around the Interaction itself. */
    HINT_ORIENT_CHILDREN_VERTICALLY;
    HINT_DRAW_IN_BOX;
}
@object GenInteractionClass Row789= {
    GI_comp = Key7, Key8, Key9;
    /* Each row is then arranged horizontally to gain three rows
     * of three items each. */
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
@object GenInteractionClass Row456= {
    GI_comp = Key4, Key5, Key6;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
@object GenInteractionClass Row123= {
    GI_comp = Key1, Key2, Key3;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
/* Each of the child `keys' will be a GenTrigger with appropriate visual
 * moniker. (For simplicity, only one trigger is shown here.) */
@object GenTriggerClass Key9 = {
    GI_visMoniker = "9";
}

Many hints from GenClass may affect the appearance of GIV_SUB_GROUP Interactions. See the Managing UI Geometry chapter for details.

Of special interest to sub-group Interactions is HINT_DRAW_IN_BOX . This hint will enclose the associated Interaction within a line border. You should use this hint when you wish to distinguish a collection of objects within an Interaction from some other objects.

GenInteractions as Menus (GIV_POPUP)

Most specific UIs implement GIV_POPUP as a menu. Menus are interactions with options that only appear when the menu is activated by the user. Popups make it possible for a user to browse and choose among a variety of commands without cluttering up the UI with dozens of objects. A popup may appear at almost any location within the UI. If a popup is placed as a direct child of a GenPrimary, it may appear in an appropriate menu bar location across the top of the window.

A popup is most often one of GIT_ORGANIZATIONAL, GIT_PROPERTIES, or GIT_COMMAND. (See GenInteraction Types .) What type of popup you select depends on the type of children the Interaction contains. For example, an application may implement a font menu as a GIT_PROPERTIES popup. A GIT_COMMAND popup might issue commands relating to the appearance of text within a text editor. Depending on the GenInteractionType , a popup may appear in forms other than a menu. (The specific UI has final say over the appearance of a popup.)

A GIV_POPUP Interaction must contain a group of children. If the Interaction appears as a menu, these children will become menu items. Most often, these children will be GenTriggers or GenItems within GenItemGroups.

Code Display 7-5 Using a GenInteraction to Create a Menu

/* This example is taken from the Hello World sample application. */
@object GenInteractionClass HelloMenu = {
    GI_visMoniker = 'M', "Menu";					/* The moniker of the menu is used in
                     * the primary window's menu bar (if the
                     * specific UI employs a menu bar). */
    GI_comp = @HelloColorBox;					/* The only child of the menu (the only
                     * item in the menu) is the dialog box. */
    GII_visibility = GIV_POPUP;					/* This attribute designates the
                     * interaction as a menu or a sub-menu. */
}
@object GenInteractionClass HelloColorBox = {
    GI_comp = @HelloBlueTrigger, @HelloGoldTrigger;
    GI_visMoniker = 'C', "Color";
    GII_visibility = GIV_DIALOG;
}
@object GenTriggerClass HelloBlueTrigger = {
    GI_visMoniker = 'B', "Blue";				/* The 'B' indicates the keyboard navigation
                 * character for this trigger. */
    GTI_destination = process;				/* Send the message to the Process object. */
    GTI_actionMsg = MSG_HELLO_CHANGE_TO_BLUE;						/* Send this message. */
}
@object GenTriggerClass HelloGoldTrigger = {
    GI_visMoniker = 'G', "Gold";				/* The 'G' indicates the keyboard navigation
                 * character for this trigger. */
    GTI_destination = process;				/* Send the message to the Process object. */
    GTI_actionMsg = MSG_HELLO_CHANGE_TO_GOLD;						/* Send this message. */
}

If a child of the GIV_POPUP Interaction is another Interaction, the specific UI might change the visual implementation of that child. What change takes place depends on the specific UI. In OSF/Motif, for example, placing a sub-group Interaction within a popup Interaction causes the UI to draw lines separating the sub-group from the other children in the popup. (You may prevent this line by adding HINT_SAME_CATEGORY_AS_PARENT .)

Placing a popup interaction within another popup is a special case and will create nested menus under most specific UIs. To do this, merely create a GIV_POPUP Interaction as a child of another GIV_POPUP Interaction. The specific UI will automatically create the second menu as a sub-menu of the first. The item in the menu bringing up the sub-menu may be labeled with an arrow under certain specific UIs. Menus may be nested to an arbitrary number of levels.

Code Display 7-6 Nested Menus

@object GenInteractionClass TextMenu = {
    GI_comp = @JustificationSubMenu;
    GI_visMoniker = `T', "Text";
    GII_visibility = GIV_POPUP;
}
@object GenInteractionClass JustificationMenu = {
    GI_comp = @JustificationList;
    GI_visMoniker = `J', "Justification";
    GII_visibility = GIV_POPUP;
}
@object GenItemGroupClass JustificationList = {
    GI_comp = @LeftEntry, @RightEntry, @CenterEntry, @FullEntry;
    /* These children are not shown. */
}

List Objects (GenItemGroups and GenBooleanGroups) within a GIV_POPUP Interaction may be used to show object properties in a menu. The menu should be GIT_PROPERTIES in this case. Depending on the Specific UI, a properties menu may be implemented in a different fashion from other menus. (In OSF/Motif, there is no visual difference between a properties menu and other menus.) Other types have no effect on the behavior or appearance of GIV_POPUPs.

GenInteractions as Dialog Boxes (GIV_DIALOG)

The GIV_DIALOG visibility will instruct the specific UI to build the Interaction as a dialog box. A dialog box is an independently-displayable interface element used to display information or other UI objects. Therefore, it is similar to a window although there are some differences. (Most dialog boxes, for example, are not resizable whereas most windows are.)

Dialog boxes also enhance the functionality of other GenInteractionType s. Many GII _types are only significant as dialog boxes. For example, a GIT_NOTIFICATION Interaction will only appear with an "OK" trigger if it is also a dialog box. A GIT_PROPERTIES Interaction may contain properties within any visibility, but only if built as a dialog can it implement delayed mode. (If a GIT_PROPERTIES Interaction is built as a menu or sub-group, it will always operate in immediate mode.) Delayed mode allows the user to change the properties of an object and later apply those changes all at once. Within a dialog box, an "Apply" trigger will make these changes.

Most specific UIs will build a dialog box with an activation trigger present in the UI that brings up the dialog box. These dialog boxes are known as user initiatable. There are ways to override this behavior if you need a non-user initiatable dialog box or a dialog box that blocks the calling thread until answered (forcing the user to respond to the dialog box immediately). See Modality for Dialogs .

By default, dialog boxes are not resizable. HINT_INTERACTION_MAKE_RESIZABLE may be used on a non-modal dialog box to make that dialog resizable. In OSF/Motif, the Specific UI will supply such a dialog with a means to alter its shape. HINT_INTERACTION_MAKE_RESIZABLE is only meaningful for GIV_DIALOG Interactions.

GenInteractions as Popouts (GIV_POPOUTS)

MSG_GEN_INTERACTION_POP_IN, MSG_GEN_INTERACTION_POP_OUT, MSG_GEN_INTERACTION_TOGGLE_POPOUT

Popouts are special GenInteractions that may act as either sub-group Interactions or as dialog boxes, depending on their state. Usually, popouts act as normal sub-group Interactions until the user or the application "pops" them out into a dialog box. Popouts are normally "popped out" into their dialog box state by double-clicking within the confines of the GenInteraction. This popout state is reflected with either the presence or absence of the ATTR_GEN_INTERACTION_POPPED_OUT vardata entry.

You can pop out a GIV_POPOUT GenInteraction in its sub-group state by sending it MSG_GEN_INTERACTION_POP_OUT. Similarly, you can pop a GIV_POPOUT back into its sub-group state from the dialog box state by sending it MSG_GEN_INTERACTION_POP_IN. MSG_GEN_INTERACTION_TOGGLE_POPOUT toggles a popout from whatever state it currently is into its other state.

By default, popouts in their dialog box state contain a special trigger to pop them back into their sub-group state. They may also be popped back in by double-clicking within the popped-out GenInteraction. Popouts also normally provide a close trigger; this trigger dismisses the dialog box but does not return the popout to its sub-group state. You should provide a trigger somewhere within your UI that sends MSG_GEN_INTERACTION_POP_IN or MSG_GEN_INTERACTION_POP_OUT to the popout, so that the user can bring any popout they dismiss back on-screen. Alternatively, you can add ATTR_GEN_INTERACTION_POPOUT_NOT_CLOSABLE on the Popout to prevent the creation of a "Close" trigger.

MSG_GEN_INTERACTION_POP_IN

void	MSG_GEN_INTERACTION_POP_IN();

This message `pops in' a GIV_POPOUT GenInteraction into its default sub-group state. This message has no effect if the GenInteraction is not a GIV_POPOUT or if the GenInteraction is already `popped in.'

Parameters: Nothing.

Return: Nothing.

MSG_GEN_INTERACTION_POP_OUT

void	MSG_GEN_INTERACTION_POP_OUT();

This message `pops out' a GIV_POPOUT GenInteraction into its dialog box state. This message has no effect if the GenInteraction is not a GIV_POPOUT or if the GenInteraction is already `popped out.'

Parameters: Nothing.

Return: Nothing.

MSG_GEN_INTERACTION_TOGGLE_POPOUT

void	MSG_GEN_INTERACTION_TOGGLE_POPOUT();

This message toggles the state of a GIV_POPOUT GenInteraction from its `popped in' state to its `popped out' state (or `popped out' and hidden from view) and vice-versa. The specific UI has control over how this message is implemented.

Parameters: Nothing.

Return: Nothing.

| Up: GEOS SDK TechDocs | Up | Prev: 3 GenInteraction Usage | Next: 3.2 Types


Types

Menus and Dialog Boxes: 3.2 GenInteraction Usage: Types

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Visibilities | Next: 4 Supplemental Usage

Possibly more important than how an Interaction is visually displayed is what role that Interaction should play within your UI. That role depends in part on what children the Interaction contains. What the Interaction does with those children is determined by the GenInteractionType of that object. Interactions of different visibility may function in essentially the same manner, but Interactions of different types may differ greatly in their functionality.

For example, if an Interaction contains a GenItemGroup with three GenItem children, it will not affect the basic functionality if that Interaction is built as a dialog, popup, or sub-group. Each item will still perform its default function (such as sending a message). The GII _type of Interaction might affect that functionality, however, by forcing the Items to send out their messages as a group or individually (for example in a GIT_PROPERTIES Interaction).

You should pick the GenInteractionType based on what actions you wish to perform on the children you select for the Interaction. By default, an Interaction is of type GIT_ORGANIZATIONAL. In some cases, the functionality you need will reside entirely with the children; in those cases, a GIT_ORGANIZATIONAL will most likely suffice.

Certain GenInteractionType Interactions, if built as GIV_DIALOG, may create a reply bar with standard response triggers . A reply bar is a special area of a dialog Interaction to place commands for user responses. Standard response triggers perform additional actions for dialog Interactions, apart from any functionality your application provides. You do not need to add these triggers yourself; they are added by the Specific UI.

Standard response triggers perform one of several pre-defined InteractionCommand commands. Depending on the particular type of Interaction, each command may perform different actions. An InteractionCommand is a special data type that the Specific UI recognizes. The Specific UI decides what action to take upon receiving this command; depending on the value of the InteractionCommand , the Specific UI may perform several different actions.

For example, the GIT_NOTIFICATION type, if built as a dialog box, may create an "OK" trigger automatically. The trigger will become one of the dialog box's children. This trigger sends out the InteractionCommand IC_OK when activated. The Specific UI will also decide how to implement that IC_OK message. (In OSF/Motif, for example, IC_OK will dismiss the dialog box as one of its actions.)

You can create your own response triggers within an Interaction. You can also replace any standard response trigger by supplying one of your own. For complete information on how InteractionCommand s and response triggers work, see Interaction Commands .

Organizational Interactions

An organizational interaction is the simplest type of interaction. It is also the default GenInteractionType . An organizational Interaction only functions to contain objects--without indicating any meaning to the makeup of those objects. Because of this, a GIT_ORGANIZATIONAL Interaction should not interpret any InteractionCommand types. See Interaction Commands .

GIT_ORGANIZATIONAL Interactions are only used, therefore, to group their children; there is never any implied meaning to the children they contain. The Interaction may have a certain visual form defined in its GenInteractionVisibility field, but presumably the Interaction should perform its activity under any visible arrangement.

You will most often need an organizational Interaction to arrange objects within another window, whether that "window" is a GenPrimary or a GIV_DIALOG GenInteraction. For example, you may need organizational Interactions solely to arrange a group of generic children in a horizontal or vertical row. There are several hints in GenClass that may affect the visual implementation of an organizational Interaction. See the Managing UI Geometry chapterfor full details.

Organizational Interactions may appear as sub-groups, menus, or dialog boxes; each of its items should already contain all the functionality needed. For example, an edit menu (GIV_POPUP Interaction) with three triggers (Cut, Copy, and Paste) could be organizational if each of those triggers is capable of performing its action purely through the sending of a message (a function of GenTriggerClass ).

No standard response triggers are provided with a GIT_ORGANIZATIONAL Interaction, even if it is built as a dialog box, though you may supply your own. See Standard Response Triggers .

Code Display 7-7 Organizational Interactions

/* This properties dialog box will group a collection of GIT_ORGANIZATIONAL
 * Interactions. */
@object GenInteractionClass MyMasterInteraction = {
    GI_comp = FirstOrganizational, SecondOrganizational;
    GII_visibility = GIV_DIALOG;
    GII_type = GIT_PROPERTIES;
}
@object GenInteractionClass FirstOrganizational = {
/* GenInteractions are GIT_ORGANIZATIONAL by default. */
    GI_comp = One, Two, Three;
}
@object GenInteractionClass SecondOrganizational = {
    GI_comp = Four, Five, Six;
}
@object GenTriggerClass One = {
/* This trigger, after sending out MY_SPECIAL_MESSAGE to the process, will send a
 * MSG_GEN_GUP_INTERACTION_COMMAND with IC_INTERACTION_COMPLETE to the first
 * non-GIT_ORGANIZATIONAL Interaction it encounters up the tree
 * (MyMasterInteraction). This behavior is due to the GenAttribute
 * GA_SIGNAL_INTERACTION_COMPLETE. (See GenClass.) */
    GTI_actionMsg = MY_SPECIAL_MESSAGE;
    GTI_destination = process;
    GI_attrs = GA_SIGNAL_INTERACTION_COMPLETE;
}

Properties Interactions

GIT_PROPERTIES Interactions allow the Interaction to display and set attributes of a specific selected object (the "current selection"). In many cases, this current selection will be the target of the application. (See the Input chapter.) Users can change the UI gadgets in the properties group to change the attributes of the selected object. When the user selects a different object, the UI gadgetry changes to reflect the attributes of the new selection.

For example, a properties dialog may alter text properties within a text editor. If some text is selected, changing properties in the text dialog will change the appearance of the text.

Properties Interactions may appear as sub-groups, popups, or dialog boxes. If a properties Interaction takes the form of a dialog box, the Specific UI may create response triggers with the InteractionCommand types IC_APPLY, IC_RESET and IC_DISMISS. These triggers will either apply changes made in the properties group, reset those changes to their initial state, or close the dialog, respectively. In OSF/Motif these triggers are labeled "Apply" or "OK," "Reset," and "Close" or "Cancel."

Code Display 7-8 GenInteraction as Properties Dialog Box

@object GenInteractionClass MyInteraction = {
    GI_visMoniker = "Dialog Box";
    GI_comp = PropertiesList;
    GII_visibility = GIV_DIALOG;
    GII_type = GIT_PROPERTIES;
}
/* The GenItemGroup below is a list of three properties. */
@object GenItemGroupClass PropertiesList = {
    GI_visMoniker = "Properties List";
    GI_comp = @PropOne, @PropTwo, @PropThree;
    GIGI_behaviorType = GIGBT_NON_EXCLUSIVE;
}
@object GenItemClass PropOne = {
    GI_visMoniker = "A";
    GII_identifier = 1;
}
@object GenItemClass PropTwo = {
    GI_visMoniker = "B";
    GII_identifier = 2;
}
@object GenItemClass PropThree = {
    GI_visMoniker = "C";
    GII_identifier = 3;
}

There are two ways that an application can deal with UI gadgetry in a property dialog. Either the application can work completely off the state change notification messages sent by the UI gadgetry when the properties are changed, or the application may need to do some extra work after the properties are changed. This extra work can include checking for valid settings in the UI gadgetry, querying the UI gadgetry for their states, or dealing with the UI gadgetry as a group rather than individually (i.e. fetching each of their states and using them all at once to make a single change instead of making separate changes for each state).

Delayed and Immediate Mode

Properties dialogs work in one of two modes: immediate and delayed. In immediate mode, the UI gadgetry within a properties box reflects the actual state of those objects. Changing selections within the properties Interaction changes the state of those objects immediately. For example, if a "Fonts" dialog box containing a list of font selections is in immediate mode, selecting each separate font will cause the selected text to immediately display itself in the selected font.

In delayed mode, the UI gadgetry within the properties box reflects a separate "user" state rather than the actual state. The user is free to change settings within the properties Interaction without causing such changes to be made on the object immediately. The UI does this by storing two states for the UI gadgetry: the actual state (which reflects the current state of the objects) and a user state (which reflects the state specified in the dialog box). The user can apply changes made within a delayed mode properties Interaction with use of an "Apply" mechanism. Usually, the Specific UI will provide an "Apply" trigger for such a case.

If either the Specific UI or hints determine that the properties dialog box should be run in delayed mode, an apply trigger will be supplied. In OSF/Motif, an "Apply" trigger will be placed in a reply bar along with a "Close" trigger that dismisses the property box.

Properties Hints

The GIT_PROPERTIES type and GIV_DIALOG visibility indicate to the Specific UI to build a property dialog box. The Specific UI will also determine whether this box should run in immediate or delayed mode. Because the application works completely through its UI gadgets within the dialog, it will support either mode. However, there are good reasons for wanting the properties to work in one mode or another. You can supply additional hints for this purpose.

These hints only affect the behavior of properties dialog boxes (GIV_DIALOG). Therefore, if an Interaction is a GIV_POPUP or GIV_SUB_GROUP, these hints will have no effect. If the Interaction is a GIV_CONTROL_GROUP or GIV_NO_PREFERENCE, these hints may force the object to appear as a dialog box.

HINT_INTERACTION_COMPLEX_PROPERTIES indicates that the properties dialog box contains complex properties and therefore a means to reset the properties to their initial state should be supplied if available. In OSF/Motif, for example, a "Reset" response trigger is supplied.

HINT_INTERACTION_SIMPLE_PROPERTIES indicates that the properties dialog box contains simple properties and therefore a means to reset the properties to their initial state is probably unneeded.

HINT_INTERACTION_RELATED_PROPERTIES indicates that the properties dialog contains properties that are closely related. Therefore, the specific UI should run the objects as a group in delayed mode and provide an "Apply" trigger. This might be useful if you set several graphics area properties (such as color, texture, and dithering) and only wish to send out the changes after the final selection has been made for all the properties.

HINT_INTERACTION_UNRELATED_PROPERTIES indicates that the properties dialog contains unrelated properties that may be set individually. Therefore, the Interaction can be run in immediate mode and no "Apply" trigger is needed.

HINT_INTERACTION_SLOW_RESPONSE_PROPERTIES indicates that the properties dialog contains properties that may take a long time to be reflected in the state of the object. The specific UI may create an "Apply" trigger operating in delayed mode so that the user can set these attributes all at once.

HINT_INTERACTION_FAST_RESPONSE_PROPERTIES indicates that this properties dialog contains properties in which changes can be quickly reflected. The Interaction will be run in immediate mode without any "Apply" and "Reset" triggers.

Modifying Triggers for Custom Properties

HINT_INTERACTION_REQUIRES_VALIDATION

If an application needs to do additional work on a properties dialog box, such as validation of the attributes, you should define a "custom" properties dialog box. You can do this by replacing the default "Apply" and "Reset" triggers with ones of your own design.

HINT_INTERACTION_REQUIRES_VALIDATION indicates that this properties dialog should prompt the user for verification before implementing changes to the properties group. The Interaction will thus be implemented in delayed mode while also allowing a custom "Apply" trigger. The trigger action should be handled in the normal fashion to add any additional functionality to validate before proceeding with the apply.

You can modify the standard IC_APPLY trigger by adding your own apply trigger with ATTR_GEN_TRIGGER_INTERACTION_COMMAND set to IC_APPLY. This trigger will replace the default IC_APPLY trigger that the Specific UI would have supplied otherwise. HINT_SEEK_REPLY_BAR places the trigger in the property dialog's reply bar. In the GenInteraction itself, you might want to include HINT_INTERACTION_REQUIRES_VALIDATION or another more appropriate hint to ensure that the properties dialog will be working in delayed mode.

An application-supplied trigger allows the application to define whatever action output and method it desires. When the user activates this trigger, the action message will be sent out to the action output (as usual for GenTriggers); the default IC_APPLY functionality will not be performed. In the handler for this custom message, the application can do whatever is necessary with the properties dialog (such as validation of the attributes).

When finished, the application can either manually fetch the state of the attributes or send MSG_GEN_GUP_INTERACTION_COMMAND with IC_APPLY to the GenInteraction. (This is the default behavior of an IC_APPLY InteractionCommand .) You may choose not to send out this message if you wish to replace instead of supplement the default behavior.

Finally, you can send MSG_GEN_GUP_INTERACTION_COMMAND with IC_INTERACTION_COMPLETE to notify the GenInteraction that the user has finished a single usage of the Interaction. (You may also mark such a trigger with GA_SIGNAL_INTERACTION_COMPLETE.) Upon this notification, the specific UI will decide if the GenInteraction should be dismissed. Instead, you may send IC_DISMISS to unconditionally dismiss the Interaction.

Code Display 7-9 Replacing the Default IC_APPLY Trigger

@object GenInteraction LineProperties = {
    GI_visMoniker = "Line Properties";
    GI_comp = LineWidth, LineStyle, LinePropertiesApply;
    GII_type = GIT_PROPERTIES;
    GII_visibility = GIV_DIALOG;
    HINT_INTERACTION_REQUIRES_VALIDATION;						/* Forces delayed mode. */
}
@object GenItemGroupClass LineWidth = {
    /* ... */
}
@object GenItemGroupClass LineStyle = {
    /* ... */
}
@object GenTriggerClass LinePropertiesApply = {
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_PROPERTIES_APPLY;
    /* Setting GA_SIGNAL_INTERACTION_COMPLETE will dismiss the dialog
     * after applying the properties. */
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    /* Setting this attribute to IC_APPLY tells the specific UI to replace
    * the standard response trigger with this one. */
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_APPLY };
    HINT_SEEK_REPLY_BAR;
}

The moniker for an application-supplied apply trigger may be set by the application or (preferably) left blank so that the Specific UI can provide an appropriate moniker. The Specific UI uses the IC_APPLY data in the ATTR_GEN_TRIGGER_INTERACTION_COMMAND vardata and any properties hints to determine what moniker to use. (In OSF/Motif this is either "Apply" or "OK.")

If the application wants to alter a moniker but does not need to do any other work, the trigger's output fields should be left blank. Because the trigger has ATTR_GEN_TRIGGER_INTERACTION_COMMAND , if the output fields are null, the trigger will send MSG_GEN_GUP_INTERACTION_COMMAND with the ATTR_GEN_TRIGGER_INTERACTION_COMMAND data to itself. This message will travel up to the appropriate properties GenInteraction where it will be handled, as usual. If doing this, you should make sure to mark any replacement triggers GA_SIGNAL_INTERACTION_COMPLETE so that the Specific UI can determine whether to dismiss the dialog or not when the trigger is activated. (See Replacing the Default IC_APPLY moniker .)

Code Display 7-10 Replacing the Default IC_APPLY moniker

@object GenTriggerClass LinePropertiesApply = {
    GI_visMoniker = "Set Line Properties";
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_APPLY };
    HINT_SEEK_REPLY_BAR;
}

An alternate way to supplement an apply trigger's default behavior is to subclass GenInteractionClass and intercept MSG_GEN_GUP_INTERACTION_COMMAND . If the InteractionCommand sent is IC_APPLY, the application's supplemental apply work may then be done. Then call the superclass to finish with the default apply behavior.

Note that this method cannot be used to override the default GA_SIGNAL_INTERACTION_COMPLETE behavior as that is done separately with MSG_GEN_GUP_INTERACTION_COMMAND and IC_INTERACTION_COMPLETE.

For either default or custom properties dialogs, if the properties dialog contains a large number of attributes, it may be useful to provide a means to reset the properties to their former state. This is often done with a hint such as HINT_INTERACTION_COMPLEX_PROPERTIES .

Just as you may use ATTR_GEN_TRIGGER_INTERACTION_COMMAND with IC_APPLY to provide a custom apply trigger, you may use ATTR_GEN_TRIGGER_INTERACTION_COMMAND with IC_RESET to provide a custom reset trigger. This is useful if the application needs to do some additional work when the user resets the attributes or if the application wishes to override the default reset moniker ("Reset" in OSF/Motif). You may still perform the default reset behavior (sending MSG_GEN_RESET to the UI objects) by sending MSG_GEN_GUP_INTERACTION_COMMAND with IC_RESET to the properties Interaction.

Command Interactions

GIT_COMMAND Interactions allow the user to set up parameters for and issue commands. For example, a "rename file" command in a file manager application needs to request the new name for the file from the user. This can be done with a command dialog that shows the file's current name and has a text entry field for the file's new name. A reply bar with a "Rename" action trigger could be provided to initiate the rename action when the user has finished entering the new name.

Command Interactions can appear as menus, dialogs, or sub-groups within a larger window. If the Interaction is displayed as a dialog, the specific UI may provide a response trigger with the IC_DISMISS InteractionCommand that closes the dialog without executing any commands. In OSF/Motif, this trigger is labeled "Close" or "Cancel." You should supply your own additional command triggers yourself. You may also add custom response triggers with ATTR_GEN_TRIGGER_INTERACTION_COMMAND set to a custom InteractionCommand

Command dialogs provide applications with a place to group related commands. They also provide a convenient place to locate UI gadgetry needed to set up parameters used by commands. A command dialog usually contains one or more command triggers in the dialog reply bar. The Specific UI will usually create a trigger to close the dialog box (with IC_DISMISS) but will not supply any command triggers; you must set up any command triggers or custom response triggers yourself.

Code Display 7-11 Using a Command Dialog Box

/* The Command Dialog created below contains one command trigger (RenameTrigger)
 * which sends MSG_MY_PROCESS_RENAME to the process object. */
@object GenInteractionClass RenameBox = {
    GI_visMoniker = "Rename";
    GI_comp = @RenameSource, @RenameDest, @RenameTrigger;
    GII_type = GIT_COMMAND;
    GII_visibility = GIV_DIALOG;
}
@object GenTextClass RenameSource = {
    /* Single-line text entry object */
}
@object GenTextClass RenameDest = {
    /* Single-line text entry object */
}
@object GenTriggerClass RenameTrigger = {
    GI_visMoniker = "Rename";
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_RENAME;
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    HINT_SEEK_REPLY_BAR;
}

The Rename trigger in the above example is marked HINT_SEEK_REPLY_BAR to place it in the reply bar of the Interaction, though this is not necessary. The GA_SIGNAL_INTERACTION_COMPLETE attribute on the command trigger indicates that when the user activates this trigger, their use of the dialog is complete and the Specific UI may dismiss the dialog, if it desires. By default, non-modal command dialogs in OSF/Motif are not dismissed. You may use hints to override this behavior.

You may wish to modify this closing behavior, however. For example, if the user chooses an invalid destination name, you might not want to close that dialog; instead, the dialog should remain up and the user should be alerted that he has chosen an incorrect destination. In this case, you should remove the GA_SIGNAL_INTERACTION_COMPLETE attribute from the trigger. You should then handle the closing behavior, after validating the destination, in the MSG_MY_PROCESS_RENAME handler. To dismiss the command dialog, send MSG_GEN_GUP_INTERACTION_COMMAND with IC_DISMISS to it. (You may also use IC_INTERACTION_COMPLETE if you wish to defer to the Specific UI whether to close the dialog box.)

The Specific UI will also create a standard response trigger to dismiss the command dialog box. If some action needs to take place when the user attempts to close this dialog box, you should replace the default Close trigger by adding your own trigger to replace the IC_DISMISS InteractionCommand . As with IC_APPLY and IC_RESET, use ATTR_GEN_TRIGGER_INTERACTION_COMMAND with IC_DISMISS to replace the default trigger. You should also mark this trigger HINT_SEEK_REPLY_BAR if you wish it to appear in the Interaction's reply bar.

Code Display 7-12 Replacing the Default Close Trigger

@object GenInteractionClass RenameBox = {
    GI_visMoniker = "Rename";
    GI_comp = RenameSource, RenameDest, RenameTrigger, RenameClose;
    GII_type = GIT_COMMAND;
    GII_visibility = GIV_DIALOG;
}
@object GenTextClass RenameSource = {
    /* Single-line text entry object */
}
@object GenTextClass RenameDest = {
    /* Single-line text entry object */
}
@object GenTriggerClass RenameTrigger = {
    GI_visMoniker = "Rename";
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_RENAME;
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    HINT_SEEK_REPLY_BAR;
}
@object GenTriggerClass RenameClose = {
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_CLOSE_RENAME;
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_DISMISS };
    HINT_SEEK_REPLY_BAR;
}

Progress Interactions

GIT_PROGRESS Interactions report the progress of an operation. You will need progress dialogs most often to show the status of an ongoing operation. Progress Interactions may appear as either a dialog or as part of a larger window. (Typically progress reports should not appear within a popup, as popups will not remain on-screen.)

If a progress Interaction takes the form of a dialog box, the Specific UI may create a response trigger with the InteractionCommand IC_STOP. This trigger will halt the operation (and therefore the progress report) and may close the dialog box if the Specific UI or the application desires this. In OSF/Motif this trigger is labeled "Stop." The Specific UI will not supply a default way to close the dialog other than this IC_STOP trigger. In OSF/Motif this means that a system menu for a non-modal progress dialog will have its "Close" trigger disabled.

For example, you can use a progress dialog to report the progress of a disk format. Gadgets within the progress dialog might reflect the current state of the disk format (for example, a bar indicating the time-percentage of the operation completed). A "Stop" trigger in the reply bar allows the user to abort the disk format. Your application should intercept the InteractionCommand IC_STOP in this case so that you can perform any actions required in the halting operation.

A progress dialog may be either modal or non-modal, depending on the application and the operation in progress. The GenInteractionType GIT_PROGRESS is designed for cases where the application supports stopping an in-progress operation.

While the Specific UI will supply an IC_STOP trigger in the reply bar for halting the on-going operation, the trigger by itself does nothing. You must either replace this IC_STOP trigger with one that performs the required halting operation, or you must provide a subclass of GenInteraction to intercept MSG_GEN_GUP_INTERACTION_COMMAND with IC_STOP. Providing a replacement IC_STOP trigger is the easiest method.

Code Display 7-13 Replacing the Default IC_STOP Trigger

/* This Progress Dialog shows the progress of a data transfer. Its first child
 * shows the number of packets sent in the operation, and the second child is an
 * IC_STOP trigger to halt the transfer operation. */
@object GenInteractionClass FileTransferProgress = {
    GI_visMoniker = "File Transfer Status";
    GI_comp = FileTransferPktCount, FileTransferStop;
    GII_visibility = GIV_DIALOG;
    GII_type = GIT_PROGRESS;
}
/* The GenText object shows the title "Packets Sent" and updates the GTI_text field
 * with the current number of packets sent. */
@object GenTextClass FileTransferPktCount = {
    GI_visMoniker = "Packets sent:";
    GTXI_text = "0";
}
/* When the user activates the "Abort File Transfer" trigger, the trigger sends out
 * MSG_MY_PROCESS_ABORT_FILE_TRANSFER to the process. This action should also
 * close the dialog box. Since this trigger may initiate a destructive action,
 * HINT_TRIGGER_DESTRUCTIVE_ACTION makes sure that the focus does not lie on the
 * dialog box (and therefore the trigger cannot be activated by a default
 * activation method such as hitting RETURN). */
@object GenTriggerClass FileTransferStop = {
    GI_visMoniker = "Abort File Transfer";
    /* OSF/Motif will dismiss this dialog box if marked
     * GA_SIGNAL_INTERACTION_COMPLETE because it is GIT_PROGRESS. */
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    GTI_destination = process;
    /* The handler for MSG_MY_PROCESS_ABORT_FILE_TRANSFER must stop the
     * operation. You may also dismiss the dialog at that time. */
    GTI_actionMsg = MSG_MY_PROCESS_ABORT_FILE_TRANSFER;
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_STOP };
    HINT_SEEK_REPLY_BAR;
    HINT_TRIGGER_DESTRUCTIVE_ACTION;					/* Don't place focus here. */
}

Notification Interactions

GIT_NOTIFICATION Interactions allow your application to send notices to the user. You should use a notification Interaction when you wish to notify the user of an event without needing to prompt the user for further information.

GIT_NOTIFICATION interactions normally appear as dialogs. If so, the Specific UI will create a response trigger with the InteractionCommand IC_OK. This command will close the dialog box in most Specific UIs. In OSF/Motif, this trigger is labeled "OK." The Specific UI will not supply a way to close the dialog without responding with this trigger. In OSF/Motif, this means that the system menu in a non-modal notification dialog will have its "Close" trigger disabled.

A notification Interaction may in theory be of any visibility, but it usually takes the form of a modal dialog box. Therefore, the user should only need to acknowledge the dialog box (usually by activating the "OK" trigger).

You will usually require a notification dialog when you need to let the user know that something has occurred that may or may not require his immediate attention. For example, a notification dialog would be appropriate to notify the user that new e-mail has arrived; a notification dialog would also be appropriate to notify the user that an error has occurred.

Notifications that require immediate attention should be marked GIA_MODAL or HINT_INTERACTION_MODAL , depending on the application's dependence on their modality. Interactions that do not require immediate attention may be marked HINT_INTERACTION_NO_DISTURB so that when brought up, they do not disturb the focus or target of the application. This is especially useful for cases such as e-mail notification, where switching the focus away from another application at each new notice might become irritating to the user.

Code Display 7-14 An E-mail Notification Dialog

/* The Notification dialog is marked HINT_INTERACTION_NO_DISTURB so that it
 * will not grab the focus and target. The notification itself has only one child:
 * the text to display to the user. The Specific UI will also create an IC_OK
 * trigger that is used to acknowledge and dismiss the notification. */
@object GenInteractionClass NewMailNotice = {
    GI_visMoniker = "Mailbox";
    GI_comp = NewMailText;
    GII_type = GIT_NOTIFICATION;
    GII_visibility = GIV_DIALOG;
    HINT_INTERACTION_NO_DISTURB;
}
@object GenGlyphClass NewMailText = {
    GI_visMoniker = "New mail has arrived";
}

By default the IC_OK trigger will just dismiss the dialog. If some other action might need to be taken when the user acknowledges the notification, you should provide a custom IC_OK trigger to replace the specific UI supplied one.

Code Display 7-15 Replacing the IC_OK Trigger

/* This Notification dialog now contains two explicit children: the text to
 * display to the user and a custom trigger to acknowledge the notification. */
@object GenInteractionClass NewMailNotice = {
    GI_visMoniker = "Mailbox";
    GI_comp = NewMailText, NewMailAcknowledge;
    GII_type = GIT_NOTIFICATION;
    GII_visibility = GIV_DIALOG;
    HINT_INTERACTION_NO_DISTURB;
}
@object GenGlyphClass NewMailText = {
    GI_visMoniker = "New mail has arrived";
}
/* When the user clicks on the Acknowledge Trigger, MSG_MY_PROCESS_NEW_MAIL_ACK is
 * sent to the process class. Marking the trigger as IC_OK replaces the default
 * trigger. Note that the Specific UI is still allowed to pick the visual moniker
 * for this trigger as its GI_visMoniker field is left blank. */
@object GenTriggerClass NewMailAcknowledge = {
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_NEW_MAIL_ACK;
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_OK };
    HINT_SEEK_REPLY_BAR;
}

Affirmation Interactions

GIT_AFFIRMATION Interactions allow your application to provide the user with a choice of yes or no. GIT_AFFIRMATIONs normally appear as dialog boxes. If so, the Specific UI will create response triggers with the commands IC_YES and IC_NO. In OSF/Motif, the triggers are labeled "Yes" and "No." The Specific UI will not provide a default manner to close the dialog without responding to one of these. In OSF/Motif, this means that non-modal dialog boxes will have their "Close" triggers disabled.

For example, your application may need to prompt the user with a yes or no question (such as "Do you want to continue?"). Depending on the value returned in the InteractionCommand , the dialog box may continue or abort any impending operations. In most cases, you will want to use an affirmation interaction when you wish to force the user to make a choice.

An affirmation dialog allows the user to make a single yes or no choice, without an option to ignore the choice and close the dialog. In general, this type of dialog is normally modal or used with UserDoDialog() to block the calling thread until the user responds. When the user chooses, the caller will be unblocked and UserDoDialog() will return IC_YES or IC_NO. Alternatively, you can supply replacement IC_YES or IC_NO (or both) triggers which send out custom messages when activated.

Code Display 7-16 An Affirmation Dialog for Deleting a File

/* Rather than using UserDoDialog(), this dialog is simply marked GIA_MODAL.
 * This approach has the advantage of allowing any views that are run by the
 * process thread to be updated if they are exposed by the application. */
/* This dialog keeps the default IC_NO trigger to dismiss the dialog but
 * replaces the default IC_YES trigger. */
@object GenInteractionClass DeleteAffirmation = {
    GI_comp = DeleteAffirmationText, DeleteAffirmationYes;
    GII_type = GIT_AFFIRMATION;
    GII_visibility = GIV_DIALOG;
    GII_attrs = @default | GIA_MODAL;
}
@object GenGlyphClass DeleteAffirmationText = {
    GI_visMoniker = "Are you sure you want to delete this file?";
}
/* This trigger replaces the default IC_YES trigger. */
@object GenTriggerClass DeleteAffirmationYes = {
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_DELETE_FILE;
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = {IC_YES};
    HINT_SEEK_REPLY_BAR;
}

Multiple Response Interactions

GIT_MULTIPLE_RESPONSE Interactions allow the UI to provide the user with a number of choices, one of which must be selected. GIT_MULTIPLE_RESPONSE Interactions normally appear as dialog boxes. The Specific UI itself supplies no standard response triggers; you must add your own response triggers. (See Standard Response Triggers .) There is no default provision to just close or cancel the dialog, ignoring the choice. In OSF/Motif, this means the system menu for a non-modal dialog will have its "Close" trigger disabled.

The GIT_MULTIPLE_RESPONSE type allows you to build a custom dialog box. By default, a multiple-response dialog provides no standard response triggers and no way to close the dialog. You must therefore provide any response triggers you might need (including an IC_DISMISS InteractionCommand ). If not, the dialog will only be dismissable by the application, not by the user. In general, you should use a GIT_MULTIPLE_RESPONSE when you wish to prompt the user with several choices, one of which must be chosen.

Code Display 7-17 A Fragmentation Warning Dialog Box

@object GenInteractionClass DatabaseFragmentWarning = {
    GI_comp = DBFragmentText, DBFragmentCompact, DBFragmentContinue;
    GII_type = GIT_MULTIPLE_RESPONSE;
    GII_visibility = GIV_DIALOG;
    GII_attrs = @default | GIA_MODAL;
}
@object GenGlyphClass DBFragmentText = {
    GI_visMoniker = "Database is getting fragmented."
}
/* As none of the following triggers are standard trigger replacements, there is no
 * need to use ATTR_GEN_TRIGGER_INTERACTION_COMMAND. */
@object GenTriggerClass DBFragmentCompact = {
    GI_visMoniker = "Run Compaction utility.";
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_DB_COMPACT;
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    HINT_SEEK_REPLY_BAR;
}
@object GenTriggerClass DBFragmentContinue = {
    GI_visMoniker = "Continue without Compacting.";
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_DB_CONTINUE;
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    HINT_SEEK_REPLY_BAR;
}

Toolboxes

Toolboxes are useful to display small icons for commonly used operations. Toolboxes are normally provided by controllers. (See the Generic UI Controllers chapter.) In many cases toolboxes are non-modal GIT_ORGANIZATIONAL dialog boxes. Any GenInteraction may be marked HINT_TOOLBOX , however. (In some cases, you may wish a toolbox to be GIT_MULTIPLE_RESPONSE.) GeoDraw provides a toolbox to alter the properties of graphics objects. This toolbox appears at appropriate times in the GeoDraw application and will remain on-screen until the user explicitly closes it by selecting "Close" in its system menu.

Toolbox dialogs should never steal the focus or target of the application; this prevents the user from constantly having to reselect current objects. The hint HINT_TOOLBOX provides this functionality. This hint prevents a toolbox Interaction from grabbing the focus whenever an object within the toolbox is activated.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Visibilities | Next: 4 Supplemental Usage


Supplemental Usage

Menus and Dialog Boxes: 4 Supplemental Usage

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 GenInteraction Usage | Next: 5 Interaction Commands

The previous section described most of what you need to get an Interaction into your application. Much of the behavior of an Interaction is taken care of by the Specific UI. If you wish to modify this behavior, the following section details several ways in which you can alter these defaults.

Initiating Interactions

Dismissing Interactions

Modality for Dialogs

Managing Input

Thread Blocking Routines

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 GenInteraction Usage | Next: 5 Interaction Commands


Initiating Interactions

Menus and Dialog Boxes: 4.1 Supplemental Usage: Initiating Interactions

| Up: GEOS SDK TechDocs | Up | Prev: 4 Supplemental Usage | Next: 4.2 Dismissing Interactions
MSG_GEN_INTERACTION_INITIATE, MSG_GEN_INTERACTION_INITIATE_NO_DISTURB, MSG_GEN_INTERACTION_DISABLE_DISCARDING

For the most part, the Specific UI decides in what way Interactions should be initiated. For example, a popup built as a menu will create a menu title which, when activated, brings down the menu. A dialog box will create an activation trigger within the UI that, when activated, brings the dialog box on-screen. (Notice that it is meaningless to "initiate" a sub-group Interaction as these objects are not independently-displayable.)

You may wish to modify this behavior, either to prevent the user from directly initiating an Interaction, or to add additional ways to bring these Interactions on-screen.

Although in most cases the user is responsible for activating menus (by activating the menu title), an application may manually open a menu with MSG_GEN_INTERACTION_INITIATE .

A GIV_DIALOG Interaction may be initiated in several ways:

  • In the default case, the specific UI will create an activation trigger whose sole function is to bring up the dialog box. The trigger will appear as a child of the dialog's parent, in the spot within the UI that the dialog would normally reside were it not independently displayable.
  • A dialog box may be set GIA_NOT_USER_INITIATABLE to prevent the building of the default trigger. Any Interaction set non-user initiatable should be sent a MSG_GEN_INTERACTION_INITIATE or a MSG_GEN_INTERACTION_INITIATE_NO_DISTURB to bring the dialog on-screen. You may also put Interactions on the GAGCNLT_WINDOWS GCN list to automatically bring them on-screen when an application is launched. Any Interaction must be fully usable and enabled to be brought up in such a manner. Note: If a dialog box is also marked GIA_INITIATED_VIA_USER_DO_DIALOG, it should be initiated with UserDoDialog() , not MSG_GEN_INTERACTION_INITIATE. These methods are exclusive.
  • A dialog box may be brought up with UserDoDialog() if it is set GIA_INITIATED_VIA_USER_DO_DIALOG. This routine is useful when you wish to block the calling thread until the user responds to the dialog box. A dialog box initiated via UserDoDialog() should also be marked GIA_NOT_USER_INITIATABLE. Note that this dialog box may only be brought up with UserDoDialog() ; it cannot be brought up with MSG_GEN_INTERACTION_INITIATE.

Within most specific UIs a trigger will appear whose sole function is to initiate the dialog. Activating the trigger will bring the dialog box on-screen. You can prevent the display of this default trigger by setting the Interaction GIA_NOT_USER_INITIATABLE in its GII_attrs instance field. (See GenInteraction Attributes ).

Your GenInteraction subclass may intercept MSG_GEN_INTERACTION_INITIATE to determine when a dialog is being brought on-screen and then perform any needed actions at that time. This is useful if you need to update the UI objects within the dialog before building its visual implementation. You should always call the superclass after setting any of this initial data to avoid unnecessary visual updates of the dialog box. MSG_GEN_INTERACTION_INITIATE_NO_DISTURB may also be subclassed, but there is little need to intercept it.

GenInteraction dialog boxes may be marked HINT_INTERACTION_DISCARD_WHEN_CLOSED .This hint informs the system to discard the dialog box's object block when it is no longer being used, but there is a timing problem if the dialog box needs to be initialized before it is brough on-screen. (The system will think it is off-screen and discard it before initialization is complete.)

In that case, send the dialog box MSG_GEN_INTERACTION_DISABLE_DISCARDING to ensure that the dialog box doesn't get discarded between the time that the initialization is performed and the time when the dialog box appears on-screen.

MSG_GEN_INTERACTION_INITIATE

void	MSG_GEN_INTERACTION_INITIATE();

This message brings an Interaction on-screen, giving it focus and target if possible. The Interaction must be both within the generic tree and set GS_USABLE before receiving this message. The Interaction must also be fully usable and enabled before it can be initiated. Interactions marked GIA_INITIATED_VIA_USER_DO_DIALOG should not be sent this message. To initiate those dialogs, you must use UserDoDialog() . (See Thread Blocking Routines .)

You may intercept this message to determine when a dialog is coming on-screen. This is useful if you need to set up UI gadgetry in the dialog to show some initial status. The gadgetry should be set up before calling the superclass to avoid unnecessary visual updating of the object.

Source: Unrestricted.

Destination: Any GS_USABLE GenInteraction, though usually only meaningful for GIV_DIALOG interactions.

Parameters: None.

Return: Nothing.

Interception: May be intercepted to find out when an Interaction is appearing on-screen. This is useful for cases in which the UI gadgetry of a dialog box needs to be set up before being visually built. The superclass should be called after setting up the UI to avoid unnecessary visual changes once the interaction is on-screen.

MSG_GEN_INTERACTION_INITIATE_NO_DISTURB

void	MSG_GEN_INTERACTION_INITIATE_NO_DISTURB();

This message brings an Interaction on-screen but does not give the dialog the focus or target. This message may place the dialog behind other windows. The Interaction must be both within the generic tree and set GS_USABLE before receiving this message. The Interaction must also be fully usable and enabled before it can be initiated. Interactions marked GIA_INITIATED_VIA_USER_DO_DIALOG should not be sent this message. To initiate those dialogs, you must use UserDoDialog() . (See Thread Blocking Routines .)

Source: Unrestricted.

Destination: Any GS_USABLE GenInteraction, though usually only meaningful for GIV_DIALOG Interactions. Not allowed for GIA_MODAL or GIA_INITIATED_VIA_USER_DO_DIALOG Interactions.

Parameters: None.

Return: Nothing.

Interception: May be intercepted to find out when an Interaction is appearing on-screen. This is useful for cases in which the UI gadgetry of a dialog box needs to be set up before being visually built. The superclass should be called after setting up the UI to avoid unnecessary visual changes once the interaction is on-screen.

MSG_GEN_INTERACTION_DISABLE_DISCARDING

void	MSG_GEN_INTERACTION_DISABLE_DISCARDING();

This message informs a Interaction with HINT_INTERACTION_DISCARD_WHEN_CLOSED that it should not be discarded until it has been brought on-screen. This message is used to ensure that a dialog box that needs initialization is not discarded between the time that initialization commences and the time that the dialog finally appears on-screen.

Source: Unrestricted.

Destination: Any GIV_DIALOG GenInteraction that is marked HINT_INTERACTION_DISCARD_WHEN_CLOSED and needs initialization.

Parameters: None.

Return: Nothing.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Supplemental Usage | Next: 4.2 Dismissing Interactions


Dismissing Interactions

Menus and Dialog Boxes: 4.2 Supplemental Usage: Dismissing Interactions

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Initiating Interactions | Next: 4.3 Modality for Dialogs

You may dismiss dialogs and popups under application control by sending MSG_GEN_GUP_INTERACTION_COMMAND with the InteractionCommand IC_DISMISS to them. This will force dialogs to close even if the user has such a dialog pinned (in Specific UIs that support pinning). Dialogs may also be dismissed when the user activates reply bar response triggers with the GA_SIGNAL_INTERACTION_COMPLETE attribute set. (See the GenClass chapter.)

You may intercept MSG_GEN_GUP_INTERACTION_COMMAND to determine when a dialog is about to be dismissed. The handler for this message should check the incoming InteractionCommand to make sure it is IC_DISMISS before doing whatever work is needed. You should make sure to pass any non-IC_DISMISS InteractionCommand immediately to the superclass. The handler may or may not need to call the superclass for IC_DISMISS, depending on what behavior the handler is adding.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Initiating Interactions | Next: 4.3 Modality for Dialogs


Modality for Dialogs

Menus and Dialog Boxes: 4.3 Supplemental Usage: Modality for Dialogs

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Dismissing Interactions | Next: 4.4 Managing Input
MSG_GEN_INTERACTION_TEST_INPUT_RESTRICTABILITY

By default, GIV_DIALOG GenInteractions appear as non-modal windows. You may specify a dialog Interaction to appear as modal by setting either the GIA_MODAL attribute in the GII_attrs instance data field or by adding HINT_INTERACTION_MODAL in the object's vardata. When a modal dialog is displayed, input to all other parts of the application will be ignored until the dialog is dismissed. In OSF/Motif, modal dialogs will appear with thick borders.

You should use GIA_MODAL dialogs when the application depends on an Interaction being modal to operate correctly. For example, if a dialog shows information about the current selection but the application cannot update that information if the user were to change the current selection, you should use a GIA_MODAL dialog.

You should use HINT_INTERACTION_MODAL dialogs for cases where the modality of a dialog simplifies the interface between the application and the user but is not needed for the application's correct operation. The Interaction's modality in this case will make the UI clearer and simpler for the user to understand. For example, a dialog for setting obscure options that is brought up from a command dialog might be marked HINT_INTERACTION_MODAL so that the user is forced to set the options before returning to the command dialog. This will prevent the user from using this dialog box in a context where the options are not self-evident. As with all hints, the Specific UI may decide to ignore the hint and implement a HINT_INTERACTION_MODAL dialog with a non-modal dialog.

You may also initiate a modal dialog with UserDoDialog() . Such a dialog must have both the GIA_INITIATED_VIA_USER_DO_DIALOG and GIA_MODAL attributes set. Using this routine not only blocks input to other parts of the application but blocks the calling thread's execution until the dialog is dismissed. UserDoDialog() is useful for displaying notifications where the user's response is required before the application's thread may continue. For more information, see Thread Blocking Routines .

Dialogs displayed with UserStandardDialog() and UserStandardDialogOptr() are also modal. Both will block the calling thread until the dialog is dismissed. (See Thread Blocking Routines .)

Some modal dialogs will have subcomponents such as pop-up lists that must be interactable while the dialog is modal. The GenApplication object running the modal dialog will query the dialog to test both its input restrictions and its interactability in these cases; it uses two messages, described below.

MSG_GEN_INTERACTION_TEST_INPUT_RESTRICTABILITY

Boolean	MSG_GEN_INTERACTION_TEST_INPUT_RESTRICTABILITY(
        optr	obj);

This message is called on a modal window by a GenApplication object to find out whether or not the various input restricting mechanisms (input hold-up, input ignore, busy states, etc.) should be overridden or not.

Source: Part of the input flow/modality mechanism--sent by the GenApplication object to the modal window.

Destination: The modal GenInteraction object.

Parameters: obj The optr of the windowed object that may or may not be restricted.

Return: Will return true if input restrictions should be overridden, false otherwise. A true return value indicates that mouse and keyboard input will flow to the object regardless of hold-up, ignore, or busy states.

Interception: May be intercepted to allow the passed windowed object to override its input restrictions in certain cases.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Dismissing Interactions | Next: 4.4 Managing Input


Managing Input

Menus and Dialog Boxes: 4.4 Supplemental Usage: Managing Input

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Modality for Dialogs | Next: 4.5 Thread Blocking Routines
ATTR_GEN_INTERACTION_OVERRIDE_INPUT_RESTRICTIONS, ATTR_GEN_INTERACTION_ABIDE_BY_INPUT_RESTRICTIONS, HINT_INTERACTION_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD

Typically, specific UIs will determine how input (primarily keyboard input) affects GenInteractions and their components. For example, many UIs will have the Tab key navigate from the active component of a dialog box to the next available component.

HINT_INTERACTION_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD allows you to trade the normal "default action" (the action typically causing the dialog to send its "apply" message) for navigation. For example, if hitting the return key normally invoked an "Apply" trigger, this hint would instead cause it to navigate to the next field in the dialog box.

Two other attributes allow a GenInteraction to either disobey or abide by certain default input restrictions (modality, input hold-up, input ignore, and busy states). ATTR_GEN_INTERACTION_OVERRIDE_INPUT_RESTRICTIONS allows the GenInteraction to override the input restrictions; ATTR_GEN_INTERACTION_ABIDE_BY_INPUT_RESTRICTIONS allows the GenInteraction to override default behavior ignoring those restrictions.

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Modality for Dialogs | Next: 4.5 Thread Blocking Routines


Thread Blocking Routines

Menus and Dialog Boxes: 4.5 Supplemental Usage: Thread Blocking Routines

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Managing Input | Next: 5 Interaction Commands

Occasionally, you may need a response from the user before continuing with a thread of execution. In these cases, you should use a dialog box to prompt the user for the needed response. However, you also need a means to block the thread of execution until the user responds to this dialog box. The routines UserDoDialog() , UserStandardDialog() , and UserStandardDialogOptr() provide this functionality.

UserDoDialog()

UserDoDialog(), UserCreateDialog(), UserDestroyDialog()

You may bring dialog boxes on-screen with several routines. The most common and easiest to use of these routines is UserDoDialog() . UserDoDialog() only operates on GIV_DIALOG Interactions set both GIA_MODAL and GIA_INITIATED_WITH_USER_DO_DIALOG.

This routine, when passed the optr of a dialog box, will bring the dialog Interaction on-screen. In addition to bringing up a dialog, however, UserDoDialog() will also block the calling thread until a response trigger in the dialog is activated by the user. When this happens, UserDoDialog() returns a value representing the response trigger selected. This return value is usually an InteractionCommand .

The dialog may contain any UI gadgetry but must have response triggers with valid InteractionCommand s to terminate the dialog box. These triggers should have a null output and message and have ATTR_GEN_TRIGGER_INTERACTION_COMMAND vardata set to the proper InteractionCommand s. (See the GenTrigger chapter.) This InteractionCommand will be the response value returned by UserDoDialog() when that trigger is activated. This can be one of the predefined InteractionCommand s or an application-defined one with IC_CUSTOM_START.

The response triggers should also have the attribute GA_SIGNAL_INTERACTION_COMPLETE set to ensure that the dialog will be dismissed when they are activated. (This may be omitted to gain manual control over dismissal over the dialog, but you still must provide a way to dismiss the dialog.) Any response triggers in the dialog should have the hint HINT_SEEK_REPLY_BAR to place the triggers within the dialog's reply bar, but this is not necessary.

Because the calling thread is blocked by UserDoDialog() , views run by that thread will not be updated when exposed.

Code Display 7-18 Using UserDoDialog()

/* This dialog box asks for confirmation before beginning a delete file operation.
 * Therefore, it is advisable to block threads before beginning this operation. The
 * dialog using UserDoDialog() should be marked both GIA_MODAL and
 * GIA_INITIATED_VIA_USER_DO_DIALOG. */
@object GenInteractionClass ConfirmDeleteBox = {
    GI_comp = @ConfirmDeleteText;
    GII_type = GIT_AFFIRMATION;
    GII_visibility = GIV_DIALOG;
    GII_attrs = @default | GIA_INITIATED_VIA_USER_DO_DIALOG | GIA_MODAL;
}
@object GenTextClass ConfirmDeleteText = {
    GTI_text = "Are you sure you want to delete this file?";
}
/* This dialog box is displayed through a normal routine call with
 * the optr of the dialog box as its one argument. */
/* Check for positive response. */
if (UserDoDialog(@ConfirmDeleteBox) == IC_YES) {
    /* delete file. */
}

UserDoDialog() may also return IC_NULL to indicate that the modal dialog has been dismissed by the system, so it is always a good idea to check for and act on positive responses even if only one response is possible (such as in a GIT_NOTIFICATION dialog box).

UserCreateDialog() duplicates a dialog box to initiate later with UserDoDialog() . Typically, the dialog box duplicated is within a template object block; the dialog box must be both not GS_USABLE and not attached to the generic tree when created. The dialog box must also be marked GIA_INITIATED_VIA_USER_DO_DIALOG.

The template block that contains the dialog box and its children must be sharable and read-only. UserCreateDialog() duplicates a template dialog box, attaches the dialog box to the GenApplication object and sets it fully GS_USABLE; it may then be called with UserDoDialog() . When you no longer have a need for the dialog box, send it UserDestroyDialog() .

These routines are useful for conserving memory space; they only take up space when actually being used. In some cases, you may need to use these routines. For example, within libraries, dialog boxes must be duplicated before being used because multiple applications may require their own copy of the dialog box template.

Code Display 7-19 Using UserCreateDialog(), UserDestroyDialog()

/*
 * The template dialog box must not be GS_USABLE. The object must also be marked
 * GIA_INITIATED_VIA_USER_DO_DIALOG. The block must be sharable, read-only, and
 * the top GenInteraction must not be linked into the generic tree.
 */
@object GenInteractionClass MyDialogTemplate = {
    GI_visMoniker = "Template";
    GI_states = @default & ~GS_USABLE;
    GII_visibility = GIV_DIALOG;
    GII_attrs = @default | GIA_INITIATED_VIA_USER_DO_DIALOG |
    GIA_NOT_USER_INITIATABLE | GIA_MODAL;
    GII_type = GIT_NOTIFICATION;
    GI_comp = @NotificationGlyph;
}
@method SomeProcessClass, MSG_BRING_UP_DUPLICATED_DIALOG
{
    optr		newDialog;
    newDialog = UserCreateDialog(@MyDialogTemplate);
    if (UserDoDialog(@newDialog) == IC_OK) {
    /*** code ***/
    }
    UserDestroyDialog(@newDialog);
}

UserStandardDialog()

UserStandardDialog(), UserStandardDialogOptr(), CustomDialogBoxFlags

UserStandardDialog() displays standardized dialog boxes. The dialog is standardized in that it has a text area, an icon glyph representing the type of situation that caused the dialog to be displayed, and one or more response triggers. Like UserDoDialog() , UserStandardDialog() blocks the calling thread until the user activates one of the response triggers. Unlike UserDoDialog() , however, UserStandardDialog() does not need an application-defined dialog box. UserStandardDialog() builds a dialog box at run-time following the specifications passed.

UserStandardDialog() passes a number of parameters:

  • CustomDialogBoxFlags
     This bitfield stores the CustomDialogType (CDBF_DIALOG_TYPE) and the GenInteractionType (CDBF_INTERACTION_TYPE) to display the Interaction with, along with several miscellaneous flags
CDBF_SYSTEM_MODAL indicates that the dialog box brought up by UserDoDialog() should not only be application modal but also system modal.
CDBF_DESTRUCTIVE_ACTION indicates that an affirmative response by the user to the dialog box denotes a destructive action, and thus should not be made the default.
CDBF_DIALOG_TYPE indicates the type of situation creating the dialog box. This type determines the icon glyph that represents what caused the dialog to be displayed. The available types ( CustomDialogType ) are:
CDT_QUESTION: Ask the user a question;
CDT_WARNING: Warn the user of a potential problem;
CDT_NOTIFICATION: Notify the user of some event;
CDT_ERROR: Report an error to the user.
The Specific UI will determine the appropriate icon glyph to use for each dialog type. For example, in OSF/Motif, a CDT_QUESTION icon glyph is a graphic question mark.
The system will also issue a beep when a CDT_ERROR dialog is displayed.
CDBF_INTERACTION_TYPE indicates the type of GenInteraction being initiated. This type specifies what response triggers should be built and is a sub-set of the supplied GenInteractionType enums. The available types are:
GIT_NOTIFICATION
The specific UI will supply a standard response trigger that has the IC_OK response value.
GIT_AFFIRMATION
The specific UI will supply standard response triggers that have the IC_YES and IC_NO response values.
GIT_MULTIPLE_RESPONSE
The application must provide its own trigger monikers and response values. If this value is set, you will have to pass the array of custom triggers.
  • Dialog string (and arg1 and arg2 )
    This is the string to display in the dialog box text area. The string can have up to two parameters. These parameters are other strings that are passed to UserStandardDialog() in arg1 and arg2 . They will replace all occurrences of \001 and \002 in the dialog string. This is useful for including filenames or other variable text in the dialog string.
  • Response trigger monikers and values
    If GIT_MULTIPLE_RESPONSE is used, this indicates how many triggers and what labels and response values the response triggers should have.
  • Help context
    The help context to use for this dialog box layer.

Code Display 7-20 Using UserStandardDialog()

/*
 * This simple example uses no help context, custom triggers, or string arguments.
 */
if ((UserStandardDialog(			(char *)0,
            (char *)0,
            (char *)0,
            (char *)0,
            "Do you wish to continue?",
            ((CDT_QUESTION << CDBF_DIALOG_TYPE_OFFSET) |
            (GIT_AFFIRMATION << CDBF_INTERACTION_TYPE_OFFSET))
            ) == IC_YES)) {
    /* code to perform on a positive response. */
    }
else {
    /* code to perform on a negative response. */
    }

You may also use UserStandardDialogOptr() for cases in which the strings are referenced through optrs rather than pointers.

Code Display 7-21 A ConfirmDeleteBox with explicit monikers

/* For this case, since we want to provide explicit monikers, we must use the
 * GIT_MULTIPLE_RESPONSE interaction type. Using this allows us to pass in the
 * monikers and response values for the response triggers for the dialog. This is
 * done by passing a pointer to a table consisting of the number of triggers in the
 * dialog and a StandardDialogResponseTriggerEntry for each trigger. Each entry
 * contains an optr of the moniker to use and the response value for the trigger.
 * The moniker may be simple text or a graphics string. The response value may be
 * one of the predefined InteractionCommands or an application-defined value based
 * on IC_CUSTOM_START. */
@visMoniker ConfirmYesMoniker = "Delete this file";
@visMoniker ConfirmNoMoniker = "Skip this file";
/* Create a table to hold the trigger data. */
static const StandardDialog2ResponseTriggerTable confirmResponseTable [] = {
    2,				/* SD2RTT_numTriggers */
    /* WRT_trigger1 */
    {ConfirmYesMoniker,				/* SDRTE_moniker */
    IC_YES}, 			/* SDRTE_responseValue */
    /* WRT_trigger2 */
    {ConfirmNoMoniker,				/* SDRTE_moniker */
    IC_NO}			/* SDRTE_responseValue */
};
/* Display the dialog with UserStandardDialog(). */
if (UserStandardDialog(			(char *)0,
            (char *)&confirmResponseTable,
            (char *)0,
            (char *)0,
            "Are you sure you want to delete this file?",
            ((CDT_QUESTION << CDBF_DIALOG_TYPE_OFFSET) |
            /* interaction type - application supplied trigger */
            (GIT_MULTIPLE_RESPONSE << CBDF_INTERACTION_TYPE_OFFSET)))
        == IC_YES) {
    /* delete file */
}

Code Display 7-22 A IC_CUSTOM_START Interaction

#define SAVE_CHANGES				IC_CUSTOM_START+0
#define ABORT_CHANGES				IC_CUSTOM_START+1
#define CANCEL_CLOSE				IC_CUSTOM_START+2
@visMoniker CloseSaveMoniker = "Save Changes";
@visMoniker CloseAbortMoniker = "Abort Changes":
@visMoniker CloseCancelMoniker = "Cancel Close";
static const StandardDialog3ResponseTriggerTable closeResponseTable [] = {
     3, 					/* SD3RTT_numTriggers */
    /* WRT_trigger1 */
    {CloseSaveMoniker,					/* SDRTE_moniker */
    IC_YES},					/* SDRTE_responseValue */
    /* WRT_trigger2 */
    {CloseAbortMoniker,					/* SDRTE_moniker */
    IC_YES},					/* SDRTE_responseValue */
    /* WRT_trigger3 */
    {CloseCancelMoniker,					/* SDRTE_moniker */
    IC_NO}					/* SDRTE_responseValue */
};
closeWithChangesResponse =
    (UserStandardDialog(
        (char *)0,
        (char *)&closeResponseTable,
        (char *)0,
        (char *)0,
        ((CDT_QUESTION << CDBF_DIALOG_TYPE_OFFSET) |
        /* interaction type - application supplied triggers */
        (GIT_MULTIPLE_RESPONSE << CDBF_INTERACTION_TYPE_OFFSET)),
        (char *)0);
switch (closeWithChangesResponse) {
    case SAVE_CHANGES:
    /* save changes */
    case ABORT_CHANGES:
    /* abort changes */
    case IC_CANCEL_CLOSE:
    /* cancel close */
    case IC_NULL:
    /* IC_NULL is always a potential response */
}
| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Managing Input | Next: 5 Interaction Commands


Interaction Commands

Menus and Dialog Boxes: 5 Interaction Commands

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Supplemental Usage | Next: 5.1 InteractionCommand Types

Much of the flexibility of GenInteraction lies in its ability to recognize and process a variety of special InteractionCommand s. These commands are data types that the Specific UI and the Interaction can use to decide what actions to take. Depending on the InteractionCommand , the UI may perform several pre-defined actions. Much of this functionality is automatic through the inclusion of standard response triggers within a dialog box.

Some GenInteractionType s provide one or more triggers for your use when the Interaction is built as a dialog box. These default triggers are automatically provided without your needing to create and attach triggers to the Interaction itself. Triggers provided in this manner are known as standard response triggers. Standard response triggers usually appear within an area of the dialog box known as a reply bar.

Each standard response trigger has a specific InteractionCommand associated with it. These triggers perform their actions by sending out their InteractionCommand with the message MSG_GEN_GUP_INTERACTION_COMMAND to themselves when activated. The message will travel up the tree to the first GIV_DIALOG Interaction, where it will be handled. See InteractionCommand Types for the pre-defined InteractionCommand s.

InteractionCommand Types

Dialog Control

Standard Response Triggers

Replacing Default Triggers

Triggers Completing Interactions

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Supplemental Usage | Next: 5.1 InteractionCommand Types


InteractionCommand Types

Menus and Dialog Boxes: 5.1 Interaction Commands: InteractionCommand Types

| Up: GEOS SDK TechDocs | Up | Prev: 5 Interaction Commands | Next: 5.2 Dialog Control

Many of these InteractionCommand s are sent out by standard response triggers provided with the Specific UI. Different types of Interactions may provide different standard response triggers with InteractionCommand s. For example, the "OK" trigger for a GIT_NOTIFICATION dialog box in OSF/Motif sends out the InteractionCommand IC_OK.

The InteractionCommand represents the actions that can be performed on an Interaction. The predefined commands are as follows:

  • IC_NULL
    This InteractionCommand is a special case for alerting a UserDoDialog() or UserStandardDialog() dialog box that it is being dismissed because of system shutdown. Because of thread blocking, you should always check for this case to occur in any UserDoDialog() routines. You should never set any custom triggers to this InteractionCommand .
  • IC_DISMISS
    This command instructs the UI to dismiss the Interaction. An Interaction receiving this command will come down whether or not the user has selected the Interaction to remain up (such as by pinning the dialog box).
  • IC_APPLY
    This command instructs the UI to apply properties selected within a GIT_PROPERTIES Interaction. This InteractionCommand causes MSG_GEN_APPLY to be sent to the Interaction's children.
  • IC_RESET
    This command instructs the UI to reset properties within a GIT_PROPERTIES Interaction. This InteractionCommand causes MSG_GEN_RESET to be sent to the Interaction's children.
  • IC_OK
    This command instructs the UI to signal an acknowledgment to a GIT_NOTIFICATION Interaction.
  • IC_YES
    This command instructs the UI to signal a positive response to a GIT_AFFIRMATION Interaction.
  • IC_NO
    This command instructs the UI to signal a negative response to a GIT_AFFIRMATION Interaction.
  • IC_STOP
    This command instructs the UI to halt a GIT_PROGRESS Interaction.
  • IC_EXIT
    This command instructs the UI to exit the application (not just the window). This InteractionCommand is only supported for GenTriggers under GIV_POPUP GenInteractions. This value should only be used with ATTR_GEN_TRIGGER_INTERACTION_COMMAND.
  • IC_HELP
    This command instructs the UI to bring up the proper help context for this dialog. This value should only be used with ATTR_GEN_TRIGGER_INTERACTION_COMMAND.
  • IC_INTERACTION_COMPLETE
    This command notifies the UI that an interaction has been completed. This InteractionCommand is sent out by any trigger with the attribute GA_SIGNAL_INTERACTION_COMPLETE before the trigger's main action has been sent out. Note that this InteractionCommand is always sent out in addition to another IC. You should never set any custom triggers to this InteractionCommand .

IC_DISMISS, IC_APPLY, IC_RESET, IC_OK, IC_YES, IC_NO, and IC_STOP correspond to standard response triggers provided with the Specific UI for dialogs of various GenInteractionType . (See GenInteraction Types .)

You may replace any of these standard response triggers by adding your own triggers with an InteractionCommand . You should do this by setting the trigger's ATTR_GEN_TRIGGER_INTERACTION_COMMAND vardata to the proper InteractionCommand to replace. (See Replacing Default Triggers .)

Adding Custom Response Triggers

In some cases, you may want to add your own response triggers. In many of these cases, a trigger tied to one of the predefined InteractionCommand s will be sufficient for your needs. If a dialog box does not contain one of the pre-defined response triggers by default, you can add any of these yourself.

You are not limited to one of these predefined types, however. UserDoDialog() and GIT_MULTIPLE_RESPONSE UserStandardDialog () allow you to define your own InteractionCommand types. Applications can define their own InteractionCommand by starting an enumeration at the special value IC_CUSTOM_START. This is to avoid conflicting with other predefined values, which still have their usual meaning to the dialog.

Code Display 7-23 Using IC_CUSTOM_START

/* You may define values directly. */
#define IC_SAVE_FILE IC_CUSTOM_START+1;
#define IC_KILL_FILE IC_CUSTOM_START+2;
/* You may also use typedef to enumerate those values. */
typedef enum {
    IC_SAVE_FILE = IC_CUSTOM_START+1,
    IC_KILL_FILE = IC_CUSTOM_START+2
} MyInteractionCommand;
/* Any triggers set up with these InteractionCommands would appear like so: */
@object GenTriggerClass MyTrigger = {
    GI_visMoniker = "Save File";
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_SAVE_FILE };
    HINT_SEEK_REPLY_BAR;
}
/* An Interaction invoked with UserDoDialog() (with MyTrigger as one of its
 * children) will return IC_SAVE_FILE when that trigger is activated. Your
 * application can then perform any required actions. */
| Up: GEOS SDK TechDocs | Up | Prev: 5 Interaction Commands | Next: 5.2 Dialog Control


Dialog Control

Menus and Dialog Boxes: 5.2 Interaction Commands: Dialog Control

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 InteractionCommand Types | Next: 5.3 Standard Response Triggers
MSG_GEN_GUP_INTERACTION_COMMAND, MSG_GEN_INTERACTION_ACTIVATE_COMMAND

There are two ways of delivering an InteractionCommand to an Interaction. The first is through the GenClass message MSG_GEN_GUP_INTERACTION_COMMAND . In most cases, this is the message you will use to deliver commands to your dialog boxes. For example, you may send MSG_GEN_GUP_INTERACTION_COMMAND with IC_DISMISS to dismiss a dialog box. The second involves sending MSG_GEN_INTERACTION_ACTIVATE_COMMAND with one of the specified InteractionCommand types.

The system sends this message whenever the user activates a response trigger with null action fields. The system will pass the command in ATTR_GEN_TRIGGER_INTERACTION_COMMAND vardata with the message up the generic tree until handled by the first GIV_DIALOG Interaction. Handling may include unblocking and returning a UserDoDialog() response value, applying or resetting properties, and dismissing, depending on the InteractionCommand . Note that if an action is specified in the trigger's instance data, MSG_GEN_GUP_INTERACTION_COMMAND will not be sent--the specified action will instead.

In OSF/Motif, dialogs perform the following default activity with their various InteractionCommand s:

IC_DISMISS
This command dismisses the dialog, resets gadgets if GIT_PROPERTIES and modal (i.e. "Cancel" trigger).
IC_APPLY
This command applies gadgets (whether or not GIT_PROPERTIES), then unblocks and returns a UserDoDialog() value if needed.
IC_RESET
This command resets gadgets (whether or not GIT_PROPERTIES), then unblocks and returns a UserDoDialog() value if needed.
IC_INTERACTION_COMPLETE
This command dismisses, depending on dialog context, the dialog box and unblocks and returns a UserDoDialog() value if needed.

All other InteractionCommands will just unblock and return a UserDoDialog() value if needed.

GIV_DIALOG GenInteractions are the most likely recipients of any InteractionCommand s. Popup Interactions only support IC_DISMISS and IC_INTERACTION_COMPLETE and will pass other InteractionCommand s up the generic tree. Any other Interactions (e.g. sub-groups) will always pass MSG_GEN_GUP_INTERACTION_COMMAND up the generic tree.

MSG_GEN_INTERACTION_ACTIVATE_COMMAND is a higher-level function. When sent to a dialog GenInteraction with a relevant InteractionCommand , it will find the standard trigger for that InteractionCommand and activate it. This can be a Specific UI-supplied trigger or a replacement trigger supplied by the application. If no such trigger exists, the message sends MSG_GEN_GUP_INTERACTION_COMMAND to perform the default handling. This message is only supported by dialog Interactions.

MSG_GEN_INTERACTION_ACTIVATE_COMMAND

void	MSG_GEN_INTERACTION_ACTIVATE_COMMAND(
        word	command);

This message activates a GenTrigger having the passed command in its ATTR_GEN_TRIGGER_INTERACTION_COMMAND; if no such trigger exists, this message calls MSG_GEN_GUP_INTERACTION_COMMAND to the Interaction itself with the supplied command value. Note that InteractionCommand type IC_INTERACTION_COMPLETE should not be used as this is a notification rather than a command.

Source: Unrestricted.

Destination: Any non GIT_ORGANIZATIONAL GenInteraction object.

Parameters: command InteractionCommand to activate (except IC_INTERACTION_COMPLETE and IC_NULL).

Return: Nothing.

Interception: May be intercepted to change or supplement default functionality of standard or custom interaction commands. Standard response triggers will use this message, so intercepting this message will allow changing the default behavior of these triggers without having to replace them with your own triggers.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 InteractionCommand Types | Next: 5.3 Standard Response Triggers


Standard Response Triggers

Menus and Dialog Boxes: 5.3 Interaction Commands: Standard Response Triggers

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Dialog Control | Next: 5.4 Replacing Default Triggers

The Specific UI provides standard response triggers for certain GenInteractionType , when built as dialog boxes. These standard response triggers contain an appropriate InteractionCommand and usually a visual moniker chosen by the Specific UI.

The standard response triggers (and their visual monikers under OSF/Motif) for each GII _type are shown in the figure below.

The Specific UI automatically places these standard response triggers in a reply bar within the dialog. In most cases, you will not have to deal directly with any of these standard response triggers; the Specific UI will create and manage them. In other cases, you must either supply additional response triggers or replace the existing default response triggers. Additional triggers must be supplied for GIT_COMMAND and GIT_MULTIPLE_RESPONSE Interactions. You may replace existing triggers to customize their behavior or even just to change their visual monikers.

You should add HINT_SEEK_REPLY_BAR to any additional triggers you supply so that they will be placed in the reply bar area of the Interaction (though this is not necessary). If you add more than one trigger, their order within the Interaction's children will be the order they appear in the reply bar. However, supplemental reply bar triggers will appear to the right of any IC_OK, IC_YES, IC_STOP, or IC_APPLY and to the left of any IC_DISMISS, IC_RESET, or IC_NO trigger.

Certain triggers may act as default triggers in the reply bar. Default triggers do not need to be explicitly activated (i.e. clicked on) to send out their action. Default triggers may also be activated if the user performs a default activation (normally hitting "Return" or double-clicking on an object within the dialog). Normally this default activation trigger will be the first trigger in the reply bar. Default triggers may be highlighted in some manner by the Specific UI. You can override this behavior by marking any reply trigger of your choosing with the hint HINT_DEFAULT_DEFAULT_ACTION .

The Specific UI gives HINT_DEFAULT_DEFAULT_ACTION to the IC_APPLY, IC_OK and IC_YES triggers in any reply bars it creates. (These are normally the first triggers anyway.) For example, the "OK" trigger in a GIT_NOTIFICATION dialog may be activated by either clicking on the trigger or hitting RETURN.

Whenever a dialog is initially brought on-screen, the trigger marked with HINT_DEFAULT_DEFAULT_ACTION will act as the default activation trigger. However, activating any other trigger will make that trigger the default activation trigger the next time the dialog is brought on-screen. You can avoid this by adding HINT_PREVENT_DEFAULT_OVERRIDES on the GenInteraction itself. This will allow the HINT_DEFAULT_DEFAULT_ACTION trigger to always remain the default trigger.

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Dialog Control | Next: 5.4 Replacing Default Triggers


Replacing Default Triggers

Menus and Dialog Boxes: 5.4 Interaction Commands: Replacing Default Triggers

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Standard Response Triggers | Next: 5.5 Triggers Completing Interactions

You may replace a trigger supplied by the Specific UI by adding your own custom GenTrigger (as a child of the GenInteraction) with its ATTR_GEN_TRIGGER_INTERACTION_COMMAND set to the proper InteractionCommand of the trigger to replace. You should also mark such triggers with HINT_SEEK_REPLY_BAR to place them within the reply bar, though this is not required. If there is no such hint, the trigger will appear within the body of the dialog.

You should set the trigger to perform whatever action your application requires (including the function of the trigger being replaced, if applicable). If necessary, the action handler should also perform the default handling of the replaced trigger. A moniker need not be supplied as the Specific UI will choose one based on the InteractionCommand and the context of the dialog. You may supply your own moniker.

In fact, in some cases you might want to merely change the default moniker of a standard response trigger without changing the functionality of the trigger itself. In this case, you should provide an override trigger with ATTR_GEN_TRIGGER_INTERACTION_COMMAND . You should then add your new visual moniker to this replacement but leave the action fields blank. The specific UI will assign any trigger set up with ATTR_GEN_TRIGGER_INTERACTION_COMMAND and a blank action to have the default behavior of the replaced trigger.

Code Display 7-24 Replacing a Standard Response Trigger

/* The GenInteractionType GIT_NOTIFICATION, when built as a dialog, automatically
 * contains a standard response trigger to send out the InteractionCommand IC_OK.
 * You can replace this trigger with one of your own by adding a trigger with that
 * InteractionCommand. Your trigger will override the system default. */
@object GenInteractionClass MyInteraction = {
    GII_type = GIT_NOTIFICATION;
    GII_visibility = GIV_DIALOG;
    GII_attrs = @default | GIA_NOT_USER_INITIATABLE | GIA_MODAL;
    GI_comp = MyGlyph, MyResponseTrigger;
}
@object GenGlyphClass MyGlyph = {
    GI_visMoniker = "New Mail has arrived";
}
/* MyResponseTrigger replaces the IC_OK default trigger for the Notification dialog
 * box. In this case, we will only change the visual moniker of the trigger. By
 * leaving the output fields blank, we cause the system to send
 * MSG_GEN_GUP_INTERACTION_COMMAND with IC_OK. (This is the default behavior.)
 * HINT_SEEK_REPLY_BAR is used to place this trigger within the reply bar of the
 * notification dialog. */
@object GenTriggerClass MyResponseTrigger = {
    GI_visMoniker = "OK, I acknowledge";
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_OK };
    HINT_SEEK_REPLY_BAR;
}

ATTR_GEN_TRIGGER_INTERACTION_COMMAND also performs a special function in dialogs displayed with UserDoDialog() . Whenever the user completes an Interaction in a UserDoDialog() dialog box by activating one of the response triggers, the InteractionCommand stored in the ATTR_GEN_TRIGGER_INTERACTION_COMMAND of the activated trigger will be returned. You can use this return value to determine which trigger the user activated. (See Thread Blocking Routines .)

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Standard Response Triggers | Next: 5.5 Triggers Completing Interactions


Triggers Completing Interactions

Menus and Dialog Boxes: 5.5 Interaction Commands: Triggers Completing Interactions

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Replacing Default Triggers

The GenClass attribute GA_SIGNAL_INTERACTION_COMPLETE is used to indicate that a response trigger completes a single user interaction within the dialog that contains it. Depending on the context (modality, type, hints) of the dialog, the Specific UI may or may not dismiss the dialog box.

For example, the "Yes" trigger (IC_YES) in a GIT_AFFIRMATION dialog might dismiss a dialog if the trigger is also marked GA_SIGNAL_INTERACTION_COMPLETE because the user has given the dialog final information. An "Apply" trigger (IC_APPLY) marked GA_SIGNAL_INTERACTION_COMPLETE might not dismiss its dialog because it might be useful to use the dialog to repeatedly apply different properties. This behavior depends on the Specific UI.

All standard response triggers provided by the Specific UI with the various GenInteractionType , except for the IC_RESET trigger), have GA_SIGNAL_INTERACTION_COMPLETE set. The IC_RESET trigger normally does not indicate the end of user interaction with the dialog and thus should not dismiss the dialog under any circumstances.

In OSF/Motif, a trigger marked GA_SIGNAL_INTERACTION_COMPLETE will dismiss the dialog containing the trigger if the dialog is not of GenInteractionType GIT_PROPERTIES or GIT_COMMAND, or if the dialog is modal. A non-modal GIT_PROPERTIES or GIT_COMMAND dialog is not dismissed when its response triggers are activated. If you do wish to dismiss the dialog in such cases, you can mark the dialog box with HINT_INTERACTION_SINGLE_USAGE . This hint tells the Specific UI that it expects the user will only use the dialog box once each time it is brought up. You may also want to do this if the dialog box is infrequently used.

Similarly, HINT_INTERACTION_FREQUENT_USAGE allows a dialog to stay up even if one of its GA_SIGNAL_INTERACTION_COMPLETE triggers would normally bring it down. In most cases, however, it is advisable to remove the GA_SIGNAL_INTERACTION_COMPLETE attribute on whichever triggers should not dismiss the Interaction. This is preferred over adding HINT_INTERACTION_FREQUENT_USAGE because the Specific UI may ignore the hint, but it cannot ignore the state of its GA_SIGNAL_INTERACTION_COMPLETE bit.

When using HINT_INTERACTION_FREQUENT_USAGE , you should ensure that the dialog is dismissed. In most cases, this can be done by sending MSG_GEN_GUP_INTERACTION_COMMAND with IC_DISMISS to the Interaction. This can be done in an action handler for a response trigger.

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Replacing Default Triggers


GenView

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenView Overview
| One-page Version of this Chapter for Easy Printing

The GenView provides a means of displaying data on the screen in a scrollable, sizeable, scalable window. This data may be in the form of a graphics document, a generic object tree, or a visible object tree. If your geode requires a means of displaying data visually, you will most likely want to use a view.

This chapter discusses how to create and use the GenView. You should have a good knowledge of the UI in general, and you should understand the GEOS coordinate system.

1 GenView Overview
1.1 GenView Model
1.2 View Features and Goals
1.3 The GenViewControl Object
2 Getting Started: View Basics
2.1 Graphics System Review
2.2 Defining the Basic View
2.3 Handling View Messages
3 Basic View Attributes
3.1 The GVI_attrs Attribute
3.2 Dimensional Attributes
3.3 Setting the Background Color
3.4 The GVI_increment Attribute
4 Advanced Concepts and Uses
4.1 The Life of a View
4.2 Documents in a View
4.3 Drawing the Document
4.4 Document and View Size
4.5 Document Scaling
4.6 Children of the View
4.7 Scrolling
4.8 Monitoring Input
4.9 Linking Views
4.10 Setting the Content
4.11 Internal Utilities
5 The GenViewControl
5.1 GenViewControl Instance Data
5.2 Notification Received
5.3 GenViewControl Example
5.4 Messages Handled

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenView Overview
| One-page Version of this Chapter for Easy Printing


GenView Overview

GenView: 1 GenView Overview

| Up: GEOS SDK TechDocs | Up | Down | Prev: GenView | Next: 2 Getting Started: View Basics

The primary goal of the GenView is to display data on the screen. It provides a window into which applications may draw any type of document from a generic object tree to a visible object tree to graphics strings defined by graphics routines. Nearly all document-oriented applications will use a view in some capacity.

GenView Model

View Features and Goals

The GenViewControl Object

| Up: GEOS SDK TechDocs | Up | Down | Prev: GenView | Next: 2 Getting Started: View Basics


GenView Model

GenView: 1.1 GenView Overview: GenView Model

| Up: GEOS SDK TechDocs | Up | Prev: 1 GenView Overview | Next: 1.2 View Features and Goals

The GenView is simply an object that maintains a window for your geode's use. It must be instructed in how to display your data, although it defaults to a certain display scheme. It receives and sends out messages that define how and when your data is drawn; you must provide an object that can receive the view's messages and respond correctly.

The object that communicates with the view on the application's side is known as the content object. The content may be one of three different classes (see below) depending on the type of data you wish to display. Essentially, what type of object you designate as your content is irrelevant to the view; the view sends and receives the same messages regardless.

There are three basic models you may employ based on the type of data you have to display:

  • Graphic document
    If you are just displaying standard graphics, you will want the content to be an object of ProcessClass . This means that your application's Process object will receive all the messages sent by the view (including MSG_META_EXPOSED , described later) and must know how to draw the graphics document when notified. This model is common.
  • Visible object tree
    If you have a tree of visible objects, you will want to set the view's content as an object of VisContentClass . This object understands how to respond to several of the view's messages and how to draw a visible tree when notified. This approach is also common.
  • Generic object tree
    Though seldom used, it is possible to display generic gadgets in a scrolling view by setting the view's content to be a GenContent object. The GenContent is analogous to the VisContent but works with generic object trees rather than visible object trees. The GenContent is used most often as the superclass of GenDocumentClass (see the Documents chapter).

As described in the Graphics Environment chapter, GEOS uses two different coordinate systems, both of which are based on points (1/72 inch). The default system uses 16-bit coordinates and allows documents up to 113 inches on a side. The large model uses 32-bit coordinates, resulting in documents up to 2,147,483,648 points on a side, or 940 miles square. The GenView uses the large model (32-bit coordinates) by default. This means that all coordinates related to the view will be 32 bits, though you can still use 16 bit coordinates for your documents.

Because the graphic document model described above is the simplest, this chapter will focus on having a ProcessClass object as your view's content.

| Up: GEOS SDK TechDocs | Up | Prev: 1 GenView Overview | Next: 1.2 View Features and Goals


View Features and Goals

GenView: 1.2 GenView Overview: View Features and Goals

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 GenView Model | Next: 1.3 The GenViewControl Object

The GenView is a powerful and flexible object that does most of your display work for you. It is designed to provide not only the functionality required by nearly all applications but also many additional features that more advanced applications will use. The following goals helped shape the GenView:

  • Common functionality
    The view, like all of GEOS, is designed with common application functionality in mind. Those functions common to many applications are integrated as much as possible to relieve programmers of having to code and debug the same features again and again.
  • Consistency of use
    The view, like every generic UI object, allows all GEOS applications to behave and look consistent. Users find this important when using several different applications.
  • Ease of use
    The view, in its simplest form, is almost trivial to implement. Applications can gain a scrolling window by adding very little code (as evidenced in the Hello World program).
  • Expandability
    Using the view's more advanced features requires little more than incremental changes to an application; you can easily and quickly add scaling to or change the background color of your view.

To meet these goals, the view provides several advanced features and makes them easy to use for all applications. All the following features are available to applications with limited effort, and most are expandable to be very powerful.

  • Drawing to the view
    Drawing to the view is simple, as simple as calling the proper graphics routines to draw to a GState. The view will send a MSG_META_EXPOSED to its content object whenever the document must be drawn (for example, when the view scrolls or when a window has uncovered a portion of the view); the content then simply must draw it. The view will handle all necessary clipping, placement, scaling, and translation for the video driver. See Documents in a View .
  • Scrolling
    You can set up your view to be scrollable and all scrolling to be automatic. You can also cause the view to scroll or even change how the view scrolls. See Scrolling .
  • Scaling
    Every view has a scale factor that determines the scale at which the document is drawn. You may leave this at the default (the normal size), or you may dynamically change it. The view simply asks the content object to redraw its document, and the view will handle all the scaling geometry. See Document Scaling .
  • Sizing
    Because most Views are resizable, you can easily alter the sizing behavior of your view. You may also set it to have a certain minimum, maximum, or fixed size. See Document and View Size .
  • Setting the Background Color
    Depending on the view's purpose, you may wish it to be either the same color as its GenPrimary parent or a different color. The View in Hello World, for example, is white; it could easily be any other color. See Setting the Background Color .
  • Handling Input
    Unless your view is display-only, your content will be able to handle either pointer or keyboard events passed through it. You can specify the level of input you want and easily get mouse or typed character events. In addition, the Ink standard for pen input is also supported.
  • Setting the Content Size and Origin
    Every content object has a size associated with it. The size defines where the content sits in the coordinate space and what part of the coordinate space the view has access to for scrolling. Every view also has an origin describing which part of the content is currently visible. See Documents in a View .
  • Changing the Content
    Although most Process objects will hang on to their Views, it is possible to change the view's content object. This is most likely used by applications that use the VisContent rather than the Process object as their content. See Setting the Content .
  • Defining Children of the view
    Every view may have UI objects designated as its children. These objects may appear at any edge of the view. By defining special scroller objects as children of the view, you can alter their placement; for example, you could have the vertical scroller always appear on the left rather than whatever is the default (typically the right). See Children of the View .
| Up: GEOS SDK TechDocs | Up | Prev: 1.1 GenView Model | Next: 1.3 The GenViewControl Object


The GenViewControl Object

GenView: 1.3 GenView Overview: The GenViewControl Object

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 View Features and Goals | Next: 2 Getting Started: View Basics

The GenView, like many objects in the UI, has its own UI controller object to further simplify its use. Though you can implement anything in the controller on your own, it's much easier simply to include a GenViewControl object and let it control the View features for you.

GenViewControlClass is described after the GenView; you must understand the GenView before you can take full advantage of the controller. See The GenViewControl for complete details.

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 View Features and Goals | Next: 2 Getting Started: View Basics


Getting Started: View Basics

GenView: 2 Getting Started: View Basics

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 GenView Overview | Next: 3 Basic View Attributes

As stated earlier, this chapter will demonstrate how an application would display a graphic document in a scrollable window. You can look at the Hello World and VisSamp sample applications for examples of the view's features and how to manipulate them. The entire code listing of the Hello World application is located in the Hello World chapter.

This section is meant to show you the basic requirements for implementation of a view. It begins with a review of the concepts employed by the GEOS graphics system and then describes how to define a GenView in your Goc code. It also shows how to handle important messages sent by the view.

Graphics System Review

Defining the Basic View

Handling View Messages

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 GenView Overview | Next: 3 Basic View Attributes


Graphics System Review

GenView: 2.1 Getting Started: View Basics: Graphics System Review

| Up: GEOS SDK TechDocs | Up | Prev: 2 Getting Started: View Basics | Next: 2.2 Defining the Basic View

The GEOS graphics system is explained in full in the Graphics Environment chapter. Some of it is reviewed here, although you should read the graphics discussions to fully understand how to draw a graphic document.

The GEOS default document space is represented by signed, 16-bit coordinates relative to a central origin. Coordinates are in 72 points per inch, and documents that use this space can be up to 8192 points, or 113.8 inches, on a side. If your document must be larger than this, you may make use of the "large document" model, which uses 32-bit signed coordinates. Large documents can be over 940 miles on a side.

Every document is drawn to a GState which keeps track of such current state as pen position, color, rotation, and scale. You may apply transformations on the GState to get different rotation and placement within the document space.

To display a graphic document in a view, you must set up the view to have your Process object as its content. This means that your Process object must be able to draw the document, handle messages received from the view, and control the view's behavior as required. The Process object must also know how to support the UI mechanisms with which the view's content object might want to interact (such as the quick-transfer mechanism).

The view defaults to using the large coordinate space for all its operations, though this does not mean only large documents can be displayed in the view. Large documents are represented in a signed, 32-bit (long integer) document coordinate space. This means large documents may be up to 2,147,483,648 points high by 2,147,483,648 points wide (941.5 miles on a side). Because the graphics system is optimized for 16-bit operations, there are special considerations you must take into account when using a view to display large documents.

There are two basic ways to deal with 32-bit coordinates in a 16-bit graphics system. Both involve performing an extended translation on the GState in order to draw in a "local" 16-bit graphics space. The extended translation is performed with GrApplyTranslationDWord() , which takes two 32-bit coordinates and translates the GState's origin to those coordinates. Any 16-bit drawing operations are then automatically done relative to the new origin. All translations are cumulative.

One of the two ways to handle large documents is to apply the extended translation before each drawing operation and untranslate after each operation. This obviously incurs quite a bit of additional overhead.

The other way of handling large documents is to "tile" them, just as the Spooler does when printing large documents on small paper. In this case, you would apply the extended translation once, from then on drawing in 16-bit coordinates relative to the local origin. When you wanted to move to another tile, you would apply another extended translation to the new tile. The drawback to this is that your drawing code has to recognize when the 16-bit local boundaries are being crossed and a transformation is necessary.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Getting Started: View Basics | Next: 2.2 Defining the Basic View


Defining the Basic View

GenView: 2.2 Getting Started: View Basics: Defining the Basic View

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Graphics System Review | Next: 2.3 Handling View Messages

To use a view to display a document, you should define it in your application's Goc code. The view can legally be a child of GenPrimary, GenDisplay, or GenInteraction. In simple applications, the view will be a child of the application's GenPrimary object. More complex applications will have multiple displays with a view in one or more GenDisplay objects.

The most basic view is resizable and has a white background. You may draw any size document in this view, and it will be clipped properly by the windowing system. At its most basic, the view is not scrollable; however, by setting two attributes as shown in The Basic GenView , the view becomes scrollable in both dimensions.

The most common view is resizable and scrollable with a white background. It generally has boundaries on its scrollable area, called document bounds. The Basic GenView shows this type of view.

Notice that the Goc code for the view is simple; much of the power of the view as a display tool rests in its dynamic flexibility. Once the basic view is defined, you can change its scale, color, scrolling behavior, and document bounds simply by sending messages to it. This dynamic flexibility is also exploited extensively by the GenViewControl.

Code Display 9-1 The Basic GenView

/*
 * The GenView object creates a window in which the application may draw all or
 * portions of its document. Whenever a portion of this window becomes "invalid"
 * (when the window is resized or moved, for example), the view will send
 * MSG_META_EXPOSED to its content (in this case the application's Process object)
 * indicating that a redraw is required. The view keeps track of the clipping
 * boundaries and will automatically display the document properly.
 */
/*
 * The view defined here provides a window with the default white background and
 * normal scrolling and sizing behavior. Its document boundaries are set to 11
 * by 17 inches (the default has all bounds set to zero). The view provides
 * scrollbars automatically, and the application does not need to understand how
 * scrolling is implemented; instead, the view will simply request a redraw of the
 * entire document.
 */
@object GenViewClass MyView = {
    /*
     * The document bounds are set to build an 11 x 17 inch document. Document
     * coordinates are in 1/72 inch (points), and document bounds must also be
     * specified in points. The upper-left corner of the document is placed at
     * (0,0), and the coordinates increase down and right to the far corner
     * (792, 1224).
     */
    GVI_docBounds = {
        0,		/* left bound */
        0,		/* top bound */
        792,		/* right bound */
        1224};		/* bottom bound */
    /*
     * The GVI_horizAttrs and GVI_vertAttrs fields determine the view's
     * scrolling, linking, and sizing behavior in the appropriate dimension.
     * Setting the scrollable attribute in both these fields will make the view
     * create and maintain scrollers (in OSF/Motif, these will appear as
     * scrollbars). All scrolling will happen automatically.
     * These lines are necessary if scrolling is desired.
     */
    GVI_horizAttrs = @default | GVDA_SCROLLABLE;
    GVI_vertAttrs = @default | GVDA_SCROLLABLE;
    /*  Lastly, we must designate which object (our Process object in this case)
     * will receive and handle the message to draw the displayed data
     * (MSG_META_EXPOSED). This object is called the Content object and may
     * be the Process, a GenContent, or a VisContent.
     * Note that no message is defined here as with most action descriptors;
     * the view will send only MSG_META_EXPOSED. You must set this attribute no
     * matter what object is designated as the content; when using a
     * GenContent or a VisContent, the object's name should appear in
     * place of process. */
    GVI_content = process;
}
| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Graphics System Review | Next: 2.3 Handling View Messages


Handling View Messages

GenView: 2.3 Getting Started: View Basics: Handling View Messages

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Defining the Basic View | Next: 3 Basic View Attributes

After defining the GenView, you must create the method that will handle MSG_META_EXPOSED sent by the view in order to draw in the view's window. This is absolutely necessary, even if your view is not scrollable; the view will send MSG_META_EXPOSED when any part of its display window becomes invalid--this includes when the view is first instantiated and its window is first displayed.

In this simple model, MSG_META_EXPOSED is the only message your application will need to handle relating to the view. If you are managing large documents, then you may also have to handle a few others pertaining primarily to input. A sample handler for MSG_META_EXPOSED --one you can modify and put in your own code--is shown in MSG_META_EXPOSED Handler .

If you are planning on displaying visible objects in the view (as opposed to having your Process object draw graphics), you should instead pay attention to MSG_VIS_DRAW . This message is defined by VisClass and is sent by a VisContent object to itself as the default behavior for MSG_META_EXPOSED . For full information on MSG_VIS_DRAW and visible objects, see the VisClass chapter.

Code Display 9-2 MSG_META_EXPOSED Handler

/* Each time some part of the view window becomes invalid, the view will send a
 * MSG_META_EXPOSED to its content object. When MSG_META_EXPOSED is received,
 * the content must draw the document to a newly-created GState, then pass the
 * GState to the windowing system for drawing to the screen. */
/* The format of this message is
 * 	void MSG_META_EXPOSED(WindowHandle win);
 * The passed value is the window handle of the view window; it will be used in
 * conjunction with the temporary GState for drawing. */
@method	MyProcessClass, MSG_META_EXPOSED {
    /* Set up a temporary GState handle variable for our document. */
    GStateHandle tempGState;
    /* Initialize the GState to the default. */
    tempGState = GrCreateState(win);
    /* Now start a window update. The routine GrBeginUpdate() notifies the window
     * system that we are in the process of drawing in the region in the window
     * that was invalidated. GrBeginUpdate() allows drawing to be clipped to only
     * the region that was invalidated. */
    GrBeginUpdate(tempGState);
    /* Now that we have a GState and have notified the windowing system that we
     * intend to draw, we can draw our document. Here, your method should call
     * whatever routines are appropriate for drawing the entire document.
     * A good hint here relates also to printing: Because printing and drawing to a
     * view are essentially the same operation, you might want to consolidate all
     * your drawing into a single routine that is called by both this method and
     * your printing method. This is what is done here; the routine called
     * MyDrawingRoutine() draws the document we wish displayed. MyDrawingRoutine()
     * will also be called by our printing method with a different GState. */
    MyDrawingRoutine(tempGState);
    /* Now that we have finished drawing the document, we must inform the window
     * system that we are done. Additionally, we must now destroy the temporary
     * GState we used to draw the document. This is essential as not destroying
     * gstates causes small locked blocks to remain on the heap, eroding system
     * performance quickly and eventually making the system run out of handles. */
    GrEndUpdate(tempGState);
    GrDestroyState(tempGState);
}
| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Defining the Basic View | Next: 3 Basic View Attributes


Basic View Attributes

GenView: 3 Basic View Attributes

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Getting Started: View Basics | Next: 4 Advanced Concepts and Uses

The GenView has several instance data fields that it uses to maintain its current status. It also has other attribute fields that determine its scrolling, scaling, and sizing behavior as well as the manner in which it accepts input events. Listed in GenView Instance Data are all the GenView's instance data fields. Each is described later in the chapter. Additionally, each is dynamically changeable with the use of messages defined by GenView.

Code Display 9-3 GenView Instance Data

/* Default settings are shown in the definitions. Other possible settings are
 * shown in a comment after the definition. */
    @instance PointDWFixed				GVI_origin = {{0, 0}, {0,0}};
    @instance RectDWord				GVI_docBounds = {0, 0, 0, 0};
    @instance PointDWord				GVI_increment = {20, 15};
    @instance PointWWFixed				GVI_scaleFactor = {{0, 1}, {0, 1}};
    @instance ColorQuad				GVI_color = {WHITE, 0, 0, 0};
    @instance GenViewAttrs				GVI_attrs = (GVA_FOCUSABLE);
    /* Possible flags for GVI_attrs:
     *	GVA_CONTROLLED			GVA_GENERIC_CONTENTS
     *	GVA_TRACK_SCROLLING			GVA_DRAG_SCROLLING
     *	GVA_NO_WIN_FRAME			GVA_SAME_COLOR_AS_PARENT_WIN
     *	GVA_VIEW_FOLLOWS_CONTENT_GEOMETRY
     *	GVA_WINDOW_COORDINATE_MOUSE_EVENTS
     *	GVA_DONT_SEND_PTR_EVENTS			GVA_DONT_SEND_KBD_RELEASES
     *	GVA_SEND_ALL_KBD_CHARS			GVA_FOCUSABLE
     *	GVA_SCALE_TO_FIT			GVA_ADJUST_FOR_ASPECT_RATIO */
    @instance GenViewDimensionAttrs					GVI_horizAttrs = 0;
    @instance GenViewDimensionAttrs					GVI_vertAttrs = 0;
    /* Possible flags for the dimension attribute records:
     *	GVDA_SCROLLABLE			GVDA_SPLITTABLE
     *	GVDA_TAIL_ORIENTED			GVDA_DONT_DISPLAY_SCROLLBAR
     *	GVDA_NO_LARGER_THAN_CONTENT
     *	GVDA_NO_SMALLER_THAN_CONTENT
     *	GVDA_SIZE_A_MULTIPLE_OF_INCREMENT
     *	GVDA_KEEP_ASPECT_RATIO					*/
    @instance GenViewInkType				GVI_inkType = GVIT_PRESSES_ARE_NOT_INK;
    /* Possible flags for GVI_inkType:
     *	GVIT_PRESSES_ARE_NOT_INK				GVIT_INK_WITH_STANDARD_OVERRIDE
     *	GVIT_PRESSES_ARE_INK				GVIT_QUERY_OUTPUT */
    @instance @optr				GVI_content;
    @instance @optr				GVI_horizLink;
    @instance @optr				GVI_vertLink;
/* The following are the hints and attributes defined for the GenView's vardata.
 * Because GenViewClass is a subclass of GenClass, it inherits all other generic
 * hints as well. */
    @vardata void			HINT_VIEW_LEAVE_ROOM_FOR_VERT_SCROLLER;
    @vardata void			HINT_VIEW_LEAVE_ROOM_FOR_HORIZ_SCROLLER;
    @vardata void			HINT_VIEW_IMMEDIATE_DRAG_UPDATES;
    @vardata void			HINT_VIEW_DELAYED_DRAG_UPDATES;
    @vardata void			HINT_VIEW_REMOVE_SCROLLERS_WHEN_NOT_SCROLLABLE;
    @vardata void			HINT_VIEW_SHOW_SCROLLERS_WHEN_NOT_SCROLLABLE;
    @vardata InkDestinationInfoParams ATTR_GEN_VIEW_INK_DESTINATION_INFO;
    @reloc ATTR_GEN_VIEW_INK_DESTINATION_INFO,
        word_offsetof(InkDestinationInfoParams, IDIP_dest), optr;
@vardata void ATTR_GEN_VIEW_DOES_NOT_ACCEPT_TEXT_INPUT;
    @vardata XYSize			ATTR_GEN_VIEW_PAGE_SIZE;
    @vardata void			ATTR_GEN_VIEW_SCALE_TO_FIT_BASED_ON_X;
    @vardata void			ATTR_GEN_VIEW_SCALE_TO_FIT_BOTH_DIMENSIONS;
    @vardata void			ATTR_GEN_VIEW_DO_NOT_WIN_SCROLL;
/* By default, all GenView objects are targetable. */
    @default GI_attrs = @default | GA_TARGETABLE;

The attributes shown in GenView Instance Data are all dynamically changeable; in fact, many (such as the scale factor) become interesting only when set or changed as a feature of the application (as in the scaling features of GeoWrite and GeoDraw). Each attribute, therefore, is described under its appropriate section of this chapter.

The GVI_attrs Attribute

Dimensional Attributes

Setting the Background Color

The GVI_increment Attribute

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Getting Started: View Basics | Next: 4 Advanced Concepts and Uses


The GVI_attrs Attribute

GenView: 3.1 Basic View Attributes: The GVI_attrs Attribute

| Up: GEOS SDK TechDocs | Up | Prev: 3 Basic View Attributes | Next: 3.2 Dimensional Attributes
GVI_attrs, MSG_GEN_VIEW_SET_ATTRS, MSG_GEN_VIEW_GET_ATTRS

The GVI_attrs field, as shown in GenView Instance Data , controls many aspects of the view, including several features related to scrolling, input events, and the content object. It is implemented as a bitfield record, and any or all of its attributes may be set at once.

Generally, the attributes specified in this field are not changed at run-time; if your application needs to change or retrieve them, however, it may with the messages MSG_GEN_VIEW_GET_ATTRS and MSG_GEN_VIEW_SET_ATTRS , both of which are shown below.

The attributes that can be set in GVI_attrs are shown in the listing below. The default settings are given in GenView Instance Data .

GVA_CONTROLLED
Set if the GenView is connected to a GenViewControl object and should therefore send out notification as appropriate and add itself to the proper GCN lists.
GVA_GENERIC_CONTENTS
Set if the content is a GenContentClass object. If this bit is set, the mouse grab mode and pointer sending mode are set by the specific UI, overriding whatever is set in the instance data.
GVA_TRACK_SCROLLING
Set if track scrolling is desired. If this is set, the view will send scrolling events to the content object. The content can then alter the scrolling behavior and send the event on to the view.
GVA_DRAG_SCROLLING
Set if drag scrolling is desired. If this bit is set, drag scrolling (when the user drags outside the window bounds, the window scrolls in that direction) is implemented by the view. The content does not have to do anything special; drag scrolling is implemented entirely by the view.
GVA_NO_WIN_FRAME
Set if no frame around the subview window is desired. This is set only if the view is meant to be a display view with its borders obscured (e.g. a text display that has no frame).
GVA_SAME_COLOR_AS_PARENT_WIN
Set if the background color of the view should be whatever color the parent window's background is. This is used in nearly every case where GVA_GENERIC_CONTENTS is also set.
GVA_VIEW_FOLLOWS_CONTENT_GEOMETRY
Set if the view should follow the size of the content in all non-scrollable dimensions. This can only be used if the view and the content are running in the same thread and if the content is not the process object.
GVA_WINDOW_COORDINATE_MOUSE_EVENTS
Set if mouse coordinates passed to the content are in window coordinates (relative to the window's origin) rather than document coordinates. If the document bounds are set outside the 16-bit boundaries of the standard graphics system, then this bit must be set. For full information on mouse event handling, see the Input chapter.

GVA_DONT_SEND_PTR_EVENTS
Set if pointer events should not be sent to the GenView's content. Use this for optimization if pointer events do not need to be sent through the view.

GVA_DONT_SEND_KBD_RELEASES
Set if no keyboard releases should be sent to the content. Applications will set this if they only care about presses, not releases.
GVA_SEND_ALL_KBD_CHARS
Set if the view should pass all keyboard presses and releases to the content before the Specific UI has a chance to interpret them. Normally, the Specific UI will receive keyboard events, filter out the ones it uses (including application-defined accelerators), and then pass the unused events on to the view. This flag will, in essence, override the authority of the Specific UI and therefore is generally highly undesirable. If an application finds a need for this flag, the application is responsible for passing unused characters on to the UI for proper processing. If the unused characters do not get passed on, synchronization problems could result; if an application decides to handle reserved characters, the specific UI's reaction will be unpredictable.
GVA_FOCUSABLE
Set if the view window can gain the input focus. This is set by default and should be set for nearly all views.
GVA_SCALE_TO_FIT
Set if the GenView should operate in "scale to fit" mode. In this mode, the vertical scale factor will automatically be set so the entire document fits vertically within the view window; the horizontal scale factor will then be set based on the vertical. This behavior can be modified by designating a page size (via ATTR_GEN_VIEW_PAGE_SIZE ), by changing the scaling to be based on the horizontal (via the attribute ATTR_GEN_VIEW_SCALE_TO_FIT_BASED_ON_X ), or by setting ATTR_GEN_VIEW_SCALE_TO_FIT_BOTH_DIMENSIONS .
GVA_ADJUST_FOR_ASPECT_RATIO
Set if the GenView should alter its scale factors appropriate to the aspect ratio of the screen.

MSG_GEN_VIEW_SET_ATTRS

void	MSG_GEN_VIEW_SET_ATTRS(
        word		attrsToSet,
        word		attrsToClear,
        VisUpdateMode		updateMode);

This message changes the view's GVI_attrs record to new values.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: attrsToSet A record of GenViewAttrs to set in GVI_attrs .

attrsToClear
A record of GenViewAttrs ; each flag set in this parameter will be cleared in GVI_attrs .
updateMode
A VisUpdateMode indicating when the GenView should be visually updated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_GET_ATTRS

word	MSG_GEN_VIEW_GET_ATTRS();

This message returns a word representing the current GVI_attrs record. To check whether an attribute is set, mask the returned word value against the attribute.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: None.

Return: The current GenViewAttrs record in the GenView's GVI_attrs field.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Basic View Attributes | Next: 3.2 Dimensional Attributes


Dimensional Attributes

GenView: 3.2 Basic View Attributes: Dimensional Attributes

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 The GVI_attrs Attribute | Next: 3.3 Setting the Background Color
GVI_horizAttrs, GVI_vertAttrs, MSG_GEN_VIEW_GET_DIMENSION_ATTRS, MSG_GEN_VIEW_SET_DIMENSION_ATTRS

Together, the GVI_horizAttrs and GVI_vertAttrs fields are called the "dimension attributes." They determine the characteristics of the view in each dimension--whether it is scrollable or splittable, whether a scrollbar should be displayed, and whether certain rules should be applied to sizing and scaling.

These attributes, once set, are not frequently changed during execution. Both of these attributes are bitfield records. Each has the same set of possible flags, and these flags are listed below (they are bits in the record GenViewDimensionAttrs ):

GVDA_SCROLLABLE
Set if view is scrollable in the given dimension.
GVDA_SPLITTABLE
Set if the view is splittable in the given dimension. This is not currently implemented but is planned.
GVDA_TAIL_ORIENTED
Set if the document prefers to be displayed at its end. This flag will cause the view to stay at the bottom of the document when the document's length or size is changed--if scrolling to the middle or top, however, the tail orientation will be ignored. Currently, tail orientation is not implemented across threads but may be approximated with track scrolling.
GVDA_DONT_DISPLAY_SCROLLBAR
Set if the scroller for the given dimension should not be displayed even if the dimension is scrollable.
GVDA_NO_LARGER_THAN_CONTENT
Set if the view should never get larger than the content's maximum size in the given dimension (based on the current GVI_docBounds settings). By default, there are no restrictions on the size of the view--it is generally dictated by the view's parent window unless a size hint is applied to the view.
GVDA_NO_SMALLER_THAN_CONTENT
Set if the view should always stay large enough to show the entire content in the given dimension (based on the current GVI_docBounds settings). By default, there are no restrictions on the size of the view.
GVDA_SIZE_A_MULTIPLE_OF_INCREMENT
Set if the view size should always be rounded down to a multiple of the GVI_increment value in the given dimension. If greater control over the sizing behavior is required, a subclass of GenViewClass should be used with an altered handler of MSG_GEN_VIEW_CALC_WIN_SIZE .
GVDA_KEEP_ASPECT_RATIO
Set if the aspect ratio implied by the view's initial length and width should be maintained. If set in GVI_horizAttrs , the view will calculate the width according to the height. If set in GVI_vertAttrs , the view will calculate the height according to the width. This flag is normally used in conjunction with HINT_INITIAL_SIZE . It is an error to set this flag in both dimensions.

If your application must retrieve or set these attributes during execution, it may do so with the messages MSG_GEN_VIEW_GET_DIMENSION_ATTRS and MSG_GEN_VIEW_SET_DIMENSION_ATTRS . Also, to combine and separate the attribute fields, you can use the three macros also shown below.

MAKE_SET_CLEAR_ATTRS

word	MAKE_SET_CLEAR_ATTRS(setAttrs, clearAttrs);
        byte	setAttrs, clearAttrs;

This macro takes two byte values and combines them into a one-word argument. It is used with MSG_GEN_VIEW_SET_DIMENSION_ATTRS , below.

MAKE_HORIZ_ATTRS

byte	MAKE_HORIZ_ATTRS(val);
        word	val;

This macro takes a word-sized value and returns the high byte only (used with MSG_GEN_VIEW_GET_DIMENSION_ATTRS , below, to retrieve the GVI_horizAttrs flag from the returned value).

MAKE_VERT_ATTRS

byte	MAKE_VERT_ATTRS(val);
        word	val;

This macro takes a word and returns the low byte only. It should be used with MSG_GEN_VIEW_GET_DIMENSION_ATTRS , below, to extract the GVI_vertAttrs flag from the returned value.

MSG_GEN_VIEW_SET_DIMENSION_ATTRS

void	MSG_GEN_VIEW_SET_DIMENSION_ATTRS(
        word	horizAttrsToSetClear,
        word	vertAttrsToSetClear,
        VisUpdateMode updateMode);

This message changes the GVI_horizAttrs and GVI_vertAttrs records of the view.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: horizAttrsToSetClear
 A word consisting of two records of GenViewDimensionAttrs flags. The high byte represents the flags to be cleared, and the low byte represents the flags to be set; these flags pertain to the GVI_horizAttrs field.

vertAttrsToSetClear
A word consisting of two records of GenViewDimensionAttrs flags. The high byte represents the flags to be cleared, and the low byte represents the flags to be set; these flags pertain to the GVI_vertAttrs field.
updateMode
A VisUpdateMode type.

Interception: Nothing.

Warnings: Generally not intercepted.

Tips: The macro MAKE_SET_CLEAR_ATTRS is defined above and combines two byte-sized records into one word-sized record for the Goc preprocessor. Its use is shown in the example below.

@call MyView::MSG_GEN_VIEW_SET_DIMENSION_ATTRS(
    MAKE_SET_CLEAR_ATTRS(horizSet, horizClear),
    MAKE_SET_CLEAR_ATTRS(vertSet, vertClear),
    VUM_NOW);

MSG_GEN_VIEW_GET_DIMENSION_ATTRS

word	MSG_GEN_VIEW_GET_DIMENSION_ATTRS();

This message returns the GenView's current dimension attribute records.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: None.

Return: A word consisting of two GenViewDimensionAttrs records. Retrieve the horizontal attributes ( GVI_horizAttrs ) with the macro MAKE_HORIZ_ATTRS and the vertical attributes ( GVI_vertAttrs ) with the macro MAKE_VERT_ATTRS.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 The GVI_attrs Attribute | Next: 3.3 Setting the Background Color


Setting the Background Color

GenView: 3.3 Basic View Attributes: Setting the Background Color

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Dimensional Attributes | Next: 3.4 The GVI_increment Attribute
GVI_color, MSG_GEN_VIEW_SET_COLOR, MSG_GEN_VIEW_GET_COLOR, GVCD_INDEX, GVCD_RED, GVCD_FLAGS, GVCD_BLUE_AND_GREEN, GVCD_BLUE, GVCD_GREEN

A view can have its background color determined by an RGB value, a grayscale value, a CMY value, or as a GEOS color index.

If a view is managing generic objects or is displaying text, it may be most appropriate for the view to appear with the same background color as its parent Primary window. To do this, set the view's GVI_attrs field to the following:

GVI_attrs = @default | GVA_SAME_COLOR_AS_PARENT_WIN;

Otherwise, you will probably want to set the view to something other than the parent's color. To use a standard color index (16-color EGA set), set the GVI_color attribute as follows:

GVI_color = {index, 0, 0, 0};
    /* index is a GEOS color index, one
     * of the Color enumerated type. */

There is no need to set any of the other color fields if you are using an index. The default background color is determined by the specific UI but is normally C_WHITE. (Color indexes are described in the Drawing Shapes chapter.)

If you want to use the RGB scheme for setting the background color, you must know the structure used for defining the color. The view uses the structure ColorQuad to hold all four of the color data fields. This structure is defined as follows:

typedef struct {
    byte			CQ_redOrIndex;
            /* color index or red value */
    ColorFlag			CQ_info;
    byte			CQ_green;		/* green value */
    byte			CQ_blue;		/* blue value */
} ColorQuad;

When using a color index, you only need to set the CQ_info field to the proper index and set the flags to zero. When using other types of value, however, you must set the CQ_info byte to the type and the other fields to their proper values as shown below:

GVI_color = { redVal,					/* red RGB value */
              CF_RGB,					/* RGB flag */
              greenVal,					/* green RGB value */
              blueVal }					/* blue RGB value */

The ColorFlag record determines how the color will appear on the screen. It record may have any of one the following values:

CF_INDEX
This flag indicates the color is a palette index, specified in the CQ_redOrIndex field.
CF_GRAY
This flag indicates the color will be set with a grayscale value as determined by the other three fields.
CF_SAME
This flag indicates the color will not be changed (used for hatch patterns, for example).
CF_CMY
This flag indicates the color is set with CMY values.
CF_RGB
This flag indicates the colors are specified in RGB values in the other three color fields.

As an alternative, you may retrieve, set, or change the view's background color during execution with the messages MSG_GEN_VIEW_SET_COLOR and MSG_GEN_VIEW_GET_COLOR . These messages are shown below. You may also use the macros referenced below to construct and extract color arguments.

MSG_GEN_VIEW_SET_COLOR

void	MSG_GEN_VIEW_SET_COLOR(
        byte		indexOrRed,				/* color index or red value */
        ColorFlag		flags,				/* color flags */
        word		greenBlue);				/* green and blue values */

This message sets the background color of the view. It will not affect the document or printing. The color may be specified either by standard GEOS color index or by RGB value.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: indexOrRed If specifying the color as an index, this is the index value. If specifying as an RGB value, this is the red component of the value.

flags
A record of ColorFlag . If specifying the color as an RGB value, set this to CF_RGB. If specifying the color as a palette index, set this to CF_INDEX.
greenBlue
The green and blue components of the RGB value. If specifying color as an index, set this to zero. If specifying as an RGB value, combine the green and blue components into a single parameter with the macro GVC_GREEN_AND_BLUE.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_GET_COLOR

dword	MSG_GEN_VIEW_GET_COLOR();

This message returns all four fields of the view's background color in a single dword value.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: None.

Return: The four color specifier fields of the GenView's current background color. The individual values may be retrieved with the macros below.

Interception: Generally not intercepted.

GVCD_INDEX

byte	 GVCD_INDEX(val);
        dword	val;

This macro extracts the color index byte from the given dword value. It is intended for use when an index is used.

GVCD_RED

byte	 GVCD_RED(val)
        dword	val;

This macro extracts the red component byte from the given dword value. It is intended for use with RGB values.

GVCD_FLAGS

byte	 GVCD_FLAGS(val)
        dword	val;

This macro extracts the color flags byte from the given dword.

GVCD_GREEN

byte	 GVCD_GREEN(val)
        dword	val;

This macro extracts the green component byte from the given dword.

GVCD_BLUE

byte	 GVCD_BLUE(val)
        dword	val;

This macro extracts the blue component byte from the given dword.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Dimensional Attributes | Next: 3.4 The GVI_increment Attribute


The GVI_increment Attribute

GenView: 3.4 Basic View Attributes: The GVI_increment Attribute

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Setting the Background Color | Next: 4 Advanced Concepts and Uses
GVI_increment, MSG_GEN_VIEW_GET_INCREMENT, MSG_GEN_VIEW_SET_INCREMENT

Every view has an increment associated with it; this increment may be used in scrolling and sizing the view window. The increment has both a horizontal and a vertical component which are separate from each other. The increment must be specified in document coordinates (points) and is represented by a signed long number. By default, the horizontal increment is set to 20 and the vertical to 15.

The increment values are stored in the GVI_increment field. This field is a structure of type PointDWord . This structure consists of two signed long integers, as follows:

  typedef struct {
    sdword		PD_x;		/* x increment */
    sdword		PD_y;		/* y increment */
} PointDWord;

It is easy to set the increment values in your Goc code, thus:

GVI_increment = {
         horiz,    /* horizontal increment value */
         vert }    /* vertical increment value */

In general, the increment will not change during execution. However, if you find it necessary to do so, you can retrieve and set the increment with MSG_GEN_VIEW_GET_INCREMENT and MSG_GEN_VIEW_SET_INCREMENT .

MSG_GEN_VIEW_SET_INCREMENT

void	MSG_GEN_VIEW_SET_INCREMENT(@stack
        sdword	yIncrement,
        sdword	xIncrement);

This message sets the horizontal and vertical increment values for the view. If the increment had been used before for scrolling, sizing, or scaling, all the same operations will subsequently use the new increment. The two increment values must be in document coordinates (1/72 inch).

Source: Unrestricted.

Destination: Any GenView object.

Parameters: yIncrement The new vertical increment value.

xIncrement
The new horizontal increment value.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_GET_INCREMENT

void	MSG_GEN_VIEW_GET_INCREMENT(
        PointDWord *increment);

This message returns the view's current horizontal and vertical increments in document coordinates.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: increment A pointer to an empty PointDWord structure.

Return: The increment pointer will point to the PointDWord structure containing the horizontal and vertical increment values.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Setting the Background Color | Next: 4 Advanced Concepts and Uses


Advanced Concepts and Uses

GenView: 4 Advanced Concepts and Uses

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Basic View Attributes | Next: 5 The GenViewControl

The GenView's greatest strength is its dynamic flexibility. By sending a message, you can change the scale factor or scroll to any point in a document. You can even define your own scroller objects and objects that can appear at the edges of the view with the scrollers.

This section of this chapter explains the features, messages, and functions of the view that beginners can defer for now; however, the view in general is not complicated and can easily be shaped into what you need.

The Life of a View

Documents in a View

Drawing the Document

Document and View Size

Document Scaling

Children of the View

Scrolling

Monitoring Input

Linking Views

Setting the Content

Internal Utilities

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Basic View Attributes | Next: 5 The GenViewControl


The Life of a View

GenView: 4.1 Advanced Concepts and Uses: The Life of a View

| Up: GEOS SDK TechDocs | Up | Prev: 4 Advanced Concepts and Uses | Next: 4.2 Documents in a View

When a view is first instantiated, the window system wants to make sure everything happens properly. As a result, the view sends out a series of messages intended to notify the content that the view is opening and that it should be ready to draw its document soon. Most of these messages are intended for the GenContent and VisContent objects, and you will not need to handle them if you designate your Process object as the view's content. Similarly, when the view is shutting down, it notifies its content of the fact that it is closing. Again, the Process does not typically need to handle these messages.

Handling View Messages

MSG_META_CONTENT_SET_VIEW, MSG_META_CONTENT_VIEW_ORIGIN_CHANGED, MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED, MSG_META_CONTENT_VIEW_WIN_OPENED, MSG_META_CONTENT_VIEW_OPENING, MSG_META_CONTENT_VIEW_SIZE_CHANGED

When a GenView is instantiated, the UI takes care of most of the work. However, the view will send several messages to its new content in order to notify it that the view is opening.

The first message sent will be a MSG_META_CONTENT_SET_VIEW , which you may intercept if you need to know the view's object pointer (optr).

The content will then receive a series of other messages indicating the view's initial state. These messages are also sent whenever the view changes its state--for example, when the user moves a scrollbar, the view's origin will change. In general, your Process will not need to handle these. If you are using one of the content objects, you may need them. They are detailed below (for full reference information on these messages, see the VisContent chapter):

  • MSG_META_CONTENT_VIEW_ORIGIN_CHANGED
     This message indicates the initial origin of the view. See Current Origin for more information.
  • MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED
     This message indicates the original scale factor of the view. If you are displaying large documents, you will need to know this in order to handle input events.
  • MSG_META_CONTENT_VIEW_WIN_OPENED
     This message indicates that the view window is being created. Unless you plan on keeping track of several open views, you will not need to handle this message. It is sent before the window is actually opened; this means that the view may or may not be mapped, and you should not draw your document in response to this message. You should instead wait until you receive a MSG_META_EXPOSED . Some applications may intercept this message to get the window handle of the GenView and cache it for future use (until the window is closed).
  • MSG_META_CONTENT_VIEW_OPENING
     This message indicates that the view has received a MSG_VIS_OPEN and that the content will soon be put on the screen. Your Process object will not likely handle this message, though some special VisContents may in order to prepare information needed before drawing.
  • MSG_META_CONTENT_VIEW_SIZE_CHANGED
     This message indicates a change in size of the view--your Process object will not need to intercept this message unless you plan on using the view's width and height for something special.

After the view has been created and opened and is on the screen, it will send a MSG_META_EXPOSED to the content. At this time, the content must make sure the document gets drawn properly (see Drawing the Document ). No special handler is required when a view starts up; the MSG_META_EXPOSED is the same as would be received normally.

When the View Shuts Down

MSG_META_CONTENT_VIEW_CLOSING, MSG_META_CONTENT_VIEW_WIN_CLOSED, MSG_META_CONTENT_SET_VIEW

When a view is about to be closed, a MSG_META_CONTENT_VIEW_CLOSING is sent to the content to notify it of the shutdown. This message is primarily used by VisContentClass so it can send MSG_VIS_CLOSE to itself and all its children.

When the view is being destroyed in addition to being closed, the content will receive a MSG_META_CONTENT_VIEW_WIN_CLOSED . You will only need to handle this message if you have cached the view's window handle. If so, your application should throw out the old window handle to avoid any future drawing to the nonexistent window.

The content will then receive a MSG_META_CONTENT_SET_VIEW again (see above), this time with a null value passed as the view's optr. As with the MSG_META_CONTENT_SET_VIEW received when the view started up, your Process object will not need to intercept this message.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Advanced Concepts and Uses | Next: 4.2 Documents in a View


Documents in a View

GenView: 4.2 Advanced Concepts and Uses: Documents in a View

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 The Life of a View | Next: 4.3 Drawing the Document

The coordinate space is the graphics space owned by your geode for drawing. Your actual data may occupy any portion of it, but the view considers the entire space to be valid. However, you can constrain the view to a certain portion of the coordinate space--for example, if your data only fills 8.5 inches by 11 inches, you can instruct the view to scroll only in that 8.5 by 11 area (or any smaller or larger portion of it).

Document Coordinates

Applications do all their drawing to a device-independent coordinate space. Document coordinates occur in real-world points (72 points per inch). The default graphics system is 8192 points square, with the origin in the middle. The maximum size of a default document is about 113.8 inches on a side. GEOS uses a "large" graphics space as well, represented by 32-bit coordinates that allow documents up to 940 miles on a side. The GenView's coordinates are all given in these 32-bit values to accommodate either the large or the default system.

Your document's data may fill any or all of the coordinate space. Most small documents will have their upper-left corners at the origin and will occupy only a small portion of a 16-bit coordinate space.

Current Origin

GVI_origin, MSG_GEN_VIEW_SET_ORIGIN, MSG_GEN_VIEW_GET_ORIGIN, MSG_GEN_VIEW_SET_ORIGIN_LOW

The GenView must know where in the coordinate space it is located. To do this, it maintains an origin--the upper left coordinates of the window's scrollers. That is, the origin is the upper-left position in the document. The origin is stored in the field GVI_origin , a PointDWFixed structure containing two DWFixed structures. The PointDWFixed structure definition is shown below:

  typedef struct {
    DWFixed		PDF_x;		/* x origin coord */
    DWFixed		PDF_y;		/* y origin coord */
} PointDWFixed;

The view notifies its content each time the origin is changed with the message MSG_META_CONTENT_VIEW_ORIGIN_CHANGED . Typically, the view will also automatically scroll its window to the new origin when the origin changes. You can override this behavior, forcing the view not to scroll on origin changes, by setting the vardata attribute ATTR_GEN_VIEW_DO_NOT_WIN_SCROLL . This attribute, in effect, disconnects the scrollers from the visible position of the document. The notification message MSG_META_CONTENT_VIEW_ORIGIN_CHANGED will still be sent to the content; the window will not scroll, however.

The default origin of the view is (0,0), but this may be set in your Goc code as follows:

GVI_origin = {
    xPosition,			/* x coordinate of origin */
    yPosition			/* y coordinate of origin */
}

You can also change the origin during execution either by causing the view to scroll or by sending a MSG_GEN_VIEW_SET_ORIGIN . To retrieve the view's current origin, use MSG_GEN_VIEW_GET_ORIGIN .

MSG_GEN_VIEW_SET_ORIGIN

void	MSG_GEN_VIEW_SET_ORIGIN(@stack
        sdword	yOrigin,		/* y coordinate of new origin */
        sdword	xOrigin);		/* x coordinate of new origin */

This message changes the view's current origin, causing it to scroll immediately to the new location. The origin represents the upper-left corner of the view in the scrollable document space and is made up of two signed long integers. Each coordinate must be in document coordinates (points).

Source: Unrestricted.

Destination: Any GenView object.

Parameters: yOrigin The new Y coordinate of the origin.

xOrigin
The new X coordinate of the origin.

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_VIEW_SCROLL , MSG_GEN_VIEW_SCROLL_..., MSG_GEN_VIEW_MAKE_RECT_VISIBLE

MSG_GEN_VIEW_GET_ORIGIN

void	MSG_GEN_VIEW_GET_ORIGIN(
        PointDWord *origin);

This message returns a PointDWord structure containing the current X and Y coordinates of the view's origin.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: origin A pointer to an empty PointDWord structure.

Return: The origin pointer will point to a filled structure containing the X and Y coordinates of the current origin. This corresponds to the GenView's GVI_origin field.

Interception: Generally not intercepted.

See Also: MSG_GEN_VIEW_GET_DOC_BOUNDS , MSG_GEN_VIEW_GET_DOC_SIZE

MSG_GEN_VIEW_SET_ORIGIN_LOW

void	MSG_GEN_VIEW_SET_ORIGIN_LOW(@stack
        sdword		yOrigin,
        sdword		xOrigin);

This low-level scroll message is just like MSG_GEN_VIEW_SET_ORIGIN except that it will not propagate to linked views.

Source: Unrestricted--usually encapsulated by MSG_GEN_VIEW_SET_ORIGIN for later dispatch with MSG_GEN_VIEW_SEND_TO_HLINK or MSG_GEN_VIEW_SEND_TO_VLINK handler.

Destination: Any GenView.

Parameters: yOrigin The new vertical origin of the GenView, in document coordinates. For no change, pass GVSOL_NO_CHANGE.

xOrigin
The new horizontal origin of the GenView, in document coordinates. For no change, pass GVSOL_NO_CHANGE.

Return: Nothing.

Interception: Generally not intercepted.

Structures: GVSOL_NO_CHANGE is equal to 0x8000000.

Document Bounds

GVI_docBounds, MSG_GEN_VIEW_SET_DOC_BOUNDS, MSG_GEN_VIEW_GET_DOC_BOUNDS

Because most documents will not actually require the entire document space, the view has an instance data field that defines the view's scrollable boundaries. This field is a RectDWord structure containing four signed dwords, each of which represents a specific boundary of the document. The RectDWord structure is shown below:

  typedef struct {
    sdword		RD_left;		/* left bound */
    sdword		RD_top;		/* top bound */
    sdword		RD_right;		/* right bound */
    sdword		RD_bottom;		/* bottom bound */
} RectDword;

The document bounds are stored in the view's GVI_docBounds instance field. The defaults are shown in the following code; note that each of the values in the field must be in document coordinates.

GVI_docBounds = {0, 0, 0, 0};					/* zero size */

To change the scrollable document bounds at run-time, send the view a message MSG_GEN_VIEW_SET_DOC_BOUNDS . To retrieve the current document bounds, send MSG_GEN_VIEW_GET_DOC_BOUNDS .

MSG_GEN_VIEW_SET_DOC_BOUNDS

void	MSG_GEN_VIEW_SET_DOC_BOUNDS(@stack
        sdword	bottom,		/* new bottom document bound */
        sdword	right,		/* new right document bound */
        sdword	top,		/* new top document bound */
        sdword	left);		/* new left document bound */

This message sets the view's scrollable document bounds to those passed. Each of the four parameters must be given in document coordinates (points), and all are signed long integers. If the new document bounds cause the view to show invalid coordinates, the view will scroll until it is showing valid document space.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: bottom , right , top , left
 As described in the header above.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_GET_DOC_BOUNDS

void	MSG_GEN_VIEW_GET_DOC_BOUNDS(
        RectDWord *bounds);

This message returns the current boundaries of the view's scrollable document space.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: bounds A pointer to an empty RectDWord structure, to be filled in by the method.

Return: The structure pointed to by bounds will be filled with the current scrollable document boundaries of the GenView, as stored in GVI_docBounds .

Interception: Generally not intercepted.

Current Coordinates in the View

MSG_GEN_VIEW_GET_VISIBLE_RECT

To learn what visible rectangle the view is currently displaying, send it a MSG_GEN_VIEW_GET_VISIBLE_RECT . This message will return the current visible rectangle in document coordinates (a RectDWord structure). If the view is not on the screen, the message will return a structure full of zeros.

MSG_GEN_VIEW_GET_VISIBLE_RECT

void	MSG_GEN_VIEW_GET_VISIBLE_RECT(
        RectDWord *rect);

This message returns the rectangle currently visible in the view, in document coordinates.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: rect A pointer to an empty RectDWord structure to be filled in by the method.

Return: The structure pointed to by rect will contain the bounds of the visible portion of the document. If the view window is not visible on the screen, the structure will contain all zeros.

Interception: Generally not intercepted.

Tips: If your document is particularly complex, you might want to call this message and draw only the visible portions upon receiving either MSG_META_EXPOSED or MSG_VIS_DRAW .

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 The Life of a View | Next: 4.3 Drawing the Document


Drawing the Document

GenView: 4.3 Advanced Concepts and Uses: Drawing the Document

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Documents in a View | Next: 4.4 Document and View Size
MSG_GEN_VIEW_GET_WINDOW, MSG_GEN_VIEW_REDRAW_CONTENT

Displaying your document inside a view is as easy as drawing the document. Any time part of the view becomes invalid (due to scrolling or window movement, for example), the content will receive a MSG_META_EXPOSED. Your content object must have a handler for MSG_META_EXPOSED . See MSG_META_EXPOSED Handler for an example of handling MSG_META_EXPOSED .

You may also cause your application to redraw the window at any time. However, you will need to either save the window handle when the view is created (see MSG_META_CONTENT_VIEW_WIN_OPENED ) or retrieve it with MSG_GEN_VIEW_GET_WINDOW . When your content has the window handle, it can create a GState for that window and draw to it as if a MSG_META_EXPOSED had been sent; drawing the changed portion of the document will change the drawing on the screen. Note that the content should not send itself a MSG_META_EXPOSED --this message should be generated only by the window system because the window system will then know which parts of the screen have become invalid. For a self-generated MSG_META_EXPOSED to work, you must also get the view's visible rectangle (with MSG_GEN_VIEW_GET_VISIBLE_RECT , above) and call GrInvalRect() to invalidate the view's window.

A simpler way to force a redraw is to make the GenView redraw its entire document with MSG_GEN_VIEW_REDRAW_CONTENT . One example of when this might be used is a "Refresh" or "Redraw" option; this causes the window to be redrawn in order to clean up anything extra that may be left on the screen. Note that this is an option provided by the GenViewControl object.

MSG_GEN_VIEW_GET_WINDOW

WindowHandle MSG_GEN_VIEW_GET_WINDOW();

This message returns the window handle of the view window. As an alternative, you can store the view's window handle when MSG_META_CONTENT_VIEW_WIN_OPENED is sent upon view startup (see The Life of a View ) and discard it when you receive MSG_META_CONTENT_VIEW_CLOSING .

Source: Unrestricted.

Destination: Any GenView object.

Parameters: None.

Return: The window handle of the GenView's window.

Interception: Generally not intercepted.

See Also: MSG_META_CONTENT_VIEW_WIN_OPENED , MSG_META_CONTENT_VIEW_CLOSING

MSG_GEN_VIEW_REDRAW_CONTENT

void	MSG_GEN_VIEW_REDRAW_CONTENT();

This message causes the GenView to redraw its entire content. This is essentially the same as exposing the entire view window.

Source: Unrestricted--typically the GenViewControl

Destination: Any GenView object.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Documents in a View | Next: 4.4 Document and View Size


Document and View Size

GenView: 4.4 Advanced Concepts and Uses: Document and View Size

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Drawing the Document | Next: 4.5 Document Scaling

Typically, the document's size will be independent of the view's size. The visual relationship between the two is maintained with the use of scrollers. In these situations, the view will size itself according to the size of its parent Primary window. However, you can change the sizing behavior of the view simply by setting attributes or applying hints.

Setting the View's Size

HINT_INITIAL_SIZE, HINT_MINIMUM_SIZE, HINT_MAXIMUM_SIZE, HINT_FIXED_SIZE

Although the view normally sizes itself to fit the specifications of its parent window, you can change this behavior by applying one of four hints or by setting vertical or horizontal attributes.

  • HINT_INITIAL_SIZE
     This hint sets the initial size of the view to a given width and height. The view window will never shrink smaller than the size set with this hint.
  • HINT_MINIMUM_SIZE
     This hint sets the smallest possible size of the view window. The view's initial size is unaffected by this hint except to ensure that the parent Primary opens large enough to accommodate the view window.
  • HINT_MAXIMUM_SIZE
     This hint sets the maximum allowable size of the view window. This may be used in place of or in addition to the document bounds.
  • HINT_FIXED_SIZE
     This hint has the effect of setting initial, minimum, and maximum sizes at once to set a permanent size for the view window.

All the above hints deal with the structure CompSizeHintArgs . This structure allows specification of size in any of a number of different formats including pixels, percentage of screen size, multiples of character widths, number of text lines, and others.

You may also set GVDA_NO_LARGER_THAN_CONTENT and/or GVDA_NO_SMALLER_THAN_CONTENT in the GVI_horizAttrs and GVI_vertAttrs attribute fields to ensure that the view stays with the content's size. These attributes can be set as follows:

GVI_horizAttrs = @default
                | GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;
GVI_vertAttrs = @default
                | GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;

The above lines will ensure that the view will remain exactly the same size as the content (within screen boundaries).

You can also instruct the view window to snap its width or height to a multiple of the current horizontal or vertical increment. To do this, set the GVI_vertAttrs or GVI_horizAttrs attribute GVDA_SIZE_A_MULTIPLE_OF_INCREMENT as below:

GVI_horizAttrs = @default
                | GVDA_SIZE_A_MULTIPLE_OF_INCREMENT;
GVI_vertAttrs = @default
                | GVDA_SIZE_A_MULTIPLE_OF_INCREMENT;

Leaving Room for Scrollers

HINT_VIEW_LEAVE_ROOM_FOR_VERT_SCROLLER, HINT_VIEW_LEAVE_ROOM_FOR_HORIZ_SCROLLER

Occasionally, you may want to leave extra room for scroller objects even when your view will not have scrollers. You can do so with the hints HINT_VIEW_LEAVE_ROOM_FOR_VERT_SCROLLER and HINT_VIEW_LEAVE_ROOM_FOR_HORIZ_SCROLLER . Although these hints will not affect the size of the view's window, they will affect the view's geometry with respect to its parent window.

Adjusting the view's Size

MSG_GEN_VIEW_CALC_WIN_SIZE

When determining its size initially, the view sends itself the message MSG_GEN_VIEW_CALC_WIN_SIZE . This message, while not useful to applications in and of itself, may be subclassed to get special window sizing behavior. When this message is sent, it includes a suggested window size; by subclassing the GenView and altering the functionality of this message, you can return a different suggested size. The new suggestion will be used to set the view's new size, except at very small sizes--the view will make itself large enough to show scrollers and other children if required.

MSG_GEN_VIEW_CALC_WIN_SIZE

SizeAsDWord MSG_GEN_VIEW_CALC_WIN_SIZE(
        word	width,		/* suggested new width of view window */
        word	height);		/* suggested new height of view window */

This message is sent by the view to itself when it is first being initialized. It takes a suggested size and returns a new suggested size, adjusted for specific conditions.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: width The new suggested width of the view window.

height
The new suggested height of the view window.

Return: A dword representing both the height and the width determined by the method. The width occupies the low word, and the height occupies the high word.

Interception: If you require special sizing behavior from your GenView window, subclass this method. Be sure to call the superclass in the handler.

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Drawing the Document | Next: 4.5 Document Scaling


Document Scaling

GenView: 4.5 Advanced Concepts and Uses: Document Scaling

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Document and View Size | Next: 4.6 Children of the View
GVI_scaleFactor, MSG_GEN_VIEW_GET_SCALE_FACTOR, MSG_GEN_VIEW_SET_SCALE_FACTOR, MSG_GEN_VIEW_SCALE_LOW, ATTR_GEN_VIEW_PAGE_SIZE, ATTR_GEN_VIEW_SCALE_TO_FIT_BASED_ON_X, ATTR_GEN_VIEW_SCALE_TO_FIT_BOTH_DIMENSIONS

Many applications implement scaling to allow users to view and modify their documents at various scale factors. This scaling behavior, apart from the implementation of the view menu, is almost entirely internal to the view. The application simply needs to instruct the view in what scale factor to use; the view will provide the proper geometry and math involved in displaying, scrolling, and resizing your document.

Every view has a scale factor stored as a PointWWFixed structure in the GVI_scaleFactor instance field. The default scale factor is one, or 100 percent. A scale factor of two means that your document will appear twice as large to the user (200 percent normal size). You can set the scale factor in your Goc code and change it with MSG_GEN_VIEW_SET_SCALE_FACTOR .

The PointWWFixed structure contains two fields, each of which is a WWFixed structure. Both WWFixed and PointWWFixed are shown below.

    typedef struct {
    word		WWF_frac;		/* 16 bits decimal */
    word		WWF_int;		/* 16 bits integral */
} WWFixed;
typedef struct {
    WWFixed		PF_x;		/* x scale factor */
    WWFixed		PF_y;		/* y scale factor */
} PointWWFixed;

A scale factor of 2.5 (250%) in both dimensions would be declared as follows:

GVI_scaleFactor = {{5, 2}, {5, 2}};

A scale factor of 100% in the horizontal and 250% in the vertical would be declared as follows:

GVI_scaleFactor = {{0, 1}, {5, 2}};

To change the scale factor at run-time (for example, when a user clicks on a Zoom button), send MSG_GEN_VIEW_SET_SCALE_FACTOR to the view, passing the proper scale factors. Additionally, you may choose a point on the screen around which to scale. You may also retrieve the current scale factor of a view with the message MSG_GEN_VIEW_GET_SCALE_FACTOR . Note that the GenViewControl has many interactive scaling and zoom features and tools available.

Another way to ensure a certain point is on the screen after scaling is to force it there by causing the view to scroll after scaling. You can suspend the window update, scale the view, then cause the view to scroll with a MSG_GEN_VIEW_MAKE_RECT_VISIBLE . When you then unsuspend the window update, the view will draw with the proper point in the proper place. See Scrolling for an in-depth discussion of scrolling the view.

In addition to the dynamic scaling described above, you can have your view scale itself automatically to fit the entire document in its window. To do so, set the GVI_attrs flag GVA_SCALE_TO_FIT. This will cause the view to scale itself so the entire document fits vertically in the view; the horizontal scale factor will change to correspond with the new vertical scale factor. You can alter this behavior with three different vardata attributes:

ATTR_GEN_VIEW_PAGE_SIZE
This attribute takes an argument of type XYSize (shown below). The view will scale itself to fit this page size rather than its entire document size.
  typedef struct {
    word		XYS_width;		/* width of page */
    word		XYS_height;		/* height of page */
} XYSize;
ATTR_GEN_VIEW_SCALE_TO_FIT_BASED_ON_X
This attribute causes the view to scale itself based on the horizontal size rather than the vertical size. The vertical scale factor will follow the horizontal scale factor.
ATTR_GEN_VIEW_SCALE_TO_FIT_BOTH_DIMENSIONS
This attribute causes the view to scale both dimensions independently when scaling to fit. The horizontal and vertical scale factors may end up different.

MSG_GEN_VIEW_SET_SCALE_FACTOR

void	MSG_GEN_VIEW_SET_SCALE_FACTOR(@stack
        sdword	yOrigin,			/* New Y origin coordinate after scaling */
        sdword	xOrigin,			/* New X origin coordinate after scaling */
        ScaleViewType scaleType,				/* Determines Positioning after scaling */
        WWFixedAsDWord yScaleFactor,						/* New scale factor on Y axis */
        WWFixedAsDWord xScaleFactor);						/* New scale factor on X axis */

This message changes the view's scale factor, causing it to redraw at the new scale. The view will continue to display your document at the new scale until it is either shut down or instructed to change the scale factor again.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: yOrigin New vertical origin of the view window after scaling, if SVT_AROUND_POINT is passed in scaleType . Ignored otherwise.

xOrigin
New horizontal origin of the view window after scaling, if SVT_AROUND_POINT is passed in scaleType . Ignored otherwise.
scaleType
A value of ScaleViewType indicating how the view is to react after scaling. The three allowable values are described below, under "Structures."
yScaleFactor
The new vertical scale factor. To create a WWFixedAsDWord structure, use the macro MakeWWFixed() .
xScaleFactor
The new horizontal scale factor. To create a WWFixedAsDWord structure, use the macro MakeWWFixed() .

Return: Nothing.

Interception: Generally not intercepted. If you want to alter scaling behavior, you may intercept this message to change the parameters. Be sure to call the superclass at the end of the handler.

Structures: The type ScaleViewType defines the way the view will be scaled. It is an enumerated type with three possible values:

SVT_AROUND_UPPER_LEFT
Scale the view around the upper-left corner, and keep the origin the same before and after scaling. The xOrigin and yOrigin parameters will be ignored.
SVT_AROUND_CENTER
Scale the view around its center, keeping the point that is central in the view before scaling in the center after scaling. The xOrigin and yOrigin parameters will be ignored.
SVT_AROUND_POINT
Scale the view around the document point whose coordinates are provided in yOrigin and xOrigin , making sure the new coordinates are set as the view's new origin.

MSG_GEN_VIEW_GET_SCALE_FACTOR

void	MSG_GEN_VIEW_GET_SCALE_FACTOR(
        GetScaleParams *retValue);

This message returns the current scale factors of the view.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: retValue A pointer to an empty GetScaleParams structure to be filled in by the method.

Return: The structure pointed to by retValue will be filled with the current scale factor of the view.

Interception: Generally not intercepted.

Structures: The GetScaleParams structure is made up of two WWFixedAsDWord values. The high word of each represents the fractional (decimal) portion of the scale factor, and the low word represents the integral portion. The GetScaleParams structure definition is shown below:

typedef struct {
    WWFixedAsDWord			GSP_yScaleFactor;			/* y */
    WWFixedAsDWord			GSP_xScaleFactor;			/* x */
} GetScaleParams;

MSG_GEN_VIEW_SCALE_LOW

void	MSG_GEN_VIEW_SCALE_LOW(@stack
        sdword		yOrigin,
        sdword		xOrigin,
        ScaleViewType		scaleType,
        WWFixedAsDWord		yScaleFactor,
        WWFixedAsDWord		xScaleFactor);

This low-level message is similar to MSG_GEN_VIEW_SET_SCALE_FACTOR except that it does not propagate to linked views. It is generally encapsulated by the GenView when another scaling message is received, then dispatched to linked views later.

Source: Sent by the GenView to itself.

Destination: Any GenView object.

Parameters: See MSG_GEN_VIEW_SET_SET_SCALE_FACTOR .

Return: Nothing.

Interception: Should not be intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Document and View Size | Next: 4.6 Children of the View


Children of the View

GenView: 4.6 Advanced Concepts and Uses: Children of the View

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Document Scaling | Next: 4.7 Scrolling
HINT_SEEK_X_SCROLLER_AREA, HINT_SEEK_Y_SCROLLER_AREA, HINT_SEEK_LEFT_OF_VIEW, HINT_SEEK_RIGHT_OF_VIEW, HINT_SEEK_TOP_OF_VIEW, HINT_SEEK_BOTTOM_OF_VIEW

Children of GenView objects always appear at a specified edge of the view; you may decide which children appear on the top, left, right, or bottom, but all children must appear along one of the view's edges. Additionally, you may request that the children be placed next to the view's scrollers; for example, if you wanted a page number to appear at the foot of the view, you might define a GenGlyph that seeks the horizontal scroller.

Each child's placement is determined independently of the others. Each child, therefore, must have a hint that describes where in relation to the view it would like to appear. If the requested configuration is supported by the specific UI, the object will appear where requested. If the specific UI does not support the desired configuration, the child will likely appear below the view. The view may have more than one child. If no hint of location is given for any child, that child will appear with the horizontal scroller.

The location hints (defined by GenClass ) available to a GenView's children include

  • HINT_SEEK_X_SCROLLER_AREA
     The object should be placed with the horizontal scroller object.
  • HINT_SEEK_Y_SCROLLER_AREA
     The object should be placed with the vertical scroller object.
  • HINT_SEEK_LEFT_OF_VIEW
     The object should be forced to the left side of the view.
  • HINT_SEEK_RIGHT_OF_VIEW
     The object should be forced to the right side of the view.
  • HINT_SEEK_TOP_OF_VIEW
     The object should be forced to the top edge of the view.
  • HINT_SEEK_BOTTOM_OF_VIEW
     The object should be forced to the bottom edge of the view.

You may also specify your own scroller objects anywhere below the view in the generic object tree. This is described in the section on Scrolling, below.

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Document Scaling | Next: 4.7 Scrolling


Scrolling

GenView: 4.7 Advanced Concepts and Uses: Scrolling

| Up: GEOS SDK TechDocs | Up | Prev: 4.6 Children of the View | Next: 4.8 Monitoring Input

When a document is larger than the view window, the user must have some means of navigation around it. The most common implementation of this is scrolling. In most situations, scrolling will be automatic when you use a view. However, you can modify the view's scrolling behavior.

Removing the Scrollbars

HINT_VIEW_REMOVE_SCROLLERS_WHEN_NOT_SCROLLABLE, HINT_VIEW_SHOW_SCROLLERS_WHEN_NOT_SCROLLABLE

You can determine when scrollbars will be visible on your GVDA_SCROLLABLE view in several ways. One is to set the GVDA_DONT_DISPLAY_SCROLLBAR attribute, either dynamically or in your .goc file, to force a scrollbar not to be displayed. Another is to allow the user to use the GenViewControl to turn the scrollbars on or off.

A third is to allow the GenView to update itself dynamically, by using the hints HINT_VIEW_REMOVE_SCROLLERS_WHEN_NOT_SCROLLABLE and HINT_VIEW_SHOW_SCROLLERS_WHEN_NOT_SCROLLABLE .

The first causes the view to remove the scroller objects when scrolling is not needed (i.e. when the entire content or document fits inside the view's window). The second causes the view to show scrollers in all cases, as long as the view is GVDA_SCROLLABLE and GVDA_DONT_DISPLAY_SCROLLBAR is not set.

Making the View Scrollable

Views, by default, are not scrollable. To make your view scrollable, set the GVDA_SCROLLABLE attribute in both the GVI_horizAttrs and GVI_vertAttrs records. It is easiest to do this in your Goc code, as follows:

GVI_horizAttrs = @default | GVDA_SCROLLABLE;
GVI_vertAttrs = @default | GVDA_SCROLLABLE;

You can also set these attributes during execution by sending the message MSG_GEN_VIEW_SET_DIMENSION_ATTRS to the view. See Dimensional Attributes .

Normal Scrolling

There are three basic ways scrolling can be initiated: First, the user can click on a special scroller object. Second, the user can initiate "drag scrolling" by clicking within the view and dragging outside the window's bounds. Third, the view's content object can request a scrolling action. Only the first of these three is activated by default.

When a scrollable view is first created, scroller objects are also instantiated. This is automatic and for the most part transparent to the geode. The scroller objects may be customized or placed in a certain spot in the generic tree: to learn how to do this, see Customizing the Scrollers .

Scrollers are generic by nature so they can conform to the specific UI in use; in OSF/Motif, for example, scrollers are manifested as scrollbars that position themselves at the right and bottom sides of the view. However, other specific UIs might implement dials, sliders, or some other gadgets for scrolling.

In normal scrolling, the user provides input to a scroller (for example, clicking on the up-arrow of a vertical scrollbar in OSF/Motif). The scroller then sends a message to the view indicating that a scroll should take place. The view calculates the new portion of the document and sends a MSG_META_EXPOSED to its content to indicate that the document must be redrawn. The content then draws its document to the view's window handle, and the view takes care of clipping the document and translating it to the screen.

To take advantage of the normal scrolling features, therefore, you only have to make the view scrollable and respond to MSG_META_EXPOSED .

Drag Scrolling

MSG_GEN_VIEW_INITIATE_DRAG_SCROLL, MSG_GEN_VIEW_SET_DRAG_BOUNDS, HINT_VIEW_IMMEDIATE_DRAG_UPDATES, HINT_VIEW_DELAYED_DRAG_UPDATES

In many cases, applications will want to supplement normal scrolling with drag scrolling, in which a user clicks within the view and drags beyond its edge, causing the view to scroll in that direction. Also, some applications may wish to implement scrolling without the use of scrollers--drag scrolling provides a good way of doing this.

Two hints affect how drag scrolling is implemented. HINT_VIEW_IMMEDIATE_DRAG_UPDATES causes the view to be updated constantly during drags. HINT_VIEW_DELAYED_DRAG_UPDATES causes the view to be updated only at the end of drags. The default behavior is to update periodically during the drag in order to provide visual feedback to the user and to avoid excessive overhead in drawing repeatedly.

To implement drag scrolling, your view must be scrollable. It also must have the GVA_DRAG_SCROLLING attribute of the GVI_attrs record set. You can do this in Goc as follows:

GVI_horizAttrs = @default | GVDA_SCROLLABLE;
GVI_vertAttrs = @default | GVDA_SCROLLABLE;
GVI_attrs = @default | GVA_DRAG_SCROLLING;

You can also set the drag scrolling attribute by sending the view the message MSG_GEN_VIEW_SET_ATTRS (see The GVI_attrs Attribute ).

Normally, drag scrolling works only for the select button (in OSF/Motif, the left mouse button). MSG_GEN_VIEW_INITIATE_DRAG_SCROLL allows you to change this. For example, if you received a MSG_META_START_MOVE_COPY (indicating that the user used the direct-action button), you might want to initiate drag scrolling if your application does not support quick-transfer.

Additionally, if you wanted drag scrolling to work only in a portion of your document (for example, if you wanted the user to be able to move an object on the screen but only move it so far), you could set the temporary drag bounds directly after initiating the drag scrolling. This is done by sending the view a MSG_GEN_VIEW_SET_DRAG_BOUNDS .

MSG_GEN_VIEW_INITIATE_DRAG_SCROLL

void	MSG_GEN_VIEW_INITIATE_DRAG_SCROLL();

This message instructs the view to begin drag scrolling until the pressed mouse button is let go by the user. When drag scrolling is enabled, the select button initiates it; this message allows initiation by any mouse button.

Source: Unrestricted--called by an application on a MSG_META_START_ ... mouse event.

Destination: The GenView in which the mouse button was clicked.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SET_DRAG_BOUNDS

void	MSG_GEN_VIEW_SET_DRAG_BOUNDS(@stack
        sdword	bottom,	/* bottom scrolling bound */
        sdword	right,	/* right scrolling bound */
        sdword	top,	/* top scrolling bound */
        sdword	left);	/* left scrolling bound */

This message sets a temporary rectangle in which drag scrolling can operate if the user should not drag scroll across the entire document.

Source: Unrestricted--called by an application on the initiation of drag scrolling.

Destination: The GenView in which the drag scrolling is underway.

Parameters: bottom , right , top , left
 The bounds (in document coordinates) of the rectangle limiting scrolling. These temporary bounds will be used only during the current drag scroll. The restriction will then be lifted.

Return: Nothing.

Interception: Generally not intercepted.

Modifying Scrolling

Many applications may find a need to scroll the view without user input or to modify the scrolling behavior of the view. For example, if your application has a trigger or menu item that brings a certain portion of the document onto the screen, you will need to instruct the view to scroll to that point, or perhaps your application must scroll by different increments depending on the situation.

Scrolling from the Content

MSG_GEN_VIEW_SCROLL_...

All typical messages sent by scrollers to the view are also available for sending by applications and other geodes. These messages are listed below. Note: With the exception of MSG_GEN_VIEW_SCROLL , these messages work only when the view is visibly initialized. This means that a view that is not on the screen will ignore these messages.

MSG_GEN_VIEW_SCROLL

void	MSG_GEN_VIEW_SCROLL(@stack
        sdword	yOffset,		/* amount to scroll vertically */
        sdword	xOffset);		/* amount to scroll horizontally */

This message causes the view to scroll a given amount in both the X and Y directions.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: yOffset The signed vertical amount to scroll.

xOffset
The signed horizontal amount to scroll.

Return: Nothing.

Interception: Generally not intercepted. If you want to alter scrolling behavior, see Tracking the Scrolling .

MSG_GEN_VIEW_SCROLL_TOP

void	MSG_GEN_VIEW_SCROLL_TOP();

Causes the view to scroll to the top of the document.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_PAGE_UP

void	MSG_GEN_VIEW_SCROLL_PAGE_UP();

Causes the view to scroll up one window height.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_UP

void	MSG_GEN_VIEW_SCROLL_UP();

Causes the view to scroll up one increment.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_SET_Y_ORIGIN

void	MSG_GEN_VIEW_SCROLL_SET_Y_ORIGIN(
        sdword	yOrigin);

Causes the view to scroll to a given vertical point and sets the Y component of the origin to the passed value. It keeps the X component of the origin fixed.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_DOWN

void	MSG_GEN_VIEW_SCROLL_DOWN();

Causes the view to scroll down one increment.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_PAGE_DOWN

void	MSG_GEN_VIEW_SCROLL_PAGE_DOWN();

Causes the view to scroll down one window height.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_BOTTOM

void	MSG_GEN_VIEW_SCROLL_BOTTOM();

Causes the view to scroll to the bottom of the document.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_LEFT_EDGE

void	MSG_GEN_VIEW_SCROLL_LEFT_EDGE();

Causes the view to scroll to the document's left edge.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_PAGE_LEFT

void	MSG_GEN_VIEW_SCROLL_PAGE_LEFT();

Causes the view to scroll left one window width.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_LEFT

void	MSG_GEN_VIEW_SCROLL_LEFT();

Causes the view to scroll left one increment.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_SET_X_ORIGIN

void	MSG_GEN_VIEW_SCROLL_SET_X_ORIGIN(
        sdword	xOrigin);

Causes the view to scroll to a given horizontal point and sets the X component of the origin to the passed value. It keeps the Y component of the origin fixed.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_RIGHT

void	MSG_GEN_VIEW_SCROLL_RIGHT();

Causes the view to scroll right one increment.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_PAGE_RIGHT

void	MSG_GEN_VIEW_SCROLL_PAGE_RIGHT();

Causes the view to scroll right one window width.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_RIGHT_EDGE

void	MSG_GEN_VIEW_SCROLL_RIGHT_EDGE();

Causes the view to scroll to the document's right edge.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

Making a Rectangle Visible

MSG_GEN_VIEW_MAKE_RECT_VISIBLE

You can easily make any portion of your document visible with the passage of a single message. For example, if you were displaying a map of the United States and the user clicked on a menu item to show the area around Cincinnati, you could easily determine the coordinates of Cincinnati and instruct the view to scroll there.

To make a certain rectangle of the document visible in the view, pass the message MSG_GEN_VIEW_MAKE_RECT_VISIBLE to the view. You can include a percentage to indicate how far through the rectangle the view should scroll--zero percent causing the rectangle to appear just at the near edge of the view and 100 percent causing the rectangle to appear just at the far edge. Fifty percent causes the object to be centered in the view window.

Normally, this message will not cause any scrolling if the rectangle is already on the screen. However, you can indicate that this message should always cause scrolling (useful if you want to center a rectangle that is already partially on the screen).

MSG_GEN_VIEW_MAKE_RECT_VISIBLE

void	MSG_GEN_VIEW_MAKE_RECT_VISIBLE(@stack
        word	yFlags,	/* MakeRectVisibleFlags for y dimension */
        word	yMargin,	/* percentage to scroll onto screen in y */
        word	xFlags,	/* MakeRectVisibleFlags for x dimension */
        word	xMargin,	/* percentage to scroll onto screen in x */
        sdword	bottom,	/* bottom bound of the target rectangle */
        sdword	right,	/* right bound of the target rectangle */
        sdword	top,	/* top bound of the target rectangle */
        sdword	left);	/* left bound of the target rectangle */

This message causes the view to scroll until a given portion of a passed rectangle is visible in the display window. If the rectangle is already partially visible, the view will not scroll unless instructed to do so by the flags arguments. If the view is not visibly initialized, this message will have no effect.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: yFlags A record of flags indicating when and how scrolling occurs. See below.

yMargin
The amount of the rectangle to be scrolled onto the screen in the vertical dimension. This is a percentage of the screen height and is based from the edge of the screen closest to the rectangle. For example, a yMargin of 50% centers the rectangle vertically; a yMargin of 100% puts the rectangle at the far edge of the screen from where it started. The percentages available are shown below.
xFlags
A record of flags indicating when and how scrolling occurs. See below.
xMargin
The amount of the rectangle to be scrolled onto the screen in the horizontal dimension. This is a percentage of the screen width and is based from the edge of the screen closest to the rectangle. For example, an xMargin of 50% centers the rectangle horizontally; an xMargin of 100% puts the rectangle at the far edge of the screen from where it started. The percentages available are shown below.
bottom , right , top , left
The bounds of the rectangle to be made visible. These should be in document coordinates.

Return: Nothing.

Structures: The yFlags and xFlags parameters are of type MakeRectVisibleFlags ; this type has the following values:

MRVF_ALWAYS_SCROLL
This flag indicates that the view should scroll even when the rectangle is already partially visible.
MRVF_USE_MARGIN_FROM_TOP_LEFT
This flag indicates that margin percentages should be taken relative to the top and left edges of the view rather than the edges closest to the defined rectangle.

The percentages passed in yMargin and xMargin have a special format. You can use a predefined constant, or you can pass custom percentage. To use your own percentage, multiply the decimal representing the desired percentage (e.g. ".50" represents 50 percent) by the hexadecimal number 0xffff; or, instead of doing that math, you can use one of the following system-provided constants:

MRVM_0_PERCENT
MRVM_25_PERCENT
MRVM_50_PERCENT
MRVM_75_PERCENT
MRVM_100_PERCENT

Interception: Generally not intercepted.

Handling Complex Scrolling Operations

MSG_GEN_VIEW_SUSPEND_UPDATE, MSG_GEN_VIEW_UNSUSPEND_UPDATE

If you cause the view to go through several scrolling operations in rapid order, you may want to suspend the view from sending a MSG_META_EXPOSED after each--it is much better to wait until all the updates have been made before the redrawing occurs.

To suspend a view's updates temporarily, send it the message MSG_GEN_VIEW_SUSPEND_UPDATE . After you have finished sending the scrolling messages, unsuspend the view's updates by sending MSG_GEN_VIEW_UNSUSPEND_UPDATE .

MSG_GEN_VIEW_SUSPEND_UPDATE

void	MSG_GEN_VIEW_SUSPEND_UPDATE();

This message causes the view to suspend sending MSG_META_EXPOSED until it receives MSG_GEN_VIEW_UNSUSPEND_UPDATE . Suspend-unsuspend pairs can be nested.

Source: Unrestricted.

Destination: Any GenView object.

Interception: Generally not intercepted.

MSG_GEN_VIEW_UNSUSPEND_UPDATE

void	MSG_GEN_VIEW_UNSUSPEND_UPDATE();

This message allows the view to send a MSG_META_EXPOSED again after being suspended with MSG_GEN_VIEW_SUSPEND_UPDATE .

Source: Unrestricted.

Destination: Any GenView object.

Interception: Generally not intercepted.

Warnings: It is an error to send this message to a non-suspended view.

Tracking the Scrolling

MSG_META_CONTENT_TRACK_SCROLLING, MSG_GEN_VIEW_SETUP_TRACKING_ARGS, MSG_GEN_VIEW_TRACKING_COMPLETE

For flexibility, the view allows applications to track scrolling as it happens and alter it before it gets implemented. When GVA_TRACK_SCROLLING is set in GVI_attrs , all scrolling events will be sent to the content object before being implemented. Because there are often many scrolling events, you should avoid track scrolling unless truly necessary.

If you are going to track the scrolling, your content object must handle the message MSG_META_CONTENT_TRACK_SCROLLING . This message passes a TrackScrollingParams structure containing all the information needed about the upcoming scrolling event. When you receive it, you must do the following three steps:

  1. Send MSG_GEN_VIEW_SETUP_TRACKING_ARGS to the view
    This message fills in all the information of the TrackScrollingParams structure. You must call this routine to ensure that there are no synchronization problems with the scrolling event.
  2. Alter the scrolling event
    If you choose to alter the scrolling event, change the TrackScrollingParams structure to the appropriate values.
  3. Send MSG_GEN_VIEW_TRACKING_COMPLETE to the view
    This message updates the TrackScrollingParams structure based on your changes, then sends a message to the view with the new scrolling information. The view will then scroll according to your changes.

The TrackScrollingParams structure contains complete information about the scrolling event including context information, the view's width and height, the view's document origin, and the action in progress. It also contains a field called TSP_change , which is a structure of type PointDWord and should, after you change the scrolling, contain an offset from the proposed origin to the new origin you desire. See TrackScrollingParams and Associated Structures for the definition of the structure.

Code Display 9-4 TrackScrollingParams and Associated Structures

/*
 * The TrackScrollingParams structure contains all the necessary information about
 * a given scrolling event. It is made up of several substructures, each of which
 * is shown below.
 */
  /*
 * ScrollAction is an enumerated type, each enumeration of which designates a
 * different type of event. The handler should not change these.
 */
typedef ByteEnum ScrollAction;				/* byte-length enumeration */
/*  SA_NOTHING			* No scrolling action
    SA_TO_BEGINNING			* Scrolling to beginning of document
    SA_PAGE_BACK			* Scrolling back one screen height or width
    SA_INC_BACK			* Scrolling back one increment
    SA_INC_FWD			* Scrolling forward one increment
    SA_DRAGGING			* Drag scrolling is underway
    SA_PAGE_FWD			* Scrolling forward one page
    SA_TO_END			* Scrolling to end of document
    SA_SCROLL			* Generic scrolling message sent
    SA_SCROLL_INTO			* Scrolling while keeping a point on screen
    SA_INITIAL_POS			* Indicating the initial scrolling position.
                * Subsequent scroll messages will be relative to
                * this origin.
    SA_SCALE			* Scaling may cause scrolling
    SA_PAN			* Pan-scrolling is underway. Otherwise
                * identical to SA_SCROLL
    SA_DRAG_SCROLL			* Drag-scrolling, otherwise like SA_SCROLL
    SA_SCROLL_FOR_SIZE_CHANGE				* Scrolling because view size changed
*/
  /*
 * ScrollFlags is a byte record of flags used to determine the type of scrolling
 * taking place and the context of the scroll.
 */
typedef ByteFlags ScrollFlags;
#define	SF_VERTICAL		0x80	/* Scrolling is vertical if set. If clear,
                 * scrolling is horizontal. Invalid for
                 * ScrollAction types SA_SCROLL_INTO,
                 * SA_SCROLL, and SA_INITIAL_POS. */
#define	SF_ABSOLUTE		0x40	/* Scrolling is to an absolute point. Set for
                 * ScrollAction types SA_TO_BEGINNING,
                 * SA_TO_END, SA_INITIAL_POS, SA_SCROLL_INTO,
                 * SA_DRAGGING, and some SA_SCROLL. */
#define	SF_DOC_SIZE_CHANGE 0x20			/* Scroll resulted from document size change. */
#define	SF_WINDOW_NOT_SUSPENDED 0x10
                /* Flag used internally only. */
#define	SF_SCALE_TO_FIT		0x08	/* Flag used when the View is in scale-to-fit
                 * mode (often changes scrolling behavior). */
#define	SF_SETUP_HAPPENED  0x04			/* Flag for error checking only. */
/*
 * The TrackScrollingParams structure contains several elements, each of
 * which is described in the comments below. All the fields will be filled by
 * MSG_GEN_VIEW_SETUP_TRACKING_ARGS; however, only certain fields will be filled
 * by MSG_META_CONTENT_TRACK_SCROLLING. Therefore, you should always send
 * MSG_GEN_VIEW_SETUP_TRACKING_ARGS in the handler for this message.
 */
 typedef struct {
    ScrollAction		TSP_action;		/* The type of scrolling underway */
    ScrollFlags		TSP_flags;		/* flags shown above */
    optr		TSP_caller;		/* The sender of the scroll message */
    PointDWord		TSP_change;		/* The relative amount being scrolled */
    PointDWord		TSP_newOrigin;		/* The new absolute origin */
    PointDWord		TSP_oldOrigin;		/* The original origin */
    sword		TSP_viewWidth;		/* Current view width */
    sword		TSP_viewHeight;		/* Current view height */
} TrackScrollingParams;

To set TSP_change , you must first understand how the entire TrackScrollingParams structure is used. When a scrolling event is being tracked, three sets of information are needed:

  • The current origin and size of the view. This is represented by the field TSP_oldOrigin .
  • The scrolling event's proposed change to the view's origin. This is represented by the field TSP_newOrigin .
  • The final change to the view's origin after your adjustments have been added. This is represented by a combination of TSP_oldOrigin and TSP_change . You should set TSP_change understanding that it gets added to TSP_oldOrigin to produce the final origin of the view.

Tracking the scrolling, however, can introduce noticeable lag in scrolling speed. If you do not need to track the scrolling, you probably shouldn't. However, if you need to track the scrolling for simple or few changes, you should consider subclassing GenViewClass and altering the functionality of the method for MSG_META_CONTENT_TRACK_SCROLLING . The GenView's handler for this message simply passes the message on to the content.

If you just need to track changes to the origin, subclass GenViewClass and intercept MSG_META_CONTENT_VIEW_ORIGIN_CHANGED .

Code Display 9-5 Handling Track Scrolling

/* This message is sent by the view to its content object when track scrolling is
 * enabled and a scrolling event is begun. If you plan to track the scrolling, you
 * MUST handle this message. The first thing your handler should do is send
 * MSG_GEN_VIEW_SETUP_TRACKING_ARGS to the view. This message fills in the
 * TrackScrollingParams structure. The last thing the handler must do is send
 * MSG_GEN_VIEW_TRACKING_COMPLETE to the view. This message locks your changes
 * in and directs the view to implement them.
 * The format of this message is
 *	void (TrackScrollingParams *args);								*/
@message		MyProcessClass, MSG_META_CONTENT_TRACK_SCROLLING {
    @call (args->TSP_caller)::MSG_GEN_VIEW_SETUP_TRACKING_ARGS(args);
    /* Here you can do whatever you want to args->TSP_change. */
    @call MyView::MSG_GEN_VIEW_TRACKING_COMPLETE(&args);
}

MSG_GEN_VIEW_SETUP_TRACKING_ARGS

void	MSG_GEN_VIEW_SETUP_TRACKING_ARGS(
        TrackScrollingParams *args);

This message takes the TrackScrollingParams structure and fills in remaining fields not passed by MSG_META_CONTENT_TRACK_SCROLLING .

Source: The content object tracking scrolling (in the handler for MSG_META_CONTENT_TRACK_SCROLLING ).

Destination: The calling object, found in args->TSP_caller .

Parameters: args A pointer to the mostly complete structure of TrackScrollingParams , to be filled in by the method.

Return: The structure pointed to by args will be filled.

Structures: See TrackScrollingParams and Associated Structures .

Interception: Generally not intercepted.

MSG_GEN_VIEW_TRACKING_COMPLETE

void	MSG_GEN_VIEW_TRACKING_COMPLETE(
        TrackScrollingParams *args);

This message sets the altered scrolling arguments and makes them official, instructing the view to implement them.

Source: The content object tracking scrolling (in its handler for MSG_META_CONTENT_TRACK_SCROLLING ).

Destination: The calling object, found in args->TSP_caller .

Parameters: args A pointer to the completed structure of TrackScrollingParams .

Return: Nothing.

Structures: See TrackScrollingParams and Associated Structures .

Interception: Generally not intercepted.

MSG_META_CONTENT_TRACK_SCROLLING

void	MSG_META_CONTENT_TRACK_SCROLLING(
        TrackScrollingParams *args);

This message is sent by the GenView to its content if the content has requested track-scrolling (with GVA_TRACK_SCROLLING set in the GenView's instance data).

Source: Sent by a GenView object to its content when a scrolling event occurs and the view has GVA_TRACK_SCROLLING set.

Destination: The content of the GenView.

Parameters: args A pointer to a TrackScrollingParams structure.

Return: Nothing.

Structures: See TrackScrollingParams and Associated Structures .

Interception: Any content that tracks scrolling must handle this message. It should send MSG_GEN_VIEW_SETUP_TRACKING_ARGS and MSG_GEN_VIEW_TRACKING_COMPLETE to the object specified in TSP_caller .

Customizing the Scrollers

HINT_VALUE_X_SCROLLER, HINT_VALUE_Y_SCROLLER

Although a view's scrollers should remain generic to maintain UI consistency, you may set your own GenValue objects to be the scrollers of your view. This allows special placement of the scrollers not only around the view but also within the generic object tree.

Before creating its own scrollers, the view will search for any objects below it in the generic object tree that have the hint HINT_VALUE_X_SROLLER or HINT_VALUE_Y_SCROLLER . For example, if you wanted to put your vertical scroller on the left of the view rather than on the right, you could add the following code:

@object GenViewClass MyView = {
    /* other view attributes */
    GI_comp = MyViewScroller;
}
@object GenRangeClass MyViewScroller = {
    HINT_Y_SCROLLER;
    HINT_SEEK_LEFT_OF_VIEW;
}

Custom scrollers do not need to be direct children of the view; instead, they may be any number of generations below the view in the generic object tree.

| Up: GEOS SDK TechDocs | Up | Prev: 4.6 Children of the View | Next: 4.8 Monitoring Input


Monitoring Input

GenView: 4.8 Advanced Concepts and Uses: Monitoring Input

| Up: GEOS SDK TechDocs | Up | Prev: 4.7 Scrolling | Next: 4.9 Linking Views

Often, applications will want to receive input--keyboard, mouse, or pen events--through the view. While keyboard events are fairly straightforward, receiving mouse and pen events can be somewhat more complicated. Depending on the types of events you choose to receive, however, input handling can be quite simple.

Mouse Events

Many applications will want to receive mouse events through the view. By default, the view's content will receive all pointer events, whether or not a mouse button has been pressed. However, you can determine which types of events you want to receive by setting the appropriate attributes in the GVI_attrs field:

  • GVA_DONT_SEND_PTR_EVENTS
    This overrides the default and ensures that no mouse (pointer) events are passed on to the content. This is used for optimization when your content will not interact with the user directly.
  • GVA_WINDOW_COORDINATE_MOUSE_EVENTS
    This changes the format of the mouse event as received by the content object and is used with large documents. See below for more information about handling input to large documents.

Both of the above attributes may be set either in your geode's Goc code or with MSG_GEN_VIEW_SET_ATTRS . To retrieve which attributes are set, use MSG_GEN_VIEW_GET_ATTRS . Due to overhead incurred by passing input events, you should only set the attributes you need. (See The GVI_attrs Attribute for more detail on these messages.)

Handling 32-Bit Input Events

Because normal pointer events use the standard GEOS 16-bit coordinate system, large documents must be able to translate these 16-bit events into 32-bit coordinates. The GenView makes this easy for you.

First, set the attribute GVA_WINDOW_COORDINATE_MOUSE_EVENTS in the GVI_attrs record. This will change the format of pointer data passed from the view to the content object. The new format will contain offset coordinates describing the pointer's position relative to the view's screen origin (in screen coordinates).

When you receive one of these events (called "large" mouse events), you can apply the simple formula shown in the figure below to get 32-bit document coordinates. Once you have the coordinates of the pointer event, you can deal with it appropriately.

To keep track of the view's origin and scale factor, you should intercept the messages MSG_META_CONTENT_VIEW_ORIGIN_CHANGED and MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED

Losing Mouse Grabs

During certain operations--for example, quick-transfers--your geode will have the mouse grab. When it loses the grab, it will be notified with a MSG_META_CONTENT_LOST_GADGET_EXCLUSIVE .

Supporting Quick-Transfer

MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER

When using a view and supporting the quick-transfer mechanism, the view will grab the mouse when a quick-transfer operation is invoked. Conclusion of the quick-transfer operation, requires, however, that other objects know when the mouse crosses their bounds; otherwise, no other object in the system could ever become a destination for the transfer.

To relinquish the mouse grab while continuing the transfer operation, you must send MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER to the view. The view will then let other objects get pointer events, thus letting them know when they become a potential destination for the transfer.

MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER

void	MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER();

This message indicates to the view that it should relinquish the mouse grab to allow a quick-transfer operation to continue. Pointer events will then be sent to other objects in the system, allowing them to accept or reject the transfer.

Source: The object with the mouse grab in the view.

Destination: The GenView displaying the caller object.

Interception: Generally not intercepted.

Keyboard Events

By default, all keyboard events not handled by the Specific UI library are passed through to the content when the view has the keyboard focus. Only views with the GVA_FOCUSABLE flags set in GVI_attrs will ever gain the focus (this is the default). Many character strokes are used for keyboard navigation or as mnemonics or accelerators and will therefore be intercepted by the Specific UI. Those that aren't will be passed on to your content.

You can set up your content to receive all keyboard events before the Specific UI has a chance to intercept them. This can have unpredictable results, however, and should be avoided unless absolutely necessary. To set up your content in this manner, set the flag GVA_SEND_ALL_KBD_CHARS in the view's GVI_attrs field. If you handle keyboard events before passing them on, you must be sure to pass every unused MSG_META_KBD_CHAR on to the UI in the form of MSG_META_VUP_KBD_CHAR . If you do not, undesirable synchronization problems could result.

You can indicate that you do not want to receive keyboard releases by setting the attribute GVA_DONT_SEND_KBD_RELEASES in the GVI_attrs record. This is provided as an optimization to improve keyboard response in applications. Most applications need to know when a key has been pressed but do not care when it has been released.

A better way to handle accelerator characters that don't appear in your UI objects is to subclass GenViewClass and intercept MSG_META_KBD_CHAR ; in the handler, take the characters you want and then call the superclass with @callsuper() to make sure unused events get passed on. As an alternative, you can subclass GenApplicationClass and change the handler for MSG_META_VUP_KBD_CHAR ; in the method, take the events you want and then call the superclass with @callsuper() .

Pen Input and Ink

GVI_inkType, MSG_GEN_VIEW_SET_INK_TYPE, MSG_GEN_VIEW_SET_EXTENDED_INK_TYPE, MSG_GEN_VIEW_RESET_EXTENDED_INK_TYPE, ATTR_GEN_VIEW_DOES_NOT_ACCEPT_TEXT_INPUT

It is suggested that you read the Input chapter, before working with Ink in the view. The GenView accepts Ink input events and can pass them to the content either as ink or as mouse events. How the view determines which type of events are desired is mandated by the value of the instance field GVI_inkType .

In addition, some views that get the focus do not accept text input. By default on many pen systems, focusable GenViews will cause an on-screen keyboard to come up on the assumption that the user may wish to enter text to objects within the view. ATTR_GEN_VIEW_DOES_NOT_ACCEPT_TEXT_INPUT allows you to specify that your view does not accept text input and therefore should not cause a floating keyboard to come up. Non-textual keyboard events (e.g. up-arrow used for scrolling) will still be transmitted to the GenView.

The GVI_inkType field will contain one of the following values (each is an enumeration of GenViewInkType ):

GVIT_PRESSES_ARE_NOT_INK
Mouse press events are not Ink input, and Ink input is not expected by the content. This is the default for views.
GVIT_INK_WITH_STANDARD_OVERRIDE
Mouse press events are considered and expected to be Ink events, but they can be overridden by the user in the normal way.
GVIT_PRESSES_ARE_INK
Mouse press events are considered and expected to be Ink.
GVIT_QUERY_OUTPUT
This value indicates that the content will expect Ink events only in certain contexts. Note that if this attribute is set, the content must handle the MSG_META_QUERY_IF_PRESS_IS_INK message.

The GVI_inkType field may be set either in your Goc code or with the message MSG_GEN_VIEW_SET_INK_TYPE , shown below.

Applications that handle Ink input can also set extended information about the Ink. The type of extended information you can set includes the color of the Ink as it's drawn, the thickness of the Ink brush, etc. Use the messages MSG_GEN_VIEW_SET_EXTENDED_INK_TYPE and MSG_GEN_VIEW_RESET_EXTENDED_INK_TYPE , shown below.

You can also pre-set the extended Ink information with the vardata attribute ATTR_GEN_VIEW_INK_DESTINATION_INFO . This attribute has an extra data structure of type InkDestinationInfoParams , shown below.

typedef struct {
    optr		IDIP_dest;
                /* destination object
                 * for Ink output. */
    word		IDIP_brushSize;
                /* Size of Ink brush.
                 * High byte is x size,
                 * Low byte is y size. */
    byte		IDIP_color;		/* Color of Ink */
    Boolean		IDIP_createGState;
                    /* Boolean indicating
                     * if the Ink should
                     * have its own
                     * GState. */
} InkDestinationInfoParams;

MSG_GEN_VIEW_SET_INK_TYPE

void	MSG_GEN_VIEW_SET_INK_TYPE(
        GenViewInkType inkType);				/* value of GenViewInkType */

This message sets the type of Ink input expected by the view's content.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: inkType The type of Ink input expected.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SET_EXTENDED_INK_TYPE

void	MSG_GEN_VIEW_SET_EXTENDED_INK_TYPE(@stack
        Boolean createGState,
        Color	inkColor,
        word	brushSize,
        optr	destObj);

This message sets the extended Ink type for the GenView. The extended Ink type is stored in the vardata ATTR_GEN_VIEW_INK_DESTINATION_INFO .

Source: Unrestricted.

Destination: Any GenView object.

Parameters: createGState A boolean specifying whether the view should create a new GState for the Ink or not.

inkColor
A palette index to use as the color of the Ink.
brushSize
The brush size, in points, to be used for the Ink.
destObj
The optr of the destination object for Ink input to this view. After the Ink is collected, it will be sent to this object.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_RESET_EXTENDED_INK_TYPE

void	MSG_GEN_VIEW_RESET_EXTENDED_INK_TYPE();

This message resets the extended Ink type to default values.

Source: Unrestricted.

Destination: Any GenView object.

Interception: Generally not intercepted.

Target and Focus

MSG_GEN_VIEW_UPDATE_CONTENT_TARGET_INFO

Some applications may draw some portion of their documents differently if they have the target or focus. The view will pass along the following target/focus messages: MSG_META_CONTENT_ENTER , MSG_META_CONTENT_LEAVE , MSG_META_RAW_UNIV_ENTER , MSG_META_RAW_UNIV_LEAVE , and MSG_META_CONTENT_VIEW_LOST_GADGET_EXCLUSIVE .

If you want your geode to be able to grab the focus, you must set the GVA_FOCUSABLE attribute in the GVI_attrs record.

MSG_GEN_VIEW_UPDATE_CONTENT_TARGET_INFO

void	MSG_GEN_VIEW_UPDATE_CONTENT_TARGET_INFO(
        ViewTargetInfo *targetInfo);

This message is sent to the view from VisContentClass contents whenever target information within the view changes. This is done so that MSG_META_GET_TARGET_EXCL may be used to retrieve target information about the portion of the target hierarchy which is within the content itself. (The UI can't directly call objects running in other threads, so this message is required to update it with the latest information.)

Source: A VisContent within the view.

Destination: The GenView associated with the caller.

Parameters: targetInfo A pointer to a ViewTargetInfo structure, shown below.

Return: Nothing.

Structures: The ViewTargetInfo structure has two fields, as shown:

 typedef struct {
     optr		TR_object;		/* optr of object */
    ClassStruct		*TR_class;		/* class of object */
} TargetReference;
 typedef struct {
     TargetReference			VTI_target;
    TargetReference			VTI_content;
} ViewTargetInfo;

The VTI_target field contains the optr and class pointer of the object in the view that currently has the target exclusive. The VTI_content field contains the optr and class pointer of the view's content object.

Interception: Generally not intercepted.

Setting the Pointer Image

MSG_GEN_VIEW_SET_PTR_IMAGE

If you want to change the pointer's image while it is over your view or while the view has the focus, use MSG_GEN_VIEW_SET_PTR_IMAGE . To use this message, you must create the new pointer image in a sharable memory block, lock the block, and pass the pointer to the locked block. To reset your request (return to the default pointer), send the same message with the pointer zeroed. The pointer image structure is detailed in the Input chapter.

MSG_GEN_VIEW_SET_PTR_IMAGE

void	MSG_GEN_VIEW_SET_PTR_IMAGE(
        optr		pointerDef;					/* Optr of pointer definition */
        PtrImageLevel		level);					/* Image level of pointer */

This message causes the view to set the pointer image to a custom image whenever the pointer is over the view.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: pointerDef The optr of a chunk containing the PointerDef32 structure of the new pointer definition for the GenView (this chunk must be in a sharable memory block). Pass NullOptr for no image request. Pass the handle portion of the optr as a NullHandle and the chunk portion of the optr as a PtrImageValue value (see below).

level
A value of PtrImageLevel . This should be either PIL_GADGET, to allow individual gadgets within the view to set the pointer, or PIL_WINDOW, to set the background cursor of the view. Note that if PIV_UPDATE is passed in pointerDef , the level parameter is not used.

Return: Nothing.

Structures: PointerDef16 has the following structure (see the appropriate reference entry for a full description):

  typedef struct {
    byte		PD_width;
    byte		PD_height
    sbyte		PD_hotX;
    sbyte		PD_hotY;
    byte		PD_mask[STANDARD_CURSOR_IMAGE_SIZE];
    byte		PD_image[STANDARD_CURSOR_IMAGE_SIZE];
} PointerDef16;

Interception: Generally not intercepted.

|  Up: GEOS SDK TechDocs | Up | Prev: 4.7 Scrolling | Next: 4.9 Linking Views


Linking Views

GenView: 4.9 Advanced Concepts and Uses: Linking Views

| Up: GEOS SDK TechDocs | Up | Prev: 4.8 Monitoring Input | Next: 4.10 Setting the Content
GVI_horizLink, GVI_vertLink, MSG_GEN_VIEW_SEND_TO_LINKS, MSG_GEN_VIEW_SEND_TO_VLINK, MSG_GEN_VIEW_SEND_TO_HLINK, MSG_GEN_VIEW_CALL_WITHOUT_LINKS

Views may be linked together either in the horizontal or the vertical dimension. Classed events may then be sent to the view's links, causing the events to propagate through all the links. Most scrolling events will be propagated through the links.

Each view may have one horizontal and one vertical link, though no links are required. The links are stored as optrs in the instance fields GVI_horizLink and GVI_vertLink , and links should be circular. In other words, if three views are linked in a single dimension, the links should be as follows: view one points to view two, view two points to view three, and view three points back to view one.

Views linked in the horizontal will share vertical scroll events; those linked in the vertical will share horizontal scroll events.

When one of the linked views receives a scrolling event, the event will automatically propagate to the view's links. Other events can be sent to all the linked views; by using @record to set up an encapsulated event, you can then send that event to the linked views with any of MSG_GEN_VIEW_SEND_TO_LINKS , MSG_GEN_VIEW_SEND_TO_VLINK , or MSG_GEN_VIEW_SEND_TO_HLINK . If you want to send an encapsulated message to a linked view without propagating the event, use MSG_GEN_VIEW_CALL_WITHOUT_LINKS .

MSG_GEN_VIEW_SEND_TO_LINKS

void	MSG_GEN_VIEW_SEND_TO_LINKS(
        EventHandle event,			/* handle of recorded event */
        optr	originator);		/* optr of view first receiving event */

This message sends a recorded event to the receiving view and then to all the view's links. Any linked view will send this message upon receiving a scrolling event.

Source: A linked view when it receives a scrolling event.

Destination: The linked View sends this to itself; it will then dispatch the passed event to itself and all views linked to it.

Parameters: event The EventHandle of the recorded event to be dispatched to all linked views. Typically this is the scrolling event received by the originator.

originator
The optr of the GenView that originally received the scrolling event and sent this message out.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SEND_TO_VLINK

void	MSG_GEN_VIEW_SEND_TO_VLINK(
        EventHandle event,			/* handle of recorded event */
        optr	originator);		/* optr of view first receiving event */

This message sends a recorded event to the receiving view and then to all the view's vertical links. It works the same as MSG_GEN_VIEW_SEND_TO_LINKS except that it restricts itself to vertically-linked views.

Source: A linked view when it receives a scrolling event, or a handler for MSG_GEN_VIEW_SEND_TO_LINKS .

Destination: The linked View sends this to itself; it will then dispatch the passed event to itself and all views linked to it.

Parameters: event The EventHandle of the recorded event to be dispatched to all vertically linked views. Typically this is the scrolling event received by the originator.

originator
The optr of the GenView that originally received the scrolling event and sent this message out.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SEND_TO_HLINK

void	MSG_GEN_VIEW_SEND_TO_HLINK(
        EventHandle event,			/* handle of recorded event */
        optr	originator);		/* optr of view first receiving event */

This message sends a recorded event to the receiving view, then to the view's horizontal links. It works the same as MSG_GEN_VIEW_SEND_TO_LINKS except that it restricts itself to horizontally-linked views.

Source: A linked view when it receives a scrolling event, or a handler for MSG_GEN_VIEW_SEND_TO_LINKS .

Destination: The linked View sends this to itself; it will then dispatch the passed event to itself and all views linked to it.

Parameters: event The EventHandle of the recorded event to be dispatched to all horizontally linked views. Typically this is the scrolling event received by the originator.

originator
The optr of the GenView that originally received the scrolling event and sent this message out.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_CALL_WITHOUT_LINKS

void	MSG_GEN_VIEW_CALL_WITHOUT_LINKS(
        EventHandle		event,					/* handle of recorded event */
        MessageFlags		messageFlags);					/* flags normally sent with
                             * classed event */

This message sends the recorded event to the view and ensures the event does not get passed on to any of the recipient's links. Because the recorded event is sent to only one GenView, it is called immediately and therefore can have return values or pass pointers.

Source: Unrestricted.

Destination: Any linked GenView object

Parameters: event The EventHandle of the recorded event to be sent to only the recipient GenView.

messageFlags
A record of MessageFlags for the recorded event. If the event should return values, be sure to pass the flag MF_CALL in this record.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.8 Monitoring Input | Next: 4.10 Setting the Content


Setting the Content

GenView: 4.10 Advanced Concepts and Uses: Setting the Content

| Up: GEOS SDK TechDocs | Up | Prev: 4.9 Linking Views | Next: 4.11 Internal Utilities
MSG_GEN_VIEW_SET_CONTENT, MSG_GEN_VIEW_GET_CONTENT

The GVI_content field in the GenView instance data determines what object is designated as its content. By default, an application's Process object acts as the content. Because the output field defaults to the Process object, you do not have to explicitly set it unless you plan on using a GenContent or VisContent.

Though not usually used when the Process is the content, it is possible to change the content during execution with MSG_GEN_VIEW_SET_CONTENT . Along with this message you must pass the optr of the object that will become the new content. When the view receives this message, it will shut down the current content just as if the view were being shut down. Then it will initialize the new content as if the view were opening for the first time.

To retrieve the optr of the current content object of a view, send the view MSG_GEN_VIEW_GET_CONTENT .

MSG_GEN_VIEW_SET_CONTENT

void	MSG_GEN_VIEW_SET_CONTENT(
        optr	content);

This message sets the view's GVI_content instance data field to a new optr. The current content will receive messages as if the view were shutting down, and the new content will receive messages as if the view were being created anew. (See The Life of a View .)

Source: Unrestricted.

Destination: Any GenView object.

Parameters: content The optr of the new content object.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_GET_CONTENT

optr	MSG_GEN_VIEW_GET_CONTENT();

This message returns the optr of the object currently set as the view's content.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: None.

Return: The optr currently set as the GenView's content.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.9 Linking Views | Next: 4.11 Internal Utilities


Internal Utilities

GenView: 4.11 Advanced Concepts and Uses: Internal Utilities

| Up: GEOS SDK TechDocs | Up | Prev: 4.10 Setting the Content | Next: 5 The GenViewControl
MSG_GEN_VIEW_DETERMINE_VIS_PARENT

Some messages supported by the view may be used as general utilities. Most of these, however, will never be needed by applications.

MSG_GEN_VIEW_DETERMINE_VIS_PARENT

optr	MSG_GEN_VIEW_DETERMINE_VIS_PARENT(
        optr	child);

This message returns the optr of the visible parent for a GenView's child object. This is used by the GenView's children during its specific-UI building and should not be needed by applications.

Source: A generic child of the GenView.

Destination: The GenView being specifically built.

Parameters: child The optr of the child to get the visible parent of.

Return: The optr of the visible parent.

Interception: Should not be intercepted.

MSG_GEN_VIEW_SEND_NOTIFICATION

void	MSG_GEN_VIEW_SEND_NOTIFICATION();

This message is part of the GenViewControl mechanism and is used almost exclusively by the GenView. It sends the special notification from the GenView to the controller, if needed.

Source: Part of GenView control mechanism.

Destination: Any GenView object.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SET_CONTROLLED_ATTRS

void	MSG_GEN_VIEW_SET_CONTROLLED_ATTRS(
        GenViewControlAttrs		controlAttrs,
        word		scaleFactor);

This message sets attributes controlled by the GenViewControl.

Source: Typically a GenViewControl object, automatically.

Destination: A controlled GenView object.

Parameters: controlAttrs A record of GenViewControlAttrs flags indicating the attributes to be set.

scaleFactor
The new scale factor to be set for the view.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.10 Setting the Content | Next: 5 The GenViewControl


The GenViewControl

GenView: 5 The GenViewControl

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Advanced Concepts and Uses | Next: 5.1 GenViewControl Instance Data

GenViewControlClass works with GenView objects to provide a number of features to your user interface. It is a subclass of GenControlClass and works with the GenToolControl to implement tool bars and tool boxes for the view.

For the controller to interact with a GenView, the view must have the GVA_CONTROLLED flag set in its GVI_attrs field. The GenViewControl will interact with all views having this flag set.

The GenViewControl features and tools are shown in GenViewControl Features and Tools . All GenViewControl objects must be placed on the MGCNLT_ACTIVE_LIST and GAGCNLT_SELF_LOAD_OPTIONS GCN lists. The view itself should be on the GAGCNLT_CONTROLLED_GEN_VIEW_OBJECTS list.

Code Display 9-6 GenViewControl Features and Tools

/* The GenViewControl supports the following features. */
  typedef		WordFlags GVCFeatures;
#define	GVCF_MAIN_100			0x4000	/* sets scale to 100% from View menu */
#define	GVCF_MAIN_SCALE_TO_FIT			0x2000	/* allows scale-to-fit in View menu */
#define	GVCF_ZOOM_IN			0x1000	/* zooms in on the document */
#define	GVCF_ZOOM_OUT			0x0800	/* zooms out on the document */
#define	GVCF_REDUCE			0x0400	/* reduces the view's scale factor */
#define	GVCF_100			0x0200	/* sets scale to 100% in View submenu */
#define	GVCF_ENLARGE			0x0100	/* enlarges the view's scale factor */
#define	GVCF_BIG_ENLARGE			0x0080	/* enlarges the scale factor twice */
#define	GVCF_SCALE_TO_FIT			0x0040	/* scales the view to fit the document */
#define	GVCF_ADJUST_ASPECT_RATIO			0x0020	/* adjusts scale for aspect ratio */
#define	GVCF_APPLY_TO_ALL			0x0010	/* applies to all active views */
#define	GVCF_SHOW_HORIZONTAL			0x0008	/* shows/hides the horizontal scroller */
#define	GVCF_SHOW_VERTICAL			0x0004	/* shows/hides the vertical scroller */
#define	GVCF_CUSTOM_SCALE			0x0002	/* allows custom scaling */
#define	GVCF_REDRAW			0x0001	/* refreshes the view window */
/* The GenViewControl provides the following tools. */
typedef		WordFlags GVCToolboxFeatures;
 #define	GVCTF_100			0x1000	/* sets the scale factor to 100% */
#define	GVCTF_SCALE_TO_FIT			0x0800	/* scales the view to fit the document */
#define	GVCTF_ZOOM_IN			0x0400	/* zooms in; enlarges the scale factor */
#define	GVCTF_ZOOM_OUT			0x0200	/* zooms out; reduces the scale factor */
#define	GVCTF_REDRAW			0x0100	/* refreshes the view window */
#define	GVCTF_PAGE_LEFT			0x0080	/* scrolls left/back one page width */
#define	GVCTF_PAGE_RIGHT			0x0040	/* scrolls right/forward one page width */
#define	GVCTF_PAGE_UP			0x0020	/* scrolls up/back one page height */
#define	GVCTF_PAGE_DOWN			0x0010	/* scrolls down/forward one page height */
#define	GVCTF_ADJUST_ASPECT_RATIO 0x0008				/* adjusts scale for aspect ratio */
#define	GVCTF_APPLY_TO_ALL			0x0004	/* applies to all active views */
#define	GVCTF_SHOW_HORIZONTAL			0x0002	/* shows/hides the horizontal scroller */
#define	GVCTF_SHOW_VERTICAL			0x0001	/* shows/hides the vertical scroller */
    /* Default features and tools */
#define GVC_DEFAULT_FEATURES				(GVCF_MAIN_100 | GVCF_MAIN_SCALE_TO_FIT |
                 GVCF_REDUCE | GCVF_ENLARGE | GVCF_BIG_ENLARGE |
                 GVCF_SCALE_TO_FIT | GVCF_ADJUST_ASPECT_RATIO |
                 GVCF_APPLY_TO_ALL | GVCF_SHOW_HORIZONTAL |
                 GVCF_SHOW_VERTICAL | GVCF_CUSTOM_SCALE)
#define GVC_DEFAULT_TOOLBOX_FEATURES (GVCTF_100 | GVCTF_ZOOM_IN | GVCTF_ZOOM_OUT)
#define GVC_SUGGESTED_SIMPLE_FEATURES (GVCF_MAIN_100 | GVCF_MAIN_SCALE_TO_FIT |
                 GVCF_ZOOM_IN | GVCF_ZOOM_OUT)
#define GVC_SUGGESTED_INTRODUCTORY_FEATURES (GVCF_MAIN_100 | GVCF_ZOOM_IN |
                 GVCF_ZOOM_OUT)
#define GVC_SUGGESTED_BEGINNING_FEATURES (GVC_SUGGESTED_INTRODUCTORY_FEATURES |
                 GVCF_MAIN_SCALE_TO_FIT)

GenViewControl Instance Data

Notification Received

GenViewControl Example

Messages Handled

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Advanced Concepts and Uses | Next: 5.1 GenViewControl Instance Data


GenViewControl Instance Data

GenView: 5.1 The GenViewControl: GenViewControl Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 5 The GenViewControl | Next: 5.2 Notification Received

In addition to the normal instance data of GenControlClass , GenViewControlClass has four other fields. These are shown in GenViewControl Instance Data along with their default values.

Code Display 9-7 GenViewControl Instance Data

/* The GenViewControl has four instance fields in addition to those inherited
 * from GenControlClass. You may wish to set these to determine the controller's
 * initial configuration, but typically you will not need to. */
    /* Constants used for scaling boundaries */
#define DEFAULT_ZOOM_MINIMUM				 25
#define DEFAULT_ZOOM_MAZIMUM				200
    /* GVCI_minZoom determines the minimum percentage zoom allowed. */
    /* GVCI_maxZoom indicates the maximum zoom percentage allowed. */
    /* GVCI_scale indicates the current view scale factor setting. */
    /* GVCI_attrs indicates which attributes the controller should
     * implement on startup. */
    @instance word			GVCI_minZoom =		DEFAULT_ZOOM_MINIMUM;
    @instance word			GVCI_maxZoom =		DEFAULT_ZOOM_MAXIMUM;
    @instance word			GVCI_scale = 		100;
    @instance GenViewControlAttrs GVCI_attrs =						(GVCA_SHOW_HORIZONTAL |
                         GVCA_SHOW_VERTICAL |
                         GVCA_APPLY_TO_ALL);
    /* Possible GenViewControlAttrs flags:
     *	GVCA_ADJUST_ASPECT_RATIO				GVCA_APPLY_TO_ALL
     *	GVCA_SHOW_HORIZONTAL				GVCA_SHOW_VERTICAL */
    @default GI_attrs = (@default | GA_KBD_SEARCH_PATH);
                /* This adds the view controller to the default
                 * keyboard accelerator search path. */
| Up: GEOS SDK TechDocs | Up | Prev: 5 The GenViewControl | Next: 5.2 Notification Received


Notification Received

GenView: 5.2 The GenViewControl: Notification Received

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 GenViewControl Instance Data | Next: 5.3 GenViewControl Example

When a view's attributes change and the controller will be affected, the view sends notification to the controller. The type of notification handled by the controller is NT_VIEW_STATE_CHANGE. This notification type will carry with it a block containing a structure of type NotifyViewStateChange , shown in The NotifyViewStateChange Structure .

The NotifyViewStateChange structure contains much more information about the view than just those features affected by the controller. Additional information includes background color, increment, document bounds, Ink information, origin, etc. If you want to add functionality to the GenViewControl, you can set up additional UI gadgetry with the GenControlClass attribute ATTR_GEN_CONTROL_APP_UI and have it change and handle the appropriate attributes.

Code Display 9-8 The NotifyViewStateChange Structure

/* This structure is passed to the GenViewControl object when a feature of the
 * active view changes. It is passed with notification NT_VIEW_STATE_CHANGE. */
typedef struct {
    PointDWFixed			NVSC_origin;			/* absolute origin of the view */
    RectDWord			NVSC_docBounds;			/* document bounds of the view */
    PointDWord			NVSC_increment;			/* the view's increment value */
    PointWWFixed			NVSC_scaleFactor;			/* view's current scale factor */
    ColorQuad			NVSC_color;			/* view's background color */
    GenViewAttrs			NVSC_attrs;			/* view's GVI_attrs record */
    GenViewDimensionAttrs				NVSC_horizAttrs;		/* view's GVI_horizAttrs record */
    GenViewDimensionAttrs				NVSC_vertAttrs;		/* view's GVI_vertAttrs record */
    GenViewInkType			NVSC_inkType;			/* view's GVI_inkType value */
  /* The following four fields are used internally by the GenViewControl. */
    XYSize			NVSC_contentSize;
    XYSize			NVSC_contentScreenSize;
    PointDWord			NVSC_originRelative;
    PointDWord			NVSC_documentSize;
} NotifyViewStateChange;
| Up: GEOS SDK TechDocs | Up | Prev: 5.1 GenViewControl Instance Data | Next: 5.3 GenViewControl Example


GenViewControl Example

GenView: 5.3 The GenViewControl: GenViewControl Example

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Notification Received | Next: 5.4 Messages Handled

An example of the use of a GenViewControl is shown in An Example of GenViewControl . This example is built on the Hello World application, and only the differences between Hello World and the example are shown. As an exercise, try adding a GenToolControl to Hello World as well.

Code Display 9-9 An Example of GenViewControl

/* This display builds on the Hello World sample application. You should make the
 * changes shown here to that program, compile it, and run it to get a solid feel
 * for how the GenViewControl works and the features it provides.
 * Only the alterations to Hello World are shown here; the basic code can be found
 * in the Hello World chapter. */
/* Add the GenViewControl definition file. */
@include <Objects/gViewCC.goh>
/* HelloApp changes
 * Add a new GCN list type to the application object, and add the HelloViewControl
 * object to the list. This list is typical of system-provided controllers. */
@object GenApplicationClass HelloApp = {
    GI_visMoniker = list { @HelloTextMoniker };
    GI_comp = @HelloPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) =							@HelloPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) =									/* add */
                            @HelloViewControl;
}
/* HelloPrimary changes
 * Add a new menu to the Primary's list of children. This new menu is called
 * HelloViewMenu and is declared below. */
@object GenPrimaryClass HelloPrimary = {
    GI_visMoniker = "Hello Sample Application";							/* unchanged */
    GI_comp = @HelloView, @HelloMenu, @HelloViewMenu;							/* add @HelloViewMenu */
    ATTR_GEN_DISPLAY_NOT_MINIMIZABLE;							/* unchanged */
    HINT_SIZE_WINDOW_AS_DESIRED;							/* unchanged */
}
/* HelloViewMenu declaration
 * Declare a new menu, HelloViewMenu. The GenViewControl object will add its
 * features to this menu. */
@object GenInteractionClass HelloViewMenu = {
    GII_visibility = GIV_POPUP;				/* This makes the GenInteraction a menu */
    GI_comp = @HelloViewControl;				/* Add the GenViewControl as the menu's child */
    ATTR_GEN_INTERACTION_GROUP_TYPE = (GIGT_VIEW_MENU);
                /* Give this menu the default characteristics
                 * of the standard View menu. */
}
/* HelloViewControl declaration
 * Declare the GenViewControl object, HelloViewControl. This needs no extra
 * settings; we will use all the defaults. */
@object GenViewControlClass HelloViewControl = {
}
/* HelloView changes
 * The only change necessary to the GenView itself is to mark it controlled.
 * This entails setting the GVA_CONTROLLED flag in GVI_attrs. */
@object GenViewClass HelloView = {
    GVI_attrs = @default | GVA_CONTROLLED;						/* set the controlled attr */
    GVI_horizAttrs = @default | GVDA_SCROLLABLE | GVDA_NO_LARGER_THAN_CONTENT;
                        /* unchanged */
    GVI_vertAttrs = @default | GVDA_SCROLLABLE | GVDA_NO_LARGER_THAN_CONTENT;
                        /* unchanged */
    GVI_content = process;						/* unchanged */
}
| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Notification Received | Next: 5.4 Messages Handled


Messages Handled

GenView: 5.4 The GenViewControl: Messages Handled

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 GenViewControl Example
MSG_GEN_VIEW_CONTROL_SET_ATTRS, MSG_GEN_VIEW_CONTROL_SET_MINIMUM_SCALE_FACTOR, MSG_GEN_VIEW_CONTROL_SET_MAXIMUM_SCALE_FACTOR

GenViewControlClass handles several messages that allow its characteristics to be set or modified at run-time. It also has several other internal messages which are not documented here.

MSG_GEN_VIEW_CONTROL_SET_ATTRS

void	MSG_GEN_VIEW_CONTROL_SET_ATTRS(
        GenViewControlAttrs		attrsToSet,
        GenViewControlAttrs		attrsToClear);

This message sets the GenViewControlAttrs in GVCI_attrs. Attributes noted in both parameters will be turned off for the controller.

Source: Unrestricted.

Destination: Any GenViewControl object.

Parameters: attrsToSet Attributes to be turned on should be set.

attrsToClear
Attributes to be turned off should be set.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_CONTROL_SET_MINIMUM_SCALE_FACTOR

void	MSG_GEN_VIEW_CONTROL_SET_MINIMUM_SCALE_FACTOR(
        word	minimumScaleFactor);

This message sets the view controller's minimum allowable scale factor.

Source: Unrestricted.

Destination: Any GenViewControl object.

Parameters: minimumScaleFactor
 The new minimum scale factor (a percentage).

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_CONTROL_SET_MAXIMUM_SCALE_FACTOR

void	MSG_GEN_VIEW_CONTROL_SET_MAXIMUM_SCALE_FACTOR(
         word	maximumScaleFactor);

This message sets the maximum settable scale factor for the view controller.

Source: Unrestricted.

Destination: Any GenViewControl object.

Parameters: maximumScaleFactor
 The maximum settable scale factor (as a percent).

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 GenViewControl Example


Managing UI Geometry

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Geometry Manager Overview
| One-page Version of this Chapter for Easy Printing

One of the more difficult aspects of programming a graphically oriented application is positioning the user interface components on the screen. Developers often have to manually place buttons, size dialog boxes, and draw frames and titles. The GEOS Geometry Manager lets you ignore most of these issues.

The geometry manager allows you to ignore position, size, and spacing of your generic UI components; if you use visual objects, the geometry manager can calculate object sizes and positions, dynamically changing them as necessary. Because the geometry manager works at run-time, you have to do very little in the way of screen management. Instead, you can set various attributes and hints to fine-tune your UI's appearance.

1 Geometry Manager Overview
1.1 Geometry Manager Features
1.2 How Geometry Is Managed
2 Arranging Your Generic Objects
2.1 General Geometry Rules
2.2 Orienting Children
2.3 Justifying and Centering Children
2.4 Sizing Objects
2.5 Outlining the Composite
2.6 Using Monikers
2.7 Using Custom Child Spacing
2.8 Allowing Children to Wrap
2.9 Object Placement
3 Positioning and Sizing Windows
3.1 Window Positioning
3.2 Determining Initial Size
3.3 On-Screen Behavior
3.4 Window Look and Feel

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Geometry Manager Overview
| One-page Version of this Chapter for Easy Printing


Geometry Manager Overview

Managing UI Geometry: 1 Geometry Manager Overview

| Up: GEOS SDK TechDocs | Up | Down | Prev: Managing UI Geometry | Next: 2 Arranging Your Generic Objects

The GEOS geometry manager is an algorithmic mechanism which interacts directly with the UI to position and size objects of all types. The geometry manager automatically manages all generic objects through interactions with the specific UI library; you can also use it to manage your visible object trees.

The Geometry Manager is what makes the generic UI possible: Since geometry of UI objects (size and position) is determined by the UI at run-time, generic UI objects do not have to be explicitly defined with certain geometry. With this feature, a specific UI library can set up the UI geometry as it wants.

Your first interaction with the geometry manager will be when you build a generic object tree for your UI. Most hints applied to an object are signals to the geometry manager about how the object should be positioned, sized, or otherwise placed on the screen. These hints are all defined in GenClass , the topmost generic object class, so they are available (though not necessarily useful) to all types of generic objects.

This chapter first describes the features of the geometry manager and how the mechanism fits into the GEOS system as a whole (later in this section). Then it discusses how to manage generic UI objects (using a dialog box as an example) in Arranging Your Generic Objects . For the hints you can apply to window objects, see Positioning and Sizing Windows . For descriptions of managing the geometry of visible object trees and composites, see the VisClass chapter.

Geometry Manager Features

How Geometry Is Managed

| Up: GEOS SDK TechDocs | Up | Down | Prev: Managing UI Geometry | Next: 2 Arranging Your Generic Objects


Geometry Manager Features

Managing UI Geometry: 1.1 Geometry Manager Overview: Geometry Manager Features

| Up: GEOS SDK TechDocs | Up | Prev: 1 Geometry Manager Overview | Next: 1.2 How Geometry Is Managed

The geometry manager, used effectively, can take nearly all the work out of displaying, sizing, and positioning objects on the screen. Many of the tasks you normally would have to do to create even a simple dialog box are taken care of automatically by the geometry manager and the specific UI library. Among the functions the geometry manager performs are the following:

  • Window Positioning and Sizing
    The geometry manager interacts directly with the UI to determine where windows should be placed and what their dimensions should be. You have some control over this with hints, messages, and object instance data, and often you will have to choose a hint to determine the window position or size.
  • Object Sizing
    The size of an object can be determined by the combined sizes of its children, by the limits of its parent, or specifically by other constraints on the object. The geometry manager allows all three types of sizing behavior. The first two are implemented automatically in geometry updates; the third depends on how the application wants to manage the "other constraints."
  • Object Positioning
    The position of an object can be determined either by the object or by the geometry manager. The geometry manager can position objects based on several criteria including the spacing of siblings in a composite, the size and/or location of the parent composite, and the justification hints applied to the object. Visible objects can control very closely how and where they are positioned; generic objects can determine how positioning occurs with various hints.
  • Automatic Geometry Updating
    Typically, when a particular object in a tree is resized, repositioned, added, or removed, the object mechanisms will mark the geometry of both that object and its parent invalid. This will invoke the geometry manager, which will traverse the object tree, determine which objects are affected by the potential geometry change, and then update the size and position of all affected objects up to the top of the tree. This entire process is automatic for generic objects; for visual objects, you may or may not have to mark the object invalid yourself, depending on the depth of custom geometry control you use.

With geometry functions covered by the geometry manager and drawing functions covered by the specific UI library, you have little else to do but specify the structure of your object tree and the hints each object should have. The system does all the dirty work of drawing and managing the objects on the screen, leaving you free to work on your application rather than on its graphical screen representation.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Geometry Manager Overview | Next: 1.2 How Geometry Is Managed


How Geometry Is Managed

Managing UI Geometry: 1.2 Geometry Manager Overview: How Geometry Is Managed

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Geometry Manager Features | Next: 2 Arranging Your Generic Objects

The geometry manager is one part of the system that draws objects on the screen and redraws them when necessary. This system is called the "visual update mechanism."

The visual update mechanism is invoked any time the geometry or image of an object is marked invalid. If only the image is invalid, typically no geometry calculations will be done. If, however, the geometry of an object is marked invalid, the geometry manager is called in to calculate the new size and position of all the affected objects in the object tree.

Normally, you won't have to do any more than set certain hints (for generic objects) or attribute flags (for visible objects) to get the desired geometry behavior. If, however, you plan on modifying how the geometry of a particular set of objects is set, you will want to understand the sequence of events that happen during a geometry update. Typically, only programmers who are building their own specific UI libraries and those who have complex visual object trees will need to understand the geometry calculation sequence.

The geometry manager's calculation can be thought of as a multiple-pass, recursive algorithm. It traverses the object tree, managing geometry within each composite until it reaches the top-most object that is unaffected by the geometry calculations.

The geometry update process is invoked when an object has its geometry marked invalid. This is determined by the object's visual flags (its VI_optFlags field), VOF_GEOMETRY_INVALID and VOF_WINDOW_INVALID. If either of these flags is set, the object's geometry requires recalculation.

The geometry manager sends MSG_VIS_UPDATE_GEOMETRY to the visible object with invalid geometry. This message determines which objects in the tree are potentially affected by the geometry update, and then it recalculates the tree's geometry.

If the object initially marked invalid is a composite, all its children are automatically included in the update. If the object is a child of a composite, the geometry manager will travel up the tree, marking any parents and siblings that also require recalculation. (Recalculation does not cross VTF_IS_WIN_GROUP boundaries, however. A VTF_IS_WIN_GROUP is any windowed object; geometry calculations generally do not cross windowed objects (move into another window layer) unless explicitly instructed to do so.)

The geometry manager then returns to the bottom-most affected object in the tree and sends it a MSG_VIS_RECALC_SIZE . This message passes the object a suggested size, which will be the same as the object's original size. (The original size is passed as an optimization in case the object does not need its geometry changed; many changes will affect many objects but will actually change only a few.)

The object then returns its new desired size based on the geometry change. If this is the original object marked invalid, the returned size will be the direct result of the geometry change. If this object is a descendent of the invalid object, it may not want to change its geometry at all and may therefore return its current size.

The geometry manager collects the returned sizes of all siblings at a given level and calculates the suggested size of their parent composite. The parent composite is then sent MSG_VIS_RECALC_SIZE with the newly calculated size and returns the size it wants to be as a result. (Some composites will have fixed, maximum, or minimum sizes or may have their sizing tied to other factors; these objects may return a different size.) If the returned size is different from the passed size, the geometry manager arbitrates between the objects. Typically, the composite will be grown large enough (even if fixed or maximum size) to fit all its children.

The geometry manager will go up the tree in this manner, calculating the new geometry of each child and then each composite until it reaches an object that is unaffected by the geometry change. (For example, if a child shrinks but other children cause the composite to remain the same size, the geometry update will stop at the composite because objects higher up are not affected.) If all objects in the tree are affected by the change, the geometry update will stop at the topmost object in the branch (the first VTF_IS_WIN_GROUP object).

If any parent objects disagree with their children on the new size, the geometry manager will arbitrate. The geometry manager may make several passes to suggest width and height changes to both the child and parent before deciding on an ultimate geometry. The geometry manager may alter child spacing, wrap the children, grow the composite, or even clip the children if necessary. A result will quickly be reached, however--the geometry manager will not thrash between the parent and children (unless you subclass MSG_VIS_RECALC_SIZE or MSG_VIS_GET_CENTER and do not pay close attention to how the message is handled).

After the geometry is recalculated up to the first VTF_IS_WIN_GROUP object (or up to the top affected object), a MSG_VIS_NOTIFY_GEOMETRY_VALID will be sent to each of the invalid objects that have VGA_NOTIFY_GEOMETRY_VALID set in their VI_attrs field. A visual update will then be started to draw the new geometry to the screen. Once a branch's geometry has been calculated, the branch will remain unaffected until the next time the geometry of one of its objects is marked invalid.

If a recalculation of a branch's geometry causes a VTF_IS_WIN_GROUP object to change its size or position, the window system will determine the effects. For example, if a VisContent object grows as a result of a geometry change, it will send a MSG_GEN_VIEW_SET_DOC_BOUNDS to the GenView; the geometry recalculation mechanism will not scroll the content-view connection. What happens to the view and its parent window then depends on the attributes of the view. For example, if the view is set to follow the size of its content, it will grow, bumping the size of its parent in the process. If the view is scrollable, it likely will stay the same size.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Geometry Manager Features | Next: 2 Arranging Your Generic Objects


Arranging Your Generic Objects

Managing UI Geometry: 2 Arranging Your Generic Objects

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Geometry Manager Overview | Next: 3 Positioning and Sizing Windows

The process of arranging user interface elements for a GEOS application differs drastically from that of traditional user interface programming methods. In most graphical systems, you must manually determine the location of each and every user interface element. For instance, to design a dialog box, you have to figure the location of each text string, button, and list entry. Normally, these items would have to be hard-coded into your application, and often different video resolutions would have to be taken into account.

Not so in GEOS. The generic and specific UI libraries work with the geometry manager to automatically arrange your generic UI objects in the most appropriate way. To describe a dialog box in GEOS, for instance, you simply have to define the objects contained in the dialog box and their relative positions in the generic object tree; GEOS does the rest, determining the position and visual effects of each object. You can even fine-tune the appearance of each object with the use of hints.

General Geometry Rules

Orienting Children

Justifying and Centering Children

Sizing Objects

Outlining the Composite

Using Monikers

Using Custom Child Spacing

Allowing Children to Wrap

Object Placement

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Geometry Manager Overview | Next: 3 Positioning and Sizing Windows


General Geometry Rules

Managing UI Geometry: 2.1 Arranging Your Generic Objects: General Geometry Rules

| Up: GEOS SDK TechDocs | Up | Prev: 2 Arranging Your Generic Objects | Next: 2.2 Orienting Children

You may feel at first that the use of generic UI components limits your control over the UI of your application. Actually, just the opposite is true: Your application becomes immediately available at all supported video resolutions in all available specific UIs. Additionally, you can concentrate on more important issues of your application; the generic UI objects take care of a tremendous amount of the work in positioning and displaying the user interface.

To determine your generic UI tree, you must remember that UI geometry is determined by three things: First and foremost, the overall structure of the UI is governed by the structure and organization of the generic object tree. For example, if GenTriggers A, B, and C are designated as children of a dialog box, they will likely be ordered in the same order they're listed in the dialog's GI_comp field.

Second, an object's attributes can determine its implementation. For example, the GIGI_visibility field of a GenInteraction can determine whether the interaction is implemented as a dialog box, a menu, or a grouping object. The attributes of a generic object determine the basic functionality of the object; that is, even though a menu might be implemented differently in different specific UIs, the functionality of a menu will be implemented in the object. The manner of display of an object is based on its functionality. In this way, the attributes of a generic object take precedence over hints.

Third, an object's behavior can be fine-tuned through the use of hints. Hints may or may not be implemented for each object that has them, and they control less strictly how the object works.

Generic Tree Structure

As stated above, the structure of your application's generic object tree determines how the generic UI objects will be organized. A simple example of a generic tree including a menu, a dialog box, and a GenView can be found in the Hello World chapter.

How Hints Work

Nearly all geometry of generic UI objects is determined by hints. You can position, size, and limit generic objects with different hints. All these hints are defined in GenClass ; inheritance allows all generic objects to have them, though not all hints are applicable to all generic objects.

Hints are described in detail in the GenClass chapter, but the basics are reviewed here for convenience. Hints are implemented as variable data entries; each hint is a different variable-data type. Hints therefore take up instance data space in an object only when the object has that hint.

As stated earlier, not all hints are appropriate for all objects. For example, HINT_CUSTOM_CHILD_SPACING is probably not useful when applied to a GenTrigger object because a GenTrigger can have no children. Hints, by definition, are also not guaranteed to be supported by all specific UI libraries. For example, a specific UI might require all elements of a dialog box to be oriented horizontally; in this case, HINT_ORIENT_CHILDREN_VERTICALLY might be useful but might not be heeded by the specific UI.

Because geometry management of generic objects can be confusing at times, this chapter follows a series of examples accompanied with diagrams. For the most part, you should be able to "plug and play" the examples in the following sections.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Arranging Your Generic Objects | Next: 2.2 Orienting Children


Orienting Children

Managing UI Geometry: 2.2 Arranging Your Generic Objects: Orienting Children

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 General Geometry Rules | Next: 2.3 Justifying and Centering Children
HINT_ORIENT_CHILDREN_HORIZONTALLY, HINT_ORIENT_CHILDREN_VERTICALLY, HINT_ORIENT_CHILDREN_ALONG_LARGER_DIMENSION, HINT_SAME_ORIENTATION_AS_PARENT

The two hints HINT_ORIENT_CHILDREN_HORIZONTALLY and HINT_ORIENT_CHILDREN_VERTICALLY , when used in a composite object, determine the orientation of the composite's children. A composite object can orient its children either horizontally or vertically. The geometry manager will determine the combined size of the children and any margins or spacing set up (none by default) and will size the composite to the minimum required by its children.

Children oriented horizontally will typically be top-justified, and children oriented vertically will be left-justified. (This is default behavior changeable either with the use of hints or by the specific UI.) The vertical composite will size to the width of the widest child; the horizontal composite will size to the height of the tallest child.

Code Display 12-1 Orienting a Composite

    /* Composite oriented horizontally */
@object GenInteractionClass MyInteraction = {
    GI_comp = @ApplyTrigger, @ResetTrigger, @CancelTrigger;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
    /* Composite oriented vertically */
@object GenInteractionClass MyOtherInteraction = {
    GI_comp = @ApplyTrigger, @ResetTrigger, @CancelTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}

A large example of a complex dialog box is shown in the figure below. This dialog box uses nested GenInteraction objects to achieve a complex grouping of children. Using the many justification, spacing, and orientation hints described throughout the chapter, you can change and fine-tune the appearance of the dialog box. The code that implements this dialog box is shown in A Complex Dialog Box .

Two other hints may be useful for arranging children, especially in toolboxes. These two hints are not shown in the examples, though they are used prominently with tool groups.

HINT_SAME_ORIENTATION_AS_PARENT is used by one windowed object (GenInteraction) when it should orient its children in the same way its parent does. This, too, is useful for toolboxes because you can set an orientation in the toolbox, then set HINT_SAME_ORIENTATION_AS_PARENT in the tool group.

HINT_ORIENT_CHILDREN_ALONG_LARGER_DIMENSION allows you to orient a composite's children along the screen's larger dimension. The hint will work like HINT_ORIENT_CHILDREN_HORIZONTALLY if the screen is wider than it is tall; it will work like HINT_ORIENT_CHILDREN_VERTICALLY if the screen is taller than it is wide.

Code Display 12-2 A Complex Dialog Box

/* This code display shows the basic Goc code that will get the configuration
 * shown in the above figure. */
/* The topmost interaction. It groups the two large interactions vertically. */
@object GenInteractionClass TopInteraction = {
    GI_comp = @ParaInteraction, @ReplyInteraction;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
/* The top of the two large interactions. It groups the three smaller interactions
 * on the top, horizontally. */
@object GenInteractionClass ParaInteraction = {
    GI_comp = @FontInteraction, @JustInteraction, @StyleInteraction;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
/* The interaction containing the Apply, Reset, and Cancel triggers. The
 * triggers are oriented horizontally. */
@object GenInteractionClass ReplyInteraction = {
    GI_comp = @ApplyTrigger, @ResetTrigger, @CancelTrigger;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
/* The three vertically-oriented interactions at the top are similar. Each
 * contains several triggers. All three are defined below. */
@object GenInteractionClass FontInteraction = {
    GI_comp = @RomanTrigger, @SansTrigger, @MonoTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@object GenInteractionClass JustInteraction = {
    GI_comp = @LeftTrigger, @RightTrigger, @CenterTrigger, @FullTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@object GenInteractionClass StyleInteraction = {
    GI_comp = @PlainTrigger, @BoldTrigger, @SuperTrigger, @SubTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
/* Triggers. All the triggers are essentially the same. Each is named as it
 * appears in the GI_comp fields above, and each has the corresponding
 * GI_visMoniker field. The RomanTrigger object is shown as an example. */
@object GenTriggerClass RomanTrigger = {
    GI_visMoniker = "Roman";
}
| Up: GEOS SDK TechDocs | Up | Prev: 2.1 General Geometry Rules | Next: 2.3 Justifying and Centering Children


Justifying and Centering Children

Managing UI Geometry: 2.3 Arranging Your Generic Objects: Justifying and Centering Children

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Orienting Children | Next: 2.4 Sizing Objects

Although child justification defaults to whatever the specific UI normally uses, you can set the justification of a composite's children with several different hints.

Typically, horizontal composites will have their children top-justified and vertical composites will have left-justified children (as shown in the previous examples).

Edge Justification

HINT_TOP_JUSTIFY_CHILDREN, HINT_BOTTOM_JUSTIFY_CHILDREN, HINT_LEFT_JUSTIFY_CHILDREN, HINT_RIGHT_JUSTIFY_CHILDREN

If you want to line all of a composite's children up on one of their edges, you should use one of these hints. They are self-explanatory; the edge specified in the name of the hint (top, left, etc.) is the edge of the composite along which all the children will appear.

Add the justification hints along with an orientation hint. Examples of justification are shown in the figure below.

Edge Alignment with Other Objects

HINT_ALIGN_LEFT_EDGE_WITH_OBJECT, HINT_ALIGN_TOP_EDGE_WITH_OBJECT, HINT_ALIGN_RIGHT_EDGE_WITH_OBJECT, HINT_ALIGN_BOTTOM_EDGE_WITH_OBJECT

These hints align an object's respective edge with the same edge of another object; the optr of the other object is the hint's argument. The other object does not necessarily have to be a direct sibling or parent, but the result must not cause the object to stray outside its parent's bounds.

Centering Children

HINT_CENTER_CHILDREN_VERTICALLY, HINT_CENTER_CHILDREN_HORIZONTALLY

Often, rather than justifying a composite's children, you will want to center them. These two hints allow you to center the children as shown in the figure below.

Full Justification

HINT_FULL_JUSTIFY_CHILDREN_HORIZONTALLY, HINT_FULL_JUSTIFY_CHILDREN_VERTICALLY, HINT_DONT_FULL_JUSTIFY_CHILDREN, HINT_INCLUDE_ENDS_IN_CHILD_SPACING, HINT_DONT_INCLUDE_ENDS_IN_CHILD_SPACING

Besides justifying the composite's children either on a single edge or on the center line of the composite, you might want to full-justify the children. To do this, you will want to give the composite an orientation first (either vertical or horizontal). The composite may also require a special sizing hint to expand its bounds--see HINT_EXPAND_WIDTH_TO_FIT_PARENT and the other sizing hints in Sizing Objects .

If the full justification hint is in the same dimension as the orientation of the composite (e.g., HINT_FULL_JUSTIFY_CHILDREN_HORIZONTALLY and HINT_ORIENT_CHILDREN_HORIZONTALLY ), then the geometry manager will divide up the entire space of the composite and spread the children out equally along it. If you also specify HINT_INCLUDE_ENDS_IN_CHILD_SPACING , the geometry manager will calculate the spacing as if there were one more child than there actually is; it will then put half the extra space on either end of the children. HINT_DONT_INCLUDE_ENDS_IN_CHILD_SPACING performs the default calculations. Examples of various full justification are shown in the figure below. See Using Full Justification of Children for the code required to implement this full justification behavior.

Typically, full justification is not implemented by default. Some specific UIs may try to full-justify children always; if you want the children non-justified (when possible), apply HINT_DONT_FULL_JUSTIFY_CHILDREN . Added spacing is also typically not turned on by default; you can ensure that added spacing will not be included (where possible) by applying the hint HINT_DONT_INCLUDE_ENDS_IN_CHILD_SPACING to your composite.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Orienting Children | Next: 2.4 Sizing Objects


Sizing Objects

Managing UI Geometry: 2.4 Arranging Your Generic Objects: Sizing Objects

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Justifying and Centering Children | Next: 2.5 Outlining the Composite

Sizing can occur in essentially one of two ways: First, the composite can size itself based on its children. Second, the children can size themselves based on their parent. You can specify additional sizing restrictions in the form of minimum, maximum, and fixed sizes.

Sizing the Parent

HINT_NO_TALLER_THAN_CHILDREN_REQUIRE, HINT_NO_WIDER_THAN_CHILDREN_REQUIRE

A composite can size itself to be only as large as its children require. This may be applied to your primary window or to other organizational composites to keep them from growing into any extra space in their parents. You can set the width and height restrictions independently; to set them both, use both hints in the composite.

Sizing the Children

HINT_EXPAND_HEIGHT_TO_FIT_PARENT, HINT_EXPAND_WIDTH_TO_FIT_PARENT, HINT_DIVIDE_WIDTH_EQUALLY, HINT_DIVIDE_HEIGHT_EQUALLY

Often a composite will have a set size and its children will want to expand themselves to take up all the available space. For example, the interaction with the Apply, Reset, and Cancel triggers in the dialog box (shown in the figure below) must have HINT_EXPAND_WIDTH_TO_FIT_PARENT set for it to expand itself to the far right edge of the dialog box. Another potential situation is a GenView within a GenPrimary; when the user resizes the primary, the view should probably expand itself to take up any extra space in the window.

A third example would be a series of triggers that want to be the same width in a vertical composite. This appears in many menus; each of the triggers expands itself to the width of the parent; the parent composite sizes its width to the largest of the triggers. An example taken from the complex dialog box is shown in the figure above. Code for this example is shown in Using Full Justification of Children .

Some more complex examples of expanding a group and justifying the elements are shown in the figure below. All of the examples use a justification hint combined with an expand-to-fit hint.

Code Display 12-3 Using Full Justification of Children

/* This code display shows the basic Goc code that will get the configuration
 * shown in the figure above. Other attributes are left out of the
 * definitions of the objects. This code display is supplemental to
 *  A Complex Dialog Box; most comments have been removed. */
@object GenInteractionClass TopInteraction = {
    GI_comp = @ParaInteraction, @ReplyInteraction;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@object GenInteractionClass ParaInteraction = {
    GI_comp = @FontInteraction, @JustInteraction, @StyleInteraction;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
@object GenInteractionClass ReplyInteraction = {
    GI_comp = @ApplyTrigger, @ResetTrigger, @CancelTrigger;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    HINT_FULL_JUSTIFY_CHILDREN_HORIZONTALLY;
    HINT_INCLUDE_ENDS_IN_CHILD_SPACING;
}
@object GenInteractionClass FontInteraction = {
    GI_comp = @RomanTrigger, @SansTrigger, @MonoTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@object GenInteractionClass JustInteraction = {
    GI_comp = @LeftTrigger, @RightTrigger, @CenterTrigger, @FullTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@object GenInteractionClass StyleInteraction = {
    GI_comp = @PlainTrigger, @BoldTrigger, @SuperTrigger, @SubTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
/* Triggers. All the upper triggers are essentially the same. Each is named as it
 * appears in the GI_comp fields above, and each has the corresponding
 * GI_visMoniker field. The RomanTrigger object is shown as an example. */
@object GenTriggerClass RomanTrigger = {
    GI_visMoniker = "Roman";
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
}
/* The Apply, Reset, and Cancel triggers do not have the hint shown above. */
@object GenTriggerClass ApplyTrigger = {
    GI_visMoniker = "Apply";
}

HINT_DIVIDE_WIDTH_EQUALLY and HINT_DIVIDE_HEIGHT_EQUALLY instruct a composite object to divide up its space along the affected dimension among the usable children. This hint can only suggest sizes for the children. The children themselves will decide if they can expand or contract to fit the requested space. (In general, children will not contract to fit a requested size, but they may expand.) Note: these hints will not work within GenValue objects; if you wish to use them on such an object, place them within a dummy GenInteraction object and use these hints on that object.

Typically, these hints work well in conjunction with HINT_EXPAND_WIDTH_TO_FIT_PARENT and HINT_EXPAND_HEIGHT_TO_FIT_PARENT. If these hints are not also in place (on a GenInteraction, for example) the division of width and/or height may be based on the children's default size; i.e., the parent will size itself based on total size of the children and then attempt to divide up its children's space, rather than the maximum allotted size for the parent, which is probably not desired.

Maximum, Minimum, Fixed, and Initial Sizes

HINT_INITIAL_SIZE, HINT_MAXIMINUM_SIZE, HINT_MINIMUM_SIZE, HINT_FIXED_SIZE

If you know certain sizing restrictions for window or interaction objects, you can set them with the following four hints. Each takes a parameter that details the appropriate height and width in points. The parameters are described for the hints.

HINT_INITIAL_SIZE
This hint sets the initial size of the object, if allowed. Other sizing restrictions may override this hint (such as the children being too large). This is primarily used with window objects (you will likely never use it for anything other than a GenPrimary, GenDisplay, or GenView), and it is effective only for when the object is first displayed. This hint takes an argument of type CompSizeHintArgs , a structure that defines a suggested width, height, and the number of children the object has.
HINT_FIXED_SIZE
This hint sets a fixed size for the object if one is allowed. Other sizing restrictions (such as children growing too large for the window) may override this hint occasionally. This hint can often be used for optimization to inhibit excessive geometry recalculations while on-screen. This hint takes the same parameters as HINT_INITIAL_SIZE , of type CompSizeHintArgs .
HINT_MAXIMUM_SIZE
This hint sets a desired maximum size for the object. Other geometry restrictions may override this hint when necessary. This hint is useful for GenText and GenView objects. It takes the same parameters as HINT_INITIAL_SIZE , of type CompSizeHintArgs .
HINT_MINIMUM_SIZE
This hint sets a desired minimum size for the object. Other geometry restrictions may override this hint when necessary. The hint takes the same parameters as HINT_INITIAL_SIZE , of type CompSizeHintArgs .

The following examples show different ways to set the initial size of an object. The third argument of the hint is the number of children in a particular line of a composite, when the composite is set up to wrap its children.

This example sets the initial size of the composite (probably a GenPrimary) to be half the screen's height and half the screen's width.

HINT_INITIAL_SIZE = {
    SST_PCT_OF_FIELD_WIDTH | PCT_50,
    SST_PCT_OF_FIELD_HEIGHT | PCT_50,
    0 };

This example sets the size of the composite to be 100 pixels high and 200 pixels wide.

HINT_FIXED_SIZE = {
    SST_PIXELS | 200,
    SST_PIXELS | 100,
    0 };

This example sets the composite's minimum size to be 10 average characters wide and 20 percent of the screen height tall.

HINT_MINIMUM_SIZE = {
    SST_AVG_CHAR_WIDTHS | 10,
    SST_PCT_OF_FIELD_HEIGHT | PCT_20,
    0 };
| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Justifying and Centering Children | Next: 2.5 Outlining the Composite


Outlining the Composite

Managing UI Geometry: 2.5 Arranging Your Generic Objects: Outlining the Composite

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Sizing Objects | Next: 2.6 Using Monikers
HINT_DRAW_IN_BOX

Often it is useful to outline a composite, especially if it is used for organizational purposes within another composite. You can use the hint HINT_DRAW_IN_BOX to have the UI automatically draw a box at the object's bounds as shown below.

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Sizing Objects | Next: 2.6 Using Monikers


Using Monikers

Managing UI Geometry: 2.6 Arranging Your Generic Objects: Using Monikers

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 Outlining the Composite | Next: 2.7 Using Custom Child Spacing

Nearly all generic objects will have visual monikers (usually text labels) that get drawn somewhere in or near the object. The moniker helps the user distinguish between different objects; for example, each trigger should have a text (or graphical) moniker giving some indication of what happens when the trigger is pressed.

Placing Monikers in Relation to Children

HINT_PLACE_MONIKER_ABOVE, HINT_PLACE_MONIKER_BELOW, HINT_PLACE_MONIKER_TO_LEFT, HINT_PLACE_MONIKER_TO_RIGHT, HINT_DO_NOT_USE_MONIKER, HINT_CENTER_MONIKER, HINT_PLACE_MONIKER_ALONG_LARGER_DIMENSION, HINT_ALIGN_LEFT_MONIKER_EDGE_WITH_CHILD

Monikers are extremely useful and dynamic--see the GenClass chapter, for full information on creating, manipulating, and using visual monikers. Monikers may be created or changed at run-time (often causing geometry updates), and all generic objects may be given monikers.

If you place a composite object's moniker to one side of its children, the geometry manager will ensure extra space in the composite gets allotted for the moniker. You can hint that the moniker should be placed above, to the right of, to the left of, or below the children. You can also hint that the moniker should not be used.

Most specific UIs will put the moniker to the left of the object's children by default. You can also center the moniker on the composite's children with HINT_CENTER_MONIKER .

You can also have the UI place the moniker along the object's larger dimension--to the left if the screen is wider than it is tall, or above if the screen is taller than it is wide. To do this, set the hint HINT_PLACE_MONIKER_ALONG_LARGER_DIMENSION .

If a moniker is placed above a group of children, HINT_ALIGN_LEFT_MONIKER_EDGE_WITH_CHILD ensures that left moniker edge coincides with the left edge of the first child below that moniker. This is usually the default behavior anyway, but different specific UIs may have different default behavior.

Justifying or Centering on Monikers

HINT_CENTER_CHILDREN_ON_MONIKERS, HINT_LEFT_JUSTIFY_MONIKERS

When you have several composites within a single composite, you can center the composites based on the position and size of the monikers. Using the hint HINT_CENTER_CHILDREN_ON_MONIKERS has the effect shown in the figure above: It aligns the child composites so their monikers are right-justified at the center. Adding HINT_LEFT_JUSTIFY_MONIKERS has a similar effect, except the monikers are left-justified.

Note that the two hints HINT_CENTER_CHILDREN_ON_MONIKERS and HINT_LEFT_JUSTIFY_MONIKERS are valid only for composites with one or more child composites, all of which have monikers placed to their left.

Removing Moniker Borders

HINT_NO_BORDERS_ON_MONIKERS

This hint removes borders that may appear by default around an object's moniker. For example, GenTriggers under most specific UIs have a rectangular border drawn around their moniker's borders. This hint removes these borders. It may be useful in cases where a custom border should be implemented, but in general should not be used to override the specific UI, as this may confuse the user.

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 Outlining the Composite | Next: 2.7 Using Custom Child Spacing


Using Custom Child Spacing

Managing UI Geometry: 2.7 Arranging Your Generic Objects: Using Custom Child Spacing

| Up: GEOS SDK TechDocs | Up | Prev: 2.6 Using Monikers | Next: 2.8 Allowing Children to Wrap
HINT_CUSTOM_CHILD_SPACING, HINT_CUSTOM_CHILD_SPACING_IF_LIMITED_SPACE, HINT_MINIMIZE_CHILD_SPACING

Normally, spacing of children within a composite is left entirely up to the specific UI. You can customize spacing, however, by using the hint HINT_CUSTOM_CHILD_SPACING . This hint takes a single argument of type SpecSizeSpec , described below.

The SpecSizeSpec structure defines both the spacing between children and how the spacing is determined. It exists as a record with two fields: The first field is a constant of type SpecSizeTypes , used to interpret the second field. The second field is data based on the type specified in the first field.

The different types allowable in the first field are

SST_PIXELS
Data specified in pixels.
SST_PCT_OF_FIELD_WIDTH
Data specified as a percentage of screen width. The data value is a fraction that gets multiplied by the width of the screen. Predefined fraction values to use in the data field are named PCT_ xx , where the xx represents any multiple of five between zero and 100 inclusive. You can get better accuracy by using your own value if desired, however.
SST_PCT_OF_FIELD_HEIGHT
Data specified as a percentage of screen height. As above except uses screen height rather than width.
SST_AVG_CHAR_WIDTHS
Data specified as a number that gets multiplied by the average width of a character in the font being used. The data may be between zero and 1023 inclusive.
SST_WIDE_CHAR_WIDTHS
Data is specified as a number that gets multiplied by the maximum width of the widest character in the font being used. The data may be between zero and 1023 inclusive.
SST_LINES_OF_TEXT
Data is specified as a number of text lines. The number will be multiplied by the height of a line of text in the font being used.

Both fields of the record are defined by using the bitwise-or operator. If you had a composite that wanted to line all its children up exactly next to each other, for example, you would specify the hint as follows:

@object GenInteractionClass @MyComp = {
    GI_comp = @child1, @child2, @child3;
    HINT_CUSTOM_CHILD_SPACING = SST_PIXELS | 0;
}

HINT_CUSTOM_CHILD_SPACING_IF_LIMITED_SPACE also takes a SpecSizeSpec to suggest a custom amount of spacing, but only implements this custom spacing if the specific UI determines that the children are already too tightly arranged. This hint may or may not be helpful, as the specific UI often allocates a minimum amount of spacing independent of this hint.

HINT_MINIMIZE_CHILD_SPACING ensures that child spacing is kept to an absolute minimum, even if this means that object's edges will touch (in color systems) or even overlap (in black and white systems).

| Up: GEOS SDK TechDocs | Up | Prev: 2.6 Using Monikers | Next: 2.8 Allowing Children to Wrap


Allowing Children to Wrap

Managing UI Geometry: 2.8 Arranging Your Generic Objects: Allowing Children to Wrap

| Up: GEOS SDK TechDocs | Up | Prev: 2.7 Using Custom Child Spacing | Next: 2.9 Object Placement
HINT_ALLOW_CHILDREN_TO_WRAP, HINT_WRAP_AFTER_CHILD_COUNT, HINT_DONT_ALLOW_CHILDREN_TO_WRAP, HINT_WRAP_AFTER_CHILD_COUNT_IF_VERTICAL_SCREEN

When a composite grows to fit its children and also must remain small enough to fit within its generic parent, it may wrap the children. This typically happens as a result of the user resizing a window smaller so the children of the composite no longer fit the window's width. Wrapping can occur in both the horizontal and the vertical, depending on the orientation of the composite object.

To allow a composite to wrap its children when necessary, apply the hint HINT_ALLOW_CHILDREN_TO_WRAP to the composite. If you want the children not to wrap, apply HINT_DONT_ALLOW_CHILDREN_TO_WRAP . An example of wrapping children appears below.

When HINT_WRAP_AFTER_CHILD_COUNT is applied and children are allowed to wrap, the composite will cause its children to wrap after a certain number are displayed. This hint takes a single integer as a parameter. For example, if you wanted three children on every line in the composite no matter how many children there were total, you would specify the composite as follows:

@object GenInteractionClass @MyComp = {
    GI_comp = @child1, @child2, @child3, @child4;
    HINT_ALLOW_CHILDREN_TO_WRAP;
    HINT_WRAP_AFTER_CHILD_COUNT = 3;
}

The configuration shown above is demonstrated in the figure below.

A variation, HINT_WRAP_AFTER_CHILD_COUNT_IF_VERTICAL_SCREEN , allows you to allow child wrapping based on whether or not the screen is "vertical." If the screen is taller than it is wide, this hint will have an effect like HINT_WRAP_AFTER_CHILD_COUNT ; if not, the hint will have no effect.

| Up: GEOS SDK TechDocs | Up | Prev: 2.7 Using Custom Child Spacing | Next: 2.9 Object Placement


Object Placement

Managing UI Geometry: 2.9 Arranging Your Generic Objects: Object Placement

| Up: GEOS SDK TechDocs | Up | Prev: 2.8 Allowing Children to Wrap | Next: 3 Positioning and Sizing Windows

As we have seen, generic objects can be arranged and distributed in several ways. You may also place generic objects within certain areas of your user interface that are not explicitly defined. For example, in many dialog boxes, a "reply bar" is typically present where reply triggers such as "OK," "Cancel," or "Apply" are located. You may specify objects to generically place themselves within such a reply bar, even though you may not know the exact location of that reply bar as the specific UI creates it.

Reply Bars

HINT_MAKE_REPLY_BAR, HINT_SEEK_REPLY_BAR

One special feature that many dialog boxes have is a reply bar . Typically, any dialog box that allows the user to set a number of options before applying them will have a reply bar. A reply bar usually has triggers in it for "Apply," "Cancel," and perhaps other functions (such as "Reset").

Note that many special dialog box types can have their reply bars built automatically according to the specific UI. See the Menus and Dialog Boxes chapter for full information on reply bars. If you want to create your own, however, you can with the hints in this section.

Although you have to declare each of the triggers that will appear in the reply bar, you can use HINT_MAKE_REPLY_BAR to set up the geometry appropriate for the specific UI. For example, Creating a Reply Bar gives the code that will create a reply bar.

Code Display 12-4 Creating a Reply Bar

/* This reply bar has three triggers, Apply, Reset, and Cancel.
 * The appropriate geometry is not determined until run-time. */
@object GenInteractionClass MyReplyBar = {
    GI_comp = @ApplyTrigger, @ResetTrigger, @CancelTrigger;
    HINT_MAKE_REPLY_BAR;
}
@object GenTriggerClass ApplyTrigger = {
    GI_visMoniker = "Apply"; }
@object GenTriggerClass ResetTrigger = {
    GI_visMoniker = "Reset"; }
@object GenTriggerClass CancelTrigger = {
    GI_visMoniker = "Cancel"; }

HINT_SEEK_REPLY_BAR instructs the generic object (usually a GenTrigger) to seek placement in the dialog box's reply bar. The specific UI will try to place the trigger in the reply bar of the first dialog box GenInteraction it finds that is not GIT_ORGANIZATIONAL.

Placing Objects in Menu Bars

HINT_SEEK_MENU_BAR, HINT_AVOID_MENU_BAR

Usually, it is left up to the specific UI whether it places any given object within a menu bar. You can suggest that objects be placed within the window's menu bar by placing HINT_SEEK_MENU_BAR on that object. Similarly, you can place HINT_AVOID_MENU_BAR on an object to suggest that it not be placed within the window's menu bar.

Placing Objects in Scroller Areas

HINT_SEEK_X_SCROLLER_AREA, HINT_SEEK_Y_SCROLLER_AREA, HINT_SEEK_LEFT_OF_VIEW, HINT_SEEK_TOP_OF_VIEW, HINT_SEEK_RIGHT_OF_VIEW, HINT_SEEK_BOTTOM_OF_VIEW

These hints affect the placement of generic objects within GenViews. The objects must be children of a GenView for the hints to take effect.

HINT_SEEK_X_SCROLLER_AREA and HINT_SEEK_Y_SCROLLER_AREA suggest that a generic object be placed alongside the scrollbar area--either the horizontal scrollbar or the vertical scrollbar, respectively--of the GenView object.

HINT_SEEK_LEFT_OF_VIEW , HINT_SEEK_TOP_OF_VIEW, HINT_SEEK_RIGHT_OF_VIEW and HINT_SEEK_BOTTOM_OF_VIEW suggest that the generic object be placed alongside the respective side of the GenView.

Placing Objects in Window Title Bars

HINT_SEEK_TITLE_BAR_LEFT, HINT_SEEK_TITLE_BAR_RIGHT

These hints suggest the placement of a generic object within a window's title bar. These hints are usually used on GenTriggers or GenInteractions; the objects involved should fit within the title bar area (i.e., be equivalent to tool bar icons). For each window with a title bar, only one object may have each of these hints, and the object must be a direct child of the windowed object.

Placement of Objects Popped Up

HINT_POPS_UP_TO_RIGHT, HINT_POPS_UP_BELOW
HINT_POPS_UP_TO_RIGHT
This hint instructs the specific UI to bring up the object to the right of its activating gadget, if the object is normally popped up below the activating gadget.
HINT_POPS_UP_BELOW
This hint instructs the specific UI to bring up the object below its activating gadget. This is usually the default behavior for objects that are popped up.
| Up: GEOS SDK TechDocs | Up | Prev: 2.8 Allowing Children to Wrap | Next: 3 Positioning and Sizing Windows


Positioning and Sizing Windows

Managing UI Geometry: 3 Positioning and Sizing Windows

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Arranging Your Generic Objects | Next: 3.1 Window Positioning

A generic object that appears as a window, such as an independently-displayable GenInteraction or a GenPrimary, has special needs for positioning and sizing itself, especially when appearing initially. Specific user interfaces may differ in how windows appear on the screen. In Motif, for instance, windows initially appear in one of several staggered positions. Primary and display objects extend by default to the right edge of the screen and to the top edge of the icon area at the bottom of the screen. Other windows are usually only as large as required by their children.

The SpecSizeWinPair structure is often used when specifying window sizes and positions. There are several hints which signal that a window should be positioned or sized depending on the position or size of another window, the size of the screen, or other conditions. The SpecSizeWinSpec structure allows you to specify an offset from another window, either by a number of points (remember that there are 72 points per inch), or by means of a ratio.

typedef struct {
    SpecWinSizeSpec			SWSP_x;
    SpecWinSizeSpec			SWSP_y;
} SpecWinSizePair;
typedef WordFlags SpecWinSizeSpec;
#define SWSS_RATIO 				0x8000
#define SWSS_SIGN 				0x4000
#define SWSS_MANTISSA 				0x3c00
#define SWSS_FRACTION 				0x03ff

The SpecSizeWinPair structure allows you to set up separate parameters for the x and y coordinates. For each coordinate, use the SWSS_RATIO flag to signal whether you are giving coordinates by means of a ratio instead of a constant. If you are using a ratio, use the SWSS_FRACTION, SWSS_MANTISSA, and SWSS_SIGN fields to specify the value of the ratio or use one of the following pre-defined constants to specify a ratio between zero and one:

#define PCT_0			0x000
#define PCT_5			0x033
#define PCT_10			0x066
#define PCT_15			0x099
#define PCT_20 			0x0cc
#define PCT_25 			0x100
#define PCT_30 			0x133
#define PCT_35 			0x166
#define PCT_40 			0x199
#define PCT_45 			0x1cc
#define PCT_50 			0x200
#define PCT_55 			0x233
#define PCT_60 			0x266
#define PCT_65 			0x299
#define PCT_70 			0x2cc
#define PCT_75 			0x300
#define PCT_80 			0x333
#define PCT_85 			0x366
#define PCT_90 			0x399
#define PCT_95 			0x3cc
#define PCT_100			0x3ff

To specify a constant offset, just use that offset, not setting the SWSS_RATIO flag. Not all windows will use the SpecWinSizePair structure.

Window Positioning

Determining Initial Size

On-Screen Behavior

Window Look and Feel

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Arranging Your Generic Objects | Next: 3.1 Window Positioning


Window Positioning

Managing UI Geometry: 3.1 Positioning and Sizing Windows: Window Positioning

| Up: GEOS SDK TechDocs | Up | Prev: 3 Positioning and Sizing Windows | Next: 3.2 Determining Initial Size
HINT_POSITION_WINDOW_AT_RATIO_OF_PARENT, HINT_POSITION_WINDOW_AT_MOUSE, HINT_STAGGER_WINDOW, HINT_CENTER_WINDOW, HINT_TILE_WINDOW, HINT_WINDOW_NO_CONSTRAINTS

GenClass has a number of hints used by the window classes to determine where the window appears initially and how the window is allowed to move about the screen. Specific UIs can override these hints when necessary or when they conflict with the UI's specifications. These hints are listed below.

HINT_POSITION_WINDOW_AT_RATIO_OF_PARENT
This hint suggests an initial position for the window based on its parent window's size and relative to the upper-left corner of the parent window. The suggested position is specified as a fraction of the parent window's size in a SpecWinSizePair structure. This structure consists of two fields, the X position and the Y position of the new window's origin. Predetermined fraction values are names PCT_ xx , where xx is a multiple of five between zero and 100 inclusive. For example, to have the window object's origin start out in the center of its parent, specify the hint as follows:
HINT_POSITION_WINDOW_AT_RATIO_OF_PARENT = {
    SWSS_RATIO | PCT_25, SWSS_RATIO | PCT_60
};
HINT_POSITION_WINDOW_AT_MOUSE
This hint will position the origin of the new window at the mouse pointer.
HINT_STAGGER_WINDOW
This hint indicates that the window's origin should be placed just below and to the right of the origin of the last staggered window.
HINT_CENTER_WINDOW
This hint centers the window in its parent window.
HINT_TILE_WINDOW
This hint tiles the window with other windows that are also marked HINT_TILE_WINDOW within the parent window. This hint is not currently supported.
HINT_WINDOW_NO_CONSTRAINTS
This hint removes all constraints upon the positioning of a window. It should only be used as the final attempt to position the window if other methods do not work to your satisfaction.
| Up: GEOS SDK TechDocs | Up | Prev: 3 Positioning and Sizing Windows | Next: 3.2 Determining Initial Size


Determining Initial Size

Managing UI Geometry: 3.2 Positioning and Sizing Windows: Determining Initial Size

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Window Positioning | Next: 3.3 On-Screen Behavior
HINT_EXTEND_WINDOW_TO_BOTTOM_RIGHT, HINT_EXTEND_WINDOW_NEAR_BOTTOM_RIGHT, HINT_SIZE_WINDOW_AS_DESIRED, HINT_SIZE_WINDOW_AS_RATIO_OF_PARENT, HINT_SIZE_WINDOW_AS_RATIO_OF_FIELD, HINT_USE_INITIAL_BOUNDS_WHEN_RESTORED

GenClass has a number of hints used by the window classes to determine the size of the window object initially and how the window is allowed to be resized on the screen. Specific UIs can override these hints when necessary or when they conflict with the UI's specifications. These hints are listed below.

HINT_EXTEND_WINDOW_TO_BOTTOM_RIGHT
This hint extends the window's bounds so its lower right corner is located at the lower right corner of its parent window.
HINT_EXTEND_WINDOW_NEAR_BOTTOM_RIGHT
This hint extends the window's bounds so its lower right corner is located near the lower right corner of its parent window. Enough room will be left for any icon area that may be present. Most major applications will apply this hint to their GenPrimary objects.
HINT_SIZE_WINDOW_AS_DESIRED
This hint allows the window to size itself according to the combined size of its children.
HINT_SIZE_WINDOW_AS_RATIO_OF_PARENT
This hint sizes the window to a ratio of its parent window. It takes a single parameter of type SpecWinSizePair , described under HINT_POSITION_WINDOW_AT_RATIO_OF_PARENT . Instead of coordinates, however, the ratios in the structure are width and height.
HINT_SIZE_WINDOW_AS_RATIO_OF_FIELD
This hint sizes the window to a ratio of the field window's size. It takes a SpecWinSizePair parameter indicating the ratio. See above.
HINT_USE_INITIAL_BOUND_WHEN_RESTORED
This hint instructs the window to come on-screen with its original values rather than the state of the window when it was saved to state.
| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Window Positioning | Next: 3.3 On-Screen Behavior


On-Screen Behavior

Managing UI Geometry: 3.3 Positioning and Sizing Windows: On-Screen Behavior

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Determining Initial Size | Next: 3.4 Window Look and Feel
HINT_KEEP_INITIALLY_ONSCREEN, HINT_DONT_KEEP_INITIALLY_ONSCREEN, HINT_KEEP_PARTIALLY_ONSCREEN, HINT_DONT_KEEP_PARTIALLY_ONSCREEN, HINT_KEEP_ENTIRELY_ONSCREEN, HINT_KEEP_ENTIRELY_ONSCREEN_WITH_MARGIN, HINT_NOT_MOVABLE
HINT_KEEP_INITIALLY_ONSCREEN
This hint will make the window resize itself to fit entirely within its parent window. Most windows will inherently know how to come up fully on-screen; some specific UIs do not implement it by default for optimization purposes.
HINT_DONT_KEEP_INITIALLY_ONSCREEN
This hint will allow the window to come up whether it will be fully on-screen or not. If the default is to keep the window entirely on-screen and the UI allows this hint, the window will be allowed to open partially off-screen.
HINT_KEEP_PARTIALLY_ONSCREEN
This hint indicates that when the user moves the window, it will be forced to remain partially on-screen.
HINT_DONT_KEEP_PARTIALLY_ONSCREEN
This hint indicates that the user can move the window around the screen at will, even if the window becomes completely obscured under its parent.
HINT_KEEP_ENTIRELY_ONSCREEN
This hint indicates that when the user moves the window, it will be forced to remain entirely on-screen.
HINT_KEEP_ENTIRELY_ONSCREEN_WITH_MARGIN
This hint indicates that when the user moves the window, it will be forced to remain on-screen. An additional margin is added to the edge of the screen to keep the window from touching the edge as well; this margin is defined by the specific UI.
HINT_NOT_MOVABLE
This hint instructs the specific UI to make the windowed object not movable. This hint should be avoided if at all possible.
| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Determining Initial Size | Next: 3.4 Window Look and Feel


Window Look and Feel

Managing UI Geometry: 3.4 Positioning and Sizing Windows: Window Look and Feel

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 On-Screen Behavior
HINT_WINDOW_NO_TITLE_BAR, HINT_WINDOW_NO_SYS_MENU, HINT_WINDOW_MINIMIZE_TITLE_BAR, HINT_WINDOW_ALWAYS_DRAW_WITH_FOCUS

HINT_WINDOW_NO_TITLE_BAR instructs the specific UI to remove the title bar from a windowed object (dialog box, GenPrimary or GenDisplay). Use this hint with extreme prejudice as most of a window's functionality will not be accessible without a title bar.

HINT_WINDOW_NO_SYS_MENU instructs the specific UI to remove the system menu (or close button) from a windowed object. Again, do not use this hint unless necessary, as it overrides important features of windowed objects.

HINT_WINDOW_MINIMIZE_TITLE_BAR instructs the specific UI to minimize the area that the window's title bar occupies, if possible. (This hint is designed for use in smaller PDA-style devices.) Note that this hint usually takes away any system menus that may normally be included with the title bar. As a result, make sure that you provide another means to dismiss the window. Also note that if a window containing this hint is disabled, the window will be dismissed by the system.

HINT_WINDOW_ALWAYS_DRAW_WITH_FOCUS instructs the specific UI to draw the window as if it always contains the focus, including "high-lighting" the window's title bar, if possible. Only use this hint if it is absolutely necessary because it may confuse the user and it violates the predominant focus paradigm.

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 On-Screen Behavior


Help Object Library

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 What Is Help?
| One-page Version of this Chapter for Easy Printing

On-line help is important for many applications, especially in the world of graphical interfaces and consumer devices. Many users will opt for exploration of an application rather than reading through the manual, and on-line help can assist them.

The GEOS on-line help system is an information viewing tool to be used by most if not all GEOS applications. It is automated, and adding it to an application is simple. All you need to do is create your help documents with GeoWrite and then add a few help attributes to your application's generic UI objects.

1 What Is Help?
1.1 Normal Help
1.2 First Aid
1.3 Simple Help
2 Adding Help to Your Application
2.1 Help Contexts and Help Triggers
2.2 Adding Default Normal Help
2.3 Bringing Up Help on the Fly
3 Customizing Help
3.1 Bringing Up Initial Help
3.2 Adding the HelpControl
3.3 Sizing the Help Dialog Box
3.4 Managing Help Types
3.5 Managing Help Files
3.6 Customizing the Pointer Image
3.7 Changing the Help Features
4 Creating Help Files
4.1 Enabling the Help Editor
4.2 Organizing and Writing the Text
4.3 Defining Files and Contexts
4.4 Using Hyperlinks
4.5 Generating the Help Files
5 HelpControlClass Reference

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 What Is Help?
| One-page Version of this Chapter for Easy Printing


What Is Help?

Help Object Library: 1 What Is Help?

| Up: GEOS SDK TechDocs | Up | Down | Prev: Help Object Library | Next: 2 Adding Help to Your Application

GEOS provides four basic flavors of help ranging from simple descriptions to a full-featured information viewer complete with linked text and embedded graphics. GEOS help is both context-sensitive and random-access; a user may get help on the current dialog box, for example, or may invoke your help system's table of contents directly.

How a user gets help depends on the system in use: A standard desktop PC will allow keyboard access with the F1 key as well as help triggers throughout the application's UI. A small-screen device may hide the help triggers and allow only a special "hard icon," special pen gesture, or other means of activation to save screen space. (Such decisions are up to the specific UI.)

Nearly all applications will use "Normal Help," which provides a full-featured help viewer. The four types available are described below.

Normal Help

First Aid

Simple Help

| Up: GEOS SDK TechDocs | Up | Down | Prev: Help Object Library | Next: 2 Adding Help to Your Application


Normal Help

Help Object Library: 1.1 What Is Help?: Normal Help

| Up: GEOS SDK TechDocs | Up | Prev: 1 What Is Help? | Next: 1.2 First Aid

Most applications will include Normal Help , which provides a full-featured help viewer complete with embedded graphics and hyperlinked text. Normal help allows you to organize your help data in any manner; it has a single Table of Contents (TOC) page, which the user can access at any point in viewing help. Your TOC page should hyperlink to any other pages associated or appropriate (it depends on the information you're presenting). See the figure below for a sample of Normal Help.

Normal Help, like the other help types (see below), is also context-sensitive. You must define the contexts, but the help system will bring up the proper help page for the context. If the user clicks the help trigger on the primary window, for example, the TOC page is presented (by default). If the user clicks the trigger in a dialog box, however, the help page for that dialog box is presented.

| Up: GEOS SDK TechDocs | Up | Prev: 1 What Is Help? | Next: 1.2 First Aid


First Aid

Help Object Library: 1.2 What Is Help?: First Aid

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Normal Help | Next: 1.3 Simple Help

First Aid help, like Normal Help, provides a full-featured help viewer. First Aid files are also organized by context, but the information is presented in a different format. Whereas Normal Help is organized around the structure of the application, First Aid is presented in a book format.

When a user clicks the First Aid trigger, the help page for the proper context is displayed; this will typically be taken from the current Focus object (as in Standard Help). From there, the user may navigate throughout the help "book" via both hyperlinks in the text and three buttons at the top of the help window: Contents, Chapter, and Article. An illustration of the First Aid help box is shown below.

To use First Aid, an application needs its own HelpControl object. Simply add one as a child of your GenApplication and set its help type to HT_FIRST_AID . The help controller must also be put on the application's active list.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Normal Help | Next: 1.3 Simple Help


Simple Help

Help Object Library: 1.3 What Is Help?: Simple Help

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 First Aid | Next: 2 Adding Help to Your Application

Simple Help provides a small scrolling dialog box with help text. This is useful for places where very simple help is required, perhaps only a few lines to illustrate a simple operation. Simple Help may be most useful for appliance-type applications; most applications will use Standard Help or First Aid help. A sample screen of Simple help is shown below.

Simple help, like First Aid, requires that the application have its own HelpControl object and that the help controller be on the active list. Text in simple help does not link, so all help text pertaining to the item must appear on a single page.

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 First Aid | Next: 2 Adding Help to Your Application


Adding Help to Your Application

Help Object Library: 2 Adding Help to Your Application

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 What Is Help? | Next: 3 Customizing Help

For most applications, simply creating help files and adding ATTR_GEN_HELP_CONTEXT to certain generic objects will be sufficient. Other applications, though, such as those that use First Aid help, will require the addition of a HelpControl object (Normal help uses a system-provided and UI-run help controller).

The HelpControl object interacts with the GenClass and MetaClass aspects of generic objects to create help triggers, put up help screens, and determine which help file is used and what help context is displayed. For normal help, you don't need a HelpControl object; the system provides one. You do, however, have to know how to declare help contexts and work with help files.

Help Contexts and Help Triggers

Adding Default Normal Help

Bringing Up Help on the Fly

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 What Is Help? | Next: 3 Customizing Help


Help Contexts and Help Triggers

Help Object Library: 2.1 Adding Help to Your Application: Help Contexts and Help Triggers

| Up: GEOS SDK TechDocs | Up | Prev: 2 Adding Help to Your Application | Next: 2.2 Adding Default Normal Help
 ATTR_GEN_HELP_CONTEXT,  HINT_PRIMARY_NO_HELP_BUTTON

The help controller displays help pages based on an object's context. An object sets its context with ATTR_GEN_HELP_CONTEXT , which holds the name of the context. If an object does not have an assigned context, it will query up the tree until one of its ancestors assigns a context. The help control will display the help page with the same name as the assigned context.

This attribute has some side effects. If placed on a GenPrimary object, it will create the help trigger that appears in the application's title bar (unless other restrictions are applied--see below). If placed on a dialog box GenInteraction, it will create a help trigger in the dialog box (placement of the trigger is subject to the specific UI, but it will normally be on the far right of the reply bar). Both of these triggers invoke help with the context of their objects.

Almost always, the GenPrimary's help context should be "TOC" to bring up the TOC help page. This is the default context, so if you don't specify a context for any object, help will always try to bring up the TOC page.

As stated above, ATTR_GEN_HELP_CONTEXT will by default create help triggers in certain situations. There are two times when creation of these help triggers will be suppressed: First, on small-screen devices or other situations wherein help triggers are not desired. In this situation, the .INI file will have a certain help option turned off. The simplest way to do this is to set

 [uiFeatures]
 helpOptions = 1

in the .INI file. This corresponds to the single help option UIHO_HIDE_HELP_BUTTONS and will suppress the help triggers. Both the F1 key (or equivalent) and MSG_META_BRING_UP_HELP will still invoke help properly.

Second, you can suppress only the GenPrimary's help trigger by adding HINT_PRIMARY_NO_HELP_BUTTON to the GenPrimary. Other help triggers will appear as dictated by the other requirements.

You can create your own help triggers if you like, though there is rarely a need to. Help triggers can be normal GenTrigger objects, with special settings. If the trigger is in a dialog box, it should have IC_HELP as its interaction type ( ATTR_GEN_TRIGGER_INTERACTION_COMMAND ). Otherwise, it should send MSG_META_BRING_UP_HELP to the object that should provide the context for the help. Also, the trigger should have its moniker specified with ATTR_GEN_DEFAULT_MONIKER , with the value GDMT_HELP . This will ensure your help trigger looks like other help triggers.

Help triggers you create, however, will not be suppressed by the system. Thus, if you want your application to be adaptable to small-screen UIs, you should only use system-provided help triggers.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Adding Help to Your Application | Next: 2.2 Adding Default Normal Help


Adding Default Normal Help

Help Object Library: 2.2 Adding Help to Your Application: Adding Default Normal Help

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Help Contexts and Help Triggers | Next: 2.3 Bringing Up Help on the Fly

To add normal help to your application, you only need to add ATTR_GEN_HELP_CONTEXT to the objects that should have help contexts. You can look at the HelpSamp sample application, though that does much more than you need. The code shown in Objects with Help Contexts is taken from HelpSamp and shows a dialog box with a list, in which each entry has its own context.

You will want to put ATTR_GEN_HELP_CONTEXT on any generic object that could conceivably need help text. Nearly all applications will put help only on GenInteractions and GenPrimaries, but it is available for all generic objects. For example, items in a GenItemGroup may have help contexts (as shown in HelpSamp) so that when the user hits the F1 key, help will be invoked for that particular item. Likewise, menus and menu items may have their own help contexts.

Nearly all GenPrimary objects should have the context "TOC" set for them.

Code Display 13-1 Objects with Help Contexts

/*     The TypeDialog box has its own help context: Help Types. This will cause
 * the UI to put a help trigger in the dialog's reply bar and, when that trigger
 * is clicked, will cause the system's help object to bring up the normal help
 * viewer on the "Help Types" page in the current help file.
 *     Each item in the GenItemGroup has its own help context. The list itself
 * does not have a context because if the user presses the F1 key, the help system
 * will bring up the selected item's help. If it were a list in which there could
 * be zero items selected, then the list might have its own context as well. */
@object GenInteractionClass TypeDialog = {
    GI_visMoniker = 'T', "Change Help Type";
    GI_comp = @TypeList;
    GII_visibility = GIV_DIALOG;
    GII_type = GIT_PROPERTIES;
    ATTR_GEN_HELP_CONTEXT = "Help Types";
}
@object GenItemGroupClass TypeList = {
    GI_comp = @NormalItem, @FirstAidItem, @SimpleItem;
    GIGI_numSelections = 1;
    GIGI_selection = HT_NORMAL_HELP;
    GIGI_applyMsg = MSG_HELPSAMP_SET_TYPE;
    GIGI_destination = process;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@object GenItemClass NormalItem = {
    GI_visMoniker = "Normal Help";
    GII_identifier = HT_NORMAL_HELP;
    ATTR_GEN_HELP_CONTEXT = "Normal Help";
}
@object GenItemClass FirstAidItem = {
    GI_visMoniker = "First Aid";
    GII_identifier = HT_FIRST_AID;
    ATTR_GEN_HELP_CONTEXT = "First Aid";
}
@object GenItemClass SimpleItem = {
    GI_visMoniker = "Simple Help";
    GII_identifier = HT_SIMPLE_HELP;
    ATTR_GEN_HELP_CONTEXT = "Simple Help";
}
| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Help Contexts and Help Triggers | Next: 2.3 Bringing Up Help on the Fly


Bringing Up Help on the Fly

Help Object Library: 2.3 Adding Help to Your Application: Bringing Up Help on the Fly

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Adding Default Normal Help | Next: 3 Customizing Help
 HelpSendHelpNotification()

The Help Controller library provides a routine to bring up a help context, or to switch to a help context, at any time. This routine, HelpSendHelpNotification() , generates the proper notification event using the values you pass, then sends that notification to the help control object you've selected. This routine is detailed in the routines reference manual.

This routine becomes most useful when, for example, you want help on visible objects in a GenView or in a document. To call up a help viewer with a specific help file and context, you would have the visible object intercept both your special "help key" (e.g. the user clicks the middle mouse button) and MSG_META_BRING_UP_HELP , then call HelpSendHelpNotification() . In the handler for the input event, the object should not call the superclass. One example of this could be a special diagram of a floorplan, with each room a visible object; each "room" would bring up a different help context describing the characteristics of the room.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Adding Default Normal Help | Next: 3 Customizing Help


Customizing Help

Help Object Library: 3 Customizing Help

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Adding Help to Your Application | Next: 4 Creating Help Files

If you want to use one of the other help types, or if you want to use multiple help types in a single application, you must do more than just apply help contexts to objects. You can customize several things about the help system:

  • Help Type
    You can set or change the type of help offered by your application. You must have one HelpControl object for each type of help offered. You can set different objects to display or use different help types.
  • Help File
    Any object can specify the name of the file from which it draws its help. You can set the help file statically, change it dynamically, or have it defined by the GEOS.INI file.
  • Pointer Image
    The help system lets you define a custom pointer image to be used when the mouse pointer is over a hyperlink. You define the image, and the help controller uses that instead of the standard link pointer.
  • Help Features
    Because the HelpControl object is a controller, it has a certain feature set that may be turned on and off with ATTR_GEN_CONTROL_REQUIRE_UI and ATTR_GEN_CONTROL_PROHIBIT_UI . The features include the buttons presented to the user for navigating the help file and for closing the dialog box.

You can even create special "viewer" applications based on the help object. To make any of the above customizations, however, you must add one or more HelpControl objects to your application.

Bringing Up Initial Help

Adding the HelpControl

Sizing the Help Dialog Box

Managing Help Types

Managing Help Files

Customizing the Pointer Image

Changing the Help Features

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Adding Help to Your Application | Next: 4 Creating Help Files


Bringing Up Initial Help

Help Object Library: 3.1 Customizing Help: Bringing Up Initial Help

| Up: GEOS SDK TechDocs | Up | Prev: 3 Customizing Help | Next: 3.2 Adding the HelpControl
 ATTR_HELP_INITIAL_HELP_FILE,  ATTR_HELP_INITIAL_HELP

An object may use ATTR_HELP_INITIAL_HELP_FILE and ATTR_HELP_INITIAL_HELP to bring up a help context when the object becomes usable. ATTR_HELP_INITIAL_HELP_FILE defines the help file from which the help is to be taken; it is only used if ATTR_HELP_INITIAL_HELP is also present. This one defines the help context to be brought up on startup.

These attributes are used normally only by help-viewer applications, as it is unlikely help will be required on startup. Normally, help is brought up by the user pressing a help trigger.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Customizing Help | Next: 3.2 Adding the HelpControl


Adding the HelpControl

Help Object Library: 3.2 Customizing Help: Adding the HelpControl

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Bringing Up Initial Help | Next: 3.3 Sizing the Help Dialog Box

To add a help controller to your application, simply declare an instance of HelpControlClass and add it as a child of your GenApplication, adding it to the active list. The code in Adding Help Controllers is taken from the HelpSamp sample application and illustrates what you have to do to support custom help. The steps are

  • Declare the HelpControl
    Declare an object of HelpControlClass . You must set its HCI_helpType field to the HelpType value it will manage. Because the standard behavior is to have the help window always appear on top of other dialog boxes, you should set the window priority accordingly (as shown). Also, because most controllers will come up disabled, set the GS_ENABLED flag to have the help buttons enabled when help is first invoked.
  • Add the HelpControl to the active list
    The HelpControl, as all controllers, must be put on the active list, the MGCNLT_ACTIVE_LIST GCN list. All of your help controllers must be on this list.
  • Set the help type in the GenApplication
    If you're simply using a different help type, set it in the GenApplication object with ATTR_GEN_HELP_TYPE . The sample code shown does not have this attribute because the application uses multiple types but defaults to Normal Help. The user has the option of changing the help type; when the type is changed, ATTR_GEN_HELP_TYPE is added to the GenApplication dynamically.

Other than the above customizations, using another help type is as simple as using Normal Help. Other considerations must be observed when creating the help files, but the code difference is simple.

Code Display 13-2 Adding Help Controllers

/*    The GenApplication has two help controllers as its children.
 * FirstAidHelpControl manages First Aid help, and SimpleHelpControl manages
 * Simple Help. In addition, the Normal Help controller--provided by the
 * system--manages Normal Help. Both custom help controllers must be put both on
 * the active list and on the GAGCNLT_NOTIFY_HELP_CONTEXT_CHANGE list.
 *    Normally, the GenApplication would have ATTR_GEN_HELP_TYPE declaring the
 * help type used. This application, however, defaults to HT_SYSTEM_HELP and
 * therefore does not need the attribute. */
@object GenApplicationClass HelpSampApp = {
    GI_visMoniker = list { @HelpSampTextMoniker };
    GI_comp = @HelpSampPrimary, @FirstAidHelpControl, @SimpleHelpControl;
    gcnList(MANUFACTURER_ID_GEOWORKS,GAGCNLT_WINDOWS) = @HelpSampPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_NOTIFY_HELP_CONTEXT_CHANGE) =                                     @FirstAidHelpControl, @SimpleHelpControl;
    gcnList(MANUFACTURER_ID_GEOWORKS, MGCNLT_ACTIVE_LIST) =
                @FirstAidHelpControl, @SimpleHelpControl;
}
@visMoniker HelpSampTextMoniker = "C Sample App with Help";
/*    The help controllers may manage at most one HelpType each. Thus, you must
 * set the HCI_helpType field so the controller knows what type of help it
 * manages. */
@object HelpControlClass FirstAidHelpControl = {
    GI_states = @default | GS_ENABLED;
    HCI_helpType = HT_FIRST_AID;
}
@object HelpControlClass SimpleHelpControl = {
    GI_states = @default | GS_ENABLED;
    HCI_helpType = HT_SIMPLE_HELP;
}
| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Bringing Up Initial Help | Next: 3.3 Sizing the Help Dialog Box


Sizing the Help Dialog Box

Help Object Library: 3.3 Customizing Help: Sizing the Help Dialog Box

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Adding the HelpControl | Next: 3.4 Managing Help Types
 HINT_HELP_TEXT_FIXED_SIZE, HINT_HELP_NOT_RESIZABLE

Depending on what you're using help for, you may want to keep the help dialog box a fixed size, or you may wish to make it not resizable. For both cases, you must use a custom help controller object and set one of the following hints on the controller:

HINT_HELP_TEXT_FIXED_SIZE
This hint is exactly the same as GenClass ' HINT_FIXED_SIZE ; it takes a CompSizeHintArgs structure as its extra data and sets the help controller's dialog box to the fixed size.
HINT_HELP_NOT_RESIZABLE
Normally, help controllers set up as GIV_DIALOG are resizable. This hint will counteract that behavior and make the window not resizable.
| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Adding the HelpControl | Next: 3.4 Managing Help Types


Managing Help Types

Help Object Library: 3.4 Customizing Help: Managing Help Types

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Sizing the Help Dialog Box | Next: 3.5 Managing Help Files
 HelpType,  ATTR_GEN_HELP_TYPE,  MSG_META_GET_HELP_TYPE

The brand of help your application provides is defined by its HelpType . This value is set in the HelpControl object's HCI_helpType instance field. For normal help, you don't need to set anything; the type will automatically be HT_SYSTEM_HELP for the system help object (you should not set this type for your own help control objects).

There are five other help types you can use: HT_NORMAL_HELP, HT_FIRST_AID, HT_STATUS_HELP, HT_SIMPLE_HELP, and HT_SYSTEM_MODAL_HELP. You should only explicitly use HT_NORMAL_HELP if you want the normal help setup with some customizations or you are creating a viewer application. The others you must use explicitly if you want to provide those types of help.

(Note: HT_SYSTEM_MODAL_HELP is used for providing help within system-modal dialog boxes only. It is rare that an application will ever use system-modal dialog boxes, but if yours does and you need to provide help within it, use this special type. Such dialog boxes should be given the attribute ATTR_GEN_HELP_TYPE with this type to distinguish them from other dialogs.)

To set an application's help type, you have to first supply a HelpControl object as a child of the GenApplication and set its HCI_helpType field to the proper help type. You also have to put your HelpControl object on the GenApplication's GAGCNLT_NOTIFY_HELP_CONTEXT_CHANGE GCN list. You may have one HelpControl object for each help type supported by your application.

Examples of how to set up the proper objects are shown above in Adding Help Controllers .

Any object may have any help type associated with it. You could have, for example, one dialog box set to Normal Help and another set for Simple Help. This is unusual, and you will usually want to stick to a single help type within one application. To set an object's help type, use ATTR_GEN_HELP_TYPE . You can retrieve the current help type of an object by sending it MSG_META_GET_HELP_TYPE .

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Sizing the Help Dialog Box | Next: 3.5 Managing Help Files


Managing Help Files

Help Object Library: 3.5 Customizing Help: Managing Help Files

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Managing Help Types | Next: 3.6 Customizing the Pointer Image
 ATTR_GEN_HELP_FILE,  ATTR_GEN_HELP_FILE_FROM_INIT_FILE,  MSG_META_GET_HELP_FILE,  MSG_META_SET_HELP_FILE,  ATTR_GEN_CONTROL_DO_NOT_USE_LIBRARY_NAME_FOR_HELP

The help controller gets the text it displays from help files. Help files are generated by the help editor (a modified GeoWrite) from GeoWrite documents and are located in USERDATA\HELP. An application may use a single help file or multiple help files, and hyperlinks may extend across files.

The help controller figures out the help file to use by looking for ATTR_GEN_HELP_FILE on the generic object that brings up the help. For example, if the user clicks a help trigger in a dialog box, the help controller first gets the context of the dialog box (contexts are described in Help Contexts and Help Triggers ) and will then get its help file. If the dialog box has an ATTR_GEN_HELP_FILE specifying a particular help file, the help controller will try to use that file.

If the dialog box does not have that attribute, the help controller queries up the generic tree until it either finds an ancestor with the attribute or gets to the GenApplication object. If no help file is specified, the help controller will by default use a file with the same name as the geode's permanent name with the extender characters removed. For example, in the sample application HelpSamp, which has the permanent name helpsamp.app (which is defined in the helpsamp.gp file), the default file name would be helpsamp .

An object can use ATTR_GEN_HELP_FILE_FROM_INIT_FILE to specify that its help file should be garnered from the GEOS.INI file. If this attribute is used, the help controller will look in the GEOS.INI file for a category of the same name as the application and a key named "helpfile." Thus, to set the help file to "My Own Help File" for the HelpSamp application, you could add the following to your GEOS.INI file:

[HelpSamp]
helpfile = My Own Help File

If, however, no object has ATTR_GEN_HELP_FILE_FROM_INIT_FILE , this entry will not be noticed by the help controller.

Controller objects, which typically exist within their own libraries, may use the help files provided by the application that includes them rather than the help file normally used by the library. To have a controller do this, set ATTR_GEN_CONTROL_DO_NOT_USE_LIBRARY_NAME_FOR_HELP for the controller in your application. This will have the controller continue querying up the generic tree when searching for the proper help file to use.

The help file used by a given object may also be changed dynamically with MSG_META_SET_HELP_FILE . This message takes a pointer to the new file's name and sets the object's ATTR_GEN_HELP_FILE attribute to the new file. This message is used by the HelpSamp sample application.

The help file can be retrieved with MSG_META_GET_HELP_FILE . This message returns the name of the file being used by the recipient generic object.

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Managing Help Types | Next: 3.6 Customizing the Pointer Image


Customizing the Pointer Image

Help Object Library: 3.6 Customizing Help: Customizing the Pointer Image

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Managing Help Files | Next: 3.7 Changing the Help Features
 ATTR_HELP_CUSTOM_POINTER_IMAGE,  MSG_HELP_CONTROL_GET_POINTER_IMAGE

The help controller changes the pointer image when the pointer is over a link in the help text. This is so the user knows where to click and expects a link to be followed. Applications that want custom pointers can set the "link" pointer image with ATTR_HELP_CUSTOM_POINTER_IMAGE . This attribute takes an optr to a custom pointer image definition.

The pointer image is a PointerDef structure and must be stored in a chunk in a sharable, non-duplicated data block. Note also that to use a custom pointer image, you must explicitly declare a HelpControl object, even for Normal Help.

You can retrieve the link pointer image by sending MSG_HELP_CONTROL_GET_POINTER_IMAGE to the HelpControl object. This will return the current image in use, either a custom image or the default. This message is detailed on MSG_HELP_CONTROL_GET_POINTER_IMAGE .

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Managing Help Files | Next: 3.7 Changing the Help Features


Changing the Help Features

Help Object Library: 3.7 Customizing Help: Changing the Help Features

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Customizing the Pointer Image | Next: 4 Creating Help Files

Because the HelpControl is a subclass of GenControlClass , you can change the features it offers both in your .goc file and dynamically. For complete information, see the Controllers chapter. The features of the help controller are detailed in HelpControlClass Reference .

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Customizing the Pointer Image | Next: 4 Creating Help Files


Creating Help Files

Help Object Library: 4 Creating Help Files

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Customizing Help | Next: 5 HelpControlClass Reference

Perhaps the most involved step of adding help to your application is actually creating the help files. Help files are special, compressed versions of GeoWrite files that reside in the \USERDATA\HELP directory. There are several steps to creating help files:

  1. Enable the Help Editor
    The Help Editor is simply a special version of GeoWrite.
  2. Organize and write the text
    Depending on your application, your help text, and how you wish to present it, this can be simple or complex. You must keep several things in mind when organizing your help.
  3. Define files and contexts
    Each help file must have its contexts named, and each context must be defined. If you are using multiple help files, you must define the help contexts for each file in use, and you must "define" each file being used.
  4. Set contexts
    Each page of help must have its own context name.
  5. Set hyperlinks
    After all contexts are named and set, you may set hyperlinks to them.
  6. Generate the help files
    When the help text is ready, you must generate the help file(s). They will then be available for your application to open and view.

Enabling the Help Editor

Organizing and Writing the Text

Defining Files and Contexts

Using Hyperlinks

Generating the Help Files

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Customizing Help | Next: 5 HelpControlClass Reference


Enabling the Help Editor

Help Object Library: 4.1 Creating Help Files: Enabling the Help Editor

| Up: GEOS SDK TechDocs | Up | Prev: 4 Creating Help Files | Next: 4.2 Organizing and Writing the Text

The Help Editor is GeoWrite with the Help Editor option turned on. To turn on this option, manually edit your .INI file to add the category and key as follow:

[configure]
 helpEditor = true

Without the above lines, you can't access the Help Editor. After adding them, start up GeoWrite and change the user level. Choose "Customize" and turn on the Help Editor option at the end of the list. When you apply the change, a new "Help Editor" menu should appear towards the right of the menu bar.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Creating Help Files | Next: 4.2 Organizing and Writing the Text


Organizing and Writing the Text

Help Object Library: 4.2 Creating Help Files: Organizing and Writing the Text

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Enabling the Help Editor | Next: 4.3 Defining Files and Contexts

If your help text is simple and straightforward, you can probably dive right in and write it. If it is not, though, you should spend some time up front organizing it, naming the contexts and files, and figuring out where hyperlinks will be placed. Defining contexts and links as you're writing can quickly get confusing, even for experienced writers.

Most help text will be configured for Normal Help. This type of help typically has a table of contents page (TOC) and numerous other contexts, many of which are linked to each other. Normally, a help context (page) will be accessible through two means: First, the user could click a help trigger or hit F1 at the right time to bring up the proper context. In this case, the user can not "go back" to the previous context, although he can get to the TOC page by clicking the Contents button. Second, the user could navigate through the help system via hyperlinks and get to the context.

Not all contexts may be available through links, however; some may be accessible only through clicking a help trigger or hitting F1. Other contexts may be accessible only via navigation of hyperlinks in the help document. You should plan this out in advance so you can write effective text and set your links properly.

Organizing the Help

Perhaps the most straightforward help organization is a simple listing of topics, each of which is a hyperlink to another help screen. For example, the "Change File" help screen in the HelpSamp sample application's default file gives a brief description of the Change File dialog box and then three subtopics, each of which links to another context in the help file. (This screen is shown below.)

A First Aid file, however, is designed differently. It has three "levels" of help: Contents, Chapter, and Article. The Contents page is a TOC page, just as in Normal Help. The TOC button allows the user quick access to that level at all times. When the user selects an entry on the TOC page, a Chapter-level context should be brought up. At this point, only the TOC button is enabled, and the Chapter button is enabled and selected.

The Chapter-level context should similarly list subtopics of interest. When the user selects one of these, an Article-level context will be displayed. At this point, both the Contents button and the Chapter button will be enabled, allowing the user to "back up" to one of those previous levels. Also at this point, the Article button will be enabled and selected. The Article-level context (currently viewed) may contain a list of several questions or subtopics. When the user follows one of these links, the Article button will be enabled but deselected, allowing the user to quickly return to any of the three levels traversed to this point.

First Aid, like Normal Help, may have contexts linked to any other contexts in the same or other files; they are not restricted to the TOC-Chapter-Article links mentioned above.

Simple Help normally will not have any links because it offers no way to go back or even to return to a TOC page. If your application offers Simple Help, you should keep it straightforward and not put in any links.

Using Visual Cues and Graphics

Although the help controller will change the pointer image when the pointer is over a hyperlink, you should use graphics or highlighted text whenever possible to call attention to the links. Because the Help Editor is extended GeoWrite, anything you can do to normal GeoWrite text you can put in your help files.

Long pages of text can be difficult to read unless you put in headers or other highlighting. Some suggested highlighting techniques are to use colored or gradient-filled text, use the boxed or button text styles, or underline the text. Using different sizes and fonts is also effective. Keep in mind, however, that many users will have monochrome, CGA-style screens.

You can also embed graphics in the text. This does not include using the "graphic layer" of GeoWrite, however--since the Help Editor uses only the text layer, it will not use graphics pasted to the graphic layer. You must paste them directly into the text.

A useful tip to creating consistent help documents is to pre-define a number of style sheets in GeoWrite and use those styles for all your help. Any time a new style is required, create a new style sheet.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Enabling the Help Editor | Next: 4.3 Defining Files and Contexts


Defining Files and Contexts

Help Object Library: 4.3 Creating Help Files: Defining Files and Contexts

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Organizing and Writing the Text | Next: 4.4 Using Hyperlinks

If all your help resides within single files and links do not cross files (all links are internal to their own files), then defining contexts is simple. However, if links can cross files, you must do a little more work.

When you "define a context," you are notifying the Help Editor that a context with a given name and type exists in the specified file. When all links are internal to a single file, or when you have no links (as with Simple Help), you can simply define contexts for the "same file." However, if you have links crossing files, you must define both the file and context for each external link (similar in function to an "external" definition in your code).

To Define a File

Open the Define File dialog box via the Help Editor menu. Type the name of the file that will have links into it, and click "Add File." You can delete or rename previously defined files.

To Define a Context

Open the Define Context dialog box via the Help Editor menu. Use the upper-left popup list to select which file the new context will be defined for. Use the upper-right popup list to define the type of the file:

Text
A normal text help page. Use this for all contexts in all help types except First Aid as noted below.
TOC
A First Aid TOC page.
Chapter
A First Aid Chapter-level page.
Article
A First Aid Article-level page.

Once you've selected the proper type, name the context and click "Add Context." You can also delete and rename previously-defined contexts.

To Set a Context

Each help page may be recognized as only one context. To set a context

  1. Open the "Set Context" dialog box via the Help Editor menu.
  2. Select some text at the beginning of the help page. Be sure that you have the page's first character selected; otherwise the context name may not "take".
  3. Select the context name from the dialog box, and click "Apply." That page will be set to the selected context.

Pages in the GeoWrite file that do not have help contexts applied will not be generated into the help file. This fact is useful for recording information within the file about the file itself (release dates, draft dates, author name, revision notes, etc.).

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Organizing and Writing the Text | Next: 4.4 Using Hyperlinks


Using Hyperlinks

Help Object Library: 4.4 Creating Help Files: Using Hyperlinks

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Defining Files and Contexts | Next: 4.5 Generating the Help Files

You can set any selected text or graphic to be a hyperlink to another context in any help file. To set a hyperlink, open the "Set Hyperlink" dialog box via the Help Editor menu. Select the text that, when clicked on, should cause the new context page to appear. Then select, in the "Set Hyperlink" dialog, the appropriate file and context names. Click "Apply," and the selected text and/or graphic will act as a hyperlink to that file and context.

Once a hyperlink is created, you can follow it in the GeoWrite document by using the "Follow Hyperlink" entry in the Help Editor menu. This option will not be enabled unless the current cursor or selection is a hyperlink.

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Defining Files and Contexts | Next: 4.5 Generating the Help Files


Generating the Help Files

Help Object Library: 4.5 Creating Help Files: Generating the Help Files

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Using Hyperlinks | Next: 5 HelpControlClass Reference

Once the help file is created, all the contexts are defined and set, and all the hyperlinks (if any) are established, you can generate the help file. When the file is generated, it is automatically placed in USERDATA\HELP, and it will have the same name as the GeoWrite document being edited. Thus, if you are editing a file called "helpsamp," the help file would be USERDATA\HELP\ helpsamp .

The help file may be compressed or not. The default is to compress the help data to save disk space. Since help files are read-only, the help controller decompresses the data when loading it, then simply discards it when done. The "Compress Data in File" option in the Help Editor menu allows you to specify whether the help file should be compressed or not (just about the only time it should not be compressed is for Status Help).

To generate the help file, simply select "Generate Help File" in the Help Editor menu. The generation is one-way; you can not edit a help file. You should, therefore, keep your original GeoWrite documents containing the help text available in case they require editing or update .

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Using Hyperlinks | Next: 5 HelpControlClass Reference


HelpControlClass Reference

Help Object Library: 5 HelpControlClass Reference

| Up: GEOS SDK TechDocs | Up | Prev: 4 Creating Help Files

This section will only be useful if you plan on customizing help for your application. Nearly all of what you need for creating normal help files and adding help to your application is described in the previous sections. The following is detailed reference information for HelpControlClass (shown in HelpControlClass ).

Code Display 13-3 HelpControlClass

    @instance HelpType			 HCI_helpType;			/* See HelpType, below */
    @instance MemHandle			HCI_curFile;			/* Internal */
    @instance MemHandle			HCI_historyBuf;			/* Internal */
    @instance word			HCI_nameArrayVM;			/* Internal */
    @instance GeodeHandle			HCI_compressLib;			/* Internal */
        @default GII_attrs = (@default | GIA_NOT_USER_INITIATABLE);
        @default GII_visibility = GIV_DIALOG;
    @vardata void				 ATTR_HELP_SUPPRESS_INITIATE;
    @vardata char[]				 ATTR_HELP_INITIAL_HELP;
    @vardata optr				 ATTR_HELP_CUSTOM_POINTER_IMAGE;
    @vardata CompSizeHintArgs 				 HINT_HELP_TEXT_FIXED_SIZE;
    @vardata void				 HINT_HELP_NOT_RESIZABLE;
    @vardata char[]				 ATTR_HELP_INITIAL_HELP_FILE;
typedef ByteEnum  HelpType;
    #define HT_NORMAL_HELP				0
    #define HT_FIRST_AID				1
    #define HT_STATUS_HELP				2
    #define HT_SIMPLE_HELP				3
    #define HT_SYSTEM_HELP				4	/* Reserved for system use */
/* Internal Vardata fields--do not use these. */
    @vardata char[]			TEMP_HELP_ERROR_FILENAME;
    @vardata char[]			TEMP_HELP_TOC_FILENAME;
    @vardata void			TEMP_HELP_DETACH_RECEIVED;

HelpControlClass is a subclass of GenControlClass and, as such, inherits the feature management attributes and messages thereof. For complete information on feature and tool sets, see the Controllers chapter. The HelpControl object has features but no tools. The feature set of HelpControlClass is shown in HelpControl Features .

Code Display 13-4 HelpControl Features

/* These features can be turned on or off with ATTR_GEN_CONTROL_REQUIRE_UI and
 * ATTR_GEN_CONTROL_PROHIBIT_UI. They can be turned on or off dynamically with
 * MSG_GEN_CONTROL_ADD_FEATURE and MSG_GEN_CONTROL_REMOVE_FEATURE. */
typedef WordFlags  HPCFeatures;
    #define HPCF_HELP					0x0100	/* has a "help on help" trigger */
    #define HPCF_TEXT					0x0080	/* has text */
    #define HPCF_CONTENTS					0x0040	/* has "Contents" button */
    #define HPCF_HISTORY					0x0020	/* has "History" button */
    #define HPCF_GO_BACK					0x0010	/* has "Go Back" button */
    #define HPCF_CLOSE					0x0008	/* has "Close" button */
    #define HPCF_INSTRUCTIONS					0x0004	/* has "Instructions" button */
    #define HPCF_FIRST_AID_GO_BACK					0x0002	/* has "Go Back" button for
                         * First Aid */
    #define HPCF_FIRST_AID					0x0001	/* has First Aid configuration */

HelpControlClass , as a GenControl subclass, receives notification when the controlled item changes. For example, a change in help context will cause the HelpControl object to display the new context page. The HelpControl object must be put on the GAGCNLT_NOTIFY_HELP_CONTEXT_CHANGE notification list. When a context change occurs, the help controller will receive MSG_META_NOTIFY_WITH_DATA_BLOCK , with the notification type GWNT_HELP_CONTEXT_CHANGE . This notification type passes a data block containing a NotifyHelpContextChange structure, which is shown below.

Code Display 13-5 Help Notification Structure

/* This data structure is passed in MSG_META_NOTIFY_WITH_DATA_BLOCK to the help
 * controller when a change in the help context occurs. This structure can be
 * generated by calling HelpSendHelpNotification(). This structure uses the
 * following constant and type. */
#define MAX_CONTEXT_NAME_SIZE				22
typedef char		ContextName[MAX_CONTEXT_NAME_SIZE];
typedef struct {
    HelpType			NHCC_type;			/* HelpType involved in change */
    ContextName			NHCC_context;			/* New context to link to */
    FileLongName			NHCC_filename;			/* New file name to link to */
    FileLongName			NHCC_filenameTOC;			/* File name to get TOC from */
}  NotifyHelpContextChange;

HelpControlClass has two exported messages that subclasses may intercept. These are not often intercepted by subclasses. Their reference information is presented below.

MSG_HELP_CONTROL_FOLLOW_LINK

Cause the HelpControl object to follow the specified link in the help file.

Source: Unrestricted; typically internal to the controller.

Destination: The HelpControl object to follow the link.

Parameters: link name 16-bit token of the link name.

link file
16-bit token of the link file.

Return: Nothing.

Interception: Not generally intercepted.

MSG_HELP_CONTROL_GET_POINTER_IMAGE

Return the pointer image to be used when the pointer is over a link in the help text. It looks for ATTR_HELP_CUSTOM_POINTER_IMAGE and returns the pointer stored there; if no custom pointer exists, the default is returned.

Source: Unrestricted; typically internal to the controller.

Destination: The HelpControl object using the custom pointer.

Parameters: not over link Pass FALSE if the pointer is not over a link, TRUE if it is. (Must pass TRUE, not just non-zero).

Return: A word of MouseReturnFlags . If MRF_SET_POINTER_IMAGE is set in this record, the optr of the pointer image will also be returned. Otherwise, the optr will be NullOptr.

Interception: A "viewer" application may subclass this and return its own pointer images if they are dynamic. If static pointer images are used, the application should use ATTR_HELP_CUSTOM_POINTER_IMAGE instead. There is no need to call the superclass with this message .

|  Up: GEOS SDK TechDocs | Up | Prev: 4 Creating Help Files


The List Objects

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 List Object Features
| One-page Version of this Chapter for Easy Printing

The GenItemGroup and GenBooleanGroup objects manage lists of objects (GenItems or GenBooleans, respectively) that contain user-selectable options. GenDynamicListClass is a subclass of GenItemGroupClass which allows efficient dynamic and scrolling lists. All of the list objects manage children which each represent a particular option. The user can generally select and deselect options in such a group, and in doing so cause an action to take place.

The list groups and list items work together to manage states of objects within your application. These list objects perform all of the complex state management of their children; the selection items themselves are very simple objects.

You should be familiar with generic objects before reading this chapter. Please see the UI Overview for an overview of each generic object in the user interface. You should be particularly familiar with the instance data of and messages handled by GenClass .

  1 List Object Features
  2 Common Behavior
    2.1 Applying the Action
    2.2 State Information
  3 GenItemGroups
    3.1 GenItemGroup Instance Data
    3.2 GenItem Instance Data
    3.3 GenItemGroup Basics
    3.4 Working with Items
    3.5 Scrolling GenItemGroups
    3.6 GenItemGroup Links
    3.7 Limitations of the GenItemGroup
  4 GenDynamicListClass
    4.1 DynamicList Instance Data
    4.2 DynamicList Basics
    4.3 Altering Instance Data
  5 GenBooleanGroups
    5.1 GenBooleanGroup Instance Data
    5.2 GenBooleanGroup Usage
    5.3 Altering Instance Data

|  Up: GEOS SDK TechDocs | Up | Down | Next: 1 List Object Features
| One-page Version of this Chapter for Easy Printing


List Object Features

The List Objects: 1 List Object Features

| Up: GEOS SDK TechDocs | Up | Prev: The List Objects | Next: 2 Common Behavior

The list objects provide a highly flexible way to display and manage user-selectable options. Depending on the Specific UI and the type of list, these objects may be built out in a variety of fashions. In some UIs, BooleanGroups may appear as checkboxes, GenItemGroups may appear as radio buttons, etc.

You may manipulate the grouping objects in much the same way as a GIV_SUB_GROUP GenInteraction. For example, hints may alter the placement and dimensions of the grouping object's children or the list may appear with special dimensions. The items themselves are simple generic objects that can be easily added to your grouping object. The grouping object will manage the geometry and selection state of these children; it will also perform any required actions based on the state of these children.

There are three list management objects.

  • GenItemGroup
     An all-purpose grouping object, this list may be exclusive (only one item may be selected at a time) or non-exclusive and may also have scroll bars. A GenItemGroup may only contain GenItemClass objects as its children.
  • GenBooleanGroup
     This list is designed for entries that are either true of false ("on" or "off") independent of other entries. The BooleanGroup keeps track of its selections with a bitfield representing the boolean state of each of its children. A GenBooleanGroup object may only contain GenBooleanClass objects.
  • GenDynamicList
     This list is designed for very long or changing length lists. In many respects it is similar to a GenItemGroup but also allows efficient dynamic lists. A GenDynamicList does not specify any explicit "children"; instead it contains a list of monikers to display to the user within a scrolling list.

These are the following main list item objects:

  • GenItem
     Objects used as entries in a GenItemGroup. In OSF/Motif, these objects appear in most cases as "diamond" settings.
  • GenBoolean
     Objects used as entries in a GenBooleanGroup representing true or false states. In OSF/Motif, these items appear most often as selectable "boxes."

The GenDynamicList does not specify any generic children, but instead will contain a list of visual monikers to cycle through a scrolling list.

| Up: GEOS SDK TechDocs | Up | Prev: The List Objects | Next: 2 Common Behavior


Common Behavior

The List Objects: 2 Common Behavior

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 List Object Features | Next: 3 GenItemGroups

Each list object acts differently and you will need to carefully review these differences before using them. There are a number of similarities in the functionality of these list objects, however. This next section details these similarities.

Each list group acts as a grouping object for its generic children. The list groups have much of the same geometry capabilities as a GIV_SUB_GROUP Interaction; they can arrange their children in a variety of fashions and can also be manipulated within their parent.

The items themselves may in general be either "on" (selected) or "off" (unselected). Depending on the behavior of the list, selecting an item may cause an action to occur and may affect the other selections. In exclusive lists, for example, selecting any item deselects any other item.

Applying the Action

State Information

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 List Object Features | Next: 3 GenItemGroups


Applying the Action

The List Objects: 2.1 Common Behavior: Applying the Action

| Up: GEOS SDK TechDocs | Up | Prev: 2 Common Behavior | Next: 2.2 State Information

Often, each list object will contain an "apply" message. This message will be sent out when the list's state has changed and the new state should be applied in the application. For example, a list of colors ("Red", "Green", and "Blue") may have a message MSG_GEN_VIEW_SET_COLOR that sets the background color of a GenView. Changing the state of the list (by selecting an item) will cause this notification message to be sent to the view. More typically, though, the apply message will be sent to the application or a controller object.

Alternatively, you could forego including an apply message in the list object's instance data. In this case, whenever the object receives a MSG_GEN_APPLY it would query the state of its children and act upon that state.

When the user makes a selection, the apply action may or may not be sent out immediately, depending on the operating mode of the list. GenItemGroups, GenBooleanGroups, and GenDynamicLists may operate in one of two modes: immediate or delayed. (For a more complete discussion of delayed and immediate modes, see the GenInteractionClass chapter.) Typically, a list object will operate in delayed mode if it is placed within a GIT_PROPERTIES Interaction. Other generic objects such as GenInteractions, GenTexts, and GenValues also utilize delayed and immediate mode.

In immediate mode, any changes made in the list's state are reflected immediately in the application. If the user changes an immediate mode list object concerning a text style change, the text style of the current selection will change immediately. Changing the state of the list will cause the apply action associated with that object to be sent out immediately.

In delayed mode, any changes made in the list's state are not reflected until some later time, when the changes are "applied." This apply may be effected by the user (through an "apply" trigger) or by the application when it deems it appropriate. In delayed mode, the notification message will not be sent on changes to the state of the list but only when the list receives notice to apply the changes.

Occasionally, a list operating in delayed mode may wish to send out a different message whenever the user changes its state, even if that change should not be immediately applied. In this case, you can supply a status message for the object to send to the destination whenever the user makes changes within the list group.

Each list must have a destination to send its apply action and, if needed, status messages to. This destination can be any object or process capable of handling the message.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Common Behavior | Next: 2.2 State Information


State Information

The List Objects: 2.2 Common Behavior: State Information

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Applying the Action | Next: 3 GenItemGroups

In most cases, a selection will be either on or off. In some cases, however, a selection or a list's state will be indeterminate--considered neither completely on or off. For example, a styles list may show the characteristics of the current highlighted text (bold, italic, underlined, etc.). Selecting a whole paragraph of text in which only one word is bold will leave the "bold" entry in the styles list in an indeterminate state--some of the selected text is bold, some is not.

The lists keep track of these indeterminate states themselves. In some cases, an entire list may be indeterminate; in other cases, individual items may be indeterminate. It is up to the specific UI to represent an indeterminate state. (In OSF/Motif settings that are indeterminate are shown unselected.)

If a list object is operating in delayed mode, changes within the list will not be effected until either the user or the application "applies" them. The list objects keep track of changes in state by storing a modified state of either the list or individual items. Whenever an apply action is made on a list group, the group will check whether its list (or its items) have been modified. If nothing has been marked modified, the list will not apply those changes (unless you wish to override this default behavior).

This modified state is independent of the list's mode. In immediate mode, a change in state to a list object will automatically mark that list object modified and simultaneously call MSG_GEN_APPLY on the list object. The list object will recognize that it is modified and will send out the group's apply action. In delayed mode, any changes made to the object will also mark the group modified; at some later point, MSG_GEN_APPLY will be sent to the list; the object will recognize that the group has been modified and will then send out its apply action.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Applying the Action | Next: 3 GenItemGroups


GenItemGroups

The List Objects: 3 GenItemGroups

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Common Behavior | Next: 4 GenDynamicListClass

The GenItemGroupClass and GenItemClass work together to allow an application to set up a list of options for a user to choose. A GenItemGroup may contain one or more GenItems; each GenItem may represent a particular option in the list. The user may select one or more of the options at any time, depending upon the behavior specified for the list, and the user can generally select or deselect an option by activating the corresponding GenItem.

GenItemClass objects are simple list items. GenItems are used only as entries within a GenItemGroup. In most cases, a GenItem will only need a moniker and a special identifier associated with the selection. The GenItemGroup will use this identifier to refer to the object when it needs to communicate to the items or when it sends out messages.

Your application interacts with GenItems though the GenItemGroup; this group controls the items and keeps track of which are selected. The GenItemGroup object has all of the same geometry features as a grouping type GenInteraction. (See the GenInteractionClass chapter).

GenItemGroup Instance Data

GenItem Instance Data

GenItemGroup Basics

Working with Items

Scrolling GenItemGroups

GenItemGroup Links

Limitations of the GenItemGroup

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Common Behavior | Next: 4 GenDynamicListClass


GenItemGroup Instance Data

The List Objects: 3.1 GenItemGroups: GenItemGroup Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 3 GenItemGroups | Next: 3.2 GenItem Instance Data

GenItemGroup and GenItem objects contain a variety of information. The bulk of the functionality of the list is contained in the GenItemGroup; the items themselves only contain identifying information that the list uses.

The GenItemGroup contains instance fields relating to the state of its children GenItems. These instance fields are listed in GenItemGroup Instance Data . Any objects of GenItemGroupClass or one of its subclasses will contain these instance fields, along with the instance fields of GenClass .

Code Display 11-1 GenItemGroup Instance Data

    @instance GenItemGroupBehaviorType					GIGI_behaviorType = GIGBT_EXCLUSIVE;
/* The GenItemGroupBehaviorType enumerations are used in the GIGI_behaviorType
 * instance field. */
typedef ByteEnum GenItemGroupBehaviorType;
#define GIGBT_EXCLUSIVE 0
#define GIGBT_EXCLUSIVE_NONE 1
#define GIGBT_EXTENDED_SELECTION 2
#define GIGBT_NON_EXCLUSIVE 3
    @instance GenItemGroupStateFlags					GIGI_stateFlags = 0;
/* The GenItemGroupStateFlags are used in the GIGI_stateFlags instance field. */
typedef ByteFlags GenItemGroupStateFlags;
#define GIGSF_INDETERMINATE 				0x80
#define GIGSF_MODIFIED				0x40
    @instance word					GIGI_selection = GIGS_NONE;
/* The GIGS_NONE constant is used with the GIGI_selection instance field and may
 * also be returned by MSG_GEN_ITEM_GROUP_GET_SELECTION. */
#define GIGS_NONE (0xffff)
    @instance word					GIGI_numSelections = 0;
    @instance optr					GIGI_destination;
    @instance Message					GIGI_applyMsg = 0;

GIGI_ behaviorType describes the selection behavior of the list. The default type is GIGBT_EXCLUSIVE, which creates an exclusive list. A full description of each type is provided in GenItemGroup Behavior .

GIGI_ stateFlags contains flags that affect the state of the GenItemGroup. These flags indicate whether a GenItemGroup is in an indeterminate or a modified state.

GIGI_ selection contains the name of the current GenItem child that is selected. GenItemGroups use the identifying keywords stored in each GenItem's GII_ identifier instance field to reference those selections. If more than one item may be selected at a time (if GIGI_ numSelections is greater than one), this instance field will point to a list of GenItem identifiers rather than store a single identifier.

GIGI_ numSelections contains the number of GenItems currently selected. For exclusive or exclusive-none lists, this number will be either one or zero. For multiple selection lists, this number may be greater than zero. Unless you have an item group that initially appears with more than one selection, you will not need to set this up in your object declaration.

GIGI_ destination contains the optr of the object or process to handle messages sent out by this GenItemGroup. This destination object will receive the notification message in the GIGI_ applyMsg instance field whenever state changes are applied within the GenItemGroup.

GIGI_ applyMsg stores the apply action for the GenItemGroup to send out whenever state changes should be applied, either through an immediate state change or through a delayed mode activation.

The GenItemGroup also has several vardata fields.

Code Display 11-2 GenItemGroup Vardata Instance Fields

@vardata Message 			ATTR_GEN_ITEM_GROUP_STATUS_MSG;
@vardata void			ATTR_GEN_ITEM_GROUP_SET_MODIFIED_ON_REDUNDANT_SELECTION;
@vardata Message			ATTR_GEN_ITEM_GROUP_CUSTOM_DOUBLE_PRESS;
@vardata optr			ATTR_GEN_ITEM_GROUP_LINK;
    @reloc ATTR_GEN_ITEM_GROUP_LINK, 0, optr;
@vardata void			ATTR_GEN_ITEM_GROUP_INIT_FILE_BOOLEAN;
/* Hints */
@vardata void		HINT_ITEM_GROUP_RADIO_BUTTON_STYLE;
@vardata void		HINT_ITEM_GROUP_TOOLBOX_STYLE;
@vardata void		HINT_ITEM_GROUP_SCROLLABLE;
@vardata void		HINT_ITEM_GROUP_MINIMIZE_SIZE;
@vardata void		HINT_ITEM_GROUP_DISPLAY_CURRENT_SELECTION;
@vardata void		HINT_ITEM_GROUP_MINIMIZE_SIZE_IF_VERTICAL_SCREEN;

ATTR_GEN_ITEM_GROUP_STATUS_MSG assigns a status message to your GenItemGroup. A status message allows objects to receive notice of a change in the GenItemGroup's current state without forcing an apply action. This attribute is useful for cases where the state of your GenItemGroup may reflect information in other user interface objects, and should therefore be updated whenever its state changes, regardless of the need to apply those changes.

In exclusive or extended-selection lists, selecting an already-select item will not change the state of the GenItemGroup and will not mark it modified. ATTR_GEN_ITEM_GROUP_SET_MODIFIED_ON_REDUNDANT_SELECTION alters this default behavior for a GenItemGroup. Including this attribute in the object's instance data will cause the group to be marked modified whenever the user makes a redundant selection, thereby forcing an apply action to later take place. Use this attribute for operations you may wish to repeat even if the state of the GenItemGroup has not changed.

ATTR_GEN_ITEM_GROUP_LINK allows two or more GenItemGroups to be linked together and act as one GenItemGroup. Each GenItemGroup may contain one or more items with identifiers unique to all other items within the linked GenItemGroups. The links should be circular, with the last group pointing back to the first. All of these GenItemGroups should have identical states and behavior types so they will work correctly. Use this attribute to circumvent undesirable geometry constraints. For more information, see GenItemGroup Links .

ATTR_GEN_ITEM_GROUP_CUSTOM_DOUBLE_PRESS allows a special activation message to be sent out on double press events; it is used only in exclusive GenItemGroup lists.

ATTR_GEN_ITEM_GROUP_INIT_FILE_BOOLEAN forces the GenItemGroup to use "true" and "false" in the .INI file rather than numerical values.

HINT_ITEM_GROUP_RADIO_BUTTON_STYLE indicates that the Item Group should appear as "radio buttons" if the Specific UI allows such behavior. OSF/Motif does this by default. HINT_ITEM_GROUP_TOOLBOX_STYLE indicates that the item's should be drawn in toolbox style, with a simple box around the visual moniker.

HINT_ITEM_GROUP_MINIMIZE_SIZE and HINT_ITEM_GROUP_MINIMIZE_SIZE_IF_VERTICAL_SCREEN force the item group to use only the smallest space available. In OSF/Motif, for example, this would cause the item group to be a popup menu. The latter of the two hints takes effect only on screens that are taller than they are wide. HINT_ITEM_GROUP_DISPLAY_CURRENT_SELECTION signals that a minimized list should display the currently seleted item's moniker when said list is in its minimized state.

| Up: GEOS SDK TechDocs | Up | Prev: 3 GenItemGroups | Next: 3.2 GenItem Instance Data


GenItem Instance Data

The List Objects: 3.2 GenItemGroups: GenItem Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 GenItemGroup Instance Data | Next: 3.3 GenItemGroup Basics

GenItemClass is a subclass of GenClass . It has one new instance field:

@instance word GII_identifier;

GII_ identifier sets the indentifying keyword (usually an enumerated type) that uniquely identifies this GenItem. This identifier is used by the GenItemGroup parent to indicate the current selection and otherwise reference the item. Each GenItem identifier within the same GenItemGroup must evaluate to a unique value.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 GenItemGroup Instance Data | Next: 3.3 GenItemGroup Basics


GenItemGroup Basics

The List Objects: 3.3 GenItemGroups: GenItemGroup Basics

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 GenItem Instance Data | Next: 3.4 Working with Items

The GenItemGroup manages a group of GenItems. In general, you must decide which behavior type you wish your GenItemGroup to exhibit, the message for the list to send out, and the object (or process) to handle messages sent out by this group. You may also set a number of initial conditions (items selected, whether the list is indeterminate or modified, etc.) for the list.

GenItemGroup Behavior

GIGI_behaviorType, MSG_GEN_ITEM_GROUP_SET_BEHAVIOR_TYPE, MSG_GEN_ITEM_GROUP_GET_BEHAVIOR_TYPE

There are several types of user selection behavior for a GenItemGroup. Items can be set up so that only one is selected at a time, one or none is selected at a time, multiple items can be selected, or multiple items can be selected only when extending the selection (e.g. dragging the mouse).

The GIGI_ behaviorType instance field defines the selection behavior of the list. The instance data must be of the enumerated type GenItemGroupBehaviorType . The default type is GIGBT_EXCLUSIVE, which creates an exclusive list; one and only one selection in an exclusive list may be selected at any time. The available types are listed below.

GIGBT_EXCLUSIVE
This is a list where only one option may be selected at a time. Once a selection has been made, making another selection will deselect the previous item. The user may not deselect a currently selected item. Many specific UIs represent these items with radio buttons; OSF/Motif represents these items with "diamond" settings. This type of list is the default for a GenItemGroup.
GIGBT_EXCLUSIVE_NONE
This list is also exclusive--at most one option may be selected at a time--but the user can also deselect an item, leaving no items selected. GenItemGroups show a "none-selected" state with GIGS_NONE in their GIGI_ selection instance fields.
GIGBT_EXTENDED_SELECTION
This list is normally exclusive, unless the user "extends" the selection. The user can extend the selection either by dragging the mouse through another selection or by clicking on a new selection in conjunction with a special keystroke. In OSF/Motif, clicking on an item while holding the SHIFT key will extend the selection (leaving any other selections unaffected) to the one clicked on. Extending a selection to a currently selected item will deselect that item.
GIGBT_NON_EXCLUSIVE
This list does not exhibit any exclusive behavior. Any or all settings may be on at any time, and selecting any items will not affect the state of other settings. In many case, you may want to use a GenBooleanGroup in place of a non-exclusive GenItemGroup. For example, a non-exclusive GenItemGroup cannot store individual indeterminate or modified states for its children; GenBooleanGroup stores this information.

Exclusive Lists

Exclusive lists are designed for cases in which one and only item should be selected at any given time. For example, a graphics application may want to prompt the user to select a color for drawing lines. The application can display this list of colors within an exclusive list. Only one color may be selected. Once a color is selected, selecting any other color will deselect the first color.

Exclusive lists may (and by default do) appear without any item initially selected. This may lead to problems if the application tries to apply any changes before an item is selected (such as within a delayed-mode dialog box). If your application needs an item to be selected at all times in order to operate correctly, your list should appear with one of the items initially selected (or with the "apply" trigger disabled).

Exclusive-None Lists

Exclusive-None lists are designed for cases in which either one item must be selected or no items should be selected. For example, a word processor may have a "Justifications" menu with several options such as left-justified, right-justified, or center-justified. Selecting any of these options will perform the justification operation on the current paragraph, but the user may also deselect any option, leaving no justification on the paragraph.

Exclusive-None lists by default appear without any items initially selected. Your application should, of course, be able to handle this case of nothing selected; if it cannot, the list should probably not be exclusive-none.

Extended-Selection Lists

Extended-Selection lists are designed for cases in which the application usually needs an exclusive selection but also desires to let the user select multiple items for bulk operations. Multiple selections are stored as lists of items.

For example, a disk copy operation may have an extended-selection list to specify the drive to copy a file to. In most cases, the user will only want to copy to one drive, and the list normally operates in exclusive mode to accommodate this. By extending the selection, however, the user can request the list to copy the file to multiple drives.

It is up to the specific UI how the user may extend a selection. In OSF/Motif, the user may select another item in tandem with a special keystroke (the SHIFT key) to extend the selection from one item to several. Extending a selection to an already selected item will deselect that item, leaving any other items unchanged.

Non-Exclusive Lists

Non-exclusive lists are designed for cases in which any or all of the items may be selected at once. Multiple selections, as in the extended-selection GenItemGroup, are stored as lists of items. Selecting an already selected item will deselect that item. Therefore, there may be a state where nothing is selected within a non-exclusive list.

For example, an interactive tutorial may ask the user if he wants more information on certain topics, giving these topics in a non-exclusive list. The user can freely choose as many or as few items as he wishes, and then select "Print" to print out information on those items.

In most cases, however, you might rather use a GenBooleanGroup object in place of a non-exclusive GenItemGroup. A GenBooleanGroup acts similar to a non-exclusive list but is streamlined to operate efficiently for lists with 16 or fewer items. (GenBooleanGroups represent their children's state through a word-length bitfield; therefore they may only accommodate 16 children.) You may also need to use a GenBooleanGroup if you need to keep track of individual indeterminate and modified states.

MSG_GEN_ITEM_GROUP_GET_BEHAVIOR_TYPE returns the behavior type stored in the list's GIGI_ behaviorType instance field. To set a new behavior type, send the list a MSG_GEN_ITEM_GROUP_SET_BEHAVIOR_TYPE .

MSG_GEN_ITEM_GROUP_GET_BEHAVIOR_TYPE

GenItemGroupBehaviorType MSG_GEN_ITEM_GROUP_GET_BEHAVIOR_TYPE();

This message returns the current behavior type of a GenItemGroup (stored in the GIGI_ behaviorType instance field).

Source: Unrestricted.

Destination: Any GenItemGroup.

Parameters: Nothing.

Return: The GenItemGroupBehaviorType of the GenItemGroup.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_BEHAVIOR_TYPE

void	MSG_GEN_ITEM_GROUP_SET_BEHAVIOR_TYPE(
        GenItemGroupBehaviorType				behaviorType);

This message sets a new behavior type for a GenItemGroup by changing the object's GIGI_ behaviorType instance field. This message should only be sent while the object is not usable.

Source: Unrestricted.

Destination: Any non-usable GenItemGroup object.

Parameters: behaviorType The new GenItemGroupBehaviorType .

Return: Nothing.

Interception: Generally not intercepted.

GenItemGroup Selections

GIGI_selection, GII_identifier, GIGI_numSelections, MSG_GEN_ITEM_GROUP_SET_NONE_SELECTED, MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION, MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS, MSG_GEN_ITEM_GROUP_GET_SELECTION, MSG_GEN_ITEM_GROUP_GET_NUM_SELECTIONS, MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS

Within a GenItemGroup, each GenItem must contain an identifier, often an enumerated value, to uniquely identify that item. The GenItemGroup will use this identifying value whenever referencing the GenItem (or items), either through its instance data or through messages. This value is stored in the GenItem's GII_ identifier instance data.

In most cases, your collection of GenItems within a particular GenItemGroup should be different enumerated values. Each item within a specific GenItemGroup must have a unique identifier to reference each item. To ensure this, it is usually convenient to use an enumerated type. Make sure that you don't use two different enumerated types because they may not contain unique values across types.

In an exclusive or exclusive-none list, the GenItemGroup will store the identifier of the current selection in the GIGI_ selection instance field. If no item is selected, this instance field will instead store the value GIGS_NONE (-1). (For an exclusive list, this case could only arise before an item is initially selected.)

Note that -1 is still a legal identifier for a GenItem. If you do use this identifier you must be sure to check the GIGI_ numSelections instance field to check whether the GIGI_ selection value corresponds to an item or to the GIGS_NONE value.

GenItemGroups by default appear with no items initially set (GIGI_ selection is set to GIGS_NONE). If you wish a GenItemGroup to initially appear with a selection set to one of the GenItems, set GIGI_ selection to the identifier of that GenItem.

Code Display 11-3 Setting Enumerated Types As Identifiers

/* In most cases, each GenItemGroup should have a single set of enumerated types
 * to name its children. In this case, MyCityGroup will have settings all related
 * to the enumerated type USCities, and San Francisco will be the initial
 * selection. */
typedef ByteEnum USCitiesEnum;
#define CITY_CHICAGO 1
#define CITY_NEW_YORK 2
#define CITY_SAN_FRANCISCO 3
#define CITY_LOS_ANGELES 4
@object GenItemGroupClass MyCityGroup = {
    GI_comp = @Chicago, @NewYork, @SanFrancisco, @LosAngeles;
    GIGI_selection = CITY_SAN_FRANCISCO;
}
@object GenItemClass Chicago = {
    GI_visMoniker = `C', "Chicago";
    GII_identifier = CITY_CHICAGO;
}
@object GenItemClass NewYork = {
    GI_visMoniker = `N', "New York";
    GII_identifier = CITY_NEW_YORK;
}
@object GenItemClass SanFrancisco = {
    GI_visMoniker = `S', "San Francisco";
    GII_identifier = CITY_SAN_FRANCISCO;
}
@object GenItemClass LosAngeles = {
    GI_visMoniker = `L', "Los Angeles";
    GII_identifier = CITY_LOS_ANGELES;
}

In an extended-selection or non-exclusive list, more than one item may be selected at a time. In this case, the GIGI_ selection instance field will not store a single identifier but instead will store a ChunkHandle to a list of identifiers. This functionality is internal to the operation of the list.

You may set up a list (if it is extended-selection or non-exclusive) to initially appear with multiple selections. The list of identifiers may be of arbitrary size but must be manually set up in its own chunk. Set GIGI_ selection to the ChunkHandle of the chunk containing this list. In this case, you must also set GIGI_ numSelections to the proper number of selections. (See Setting Multiple Initial Selections .)

For multiple selection lists (GIGBT_EXTENDED_SELECTION or GIGBT_NON_EXCLUSIVE) GIGI_ numSelections may be set greater than 1 if more than one item is selected. In this case, GIGI_ selection will contain a ChunkHandle to a list of identifiers instead of a single identifier.

Code Display 11-4 Setting Multiple Initial Selections

/* To set a GenItemGroup to appear initially with multiple selections, set up a
 * chunk containing the identifiers and a ChunkHandle in the GIGI_selection
 * instance field. Only the new list chunk and the GenItemGroup are shown; other
 * information is in the previous code display. */
@object GenItemGroupClass MyCityGroup = {
    GI_comp = @Chicago, @NewYork, @SanFrancisco, @LosAngeles;
    GIGI_behaviorType = GIGBT_NON_EXCLUSIVE;
    GIGI_numSelections = 2;
    GIGI_selection = (ChunkHandle) ChunkOf(@SelectionList);
}
@chunk word SelectionList[2] = {
    CITY_CHICAGO, CITY_SAN_FRANCISCO
};

In most cases, the user will change the selections of the GenItemGroup by clicking on items. Your application may also retrieve or alter the items selected with the following messages. In many of these messages, you may have to pass an indeterminate state; if you are unsure about what indeterminate state a list object should be in, pass FALSE. Most GenItemGroups do not need to worry about their indeterminate state.

None of the messages which alter the state or number of selections cause an apply or status message to be sent out by the GenItemGroup. If you wish to send out an apply or a status message in these cases, mark the GenItemGroup modified and send the object MSG_GEN_APPLY or MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG .

MSG_GEN_ITEM_GROUP_GET_SELECTION returns the currently selected item for the GenItemGroup. If there are no items currently selected, this message will return GIGS_NONE. If there is more than one item selected, this message will only return the first item selected in that GenItemGroup. (You should use MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS in this case.)

MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS returns the current list of selections for non-exclusive and extended-selection lists. You must pass this message a pointer to a buffer to hold the list of selections, along with the number of items the buffer can hold. Make sure to allocate enough space in this buffer, or the buffer will not be filled in. Also make sure to send this message with @call and not @send , as it passes a pointer. You can check for the number of items currently selected with MSG_GEN_ITEM_GROUP_GET_NUM_SELECTIONS .

MSG_GEN_ITEM_GROUP_SET_NONE_SELECTED deselects any settings currently on so that the list will appear with no items selected. It takes a flag indicating whether the list should be in its indeterminate state. This message will also clear the object's modified flag. If you wish to set this flag, send the GenItemGroup a MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE after sending this message.

MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION sets a single selection for a GenItemGroup. Any old selections will be deselected automatically, regardless of the behavior type of the list. It takes a flag indicating whether the list should be in its indeterminate state. If the GenItemGroup is scrollable and exclusive, this message will usually ensure that the list will automatically scroll to the selected item. This message also clears the object's modified flag.

MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS sets a a group of selections for a GenItemGroup. Any previously selected items will be deselected. The message must pass the number of selections and a pointer to the list of identifiers to set. This message will clear the GenItemGroup's modified flag.

MSG_GEN_ITEM_GROUP_GET_SELECTION

word	MSG_GEN_ITEM_GROUP_GET_SELECTION();

This message returns the current selection identifier for the GenItemGroup. If there is no selection, this message returns GIGS_NONE. If there are multiple selections, this message only returns the first selection. Use MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS for multiple selections. A selection may be returned even if the item is not usable, not enabled, or not in the GenItemGroup.

If you are using a GenItemGroup which may contain a selection of -1, you will want to call MSG_GEN_ITEM_GROUP_GET_NUM_SELECTIONS to differentiate between the item with -1 being selected and GIGS_NONE.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Return: The current selection stored in the GenItemGroup's GIGI_ selection instance field (or GIGS_NONE if there are no selections).

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS

word	MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS(
        word	*selectionList,
        word	maxSelections);

This message returns the current list of selections for non-exclusive and extended-selection GenItemGroups. The caller must allocate a buffer for the entries and pass the size of that buffer. If there is insufficient space in the passed buffer, no entries will be filled in. You should call MSG_GEN_ITEM_GET_NUM_SELECTIONS beforehand in order to allocate the correct maximum size for the buffer.

This message returns the number of selections filled into the buffer. The buffer is filled in with the proper identifiers. The list of identifiers within the buffer will not be null-terminated.

If you are using an exclusive or exclusive-none list, use MSG_GEN_ITEM_GROUP_GET_SELECTION instead.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: selectionList A pointer to the buffer to store the selection entries.

maxSelections
The maximum number of selections that may be returned by the message.

Return: The total number of selections returned in the buffer.

selectionList
The pointer to the buffer containing the identifiers of the selections.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_GET_NUM_SELECTIONS

word	MSG_GEN_ITEM_GROUP_GET_NUM_SELECTIONS();

This message returns the GenItemGroup's current number of selections (stored in GIGI_ numSelections ), or zero if there is no selection.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Return: The number of selections in the GenItemGroup.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_NONE_SELECTED

void	MSG_GEN_ITEM_GROUP_SET_NONE_SELECTED(
        Boolean		indeterminate);

This message sets a GenItemGroup to show no selections; it should not be sent to an exclusive GenItemGroup. This message will also clear the GenItemGroup's modified flag. If you wish to set the modified flag, send the list a MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE .

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: indeterminate Pass TRUE if the GenItemGroup should be marked indeterminate, FALSE otherwise.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION

void	MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION(
        word		identifier,
        Boolean		indeterminate);

This message sets exactly one selection for a GenItemGroup. Any previously-selected items will be deselected, regardless of the GenItemGroup's behavior type. To set items without disturbing other settings, use MSG_GEN_ITEM_GROUP_SET_ITEM_STATE . If the list is on-screen, this message will cause a visual update. If the GenItemGroup is scrollable, this message will usually ensure that the item selected will appear on-screen.

This message clears the list's modified state. If you wish to set the group modified, send it a MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE . You may set a selection that is not usable, not enabled, or even not in the GenItemGroup. (This is useful for linked GenItemGroups.)

Source: Unrestricted.

Destination: Any GenItemGroup.

Parameters: identifier The identifier (enumerated value) of the item to select.

indeterminate
Pass TRUE if the group should be marked indeterminate.

Return: Nothing.

Interception: Generally not intercepted.

Tips: An item with a matching identifier need not be present to become the GenItemGroup's "selection." Using this knowledge, you can set up several "linked" GenItemGroups, each with only one selection. (See GenItemGroup Links for information and examples.)

MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS

void	MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS(
        word	*selectionList,
        word	numSelections);

This message sets multiple selections for a GenItemGroup. The caller must pass a pointer to a list of identifiers that should be selected, along with the number of selections to set. This message will clear the GenItemGroup's modified flag. If you wish to set the group modified send it a MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE .

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: selectionList A pointer to the buffer containing a list of item identifiers.

numSelections
The number of selections in the passed list.

Return: Nothing.

Interception: Generally not intercepted.

GenItemGroup States

GIGI_stateFlags, ATTR_GEN_ITEM_GROUP_SET_MODIFIED_ON_REDUNDANT_SELECTION, MSG_GEN_ITEM_GROUP_SET_INDETERMINATE_STATE, MSG_GEN_ITEM_GROUP_IS_INDETERMINATE, MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE, MSG_GEN_ITEM_GROUP_IS_MODIFIED

The GIGI_ stateFlags contains flags relating to the state of the entire GenItemGroup. The two flags possible are

  • GIGSF_INDETERMINATE
    This flag marks the GenItemGroup indeterminate. Only the group may be indeterminate; individual GenItems may not be marked indeterminate.
  • GIGSF_MODIFIED
    This flag marks the GenItemGroup modified. Only the group may be marked as modified; individual GenItems may not be marked as modified.

A GenItemGroup is in the indeterminate state if its GIGI_ stateFlags contains the GIGSF_INDETERMINATE flag. GenItems cannot be in an indeterminate state by themselves; either the entire list is indeterminate or it is not. If you want individual items to be indeterminate, you may want to use a GenBooleanGroup object instead.

A GenItemGroup is in the modified state if its GIGI_ stateFlags contains the GIGSF_MODIFIED flag. GenItems may not be in a modified state by themselves; either the entire list is modified or it is not. If you want to mark individual items as modified, you may want to use a GenBooleanGroup object instead.

In immediate mode, any user change to the GenItemGroup's state automatically sets the GIGSF_MODIFIED flag and also forces an "apply." Therefore, in immediate mode, any user changes will, by default, cause the apply action to be sent out immediately. In delayed mode, the GIGSF_MODIFIED flag is set, but an apply action will not automatically occur. The apply action will only occur when the user forces it (such as clicking on an "apply" trigger) or when the application forces it (by sending out a MSG_GEN_APPLY ).

You can force an apply by sending the GenItemGroup MSG_GEN_APPLY . You can also override the default behavior, forcing the object to send out its notification message on all applies, by including ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED in the object's instance data. Any time the object receives an "apply," the GenItemGroup will send out the notification message even if the object does not contain any modified GenItem children.

In general, if the user makes redundant changes (such as repeatedly selecting the same item) the group will not be marked modified--the group has not changed state since the last apply, so there is no need to send out a notification message. If, however, you wish for the group to be set modified on redundant changes, add the vardata attribute ATTR_GEN_ITEM_GROUP_SET_MODIFIED_ON_REDUNDANT_SELECTION to the object's instance data. Whenever the user makes any selection, the group will be marked modified.

To check whether a GenItemGroup is indeterminate, send it a MSG_GEN_ITEM_GROUP_IS_INDETERMINATE . The message will return true if the list is indeterminate, false if it is not.

To set a GenItemGroup indeterminate, send it MSG_GEN_ITEM_GROUP_SET_INDETERMINATE_STATE . Note that the messages to set a GenItemGroup's selections already provide an argument to set the GenItemGroup indeterminate.

To check whether a GenItemGroup has been modified, send it MSG_GEN_ITEM_GROUP_IS_MODIFIED . This will return true if the list has been modified, false if it has not. To set a GenItemGroup modified, send it a MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE .

MSG_GEN_ITEM_GROUP_SET_INDETERMINATE_STATE

void	MSG_GEN_ITEM_GROUP_SET_INDETERMINATE_STATE(
        Boolean		indeterminateState);

This message sets the GenItemGroup's GIGSF_INDETERMINATE flag in its GIGI_ stateFlags instance field. Usually this message is used in conjunction with MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS . ( MSG_GEN_ITEM_GROUP_SET_NONE_SELECTED and MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION set the indeterminate state themselves.)

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: indeterminate Pass TRUE to mark the group indeterminate, FALSE to set the group not indeterminate.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_IS_INDETERMINATE

Boolean	MSG_GEN_ITEM_GROUP_IS_INDETERMINATE();

This message checks whether a GenItemGroup is in the indeterminate state; it checks the GIGSF_INDETERMINATE flag in the GenItemGroup's GIGI_ stateFlags .

Source: Unrestricted.

Destination: Any GenItemGroup object.

Return: TRUE if the GenItemGroup is indeterminate, FALSE otherwise.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE

void	MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE(
        Boolean		modifiedState);

This message marks a GenItemGroup modified by setting the GIGSF_MODIFIED flag in its GIGI_ stateFlags instance field.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: modifiedState TRUE to mark the list modified, FALSE otherwise.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_IS_MODIFIED

Boolean	MSG_GEN_ITEM_GROUP_IS_MODIFIED();

This message checks whether a GenItemGroup has been modified by checking the GIGSF_MODIFIED flag in the GenItemGroup's GIGI_ stateFlags .

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: None.

Return: TRUE if GenItemGroup is marked modified, FALSE otherwise.

Interception: Generally not intercepted.

Sending the Event

GIGI_applyMsg, GIGI_destination, GEN_ITEM_GROUP_APPLY_MSG, ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED, MSG_GEN_ITEM_GROUP_GET_DESTINATION, MSG_GEN_ITEM_GROUP_SET_DESTINATION, MSG_GEN_ITEM_GROUP_GET_APPLY_MSG, MSG_GEN_ITEM_GROUP_SET_APPLY_MSG

When the GenItemGroup receives notice to apply any changes to its state, it will first check its GIGSF_MODIFIED flag. If the list has been modified, the GenItemGroup will send the apply action stored in its GIGI_ applyMsg field to the destination stored in its GIGI_ destination field.

GIGI_ applyMsg stores the apply action you wish the GenItemGroup to send out whenever you make a selection choice within the group. This message should be based on the prototype GEN_ITEM_GROUP_APPLY_MSG so that the proper arguments ( selection , numSelections , and stateFlags ) are automatically passed. GIGI_ destination should store the object or process you wish to handle the message sent out by this group. This object should have a handler for the GIGI_ applyMsg .

You can force a GenItemGroup to apply its changes with MSG_GEN_APPLY . You can also override the default behavior, forcing the object to send out its apply action on all applies, whether modified or not, by including ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED in the object's instance data. Any time the object needs to apply its changes, the GenItemGroup will send out the notification message even if the object is not marked GIGSF_MODIFIED.

To retrieve the object or process stored in the GenItemGroup's GIGI_ destination instance field or the message stored in its GIGI_ applyMsg instance field, send the list MSG_GEN_ITEM_GROUP_GET_DESTINATION or MSG_GEN_ITEM_GROUP_GET_APPLY_MSG .

To set the GenItemGroup's destination, send the list MSG_GEN_ITEM_GROUP_SET_DESTINATION , passing the optr of the new destination. To set the GenItemGroup's notification message, send the list MSG_GEN_ITEM_GROUP_SET_APPLY_MSG , passing it the message to use.

GEN_ITEM_GROUP_APPLY_MSG

void	GEN_ITEM_GROUP_APPLY_MSG(
        word	selection,
        word	numSelections
        byte	stateFlags);

Use this prototype to define the apply message for your GenItemGroup (stored in the GIGI_ applyMsg instance field). This prototype ensures that the message passes the correct parameters to your message handler.

Source: Your GenItemGroup object.

Destination: Destination of your GenItemGroup.

Parameters: selection The current selection of the GenItemGroup.

numSelections
The current number of selections of the GenItemGroup.
stateFlags
The current state flags of the GenItemGroup.

Return: Nothing.

MSG_GEN_ITEM_GROUP_GET_DESTINATION

optr	MSG_GEN_ITEM_GROUP_GET_DESTINATION();

This message returns the optr of the current destination object as specified in the GIGI_ destination instance field.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: Nothing.

Return: The optr of the current destination object.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_DESTINATION

void	MSG_GEN_ITEM_GROUP_SET_DESTINATION(
        optr	dest);

This message sets a new destination object for the list, stored in the list's GIGI_ destination field. The apply and status messages of this GenItemGroup will be sent to this new destination.

Source: Unrestricted.

Destination: Any GenItemGroup.

Parameters: dest The optr of the new destination object to be put in GIGI_ destination .

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_GET_APPLY_MSG

Message	MSG_GEN_ITEM_GROUP_GET_APPLY_MSG();

This message returns the current apply message in the GenItemGroup's GIGI_ applyMsg instance field.

Source: Unrestricted.

Destination: Any GenItemGroup.

Parameters: Nothing.

Return: The current apply message.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_APPLY_MSG

void	MSG_GEN_ITEM_GROUP_SET_APPLY_MSG(
        Message		message);

This message sets a new apply message in the GenItemGroup's GIGI_ applyMsg instance field.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: message The new notification message.

Return: Nothing.

Interception: Generally not intercepted.

Sending Status Messages

GEN_ITEM_GROUP_STATUS_MSG, ATTR_GEN_ITEM_GROUP_STATUS_MSG, MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG

If you wish your application to receive notice whenever the list's state changes, even if those changes will not be immediately applied, you may set a status message in the object's instance data.

For example, a color selection dialog box provides two means to set the RGB value of a color: one in a GenItemGroup listing the total possible states and another in a GenBooleanGroup with separate toggle states for Red, Green, and Blue. In delayed mode, setting the values in one group should change the display of the other group simultaneously but should not result in an apply action until the user activates the apply trigger.

One list can notify its partner of state changes with a status message. The other list can respond to state changes (and therefore change its corresponding state) without actually applying those changes.

To set up a status message, add a message based on the prototype GEN_ITEM_GROUP_STATUS_MSG. This message will be sent to the list's destination object. Set ATTR_GEN_ITEM_GROUP_STATUS_MSG to the status message.

Code Display 11-5 Status Message in GenItemGroup

/* Use the prototype GEN_ITEM_GROUP_STATUS_MSG to set up a status message. The
 * prototype takes three arguments: selection (the contents of GIGI_selection),
 * numSelections (the contents of GIGI_numSelections), and stateFlags (the contents
 * of GIGI_stateFlags). */
    /* In the destination's class definition: */
@message (GEN_ITEM_GROUP_STATUS_MSG) MSG_COLOR_STATUS_CHANGE;
    /* In the GenItemGroup's object declaration, set
     * ATTR_GEN_ITEM_GROUP_STATUS_MSG to the message you declared. */
@object GenItemGroupClass MyColorGroup = {
    GIGI_applyMsg = MSG_COLOR_CHANGE;
    GIGI_destination = process;
    ATTR_GEN_ITEM_GROUP_STATUS_MSG = MSG_COLOR_STATUS_CHANGE;
}
    /* Finally, in your handler for this status message, do required work. */
@method MyProcessClass, MSG_COLOR_STATUS_CHANGE {
    switch (selection) {
    case C_BLACK:
        /* ...(code)... */
    case C_WHITE:
        /* ...(code)... */
    }
}

GEN_ITEM_GROUP_STATUS_MSG

void	GEN_ITEM_GROUP_STATUS_MSG(
        word	selection,
        word	numSelections,
        byte	stateFlags);

Use this prototype to define a status message for your GenItemGroup (stored in the ATTR_GEN_ITEM_GROUP_STATUS_MSG vardata). This prototype ensures that your status message passes the correct parameters.

Source: Your GenItemGroup object.

Destination: The destination of your GenItemGroup.

Parameters: selection The current selection (enumerated type stored in GIGI_ selection ) of the GenItemGroup.

numSelections
The current number of selections (stored in GIGI_ numSelections ) of the GenItemGroup.
stateFlags
The current state flags (stored in GIGI_ stateFlags ) of the GenItemGroup.

Return: Nothing.

Tips: It is usually convenient to set up a switch statement based on the enumerated value of the selection passed in this message handler.

MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG

void	MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG(
        Boolean modifiedState);

This message causes the GenItemGroup to send out the status message to the destination object. This status message is stored in ATTR_GEN_ITEM_GROUP_STATUS_MSG .

Source: Unrestricted.

Destination: Any GenItemGroup object that contains a status message.

Parameters: modifiedState Pass TRUE if the GenItemGroup should be marked modified.

Return: Nothing.

Interception: Generally not intercepted.

List Focus

MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM, MSG_GEN_ITEM_GROUP_SET_FOCUS_ITEM

A GenItem in a GenItemGroup which has the cursor over it is said to have the focus. Your application may get or set the current focus of any exclusive GenItemGroup.

MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM

word  MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM();

Gets the focus item of a GenItemGroup.

Source: Anywhere.

Destination: Any GenItemGroup.

Return: The identifier of the item which currently has the focus. If the GenItemGroup is not an exclusive list or if no item has focus, this message will return GIGS_NONE.

Tips: This message differs from MSG_META_GET_FOCUS_EXCL because it works on lists that don't have the focus and it does not suffer from possible synchronization problems.

MSG_GEN_ITEM_GROUP_SET_FOCUS_ITEM

 void   MSG_GEN_ITEM_GROUP_SET_FOCUS_ITEM(word identifier);

This message scrolls the GenItemGroup list so that the specified item will be the visible focus. While this message can be sent to a GenItemGroup at any time, it will only affect item groups which have already been visually built.

Source: Anywhere.

Destination: Any GenItemGroup.

Parameters:

identifier
The ID of the item which will be the focus of the item group. identifier may represent an item that is not usable, not enabled, not in the item group, or it may even be set to GIGS_NONE and represent no item. If identifier cannot be made the focus of the list, then no item in that list will have the exclusive focus.

Return: Nothing.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 GenItem Instance Data | Next: 3.4 Working with Items


Working with Items

The List Objects: 3.4 GenItemGroups: Working with Items

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 GenItemGroup Basics | Next: 3.5 Scrolling GenItemGroups

With several GenItem and GenItemGroup messages, you can work with individual items. The following sections detail these messages.

Altering the Identifiers

MSG_GEN_ITEM_GET_IDENTIFIER, MSG_GEN_ITEM_SET_IDENTIFIER, MSG_GEN_ITEM_GROUP_GET_UNIQUE_IDENTIFIER

You may change the identifiers of GenItems dynamically. To retrieve a GenItem's identifier, send the GenItem MSG_GEN_ITEM_GET_IDENTIFIER . To set a GenItem's identifier, send it MSG_GEN_ITEM_SET_IDENTIFIER , passing the message the value you wish to set it to. Make sure that this value is unique and not duplicated by any other GenItem in the same GenItemGroup.

To generate a unique identifier for an item in a group, the GenItemGroup may use MSG_GEN_ITEM_GROUP_GET_UNIQUE_IDENTIFIER .

MSG_GEN_ITEM_GET_IDENTIFIER

word	MSG_GEN_ITEM_GET_IDENTIFIER();

This message returns the identifier stored in the item's GII_ identifier instance field.

Source: Unrestricted.

Destination: Any GenItem object.

Return: The identifier of the GenItem object.

Interception: Generally not intercepted.

MSG_GEN_ITEM_SET_IDENTIFIER

void	MSG_GEN_ITEM_SET_IDENTIFIER(
        word	identifier);

This message sets a GenItem's GII_ identifier instance field to a new value. There is no effect on the list itself. The item whose identifier is changed must not be currently selected. It is therefore up to the application to make sure that the item is de-selected before changing its identifier.

Source: Unrestricted.

Destination: Any GenItem object.

Return: Nothing.

Interception: Generally not intercepted.

Warnings: The new enumerated value should be unique among all of the items in the list. Use MSG_GEN_ITEM_GROUP_GET_UNIQUE_IDENTIFIER .

MSG_GEN_ITEM_GROUP_GET_UNIQUE_IDENTIFIER

word	MSG_GEN_ITEM_GROUP_GET_UNIQUE_IDENTIFIER();

This message returns an identifier that is unique among the items in the GenItemGroup's group. It does not check across links, however.

Source: Unrestricted.

Destination: Any non-usable GenItemGroup object.

Parameters: None.

Return: The unique item identifier.

Interception: Generally not intercepted.

Altering the Item's State

MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR, MSG_GEN_ITEM_GROUP_SET_ITEM_STATE, MSG_GEN_ITEM_GROUP_IS_ITEM_SELECTED

You can retrieve the optr of a selection by sending the GenItemGroup a MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR , passing the identifier of the requested item. You may then use this item optr to send messages directly to the individual items or to set items enabled/disabled, usable/not usable, etc. If the item is not found, this message will return null.

You may also set an item selected without altering any other settings in the GenItemGroup. Send the list a MSG_GEN_ITEM_GROUP_SET_ITEM_STATE , passing it the identifier of the item to mark as selected. If the list is exclusive or exclusive-none, any other settings will be deselected; if the list is extended-selection or non-exclusive, the other settings will remain intact.

To check on whether an item is selected, send the GenItemGroup a MSG_GEN_ITEM_GROUP_IS_ITEM_SELECTED , passing it the identifier of the item in question. This message will return true if the item is currently selected, false if it is not.

Send MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBILE to force a scrolling GenItemGroup to scroll to an item. Pass this message the identifier of the requested item. This message has no effect on non-scrolling lists.

MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR

optr	MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR(
        word	identifier);

This message returns the optr of a GenItem within a GenItemGroup. You may then use this optr for directly manipulating the GenItem. If an item with the requested identifier is not found as one of the GenItemGroup's children, this message will return a null optr.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: identifier The identifier of the item to search for (stored in the GenItem's GII_ identifier instance field).

Return: The optr of the GenItem with the requested identifier (or a null optr if none is found).

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_ITEM_STATE

void	MSG_GEN_ITEM_GROUP_SET_ITEM_STATE(
        word		identifier,
        Boolean		state);

This message sets the state (selected or unselected) of a particular GenItem within a GenItemGroup. Other items will be unaffected by the new state. This message also clears the GenItemGroup's modified state. If an identifier is passed which does not match an item within the GenItemGroup (or matches a not usable item), it will still be made the selection.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: identifier The identifier of the item to change state.

state
TRUE to mark item selected, FALSE to mark item unselected.

Return: Nothing.

Interception: Generally not intercepted.

Tips: An item with a matching identifier need not be present to become the GenItemGroup's selection. Using this knowledge, you can set up several "linked" GenItemGroups, each with only one selection. (See GenItemGroup Links for information and examples.)

MSG_GEN_ITEM_GROUP_IS_ITEM_SELECTED

Boolean	MSG_GEN_ITEM_GROUP_IS_ITEM_SELECTED(
        word	identifier);

This message checks whether an item within a GenItemGroup is selected, even if that item is not usable, not enabled, or not within the GenItemGroup. The message returns true if the item is selected, false if it is not selected.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: identifier The identifier of the item to check for.

Return: Will return true if the item is selected, false if it is not.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 GenItemGroup Basics | Next: 3.5 Scrolling GenItemGroups


Scrolling GenItemGroups

The List Objects: 3.5 GenItemGroups: Scrolling GenItemGroups

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Working with Items | Next: 3.6 GenItemGroup Links
HINT_ITEM_GROUP_SCROLLABLE, MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE

A scrolling list is a special version of a GenItemGroup that allows the user to scroll through a list of items which are not all visible at one time. Scrolling lists usually provide scrollbars or some other means to quickly scan through a list. You may make a GenItemGroup scrollable if the specific UI allows by including the hint HINT_ITEM_GROUP_SCROLLABLE in the list's object declaration.

The list will come up in a standard size determined by the specific UI. You may override this by adding a size hint to the object ( HINT_FIXED_SIZE , HINT_INITIAL_SIZE , etc.). Notification of changes for a scrolling list, as well as setting and getting the selections, works the same for a scrolling list as for a normal GenItemGroup. If an item is selected, the scrolling list will almost always scroll automatically to the selected item.

To force a GenItemGroup to scroll to an item, send MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE . Pass this message the identifier of the item to be made visible in the scrolling list. This message has no effect on a non-scrolling list.

You may wish in some cases for a scrolling list to be for display only. If so, set the scrollable GenItemGroup's GI_ attrs instance data to include the flag GA_READ_ONLY. The user will be unable to interact with a display-only scrolling list (although, of course, the user will still be able to scroll through the list).

If your scrolling list will have many settings or will often be dynamically updated, you should consider using a GenDynamicList instead.

MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE

void	MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE(
        word	identifier);

This message ensures that an item within a scrolling GenItemGroup is visible, by scrolling the list if necessary. This message has no effect if the GenItemGroup is not scrollable or is not usable.

Source: Unrestricted.

Destination: Any scrollable, usable GenItemGroup object.

Parameters: identifier The identifier of the item to be made visible.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Working with Items | Next: 3.6 GenItemGroup Links


GenItemGroup Links

The List Objects: 3.6 GenItemGroups: GenItemGroup Links

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Scrolling GenItemGroups | Next: 3.7 Limitations of the GenItemGroup
ATTR_GEN_ITEM_GROUP_LINK

In some cases, geometry constraints interfere with functionality. For example, you may wish for "nested" GenItemGroup behavior. Each main selection should act as an exclusive selection within the GenItemGroup, but it should also allow other generic objects "in-between" these exclusive items. Normal geometry constraints prevent this, as GenItemGroups may only contain GenItems as children.

To enable this, you may set up each main section as a separate GenItemGroup with a single selection. Within each GenItemGroup, include a link to the next GenItemGroup with ATTR_GEN_ITEM_GROUP_LINK . Links should be circular; i.e., if three GenItemGroups should be linked, the first group should point to the second, the second to the third, and the third back to the first. The three linked GenItemGroups will act as one entity. If each GenItemGroup is exclusive, one group will select its single item child if selected and the other groups will deselect their children.

These links work because you are allowed to "select" items that are not actually within a GenItemGroup. If an item not within the group is selected, any current selection will therefore be de-selected.

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Scrolling GenItemGroups | Next: 3.7 Limitations of the GenItemGroup


Limitations of the GenItemGroup

The List Objects: 3.7 GenItemGroups: Limitations of the GenItemGroup

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 GenItemGroup Links | Next: 4 GenDynamicListClass

GenItemGroups may only have GenItems as children. Likewise, GenItems must always be generic children of a GenItemGroup object. There also is no capability to allow some items under a GenItemGroup object to be exclusive while others are non-exclusive. This can often be accomplished instead by making the list non-exclusive and clearing other items when necessary to create the exclusive behavior.

In some cases, you may wish to store modified or indeterminate states for individual items rather than for only the whole group. In this case, you should probably use a GenBooleanGroup.

For long lists of items or lists that may require frequent updating, you might wish to use a GenDynamicList instead.

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 GenItemGroup Links | Next: 4 GenDynamicListClass


GenDynamicListClass

The List Objects: 4 GenDynamicListClass

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 GenItemGroups | Next: 5 GenBooleanGroups

The GenDynamicListClass is a subclass of GenItemGroupClass that allows an application to pass monikers to the object as needed rather than specify a GenItem for each item in the list. In almost all cases, you will want a GenDynamicList to be a scrolling list so that not all of the list entries will be displayed at any given time. (You can, however, still choose to create a non-scrolling dynamic list.)

The GenDynamicList displays a list of monikers; this list of monikers is usually in the form of a data chunk managed by some other (application-supplied) object. For each moniker the GenDynamicList wishes to display, it will send out a query message and the position of the moniker requested. The object handling the query message will return a pointer to the moniker and will draw the moniker within the GenDynamicList. In this way, monikers for a GenDynamicList are only drawn as needed.

The dynamic list allows the following:

  • Long Lists
    Very long lists can be stored by the application in any way it wants and as efficiently as possible. Because these lists are often scrolling lists, the UI is not cluttered up with a multitude of list entries. Only those within the scroller's bounds will be visually displayed.
  • Dynamic Display of Data
    Applications can easily create a scrolling list from arbitrarily organized data. As long as you provide a means to convert the data into the proper moniker format, you can use any data you wish for moniker entries. You can also dynamically access this data and update the list entries.

An application specifies the number of items in the list and if desired can set up a custom size for the dynamic list. Whenever the object needs to display a moniker, it will query its destination for the visual moniker to display as an item in the list. At any time, the application can remove or add items from the dynamic list or change the total number of items and start with all new items. Because the list only queries for items as they become visible, it is more efficient than having to display a large number of entries in a long list.

Since GenDynamicListClass is a subclass of GenItemGroupClass , all of the same messages that are passed to GenItemGroups can be passed to GenDynamicList (except MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR , which has no meaning for a GenDynamicList). The lists can be exclusive, exclusive-none, extended-selection, or non-exclusive. The user can select and deselect items depending on the behavior type, etc.

DynamicList Instance Data

DynamicList Basics

Altering Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 GenItemGroups | Next: 5 GenBooleanGroups


DynamicList Instance Data

The List Objects: 4.1 GenDynamicListClass: DynamicList Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 4 GenDynamicListClass | Next: 4.2 DynamicList Basics

GenDynamicListClass inherits all of the instance fields of GenItemGroupClass . The GenDynamicList object provides two additional instance fields:

@instance word GDLI_numItems = 0;
@instance word GDLI_queryMsg = 0;

GDLI_ numItems stores the number of items in the dynamic list. This field should be set to the initial number of monikers needed within your GenDynamicList. If this value is initially unknown (i.e. is retrieved dynamically) then you should set this value after the number of items becomes known (using MSG_GEN_DYNAMIC_LIST_INITIALIZE ). If this instance field is zero, no items will be drawn.

GDLI_ queryMsg stores the querying message that the dynamic list sends each time it needs to display an entry's moniker. (For example, when initially realized, a scrolling dynamic list may display only four out of 16 entries; this message will be sent out four times to retrieve the monikers for the items initially displayable.)

The query message will be handled by whatever object you set in GIGI_destination . The handler should search a list of data for the visual monikers to display within the list. You will want to set up a custom handler for this message and the list of data to search through. Note that this list of data will serve as the GenDynamicList's "children"; there are no GenItems allowed as children of a GenDynamicList. The query message is based on the prototype GEN_DYNAMIC_LIST_QUERY_MSG .

GEN_DYNAMIC_LIST_QUERY_MSG

void	GEN_DYNAMIC_LIST_QUERY_MSG(
        optr	list,
        word	item);

Use this prototype to define the query message for your GenDynamicList (stored in GDLI _queryMsg ). This prototype ensures that the message passes the correct parameters to your message handler.

Source: Your GenDynamicList object.

Destination: The destination of your GenDynamicList object ( GIGI_destination ).

Parameters: list The optr of the list.

item
The item (position number) needing a moniker.

Return: Nothing.

| Up: GEOS SDK TechDocs | Up | Prev: 4 GenDynamicListClass | Next: 4.2 DynamicList Basics


DynamicList Basics

The List Objects: 4.2 GenDynamicListClass: DynamicList Basics

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 DynamicList Instance Data | Next: 4.3 Altering Instance Data
MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER, MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR, MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT

You will need to perform several preparatory steps in order to use a GenDynamicList object, even in its most basic form. The following steps are required for all dynamic lists.

  • Create a GenDynamicList object. You must include the number of items to appear in your list (or set them dynamically when this number is known) and a query message to handle the extraction and display of monikers from a list of data.
  • Create a list of data to use as entries in the list. For simplicity, this can be a chunk of data containing a list of visual monikers, although the data may come from any source--as long as you provide the means to convert the data into monikers.
  • Create a query message handler to search through the list of data. This handler should convert any data into a proper format (if necessary) and send the moniker to the dynamic list to display the item. You can convert the data into a proper format by using one of several messages provided with GenDynamicListClass . These messages may take text strings, visual monikers, or other forms of data to set the monikers for the dynamic list.

Code Display 11-6 Creating a GenDynamicList object

/* In the definition of your destination object's class, define a message based
 * on the prototype GEN_DYNAMIC_LIST_QUERY. This prototype passes two arguments:
 * list (the optr of the list sending info) and item (the selection enum). */
@message (GEN_DYNAMIC_LIST_QUERY_MSG) MSG_QUERY_COLOR_MONIKER;
/* Set up the selections, just as you would in a normal GenItemGroup. */
typedef enum {
    MC_RED, MC_YELLOW, MC_BLUE, MC_GREEN
} MyColors;
#define NUM_COLORS MC_GREEN+1
/* In the object declaration, include normal instance data for a GenItemGroup,
 * making sure to also fill in GDLI_numItems and GDLI_queryMsg with the proper
 * information. Note that there are no generic children provided for this object.
 * The list will retrieve and display monikers automatically through the query
 * message as they are needed. */
@object GenDynamicListClass MyList = {
    /* Set the initial selection MC_BLUE (and the number of items selected to 1).*/
    GIGI_selection = MC_BLUE;
    GIGI_applyMsg = MSG_NOTIFY_COLOR_CHANGE;
    GIGI_destination = process;
    /* Set numItems to the total number of items to appear in this list. If you
     * will not know this initially, make sure to set this instance data when you
     * do know (with MSG_GEN_DYNAMIC_LIST_INITIALIZE). */
    GDLI_numItems = NUM_COLORS;
    GDLI_queryMsg = MSG_QUERY_COLOR_MONIKER;
    HINT_ITEM_GROUP_SCROLLABLE;
}

When a dynamic list is first built, the specific UI will decide how many items within the scrolling list should be initially displayed. For each of these items, the dynamic list will send out a query message, passing the number (position) of the item to search for. The querying message will search through a list of data (either custom or pre-existing), extracting and converting the data into text, graphics strings, or visual monikers, etc.

Finally, your query message handler should set each moniker in the list using one of the MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER messages. There are three messages to set the moniker of a dynamic list entry:

  • MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER
     This is a general purpose moniker replacement message. It can take a variety of source types (optrs, fptrs, hptrs), data types (visMonikers, text strings, graphics strings, or even geode tokens), and (in the case of graphics strings) the height and width of the moniker. This message also provides a flag allowing a moniker entry to appear visually disabled (grayed out in OSF/Motif).
  • MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR
     This is a simplified version of the above message which takes a visual moniker and displays it within the dynamic list. The message's only arguments are the item in the list to update and the optr of the moniker for that item.
  • MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT
     This is another simplified version of the first message which takes a text string and displays it within the dynamic list. The message's only arguments are the item in the list to update and a pointer to the text string to use.

The simpler messages are useful if your data is a list of complete visual moniker structures or is a list of text strings. If you need to retrieve special data (such as GStrings or geode tokens), or if you need to perform special operations on the moniker (such as bringing it up disabled), then you must use MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER .

The data itself may be placed in several areas of your code. If the data is simple text strings, set up within an array, it may appear within the dynamic list's object block, its own resource block, or within the handler for the query message. If the data is visual monikers, the data must appear within a resource block.

If the data is within a different block from the dynamic list, that block must be locked before its data can be accessed. Make sure to unlock the block after using the data.

Code Display 11-7 Creating the List of Data

/* Create the list of data (in this case within its own data block). This list of
 * data will be in form of text strings. */
@start ItemText, data;
@chunk char *listMonikers[NUM_COLORS] = {
    "Red", "Yellow", "Blue", "Green"
};
@end ItemText
/* Create the query message handler. Each time the GenDynamicList needs to display
 * an item, it will call this querying message with the number (position) of the
 * item it needs to display. In this case, since the monikers are already in the
 * form of text strings, you can use MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT
 * to set the moniker of the list item using the text string.
 * DEFINITION: void MSG_QUERY_COLOR_MONIKER(optr list, word item) */
@method MyProcessClass, MSG_QUERY_COLOR_MONIKER {
char **listString;
    /* The method will retrieve the entry's text moniker from the array of
     * text monikers in listMonikers. It locks itemText, extracts the
     * text string, and uses the REPLACE_ITEM message to set the dynamic list
     * entry. */
    MemLock(HandleOf(@listMonikers));
    listString = LMemDeref(@listMonikers);
    /* Then send off the item string to the dynamic list
     * and unlock the data block. */
    @call listObject::MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT(item, listString[item]);
    MemUnlock(HandleOf(@listMonikers));
}

MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER

void	MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER(@stack
        word			item,
        word			flags,
        word			height,
        word			width,
        word			length,
        VisMonikerDataType			dataType,
        VisMonikerSourceType			sourceType,
        dword			source);

This message sends a moniker to use for a particular list item and is usually sent in response to a moniker query by a dynamic list object. Within your query message handler (GDLI_ queryMsg ), you should access the data for the moniker to use and send off this message with the proper parameters. You may also use this message to change an item's moniker, guaranteeing that it is updated if currently visible.

If you pass this message the flag RIMF_NOT_ENABLED, the moniker for the item will be disabled (usually grayed out).

On the Nokia 9000i Communicator devices, you can use the routine CreateVisMonikerLine() to to arrange the line's contents so that they will form columns when displayed with similarly arranged lines.

This is an all-purpose message that allows many different source types and many ways to reference the data in these sources. If you are using lists of visual monikers or simple text strings and you do not to perform any fancy operations on this data, you may use MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR or MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT .

Source: The object handling a dynamic list's query message.

Destination: The GenDynamicList specified in the query message.

Parameters: item The position of the item needing a moniker.

flags
RIMF_NOT_ENABLED if the item's moniker should be shown disabled.
height
If the item is a GString, the height in points.
width
If the item is a GString, the width in points.
length
Size of moniker data, in bytes. This value is ignored for VMST_OPTR. If the dataType is VMDT_TEXT and length is zero, the text moniker is assumed to be null-terminated. If the dataType is VMDT_GSTRING and the length is zero, the GString is assumed to end with a GR_END_GSTRING element.
dataType
The VisMonikerDataType of the actual moniker.
sourceType
The type of pointer referencing the moniker.
source
A pointer to the moniker data.

Structures: The VisMonikerDataType and VisMonikerSourcetype are defined by VisClass . Their values are shown here for easy reference.

  typedef ByteEnum VisMonikerDataType;
/*	VMDT_NULL
 *	VMDT_VIS_MONIKER
 *	VMDT_TEXT
 *	VMDT_GSTRING
 *	VMDT_TOKEN */
  typedef ByteEnum VisMonikerSourceType;
/*	VMST_FPTR
 *	VMST_OPTR
 *	VMST_HPTR */

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR

void	MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR(
        word	item,
        optr	moniker);

This message is a simplified version of MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER . The moniker must be of type VisMoniker and referenced by an optr. No special operations (such as marking the item disabled) are possible with this message. In those cases, use MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER .

Source: The object handling a dynamic list's query message.

Destination: The GenDynamicList object specified by the query message.

Parameters: item The position of the item to display.

optr
The optr of the VisMoniker to use.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT

void	MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT(
        word	item,
        char	*text);

This message is a simplified version of MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER . The moniker passed must be a null-terminated text string. No special operations (such as marking the item disabled) are possible with this message. In those cases, use MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER .

Source: The object handling a dynamic list's query message.

Destination: The GenDynamicList object specified by the query message.

Parameters: item The position of the item to display.

text
A pointer to the null-terminated text string.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 DynamicList Instance Data | Next: 4.3 Altering Instance Data


Altering Instance Data

The List Objects: 4.3 GenDynamicListClass: Altering Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 DynamicList Basics | Next: 5 GenBooleanGroups

You may alter the instance data of a GenDynamicList dynamically. Any of the appropriate GenItemGroup messages are valid for a GenDynamicList object except for MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR .

Altering the Number of Items

MSG_GEN_DYNAMIC_LIST_INITIALIZE, MSG_GEN_DYNAMIC_LIST_GET_NUM_ITEMS

In order to ensure the proper display of monikers in a dynamic list, the list object must know the total number of items that may be displayed. In most cases, you will set this number in the initial instance data for the object. In some cases, however, you will not initially know how many entries may be displayed; the total number of items will only be known after the data block is accessed.

In these cases, you should use MSG_GEN_DYNAMIC_LIST_INITIALIZE , MSG_GEN_DYNAMIC_LIST_ADD_ITEMS or MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS to dynamically set the GDLI_ numItems instance field. You can retrieve the current number of items with MSG_GEN_DYNAMIC_LIST_GET_NUM_ITEMS .

MSG_GEN_DYNAMIC_LIST_INITIALIZE

void	MSG_GEN_DYNAMIC_LIST_INITIALIZE(
        word	numItems);

This message initializes a dynamic list, updating the display to reflect a new number of items or new data within the moniker list. If the list is already usable, this message invalidates all the current items and requests new monikers for all displayable items. The dynamic list will clear all previous selections; it will not scroll to any new selections. Use MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE to scroll to a selected item.

This message is useful for cases when initially building a dynamic list without knowing the number of items; after you know the number of items, make sure to send the dynamic list this message.

Source: Unrestricted.

Destination: Any GenDynamicList object.

Parameters: numItems The new number of items for the dynamic list or GDLI_NO_CHANGE to keep the current number of selections.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_DYNAMIC_LIST_GET_NUM_ITEMS

word	MSG_GEN_DYNAMIC_LIST_GET_NUM_ITEMS();

This message returns the current number of items for a dynamic list. If no items are within the list, this message returns zero.

Source: Unrestricted.

Destination: Any GenDynamicList object.

Parameters: None.

Return: The number of items in the GenDynamicList (GDLI_ numItems ).

Adding and Removing Items Dynamically

MSG_GEN_DYNAMIC_LIST_ADD_ITEMS, MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS, MSG_GEN_DYNAMIC_LIST_REMOVE_ITEM_LIST

Part of the strength of a dynamic list is its ability to be easily updated with new data. MSG_GEN_DYNAMIC_LIST_ADD_ITEM S adds one or more new entries at any position in a dynamic list. The total number of items (GDLI_ numItems ) will be automatically incremented, and all entries after the added item will have their positions increased by one.

The constants GDLP_FIRST and GDLP_LAST indicate either the first or last item, respectively, and are available for use with these messages.

MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS removes one or more entries from any position within a dynamic list. The total number of items (GDLI_ numItems ) will be decremented, and all entries after the deleted item will have their positions decreased by one.

MSG_GEN_DYNAMIC_LIST_ADD_ITEMS

void	MSG_GEN_DYNAMIC_LIST_ADD_ITEMS(
        word	item,
        word	numItems);

This message adds an item after the position passed. All items that previously appeared at or after the position specified will have their positions incremented, and the total number of items will be incremented by numItems .

Usually, you will send this message after a change in the moniker data occurs. The moniker for the new item will be requested when it is made visible.

Source: Unrestricted.

Destination: Any GenDynamicList object.

Parameters: item The position of the first item to be added to the list. You may pass GDLP_FIRST to add the items at the beginning of the list or GDLP_LAST to add them at the end.

numItems
The number of items to add.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS

void	MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS(
        word	item,
        word	numItems);

This message removes the specified item(s) from a dynamic list at the position passed. All items that previously appeared after those deleted will have their positions decremented, and the total number of items will be decremented by the number of items removed.

Usually, you will send this message after a change in the moniker data. If the moniker for the deleted item is visible, a visual update will occur to reflect the list's new item positions. If the deleted item was selected, it will be deselected before being removed.

Source: Unrestricted.

Destination: Any GenDynamicList object.

Parameters: itemPosition The position of first item to be removed. GDLP_LAST or GDLP_FIRST specify the first or last position in the list.

numItems
The number of items to be removed.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_DYNAMIC_LIST_REMOVE_ITEM_LIST

void	MSG_GEN_DYNAMIC_LIST_REMOVE_ITEM_LIST(
        word	*deletionList,
        word	numItems);

This message removes a list of items from a dynamic list. Therefore, unlike MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS, this message can remove separate items that occur at different positions in the list. The message updates other item positions appropriately, causing a visual refresh, if necessary, and updates the total number of items.

Source: Unrestricted.

Destination: Any GenDynamic List.

Parameters: deletionList Pointer to buffer storing positions (word values) of the items to be removed.

numItems
Number of items being deleted.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 DynamicList Basics | Next: 5 GenBooleanGroups


GenBooleanGroups

The List Objects: 5 GenBooleanGroups

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 GenDynamicListClass | Next: 5.1 GenBooleanGroup Instance Data

GenBooleanGroupClass provides a list of items (GenBoolean objects) that may be individually set regardless of the state of other items in the list. The GenBooleanGroup is similar in behavior to a non-exclusive GenItemGroup in that each item may be selected independently of other selections. Unlike a non-exclusive GenItemGroup, however, GenBooleanGroup also allows your application to keep track of individual states for each item.

The GenBooleanGroup uses a word of data (16 bits) to keep track of the various states of its GenBoolean children. As a result, the maximum number of children allowed for a GenBooleanGroup object is 16. If you need non-exclusive behavior but have more than 16 children, you should either split the children between several GenBooleanGroups or use a non-exclusive GenItemGroup (or GenDynamicList).

GenBooleanClass is the basic item class for the GenBooleanGroupClass . It contains an entry that is set on or off. For example, a menu of font styles might have many entries such as "bold" or "italic," each of which could be on or off, independent of each other. Each of those entries should be represented as a GenBoolean object.

GenBooleanGroup Instance Data

GenBooleanGroup Usage

Altering Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 GenDynamicListClass | Next: 5.1 GenBooleanGroup Instance Data


The List Objects: 5.1 GenBooleanGroups: GenBooleanGroup Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 5 GenBooleanGroups | Next: 5.2 GenBooleanGroup Usage

GenBooleanGroup and GenBoolean objects contain a variety of information. The bulk of the functionality of the list is contained in the GenBooleanGroup; the items themselves only contain identifying information that the list uses.

GenBooleanGroup Instance Data

The GenBooleanGroup contains instance fields relating to the state of its individual children. These instance fields are listed in GenBooleanGroup Instance Fields .

Code Display 11-8 GenBooleanGroup Instance Fields

    @instance word			GBGI_selectedBooleans = 0;
    @instance word			GBGI_indeterminateBooleans = 0;
    @instance word			GBGI_modifiedBooleans = 0;
    @instance optr			GBGI_destination;
    @instance Message			GBGI_applyMsg = 0;
@vardata Message			ATTR_GEN_BOOLEAN_GROUP_STATUS_MSG;
@vardata optr			ATTR_GEN_BOOLEAN_GROUP_LINK;
    @reloc ATTR_GEN_BOOLEAN_GROUP_LINK, 0, optr;
@vardata void			ATTR_GEN_BOOLEAN_GROUP_INIT_FILE_BOOLEAN;
/* Hints */
@vardata void			HINT_BOOLEAN_GROUP_SCROLLABLE;
@vardata void			HINT_BOOLEAN_GROUP_MINIMIZE_SIZE;
@vardata void			HINT_BOOLEAN_GROUP_CHECKBOX_STYLE;
@vardata void			HINT_BOOLEAN_GROUP_TOOLBOX_STYLE;

GBGI_ selectedBooleans is a record representing the selection state of the GenBooleanGroup's children. If a bit is set, the GenBoolean corresponding to that position is selected. Because of this field's size, no more than 16 GenBooleans may belong to a single GenBooleanGroup.

GBGI_ indeterminateBooleans is a record representing the indeterminate state of the GenBooleanGroup's children. A GenBoolean is indeterminate if it cannot be considered either selected or unselected. If a bit is set, the GenBoolean corresponding to that position is indeterminate.

GBGI_ modifiedBooleans is a record representing the modified state of the GenBooleanGroup's children. A GenBoolean is modified if its state (selected, deselected, or indeterminate) has changed since the last apply. If a bit is set, the GenBoolean corresponding to that position has been marked modified.

GBGI_ destination contains the optr of the object or process that handles messages sent out by the GenBooleanGroup. This destination object will receive the apply action stored in GBGI_ applyMsg whenever the user changes the GenBooleanGroup and that state change is later applied.

GBGI_ applyMsg stores the apply action for the GenBooleanGroup to send out whenever a MSG_GEN_APPLY takes place, either through an immediate state change or through a delayed mode activation.

ATTR_GEN_BOOLEAN_GROUP_STATUS_MSG assigns a status message to your GenBooleanGroup. A status message allows objects to receive notice of a change in the BooleanGroup's current state without forcing an apply. This attribute is useful for cases where the state within your BooleanGroup may reflect information within other user interface objects and should therefore be updated whenever its user changes occur, regardless of the need to apply those changes.

ATTR_GEN_BOOLEAN_GROUP_LINK allows two or more GenBooleanGroups to be linked together and act as one BooleanGroup. Each BooleanGroup may contain one or more booleans with constant values unique to all other booleans within the linked BooleanGroups. The links should also be circular, with the last group pointing back to the first. All of these BooleanGroups should have identical states so they will work correctly.

ATTR_GEN_BOOLEAN_GROUP_INIT_FILE_BOOLEAN forces the GenBooleanGroup to use "true" and "false" values in the GEOS.INI file.

HINT_BOOLEAN_GROUP_SCROLLABLE implements the Boolean Group as a scrollable list, if that feature is supported by the specific UI. HINT_BOOLEAN_GROUP_MINIMIZE_SIZE instructs the UI to minimize the size of the Boolean Group. Depending on the specific UI, the Boolean group may be placed within a popup list or made scrollable.

HINT_BOOLEAN_GROUP_CHECKBOX_STYLE indicates that the Boolean Group should display its Booleans with the "checkbox" style, if that style is supported by the Specific UI. HINT_BOOLEAN_GROUP_TOOLBOX_STYLE indicates that the group should use the toolbox style, with a simple box around the Boolean's visual moniker. This style is the default for toolboxes in OSF/Motif, but not for other Booleans.

GenBoolean Instance Data

GenBooleanClass contains one additional instance field:

@instance word			GBI_identifier;

GBI_ identifier sets the identifying keyword (a 16-bit mask representing a specific bit--or bits--within a word-length bitfield) that uniquely identifies this GenBoolean. This identifier is used by the GenBooleanGroup parent to reference the Boolean. No two Booleans may specify common bits set, although a single Boolean's identifier may have several bits set.

| Up: GEOS SDK TechDocs | Up | Prev: 5 GenBooleanGroups | Next: 5.2 GenBooleanGroup Usage


GenBooleanGroup Usage

The List Objects: 5.2 GenBooleanGroups: GenBooleanGroup Usage

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 GenBooleanGroup Instance Data | Next: 5.3 Altering Instance Data

The GenBooleanGroup manages up to 16 GenBoolean objects. You must define a new (or use a pre-existing) constant to represent each GenBoolean within the GenBooleanGroup. Each constant represents a bit within a word-length record.

The GenBooleanGroup uses 16-bit records to designate its list of items; each bit in the record corresponds to an item. The GenBooleanGroup keeps three such records: GBGI_ selectedBooleans , GBGI_ indeterminateBooleans , and GBGI_ modifiedBooleans to represent the selected state, the indeterminate state, and the modified state of each GenBoolean.

Selected Booleans

GBI_identifier, GBGI_selectedBooleans

GBI_ identifier stores the identifying keyword for a GenBoolean item. The BooleanGroup object will always use this identifier when referring to GenBooleans. These identifiers are set up as constants representing bits in a 16-bit record. (See Setting Up a GenBooleanGroup .)

Code Display 11-9 Setting Up a GenBooleanGroup

/* The identifiers for any particular GenBooleanGroup are
 * constant bits within a word-length bitfield. */
typedef WordFlags USCities;
#define USC_CHICAGO 				0x0001		/* The first bit */
#define USC_NEW_YORK				0x0002		/* The second bit */
#define USC_SAN_FRANCISCO				0x0004		/* The third bit */
@object GenBooleanGroupClass CityList = {
    GI_comp = @Chicago, @NewYork, @SanFrancisco;
    /* The first and second bit in the selectedBooleans record are set to
     * indicate that Chicago and New York are initially selected. */
    GBGI_selectedBooleans = USC_CHICAGO | USC_NEW_YORK;
}
@object GenBooleanClass Chicago = {
    GI_visMoniker = "Chicago";
    GBI_identifier = USC_CHICAGO;
}
@object GenBooleanClass NewYork = {
    GI_visMoniker = "New York";
    GBI_identifier = USC_NEW_YORK;
}
@object GenBooleanClass SanFrancisco = {
    GI_visMoniker = "San Francisco";
    GBI_identifier = USC_SAN_FRANCISCO;
}

GBGI_ selectedBooleans stores a 16-bit record corresponding to the selection state of a GenBooleanGroup's children. Selecting or deselecting the Booleans will alter the bit representing that GenBoolean child in this instance field. By setting selections in this field, you can specify initial selections for the GenBooleanGroup to initially appear set.

Indeterminate and Modified Booleans

GBGI_indeterminateBooleans, GBGI_modifiedBooleans

GBGI_ indeterminateBooleans stores a 16-bit record corresponding to the indeterminate state of a GenBooleanGroup's children. Your BooleanGroup may appear initially with GenBooleans in an indeterminate state. As opposed to a GenItemGroup, where either the entire list is indeterminate or not, individual Booleans may be indeterminate on their own accord.

A GenBoolean may be indeterminate while it is either selected or unselected. Making an item indeterminate will not change its selected state, though the item's visual representation might change. (In OSF/Motif, an indeterminate selection appears "off" whether or not it is also selected.)

GBGI_ modifiedBooleans stores a 16-bit record corresponding to the modified state of a GenBooleanGroup's children. Any GenBooleans that have been modified since the occurrence of the last apply action will be marked modified. In general, you should not need to set any GenBooleans initially modified.

Whenever the GenBooleanGroup receives notice to "apply" any changes, it will check whether any Booleans have been modified. If any have, the GenBooleanGroup will send its apply action to its destination.

In immediate mode, any change to the GenBoolean's state automatically sets the corresponding bit in the BooleanGroup's GBGI_ modifiedBooleans instance data and also sends out MSG_GEN_APPLY . Therefore, in immediate mode, any user changes will cause the apply action to be sent out immediately. In delayed mode, the corresponding bits in the GBGI_ modifiedBooleans are set, but MSG_GEN_APPLY will be sent out at a later time. The apply action will only occur when the user forces it (such as clicking on an "apply" trigger) or when the application forces it (with MSG_GEN_APPLY ).

Destination

GBGI_applyMsg, GBGI_destination, GEN_BOOLEAN_GROUP_APPLY_MSG, ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED

When the GenBooleanGroup receives notice to apply any changes made in the list, it will first determine whether the group has been modified (by checking GBGI_ modifiedBooleans ). If any GenBoolean has been modified, the BooleanGroup will send the apply message stored in GBGI_ applyMsg to the destination stored in GBGI_ destination .

GBGI_ applyMsg should contain the message you wish the GenBooleanGroup to send out whenever changes should be applied. This message should be based on the prototype GEN_BOOLEAN_GROUP_APPLY_MSG so the proper arguments are passed to its handler. GBGI_ destination should contain the object or process that will handle the message sent out by this group.

You can force an apply to occur by marking GenBooleans modified with MSG_GEN_BOOLEAN_GROUP_SET_MODIFIED_STATE and sending MSG_GEN_APPLY to the GenBooleanGroup. You can also override the default behavior, forcing the object to send out its apply action on all applies, with ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED in the object's instance data.

GEN_BOOLEAN_GROUP_APPLY_MSG

void	GEN_BOOLEAN_GROUP_APPLY_MSG(
        word	selectedBooleans,
        word	indeterminateBooleans,
        word	modifiedBooleans);

Use this prototype to define the apply message for your GenBooleanGroup (stored in GBGI_ applyMsg ). This prototype ensures that the message passes the correct parameters to your message handler.

Source: Your GenBooleanGroup object.

Destination: The destination of your GenBooleanGroup.

Parameters: selectedBooleans The currently selected GenBooleans (the contents of the GBGI_ selectedBooleans instance field).

indeterminateBooleans
The current indeterminate GenBooleans (the contents of the GBDI_ indeterminateBooleans instance field).
modifiedBooleans
The current modified GenBooleans (the contents of the GBGI_ modifiedBooleans instance field).

Return: Nothing.

Sending the Status Messages

GEN_BOOLEAN_GROUP_STATUS_MSG, ATTR_GEN_BOOLEAN_GROUP_STATUS_MSG, MSG_GEN_BOOLEAN_GROUP_SEND_STATUS_MSG

In delayed mode, GenBooleanGroups will only send out their apply action if an apply occurs. If you wish your application to receive notice whenever any user changes take place in the list's state, even if those changes should not be "applied," you may set up a status message in the object's instance data.

Set a message based on the prototype GEN_BOOLEAN_GROUP_STATUS_MSG. Set ATTR_GEN_BOOLEAN_GROUP_STATUS_MSG to this message in your object declaration, and provide a handler for this message in the class.

GEN_BOOLEAN_GROUP_STATUS_MSG

void	GEN_BOOLEAN_GROUP_STATUS_MSG(
        word	selectedBooleans,
        word	indeterminateBooleans,
        word	changedBooleans);

Use this prototype to define a status message for your GenBooleanGroup (stored in the ATTR_GEN_BOOLEAN_GROUP_STATUS_MSG vardata). This prototype ensures that the message passes the correct parameters to your message handler.

Source: Your GenBooleanGroup object.

Destination: The destination of your GenBooleanGroup object.

Parameters: selectedBooleans The currently selected GenBooleans (the contents of the GBGI_ selectedBooleans instance field).

indeterminateBooleans
The current indeterminate GenBooleans (the contents of the GBDI_ indeterminateBooleans instance field).
changedBooleans
The GenBooleans that caused the status message to be sent out (not necessarily the contents of the GBGI_ modifiedBooleans instance field).

Return: Nothing.

MSG_GEN_BOOLEAN_GROUP_SEND_STATUS_MSG

void	MSG_GEN_BOOLEAN_GROUP_SEND_STATUS_MSG(
        word	changedBooleans);

This message causes the GenBooleanGroup to send out its status message stored in ATTR_GEN_ITEM_GROUP_STATUS_MSG . The message should include the current state of the GenBooleanGroup. (Base the status message on the prototype GEN_BOOLEAN_GROUP_STATUS_MSG to ensure this.) To mark individual GenBooleans as changed since the last apply action, pass this message a record of the changed GenBooleans.

Source: Unrestricted.

Destination: Any GenBooleanGroup object with a status message.

Parameters: changedBooleans A record representing the GenBooleans that have changed to cause this GenBooleanGroup to send out its status message.

Return: Nothing.

Interception: Generally not intercepted.

BooleanGroup Links

ATTR_GEN_BOOLEAN_GROUP_LINK

Just as you may create links between GenItemGroups, you may also create links between BooleanGroups. For more complete information on the purpose and effects of linked groups, see GenItemGroup Links .

To enable linked BooleanGroups, you may set up small sub-sets of GenBooleans as separate GenBooleanGroups. Within each BooleanGroup, include a link to the next BooleanGroup with ATTR_GEN_BOOLEAN_GROUP_LINK . Links should be circular; i.e., if three BooleanGroups should be linked, the first group should point to the second, the second to the third, and the third to the first. The three linked BooleanGroups will act as one entity.

Scrolling Boolean Groups

HINT_BOOLEAN_GROUP_SCROLLABLE, MSG_GEN_BOOLEAN_GROUP_MAKE_BOOLEAN_VISIBLE

Boolean group lists, like item group lists, can be scrollable. To make a Boolean list scrollable, set HINT_BOOLEAN_GROUP_SCROLLABLE . To make a particular item in a scrollable Boolean list visible, use the message MSG_GEN_BOOLEAN_GROUP_MAKE_BOOLEAN_VISIBLE .

MSG_GEN_BOOLEAN_GROUP_MAKE_BOOLEAN_VISIBLE

void	MSG_GEN_BOOLEAN_GROUP_MAKE_BOOLEAN_VISIBLE(
        word	identifier);

This message, when sent to a scrollable BooleanGroup object, will make the specified GenBoolean visible in the list. If the passed identifier is unknown or is not usable, the BooleanGroup will do nothing.

Source: Unrestricted.

Destination: Any scrollable, usable GenBooleanGroup.

Parameters: identifier The item number of the GenBoolean to be made visible.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 GenBooleanGroup Instance Data | Next: 5.3 Altering Instance Data


Altering Instance Data

The List Objects: 5.3 GenBooleanGroups: Altering Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 GenBooleanGroup Usage

Both the BooleanGroups and the Booleans themselves may be dynamically changed to alter the behavior of the list objects.

Altering the Identifiers

MSG_GEN_BOOLEAN_GET_IDENTIFIER, MSG_GEN_BOOLEAN_SET_IDENTIFIER

Each GenBoolean object contains an instance field to store the identifier of that object. It is this identifier that the parent GenBooleanGroup object uses when referring to individual GenBoolean items. You may change these identifiers dynamically if you wish.

To get a Boolean's identifier, send it MSG_GEN_BOOLEAN_GET_IDENTIFIER . To change a GenBoolean's identifier, send the object a MSG_GEN_BOOLEAN_SET_IDENTIFIER , passing the new identifier. You can send the GenBoolean object this message at any time, though sending it while the item is still GS_USABLE would be unusual.

MSG_GEN_BOOLEAN_GET_IDENTIFIER

word	MSG_GEN_BOOLEAN_GET_IDENTIFIER();

This message returns the identifier for the GenBoolean item.

Source: Unrestricted.

Destination: Any GenBoolean object.

Parameters: None.

Return: Identifier stored in the object's GBI_identifier instance field.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_SET_IDENTIFIER

void	MSG_GEN_BOOLEAN_SET_IDENTIFIER(
        word	identifier);

This message sets a new identifier for a GenBoolean object. No change is made to the GenBooleanGroup object. You must make sure that you are not referencing the GenBoolean with its old identifier before sending this message.

Source: Unrestricted.

Destination: Any GenBoolean object.

Parameters: identifier A word representing the constant of the new identifier. This should be a mask representing a bit within a 16-bit record.

Return: Nothing.

Interception: Generally not intercepted.

Warnings: The GenBooleanGroup must not be referring to the object when the GenBoolean receives this message. You should also not refer to the GenBoolean by its old identifier from then on.

Altering the Group State

You may alter the state of a GenBooleanGroup's children in two ways: First, you can set the entire group's state by setting the 16-bit records in the GenBooleanGroup's instance data. Second, you may set individual bits representing an item's state individually.

Setting the State Collectively

MSG_GEN_BOOLEAN_GROUP_GET_SELECTED_BOOLEANS, MSG_GEN_BOOLEAN_GROUP_SET_GROUP_STATE, MSG_GEN_BOOLEAN_GROUP_GET_INDETERMINATE_BOOLEANS, MSG_GEN_BOOLEAN_GROUP_SET_GROUP_INDETERMINATE_STATE, MSG_GEN_BOOLEAN_GROUP_GET_MODIFIED_BOOLEANS, MSG_GEN_BOOLEAN_GROUP_SET_GROUP_MODIFIED_STATE

To retrieve the contents of the GBGI_ selectedBooleans instance field, send the BooleanGroup a MSG_GEN_BOOLEAN_GROUP_GET_SELECTED_BOOLEANS . This message returns a 16-bit record. You can set the selection state of a BooleanGroup's children by sending the group a MSG_GEN_BOOLEAN_GROUP_SET_GROUP_STATE .

To retrieve the contents of the GBGI_ indeterminateBooleans instance field, use MSG_GEN_BOOLEAN_GROUP_GET_INDETERMINATE_BOOLEANS . This message returns a 16-bit record. You can set the indeterminate state with MSG_GEN_BOOLEAN_GROUP_SET_GROUP_INDETERMINATE_STATE .

To retrieve the contents of the GBGI_ modifiedBooleans instance field, use MSG_GEN_BOOLEAN_GROUP_GET_MODIFIED_BOOLEANS . This message returns a16-bit record. You can set the selection state of a BooleanGroup's children by sending the group a MSG_GEN_BOOLEAN_GROUP_SET_GROUP_MODIFIED_STATE .

MSG_GEN_BOOLEAN_GROUP_GET_SELECTED_BOOLEANS

word	MSG_GEN_BOOLEAN_GROUP_GET_SELECTED_BOOLEANS();

This message returns the current state of all GenBooleans in a GenBooleanGroup (selected or unselected) by returning the contents of the GBGI_ selectedBooleans instance field.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: None.

Return: A record indicating the GenBooleans selected.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_GROUP_STATE

void	MSG_GEN_BOOLEAN_GROUP_SET_GROUP_STATE(
        word	selectedBooleans,
        word	indeterminateBooleans);

This message sets new selections and indeterminate states for a GenBooleanGroup (altering the contents of the GBGI_ selectedBooleans and GBGI_ indeterminateBooleans instance field). You should pass the records indicating the GenBooleans to select and/or mark indeterminate. Any GenBooleans which are not set will be cleared.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: selectedBooleans The GenBooleans which should be selected.

indeterminateBooleans
The GenBooleans which should be marked indeterminate.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_GET_INDETERMINATE_BOOLEANS

word	MSG_GEN_BOOLEAN_GROUP_GET_INDETERMINATE_BOOLEANS();

This message returns the indeterminate items of a GenBooleanGroup by returning the contents of the GBDI_ indeterminateBooleans instance field.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: None.

Return: A record indicating the current indeterminate booleans, represented by the OR-ed sum of their identifiers.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_GET_MODIFIED_BOOLEANS

word	MSG_GEN_BOOLEAN_GROUP_GET_MODIFIED_BOOLEANS();

This message returns selections marked modified within a GenBooleanGroup since the last apply (by returning the contents of the GBGI_ modifiedBooleans instance field).

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: None.

Return: The current modified Booleans.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_GROUP_MODIFIED_STATE

void	MSG_GEN_BOOLEAN_GROUP_SET_GROUP_MODIFIED_STATE(
        word	setBooleans,
        word	clearBooleans);

GenBooleans are normally marked modified any time their state is altered, and they are marked not modified after these changes have been applied. You may also mark GenBooleans modified (or mark them nor modified) with this message. You should pass this message both the GenBooleans to set and the GenBooleans to clear (in the GBGI _modifiedBooleans instance field).

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: setBooleans A record indicating the GenBooleans to be set modified in GBGI_ modifiedBooleans .

clearBooleans
A record (mask) indicating the GenBooleans to be set not modified (cleared) in GBGI_ modifiedBooleans .

Return: Nothing.

Interception: Generally not intercepted.

Setting the Booleans Individually

MSG_GEN_BOOLEAN_GROUP_GET_BOOLEAN_OPTR, MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_SELECTED, MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_STATE, MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_INDETERMINATE, MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_INDETERMINATE_STATE, MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_MODIFIED, MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_MODIFIED_STATE

You can retrieve the optr of a particular GenBoolean object by sending the BooleanGroup MSG_GEN_BOOLEAN_GROUP_GET_ITEM_OPTR , passing it the identifier of the boolean in question. You can then use that optr to directly communicate with the object for purposes such as enabling/disabling, setting not usable, etc.

To check on whether a GenBoolean is currently selected, send the BooleanGroup MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_SELECTED , passing it the identifier to check. To set the selection state of a Boolean, send the BooleanGroup MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_STATE , passing it the identifier and the selection state (on or off).

To check on the indeterminate state of a GenBoolean, send the BooleanGroup MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_INDETERMINATE , passing the identifier to check. To set the indeterminate state of a Boolean, send MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_INDETERMINATE_STATE , passing it the identifier and the indeterminate state to set the Boolean to.

To check on the modified state of a GenBoolean, send the BooleanGroup MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_MODIFIED , passing the identifier to check. To set the modified state of a Boolean, send MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_MODIFIED_STATE , passing it the identifier and the modified state.

MSG_GEN_BOOLEAN_GROUP_GET_BOOLEAN_OPTR

optr	MSG_GEN_BOOLEAN_GROUP_GET_BOOLEAN_OPTR(
        word	identifier);

This message returns the optr of the requested GenBoolean. You may then use this optr to alter that Boolean's state individually. If the GenBoolean with the matching identifier is not found, a null optr will be returned.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: identifier The identifier of the GenBoolean to look for.

Return: The optr of the requested GenBoolean object (or a null optr if none is found).

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_SELECTED

Boolean	MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_SELECTED(
        word	identifier);

This message checks whether the GenBoolean with the passed identifier is selected.

Source: Unrestricted.

Destination: Any GenBooleanGroup object

Parameters: identifier The identifier of the GenBoolean to check.

Return: Will return true if GenBoolean is selected, false if GenBoolean is unselected.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_STATE

void	MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_STATE(
        word		identifier,
        Boolean		state);

This message sets an individual GenBoolean's selection state, leaving other GenBooleans unaffected. You may alter the selection state of individual GenBooleans even if they are not usable, not enabled, or not even within the BooleanGroup. (This is useful for linked BooleanGroups.)

Source: Unrestricted.

Destination: Any GenBooleanGroup.

Parameters: identifier The identifier of the GenBoolean.

state
TRUE to mark selected, FALSE to mark unselected.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_INDETERMINATE

Boolean	MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_INDETERMINATE(
        word	identifier);

This message checks whether the GenBoolean with the passed identifier is indeterminate (by examining the GBDI_ indeterminateBooleans instance field).

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: identifier The identifier of the GenBoolean to check.

Return: Will return true if the GenBoolean is indeterminate, false if is not.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_INDETERMINATE_STATE

void	MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_INDETERMINATE_STATE(
        word		identifier,
        Boolean		indeterminateState);

This message sets the indeterminate state of an individual GenBoolean without affecting any other GenBooleans. You may mark GenBooleans indeterminate even if they are not usable, not enabled, or not even present within the BooleanGroup.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: identifier The identifier of the GenBoolean.

indeterminateState
Pass TRUE to mark the GenBoolean indeterminate, FALSE to mark it not indeterminate.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_MODIFIED

Boolean	MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_MODIFIED(
        word	identifier);

This message checks whether the GenBoolean with the passed identifier has been modified (by examining the GBGI_ modifiedBooleans instance field).

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: identifier The identifier of the GenBoolean to check.

Return: Will return true if boolean has been modified, false if it has not.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_MODIFIED_STATE

void	MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_MODIFIED_STATE(
        word		identifier,
        Boolean		modifiedState);

This message sets the modified state of an individual GenBoolean without affecting any other GenBooleans. You may mark GenBooleans modified even if they are not usable, not enabled, or not even present within the BooleanGroup.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: identifier The identifier of the GenBoolean.

modifiedState
TRUE to mark the GenBoolean modified, FALSE to mark it not modified.

Return: Nothing.

Interception: Generally not intercepted.

Altering the Destination

MSG_GEN_BOOLEAN_GROUP_GET_DESTINATION, MSG_GEN_BOOLEAN_GROUP_SET_DESTINATION, MSG_GEN_BOOLEAN_GROUP_GET_APPLY_MSG, MSG_GEN_BOOLEAN_GROUP_SET_APPLY_MSG

To retrieve the GenBooleanGroup's destination stored in its GBGI_ destination instance field or the message stored in its GBGI_ applyMsg instance field, use MSG_GEN_BOOLEAN_GROUP_GET_DESTINATION or MSG_GEN_BOOLEAN_GROUP_GET_APPLY_MSG .

To set the GenBooleanGroup's destination to an object or process, send the list a MSG_GEN_BOOLEAN_GROUP_SET_DESTINATION , passing it the optr of the new destination. This object will thereafter handle all apply actions sent by the GenBooleanGroup.

To set the GenBooleanGroup's apply action to a new message, send the list a MSG_GEN_BOOLEAN_GROUP_SET_APPLY_MSG , passing it the message to use.

MSG_GEN_BOOLEAN_GROUP_GET_DESTINATION

optr	MSG_GEN_BOOLEAN_GROUP_GET_DESTINATION();

This message returns the current destination object for the BooleanGroup, as specified in the GBGI_ destination instance field.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: None.

Return: The optr of the current destination object.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_DESTINATION

void	MSG_GEN_BOOLEAN_GROUP_SET_DESTINATION(
        optr	dest);

This message sets a new destination object for a BooleanGroup. The apply and status messages of this BooleanGroup will be sent to this new destination.

Source: Unrestricted.

Destination: Any GenBooleanGroup.

Parameters: dest The optr of the new destination object; this will be put into GBGI_ destination .

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_GET_APPLY_MSG

Message	MSG_GEN_BOOLEAN_GROUP_GET_APPLY_MSG();

This message returns the current apply action in the GenBooleanGroup's GBGI_ applyMsg instance field.

Source: Unrestricted.

Destination: Any GenBooleanGroup.

Parameters: None.

Return: The current apply message.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_APPLY_MSG

void	MSG_GEN_BOOLEAN_GROUP_SET_APPLY_MSG(
        Message		message);

This message sets a new apply message in the GenBooleanGroup's GBGI_ applyMsg instance field.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: message The new apply message.

Return: Nothing.

Return: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 GenBooleanGroup Usage


Pen Object Library

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 The Ink Object
| One-page Version of this Chapter for Easy Printing

The Pen Library provides routines and object classes which work together to form the backbone of a note-book style database storing pen input.

An Ink object is a visual object and may be used by any application that wants to work with pen input. Though any targetable object may accept ink (see the Input chapter), InkClass has many optimizations for working with ink.

The Ink Database routines provide a front end to the standard GEOS Database (DB) library routines well suited for storing and organizing several small pieces of information. These routines allow the storage of notes within a hierarchical arrangement of folders. Each note may contain one or more pages of textual or ink information.

  1 The Ink Object
    1.1 Instance Data and Messages
    1.2 Storing Ink to DB Items
  2 Working with the Ink DB
    2.1 Getting Started
    2.2 Displaying the Data
    2.3 Titles and Keywords
    2.4 Navigating the Folder Tree
    2.5 Managing Notes and Folders
    2.6 Manipulating Notes
    2.7 Searching and Traversing the Tree
  3 InkControlClass

|  Up: GEOS SDK TechDocs | Up | Down | Next: 1 The Ink Object
| One-page Version of this Chapter for Easy Printing


The Ink Object

Pen Object Library: 1 The Ink Object

| Up: GEOS SDK TechDocs | Up | Down | Prev: Pen Object Library | Next: 2 Working with the Ink DB

InkClass provides methods for storing multiple pen inputs in a compact form. It automatically handles all queries about pen input. It handles display, with the power to display the ink in any color, and it allows the use of standard or custom background pictures.

To change the way the Ink class (or any other appropriate class) handles ink, the messages to subclass are MSG_META_NOTIFY_WITH_DATA_BLOCK (with the notification type MANUFACTURER_ID_GEOWORKS, NT_INK), MSG_META_QUERY_IF_PRESS_IS_INK , and MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK .

Instance Data and Messages

Storing Ink to DB Items

| Up: GEOS SDK TechDocs | Up | Down | Prev: Pen Object Library | Next: 2 Working with the Ink DB


Instance Data and Messages

Pen Object Library: 1.1 The Ink Object: Instance Data and Messages

| Up: GEOS SDK TechDocs | Up | Prev: 1 The Ink Object | Next: 1.2 Storing Ink to DB Items

When setting up an Ink object, probably the only pieces of instance data the application will be concerned with will be II_flags , II_dirtyOutput , and II_dirtyMsg .

Most of the flags are easy to understand, with the possible exceptions of the IF_HAS_TARGET and IF_DIRTY fields, which should not be set when creating the object in any case.

Code Display 21-1 InkClass Instance Data

/* 	II_flags:
 * 	  This field holds flags governing the object's behavior:
 *		IF_MOUSE_FLAGS,
 * 		IF_SELECTING,
 * 		IF_HAS_TARGET,
 *		IF_HAS_SYS_TARGET,
 *		IF_DIRTY,
 *		IF_ONLY_CHILD_OF_CONTENT,
 *		IF_CONTROLLED, (Set if to be used with an InkControl)
 *		IF_INVALIDATE_ERASURES,
 * 		IF_HAS_UNDO */
@instance 	InkFlags 			II_flags = IF_HAS_UNDO;
/*   	II_tool:
 * 	This field keeps track of how the user is interacting with the Ink. There
 * 	are three possible tools: IT_PENCIL, IT_SELECTOR, and IT_ERASER. */
@instance 	InkTool 		II_tool;
 /* 	II_penColor:
 * 	The color to use when drawing ink. */
@instance 	Color 		II_penColor = C_BLACK;
/* 	II_segments:
 * 	Do not set this field explicitly. This field is a handle to the chunk array
 * 	containing the pen segments. The segments are stored as an array of Point
 * 	structures. The stored coordinates are all positive; any x coordinate with
 * 	its sign bit set belongs to the last point in a gesture. Thus, a small
 * 	cross shape centered at (72, 72) might be stored:
 * 		(0x0048, 0x0046)
 * 		(0x8048, 0x004A) [note sign bit set in x coordinate]
 * 		(0x0046, 0x0048)
 * 		(0x804A, 0x0048)		 [sign bit set in x coordinate]	*/
@instance 	ChunkHandle 		II_segments;
/* 	II_dirtyMsg, II_dirtyOutput:
 * 	Together, these fields form an Action Descriptor. When the Ink processes
 * 	a point of pen information, erases anything, or handles an undo event the
 * 	IF_DIRTY flag will be set. If the flag was not set already, then the Ink
 *	will send the AD's message to the AD's object. The handler for this message
 * 	should probably clear the IF_DIRTY bit. */
@instance optr			II_dirtyOutput;
@instance Message	 		II_dirtyMsg;
@instance Rectangle 			II_selectBounds;			/* Internal */
@instance GStateHandle			II_cachedGState;			/* Internal */
@instance TimerHandle 			II_antTimer;			/* Internal */
@instance word 			II_antTimerID;			/* Internal */
@instance byte			II_antMask;			/* Internal */
/*	ATTR_INK_STROKE_SIZE:
 * 	Size of line used to draw ink. */
@vardata    InkStrokeSize   ATTR_INK_STROKE_SIZE;
typedef struct {
    byte    ISS_width;
    byte    ISS_height;
} InkStrokeSize;
/* 	ATTR_INK_MAX_POINTS:
 * 	Maximum number of points of ink data the object is prepared to store. */
@vardata    word ATTR_INK_MAX_POINTS;

Most of the Ink messages just change or retrieve the values of the instance fields. The exceptions are two messages which help those applications which need to save or transfer the Ink object's pen data. Use MSG_INK_SAVE_TO_DB_ITEM to save the pen data to an arbitrary DB item. If the application changes this information and wishes to pass it back to the ink object, use MSG_INK_LOAD_FROM_DB_ITEM .

MSG_INK_SET_TOOL

void 	MSG_INK_SET_TOOL(
        InkTool 	tool);

This message allows the Ink to switch between pencil and eraser tools, changing the II_tool field.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: tool A tool, either IT_PENCIL or IT_ERASER.

Return: Nothing.

Interception: Unlikely.

MSG_INK_GET_TOOL

InkTool 	MSG_INK_GET_TOOL();

This message returns the Ink's present tool, as stored in II_tool .

Source: Unrestricted.

Destination: Any Ink object.

Parameters: None.

Return: The present tool, either IT_PENCIL or IT_ERASER.

Interception: Unlikely.

MSG_INK_SET_PEN_COLOR

void 	MSG_INK_SET_PEN_COLOR(
        Color 	clr);

This message changes the color used to draw the ink, changing the value in II_penColor .

Source: Unrestricted.

Destination: Any Ink object.

Parameters: clr Index to a palette (e.g. C_RED).

Return: Nothing.

Interception: Unlikely.

MSG_INK_SET_DIRTY_AD

void 	MSG_INK_SET_DIRTY_AD(
        word	 method,
        optr	 object);

This message sets the Action Descriptor to be activated when the user dirties the object, changing the values in II_dirtyMsg and II_dirtyOutput .

Source: Unrestricted.

Destination: Any Ink object.

Parameters: method The message to send when the object is dirty.

object
The object which should receive the above message.

Return: Nothing.

Interception: Unlikely.

MSG_INK_SET_MAX_POINTS

void MSG_INK_SET_MAX_POINTS(
        word 	maxPoints);

Set the maximum number of points of ink data the object is prepared to store.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: maxPoints The message to send when the object is dirty.

Return: Nothing.

Interception: Unlikely.

MSG_INK_SET_FLAGS

void 	MSG_INK_SET_FLAGS(
        InkFlags 	setFlags,
        InkFlags 	clearFlags);

This message changes the value of the II_flags field. Note that something which sets the IF_DIRTY bit should probably also perform the action stored in the II_dirtyMsg and II_dirtyOutput fields.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: setFlags The flags to turn on.

clearFlags
The flags to turn off.

Return: Nothing.

Interception: Unlikely.

MSG_INK_GET_FLAGS

InkFlags 	MSG_INK_GET_FLAGS();

This message gets the value of the II_flags field.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: None.

Return: The present value of the II_flags field.

Interception: Unlikely.

MSG_INK_DELETE_LAST_GESTURE

int MSG_INK_DELETE_LAST_GESTURE();

This message removes the last gesture stored by the ink object; if the object had no ink stored, then nothing happens. The message returns zero if nothing was deleted.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: None.

Return: Zero if nothing deleted.

Interception: Unlikely.

MSG_INK_SET_STROKE_SIZE

void MSG_INK_SET_STROKE_SIZE(
        byte 	width,
        byte 	height);

Set the brush size to use when drawing ink.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: width Brush width.

height
Brush height.

Return: Nothing.

Interception: Unlikely.

MSG_INK_SAVE_TO_DB_ITEM

void	MSG_INK_SAVE_TO_DB_ITEM(
        DBReturn		* RetValue,
        InkDBFrame		* ptr);

This message saves the Ink's pen data into the passed DB item. The pen data will be stored compressed. Calling this message sets the object not dirty.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: ptr A pointer to an InkDBFrame structure, shown below.

RetValue
A pointer to an empty DBReturn structure, to be filled by the handler.

Return: The structure pointed to by RetValue will contain the returned information.

Structures: The InkDBFrame and DBReturn structures are defined below:

  typedef struct {
    Rectangle			IDBF_bounds;
        /* The bounds of the Ink data */
    VMFileHandle			IDBF_VMFile;
        /* VM file to write to*/
    DBGroupAndItem 			IDBF_DBGroupAndItem;
        /* DB Item to save to
         * (or NULL to create a new one) */
    word 			IDBF_DBExtra;
        /* Extra space to skip at start
         * of block */
} InkDBFrame;
  typedef struct {
    word		DBR_group;
    word		DBR_item;
    word		DBR_unused1;
    word		DBR_unused2;
} DBReturn;

Interception: Unlikely.

MSG_INK_LOAD_FROM_DB_ITEM

void	MSG_INK_LOAD_FROM_DB_ITEM(
        InkDBFrame 	*ptr);

This message loads the compressed data into the Ink from the passed DB item. If a NULL handle is passed, then the Ink is cleared. This message marks the Ink as clean.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: ptr A pointer to an InkDBFrame structure.

Return: Nothing.

Structures: For the InkDBFrame structure, see MSG_INK_SAVE_TO_DB_ITEM .

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 1 The Ink Object | Next: 1.2 Storing Ink to DB Items


Storing Ink to DB Items

Pen Object Library: 1.2 The Ink Object: Storing Ink to DB Items

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Instance Data and Messages | Next: 2 Working with the Ink DB

Pen information comes in as a MSG_META_NOTIFY_WITH_DATA_BLOCK of type NT_INK accompanied by an array containing the coordinates visited by the pen. The pen data keeps track of the coordinates of the pen input. Every time pen input comes in, the ink object notes the coordinates. The ink object is optimized to save space. For instance, the Ink object eliminates collinear points: if three pen events are collinear, it will not record the middle one, recognizing it as redundant.

The non-redundant points are written out to the II_segments field, a chunk array of Point structures. Note that the coordinates are unsigned. If a point's x coordinate's sign bit is set, that does not mean that the x coordinate is negative; if this sign bit is set this is a signal that this point is the last point of a gesture.

When writing pen data to a DB item, the Ink object does some more compression. Applications which work with the items used by MSG_INK_SAVE_TO_DB_ITEM and MSG_INK_LOAD_FROM_DB_ITEM must work with this compression. Since the user is dragging the pen around in a continuous gesture, the pen events tend to occur close together. Thus, it is nice to have a way to record a coordinate as a small offset from another coordinate. Since many strokes are almost horizontal or vertical, quite often the horizontal or vertical offset will be zero or one.

To take advantage of these tendencies, the ink object stores pen input as a bitstream. Coordinates may be recorded either as absolute positions or as offsets from the last coordinate.

When writing out a gesture to a DB item, the first point will always be recorded as an absolute position. Thus, first the x coordinate will be recorded, then the y coordinate. Each coordinate will be marked as absolute by the 1011 bit pattern.

For each subsequent pen point, the algorithm will first make sure that the new point is not collinear with the previous two. If it is, then the algorithm will make the incoming pen event overwrite the previous event's coordinates.

For each event, the algorithm will first write out the x coordinate, then the y coordinate.

  • If the coordinate is at 0 or 1 offset from the previous coordinate, the algorithm will write out the appropriate two-bit code (00, 01, or 11).
  • If the coordinate is at an offset from the previous coordinate between 2 and 8, then the algorithm will write out the appropriate 7 bit code (1000xxx for a positive offset, 1001xxx for a negative offset).
  • If the coordinate is more than 8 points from the previous coordinate, the algorithm writes out 1011 followed by the absolute coordinate, represented as a 15 bit unsigned quantity.

When the input is finished, the algorithm writes a 1000000 bit pattern, signalling the end of the segment.

Decompressing the data is a matter of traversing the bitstream and detecting the appropriate patterns.

As an example of how the algorithm compresses pen input, suppose the Ink object were writing the following gesture to a DB item:

(72, 71)
(82, 74)
(84, 74)
(85, 72)

The first coordinate is 72, so the algorithm will write out:
1011 (signals absolute coordinate) 000000001001000
 The second coordinate is 71, so after handling the second coordinate, the stream will be:
1011 000000001001000 1011 000000001000111
 The x coordinate of the second point is 82, which is 10 points away from the previous x coordinate. Unfortunately, this is too far to express as a short offset, so the algorithm writes another absolute coordinate (the new part of the stream is shown in italics):
1011 000000001001000 1011 000000001000111 1011 000000001010010
The y coordinate of the second point is 74, at a positive 3 offset from the previous y value, so the algorithm will write out the appropriate offset code instead of an absolute position code:
...1011 000000001000111 1011 000000001010010 10 00 010
The third point's x coordinate is 84, at a +2 offset from 82. The y coordinate is 74, the same as the previous point's y coordinate:
...1011 000000001000111 1011 000000001010010 10 00 010 10 00 001 00
The last point's x coordinate is one higher than the previous; its y coordinate is two less.
...1011 000000001010010 10 00 010 10 00 001 00 01 10 01 001
Since it has reached the end of the pen input (this was a suspiciously short gesture, a somewhat contrived example), the algorithm then writes an end-of-segment code:
...1011 000000001010010 10 00 010 10 00 001 00 01 10 01 001 10 00 000
If the Ink object were holding more than one gesture of information, it would write the next gesture's elements starting after the end-of-segment code of the first.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Instance Data and Messages | Next: 2 Working with the Ink DB


Working with the Ink DB

Pen Object Library: 2 Working with the Ink DB

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 The Ink Object | Next: 3 InkControlClass

The Ink Database provides a simplified, specialized API to the database library. It allows the user to organize pieces of information on notes stored in a hierarchy of folders. Each note may have one or more pages, with each page corresponding to the contents of an ink or text object. The data stored in each page is a DB item returned by MSG_INK_SAVE_TO_DB_ITEM (for Ink objects) or MSG_VIS_TEXT_GET_ALL_DB_ITEM (for Text objects).

Notes and folders are specified by means of a dword identifier. This identifier has nothing to do with where the note's (or folder's) data is stored, or where it appears in the folder tree. Applications should use the InkLoadPage() and InkSavePage() routines to work with a note's data, and use the routines described below to determine where a note or folder appears in the folder tree.

Getting Started

Displaying the Data

Titles and Keywords

Navigating the Folder Tree

Managing Notes and Folders

Manipulating Notes

Searching and Traversing the Tree

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 The Ink Object | Next: 3 InkControlClass


Getting Started

Pen Object Library: 2.1 Working with the Ink DB: Getting Started

| Up: GEOS SDK TechDocs | Up | Prev: 2 Working with the Ink DB | Next: 2.2 Displaying the Data
InkDBInit()

To create an Ink Database, an application needs a file handle, perhaps the file holding a GenDocument's data. Before calling any other Ink Database functions, call InkDBInit() to set up the file correctly; this routine should be called exactly once per Ink DB. If the database is part of a GenDocument, then this routine should be called within the MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE . Other routines (described below) which might be appropriate when first setting up an Ink Database include InkSetDocPageInfo() and InkSetDocGString() .

| Up: GEOS SDK TechDocs | Up | Prev: 2 Working with the Ink DB | Next: 2.2 Displaying the Data


Displaying the Data

Pen Object Library: 2.2 Working with the Ink DB: Displaying the Data

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Getting Started | Next: 2.3 Titles and Keywords
InkNoteLoadPage(), InkNoteSavePage(), InkGetDocPageInfo(), InkSetDocPageInfo(), InkSetDocGString(), InkGetDocGString(), InkNoteGetNoteType()

Assuming that the application is using text and ink objects to display the information held in the Ink DB, use InkNoteLoadPage() and InkNoteSavePage() to transfer information between the Ink object and the Ink DB. InkNoteLoadPage() loads an ink or text object with the data stored within the passed note. Use InkNoteGetNoteType() to determine what sort of data is stored within the note. Once the user has made changes, those changes should be stored to the database. Call InkNoteSavePage() to write the changes.

To find out the document size associated with an Ink Database, call InkGetDocPageInfo() . To change the page size, call InkSetDocPageInfo() .

The Ink DB routines support the notion of a background picture for ink information. There is one background picture for the entire database. To set the background picture, use InkSetDocGString() . To find out the current background picture, call InkGetDocGString() .

The background GString is stored in VM; call GrLoadGString() and GrDrawGString() to draw it.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Getting Started | Next: 2.3 Titles and Keywords


Titles and Keywords

Pen Object Library: 2.3 Working with the Ink DB: Titles and Keywords

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Displaying the Data | Next: 2.4 Navigating the Folder Tree
InkNoteSetKeywords(), InkNoteSetKeywordsFromTextObject(), InkNoteGetKeywords(), InkNoteSendKeywordsToTextObject(), InkGetTitle(), InkSendTitleToTextObject(), InkFolderSetTitle(), InkFolderSetTitleFromTextObject(), InkNoteSetTitle(), InkNoteSetTitleFromTextObject()

Each note may have two text strings which are helpful for identification: a title and a set of keywords. These words may be used as the fields for a computed search if the application supports these; regardless, the user will certainly find these fields useful for organizing notes.

To set a note's title, call InkNoteSetTitle() . There is a corresponding InkFolderSetTitle() for setting the title of a folder. Since applications may wish to set the titles of these items based upon the user's entry in a text object, there are two routines InkNoteSetTitleFromTextObject() and InkFolderSetTitleFromTextObject() which take an item's name from a text object. InkGetTitle() gets any item's title, and InkSendTitleToTextObject() is a specialized function used to update the passed text object's text to hold the item's title. The maximum length of any title should be INK_DB_MAX_TITLE_SIZE.

Notes may have keywords: words which should not appear in the title but which are still useful for searches. Folders do not have keywords. To set a note's keywords, use InkNoteSetKeywords() ; to use the contents of a text object as the keywords, use InkNoteSetKeywordsFromTextObject() . To retrieve the keywords, call InkNoteGetKeywords() . InkNoteSendKeywordsToTextObject() replaces a text object's text with the passed note's keywords. The maximum length of any keyword should be INK_DB_MAX_NOTE_KEYWORDS_SIZE .

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Displaying the Data | Next: 2.4 Navigating the Folder Tree


Navigating the Folder Tree

Pen Object Library: 2.4 Working with the Ink DB: Navigating the Folder Tree

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Titles and Keywords | Next: 2.5 Managing Notes and Folders
InkDBGetDisplayInfo(), InkDBSetDisplayInfo(), InkDBGetHeadFolder(), InkGetParentFolder(), InkFolderGetContents(), InkFolderGetNumChildren(), InkFolderDisplayChildInList(), InkFolderGetChildInfo(), InkFolderGetChildNumber(), InkNoteGetNumPages()

Assuming the application allows the existence of more than one folder, it must allow some way to move around within the folder tree. If the application allows the user to change the structure of the folder tree, then it will need UI which allows the user to navigate an arbitrary tree. There are routines to find out and change which page is being displayed. For those applications which will need to get information about the folder tree, there are routines to get information about the folder tree.

To find the application's current location within the DB, call InkDBGetDisplayInfo() . This routine returns the current folder ID, the note ID if any is selected, and the page number within the note. To go to a different location, call InkDBSetDisplayInfo() . To use this routine, the application must pass a folder ID, along with a valid note ID and page number if a note is to be selected.

Chances are the user will be maneuvering within the folder tree. To get the ID of the root folder, use InkDBGetHeadFolder() . To find the parent folder of the passed parent or note, call InkGetParentFolder() . InkFolderGetContents() returns two chunk arrays, one containing the double word identifiers of all the folder's subfolders, the other containing the identifiers of the folder's child notes. InkFolderGetNumChildren() returns the number of subfolders and notes within a folder.

To display a note or folder's name in a GenDynamicList, use InkFolderDisplayChildInList() . This routine comes in handy when constructing UI for navigating the folder tree. To copy the icon and folder or note name of a folder or note into the visual moniker of an entry in a list, call InkNoteCopyMoniker() .

To get information about a folder's child, call InkFolderGetChildInfo() . This routine returns a bit specifying whether the child is a folder or note, along with the child's ID number. The InkFolderGetChildNumber() routine returns the passed child's place number within the folder.

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Titles and Keywords | Next: 2.5 Managing Notes and Folders


Managing Notes and Folders

Pen Object Library: 2.5 Working with the Ink DB: Managing Notes and Folders

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Navigating the Folder Tree | Next: 2.6 Manipulating Notes
InkFolderCreateSubFolder(), InkFolderMove(), InkFolderDelete(), InkNoteCreate(), InkNoteDelete(), InkNoteMove(), InkNoteCreatePage()

Some Ink DB applications might just create a hierarchy of notes and not allow the user to move or change notes. Applications that will move notes and folders should use the following functions to make changes.

The InkFolderCreateSubFolder() routine creates a new folder as a child of the passed existing folder. Use InkFolderMove() to move a folder to a new parent folder. InkNoteMove() similarly moves a note to a new parent folder. InkFolderDelete() deletes a folder and all subfolders and notes that folder contained. InkNoteCreate() creates a new note. InkNoteDelete() deletes a note. InkNoteCreatePage() adds a new page to a note.

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Navigating the Folder Tree | Next: 2.6 Manipulating Notes


Manipulating Notes

Pen Object Library: 2.6 Working with the Ink DB: Manipulating Notes

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 Managing Notes and Folders | Next: 2.7 Searching and Traversing the Tree
InkNoteGetPages(), InkNoteGetNumPages(), InkNoteSetModificationDate(), InkNoteGetModificationDate(), InkNoteGetCreationDate(), InkNoteSetNoteType(), InkNoteGetNoteType()

Normally, the note will store information supplied by an Ink or Text object. However, applications may work with a note's information directly. Call InkNoteGetPages() to get the DB item in which the note's information is stored. The DB item contains a chunk array; each entry of the array contains the information for one page (the DB item associated with an Ink or Text object). To find out how many pages there are in a given note, call InkNoteGetNumPages() .

The note will be expecting either text or ink; call InkNoteSetNoteType() to specify what sort of data will be coming in. The note type is specified by means of a NoteType value: NT_INK or NT_TEXT. To find out a note's type, call InkNoteGetNoteType() .

When writing changes, you may wish to update the note's modification date. Call InkNoteSetModificationDate() to update this information. To find out the date last modified, call InkNoteGetModificationDate() . To find out the date the note was created, call InkNoteGetCreationDate() .

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 Managing Notes and Folders | Next: 2.7 Searching and Traversing the Tree


Searching and Traversing the Tree

Pen Object Library: 2.7 Working with the Ink DB: Searching and Traversing the Tree

| Up: GEOS SDK TechDocs | Up | Prev: 2.6 Manipulating Notes | Next: 3 InkControlClass
InkNoteFindByTitle(), InkNoteFindByKeywords(), InkFolderDepthFirstTraverse()

Sometimes the user will remember what a note is called, but has lost it in the tree of folders. Sometimes the user will want to find all notes which contain a certain keyword. Use InkNoteFindByTitle() to get a buffer containing IDs of all notes whose titles match the passed string. InkNoteFindByKeywords() similarly returns a buffer containing the IDs of all notes with matching keywords.

For more complicated commands, InkFolderDepthFirstTraverse() allows the application to perform a depth-first traversal of the folder tree, calling the passed routine with all encountered folders.

| Up: GEOS SDK TechDocs | Up | Prev: 2.6 Manipulating Notes | Next: 3 InkControlClass


InkControlClass

Pen Object Library: 3 InkControlClass

| Up: GEOS SDK TechDocs | Up | Prev: 2 Working with the Ink DB

InkControlClass , a subclass of GenControlClass , provides a menu which allows the user to select an Ink tool for use with an Ink object.

Code Display 21-2 InkControlClass Features

  typedef ByteFlags 	InkControlFeatures;
/* These features may be combined using | and &:
    ICF_PENCIL_TOOL,
    ICF_ERASER_TOOL
    ICF_SELECTION_TOOL */
  typedef ByteFlags 	InkControlToolboxFeatures;
/* These features may be combined using | and &:
    ICTF_PENCIL_TOOL,
    ICTF_ERASER_TOOL
    ICTF_SELECTION_TOOL */
#define  IC_DEFAULT_FEATURES 					(ICF_PENCIL_TOOL | ICF_ERASER_TOOL | \
                     ICF_SELECTION_TOOL)
#define  IC_DEFAULT_TOOLBOX_FEATURES 					(ICTF_PENCIL_TOOL | ICTF_ERASER_TOOL | \
                     ICTF_SELECTION_TOOL)
/* Add this controller to the application's self-load options GCN list. */
| Up: GEOS SDK TechDocs | Up | Prev: 2 Working with the Ink DB


The Table Objects

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Table Objects
| One-page Version of this Chapter for Easy Printing

The Table Library allows you to easily create dynamic scrolling tables. By using objects from TableClass , you can create tables with certain characteristics; in particular, tables with many rows, with the data organized by column. For example, you could use TableClass to present a phone number list. One column might have a person's name, the next the phone number, the next the fax number, and so on. Each row would have the same basic format, though it would have different contents. The Table is designed primarily for text tables, though it can display anything you wish to draw.

You can give each column its own characteristics. Users may begin entering data directly into the Table; the TableClass object will automatically present a VisText to let the user enter text, then send a message to itself to replace the cell's contents with the new text; you can subclass that message if you need to check the text the user has entered or perform some operation on the new data.

The Table object does not actually store data; it rather helps you organize data stored elsewhere, and helps you display that data in a tabular format. It also makes it much easier for a user to edit the data.

1 Table Objects
2 TableClass Overview
3 TableClass Instance Data
3.1 Table Attributes
3.2 Defining Columns
3.3 Working with Rows
4 Using a Table Object
4.1 Drawing Cells
4.2 Selecting Cells
4.3 Editing Cells
4.4 Dragging and Dropping
4.5 Custom Scrolling Behavior
4.6 Handling Locator Searches
4.7 Table Headings
4.8 Changing Column Definitions
5 TableContentClass
5.1 TableContent Instance Data
5.2 Altering TableContent Instance Data
6 LocatorClass
6.1 Index Mode
6.2 Text Mode
6.3 LocatorClass Instance Data
6.4 Locator Actions

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Table Objects
| One-page Version of this Chapter for Easy Printing


Table Objects

The Table Objects: 1 Table Objects

| Up: GEOS SDK TechDocs | Up | Prev: The Table Objects | Next: 2 TableClass Overview

There are a number of Table objects available for you to use:

  • TableViewClass
     This subclass of GenViewClass acts as a holder for your table. Its GVI_content should be a TableContent. Specify the main table in the view with the TVI_mainTable instance data field.
  • TableContentClass
     This subclass of VisContentClass acts as an interface between the TableView object and the Table children.
  • TableClass
     This subclass of VisCompClass contains all the main Table functionality. This is where the bulk of the Table object functionality is located. You will typically subclass this object to add your own features.
  • LocatorClass
     This subclass of TableClass isn't really a Table at all. The Locator is a utility object that interacts with the Table to provide Table searching. You typically don't subclass this object; just include one in your application.
| Up: GEOS SDK TechDocs | Up | Prev: The Table Objects | Next: 2 TableClass Overview


TableClass Overview

The Table Objects: 2 TableClass Overview

| Up: GEOS SDK TechDocs | Up | Prev: 1 Table Objects | Next: 3 TableClass Instance Data

TableClass implements a scrolling table. The table should be composed of one or more different columns, and one or more similar rows. Typically, each column would be a different category, and each row would have an entry for every different category.

TableClass is a subclass of VisCompClass . This means that you can group tables together or with other Vis objects. (You can put titles on a scrolling table by grouping two different Table objects together; this is discussed in Table Headings .)

TableClass supports the following features:

  • Redrawing:
    TableClass sends itself messages to instruct itself to redraw cells as needed; you can intercept these messages if you wish to change how cells are drawn. You can instruct a Table object to redraw a range of cells; it will automatically send the appropriate messages to instruct itself to redraw just those cells currently visible on-screen.
  • Pointer Events:
    The TableClass object intercepts pointer events and sends itself appropriate MSG_TABLE_SELECT messages. Applications can subclass these messages if they want to take special actions when cells are selected. When you instantiate a Table object, you specify (by column) what combination of pointer events should act as a selection. TableClass can identify start-select, end-select, double-clicking, clicking and holding, and reselecting (i.e. clicking on an already-selected cell).
  • Selecting Cells
    When cells are selected, a Table object sends itself a MSG_TABLE_SELECT message. You can subclass this message if you wish. If you do not subclass this message, TableClass can handle the selection in a number of different ways: it can select a single cell, a whole row, a rectangular region, etc. The table also lets you specify how selections should be identified (e.g. drawn in reverse video, surrounded by a solid box, etc.).
  • Scrolling:
    If the Table object contains more rows than can be shown on-screen, it will automatically scroll as needed. You can instruct the Table to scroll up or down by sending it messages. You can also set a flag, instructing the Table to scroll automatically when it receives certain pointer events (e.g. when the pointer is dragged off the top or bottom of the table). The Table can also be set to scroll when certain keys are pressed.
  • Editing:
    The table can automatically create a VisText object when the user wants to edit the text in a cell. You can send messages to instruct it to create that VisText ; you can also subclass messages to set the default contents of the VisText , or to process the text entered by the user before it's entered in the cell. This is discussed in Editing Cells .
  • Drag-Drop:
    The Table implements a quick-copy, drag-and-drop mechanism. When you activate quick-copy mode, the user can drag a cell to a new location. At this time, the Table sends itself a message; you can intercept this message, and take an appropriate action (such as copying the data from the source cell over the destination cell). This is discussed in Dragging and Dropping .

There is one function the Table object does not perform. It does not store the data contained in the cells. When it needs to know the contents of a cell, it requests that information; when the user changes a cell, it informs you. You, however, must store the information yourself.

For example, whenever the Table object needs to draw a cell, it sends itself MSG_TABLE_QUERY_DRAW , specifying which cell needs to be drawn. You should intercept this message; your handler should look up the data stored in that cell, and draw its contents. Similarly, when a user edits a cell, you first tell the Table object what the current contents of the cell are (by intercepting MSG_TABLE_START_EDIT_CELL_TEXT ). The Table then brings up a VisText which contains the current contents of the cell. The user can edit or change this text. When the user is finished, the Table object informs the application (via MSG_TABLE_DONE_EDIT_CELL_TEXT ) what the cell's new contents are. The application should intercept the message and store the new contents.

Since the Table refers to its cells by row and column numbers, you may wish to store its data in a GEOS cell file . A cell file is a GEOS VM file which uses special structures and routines to organize DB items so they can be accessed by row and column numbers, instead of by group and item handles. For more information about cell files, consult the "Database" chapter of the Concepts book provided with the SDK.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Table Objects | Next: 3 TableClass Instance Data


TableClass Instance Data

The Table Objects: 3 TableClass Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 TableClass Overview | Next: 4 Using a Table Object

TableClass is a subclass of VisCompClass . This lets you group Tables together with other Tables and other Vis objects. Note, however, that you are not permitted to add children to a Table object. When TableClass creates a VisText object to let users enter data, it makes that VisText a child of the Table ; if you try to add children to the TableClass , you will interfere with that process, and cause many unpredictable problems.

This section describes the instance data fields defined for TableClass . It also describes how to set, examine, and change those fields which are of interest to the application.

The TableClass has the following instance data:

Code Display 5-1 TableClass Instance Data

/* TI_rows specifies the number of rows in the table object. This field can be set
 * dynamically with MSG_TABLE_SET_ROW_COUNT. This height includes the row
 * separators.
 */
    @instance		word		TI_rows;
/* TI_columns specifies the total number of columns in the table. All columns must
 * be visible at once (the table does not scroll horizontally). You must set this
 * field in your source file; it cannot currently be set at run-time.
 */
    @instance		word		TI_columns;
/* TI_visibleRows contains the number of rows currently displayed. You must set
 * this field in your source file; it cannot currently be set at run-time.
 */
    @instance		word		TI_visibleRows;
/* TI_topRow contains the row number of the first visible row. This is maintained
 * automatically by the Table object; applications should not set this field.
 */
    @instance		word		TI_topRow;
/* TI_tableFlags contains a record of TableFlags. This record is described below
 * in  Table Attributes.
 */
    @instance		TableFlags		TI_tableFlags;
/* TI_rowFlags contains a record of TableRowFlags. This record is described below
 * in  Working with Rows.
 */
    @instance		TableRowFlags		TI_rowFlags;
/* TI_columnDefinitions contains the chunk handle of a list of column definitions.
 * Column definitions are discussed in  Defining Columns.
 */
    @instance		ChunkHandle		TI_columnDefinitions;
/* TI_rowHeight specifies the height of each row in "points" (72 points = 1 inch).
 * You must set this in your source code; there is no way to change this
 * dynamically.
 */
    @instance		word		TI_rowHeight;
/* This field specifies the Table object's TableBorderFlags, which specify the
 * border and bounds for the Table object. TableBorderFlags are described
 * below in  Table Attributes.
 */
    @instance		TableBorderFlags			TI_borderFlags;
/* The following instance data fields are for internal use by the Table object.
 * You should not set, inspect, or change any of these fields, either in your
 * source code or at run-time.
 */
    @instance		TableCellLocation			TI_currentSelectionStart =
    { T_NONE_SELECTED, T_NONE_SELECTED };
    @instance		TableCellLocation			TI_currentSelectionEnd =
    { T_NONE_SELECTED, T_NONE_SELECTED };
    @instance		TableCellLocation			TI_lastSelectionStart =
    { T_NONE_SELECTED, T_NONE_SELECTED };
    @instance		TableCellLocation			TI_lastSelectionEnd =
    { T_NONE_SELECTED, T_NONE_SELECTED };
    @instance		Rectangle			TI_bounds = {0, 0, 0, 0};
    @instance		TableRangeInversionType			TI_tableRangeInversion;
    @instance		TableCellLocation			TI_lastCell = {
            T_NONE_SELECTED,
            T_NONE_SELECTED};
    @instance		TableSelectionDrawStyleType 			TI_selectionDrawStyle;
    @instance		TablePrivateFlags			TI_privateFlags;
    @instance		ChunkHandle			TI_textObj;

Table Attributes

Defining Columns

Working with Rows

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 TableClass Overview | Next: 4 Using a Table Object


Table Attributes

The Table Objects: 3.1 TableClass Instance Data: Table Attributes

| Up: GEOS SDK TechDocs | Up | Prev: 3 TableClass Instance Data | Next: 3.2 Defining Columns
TableFlags, TableBorderFlags, MSG_TABLE_SET_FLAGS, MSG_TABLE_GET_FLAGS, MSG_TABLE_SET_BORDER_FLAGS, MSG_TABLE_GET_BORDER_FLAGS

There are a few attributes which apply to the entire Table . Ordinarily, you would set these up when you define the Table , and leave them unchanged; however, you can get or change these attributes at will, by sending messages.

Every Table has a record of TableFlags , stored in the instance data field TI _tableFlags . This record contains the following flags:

TF_MAIN_TABLE
This flag indicates that this Table is the main (primary) table below the TableContent object. A Table marked with this flag will be the one that is sent scroll messages from the TableView. This is important if you use a separate TableClass object to contain headers for your table. (See Table Headings .)
TF_INTERNAL_DRAG_DROP
This flag should not be modified; it is internally managed. If this flag is set, this indicates that the Table is in drag-and-drop mode from one location in the Table to another location within that same Table; this lets the user copy one cell to another.The user does this by start-selecting the cell he or she wants to copy, and dragging ("hold-selecting") the pointer to the new location, then releasing the pointer ("end-selecting"); the cell's contents will be copied to the new location. If TF_EXIT_DRAG_DROP_UPON_COMPLETION is set, the Table will automatically clear TF_INTERNAL_DRAG_DROP after every drag-drop operation. (See Dragging and Dropping .)
TF_EXTERNAL_DRAG_DROP
This flag should not be modified; it is internally managed. If this flag is set, this indicates that the Table is in drag-and-drop mode from a location outside the Table to another location within the Table.
TF_ENABLE_AUTO_SCROLLING
If this flag is set, the Table will automatically scroll whenever the user clicks the pointer inside the Table and drags it across the top or bottom boundary. This flag does not affect any other scrolling behavior you may have added to your Table (such as scroll buttons in the parent TableView).
TF_SELECTION_ALWAYS_VISIBLE
If this flag is set, a Table's selection will always remain visible when scrolling. Any scrolling that might move the current selection off-screen will result in the movement of the selection (row by row) to stay visible. In other words, this flag does not ensure that the current selection remains visible; it only ensures that a selection bar exists on-screen.
TF_FIXED_SELECTION
This flag is currently unsupported.
TF_EXIT_DRAG_DROP_UPON_COMPLETION
If this flag is set, when the user finishes a drag-and-drop operation, the Table will automatically leave drag-and-drop mode, clearing TF_INTERNAL_DRAG_DROP. (See Dragging and Dropping .) If you do not set this flag, once you begin a drag and drop operation, you will need to manually exit drag and drop by clearing the TF_INTERNAL_DRAG_DROP flag yourself. (This is not recommended.)
TF_TARGETABLE
If this flag is set, the Table object will grab the target whenever it receives a MSG_META_START_SELECT . Unless your Table is display-only, you will probably always want this flag set.
TF_NOTIFY_SELECTION_CHANGED
If this flag is set, the Table object will send itself MSG_TABLE_NOTIFY_SELECTION_CHANGED whenever the stored selection value changes. If you wish to have that message sent (and intercept it) you must set this flag.

You may set these bits in TI _tableFlags when you define the table object in your source file. If you wish to change the TableFlags record of an existing Table object, you must do this by sending the object MSG_TABLE_SET_FLAGS , not by editing TI _tableFlags directly. You can find out the current settings of TI_tableFlags by sending MSG_TABLE_GET_FLAGS to the Table object.

Each table object also has a record of TableBorderFlags , stored in the instance data field TI _borderFlags . This record specifies what borders should be drawn around the Table . This record has the following flags:

TBF_BOX_BORDERS
If this flag is set, a box will be drawn around the entire portion of the Table visible on-screen. Setting this flag is equivalent to setting TBF_TOP_BORDER, TBF_BOTTOM_BORDER, TBF_LEFT_BORDER, and TBF_RIGHT_BORDER.
TBF_TOP_BORDER
If this flag is set, a line will be drawn at the top border of the portion of the Table visible on-screen.
TBF_BOTTOM_BORDER
If this flag is set, a line will be drawn at the bottom border of the portion of the Table visible on-screen.
TBF_LEFT_BORDER
If this flag is set, a line will be drawn at the left border of the portion of the Table visible on-screen.
TBF_RIGHT_BORDER
If this flag is set, a line will be drawn at the right border of the portion of the Table visible on-screen.
TBF_BOX_MARGINS
If this flag is set, margins will be added around the entire portion of the Table visible on-screen. Setting this flag is equivalent to setting TBF_TOP_MARGIN, TBF_BOTTOM_MARGIN, TBF_LEFT_MARGIN, and TBF_RIGHT_MARGIN.
TBF_TOP_MARGIN
If this flag is set, a margin will be added along the top border of the portion of the Table visible on-screen.
TBF_BOTTOM_MARGIN
If this flag is set, a margin will be added along the bottom border of the portion of the Table visible on-screen.
TBF_LEFT_MARGIN
If this flag is set, a margin will be added along the left border of the portion of the Table visible on-screen.
TBF_RIGHT_MARGIN
If this flag is set, a margin will be added along the right border of the portion of the Table visible on-screen.

To find out what TableBorderFlags are currently set, send MSG_TABLE_GET_BORDER_FLAGS to the Table . As with TableFlags , you may set the instance field TI _borderFlags in your source file, but you may not alter it directly at run-time; instead, you can set the field indirectly, by sending MSG_TABLE_SET_BORDER_FLAGS to the Table object.

MSG_TABLE_GET_FLAGS

TableFlags 	MSG_TABLE_GET_FLAGS();

This message returns the current TableFlags settings in the recipient's TI _tableFlags field. To change this field, send MSG_TABLE_SET_FLAGS .

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: None.

Return: The TableFlags record currently stored in the Table object's TI _tableFlags field.

Structures: The TableFlags record (see Every Table has a record of TableFlags, stored in the instance data field TI_tableFlags. This record contains the following flags: ).

MSG_TABLE_SET_FLAGS

void	MSG_TABLE_SET_FLAGS(
        TableFlags		setTableFlags,
        TableFlags		unsetTableFlags);

This message alters the TableFlags settings in the recipient's TI _tableFlags field. To find out what the current settings are, send MSG_TABLE_GET_FLAGS .

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: setTableFlags All the flags in this record will be turned on .

unsetTableFlags
All the flags in this record will be turned off .

Return: Nothing.

Structures: The TableFlags record (see Every Table has a record of TableFlags, stored in the instance data field TI_tableFlags. This record contains the following flags: ).

MSG_TABLE_GET_BORDER_FLAGS

TableBorderFlags 	MSG_TABLE_GET_BORDER_FLAGS();

This message returns the current TableBorderFlags settings in the recipient's TI _borderFlags field. To change this field, send MSG_TABLE_SET_BORDER_FLAGS .

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: None.

Return: The TableBorderFlags record currently stored in the Table object's TI _borderFlags field.

Structures: The TableBorderFlags record (see Each table object also has a record of TableBorderFlags, stored in the instance data field TI_borderFlags. This record specifies what borders should be drawn around the Table. This record has the following flags: ).

MSG_TABLE_SET_BORDER_FLAGS

void	MSG_TABLE_SET_BORDER_FLAGS(
        TableBorderFlags		borderFlags);

This message alters the TableBorderFlags settings in the recipient's TI _borderFlags field. To find out what the current settings are, send MSG_TABLE_GET_FLAGS .

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: borderFlags The current value in TI _borderFlags will be replaced by the value in this record.

Return: Nothing.

Structures: The TableBorderFlags record (see Each table object also has a record of TableBorderFlags, stored in the instance data field TI_borderFlags. This record specifies what borders should be drawn around the Table. This record has the following flags: ).

| Up: GEOS SDK TechDocs | Up | Prev: 3 TableClass Instance Data | Next: 3.2 Defining Columns


Defining Columns

The Table Objects: 3.2 TableClass Instance Data: Defining Columns

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Table Attributes | Next: 3.3 Working with Rows
TableColumnDefinition, TableColumnFlags, TableRangeInversionType, MSG_GEN_TABLE_GET_COLUMN_COUNT

When you create a Table object, you must specify what columns the Table has. Typically, each column will represent a different kind of data, and can be of a different length. When you define a Table object, you specify how many columns the table will have; this number is fixed for the table, and cannot be changed later. (It is possible to have your Table switch the column definitions that they use, although it will take a fair amount of work on your part; that is discussed in Changing Column Definitions .) You can find out how many columns a table has by sending it MSG_TABLE_GET_COLUMN_COUNT .

Each column has its own definition--stored in the Table's TI_ columnDefinitions array--specified by a TableColumnDefinition structure that cannot be changed later. This structure has the following definition:

 typedef struct {
    TableColumnFlags			TCD_flags;
    word			TCD_width;
} TableColumnDefinition;
TCD _flags
This is a TableColumnFlags record (described on The TableColumnFlags record has the following flags: below).
TCD _width
This is the width of the column in points (72 points = 1 inch, so 28.3 points ª 1 centimeter).

The TableColumnFlags record has the following flags:

TCF_DRAW_RIGHT_SEPARATOR
If this flag is set, the column will have a solid line drawn on its right border (separating it from the next column to its right). This right-separator will not be drawn if the column is the last (right-most) column in the Table.
TCF_START_SELECT
If this flag is set, then if the user performs a "start-select" anywhere in the column, the Table object will send itself MSG_TABLE_SELECT.
TCF_END_SELECT
If this flag is set, then if the user performs an "end-select" anywhere in the column, the Table object will send itself MSG_TABLE_SELECT.
TCF_DRAG_SELECT
If this flag is set, then if the user performs a pointer drag that crosses cell boundaries within the column, or enters or leaves the column, the Table object will send itself MSG_TABLE_SELECT.
TCF_DOUBLE_SELECT
If this flag is set, then if the user double-clicks anywhere in the column, the Table object will present a VisText , allowing the user to edit the contents of the cell. (See Editing Cells .)
TCF_RESELECT
If this flag is set, then if the user performs a "start-select" in the column on a cell that is already in a range of selected cells, the Table object will send itself MSG_TABLE_SELECT.
TCF_HOLD_SELECT
If this flag is set, then if the user performs a "start-select" in the column on a cell and holds for a certain period of time, the Table will send itself MSG_TABLE_SELECT. This flag also sets the TF_INTERNAL_DRAG_DROP flag.
TCF_TRIT
This is a three-bit field. The value in this field is a member of the TableRangeInversionType enumerated type; it specifies how the Table should display selected cells in this column.

TableRangeInversionType specifies which cells should be drawn as highlighted when the user performs a selection action. Note that each column has its own TableRangeInversionType value. If a selection action crosses from one column into another, the Table uses the TableRangeInversionType of the last column the pointer was in. TableRangeInversionType has the following values:

TRIT_CELL
When the user selects a cell in this column, highlight just that cell.
TRIT_ROW
When the user selects a cell in this column, highlight the entire row containing that cell.
TRIT_COLUMN
When the user selects a cell in this column, highlight the entire visible portion of this column (i.e. every cell in this column that is (at least partially) visible on-screen).
TRIT_MULTI_ROW
Highlight the area bounded by the two cells in which the selection started and ended. The selection is made row by row as if all cells were in a single sequence with the last (right-most) cell of one row being followed by the first (left-most) cell of the next row. (One can think of this selection being analogous to a text selection across several lines.)
TRIT_RECTANGLE
Highlight all the cells in the rectangle pointed by the start-select cell and the end-select cell.
TRIT_MULTI_ROW_FULL
Highlight the row in which the user start-selects, and all rows through which the user drags the selection pointer.
TRIT_MULTI_COL_FULL
Highlight the first column in which the user start-selects, and all columns through which the user drags the selection pointer.
TRIT_NONE
This indicates that the Table should not highlight any selections.

When you create a Table , you must specify how many columns the table has, and what the characteristics of each column are. You specify the number of columns the Table has by setting TI _columns to that number. You cannot change the number of columns in the Table after the Table has been created. To find out how many columns a Table has, send it MSG_TABLE_GET_COLUMN_COUNT .

To specify the attributes of each column, you must create a chunk in the same object block as the Table . That chunk must contain an array of TableColumnDefinition structures, one for each column in the Table . The structures should be in the same order as the columns (i.e. the left-most column would be the first structure in the array).

MSG_GEN_TABLE_GET_COLUMN_COUNT

word	MSG_GEN_TABLE_GET_COLUMN_COUNT();

This message returns the number of columns in a Table object.

Source: Unrestricted.

Destination: Any Table object.

Parameters: None.

Return: The number of columns in the Table .

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Table Attributes | Next: 3.3 Working with Rows


Working with Rows

The Table Objects: 3.3 TableClass Instance Data: Working with Rows

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Defining Columns | Next: 4 Using a Table Object
TableRowFlags, MSG_TABLE_GET_ROW_FLAGS, MSG_TABLE_SET_ROW_FLAGS, MSG_TABLE_GET_ROW_COUNT, MSG_TABLE_SET_ROW_COUNT

In a Table object, rows and columns are fundamentally different. Each column has its own properties; columns are generally used to display different kinds of information. Rows, on the other hand, are similar throughout a Table . Each row may contain different data, but all rows have the same basic structure throughout a given Table .

This has some consequences. For example, all columns are usually displayed; the Table should be wide enough to display all visible columns at once.

All the rows, on the other hand, are generally not displayed at once. The Table will display as many rows as it has room for; the user can scroll the table to see other rows. Also, while there are row properties (defined in TableRowFlags ) that are similar in nature to the column properties defined in TableColumnFlags , you cannot set these properties individually for a row; all rows must have the same TableRowFlags .

The instance data field TI _rowFlags specifies what TableRowFlags will be used for every row in the table. TableRowFlags is a word-sized record with the following field:

TRF_DRAW_ROW_SEPARATOR
If this field is set, the Table will draw a line below each column separating the rows. No line will be drawn below the Table's last row.

To find out how TI _rowFlags is set, send MSG_TABLE_GET_ROW_FLAGS to the Table . To change the settings in this field, send MSG_TABLE_SET_ROW_FLAGS.

To find out how many rows a table has, send it MSG_TABLE_GET_ROW_COUNT . You can also add or remove rows at will by sending MSG_TABLE_SET_ROW_COUNT. This message adds rows to, or removes them from, the end of the Table .

MSG_TABLE_GET_ROW_FLAGS

TableRowFlags MSG_TABLE_GET_ROW_FLAGS();

This message returns the current settings of a Table object's TI _rowFlags field. This field holds the TableRowFlags used for every row in the Table .

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: Nothing.

Return: The TableRowFlags used for all rows in the Table .

Structures: TableRowFlags (described on TRF_DRAW_ROW_SEPARATOR If this field is set, the Table will draw a line below each column separating the rows. No line will be drawn below the Table's last row. ).

MSG_TABLE_SET_ROW_FLAGS

void	MSG_TABLE_SET_ROW_FLAGS(
        TableRowFlags		flags);

This message changes current settings of a Table object's TI _rowFlags field. This field holds the TableRowFlags used for every row in the Table .

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: flags This is the new set of TableRowFlags to be used for every row in the Table .

Return: Nothing.

Structures: TableRowFlags (described on TRF_DRAW_ROW_SEPARATOR If this field is set, the Table will draw a line below each column separating the rows. No line will be drawn below the Table's last row. ).

MSG_TABLE_GET_ROW_COUNT

word	MSG_TABLE_GET_ROW_COUNT();

This message returns the total number of rows in a Table object.

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: Nothing.

Return: The number of rows in the Table .

MSG_TABLE_SET_ROW_COUNT

void	MSG_TABLE_SET_ROW_COUNT(
        word	rowCount);

This message changes the Table object's total number of rows. If the new row count is smaller than the old one, the last rows will be truncated.

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: rowCount The new number of rows the Table should contain. Rows are added and removed at the end of the Table .

Return: Nothing.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Defining Columns | Next: 4 Using a Table Object


Using a Table Object

The Table Objects: 4 Using a Table Object

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 TableClass Instance Data | Next: 5 TableContentClass

The Table object is designed to be easy to use. You cannot use a TableClass object directly; you will need to define a subclass of TableClass in order to intercept certain required messages. You will also need to set a few instance fields when you define the Table in your source code.

The Table object must appear within a TableContent object. If it is within a regular VisContent, crashes may result.

When you create a Table object, you will need to set the following instance data fields (the other fields may be left with there default values):

TI _rows
This is the number of rows in the Table . You may set this field to zero. (You can add and remove rows at run-time.) For more information, see Working with Rows .
TI _columns
This is the number of columns in the Table . This must be at least one. You cannot currently add or remove columns at run-time. For more information, see Defining Columns .
TI _visibleRows
This is the number of rows visible on-screen at any one time; thus, this field, together with TI _rowHeight , define the height of a Table object. This field must be at least 1.
TI _columnDefinitions
This is the handle of a chunk containing an array of TableColumnDefinition structures, which specify the properties of the Table 's columns. For more information, see Defining Columns .
TI _rowHeight
This is the height of a row in points (72 points = 1 inch, so 28.3 points ª 1 centimeter). This must be larger than zero.

In order to use a TableClass object, you must define handlers for a few messages. As a general rule, your handlers should call the TableClass handlers after they take their actions; in some cases, your handlers's main role is to add extra data to the message, which will be interpreted by the default handler.

You should define handlers for the following tasks:

  • You must define a handler for MSG_TABLE_QUERY_DRAW , which redraws a specified cell to a GState. (The TableClass object doesn't actually know what the contents of the cells are; it relies on your subclass's code to draw the cells.) For more information, see Drawing Cells .
  • You may wish to define a handler for MSG_TABLE_SELECT . This will let you know when the user has selected a cell; you can perform appropriate actions, and decide whether to call the default handler (which highlights the selection and performs other selection actions). For more information, see Selecting Cells .
  • You should provide handlers for MSG_TABLE_START_EDIT_CELL_TEXT and MSG_TABLE_DONE_EDIT_CELL_TEXT . These messages let the user edit the text in Table cells. By subclassing the messages, you can provide the current cell contents for the user to edit, and store the user's text after he or she has finished entering it. For more information, see Editing Cells .
  • The Table object supports drag-and-drop copying. However, it does not actually know what the cells contain, so it can't perform the copying itself. Instead, when the user performs a drag-and-drop operation, the Table object sends MSG_TABLE_DRAG_DROP_COMPLETE ; the application should intercept the message, and do any appropriate copying of data. For more information, see Dragging and Dropping .
  • You may want to implement your own custom scrolling behavior. ( TableClass supports auto-scrolling with the addition of the TF_ENABLE_AUTO_SCROLLING flag; auto scrolling does not, however, provide any visual UI (such as scroll buttons) to enable scrolling.) If you want custom scrolling behavior, you will need to implement scroll buttons in your TableView and appropriate messages to your Table object (such as MSG_TABLE_SCROLL_SINGLE_UP and MSG_TABLE_SCROLL_SINGLE_DOWN) . For more information, see Custom Scrolling Behavior .
  • If you link a Locator object to your Table (see LocatorClass ) you will want to intercept MSG_TABLE_CHAR_LOCATE and MSG_TABLE_STRING_LOCATE to implement your own search methods. For more information, see Handling Locator Searches .
  • If you want to change your column definitions dynamically, you will need to define additional column definitions and switch which ones you use under the Table. There is no API currently for this behavior; you will need to write the messages and handle them yourself. This is discussed in Changing Column Definitions .

Drawing Cells

Selecting Cells

Editing Cells

Dragging and Dropping

Custom Scrolling Behavior

Handling Locator Searches

Table Headings

Changing Column Definitions

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 TableClass Instance Data | Next: 5 TableContentClass


Drawing Cells

The Table Objects: 4.1 Using a Table Object: Drawing Cells

| Up: GEOS SDK TechDocs | Up | Prev: 4 Using a Table Object | Next: 4.2 Selecting Cells
MSG_TABLE_QUERY_DRAW, MSG_TABLE_REDRAW_TABLE, MSG_TABLE_REDRAW_ROW, MSG_TABLE_REDRAW_COLUMN, MSG_TABLE_REDRAW_CELL, MSG_TABLE_REDRAW_RANGE

The Table object does not actually store the contents of any of the cells. Instead, it helps the application manage and display its data. This means that whenever the Table needs to draw a cell (e.g. when the Table scrolls, or when a cell is covered and then exposed), it has to ask the application to draw it.

Whenever the Table object needs to draw a cell, it sends itself MSG_TABLE_QUERY_DRAW , once for each cell that needs to be redrawn. This message is designed to be subclassed; if you do not subclass it, the default handler will not do anything, and the cell will not be drawn.

The message comes with two arguments:

  • A TableCellLocation structure, specifying which cell needs to be redrawn.
  • A GStateHandle . The GState's clipping region is set to the current cell's boundaries; the current position in the GState is the upper-left corner of the cell.

You should intercept this message and take whatever actions are necessary to draw the contents of the cell. Ordinarily, this means calling a routine such as GrDrawTextAtCP() . The default handler doesn't do anything, so you need not call the superclass. You should not free the passed GState.

For efficiency reasons, the Table object actually uses the same GState for several cells that all need to be redrawn at once. For example, suppose the Table needs to redraw cells (10, 0), (10, 1), (10, 2), and (10, 3). The table will take the following steps:

  1. First, the Table creates a GState.
  2. The Table sets the GState's clipping region to the boundaries of cell (10, 0), and puts the current position in the upper-left corner of that cell.
  3. The Table sends itself MSG_TABLE_QUERY_DRAW, specifying that cell (10, 0) needs to be redrawn.
  4. The application intercepts this message, drawing the contents of cell (10, 0) to the passed GState.
  5. The Table changes the GState's clipping region to the boundaries of cell (10, 1), and moves the current position to the upper-left corner of that cell.
  6. The Table sends itself MSG_TABLE_QUERY_DRAW, specifying that cell (10, 1) needs to be redrawn.
  7. The application intercepts this message, drawing the contents of cell (10, 1) to the passed GState.
  8. The Table repeats steps 5-7 for cells (10, 2) and (10, 3).
  9. Finally, the Table frees the GState.

Code Display 5-2 Handling MSG_TABLE_QUERY_DRAW

/* For simplicity, we previously defined an instance field to hold the optr of the
 * chunk array we are using. We will lock down that data using that instance
 * field. CoffeeTableClass is a subclass of TableClass. */
@method CoffeeTableClass, MSG_TABLE_QUERY_DRAW
{
    char *data;
    word cArrayIndex, size;
    cArrayIndex = (location.TCL_row * TABLE_COLS) + location.TCL_column;
    MemLock(OptrToHandle(pself->CTI_chunkArray));
    data = ChunkArrayElementToPtr((pself->CTI_chunkArray),
                cArrayIndex, &size);
    GrDrawTextAtCP(gstate, data, 0);
    MemUnlock(OptrToHandle(pself->CTI_chunkArray);
}

The Table automatically sends MSG_TABLE_QUERY_DRAW when it knows a part of the Table may be inaccurate, e.g. when the user has edited a cell. You can also instruct the Table to redraw part or all of itself by sending it one of the MSG_TABLE_REDRAW... messages. These messages instruct the Table to send out appropriate MSG_TABLE_QUERY_DRAW messages for one or more visible cells. There are five MSG_TABLE_REDRAW... messages:

MSG_TABLE_REDRAW_TABLE
This instructs the Table to redraw all cells visible on-screen.
MSG_TABLE_REDRAW_ROW
This instructs the Table to redraw a specified row, if it's on-screen.
MSG_TABLE_REDRAW_COLUMN
This instructs the Table to redraw those cells in a specified column which are on-screen.
MSG_TABLE_REDRAW_CELL
This instructs the Table to redraw a specified cell, if it's on-screen.
MSG_TABLE_REDRAW_RANGE
This instructs the Table to redraw the portion of a specified range of cells that is visible on-screen.

MSG_TABLE_QUERY_DRAW

void	MSG_TABLE_QUERY_DRAW(
        TableCellLocation		location,
        GStateHandle		gstate);

The Table object sends itself this message whenever a cell needs to be redrawn. The default handler does nothing; when you use a Table object, you must subclass this message to draw the cell.

Source: A TableClass object.

Destination: The Table sends this message to itself.

Parameters: location The cell which needs to be redrawn.

gstate
A GStateHandle specifying a GState you should draw to. The GState's clipping region is set to the cell boundaries; the current position (CP) is the upper-left corner of the cell.

Return: Nothing. (You should not free the GState.)

Structures: TableCellLocation (see A TableCellLocation structure is used to specify a cell within the Table. It has the following definition: ).

Interception: This message must be intercepted; your handler must draw the cell's contents. You need not call the superclass's handler (which does nothing).

MSG_TABLE_REDRAW_TABLE

void	MSG_TABLE_REDRAW_TABLE();

This message instructs the Table to redraw the entire portion of the Table visible on-screen (creating a GState, sending out appropriate MSG_TABLE_QUERY_DRAW messages, etc.).

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: None.

Return: Nothing.

MSG_TABLE_REDRAW_ROW

void	MSG_TABLE_REDRAW_ROW(
        word	row);

This message instructs the Table to redraw a specified row, if it's visible on-screen (creating a GState, sending out appropriate MSG_TABLE_QUERY_DRAW messages, etc.).

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: row The index of the row to redraw. (The first row has index 0.)

Return: Nothing.

MSG_TABLE_REDRAW_COLUMN

void	MSG_TABLE_REDRAW_COLUMN(
        word	column);

This message instructs the Table to redraw those cells in a specified column that are visible on-screen (creating a GState, sending out appropriate MSG_TABLE_QUERY_DRAW messages, etc.).

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: column The index of the column to redraw. (The first column has index 0.)

Return: Nothing.

MSG_TABLE_REDRAW_CELL

void	MSG_TABLE_REDRAW_CELL(
        TableCellLocation		location);

This message instructs the Table to redraw a specified cell if it is visible on-screen (creating a GState, sending out appropriate MSG_TABLE_QUERY_DRAW messages, etc.).

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: location The cell to redraw.

Return: Nothing.

Structures: The cell is specified with a TableCellLocation structure (described on A TableCellLocation structure is used to specify a cell within the Table. It has the following definition: ).

MSG_TABLE_REDRAW_RANGE

void	MSG_TABLE_REDRAW_RANGE(
        TableCellRange		cellRange);

This message instructs the Table to redraw a range of cells (creating a GState, sending out appropriate MSG_TABLE_QUERY_DRAW messages, etc.).

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: cellRange The range of cells to redraw.

Return: Nothing.

Structures: The cell range to redraw is specified with a TableCellRange structure (described on A TableCellRange structure is used to specify a range of cells. It has the following definition: ).

| Up: GEOS SDK TechDocs | Up | Prev: 4 Using a Table Object | Next: 4.2 Selecting Cells


Selecting Cells

The Table Objects: 4.2 Using a Table Object: Selecting Cells

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Drawing Cells | Next: 4.3 Editing Cells
TableCellLocation, CellRange, MSG_TABLE_SELECT, MSG_TABLE_GET_CURRENT_SELECTION, MSG_TABLE_SET_CURRENT_SELECTION. MSG_TABLE_SET_ROW_RANGE_SELECTION

You can configure under what circumstances a Table object will select a cell by setting the TableColumnFlags for the various columns in the table. The TableColumnFlags determine what will constitute a "selection event", and what cells will be selected. When a selection event occurs, the Table object will send itself MSG_TABLE_SELECT. This message passes two arguments:

  • A TableCellLocation structure (described below). This structure specifies which cell was selected. If more than one cell was selected, this will specify the cell where the selection action began .
  • A TableColumnFlags record. This record specifies two things. It specifies what pointer event or events caused the selection; and it specifies, in its TCF_TRIT field, which cells should be highlighted as selected.

A TableCellLocation structure is used to specify a cell within the Table . It has the following definition:

typedef struct {
    word	TCL_row;
    word	TCL_column;
} TableCellLocation;
TCL _row
This is the cell's row. (The first row is row number zero, as always.)
TCL _column
This is the cell's column. (The first column is column number zero, as always.)

The default handler for this message causes an area of the table to be highlighted. The area is determined by the TCF_TRIT field of the TableColumnFlags record passed with the message. If you wish, you may intercept the message before it is handled by the default handler, and change the TCF_TRIT field in the passed TableColumnFlags ; this will change which cells will be highlighted by the default handler. For example, if you change the TCF_TRIT field to contain TRIT_NONE, then the default handler will not highlight any rows.

You can find out what cells, if any, are currently selected by sending MSG_TABLE_GET_CURRENT_SELECTION to the Table object. This message takes one argument, a pointer to a TableCellRange structure. The handler will fill in this structure with the currently selected range.

A TableCellRange structure is used to specify a range of cells. It has the following definition:

typedef struct {
    TableCellLocation				TCR_start;
    TableCellLocation				TCR_end;
} TableCellRange;
TCR _start
This TableCellLocation specifies the first cell in the selected range. (The TableCellLocation structure is described on A TableCellLocation structure is used to specify a cell within the Table. It has the following definition: .)
TCR _end
This TableCellLocation specifies the last cell in the selected range. (Note that this cell may be higher up in the Table then TCR _start ; the same range is defined, whichever order TCR _start and TCR _end appear in.)

Note that a TableCellRange forms a rectangle. You can change the selected cells at any time by sending MSG_TABLE_SET_CURRENT_SELECTION . This message is passed one argument, a TableCellRange structure; that structure specifies what the current selection should be. The Table object responds by changing the current selection but does not send a MSG_TABLE_SELECT . To cancel the current selection (and leave the Table with nothing selected), send MSG_TABLE_SET_CURRENT_SELECTION , and put the constant T_NONE_SELECTED in the TCR _start. TCL _column field of the passed TableCellRange structure. (If you do this, the other fields of the TableCellRange will be ignored.) Again, MSG_TABLE_SELECT is not sent in this case.

MSG_TABLE_SELECT

void	MSG_TABLE_SELECT(
        TableCellLocation		location,
        TableColumnFlags		tableColumnFlags);

The Table object sends this message to itself when the user selects one or more cells with a pointer object. (I.e. this message is generated through use of the pointer for Table selection, not when the selection changes through other means.) The TableColumnFlags of the various columns determine whether a particular mouse action is interpreted as a selection. If it is, the Table object sends itself this message, instructing itself to highlight the appropriate cells.

Source: A TableClass object.

Destination: The Table object sends this message to itself.

Parameters: location This is the selected cell; that is, the cell in which the pointer was when the column's selection criteria were met.

tableColumnFlags
This is a record of TableColumnFlags . The flags specify two things: They specify what criteria were used to determine that a selection had happened; and they specify (in the TCF_TRIT field) which cells should be highlighted.

Return: Nothing.

Structures: TableCellLocation (see A TableCellLocation structure is used to specify a cell within the Table. It has the following definition: ) and TableColumnFlags (see The TableColumnFlags record has the following flags: ).

Interception: You may intercept this. If you intercept this message, you can change which cells will be highlighted by changing the tableColumnFlags. TCF_TRIT field.

MSG_TABLE_GET_CURRENT_SELECTION

void	MSG_TABLE_GET_CURRENT_SELECTION(
        TableCellRange *		cellRange);

This message retrieves what cells are currently selected within a Table object

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: cellRange A pointer to a TableCellRange structure.

Return: *cellRange will specify the first and last selected cells.

Structures: TableCellRange (described on A TableCellRange structure is used to specify a range of cells. It has the following definition: ).

MSG_TABLE_SET_CURRENT_SELECTION

void	MSG_TABLE_SET_CURRENT_SELECTION(
        TableCellRange		cellRange);

This message changes what cells are currently selected within a Table object.

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: cellRange A pointer to a TableCellRange structure, specifying what cells should be selected. (If cellRange. TCR _start. TCL _column = T_NONE_SELECTED, no cells will be selected.)

Return: Nothing.

Structures: TableCellRange (described on A TableCellRange structure is used to specify a range of cells. It has the following definition: ).

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Drawing Cells | Next: 4.3 Editing Cells


Editing Cells

The Table Objects: 4.3 Using a Table Object: Editing Cells

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Selecting Cells | Next: 4.4 Dragging and Dropping
TableEditCellTextParams, MSG_TABLE_START_EDIT_CELL_TEXT, MSG_TABLE_DONE_EDIT_CELL_TEXT, MSG_TABLE_STOP_CELL_EDIT

The Table objects make it easy for the user to edit cells. If the column has TCF_DOUBLE_SELECT enabled, a user can edit a cell in the column just by double-clicking on it. When the user does this, the Table object will take the following steps:

  1. First, it sends itself MSG_TABLE_START_EDIT_CELL_TEXT . The default handler causes the Table object to create a VisText under the TableContent which the user can enter text in.
  2. When the user has finished entering the text, and closes the VisText (by pressing ENTER ) , the Table object sends itself MSG_TABLE_DONE_EDIT_CELL_TEXT , passing along the text that the user entered in the VisText . The application should store the text wherever it stores the Table 's data.
    If the user finishes editing by clicking in another cell, a MSG_TABLE_STOP_CELL_EDIT is generated and the edit is cancelled. (If your program does not wish to cancel the edit under these circumstances, then intercept the message.)
  3. Finally, the Table sends itself a MSG_TABLE_QUERY_DRAW for the cell which was edited. The application should intercept this message and redraw the cell normally.

Both MSG_TABLE_START_EDIT_CELL_TEXT and MSG_TABLE_DONE_EDIT_CELL_TEXT use a special structure, the TableEditCellTextParams structure; the structure's fields have slightly different meanings for each message. This structure has the following definition:

typedef struct {
    TableCellRange			TECT_cells;
    MemHandle			TECT_text;
    word			TECT_length;
} TableEditCellTextParams;
TECT _cells
This is a TableCellRange structure (described on A TableCellRange structure is used to specify a range of cells. It has the following definition: ), specifying what cells have been selected for editing. If a single cell has been selected for editing, TCR _start will be the same as TCR _end .
TECT _text
This is the handle of a global memory block, containing the text in the cell.
TECT _length
This is the length of the string in TECT _text , or zero if the string is null-terminated.

MSG_TABLE_START_EDIT_CELL_TEXT creates a VisText object (within the TableContent and over the cell to be edited) to allow the user to edit the cell's text. This message passes a set of TableEditCellTextParams containing the cell range being edited (TECT _cells ) and the initial text to display to the user (if any) in TECT _text and TECT _length . This text object, which may be referenced via TI_textObj , can persist between edits. Thus, if you change its instance data, be aware that these changes may or may not linger in future edits. You may wish to update any changes in your MSG_TABLE_START_EDIT_CELL_TEXT handler.

The default handler clears both TECT _text and TECT _length . Your application may subclass the message, and fill in these fields with default text for the VisText (e.g., you may want to retrieve the text currently in the specified cell and display that to be edited). If TECT _text contains a null handle, the VisText will start out blank.

To provide this default text, allocate a global memory block, copy the string into the beginning of the block, and put the block's handle in TECT _text . (You should not store anything else in this block, because the block will be freed by the handler for MSG_TABLE_DONE_EDIT_CELL_TEXT.) If a valid handle was not passed in TECT _text , it allocates a global memory block for the VisText to write to; otherwise, the VisText will simply write to the block specified in TECT _text (overwriting the original text).

Code Display 5-3 Handling MSG_TABLE_START_EDIT_CELL_TEXT

@method CoffeeTableClass, MSG_TABLE_START_EDIT_CELL_TEXT
{
    char *cellData, *textPtr;
    word cArrayIndex, size;
    cArrayIndex = (params.TECT_cells.TCR_start.TCL_row * TABLE_COLS) +
        params.TECT_cells.TCR_start.TCL_column;
    /* The superclass will free this block that we allocate here. */
    params.TECT_text = MemAlloc(CELL_DATA_LENGTH, HF_SWAPABLE, HAF_LOCK);
    textPtr = MemDeref(params.TECT_text);
    /* Retrieve the current data from the cell and stuff it in the memory block. */
    pself = ObjDerefVis(oself);
    MemLock(OptrToHandle(pself->CTI_chunkArray);
    cellData = ChunkArrayElementToPtr((pself->CTI_chunkArray),
                cArrayIndex, &size);
    strcpy(textPtr, cellData);
    MemUnlock(OptrToHandle(pself->CTI_chunkArray));
    params.TECT_length = 0;				/* Zero indicates null-termination. */
    @callsuper();
}

When the user has finished entering text, the VisText object sends the Table object MSG_TABLE_DONE_EDIT_CELL_TEXT. This message takes one argument, a TableEditCellTextParams structure. The TECT _text field will contain the handle of a global memory block, containing the text the user has entered.The application should intercept the message, so it can store the new text; afterwards, it should call the default handler, which will free the memory block specified by TECT _text and perform other bookkeeping chores. The Table object will automatically send an appropriate MSG_TABLE_QUERY_DRAW , so the cell will be redrawn with the new contents.

Note that the Table object supports allowing the user to edit several cells at once. If you choose to allow this, you must decide what the functionality is; that is, in your handler for MSG_TABLE_START_EDIT_CELL_TEXT, you must decide what cell's text to display in the VisText (e.g. the first cell's text, or a composite of all the selected cells's text); and in your handler for MSG_TABLE_DONE_EDIT_CELL_TEXT, you must decide where and how to store the entered text (e.g. whether to overwrite all the edited cells with the same text, divide the entered text amongst the cells, etc.).

Code Display 5-4 Handling MSG_TABLE_DONE_EDIT_CELL_TEXT

@method CoffeeTableClass, MSG_TABLE_DONE_EDIT_CELL_TEXT
{
    char *cellData, *textPtr;
    word cArrayIndex, size;
    cArrayIndex = (params.TECT_cells.TCR_start.TCL_row * TABLE_COLS) +
        params.TECT_cells.TCR_start.TCL_column;
    textPtr = MemLock(params.TECT_text);
    /* Make sure we're null terminated ... */
    textPtr[CELL_DATA_LENGTH -1]; = (char)0;
    /* Lock down the cell and copy the string to it. */
    MemLock(OptrToHandle(pself->CTI_chunkArray));
    cellData = ChunkArrayElementToPtr((pself->CTI_chunkArray),
        cArrayIndex, &size);
    strcpy(cellData, textPtr);
    MemUnlock(OptrToHandle(pself->CTI_chunkArray));
    @callsuper();
}

MSG_TABLE_START_EDIT_CELL_TEXT

void	MSG_TABLE_START_EDIT_CELL_TEXT(
        TableEditCellTextParams			params);

The Table object sends itself this message when the user starts editing a cell. The default handler brings up a VisText for the user to enter text. You should intercept this message, so you can look up and provide the cell's current contents; that way, the contents will be put in the VisText for the user to edit.

Source: Any Table object.

Destination: The Table sends this message to itself.

Parameters: params. TECT _cells
 This field specifies the cell or cells the user is editing.

params. TECT _text
This field contains the handle of a global memory block, containing the text to be displayed in the VisText when it is brought up. When this message is sent, params. TECT _text contains a null handle; your subclass's handler may allocate a global memory block, copy the text into that block, and write the handle here. (Note that the VisText will write to the block you specify, so it should not contain any other data.)
params. TECT _length
This is the length (in bytes) of the string in the block specified by params. TECT _text . If this field is zero, the string is null-terminated.

Return: Nothing. (If params. TECT _text contained a null handle, the default handler will allocate a block.)

Structures: TableEditCellTextParams (see Both MSG_TABLE_START_EDIT_CELL_TEXT and MSG_TABLE_DONE_EDIT_CELL_TEXT use a special structure, the TableEditCellTextParams structure; the structure's fields have slightly different meanings for each message. This structure has the following defi ).

Interception: You should intercept this message to provide the text for the VisText to present for editing (which will usually be the current contents of the cell being edited). After this, you should call the default handler with @callsuper , so the VisText can be created.

Warnings: If you put a memory handle in params. TECT _text , that block will be written to by the VisText , and freed by the handler for MSG_TABLE_DONE_EDIT_CELL_TEXT; so make sure it doesn't contain any other data.

MSG_TABLE_DONE_EDIT_CELL_TEXT

void	MSG_TABLE_DONE_EDIT_CELL_TEXT(
        TableEditCellTextParams				params);

The VisText object sends this message to the Table object after the user has finished entering data. The application should intercept this message to copy data into its storage location; you should also make sure to call the default handler, which will remove the VisText and perform other bookkeeping chores.

Source: This message is sent by a VisText object used by the Table for text editing.

Destination: The VisText sends this message to its parent Table .

Parameters: params. TECT _cells
 This field specifies the cell or cells the user has edited.

params. TECT _text
This field contains the handle of a global memory block, containing the text the user entered in the VisText . The default handler will free this block.
params. TECT _length
This is the length (in bytes) of the string in the block specified by params. TECT _text . If this field is zero, the string is null-terminated.

Return: Nothing. (The default handler will free the block specified by params. TECT _text .)

Structures: TableEditCellTextParams (see Both MSG_TABLE_START_EDIT_CELL_TEXT and MSG_TABLE_DONE_EDIT_CELL_TEXT use a special structure, the TableEditCellTextParams structure; the structure's fields have slightly different meanings for each message. This structure has the following defi ).

Interception: You should intercept this message, so you can copy the new data into your application's storage. Afterwards, you should make sure to call the default handler with @callsuper .

MSG_TABLE_STOP_EDIT_CELL_TEXT

void	MSG_TABLE_STOP_EDIT_CELL_TEXT();

The VisText object sends this message to the Table if the user clicks outside the cell while editing data. This cancels the edit: the Table returns to its normal mode and the user's edits are discarded.

Source: This message is sent by a VisText object used by the Table for text editing.

Destination: The VisText sends this message to its parent Table .

Parameters: None.

Return: Nothing.

Interception: If your program wants to treat a tap outside the edited cell as a signal that the edit is finished, not cancelled, then get the (abandoned) text that the user was editing from the VisText child of the Table. The chunk handle of this VisText child is stored in TI_textObj. You can build an optr to the VisText by using:

textObj = ConstructOptr(OptrToHandle(@YourTable),
                            pself->TI_textObj);

To retrieve the text, use:

@call textObj::MSG_VIS_TEXT_GET_ALL_PTR(buffer);

where "buffer" has been declared as "char buffer[]".

Once you've retrieved the text, the remainder of your method should just save away this data in whatever data structure you've built to hold your table's data -- just like in the method for MSG_TABLE_DONE_EDIT_CELL_TEXT .

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Selecting Cells | Next: 4.4 Dragging and Dropping


Dragging and Dropping

The Table Objects: 4.4 Using a Table Object: Dragging and Dropping

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Editing Cells | Next: 4.5 Custom Scrolling Behavior
TableDragDropCompleteParams, TableDragDropCell, TableDragDropFlags, MSG_TABLE_DRAG_DROP_COMPLETE

The Table object supports the standard GEOS drag-drop functionality. It allows the user to select one or more cells, then drag their contents to other cells in the Table . However, the Table object does not actually keep track of the contents of the cells. Instead, it detects which cells the user dragged and where the user dropped them, and it notifies the application with MSG_TABLE_DRAG_DROP_COMPLETE . The application should intercept this message and use this information to copy data to its appropriate places in its storage.

The Table is put in drag-and-drop mode when the TableFlags bit TF_INTERNAL_DRAG_DROP is set (for example through the sending of MSG_TABLE_SET_FLAGS ). If the Table's column flags contain TCF_HOLD_SELECT, holding for the specified period of time to initiate a "hold" will also set the TF_INTERNAL_DRAG_DROP flag. When the Table is in drag-and-drop mode, the user can copy a cell's contents to another cell by clicking on one cell, dragging the pointer to the other cell, and releasing. If the TableFlags bit TF_EXIT_DRAG_DROP_UPON_COMPLETION is set, the Table will automatically clear TF_INTERNAL_DRAG_DROP after every drag-and-drop.

The Table object sends MSG_TABLE_DRAG_DROP_COMPLETE when the user finishes a drag-and-drop operation. This message passes one argument, a TableDragDropCompleteParams structure. This structure has the following definition:

typedef struct {
    TableDragDropCell				TDDCP_dragDrop;
    TableDragDropFlags				TDDCP_flags;
} TableDragDropCompleteParams;
TDDCP _dragDrop
This structure specifies which cells are being copied, and where they will be copied to. The TableDragDropCell structure is described below.
TDDCP _flags
This field is a record of TableDragDropFlags flags, described below.

The cells being dragged, and the location to which they are being dropped, are specified with a TableDragDropCell structure. This structure has the following definition:

typedef struct {
    TableCellRange			TDDC_from;
    TableCellRange			TDDC_to;
} TableDragDropCell;
TDDC _from
The cell or cells being dragged.
TDDC _to
The cell or cells on which TDDC _from is being dropped.

When MSG_TABLE_DRAG_DROP_COMPLETE is sent, the TDDCP _flags field of the passed TableDragDropCompleteParams is blank. If you intercept this message, you can change this field, setting any TableDragDropFlags bits in this field. Currently, only one flag is defined:

TDDF_DONT_RESCAN_CELLS
If this flag is clear, the Table will redraw all visible cells after a drag-drop operation. If it is set, it will not redraw any cells. (You may wish to set this flag, then force redrawing of a few affected cells with an appropriate MSG_TABLE_REDRAW... message; these messages are described on The Table automatically sends MSG_TABLE_QUERY_DRAW when it knows a part of the Table may be inaccurate, e.g. when the user has edited a cell. You can also instruct the Table to redraw part or all of itself by sending it one of the MSG_TABLE_REDR .)

You should intercept the message, and perform any appropriate data operations, e.g. copying the data from the "dragged" cell to the location where it was "dropped".

If the TF_EXIT_DRAG_DROP_UPON_COMPLETION flag is set, drag and drop mode will be exited upon receipt of MSG_TABLE_DRAG_DROP_COMPLETE . Otherwise, you will need to determine when you wish to exit drag and drop mode and clear the TF_INTERNAL_DRAG_DROP flag manually by sending the Table MSG_TABLE_SET_FLAGS . (You might wish to do this if you want to drag and drop to multiple locations, for example.) Because you are modifying an internal flag in this process, this approach is highly discouraged.

Code Display 5-5 Implementing Drag and Drop Mode

/* This message is sent to the Table whenever it completes a drag and drop
 * operation (by releasing the pen). In your handler, determine the starting and
 * ending point of the operation and copy the data from one location to the other.
 */
@method CoffeeTableClass, MSG_TABLE_DRAG_DROP_COMPLETE
{
    char *fromData, *toData;
    word fromIndex, toIndex, size;
    /* We are storing our data in a chunk array (not shown). We obtain a
     * linear index into this data based on the cell row and column. */
    fromIndex =
    (cellFromTo.TDDCP_dragDrop.TDDC_from.TCR_start.TCL_row * TABLE_COLS) +
    cellFromTo.TDDCP_dragDrop.TDDC_from.TCR_start.TCL_column;
    toIndex =
    (cellFromTo.TDDCP_dragDrop.TDDC_to.TCR_start.TCL_row * TABLE_COLS) +
    cellFromTo.TDDCP_dragDrop.TDDC_to.TCR_start.TCL_column;
    /* Check if this drag and drop is from within the same Table. */
    if (pself->TI_tableFlags & TF_INTERNAL_DRAG_DROP) {
    MemLock(OptrToHandle(pself->CTI_chunkArray);
    fromData = ChunkArrayElementToPtr((pself->CTI_chunkArray),
                    fromIndex, &size);
    toData = ChunkArrayElementToPtr((pself->CTI_chunkArray),
                    toIndex, &Size);
        /* Copy the actual data. *
    strcpy(toData, fromData);
    MemUnlock(OptrToHandle(pself->CTI_chunkArray));
    }
    /* Call the superclass. This will also exit the Table from drag and drop mode
     * if the TF_EXIT_DRAG_DROP_UPON_COMPLETION flag is set. */
    @callsuper();
}

MSG_TABLE_DRAG_DROP_COMPLETE

void	MSG_TABLE_DRAG_DROP_COMPLETE(
        TableDragDropCompleteParams					cellFromTo);

The Table object sends this message to itself when the user has finished performing a drag-and-drop operation. Application writers who wish to perform operations on the data being dragged and dropped must intercept this message.

Source: A TableClass object.

Destination: The Table object sends this message to itself.

Parameters: cellFromTo. TDDCP _dragDrop
 This field specifies what cells are being dragged, and where they are being dropped.

cellFromTo.TDDCP_flags
This field is initially blank. If you intercept the message, you can change the flags before calling the default handler.

Return: Nothing.

Structures: The arguments are passed in a TableDragDropCompleteParams structure (described on The Table object sends MSG_TABLE_DRAG_DROP_COMPLETE when the user finishes a drag-and-drop operation. This message passes one argument, a TableDragDropCompleteParams structure. This structure has the following definition: ).

Interception: Must intercept to perform data operations necessary to implement the drag-and-drop. Afterwards, make sure to call the default handler with @callsuper . The default handler will cause the entire visible portion of the Table to be redrawn, unless you have set the TDDF_DONT_RESCAN_CELLS bit in cellFromTo.TDDCP_flags .

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Editing Cells | Next: 4.5 Custom Scrolling Behavior


Custom Scrolling Behavior

The Table Objects: 4.5 Using a Table Object: Custom Scrolling Behavior

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Dragging and Dropping | Next: 4.6 Handling Locator Searches
MSG_TABLE_SCROLL, MSG_TABLE_SCROLL_TO_ROW, MSG_TABLE_SCROLL_SINGLE_UP, MSG_TABLE_SCROLL_SINGLE_DOWN

Auto-scrolling (enabled by adding the TF_ENABLE_AUTO_SCROLLING to TI_ tableFlags ) allows the user to scroll within a Table by clicking within a Table and dragging above or below the current Table bounds. The Table will scroll in the direction that the pen was dragged. Auto-scrolling does not provide any UI to scroll the Table, however. If you want scroll buttons, for example, you will need to take the following steps:

  • Make sure you've declared a TableViewClass object. Set its TVI_mainTable field to the Table you wish to scroll.
  • Mark the Table that you want scrolling to occur with the TF_MAIN_TABLE flag.

MSG_TABLE_SCROLL is a generic scrolling message that implements a particular scroll operation (passed in the message's TableScrollType parameter). Possible Table scroll types are:

TST_SCROLL_PAGE_UP
TST_SCROLL_PAGE_DOWN
TST_SCROLL_UP
TST_SCROLL_DOWN
TST_SCROLL_TOP
TST_SCROLL_DOWN

TST_SCROLL_UP scrolls the Table up one row. Similarly, TST_SCROLL_DOWN scrolls the Table down one row.

You may send MSG_TABLE_SCROLL_TO_ROW if you want the Table to scroll to a particular (zero-based) row. The Table will scroll so as to make that row the first visible row, if possible. MSG_TABLE_SCROLL_SINGLE_UP scrolls the Table up one row. Similarly, MSG_TABLE_SCROLL_SINGLE_DOWN scrolls the Table down one row.

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Dragging and Dropping | Next: 4.6 Handling Locator Searches


Handling Locator Searches

The Table Objects: 4.6 Using a Table Object: Handling Locator Searches

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Custom Scrolling Behavior | Next: 4.7 Table Headings
MSG_TABLE_CHAR_LOCATE, MSG_TABLE_STRING_LOCATE

The Table object may wish to respond to searches on its data. For this purpose, the Table object is designed to interact with a LocatorClass object; the Locator object is a UI object that the user uses to initiate searches on the Table. The Locator object implements this search by sending the Table object one of two messages. You must intercept these messages to provide searching behavior. TableClass provides no default searching behavior.

The Locator object is discussed in detail in LocatorClass ; however, some background on how the Locator works is illustrative to providing the Table`s searching behavior.

The Locator object can be in one of two modes: index mode or text search mode. The Locator object will appear and act differently depending on which mode it is in.

When the Locator object is in index mode, it takes the form of an action bar, shown below.

Tapping on one of the Locator's dyad buttons (`AB', `EF', etc.) will send the Table object MSG_TABLE_CHAR_LOCATE , passing it the letter to search. Repeatedly clicking on a dyad button will cycle through the two letters to perform the search. For example, clicking once on the `AB' dyad button will send the Table MSG_TABLE_CHAR_LOCATE with the character `A.' Clicking again on that button will send the Table MSG_TABLE_CHAR_LOCATE with the character `B.' Clicking yet again on that button will send the Table MSG_TABLE_CHAR_LOCATE with the character `C.'

If you have a Locator linked to your Table, you will want to intercept MSG_TABLE_CHAR_LOCATE to make sure that you can implement searching behavior in index mode.

When the Locator object is in text search mode, the action bar "morphs" into a text search entry field shown below.

Entering text into the text search field of the Locator will send the linked Table object MSG_TABLE_STRING_LOCATE after each keystroke, passing it the current contents of the text entry field. You will want to intercept this message to provide your searching method for when the Locator object is in text mode.

You may find it convenient to write a generic searching routine that your message handlers for both MSG_TABLE_CHAR_LOCATE and MSG_TABLE_STRING_LOCATE may call. This searching routine will likely want to perform a case-insensitive search on your Table. You may want to use a routine such as LocalCmpStringsNoCase() for this purpose. Note that you must still provide separate message handlers for each of these messages even if you provide a generic searching routine.

Code Display 5-6 Implementing a Search

/* This code implements a single character search initiated by a Locator object. */
@method MyTableClass, MSG_TABLE_CHAR_LOCATE
{
    char *data;			/* Pointer to the table data array. */
    word index =0;			/* A linear index into the Table data array. */
    /* We'll want to send MSG_TABLE_SET_CURRENT_SELECTION, so we'll want
     * to pass a TableCellRange.
     */
    TableCellRange	selection;
    Boolean searchResult;			/* Reports the success or failure of the search. */
    /* We initialize the selection to start searching from the beginning of the
     * Table. */
    selection.TCR_start.TCL_row					= 0;
    selection.TCR_start.TCL_column					= 0;
    selection.TCR_end.TCL_row					= 0;
    selection.TCR_end.TCL_column					= 0;
    /* Lock down our data block. */
    MemLock(OptrToHandle(@cellDataArray));
    /* Perform a case-insensitive search on the data in the array. We do this by
     * searching linearly, multiplying by the total number of columns in the
     * Table, so that we only search the first column. (We could have also
     * performed our search cell by cell in a linear fashion; in that case we
     * would not have multiplied the index by TABLE_COLS in the `step' section of
     * the while statement.) The data in this case is assumed to be stored in a      * linear chunk array. */
    do {
    data = ChunkArrayElementToPtr(@cellDataArray, index, 1);
    } while(
    (LocalCmpStringsNoCase(data, searchText, 1)) &&
    ((++index * TABLE_COLS) < (TABLE_COLS * TABLEROWS)));
    /* We've finished searching; we can free the data block. */
    MemUnlock(OptrToHandle(@cellDataArray));
    /* Determine if search found a match. */
    if (index >= (TABLE_COLS * TABLE_ROWS))
    {
    searchResult = TRUE;			/* Indicates no match was found. */
    }
    else
    {
    /* If successful, set selection to "current" index cell. */
    selection->TCR_start.TCL_row 				= index / TABLE_COLS;
    selection->TCR_start.TCL_column 				= index % TABLE_COLS;
    selection->TCR_end.TCL_row				= selection->TCR_start.TCL_row;
    selection->TCR_end.TCL_column				= selection->TCR_start.TCL_column;
    @call self::MSG_TABLE_SET_CURRENT_SELECTION(selection);
    searchResult = FALSE;			/* Indicates that we found a match. */
    }
    return(searchResult);				/* We don't call the superclass. Default behavior
                 * is to return TRUE indicating no matches were
                 * found. */
}

MSG_TABLE_CHAR_LOCATE

Boolean	MSG_TABLE_CHAR_LOCATE(
        char		searchChar);

This message is sent to the TableClass object by the Locator object instructs a Locator object (in index mode) to locate the entered character within the TableClass object that is in the Locator's LI_ destination field. It does this by sending the Table object MSG_TABLE_CHAR_LOCATE , passing it the search character. You will want to intercept that message within your subclass of TableClass to implement your search criteria.

When in index mode, this message is sent with a single index letter (e.g. `A', or `B' in the "AB" dyad) of the tapped dyad button. Repeatedly tapping on a dyad button will cycle through the indexed letters, sending out this message a separate time for each tap (first with `A,' then with `B,' and then with `A' again). For this reason, make sure that code that implements this message is not run under the UI thread. (Doing so may result in bothersome screen "glitches" as the Table scrolls first to the `A' entries, then to the `B' entries, etc.

Source: The Locator object.

Destination: The Locator's linked Table object.

Parameters: searchChar Character to search. Passed from one of the Locator's dyad buttons.

Return: Non-zero if the search failed. Note that if you do not write a search method for TableClass , this message will always return non-zero.

MSG_TABLE_STRING_LOCATE

Boolean	MSG_TABLE_STRING_LOCATE(
        char		*text);

This message instructs a Locator object (in text mode) to locate the entered string within the TableClass object that is in the Locator's LI_ destination field. It does this by sending the Table object MSG_TABLE_STRING_LOCATE , passing it the pointer to the search string. You will want to intercept that message within your subclass of TableClass to implement your search criteria.

The Locator object enters text search mode whenever the user types a printable character on the keyboard; in that case, the locateString contains the character typed and a search (with this message) is performed. I.e. a search is performed after each successive character is entered. If the search is unsuccessful, the character will not appear.

Source: The Locator object.

Destination: A Locator object.

Parameters: text Pointer to the null-terminated search string to pass to MSG_TABLE_STRING_LOCATE .

Return: Non-zero if the search failed. Note that if you do not write a search method for TableClass, this message will always return non-zero.

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Custom Scrolling Behavior | Next: 4.7 Table Headings


Table Headings

The Table Objects: 4.7 Using a Table Object: Table Headings

| Up: GEOS SDK TechDocs | Up | Prev: 4.6 Handling Locator Searches | Next: 4.8 Changing Column Definitions

TableClass does not automatically support the drawing of Table headings. However, it is easy to implement this yourself.

To create a Table with headings, simply create two different Table objects: one for the body of the Table , and one for the headings. Make sure the two Tables have the same left and right margins, and make sure their columns are of the same width; but other than that, you can set up the two tables completely differently. For example, the upper Table (which would have only one line) might well not be user-editable or selectable.

Group the two Tables below the single TableContent object. Once you've set up the two Tables, you may want to experiment with headings and borders to get the look you want. Notice that the two Table s scroll independently. This means that when a user scrolls the lower Table , the same headings will always be visible at the top of the columns. You will want to add the TF_MAIN_TABLE flag to your main Table object so that it does not attempt to scroll the heading table.

Code Display 5-7 Adding a Header Table

/* A Header is simply a Table, though it is usually only one row. */
@object CoffeeTableClass, CoffeeTableHeader = {
    TI_rows = 1;			/* Because this is a header, only one row is needed. */
    TI_columns = MY_TABLE_COLS;				/* Make sure you have the same number of columns
                 * as in the main table. */
    TI_visibleRows = 1;
    TI_rowHeight = 18;			/* Your row height may be different than the row height
             * of the main table. */
    TI_columnDefinitions = @CoffeeTableHeaderColumnDefs;
    TI_borderFlags = TBF_BOX_BORDERS;
    CTI_chunkArray = @CoffeeTableHeaderDataArray;
}
/* The column definition widths should match those of the main table. Headers
 * should not contain any TableColumnFlags, however. Set these to zero. */
@chunk TableColumnDefinition CoffeeTableHeaderColumnDefs[] = {
    0, 25,
    0, 100,
    0, 45,
    0, 70};
/* The Table header data array contains the strings to display in the Table
 * header. */
@chunkArray CellDataStruct CoffeeTableHeaderDataArray = {
    "#","Description","Type","$/lb."
};
| Up: GEOS SDK TechDocs | Up | Prev: 4.6 Handling Locator Searches | Next: 4.8 Changing Column Definitions


Changing Column Definitions

The Table Objects: 4.8 Using a Table Object: Changing Column Definitions

| Up: GEOS SDK TechDocs | Up | Prev: 4.7 Table Headings | Next: 5 TableContentClass

You may wish in exceptional circumstances to display different columns than the ones you statically define using TI_ columnDefinitions . In that case, you may want to define alternate column definitions and switch which ones you use at appropriate times in your application. There is a fair amount of overhead in doing this yourself--there currently exists no API to take care of this behavior--so you should make sure you want to take that approach.

The following steps are recommended to get this behavior:

  • Define separate column definitions for each group of columns you want displayed. Each column definition should reside in its own separate chunk. Make sure that each set of column definitions resides within the resource block of the table object. (Because TI_columnDefinitions is a chunk handle, it cannot reference chunks outside of the resource.)
  • Create an instance field in your Table subclass to index which column definitions you are currently using. You will use this instance field to determine what chunk array of data to use.
  • Create separate chunk arrays to hold your individual sets of data and an instance field to hold the correct pointer to that chunk array. Alternatively, you could have one chunk array hold both sets of data and define an offset into the second set of data.
  • Define a message in your Table subclass to change the column definitions, passing it an index number of the column definitions you want to use. Set TI_ columnDefinitions to the appropriate column definitions in your handler for this message. Also change your index number to indicate the new column definitions being used and the pointer to the chunk array of data.
  • Redraw the Table by sending MSG_TABLE_REDRAW_TABLE from this handler. This will dispatch MSG_TABLE_QUERY_DRAW to each of the affected cells.
  • Handle MSG_TABLE_QUERY_DRAW as usual.

Code Display 5-8 Dynamically Changing Column Definitions

/* First, make sure you create a message to change the column definitions and an
 * instance field to indicate which column definitions we are using. */
@class MyTableClass, TableClass;
    @message void MSG_MY_TABLE_CHANGE_COLUMN_DEFINITIONS(int columnIndex);
    @instance int 			MTI_columnIndex = 0;
    @instance optr			MTI_chunkArray = NullOptr;
@endc;
@classdecl MyTableClass;
/* Define your column definitions. Make sure that each set of column definitions
 * adds up to 240 pixels across. Note that if you want headers for each of these
 * column definitions, you will need to handle that yourself.*/
#define MY_COLUMN_FLAGS (TCF_START_SELECT | TCF_DOUBLE_SELECT | TCF_DRAG_SELECT |
            TCF_HOLD_SELECT | TRIT_CELL )
@chunk TableColumnDefinition ColumnSet1[] = {
    MY_COLUMN_FLAGS, 25,
    MY_COLUMN_FLAGS, 100,
    MY_COLUMN_FLAGS, 45,
    MY_COLUMN_FLAGS, 70};
@chunk TableColumnDefinition ColumnSet2[] = {
    MY_COLUMN_FLAGS, 25,
    MY_COLUMN_FLAGS, 100,
    MY_COLUMN_FLAGS, 115};
/* Define our cell data in two separate chunk arrays. */
@chunkArray CellDataStruct myMainData = {
    "0","Viennese","DARK", "$ 8.75",
    "1","Italian","DARK","$ 8.75",
    "2","French","DARK","$ 9.25",
    "3","Sumatra","RICH","$ 8.95"};
@chunkArray CellDataStruct mySecondaryData = {
    "0","Mr. Cringle","1412 Dowdy Ln.",
    "1","Ms. Holworth","23 Abercrombie",
    "2","Jeeves","10 Downing St.",
    "3","Kahlia","345 Cedar St."};
/* Define your method for MSG_MY_TABLE_CHANGE_COLUMN_DEFINITIONS. */
@method MyTableClass, MSG_MY_TABLE_CHANGE_COLUMN_DEFINITIONS
{
    switch(columnIndex) {
    case COLUMN_SET_1:
        pself->TI_columnDefinitions = OptrToHandle(@ColumnSet1);
        pself->MTI_chunkArray = @myMaindata;
        break;
    case COLUMN_SET_2:
        pself->TI_columnDefinitions = OptrToHandle(@ColumnSet2);
        pself->MTI_chunkArray = @mySecondaryData;
        break;
    default:
        break;
    }
    pself->MTI_columnIndex = columnIndex;
    @call self::MSG_TABLE_REDRAW_TABLE;
}
/* Handle MSG_TABLE_QUERY_DRAW. Because we set up the MTI_chunkArray instance
 * field in MSG_MY_TABLE_CHANGE_COLUMN_DEFINITIONS, nothing special is done here.
 */
@method MyTableClass, MSG_TABLE_QUERY_DRAW
{
    char *data;
    word cArrayIndex, offset, size;
    cArrayIndex = (location.TCL_row * TABLE_COLS) + location.TCL_column;
    MemLock(OptrToHandle(pself->MTI_chunkArray));
    data = ChunkArrayElementToPtr((pself->MTI_chunkArray),
                cArrayIndex, &size);
    GrDrawTextAtCP(gstate, data, 0);
    MemUnlock(OptrToHandle(pself->MTI_chunkArray);
}
| Up: GEOS SDK TechDocs | Up | Prev: 4.7 Table Headings | Next: 5 TableContentClass


TableContentClass

The Table Objects: 5 TableContentClass

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Using a Table Object | Next: 6 LocatorClass

A TableContentClass object manages TableClass objects. The TableContent is a visual grouping object (subclassed off of VisContentClass ) and therefore manages the visual implementation of its children. A TableContent object will contain just two types of children: usually a single TableClass object, possibly an additional TableClass object acting as a set of headings, and a LocatorClass object. ( LocatorClass is discussed later in the chapter.)

The TableContent object manages the dispatch of keyboard events to its Table anTableView object, and acts as the bridge between the generic and visual object hierarchies. The hierarchy of this arrangement is shown below:

TableContent Instance Data

Altering TableContent Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Using a Table Object | Next: 6 LocatorClass


TableContent Instance Data

The Table Objects: 5.1 TableContentClass: TableContent Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 5 TableContentClass | Next: 5.2 Altering TableContent Instance Data

TableContentClass is a subclass of VisContentClass . As such, it inherits instance data and messages from that class. This section describes the instance data fields defined for TableContentClass . It also describes how to set, examine and change those fields which are of interest to developers.

Code Display 5-9 TableContentClass instance data

/*
 * TableContentClass is a subclass of VisContentClass.
 */
@default 		VCI_geoAttrs = 		(@default | VCGA_ORIENT_CHILDREN_VERTICALLY);
@instance 		byte		TCI_kbdSend = 255;
@instance 		optr		TCI_exclDestination = NullOptr;
@instance		word		TCI_childSpacing = 0;
@instance 		word		TCI_childWrapSpacing = 0;

TCI_ kbdSend is an internal field that determines whether keyboard messages will be sent to the TableContent's complement of children. (This is the default behavior.) If you want to send any keyboard events to any of the TableContent's children, this flag must be "on." You should not set or modify this flag directly; it should only be altered by sending the TableContent object MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_ON and MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_OFF.

TCI_ exclDestination is an internal field that determines which specific child object is sent keyboard messages. If TCI_ exclDestination is non-null, then keyboard messages will not be broadcast to all of the TableContent's children, but only to that specific object. This destination should only be altered by sending the TableContent MSG_TABLE_CONTENT_GRAB_KBD_EXCLUSIVE or MSG_TABLE_CONTENT_RELEASE_KBD_EXCLUSIVE (to set the output to null); the field should not be set or modified directly.

TCI_ childSpacing sets the child spacing (in points) for the Table and Locator children of this TableContent object. You should not need to manipulate this field.

TCI_ childWrapSpacing sets the child spacing for children that are forced to wrap (in points) for the children of the TableContent object. You should not need to manipulate this field.

| Up: GEOS SDK TechDocs | Up | Prev: 5 TableContentClass | Next: 5.2 Altering TableContent Instance Data


Altering TableContent Instance Data

The Table Objects: 5.2 TableContentClass: Altering TableContent Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 TableContent Instance Data | Next: 6 LocatorClass
MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_ON, MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_OFF, MSG_TABLE_CONTENT_GRAB_KBD_EXCLUSIVE, MSG_TABLE_CONTENT_RELEASE_KBD_EXCLUSIVE

By default, the TableContent broadcasts keyboard events to its children. If this behavior is not desired, you can send the TableContent MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_OFF . Sending the TableContent MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_ON turns keyboard event broadcasting back on.

Keyboard event broadcasting must be on for any child object to receive any keyboard events, even if a child object attempts to grabs the keyboard exclusive on its own. A TableContent's child can grab the keyboard exclusive by sending MSG_TABLE_CONTENT_GRAB_KBD_EXCLUSIVE to its parent TableContent object, passing it the optr of the child object (usually itself) which should be given the keyboard exclusive. If keyboard event broadcasting is not on, or if another child object already has the exclusive, the grab will not be successful.

The child object should also make sure to send its parent MSG_TABLE_CONTENT_RELEASE_KBD_EXCLUSIVE when it can release the keyboard exclusive.

MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_ON

Boolean	MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_ON();

This message instructs the TableContent object to allow keyboard messages to be sent to its children. (This must be enabled if any child object is to grab the keyboard exclusive.) It does this by modifying the TableContent's TCI_ kbdSend instance field. Do not modify this field yourself.

Source: Unrestricted.

Destination: A TableContent object.

Parameters: None.

Return: true (non-zero ) if the TableContent object was not able to turn on keyboard dispatching to its children; false (zero) otherwise.

MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_OFF

Boolean	MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_OFF();

This message instructs the TableContent object to disallow keyboard messages to be sent to its children. (If any child object currently has the keyboard exclusive, this message will not succeed in turning off keyboard event broadcasting.) The TableContent modifies this behavior by modifying the TableContent's TCI_ kbdSend instance field. Do not modify this field yourself.

Source: Unrestricted.

Destination: A TableContent object.

Parameters: None.

Return: true (non-zero) if the TableContent object was not able to turn off keyboard dispatching to its children; false (zero) otherwise.

MSG_TABLE_CONTENT_GRAB_KBD_EXCLUSIVE

Boolean	MSG_TABLE_CONTENT_GRAB_KBD_EXCLUSIVE(
        optr		obj);

This message instructs the TableContent object to give the keyboard grab to the child object whose optr is passed in the message. It does this by modifying the TableContent's TCI_ exclDestination modify this field yourself. Keyboard event broadcasting must be "on" for this grab to succeed.

Source: One of the TableContent's children

Destination: A TableContent object.

Parameters: obj Object to receive the TableContent's keyboard exclusive.

Return: true (non-zero) if the child object was not able to grab the TableContent's keyboard exclusive; false (zero) otherwise.

MSG_TABLE_CONTENT_RELEASE_KBD_EXCLUSIVE

Boolean	MSG_TABLE_CONTENT_RELEASE_KBD_EXCLUSIVE(
        optr		obj);

This message instructs the TableContent object to release the keyboard grab from the child object whose optr is passed in the message. Only a child object which currently has the grab can release it. It does this by modifying the TableContent's TCI _exclDestination instance field, setting it to null. Do not modify this field yourself.

Source: Unrestricted.

Destination: A TableContent object.

Parameters: obj Object releasing the TableContent's keyboard exclusive.

Return: true (non-zero) if the child object was not able to release the TableContent's keyboard exclusive; false (zero) otherwise.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 TableContent Instance Data | Next: 6 LocatorClass


LocatorClass

The Table Objects: 6 LocatorClass

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 TableContentClass | Next: 6.1 Index Mode

The LocatorClass is a subclass of the TableClass . Locator objects do not, however, behave like other Table objects. The Locator object is a specific object designed to be used in tandem with a linked Table object. An action taken on the Locator object will perform a searching action on the linked Table object. In most cases, you will simply want to include a Locator object in your application and link it to your Table. (You implement the actual searching algorithms in your Table object.) You do not need to subclass or add any behavior to the Locator object.

Each Table-Locator pair is placed within a parent TableContent. In the current version of GEOS, multiple tables may not appear under a TableContent (excepting, of course, that LocatorClass is actually a subclass of TableClass itself). The following information mirrors information appearing in Handling Locator Searches . Consult that section for information about implementing a search within your Table object. The information provided here is solely for completeness' sake.

Index Mode

Text Mode

LocatorClass Instance Data

Locator Actions

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 TableContentClass | Next: 6.1 Index Mode


Index Mode

The Table Objects: 6.1 LocatorClass: Index Mode

| Up: GEOS SDK TechDocs | Up | Prev: 6 LocatorClass | Next: 6.2 Text Mode
MSG_LOCATOR_DO_CHAR_LOCATE

The Locator object can be in one of two modes: index mode or text search mode. The Locator object will appear and act differently depending on it operating mode.

When the Locator object is in index mode, it takes the form of an action bar, shown below:

Tapping on one of the Locator's dyad buttons (`AB', `EF', etc.) sends MSG_LOCATOR_DO_CHAR_LOCATE to the Locator object itself. The Locator responds to this request by sending MSG_TABLE_CHAR_LOCATE to its linked Table. Intercept that message in TableClass to provide your searching behavior.

Repeatedly clicking on a dyad button will cycle through the two letters of the dyad to perform the search. For example, clicking once on the `AB' dyad button will send the aforementioned messages with a character argument of `A.' Clicking again on that button will send these messages with an argument of `B' and clicking yet again on that button will send these messages with an argument of `A' again.

You will probably not want to implement any searching behavior in your Locator object; it is sufficient merely to include the Locator object and intercept MSG_TABLE_CHAR_LOCATE in the Table object itself. This is discussed in Handling Locator Searches .

MSG_LOCATOR_DO_CHAR_LOCATE

Boolean	MSG_LOCATOR_DO_CHAR_LOCATE(
        char		searchChar);

This message instructs a Locator object (in index mode) to locate the entered character within the TableClass object that is in the Locator's LI_ destination field. It does this by sending the Table object MSG_TABLE_CHAR_LOCATE , passing it the search character. You will want to intercept that message within your subclass of TableClass to implement your search criteria.

When in index mode, this message is sent with a single index letter (e.g. `A', or `B' in the "AB" dyad) of the tapped dyad button. Repeatedly tapping on a dyad button will cycle through the indexed letters, sending out this message a separate time for each tap (first with `A,' then with `B,' and then with `A' again). For this reason, make sure that code that implements this message is not run under the UI thread. (Doing so may result in bothersome screen "glitches" as the Table scrolls first to the `A' entries, then to the `B' entries, etc.

Source: The Locator object.

Destination: A Locator object.

Parameters: searchChar Character to pass to MSG_TABLE_CHAR_LOCATE .

Return: Non-zero if the search failed. Note that if you do not write a search method for TableClass, this message will always return non-zero.

| Up: GEOS SDK TechDocs | Up | Prev: 6 LocatorClass | Next: 6.2 Text Mode


Text Mode

The Table Objects: 6.2 LocatorClass: Text Mode

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 Index Mode | Next: 6.3 LocatorClass Instance Data
MSG_LOCATOR_DO_STRING_LOCATE

When the Locator object is in text search mode, the action bar "morphs" into a text search entry field shown below:

Entering text into the text search field will send MSG_LOCATOR_DO_STRING_LOCATE to itself. The Locator responds to this message by immediately sending its linked Table MSG_TABLE_STRING_LOCATE . Repeatedly entering valid characters will match succeeding characters.

For example, typing `B' in a Table object will send MSG_TABLE_STRING_LOCATE to the Table with the string "B." Typing `e' after that letter will send MSG_TABLE_STRING_LOCATE passing the string "Be." Whenever the Table can no longer find a match for its character string, MSG_TABLE_STRING_LOCATE will return TRUE (an error condition) and the character will not be allowed into the text entry field. You can complete a text mode search and return to index mode by pressing ENTER.

MSG_LOCATOR_DO_STRING_LOCATE

Boolean	MSG_LOCATOR_DO_STRING_LOCATE(
        char		*locateString);

This message instructs a Locator object (in text mode) to locate the entered string within the TableClass object that is in the Locator's LI_ destination field. It does this by sending the Table object MSG_TABLE_STRING_LOCATE , passing it the pointer to the search string. You will want to intercept that message within your subclass of TableClass to implement your search criteria.

The Locator object enters text search mode whenever the user types a printable character on the keyboard; in that case, the locateString contains the character typed and a search (with this message) is performed. I.e. a search is performed after each successive character is entered. If the search is unsuccessful, the character will not appear.

Source: The Locator object.

Destination: A Locator object.

Parameters: locateString Pointer to the null-terminated search string to pass to MSG_TABLE_STRING_LOCATE .

Return: Non-zero if the search failed. Note that if you do not write a search method for TableClass, this message will always return non-zero.

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 Index Mode | Next: 6.3 LocatorClass Instance Data


LocatorClass Instance Data

The Table Objects: 6.3 LocatorClass: LocatorClass Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 6.2 Text Mode | Next: 6.4 Locator Actions

LocatorClass contains a number of instance fields that are usually not accessed directly. In most cases, these instance fields are maintained by the system. In some cases, you can send messages to alter these instance fields.

Code Display 5-10 LocatorClass Instance Data

/*
 * LocatorClass is a subclass of TableClass. Do not treat it as a
 * normal subclass of TableClass, however. Many of the instance fields of
 * TableClass undergo special treatment in LocatorClass (especially the column
 * definitions). All of the Locator object's instance fields (except
 * LI_destination) are internal and maintained by the Locator object itself.
 */
@instance 		LocatorStateFlags			LI_state;
@instance 		ChunkHandle			LI_actionBarColDefsHandle = 0;
@instance 		ChunkHandle			LI_textSearchColDefsHandle = 0;
@instance 		TableCellLocation			LI_selectionStart = 0;
@instance 		LocatorSelections			LI_selections = 0;
@instance 		optr			LI_destination = 0;

LI_ state is an internal field that is maintained by the LocatorClass object. This field determines whether the Locator has been initialized, and the status of its current state ( index or text search ). Do not alter this field.

LI_ actionBarColDefsHandle is an internal field; this field contains the chunk handle of the column definitions for the Locator object when it is in index mode. Note that the Locator object contains two separate column definitions, unusual for normal Table objects. Do not alter this field.

LI _textSearchColDefsHandle is an internal field; this field contains the chunk handle of the column definitions for the Locator object when it is in text search mode. Do not alter this field.

LI _selectionStart and LI_ selections are internal fields that manage information about the dyad button's states. Do not alter this field.

LI_ destination contains the optr of the object to receive messages (e.g., MSG_LOCATOR_DO_STRING_LOCATE ) sent out by this Locator object. This is usually the sibling Table object that searches are performed on. This field should be set statically.

| Up: GEOS SDK TechDocs | Up | Prev: 6.2 Text Mode | Next: 6.4 Locator Actions


Locator Actions

The Table Objects: 6.4 LocatorClass: Locator Actions

| Up: GEOS SDK TechDocs | Up | Prev: 6.3 LocatorClass Instance Data
MSG_LOCATOR_CHANGE_TO_ACTION_BAR, MSG_LOCATOR_CHANGE_TO_TEXT_SEARCH, MSG_LOCATOR_DO_STRING_LOCATE, MSG_LOCATOR_DO_CHAR_LOCATE

Each time the Locator object changes its mode ( index or text search ) it will send itself either MSG_LOCATOR_CHANGE_TO_ACTION_BAR (if it is changing to index mode) or MSG_LOCATOR_CHANGE_TO_TEXT_SEARCH .

For example, if the Locator is in text search mode, it is desirable to revert to index mode whenever it loses the focus (and loses keyboard input). If the Locator is in text search mode and does lose the focus, the Locator's superclass ( TableClass ) senses this, sending MSG_TABLE_STOP_EDIT_CELL_TEXT to itself. In the handler for that message, the Locator sends itself MSG_LOCATOR_CHANGE_TO_ACTION_BAR .

MSG_LOCATOR_CHANGE_TO_ACTION_BAR

void	MSG_LOCATOR_CHANGE_TO_ACTION_BAR();

This message instructs the Locator object to appear as an "action bar" with a row of dyad buttons ("AB", "CD", etc.). This index mode is distinct from the Locator's other mode, that of a text search field. When the Locator object is in index mode, tapping (once) on a dyad button will send MSG_LOCATOR_DO_STRING_LOCATE with the first letter of the dyad tapped on (e.g. "A" in the `AB' dyad; "E" in the `EF' dyad). Repeatedly tapping on that dyad button will send MSG_LOCATOR_DO_STRING_LOCATE with the succeeding characters. The Locator object automatically "wraps" when it performs a search with its second character. I.e. after `B' is sent in the `AB dyad, `A' is sent again.

Source: The Locator object sends this message to itself.

Destination: A Locator object.

Parameters: None.

Return: Nothing.

Interception: May intercept to receive notification when the Locator object is switching into index mode. Make sure to pass this message on to the superclass, however.

MSG_LOCATOR_CHANGE_TO_TEXT_SEARCH

void	MSG_LOCATOR_CHANGE_TO_TEXT_SEARCH();

This message instructs the Locator object to appear as a text search field. When the Locator object is in text search mode any characters entered into the device will appear within the Locator's text search field. Pressing RETURN will send the text string to the Table with MSG_LOCATOR_DO_STRING_LOCATE.

Source: The Locator object.

Destination: A Locator object.

Parameters: None.

Return: Nothing.

Interception: May intercept to receive notification when the Locator is switching into text search mode. Make sure to pass this message on to the superclass, however.

| Up: GEOS SDK TechDocs | Up | Prev: 6.3 LocatorClass Instance Data


The Text Objects

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 The Text Objects
| One-page Version of this Chapter for Easy Printing

Text support is one of the integral and most important pieces of the GEOS system software. Many text formatting, editing, publishing, and advanced features are built into the system; therefore, many applications will never need to deal directly with these issues. The text features of GeoWrite, for example, are almost entirely implemented within the text object library.

If your geode will display text, receive text input, or provide any type of text formatting features, you should use one of the text objects, either GenText or VisText. (If you only need a single-line non-editable text field, you may be able to use a simple GenGlyph instead.)

The GenText object is primarily a user interface object, designed to communicate directly with the user and accept keyboard input. The VisText object is a much more versatile (and complex) object designed not only to display text but provide complex formatting and WYSIWYG word processing operations.

Both objects make use of common functionality defined within the Text Object Library. Therefore, it is important to be familiar with the features and standards of that object library before using either GenText or VisText. Additionally, if you plan on using your text object to perform any complex style sheets, you should be familiar with the chunk array mechanisms, as these are widely employed in complex text objects.

1 The Text Objects
1.1 Which Object Should I Use?
1.2 How this Chapter is Organized
2 General Text Features
2.1 Input Management and Filters
2.2 Text Flow Through Regions
2.3 Style Sheets
2.4 Undo
2.5 General Import and Export
2.6 Geometry Management
2.7 Embedded Graphics and Characters
2.8 Search and Replace
2.9 Spell-Checking
2.10 Printing
2.11 Text Controller Objects
3 The Text Object Library
3.1 Character Attribute Definitions
3.2 Paragraph Attribute Definitions
3.3 Storage Flags
4 Text Object Chunks
4.1 The Text
4.2 Lines and Fields
4.3 Character Runs
4.4 Paragraph Runs
5 Using VisText
5.1 VisText Features
5.2 VisText States
5.3 VisText VM File Storage
5.4 Text Filters
5.5 Key Functions
5.6 Setting Text Confines
5.7 Output Messages
5.8 Getting Geometry Information
6 Using GenText
6.1 GenText Instance Data
6.2 GenText Basics
7 The Controllers
7.1 Character Attribute Controllers
7.2 Paragraph Attribute Controllers
7.3 Search and Replace and Spell-Checking

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 The Text Objects
| One-page Version of this Chapter for Easy Printing


The Text Objects

The Text Objects: 1 The Text Objects

| Up: GEOS SDK TechDocs | Up | Down | Prev: The Text Objects | Next: 2 General Text Features

Many applications will require some form of typed input from the user. Some will provide text formatting, word processing, or WYSIWYG desktop publishing features, and nearly all will have some text-based information displayed on the screen. Each of these features can be provided easily with either GenText or VisText objects. To use either of these objects, you should make sure to include the text library in your geode.

The VisText and GenText classes have many features in common. Under the hood, both the VisText and GenText objects contain similar instance data, and both are capable of handling messages from the text object library. Which object you use depends on what role you need your text object to assume.

Which Object Should I Use?

How this Chapter is Organized

| Up: GEOS SDK TechDocs | Up | Down | Prev: The Text Objects | Next: 2 General Text Features


Which Object Should I Use?

The Text Objects: 1.1 The Text Objects: Which Object Should I Use?

| Up: GEOS SDK TechDocs | Up | Prev: 1 The Text Objects | Next: 1.2 How this Chapter is Organized

The VisText object is the more complex of the two objects. In general, a VisText object may exhibit any of the many features present within the text object library; the GenText object may only exhibit a subset of this behavior. The GenText object is not a low-level object, however; in most cases, GenTextClass may use most of the messages provided with VisTextClass . It is only in more complex text operations (such as drawing text to regions or incorporating complex graphics into text) that a GenText object is not as useful.

Typically, geodes that want full-featured word processing will use a VisText object (typically within a scrollable GenView and a VisContent). Simple text editing and input (within a database field, for example) are normally done with an editable GenText object. For other cases, it is up to you. There are advantages to either approach. GenText is easier to use, but VisText is more versatile.

Typically, the makeup of your user interface will decide which text object is more appropriate. GenText objects should be attached to the generic tree; VisText objects should instead be attached to the visible tree. Most of the features listed below are described with respect to VisText objects; in most cases, however, they are equally applicable to the GenText.

The main functional difference between a VisText and a GenText object is the ability of VisText to use what is known as the large text model. The large model allows a text object to define flow regions, provide for text to be stored within a VM file, and offers several other rather complex features. This behavior is essential for providing complex text formatting features. It is usually not required for most text needs, however.

The GenText object does not support this text model. In cases where you feel you need to use functionality of the large model, you should use a VisText object within a visible tree. Luckily, most of these capabilities are automatic when using a VisText object. (You will rarely set up a VisText object already using this model, for instance; instead, whenever the VisText is forced to use operations requiring the large model, the application will force it to convert itself.)

| Up: GEOS SDK TechDocs | Up | Prev: 1 The Text Objects | Next: 1.2 How this Chapter is Organized


How this Chapter is Organized

The Text Objects: 1.2 The Text Objects: How this Chapter is Organized

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Which Object Should I Use? | Next: 2 General Text Features

This chapter is organized in a special fashion. Not including this section, the contents of this chapter are:

10.2
A description of some of the more powerful features of the text objects.
10.3
A complete list of the features defined within the text object library, for use with both GenText and VisText.
10.4
An in-depth description of the common ways to change text and attributes within a text object. These are the most commonly used operations for text objects.
10.5
A description in detail of the VisText object.
10.6
A description in detail of the GenText object.
10.7
A description of the controllers specifically tailored for use with the text objects.

If you are using a simple GenText object for getting and setting text, you will probably only need to read the in-depth description of how to alter text and the GenText section. If you need other functionality, you should probably read this chapter straight through.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Which Object Should I Use? | Next: 2 General Text Features


General Text Features

The Text Objects: 2 General Text Features

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 The Text Objects | Next: 3 The Text Object Library

The Text objects provide a powerful tool for complex text formatting. This section describes some of the more powerful features of these text objects.

Input Management and Filters

Text Flow Through Regions

Style Sheets

Undo

General Import and Export

Geometry Management

Embedded Graphics and Characters

Search and Replace

Spell-Checking

Printing

Text Controller Objects

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 The Text Objects | Next: 3 The Text Object Library


Input Management and Filters

The Text Objects: 2.1 General Text Features: Input Management and Filters

| Up: GEOS SDK TechDocs | Up | Prev: 2 General Text Features | Next: 2.2 Text Flow Through Regions

A large portion of the text library is devoted to input management and interaction with the UI and the user. Almost every input and display function for text is implemented within the text library, relieving your applications from containing often complex code. In almost all cases, you will not need to know how input is transferred to your text objects.

The text library handles all keyboard and mouse input to text fields, interacting intelligently with the UI to process input events. (This includes pen input supported by the input manager and the pen input mechanisms.) Selection with the mouse and keyboard is handled automatically, as are quick-transfer operations.

Because the text objects understand the focus and target hierarchies of the UI, they can interact directly with the controller objects. Applications using the text objects don't even have to know when a user has applied a new style, changed the paragraph spacing, or set the text color. The user can select text, call up the Style menu, and apply a new style without using a line of application-specific code. If you wish such behavior to happen automatically, you should mark your text object GA_TARGETABLE in its GI_ attrs instance field.

GEOS also provides filters to text objects; these filters allow the text object to selectively intercept and process text as it is being added to a text object (usually a GenText), either from direct keyboard typing or from a more complex operation (such as a clipboard paste). Filters allow the text object to accept or reject text on a number of bases (such as character by character).

| Up: GEOS SDK TechDocs | Up | Prev: 2 General Text Features | Next: 2.2 Text Flow Through Regions


Text Flow Through Regions

The Text Objects: 2.2 General Text Features: Text Flow Through Regions

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Input Management and Filters | Next: 2.3 Style Sheets

The text library also allows a continuous stream of text to flow through various connected regions. These regions may be of arbitrary size, shape, and location, and each region occupies one spot in the text object's region list. The text library, once the region list is created, takes care of all text flowing and rippling from one region to the next. GenText objects may not implement regions, though; ideally their geometry should be left up to the specific UI.

Applications can easily set up regions using the graphics system's path code, then add the regions to the text object's region list. These two simple steps can result in powerful publishing features such as flowing text around graphics and flowing text through arbitrarily-shaped regions.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Input Management and Filters | Next: 2.3 Style Sheets


Style Sheets

The Text Objects: 2.3 General Text Features: Style Sheets

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Text Flow Through Regions | Next: 2.4 Undo

Many word processors and publishing programs today implement styles . A style is a set of character and paragraph attributes typically associated with a specific name; the user chooses the attributes for the style and then can apply that style to any selected text by choosing the associated name. A style sheet is a set of styles. Styles can be (and normally are) based on other styles.

Normally, the definition and use of styles is left entirely to the application. In GEOS, however, the text library provides a controller object for styles and style sheets, so if your application will use style sheets, it can do so without needing complex code.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Text Flow Through Regions | Next: 2.4 Undo


Undo

The Text Objects: 2.4 General Text Features: Undo

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Style Sheets | Next: 2.5 General Import and Export

For complex text formatting, the ability to "undo" any changes is nearly a necessity. The GEOS text library provides a single-level undo that will work with all text-edit objects. This undo may be implemented by either the user or the application.

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Style Sheets | Next: 2.5 General Import and Export


General Import and Export

The Text Objects: 2.5 General Text Features: General Import and Export

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Undo | Next: 2.6 Geometry Management

Through the Impex Library, any application using a text object can import from and export to many popular word processor formats. You don't have to go through the work of creating your own format translator for each supported format; any format supported by the Impex Library will automatically be supported by your application. (See the Impex chapter.) In addition, the Impex Library is designed so users can add new translators to the importer/exporter; these translators are then accessible to your application without recompiling or upgrading.

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Undo | Next: 2.6 Geometry Management


Geometry Management

The Text Objects: 2.6 General Text Features: Geometry Management

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 General Import and Export | Next: 2.7 Embedded Graphics and Characters

The GenText object automatically will interact with its parent generic object (usually a GenInteraction or GenPrimary) to provide the proper sizing behavior. GenText objects automatically implement their own scrolling when necessary, adding horizontal or vertical scrollers as the need arises.

VisText, however, does not provide automatic scrolling. The VisText may only exist as a node in a visible object tree under a VisContent; the content (in concert with its parent GenView) will provide whatever scrolling, scaling, and sizing that is required.

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 General Import and Export | Next: 2.7 Embedded Graphics and Characters


Embedded Graphics and Characters

The Text Objects: 2.7 General Text Features: Embedded Graphics and Characters

| Up: GEOS SDK TechDocs | Up | Prev: 2.6 Geometry Management | Next: 2.8 Search and Replace

Every text field with the capability to define regions may contain embedded graphics strings. Each graphic appears as a single character within the text, and graphics may be pasted in by the user.

It is also possible to flow text around graphics. The large object model provides this feature, but the regular text model does not. Thus, to flow text around graphics requires a VisText object set up for large text.

| Up: GEOS SDK TechDocs | Up | Prev: 2.6 Geometry Management | Next: 2.8 Search and Replace


Search and Replace

The Text Objects: 2.8 General Text Features: Search and Replace

| Up: GEOS SDK TechDocs | Up | Prev: 2.7 Embedded Graphics and Characters | Next: 2.9 Spell-Checking

Global search-and-replace functions are included in the text object. GEOS provides a standard search-and-replace dialog box that can be called up by any text object. The search-and-replace mechanism can work with all text objects in the system; if two applications are both using search-and-replace at the same time, only one dialog box will appear on the screen. The search and replace controller understands which application is the current target of the search operations and interacts intelligently with the active applications.

| Up: GEOS SDK TechDocs | Up | Prev: 2.7 Embedded Graphics and Characters | Next: 2.9 Spell-Checking


Spell-Checking

The Text Objects: 2.9 General Text Features: Spell-Checking

| Up: GEOS SDK TechDocs | Up | Prev: 2.8 Search and Replace | Next: 2.10 Printing

Like search-and-replace, spell-checking is a function of the text library and is available globally to all applications and libraries that use text objects. GEOS uses a licensed Houghton-Mifflin dictionary and spell-checking engine; this engine as well as the common UI are available to each application and library that uses a text object. The spelling checker, like the search-and-replace mechanism, interacts intelligently with the text object and the UI to ensure that only one spell-check dialog box is on the screen.

The simplest and most efficient way to include spell-checking in your application is to include an object of SpellControlClass in your application, linking it to the text object to spell-check. This controller is discussed in Search and Replace and Spell-Checking .

| Up: GEOS SDK TechDocs | Up | Prev: 2.8 Search and Replace | Next: 2.10 Printing


Printing

The Text Objects: 2.10 General Text Features: Printing

| Up: GEOS SDK TechDocs | Up | Prev: 2.9 Spell-Checking | Next: 2.11 Text Controller Objects

VisText objects automatically know how to print themselves when sent the proper messages. In nearly all cases where printing text is essential, a VisText will be included in a visual object tree. Then, when printing occurs, the topmost object in the tree (most likely a VisContent) will receive the print message; this message will be passed down the tree until all the visual objects (including any VisTexts) have printed themselves.

| Up: GEOS SDK TechDocs | Up | Prev: 2.9 Spell-Checking | Next: 2.11 Text Controller Objects


Text Controller Objects

The Text Objects: 2.11 General Text Features: Text Controller Objects

| Up: GEOS SDK TechDocs | Up | Prev: 2.10 Printing | Next: 3 The Text Object Library

As with several other major features of the GEOS user interface, the text library has associated controller classes for many features. Because text formatting has so many options and choices for the user, providing the menus, dialog boxes, and choices can be a lot of work for an application. The text controller objects, however, remove nearly all this work while allowing applications the full support they require.

Nearly every feature of the text library has one corresponding controller object. In addition, master controllers exist to manage other controller objects. Therefore, if you use a master controller, other controllers that may be added to the text object (by Geoworks) will be automatically included in your geode without changing the source code or recompiling.

Using controllers and an editable text object, you can actually create a simple but full-featured word processor without writing any code other than UI object definitions. Controllers typically work on both GenText or VisText objects; whichever text object has the target will receive updates from controllers.

| Up: GEOS SDK TechDocs | Up | Prev: 2.10 Printing | Next: 3 The Text Object Library


The Text Object Library

The Text Objects: 3 The Text Object Library

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 General Text Features | Next: 4 Text Object Chunks

Both Text Objects rely on a single framework; this framework is the Text Object Library. The Text Object Library ( tCommon.h ) contains many definitions and structures that text objects will use. Every definition and structure in this section can be used by both VisText and GenText objects.

VisTextClass is the class that utilizes most of these features directly. GenTextClass also utilizes almost every one of these features through VisTextClass . This is because GenTextClass , as a generic object, is manifested through VisClass by specific UI libraries.

The text library also has data structures and mechanisms defined to support other features that, due to time constraints, have no strict API definition in the library. To provide these "potential features" in your applications, you can directly access the text data structures and set them appropriately. (For example, automatic paragraph numbering is not directly supported, but the paragraph attributes data structures allow defining characters that get prepended to individual paragraphs.)

Many of the features are implemented and controlled automatically by GEOS. For common features (character styles and font sizes, for example), controller objects can handle almost everything. For less common features (automatic paragraph numbering, for example), your application will likely have to provide the UI and the control mechanisms necessary.

Character Attribute Definitions

Paragraph Attribute Definitions

Storage Flags

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 General Text Features | Next: 4 Text Object Chunks


Character Attribute Definitions

The Text Objects: 3.1 The Text Object Library: Character Attribute Definitions

| Up: GEOS SDK TechDocs | Up | Prev: 3 The Text Object Library | Next: 3.2 Paragraph Attribute Definitions

The text object library provides a multitude of ways to shape the appearance of individual characters. Each character within a text object may even exhibit its own individual attributes, if the object is defined to use multiple character attributes. The definitions within this section show the full range of possibilities for character attributes.

The VisTextCharAttr Structure

The VisTextCharAttr structure provides a great variety of attributes for characters within text objects. Many of the VisTextCharAttr fields use graphics structures to tailor the appearance of individual characters within the text object. You may wish to review the Graphics Environment chapter to discover the full features of this structure.

Code Display 10-1 The VisTextCharAttr Structure

typedef struct {
    StyleSheetElementHeader				VTCA_meta;
    FontID				VTCA_fontID;
    WBFixed				VTCA_pointSize;
    TextStyle				VTCA_textStyles;
    ColorQuad				VTCA_color;
    sword				VTCA_trackKerning;
    byte				VTCA_fontWeight;
    byte				VTCA_fontWidth;
    VisTextExtendedStyles				VTCA_extendedStyles;
    SystemDrawMask				VTCA_grayScreen;
    GraphicPattern				VTCA_pattern;
    ColorQuad				VTCA_bgColor;
    SystemDrawMask				VTCA_bgGrayScreen;
    GraphicPattern				VTCA_bgPattern;
    byte				VTCA_reserved[7];
} VisTextCharAttr;

VTCA_ meta stores a StyleSheetElementHeader . This structure field allows the VisTextCharAttr structure to act as an element in an element array. For complete information on using character attribute element arrays to implement character style runs, see Character Runs .

VTCA_ fontID stores the FontID of the font to be used for this character. Any current font allowed by the system is valid. There may exist up to 65536 fonts of type FontID .

VTCA_ pointSize stores the point size of the character. This may be any fixed point value between 4 and 792.

VTCA_ textStyles stores the text styles (of type TextStyle ) to be applied to this character. For a list of text styles, see the Drawing Shapes chapter.

VTCA_ color stores the current color (of type ColorQuad ) of the foreground text character.

VTCA_ trackKerning stores the kerning value (either positive or negative) for this character. This value is expressed in points and reflects how much extra space (if positive) or removal of space (if negative) to apply between this character and the next character.

VTCA_ fontWeight stores the font-weight (thickness) of the character. This value is expressed as a FontWeight value, a percentage between 75% and 125%.

VTCA_ fontWidth stores the width of the font, expressed as a FontWidth value, a percentage between 25% and 200%. Several constants are defined in font.h with common font widths, including FWI_CONDENSED and FWI_EXPANDED.

VTCA_ extendedStyles stores any VisTextExtendedStyles (separate from the TextStyle in VTCA_ textStyles ) in use by this character. These styles are defined in the text object library. See VisTextExtendedStyles for a list of extended styles.

VTCA_ grayScreen stores the SystemDrawMask to use when displaying this text. This mask is applied to the text characters but not to the background.

VTCA_ pattern stores the GraphicPattern to use when displaying this text. This pattern is applied to the text characters but not to the background.

VTCA_bgColor stores the background color to apply to the character's background (but not to the character itself).

VTCA_ bgGrayScreen stores the SystemDrawMask to apply to the character's background.

VTCA_ bgPattern stores the GraphicPattern to apply to the character's background.

Code Display 10-2 VisTextExtendedStyles

  /* These flags are stored in the VisTextCharAttr entry VTCA_extendedStyles */
typedef WordFlags VisTextExtendedStyles;
#define VTES_BOXED				0x8000
#define VTES_BUTTON				0x4000
#define VTES_INDEX				0x2000
#define VTES_ALL_CAP				0x1000
#define VTES_SMALL_CAP				0x0800
#define VTES_HIDDEN				0x0400
#define VTES_CHANGE_BAR				0x0200
#define VTES_BACKGROUND_COLOR				0x0100

VTES_BOXED draws the character within a box (a rectangle surrounding the character). If consecutive characters are marked VTES_BOXED they will all be drawn within a single rectangular outline.

VTES_BUTTON frames the text characters within a drop shadow. Consecutive characters marked VTES_BUTTON will be framed within a single drop-shadow.

VTES_INDEX marks the character for indexing purposes. This feature is currently not implemented.

VTES_ALL_CAP indicates that the character should be capitalized in the current point size.

VTES_SMALL_CAP indicates that the character should be capitalized, but at a smaller point size.

VTES_HIDDEN indicates that this character acts as hidden text. Hidden text may exhibit special properties (such as being masked out when printing).

VTES_CHANGE_BAR is currently unimplemented.

VTES_BACKGROUND_COLOR indicates that this character exhibits a special background color.

Default Character Attributes

You may decide that you do not need your characters to exhibit the many attributes available within the VisTextCharAttr structure. If your text object is simple, you may want instead to use the default character attributes provided in the text object library. These default attributes supersede the use of the VisTextCharAttr structure. All of these attributes will fit into a single word record.

The VisTextDefaultCharAttr record allows your text object to exhibit three styles (underline, bold, and italic), 16 color indexes of type Color , eight point sizes, and 32 different fonts.

Code Display 10-3 VisTextDefaultCharAttr

typedef WordFlags VisTextDefaultCharAttr;
#define VTDCA_UNDERLINE			0x8000
#define VTDCA_BOLD			0x4000
#define VTDCA_ITALIC			0x2000
#define VTDCA_COLOR			0x0f00	/* Color */
#define VTDCA_SIZE			0x00e0	/* VisTextDefaultSize */
#define VTDCA_FONT			0x001f	/* VisTextDefaultFont */
#define VTDCA_COLOR_OFFSET				8
#define VTDCA_SIZE_OFFSET				5
#define VTDCA_FONT_OFFSET				0
  typedef ByteEnum VisTextDefaultSize;
    #define		VTDS_8	0		/* Point size of 8 */
    #define		VTDS_9	1		/* Point size of 9 */
    #define		VTDS_10	2		/* Point size of 10 */
    #define		VTDS_12	3		/* Point size of 12 */
    #define		VTDS_14	4		/* Point size of 14 */
    #define		VTDS_18	5		/* Point size of 18 */
    #define		VTDS_24	6		/* Point size of 24 */
    #define		VTDS_36	7		/* Point size of 36 */
  typedef ByteEnum VisTextDefaultFont;
    #define		VTDF_BERKELEY		0		/* Berkeley Font */
    #define		VTDF_CHICAGO		1		/* Chicago Font */
    #define		VTDF_BISON		2		/* Bison Font */
    #define		VTDF_WINDOWS		3		/* Windows Font */
    #define		VTDF_LED		4		/* LED Font */
    #define		VTDF_ROMA		5		/* Roma Font */
    #define		VTDF_UNIVERSITY		6		/* University Font */
    #define		VTDF_URW_ROMAN		7		/* URW Roman Font */
    #define		VTDF_URW_SANS		8		/* URW Sans Font */
    #define		VTDF_URW_MONO		9		/* URW Mono Font */
    #define		VTDF_URW_SYMBOLPS 10				/* URW Symbols Font */
    #define		VTDF_CENTURY_SCHOOLBOOK 11				/* Century Schoolbook Font */
#define  VIS_TEXT_INITIAL_CHAR_ATTR \
    ((VTDS_12 << VTDCA_SIZE_OFFSET) | VTDF_BERKELEY)

Macros for Defining VisTextCharAttr Structures

The text object library also provides several macros to make the definition of the VisTextCharAttr structure simpler. These macros are listed in VisTextCharAttr Macros . In many cases, these macros make the definition of character style runs vastly easier.

Code Display 10-4 VisTextCharAttr Macros

 #define CHAR_ATTR_STYLE_FONT_SIZE_STYLE_COLOR(ref, style, font, psize, tstyle, \ color) { \
    {{{ref, 0}}, style}, font, {0, psize}, tstyle, \
    {color, CF_INDEX, 0, 0}, 0, FWI_MEDIUM, FW_NORMAL, 0, SDM_100, {0}, \
    {C_WHITE, CF_INDEX, 0, 0}, SDM_0, {0}, {0, 0, 0, 0, 0, 0, 0}}
 #define CHAR_ATTR_FONT_SIZE_STYLE(font, psize, tstyle) \
    CHAR_ATTR_STYLE_FONT_SIZE_STYLE_COLOR(2, CA_NULL_ELEMENT, font, \
                        psize, tstyle, C_BLACK)
 #define CHAR_ATTR_FONT_SIZE(font, psize) \
    CHAR_ATTR_STYLE_FONT_SIZE_STYLE_COLOR(2, CA_NULL_ELEMENT, font, \
                        psize, 0, C_BLACK)
| Up: GEOS SDK TechDocs | Up | Prev: 3 The Text Object Library | Next: 3.2 Paragraph Attribute Definitions


Paragraph Attribute Definitions

The Text Objects: 3.2 The Text Object Library: Paragraph Attribute Definitions

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Character Attribute Definitions | Next: 3.3 Storage Flags

The text object library also provides many ways to shape the appearance of individual paragraphs. Each paragraph within a text object may exhibit its own individual attributes if the text object is set to allow multiple paragraph attributes. The definitions within this section show the full range of possibilities for paragraph attributes.

The VisTextParaAttr Structure

The VisTextParaAttr structure provides the largest set of attributes for use by paragraphs within text objects. Note that although the VisTextParaAttr structure is of a fixed size, the paragraph attributes consist of this structure and a variable number of tabs; therefore, the paragraph attributes within a "run" may be variably sized. These entries are listed in VisTextParaAttr . Comments follow the code display.

Code Display 10-5 VisTextParaAttr

typedef struct {
    StyleSheetElementHeader				VTPA_meta;
    VisTextParaBorderFlags				VTPA_borderFlags;
    ColorQuad				VTPA_borderColor;
    VisTextParaAttrAttributes				VTPA_attributes;
    word				VTPA_leftMargin;
    word				VTPA_rightMargin;
    word				VTPA_paraMargin;
    BBFixedAsWord				VTPA_lineSpacing;
    word				VTPA_leading;
    BBFixedAsWord				VTPA_spaceOnTop;
    BBFixedAsWord				VTPA_spaceOnBottom;
    ColorQuad				VTPA_bgColor;
    byte				VTPA_numberOfTabs;
    byte				VTPA_borderWidth;
    byte				VTPA_borderSpacing;
    byte				VTPA_borderShadow;
    SystemDrawMask				VTPA_borderGrayScreen;
    SystemDrawMask				VTPA_bgGrayScreen;
    HatchPattern				VTPA_borderHatch;
    HatchPattern				VTPA_bgHatch;
    word				VTPA_defaultTabs;
    word				VTPA_startingParaNumber;
    char				VTPA_prependChars[4];
    VisTextHyphenationInfo				VTPA_hyphenationInfo;
    VisTextKeepInfo				VTPA_keepInfo;
    VisTextDropCapInfo				VTPA_dropCapInfo;
    word				VTPA_nextStyle;
    StandardLanguage				VTPA_language;
    byte				VTPA_reserved[15];
} VisTextParaAttr;

VTPA_ meta stores a StyleSheetElementHeader . This structure field allows the VisTextParaAttr structure to act as an element in an element array. For complete information on using paragraph attribute element arrays to implement paragraph style runs, see Paragraph Runs .

VTPA_borderFlags stores the VisTextParaBorderFlags to use in drawing any borders around the text paragraph.

VTPA_ borderColor stores the color (of type ColorQuad ) of the border.

VTPA_ attributes stores the VisTextParaAttrAttributes to use on this paragraph. These attributes specify whether the paragraph uses keep-with, drop caps, automatic hyphenation, or paragraph numbering.

VTPA_ leftMargin stores the left margin of the paragraph. This margin is the distance (offset in points) from the left edge of the text object.

VTPA_ rightMargin stores the right margin of the paragraph. This margin is the distance (offset in points) from the right edge of the text object.

VTPA_ paraMargin stores the paragraph margin. This margin is the additional distance (offset in points from the left margin) to indent the first line of a paragraph.

VTPA_ lineSpacing stores the distance between lines of the paragraph. Single-spacing (a VTPA_ lineSpacing of 1.0) is the default value.

VTPA_ leading stores the leading of the paragraph. Leading is the additional space between all lines of a paragraph, independent of line spacing. This value is expressed in points.

VTPA_ spaceOnTop stores the additional vertical distance (expressed in points) before the first line of the paragraph. VTPA_ spaceOnTop plus VTPA_ spaceOnBottom is the total vertical distance between paragraphs.

VTPA_ spaceOnBottom stores the additional vertical distance (expressed in points) after the last line of the paragraph. VTPA_ spaceOnTop plus VTPA_ spaceOnBottom is the total vertical distance between paragraphs.

VTPA_bgColor stores the background color (of type ColorQuad ) of the paragraph. Note this is different from the background color of an individual character (in VTCA_ bgColor ). The background color of a character, if any, takes precedence over the background color of a paragraph.

VTPA_ numberOfTabs stores the number of tab stops contained in this paragraph. Tabs are added at the end of the VisTextParaAttr structure. Therefore, this structure may be of variable size, depending on the number of tabs.

VTPA_ borderWidth stores the width (in units of eight pixels) of the border, if any, attached to this paragraph.

VTPA_ borderSpacing stores the spacing (in units of eight pixels) between the paragraph border, if any, and the paragraph itself.

VTPA_ borderShadow stores the distance (in units of eight pixels) of the border shadow, if any, from the main border.

VTPA_ borderGrayScreen stores the SystemDrawMask to use when drawing the paragraph border.

VTPA_ bgGrayScreen stores the SystemDrawMask to use when drawing the paragraph background.

VTPA_ borderHatch stores HatchPattern to use when drawing the border.

VTPA_ bgHatch stores the HatchPattern to use when drawing the background.

VTPA_ defaultTabs stores the spacing for default tabs.

VTPA_ startingParaNumber stores the sequential number of this paragraph. This value depends on the VisTextNumberType in VTPA_ attributes .

VTPA_ prependChars stores the characters (up to four) to prepend to the beginning of this paragraph.

VTPA_ hyphenationInfo stores the VisTextHyphenationInfo (see below) to use between lines of text within a paragraph.

VTPA_ keepInfo stores the VisTextKeepInfo (see below) to use between this and other paragraphs.

VTPA_ dropCapInfo stores the VisTextDropCapInfo (see below) to use at the start of this paragraph.

VTPA_nextStyle and VTPA_language represent text attributes that are currently unimplemented but will be added.

VTPA_ reserved reserves fifteen bytes of data at the end of the VisTextParaAttr structure. These bytes are used to designate extra tabs.

Code Display 10-6 VisTextParaBorderFlags

  typedef WordFlags VisTextParaBorderFlags;
#define VTPBF_LEFT				0x8000
#define VTPBF_TOP				0x4000
#define VTPBF_RIGHT				0x2000
#define VTPBF_BOTTOM				0x1000
#define VTPBF_DOUBLE				0x0800
#define VTPBF_DRAW_INNER_LINES				0x0400
#define VTPBF_SHADOW				0x0200
#define VTPBF_ANCHOR				0x0003 /* ShadowAnchor */
#define VTPBF_ANCHOR_OFFSET				0
  typedef ByteEnum ShadowAnchor;
#define	SA_TOP_LEFT		0
#define	SA_TOP_RIGHT		1
#define	SA_BOTTOM_LEFT		2
#define	SA_BOTTOM_RIGHT		3

The VisTextParaBorderFlags specify the manner in which a border should be drawn around the paragraph.

VTPBF_LEFT, VTPBF_TOP, VTPBF_RIGHT, and VTPBF_BOTTOM all specify whether their respective side is drawn with a border. If you wish a border to entirely surround the text object, you should set all of these flags.

VTPBF_DOUBLE specifies that there should be a two-line border around this paragraph. This flag will double any lines set to be drawn with VTPBF_LEFT, VTPBF_TOP, VTPBF_RIGHT, and VTPBF_BOTTOM.

VTPBF_DRAW_INNER_LINES draws lines between bordered paragraphs. If two consecutive paragraphs are marked with this flag, a line border will be drawn between them.

VTPBF_SHADOW specifies that the border should be shadowed. The direction of shadowing is specified in the VTPBF_ANCHOR flag.

VTPBF_ANCHOR specifies the ShadowAnchor to use if drawing a border shadow. The ShadowAnchor specifies the corner of the paragraph to anchor the shadow. The shadowing effect on the paragraph will proceed from this corner towards the opposite corner.

Code Display 10-7 VisTextNumberType

  typedef ByteEnum VisTextNumberType;
#define	VTNT_NUMBER			0
#define	VTNT_LETTER_UPPER_A			1
#define	VTNT_LETTER_LOWER_A			2
#define	VTNT_ROMAN_NUMERAL_UPPER	3
#define	VTNT_ROMAN_NUMERAL_LOWER	4

The VisTextNumberType specifies the paragraph numbering scheme for this paragraph. For each paragraph with a numbering type, the object will number that paragraph sequentially based on the last occurrence of a paragraph with the same type. The text object stores this sequential number in the VTPA_ startingParaNumber entry.

Code Display 10-8 VisTextParaAttrAttributes

  typedef WordFlags VisTextParaAttrAttributes;
#define VTPAA_JUSTIFICATION					0xc000
#define VTPAA_KEEP_PARA_WITH_NEXT					0x2000
#define VTPAA_KEEP_PARA_TOGETHER					0x1000
#define VTPAA_ALLOW_AUTO_HYPHENATION					0x0800
#define VTPAA_DISABLE_WORD_WRAP					0x0400
#define VTPAA_COLUMN_BREAK_BEFORE					0x0200
#define VTPAA_PARA_NUMBER_TYPE					0x01c0
#define VTPAA_DROP_CAP					0x0020
#define VTPAA_KEEP_LINES					0x0010
#define VTPAA_JUSTIFICATION_OFFSET					14
#define VTPAA_PARA_NUMBER_TYPE_OFFSET					5

VTPAA_JUSTIFICATION stores the Justification to use with this paragraph.

VTPAA_KEEP_PARA_WITH_NEXT ensures that this paragraph and the next one will not be split along page breaks.

VTPAA_KEEP_PARA_TOGETHER ensures that the entire paragraph will not be split along page breaks. In most cases, this will cause a paragraph capable of being split to appear at the top of the next page.

VTPAA_ALLOW_AUTO_HYPHENATION allows the paragraph to automatically hyphenate words that cross line breaks. If this flag is set, the entry VTPA_ hyphenationInfo will store the VisTextHyphenationInfo to use when hyphenating words.

VTPAA_DISABLE_WORD_WRAP disables automatic word wrapping. Each line break will immediately wrap to next line without keeping words together.

VTPAA_PARA_NUMBER_TYPE stores the VisTextParaType for this paragraph. The actual paragraph number is stored in the VTPA_ startingParaNumber entry. The paragraph number type in tandem with the paragraph number determines what number heading this paragraph will have.

VTPAA_DROP_CAP signifies that a this paragraph uses Drop Caps. If this flag is set, the VisTextParaAttr entry VTPA_ dropCapInfo stores the VisTextParaDropCapInfo (see below).

VTPAA_KEEP_LINES specifies that this paragraph should make sure that beginning or ending lines of certain lengths should not be allowed to stand alone. If this flag is set, the VisTextParaAttr entry VTCA_ keepInfo stores the VisTextKeepInfo (see below).

Code Display 10-9 VisTextHyphenationInfo

  typedef WordFlags VisTextHyphenationInfo;
#define VTHI_HYPHEN_MAX_LINES					0xf000
#define VTHI_HYPHEN_SHORTEST_WORD					0x0f00
#define VTHI_HYPHEN_SHORTEST_PREFIX					0x00f0
#define VTHI_HYPHEN_SHORTEST_SUFFIX					0x000f
#define VTHI_HYPHEN_MAX_LINES_OFFSET						12
#define VTHI_HYPHEN_SHORTEST_WORD_OFFSET						 8
#define VTHI_HYPHEN_SHORTEST_PREFIX_OFFSET						 4
#define VTHI_HYPHEN_SHORTEST_SUFFIX_OFFSET						 0

If the VTPA_attributes flag VTPAA_ALLOW_AUTO_HYPHENATION is set, VisTextHyphenationInfo stores the criteria to use when hyphenating words in VTPA_ hyphenationInfo . If hyphenation is set, words will be automatically hyphenated using a Houghton-Mifflin engine.

VTHI_SHORTEST_WORD sets the shortest word-length (in character counts) to hyphenate. Words shorter than this character count will be wrapped to the next line.

VTHI_SHORTEST_PREFIX and VTHI_SHORTEST_SUFFIX set the shortest prefix and suffix to leave on a line after hyphenation. Prefixes or suffixes shorter than this character count will be tied to the main word and wrapped, if necessary.

Code Display 10-10 VisTextKeepInfo

  typedef ByteFlags VisTextKeepInfo;
#define VTKI_TOP_LINES				0xf0
#define VTKI_BOTTOM_LINES				0x0f
#define VTKI_TOP_LINES_OFFSET				4
#define VTKI_BOTTOM_LINES_OFFSET				0

If the VTPA_ attributes flag VTPAA_KEEP_LINES is set, VisTextKeepInfo specifies the number of lines at the beginning and end of a paragraph that should not remain across page breaks. This prevents "widows" and "orphans"--single lines either at the end of one page or the beginning of another.

VTKI_TOP_LINES specifies the minimum number of lines at the beginning of a paragraph to be considered able to stand alone and not kept part of the main paragraph along a page break.

VTKI_BOTTOM_LINES specifies the minimum number of lines at the end of a paragraph to be considered able to stand alone and not kept part of the main paragraph along a page break.

For example, if VTKI_BOTTOM_LINES is set to three, then single or double lines will not be able to stand on their own at the top of a page, and either the orphans will be kept with the main paragraph or additional line(s) will be brought over from the preceding page to pad the next page.

Code Display 10-11 VisTextDropCapInfo

  typedef WordFlags VisTextDropCapInfo;
#define VTDCI_CHAR_COUNT				0xf000
#define VTDCI_LINE_COUNT				0x0f00
#define VTDCI_POSITION				0x00f0
#define VTDCI_CHAR_COUNT_OFFSET					12
#define VTDCI_LINE_COUNT_OFFSET					8
#define VTDCI_POSITION_OFFSET					4

If the VTPA_ attributes flag VTPAA_DROP_CAP is set, VisTextDropCapInfo specifies the criteria to use when implementing Drop Caps. Drop Caps are currently not implemented in GEOS though they will be supported for later releases.

VTDCI_CHAR_COUNT sets the number of characters to capitalize at the start of the paragraph before resuming normal capitalization.

VTDCI_LINE_COUNT sets the number of lines to capitalize at the start of a paragraph before resuming normal capitalization.

Code Display 10-12 Tabs

  typedef ByteEnum TabLeader;
#define	TL_NONE 		0
#define	TL_DOT 		1
#define	TL_LINE		2
#define	TL_BULLET		3
#define	TL_GRAY_LINE		4
  typedef ByteEnum TabType;
#define TT_LEFT			0
#define TT_CENTER			1
#define TT_RIGHT			2
#define TT_ANCHORED			3
  typedef ByteFlags TabAttributes;
#define	TA_LEADER		0x1c		/* TabLeader */
#define	TA_TYPE		0x03		/* TabType */
#define TA_LEADER_OFFSET			2
#define TA_TYPE_OFFSET			0
  typedef struct {
    word			T_position;
    TabAttributes			T_attr;		/* TabAttributes */
    SystemDrawMask			T_grayScreen;
    byte			T_lineWidth;
    byte			T_lineSpacing;
    word			T_anchor;
} Tab;
  typedef struct {
    VisTextParaAttr			VTMPA_paraAttr;
    Tab			VTMPA_tabs[VIS_TEXT_MAX_TABS];
} VisTextMaxParaAttr;

The Tab structure allows your paragraph to add custom tab stops to a paragraph. These tabs are in addition to any default tab stops that are defined in VTPA_ defaultTabs . You may add up to 25 custom tabs to each paragraph. These tabs are added at the end of the VisTextParaAttr structure. Therefore, different paragraphs may have paragraph attributes of different lengths depending on the number of custom tabs in use by that paragraph.

Default Paragraph Attributes

You may decide that you do not need many of the features within the VisTextParaAttr structure in your paragraphs. If your text object is simple, you may want to use a set of default paragraph attributes provided in the text object library. These default attributes supersede the use of the VisTextParaAttr structure. All of these attributes will fit into a single word record of type VisTextDefaultParaAttr .

The VisTextDefaultParaAttr structure allows your text object to exhibit a Justification , a subset of default tabs, and left, right, and paragraph margins.

Code Display 10-13 VisTextDefaultParaAttr

typedef WordFlags VisTextDefaultParaAttr;
#define VTDPA_JUSTIFICATION				0xc000 /* Justification */
#define VTDPA_DEFAULT_TABS				0x3000 /* VisTextDefaultTab */
#define VTDPA_LEFT_MARGIN				0x0f00 /* In units of half-inches */
#define VTDPA_PARA_MARGIN				0x00f0 /* In units of half-inches */
#define VTDPA_RIGHT_MARGIN				0x000f /* In units of half-inches */
#define VTDPA_JUSTIFICATION_OFFSET					14
#define VTDPA_DEFAULT_TABS_OFFSET					12
#define VTDPA_LEFT_MARGIN_OFFSET					8
#define VTDPA_PARA_MARGIN_OFFSET					4
#define VTDPA_RIGHT_MARGIN_OFFSET					0
#define VIS_TEXT_INITIAL_PARA_ATTR ( (0*2) << VTDPA_LEFT_MARGIN_OFFSET) | \
                ( (0*2) << VTDPA_PARA_MARGIN_OFFSET) | \
                ( (0*2) << VTDPA_RIGHT_MARGIN_OFFSET) | \
                (VTDDT_INCH << VTDPA_DEFAULT_TABS_OFFSET) | \
                (J_LEFT << VTDPA_JUSTIFICATION_OFFSET)
  typedef ByteEnum VisTextDefaultDefaultTab;
#define VTDDT_NONE				0
#define VTDDT_HALF_INCH				1
#define VTDDT_INCH				2
#define VTDDT_CENTIMETER				3

The VisTextDefaultDefaultTab type is solely for use within a VisTextDefaultParaAttr record. The default tab stops provide either no tab stops (VTDDT_NONE) or tab stops every half-inch, centimeter, or inch. Note that no other application-defined tabs are allowed when using the default paragraph attributes.

Macros for Defining VisTextParaAttr Structures

The text object library also provides a macro to make the definition of the VisTextParaAttr structure simpler; there are also a few macros to aid in setting up justification and tab structures. These macros are listed in VisTextParaAttr Macros . In many cases, these macros make the definition of paragraph style runs vastly easier.

Code Display 10-14 VisTextParaAttr Macros

 #define PARA_ATTR_STYLE_JUST_LEFT_RIGHT_PARA(ref, style, just, left, right, para)\
{ \
    {{{ref, 0}}, style}, 0, {C_BLACK, CF_INDEX, 0, 0}, \
    just << VTPAA_JUSTIFICATION_OFFSET, (left)*PIXELS_PER_INCH, \
    (right)*PIXELS_PER_INCH, (para)*PIXELS_PER_INCH, \
    1<<8, 0, 0, 0, {C_WHITE, CF_INDEX, 0, 0}, \
    0, 1*8, 2*8, 1*8, SDM_100, SDM_0, {0}, {0}, \
    PIXELS_PER_INCH/2*8, VIS_TEXT_DEFAULT_STARTING_NUMBER, "", 0, 0, 0,
    CA_NULL_ELEMENT, SL_ENGLISH, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
#define DEF_PARA_ATTR_JUST_TABS(just, tabs) \
    (( (0*2) << VTDPA_LEFT_MARGIN_OFFSET ) | \
     ( (0*2) << VTDPA_PARA_MARGIN_OFFSET ) | \
     ( (0*2) << VTDPA_RIGHT_MARGIN_OFFSET ) | \
     ( (tabs) << VTDPA_DEFAULT_TABS_OFFSET ) | \
     ( (just) << VTDPA_JUSTIFICATION_OFFSET ))
#define DEF_PARA_ATTR_CENTER DEF_PARA_ATTR_JUST_TABS(J_CENTER, VTDDT_INCH)
#define DEF_PARA_ATTR_RIGHT DEF_PARA_ATTR_JUST_TABS(J_RIGHT, VTDDT_INCH)
| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Character Attribute Definitions | Next: 3.3 Storage Flags


Storage Flags

The Text Objects: 3.3 The Text Object Library: Storage Flags

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Paragraph Attribute Definitions | Next: 4 Text Object Chunks

As you have seen, the Text Object Library provides a multitude of character and paragraph attributes to use in the display of text. The library also provides a number of different subsets of attributes and ways to store these attributes.

A simple text object may only need a 16-bit record (such as VisTextDefaultCharAttr or VisTextDefaultParaAttr ) to store its text attributes; a highly complex text object may need a chunk array of several 30+ byte elements. The manner in which these attributes are stored is specified in the VisTextStorageFlags of the text object.

Code Display 10-15 VisTextStorageFlags

typedef ByteFlags VisTextStorageFlags;
#define VTSF_LARGE				0x80
#define VTSF_MULTIPLE_CHAR_ATTRS				0x40
#define VTSF_MULTIPLE_PARA_ATTRS				0x20
#define VTSF_TYPES				0x10
#define VTSF_GRAPHICS				0x08
#define VTSF_DEFAULT_CHAR_ATTR				0x04
#define VTSF_DEFAULT_PARA_ATTR				0x02
#define VTSF_STYLES				0x01
VTSF_LARGE
This flag states that this text object is using the large model for its data. In this case, the rest of the settings in VisTextStorageFlags are ignored. The large model is a highly complex text formatting tool; you should avoid it entirely until you are familiar with other aspects of the text object.
VTSF_MULTIPLE_CHAR_ATTRS
This flag specifies that the text may display multiple character attributes. Each character may then display its own character attributes independent of other characters. If this flag is set, VTSF_DEFAULT_CHAR_ATTRS must be not set.
VTSF_MULTIPLE_PARA_ATTRS
This flag specifies that the text may display multiple paragraph attributes. Each paragraph may then display its own attributes independent of other paragraphs. If this flag is set, VTSF_DEFAULT_PARA_ATTRS must not be set.
VTSF_TYPES
This flag specifies that this text object contains a types run.
VTSF_GRAPHICS
This flag specifies that this text object contains a graphics run.
VTSF_DEFAULT_CHAR_ATTRS
This flag specifies that this text object only uses a set of provided default character attributes. These default attributes are represented by a record of type VisTextDefaultCharAttrs . If this flag is not set, the text object will use the larger VisTextCharAttr structure to store its character attributes instead.
VTSF_DEFAULT_PARA_ATTRS
This flag specifies that this text object only uses a set of provided default paragraph attributes. These default attributes are represented by a record of type VisTextDefaultParaAttrs . If this flag is not set, the text object will use the larger VisTextParaAttr structure to store its paragraph attribute.
VTSF_STYLES
This flag specifies that this text object contains a styles run.
| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Paragraph Attribute Definitions | Next: 4 Text Object Chunks


Text Object Chunks

The Text Objects: 4 Text Object Chunks

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 The Text Object Library | Next: 5 Using VisText

Each text object contains several instance fields that may reference outside chunks. Most non-large text objects will reference the following chunks:

  • A text chunk containing the string of text for that object.
  • A lines/fields chunk containing internal information about the length and position of lines and tab fields.
  • A character attributes chunk, containing either a single VisTextCharAttr structure or (for multiple character attributes) a list of VisTextCharAttr structures.
  • A paragraph attributes chunk, containing either a single VisTextParaAttr structure or (for multiple paragraph attributes) a list of VisTextParaAttr structures.
  • For multiple character attributes, a chunk of character attribute runs, referencing the list of VisTextCharAttr structures above.
  • For multiple paragraph attributes, a chunk of paragraph attribute runs, referencing the list of VisTextParaAttr structures above.

The Text Object Library provides many other features, but these are the most often-used and difficult to master. You should have a complete grasp of these mechanisms before using either VisText or GenText. To make their use easier, both objects use these features in essentially the same manner.

The Text

Lines and Fields

Character Runs

Paragraph Runs

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 The Text Object Library | Next: 5 Using VisText


The Text

The Text Objects: 4.1 Text Object Chunks: The Text

| Up: GEOS SDK TechDocs | Up | Prev: 4 Text Object Chunks | Next: 4.2 Lines and Fields

The most important data associated with a text object is the text itself. The text of all non-large Text Objects is stored within a single chunk; this chunk is located in the same resource as the object itself. Text objects contain an instance field with a ChunkHandle to this text chunk. (In GenText objects, this is the instance field GTXI_ text ; in VisText objects, this is VTI_ text .)

Unless your text object uses the complex large model, it stores all of its text within this chunk. You may set initial text for your text object to appear within its instance data. The text object library automatically handles keyboard input from the user and translates that into characters displayed within the text object, altering the contents of the text chunk at the same time.

The text within the chunk is represented by a null-terminated character string. Each character of the text occupies a zero-based position within the text field. (The first character in the text field is at position zero.) These character positions are useful for setting character and paragraph attributes, marking the current selection, and marking the insertion point for new text.

The text object library provides a number of operations that you can perform to alter the display of text. These messages may take text from a variety of sources and include it within your text object; alternatively, you may retrieve text from your object and send it to other objects or processes.

Though the text within a non-large text object will always reside in a chunk (and may therefore be referenced with an optr) text may come from (and go to) several different sources. If you intend to retrieve text from or send text to a text object, you must know what type of source (or destination) you are dealing with.

Typically, text outside of a text object will reside in one of the following six forms. The first two cases are the most common.

  • A pointer to null terminated text string.
  • An optr to a chunk. (This is the same format the text object itself stores its text.)
  • A handle of a global memory block.
  • A handle of a VM block.
  • A database item.
  • A huge array.

For each operation on a text object, the text object library provides specific messages tailored to the format you are retrieving text from or transferring text to.

Text Ranges

VisTextRange, MSG_VIS_TEXT_GET_RANGE, VisTextRangeContext, MSG_VIS_TEXT_GET_TEXT_SIZE

Frequently, your application may wish to specify a range of text to act on. This range specifies the starting and ending points within the text for the relevant operation. Each of these starting and ending points is a zero-based character position.

typedef struct {
    dword		VTR_start;
            /* starting character position */
    dword		VTR_end;
            /* ending character position */
} VisTextRange;

To select a starting point at the first character, set VTR_ start to zero. To select an ending point at the last character, set VTR_end to the special constant TEXT_ADDRESS_PAST_END.

If you want to pass the current selection as a range in any message that demands a VisTextRange , pass VIS_TEXT_RANGE_SELECTION to indicate that the currently selected area should be used to specify the range. Note that for some operations (such as paragraph attribute changes) the affected area may be larger than the text selection. Pass VIS_TEXT_RANGE_PARAGRAPH_SELECTION if you want the currently selected area to be used after it has been adjusted to reflect its paragraph boundaries.

You can use MSG_VIS_TEXT_GET_RANGE to return an actual VisTextRange of the selection (or paragraph selection). Pass this message a VisTextRangeContext , which specifies whether the range will include just the selection or kick out the range to its paragraph boundaries.The VisTextRange buffer that you also pass must be initially filled in (with VTR_ start as 0 and VTR_ end as TEXT_ADDRESS_PAST_END).

typedef WordFlags VisTextRangeContext;
#define VTRC_PARAGRAPH_CHANGE							0x8000
#define VTRC_CHAR_ATTR_CHANGE							0x4000
#define VTRC_PARA_ATTR_BORDER_CHANGE							0x2000

MSG_VIS_TEXT_GET_TEXT_SIZE returns the total size of the text within the text object.

MSG_VIS_TEXT_GET_RANGE

void	MSG_VIS_TEXT_GET_RANGE(
        VisTextRange		*range,
        word		context);

This message fills in a VisTextRange buffer based on the selection criteria passed in the context argument. The context information specifies whether the range will be used for a character attribute change (in which case the normal selection positions will be used) or whether the range will be used for a paragraph attribute change (in which case the boundaries of the selection will be kicked out to paragraph boundaries).

Source: Unrestricted.

Destination: Any text object.

Parameters: range VisTextRange buffer to hold the range text positions returned from the message handler. This buffer must be initially filled inwith VTR_ start as 0 and VTR_ end as TEXT_ADDRESS_PAST_END.

context
VisTextRangeContext .

Return: The VisTextRange buffer filled in.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_TEXT_SIZE

dword	MSG_VIS_TEXT_GET_TEXT_SIZE();

This message returns the current size of text.

Source: Unrestricted.

Destination: Any text object.

Return: The size of the text within the text object.

Interception: Generally not intercepted.

Replacing Text

MSG_VIS_TEXT_REPLACE_TEXT

There are many messages provided in VisTextClass to replace text within your Text Object. The most generic (and therefore versatile) of these messages is MSG_VIS_TEXT_REPLACE_TEXT. Simpler messages that you can use in special cases are documented in the following section (e.g. if you need to replace all of the text--or all of the text within a selection).

You may replace any range of text displayed within a Text Object by sending MSG_VIS_TEXT_REPLACE_TEXT, passing a pointer to a VisTextReplaceParameters structure. This structure must contain a range of text to replace and a TextReference that defines how the replacement text is referenced and stored. The structure is shown below.

typedef struct {
    VisTextRange         VTRP_range;
    dword                VTRP_insCount;
    TextReference        VTRP_textReference;
    VisTextReplaceFlags  VTRP_flags;
} VisTextReplaceParameters;

VTRP_ range defines the range of text being replaced.

VTRP_ insCount contains the number of characters in the replacement string buffer that is passed in VTRP_ textReference . If you wish to have the Text object calculate this value itself dynamically, you may pass INSERT_COMPUTE_TEXT_LENGTH in the high word of this field (and zero in the low word).

VTRP_ textReference contains a TextReference structure. This structure is explained in more detail below.

VTRP_ flags contains a bitfield of VisTextReplaceFlags . Valid flags are:

VTRF_FILTER
This flag indicates that the passed replacement string should be passed through the Text object's VTI_ filters , if any, before it is inserted into the Text object.
VTRF_KEYBOARD_INPUT
This flag indicates that the text being inserted is coming from the keyboard rather than from some other source.
VTRF_USER_MODIFICATION
This flag indicates that the text being inserted is due to a user modification. (User modifications will mark the Text object dirty; text inserted due to an application-initiated action may not mark the Text object dirty.)
VTRF_UNDO
This flag indicates that the text being inserted is due to an UNDO operation.
VTRF_DO_NOT_SEND_CONTEXT_UPDATE
This flag is internally set. It speeds up context notifications during global replaces, ensuring that the context is only updated after the entire operation has finished. Do not set this flag yourself.

The TextReference structure is shown below:

typedef struct {
    TextReferenceType     TR_type;
    TextReferenceUnion    TR_ref;
} TextReference

The TextReferenceType indicates what structure is referenced within the TextReferenceUnion . For each case, the reference is a structure of different arguments and size. These types are listed below, and correspond to teh indicated matching structure:

TRT_POINTER /* TextReferencePointer */
TRT_SEGMENT_CHUNK /* TextReferenceSegmentChunk */
TRT_OPTR /* TextReferenceBlockChunk */
TRT_BLOCK /* TextReferenceBlock */
TRT_VM_BLOCK /* TextReferenceVMBlock */
TRT_DB_ITEM /* TextReferenceDBItem */
TRT_HUGE_ARRAY /* TextReferenceHugeArray */

The TextReferenceUnion may be any of the following:

typedef union {
    TextReferencePointer       TRU_pointer;
    TextReferenceSegmentChunk  TRU_segChunk;
    TextReferenceBlockChunk    TRU_blockChunk;
    TextReferenceBlock         TRU_block;
    TextReferenceVMBlock       TRU_vmBlock;
    TextReferenceDBItem        TRU_dbItem;
    TextReferenceHugeArray     TRU_hugeArray;
} TextReferenceUnion;

The formats of these individual reference structures are contained within the Structures Reference book.

MSG_VIS_TEXT_REPLACE_TEXT

Boolean	MSG_VIS_TEXT_REPLACE_TEXT(
        VisTextReplaceParameters				*params);

This message replaces the text string within a text object (either a VisText or a GenText object) with the text referenced within the passed structure.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: *params A pointer to a VisTextReplaceParameters structure.

Return: true if an error occurred; false if the Text object's text is replaced.

Interception: Generally not intercepted.

Replacing Text All At Once

MSG_VIS_TEXT_REPLACE_ALL_PTR, MSG_VIS_TEXT_REPLACE_ALL_OPTR, MSG_VIS_TEXT_REPLACE_ALL_BLOCK, MSG_VIS_TEXT_REPLACE_ALL_VM_BLOCK, MSG_VIS_TEXT_REPLACE_ALL_DB_ITEM, MSG_VIS_TEXT_REPLACE_ALL_HUGE_ARRAY

You may replace the text displayed within a Text Object all at once with the following messages. Any previous text within the text object will be replaced with new text. The new text may come from any of the formats previously described. The format of this source determines which message you should use to replace the text.

MSG_VIS_TEXT_REPLACE_ALL_PTR replaces the text with text referenced by a simple pointer to a text string.

MSG_VIS_TEXT_REPLACE_ALL_OPTR replaces the text with text referenced by an optr. (This is the format that non-large text objects store their text.)

MSG_VIS_TEXT_REPLACE_ALL_BLOCK replaces the text with text residing in a global memory block.

MSG_VIS_TEXT_REPLACE_ALL_VM_BLOCK replaces the text with text residing in a VM block. Text objects that use the large model usually store their text within a VM block, so this message is useful for transferring text from one VisText object to another, for example.

MSG_VIS_TEXT_REPLACE_ALL_DB_ITEM replaces the text with text from a database item.

MSG_VIS_TEXT_REPLACE_ALL_HUGE_ARRAY replaces the text with text from a huge array.

MSG_VIS_TEXT_REPLACE_ALL_PTR

void	MSG_VIS_TEXT_REPLACE_ALL_PTR(
        const char 		*text,
        word 		textLen);

This message replaces the text string within a text object (either a VisText or a GenText object) with the text referenced by the passed pointer.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: text A pointer to a text string.

textLen
The text length (in characters) or zero if null-terminated.

Return: Nothing. The Text object's text is replaced.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_ALL_OPTR

void	MSG_VIS_TEXT_REPLACE_ALL_OPTR(
        optr o,
        word textLen);

This message replaces the text string within a text object (either a VisText or a GenText) with the text referenced by the passed optr.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: o The optr of the chunk containing the text.

textLen
Length of the text string or zero if null-terminated.

Return: Nothing. The text object's text is replaced.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_ALL_BLOCK

void	MSG_VIS_TEXT_REPLACE_ALL_BLOCK(
        word block,
        word textLen);

This message replaces the text string within a text object (either a VisText or a GenText) with the text within the passed data block.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: block The handle of the data block containing the text to use in the replacement operation.

textLen
The length of the text or zero if null-terminated.

Return: Nothing. The text object's text is replaced.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_ALL_VM_BLOCK

void	MSG_VIS_TEXT_REPLACE_ALL_VM_BLOCK(
        VMFileHandle		file,
        VMBlockHandle		block,
        word 		textLen);

This message replaces the text string within a text object (either a VisText or a GenText) with the text within the passed data block.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: file The handle of the VM file containing the text.

block
The VM block handle of the block within the VM file.
textLen
The length of the text or zero if null-terminated.

Return: Nothing. The text object's text is replaced.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_ALL_DB_ITEM

void	MSG_VIS_TEXT_REPLACE_ALL_DB_ITEM(
        VMFileHandle 		file,
        DBGroup 		group,
        DBItem		item);

This message replaces the text string within a text object (either a VisText or a GenText) with the text within the passed database item. The text is assumed to be null-terminated.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: file The handle of the database item's associated VM file.

group
The database group containing the text.
item
The DB item containing the text.

Return: Nothing. The text object's text is replaced.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_ALL_HUGE_ARRAY

void	MSG_VIS_TEXT_REPLACE_ALL_HUGE_ARRAY(
        VMFileHandle		file,
        VMBlockHandle		hugeArrayBlock,
        word		textLen);

This message replaces the text string within a text object (either a VisText or a GenText) with the text within the passed HugeArray.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: file The handle of the huge array's associated VM file.

hugeArrayBlock
The VM block handle of the huge array directory block.
textLen
The length of the text or zero if null-terminated.

Return: Nothing. The text object's text is replaced.

Interception: Generally not intercepted.

Replacing the Text Within a Selection

MSG_VIS_TEXT_REPLACE_SELECTION_PTR, MSG_VIS_TEXT_REPLACE_SELECTION_OPTR, MSG_VIS_TEXT_REPLACE_SELECTION_BLOCK, MSG_VIS_TEXT_REPLACE_SELECTION_VM_BLOCK, MSG_VIS_TEXT_REPLACE_SELECTION_DB_ITEM, MSG_VIS_TEXT_REPLACE_SELECTION_HUGE_ARRAY

The text object library automatically allows text selection. By using the mouse (or other device, depending on the specific UI), a user may select any section of text and perform operations on this selected text. The following messages replace any selected text with text from one of the described sources. What message you use depends on what type of source your text comes from.

Note that if no text is selected, these messages will enter text at the current cursor position. This allows these messages either to replace selected text or to "insert" text if none is selected. You may override this insertion behavior by setting paragraph attributes. In those cases, text entered at the current position will overstrike current text.

MSG_VIS_TEXT_REPLACE_SELECTION_PTR

void	MSG_VIS_TEXT_REPLACE_SELECTION_PTR(
        const char		*text,
        word		textLen);

This message replaces the current selection within a text object with the text contained in the passed string pointer. If no text is currently selected, the text will be inserted at the current cursor position.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: text The pointer to the character string.

textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_SELECTION_OPTR

void	MSG_VIS_TEXT_REPLACE_SELECTION_OPTR(
        optr	o,
        word	textLen);

This message replaces the current selection within a text object with the text in the chunk specified by o . If no text is currently selected, the text will be inserted at the current cursor position.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: o The optr of the text chunk.

textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_SELECTION_BLOCK

void	MSG_VIS_TEXT_REPLACE_SELECTION_BLOCK(
        word	block,
        word	textLen);

This message replaces the current selection within a text object with the text contained in the passed block. If no text is currently selected, the text will be inserted at the current cursor position.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: block Handle of the text block.

textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_SELECTION_VM_BLOCK

void	MSG_VIS_TEXT_REPLACE_SELECTION_VM_BLOCK(
        VMFileHandle		file,
        VMBlockHandle		block,
        word		textLen);

This message replaces the current selection within a text object by the text contained in the passed VM block. If no text is currently selected, the text will be inserted at the current cursor position.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: file Handle of the VM file containing the text.

block
Handle of the block within the VM file.
textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_SELECTION_DB_ITEM

void	MSG_VIS_TEXT_REPLACE_SELECTION_DB_ITEM(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This message replaces the current selection within a text object with the text contained in the passed database item. If no text is currently selected, the text will be inserted at the current cursor position. The text is assumed to be null-terminated.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: file The handle of the VM file containing the text.

group
The database group containing the text.
item
The database item.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_SELECTION_HUGE_ARRAY

void	MSG_VIS_TEXT_REPLACE_SELECTION_HUGE_ARRAY(
        VMFileHandle		file,
        VMBlockHandle		hugeArrayBlock,
        word		textLen);

This message replaces the current selection within a text object with the text contained in the passed huge array. If no text is currently selected, the text will be inserted at the current cursor position.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: file The VM file containing the huge array.

hugeArrayBlock
The VM block handle of the huge array directory block.
textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

Appending the Text

MSG_VIS_TEXT_APPEND_PTR, MSG_VIS_TEXT_APPEND_OPTR, MSG_VIS_TEXT_APPEND_BLOCK, MSG_VIS_TEXT_APPEND_VM_BLOCK, MSG_VIS_TEXT_APPEND_DB_ITEM, MSG_VIS_TEXT_APPEND_HUGE_ARRAY

In many cases, you may not want to replace text but instead add it to the end of the current text. You may use any of the following messages to append text to your text object. Again, the new text may come from any of the formats previously described. The format of this source determines which message you should use to add the text.

MSG_VIS_TEXT_APPEND_PTR

void	MSG_VIS_TEXT_APPEND_PTR(
        const char		*text,
        word		textLen);

This message appends text to a text object; the text is added at the end of the current text. None of the previous text is changed.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: text The pointer to the character string.

textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_APPEND_OPTR

void	MSG_VIS_TEXT_APPEND_OPTR(
        optr	o,
        word	textLen);

This message adds text to a text object; the text is added at the end of the current text. None of the previous text is changed.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: o The optr of the text chunk.

textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_APPEND_BLOCK

void	MSG_VIS_TEXT_APPEND_BLOCK(
        word	block,
        word	textLen);

This message appends text to a text object; the text is added at the end of the current text. None of the previous text is changed.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: block Handle of the text block.

textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_APPEND_VM_BLOCK

void	MSG_VIS_TEXT_APPEND_VM_BLOCK(
        VMFileHandle		file,
        VMBlockHandle		block,
        word		textLen);

This message appends text to a text object; the text is added at the end of the current text. None of the previous text is changed.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file Handle of the VM file containing the text.

block
Handle of the block within the VM file.
textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_APPEND_DB_ITEM

void	MSG_VIS_TEXT_APPEND_DB_ITEM(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This message appends text to a text object; the text is added at the end of the current text. None of the previous text is changed. The text is assumed to be null-terminated.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The handle of the VM file containing the text.

group
The database group containing the text.
item
The database item.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_APPEND_HUGE_ARRAY

void	MSG_VIS_TEXT_APPEND_HUGE_ARRAY(
        VMFileHandle		file,
        VMBlockHandle		hugeArrayBlock,
        word		textLen);

This message appends text to a text object; the text is added at the end of the current text. None of the previous text is changed.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file containing the huge array.

hugeArrayBlock
The block handle of the huge array.
textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

Retrieving the Text

MSG_VIS_TEXT_GET_ALL_PTR, MSG_VIS_TEXT_GET_ALL_OPTR, MSG_VIS_TEXT_GET_ALL_BLOCK, MSG_VIS_TEXT_GET_ALL_VM_BLOCK, MSG_VIS_TEXT_GET_ALL_DB_ITEM, MSG_VIS_TEXT_GET_ALL_HUGE_ARRAY

You may also retrieve the text from your text object and place its text into any of the previously mentioned formats. The format of your destination determines which message you should use.

MSG_VIS_TEXT_GET_ALL_PTR

word	MSG_VIS_TEXT_GET_ALL_PTR(
        const char		*text);

This message retrieves the entire text of a text object and copies it to the buffer passed. The text within the text object is unchanged.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: text The pointer to a locked or fixed buffer. The buffer must be large enough to accommodate all the text.

Return: The length of the null-terminated string not counting the null character.

text
The buffer pointed to by text will contain the null-terminated text of the text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_ALL_OPTR

word	MSG_VIS_TEXT_GET_ALL_OPTR(
        optr	o);

This message retrieves the entire text of a text object and copies it into the chunk specified by o . The text within the text object is unchanged.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: o The optr of the chunk into which the text will be copied. Pass a valid memory handle with a null chunk handle to have the message allocate a new chunk.

Return: The chunk handle of the resized (or new) chunk. The chunk will always exist upon return and will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_ALL_BLOCK

word	MSG_VIS_TEXT_GET_ALL_BLOCK(
        word	block);

This message retrieves the entire text of a text object and copies it into the passed data block.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: block Handle of the memory block into which the text will be copied. Pass a null handle to have the message allocate a new block.

Return: The handle of the resized (or new) block. The block will always exist upon return and will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_ALL_VM_BLOCK

word	MSG_VIS_TEXT_GET_ALL_VM_BLOCK(
        VMFileHandle		file,
        VMBlockHandle		block);

This message retrieves the entire text of a text object and copies it into the passed VM block.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file handle of the VM file.

block
The VM block handle of the VM block into which the text will be copied. Pass a null handle to have the message allocate a new VM block in the file.

Return: The VM block handle of the resized (or new) VM block. The VM block will always exist (assuming a valid VM file was passed), and it will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_ALL_DB_ITEM

DBGroupAndItem MSG_VIS_TEXT_GET_ALL_DB_ITEM(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This message retrieves the entire text of a text object and copies it into the passed database item.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file handle of the VM file.

group
The database group number (may be DB_UNGROUPED--if so, a new group will be returned as part of the return value).
item
The item number of the database item into which the text will be copied. Pass zero to allocate a new database item in the specified group .

Return: The DBGroupAndItem representing the resized (or new) DB item into which the text was copied. The item will always exist (assuming a valid VM file was specified) upon return, and it will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_ALL_HUGE_ARRAY

word	MSG_VIS_TEXT_GET_ALL_HUGE_ARRAY(
        VMFileHandle		file,
        VMBlockHandle		hugeArrayBlock);

This message retrieves the entire text of a text object and copies it into the passed huge array block.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file handle of the VM file containing the huge array.

hugeArrayBlock
The VM block handle of the huge array. Pass a null handle to have the message allocate a new huge array.

Return: The VM block handle of the first block of the resized (or new) huge array. The huge array will always exist upon return (assuming a valid VM file was specified), and it will contain at least the terminating null character.

Interception: Generally not intercepted.

Retrieving the Text Within a Selection

MSG_VIS_TEXT_GET_SELECTION_PTR, MSG_VIS_TEXT_GET_SELECTION_OPTR, MSG_VIS_TEXT_GET_SELECTION_BLOCK, MSG_VIS_TEXT_GET_SELECTION_VM_BLOCK, MSG_VIS_TEXT_GET_SELECTION_DB_ITEM, MSG_VIS_TEXT_GET_SELECTION_HUGE_ARRAY

To retrieve the text within the current selection, you may send any of the following messages to your text object. If no text is currently selected no text will be retrieved.

MSG_VIS_TEXT_GET_SELECTION_PTR

word	MSG_VIS_TEXT_GET_SELECTION_PTR(
        const char		*text);

This message retrieves the currently selected text and stores it in the character string referenced by the passed pointer. The text remains selected and intact in the original text object. The passed buffer must be large enough to accommodate the text.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: text The pointer to the character string.

Return: The length of the string not including the terminating null character.

text
The buffer pointed to by text will contain the null-terminated character string.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_SELECTION_OPTR

word	MSG_VIS_TEXT_GET_SELECTION_OPTR(
        optr	o);

This message retrieves the currently selected text and copies it into the passed chunk. The text remains selected and intact in the original text object.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: o The optr of the text chunk into which the text will be copied. Pass a valid memory handle and a null chunk handle to have the message allocate a new chunk in the give block.

Return: The chunk handle of the resized (or new) chunk containing the text. The chunk will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_SELECTION_BLOCK

word	MSG_VIS_TEXT_GET_SELECTION_BLOCK(
        word	block);

This message retrieves the currently selected text and copies it into the passed data block. The text remains selected and intact in the original text object.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: block Handle of the text block to place the text into. Pass a null handle to have the message allocate a new memory block.

Return: The memory handle of the resized (or new) block containing the selected text. The block will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_SELECTION_VM_BLOCK

word	MSG_VIS_TEXT_GET_SELECTION_VM_BLOCK(
        VMFileHandle		file,
        VMBlockHandle		block);

This message retrieves the currently selected text and copies it into the passed VM block. The text remains selected and intact in the original text object.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file handle of the VM file.

block
The VM block handle of the VM block into which the text will be copied. Pass a null block handle to have the message allocate a new VM block in the file.

Return: The VM block handle of the resized (or new) VM block. The VM block will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_SELECTION_DB_ITEM

DBGroupAndItem MSG_VIS_TEXT_GET_SELECTION_DB_ITEM(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This message retrieves the currently selected text and copies it into the given database item. The text remains selected and intact in the original text object.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file handle of the VM file.

group
The group number of the passed item (if DB_UNGROUPED, a group will be determined).
item
The item number of the database item into which the text will be copied. Pass zero to have the message allocate a new database item in the specified group and file.

Return: The DBGroupAndItem representing the resized (or new) database item. The item will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_SELECTION_HUGE_ARRAY

word	MSG_VIS_TEXT_GET_SELECTION_HUGE_ARRAY(
        VMFileHandle		file,
        VMBlockHandle		hugeArrayBlock);

This message retrieves the currently selected text and copies it into the passed huge array. The text remains selected and intact in the original text object.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file handle of the VM file.

hugeArrayBlock
The VM block handle of the huge array into which the text will be copied. Pass a null handle to have the message allocate a new huge array in the passed VM file.

Return: The VM block handle of the resized (or new) huge array block. The huge array will contain at least the terminating null character.

Interception: Generally not intercepted.

Other Operations on the Text

MSG_VIS_TEXT_DELETE_ALL, MSG_VIS_TEXT_DELETE_SELECTION

To delete the entire contents of a text object, send it MSG_VIS_TEXT_DELETE_ALL . This message will also resize the text's chunk to its minimum size. To delete only the current selection, send the text object MSG_VIS_TEXT_DELETE_SELECTION . No text will be selected after this message is sent.

MSG_VIS_TEXT_DELETE_ALL

void	MSG_VIS_TEXT_DELETE_ALL();

This message deletes the entire contents of a text object's text chunk. The chunk will be resized to zero.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: None.

Return: Nothing

Interception: Generally not intercepted.

MSG_VIS_TEXT_DELETE_SELECTION

void	MSG_VIS_TEXT_DELETE_SELECTION();

This message deletes the currently selected text. The text after the deletion will be automatically repositioned, and the text chunk will be resized.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

Marking the Text Selection

MSG_VIS_TEXT_GET_SELECTION_RANGE, MAG_VIS_TEXT_SELECT_RANGE, MSG_VIS_TEXT_SELECT_RANGE_SMALL, MSG_VIS_TEXT_SELECT_ALL, MSG_VIS_TEXT_SELECT_START, MSG_VIS_TEXT_SELECT_END, MSG_VIS_TEXT_SELECT_RELATIVE

MSG_VIS_TEXT_GET_SELECTION_RANGE returns the VisTextRange of the current selection.

MSG_VIS_TEXT_SELECT_RANGE selects the selected area of text to the passed range. This message can be used with both large and small model text objects.

MSG_VIS_TEXT_SELECT_RANGE_SMALL selects a range of text. The message must pass the VisTextRange to "select." This message only works with non-large text objects. Any previous selection will be deselected.

MSG_VIS_TEXT_SELECT_ALL selects the recipient's entire text.

MSG_VIS_TEXT_SELECT_START selects the start of the text with a VisTextRange of zero length. This places the cursor at the beginning of the text object and deselects any previous selection.

MSG_VIS_TEXT_SELECT_END selects the end of the text with a VisTextRange of zero length. This places the cursor at the end of the text object and deselects any previous selection.

MSG_VIS_TEXT_SELECT_RELATIVE moves the cursor from its current position to a new location relative to the old position. You may pass this message a relative selection range to move the cursor and select text at the new position.

MSG_VIS_TEXT_GET_SELECTION_RANGE

void	MSG_VIS_TEXT_GET_SELECTION_RANGE(
        VisTextRange		*vtr);

This message returns the range of the text object's current selection. You must pass this message a VisTextRange buffer for the message to fill in with the selection range.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: vtr A pointer to a VisTextRange structure to fill in with the selection range.

Return: The VisTextRange buffer will be filled in.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SELECT_RANGE

void	MSG_VIS_TEXT_SELECT_RANGE(@stack
        dword	end
        dword	start);

This message creates a selection for a text object. The message must pass the starting and ending character positions to mark as the text object's selection. Any previous selection will be deselected. Note that this message uses dword offsets into the text, and can therefore be used with LARGE model text objects.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: start The zero-based character position of the start of the selection.

end
The zero-based character position of the end of the selection. This must be equal to or larger than the start position.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SELECT_RANGE_SMALL

void	MSG_VIS_TEXT_SELECT_RANGE_SMALL(
        word	start,
        word	end);

This message creates a selection for a text object. The message must pass the starting and ending character positions to mark as the text object's selection. Any previous selection will be deselected.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: start The zero-based character position of the start of the selection.

end
The zero-based character position of the end of the selection. This must be equal to or larger than the start position.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SELECT_ALL

void	MSG_VIS_TEXT_SELECT_ALL();

This message selects the entire text of a text object as its selection.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SELECT_START

void	MSG_VIS_TEXT_SELECT_START();

This message places the cursor at the start of the text. Any current selection will be deselected.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SELECT_END

void	MSG_VIS_TEXT_SELECT_END();

This message places the cursor at the end of the text. Any current selection will be deselected.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SELECT_RELATIVE

void	MSG_VIS_TEXT_SELECT_RELATIVE(
        word	newStart,
        word	newEnd);

This message moves a selection from the current cursor position to a position relative to that position.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: newStart The number of character positions from the current cursor position to start the new selection.

newEnd
number of character positions from the current cursor position to end the new selection. This value must be equal to or greater than newStart .

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Text Object Chunks | Next: 4.2 Lines and Fields


Lines and Fields

The Text Objects: 4.2 Text Object Chunks: Lines and Fields

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 The Text | Next: 4.3 Character Runs

Each text object contains a chunk storing its line and field information. The line and field information is internal; you should not alter it.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 The Text | Next: 4.3 Character Runs


Character Runs

The Text Objects: 4.3 Text Object Chunks: Character Runs

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Lines and Fields | Next: 4.4 Paragraph Runs

The character attributes of your text object may be singular or multiple. Singular character attributes have one common set of character attributes for use by all characters within a text object. Multiple character attributes store their characteristics in "runs" which allow separate characters in the same object to show different character attributes.

Singular Character Attributes

In many cases, your text object will need only one set of attributes for all characters. In those cases, the text object's character attributes instance field (VTI_ charAttrRuns for VisText, ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR or ATTR_GEN_TEXT_CHAR_ATTR for GenText) will store a single set of attributes. This word of data will contain either a 16-bit record or a ChunkHandle to a VisTextCharAttr structure. Those attributes will be exhibited by all characters in that text object.

Unless your VisTextStorageFlags specify VTSF_MULTIPLE_CHAR_ATTRS, your character attributes will be singular. (For GenText, this field is set automatically if using the singular attributes shown above.) All text within the text object will exhibit the same characteristics; also, any changes to the character attributes will affect every character in the entire text. If you need your text object to allow different characters to show different attributes (such as in a word processor), see Multiple Character Attributes .

If your character attributes are singular, you have two choices: use a default set of characteristics or use a more complex (and complete) set of characteristics. For most simple generic text objects, the default set offers enough variety to accomplish most goals. If you need other character styles not offered in the default set, you will have to use the complete set of character attributes.

To use the default character attributes with a VisText, specify VTSF_DEFAULT_CHAR_ATTRS in your VisTextStorageFlags . The default set uses the VisTextDefaultCharAttr record to store the character's attributes. This record offers a simple collection of character fonts, point sizes, and other attributes (see below). If you need other character styles not found in this record, you should not set VTSF_DEFAULT_CHAR_ATTRS. The text object will automatically use the VisTextCharAttr structure.

By default, text objects using the default character attributes will be set to a font of VTDF_BERKELEY, a point size of VTDS_12, and a text color of C_BLACK.

If you do not use the default character attributes record, you must use the VisTextCharAttr structure to store your text object's character attributes. (In this case, do not set the VTSF_DEFAULT_CHAR_ATTRS flag in VisTextStorageFlags .) The text object's character attributes instance field will contain a ChunkHandle to this structure instead of the VisTextDefaultCharAttr record (see Setting Character Attributes ).

Code Display 10-16 Setting Character Attributes

/* This example shows how to set the default character attributes. */
@object GenTextClass MyTextObject = {
    GTXI_text = "";
    ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR = (VTDCA_UNDERLINE |
                (VTDS_18 << VTDCA_SIZE_OFFSET) | VTDF_BERKELEY);
        /* This sets the default character attributes record (of type
         * VisTextDefaultCharAttr) to use underlined Berkeley font in
         * point size 18. */
}
/* This example shows how to use non-default singular character attributes. */
@object GenTextClass MyTextObject = {
    GTXI_text = "";
    ATTR_GEN_TEXT_CHAR_ATTR = (ChunkHandle) @MyTextCharAttrs;
        /* This is a chunk handle to the chunk holding the text
         * attributes (of type VisTextCharAttr). */
}
@chunk VisTextCharAttr MyCharAttrs =
    CHAR_ATTR_FONT_SIZE_STYLE(FID_DTC_URW_ROMAN, 32, TS_UNDERLINE);
        /* This chunk holds the record that will determine the character
         * attributes of all the GenText's text. The text will be
         * 32-point Roman, underlined. */

Multiple Character Attributes

To allow a VisText object to exhibit multiple character attributes, you should set VTSF_MULTIPLE_CHAR_ATTRS and clear VTSF_DEFAULT_CHAR_ATTR flag in the object's VisTextStorageFlags (stored in VTI_storageFlags ). For GenText, you should set ATTR_GEN_TEXT_MULTIPLE_CHAR_ATTR_RUNS . Any multiple character attribute object will not be able to use any of the default character attributes of type VisTextDefaultCharAttr .

Multiple character attributes allow your object's individual characters to exhibit different attributes. One character may be bold and italic; another may be underlined and in a different font. Any character may exhibit any of the attributes allowed within the VisTextCharAttr structure.

Multiple attributes are stored as lists of VisTextCharAttr

Character attributes are specified by "runs." Each character run is used until another run is encountered, at which point the new character attributes are used. Runs are defined by a character position within a chunk array and an associated token element. At the character position, the token corresponds to a VisTextCharAttr element. The character attributes specified in this VisTextCharAttr structure are used until the character position of the next VisTextCharAttr token.

If, for example, a GenText object contains the text "Initial Text Here" with the word "Text" underlined, the chunk array of elements would look as shown in the figure above.

Code Display 10-17 Setting Multiple Character Attribute Runs

@object GenTextClass MultipleCharAttrsObject = {
    ATTR_GEN_TEXT_MULTIPLE_CHAR_ATTR_RUNS = (ChunkHandle) @MyMultCharAttrs;
    GTXI_text = "Initial Text Here";
}
#define MCA_PLAIN			0
#define MCA_UNDERLINE			1
@CHAR_ATTR_ELEMENT_ARRAY_HEADER CharAttrElements = {
    CHAR_ATTR_FONT_SIZE_STYLE(FID_DTC_URW_ROMAN, 12, 0),								 /* Element 0 */
    CHAR_ATTR_FONT_SIZE_STYLE(FID_DTC_URW_ROMAN, 12, TS_UNDERLINE)	/* Element 1 */
};
@RUN_ARRAY_HEADER(CharAttrElements) MyMultCharAttrs = {
    TRAE_ABS(0, MCA_PLAIN),					/* Element 0 */
    TRAE_ABS(8, MCA_UNDERLINE),					/* Element 1 */
    TRAE_END
};

Changing Character Attributes

MSG_VIS_TEXT_GET_CHAR_ATTR,VisTextCharAttrFlags, VisTextCharAttrDiffs, VisTextGetAttrFlags, MSG_VIS_TEXT_SET_CHAR_ATTR_BY_DEFAULT, MSG_VIS_TEXT_SET_CHAR_ATTR, MSG_VIS_TEXT_SET_FONT_ID, MSG_VIS_TEXT_SET_POINT_SIZE, MSG_VIS_TEXT_SET_TEXT_STYLE

There may be cases in which you would like to change the attributes exhibited by certain characters in your Text object. In most cases, you can do this most easily by including one of the controllers mentioned at the end of this chapter. The controllers leave any underlying work up to the specific controller implementation.

If you wish to manually change these character attributes, however, there are several messages to retrieve and set character attributes.

MSG_VIS_TEXT_GET_CHAR_ATTR

word 	MSG_VIS_TEXT_GET_CHAR_ATTR(@stack
        VisTextGetAttrFlags 			flags,
        VisTextCharAttrDiffs 			*diffs,
        VisTextCharAttr 			*attrs,
        dword 			rangeEnd,
        dword 			rangeStart);

This message returns a buffer filled in with the VisTextCharAttr attributes of the given range of text. If VTGAF_MERGE_WITH_PASSED is passed in the VisTextGetAttrFlags , then the passed VisTextCharAttr structure will be compared with the range of text that this message is sent to (and any differences will be pointed out in the passed diffs buffer.(If this flag is not passed, any information initially in that buffer will be ignored.)

Over the passed range, different characters may exhibit different attributes; a pointer to a VisTextCharAttrDiffs structure is also passed to store information about attributes that are different across the range of text.

VisTextCharAttrDiffs contains a list of VisTextCharAttrFlags specifying what attributes are multiply present. The structure also contains the bitfields of the TextStyle and VisTextExtendedStyles present over the range of text. Any attribute differences will produce a mention in one of these records; you can think of this process as XORing the attribute bitfields together.

Possible VisTextCharAttrFlags :
VTCAF_MULTIPLE_FONT_IDS
VTCAF_MULTIPLE_POINT_SIZES
VTCAF_MULTIPLE_COLORS
VTCAF_MULTIPLE_GRAY_SCREENS
VTCAF_MULTIPLE_PATTERNS
VTCAF_MULTIPLE_TRACK_KERNINGS
VTCAF_MULTIPLE_FONT_WEIGHTS
VTCAF_MULTIPLE_FONT_WIDTHS
VTCAF_MULTIPLE_BG_COLORS
VTCAF_MULTIPLE_BG_GRAY_SCREENS
VTCAF_MULTIPLE_BG_PATTERNS
VTCAF_MULTIPLE_STYLES

Structures:

typedef struct {
    VisTextCharAttrFlags 				VTCAD_diffs;
    VisTextExtendedStyles 				VTCAD_extendedStyles;
    TextStyle 				VTCAD_textStyles;
    byte 				VTCAD_unused;
} VisTextCharAttrDiffs;

Source: Unrestricted.

Destination: Any text object.

Parameters: flags VTGAF_MERGE_WITH_PASSED to merge the retrieved text attributes with the text in the passed range.

diffs
Pointer to a VisTextCharAttrDiffs structure to store attribute differences.
attrs
Pointer to a VisTextCharAttr buffer to store the retrieved character attributes. If VTGAF_MERGE_WITH_PASSED is passed in flags, this buffer initially contains attributes to match against the retrieved attributes.
rangeEnd
End of the range (character position).
rangeStart
Beginning of the range (character position).

Return: The token of the specific character attribute run (word value) if the text object is storing runs of VisTextCharAttr structures. The attrs and diffs buffers are also filled with their relevant information.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_CHAR_ATTR_BY_DEFAULT

void	MSG_VIS_TEXT_SET_CHAR_ATTR_BY_DEFAULT(@stack
        VisTextDefaultCharAttr 				defCharAttrs,
        dword 				rangeEnd,
        dword 				rangeStart);

This message sets the character attributes passed in VisTextDefaultCharAttr over the specified range of the text object. If the text object is not in "default" character attribute mode (i.e. it is storing runs of VisTextCharAttr structures) it will translate the default attributes into their matching VisTextCharAttr attributes.

Source: Unrestricted.

Destination: Any Text object.

Parameters: defCharAttrs VisTextDefaultCharAttr record to set over the passed range of text.

rangeEnd
End of the range (Character position).
rangeStart
Start of the range (Character position).

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_CHAR_ATTR

void	MSG_VIS_TEXT_SET_CHAR_ATTR(@stack
        VisTextCharAttr 		*attrs,
        dword 		rangeEnd,
        dword 		rangeStart);

This message sets the character attributes passed in the VisTextCharAttr buffer over the specified range of the text object. If the text object is in "default" character attribute mode, it will force the text object to begin storing runs of VisTextCharAttr structures.

Source: Unrestricted.

Destination: Any Text object.

Parameters: attrs Pointer to a VisTextCharAttr buffer.

rangeEnd
End of the range (Character position).
rangeStart
Start of the range (Character position).

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_FONT_ID

@importMessage MetaTextMessages, void MSG_VIS_TEXT_SET_FONT_ID(@stack
        FontID 		fid,
        dword 		rangeEnd,
        dword 		rangeStart);

This message sets the passed FontID over the specified range of the text object. If the text object currently displays its values in "default" character attributes, it will force the text object to use character attribute runs to store its VisTextCharAttr structures.

Source: Unrestricted.

Destination: Any Text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_POINT_SIZE

@importMessage MetaTextMessages, void MSG_VIS_TEXT_SET_POINT_SIZE(@stack
        WWFixed 		pointSize,
        dword 		rangeEnd,
        dword 		rangeStart);

This message sets the passed point size over the specified range of the text object. If the text object currently displays its values in "default" character attributes, it will force the text object to use character attribute runs to store its VisTextCharAttr structures.

Source: Unrestricted.

Destination: Any Text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_TEXT_STYLE

@importMessage MetaTextMessages, void MSG_VIS_TEXT_SET_TEXT_STYLE(@stack
        word 		extBitsToClear,
        word 		extBitsToSet,
        word 		styleBitsToClear,
        word 		styleBitsToSet,
        dword 		rangeEnd,
        dword 		rangeStart);

This message sets the passed text styles over the specified range of the text object. You must specify the specific style bits to set and clear, both for the VTCA_ styles and the VTCA_ extendedStyles entries.

If the text object currently displays its values in "default" character attributes, it will force the text object to use character attribute runs to store its VisTextCharAttr structures.

Source: Unrestricted.

Destination: Any Text object.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Lines and Fields | Next: 4.4 Paragraph Runs


Paragraph Runs

The Text Objects: 4.4 Text Object Chunks: Paragraph Runs

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Character Runs | Next: 5 Using VisText

The paragraph attributes of your text object may also be singular or multiple. Singular paragraph attributes use one common set of characteristics for all the text in the text object. Multiple paragraph attributes store their characteristics in "runs" which allow separate paragraphs in the same object to use different attributes.

Singular Paragraph Attributes

You may only need your text object to exhibit a singular set of paragraph attributes. In this case, the text object's paragraph attributes instance field (VTI_ paraAttrRuns for VisText, GEN_TEXT_DEFAULT_PARA_ATTR or GEN_TEXT_PARA_ATTR for GenText) will store a single set of attributes. This word of data will contain either a 16-bit record or a ChunkHandle to a chunk of more complex attributes.

Unless your VisTextStorageFlags (in VTI_storageFlags ) specify VTSF_MULTIPLE_PARA_ATTRS, your paragraph attributes will be singular. (This flag will be set automatically for GenTexts using the above attributes.) Initially, all text within the text object will exhibit these same characteristics; also, any changes to the text object's paragraph attributes will affect the entire text. If your text object should allow different paragraphs to exhibit different attributes, see Multiple Paragraph Attributes .

If your paragraph attributes are singular, you have two choices: use a default set of characteristics or use a more complex (and complete) set of characteristics. What you use depends on the variety of choices you wish your application to have over the display of its text. For most simple generic text objects, the default set offers enough variety to accomplish most goals.

To use the default paragraph attributes, specify VTSF_DEFAULT_PARA_ATTRS in the text object's VTI_storageFlags field (for VisText) or set ATTR_GEN_TEXT_DEFAULT_PARA_ATTR (for GenText). The default set uses the VisTextDefaultParaAttr record to store the paragraph's attributes. This record offers a simple collection of paragraph justifications, tab stops and margins, and its definition is given in VisTextDefaultParaAttr . If you need other paragraph styles not found in this record, you should not set VTSF_DEFAULT_PARA_ATTRS.

By default, text objects using the default paragraph attributes will be left-justified, have default tab stops at one inch intervals, and have zero right, left, and paragraph margins.

If you do not use the default paragraph attributes record, you must use the VisTextParaAttr structure to store your text object's paragraph attributes. (In this case, do not set VTSF_DEFAULT_PARA_ATTRS in VTI_storageFlags .) The text object's paragraph attributes instance field will contain a ChunkHandle to this structure instead of the VisTextDefaultParaAttr record.

Code Display 10-18 Setting VisTextParaAttr Attributes

/* This example shows setting the initial default paragraph attributes. */
@object GenTextClass MyTextObject = {
    GTXI_text = "";
    ATTR_GEN_TEXT_DEFAULT_PARA_ATTR = ((J_CENTER << VTDPA_JUSTIFICATION_OFFSET) |
            ( (2*2) << VTDPA_LEFT_MARGIN_OFFSET) |
            ( (1*2) << VTDPA_PARA_MARGIN_OFFSET) |
            ( VTDDT_HALF_INCH << VTDPA_DEFAULT_TABS_OFFSET));
}
/* This example shows setting non-default singular paragraph attributes. */
@object GenTextClass MyTextObject = {
    GTXI_text = "";
    ATTR_GEN_TEXT_PARA_ATTR = (ChunkHandle) @MyParaAttrs;
}
@chunk VisTextParaAttr MyParaAttrs =
    PARA_ATTR_STYLE_JUST_LEFT_RIGHT_PARA(0, 0, J_CENTER, 0, 2, 1);

Multiple Paragraph Attributes

To allow your VisText object to exhibit individual paragraph attributes, you should set VTSF_MULTIPLE_PARA_ATTRS and clear the VTSF_DEFAULT_PARA_ATTR flag in the object's VTI_storageFlags record. For GenText, set the attribute ATTR_GEN_TEXT_MULTIPLE_PARA_ATTR_RUNS . Any multiple paragraph attribute object will not be able to use any of the default paragraph attributes of type VisTextDefaultParaAttr .

Multiple paragraph attributes allow your object's individual paragraphs to exhibit different attributes. One paragraph may be left justified; another may be centered with different margins. Any paragraph may exhibit any of the attributes allowed within the VisTextParaAttr structure separately.

Multiple paragraph attributes are stored in the same manner as multiple character attributes. See Character Runs for a full description of how runs are stored.

Paragraph attributes are specified by runs. Each paragraph run is used until another run is encountered, at which point the new paragraph attributes are applied and used. Runs are defined by the character position within a chunk array and an associated token element; therefore, you must take care when defining a paragraph run so that it coincides with the first character of a new paragraph. At the character position, the token corresponds to a VisTextParaAttr element. The paragraph attributes specified in this VisTextParaAttr structure are used until the next VisTextParaAttr element.

Changing Paragraph Attributes

MSG_VIS_TEXT_GET_PARA_ATTR,VisTextParaAttrFlags, VisTextParaAttrBorderFlags, VisTextParaAttrDiffs, MSG_VIS_TEXT_SET_PARA_ATTR_BY_DEFAULT, MSG_VIS_TEXT_SET_PARA_ATTR, MSG_VIS_TEXT_SET_PARA_ATTRIBUTES, MSG_VIS_TEXT_ADD_PARA_ATTR

There may be cases in which you would like to change the attributes exhibited by certain paragraphs in your Text object. As with character attributes, you can do this most easily by including one of the controllers mentioned at the end of this chapter.

If you wish to manually change these character attributes, however, there are several messages to retrieve and set character attributes.

MSG_VIS_TEXT_GET_PARA_ATTR

word 	MSG_VIS_TEXT_GET_PARA_ATTR(@stack
        VisTextGetAttrFlags 			flags,
        VisTextParaAttrDiffs 			*diffs,
        VisTextParaAttr 			*attrs,
        dword 			rangeEnd,
        dword 			rangeStart);

This message returns a buffer filled in with the VisTextParaAttr attributes of the given range of text. If VTGAF_MERGE_WITH_PASSED is passed in the VisTextGetAttrFlags , then a passed VisTextParaAttr structure will be merged with the range of text that this message is sent to. (If this flag is not passed, any information initially in that buffer will be ignored.)

Over the passed range, different paragraphs may exhibit different attributes; a pointer to a VisTextParaAttrDiffs structure is also passed to store information about attributes that are different across the range of text. This structure contains lists of VisTextParaAttrFlags and VisTextParaAttrBorderFlags specifying what paragraph and paragraph border attributes are multiply present. The structure also contains the bitfields of the VisTextParaAttrAttributes , VisTextHyphenationInfo, VisTextKeepInfo , and VisTextDropCapInfo present over the range of text.

Possible VisTextParaAttrFlags . If any of these are set, the selection exhibits the indicated multiple property over the range of text:
VTPAF_MULTIPLE_LEFT_MARGINS
VTPAF_MULTIPLE_RIGHT_MARGINS
VTPAF_MULTIPLE_PARA_MARGINS
VTPAF_MULTIPLE_LINE_SPACINGS
VTPAF_MULTIPLE_DEFAULT_TABS
VTPAF_MULTIPLE_TOP_SPACING
VTPAF_MULTIPLE_BOTTOM_SPACING
VTPAF_MULTIPLE_LEADINGS
VTPAF_MULTIPLE_BG_COLORS
VTPAF_MULTIPLE_BG_GRAY_SCREENS
VTPAF_MULTIPLE_BG_PATTERNS
VTPAF_MULTIPLE_TAB_LISTS
VTPAF_MULTIPLE_STYLES
VTPAF_MULTIPLE_PREPEND_CHARS
VTPAF_MULTIPLE_PARA_NUMBERS
Possible VisTextParaAttrBorderFlags :
VTPABF_MULTIPLE_BORDER_LEFT
VTPABF_MULTIPLE_BORDER_TOP
VTPABF_MULTIPLE_BORDER_RIGHT
VTPABF_MULTIPLE_BORDER_BOTTOM
VTPABF_MULTIPLE_BORDER_DOUBLES
VTPABF_MULTIPLE_BORDER_DRAW_INNERS
VTPABF_MULTIPLE_BORDER_ANCHORS
VTPABF_MULTIPLE_BORDER_WIDTHS
VTPABF_MULTIPLE_BORDER_SPACINGS VTPABF_MULTIPLE_BORDER_SHADOWS VTPABF_MULTIPLE_BORDER_COLORS VTPABF_MULTIPLE_BORDER_GRAY_SCREENS VTPABF_MULTIPLE_BORDER_PATTERNS

Structures:

typedef struct {
    VisTextParaAttrFlags 					VTPAD_diffs;
    VisTextParaAttrBorderFlags					VTPAD_borderDiffs;
    VisTextParaAttrAttributes					VTPAD_attributes;
    VisTextHyphenationInfo					VTPAD_hyphenationInfo;
    VisTextKeepInfo					VTPAD_keepInfo;
    VisTextDropCapInfo					VTPAD_dropCapInfo;
} VisTextParaAttrDiffs;

Source: Unrestricted.

Destination: Any text object.

Parameters: flags VTGAF_MERGE_WITH_PASSED to merge the retrieved text attributes with the text in the passed range.

diffs
Pointer to a VisTextParaAttrDiffs structure to store attribute differences.
attrs
Pointer to a VisTextParaAttr buffer to store the retrieved paragraph attributes. If VTGAF_MERGE_WITH_PASSED is passed in flags, this buffer initially contains attributes to match against the retrieved attributes.
rangeEnd
End of the range (character position).
rangeStart
Beginning of the range (character position).

Return: The token of the specific paragraph attribute run (word value) if the text object is storing runs of VisTextParaAttr structures. The attrs and diffs buffers are also filled with their relevant information.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_PARA_ATTR_BY_DEFAULT

void	MSG_VIS_TEXT_SET_PARA_ATTR_BY_DEFAULT(@stack
        VisTextDefaultParaAttr 				defParaAttrs,
        dword 				rangeEnd,
        dword 				rangeStart);

This message sets the paragraph attributes passed in VisTextDefaultParaAttr over the specified range of the text object. If the text object is not in "default" paragraph attribute mode (i.e. it is storing runs of VisTextParaAttr structures) it will translate the default attributes into their matching VisTextParaAttr attributes.

Source: Unrestricted.

Destination: Any Text object.

Parameters: defParaAttrs VisTextDefaultParaAttr record to set over the passed range of text.

rangeEnd
End of the range (Character position).
rangeStart
Start of the range (Character position).

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_PARA_ATTR

void	MSG_VIS_TEXT_SET_PARA_ATTR(@stack
        VisTextParaAttr 		*newParaAttrs,
        dword 		rangeEnd,
        dword 		rangeStart);

This message sets the paragraph attributes passed in the VisTextParaAttr buffer over the specified range of the text object. If the text object is in "default" paragraph attribute mode, it will force the text object to begin storing runs of VisTextParaAttr structures.

Source: Unrestricted.

Destination: Any Text object.

Parameters: newParaAttrs Pointer to a VisTextParaAttr buffer.

rangeEnd
End of the range (Character position).
rangeStart
Start of the range (Character position).

Interception: Generally not intercepted.

MSG_VIS_TEXT_ADD_PARA_ATTR

word	MSG_VIS_TEXT_ADD_PARA_ATTR(
        VisTextMaxParaAttr 		*paraAttr);

This message adds a given set of paragraph attributes passed in the VisTextParaAttr buffer to the paragraph "run" for that text object. The text object must not be in "default" paragraph attribute mode; it must already be storing runs of VisTextParaAttr structures.

Source: Unrestricted.

Destination: Any Text object that is storing runs of VisTextParaAttr structures.

Parameters: paraAttr P ointer to a VisTextParaAttr buffer.

Return: Token of the paragraph run entry (word-value).

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Character Runs | Next: 5 Using VisText


Using VisText

The Text Objects: 5 Using VisText

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Text Object Chunks | Next: 6 Using GenText

This section describes how to use a VisText object. At the time of documentation printing, VisTextClass is undergoing improvement and will therefore be covered fully in future documentation releases. This section currently gives much useful information about the class, however.

VisText Instance Fields shows all the instance data fields of VisTextClass . Following the display are descriptions of the individual fields.

Code Display 10-19 VisText Instance Fields

 /* These are all the instance data fields of the VisText. Many of them are used
 * internally by the class and should not be used by applications. Because they
 * are documented as internal, Geoworks reserves the right to change their meaning
 * or use at any time. Those that are internal are clearly noted as such. */
@instance ChunkHandle				VTI_text;
@instance word				VTI_charAttrRuns = VIS_TEXT_INITIAL_CHAR_ATTR;
@instance word				VTI_paraAttrRuns = VIS_TEXT_INITIAL_PARA_ATTR;
@instance VMFileHandle				VTI_vmFile = NullHandle;
@instance word				VTI_lines = 0;				/* INTERNAL */
@instance VisTextStorageFlags				VTI_storageFlags = (VTSF_DEFAULT_CHAR_ATTR |
                         VTSF_DEFAULT_PARA_ATTR);
@instance VisTextFeatures				VTI_features = 0;
@instance VisTextStates				VTI_state = 0;
@instance VisTextIntFlags				VTI_intFlags = 0;				/* INTERNAL */
@instance VisTextIntSelFlags				VTI_intSelFlags = 0;				/* INTERNAL */
@instance GSRefCountAndFlags				VTI_gsRefCount = 0;				/* INTERNAL */
@instance GStateHandle				VTI_gstate = NullHandle;				/* INTERNAL */
@instance word				VTI_gstateRegion = -1;				/* INTERNAL */
@instance dword				VTI_selectStart = 0;
@instance dword				VTI_selectEnd = 0;
@instance PointDWord				VTI_startEventPos = {0,0};				/* INTERNAL */
@instance dword				VTI_selectMinStart = 0;				/* INTERNAL */
@instance dword				VTI_selectMinEnd =0;				/* INTERNAL */
@instance dword				VTI_lastOffset = 0;				/* INTERNAL */
@instance word				VTI_goalPosition = 0;				/* INTERNAL */
@instance Point				VTI_cursorPos = {0,0};				/* INTERNAL */
@instance word				VTI_cursorRegion = 0;				/* INTERNAL */
@instance word				VTI_leftOffset = 0;				/* INTERNAL */
@instance byte				VTI_lrMargin = 0;
@instance byte				VTI_tbMargin = 0;
@instance ColorQuad				VTI_washColor = {
                    C_WHITE,
                    CF_INDEX,
                    0, 0 }
@instance word				VTI_maxLength = 10000;
@instance VisTextFilters				VTI_filters = 0;
@instance optr				VTI_output;
@instance WBFixed				VTI_height = {0,0};				/* INTERNAL */
@instance word				VTI_lastWidth = -1;				/* INTERNAL */
@instance TimerHandle				VTI_timerHandle = NullHandle;				/* INTERNAL */
@instance word				VTI_timerID = 0;				/* INTERNAL */

VTI_ text stores the ChunkHandle of the object's text chunk. This chunk will be stored within a chunk in the same resource as the text object. See The Text for a full discussion of how to manipulate and alter text.

VTI_ storageFlags contains flags related to how a VisText object stores its paragraph and character attributes. The composition of these flags affects the function of VTI_ charAttrRuns and VTI_ paraAttrRuns .

VTI_ charAttrRuns stores the character attributes for the text object. Depending on VTI_ storageFlags , this instance field may contain a 16-bit record, a ChunkHandle to a VisTextCharAttr structure, or a ChunkHandle to a chunk array of character style runs.

VTI_ paraAttrRuns stores the paragraph attributes for the text object. Depending on GTI_ storageFlags , this instance field may contain a 16-bit record, a ChunkHandle to a VisTextParaAttr structure, or a ChunkHandle to a chunk array of paragraph style runs.

VTI_ features stores a VisTextFeatures record to display text within this text object. These features allow such utilities as word wrapping, hyphenation, smart quotes, etc.

VTI_ state stores different states of type VisTextStates that the text object can operate under, such as editable, selectable, targetable, etc.

VTI_ vmFile stores the handle for a text object's associated VM file; this VM file will store the text if the text object incorporates the large model. This instance field is only used for text objects using the large model.

VTI_ selectStart stores the character position of the selection's start. VTI_ selectEnd stores the character position of the selection's end. If both the selection start and selection end coincide, there is no selected text.

VTI_lrMargin stores the left and right margins of the paragraph in points. VTI_ tbMargin stores the top and bottom margins of the paragraph in points.

VTI_ washColor stores the background color (of type ColorQuad ) of the text object.

VTI_ maxLength stores the maximum number of characters allowed in this text object (for non-large objects).

VTI_ filters stores the VisTextFilters in use by this text object. Filters allow your text object to accept or reject certain sets of characters.

VTI_ output stores the destination for actions taken by this text object.

Code Display 10-20 VisText Variable Data

@vardata word		ATTR_VIS_TEXT_TYPE_RUNS;
@vardata word		ATTR_VIS_TEXT_GRAPHICS_RUNS;
@vardata word		ATTR_VIS_TEXT_REGION_ARRAY;
@vardata word		ATTR_VIS_TEXT_STYLE_ARRAY;
@vardata word		ATTR_VIS_TEXT_NAME_ARRAY;
@vardata word		ATTR_VIS_TEXT_SELECTED_TAB;
@vardata void		ATTR_VIS_TEXT_DO_NOT_INTERACT_WITH_SEARCH_CONTROL;
@vardata VisTextExtendedFilterType ATTR_VIS_TEXT_EXTENDED_FILTER;
typedef struct {
    word			VTSD_count;
    VisTextRange			VTSD_recalcRange;
    VisTextRange			VTSD_selectRange;
    dword			VTSD_showSelectionPos;
    WordFlags			VTSD_notifications;
    byte			VTSD_needsRecalc;
} VisTextSuspendData;
@vardata VisTextSuspendData ATTR_VIS_TEXT_SUSPEND_DATA;
@vardata void		ATTR_VIS_TEXT_NOTIFY_CONTENT;
@vardata void		ATTR_VIS_TEXT_DO_NOT_NOTIFY_CONTENT;
@vardata void		ATTR_VIS_TEXT_SEND_CONTEXT_NOTIFICATIONS_EVEN_IF_NOT_FOCUSED;
typedef struct {
    wchar		VTCFD_startOfRange;
    wchar		VTCFD_endOfRange;
} VisTextCustomFilterData;
@vardata ChunkHandle ATTR_VIS_TEXT_CUSTOM_FILTER;
@vardata word		ATTR_VIS_TEXT_CHAR_ATTR_INSERTION_TOKEN;
@vardata word		ATTR_VIS_TEXT_TYPE_INSERTION_TOKEN;
@vardata void		ATTR_VIS_TEXT_UPDATE_VIA_PROCESS;
@vardata void		ATTR_VIS_TEXT_DOES_NOT_ACCEPT_INK;
@vardata WBFixed		ATTR_VIS_TEXT_MINIMUM_SINGLE_LINE_HEIGHT;
@vardata word		ATTR_VIS_TEXT_ADD_SPACE_TO_ALL_LINES;
@vardata void		ATTR_VIS_TEXT_DONT_SHOW_POSITION_ON_GAINED_TARGET_EXCL;
@vardata void		ATTR_VIS_TEXT_SHOW_POSITION_ON_GAINED_TARGET_EXCL;
@vardata void		ATTR_VIS_TEXT_DONT_BEEP_ON_INSERTION_ERROR;

ATTR_VIS_TEXT_TYPE_RUNS specifies the Type runs for this text object. You should not need type runs unless you need hyperlink capabilities. ATTR_VIS_TEXT_GRAPHIC_RUNS specifies a graphics run to use within the text object.

ATTR_VIS_TEXT_REGION_ARRAY specifies the array of regions defined for this text object. Text within the VisText will flow from each of the defined regions to the next region. ATTR_VIS_TEXT_STYLE_ARRAY specifies the name array of styles used in the VisText's style sheets.

ATTR_VIS_TEXT_SELECTED_TAB specifies the position of the currently-selected tab, if any.

ATTR_VIS_TEXT_DO_NOT_INTERACT_WITH_SEARCH_CONTROL instructs the text object not to send the search-control notification.

ATTR_VIS_TEXT_EXTENDED_FILTERS stores the VisTextExtendedFilterType to use instead of the VisTextFilters stored within VTI_ filters . Extended filters offer more powerful (and complex) filtering mechanisms for accepting and rejecting text. Including this attribute will affect the performance of your text object, however.

ATTR_VIS_TEXT_SUSPEND_DATA stores data to be used by the text object during a SUSPEND/UNSUSPEND operation ( VisTextSuspendData ).

ATTR_VIS_TEXT_SEND_CONTEXT_NOTIFICATIONS_EVEN_IF_NOT_FOCUSED sends out context notification even if the object is not focused. Applications must make sure that objects with this attribute do not get modified while another text object has the focus.

ATTR_VIS_TEXT_UPDATE_VIA_PROCESS indicates that status updates must be sent via the process object (usually because there are targetable text objects in multiple threads).

ATTR_VIS_TEXT_DOES_NOT_ACCEPT_INK indicates that the VisText object will not accept ink.

ATTR_VIS_TEXT_MINIMUM_SINGLE_LINE_HEIGHT defines the height of one-line text objects so that custom graphics, etc. can be placed inside the text object.

ATTR_VIS_TEXT_ADD_SPACE_TO_ALL_LINES adds additional spacing above all lines in a text object, and it takes a word argument. This optional attribute is intended to be added to objects that use accent characters that may draw above the top of the text's "font box." This spacing value differs depending on the system that text is implemented on; only use this attribute if redraws are not drawing your text characters properly. This attribute will also affect any text lines that contain borders, adding additional space between the text and the border.

ATTR_VIS_TEXT_DONT_SHOW_POSITION_ON_GAINED_TARGET_EXCL and ATTR_VIS_TEXT_SHOW_POSITION_ON_GAINED_TARGET_EXCL affect the display of the cursor within a text object upon gaining the target exclusive. By default, a VisText object will display the cursor upon gaining the target; a VisLargeText object, on the other hand, will not display this position. You may use either of these hints to override this default behavior.

ATTR_VIS_TEXT_DONT_BEEP_ON_INSERTION_ERROR indicates that the VisText object should not call the default error sound routine in cases where an insertion error occurs.

VisText Features

VisText States

VisText VM File Storage

Text Filters

Key Functions

Setting Text Confines

Output Messages

Getting Geometry Information

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Text Object Chunks | Next: 6 Using GenText


VisText Features

The Text Objects: 5.1 Using VisText: VisText Features

| Up: GEOS SDK TechDocs | Up | Prev: 5 Using VisText | Next: 5.2 VisText States
VTI_features, VisTextFeatures, MSG_VIS_TEXT_SET_FEATURES, MSG_VIS_TEXT_GET_FEATURES

VTI_ features stores a bitfield of VisTextFeatures specifying whether a particular feature is enabled in the Text object. These flags are listed below.

VTF_NO_WORD_WRAPPING
If set, no word-wrapping is allowed. Characters added after the end of the line will start at the next line.
VTF_AUTO_HYPHENATE
If set, the text object will auto-hyphenate words that span a line break.
VTF_ALLOW_SMART_QUOTES
If set, smart quotes will be allowed if they are enabled.
VTF_ALLOW_UNDO
If set, undo operations will be allowed on the text object. (The text object will handle MSG_META_UNDO.)
VTF_SHOW_HIDDEN_TEXT
If set, hidden text will be displayed. This feature is currently not implemented.
VTF_OUTLINE_MODE
If set, text will be shown in outline mode. This feature is currently not implemented.
VTF_DONT_SHOW_SOFT_PAGE_BREAKS
If set, any soft page breaks (i.e. any page breaks that are not C_PAGE_BREAK) will be ignored.
VTF_DONT_SHOW_GRAPHICS
If set, any graphics in the text object will not be displayed.
VTF_TRANSPARENT
If set, the text object will not do a background wash behind the text after it draws.
VTF_USE_50_PCT_TEXT_MASK
If set, the text object will use a 50% draw mask when drawing, regardless of other character attribute runs. This is used by the GenText object to show a disabled state.

MSG_VIS_TEXT_SET_FEATURES

void	MSG_VIS_TEXT_SET_FEATURES(
        VisTextFeatures		bitsToSet,
        VisTextFeatures		bitsToClear);

This message sets a text object's VisTextFeatures (VTI_ features ).

Source: Unrestricted.

Destination: Any text object.

Parameters: bitsToSet VisTextFeatures to add.

bitsToClear
VisTextFeatures to remove.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_FEATURES

VisTextFeatures MSG_VIS_TEXT_GET_FEATURES();

This message retrieves the text object's VisTextFeatures (VTI_ features ).

Source: Unrestricted.

Destination: Any text object.

Parameters: None.

Return: VisTextFeatures in use by the text object.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Using VisText | Next: 5.2 VisText States


VisText States

The Text Objects: 5.2 Using VisText: VisText States

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 VisText Features | Next: 5.3 VisText VM File Storage
VTI_state, VisTextStates, MSG_VIS_TEXT_GET_STATE, MSG_VIS_TEXT_GET_USER_MODIFIED_STATE, MSG_VIS_TEXT_SET_NOT_USER_MODIFIED, MSG_VIS_TEXT_SET_USER_MODIFIED, MSG_VIS_TEXT_ENTER_OVERSTRIKE_MODE, MSG_VIS_TEXT_ENTER_INSERT_MODE

VTI_state stores a bitfield of VisTextStates that determine the ability of the user to interact with the text object. These flags are listed below.

VTS_EDITABLE
If set, the text object is editable. You must set this flag to allow the user to write text into a text object.
VTS_SELECTABLE
If set, text within the text object may be selected. Text may be selectable without it being editable; it can be copied but not cut in that case.
VTS_TARGETABLE
If set, the text object is capable of acting in the target hierarchy. This flag must be set for text objects that interact with controllers.
VTS_ONE_LINE
If set, this object is a simple one-line text object. The text will not scroll vertically, though it may scroll horizontally.
VTS_SUBCLASS_VIRT_PHYS_TRANSLATION
If set, the mapping of virtual attributes to physical attributes will be effected by sending a message (which can be subclassed) rather than through a routine. Normally, this mapping is done through the faster direct routine call, but this flag allows application-specific operations.
VTS_OVERSTRIKE_MODE
If set, the text object is in overstrike mode, not its default insertion mode. Text entered at the cursor will overwrite following text; it will not insert that text at the cursor position.
VTS_USER_MODIFIED
If set, the text inside the object has been changed since the last time the text object sent out its apply message in GTXI_ applyMsg . You can send the text object MSG_VIS_TEXT_SET_NOT_USER_MODIFIED or MSG_VIS_TEXT_SET_USER_MODIFIED to clear or set this flag.

MSG_VIS_TEXT_GET_STATE

VisTextStates MSG_VIS_TEXT_GET_STATE();

This message retrieves the text object's VisTextStates (VTI_state).

Source: Unrestricted.

Destination: Any text object.

Parameters: None.

Return: VisTextStates in use by the text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_USER_MODIFIED_STATE

Boolean	MSG_VIS_TEXT_GET_USER_MODIFIED_STATE();

This message returns the state of the text object's VTS_MODIFIED flag in its VTI_state entry.

Source: Unrestricted.

Destination: Any text object.

Parameters: None.

Return: true if object has been modified .

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_NOT_USER_MODIFIED

void	MSG_VIS_TEXT_SET_NOT_USER_MODIFIED();

This message clears the text object's VTS_USER_MODIFIED flag.

Source: Unrestricted.

Destination: Any text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_USER_MODIFIED

void	MSG_VIS_TEXT_SET_USER_MODIFIED();

This message sets the text object's VTS_USER_MODIFIED flag.

Source: Unrestricted.

Destination: Any text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_MODIFY_EDITABLE_SELECTABLE

void	MSG_VIS_TEXT_MODIFY_EDITABLE_SELECTABLE(
        VisTextStates		setBits,
        VisTextStates		clearBits);

This message modifies the text object's VTS_EDITABLE and VTS_SELECTABLE states. These are the only VisTextStates you should modify with this message.

Source: Unrestricted.

Destination: Any text object.

Parameters: setBits The VTS_EDITABLE and/or VTS_SELECTABLE states to set.

clearBits
The VTS_EDITABLE and/or VTS_SELECTABLE states to clear.

Interception: Generally not intercepted.

MSG_VIS_TEXT_ENTER_OVERSTRIKE_MODE

void	MSG_VIS_TEXT_ENTER_OVERSTRIKE_MODE(
        Boolean		calledFromTextObject);

This message enters the text object into overstrike mode (setting the VTS_OVERSTRIKE_MODE flag).

Source: Unrestricted.

Destination: Any text object.

Parameters: calledFromTextObject
true if this message was called internally by the text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_ENTER_INSERT_MODE

void	MSG_VIS_TEXT_ENTER_INSERT_MODE(
        Boolean		calledFromTextObject);

This message enters the text object into insert mode (clearing the VTS_OVERSTRIKE_MODE flag).

Source: Unrestricted.

Destination: Any text object.

Parameters: calledFromTextObject
true if this message was called internally by the text object.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 VisText Features | Next: 5.3 VisText VM File Storage


VisText VM File Storage

The Text Objects: 5.3 Using VisText: VisText VM File Storage

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 VisText States | Next: 5.4 Text Filters
VTI_vmFile, MSG_VIS_TEXT_SET_VM_FILE, MSG_VIS_TEXT_SAVE_TO_DB_ITEM, MSG_VIS_TEXT_SAVE_TO_DB_ITEM_WITH_STYLES, MSG_VIS_TEXT_LOAD_FROM_DB_ITEM, MSG_VIS_TEXT_LOAD_FROM_DB_ITEM_WITH_STYLES

VTI_ vmFile stores the VM file handle that is associated with this text object. This file is the default VM file that will be used for operations that require a storage area. You can set this file handle with MSG_VIS_TEXT_SET_VM_FILE.

To save a text object's current text (including attributes) you can send the text object MSG_VIS_TEXT_SAVE_TO_DB_ITEM. This message takes several VisTextSaveDBFlags which specify what aspects of the text to save to the DB item. These flags are listed below:

VTSDBF_TEXT
VTSDBF_CHAR_ATTR
VTSDBF_PARA_ATTR
VTSDBF_TYPE
VTSDBF_GRAPHIC
VTSDBF_STYLE
VTSDBF_REGION
VTSDBF_NAME

To retrieve text saved to a DB item in such a manner, use MSG_VIS_TEXT_LOAD_FROM_DB_ITEM. The text object is able to figure out what features of the text were saved and bring it up with any attributes that were saved previously.

MSG_VIS_TEXT_SET_VM_FILE

void	MSG_VIS_TEXT_SET_VM_FILE(
        VMFileHandle		file);

This message sets the text object's VTI_ vmFile instance field to the passed VM file handle. This file handle will be used as the default for operations that require a VM file.

Source: Unrestricted.

Destination: Any text object.

Parameters: file VM file handle.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SAVE_TO_DB_ITEM

DBGroupAndItem MSG_VIS_TEXT_SAVE_TO_DB_ITEM(
        DBGroupAndItem		item,
        VisTextSaveDBFlags		flags);

This message saves information about the current state of the text object. Data saved with this message can be retrieved with MSG_VIS_TEXT_LOAD_FROM_DB_ITEM.

Source: Unrestricted.

Destination: Any text object.

Parameters: item DB group and item to store the text information. If this is left zero, the info will be saved to the VM file specified in VTI_ vmFile .

flags
VisTextSaveDBFlags specifying what aspect of the text's info to save.

Return: DBItem of saved text info.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SAVE_TO_DB_ITEM_WITH_STYLES

DBGroupAndItem MSG_VIS_TEXT_SAVE_TO_DB_ITEM_WITH_STYLES(
        FileHandle		xferFile,
        VisTextSaveDBFlags		flags,
        DBGroupAndItem		item,
        StyleSheetParams		*params);

This message saves information about the current state of the text object, passing a StyleSheetParams structure.

VTST_RUNS_ONLY should be passed in the VisTextSaveDBFlags entries VTSDBF_CHAR_ATTR and VTSDBF_PARA_ATTR. Do not set VTSDBF_STYLE.

Source: Unrestricted.

Destination: Any text object.

Parameters: xferFile If non-zero, the StyleSheetParams structure is not initialized and this is the file to put the style sheet arrays into.

flags
VisTextSaveDBFlags specifying what aspect of the text's info to save.
item
DB group and item to store the text information. If this is left zero, the info will be saved to the VM file specified in VTI_ vmFile .
* params
Pointer to a StyleSheetParams structure.

Interception: Generally not intercepted.

MSG_VIS_TEXT_LOAD_FROM_DB_ITEM

void	MSG_VIS_TEXT_LOAD_FROM_DB_ITEM(
        DBGroupAndItem		item,
        VMFileHandle		file);

This message loads text saved with MSG_VIS_TEXT_SAVE_TO_DB_ITEM.

Source: Unrestricted.

Destination: Any text object.

Parameters: item DBItem to load.

file
Handle of the VM file to use. If zero, the VM file in VTI_ vmFile will be used.

Interception: Generally not intercepted.

MSG_VIS_TEXT_LOAD_FROM_DB_ITEM_WITH_STYLES

void	MSG_VIS_TEXT_LOAD_FROM_DB_ITEM_WITH_STYLES(
        FileHandle		file,
        DBGroupAndItem		item,
        StyleSheetParams		*params);

This message loads text and style sheets saved with MSG_VIS_TEXT_SAVE_TO_DB_ITEM_WITH_STYLES.

Source: Unrestricted.

Destination: Any text object.

Parameters: file Handle of the VM file to use. If zero, the VM file in VTI_ vmFile will be used.

item
DBItem to load.
* params
Pointer to a StyleSheetParams structure.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 VisText States | Next: 5.4 Text Filters


Text Filters

The Text Objects: 5.4 Using VisText: Text Filters

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 VisText VM File Storage | Next: 5.5 Key Functions
VTI_filters, VisTextFilters, VisTextFilterClass, ATTR_VIS_TEXT_EXTENDED_FILTER, MSG_VIS_TEXT_SET_FILTER, MSG_VIS_TEXT_GET_FILTER

When the user enters text into the text object, the text object is able to filter out characters that it does not wish to be displayed. This behavior is produced through use of VisTextFilters .

VTI_ filters stores a VisTextFilters record. This record has several entries:

VTF_NO_SPACES filters out any space characters.
VTF_NO_TABS filters out any tab characters.

VTF_UPCASE_CHARS ensures that any alphabetic characters are made uppercase. Other characters are unaffected.

VTF_FILTER_CLASS stores a VisTextFilterClass . This value can be any one of the following:

VTFC_NO_FILTER
VTFC_ALPHA
VTFC_NUMERIC
VTFC_SIGNED_NUMERIC
VTFC_SIGNED_DECIMAL
VTFC_FLOAT_DECIMAL
VTFC_ALPHA_NUMERIC
VTFC_FILENAMES
VTFC_DOS_FILENAMES
VTFC_DOS_PATH
VTFC_DATE
VTFC_TIME
VTFC_DASHED_ALPHA_NUMERIC
VTFC_NORMAL_ASCII
VTFC_DOS_VOLUME_NAMES
VTFC_DOS_CHARACTER_SET
VTFC_ALLOW_COLUMN_BREAKS

For more complex filtering, you can include ATTR_VIS_TEXT_EXTENDED_FILTER in your text object. Each value corresponds to a different message which you can intercept to provide custom filtering. These values are:

VTEFT_REPLACE_PARAMS
This filter generates MSG_VIS_TEXT_FILTER_VIA_REPLACE_PARAMS. This message passes a VisTextReplaceParameters structure that you can either accept or reject as a whole. This will be supported in an upcoming release.
VTEFT_CHARACTER_LEVELER_LEVEL
This filter generates MSG_VIS_TEXT_FILTER_VIA_CHARACTER. This message passes a single character that you can either accept or reject.
VTEFT_BEFORE_AFTER
This filter generates MSG_VIS_TEXT_FILTER_VIA_BEFORE_AFTER. This message passes two buffers containing the current text and the proposed new text. This will be supported in an upcoming release.

MSG_VIS_TEXT_GET_FILTER

byte	MSG_VIS_TEXT_GET_FILTER();

This message retrieves the current VisTextFilters in use by the text object (stored in VTI_ filters ).

Source: Unrestricted.

Destination: Any text object.

Return: VisTextFilters in use by the text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_FILTER

void	MSG_VIS_TEXT_SET_FILTER(
        byte		filter);

This message sets the text object's VisTextFilters (stored in VTI_ filters ) to the passed value. Resetting this filter does not affect any text that already exists in the text object.

Source: Unrestricted.

Destination: Any text object.

Parameters: filter VisTextFilters to set.

Interception: Generally not intercepted.

MSG_VIS_TEXT_FILTER_VIA_CHARACTER

word	MSG_VIS_TEXT_FILTER_VIA_CHARACTER(
        word		charToFilter);

This message allows the text object to filter text on a character by character basis. This message is called for each character that is entered into the text object.

This message will be generated if the text object includes ATTR_VIS_TEXT_EXTENDED_FILTER in its instance data, with its value set to the VisTextExtendedFilterType VTEFT_CHARACTER_LEVELER_LEVEL.

Source: Sent by the system if text object contains ATTR_VIS_TEXT_EXTENDED_FILTER set to VTEFT_CHARACTER_LEVELER_LEVEL.

Destination: Any text object

Parameters: charToFilter Character value that is being filtered. You can pass this value on to the superclass or filter it out.

Interception: Must intercept to provide character by character filtering.

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 VisText VM File Storage | Next: 5.5 Key Functions


Key Functions

The Text Objects: 5.5 Using VisText: Key Functions

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Text Filters | Next: 5.6 Setting Text Confines
VisTextKeyFunction, MSG_VIS_TEXT_DO_KEY_FUNCTION

The VisText object is able to recognize certain generic functions that operate on text. Most of these functions are initiated through a key sequence entered by the user, but you can mimic such a key sequence by sending the text object MSG_VIS_TEXT_DO_KEY_FUNCTION, passing it a VisTextKeyFunction .

The following VisTextKeyFunction values are recognized by the text object:

VTKF_FORWARD_LINE
VTKF_BACKWARD_LINE
VTKF_SELECT_ADJUST_FORWARD_LINE
VTKF_SELECT_ADJUST_BACKWARD_LINE
VTKF_FORWARD_CHAR
VTKF_BACKWARD_CHAR
VTKF_FORWARD_WORD
VTKF_BACKWARD_WORD
VTKF_FORWARD_PARAGRAPH
VTKF_BACKWARD_PARAGRAPH
VTKF_START_OF_LINE
VTKF_END_OF_LINE
VTKF_START_OF_TEXT
VTKF_END_OF_TEXT
VTKF_SELECT_WORD
VTKF_SELECT_LINE
VTKF_SELECT_PARAGRAPH
VTKF_SELECT_OBJECT
VTKF_SELECT_ADJUST_FORWARD_CHAR
VTKF_SELECT_ADJUST_BACKWARD_CHAR
VTKF_SELECT_ADJUST_FORWARD_WORD
VTKF_SELECT_ADJUST_BACKWARD_WORD
VTKF_SELECT_ADJUST_FORWARD_PARAGRAPH
VTKF_SELECT_ADJUST_BACKWARD_PARAGRAPH
VTKF_SELECT_ADJUST_TO_START
VTKF_SELECT_ADJUST_TO_END
VTKF_SELECT_ADJUST_START_OF_LINE
VTKF_SELECT_ADJUST_END_OF_LINE
VTKF_DELETE_BACKWARD_CHAR
VTKF_DELETE_BACKWARD_WORD
VTKF_DELETE_BACKWARD_LINE
VTKF_DELETE_BACKWARD_PARAGRAPH
VTKF_DELETE_TO_START
VTKF_DELETE_CHAR
VTKF_DELETE_WORD
VTKF_DELETE_LINE
VTKF_DELETE_PARAGRAPH
VTKF_DELETE_TO_END
VTKF_DELETE_EVERYTHING
VTKF_DESELECT
VTKF_TOGGLE_OVERSTRIKE_MODE
VTKF_TOGGLE_SMART_QUOTES

MSG_VIS_TEXT_DO_KEY_FUNCTION

void	MSG_VIS_TEXT_DO_KEY_FUNCTION
        VisTextKeyFunction			func,
        word			data);

This message allows the text object to mimic certain text operations that normally are the result of user keystrokes.

Note: the VisTextKeyFunction types VTKF_SELECT_LINE, VTKF_SELECT_PARAGRAPH, and VTKF_SELECT_OBJECT will be supported in an upcoming release.

Source: Unrestricted.

Destination: Any text object

Parameters: func VisTextKeyFunction to perform.

data
Data for that key function (if needed).

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Text Filters | Next: 5.6 Setting Text Confines


Setting Text Confines

The Text Objects: 5.6 Using VisText: Setting Text Confines

| Up: GEOS SDK TechDocs | Up | Prev: 5.5 Key Functions | Next: 5.7 Output Messages
MSG_VIS_TEXT_GET_MAX_LENGTH, MSG_VIS_TEXT_SET_MAX_LENGTH, MSG_VIS_TEXT_GET_LR_MARGIN, MSG_VIS_TEXT_SET_LR_MARGIN, MSG_VIS_TEXT_GET_TB_MARGIN, MSG_VIS_TEXT_SET_TB_MARGIN

MSG_VIS_TEXT_GET_MAX_LENGTH

word	MSG_VIS_TEXT_GET_MAX_LENGTH();

This message returns the maximum length of text in the text object (stored in VTI_ maxLength ).

Source: Unrestricted.

Destination: Any text object.

Return: Maximum length of the text, in characters.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_MAX_LENGTH

void	MSG_VIS_TEXT_SET_MAX_LENGTH(
        word		newMaxLength);

This message sets the maximum length of text within a text object (stored in VTI_ maxLength ). If the current text is too long, any excess text is removed.

Source: Unrestricted.

Destination: Any text object (except VisLargeTextClass ).

Parameters: newMaxLength Maximum length of the text object (in characters).

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_LR_MARGIN

byte	MSG_VIS_TEXT_GET_LR_MARGIN();

This message returns the current left/right margins. Both margins are the same and are thus represented by the same returned value.

Source: Unrestricted.

Destination: VisTextClass objects (not GenText objects).

Return: The current left/right margin.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_LR_MARGIN

void	MSG_VIS_TEXT_SET_LR_MARGIN(
        byte		lrMargin);

This message sets the left/right margins of the text object. Both margins will be set to the same value. Do not use this message on GenText objects, as the specific UI is responsible for this behavior.

Source: Unrestricted.

Destination: VisTextClass objects (not GenText objects).

Parameters: lrMargin New left/right margins.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_TB_MARGIN

byte	MSG_VIS_TEXT_GET_TB_MARGIN();

This message returns the current top/bottom margins. Both margins are the same and are thus represented by the same returned value.

Source: Unrestricted.

Destination: VisTextClass objects (not GenText objects).

Return: The current top/bottom margin.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_TB_MARGIN

void	MSG_VIS_TEXT_SET_TB_MARGIN(
        byte		tbMargin);

This message sets the top/bottom margins of the text object. Both margins will be set to the same value. Do not use this message on GenText objects, as the specific UI is responsible for this behavior.

Source: Unrestricted.

Destination: VisTextClass objects (not GenText objects).

Parameters: tbMargin New top/bottom margins.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5.5 Key Functions | Next: 5.7 Output Messages


Output Messages

The Text Objects: 5.7 Using VisText: Output Messages

| Up: GEOS SDK TechDocs | Up | Prev: 5.6 Setting Text Confines | Next: 5.8 Getting Geometry Information
VTI_output, MSG_VIS_TEXT_GET_OUTPUT, MSG_VIS_TEXT_SET_OUTPUT, MSG_META_TEXT_USER_MODIFIED, MSG_META_TEXT_NOT_USER_MODIFIED,MSG_META_TEXT_CR_FILTERED,MSG_META_TEXT_TAB_FILTERED, MSG_META_TEXT_LOST_FOCUS, MSG_META_TEXT_GAINED_FOCUS, MSG_META_TEXT_LOST_TARGET, MSG_META_TEXT_GAINED_TARGET, MSG_META_TEXT_EMPTY_STATUS_CHANGED

VTI_ output stores the object to receive messages sent out by the text object. A range of messages imported from MetaClass ( MetaTextMessages ) are sent to this destination optr. These messages are listed below:

MSG_META_TEXT_USER_MODIFIED is sent to VTI_ output when the user first modifies text within the object. This message is also sent to the text object itself to set its VTS_MODIFIED state (after it is sent to VTI_ output ). If the text object's VTS_MODIFIED bit is later cleared, it is sent MSG_META_TEXT_NOT_USER_MODIFIED (and may receive MSG_META_TEXT_USER_MODIFIED again).

MSG_META_TEXT_CR_FILTERED is sent when a Carriage Return is filtered out of the text input by the text object's filtering mechanism. This message is first sent to the text object itself.

MSG_META_TEXT_TAB_FILTERED is sent when a Tab is filtered out of the text input by the text object's filtering mechanism. This message is first sent to the text object itself.

MSG_META_TEXT_LOST_FOCUS is sent when the text object loses the focus. MSG_META_TEXT_GAINED_FOCUS is sent when the text object gains the focus.

MSG_META_TEXT_LOST_TARGET is sent when the text object loses the target. MSG_META_TEXT_GAINED_TARGET is sent when the text object gains the target.

MSG_META_TEXT_EMPTY_STATUS_CHANGED is sent when the text object either changes from being empty to being non-empty or vice-versa.

To retrieve the current output of the text object, send it MSG_VIS_TEXT_GET_OUTPUT. To change the current output, send it MSG_VIS_TEXT_SET_OUTPUT.

MSG_META_TEXT_USER_MODIFIED

@importMessage MetaTextMessages, void MSG_META_TEXT_USER_MODIFIED(
        optr		obj);

MSG_META_TEXT_USER_MODIFIED is sent to VTI_ output when the user modifies text within the object. The message is also sent to the text object itself (to mark itself modified) after it is sent to the destination, so the state of the text's VTS_MODIFIED bit may not accurately reflect the state of the text object. If you need to intercept this message and also need to perform an operation using the text object's modified state.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data. The message is also sent to the text object itself.

Parameters: obj Optr of the text object modified.

Interception: May intercept to receive notification of user modification of text. Because modification of the text object's VTS_MODIFIED bit may be asynchronous, you should send any operations that depend on this information (such as clearing the VTS_MODIFIED bit) to the text object using MF_FORCE_QUEUE

MSG_META_TEXT_NOT_USER_MODIFIED

@importMessage MetaTextMessages, void MSG_META_TEXT_NOT_USER_MODIFIED(
        optr		obj);

This message is sent to VTI_ output when the VTS_MODIFIED state of the text object is cleared, either by the system, or by an application inspired action.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data. The message is also sent to the text object itself first.

Parameters: obj Optr of the text object marked not modified.

Interception: Intercept to receive notification of when a text object is marked not modified.

MSG_META_TEXT_CR_FILTERED

@importMessage MetaTextMessages, void MSG_META_TEXT_CR_FILTERED(
        word		character,
        word		flags,
        word		state);

This message is sent to VTI_ output when a Carriage Return is filtered out in the text input stream.

Source: Sent by the system when a CR is filtered out of the text object.

Destination: VTI_ output of the text object's instance data. The message is also sent to the text object itself.

Parameters: character The character value (CR).

flags
ShiftState and CharFlags .
state
The state of the text object (VTI_ state ) at the time of the filtering action.

Interception: Intercept to receive notification that a Carriage Return has been filtered out of the text object.

MSG_META_TEXT_TAB_FILTERED

@importMessage MetaTextMessages, void MSG_META_TEXT_TAB_FILTERED(
        word		character,
        word		flags,
        word		state);

This message is sent to VTI_ output when a Tab is filtered out in the text input stream.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data. The message is also sent to the text object itself.

Parameters: character The character value (Tab).

flags
ShiftState and CharFlags .
state
The state of the text object (VTI_ state ) at the time of the filtering action.

Interception: Intercept to receive notification that a Tab has been filtered out of the text object.

MSG_META_TEXT_LOST_FOCUS

@importMessage MetaTextMessages, void MSG_META_TEXT_LOST_FOCUS(
        optr		obj);

This message is sent when the text object loses the focus of the application.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data.

Parameters: obj Optr of the text object.

Interception: Intercept to receive notification of when the text object loses the focus.

MSG_META_TEXT_GAINED_FOCUS

@importMessage MetaTextMessages, void MSG_META_TEXT_GAINED_FOCUS(
        optr		obj);

This message is sent when the text object gains the focus of the application.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data.

Parameters: obj Optr of the text object.

Interception: Intercept to receive notification of when the text object gains the focus.

MSG_META_TEXT_LOST_TARGET

@importMessage MetaTextMessages, void MSG_META_TEXT_LOST_TARGET(
        optr		obj);

This message is sent when the text object loses the target of an application.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data.

Parameters: obj Optr of the text object.

Interception: Intercept to receive notification of when the text object loses the target.

MSG_META_TEXT_GAINED_TARGET

@importMessage MetaTextMessages, void MSG_META_TEXT_GAINED_TARGET(
        optr		obj);

This message is sent when the text object gains the target of an application.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data.

Parameters: obj Optr of the text object.

Interception: Intercept to receive notification of when the text object gains the target.

MSG_META_TEXT_EMPTY_STATUS_CHANGED

@importMessage MetaTextMessages, void MSG_META_TEXT_EMPTY_STATUS_CHANGED(
        optr		object,
        Boolean		hasTextFlag);

This message is sent out when the text object is either becoming empty or not empty.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data. The message is also sent to the text object itself first.

Parameters: object Optr of the text object whose empty status is changing.

hasTextFlag
Non-zero if the text object is becoming non-empty.

Interception: Intercept to receive notification when the empty status of the text object is changing.

MSG_VIS_TEXT_GET_OUTPUT

optr	MSG_VIS_TEXT_GET_OUTPUT();

This message returns the current destination (output) for actions taken by the text object.

Source: Unrestricted.

Destination: Any text object.

Return: Optr of the object's destination (in the VTI_ output field).

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_OUTPUT

void	MSG_VIS_TEXT_SET_OUTPUT(
        optr		newOutput);

This message sets the destination object for actions taken by this text object.

Source: Unrestricted.

Destination: Any text object.

Parameters: newOutput New destination object (placed in object's VTI_ output field).

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5.6 Setting Text Confines | Next: 5.8 Getting Geometry Information


Getting Geometry Information

The Text Objects: 5.8 Using VisText: Getting Geometry Information

| Up: GEOS SDK TechDocs | Up | Prev: 5.7 Output Messages | Next: 6 Using GenText
MSG_VIS_TEXT_GET_MIN_WIDTH, MSG_VIS_TEXT_GET_AVERAGE_CHAR_WIDTH, MSG_VIS_TEXT_GET_LINE_HEIGHT, MSG_VIS_TEXT_RECALC_AND_DRAW, MSG_VIS_TEXT_GET_ONE_LINE_WIDTH, MSG_VIS_TEXT_GET_SIMPLE_MIN_WIDTH

MSG_VIS_TEXT_GET_MIN_WIDTH

word	MSG_VIS_TEXT_GET_MIN_WIDTH();

This message returns the minimum width that can be supported for attributes of the text object.

Source: Unrestricted.

Destination: Any text object.

Return: Minimum width (in points) that can be supported.

MSG_VIS_TEXT_GET_AVERAGE_CHAR_WIDTH

word	MSG_VIS_TEXT_GET_AVERAGE_CHAR_WIDTH();

This message returns the average character width for the first font/character attribute run.

Source: Unrestricted.,

Destination: Any text object.

Return: Average character width (in points).

MSG_VIS_TEXT_GET_LINE_HEIGHT

word	MSG_VIS_TEXT_GET_LINE_HEIGHT();

This message returns the height of a line (in points) for a single-line text object only .

Source: Unrestricted.

Destination: Single-line text objects that are not within document objects.

Return: Height of line (in points).

MSG_VIS_TEXT_RECALC_AND_DRAW

void	MSG_VIS_TEXT_RECALC_AND_DRAW();

This message recalculates and redraws a text object, usually after constraints on either its width or its height is made without its knowledge.

Source: Unrestricted.

Destination: Any text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_ONE_LINE_WIDTH

word	MSG_VIS_TEXT_GET_ONE_LINE_WIDTH(
        word		charsToCalc);

This message calculates the width of a single-line text object. This width is calculated using the first encountered character attributes.

Source: Unrestricted.

Destination: Any single-line text object.

Parameters: charsToCalc Number of characters (from start of text) to use in the calculation.

Return: Width of the text.

MSG_VIS_TEXT_GET_SIMPLE_MIN_WIDTH

word	MSG_VIS_TEXT_GET_SIMPLE_MIN_WIDTH();

This message returns the minimum width that can be supported for the text object's current text, font and character attributes. This message assumes that the caller is not worried about keeping a width for the largest border possible.

Source: Unrestricted.

Destination: Any text object.

Return: Minimum width which can be supported.

| Up: GEOS SDK TechDocs | Up | Prev: 5.7 Output Messages | Next: 6 Using GenText


Using GenText

The Text Objects: 6 Using GenText

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 Using VisText | Next: 7 The Controllers

GenTextClass provides your application with a generic UI object to both display and edit text. The GenText object is a highly functional object, capable of displaying text to the user under a variety of constraints. The object's role is primarily to display text pertaining to user interface operations. The object also provides a wide array of text processing features, however, to display the text in different formats. For more powerful and diverse text formatting (such as word-processing), you should probably use a VisText object instead.

The GenText has many advanced features of the Text Object Library built in. Most of these features may not be useful for cases where you will most likely use a GenText object; GenText is designed for simpler text edit capabilities than incorporating graphics into text, for example. Still, the GenText object provides a great deal of default behavior and, with a little work, a highly powerful and specifically tailored text object.

The GenText object may not only show text to the user but may also allow the user to supply or edit the displayed text. For example, a file copy operation may request the new name to write the file to; a GenText object would allow the user to enter this file name within its text field.

The text object library offers the following key features:

  • Display-only and editable text fields.
  • Automatic keyboard support, allowing the user to immediately begin entering text into the GenText's text field.
  • Automatic word wrapping from one line to the next (or the capability to disable this option).
  • Vertical scrolling of text (providing scrollbars either initially or when needed).
  • Selection of text, usually represented by highlighting.
  • The specification of actions to perform when the user completes text edits.
  • Text character styles to display the text. Sample character styles are bold, underline, strike-through, etc.
  • Paragraph styles (rulers) to display the text. Sample paragraph styles are right-justified, paragraph spacing, hidden text, etc.
  • Fonts to display the text.
  • Ability to store the text in a variety of data structures.
  • Interaction with the text controllers.

GenText inherits most of its capabilities from VisTextClass which in turn uses the text object library. You may wish to peruse the structure of this library if you want to know more details of how the GenText performs its default behavior.

GenText is used primarily in three situations: First, when you need to display text (such as in a "help" window), the GenText object can act as a non-editable text field with its own scrolling and geometry management. Second, when you need to get textual input not related to word processing or text editing (such as a file name), the GenText can act as an editable field of one or more lines. Third, when simple word processing or text editing in the generic tree is required (as in the NotePad application), the GenText can provide a scrollable, sizable text-edit field. If you want, the editable GenText can even support advanced paragraph and text formatting.

VisText is used in most situations where complex or advanced formatting or publishing features are desired. Typically, a GenView and a VisContent will be used to provide the root of the visible tree and its interaction with the UI. The VisText is much more flexible than the GenText and allows complex publishing and page layout features that would be difficult with GenText. VisText also allows you to have the same interface no matter what specific UI is in use since it is a visible object and not subject to the constraints of the specific UI.

GenText Instance Data

GenText Basics

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 Using VisText | Next: 7 The Controllers


GenText Instance Data

The Text Objects: 6.1 Using GenText: GenText Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 6 Using GenText | Next: 6.2 GenText Basics

The GenText object has several instance fields which affect the visual presentation of text. These are listed in GenText Instance Data .

Code Display 10-21 GenText Instance Data

 @instance ChunkHandle				GTXI_text;
@instance word				GTXI_maxLength = 32767;
@instance GenTextAttrs				GTXI_attrs = GTA_USE_TAB_FOR_NAVIGATION;
@instance GenTextStateFlags				GTXI_stateFlags = 0;
@instance optr				GTXI_destination;
@instance word				GTXI_applyMsg = 0;
/* GenTextAttrs */
typedef ByteFlags GenTextAttrs;
#define GTA_SINGLE_LINE_TEXT					0x80
#define GTA_USE_TAB_FOR_NAVIGATION					0x40
#define GTA_INIT_SCROLLING					0x20
#define GTA_NO_WORD_WRAPPING					0x10
#define GTA_ALLOW_TEXT_OFF_END					0x08
#define GTA_TAIL_ORIENTED					0x04
#define GTA_DONT_SCROLL_TO_CHANGES					0X02
/* GenTextStateFlags */
typedef ByteFlags GenTextStateFlags
#define GTSF_INDETERMINATE					0x80
#define GTSF_MODIFIED					0x40

GTXI_ text stores the ChunkHandle of the chunk containing the GenText's text. This chunk will be stored in the same resource as the text object. You may alter the text within this chunk with any of the previously mentioned VisTextClass messages. See The Text for a full discussion of how to manipulate and alter text.

GTXI_ maxLength specifies the maximum number of character positions allowed within the GenText. By default, up to 32767 characters may appear within a GenText. If you set GTXI_ maxLength to a lower number, any characters beyond the maximum length will be ignored.

GTXI_ attrs specifies the GenTextAttrs to set for the GenText object. These attributes affect the presentation of the text.

GTXI_stateFlags specifies the GenTextStateFlags to use with this text object. These state flags affect the modified and indeterminate states of the object.

GTXI_ applyMsg sets the message for this object to send out whenever activated. In most cases, your handler for this message may then retrieve the text and perform whatever required operations on that text.

GTXI_ destination sets the object or process to handle apply messages sent out by this object.

Code Display 10-22 GenText VarData Instance Fields

@vardata Message			ATTR_GEN_TEXT_STATUS_MSG;
@vardata void			ATTR_GEN_TEXT_SET_MODIFIED_ON_REDUNDANT_SELECTION;
@vardata void			ATTR_GEN_TEXT_SELECTABLE;
@vardata VisTextDefaultCharAttr ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR;
@vardata VisTextDefaultParaAttr ATTR_GEN_TEXT_DEFAULT_PARA_ATTR;
@vardata ChunkHandle			ATTR_GEN_TEXT_CHAR_ATTR;
@vardata ChunkHandle			ATTR_GEN_TEXT_MULTIPLE_CHAR_ATTR_RUNS;
@vardata ChunkHandle			ATTR_GEN_TEXT_PARA_ATTR;
@vardata ChunkHandle			ATTR_GEN_TEXT_MULTIPLE_PARA_ATTR_RUNS;
@vardata word			ATTR_GEN_TEXT_EXTENDED_FILTER;
@vardata word			ATTR_GEN_TEXT_TYPE_RUNS;
@vardata word			ATTR_GEN_TEXT_GRAPHIC_RUNS;
@vardata word			ATTR_GEN_TEXT_REGION_ARRAY;
@vardata word			ATTR_GEN_TEXT_STYLE_ARRAY;
@vardata word			ATTR_GEN_TEXT_NAME_ARRAY;
@vardata optr			ATTR_GEN_TEXT_RUNS_ITEM_GROUP;
    @reloc		ATTR_GEN_TEXT_RUNS_ITEM_GROUP;
@vardata void			ATTR_GEN_TEXT_DO_NOT_INTERACT_WITH_SEARCH_CONTROL;
@vardata optr			ATTR_GEN_TEXT_SET_OBJECT_ENABLED_WHEN_TEXT_EXISTS;
@vardata void			ATTR_GEN_TEXT_LEGAL_DOS_PATH;
@vardata void			ATTR_GEN_TEXT_DATE;
@vardata void			ATTR_GEN_TEXT_TIME;
@vardata void			ATTR_GEN_TEXT_MAKE_UPPERCASE;
@vardata void			ATTR_GEN_TEXT_ALLOW_COLUMN_BREAKS;
@vardata void			ATTR_GEN_TEXT_UPPERCASE_ALPHA;
@vardata void			ATTR_GEN_TEXT_UPPERCASE_ALPHA_NUMERIC;
@vardata void			ATTR_GEN_TEXT_DASHED_ALPHA_NUMERIC;
@vardata void			ATTR_GEN_TEXT_NORMAL_ASCII;
@vardata void			ATTR_GEN_TEXT_LEGAL_DOS_VOLUME_NAMES;
@vardata void			ATTR_GEN_TEXT_DOS_CHARACTER_SET;
@vardata void			ATTR_GEN_TEXT_NO_SPACES;
@vardata void			ATTR_GEN_TEXT_ALLOW_SPACES;
@vardata void			ATTR_GEN_TEXT_ALPHA;
@vardata void			ATTR_GEN_TEXT_NUMERIC;
@vardata void			ATTR_GEN_TEXT_SIGNED_NUMERIC;
@vardata void			ATTR_GEN_TEXT_SIGNED_DECIMAL;
@vardata void			ATTR_GEN_TEXT_FLOAT_DECIMAL;
@vardata void			ATTR_GEN_TEXT_ALPHA_NUMERIC;
@vardata void			ATTR_GEN_TEXT_LEGAL_FILENAMES;
@vardata void			ATTR_GEN_TEXT_LEGAL_DOS_FILENAMES;
@vardata void			ATTR_GEN_TEXT_NEVER_MAKE_SCROLLABLE;
@vardata void			ATTR_GEN_TEXT_DOES_NOT_ACCEPT_INK;
@vardata void			ATTR_GEN_TEXT_CURSOR_NO_FOCUS;
@vardata ChunkHandle			ATTR_GEN_TEXT_CUSTOM_FILTER;

ATTR_GEN_TEXT_STATUS_MSG sets a status message for this GenText. A status message allows the GenText to send notification other than its apply message.

ATTR_GEN_TEXT_SET_MODIFIED_ON_REDUNDANT_SELECTION sets the text dirty (modified) on any modification, even if it does not change the text or the text's attributes.

ATTR_GEN_TEXT_SELECTABLE should be set if text should be selectable whether or not the text is editable. By default non-editable text is not selectable. Editable text is always selectable.

ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR stores singular default character attributes of type VisTextDefaultCharAttr in a word-length record. ATTR_GEN_TEXT_DEFAULT_PARA_ATTR stores singular default paragraph attributes of type VisTextDefaultParaAttr in a word-length record.

ATTR_GEN_TEXT_CHAR_ATTR stores the singular character attributes for the text object. This vardata field stores the ChunkHandle to a single VisTextCharAttr structure.

ATTR_GEN_TEXT_MULTIPLE_CHAR_ATTR_RUNS stores the multiple character attributes for the text object. This vardata field will store the ChunkHandle to a chunk array of character style runs (each of type VisTextCharAttr ). See Character Runs .

ATTR_GEN_TEXT_PARA_ATTR stores the singular paragraph attributes for the text object. This vardata field stores the ChunkHandle to a single VisTextParaAttr structure.

ATTR_GEN_TEXT_MULTIPLE_PARA_ATTR_RUNS stores the multiple paragraph attributes for the text object. This vardata field will store the ChunkHandle to a chunk array of paragraph style runs (each of type VisTextParaAttr ). See Paragraph Runs .

ATTR_GEN_TEXT_EXTENDED_FILTER stores the VisTextExtendedFilterTypes to use instead of the default VisTextFilters . Extended filters offer more powerful (and complex) filtering mechanisms for accepting and rejecting text into the GenText.

ATTR_GEN_TEXT_TYPE_RUNS specifies the type runs for this text object. You should not need type runs unless you need hyperlink capabilities. ATTR_GEN_TEXT_GRAPHIC_RUNS specifies a graphics run to use within the text object.

ATTR_GEN_TEXT_REGION_ARRAY specifies the array of regions defined for this text object. Text within the GenText will flow from each of the defined regions to the next region. ATTR_GEN_TEXT_STYLE_ARRAY specifies the name array of styles used in the GenText's style sheets.

ATTR_GEN_TEXT_RUNS_ITEM_GROUP indicates that this text object is linked to an item group (list). When the user hits Return (or the equivalent), the text object sends a message to the item group; similarly, the item group will attempt to update the text object on changes.

ATTR_GEN_TEXT_DO_NOT_INTERACT_WITH_SEARCH_CONTROL indicates that the text object should not send notification to the search control object.

ATTR_GEN_TEXT_SET_OBJECT_ENABLED_WHEN_TEXT_EXISTS makes the specified object enabled any time the GenText object has text in it.

ATTR_GEN_TEXT_LEGAL_DOS_PATH makes sure that only legal DOS paths are used.

ATTR_GEN_TEXT_DATE and ATTR_GEN_TEXT_TIME allows short date and time characters and spaces; the text object will be affected by the localization driver.

ATTR_GEN_TEXT_MAKE_UPPERCASE indicates that in addition to any other filters, any alphabetic characters are forced into uppercase.

ATTR_GEN_TEXT_ALLOW_COLUMN_BREAKS allows all characters including column breaks. Column breaks are normally filtered out in a GenText object. The column breaks usually will appear as new lines (like carriage returns) in the text object.

ATTR_GEN_TEXT_UPPERCASE_ALPHA allows alphabetic characters and spaces. ATTR_GEN_TEXT_UPPERCASE_ALPHA_NUMERIC , an allows only uppercase alphabetic characters and spaces. ATTR_GEN_TEXT_DASHED_ALPHA_NUMERIC allows only uppercase alphabetic characters and spaces but also allows dashes.

ATTR_GEN_TEXT_NORMAL_ASCII allows only normal ASCII characters (no extended ASCII characters).

ATTR_GEN_TEXT_LEGAL_DOS_VOLUME_NAMES allows only characters that may appear in a legal DOS volume name.

ATTR_GEN_TEXT_DOS_CHARACTER_SET allows only characters in the legal DOS character set.

ATTR_GEN_TEXT_NO_SPACES and ATTR_GEN_TEXT_ALLOW_SPACES govern whether spaces are allowed in the text object.

ATTR_GEN_TEXT_ALPHA , ATTR_GEN_TEXT_NUMERIC , ATTR_GEN_TEXT_SIGNED_NUMERIC , ATTR_GEN_TEXT_SIGNED_DECIMAL , ATTR_GEN_TEXT_FLOAT_DECIMAL , and ATTR_GEN_TEXT_ALPHA_NUMERIC govern the character types allowed.

ATTR_GEN_TEXT_LEGAL_FILENAMES allows any characters legal to file names.

ATTR_GEN_TEXT_NEVER_MAKE_SCROLLABLE forces the text object to expand larger as the text grows without forcing it into scrollable text.

ATTR_GEN_TEXT_DOES_NOT_ACCEPT_INK indicates that this text object will not accept ink under any circumstances.

ATTR_GEN_TEXT_CURSOR_NO_FOCUS causes the text object to have a cursor whether it has the focus or not.

ATTR_GEN_TEXT_CUSTOM_FILTER contains the ChunkHandle of an array of VisTextCustomFilterData structures. Each of these structures contains a range of character values to filter out. These values are determined from the Chars enumerated type. The text object figures out the number of filters in place by getting the size of the chunk.

Code Display 10-23 GenText Hints

@vardata ColorQuad			 HINT_TEXT_WASH_COLOR
@vardata void			HINT_TEXT_WHITE_WASH_COLOR;
@vardata void			HINT_TEXT_AUTO_HYPHENATE;
@vardata void			HINT_TEXT_SELECT_TEXT;
@vardata void			HINT_TEXT_CURSOR_AT_START;
@vardata void			HINT_TEXT_CURSOR_AT_END;
@vardata void			HINT_TEXT_FRAME;
@vardata void			HINT_TEXT_NO_FRAME;
@vardata void			HINT_TEXT_ALLOW_UNDO;
@vardata void			HINT_TEXT_ALLOW_SMART_QUOTES;
@vardata void			HINT_TEXT_DO_NOT_MAKE_LARGER_ON_PEN_SYSTEMS;

HINT_TEXT_WASH_COLOR indicates the background color for the GenText to exhibit. This hint takes a ColorQuad argument.

HINT_TEXT_WHITE_WASH_COLOR indicates the background color of the GenText should be white.

| Up: GEOS SDK TechDocs | Up | Prev: 6 Using GenText | Next: 6.2 GenText Basics


GenText Basics

The Text Objects: 6.2 Using GenText: GenText Basics

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 GenText Instance Data | Next: 7 The Controllers

The GenText instance fields can be set to specific values in your Goc files. They may also be modified by your application at run-time. These instance fields may be modified by both GenTextClass messages or VisTextClass messages.

GenTextClass always builds out into a completely functioning subclass of VisTextClass . With the exception of geometry management behavior, you can assume that they will behave the same. You may send any VisTextClass message to a GenText object. Messages are only provided in GenTextClass to manipulate and alter behavior solely of GenTextClass origins.

VisText Instance Fields

The chunk handle stored in GTXI_ text is copied to the VisText instance field VTI_ text at run-time. To perform operations on this text, you may use any of the VisTextClass messages in The Text . Similarly, the text object maximum length stored in GTXI_ maxLength is copied to VTI_ maxLength at run-time. To perform operations on this data, use an appropriate message under Using VisText .

GenText Attributes

GTXI_attrs, MSG_GEN_TEXT_SET_ATTRS, MSG_GEN_TEXT_GET_ATTRS

GTXI_ attrs stores the GenTextAttrs of the GenText object. These attribute flags are listed below.

GTA_SINGLE_LINE_TEXT
This flag indicates that this text object is only one line high. Vertical scrolling is disabled. Scrolling may be implemented horizontally if this is set.
GTA_USE_TAB_FOR_NAVIGATION
This flag indicates that the Tab key is used for navigation purposes in your application and should be interpreted to move to the next field rather than inserted into the text object.
GTA_INIT_SCROLLING
This flag indicates that the text object should appear with initial scrollbars.
GTA_NO_WORD_WRAPPING
This flag disables word wrapping.
GTA_ALLOW_TEXT_OFF_END
This flag is set if text may be allowed to overflow past the end of the text box. The text will still be stored in the object's text chunk, but it will not force horizontal (or vertical) scrolling to show the text on-screen.
GTA_TAIL_ORIENTED
This flag is set if you prefer to display the tail end of text rather than the top end. In a scrolling text box, this ensures that the text being added at the end is always displayed.
GTA_DONT_SCROLL_TO_CHANGES
This flag disables the default behavior of scrolling to any changes being made within the text object. Normally, insertion or deletion of text will force a scrolling GenText to scroll to the point of action; this flag will turn this behavior off.

You may alter the contents of a GenText's GTXI_ attrs instance field at run-time by sending the object a MSG_GEN_TEXT_SET_ATTRS . You may only send this message to a non-usable (~GS_USABLE) text object. To retrieve the current GenTextAttrs in use, send the text object MSG_GEN_TEXT_GET_ATTRS .

MSG_GEN_TEXT_SET_ATTRS

void	MSG_GEN_TEXT_SET_ATTRS(
        byte	attrsToSet,
        byte	attrsToClear);

This message sets a GenText object's GenTextAttrs (GTXI_ attrs ). The GenText object must not be GS_USABLE when sent this message.

Source: Unrestricted.

Destination: Any non-usable GenText object.

Parameters: attrsToSet GenTextAttrs to add.

attrsToClear
GenTextAttrs to remove. An attribute set in both parameters will be cleared.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_TEXT_GET_ATTRS

byte	MSG_GEN_TEXT_GET_ATTRS();

This message retrieves the GenText object's GenTextAttrs (GTXI_ attrs ).

Source: Unrestricted.

Destination: Any GenText object.

Parameters: None.

Return: GenTextAttrs in use by the GenText object( GTXI_attrs ).

Interception: Generally not intercepted.

GenTextStates

GTXI_stateFlags, MSG_GEN_TEXT_SET_INDETERMINATE_STATE, MSG_GEN_TEXT_SET_MODIFIED_STATE, MSG_GEN_TEXT_IS_INDETERMINATE, MSG_GEN_TEXT_IS_MODIFIED

GTXI_ stateFlags stores the current state of the GenText object. There are two GenTextStateFlags :

GTSF_INDETERMINATE
This flag specifies that the text within the GenText is indeterminate (may or may not reflect the current state). In most cases, you will not need to set this flag.
GTSF_MODIFIED
This flag specifies that the text within the GenText has changed since it last received a MSG_GEN_APPLY . The handler for MSG_GEN_APPLY checks whether this flag is set before sending out the GenText's GTXI_ applyMsg .

GenText objects should normally be marked by the application as not modified anytime their state is set with an external message. They will automatically be marked modified whenever the user interacts with them and marked not modified after receiving MSG_GEN_APPLY .

You may set a GenText's indeterminate or modified state with MSG_GEN_TEXT_SET_INDETERMINATE_STATE or MSG_GEN_TEXT_SET_MODIFIED_STATE , respectively. To check whether a GenText is indeterminate or modified, use MSG_GEN_TEXT_IS_INDETERMINATE or MSG_GEN_TEXT_IS_MODIFIED .

MSG_GEN_TEXT_SET_INDETERMINATE_STATE

void	MSG_GEN_TEXT_SET_INDETERMINATE_STATE(
        Boolean indeterminateState);

This message sets the GenText object's indeterminate state (the GTSF_INDETERMINATE flag in GTXI_ stateFlags ). This message does not affect the stored text.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: indeterminateState
True to set the text object indeterminate (GTSF_INDETERMINATE); false otherwise.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_TEXT_SET_MODIFIED_STATE

void	MSG_GEN_TEXT_SET_MODIFIED_STATE(
        Boolean modifiedState);

This message allows you to set the modified state of a GenText object (the GTSF_MODIFIED flag in GTXI_ stateFlags ). This message does not affect the stored text.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: modifiedState True to set the text object modified (GTSF_MODIFIED); false otherwise.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_TEXT_IS_INDETERMINATE

Boolean	MSG_GEN_TEXT_IS_INDETERMINATE();

This message checks whether a GenText object is indeterminate in state.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: None.

Return: The indeterminate state of the GenText object ( true if the object is indeterminate, false otherwise).

Interception: Generally not intercepted.

MSG_GEN_TEXT_IS_MODIFIED

Boolean	MSG_GEN_TEXT_IS_MODIFIED();

This message checks whether a GenText object has been modified.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: None.

Return: The modified state of the GenText object ( true if the object has been modified, false otherwise).

Interception: Generally not intercepted.

Sending an Action

GTXI_applyMsg, GTXI_destination, MSG_GEN_TEXT_GET_DESTINATION, MSG_GEN_TEXT_SET_DESTINATION, MSG_GEN_TEXT_GET_APPLY_MSG, MSG_GEN_TEXT_SET_APPLY_MSG

GTXI_ applyMsg holds the message for the GenText to send out whenever it has been modified and needs to apply its changes. Whenever a GenText receives MSG_GEN_APPLY , it checks whether its GTSF_MODIFIED flag has been set; if it has, it will send out its apply message. If a GenText is operating in immediate mode, these actions will happen immediately, resulting in an immediate action.

Use the prototype GEN_TEXT_APPLY_MSG to define your apply message. This ensures that the apply message passes the correct parameters (the current GenTextStateFlags ). GTXI_ destination specifies the destination object (or process) to send the GTXI_ applyMsg to. (This may also be a TravelOption such as TO_APP_FOCUS.) Use of these two fields is shown in Sending an Apply Message .

Code Display 10-24 Sending an Apply Message

    /* Define the apply message using the provided prototype. */
@message (GEN_TEXT_APPLY_MSG) MSG_MY_TEXT_STUFF_TEXT_IN_BUFFER;
    /* In the object declaration, set the destination and the apply message. */
@object GenTextClass MyText = {
    GI_visMoniker = "My Text Object";
    GTXI_text = "Initial Text Here";
    GTXI_maxLength = 99;
    GTXI_applyMsg = MSG_MY_TEXT_STUFF_TEXT_IN_BUFFER;
    GTXI_destination = process;
}
/* Retrieve the current text and place it in the indicated buffer. */
@method MyTextProcessClass, MSG_MY_TEXT_STUFF_TEXT_IN_BUFFER {
    char tempBuffer[100];
/* The GET_ALL_PTR retrieves the current text and stuffs at the location it in the
 * passed pointer. Pass a text length of zero for null-terminated text strings. */
    @call MyText::MSG_VIS_TEXT_GET_ALL_PTR(tempBuffer);
}

To change a GenText's apply message or destination, send it MSG_GEN_TEXT_SET_APPLY_MSG or MSG_GEN_TEXT_SET_DESTINATION , respectively. Use MSG_GEN_TEXT_GET_APPLY_MSG or MSG_GEN_TEXT_GET_DESTINATION to return the current apply message or destination.

MSG_GEN_TEXT_SET_APPLY_MSG

void	MSG_GEN_TEXT_SET_APPLY_MSG(
        Message		message);

This message sets a new apply message (GTXI_ applyMsg ) for the text object.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: message The new apply message.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_TEXT_GET_APPLY_MSG

Message	MSG_GEN_TEXT_GET_APPLY_MSG();

This message retrieves the current apply message (GTXI_ applyMsg ) of a GenText object.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: None.

Return: The apply message of the GenText object.

Interception: Generally not intercepted.

MSG_GEN_TEXT_SET_DESTINATION

void	MSG_GEN_TEXT_SET_DESTINATION(
        optr	dest);

This message sets the destination object or process (GTXI_ destination ) of a GenText object.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: dest The optr of the new destination object or process.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_TEXT_GET_DESTINATION

optr	MSG_GEN_TEXT_GET_DESTINATION();

This message returns the current destination object (GTXI_ destination ) of a GenText object.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: None.

Return: The optr of the GenText's destination object (GTXI_ destination ).

Interception: Generally not intercepted.

GEN_TEXT_APPLY_MSG

void	GEN_TEXT_APPLY_MSG(
        word	stateFlags);

This prototype should be used to define your GenText's apply message (with GTXI_ applyMsg ).

Parameters: stateFlags The text object's GTXI_ stateFlags status.

Status Messages

ATTR_GEN_TEXT_STATUS_MSG, MSG_GEN_TEXT_SEND_STATUS_MSG

If your GenText is operating in delayed mode, there usually occur times when its current state may not reflect the most recent changes. In most cases, this is fine, but in some cases you may wish to notify other UI objects of a change in your GenText's state without sending out an apply message. This can be done with a status message .

A status message allows your GenText object to send out a message whenever the user interacts with the text object, regardless of whether that change will be immediately applied. This is most useful for cases in which two UI objects depend on each other. The status message allows one UI object to inform its counterpart that its state has changed, and that the counterpart should change its state to reflect the new information.

To give a GenText object a status message, include ATTR_GEN_TEXT_STATUS_MSG in the object's declaration. Use the prototype GEN_TEXT_STATUS_MSG to define your status message. This prototype ensures that the status message passes the correct parameters (the current GenTextStateFlags ).

Any user changes that do not result in the sending of the object's apply message will result in the sending of the object's status message. For an object in immediate mode, this attribute will have no effect. You may also manually send an object's status message by sending the GenText object MSG_GEN_TEXT_SEND_STATUS_MSG .

MSG_GEN_TEXT_SEND_STATUS_MSG

void	MSG_GEN_TEXT_SEND_STATUS_MSG(
        Boolean		modifiedState);

This message causes a GenText object to send out its status message, stored in the text object's ATTR_GEN_TEXT_STATUS_MSG vardata field. This message will still function even if the text object is not enabled (or usable).

Source: Unrestricted.

Destination: Any GenText object.

Parameters: modifiedState Non-zero if GVSF_MODIFIED bit should be passed with the status message.

Return: Nothing.

Interception: Generally not intercepted.

GEN_TEXT_STATUS_MSG

void	GEN_TEXT_STATUS_MSG(
        word	stateFlags);

This prototype should be used to define your GenText's status message (with ATTR_GEN_TEXT_STATUS_MSG).

Parameters: stateFlags The text object's GTXI_ stateFlags status.

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 GenText Instance Data | Next: 7 The Controllers


The Controllers

The Text Objects: 7 The Controllers

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Using GenText | Next: 7.1 Character Attribute Controllers

This section describes the various text controller classes and how best to use them. Controllers create their UI gadgetry wherever in the generic tree they are placed; this section will give some pointers to designing the text controller UI. For full information on GenControlClass and how controllers work, see the Controllers chapter.

Character Attribute Controllers

Paragraph Attribute Controllers

Search and Replace and Spell-Checking

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Using GenText | Next: 7.1 Character Attribute Controllers


Character Attribute Controllers

The Text Objects: 7.1 The Controllers: Character Attribute Controllers

| Up: GEOS SDK TechDocs | Up | Prev: 7 The Controllers | Next: 7.2 Paragraph Attribute Controllers

The following controllers affect character attributes within a text object. As such, they each affect the contents of a text object's character attributes instance field (usually the VisTextCharAttr structure).

TextStyleControlClass

This controller works with the style character attributes of the current text selection. The features and tools of this controller are shown in TextStyleControlClass Features .

Code Display 10-25 TextStyleControlClass Features

  typedef WordFlags TSCFeatures;
#define TSCF_PLAIN					0x0800
#define TSCF_BOLD					0x0400
#define TSCF_ITALIC					0x0200
#define TSCF_UNDERLINE					0x0100
#define TSCF_STRIKE_THRU					0x0080
#define TSCF_SUBSCRIPT					0x0040
#define TSCF_SUPERSCRIPT					0x0020
#define TSCF_BOXED					0x0010
#define TSCF_BUTTON					0x0008
#define TSCF_INDEX					0x0004
#define TSCF_ALL_CAP					0x0002
#define TSCF_SMALL_CAP					0x0001
  typedef WordFlags TSCToolboxFeatures;
#define TSCTF_PLAIN					0x0800
#define TSCTF_BOLD					0x0400
#define TSCTF_ITALIC					0x0200
#define TSCTF_UNDERLINE					0x0100
#define TSCTF_STRIKE_THRU					0x0080
#define TSCTF_SUBSCRIPT					0x0040
#define TSCTF_SUPERSCRIPT					0x0020
#define TSCTF_BOXED					0x0010
#define TSCTF_BUTTON					0x0008
#define TSCTF_INDEX					0x0004
#define TSCTF_ALL_CAP					0x0002
#define TSCTF_SMALL_CAP					0x0001
#define  TSC_DEFAULT_FEATURES				(TSCF_PLAIN | TSCF_BOLD | TSCF_ITALIC |
                 TSCF_UNDERLINE | TSCF_SUBSCRIPT |
                 TSCF_SUPERSCRIPT | TSCF_STRIKE_THRU |
                 TSCF_BOXED | TSCF_BUTTON | TSCF_INDEX |
                 TSCF_ALL_CAP | TSCF_SMALL_CAP)
#define  TSC_DEFAULT_TOOLBOX_FEATURES (TSCTF_PLAIN | TSCTF_BOLD | TSCTF_ITALIC |
                TSCTF_UNDERLINE)

FontControlClass

This controller allows the user to change font types of the current text selection. Its features and tools are shown in FontControlClass Features .

Code Display 10-26 FontControlClass Features

  typedef WordFlags FCFeatures;
#define FCF_SHORT_LIST				0x0002
#define FCF_LONG_LIST				0x0001
  typedef WordFlags FCToolboxFeatures;
#define FCTF_ROMAN				0x0004
#define FCTF_SANS				0x0002
#define FCTF_MONO				0x0001
#define  FC_DEFAULT_FEATURES				(FCF_SHORT_LIST | FCF_LONG_LIST)
#define  FC_DEFAULT_TOOLBOX_FEATURES (FCTF_ROMAN | FCTF_SANS | FCTF_MONO)

PointSizeControlClass

This controller controls the point size of the current text selection. Its features and tools are shown in PointSizeControlClass Features .

Code Display 10-27 PointSizeControlClass Features

  typedef WordFlags PSCFeatures;
#define PSCF_9					0x0800
#define PSCF_10					0x0400
#define PSCF_12					0x0200
#define PSCF_14					0x0100
#define PSCF_18					0x0080
#define PSCF_24					0x0040
#define PSCF_36					0x0020
#define PSCF_54					0x0010
#define PSCF_72					0x0008
#define PSCF_SMALLER					0x0004
#define PSCF_LARGER					0x0002
#define PSCF_CUSTOM_SIZE					0x0001
  typedef WordFlags PSCToolboxFeatures;
#define PSCTF_9					0x0400
#define PSCTF_10					0x0200
#define PSCTF_12					0x0100
#define PSCTF_14					0x0080
#define PSCTF_18					0x0040
#define PSCTF_24					0x0020
#define PSCTF_36					0x0010
#define PSCTF_54					0x0008
#define PSCTF_72					0x0004
#define PSCTF_SMALLER					0x0002
#define PSCTF_LARGER					0x0001
#define  PSC_DEFAULT_FEATURES 					(PSCF_9 | PSCF_10 | PSCF_12 | PSCF_14 |
                     PSCF_18 | PSCF_24 | PSCF_36 | PSCF_72 |
                     PSCF_CUSTOM_SIZE | PSCF_SMALLER |
                     PSCF_LARGER)
#define  PSC_DEFAULT_TOOLBOX_FEATURES					(PSCTF_9 | PSCTF_10 | PSCTF_12 |
                     PSCTF_14 | PSCTF_18 | PSCTF_24 |
                     PSCTF_36 | PSCTF_72 | PSCTF_SMALLER |
                     PSCTF_LARGER)

CharFGColorControlClass and CharBGColorControlClass

These controllers handle changes to the foreground (text) and background colors of the current text selection. They are both subclassed from ColorSelectorClass and therefore inherit the feature set of that class. The additional definitions for these classes are given in Character Color Controller Features .

Code Display 10-28 Character Color Controller Features

/* Both CharFGColorControlClass and CharBGColorControlClass are subclasses of
 * ColorSelectorClass. For full details, see that class. */
@class CharFGColorControlClass, ColorSelectorClass;
#define  CFGCC_DEFAULT_FEATURES				(CSF_INDEX | CSF_RGB | CSF_DRAW_MASK |
                 CSF_PATTERN)
    @default GI_states = @default & ~GS_ENABLED;
    @default GCI_output = (TO_APP_TARGET);
@endc
@class CharBGColorControlClass, ColorSelectorClass;
#define  CBGCC_DEFAULT_FEATURES				(CSF_INDEX | CSF_RGB | CSF_DRAW_MASK |
                 CSF_PATTERN)
    @default GI_states = @default & ~GS_ENABLED;
    @default GCI_output = (TO_APP_TARGET);
@endc

FontAttrControlClass

This controller controls the font weight, font width, and track kerning of characters of the current text selection. Its features are shown in FontAttrControlClass Features .

Code Display 10-29 FontAttrControlClass Features

  typedef WordFlags FACFeatures;
#define FACF_FONT_WEIGHT				0x0004
#define FACF_FONT_WIDTH				0x0002
#define FACF_TRACK_KERNING				0x0001
  typedef WordFlags FACToolboxFeatures;
#define  FAC_DEFAULT_FEATURES				(FACF_FONT_WEIGHT | FACF_FONT_WIDTH |
                 FACF_TRACK_KERNING)
#define  FAC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);
| Up: GEOS SDK TechDocs | Up | Prev: 7 The Controllers | Next: 7.2 Paragraph Attribute Controllers


Paragraph Attribute Controllers

The Text Objects: 7.2 The Controllers: Paragraph Attribute Controllers

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 Character Attribute Controllers | Next: 7.3 Search and Replace and Spell-Checking

The following controllers affect paragraph attributes within a text object. As such, they each affect the contents of a text object's paragraph attributes instance field (usually the VisTextParaAttr structure).

JustificationControlClass

This controller allows the user to specify paragraph justification of the current text selection (left, right, center, or full). Its features and tools are shown in JustificationControlClass Features .

Code Display 10-30 JustificationControlClass Features

  typedef WordFlags JCFeatures;
#define JCF_LEFT				0x0008
#define JCF_RIGHT				0x0004
#define JCF_CENTER				0x0002
#define JCF_FULL				0x0001
  typedef WordFlags JCToolboxFeatures;
#define JCTF_LEFT				0x0008
#define JCTF_RIGHT				0x0004
#define JCTF_CENTER				0x0002
#define JCTF_FULL				0x0001
#define  JC_DEFAULT_FEATURES				(JCF_LEFT | JCF_RIGHT | JCF_CENTER | JCF_FULL)
#define  JC_DEFAULT_TOOLBOX_FEATURES (JCTF_LEFT | JCTF_RIGHT | JCTF_CENTER |
                  JCTF_FULL)
    @default GCI_output = (TO_APP_TARGET);
    @default GI_attrs = (@default | GA_KBD_SEARCH_PATH);

ParaSpacingControlClass

This controller controls several paragraph spacing characteristics of the current text selection (such as paragraph spacing on top, spacing on bottom, leading, and line spacing). Its features and tools are shown in ParaSpacingControlClass Features .

Code Display 10-31 ParaSpacingControlClass Features

  typedef WordFlags PASCFeatures;
#define PASCF_SPACE_ON_TOP_BOTTOM					0x0002
#define PASCF_LEADING					0x0001
  typedef WordFlags PASCToolboxFeatures;
#define PASCTF_SINGLE					0x8000
#define PASCTF_ONE_AND_A_HALF					0x4000
#define PASCTF_DOUBLE					0x2000
#define PASCTF_TRIPLE					0x1000
#define  PASC_DEFAULT_FEATURES				(PASCF_SPACE_ON_TOP_BOTTOM | PASCF_LEADING)
#define  PASC_DEFAULT_TOOLBOX_FEATURES (PASCTF_SINGLE | PASCTF_ONE_AND_A_HALF |
                PASCTF_DOUBLE | PASCTF_TRIPLE)
@default GCI_output = (TO_APP_TARGET);

DefaultTabsControlClass and TabControlClass

These controllers set the placement and features of both default and user-defined tabs in the current text selection. The features and tools of both controllers are shown in Tab Controllers .

Code Display 10-32 Tab Controllers

    /* DefaultTabsControlClass */
  typedef WordFlags DTCFeatures;
#define DTCF_LIST				0x0002
#define DTCF_CUSTOM				0x0001
  typedef WordFlags DTCToolboxFeatures;
#define  DTC_DEFAULT_FEATURES				(DTCF_LIST | DTCF_CUSTOM)
#define  DTC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);
     /* TabControlClass */
  typedef WordFlags TCFeatures;
#define TCF_LIST					0x0080
#define TCF_POSITION					0x0040
#define TCF_GRAY_SCREEN					0x0020
#define TCF_TYPE					0x0010
#define TCF_LEADER					0x0008
#define TCF_LINE					0x0004
#define TCF_CLEAR					0x0002
#define TCF_CLEAR_ALL					0x0001
 typedef WordFlags TCToolboxFeatures;
#define  TC_DEFAULT_FEATURES				(TCF_LIST | TCF_POSITION | TCF_GRAY_SCREEN |
                 TCF_TYPE | TCF_LEADER | TCF_LINE | TCF_CLEAR |
                 TCF_CLEAR_ALL)
#define  TC_DEFAULT_TOOLBOX_FEATURES 0
     @instance word TCI_numberOfTabs;
    @instance Tab TCI_tabList[VIS_TEXT_MAX_TABS];
    @instance word TCI_selectedTab;
    @default GCI_output = (TO_APP_TARGET);

ParaBGColorControlClass

This controller controls the background color, draw mask, and pattern of the background color of the current paragraph selections. Since ParaBGColorControlClass is a subclass of ColorSelectorClass , it inherits the functions of that class. Its features and tools are shown in ParaBGColorControlClass Features .

Code Display 10-33 ParaBGColorControlClass Features

#define  PBGCC_DEFAULT_FEATURES				(CSF_INDEX | CSF_RGB | CSF_DRAW_MASK |
                 CSF_PATTERN)
    @default GI_states = @default & ~GS_ENABLED;
    @default GCI_output = (TO_APP_TARGET);

ParaAttrControlClass

This controller the user to set the "keep with," hidden, word wrapping, an widow and orphan attributes of the current paragraph selection. Its features and tools are shown in ParaAttrControlClass Features .

Code Display 10-34 ParaAttrControlClass Features

  typedef WordFlags PACFeatures;
#define PACF_WORD_WRAP					0x0010
#define PACF_HIDDEN_TEXT					0x0008
#define PACF_KEEP_PARA_WITH_NEXT					0x0004
#define PACF_KEEP_PARA_TOGETHER					0x0002
#define PACF_KEEP_LINES					0x0001
 typedef WordFlags PACToolboxFeatures;
#define  PAC_DEFAULT_FEATURES				(PACF_WORD_WRAP | PACF_HIDDEN_TEXT |
                 PACF_KEEP_PARA_WITH_NEXT |
                 PACF_KEEP_PARA_TOGETHER | PACF_KEEP_LINES
#define  PAC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);

BorderControlClass and BorderColorControlClass

This controllers set border color and placement. BorderColorControlClass is a subclass of ColorSelectorClass and therefore inherits the features and tools of that class. The tools and features of both controllers are shown in Border Controller Features .

Code Display 10-35 Border Controller Features

    /* BorderControlClass */
  typedef WordFlags BCFeatures;
#define BCF_LIST				0x0002
#define BCF_CUSTOM				0x0001
 typedef WordFlags BCToolboxFeatures;
#define  BC_DEFAULT_FEATURES				(BCF_LIST | BCF_CUSTOM)
#define  BC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);
    /* BorderColorControlClass */
 typedef WordFlags BCCToolboxFeatures;
#define  BCC_DEFAULT_FEATURES				(CSF_INDEX | CSF_RGB | CSF_DRAW_MASK |
                 CSF_PATTERN)
#define  BCC_DEFAULT_TOOLBOX_FEATURES 0
    @default GI_states = @default & ~GS_ENABLED;
    @default GCI_output = (TO_APP_TARGET);

DropCapControlClass

This controller allows the user to set "drop cap" features of the current paragraph selection. Its features and tools are shown in DropCapControlClass Features .

Code Display 10-36 DropCapControlClass Features

 typedef WordFlags DCCFeatures;
#define  DCCF_DROP_CAP				0x0001
 typedef WordFlags DCCToolboxFeatures;
#define  DCC_DEFAULT_FEATURES				(DCCF_DROP_CAP)
#define  DCC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);

HyphenationControlClass

This controller sets the implementation of automatic word hyphenation of the current paragraph selection. Its features and tools are shown in HyphenationControlClass .

Code Display 10-37 HyphenationControlClass

 typedef WordFlags HCFeatures;
#define  HCF_LIST				0x0001
typedef WordFlags  HCToolboxFeatures;
#define  HC_DEFAULT_FEATURES				(HCF_LIST)
#define  HC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);

MarginControlClass

This controller sets the left, paragraph, and right margins of the current paragraph selection. Its tools and features are shown in MarginControlClass .

Code Display 10-38 MarginControlClass

  typedef WordFlags MCFeatures;
#define MCF_LEFT_MARGIN				0x0004
#define MCF_PARA_MARGIN				0x0002
#define MCF_RIGHT_MARGIN				0x0001
typedef WordFlags  MCToolboxFeatures;
#define  MC_DEFAULT_FEATURES				(MCF_LEFT_MARGIN | MCF_PARA_MARGIN |
                 MCF_RIGHT_MARGIN)
#define  MC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);

TextRulerControlClass

This controller manages how the TextRulerClass ruler operates, if any text ruler is used. The TextRuler provides a complete ruler for the text object. The features and tools of the TextRulerControl are shown in TextRulerControlClass .

Code Display 10-39 TextRulerControlClass

  typedef WordFlags TRCCFeatures;
#define TRCCF_ROUND					0x0002
#define TRCCF_IGNORE_ORIGIN					0x0001
typedef WordFlags  TRCCToolboxFeatures;
#define  TRCC_DEFAULT_FEATURES				(TRCCF_ROUND | TRCCF_IGNORE_ORIGIN)
#define  TRCC_DEFAULT_TOOLBOX_FEATURES 0
   typedef WordFlags TextRulerControlAttributes;
#define TRCA_ROUND					0x8000
#define TRCA_IGNORE_ORIGIN					0x4000
    @instance TextRulerControlAttributes TRCI_attrs = TRCA_ROUND;

TextStyleSheetControlClass

This controller allows the user to set up and use style sheets with the target text object. It is subclassed off StyleSheetControlClass and inherits all the features and tools of that class. It has no additional tools or features of its own.

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 Character Attribute Controllers | Next: 7.3 Search and Replace and Spell-Checking


Search and Replace and Spell-Checking

The Text Objects: 7.3 The Controllers: Search and Replace and Spell-Checking

| Up: GEOS SDK TechDocs | Up | Prev: 7.2 Paragraph Attribute Controllers
TextSearchInString(), TextSearchInHugeArray(), SearchOptions, WildCard

Text searches are not specifically part of VisTextClass , but are commonly used on text objects, and they are discussed here for that reason.

TextSearchInString() searches in a single text chunk for a passed text string. If a match is found, a pointer to that match (and the length of the match) are returned in buffers. TextSearchInHugeArray() performs the same searching operations, but can return the segment and offset of a found match within a huge array.

The following SearchOptions may affect the search procedure:

SO_NO_WILDCARDS
If set, the search mechanism ignores any passed WildCard values and treats them as the control character values (CTRL_P, CTRL_Q and CTRL_R) which they overlap.
SO_IGNORE_SOFT_HYPHENS
If set, soft hyphens in the "searched-in" text are treated as if they do not exist, therefore allowing the text "hyphenation" to match "hyphen-ation" in the destination text. If the match string contains soft hyphens, do not set this flag as strings will never match.
SO_BACKWARD_SEARCH
If set, the search will propagate backwards.
SO_IGNORE_CASE
If set, case will be ignored in the search criteria.
SO_PARTIAL_WORD
If set, partial words will be matched.
SO_PRESERVE_CASE_OF_DOCUMENT_STRING
If set, the case of the match string will be altered to preserve the case of the before

Both routines may take wild cards (type WildCard ) in the place of any character. These wildcards tell the search mechanism to accept any character in the allotted space. Passing WC_MATCH_MULTIPLE_CHAR instructs the search mechanism to accept any number of random characters in that place in the search string. The values of WildCard are:

WC_MATCH_SINGLE_CHAR
WC_MATCH_MULTIPLE_CHAR
WC_MATCH_WHITESPACE_CHAR

You can set higher-level features of your text objects with the SearchReplaceControlClass and SpellControlClass controllers. These controllers allow spell-checking and search and replace. These two controllers are designed to interact with each other and use common messages.

SearchReplaceControlClass

This controller allows the use of the text object's built-in search and replace features. The controller must be placed on the GAGCNLT_SELF_LOAD_OPTIONS Application GCN list. This controller sends out the following messages to its GCI_ output , which may be intercepted to provide custom behavior:

MSG_SEARCH
MSG_REPLACE_CURRENT
MSG_REPLACE_ALL_OCCURENCES
MSG_REPLACE_ALL_OCCURENCES_IN_SELECTION
MSG_META_GET_OBJECT_FOR_SEARCH_SPELL
MSG_META_DISPLAY_OBJECT_FOR_SEARCH_SPELL

Because many of these messages are shared by the SpellControlClass, they are described in Shared Functionality .

The Search & Replace messages make use of a SearchReplaceStruct . This structure is followed by text string(s) that represent the strings to search (and replace if that is the case). These strings may contain WildCard values (unless SO_NO_WILDCARDS is set in params ).The SearchReplaceStruct is shown below:

typedef struct {
    word		searchSize;
    word		replaceSize;
    byte		params;
    optr		replyObject;
    Message		replyMsg;
} SearchReplaceStruct;

searchSize stores the number of characters in the search string, including the null terminator.

replaceSize stores the number of characters in the replace string (if present), including the null terminator.

params stores the SearchOptions in use for this operation.

replyObject stores the optr of the object to send the replyMsg to if the string is not found.

replyMsg stores the message sent to the replyObject above.

The block containing this SearchReplaceStruct will contain either one null-terminated string (if the operation is just a simple search operation) or a null-terminated search string followed by a null-terminated replace string (if the operation is a search & replace).

MSG_SEARCH

@importMessage MetaSearchSpellMessages, void MSG_SEARCH(
        MemHandle		searchInfo);

This message is sent by the SearchReplace controller when an object is starting a search operation. You may intercept this message to find out the nature and specifics of the search.

Source: Search controller.

Destination: GCI_ output of the search controller (usually a text object).

Parameters: searchInfo Handle of block containing the SearchReplaceStruct . The search string immediately follows this structure in the passed block. This block should be freed by the message handler.

Interception: Intercept to find out the nature of the search and replace operation.

The features and tools of this controller are shown in SearchReplaceControlClass Features .

Code Display 10-40 SearchReplaceControlClass Features

  typedef WordFlags SRCFeatures;
#define SRCF_CLOSE					0x200
#define SRCF_FIND_NEXT					0x100
#define SRCF_FIND_PREV					0x80
#define SRCF_REPLACE_CURRENT					0x40
#define SRCF_REPLACE_ALL_IN_SELECTION					0x20
#define SRCF_REPLACE_ALL					0x10
#define SRCF_PARTIAL_WORDS					0x08
#define SRCF_IGNORE_CASE					0x04
#define SRCF_WILDCARDS					0x02
#define SRCF_SPECIAL_CHARS					0x01
  typedef WordFlags SRCToolboxFeatures;
#define SRCTF_SEARCH_REPLACE					0x01
#define  SRC_DEFAULT_FEATURES				(SRCF_FIND_NEXT | SRCF_FIND_PREV |
                 SRCF_REPLACE_CURRENT | SRCF_REPLACE_ALL |
                 SRCF_PARTIAL_WORDS | SRCF_IGNORE_CASE |
                 SRCF_WILDCARDS | SRCF_SPECIAL_CHARS |
                 SRCF_REPLACE_ALL_IN_SELECTION | SRCF_CLOSE)
#define  SRC_DEFAULT_TOOLBOX_FEATURES (SRCTF_SEARCH_REPLACE)

SpellControlClass

This controller allows you to include the advanced spelling-checker features of the text object. The controller must be placed on the GAGCNLT_SELF_LOAD_OPTIONS Application GCN list. The SpellControlClass controller defaults to a state of not enabled; it should be set GS_ENABLED before use.

If the controller will affect a text object within a document object, the SpellControl should be added to the GenDocumentControlClass object's GDCI_ enableDisableList . This will ensure that the spell controller is automatically enabled when text appears within the document.

This controller sends out the following messages, which may be intercepted to provide custom behavior:

MSG_SPELL_CHECK
MSG_REPLACE_CURRENT
MSG_REPLACE_ALL_OCCURENCES
MSG_META_GET_CONTEXT
MSG_META_CONTEXT
MSG_META_GET_OBJECT_FOR_SEARCH_SPELL
MSG_META_DISPLAY_OBJECT_FOR_SEARCH_SPELL

MSG_SPELL_CHECK

@importMessage MetaSearchSpellMessages, void MSG_SPELL_CHECK(@stack
        optr		replyObj,
        dword		numCharsToCheck,
        SpellCheckOptions		options,
        MemHandle		ICbuff);

This message is sent by the Spell controller to continue spell checking from the current position in the document.

Source: Spell controller.

Destination: GCI_ output of the spell controller.

Parameters: replyObj Object to send MSG_SPELL_CHECK_COMPLETED.

numCharsToCheck
Number of characters to check (if options has SCO_CHECK_NUM_CHARS set).
options
SpellCheckOptions in use for this operation.
IC buff
Handle of buffer to pass spell check library.

Interception: Intercept to find out the nature of the spell checking operation.

MSG_META_GET_CONTEXT

@importMessage MetaSearchSpellMessages, void MSG_META_GET_CONTEXT(@stack
        dword		position,
        ContextLocation		location,
        word		numCharsToGet,
        optr		replyObj);

This message is sent by the Spell controller to find out the current context of the text object. Other objects may also send this message to the text object. The text object will return MSG_META_CONTEXT to the requestor.

Source: The Spell controller. (Other objects may want to send this to retrieve the text context displayed in the spell box.)

Destination: GCI_ output of the Spell controller (usually a text object).

Parameters: position Position of the context. The context returned depends on this value and the ContextLocation passed in location .

location
ContextLocation . This may be one of the following values:
CL_STARTING_AT_POSITION
CL_ENDING_AT_POSITION
CL_CENTERED_AROUND_POSITION
CL_CENTERED_AROUND_SELECTION
CL_CENTERED_AROUND_SELECTION_START
CL_SELECTED_WORD
numCharsToGet
Maximum number of characters to return.
replyObj
Optr of the object to reply to via MSG_META_CONTEXT.

Interception: You may intercept to alter the nature of the context request.

MSG_META_CONTEXT

@importMessage MetaSearchSpellMessages, void MSG_META_CONTEXT(
        MemHandle		data);

This message returns the current context to the SpellControl object.

Source: A text object receiving MSG_META_GET_CONTEXT.

Destination: The object that sent the request.

Parameters: data ContextData structure storing information on the context, and the null-terminated context string.

Structures:

typedef struct {
    optr			CD_object;
    dword			CD_numChars;
    dword			CD_startPos;
    VisTextRange			CD_selection;
} ContextData;

CD_ object stores the optr of the object that the context is coming from.

CD_ numChars stores the number of characters in the text object.

CD_ range stores the range of characters that this context represents within the text object.

CD_ selection stores the range of characters that represent the current text selection.

The null-terminated context data follows this structure.

Interception: Generally not intercepted.

Its feature and tool definitions are shown in SpellControlClass .

Code Display 10-41 SpellControlClass

  typedef WordFlags SpellFeatures;
#define SF_CLOSE				0x2000
#define SF_CONTEXT				0x1000
#define SF_SIMPLE_MODAL_BOX				0x0800
#define SF_SUGGESTIONS				0x0400
#define SF_CHECK_ALL				0x0200
#define SF_CHECK_TO_END				0x0100
#define SF_CHECK_SELECTION				0x0080
#define SF_SKIP				0x0040
#define SF_SKIP_ALL				0x0020
#define SF_REPLACE_CURRENT				0x0010
#define SF_REPLACE_ALL				0x0008
#define SF_ADD_TO_USER_DICTIONARY				0x0004
#define SF_EDIT_USER_DICTIONARY				0x0002
#define SF_STATUS				0x0001
  typedef WordFlags SpellToolboxFeatures;
#define STF_SPELL				0x01
#define  SC_DEFAULT_FEATURES					(SF_STATUS | SF_EDIT_USER_DICTIONARY |
        SF_ADD_TO_USER_DICTIONARY | SF_REPLACE_ALL | SF_REPLACE_CURRENT |
        SF_SKIP_ALL | SF_SKIP | SF_CHECK_SELECTION | SF_CHECK_TO_END |
        SF_SUGGESTIONS | SF_CLOSE | SF_CONTEXT)
#define  SC_DEFAULT_TOOLBOX_FEATURES					STF_SPELL

Shared Functionality

The SearchReplace and Spell controllers share a common range of messages ( MetaSearchSpellMessages ) imported from MetaClass . (The messages MSG_SEARCH and MSG_SPELL_CHECK are specific to their respective controllers and were described earlier.) These messages common to both controllers are listed and described here.

MSG_REPLACE_CURRENT

@importMessage MetaSearchSpellMessages, void MSG_REPLACE_CURRENT(
        MemHandle		replaceInfo);

This message is sent by the SearchReplace controller when an object is starting a search operation that should replace the found match with the passed replacement string. This message involves a single replacement operation. To perform a replace-all operation, use MSG_REPLACE_ALL_OCCURENCES.

Source: Search controller.

Destination: GCI_ output of the search controller (usually a text object).

Parameters: replaceInfo Handle of block containing the SearchReplaceStruct . The null-terminated search string immediately follows this structure. The null-terminated replace string follows that string. This block should be freed by the message handler.

Interception: Intercept to find out the nature of the replacement operation.

MSG_REPLACE_ALL_OCCURRENCES

@importMessage MetaSearchSpellMessages, void MSG_REPLACE_ALL_OCCURRENCES(
        MemHandle		replaceInfo,
        Boolean		replaceFromBeginning);

This message is sent by the SearchReplace controller when an object is starting a search operation that should replace all found matches (in the search string) with the passed replacement string.

Source: Search controller.

Destination: GCI_ output of the search controller (usually a text object).

Parameters: replaceInfo Handle of block containing the SearchReplaceStruct . The null-terminated search string immediately follows this structure. The null-terminated replace string follows that string. This block should be freed by the message handler.

replaceFromBeginning
Non-zero if the replace-all operation should proceed from the beginning of the document; normally, the replace-all operation begins after the current position.

Interception: Intercept to find out the nature of the replacement operation.

MSG_REPLACE_ALL_OCCURRENCES_IN_SELECTION

@importMessage MetaSearchSpellMessages, void MSG_REPLACE_ALL_OCCURRENCES_IN_SELECTION(
        MemHandle		replaceInfo,
        Boolean		replaceFromBeginning);

This message is sent by the SearchReplace controller when an object is starting a search operation that should replace all found matches within a selection (in the search string) with the passed replacement string.

Source: Search controller.

Destination: GCI_ output of the search controller (usually a text object).

Parameters: replaceInfo Handle of block containing the SearchReplaceStruct . The null-terminated search string immediately follows this structure. The null-terminated replace string follows that string. This block should be freed by the message handler.

replaceFromBeginning
Non-zero if the replace-all operation should proceed from the beginning of the selection; normally, the replace-all operation begins after the current position.

Interception: Intercept to find out the nature of the replacement operation.

MSG_META_GET_OBJECT_FOR_SEARCH_SPELL

@importMessage MetaSearchSpellMessages, void MSG_META_GET_OBJECT_FOR_SEARCH_SPELL(
        GetSearchSpellObjectOption					option,
        optr					curObject);

This message is sent by a text object to itself when a search reaches the end of the current object and needs to determine where to continue the search.

Source: Any text object.

Destination: The text object sends this to itself.

Parameters: option GetSearchSpellObjectOption specifying the next object to continue the search at. This value may be GSSOT_FIRST_OBJECT, GSSOT_LAST_OBJECT, GSSOT_NEXT_OBJECT, or GSSOT_PREV_OBJECT.

curObject
Optr of the object that is currently being searched through. If you intercept this message, you can use this information to set the option above.

Interception: May intercept if you want to direct where a search or spell check operation should continue to.

MSG_META_DISPLAY_OBJECT_FOR_SEARCH_SPELL

@importMessage MetaSearchSpellMessages, void MSG_META_DISPLAY_OBJECT_FOR_SEARCH_SPELL();

This message is sent by the text object to itself when a matching word (or misspelled word if the controller is a Spell controller) has been found and should be displayed.

Source: The text object.

Destination: The text object sends this to itself.

Interception: You may intercept this message at the text object to receive notification of a match.

| Up: GEOS SDK TechDocs | Up | Prev: 7.2 Paragraph Attribute Controllers


GenTrigger

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenTrigger Overview
| One-page Version of this Chapter for Easy Printing

The GenTrigger object allows a user to initiate an action. In most graphical user interfaces, a GenTrigger is represented by a button; clicking on the button initiates the action. This action sends a pre-specified message to a pre-specified object or process in the trigger's instance data. You may also customize a GenTrigger to pass data along with the message.

1 GenTrigger Overview
2 GenTrigger Usage
3 Supplemental GenTrigger Usage
3.1 Passing Data with a GenTrigger
3.2 Interaction Commands
3.3 Interpreting Double-Clicks
3.4 Initiating an Action
3.5 Setting a Trigger As the Default
3.6 Manipulating Instance Data
3.7 Other Hints
4 Customizations

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenTrigger Overview
| One-page Version of this Chapter for Easy Printing


GenTrigger Overview

GenTrigger: 1 GenTrigger Overview

| Up: GEOS SDK TechDocs | Up | Prev: GenTrigger | Next: 2 GenTrigger Usage

Whenever you need to allow the user to initiate a single action, you should probably use a GenTrigger. When activated, a GenTrigger simply sends a message to another object or process. Both the message and its destination can be set up in the GenTrigger's instance data. The trigger can also send data with the message. This is useful if several triggers send the same message (for example MSG_CHOOSE_COLOR) but send different data (for example "Blue," "Red," or "Green").

In some cases, the use of triggers is readily apparent (the "OK" button in a dialog box, for example, which will dismiss the dialog box) while at other times the use of triggers is not as obvious (as menu items, for example). The GenTrigger is a simple and widely used object, appearing in menus, dialog boxes, and primary windows--wherever you need a UI object whose sole action is to send out a message, a GenTrigger will probably suffice.

Because of the trigger's basic role, they cannot have children. Note that this does not mean that activating a trigger may not necessarily bring up an object indirectly (such as a dialog box), but only that an object may not be directly attached below a GenTrigger in a generic object tree.

Your application will choose the function of the trigger but it is left to the Specific UI to choose in what manner a trigger will be activated. (For example, under OSF/Motif, a GenTrigger may be built as a button and will be activated by clicking on the button.)

GenTriggerClass provides two instance fields. These instance fields are listed in GenTrigger Instance Fields .

Code Display 5-1 GenTrigger Instance Fields

/* GenTrigger inherits all the instance fields of GenClass. */
    @instance optr GTI_destination = 0;
    @instance Message GTI_actionMsg = 0;
@vardata void HINT_TRIGGER_BRINGS_UP_WINDOW;
@vardata void ATTR_GEN_TRIGGER_IMMEDIATE_ACTION;
@vardata void HINT_TRIGGER_DESTRUCTIVE_ACTION;
@vardata word ATTR_GEN_TRIGGER_ACTION_DATA;
@vardata word ATTR_GEN_TRIGGER_INTERACTION_COMMAND;
@vardata Message ATTR_GEN_TRIGGER_CUSTOM_DOUBLE_PRESS;
    /* Alias for messages with "void (word a, word b)" */
 typedef struct { word a, b; } TwoWordArgs;
@vardataAlias (ATTR_GEN_TRIGGER_ACTION_DATA)
        TwoWordArgs ATTR_GEN_TRIGGER_ACTION_TWO_WORDS;
    /* Alias for messages with "void (word a, word b, word c)" */
 typedef struct { word a, b, c; } ThreeWordArgs;
@vardataAlias (ATTR_GEN_TRIGGER_ACTION_DATA)
        ThreeWordArgs ATTR_GEN_TRIGGER_ACTION_THREE_WORDS;
    /* Alias for messages with "void (optr output, word a)" */

The GTI _actionMsg instance field sets the message for the trigger to send out upon activation. The activation itself is determined by the specific UI. The trigger will send this message to the output specified in the GTI_ destination instance field.

The GTI _destination instance field sets the object or process for the trigger to send its message to upon activation. The trigger will send the message in the GTI _actionMsg field to this optr. Often this will be a TravelOption or a GenTravelOption .

| Up: GEOS SDK TechDocs | Up | Prev: GenTrigger | Next: 2 GenTrigger Usage


GenTrigger Usage

GenTrigger: 2 GenTrigger Usage

| Up: GEOS SDK TechDocs | Up | Prev: 1 GenTrigger Overview | Next: 3 Supplemental GenTrigger Usage

A simple GenTrigger should contain a message to send out upon activation, a destination to send the message to, and a visual moniker to distinguish the trigger from other objects (and describe its function to the user).

You may use the prototype GEN_TRIGGER_ACTION to define the action message. This prototype passes the optr of the sending trigger in the variable name `trigger' and can be accessed within a message handler.

GenTriggers may appear within GenPrimarys, GenViews, or, most often, within a GenInteraction. If a GenTrigger is placed as a child of a GIV_POPUP GenInteraction, the GenTriggers may be represented as menu items.

Code Display 5-2 Code for Basic GenTriggers

/* Use the prototype GEN_TRIGGER_ACTION to define a GenTrigger's action message.
 * Within a method, you can retrieve this optr with the variable name `trigger.' */
@message (GEN_TRIGGER_ACTION) MSG_HELLO_CHANGE_TO_BLUE();
@message (GEN_TRIGGER_ACTION) MSG_HELLO_CHANGE_TO_GOLD();
/* The GenInteraction object will contain the child GenTriggers. */
@object GenInteractionClass HelloColorBox = {
    GI_comp = @HelloBlueTrigger, @HelloGoldTrigger;
    GI_visMoniker = 'C', "Color";
    GII_visibility = GIV_DIALOG;
}
/* GenTriggers
 * Buttons are implemented by GenTriggerClass. When the trigger is pushed by
 * the user (clicked on with the mouse), it will send the specified message
 * to the specified destination object. In both cases below, the trigger will
 * send an application-defined message to the application's Process
 * object. A trigger's moniker is displayed within the trigger. In this
 * case both are text, but any graphics string could also be used to create
 * graphical triggers. */
@object GenTriggerClass HelloBlueTrigger = {
    GI_visMoniker = 'B', "Blue";
    /* Send MSG_HELLO_CHANGE_TO_BLUE to the Process object. */
    GTI_destination = process;
    GTI_actionMsg = MSG_HELLO_CHANGE_TO_BLUE;
}
@object GenTriggerClass HelloGoldTrigger = {
    GI_visMoniker = 'G', "Gold";
    /* Send MSG_HELLO_CHANGE_TO_GOLD to the Process object. */
    GTI_destination = process;
    GTI_actionMsg = MSG_HELLO_CHANGE_TO_GOLD;
}
| Up: GEOS SDK TechDocs | Up | Prev: 1 GenTrigger Overview | Next: 3 Supplemental GenTrigger Usage


Supplemental GenTrigger Usage

GenTrigger: 3 Supplemental GenTrigger Usage

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 GenTrigger Usage | Next: 4 Customizations

GenTriggers may also perform other actions besides the simple sending of a message to an object.

For example, your trigger may pass data along with its message; you may then use this data within a method to perform some additional work. In some cases, this data may be interpreted by a GenInteraction object to provide additional functionality to dialog boxes.

You may also alter the manner in which a trigger is initiated through the implementation of hints and several messages. You can also modify the trigger's instance data dynamically, reassigning its action message and destination.

Passing Data with a GenTrigger

Interaction Commands

Interpreting Double-Clicks

Initiating an Action

Setting a Trigger As the Default

Manipulating Instance Data

Other Hints

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 GenTrigger Usage | Next: 4 Customizations


Passing Data with a GenTrigger

GenTrigger: 3.1 Supplemental GenTrigger Usage: Passing Data with a GenTrigger

| Up: GEOS SDK TechDocs | Up | Prev: 3 Supplemental GenTrigger Usage | Next: 3.2 Interaction Commands
ATTR_GEN_TRIGGER_ACTION_DATA, ATTR_GEN_TRIGGER_ACTION_TWO_WORDS, ATTR_GEN_TRIGGER_ACTION_THREE_WORDS

A GenTrigger may pass data along with its message. Use ATTR_GEN_TRIGGER_ACTION_DATA to assign a word of data that you wish to pass with the particular message. If you need to pass more than a single word of data, you will have to use @vardataAlias ; this Goc keyword allows ATTR_GEN_TRIGGER_ACTION_DATA to pass a structure instead of a single word.

Several ATTR_GEN_TRIGGER_ACTION_DATA aliases already exist for your use. ATTR_GEN_TRIGGER_ACTION_TWO_WORDS and ATTR_GEN_TRIGGER_ACTION_THREE_WORDS allow you to pass two or three words of data along with the trigger's message .

If you need to pass more than three words of data with a message, you will need to use the stack to pass arguments. To do so, use the @stack parameter within your message definition; because of the implementation of the stack, make sure to set up your structure to pass its arguments in reverse order .

Code Display 5-3 Passing Data from a GenTrigger

/* In a class declaration, you should define a message that passes data. */
@class MyProcessClass, GenProcessClass;
@message void MSG_SET_MY_DATA(int myData);
@endc;
@object GenTriggerClass MyHundredTrigger = {
    /* This trigger will pass 100 to the method for MSG_SET_MY_DATA. */
    GI_visMoniker = "Set My Data to 100";
    GTI_actionMsg = MSG_SET_MY_DATA;
    GTI_destination = process;
    ATTR_GEN_TRIGGER_ACTION_DATA = 100;					/* This object's data is 100. */
}
@object GenTriggerClass MyTenTrigger = {
    /* This trigger will pass `10' to the method for MSG_SET_MY_DATA.*/
    GI_visMoniker = "Set My Data to 10";
    GTI_actionMsg = MSG_SET_MY_DATA;
    GTI_destination = process;
    ATTR_GEN_TRIGGER_ACTION_DATA = 10;					/* This object's data is 10. */
}
@method MyProcessClass, MSG_SET_MY_DATA {
    /* This message passes one parameter (the integer myData). Goc knows
     * that the data is located within the ATTR_GEN_TRIGGER_ACTION_DATA
     * field. */
    UpdateDisplay(myData);				/* Use that data for your own purposes. */
}
/* The following examples show how to pass longer structures than the above. */
    /* Define the message. */
@message void MSG_CUSTOM_MESSAGE(@stack char name[10], optr arg1, int arg2);
    /* Define the structure you wish to pass with the message. If the
     * message will pass parameters on the stack (as in this case), you
     * must define your structure to pass its elements in reverse order. */
typedef struct {
    int		MS_arg2;
    optr		MS_arg1;
    char		MS_name[10]
} MyStruct;
    /* Use @vardataAlias to define your own attribute (ATTR_MY_STRUCT_TO_PASS)
     * to store the custom structure. */
@vardataAlias (ATTR_GEN_TRIGGER_ACTION_DATA) MyStruct ATTR_MY_STRUCT_TO_PASS;
    /* Declare your object and set the arguments to pass. */
@object GenTriggerClass MyTrigger = {
    GI_visMoniker = "Custom Trigger";
    GTI_actionMsg = MSG_CUSTOM_MESSAGE;
    GTI_destination = process;
    ATTR_MY_STRUCT_TO_PASS = { 100, @ListObject, "Zow!" }
}
| Up: GEOS SDK TechDocs | Up | Prev: 3 Supplemental GenTrigger Usage | Next: 3.2 Interaction Commands


Interaction Commands

GenTrigger: 3.2 Supplemental GenTrigger Usage: Interaction Commands

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Passing Data with a GenTrigger | Next: 3.3 Interpreting Double-Clicks
ATTR_GEN_TRIGGER_INTERACTION_COMMAND

A GenTrigger may perform special actions within a GenInteraction object. The ATTR_GEN_TRIGGER_INTERACTION_COMMAND attribute sets the special activity for these types of triggers. This attribute indicates that the GenTrigger is an Interaction command trigger for the parent GenInteraction object. ATTR_GEN_TRIGGER_INTERACTION_COMMAND takes a word of data (of type InteractionCommand ) specifying the command type of the trigger. A trigger with an InteractionCommand can perform one of several standard actions. Most of these actions are specific to dialog boxes.

The InteractionCommand types are

IC_NULL
This command is special, alerting an application that the interaction containing the trigger has been closed because the system is about to shut down. You should never set any triggers to this InteractionCommand .
IC_DISMISS
This command instructs the UI to dismiss the parent GenInteraction. Set this if you want the trigger to dismiss an Interaction or provide a custom "Close" trigger.
IC_INTERACTION_COMPLETE
This command is special, indicating to the UI that a single interaction has been completed (the user has clicked on an InteractionCommand trigger marked with GA_SIGNAL_INTERACTION_COMPLETE). You should never set any triggers to this InteractionCommand . If you wish a trigger to exhibit this behavior, set its GI_ attrs field to include GA_SIGNAL_INTERACTION_COMPLETE. (See the GenClass chapter.)

IC_APPLY This command instructs the UI to apply any properties within a GIT_PROPERTIES Interaction. By default, this trigger will send out a MSG_GEN_APPLY .

IC_RESET This command instructs the UI that this is a "reset" trigger for a properties Interaction; the application is responsible for actual reset behavior. You should provide a method to handle the message sent out by this trigger, set up the state of items to reset, and call MSG_GEN_RESET within that handler.
IC_OK
This command instructs the UI that the user has acknowledged a GIT_NOTIFICATION and should take any appropriate measures.
IC_YES
This command instructs the UI that the user has signalled a positive response from a GIT_AFFIRMATION Interaction.
IC_NO
This command instructs the UI that the user has signalled a negative response from a GIT_AFFIRMATION Interaction.
IC_STOP
This command instructs the UI to halt a GIT_PROGRESS Interaction; (the application is responsible for halting any ongoing operations related to that Interaction).
IC_EXIT
This command is special, causing the trigger to exit the application. Typically, the File menu already has a trigger set to this command.
IC_HELP
This command indicates that this GenTrigger brings up help, searching for the proper help context (ATTR_GEN_HELP_CONTEXT) and bringing up the help dialog box with that context. Any contexts below the trigger's containing dialog are ignored, however; i.e. the lowest possible context is the that on the dialog box itself. For more information on Help, see the Help Library chapter.

In most cases, you should not provide a visual moniker for any triggers with an InteractionCommand ; the specific UI will select an appropriate moniker. (You may supply a moniker if you wish to override the specific UI's selection, however.)

Activating an ATTR_GEN_TRIGGER_INTERACTION_COMMAND trigger will cause the UI to send MSG_GEN_GUP_INTERACTION_COMMAND to the trigger itself. This message will travel up to the appropriate GenInteraction object, where it will be handled.

This activity only occurs if both the trigger's destination and its action message fields are null, however. If these fields are non-null (specifying your own action message and destination), the sending of the InteractionCommand will be overridden. You may want to do this to replace the default functionality of a Specific UI-supplied trigger. For complete usage of an InteractionCommand trigger within a GenInteraction object, see the GenInteractionClass chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Passing Data with a GenTrigger | Next: 3.3 Interpreting Double-Clicks


Interpreting Double-Clicks

GenTrigger: 3.3 Supplemental GenTrigger Usage: Interpreting Double-Clicks

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Interaction Commands | Next: 3.4 Initiating an Action
ATTR_GEN_TRIGGER_CUSTOM_DOUBLE_PRESS

Your trigger sends out the message in the GTI_ actionMsg field and data in the ATTR_GEN_TRIGGER_ACTION_DATA field whenever the user clicks on the trigger, whether that click is a single or a double-click. If you wish your trigger to send out an alternate message if the user double-clicks, use ATTR_GEN_TRIGGER_CUSTOM_DOUBLE_PRESS .

If you set a message for this attribute, the trigger may send out two different messages: one for a single click action and another for a double-click action.

Code Display 5-4 A Trigger with a Double-Click Message

@object GenTriggerClass DoubleClickTrigger = {
/* If the user single-clicks on this trigger, MSG_MY_ACTION will be sent along
 * with data1 to the process. If the user double-clicks,
 * MSG_MY_DOUBLE_PRESS_ACTION will be sent instead. */
    GTI_actionMsg = MSG_MY_ACTION;
    GTI_destination = process;
    GI_visMoniker = "Single or Double Click!";
    ATTR_GEN_TRIGGER_ACTION_DATA = data1;
    ATTR_GEN_CUSTOM_DOUBLE_PRESS = MSG_MY_DOUBLE_PRESS_ACTION;
}
| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Interaction Commands | Next: 3.4 Initiating an Action


Initiating an Action

GenTrigger: 3.4 Supplemental GenTrigger Usage: Initiating an Action

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Interpreting Double-Clicks | Next: 3.5 Setting a Trigger As the Default
MSG_GEN_TRIGGER_SEND_ACTION, MSG_GEN_ACTIVATE

In almost all cases, the specific UI initiates the action of the GenTrigger. Once the specific UI determines that a trigger is activated, it will send a MSG_GEN_TRIGGER_SEND_ACTION to the object, which will in turn send the message in GTI_ actionMsg to the object in GTI_ destination . In some rare cases, however, you may wish to manually "trigger" the GenTrigger yourself. This can be done by sending the object the MSG_GEN_TRIGGER_SEND_ACTION yourself.

Note that this message, in effect, skips the specific UI and thus any specific functionality defined for activating a trigger will be ignored. In OSF/Motif, for example, a trigger may "blink" (display itself in reverse video) when activated. To perform the default specific UI behavior, send the object a MSG_GEN_ACTIVATE instead, which will perform the default activation for an object and then call MSG_GEN_TRIGGER_SEND_ACTION .

MSG_GEN_TRIGGER_SEND_ACTION

void	MSG_GEN_TRIGGER_SEND_ACTION(
        Boolean doublePressFlag);

This message causes a GenTrigger to send its action message specified in GTI_actionMsg to the output specified in GTI_ destination if the object is GS_USABLE. This message is sent automatically by the UI as part of the default behavior when a GenTrigger is activated by the specific UI. If you wish to simulate a double-click, sending the message included in an object's ATTR_GEN_TRIGGER_CUSTOM_DOUBLE_PRESS, pass this message with a value of true . Double-press messages may not pass any data in a trigger's ATTR_GEN_TRIGGER_ACTION_DATA.

Slightly more useful is the GenClass message MSG_GEN_ACTIVATE , which activates the trigger at the specific UI's level, performing any default activation behavior (such as making the trigger blink to show that it is being activated).

Source: Unrestricted. Usually the specific UI.

Destination: Any usable GenTrigger object

Parameters: doublePressFlag Pass true to use the message stored in ATTR_GEN_TRIGGER_CUSTOM_DOUBLE_PRESS rather than message in GTI_ actionMsg .

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_ACTIVATE

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Interpreting Double-Clicks | Next: 3.5 Setting a Trigger As the Default


Setting a Trigger As the Default

GenTrigger: 3.5 Supplemental GenTrigger Usage: Setting a Trigger As the Default

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Initiating an Action | Next: 3.6 Manipulating Instance Data
MSG_GEN_TRIGGER_MAKE_DEFAULT_ACTION

Often, a dialog box or other windowed generic object will have several triggers. You will likely want one of these triggers to be the default action--the default action is activated when the user double-clicks on some item in the dialog or when the user hits Return, typically. (Specific UIs can determine how the activation occurs.)

To set a trigger the default, use HINT_DEFAULT_DEFAULT_ACTION ; this only works for the first time the trigger is created, though. To set a trigger to be the default action item dynamically, use the message MSG_GEN_TRIGGER_MAKE_DEFAULT_ACTION .

MSG_GEN_TRIGGER_MAKE_DEFAULT_ACTION

void	MSG_GEN_TRIGGER_MAKE_DEFAULT_ACTION();

This message sets the trigger to be the default action in its windowed parent. It is the message equivalent of HINT_DEFAULT_DEFAULT_ACTION , defined in GenClass .

Source: Unrestricted.

Destination: Any GenTrigger object.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Initiating an Action | Next: 3.6 Manipulating Instance Data


Manipulating Instance Data

GenTrigger: 3.6 Supplemental GenTrigger Usage: Manipulating Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Setting a Trigger As the Default | Next: 3.7 Other Hints
MSG_GEN_TRIGGER_GET_ACTION_MSG, MSG_GEN_TRIGGER_SET_ACTION_MSG, MSG_GEN_TRIGGER_GET_DESTINATION, MSG_GEN_TRIGGER_SET_DESTINATION

A GenTrigger's instance data need not be static. The instance data can be altered dynamically using the following messages of GenTriggerClass .

MSG_GEN_TRIGGER_GET_ACTION_MSG

Message MSG_GEN_TRIGGER_GET_ACTION_MSG();

This message retrieves the GTI_ actionMsg instance data of the trigger.

Source: Unrestricted.

Destination: Any GenTrigger object.

Parameters: None.

Return: Message in GTI_ actionMsg .

Interception: Generally not intercepted.

MSG_GEN_TRIGGER_SET_ACTION_MSG

void	MSG_GEN_TRIGGER_SET_ACTION_MSG(
        Message message);

This message sets the GTI_ actionMsg instance field of a GenTrigger to the message passed.

Source: Unrestricted.

Destination: Any GenTrigger object.

Parameters: message Message to assign GTI_ actionMsg to.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_TRIGGER_GET_DESTINATION

optr	MSG_GEN_TRIGGER_GET_DESTINATION();

This message returns the GTI_ destination instance data field of the GenTrigger.

Source: Unrestricted.

Destination: Any GenTrigger object.

Parameters: Nothing.

Return: The optr of destination object (in GTI_ destination ).

Interception: Generally not intercepted.

MSG_GEN_TRIGGER_SET_DESTINATION

void	MSG_GEN_TRIGGER_SET_DESTINATION(
        optr dest);

This message sets the GTI_ destination instance field of the GenTrigger to the optr passed.

Source: Unrestricted.

Destination: Any GenTrigger object.

Parameters: dest The optr of the new destination.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Setting a Trigger As the Default | Next: 3.7 Other Hints


Other Hints

GenTrigger: 3.7 Supplemental GenTrigger Usage: Other Hints

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Manipulating Instance Data | Next: 4 Customizations
HINT_TRIGGER_BRINGS_UP_WINDOW, ATTR_GEN_TRIGGER_IMMEDIATE_ACTION, HINT_TRIGGER_DESTRUCTIVE_ACTION

Besides the hints discussed in earlier sections and the hints inherited from GenClass , GenTriggerClass provides two others, as well as one additional attribute:

  • HINT_TRIGGER_BRINGS_UP_WINDOW
     Use this hint if your trigger action indirectly brings up a window (such as a GIV_DIALOG GenInteraction or some other interaction window). This hint, depending on the specific UI, may be used to display the button with an ellipsis "..." to symbolize that the trigger leads to some other interaction.
  • ATTR_GEN_TRIGGER_IMMEDIATE_ACTION
     Use this attribute if your trigger is in a menu (GIV_POPUP interaction) and its action should take place immediately before other pending UI events. This will prevent default UI events from occurring before the trigger's action. For example, this attribute is used for the tack trigger in the menu box, to prevent the menu from being closed before it is tacked.
  • HINT_TRIGGER_DESTRUCTIVE_ACTION
    Use this hint if your action may be potentially destructive. This hint prevents the trigger from being recognized as the default trigger.

For more information on other hints not specific to GenTriggers see the Managing UI Geometry chapter and hints defined under GenClass .

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Manipulating Instance Data | Next: 4 Customizations


Customizations

GenTrigger: 4 Customizations

| Up: GEOS SDK TechDocs | Up | Prev: 3 Supplemental GenTrigger Usage

You may customize a GenTrigger using all the basic GenClass attributes as shown in the GenClass chapter. Of special significance are the ways in which you can tailor a moniker to print a graphics string on a button, as in a DOS room icon. The moniker should have as its argument a GString. (See the Graphics Environment chapter).

Code Display 5-5 Code for a "Graphic Button" GenTrigger

@start TriggerGraphicsMonikerResource, notDetachable;
@visMoniker TriggerGraphicsMoniker = {
    size = standard;
    color = color4;
    aspectRatio = normal;
    cachedSize = 95, 70;
    gstring {
        GSBeginString
        GSDrawBitmapAtCP <(EndTriggerGraphicsIcon-
                    StartTriggerGraphicsIcon)>
        StartTriggerGraphicsIcon label byte
        <...Bitmap here...>
        EndTriggerGraphicsIcon label byte
        GSEndString
    }
}
@end	TriggerGraphicsMonikerResource;
@object GenTriggerClass GraphicTrigger = {
    GI_visMoniker = list
        {
        TriggerGraphicsMoniker
        }
    GTI_actionMsg = MSG_CUSTOM_MESSAGE;
    GTI_destination = process;
    ATTR_GEN_TRIGGER_ACTION_DATA = {DataToPass};
}

| Up: GEOS SDK TechDocs | Up | Prev: 3 Supplemental GenTrigger Usage


UI Overview

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 The GUI
| One-page Version of this Chapter for Easy Printing

The GEOS user interface (UI) is an integral part of the system software and does an amazing amount of work for applications. The UI is a sophisticated and powerful object library that includes everything from simple buttons to self-scrolling windows to plug-in objects that provide nearly complete word processing.

GEOS also uses a new concept in user interface technology: the generic UI. By using the generic UI, GEOS programs simply declare their UI needs and leave nearly all drawing, screen management, and input management up to GEOS. The generic UI technology also allows a single application executable file to run with several different look-and-feel specifications.

1 The GUI
2 The GEOS User Interface
3 Using the Generic Classes
3.1 The Generic Class Tree
3.2 Creating a Generic Object Tree
4 Using the Visible Classes
4.1 Visible Objects and the GenView
4.2 The Visible Object Document
4.3 Visible Object Abilities
4.4 The Vis Class Tree
4.5 Creating a Visible Object Tree
4.6 Working with Visible Object Trees
5 A UI Example
5.1 What TicTac Illustrates
5.2 What TicTac Does
5.3 The Structure of TicTac
5.4 TicTacBoard Specifics
5.5 TicTacPiece Specifics

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 The GUI
| One-page Version of this Chapter for Easy Printing


The GUI

UI Overview: 1 The GUI

| Up: GEOS SDK TechDocs | Up | Prev: UI Overview | Next: 2 The GEOS User Interface

Ever since the first text-based computer application was created, computer users have looked for easier, more intuitive interfaces to their programs. If the user has to struggle to remember commands or to find the right function, the program is of very little use to him. Thus, the user interface of a program, the program's look and feel, can often be as important as its feature set.

The graphical user interface (GUI) concept has prompted millions of people to begin using computers and has made computer use for millions of others easier. Many corporations and school systems even make GUI a requirement for all their computer acquisitions.

GUIs, however, are not without problems. With so many hundreds of different programs available, even well-designed interfaces can differ significantly from other well-defined interfaces. Although users no longer have to remember whether the "print" command is Ctrl-Alt-P or F4, they may have to search through dozens of menus just to find it. To solve this consistency problem, different manufacturers have created GUI specifications that detail menu structure, color schemes, input management, and many other aspects of the GUI.

GEOS takes this one step further with its Generic User Interface. Application programmers do not have to decide beforehand which GUI specification will be run by their program; nor do they have to write one version for each GUI supported. Instead, they define their generic UI needs and then let GEOS determine the manifestation of those needs at run-time.

Another problem with GUIs is their speed. Without powerful hardware, many GUI-based systems are sluggish because of high overhead associated with geometry management and drawing. GEOS, however, is extremely fast because it is entirely object-oriented and is programmed in assembly language. And, since most of the user interface is managed and drawn by GEOS, applications written for the system automatically take advantage of this speed.

| Up: GEOS SDK TechDocs | Up | Prev: UI Overview | Next: 2 The GEOS User Interface


The GEOS User Interface

UI Overview: 2 The GEOS User Interface

| Up: GEOS SDK TechDocs | Up | Prev: 1 The GUI | Next: 3 Using the Generic Classes

Most programmers who write applications for GEOS will have worked with other GUI systems. Some will even have worked with object-oriented systems. Using the GEOS UI, however, will be a new experience for nearly every programmer.

The GEOS user interface consists of a number of dynamic libraries containing object classes. Applications include objects of these classes to gain various types of functionality. In general, applications will use two different basic types of objects for their UI: Generic objects provide basic UI functionality including windows, menus, and dialog boxes, and their implementation on the screen is determined by GEOS. Visible objects provide more application-specific functionality and offer complete display control to the application.

Generic object classes are all based on the class GenClass . Applications will never use any objects of GenClass , but they will use many objects of its subclasses. A list of these subclasses is shown below, and each of these classes is described in Using the Generic Classes .

Visible object classes are subclassed from VisClass . VisClass may be used often by applications, as will its subclasses. The subclasses of VisClass are shown below, and each is described in Using the Visible Classes .

When to use the generic or visible objects is subject to many criteria. In order to learn more about each of these components of the UI, you should read the two sections following this one.

The GEOS UI also contains many features that will often be used by applications but which don't fall into either the generic or the visible world. These features are called "mechanisms" and include input management, geometry management, general change notification, and the clipboard and quick-transfer functions. As these mechanisms are not UI-specific but are only implemented within the UI libraries, they are documented elsewhere in this book.

| Up: GEOS SDK TechDocs | Up | Prev: 1 The GUI | Next: 3 Using the Generic Classes


Using the Generic Classes

UI Overview: 3 Using the Generic Classes

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 The GEOS User Interface | Next: 4 Using the Visible Classes

Most of your application's UI needs will be satisfied by the use of generic UI objects. Many applications may need only the generic classes. Generic UI objects are powerful and easy to use, and they provide a number of services normally left up to application code.

Generic object classes have no inherent visual representation. Rather, each generic object represents a certain set of UI functions instead of UI components. For example, the GenInteraction class implements grouping and organizational functions; it can appear in several forms including menus and dialog boxes. It can also have no visual representation but merely provide geometry management for other generic objects.

What visual form a generic object takes, if any, is determined by two factors: the specific UI library in use, and the instance data of the generic object.

  • A specific UI library is essentially a "UI driver" that translates the generic class into its specific representation. Just as each video card has its own driver, each UI specification (e.g. OSF/Motif or Presentation Manager) has its own specific UI library. This library uses the generic object's instance data to determine the exact form of that object's output and input. A GenValue object, for example, may be implemented as a spinner object in one specific UI but as a slider object in another.

The generic object's instance data determines the features of the object as implemented by the specific UI. Across different GUI specifications, each similar object may appear different or handle input differently. For example, a button in one GUI may invert itself when pressed while the same button in another GUI might simply darken its outline when pressed. However, both buttons will do the same task. Therefore, a generic object's instance data must be categorized into two types: Attributes are instance data that will cause the same results no matter what GUI is used (e.g. the function the button performs). Hints are instance data that suggest particular implementations that may or may not be implemented by the GUI in use.

The way the generic object gets translated by GEOS into a visible representation is a complicated process. All generic classes are subclasses of GenClass , which is defined as a variant class. This means that GenClass has no defined superclass, that the superclass can be changed from time to time. The superclass is determined when the object is instantiated and resolved--when its visual representation is called for. (See the GEOS Programming chapter.)

The specific UI library contains objects subclassed off VisClass . These objects know how to handle input events and have very specific representation on the screen. These specific UI classes connect to the generic classes through the master/variant mechanism; in this way, the superclass of a generic object is assigned to one of the classes in the specific UI library. The generic object thus inherits the specific class' visible representation.

The above process happens entirely at run-time. The application does not have to have any knowledge of which particular GUI is in use; the same application executable code will work with any specific UI library. Because of the generic UI of GEOS, a user can run the same application under several look-and-feel specifications; this is desirable to the programmer because he only has to code the application once to receive the benefits of several different GUIs.

The Generic Class Tree

Creating a Generic Object Tree

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 The GEOS User Interface | Next: 4 Using the Visible Classes


The Generic Class Tree

UI Overview: 3.1 Using the Generic Classes: The Generic Class Tree

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using the Generic Classes | Next: 3.2 Creating a Generic Object Tree

Generic objects have a tremendous amount of built-in functionality. Much of this is built into GenClass , the topmost class in the generic class tree. For full details on GenClass and the other generic classes, see the Object Reference Book.

Among the features offered by all generic classes are

  • Visual Representation
    Through the specific UI library, every generic object provides its own visual representation. The application does not have to do any gadget drawing.
  • Monikers
    Every generic object can have a moniker, which is a name or graphic that gets displayed along with the object.
  • Input Management
    Through the specific UI library, every generic object properly handles mouse and keyboard input.
  • Messaging
    As with all objects in the system, generic objects can receive and send messages. In addition, generic objects can pass messages up or down the generic object tree automatically.
  • Enabled and Usable States
    Generic objects understand their usable and enabled states. If an object is not enabled, it may be visible but the user cannot invoke its action. If an object is not usable, it will not be visible on the screen (in most specific UIs). Entire object trees can be set usable or enabled with one command.
  • State Saving
    Generic objects automatically save their state when the system shuts down. Therefore, when the system comes back up, dialog boxes and windows will automatically be restored to the same state they were left in when shut down.
  • Object Tree Management
    All generic objects must be part of a generic object tree to be displayed. Generic objects inherently understand the tree structures and functions. UI gadgetry can dynamically be added, moved, or removed.

Each of the different generic classes is described in overview depth below. Note that every one of these classes may be subclassed to add, change, or remove functionality. Changing or removing functions from a generic class is not encouraged, however, as it can cause a specific UI library to give unpredictable results.

GenClass

GenClass provides the functionality basic to all generic objects. GenClass is not used directly by any applications and has no visible representation. Rather, all generic classes are subclassed off GenClass . GenClass provides instance fields common to all of its subclasses. Instance fields of special interest include

  • links between parents and children, providing the means of constructing a generic tree.
  • text or graphics strings to serve as an object's visual moniker.
  • a keyboard accelerator to activate an object through keyboard events.
  • a state field relating to the usable state of an object.
  • an attributes field relating to other default behavior, such as how the object handles busy states when an application is waiting for a routine to finish.

GenClass also implements scores of hints that can affect UI geometry, visual representation, data structures, and functions.

GenApplicationClass

GenApplicationClass provides the basic functionality to open and close applications within GEOS. An object of this class serves as the top object in any application for GEOS.

GenPrimaryClass

GenPrimaryClass is a subclass of GenDisplayClass . The GenPrimary is the chief UI grouping object of an application, and it usually appears as the application's primary window. An application's GenPrimary object manages all controls and output areas that are invoked when an application is first launched. You will usually create a GenPrimary as the sole child of your GenApplication object.

GenTriggerClass

A GenTrigger is a simple pushbutton that executes an action when activated by the user. Typically, the trigger will have a moniker displayed within it and will be activated by a mouse click or by a special keystroke sequence. GenTriggers are very common in applications.

GenInteractionClass

GenInteraction objects are essentially grouping mechanisms. GenInteractions are the key objects for creating both menus and dialog boxes, and they can be used to organize the geometry of other generic objects. Typically, a GenInteraction will have a number of children, each of which will appear within the interaction on the screen. The Interaction itself may or may not have a visible representation.

GenViewClass

The GenView object provides a scrollable window in which the application has complete drawing control. Most applications will use a GenView, and many will use it in conjunction with a VisContent object. The View is extremely powerful, providing all clipping, scrolling, resizing, and scaling automatically. A View can even be splittable or linked to other views. The GenView can display either normal graphic documents or hierarchies of visible objects.

List Classes

Together, GenBoolean, GenBooleanGroup, GenItem, GenItemGroup, and GenDynamicList provide many different types of lists. List objects may be used to create lists that are dynamic or static; scrollable or not; exclusive, non-exclusive, or otherwise. List objects may appear within menus or dialog boxes as well as within an application's primary window.

GenValueClass

The GenValue object allows the user to set a value within a particular range. This may be implemented as a slider, a spinner, or a pair of up/down buttons next to the value. Ranges may use scalar or distance values and can have their maximum and minimum values set by the application.

GenTextClass

GenTextClass is tremendously versatile and can be used for text displays or text-edit fields. The GenText is used by nearly every application that either displays text or requires text input. It is so versatile and powerful that it can provide the power of an entire word processor without any additional code in the application.

The GenText object supports selection and control of fonts, point sizes, text color, paragraph color, paragraph borders, margin settings, tab stops, manual leading, and character kerning, as well as several other features. The text library also provides several controllers that work with the GenText to allow the user to set all these features.

Document and Document Control Classes

Together, GenDocumentClass , GenDocumentGroupClass , and GenDocumentControlClass provide all the functions necessary to create, save, open, and edit document files. These classes provide not only the UI menus and tools (the File menu) but also the functions for managing the document files. Applications that use these classes never have to call routines to open, close, or save files--all that is done automatically, including the file selector mechanisms to aid the open and save-as functions.

GenDocument objects are created and managed automatically by the GenDocumentGroup. Each document object represents a single file which has been opened or newly created by the user.

Display and Display Control Classes

Together, the GenDisplay, GenDisplayGroup, and GenDisplayControl provide display windows and the UI gadgetry to manage them. Typically, these objects will be used in conjunction with the document and document control objects to provide one display for each document.

The GenDisplayGroup object creates and manages multiple GenDisplay document windows. The GenDisplayControl object creates and maintains a Window menu to allow the user to operate on the individual displays. If your application will have multiple documents or multiple displays open, you will want to use these objects.

GenControlClass and UI Controllers

GenControlClass is used to create UI controller objects. Applications will most likely not use GenControlClass directly, though some object libraries might. For example, the Text Library uses controller objects for font control, point size control, and style control, among other things. Any application that uses the Text Library can include the font controller object; the user will then be able to select and apply fonts without the application having to do any work to support it.

The UI and various libraries provide many controllers you can use immediately. Some examples are the GenEditControlClass , which creates and maintains the Edit menu and tools; ColorSelectorClass , which creates and displays UI gadgetry to set color information; and GenViewControlClass , which creates and maintains a View menu allowing the user to set scaling and scrolling behavior.

GenToolControlClass

GenToolControlClass lets the user select which of an application's tools are available and where they should be placed (in a toolbox, in a menu, etc.). Tools are provided by UI controllers. Typically, an application that uses controllers will provide several tool areas and a GenToolControl; the GenToolControl will automatically create all the UI gadgetry to let the user select which tools are active and where they will appear.

GenFileSelectorClass

The GenFileSelector provides user interface to allow the user to navigate through his or her file system. It is used most often by the document control objects and is used directly by only some applications.

GenGlyphClass

GenGlyph displays simple text or graphics strings. Text displayed by a Glyph object is not selectable or editable; these objects are typically used for labeling areas or items on the screen.

GenContentClass

GenContentClass is used with a GenView to display other generic UI objects within a scrollable window. The GenContent is rarely used because having some of an application's UI objects not visible can confuse some users. More often, a GenDocument is used as the view's content; GenDocumentClass is subclassed from the GenContent.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using the Generic Classes | Next: 3.2 Creating a Generic Object Tree


Creating a Generic Object Tree

UI Overview: 3.2 Using the Generic Classes: Creating a Generic Object Tree

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 The Generic Class Tree | Next: 4 Using the Visible Classes

You don't have to understand all the generic object classes to create a complete generic object tree for your application. For insight into and an example of creating a generic tree (including the primary window, a menu, a dialog box, and a scrolling view window), see the Hello World chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 The Generic Class Tree | Next: 4 Using the Visible Classes


Using the Visible Classes

UI Overview: 4 Using the Visible Classes

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Using the Generic Classes | Next: 5 A UI Example

The visible classes in GEOS provide custom objects that can be used for any number of purposes. There are many visible object classes and they are so versatile that everything from spreadsheets to drawing programs to interactive games can be created from them.

Visible Objects and the GenView

The Visible Object Document

Visible Object Abilities

The Vis Class Tree

Creating a Visible Object Tree

Working with Visible Object Trees

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Using the Generic Classes | Next: 5 A UI Example


Visible Objects and the GenView

UI Overview: 4.1 Using the Visible Classes: Visible Objects and the GenView

| Up: GEOS SDK TechDocs | Up | Prev: 4 Using the Visible Classes | Next: 4.2 The Visible Object Document

Visible objects are designed for flexibility and for interacting with the user. Typically, visible objects will reside in an object tree, the root of which is a VisContent or GenDocument object connected to a GenView (this section assumes the VisContent, though the GenDocument is roughly equivalent for the purposes of overview). The relationship between these objects is simple, yet it accomplishes an immense amount of work for the application.

The GenView provides a window that may be scrollable, scalable, and resizable. The view is directly connected to the VisContent object and works very closely with it to handle input and drawing events. The VisContent acts as a manager for the visible object tree; it passes input events (mouse moves and mouse clicks) on to the proper visible object directly under the mouse pointer, and it makes sure all visible objects in the tree draw themselves at the appropriate time.

The view and VisContent may be connected tightly or very loosely for sizing purposes. For example, the view may be forced to resize itself to the proper size of the VisContent; or, the view might be scrollable and resizable independent of the content's size. The VisContent can choose whether mouse events are expected or required, and the view will notify the VisContent whenever the window has been invalidated and the document needs redrawing.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Using the Visible Classes | Next: 4.2 The Visible Object Document


The Visible Object Document

UI Overview: 4.2 Using the Visible Classes: The Visible Object Document

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Visible Objects and the GenView | Next: 4.3 Visible Object Abilities

Visible objects exist in an object tree and draw themselves in the GEOS graphic space. Every visible object knows where in the graphic coordinate space it sits and how big it is. The top object of the object tree, the VisContent, manages the entire tree. Composite objects (of class VisCompClass ) can be used as organizational objects that control other objects.

Using the VisContent, the VisComp, and the standard Vis objects, you can create just about any interactive document you want. (There are other available object libraries such as the spreadsheet and ruler libraries, and they are based on these three building blocks.)

Every visible object knows what its bounds are; that is, each object knows exactly how big it is and where it sits in the graphics document. The bounds are always rectangular in the base VisClass format, though a subclass of VisClass could easily be created to handle more complex shapes. Bounds are in the standard graphics coordinates (i.e. 16-bit numbers based on a 72 dpi grid). Vis objects may also exist in large documents (32-bit coordinates), but the objects must handle the majority of the extra coordinate manipulation on their own.

Visible objects do not inherently know how to draw themselves. However, the GenView, when resized or scrolled (for example), will send MSG_META_EXPOSED to the VisContent. The VisContent object will then send a MSG_VIS_DRAW message to itself and to all its visible object children, directing them to draw themselves in the proper place. For the visible object to draw itself, it must handle MSG_VIS_DRAW . In its handler, it can call any normal graphics commands to draw anything wherever it wants (not just within its bounds).

For examples of visible objects that know their bounds and that handle MSG_VIS_DRAW , see A UI Example .

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Visible Objects and the GenView | Next: 4.3 Visible Object Abilities


Visible Object Abilities

UI Overview: 4.3 Using the Visible Classes: Visible Object Abilities

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 The Visible Object Document | Next: 4.4 The Vis Class Tree

Visible objects can be used in innumerable situations; with a little work from the application developer, they provide dozens of useful features including

  • Knowledge of Their Bounds
    Every visible object knows exactly where in the application's document it sits and exactly how big it is. It can easily change its own bounds, either moving itself in the document space or resizing itself.
  • Ability to Draw Themselves
    Every visible object is responsible for drawing itself. When it receives a drawing message ( MSG_VIS_DRAW ), it must recognize the context of the message and draw itself appropriately. VisClass does not have a default handler for MSG_VIS_DRAW ; each subclass must handle this message itself.
  • Input Handling
    Pointer and click events as well as keyboard input can be automatically transmitted through the view to the VisContent if desired. Several combinations of event pass-throughs are available. When the VisContent receives a mouse event, it can intercept and handle it or pass it on to whatever object is under the pointer. If the VisContent does not handle the event (such as MSG_META_PTR ) explicitly, the message will automatically be passed on to the object under the pointer.
  • Geometry Management
    Both the view and the VisContent can interact to provide specific sizing behavior for the window and content. Additional geometry management is available to help a VisComp composite object manage its children.
  • Object Tree Management
    Messages may easily be passed to an object's parent or children. Also, objects may be added to or removed from the visible tree without difficulty.
  • Use of VisMonikers
    In addition to simply drawing itself, a visible object can have a visible moniker that it can draw as well. This simplifies drawing text or handling MSG_VIS_DRAW in that the moniker can be set beforehand and simply drawn with a single call.

Using visible objects allows an application a lot more flexibility in display and input handling than is available with the generic objects. It also requires more programming; however much of the most difficult work is made easy by the view and VisContent objects. The application does not have to worry about clipping or scrolling or even determining what type of input event is taking place. All this is somewhat automatic and can, for the most part, be ignored.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 The Visible Object Document | Next: 4.4 The Vis Class Tree


The Vis Class Tree

UI Overview: 4.4 Using the Visible Classes: The Vis Class Tree

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Visible Object Abilities | Next: 4.5 Creating a Visible Object Tree

There are four base visible classes on which the other object libraries are founded. VisClass is the most basic and at the root of the visible class tree. Under it are VisCompClass and VisTextClass . VisContentClass is a subclass of VisCompClass .

VisTextClass is special in that it is rarely subclassed as it already contains nearly all the functionality an application will need from a visible text object.

The other three classes are typically subclassed by applications. None of the three can draw itself; instead, the subclass must handle MSG_VIS_DRAW , the message that indicates the object must draw itself. The three different classes are used as follows:

  • VisClass
    VisClass     is the head of the visible class tree and therefore is the most broad-based in functionality. VisClass objects can not have children and therefore can only exist as the leaves of the object tree.
  • VisCompClass
    VisCompClass     provides a composite VisClass object. Essentially, this class is the same as VisClass except that it can have children. It also includes several special geometry options that allow it to manage and place its children as well as set their bounds.
  • VisContentClass
    VisContentClass     is used only as the content of a GenView. It is a subclass of VisCompClass and therefore can have children and manage them with the same geometry features available to VisComp. In addition, the VisContent interacts with the GenView to handle input and drawing. The content can interact with the view to determine sizing behavior as well as input behavior.
| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Visible Object Abilities | Next: 4.5 Creating a Visible Object Tree


Creating a Visible Object Tree

UI Overview: 4.5 Using the Visible Classes: Creating a Visible Object Tree

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 The Vis Class Tree | Next: 4.6 Working with Visible Object Trees

You can create a visible object tree either in your Goc source code or at run-time. As stated earlier, the visible tree is linked to the generic UI object tree through the GenView object. Without a view object, you will not be able to display your visible objects.

The visible object tree must have a VisContentClass object (or a subclass of VisContentClass ) as its root object. If another class is chosen for the topmost object, that class will have to handle all the messages that would normally be received by a content; otherwise, results are unpredictable and your application will likely not function the way you expect.

VisContent objects are rarely, if ever, used lower in the visible tree than as the top node. For levels further down in the tree, you can use VisCompClass (or, again, a subclass of VisCompClass ). VisComp allows the object to have children and does not incur the same amount of overhead associated with a VisContent. Any object in the visible tree that may have children must be a "composite" object, or a subclass of VisCompClass .

Leaf nodes of the visible tree can be direct subclasses of VisClass . Objects of VisClass can not have children, but they have all the attributes and instance data you need for a leaf node. You must subclass VisClass , however, if your object is ever to be shown on the screen: Since visible objects can have any application-defined visible representation, you must write the MSG_VIS_DRAW handler yourself; this can be done only in a subclass.

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 The Vis Class Tree | Next: 4.6 Working with Visible Object Trees


Working with Visible Object Trees

UI Overview: 4.6 Using the Visible Classes: Working with Visible Object Trees

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Creating a Visible Object Tree | Next: 5 A UI Example

Working with visible object trees is quite easy and provides immense flexibility to your applications. Entire groups of objects can be added to, removed from, or moved around in the display with a single command. New objects can be created during execution, and others can be destroyed at will.

Sending Messages in the Tree

Often, an object in the visible tree will need to contact its parent or its children. For example, a child that needs to know the state of its parent must be able to find and contact that parent. Likewise, a parent that must notify all its children to redraw themselves must be able to quickly send out the notification.

An object can send a message to its parent by simply specifying @visParent as the message recipient. That same object could also contact all its children by specifying the recipient as @visChildren . The message sent (only with @send since there are multiple destinations) will go to each of the object's children.

If you need more complicated message passing, you can build this into your Vis subclasses. It is not difficult for an object to retrieve the optr of any of its children or of its parent; it can then send the message appropriately. Each recipient can handle it in the same manner, forwarding it on to the next layer of the tree.

VisClass also employs several messages known as "visual upward queries," or "VUP" messages. These travel up the visible tree until they encounter an object of the proper class, where they will be handled.

Altering the Tree

VisClass and VisCompClass can handle messages that retrieve various information about the visible tree. They also have messages for altering the visible tree's structure.

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Creating a Visible Object Tree | Next: 5 A UI Example


A UI Example

UI Overview: 5 A UI Example

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Using the Visible Classes | Next: 5.1 What TicTac Illustrates

This section uses the sample application TicTac, a simple Tic Tac Toe board and pieces that can be moved around the board.

What TicTac Illustrates

What TicTac Does

The Structure of TicTac

TicTacBoard Specifics

TicTacPiece Specifics

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Using the Visible Classes | Next: 5.1 What TicTac Illustrates


What TicTac Illustrates

UI Overview: 5.1 A UI Example: What TicTac Illustrates

| Up: GEOS SDK TechDocs | Up | Prev: 5 A UI Example | Next: 5.2 What TicTac Does

The TicTac sample application can teach you several things about how the visible world works and about how to manage visible objects in an application. It is simple but illustrates the following concepts:

  • Drawing Visible Objects
    Every visible object must be able to draw itself. VisClass does not have any inherent code to make visible objects visible; instead, subclasses of VisClass must handle MSG_VIS_DRAW .
  • Changing an Object's Position
    The pieces of the TicTac game allow the user to move them around the screen using the mouse. This entails tracking the mouse and then changing the object's position in the document.
  • Sending Messages Up and Down the Visible Tree
    The pieces must interact with the VisContent object to ensure they stay on the game board, and the VisContent must notify the pieces when the "New Game" button has been pressed. Therefore, messages must be passed both up and down the visible tree.
  • Handling Input Events
    In order for the user to move the pieces around the board, the pieces must be able to receive mouse events. Several different types of mouse events are handled to show the pieces moving around the board and to handle input properly.
  • Drawing Background Graphics
    Because the game board does not change at all, its background is drawn by the content object. The content could change its drawing behavior if necessary, but background graphics are most easily drawn by the content.
  • Visible Tree Structure
    A simple, two-layer visible tree is used in the TicTac application: The VisContent is the root of the tree, and each of the pieces is a leaf.
| Up: GEOS SDK TechDocs | Up | Prev: 5 A UI Example | Next: 5.2 What TicTac Does


What TicTac Does

UI Overview: 5.2 A UI Example: What TicTac Does

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 What TicTac Illustrates | Next: 5.3 The Structure of TicTac

The TicTac sample application is extremely simple. It draws a Tic Tac Toe board and its outline, and it has ten pieces which the user can move. Five of these pieces are gray squares, and five are gray circles. Each of the pieces may be moved by clicking and dragging it with the mouse. Any piece may be moved anywhere on the board, but pieces may not be moved off the board.

A Game menu with a "New Game" trigger replaces all the pieces to their original locations. Because this is a simple example, no actual rules of any kind are enforced. Recognition of winning sequences and rules involving turn sequencing or playing against the computer are left as exercises for the reader.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 What TicTac Illustrates | Next: 5.3 The Structure of TicTac


The Structure of TicTac

UI Overview: 5.3 A UI Example: The Structure of TicTac

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 What TicTac Does | Next: 5.4 TicTacBoard Specifics

The TicTac sample application is coded in two files: The first, tictac.gp , is the geode parameters file. The other, tictac.goc , contains all the code for objects in the application. The geode parameters file is similar to other .gp files and is not discussed in this section.

The application uses two different object trees, one for its generic UI and one for the game board and game pieces. The first consists of generic UI objects and the second of objects subclassed off VisClass and VisContentClass . The two object trees are diagrammed and described below.

TicTac's Generic Tree

The TicTac application begins with two standard generic objects common to all applications: GenApplication and GenPrimary. These two objects are described in detail in other sections and are not covered here, though their definitions are shown in TicTacApp and TicTacPrimary .

The primary object, TicTacPrimary, has two children. One, TicTacGameMenu, implements the game menu; the other, TicTacView, provides the window through with the user can interact with the visible tree.

Code Display 6-1 TicTacApp and TicTacPrimary

@start AppResource;
    /* The AppResource resource block contains the TicTacApp object only. This
     * object is in its own resource for performance purposes. */
@object GenApplicationClass TicTacApp = {
    GI_visMoniker = list { TicTacTextMoniker }
    GI_comp = TicTacPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = TicTacPrimary;
}
@visMoniker TicTacTextMoniker = "TicTacToe";
@end AppResource
@start Interface;
    /* This is the Primary window of the application. It is not minimizable
     * (since no icon is defined for it). It has two children: The View
     * object and the Menu object. */
@object GenPrimaryClass TicTacPrimary = {
    GI_comp = TicTacView, TicTacGameMenu;
    ATTR_GEN_DISPLAY_NOT_MINIMIZABLE;
    HINT_SIZE_WINDOW_AS_DESIRED;
}

TicTacGameMenu

This object is a standard GenInteraction object set up as a menu. The code for the entire menu (including the TicTacNewTrigger) is shown in The TicTac Game Menu and is heavily commented as to how each attribute is used.

The New Game trigger may be invoked at any time except when a piece is being moved. When a piece is being moved, that piece object has the "mouse grab," meaning that it will receive all mouse and pointer events until it releases the grab. When an object has the mouse grab, no mouse events may be sent to another object, and therefore the menu object can not be clicked on while the piece has the grab.

When the user presses the New Game trigger, the trigger sends its message, MSG_TICTAC_NEW_GAME , to the TicTacBoard object. The TicTacBoard object is the top object in the visible tree and, upon receipt of this message, resets the game board and notifies all the piece objects of the reset. This process is described below in TicTacBoard Specifics .

Code Display 6-2 The TicTac Game Menu

/* The TicTacGameMenu object is the only menu of this application. Its only child
 * and only menu entry is the TicTacNewTrigger object. */
@start Interface;			/* In the same resource block as TicTacPrimary. */
@object GenInteractionClass TicTacGameMenu = {
    GI_visMoniker = "Game";					/* The name of the menu. */
    GI_comp = @TicTacNewTrigger;					/* The only menu item. */
    GII_visibility = GIV_POPUP;					/* This attribute indicates that this
                     * interaction is a menu rather than
                     * a dialog. */
}
@object GenTriggerClass TicTacNewTrigger = {
    GI_visMoniker = "New Game";					/* The name of the menu item. */
    GTI_destination = @TicTacBoard;					/* The object to receive the "New Game"
                     * message: the game board object. */
    GTI_actionMsg = MSG_TICTAC_NEW_GAME;					/* The message to be sent when the trigger
                     * is pressed. */
}
@end Interface			/* End of the Interface resource block */

TicTacView

The TicTacView object is a standard GenView set up to run the TicTacBoard object. The code for TicTacView is shown in The TicTacView Object and is heavily commented to show what each of the view's attributes is used for.

TicTacView's content object is TicTacBoard, the game board object. This means that any appropriate input events as well as all messages sent out by the view will be passed directly to the TicTacBoard object. The sizing attributes and the fact that the view is not marked GVDA_SCROLLABLE in either dimension makes sure the view sizes exactly to the game board's bounds.

A GenView object is necessary in every case where a visible object tree is used. The view not only displays the visible tree but also handles all clipping, scaling, scrolling, and sizing if any is desired (none is used in TicTac). It also takes and passes on any appropriate mouse or keyboard input events. Additionally, it interacts directly with its content object (in this case TicTacBoard) to determine proper geometry and sizing behavior of the content and the view.

This view also provides the background color of the game board, dark blue.

Code Display 6-3 The TicTacView Object

    /* This object provides the window through which the user interacts with the
     * visible object tree. This object communicates with the game board object (a
     * subclass of VisContentClass) to coordinate drawing, clipping, sizing, and
     * even input handling. */
@start Interface;			/* In the same resource block as TicTacPrimary. */
@object GenViewClass TicTacView = {
    GVI_content = @TicTacBoard;				/* The content object of this view is the
                 * TicTacBoard object, the root object of the
                 * visible object tree. */
    GVI_color = { C_BLUE, 0, 0, 0 };					/* The background color of this view
                     * should be dark blue. */
    /* The horizontal attributes of this view set it to the same
     * size as the game board, and the view is not scrollable. */
    GVI_horizAttrs = @default				| GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;
    /* The vertical attributes of this view set it to the same size
     * as the game board, and the view is not scrollable. */

    GVI_vertAttrs = @default			|	 GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT
                | GVDA_KEEP_ASPECT_RATIO;
    /* The user won't need to type anything, so there's no need for
     * a floating keyboard. */
    ATTR_GEN_VIEW_DOES_NOT_ACCEPT_TEXT_INPUT;
}
@end Interface			/* End of the Interface resource block */

TicTac's Visible Tree

The visible tree contains eleven objects. One acts as TicTacView's content and is of TicTacBoardClass , a subclass of VisContentClass . The other ten are all game pieces of class TicTacPieceClass , a subclass of VisClass . Both the class definitions and the object definitions are given in TicTacBoardClass and TicTacPieceClass .

All eleven of the visible objects remain on the screen and in the view during their entire existence. The game board and all its pieces are shown in the figure above; this illustration represents the basic configuration of the game board when the application first starts or when the user presses the "New Game" trigger in the "Game" menu.

The TicTacBoard object draws the border around the board and makes sure the view window sizes itself to the same size as the board. The board is 180 points (2.5 inches) in height and 270 points (3.75 inches) in width; these numbers are stored as the constants BOARD_HEIGHT and BOARD_WIDTH. TicTacBoard also draws the playing field--this consists of the four white lines on the left side of the game board. TicTacBoard's other main function is to ensure that all the children (game pieces) behave properly; it makes sure the child's bounds are on the game board when the piece is moved, and it notifies the game pieces when they must draw themselves due to a view exposure. Finally, TicTacBoard receives the "New Game" message from the Game menu; it then redraws the game board and notifies each of the game pieces that they should return to their initial locations.

Each of the game piece objects knows about its location and status. Each piece knows its initial location, current location, and proposed location (during a move). Every game piece is an instance of TicTacPieceClass . This class is shown in TicTacBoardClass and TicTacPieceClass ; it contains a number of instance data fields for these locations. It also has an instance data field indicating what type of piece (i.e. "box" or "ring") the object is.

The "box" objects are designated by having the value TTPT_BOX in their TTP_pieceType fields; the "ring" objects have TTPT_RING in that field. Both types of objects act and react in the same way to various events; the only difference is in their shape and color.

Code Display 6-4 TicTacBoardClass and TicTacPieceClass

/* The TicTacPieceTypes enumerated type lists the different types of game pieces a
 * particular piece object can be. In this game, a piece is either a "box" (gray
 * square) or a "ring" (light gray circle).									*/
typedef ByteEnum TicTacPieceTypes;
#define TTPT_BOX 0
#define TTPT_RING 1
/***********************************************************************
 * TicTacBoardClass
 * This class is a subclass of VisContentClass and provides the game board
 * for this application. It also manages all the children (piece objects).
 * Because it is a subclass of VisContentClass, it inherits all the instance
 * data fields and messages of that class.
 ***********************************************************************/
@class TicTacBoardClass, VisContentClass;					/* this class is a subclass
                     * of VisContentClass */
    /* Message definitions for this class */
    @message void MSG_TICTAC_NEW_GAME();
    /* This message is sent by the New Game trigger in the Game menu
     * when the user wants to reset the game. It is sent directly to
     * the game board object and causes the board object first to
     * send the "new game" message to each of its children and then
     * to redraw the game board.							*/
    @message Boolean MSG_TICTAC_VALIDATE_BOUNDS(word bottom, word right,
                        word top, word left);
    /* This message is sent by a game piece that is being moved by the
     * user and is about to be set down. The four parameters are the
     * proposed new bounds of the moved piece; if they are within the
     * game board's limit, this message returns TRUE. If they are at
     * all outside the game board, this message returns FALSE.	*/
@endc
/* Declare the class in memory so the method table will be built. */
@classdecl TicTacBoardClass;
/***********************************************************************
 * TicTacPieceClass
 * This class is a subclass of VisClass and provides all the functions
 * necessary for a game piece in this game. Because it is a subclass of
 * VisClass, it inherits all the instance data fields and messages of
 * that class.
 ***********************************************************************/
@class TicTacPieceClass, VisClass;					/* this class is a subclass
                     * of VisClass */
    /* The instance data fields of this class: */
    @instance TicTacPieceTypes TTP_pieceType;
    /* TTP_pieceType defines whether the object of this class is
     * a "box" or a "ring."							*/
    @instance int TTP_vertPos;
    /* TTP_vertPos indicates the current y position of
     * the piece. This does not indicate the piece's actual
     * bounds but rather where its moving outline appears.							*/
    @instance int TTP_horizPos;
    /* TTP_horizPos indicates the current x position of
     * the piece. This does not indicate the piece's actual
     * bounds but rather where its moving outline appears.							*/
    @instance int TTP_origVertPos;
    /* TTP_origVertPos indicates the y position where this
     * piece should return when the New Game trigger is pushed
     * and the piece goes back to its original location.							*/
    @instance int TTP_origHorizPos;
    /* TTP_origHorizPos indicates the x position where this
     * piece should return when the New Game trigger is pushed
     * and the piece goes back to its original location.							*/
    @instance Boolean TTP_dragging;
    /* A flag indicating whether the user is in the process of dragging
     * the game piece around the board. */
    /* Message definitions unique to this class. */
    @message void MSG_PIECE_NEW_GAME();
    /* This message notifies the piece object that the user has pushed *
     * the New Game trigger and that the piece should return to its *
     * original position on the board (the TTP_orig(Horiz/Vert)Pos *
     * fields).							*/
@endc
/* Declare the class in memory so the method table will be built. */
@classdecl TicTacPieceClass;
| Up: GEOS SDK TechDocs | Up | Prev: 5.2 What TicTac Does | Next: 5.4 TicTacBoard Specifics


TicTacBoard Specifics

UI Overview: 5.4 A UI Example: TicTacBoard Specifics

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 The Structure of TicTac | Next: 5.5 TicTacPiece Specifics

The TicTacBoard object, being of a subclass of VisContentClass , handles many messages specific to content objects. However, only three messages are handled specifically by TicTacBoardClass . These messages are

MSG_VIS_DRAW
This message notifies the object it must draw itself and any accompanying graphics. TicTacBoard responds by drawing the game board (the border and crossed lines) and by passing the MSG_VIS_DRAW on to all of its children.
MSG_TICTAC_NEW_GAME
This message is sent by the New Game trigger in the Game menu. TicTacBoard responds by sending another new game message, MSG_PIECE_NEW_GAME , to each of its children. Each child will position itself and draw itself properly; TicTacBoard will then redraw the game board by sending itself a MSG_VIS_DRAW .
MSG_TICTAC_VALIDATE_BOUNDS
This message is sent by a game piece object when it is being moved. TicTacBoard checks the parameters passed and determines whether they are on the game board or not; if they are it returns TRUE, and if they aren't it returns FALSE.

Each of the methods for the above messages is shown in Methods of TicTacBoardClass . Each is heavily commented and explains the theory behind the method.

Code Display 6-5 Methods of TicTacBoardClass

/***********************************************************************
 *
 * MESSAGE:		MSG_TICTAC_NEW_GAME for TicTacBoardClass
 *
 * DESCRIPTION:		This method notifies each of the visible children that
 * 		a new game is beginning; they should take their places,
 * 		and then the board object will redraw itself
 *
 * PARAMETERS:
 * 	void ()
 *
 ***********************************************************************/
@method TicTacBoardClass, MSG_TICTAC_NEW_GAME {
    WindowHandle win;				/* the window to draw to			*/
    GStateHandle gstate;				/* the gstate of the window */
    /* First notify all the children (game pieces)
     * that a new game is beginning.						*/
    @send @visChildren::MSG_PIECE_NEW_GAME();
    /* Now initiate a new gstate for the view window.
     * Get the window handle from the view, and then
     * create a new gstate for it.						*/
    win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW();
    gstate = GrCreateState(win);
    /* Invalidate the game board rectangle in the document.
     * This will cause the view object to generate a
     * MSG_META_EXPOSED for the rectangle, causing MSG_VIS_DRAW
     * to be sent to this object (TicTacBoard).						*/
    GrInvalRect(gstate, 0, 0, BOARD_WIDTH, BOARD_HEIGHT);
    /* Now destroy the temporary gstate. This is important
     * to keep too many gstate handles from being locked
     * and slowing down the system.						*/
    GrDestroyState(gstate);
}
/***********************************************************************
 *
 * MESSAGE:		MSG_VIS_DRAW for TicTacBoardClass
 *
 * DESCRIPTION:		This method draws the board's outline and the
 *		lines of the playing field. It is sent each time
 *		a portion of the view window becomes invalid (such
 *		as when the primary is moved).
 * PARAMETERS:
 *	void (word drawFlags, GStateHandle gstate)
 *		gstate is the handle of the graphics state associated
 *		with the exposed portion of the view window
 ***********************************************************************/
@method TicTacBoardClass, MSG_VIS_DRAW {
    /* Set up the graphic state properly. The board
     * lines are to be white and three points thick.						*/
    GrSetLineColor(gstate, CF_INDEX, C_WHITE, 0, 0);
    GrSetLineWidth(gstate, 3);
    /* Now draw the border of the game board. It is a
     * rectangle that outlines the entire board.						*/
    GrDrawRect(gstate, 0, 0, BOARD_WIDTH, BOARD_HEIGHT);
    /* Set and draw the Tic Tac Toe playing field. The
     * lines are now set to 4 points thickness, and the
     * lines are drawn with HLine and VLine graphics
     * commands. Ideally, preset constants would be used.*/
    GrSetLineWidth(gstate, 4);
    GrDrawHLine(gstate, 5, 60, 175);
    GrDrawHLine(gstate, 5, 120, 175);
    GrDrawVLine(gstate, 60, 5, 175);
    GrDrawVLine(gstate, 120, 5, 175);
    /* When the MSG_VIS_DRAW is received by the game board,
     * it must pass it on to its visible children. It must
     * also pass on the parameters of the message as passed
     * to ensure all drawing is done properly.						*/
    @send @visChildren::MSG_VIS_DRAW(drawFlags, gstate);
}

/***********************************************************************
 *
 * MESSAGE:		MSG_TICTAC_VALIDATE_BOUNDS for TicTacBoardClass
 *
 * DESCRIPTION:		This method checks to see if the bounds passed
 *		are on the game board. This is invoked when a game
 *		piece is in motion and receives an END_SELECT message
 *		indicating it is being put down. The piece must
 *		determine whether the suggested bounds are on the
 *		game board; the piece should always query the board
 *		object rather than check directly; if the board were
 *		resizable, the piece could be incorrect sometimes.
 *
 * STRATEGY:		Check the four bounds against the board's edges. If
 *		all four are on the board, return TRUE. If any one
 *		of the four is off the board, return FALSE.
 * PARAMETERS:
 *	void (word bottom, word right, word top, word left)
 ***********************************************************************/
@method TicTacBoardClass, MSG_TICTAC_VALIDATE_BOUNDS {
    if (((bottom < BOARD_HEIGHT) && (top > 0))
            && ((right < BOARD_WIDTH) && (left >= 0))) {
    return(TRUE);
    } else {
    return(FALSE);
    }
}
| Up: GEOS SDK TechDocs | Up | Prev: 5.3 The Structure of TicTac | Next: 5.5 TicTacPiece Specifics


TicTacPiece Specifics

UI Overview: 5.5 A UI Example: TicTacPiece Specifics

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 TicTacBoard Specifics

TicTacPieceClass contains most of the game's functionality. Since the user interacts directly with each game piece, the piece must know not only how to draw itself but also how to react to user input.

MSG_PIECE_NEW_GAME
This is the only message generated by the game itself that a game piece receives; it is sent by the TicTacBoard object when the user has pressed the New Game trigger. The game piece object responds by resetting its bounds to the original settings. It does not have to redraw or invalidate its old bounds because the TicTacBoard object will send a MSG_VIS_DRAW later and will invalidate the entire board.
MSG_VIS_DRAW
This message notifies the object that it must draw itself and any accompanying graphics. The game piece responds by drawing the proper shape in the proper color in the proper place. Since every VisClass object inherently knows its location and bounds, the object already knows where and how big the shape should appear. Whether a gray box or circle is drawn depends on the TTP_pieceType instance data field.
MSG_META_START_SELECT
This message is sent by the system when the user clicks a mouse button. The UI sends the message to whatever object lies under the pointer. The UI objects then pass the message down the object tree until it gets handled. The progression sends the message to TicTacApp, which passes it to TicTacPrimary, which passes it to TicTacView, which passes it to TicTacBoard, which (by letting the default VisContentClass method handle it) passes it to the proper game piece object (if any) under the pointer. The game piece responds by grabbing the mouse and all subsequent pointer events.
MSG_META_DRAG_SELECT
This message, like MSG_META_START_SELECT , indicates that the user has clicked a mouse button and has initiated a drag event. (Normally, this is used to select ranges or groups of objects; in TicTac, however, it is treated like MSG_META_START_SELECT .)
MSG_META_DRAG
This message is sent after the user has clicked the mouse button and is now moving the mouse pointer (and has not released the button yet).
MSG_META_PTR
This message is sent when the pointer image is over the bounds of the game piece whether or not a mouse button has been pressed. (After the object has grabbed the mouse events by handling MSG_META_START_SELECT , the pointer event is sent whenever the mouse pointer is moved.) The piece determines whether or not it is being dragged around the screen. This is known as a "drag event," and the game piece responds by drawing a piece-shaped outline around the mouse pointer. This outline will follow the pointer around the screen until the user releases the mouse button (causing a MSG_META_END_SELECT , below). The outline is first drawn in either the MSG_META_START_SELECT or MSG_META_DRAG_SELECT handler (whichever is called to start the drag event). MSG_META_PTR and MSG_META_END_SELECT erase the outline before drawing a new one. The game piece will maintain three locations in its instance data: Its VI_bounds field maintains its position when selected. Its TTP_orig(horiz/vert)Pos fields maintain its original position when the game was first started. Its TTP_(horiz/vert)Pos fields maintain the current position of the outline and where the object would relocate to if the move was ended now. If the event is not a drag, the object will not react because it is assumed that no mouse button has been pressed and therefore the user is taking no action.
MSG_META_END_SELECT
This message is sent when the user releases a pressed mouse button, ending the select-and-drag process. The game piece reacts by checking if the location of the pointer is a legal position on the game board. (It does this by sending a verification message to the TicTacBoard object to make sure the proposed new bounds are on the game board.) If the position is legal, the game piece moves itself there, erasing any leftover outlines (from the drag sequence) and its original image on the board. It then causes itself to draw in the new location by sending itself a MSG_VIS_DRAW . If the new location is not legally on the game board, then the object will reset all its instance data and erase any leftover outlines, causing it to revert to its location before the select-and-drag sequence began.

Each of the methods for the above messages is shown in Methods for TicTacPieceClass .

Code Display 6-6 Methods for TicTacPieceClass

/***********************************************************************
 *
 * MESSAGE:		 MSG_PIECE_NEW_GAME for TicTacPieceClass
 *
 * DESCRIPTION:		This message causes the piece to replace itself
 *		to its original position. It is invoked when the
 *		user presses the New Game trigger; the trigger sends
 * 		MSG_TICTAC_NEW_GAME to the TicTacBoard object, and
 * 		the board object sends this message to each of
 * 		the game piece objects.
 *
 * PARAMETERS:
 * 	void ()
 ***********************************************************************/
@method TicTacPieceClass, MSG_PIECE_NEW_GAME {
    /* Set the current (motion) positions to the original positions. */
    pself->TTP_vertPos = pself->TTP_origVertPos;
    pself->TTP_horizPos = pself->TTP_origHorizPos;
    /* Send a MSG_VIS_BOUNDS_CHANGED to ourselves to make
     * sure the old bounds get redrawn. This message will
     * cause an invalidation of the document where the old
     * (passed) bounds were, causing that portion of the
     * window to be redrawn.						*/
    @call self::MSG_VIS_BOUNDS_CHANGED(pself->VI_bounds.R_bottom,
            pself->VI_bounds.R_right, pself->VI_bounds.R_top,
            pself->VI_bounds.R_left);
    /* Set the bounds of the object (VI_bounds) back to
     * their original values. The Rectangle structure
     * contains four fields, each of which must be set.						*/
    pself->VI_bounds.R_left = pself->TTP_origHorizPos;
    pself->VI_bounds.R_top = pself->TTP_origVertPos;
    pself->VI_bounds.R_right = (pself->TTP_origHorizPos + PIECE_WIDTH);
    pself->VI_bounds.R_bottom = (pself->TTP_origVertPos + PIECE_HEIGHT);
    /* This method does not need to invoke a MSG_VIS_DRAW
     * because the TicTacBoard object will do that. The
     * piece object will later receive a MSG_VIS_DRAW that
     * will cause the piece to be redrawn back at its
     * original location (the newly set bounds).						*/
}
/***********************************************************************
 *
 * MESSAGE:		MSG_VIS_DRAW for TicTacPieceClass
 *
 * DESCRIPTION:		Draw the piece at the current location. If the piece
 *		is a "box," draw a gray square. If the piece is a
 *		"ring," draw a gray circle. This message is received
 *		whenever a portion of the view window becomes invalid;
 *		TicTacView will send a MSG_META_EXPOSED to TicTacBoard,
 *		which will send itself (by default) a MSG_VIS_DRAW.
 *		The MSG_VIS_DRAW will be handled and then will be
 *		passed on to each of the game pieces. Then each piece
 *		(in this handler) will draw itself at its own bounds.
 *
 * PARAMETERS:
 *	void (word drawFlags GStateHandle gstate)
 *
 ***********************************************************************/
@method TicTacPieceClass, MSG_VIS_DRAW {
    /* Set the mode to MM_COPY; this means that the image
     * drawn now will be drawn over whatever is there now.*/
    GrSetMixMode(gstate, MM_COPY);
    /* If the type is TTPT_BOX, set the color to gray and
     * draw a rectangle the size of the object's bounds.
     * Otherwise (since there are just two types), set the
     * color to gray and draw an ellipse of that size.						*/
    if (pself->TTP_pieceType == TTPT_BOX) {
    GrSetAreaColor(gstate, CF_INDEX, C_DARK_GRAY, 0, 0);
    GrFillRect(gstate, pself->VI_bounds.R_left, pself->VI_bounds.R_top,
            pself->VI_bounds.R_right, pself->VI_bounds.R_bottom);
    } else {
    GrSetAreaColor(gstate, CF_INDEX, C_LIGHT_GRAY, 0, 0);
    GrFillEllipse(gstate, pself->VI_bounds.R_left, pself->VI_bounds.R_top,
            pself->VI_bounds.R_right, pself->VI_bounds.R_bottom);
    }
    /* After handling the message, call the superclass to
     * ensure that no default behavior has been mucked up.
     * This is actually not necessary in this particular case. */
    @callsuper();
}
/***********************************************************************
 * MESSAGE:		MSG_META_START_SELECT for TicTacPieceClass
 *
 * DESCRIPTION:		Grabs the mouse and calls for future pointer events.
 *		When the user clicks in the view, TicTacView will pass
 *		the click event to TicTacBoard. Since TicTacBoardClass
 *		does not intercept the event, VisContentClass passes
 *		it on to its child object currently under the pointer.
 *
 *		When the piece object receives this message, it means
 *		it has been clicked on by the user and the mouse button
 *		is still down. The piece must grab the mouse so that it
 *		gets all future mouse events, and it must request that
 *		all future mouse events be sent to it. This ensures
 *		that if the pointer leaves the object's bounds while
 *		the button is still pressed, the piece object will still
 *		receive all the pointer events (otherwise they would be
 *		sent to whatever object was under the new pointer
 *		position).
 * PARAMETERS:
 *	void (MouseReturnParams *retVal, word xPosition,
 *			word yPosition, word inputState)
 ***********************************************************************/
@method TicTacPieceClass, MSG_META_START_SELECT {
    /* First grab the gadget exclusive so we're allowed to
     * grab the mouse. Then grab the mouse, so all future
     * pointer events get passed directly to the game piece. */
    @call @visParent::MSG_VIS_TAKE_GADGET_EXCL(oself);
    @call self::MSG_VIS_GRAB_MOUSE();						/* grab mouse */
    /* Finally, return that this particular click
     * event has been processed. If this flag is
     * not returned, the system will send out the
     * click event again.					*/
    retVal->flags = MRF_PROCESSED;						/* this event processed */
}
/***********************************************************************
 *
 * MESSAGE:		MSG_META_DRAG_SELECT for TicTacPieceClass
 *
 * DESCRIPTION:		This message is sent to the piece object when the
 *		select button has been pressed and the mouse has been
 *		moved, resulting in a "drag-select" event.
 *		For event processing from the View, see the header
 *		for MSG_META_START_SELECT.
 *
 * PARAMETERS:
 *	void (MouseReturnParams *retVal, word xPosition,
 *			word yPosition, word inputState)
 ***********************************************************************/
@method TicTacPieceClass, MSG_META_DRAG_SELECT {
    GStateHandle gstate;					/* temporary gstate to draw to */
    WindowHandle win;					/* window handle of view window */
    /* Start off by setting the flag indicating that
     * the piece is being dragged around the screen. */
    pself->TTP_dragging = TRUE;
    /* Next, get the window handle of the view window.
     * Then, create a new, temporary gstate to draw into
     * for that window.						*/
    win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW();
    gstate = GrCreateState(win);
    /* Now, set the current position of the game piece
     * to be centered on the pointer.						*/
    pself->TTP_vertPos = yPosition - (PIECE_HEIGHT/2);
    pself->TTP_horizPos = xPosition - (PIECE_WIDTH/2);
    /* Now, set the drawing mode of the game piece
     * to MM_INVERT to draw a new game piece outline.
     * MM_INVERT is chosen so the outline can be redrawn
     * in invert mode later to erase it and not destroy
     * anything under it.					*/
    GrSetMixMode(gstate, MM_INVERT);
    /* Now draw the outline. If the game piece is of type
     * TTPT_BOX, draw a rectangle outline. Otherwise, draw
     * an ellipse outline.						*/
    if (pself->TTP_pieceType == TTPT_BOX) {
    GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                (pself->TTP_horizPos + PIECE_WIDTH),
                (pself->TTP_vertPos + PIECE_HEIGHT));
    } else {
    GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                (pself->TTP_horizPos + PIECE_WIDTH),
                (pself->TTP_vertPos + PIECE_HEIGHT));
    }
    /* Next, destroy the temporary gstate. This is important
     * to make sure the gstate does not stay in memory and
     * begin to slow down the system as more and more
     * temporary gstates are created but not destroyed.						*/
    GrDestroyState(gstate);
    /* Finally, return that this event has been processed
     * by this method.						*/
    retVal->flags = MRF_PROCESSED;
}
/***********************************************************************
 *
 * MESSAGE:		MSG_META_PTR for TicTacPieceClass
 *
 * DESCRIPTION:		This message is received whenever the pointer passes
 *		over this game piece object's bounds (and another
 *		game piece is not sitting directly on top of it).
 *		See MSG_META_START_SELECT for a description of how the event
 *		gets passed from TicTacView to this object.
 *
 *		This message can be either a drag event or a simple
 *		pointer event. If the latter, we want to do nothing
 *		because no mouse button is pressed. If the latter,
 *		we want to execute the same function as MSG_META_DRAG.
 *
 * PARAMETERS:
 *	void (MouseReturnParams *retVal, word xPosition,
 *			word yPosition, word inputState)
 *
 ***********************************************************************/
@method TicTacPieceClass, MSG_META_PTR {
    GStateHandle gstate;					/* temporary gstate to draw to */
    WindowHandle win;					/* window handle of view window */
    /* First check if this is a drag event. If not, do
     * nothing. If so, then draw a new outline and erase
     * the old outline.					*/
    if (pself->TTP_dragging) {
    /* Get the view's window handle and create a
     * temporary gstate for drawing into.					*/
    win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW();
    gstate = GrCreateState(win);
    /* Set the drawing mode of the game piece to
     * MM_INVERT for outline drawing.					*/
    GrSetMixMode(gstate, MM_INVERT);
    /* Erase the old outline by drawing an inverse
     * outline at the old bounds.					*/
    if (pself->TTP_pieceType == TTPT_BOX) {
        GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                    (pself->TTP_horizPos + PIECE_WIDTH),
                    (pself->TTP_vertPos + PIECE_HEIGHT));
    } else {
        GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                    (pself->TTP_horizPos + PIECE_WIDTH),
                    (pself->TTP_vertPos + PIECE_HEIGHT));
    }
    /* Now set the current motion position to be
     * centered on the pointer.					*/
    pself->TTP_vertPos = yPosition - (PIECE_HEIGHT/2);
    pself->TTP_horizPos = xPosition - (PIECE_WIDTH/2);
    /* Draw the new outline at the current position.*/
    if (pself->TTP_pieceType == TTPT_BOX) {
        GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                    (pself->TTP_horizPos + PIECE_WIDTH),
                    (pself->TTP_vertPos + PIECE_HEIGHT));
    } else {
        GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                    (pself->TTP_horizPos + PIECE_WIDTH),
                    (pself->TTP_vertPos + PIECE_HEIGHT));
    }
    /* Destroy the temporary gstate and return that
     * this event has been processed.						*/
    GrDestroyState(gstate);
    }
    retVal->flags = MRF_PROCESSED;
}
/***********************************************************************
 *
 * MESSAGE:		MSG_META_END_SELECT for TicTacPieceClass
 *
 * DESCRIPTION:		This message is received when the selection button has
 *		been released and this game piece had the mouse grab.
 *		All it does is release the gadget exclusive, which will
 *		cause us to end any dragging in progress and release
 *		the mouse.
 *		When we release the gadget exclusive, the UI will then
 *		sent MSG_VIS_LOST_GADGET_EXCL to this piece, which will
 *		tell us to erase the outline and draw the game piece.
 * PARAMETERS:
 *	void (MouseReturnParams *retVal, word xPosition,
 * 			word yPosition, word inputState);
 ***********************************************************************/
@method TicTacPieceClass, MSG_META_END_SELECT {
    /* Release the gadget exclusive, then return that the
     * event has been processed. */
    @call @visParent::MSG_VIS_RELEASE_GADGET_EXCL(oself);
    retVal->flags = MRF_PROCESSED; /* this event processed */
}
/***********************************************************************
 *
 * MESSAGE:		MSG_VIS_LOST_GADGET_EXCL for TicTacPieceClass
 *
 * DESCRIPTION:		This message is received when the piece lots go of the
 *		gadget exclusive (see MSG_META_END_SELECT, above).
 *		It first checks to see if the new, proposed bounds are
 *		on the game board. If the bounds are valid, then
 *		it sets the objects VI_bounds field to the new values
 *		and causes the object to erase its original drawing
 *		and draw itself at its new bounds. If the bounds are
 *		not on the game board, it will retain the original bounds
 *		and redraw using them.
 *
 * PARAMETERS:
 *	void ()
 *
 ***********************************************************************/
@method TicTacPieceClass, MSG_VIS_LOST_GADGET_EXCL {
    WindowHandle win;					/* window handle of view window */
    GStateHandle gstate;					/* temporary gstate to draw to */
    /* First check if the piece was being dragged.
     * If not, we don't have to do anything.					*/
    if (pself->TTP_dragging) {
    /* Get the window handle of the view window and
     * create a temporary gstate for it to draw to.						*/
    win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW();
    gstate = GrCreateState(win);
    /* Set the mode for drawing the outline.						*/
    GrSetMixMode(gstate, MM_INVERT);
    /* If the game piece type is TTPT_BOX, draw a rectangle
     * outline. Otherwise draw an ellipse outline.						*/
    if (pself->TTP_pieceType == TTPT_BOX) {
        GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                ((pself->TTP_horizPos) + PIECE_WIDTH),
                ((pself->TTP_vertPos) + PIECE_HEIGHT));
    } else {
        GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                ((pself->TTP_horizPos) + PIECE_WIDTH),
                ((pself->TTP_vertPos) + PIECE_HEIGHT));
    }
    /* Check to see if the new bounds are on the game
     * board. If they are, set the object's bounds to the
     * new values. If they are not, retain the original
     * values and set the values to those last stored.						*/
    if (@call TicTacBoard::MSG_TICTAC_VALIDATE_BOUNDS(
                    ((pself->TTP_vertPos) + PIECE_HEIGHT),
                    ((pself->TTP_horizPos) + PIECE_WIDTH),
                    pself->TTP_vertPos,
                    pself->TTP_horizPos)) {
    /* Invalidate the original drawing of the game piece.
     * Send the VI_bounds rectangle as the parameters
     * because they have not been changed since the
     * START_SELECT. This message is the equivalent of
     * calling GrInvalRect() with the same bounds.						*/
        @call self::MSG_VIS_BOUNDS_CHANGED(pself->VI_bounds.R_bottom,
                    pself->VI_bounds.R_right,
                    pself->VI_bounds.R_top,
                    pself->VI_bounds.R_left);
    /* Now set the current position to be centered
     * on the pointer image.					*/
        pself->TTP_vertPos = yPosition - (PIECE_HEIGHT/2);
        pself->TTP_horizPos = xPosition - (PIECE_WIDTH/2);
    /* Set the game piece object's bounds to
     * the new coordinates.					*/
        pself->VI_bounds.R_left = pself->TTP_horizPos;
        pself->VI_bounds.R_right = (pself->TTP_horizPos) + PIECE_WIDTH;
        pself->VI_bounds.R_top = pself->TTP_vertPos;
        pself->VI_bounds.R_bottom = (pself->TTP_vertPos) + PIECE_HEIGHT;
    } else {
    /* If the bounds are not on the game board, then reset
     * the current positions to be the original bounds. */
        pself->TTP_horizPos = pself->VI_bounds.R_left;
        pself->TTP_vertPos = pself->VI_bounds.R_top;
    }
    /* Now, the game piece must draw itself at its newly-
     * set bounds (will draw itself over its original
     * picture if the new bounds were invalid).						*/
    @call self::MSG_VIS_DRAW(0, gstate);
    /* Destroy the temporary gstate used for drawing. */
    GrDestroyState(gstate);
    /* Finally, clear the dragging flag to indicate that
     * no drag event is in progress. */
    pself->TTP_dragging = FALSE;
    }
    /* Release the mouse grab now that the move has
     * finished. Other objects in the view (other game
     * pieces, for example) may now receive pointer,
     * select, and drag events.						*/
    @call self::MSG_VIS_RELEASE_MOUSE();
}
| Up: GEOS SDK TechDocs | Up | Prev: 5.4 TicTacBoard Specifics


GenValue

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenValue Features
| One-page Version of this Chapter for Easy Printing

GenValueClass allows the user to retrieve or set a numerical value for use in your application. The GenValue object offers a display for the current value and the means to either increase or decrease this value. The GenValue object also manages the range that this value may fall between and the action to send out when a value has been selected.

Before reading this chapter, you should be familiar with GenClass

1 GenValue Features
2 GenValue Instance Data
3 GenValue Basics
3.1 The Value
3.2 The Minimum and Maximum
3.3 The Increment
3.4 GenValue States
3.5 Display Formats
3.6 Sending an Action
4 Supplemental Usage
4.1 Adjusting the Value Indirectly
4.2 Status Messages
4.3 Retrieving Text
4.4 Using Value Ratios
4.5 Text Filters for the GenValue
4.6 Using Ranges in GenValues

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenValue Features
| One-page Version of this Chapter for Easy Printing


GenValue Features

GenValue: 1 GenValue Features

| Up: GEOS SDK TechDocs | Up | Prev: GenValue | Next: 2 GenValue Instance Data

Depending upon the specific UI, the GenValue object can look like one of many things. It may be a spin gadget, a slider, a dial, or some other gadget. A GenValue typically allows the user to set a value within a specified range through keyboard or mouse input. For example, many GenValue objects in OSF/Motif are comprised of a text field showing the current value, an incrementor and a decrementor, and possibly a moniker. (Scroll bars are also GenValues, though they have very different appearance.)

In OSF/Motif, the user can enter the value by keyboard input into the text field or by clicking the mouse on the incrementor or decrementor.

The GenValue provides the following capabilities:

  • A user interface object to enter values into your application.
  • Application defined minimum and maximum values.
  • The increment value to increase or decrease values by.
  • Ability for the value to be represented in a variety of forms, including integer, decimal, or a unit of distance.
  • An action to apply any value to.

The user may change a value in a GenValue in one of two ways. It can be entered with the keyboard by typing directly into the display area, or it can be entered graphically by using the arrows, dials, or sliders that the specific UI provides. To enter a value by typing directly into the text field, first the user clicks on the area, bringing up a cursor. Then a value may be typed into the GenValue. To enter a value graphically, the user clicks on either the incrementor or decrementor, thus changing the value of the GenValue.

Once the value is changed, the value may become set immediately (if the object is operating in immediate mode), and the GenValue will perform its action (if any) every time the value is changed. As an alternative, the GenValue may be operating in delayed mode, and the value will not be set until a MSG_GEN_APPLY is received through an "Apply" or "OK" button. The mode (delayed or immediate) of the GenValue is controlled by the parent. Usually a GenValue will operate in immediate mode unless it is placed as a child of a GIT_PROPERTIES GenInteraction.

GenValue only allows the user to set a value within a specific range; this range consists of a minimum and maximum value that the GenValue may display. These values are set by the application and cannot be directly modified by the user, but they can be changed by your application. If the user enters a value above the maximum, the GenValue simply takes on its maximum possible value. If the user enters a value below the minimum, the GenValue takes on its least possible value.

In addition to the limits upon the range of values entered by the user, the GenValue also has a defined increment value. The increment value is the specific amount to increment or decrement the GenValue's value when using graphical input. The user cannot directly modify this value, but it can be changed by your application.

The GenValue object always displays a numerical value within its text field. The units of that numerical value may be set by your application. By default, any value within a GenValue is an integer, but that value could also be a decimal value, or even a unit of distance such as centimeters, points, or inches. The GenValue automatically provides whatever notation is required by the display units (decimal points, the words "pt" and "cm," or any other distance notation in the text field).

| Up: GEOS SDK TechDocs | Up | Prev: GenValue | Next: 2 GenValue Instance Data


GenValue Instance Data

GenValue: 2 GenValue Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 1 GenValue Features | Next: 3 GenValue Basics

The GenValue contains instance fields that affect the current value, the display units of that value, and the action to take when any value operation has been completed. These instance fields are listed in GenValue Instance Data . Remember, in addition to these instance data variables, there are also the instance fields inherited from the GenValue's superclass, GenClass .

Code Display 8-1 GenValue Instance Data

/* The instance data fields for GenValue are shown below. Those that are records
 * of flags have their default flags shown with other possible flags listed below.
 * Other fields are shown with their default values. */
    @instance WWFixedAsDWord				GVLI_value = MakeWWFixed(0.0);
    @instance WWFixedAsDWord				GVLI_minimum = MakeWWFixed(0.0);
    @instance WWFixedAsDWord				GVLI_maximum = MakeWWFixed(32766);
    @instance WWFixedAsDWord				GVLI_increment = MakeWWFixed(1.0);
    @instance GenValueStateFlags				GVLI_stateFlags = 0;
/* GenValueStateFlags */
    typedef ByteFlags GenValueStateFlags;
    #define GVSF_INDETERMINATE				0x80
    #define GVSF_MODIFIED				0x40
    #define GVSF_OUT_OF_DATE				0x20
    @instance GenValueDisplayFormat 			GVLI_displayFormat = GVDF_INTEGER;
/* GenValueDisplayFormat */
    typedef ByteEnum GenValueDisplayFormat;
    #define GVDF_INTEGER 0
    #define GVDF_DECIMAL 1
    #define GVDF_POINTS 2
    #define GVDF_INCHES 3
    #define GVDF_CENTIMETERS 4
    #define GVDF_MILLIMETERS 5
    #define GVDF_PICAS 6
    #define GVDF_EUR_POINTS 7
    #define GVDF_CICEROS 8
    #define GVDF_POINTS_OR_MILLIMETERS 9
    #define GVDF_POINTS_OR_CENTIMETERS 10
    @instance optr				GVLI_destination;
    @instance Message				GVLI_applyMsg = 0;

GVLI_value is the current numerical value of the GenValue. By default, it is an integer constant defined by the application. Depending on the GVLI_ displayFormat , it can be interpreted as an integer, a decimal, or distance unit.

GVLI_maximum is the maximum possible value that the GenValue may display. This value may be positive or negative. The default value is 32766.

GVLI_minimum is the minimum possible value that the GenValue may display. This value may be positive or negative. The default value is zero.

GVLI_increment is the value to increment (or decrement) the GenValue when its value is changed by UI controls rather than direct text input. The default value is one.

GVLI_ stateFlags specifies the GenValueStateFlags for the GenValue to operate under. These flags affect whether the data within the GenValue is indeterminate (not necessarily true) or modified (changed since the last MSG_GEN_APPLY ).

GVLI_ displayFormat specifies the units of measurement ( GenValueDisplayFormat ) that the numerical value represents. By default, this is an integer, but it could also be a decimal or a unit of measurement (such as inches, points, or centimeters).

GVLI_applyMsg is the message to send out upon GenValue changes (i.e. whenever it receives MSG_GEN_APPLY). There is no default message.

GVLI_destination is the object or process to send the message upon GenValue changes. This can be an optr to an object or a valid TravelOption . See the MetaClass chapter. There is no default destination object.

Code Display 8-2 GenValue Optional Attribute Fields

    @vardata Message			ATTR_GEN_VALUE_STATUS_MSG;
    @vardata word			ATTR_GEN_VALUE_DECIMAL_PLACES;
    @vardata WWFixed			ATTR_GEN_VALUE_METRIC_INCREMENT;
    @vardata void			ATTR_GEN_VALUE_SET_MODIFIED_ON_REDUNDANT_SELECTION;

ATTR_GEN_VALUE_STATUS_MSG sets a status message for a GenValue. A status message allows your object to communicate with other objects when changes occur without sending out its apply message (GVLI_ applyMsg ).

ATTR_GEN_VALUE_DECIMAL_PLACES specifies the number of decimal places to display to the right of the decimal point if the GVLI_ displayFormat allows fractional quantities.

ATTR_GEN_VALUE_METRIC_INCREMENT allows you to specify a particular metric increment to use besides the default if the GVLI_ displayFormat is GVDF_POINTS_OR_MILLIMETERS or GVDF_INCHES_OR_CENTIMETERS.

ATTR_GEN_VALUE_SET_MODIFIED_ON_REDUNDANT_SELECTION specifies that the GenValue should be marked modified whether or not a change in the value has occurred. This will result in that value being applied whenever it receives a MSG_GEN_APPLY . (The default behavior for when no change in state occurs, no message will be sent out.)

Code Display 8-3 GenValue Hints

    @vardata void 			HINT_VALUE_INCREMENTABLE;
    @vardata void 			HINT_VALUE_NOT_INCREMENTABLE;
    @vardata void			HINT_VALUE_NAVIGATE_TO_NEXT_FIELD_ON_RETURN_PRESS;
    @vardata Message			HINT_VALUE_CUSTOM_RETURN_PRESS;
    @vardata WWFixedAsDWord 				HINT_VALUE_DISPLAYS_RANGE;
    @vardata GenValueIntervals				HINT_VALUE_DISPLAY_INTERVALS;
    @vardata void			HINT_VALUE_CONSTRAIN_TO_INTERVALS;
typedef struct {
    word	GVI_numMajorIntervals;
    word	GVI_numMinorIntervals;
} GenValueIntervals;
    @vardata void			HINT_VALUE_SHOW_MIN_AND_MAX;
    @vardata void			HINT_VALUE_IMMEDIATE_DRAG_NOTIFICATION;
    @vardata void			HINT_VALUE_DELAYED_DRAG_NOTIFICATION;
    @vardata void			HINT_VALUE_ORIENT_HORIZONTALLY;
    @vardata void			HINT_VALUE_ORIENT_VERTICALLY;
    @vardata void			HINT_VALUE_ANALOG_DISPLAY;
    @vardata void			HINT_VALUE_DIGITAL_DISPLAY;
    @vardata void			HINT_VALUE_NO_DIGITAL_DISPLAY;
    @vardata void			HINT_VALUE_NO_ANALOG_DISPLAY;
    @vardata void			HINT_VALUE_NOT_DIGITALLY_EDITABLE;
    @vardata void			HINT_VALUE_DO_NOT_MAKE_LARGER_ON_PEN_SYSTEMS;

HINT_VALUE_INCREMENTABLE and HINT_VALUE_NOT_INCREMENTABLE specify whether increment (and decrement) gadgets are appropriate for this GenValue. By default, GenValues are incrementable.

HINT_VALUE_CUSTOM_RETURN_PRESS stores a Message to send out if the user hits return within the textual portion of a GenValue. Normally, this action triggers the interaction default (sending the apply message).

HINT_VALUE_DISPLAYS_RANGE indicates that this GenValue displays a range of values between its minimum and maximum. In most cases, this suggests that the GenValue use sliders or some other UI gadget that is able to show the width of a range. It is up to the specific UI to support range values. If this hint is present, GVLI_ value refers to the starting point of the range of values, whose width is stored in the WWFixedAsDWord value here. The maximum GVLI_ value in this case would be GVLI_ maximum minus the range. If this hint is not present, the range "length" is presumed to be zero, even in gadgets that specify a range length by default.

HINT_VALUE_DISPLAY_INTERVALS indicates that intervals should be displayed along an object's range. This hint is used most often in analog (e.g. slider) type displays. If possible, hash marks will appear along the GenValue's display at the intervals specified by GenValueIntervals . This structure stores entries for both major intervals and minor intervals. In general, minor intervals will have smaller tick marks than major intervals.

typedef struct {
    word GVI_numMajorIntervals;
    word GVI_numMinorIntervals;
} GenValueIntervals;

If either GVI_ numMajorIntervals or GVI_ numMinorIntervals is zero, only one set of marks will appear.

HINT_VALUE_CONSTRAIN_TO_INTERVALS suggests that the value within GVLI_ value constrain itself to the GVI_ numMinorIntervals interval within HINT_VALUE_DISPLAY_INTERVALS.

HINT_VALUE_SHOW_MIN_AND_MAX instructs the GenValue to display its minimum and maximum values, if possible.

HINT_VALUE_IMMEDIATE_DRAG_NOTIFICATION instructs the GenValue to send out its status and/or apply messages constantly during a drag operation (each time the value changes). It is up to the specific UI to support this behavior.

HINT_VALUE_DELAYED_DRAG_NOTIFICATION instructs the GenValue to delay sending out status and/or apply message until the user releases the mouse after the drag operation. It is up to the specific UI to support this behavior.

HINT_VALUE_ORIENT_HORIZONTALLY instructs the specific UI to arrange an analog GenValue, if available, in the horizontal dimension.

HINT_VALUE_ORIENT_VERTICALLY instructs the specific UI to arrange an analog GenValue, if available, in the vertical dimension.

HINT_VALUE_ANALOG_DISPLAY indicates that the GenValue should be displayed in an analog fashion, if applicable. HINT_VALUE_DIGITAL_DISPLAY indicates that the GenValue should be displayed in a digital fashion (i.e. numerically). Similarly, HINT_VALUE_NO_DIGITAL_DISPLAY and HINT_VALUE_NO_ANALOG_DISPLAY indicate that a digital or analog display is not appropriate.

HINT_VALUE_NOT_DIGITALLY_EDITABLE instructs the UI to disallow editing of the text within a numerical GenValue. This hint is only applicable if some other UI means of changing the value is available. To remove a numeric display of values entirely, use HINT_VALUE_NO_DIGITAL_DISPLAY.

HINT_VALUE_DO_NOT_MAKE_LARGER_ON_PEN_SYSTEMS instructs the specific UI to avoid expanding a GenValue object to accept pen input, if possible. By default, text areas of GenValues grow larger under pen systems to allow ink strokes. This hint usually indicates that non-ink means of entering values is available.

| Up: GEOS SDK TechDocs | Up | Prev: 1 GenValue Features | Next: 3 GenValue Basics


GenValue Basics

GenValue: 3 GenValue Basics

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 GenValue Instance Data | Next: 4 Supplemental Usage

The GenValue instance fields can be set to specific values in your Goc file, and they can also be modified by your application at run-time. This section describes how to set and modify these fields in your Goc file.

The Value

The Minimum and Maximum

The Increment

GenValue States

Display Formats

Sending an Action

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 GenValue Instance Data | Next: 4 Supplemental Usage


The Value

GenValue: 3.1 GenValue Basics: The Value

| Up: GEOS SDK TechDocs | Up | Prev: 3 GenValue Basics | Next: 3.2 The Minimum and Maximum
GVLI_value, MSG_GEN_VALUE_SET_VALUE, MSG_GEN_VALUE_SET_INTEGER_VALUE, MSG_GEN_VALUE_GET_VALUE

The GVLI_ value instance field stores the current numerical value of the GenValue. You may set an initial value for the GenValue to appear with by setting this instance field in your Goc file. This value is a fixed point number; use MakeWWFixed to create this fixed point number in your instance data.

Any user changes on the value within the text field will not affect GVLI_ value until MSG_GEN_APPLY applies that value. If the GenValue operates in delayed mode, it will be marked modified in its GVLI_ stateFlags whenever a user change occurs; those changes will be applied when the GenValue receives a MSG_GEN_APPLY . In most cases, however, a GenValue operates in immediate mode, which will result in an immediate change in GVLI_ value .

Code Display 8-4 Setting an Initial Value

/* This GenValue will appear with the initial integer value of two. MakeWWFixed
 * creates a fixed point value. */
@object GenValueClass MyValue = {
    GI_visMoniker = "My Value";
    GVLI_value = MakeWWFixed(2.0);
}

GenValueClass provides several messages to change the value without user control. MSG_GEN_VALUE_SET_VALUE sets this numeric value to a passed fixed point value; this fixed point value may be any integer or decimal value. MSG_GEN_VALUE_SET_INTEGER_VALUE is a simpler message which sets GVLI_ value to an integer value passed. Neither of these messages mark the GenValue modified; you can do this with MSG_GEN_VALUE_SET_MODIFIED_STATE .

MSG_GEN_VALUE_SET_VALUE

void	MSG_GEN_VALUE_SET_VALUE(
        WWFixedAsDWord		value,
        Boolean		indeterminate);

This message sets the GVLI_value field of the GenValue to the passed value . This message clears a GenValue's modified state in its GVLI_ stateFlags . To mark the GenValue modified send MSG_GEN_VALUE_SET_MODIFIED_STATE after sending this message.

Source: Unrestricted. This message is also used internally when responding to user actions.

Destination: Any GenValue object.

Parameters: value The fixed point value to set GVLI_ value to. If you only need an integral value, consider using MSG_GEN_VALUE_SET_INTEGER_VALUE instead.

indeterminate
TRUE to mark the GenValue indeterminate, FALSE to mark it non indeterminate.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_INTEGER_VALUE

void	MSG_GEN_VALUE_SET_INTEGER_VALUE(
        word		value,
        Boolean		indeterminate);

This message sets the GVLI_ value to the passed integer (word-sized) value. GVLI_ value will then contain this value in its high (integral) word and zero in its low (fractional) word. The modified state of the GenValue will be cleared.

Source: Unrestricted. This message is also used internally when responding to user actions.

Destination: Any GenValue object.

Parameters: value The signed integer value to set GVLI_ value to.

indeterminate
TRUE to mark the GenValue indeterminate, FALSE to mark it non indeterminate.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VALUE_GET_VALUE

WWFixedAsDWord MSG_GEN_VALUE_GET_VALUE();

This message returns the value stored in the GenValue's GVLI_value instance field. This returned value will be a fixed point number.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The fixed point numerical value of the GVLI_ value instance field.

Interception: Generally not intercepted.

MSG_GEN_VALUE_GET_INTEGER_VALUE

@alias (MSG_GEN_VALUE_GET_VALUE)
word MSG_GEN_VALUE_GET_INTEGER_VALUE();

This message returns the integral portion of the GenValue's value.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The integral value of the GVLI_ value instance field.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3 GenValue Basics | Next: 3.2 The Minimum and Maximum


The Minimum and Maximum

GenValue: 3.2 GenValue Basics: The Minimum and Maximum

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 The Value | Next: 3.3 The Increment
GVLI_minimum, GVLI_maximum, MSG_GEN_VALUE_GET_MAXIMUM, MSG_GEN_VALUE_SET_MAXIMUM, MSG_GEN_VALUE_GET_MINIMUM, MSG_GEN_VALUE_SET_MINIMUM

The maximum ( GVLI_maximum ) and minimum ( GVLI_minimum ) values of a GenValue constrain the value of GVLI_ value . The maximum is the greatest value that the GenValue's GVLI_ value can have and can be any fixed point number from -32767 up to 32767. The minimum is the least value that the GenValue's GVLI_ value can have (including negative numbers) and can be any fixed point number from 32767 down to -32767. The maximum value must always be greater than or equal to the minimum value; otherwise, an error will occur.

The maximum and minimum values of the GenValue also can be examined and modified by the application. This is helpful if you need to use the same GenValue for two functions with different ranges of values. You can use the GenValue for one function, change the bounds, and use it for another function.

MSG_GEN_VALUE_GET_MINIMUM and MSG_GEN_VALUE_GET_MAXIMUM return the values of GVLI_ minimum and GVLI_ maximum respectively. This value is in fixed point format.

MSG_GEN_VALUE_SET_MINIMUM and MSG_GEN_VALUE_SET_MAXIMUM set the values of GVLI_ minimum and GVLI_ maximum respectively. If this new minimum or maximum places the current GVLI_ value outside the valid value range, the value will be adjusted to fall within the current range.

MSG_GEN_VALUE_GET_MAXIMUM

WWFixedAsDWord MSG_GEN_VALUE_GET_MAXIMUM()

This message returns the fixed point value within the GVLI_maximum instance field of the GenValue.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The fixed point maximum value in GVLI_ maximum .

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_MAXIMUM

void	MSG_GEN_VALUE_SET_MAXIMUM(
        WWFixedAsDWord 		value);

This message sets the GVLI_maximum field of the GenValue to the passed fixed point value. If you set a new maximum that places the current value (in GVLI_ value ) above that maximum, GVLI_ value will be adjusted to this maximum.

Source: Unrestricted. This message is also used internally when the GenValue is being built.

Destination: Any GenValue object.

Parameters: value fixed point value to set GVLI_ maximum to.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VALUE_GET_MINIMUM

WWFixedAsDWord MSG_GEN_VALUE_GET_MINIMUM()

This message returns the fixed point value within the GVLI_minimum instance field of the GenValue.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The fixed point minimum value in GVLI_ minimum .

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_MINIMUM

void	MSG_GEN_VALUE_SET_MINIMUM(
        WWFixedAsDWord		value);

This message sets the GVLI_minimum instance field of the GenValue to the passed fixed point value. If you set a new minimum that places the current value (in GVLI_ value ) below that minimum, GVLI_ value will be adjusted to this minimum.

Source: Unrestricted. This message is also used internally when the GenValue is being built.

Destination: Any GenValue object.

Parameters: value fixed point value to set GVLI_ minimum to.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 The Value | Next: 3.3 The Increment


The Increment

GenValue: 3.3 GenValue Basics: The Increment

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 The Minimum and Maximum | Next: 3.4 GenValue States
GVLI_increment, MSG_GEN_VALUE_GET_INCREMENT, MSG_GEN_VALUE_SET_INCREMENT, HINT_VALUE_INCREMENTABLE, HINT_VALUE_NOT_INCREMENTABLE

The GVLI_increment instance field stores the fixed point increment value for a GenValue. The increment value is the amount by which the current value (in GVLI_ value ) may increase or decrease when that value is changed by UI controls. The increment value can be any positive fixed point value between one and 65535.

If GVLI_ increment is greater than the possible range of the GenValue (the distance between minimum and maximum), then incrementing or decrementing the GenValue will toggle GVLI_ value between its maximum and minimum.

Code Display 8-5 Setting Minimum, Maximum, Increment Values

@object GenValueClass MyValue = {
    GI_visMoniker = "My Value";
    GVLI_value = MakeWWFixed(1.0);
    GVLI_minimum = MakeWWFixed(-100.0);
    GVLI_maximum= MakeWWFixed(100.0);
    GVLI_increment = MakeWWFixed(5.0);
}

You may change the fixed point value of this increment with MSG_GEN_VALUE_SET_INCREMENT . You may also return the current increment stored within GVLI_ increment with MSG_GEN_VALUE_GET_INCREMENT . You may wish to change a GenValue's increment one GenValue is being used for multiple roles and must change its increment value.

By default, all GenValues are incrementable. If you do not wish to have your GenValue provide UI controls for incrementing (or decrementing) its value, add HINT_VALUE_NOT_INCREMENTABLE in its instance data. HINT_VALUE_INCREMENTABLE provides the default behavior. (You may still increment or decrement the value manually with MSG_GEN_VALUE_INCREMENT or MSG_GEN_VALUE_DECREMENT .)

MSG_GEN_VALUE_SET_INCREMENT

void	MSG_GEN_VALUE_SET_INCREMENT (
        WWFixedAsDWord 		value);

This message sets the GVLI_increment field to the passed value .

Source: Unrestricted. This message is also used internally when the GenValue is being built.

Destination: Any GenValue object.

Parameters: value fixed point value to set GVLI_ increment to.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VALUE_GET_INCREMENT

WWFixedAsDWord MSG_GEN_VALUE_GET_INCREMENT();

This message returns the value of the GVLI_increment field of the GenValue.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The fixed point value of GVLI_ increment .

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 The Minimum and Maximum | Next: 3.4 GenValue States


GenValue States

GenValue: 3.4 GenValue Basics: GenValue States

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 The Increment | Next: 3.5 Display Formats
GVLI_stateFlags, MSG_GEN_VALUE_SET_INDETERMINATE_STATE, MSG_GEN_VALUE_IS_INDETERMINATE, MSG_GEN_VALUE_SET_MODIFIED_STATE, MSG_GEN_VALUE_IS_MODIFIED, ATTR_GEN_SET_MODIFIED_ON_REDUNDANT_SELECTION

GVLI_ stateFlags stores the current state of the GenValue. There are two GenValueStateFlags :

  • GVSF_INDETERMINATE
    This flag specifies that the value within the GenValue is indeterminate (may or may not be true). In most cases, you will not need to set this flag.
  • GVSF_MODIFIED
    This flag specifies that the value within the GenValue has changed since it last received a MSG_GEN_APPLY . The handler for MSG_GEN_APPLY checks whether this flag is set before sending out the GenValue's GVLI_ applyMsg .
  • GVSF_OUT_OF_DATE
    This flag specifies that the value within the GenValue is out of date with what the user has typed in. This is distinct from the GVSF_MODIFIED state; while the user is typing an a value ("123" for example) the typed value may be temporarily out of range, or incomplete. In this case, the value would be marked GVSF_OUT_OF_DATE and GVSF_MODIFIED. Notice that if the value were incremented or decremented, it would be marked GVSF_MODIFIED, but not GVSF_OUT_OF_DATE because the value is legal and presumable valid for operations. This flag is most useful when telling status messages whether a value should be used.

GenValues are normally marked as not modified anytime their state is set with an external message, marked modified whenever the user interacts with them, and marked not modified after receiving MSG_GEN_APPLY . MSG_GEN_VALUE_SET_MODIFIED_STATE allows you to control the modified state of a GenValue outside of these events.

You may set a GenValue's indeterminate or modified state with MSG_GEN_VALUE_SET_INDETERMINATE_STATE or MSG_GEN_VALUE_SET_MODIFIED_STATE , respectively.

To check whether a GenValue is indeterminate or modified, use MSG_GEN_VALUE_IS_INDETERMINATE or MSG_GEN_VALUE_IS_MODIFIED.

You may also mark a GenValue GVSF_OUT_OF_DATE by sending it MSG_GEN_VALUE_SET_OUT_OF_DATE.

MSG_GEN_VALUE_SET_INDETERMINATE_STATE

void	MSG_GEN_VALUE_SET_INDETERMINATE_STATE(
        Boolean		indeterminateState);

This message sets the indeterminate state for a GenValue. Pass TRUE to mark the GenValue indeterminate, FALSE to mark it not indeterminate. The GenValue will not be marked modified after this message.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: indeterminateState
TRUE to set the GVSF_INDETERMINATE flag in the GenValue's GVLI_ stateFlags ,
FALSE to clear the GVSF_INDETERMINATE flag.

Interception: Generally not intercepted.

MSG_GEN_VALUE_IS_INDETERMINATE

Boolean	MSG_GEN_VALUE_IS_INDETERMINATE();

This message checks whether a GenValue is indeterminate.

Source: Unrestricted.

Destination: Any GenValue object.

Return: TRUE if GenValue is indeterminate, FALSE if it is not.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_MODIFIED_STATE

void	MSG_GEN_VALUE_SET_MODIFIED_STATE(
        Boolean		modifiedState);

This message sets the modified state for a GenValue. Pass TRUE to mark the GenValue modified, FALSE to mark it not modified.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: modifiedState
TRUE to set the GVSF_MODIFIED flag in the GenValue's GVLI_ stateFlags ,
FALSE to clear the GVSF_MODIFIED flag.

Interception: Generally not intercepted.

MSG_GEN_VALUE_IS_MODIFIED

Boolean	MSG_GEN_VALUE_IS_MODIFIED();

This message checks whether a GenValue has been modified since the last MSG_GEN_APPLY .

Source: Unrestricted.

Destination: Any GenValue object.

Return: TRUE if GenValue is modified, FALSE if it is not.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_OUT_OF_DATE

void	MSG_GEN_VALUE_SET_OUT_OF_DATE();

This message sets a GenValue's GVSF_OUT_OF_DATE flag.

Source: Unrestricted.

Destination: Any GenValue object.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 The Increment | Next: 3.5 Display Formats


Display Formats

GenValue: 3.5 GenValue Basics: Display Formats

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 GenValue States | Next: 3.6 Sending an Action
GVLI_displayFormat, MSG_GEN_VALUE_SET_DISPLAY_FORMAT, MSG_GEN_VALUE_GET_DISPLAY_FORMAT, ATTR_GEN_VALUE_METRIC_EQUIVALENT, ATTR_GEN_VALUE_DECIMAL_PLACES

In addition to displaying an integer numerical value, a GenValue may also display numerical values of several other formats. These formats may be any one of the GenValueDisplayFormat enumerations provided in GenValueClass . The allowed enumerations of type GenValueDisplayFormat are:

GVDF_INTEGER
GVDF_DECIMAL
GVDF_POINTS
GVDF_INCHES
GVDF_CENTIMETERS
GVDF_MILLIMETERS
GVDF_PICAS
GVDF_EUR_POINTS
GVDF_CICEROS
GVDF_POINTS_OR_MILLIMETERS
GVDF_INCHES_OR_CENTIMETERS

GVLI_ displayFormat controls how the values of the GenValue will be represented. For example, if the display format is in centimeters, the display will include the text "cm" after the numerical value; if the display includes a fractional part, a decimal point will be present. These display formats will also convert the values (which are stored as points) into the proper distance units for the textual display.

By default, the value's fractional portion will be displayed using 3 places to the right of the decimal point. You may alter this number of places with ATTR_GEN_VALUE_DECIMAL_PLACES. You may only choose a number of decimal places between zero to four (inclusive), because of the limited text space offered within a GenValue.

Your GenApplication object contains instance data which specifies whether the application is being run under US or metric units. This setting may affect the display of your units depending on the particular GenValueDisplayFormat .

GVDF_INTEGER displays the value as an integer (the high word of the fixed point value) and ignores any fractional part.
GVDF_DECIMAL displays the value as a decimal value.
GVDF_POINTS displays the value in points (1/72 of an inch) regardless of whether metric or US units are specified for the application.
GVDF_INCHES displays the value in inches regardless of whether metric or US units are specified for the application.
GVDF_CENTIMETERS displays the value in centimeters regardless of whether metric or US units are specified for the application.
GVDF_MILLIMETERS displays the value in millimeters regardless of whether metric or US units are specified for the application.
GVDF_PICAS displays the value as a distance in picas. One pica is equal to 12 US Points, or 1/6 of an inch.
GVDF_EUR_POINTS displays the value as a distance in European Points. One european point is about equal to 1.0656 US Points.
GVDF_CICEROS displays the value as a distance in Ciceros. One Cicero is equal to 12 European Points.
GVDF_POINTS_OR_MILLIMETERS and GVDF_INCHES_OR_CENTIMETERS are special cases. These display formats display the value in points (or inches) if US units are specified for the application; the value will be represented in millimeters (or centimeters) if metric units are instead specified.

Importantly, all distance units (inches, picas, centimeters, etc.) store their values as Points (1/72 inch). The system automatically converts these values (in Points) into the proper units of the GenValue's GVLI_ displayFormat when it displays the numerical value within the textual display.

Code Display 8-6 Setting a Distance Display Format

@object GenValueClass MyValue = {
    GI_visMoniker = "My Value";
    GVLI_displayFormat = GVDF_INCHES;
/* For an initial value of 1 inch, the equivalent value in Points is 72. */
    GVLI_value = MakeWWFixed(72.0);
/* For an initial value of 1/2 inch, the equivalent value in Points is 36. */
    GVLI_increment = MakeWWFixed(36.0);
}

For example, if your display format is GVDF_INCHES and you wish to set an initial value of 1 inch and an increment of 1/2 an inch, you should set these values to 72 (points which equals 1 inch) and 36 (points which equals one-half inch) respectively. This is necessary because the system expects these values to be in Points for other system operations. A conversion table is provided below.

Any increments for GVDF_POINTS_OR_MILLIMETERS or GVDF_INCHES_OR_CENTIMETERS are assumed to be in US units. If the application instead operates in metric, the increment will be automatically converted to a metric equivalent by the system; this metric equivalent will usually be rounded to a convenient numerical value. You may specify an ATTR_GEN_VALUE_METRIC_EQUIVALENT to override the default increment that the system calculates, however.

For example, assume GVDF_INCHES_OR_CENTIMETERS is selected and the value and increment are 72 (points which equals 1 inch). If the application is in US units, the display will specify inches and the value and increment will be 72 (1 inch); if the application is instead in metric, the display will specify centimeters and the increment will be 70.866 (2.5 cm). If instead, you choose an ATTR_GEN_VALUE_METRIC_INCREMENT of 52.692 (2 cm), that will be the increment used if the application is metric.

Code Display 8-7 ATTR_GEN_VALUE_METRIC_INCREMENT

/* If the application is US, the initial value will be 1 inch and the increment
 * will be 1 inch. If the application is metric, the initial value will be 2.54 cm
 * (1 inch or 72 points) but the increment will be 2.0 cm (56.692 points). If
 * ATTR_GEN_VALUE_METRIC_INCREMENT were not included, the system would have chosen
 * an increment of 2.5 cm (70.866 points) which is the closest "nice" value to the
 * original increment of 72 points (1 inch). */
@object GenValueClass MyValue = {
    GI_visMoniker = "My Value";
    GVLI_displayFormat = GVDF_INCHES_OR_CENTIMETERS;
    GVLI_value = MakeWWFixed(72.0);
    GVLI_increment = MakeWWFixed(72.0);
    ATTR_GEN_VALUE_METRIC_INCREMENT = MakeWWFixed(56.692);
}

To set a new display format, send MSG_GEN_VALUE_SET_DISPLAY_FORMAT . To retrieve the current display format, send the GenValue MSG_GEN_VALUE_GET_DISPLAY_FORMAT . Note that changing the display format will not change the numerical value of that display. For example, if the display format changes from decimal to integer, the GenValue will round the number down and display only the integer portion of the value.

MSG_GEN_VALUE_GET_DISPLAY_FORMAT

GenValueDisplayFormat MSG_GEN_VALUE_GET_DISPLAY_FORMAT();

This message returns the GVLI_ displayFormat field of the GenValue.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: GenValueDisplayFormat of the GenValue.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_DISPLAY_FORMAT

void	MSG_GEN_VALUE_SET_DISPLAY_FORMAT(
        GenValueDisplayFormat			format);

This message sets the GVLI_displayFormat of the GenValue to the given format. The current value in GVLI_ value is unaffected, but the display will be updated to reflect the new display format.

Source: Unrestricted. This message is also used internally when the GenValue is being built.

Destination: Any GenValue object.

Parameters: format GenValueDisplayFormat to set the GenValue to.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 GenValue States | Next: 3.6 Sending an Action


Sending an Action

GenValue: 3.6 GenValue Basics: Sending an Action

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Display Formats | Next: 4 Supplemental Usage
GVLI_applyMsg, GVLI_destination, HINT_VALUE_NAVIGATE_TO_NEXT_FIELD_ON_RETURN_PRESS, HINT_VALUE_CUSTOM_RETURN_PRESS, MSG_GEN_VALUE_GET_APPLY_MSG, MSG_GEN_VALUE_SET_APPLY_MSG, MSG_GEN_VALUE_GET_DESTINATION, MSG_GEN_VALUE_SET_DESTINATION

GVLI_ applyMsg sets the message for the GenValue to send out whenever it has been modified and needs to apply its changes. Whenever a GenValue receives MSG_GEN_APPLY, i t will check whether its GVSF_MODIFIED flag has been set; if it has, it will send out its apply message. If a GenValue is operating in immediate mode, these actions will happen immediately, resulting in an immediate action.

GVLI_ destination specifies the destination object (or process) to send the GVLI_ applyMsg to. (This may also be a TravelOption , such as TO_APP_TARGET.)

Code Display 8-8 Sending an Apply Message

@object GenValueClass MyValue = {
    GI_visMoniker = "My Value";
    GVLI_value = MakeWWFixed(1.0);
    GVLI_applyMsg = MSG_MY_VALUE_DOUBLE_VALUE;
    GVLI_destination = process;
}
/* Retrieve the current value. This value will be a fixed point dword. */
@method MyValueProcessClass, MSG_MY_VALUE_DOUBLE_VALUE {
    WWFixedAsDWord	curValue;
    curValue = @call MyValue::MSG_GEN_VALUE_GET_VALUE();
    curValue = curValue*2;
    @call MyValue::MSG_GEN_VALUE_SET_VALUE(curValue, 0);
}

A GenValue's changes are typically applied when the user hits the Return key and the GenValue has the focus. You can change this behavior, though, with the following hints: HINT_VALUE_CUSTOM_RETURN_PRESS allows a textually-oriented GenValue to send the specified message to the destination object when the Return key is pressed. HINT_VALUE_NAVIGATE_TO_NEXT_FIELD_ON_RETURN_PRESS instructs the GenValue to navigate (via the UI) to the next textually-activated object (as the tab key works in many situations).

To change a GenValue's apply message or destination, send it MSG_GEN_VALUE_SET_APPLY_MSG or MSG_GEN_VALUE_SET_DESTINATION , respectively. Use MSG_GEN_VALUE_GET_APPLY_MSG or MSG_GEN_VALUE_GET_DESTINATION to return the current apply message or destination.

The apply message should be defined on the prototype GEN_VALUE_APPLY_MSG , whose values are shown below.

GEN_VALUE_APPLY_MSG

void	GEN_VALUE_APPLY_MSG(
        WWFixedAsDWord		value,
        word		stateFlags);

This prototype defines the message sent out when the GenValue is "applied." The output of the GenValue should handle a message with these parameters.

Source: GenValue, when "applied."

Destination: The GenValue's output ( GVLI_destination ) object.

Parameters: value The current value of the GenValue.

stateFlags
The GenValueStateFlags stored in GVLI_stateFlags .

Return: Nothing.

Interception: The destination object should handle the apply message with this format.

MSG_GEN_VALUE_GET_APPLY_MSG

Message	MSG_GEN_VALUE_GET_APPLY_MSG();

This message returns the GenValue's GVLI_ applyMsg .

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The apply message of the GenValue.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_APPLY_MSG

void	MSG_GEN_VALUE_SET_APPLY_MSG(
        Message		message);

This message sets the apply message (in GVLI_ applyMsg ) for a GenValue.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: message The apply message to set for the GenValue.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VALUE_GET_DESTINATION

optr	MSG_GEN_VALUE_GET_DESTINATION();

This message returns the current destination object (or process) that the GenValue sends its apply messages to.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The destination optr (GVLI_ destination ) of the GenValue.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_DESTINATION

void	MSG_GEN_VALUE_SET_DESTINATION(
        optr	dest);

This message sets the GVLI_destination field of the range to the passed optr. The object can be a pointer to a specific object in the system (i.e. the GenProcess object) or can be a pointer to a generic location in the system (i.e. a TravelOption ).

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: dest The optr of the new destination object.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Display Formats | Next: 4 Supplemental Usage


Supplemental Usage

GenValue: 4 Supplemental Usage

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 GenValue Basics | Next: 4.1 Adjusting the Value Indirectly

Besides altering instance data, there are several other messages and mechanisms for your use in GenValueClass .

Adjusting the Value Indirectly

Status Messages

Retrieving Text

Using Value Ratios

Text Filters for the GenValue

Using Ranges in GenValues

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 GenValue Basics | Next: 4.1 Adjusting the Value Indirectly


Adjusting the Value Indirectly

GenValue: 4.1 Supplemental Usage: Adjusting the Value Indirectly

| Up: GEOS SDK TechDocs | Up | Prev: 4 Supplemental Usage | Next: 4.2 Status Messages
MSG_GEN_VALUE_INCREMENT, MSG_GEN_VALUE_DECREMENT, MSG_GEN_VALUE_SET_VALUE_TO_MINIMUM, MSG_GEN_VALUE_SET_VALUE_TO_MAXIMUM

To increase the value of GVLI_ value by the increment in GVLI_ increment , send the GenValue MSG_GEN_VALUE_INCREMENT . To decrease the value of GVLI_ value by the increment in GVLI_ increment , send the GenValue MSG_GEN_VALUE_DECREMENT . These messages are equivalent to clicking on the UI controls provided to increment or decrement the GenValue (usually up and down arrows). Both of these messages clear the indeterminate state of the object and do not change its modified state.

To set the value of GVLI_ value to the minimum in GVLI_ minimum , send the GenValue MSG_GEN_VALUE_SET_VALUE_TO_MINIMUM . To set the value of GVLI_ value to the maximum in GVLI_ maximum , send the GenValue MSG_GEN_VALUE_SET_VALUE_TO_MAXIMUM . Both of these messages clear the indeterminate state of the GenValue and do not change its modified state.

Note that HINT_VALUE_NOT_INCREMENTABLE has no effect on these messages; that hint only removes any user controls for incrementing or decrementing a value.

MSG_GEN_VALUE_INCREMENT

void	MSG_GEN_VALUE_INCREMENT();

This message increases the value of GVLI_ value within the GenValue by the increment in GVLI_ increment . This message will clear the indeterminate flag of the GenValue but will not affect its modified flag.

Source: Unrestricted. This message is also used internally when responding to user actions.

Destination: Any GenValue object.

Parameters: None.

Return: Nothing. GVLI_ value will be incremented (or set to the maximum if increasing the value would push it over the maximum).

Interception: Generally not intercepted.

MSG_GEN_VALUE_DECREMENT

void	MSG_GEN_VALUE_DECREMENT();

This message decreases the value of GVLI_ value within the GenValue by the increment in GVLI_ increment . This message will clear the indeterminate flag of the GenValue but will not affect its modified flag.

Source: Unrestricted. This message is also used internally when responding to user actions.

Destination: Any GenValue object.

Parameters: None.

Return: Nothing. GVLI_ value will be decremented (or set to the minimum if decreasing the value would push it below the minimum).

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_VALUE_TO_MINIMUM

void	MSG_GEN_VALUE_SET_VALUE_TO_MINIMUM();

This message sets the value of GVLI_ value to the minimum value in GVLI_ minimum . This message will clear the indeterminate flag of the GenValue but will not affect its modified flag.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: Nothing. GVLI_value will be set to GVLI_ minimum .

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_VALUE_TO_MAXIMUM

void	MSG_GEN_VALUE_SET_VALUE_TO_MAXIMUM();

This message sets the value of GVLI_ value to the maximum value in GVLI_ maximum . This message will clear the indeterminate flag of the GenValue but will not affect its modified flag.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: Nothing. GVLI_value will be set to GVLI_ maximum .

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Supplemental Usage | Next: 4.2 Status Messages


Status Messages

GenValue: 4.2 Supplemental Usage: Status Messages

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Adjusting the Value Indirectly | Next: 4.3 Retrieving Text
ATTR_GEN_VALUE_STATUS_MSG, MSG_GEN_VALUE_SEND_STATUS_MSG

If your GenValue is operating in delayed mode, there usually occur times when your GenValue's state may not reflect the most recent changes. In most cases this is fine, but in some cases you may wish other UI objects to be notified of a change in your GenValue's state without sending out an apply message. This can be done with a status message.

A status message allows your GenValue to send out a message whenever the user interacts with the GenValue, regardless of whether that change will be immediately applied. This is most useful for cases in which two UI objects are representing information that depends on each other. The status message allows one UI object to inform its friend that its state has changed, and that the friend should change its state to reflect the new information.

To give a GenValue a status message, include ATTR_GEN_VALUE_STATUS_MSG in the object's declaration. Use the prototype GEN_VALUE_STATUS_MSG to define your status message. This prototype ensures that the status message passes the correct parameters (the current value and state flags of the GenValue).

Any user changes that do not result in the sending of the object's apply message will result in the sending of the object's status message. For an object in immediate mode, this ATTR will have no effect. You may also manually send an object's status message by sending the GenValue MSG_GEN_VALUE_SEND_STATUS_MSG .

GEN_VALUE_STATUS_MSG

void	GEN_VALUE_STATUS_MSG(
        WWFixedAsDWord		value,
        word		stateFlags);

This prototype should be used to define the status message of the GenValue.

Source: The GenValue, when its status message is sent.

Destination: The GenValue's destination object ( GVLI_destination ).

Parameters: value The current user value of the GenValue.

stateFlags
The current value of GVLI_stateFlags .

Return: Nothing.

Interception: Must be handled by the output object if the status message is to have any effect.

MSG_GEN_VALUE_SEND_STATUS_MSG

void	MSG_GEN_VALUE_SEND_STATUS_MSG(
        Boolean		modifiedState);

This message sends the status message stored in the object's ATTR_GEN_VALUE_STATUS_MSG instance field. You should pass this message the modified State you wish to send. This modified state may or not reflect the GVSF_MODIFIED flag in the GenValue's GVLI_ stateFlags .

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: modifiedState TRUE if this message should pass the modified bit (GVSF_MODIFIED) set, FALSE if it should pass GVSF_MODIFIED cleared.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Adjusting the Value Indirectly | Next: 4.3 Retrieving Text


Retrieving Text

GenValue: 4.3 Supplemental Usage: Retrieving Text

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Status Messages | Next: 4.4 Using Value Ratios
MSG_GEN_VALUE_GET_VALUE_TEXT, MSG_GEN_VALUE_SET_VALUE_FROM_TEXT, MSG_GEN_VALUE_GET_MAX_TEXT_LEN, MSG_GEN_VALUE_SET_MAX_TEXT_LEN, MSG_GEN_VALUE_SELECT_TEXT

The GenValue's numeric values are displayed within a special text field. In addition to retrieving the numeric value of the GenValue, you may also retrieve the textual representation of that number with MSG_GEN_VALUE_GET_VALUE_TEXT . Similarly, you can set the value of the GenValue from a textual representation with MSG_GEN_VALUE_SET_VALUE_FROM_TEXT .

You must pass these messages a GenValueType which specifies the instance data field you are wishing to set or get. The GenValueType types are:

typedef enum /* word */ {
    GVT_VALUE,			/* GVLI_value */
    GVT_MINIMUM,			/* GVLI_minimum */
    GVT_MAXIMUM,			/* GVLI_maximum */
    GVT_INCREMENT		,	/* GVLI_increment */
    GVT_LONG,			/* Longest value we can
                 * create. */
    GVT_RANGE_LENGTH,			/* End of the displayed
                 * range, if applicable. */
    GVT_RANGE_END,			/* Last value in the range,
                 * if applicable. */
    GVT_VALUE_AS_RATIO_OF_AVAILABLE_RANGE
                /* The current value,
                 * relative to minimum. */
} GenValueType;

Some special subclasses of the GenValue will want to calculate how many characters it will allow the user to type into its text field. By default, the number of characters is determined by the maximum and minimum values, but subclasses can handle MSG_GEN_VALUE_GET_MAX_TEXT_LEN to set it specifically. This message is sent by the range to itself when deciding how big the text field should be. The maximum text length allowed is thirty characters long.

If you wish to select a GenValue's text, send the GenValue MSG_GEN_VALUE_SELECT_TEXT. The specific UI has final say on whether it allows a GenValue to exhibit selectable text.

MSG_GEN_VALUE_GET_VALUE_TEXT

void	MSG_GEN_VALUE_GET_VALUE_TEXT(
        char		*buffer,
        GenValueType		valueType);

This message retrieves a fixed point value (either GVLI_value , GVLI_ minimum , GVLI_ maximum , or GVLI_ increment ) from the GenValue and stores its textual representation (in a null-terminated text string) in the passed buffer. This message is not affected by the indeterminate state of the GenValue.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: buffer The pointer to the buffer to store the null-terminated text string. This buffer must be at least GEN_VALUE_MAX_TEXT_LEN bytes long.

valueType
The GenValueType , specifying the instance data to retrieve and convert into text.

Return: Nothing. The buffer will be filled in with text.

Interception: Can be intercepted by a subclass of GenValue to allow custom text formats to be displayed on the screen. In this case you would also subclass MSG_GEN_VALUE_SET_VALUE_FROM_TEXT .

MSG_GEN_VALUE_SET_VALUE_FROM_TEXT

void	MSG_GEN_VALUE_SET_VALUE_FROM_TEXT(
        char		*text,
        GenValueType		valueType);

This message sets a fixed point value (either GVLI_value , GVLI_ minimum , GVLI_ maximum , or GVLI_ increment ) of the GenValue from a textual representation (in a null-terminated text string) in the passed buffer. This message clears the indeterminate state of the GenValue but does not change its modified state.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: text The pointer to a null-terminated text string to set the GenValue's text to.

valueType
The GenValueType , specifying the instance data to convert into a fixed point value from the passed text.

Return: Nothing. The instance field will be updated.

Interception: Can be intercepted by a subclass of GenValue to allow custom text formats to be displayed on the screen. In this case, you would also subclass MSG_GEN_VALUE_GET_VALUE_TEXT .

MSG_GEN_VALUE_GET_MAX_TEXT_LEN

byte	MSG_GEN_VALUE_GET_MAX_TEXT_LEN();

This message retrieves the maximum number of characters allowed to be typed into a textual GenValue. By default, this number is calculated from the minimums and maximums specified. Subclasses of GenValue may wish to intercept this message to allow different text lengths (such as values represented with floating point or enumerated type instance data).

Source: The GenValue object sends this to itself when it needs to figure out its maximum text length.

Destination: Itself.

Return: Maximum number of allowable characters

Interception: May be intercepted to allow different text lengths.

MSG_GEN_VALUE_SELECT_TEXT

void	MSG_GEN_VALUE_SELECT_TEXT();

This message selects a GenValue's text, if allowed by the specific UI.

Source: Unrestricted.

Destination: Any GenValue object.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Status Messages | Next: 4.4 Using Value Ratios


Using Value Ratios

GenValue: 4.4 Supplemental Usage: Using Value Ratios

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Retrieving Text | Next: 4.5 Text Filters for the GenValue
MSG_GEN_VALUE_GET_VALUE_RATIO, MSG_GEN_VALUE_SET_VALUE_FROM_RATIO

Instead of setting or getting a specific value, you may want in some cases to set or get a value as a ratio; this ratio is determined as the percentage of the difference between the minimum and maximum. For example, if the minimum of a GenValue is 10 and the maximum is 100, the 50% ratio would be 55 (halfway between 10 and 100).

To retrieve the value of a GenValue as a ratio, send it MSG_GEN_VALUE_GET_VALUE_RATIO . This message will return the ratio as a dword value. You must also pass this message the GenValueType telling it which instance data you wish to retrieve.

To set a value within a GenValue as a ratio, send it MSG_GEN_VALUE_SET_VALUE_RATIO , passing it the ratio and the GenValueType (instance data field) to set. The correct value will be computed and set within your GenValue's instance data.

MSG_GEN_VALUE_GET_VALUE_RATIO

WWFixedAsDWord MSG_GEN_VALUE_GET_VALUE_RATIO(
        GenValueType		valueType);

This message gets the value of a GenValue (GVLI_ value ) as a ratio of its distance between the minimum value and the maximum value. It returns this ratio as a dword (0000.0000h meaning 0%, ffff.ffffh meaning 100%).

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: valueType GenValueType of the instance data field to get the value ratio for.

Return: The ratio as a dword.

Interception: You may intercept subclasses of GenValueClass to allow custom text formats to be displayed on the screen. Someone subclassing this message for this reason would also want to subclass MSG_GEN_VALUE_SET_VALUE_FROM_TEXT to correctly parse text to yield a new value.

MSG_GEN_VALUE_SET_VALUE_RATIO

void	MSG_GEN_VALUE_SET_VALUE_RATIO(
        WWFixed		ratio,
        GenValueType		valueType);

This message sets the value (GVLI_ value ) of a GenValue as a ratio of the distance between its minimum and the maximum values. This ratio should be in the form of a dword (0000.0000h meaning 0%, ffff.ffffh meaning 100%).

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: ratio The dword ratio value.

valueType
The GenValueType of the instance data to set as a ratio.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Retrieving Text | Next: 4.5 Text Filters for the GenValue


Text Filters for the GenValue

GenValue: 4.5 Supplemental Usage: Text Filters for the GenValue

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Using Value Ratios | Next: 4.6 Using Ranges in GenValues
MSG_GEN_VALUE_GET_TEXT_FILTER

One of the GenValue's components is a text field to enter values into. When this visual component is being built, the GenValue will send MSG_GEN_VALUE_GET_TEXT_FILTER to itself to set up a text filter. A text filter allows a textual object to accept or reject certain characters on a number of bases. The default VisTextFilter provides numeric-only filtering with no spaces and no tabs on GVDF_INTEGER or GVDF_DECIMAL display formats; no tabs on distance unit display formats.

You may set up your own filter by intercepting this message and returning a VisTextFilter of your own choosing.

MSG_GEN_VALUE_GET_TEXT_FILTER

VisTextFilter MSG_GEN_VALUE_GET_TEXT_FILTER();

This message retrieves the text filtering in use on a GenValue object. By default, GenValues use numeric-only filtering with no spaces and no tabs for numbers and a filtering of no tabs for distance units.

Source: Unrestricted. This message is normally sent by a GenValue to itself when building its textual component.

Destination: Any GenValue object.

Parameters: None.

Return: VisTextFilter in use by the textual portion of the GenValue.

Interception: Usually, you will want to intercept this message if you subclass GenValueClass and provide your own custom filtering.

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Using Value Ratios | Next: 4.6 Using Ranges in GenValues


Using Ranges in GenValues

GenValue: 4.6 Supplemental Usage: Using Ranges in GenValues

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Text Filters for the GenValue
HINT_VALUE_DISPLAYS_RANGE, MSG_GEN_VALUE_SET_RANGE_LENGTH, MSG_GEN_VALUE_GET_RANGE_LENGTH, MSG_GEN_VALUE_ADD_RANGE_LENGTH, MSG_GEN_VALUE_SUBTRACT_RANGE_LENGTH

Special GenValue objects may actually display ranges rather than individual values within a range--for example, the GEOS scrollbars in OSF/Motif display the percentage of the document visible in the view.

For your GenValue to display a range, use HINT_VALUE_DISPLAYS_RANGE . If this hint is not present, the GenValue is assumed to have a range size of zero. This hint takes an argument of WWFixedAsDWord to indicate the size of the range.

To get the range length, use MSG_GEN_VALUE_GET_RANGE_LENGTH . To set the range, use MSG_GEN_VALUE_SET_RANGE_LENGTH . Two other messages, MSG_GEN_VALUE_ADD_RANGE_LENGTH and MSG_GEN_VALUE_SUBTRACT_RANGE_LENGTH , add or subtract the value of the GenValue by the range length.

MSG_GEN_VALUE_GET_RANGE_LENGTH

WWFixedAsDWord MSG_GEN_VALUE_GET_RANGE_LENGTH();

This message returns the value stored in HINT_VALUE_DISPLAYS_RANGE .

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The value stored in HINT_VALUE_DISPLAYS_RANGE . If this hint is not set, the return value will be zero.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_RANGE_LENGTH

void	MSG_GEN_VALUE_SET_RANGE_LENGTH(
        WWFixedAsDWord value);

This message has the effect of adding or changing the hint HINT_VALUE_DISPLAYS_RANGE for the GenValue. Setting a value of zero will cause the GenValue to act as if it did not have this hint.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: value The new range length.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VALUE_ADD_RANGE_LENGTH

void	MSG_GEN_VALUE_ADD_RANGE_LENGTH();

This message adds the range length to the current value of the GenValue object. It has the effect of incrementing the GVLI_value field by the range length.

Source: Unrestricted.

Destination: Any GenValue object.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SUBTRACT_RANGE_LENGTH

void	MSG_GEN_VALUE_SUBTRACT_RANGE_LENGTH();

This message subtracts the range length from the current value of the GenValue object. It has the effect of decrementing the GVLI_value field by the range length.

Source: Unrestricted.

Destination: Any GenValue object.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Text Filters for the GenValue


VisClass

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Introduction to VisClass
| One-page Version of this Chapter for Easy Printing

The visible classes-- VisClass , VisCompClass , VisContentClass , and VisTextClass --can be extremely useful to application developers. They provide a flexibility of display and use that is unavailable with the generic UI objects. Among other features, visible objects provide the hooks for and basic functionality of custom drawing, custom input handling, and custom geometry management.

VisClass is the parent class of all the visible classes and all visible UI objects. VisClass objects, by themselves, do nothing; VisClass must be subclassed and the objects must be placed in a visible object tree as children of a VisCompClass object. Together, VisClass and VisCompClass provide a large amount of the functionality required for creating a hierarchy of non-overlapping objects.

Many applications will use both VisClass and VisCompClass as well as VisContentClass . Before reading this chapter, however, you should be familiar with the concepts of the generic UI and with basic GEOS applications. You should have created a simple application to get familiar with the GEOS system and Goc, and you should be relatively familiar with the GenView.

You should also become familiar with both VisContentClass and VisCompClass ; both of these classes and their relationship to VisClass

1 Introduction to VisClass
2 The Visible Class Tree
3 VisClass Instance Data
3.1 VI_bounds
3.2 VI_typeFlags
3.3 VI_attrs
3.4 VI_optFlags
3.5 VI_geoAttrs
3.6 VI_specAttrs
3.7 VI_link
4 Using VisClass
4.1 Basic VisClass Rules
4.2 Drawing to the Screen
4.3 Positioning Visible Objects
4.4 Handling Input
5 Working with Visible Object Trees
5.1 Creating and Destroying
5.2 Adding and Removing
5.3 Getting Visible Tree Information
5.4 Sending Messages Through the Tree
5.5 Visible Object Window Operations
6 Visible Layers and 32-Bit Graphics
6.1 Using Visible Document Layers
6.2 Using 16-Bit Drawing Commands
6.3 The 16-Bit Limit on Visual Bounds
6.4 Handling MSG_VIS_DRAW
6.5 Managing 32-Bit Geometry
6.6 Handling Mouse Events
6.7 Setting Up the Objects
7 VisClass Error Checking
8 Creating Specific UIs
9 Basic Summary

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Introduction to VisClass
| One-page Version of this Chapter for Easy Printing


Introduction to VisClass

VisClass: 1 Introduction to VisClass

| Up: GEOS SDK TechDocs | Up | Prev: VisClass | Next: 2 The Visible Class Tree

VisClass is one of the three visible classes you'll have to have a working knowledge of in order to use visible objects. Both of the others, VisCompClass and VisContentClass , are its subclasses, and both are important in visible object trees. However, VisClass provides the heart of the visible world.

VisClass objects do nothing useful by themselves. When you subclass VisClass and add your own methods, however, the real power of the visible classes can be used without the programmer having to worry about the nuts and bolts of most UI issues. These classes inherently provide the following features:

Knowledge of Visible Bounds
Every visible object has an instance data field that defines the object's bounds in the GEOS graphic coordinate space. These bounds are used for many purposes, including drawing and input handling. See Positioning Visible Objects .
Comprehensive Visual Updating
When a visible object is included in an object tree displayed by a GenView object, the system automatically tells the object when it must redraw itself. For example, when the view scrolls or when part of the window becomes exposed, the system will send a drawing message through the visible tree; the objects then draw themselves at the proper location in the document space, and the view window is updated. See Drawing to the Screen .
Geometry Management
When visible objects are arranged as children of a VisComp or a VisContent object, they can be set up to update their size and position appropriate to the current window and view context. The VisComp and VisContent classes provide extensive and sophisticated child management functions, though custom management is also allowed and is often used. See Positioning Visible Objects , especially the messages MSG_VIS_RECALC_SIZE and MSG_VIS_POSITION_BRANCH in Geometry Management .
Object Tree Manipulation
VisClass objects can not have children, but they can be children of VisCompClass and VisContentClass objects. All of these classes support extensive tree operations including passing messages up the tree, passing messages down the tree, adding branches, removing branches, and moving branches. See Working with Visible Object Trees .
Mouse Event Handling
When an input event occurs over the GenView window in which the visible tree is displayed, that event is immediately passed on to the VisContent object. The event then travels down the visible object tree (automatically) until it is handled by some visible object. Most often, the event is handled by the leaf object with its visible bounds directly under the mouse pointer. Individual Vis objects can also grab the mouse so that they receive input events that occur even outside their bounds. See both Handling Input and the Input chapter.

Keyboard Event Handling
 A visible object can grab the keyboard input stream and receive keyboard events as they occur. Typically, certain keystrokes will be intercepted by the specific UI even if a visible object has the keyboard grab; this ensures the object won't usurp functions expected by the user from his specific UI. (The object can, however, override keystroke interception, though this is strongly discouraged.) See the Input chapter.

Interaction with Input Hierarchies
 VisClass objects have an inherent knowledge of the input hierarchies. Many visible objects may need to interact with the Focus and Target input hierarchies especially, and they can also interact with the Model hierarchy as well. See the Input chapter.

Use of VisMonikers
 Typically, visible objects will draw themselves in their entirety and will not need visual monikers. Monikers are typically labels attached to objects that get displayed either on or near the object. Visual monikers are most frequently used with generic UI objects, but they can be set up and used with visible objects as well. See Using Visual Monikers .

Standard MetaClass Functions
 Because VisClass is a subclass of MetaClass
, it inherits all the basic, standard object functions of that class (e.g., state saving, instantiation and initialization, detaching and destruction, etc.). See the MetaClass chapter.

Many programmers new to object-oriented programming may think that visible objects are used only in certain circumstances. On the contrary, visible objects may be used for nearly any purpose and to provide nearly any application-specific graphical or user interface functions. When used properly, visible objects can relieve you of hours of writing specialized code.

Programmers new to OOP may need to practice with several small applications in order to gain experience using visible objects. You can also study the sample applications, many of which use visible objects extensively.

Visible objects are most useful when you have well-defined items that must appear on the screen or interact with the user. Pieces in a board game provide a good example: Each piece has its own visual representation, and each piece has a special set of behaviors that depend on user actions and game context. Therefore, it makes sense to have each piece be an object of VisClass .

Another, less immediately obvious, example might be a program that represents maps on the screen. The map itself could be stored as a visible composite object ( VisCompClass ), and each of the major subdivisions on the map could be a visible object. For example, in a map of the United States, the country would be a VisComp object, and each state could be a Vis object. Taken one step further, each state could be a composite object, and each county in the state a visible child of the state. Another step turns the counties into composite objects and each town a visible object. This arrangement lends itself to a tree structure, which is how visible objects are stored.

Another example is a spreadsheet application. The spreadsheet itself could be one composite object containing a number of row objects. Each row object is a composite that holds a number of cell objects. Each cell is a visible object with the functions of the cell built into it. The row and spreadsheet composites can use the geometry manager to arrange the cells properly, thereby making the programmer free of ever having to worry where the cells are in the document. The visible object tree will automatically pass input events to the cell under the mouse pointer. Entire rows can be created, destroyed, drawn, erased, or moved with a few simple commands by the programmer.

| Up: GEOS SDK TechDocs | Up | Prev: VisClass | Next: 2 The Visible Class Tree


The Visible Class Tree

VisClass: 2 The Visible Class Tree

| Up: GEOS SDK TechDocs | Up | Prev: 1 Introduction to VisClass | Next: 3 VisClass Instance Data

As stated earlier, the visible class tree consists of four classes.

VisClass
This is the parent class of the visible tree. It contains the basic functionality common to every visible object and is the subject of this chapter.
VisCompClass
A subclass of VisClass , this class has the added ability to have and arrange children in the visible tree. It has sophisticated geometry management functions built in and can be used as an organizational object.
VisContentClass
A subclass of VisCompClass , this class is used only as the content of a GenView. The VisContent has default handlers for many messages sent out by the view, and it interacts with the view window to display the visible object tree. Every visible object tree, to be displayed in a GenView, must have a VisContent as its root object.
VisTextClass
This class, not discussed in this chapter, is a visible version of the GenTextClass . It provides a nearly full-featured word processing object that can be put in the visible tree.

The first three of these classes are used extensively by many applications.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Introduction to VisClass | Next: 3 VisClass Instance Data


VisClass Instance Data

VisClass: 3 VisClass Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 The Visible Class Tree | Next: 4 Using VisClass

VisClass has seven different instance data fields as shown in VisClass Instance Data . Each of these fields is discussed in detail in the following sections, and each may be set or reset during execution by sending the visible object certain messages.

Several of the instance fields will never be used by the application programmer. Most likely, you will work directly with the VI_bounds field and perhaps the VI_attrs or VI_geoAttrs fields. It's unlikely you will need to use the others unless you're doing something quite unusual.

Code Display 15-1 VisClass Instance Data

/* This is the definition of the VisClass instance data fields with some comments
 * as to their use. */
    /* VI_bounds
     * VI_bounds is a Rectangle structure defining the outermost boundaries of
     * the visible object. */
    @instance Rectangle				 VI_bounds = {0, 0, 0, 0};
    /* VI_typeFlags
     * VI_typeFlags is a record containing eight flags that determine the
     * type of the visible object. None are set by default. */
    @instance  VisTypeFlags				 VI_typeFlags = 0;
    /* Possible flags:
     * VTF_IS_COMPOSITE				VTF_IS_WINDOW
     * VTF_IS_PORTAL				VTF_IS_WIN_GROUP
     * VTF_IS_CONTENT				VTF_IS_INPUT_NODE
     * VTF_IS_GEN				VTF_CHILDREN_OUTSIDE_PORTAL_WIN */
    /* VI_attrs
     * VI_attrs is a record that contains eight attribute flags for the
     * object. Those set by default are shown. */
    @instance  VisAttrs				 VI_attrs = (VA_MANAGED | VA_DRAWABLE |
                       VA_DETECTABLE | VA_FULLY_ENABLED);
    /* Possible flags:
     * VA_VISIBLE				VA_FULLY_ENABLED
     * VA_MANAGED				VA_DRAWABLE
     * VA_DETECTABLE				VA_BRANCH_NOT_MINIMIZABLE
     * VA_OLD_BOUNDS_SAVED				VA_REALIZED */
    /* VI_optFlags
     * VI_optFlags is a record containing eight window update flags.
     * The flags set by default are shown. */
    @instance  VisOptFlags				 VI_optFlags = (VOF_GEOMETRY_INVALID |
                    VOF_GEO_UPDATE_PATH | VOF_WINDOW_INVALID |
                    VOF_WINDOW_UPDATE_PATH |
                    VOF_IMAGE_INVALID |
                    VOF_IMAGE_UPDATE_PATH);
    /* Possible flags:
     * VOF_GEOMETRY_INVALID				VOF_GEO_UPDATE_PATH
     * VOF_IMAGE_INVALID				VOF_IMAGE_UPDATE_PATH
     * VOF_WINDOW_INVALID				VOF_WINDOW_UPDATE_PATH
     * VOF_UPDATE_PENDING				VOF_EC_UPDATING */
    /* VI_geoAttrs
     * VI_geoAttrs is a record of eight geometry management flags. */
    @instance  VisGeoAttrs				 VI_geoAttrs = 0;
    /* Possible flags:
     * VGA_GEOMETRY_CALCULATED				VGA_NO_SIZE_HINTS
     * VGA_NOTIFY_GEOMETRY_VALID				VGA_DONT_CENTER
     * VGA_USE_VIS_SET_POSITION				VGA_USE_VIS_CENTER
     * VGA_ALWAYS_RECALC_SIZE				VGA_ONLY_RECALC_SIZE_WHEN_INVALID
    /* VI_specAttrs
     * VI_specAttrs is a record containing eight flags used when the visible
     * object is part of a specific UI library. This field is rarely if ever
     * used by applications. */
    @instance  SpecAttrs				 VI_specAttrs = 0;
    /* Possible flags:
     * SA_ATTACHED				SA_REALIZABLE
     * SA_BRANCH_MINIMIZED				SA_USES_DUAL_BUILD
     * SA_CUSTOM_VIS_PARENT				SA_SIMPLE_GEN_OBJ
     * SA_CUSTOM_VIS_PARENT_FOR_CHILD
     * SA_TREE_BUILT_BUT_NOT_REALIZED */
    /* VI_link
     * VI_link contains an object pointer to the visible object's next sibling
     * in the tree. Note that VisClass has no corresponding @composite field;
     * if the object is to have children, it must be of class VisCompClass.
    @instance @link				 VI_link;

VI_bounds

VI_typeFlags

VI_attrs

VI_optFlags

VI_geoAttrs

VI_specAttrs

VI_link

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 The Visible Class Tree | Next: 4 Using VisClass


VI_bounds

VisClass: 3.1 VisClass Instance Data: VI_bounds

| Up: GEOS SDK TechDocs | Up | Prev: 3 VisClass Instance Data | Next: 3.2 VI_typeFlags

The VI_bounds field is a Rectangle structure containing four values: The left, top, right, and bottom outer bounds of the object. The bounds of a visible object describe the object to the rest of the world: the input manager and UI use them to determine when the mouse pointer is over the object, and the object's visible parent (if any) uses them to figure out the total geometry of all its children.

The VI_bounds field is very important to nearly all visible objects. In fact, Positioning Visible Objects is devoted to this field and how it is used. For full information on visible bounds, see that section.

| Up: GEOS SDK TechDocs | Up | Prev: 3 VisClass Instance Data | Next: 3.2 VI_typeFlags


VI_typeFlags

VisClass: 3.2 VisClass Instance Data: VI_typeFlags

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 VI_bounds | Next: 3.3 VI_attrs
VI_typeFlags, MSG_VIS_SET_TYPE_FLAGS, MSG_VIS_GET_TYPE_FLAGS

The VI_typeFlags field is a bitfield record containing eight flags. These flags determine the type of the visible object and are used primarily by the UI and the windowing system. When used at all, these flags are typically set in the object's definition or when the object is first created, and they are rarely changed dynamically by an application. The eight flags in this record are listed below. The record is of type VisTypeFlags .

VTF_IS_COMPOSITE
This flag is set if the object is of VisCompClass rather than VisClass . This flag indicates the object may have children (normal VisClass objects can not have children). Because this is not the only difference between these classes, changing this flag can have unpredictable results.
VTF_IS_WINDOW
This flag is set if the object creates a window via the window system in order to display itself and its children. This flag implies the object must also have VTF_IS_COMPOSITE set, meaning the object must actually be of VisCompClass and not VisClass . If this flag is set, the UI will assume that the VI_bounds field of the object represents the current size of the window. Objects with the VTF_IS_WINDOW flag set are skipped by the normal input and output messages such as MSG_VIS_DRAW and MSG_META_START_SELECT ; these messages will be passed directly to the object's appropriate child. VTF_IS_WINDOW and VTF_IS_PORTAL are mutually exclusive.
VTF_IS_PORTAL
This flag is set if the object is a portal. A portal is an object that has its own window and appears in its parent's window. A portal differs from a window (VTF_IS_WINDOW) in that the window has no part of itself drawn in its parent window. This flag implies that the VTF_IS_COMPOSITE flag must also be set indicating the object may have visible children. All visible children of a portal will appear in the portal's associated window if VTF_CHILDREN_OUTSIDE_PORTAL_WIN is set. The object itself may display portions of itself within its parent's window, though; an example of this is the UI's implementation of the GenDisplayControl object, which manages several child windows within its own window area. Its border is drawn in the parent window, then its window is defined to be one pixel inside this border. VTF_IS_PORTAL and VTF_IS_WINDOW are mutually exclusive.
VTF_IS_WIN_GROUP
This flag is set for the top visible object in a visible branch which makes that branch a realizable entity. This flag implies that both VTF_IS_COMPOSITE and VTF_IS_WINDOW must be set as well. An example would be the specific UI's implementation of a GenPrimary object--if the primary is not visible, none of its children may be visible (with some exceptions).
VTF_IS_CONTENT
This flag is set if the object is the output of a window object. Typically, this will be set only if the object is of VisContentClass (not for VisClass or VisCompClass ). Note that if this is set, both VTF_IS_WINDOW and VTF_IS_WIN_GROUP must also be set.
VTF_IS_INPUT_NODE
This flag is an optimization bit. It is set if the object controls input flow (such as a VisContent) or is a target or focus node that is not a window group (VTF_IS_WIN_GROUP). MSG_VIS_VUP_ALTER_INPUT_FLOW is sent directly to objects with this bit set unless there is a need for them to actually use the VUP mechanism, as is the case with mouse grabs in a 32-bit content. Similarly, MSG_META_MUP_ALTER_FTVMC_EXCL , MSG_VIS_FUP_QUERY_FOCUS_EXCL , and MSG_VIS_VUP_QUERY_FOCUS_EXCL are sent to the first visible object up the tree that either is a window group or has this bit set.
VTF_IS_GEN
This flag is set if the visible object is the specific UI counterpart to a generic object. This flag must be set for the object to handle messages that begin MSG_SPEC . The only objects that will ever have this flag set are those in Specific UI libraries.
VTF_CHILDREN_OUTSIDE_PORTAL_WIN
This flag is set if children of a portal object may appear in the object's parent window rather than in the portal's specially-created window. The VTF_IS_PORTAL flag must also be set for this object. An example of this is the specific UI's representation of a GenView object and its children; the view's children appear along the edges of the view window rather than inside it. The GenDisplayControl, by contrast, has all its children appearing within its window; thus, it does not have this flag set.

Although the VI_typeFlags record will rarely be changed at run-time, you can set new values by sending MSG_VIS_SET_TYPE_FLAGS to the visible object while it is realized. To retrieve the current type flags, send the object a MSG_VIS_GET_TYPE_FLAGS .

MSG_VIS_SET_TYPE_FLAGS

void	MSG_VIS_SET_TYPE_FLAGS(
        VisTypeFlags		flagsToSet,
        VisTypeFlags		flagsToClear);

This message causes the object to set its VI_typeFlags field to the values passed. The flagsToSet parameter will be OR-ed with VI_typeFlags , and the flagsToClear parameter will be logically inverted and then AND-ed with the field. The clear operation will occur after the set. Therefore a flag set in both parameters will be cleared by this message.

Source: Unrestricted.

Destination: Any visible object; however, the object must not be realized when it receives the object.

Parameters: flagsToSet A record of VisTypeFlags indicating which flags are to be set for the object.

flagsToClear
A record of VisTypeFlags indicating which flags are to be cleared for the object (a flag set in flagsToClear will be cleared in VI_typeFlags ).

Return: Nothing.

Interception: Unlikely.

MSG_VIS_GET_TYPE_FLAGS

VisTypeFlags MSG_VIS_GET_TYPE_FLAGS();

This message returns a visible object's current VI_typeFlags field.

Source: Unrestricted.

Destination: Any visible object.

Parameters: None.

Return: A byte of VisTypeFlags representing the object's current VI_typeFlags field.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 VI_bounds | Next: 3.3 VI_attrs


VI_attrs

VisClass: 3.3 VisClass Instance Data: VI_attrs

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 VI_typeFlags | Next: 3.4 VI_optFlags
VI_attrs, MSG_VIS_GET_ATTRS, MSG_VIS_SET_ATTRS

The VI_attrs field is a record of eight flags ( VisAttrs )that determine the general visible attributes of the object. These attributes may be changed on the fly and often are. Applications that don't involve visible tree operations (adding, removing, or moving branches) probably will not change these attributes dynamically.

To set the flags after the object has been instantiated, send it a MSG_VIS_SET_ATTRS with the appropriate flags to be set. To retrieve the VI_attrs record, send the object a MSG_VIS_GET_ATTRS . The VA_REALIZED flag may not be changed with MSG_VIS_SET_ATTRS .

The eight flags of the VisAttrs record are listed below:

VA_VISIBLE
This flag is effective only for window group objects (those with VTF_IS_WIN_GROUP set in VI_typeFlags ) and is ignored for other objects. If set, it indicates that the visible branch headed by this object can be made visible as soon as it is linked visibly to another visible branch. For example, a window group object may have this bit set and not be on the screen if its parent window group is not set VA_VISIBLE; as soon as the parent window group becomes VA_VISIBLE, though, the entire branch will become visible.
VA_FULLY_ENABLED
This flag is normally set for visible objects. If it is set, the object is fully enabled when visible on the screen. If not set, the object is not fully enabled--the object will typically be drawn in a 50% pattern (grayed out) and will not receive click events. This flag works even for normal visible objects, though this behavior is seen almost exclusively with generic UI objects.
VA_MANAGED
This flag indicates that the object's location is managed by its parent. If the flag is set, space is set aside in the parent composite by the geometry manager. When the window displaying this object changes, the geometry will have to be calculated. If this flag is not set, the object can alter its own location and bounds without affecting the parent's geometry.
VA_DRAWABLE
This flag indicates whether the object is drawn or invisible. If the flag gets set or reset during execution, the rectangle defined by its VI_bounds field will automatically be marked invalid.
VA_DETECTABLE
This flag, when set, indicates that the object might respond to mouse, pointer, keyboard, or other similar events. If the flag is reset (such as with display-only objects), its parent composite object will not bother passing it these events. This flag must be set if the object is a window composite. If the status of this flag is changed during execution, the object's grab state will not change. For example, if the object has the mouse grab when a MSG_VIS_SET_ATTRS resets the flag, the object will retain the mouse grab until it is released or preempted normally.
VA_BRANCH_NOT_MINIMIZABLE
This flag is used only by the visible counterparts of generic UI objects. If set, it indicates that this branch (e.g., a GenInteraction and its children) stays visible and usable even when the parent primary window is minimized.
VA_OLD_BOUNDS_SAVED
This flag, when set, causes the geometry manager to store the object's old bounds when its VI_bounds field changes. The old bounds are stored in a TEMP_VIS_OLD_BOUNDS vardata field.
VA_REALIZED
This flag indicates whether the object is visibly open on the screen--i.e., whether it is positioned on an open window. If the flag is set, the object is visible on the screen. When the object is no longer visible on the screen, this flag is reset. This flag may not be altered by MSG_VIS_SET_ATTRS ; it may only be changed by MSG_VIS_OPEN (which sets the flag) and MSG_VIS_CLOSE (which resets it).

MSG_VIS_SET_ATTRS

void	MSG_VIS_SET_ATTRS(
        VisAttrs		attrsToSet,
        VisAttrs		attrsToClear,
        VisUpdateMode		updateMode);

This message sets the object's VI_attrs field to the values passed. The attrsToSet parameter will be OR-ed with VI_attrs , and the attrsToClear parameter will be logically inverted and then AND-ed with the field. The clear operation will occur after the set, and therefore a flag set in both parameters will be cleared. The updateMode parameter indicates when the visual update of the object should occur after the new attributes have been set.

Source: Unrestricted.

Destination: Any visible object.

Parameters: attrsToSet A record of VisAttrs indicating which flags are to be set for the object.

attrsToClear
A record of VisAttrs indicating which flags are to be cleared for the object (a flag set in attrsToClear will be cleared in VI_attrs ).
updateMode
A VisUpdateMode indicating when the object should be visually updated on the screen.

Return: Nothing.

Interception: Unlikely.

Warnings: Not all flags in the VI_attrs record may be set with this message.

MSG_VIS_GET_ATTRS

VisAttrs	MSG_VIS_GET_ATTRS();

This message returns a visible object's current VI_attrs field.

Source: Unrestricted.

Destination: Any visible object.

Parameters: None.

Return: A byte of type VisAttrs representing the current flags set in the object's VI_attrs field.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 VI_typeFlags | Next: 3.4 VI_optFlags


VI_optFlags

VisClass: 3.4 VisClass Instance Data: VI_optFlags

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 VI_attrs | Next: 3.5 VI_geoAttrs
VI_optFlags, MSG_VIS_GET_OPT_FLAGS

The VI_optFlags field is a VisOptFlags record containing eight flags. These flags are set and used by the geometry manager to mark which objects need to be updated and how. The flags can not be set individually by applications; however, they can be retrieved with the message MSG_VIS_GET_OPT_FLAGS . It is unlikely that your code will ever access these flags directly. The eight flags in this field are listed below.

VOF_GEOMETRY_INVALID
This flag indicates that the general geometry of this object has become invalid (with a MSG_VIS_MARK_INVALID ). This is used primarily when an object knows that its bounds must be recalculated or re-verified and that any change must be reflected in its parent window. This flag will be reset when a geometry update resolves the object's bounds. Typically, this will occur with a window update message such as MSG_VIS_UPDATE_WIN_GROUP .
VOF_GEO_UPDATE_PATH
This flag, also set by MSG_VIS_MARK_INVALID , marks a trail of objects that all must have their geometry updated.
VOF_IMAGE_INVALID
This flag indicates that the image of the object or the bounds of a windowed object have changed and therefore need to be updated. This flag is set by MSG_VIS_MARK_INVALID . The image redraw will occur the next time one of the window update messages is used on the object's visual branch. This flag will be reset when a visual update redraws the object. Typically, this will occur with MSG_VIS_UPDATE_WIN_GROUP or another window update message.
VOF_IMAGE_UPDATE_PATH
This flag, also set by MSG_VIS_MARK_INVALID , marks a trail of objects that must have their images updated.
VOF_WINDOW_INVALID
This flag indicates that the window the object resides in needs to be opened or closed, or that the object itself needs to be opened or closed via MSG_VIS_OPEN or MSG_VIS_CLOSE . It is set by MSG_VIS_MARK_INVALID . This flag will be reset when a visual update occurs on the window group. Typically, this will occur with a window update message such as MSG_VIS_VUP_UPDATE_WIN_GROUP . (This message should be called whenever an object is added to a visible tree; it ensures the object will be properly opened.)
VOF_WINDOW_UPDATE_PATH
This flag, also set by MSG_VIS_MARK_INVALID , marks a trail of window objects that all must have their windows and open/closed state updated.
VOF_UPDATE_PENDING
This flag indicates that the object has an unprocessed MSG_VIS_UPDATE_WIN_GROUP in its event queue.
VOF_EC_UPDATING
This flag is used by the VisClass error-checking code only. It indicates that a visible branch is currently being updated; the system checks it to make sure nested updates do not occur.

MSG_VIS_GET_OPT_FLAGS

VisOptFlags MSG_VIS_GET_OPT_FLAGS();

This message returns the current VI_optFlags field of the object.

Source: Unrestricted.

Destination: Any visible object.

Parameters: None.

Return: A byte record of VisOptFlags reflecting the current settings in the object's VI_optFlags field.

Interception: Unlikely.

See Also: MSG_VIS_MARK_INVALID.

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 VI_attrs | Next: 3.5 VI_geoAttrs


VI_geoAttrs

VisClass: 3.5 VisClass Instance Data: VI_geoAttrs

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 VI_optFlags | Next: 3.6 VI_specAttrs
VI_geoAttrs, MSG_VIS_SET_GEO_ATTRS, MSG_VIS_GET_GEO_ATTRS

The VI_geoAttrs field is a VisGeoAttrs record of eight flags that determine the state of the object's geometry. They can be set and reset during execution to dynamically change the geometry behavior of the object. None of the flags is set by default. To set the flags, send the object a MSG_VIS_SET_GEO_ATTRS . The flags can be retrieved with a MSG_VIS_GET_GEO_ATTRS . The eight flags in this record are listed below.

VGA_GEOMETRY_CALCULATED
This flag indicates whether the object has ever been calculated. If the flag is clear, the object's geometry is about to be calculated. An object may use this flag if it has a particular desired size. This flag is cleared before a geometry recalculation by MSG_VIS_RESET_TO_INITIAL_SIZE .
VGA_NO_SIZE_HINTS
This flag is used by the Specific UI library to determine whether the object has any sizing hints or not. If the flag is not set, the object has one or more sizing hints (e.g., HINT_INITIAL_SIZE or HINT_FIXED_SIZE ).
VGA_NOTIFY_GEOMETRY_VALID
If this flag is set, the geometry manager will notify the object when its geometry messages have all been handled and the object's geometry is set. Notification will be in the form of MSG_VIS_NOTIFY_GEOMETRY_VALID . This flag may be used by visual objects for calculations or other sizing once geometry is redone but before redrawing occurs.
VGA_DONT_CENTER
This flag, when set, allows the object to individually override its parent's centering behavior. The object will instead appear at either the left edge of a vertical composite or the top edge of a horizontal composite.
VGA_USE_VIS_SET_POSITION
This flag is set to indicate that the object does not use the default handlers for MSG_VIS_SET_POSITION and MSG_VIS_POSITION_BRANCH . This flag provides an optimization that allows static calls to the geometry manager.
VGA_USE_VIS_CENTER
If this flag is set, the geometry manager will use the standard VisClass or VisCompClass center message to calculate the object's center. Unless an object is using some unusual centering behavior, it should have this flag set. This flag allows the geometry manager to speed up calculations for normal center behavior.
VGA_ONLY_RECALC_SIZE_WHEN_INVALID
This flag indicates that the object's size should be recalculated only when its geometry is marked invalid. That is, once the object's geometry is calculated, it will always return the same size until its geometry is once again marked invalid. Because the geometry manager often queries objects for their sizes, most objects should have this flag set. It allows the geometry manager to speed up calculations.
VGA_ALWAYS_RECALC_SIZE
This flag avoids optimizations that do not recalculate the object's size when passed its current size. Though not often used, this flag may be needed in certain situations (e.g., when a composite object must expand to fit and center its children).

MSG_VIS_SET_GEO_ATTRS

void	MSG_VIS_SET_GEO_ATTRS(
        VisGeoAttrs		attrsToSet,
        VisGeoAttrs		attrsToClear,
        VisUpdateMode		updateMode);

This message causes the object to set its VI_geoAttrs field to the values passed. The attrsToSet parameter will be OR-ed with VI_geoAttrs , and the attrsToClear parameter will be logically inverted and then AND-ed with the field. The clear operation will occur after the set, and therefore a flag set in both parameters will end up cleared. Many of the attributes in this record are used only by Specific UI libraries and should not be altered by applications.

Source: Unrestricted.

Destination: Any visible object.

Parameters: attrsToSet A record of VisGeoAttrs indicating which flags are to be set for the object.

attrsToClear
A record of VisGeoAttrs indicating which flags are to be cleared for the object (a flag set in attrsToClear will be cleared in VI_geoAttrs ).
updateMode
A VisUpdateMode indicating when the object should be visually updated on the screen.

Return: Nothing.

Interception: Unlikely.

MSG_VIS_GET_GEO_ATTRS

VisGeoAttrs MSG_VIS_GET_GEO_ATTRS();

This message returns a visible object's current VI_geoAttrs field.

Source: Unrestricted.

Destination: Any visible object.

Parameters: None.

Return: A record of type VisGeoAttrs reflecting the flags currently set in the object's VI_geoAttrs field.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 VI_optFlags | Next: 3.6 VI_specAttrs


VI_specAttrs

VisClass: 3.6 VisClass Instance Data: VI_specAttrs

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 VI_geoAttrs | Next: 3.7 VI_link
VI_specAttrs, MSG_SPEC_SET_ATTRS, MSG_SPEC_GET_ATTRS

The VI_specAttrs field is a record of eight flags. This field is accessed only by objects that are part of a Specific UI library. Applications will almost never access this field or use its flags. Documentation on creating specific UI libraries may be published under separate cover.

MSG_SPEC_SET_ATTRS

void	MSG_SPEC_SET_ATTRS(
        SpecAttrs		attrsToSet,
        SpecAttrs		attrsToClear,
        VisUpdateMode		updateMode);

This message sets the current VI_specAttrs field to the values passed. A flag set in both attributes parameters will be cleared.

Source: Unrestricted.

Destination: Any visible object used as a specific UI object.

Parameters: attrsToSet A record of SpecAttrs indicating which flags are to be set in the object's VI_specAttrs field.

attrsToClear
A record of VisAttrs indicating which flags are to be cleared for the object (a flag set in attrsToClear will be cleared in VI_specAttrs ).
updateMode
A VisUpdateMode indicating when the object should be visually updated on the screen.

Return: Nothing.

Interception: Unlikely.

MSG_SPEC_GET_ATTRS

SpecAttrs MSG_SPEC_GET_ATTRS();

This message returns a visible object's current VI_specAttrs field. This field is used only by Specific UI libraries and is meaningful only for objects in these libraries.

Source: Unrestricted.

Destination: Any visible object that is used as a specific UI object.

Parameters: None.

Return: A byte of type SpecAttrs indicating which flags are set in the object's VI_specAttrs field.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 VI_geoAttrs | Next: 3.7 VI_link


VI_link

VisClass: 3.7 VisClass Instance Data: VI_link

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 VI_specAttrs | Next: 4 Using VisClass
VI_link

The VI_link field holds the optr of the visible object's next sibling in the visible object tree. This field may not be accessed directly by applications but may be set or changed with the various visible tree messages. For full information on how to build and manipulate visible object trees, see Working with Visible Object Trees .

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 VI_specAttrs | Next: 4 Using VisClass


Using VisClass

VisClass: 4 Using VisClass

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 VisClass Instance Data | Next: 5 Working with Visible Object Trees

As stated earlier, visible objects are nearly useless unless you subclass one of the visible classes and handle certain messages. Although there's a lot of functionality built into VisClass and its subclasses, they must be customized to be of any use. In addition, visible objects must be put into a visible tree and connected to a GenView or other windowed UI object to be displayed.

Basic VisClass Rules

Drawing to the Screen

Positioning Visible Objects

Handling Input

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 VisClass Instance Data | Next: 5 Working with Visible Object Trees


Basic VisClass Rules

VisClass: 4.1 Using VisClass: Basic VisClass Rules

| Up: GEOS SDK TechDocs | Up | Prev: 4 Using VisClass | Next: 4.2 Drawing to the Screen

To use visible objects, you must subclass the Vis classes and create a visible object tree. Otherwise, the visible objects will not be able to display themselves or accept user input, the two things that make visible objects worthwhile.

Nearly all visible objects you use will at least display themselves on the screen. Some may accept mouse or keyboard input; some may move themselves around the screen, resize themselves, or implement custom geometry management functions.

For a subclass of VisClass to display itself on the screen, it must handle the message MSG_VIS_DRAW . This message is sent to all visible objects in a given window when that window is subjected to an exposure event. In addition, the object must be part of a tree of visible objects, the top node of which is connected to a GenView or other windowed object. Displaying a basic visible object is discussed below.

For a visible object to handle mouse input, it will need to handle a subset of the mouse event messages. Typically, a visible object will want to know when the mouse is clicked within the object's bounds ( MSG_META_START_SELECT ) and when the mouse button is released ( MSG_META_END_SELECT ). Other messages dealing with mouse motion or other mouse buttons may also be of interest to the object. In addition, visible objects may request and handle Ink input. Handling of these messages is discussed in Handling Input .

Most applications using visible object trees will add objects to and remove them from the trees. MSG_VIS_ADD_CHILD and MSG_VIS_REMOVE are the two messages used most often for tree management. When an object should be freed, it can be destroyed with MSG_VIS_DESTROY , which will remove it from the tree and then free it.

Some applications will want to use the built-in geometry management features of GEOS. The geometry manager can automatically resize and reposition an entire visible object tree properly according to pre-set constraints. (The constraints can also be changed at run-time.) How the system manages visible object geometry is discussed in Geometry Management .

The geometry manager, though extremely useful for non-overlapping objects, may not be sufficient for all the needs of a complex application. For an object to determine its own size and position, it has to handle some of the messages sent out by the geometry manager. You can also manually set the size and position of each visible object. This is discussed in Positioning Visible Objects .

Often applications may need to change something about a visible object or the tree it's in. When this happens, the application must force a visual update by invalidating either the geometry or the image (or both) of the object and then calling MGS_VIS_VUP_UPDATE_WIN_GROUP . To mark any object invalid, the application must call MSG_VIS_MARK_INVALID .

Many visible objects will have specific functions they perform. For example, if the user presses on a menu item labeled "New Game" or something similar, the visible object may return itself to its original location (as in the TicTac sample application). To get this type of functionality, you must define new messages for your subclass of VisClass and have the object handle them. In the case of the TicTac game pieces, each piece handles the MSG_PIECE_NEW_GAME message by resetting the object's position to its original position.

Visible objects are maintained and managed in a tree structure. The tree has three basic elements: The root (topmost) node must be an object of VisContentClass . Any nodes in the middle of the tree, nodes that are allowed to have children, must be of VisCompClass . Any leaf nodes (guaranteed not to have children) may be of either VisClass or VisCompClass . (Subclasses of the above classes may also be used.)

The visible object tree is displayed normally through a GenView object. The output of the GenView must be tied directly to the top node of the visible object tree; this is why the visible tree must be headed by a VisContent--only the VisContent has the methods to handle GenView messages.

The VisContent object may or may not do things other than manage the visible tree. For example, the content may draw some background graphics to the window before the other visible objects draw themselves, or it may implement some special geometry behavior to arrange its children.

The VisContent can have children. These children can be either standard visible objects or composite visible objects. Composites, like the content, are allowed to have children. For efficiency, standard visible objects can not have children. Therefore, only leaf objects in the tree may be of VisClass .

The visible object tree may have any number of layers of composites. You should pay attention to the functions required in your application and fit the structure of the tree to the application's needs.

These restrictions, combined with the link to the generic object tree through GenView, result in an object tree structure somewhat like that shown in the figure above. Of course, the actual generic and visible tree structure will vary from application to application. Also, note that the generic and visible sections are actually two different object trees; they are not connected through a parent-child relationship anywhere.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Using VisClass | Next: 4.2 Drawing to the Screen


Drawing to the Screen

VisClass: 4.2 Using VisClass: Drawing to the Screen

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Basic VisClass Rules | Next: 4.3 Positioning Visible Objects

One of the main features of visible objects is their ability to draw themselves on the screen. The visible tree does not have to detect when drawing or redrawing should be done; the UI will do this automatically through the window system and the GenView object.

Visual Updates

MSG_VIS_OPEN, MSG_VIS_CLOSE

The visible tree will have to draw or redraw itself when a portion of its GenView window becomes newly exposed. The GenView keeps track of its window and will notify its content when any portion of the window becomes exposed. Exposure can occur from several events: The view window could be newly created; another window could have been moved, exposing the view's window; or the view window could have been scrolled.

Each of these exposure events looks exactly the same to the visible tree. When one of them occurs, the GenView will send a MSG_META_EXPOSED to its content object. The content (a VisContent or GenDocument if you're using a visible tree) will create a GState for the window and translate the MSG_META_EXPOSED into a MSG_VIS_DRAW . It will then send this MSG_VIS_DRAW to itself.

The default handler for MSG_VIS_DRAW in a composite is to simply pass the message on to all of the composite's children. There is no default behavior for MSG_VIS_DRAW in VisClass .

Any object (content, composite, or leaf) that wants to represent itself on the screen must handle MSG_VIS_DRAW . Any composite that subclasses this message must be sure to pass it on to all of its children with the following line:

@send @visChildren:: MSG_VIS_DRAW(drawFlags, gstate);

Redrawing is not initiated only by the view window becoming exposed. It can also be initiated by some portion of the visible tree changing so that it needs to be redrawn (e.g., a child is moved from one composite to another, which may have an effect on the visual representation of the composite). Typically, any change that necessitates a redrawing will automatically generate a MSG_VIS_DRAW to the appropriate objects. These operations, however, will require a VisUpdateMode to be passed along with the other parameters.

All of the above assumes that the visible object is "open" or "realized." If an object is not open, it will not appear on the screen or as part of a composite's geometry calculations. When a window is first created, the window's content is automatically opened with MSG_VIS_OPEN . In the case of a visible tree attached to a GenView, this means that the VisContent object will be opened along with the window. All of the VisContent's children will also be opened automatically (assuming their VOF_WINDOW_INVALID bits are set, which is true the first time a visible object comes up).

If a visible object is added to an already-opened composite, the child will automatically be opened and marked invalid. This will cause the composite's geometry and image to be updated during the next visual update. If a child is added to a composite that is not currently open, the child will not be opened.

Although it is rarely done directly, you can manually open a visible object with the message MSG_VIS_OPEN , and you can "close" an object (remove it from the screen and from the tree's geometry calculations) with MSG_VIS_CLOSE . To open or close an entire branch of the visible tree, you can send the appropriate message to the top composite object in the branch. The message will propagate down through all the visible children.

MSG_VIS_OPEN

void	MSG_VIS_OPEN(
        WindowHandle window);

This message is part of the visual update mechanism and will be sent by the system when an object must be visually updated to appear on the screen. It will be sent to any VOF_WINDOW_INVALID objects in a branch whose VTF_IS_WIN_GROUP object has become VA_VISIBLE. Any object added into a visible tree having VOF_WINDOW_INVALID set will receive a MSG_VIS_OPEN at the next visual update. This message propagates down the visible tree and does not cross over boundaries of window groups. This message is often subclassed by objects that want to initialize some information before being drawn on the screen. If an object subclasses this message, it should be sure to call its superclass somewhere in the handler.

Source: Visual update mechanism.

Destination: Any visible object.

Parameters: window Handle of window in which the object is to appear, or zero if it should appear in the top window object.

Return: Nothing.

Interception: Will be intercepted by objects that want to initialize certain data or take certain actions before being drawn on the screen. Any intercepting objects must call the superclass somewhere in their handler.

MSG_VIS_CLOSE

void	MSG_VIS_CLOSE();

This message is part of the visual update mechanism and is sent to objects that are being taken off the screen. The system will send this message to any objects in a visual branch whose VTF_IS_WIN_GROUP object is set not visible (~VA_VISIBLE). This message closes appropriate windows and propagates to all children to the bottom leaf objects of the visible branch. This message will not cross window group boundaries; if a lower VTF_IS_WIN_GROUP object is encountered during the message propagation, an error will likely occur. Therefore, lower window groups must be closed before higher window groups may be closed.

Source: Visual update mechanism.

Destination: Any visible object.

Interception: Will be intercepted by objects that want to do something as it is being taken off the screen. Any objects that intercept the message must call their superclasses in the handlers.

VisUpdateMode

VisUpdateMode is an enumerated type that determines when and how the visible tree will be visually updated when its image or geometry becomes invalid. This type has four enumerations, described below:

VUM_MANUAL
This mode indicates that the UI should not update at all after this change. Instead, the visual update will be initiated manually later on.
VUM_NOW
This mode indicates that the visual update should occur immediately after the change is effected.
VUM_DELAYED_VIA_UI_QUEUE
This mode indicates that the visual update should occur only when the UI queue is completely empty (indicating no further changes are coming).
VUM_DELAYED_VIA_APP_QUEUE
This mode indicates that the visual update should occur only when the application's queue is empty (indicating that no more changes are coming). This mode is very useful for making several changes that each require an update; it will put the updating off until the very end and will cause only a single update.

A VisUpdateMode is passed as a parameter to certain Vis and Gen messages. These messages typically have some effect on the visual representation of the objects--either on the geometry or on the image itself.

The Initial GState

 MSG_VIS_VUP_CREATE_GSTATE

For any drawing to occur, a graphic state must exist. The graphic state typically is associated with the GenView's window and is created automatically, either when the window is first realized on the screen or when an exposure event occurs.

To create a GState, the system generates a MSG_VIS_VUP_CREATE_GSTATE and sends it to the object that first requires a visual update. This message travels up the visible tree until it gets to a window group (an object with its VTF_IS_WINDOW flag set), and then it creates a GState associated with that window object. Sometimes a content object will subclass this message to set up its own display information before the GenView window can create the new GState. The other time in which this message may be subclassed is by a content that manages a 32-bit graphics space; the content will have to translate the gstate to the proper position in the large document before letting other objects use it.

MSG_VIS_VUP_CREATE_GSTATE

GStateHandle MSG_VIS_VUP_CREATE_GSTATE();

This message travels up the visible object tree until it reaches either the root object or a window group object. It then creates a graphic state associated with that object; this graphic state is then used for the subsequent visual update. You may wish to subclass this message in order to alter the GState created or to set it up to take 32-bit coordinates. If you subclass it, however, be sure to call the superclass somewhere in the handler.

Source: Unrestricted--typically sent by a visible object to itself.

Destination: Any visible object--typically sent by a visible object to itself.

Parameters: None.

Return: The GStateHandle of the newly-created GState.

Interception: Unlikely--VisContent objects may intercept this message to translate the returned GState in a 32-bit graphics space. In this case, the VisContent must first call its superclass, then translate the GState appropriately before returning.

Retrieving the Current Window Handle

MSG_VIS_QUERY_WINDOW

Often a visible object will require the window handle of the window it's currently residing in. Some objects, for example, will want to force visual updates in real-time, and they can do this by using the current window handle, creating a special GState for it, and drawing to that GState. The visible object can either cache the window handle when the window is first opened, or it can retrieve it with MSG_VIS_QUERY_WINDOW .

MSG_VIS_QUERY_WINDOW

WindowHandle MSG_VIS_QUERY_WINDOW();

This message returns the window handle of the window the object currently resides in. In many cases, this will be the window handle of a GenView window.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to retrieve its current window handle.

Parameters: None.

Return: The window handle of the object's current window.

Interception: Unlikely.

See Also: MSG_GEN_VIEW_GET_WINDOW.

Causing Redrawing

MSG_VIS_BOUNDS_CHANGED, MSG_VIS_REDRAW_ENTIRE_OBJECT, MSG_VIS_INVALIDATE, MSG_VIS_MARK_INVALID, MSG_VIS_INVAL_TREE, MSG_VIS_ADD_RECT_TO_UPDATE_REGION

If your visible tree contains a composite that manages its own children, chances are you'll need to force a visual update after certain changes occur. For example, if you have an object that can be moved with the mouse, it will probably want to erase its image and redraw itself somewhere else after the move; since the geometry manager is not involved here, the object must force a redrawing of the affected portion of the visible tree.

There are several ways an object can force a redraw. These all take the form of messages, each of which is described below.

MSG_VIS_DRAW
This message causes the entire visible branch to draw itself. This message may be sent by any object to any other object, including itself. It does not, however, update the screen image; you must call MSG_VIS_VUP_UPDATE_WIN_GROUP .
MSG_VIS_BOUNDS_CHANGED
This message causes the visible object to mark the affected portions invalid, causing the visual update mechanism to redraw that portion during the next visual update. This message is part of the visible update mechanism.
MSG_VIS_REDRAW_ENTIRE_OBJECT
This message causes the visible object to redraw itself entirely (as opposed to just the invalid portion) if it is currently drawable. Essentially, the object will send itself a MSG_VIS_DRAW when it receives this message. This is especially useful if an object needs to completely change its image.
MSG_VIS_INVALIDATE
This message invalidates the entire region inside the visible object's bounds. It will cause the window system to generate a MSG_META_EXPOSED for the affected area, causing a MSG_VIS_DRAW to propagate down the tree to all affected objects. Note that this message must be subclassed to work with scaled windows or large documents.
MSG_VIS_INVAL_TREE
This message invalidates the entire region inside the visible object's bounds, including any child window areas. The window system will generate a MSG_META_EXPOSED for all affected windows.
MSG_VIS_MARK_INVALID
This message allows the caller to set an object's invalid flags so the object will be properly updated later. The object is marked as being invalid, and the default handler ensures a visual update path is created. This method may not be subclassed by applications.
MSG_VIS_ADD_RECT_TO_UPDATE_REGION
This message adds the passed rectangular region to a window's update list. Essentially, the window group associated with the recipient object will ensure that the given rectangle gets marked invalid and included in the next visual update.

MSG_VIS_DRAW

void	MSG_VIS_DRAW(
        DrawFlags		drawFlags,
        GStateHandle		gstate);

This message causes the visible object to draw itself. The default behavior of this message for composite objects is to pass the message on to all children. There is no default behavior for VisClass for this message.

Source: Unrestricted.

Destination: Any visible object.

Parameters: drawFlags A record of type DrawFlags (defined below). The flags in this record describe what type of action initiated the MSG_VIS_DRAW .

gstate
The GStateHandle of the gstate created for this visual update. All drawing done in the handler should be for this GState.

Return: Nothing.

Interception: All visible objects that wish to draw anything to the screen must intercept this message. Composites that intercept it must be sure to send it to their children (with @send @visChildren ).

Structures: The flags in the DrawFlags record are listed below:

DF_EXPOSED
This flag indicates the MSG_VIS_DRAW is a result of a MSG_META_EXPOSED from the window system. This is the standard and most usual form of MSG_VIS_DRAW .
DF_OBJECT_SPECIFIC
This flag is used by various Specific UI objects for object-specific things. Your visible objects should not be concerned with this flag.
DF_PRINT
This flag indicates that the MSG_VIS_DRAW is a result of a MSG_META_EXPOSED_FOR_PRINT , the printing message. The GState passed will be a spooler GState rather than a window GState. If this flag is set, DF_EXPOSED will also be set.
DF_DONT_DRAW_CHILDREN
This flag indicates that composite objects should draw themselves but should not propagate the drawing message on to their children (this is nonstandard behavior).
DF_DISPLAY_TYPE
This is actually an enumerated type indicating the display type being used by the system right now. The display type enumeration is named DisplayClass . For review, the types are listed below:
DC_TEXT, DC_GRAY_1, DC_GRAY_2, DC_GRAY_4, DC_GRAY_8, DC_COLOR_2, DC_COLOR_4, DC_CF_RGB.

MSG_VIS_BOUNDS_CHANGED

void	MSG_VIS_BOUNDS_CHANGED(@stack
        word	bottom,
        word	right,
        word	top,
        word	left);

The geometry manager sends this message when a geometry update changes an object's bounds.

Source: Unrestricted--sent by the geometry manager when a visible object's bounds have changed as the result of a geometry change.

Destination: Any visible object.

Parameters: bottom , right , top , left
 The four parameters correspond to the rectangle defining the object's old bounds. The parameters are passed on the stack.

Return: Nothing.

Interception: Unlikely. If the object subclasses this message, it should invalidate any part of its old bounds that it might have drawn in (the default handler invalidates the entire passed range).

MSG_VIS_REDRAW_ENTIRE_OBJECT

void	MSG_VIS_REDRAW_ENTIRE_OBJECT();

This message causes the object send itself a MSG_VIS_DRAW , creating and destroying a GState for itself.

Source: Unrestricted--typically sent by an object to itself because of a state change, not as part of the visual update mechanism.

Destination: Any visible object--typically sent by an object to itself.

Interception: Unlikely.

MSG_VIS_INVALIDATE

void	MSG_VIS_INVALIDATE();

This message invalidates the entire region within the object's bounds. The message will cause the window system to generate a MSG_META_EXPOSED for the area covered by the bounds, causing a visual update to occur.

Source: Unrestricted.

Destination: Any visible object.

Interception: Must be intercepted if the object is working in a 32-bit document space. Must also be intercepted to work with scaled views.

MSG_VIS_INVAL_TREE

void	MSG_VIS_INVAL_TREE();

This message has the effect of MSG_VIS_INVALIDATE on an entire branch of the visible tree. The window system will generate MSG_META_EXPOSED for each affected window.

Source: Unrestricted.

Destination: Any visible object.

Interception: Unlikely.

MSG_VIS_MARK_INVALID

void	MSG_VIS_MARK_INVALID(
        VisOptFlags		flagsToSet,
        VisUpdateMode		updateMode);

This message allows the caller to set the recipient's VI_optFlags record so the object will get updated properly during the next visual update. The flags that can be set with this message are VOF_GEOMETRY_INVALID, VOF_WINDOW_INVALID, and VOF_IMAGE_INVALID. For more information on these flags, see VI_optFlags .

Source: Unrestricted.

Destination: Any visible object.

Parameters: flagsToSet A record of VisOptFlags indicating which type of invalidation is being caused. The flags allowed with this message are VOF_GEOMETRY_INVALID, VOF_WINDOW_INVALID, and VOF_IMAGE_INVALID.

updateMode
A VisUpdateMode indicating when the visual update caused by this message should occur.

Return: Nothing.

Interception: May not be subclassed. Certain optimizations are made in the default handler, and subclassing may have unpredictable results.

MSG_VIS_ADD_RECT_TO_UPDATE_REGION

void	MSG_VIS_ADD_RECT_TO_UPDATE_REGION(@stack
        byte		unused,		/* for word alignment of parameters */
        VisAddRectFlags		addRectFlags,
        word		bottom,
        word		right,
        word		top,
        word		left);

This message adds the passed rectangular region to the window group's list of regions that require visual update. The handler for this message will ensure that the window group object for this branch of the tree marks the region as invalid so it will be included in the next visual update. The addRectFlags parameter contains either of the flags listed below.

Source: Unrestricted--usually sent by an object to itself.

Destination: Any visible object--usually sent by an object to itself.

Parameters: unused An unused byte for alignment of parameters.

addRectFlags
A record of VisAddRectFlags , defined below.
rectangle coords
The coordinates of the rectangle to be added to the invalidation region.

Return: Nothing.

Interception: Unlikely--a composite object may wish to optimize invalidation by altering the passed bounds and then passing the message on to its superclass.

Structures: The VisAddRectFlags are listed below:

VARF_NOT_IF_ALREADY_INVALID
This flag indicates that the passed rectangle should not be invalidated if any visible object in the hierarchy going up to the window group object is marked invalid.
VARF_ONLY_REDRAW_MARGINS
This flag indicates that the object is invalidating old bounds and may indicate that the system can use a special optimization for invalidation.

Updating Window Groups

MSG_VIS_VUP_UPDATE_WIN_GROUP, MSG_VIS_UPDATE_WIN_GROUP, MSG_VIS_UPDATE_WINDOWS_AND_IMAGE

A window group is an object that has a window in which visible objects are displayed. Typically, you will not create your own window group objects, but you may need to notify a window group when it needs to be updated visually. To be a window group, an object has to have its VTF_IS_WINDOW or VTF_IS_PORTAL flag set in its VI_typeFlags field.

The window group is responsible for keeping track of the regions of its coordinate space that require visual updating. When a region or object gets invalidated, the window group remembers that it is invalid and makes sure it gets updated during the next visual update.

Any object can request that a visual update occur for the entire branch managed by a window group. It can use the three messages described in this section to request or force a visual update for the window group.

MSG_VIS_VUP_UPDATE_WIN_GROUP

Boolean	MSG_VIS_VUP_UPDATE_WIN_GROUP(
        VisUpdateMode updateMode);

This message travels up the visible tree from the recipient object to the first window group encountered; it will then cause that window group to go through a visible update by sending the window group object a MSG_VIS_UPDATE_WIN_GROUP , below.

Source: Unrestricted.

Destination: Any visible object residing in the window group that is to be updated.

Parameters: updateMode A VisUpdateMode indicating when the visual update should occur. VUM_MANUAL has the effect of a "no operation" because VUM_MANUAL does not cause visual updates.

Return: True if the update mechanism was invoked; false if it was not.

Interception: Not allowed.

Warnings: You may not subclass this message.

MSG_VIS_UPDATE_WIN_GROUP

void	MSG_VIS_UPDATE_WIN_GROUP(
        VisUpdateMode updateMode);

This message may only be sent by a window group object to itself. It causes the window group to actually go through a visual update.

Source: Visible update mechanism.

Destination: The window group object that is to be updated.

Parameters: updateMode A VisUpdateMode indicating when the visual update should occur. This is the same as passed to MSG_VIS_VUP_UPDATE_WIN_GROUP.

Return: Nothing.

Interception: Not allowed.

Warnings: You may not subclass this message.

MSG_VIS_UPDATE_WINDOWS_AND_IMAGE

void	MSG_VIS_UPDATE_WINDOWS_AND_IMAGE(
        VisUpdateImageFlags updateImageFlags);

This message is called by a window group during a visual update. It should not be sent or handled by anything other than a window group. It causes both the geometry and image of the affected branch of the visible tree to be updated, and it is used on branches which are already visually realized on the screen.

Source: Visual update mechanism.

Destination: A window group object.

Parameters: updateImageFlags
 This is a record of VisUpdateImageFlags that govern the visual update of the window group.

Return: Nothing.

Interception: Not allowed.

Structures: The flags of VisUpdateImageFlags are listed below:

VUIF_ALREADY_INVALIDATED
An optimization flag to keep an already-invalid composite from invalidating all its children.
VUIF_SEND_TO_ALL_CHILDREN
A flag to ensure that all children get invalidated; it is used when the composite only updates its margins for optimization.

Warnings: You may not subclass this message.

Using Visual Monikers

MSG_VIS_DRAW_MONIKER, MSG_VIS_GET_MONIKER_POS, MSG_VIS_GET_MONIKER_SIZE, MSG_VIS_FIND_MONIKER, MSG_VIS_CREATE_VIS_MONIKER

Although visual monikers are typically used with generic objects (and are, in fact, documented in the GenClass chapter), you can display monikers with visible objects as well. The four messages discussed in this section can be used to draw a visible moniker, get information about a moniker, or locate a particular moniker in the visible object tree.

MSG_VIS_DRAW_MONIKER

void	MSG_VIS_DRAW_MONIKER(@stack
        DrawMonikerFlags		monikerFlags,
        ChunkHandle		visMoniker,
        word		textHeight,
        GStateHandle		gstate,
        word		yMaximum,
        word		xMaximum,
        word		yInset,
        word		xInset);

This message draws a visual moniker for the object. This message may be called by an object on itself in its MSG_VIS_DRAW handler.

Source: Unrestricted.

Destination: Any visible object.

Parameters: monikerFlags A record of DrawMonikerFlags indicating how the moniker should be drawn. These flags are described below.

visMoniker
The chunk handle of the chunk containing the actual moniker to be drawn. This chunk must be in the same block as the object handling the message.
textHeight
A parameter used for optimization--if the caller knows the system text height, it should pass it here. If not, it should pass zero.
gstate
The handle of the GState to use when drawing the moniker. Typically, this is received by MSG_VIS_DRAW , which invokes this message.
yMaximum
The maximum bottom bound of the moniker if DMF_CLIP_TO_MAX_WIDTH is set in monikerFlags . Pass MAX_COORD to avoid clipping.
xMaximum
The maximum right bound of the moniker if DMF_CLIP_TO_MAX_WIDTH is set in monikerFlags . Pass MAX_COORD to avoid clipping.
yInset
The vertical inset at which to begin drawing the moniker, if left or right justified.
xInset
The horizontal inset at which to begin drawing the moniker, if top or bottom justified.

Return: Nothing.

Interception: Unlikely.

Structures: The DrawMonikerFlags structure is defined as follows:

typedef ByteFlags DrawMonikerFlags;
#define DMF_UNDERLINE_ACCELERATOR						0x40
        /* Underlines accelerator key, if any */
#define DMF_CLIP_TO_MAX_WIDTH						0x20
        /* Clips the moniker to the xMaximum
         * parameter */
#define DMF_NONE						0x10
        /* Set to draw the moniker at the
         * current pen position (ignore the
         * xInset and yInset parameters) */
#define DMF_Y_JUST_MASK						0x0c
#define DMF_X_JUST_MASK						0x03
        /* These are two bitfields that
         * determine the justification.
         * Their offsets are below; they
         * are of type Justification. */
#define DMF_Y_JUST_OFFSET					2
#define DMF_X_JUST_OFFSET					0

MSG_VIS_GET_MONIKER_POS

XYValueAsDWord MSG_VIS_GET_MONIKER_POS(@stack
        DrawMonikerFlags		monikerFlags,
        ChunkHandle		visMoniker,
        word		textHeight,
        GStateHandle		gstate,
        word		yMaximum,
        word		xMaximum,
        word		yInset,
        word		xInset);

This message returns the position at which the moniker would appear if it were drawn with MSG_VIS_DRAW_MONIKER . The moniker is not actually drawn by this message.

Source: Unrestricted.

Destination: Any visible object.

Parameters: See MSG_VIS_DRAW_MONIKER above.

Return: A dword value representing the horizontal and vertical positions where the moniker would be drawn if it were drawn with the passed parameters. The horizontal position is returned in the high word; the vertical position is returned in the low word. Use the DWORD_X and DWORD_Y macros to extract the x and y values. These macros can be found in the file graphics.h .

Interception: Unlikely.

MSG_VIS_GET_MONIKER_SIZE

SizeAsDWord MSG_VIS_GET_MONIKER_SIZE(@stack
        DrawMonikerFlags		monikerFlags,
        ChunkHandle		visMoniker,
        word		textHeight,
        GStateHandle		gstate,
        word		yMaximum,
        word		xMaximum,
        word		yInset,
        word		xInset);

This message returns the size of the moniker specified by the parameters. The moniker is not drawn.

Source: Unrestricted.

Destination: Any visible object.

Parameters: See MSG_VIS_DRAW_MONIKER above.

Return: A dword value representing the size of the moniker. The width of the moniker is returned in the high word; the height is returned in the low word. Use the macros DWORD_WIDTH and DWORD_HEIGHT, which can be found in the file visC.goh and also on DWORD_HEIGHT .

Interception: Unlikely.

MSG_VIS_FIND_MONIKER

optr	MSG_VIS_FIND_MONIKER(@stack
        VisMonikerSearchFlags			searchFlags,
        MemHandle			destBlock,
        ChunkHandle			monikerList,
        DisplayType			displayType);

This message locates the given visual moniker list and returns the optr of the moniker most appropriate for the passed display scheme.

Source: Unrestricted.

Destination: Any visible object.

Parameters: searchFlags A record of VisMonikerSearchFlags (described below) indicating the attributes of the moniker that should be returned.

destBlock
Handle of the block into which the moniker chunk should be copied.
monikerList
Chunk handle of the chunk containing the moniker list to search. The moniker list must be in the same object block as the object handling the message.
displayType
The DisplayType of the moniker to be found (see below).

Interception: Unlikely.

Structures: The VisMonikerSearchFlags are listed below.

 typedef WordFlags VisMonikerSearchFlags;
#define VMSF_STYLE					0xf000
    /* Four bits defining the preferred style of
     * the moniker. These bits are of type
     * VMStyle, which is defined below. */
#define VMSF_COPY_CHUNK					0x0400
    /* Set if the moniker should be copied into
     * the block specified if not already in
     * that block. */
#define VMSF_REPLACE_LIST					0x0200
    /* Set if the moniker list chunk containing
     * the VisMoniker should be replaced. The
     * chunk handle of the list will then point
     * to the moniker rather than the list. */
#define VMSF_GSTRING					0x0100
    /* Set if a GString moniker is expected,
     * clear if a text moniker is expected. */
    /* The remaining bits of this record are
     * reserved for internal use. */
#define VMSF_STYLE_OFFSET 12
typedef ByteEnum  VMStyle;
#define VMS_TEXT					0
            /* Normal text moniker */
#define VMS_ABBREV_TEXT					1
            /* short text abbreviation */
#define VMS_GRAPHIC_TEXT					2
            /* textual GString */
#define VMS_ICON					3
            /* normal GString moniker */
#define VMS_TOOL					4
            /* tool moniker, normally smaller
             * than a standard moniker */

The DisplayType flags are listed below and can be found in win.h:

typedef ByteFlags  DisplayType;
#define DT_DISP_SIZE						0xc0
        /*   Two bits indicating the size of the
         * display; a DisplaySize value, one of
         * DS_TINY (CGA, or 256 x 320),
         * DS_STANDARD (EGA, VGA, HGC, MCGA),
         * DS_LARGE (800 x 600 SVGA), or
         * DS_HUGE (huge screens). */
#define DT_DISP_ASPECT_RATIO						0x30
        /* Two bits indicating the aspect
         * ratio of the screen; a value of
         * DisplayAspectRatio, one of
         * DAR_NORMAL (VGA or MCGA),
         * DAR_SQUISHED (EGA or HGC), or
         * DAR_VERY_SQUISHED (CGA) */
#define DT_DISP_CLASS						0x0f
        /* Four bits indicating the class of
         * the display driver (or closest
         * match); A DisplayClass value, one of
         * DC_TEXT (char only, not implemented),
         * DC_GRAY_1 (1 bit/pixel gray scale),
         * DC_GRAY_2 (2 bit/pixel gray scale),
         * DC_GRAY_4 (4 bit/pixel gray scale),
         * DC_GRAY_8 (8 bit/pixel gray scale),
         * DC_COLOR_2 (2 bit/pixel color index),
         * DC_COLOR_4 (4 bit/pixel color index),
         * DC_COLOR_8 (8 bit/pixel color index),
         * DC_CF_RGB (color with RGB values) */

MSG_VIS_CREATE_VIS_MONIKER

ChunkHandle MSG_VIS_CREATE_VIS_MONIKER(@stack
        CreateVisMonikerFlags			flags,
        word			height,
        word			width
        word			length,
        VisMonikerDataType			dataType,
        VisMonikerSourceType			sourceType,
        dword			source);

This message creates a new chunk for a visual moniker within the recipient's object block. The new moniker can be created from an already existing visual moniker, a visual moniker list, a text string, a GString, or a token from the token database. The source may be defined by a far pointer, a global memory handle, or an optr. If the source is a text string or GString, a visual moniker structure will be created for the string. The newly-created chunk is marked dirty if the CVMF_DIRTY flag is passed.

If a moniker list is passed, the entire list will be copied into the destination object block. You must make sure that all the monikers in the list will still exist when the list is used.

Source: Unrestricted.

Destination: Any visible object.

Parameters: flags A record of CreateVisMonikerFlags . Currently only one may be passed: CVMF_DIRTY, which indicates that the chunk should be marked dirty.

height
The height of the moniker.
width
The width of the moniker.
length
The length of the moniker, if a text string.
dataType
The type of moniker, an enumeration of the type VisMonikerDataType . This parameter determines the type of moniker to be created.
sourceType
The type of source used to create the new moniker, an enumeration of VisMonikerSourceType .
source
This parameter can be a pointer to the source, the global handle of the block containing the source, or an optr pointing to the chunk containing the source, depending on the value in sourceType .

Return: The chunk handle of the new visual moniker. The visual moniker chunk resides in the object block of the object receiving the message. If the flag CVMF_DIRTY is passed, the chunk will be marked dirty.

Interception: Unlikely--custom UI gadgets may intercept in some cases.

Structures: VisMonikerSourceType and VisMonikerDataType are defined below. Both can be found in visC.goh .

typedef ByteEnum  VisMonikerSourceType;
#define VMST_FPTR					0
        /* Source is referenced by a pointer.
         * CVMF_source is a far pointer. */
#define VMST_OPTR					1
        /* Source is referenced by an optr.
         * CVMF_source is an optr. */
#define VMST_HPTR					2
        /* Source is referenced by a combination
         * memory handle and offset into the
         * memory block (as opposed to an optr
         * in which the low word is actually a
         * chunk handle, not an offset). */
typedef ByteEnum  VisMonikerDataType;
#define VMDT_NULL					0
        /* There is no source. Not valid for
         * MSG_VIS_CREATE_VIS_MONIKER. */
#define VMDT_VIS_MONIKER					1
        /* Source is a complete VisMoniker
         * structure. CVMF_length indicates the
         * size of a complete VisMoniker
         * structure; CVMF_width and
         * CVMF_height are unused. */
#define VMDT_TEXT					2
        /* Source is a text string. If the
         * string is null-terminated,
         * CVMF_length should be zero.
         * Otherwise, CVMF_length is the length
         * of the string; A VisMoniker
         * structure will be created for the
         * string. CVMF_width and CVMF_height
         * are unused. */
#define VMDT_GSTRING					3
        /* Source is a GString. If CVMF_length
         * is zero, the GString length is
         * determined by scanning for
         * GR_END_STRING. Otherwise, CVMF_length
         * indicates the length of the GString.
         * CVMF_width and CVMF_height indicate
         * the width and height of the GString.
         * If either is zero, the dimension will
         * be calculated by examining the
         * string. A VisMoniker structure will
         * be created for the GString. */
#define VMDT_TOKEN					4
        /* Source is a GeodeToken. CVMF_length,
         * CVMF_width, and CVMF_height are
         * unused. The destination must be able
         * to use this data type because the
         * specific UI must decide which moniker
         * to choose from the token in the token
         * database. */

DWORD_HEIGHT

word	 DWORD_HEIGHT(val);
        dword	val;

This macro extracts the height from a SizeAsDWord structure (dword).

DWORD_WIDTH

word	 DWORD_WIDTH(val);
        dword	val;

This macro extracts the width from a SizeAsDWord structure (dword).

MAKE_SIZE_DWORD

SizeAsDWord  MAKE_SIZE_DWORD(width, height);
        word	width, height;

This macro takes a width and height and creates a SizeAsDWord value.

Cached GStates

MSG_VIS_CREATE_CACHED_GSTATES, MSG_VIS_RECREATE_CACHED_GSTATES, MSG_VIS_DESTROY_CACHED_GSTATES

Many complex visible objects (such as VisText and VisSpline) have both a cached GState and a reference count. When the reference count goes from zero to one, the Vis object creates and caches a GState; when the reference count goes from one to zero, the cached GState is destroyed. VisClass has three messages to create, update, and destroy the cached GStates. None of these messages has any default behavior; they are provided for complex objects to handle should they need them.

MSG_VIS_CREATE_CACHED_GSTATES

void	MSG_VIS_CREATE_CACHED_GSTATES();

This message may be used to create and cache a GState, typically to avoid having a complex Vis object update several times for several simple operations (such as pointer events).

Source: Unrestricted.

Destination: Any visible object.

Interception: Must be intercepted to have any effect; there is no default behavior.

MSG_VIS_RECREATE_CACHED_GSTATES

void	MSG_VIS_RECREATE_CACHED_GSTATES();

This message may be used to have the Vis object destroy and recreate any cached GStates it has. For example, cached GStates of a complex visible object that gets moved or resized will no longer map to the proper place in the object's document. After the move or resize, this message may be used to update those GStates.

Source: Unrestricted.

Destination: Any visible object.

Interception: Must be intercepted to have any effect; there is no default behavior.

MSG_VIS_DESTROY_CACHED_GSTATES

void	MSG_VIS_DESTROY_CACHED_GSTATES();

This message may be used to destroy any cached GStates the visible object may have.

Source: Unrestricted.

Destination: Any visible object.

Interception: Must be intercepted to have any effect; there is no default behavior.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Basic VisClass Rules | Next: 4.3 Positioning Visible Objects


Positioning Visible Objects

VisClass: 4.3 Using VisClass: Positioning Visible Objects

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Drawing to the Screen | Next: 4.4 Handling Input

Every visible object has an instance data field called VI_bounds that records the object's visible bounds. Visible bounds are the left, top, right, and bottom bounds of a rectangle that define the maximum area covered by the visible object. The bounds are used primarily for determining the object's size and location.

The VI_bounds field is a Rectangle structure, provided by the graphics system. This structure has four components, as shown below:

  typedef struct {
    sword   R_left;    /* x of upper-left corner */
    sword   R_top;     /* y of upper-left corner */
    sword   R_right;   /* x of lower-right corner */
    sword   R_bottom;  /* y of lower-right corner */
} Rectangle;

The bounds of non-window objects are stored in document coordinates relative to the document displayed by the window in which they appear. The left and top bounds constitute the position of the object. The difference between the right and left bounds is the object's width; the difference between bottom and top is its height. Together, the width and height constitute the object's size.

The bounds of a window or portal object (VTF_IS_WINDOW or VTF_IS_PORTAL) represent the location of the window, in document coordinates, within its parent window. The coordinate system within the window object is independent from that of its parent.

Unless an object's bounds are set in the Goc code, they will be set at zero and must be initialized to some values before the object can be drawn. This initialization can be done with the messages that set position and size, or it can be done automatically by the geometry manager (if a composite is managing its children).

The VI_bounds instance field exists in VisClass and therefore occurs also in VisCompClass and VisContentClass and all their subclasses. Any object can directly set any or all of its bounds; other objects (such as a parent VisComp) can direct the object to set its bounds to certain coordinates as a result of either geometry management or some user action. For example, a VisComp that has ten children may recalculate their positions if one of those children gets resized; the composite must then notify all the children of their new positions. To learn how a composite object can manage its children in different ways, see the VisComp chapter.

Object Position and Bounds

MSG_VIS_GET_POSITION, MSG_VIS_SET_POSITION, MSG_VIS_GET_BOUNDS

An object's bounds determine both the location and the size of the object in the document space. The object may reposition itself or resize itself at will; for example, it may allow the user to move or resize it with the mouse. The object can also receive direction from other objects that it should change its bounds. Note that none of these messages by themselves will cause the object to redraw; you must also send a MSG_VIS_MARK_INVALID to the object to get either its geometry or its image to be marked invalid.

An object's position is represented by the upper-left corner of its bounds, defined by VI_bounds . R_top and VI_bounds . R_left . You can retrieve an object's position by sending it MSG_VIS_GET_POSITION , or you can set it with MSG_VIS_SET_POSITION . You can also use MSG_VIS_GET_BOUNDS to get all four components of the object's bounds.

An object can access its own bounds field with a simple pointer. Since every message has pself as an implicit parameter (a pointer to the object's instance data structure), any object can look at or change its bounds directly. (See the example in Grabbing and Releasing the Mouse .)

Using Swat, you can see a visible object's bounds with the command pobj .

If you have a composite object managing several children, you can arrange to have their positions determined automatically. This is discussed in Geometry Management .

MSG_VIS_SET_POSITION

void	MSG_VIS_SET_POSITION(
        int	xOrigin,
        int	yOrigin);

This message causes the object to set its VI_bounds to the new location specified, retaining its current size. The left bound ( VI_bounds . R_left ) is set to the value of xOrigin , and the top bound ( VI_bounds . R_top ) is set to that of yOrigin . The right and bottom bounds are set to match the new origin and the width and height of the object.

Source: Unrestricted--usually the visible parent or geometry manager.

Destination: Any visible object.

Parameters: xOrigin New horizontal coordinate of the object's position.

yOrigin
New vertical coordinate of the object's position.

Return: Nothing.

Interception: Unlikely. It's much more likely that the object will subclass MSG_VIS_POSITION_BRANCH .

Warnings: This message will not cause an image invalidation. You must mark the object invalid with MSG_VIS_MARK_INVALID .

See Also: MSG_VIS_POSITION_BRANCH , MSG_VIS_MARK_INVALID .

MSG_VIS_GET_POSITION

XYValueAsDWord MSG_VIS_GET_POSITION();

This message returns the current origin of the object. The origin returned consists of the left and top object bounds ( VI_bounds . R_left and VI_bounds . R_top , respectively).

Source: Unrestricted.

Destination: Any visible object.

Parameters: None.

Return: A dword value representing the left bound in the low word and the top bound in the high word. Use the macros DWORD_X and DWORD_Y to extract the bounds from the return value (see the file graphics.h ).

Interception: Unlikely.

MSG_VIS_GET_BOUNDS

void	MSG_VIS_GET_BOUNDS(
        Rectangle *retValue);

This message returns the current rectangle structure stored in the object's VI_bounds field. This rectangle represents both the position and the size of the object by giving its left, top, right, and bottom bounds.

Source: Unrestricted.

Destination: Any visible object.

Parameters: retValue A pointer to an empty Rectangle structure. The values in the object's VI_bounds field will be returned in the structure pointed to.

Return: The retValue pointer will be returned with the bounds filled into the Rectangle structure.

Interception: Unlikely.

Object Size

MSG_VIS_GET_SIZE, MSG_VIS_SET_SIZE

A visible object's size is defined as its width and height. The width is the difference between the object's left and right bounds; the height is the difference between the object's top and bottom bounds.

You can retrieve a visible object's size with the message MSG_VIS_GET_SIZE , and you can set it with MSG_VIS_SET_SIZE . MSG_VIS_SET_SIZE will set the object's right and bottom bounds without recalculating the object's position. If geometry is being managed automatically, you may want to subclass other messages; these are described in Geometry Management .

In Swat, you can see the object's size indirectly with the command pobj . This command will show, among other things, the VI_bounds field of the object. You can manually calculate the size from that information.

MSG_VIS_SET_SIZE

void	MSG_VIS_SET_SIZE(
        int	width,
        int	height);

This message sets the object's height and width to the passed values, retaining the object's current position. The object's position will not be recalculated as a result of the size change.

Source: Unrestricted--usually the geometry manager or the object's visible parent calculating geometry.

Destination: Any visible object.

Parameters: width The new width of the object. width will be added to the object's left bound to get the object's new right bound.

height
The new height of the object. height will be added to the object's top bound to get the object's new bottom bound.

Return: Nothing.

Interception: Unlikely.

Warnings: This message will not cause an image invalidation. You must mark the object invalid with MSG_VIS_MARK_INVALID .

MSG_VIS_GET_SIZE

SizeAsDWord MSG_VIS_GET_SIZE();

This message returns the current size (width and height) of the object. Both values are word-sized integers and are returned in a single dword value. The high word of this value is the object's width, and the low word is the object's height.

Source: Unrestricted.

Destination: Any visible object.

Parameters: None.

Return: A dword value representing the object's width in the low word and height in the high word. Use the macros DWORD_WIDTH and DWORD_HEIGHT to extract the width and height; see DWORD_HEIGHT .

Interception: Unlikely.

Geometry Management

MSG_VIS_UPDATE_GEOMETRY, MSG_VIS_NOTIFY_GEOMETRY_VALID, MSG_VIS_RECALC_SIZE, MSG_VIS_GET_CENTER, MSG_VIS_RECALC_SIZE_AND_INVAL_IF_NEEDED, MSG_VIS_RESET_TO_INITIAL_SIZE, MSG_VIS_POSITION_BRANCH, MSG_VIS_POSITION_AND_INVAL_IF_NEEDED, MSG_VIS_INVAL_ALL_GEOMETRY

Geometry management consists of positioning and sizing visible objects properly. This can be done manually by the programmer or automatically by GEOS. If you wish to manually control your visible tree's geometry, set the flag VCGA_CUSTOM_MANAGE_CHILDREN in the top composite's VCI_geoAttrs field (see the VisComp chapter). You can then use any messages, either native to VisClass or defined by your subclasses, for object positioning and sizing.

Automatic geometry management occurs when the geometry of the visible tree is marked invalid somewhere and a visual update is initiated. The tree's geometry can be marked invalid in several ways--an object can be added to or removed from the tree, an object can be manually marked invalid (with MSG_VIS_MARK_INVALID ), or an object can be opened or closed ( MSG_VIS_OPEN or MSG_VIS_CLOSE ).

When automatic geometry management is invoked, the geometry manager will recalculate the entire affected branch's geometry. It will calculate geometry for each affected window group, from the topmost affected window object down to the bottommost affected object. If the geometry manager encounters window groups below the topmost window, it will check first to see if the lower window's size is controlled by its children (e.g., a GenView that follows the geometry of its content). If the lower window is scrollable or otherwise not subject to its children's geometry, the geometry manager will finish calculating the geometry above the lower window before proceeding.

Calculating geometry is an involved, complex task. The process involves composites and their children negotiating on desired sizes. How the geometry is calculated depends primarily on the relationships between the objects and how their flags are set.

For example, if a GenView is set up to be exactly the same size as its VisContent object, the calculations will be different than if the GenView were scrollable. The same is true for visible composites that manage their children: If the composite resizes itself large enough to hold all its children, the children must be resized first.

The negotiation between parent and child in the visible tree takes the form of a single message: MSG_VIS_RECALC_SIZE . This is the single most important message in calculating the tree's geometry, though others are also significant. This message is sent by a composite to each of its children in turn; it passes a suggested size, and the child returns its desired size.

The composite collects all the desired sizes of its children and compares that to the size it thinks it should be. If necessary, it makes another pass through the children, or it passes on its desired size to its parent composite. Depending on the situation and the disparity between parent and children sizes, the geometry calculation may take a single pass or several. If the sizes can not be resolved through this negotiation, the geometry manager will make a decision after several passes, typically expanding the parent as much as possible to fit all the children.

After a visible object has calculated its size, its size will be set either by itself or by its parent. Often this is done with MSG_VIS_SET_SIZE . Once all sizes have been determined and set, the objects set their positions with MSG_VIS_POSITION_BRANCH , which propagates down the tree. It sets the position of the topmost composite and then sets each of its children's positions appropriately.

When geometry calculations are complete, the geometry manager will send a MSG_VIS_NOTIFY_GEOMETRY_VALID to all visible objects with the flag VGA_NOTIFY_GEOMETRY_VALID set in its VI_geoAttrs field.

Several other messages may be used during geometry updates. Some of them may be intercepted to alter the behavior of the default method. These messages are listed below. Others can be used to invalidate a visible object's geometry or to cause a geometry update on the tree.

MSG_VIS_UPDATE_GEOMETRY
This message is typically called by the UI during a window group update. It calculates the geometry of the entire branch receiving it. It's not likely you'll subclass this method--altering its behavior can be used for certain optimizations, however.
MSG_VIS_NOTIFY_GEOMETRY_VALID
This message will be sent to an object when all its geometry messages have been handled and its geometry has been fully resolved (at least for the moment). This message will be sent only if VGA_NOTIFY_GEOMETRY_VALID is set in the object's VI_geoAttrs field. There is no default behavior for this message; it should be intercepted only if the object needs to do calculations after the geometry has settled down.
MSG_VIS_RECALC_SIZE
This message is normally sent by the geometry manager to a composite object. The composite is expected to send it to all its children; the children will then determine their sizes, and the composite will figure the total size and return. A composite that manages its own children can also use this message to query its children for their desired sizes.
MSG_VIS_RECALC_SIZE_AND_INVAL_IF_NEEDED
This message is an optimized MSG_VIS_RECALC_SIZE . Composites should use this version when sending a recalculation message to their children. This message only causes recalculation if needed and will invalidate the object if the size gets recalculated. It invalidates the object's image at its old bounds and only invalidates its geometry if the bounds change. It is highly unlikely that you'll subclass this method.
MSG_VIS_GET_CENTER
This message returns the amount of space on each side the object wants reserved for itself. It does not, as the name suggests, return the coordinates of the object's center. Instead, it returns the distance in each direction the object requires (you may think of it as external margins).
MSG_VIS_RESET_TO_INITIAL_SIZE
This message propagates down the visible tree and causes every object that receives it to reset itself to its original size. It does this by clearing VGA_GEOMETRY_CALCULATED in the object's VI_geoAttrs field, causing the object to believe it is being put on-screen for the first time. This message will also set the object's flags to indicate that its geometry has become invalid. It's highly unlikely your application will subclass this method.
MSG_VIS_POSITION_BRANCH
This message is sent by the geometry manager when an entire visible branch should be repositioned. If an application wants to do something special, it should subclass the handler for this message. Typically, though, only subclasses of VisCompClass and perhaps VisContentClass will change this method.
MSG_VIS_POSITION_AND_INVAL_IF_NEEDED
This message is an optimized MSG_VIS_POSITION_BRANCH that causes a branch reposition only if it's necessary. It will also invalidate both the object's and the parent's original images and, if the position changes, the geometry as well. It is highly unlikely that you'll subclass this method.
MSG_VIS_INVAL_ALL_GEOMETRY
This message invalidates all geometry under the recipient object, forcing invalidation of all children. This is a brute-force message used only when absolutely necessary.

MSG_VIS_UPDATE_GEOMETRY

void	MSG_VIS_UPDATE_GEOMETRY();

This message is sent during a visual update to cause objects to recalculate their geometry. Applications can use this message to ensure that geometry gets updated for a visual branch even if the branch is not currently drawn on the screen.

Source: Visual update mechanism.

Destination: Any visible object.

Interception: May be intercepted for optimizations. A thorough knowledge of the geometry update mechanism and the specifics of the visible tree's geometry is strongly recommended, however.

MSG_VIS_NOTIFY_GEOMETRY_VALID

void	MSG_VIS_NOTIFY_GEOMETRY_VALID();

This message is sent by the geometry manager to objects that have VGA_NOTIFY_GEOMETRY_VALID set. The message is sent only after all geometry messages have been handled for the object. It has no default behavior.

Source: Visible update mechanism.

Destination: Any visible object--typically a child of the object sending the message.

Interception: This message has no default behavior. It should be intercepted by any object that needs to do something after its geometry has been made valid.

MSG_VIS_RECALC_SIZE

SizeAsDWord MSG_VIS_RECALC_SIZE(
        int	width,
        int	height);

This message takes the suggested height and width of the object and recalculates the object's desired size based on them. Composite objects are expected to pass this message to their children and then calculate their sizes based on those of their children. The default behavior of the handler for this is to return the current size no matter what the suggested size is.

Source: Visible update mechanism.

Destination: Any visible object.

Parameters: width Suggested new width of the object as determined by the object's parent.

height
Suggested new height of the object as determined by the object's parent.

Return: A dword value representing the object's desired size as calculated by the object. The high word of the return value represents the width and the low word the height. Use the macros DWORD_WIDTH and DWORD_HEIGHT to extract the width and height; see DWORD_HEIGHT .

Interception: Default behavior is to return the object's current size no matter what the passed width and height are. Any object that wants special sizing behavior based either on the object's children or on the suggested size should subclass the method.

MSG_VIS_RECALC_SIZE_AND_INVAL_IF_NEEDED

SizeAsDWord MSG_VIS_RECALC_SIZE_AND_INVAL_IF_NEEDED(
        int	width,
        int	height);

This message is the same as MSG_VIS_CALC_RESIZE except that it is somewhat optimized. It will recalculate only if certain flags are set for the object, and it will mark the geometry invalid only if recalculation is done.

Source: Visible update mechanism.

Destination: Any visible object.

Parameters: width Suggested new width of the object as determined by the object's parent.

height
Suggested new height of the object as determined by the object's parent.

Return: A dword value representing the object's desired size as calculated by the object. The low word of the return value represents the width and the high word the height.

Interception: Unlikely--Default behavior is to return the object's current size no matter what the passed width and height are. Any object that wants special sizing behavior based either on the object's children or on the suggested size should subclass the method.

MSG_VIS_GET_CENTER

void	MSG_VIS_GET_CENTER(
        GetCenterParams *retValue);

This message returns the amount of space needed by the object in each direction from its center. It does not, as the name suggests, return the coordinates of the center of the object. Instead, the object must determine the amount of space it requires from its center to each edge for geometry management and return them in the structure passed.

Source: Visible update mechanism.

Destination: Any visible object

Parameters: retValue A pointer to an empty GetCenterParams structure, defined below.

Return: The method must fill the GetCenterParams structure pointed to by the retValue parameter.

Interception: Any object that wants to effect a different centering than the default should subclass this method. For example, if a set of objects can be overlapped, or if they should have extra space between them, they should subclass this message and alter the returned values appropriately.

Structures: The GetCenterParams structure is defined as follows:

typedef struct {
    word   GCP_aboveCenter;    /* space above */
    word   GCP_belowCenter;    /* space below */
    word   GCP_leftOfCenter;   /* space left */
    word   GCP_rightOfCenter;  /* space right */
}  GetCenterParams;

MSG_VIS_RESET_TO_INITIAL_SIZE

void	MSG_VIS_RESET_TO_INITIAL_SIZE(
        VisUpdateMode updateMode);

This message propagates down an entire visual branch, causing the objects in it to reset their sizes to their original width and height. First it invalidates the object's geometry, then it recalculates the geometry starting at the top.

Source: Visible update mechanism.

Destination: Any visible object.

Parameters: updateMode A VisUpdateMode indicating when the visual update of the tree should occur.

Return: Nothing.

Interception: Unlikely.

MSG_VIS_POSITION_BRANCH

void	MSG_VIS_POSITION_BRANCH(
        word	xOrigin,
        word	yOrigin);

This message repositions an entire visible branch. It propagates down the branch, causing each visible object to reposition itself based on the new origins passed. Composites must pass the appropriate altered positions to their children.

Source: Visible update mechanism.

Destination: Any visible object.

Parameters: xOrigin The new horizontal position of the object relative to the document or window it's in. This value is set into the object's VI_bounds . R_left bound, and the right bound is set according to the object's size.

yOrigin
The new vertical position of the object relative to the document or window it's in. This value is set into the object's VI_bounds . R_top bound, and the bottom bound is set according to the object's size.

Return: Nothing.

Interception: The default behavior is to set the object to the passed position. Composite objects that want to position their children a special way should subclass this (and not call the superclass) method. Non-composites will rarely, if ever, subclass this message.

MSG_VIS_POSITION_AND_INVAL_IF_NEEDED

Boolean	MSG_VIS_POSITION_AND_INVAL_IF_NEEDED(
        word	xPosition,
        word	yPosition);

This message is an optimized version of MSG_VIS_POSITION_BRANCH . It repositions the branch only if necessary and will mark the image invalid only if the position was changed. It returns a flag indicating whether or not the object was repositioned.

Source: Visible update mechanism.

Destination: Any visible object--typically sent by a composite to its children.

Parameters: xPosition The new horizontal position of the object relative to the document or window it's in. A negative value means that the object should choose its own position as best it can.

yPosition
The new vertical position of the object relative to the document or window it's in. A negative value means that the object should choose its own position as best it can.

Return: If the object was repositioned, the message will return true . Otherwise, it will return false .

Interception: Unlikely.

MSG_VIS_INVAL_ALL_GEOMETRY

void	 MSG_VIS_INVAL_ALL_GEOMETRY(
        VisUpdateMode		updateMode);

This is a brute-force, desperation message used to invalidate all geometry in the recipient's tree; that is, all geometry of the recipient and all objects under it in the tree will be invalidated. You should only use this message when the whole tree should be recalculated.

Source: Unrestricted.

Destination: The object requiring geometry recalculation.

Parameters: updateMode A visual update mode indicating when the visual update of the recalculation should occur.

Return: Nothing.

Interception: Generally not intercepted.

|  Up: GEOS SDK TechDocs | Up | Prev: 4.2 Drawing to the Screen | Next: 4.4 Handling Input


Handling Input

VisClass: 4.4 Using VisClass: Handling Input

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Positioning Visible Objects | Next: 5 Working with Visible Object Trees

Input events, what they mean, and how they are generated are discussed fully in the Input chapter. The visible world has some default behavior for handling input messages such as MSG_META_START_SELECT , MSG_META_END_SELECT , and MSG_META_PTR . For the most part, however, visible objects that want to respond to input must subclass and handle the input messages that interest them.

Composite objects will automatically pass mouse messages on to the proper child directly under the mouse pointer. Non-composite objects (direct subclasses of VisClass ) have no special default behavior for input handling.

Note that VisClass does not intelligently handle layering of objects. That is, if one object is visibly on top of another object, it will not necessarily receive the input events--the events may be passed to the partially hidden object. This is due to the order in which drawing and mouse events are handled by a visible object's children.

Take the example of a visible composite with two children. Each child draws a filled rectangle covering its bounds; the first child has a width and height of 40, and the second child has a width and height of 20. The composite's declaration shows

    ...
    VCI_comp = LargeChild, SmallChild;
    ...

Because drawing occurs in the order the children are listed, the small child will be drawn over the large child, as shown in the figure below. If each object can intercept and handle mouse events (e.g., MSG_META_START_SELECT ), clicks will never reach the small object without special behavior added to the composite. This is because input events are handled in the same order as drawing; the input event will be given to the first child whose bounds include the event's coordinates.

So, a click event on (10, 10), which is in the middle of the small square, would first be passed to the LargeChild object. The large child would see that the click was within its bounds, and it would take the event. The event would never be handled by the small object.

Therefore, if you need to have overlapping objects which handle user input, you will have to add special features for bounds detection. In the example of the two squares, if neither were to move, the composite could simply pass the event to the small child first and then to the large child, assuming the small child is always on top. If the objects could be moved forward and back so they hide each other, the composite (or the objects themselves) will need some other, more complex, detection behavior.

Mouse Events

MSG_META_PTR, MSG_META_START_SELECT, MSG_META_END_SELECT, MSG_META_DRAG_SELECT, MSG_META_DRAG, MSG_META_START_MOVE_COPY, MSG_META_DRAG_MOVE_COPY, MSG_META_START_OTHER, MSG_META_END_OTHER, MSG_META_DRAG_OTHER

Composite objects will automatically pass mouse messages on to the first child directly under the mouse pointer. Non-composite objects (direct subclasses of VisClass ) have no special default behavior for input handling.

Typically, a visible object that needs to handle mouse input will subclass MSG_META_START_SELECT (or one of the other press events such as MSG_META_START_MOVE_COPY ). This message is sent when the user presses on the "select" mouse button while the pointer is over the object. In this method, the object should grab the mouse (as shown below). When it has the mouse grab, it will receive MSG_META_PTR each time the mouse is moved; the object should subclass this message to provide the desired reaction to mouse moves. Finally, when the user releases the select button, the object will receive a MSG_META_END_SELECT . At this point, it should release the mouse grab.

Typically, mouse events will arrive with coordinates specified in 16 bits. This is the default graphics space, and it represents a coordinate system more than 25 yards on a side. Nearly all applications will find this graphic system big enough for their uses. However, if you need larger coordinates, you can use the large document space with 32-bit coordinates. To do this, you must set the GVA_WINDOW_COORDINATE_MOUSE_EVENTS in the GVI_attrs field of your GenView object and set up your visible objects correctly. For more information on supporting 32-bit graphic spaces with visible objects, see Visible Layers and 32-Bit Graphics .

Grabbing the Mouse

MSG_VIS_GRAB_MOUSE, MSG_VIS_FORCE_GRAB_MOUSE, MSG_VIS_GRAB_LARGE_MOUSE, MSG_VIS_FORCE_GRAB_LARGE_MOUSE, MSG_VIS_RELEASE_MOUSE

When an object "grabs the mouse," it requests that the UI send all future mouse events directly to that object. Normally, mouse events will travel down the visible object tree until they reach either the leaf object under the pointer or an object under the pointer that handles them. When an object has the mouse grab, the mouse events will go to the object with the grab regardless of the location of the pointer.

Typically, if an object wants to receive mouse events, it will subclass MSG_META_START_SELECT . This message is sent to the object when the pointer is over the object's bounds and the user presses the select button. In this handler, the object grabs the UI's gadget exclusive and then grabs the mouse. A good example of this can be found in the TicTac sample application's handler for MSG_META_START_SELECT .

After an object grabs the mouse, it receives all pointer and drag events that occur. These are primarily MSG_META_PTR messages, and the object must subclass MSG_META_PTR to achieve the proper behavior it wants. When the user lets go of the select button, the object will receive a MSG_META_END_SELECT . If it grabbed the mouse (which it probably did if it receives this message), it must relinquish both the gadget exclusive and its mouse grab.

An example of getting and releasing the mouse grab (taken from the TicTac sample application) is shown in Grabbing and Releasing the Mouse . The object subclasses these messages to let the user drag it around the screen.

The UI allows the visible object to get the normal mouse grab by using four different messages. Each of these is detailed below. If you're using a large document (32-bit coordinates), you will use the "large" mouse grabs. Otherwise, the normal mouse grab messages will do.

MSG_VIS_GRAB_MOUSE

void	MSG_VIS_GRAB_MOUSE();

A visible object sends itself this message (with @call ) when it wants to acquire the normal mouse grab. The message MSG_VIS_RELEASE_MOUSE must be used to relinquish the grab.

Source: Unrestricted.

Destination: Any visible object--typically sent by the object to itself.

Interception: Highly unlikely.

MSG_VIS_FORCE_GRAB_MOUSE

void	MSG_VIS_FORCE_GRAB_MOUSE();

A visible object sends itself this message (with @call ) when it wants to forcibly grab the mouse input. The object will acquire the mouse grab even if another object currently has it; the other object will receive a MSG_VIS_LOST_GADGET_EXCL when it loses the mouse by a forced grab. The object must release the grab later with MSG_VIS_RELEASE_MOUSE .

Source: Unrestricted.

Destination: Any visible object--typically sent by the object to itself.

Interception: Highly unlikely.

MSG_VIS_GRAB_LARGE_MOUSE

void	MSG_VIS_GRAB_LARGE_MOUSE();

A visible object sends itself this message (with @call ) when it wants to acquire the mouse grab and when it wants 32-bit coordinates. If the object wants 16-bit coordinates, it should use MSG_VIS_GRAB_MOUSE , above. The object must relinquish the mouse grab with MSG_VIS_RELEASE_MOUSE .

Source: Unrestricted.

Destination: Any visible object--typically sent by the object to itself.

Interception: Highly unlikely.

MSG_VIS_FORCE_GRAB_LARGE_MOUSE

void	MSG_VIS_FORCE_GRAB_LARGE_MOUSE();

A visible object sends itself this message (with @call ) when it wants to forcibly grab the mouse input and when it wants large (32-bit) input coordinates. If it wants normal (16-bit) coordinates, it should use MSG_VIS_FORCE_GRAB_MOUSE , above. The object will acquire the mouse grab even if another object currently has it; the other object will receive a MSG_VIS_LOST_GADGET_EXCL when it loses the mouse by a forced grab. The object must release the grab later with MSG_VIS_RELEASE_MOUSE .

Source: Unrestricted.

Destination: Any visible object--typically sent by the object to itself.

Interception: Highly unlikely.

MSG_VIS_VUP_ALTER_INPUT_FLOW

void	MSG_VIS_VUP_ALTER_INPUT_FLOW(@stack
        PointDWord		translation,
        WindowHandle		window,
        optr		object,
        word		grapTypeAndFlags);

This message is the primitive employed by the grab/release messages shown above. Objects will rarely use this unless they are large objects (using 32-bit coordinates). Large objects and composites should subclass this message. In the case of a mouse grab, they should add in their 32-bit offset amounts to the translation structure before passing the event on to their superclasses. This will result in large mouse events being properly translated for the duration of the mouse grab.

Source: Unrestricted.

Destination: Any visible object--typically a VisContent which uses a 32-bit graphics space for its children.

Parameters: translation The offsets to the origin of the "local" 16-bit graphics space.

window
The handle of the view window associated with this input flow alteration.
object
The optr of the object altering the input flow.
grabTypeAndFlags
The type and flags for the input flow grab affected by this alteration. The high byte contains a record of VisInputFlowGrabType , described below; the low byte contains a VisInputFlowGrabFlags record, also described below.

Return: Nothing.

Interception: Any VisContent object that uses a large graphics space should subclass this method. In the handler, it should apply the stated translation, then call its superclass. Each subsequent mouse event will have the translation applied before being passed on to the composite's children.

Structures: The types of VisInputFlowGrabType are listed below:

VIFGT_ACTIVE
The affected grab type is the active grab.
VIFGT_PRE_PASSIVE
The affected grab type is the pre-passive grab.
VIFGT_POST_PASSIVE
The affected grab type is the post-passive grab.
The flags of VisInputFlowGrabFlags are listed below:
VIFGF_NOT_HERE
Flag set to indicate that the message should not be handled in this method but rather passed up the tree with this flag cleared. It is used to distinguish between requests made by other objects and requests made by an object sent to itself. It also allows an object to redirect the message to another node not in the visible hierarchy by handling the message and passing it in a direction other than up the visible tree. This overrides all the other flags in this record.
VIFGF_FORCE
Flag set if the grab should be forced. Note that both VIFGF_GRAB and VIFGT_ACTIVE must also be specified.
VIFGF_GRAB
Flag set if the grab is being made, clear if the grab is being released. In either case, object must be passed--if a release, the grab will not be released unless the objects match. Note also that only one object may have the active grab, but many objects may have either passive grab at a given time.
VIFGF_KBD
Flag set if action is a keyboard grab or release.
VIFGF_MOUSE
Flag set if action is a mouse grab or release.
VIFGF_LARGE
Flag set if large mouse events are affected by the flow alteration. VIFGF_MOUSE must also be set.
VIFGF_PTR
Flag set if mouse grab and pointer events are requested. If flag clear, only button-related events will be sent. VIFGF_MOUSE must also be set.

MSG_VIS_RELEASE_MOUSE

void	MSG_VIS_RELEASE_MOUSE();

A visible object sends itself this message if it has the mouse grab and wants to release it. Typically, this message will be called in the object's MSG_META_END_SELECT handler. It works for both large and small grabs.

Source: Any visible object.

Destination: Any visible object--typically sent by the object to itself.

Interception: Unlikely.

Code Display 15-2 Grabbing and Releasing the Mouse

/* This code display shows the  MSG_META_START_SELECT, MSG_META_END_SELECT,
 * and MSG_VIS_LOST_GADGET_EXCL handlers from the TicTac sample application.
 *
 * The sequence of input events and the messages they generate follow the
 * following basic patterns:
 * 1.	User presses select button
 *	MSG_META_START_SELECT generated, sent to object under mouse pointer
 *	If object under pointer is a game piece,
 *		Game piece grabs gadget exclusive
 *		Game piece grabs mouse
 * 2.	User holds button and drags mouse
 *	MSG_META_DRAG_SELECT generated, sent to object with mouse grab
 *		Game piece sets internal "dragging" flag
 *		Game piece draws outline
 *	MSG_META_PTR messages generated during drag, sent to object with mouse grab
 *		Game piece erases previous outline
 *		Game piece draws new outline at new pointer position
 * 3.	User releases button
 *	MSG_META_END_SELECT generated, sent to object with mouse grab
 *		Game piece releases gadget exclusive (MSG_VIS_RELEASE_GADGET_EXCL)
 *		MSG_VIS_LOST_GADGET_EXCL generated, sent to mouse grab (game piece)
 *		Game piece erases old outline, if any
 *		Game piece moves itself, erases old bounds, draws new bounds
 *		Game piece releases mouse grab (MSG_VIS_RELEASE_MOUSE)
 */
/***********************************************************************
 *		MSG_META_START_SELECT for TicTacPieceClass
 ***********************************************************************
 * SYNOPSIS:		Grabs the mouse and calls for future pointer events.
 * 		When the user clickes in the view, TicTacView will pass
 * 		the click event to TicTacBoard. Since TicTacBoardClass
 * 		does not intercept the event, VisContentClass passes
 * 		it on to its child object currently under the pointer.
 * PARAMETERS:
 * 	void	(MouseReturnParams *retVal,
 *		 word xPosition, word yPosition, word inputState);
 *
 * STRATEGY:		When the piece object receives this message, it means
 * 		it has been clicked on by the user and the mouse button
 * 		is still down. The piece must grab the mouse so that it
 * 		gets all future mouse events, and it must request that
 * 		all future mouse events be sent to it. This ensures
 * 		that if the pointer leaves the object's bounds while
 * 		the button is still pressed, the piece object will still
 * 		receive all the pointer events (otherwise they would be
 * 		sent to whatever object was under the new pointer position).
 ***********************************************************************/
@method TicTacPieceClass, MSG_META_START_SELECT {
    /* Grab the gadget exclusive so we're allowed to grab the mouse. */
    @call @visParent::MSG_VIS_TAKE_GADGET_EXCL(oself);
    /* Grab the mouse. This requests that all future pointer
     * events be passed directly to this game piece. */
    @call self::MSG_VIS_GRAB_MOUSE();
    /* Finally, return that this particular click event has been processed.
     * If this flag is not returned, the system will send out the click
     * event again. */
    retVal->flags = MRF_PROCESSED;					 /* this event processed */
}
/***********************************************************************
 *		MSG_META_END_SELECT for TicTacPieceClass
 ***********************************************************************
 * SYNOPSIS:		This message is received when the selection button has
 * 		been released and this game piece had the mouse grab.
 * 		All it does is release the gadget exclusive.
 * PARAMETERS:
 * 	void	(MouseReturnParams *retVal,
 * 		 word xPosition, word yPosition, word inputState);
 ***********************************************************************/
@method TicTacPieceClass, MSG_META_END_SELECT {
    @call @visParent::MSG_VIS_RELEASE_GADGET_EXCL(oself);
    retVal->flags = MRF_PROCESSED;					/* this event processed */
}
/***********************************************************************
 *		MSG_VIS_LOST_GADGET_EXCL for TicTacPieceClass
 ***********************************************************************
 * SYNOPSIS:		This message is received when the selection button has
 * 		been released and this game piece had the mouse grab.
 * 		It first checks to see if the new, proposed bounds are
 * 		on the game board. If the bounds are valid, then
 * 		it sets the objects VI_bounds field to the new values
 * 		and causes the object to erase its original drawing
 * 		and draw itself at its new bounds. If the bounds are
 * 		not on the game board, it will retain the original bounds
 * 		and redraw using them.
 * PARAMETERS:		void ();
 ***********************************************************************/
@method TicTacPieceClass, MSG_VIS_LOST_GADGET_EXCL {
    WindowHandle win;				/* window handle of view window */
    GStateHandle gstate;				/* temporary gstate to draw to */
    /* See if piece was being dragged or not. If so, clear the outline. */
    if (pself->TTP_dragging) {
        /* Get the window handle of the view window and create a
         * temporary gstate for it to draw to. */
    win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW();
    gstate = GrCreateState(win);
        /* Set the line color and mode for drawing the outline. */
    GrSetLineColor(gstate, CF_INDEX, C_BLACK, 0, 0);
    GrSetMixMode(gstate, MM_INVERT);
        /* Erase outline on screen. */
        /* If the game piece type is TTPT_BOX, draw a rectangle
         * outline. Otherwise draw an ellipse outline. */
    if (pself->TTP_pieceType == TTPT_BOX) {
        GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
             ((pself->TTP_horizPos) + PIECE_WIDTH),
             ((pself->TTP_vertPos) + PIECE_HEIGHT));
    } else {
        GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
             ((pself->TTP_horizPos) + PIECE_WIDTH),
             ((pself->TTP_vertPos) + PIECE_HEIGHT));
    }
        /* Check to see if the new bounds are on the game board. If
         * they are, set the object's bounds to the new values. If
         * the are not, retain the original values and set the values
         * to those last stored. */
    if (@call TicTacBoard::MSG_TICTAC_VALIDATE_BOUNDS(
            ((pself->TTP_vertPos) + PIECE_HEIGHT),
            ((pself->TTP_horizPos) + PIECE_WIDTH),
            pself->TTP_vertPos, pself->TTP_horizPos)) {
        /* Invalidate the original drawing of the game piece. Send
         * the VI_bounds rectangle as the parameters because they have
         * not been changed since the START_SELECT. This message is
         * the equivalent of calling GrInvalRect() with the same bounds */
        @call self::MSG_VIS_BOUNDS_CHANGED(pself->VI_bounds.R_bottom,
                    pself->VI_bounds.R_right,
                    pself->VI_bounds.R_top,
                    pself->VI_bounds.R_left);
        /* Set the game piece object's bounds to the new coordinates. */
        pself->VI_bounds.R_left = pself->TTP_horizPos;
        pself->VI_bounds.R_right = (pself->TTP_horizPos) + PIECE_WIDTH;
        pself->VI_bounds.R_top = pself->TTP_vertPos;
        pself->VI_bounds.R_bottom = (pself->TTP_vertPos) + PIECE_HEIGHT;
    } else {
        /* If the bounds are not on the game board, then reset the
         * current positions to be the original bounds. */
        pself->TTP_horizPos = pself->VI_bounds.R_left;
        pself->TTP_vertPos = pself->VI_bounds.R_top;
    }
        /* Now, the game piece must draw itself at its newly-set
         * bounds (will draw itself over its original picture if the
         * new bounds were invalid). */
    @call self::MSG_VIS_DRAW(0, gstate);
        /* Finally, destroy the temporary gstate used for drawing. */
    GrDestroyState(gstate);
        /* Clear flag to indicate we are no longer dragging. */
    pself->TTP_dragging = FALSE;
    }
    /* Release the mouse grab now that the move has finished.
     * Other objects in the view (other game pieces, for example)
     * may now receive pointer, select, and drag events. */
    @call self::MSG_VIS_RELEASE_MOUSE();
}

Passive Grabs

MSG_VIS_ADD_BUTTON_PRE_PASSIVE, MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE, MSG_VIS_ADD_BUTTON_POST_PASSIVE, MSG_VIS_REMOVE_BUTTON_POST_PASSIVE

The type of mouse grab described in the previous section is also known as an "active" mouse grab. An object can also have two other types of mouse grabs: pre-passive and post-passive . While only one object may have the active mouse grab at any given time, any number of objects may have pre- or post-passive grabs on the mouse.

An object with a pre-passive mouse grab receives a copy of the input event before the event goes to its true destination. The copy will be a message of the form MSG_META_PRE_PASSIVE_... (e.g., if the event is MSG_META_PTR , the pre-passive event is MSG_META_PRE_PASSIVE_PTR ). All pre-passive events will be handled before the true event is delivered to the object with the active grab. If any of the pre-passive copies of the event is returned with the flag MRF_PREVENT_PASS_THROUGH, the active event will not be sent to its destination. You can use this feature to set up input filters, having an object receive all mouse events pre-passive and filtering out those that should not be delivered. The message MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE removes any pre-passive mouse grab from the object.

A post-passive mouse grab is similar to a pre-passive except that the post-passive object receives the event after it is sent to its normal destination. The post-passive object will receive a MSG_META_POST_PASSIVE_... with the same flags and position information as sent with the input event. An object that has the post-passive grab can release it by sending itself a MSG_VIS_REMOVE_BUTTON_POST_PASSIVE .

MSG_VIS_ADD_BUTTON_PRE_PASSIVE

void	MSG_VIS_ADD_BUTTON_PRE_PASSIVE();

A visible object sends itself this message (with @call ) when it wants to gain a pre-passive mouse grab. The object will receive all subsequent mouse events in the form of MSG_META_PRE_PASSIVE_... before the event's destination object receives them; this message will carry the same input information as passed with the input event. The object should handle MSG_META_PRE_PASSIVE_... and, if the event is not to be sent normally to its destination, should return the flag MRF_PREVENT_PASS_THROUGH from that handler. The pre-passive grab will be released when the object sends itself a MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE .

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself when it wants to gain a pre-passive mouse grab.

Interception: Unlikely--Note that the object must, however, intercept any MSG_META_PRE_PASSIVE... events it is interested in.

MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE

void	MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE();

This message removes the pre-passive grab from a visible object.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself when it has a pre-passive grab and wants to release it.

Interception: Unlikely.

MSG_VIS_ADD_BUTTON_POST_PASSIVE

void	MSG_VIS_ADD_BUTTON_POST_PASSIVE();

A visible object sends itself this message (with @call ) when it wants to gain the post-passive mouse grab. The object will receive all subsequent mouse events in the form of MSG_META_POST_PASSIVE_... after the event's destination object receives them; this message will carry the same input information as passed with the input event. The object should handle the appropriate MSG_META_POST_PASSIVE_... to properly receive the events. The post-passive grab will be released when the object receives a MSG_VIS_REMOVE_BUTTON_POST_PASSIVE .

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself when it wants to receive post-passive input events.

Interception: Unlikely--Note that the object must, however, handle any MSG_META_POST_PASSIVE_... events it is interested in.

MSG_VIS_REMOVE_BUTTON_POST_PASSIVE

void	MSG_VIS_REMOVE_BUTTON_POST_PASSIVE();

This message removes any post-passive mouse grab from the object.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to release a post-passive mouse grab.

Interception: Unlikely.

The Gadget Exclusive and Focus Hierarchy

MSG_VIS_TAKE_GADGET_EXCL, MSG_VIS_RELEASE_GADGET_EXCL, MSG_VIS_LOST_GADGET_EXCL, MSG_VIS_VUP_QUERY_FOCUS_EXCL, MSG_VIS_FUP_QUERY_FOCUS_EXCL, MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER

The gadget exclusive is a marker in the system used by the UI to indicate which object has exclusive access to input gadget functionality. Only one gadget at a time may have the gadget exclusive, and the gadget exclusive may be forcibly taken from an object by another object (for example, when a system-modal dialog comes up during a drag operation).

At times, an object will force the mouse input to go to a different object from that which has the current active grab. This is done almost exclusively by the Specific UI library and is rarely, if ever, done by applications. Three messages can be used in determining which object has the current "gadget exclusive," or active mouse grab. These are shown below.

In addition, individual objects can get information about the current state of the Focus hierarchy with MSG_VIS_VUP_QUERY_FOCUS_EXCL and MSG_VIS_FUP_QUERY_FOCUS_EXCL , also shown below.

To deal with a quick-transfer operation, a visible object may also have to use the message MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER . This message may be used to remove the mouse grab from the GenView in which the visible object resides, thereby allowing mouse events to reach other objects outside the view window.

MSG_VIS_TAKE_GADGET_EXCL

void	MSG_VIS_TAKE_GADGET_EXCL(
        optr	child);

This message causes the passed object to be given the gadget exclusive. Any object currently having the gadget exclusive will subsequently receive a MSG_VIS_LOST_GADGET_EXCL . This is used primarily by the Specific UI.

Source: Unrestricted. Typically sent by a visible object that will next grab the mouse.

Destination: Any visible object--normally sent by an object to its visible parent.

Parameters: child The optr of the object to which the gadget exclusive will be given (normally the calling object).

Return: Nothing.

Interception: Unlikely.

See Also: MSG_VIS_GRAB_MOUSE , MSG_VIS_LOST_GADGET_EXCL

MSG_VIS_RELEASE_GADGET_EXCL

void	MSG_VIS_RELEASE_GADGET_EXCL(
        optr	child);

This message causes the passed object to release the gadget exclusive. The object specified by child will then receive a MSG_VIS_LOST_GADGET_EXCL .

Source: Unrestricted. Typically sent by an object that has a mouse grab and expects to release it.

Destination: Any visible object--normally sent by the object to it's parent.

Parameters: child The optr of the object which is to lose the gadget exclusive (normally the calling object).

Return: Nothing.

Interception: Unlikely.

See Also: MSG_VIS_RELEASE_MOUSE , MSG_VIS_LOST_GADGET_EXCL

MSG_VIS_LOST_GADGET_EXCL

void	MSG_VIS_LOST_GADGET_EXCL();

This message, when received, indicates that the recipient has lost its hold on the gadget exclusive. When an object receives this, it should release the active mouse grab, if appropriate.

Source: Unrestricted--typically sent by the UI.

Destination: The visible object losing the gadget exclusive.

Interception: Mouse grabs should be released in this handler.

MSG_VIS_VUP_QUERY_FOCUS_EXCL

void	MSG_VIS_VUP_QUERY_FOCUS_EXCL(
        ObjectAndGrabParams *retValue);

This message queries up the visible hierarchy to find the object having the current focus in the current window. The current window is taken to be the window in which the object receiving the message resides.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to find the focus optr in its window.

Parameters: retValue A pointer to an empty ObjectAndGrabParams structure, shown below.

Return: The ObjectAndGrabParams structure pointed to by retVal will be filled with the appropriate data.

Interception: Unlikely.

Structures: The structure of the ObjectAndGrabParams is shown below:

typedef struct {
    word		OAGP_grabFlags;  /* flags */
    word		OAGP_unused;     /* reserved */
    optr		OAGP_object;     /* object with focus */
}  ObjectAndGrabParams;

The OAGP_grabFlags field contains a record of HierarchicalGrabFlags

, defined in the Input chapter. The allowable flags are

HGF_SYS_EXCL
Indicates this object has the system exclusive.
HGF_APP_EXCL
Indicates this object has the application's exclusive.
HGF_GRAB
Indicates this object wishes to grab the exclusive (if clear, the object wishes to release), when passed as a parameter. In the structure, it merely indicates that the object in OAGP_object has the exclusive.
HGF_OTHER_INFO
Twelve bits used by the specific UI depending on the type of hierarchical grab.

MSG_VIS_FUP_QUERY_FOCUS_EXCL

void	MSG_VIS_FUP_QUERY_FOCUS_EXCL(
        ObjectAndGrabParams *retValue);

This message queries the focus hierarchy to see which object has the current focus. The current focus object does not have to be in the caller's visible tree.

Source: Unrestricted.

Destination: Any visible object.

Parameters: retValue A pointer to an empty ObjectAndGrabParams structure, shown above in the entry for MSG_VIS_VUP_QUERY_FOCUS_EXCL .

Return: The ObjectAndGrabParams structure pointed to by retVal will be filled with the appropriate data.

Interception: Unlikely. This message is not deliverd to all visible objects, rather only those marked as VTF_WIN_GROUP or VTF_IS_INPUT_NODE. If you are implementing a new focus node, and need this message, and are not a window group, you will need to set the VTF_IS_INPUT_NODE bit in your init routine.

Structures: The structure of the ObjectAndGrabParams is shown in the entry for MSG_VIS_VUP_QUERY_FOCUS_EXCL .

MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER

void	MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER();

This message is sent by a visible object to itself when a quick-transfer operation is underway and the pointer has to be allowed to leave the bounds of the object's view window. This message will travel up to the content object, which will indicate to the GenView that the pointer events must be allowed to go to other windows in the system.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself when the global transfer must be allowed outside its window.

Interception: Unlikely.

The Mou se Status

MSG_VIS_VUP_SET_MOUSE_INTERACTION_BOUNDS, MSG_VIS_VUP_GET_MOUSE_STATUS, MSG_VIS_VUP_TERMINATE_ACTIVE_MOUSE_FUNCTION, MSG_VIS_VUP_BUMP_MOUSE

The UI can control the mouse image and events as they occur. Because the UI is based on the visible classes, the messages that control the mouse status are also available to you, though they will probably be of very little utility. These messages are given below.

MSG_VIS_VUP_SET_MOUSE_INTERACTION_BOUNDS

void	MSG_VIS_VUP_SET_MOUSE_INTERACTION_BOUNDS(@stack
        int	bottom,
        int	right,
        int	top,
        int	left);

A visible object requesting the mouse grab sends this message up the visible tree. This message will be handled by a content object. It indicates a new set of draggable bounds in case the user initiates a drag-scrolling operation. This message is sent automatically when the visible object requests the grab.

Source: A visible object requesting a mouse grab.

Destination: The sender sends this message to itself.

Parameters: bottom, right, top, left
 The four bounds of the new bounding rectangle. Default is the bounds of the object sending the message.

Return: Nothing.

Interception: Unlikely.

MSG_VIS_VUP_GET_MOUSE_STATUS

word	MSG_VIS_VUP_GET_MOUSE_STATUS();

This message is rarely used by any objects outside the UI library. It returns the information passed with the last mouse event. The word return value contains two byte-sized sets of flags. The high byte is a list of the active UI functions, and the low byte represents the current button information.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself when it can not locally store the latest mouse information.

Parameters: None.

Return: A word containing two values: The high word is a record of type UIFunctionsActive , and the low word is a word of ButtonInfo .

Interception: Unlikely.

MSG_VIS_VUP_TERMINATE_ACTIVE_MOUSE_FUNCTION

void	MSG_VIS_VUP_TERMINATE_ACTIVE_MOUSE_FUNCTION();

This message is sent by a visible object to itself to terminate any active mouse function, forcing it to be a function of type "other." This message is used only by the Specific UI library in cases where input synchronization problems occur in specific places. Applications should generally not use this message.

Source: A visible object handling active input events.

Destination: Sent by the visible object to itself.

Interception: This message should not be subclassed.

MSG_VIS_VUP_BUMP_MOUSE

void	MSG_VIS_VUP_BUMP_MOUSE(
        int	xBump,
        int	yBump);

This message causes the UI to bump the pointer image on the screen by the passed offsets as if the user had moved the mouse. It's unlikely your objects will subclass this message; you may, however, use it to bump the mouse. It is most often used by specific UI objects (menus, scrollers) that make the mouse follow their movements.

Source: Unrestricted.

Destination: Any visible object.

Parameters: xBump The horizontal amount to bump the mouse.

yBump
The vertical amount to bump the mouse.

Return: Nothing.

Interception: Unlikely.

Keyboard Events

MSG_META_GRAB_KBD, MSG_META_FORCE_GRAB_KBD, MSG_META_RELEASE_KBD

The keyboard, like the mouse, may be grabbed by visible objects. Keyboard events arrive in the form MSG_META_KBD_CHAR ; a single message represents all different types of keyboard events (unlike mouse events, which can be much more diverse).

To grab the keyboard, the visible object should send itself the message MSG_META_GRAB_KBD . If the grab should be made in any circumstances, the object should use MSG_META_FORCE_GRAB_KBD . To release the keyboard grab, the object should send itself MSG_META_RELEASE_KBD .

MSG_META_GRAB_KBD

void	MSG_META_GRAB_KBD();

A visible object will send itself this message (using @call ) when it wants to gain the keyboard grab. To release the grab, it must send itself a MSG_META_RELEASE_KBD . If it wants to gain the exclusive regardless of whether another object has it, it should use MSG_META_FORCE_GRAB_KBD .

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to gain the keyboard grab.

Interception: Unlikely--Note that the object must, however, intercept MSG_META_KBD_CHAR to receive the subsequent keyboard events.

MSG_META_FORCE_GRAB_KBD

void	MSG_META_FORCE_GRAB_KBD();

A visible object will send itself this message (using @call ) when it wants to gain the keyboard grab whether or not another object currently has it. To release the grab, the object must later send itself MSG_META_RELEASE_KBD .

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to force a keyboard grab.

Interception: Unlikely.

MSG_META_RELEASE_KBD

void	MSG_META_RELEASE_KBD();

A visible object that has the keyboard grab must send itself this message to release the grab.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to release its keyboard grab.

Interception: Unlikely.

Ink and P en Events

MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK

If a visible object expects and wants Ink input, it should subclass the message MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK . This message queries the visible object, which should return specific values based on the current input state and object bounds.

This message has two default handlers; the default VisClass handler always returns false , indicating that the visible object does not expect Ink input. You can, however, substitute a second default handler for the VisClass handler by adding the following line to your Goc file after the object's class definition:

@method  VisObjectHandlesInkReply, <YourClass>,
                MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK;

This will, in essence, substitute the system-provided routine VisObjectHandlesInkReply() for a subclassed message handler. This routine will do the right thing for the object in interacting with the input manager. All subsequent input events will be considered Ink events.

MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK

void	MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK(
        VisCallChildrenInBoundsFrame					*data);

This message is subclassed by objects that handle Ink input. See above for directions on handling this message. The parameters are five data words that may be modified and the bounds of a rectangle. This message will be sent to all objects whose bounds fall within the passed rectangle.

Source: Unrestricted.

Destination: Any visible object.

Parameters: data A pointer to a VisCallChildrenInBoundsFrame structure containing the bounding rectangle and five words of data. This structure is described below.

Return: Three fields of the VisCallChildrenInBoundsFrame structure pointed to by data will have meaning on return. See the Interception paragraph below to determine whether the return values should be set or not in a subclassed handler.

VCCIBF_data1
Memory handle of the top object desiring ink.
VCCIBF_data2
Chunk handle of the top object desiring ink.
VCCIBF_data3
Upper bound of the object desiring ink.

Interception: If an object wants to receive ink messages, it should do the following things in its handler for this message: First compare the passed VCCIBF_data3 value with the top bound of the object. Then, if the upper bound of the object is less than the VCCIBF_data3 value (or if the value in VCCIBF_data1 is zero), then the object should return the values indicated in the Return paragraph above. Otherwise, if these conditions are not met, it should return with the structures and registers unchanged.

Structures: The VisCallChildrenInBoundsFrame structure is shown below:

typedef struct {
    word		VCCIBF_data1;
    word		VCCIBF_data2;
    word		VCCIBF_data3;
    word		VCCIBF_data4;
    word		VCCIBF_data5;
        /* Five data words as described above */
    Rectangle		VCCIBF_bounds;
        /* bounds must be in the coordinate
         * system of the parent of the caller */
} VisCallChildrenInBoundsFrame;
| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Positioning Visible Objects | Next: 5 Working with Visible Object Trees


Working with Visible Object Trees

VisClass: 5 Working with Visible Object Trees

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Using VisClass | Next: 6 Visible Layers and 32-Bit Graphics

Visible objects can not be used unless they are placed within object trees. The top object of the tree must be of a subclass of VisContentClass . Other objects in the tree can be of any visible object class. Keep in mind, however, that VisClass objects can not have children; only the composite classes ( VisCompClass and VisContentClass ) may have children. Therefore, VisClass is useful only for leaf objects.

Each visible object has a VI_link instance field. This field specifies the optr of that object's next sibling in the tree. Since this is a field of VisClass , both composites and noncomposites have it. Composite objects also have a VCI_comp field which contains the optr of the composite's first child. Thus, visible trees use the same format as normal object trees.

Creating and Destroying

Adding and Removing

Getting Visible Tree Information

Sending Messages Through the Tree

Visible Object Window Operations

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Using VisClass | Next: 6 Visible Layers and 32-Bit Graphics


Creating and Destroying

VisClass: 5.1 Working with Visible Object Trees: Creating and Destroying

| Up: GEOS SDK TechDocs | Up | Prev: 5 Working with Visible Object Trees | Next: 5.2 Adding and Removing
MSG_VIS_DESTROY

Visible objects are just like any other objects in the ways they are created and destroyed. You can create visible objects in many ways; the easiest is to set them up in a resource and simply load or duplicate the resource block at run-time. You can also use the ObjInstantiate() routine to instantiate new visible objects on the fly from your predefined subclass of VisClass , VisCompClass , or VisContentClass . the GEOS Programming chapter details how to instantiate a new object of a given class.

Destroying visible objects is very simple. You only need to use the message MSG_VIS_DESTROY , which works on a visible object and all its children (if it has any). This message removes the visible branch from the screen if necessary, detaches the visible objects from the visible tree, and then destroys the object (freeing its instance chunk). The only thing the object must do is clean up after itself--in its MSG_META_DETACH or MSG_META_FINAL_OBJ_FREE handler, it should free any memory that it allocated explicitly for its own use, for example.

MSG_VIS_DESTROY also marks the destroyed object's parent invalid both in geometry and image so the tree will be updated properly.

MSG_VIS_DESTROY

void	MSG_VIS_DESTROY(
        VisUpdateMode updateMode);

This message is the high level routine for destroying branches of visible trees. This message will close and destroy the entire branch, unlinking any visible objects in the branch under the recipient object. The parent of the removed branch will be marked invalid for later visual update. This message may be subclassed to replace the geometry update handling, but this is not easily done and is rare.

Source: Unrestricted.

Destination: Any visible object (or head of a visible branch).

Parameters: updateMode A VisUpdateMode that determines when the visible parent of the destroyed object/branch will be updated on the screen.

Return: Nothing.

Interception: Unlikely--may be subclassed if the parent is not to be marked invalid. This is extremely rare.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Working with Visible Object Trees | Next: 5.2 Adding and Removing


Adding and Removing

VisClass: 5.2 Working with Visible Object Trees: Adding and Removing

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Creating and Destroying | Next: 5.3 Getting Visible Tree Information
MSG_VIS_ADD_CHILD, MSG_VIS_REMOVE, MSG_VIS_REMOVE_CHILD, MSG_VIS_MOVE_CHILD, MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD, MSG_VIS_REMOVE_NON_DISCARDABLE_VM_CHILD, MSG_VIS_REMOVE_NON_DISCARDABLE

Once you have objects instantiated, you can connect them together into an object tree. If you know the structure of your tree beforehand, you can create the tree explicitly in your .goc file, just as you would create your generic object tree.

For many purposes, however, a dynamic object tree is much more useful. To use it, you must be able to add and remove objects easily as well as move them within the tree easily.

Adding and Removing Normally

To add an object to a branch of a visible tree, use the message MSG_VIS_ADD_CHILD . This message adds a visible object as the child of a composite; if the new child is a composite with its own children, the entire branch is added to the tree. You can add a child at any position in the composite's child list (e.g., as the first child, as the second child, as the last child, etc.), and you can mark the child dirty after addition if you want.

For example, if you have a composite visible object with two children that is currently unattached to any visible tree, you can attach it to a tree with MSG_VIS_ADD_CHILD . The figure above shows this process and the line of code used to add the composite as the second child of an object in the tree.

Note that the child in the example is added as child one. The child list of a composite is zero-based, so the first child is referred to as number zero, the second is number one, and so on. The last child may always be referred to as CCO_LAST; likewise, the first child may always be referred to as CCO_FIRST.

To remove an object or branch from a visible tree, you can use either MSG_VIS_REMOVE or MSG_VIS_REMOVE_CHILD . MSG_VIS_DESTROY may also be used, but only if the entire branch should be destroyed after being removed from the tree. Both MSG_VIS_REMOVE and MSG_VIS_REMOVE_CHILD detach the object from the visible tree and visually update the parent immediately. MSG_VIS_REMOVE , however, should be sent directly to the object being removed, while MSG_VIS_REMOVE_CHILD is sent to the parent of the object being removed. When removing a child from its parent, you can specify the child by position number (e.g., remove the last child or the first child).

You can move a child within its child list with MSG_VIS_MOVE_CHILD . This message is sent to the parent and simply moves the child within the parent's child list. To move a child from one parent to another, you must first remove it and then add it to the other parent with MSG_VIS_REMOVE_CHILD and MSG_VIS_ADD_CHILD . This message, as the others, will move the child's entire branch with it.

Adding and Removing Objects Not Saved to Documents

In many cases, you may save visible objects directly to VM documents using GenDocument objects and the document controllers. In cases like this, the objects you save to the file must not be discarded by the UI if the visible object is taken off the tree or removed. These objects are known as "non-discardable" visible objects stored in a VM file, and unless they're handled specially, they can cause your documents to crash your application.

Any top-level object you save directly to a file must be treated as non-discardable. That is, any object that gets dynamically added to or removed from a visible tree (such as a vis object that gets added as the child of a GenDocument when the document is opened) must be added, removed, and managed as a non-discardable object. There are three messages that deal specifically with non-discardable visible objects; these are detailed below and are MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD , MSG_VIS_REMOVE_NON_DISCARDABLE_VM_CHILD , and MSG_VIS_REMOVE_NON_DISCARDABLE .

MSG_VIS_ADD_CHILD

void	MSG_VIS_ADD_CHILD(
        optr		child,
        CompChildFlags		flags);

This message attaches the passed object as a child of the composite handling the message. If the parent is already opened and on the screen, you must invalidate the child with MSG_VIS_MARK_INVALID , passing the invalidation VOF_WINDOW_INVALID; the composite and child will be updated appropriately according to the VisUpdateMode passed with the invalidation. If, however, the parent is not opened, the child will automatically be opened when the parent is opened.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: child The optr of the visible object to be added as a child.

flags
A structure of CompChildFlags , described below.

Return: Nothing.

Interception: You should not subclass this message.

Structures: The CompChildFlags structure contains one one-bit flag and one 15-bit unsigned numerical field. These are described below and can be found in metaC.goh .

 typedef WordFlags  CompChildFlags;
#define CCF_MARK_DIRTY					0x8000
#define CCF_REFERENCE					0x7fff
#define CCO_FIRST					0x0000
#define CCO_LAST					0x7FFF
#define CCF_REFERENCE_OFFSET					     0
CCF_MARK_DIRTY
If this flag is set, the object will be marked dirty when added to the tree.
CCF_REFERENCE
The zero-based position of the new child. Other children will be shuffled forward in the child list as necessary. If greater than the number of children, it will be taken to be CCO_LAST.
Two special numbers may be used in the CCF_REFERENCE field:
CCO_FIRST
Add as the first child.
CCO_LAST
Add as the last child.

Warnings: Do not pass the optr of an object that is already the child of another object. The results are unpredictable and will likely result in an error.

MSG_VIS_REMOVE

void	MSG_VIS_REMOVE(
        VisUpdateMode updateMode);

This is the high-level message that closes (if necessary) and removes a visible branch from the object tree. The parent of the branch is marked invalid for visual update according to the passed VisUpdateMode .

Unlike MSG_VIS_DESTROY , this message does not destroy the visible branch but only unlinks it from the tree. This message may not be subclassed by any object. This message is useful for hiding visible branches which may be added again later with MSG_VIS_ADD_CHILD .

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: updateMode The VisUpdateMode used to update the composite from which the child is removed. VUM_MANUAL is not allowed.

Return: Nothing.

Interception: Do not subclass this message.

Warnings: This message will not allow VUM_MANUAL to be passed as the updateMode .

MSG_VIS_REMOVE_CHILD

void	MSG_VIS_REMOVE_CHILD(
        optr		child,
        CompChildFlags		flags);

This message removes the specified child from the object tree. This message should be rarely used and used with care; it does not close the visible branch but simply removes it. Consider using the higher-level MSG_VIS_REMOVE instead. Note that all grabs (focus, gadget, mouse, etc.) must be released by the child and all its children before the branch can be removed safely.

Source: Unrestricted.

Destination: The parent composite of the passed object.

Parameters: child The optr of the child to be removed. If this optr is not among the recipient's children, an error will likely occur.

flags
The CompChildFlags as described in MSG_VIS_ADD_CHILD on typedef WordFlags CompChildFlags; #define CCF_MARK_DIRTY 0x8000 #define CCF_REFERENCE 0x7fff .

Return: Nothing.

Interception: You should not subclass this message.

Warnings: Most likely you should use MSG_VIS_REMOVE instead of this message. MSG_VIS_REMOVE takes care of extra bookkeeping automatically that can be difficult.

MSG_VIS_MOVE_CHILD

void	MSG_VIS_MOVE_CHILD(
        optr		child,
        CompChildFlags		flags);

This message moves a child of the recipient to another location among its siblings. It essentially removes the child from the branch and then re-adds it in the same manner as MSG_VIS_ADD_CHILD . This message does not move the child from one parent to another; you must use a combination of MSG_VIS_REMOVE and MSG_VIS_ADD_CHILD to achieve that.

Source: Unrestricted.

Destination: The parent composite of the passed object.

Parameters: child The optr of the child to be removed. If this optr is not among the recipient's children, an error will likely occur.

flags
The CompChildFlags as described in MSG_VIS_ADD_CHILD on typedef WordFlags CompChildFlags; #define CCF_MARK_DIRTY 0x8000 #define CCF_REFERENCE 0x7fff .

Return: Nothing.

Interception: You should not subclass this message.

MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD

void	MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD(
        optr		child,
        CompChildFlags		flags);

This message performs exactly the same as MSG_VIS_ADD_CHILD except that it also increments the object's in-use count so it will never be discarded. This is used on top objects in visible sub-trees that are saved to document files: The object is added as a non-discardable file when the document is opened, and it is removed as a non-discardable child (with MSG_VIS_REMOVE_NON_DISCARDABLE_VM_CHILD or MSG_VIS_REMOVE_NON_DISCARDABLE ) when the document is closed.

Source: Unrestricted--typically a document object when opening the file.

Destination: The new parent of the visible object being added to the tree.

Parameters: child The optr of the new child being added to the tree.

flags
A record of CompChildFlags indicating where the child should be added. CCF_MARK_DIRTY is ignored in this record.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_REMOVE_NON_DISCARDABLE_VM_CHILD

void	MSG_VIS_REMOVE_NON_DISCARDABLE_VM_CHILD(
        optr	child);

This message performs exactly the same as MSG_VIS_REMOVE_CHILD except that it is used with MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD rather than with MSG_VIS_ADD_CHILD . This message decrements the in-use count before removing the child, thereby undoing the extra increment performed by MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD .

Source: Unrestricted--typically a document object when closing the file.

Destination: The parent of the visible object being removed from the tree.

Parameters: child The optr of the child being removed.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_REMOVE_NON_DISCARDABLE

void	MSG_VIS_REMOVE_NON_DISCARDABLE(
        VisUpdateMode		updateMode)

This message performs exactly the same as MSG_VIS_REMOVE except that it decrements the object's in-use count before removing it. This message should therefore be used in conjunction with the adding message MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD and should not be used with MSG_VIS_ADD_CHILD. Likewise, MSG_VIS_REMOVE should not be used with this MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD.

Source: Unrestricted--typically a document object when closing the file.

Destination: The visible object being removed from the tree.

Parameters: updateMode A VisUpdateMode value indicating when the object should be visibly removed from the tree. VUM_MANUAL is not allowed.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Creating and Destroying | Next: 5.3 Getting Visible Tree Information


Getting Visible Tree Information

VisClass: 5.3 Working with Visible Object Trees: Getting Visible Tree Information

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Adding and Removing | Next: 5.4 Sending Messages Through the Tree
 MSG_VIS_FIND_CHILD, MSG_VIS_FIND_CHILD_AT_POSITION, MSG_VIS_COUNT_CHILDREN, MSG_VIS_FIND_PARENT

When an application manages a visible object tree, it will most likely need to find children, count children, or get the optr of a particular object in the tree. VisClass offers four messages to provide this bookkeeping information. These four messages are listed below.

MSG_VIS_FIND_CHILD

word	MSG_VIS_FIND_CHILD(
        optr	object);

This message returns the zero-based position of the specified child among the parent's children.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: object The optr of the child to be found.

Return: The zero-based position of the child in the parent's child list. (E.g., the first child returns zero, the second child returns one, the third child returns two, etc.) If object is not the optr of one of the composite's children, the value -1 will be returned. If sent to a non-composite object, the message will also return -1.

Interception: Unlikely.

MSG_VIS_FIND_CHILD_AT_POSITION

optr	MSG_VIS_FIND_CHILD_AT_POSITION(
        word	position);

This message returns the optr of the child occupying the position specified among the recipient's children.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: position The zero-based position of the child whose optr is to be returned.

Return: The optr of the child occupying the passed position . If no child occupies that position, or if the message is sent to a non-composite object, the retuned value will be NullOptr.

Interception: Unlikely.

MSG_VIS_COUNT_CHILDREN

word	MSG_VIS_COUNT_CHILDREN();

This message returns the number of children the recipient object has. The count is not zero-based; if a composite has five children, this message will return five.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: None.

Return: The number of children the object has. If the object has no children (includes non-composite objects), the return will be zero.

Interception: Unlikely.

MSG_VIS_FIND_PARENT

optr	MSG_VIS_FIND_PARENT();

This message returns the optr of the recipient's parent composite.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: None.

Return: The optr of the object's parent composite. If the message is sent to the top node of a visible tree, the return value will be NullOptr.

Interception: Unlikely.

Tips: If you want to send a message to your parent, use the @visParent macro defined in the next section.

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Adding and Removing | Next: 5.4 Sending Messages Through the Tree


Sending Messages Through the Tree

VisClass: 5.4 Working with Visible Object Trees: Sending Messages Through the Tree

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Getting Visible Tree Information | Next: 5.5 Visible Object Window Operations

Often an object will need to contact its parent or children but will not know their precise optrs. Other times, you may need to send a message to a particular object in the tree but don't know where in the tree the object is. GEOS provides several ways to dispatch messages to parents and children and even to objects of a given class.

Contacting Parents and Children Directly

 @visParent,  @visChildren, MSG_VIS_CALL_PARENT, MSG_VIS_SEND_TO_PARENT, MSG_VIS_SEND_TO_CHILDREN

To send a given message to an object's parent (to check valid bounds, to check the parent's state, etc.), you can use the macro @visParent . To do this, substitute the macro in place of the destination object's name, as follows:

kids = @call @visParent::MSG_VIS_COUNT_CHILDREN();

The above call is more efficient than using two message calls for the same thing, as follows:

myParent = @call self::MSG_VIS_FIND_PARENT();
kids = @call myParent::MSG_VIS_COUNT_CHILDREN();

Note that either @call or @send may be used with the @visParent macro.

A similar macro, @visChildren , is offered for sending messages to a composite's children. Note, however, that you can only use the @send keyword to send messages with this macro; there is no way for a single call to collect return values from many different objects. So, if you need to pass pointers to your children, you will have to get each child's optr in turn and dispatch the message to it individually. (Recall that pointers should not be passed with @send because they may be invalidated by the time the message is handled.) The format for using the @visChildren macro is as follows:

@send @visChildren::MSG_VIS_INVALIDATE();

This usage is much more efficient than counting the composite's children and using a loop to get the child's optr and send the child the message.

In addition to providing the above macros, VisClass lets you pass recorded events to a visible object's parent or children. This can be useful, for example, if you need to send a message to another object's parent; you might otherwise first have to find the optr of the object's parent and then send the message directly. Instead, you can use the VisClass messages MSG_VIS_CALL_PARENT and MSG_VIS_SEND_TO_PARENT to pass recorded (encapsulated) events to the recipient's parent.

To send a recorded event to a visible object's children use the message MSG_VIS_SEND_TO_CHILDREN . The recorded event will not be allowed to return anything for the same reason the @visChildren macro can not.

MSG_VIS_CALL_PARENT

void	MSG_VIS_CALL_PARENT(
        EventHandle event);

This message delivers the passed recorded event to the parent of the recipient object. It acts as a call, executing immediately even across threads.

Source: Unrestricted.

Destination: Any visible object.

Parameters: event The event handle of the prerecorded event to be delivered.

Return: Nothing.

Interception: Unlikely.

Tips: In Goc, an object should not send this message to itself; instead, it should use the @visParent macro described above.

MSG_VIS_SEND_TO_PARENT

void	MSG_VIS_SEND_TO_PARENT(
        EventHandle event);

This message delivers the passed recorded event to the parent of the recipient object. It acts as a send, allowing the caller to continue execution without waiting for the event to be handled.

Source: Unrestricted.

Destination: Any visible object.

Parameters: event The event handle of the recorded event to be delivered.

Return: Nothing.

Interception: Unlikely.

Tips: In Goc, an object should not send this message to itself; instead, it should use the @visParent macro described above.

MSG_VIS_SEND_TO_CHILDREN

void	MSG_VIS_SEND_TO_CHILDREN(
        EventHandle event);

This message delivers the passed recorded event to each of the recipient's children. It acts as a message send and can not return values; there is no way for a single message to call several objects and return values from each with a single call.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: event The event handle of the recorded event to be delivered.

Return: Nothing.

Structures: In Goc, an object should not send this message to itself; instead, it should use the @visChildren macro described above.

Warnings: Do not pass pointers in the recorded event's parameters because the message may not be handled before the pointers are invalidated.

Sending Classed Events Up the Tree

MSG_VIS_VUP_FIND_OBJECT_OF_CLASS, MSG_VIS_VUP_CALL_OBJECT_OF_CLASS, MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS, MSG_VIS_VUP_TEST_FOR_OBJECT_OF_CLASS

VisClass has several messages that automatically travel up the visible object tree until they get to an object that meets the proper criteria; they then deliver themselves to that object. This is accomplished by the default handler in VisClass recognizing whether the given object meets the criteria; if it does not, the handler passes the message up to the object's parent.

To find an object of a given class in the tree, use the message MSG_VIS_VUP_FIND_OBJECT_OF_CLASS . This message travels up the tree starting at the recipient until it reaches the first object of that class. So if you are looking for an object of VisCompClass , at the most the message will be passed up a single level.

If you need to send a message to an object of a given class in the visible tree, you can either find it and send it the message or send the message directly with the visible upward messages MSG_VIS_VUP_CALL_OBJECT_OF_CLASS and MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS . These messages search up the tree in the same was as MSG_VIS_VUP_FIND_OBJECT_OF_CLASS , and they then deliver the passed event to the resulting object.

To see simply whether an object of a given class exists in the visible tree, use MSG_VIS_VUP_TEST_FOR_OBJECT_OF_CLASS . This is useful if you are going to use MSG_VIS_VUP_CALL_OBJECT_OF_CLASS (or its counterpart). If no object of the given class exists, you can skip sending the recorded message.

To send classed messages to an object in the tree that is of a specific class, you can use TravelOption with certain MetaClass messages. VisClass defines a travel option TO_VIS_PARENT, which sends the message to the first object up the tree that is of the specified class.

MSG_VIS_VUP_FIND_OBJECT_OF_CLASS

optr	MSG_VIS_VUP_FIND_OBJECT_OF_CLASS(
        ClassStruct *class);

This message searches up the visible object tree until it encounters an object of the specified class. It then returns the optr of that object.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to find an object of the given class above it in the visible object tree.

Parameters: class A pointer to the ClassStruct structure of the class to be searched for.

Return: The optr of the object of the passed class. If multiple objects of this class exist in the tree, the first encountered will be returned. If no objects of this class are in the tree, a NullOptr will be returned.

Interception: Unlikely--only objects that masquerade as objects of a different class would subclass this message. This practice is highly discouraged.

MSG_VIS_VUP_TEST_FOR_OBJECT_OF_CLASS

Boolean	MSG_VIS_VUP_TEST_FOR_OBJECT_OF_CLASS(
        ClassStruct *class);

This message searches up the visible tree and determines whether an object of the given class is in the tree.

Source: Unrestricted.

Destination: Any visible object--typically sent before MSG_VIS_VUP_CALL_OBJECT_OF_CLASS or MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS to ensure that a recipient for the message exists.

Parameters: class A pointer to the ClassStruct structure of the class to be searched for.

Return: True if the class is found; false if it is not.

Interception: Unlikely.

MSG_VIS_VUP_CALL_OBJECT_OF_CLASS

void	MSG_VIS_VUP_CALL_OBJECT_OF_CLASS(
        EventHandle event);

This message searches up the visible tree until it encounters an object of the proper class for the recorded event. The class is specified within the passed event . When the first such object is found, it will be called with the classed event as if an @call had been used with the event directly.

Source: Unrestricted.

Destination: Any visible object--The passed event will be delivered to the first object of the appropriate class, not necessarily to the recipient of the MSG_VIS_VUP_CALL_OBJECT_OF_CLASS .

Parameters: event The event handle of a recorded event.

Return: The classed event delivered by this message may return values as determined by the event. This message returns nothing.

Interception: Unlikely.

MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS

void	MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS(
        EventHandle event);

This message searches up the visible tree until it encounters an object of the proper class for the recorded event. The class is specified within the passed event . When the first such object is found, the message will be delivered to that object as if an @send had been used with the event directly.

Source: Unrestricted.

Destination: Any visible object--The passed event will be delivered to the first object of the appropriate class, not necessarily to the object that received the MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS .

Parameters: event The event handle of a recorded event.

Return: Nothing. The recorded event may not return anything.

Interception: Unlikely.

Warnings: The recorded event should not pass pointers among its parameters.

Visible Upward Queries

MSG_VIS_VUP_QUERY

You can create your own messages that get passed up the visible tree by creating aliases of MSG_VIS_VUP_QUERY . MSG_VIS_VUP_QUERY , by itself, does nothing useful; it simply gets passed up the visible tree without ever being handled.

You can create your own visible queries to return different types of values from objects higher up in the object tree. Take, for example, a music-teaching application which uses note objects as distant children of a sheet music object. If the note objects needed to know what key they were supposed to be in, they would query up the tree with a special upward query message. If we assume the sheet music object kept this information, it would want to respond to the query message with the appropriate value. Thus, the sheet music class would have a line similar to the following:

@alias(MSG_VIS_VUP_QUERY)
        MusicKeyType MSG_VIS_VUP_QUERY_MUSIC_KEY();

This line creates an alias for the general-purpose upward query. The sheet music object should have a handler for MSG_VIS_VUP_QUERY_MUSIC_KEY in which it figures out and returns the appropriate key. This type of upward query can be invaluable in many different situations.

MSG_VIS_VUP_QUERY

void	MSG_VIS_VUP_QUERY();

This message simply queries up the tree until it is handled. It is extensible so visible objects can implement their own upward queries without adding handlers to every class in between it and the query handler.

It is extremely rare that any object would send or handle MSG_VIS_VUP_QUERY on its own; many classes, however, may alias this message and create their own versions of the upward query.

Source: Unrestricted--see note above.

Destination: Any visible object--see note above. Typically sent by a visible object either to itself or directly to its visible parent.

Parameters: None--an alias of this message may have parameters.

Return: Nothing--an alias of this message may have return values.

Interception: MSG_VIS_VUP_QUERY itself should not be intercepted. Its aliases, however, should be intercepted by the appropriate classes that will handle them.

Sending Messages to Window Groups

MSG_VIS_VUP_CALL_WIN_GROUP, MSG_VIS_VUP_SEND_TO_WIN_GROUP

VisClass offers two specific messages for contacting window group objects in a visible tree. These are similar to MSG_VIS_VUP_CALL_OBJECT_OF_CLASS and MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS , except they find the window group of the recipient and deliver the event to the window object. These two messages are detailed below.

MSG_VIS_VUP_CALL_WIN_GROUP

void	MSG_VIS_VUP_CALL_WIN_GROUP(
        EventHandle event);

This message searches up the visible object tree until it encounters a window group object. When the first window group is found, it will be called with the classed event as if an @call had been used.

Source: Unrestricted--typically sent by a visible object to itself to deliver the event to its window.

Destination: Any visible object--the passed event will be delivered to a window object, not necessarily to the object that receives the MSG_VIS_VUP_CALL_WIN_GROUP .

Parameters: event The event handle of a recorded event to be delivered to the window object.

Return: The passed event may return its own values, but MSG_VIS_VUP_CALL_WIN_GROUP returns nothing.

Interception: Unlikely.

See Also: MSG_VIS_VUP_CALL_OBJECT_OF_CLASS.

MSG_VIS_VUP_SEND_TO_WIN_GROUP

void	MSG_VIS_VUP_SEND_TO_WIN_GROUP(
        EventHandle event);

This message searches up the visible object tree until it encounters a window group object. When the first window group is found, the passed event will be delivered to that object as if an @send had been used directly.

Source: Unrestricted--typically sent by a visible object to itself to deliver the event to its window.

Destination: Any visible object--the passed event will be delivered to a window object, not necessarily to the object that receives the MSG_VIS_VUP_CALL_WIN_GROUP .

Parameters: event The event handle of a recorded event to be delivered to the window object.

Return: Nothing. The recorded event may not return values.

Interception: Unlikely.

Warnings: The recorded event may not pass pointers among its parameters.

See Also: MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS.

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Getting Visible Tree Information | Next: 5.5 Visible Object Window Operations


Visible Object Window Operations

VisClass: 5.5 Working with Visible Object Trees: Visible Object Window Operations

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Sending Messages Through the Tree | Next: 6 Visible Layers and 32-Bit Graphics
MSG_VIS_OPEN_WIN, MSG_VIS_CLOSE_WIN, MSG_VIS_WIN_ABOUT_TO_BE_CLOSED, MSG_VIS_MOVE_RESIZE_WIN

Typically, windows will be managed entirely by generic UI objects and the Specific UI library currently in use. You can, however, manage your own VisClass -based window objects by using messages normally used only by Specific UI objects. This is a difficult and complex task, however, and it is not recommended. The four messages used for this purpose are detailed below.

MSG_VIS_OPEN_WIN

void	MSG_VIS_OPEN_WIN(
        WindowHandle parentWindow);

This message is sent to a window or portal object to open its window. The graphics window will be opened and drawn on the screen, and a visual update will propagate down the displayed visible tree.

Source: Visual update mechanism.

Destination: A window group (VTF_IS_WINDOW) or portal (VTF_IS_PORTAL) object.

Parameters: parentWindow The window handle of the open window in which the new window will be created.

Return: Nothing.

Interception: The window group or portal must intercept in order to open the new graphics window and set initial parameters ( VCI_window ).

MSG_VIS_CLOSE_WIN

void	MSG_VIS_CLOSE_WIN();

This message is sent to a window or portal object that has its graphics window currently on the screen. The window and its visible branch will be closed (taken off the screen).

Source: Visual update mechanism.

Destination: A window group (VTF_IS_WINDOW) or portal (VTF_IS_PORTAL) object.

Interception: The window group or portal may intercept if more needs to be done than simply closing the graphics window.

MSG_VIS_WIN_ABOUT_TO_BE_CLOSED

void	MSG_VIS_WIN_ABOUT_TO_BE_CLOSED();

This message notifies the recipient that the window it's displayed in is about to be closed. The default handler for this will remove the visible branch of the window off the screen so no redraws will occur before the window closes.

Source: Visual update mechanism.

Destination: The window group (VTF_IS_WINDOW) or portal (VTF_IS_PORTAL) object that is being closed; the message will then propagate down the visible tree.

Interception: May be intercepted by either the window group or the VisContent object if the default behavior is not appropriate for the object.

MSG_VIS_MOVE_RESIZE_WIN

void	MSG_VIS_MOVE_RESIZE_WIN();

This message causes the window object to move and/or resize itself based on the bounds of its content object. This message is used only by window or portal objects and is rarely, if ever, used by objects in your applications.

Source: Part of the visual update mechanism--typically called by the window object on itself in its MSG_VIS_UPDATE_WINDOWS_AND_IMAGE handler.

Destination: The window group (VTF_IS_WINDOW) or portal (VTF_IS_PORTAL) object to be resized or moved.

Interception: The window object may intercept if it does not want to resize to the bounds of its content object.

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Sending Messages Through the Tree | Next: 6 Visible Layers and 32-Bit Graphics


Visible Layers and 32-Bit Graphics

VisClass: 6 Visible Layers and 32-Bit Graphics

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 Working with Visible Object Trees | Next: 7 VisClass Error Checking
MSG_VIS_LAYER_SET_DOC_BOUNDS, MSG_VIS_LAYER_GET_DOC_BOUNDS, MSG_VIS_LAYER_INSERT_OR_DELETE_SPACE

The GEOS graphic space is built on a basis of 16-bit coordinates. A few applications, however, will require much more space for their documents. GEOS also supports the special use of 32-bit coordinates; applications using these coordinates are said to be using the "large document" model.

The visible object library was originally designed with 16-bit coordinates in the default GEOS graphics space. All instance data, message parameters and return values, and coordinates are assumed to be word-sized integers. A large-document visible object library is not provided primarily because there is no single visible model that works well with large coordinates, and also because no single large model works efficiently for all applications.

It is possible, however, for applications to subclass the standard visible classes in order to get them to support the large document model. In order to do this, however, you must understand the basic issues involved with the large model and with using 32-bit coordinates.

As the application developer, you should read the following sections if you will be using the large document model. Each one deals with a different problem facing applications that want large documents.

In addition, some complex applications may offer several different visible layers in the program. For example, a spreadsheet might want to include a graphic object layer for graphics and charts. The content object of the visible tree can be set up to have several different children, each of which is a "layer" that manages its own objects in the window.

The content in charge of the layers must occasionally set and retrieve the document bounds of its children. For example, if the user wants to set the page size of the spreadsheet that has a graphical layer, the content object must set the new size for all its children. To retrieve the current document bounds of a particular object, send it a MSG_VIS_LAYER_GET_DOC_BOUNDS . To set the document bounds, send a MSG_VIS_LAYER_SET_DOC_BOUNDS . In addition, the application may add or delete space into a layer with MSG_VIS_LAYER_INSERT_OR_DELETE_SPACE ; for example, if a spreadsheet needs to resize a column, it must send this message to the graphic layer to ensure the layers handle the sizing properly.

MSG_VIS_LAYER_SET_DOC_BOUNDS
void	MSG_VIS_LAYER_SET_DOC_BOUNDS(@stack
        sdword	bottom,
        sdword	right,
        sdword	top,
        sdword	left);

This message sets the 32-bit document bounds for a particular layer object. This message is typically sent by a VisContent object to its children, which are assumed to be layer objects.

Source: Unrestricted--typically sent by a VisContent to its large children.

Destination: Any large (32-bit) visible object or composite.

Parameters: all The four parameters describe the new bounds of the large document. Since layer objects typically cover the entire document, the layer object will set its bounds to these values.

Return: Nothing.

Interception: All layer objects and large objects must subclass this message. There is no default handler.

MSG_VIS_LAYER_GET_DOC_BOUNDS
void	MSG_VIS_LAYER_GET_DOC_BOUNDS(
        RectDWord *bounds);

This message returns the 32-bit document bounds of a particular large visible object. Typically, a VisContent will query its large children with this message when it needs to know their bounds.

Source: Unrestricted--typically sent by a VisContent to its large children.

Destination: Any large (32-bit) visible object or composite.

Parameters: bounds A pointer to an empty RectDWord structure that describes a set of large bounds.

Return: The pointer to the filled RectDWord structure. The structure should, upon return, contain the 32-bit bounds of the large visible object.

Interception: All layer objects and large objects must subclass this message. There is no default handler.

Structures: The RectDWord structure is show below for convenience:

typedef struct {
    sdword   RD_left;
    sdword   RD_top;
    sdword   RD_right;
    sdword   RD_bottom;
}  RectDWord;
MSG_VIS_LAYER_INSERT_OR_DELETE_SPACE
void	MSG_VIS_LAYER_INSERT_OR_DELETE_SPACE(
        InsertDeleteSpaceTypes 			type,
        DWFixed 			spaceY,
        DWFixed 			spaceX,
        DWFixed 			positionY,
        DWFixed 			positionX)

This message should be sent by one layer to another when it changes sizes. An example would be a spreadsheet application with a graphic layer; when a column in the spreadsheet resizes, the graphic layer must resize accordingly.

Source: Any visible layer object.

Destination: The visible layer object requiring resizing.

Parameters: type InsertDeleteSpaceType specifying what sort of operation is taking place.

spaceX , spaceY
Change in space for the x and y directions.
positionX , positionY
Change in x and y position.

Return: Nothing.

Interception: This message must be intercepted; it has no default behavior.

Using Visible Document Layers

Using 16-Bit Drawing Commands

The 16-Bit Limit on Visual Bounds

Handling MSG_VIS_DRAW

Managing 32-Bit Geometry

Handling Mouse Events

Setting Up the Objects

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 Working with Visible Object Trees | Next: 7 VisClass Error Checking


Using Visible Document Layers

VisClass: 6.1 Visible Layers and 32-Bit Graphics: Using Visible Document Layers

| Up: GEOS SDK TechDocs | Up | Prev: 6 Visible Layers and 32-Bit Graphics | Next: 6.2 Using 16-Bit Drawing Commands

The first issue with large documents is the basic structure of the visible tree. The topmost object of a large visible tree must be able to keep track of information about the current translations and coordinates being used. Typically, the topmost object would be a subclass of VisContentClass ; this does not change with large trees, though another consideration has to be taken care of.

A content object should not contain any information about the document itself; the content object should not be stored along with other document information. Thus, the object that acts as the content can not be the object that also manages the 32-bit document space. Instead, it is easiest to use a VisCompClass object to manage the document space and to place this object as a child of the content.

In addition, you can set up several different composite children of the content, each one managing its own large document space. Each one of these composites is called a "layer object" and manages a "layer" of the document. This way, you can have several different layers (such as a spreadsheet layer and a graphic layer) in the same document. The layer object should manage the calculations and translations necessary for supporting the large document space.

| Up: GEOS SDK TechDocs | Up | Prev: 6 Visible Layers and 32-Bit Graphics | Next: 6.2 Using 16-Bit Drawing Commands


Using 16-Bit Drawing Commands

VisClass: 6.2 Visible Layers and 32-Bit Graphics: Using 16-Bit Drawing Commands

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 Using Visible Document Layers | Next: 6.3 The 16-Bit Limit on Visual Bounds

All the GEOS drawing commands are based on 16-bit coordinates. Drawing may only occur within a 16-bit graphics space. The graphics system does, however, support extended translations of the 16-bit graphics space within 32-bit coordinates.

This means that you can think of the large document space as a number of smaller, 16-bit "local" graphics spaces. When you need to draw anything, you first use an extended translation to get near your drawing point in the large document. Then you can draw using 16-bit offsets from the point to which you translated.

The application using the extended translation can do so in two different ways: It can store a 32-bit translation with each visible object, or it can store a 32-bit translation in a visible composite, thereby making the entire visible branch under the composite in a 32-bit graphics space. Visible trees implementing layer objects must use the second option; the layer must contain the translation for the entire visible branch that it manages. If a simple large visible tree does not need layers, it may wish to use the first solution.

The first of the two solutions is simple but takes up additional memory and time; each object, when it draws, must translate the coordinates, draw, then translate back. Two translations for each object could significantly affect drawing performance unless visual updates are handled carefully.

The other solution, storing a translation in the composite object, requires just two translations for the entire branch headed by the composite. Whenever the composite receives MSG_VIS_DRAW or MSG_VIS_VUP_CREATE_GSTATE , it executes the translation on the GState. As long as the translation is always relative to the GState's current transformation (done using GrApplyTranslationDWord() ), and as long as the translation is undone before the GState is returned to the caller, drawing will be done properly.

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 Using Visible Document Layers | Next: 6.3 The 16-Bit Limit on Visual Bounds


The 16-Bit Limit on Visual Bounds

VisClass: 6.3 Visible Layers and 32-Bit Graphics: The 16-Bit Limit on Visual Bounds

| Up: GEOS SDK TechDocs | Up | Prev: 6.2 Using 16-Bit Drawing Commands | Next: 6.4 Handling MSG_VIS_DRAW

Individual visual objects have 16-bit bounds. If the rules of drawing in the 32-bit graphics space are followed, this is not a problem. Individual objects are constrained to 16-bit width and 16-bit height, however.

The only objects for which having 16-bit bounds is a problem are the tree's content object and all the layer objects. This is because these two types of objects must have width and height greater than 16 bits. This problem is handled differently for each object:

For the content object, the VI_bounds field is set to all zeros and is not used. Normally, the content uses its bounds to set the document size in the GenView; the responsibility for setting the GenView's document size then falls to the application.

For the layer object, the VI_bounds field is set to all zeros and is not used. If the bounds of the layer object are important to the application, the layer object must be given new instance fields to hold the 32-bit coordinates of the bounds. If not, nothing needs to be done; the bounds can simply be ignored.

| Up: GEOS SDK TechDocs | Up | Prev: 6.2 Using 16-Bit Drawing Commands | Next: 6.4 Handling MSG_VIS_DRAW


Handling MSG_VIS_DRAW

VisClass: 6.4 Visible Layers and 32-Bit Graphics: Handling MSG_VIS_DRAW

| Up: GEOS SDK TechDocs | Up | Prev: 6.3 The 16-Bit Limit on Visual Bounds | Next: 6.5 Managing 32-Bit Geometry

In normal-sized visible trees, MSG_VIS_DRAW gets sent to all objects in the tree. This draws the entire visible tree, no matter what portions are visible on the screen. For large documents, this can quickly get unwieldy and inefficient.

A better solution is for the layer object of each particular layer to intercept MSG_VIS_DRAW and pass it on only to those objects in the affected portion of the document. For this to work, of course, the layer object must know something of the structure and composition of the visible branch below it. This is the primary reason the visible object library does not offer a standard visible layer class--in essence, it must be defined specifically for each application.

| Up: GEOS SDK TechDocs | Up | Prev: 6.3 The 16-Bit Limit on Visual Bounds | Next: 6.5 Managing 32-Bit Geometry


Managing 32-Bit Geometry

VisClass: 6.5 Visible Layers and 32-Bit Graphics: Managing 32-Bit Geometry

| Up: GEOS SDK TechDocs | Up | Prev: 6.4 Handling MSG_VIS_DRAW | Next: 6.6 Handling Mouse Events

Because visible objects and composites maintain their 16-bit bounds in a large document, they can still use the 16-bit geometry manager. Layer objects, however, can not use the geometry manager. All geometry management on the layer level (child positioning, resizing, etc.) must be done by the layer object itself in conjunction with its child composites.

| Up: GEOS SDK TechDocs | Up | Prev: 6.4 Handling MSG_VIS_DRAW | Next: 6.6 Handling Mouse Events


Handling Mouse Events

VisClass: 6.6 Visible Layers and 32-Bit Graphics: Handling Mouse Events

| Up: GEOS SDK TechDocs | Up | Prev: 6.5 Managing 32-Bit Geometry | Next: 6.7 Setting Up the Objects

The GenView is built to support large documents, though its default is normal-sized documents. Thus, you can request the view to send large mouse events, which carry with them 32 bits of integer and 16 bits of fraction in each dimension. The visible classes have no default handlers for these messages, so your own classes will have to handle them appropriately.

The large mouse events are the equivalent of their 16-bit counterparts. They just carry different data to represent the coordinates of the pointer. All the mouse events, both large and normal, are described in the Input chapter.

Normal composite objects will not be affected by large mouse events; the composite will continue to send the event on to the proper child as if it were in a 16-bit document. Layer and content objects, however, must process these messages differently.

If no object within the visible tree has the mouse grabbed, the content of the tree must decide which layer object should receive the mouse events. How the content and layer objects negotiate this is up to the particular application; some applications may consider that one layer is "on top of" the others, and other applications may determine which layer is the most appropriate based on the event context. By default, the large mouse events are passed by VisContentClass straight on to the first layer child. If you want a more complex scheme, you will have to subclass VisContentClass and intercept these messages.

| Up: GEOS SDK TechDocs | Up | Prev: 6.5 Managing 32-Bit Geometry | Next: 6.7 Setting Up the Objects


Setting Up the Objects

VisClass: 6.7 Visible Layers and 32-Bit Graphics: Setting Up the Objects

| Up: GEOS SDK TechDocs | Up | Prev: 6.6 Handling Mouse Events | Next: 7 VisClass Error Checking

Finally, you should be aware of what attributes to set in both the GenView and the VisContent objects of your visible tree to support the large document. You could easily find this out by looking through the various attributes of those classes (and you are encouraged to do so), but the following guide is provided for convenience:

In the GenView object, set GVA_WINDOW_COORDINATE_MOUSE_EVENTS in the GVI_attrs instance data field. This will pass mouse events in terms of offsets from the upper-left corner of the view window rather than in absolute document coordinates. VisContentClass handles these events and automatically turns them into document coordinates.

In the VisContent, set VCNA_WINDOW_COORDINATE_MOUSE_EVENTS and VCNA_LARGE_DOCUMENT_MODEL in the VCNI_attrs field. The first of these tells the content that it will receive input events in window coordinates rather than in document coordinates. The second will have the effect of making the content object ignore its bounds field and geometry management. It will also tell the content that all its children are layer objects. Setting the large document model will cause the content to send all the following messages on to all its children rather than handle them directly: MSG_VIS_DRAW , MSG_VIS_CONTENT_VIEW_ORIGIN_CHANGED , MSG_VIS_CONTENT_VIEW_SCALE_FACTOR_CHANGED , and MSG_VIS_CONTENT_VIEW_SIZE_CHANGED .

You do not have to create all the objects you may need; be aware that the Graphic Object Library provides its own layer object that supports a 32-bit graphic object layer. The spreadsheet object also exports its own layer object.

Requirements of Layer Objects

Layer objects will need to handle MSG_VIS_LAYER_SET_DOC_BOUNDS , which will be sent to all layers when the document bounds change.

In essence the layer object's job is to isolate its children and their children from the fact that they are in a 32-bit document rather than a 16-bit document. In order to do this, layer objects must do several things:

  • Store the translation
    This is a set of 32-bit translation offsets. These offsets represent the current location, if any, where the layer is being drawn in the 32-bit coordinate space.
  • Intercept MSG_VIS_DRAW
     In its handler, the layer should apply any 32-bit translations (using GrApplyTranslationDWord() ) on the GState provided by MSG_VIS_DRAW . It should then intelligently select which of its children are affected by the drawing event and pass the message on to them with the translated GState. The handler should then untranslate the GState before returning.
  • Intercept MSG_VIS_VUP_CREATE_GSTATE
     In the handler, the layer must first call its superclass (to create the GState normally), then apply the necessary translation using GrApplyTranslationDWord() . It can then return the translated GState.
  • Intercept and handle large mouse events
    Each large mouse event that may be interesting to the layer must be handled by the layer object. This is to ensure that mouse events that occur when no object has the mouse grab get handled. If the layer has 16-bit visible children, it should translate the mouse event into small mouse events before passing it to its superclass. If the layer has 32-bit visible children, it should first translate both mouse coordinates and then decide which of its large children should receive the event.

Requirements of 32-bit Visible Objects

Large visible objects in the visible tree should be equipped to handle both large and small mouse events. This is because the parent object may have both 32-bit and 16-bit children. If it does, it will have to send out only small mouse events; otherwise, the 16-bit objects would be confused by the large mouse events.

Requirements of 16-bit Visible Objects

Because 16-bit objects are the norm and not the exception, there are few requirements for them. However, 16-bit objects in 32-bit documents must do one thing: they must prevent the system from allowing the user to select the object and then drag-scroll too far. To prevent this from happening, the visible object must call MSG_VIS_VUP_SET_MOUSE_INTERACTION_BOUNDS to set a temporary boundary on drag scrolling.

| Up: GEOS SDK TechDocs | Up | Prev: 6.6 Handling Mouse Events | Next: 7 VisClass Error Checking


VisClass Error Checking

VisClass: 7 VisClass Error Checking

| Up: GEOS SDK TechDocs | Up | Prev: 6 Visible Layers and 32-Bit Graphics | Next: 8 Creating Specific UIs
MSG_VIS_VUP_EC_ENSURE_WINDOW_NOT_REFERENCED, MSG_VIS_VUP_EC_ENSURE_OBJ_BLOCK_NOT_REFERENCED, MSG_VIS_VUP_EC_ENSURE_OD_NOT_REFERENCED

VisClass provides three messages you can use for special error checking. These three messages are provided especially to make sure that all objects being destroyed or removed will be destroyed cleanly. The default handlers for these messages should cause a fatal error if their criteria are not met.

MSG_VIS_VUP_EC_ENSURE_WINDOW_NOT_REFERENCED
void	MSG_VIS_VUP_EC_ENSURE_WINDOW_NOT_REFERENCED(
        WindowHandle window);

This message travels up the visible object tree, ensuring that the window handle passed is not stored anywhere that it might cause trouble. This message may be used when a window is being destroyed and its handle is about to be freed.

Source: Unrestricted.

Destination: Any visible object in the affected visible tree.

Parameters: window The handle of the window being destroyed.

Return: Nothing--if it returns, no error was encountered.

Interception: Do not intercept this message.

MSG_VIS_VUP_EC_ENSURE_OBJ_BLOCK_NOT_REFERENCED
void	MSG_VIS_VUP_EC_ENSURE_OBJ_BLOCK_NOT_REFERENCED(
        MemHandle objBlock);

This message travels up the visible object tree, ensuring that the memory handle (the handle of an object block) passed is not stored anywhere that it might cause trouble. This message may be used just before the object block's handle is freed to ensure that no stale references will remain.

Source: Unrestricted.

Destination: Any visible object in the affected visible tree.

Parameters: objBlock The handle of the block being freed.

Return: Nothing--if it returns, no error was encountered.

Interception: Do not intercept this message.

MSG_VIS_VUP_EC_ENSURE_OD_NOT_REFERENCED
void	MSG_VIS_VUP_EC_ENSURE_OD_NOT_REFERENCED(
        optr	object);

This message travels up the visible object tree, ensuring that the optr passed is not stored by any objects in the tree. This message may be sent when the object is being destroyed; any objects methods encountering a match between a stored optr and the passed optr should force a fatal error.

Source: Unrestricted.

Destination: Any visible object in the affected visible tree.

Parameters: object The handle of the object being destroyed.

Return: Nothing--if it returns, no error was encountered.

Interception: Do not intercept this message.

| Up: GEOS SDK TechDocs | Up | Prev: 6 Visible Layers and 32-Bit Graphics | Next: 8 Creating Specific UIs


Creating Specific UIs

VisClass: 8 Creating Specific UIs

| Up: GEOS SDK TechDocs | Up | Prev: 7 VisClass Error Checking | Next: 9 Basic Summary

It is possible, using visible object classes, to create your own specific UIs. It is a complex and difficult task, however, and only very few developers will expect to do this. Documentation on creating a custom specific UI may appear either in a future release of this documentation or under separate cover. Contact Geoworks Developer Products for more information.

| Up: GEOS SDK TechDocs | Up | Prev: 7 VisClass Error Checking | Next: 9 Basic Summary


Basic Summary

VisClass: 9 Basic Summary

| Up: GEOS SDK TechDocs | Up | Prev: 8 Creating Specific UIs

Visible objects are involved in several basic operations, each of which has its own primary messages:

  • Drawing
    Vis objects must handle MSG_VIS_DRAW ( MSG_VIS_DRAW ) if they are to draw after their window has been exposed. Objects wishing to redraw themselves outside of exposure events can call MSG_VIS_VUP_CREATE_GSTATE ( MSG_VIS_VUP_CREATE_GSTATE ) to create a GState in their view windows for drawing.
  • Being added to an object tree
    When an object is added to a visible tree, it must then force a redraw of the tree. It can be added to the tree with MSG_VIS_ADD_CHILD ( MSG_VIS_ADD_CHILD ), and the tree can be updated with MSG_VIS_VUP_UPDATE_WIN_GROUP ( MSG_VIS_VUP_UPDATE_WIN_GROUP ).
  • Being removed from an object tree
    A visible object may be removed from a visible tree with MSG_VIS_REMOVE ( MSG_VIS_REMOVE ). This message will close the object (take it off the screen) and then segregate it from its parent.
  • Undergoing visual update
    The visual update mechanism is automatic for the most part. An object that requires visual update, however, can use MSG_VIS_MARK_INVALID ( MSG_VIS_MARK_INVALID ) in conjunction with MSG_VIS_VUP_UPDATE_WIN_GROUP ( MSG_VIS_VUP_UPDATE_WIN_GROUP ) to cause the update.
  • Geometry management
    Geometry management of visible objects can be as simple as setting a few flags in the instance data or as complex as manually setting position and size of the objects. For a full description of geometry management of visible objects, see Positioning Visible Objects .
  • Being Destroyed
    Visible objects may be destroyed even when they are part of a visible tree that is on the screen. MSG_VIS_DESTROY ( MSG_VIS_DESTROY ) first removes the object from the tree (with MSG_VIS_REMOVE ) and then destroys it (through the MetaClass detach and destroy mechanisms).
| Up: GEOS SDK TechDocs | Up | Prev: 8 Creating Specific UIs


VisComp

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 VisCompClass Features
| One-page Version of this Chapter for Easy Printing

VisCompClass allows visible objects to have children. VisComp objects are the grouping elements within visible object trees.

If you need to have a visible object tree with several levels, or if you need to manage the geometry of visible objects, you will want to read this chapter. You should already be familiar with VisClass

1 VisCompClass Features
2 VisCompClass Instance Data
2.1 VCI_comp
2.2 VCI_gadgetExcl
2.3 VCI_window
2.4 VCI_geoAttrs
2.5 VCI_geoDimensionAttrs
2.6 Managing Instance Data
3 Using VisCompClass
3.1 Managing Geometry
3.2 Managing Graphic Windows

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 VisCompClass Features
| One-page Version of this Chapter for Easy Printing


VisCompClass Features

VisComp: 1 VisCompClass Features

| Up: GEOS SDK TechDocs | Up | Prev: VisComp | Next: 2 VisCompClass Instance Data

Composite visible objects provide several features and functions that normal visible objects can not. VisCompClass has several instance data fields above and beyond those found in VisClass (since VisCompClass is a subclass of VisClass , it inherits all its instance data and methods). Although it only handles a few messages not defined in VisClass , it provides much more functionality. Some of the main features of composite objects are listed below:

  • They can have children.
    Normal visible objects can exist only as leaves of a visible object tree. VisCompClass can have children, allowing the tree to be built to any number of levels. See the VisClass chapterfor full information on visible trees.
  • They can manage their children's geometry.
    A composite's children can be managed arbitrarily by the composite, or the composite can use the geometry manager to automatically position and size its children. Since a composite can have other composites as its children, this sizing can descend recursively throughout the visible tree, making the entire tree's geometry completely self-managed. See Managing Geometry for information on geometry management.
  • They can create and manage their own windows.
    Composite objects can create their own graphics windows. Though this is not typically done (except in Specific UI libraries), it is possible. See Managing Graphic Windows for more information on windows.
  • They support other VisClass functions.
    VisCompClass inherits all the instance data fields and messages of VisClass . As such, it can do everything normal VisClass objects can do. See the VisClass chapterfor full information on VisClass .
| Up: GEOS SDK TechDocs | Up | Prev: VisComp | Next: 2 VisCompClass Instance Data


VisCompClass Instance Data

VisComp: 2 VisCompClass Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 VisCompClass Features | Next: 3 Using VisCompClass

As stated above, VisCompClass inherits all the instance data fields from VisClass . All of those fields may be set and reset as they could be for an object of that class. Composite objects also have four other instance fields, shown in VisCompClass Instance Fields .

Code Display 16-1 VisCompClass Instance Fields

/* The VisCompClass instance data fields are shown below and are discussed in
 * detail throughout the chapter. */
    /* VCI_comp
     * VCI_comp contains the link to the composite object's first child. */
    @instance @composite				 VCI_comp = VI_link;
    /* VCI_gadgetExcl
     * VCI_gadgetExcl is an optr to the object that currently has the gadget
     * exclusive. This field is rarely used directly by applications. */
    @instance optr				 VCI_gadgetExcl;
    /* VCI_window
     * VCI_window contains the window handle of the graphics window associated
     * with this object. This field is rarely accessed directly by
     * applications; it is set by the visual update mechanism. */
    @instance WindowHandle				 VCI_window = NullHandle;
    /* VCI_geoAttrs
     * VCI_geoAttrs is a record that defines some of the geometry management
     * information for the composite. None of its values are set by default;
     * the possible flags in this record are shown after the definition. */
    @instance VisCompGeoAttrs				 VCI_geoAttrs = 0;
    /* Possible flags:
     *	VCGA_ORIENT_CHILDREN_VERTICALLY					0x80
     *	VCGA_INCLUDE_ENDS_IN_CHILD_SPACING					0x40
     *	VCGA_ALLOW_CHILDREN_TO_WRAP					0x20
     *	VCGA_ONE_PASS_OPTIMIZATION					0x10
     *	VCGA_CUSTOM_MANAGE_CHILDREN					0x08
     *	VCGA_HAS_MINIMUM_SIZE					0x04
     *	VCGA_WRAP_AFTER_CHILD_COUNT					0x02
     *	VCGA_ONLY_DRAWS_IN_MARGINS 					0x01
     */
    /* VCI_geoDimensionAttrs
     * VCI_geoDimensionAttrs is a record that contains additional information
     * about the composite's geometry. This field contains two two-bit fields
     * among its other flags. The default settings are shown in the definition;
     * all possible flags and settings are shown following. */
    @instance  VisCompGeoDimensionAttrs					 VCI_geoDimensionAttrs = 0;
    /* Possible flags:
      * Width Justification flags (mutually exclusive):
     * VCGDA_WIDTH_JUSTIFICATION						0xc0
     *	WJ_LEFT_JUSTIFY_CHILDREN					0x00
     *	WJ_RIGHT_JUSTIFY_CHILDREN					0x40
     *	WJ_CENTER_CHILDREN_HORIZONTALLY					0x80
     *	WJ_FULL_JUSTIFY_CHILDREN_HORIZONTALLY					0xc0
     *
     * Height Justification flags (mutually exclusive):
     * VCGDA_HEIGHT_JUSTIFICATION 						0x0c
     *	HJ_TOP_JUSTIFY_CHILDREN					0x00
     *	HJ_BOTTOM_JUSTIFY_CHILDREN,					0x04
     *	HJ_CENTER_CHILDREN_VERTICALLY					0x08
     *	HJ_FULL_JUSTIFY_CHILDREN_VERTICALLY					0x0c
     *
     * Other flags:
     *	VCGDA_EXPAND_WIDTH_TO_FIT_PARENT					0x20
     *	VCGDA_DIVIDE_WIDTH_EQUALLY					0x10
     *	VCGDA_EXPAND_HEIGHT_TO_FIT_PARENT					0x02
     *	VCGDA_DIVIDE_HEIGHT_EQUALLY					0x01
     */
    @default  VI_typeFlags = VTF_IS_COMPOSITE;

VCI_comp

VCI_gadgetExcl

VCI_window

VCI_geoAttrs

VCI_geoDimensionAttrs

Managing Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 VisCompClass Features | Next: 3 Using VisCompClass


VCI_comp

VisComp: 2.1 VisCompClass Instance Data: VCI_comp

| Up: GEOS SDK TechDocs | Up | Prev: 2 VisCompClass Instance Data | Next: 2.2 VCI_gadgetExcl

Normal visible objects have just one instance field for tree construction: the VI_link field that points to the object's next sibling. For an object to have children, it must also have an instance field declared with the keyword @composite ; this field, in VisCompClass , is VCI_comp . The composite field contains the optr of the object's first child; the link field contains a pointer to either the object's next sibling or its parent.

Applications should never access the VI_link or VCI_comp fields directly at run-time; instead, these fields can be altered or queried with the following messages (all defined in VisClass ):

MSG_VIS_ADD_CHILD
This message adds a new child object to the composite.
MSG_VIS_REMOVE_CHILD
This message removes a child from the composite.
MSG_VIS_MOVE_CHILD
This message moves a child within the composite's list of children.
MSG_VIS_GET_PARENT
This message returns a pointer to the object's parent.

Several other messages may also be used to change or query any visible object's VI_link and VCI_comp fields. These are discussed in the VisClass chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 2 VisCompClass Instance Data | Next: 2.2 VCI_gadgetExcl


VCI_gadgetExcl

VisComp: 2.2 VisCompClass Instance Data: VCI_gadgetExcl

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 VCI_comp | Next: 2.3 VCI_window

Since the composite object must manage several other objects, it must also keep track of certain hierarchies used by the UI; the gadget exclusive is one of these. Within each branch of the visible object tree, only one visible object may have the gadget exclusive at a time. The gadget exclusive is kept track of via a path of pointers from the top of the tree down to the object having the exclusive.

The VCI_gadgetExcl field determines which child of the composite has the gadget exclusive. The child indicated in this field may or may not actually have the gadget exclusive for the entire visible tree; if the branch does not have the exclusive, neither will the child object. The gadget exclusive is similar to the other hierarchies of the input manager and acts the same way.

Applications never access the VCI_gadgetExcl field of a composite directly. Instead, this field may be set with the following three VisClass messages:

MSG_VIS_TAKE_GADGET_EXCL
This message causes a visible object to set itself as having the gadget exclusive. The object's parent composite will set its VCI_gadgetExcl field appropriately.
MSG_VIS_RELEASE_GADGET_EXCL
This message causes a visible object to relinquish its hold on the gadget exclusive. The object's parent composite will set its VCI_gadgetExcl field appropriately.
MSG_VIS_LOST_GADGET_EXCL
This message is sent to the object having the gadget exclusive for that branch when it has lost the exclusive.

The above messages are detailed in the VisClass chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 VCI_comp | Next: 2.3 VCI_window


VCI_window

VisComp: 2.3 VisCompClass Instance Data: VCI_window

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 VCI_gadgetExcl | Next: 2.4 VCI_geoAttrs

Every visible object has a window associated with it in which it will be drawn. Normal VisClass objects have no control over what window they're associated with; they must be in the same window as their composite parents. Composites may need to appear in different windows from their parents, however. Thus, VisCompClass has the VCI_window field, which contains the window handle of the window in which the composite's branch will be drawn.

Applications will not access this field directly. Typically, composites will be drawn in the window associated with their parent composites or contents. Sometimes, however, an application will want a visible branch to appear in a window different from that of the rest of the visible tree. The composite then must use other VisClass messages for creating and associating a window. This process is discussed in more detail in Managing Graphic Windows .

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 VCI_gadgetExcl | Next: 2.4 VCI_geoAttrs


VCI_geoAttrs

VisComp: 2.4 VisCompClass Instance Data: VCI_geoAttrs

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 VCI_window | Next: 2.5 VCI_geoDimensionAttrs

The most-used feature of composite objects is their ability to manage the sizing and placement of their children. This is known as managing the composite's geometry.

The geometry management behavior implemented by a particular composite object is determined by its VCI_geoAttrs and VCI_geoDimensionAttrs fields. Both of these fields may be set and altered by applications as their geometry needs change. The VCI_geoAttrs field, specifically, determines the type of geometry management employed by the composite.

The VCI_geoAttrs field may contain any or all of the following eight flags:

VCGA_ORIENT_CHILDREN_VERTICALLY
This flag indicates that the composite's children should be arranged vertically rather than the default (horizontally).
VCGA_INCLUDE_ENDS_IN_CHILD_SPACING
When the composite is using full justification (see VCI_geoDimensionAttrs ), this flag indicates that there should be as much space before the first child and after the last child as there is between the children. If this flag is not set, there will be no space outside the first and last children.
VCGA_ALLOW_CHILDREN_TO_WRAP
This flag will allow the children to wrap if their combined lengths won't allow them to fit within the composite's bounds. The composite will keep within the bounds of its parent, and its children will wrap as necessary. If this flag is not set, the composite will try to grow to be as large as necessary to fit all children.
VCGA_ONE_PASS_OPTIMIZATION
This flag makes the geometry manager make only one pass at managing the children. It should only be set if the children can be guaranteed not to wrap or resize.
VCGA_CUSTOM_MANAGE_CHILDREN
This flag indicates that the composite will manage its children without using the geometry manager. This allows the composite or its children to manually determine their positions and sizes. If this flag is set, the composite will keep its own bounds in its VI_bounds field, just as other Vis objects. This flag in a composite indicates custom management for the composite's entire branch, not just its children.
VCGA_HAS_MINIMUM_SIZE
This flag indicates that the composite has a minimum size. The geometry manager will query the composite for this minimum and will ensure the object never gets smaller than that regardless of the size of its children. The minimum size must be returned by a custom MSG_VIS_COMP_GET_MINIMUM_SIZE handler.
VCGA_WRAP_AFTER_CHILD_COUNT
Used in conjunction with VCGA_ALLOW_CHILDREN_TO_WRAP, this flag will cause child wrapping after a certain number of children. This can cause wrapping based on the number of children rather than on child size. The geometry manager will query the composite with MSG_VIS_COMP_GET_WRAP_COUNT .
VCGA_ONLY_DRAWS_IN_MARGINS
This flag is used for optimized visual updates. It causes only the margins of the composite to be drawn when its image is marked invalid; all children of the composite must have their own images marked invalid if they are to be redrawn as well.
| Up: GEOS SDK TechDocs | Up | Prev: 2.3 VCI_window | Next: 2.5 VCI_geoDimensionAttrs


VCI_geoDimensionAttrs

VisComp: 2.5 VisCompClass Instance Data: VCI_geoDimensionAttrs

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 VCI_geoAttrs | Next: 2.6 Managing Instance Data

The VCI_geoDimensionAttrs field determines how the composite manages its children in each dimension. It provides the geometry information not given in VCI_geoAttrs such as child justification and certain sizing behavior.

It contains three fields for each dimension (horizontal and vertical): The first field represents the justification of the children in that dimension. This field is two bits and can be one of four different enumerations. The second and third fields are sizing flags. These fields are listed below:

VCGDA_WIDTH_JUSTIFICATION
This is a two-bit field that can be set to any one of four possible width justifications. If the name of this field is used in place of one of the four values, full justification will be used. The justification can be set as a normal flag. The four different values are
WJ_LEFT_JUSTIFY_CHILDREN
Left justify the children.
WJ_RIGHT_JUSTIFY_CHILDREN
Right justify the children.
WJ_CENTER_CHILDREN_HORIZONTALLY
Center the children horizontally.
WJ_FULL_JUSTIFY_CHILDREN_HORIZONTALLY
Full justify the children. Horizontal full justification is only meaningful if the children are oriented horizontally (by clearing VCGA_ORIENT_CHILDREN_VERTICALLY in VCI_geoAttrs ).
VCGDA_EXPAND_WIDTH_TO_FIT_PARENT
If this flag is set, the composite will try to expand to fill the available width of its parent. By default, this flag is not set; the composite will be only as wide as its children require.
VCGDA_DIVIDE_WIDTH_EQUALLY
If this flag is set, the composite will try to divide space equally between all its manageable, horizontally-oriented children. The composite will only suggest sizes--the children may or may not cooperate.
VCGDA_HEIGHT_JUSTIFICATION
This is a two-bit field that can be set to any one of four height justifications. If the name of this field is used in place of one of the four values, full justification will be used. The justification can be set as a normal flag. The four different values are
HJ_TOP_JUSTIFY_CHILDREN
Justify the children to the composite's top bound.
HJ_BOTTOM_JUSTIFY_CHILDREN
Justify the children to the composite's bottom bound.
HJ_CENTER_CHILDREN_VERTICALLY
Center the children vertically.
HJ_FULL_JUSTIFY_CHILDREN_VERTICALLY
Full justify the children. Vertical full justification is only meaningful if the children are oriented vertically (by setting VCGA_ORIENT_CHILDREN_VERTICALLY in VCI_geoAttrs ).
VCGDA_EXPAND_HEIGHT_TO_FIT_PARENT
If this flag is set, the composite will try to expand to fill the available height of its parent. By default, this flag is not set; the composite will be only as tall as its children require.
VCGDA_DIVIDE_HEIGHT_EQUALLY
If this flag is set, the composite will try to divide space equally between all its manageable, vertically-oriented children. The composite will only suggest sizes--the children may or may not cooperate .
| Up: GEOS SDK TechDocs | Up | Prev: 2.4 VCI_geoAttrs | Next: 2.6 Managing Instance Data


Managing Instance Data

VisComp: 2.6 VisCompClass Instance Data: Managing Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 VCI_geoDimensionAttrs | Next: 3 Using VisCompClass
MSG_VIS_COMP_GET_GEO_ATTRS, MSG_VIS_COMP_SET_GEO_ATTRS

To retrieve the flags currently set in both VCI_geoAttrs and VCI_geoDimensionAttrs , use MSG_VIS_COMP_GET_GEO_ATTRS . To set the attributes in either or both fields, use MSG_VIS_COMP_SET_GEO_ATTRS . both of these messages are detailed below.

MSG_VIS_COMP_SET_GEO_ATTRS

void	MSG_VIS_COMP_SET_GEO_ATTRS(
        word	attrsToSet,
        word	attrsToClear);

This message sets the flags in the composite object's VCI_geoAttrs and VCI_geoDimensionAttrs fields. The high byte of each parameter represents the dimension attributes, and the low byte represents the geometry attributes. This message does not invalidate or update the object's geometry.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: attrsToSet A word of attributes that should be set for the object. The high byte is a record of VisCompGeoDimensionAttrs , and the low byte is a record of VisCompGeoAttrs . The attributes set in this parameter will be set for the object.

attrsToClear
A word of attributes to be cleared from the object's instance data. It has the same form as attrsToSet , above. Any attribute set in this parameter will be cleared in the instance fields.

Return: Nothing.

Interception: Unlikely.

MSG_VIS_COMP_GET_GEO_ATTRS

word	MSG_VIS_COMP_GET_GEO_ATTRS();

This message retrieves the flags set in the object's VCI_geoAttrs and VCI_geoDimensionAttrs fields. The high byte of the return value represents the dimension attributes, and the low byte represents the geometry attributes. This message does not invalidate or update the object's geometry.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: None.

Return: A word of flags. The high byte is a record of type VisCompGeoDimensionAttrs ; the low byte is a record of type VisCompGeoAttrs . The high byte represents the attributes set in the object's VCI_geoDimensionAttrs field, and the low byte represents the attributes set in the object's VCI_geoAttrs field.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 VCI_geoDimensionAttrs | Next: 3 Using VisCompClass


Using VisCompClass

VisComp: 3 Using VisCompClass

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 VisCompClass Instance Data | Next: 3.1 Managing Geometry

With the exception of the geometry flags mentioned above and the management of children, the use of VisCompClass is little different from the use of VisClass . In fact, most of the functionality designed for VisCompClass is built directly into VisClass ; for example, nearly all geometry management and object tree management messages are defined in VisClass . Some reminders are listed below, however.

Not all composite objects will have something to draw. Some composites will be used simply as grouping objects to manage visible children. In these cases, you will not have to subclass VisCompClass but can instead use the class directly.

Many composite objects will have something to draw, though. For example, a composite may want to draw a box around its children or wash a different background color behind them. If this is the case, the object must be a subclass of VisCompClass and must handle MSG_VIS_DRAW .

One of the primary functions of VisCompClass is to pass input events and other messages down and up the tree to the proper objects. This is done automatically. You can, however, change this behavior by subclassing VisCompClass and intercepting the messages in which the object will be interested.

Managing Geometry

Managing Graphic Windows

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 VisCompClass Instance Data | Next: 3.1 Managing Geometry


Managing Geometry

VisComp: 3.1 Using VisCompClass: Managing Geometry

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using VisCompClass | Next: 3.2 Managing Graphic Windows
MSG_VIS_COMP_GET_CHILD_SPACING, MSG_VIS_COMP_GET_MINIMUM_SIZE, MSG_VIS_COMP_GET_MARGINS, MSG_VIS_COMP_GET_WRAP_COUNT

A special feature of VisCompClass , and one that can be used in many ways, is its ability to automatically manage its children. By setting various flags in the composite's instance fields, you can have it control its children's sizing and position without additional code in your application.

Most of the flags you can set in VCI_geoAttrs and VCI_geoDimensionAttrs are explained fully in VisCompClass Instance Data . You should especially be aware that if you do not want to use the geometry management capabilities of VisCompClass , you should set VCGA_CUSTOM_MANAGE_CHILDREN in VCI_geoAttrs .

In addition to the messages provided in VisClass for geometry management, VisCompClass has four that return information about its current geometry. These are necessary because composites may be children of other composites, and therefore they may be managed. These messages are detailed below.

Most of the issues of geometry management are discussed in the VisClass chapter.

DWORD_CHILD_SPACING

word	DWORD_CHILD_SPACING(val);
        SpacingAsDWord val;

This macro extracts the child spacing from the given SpacingAsDWord value. Use it with MSG_VIS_COMP_GET_CHILD_SPACING .

DWORD_WRAP_SPACING

word	DWORD_WRAP_SPACING(val);
        SpacingAsDWord val;

This macro extracts the wrap spacing from the given SpacingAsDWord value. Use it with MSG_VIS_COMP_GET_CHILD_SPACING .

MAKE_SPACING_DWORD

SpacingAsDWord MAKE_SPACING_DWORD(child, wrap);
        word	child;
        word	wrap;

This macro creates a SpacingAsDWord dword from the two given arguments. The child argument is the child spacing, and the wrap argument is the wrap spacing. Use this macro in your handler (if any) for the message MSG_VIS_COMP_GET_CHILD_SPACING .

MSG_VIS_COMP_GET_CHILD_SPACING

SpacingAsDWord MSG_VIS_COMP_GET_CHILD_SPACING();

This message returns the child spacing used by the composite. The high word of the return value is the spacing between lines of wrapped children; the low word is the horizontal spacing between children.

Source: Unrestricted.

Destination: Any visible composite object--typically sent by a composite to itself during geometry calculations.

Parameters: None.

Return: A dword containing the child spacing used by the composite. The dword contains two values: The child spacing--the amount of spacing placed between the composite's children--can be extracted from the return value with the macro DWORD_CHILD_SPACING . The wrap spacing--the amount of space placed between lines of wrapped children--can be extracted from the return value with the macro DWORD_WRAP_SPACING .

Interception: If a composite wants special child or wrap spacing other than the default, it should subclass this message and return the desired values. There is no need to call the superclass in the method.

Tips: In your handler, you can use the macro MAKE_SPACING_DWORD to form the return value from the two spacing values.

MSG_VIS_COMP_GET_MINIMUM_SIZE

SizeAsDWord MSG_VIS_COMP_GET_MINIMUM_SIZE();

This message returns the minimum size of the composite. It is used by the geometry manager if the composite has VCGDA_HAS_MINIMUM_SIZE set. This message does not invalidate or update the object's geometry.

Source: Unrestricted.

Destination: Any visible composite object--typically sent by a composite to itself during geometry calculations.

Parameters: None.

Return: A dword containing the minimum size of the composite. The high word is the width, and the low word is the height. Use the macros DWORD_WIDTH and DWORD_HEIGHT to extract the individual values from the SizeAsDWord structure.

Interception: Any composite that wants a minimum size should subclass this message and return its desired size. There is no need to call the superclass in your handler.

Tips: In your handler, use the macro MAKE_SIZE_DWORD to create the SizeAsDWord return value from the width and height. This macro has the same format as MAKE_SPACING_DWORD .

MSG_VIS_COMP_GET_MARGINS

void	MSG_VIS_COMP_GET_MARGINS(
        Rectangle *retValue);

This message returns the margins the composite should use when recalculating its child spacing. If you want a special left, top, right, or bottom margin around the composite's children, intercept this message and return the margin(s) in the appropriate field(s) of the Rectangle structure.

Source: Unrestricted.

Destination: Any visible composite object--typically sent by a composite to itself during geometry calculations.

Parameters: retValue A pointer to an empty Rectangle structure that will be filled with the composite's desired margins.

Return: The pointer to the filled Rectangle structure is preserved. The structure contains the four margins desired by the object outside of its bounds (e.g. if retValue->R_top is 100 upon return, the composite is requesting 100 points of extra "margin" spacing below its top bound before its children are placed).

Interception: Any composite that wants extra margin space added to its bounds when geometry is calculated should subclass this message and return its desired margins. There is no need to call the superclass in your handler.

MSG_VIS_COMP_GET_WRAP_COUNT

word	MSG_VIS_COMP_GET_WRAP_COUNT();

This message returns the number of children to be counted before wrapping if the composite has VCGA_WRAP_AFTER_CHILD_COUNT set.

Source: Unrestricted.

Destination: Any visible composite object--typically sent by a composite to itself during geometry calculations.

Parameters: None.

Return: The number of children that will be allowed before the composite wraps additional children to a new line.

Interception: Any composite that wants to wrap after a certain number of children should subclass this message and return the proper number of children. There is no need to call the superclass in your handler .

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using VisCompClass | Next: 3.2 Managing Graphic Windows


Managing Graphic Windows

VisComp: 3.2 Using VisCompClass: Managing Graphic Windows

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Managing Geometry

It is very rare that a visible object will want to create its own window without using a generic object. This practice is highly discouraged as well because it will almost certainly violate some principles of most specific UI specifications. You can, however, set up a visible object to have its own window with the following steps:

  1. Set up the VisComp object as a window group.
    Either instantiate a new VisComp object or set it up and load it in, off-screen. Then set the flags VTF_IS_WINDOW and VTF_IS_WIN_GROUP in the composite to make it a window and the top of the visible tree.
  2. Add visible children to the window object.
    Using MSG_VIS_ADD_CHILD , add any visible object children to the window group as you need. If some of the children had possibly been removed from the visible tree earlier, you may have to mark them invalid.
  3. Add your window to the field window.
    Determine the handle of the parent window you need, then set your window group object as a child of the parent.
  4. Set the top object in your window group visible.
    Set VA_VISIBLE using MSG_VIS_SET_ATTR to mark the window group visible. The window will be opened, and it will receive a MSG_META_EXPOSED indicating that it and all its children should draw themselves.
| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Managing Geometry


VisContent

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 VisContent Instance Data
| One-page Version of this Chapter for Easy Printing

VisContentClass provides objects that can be used to head visible trees and interact with the GenView. The VisContent is a necessary class for any application that plans on displaying visible objects in a view.

You should be familiar with visible objects, visible object trees, and the GenView class. These topics can be found in the VisClass chapter and the GenView chapter.

Because VisContentClass is a subclass of VisCompClass , it inherits all the instance data fields and messages of both that class as well as VisClass . You will likely want to read about both of those classes before reading this chapter. You can just read Basic VisContent Usage if you need a basic VisContent object--that section describes the most basic ways to use the VisContent without understanding its inner workings.

1 VisContent Instance Data
1.1 The VCNI_attrs Field
1.2 Fields That Affect the View
1.3 Fields That Affect the Document
1.4 Fields That Affect Input Events
2 Basic VisContent Usage
2.1 Setting Up Sizing Behavior
2.2 Messages Received from the View

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 VisContent Instance Data
| One-page Version of this Chapter for Easy Printing


VisContent Instance Data

VisContent: 1 VisContent Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: VisContent | Next: 2 Basic VisContent Usage

The VisContent has many instance data fields above and beyond those defined in VisClass and VisCompClass . These new instance fields deal with such topics as content attributes, the way the content interacts with the view, the document described by the content, and how the content handles input.

All the instance fields defined by VisContentClass and their definitions are shown in VisContent Instance Data Fields . Each field is described later in this section.

Code Display 17-1 VisContent Instance Data Fields

/* All the instance fields defined in VisContentClass are described below. You
 * will likely use very few of them, if any; see the later sections of this
 * chapter for specific information about each field. */
    /* The following fields deal with the GenView and the view window. */
    @instance optr				 VCNI_view = 0;
    @instance WindowHandle				 VCNI_window = 0;
    @instance word				 VCNI_viewHeight = 0;
    @instance word				 VCNI_viewWidth = 0;
    /* VCNI_attrs determines the content's attributes. */
    @instance  VisContentAttrs				 VCNI_attrs = 0;
    /* The possible flags for VCNI_attrs are
     *	VCNA_SAME_WIDTH_AS_VIEW						0x80
     *	VCNA_SAME_HEIGHT_AS_VIEW						0x40
     *	VCNA_LARGE_DOCUMENT_MODEL						0x20
     *	VCNA_WINDOW_COORDINATE_MOUSE_EVENTS						0x10
     *	VCNA_ACTIVE_MOUSE_GRAB_REQUIRES_LARGE_EVENTS						0x08
     *	VCNA_VIEW_DOC_BOUNDS_SET_MANUALLY						0x04	 */
    /* The following fields determine features of the content's document. */
    @instance PointDWord				 VCNI_docOrigin = {0, 0};
    @instance PointWWFixed				 VCNI_scaleFactor = {{0, 1}, {0, 1}};
     /* The following fields deal with how the content handles input. */
    @instance ChunkHandle				VCNI_prePassiveMouseGrabList = 0;
    @instance VisMouseGrab				VCNI_impliedMouseGrab =
                    {0, 0, {0, 0}, (VIFGF_MOUSE | VIFGF_PTR), 0};
    @instance VisMouseGrab				VCNI_activeMouseGrab =
                    {0, 0, {0, 0}, 0, 0};
    @instance ChunkHandle				VCNI_postPassiveMouseGrabList = 0;
    @instance KbdGrab				VCNI_kbdGrab = {0, 0};
    @instance FTVMCGrab				VCNI_focusExcl = {0, MAEF_FOCUS};
    @instance FTVMCGrab				VCNI_targetExcl = {0, MAEF_TARGET};
    @instance Handle				VCNI_holdUpInputQueue = 0;
    @instance word				VCNI_holdUpInputCount = 0;
    @instance byte				VCNI_holdUpInputFlags = 0;
    /* The type flags of the content are special and should not be altered. */
    @default		VI_typeFlags =		VTF_IS_COMPOSITE | VTF_IS_WINDOW |
                VTF_IS_CONTENT | VTF_IS_WIN_GROUP |
                VTF_IS_INPUT_NODE;

The VCNI_attrs Field

Fields That Affect the View

Fields That Affect the Document

Fields That Affect Input Events

| Up: GEOS SDK TechDocs | Up | Down | Prev: VisContent | Next: 2 Basic VisContent Usage


The VCNI_attrs Field

VisContent: 1.1 VisContent Instance Data: The VCNI_attrs Field

| Up: GEOS SDK TechDocs | Up | Prev: 1 VisContent Instance Data | Next: 1.2 Fields That Affect the View
VCNI_attrs, MSG_VIS_CONTENT_SET_ATTRS, MSG_VIS_CONTENT_GET_ATTRS

The VCNI_attrs field is a record of VisContentAttrs that contains several attributes which affect how the content object interacts with the view and with the visible object tree. You can set these attributes with MSG_VIS_CONTENT_SET_ATTRS dynamically, and you can retrieve them with MSG_VIS_CONTENT_GET_ATTRS .

The flags in this field are shown below. None of them is set by default.

VCNA_SAME_WIDTH_AS_VIEW
This flag indicates that the content's width should follow the width of the view window, if possible. Most likely, you will want to set this if the view is not horizontally scrollable.
VCNA_SAME_HEIGHT_AS_VIEW
This flag indicates that the content's height should follow the height of the view window, if possible. Most likely you will want to set this flag if the view is not vertically scrollable.
VCNA_LARGE_DOCUMENT_MODEL
This flag indicates that the content object manages a large document (32-bit coordinates rather than the standard 16-bit coordinates). For information on how this affects the content and its children, see the VisClass chapter.

VCNA_WINDOW_COORDINATE_MOUSE_EVENTS
This flag must be set if VCNA_LARGE_DOCUMENT_MODEL is set. It indicates that the associated GenView will pass input events with window coordinates rather than document coordinates. The default handlers in the content object will then automatically translate the events into document coordinates.

VCNA_ACTIVE_MOUSE_GRAB_REQUIRES_LARGE_EVENTS
This flag indicates that the object that currently has the active mouse grab requires mouse input events to carry large document coordinates rather than the standard document coordinates. This flag is set and reset with the message MSG_VIS_VUP_ALTER_INPUT_FLOW . Large content objects should not set this flag in their Goc declarations.
VCNA_VIEW_DOC_BOUNDS_SET_MANUALLY
Not often used, this flag indicates that the content should not send its document bounds off to the view during a geometry update. The GenView's document bounds must be set manually, most likely with the GenView message MSG_GEN_VIEW_SET_DOC_BOUNDS .
VCNA_VIEW_DOES_NOT_WIN_SCROLL
This flag indicates that the view does not scroll but instead sends MSG_META_CONTENT_VIEW_ORIGIN_CHANGED to the content when the user interacts with the scroller. The UI will use this flag to invalidate the correct region of the content. This should be set when ATTR_GEN_VIEW_DO_NOT_WIN_SCROLL is set in the GenView. See the GenView chapter for full information on view scrolling.

MSG_VIS_CONTENT_SET_ATTRS

void	MSG_VIS_CONTENT_SET_ATTRS(
        VisContentAttrs attrsToSet,
        VisContentAttrs attrsToClear);

This message sets the VCNI_attrs field of the content object according to the passed values.

Source: Unrestricted.

Destination: Any VisContent object.

Parameters: attrsToSet This is a record of VisContentAttrs to set. A flag set in this field will be set in the object's VCNI_attrs field.

attrsToClear This is a record of VisContentAttrs to clear. A flag set in this field will be cleared in the object's VCNI_attrs field. This parameter takes precedence over attrsToSet ; that is, if a flag is set in both parameters, it will end up cleared.

Return: Nothing.

Interception: Unlikely.

MSG_VIS_CONTENT_GET_ATTRS

VisContentAttrs MSG_VIS_CONTENT_GET_ATTRS();

This message returns the current contents of the object's VCNI_attrs field, a record of VisContentAttrs .

Source: Unrestricted.

Destination: Any VisContent object.

Parameters: None.

Return: A record of VisContentAttrs reflecting the flags currently set in the object's VCNI_attrs field.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 1 VisContent Instance Data | Next: 1.2 Fields That Affect the View


Fields That Affect the View

VisContent: 1.2 VisContent Instance Data: Fields That Affect the View

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 The VCNI_attrs Field | Next: 1.3 Fields That Affect the Document
VCNI_view, VCNI_viewHeight, VCNI_viewWidth, VCNI_window, MSG_VIS_CONTENT_GET_WIN_SIZE

Because the content must interact directly with the view, it must maintain some information about both the GenView object and its associated window. VisContentClass defines four fields that deal exclusively with view-related information. Each of these fields is described below.

VCNI_view
This field contains the optr of the GenView object. The content should not ever actually use this field for the view's optr; if you need to contact the view, you should instead use the messages MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS and MSG_VIS_VUP_CALL_OBJECT_OF_CLASS . The field will be set automatically by the GenView when the view is opened; the view will send a MSG_META_CONTENT_VIEW_OPENING .
VCNI_viewHeight
This field contains the height, in document coordinates, of the view window. This will be set by the view object and should not be altered by the application; it can be retrieved with the message MSG_VIS_CONTENT_GET_WIN_SIZE (below). The view automatically notifies the content each time the window size changes with MSG_META_CONTENT_VIEW_SIZE_CHANGED .
VCNI_viewWidth
This field contains the width, in document coordinates, of the view window. This will be set by the view object and should not be altered by the application; it can be retrieved with the message MSG_VIS_CONTENT_GET_WIN_SIZE (below). The view automatically notifies the content on each window resize with MSG_META_CONTENT_VIEW_SIZE_CHANGED .
VCNI_window
This field contains the window handle of the view's window. This field is set by the view object and should not be accessed by the application. If an object under the content needs to get the window handle, it should use MSG_VIS_QUERY_WINDOW . This field will be set automatically by the GenView when the view's window is first opened. The view will send the messages MSG_META_CONTENT_VIEW_WIN_OPENED and MSG_META_CONTENT_VIEW_OPENING to set the window handle. When the view window closes, the view will send MSG_META_CONTENT_VIEW_CLOSING and MSG_META_CONTENT_VIEW_WIN_CLOSED .

The messages shown above that are sent by the view to the content are detailed in Messages Received from the View .

MSG_VIS_CONTENT_GET_WIN_SIZE

SizeAsDWord MSG_VIS_CONTENT_GET_WIN_SIZE();

This message returns the size of the content object's associated window in terms of width and height.

Source: Unrestricted.

Destination: Any VisContent object.

Parameters: None.

Return: A SizeAsDWord value with the window's width in the high word and the window's height in the low word. Use the DWORD_HEIGHT and DWORD_WIDTH macros to extract the proper values.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 The VCNI_attrs Field | Next: 1.3 Fields That Affect the Document


Fields That Affect the Document

VisContent: 1.3 VisContent Instance Data: Fields That Affect the Document

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Fields That Affect the View | Next: 1.4 Fields That Affect Input Events
VCNI_docOrigin, VCNI_scaleFactor, MSG_VIS_CONTENT_SET_DOC_BOUNDS, MSG_VIS_CONTENT_RECALC_SIZE_BASED_ON_VIEW

The GenView object maintains quite a bit of information about the document as managed by the content object. The content must also keep information about the document and how the view is displaying it. This information is stored in two fields, VCNI_docOrigin and VCNI_scaleFactor , both detailed below.

The document bounds of the content are typically equal to the bounds of the VisContent itself. The content's bounds are stored in the VI_bounds field inherited from VisClass . When a content is managing layers or large documents, however, its bounds are set to zero. It then manages its document bounds within the layer objects and the GenView. When the document bounds change, a MSG_VIS_CONTENT_SET_DOC_BOUNDS should be sent to the content to get it to notify all its layer children and the GenView of the new bounds. This message is shown at the end of this section.

If the content is not a large document and is set up to follow the GenView's geometry (it has either or both of VCNA_SAME_WIDTH_AS_VIEW or VCNA_SAME_HEIGHT_AS_VIEW set), it will be affected by changes in the view's geometry. During geometry updates, the view will send it a MSG_VIS_CONTENT_RECALC_SIZE_BASED_ON_VIEW . This message sets the content's width and/or height and therefore affects its VI_bounds field. This message is also shown at the end of this section.

VCNI_docOrigin
This field contains the current origin of the view window. The origin is the location of the view's upper left corner in the document (where the scrollers are). This field is of type PointDWord , which has the following structure:
typedef struct {
    sdword   PD_x;    /* x coordinate of origin */
    sdword   PD_y;    /* y coordinate of origin */
} PointDWord;
Normally, this field is set when the view is first opened or when the view is scrolled, scaled, or otherwise changed in document position. The view will send the message MSG_META_CONTENT_VIEW_ORIGIN_CHANGED to indicate the origin is different from its current setting.
VCNI_scaleFactor
This field contains the current scale factor the view is displaying. Scaling is implemented almost entirely in the GenView object; some content objects, however, will want to react in a special way when the scale factor is changed. The scale factor is stored in a PointWWFixed structure, as follows:
typedef struct {
    WWFixed    PF_x;   /* horizontal scale factor */
    WWFixed    PF_y;   /* vertical scale factor */
} PointWWFixed;
The WWFixed structures that determine the scale factor in each dimension consist of two elements. This structure is shown below:
typedef struct {
    word    WWF_frac;   /* fractional portion */
    word    WWF_int;    /* integral portion */
} WWFixed;
The VCNI_scaleFactor field in the content is never set directly by the application; instead, it is set with MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED whenever the view's scale factor changes.

Messages that set these fields are discussed in Messages Received from the View .

MSG_VIS_CONTENT_SET_DOC_BOUNDS

void	MSG_VIS_CONTENT_SET_DOC_BOUNDS(@stack
        sdword	bottom,
        sdword	right,
        sdword	top,
        sdword	left);

This message is used to set the document bounds of a content's large document. The content will send MSG_GEN_VIEW_SET_DOC_BOUNDS to its view and MSG_VIS_LAYER_SET_DOC_BOUNDS to each of its children with the new bounds. The recipient content must have the flag VCNA_LARGE_DOCUMENT_MODEL set in VCNI_attrs .

Source: Unrestricted.

Destination: Any large VisContent object--if the content is not using the large document model, an error will result.

Parameters: bottom, right, top, left
 The new document bounds in 32-bit document coordinates.

Return: Nothing.

Interception: Unlikely.

Warnings: You may only send this message to a content that is using the large document model. All other contents will not handle this message but will result in a fatal error.

MSG_VIS_CONTENT_RECALC_SIZE_BASED_ON_VIEW

SizeAsDWord MSG_VIS_CONTENT_RECALC_SIZE_BASED_ON_VIEW();

This message causes the content to recalculate its size based on the view's geometry. It will try to set its width if it has VCNA_SAME_WIDTH_AS_VIEW set, and it will try to set its height if it has VCNA_SAME_HEIGHT_AS_VIEW set.

Source: Unrestricted--typically sent by the view or by the content to itself during geometry updates.

Destination: Any VisContent object.

Parameters: None.

Return: A SizeAsDWord value indicating the new size of the content. Use the macros DWORD_HEIGHT and DWORD_WIDTH to extract the appropriate values.

Interception: Unlikely .

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Fields That Affect the View | Next: 1.4 Fields That Affect Input Events


Fields That Affect Input Events

VisContent: 1.4 VisContent Instance Data: Fields That Affect Input Events

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Fields That Affect the Document | Next: 2 Basic VisContent Usage

One of the main features of VisContentClass is its ability to handle, manage, and pass on input events sent through the GenView. The content has a large amount of functionality built into it to provide these features.

Mouse and Keyboard Grabs

VCNI_prePassiveMouseGrabList, VCNI_impliedMouseGrab, VCNI_activeMouseGrab, VCNI_postPassiveMouseGrabList, VCNI_kbdGrab, MSG_VIS_CONTENT_UNWANTED_MOUSE_EVENT, MSG_VIS_CONTENT_UNWANTED_KBD_EVENT, MSG_VIS_CONTENT_TEST_IF_ACTIVE_OR_IMPLIED_WIN

VisContentClass , as the head of the visible tree displayed in the view, keeps track of which object in its tree has each different type of input grab. With this information, the content can simply pass the input event directly to the object that has the grab. It can also easily send the event to both the prepassive and postpassive grab objects, if any.

To do this, it uses five instance fields that applications can not directly access. These fields are altered by the messages that allow an object to gain and release the subject grab. (These messages are all detailed in the VisClass chapter.) The instance fields are listed below:

VCNI_prePassiveMouseGrabList
This field is a pointer to a chunkarray containing the list of objects that currently have the prepassive mouse grab.
VCNI_impliedMouseGrab
This field is a VisMouseGrab (described below) structure that contains details about the object which has the implied mouse grab.
VCNI_activeMouseGrab
This field is a VisMouseGrab (described below) structure that contains details about the object which has the active mouse grab.
VCNI_postPassiveMouseGrabList
This field is a pointer to a chunkarray containing the list of objects that currently have the postpassive mouse grab.
VCNI_kbdGrab
This field is a KbdGrab structure that contains details about the object which currently has the keyboard grab.

You will probably not ever have to know the structure of these fields, how to set them, or the information they contain. However the two structures VisMouseGrab and KbdGrab are shown in Grab Data Structures for your information.

Code Display 17-2 Grab Data Structures

/* These structures are obscure, and you will likely never have to use them. */
  /* The VisMouseGrab structure contains information about the object that
 * currently has the mouse grab. */
typedef struct {
    optr		VMG_object;		/* The optr of the object that has the grab.
                 * If no object has the grab, this is zero. */
    WindowHandle		VMG_gWin;		/* The window handle of the window with the object
                 * having the grab, if it's different from
                 * the content's window. If it's in the content's
                 * window, this field contains zero.	*/
    PointDWord		VMG_translation;		/* The 32-bit translation applied to mouse events
                 * if the large document model is in use. This is
                 * set with a previous message call. */
    VisInputFlowGrabFlags VMG_flags; /* A record of VisInputFlowGrabFlags,
                   * described below. */
    byte		VMG_unused;		/* Reserved byte. */
} VisMouseGrab;
/* The VisInputFlowGrabFlags determine the type and context of the grab. These
 * flags are not listed here for simplicity. You do not have to know these flags;
 * they are set with MSG_VIS_VUP_ALTER_INPUT_FLOW. */
  /* The KbdGrab structure contains information about the object that currently
 * has the keyboard grab. */
typedef struct {
    optr		KG_OD;		/* The optr of the object that has the
                 * keyboard grab. */
    word		KG_unused;		/* Reserved word. */
} KbdGrab;

In addition, the VisContent has the following messages that are affected by the input grab fields:

MSG_VIS_CONTENT_TEST_IF_ACTIVE_OR_IMPLIED_WIN
This message returns true if the passed window handle is the same as the active or implied window currently translating mouse events. It returns false otherwise. This is used by specific UI objects.
MSG_VIS_CONTENT_UNWANTED_MOUSE_EVENT
This message is sent by the content to itself if a mouse event has arrived and there is no implied or active grab in the content's visible tree. The default reaction is to beep if the event is a button press. It's highly unlikely that you'll send or intercept this message.
MSG_VIS_CONTENT_UNWANTED_KBD_EVENT
This message is sent by the content to itself if a keyboard event has arrived and there is no keyboard grab set up. It is highly unlikely that you will ever send or intercept this message.

MSG_VIS_CONTENT_TEST_IF_ACTIVE_OR_IMPLIED_WIN

Boolean	MSG_VIS_CONTENT_TEST_IF_ACTIVE_OR_IMPLIED_WIN(
        WindowHandle window);

This message checks to see if the passed window handle is the same as the window of the object having either the implied or active mouse grab. This is typically used by objects in a Specific UI library to determine if the mouse event was actually within the window or directly on the window's border.

Source: Unrestricted.

Destination: Any VisContent object.

Parameters: window The window handle to be checked.

Return: True if the window handle is the same as either the active or the implied window, false otherwise.

Interception: Unlikely.

MSG_VIS_CONTENT_UNWANTED_MOUSE_EVENT

void	MSG_VIS_CONTENT_UNWANTED_MOUSE_EVENT(
        VisMouseGrab *mouseGrab,
        word	inputState);

This message is received by the content if a mouse event was received and there was no active or implied grab. This is most frequently encountered when the user presses a mouse button outside a modal dialog box. The default action of this handler is to beep (on presses only, not releases) and get rid of the event as if it had been handled.

Source: Input flow mechanism.

Destination: The affected VisContent object.

Parameters: mouseGrab A pointer to the appropriate VisMouseGrab structure.

inputState
Same as passed with the actual event.

Return: Nothing.

Interception: Unlikely.

MSG_VIS_CONTENT_NOTIFY_ACTIVE_MOUSE_GRAB_WIN_CHANGED

void 	MSG_VIS_CONTENT_NOTIFY_ACTIVE_MOUSE_GRAB_WIN_CHANGED();

A message we send ourselves from within our MSG_VIS_VUP_ALTER_INPUT_FLOW handler in the case that VCNI_activeMouseGrab . VMG_gWin changes. Used by the specific UI's implementation of GenApplicationClass to keep track of the currently active window within the application.

Source: See above.

Destination: Self.

Parameters: None

Return: Nothing.

Interception: Intercepted by specific UI's implementation of GenApplication for the purpose of finding out a change in the active window within the application has occurred. It will in turn update the window system so that mouse grabs and pointer image changes work properly.

MSG_VIS_CONTENT_UNWANTED_KBD_EVENT

void	MSG_VIS_CONTENT_UNWANTED_KBD_EVENT(
        word	character,
        word	flags,
        word	state);

This message will be received by the content if a keyboard event was sent and there was no keyboard grab set up. The default action of the content is to beep (on presses only, not releases) and return as if the event had been processed.

Source: Input flow mechanism.

Destination: The affected VisContent object.

Parameters: character The keyboard character pressed.

flags
A word of flags: The low byte is a CharFlags record, and the high byte is a ShiftState record. Both are the same as passed with the original MSG_META_KBD_CHAR .
state
A word containing two values: The low byte is a record of ToggleState , and the high byte is the scan code. Both are the same as passed with the original MSG_META_KBD_CHAR .

Return: Nothing.

Interception: Unlikely.

Focus and Target

VCNI_focusExcl, VCNI_targetExcl, MSG_META_CONTENT_APPLY_DEFAULT_FOCUS

In addition to keeping track of which of its children have the mouse and keyboard grabs, the content also keeps track of which objects have the focus and target input exclusives. Both VCNI_focusExcl and VCNI_targetExcl contain a structure of type FTVMCGrab that describes the object that has the subject exclusive. The messages sent by the GenView that set these fields are described in Messages Received from the View .

Input Flow Control

VCNI_holdUpInputQueue, VCNI_holdUpInputCount, VCNI_holdUpInputFlags, MSG_VIS_CONTENT_HOLD_UP_INPUT_FLOW, MSG_VIS_CONTENT_RESUME_INPUT_FLOW, MSG_VIS_CONTENT_DISABLE_HOLD_UP, MSG_VIS_CONTENT_ENABLE_HOLD_UP

GEOS allows a visible tree to hold up input--that is, input will be stored elsewhere while the visible tree is doing something else. This can be useful if complex tree operations are going on and you don't want input to go to the wrong object.

VisContentClass provides three instance fields that define the input holdup mechanism. These three fields are

VCNI_holdUpInputQueue
This field contains the queue handle of the queue where held-up input will be temporarily stored. Input events will go into this event queue until they are allowed to be handled again; then they will be sent to their proper recipients.
VCNI_holdUpInputCount
This field contains a count of the number of objects that have requested that input be held up. If this count is positive, input will be held up and input events will go into the hold-up queue.
VCNI_holdUpInputFlags
This field contains a record of flags which determine the state of the hold-up mechanism. The following two flags are allowed:
HUIF_FLUSHING_QUEUE
This flag indicates that the hold-up queue is currently being flushed.
HUIF_HOLD_UP_MODE_DISABLED
This flag forces input events to flow normally. It is used primarily by GEOS to ensure that the user can interact with a system-modal dialog box.

VisContentClass has four messages that it sends to itself to set the state of information hold-up. These messages are detailed below.

MSG_VIS_CONTENT_HOLD_UP_INPUT_FLOW

void	MSG_VIS_CONTENT_HOLD_UP_INPUT_FLOW();

This message increments the count in VCNI_holdUpInputCount . If this count is nonzero and HUIF_HOLD_UP_MODE_DISABLED is clear, subsequent input events will be sent into the hold-up queue until either the flag is set or the count once more drops to zero.

Source: Unrestricted.

Destination: Any VisContent object.

Interception: Unlikely.

Warnings: Do not forget to resume input with a later use of the message MSG_VIS_CONTENT_RESUME_INPUT_FLOW .

MSG_VIS_CONTENT_RESUME_INPUT_FLOW

void	MSG_VIS_CONTENT_RESUME_INPUT_FLOW();

This message decrements the count in VCNI_holdUpInputCount . If the count becomes zero with this call, the hold-up event queue is flushed and all the events in it are "played back." If the count goes below zero, GEOS will give an error. Therefore, do not use this message unless it is preceded with a MSG_VIS_CONTENT_HOLD_UP_INPUT_FLOW .

Source: Unrestricted.

Destination: Any VisContent object.

Interception: Unlikely.

Warnings: If this message is used without first holding up input with MSG_VIS_CONTENT_HOLD_UP_INPUT_FLOW , an error will be the likely result. The error condition is the VCNI_holdUpInputCount field going below zero.

MSG_VIS_CONTENT_DISABLE_HOLD_UP

void	MSG_VIS_CONTENT_DISABLE_HOLD_UP();

This message sets the HUIF_HOLD_UP_MODE_DISABLED flag, forcing all input events to flow normally until the flag is cleared. In essence, it turns off the hold-up mechanism.

Source: Unrestricted.

Destination: Any VisContent object.

Interception: Unlikely.

MSG_VIS_CONTENT_ENABLE_HOLD_UP

void	MSG_VIS_CONTENT_ENABLE_HOLD_UP();

This message clears the HUIF_HOLD_UP_MODE_DISABLED flag, allowing input events to be held up in the hold-up event queue.

Source: Unrestricted.

Destination: Any VisContent object.

Interception: Unlikely.

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Fields That Affect the Document | Next: 2 Basic VisContent Usage


Basic VisContent Usage

VisContent: 2 Basic VisContent Usage

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 VisContent Instance Data | Next: 2.1 Setting Up Sizing Behavior

You will probably not have to subclass VisContentClass unless you are using a large document or you want the content to draw something in response to a MSG_VIS_DRAW . In most other cases, you can declare your content object directly on VisContentClass .

Most VisContent behavior is inherited directly from VisCompClass . Some additional behavior is detailed in the following sections.

If you are planning on using the document control objects with GenViews, you will not need VisContent objects. The GenDocument resolves at run-time into a subclass of VisContent and therefore replaces the need for an explicitly-defined VisContent object. See the Documents chapter for full information on GenDocuments.

Setting Up Sizing Behavior

Messages Received from the View

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 VisContent Instance Data | Next: 2.1 Setting Up Sizing Behavior


Setting Up Sizing Behavior

VisContent: 2.1 Basic VisContent Usage: Setting Up Sizing Behavior

| Up: GEOS SDK TechDocs | Up | Prev: 2 Basic VisContent Usage | Next: 2.2 Messages Received from the View

Most geometry behavior revolves around whether the GenView is scrollable or not. You can set up several different sizing behaviors depending on the attributes you set in the GenView. Some are listed below with the attributes you should set in both the GenView and the VisContent.

Typically, setting the view's attributes will involve either setting or not setting the GVDA_SCROLLABLE flag in the appropriate GenView dimension; also, you will want to be aware of the GVDA_NO_LARGER_THAN_CONTENT and GVDA_NO_SMALLER_THAN_CONTENT flags.

If the Content Is Fixed Size

Typically, if the content object is a fixed size, the view will either conform to the content's size or scroll in one or both dimensions. This behavior is determined entirely within the GenView's instance data. Three types of this behavior are shown in Sizing the View with a Fixed Content .

Code Display 17-3 Sizing the View with a Fixed Content

/* This code display shows three different types of sizing behavior of the GenView
 * if its VisContent object is of a fixed size. Note that if the content is
 * managing its geometry, its bounds (and therefore the view's) will be determined
 * by the content's children. */
/* The view window is scrollable in both dimensions. This will result in the view
 * being sizable and scrollable in both dimensions. */
@object GenViewClass MyView = {
    GVI_content = @MyVisContent;
    GVI_horizAttrs = @default | GVDA_SCROLLABLE;
    GVI_vertAttrs = @default | GVDA_SCROLLABLE;
};
/* The view window is scrollable in only the vertical dimension. It follows the
 * width of the VisContent object and therefore does not scroll vertically. The
 * VisContent's VI_bounds field should be set by the content. */
@object GenViewClass MyView = {
    GVI_content = @MyVisContent;
    GVDI_horizAttrs = @default				| GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;
    GVDI_vertAttrs = @default				| GVDA_SCROLLABLE;
};
/* The view window sizes itself exactly to the size of the VisContent's bounds.
 * The VisContent's VI_bounds field should be set appropriately by the content.
 * Note that this is not a valid combination for VisContents that display large
 * documents or layer objects. */
@object GenViewClass MyView = {
    GVI_content = @MyVisContent;
    GVDI_horizAttrs = @default				| GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;
    GVDI_vertAttrs = @default				| GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;
}

Another type of behavior with fixed-size contents is called "keeping the aspect ratio." The view and content can work together to allow the user to resize the view while automatically setting the view's scale factor to keep the entire content in the window. This might be used, for example, for a game board; the game could be resized, and the entire game board would stay in the view.

In this situation, the content calculates its new size based on one of the view's dimensions. For example, when the user resizes the view, the content may keep the width but calculate the height based on the width. The proper scale factor is then set, and the content does not have to do anything special beyond that. To gain this behavior, set up your view and content as shown in Keeping the View Aspect Ratio .

Code Display 17-4 Keeping the View Aspect Ratio

/* This example shows a view and its content. The content object is of a fixed
 * size, and the view is resizable. The content/view pair will keep the aspect
 * ratio to automatically figure the view's height based on its width and then
 * scale the image to keep the entire bounds of the content within the view
 * window. */
@object GenViewClass MyView = {
    GVI_content = @MyVisContent;
    GVDI_horizAttrs = @default				| GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;
    GVDI_vertAttrs = @default				| GVDA_KEEP_ASPECT_RATIO;
};
@object VisContentClass MyVisContent = {
    VI_bounds =		{0,	/* left bound */
         0,	/* top bound */
         250,	/* right bound */
         250};	/* bottom bound */
    VCI_comp =			/* put any children here */;
    VCI_geoAttrs = VCGA_CUSTOM_MANAGE_CHILDREN;
        /* This is set because typically a content's bounds are determined
         * by its children. If we want to set our own bounds, we should
         * custom manage our geometry. This is true of contents used with
         * the views in the previous example. */
};

If the Content Is Variable Size

Many visible trees will have contents that are of variable size. How the content determines its size differs from use to use; some contents will adjust their geometries to those of their view objects, and some will resize themselves based on the geometry of their children.

Typically, if the content resizes itself based on its children's geometry, the view either will be scrollable or will adjust its size to that of its content.

If the content rearranges its children to meet the size of the view, the view will not be scrollable and will not adjust its size to the content at all. Instead, the content will have the VCNA_SAME_WIDTH_AS_VIEW and VCNA_SAME_HEIGHT_AS_VIEW flags set in its VCNI_attrs field .

| Up: GEOS SDK TechDocs | Up | Prev: 2 Basic VisContent Usage | Next: 2.2 Messages Received from the View


Messages Received from the View

VisContent: 2.2 Basic VisContent Usage: Messages Received from the View

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Setting Up Sizing Behavior
MSG_META_CONTENT_SET_VIEW, MSG_META_CONTENT_VIEW_ORIGIN_CHANGED, MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED, MSG_META_CONTENT_VIEW_OPENING, MSG_META_CONTENT_VIEW_WIN_OPENED, MSG_META_CONTENT_VIEW_SIZE_CHANGED, MSG_META_CONTENT_VIEW_CLOSING, MSG_META_CONTENT_VIEW_WIN_CLOSED

A s detailed in the discussions on GenView, the view sends a sequence of messages to its content when the view is first opening and when it is closing. These messages set up the content's visible instance data and prime the visible tree to be drawn on the screen. The messages are handled by the default handlers in VisContentClass , and you do not need to add anything to them to make them work.

The messages sent to the content when the view is first created are

  1. MSG_META_CONTENT_SET_VIEW
     This message passes the view's optr to the content, setting the VCNI_view field properly.
  2. MSG_META_CONTENT_VIEW_ORIGIN_CHANGED
     This message passes the view's initial origin (which may be set other than the default) to the content.
  3. MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED
     This message passes the view's initial scale factor (which may be set other than the default) to the content.
  4. MSG_META_CONTENT_VIEW_OPENING
     This message is sent to the content when the view receives a MSG_VIS_OPEN . This notifies the content that the view is about to be put on the screen and that it should prepare itself to be drawn.
  5. MSG_META_CONTENT_VIEW_WIN_OPENED
     This message passes the window handle of the newly created view window so the content can record it in VCNI_window .
  6. MSG_META_CONTENT_VIEW_SIZE_CHANGED
     This message passes the view window's size (height and width) to the content so the content can determine its proper size and geometry.
  7. MSG_META_EXPOSED
     This message is sent when the view's window is finally on the screen. It signifies that the content should draw itself and then send MSG_VIS_DRAW s to all its children.

The view will also send certain messages to the content when different things happen to change the content's instance data:

  • MSG_META_CONTENT_VIEW_SIZE_CHANGED
     This message is passed whenever the view window's size changes for any reason.
  • MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED
     This message is passed whenever the view's scale factor is changed, usually due to the user setting it from a menu.
  • MSG_META_CONTENT_VIEW_ORIGIN_CHANGED
     This message is passed whenever the view's origin is changed, usually when the view is scrolled.
  • MSG_META_EXPOSED
     This message is passed to the content whenever a portion of the view window becomes exposed and must be drawn. The content automatically translates this into a MSG_VIS_DRAW .

When the view is shutting down, it will send the following three messages to the content to set the proper data:

  1. MSG_META_CONTENT_VIEW_CLOSING
     This message is sent to the content when the view receives a MSG_VIS_CLOSE . It indicates that the view is being taken off the screen and that all the visible objects in the content's tree should remove themselves from the screen.
  2. MSG_META_CONTENT_VIEW_WIN_CLOSED
     This message is sent when the view's window is finally destroyed. The copy of the window handle in VCNI_window will be thrown out now so no drawing to the stale window handle will be done.
  3. MSG_META_CONTENT_SET_VIEW
     This message is sent to set the content's VCNI_view field to a null handle. When the view is finally taken off the screen, it no longer should have a content associated with it since it is meaningless to work with a visible tree that is not on the screen. If the view is opened again later, the content will receive another MSG_META_CONTENT_VIEW_OPENING and will once again be passed the view's optr.

MSG_META_CONTENT_SET_VIEW

void	MSG_META_CONTENT_SET_VIEW(
        optr	view);

This message passes the optr of the GenView object that will display this content object. The default handler will set the content's VCNI_view field to the passed optr. This message is also used when the view has been shut down; the passed optr will be null.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: view The optr of the GenView using this object as its content.

Return: Nothing.

Interception: Unlikely.

MSG_META_CONTENT_VIEW_ORIGIN_CHANGED

void	MSG_META_CONTENT_VIEW_ORIGIN_CHANGED(@stack
        WindowHandle	 viewWindow,
        sdword		xOrigin,
        sdword		yOrigin);

This message notifies the content that the view's origin has changed. The content will set its VCNI_docOrigin field to the passed values.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: viewWindow The window handle of the GenView's window.

xOrigin
The new horizontal origin of the view.
yOrigin
The new vertical origin of the view.

Return: Nothing.

Interception: Any content that is managing large documents will probably need to subclass this message and apply the proper translations for the 32-bit coordinates.

MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED

void	MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED(@stack
        WindowHandle viewWindow,
        WWFixedAsDWord		yScaleFactor,
        WWFixedAsDWord		xScaleFactor);

This message notifies the content that the view window's scale factor has changed. The content will set its VCNI_scaleFactor field to the passed values.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: viewWindow The window handle of the GenView's window.

yScaleFactor
The new vertical scale factor.
xScaleFactor
The new horizontal scale factor.

Return: Nothing.

Interception: Any content that is managing large documents will probably need to subclass this message and apply the proper translations for the 32-bit coordinates.

MSG_META_CONTENT_VIEW_WIN_OPENED

void	MSG_META_CONTENT_VIEW_WIN_OPENED(
        word		viewWidth,
        word		viewHeight,
        WindowHandle		viewWindow);

This message notifies the content that the view's window has been created and is being put on the screen. This message will be followed by MSG_META_EXPOSED , so the content should not draw anything here.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: viewWidth The new window's initial width.

viewHeight
The new window's initial height.
viewWindow
The window handle of the GenView's window.

Return: Nothing.

Interception: A content may wish to subclass this message if it needs to initialize data before the view's window is actually on the screen.

MSG_META_CONTENT_VIEW_OPENING

void	MSG_META_CONTENT_VIEW_OPENING(
        optr	view);

This message notifies the content that the view window is being put on the screen. Although the window will usually be fully realized by the time the content handles this message, the content should not draw anything in this handler. Because the view and content are often in different threads, a context switch could have occurred and the window might not be fully realized. This message will be followed by a MSG_META_EXPOSED indicating that the visible tree can be drawn and that the window is fully opened.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: view The optr of the GenView.

Return: Nothing.

Interception: A content may wish to subclass this message to initialize data before the view window is fully opened.

MSG_META_CONTENT_VIEW_SIZE_CHANGED

void	MSG_META_CONTENT_VIEW_SIZE_CHANGED(
        word		viewWidth,
        word		viewHeight,
        WindowHandle		viewWindow);

This message is sent to the content whenever the view's size changes for any reason. The passed height and width will be stored in the content's VCNI_viewHeight and VCNI_viewWidth fields.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: viewWidth The new width of the view window.

viewHeight
The new height of the view window.
viewWindow
The window handle of the GenView's window.

Return: Nothing.

Interception: Any content that is managing large documents may need to subclass this message to apply translations for 32-bit coordinates.

MSG_META_CONTENT_VIEW_CLOSING

void	MSG_META_CONTENT_VIEW_CLOSING();

This message indicates to the content that the view window is being shut down. The content should remove the visible tree from the screen and should prepare itself for the window to be closed.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Interception: A content may subclass this message if it wants to do additional things when the view is taken off the screen.

MSG_META_CONTENT_VIEW_WIN_CLOSED

void	MSG_META_CONTENT_VIEW_WIN_CLOSED(
        WindowHandle viewWindow);

This message indicates that the view's window has been shut down, taken off the screen, and destroyed. The content responds to this message by discarding the window handle stored in its VCNI_window field. The content should already have removed itself from the screen when it received an earlier MSG_VIS_CLOSE .

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: viewWindow The window handle of the GenView's window.

Return: Nothing.

Interception: A content may subclass this message to clean up after the view window is closed (e.g. if the content cached the view's window handle to a global variable, it will need to zero that handle now).

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Setting Up Sizing Behavior


System Object Classes

GEOS SDK Documentation: System Object Classes

| Up: GEOS SDK TechDocs

The following documents describe GEOS object classes which are low-level. Most programmers will not have much use for the low-level mechanisms described in these documents.

GenClass
The ancestor of all generic UI object classes.
MetaClass
The ancestor of all object classes.
Input
How keyboard input finds its way to the intended object; mouse- and pen-input is discussed for those devices which support such.
GenProcessClass
All applications subclass GenProcessClass; all applications must. Few will alter the class' behavior; this chapter describes that behavior.
ProcessClass
Manages a thread.
GenApplicationClass
Ancestor class of the Nokia 9000i Communicator-enhanced FoamSubApplicationClass.
Clipboard
The mechanisms behind copy, paste, and quick-transfer.
GenDisplay
The object class underlying floating windows.
Generic UI Controllers
What you need to know to write a custom controller class.
| Up: GEOS SDK TechDocs


The Clipboard

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Overview
| One-page Version of this Chapter for Easy Printing

For an application to survive in the GUI world today, it must support at least the ever-present "Cut, Copy, and Paste" functions to which users have grown so attached. GEOS makes this very simple through the Clipboard and Quick-Transfer mechanisms.

All applications should support and use these mechanisms in order to maintain consistency throughout the system. In addition, these mechanisms provide certain functionality for data transfer that can be easily used.

To understand this chapter fully, you should have a working knowledge of Virtual Memory management as well as some familiarity with the concepts of mouse input. However, the concepts of the Clipboard and quick-transfer mechanisms are simple and may be understood without having read those sections.

1 Overview
1.1 Cut, Copy, and Paste
1.2 Quick-Transfer
2 Transfer Data Structures
2.1 The Transfer VM File Format
2.2 ClipboardItemFormatInfo
2.3 Transfer Data Structures
2.4 Clipboard Item Formats
3 Using The Clipboard
3.1 Registering with the Clipboard
3.2 Managing the Edit Menu
3.3 The GenEditControl
3.4 Handling Cut and Copy
3.5 Handling Paste
3.6 Unregistering with the Clipboard
3.7 Implementing Undo
3.8 Transfer File Information
3.9 Undoing a Clipboard Change
4 Using Quick-Transfer
4.1 Supporting Quick-Transfer
4.2 Quick-Transfer Procedure
4.3 Quick-Transfer Data Structures
4.4 Source Object Responsibility
5 Shutdown Issues

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Overview
| One-page Version of this Chapter for Easy Printing


Overview

The Clipboard: 1 Overview

| Up: GEOS SDK TechDocs | Up | Down | Prev: The Clipboard | Next: 2 Transfer Data Structures

The user understands two ways to cut and paste information: The first is to use the Edit menu and the Cut , Copy , and Paste functions. The second is to use the mouse by selecting and dragging text, graphics, or objects with the quick-copy and quick-move functions. For a full description of how these two operations are accomplished, see the user's guide provided in the Geoworks retail products.

Both these functions use similar data structures; a single flag determines whether the operation is a quick-transfer or a normal Clipboard operation. However, the procedures and rules of usage have significant differences.

You should be familiar with the features and uses of both the Clipboard and the quick-transfer mechanism. If you are, you can skip this section; however, if you are not familiar with the features and rules governing the Edit menu and the quick-transfer operations, you most likely will want to read this section.

Cut, Copy, and Paste

Quick-Transfer

| Up: GEOS SDK TechDocs | Up | Down | Prev: The Clipboard | Next: 2 Transfer Data Structures


Cut, Copy, and Paste

The Clipboard: 1.1 Overview: Cut, Copy, and Paste

| Up: GEOS SDK TechDocs | Up | Prev: 1 Overview | Next: 1.2 Quick-Transfer

Even the simplest, text-using applications provide an Edit menu with the Cut , Copy , and Paste options.

The Edit menu works with a hidden data area called the Clipboard . The Clipboard has no visual representation, and it can contain any format of data from text to graphics strings to custom formats defined by applications. The Clipboard's structure and implementation are completely invisible to the user beyond the functions Cut , Copy , and Paste .

The most common use of the Edit menu is within a word processor or draw program. For example, when a user wants to place a graphic from GeoDraw into GeoWrite, or when he wants to rearrange text in his NotePad, he can use the cut, copy, and paste operations.

When a user selects an item such as the word "puddle," the Cut option becomes enabled. This indicates that the user can remove the selection from the document and it will be placed on the Clipboard (anything already on the Clipboard will be replaced with the selection). When nothing is selected, the Cut option is disabled.

In order to enable the Copy option, the user must select an item, just as with Cut . Copying, however, will not remove the selection from the document--it will simply create a copy of the selection and place it on the Clipboard, removing whatever had previously been on the Clipboard.

Pasting is allowed only when something in a compatible data format is already sitting on the Clipboard. The Paste operation copies what is on the Clipboard into a document at the current insertion point (in the case of text, this is where the cursor was last placed); if something is already selected, the selection will be replaced with the pasted material. If there is nothing on the Clipboard, then there is nothing to paste; therefore, the option is disabled.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Overview | Next: 1.2 Quick-Transfer


Quick-Transfer

The Clipboard: 1.2 Overview: Quick-Transfer

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Cut, Copy, and Paste | Next: 2 Transfer Data Structures

The user can copy and move selected items without using the Edit menu; he simply drags the item using the quick-transfer button of his mouse, and the item will be either copied or moved.

There are several rules which govern whether a move or copy is taking place. The user can override these rules with a certain set of keypresses to force either a copy or move. Copy is equivalent to using the Copy and Paste functions in succession, and move is equivalent to using the Cut and Paste functions in succession.

The operation of a quick transfer (move or copy), however, depends on whether the transfer is across documents or internal to a single document. If the transfer is internal, it should become a move. If the transfer occurs across documents, it should become a copy. (Note that, for the purposes of quick-transfer, the term document is used to refer to anything the user will conceptually view as a single data holder--for example, a GeoManager "document" consists of one disk.)

Because not all documents will support a given transfer operation (for example, a text file editor can not receive a GeoDraw graphic), the user is provided with immediate feedback about the type and validity of the transfer.

For example, if a GeoDraw ellipse was selected and a quick-transfer was begun, immediately the mouse pointer would show the transfer as a move operation. As the user moves the mouse outside the GeoDraw document and into another GeoDraw document, the cursor changes to indicate a copy operation. If the user then moves the mouse over a text-only document, a "no operation," or "invalid," cursor would be shown, indicating that the ellipse could not be received.

Some applications may implement an additional feature that provides feedback to the user: modifying the source object as the type of transfer changes. For example, a drawing program may wish to alter the shape or color of an object while it is being quick-moved but leave it normal during a quick-copy or no-operation transfer.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Cut, Copy, and Paste | Next: 2 Transfer Data Structures


Transfer Data Structures

The Clipboard: 2 Transfer Data Structures

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Overview | Next: 3 Using The Clipboard

Both the Clipboard and quick-transfer mechanisms use similar structures to accomplish data transfer. These data structures are owned and managed by the GEOS User Interface; however, they are accessible to all geodes in the system through a number of routines and messages.

The Clipboard consists of a VM file known as the Transfer VM File. The quick-transfer mechanism uses the same file for its data transfer. Typically, this is a file designated and managed by the User Interface.

In addition, certain formats of data are supported automatically--GEOS text and graphics strings are special formats that are described below. Geodes can create their own specific formats; for example, GeoManager uses a special format for quick-transfer of files between disks and directories.

The Transfer VM File Format

ClipboardItemFormatInfo

Transfer Data Structures

Clipboard Item Formats

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Overview | Next: 3 Using The Clipboard


The Transfer VM File Format

The Clipboard: 2.1 Transfer Data Structures: The Transfer VM File Format

| Up: GEOS SDK TechDocs | Up | Prev: 2 Transfer Data Structures | Next: 2.2 ClipboardItemFormatInfo

The Transfer VM File is a normal VM file, managed by the UI. It contains several components, each of which is accessed through special routines that take care of nearly all the synchronization issues involved with the Clipboard and quick-transfer mechanisms. Because both the Clipboard and the quick-transfer mechanism use this file and its data structures, these structures are detailed here; the following section ( Using The Clipboard ) details how to use them for either the Clipboard or the quick-transfer mechanism.

The Transfer VM File has one header block for the Clipboard transfer item and another for the quick-transfer transfer item. These headers have the same structure, and they contain all the information necessary to work with the file's contents. They contain pointers to and information about each format available for both the Clipboard and the quick-transfer functions. The transfer item's header is shown in ClipboardItemHeader .

Code Display 7-1 ClipboardItemHeader

/* The ClipboardItemHeader structure details what formats, if any, exist in the
 * Transfer VM File for both the Clipboard and quick-transfer mechanisms.*/
typedef struct {
        /* The CIH_owner field is the optr of the object that put
         * the current transfer item into the Transfer VM File. */
    optr			CIH_owner;
        /* The CIH_flags field determines whether the transfer item is used by
         * the quick transfer mechanism or by the normal Clipboard. If this
         * field is equal to the constant CIF_QUICK, then the transfer item
         * belongs to the quick transfer mechanism. If it is any other value,
         * the transfer item belongs to the normal Clipboard. */
    ClipboardItemFlags			CIH_flags;
      /* The ClipboardItemFlags type has one predefined value:
     *	CIF_QUICK		0x4000
     * If this flag does not apply, use TIF_NORMAL, which is 0x0000. */
        /*  The CIH_name field is a 33-character buffer that should contain a
         * null-terminated character string representing the name of the given
         * transfer item. */
    ClipboardItemNameBuffer 			CIH_name;
        /* The CIH_formatCount field indicates the number of formats of the
         * transfer item currently available. For example, this field would be
         * 2 if CIF_TEXT and CIF_GRAPHICS_STRING formats were supported and
         * available in the Transfer VM File. */
    word			CIH_formatCount;
        /* The CIH_sourceID field contains information about the source
         * document of the transfer. Typically, this will be the optr of the
         * parent GenDocument object. */
    dword			CIH_sourceID;
        /* The CIH_formats field is actually an array of ten
         * ClipboardFormatInfo structures, each of which contains the
         * important details about a specific format of the transfer item. */
    FormatArray			CIH_formats;
        /* This field is reserved and should not be used. */
    dword			CIH_reserved;
} ClipboardItemHeader ;
/* The FormatArray type is an array of ClipboardItemFormatInfo structures. The
 * definition of this field is shown here: */
 typedef	ClipboardItemFormatInfo			FormatArray[CLIPBOARD_MAX_FORMATS];
    /* CLIPBOARD_MAX_FORMATS is a constant defining the maximum number of
     * formats allowed for any given transfer item. It is defined as 10. */

The rest of the Transfer VM File consists of VM blocks containing the data that is to be transferred. Each format supported will have its own VM block or series of VM blocks, pointed to by the ClipboardItemFormatInfo structure in the item's header.

The Transfer VM File actually contains two transfer items: One for the Clipboard and one for the quick-transfer mechanism (see the figure below). When calling ClipboardQueryItem() , the requesting geode must specify which item it wants. See Using The Clipboard and Using Quick-Transfer .

| Up: GEOS SDK TechDocs | Up | Prev: 2 Transfer Data Structures | Next: 2.2 ClipboardItemFormatInfo


ClipboardItemFormatInfo

The Clipboard: 2.2 Transfer Data Structures: ClipboardItemFormatInfo

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 The Transfer VM File Format | Next: 2.3 Transfer Data Structures

This structure contains information about a specific format of the transfer item. The Transfer VM File will support up to ten formats of a given item at once (for both the Clipboard and quick-transfer); each of these formats is stored in its own VM block or VM chain and is represented in the header by a ClipboardItemFormatInfo structure in the array CIH_formats .

The ClipboardItemFormatInfo structure contains other information about the specific format as well as space for two extra words of data. The structure is shown in ClipboardItemFormatInfo .

Each element in the CIH_formats array contains two items: One word represents the manufacturer ID of the geode responsible for the format (useful if a custom format is used within several applications from a single manufacturer), and the other represents the actual format number. To combine these words into a ClipboardItemFormatID record, or to extract either word from the record, use the macros shown after ClipboardItemFormatInfo .

Code Display 7-2 ClipboardItemFormatInfo

/* The ClipboardItemFormatInfo structure contains information about a given
 * format of the transfer item. */
typedef struct {
        /* CIFI_format contains a format ID as well as the
         * manufacturer ID of the geode responsible for the format. */
    ClipboardItemFormatID	 		CIFI_format;
        /* CIFI_extra1 and CIFI_extra2 are extra words provided for
         * format-specific use. */
    word		CIFI_extra1;
    word		CIFI_extra2;
        /* CIFI_vmChain is a VM handle pointing to the first block in the
         * specific format. */
    VMChain		CIFI_vmChain;
        /* CIFI_renderer contains the token of the geode that created
         * the format. */
    GeodeToken		CIFI_renderer;
} ClipboardItemFormatInfo ;
| Up: GEOS SDK TechDocs | Up | Prev: 2.1 The Transfer VM File Format | Next: 2.3 Transfer Data Structures


Transfer Data Structures

The Clipboard: 2.3 Transfer Data Structures: Transfer Data Structures

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 ClipboardItemFormatInfo | Next: 2.4 Clipboard Item Formats

Two structures are used with specific routines when dealing with the transfer mechanisms. The ClipboardQueryArgs structure is returned by ClipboardQueryItem() , and the ClipboardRequestArgs structure is returned by ClipboardRequestItemFormat() . Both routines are used during a Paste operation, and both structures are shown in ClipboardQueryArgs and ClipboardRequestArgs .

Note that the CQA_header field is of type TransferBlockID . This type is a dword made up of two word-sized components: a VM file handle and a VM block handle. The three macros listed after ClipboardQueryArgs and ClipboardRequestArgs can be used to create the TransferBlockID argument and extract either of the components from the whole.

Code Display 7-3 ClipboardQueryArgs and ClipboardRequestArgs

/* ClipboardQueryArgs is filled by ClipboardQueryItem(), which is called when
 * determining whether a transfer item exists. */
typedef struct {
    word		CQA_numFormats;		/* the total number of formats available */
    optr		CQA_owner;		/* the optr of the originating object */
    TransferBlockID CQA_header;				/* The combined VM file handle and VM block
                 * handle of the block containing the
                 * ClipboardItemHeader */
} ClipboardQueryArgs;
/* ClipboardRequestArgs is filled by ClipboardRequestItemFormat(), which is called
 * when the application wants to retrieve the current transfer item. */
typedef struct {
    VMFileHandle		CRA_file;		/* The VM file handle of the transfer file */
    VMChain		CRA_data;		/* The handle of the VM chain containing the
                 * transfer item */
    word		CRA_extra1;		/* an extra word of data */
    word		CRA_extra2;		/* another extra word of data */
} ClipboardRequestArgs;
| Up: GEOS SDK TechDocs | Up | Prev: 2.2 ClipboardItemFormatInfo | Next: 2.4 Clipboard Item Formats


Clipboard Item Formats

The Clipboard: 2.4 Transfer Data Structures: Clipboard Item Formats

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Transfer Data Structures | Next: 3 Using The Clipboard

There are several built-in transfer formats that many GEOS applications may support; each of these types is an enumeration of ClipboardItemFormat . Additionally, custom formats can be defined to allow special data structures to be cut, copied, pasted, or quick-transferred without translation into text or graphics strings. The Transfer VM File may contain up to ten formats of a given transfer item. ClipboardItemFormat is shown below.

typedef enum /* word */ {
    CIF_TEXT,
    CIF_GRAPHICS_STRING,
    CIF_FILES,
    CIF_SPREADSHEET,
    CIF_INK,
    CIF_GROBJ,
    CIF_GEODEX,
    CIF_BITMAP,
    CIF_SOUND_SYNTH,
    CIF_SOUND_SAMPLE
} ClipboardItemFormat;

A transfer item of CIF_TEXT format is headed by a TextTransferBlockHeader structure. The text follows this header in the VM chain. A transfer item of CIF_GRAPHICS_STRING format is simply the entire GString stored in the transfer VM file in the VM chain.

Because every format identifier has two components, it is highly unlikely that two different designers will create overlapping custom formats.

The format is defined as a ClipboardItemFormatID type, which is a dword composed of two word-sized pieces. The first piece is a constant representing the format ID number (such as CIF_TEXT or CIF_GRAPHICS_STRING). The second piece is a constant representing the Manufacturer ID number of the manufacturer responsible for creating the format.

To create a custom format, simply define these two items as appropriate (your Manufacturer ID should be set already). Then define your format to fit within the structures used by the Clipboard (shown above).

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Transfer Data Structures | Next: 3 Using The Clipboard


Using The Clipboard

The Clipboard: 3 Using The Clipboard

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Transfer Data Structures | Next: 4 Using Quick-Transfer
ClipboardQueryItem(), ClipboardRegisterItem(), ClipboardDoneWithItem()

To use the Clipboard, your application must have an Edit menu (or a GenEditControl object) and an object which can implement the Cut, Copy, and Paste operations. This object is often the application's Process object or some other coordinating object. This object must be able to do each of the things in the following list:

  • Register with the Clipboard
    In a multitasking, multithreaded system, another application could change the Clipboard's contents. By registering with the Clipboard on application startup, the object will receive notification whenever the Clipboard's contents are changed (including when your application perpetrates the change). Registration is normally accomplished via the ClipboardAddToNotificationList() routine. The GCN notification list GCNSLT_TRANSFER_NOTIFICATION is used.Objects on this list can intercept MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED and MSG_META_CLIPBOARD_NOTIFY_TRANSFER_ITEM_FREED .
  • Maintain the Edit Menu
    In order to provide the user with the Cut , Copy , and Paste features, your application must have an Edit menu. Most programmers will simply include a GenEditControl object to create and maintain the Edit menu. Your clipboard-management object must update the triggers properly whenever the contents of the Clipboard are changed.
  • Handle MSG_META_CLIPBOARD_COPY, MSG_META_CLIPBOARD_CUT
     When the user issues a Cut or Copy order, the object must put the proper data into the Clipboard Transfer VM File.
  • Handle MSG_META_CLIPBOARD_PASTE
     When the user issues a Paste order, the object must query the Clipboard to ensure a proper format is available and then copy the information from the Clipboard.
  • Check out when shutting down
    Each object that registers with the Clipboard must unregister when shutting down. Otherwise, the UI might try to send notification to a defunct object or process, resulting in unpredictable behavior.

Because the Clipboard is constantly in use by many different threads, you must always gain exclusive access to the transfer VM file when you want to use it. After you're done with the transfer file, you should relinquish exclusive access so other threads can continue to use it.

For operations that involve changing the transfer item (cut and copy, for example), you must register your new transfer item with ClipboardRegisterItem() , which also allows other threads to use the file. For operations that involve looking at but not changing the transfer item, you should use ClipboardQueryItem() . Since you have no changes to register, you must later use ClipboardDoneWithItem() to give up your exclusive access to the transfer VM file.

Registering with the Clipboard

Managing the Edit Menu

The GenEditControl

Handling Cut and Copy

Handling Paste

Unregistering with the Clipboard

Implementing Undo

Transfer File Information

Undoing a Clipboard Change

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Transfer Data Structures | Next: 4 Using Quick-Transfer


Registering with the Clipboard

The Clipboard: 3.1 Using The Clipboard: Registering with the Clipboard

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using The Clipboard | Next: 3.2 Managing the Edit Menu
ClipboardAddToNotificationList()

Because the Clipboard is a system entity available to all geodes, another thread may change it without your application noticing. The Clipboard therefore provides notification for this case. Because the Clipboard does not know which geodes are interested in its contents, however, applications must register when they first start up.

Calling ClipboardAddToNotificationList() allows an application to add an object to the list of those notified of changes to the Clipboard. This routine should be called by whichever object is going to be handling the Cut, Copy, and Paste operations, typically in the object's MSG_META_INITIALIZE handler. If the object handling the Clipboard operations is the application's Process object, however, it may call ClipboardAddToNotificationList() in its MSG_GEN_PROCESS_OPEN_APPLICATION handler.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using The Clipboard | Next: 3.2 Managing the Edit Menu


Managing the Edit Menu

The Clipboard: 3.2 Using The Clipboard: Managing the Edit Menu

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Registering with the Clipboard | Next: 3.3 The GenEditControl
MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED, ClipboardTestItemFormat()

The Edit menu is simply a normal menu with several standard triggers. Most applications will simply include a GenEditControl object in their UI, add a menu GenInteraction of type GIGT_EDIT_MENU, and leave the Edit menu construction up to them (see The GenEditControl ). Some, however, may want to create their own menu and triggers. A sample of this type of setup is shown in A Sample Edit Menu .

Code Display 7-4 A Sample Edit Menu

/* Other objects, including a GenPrimary as the parent of the GenInteraction, are
 * left out for clarity. */
/* The GenInteraction is the menu in which the three triggers will appear. */
@object GenInteractionClass EditMenu = {
    GI_visMoniker = `E', "Edit";
    GI_comp = EditCut, EditCopy, EditPaste;
    GII_visibility = GIV_POPUP;
}
/* The Cut trigger sends a MSG_META_CLIPBOARD_CUT to the Process
 * object when pressed. */
@object GenTriggerClass EditCut = {
    GI_visMoniker = `t', "Cut";
    GTI_destination = process;
    GTI_actionMsg = MSG_META_CLIPBOARD_CUT;
}
/* The Copy trigger sends a MSG_META_CLIPBOARD_COPY to the
 * Process object when pressed. */
@object GenTriggerClass EditCopy = {
    GI_visMoniker = `C', "Copy";
    GTI_destination = process;
    GTI_actionMsg = MSG_META_CLIPBOARD_COPY;
}
/* the Paste trigger is set up initially disabled. It sends a
 * MSG_META_CLIPBOARD_PASTE to the Process object when pressed. */
@object GenTriggerClass EditPaste = {
    GI_visMoniker = `P', "Paste";
    GTI_destination = process;
    GTI_actionMsg = MSG_META_CLIPBOARD_PASTE;
    GI_states = @default & ~GS_ENABLED;
}

Some Edit menus may also contain other triggers such as "Delete Event," or "Remove Item." These triggers, however, are not standard and must be implemented exclusively by the application.

Two main rules govern the maintenance of the Edit menu:

  • The Cut and Copy triggers are enabled only when some data is selected that may be cut or copied from the document into the Clipboard.
  • The Paste trigger is enabled only when data in a pasteable format exists in the Clipboard's Transfer VM File.

The first rule must be implemented entirely by the application; the Clipboard will not enable or disable the Copy or Cut triggers. The second rule, however, requires that the application be notified whenever the Clipboard's contents change--it is possible, for example, for the application to copy data to the Clipboard, enable its Paste trigger, and have another application then also copy some custom data to the Clipboard. In this case, the original application must disable its Paste trigger if it can not read the data.

Whenever the Clipboard's contents change, the UI will send notification to all objects that have registered with it. The notification will be in the message MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED . Use of this message can be found in the ClipSamp sample application.

Code Display 7-5 Handling Clipboard Changes

/* MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED is sent with no
 * parameters and requires no return value. */
/* The strategy of this message is to first check whether the CIF_TEXT format, the
 * only format supported by this sample application, is available on the Clipboard.
 * If so, the Paste trigger is enabled; if not, the Paste trigger is disabled. */
@method MyClipProcessClass, MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED
{
    ClipboardQueryArgs			query;			/* A structure of arguments */
    Boolean			endisable = FALSE;			/* The trigger is disabled */
    /* Call ClipboardQueryItem() to gain exclusive access to and information about
     * the current transfer item. Pass it zero indicating we're checking the
     * normal transfer item (not the quick-transfer item) and the empty arguments
     * structure. */
    ClipboardQueryItem(TIF_NORMAL, &query);
    /* If there are any formats, then test if CIF_TEXT is one of them. The routine
     * ClipboardTestItemFormat() tests a format against all those available and
     * returns true if it is supported, false if it is not. Use the macro
     * FormatIDFromManufacturerAndType to create the format argument. If CIF_TEXT
     * is not supported, then the enDisable argument is set to TRUE. */
    if (query.CQA_numFormats) {
    if (ClipboardTestItemFormat(query.CQA_header,
        FormatIDFromManufacturerAndType(MANUFACTURER_ID_ME, CIF_TEXT))) {
        endisable = TRUE;
    }
    }
    /* Because we've found out what we need to know, restore the Clipboard with a
     * call to ClipboardDoneWithItem(). This routine takes the transfer item's
     * header and returns nothing; it also relinquishes our exclusive
     * access to the Clipboard and is therefore very important. */
    ClipboardDoneWithItem(query.CQA_header);
    /* Now, if endisable is true, set the Paste trigger enabled. If endisable is
     * false, set it disabled. These operations are accomplished by sending the
     * appropriate message to the trigger object. */
    if (endisable) {
    @call EditPaste::MSG_GEN_SET_ENABLED(VUM_NOW);
    } else {
    @call EditPaste::MSG_GEN_SET_NOT_ENABLED(VUM_NOW);
    }
}
| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Registering with the Clipboard | Next: 3.3 The GenEditControl


The GenEditControl

The Clipboard: 3.3 Using The Clipboard: The GenEditControl

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Managing the Edit Menu | Next: 3.4 Handling Cut and Copy

As stated above, most applications will simply let a GenEditControl object create and maintain their Edit menu. GenEditControlClass is a subclass of GenControlClass (see the Controllers chapter for usage of controllers in general).

The GenEditControl object can provide triggers and/or tools for Undo, Cut, Copy, Paste, Select All, and Delete operations. These operations must all be handled by your application, of course, just as if you did not use a GenEditControl; using this controller, however, simplifies your UI programming and allows the Edit tools to be used by the GenToolControl.

The features of the GenEditControl are listed below (they are flags of the GECFeatures record type):

GECF_UNDO
This feature adds an "Undo" trigger to the Edit menu. It sends MSG_META_UNDO to the application's target.
GECF_CUT
This feature adds a "Cut" trigger to the Edit menu. When the user activates this, it sends MSG_META_CLIPBOARD_CUT to the application's target.
GECF_COPY
This feature adds a "Copy" trigger to the Edit menu. When the user activates this, it sends MSG_META_CLIPBOARD_COPY to the application's target.
GECF_PASTE
This feature adds a "Paste" trigger to the Edit menu. When the user activates this, it sends MSG_META_CLIPBOARD_PASTE to the application's target.
GECF_SELECT_ALL
This feature adds a "Select All" trigger to the Edit menu. It sends MSG_META_SELECT_ALL to the applications' target.
GECF_DELETE
This feature adds a "Delete" trigger to the Edit menu. When the user activates this, it sends MSG_META_DELETE to the application's target.

The GenEditControl also provides an equivalent set of tools. Each tool executes the exact same functions as the analogous feature; see GenEditControl Features and Tools for the listing of the features and tools as well as the standard settings.

The GenEditControl handles two different notification types: GWNT_SELECT_STATE_CHANGE, sent when the selection state changes, and GWNT_UNDO_STATE_CHANGE, sent when a state change in the Undo status occurs. In both cases, the GenEditControl will appropriately update the Cut, Copy, Paste, Delete, and Undo triggers (the Select All trigger will always be enabled).

Code Display 7-6 GenEditControl Features and Tools

/* This display shows the features and tools records of GenEditControlClass, as
 * well as the default settings and instance data. */
    /* GenEditControlClass features */
typedef WordFlags GECFeatures;
#define GECF_UNDO				0x0020		/* MSG_META_UNDO */
#define GECF_CUT				0x0010		/* MSG_META_CLIPBOARD_CUT */
#define GECF_COPY				0x0008		/* MSG_META_CLIPBOARD_COPY */
#define GECF_PASTE				0x0004		/* MSG_META_CLIPBOARD_PASTE */
#define GECF_SELECT_ALL				0x0002		/* MSG_META_SELECT_ALL */
#define GECF_DELETE				0x0001		/* MSG_META_DELETE */
#define GEC_DEFAULT_FEATURES				(GECF_UNDO | GECF_CUT | GECF_COPY | \
                 GECF_PASTE | GECF_SELECT_ALL | GECF_DELETE)
    /* GenEditControlClass tools */
typedef WordFlags GECToolboxFeatures;
#define GECTF_UNDO				0x0020		/* MSG_META_UNDO */
#define GECTF_CUT				0x0010		/* MSG_META_CLIPBOARD_CUT */
#define GECTF_COPY				0x0008		/* MSG_META_CLIPBOARD_COPY */
#define GECTF_PASTE				0x0004		/* MSG_META_CLIPBOARD_PASTE */
#define GECTF_SELECT_ALL				0x0002		/* MSG_META_SELECT_ALL */
#define GECTF_DELETE				0x0001		/* MSG_META_DELETE */
#define GEC_DEFAULT_TOOLBOX_FEATURES					(GECTF_UNDO | GECTF_CUT | GECTF_COPY |
                     GECTF_PASTE | GECTF_SELECT_ALL |
                     GECTF_DELETE)
    /* GenEditControlClass Instance Data Settings */
    @default GCI_output = (TO_APP_TARGET);						/* Send output to the target */
    @default GI_states = (@default | GS_ENABLED);
    @default GI_attrs = (@default | GA_KBD_SEARCH_PATH);
| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Managing the Edit Menu | Next: 3.4 Handling Cut and Copy


Handling Cut and Copy

The Clipboard: 3.4 Using The Clipboard: Handling Cut and Copy

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 The GenEditControl | Next: 3.5 Handling Paste
MSG_META_CLIPBOARD_CUT, MSG_META_CLIPBOARD_COPY

Cut and Copy are very similar in function; both put data onto the Clipboard. However, Cut causes the data to subsequently be deleted from the document, and Copy does not.

When the user starts either of these operations, the object that handles them must go through a series of specific steps to load the data into the Clipboard's VM file. (For simplicity of example, this chapter will assume that the Process object will handle all Clipboard operations; this may not be the case in complex programs.)

The steps are simple; each is enumerated below, and edited examples from the sample application ClipSamp are provided in MSG_META_CLIPBOARD_CUT and MSG_META_CLIPBOARD_COPY . Note that these examples do not use the default text object handlers for copy and paste; they treat the entire text flow as the current selection.

  1. Duplicate and attach the data
    You must create a duplicate of whatever data is being loaded into the Clipboard. This step includes allocating new VM blocks in the Transfer VM File with VMAlloc() . As an alternative, you may pre-duplicate the item in memory with MemAlloc() and then simply attach them to the Transfer VM File with VMAttach() .
  2. Complete the transfer item's header
    Fill in all information fields in the transfer item's header block including formats, owner, and flags.
  3. Register the transfer item
    Once the data has been attached and the header completed, you must register the transfer with the Clipboard. The UI will then delete any old data in the Clipboard and replace it with your new transfer item. To register the transfer item, use ClipboardRegisterItem() .

Code Display 7-7 MSG_META_CLIPBOARD_CUT

/* This is the same as MSG_META_CLIPBOARD_COPY except that after copying the
 * data to the Clipboard, it deletes the data from the document.
 *
 * MSG_META_CLIPBOARD_CUT has no parameters and no return value. The strategy
 * is as follows: First, copy the subject data into the Clipboard with
 * MSG_META_CLIPBOARD_COPY. Then, delete the data (which, in this case, is a single
 * memory block containing all the subject text). */
@method ClipSampProcessClass, MSG_META_CLIPBOARD_CUT {
    /* Use MSG_META_CLIPBOARD_COPY to copy the data to the clipboard. */
    @call self::MSG_META_CLIPBOARD_COPY();
    /* Delete the data. The data is contained entirely within a single memory
     * block and is just text. The block is referenced by the memory handle
     * textHandle. If textHandle is not null, then the block may be freed. */
    if (textHandle) {					/* If textHandle is valid, */
    MemFree(textHandle);				/* free the memory block */
    textHandle = 0;				/* and zero the handle. */
    }
    /* Redraw the view area to reflect the deleted text. */
    ResetViewArea();			/* Custom routine to redraw the view. */
}

Code Display 7-8 MSG_META_CLIPBOARD_COPY

/* This message handler goes through all the steps necessary for a Copy operation
 * that works with text data only.
 * MSG_META_CLIPBOARD_COPY has no parameters and requires no return.
 *
 * The strategy employed by this handler is as follows:
 * First, allocate memory for and create the duplicate data block, filling in all
 * the appropriate fields.
 * Next, retrieve the Transfer VM File and attach the data block to the file.
 * Next, allocate and construct the transfer item header VM block.
 * Finally, register and lock in the transfer item to the Clipboard.
 *
 * A single global variable named textHandle refers to the block of text owned and
 * used by the sample application. All other data structures are defined within the
 * message handler. */
@method ClipSampProcessClass, MSG_META_CLIPBOARD_COPY {
    char			*textText;		/* temporary string for the text */
    int			textLength;		/* length of string including null */
    MemHandle			headerMemHandle;		/* handle of ClipboardItemHeader block */
    VMFileHandle 			transferVMFile;		/* VM file handle of Transfer VM File */
    VMBlockHandle 			dataVMBlock;		/* VM handle of attached data block */
    VMBlockHandle 			headerVMBlock;		/* VM handle of attached header block */
    ClipboardItemHeader 			*headerMem;		/* ClipboardItemHeader for the VM file */
    optr			textObj;		/* temporary text object for transfer */
    /* First, lock the text string into memory and get its length, adding one for
     * the null character at the end. Then unlock the text string's block. */
    textText = (char *) MemLock(textHandle);
    textLength = (strlen(textText) + 1);
    MemUnlock(textHandle);
    /* Next, build the transfer item block by creating a temporary text object and
     * copying our text into it. Other formats may simply copy the text directly
     * into a VM block. */
    textObj = TextAllocClipboardObject(ClipboardGetClipboardFile(), 0, 0);
    @call textObj::MSG_VIS_TEXT_REPLACE_ALL_PTR((char *)MemLock(textHandle), 0);
    MemUnlock(textHandle);
    dataVMBlock = TextFinishedWithClipboardObject(
                    textObj, TCO_RETURN_TRANSFER_FORMAT);
    /* Now get the transfer VM file. */
    transferVMFile = ClipboardGetClipboardFile();
    /* Now, allocate and fill in the transfer item header block. */
    headerVMBlock = VMAlloc(				transferVMFile,
                sizeof(ClipboardItemHeader),
                MY_TRANSFER_ID);
    headerMem = (ClipboardItemHeader *)VMLock(transferVMFile, headerVMBlock,
                            &headerMemHandle);
    headerMem->CIH_owner = (optr) (((dword)GeodeGetProcessHandle()<<16) | 0);
    headerMem->CIH_flags = 0;					/* Normal transfer; no flags. */
    (void) strcpy(headerMem->CIH_name, "Sample Text");
    headerMem->CIH_formatCount = 1;
    headerMem->CIH_sourceID = 0;
    headerMem->CIH_formats[0].CIFI_format =
        FormatIDFromManufacturerAndType(MANUFACTURER_ID_ME, CIF_TEXT);
    headerMem->CIH_formats[0].CIFI_vmChain =
                VMCHAIN_MAKE_FROM_VM_BLOCK(dataVMBlock);
    headerMem->CIH_formats[0].CIFI_extra1 = 0;
    headerMem->CIH_formats[0].CIFI_extra2 = 0;
    VMUnlock(headerMemHandle);
    /* Now register the transfer item with the Clipboard. This will actually put
     * the transfer item and its header into the Clipboard. */
    ClipboardRegisterItem(BlockIDFromFileAndBlock(
                    transferVMFile, headerVMBlock),
            0);
}
| Up: GEOS SDK TechDocs | Up | Prev: 3.3 The GenEditControl | Next: 3.5 Handling Paste


Handling Paste

The Clipboard: 3.5 Using The Clipboard: Handling Paste

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Handling Cut and Copy | Next: 3.6 Unregistering with the Clipboard
ClipboardRequestItemFormat(), MSG_META_CLIPBOARD_PASTE

The Paste operation pulls data off the Clipboard and places it at the insertion point in the application's data. The Clipboard remains unchanged throughout the operation; the data is simply duplicated and passed on to the application.

The steps in handling a MSG_META_CLIPBOARD_PASTE are simple; each is enumerated below, and a sample method for pasting is shown in MSG_META_CLIPBOARD_PASTE .

  1. Query the Clipboard
    First, you must make sure that you have exclusive access to the clipboard item. To this end call ClipboardQueryItem() . You should also call ClipboardRequestItemFormat() to make sure that the present clipboard item is pasteable.
  2. Allocate memory if necessary
    If necessary, allocate the memory into which the transfer item will be duplicated. You can not simply reference handles to the transfer item in the clipboard because the transfer item may be changed by another thread at any time.
  3. Lock the Transfer VM File and grab the transfer item
    Lock the Transfer VM File with a call to ClipboardRequestItemFormat() . Finally, copy the transfer item into your pre-allocated memory.
  4. Unlock the Transfer VM File
    By calling ClipboardDoneWithItem() , relinquish your exclusive access to the Transfer VM File and to the clipboard item itself. The Paste operation can then be completed entirely by your application by assimilating the pasted data and displaying it properly.

Code Display 7-9 MSG_META_CLIPBOARD_PASTE

/* This message handler goes through the necessary steps to grab the transfer item
 * from the Clipboard and copy it into application's memory. This example uses a
 * single global variable called textHandle, a memory handle of the only data block
 * owned by the application. The memory block contains text.
 * MSG_META_CLIPBOARD_PASTE has no parameters and requires no return value. */
@method ClipSampProcessClass, MSG_META_CLIPBOARD_PASTE {
    ClipboardQueryArgs				query;		/* returned by
                         * ClipboardQueryItem() */
    ClipboardRequestArgs 				request;		/* returned by
                         * ClipboardRequestItemFormat() */
    TextTransferBlockHeader				*dataBlock		/* pointer to block header */
    MemHandle				dataBlockMemHandle; /* handle of locked block */
    word				charsAvail;		/* number of chars in block */
    int				textLength;		/* length of text */
    word				transferFlags = 0; /* flags for the transfer
                         * (normal transfer) */
        /* Call ClipboardQueryItem() to be sure that a normal
         * transfer item exists in the Clipboard. */
    ClipboardQueryItem(transferFlags, &query);
                    /* Fills ClipboardQueryArgs structure */
        /* If a transfer item exists, check for a CIF_TEXT
         * version, the only format we support. */
    if (query.CQA_numFormats) {				/* if more than zero formats available */
       if (ClipboardTestItemFormat(query.CQA_header,
                    FormatIDFromManufacturerAndType(
                            MANUFACTURER_ID_ME,
                            CIF_TEXT))) {
    /* A CIF_TEXT version exists. Now we grab that transfer item by calling
     * ClipboardRequestItemFormat(). This routine fills ClipboardRequestArgs,
     * which contains information about the Transfer VM File and the
     * ClipboardItemHeader block in that file. */
        ClipboardRequestItemFormat(FormatIDFromManufacturerAndType(
                    MANUFACTURER_ID_ME, CIF_TEXT),
                    query.CQA_header, &request);
    /* Now we have the VM file handle of the Transfer VM File and the VM block
     * handle of the ClipboardItemHeader structure. From this we can get the
     * data in the data block. To speed things up, we will copy the transfer
     * text directly into our already-allocated memory block; the handle to our
     * memory block is in textHandle. */
    dataBlock = (TextTransferBlockHeader *)VMLock(
                request.CRA_file,
                VMCHAIN_GET_VM_BLOCK(request.CRA_data),
                &dataBlockMemHandle);
    textHugeArray = VMCHAIN_GET_VM_BLOCK(dataBlock->TTBH_text);
    VMUnlock(dataBlockMemHandle);
    /* Since the data is CIF_TEXT, we know the data block is in the format of
     * TextTransferBlockHeader. We get the text by cycling through the
     * format's HugeArray. This code has been taken out of this example for
     * simplicity; you can look at the ClipSamp sample application source
     * code for it. */
    }
    /* After copying the text into our block, we signal we're done with the
     * transfer file. We do this by calling ClipboardDoneWithItem(). After
     * that, we update our view and return. */
    ClipboardDoneWithItem(query.CQA_header);
    ResetViewArea();				/* Routine defined in ClipSamp. */
}
| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Handling Cut and Copy | Next: 3.6 Unregistering with the Clipboard


Unregistering with the Clipboard

The Clipboard: 3.6 Using The Clipboard: Unregistering with the Clipboard

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Handling Paste | Next: 3.7 Implementing Undo
ClipboardRemoveFromNotificationList()

Because the Clipboard sends notification out to all registered geodes, geodes must "unregister" when they are shutting down. Otherwise, the Clipboard will attempt to send a message to a defunct object, and this can cause problems for the operating system. Therefore, in your MSG_GEN_PROCESS_CLOSE_APPLICATION handler you should make a call to the routine ClipboardRemoveFromNotificationList() , which removes the passed object from the notification list.

| Up: GEOS SDK TechDocs | Up | Prev: 3.5 Handling Paste | Next: 3.7 Implementing Undo


Implementing Undo

The Clipboard: 3.7 Using The Clipboard: Implementing Undo

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Unregistering with the Clipboard | Next: 3.8 Transfer File Information

For the most part, implementation of Undo is left up to the application. This is due to the fact that operations that may be undone are typically very application-specific. The text objects and the Ink object are the only exceptions to this; they provide their own Undo functions in response to MSG_META_UNDO . For more information on Undo and how it works in GEOS, see the MetaClass chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 3.6 Unregistering with the Clipboard | Next: 3.8 Transfer File Information


Transfer File Information

The Clipboard: 3.8 Using The Clipboard: Transfer File Information

| Up: GEOS SDK TechDocs | Up | Prev: 3.7 Implementing Undo | Next: 3.9 Undoing a Clipboard Change
ClipboardTestItemFormat(), ClipboardEnumItemFormats(), ClipboardGetItemInfo(), ClipboardGetNormalItemInfo(), ClipboardGetUndoItemInfo(), ClipboardGetClipboardFile()

With the following routines, you can get information about any of the transfer files in use.

ClipboardTestItemFormat()
Given a transfer format, test if the selected transfer item supports that format. Before using this routine, you must first call ClipboardQueryItem() to get the transfer item header.
ClipboardEnumItemFormats()
Return a list of supported transfer formats for the selected transfer item. Before using this routine, you must first call ClipboardQueryItem() to get the transfer item header.
ClipboardGetItemInfo()
Return the source identifier for the transfer item. Before using this routine, you must first call ClipboardQueryItem() to get the transfer item header.
ClipboardGetNormalItemInfo()
Return the VM file handle and VM block handle of the transfer item header for the "Normal" transfer item.
ClipboardGetUndoItemInfo()
Return the VM file handle and VM block handle of the transfer item header for the "Undo" transfer item.
ClipboardGetClipboardFile()
Return the VM file handle of the UI transfer file (the one typically used when copying, cutting, and pasting).
| Up: GEOS SDK TechDocs | Up | Prev: 3.7 Implementing Undo | Next: 3.9 Undoing a Clipboard Change


Undoing a Clipboard Change

The Clipboard: 3.9 Using The Clipboard: Undoing a Clipboard Change

| Up: GEOS SDK TechDocs | Up | Prev: 3.8 Transfer File Information | Next: 4 Using Quick-Transfer
ClipboardUnregisterItem()

Using ClipboardUnregisterItem() , you can revert one level of clipboard changes. Note that this works for only one level; there is no way to back out more than one change to the clipboard. This routine can not "undo" itself; that is, calling this routine twice in a row will leave the clipboard in a state other than the original state.

| Up: GEOS SDK TechDocs | Up | Prev: 3.8 Transfer File Information | Next: 4 Using Quick-Transfer


Using Quick-Transfer

The Clipboard: 4 Using Quick-Transfer

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Using The Clipboard | Next: 5 Shutdown Issues

An application must understand the Clipboard and its structure before being able to support the quick-transfer feature of the UI. However, because quick-transfer is such a convenient feature for users, every appropriate application should support it. To use the quick-transfer mechanism, you will probably want to understand how mouse input is handled.

The quick-transfer mechanism makes extensive use of the Clipboard's data structures. However, this does not mean that when a quick-transfer is initiated, the Clipboard is altered. Instead, the Clipboard maintains a separate (but similar) data structure within the Transfer VM File.

When a transfer is in progress, the distinction between a normal transfer and a quick transfer is made with the flag CIF_QUICK. When passed to the transfer mechanism's routines, this flag indicates that the quick-transfer item should be accessed and the Clipboard data should remain intact.

Supporting Quick-Transfer

Quick-Transfer Procedure

Quick-Transfer Data Structures

Source Object Responsibility

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Using The Clipboard | Next: 5 Shutdown Issues


Supporting Quick-Transfer

The Clipboard: 4.1 Using Quick-Transfer: Supporting Quick-Transfer

| Up: GEOS SDK TechDocs | Up | Prev: 4 Using Quick-Transfer | Next: 4.2 Quick-Transfer Procedure

In order for an application to support the quick-transfer mechanism, it must be able to handle several situations. The list below enumerates all the tasks the application must be ready to perform:

  • Recognize initiation of a move/copy operation
    When the user initiates a quick-transfer, the object under the mouse pointer will be notified the operation has begun.
  • Provide feedback
    Once a quick-transfer is initiated, every object on the screen becomes a potential destination for the transfer. When the mouse pointer moves over an object, that object must indicate what type of operation can be supported, if any.
  • Receive the transfer item
    If the move/copy operation is completed by the user when the mouse pointer is over an object, that object must be able to receive the item and provide feedback to the UI about the status of the quick-transfer.
  • Recognize completion of the operation
    When a quick-transfer is completed, the source object is notified and informed what type of operation took place.
  • Shut off quick-transfer feedback on shutdown
    When an object that can handle quick-transfers is shut down or destroyed, it must remove itself from the quick-transfer notification list. This takes care of any potential synchronization problems between object shutdown and the quick-transfer mechanism.

Applications should also understand the three rules that govern the behavior of a quick-transfer:

  • Transfers within a single document default to move operations.
  • Transfers across documents default to copy operations.
  • Transfers in a format not supported by the destination are "no operation" transfers.
| Up: GEOS SDK TechDocs | Up | Prev: 4 Using Quick-Transfer | Next: 4.2 Quick-Transfer Procedure


Quick-Transfer Procedure

The Clipboard: 4.2 Using Quick-Transfer: Quick-Transfer Procedure

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Supporting Quick-Transfer | Next: 4.3 Quick-Transfer Data Structures

Although applications must handle several situations to support the quick-transfer mechanism, the procedure involved in a quick-transfer is quite simple. The steps of how a quick-transfer operation is performed are outlined below:

  1. The user initiates a quick-transfer
    By pressing the right mouse button (in OSF/Motif), the user initiates a quick-transfer. The UI recognizes the mouse press and sends MSG_META_START_MOVE_COPY to the object under the pointer image.
  2. The source object builds the transfer item
    The object under the pointer image then becomes the "source" of the quick-transfer. It first calls ClipboardStartQuickTransfer() to initiate the quick-transfer mechanism. It then builds the transfer item just as it would if the user had clicked on the Copy trigger in the Edit menu. It then logs the transfer item with the quick-transfer mechanism.
  3. The source becomes a potential destination
    Immediately after logging the transfer item, the source object becomes a potential destination of the quick-transfer. It must immediately provide feedback to the UI indicating whether it can accept the transfer item and whether the operation would be a move or a copy. The feedback is provided by calling ClipboardSetQuickTransferFeedback() . If the source object is a visible object in a GenView, it must also send the message MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER to itself to allow the pointer events to be sent to other objects in other windows (because the GenView grabs the mouse on the press).
  4. The user moves the mouse
    When the user moves the mouse (continuing to hold down the move/copy button), the pointer image may cross over several objects. Each of these is a potential destination and as such must provide feedback similar to that described in (3) above until the pointer moves outside of its bounds. Each object that receives a MSG_META_PTR should check if a quick transfer is in progress by either checking the passed event flags or by calling ClipboardGetQuickTransferStatus() . The object should, in response, provide feedback as to whether it can accept the transfer item or not. It calls ClipboardSetQuickTransferFeedback() with the proper feedback signal.
  5. The user finishes the transfer
    When the user lets up the move/copy button, the object under the pointer image (if any) becomes the destination object (it will receive a MSG_META_END_MOVE_COPY from the UI).
  6. The destination receives the transfer item
    If the transfer item is in a receivable format, the destination will retrieve the item from the Transfer VM File just as if the user had selected the Paste trigger from the Edit menu (except the quick-transfer transfer item is received, not the Clipboard transfer item). The object first checks if it can take the item by calling ClipboardGetQuickItemInfo() on the transfer item. If it can handle the item, it calls ClipboardQueryItem() , grabs the transfer item, and finally calls ClipboardEndQuickTransfer() .
  7. The UI informs the source of the outcome
    After the transfer has been completed by the destination, the UI will send a MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED to the source object, informing it about the final outcome of the operation. Some source objects will change shape, shading, or color during a quick-transfer and must know when the transfer is concluded. If the operation is a quick-move, the source must delete the information or object that was moved.
| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Supporting Quick-Transfer | Next: 4.3 Quick-Transfer Data Structures


Quick-Transfer Data Structures

The Clipboard: 4.3 Using Quick-Transfer: Quick-Transfer Data Structures

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Quick-Transfer Procedure | Next: 4.4 Source Object Responsibility

The quick-transfer mechanism uses the same structures as the Clipboard. However, there are special data structures that are used exclusively by the quick-transfer mechanism. These data structures are used by individual UI routines and are documented with those routines.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Quick-Transfer Procedure | Next: 4.4 Source Object Responsibility


Source Object Responsibility

The Clipboard: 4.4 Using Quick-Transfer: Source Object Responsibility

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Quick-Transfer Data Structures | Next: 5 Shutdown Issues
MSG_META_START_MOVE_COPY, ClipboardStartQuickTransfer(),
MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK

When the user presses the move/copy button, the UI sends a MSG_META_START_MOVE_COPY to the object under the mouse pointer. The selected object can be either a gadget run in the UI thread (such as a GenText object) or a process-run visible object within a view.

If the object is a process-run visible object in a view, both the application object and the object under the mouse pointer will receive the notification message. If the object is UI-run, only it will receive the message.

Receipt of this message tells an object to begin a quick-transfer operation. This operation consists of several steps:

  1. Grab the mouse to track it so you can find out when it leaves your object's bounds. (When it leaves, your object is no longer the destination.) Note that only objects need to grab the mouse; if a process is the content of the view, it does not have to.
  2. Provide feedback to the quick-transfer mechanism immediately.
  3. Build and register the transfer item.
  4. Continue providing feedback as long as the mouse is in your bounds.
  5. If desired, provide visual feedback to the user as the type of operation changes when the user moves the mouse pointer.

The reason for providing feedback to the quick-transfer mechanism is simple: to indicate to the user what is going on. By giving information to the quick-transfer mechanism, objects allow the user to be informed immediately what type of operation is in progress--a move, a copy, or nothing at all.

Immediately after your object has grabbed the mouse, it should call the routine ClipboardStartQuickTransfer() . This routine allows the object not only to indicate which type of operation is in progress but also to attach a special graphical region to the cursor (though not required). This allows the application to provide additional information to the user as to what is going on (e.g. GeoManager attaches an image when a file transfer is initiated).

You must also indicate to ClipboardStartQuickTransfer() the object that will receive notification when the transfer has concluded. This is important because when a quick-move has been completed, the source object must ensure that the original copy of the item (usually the source object itself) is deleted.

After calling ClipboardStartQuickTransfer() , the source object should duplicate and register the transfer item with the Transfer VM File. To do this, register the item as you normally would for a cut or copy operation (see Registering with the Clipboard ); however, be sure to use the flag CIF_QUICK to ensure that the normal Clipboard data remains unaffected.

Once the transfer item has been registered, the source object becomes a potential destination and should act as such. However, you may wish to continue to provide source-related visual feedback to the user as long as the quick-transfer is going on: During a quick-transfer, the source will receive MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK each time the mode of the quick-transfer changes. This message will tell the source object what the current mode of transfer is in order for the source to give extra visual feedback to the user. This behavior is not required of the source object but can be beneficial to your application. It is also supplemental to the destination-related feedback that must be provided.

Responsibilities of a Potential Destination

ClipboardGetQuickTransferStatus(), ClipboardSetQuickTransferFeedback(), MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER, MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER

All objects that can potentially receive a transfer item are considered potential destinations during a quick-transfer operation. During the operation, the user will likely move the mouse pointer across the screen, entering and leaving several different potential destinations.

When the mouse first moves over the object, the object will receive a MSG_META_PTR . When your object receives this message, it must provide immediate feedback to the transfer mechanism to indicate whether a move, a copy, or no operation is to be performed (the object should provide this feedback with the understanding that the operation type is what would happen if the transfer were to conclude at that moment).

When the first MSG_META_PTR is received, the object should call the routine ClipboardGetQuickTransferStatus() . This routine returns whether a quick-transfer is in progress; if so, the object should acquire a mouse grab in order to provide feedback until the mouse pointer leaves its bounds.

The object should then check the quick-transfer Clipboard for supported formats. This is done just as with the Clipboard--with the routine ClipboardQueryItem() . If no supported formats are available, the object should provide the "no operation" feedback. However, if one or more is available, the object should determine whether the operation is a move or copy (call ClipboardGetItemInfo() ) and act accordingly.

To provide feedback, the object must call ClipboardSetQuickTransferFeedback() in its method for MSG_META_PTR . This routine sets the mode of the transfer to one of the enumerated type ClipboardQuickTransferFeedback . If the format is not supported, ClipboardSetQuickTransferFeedback() is passed CQTF_CLEAR.

When the mouse has left an object's bounds, the object must relinquish its mouse grab. Either a MSG_META_CONTENT_LOST_GADGET_EXCLUSIVE or a MSG_META_PTR with the UIFA_IN flag cleared will indicate this situation to the object. The former occurs when the mouse has moved onto a window or other object that is obscuring your object, and the latter is a result of the mouse moving outside of your bounds altogether.

At this point, the object must do two things: Reset the mouse cursor and re-transmit the last mouse pointer event.

To reset the mouse pointer, call ClipboardSetQuickTransferFeedback() and pass it CQTF_CLEAR. This will set the default cursor. To re-send the last pointer event received (you must do this because the last one occurred outside your object's bounds and might have been within another object's bounds), you simply have to return the flag MRF_REPLAY when releasing the mouse grab.

If the source object wishes a quick transfer to be able to be carried outside its view, it must send MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER to itself. Process objects acting as a content must send MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER to the GenView.

The object then becomes oblivious to future quick-transfer events until the pointer returns to its window (or unless it was registered for notification of quick-transfer conclusion).

Responsibilities of the Destination Object

MSG_META_END_MOVE_COPY, ClipboardEndQuickTransfer()

If, when the mouse pointer is within your object's bounds, the user releases the Move/Copy button, your object becomes the true destination of the transfer. You will be notified by a MSG_META_END_MOVE_COPY .

Upon receipt of this message, the object should first determine the move/copy/no-operation behavior as above, with one exception: If either of the flags CQNF_MOVE or CQNF_COPY is set for the transfer item, then the user has overridden the normal behavior and the destination object should respond with the appropriate operation.

After determining the proper action, the object should retrieve the transfer item (as it would from the Clipboard), passing one of CQNF_MOVE, CQNF_COPY, or CQNF_NO_OPERATION. This flag will cause the proper notification to be sent to the transfer's source and allow it to complete its actions properly. To finish the transfer, the object should call ClipboardEndQuickTransfer() .

Getting More Information

In addition to the routines above, you can use one other to retrieve information about a quick-transfer item. ClipboardGetQuickItemInfo() returns a set of handles for the transfer VM file and the file's header block.

When the Transfer Is Concluded

MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED

After the transfer has concluded, the original source of the transfer will receive MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED if it requested notification when it registered the original transfer. This message will be accompanied by a ClipboardQuickNotifyFlags record indicating what type of operation the transfer ended up being. The source object should then follow the rules of quick transfer and act appropriately (e.g. delete the source object on a quick-move operation).

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Quick-Transfer Data Structures | Next: 5 Shutdown Issues


Shutdown Issues

The Clipboard: 5 Shutdown Issues

| Up: GEOS SDK TechDocs | Up | Prev: 4 Using Quick-Transfer
ClipboardClearQuickTransferNotification(), ClipboardAbortQuickTransfer(), ClipboardRemoveFromNotificationList()

It is possible for a quick-transfer source object to shut down (be destroyed) before the completion of a quick-transfer operation. If the object registered to be notified of transfer completion, it must un-register as it is shutting down. This is done with ClipboardClearQuickTransferNotification() or ClipboardAbortQuickTransfer() .

Additionally, any application that registers with the Clipboard must un-register when it is shutting down. This is done with the routine ClipboardRemoveFromNotificationList() .

| Up: GEOS SDK TechDocs | Up | Prev: 4 Using Quick-Transfer


Generic UI Controllers

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Controller Features and Functions
| One-page Version of this Chapter for Easy Printing

Many applications will create their menus and dialog boxes from scratch, using various GenInteraction and GenTrigger objects. Several system components, however, use a standard base format across all applications. For these components, GEOS provides a number of controller objects.

A controller object is one that provides UI gadgetry to control a certain set of features. For example, the GenViewControl object provides all the UI--including menus, dialogs, and tool bars, if appropriate--to alter a GenView's scaling and other features. Many libraries such as the text library provide controller objects that make the use of the library almost trivial.

To make inclusion of these controllers even easier, the UI offers a GenToolControl; this object provides all the functionality and UI gadgetry to allow the user to configure his own toolbars and menus from the tools provided by a controller.

Finally, developers writing libraries or suites of applications may wish to create their own controller classes. This is neither difficult nor complex, though it will be done primarily by programmers writing libraries for use by many applications.

This chapter describes what controllers are and how they work. It also discusses which controllers are available in the system, as well as the GenToolControl object and how it's used. Finally, it details how to create your own controller classes. In order to fully understand this chapter, you should be familiar first with the generic UI, menus and dialog boxes, and the General Change Notification mechanism.

1 Controller Features and Functions
1.1 Controller Features
1.2 How Controllers Work
1.3 Using Controllers
2 Standard Controllers
3 Using Controllers
3.1 Using a Basic GenControl Object
3.2 Using Tools
4 Creating Your Own Controllers
4.1 GenControlClass Instance Data
4.2 Subclassing GenControlClass
4.3 Advanced GenControlClass Usage
5 GenToolControlClass
6 GenToolGroupClass
7 Other Controllers
7.1 ColorSelectorClass
7.2 GenPageControlClass
7.3 The Float Format Controller

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Controller Features and Functions
| One-page Version of this Chapter for Easy Printing


Controller Features and Functions

Generic UI Controllers: 1 Controller Features and Functions

| Up: GEOS SDK TechDocs | Up | Down | Prev: Generic UI Controllers | Next: 2 Standard Controllers

Controller objects let you include a library's features in your applications with limited work. The controller provides the basic UI gadgetry and feature management functions so you don't have to.

Every library has a set of features that will be used by all or most of the library's users. For example, applications which use the text objects (GenText and VisText) are likely to allow the user to alter justification, font, and text style. Rather than forcing every application to create its own menus and dialogs and lists for managing these features, the text library exports a number of controllers that can be included in the application's UI; these controllers provide and manage the menu items and lists for the user. They also interact directly with the text objects to change the attributes affected.

Controller Features

How Controllers Work

Using Controllers

| Up: GEOS SDK TechDocs | Up | Down | Prev: Generic UI Controllers | Next: 2 Standard Controllers


Controller Features

Generic UI Controllers: 1.1 Controller Features and Functions: Controller Features

| Up: GEOS SDK TechDocs | Up | Prev: 1 Controller Features and Functions | Next: 1.2 How Controllers Work

Controller objects provide the following features and benefits to application and library programmers:

  • Ease of use
    Controllers are intended to be "plug-and-play" objects. Programmers simply include the controller to get all the appropriate UI gadgetry.
  • Consistency of appearance
    All applications that use a particular controller will appear consistent to the user. The controller knows the appropriate structure of its menus and dialogs, and all such controllers will appear the same to the user, increasing the application's ease of use.
  • Automatic tool management
    By using controllers along with a GenToolControl object, an application can allow the user to configure his own tool set. Controllers can have their UI placed along a display's edge, in the normal menus, or in a floating tool box; the GenToolControl allows users to decide where the tools will appear without affecting the application at all.
  • Extendability of features
    Any application that uses a controller can automatically include future features of a library. For example, if the GenView object were upgraded in the future to allow new features, the GenViewControl object would likely also be updated. Any application including the GenViewControl would automatically get the new UI and features without recompilation. Applications that do not use controllers will not automatically gain the benefits of upgraded libraries.

The GenControl is most useful for simple controllers. It is based on core UI objects (GenTrigger, GenInteraction, etc.) and is subclassed from GenInteractionClass . Applications that will have extremely complex controller-type functions may want to write their controllers from scratch--this is discussed in Creating Your Own Controllers .

| Up: GEOS SDK TechDocs | Up | Prev: 1 Controller Features and Functions | Next: 1.2 How Controllers Work


How Controllers Work

Generic UI Controllers: 1.2 Controller Features and Functions: How Controllers Work

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Controller Features | Next: 1.3 Using Controllers

When you use a controller object, you are really using three different types of objects: The controller itself, the UI gadgetry it creates and manages, and the controlled object (sometimes called a "data item") it acts on. For the most part, you will not have to understand these relationships unless you are creating your own controllers with GenControlClass .

To explain how a controller works with both of these other objects, this section uses a simple application that uses a single controller. The controller used is a PointSizeControl object, exported by the text library and used with the GenText and VisText objects; in this example, it interacts with two GenText objects. The entire source code of the application's psctext.goc file is given in A Sample Controller Application (psctext.goc) . This code display does not show the psctext.gp file because it is similar to Hello World's parameters file.

A screen dump of the application is shown below and the entire object tree declared in the application is shown above. The controller object (PSCTSizeControl) declares and sets up the entire menu structure of the Sizes menu, so the menu items do not need to be declared in the application's .goc file.

The interaction between the controller, the text objects, and the controller's UI objects is simple. When the controller is first loaded in from the application's Interface resource block, it also loads in the associated UI gadgetry for the Sizes menu. The menu items are added as children of the controller without the programmer having to do anything extra.

The data item objects (in this example the GenText objects), when loaded or created, create and set up special General Change Notification (GCN) lists. In the PSCText sample application, the text library sets up a GCN list for the notification type GAGCNLT_APP_TARGET_NOTIFY_CHAR_ATTR_CHANGE. When changes to the text object occur, the text object will send a message to this GCN list.

The controller object knows inherently which GCN lists it should be added to. When it is loaded, it adds itself to these lists. In the sample application, the PSCTSizeControl object will add itself automatically to the GCN list for GAGCNLT_APP_TARGET_NOTIFY_CHAR_ATTR_CHANGE.

The interactions between the three components are summarized in the figure below. The application programmer needs to know very little about the interactions of these components; in fact, unless you are creating your own controller objects, you can get away with knowing only a very few things. Building your own controller is detailed in Creating Your Own Controllers , and the basics of controller use, along with the source code for psctext.goc , are shown in Using Controllers .

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Controller Features | Next: 1.3 Using Controllers


Using Controllers

Generic UI Controllers: 1.3 Controller Features and Functions: Using Controllers

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 How Controllers Work | Next: 2 Standard Controllers

As stated above, for an application to use a controller (or several controllers), the application programmer needs to know very little about the controller itself. A Sample Controller Application (psctext.goc) shows the entire code for the PSCText sample application; note that the programmer has to add no code (just object declarations) to create two editable text objects which can have selectable and settable point sizes.

The application programmer must do two essential things to use a controller object: First, you must include and set up the controller properly. In the example application, the PSCTSizeControl object is a menu; it could easily have been set up as GIV_DIALOG (typically, though, this particular controller is implemented as a menu). Because GenControlClass is a subclass of GenInteractionClass , you can set up the controller exactly like any other GenInteraction.

Second, you must set up your data objects to interact with the controller. The data objects should be written for this automatically; for example, the GenText objects automatically send the proper notification to the GCN mechanism to ensure that all appropriate controllers are notified of all changes. Note, however, that the GenText objects have to be set targetable before the controller can operate on them. Some data objects will be set up to work with their controllers automatically; others may need to have certain attributes set. The GenText objects, for example, are not by default targetable; the controller, however, sends its messages to the application's target object. If the GenTexts are not targetable, they will never gain the application's target and therefore will never react to the controller's requests.

To use a particular controller, you should learn about how that controller's data object needs to be set up. To help you with this, Standard Controllers lists the various controllers included in the system and where they are documented.

Code Display 12-1 A S ample Controller Application (psctext.goc)

@include <stdapp.goh>
@include <ui.goh>
@include <Objects/Text/tCtrlC.goh>
/* The PSCText application's process class runs the application's primary
 * thread. For a description of GenProcessClass, see Hello World. */
@class PSCTextProcessClass, GenProcessClass;
@endc
@classdecl PSCTextProcessClass;
@start AppResource;
    /* The PSCTextApp object defines the application object for the
     * application. For full information, see Hello World.
     * The controller is also placed on the application's self-load-options
     * GCN list to ensure that it loads its options properly when returning
     * from saved state. This is shown below in the second of the two
     * GCN list declarations. */
@object GenApplicationClass PSCTextApp = {
    GI_visMoniker = "Point Size Control Sample Application";
    GI_comp = @PSCTPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS,GAGCNLT_WINDOWS) = @2PSCTPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS,GAGCNLT_SELF_LOAD_OPTIONS) = @PSCTSizeControl;
}
@end AppResource
@start Interface;
/* The PSCTPrimary object serves as the primary window of this sample application.
 * Its children are the Point Size Control object and the two GenText objects. */
@object GenPrimaryClass PSCTPrimary = {
    GI_comp = @PSCTSizeControl, @PSCTopTextObj, @PSCBotTextObj;
    HINT_SIZE_WINDOW_AS_DESIRED;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
/* The PSCTSizeControl object is the controller that provides all the point size
 * functionality. It will automatically create a menu called "Sizes" (due to the
 * GI_visMoniker) and all the entries of that menu.
 * This controller will work on whichever of the two GenText objects
 * (PSCGTopTextObj and PSCBotTextObj) is set the target; the controller's UI
 * objects (the Sizes menu) send their messages directly to the target via the
 * TravelOption TO_TARGET. If a point size is selected as the user's first action,
 * it will work on PSCTopTextObj because that is set up as the default target. */
@object PointSizeControlClass PSCTSizeControl = {
    GI_visMoniker = 'z', "Sizes";					/* Give the controller a name */
    GII_visibility = GIV_POPUP;					/* Make the controller a menu */
}
/* These two GenText objects are simple; they use only the defaults plus the
 * items shown here. Both must be set targetable (GA_TARGETABLE) to be included
 * in the target hierarchy; this is necessary when using controllers because of
 * the note in the above comment. The PSCTopTextObj is made the default focus
 * (to get keyboard input) and the default target (for controller operation). */
@object GenTextClass PSCTopTextObj = {
    GI_attrs = @default | GA_TARGETABLE;
    /* Initially, this text object uses the VisTextDefaultSize VTDS_12
     * (12 pts) and the VisTextDefaultFont VTDF_URW_ROMAN. You can use
     * the PointSizeControl object to change this point size. */
    ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR =					((VTDS_12 << VTDCA_SIZE_OFFSET) |
                     VTDF_URW_ROMAN);
    /* Set the font mapping to none to turn off the defaults. */
    HINT_DEFAULT_FOCUS;
    HINT_DEFAULT_TARGET;
}
@object GenTextClass PSCBotTextObj = {
    GI_attrs = @default | GA_TARGETABLE;
    ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR =					((VTDS_12 << VTDCA_SIZE_OFFSET) |
                     VTDF_URW_ROMAN);
}
@end Interface
| Up: GEOS SDK TechDocs | Up | Prev: 1.2 How Controllers Work | Next: 2 Standard Controllers


Standard Controllers

Generic UI Controllers: 2 Standard Controllers

| Up: GEOS SDK TechDocs | Up | Prev: 1 Controller Features and Functions | Next: 3 Using Controllers

This section lists the various controller classes exported by system objects and libraries. Many are part of the GEOS UI; others are part of particular libraries. Some are system-wide (such as the ColorSelectorClass); others are object-specific (such as the InkControllerClass).

All of these controllers are set up to work with GenToolControlClass and GenToolGroupClass . When a GenToolControl object is also included in an application, users may set up menus and floating toolboxes however they like with the various tools from the controller's lists. For more information on the GenToolControl and how it is used, see Using Tools and GenToolControlClass .

  • GenToolControlClass
     This controller class provides the UI for the user to select where other controller tools appear. Controller tools must be specified by the individual controller classes; GenToolControlClass simply manages them according to the user's specifications. Individual tools may be placed in a floating tool box, in an application's tool bar, alongside the active display, alongside the primary display, or other places. GenToolControlClass is described in full in Using Controllers .
  • GenEditControlClass
     This controller class provides the Edit menu with the Undo, Cut, Copy, Paste, Select All, and Delete triggers. This controller will cooperate fully with most system objects such as the text, graphic object, spreadsheet, and Ink objects. The GenEditControl sends out MSG_META_CUT , MSG_META_COPY , etc., to these objects. GenEditControlClass is described in the Clipboard chapter.
  • GenViewControlClass
     This controller class provides a View menu with the functions for changing a GenView's scale, paging, scrolling, aspect ratio, and other features. It is described in full in the GenView chapter.
  • GenDisplayControlClass
     This controller class provides the UI for a multiple display interface. It works with the GenDisplayGroup and GenDisplay objects and is described in full in the GenDisplay chapter.
  • GenDocumentControlClass
     This controller class provides all the functions of the File menu including New, Open, Use Template, Close, Save, Save As, and Revert. It also interacts with the GenDocument and GenDocumentGroup objects to manage how the user interacts with documents in general. GenDocumentControlClass is described in full in the Documents chapter.
  • GenPageControlClass
     This controller class provides the UI for a user to go to a previous, next or specified page. It is described in full in GenPageControlClass .
  • PrintControlClass , PageSizeControlClass
     Both of these controller classes are exported by the spool object library. Both are described in full in the Printing chapter.
  • ImportControlClass , ExportControlClass
     These controller classes provide the UI and functions necessary for the user to select files for importing and exporting. They are exported by the Impex library are described in full in the Impex chapter.
  • InkControlClass
     This controller class provides the tool interface for working with Ink-related tools. It is exported by the pen library and is described in full in the Pen Object library chapter.
  • ColorSelectorClass
     This controller class provides the UI for a user to set a color using either the palette index or a set of RGB values, to set a draw mask, and to set an area fill pattern. ColorSelectorClass is described in ColorSelectorClass .
  • StyleSheetControlClass
     This controller class provides the UI and functions necessary for a user to define and change an application-specific style type and apply style changes. It works primarily with the Text library and graphic object library.
  • FloatFormatClass
     This controller class provides the UI and functions to allow the user to format numerical values into text (for example scientific notation, dates and times, etc.) This controller is described in The Float Format Controller .
  • Various Text Controller Classes
    The text library exports a number of controllers that are specific to text operations. These classes are described in the Text Object chapter.
  • Various Ruler Controller Classes
    The ruler library exports a number of controllers that manage the user's interactions and control over the ruler objects. These controller classes include RulerTypeControlClass , GuideCreateControlClass , and RulerGridControlClass . They are described in full in the Ruler chapter.
| Up: GEOS SDK TechDocs | Up | Prev: 1 Controller Features and Functions | Next: 3 Using Controllers


Using Controllers

Generic UI Controllers: 3 Using Controllers

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Standard Controllers | Next: 4 Creating Your Own Controllers

There are essentially two ways to use controller objects: The first, and the simplest, is to simply include an object of an existing controller class and the data object for which it's designed (e.g. using a PointSizeControl object with a GenText object). The second, and quite more complex, is to create either your own data objects or your own controllers.

This section describes the basic components of a controller object on the most general level--how to include a pre-existing controller along with its data object. For full information on how the GenControlClass works and how a data object interacts with it, see Creating Your Own Controllers . This section focuses on how you can use a controller object; nearly all of this will be academic if you use a GenToolControl object in your application.

Using a Basic GenControl Object

Using Tools

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Standard Controllers | Next: 4 Creating Your Own Controllers


Using a Basic GenControl Object

Generic UI Controllers: 3.1 Using Controllers: Using a Basic GenControl Object

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Controllers | Next: 3.2 Using Tools

To use a typical controller, you have to set up its instance data appropriately and set up the generic tree properly. This section describes the basics of GenControlClass instance data; for full information on GenControlClass , see Creating Your Own Controllers .

Every controller has a default set of features and a default set of tools. When you use a controller in your application, you can use the default configuration, the configuration appropriate to the application's user level, or a specific configuration. Most applications will want to use the second option; the controller queries the GenApplication object for its GAI_appFeatures record and determines from that which of its features and tools should be active.

Controllers can be manifested in any way the specific UI determines appropriate; three main ways, however, are menus or submenus, floating tool boxes (dialogs), and groupings along a tool bar in a window. These three modes correspond to the three manifestations of a typical GenInteraction object: GIV_POPUP (menus), GIV_DIALOG (dialogs and tool boxes), and GIV_SUB_GROUP (groupings of other generic objects). If you use a GenToolControl, you can let the user decide how the controller is displayed; otherwise, you will have to set it manually (as in the samples previously).

Every controller also has two sets of UI objects: The first set represents the UI objects used for menu entries (when the controller is in GIV_POPUP mode). The second set represents the tools that appear in a tool box (GIV_DIALOG mode) or tool bar (GIV_SUB_GROUP mode). Tools are almost always functionally redundant to the "menu" feature set. Because the tool and the menu UI resources contain different objects, the UI objects can exist in any combination of interactable states--for example, a particular feature could be in the menu only, in the tool box only, in both, or in neither. The feature can not, however, be in both a tool bar and a tool box at the same time because the set of tools can be grouped in only one location.

Using Normal Features

ATTR_GEN_CONTROL_REQUIRE_UI, ATTR_GEN_CONTROL_PROHIBIT_UI

GenControlClass has no controller features of its own; instead, each controller class must define the features it supports in both the menu implementation and the tool implementation. Any controller object is free to determine which of the features it will support and which it will not.

To set individual features on or off for a controller object in your application, use the vardata fields ATTR_GEN_CONTROL_REQUIRE_UI and ATTR_GEN_CONTROL_PROHIBIT_UI ; these specify which features will be on and which will be off. For listings of a controller class' features, you must see the description of the individual class.

The example in Declaring a Controller's Features extends the example from earlier in the chapter to turn on only the 10-, 12-, and 24-point as well as the "Larger" and "Smaller" features. It turns off all other features.

Code Display 12-2 Declaring a Controller's Features

/* This example is based on that of  A Sample Controller Application (psctext.goc). It shows what
 * would change in order to turn on only the 10-, 12-, and 24-point as well as the
 * "Larger" and "Smaller" features. Note that only the Controller object must
 * be altered. */
@object PointSizeControlClass PSCTSizeControl = {
    GI_visMoniker = 'z', "Sizes";					/* Give the controller a name */
    GII_visibility = GIV_POPUP;					/* Make the controller a menu */
    /* The following attribute defines which of the controller's
     * features are to be supported. These menu items will appear
     * in the controller's Size menu. */
    ATTR_GEN_CONTROL_REQUIRE_UI = (PSCF_10 | PSCF_12 | PSCF_24 |
                PSCF_SMALLER | PSCF_LARGER);
    /* The following attribute defines which of the controller's
     * features will not be supported. These menu items will not
     * appear in the controller's Size menu. */
    ATTR_GEN_CONTROL_PROHIBIT_UI = (PSCF_14 | PSCF_18 | PSCF_36 | PSCF_54 |
                 PSCF_72 | PSCF_CUSTOM_SIZE);
}

Adding Application-Specific UI Gadgetry

ATTR_GEN_CONTROL_APP_UI, ATTR_GEN_CONTROL_APP_TOOLBOX_UI

Occasionally, an application will want to add its own UI gadgetry to a controller. This is not the same as changing the controller's functionality--to do that, you would need to subclass the controller class. Rather, this entails simply specifying a group of generic UI objects (e.g. a GenInteraction and some GenTriggers) that will be included with the controller's UI objects.

The GenControlClass vardata attribute ATTR_GEN_CONTROL_APP_UI allows you to specify a generic tree that will be added as a child of the controller. The top node of this tree must be an object that can be a child of a GenInteraction object--typically, it will be a GenInteraction, a GenTrigger, or a GenValue.

For example, if you wanted the sample application to have two extra triggers added to the Size menu, you would use ATTR_GEN_CONTROL_APP_UI as shown in Adding UI to a Controller . This example adds two triggers that turn the bottom GenText object on and off. (Of course, you would not likely put such functionality in the Size menu; this is given for illustration.)

This attribute has no effect on the tools managed by the controller, only on its features. To add application-specific tools to a controller, you must use ATTR_GEN_CONTROL_APP_TOOLBOX_UI .

Code Display 12-3 Adding UI to a Controller

/* This display shows the modified PointSizeControl object and the additional
 * UI gadgetry required to add two triggers to it. Although the triggers are
 * shown here in the same resource block as the controller, they do not have to
 * be. They do, however, have to be run by the same thread as the controller. */
@object PointSizeControlClass PSCTSizeControl = {
    GI_visMoniker = 'z', "Sizes";					/* Give the controller a name */
    GII_visibility = GIV_POPUP;					/* Make the controller a menu */
    /* The following attribute specifies the top object of a generic
     * tree to be included with the controller's UI. This attribute does
     * not affect the toolbar implementation of the controller. */
    ATTR_GEN_CONTROL_APP_UI = (@PSCTSpecialTrigs);						/* must be an optr */
}
/* This GenInteraction and its children (the two GenTriggers) will be included in
 * the controller's default representation (typically a menu). The GenInteraction
 * will appear as a submenu in OSF/Motif. All of the objects must be set not
 * usable (~GS_USABLE); they will be made usable by the controller when it becomes
 * usable. */
@object GenInteractionClass PSCTSpecialTrigs = {
    GI_comp = @PSCTEnableTrig, @PSCTDisableTrig;
    GI_states = @default & ~GS_USABLE;
    GII_visibility = GIV_POPUP;					/* Appear as a submenu-type item */
}
/* The triggers set the bottom GenText object usable or not usable. The instance
 * data of the triggers is unimportant for this example, but it is shown here
 * to complete the example. */
@object GenTriggerClass PSCTEnableTrig = {
    GI_visMoniker = "Use Two Text Fields";
    GI_states = @default & ~GS_USABLE;
    GTI_actionMsg = MSG_GEN_SET_USABLE;
    GTI_destination = PSCBotTextObj;
    ATTR_GEN_TRIGGER_ACTION_DATA = (VUM_NOW);
}
@object GenTriggerClass PSCTDisableTrig = {
    GI_visMoniker = "Use One Text Field";
    GI_states = @default & ~GS_USABLE;
    GTI_actionMsg = MSG_GEN_SET_NOT_USABLE;
    GTI_destination = PSCBotTextObj;
    ATTR_GEN_TRIGGER_ACTION_DATA = (VUM_NOW);
}
| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Controllers | Next: 3.2 Using Tools


Using Tools

Generic UI Controllers: 3.2 Using Controllers: Using Tools

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Using a Basic GenControl Object | Next: 4 Creating Your Own Controllers
HINT_GEN_CONTROL_TOOLBOX_ONLY, ATTR_GEN_CONTROL_REQUIRE_TOOLBOX_UI, ATTR_GEN_CONTROL_PROHIBIT_TOOLBOX_UI, ATTR_GEN_CONTROL_APP_TOOLBOX_UI

A s stated earlier, any GenControl object can be manifested either as menus and menu items or as a set of tools. In fact, the controller can potentially have both its menu and its tools usable at once. Creating and using a toolbox or tool bar is a bit more complex than simply including the controller, however.

You can add the use of tool bars and tool boxes to your application in two ways: First, you can simply add a GenToolControl object and associated GenToolGroups and let them do all the work for you. Second, you can interact directly with the controller object(s) to put up, take down, and otherwise manage the tools. In nearly all cases, the first is preferable.

This section focuses on the use of a GenToolControl and GenToolGroup objects to manage and place your tools and toolboxes. If you want to manage tools without using a GenToolControl, you will have to know more about GenControlClass ; see Creating Your Own Controllers for complete details.

Components of Tool Management

Tools are most often represented by specific buttons or popup lists presented in various toolbars for easy use. They are functionally redundant to the menu features represented by the normal features of the controller, but the tools and menus can both be visible and usable at the same time.

To provide and manage tools, you need to understand the following components:

  • Controller
    The controller is the same GenControl subclass object discussed and shown in the previous sections. You can set minimum and maximum tool sets just like setting minimum and maximum feature set.
  • Tool Group
    Each controller object should have exactly one corresponding GenToolGroup object. The GenToolGroup manages the controller's tools and is, in turn, managed by the GenToolControl object (below).
  • Tool Controller
    You should have exactly one GenToolControl object; this object is a controller that allows the user to turn on and off individual tools and entire tool sets. It also controls placement of each tool group--which tool bar the tool group appears in.
  • Tool Bars and Tool Boxes
    You can have any number of tool bars and floating tool boxes. A tool bar or tool box is simply a GenInteraction set up properly and accessible by the user. Examples of tool bars are given in the next sections.
  • Data Tables
    Because the GenToolControl needs to describe both the tool bars and the tool groups to the user, you must set up special data chunks containing tables of names for each tool bar and tool group you use.

Using a Basic Tool Box

The most basic tool configuration includes a floating tool box in which the controllers' tools appear. To use a tool box, you only need to define a toolbox GenInteraction, add a GenToolControl object, and put them both in a menu. Typically, this will be an "Options" menu.

Providing a Basic Tool Box shows the configuration required for providing a basic tool box as well as the controller's default menu. (You could prohibit the appearance of the menu by setting ATTR_GEN_CONTROL_TOOLBOX_ONLY in the controller's instance data.) This example provides all the tools by setting the application's UI level in the GenApplication object. All the tools will then appear in the toolbox and its associated popups as shown in the figure above.

Setting Up the Tool Box

The tool box must be a GenInteraction object; for a floating tool box (as opposed to a tool bar), set it to be a dialog box (GIV_DIALOG). It should also have a moniker and HINT_TOOLBOX . The tool box must also be given a name. This name is used by the GenToolControl to identify the tool box. The name is set in a separate chunk and is a simple character string.

At startup, the GenToolGroup for the point size controller is set as a child of the floating tool box. The GenToolControl will move the GenToolGroup to other toolbars if possible; the tools must be set somewhere at startup, though, and the tool box is the logical starting point.

Setting Up the Tool Controller

The GenToolControl object will be of GenToolControlClass , as shown. It must at least have the GTCI_toolboxList field set to the chunk handle of a table of tool locations. In this example, the only location available to tools is in the floating tool box; other locations may be specified as detailed in Tool Placement below.

The table must appear in its own chunk in the same resource block as the GenToolControl object. It is an array of ToolboxInfo structures, each of which contains two optrs. The first is the optr of a tool location (typically a GenInteraction that may contain tools), and the second is the optr of the associated name. The structure of ToolboxInfo is shown below:

typedef struct {
    optr	TI_object;		/* A GenInteraction that
                 * can contain tools */
    optr	TI_name;		/* The name chunk of the
                 * TI_object object */
} ToolboxInfo;

The table is defined as shown in Providing a Basic Tool Box . If you had other controllers to be managed by the tool control, you would add other entries separated by commas.

The GenToolControl also has a tool group list indicating the name of each GenToolGroup. It is set up in a similar form to the tool box table.

Code Display 12-4 Providing a Basic Tool Box

/* This code display shows the entire psctext.goc file, with changes noted. All
 * unchanged code has had its comments stripped. For descriptions, see earlier
 * displays in this chapter. */
@include <stdapp.goh>
@include <ui.goh>
@include <Objects/Text/tCtrlC.goh>
@class	PSCTextProcessClass, GenProcessClass;
@endc
@classdecl PSCTextProcessClass, neverSaved;
@start	AppResource;
@object GenApplicationClass PSCTextApp = {
    GI_visMoniker = "Point Size Control Sample Application";
    GI_comp = @PSCTPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = @PSCTPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) =
                     @PSCTSizeControl, @PSCTToolControl;
}
@end	AppResource
@start	Interface;
/*	GenPrimary Object
 * Typically, an Options menu will be set up as a child of the Primary and
 * the tool box and tool control will be children of that menu. */
@object GenPrimaryClass PSCTPrimary = {
    GI_comp =		@PSCTSizeControl, @PSCTopTextObj, @PSCBotTextObj, @PSCTOptionsMenu;
    HINT_SIZE_WINDOW_AS_DESIRED;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
/*	Options Menu
 * This menu is the parent for both the Tool Box and the tool controller. */
@object GenInteractionClass PSCTOptMenu = {
    GI_comp = @PSCTToolBox, @PSCTToolControl;
    GII_visibility = GIV_POPUP;
    ATTR_GEN_INTERACTION_GROUP_TYPE = (GIGT_OPTIONS_MENU);
}
/*	Tool Box Location Table
 * The Tool Box Location Table is used by the GenToolControl object
 * to associate tool locations with their names. The table is an array
 * of ToolboxInfo structures. Multiple entries would be separated with
 * commas. */
@chunk ToolboxInfo PSCTToolboxList[] = {
    {@PSCTToolBox, @PSCTToolBoxName}					/* The single tool location is the
                     * floating tool box PSCTToolBox. */
};
/*	Tool Group Information Table
 * The Tool Group Information Table is used by the GenToolControl to associate
 * tool groups with their names. The table is an array of ToolGroupInfo structures.
 * Multiple entries would be separated with commas. */
@chunk ToolGroupInfo PSCTToolGroupTable[] = {
    {@PSCTPointSizeToolGroup}
};
/*	Floating Tool Box
 * The Tool Box object is a GenInteraction dialog box. All toolboxes must
 * have HINT_TOOLBOX and may have any additional geometry hints you
 * deem necessary. Because every controller's GenToolGroup object must
 * be a child of some tool bar, the PSCTPointSizeToolGroup is set at
 * startup as a child of this floating toolbox. */
@object GenInteractionClass PSCTToolBox = {
    GI_visMoniker = `T', "Tools";
    GI_comp = @PSCTPointSizeToolGroup;
    GII_visibility = GIV_DIALOG;
    HINT_TOOLBOX;
    HINT_ALLOW_CHILDREN_TO_WRAP;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_FULL_JUSTIFY_CHILDREN_HORIZONTALLY;
}
/* The Tool Box Name must be specified for the tool control object. It
 * must be a character string, and there must be one for each entry in
 * the Tool Location table. */
@chunk char PSCTToolBoxName[] = "Floating Tool Box";
/*	GenToolControl Object
 * The Tool Control object must have just the GTCI_toolboxList and
 * GTCI_toolGroupList fields set; these contain lists of tool bars and
 * tool groups along with their names. */
@object GenToolControlClass PSCTToolControl = {
    GTCI_toolboxList = @PSCTToolboxList;
    GTCI_toolGroupList = @PSCTToolGroupTable;
    HINT_SAME_CATEGORY_AS_PARENT;
}
/*	PointSizeControl			*/
@object PointSizeControlClass PSCTSizeControl = {
    GI_visMoniker = `z', "Sizes";
    GII_visibility = GIV_POPUP;
}
/*	GenToolGroup Object
 * Each controller object has exactly one GenToolGroup object for managing
 * its tools and for management by the GenToolControl. The Tool Group has
 * a single instance field specifying the controller for which it works. */
@object GenToolGroupClass PSCTPointSizeToolGroup = {
    GTGI_controller = @PSCTSizeControl;
}
/*	GenText Objects			*/
@object GenTextClass PSCTopTextObj = {
    GI_attrs = @default | GA_TARGETABLE;
    HINT_DEFAULT_FOCUS;
    HINT_DEFAULT_TARGET;
    ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR = ((VTDS_12 << VTDCA_SIZE_OFFSET) |
                    VTDF_URW_ROMAN);
}
@object GenTextClass PSCBotTextObj = {
 GI_attrs = @default | GA_TARGETABLE;
 ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR =
 ((VTDS_12 << VTDCA_SIZE_OFFSET) | VTDF_URW_ROMAN);
}
@end Interface

Tool Placement

Tools are movable; this means that controller tools can appear in any GenInteraction you may specify in the Tool Location Table. For example, you may want the user to be able to specify where she or he wants the tools to appear: in the floating tool box, at the top of the primary window, at the left of the display, or between the text objects. To support these locations for the tools, all you need to do is set up empty GenInteraction objects in the appropriate locations and add entries to the Tool Location Table.

The setup described above requires quite a few GenInteraction objects to be added to the application's generic tree. A line drawing of the geometry with all the empty GenInteractions is given in the figure above and the new generic tree of the application is shown in the figure below. The code representing this configuration is shown in Movable Tools --pay particular attention to the Tool Location Table and to the fact that each GenInteraction must have the hint HINT_TOOLBOX set in order to receive the tools.

Code Display 12-5 Movable Tools

/* This code display builds on  Providing a Basic Tool Box to show how tools
 * may be moved around your application's window by the user. Although this is
 * not difficult to do from scratch, it is quite involved; if you want this
 * functionality, it is best to include a GenToolControl object.
 * This code display only shows those objects that are additional to or altered
 * from the previous display. */
/*	GenPrimary Object
 * Two GenInteractions are made children of the Primary for geometry purposes.
 * The first, LeftToolBar, is actually a tool bar; the second,
 * TextAndToolInteraction, is a grouper interaction for geometry purposes. */
@object GenPrimaryClass PSCTPrimary = {
    GI_comp = @LeftToolBar, @PSCTSizeControl, @PSCTOptMenu,
                    @TextAndToolInteraction;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) =
                    @PSCTSizeControl, @PSCTToolControl;
    HINT_SIZE_WINDOW_AS_DESIRED;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
/*	Tool Location Table
 * The Tool Location Table is updated with the new tool box information. Each of
 * the new tool boxes is given a name, and each must appear in this table.
 * Note that the ToolGroup Information Table does not change. */
@chunk ToolboxInfo PSCTToolboxList[] = {
    {@PSCTToolBox, @PSCTToolBoxName},
    {@LeftToolBar, @LeftToolBarName},
    {@TopToolBar, @TopToolBarName},
    {@MiddleToolBar, @MiddleToolBarName}
};
/*	TextAndToolInteraction Interaction
 * This GenInteraction is used solely as a place holder grouping object to allow
 * the LeftToolBar object to extend the full height of the Primary window. */
@object GenInteractionClass TextAndToolInteraction = {
    GI_comp = @TopToolBar, @PSCTopTextObj, @MiddleToolBar, @PSCBotTextObj;
    HINT_ORIENT_CHILDREN_VERTICALLY;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
}
/*	New Tool Box Interactions
 * These GenInteraction objects are all tool boxes that appear in the Tool Location
 * Table. None actually has tools in it on startup; the tool controller allows the
 * user to place the tools of each active controller in any of these tool boxes. */
@object GenInteractionClass LeftToolBar = {
    HINT_TOOLBOX;
    HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
    HINT_ALLOW_CHILDREN_TO_WRAP;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@chunk char LeftToolBarName[] = "Left of Text";
@object GenInteractionClass TopToolBar = {
    HINT_TOOLBOX;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_ALLOW_CHILDREN_TO_WRAP;
}
@chunk char TopToolBarName[] = "Above Text";
@object GenInteractionClass MiddleToolBar = {
    HINT_TOOLBOX;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_ALLOW_CHILDREN_TO_WRAP;
}
@chunk char MiddleToolBarName[] = "In Between Text";

Adding Application-Specific UI to the Tool Box

ATTR_GEN_CONTROL_APP_TOOLBOX_UI

Occasionally an application may want to add some additional UI gadgetry to a set of controller tools. ATTR_GEN_CONTROL_APP_TOOLBOX_UI is analogous to ATTR_GEN_CONTROL_APP_UI , described in Adding Application-Specific UI Gadgetry . This attribute specifies a generic object tree that can be attached to the controller's tools as if it were part of the controller normally. For an example of ATTR_GEN_CONTROL_APP_UI 's use, see Adding Application-Specific UI Gadgetry .

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Using a Basic GenControl Object | Next: 4 Creating Your Own Controllers


Creating Your Own Controllers

Generic UI Controllers: 4 Creating Your Own Controllers

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Using Controllers | Next: 5 GenToolControlClass

To create your own controller classes, you will have to subclass GenControlClass and handle certain messages and data structures. Most applications, however, will find that the controller classes provided with GEOS are adequate for their needs. Some library programmers may want to create their own controller classes.

GenControlClass is a subclass of GenInteractionClass . All instance data, hints, and messages appropriate for a GenInteraction are also appropriate for a GenControl object.

Typically, when you create your own controller, you will create a library in which the controller will reside. The controller may be in a library with a particular data object (e.g. PointSizeControlClass is part of the text library), or it may be in a library all by itself. For this discussion, the controller is considered to be part of a larger library.

The files of this library are

psCtrl.gp
The global parameters file for the controller's library. Only those portions of this file that pertain to the controller are discussed in this section.
psCtrl.goh
The header file containing the class definition of the controller class. This is separated from the class' code so the user of the controller can include the .goh file and thereby the controller.
psCtrl.goc
The code and resource file of the controller class. This file contains the UI objects and the methods of the controller.

All of these files are detailed throughout the following subsections. Each code display explains in which of these files it belongs.

GenControlClass Instance Data

Subclassing GenControlClass

Advanced GenControlClass Usage

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Using Controllers | Next: 5 GenToolControlClass


GenControlClass Instance Data

Generic UI Controllers: 4.1 Creating Your Own Controllers: GenControlClass Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 4 Creating Your Own Controllers | Next: 4.2 Subclassing GenControlClass

GenControlClass has several instance data fields that determine the controller's features and supported tool set. These instance fields, along with the messages defined for the class, are shown in GenControlClass Instance Data .

Code Display 12-6 GenControlClass Instance Data

/* GenControlClass has one static instance data field and several dynamic (vardata)
 * fields. These are shown below. */
    /* The GCI_output field contains the optr of the object to which
     * the controller is currently sending its "apply" messages. This
     * field is typically set to a GenTravelOption (such as TO_APP_TARGET)
     * or a TravelOption (such as TO_OBJ_BLOCK_OUTPUT). */
    @instance		optr	 GCI_output;
    /* Controller class objects are set disabled by default. When the
     * controller is initialized, it will set itself enabled. */
    @default		GI_states = (@default & ~GS_ENABLED);
/* the following attributes and hints determine the controller's
 * feature set. All of these hints and attributes are described in
 *  Using Controllers. */
    @vardata void			 HINT_GEN_CONTROL_TOOLBOX_ONLY;
    @vardata WordFlags			 ATTR_GEN_CONTROL_REQUIRE_UI;
    @vardata WordFlags			 ATTR_GEN_CONTROL_PROHIBIT_UI;
    @vardata WordFlags			 ATTR_GEN_CONTROL_REQUIRE_TOOLBOX_UI;
    @vardata WordFlags			 ATTR_GEN_CONTROL_PROHIBIT_TOOLBOX_UI;
/* The following two attributes control additional UI gadgetry added to a
 * controller object. */
    @vardata optr			 ATTR_GEN_CONTROL_APP_UI;
    @reloc	ATTR_GEN_CONTROL_APP_UI, 0, optr;
    @vardata optr			ATTR_GEN_CONTROL_APP_TOOLBOX_UI;
    @reloc	 ATTR_GEN_CONTROL_APP_TOOLBOX_UI, 0, optr;
 /* The following two hints allow an application to specify the initial
 * state of a controller including its placement, features, and
 * additional UI objects. */
    @vardata GenControlUserData  HINT_GEN_CONTROL_MODIFY_INITIAL_UI;
    @vardata GenControlUserData  HINT_GEN_CONTROL_USER_MODIFIED_UI;
 /* The following temporary data field is used to determine the tool settings
 * for a controller's set of tools. */
    @vardata TempGenControlInstance  TEMP_GEN_CONTROL_INSTANCE;

In general, a controller class will not have to worry about the instance data fields in GenControlClass Instance Data . Instead, it will handle a particular message, set up some UI resources, and rely on the functionality built into GenControlClass .

| Up: GEOS SDK TechDocs | Up | Prev: 4 Creating Your Own Controllers | Next: 4.2 Subclassing GenControlClass


Subclassing GenControlClass

Generic UI Controllers: 4.2 Creating Your Own Controllers: Subclassing GenControlClass

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 GenControlClass Instance Data | Next: 4.3 Advanced GenControlClass Usage

When creating your own controller, you must subclass GenControlClass . You must also follow the steps outlined below (each is described in more detail throughout this section):

  1. Define the features and tool records
    You must define records and flags for the entire feature set and tool set of the controller. Each bit in one of these records corresponds to a single feature or tool of the controller; by turning these bits on and off, the controller manages which features and tools are available to the user. See Defining a Controller's Feature and Tool Sets .
  2. Define the default UI configuration
    Define a resource block containing the objects that will be the controller's default UI representation (typically a menu structure). See Defining the Default UI and Tool Configurations .
  3. Define the tool configuration
    Define a resource block containing generic UI objects that will make up the controller's tool set. See Defining the Default UI and Tool Configurations .
  4. Handle MSG_GEN_CONTROL_GET_INFO
     Every controller must handle this message and return critical information about the controller, its features, and its tools. This is the most involved step of creating a controller. See Mandatory Message Handling .
  5. Intercept appropriate GenControlClass messages
    Different controller classes will intercept different GenControlClass messages depending on how much additional functionality they require. Most controllers will intercept MSG_GEN_CONTROL_UPDATE_UI .
  6. Define and handle controller-specific messages
    Some controllers will set up their features and tools so that the messages they generate require some translation. For example, if a controller has one feature that sets a value based on all the other values, that feature may send a "set" message to the controller, which will determine the appropriate value to set. The controller will then pass the result on to the output object.

Defining a Controller's Feature and Tool Sets

E very controller must have a definition of all its features and tools. This definition typically resides in the controller class header file (in this case, psCtrl.goh ). Applications that use the controller must be able to turn on and off individual tools and features. These definitions take the form of records in which each bit represents a particular feature or tool. No controller may have more than sixteen features or tools; the controller has just one word representing which features and tools are "on."

To define the feature set of your controller class, define a record type of type WordFlags and one flag for each feature. The record type you define may be named anything; typically, however, its name will consist of the acronym of the controller class with the suffix "Features." For example, the features type and flags of PointSizeControlClass are shown below:

 typedef WordFlags PSCFeatures;
#define PSCF_10				0x0400
#define PSCF_12				0x0200
#define PSCF_14				0x0100
#define PSCF_18				0x0080
#define PSCF_24				0x0040
#define PSCF_36				0x0020
#define PSCF_54				0x0010
#define PSCF_72				0x0008
#define PSCF_SMALLER				0x0004
#define PSCF_LARGER				0x0002
#define PSCF_CUSTOM_SIZE 0x0001

Each of the flags represents one feature of the controller. For example, the PSCF_10 flag represents the "10 Point" trigger in the Sizes menu, and the PSCF_SMALLER flag represents the "Smaller" trigger. When a flag is set, the feature it represents is turned on; when clear, its feature is off.

Each controller class must create a similar record type and flags for its tool set. Because the tools are independent of the default features, two different sets of flags must be defined. An example (the PSCToolboxFeatures record of PointSizeControlClass ) follows.

 typedef WordFlags PSCToolboxFeatures;
#define PSCTF_9				0x0400
#define PSCTF_10				0x0200
#define PSCTF_12				0x0100
#define PSCTF_14				0x0080
#define PSCTF_18				0x0040
#define PSCTF_24				0x0020
#define PSCTF_36				0x0010
#define PSCTF_54				0x0008
#define PSCTF_72				0x0004
#define PSCTF_SMALLER				0x0002
#define PSCTF_LARGER				0x0001

After you have defined your controller class' feature and tool sets, you should define the controller's default feature and tool sets. The definitions for PointSizeControlClass are shown below:

#define PSC_DEFAULT_FEATURES					(PSCF_9 | PSCF_10 |
        PSCF_12 | PSCF_14 | PSCF_18 | PSCF_24 |
        PSCF_36 | PSCF_72 | PSCF_CUSTOM_SIZE |
        PSCF_SMALLER | PSCF_LARGER)
#define PSC_DEFAULT_TOOLBOX_FEATURES (PSCTF_9 |
        PSCTF_10 | PSCTF_12 | PSCTF_14 |
        PSCTF_18 | PSCTF_24 | PSCTF_36 |
        PSCTF_72 | PSCTF_SMALLER | PSCTF_LARGER)

These values and flags will be used in your handler for the controller message MSG_GEN_CONTROL_GET_INFO .

Defining the Default UI and Tool Configurations

After you have determined which features and tools your controller class will support, you must create the UI objects that correspond to them. To do this, declare two separate resource segments--one to contain the feature objects and the other to contain the tool objects. Then declare a third that contains just chunks with text strings in it.

Both resources should be defined notDetachable , meaning that the feature and tool objects will not be saved to a state file. In the global parameters file for your controller, each resource must be declared with the ui-object , read-only , and shared flags as below:

resource SIZECTRLUI ui-object read-only shared
resource SIZECTRLTOOLUI ui-object read-only shared
resource CONTROLSTRINGS lmem read-only shared

The UI resources typically contain list objects and their corresponding items. As an alternative, they can contain triggers and dialogs. These objects are declared as a standard object resource with generic objects, as shown in Controller UI Resources . All these objects must be set not usable (~GS_USABLE).

Code Display 12-7 Controller UI Resources

/* This display contains only code that appears in the psCtrl.goc file. The first
 * elements of the file are other included files, followed by a class declaration
 * statement. The two UI resources are shown after that, simplified somewhat;
 * Only redundant objects are left out of the display. */
/* Include the controller class definition and declare the class structure. */
@include <psCtrl.goh>
@classdecl PointSizeControlClass;
@start	SizeCtrlUI, notDetachable;
/* Define the features UI resource. This resource can contain any objects that may
 * typically appear in a menu (e.g. GenInteractions, GenTriggers, and list
 * objects). This example shows a single list object and a few of its entries. */
@object GenItemGroupClass SizesList = {
    GI_states = @default & ~GS_USABLE;					/* Set the list not usable */
    /* The children of the list are defined below. Each entry in the
     * list will appear as a single menu item. */
    GI_comp =		@Size10Entry, @Size12Entry, @Size14Entry, @Size18Entry,
        @Size24Entry, @Size36Entry, @Size54Entry, @Size72Entry;
    /* The "apply" message will be sent to the destination specified
     * in the GIGI_destination field. */
    GIGI_applyMsg = MSG_PSC_SET_POINT_SIZE_FROM_LIST;
    /* The destination is defined as the TravelOption TO_OBJ_BLOCK_OUTPUT.
     * This will send the apply message to the controller's output object. */
    GIGI_destination = (TO_OBJ_BLOCK_OUTPUT);
}
/* An example of a GenItem for the above list. all the other children are similar
 * with different monikers and identifiers. The identifiers in this case are
 * equivalent to the point size setting for the feature. */
@object GenItemClass Size10Entry = {
    GI_visMoniker = `1', "1. 10 point";
    GII_identifier = 10;
}
/* A GenTrigger. Shown below is the "Smaller" menu entry of the Point Size
 * controller. Another trigger ("Larger") and a GenInteraction (the "Custom
 * Size" entry) are also declared. These objects do not have to be declared
 * as children of any object; they will automatically, like the list above,
 * be designated as children of the controller when it is initialized.
 * Note that all of these objects must also be set not usable. */
@object GenTriggerClass SmallerTrigger = {
    GI_states = @default & ~GS_USABLE;
    GI_visMoniker = `S', "Smaller";
    GI_kbdAccelerator = control `9';
    GTI_actionMsg = MSG_PSC_SMALLER_POINT_SIZE;
    GTI_destination = (TO_OBJ_BLOCK_OUTPUT);
}
@end	SizeCtrlUI
/* Define the Tools UI resource. This follows exactly the same rules as the
 * Features UI resource above, but it represents the UI gadgetry that will appear
 * in the controller's tool boxes rather than its default menus. */
@start	SizeCtrlToolUI, notDetachable;
@object GenItemGroupClass SizesToolList = {
    /* Same as SizesList above, but with the following hints applied: */
    HINT_ITEM_GROUP_MINIMIZE_SIZE;
    HINT_ITEM_GROUP_DISPLAY_CURRENT_SELECTION;
}
/* The list entry items have the exact configuration as above but different
 * names that reflect their tool usage.
 * The only objects allowed as tools for the Point Size controller are the
 * point size list entries, the larger trigger, and the smaller trigger. The
 * "Custom Size" entry is not allowed in the tool box as a matter of style. */
@object GenTriggerClass SmallerToolTrigger = {
    GI_states = @default & ~GS_USABLE;
    GI_visMoniker = "S";
    /* The moniker of a tool is typically a graphic. The moniker
     * specified here is text for simplicity. */
    GTI_actionMsg = MSG_PSC_SMALLER_POINT_SIZE;
    GTI_destination = TO_OBJ_BLOCK_OUTPUT;
}
@end	SizeCtrlToolUI
@start	ControlStrings;
/* In addition to the above two resources, you must also create a third that
 * contains name strings for the various tools and features. These name strings
 * will be used by the GenToolControl to identify the feature type in its
 * dialog box. */
@chunk char		PSCName[] = "Point Size";
@chunk char		Size10Name[] = "10 Point";
@chunk char		Size12Name[] = "12 Point";
    /* The rest of the point sizes are similar */
@chunk char		SmallerName[] = "Smaller Point Size";
@chunk char		LargerName[] = "Larger Point Size";
@chunk char		CustomSizeName[] = "Custom Point Size";
@end	ControlStrings

Mandatory Message Handling

 MSG_GEN_CONTROL_GET_INFO, GenControlBuildInfo

Every controller must handle MSG_GEN_CONTROL_GET_INFO . This message is sent to the controller in several circumstances, and it must return critical information about the controller's state and configuration. It takes a pointer to an empty GenControlBuildInfo structure and must fill in all the structure's fields before returning. This structure is shown in MSG_GEN_CONTROL_GET_INFO Handler with a description of each of its fields following.

Code Display 12-8 The GenControlBuildInfo Structure

 /* This structure must be filled and returned by the controller class. It details
 * general information as well as specific information about the controller, the
 * controller's features, and the controller's tools. */
typedef struct {
    GenControlBuildFlags				GCBI_flags;
    const char				*GCBI_initFileKey;
    const GCNListType				*GCBI_gcnList;
    word				GCBI_gcnCount;
    const NotificationType				*GCBI_notificationList;
    word				GCBI_notificationCount;
    optr				GCBI_controllerName;
    MemHandle				GCBI_dupBlock;
    const GenControlChildInfo				*GCBI_childList;
    word				GCBI_childCount;
    const GenControlFeaturesInfo				*GCBI_featuresList;
    word				GCBI_featuresCount;
    WordFlags				GCBI_features;
    MemHandle				GCBI_toolBlock;
    const GenControlChildInfo				*GCBI_toolList;
    word				GCBI_toolCount;
    const GenControlFeaturesInfo				*GCBI_toolFeaturesList;
    word				GCBI_toolFeaturesCount;
    WordFlags				GCBI_toolFeatures;
    char				*GCBI_helpContext;
    byte				GCBI_reserved[8];
} GenControlBuildInfo;

The following fields define general information about the controller.

GCBI_flags
A record of GenControlBuildFlags . These flags affect several UI-related and object storage related functions, and they are detailed on GenControlBuildFlags .
GCBI_initFileKey
A pointer to a text string indicating the controller's key in the GEOS.INI file. Controller options will be saved under this key.
GCBI_gcnList
A pointer to a list of GCN list types. Objects of this controller class will be added to these GCN lists and will receive notification from them. GCN lists are detailed in the GCN chapter.

GCBI_gcnCount
 The length of the list pointed to by GCBI_gcnList above. This length should be the number of lists specified.

GCBI_notificationList
 A pointer to a list of notification types supported by the controller.
GCBI_notificationCount
The size of the list pointed to by GCBI_notificationList above.
GCBI_controllerName
The optr of a chunk containing the text string that serves as the controller's name. This name string is displayed by the GenToolControl in its dialog box to identify the controller.

The following fields define information about the controller's features. These fields will be filled dependent on the features set in the object's instance data and the UI level of the controller.

GCBI_dupBlock
The handle of the resource block containing the controller's feature generic objects. In the example, this would contain the handle of the SizeCtrlUI resource.
GCBI_childList
A pointer to a list of GenControlChildInfo structures; each of these structures details which features are set and which should always be set for each of the controller's children. This structure is shown below:
    typedef struct {
        ChunkHandle			GCCI_object;
        WordFlags			GCCI_featureMask;
        GenControlChildFlags GCCI_flags;
    }  GenControlChildInfo;
Each structure contains the chunk handle of the given child in t he resource block, a feature mask indicating which features are possibly supported by the child, and a record indicating whether the child is a feature or not and whether the child is always added to the controller's UI. More specific information is shown on GenControlChildInfo .
GCBI_childCount
The number of children specified in GCBI_childList above.
GCBI_featuresList
A pointer to a list of GenControlFeaturesInfo structures, one for each child. These structures define the following:
    typedef struct {
        ChunkHandle			GCFI_object;
        optr			GCFI_name;
        GenControlFeatureFlags GCFI_flags;
    }  GenControlFeaturesInfo;
The three fields are the chunk handle of the child; the optr of the child's name string, as defined in the name string resource; and a record of GenControlFeatureFlags . This structure and its fields are described more fully on GenControlFeaturesInfo .
GCBI_featuresCount
The number of GenControlFeaturesInfo structures listed in GCBI_featuresList above.
GCBI_features
A features mask describing the features supported by the current UI level as specified in the GenApplication's GAI_appFeatures field.

The following fields describe information about the controller's tools and their configuration.

GCBI_toolBlock
The handle of the resource block containing the controller's tool generic objects. In the example, this would contain the handle of the SizeCtrlToolUI resource.
GCBI_toolList
A pointer to a list of GenControlChildInfo structures; each of these structures details which tools are set and which should always be set for each of the controller's children. This structure is shown below:
    typedef struct {
        ChunkHandle			GCCI_object;
        WordFlags			GCCI_featureMask;
        GenControlChildFlags GCCI_flags;
    }  GenControlChildInfo;
Each structure contains the chunk handle of the given child in t he resource block, a feature mask indicating which tools are possibly supported by the child, and a record indicating whether the child is a tool or not (e.g. a list) and whether the child is always added to the controller's tool box UI. More specific information is shown on GenControlChildInfo .
GCBI_toolCount
The number of children specified in GCBI_toolList above.
GCBI_toolFeaturesList
A pointer to a list of GenControlFeaturesInfo structures, one for each child. These structures define the following:
    typedef struct {
        ChunkHandle			GCFI_object;
        optr			GCFI_name;
        byte			GCFI_flags;
                /* GenControlFeatureFlags */
    }  GenControlFeaturesInfo;
The three fields are the chunk handle of the child; the optr of the child's name string, as defined in the name string resource; and a record of GenControlFeatureFlags . This structure and its fields are described more fully on GenControlFeaturesInfo .
GCBI_toolFeaturesCount
The number of GenControlFeaturesInfo structures in the list pointed to by GCBI_toolFeaturesList above.
GCBI_toolFeatures
A tools mask describing the tools supported for the UI level specified in the GenApplication's GAI_appFeatures field.

The following field is used by controllers that offer their own help files and help text.

GCBI_helpContext
A pointer to a character string giving the name of the controller's help context. If this is a non-null pointer, then the controller will automatically add ATTR_GEN_HELP_CONTEXT to itself with the specified string.

The structure also has eight bytes that are reserved for the use of GenControlClass, in the GCBI_reserved field.

GenControlBuildFlags

This flags record defines several UI-related things about the controller object. Set them appropriate to your controller. The flags are

GCBF_SUSPEND_ON_APPLY
Causes MSG_META_SUSPEND to be sent on feature activation and MSG_META_UNSUSPEND afterward. This is often set by controllers.
GCBF_USE_GEN_DESTROY
Ensures that unused objects can not be freed with LMemFree() . Not often set by controllers.
GCBF_SPECIFIC_UI
Indicates that the controller may be implemented in the specific UI and therefore some special action must be taken. Very rarely set by controllers.
GCBF_CUSTOM_ENABLE_DISABLE
Indicates that the controller uses a custom enable/disable mechanism rather than responding to GCN notifications.
GCBF_ALWAYS_UPDATE
Indicates that the controller should always undergo visual updates even if it appears unnecessary. Not often set by controllers.
GCBF_EXPAND_TOOL_WIDTH_TO_FIT_PARENT
Indicates that the tool width should be expanded to take full advantage of all the space available in the parent composite.
GCBF_ALWAYS_INTERACTABLE
Indicates that the controller should always be on its appropriate GCN lists, even if no part of it is visible. This flag requires GCBF_IS_ON_ACTIVE_LIST.
GCBF_ALWAYS_ON_GCN_LIST
Indicates that the controller should constantly be on the GCN lists rather than periodically adding and removing itself as is done in some optimization code. Not often set by controllers. This flag requires GCBF_IS_ON_ACTIVE_LIST.
GCBF_MANUALLY_REMOVE_FROM_ACTIVE_LIST
Indicates that the controller should not remove itself from the active list in its default detach handler.
GCBF_IS_ON_ACTIVE_LIST
Indicates that the controller is on the active list in its .goh file definition.
GCBF_IS_ON_START_LOAD_OPTIONS_LIST
Indicates this controller must be on the startup load options list.
GCBF_NOT_REQUIRED_TO_BE_ON_SELF_LOAD_OPTIONS_LIST
Indicates this controller is not required to be on any options-load list.

GenControlChildInfo

The GenControlChildInfo structure defines the features or tools appropriate to each object in a controller's UI resources. It has the following structure, and its fields are described below:

typedef struct {
    ChunkHandle				GCCI_object;
    WordFlags				GCCI_featureMask;
    GenControlChildFlags				GCCI_flags;
}  GenControlChildInfo;
GCCI_object
The chunk handle of the object in the appropriate resource.
GCCI_featureMask
The feature mask representing the feature set represented by the object.
GCCI_flags
A record of GenControlChildFlags , the flags of which are described below.

The GenControlChildFlags flags are

GCCF_NOTIFY_WHEN_ADDING
This flag indicates that the child will be notified before the feature is added and set usable with MSG_GEN_CONTROL_NOTIFY_ADDING_FEATURE .
GCCF_ALWAYS_ADD
This flag indicates that the child object should always be added to the controller's UI, even if it is not specified.
GCCF_IS_DIRECTLY_A_FEATURE
This flag indicates that the child is a feature in itself and thus on the feature list. This is typically set for most objects. (It is not set, for example, for list objects, whose children are the actual features.)

GenControlFeaturesInfo

The GenControlFeaturesInfo structure describes each UI feature's name and certain flags. The structure is defined below, and its fields are described following:

typedef struct {
    ChunkHandle				GCFI_object;
    optr				GCFI_name;
    GenControlFeatureFlags				GCFI_flags;
}  GenControlFeaturesInfo;
GCFI_object
The chunk handle of the child in the appropriate resource block.
GCFI_name
The optr of the chunk containing the object's name. This name is used by the GenToolControl to represent the particular feature or tool in its dialog box.
GCFI_flags
A record of GenControlFeatureFlags , reserved.

MSG_GEN_CONTROL_GET_INFO

void	MSG_GEN_CONTROL_GET_INFO(
        GenControlBuildInfo *info);

This message must be handled by all controllers. It takes an empty GenControlBuildInfo structure and fills it; this message is called in several circumstances by different objects and controller methods.

Source: Unrestricted--typically generated in GenControlClass methods.

Destination: Any controller object.

Parameters: info A pointer to an empty GenControlBuildInfo structure.

Return: The GenControlBuildInfo structure filled with the appropriate controller information.

Interception: Every controller class must intercept this message. There is no need to call the superclass anywhere in the handler.

Code Display 12-9 MSG_GEN_CONTROL_GET_INFO Handler

/* This method is a sample of how to handle MSG_GEN_CONTROL_GET_INFO. It is
 * specific to UICTextStyleControlClass. To handle this message, it is easiest to
 * set up a number of static local variables with the base information and set
 * the structure to these variables. */
/* Handler for MSG_GEN_CONTROL_GET_INFO
 * void (GenControlBuildInfo *info);					*/
@method UICTextStyleControlClass, MSG_GEN_CONTROL_GET_INFO {
    /* General information constants */
 static const char TSC_IniFileKey[] = "textStyleControl";
 static const GCNListType TSC_gcnList[] = {
     {MANUFACTURER_ID_GEOWORKS, GAGCNLT_APP_TARGET_NOTIFY_TEXT_CHAR_ATTR_CHANGE}
 };
 static const NotificationType TSC_notifyTypeList[] = {
     {MANUFACTURER_ID_GEOWORKS, GWNT_TEXT_CHAR_ATTR_CHANGE}
 };
    /* Features information constants */
    static const  GenControlChildInfo TSC_childList[] = {
    {@PlainTextList, TSCF_PLAIN, GCCF_IS_DIRECTLY_A_FEATURE},
    {@TextStyleList, TSCF_BOLD|TSCF_ITALIC|TSCF_UNDERLINE|TSCF_STRIKE_THRU|
     TSCF_SUBSCRIPT|TSCF_SUPERSCRIPT, 0}
    };
    /* The order of this list is actually backwards from the
     * record it reflects. */
    static const  GenControlFeaturesInfo TSC_featuresList[] = {
     {@SuperscriptEntry, @SuperscriptName, 0},
     {@SubscriptEntry, @SubscriptName, 0},
     {@StrikeThruEntry, @StrikeThruName, 0},
     {@UnderlineEntry, @UnderlineName, 0},
     {@ItalicEntry, @ItalicName, 0},
     {@BoldEntry, @BoldName, 0},
     {@PlainTextList, @PlainTextName, 0}
    };
    /* Tools information constants */
    static const GenControlChildInfo TSC_toolList[] = {
     {@PlainTextToolList, TSCTF_PLAIN, GCCF_IS_DIRECTLY_A_FEATURE},
     {@TextStyleToolList, TSCTF_BOLD|TSCTF_ITALIC|TSCTF_UNDERLINE|
     TSCTF_STRIKE_THRU|TSCTF_SUBSCRIPT|TSCTF_SUPERSCRIPT, 0} };
    };
    static const GenControlFeaturesInfo TSC_toolFeaturesList[] = {
     {@SuperscriptToolEntry, @SuperscriptName, 0},
     {@SubscriptToolEntry, @SubscriptName, 0},
     {@StrikeThruToolEntry, @StrikeThruName, 0},
     {@UnderlineToolEntry, @UnderlineName, 0},
     {@ItalicToolEntry, @ItalicName, 0},
     {@BoldToolEntry, @BoldName, 0},
     {@PlainTextToolList, @PlainTextName, 0}
    };
    /* Our constant for the GenControlBuildInfo structure.
     * Fields with a marker to the left of their names are
     * filled in dynamically by the handler following the
     * constant definition. */
    static const  GenControlBuildInfo TSC_dupInfo = {
    GCBF_SUSPEND_ON_APPLY, 					/* GCBI_flags */
     TSC_IniFileKey, 					/* GCBI_initFileKey */
     TSC_gcnList, 					/* GCBI_gcnList */
     ARRAY_LEN(TSC_gcnList,GCNListType), 					/* GCBI_gcnCount */
     TSC_notifyTypeList, 					/* GCBI_notificationList */
     ARRAY_LEN(TSC_notifyTypeList, NotificationType),
                        /* GCBI_notificationCount */
     @TSCName, 					/* GCBI_controllerName */

     /* ## */ NullHandle, 					/* GCBI_dupBlock */
     TSC_childList, 					/* GCBI_childList */
     ARRAY_LEN(TSC_childList, GenControlChildInfo),
                         /* GCBI_childCount */
     TSC_featuresList, 					/* GCBI_featuresList */
     ARRAY_LEN(TSC_featuresList, GenControlFeaturesInfo),
                         /* GCBI_featuresCount */
     TSC_DEFAULT_FEATURES, 					/* GCBI_features */
     /* ## */ NullHandle, 					/* GCBI_toolBlock */
     TSC_toolList, 					/* GCBI_toolList */
     ARRAY_LEN(TSC_toolList, GenControlChildInfo),
                         /* GCBI_toolCount */
     TSC_toolFeaturesList, 					/* GCBI_toolFeaturesList */
     ARRAY_LEN(TSC_toolFeaturesList, GenControlFeaturesInfo),
                         /* GCBI_toolFeaturesCount */
     TSC_DEFAULT_TOOLBOX_FEATURES 					/* GCBI_toolFeatures */
    };
    /* Here is the code that fills in the above missing fields and
     * returns the proper structure. */
    /* Copy the structure containing most of the correct information. */
    memcpy(info, MemLockFixedOrMovable(&TSC_dupInfo), sizeof(GenControlBuildInfo));
    MemUnlockFixedOrMovable(&TSC_dupInfo);
    /* Fill the remaining fields in manually. */
    info->GCBI_dupBlock = HandleOf(@PlainTextList);
    info->GCBI_toolBlock = HandleOf(@PlainTextToolList);
}
| Up: GEOS SDK TechDocs | Up | Prev: 4.1 GenControlClass Instance Data | Next: 4.3 Advanced GenControlClass Usage


Advanced GenControlClass Usage

Generic UI Controllers: 4.3 Creating Your Own Controllers: Advanced GenControlClass Usage

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Subclassing GenControlClass | Next: 5 GenToolControlClass

Everything you need to create a basic custom controller class is detailed in the previous sections. GenControlClass , however, has a number of messages and structures that will be used by some subclasses, though this will be relatively rare. This section details these messages and structures.

Adding and Removing UI Gadgetry

MSG_GEN_CONTROL_GENERATE_UI, MSG_GEN_CONTROL_DESTROY_UI, MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI, MSG_GEN_CONTROL_DESTROY_TOOLBOX_UI, MSG_GEN_CONTROL_UPDATE_UI, MSG_GEN_CONTROL_ENABLE_DISABLE, MSG_GEN_CONTROL_ADD_APP_UI, MSG_GEN_CONTROL_ADD_APP_TOOLBOX_UI, MSG_GEN_CONTROL_REBUILD_NORMAL_UI, MSG_GEN_CONTROL_REBUILD_TOOLBOX_UI

GenControlClass has several messages that add, remove, and manipulate the controller UI gadgetry. Keep in mind also that, as a subclass of GenInteractionClass and thereby of GenClass , the GenControl also can use any of the generic UI messages for object tree manipulation. These messages are detailed in the following list.

MSG_GEN_CONTROL_GENERATE_UI

void	MSG_GEN_CONTROL_GENERATE_UI();

This message generates the UI gadgetry for the controller.

Source: Unrestricted--sent by self as part of specific UI build.

Destination: Any GenControl object

Interception: If subclassed to add functionality, the subclass must call the superclass at the beginning of the handler.

Warnings: If you intercept this message, you should also intercept MSG_GEN_CONTROL_DESTROY_UI , below.

MSG_GEN_CONTROL_DESTROY_UI

void	MSG_GEN_CONTROL_DESTROY_UI();

This message destroys the UI gadgetry for the controller.

Source: Unrestricted--sent by self when being taken off the screen.

Destination: Any GenControl object

Interception: Any subclass that intercepts MSG_GEN_CONTROL_GENERATE_UI must intercept this. The subclass must call the superclass at the end of the handler.

MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI

void	MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI(
        optr	parent);

This message generates the UI gadgetry for the controller's tool box.

Source: Sent by tool box object via the active list as part of its specific UI build mechanism.

Destination: The GenControl object that owns the tool box.

Parameters: parent The optr of GenInteraction that will be the parent of the controller's tools.

Return: Nothing.

Interception: A subclass may intercept this to add UI gadgetry to the toolbox when the toolbox is built. The subclass must call the superclass at the beginning of the handler.

Warnings: Any controller that subclasses this message must also subclass MSG_GEN_CONTROL_DESTROY_TOOLBOX_UI as well.

MSG_GEN_CONTROL_DESTROY_TOOLBOX_UI

void	MSG_GEN_CONTROL_DESTROY_TOOLBOX_UI();

This message destroys all toolbox UI associated with the controller.

Source: Sent by the tool box being destroyed as part of its specific UI destruction mechanism.

Destination: The GenControl object that owns the tool box.

Interception: Any controller class that subclasses the message MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI must also subclass this message. The subclass must call its superclass at the end of its handler.

MSG_GEN_CONTROL_UPDATE_UI

void	MSG_GEN_CONTROL_UPDATE_UI(@stack
        MemHandle		toolBlock,
        MemHandle		childBlock,
        WordFlags		toolboxFeatures,
        WordFlags		features,
        MemHandle		data,
        word		changeID,
        ManufacturerID		manufID);

This message updates all UI components for the recipient controller.

Source: Sent by GenControlClass in its default handler for MSG_META_NOTIFY_WITH_DATA_BLOCK.

Destination: The GenControl object being updated.

Parameters: toolBlock Handle of the object resource block containing the controller's tool UI gadgetry.

childBlock
Handle of the object resource block containing the controller's default UI gadgetry. This may be taken from the TEMP_GEN_CONTROL_INSTANCE vardata field.
toolboxFeatures
A record of flags indicating which tools are currently on for the controller. This may be taken from the TEMP_GEN_CONTROL_INSTANCE vardata field. If the toolbox UI is not interactible, then this field will be zero.
features
A record of flags indicating which features are currently on for the controller. This may be taken from the TEMP_GEN_CONTROL_INSTANCE vardata field. If the UI is not interactible, then this field will be zero.
data
The data block handle passed with the notification message.
changeID
The type of update to undergo, as passed with the notification message.
manufID
The manufacturer ID of the notification type passed with the notification message.

Interception: Controllers should intercept this message in order to properly update their UI gadgetry. There is no default handler for this message.

Structures: The TEMP_GEN_CONTROL_INSTANCE vardata field is of type TempGenControlInstance , which has the following structure:

  typedef struct {
    GenControlInteractableFlags
                TGCI_interactableFlags;
    MemHandle			TGCI_childBlock;
    MemHandle			TGCI_toolBlock;
    optr			TGCI_toolParent;
    WordFlags			TGCI_features;
    WordFlags			TGCI_toolboxFeatures;
    GCNListType			TGCI_activeNotificationList;
    GenControlInteractableFlags TGCI_upToDate;
} TempGenControlInstance;
TGCI_interactableFlags
This record describes which, if any, of a controller's UI parts is interactable (visible and usable). Its flags are listed below.
TGCI_childBlock
The handle of the resource block containing the controller's default UI objects.
TGCI_toolBlock
The handle of the resource block containing the tool objects.
TGCI_toolParent
The optr of the object passed with MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI , if any (tools will be added to this object).
TGCI_features
A record of flags indicating which controller features are currently active.
TGCI_toolboxFeatures
A record of flags indicating which controller tools are currently active.
TGCI_activeNotificationList
The notification type currently active.
TGCI_upToDate
The status of GenControlInteractableFlags at the time of the last notification; that is, which portions of the controller's UI were up to date. This is used by the specific UI for drawing optimizations.

The flags for the GenControlInteractableFlags record are listed below:

GCIF_CONTROLLER
This flag indicates the controller object is interactable.
GCIF_TOOLBOX_UI
This flag indicates that some or all of the controller's tool gadgetry is interactable.
GCIF_NORMAL_UI
This flag indicates that some or all of the controller's normal feature gadgetry is interactable.

MSG_GEN_CONTROL_ENABLE_DISABLE

void	MSG_GEN_CONTROL_ENABLE_DISABLE(
        Message		msg;
        VisUpdateMode		updateMode);

This message enables or disables the controller object as well as its default and tool box UI gadgetry.

Source: Sent by GenControlClass to the controller object when it receives MSG_META_NOTIFY_WITH_DATA_BLOCK .

Destination: Sent by controller to itself.

Parameters: msg Either MSG_GEN_SET_ENABLED or MSG_GEN_SET_NOT_ENABLED as appropriate.

updateMode
A VisUpdateMode indicating when the visual update should occur.

Interception: Unlikely--typically should not be intercepted.

MSG_GEN_CONTROL_ADD_APP_UI

void	MSG_GEN_CONTROL_ADD_APP_UI(
        optr	appUI);

This message adds the passed UI object to the controller's generic tree as if it had been originally defined in the default child block. By default, the new object is added as the last child of the controller.

Source: Unrestricted--generated as part of the default functionality of MSG_GEN_CONTROL_GENERATE_UI if the controller has ATTR_GEN_CONTROL_APP_UI set.

Destination: Any GenControl object.

Parameters: appUI The optr of the object to be added.

Interception: Should be intercepted by controllers that wish to add the new object somewhere other than as the last child.

MSG_GEN_CONTROL_ADD_APP_TOOLBOX_UI

void	MSG_GEN_CONTROL_ADD_APP_TOOLBOX_UI(
        optr	appUI);

This message adds the passed UI object to the controller's tool UI gadgetry as if it had been defined as a tool in the tool resource block. By default, the new object is added as the last child of the controller.

Source: Unrestricted--generated as part of the default functionality of MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI if the controller has ATTR_GEN_CONTROL_APP_TOOLBOX_UI set.

Destination: Any GenControl object.

Parameters: appUI The optr of the object to be added.

Interception: Should be intercepted by controllers that wish to add the new object somewhere other than as the last child.

MSG_GEN_CONTROL_REBUILD_NORMAL_UI

void	MSG_GEN_CONTROL_REBUILD_NORMAL_UI();

This message forces the normal UI of the controller to be rebuilt; each component will be visually destroyed and rebuilt.

Source: Unrestricted.

Destination: Any GenControl object.

Interception: Should not be intercepted.

MSG_GEN_CONTROL_REBUILD_TOOLBOX_UI

void	MSG_GEN_CONTROL_REBUILD_TOOLBOX_UI();

This message forces the toolbox UI of the controller to be rebuilt; each component will be visually destroyed and rebuilt.

Source: Unrestricted.

Destination: Any GenControl object.

Interception: Should not be intercepted.

Adding and Removing Features and Tools

MSG_GEN_CONTROL_SCAN_FEATURE_HINTS, MSG_GEN_CONTROL_ADD_FEATURE, MSG_GEN_CONTROL_REMOVE_FEATURE, MSG_GEN_CONTROL_ADD_TOOLBOX_FEATURE, MSG_GEN_CONTROL_REMOVE_TOOLBOX_FEATURE, MSG_GEN_CONTROL_GET_NORMAL_FEATURES, MSG_GEN_CONTROL_GET_TOOLBOX_FEATURES

Besides being able to set and clear features of a given controller, both the controller class and outside agents can dynamically alter the features of a given controller object. GenControlClass has several messages that you can use for this purpose; these messages are detailed below.

MSG_GEN_CONTROL_SCAN_FEATURE_HINTS

void	MSG_GEN_CONTROL_SCAN_FEATURE_HINTS(
        GenControlUIType		type,
        GenControlScanInfo		*info);

This message scans the feature hints set for the controller object to set the required and prohibited features.

Source: Unrestricted--Sent by GenControlClass to itself in numerous circumstances.

Destination: The GenControl object to be scanned.

Parameters: type An indicator whether the normal or tool box UI hints are to be scanned. This should be GCUIT_NORMAL for the normal hints, GCUIT_TOOLBOX for the tool box hints.

info
A pointer to an empty GenControlScanInfo structure to be filled in by the handler. This structure is shown below.

Return: No value is returned directly.

info
The pointer to the GenControlScanInfo structure filled in by the method.

Interception: Should not be intercepted.

Structures: The GenControlScanInfo structure has the following fields:

typedef struct {
    WordFlags		GCSI_userAdded;
    WordFlags		GCSI_userRemoved;
    WordFlags		GCSI_appRequired;
    WordFlags		GCSI_appProhibited;
} GenControlScanInfo;
GCSI_userAdded
A record of features added by the user through the use of the GenToolControl object. This record also reflects the use of MSG_GEN_CONTROL_ADD_FEATURE .
GCSI_userRemoved
A record of features removed by the user through the use of the GenToolControl object.
GCSI_appRequired
A record of features required; this is set for the controller with ATTR_GEN_CONTROL_REQUIRE_TOOLBOX_UI or ATTR_GEN_CONTROL_REQUIRE_UI .
GCSI_appProhibited
A record of features prohibited; this is set for the controller with ATTR_GEN_CONTROL_PROHIBIT_TOOLBOX_UI or ATTR_GEN_CONTROL_PROHIBIT_UI .

MSG_GEN_CONTROL_ADD_FEATURE

void	MSG_GEN_CONTROL_ADD_FEATURE(
        WordFlags featureToAdd);

This message adds a feature to those currently supported by the controller. This is used for the default settings only, not for tool box UI. The controller is destroyed and then rebuilt with the new feature, causing it to be marked for saving to the state file in the new configuration.

Source: Unrestricted.

Destination: Any GenControl object.

Parameters: featureToAdd A feature record with the flag of the feature to be added set.

Interception: Should not be intercepted.

MSG_GEN_CONTROL_REMOVE_FEATURE

void	MSG_GEN_CONTROL_REMOVE_FEATURE
        WordFlags featureToRemove);

This message removes a feature from those currently supported by the controller. It does not affect the active tools. The controller will be marked for saving to the state file in the new configuration; it is destroyed and rebuilt with feature removed.

Source: Unrestricted.

Destination: Any GenControl object.

Parameters: featureToRemove A feature record with the flag of the feature to be removed set.

Interception: Should not be intercepted.

MSG_GEN_CONTROL_ADD_TOOLBOX_FEATURE

void	MSG_GEN_CONTROL_ADD_TOOLBOX_FEATURE(
        WordFlags featureToAdd);

This message adds a tool to those currently supported by the controller. This is used for the tool box settings only, not for the default UI features. The controller is destroyed and then rebuilt with the new tool, causing it to be marked for saving to the state file in the new configuration.

Source: Unrestricted.

Destination: Any GenControl object.

Parameters: featureToAdd A feature record with the flag of the to be added set.

Interception: Should not be intercepted.

MSG_GEN_CONTROL_REMOVE_TOOLBOX_FEATURE

void	MSG_GEN_CONTROL_REMOVE_TOOLBOX_FEATURE(
        WordFlags featureToRemove);

This message removes a tool from those currently supported by the controller. It does not affect the default active feature list. The controller will be marked for saving to the state file in the new configuration; it is destroyed and rebuilt with the tool removed.

Source: Unrestricted.

Destination: Any GenControl object.

Parameters: featureToRemove A feature record with the flag of the tool to be removed set.

Interception: Should not be intercepted.

MSG_GEN_CONTROL_GET_NORMAL_FEATURES

void MSG_GEN_CONTROL_GET_NORMAL_FEATURES(
        GenControlGetFeaturesReturn *return);

This message returns a structure indicating which of the default features of the controller are currently active, which are required, and which are prohibited.

Source: Unrestricted--typically GenToolControl.

Destination: Any GenControl object.

Parameters: return A pointer to an empty structure to be returned.

Return: The return parameter, upon return, points to a filled GenControlGetFeaturesReturn structure.

Structures: The GenControlGetFeaturesReturn structure is shown below:

typedef struct {
    WordFlags		GCSR_features;
    WordFlags		GCSR_required;
    WordFlags		GCSR_prohibited;
    WordFlags		GCSR_supported;
} GenControlGetFeaturesReturn;
GCSR_features
A record of flags indicating which features are currently active.
GCSR_required
A record of flags indicating which features, at the least, are required for the controller.
GCSR_prohibited
A record of flags indicating which features are prohibited to the controller.
GCSR_supported
A record of flags indicating the full range of supported features.

MSG_GEN_CONTROL_GET_TOOLBOX_FEATURES

void MSG_GEN_CONTROL_GET_TOOLBOX_FEATURES(
        GenControlGetFeaturesReturn *return);

This message returns a structure indicating which of the controller's tools are currently active, which are required, and which are prohibited.

Source: Unrestricted--typically GenToolControl.

Destination: Any GenControl object.

Parameters: return A pointer to an empty structure to be returned.

Return: The return parameter, upon return, points to a filled GenControlGetFeaturesReturn structure.

Structures: The GenControlGetFeaturesReturn structure is shown in MSG_GEN_CONTROL_GET_NORMAL_FEATURES , above.

Working with GCN Lists

MSG_GEN_CONTROL_ADD_TO_GCN_LISTS, MSG_GEN_CONTROL_REMOVE_FROM_GCN_LISTS

Because GenControlClass uses GCN lists, it has two messages that add itself to and remove itself from the lists. You can intercept or send these if you need to; applications generally will not, however. These two messages are detailed below.

MSG_GEN_CONTROL_ADD_TO_GCN_LISTS

void	MSG_GEN_CONTROL_ADD_TO_GCN_LISTS();

This message adds the controller object to the GCN lists specified by its return value of MSG_GEN_CONTROL_GET_INFO . It will force a status update to ensure that the controller updates itself.

Source: Generated internally on startup and at other times.

Destination: Sent to self.

Interception: Generally not intercepted. Subclasses should use this only to see when the controller is being added to its lists; it should call the superclass to do the actual addition.

MSG_GEN_CONTROL_REMOVE_FROM_GCN_LISTS

void	MSG_GEN_CONTROL_REMOVE_FROM_GCN_LISTS();

This message removes the controller from all its GCN lists.

Source: Generated internally on shutdown and at other times.

Destination: Sent to self.

Interception: Generally not intercepted. Subclasses should use this only to see when the controller is being removed from its lists; it should call the superclass to do the actual removal.

Working with Controller Visibility

MSG_GEN_CONTROL_NOTIFY_INTERACTABLE, MSG_GEN_CONTROL_NOTIFY_NOT_INTERACTABLE, MSG_GEN_CONTROL_UNBUILD_NORMAL_UI_IF_POSSIBLE

Because controllers are generic objects which may or may not be interactable, GenControlClass has two messages to handle the interactable state. It has an additional message to close the normal UI tools, if possible. These three messages are detailed below.

MSG_GEN_CONTROL_NOTIFY_INTERACTABLE

void	MSG_GEN_CONTROL_NOTIFY_INTERACTABLE(
        GenControlInteractableFlags flags);

This message instructs the controller to take any actions necessary before being put on the screen and made interactable. It causes the controller to add itself to its GCN lists (if it wasn't already) and take any final steps to get ready to go on the screen.

Source: Generated Internally--do not generate externally.

Destination: Sent to self.

Parameters: flags A single record of flags indicating which portions of the controller will be made interactable.

Interception: May be intercepted to circumvent default behavior.

MSG_GEN_CONTROL_NOTIFY_NOT_INTERACTABLE

void	MSG_GEN_CONTROL_NOTIFY_NOT_INTERACTABLE(
        GenControlInteractableFlags flags);

This message indicates that the UI has determined that the controller can no longer be seen or interacted with by the user. The default behavior of the controller is to remove itself from its GCN lists for optimization.

Source: Generated Internally--do not generate externally.

Destination: Sent to self.

Parameters: flags A single record of flags indicating which portions of the controller are no longer interactable.

Interception: May be intercepted to circumvent default behavior.

MSG_GEN_CONTROL_UNBUILD_NORMAL_UI_IF_POSSIBLE

void	MSG_GEN_CONTROL_UNBUILD_NORMAL_UI_IF_POSSIBLE();

This internal message requests that the normal default controller UI gadgetry be removed and destroyed if they are not in use. This is an optimization intended to save swap space and perhaps memory space; it causes the objects to be loaded back in again when needed, however. Thus, the optimization is only suited to machines with limited swap space (it originated due to these conditions on palmtop machines).

Source: Generated internally. Do not generate externally.

Destination: Sent to self.

Interception: May be intercepted to avoid the optimization. May also need to be intercepted if a subclass creates its own UI gadgetry by intercepting MSG_GEN_CONTROL_GENERATE_UI .

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Subclassing GenControlClass | Next: 5 GenToolControlClass


GenToolControlClass

Generic UI Controllers: 5 GenToolControlClass

| Up: GEOS SDK TechDocs | Up | Prev: 4 Creating Your Own Controllers | Next: 6 GenToolGroupClass

GenToolControlClass is a subclass of GenControlClass ; the tool control allows the user to manipulate and manage all the other controllers in an application and save the configuration he or she sets up.

Previous sections of this chapter explain how to include a GenToolControl object in your application; this section details the specific structures, messages, and internals of GenToolControlClass . Most application programmers will never need to know this information, though some library programmers will want to read the next section.

GenToolControlClass has two instance fields beyond those inherited from GenControlClass . One field is the chunk handle of the Tool Location Table, and the other is the chunk handle of the Tool Group List, both defined below:

@instance ChunkHandle				 GTCI_toolboxList;
@instance ChunkHandle				 GTCI_toolGroupList;
    @default GI_states = @default | GS_ENABLED;

The Tool Location Table must be set up for any application that uses a GenToolControl. The structure of this table is given in Using Tools . The total number of allowable entries in the Tool Location Table is MAX_NUM_TOOLBOXES, which is 25. Because the GenToolControl uses one of these entries, you can have at most 24 other locations for a controller's tools to appear.

The Tool Group List is a list of all the GenToolGroup objects associated with controllers in the application. Each controller may have exactly one GenToolGroup object for managing its tools, and all the ToolGroups must appear in this list if the GenToolControl is to manage them. The Tool Group List is described in Using Tools .

GenToolControlClass also uses a vardata field to store temporary information about a particular controller; the tool control uses this information when presenting the user with options of where the controller's features may be placed. This vardata field is shown below (both the definition of the field and the structure it uses):

@vardata TempGenToolControlInstance
             TEMP_GEN_TOOL_CONTROL_INSTANCE;
  typedef struct {
    optr		TGTCI_curController;
    word		TGTCI_features;
    word		TGTCI_required;
    word		TGTCI_allowed;
}  TempGenToolControlInstance;

The individual fields of the temporary structure are described below.

TGTCI_curController
The optr of the current controller whose tool options are being presented to the user. The GenToolControl allows the user to select and edit the features and location of this controller. If the user selects a new controller for editing, this field will be changed to the new controller's optr.
TGTCI_features
The mask of the currently active features of the controller.
TGTCI_required
The mask of the features which the controller object has defined as required--these must always be active and can not be "hidden" by the user. This mask is set by the controller with ATTR_GEN_CONTROL_REQUIRE_TOOLBOX_UI .
TGTCI_allowed
The mask of features that are allowed by both the controller and the application. Features set in this mask but not in TGTCI_features will be implemented but will be "hidden" from the user. Features not in either list will not be implemented by the controller for the application.

The GenToolControl uses a number of internal messages that should not be intercepted. You can, however, use the data structures of GenToolControlClass in a subclass and add functionality to the tool controller.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Creating Your Own Controllers | Next: 6 GenToolGroupClass


GenToolGroupClass

Generic UI Controllers: 6 GenToolGroupClass

| Up: GEOS SDK TechDocs | Up | Prev: 5 GenToolControlClass | Next: 7 Other Controllers

The GenToolGroup object is used to group tools for an individual controller. It is highly unlikely you will ever need to subclass or interact directly with GenToolGroupClass , though you will often use GenToolControl objects.

GenToolGroupClass is subclassed from GenInteractionClass because its primary functions are grouping and geometry management. Each GenControl object in your application should have exactly one GenToolGroup associated with it, and all the GenToolGroups should appear in the Tool Group List of the GenToolControl.

GenToolGroupClass has one instance data field in addition to those inherited from GenInteractionClass :

@instance optr			 GTGI_controller;
    @default GI_states = (@default & ~GS_ENABLED);

This field contains the optr of the GenControl object whose tools are managed by this GenToolGroup. This field is nearly always set in your .goc file.

The GenToolGroup also has a temporary vardata field that indicates the color in which the tool group should be highlighted; this allows the user to better see which set of tools is being "discussed" (e.g. which set is currently being manipulated by the GenToolControl).

@vardata Color  TEMP_TOOL_GROUP_HIGHLIGHT;

To set the highlight type, GenToolGroupClass has a single message. MSG_GEN_TOOL_GROUP_SET_HIGHLIGHT sets the color of the group's highlight; if the group should not be highlighted, the color should be set to -1.

MSG_GEN_TOOL_GROUP_SET_HIGHLIGHT
void	MSG_GEN_TOOL_GROUP_SET_HIGHLIGHT(
        ToolGroupHighlightType			hlType);

This message sets the highlight color of the tool group. This is used almost exclusively by the GenToolControl.

Source: GenToolControl object; can also be sent by others.

Destination: The GenToolGroup whose highlight color is to be set.

Parameters: hlType The new highlight type to be used by the tool group. The values are TGHT_INACTIVE_HIGHLIGHT, TGHT_ACTIVE_HIGHLIGHT, and TGHT_NO_HIGHLIGHT.

Return: Nothing.

Interception: Should not be intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5 GenToolControlClass | Next: 7 Other Controllers


Other Controllers

Generic UI Controllers: 7 Other Controllers

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 GenToolGroupClass | Next: 7.1 ColorSelectorClass

Most system objects provide their own controllers; those controllers are documented along with the objects they control. Some controllers, however, may be used by more than one system object; these are documented here. The three classes below are ColorSelectorClass , GenPageControlClass , and StyleSheetControlClass .

ColorSelectorClass

GenPageControlClass

The Float Format Controller

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 GenToolGroupClass | Next: 7.1 ColorSelectorClass


ColorSelectorClass

Generic UI Controllers: 7.1 Other Controllers: ColorSelectorClass

| Up: GEOS SDK TechDocs | Up | Prev: 7 Other Controllers | Next: 7.2 GenPageControlClass

The ColorSelector controller provides all the UI necessary to allow the user to select a color, a draw mask, and a draw pattern. The color may be selected either as an index or an RGB value. The ColorSelector must be put on the GenApplication's GAGCNLT_SELF_LOAD_OPTIONS GCN list.

The ColorSelector has the following features:

  typedef WordFlags CSFeatures;
#define CSF_FILLED_LIST				0x10
#define CSF_INDEX				0x08
#define CSF_RGB				0x04
#define CSF_DRAW_MASK				0x02
#define CSF_PATTERN				0x01
#define CS_DEFAULT_FEATURES					(CSF_FILLED_LIST |
        CSF_INDEX | CSF_RGB | CSF_DRAW_MASK |
        CSF_PATTERN)

In addition, the ColorSelector has several instance fields, all shown below:

 @instance ColorQuad				CSI_color = {0, 0, 0, 0};
        /* currently selected color */
@instance byte				CSI_colorIndeterminate;
        /* true if color is indeterminate */
@instance SystemDrawMask CSI_drawMask = SDM_0;
        /* draw mask in use by the color */
@instance byte				CSI_drawMaskIndeterminate;
        /* true if mask is indeterminate */
@instance GraphicPattern CSI_pattern = {0, 0};
        /* pattern in use by the color */
@instance byte				CSI_patternIndeterminate;
        /* true if pattern indeterminate */
@instance ColorModifiedStates CSI_states = 0;
        /* indicates which aspects have changed:
         * CMS_COLOR_CHANGED
         * CMS_DRAW_MASK_CHANGED
         * CMS_PATTERN_CHANGED */
@instance ColorToolboxPreferences CSI_toolboxPrefs
                        = CTP_IS_POPUP;
        /* preferences for color selector:
         * CTP_INDEX_ORIENTATION
         * CTP_DRAW_MASK_ORIENTATION
         * CTP_PATTERN_ORIENTATION
         * CTP_IS_POPUP */
@vardata optr ATTR_COLOR_SELECTOR_DISABLE_OBJECT;
        /* when color selector is disabled, the
         * object named will also be disabled */

Messages Sent Out by the ColorSelector

MSG_META_COLORED_OBJECT_SET_COLOR, MSG_META_COLORED_OBJECT_SET_DRAW_MASK, MSG_META_COLORED_OBJECT_SET_PATTERN

The ColorSelector sends out three messages to its data objects. These messages each serve to set a particular attribute of the object it currently controls (the target object). The target object must be able to handle these messages if it is to use the ColorSelector.

MSG_META_COLORED_OBJECT_SET_COLOR

void	MSG_META_COLORED_OBJECT_SET_COLOR(
        ColorQuad color);

This message notifies the controlled object that it should set its color to the passed value.

Source: ColorSelector object.

Destination: The current Target object.

Parameters: color The ColorQuad structure describing the color to be set.

Return: Nothing.

Interception: Must be intercepted by the controlled object if it is to work with the ColorSelector controller.

MSG_META_COLORED_OBJECT_SET_DRAW_MASK

void	MSG_META_COLORED_OBJECT_SET_DRAW_MASK(
        SystemDrawMask mask);

This message notifies the controlled object that it should set its draw mask to the passed value.

Source: ColorSelector object.

Destination: The current Target object.

Parameters: mask The SystemDrawMask to be set as the object's draw mask.

Return: Nothing.

Interception: Must be intercepted by the controlled object if it is to work with the ColorSelector controller.

MSG_META_COLORED_OBJECT_SET_PATTERN

void	MSG_META_COLORED_OBJECT_SET_PATTERN(
        GraphicPattern pattern);

This message notifies the controlled object that it should sets its draw pattern to that passed.

Source: ColorSelector object.

Destination: The current Target object.

Parameters: pattern The GraphicPattern to be set as the object's draw pattern.

Return: Nothing.

Interception: Must be intercepted by the controlled object if it is to work with the ColorSelector controller.

Messages Handled by ColorSelectorClass

MSG_COLOR_SELECTOR_GET_COLOR, MSG_COLOR_SELECTOR_SET_COLOR, MSG_COLOR_SELECTOR_GET_DRAW_MASK, MSG_COLOR_SELECTOR_SET_DRAW_MASK, MSG_COLOR_SELECTOR_GET_PATTERN, MSG_COLOR_SELECTOR_SET_PATTERN

The ColorSelector handles a number of messages including the normal GenControlClass messages it inherits. These messages allow the setting or retrieval of the instance data of the controller.

MSG_COLOR_SELECTOR_GET_COLOR

void	MSG_COLOR_SELECTOR_GET_COLOR(
        ColorQuad		*retVal);

This message retrieves the controller's CSI_color field.

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: retVal A pointer to an empty ColorQuad structure.

Return: The ColorQuad structure pointed to by retVal will be the color set in CSI_color .

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_SET_COLOR

void	MSG_COLOR_SELECTOR_SET_COLOR(
        ColorQuad color);

This message sets the color in CSI_color .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: color The ColorQuad structure representing the new color for the CSI_color field.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_GET_DRAW_MASK

SystemDrawMask MSG_COLOR_SELECTOR_GET_DRAW_MASK();

This message retrieves the draw mask set in CSI_drawMask .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: None.

Return: The SystemDrawMask set in CSI_drawMask .

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_SET_DRAW_MASK

void	MSG_COLOR_SELECTOR_SET_DRAW_MASK(
        SystemDrawMask mask);

This message sets the draw mask stored in CSI_drawMask .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: mask The new SystemDrawMask to be set into CSI_drawMask .

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_GET_PATTERN

GraphicPattern MSG_COLOR_SELECTOR_GET_PATTERN();

This message returns the pattern set in CSI_pattern .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: None.

Return: The GraphicPattern currently set in CSI_pattern .

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_SET_PATTERN

void	MSG_COLOR_SELECTOR_SET_PATTERN(
        GraphicPattern pattern);

This message sets the drawing pattern stored in CSI_pattern .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: pattern The GraphicPattern value to be set.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_UPDATE_COLOR

See colorC.goh

Update the current color in the toolbox and normal UI. This message is normally sent from within a MSG_GEN_CONTROL_UPDATE_UI handler.

Source: Unrestricted--sent in a MSG_GEN_CONTROL_UPDATE_UI handler.

Destination: Any ColorSelector object.

Parameters: A ColorQuad structure and an indeterminate flag.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_APPLY_COLOR

See colorC.goh

Makes the color selector send MSG_META_COLORED_OBJECT_SET_COLOR .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: A Color enumeration value.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_UPDATE_FILLED_STATUS

See colorC.goh

Makes the color selector update the draw mask and associated UI.

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: A SystemDrawMask , an indeterminate flag, and a tools update flag.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_GET_FILLED_MONIKER

See colorC.goh

Returns the VisMoniker that should be used to represent the "do draw" item in the color selector.

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: None.

Return: The optr of the VisMoniker chunk or NullOptr to use the default.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_GET_UNFILLED_MONIKER

See colorC.goh

Returns the VisMoniker that should be used to represent the "don't draw" item in the color selector.

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: None.

Return: The optr of the VisMoniker chunk or NullOptr to use the default.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_UPDATE_DRAW_MASK

See colorC.goh

Update the current draw mask in the toolbox and the UI. This is often sent from within a MSG_GEN_CONTROL_UPDATE_UI handler.

Source: Unrestricted--sent in a MSG_GEN_CONTROL_UPDATE_UI handler.

Destination: Any ColorSelector object.

Parameters: A SystemDrawMask value and an indeterminate flag.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_APPLY_DRAW_MASK

See colorC.goh

Generates MSG_META_COLORED_OBJECT_SET_DRAW_MASK .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: A SystemDrawMask value.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_UPDATE_PATTERN

See colorC.goh

Update the current pattern in the toolbox and the UI. This is often sent from within a MSG_GEN_CONTROL_UPDATE_UI handler.

Source: Unrestricted--sent in a MSG_GEN_CONTROL_UPDATE_UI handler.

Destination: Any ColorSelector object.

Parameters: A GraphicPattern value and an indeterminate flag.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_APPLY_PATTERN

See colorC.goh

Generates MSG_META_COLORED_OBJECT_SET_PATTERN .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: A GraphicPattern value.

Return: Nothing.

Interception: Generally not intercepted.

MSG_CS_SET_FILLED_STATUS

See colorC.goh

Tells the controller to disable itself and set the mask to zero or to re-enable itself and set the mask to 100, based on the passed flag.

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: A SysDrawMask value.

Return: Nothing.

Interception: Generally not intercepted.

MSG_CS_SET_CF_INDEX

See colorC.goh

Sent by the color list to set a color via an index.

Source: The color list.

Destination: Any ColorSelector object.

Parameters: See colorC.goh .

Return: Nothing.

Interception: Generally not intercepted.

MSG_CS_SET_CF_RGB_RED

See colorC.goh

Sent by the color list to set a color's red value.

Source: The color list.

Destination: Any ColorSelector object.

Parameters: see colorC.goh .

Return: Nothing.

Interception: Generally not intercepted.

MSG_CS_SET_CF_RGB_GREEN

See colorC.goh

Sent by the color list to set a color's green value.

Source: The color list.

Destination: Any ColorSelector object.

Parameters: see colorC.goh .

Return: Nothing.

Interception: Generally not intercepted.

MSG_CS_SET_CF_RGB_BLUE

See colorC.goh

Sent by the color list to set a color's blue value.

Source: The color list.

Destination: Any ColorSelector object.

Parameters: see colorC.goh .

Return: Nothing.

Interception: Generally not intercepted.

MSG_CS_SET_DRAW_MASK

See colorC.goh

Sets the draw mask for the color selector.

MSG_CS_SET_PATTERN

See colorC.goh

Sets the pattern for the color selector .

| Up: GEOS SDK TechDocs | Up | Prev: 7 Other Controllers | Next: 7.2 GenPageControlClass


GenPageControlClass

Generic UI Controllers: 7.2 Other Controllers: GenPageControlClass

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 ColorSelectorClass | Next: 7.3 The Float Format Controller

GenPageControlClass provides a controller object that allows the user to go to any page in a page range. This controller provides UI gadgetry for going to a specified page, going to the next page, or going to the previous page.

The GenPageControl must be put on the GAGCNLT_SELF_LOAD_OPTIONS GCN list in the GenApplication object. It responds to the notification type MANUFACTURER_ID_GEOWORKS.GWNT_PAGE_STATE_CHANGE (sent with a NotifyPageStateChange structure) and sends out three messages exported from MetaClass .

GenPageControlClass is subclassed from GenControlClass and has the features and tools shown below. It has none of its own instance data fields.

  typedef WordFlags GPCFeatures;
#define GPCF_GOTO_PAGE					0x0004
#define GPCF_NEXT_PAGE					0x0002
#define GPCF_PREVIOUS_PAGE					0x0001
  typedef WordFlags GPCToolboxFeatures;
#define GPCTF_PREVIOUS_PAGE					0x0004
#define GPCTF_GOTO_PAGE					0x0002
#define GPCTF_NEXT_PAGE					0x0001
#define  GPC_DEFAULT_FEATURES (GPCF_GOTO_PAGE |
        GPCF_NEXT_PAGE | GPCF_PREVIOUS_PAGE)
#define  GPC_DEFAULT_TOOLBOX_FEATURES
        (GPCTF_GOTO_PAGE | GPCTF_NEXT_PAGE |
         GPCTF_PREVIOUS_PAGE)
@default GCI_output = (TO_APP_TARGET);

The messages this controller sends out must be handled by any object controlled by it.

M SG_META_PAGED_OBJECT_GOTO_PAGE

void	MSG_META_PAGED_OBJECT_GOTO_PAGE(
        word	page);

This message causes the paged object to go to the specified page.

Source: The GenPageControl object.

Destination: The target paged object.

Parameters: page The page number to which the recipient should go.

Return: Nothing.

Interception: This message must be intercepted for the paged object to interact properly with the GenPageControl object.

MSG_META_PAGED_OBJECT_NEXT_PAGE

void	MSG_META_PAGED_OBJECT_NEXT_PAGE();

This message indicates that the recipient should go to the next page.

Source: The GenPageControl object.

Destination: The target paged object.

Interception: This message must be intercepted for the paged object to interact properly with the GenPageControl object.

MSG_META_PAGED_OBJECT_PREVIOUS_PAGE

void	MSG_META_PAGED_OBJECT_PREVIOUS_PAGE();

This message indicates that the recipient should go to the previous page.

Source: The GenPageControl object.

Destination: The target paged object.

Interception: This message must be intercepted for the paged object to interact properly with the GenPageControl object.

MSG_PC_GOTO_PAGE

see gPageCC.goh

Causes the page control object to go to the specified page.

Source: Unrestricted.

Destination: The page control object.

Parameters: The page to go to.

Return: Nothing.

Interception: Should not be intercepted.

MSG_PC_NEXT_PAGE

void	MSG_PC_NEXT_PAGE();

Causes the page controller to go to the next page.

Source: Unrestricted.

Destination: The page control object.

Interception: Should not be intercepted.

MSG_PC_PREVIOUS_PAGE

void	MSG_PC_PREVIOUS_PAGE();

Causes the page controller to go to the previous page.

Source: Unrestricted.

Destination: The page control object.

Interception: Should not be intercepted .

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 ColorSelectorClass | Next: 7.3 The Float Format Controller


The Float Format Controller

Generic UI Controllers: 7.3 Other Controllers: The Float Format Controller

| Up: GEOS SDK TechDocs | Up | Prev: 7.2 GenPageControlClass

Including the Float Format controller in your application allows the user to format FP numbers into any of the system-defined formats. The controller also allows the user to define his or her own formats.

The following messages, routines and data structures all manage arrays of system-defined and user-defined formats. If you include a Float Format controller, you will need to intercept these messages and call many of these routines.

Retrieving Parameters of the Current Entry

 FloatFormatGetFormatParamsWithListEntry(),  FloatFormatGetFormatParamsWithToken(),  FloatFormatGetFormatTokenWithName()

The Float Format controller routines use a FormatInfoStruc structure to hold information about a particular format. This structure is convenient to pass around to other float format routines. The Float Format controller contains one instance field, formatInfoStrucHan , that stores the handle to the current FormatInfoStruc , if one is being used by the controller.

Code Display 12-10 FormatInfoStruc

typedef struct {
    /*
     * FIS_signature is used internally for error-checking.
     */
    word		FIS_signature;
    /*
     * These two entries store the user defined format array for the
     * controller to work on. This array is created by FloatFormatInit().
     */
    FileHandle		FIS_userDefFmtArrayFileHan;
    VMBlockHandle 		FIS_userDefFmtArrayBlkHan;
    /*
     * These two entries store the object block and format list chunk that the
     * controller resides in.
     */
    word		FIS_childBlk;
    word		FIS_chooseFmtListChunk;
    /*
     * FIS_features stores the features list of the controller.
     */
    word		FIS_features;
    /*
     * FIS_editFlag is -1 if we are editing a current user-defined entry and 0
     * if we are creating a new user-defined entry.
     */
    byte		FIS_editFlag;
    /* FIS_curSelection stores the current selection in the format list. */
    word		FIS_curSelection;
    /* FIS_curToken stores the token of the selected item in the list. */
    word		FIS_curToken;
    /* FIS_curParams stores the FormatParams of the selected item. */
    FormatParams		FIS_curParams;
} FormatInfoStruc;

FloatFormatGetFormatParamsWithListEntry() fills in a FormatInfoStruc corresponding to the passed list entry position in the Float Format controller's dynamic list. The routine must be passed a FormatInfoStruc with the entry position in FIS_ curSelection already filled in. You must also have the FIS_ userDefFmtArrayFileHan and FIS_ userDefFmtArrayBlkHan filled in properly before calling this routine.

This routine is called whenever the user clicks on a new item, or whenever the dynamic list needs to retrieve new monikers for the list. You will also probably need to use this routine in your application code when intercepting many of the Float Format controller messages.

FloatFormatGetFormatParamsWithToken() fills in the FormatParams of a particular format entry when passed a format's token. The routine must be passed the FormatInfoStruc with FIS_ curToken already filled in. You must also have the FIS_ userDefFmtArrayFileHan and FIS_ userDefFmtArrayBlkHan filled in properly before calling this routine.

FloatFormatGetFormatTokenWithName() returns the format token of a particular format when passed its name (in a FormatInfoStruc ).

Initializing the UI

 MSG_FLOAT_CTRL_UPDATE_UI,  MSG_FLOAT_CTRL_REQUEST_MONIKER,  FloatFormatInit(),  FloatFormatInitFormatList()

FloatFormatInit() initializes a format array to serve as the storage space for user-defined formats. It must be passed the VM file handle to create the array within.

MSG_FLOAT_CTRL_UPDATE_UI is sent to the Float Format controller whenever the controller needs to perform a visual update. Your Float Format controller should intercept this message and call FloatFormatInitFormatList() .

FloatFormatInitFormatList() initializes the Float Format dynamic list whenever the list needs to display a new moniker. It extracts the moniker text of the selected format from the FormatInfoStruc structure. Make sure that this structure has the proper VM file and block handles filled in prior to calling this routine.

MSG_FLOAT_CTRL_REQUEST_MONIKER is sent to the Float Format controller whenever it needs to display the text within the controller's dynamic list. To extract the current format's moniker, intercept this message and call FloatFormatGetFormatParamsWithListEntry() . You can then pass the format's textual name (from FIS_ curParams .FP_ formatName ) to MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT.

Setting Up Selected Formats

 MSG_FLOAT_CTRL_FORMAT_SELECTED,  FloatFormatProcessFormatSelected()

MSG_FLOAT_CTRL_FORMAT_SELECTED is sent to the Float Format controller whenever the user makes a new selection. This message allows your application to set up a new FormatInfoStruc based on the new selection. Your handler for this message needs to call FloatFormatProcessFormatSelected() .

User Defined Format Creation

 MSG_FLOAT_CTRL_USER_DEF_INVOKE,  FloatFormatInvokeUserDefDB(),  MSG_FLOAT_CTRL_USER_DEF_OK,  FloatFormatUserDefOK()

MSG_FLOAT_CTRL_USER_DEF_INVOKE is sent to the Float Format controller whenever the user has defined a new format and wishes to add it to the controller's dynamic list. Your handler for this message should call FloatFormatInvokeUserDefDB() in turn. If the user attempts to create a new format when MAX_FORMATS already exist, the routine will invoke an error box.

MSG_FLOAT_CTRL_USER_DEF_OK is sent to the Float Format controller to check whether the user-defined format is legal or previously duplicated. Your handler for this message should call FloatFormatUserDefOK() to perform this check and apply the user-defined format to the list of format entries if it is successful.

Deleting Formats

 MSG_FLOAT_CTRL_FORMAT_DELETE,  FloatFormatDelete()

MSG_FLOAT_CTRL_FORMAT_DELETE is sent to the Float Format controller when the user attempts to delete a user-defined entry. Your handler for this message needs to call FloatFormatDelete() .

Applying Formats

 MSG_FLOAT_CTRL_FORMAT_APPLY

MSG_FLOAT_CTRL_FORMAT_APPLY is sent to the FloatFormat controller when the user attempts to apply a format selected in the controller to the appropriate text selection. Your handler will need to extract information from the FormatInfoStruc and call the appropriate text formatting routine (e.g. FloatFloatToAsciI() ).

| Up: GEOS SDK TechDocs | Up | Prev: 7.2 GenPageControlClass


GenClass

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenClass Features
| One-page Version of this Chapter for Easy Printing

GenClass provides the basic functionality, via message handlers and instance data, for all objects in the generic object library. All generic objects are subclassed off this master class. Therefore, all generic objects will contain common behavior in the instance fields and message handlers of GenClass . GenClass allows you to create generic trees, manage state data, and attach data such as visual monikers, keyboard accelerators, and even variable-length data to a specific object.

The Gen world, along with the Vis world, contains a large variety of pre-defined objects ready to install into your applications. GenClass itself is not a normally usable object, however. It should not be directly subclassed and used by your application. The functionality within this chapter will normally be accessed within one of GenClass ' subclasses.

All of these pre-defined objects will contain behavior explained in this chapter. The instance data and message handlers described in this chapter may be implemented differently under different specific UIs, but the basic functionality will remain the same. Therefore, it is important to fully understand the concepts explained in this chapter before using any generic object.

1 GenClass Features
2 GenClass Instance Data
2.1 Instance Fields
2.2 Vardata
3 GenClass Basics
3.1 Visual Monikers
3.2 Composite Links
3.3 Keyboard Accelerators
3.4 Attributes
3.5 States
4 Modifying GenClass Instance Data
4.1 Visual Monikers
4.2 Managing Visual Monikers
4.3 Managing Keyboard Accelerators
5 Generic Trees
5.1 Child/Parent Searches
5.2 Manipulating Children Directly
5.3 Branch Construction/Destruction
6 Vardata
6.1 Optional Attributes (ATTRs)
6.2 Hints to the Specific UI
6.3 Dynamically Managing VarData
7 Special Message Passing
7.1 Parent and Child Message Passing
7.2 Generic Upward Queries
7.3 Object-Specific Queries
8 Visual Refreshing
9 Setting Sizes
10 Activation Messages
11 Focus Modifications
12 Navigation Methods
13 Window Positions and Sizes

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenClass Features
| One-page Version of this Chapter for Easy Printing


GenClass Features

GenClass: 1 GenClass Features

| Up: GEOS SDK TechDocs | Up | Prev: GenClass | Next: 2 GenClass Instance Data

GenClass provides the instance fields and message handlers common to all generic objects. You will need to understand and manipulate GenClass operations in almost every generic object you create. GenClass is very powerful and diverse, providing a number of wide-ranging functions within your user interface. GenClass in specific provides the following:

  • an abstract UI providing code common within any specific user interface.
  • an object-oriented generic tree structure, including dynamic creation and destruction of generic UI objects "on the fly."
  • an object-oriented messaging system, including generic upward queries, allowing the UI objects to pass messages without defining a specific destination.
  • pre-instantiation and persistence (state-saving) across sessions.
  • window sizing and positioning with an abstract geometry manager without involved coordinate space computations.
  • a generic visual update mechanism for your application, allowing you to ensure an accurate and visually up-to-date UI.
  • the ability to create custom UI gadgets of your own.

Most importantly, GenClass provides the common functionality that will be accessed by all generic objects from the simplest GenTrigger to the most complex GenView.

| Up: GEOS SDK TechDocs | Up | Prev: GenClass | Next: 2 GenClass Instance Data


GenClass Instance Data

GenClass: 2 GenClass Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 GenClass Features | Next: 3 GenClass Basics

Generic objects, along with visual objects, are the main means through which a user can communicate with an application. All generic objects follow an object-oriented class hierarchy with GenClass acting as their superclass. Therefore, all generic objects will share the attributes and behavior of GenClass , although many may not make use of the entire utility provided.

Instance Fields

Vardata

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 GenClass Features | Next: 3 GenClass Basics


Instance Fields

GenClass: 2.1 GenClass Instance Data: Instance Fields

| Up: GEOS SDK TechDocs | Up | Prev: 2 GenClass Instance Data | Next: 2.2 Vardata

GenClass provides a set of instance fields common to all subclasses. These instance fields are listed in GenClass Instance Data Fields .

Code Display 2-1 GenClass Instance Data Fields

    @instance @link				GI_link;
    @instance @visMoniker				GI_visMoniker;
    @instance @composite				GI_comp;
    @instance @kbdAccelerator				GI_kbdAccelerator;
    @instance GenAttrs				GI_attrs = 0;
    @instance GenStates GI_states = GS_USABLE | GS_ENABLED;
/* GenAttrs store attributes of a generic object. */
typedef ByteFlags GenAttrs;
#define GA_SIGNAL_INTERACTION_COMPLETE					0x80
#define GA_INITIATES_BUSY_STATE					0x40
#define GA_INITIATES_INPUT_HOLD_UP					0x20
#define GA_INITIATES_INPUT_IGNORE					0x10
#define GA_READ_ONLY					0x08
#define GA_KBD_SEARCH_PATH					0x04
#define GA_TARGETABLE					0x02
#define GA_NOTIFY_VISIBILITY					0x01
/* GenStates store the usability states of a generic object. */
typedef ByteFlags GenStates;
#define GS_USABLE			0x80
#define GS_ENABLED			0x40*

GI_visMoniker chooses the title for the generic object, usually added in an appropriate location within the object by the specific UI. The moniker can be text, in the form of a null-terminated text string, or as a graphics string, usually set up separately. For more information on visual monikers, see Visual Monikers .

GI_comp assigns child objects to the current object (the parent). GI_comp takes a list of generic objects separated by commas as arguments in its instance field. It then sets each of those arguments as children of the current object. In the Goc preprocessor, the GI_comp arguments will be used to create a generic tree structure, reassigning present links using both the GI_comp instance field and an internal instance field, GI_link . The GI_link instance field is therefore an elemental instance field for every generic object but is rarely, if ever, accessed directly. For more information, see Composite Links .

GI_kbdAccelerator sets a given keystroke as a keyboard accelerator for the object. Usually, an object is activated in a manner relating to its specific UI implementation. Pressing the accelerator keystroke will activate the object in the same manner as if its default activation action had occurred. The instance field takes a modifier ( alt , shift , ctrl , or combinations of these) and a character (a valid keyboard character) as its arguments. You can further specify the specific keyboard set to use. For more information, see Keyboard Accelerators .

GI_attrs defines how actions performed by the generic object will affect user interface input and output. You may set any, all, or none of these attributes within your object declaration. For more information, see Attributes .

GI_states defines in what state a generic object will be represented. Default states are GS_USABLE and GS_ENABLED. You may set any, all, or none of these states within your object declaration. For more information, see States .

| Up: GEOS SDK TechDocs | Up | Prev: 2 GenClass Instance Data | Next: 2.2 Vardata


Vardata

GenClass: 2.2 GenClass Instance Data: Vardata

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Instance Fields | Next: 3 GenClass Basics

GenClass provides a multitude of pre-defined vardata instance fields. You may add these vardata entries to a generic object directly within its object declaration. Vardata instance fields perform several key roles for a generic object. Specifically, they provide two distinct uses:

  • hints to the specific UI on its implementation of an object. Hints always appear in the form HINT_hintname.
  • an attribute data storage area considered part of an object's state data (which may therefore be saved to state). Vardata storage areas act as attributes attached to an object if they are explicitly declared within the object declaration. These data appear in the form ATTR_attributename. These data act in much the same way as fixed instance fields.

Any of these data may be accessed and altered using object system routines. For more information, see the GEOS Programming chapter.

Vardata Attributes

Vardata attributes may contain the same functionality as other instance fields. Variable attributes, however, will only be allocated space within an object declaration if they are actually declared; instance data fields always take up space within an object, whether used or not. Therefore, vardata attributes are ideal for data that should only take up space if actually used. Vardata attributes are prefaced with ATTR_ . Several ATTRs are included in GenClass . See ATTR vardata fields for a complete list of these vardata attributes.

Code Display 2-2 ATTR vardata fields

@vardata void 			ATTR_GEN_PROPERTY;
@vardata void 			ATTR_GEN_NOT_PROPERTY;
@vardata DestinationClassArgs				ATTR_GEN_DESTINATION_CLASS;
    @reloc ATTR_GEN_DESTINATION_CLASS, 0, optr;
@vardata char[]			ATTR_GEN_INIT_FILE_KEY;
@vardata char[]			ATTR_GEN_INIT_FILE_CATEGORY;
@vardata void			ATTR_GEN_INIT_FILE_PROPAGATE_TO_CHILDREN;
@vardata void			ATTR_GEN_USES_HIERARCHICAL_INIT_FILE_CATEGORY;
@vardata Point			ATTR_GEN_POSITION;
@vardata sword			ATTR_GEN_POSITION_X;
@vardata sword			ATTR_GEN_POSITION_Y;
@vardata void			ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED;
@vardata void			ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_ENABLED;
@vardata dword			ATTR_GEN_VISIBILITY_DATA;
@vardata word			ATTR_GEN_VISIBILITY_MESSAGE;
@vardata optr			ATTR_GEN_VISIBILITY_DESTINATION;
    @reloc ATTR_GEN_VISIBILITY_DESTINATION, 0, optr;
@vardata GenFilePath			ATTR_GEN_PATH_DATA;
@vardata ChunkHandle			ATTR_GEN_FEATURE_LINK;
@vardata GenDefaultMonikerType ATTR_GEN_DEFAULT_MONIKER;
@vardata optr 			ATTR_GEN_OUTPUT_TRAVEL_START;
    @reloc ATTR_GEN_OUTPUT_TRAVEL_START, 0, optr;
/* Generic Help attributes */
@vardata char[]			ATTR_GEN_HELP_FILE;
@vardata byte			ATTR_GEN_HELP_TYPE;
@vardata void			ATTR_GEN_HELP_FILE_FROM_INIT_FILE;
@vardata optr			ATTR_GEN_FOCUS_HELP;
@vardata optr			ATTR_GEN_FOCUS_HELP_LIB;
    @reloc ATTR_GEN_FOCUS_HELP_LIB, 0, optr;
@vardata char[]			ATTR_GEN_HELP_CONTEXT;
/* Generic Window attributes */
@vardata MemHandle			ATTR_GEN_WINDOW_CUSTOM_LAYER_ID;
    @reloc ATTR_GEN_WINDOW_CUSTOM_LAYER_ID, 0, optr;
@vardata WinPriority			ATTR_GEN_WINDOW_CUSTOM_WINDOW_PRIORITY;
@vardata LayerPriority			ATTR_GEN_WINDOW_CUSTOM_LAYER_PRIORITY;
@vardata MemHandle			ATTR_GEN_WINDOW_CUSTOM_PARENT;
@vardata void			ATTR_GEN_CUSTOM_WINDOW;
@vardata void			ATTR_GEN_WINDOW_ACCEPT_INK_EVEN_IF_NOT_FOCUSED;
@vardata KeyboardOverride ATTR_GEN_WINDOW_KBD_OVERRIDE;
@vardata Point			ATTR_GEN_WINDOW_KDB_POSITION;

ATTR_GEN_PROPERTY and ATTR_GEN_NOT_PROPERTY affect a generic object's ability to behave as a properties group. These attributes override the property group's default behavior. For more information, see Generic Properties .

ATTR_GEN_DESTINATION_CLASS specifies an object class that should handle all messages sent out along a destination path. The type DestinationClassArgs contains a pointer to a ClassStruct . Typically, this attribute is used with a TravelOption destination to form a search path. The message for that generic object travels down a path specified in the TravelOption until it encounters an object class of type ATTR_GEN_DESTINATION_CLASS ; at that point, the message will be handled. For more information, see Destination Classes .

ATTR_GEN_INIT_FILE_KEY , ATTR_GEN_INIT_FILE_CATEGORY , ATTR_GEN_INIT_FILE_PROPAGATE_TO_CHILDREN , and ATTR_GEN_USES_HIERARCHICAL_INIT_FILE_CATEGORY allow a generic object to load options from the .INI file upon startup (after receiving a MSG_META_ATTACH ). Generic objects with these attributes will receive a MSG_GEN_LOAD_OPTIONS (with the category and key of the proper entry in the .INI file) and may intercept that message to provide additional behavior. For more information, see Initialization File Management .

ATTR_GEN_POSITION , ATTR_GEN_POSITION_X , and ATTR_GEN_POSITION_Y override default geometry management of an object and set a precise position for the object to appear within the UI, offset from the parent object. ATTR_GEN_POSITION specifies both a horizontal and a vertical position to place the object. ATTR_GEN_POSITION_X and ATTR_GEN_POSITION_Y specify either a horizontal or vertical position to align the object along. You should avoid using these attributes because they override default behavior and may result in different (and potentially unsightly) appearances under different specific UIs. For more information, see Altering Default Geometry Management .

ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED and ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_ENABLED affect generic objects that contain an apply message--a message that an object sends out when it wishes to apply changes made within that object. By default, an apply message will not be sent out if the object's state has not changed since the last apply or if the object is not enabled; these attributes tell the object to send out its apply message regardless of these conditions. For more information, see Altering Delayed Mode Activity .

ATTR_GEN_VISIBILITY_DATA , ATTR_GEN_VISIBILITY_MESSAGE , and ATTR_GEN_VISIBILITY_DESTINATION set the data, message or destination to use with the visibility notification mechanism in GenClass . The visibility mechanism allows an application to know when an object becomes visible and not visible. An object provides this notification if its GA_NOTIFY_VISIBILITY bit is set. For more information, see Notification of Visibility .

ATTR_GEN_PATH_DATA stores an object's generic path information in the form of a GenFilePath structure. This structure contains disk and path information including path name and file name; generic objects can use this attribute to store current or default paths and files. For more information, see Generic Paths .

ATTR_GEN_FEATURE_LINK contains a link to the next feature used by the GenControl mechanism when a controller's features map to multiple objects. It is unlikely you will use this attribute. For more information, see Feature Links .

ATTR_GEN_DEFAULT_MONIKER specifies a default moniker for a generic object. This is generally used for graphic monikers that occur several places within the system. For more information, see Default Monikers .

ATTR_GEN_OUTPUT_TRAVEL_START allows you to set a specific object to begin the TravelOption . Normally, objects that contain a TravelOption in their destination field use their own object as the point of reference for sending messages along travel paths. (I.e sending a message TO_FOCUS would send a message along the focus path, starting at the sending object.) This can be used for optimization purposes, enabling you to skip several layers of hierarchies.

ATTR_GEN_HELP_FILE , ATTR_GEN_HELP_TYPE, ATTR_GEN_HELP_FILE_FROM_INIT_FILE, ATTR_GEN_FOCUS_HELP, ATTR_GEN_FOCUS_HELP_LIB, and ATTR_GEN_HELP_CONTEXT specify the configuration of the help files for a generic object. Focus help starts help notification when the generic object gains the focus. For more information on Help files, see Help Management .

ATTR_GEN_WINDOW_CUSTOM_LAYER_ID , ATTR_GEN_WINDOW_CUSTOM_WINDOW_PRIORITY, ATTR_GEN_WINDOW_CUSTOM_LAYER_PRIORITY, ATTR_GEN_WINDOW_CUSTOM_PARENT, and ATTR_GEN_CUSTOM_WINDOW specify a window's interaction and place within the window system. ATTR_GEN_WINDOW_ACCEPT_INK_EVEN_IF_NOT_FOCUSED, ATTR_GEN_WINDOW_KBD_OVERRIDE, and ATTR_GEN_WINDOW_KBD_POSITION affect how input flows within a generic window. For more information, see Window Management .

Hints

Hints to the specific UI suggest added functionality to your application, and are prefaced with HINT_ (such as HINT_SEEK_REPLY_BAR ). Hints may also have data structures attached to them. There are a large number of pre-defined hints with GenClass . See Hints to the Specific UI for details on the operation of hints.

Many of the hints provided in GenClass manage the geometry of generic objects. For complete information on these hints, see the Managing UI Geometry chapter.

Code Display 2-3 Geometry Hints

 /* These hints are explained within the Geometry Management chapter. */
@vardata void			 HINT_DONT_ALLOW_CHILDREN_TO_WRAP;
@vardata void			 HINT_ALLOW_CHILDREN_TO_WRAP;
@vardata word			 HINT_WRAP_AFTER_CHILD_COUNT;
@vardata word			 HINT_WRAP_AFTER_CHILD_COUNT_IF_VERTICAL_SCREEN;
@vardata void			 HINT_BOTTOM_JUSTIFY_CHILDREN;
@vardata void			 HINT_LEFT_JUSTIFY_CHILDREN;
@vardata void			 HINT_RIGHT_JUSTIFY_CHILDREN;
@vardata void			 HINT_TOP_JUSTIFY_CHILDREN;
@vardata void			 HINT_FULL_JUSTIFY_CHILDREN_HORIZONTALLY;
@vardata void			 HINT_FULL_JUSTIFY_CHILDREN_VERTICALLY;
@vardata void			 HINT_DONT_FULL_JUSTIFY_CHILDREN;
@vardata SpecSizeSpec			 HINT_CUSTOM_CHILD_SPACING;
@vardata SpecSizeSpec			 HINT_CUSTOM_CHILD_SPACING_IF_LIMITED_SPACE;
@vardata void			 HINT_DONT_INCLUDE_ENDS_IN_CHILD_SPACING;
@vardata void			 HINT_INCLUDE_ENDS_IN_CHILD_SPACING;
@vardata void 			 HINT_MINIMIZE_CHILD_SPACING;
@vardata void			 HINT_LEFT_JUSTIFY_MONIKERS;
@vardata void			 HINT_CENTER_MONIKER;
@vardata void			 HINT_DO_NOT_USE_MONIKER;
@vardata void			 HINT_ALIGN_LEFT_MONIKER_EDGE_WITH_CHILD;
@vardata void			 HINT_CENTER_CHILDREN_HORIZONTALLY;
@vardata void			 HINT_CENTER_CHILDREN_ON_MONIKERS;
@vardata void			 HINT_CENTER_CHILDREN_VERTICALLY;
@vardata optr			 HINT_ALIGN_LEFT_EDGE_WITH_OBJECT;
    @reloc HINT_ALIGN_LEFT_EDGE_WITH_OBJECT, 0, optr;
@vardata optr			 HINT_ALIGN_TOP_EDGE_WITH_OBJECT;
    @reloc HINT_ALIGN_TOP_EDGE_WITH_OBJECT, 0, optr;
@vardata optr			 HINT_ALIGN_RIGHT_EDGE_WITH_OBJECT;
    @reloc HINT_ALIGN_RIGHT_EDGE_WITH_OBJECT, 0, optr;
@vardata optr			 HINT_ALIGN_BOTTOM_EDGE_WITH_OBJECT;
    @reloc HINT_ALIGN_BOTTOM_EDGE_WITH_OBJECT, 0, optr;
@vardata CompSizeHintArgs			 HINT_FIXED_SIZE;
@vardata CompSizeHintArgs	 		HINT_INITIAL_SIZE;
@vardata CompSizeHintArgs	 		HINT_MAXIMUM_SIZE;
@vardata CompSizeHintArgs	 		HINT_MINIMUM_SIZE;
@vardata void			 HINT_SIZE_WINDOW_AS_DESIRED;
@vardata void			 HINT_DRAW_IN_BOX;
@vardata void			 HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
@vardata void			 HINT_EXPAND_WIDTH_TO_FIT_PARENT;
@vardata void			 HINT_MAKE_REPLY_BAR;
@vardata void			 HINT_NO_TALLER_THAN_CHILDREN_REQUIRE;
@vardata void			 HINT_NO_WIDER_THAN_CHILDREN_REQUIRE;
@vardata void			HINT_ORIENT_CHILDREN_HORIZONTALLY;
@vardata void			 HINT_ORIENT_CHILDREN_VERTICALLY;
@vardata void			 HINT_ORIENT_CHILDREN_ALONG_LARGER_DIMENSION;
@vardata void			 HINT_SAME_ORIENTATION_AS_PARENT;
@vardata void			 HINT_PLACE_MONIKER_ABOVE;
@vardata void			 HINT_PLACE_MONIKER_BELOW;
@vardata void			 HINT_PLACE_MONIKER_TO_LEFT;
@vardata void			 HINT_PLACE_MONIKER_TO_RIGHT;
@vardata void			 HINT_PLACE_MONIKER_ALONG_LARGER_DIMENSION;
@vardata void			HINT_NO_BORDER_ON_MONIKERS
@vardata void			 HINT_SEEK_MENU_BAR;
@vardata void			 HINT_AVOID_MENU_BAR;
@vardata void			 HINT_SEEK_REPLY_BAR;
@vardata void			 HINT_SEEK_X_SCROLLER_AREA;
@vardata void			 HINT_SEEK_Y_SCROLLER_AREA;
@vardata void			 HINT_SEEK_LEFT_OF_VIEW;
@vardata void			 HINT_SEEK_TOP_OF_VIEW;
@vardata void			 HINT_SEEK_RIGHT_OF_VIEW;
@vardata void			 HINT_SEEK_BOTTOM_OF_VIEW;
@vardata void			 HINT_SEEK_TITLE_BAR_LEFT;
@vardata void			 HINT_SEEK_TITLE_BAR_RIGHT;
@vardata void			 HINT_USE_INITIAL_BOUNDS_WHEN_RESTORED;
@vardata void			 HINT_DIVIDE_WIDTH_EQUALLY;
@vardata void			 HINT_DIVIDE_HEIGHT_EQUALLY;
@vardata void			 HINT_KEEP_INITIALLY_ONSCREEN;
@vardata void			 HINT_DONT_KEEP_INITIALLY_ONSCREEN;
@vardata void			 HINT_KEEP_PARTIALLY_ONSCREEN;
@vardata void			 HINT_DONT_KEEP_PARTIALLY_ONSCREEN;
@vardata void			 HINT_KEEP_ENTIRELY_ONSCREEN;
@vardata void			 HINT_KEEP_ENTIRELY_ONSCREEN_WITH_MARGIN;
@vardata void			 HINT_POPS_UP_TO_RIGHT;
@vardata void			 HINT_POPS_UP_BELOW;
@vardata void			 HINT_NOT_MOVABLE;
@vardata SpecWinSizePair			 HINT_POSITION_WINDOW_AT_RATIO_OF_PARENT;
@vardata void			 HINT_POSITION_WINDOW_AT_MOUSE;
@vardata void			 HINT_WINDOW_NO_TITLE_BAR;
@vardata void			 HINT_WINDOW_NO_SYS_MENU;
@vardata void			HINT_WINDOW_MINIMIZE_TITLE_BAR;
@vardata void			HINT_WINDOW_ALWAYS_DRAW_WITH_FOCUS;
@vardata void			 HINT_STAGGER_WINDOW;
@vardata void			 HINT_CENTER_WINDOW;
@vardata void			 HINT_TILE_WINDOW;
@vardata void			 HINT_EXTEND_WINDOW_TO_BOTTOM_RIGHT;
@vardata void			 HINT_EXTEND_WINDOW_NEAR_BOTTOM_RIGHT;
@vardata SpecWinSizePair			 HINT_SIZE_WINDOW_AS_RATIO_OF_PARENT;
@vardata SpecWinSizePair			 HINT_SIZE_WINDOW_AS_RATIO_OF_FIELD;
@vardata void			 HINT_WINDOW_NO_CONSTRAINTS;
@vardata SpecWinSizePair			 HINT_POSITION_ICON_AS_RATIO_OF_FIELD;

Other hints in GenClass do not affect geometry. These hints are listed below and are explained within this chapter. Within GenClass , some hints are internal and are not listed in this code display.

Code Display 2-4 Hints Explained Within This Chapter

/* These hints are explained within this chapter (or they are self-explanatory). */
@vardata void			 HINT_FREQUENTLY_USED;
@vardata void			 HINT_INFREQUENTLY_USED;
@vardata void			 HINT_AN_ADVANCED_FEATURE;
@vardata void			HINT_DEFAULT_DEFAULT_ACTION;
@vardata void			 HINT_ENSURE_TEMPORARY_DEFAULT;
@vardata void			 HINT_PREVENT_DEFAULT_OVERRIDES;
@vardata void			 HINT_SAME_CATEGORY_AS_PARENT;
@vardata void			 HINT_USE_TEXT_MONIKER;
@vardata void			 HINT_USE_ICONIC_MONIKER;
@vardata void			 HINT_DEFAULT_FOCUS;
@vardata void			 HINT_DEFAULT_TARGET;
@vardata void			 HINT_DEFAULT_MODEL;
@vardata void			 HINT_PRESERVE_FOCUS;
@vardata void			 HINT_DO_NOT_PRESERVE_FOCUS;
@vardata void			 HINT_GENERAL_CONSUMER_MODE;
@vardata void			 HINT_NEVER_ADOPT_MENUS;
@vardata void			 HINT_ALWAYS_ADOPT_MENUS;
@vardata void			 HINT_NAVIGATION_ID;
@vardata void			 HINT_NAVIGATION_NEXT_ID
@vardata void			 HINT_DISMISS_WHEN_DISABLED;
@vardata void			HINT_TOOLBOX;
@vardata void			 HINT_SHOW_SHORTCUT;
@vardata void			 HINT_DONT_SHOW_SHORTCUT;
@vardata void			HINT_DRAW_SHORTCUT_BELOW;
@vardata void			 HINT_CAN_CLIP_MONIKER_WIDTH;
@vardata void			 HINT_CAN_CLIP_MONIKER_HEIGHT;
@vardata void			 HINT_SHOW_ENTIRE_MONIKER;
@vardata BackgroundColors				 HINT_GADGET_BACKGROUND_COLORS;
@vardata word			 HINT_GADGET_TEXT_COLOR;
@vardata SystemAttrs			 HINT_IF_SYSTEM_ATTRS;
@vardata void			 HINT_ENDIF;
@vardata void			 HINT_ELSE;

HINT_FREQUENTLY_USED, HINT_INFREQUENTLY_USED, and HINT_AN_ADVANCED_FEATURE are descriptive hints that describe the generic object's role within the UI. It is, of course, left up to the specific UI to implement any special behavior based on these hints.

HINT_DEFAULT_DEFAULT_ACTION , HINT_ENSURE_TEMPORARY_DEFAULT, and HINT_PREVENT_DEFAULT_OVERRIDES affect the default activation of objects within the UI. Objects with default activation can be activated indirectly (e.g., pressing the ENTER key within a dialog box activates a default trigger). For more information, see Default Actions .

HINT_SAME_CATEGORY_AS_PARENT indicates to the specific UI that this object should be treated in the same manner as its parent. Currently, this hint only affects GenInteractions. By default, GIV_SUB_GROUP GenInteractions within a GIV_POPUP menu have separator lines drawn around them to set them apart from other children. This hint removes those lines.

HINT_USE_TEXT_MONIKER and HINT_USE_ICONIC_MONIKER indicates that the generic object should choose either a text moniker or an iconic moniker if there exists a choice within the object's moniker list. Currently, these hints are not implemented.

HINT_DEFAULT_FOCUS , HINT_DEFAULT_TARGET, and HINT_DEFAULT_MODEL specify that the generic object is the default focus, target, or model node at this level in the application. HINT_PRESERVE_FOCUS and HINT_DO_NOT_PRESERVE_FOCUS affect how the focus node changes after an object's activation. For a complete discussion of the focus and target hierarchies, see the Input chapter.

HINT_GENERAL_CONSUMER_MODE instructs the specific UI to show the object in GCM mode. This hint takes an argument of type GeneralConsumerModeFlags ; this record contains two three-bit flags: GCMF_LEFT_ICON and GCMF_RIGHT_ICON, which specify which icon to use on both the left and right sides of the title bar. Each icon must be of type GCMIcon ; current types are GCMI_NONE, GCMI_EXIT, and GCMI_HELP.

HINT_NEVER_ADOPT_MENUS and HINT_ALWAYS_ADOPT_MENUS affect the behavior of menus placed within a GenDisplay. Menus can either sit within the GenDisplay or appear in the associated GenPrimary when that GenDisplay is the target. Menus within GenDisplays are discouraged, however, as they are a fairly advanced UI metaphor.

HINT_NAVIGATION_ID and HINT_NAVIGATION_NEXT_ID allow the field navigation path within a windowed object. HINT_NAVIGATION_NEXT_ID sets the object to navigate to from the current object. An object with a matching HINT_NAVIGATION_ID must exist. For more information, see Keyboard Navigation Hints .

HINT_DISMISS_WHEN_DISABLED instructs the specific UI to dismiss the object when it is no longer fully enabled. This is typically useful for independently-displayable windows that should be dismissed in such cases.

HINT_TOOLBOX indicates that the generic object and all objects below it in the visual hierarchy are part of a "toolbox" and should draw themselves appropriately. Objects in toolboxes are typically much smaller than other components. For more information on toolboxes, see the Menus and Dialogs chapter.

HINT_SHOW_SHORTCUT , HINT_DONT_SHOW_SHORTCUT, and HINT_DRAW_SHORTCUT_BELOW affect the display of keyboard accelerators for generic objects. For more information, see Keyboard Accelerators .

HINT_CAN_CLIP_MONIKER_WIDTH and HINT_CAN_CLIP_MONIKER_HEIGHT instruct the specific UI to clip the object's moniker height or width if not enough space is provided for them. Objects with HINT_SHOW_ENTIRE_MONIKER will force the object to leave enough space to show its entire moniker, if possible.

HINT_GADGET_BACKGROUND_COLORS causes grouping objects in toolboxes to draw custom background colors. The hint takes an argument of type BackgroundColors . Two colors can each be chosen for both the unselected state and the selected state; they will be blended together depending on the state of the object. If both colors are set to the same value, a solid color results. HINT_GADGET_TEXT_COLOR selects the text color of a visual moniker, typically within a toolbox. This hint takes an argument of type TextColors .

HINT_IF_SYSTEM_ATTRS , HINT_ENDIF, and HINT_ELSE may be used together to conditionally add hints to an object based on certain system criteria. If the SystemAttrs set in the HINT_IF_SYSTEM_ATTRS field are true for the current system, then the hints that follow (until a HINT_ENDIF is encountered) are included. If no HINT_ENDIF is encountered, then only the next hint is included. If the SystemAttrs do not match the current system, the following group of hints is deleted. For more information, see System Attributes .

Remember that these vardata entries are hints; they can be ignored by the specific UI and their implementation varies from object to object. Some hints, for example, are only relevant for certain object classes. Your mileage may vary.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Instance Fields | Next: 3 GenClass Basics


GenClass Basics

GenClass: 3 GenClass Basics

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 GenClass Instance Data | Next: 4 Modifying GenClass Instance Data

In most cases, you will set much of your generic object's instance data upon instantiation according to the values you include in your .goc file. You may also update and manipulate this instance data during execution with a variety of messages. The following section deals only with directly setting instance fields in your .goc file. For information on dynamically altering this instance data, see Modifying GenClass Instance Data .

Visual Monikers

Composite Links

Keyboard Accelerators

Attributes

States

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 GenClass Instance Data | Next: 4 Modifying GenClass Instance Data


Visual Monikers

GenClass: 3.1 GenClass Basics: Visual Monikers

| Up: GEOS SDK TechDocs | Up | Prev: 3 GenClass Basics | Next: 3.2 Composite Links

A visual moniker is a text or graphics string attached to a generic object. This visual moniker displays the given string in a manner relevant to the specific implementation of an object. For example, a GenTrigger might create a button with text inserted as the object's main visual implementation. The visual moniker in this case might cover the object's total area. A GenPrimary, on the other hand, might only add such text in a title bar of the window. In all cases, the specific user interface has final control over how a visual moniker is displayed to the user.

Simple Visual Monikers

You may set the visual moniker of an object using the GI_visMoniker instance field. This instance field expects an argument of type @visMoniker . The visual moniker of an object is not actually stored within this instance field, however. The visual moniker of an object is stored within its own chunk, outside of the object itself. The GI_visMoniker instance field contains a chunk handle to this visual moniker chunk. Because a visual moniker can only be referenced by a chunk handle, visual monikers for an object must reside in the same block as the object that points to them. The UI will set up this chunk and chunk handle automatically.

Within the object's definition, set the GI_visMoniker field to the desired visual moniker (either a text or graphics string). This can be done either by directly setting the GI_visMoniker field to the string or by indirectly setting the field to a visual moniker chunk defined elsewhere within your user interface with the Goc keyword @visMoniker . (See Setting Visual Monikers .)

Code Display 2-5 Setting Visual Monikers

/* A visual moniker can be set directly within the object's definition. */
@object GenTriggerClass MyTrigger = {
    GI_visMoniker = "Push Me";
}
/* A visual moniker may also be defined indirectly within a .goc file. */
/* The data for MyMoniker is set using the Goc keyword @visMoniker. This moniker
 * must reside in the same resource block as the object using it. This moniker
 * should be declared before being used in an object declaration. */
@visMoniker MyMoniker = "Push Me";
@object GenTriggerClass MyTrigger = {
    GI_visMoniker = @MyMoniker;
}

Remember that the GI_visMoniker instance field only stores a chunk handle to the actual string. The visual moniker for the object is not contained in the object chunk itself.

Text monikers may also specify a character to act as a mnemonic. Mnemonics are keyboard shortcuts used to activate an object without use of the mouse. Menus, menu items, and buttons frequently have mnemonic characters attached. This allows a user to navigate quickly to other UI objects or to activate them. If the specified character is within the text moniker, that letter will be highlighted in whatever fashion the specific UI decides is relevant. For instance, in OSF/Motif, mnemonic characters are underlined.

Usually the first letter of the text moniker is used for the mnemonic, though any character may be used. The specific UI will underline the first such occurrence of the character within the text moniker. If the character is not within the text itself, the specific UI may place the character within parentheses at the end of the text moniker. Mnemonics are activated according to specifications in the specific UI. In OSF/Motif, pressing the ALT key puts the user interface into its keyboard navigation mode. In this mode, all mnemonics for objects in the focus are activated by pressing the character of the mnemonic without need of an additional keystroke.

Mnemonics are set by enclosing the desired character in single quotes before the text string in your instance data. Mnemonics are case specific when defined. For example, if a visual moniker is the text "File," only an uppercase mnemonic of F would highlight the first character in the text. Mnemonics are not case specific in their activation methods, however. For example, either alt -f or alt -F will activate a keyboard mnemonic of F.

Mnemonics are only valid for an object if that object is visually displayed and currently has the focus (see the Input chapter). Therefore, you may duplicate mnemonics for objects that will not exist at the same focus level. (An example of objects at the same focus level would be objects within the same menu.) Be careful not to duplicate the same mnemonic within the same focus level, as the UI will only associate the mnemonic with the first object that matches it.

The mnemonic may also store one of the following constants:

VMO_CANCEL corresponds to the specific UI defined cancel mnemonic. Use this rather than a special cancel mnemonic of your own choosing to keep the UI consistent.
VMO_MNEMONIC_NOT_IN_MKR_TEXT indicates that the mnemonic occurs after the null-terminator for the moniker text. This moniker is itself not null-terminated.
VMO_NO_MNEMONIC indicates that this moniker contains no mnemonic.

If you need other functionality that requires keyboard control of some form and mnemonics are not satisfactory, see Keyboard Accelerators .

Code Display 2-6 Setting Mnemonic Characters

/* Mnemonics are case specific. If the following example enclosed f instead of
 * F within single quotes, then the character f in parentheses would follow the
 * text "File." Note that this case specificity does not apply to the user's
 * activation of the object. That is, alt f will activate a mnemonic of F. */
@object MyTrigger GenTriggerClass {
    GI_visMoniker = `F', "File";
}
/* You can also specify the character to highlight with an actual numerical
 * position, counting from a zero-based (1st character is 0, 2nd character is 1,
 * etc.) character position in the text string. The "5" in the following example
 * will underline the F character (the sixth character in the text string.) */
    GI_visMoniker = 5, "Open File";

Graphics String Visual Monikers

The GI_ visMoniker instance field recognizes several keywords. These keywords are listed below and are usually only used in the construction of gstring visual monikers.

  • style = { text , abbrevText , graphicText , icon , tool }
    This keyword specifies the VMStyle in use by this visual moniker.

    text Normal text moniker
    abbrevText Abbreviated text moniker (i.e., a short textual
    description rather than the full title). This is used
    for the name under an icon of an iconified
    GenPrimary.
    graphicText Textual gstring
    icon Normal gstring moniker
    tool Moniker for a tool, usually smaller than a standard
    visual moniker
  • size = { tiny , standard , large , huge }
    This keyword specifies the DisplaySize that this moniker is intended for. It has nothing to do with the actual size of the moniker, which can be set with the keyword `cachedSize.' Each size corresponds to a display type's resolution level.

    tiny Tiny screens: CGA, 256x320.
    standard Standard screens: EGA, VGA, HGC, MCGA
    large Large screens: 800x680 SVGA
    huge Huge screens

    You should not, in general, use monikers whose sizes are larger than their intended display type. (I.e. using `huge' sized monikers on VGA screens.) You may use smaller monikers on larger screens, however.
  • color = { gray1 , gray2 , gray4 , gray8 , color2 , color4 , colorRGB }
    This keyword specifies the color capability of the display that this setting is intended for. Each setting corresponds to a DisplayClass .

    gray1 1 bit/pixel gray scale
    gray2 2 bit/pixel gray scale
    gray4 4 bit/pixel gray scale
    gray8 8 bit/pixel gray scale
    color2 2 bit/pixel color index
    color4 4 bit/pixel color index
    color RGB color with RGB values
  • aspectRatio = { normal , squished , verySquished }
    This keyword specifies the aspect ratio of the display that this moniker is intended for.

    normal VGA, MCGA
    squished EGA, HGC (though normal is usually used)
    verySquished CGA
  • cachedSize = { X , Y }
    This keyword sets the cached size (pixel by pixel) of the moniker.
  • gstring { < GString OpCodes > }
    This keyword stores the gstring operations used in creating the visual moniker. These operations may also specify the creation of a bitmap by using the Bitmap operator.

Code Display 2-7 Examples of GString Visual Monikers

/* A Graphics string consisting of GString opcodes. */
    GI_visMoniker = {
    size = tiny;
    color = color4;
    aspectRatio = normal;
    cachedSize = 15,15;
    gstring{
        GSSaveTransform(),
        GSApplyRotation(45),
        GSFillEllipse(0,0,10,15),
        GSRestoreTransform(),
        GSDrawEllipse(0,0,10,15),
        GSEndString()
    }
    }
/* A Graphics string containing a bitmap. */
@visMoniker MyBitmap = {
    style = icon;
    size = standard;
    color = color4;
    aspectRatio = normal;
    cachedSize = 64, 40;
    gstring {
        GSDrawBitmapAtCP(166),
        Bitmap (64,40,BMC_PACKBITS, (BMT_MASK|BMF_4BIT)),
        251, 0,
        233, 221,
        ...,
        GSEndString()
    }
    }

Visual Moniker Lists

In some cases, an application may wish to use different visual monikers under specific circumstances. For example, a GenApplication's icon may need separate graphics strings for different display types (VGA, SVGA, etc.) In such cases, the argument for the moniker instance field should be given as a list of separate and distinct visual monikers. (See Simple Lists .)

You may then place each of these monikers within its own separate resource. Because only one moniker will be selected from the list, only one resource will be loaded into the object block, thereby conserving memory. When the application selects its appropriate moniker, the list will be replaced with the specific moniker, copied into the object block. The system performs this function automatically.

Code Display 2-8 Simple Lists

/* A list of monikers. */
    GI_visMoniker = list {
            @moniker1,
            @moniker2,
            @moniker3
        }
/* If several monikers are specified in a list, they must be explicitly defined
 * somewhere else. If these monikers are complicated (as in the case of graphics
 * strings) they should each reside within their own resource because they will be
 * loaded in only once per application run. (Those that will be used together can
 * be within the same resource.) For example, in GEOS, several monikers
 * pertaining to different display types would be placed within separate resources.
 * When the appropriate moniker is selected, the list will be replaced with the
 * specific moniker. */
@start AppMonikerOneResource, notDetachable;
@visMoniker moniker1 = {
    size = large;
    color = color4;
    aspectRatio = normal;
    cachedSize = 64, 40;
    gstring {gstring data}
}
@end AppMonikerOneResource;
/* That moniker could then be declared within the object's instance data. */
@object GenPrimaryClass MyObject = {
    GI_visMoniker = list { @moniker1 }
}
/*
 * The GenApplication object usually contains a moniker list that allows the
 * specific UI to select a moniker based on the display.
 */
@object GenApplicationClass MyApplication = {
    GI_visMoniker = list {
    @TrigTextMoniker,			/* a simple text string */
    @TrigLCMoniker,			/* Large Color */
    @TrigLMMoniker,			/* Large Mono */
    @TrigSCMoniker,			/* Small Color */
    @TrigSMMoniker,			/* Small Mono */
    @TrigLCGAMoniker,			/* Large CGA */
    @TrigSCGAMoniker			/* Small CGA */
    }
}
@visMoniker TrigTextMoniker = "Push Me";
/* Graphics monikers might then appear within their own resource block. This
 * enables efficient memory management. */
@start AppMonikerResource, notDetachable;
@visMoniker TrigLCMoniker = {
    style = icon;
    size = large;
    color = color4;
    aspectRatio = normal;
    cachedSize = 64, 40;
    gstring {
    GSDrawBitmapAtCP(166),
    Bitmap (64,40,BMC_PACKBITS, (BMT_MASK|BMF_4BIT)),
    /*** insert Bitmap here ***/
    GSEndString()
    }
}
@end AppMonikerResource

The Inner Workings of Visual Monikers

The following section explains the inner workings of visual monikers within GEOS. It is not necessary to understand many of these concepts but it is illustrative of the system, and may aid in debugging and custom moniker use.

GI_visMoniker can take several types and combinations of arguments, all involving either text or graphics strings. Specifically, GI_visMoniker may indicate the following:

1) a simple text string
2) a text string with a mnemonic character
3) a graphics string consisting of GString opcodes
4) a graphics string containing a bitmap
5) a list of monikers of any type above.

Each of these arguments can be set up with the @visMoniker Goc keyword, but each will store their data in different manners. GEOS automatically sets up the visual moniker in the correct format.

All visual monikers make use of the VisMoniker structure. This basic structure indicates whether the moniker is text, a gstring, or a list of several types.

Code Display 2-9 The Basic VisMoniker Structure

/* The Basic VisMoniker structure contains a header which describes the type of
 * VisMoniker (VisMonikerType) and stores the cached width (width in pixels) of the
 * VisMoniker. The actual visual moniker data (either text or a gstring) follows
 * this header.
 *
 * If the visual moniker is text, this VisMoniker structure is contained within a
 * VisMonikerWithText structure. If the visual moniker is a gstring, this
 * VisMoniker structure is contained within a VisMonikerWithGString structure.*/
typedef struct {
    byte		VM_type;		/* VisMonikerType */
    word		VM_width;		/* Cached width of moniker */
} VisMoniker;
/* VisMonikerType specifies the type of moniker contained in the VisMoniker
 * structure.
 *
 * The flag VMT_MONIKER_LIST is actually a dummy flag. (This flag is never set
 * within a VisMoniker structure.) VisMonikerListEntryType has a matching flag
 * in the same location (VMLET_MONIKER_LIST). If that flag is set, it tells the
 * system that this isn't actually a VisMoniker structure but is instead a
 * VisMonikerListEntry. This is used in moniker lists (see below).
 *
 * The flag VMT_GSTRING is set if the visual moniker is in the form of a gstring
 * instead of a simple text string. If this flag is set, VMT_GS_ASPECT_RATIO and
 * VMT_GS_COLOR specify the DisplayAspectRatio and DisplayClass used by this
 * gstring.
 */
typedef ByteFlags VisMonikerType;
#define VMT_MONIKER_LIST				0x80
#define VMT_GSTRING				0x40
#define VMT_GS_ASPECT_RATIO				0x30	/* DisplayAspectRatio */
#define VMT_GS_COLOR				0x0f	/* Color */
#define VMT_GS_ASPECT_RATIO_OFFSET 4
#define VMT_GS_COLOR_OFFSET 0

If the visual moniker is a simple text string, the ChunkHandle within GI_ visMoniker will point to a chunk containing a VisMonikerWithText structure. This chunk will contain the basic VisMoniker header, along with the moniker's mnemonic character and a null-terminated text-string.

Code Display 2-10 VisMonikers With Text

/* If the VisMoniker contains simple text, the ChunkHandle within GI_visMoniker
 * points to a VisMonikerWithText structure. This structure contains the
 * basic VisMoniker header and the character of the mnemonic. (A value of -1 is
 * stored in VMWT_mnemonicOffset if there is no mnemonic for this visual moniker.)
 */
typedef struct {
    VisMoniker		VMWT_common;
    char		VMWT_mnemonicOffset;
} VisMonikerWithText;
/* The text, in the form of a null-terminated text string, follows this structure.
 * This text may be accessed with the VMWT_text offset. */
#define VMWT_text		(sizeof(VisMonikerWithText)) /* Start of text. */

If the visual moniker is a gstring, the ChunkHandle within GI_ visMoniker will point to a VisMonikerWithGString structure instead. This structure will contain the basic VisMoniker header, along with the moniker's cached height and the actual gstring. (The cached width is stored within the VisMoniker header.)

Code Display 2-11 VisMonikers With GStrings

/* If the VisMoniker contains a gstring, the ChunkHandle within GI_visMoniker
 * points to a VisMonikerWithGString structure. This structure contains the
 * basic VisMoniker header and the cached height of the gstring. (The cached width
 * is stored within the VisMoniker structure.
 */
typedef struct {
    VisMoniker		VMWGS_common;
    word		VMWGS_height;
} VisMonikerWithGSTring;
/* The gstring follows this structure.This gstring may be accessed (though it is
 * not recommended) with the VMWGS_gString offset. */
#define VMWGS_gString		(sizeof(VisMonikerWithGString)) /* Start of gstring. */

If instead of a single visual moniker, be it a text string or a gstring, GI_ visMoniker specifies a list of monikers, the case is more complex.

If GI_ visMoniker contains a list of monikers, that ChunkHandle will point to a group of VisMonikerListEntry structures (one for each moniker in the list). Each of these list entries will contain the type of moniker it references and the optr of the moniker it refers to. The actual moniker itself is not stored in that chunk. Also, because the moniker may be referred to by an optr, the actual visual monikers may reside in separate resources.

When the object containing the moniker list is first built, the system will select one of the monikers in the list (based on matching criteria in the VMLE_ type field) and replace the moniker list with the single selected visual moniker.

Code Display 2-12 VisMoniker Lists

/* If GI_visMoniker contains a list of monikers instead of a single moniker, the
 * ChunkHandle of that instance field actually points to a collection of
 * VisMonikerListEntry structures. (The total number can be calculated by dividing
 * the size of the chunk by sizeof(VisMonikerListEntry) if needed.)
 *
 * Each VisMonikerListEntry structure contains a header which describes the type of
 * VisMoniker stored in that list entry (VisMonikerListEntryType) and the optr of
 * the stored moniker. The actual moniker may reside in a different resource. */
typedef struct {
    word		VMLE_type;
    optr		VMLE_moniker;
} VisMonikerListEntry;
/* VisMonikerListEntryType specifies the type of moniker specified in the
 * VisMonikerListEntry structure.
 *
 * The flag VMLET_MONIKER_LIST must be set within this structure. This flag tells
 * the system that this isn't actually a VisMoniker structure but is instead a
 * VisMonikerListEntry.
 *
 * The system uses the other flags in this structure to determine the type of
 * moniker contained as the list entry. The system will use this information to
 * select the most appropriate moniker that will satisfy the system's needs. This
 * method is used most often in the selection of a GenPrimary's main application
 * moniker based on the DisplaySize of the system.
 *
 * VMLET_GS_SIZE stores the DisplaySize that this moniker is most appropriate for.
 * This DisplaySize is set using the "size" entry within the visual moniker
 * declaration.
 *
 * VMLET_STYLE stores the VMStyle that this moniker most closely matches. This
 * VMStyle is set using the "style" entry within the visual moniker declaration.
 *
 * VMLET_GSTRING specifies that this moniker list entry is in the form of a gstring
 * If this flag is set, VMLET_GS_ASPECT_RATIO and VMLET_GS_COLOR specify the
 * DisplayAspectRatio and DisplayClass used by this gstring.
 */
typedef ByteFlags VisMonikerListEntryType;
#define VMLET_GS_SIZE				0x0300 	/* DisplaySize. */
#define VMLET_STYLE				0x0f00	/* VMStyle */
#define VMLET_MONIKER_LIST				0x0080
#define VMLET_GSTRING				0x0040
#define VMLET_GS_ASPECT_RATIO				0x0030	/* DisplayAspectRatio */
#define VMLET_GS_COLOR				0x000f	/* DisplayClass */
#define VMLET_GS_SIZE_OFFSET	12
#define VMLET_STYLE_OFFSET 8
#define VMLET_GS_ASPECT_RATIO_OFFSET 4
#define VMLET_GS_COLOR_OFFSET 0
/* VMStyle specifies the style of the visual moniker. The system may select a
 * moniker based on the style it wishes to display. */
typedef ByteEnum VMStyle;
#define VMS_TEXT 0				/* Simple text */
#define VMS_ABBREV_TEXT 1				/* Abbreviated text */
#define VMS_GRAPHIC_TEXT 2				/* Textual graphics string */
#define VMS_ICON 3				/* Normal gstring */
#define VMS_TOOL 4				/* Tool-sized gstring */

For information on manipulating visual monikers dynamically using GenClass messages, see Managing Visual Monikers .

| Up: GEOS SDK TechDocs | Up | Prev: 3 GenClass Basics | Next: 3.2 Composite Links


Composite Links

GenClass: 3.2 GenClass Basics: Composite Links

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Visual Monikers | Next: 3.3 Keyboard Accelerators

Composite links form the connections between parent objects and child objects within a generic tree (see the UI Overview). These links are set up using the GI_comp and GI_link instance fields. The GI_comp field points to an object's first child. The GI_link field points to an object's next sibling or to its parent if no next sibling exists. In Goc, however, this usage is greatly simplified. The developer only needs to set the GI_comp field equal to its complete list of children for the parent object. The Goc preprocessor will then create and reassign all necessary links.

Code Display 2-13 Using GI_comp to Add Children

/* The GenInteraction (MyInteraction) acts as the parent object for the three
 * child GenTriggers. All three GenTrigger children will be placed within the
 * GenInteraction object. */
@object GenInteractionClass MyInteraction = {
    GI_visMoniker = "Menu";						 /* Text Moniker */
    GII_visibility = GIV_POPUP;						 /* Creates a Menu */
    GI_comp = @MyFirstChild, @MySecondChild, @MyThirdChild;
                        /* list of children */
}
@object GenTriggerClass MyFirstChild= {
    GI_visMoniker = "Child 1";					/* Text Moniker */
}
@object GenTriggerClass MySecondChild = {
    GI_visMoniker = "Child 2";					/* Text Moniker */
}
@object GenTriggerClass MyThirdChild = {
    GI_visMoniker = "Child 3";					/* Text Moniker */
}

This simple functionality is all you need to know to add children to your generic objects (and thus to create generic trees). However, it is somewhat helpful in certain cases (as in debugging) to understand what takes place underneath the surface. When an object in GEOS is assigned children, the preprocessor actually only assigns one composite link ( GI_comp ) to the first child. Each additional child acquires a link from its previous sibling using the internal instance field GI_link . Therefore, the parent will have a GI_comp to its first child, the first child will have a GI_link to the next sibling (the parent's second child) and so forth. The last sibling (the parent's last child) will have a GI_link back to the parent. This GI_link will have the LF_IS_PARENT bit set to indicate that the child points to a parent and not to a sibling. This forms what amounts to a circular linked list rather than a branching tree structure. (See the figure below.)

This structure provides a simple and convenient usage. Any object will always have at most two links to other children or parent objects. Therefore the two instance fields GI_comp and GI_link provide the entire means of constructing a generic tree.

You can travel anywhere in the generic tree through these two links. For example, for MyInteraction to communicate with MyThirdChild, it follows the path of the GI_comp to the first child, MyFirstChild, and then continues through the two GI_link s to the third child. Conversely, a child can reach a parent by travelling along the GI_link s of siblings until it reaches the last sibling, whose GI_link points to its parent object.

GenClass message handlers provide several means of pointing to the proper parent/child object without needing to explicitly state the proper GI_comp and GI_link paths. In practice, you will never need to, and in fact should not, manipulate the GI_link field at all. This information is provided merely for your use in debugging your applications. In fact, you can usually assume that a conventional branching tree structure exists rather than the linked-list tree structure shown here.

For information on manipulating these links dynamically using GenClass messages, see Generic Trees .

|  Up: GEOS SDK TechDocs | Up | Prev: 3.1 Visual Monikers | Next: 3.3 Keyboard Accelerators


Keyboard Accelerators

GenClass: 3.3 GenClass Basics: Keyboard Accelerators

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Composite Links | Next: 3.4 Attributes

Keyboard accelerators permit the user to activate objects without the use of default activations in the specific UI. (Much of the default activations in most graphical user interfaces use the mouse, for example.) In most cases, these keyboard accelerators require the use of a modifier (the alt , shift or ctrl key) and another key. The keyboard accelerator may also be a function key (F10, etc.). In general, you should avoid using the alt key because of its special role as a keyboard mnemonic within a visual moniker (see Visual Monikers ).

Keyboard accelerators may be activated at any time; they are valid at any point within the application whether the generic object involved is being displayed currently or not. (The only exception to this is during the display of a modal dialog box, which will block input to any other part of the application.) The system will search through the generic tree for the object containing the proper key sequence. In this manner, accelerators differ from mnemonics, which are only valid for an active level. Therefore, only use keyboard accelerators for heavily utilized commands; this prevents a glut of modified keystrokes, which may confuse the user. In general, it is good practice to use the "control" key exclusively for keyboard accelerators to prevent overlapping with GEOS keyboard acclelators and mnemonics.

You can set up keyboard accelerators using the GI_kbdAccelerator attribute. A keyboard accelerator must contain a valid modifier such as alt , shift , control , or ctrl . You may use any alpha-numeric character as the action key for the modifier to act on. In addition to alpha-numeric characters, the action keys shown above are also available.

The specific UI has final control over how and whether a keyboard accelerator will be implemented. Currently, keyboard accelerators will only work for GenTriggers and GenItems. In most cases, a keyboard accelerator will be displayed alongside the text for the visual moniker of an object. In OSF/Motif, keyboard accelerators are shown to the right of menu items and to the left of other items). You may add HINT_DRAW_SHORTCUT_BELOW on non-menu items to draw the keyboard accelerator below the object's visual moniker.

Code Display 2-14 Using a Keyboard Accelerator

@object GenTriggerClass MyTrigger = {
    /* Case is ignored for keyboard accelerators. */
    GI_kbdAccelerator = ctrl `R';					/* `ctrl' and `control' are both valid. */
}
/ * Other examples:
    GI_kbdAccelerator = control `A';
   Case is ignored for keyboard accelerators (unlike mnemonics). If you need
   an uppercase `A', use `shift' as a modifier.
    GI_kbdAccelerator = control shift `A';
    GI_kbdAccelerator = alt `z';
    GI_kbdAccelerator = alt shift `Z';
    GI_kbdAccelerator = ctrl shift `G';
   If you know what specific keyboard set will be implemented, you can add such
   special characters with the @specificUI keyword, though this is discouraged.
    GI_kbdAccelerator = @specificUI alt shift DELETE;
    GI_kbdAccelerator = @specificUI F6;						*/

For information on manipulating keyboard accelerators dynamically, see Managing Keyboard Accelerators .

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Composite Links | Next: 3.4 Attributes


Attributes

GenClass: 3.4 GenClass Basics: Attributes

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Keyboard Accelerators | Next: 3.5 States
GI_attrs, MSG_GEN_GET_ATTRIBUTES, MSG_GEN_SET_ATTRS

The GI_attrs attribute is a record specifying how an object will behave under various circumstances. In some cases, the GI_attrs instance field indicates that an object may initiate a busy state within the application. Several attributes only affect how an object will behave during activation. Prior to an object's activation, some of these attributes will not affect the object's behavior.

GA_SIGNAL_INTERACTION_COMPLETE
This flag instructs the UI that an Interaction has been completed. The specific UI may then decide whether to dismiss the Interaction or not. Most often, this attribute is attached to a GenTrigger within an independently displayable GenInteraction (dialog box). This attribute is useful for dismissing temporary dialogs that request information from the user. This attribute will cause the generic object to send a MSG_GEN_GUP_INTERACTION_COMMAND to its generic parent with the InteractionCommand IC_INTERACTION_COMPLETE. This message is then passed up the generic tree until it reaches an appropriate Interaction. See the Menus and Dialog Boxes chapter for more information.

GA_INITIATES_BUSY_STATE
This flag instructs the UI to mark the application as busy whenever the generic object is activated. You should only set this attribute for objects that may initiate long operations. This attribute requests that the UI visually change the cursor to show that the application is busy. (In OSF/Motif this is represented by an hourglass.) When the application finishes its operation, the cursor will revert to its former state. This attribute causes the object to send MSG_GEN_APPLICATION_MARK_BUSY to the object's application object along with MSG_GEN_APPLICATION_MARK_NOT_BUSY sent via the process' event queue. This allows the system to process the first message (showing the busy cursor) and only process the second message (removing the busy cursor) after the application finishes its current operation. (If the action is processed quickly, the cursor will often not appear.) The busy state cursor is only reflected in the current application; if the cursor roams outside the application bounds, the default behavior will occur.

GA_INITIATES_INPUT_HOLD_UP
This flag instructs the UI to mark the application as busy and to delay processing input messages until the UI and Application queues have been flushed. The application will then complete its current operation before beginning to process its input events. You should set this attribute when an object's activation may modify the UI, thereby preventing the user from clicking on objects that may become invalid after the operation. This attribute immediately sends a MSG_GEN_APPLICATION_HOLD_UP_INPUT to the application object along with a MSG_GEN_APPLICATION_RESUME_INPUT delayed via the process' event queue. This functionality is only reflected in the current application; if the cursor roams outside the application bounds, the default behavior will occur.
GA_INITIATES_INPUT_IGNORE
This flag instructs the UI to mark the application as busy and to completely ignore all subsequent input events to the application. The application will enter a modal state, meaning that all other application events will be ignored until the UI and Application queues are flushed. Usually, this state is conveyed to the user by broadcasting an audible beep whenever an input event is attempted. The generic object will immediately send a MSG_GEN_APPLICATION_IGNORE_INPUT to the application object along with a MSG_GEN_APPLICATION_ACCEPT_INPUT delayed via the application queue. This functionality is only reflected in the current application; if the cursor roams outside the application bounds, the default behavior will occur.
GA_READ_ONLY
This flag indicates that this object's only function is to display information to the user; the user will not be able to interact with this object. This attribute is set most often in lists or text objects.
GA_KBD_SEARCH_PATH
This flag indicates that this generic branch contains objects with keyboard accelerators and should therefore be searched when evaluating such events. This attribute bit is set internally by the system. There should be no need for your application to deal with this attribute directly.
GA_TARGETABLE
This flag indicates that this object is targetable and is eligible to receive the target exclusive within its target level. Most specific UIs will automatically grab the target for this object whenever the user interacts with it. This attribute is set by default within the following classes:
GenField
GenApplication
GenPrimary
GenDisplayControl
GenDisplay
GenView
GA_NOTIFY_VISIBILITY
This flag indicates that this object should send notification when it becomes visible and not visible. Objects thus will send MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION
to the GenApplication object whenever the state of their visibility changes (see the GenApplication chapter). You may alter this behavior by including one of the visibility vardata attributes.

Code Display 2-15 Using GI_attrs in a Dialog Box

@object GenInteractionClass MyDialogBox = {
    GI_comp = @MyButton, @MyOtherButton;
    GII_visibility = GIV_DIALOG;				/* build this Interaction as a dialog box.*/
}
@object GenTriggerClass MyButton = {
    GTI_actionMsg = MSG_MY_SPECIAL_MESSAGE;
    GTI_destination = process;
    /* MyButton, when activated, will send the message above to the
     * process object. Only when that happens will it activate the
     * behavior within the GI_attrs instance data below. */
    GI_attrs = @default |
    /* This flag will close the MyDialogBox object */
    GA_SIGNAL_INTERACTION_COMPLETE |
    /* This flag will set the application to ignore all input events while the
     * message above is processed. */
    GA_INITIATES_INPUT_IGNORE;
}

MSG_GEN_GET_ATTRIBUTES

byte	MSG_GEN_GET_ATTRIBUTES();

This message retrieves the GI_attrs instance data for the object the message is sent to. This message returns a byte length bitfield.

Source: Unrestricted.

Destination: Any generic object.

Return: Byte length GI_ attrs bitfield.

Interception: Generally not intercepted.

MSG_GEN_SET_ATTRS

void	MSG_GEN_SET_ATTRS(
        byte attrsToSet,
        byte attrsToClear);

This message sets the recipient's GI_attrs field. This message takes two arguments: the attributes to set and the attributes to clear. There is no need to repeat attributes that have been previously set. Note that these attributes will not take effect until the object is activated in the normal manner. (Sending this message does not in itself initiate the activity described).

You should only send this message while an object is not GS_USABLE, because these attributes are only checked when an object is specifically built. Setting the attributes of a GS_USABLE object may cause an error.

Source: Unrestricted.

Destination: Any non-usable generic object.

Parameters: attrsToSet GenAttributes to set in GI_ attrs .

attrsToClear GenAttributes to clear in GI_ attrs .

Interception: Generally not intercepted.

Code Display 2-16 Conditionally Altering the GI_attrs Field

@method MyProcessClass, MSG_DO_CUSTOM_ATTRS {
    byte MyAttrs;
    /* retrieve the GI_attrs field */
    MyAttrs = @call @MyObject::MSG_GEN_GET_ATTRIBUTES();
    /* If the GA_COMPLETES_INTERACTION bit is set, then set it
     * GA_INITIATES_BUSY_STATE also. Otherwise set it
     * GA_INITIATES_INPUT_IGNORE. */
    if (MyAttrs & GA_COMPLETES_INTERACTION){
    @call @MyObject::MSG_GEN_SET_NOT_USABLE();
    @call @MyObject::MSG_GEN_SET_ATTRS(GA_INITIATES_BUSY_STATE, 0);
    /* Note that setting an object's GA_INITIATES_BUSY_STATE attribute will
     * not in itself initiate a busy state. That object will only issue a busy
     * state when it is activated in the normal fashion. */
    @call @MyObject::MSG_GEN_SET_USABLE();
    } else {
    @call @MyObject::MSG_GEN_SET_NOT_USABLE();
    @call @MyObject::MSG_GEN_SET_ATTRS(GA_INITITATES_INPUT_IGNORE, 0);
    @call @MyObject::MSG_GEN_SET_USABLE();
    }
}
| Up: GEOS SDK TechDocs | Up | Prev: 3.3 Keyboard Accelerators | Next: 3.5 States


States

GenClass: 3.5 GenClass Basics: States

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Attributes | Next: 4 Modifying GenClass Instance Data
GI_states

The GI_states attribute is a record that holds the state of the generic object. These states affect the object's visual representation and its functionality. By default, all objects have both GS_USABLE and GS_ENABLED set when first built. There are two GI_states :

GS_USABLE
This state controls the usability of an object. Setting an object GS_USABLE will indicate that both the object and the entire generic branch below this object should be considered as an active part of the user interface and therefore should be visually represented. An object that is not set GS_USABLE cannot appear as part of the user interface. Before an object can be visually built and considered part of the user interface, however, it must satisfy two conditions: The object itself must be GS_USABLE, and all ancestors (parents) of the object must also be GS_USABLE.
This means that a continuous path of GS_USABLE objects must exist from the top node of your application to the object for it to be usable. In this case, an object is considered to be fully usable. Conversely, if an object is not GS_USABLE, then no object in the branch below it can be visually displayed either (even if it is GS_USABLE). Therefore, an object may be set GS_USABLE without being fully usable. This allows you to set an entire branch fully usable by setting just one node object usable, provided that all other objects below that node had previously been set GS_USABLE. This attribute is set by default.
GS_ENABLED
This state sets the object enabled. This state controls whether the object is available to the user for activity. An object must be enabled for the user to interact with it. If an object is set GS_USABLE but not GS_ENABLED, it will be visually displayed but will not accept input from the user. The object will also be unable to perform its normal activities. This is usually represented by "graying out" the object's visual moniker. Before an object can be "enabled" for user input and activation, however, it must satisfy two conditions: The object must be GS_ENABLED; all ancestors (parents) of the object must also be GS_ENABLED. Therefore, a continuous path of GS_ENABLED objects must exist from the top node of your application to the object in question for it to be enabled. In this case, an object is considered to be fully enabled.
Conversely, if an object is not GS_ENABLED, then no object in the branch below it can be interacted with either (even if it is GS_ENABLED). Therefore, an object may be GS_ENABLED without being fully enabled. This allows you to set an entire branch fully enabled by setting just one node object enabled, provided that all other objects below that node had previously been set GS_ENABLED.

These states not only affect the state of the current object but all of its descendants as well. When your user interface is first built (or rebuilt) it conducts a top-down search, building any objects that are declared GS_USABLE. If an object is not GS_USABLE, it will not be visually built. Furthermore, none of its descendants will be searched (or built). Therefore, if any single node is not GS_USABLE, no other object below that node will be fully usable.

In cases where you wish to alter any fundamental behavior of an object, you may have to set the object not usable, change its behavior, and then set the object GS_USABLE again. This ensures that the object is built out correctly (including visually) according to the new criteria.

Code Display 2-17 Setting GI_states

@object GenTriggerClass MyTrigger = {
        /* The default GI_states are GS_USABLE and GS_ENABLED.
         * This object will only be GS_USABLE */
    GI_states = @default & ~GS_ENABLED;
}

The Usable State

MSG_GEN_GET_USABLE, MSG_GEN_SET_USABLE, MSG_GEN_SET_NOT_USABLE, MSG_GEN_CHECK_IF_FULLY_USABLE

Setting an object GS_USABLE will incorporate the object into the generic tree and regard the object as part of the user interface. (It will not by itself enable an object for user input; this requires an object to be set GS_ENABLED also.) If an object is not GS_USABLE, it cannot be used in any manner including any visual implementation by the user interface. The following messages manipulate an object's usable state.

MSG_GEN_GET_USABLE

Boolean	MSG_GEN_GET_USABLE();

This message checks the GS_USABLE bit of the GI_states field.

Source: Unrestricted.

Destination: Any generic object.

Parameters: None.

Return: Will return true if the object is GS_USABLE, false if it is not usable.

Interception: Generally not intercepted.

MSG_GEN_SET_USABLE

void	MSG_GEN_SET_USABLE(
        VisUpdateMode updateMode);

This message sets an object GS_USABLE. (This message has no effect on an object already GS_USABLE.) Objects may be set GS_USABLE only after they have been attached to a generic tree. Setting an object usable forces that object to be specifically built. If the object's associated window is realized, the object will be visually built and updated. Setting an object GS_USABLE that is not attached to the generic tree will cause an error.

Setting an object usable allows that object to become part of the user interface. Before the object can be used, though, it must be fully usable . An object becomes fully usable only if all of its ancestors are GS_USABLE. If any ancestor is not GS_USABLE, then the entire branch below it will not be fully usable. You can check if an object is fully usable with the message MSG_GEN_CHECK_IF_FULLY_USABLE .

Conversely, setting a node GS_USABLE, in which every branch object below it is already GS_USABLE will make that entire branch fully usable. This is useful for bringing up an entire section of a generic tree by setting a single object usable.

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode VisUpdateMode to use when updating changes to the screen.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_SET_NOT_USABLE

void	MSG_GEN_SET_NOT_USABLE(
        VisUpdateMode updateMode);

This message sets an object not usable (clears the GS_USABLE bit). Because an object may be visually unbuilt by this message, the VisUpdateMode VUM_MANUAL is not allowed.

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode VisUpdateMode to use when updating changes to the screen. May not be VUM_MANUAL.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_CHECK_IF_FULLY_USABLE

Boolean	MSG_GEN_CHECK_IF_FULLY_USABLE();

This message checks whether an object is fully usable. The object and all of its parents must be GS_USABLE for the object to be fully usable.

Source: Unrestricted.

Destination: Any generic object.

Return: Will return true if the object is fully usable, false if it is not.

Interception: Generally not intercepted.

The Enabled State

MSG_GEN_GET_ENABLED, MSG_GEN_SET_ENABLED, MSG_GEN_SET_NOT_ENABLED, MSG_GEN_CHECK_IF_FULLY_ENABLED

An object that is GS_ENABLED is ready for user interaction. As in the case with GS_USABLE, all parents of the object in question must be GS_ENABLED to fully enable the object. An object may be fully enabled without being fully usable, but the object will not be visually represented. You can check if an object is fully enabled with MSG_GEN_CHECK_IF_FULLY_ENABLED .

An object that is not enabled but is set usable will be represented in the user interface; it will not, however, allow the user to interact with it. In many cases, the specific UI implements this by "graying out" the object. Setting a disabled object will visually update it.

MSG_GEN_GET_ENABLED

Boolean	MSG_GEN_GET_ENABLED();

This message returns the enabled state of the object the message is sent to.

Source: Unrestricted.

Destination: Any generic object.

Return: Will return true if the object is enabled, false if it is not.

Interception: Generally not intercepted.

MSG_GEN_SET_ENABLED

void	MSG_GEN_SET_ENABLED(
        VisUpdateMode updateMode);

This message sets an object GS_ENABLED. (This message has no effect on an object already GS_ENABLED.) You must pass this message a VisUpdateMode . Sending this message allows the object to receive user input. Only send this message if your application will be ready to interact with the object after the period specified in the VisUpdateMode has passed.

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode VisUpdateMode to use when updating changes to the screen.

Interception: Generally not intercepted.

MSG_GEN_SET_NOT_ENABLED

void	MSG_GEN_SET_NOT_ENABLED(
        VisUpdateMode updateMode);

This message sets the object not enabled (clears the object's GS_ENABLED bit.) You must pass the message a VisUpdateMode . In most specific UIs a disabled state is implemented by "graying out" the object. The user will be unable to interact with the object according to the VisUpdateMode passed.

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode VisUpdateMode to use when updating changes to the screen. May not be VUM_MANUAL.

Interception: Generally not intercepted.

MSG_GEN_CHECK_IF_FULLY_ENABLED

Boolean	MSG_GEN_CHECK_IF_FULLY_ENABLED();

This message checks whether an object is fully enabled. An object is only fully enabled (ready for user interaction) when both it and all of its ancestors are enabled.

Source: Unrestricted.

Destination: Any generic object.

Return: Will return true if fully enabled, false if not.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Attributes | Next: 4 Modifying GenClass Instance Data


Modifying GenClass Instance Data

GenClass: 4 Modifying GenClass Instance Data

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 GenClass Basics | Next: 5 Generic Trees

Instance data need not be set solely within your .goc file. You may also retrieve or alter this data dynamically with messages in GenClass . The following section details messages to modify the GI_visMoniker , GI_kbdAccelerator , GI_attrs and GI_states instance fields. This section also details ways of using the variable data object routines and messages to modify a generic object's vardata.

Remember that when accessing and modifying instance data, you may need to dereference the pointer to the object's instance data if the location of that instance data may have changed. This pointer ( pself ) is automatically set up correctly inside a method upon receipt of the corresponding message. If you perform actions within that method that may move the object, you can use ObjDerefGen () to dereference pself again.

If the instance fields you wish to modify are related to generic tree mechanisms ( GI_comp and GI_link ), see Special Message Passing .

Visual Monikers

Managing Visual Monikers

Managing Keyboard Accelerators

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 GenClass Basics | Next: 5 Generic Trees


Visual Monikers

GenClass: 4.1 Modifying GenClass Instance Data: Visual Monikers

| Up: GEOS SDK TechDocs | Up | Prev: 4 Modifying GenClass Instance Data | Next: 4.2 Managing Visual Monikers

A visual moniker must reside in a local memory (LMem) chunk. That chunk should be located within the object block of the particular generic object it is associated with. Once monikers are defined, there are several messages in GenClass that you can use to change the visual moniker for an object.

The GI_visMoniker field contains a ChunkHandle of the VisMoniker structure. The VisMoniker structure contains three pieces of data: the type of moniker, the width of the moniker chunk, and the moniker data.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Modifying GenClass Instance Data | Next: 4.2 Managing Visual Monikers


Managing Visual Monikers

GenClass: 4.2 Modifying GenClass Instance Data: Managing Visual Monikers

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Visual Monikers | Next: 4.3 Managing Keyboard Accelerators
MSG_GEN_GET_VIS_MONIKER, MSG_GEN_USE_VIS_MONIKER, MSG_GEN_CREATE_VIS_MONIKER, MSG_GEN_REPLACE_VIS_MONIKER, MSG_GEN_REPLACE_VIS_MONIKER_OPTR, MSG_GEN_REPLACE_VIS_MONIKER_TEXT, MSG_GEN_FIND_MONIKER, MSG_GEN_DRAW_MONIKER, MSG_GEN_GET_MONIKER_POS, MSG_GEN_GET_MONIKER_SIZE, MSG_GEN_RELOC_MONIKER_LIST, MSG_GEN_FIND_OBJECT_WITH_TEXT_MONIKER

Sending MSG_GEN_GET_VIS_MONIKER to an object returns the chunk handle of the visual moniker ( GI_visMoniker ) for that object. You can then inspect that visual moniker or use it within other objects (though this is not recommended). You must be careful when sharing monikers with other objects, as freeing one object (and therefore its associated moniker) will interfere with any other objects referencing that moniker.

Sending MSG_GEN_USE_VIS_MONIKER to an object sets a visual moniker for that object to use. The message must pass the chunk handle of the moniker desired for the object along with a VisUpdateMode . Valid VisUpdateMode s are described in the VisClass chapter. The moniker to use must reside in the same block as the object being set. If you need to set an object to use a new moniker from a location outside of its object block, use MSG_GEN_REPLACE_VIS_MONIKER to copy the moniker to the new location.

MSG_GEN_REPLACE_VIS_MONIKER copies a visual moniker from a source location into a destination. This message must pass parameters specifying the type of copy operation. The source for the moniker can be referenced using an optr or a simple text string. Use this message when you wish to copy a moniker from outside of the object's current object block.

MSG_GEN_REPLACE_VIS_MONIKER_OPTR and MSG_GEN_REPLACE_VIS_MONIKER_TEXT are simplified versions of the above message, allowing you to set a visual moniker from an optr or from a pointer to a null-terminated text string without having to pass many other arguments.

MSG_GEN_CREATE_VIS_MONIKER creates a visual moniker (within the resource block of the object this message is sent to), copying it from a source; it does not, however, attach this moniker to any object.

MSG_GEN_FIND_MONIKER searches for a specific type of moniker within a moniker list. You may then use the moniker directly. You can also replace the original moniker list with the specified moniker.

MSG_GEN_DRAW_MONIKER, MSG_GEN_GET_MONIKER_POS , and MSG_GEN_GET_MONIKER_SIZE all manipulate a currently existing moniker. These messages are most useful for custom gadgets. MSG_GEN_DRAW_MONIKER draws the moniker according to the criteria passed with the message. MSG_GEN_GET_MONIKER_POS and MSG_GEN_GET_MONIKER_SIZE return GEOS coordinates specifying the location and size of the moniker, respectively. You may then inspect and alter these coordinates before sending the moniker a MSG_GEN_DRAW_MONIKER to redraw the moniker according to the changed criteria.

The utility message MSG_GEN_RELOC_MONIKER_LIST relocates an object's moniker list.

To find an object with a particular visual moniker, send MSG_GEN_FIND_OBJECT_WITH_MONIKER_FLAGS to the object at which to start the top-down search, passing the text to perform the match.

MSG_GEN_GET_VIS_MONIKER

ChunkHandle	 MSG_GEN_GET_VIS_MONIKER();

This message retrieves the instance data in the object's current GI_visMoniker instance field. This message returns the ChunkHandle of the moniker data structure. You can then use that chunk handle to manipulate the visual moniker directly or to copy the moniker for use by other objects.

Source: Unrestricted.

Destination: Any generic object.

Return: The chunk handle of the visual moniker in use by this object.

Interception: Generally not intercepted.

MSG_GEN_USE_VIS_MONIKER

void	MSG_GEN_USE_VIS_MONIKER(
        ChunkHandle moniker,
        VisUpdateMode updateMode);

This message allows an object to reference a visual moniker; this message does not destroy the chunk of the object's current visual moniker. The moniker must reside in the same block as the object. Use MSG_GEN_REPLACE_VIS_MONIKER if you wish to set a moniker from a source outside the object block or if you wish to use a new moniker, overwriting the old one.

You cannot pass a moniker list's chunk handle using this message. See MSG_GEN_FIND_MONIKER for information on selecting monikers from within a list.

Source: Unrestricted.

Destination: Any generic object.

Parameters: moniker The chunk handle of the visual moniker to use

updateMode
VisUpdateMode to determine when the moniker will be redrawn.

Return: Nothing.

Interception: Generally not intercepted.

Code Display 2-18 Getting and Using Visual Monikers

/* This method effectively copies a visual moniker from ObjectOne into ObjectTwo.
 * Both objects must reside in the same object block. Note that in effect, both
 * ObjectOne and ObjectTwo will "share" the same moniker. This can be dangerous if
 * one object is freed, thereby causing the other object to become dereferenced.
 * For more complex copy operations, use MSG_GEN_REPLACE_VIS_MONIKER instead. */
@method MyProcessClass, MSG_MY_MONIKER_MESSAGE {
    /* Set up variable to store the chunk handle of the visual moniker. */
    ChunkHandle MyVisMonikerCH;
    /* Retrieve the visMoniker of ObjectOne and store it in the variable. */
    MyVisMonikerCH = @call @ObjectOne::MSG_GEN_GET_VIS_MONIKER();
    /* Set the visMoniker of Object Two to MyVisMonikerCH and update
     * immediately. Use @send if you don't expect a return value. */
    @send @ObjectTwo::MSG_GEN_USE_VIS_MONIKER(MyVisMonikerCH, VUM_NOW);
}
/* You can also declare visual monikers in your .goc file and set them later */
    @visMoniker OnMoniker = "On";
    @visMoniker OffMoniker = "Off";
    /* Within a method, the following routine will set the moniker to "On". The
     * OptrToChunk operator casts the object from an optr to a ChunkHandle. */
    @call @MyObject::MSG_GEN_SET_MONIKER(OptrToChunk(@OnMoniker), VUM_NOW);
    /* Within a method, the following routine will set the moniker to "Off" */
    @call @MyObject::MSG_GEN_SET_MONIKER(OptrToChunk(@OffMoniker), VUM_NOW);

MSG_GEN_REPLACE_VIS_MONIKER

ChunkHandle	 MSG_GEN_REPLACE_VIS_MONIKER(@stack
        VisUpdateMode			updateMode,
        word			height,
        word			width,
        word			length,
        VisMonikerDataType			dataType,
        VisMonikerSourceType			sourceType,
        dword			source);

This message copies a visual moniker from a source to the object sent the message. The message returns the chunk handle of the newly copied moniker. The object's current visual moniker is overwritten by this message; therefore, if more than one object shares the overwritten block, the other object should be updated with MSG_GEN_USE_VIS_MONIKER . This message can copy a visual moniker from a variety of sources.

This message is a general, all-purpose, moniker replacement device. If your visual moniker source is a visMoniker structure or a null-terminated text string, you may be able to use the simpler messages MSG_GEN_REPLACE_VIS_MONIKER_OPTR and MSG_GEN_REPLACE_VIS_MONIKER_TEXT .

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode Specifies the VisUpdateMode to use when visually updating the changes to the screen. See the VisClass Chapter for more information on VisUpdateMode types.

height
If moniker is a GString, this specifies the height of the moniker in points. Otherwise, set this to zero.
width
If the moniker is a GString, this specifies the width of the moniker in points. Otherwise, set this to zero.
length
If the moniker is a GString, this specifies the size of the moniker in bytes. If the moniker is text and this argument is zero, the string is assumed to be null-terminated.
dataType
Specifies the type of data referenced by the source . Valid VisMonikerDataType types include VMDT_VIS_MONIKER, VMDT_TEXT, VMDT_GSTRING, or VMDT_TOKEN.
sourceType
Specifies the type of pointer referencing the source . Valid VisMonikerSourceType types are VMST_FPTR, VMST_OPTR, and VMST_HPTR.
source
Specifies the source of the moniker to be used during the copy process. This source may be a pointer to a text string, an optr pointing to a visual moniker outside the current block, or a variety of other sources. The source type should be specified in sourceType .

Return: The chunk handle of the new visual moniker for the object.

Interception: Generally not intercepted.

MSG_GEN_REPLACE_VIS_MONIKER_OPTR

ChunkHandle	 MSG_GEN_REPLACE_VIS_MONIKER_OPTR(
        optr		source,
        VisUpdateMode		updateMode);

This message is a simplified version of MSG_GEN_REPLACE_VIS_MONIKER. If your visual moniker source is an existing visMoniker structure, you can use this message to replace an object's moniker.

Source: Unrestricted.

Destination: Any generic object.

Parameters: source The optr of the visMoniker structure to use in the replacement operation.

updateMode
Specifies the VisUpdateMode to use when visually updating the changes to the screen.

Return: The chunk handle of the new visual moniker for the object.

Interception: Generally not intercepted.

MSG_GEN_REPLACE_VIS_MONIKER_TEXT

ChunkHandle	 MSG_GEN_REPLACE_VIS_MONIKER_TEXT(
        const char		*source,
        VisUpdateMode		updateMode);

This message is a simplified version of MSG_GEN_REPLACE_VIS_MONIKER. If your visual moniker source is a pointer to a null-terminated text string, you can use this message to replace an object's moniker without having to pass the arguments of MSG_GEN_REPLACE_VIS_MONIKER .

Source: Unrestricted.

Destination: Any generic object.

Parameters: source A pointer to a null-terminated character string to use in the replacement operation.

updateMode
Specifies the VisUpdateMode to use when visually updating the changes to the screen.

Return: The chunk handle of the new visual moniker for the object.

Interception: Generally not intercepted.

MSG_GEN_CREATE_VIS_MONIKER

ChunkHandle MSG_GEN_CREATE_VIS_MONIKER(@stack
        CreateVisMonikerFlags			flags,
        word			height,
        word			width,
        word			length,
        VisMonikerDataType			dataType,
        VisMonikerSourceType			sourceType,
        dword			source);

This message creates a visual moniker chunk within the resource block of the object sent this message. The moniker is copied from the source specified but is not attached to any object in the new object block.

Source: Unrestricted

Destination: Any GenClass object

Parameters: flags Flags to use in the creation of the new visual moniker. CVMF_DIRTY marks the new moniker chunk OCF_DIRTY .

height
If moniker is a GString, this specifies the height of the moniker in points. Otherwise, set this to zero.
width
If the moniker is a GString, this specifies the width of the moniker in points. Otherwise, set this to zero.
length
If the moniker is a GString, this specifies the length of the moniker in points. Otherwise, set this to zero.
dataType
Specifies the type of data referenced by the source . Valid VisMonikerDataType types include VMDT_VIS_MONIKER, VMDT_TEXT, VMDT_GSTRING, or VMDT_TOKEN.
sourceType
Specifies the type of pointer referencing the source . Valid VisMonikerSourceType types are VMST_FPTR, VMST_OPTR, and VMST_HPTR.
source
Specifies the source of the moniker to be used during the copy process. This source may be a pointer to a text string, an optr pointing to a visual moniker outside the current block, or a variety of other sources. The source type should be specified in sourceType .

Return: The chunk handle of the new visual moniker for the object.

Interception: Generally not intercepted.

MSG_GEN_FIND_MONIKER

optr	MSG_GEN_FIND_MONIKER(
        Boolean		useAppMonikerList,
        word		searchFlags,					/* VisMonikerSearchFlags */
        MemHandle		destBlock);

This message scans a list of monikers and selects a specific moniker (or the most appropriate moniker). The moniker selected is determined according to the passed VisMonikerSearchFlags . This message is usually used by the specific UI to select an application moniker from a list.

If the first argument ( useAppMonikerList ) is not zero, the message will use the GenApplication's moniker list instead of the recipient's. This is useful for finding the icon used for an iconified application.

Note that this message returns an optr, not a chunk handle, as this message is most often used to find a moniker outside of the current object block. This message returns zero if no moniker is found.

Source: Unrestricted.

Destination: Any generic object.

Parameters: useAppMonikerList
 Non-zero to use the moniker list of the application associated with this object.

searchFlags
VisMonikerSearchFlags to use for the search criteria. If VMSF_COPY_CHUNK is specified in the searchFlags , a block handle must be supplied in destBlock . The selected moniker will be copied into that block.
destBlock
Block to copy a moniker into, if VMSF_COPY_CHUNK is specified in the searchFlags .

Return: The optr of the moniker found according to the search criteria. (This may be outside the current object block.)

Interception: Generally not intercepted.

MSG_GEN_DRAW_MONIKER

void	MSG_GEN_DRAW_MONIKER(@stack
        DrawMonikerFlags		monikerFlags,
        word		textHeight,
        GStateHandle		gState,
        word		yMaximum,
        word		xMaximum,
        word		yInset,
        word		xInset);

This message draws a moniker for an object according to the parameters passed. The moniker must currently exist. You can use this message to change the way you wish a visual moniker to appear within a generic object without actually changing the visual moniker itself.

Source: Unrestricted.

Destination: Any GS_USABLE generic object.

Parameters: monikerFlags Specifies the DrawMonikerFlags to use when drawing the moniker. The DrawMonikerFlags record specifies the conditions to draw the moniker under.

textHeight
Specifies the height of the system text. Passing this value speeds the processing of this message. If you do not know the height of the system text, pass zero.
gState
Specifies the GState, if any, to use when drawing the moniker.
yMaximum, xMaximum
These specify the maximum height and width of the moniker. Typically, these are used with DMF_CLIP_TO_MAX_WIDTH (in DrawMonikerFlags ) to perform a clipping operation on the moniker. Pass zero if no maximum width is desired.
yInset
Specifies the point to begin drawing the moniker if the moniker is right or left justified.
xInset
Specifies the point to begin drawing the moniker if the moniker is top or bottom justified.

Return: Nothing

Interception: Generally not intercepted. Custom gadgets may handle this if they are supplementing or replacing default functionality.

Structures: The DrawMonikerFlags record defines the parameters to use when drawing a currently existing visual moniker. This record is used by MSG_GEN_DRAW_MONIKER , MSG_GEN_GET_MONIKER_POS and MSG_GEN_GET_MONIKER_SIZE . Its flags are shown below:

DMF_UNDERLINE_ACCELERATOR
DMF_CLIP_TO_MAX_WIDTH
DMF_NONE
DMF_Y_JUST_MASK    /* of type Justifications */
DMF_X_JUST_MASK    /* of type Justifications */

Use DMF_UNDERLINE_ACCELERATOR to underline the keyboard accelerator of a text moniker. Use DMF_CLIP_TO_MAX_WIDTH to clip the visual bounds of the moniker to the width specified in maxWidth . Use DMF_NONE to draw the moniker at the current pen position.

The two justification masks are of type Justification , and they specify the vertical and horizontal justifications to use when drawing the moniker. The values you can use in the justification fields are

J_LEFT
J_RIGHT
J_CENTER
J_FULL

MSG_GEN_GET_MONIKER_POS

XYValueASDWord MSG_GEN_GET_MONIKER_POS(@stack
        DrawMonikerFlags		monikerFlags,
        word		textHeight,
        GStateHandle		gState,
        word		yMaximum,
        word		xMaximum,
        word		yInset,
        word		xInset);

This message returns the x and y coordinates of the moniker for the object as if the moniker were drawn according to the passed parameters (the same as for MSG_GEN_DRAW_MONIKER ). Use the macros DWORD_X and DWORD_Y to extract the appropriate coordinates from the returned value.

This message, along with MSG_GEN_DRAW_MONIKER and MSG_GEN_GET_MONIKER_SIZE is most useful in the construction and modification of custom gadgets.

Source: Unrestricted.

Destination: Any generic object.

Parameters: See MSG_GEN_DRAW_MONIKER .

Return: XYValueAsDWord which can be split up into x and y coordinates of the moniker position with the macros DWORD_X and DWORD_Y.

Interception: Generally not intercepted.

MSG_GEN_GET_MONIKER_SIZE

SizeAsDWord MSG_GEN_GET_MONIKER_SIZE(
        word		textHeight,
        GStateHandle		gState);

This message returns the width and height (in points) of the moniker for the object. Use the macros DWORD_WIDTH and DWORD_HEIGHT to extract the appropriate coordinates from the returned value. This message, along with MSG_GEN_DRAW_MONIKER and MSG_GEN_GET_MONIKER_POS , is most useful in the construction and modification of custom gadgets.

Source: Unrestricted.

Destination: Any generic object.

Parameters: textHeight The system text height, if known; otherwise, pass zero.

gstate
The GState handle of the GState to draw to.

Return: SizeAsDWord which can be split up into with and height with the macros DWORD_WIDTH and DWORD_HEIGHT.

Interception: Generally not intercepted.

MSG_GEN_RELOC_MONIKER_LIST

void 	MSG_GEN_RELOC_MONIKER_LIST(
        optr		monikerList,
        word 		relocFlag); /* 0: relocate; 1: unrelocate */

This method (un)relocates a passed moniker list.

Source: Anyone.

Destination: Any GenClass object.

Parameters: monikerList optr of moniker list.

reloc
Flag specifying whether relocation or unrelocation is desired.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_FIND_OBJECT_WITH_TEXT_MONIKER

optr	MSG_GEN_FIND_OBJECT_WITH_TEXT_MONIKER(
        char	*text,
        word	flags);

This message recursively searches through the generic tree (starting at the object sent the message) for an object whose visual moniker matches the passed text. This match does not need to be complete (unless the flag GFTMF_EXACT_MATCH is passed); the message will return the first object whose initial characters match the given text. The message must pass GenFindObjectWithMonikerFlags .

typedef WordFlags GenFindObjectWithMonikerFlags;
#define GFTMF_EXACT_MATCH 0x8000
#define GFTMF_SKIP_THIS_NODE 0x4000

If GFTMF_EXACT_MATCH is passed, the text and the visual moniker text must match exactly (null-terminated). If GFTMF_SKIP_THIS_NODE is passed, the object which sent the message will not be checked; only its children will be checked.

Source: Unrestricted.

Destination: Any generic object.

Parameters: text Null-terminated text.

flags
GenFindObjectWithMonikerFlags .

Return: optr of the object with the matching moniker, or null if no moniker was found.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Visual Monikers | Next: 4.3 Managing Keyboard Accelerators


Managing Keyboard Accelerators

GenClass: 4.3 Modifying GenClass Instance Data: Managing Keyboard Accelerators

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Managing Visual Monikers | Next: 5 Generic Trees
MSG_GEN_GET_KBD_ACCELERATOR, MSG_GEN_SET_KBD_ACCELERATOR, MSG_GEN_CHANGE_ACCELERATOR

Keyboard Accelerators provide keyboard shortcuts for heavily used application functions. For complete information on valid GI_kbdAccelerator instance data and the functionality those data provide, see Keyboard Accelerators .

MSG_GEN_GET_KBD_ACCELERATOR returns a word of data specifying the current keyboard accelerator, if any, for the object the message is sent to.

MSG_GEN_SET_KBD_ACCELERATOR sets the keyboard accelerator of the object the message is sent to. The message must pass the keyboard accelerator to use along with a VisUpdateMode . If you wish to remove a keyboard accelerator from an object, pass this message with a keyboard acclerator of zero.

MSG_GEN_CHANGE_ACCELERATOR changes the keyboard accelerator using a low-level replace operation.

MSG_GEN_GET_KBD_ACCLERATOR

word	MSG_GEN_GET_KBD_ACCELERATOR();

This message returns the object's current GI_kbdAccelerator. The message returns a word-length data structure ( KeyboardShortcut ). You can then manipulate or use this word of data to set other object's keyboard accelerators. Make sure that you do not have two objects sharing the same keyboard accelerator.

Source: Unrestricted.

Destination: Any generic object.

Return: KeyboardShortcut of the generic object (in GI_ kbdAccelerator ).

Interception: Generally not intercepted.

MSG_GEN_SET_KBD_ACCELERATOR

void	MSG_GEN_SET_KBD_ACCELERATOR(
        word		accelerator,
        VisUpdateMode		updateMode);

This message sets the GI_kbdAccelerator of the object the message is sent to.

Source: Unrestricted.

Destination: Any generic object.

Parameters: accelerator KeyboardShortcut to set GI_ kbdAccelerator of the generic object to.

updateMode
VisUpdateMode to determine when redrawing occurs.

Interception: Generally not intercepted.

Code Display 2-19 Using a Keyboard Accelerator

@kbdAccelerator ShortcutOne = ctrl shift `k';
    /* @specificUI keyword needed to use DELETE key */
@kbdAccelerator ShortcutTwo = @specificUI ctrl DELETE;
@object GenTriggerClass MyTrigger = {
    GI_kbdAccelerator = ShortcutOne;				/* sets accel to "ctrl shift `k'" */
}
@object GenTriggerClass MyOtherTrigger = {
    GI_kbdAccelerator = ShortcutCopy;				/* Copies "ctrl shift `k'" to
                     * MyOtherTrigger */
}

MSG_GEN_CHANGE_ACCELERATOR

void	MSG_GEN_CHANGE_ACCELERATOR(
        word	bitsToClear,
        word	bitsToSet);

This message changes the keyboard accelerator using constants defined in the input library. In most cases, use MSG_GEN_SET_KBD_ACCELERATOR instead. MSG_GEN_CHANGE_ACCELERATOR is useful in some cases where you wish to tinker slightly with the keyboard shortcut's usage (as in a case where an accelerator of ctrl z should change to alt z in the duplication of a template object).

Source: Unrestricted.

Destination: Any generic object.

Parameters: bitsToClear KeyboardShortcut and/or VChar to clear.

bitsToSet
KeyboardShortcut and/or VChar to set.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Managing Visual Monikers | Next: 5 Generic Trees


Generic Trees

GenClass: 5 Generic Trees

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Modifying GenClass Instance Data | Next: 6 Vardata

Generic trees are constructed in several ways. The most common is to add, remove, and move individual children. These children may be the top of a generic branch and will therefore add, remove, or move the entire branch below them. GenClass also provides other means of manipulating entire branches of generic trees. Each manner of generic tree construction and destruction has certain advantages and disadvantages. The various methods are detailed below:

  • The object may be declared in the .goc file, attached to the generic tree. In this case, there is no need to add the child as this is taken care of by the compiler. You can remove the object and any of its children with MSG_GEN_REMOVE (or MSG_GEN_REMOVE_CHILD ). After removal, any single object may be destroyed with MSG_META_OBJ_FREE . You may also destroy a tree of generic objects with MSG_GEN_DESTROY . You may add the removed object (and its children) to a different location in the generic tree with MSG_GEN_ADD_CHILD .
  • The object may exist in your .goc file but remain unattached to the generic tree. You may attach the object with MSG_GEN_ADD_CHILD . You can remove the object, and any children of this object, with MSG_GEN_REMOVE (or MSG_GEN_REMOVE_CHILD ). After removal, destroy any single object with MSG_META_OBJ_FREE . You can destroy a tree of generic objects with MSG_GEN_DESTROY .
  • ObjInstantiate(), ObjInstantiateForThread()
     These routines create a new instance of a single object. You can then add the object to your generic tree with MSG_GEN_ADD_CHILD . After a child is removed using MSG_GEN_REMOVE (or MSG_GEN_REMOVE_CHILD ), it can be destroyed using MSG_META_OBJ_FREE . The object destroyed in such a manner must not have any children attached to it at the time of destruction.
  • ObjDuplicateResource()
     This routine duplicates an instance of a previously defined object resource. That object block may contain one or more objects. In your .gp file, make sure the resource is marked discard-only . Only use this routine if you want to duplicate an instance of a predefined resource block. After a block duplicated in this manner is removed using MSG_GEN_REMOVE (or MSG_GEN_REMOVE_CHILD ), it can be destroyed using ObjFreeDuplicate() .
  • MSG_GEN_COPY_TREE
    This message copies an entire generic branch headed by the recipient. The branch will be set usable and added automatically without need for a MSG_GEN_ADD_CHILD . A generic branch created in this manner can be removed with MSG_GEN_DESTROY . These messages are useful because they can duplicate and destroy generic trees across object blocks. (The copied branch will reside in one block, however.)

Objects created in these ways must be added using MSG_GEN_ADD_CHILD (except for those created with MSG_GEN_COPY_TREE ).

  • MSG_GEN_ADD_CHILD
    This message adds a generic object that was previously created but not attached to the generic tree. The object may have been created in your .goc file but not attached as a child of any other object. The object may also have been created through ObjInstantiate() or ObjInstantiateForThread() or with ObjDuplicateResource() (see above). Use MSG_GEN_REMOVE (or MSG_GEN_REMOVE_CHILD ) to remove a child added in this manner.

Child/Parent Searches

Manipulating Children Directly

Branch Construction/Destruction

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Modifying GenClass Instance Data | Next: 6 Vardata


Child/Parent Searches

GenClass: 5.1 Generic Trees: Child/Parent Searches

| Up: GEOS SDK TechDocs | Up | Prev: 5 Generic Trees | Next: 5.2 Manipulating Children Directly
MSG_GEN_FIND_CHILD, MSG_GEN_FIND_CHILD_AT_POSITION, MSG_GEN_FIND_PARENT, MSG_GEN_COUNT_CHILDREN

The following messages provide means of searching the generic tree. It is necessary (in most cases) to check whether a child exists for an object before attempting to delete or access the child. Note that it is bad practice to retrieve optrs using these messages and then some time later send messages manipulating these optrs directly because the tree might change. Instead, use the generic dispatch messages MSG_GEN_CALL_PARENT , MSG_GEN_SEND_TO_CHILDREN , etc.

MSG_GEN_FIND_CHILD

word	MSG_GEN_FIND_CHILD(
        optr child);

This message checks whether the object is a child of the recipient. If so, it returns the child's position. If you wish to find out if a specific child currently exists at a certain position, use MSG_GEN_FIND_CHILD_AT_POSITION .

Source: Unrestricted.

Destination: Any generic object.

Parameters: child The optr of child to search for.

Return: The numbered position (zero-based) of the child, or -1 if the specific child is not found.

Interception: Generally not intercepted.

MSG_GEN_FIND_CHILD_AT_POSITION

optr	MSG_GEN_FIND_CHILD_AT_POSITION(
        word position);

This message returns the optr of the child at position , if any. Pass the message the zero-based (zero indicates the first child) position to check. If no child is found, the message returns zero.

Source: Unrestricted.

Destination: Any generic object.

Parameters: position The position (zero-based) of the child to search for.

Return: The optr of child at position passed, if found. Otherwise returns zero.

Interception: Generally not intercepted.

MSG_GEN_FIND_PARENT

optr	MSG_GEN_FIND_PARENT();

This message returns the parent of the recipient object, if any.

Source: Unrestricted.

Destination: Any generic object.

Return: The optr of the parent object. If no parent is found, this message returns zero.

Interception: Generally not intercepted.

MSG_GEN_COUNT_CHILDREN

word	MSG_GEN_COUNT_CHILDREN();

This message returns the number of children of the recipient.

Source: Unrestricted.

Destination: Any generic object.

Return: The number of children the object has.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Generic Trees | Next: 5.2 Manipulating Children Directly


Manipulating Children Directly

GenClass: 5.2 Generic Trees: Manipulating Children Directly

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Child/Parent Searches | Next: 5.3 Branch Construction/Destruction
MSG_GEN_ADD_CHILD, MSG_GEN_REMOVE, MSG_GEN_REMOVE_CHILD, MSG_GEN_MOVE_CHILD, MSG_GEN_ADD_CHILD_UPWARD_LINK_ONLY

The following messages create, move, and remove objects set up directly in your .goc file or created with ObjInstantiate() , ObjInstantiateForThread() or ObjDuplicateResource() . These objects may have children, in which case those children will travel with their parent (and be moved and destroyed with the parent).

When adding or removing children, you typically have to use a CompChildFlags record. This record has two fields, one of which is a dirty flag; the other is a position number indicating a child's position. The record has the following structure:

typedef WordFlags CompChildFlags;
#define CCF_MARK_DIRTY				0x8000 /* high bit */
#define CCF_REFERENCE				0x7FFF /* low 15 bits */
#define CCF_REFERENCE_OFFSET					0
 /* The CCF_REFERENCE field may have any integral
 * number or may be set to one of the following
 * constants:
 *	CCO_FIRST			first child's position
 *	CCO_LAST			last child's position */

The CompChildFlags fields are

CCF_MARK_DIRTY
If set, this flag indicates that the operation in progress should mark the affected objects dirty. Any objects marked dirty will be saved to the state file upon shutdown.
CCF_REFERENCE
This field consists of the lower 15 bits of the word and is a zero-based integer representing the position of the child in its parent's child list. This number cannot be greater than 32767 (0x7fff hex). If the number given is greater than the number of current children, the child will be assumed to be last. For example, a CCF_REFERENCE of four would specify the fifth child of the parent object, or the last child if there are fewer than five children currently. When specifying a position for CCF_REFERENCE, use the CCF_REFERENCE_OFFSET (using the shift operator).

CCO_FIRST and CCO_LAST specify either the first or last child of the parent, respectively. There is no need to use the CCF_REFERENCE_OFFSET in these cases.

MSG_GEN_ADD_CHILD

void	MSG_GEN_ADD_CHILD(
        optr		child,
        CompChildFlags		flags);

This message adds the passed object as a child of the recipient. The child object must not be GS_USABLE before being added to the generic tree. Make sure not to add a child that is already a child of some other parent. It is also illegal to add an object that is already a child of the parent. If necessary, check first if the specific child currently exists using MSG_GEN_FIND_CHILD .

In most cases a routine that adds a new child will follow three phases: checking whether the child exists, adding the child, and setting the child GS_USABLE.

The child object, if already specifically initialized, must be unbuilt before being added to the parent. This ensures that the object will be built out correctly. The internal keyboard search path attribute, GA_KBD_SEARCH_PATH, is also cleared and reset for the child.

Pass this message the optr of the child object to add, along with the CompChildFlags to use. If CCF_MARK_DIRTY is specified, the new linkage will be saved to state when the application is detached. You must pass a CCF_REFERENCE in CompChildFlags to specify the position to add the child. The special constants CCO_FIRST and CCO_LAST, which are special cases of CCF_REFERENCE, will add the object as the first or last child of the parent, respectively.

Note that the object must currently exist. MSG_GEN_ADD_CHILD merely sets up the correct linkage and reconfigures your UI. Note also that successive additions of children using the flag CCO_FIRST will result in a "reverse order" of children (the last added will be the first child, the first added will be the last).

Source: Unrestricted.

Destination: Any generic object.

Parameters: child The optr of the object to add to the current object's children. This child must not be usable.

flags
CompChildFlags to use when adding the child.

Interception: Generally not intercepted. Custom gadgets may intercept to supplement or supersede default functionality.

Code Display 2-20 ObjDuplicateResource() with MSG_GEN_ADD_CHILD

/* This method duplicates a pre-instantiated version of MyMenu.
 * A duplicated object block may also be added using MSG_GEN_ADD_CHILD.*/
@method MyProcessClass, MSG_DUPLICATE_MY_MENU {
    MemHandle newBlock;				/* The handle of the Duplicate block. */
    optr newMenu;				/* The optr of the new menu. */
    GeodeHandle procHan;				/* The geode handle of the process. */
    procHan = GeodeGetProcessHandle();
    newBlock = ObjDuplicateResource(OptrToHandle(@MyMenu), procHan, -1);
            /* Pass the handle of MyMenu's resource as well as the
             * GeodeHandle of the process. Leave the burden thread
             * the same.*/
    /* The new optr is created from the newly created block. */
    newMenu = ConstructOptr(newBlock, OptrToChunk(MyMenu));
    /* Add the duplicated object tree (MyMenu) as the
     * first child of MyPrimary. */
    @call @MyPrimary::MSG_GEN_ADD_CHILD(newMenu, (CCF_MARK_DIRTY | CCO_FIRST));
    /* Then set it usable. Remember, you cannot add a child
     * that is already GS_USABLE. */
    @call @newMenu::MSG_GEN_SET_USABLE(VUM_NOW);
}

MSG_GEN_ADD_CHILD_UPWARD_LINK_ONLY

void	MSG_GEN_ADD_CHILD_UPWARD_LINK_ONLY(
        optr		child);

This message sets a parent link to a passed child object without adding a composite link from the parent to the child. This is a "one way" link in that the parent does not have knowledge of its new child. Therefore, it must be used with caution.

Source: Unrestricted.

Destination: Any generic object.

Parameters: child optr of the child to add with an upward link.

Interception: Generally not intercepted.

MSG_GEN_REMOVE

void 	MSG_GEN_REMOVE(
        VisUpdateMode 		updateMode,
        CompChildFlags 		flags)

This message removes the receiving object from the generic tree. The object to be removed need not be set not usable. Note that all objects below this object will also be removed.

Source: Anyone.

Destination: Any generic object.

Parameters: updateMode Visual update mode. VUM_MANUAL is not allowed.

flags
Set CCF_MARK_DIRTY to mark links dirty.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_REMOVE_CHILD

void	MSG_GEN_REMOVE_CHILD(
        optr		child,
        CompChildFlags		flags);

This message removes the passed object from the recipient. A child must be marked not GS_USABLE in order to be removed. The child must currently exist, so your routine should check this using MSG_GEN_FIND_CHILD .

Pass this message the optr of the child object to be removed along with a word of CompChildFlags . If CCF_MARK_DIRTY is specified, the updated linkage will be saved to state when the application is detached.

Source: Unrestricted.

Destination: Any generic object.

Parameters: child The optr of child to remove. This child must be non-usable in order to be removed. The child must also exist as a child of the recipient.

flags
CompChildFlags to use when removing the child.

Interception: Generally not intercepted. Custom gadgets may intercept to supplement or supersede default functionality.

Code Display 2-21 MSG_GEN_REMOVE_CHILD

/* This sample method removes the MyChild object from its parent, MyParent. */
@method MyProcessClass, MSG_REMOVE_MY_CHILD {
    /* If the child currently exists, mark it not usable and remove it. */
    if (@call @MyParent::MSG_GEN_FIND_CHILD(@MyChild) != -1) {
    @call @MyChild::MSG_GEN_SET_NOT_USABLE(VUM_NOW);
    @call @MyParent::MSG_GEN_REMOVE_CHILD(@MyChild, CCF_MARK_DIRTY);
    }
}

MSG_GEN_MOVE_CHILD

void	MSG_GEN_MOVE_CHILD(
        optr		child,
        CompChildFlags		flags);

This message moves the given object from the location it currently occupies among its siblings to another location among its siblings. The object will still remain a child of the same parent.

Pass this message the optr of the child to move along with a word of CompChildFlags . If you pass the flag CCF_MARK_DIRTY, the new linkage will be saved to state when the application is detached. You must also pass a CCF_REFERENCE so the object will be moved to the position specified. CCO_FIRST and CCO_LAST will move the object to the first or last position, respectively.

If no flags are specified, the object will be moved to the parent object's first position without marking the linkages dirty. Note that for successive moves of children this will result in a "reverse order" of the children.

Note that this message only moves a child among its siblings. To move an object from one parent of the generic tree to another (different) parent, you must use MSG_GEN_REMOVE (or MSG_GEN_REMOVE_CHILD ) and MSG_GEN_ADD_CHILD .

Source: Unrestricted.

Destination: Any generic object.

Parameters: child The optr of the child to move.

flags
CompChildFlags to use when moving the child.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Child/Parent Searches | Next: 5.3 Branch Construction/Destruction


Branch Construction/Destruction

GenClass: 5.3 Generic Trees: Branch Construction/Destruction

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Manipulating Children Directly | Next: 6 Vardata
MSG_GEN_COPY_TREE, MSG_GEN_DESTROY, MSG_GEN_DESTROY_AND_FREE_BLOCK, MSG_GEN_BRANCH_REPLACE_PARAMS, MSG_GEN_BRANCH_REPLACE_OUTPUT_OPTR_CONSTANT

In addition to instantiating and manipulating generic objects one at a time, you can create, move, and destroy entire branches. One way of doing this is copying a resource using ObjDuplicateResource() and sending MSG_GEN_ADD_CHILD to the parent object, passing it the optr of the top object in the tree. This method has the advantage of retaining the same chunk offsets to objects within the duplicated block as in the source block.

Code Display 2-22 Using ObjDuplicateResource()

@start ResourceToCopy;
/* For simplicity, the GenItems in this listing contain no monikers or
 * identifiers. */
@object GenItemGroupClass MyList = {
    GI_comp = @AirplaneEntry, @TrainEntry, @CarEntry, @BicycleEntry;
}
@object GenItemClass AirplaneEntry = {}
@object GenItemClass TrainEntry = {}
@object GenItemClass CarEntry = {}
@object GenItemClass BicycleEntry = {}
@end ResourceToCopy;
@method MyProcessClass, MSG_COPY_MY_RESOURCE {
    MemHandle newBlock;
    newBlock = ObjDuplicateResource(OptrToHandle(@MyList),
        /* Block will be owned by the same geode. */
                    -1,
        /* Block will be run by the same thread. */
                    -1);
    @call MyPrimary::MSG_GEN_ADD_CHILD(ConstructOptr(newBlock,
                    OptrToChunk(@MyList));
}

Another way to duplicate groups of objects is with MSG_GEN_COPY_TREE . This message greatly simplifies generic tree construction. Those branches of the tree which are roughly similar can be duplicated with one message instead of several.

In many cases, it might be useful to create a UI resource template. This template should contain objects but should not contain object-specific information such as visual monikers, optrs, etc., as this information may be different for different instances of the branch. You can then duplicate these templates with MSG_GEN_COPY_TREE . The trees can then be updated to reflect their unique data either object by object or by using MSG_GEN_BRANCH_REPLACE_PARAMS , which will replace instance data within an entire branch.

To set up a template, create the generic tree you wish to function as a template, making sure that the top-level object of that tree is not usable (~GS_USABLE). Then use MSG_GEN_COPY_TREE to copy the tree to the proper location. You can then set the other instance data within the method. Finally, to make that tree appear on screen, set the top-level object GS_USABLE. You can remove any tree created with MSG_GEN_COPY_TREE with MSG_GEN_DESTROY . If you use ObjDuplicateResource() to copy a resource block, you can use ObjFreeDuplicate() to remove it. If the generic branch to be destroyed resides completely within a single block, you may send MSG_GEN_DESTROY_AND_FREE_BLOCK to the top object in that generic branch.

MSG_GEN_COPY_TREE

optr	MSG_GEN_COPY_TREE(
        MemHandle destBlock,
        ChunkHandle parentChunk,
        word flags);

This message copies an entire branch of a generic object tree, starting at the object first called. Pass this message the handle of the destination object block. The object sent the message must not be GS_USABLE.

You may pass parentChunk null if you wish the block to remain unattached to the generic tree. Note that if a generic tree is copied using MSG_GEN_COPY_TREE , the objects contained therein not only will have a new resource handle but will also have new chunk handles. Note that this is different from using ObjDuplicateResource() , which will keep the same chunk offsets into the object block.

Unlike ObjDuplicateResource() , this message will copy the entire branch desired, even if those objects reside in separate resources. This message is therefore more flexible than ObjDuplicateResource() , which only copies a given object block.

GenClass provides the default behavior of broadcasting this message down a generic tree, where each object will make a copy of itself in the destination block. It creates an object chunk the same size as the object chunk being copied, copies over the entire contents of any instance fields and creates an outside chunk for a visual moniker, if any.

If you have a subclass of a generic object which references (and therefore might need to create) a chunk outside the object, you must be sure to intercept this message and allocate, copy over, and update any references to this chunk.

Source: Unrestricted.

Destination: Any generic object.

Parameters: destBlock The block to copy the tree into. This block cannot be the same as the source object block.

parentChunk
The chunk handle of a generic object in the destination block. The recipient of this message will be copied into parentChunk . If zero, copy tree into block without linking it to a particular chunk.
flags
CompChildFlags to use when adding the tree.

Interception: This message is handled by all generic objects. If you wish to intercept this message, you should first call the superclass to copy over the GenClass instance data and then copy over any lmem chunks that are referenced by instance data of the intercepting class.

Code Display 2-23 MSG_GEN_COPY_TREE

/* Create a template resource block. For demonstration purposes, this block will
 * only contain one object, a GenItem. All declared resources must also be
 * declared in an application's corresponding .gp file. */
@start MyTemplate;
@object GenItemClass TemplateItem = {
    GI_states = @default & ~GS_USABLE;					/* Make sure it is NOT usable. */
}
@end MyTemplate;
/* Start the Interface resource block */
@start Interface;
@object GenItemGroupClass MyList = { }
                /* Object the template above will be added to. */
@end Interface;
@method MyProcessClass, MSG_COPY_A_LIST_ITEM {
    optr		newListEntry;
    /* This call copies TemplateItem to the location at MyList. The macros
     * OptrToHandle and OptrToChunk are used to cast the optr of MyList into
     * the proper type for MSG_GEN_COPY_TREE. This new linkage will be marked
     * dirty, and the child will become the first child of MyList. */
    newListEntry = @call TemplateItem::MSG_GEN_COPY_TREE(
                OptrToHandle(@MyList),
                OptrToChunk(@MyList),
                (CCF_MARK_DIRTY | CCO_FIRST));
    /* The newListEntry is set usable to bring it up on screen. */
    @call newListEntry::MSG_GEN_SET_USABLE(VUM_NOW);
}

MSG_GEN_DESTROY

void	MSG_GEN_DESTROY(
        VisUpdateMode		updateMode,
        word		flags);

This message destroys a generic branch starting at the object called. The message sets all required generic objects not usable, removes them from the generic tree, and destroys them, including any chunks associated with the specific object destroyed. This message is the safest way to destroy a generic branch.

The message will leave the block that the objects previously resided in intact (except for objects removed, of course), but any chunks coming from those resources will be marked dirty and will be resized to zero.

It is usually a good idea to destroy generic branches that are not needed to be saved to state when an application detaches or exits. This ensures that when an application is reattached, it will not contain the previous links. This keeps the state file more compact and avoids the possibility of a child not being saved to state while retaining the parent's child pointer.

The only valid flag in CompChildFlags is CCF_MARK_DIRTY, which will mark the links as dirty and save them to state.

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode VisUpdateMode to determine when the generic tree is to be visually updated. May not be VUM_MANUAL.

flags
CompChildFlags to use when destroying the branch (either CCF_MARK_DIRTY or zero).

Interception: This message is handled by all generic objects. If you wish to intercept this message, you should first destroy any chunks that are referenced by instance data of the intercepting class and then call the superclass to destroy the object's GenClass instance data.

MSG_GEN_DESTROY_AND_FREE_BLOCK

Boolean	MSG_GEN_DESTROY_AND_FREE_BLOCK();

This is a utility message used to destroy a generic branch which resides completely within one block. The block will be freed. The object called with this message must be the only top object in the block. This message is called by UserDestroyDialog() .

If any object within the block resides on the application active list, or if the object is not a window, a slow, non-optimized approach is taken: the branch is set not usable, removed from the generic tree, and sent MSG_META_BLOCK_FREE . If no object of the block can be found on this list, an optimized approach is taken: the window is taken off the screen; FTVMC, mouse, etc. exclusives released; the linkage changed to be one-way upward; and the block sent MSG_META_BLOCK_FREE . In other words, the whole unbuild process is carefully avoided.

Source: Unrestricted

Destination: Top object within a block to be freed.

Return: true if the message was handled, false otherwise.

Interception: Generally not intercepted.

MSG_GEN_BRANCH_REPLACE_PARAMS

void	MSG_GEN_BRANCH_REPLACE_PARAMS(@stack
        BranchReplaceParamType				type,
        dword				replaceParam,
        dword				searchParam);

This message travels down a generic branch to all of an object's children; the message replaces all instance data specified in type with replacement instance data. A typical way to implement this is to set up a template UI branch with MSG_GEN_COPY_TREE . Most often, you will use this message to search and replace optrs set within your template. This is easily done by setting these optrs to certain constant values and then searching for those values in type . If you only wish to replace your destination optrs, use MSG_GEN_BRANCH_REPLACE_OUTPUT_OPTR_CONSTANT , which is just a specialized case of this message.

When replacing optrs, the constant BRPT_DUMMY_OPTR_START should be used as the searchParam value. If multiple optrs are to be replaced, enumerated values based on that value should be used for other optrs. These values will be constants, and since the TravelOption types are also constants, care must be taken to replace these dummy values before anything else is done on the generic branch; otherwise, their output may be interpreted as a TravelOption .

This message may not be supported by several subclasses of GenClass .

Source: Unrestricted.

Destination: Any generic object.

Parameters: searchParam Specifies the search parameter. Typically this search parameter is a pre-set constant used to identify the instance data to replace. The search parameter must be a dword.

replaceParam
Specifies the replacement parameter. Any instance data in the generic branch that matches searchParam will be replaced with the data specified in replaceParam . The data must be a dword.
type
A value of BranchReplaceParamType . The only value supported is BRPT_OUTPUT_OPTR, which specifies that all optrs with values matching searchParam should be replaced by the value in replaceParam .

Interception: This message is handled by most generic objects (although not all). If you wish to intercept this message, you should first call the superclass to search and replace any GenClass instance data, if applicable, and then search and replace any instance data in the intercepting class.

MSG_GEN_BRANCH_REPLACE_OUTPUT_OPTR_CONSTANT

void	MSG_GEN_BRANCH_OUTPUT_OPTR_CONSTANT(
        optr	replacementOptr,
        word	searchConstant);

This message is a special case of MSG_GEN_BRANCH_REPLACE_PARAMS where the BranchReplaceParamType is BRPT_OUTPUT_OPTR with a search parameter of a constant value set up beforehand. You must pass this message the optr to replace the search constant found.

Source: Unrestricted.

Destination: Any generic object.

Parameters: replacementOptr The optr to replace all instance fields with destinations matching the searchConstant .

searchConstant
The constant to search for and replace with the replacementOptr .

Interception: Generally not intercepted. This message invokes MSG_GEN_BRANCH_REPLACE_PARAMS so intercept that instead.

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Manipulating Children Directly | Next: 6 Vardata


Vardata

GenClass: 6 Vardata

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 Generic Trees | Next: 7 Special Message Passing

Within GenClass , much of your instance data will appear within the basic instance fields allocated with @instance . These instance data fields are fixed; each generic object you create will contain each one of these instance fields. You may also make use of special instance data (variable data, also called "vardata") using the object variable data storage mechanism. See the GEOS Programming chapter for complete information on variable data.

Vardata performs many of the same functions that fixed instance data does. However, vardata differs significantly from fixed instance data in that it will only occupy memory if it is in fact declared. You may also only access vardata entries through special kernel routines and messages. For generic objects, most vardata is either in the form of attributes (beginning with ATTR_) or hints (beginning with HINT_).

Optional Attributes (ATTRs)

Hints to the Specific UI

Dynamically Managing VarData

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 Generic Trees | Next: 7 Special Message Passing


Optional Attributes (ATTRs)

GenClass: 6.1 Vardata: Optional Attributes (ATTRs)

| Up: GEOS SDK TechDocs | Up | Prev: 6 Vardata | Next: 6.2 Hints to the Specific UI

Vardata attribute entries begin with the form ATTR_attributename (as in ATTR_GEN_DESTINATION_CLASS). You can initialize this data within your Goc object declaration. ATTR s within GenClass are available for use by any generic object. Several subclasses of GenClass also contain their own ATTR vardata entries. In GenClass no vardata attributes or hints are set by default.

ATTRs often behave as additional instance data that just happens to be vardata (and therefore optionally added). For generic objects, these should always be considered part of the generic state of an object and therefore may be saved out to the state file. You may, of course, decide not to save any such fields to the state file.

Temporary vardata to be used by an object class can be specified using the format TEMP _tempname. Any data set up with TEMP in a generic object should not be saved to state and should not be considered part of the API but rather an internal field. In almost all cases, you will not need to access such vardata entries.

For information on manipulating the variable length instance fields dynamically, along with a list of valid hints for GenClass , see Dynamically Managing VarData .

Destination Classes

 ATTR_GEN_DESTINATION_CLASS

ATTR_GEN_DESTINATION_CLASS specifies the object class that should handle messages sent out by this object. Typically, an object marked with this ATTR will not have a specific destination optr, but instead a destination path defined by a TravelOption . The message for a generic object with a destination class travels down the generic tree along the path specified in the TravelOption until it encounters an object class of type ATTR_GEN_DESTINATION_CLASS ; at that point, the message will be handled.

ATTR_GEN_DESTINATION_CLASS takes an argument of type DestinationClassArgs . This type contains a pointer to a ClassStruct . You may have to cast the class name into a ClassStruct type when setting ATTR_GEN_DESTINATION_CLASS.

 typedef struct {
    ClassStruct		*  DCA_class;
} DestinationClassArgs;

Code Display 2-24 Using ATTR_GEN_DESTINATION_CLASS

/* This object will send MSG_META_CUT to the application object and follow the
 * target path until it encounters a VisTextClass object. At that point, the
 * message will be handled. */
@object GenTriggerClass MyTrigger = {
    GI_visMoniker = "This trigger cuts text from the target object."
    GTI_actionMsg = MSG_META_CUT;
    GTI_destination = (TO_APP_TARGET);
/* The class pointer points to a ClassStruct structure. */
    ATTR_GEN_DESTINATION_CLASS = { (ClassStruct *)&VisTextClass };
}
/* To set up a destination class as any generic object, set
 * ATTR_GEN_DESTINATION_CLASS to GenClass. This interaction sends
 * MSG_GEN_SET_NOT_USABLE to the first focus object below the GenInteraction. */
@object GenTriggerClass MyTrigger = {
    GI_visMoniker = "Remove the Focus object";
    GTI_actionMsg = MSG_GEN_SET_NOT_USABLE;
    GTI_destination = (TO_FOCUS);
    ATTR_GEN_DESTINATION_CLASS = { (ClassStruct *)&GenClass };
}
   typedef enum {
        /* These values may be used as normal
         * TravelOptions, and have been set up
         * so that they will have no value in
         * common with normal TravelOptions. */
    TO_GEN_PARENT=_FIRST_GenClass,
    TO_FOCUS,
    TO_TARGET,
    TO_MODEL,
    TO_APP_FOCUS,
    TO_APP_TARGET,
    TO_APP_MODEL,
    TO_SYS_FOCUS,
    TO_SYS_TARGET,
    TO_SYS_MODEL,
} GenTravelOption;

The TravelOption types provided with GenClass are additional enumerations of the types supplied with MetaClass . MetaClass provides the types TO_NULL, TO_SELF, TO_OBJ_BLOCK_OUTPUT and TO_PROCESS. Therefore, any generic object can use these types or the further enumerations included here.

These TravelOption types typically replace a generic object's destination with a path for a message to be delivered along. Most often, these TravelOption s are used in conjunction with the vardata attribute ATTR_GEN_DESTINATION_CLASS. Together, they allow a message to follow a path specified in TravelOption until it encounters an object of a class specified in ATTR_GEN_DESTINATION_CLASS . This allows an object to generically send a message down an output path without knowing a specific destination.

Initialization File Management

MSG_GEN_LOAD_OPTIONS, MSG_GEN_SAVE_OPTIONS, ATTR_GEN_INIT_FILE_KEY, ATTR_GEN_INIT_FILE_CATEGORY, ATTR_GEN_INIT_FILE_PROPAGATE_TO_CHILDREN, ATTR_GEN_USES_HIERARCHICAL_INIT_FILE_CATEGORY

Your object may request information from the GEOS.INI file upon startup. Each object on the appropriate GenApplication GCN list may receive a MSG_GEN_LOAD_OPTIONS upon receiving MSG_META_ATTACH (and a MSG_GEN_SAVE_OPTIONS upon MSG_META_SAVE_OPTIONS ). Various generic objects intercept this message and perform actions according to the information provided.

Your object may also access information in the GEOS.INI file as it is being attached by including ATTR_GEN_INIT_FILE_CATEGORY of the GEOS.INI category you are interested in and the ATTR_GEN_INIT_FILE_KEY of the specific keyword you want the data for. If a file key, but no file category, exists within an object declaration, then the system will query the application object for its ATTR_GEN_INIT_FILE_CATEGORY.

Normally only the GenApplication object and any objects in its GCN list will receive MSG_GEN_LOAD_OPTIONS and MSG_GEN_SAVE_OPTIONS . If you wish other children below these objects to also receive these messages (and therefore request or save information from the GEOS.INI file) you can add the attribute ATTR_GEN_INIT_FILE_PROPAGATE_TO_CHILDREN. Any objects with this attribute will send these messages on down the generic tree to their children. Any children may then also contain this attribute and pass the messages on down to their children, etc.

Normally, the object itself and then the GenApplication object is queried for an ATTR_GEN_INIT_FILE_CATEGORY. If instead, the object wants a generic upward query of the object's parents, you can attach ATTR_GEN_USES_HIERARCHICAL_INIT_FILE_CATEGORY to the object. In this case, the object will be queried for its file category, and then query up the generic tree for the first object encountered with an ATTR_GEN_INIT_FILE_CATEGORY. In this manner, one parent may contain a file category and several children may contain different file keys within that category.

Because this method is recursive, it is slow and therefore should be avoided. Most applications should only need one init file category.

MSG_GEN_LOAD_OPTIONS

void	MSG_GEN_LOAD_OPTIONS(
        GenOptionsParams		*params);

This message instructs the generic object to load a value from the GEOS.INI file. It is called automatically by the handler in GenClass for MSG_META_LOAD_OPTIONS . The message scans the object's vardata for an ATTR_GEN_INIT_FILE_KEY and an ATTR_GEN_INIT_FILE_CATEGORY to scan the .INI file. If no file category is found in the object's vardata, the handler will search up the generic tree for a parent with an ATTR_GEN_INIT_FILE_CATEGORY.

You may send this message, passing it the proper GenOptionsParams . The return value must be cast into the proper type of whatever the entry within the file key is. This is a null-terminated text string.

Source: Sent by GenClass on MSG_META_ATTACH . GenClass generates the GenOptionsParams structure by looking in the object's vardata (and by querying up the generic tree, if necessary).

Destination: Any generic object.

Parameters: params GenOptionsParams structure (automatically generated by GenClass ). See MSG_GEN_SAVE_OPTIONS .

Return: Return values must be cast into the proper type of whatever the entry of category and file key is.

Interception: Various generic classes intercept this and provide default behavior

MSG_GEN_SAVE_OPTIONS

void	MSG_GEN_SAVE_OPTIONS(
        GenOptionsParams		*params);

This message instructs the generic object to save its options to the initialization file.

Source: Sent by GenClass on MSG_META_SAVE_OPTIONS . GenClass generates the proper GenOptionsParams structure by looking in the object's vardata (and querying up the generic tree, if necessary).

Destination: Any generic object.

Parameters: params GenOptionsParams structure (automatically generated by GenClass ).

Return: Return values must be cast into the proper type of whatever the entry of category and file key is.

Interception: Various generic classes intercept this and provide default behavior

Structures: The GenOptionsParams structure is defined as follows:

  typedef struct {
    char	GOP_category[INIT_CATEGORY_BUFFER_SIZE];
    char	GOP_key[INIT_CATEGORY_BUFFER_SIZE];
} GenOptionsParams;

Altering Default Geometry Management

ATTR_GEN_POSITION, ATTR_GEN_POSITION_X, ATTR_GEN_POSITION_Y

You may wish, in rare cases, to hard-wire a generic object's position relative to its parent. You can do this by including one of these attributes with a position offset from the parent. These attributes supersede the geometry manager; because of this, you should exercise extreme caution whenever using these attributes. You should only use these attributes in the rare cases when the geometry manager (and hints) cannot accommodate your geometry concerns.

ATTR_GEN_POSITION takes a Point argument, specifying the x and y offset (in points) from the parent's top left corner. This point will become the object's top left corner.

ATTR_GEN_POSITION_X specifies the horizontal (x) offset (in points) from the left edge of the parent. The vertical (y) offset will be left to the specific UI (or ATTR_GEN_POSITION_Y) to determine.

ATTR_GEN_POSITION_Y specifies the vertical (y) offset (in points) from the top edge of the parent. The horizontal (x) offset will be left to the specific UI (or ATTR_GEN_POSITION_X) to determine.

Several hints also accomplish similar geometry overrides. HINT_ALIGN_TOP_EDGE_WITH_OBJECT aligns an object's top edge with the top edge of the object (optr) this hint is set to. Similarly, HINT_ALIGN_LEFT_EDGE_WITH_OBJECT , HINT_ALIGN_RIGHT_EDGE_WITH_OBJECT , and HINT_ALIGN_BOTTOM_EDGE_WITH_OBJECT all line up an object's edge with the same edge of the object these hints are set to. One of these hints can be used in each direction. The same concerns over superceding the default geometry manager in the above attributes are valid here as well. Therefore, avoid using these hints if possible.

Altering Delayed Mode Activity

ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED, ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_ENABLED

Most generic objects act on changes made by the user. Some generic objects may not immediately react to these changes; instead these objects allow the user to select a variety of settings and then later "apply" these changes. These generic objects operate in delayed mode and each has an "apply message" associated with it. For these objects, the user may alter the state of the object or its children, but the application only sends out the action to perform those changes when the "apply" message is sent out.

For example, a dialog box may contain a list of settings that represent the state of a paragraph in a word processor. If the user ever actually changes those settings, the dialog box is marked as modified, but the changes are not actually made until the object receives notification to apply its changes (send out its apply message).

By default, these generic objects check whether their state has been modified since the last apply message has been sent out. If their state has not been modified and they receive a request to apply changes made, they instead will ignore the request. Likewise, objects set not enabled will ignore the request. ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED and ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_ENABLED override this behavior, telling the generic object to send out its apply message regardless of the modified or enabled state of a generic object.

In the above example, this would mean that whenever the dialog box receives notification to apply its changes it would not check whether its settings have been altered since the last time it applied its changes. Instead it would apply these changes anyway.

Notification of Visibility

ATTR_GEN_VISIBILITY_DATA, ATTR_GEN_VISIBILITY_MESSAGE, ATTR_GEN_VISIBILITY_DESTINATION

If a generic object has its GA_NOTIFY_VISIBILITY bit set, it will notify the GenApplication object when it becomes visible or not visible. The object does this by sending MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION to the GenApplication object, passing the sending object's optr as data.

You can alter this default behavior by including one or more attributes. ATTR_GEN_VISIBILITY_MESSAGE specifies a different message than MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION ; ATTR_GEN_VISIBILITY_DESTINATION specifies a different destination to send visibility notification to than the GenApplication object; ATTR_GEN_VISIBILITY_DATA specifies a different word of data to send out than the default data (the optr of the sending object). These attributes will have no effect on objects not marked GA_NOTIFY_VISIBILITY.

Generic Properties

ATTR_GEN_PROPERTY, ATTR_GEN_NOT_PROPERTY

Objects that exhibit properties (List Objects, GenValues, GenTexts, etc.) sometimes do not exhibit immediate effects when the user interacts with the object. These objects act in what is known as delayed mode ; they only apply their properties when their object receives a MSG_GEN_APPLY.

Properties objects such as these are usually placed within a GIT_PROPERTIES GenInteraction object. The GIT_PROPERTIES dialog box provides an "Apply" trigger to get the objects to apply their properties. You can, however, change this default behavior, though it is uncommon to need to do so.

ATTR_GEN_PROPERTY, when added to an object that exhibits properties, allows an object to act like it is within a properties dialog box even if it normally operates in immediate mode. That is, it will not apply its changes until explicitly told to do so (using MSG_GEN_APPLY).

ATTR_GEN_NOT_PROPERTY allows an object within a GIT_PROPERTIES dialog box to act like it is not within such a dialog box. That is, any user interaction with the object will result in an immediate application of those changes.

Window Management

GenClass supplies several optional attributes that affect the display of windows within the user interface. Windowed objects include GenPrimarys, GenDisplays, and GenInteractions (as dialog boxes).

The optional attributes that follow are divided into two groups. Window layering affects how windowed objects are arranged on the screen; i.e., where they appear and whether they appear above or below other windowed objects. In general, this layering behavior is only determined for windowed objects having the same parent, i.e., windows having the same parent can be custom layered, and their parents will be layered among its siblings.

There also exist optional attributes to alter the manner in which input travels within a window. These optional attributes allow the application to provide floating keyboards for pen input.

Window Layering

ATTR_GEN_WINDOW_CUSTOM_LAYER_ID, ATTR_GEN_WINDOW_CUSTOM_WINDOW_PRIORITY, ATTR_GEN_WINDOW_CUSTOM_LAYER_PRIORITY, ATTR_GEN_WINDOW_CUSTOM_PARENT, ATTR_GEN_WINDOW_CUSTOM_WINDOW

ATTR_GEN_WINDOW_CUSTOM_LAYER_ID sets a unique layer identifier for the windowed object. All objects with a common identifier are considered part of the same window layer. These layers can then be manipulated as a unit. By convention, these IDs are usually set to the handles owned by the application; this avoids collisions with other application window layer IDs.

If a null layer ID is specified, the handle of the block the object lies in will be selected as the layer ID at run-time. This is the common method of assigning layer IDs.

ATTR_GEN_WINDOW_CUSTOM_LAYER_PRIORITY allows a windowed object to set its layer priority to a custom value. The optional attribute expects an argument of type LayerPriority . Note that all windowed objects with the same Layer ID must have the same layer priority; therefore, this attribute must be used with care. It should only be used if a layer ID declared with ATTR_GEN_WINDOW_CUSTOM_LAYER_ID is also used.

ATTR_GEN_WINDOW_CUSTOM_WINDOW_PRIORITY sets a custom window priority for this specific window among all other windows of the same layer ID. This optional attribute expects an argument of type WindowPriority .

ATTR_GEN_WINDOW_CUSTOM_PARENT allows windowed objects to set a custom parent window. Because window layering applies only to the ordering of windows having the same parent, a window with a custom parent will have its layer ID, layer priority, and window priority compared to other windows having the same parent as ATTR_GEN_WINDOW_CUSTOM_PARENT.

If ATTR_GEN_WINDOW_CUSTOM_PARENT is set null, this indicates that the system screen window should be the parent. Because this window is the parent window for all base windows in the system, an object setting the screen window as its custom parent will be able to appear on top of (or below) all other windows in the system (that do not have a matching layer ID). This is very low-level behavior and should be used with caution, however.

Window Input Management

ATTR_GEN_WINDOW_ACCEPT_INK_EVEN_IF_NOT_FOCUSED, ATTR_GEN_WINDOW_KBD_OVERRIDE, ATTR_GEN_WINDOW_KBD_POSITION, MSG_GEN_SET_KBD_OVERRIDE, MSG_GEN_SET_KBD_POSITION

ATTR_GEN_WINDOW_ACCEPT_INK_EVEN_IF_NOT_FOCUSED indicates that the object should accept ink even if it doesn't have the focus. In general, presses on non-focused windows will never turn into ink. If this attribute is present, then presses on the window can turn into ink. The normal mechanism for determining if a press should be ink is then followed.

ATTR_GEN_WINDOW_KBD_OVERRIDE indicates the manner in which a floating keyboard is provided for a given window. This optional attribute expects an argument of type of KeyboardOverride . This attribute specifies whether a floating keyboard is provided for text input via a pen device.

The following KeyboardOverride values are valid:

KO_NO_KEYBOARD
This type indicates that the window should act as if none of its children accepts text input; no floating keyboard will be made available.
KO_KEYBOARD_REQUIRED
This type indicates that the window should act as if one of its children requires text input; a floating keyboard will be brought on-screen whenever the window gains the focus.
KO_KEYBOARD_EMBEDDED
This type indicates that the application is directly providing the keyboard; no system-created floating keyboard will be provided.

You can change the KeyboardOverride exhibited by a windowed object by sending the object MSG_GEN_SET_KBD_OVERRIDE.

The following rules will ensure that a keyboard will be present whenever the user interacts with a display requiring a keyboard: Set the KO_KEYBOARD_REQUIRED bit for any GenDisplay which should be accompanied by a floating keyboard. If all GenDisplays in your geode require a floating keyboard, then set this bit for each of them. GenDisplays that contain embedded keyboards should have the KO_KEYBOARD_EMBEDDED bit set instead of the KO_KEYBOARD_REQUIRED bit.

If you want a keyboard to appear visible even when no GenDisplays are open, mark the GenPrimary KO_KEYBOARD_REQUIRED and mark each GenDisplay KO_NO_KEYBOARD.

ATTR_GEN_WINDOW_KBD_POSITION specifies the default position for the floating keyboard to appear when the window gains the focus. If the user moves the floating keyboard, this attribute is updated to reflect the new position and the keyboard will appear at that position when brought up in the future.

You can change this keyboard position by sending the object MSG_GEN_SET_KBD_POSITION, passing it the Point to position the keyboard at.

MSG_GEN_SET_KBD_OVERRIDE

void	MSG_GEN_SET_KBD_OVERRIDE(
        KeyboardOverride 		override);

This message sets an object's ATTR_GEN_WINDOW_KBD_OVERRIDE to the passed KeyboardOverride value.

Source: Unrestricted.

Destination: Any windowed generic object.

Parameters: override KeyboardOverride .

Interception: Generally not intercepted.

MSG_GEN_SET_KBD_POSITION

void	MSG_GEN_SET_KBD_POSITION(
        sword		xCoord,
        sword		yCoord);

This message sets an object's ATTR_GEN_WINDOW_KBD_POSITION to the passed Point values.

Source: Unrestricted.

Destination: Any windowed generic object.

Parameters: xCoord X coordinate to position the keyboard (relative to the window).

yCoord
Y coordinate to position the keyboard (relative to the window).

Interception: Generally not intercepted.

Help Management

The Help system is discussed in full in the Help chapter. You should read that chapter if you wish to include help within your application. The information provided here is mostly an overview of that information.

Help Files

ATTR_GEN_HELP_FILE, ATTR_GEN_HELP_TYPE, ATTR_GEN_HELP_FILE_FROM_INIT_FILE, ATTR_GEN_HELP_CONTEXT

ATTR_GEN_HELP_FILE stores the help file associated with this object. Help files are generated by the help editor (a modified GeoWrite) from GeoWrite documents and are located in USERDATA\HELP.If the help system queries this object for a help file and this optional attribute does not exist, it will query up the generic tree for the first object with an ATTR_GEN_HELP_FILE.

An object can use ATTR_GEN_HELP_FILE_FROM_INIT_FILE to specify that its help file should be taken from the GEOS.INI file. If this attribute is used, the help controller will look in the GEOS.INI file for a category of the same name as the application and a key named "helpfile." Thus, to set the help file to "My Own Help File" for the HelpSamp application, you could add the following to your GEOS.INI file:

[HelpSamp]
helpfile = My Own Help File

If, however, no object has ATTR_GEN_HELP_FILE_FROM_INIT_FILE , this init file entry will not be noticed by the help controller.

ATTR_GEN_HELP_TYPE stores the HelpType associated with this object. If the help system queries this object for a help type and this optional attribute does not exist, it will query up the generic tree for the first object with an ATTR_GEN_HELP_TYPE.

Each help file must have several contexts named within the file. These contexts are set up when the help file is written. When an object brings up help, it should reference the specific context for which the user needs help. This context is stored within the object's ATTR_GEN_HELP_CONTEXT field. Usually this optional attribute is placed on a windowed GenInteraction (dialog box).

If an object needs to set a different help context for itself at run-time, it can use MSG_META_DELETE_VAR_DATA and MSG_META_ADD_VAR_DATA to alter the contents of the object's ATTR_GEN_HELP_CONTEXT.

Focus Help

ATTR_GEN_FOCUS_HELP, ATTR_GEN_FOCUS_HELP_LIB

ATTR_GEN_FOCUS_HELP is currently unsupported. This help mechanism is a simple, scaled down version of the previously mentioned help system. A fixed area at the bottom of the screen on certain displays acts as a help text area. An object with ATTR_GEN_FOCUS_HELP indicates that this text area should display the its help text when this object gains the focus within the application. This field expects an optr pointing to the required text.

ATTR_GEN_FOCUS_HELP_LIB is similar to ATTR_GEN_FOCUS_HELP but only applies to objects exported from a library(e.g., controllers).

Default Monikers

ATTR_GEN_DEFAULT_MONIKER, GenDefaultMonikerType

ATTR_GEN_DEFAULT_MONIKER specifies a default moniker for this object. Default monikers are generally used for gstring monikers that occur several places within the system (i.e., they are stored outside of the application). ATTR_GEN_DEFAULT_MONIKER expects an argument of type GenDefaultMonikerType .

typedef enum {
    GDMT_LEVEL_0,
    GDMT_LEVEL_1,
    GDMT_LEVEL_2,
    GDMT_LEVEL_3,
    GDMT_HELP,
    GDMT_HELP_PRIMARY
} GenDefaultMonikerType;

The GDMT_LEVEL monikers correspond to the gstring monikers used to indicate the current user level. GDMT_HELP corresponds to the default help trigger moniker. GDMT_HELP_PRIMARY corresponds to the special help trigger for a GenPrimary object.

Feature Links

ATTR_GEN_FEATURE_LINK

This optional attribute indicates that a feature within a GenControl object maps to multiple generic trees within the child block. If a feature in a GenControl so marked is turned off, for example, the controller will remove the feature from the first generic tree associated with this feature and then will remove the feature from the link to the next generic tree. Controllers and features are discussed more fully in the Controllers chapter.

Generic Paths

ATTR_GEN_PATH_DATA, MSG_GEN_PATH_SET, MSG_GEN_PATH_GET, GenFilePath

GenClass is able to keep track of file path information. GenClass does not use this information itself, but maintains it so that various subclasses (e.g., the file selector and document control) will be able to use the same instance fields and messages.

Because handlers for all these messages are implemented in GenClass , it is possible to store a path with any generic object, whether the particular object normally does anything with the file system or not.

The path itself is stored in the object's vardata in the ATTR_GEN_PATH_DATA field. In the absence of such a field, GenClass will use SP_TOP as the standard path. Subclasses can change this default by intercepting MSG_META_INITIALIZE_VAR_DATA , filling in the GenFilePath structure after it has called the superclass (which will actually allocate the attribute).

The ATTR_GEN_PATH_DATA field contains the following structure;

 typedef struct {
    DiskHandle 		GFP_disk;
    PathName 		GFP_path;
} GenFilePath;

GFP_ disk stores the handle of the disk on which the path resides. This value may be initialized to a StandardPath constant. GFP_ path stores the absolute path to the directory; this path may be a relative path if GFP_ disk is a StandardPath directory.

You can retrieve an object's current path with MSG_GEN_PATH_GET. You can alter an object's path with MSG_GEN_PATH_SET.

MSG_GEN_PATH_SET

Boolean 	MSG_GEN_PATH_SET(
        char 		*path,
        DiskHandle 		disk);

This message sets the path associated with the object. Normally, a complete path name must be passed. Note that this path string should not include a drive specifier, as this is implied by the passed disk handle. If the passed DiskHandle is actually a StandardPath value, then the path string will be taken to be relative to this standard path.

Source: Unrestricted.

Destination: Generic object which has an associated path.

Parameters: path Null-terminated pathname; may not be in the same block as the object receiving this message. (If null, default handler will use root directory of disk handle or directory associated with standard path.)

disk
Disk handle of path, or StandardPath constant.

Return: Returns true if error.

Interception: Generally not intercepted. If the object is specifically grown, the message will be forwarded to the specific class after it has been acted on by GenClass .

MSG_GEN_PATH_GET

Boolean 	MSG_GEN_PATH_GET(
        char 		*buffer,
        word 		bufSize);

This message returns a null-terminated complete path (no drive letter--drive is implied by the disk handle) for the object. Note that if the object is within a standard directory, the disk handle will be a StandardPath constant and the path will appear relative.

Source: Unrestricted.

Destination: Generic object which has an associated path.

Parameters: buffer Pointer to character buffer which will be filled with return value.

bufSize
Size of the above buffer.

Return: Returns true if error (if the path won't fit in the passed buffer or is invalid).

buffer
Null-terminated pathname.

Interception: Generally not intercepted.

MSG_GEN_PATH_GET_BLOCK

@alias (MSG_GEN_PATH_GET) MemHandle 	MSG_GEN_PATH_GET_BLOCK(
        char 		*buffer, 	/* This must be NULL */
        word 		bufSize);

This message returns the handle of a block containing the path.

Source: Unrestricted.

Destination: Generic object which has an associated path.

Parameters: buffer This must be NULL.

bufSize
This argument is not used.

Return: Handle of block containing path.

Interception: Generally not intercepted.

MSG_GEN_PATH_GET_DISK_HANDLE

DiskHandle MSG_GEN_PATH_GET_DISK_HANDLE();

This message returns the disk handle for the path bound to the object.

Source: Unrestricted.

Destination: Generic object which has an associated path.

Parameters: None.

Return: Disk handle associated with path, or StandardPath if object is associated with a standard directory.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 6 Vardata | Next: 6.2 Hints to the Specific UI


Hints to the Specific UI

GenClass: 6.2 Vardata: Hints to the Specific UI

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 Optional Attributes (ATTRs) | Next: 6.3 Dynamically Managing VarData

Hints permit the developer to add suggested behavior to the user interface without requiring the specific UI to implement that functionality. Hints therefore provide the specific UI with less stringent guidelines than other attributes. The specific UI has the option of completely ignoring a hint if it cannot understand it or implement it in the requested fashion.

For example, some hints deal with the spacial arrangement of objects and can be ignored if the specific UI cannot accommodate their requests. Developers should use hints to add suggested user interface behavior throughout their UI. This suggested use, however, should not be crucial to their application.

For example, the hint HINT_LIST_CHECKBOXES has no effect under OSF/Motif because the OSF/Motif specification does not include checkboxes. However, in some other specific UI (OpenLook, for example) that feature may be implemented.

Code Display 2-25 Setting Hints

@object GenInteractionClass MyInteraction = {
    GI_comp = @OneTrigger, @TwoTrigger, @ThreeTrigger;
    /* Hints are added directly in an object's declaration. Note that each
     * vardata entry will expand the size of the object in memory. */
     HINT_ORIENT_CHILDREN_HORIZONTALLY;
    /* This hint instructs the specific UI to arrange the object's children
     * in one or more horizontal rows, if possible. */
}
/* Hints are entered separately in C if multiple hints are desired. */
@object MyObjectClass MyObject = {
    GI_comp = @MyObjectChild;
    HINT_LIST_CHECKBOXES;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}

System Attributes

SystemAttrs, HINT_IF_SYSTEM_ATTRS, HINT_ELSE, HINT_ENDIF

The system attributes are global flags that describe what sort of environment is running the generic object. HINT_IF_SYSTEM_ATTRS is used to conditionally add hints to an object based on these SystemAttrs . If the SystemAttrs set in the HINT_IF_SYSTEM_ATTRS field are true for the current system, then the hints that follow (until a HINT_ENDIF is encountered) are included. If no HINT_ENDIF is encountered, then only the next hint is included. If the SystemAttrs do not match the current system, the following group of hints is instead deleted.

The following SystemAttrs are defined:

SA_TINY
If set, the screen must be either horizontally or vertically tiny.
SA_HORIZONTALLY_TINY
If set, the screen must be horizontally tiny.
SA_VERTICALLY_TINY
If set, the screen must be vertically tiny.
SA_COLOR
If set, the system must have a color screen.
SA_PEN_BASED
If set, the system must be pen-based.
SA_KEYBOARD_ONLY
If set, the system must be set keyboard-only.
SA_NO_KEYBOARD
If set, the system must be set no-keyboard.
SA_NOT
If this flag is set, any other set bits indicate criteria that must not match. This is the equivalent of a logical NOT operation.

The HINT_ELSE hint can be used between HINT_IF_SYSTEM_ATTRS and HINT_END_IF. This allows the inclusion of a separate set of hints to be included when the system conditions in HINT_IF_SYSTEM_ATTRS are not satisfied.

Code Display 2-26 System Attributes

/* The SA_NOT bit acts as a logical not operation. In this case, the hint
 * HINT_EXPAND_HEIGHT_TO_FIT_PARENT will only be included in the object
 * declaration if the system is not pen-based. In addition, if the system is tiny,
 * the other hints will be included. The HINT_ENDIF marks the end of these
 * conditional hints. */
    HINT_IF_SYSTEM_ATTRS = SA_NOT | SA_PEN_BASED;
    HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
    HINT_IF_SYSTEM_ATTRS = SA_TINY;
    HINT_MINIMIZE_SIZE;
    HINT_DISPLAY_CURRENT_SELECTION;
    HINT_ENDIF;
/* Example within an object declaration. */
@object GenInteractionClass GroupingObject = {
    GI_comp = @One, @Two, @Three, @Four, @Five, @Six;
    HINT_IF_SYSTEM_ATTRS = SA_HORIZONTALLY_TINY;
        HINT_ORIENT_CHILDREN_VERTICALLY;
        HINT_WRAP_AFTER_CHILD_COUNT = 3;
    HINT_ELSE;
        HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_ENDIF;
}

Default Actions

HINT_DEFAULT_DEFAULT_ACTION, HINT_ENSURE_TEMPORARY_DEFAULT, HINT_PREVENT_DEFAULT_OVERRIDES

Normally, objects in the user interface are activated by direct action (e.g., clicking on them). Objects may also be indirectly activated, however. The object activated indirectly is known as the default action object. For example, within a window, pressing ENTER or sending the window MSG_GEN_ACTIVATE_INTERACTION_DEFAULT will activate the object marked as the default action.

HINT_DEFAULT_DEFAULT_ACTION marks an object as the default action for the window it appears in. This hint is only relevant for GenTriggers or dialog GenInteractions (that are brought up by an activation trigger). The default activation object is usually activated by pressing the ENTER key within the window.

Normally, whenever a trigger is activated within a window, it will become the default activation object the next time around. Even if a trigger has HINT_DEFAULT_DEFAULT_ACTION, if some other trigger is activated, that other trigger will become the default action in the future. To prevent these automatic default overrides, add HINT_PREVENT_DEFAULT_OVERRIDES to the object's instance data.

HINT_ENSURE_TEMPORARY_DEFAULT ensures that an object that can be navigated to (via the TAB key for example) will act as a default activation object even if the specific UI does not normally allow such behavior. (OSF/Motif does this automatically.)

Keyboard Navigation Hints

HINT_NAVIGATION_ID, HINT_NAVIGATION_NEXT_ID

Most specific UIs allow keyboard navigation within windows, usually through use of the TAB key. Normally, the navigation path follows the order of the children within the windowed object, and this is sufficient for most needs.

HINT_NAVIGATION_ID sets a navigation identifier for an object. Objects may "jump" to this object by including HINT_NAVIGATION_NEXT_ID in their instance data with a value equal to the matching navigation ID of the object to travel to.

Code Display 2-27 Navigation IDs

/* Essentially, this code allows keyboard navigation to skip the Two
 * trigger. Hitting the TAB key on the `One' trigger navigates the focus to the
 * Three trigger. */
@object GenTriggerClass One = {
    GI_visMoniker = "TAB here to get to Three";
    HINT_NAVIGATION_NEXT_ID = 3;
}
@object GenTriggerClass Two = {
    GI_visMoniker = "2";
}
@object GenTriggerClass Three = {
    GI_visMoniker = "3";
    HINT_NAVIGATION_ID = 3;
}

Note that an object with a matching navigation ID must exist, so this method is not recommended. If possible, your UI should be constructed in such a way to allow the default behavior.

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 Optional Attributes (ATTRs) | Next: 6.3 Dynamically Managing VarData


Dynamically Managing VarData

GenClass: 6.3 Vardata: Dynamically Managing VarData

| Up: GEOS SDK TechDocs | Up | Prev: 6.2 Hints to the Specific UI | Next: 7 Special Message Passing
MSG_META_ADD_VAR_DATA, MSG_META_DELETE_VAR_DATA, MSG_GEN_ADD_GEOMETRY_HINT, MSG_GEN_REMOVE_GEOMETRY_HINT

You must use special kernel routines and MetaClass messages to access, add, or remove entries within an object's instance fields.

  • ObjVarScanData()
     This routine scans an object's vardata and calls routines listed in the variable data handler table. This routine is useful for processing several HINTs and ATTRs at one time.
  • MSG_META_ADD_VAR_DATA
     You can use this message to add a vardata entry to an object. The object must first be set not usable before adding the entry. Set the object usable again after you have added the vardata entry.
  • MSG_META_DELETE_VAR_DATA
     Use this message to delete a vardata entry from an object. The object must first be set not usable before removing the entry. Set the object usable again after you have removed the vardata entry.
  • ObjVarFindData()
     This routine checks the object for the passed type and returns a pointer to its data. The message returns true if the entry is present, false if it is not. This routine is useful for processing a single vardata entry.

In addition to these routines and messages, two messages specific to GenClass allow you to add or remove hints dynamically: MSG_GEN_ADD_GEOMETRY_HINT and MSG_GEN_REMOVE_GEOMETRY_HINT . These messages allow geometry and window positioning hints to be added or removed while the object is on-screen. Sending a GS_USABLE object one of these messages with a valid geometry hint forces that object to redraw itself according to the new geometry.

MSG_GEN_ADD_GEOMETRY_HINT

void	MSG_GEN_ADD_GEOMETRY_HINT(
        word		hint,
        word		hintArgument,
        VisUpdateMode		updateMode);

This message adds a geometry hint to a generic object. The object may be GS_USABLE at the time, in which case the object is redrawn according to the new geometry configuration.

Source: Unrestricted.

Destination: Any generic object.

Parameters: hint The hint to add to the object's instance data

hintArgument
A word of data for any hints that require an argument.
updateMode
VisUpdateMode to determine when the object will be redrawn.

Interception: Generally not intercepted.

MSG_GEN_REMOVE_GEOMETRY_HINT

void	MSG_GEN_REMOVE_GEOMETRY_HINT(
        word		hint,
        VisUpdateMode		updateMode);

This message removes a geometry hint from an object's instance data. The object may be GS_USABLE at the time, in which case it will be redrawn according to the new geometry configuration.

Source: Unrestricted.

Destination: Any generic object.

Parameters: hint The hint to remove.

updateMode
VisUpdateMode to determine when the object will be redrawn.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 6.2 Hints to the Specific UI | Next: 7 Special Message Passing


Special Message Passing

GenClass: 7 Special Message Passing

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Vardata | Next: 8 Visual Refreshing

It is often useful to pass messages "generically" rather than having to specify the particular optr directly. For example, in many cases, retrieving and storing the optr of an object is cumbersome, and it would be much easier to say "deliver this message to my parent." GenClass provides many useful messages which handle cases such as these. These message passers are of three types:

  • Parent and Child Generic Message Passers
  • Generic Upward Queries
  • Object-Specific Upward Queries

In all cases, the message must be stored as a classed event . A classed event is a combination of a class and a message. The message itself may pass data. The classed event enables the object to specify which class it would like the message to be handled by. In Goc, a classed event is created by encapsulating the message and class using the @record keyword.

Parent and Child Message Passing

Generic Upward Queries

Object-Specific Queries

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Vardata | Next: 8 Visual Refreshing


Parent and Child Message Passing

GenClass: 7.1 Special Message Passing: Parent and Child Message Passing

| Up: GEOS SDK TechDocs | Up | Prev: 7 Special Message Passing | Next: 7.2 Generic Upward Queries
MSG_GEN_CALL_PARENT, MSG_GEN_SEND_TO_PARENT, MSG_GEN_SEND_TO_CHILDREN

Three messages enable you to pass other messages to a generic object's parents and children without having to know the proper optrs. Using these messages, you can perform operations on any generic object's parents and children. If you need return values from an operation being performed by the parent object, use MSG_GEN_CALL_PARENT . If no return values are needed, you may use MSG_GEN_SEND_TO_PARENT .

To pass a message to all of your generic children, use MSG_GEN_SEND_TO_CHILDREN . Note that no comparable MSG_GEN_CALL_CHILDREN exists because it is meaningless to expect return values from multiple objects for a single event.

You may also use the Goc macros genParent and genChildren to send a message to a parent or the generic children of an object.

MSG_GEN_CALL_PARENT

void	MSG_GEN_CALL_PARENT(
        EventHandle		event);

This message delivers an event to the generic parent of the recipient. This message must pass a classed event that the parent object will handle. You should use this message if return values are expected. Always make sure to cast the return (following the call) into the proper type. The most effective way to do this is by enclosing the actual message sent within parentheses. The event will be freed after it is sent.

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to parent of this object.

Return: The return value of the classed event (cast to the proper type).

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

Code Display 2-28 MSG_GEN_CALL_PARENT

/* The following method retrieves the visual moniker
 * of an object's parent. */
@method MyProcessClass, MSG_GET_MY_PARENTS_MONIKER {
    ChunkHandle parentMoniker;
    EventHandle myEvent;
    /* Encapsulate the message to be handled
     * by any generic (GenClass) object. */
    myEvent = @record GenClass::MSG_GEN_GET_MONIKER();
/* Calls the parent of EntryNumberTwo with the classed event specified above. Note
 * that the return value is cast to type (MSG_GEN_GET_MONIKER) because
 * MSG_GEN_CALL_PARENT itself returns void. */
    parentMoniker = @call (MSG_GEN_GET_MONIKER)
            @EntryNumberTwo::MSG_GEN_CALL_PARENT(myEvent);
    return(parentMoniker); /* return the parentMoniker. */
}

MSG_GEN_SEND_TO_PARENT

void	MSG_GEN_SEND_TO_PARENT(
        EventHandle event);

This message sends an encapsulated event to the parent but expects no return values. The event will be freed after it is sent.

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to parent of this object.

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_SEND_TO_CHILDREN

void	MSG_GEN_SEND_TO_CHILDREN(
        EventHandle event);

This message sends an encapsulated event to all children of the generic object receiving it. This message cannot return values. The event will be freed after it is sent.

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to all children.

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

| Up: GEOS SDK TechDocs | Up | Prev: 7 Special Message Passing | Next: 7.2 Generic Upward Queries


Generic Upward Queries

GenClass: 7.2 Special Message Passing: Generic Upward Queries

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 Parent and Child Message Passing | Next: 7.3 Object-Specific Queries
MSG_GEN_GUP_CALL_OBJECT_OF_CLASS, MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS, MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS, MSG_GEN_GUP_FIND_OBJECT_OF_CLASS, MSG_GEN_GUP_QUERY, MSG_GEN_GUP_INTERACTION_COMMAND, MSG_GEN_GUP_FINISH_QUIT

GenClass provides the capability to search up the generic tree beyond the parent. Using the following messages, the UI can continue passing classed events up the generic tree until it reaches an object of the proper class. This behavior is known as a Generic UPward query (GUP).

For example, if you specify GenInteractionClass in your classed event and later send a GUP message to any object in the generic tree, the stored message will be handled at the first available GenInteraction object it encounters.

MSG_GEN_GUP_CALL_OBJECT_OF_CLASS performs a GUP, returning values from the passed message. You must cast the return values into the proper type based on the return values of the passed message. MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS performs a GUP but does not allow return values.

MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS performs a GUP whose sole function is to search for the existence of an object of the specified class among the object's parents.

MSG_GEN_GUP_FIND_OBJECT_OF_CLASS performs a GUP searching for any object of the passed class, but it also returns the optr of the object found. Note that it is unwise to later use this optr as the generic tree may have changed.

MSG_GEN_GUP_CALL_OBJECT_OF_CLASS

void	MSG_GEN_GUP_CALL_OBJECT_OF_CLASS(
        EventHandle event);

This message performs a generic upward query, passing the classed event upward until it reaches an object of the passed class. This message allows return values and should be cast into whatever return values are expected of the passed message. This is most easily done by enclosing the message sent within parentheses.

An object of the expected class should be present or the event will not be delivered to any object. To check for the existence of such a class, use MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS .

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to an object up the generic tree.

Return: The return values of the classed event (cast to the proper type).

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS

void	MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS(
        EventHandle event);

This message performs a generic upward query, passing the classed event upward until it reaches an object of the described class. Since this message performs a send, it does not allow return values. An object of the expected class should be present; otherwise, the event will not be delivered to any object. To check for the existence of such a class, use MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS .

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to an object up the generic tree.

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

Code Display 2-29 Nesting Classed Events

/* The following method can be used by any object in the generic tree below the
 * primary to set all menus and GenInteractions below the primary window enabled
 * all at once. */
@method MyProcessClass, MSG_ENABLE_ALL_MY_MENUS {
    EventHandle menuEvent, primaryEvent;
/* The first classed event (menuEvent) encapsulates MSG_GEN_SET_ENABLED for any
 * object of GenInteractionClass (typically menus). The @record keyword
 * encapsulates the message to be handled by GenInteractionClass. The
 * VisUpdateMode delays updates via the Application queue to avoid constant visual
 * updates and "flashing." */
    menuEvent = @record GenInteractionClass::MSG_GEN_SET_ENABLED(
                    VUM_DELAYED_VIA_APP_QUEUE);
/* The second classed event binds MSG_GEN_SEND_TO_CHILDREN to the GenPrimaryClass.
 * Sending this message to the GenPrimary will send the menuEvent to all
 * its children. In this case, sending this event to the Primary will send
 * MSG_GEN_SET_ENABLED to all its GenInteraction children. */
    primaryEvent = @record GenPrimaryClass::MSG_GEN_SEND_TO_CHILDREN(menuEvent);
/* Finally, to send this message to the GenPrimary from any object below the
 * GenPrimary, simply send a MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS with the
 * PrimaryEvent above. The GUP message will send the message
 * MSG_GEN_SEND_TO_CHILDREN up the generic tree until it encounters the GenPrimary,
 * at which point the Primary will send the MenuEvent MSG_GEN_SEND_TO_CHILDREN to
 * all its GenInteraction children. */
    @send @MyObject::MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS(primaryEvent);
}

MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS

Boolean	MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS(
        ClassStruct  *class);

This message searches up the generic tree for any object of the specific class. This message is useful for testing the existence of required classes before sending MSG_GEN_GUP_CALL_OBJECT_OF_CLASS and MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS .

Source: Unrestricted.

Destination: Any generic object.

Parameters: class ClassStruct to search for among the object's parents.

Return: Will return true if the class is found, false if not.

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_GUP_FIND_OBJECT_OF_CLASS

optr	MSG_GEN_GUP_FIND_OBJECT_OF_CLASS(
        ClassStruct  *class);

This message performs a function similar to that of MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS , but it returns the optr of the class found, if any. This optr can then be used as the recipient of other messages. You should not use an optr returned by this message at a later time, as the object may have been removed.

Source: Unrestricted.

Destination: Any generic object.

Parameters: class ClassStruct to search for.

Return: The optr of the object of the requested class, or a null optr if no such object was found.

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_GUP_QUERY

void 	MSG_GEN_GUP_QUERY(
        GenReturnParams 		*retValue,
        word 		queryType);

This message is used to query up the generic composite tree.

Source: Anyone.

Destination: Any usable generic object, though only certain objects will answer certain queries.

Parameters: retValue Structure to hold return values.

queryType
GenUpwardQueryType .

The GenUpwardQueryType should be one of the following:

GUQT_DELAYED_OPERATION
This query type determines if a gadget should be operating in "delayed mode," i.e., whether it should wait for a MSG_GEN_APPLY before sending notification to its output, or whether it should send notification whenever it changes state.
GUQT_FIELD
Returns field object in GRP_ cx :GRP_ dx and window (if realized) handle in GRP_ bp . This is a reasonably safe thing to do, since the field window will remain open until all applications within the field are shut down. Only field objects or substitutes for them should intercept this message.
GUQT_SCREEN
Returns screen object in GRP_ cx :GRP_ dx and window (if any) handle in GRP_ bp . This is a reasonably safe thing to do, since the screen window will remain open until all applications within the field are shut down. Screen object being returned will be default screen for the field.

Return: Nothing returned explicitly. retValue struct filled with values which depend on query type.

Structures:

  typedef struct {
    word 	GRP_ax;
    word 	GRP_bp;
    word 	GRP_cx;
    word 	GRP_dx;
} GenReturnParams;

MSG_GEN_GUP_INTERACTION_COMMAND

Boolean 	MSG_GEN_GUP_INTERACTION_COMMAND(
        InteractionCommand 		command);

This message travels up the generic tree until it reaches a non-GIT_ORGANIZATIONAL GenInteraction. Once there, it performs the indicated InteractionCommand . See InteractionCommand declaration for the different command types and how specific user interfaces interpret the commands. If the GenInteraction is a dialog brought up with UserDoDialog() , InteractionCommand will be returned to the caller, after performing the command, via MSG_GEN_INTERACTION_RELEASE_BLOCKED_THREAD_WITH_RESPONSE .

Source: Unrestricted. Normally sent by a MSG_GEN_INTERACTION_ACTIVATE_COMMAND handler or by a custom trigger's action message handler to perform some default command after perform some application-specific function.

Destination: Any generic object. Will travel up generic tree, stopping at first non organizational interaction.

Parameters: command Standard InteractionCommand.

Return: Returns true if handled.

Interception: Not normally intercepted.

MSG_GEN_GUP_FINISH_QUIT

void 	MSG_GEN_GUP_FINISH_QUIT(
        Boolean 		abortFlag,
        Boolean 		notifyParent);

Message to be used by nodes that implement a GCN active list. This should be used if MSG_META_QUIT is handled and sent to the active list items by that node. When a node has finished sending a MSG_META_QUIT to the active list or if the quit is aborted, this allows the notification of the above node in the generic tree.

Source: Sent by active list nodes that implement quit mechanism.

Destination: Any generic object involved in quit mechanism.

Parameters: abortFlag Pass true to abort. This will be passed on to MSG_META_FINISH_QUIT .

notifyParent
Pass true to notify parent of finished quit sequence.

Interception: Generally not intercepted. Default handler sends MSG_META_FINISH_QUIT to self, then sends to generic parent if flag indicates such. MSG_META_FINISH_QUIT is normally the message to intercept to know when a quit is aborted or finished.

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 Parent and Child Message Passing | Next: 7.3 Object-Specific Queries


Object-Specific Queries

GenClass: 7.3 Special Message Passing: Object-Specific Queries

| Up: GEOS SDK TechDocs | Up | Prev: 7.2 Generic Upward Queries | Next: 8 Visual Refreshing
MSG_GEN_CALL_APPLICATION, MSG_GEN_SEND_TO_PROCESS, MSG_GEN_CALL_SYSTEM,MSG_GEN_OUTPUT_ACTION MSG_GEN_QUERY_NEED_TO_BE_ON_ACTIVE_LIST

Besides the Generic Upward Queries, GenClass also contains several object-specific queries. These messages behave in the same manner as the GUP messages except that there is no need to set up a classed event. The object-specific upward query automatically sets up the class to send the message to.

MSG_GEN_CALL_APPLICATION sends a message up the generic tree to the GenApplication object. This message allows return values and should be cast to whatever return values are expected of the passed message.

MSG_GEN_SEND_TO_PROCESS sends a message to the process object associated with this object. Because this message may cross threads, no return values are allowed.

MSG_GEN_CALL_SYSTEM sends a message up the generic tree to the GenSystem object. This message allows return values and should be cast into whatever return values are expected of the passed message.

MSG_GEN_QUERY_NEED_TO_BE_ON_ACTIVE_LIST

MSG_GEN_CALL_APPLICATION

void	MSG_GEN_CALL_APPLICATION(
        EventHandle event);

This message calls the GenApplication object associated with the recipient. This message allows return values and should be cast to whatever return values are expected of the passed message.

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to the object's GenApplication object.

Return: The return values of the classed event (cast to the proper type).

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_SEND_TO_PROCESS

void	MSG_GEN_SEND_TO_PROCESS(
        EventHandle event);

This message sends the event to the Process object of your application. Because the message may cross threads, return values are not allowed.

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to the object's GenProcess object.

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_CALL_SYSTEM

void	MSG_GEN_CALL_SYSTEM(
        EventHandle event);

This message calls the GenSystem object (the root object) associated with your application and sends the event. If you expect return values, make sure to cast them into the proper variable type.

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to the object's GenSystem object.

Return: The return values of the classed event (cast to the proper type).

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_OUTPUT_ACTION

 void	MSG_GEN_OUTPUT_ACTION(
        EventHandle		event,
        optr		dest);

This message sends an event via the destination optr (if any) of the object sent the message. The default handler (which is rarely subclassed) also scans the vardata looking for an ATTR_GEN_DESTINATION_CLASS and records that class in a classed event.

This message is usually sent at a low level and dispatches a message via the destination optr of the object. You may intercept MSG_GEN_OUTPUT_ACTION if you wish knowledge of when an object is being activated (although you may wish to intercept MSG_GEN_ACTIVATE in those cases). If you do send this message yourself, you must either pass a destination optr or leave it null, depending on the object being activated.

Source: Normally sent by an object to itself.

Destination: Normally sent by an object to itself.

Parameters: event A classed event.

optr
The destination optr to send event to or a TravelOption .

Interception: Generally not intercepted.

MSG_GEN_QUERY_NEED_TO_BE_ON_ACTIVE_LIST

Boolean	MSG_GEN_QUERY_NEED_TO_BE_ON_ACTIVE_LIST();

This message queries whether an object still needs to be on an active list. The active list mechanism sends this message if a mechanism no longer needs an object to remain on the active list; it checks whether any other mechanism also needs the object to be on the active list also. Windowed specific UI objects, for example, will return true if ATTR_INITIATE_ON_STARTUP is present on the object, because this optional attribute requires the object to be on the active list.

Source: Unrestricted, though it is generally called on itself. Specifically, if an object no longer needs to remain on the active list for a particular reason, this message should be called on the object to see if it should remain on the active list for any other reason. If so, the caller should not remove the object from the active list. The only exception to this would be if the object were being destroyed, in which the object should be removed in any case.

Destination: Any generic object.

Return: true if the object needs to remain on the active list for some other reason.

Interception: Object classes which add the object to the active list must intercept this message and return true if the class desires the object to remain on the active list. If the message is intercepted and this is not the case, it should be sent to the superclass for handling.

| Up: GEOS SDK TechDocs | Up | Prev: 7.2 Generic Upward Queries | Next: 8 Visual Refreshing


Visual Refreshing

GenClass: 8 Visual Refreshing

| Up: GEOS SDK TechDocs | Up | Prev: 7 Special Message Passing | Next: 9 Setting Sizes
MSG_GEN_UPDATE_VISUAL

MSG_GEN_UPDATE_VISUAL performs a visual update of the object sent the message. Use this message to force a visual update on objects previously updated using the VisUpdateMode VUM_MANUAL.

MSG_GEN_UPDATE_VISUAL
void	MSG_GEN_UPDATE_VISUAL(
        VisUpdateMode updateMode);

This message updates the visual tree at the node specified and is used for updating objects modified with VUM_MANUAL.

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode VisUpdateMode to determine when the object should be redrawn.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 7 Special Message Passing | Next: 9 Setting Sizes


Setting Sizes

GenClass: 9 Setting Sizes

| Up: GEOS SDK TechDocs | Up | Prev: 8 Visual Refreshing | Next: 10 Activation Messages
MSG_GEN_SET_INITIAL_SIZE, MSG_GEN_SET_MINIMUM_SIZE, MSG_GEN_SET_MAXIMUM_SIZE, MSG_GEN_SET_FIXED_SIZE, MSG_GEN_GET_INITIAL_SIZE, MSG_GEN_GET_MINIMUM_SIZE, MSG_GEN_GET_MAXIMUM_SIZE, MSG_GEN_GET_FIZED_SIZE, MSG_GEN_RESET_TO_INITIAL_SIZE, CompSizeHintArgs

These messages all perform resizing operations on generic objects. In some cases, the geometry is not redone by sending these messages; the object will need to be set not usable and then set usable to force the new sizing scheme. In most cases, however, sending these messages will affect the current geometry of an object. These messages manipulate their associated hints, either by creating new hints or by altering old ones. As such, these messages will only affect an object if the associated hint does also.

These messages alter four size restrictions associated with an object: initial, maximum, minimum, and fixed. These restrictions are set up using the GenClass hints HINT_INITIAL_SIZE , HINT_MAXIMUM_SIZE , HINT_MINIMUM_SIZE , and HINT_FIXED_SIZE , all of which are described in the Managing UI Geometry chapter.

The size parameter required by each of these hints is a structure of type CompSizeHintArgs . This structure is shown below:

  typedef struct {
    SpecWidth			CSHA_width;
    SpecHeight			CSHA_height;
    sword			CSHA_count;
} CompSizeHintArgs;

Many of the messages pass a structure of GetSizeArgs to place size information retrieved from the message. This size information is used by the specific UI to visually construct the object, bypassing default sizes in the process. The structure is shown below:

typedef struct {
    word		GSA_width;
    word		GSA_height;
    word		GSA_unused;
    word		GSA_count;
} GetSizeArgs;
MSG_GEN_GET_INITIAL_SIZE
void	MSG_GEN_GET_INITIAL_SIZE(
        GetSizeArgs *initSize);

This message retrieves the initial size specifications of a generic object stored in the hint HINT_INITIAL_SIZE. The object must be passed a pointer to a structure of type GetSizeArgs to store the retrieved data. If the hint HINT_INITIAL_SIZE does not exist for this object , the structure passed to the object of size GetSizeArgs will remain empty.

Source: Unrestricted.

Destination: Any generic object.

Parameters: initSize A pointer to an empty GetSizeArgs structure.

Return: The GetSizeArgs structure will contain the object's initial size.

Interception: Generally not intercepted.

MSG_GEN_SET_INITIAL_SIZE
void	MSG_GEN_SET_INITIAL_SIZE(@stack
        byte		updateMode,
        word		count,
        word		height,				/* SpecHeight */
        word		width);				/* SpecWidth */

This message sets the initial size of a generic object (its size on being first built). This is only done by creating or modifying the hint HINT_INITIAL_SIZE ; there is no guarantee that the specific UI will implement this message in the expected fashion.

The message must pass a VisUpdateMode . This message will only affect the size of an object when it first opens; it will not modify the geometry of a currently built object. For the defined geometry to take effect on an already built object, the object must be set not usable and then set usable again.

Source: Unrestricted.

Destination: Any generic object.

Parameters: count The number of children (or zero, if not applicable).

height
The height of each child.
width
The width of the composite.

Interception: Generally not intercepted.

MSG_GEN_RESET_TO_INITIAL_SIZE
void	MSG_GEN_RESET_TO_INITIAL_SIZE(
        VisUpdateMode		updateMode);

This message forces the recipient and all of its children to be resized according to their initial size specifications. If the objects contain HINT_INITIAL_SIZE , they will be resized according to the hint; otherwise they will be resized according to their default geometry. This message is useful for activating sizes set with MSG_GEN_SET_INITIAL_SIZE .

Source: Unrestricted.

Destination: Any GS_USABLE generic object.

Parameters: updateMode VisUpdateMode to determine when the object will be redrawn.

Interception: Generally not intercepted.

MSG_GEN_GET_MINIMUM_SIZE
void	MSG_GEN_GET_MINIMUM_SIZE(
        GetSizeArgs  *minSize);

This message retrieves the recipient's minimum size specifications stored in the hint HINT_MINIMUM_SIZE . If HINT_MINIMUM_SIZE is not set , the structure passed to the object will remain empty.

Source: Unrestricted.

Destination: Any generic object.

Parameters: minSize A pointer to an empty GetSizeArgs structure.

Return: The GetSizeArgs structure will contain the object's minimum size.

Interception: Generally not intercepted.

MSG_GEN_SET_MINIMUM_SIZE
void	MSG_GEN_SET_MINIMUM_SIZE(@stack
        byte	updateMode,
        word	count,
        word	height,				/* SpecHeight */
        word	width);				/* SpecWidth */

This message sets the minimum allowable size of an object. An object with a minimum size is not allowed to shrink smaller than the bounds set in HINT_MINIMUM_SIZE . This message alters the minimum size by creating or modifying the hint HINT_MINIMUM_SIZE . Due to the nature of hints, there is no guarantee that the specific UI will implement this message in the expected fashion.

This message will modify the geometry of a currently built object, forcing that object to be unbuilt and then built again. Note that the minimum size of an object has nothing to do with the minimized state of an object (for GenDisplays, the iconification of an object) but merely the minimum allowable size of an object in its normal usable state.

Source: Unrestricted.

Destination: Any generic object.

Parameters: count The number of children (or zero, if not applicable).

height
The height of each child.
width
The width of the composite.

Interception: Generally not intercepted.

MSG_GEN_GET_MAXIMUM_SIZE
void	MSG_GEN_GET_MAXIMUM_SIZE(
        GetSizeArgs  *maxSize);

This message retrieves the recipient's maximum size stored in the hint HINT_MAXIMUM_SIZE . If HINT_MAXIMUM_SIZE is not set , the structure passed will remain empty.

Source: Unrestricted.

Destination: Any generic object.

Parameters: maxSize A pointer to an empty GetSizeArgs structure.

Return: The GetSizeArgs structure will contain the object's maximum size.

Interception: Generally not intercepted.

MSG_GEN_SET_MAXIMUM_SIZE
void	MSG_GEN_SET_MAXIMUM_SIZE(
        word	count,
        word	height,				/* SpecHeight */
        word	width);				/* SpecWidth */

This message sets the maximum allowable size of an object. An object with a maximum size is not allowed to grow larger than the bounds set with HINT_MAXIMUM_SIZE . This message alters the maximum size by creating or modifying the hint HINT_MAXIMUM_SIZE . Due to the nature of hints, there is no guarantee that the specific UI will implement this message in the expected fashion.

This message will modify the geometry of a currently built object, forcing that object to be unbuilt and then built again. Note that the maximum size of an object has nothing to do with the maximized state of an object but merely the maximum allowable size of an object in its normal usable state.

Source: Unrestricted.

Destination: Any generic object.

Parameters: count The number of children (or zero, if not applicable).

height
The height of each child.
width
The width of the composite.

Interception: Generally not intercepted.

MSG_GEN_GET_FIXED_SIZE
void	MSG_GEN_GET_FIXED_SIZE(
        GetSizeArgs *fixedSize);

This message retrieves the size stored in HINT_FIXED_SIZE . If HINT_FIXED_SIZE is not set, the structure passed will remain empty.

Source: Unrestricted.

Destination: Any generic object.

Parameters: fixedSize A pointer to an empty GetSizeArgs structure.

Return: The GetSizeArgs structure will contain the object's fixed size.

Interception: Generally not intercepted.

MSG_GEN_SET_FIXED_SIZE
void	MSG_GEN_SET_FIXED_SIZE(
        word	count,
        word	height,				/* SpecHeight */
        word	width);				/* SpecWidth */

This message sets the fixed size of an object. An object with a fixed size is forced to remain at the size set with HINT_FIXED_SIZE . This message alters this fixed size by creating or modifying HINT_FIXED_SIZE . Due to the nature of hints, there is no guarantee that the specific UI will implement this message in the expected fashion.

Source: Unrestricted.

Destination: Any generic object.

Parameters: count The number of children (or zero, if not applicable).

height
The height of each child.
width
The width of the composite.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 8 Visual Refreshing | Next: 10 Activation Messages


Activation Messages

GenClass: 10 Activation Messages

| Up: GEOS SDK TechDocs | Up | Prev: 9 Setting Sizes | Next: 11 Focus Modifications
MSG_GEN_APPLY, MSG_GEN_PRE_APPLY, MSG_GEN_POST_APPLY, MSG_GEN_RESET, MSG_GEN_MAKE_APPLYABLE, MSG_GEN_MAKE_NOT_APPLYABLE, MSG_GEN_ACTIVATE, MSG_GEN_ACTIVATE_INTERACTION_DEFAULT

These messages only affect objects which contain properties. These property gadgets (such as GenItemGroups, GenValues, GenTexts, etc.) typically operate in one of two modes: immediate or delayed .

If an object is operating in immediate mode, any changes in the object's state will cause an immediate change to occur in the application (through the sending of an apply message in the object's instance data). For example, if a GenBooleanGroup is operating in immediate mode, every time the user makes a selection within the BooleanGroup, those changes will be applied to the application immediately, as they occur.

If instead an object is operating in delayed mode, any changes in the object's state will not be sent out immediately. They will only be sent out at a later time when the object receives instructions to apply its changes. The UI asks a generic object to apply its changes with MSG_GEN_APPLY .

Typically, each properties gadget operating in delayed mode has a modified state, indicating whether the object has changed since it last applied its changes. Upon receiving MSG_GEN_APPLY , the object checks whether it has been modified; if it has, it will send out its apply message in the object's instance data. Either you or the system may send out MSG_GEN_APPLY to force a properties gadget to apply its changes. Sending out MSG_GEN_APPLY clears any modified states associated with a properties gadget (after those properties are applied, of course).

If MSG_GEN_APPLY is sent to either a dialog GenInteraction or another GenInteraction within that dialog, it will be propagated to all property gadgets within that Interaction. In this manner, you can apply multiple properties within a dialog box all at once. A GIT_PROPERTIES GenInteraction automatically sends out MSG_GEN_APPLY upon receiving MSG_GEN_GUP_INTERACTION_COMMAND with the InteractionCommand IC_APPLY. (This is typically through an "Apply" trigger in the dialog box.) The dialog will also send MSG_GEN_PRE_APPLY and MSG_GEN_POST_APPLY messages before and after the apply, respectively.

MSG_GEN_RESET also exhibits this automatic propagation behavior; if this message is sent to a dialog GenInteraction, it will be sent to all property gadgets within that Interaction. However, generic property gadgets provide no default handling for MSG_GEN_RESET . If you wish for an object to reset its properties upon receiving MSG_GEN_RESET , you must subclass the object and handle this message yourself. A GIT_PROPERTIES GenInteraction automatically sends out MSG_GEN_RESET upon receiving MSG_GEN_GUP_INTERACTION_COMMAND with the InteractionCommand IC_RESET. (This is typically through a "Reset" trigger in the dialog box.)

MSG_GEN_MAKE_APPLYABLE instructs an object to enable any apply and reset mechanisms that may be disabled. The object must be in delayed mode.

MSG_GEN_MAKE_NOT_APPLYABLE instructs an object to disable any apply and reset mechanisms. The object must be in delayed mode.

MSG_GEN_ACTIVATE activates a generic object as if it had been activated by the user through the specific UI. For example, sending a GenTrigger MSG_GEN_ACTIVATE will cause the trigger to send out its action message in the same manner as if the user had clicked on the trigger.

MSG_GEN_ACTIVATE_INTERACTION_DEFAULT sends a MSG_GEN_ACTIVATE to the object marked as the default within a GenInteraction. This message travels up the generic tree to the first independently-displayable object (usually a dialog box) and then activates the default object for that windowed object.

MSG_GEN_APPLY
void	MSG_GEN_APPLY();

This message is sent to property gadgets to cause them to dispatch their "apply" message and at the same time reset any modified states associated with that object. If this message is sent to a GenInteraction within a dialog box, the GenInteraction will propagate this message to all of its children.

This message may be sent by a GIT_PROPERTIES Interaction upon receipt of MSG_GEN_GUP_INTERACTION_COMMAND with IC_APPLY. You may send this message yourself if you wish for changes in state within the dialog box to be reflected in your application.

Source: Usually a GIT_PROPERTIES Interaction (with an IC_APPLY trigger). You may send this message yourself to force generic objects to apply their changes.

Destination: Any GenInteraction within a dialog box, or any gadget with properties (GenItemGroup, GenValue, etc.).

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_PRE_APPLY
Boolean 	MSG_GEN_PRE_APPLY();

A properties dialog will send this message before MSG_GEN_APPLY . Geodes may use this to do validation of settings or to prepare for applying of changes.

Source: Normally sent by properties dialog before sending MSG_GEN_APPLY .

Destination: Same as MSG_GEN_APPLY .

Parameters: None.

Return: Return true if error.

Interception: Must be intercepted to handle properties validation.

MSG_GEN_POST_APPLY
void	MSG_GEN_POST_APPLY();

A properties dialog will send this message after MSG_GEN_APPLY . This can be used to clean up after application of changes.

Source: Normally sent by properties dialog after sending MSG_GEN_APPLY .

Destination: Same as MSG_GEN_APPLY .

Parameters: None.

Return: Nothing.

Interception: Can be intercepted to handle clean up work after MSG_GEN_APPLY .

MSG_GEN_RESET
void	MSG_GEN_RESET();

This message, if sent to a GenInteraction within a GIT_PROPERTIES dialog box, will propagate to all children below the receiving Interaction. This message provides a convenient means to reset properties within gadgets. There is no default handling of MSG_GEN_RESET by generic objects. You must provide the reset behavior yourself by subclassing individual objects.

Source: Usually a GIT_PROPERTIES GenInteraction (with an IC_RESET trigger), or your application.

Destination: Any GenInteraction within a dialog box, or any gadget subclassed to handle MSG_GEN_RESET .

Parameters: None.

Return: Nothing.

Interception: Your subclassed generic object should intercept this message to provide its own custom reset behavior.

MSG_GEN_MAKE_APPLYABLE
void	MSG_GEN_MAKE_APPLYABLE();

This message is typically sent to a dialog GenInteraction to enable its default "apply" and "reset" buttons. This message also sets up the proper user and actual states for the object. This message is useful in the implementation of custom gadgets.

Source: Unrestricted.

Destination: Any GS_USABLE GenClass object. (This message is only meaningful for objects within a GIT_PROPERTIES GenInteraction, however.)

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_MAKE_NOT_APPLYABLE
void	MSG_GEN_MAKE_NOT_APPLYABLE();

This message is typically sent to a dialog GenInteraction to disable its default "apply" and "reset" buttons. This message is useful in the implementation of custom gadgets.

Source: Unrestricted.

Destination: Any GS_USABLE GenClass object. (This message is only meaningful for objects within a GIT_PROPERTIES GenInteraction, however.)

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_ACTIVATE
void	MSG_GEN_ACTIVATE();

This message manually activates an object as if the user had activated it in the manner defined by the specific UI. For example, this message may activate GenTriggers without use of the mouse. This message causes all normal visual cues associated with the activation.

Source: Unrestricted.

Destination: Usually sent by an object to itself.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_ACTIVATE_INTERACTION_DEFAULT
Boolean	MSG_GEN_ACTIVATE_INTERACTION_DEFAULT();

This message acts on a default exclusive object within a window, forcing that object to activate as in MSG_GEN_ACTIVATE . This message travels up the visible tree to the first WIN_GROUP (windowed) object and sends a MSG_GEN_ACTIVATE to the default object selected there. (This default object is typically marked with the hint HINT_DEFAULT_DEFAULT_ACTION .)

Source: Unrestricted.

Destination: Any GS_USABLE GenClass object (usually within a dialog box).

Parameters: None.

Return: Will return true if the object was activated, false if it was not.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 9 Setting Sizes | Next: 11 Focus Modifications


Focus Modifications

GenClass: 11 Focus Modifications

| Up: GEOS SDK TechDocs | Up | Prev: 10 Activation Messages | Next: 12 Navigation Methods
MSG_GEN_BRING_TO_TOP, MSG_GEN_LOWER_TO_BOTTOM

MetaClass provides the main means of changing the focus and target hierarchies within an application. GenClass also provides two messages to perform special cases with windowed objects.

MSG_GEN_BRING_TO_TOP brings a windowed object to the front of all overlapping windowed objects within its window layer. Depending on the specific UI, this message may alter the focus and target by sending MSG_META_GRAB_FOCUS_EXCL or MSG_META_GRAB_TARGET_EXCL to the window brought to the front.

MSG_GEN_LOWER_TO_BOTTOM lowers a windowed object to the bottom of all overlapping windowed objects within its window layer. Depending on the specific UI, this message may alter the focus and target hierarchies by sending a MSG_META_RELEASE_FOCUS_EXCL and MSG_META_RELEASE_TARGET_EXCL to the window lowered to the bottom.

MSG_GEN_BRING_TO_TOP
void	MSG_GEN_BRING_TO_TOP();

This message brings a windowed object to the front if it is in a maximized or overlapping arrangement with other window objects. This message may also grab the focus and target exclusives for the window involved, if the specific UI allows such behavior.

Source: Unrestricted.

Destination: Any GS_USABLE GenClass object. (Only GenDisplays, independently displayable GenInteractions, and GenPrimarys are affected.)

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_LOWER_TO_BOTTOM
void	MSG_GEN_LOWER_TO_BOTTOM();

This message lowers a windowed object to the bottom if it is in an overlapping arrangement with other windowed objects. This message may also release the focus and target exclusives for the window involved, if the specific UI allows such behavior.

Source: Unrestricted.

Destination: Any GS_USABLE GenClass object. (Only GenDisplays, independently displayable GenInteractions, and GenPrimarys are affected.)

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 10 Activation Messages | Next: 12 Navigation Methods


Navigation Methods

GenClass: 12 Navigation Methods

| Up: GEOS SDK TechDocs | Up | Prev: 11 Focus Modifications | Next: 13 Window Positions and Sizes
MSG_GEN_NAVIGATE_TO_NEXT_FIELD, MSG_GEN_NAVIGATE_TO_PREVIOUS_FIELD, MSG_GEN_NAVIGATION_QUERY

These messages move the navigation cursor between fields at the same level. Typically, these messages alter the focus and target exclusives to implement this behavior. These messages are rarely used within applications.

MSG_GEN_NAVIGATE_TO_NEXT_FIELD
void	MSG_GEN_NAVIGATE_TO_NEXT_FIELD();

This message moves the navigation cursor from its current object to the next valid object. This message is useful if you are trapping keyboard characters using MSG_META_KBD_CHAR and wish to use special characters to navigate the cursor to various fields within a View.

Source: Normally sent by the specific UI, although your application could also send this.

Destination: Any GS_USABLE GenClass object.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted. To alter navigation functionality, intercept MSG_GEN_NAVIGATION_QUERY instead.

MSG_GEN_NAVIGATE_TO_PREVIOUS_FIELD
void	MSG_GEN_NAVIGATE_TO_PREVIOUS_FIELD();

This message moves the navigation cursor from its current object to the previous valid object. This message is useful if you are trapping keyboard characters using MSG_META_KBD_CHAR and wish to use special characters to navigate the cursor to various fields within a View.

Source: Normally sent by the specific UI, although your application could also send this.

Destination: Any GS_USABLE GenClass object.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted. To alter navigation functionality, intercept MSG_GEN_NAVIGATION_QUERY instead.

MSG_GEN_NAVIGATION_QUERY
Boolean	MSG_GEN_NAVIGATION_QUERY(
        NavigationQueryParams 		*retValue,
        optr 				queryOrigin,
        NavigationFlags			navFlags);

This message can be used by applications to initiate navigation-oriented queries, as the MSG_GEN_NAVIGATE_TO_ [ NEXT | PREVIOUS ] _FIELD messages do.

This structure's return values determine how it fits into the navigation network. If this message returns true , then the object which should receive the focus was found, and the NQP_nextObject field will hold the pointer of this object.

If the message returns false , then the focusable object has not yet been found. The NQP_nextObject field contains the field of the next object to try and the NQP_navFlags contains the updated navigation flags which should be passed in the next query.

Source: Unrestricted.

Destination: Any generic object.

Parameters: navFlags Flags giving state of navigation.

queryOrigin
Object which originated this query.
retValue
Structure to hold return values.

Return: true if the queried object should receive the focus; false otherwise.

Structures:

typedef struct {
    word		NQP_navFlags;
     byte 		NQP_backtrackFlag;
     byte 		NQP_unused;
    optr		NQP_nextObject;
} NavigationQueryParams;
| Up: GEOS SDK TechDocs | Up | Prev: 11 Focus Modifications | Next: 13 Window Positions and Sizes


Window Positions and Sizes

GenClass: 13 Window Positions and Sizes

| Up: GEOS SDK TechDocs | Up | Prev: 12 Navigation Methods
MSG_GEN_SET_WIN_POSITION, MSG_GEN_SET_WIN_SIZE, MSG_GEN_SET_WIN_CONSTRAIN

These messages allow window objects to override their default window behavior. Sending these messages may cause the objects involved to be specifically rebuilt.

You may also override the default constraints on a windowed object. To do so, send the windowed object MSG_GEN_SET_WIN_CONSTRAIN, passing it the WinConstrainType to set for the window.

MSG_GEN_SET_WIN_POSITION
void	MSG_GEN_SET_WIN_POSITION(
        word		modeAndType,
        SpecWinSizeSpec		xPosSpec,
        SpecWinSizeSpec		yPosSpec);

This message sets a window's position. The message must be passed a WinPositionType along with two x and y coordinates passed in SpecWinSizeSpec structures. The window's upper left corner will be drawn at the supplied coordinates.

Source: Unrestricted.

Destination: Any GS_USABLE GenDisplay, GenPrimary, or independently-displayable GenInteraction.

Parameters: modeAndType VisUpdateMode and WinPositionType . This message expects a VisUpdateMode and WinPositionType passed in the parameter modeAndType . Use the macro GET_MM_AND_TYPE to combine these two values into one word.

xPosSpec
The new x coordinates of the window.
yPosSpec
The new y coordinates of the window.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_SET_WIN_SIZE
void	MSG_GEN_SET_WIN_SIZE(
        word		modeAndType,
        SpecWinSizeSpec		xSizeSpec,
        SpecWinsizeSpec		ySizeSpec);

This message sets a window's size. The message must be passed a WinPositionType along with two x and y coordinates passed in SpecWinSizeSpec structures.

Source: Unrestricted.

Destination: Any GS_USABLE GenDisplay, GenPrimary, or independently-displayable GenInteraction.

Parameters: modeAndType VisUpdateMode and WinSizeType . Use the macro GET_MM_AND_TYPE to combine these two values into one word.

xSizeSpec
The new x coordinates of the window.
ySizeSpec
The new y coordinates of the window.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_SET_WIN_CONSTRAIN
void	MSG_GEN_SET_WIN_CONSTRAIN(
        VisUpdateMode		updateMode,
        byte		constrainType);

This message changes a window's constrain type to the passed WindowConstrainType .

Source: Unrestricted.

Destination: Any GS_USABLE GenPrimary or GenInteraction object.

Parameters: updateMode VisUpdateMode .

constrainType
WinConstrainType .
WCT_NONE
WCT_KEEP_PARTIALLY_VISIBLE
WCT_KEEP_VISIBLE
WCT_KEEP_VISIBLE_WITH_MARGIN

Interception: Generally not intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 12 Navigation Methods


The Application Object

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenApplication Basics
| One-page Version of this Chapter for Easy Printing

The GenApplication object is used as the top object in every application geode. It acts as the head of the generic object tree, and it provides all the functionality necessary for launching and shutting down the application. It is a subclass of GenClass and therefore inherits all the instance data and messages of that class.

GenApplicationClass has no inherent visual representation. Instead, the main windows of an application are created and managed by one or more GenPrimary objects, which should be placed both as children of the Application object and on the application's GAGCNLT_WINDOWS notification list (if it should appear when the application starts up).

1 GenApplication Basics
1.1 Instance Data
1.2 Application GCN Lists
1.3 Application Instance Reference
1.4 Attach and Launch Flags
1.5 ApplicationStates
1.6 Application Features and Levels
1.7 IACP
2 Advanced GenApplication Usage
2.1 An Application's Life Cycle
2.2 Application Busy States
2.3 The GenApplication's Moniker
2.4 Measurement Type
2.5 Interaction with the UI

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 GenApplication Basics
| One-page Version of this Chapter for Easy Printing


GenApplication Basics

The Application Object: 1 GenApplication Basics

| Up: GEOS SDK TechDocs | Up | Down | Prev: The Application Object | Next: 2 Advanced GenApplication Usage

The top-level generic object of your application must be a GenApplication object. You should place this application object within its own resource; this ensures that your application will take up little memory when minimized. Your application tree should branch from this single node. HelloApp from Hello World shows a section of Hello World to illustrate the typical use of a GenApplicationClass object.

Code Display 3-1 HelloApp from Hello World

/*			Application Object
 * The hello.gp file contains an "appobj" statement which indicates that this
 * "HelloApp" object is the top-level UI object. Note that the name of the
 * resource you place the application object in may be whatever you choose;
 * it does not have to be AppResource. */
@start AppResource;				/* Begin definition of objects in AppResource. */
@object GenApplicationClass HelloApp = {
    GI_comp = @HelloPrimary;
            /* The GI_comp attribute lists the generic children
             * of the object. The HelloApp object has just one
             * child, the primary window of the application. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = HelloPrimary;
            /* The window's application GCN list determines which
             * windowable children should be launched on
             * startup. The primary window in most cases should be
             * launched on startup.*/
}
@end AppResource				/* End definition of objects in AppResource. */

Typically, you will not subclass GenApplicationClass . You may occasionally send messages to it, but otherwise it exists primarily to interact with the User Interface.

Note that the Application object you set up in your .goc file must be reflected in the Geode Parameters file (the .gp file). The name of the Application object should appear in the appobj field of the .gp file.

Instance Data

Application GCN Lists

Application Instance Reference

Attach and Launch Flags

ApplicationStates

Application Features and Levels

IACP

| Up: GEOS SDK TechDocs | Up | Down | Prev: The Application Object | Next: 2 Advanced GenApplication Usage


Instance Data

The Application Object: 1.1 GenApplication Basics: Instance Data

| Up: GEOS SDK TechDocs | Up | Prev: 1 GenApplication Basics | Next: 1.2 Application GCN Lists

GenApplicationClass provides several instance data fields, most of which you will not use. All the instance fields of GenApplication are listed in GenApplication Instance Fields for reference, however.

Code Display 3-2 GenApplication Instance Fields

/* These fields will not be used directly. They can be accessed dynamically,
 * however, with the various messages that set and retrieve the instance data. */
@instance AppInstanceReference				GAI_appRef = {"","",NullHandle,{0}};
@instance word				GAI_appMode = 0;
@instance AppLaunchFlags				GAI_launchFlags = 0;
@instance byte				GAI_optFlags = 0;
@instance word				GAI_appFeatures = 0;
@instance Handle				GAI_specificUI = 0;
@instance ApplicationStates				GAI_states = AS_FOCUSABLE | AS_MODELABLE;
@instance AppAttachFlags				GAI_attachFlags = 0;
@instance UIInterfaceLevel				GAI_appLevel = UIIL_ADVANCED;
@instance ChunkHandle				GAI_iacpConnects = 0;
/* ApplicationStates */
typedef WordFlags ApplicationStates;
#define AS_HAS_FULL_SCREEN_EXCL					0x2000
#define AS_SINGLE_INSTANCE					0x1000
#define AS_QUIT_DETACHING					0x0800
#define AS_AVOID_TRANSPARENT_DETACH					0x0400
#define AS_TRANSPARENT_DETACHING					0x0200
#define AS_REAL_DETACHING					0x0100
#define AS_QUITTING					0x0080
#define AS_DETACHING					0x0040
#define AS_FOCUSABLE					0x0020
#define AS_MODELABLE					0x0010
#define AS_NOT_USER_INTERACTABLE					0x0008
#define AS_RECEIVED_APP_OBJECT_DETACH					0x0004
#define AS_ATTACHED_TO_STATE_FILE					0x0002
#define AS_ATTACHING					0x0001
/* Optimization Flags */
typedef ByteFlags AppOptFlags;
#define AOF_MULTIPLE_INIT_FILE_CATEGORIES	0x80
/* GenApplicationClass also modifies two GenClass instance fields. */
@default GI_states = @default & ~GS_USABLE;
@default GI_attrs = @default | GA_TARGETABLE;

GAI_ appRef is internal. It stores information needed to reload this application. If the application is detached, this instance field contains information necessary to reload this application to its state at detachment.

GAI_ appMode stores the message that should be sent to the application's Process object to bring the application back from a saved state. This is initially null and is set by the GenProcessClass object as soon as it is determined.

GAI_ launchFlags stores the AppLaunchFlags that govern how the application should be run. These flags are used internally and are set when the application is first launched.

GAI_ optFlags stores miscellaneous optimization flags.

GAI_appFeatures stores a word representing the application's features as determined by the user's level of expertise. This field is used primarily by hints in GenControl objects and is rarely used directly otherwise.

GAI_ specificUI stores the handle of the specific UI under which this application is running. This is determined and set by the system when the application is launched.

GAI_ states stores the ApplicationStates of the GenApplication. See ApplicationStates for full information on application states.

GAI_ attachFlags stores the AppAttachFlags relating to restoring the application from a state file when attached.

GAI_ appLevel stores an application's user interface level. This level controls the degree of complexity allowed in the application.

GAI_ iacpConnects stores the chunk handle to an array of active IACP connections.

Code Display 3-3 GenApplication Vardata Fields

@vardata void HINT_APP_IS_ENTERTAINING;
@vardata void HINT_APP_IS_EDUCATIONAL;
@vardata void HINT_APP_IS_PRODUCTIVITY_ORIENTED;
@vardata void HINT_APPLICATION_NO_INBOX_QUERY_WHEN_FOREGROUND_APP;
@vardata void HINT_APPLICATION_QUIT_ON_IACP_ALLOW_FILE_ACCESS;
@vardata optr ATTR_GEN_APPLICATION_PRINT_CONTROL;
    @reloc ATTR_GEN_APPLICATION_PRINT_CONTROL, 0, optr;
@vardata optr ATTR_GEN_APPLICATION_KBD_OBJ;
@vardata optr ATTR_GEN_APPLICATION_SAVE_OPTIONS_TRIGGER;
    @reloc ATTR_GEN_APPLICATION_SAVE_OPTIONS_TRIGGER, 0, optr;
@vardata GeodeToken ATTR_GEN_APPLICATION_ADDITIONAL_TOKENS;
/* GenApplication adds a TravelOption to communicate with the Print Control. */
typedef enum {
    TO_PRINT_CONTROL=_FIRST_GenApplicationClass
} GenApplicationTravelOption;

GenApplication also provides several hints that indicate the type of application. HINT_APP_IS_ENTERTAINING, HINT_APP_IS_EDUCATIONAL and HINT_APP_IS_PRODUCTIVITY_ORIENTED are provided for this purpose.

HINT_APPLICATION_NO_INBOX_QUERY_WHEN_FOREGROUND_APP is only applicable for devices that contain a system inbox for the receipt of messages. If this is the case and the application is set up to receive these messages, this hint indicates that the system should deliver the message to the application without prompting the user. (By default, the system initiates a dialog box asking the user whether to accept any newly received messages.)

HINT_APPLICATION_QUIT_ON_IACP_ALLOW_FILE_ACCESS indicates that the application should quit (send itself a MSG_META_QUIT ) if it receives a MSG_META_IACP_CLOSE_FILE . This message indicates that a client application is requesting access to a file that the current application is accessing.

ATTR_GEN_APPLICATION_PRINT_CONTROL stores the optr of the object to act as the destination for any messages sent to the GenApplicationTravelOption TO_PRINT_CONTROL. Specifically, this attribute is designed to allow remote printing capabilities.

ATTR_GEN_APPLICATION_KBD_OBJ stores the optr of the object to act as the application's floating keyboard. This object must be a subclass of GenInteractionClass and must be in the generic tree below the application object. MSG_GEN_APPLICATION_DISPLAY_FLOATING_KEYBOARD will display this keyboard.

ATTR_GEN_APPLICATION_SAVE_OPTIONS_TRIGGER contains the optr of the Save Options trigger within the options menu. If you have a custom Save Options trigger, you should add the optr of this object in this field.

ATTR_GEN_APPLICATION_ADDITIONAL_TOKENS

| Up: GEOS SDK TechDocs | Up | Prev: 1 GenApplication Basics | Next: 1.2 Application GCN Lists


Application GCN Lists

The Application Object: 1.2 GenApplication Basics: Application GCN Lists

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Instance Data | Next: 1.3 Application Instance Reference

The GCN mechanism is fully discussed in the General Change Mechanism chapter.

Your application may use application GCN lists to notify objects of certain events. For example, it is essential that any windowed objects that you wish to appear upon startup (including your GenPrimary) are added to the GAGCNLT_WINDOWS GCN list.

GenApplication uses its own GCN list types. The four most often used, and the four you will likely use in most of your applications, are listed below. The others can be found in the file geoworks.def and following this short list. Note that all of these GCN list types correspond to the manufacturer ID MANUFACTURER_ID_GEOWORKS.

  • MGCNLT_ACTIVE_LIST
    This GCN list keeps a record of all objects that need to be built upon application startup. Objects on this list will receive several system attach and detach messages. Several types of GenControl objects need to be on this list to function properly. In that case, these controllers must also appear on either the GAGCNLT_SELF_LOAD_OPTIONS or GAGCNLT_STARTUP_LOAD_OPTIONS lists as well. Objects that do not need to receive attach notification (but do need to receive detach notification) may be added dynamically to this list.
  • GAGCNLT_WINDOWS
    This GCN list keeps a record of windowed objects. All windowed objects that should be visible on startup must be added to this list. Any time a windowed object is visually initialized, it will be added to this list. It will not be removed until the object is visually closed. This is used to save window state across shutdown.
  • GAGCNLT_SELF_LOAD_OPTIONS
    This GCN list contains a record of all objects that save options upon receiving MSG_META_SAVE_OPTIONS . These objects will self-load their options; objects on this list will not receive MSG_META_LOAD_OPTIONS automatically. If they need to do so, they should be added to the GAGCNLT_STARTUP_LOAD_OPTIONS list instead. Objects on this list will be sent MSG_META_SAVE_OPTIONS when the GenApplication receives MSG_META_SAVE_OPTIONS .
  • GAGCNLT_STARTUP_LOAD_OPTIONS
    This GCN list contains a record of all objects that should both load their options upon startup and save their options upon receiving MSG_META_SAVE_OPTIONS . Objects on this list will receive MSG_META_LOAD_OPTIONS when they are first loaded.

Code Display 3-4 Sample GenApplication with Controllers

/* This application includes six controllers. One, the TabControl, must receive
 * MSG_META_ATTACH to work properly and is placed on the GCN active list. Another
 * controller, the GenViewController, must receive MSG_META_LOAD_OPTIONS at
 * startup and is therefore placed on the STARTUP_LOAD_OPTIONS list. All other
 * controllers are placed on the SELF_LOAD_OPTIONS list. Note that controllers
 * placed on the active list still need to be placed on one options list. */
@object GenApplicationClass MyApplication = {
    GI_comp = @MyPrimary;
    /* Windows GCN list. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = @MyPrimary;
    /* Active GCN list. All objects that should receive
     * MSG_META_ATTACH should be on this list. These controllers
     * should also be added to the appropriate LOAD_OPTIONS list. */
    gcnList(MANUFACTURER_ID_GEOWORKS, MGCNLT_ACTIVE_LIST) = @MyTabControl,
            @MyToolControl;
    /* Startup Load Options GCN list. This list must include
     * all objects that should receive MSG_META_LOAD_OPTIONS
     * at attach time. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_STARTUP_LOAD_OPTIONS) =
            @MyGenViewControl;
    /* Self Load Options GCN list. All objects that save
     * options and are not on the Startup Load Options list
     * should appear here. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) = @MyTabControl,
            @MyToolControl, @MyEditControl, @MyCharControl,
            @MyParaControl;
}

The other GenApplication-defined GCN lists are listed below with comments about their functions. Other GCN list types are also declared by other classes (e.g. MetaClass ).

GAGCNLT_GEN_CONTROL_NOTIFY_STATUS_CHANGE
Keeps the GenToolControl up-to-date on the status of all the GenControl objects. The data block passed with this list is of type NotifyGenControlStatusChange .
GAGCNLT_APP_TARGET_NOTIFY_SELECT_STATE_CHANGE
Notifies objects of changes in the selection state. The data block passed with this list is of type NotifySelectStateChange .
GAGCNLT_EDIT_CONTROL_NOTIFY_UNDO_STATE_CHANGE
Notifies objects of changes in the state of the undo item. The data block passed with this list is of type NotifyUndoStateChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_CHAR_ATTR_CHANGE
Notifies objects of changes in the text character attributes.The data block passed with this list is of type VisTextNotifyCharAttrChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_PARA_ATTR_CHANGE
Notifies objects of changes in the text paragraph attributes. The data block passed with this list is of type VisTextNotifyParaAttrChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_TYPE_CHANGE
Notifies objects of changes in the text type change.The data block passed with this list is of type VisTextNotifyTypeChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_SELECTION_CHANGE
Notifies objects of changes in the text selection. The data block passed with this list is of type VisTextNotifySelectionChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_COUNT_CHANGE
Notifies objects of changes in the text count. The data block passed with this list is of type VisTextNotifyCountChange .
GAGCNLT_APP_TARGET_NOTIFY_STYLE_TEXT_CHANGE
Notifies objects of possible changes in the style. The data block passed with this list is of type NotifyStyleChange .
GAGCNLT_APP_TARGET_NOTIFY_STYLE_SHEET_TEXT_CHANGE
Notifies objects of possible changes in the style sheet.The data block passed with this list is of type NotifyStyleSheetChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_STYLE_CHANGE
Notifies objects of changes in the current text style. The data block passed with this list is of type NotifyTextStyleChange .
GAGCNLT_APP_TARGET_NOTIFY_FONT_CHANGE
Notifies objects of changes in the font. The data block passed with this list is of type NotifyFontChange .
GAGCNLT_APP_TARGET_NOTIFY_POINT_SIZE_CHANGE
Notifies objects of changes in text point size. The data block passed with this list is of type NotifyPointSizeChange .
GAGCNLT_APP_TARGET_NOTIFY_FONT_ATTR_CHANGE
Notifies objects of changes in the font attributes. The data block passed with this list is of type NotifyFontAttrChange .
GAGCNLT_APP_TARGET_NOTIFY_JUSTIFICATION_CHANGE
Notifies objects of changes in the paragraph justification. The data block passed with this list is of type NotifyJustificationChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_FG_COLOR_CHANGE
Notifies objects of changes in the text foreground (character) color. The data block passed with this list is of type NotifyColorChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_BG_COLOR_CHANGE
Notifies objects of changes in the text background color. The data block passed with this list is of type NotifyColorChange .
GAGCNLT_APP_TARGET_NOTIFY_PARA_COLOR_CHANGE
Notifies objects of changes in the text paragraph color.The data block passed with this list is of type NotifyColorChange .
GAGCNLT_APP_TARGET_NOTIFY_BORDER_COLOR_CHANGE
Notifies objects of changes in text border color. The data block passed with this list is of type NotifyColorChange .
GAGCNLT_APP_TARGET_NOTIFY_SEARCH_SPELL_CHANGE
Notifies objects of changes in the search/spell objects.
GAGCNLT_APP_TARGET_NOTIFY_SEARCH_REPLACE_CHANGE
Notifies objects of changes in the search-and-replace mechanism.
GAGCNLT_APP_TARGET_NOTIFY_CHART_TYPE_CHANGE
Notifies objects of changes in chart type.
GAGCNLT_APP_TARGET_NOTIFY_CHART_GROUP_FLAGS
Notifies objects of changes in chart group flags.
GAGCNLT_APP_TARGET_NOTIFY_CHART_AXIS_ATTRIBUTES
Notifies objects of changes in chart axis attributes.
GAGCNLT_APP_TARGET_NOTIFY_CHART_MARKER_SHAPE
Notifies objects of changes in the chart marker shape.
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_CURRENT_TOOL_CHANGE
Notifies objects of changes in the selected GrObj tool.
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_BODY_SELECTION_STATE_CHANGE
Notifies objects of changes in the GrObj body selection state.
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_AREA_ATTR_CHANGE
Notifies objects of changes in the GrObj area attributes.
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_LINE_ATTR_CHANGE
Notifies objects of changes in the GrObj line attributes.
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_TEXT_ATTR_CHANGE
Notifies objects of changes in the GrObj text attributes.
GAGCNLT_APP_TARGET_NOTIFY_STYLE_GROBJ_CHANGE
Notifies objects of changes in style changes relating to the GrObj.
GAGCNLT_APP_TARGET_NOTIFY_STYLE_SHEET_GROBJ_CHANGE
Notifies objects of changes in the style sheet.
GAGCNLT_APP_TARGET_NOTIFY_RULER_TYPE_CHANGE
Notifies objects of changes in ruler type.
GAGCNLT_APP_TARGET_NOTIFY_RULER_GRID_CHANGE
Notifies objects of changes in the ruler grid.
GAGCNLT_TEXT_RULER_OBJECTS
Notifies objects of changes in the active ruler.
GAGCNLT_APP_TARGET_NOTIFY_BITMAP_CURRENT_TOOL_CHANGE
Notifies objects of changes in the selected bitmap tool.
GAGCNLT_APP_TARGET_NOTIFY_BITMAP_CURRENT_FORMAT_CHANGE
Notifies objects of changes in the current bitmap format.
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_FIELD_PROPERTIES_STATUS_CHANGE
Notifies objects of changes in the flatfile database properties status.
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_FIELD_LIST_CHANGE
Notifies objects of changes in the flatfile field list.
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_RCP_STATUS_CHANGE
Notifies objects of changes in flatfile status.
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_FIELD_APPEARANCE_CHANGE
Notifies objects that a field within the flat file has changed its appearance.
GAGCNLT_APP_NOTIFY_DOC_SIZE_CHANGE
Notifies objects of changes in document size. The data block passed with this list is of type NotifyPageSetupChange .
GAGCNLT_APP_NOTIFY_PAPER_SIZE_CHANGE
Notifies objects of changes in chosen paper size. The data block passed with this list is of type NotifyPageSetupChange .
GAGCNLT_APP_TARGET_NOTIFY_VIEW_STATE_CHANGE
Notifies objects of changes in of view state. The data block passed with this list is of type NotifyViewStateChange .
GAGCNLT_CONTROLLED_GEN_VIEW_OBJECTS
A list of GenView objects controlled by the GenViewControl. (These GenViews will have ATTR_GEN_VIEW_INTERACT_WITH_CONTROLLER set in their instance data.)
GAGCNLT_APP_TARGET_NOTIFY_INK_STATE_CHANGE
Notifies objects of changes in Ink state.
GAGCNLT_CONTROLLED_INK_OBJECTS
A list of Ink objects controlled by the InkControl.
GAGCNLT_APP_TARGET_NOTIFY_PAGE_STATE_CHANGE
Notifies objects of changes in page state. The data block passed with this list is of type NotifyPageStateChange .
GAGCNLT_APP_TARGET_NOTIFY_DOCUMENT_CHANGE
Notifies objects of changes in a document. The data block passed with this list is of type NotifyPageStateChange .
GAGCNLT_APP_TARGET_NOTIFY_DISPLAY_CHANGE
Notifies objects of changes in a display. The data block passed with this list is of type NotifyDisplayChange .
GAGCNLT_APP_TARGET_NOTIFY_DISPLAY_LIST_CHANGE
Notifies objects of changes in the display list. The data block passed with this list is of type NotifyColorChange .
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_MARKER_SHAPE
Notifies objects of changes in a spline marker shape.
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_POINT
Notifies objects of changes in spline points.
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_POLYLINE
Notifies objects of changes in spline polylines.
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_SMOOTHNESS
Notifies objects of changes in spline smoothness.
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_OPEN_CLOSE_CHANGE
Notifies objects of changes in a spline's open/close state.
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_ACTIVE_CELL_CHANGE
Notifies objects of changes in the spreadsheet's active cell range. The data block passed with this list is of type NotifySSheetActiveCellChanged .
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_EDIT_BAR_CHANGE
Notifies objects of changes in the spreadsheet's edit bar. The data block passed with this list is of type NotifySSheetEditBarChanged .
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_SELECTION_CHANGE
Notifies objects of changes in the spreadsheet's selection. The data block passed with this list is of type NotifySSheetSelectionChanged .
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_CELL_WIDTH_HEIGHT_CHANGE
Notifies objects of changes in the spreadsheet's cell width or height. The data block passed with this list is of type NotifySSheetCellWidthHeightChang e.
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_DOC_ATTR_CHANGE
Notifies objects of changes in the spreadsheet's document attributes. The data block passed with this list is of type NotifySSheetDocAttrChange .
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_CELL_ATTR_CHANGE
Notifies objects of changes in the spreadsheet's cell attributes. The data block passed with this list is of type NotifySSheetCellAttrChange .
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_CELL_NOTES_CHANGE
Notifies objects of changes in the notes of a cell within a spreadsheet.
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_DATA_RANGE_CHANGE
Notifies objects of changes in the spreadsheet's data range selection. The data block passed with this list is of type NotifySSheetDataRangeChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_NAME_CHANGE
Notifies objects of changes in a text object's names run. The data block passed with this list is of type VisTextNotifyNameChange.
GAGCNLT_FLOAT_FORMAT_CHANGE
Notifies objects of changes in a particular float format within the float format controller.
GAGCNLT_DISPLAY_OBJECTS_WITH_RULERS
A list of GenDisplay objects that have rulers.
GAGCNLT_APP_TARGET_NOTIFY_APP_CHANGE
Notifies objects of changes in an application.
GAGCNLT_APP_TARGET_NOTIFY_LIBRARY_CHANGE
Notifies objects of changes in a library.
GAGCNLT_APP_TARGET_NOTIFY_CARD_BACK_CHANGE
Notifies objects that a card back has changed.
GAGCNLT_NOTIFY_FOCUS_TEXT_OBJECT
Notifies objects that a an editable text object has a gained the focus. This list is used by the floating keyboard to determine when it should be enabled or not.
GAGCNLT_NOTIFY_TEXT_CONTEXT
Notifies objects that a the selection or data in a focused text object has changed, if that text object has text contexts turned on. This list is used by hand-writing recognition.
GAGCNLT_NOTIFY_HELP_CONTEXT_CHANGE
Notifies objects that a help context has changed.
GAGCNLT_FLOAT_FORMAT_INIT
Notifies the float controller that it should re-initialize itself. This normally sent to the controller when the target document has changed.
GAGCNLT_ALWAYS_INTERACTABLE_WINDOWS
This list stores windows that should always remain interactable, even if modal windows are on-screen. Objects on this list will get messages even if GenInteractions invoked by UserDoDialog() are on-screen. These objects also receive MSG_META_CHECK_IF_INTERACTABLE_OBJECT to allow them to specify objects under them (such as objects in the child blocks) that should also receive messages.
GAGCNLT_USER_DO_DIALOGS
This list stores all dialog boxes initiated via UserDoDialog() .
GAGCNLT_MODAL_WIN_CHANGE
Notifies objects that modal window changes have occurred within the application.
GACGNLT_APP_TARGET_NOTIFY_SPREADSHEET_NAME_CHANGE
Notifies objects that a spreadsheet's name has changed.
GAGCNLT_CONTROLLERS_WITHIN_USER_DO_DIALOGS
This list stores objects (usually controllers) that will appear within the context of UserDoDialog() but will not be within the same block as the dialog box. Objects on this list will receive MSG_META_CHECK_IF_INTERACTABLE_OBJECT.
GAGCNLT_FOCUS_WINDOW_KBD_STATUS
Notifies floating keyboards when windows gain the focus on pen systems.
| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Instance Data | Next: 1.3 Application Instance Reference


Application Instance Reference

The Application Object: 1.3 GenApplication Basics: Application Instance Reference

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Application GCN Lists | Next: 1.4 Attach and Launch Flags
GAI_appRef, GAI_appMode, GAI_optFlags, MSG_GEN_APPLICATION_SET_APP_MODE_MESSAGE, MSG_GEN_APPLICATION_GET_APP_MODE_MESSAGE, MSG_GEN_APPLICATION_SET_APP_INSTANCE_REFERENCE, MSG_GEN_APPLICATION_GET_APP_INSTANCE_REFERENCE, MSG_GEN_APPLICATION_SEND_APP_INSTANCE_REFERENCE_TO_FIELD

GAI_ appRef stores information (within an AppInstanceReference structure) that allows a GenApplication object to be reloaded from its former state. This structure contains a path name, long file name, and disk handle of a state file as well as an additional byte of disk data. The system automatically manages this state file and this instance field.

GAI_ appMode stores the application message that should be sent to the process to bring this application back from a saved state. This is initially null (unless previously saved to a state file); it is set by GenProcessClass when a mode is determined at MSG_META_ATTACH time. You should not alter this instance field.

GAI_ optFlags stores miscellaneous optimization flags. The only flag at this time--AOF_MULTIPLE_INIT_FILE_CATEGORIES--indicates that within this application there may be several different init file categories (marked with ATTR_GEN_INIT_FILE_CATEGORY). This allows MSG_META_GET_INI_CATEGORY to perform a full upward recursive search to find the appropriate init file category; by default, if an init file category is not found on an object, only the GenApplication object is queried.

MSG_GEN_APPLICATION_SET_APP_MODE_MESSAGE

void	MSG_GEN_APPLICATION_SET_APP_MODE_MESSAGE(
        Message modeMessage);

This message stores a message into the GenApplication's GAI_appMode field. Generally, this message indicates the current mode of the application. Should the application be shut down and restored, this message will be sent to the Process object to restore the state to the same mode.

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: modeMessage The message number to be stored in GAI_appMode .

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_GET_APP_MODE_MESSAGE

Message	MSG_GEN_APPLICATION_GET_APP_MODE_MESSAGE();

This message returns the message number stored in the GenApplication's GAI_appMode field.

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: None.

Return: The message number stored in GAI_appMode .

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_APP_INSTANCE_REFERENCE

void	MSG_GEN_APPLICATION_SET_APP_INSTANCE_REFERENCE(
        Handle	appInstance);

This message sets the GAI_appRef field to the passed structure.

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: appInstance The handle of the reference data block.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_GET_APP_INSTANCE_REFERENCE

Handle	MSG_GEN_APPLICATION_GET_APP_INSTANCE_REFERENCE();

This message retrieves the values in GAI_appRef .

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: None.

Return: The handle of the reference stored in GAI_appRef .

Interception: Do not intercept.

MSG_GEN_APPLICATION_SEND_APP_INSTANCE_REFERENCE_TO_FIELD

void	MSG_GEN_APPLICATION_SEND_APP_INSTANCE_REFERENCE_TO_FIELD();

This message causes the GenApplication to send the contents of its GAI_appRef field off to its parent GenField object.

Source: Rarely used.

Destination: Any GenApplication object.

Interception: Do not intercept.

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Application GCN Lists | Next: 1.4 Attach and Launch Flags


Attach and Launch Flags

The Application Object: 1.4 GenApplication Basics: Attach and Launch Flags

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Application Instance Reference | Next: 1.5 ApplicationStates
GAI_launchFlags, GAI_attachFlags, MSG_GEN_APPLICATION_GET_LAUNCH_FLAGS, MSG_GEN_APPLICATION_GET_ATTACH_FLAGS

GAI_ launchFlags stores flags that are passed when the application is first launched. These flags are never set within your object declaration but may be passed with other messages.

ALF_SEND_LAUNCH_REQUEST_TO_UI_TO_HANDLE
If this bit is set, the application will not immediately be launched but will instead wait for the UI to launch it. This flag should not be set by the application itself; it is only used by UserLoadApplication() .
ALF_OPEN_IN_BACK
If this bit is set, the application will be opened behind other applications, in an inactive state. This flag is only used with MSG_GEN_PROCESS_OPEN_APPLICATION .
ALF_DESK_ACCESSORY
If this bit is set, the application will be treated as a "desk accessory," in a layer above normal applications.
ALF_DO_NOT_OPEN_ON_TOP
If this bit is set, the application will be prevented from both gaining the focus and opening on top of other applications.
ALF_OVERRIDE_MULTIPLE_INSTANCE
If this bit is set, and UILM_MULTIPLE_INSTANCES is also set as the application's UILaunchModel , the application will not query the user if he or she attempts to initiate multiple instances of the same application; the application will be multiply launched without first checking whether the already running application should be used instead.
ALF_OPEN_FOR_IACP_ONLY
If this bit is set, the application will be opened only to facilitate a connection with IACPConnect() ; the application should close once that task is completed. If the application should remain open after such an IACP connection, this bit should be cleared. This behavior is used only for MSG_GEN_PROCESS_OPEN_APPLICATION connections. The application cannot be opened in engine mode.

GAI_ attachFlags stores flags related to an application attaching from a state file. These flags are never set within your object declaration but may be passed with other messages.

AAF_RESTORING_FROM_STATE
If this bit is set, the application was launched via MSG_GEN_PROCESS_RESTORE_FROM_STATE . AAF_STATE_FILE_PASSED will also be set.
AAF_STATE_FILE_PASSED
If this bit is set, the application is being restored from a state file.
AAF_DATA_FILE_PASSED
If this bit is set, a data file containing much of the instance data of the application (of type AppLaunchBlock ) is being passed to the launching message. This is internal and should not be used.
AAF_RESTORING_FROM_QUIT
If this bit is set, the application was in the process of quitting, reached engine mode, and is now being started up into application mode again before completely exiting. If set, then AAF_RESTORING_FROM_STATE will also be set. This bit ensures that certain clean-up operations that are done before an application is quit are un-done, and that the application returns to its former state.

MSG_GEN_APPLICATION_GET_LAUNCH_FLAGS

AppLaunchFlags MSG_GEN_APPLICATION_GET_LAUNCH_FLAGS();

This message retrieves the contents of the GenApplication's GAI_launchFlags field.

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: None.

Return: The AppLaunchFlags record stored in GAI_launchFlags .

Interception: Do not intercept.

MSG_GEN_APPLICATION_GET_ATTACH_FLAGS

AppAttachFlags MSG_GEN_APPLICATION_GET_ATTACH_FLAGS();

This message retrieves the contents of the GenApplication's GAI_attachFlags field.

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: None.

Return: The AppAttachFlags stored in GAI_attachFlags .

Interception: Do not intercept.

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Application Instance Reference | Next: 1.5 ApplicationStates


ApplicationStates

The Application Object: 1.5 GenApplication Basics: ApplicationStates

| Up: GEOS SDK TechDocs | Up | Prev: 1.4 Attach and Launch Flags | Next: 1.6 Application Features and Levels
GAI_states, MSG_GEN_APPLICATION_GET_STATE, MSG_GEN_APPLICATION_SET_STATE, MSG_GEN_APPLICATION_SET_NOT_USER_INTERACTABLE, MSG_GEN_APPLICATION_SET_USER_INTERACTABLE, MSG_GEN_APPLICATION_SET_NOT_QUITTING, MSG_GEN_APPLICATION_SET_ATTACHED_TO_STATE_FILE, MSG_GEN_APPLICATION_SET_NOT_ATTACHED_TO_STATE_FILE

GAI_ states stores the ApplicationStates of the application. By default, a GenApplication is both AS_FOCUSABLE and AS_MODELABLE, therefore enabling those hierarchies for this application. Only under extremely rare conditions will you alter this behavior. The flags of ApplicationStates are listed below:

AS_HAS_FULL_SCREEN_EXCL
This bit is set if the application is currently the top screen object at its level. This bit may only be set if the application is between receiving a MSG_META_GAINED_FULL_SCREEN_EXCL and a MSG_META_LOST_FULL_SCREEN_EXCL .
AS_SINGLE_INSTANCE
This bit is set if the application is not capable of being launched multiple times. You should not need to set this.
AS_QUIT_DETACHING
If this bit is set, the detach sequence has been initiated as the result of a QUIT. This bit will only be set if AS_QUITTING is also set; the bit is set in the UI thread at the same time MSG_META_DETACH is sent to the process. Therefore, this bit represents an intermediate step between AS_QUITTING and AS_DETACHING.
AS_AVOID_TRANSPARENT_DETACH
This bit is set if the application should not be transparently detached. If the application is running within UILM_TRANSPARENT mode, then the application will not detach when another application is launched.
AS_TRANSPARENT_DETACHING
This bit is set if the application is being transparently detached. An application can be transparently detached if another application is started in this application's field and that field is marked UILM_TRANSPARENT.
AS_REAL_DETACHING
This bit is set if MSG_GEN_PROCESS_REAL_DETACH has been sent to the process, signalling the irreversible demise of the application. This bit is only set if the UI has finished detaching and the GS_USABLE bit on the application has been cleared.
AS_QUITTING
The application is currently quitting.
AS_DETACHING
The application object has received MSG_META_DETACH and is detaching.
AS_FOCUSABLE
The application may receive the focus exclusive from its field parent. When launched, if this flag is set, the application automatically grabs the focus. This flag is set by default.
AS_MODELABLE
The application may receive the model exclusive from its field parent. When launched, if this flag is set, the application will automatically grab the model exclusive. This flag is set by default.
AS_NOT_USER_INTERACTABLE
The application should not be interactable with the user. This prevents the user from navigating to non-visible applications or otherwise selecting the application.
AS_RECEIVED_APP_OBJECT_DETACH
The application has received a detach message.
AS_ATTACHED_TO_STATE_FILE
The application is currently attached to a state file.
AS_ATTACHING
The application is currently attaching (processing MSG_META_ATTACH ).

MSG_GEN_APPLICATION_GET_STATE

ApplicationStates MSG_GEN_APPLICATION_GET_STATE();

This message retrieves the current application state, stored in GAI_states .

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: None.

Return: The ApplicationStates record stored in GAI_states .

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_STATE

void	MSG_GEN_APPLICATION_SET_STATE(
        ApplicationStates			set,
        ApplicationStates			clear);

This message alters a GenApplication's GAI_ states flags. This message should only be used to set flags that aren't set internally by the UI. Flags that can be altered are the AS_FOCUSABLE, AS_MODELABLE, AS_NOT_USER_INTERACTABLE and AS_AVOID_TRANSPARENT_DETACH state bits.

This message does not reject attempts to set internal bits; therefore, be careful in using this message and only use it to set the external bits mentioned above.

Source: Unrestricted. This message is also used internally.

Destination: Any GenApplication object.

Parameters: set ApplicationStates to set.

clear
ApplicationStates to clear.

Return: Nothing.

Warnings: Do not attempt to set any internal ApplicationStates bits with this message.

Interception: May intercept, but must pass to superclass at some point.

MSG_GEN_APPLICATION_SET_NOT_QUITTING

void	MSG_GEN_APPLICATION_SET_NOT_QUITTING();

This message clears the AS_QUITTING bit in the application's GAI_ states bitfield.

Source: Sent by the UI or the kernel.

Destination: A GenApplication object.

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_NOT_USER_INTERACTABLE

void	MSG_GEN_APPLICATION_SET_NOT_USER_INTERACTABLE();

This message sets the AS_NOT_USER_INTERACTABLE flag in the application's GAI_states field.

Source: Infrequently used.

Destination: The GenApplication to be made not interactable.

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_USER_INTERACTABLE

void	MSG_GEN_APPLICATION_SET_USER_INTERACTABLE();

This message clears the AS_NOT_USER_INTERACTABLE flag in the application's GAI_states field.

Source: Infrequently used.

Destination: The GenApplication to be made interactable.

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_ATTACHED_TO_STATE_FILE

void	MSG_GEN_APPLICATION_SET_ATTACHED_TO_STATE_FILE();

This message sets the AS_ATTACHED_TO_STATE_FILE in the GenApplication's GAI_states field.

Source: Sent by the UI or the kernel.

Destination: The GenApplication object that has been attached to a state file.

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_NOT_ATTACHED_TO_STATE_FILE

void	MSG_GEN_APPLICATION_SET_NOT_ATTACHED_TO_STATE_FILE();

This message clears the AS_ATTACHED_TO_STATE_FILE in the GenApplication's GAI_states field.

Source: Sent by the UI or the kernel.

Destination: The GenApplication object that has been detached to a state file.

Interception: Do not intercept.

| Up: GEOS SDK TechDocs | Up | Prev: 1.4 Attach and Launch Flags | Next: 1.6 Application Features and Levels


Application Features and Levels

The Application Object: 1.6 GenApplication Basics: Application Features and Levels

| Up: GEOS SDK TechDocs | Up | Prev: 1.5 ApplicationStates | Next: 1.7 IACP
GAI_appFeatures, GAI_appLevel, MSG_GEN_APPLICATION_GET_APP_FEATURES, MSG_GEN_APPLICATION_SET_APP_FEATURES, MSG_GEN_APPLICATION_SET_APP_LEVEL, MSG_GEN_APPLICATION_UPDATE_APP_FEATURES, MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE, GenAppUsabilityTupleFlags, GenAppUsabilityTuple, GenAppUsabilityCommands

A GenApplication may store a word of features ( GAI_appFeatures ); these features correspond to groups of UI objects. Depending on a certain feature being set or not set, certain groups of UI objects may or may not appear, allowing you to customize your application for different users or criteria. It is up to your application to define both the features and the objects that these features correspond to.

GAI_ appFeatures is a word-length bitfield. Each bit corresponds to a certain group of features which you define. You may thus have up to 16 different feature groups for any application. (Note that each feature group may include several UI objects.) In general, you group these features together so that they correspond to a specific UIInterfaceLevel . If the application appears at a different User Interface level, the makeup of the UI will be different.

An application's user interface level is stored in the GenApplication's GAI_ appLevel instance data entry. Each UIInterfaceLevel corresponds to a certain group of features. Changing the UI level changes the group of features that may be displayed.

The features represented in the bitfield may be represented in hints added to GenControl objects. Most often, the controllers and the application will adjust their menus, tools, and other UI gadgetry to conform to the features specified in this record.

Code Display 3-5 Setting Up Features

/* Features are stored in a word-length bitfield. */
typedef WordFlags MyFeatures;
@define MF_EDIT_FEATURES				(0x8000)
@define MF_PASTE_FEATURES				(0x4000)
@define MF_FORMAT_FEATURES				(0x2000)
/* We might want to group certain features together based on the level of
 * expertise of the user. In this example, if the user level is "intermediate"
 * (which we will define later), we allow features for editing and pasting to the
 * UI. If the user level is "advanced" we allow the intermediate features and also
 * allow formatting features. */
@define INTRODUCTORY_FEATURES				(0)
@define INTERMEDIATE_FEATURES				(@MF_EDIT_FEATURES | @MF_PASTE_FEATURES)
@define ADVANCED_FEATURES				(@INTERMEDIATE_FEATURES | @MF_FORMAT_FEATURES)

MSG_GEN_APPLICATION_GET_APP_FEATURES returns the current application features and UIInterfaceLevel in use by an application.

You may set the application's GAI_ appFeatures by sending it MSG_GEN_APPLICATION_SET_APP_FEATURES. You may also change the application's user level by sending it MSG_GEN_APPLICATION_SET_APP_LEVEL. Each of these messages in turn generates a MSG_GEN_APPLICATION_UPDATE_APP_FEATURES.

This message is meant to be sub-classed so that you can alter the behavior for different features. In most cases, however, you will simply handle this message, fill in relevant parameters, and send the GenApplication MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE--the message which performs the actual work done in changing the UI. This message expects a number of arguments.

The most important argument is the table of GenAppUsabilityTuple entries that correspond to each feature. These entries define what sort of UI change is required, and what object is required to change.You must set up this table beforehand.

The types of usability commands available (in the bit positions set aside with GAUTF_COMMAND in the tuple's GenAppUsabilityTupleFlags ) are:

  • GAUC_USABILITY
    If set, the object should be made GS_USABLE if the feature is on. This is the default behavior.
  • GAUC_RECALC_CONTROLLER
    If set, the controller needs to have its features recalculated. The particular feature bits are stored with the controller itself; the application knows nothing more about the controller's features other than that they need to be changed.
  • GAUC_REPARENT
    If set, the object needs to be relocated to another part of the UI, underneath a different parent. This parent is passed in the reparentObject entry for MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE. Only one object may be re-parented for each application.
  • GAUC_POPUP
    If set, the object should be made a popup menu. Note that this allows a sub-group to become a menu without having to use GAUC_REPARENT.
  • GAUC_TOOLBAR
    If set, the object is a GenBoolean that corresponds to a toolbar state. Turning the feature on or off forces the GenBoolean to send an apply in addition to other behavior.
  • GAUC_RESTART
    If set, the object needs to be "kick started" by first setting it GS_NOT_USABLE and then GS_USABLE.

Because each feature may have multiple objects affected, the GenAppUsabilityTupleFlags entry GAUTF_END_OF_LIST indicates that there are no more commands for that feature. The flag GAUTF_OFF_IF_BIT_IS_ON indicates that a given command should be reversed for the object. (I.e. if the feature is on, the object should be removed, not added.)

Code Display 3-6 Setting Up the GenAppUsabilityTuple Tables

/* Each GenAppUsabilityTuple will refer to a specific set of features. */
/*
 * Since GUAC_USABILITY is the default setting (and is zero) setting any other
 * flags either supersedes or complements this behavior. In this case, setting the
 * EditToolEntry as a GUAC_TOOLBAR command supersedes the GUAC_USABILITY command.
 * Setting the GUATF_END_OF_LIST flag for the EditTrigger does not alter the
 * GUAC_USABILITY command, which is still implicit.
 */
static const GenAppUsabilityTuple editFeaturesList [] =
{
    {GUAC_TOOLBAR,			@EditToolEntry 		},
    {GUATF_END_OF_LIST			@EditTrigger		}
};
static const GenAppUsabilityTuple pasteFeaturesList [] =
{
    {GUAC_END_OF_LIST,			@PasteTrigger		}
};
static const GenAppUsabilityTuple formatFeaturesList [] =
{
    {
    GAUTF_END_OF_LIST | GUAC_RECALC_CONTROLLER,
    @FeatureController
    }
};
/* After each feature's GenAppUsabilityTuple is set up, you should set up a table
 * of these structures to pass to relevant messages. */
static const GenAppUsabilityTuple * const usabilityTable [] =
{
    editFeaturesList,
    pasteFeaturesList,
    formatFeaturesList
};
/*
 * Within your code, decide where you wish to set the application features
 * (usually within some sort of User level dialog box that passes a selection of
 * feature bits) and send either MSG_GEN_APPLICATION_SET_APP_FEATURES or
 * MSG_GEN_APPLICATION_SET_APP_LEVEL with the proper feature bits set.
 */
@method MyLevelApplicationClass, MSG_MY_APPLICATION_SET_USER_LEVEL
{
   @call oself::MSG_GEN_APPLICATION_SET_APP_FEATURES(selection);
}
/*
 * Then intercept MSG_GEN_APPLICATION_UPDATE_APP_FEATURES and set up the correct
 * parameters for MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE.
 */
@method MyLevelApplicationClass, MSG_GEN_APPLICATION_UPDATE_APP_FEATURES
{
    @call oself::MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE(
    NullOptr,
    @ObjectToReparent,			/* if any */
    levelTable,			/* if any */
    sizeof(usabilityTable) / sizeof(usabilityTable [0]),
    usabilityTable,
    appOpeningFlag,
    oldLevel,
    level,
    featuresChanged,
    featuresOn);
}

MSG_GEN_APPLICATION_GET_APP_FEATURES

dword	MSG_GEN_APPLICATION_GET_APP_FEATURES();

This message retrieves the set of features set for the application.

Source: Unrestricted--typically a GenControl object finding out the application's UI level.

Destination: The GenApplication running the controller.

Parameters: None.

Return: A dword containing the word of features stored in GAI_appFeatures and the UIInterfaceLevel for the application. The features are stored in the high word; the interface level is stored in the low word.

Interception: Generally not intercepted.

MSG_GEN_APPLICATION_SET_APP_FEATURES

void	MSG_GEN_APPLICATION_SET_APP_FEATURES(
        word	features);

This message sets a new set of features into the GenApplication's GAI_appFeatures record. This message in turn generates a MSG_GEN_APPLICATION_UPDATE_APP_FEATURES for your application object to intercept. (The message handler for that message must in turn send MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE to activate the UI changes specified in the features list.)

Source: Unrestricted--typically a system function.

Destination: The GenApplication having its features set.

Parameters: features The new word-sized record of application features to set.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_APPLICATION_UPDATE_APP_FEATURES

void	MSG_GEN_APPLICATION_UPDATE_APP_FEATURES(@stack
        optr		unReparentObject,
        optr		reparentObject,
        GenAppUsabilityTuple		*levelTable,
        word		tableLength,
        void		*table,
        word		appOpeningFlag,
        UIInterfaceLevel		oldLevel,
        UIInterfaceLevel		level,
        word		featuresChanged,
        word		featuresOn);

This message is sent by the application to itself when it is told to change either its features or its UIInterfacelevel . This message is passed a number of parameters, most of which should simply be passed to MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE. If you have reparent objects (or un-reparent objects), you must set them up here.

Source: Sent by an application object to itself in response to a MSG_GEN_APPLICATION_SET_APP_FEATURES or MSG_GEN_APPLICATION_SET_APP_LEVEL.

Destination: The GenApplication object.

Parameters: unReparentObject The optr of the object to be unreparented. If a null optr is passed, the object will by default be moved up and added as the next sibling of its current parent.

reparentObject
The optr of the object to be reparented to another UI location. You must supply this object if you have a GenAppUsabilityTuple entry that contains a GAUC_REPARENT entry.
levelTable
This table contains the GenAppUsabilityTuples corresponding to objects that contain their own features and that must be notified when the user level changes. This is so that those objects can generate their own feature updates. Typically, controllers are included among these objects.
tableLength
The number of table entries in table .
table
Table of GenAppUsabilityTuple entries that must be updated when the user level changes. This table is usually set up as global data and maps each user level feature to a GenAppUsabilityTuple .
appOpeningFlag
Set if the application is starting.
oldLevel
The previous UIInterfaceLevel .
level
The new UIInterfaceLevel .
featuresChanged
The set of features changed (deleted).
featuresOn
The set of features to set on.

Interception: To set an application's features, you must intercept this message and send MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE; there is no default message handler. This message is provided as a convenient point to intercept and change features before executing the changes.

MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE

void	MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE(
        optr		unReparentObject,
        optr		reparentObject,
        GenAppUsabilityTuple		*levelTable,
        word		tableLength,
        void		*table,
        word		appOpeningFlag,
        UIInterfaceLevel		oldLevel,
        UIInterfaceLevel		level,
        word		featuresChanged,
        word		featuresOn);

This message is called to update the application's features to reflect a new set of features to

Source: Typically, your message handler for MSG_GEN_APPLICATION_UPDATE_APP_FEATURES.

Destination: The GenApplication object.

Parameters: unReparentObject The optr of the object to be unreparented. If a null optr is passed, the object will by default be moved up and added as the next sibling of its current parent.

reparentObject
The optr of the object to be reparented to another UI location. You must supply this object if you have a GenAppUsabilityTuple entry that contains a GAUC_REPARENT entry.
levelTable
This table contains the GenAppUsabilityTuples corresponding to objects that contain their own features and that must be notified when the user level changes. This is so that those objects can generate their own feature updates. Typically, controllers are included among these objects.
tableLength
The number of table entries in table .
table
Table of GenAppUsabilityTuple entries that must be updated when the user level changes. This table is usually set up as global data and maps each user level feature to a GenAppUsabilityTuple .
appOpeningFlag
Set if the application is starting.
oldLevel
The previous UIInterfaceLevel .
level
The new UIInterfaceLevel .
featuresChanged
The set of features changed (deleted).
featuresOn
The set of features to set on.

Interception: Generally not intercepted. Intercept MSG_GEN_APPLICATION_UPDATE_APP_FEATURES instead.

| Up: GEOS SDK TechDocs | Up | Prev: 1.5 ApplicationStates | Next: 1.7 IACP


IACP

The Application Object: 1.7 GenApplication Basics: IACP

| Up: GEOS SDK TechDocs | Up | Prev: 1.6 Application Features and Levels | Next: 2 Advanced GenApplication Usage
GAI_iacpConnects, MSG_GEN_APPLICATION_IACP_REGISTER, MSG_GEN_APPLICATION_IACP_UNREGISTER, MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_CONNECTIONS, MSG_GEN_APPLICATION_IACP_
GET_NUMBER_OF_APP_MODE_CONNECTIONS, MSG_GEN_APPLICATION_IACP_SHUTDOWN_ALL_CONNECTIONS, MSG_GEN_APPLICATION_IACP_COMPLETE_CONNECTIONS, MSG_GEN_APPLICATION_APP_MODE_COMPLETE

IACP (the GEOS I nter A pplication C ommunication P rotocol) allows applications to communicate with each other. IACP is flexible enough to let applications know whether another application is open, closed, or in the process of attaching or detaching. IACP allows applications to convey information to one another, and could be used to support updating data (e.g. documents) across applications. The IACP mechanism is discussed more fully in the Applications and Geodes chapter. The information included below only discusses GenApplicationClass support of IACP mechanisms.

GAI_ iacpConnects stores the chunk handle to an array of active IACP connections. This chunk stores the IACP connection value referring to the remote application and the type of IACP connection (i.e. a connection that is enabled during a MSG_GEN_PROCESS_OPEN_APPLICATION, for example). These values are manipulated internally and there is no need to access them. You will instead use a variety of messages provided with GenApplicationClass to register and unregister for application notification.

A good deal of support has been added to GenApplicationClass to support IACP. The main things you need to know about this support are:

  • A GenApplication object will refuse to quit so long as there are IACP connections open to it. It can, however, be forcibly detached, as happens when the system shuts down. In such a case, it will call IACPShutdownAll() to shut down all remaining connections either to or from it.
  • When it receives MSG_META_IACP_LOST_CONNECTION sent to it as a server, it will eventually call IACPShutdown() for the connection when it is certain no more messages relating to the connection are in any relevant queue. It will forward this message to all GenDocument objects below any GenDocumentGroup object in the application, so they can close themselves if the lost connection was the last reference to them.
  • It will automatically register itself as a server for the application's token, either when it receives MSG_META_APP_STARTUP if the AppLaunchBlock indicates it's running in engine mode, or when it receives MSG_META_ATTACH and has attached all the various pieces of UI. It will unregister itself as a server when it loses its last IACP connection and is no longer functioning in application mode (either because the user quit the application long since, or because it was never functioning in application mode).
  • It registers and unregisters itself by sending MSG_GEN_APPLICATION_IACP_REGISTER and MSG_GEN_APPLICATION_IACP_UNREGISTER to itself, allowing an application to subclass these messages and register with other lists as appropriate.
  • To determine whether it has any IACP connections remaining, it invokes MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_CONNECTIONS on itself. Should an application register the GenApplication or any other object as a server for another list, it can determine how many connections remain from that list and augment the number returned by the default handler in GenApplicationClass . If this returns non-zero, GenApplication will not shut down the application voluntarily.
  • After unregistering, it will force-queue a message to itself that will check the number of connections again. If the number has become non-zero between the unregister and the check, it re-registers and does not shut down. If the number remains zero, however, the application will exit.
  • When forcibly detached, it will send MSG_GEN_APPLICATION_IACP_SHUTDOWN_ALL_CONNECTIONS to itself. The default handler will call IACPShutdownAll() , passing its own optr. A subclass can use this to perform a similar operation for any other server objects the application might have.

MSG_GEN_APPLICATION_IACP_REGISTER

void	MSG_GEN_APPLICATION_IACP_REGISTER();

This message is sent by a GenApplication object to itself when it registers for IACP. It is not a message meant to be sent externally to an application to register it for IACP. Instead, you can subclass this message and register the object with other lists.

Source: Sent by the GenApplication object to itself.

Destination: The GenApplication object to register with IACP.

Interception: May be intercepted if there are other lists with which you want to register the application, or other server objects. You must make sure to call the superclass, however.

MSG_GEN_APPLICATION_IACP_UNREGISTER

void	MSG_GEN_APPLICATION_IACP_UNREGISTER();

This message is sent by a GenApplication object to itself when it unregisters for IACP. It is not a message meant to be sent externally to an application to unregister it for IACP. Instead, you can subclass this message and unregister the object with other lists.

Source: Sent by the GenApplication object to itself.

Destination: The GenApplication object to unregister with IACP.

Interception: May be intercepted if there are other lists with which you need to unregister the application, or other server objects. You must make sure to call the superclass, however.

MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_CONNECTIONS

word	MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_CONNECTIONS();

This message returns the number of active engine or app-mode IACP connections for a given application. This message is used to check whether an application open only for IACP purposes may be closed.

Source: Called by GenProcessClass when the UI has finished processing MSG_META_QUIT for the application; this determines if the application should exit at this point or if there are client applications that need the application to stay open.

Destination: GenApplication object of the application.

Return: Number of open connections. If non-zero, application will remain open.

Interception: Only intercept if you have other server objects beside your GenApplication object. If intercepting, call the superclass first and then add the number of connections to the other objects onto the result returned by GenApplicationClass .

MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_APP_MODE_CONNECTIONS

word	MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_APP_MODE_CONNECTIONS();

This message retrieves the number of connections which require that the application be open in app-mode (as opposed to engine mode). This message is used to check whether an application can be closed down into engine mode even if some IACP connections are still open.

Source: This message is called by the GenProcessClass when the UI has finished processing MSG_META_QUIT to determine if the application should really close down to engine mode.

Destination: GenApplication object of application.

Return: Number of open app-mode connections.

Interception: Only intercept if you have other server objects besides your GenApplication object. You should call the superclass first and then add the number of connections to other objects onto the result returned by GenApplicationClass .

MSG_GEN_APPLICATION_IACP_SHUTDOWN_ALL_CONNECTIONS

void	MSG_GEN_APPLICATION_IACP_SHUTDOWN_ALL_CONNECTIONS();

This message shuts down all IACP connections for a given application, either on the server or the client side of the connection.

Source: Sent by the GenApplication object to itself.

Destination: GenApplication object of the application.

Interception: May be intercepted to allow connections to other server objects to be shut down. You must call the superclass at some point to ensure that application connections are shut down as well.

MSG_GEN_APPLICATION_APP_MODE_COMPLETE

void	MSG_GEN_APPLICATION_APP_MODE_COMPLETE();

This message is sent to the application when its life as a user-interactable app is complete. The default behavior is to continue shutting down the process if there are no IACP connections active.

Source: Sent by the GenProcess object after it receives a MSG_META_ACK from detaching the application.

Destination: GenApplication object.

Interception: Generally not intercepted; If you have other server connections which you want taken into account before shutting the application completely down, you should intercept MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_CONNECTIONS instead.

MSG_GEN_APPLICATION_IACP_COMPLETE_CONNECTIONS

void	MSG_GEN_APPLICATION_IACP_COMPLETE_CONNECTIONS();

This message completes all pending IACP connections, accepting any queued messages that have been waiting to be handled. If you subclass it, be sure to call the superclass at some point.

Source: Sent by the GenApplication object to itself in its default MSG_GEN_APPLICATION_OPEN_COMPLETE method, as we assume that the object should be able to handle IACP messages at this latter stage of opening the IACP mechanism.

Destination: GenApplication object of the application.

Interception: May be intercepted if there are other lists besides those connected to the application's token, if those other connections might be pending. If intercepting, you must call the superclass eventually.

|  Up: GEOS SDK TechDocs | Up | Prev: 1.6 Application Features and Levels | Next: 2 Advanced GenApplication Usage


Advanced GenApplication Usage

The Application Object: 2 Advanced GenApplication Usage

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 GenApplication Basics | Next: 2.1 An Application's Life Cycle

Typically, you will merely set up a GenApplication object in your .goc file and then leave it alone. You may occasionally send it messages to invoke functions or to query the application. These messages are infrequently used, however, and you will likely not have need for them.

An Application's Life Cycle

Application Busy States

The GenApplication's Moniker

Measurement Type

Interaction with the UI

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 GenApplication Basics | Next: 2.1 An Application's Life Cycle


An Application's Life Cycle

The Application Object: 2.1 Advanced GenApplication Usage: An Application's Life Cycle

| Up: GEOS SDK TechDocs | Up | Prev: 2 Advanced GenApplication Usage | Next: 2.2 Application Busy States

For information on how an application is launched and closed, see the Applications and Geodes chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Advanced GenApplication Usage | Next: 2.2 Application Busy States


Application Busy States

The Application Object: 2.2 Advanced GenApplication Usage: Application Busy States

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 An Application's Life Cycle | Next: 2.3 The GenApplication's Moniker
MSG_GEN_APPLICATION_MARK_BUSY, MSG_GEN_APPLICATION_MARK_NOT_BUSY, MSG_GEN_APPLICATION_HOLD_UP_INPUT, MSG_GEN_APPLICATION_RESUME_INPUT, MSG_GEN_APPLICATION_IGNORE_INPUT, MSG_GEN_APPLICATION_ACCEPT_INPUT, MSG_GEN_APPLICATION_MARK_APP_COMPLETELY_BUSY, MSG_GEN_APPLICATION_MARK_APP_NOT_COMPLETELY_BUSY

An application's busy state is reflected by its mouse pointer. An application may have several busy states, set appropriately for the action going on at the time. The messages below can set the application's busy state. You will not usually send any of these messages to your GenApplication object. Instead, you will usually set appropriate GI_ attrs to automatically send out these messages during times when the application will be busy.

MSG_GEN_APPLICATION_MARK_BUSY marks the application busy (usually by changing the cursor to an appropriate shape determined by the specific UI) until the current operation in the application thread completes. This message is sent by UI gadgets that have GA_INITIATES_BUSY_STATE set in their GI_attrs fields. It may also be called by any other object that wants to mark the application busy. When an application is busy, the user may continue to interact with it.

MSG_GEN_APPLICATION_MARK_NOT_BUSY removes the busy state marker. This message is automatically sent to the application object when the operation that initiated the busy state completes.

MSG_GEN_APPLICATION_HOLD_UP_INPUT instructs the User Interface to place all input events into a special "hold-up" queue until the input is resumed. This message also marks the application busy. Applications marked GA_INITIATES_INPUT_HOLD_UP will receive this message whenever they initiate an operation.

MSG_GEN_APPLICATION_RESUME_INPUT removes the input hold-up state, allowing normal input flow. This message flushes the "hold-up" event queue into the application's input queue, ensuring that all events during the "hold up" operation are handled before any new events.

MSG_GEN_APPLICATION_IGNORE_INPUT instructs the GenApplication object to ignore all input events it receives. This may be accompanied with an audible warning (beep). Applications marked GA_INITIATES_INPUT_IGNORE will receive this message whenever they initiate an operation.

MSG_GEN_APPLICATION_ACCEPT_INPUT removes the input ignore state and directs the GenApplication object to again receive input events and handle them.

All of these messages are cumulative. The application will keep track of how many times each of these messages is sent. For example, each MSG_GEN_APPLICATION_MARK_NOT_BUSY message will remove a MSG_GEN_APPLICATION_MARK_BUSY . When the count reaches zero, the busy state is removed.

MSG_GEN_APPLICATION_MARK_BUSY

void	MSG_GEN_APPLICATION_MARK_BUSY();

This message marks the application busy and changes the cursor image.

Source: Sent automatically by objects with GA_INITIATES_BUSY_STATE set.

Destination: The GenApplication object running the sender.

Interception: Do not intercept.

MSG_GEN_APPLICATION_MARK_NOT_BUSY

void	MSG_GEN_APPLICATION_MARK_NOT_BUSY();

This message marks the application not busy, removing the effect of a previous MSG_GEN_APPLICATION_MARK_BUSY .

Source: Sent automatically by objects with GA_INITIATES_BUSY_STATE set.

Destination: The GenApplication object running the sender.

Interception: Do not intercept.

MSG_GEN_APPLICATION_HOLD_UP_INPUT

void	MSG_GEN_APPLICATION_HOLD_UP_INPUT();

This message causes the GenApplication to mark itself busy and redirect input events to a special "hold-up" queue. When the application is ready to resume normal activity, it first handles the messages in the hold-up queue before handling new input messages.

Source: Used infrequently.

Destination: The GenApplication object to be held up.

Interception: Do not intercept.

MSG_GEN_APPLICATION_RESUME_INPUT

void	MSG_GEN_APPLICATION_RESUME_INPUT();

This message causes a GenApplication to resume normal input handling after it has been held up with MSG_GEN_APPLICATION_HOLD_UP_INPUT .

Source: Used infrequently.

Destination: The GenApplication object running the sender.

Interception: Do not intercept.

MSG_GEN_APPLICATION_IGNORE_INPUT

void	MSG_GEN_APPLICATION_IGNORE_INPUT();

This message causes the GenApplication to consume all input events it receives rather than handle them. This message may be used during debugging as a last resort to help find synchronization problems.

Source: Infrequently used.

Destination: The GenApplication object to consume input events.

Interception: Do not intercept.

MSG_GEN_APPLICATION_ACCEPT_INPUT

void	MSG_GEN_APPLICATION_ACCEPT_INPUT();

This message undoes a previous MSG_GEN_APPLICATION_IGNORE_INPUT , allowing the GenApplication to once again handle input events normally.

Source: Infrequently used.

Destination: The GenApplication object to resume input handling.

Interception: Do not intercept.

MSG_GEN_APPLICATION_MARK_APP_COMPLETELY_BUSY

void	MSG_GEN_APPLICATION_MARK_APP_COMPLETELY_BUSY();

This message is rarely used and forces a busy state over the application regardless of other states. It should be used only when a time-intensive task is going on in the UI and the program can not handle input during that time.

Source: Infrequently used.

Destination: The GenApplication object to be marked completely busy.

Interception: Do not intercept.

MSG_GEN_APPLICATION_MARK_APP_NOT_COMPLETELY_BUSY

void	MSG_GEN_APPLICATION_MARK_APP_NOT_COMPLETELY_BUSY();

This message undoes MSG_GEN_APPLICATION_MARK_COMPLETELY_BUSY , allowing the application to once again handle user input.

Source: Infrequently used.

Destination: The GenApplication object to be marked not busy.

Interception: Do not intercept.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 An Application's Life Cycle | Next: 2.3 The GenApplication's Moniker


The GenApplication's Moniker

The Application Object: 2.3 Advanced GenApplication Usage: The GenApplication's Moniker

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Application Busy States | Next: 2.4 Measurement Type
MSG_GEN_APPLICATION_FIND_MONIKER, MSG_GEN_APPLICATION_SET_TASK_ENTRY_MONIKER

Every GenApplication object should be given a moniker; this moniker is displayed by the UI in its task list. (In OSF/Motif, the task list is manifested as the floating "Express" menu.) While you will set the moniker just like for any generic object, the GenApplication has two messages that can be used to get or set the moniker used in the task list.

MSG_GEN_APPLICATION_FIND_MONIKER

optr	MSG_GEN_APPLICATION_FIND_MONIKER(
        MemHandle		destBlock,
        word		searchFlags,
        DisplayType		displayType);

This message finds the specified moniker in the GenApplication's VisMonikerList and optionally copies it into a specified destination block.

Source: Infrequently used.

Destination: The GenApplication to get the moniker from.

Parameters: destBlock The handle of the destination block into which the chunk will be copied. For this to work, you must pass VMSF_COPY_CHUNK in searchFlags .

searchFlags
A record of VisMonikerSearchFlags indicating what type of moniker to find in the moniker list and what to do with it when it is found.
displayType
The display type of the moniker to search for.

Return: The optr of the GenApplication's VisMonikerList chunk.

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_TASK_ENTRY_MONIKER

void	MSG_GEN_APPLICATION_SET_TASK_ENTRY_MONIKER(
        optr	entryMoniker);

This message changes the moniker which is used in the GenField's task list. The task list menu will be updated if currently visible.

Source: Infrequently used.

Destination: The GenApplication to have its moniker changed.

Parameters: entryMoniker The optr of the chunk containing the moniker that will be set into the task list.

Return: Nothing.

Interception: Do not intercept.

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Application Busy States | Next: 2.4 Measurement Type


Measurement Type

The Application Object: 2.4 Advanced GenApplication Usage: Measurement Type

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 The GenApplication's Moniker | Next: 2.5 Interaction with the UI
MSG_GEN_APPLICATION_SET_MEASUREMENT_TYPE, MSG_GEN_APPLICATION_GET_MEASUREMENT_TYPE, GET_APP_MEASUREMENT_TYPE

Each application has a "measurement type" associated with it. The measurement type indicates whether measurements should default to metric or standard US measurements.

MSG_GEN_APPLICATION_SET_MEASUREMENT_TYPE

void	MSG_GEN_APPLICATION_SET_MEASUREMENT_TYPE(
        byte	measurementType);

This message sets the measurement type used by the application.

Source: Infrequently used.

Destination: The GenApplication to have the new measurement type.

Parameters: measurementType
 A value of AppMeasurementType to set for the application. Can be AMT_US, AMT_METRIC, or AMT_DEFAULT.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_GET_MEASUREMENT_TYPE

word	MSG_GEN_APPLICATION_GET_MEASUREMENT_TYPE();

This message gets the measurement currently used by the application.

Source: Infrequently used.

Destination: The GenApplication whose measurement is to be retrieved.

Parameters: None.

Return: A word value, the low byte of which represents the application's measurement type. Use the macro GET_MEASUREMENT_TYPE to extract the measurement type from the return value.

Interception: Do not intercept.

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 The GenApplication's Moniker | Next: 2.5 Interaction with the UI


Interaction with the UI

The Application Object: 2.5 Advanced GenApplication Usage: Interaction with the UI

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Measurement Type

The GenApplication is an application's main point of contact with the UI. As such, it has several messages that are sent by the UI or by other objects to initiate certain UI-related functions. These messages will rarely, if ever, be used by application programmers, but they are documented here in case you find them useful.

Attaching and Detaching

MSG_GEN_APPLICATION_INITIATE_UI_QUIT, MSG_GEN_APPLICATION_INSTALL_TOKEN, MSG_GEN_APPLICATION_DETACH_PENDING, MSG_GEN_APPLICATION_OPEN_COMPLETE, MSG_GEN_APPLICATION_QUIT_AFTER_UI

MSG_GEN_APPLICATION_INITIATE_UI_QUIT

void	MSG_GEN_APPLICATION_INITIATE_UI_QUIT();

This message causes the GenApplication to begin quitting. The application will automatically go through the entire quit sequence.

Source: Infrequently used.

Destination: The GenApplication of the application to be shut down.

Interception: Do not intercept.

MSG_GEN_APPLICATION_INSTALL_TOKEN

void	MSG_GEN_APPLICATION_INSTALL_TOKEN();

This message instructs the GenApplication object to set its token into the token database file.

Source: Infrequently used.

Destination: The GenApplication to have its token installed.

Interception: Do not intercept.

MSG_GEN_APPLICATION_DETACH_PENDING

void	MSG_GEN_APPLICATION_DETACH_PENDING();

This message is sent to the specific UI library through the GenApplication to notify it that the application is about to be shut down. It is used to abort any application-modal dialog boxes so the application's Process object will be able to detach.

Source: The GenApplication object before it detaches.

Destination: The GenApplication of the application about to be detached.

Interception: Do not intercept.

MSG_GEN_APPLICATION_OPEN_COMPLETE

void	MSG_GEN_APPLICATION_OPEN_COMPLETE();

This message is sent by the GenApplication object to itself when it has finished opening (after it has set itself usable). It is sent via the queue and indicates that the application's UI is fully usable.

Source: A GenApplication after it has set itself GS_USABLE.

Destination: Sent to itself.

Interception: Do not intercept.

MSG_GEN_APPLICATION_QUIT_AFTER_UI

void	MSG_GEN_APPLICATION_QUIT_AFTER_UI();

This message is called from the MSG_META_QUIT handler in GenProcessClass, after the UI has finished its MSG_GEN_APPLICATION_INITIATE_UI_QUIT sequence. This message is the application's last chance to abort a quit before the DETACH sequence begins. The default behavior is to abort the QUIT if the application is still open for the user (i.e. not ALF_OPEN_FOR_IACP_CONNECTION_ONLY) or if an IACP connection remains that requires the application to remain open.

Source: GenProcessClass .

Destination: GenApplication object of the application.

Interception: May be intercepted and not sent to the superclass to abort the QUIT.

Queries of the UI

MSG_GEN_APPLICATION_GET_DISPLAY_SCHEME, MSG_GEN_APPLICATION_QUERY_UI, MSG_GEN_APPLICATION_NOTIFY_MODAL_WIN_CHANGE, MSG_GEN_APPLICATION_INK_QUERY_REPLY, MSG_GEN_APPLICATION_GET_GCN_LIST_OF_LISTS, MSG_GEN_APPLICATION_TEST_WIN_INTERACTABILITY, MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION, MSG_GEN_APPLICATION_GET_MODAL_WIN, MSG_GEN_APPLICATION_CHECK_IF_ALWAYS_INTERACTABLE_OBJECT

MSG_GEN_APPLICATION_GET_DISPLAY_SCHEME

void	MSG_GEN_APPLICATION_GET_DISPLAY_SCHEME(
        DisplayScheme * displayScheme);

This message gets the current display scheme used by the application.

Source: Infrequently used.

Destination: Any GenApplication.

Parameters: displayScheme A pointer to a structure of type DisplayScheme . This structure will be filled by the method and contains information about the color scheme, display type, font ID, and point size used by the application.

Return: The DisplayScheme structure pointed to by displayScheme will be filled upon return.

Interception: Do not intercept.

MSG_GEN_APPLICATION_QUERY_UI

Handle	MSG_GEN_APPLICATION_QUERY_UI();

This message is used to determine which UI should be used at a given point in the generic tree for a certain type of object.

Source: Infrequently used.

Destination: Any GenApplication object.

Parameters: None.

Return: The handle of the specific UI library geode.

Interception: Do not intercept.

MSG_GEN_APPLICATION_NOTIFY_MODAL_WIN_CHANGE

void	MSG_GEN_APPLICATION_NOTIFY_MODAL_WIN_CHANGE();

This message is called on a GenApplication object by the UI whenever the application should check to see if there is a change in modal status. The behavior is to look for the top system-modal window owned by the application and then the top application-modal window within the application's layer.

This message sets the AS_ATTACHED_TO_STATE_FILE in the GenApplication's GAI_states field.

Source: Sent by the UI.

Destination: Any GenApplication object.

Interception: Do not intercept.

MSG_GEN_APPLICATION_INK_QUERY_REPLY

void	MSG_GEN_APPLICATION_INK_QUERY_REPLY(
        InkReturnValue		inkReturnValue,
        word		inkGstate);

This message is sent to an Application object in reply to a MSG_META_QUERY_IF_PRESS_IS_INK . It indicates whether the object that was queried can or can not handle Ink presses. The GenApplication object responds by sending a message to the UI.

Source: Sent by an object in response to MSG_META_QUERY_IF_PRESS_IS_INK .

Destination: The GenApplication object associated with the sender.

Parameters: inkReturnValue A value indicating whether the object queried can handle Ink input or not. Will be one of IRV_NO_INK, IRV_INK_WITH_STANDARD_OVERRIDE, IRV_DESIRES_INK, or IRV_WAIT.

inkGstate
The GState, if any, to be used when drawing Ink.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_GET_GCN_LIST_OF_LISTS

ChunkHandle MSG_GEN_APPLICATION_GET_GCN_LIST_OF_LISTS();

This message retrieves the GenApplication's GCN list of lists chunk handle. This chunk handle may then be used with a number of kernel routines for GCN list management or to perform operations on individual GCN lists.

Source: Any object in the GenApplication's thread.

Destination: Any GenApplication object.

Parameters: None.

Return: The chunk handle of the GCN list of lists chunk; a null chunk handle will be returned if the chunk does not exist.

Interception: Do not intercept.

MSG_GEN_APPLICATION_TEST_WIN_INTERACTABILITY

Boolean	MSG_GEN_APPLICATION_TEST_WIN_INTERACTABILITY(
        optr	inputOD,
        Handle	window);

This message tests whether the passed window object is interactable.

Source: Unrestricted.

Destination: Any GenApplication object.

Parameters: inputOD The optr of the windowed object to be tested.

window
The window handle of the window to be tested.

Return: The return value will be false if there are no modal windows in the system or if the window object passed is the topmost active modal window. The return value will be true if an active modal window exists and is not the passed window object (in this case, if the passed object has any window grabs, it should release them). True is also returned if there is no modal window but the GenApplication is ignoring input.

Interception: Do not intercept.

MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION

void	MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION(
        optr		obj,
        Boolean 		opening);

Notifies the GenApplication object that it has become visible or not visible.

Source: The specific UI.

Destination: The GenApplication that has become visible or not visible.

Parameters: obj The optr of the object sending the notification message.

opening
A Boolean indicating the state of the object: true if open, false if closed.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_GET_MODAL_WIN

optr	MSG_GEN_APPLICATION_GET_MODAL_WIN();

This message returns the current top modal window for the application, if any is present.

Source: Unrestricted.

Destination: GenApplication object.

Return: optr of top modal windowed object.

MSG_GEN_APPLICATION_CHECK_IF_ALWAYS_INTERACTABLE_OBJECT

Boolean	MSG_GEN_APPLICATION_CHECK_IF_ALWAYS_INTERACTABLE_OBJECT(
        optr		objToCheck);

This message checks if the passed object should always remain interactable.

Source: Unrestricted.

Destination: GenApplication object.

Parameters: objToCheck optr of object to check the interactable state.

Return: true if the object is always interactable, false if not.

Alterations of Functionality

MSG_GEN_APPLICATION_BRING_WINDOW_TO_TOP, MSG_GEN_APPLICATION_LOWER_WINDOW_TO_BOTTOM, MSG_GEN_APPLICATION_BUILD_STANDARD_DIALOG, MSG_GEN_APPLICATION_DO_STANDARD_DIALOG, MSG_GEN_APPLICATION_TOGGLE_CURSOR, MSG_GEN_APPLICATION_BRING_UP_HELP, MSG_GEN_APPLICATION_TOGGLE_CURRENT_MENU_BAR, MSG_GEN_APPLICATION_TOGGLE_FLOATING_KEYBOARD, MSG_GEN_APPLICATION_TOGGLE_EXPRESS_MENU

MSG_GEN_APPLICATION_BRING_WINDOW_TO_TOP

void	MSG_GEN_APPLICATION_BRING_WINDOW_TO_TOP(
        optr	window);

This message brings the passed window to the front of the screen.

Source: Unrestricted

Destination: Any GenApplication object.

Parameters: window The optr of the window object to be brought to the front of the screen.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_LOWER_WINDOW_TO_BOTTOM

void	MSG_GEN_APPLICATION_LOWER_WINDOW_TO_BOTTOM(
        optr	window);

This message sends the specified window to the back of the screen, behind other window objects.

Source: Unrestricted.

Destination: Any GenApplication object.

Parameters: window The optr of the window object to be sent to the back of the screen.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_BUILD_STANDARD_DIALOG

optr	MSG_GEN_APPLICATION_BUILD_STANDARD_DIALOG(
        char	* customTriggers,
        char	* arg2,
        char	* arg1,
        char	* string,
        CustomDialogBoxFlags dialogFlags);

This message builds a standard dialog box for the application.

Source: Infrequently used.

Destination: The GenApplication to use the dialog box.

Parameters: customTriggers A pointer to a table of custom GenTrigger information. Each trigger given in the table will appear in the dialog box in the order declared. The table is made up of structures of type StandardDialogResponseTriggerTable .

arg 1
A pointer to a character string to be displayed in the dialog box.
arg 2
A pointer to a second string to be displayed in the dialog box.
string
A pointer to a custom character string to be displayed in the dialog box.
dialogFlags
A record of CustomDialogBoxFlags indicating what type of dialog box is to be created.

Return: The optr of the dialog box object.

Interception: Do not intercept.

MSG_GEN_APPLICATION_DO_STANDARD_DIALOG

void	MSG_GEN_APPLICATION_DO_STANDARD_DIALOG(@stack
        word	dialogMethod,
        optr	dialogOD,
        char	*helpContext,
        char	* customTriggers,
        char	* arg2,
        char	* arg1,
        char	* string,
        CustomDialogBoxFlags dialogFlags);

This message executes a standard dialog box and returns immediately. When the dialog box is shut down, the message passed in the dialogMethod parameter is sent to the object specified in dialogOD . Only one dialog box at a time may be displayed with this message.

Source: Infrequently used.

Destination: The GenApplication to use the created dialog box.

Parameters: dialogMethod The message to be sent out when the user is finished with the dialog. This message should be defined based on the prototype GEN_APP_DO_STANDARD_DIALOG_MSG .

dialogOD
The recipient of the message specified in dialogMethod above.
helpContext
The help context for this dialog box.
customTriggers
A pointer to a table of custom GenTrigger information. Each trigger given in the table will appear in the dialog box in the order declared. The table is made up of structures of type StandardDialogResponseTriggerTable .
arg2
A pointer to a second string to be displayed in the dialog box.
arg1
A pointer to a character string to be displayed in the dialog box.
string
A pointer to a custom character string to be displayed in the dialog box.
dialogFlags
A record of CustomDialogBoxFlags indicating what type of dialog box is to be created.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_TOGGLE_CURSOR

void	MSG_GEN_APPLICATION_TOGGLE_CURSOR();

This message toggles the cursor for a text object.

Source: Infrequently used.

Destination: The GenApplication of the text object.

Interception: Do not intercept.

MSG_GEN_APPLICATION_BRING_UP_HELP

void	MSG_GEN_APPLICATION_BRING_UP_HELP();

This message brings up help for an application. Normally, this is accomplished by sending a message to the focus object telling it to bring up a help window with the focus' help context.

Source: Unrestricted, though generally from an application Help icon or <F1>.

Destination: GenApplication object.

Interception: Generally not intercepted, though it may be useful if for some reason you do not wish to bring up help (such as it doesn't exist for this application).

MSG_GEN_APPLICATION_TOGGLE_CURRENT_MENU_BAR

void	MSG_GEN_APPLICATION_TOGGLE_CURRENT_MENU_BAR();

This message toggles the GIV_POPOUT state of the current GenPrimary's menu bar. This message only takes effect if the menu bar is toggleable (i.e. if UIWO_POPOUT_MENU_BAR is set).

Source: Unrestricted.

Destination: GenApplication object.

MSG_GEN_APPLICATION_TOGGLE_FLOATING_KEYBOARD

void	MSG_GEN_APPLICATION_TOGGLE_FLOATING_KEYBOARD();

This message toggles the state of the floating keyboard within the current application. Applications may subclass this to bring up their own PenInputControl (or equivalent) object. Otherwise the application object will create its own.

Source: Unrestricted, though generally only supported on pen-based systems.

Destination: GenApplication object.

Interception: May be intercepted if the application has its own PenInputControl object.

MSG_GEN_APPLICATION_TOGGLE_EXPRESS_MENU

void	MSG_GEN_APPLICATION_TOGGLE_EXPRESS_MENU();

This message toggles (opens or closes) the parent field's express menu.

Source: Unrestricted.

Destination: GenApplication object.

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 Measurement Type


Window Objects

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 A First Look at GenDisplay
| One-page Version of this Chapter for Easy Printing

Applications communicate with users through a "user interface." The system needs a way of grouping the user-interface components together. For this reason, most applications will have a GenPrimary object. This object serves as the top-level object in the user interface tree.

GenPrimaryClass is a subclass of GenDisplayClass . The GenDisplay object, like the GenPrimary object, manages other pieces of the user interface (menus, triggers, text objects, etc.). Furthermore, the GenDisplay object is a great boon to applications that need to perform several tasks at once (as, for example, a word processor which can have several files open at once). An application can have several GenDisplay objects, all of them children of a GenDisplayGroup object. Collectively, displays and primaries are called windows . All windows have certain functionality in common; that functionality can vary according to the specific UI.

The Document Control objects can be set to create GenDisplay objects when files are opened and to attach them automatically to a GenDisplayGroup. This enormously simplifies managing multiple documents. See the Documents chapter for information on the Document Control objects.

These objects are very simple to use. In particular, the GenPrimary is very easy to declare and use. Although all applications use the GenPrimary, many will not need the GenDisplay. However, because GenPrimaryClass is a subclass of GenDisplayClass , this chapter begins with an overview of GenDisplayClass , then continues with a full discussion of GenPrimaryClass , and concludes with advanced information about GenDisplayClass . Before reading this chapter, you should be familiar with the use and creation of generic user interface objects.

1 A First Look at GenDisplay
1.1 GenDisplay Object Structure
1.2 Minimizing and Maximizing
2 Using the GenPrimary
3 Using Multiple Displays
3.1 GenDisplayGroup
3.2 GenDisplayControl
3.3 Using GenDisplayClass Objects

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 A First Look at GenDisplay
| One-page Version of this Chapter for Easy Printing


A First Look at GenDisplay

Window Objects: 1 A First Look at GenDisplay

| Up: GEOS SDK TechDocs | Up | Down | Prev: Window Objects | Next: 2 Using the GenPrimary

Not all applications will need to use GenDisplay objects. However, almost all applications will have a GenPrimary object. Since GenPrimaryClass is a subclass of GenDisplayClass , programmers should be acquainted with GenDisplayClass .

This section describes the data fields of GenDisplayClass and certain useful messages. It does not have all the information you will need to create these objects. If you will be using GenDisplay objects in your application, you will have to read Using Multiple Displays .

GenDisplay Object Structure

Minimizing and Maximizing

| Up: GEOS SDK TechDocs | Up | Down | Prev: Window Objects | Next: 2 Using the GenPrimary


GenDisplay Object Structure

Window Objects: 1.1 A First Look at GenDisplay: GenDisplay Object Structure

| Up: GEOS SDK TechDocs | Up | Prev: 1 A First Look at GenDisplay | Next: 1.2 Minimizing and Maximizing

The GenDisplay object is a subclass of GenClass and therefore inherits all the data fields and attributes of that class. It has few data fields that are set by the application; these fields are listed in Instance Data of GenDisplayClass .

Code Display 4-1 Instance Data of GenDisplayClass

/* There are only two instance fields specifically defined for GenDisplayClass.
 * Also, an instance field for GenClass, GI_attrs, has different defaults in
 * GenDisplayClass. */
/* GDI_attributes is a one-byte field for attributes flags. There is only one flag
 * defined for this field, namely GDA_USER_DISMISSABLE, which is on by default. */
    @instance GenDisplayAttrs				GDI_attributes = GDA_USER_DISMISSABLE;
/* The GenDisplay object has a datum for an optr to a document object. If a
 * Document Control is used to create display objects, it will associate each
 * display with a document object; each will have an optr to the other. */
    @instance optr				GDI_document;
/* The default setting of GI_attrs is different in GenDisplayClass than it is in
 * GenClass: */
    @default GI_attrs = 			(@default
             | GA_TARGETABLE
             | GA_KBD_SEARCH_PATH);
/* The following hints specify whether the display should be minimized or
 * maximized when it is built, and its appearance when minimized. */
    @vardata void  HINT_DISPLAY_MINIMIZED_ON_STARTUP;
    @vardata void HINT_DISPLAY_NOT_MINIMIZED_ON_STARTUP;
    @vardata void HINT_DISPLAY_MAXIMIZED_ON_STARTUP;
    @vardata void HINT_DISPLAY_NOT_MAXIMIZED_ON_STARTUP;
    @vardata void HINT_DISPLAY_USE_APPLICATION_MONIKER_WHEN_MINIMIZED;
/* The following hints and attributes indicate whether the user should be able to
 * minimize, maximize, or resize the window. */
    @vardata void  ATTR_GEN_DISPLAY_NOT_MINIMIZABLE;
    @vardata void ATTR_GEN_DISPLAY_NOT_MAXIMIZABLE;
    @vardata void HINT_DISPLAY_NOT_RESIZABLE;
/* ATTR_GEN_DISPLAY_NOT_RESTORABLE indicates that the user should not be able to
 * de-maximize the display once it is maximized. */
    @vardata void ATTR_GEN_DISPLAY_NOT_RESTORABLE;
/* ATTR_GEN_DISPLAY_TRAVELING_OBJECTS is the ChunkHandle of a list of "traveling
 * objects;" these objects are made the children of whichever GenDisplay is on top
 * in a given display region (see  Traveling Objects).*/
    @vardata ChunkHandle ATTR_GEN_DISPLAY_TRAVELING_OBJECTS;
/* The following hints and attributes specify whether the display's menu bar
 * appears and whether it appears as a "popped out" floating menu. */
    @vardata void HINT_DISPLAY_MENU_BAR_HIDDEN_ON_STARTUP;
    @vardata void  TEMP_GEN_DISPLAY_MENU_BAR_HIDDEN;
    @vardata void ATTR_GEN_DISPLAY_MENU_BAR_POPPED_OUT;
    @vardata void HINT_DISPLAY_USE_APPLICATION_MONIKER_WHEN_MENU_BAR_POPPED_OUT;
/* The GenDisplay uses the following vardata fields to store its
 * minimized/maximized state across a shutdown. You should not access these
 * fields. If you want to find out if a GenDisplay is minimized or maximized, send
 * it MSG_GEN_DISPLAY_GET_MINIMIZED or MSG_GEN_DISPLAY_GET_MAXIMIZED. */
    @vardata		void ATTR_GEN_DISPLAY_MINIMIZED_STATE;
    @vardata		void ATTR_GEN_DISPLAY_MAXIMIZED_STATE;
/* HINT_DISPLAY_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD specifies the default
 * action for the GenDisplay. */
    @vardata void HINT_DISPLAY_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD;

The GDI_attributes Field

MSG_GEN_DISPLAY_GET_ATTRS, MSG_GEN_DISPLAY_SET_ATTRS

The GenDisplay object has a one-byte record called GDI_attributes to store attribute flags. There is only one attribute flag, namely GDA_USER_DISMISSABLE . If this attribute is set, the user can dismiss a display through the UI (without choosing a command in the application). Details of this depend on the specific UI; for example, in OSF/Motif, the user could dismiss a display by double-clicking the "Control button."

MSG_GEN_DISPLAY_GET_ATTRS

GenDisplayAttrs 	MSG_GEN_DISPLAY_GET_ATTRS();

This message retrieves the GDI _attributes field from the destination object.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object.

Return: A GenDisplayAttrs record. The only flag defined is GDA_USER_DISMISSABLE.

Interception: This message is not normally intercepted.

MSG_GEN_DISPLAY_SET_ATTRS

void	MSG_GEN_DISPLAY_SET_ATTRS(
        GenDisplayAttrs		attrs);

This message changes the GenDisplayAttrs field of the destination object.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Parameters: attrs Field of GenDisplayAttrs flags. There is only one flag defined, namely GDA_USER_DISMISSABLE.

Interception: This message is not normally intercepted.

The GDI_document Field

Applications often use the Document Control objects to manage files. With this mechanism, every file is associated with a document object. Often, each file will have its own GenDisplay object as well. In this case, GDI _document will contain an optr to the GenDocument object associated with this GenDisplay. For more information on this, see Using Multiple Displays . The Document Control objects create and destroy the GenDisplays automatically, and set this field accordingly. The GenDisplay object uses this field only when the display is closed; see Closing GenDisplays . To retrieve the value of this field, send MSG_GEN_DISPLAY_GET_DOCUMENT to the display (see Messages sent to GenDisplays ).

| Up: GEOS SDK TechDocs | Up | Prev: 1 A First Look at GenDisplay | Next: 1.2 Minimizing and Maximizing


Minimizing and Maximizing

Window Objects: 1.2 A First Look at GenDisplay: Minimizing and Maximizing

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 GenDisplay Object Structure | Next: 2 Using the GenPrimary
MSG_GEN_DISPLAY_SET_MINIMIZED, MSG_GEN_DISPLAY_SET_NOT_MINIMIZED, MSG_GEN_DISPLAY_GET_MINIMIZED, MSG_GEN_DISPLAY_SET_MAXIMIZED, MSG_GEN_DISPLAY_SET_NOT_MAXIMIZED, MSG_GEN_DISPLAY_GET_MAXIMIZED, HINT_DISPLAY_MINIMIZED_ON_STARTUP, HINT_DISPLAY_NOT_MINIMIZED_ON_STARTUP, HINT_DISPLAY_MAXIMIZED_ON_STARTUP, HINT_DISPLAY_NOT_MAXIMIZED_ON_STARTUP, ATTR_GEN_DISPLAY_NOT_MINIMIZABLE, HINT_DISPLAY_NOT_MAXIMIZABLE, ATTR_GEN_DISPLAY_NOT_RESTORABLE,
HINT_DISPLAY_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD

Windows (i.e. displays and "primary" windows) can be resized by the user. How resizing is done depends on the specific UI; for example, in OSF/Motif, a user resizes a window by dragging its edge. Most specific UIs also allow the user to "minimize" or "maximize" a window. When a window is maximized, it is expanded to fill all available space; that is, a primary window will fill the screen, while a GenDisplay will fill the display area. Windows can also be "minimized." A window's behavior when it is minimized depends on the specific UI. For example, in OSF/Motif, a minimized Primary is displayed as an icon at the bottom of the screen; a minimized Display is removed from the display area, but stays in the display control's display list.

Most of the mechanics of minimizing and maximizing windows is taken care of by the specific UI. For example, OSF/Motif provides minimize and maximize buttons on all Displays and Primaries which do not specifically disable the functionality. However, an application can send messages to Primary and Display objects to change their minimized/maximized state, or to find out what the current state is.

If you do not wish to have a window be minimizable, you can set the vardata flag ATTR_GEN_DISPLAY_NOT_MINIMIZABLE. Similarly, if you do not wish the window to be maximizable, you can set the flag ATTR_GEN_DISPLAY_NOT_MAXIMIZABLE. These instruct the specific UI not to provide the controls for minimizing and maximizing.

You can find out whether a window is currently minimized by sending it MSG_GEN_DISPLAY_GET_MINIMIZED. Similarly, you can find out whether the window is maximized by sending MSG_GEN_DISPLAY_GET_MAXIMIZED.

If a GenDisplay or GenPrimary has the vardata HINT_DISPLAY_MINIMIZED_ON_STARTUP , the object will be created in its minimized state. Similarly, if you set HINT_DISPLAY_NOT_MINIMIZED_ON_STARTUP, the display will be created in its non-minimized form. If you set HINT_DISPLAY_MAXIMIZED_ON_STARTUP, the specific UI will create the object in its maximized state; correspondingly, if you set HINT_DISPLAY_NOT_MAXIMIZED_ON_STARTUP, the specific UI will create the display in a non-maximized state. As with all hints, the specific UI may ignore these directives. If you set conflicting hints (for example, both HINT_DISPLAY_MINIMIZED_ON_STARTUP and HINT_DISPLAY_MAXIMIZED_ON_STARTUP), the results are undefined.

Most displays which can be maximized can also be "restored"; that is, a control is provided which de-maximizes the object, restoring it to the size it was before it was maximized. If the object has the attribute ATTR_GEN_DISPLAY_NOT_RESTORABLE, this control will not be provided; once a display is maximized, the user will not be able to un-maximize it. This hint is generally set only for GenDisplay objects that are maximized on startup.

If you do not want a user to be able to resize a GenDisplay or GenPrimary, set the vardata HINT_DISPLAY_NOT_RESIZABLE. The specific UI will not provide the means for the user to resize the window. This hint will not prevent the user from minimizing or maximizing the display.

If you want the user to be able to navigate from GenDisplays using TAB navigation, add HINT_DISPLAY_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD to the object's instance data.

MSG_GEN_DISPLAY_SET_MINIMIZED

void	MSG_GEN_DISPLAY_SET_MINIMIZED();

This message instructs a display or primary object to minimize itself. The result depends on the specific UI. Primary windows are usually iconified; display windows might be iconified or temporarily removed. If the window is already minimized, the message has no effect.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Interception: You should not change the behavior of this message. You may intercept this message to find out when a window is being minimized; however, you should make sure to pass this message on to the superclass.

MSG_GEN_DISPLAY_SET_MAXIMIZED

void	MSG_GEN_DISPLAY_SET_MAXIMIZED();

This message instructs a display or primary object to maximize itself. If the window is already maximized, the message has no effect.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Interception: You should not change the behavior of this message. You may intercept this message to find out when a window is being maximized; however, you should make sure to pass this message on to the superclass.

MSG_GEN_DISPLAY_SET_NOT_MINIMIZED

void	MSG_GEN_DISPLAY_SET_NOT_MINIMIZED();

This message instructs a display or primary object to de-minimize itself. It will generally be restored to its position and configuration as of the time it was minimized. If the window is not minimized, the message has no effect.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Interception: You should not change the behavior of this message. You may intercept this message to find out when a window is being de-minimized; however, you should make sure to pass this message on to the superclass.

MSG_GEN_DISPLAY_SET_NOT_MAXIMIZED

void	MSG_GEN_DISPLAY_SET_NOT_MAXIMIZED();

This message instructs a display or primary object to de-maximize itself. It will generally be restored to its position and configuration as of the time it was maximized. If the window is not maximized, the message has no effect.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Interception: You should not change the behavior of this message. You may intercept this message to find out when a window is being de-maximized; however, you should make sure to pass this message on to the superclass.

MSG_GEN_DISPLAY_GET_MINIMIZED

Boolean	MSG_GEN_DISPLAY_GET_MINIMIZED();

This message indicates whether the recipient is minimized.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Return: Returns true (i.e. non-zero) if recipient is minimized; otherwise, it returns false (i.e. zero).

Interception: You should not intercept this message.

MSG_GEN_DISPLAY_GET_MAXIMIZED

Boolean	MSG_GEN_DISPLAY_GET_MAXIMIZED();

This message indicates whether the recipient is maximized.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Return: Returns true (i.e. non-zero) if recipient is maximized; otherwise, it returns false (i.e. zero).

Interception: You should not intercept this message.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 GenDisplay Object Structure | Next: 2 Using the GenPrimary


Using the GenPrimary

Window Objects: 2 Using the GenPrimary

| Up: GEOS SDK TechDocs | Up | Prev: 1 A First Look at GenDisplay | Next: 3 Using Multiple Displays
MSG_GEN_PRIMARY_GET_LONG_TERM_MONIKER, MSG_GEN_PRIMARY_USE_LONG_TERM_MONIKER, MSG_GEN_PRIMARY_REPLACE_LONG_TERM_MONIKER, HINT_PRIMARY_FULL_SCREEN, HINT_PRIMARY_NO_FILE_MENU, HINT_PRIMARY_NO_EXPRESS_MENU, HINT_PRIMARY_OPEN_ICON_BOUNDS, HINT_PRIMARY_NO_HELP_BUTTON

Almost every application will have a single GenPrimary object. GEOS uses the GenPrimary to create and manage the primary window of an application. A few applications may have several GenPrimary objects; a very few will have no GenPrimary at all. (Applications with no GenPrimary generally do not have any user interface; they often are intended to work with other applications, communicating via streams.)

The structure of the GenPrimary object is almost the same as that of GenDisplay. The instance data definitions for GenPrimaryClass are shown in Instance data for GenPrimaryClass below.

Code Display 4-2 Instance data for GenPrimaryClass

/* There is only one instance field specifically defined for GenPrimaryClass. */
/* A GenPrimary object can have a long moniker, which is displayed at the top of
 * the window. The moniker is stored in a chunk in the object block containing the
 * GenPrimary; the attribute GPI_longTermMoniker contains the handle of this
 * chunk. The long-term moniker is described below.*/
    @instance ChunkHandle				GPI_longTermMoniker;
/* GenPrimaryClass also modifies the default GI_attrs settings. */
    @default GI_attrs = @default | GA_TARGETABLE;
/* HINT_PRIMARY_FULL_SCREEN indicates that the primary object should be sized to
 * fill a large portion of the screen. If this hint is not present, the Primary
 * will be just large enough to accommodate its children.*/
    @vardata void 			HINT_PRIMARY_FULL_SCREEN;
/* Ordinarily, every Primary window is created with a File menu. You can suppress
 * this by including HINT_PRIMARY_NO_FILE_MENU. */
    @vardata void 			HINT_PRIMARY_NO_FILE_MENU;
/* Also by default, any launched Primary window gets added to the Express Menu. If
 * you wish to suppress this behavior, add HINT_PRIMARY_NO_EXPRESS_MENU. */
    @vardata void			HINT_PRIMARY_NO_EXPRESS_MENU;
/* If a primary object is minimizable, the location of the minimized primary is
 * stored in the vardata field HHINT_PRIMARY_OPEN_ICON_BOUNDS.
 */
    @vardata Rectangle			 HINT_PRIMARY_OPEN_ICON_BOUNDS
/* By default, all primary windows have a "help" button; when the user clicks on
 * it, the window's help text is brought up. If you don't want the primary to
 * provide help text, you can use the hint HINT_PRIMARY_NO_HELP_BUTTON.
 */
    @vardata void 		 HINT_PRIMARY_NO_HELP_BUTTON;

When a Primary window is created, it is usually sized to contain all of its components. However, you can suggest that it be sized to fill almost all the screen by setting the hint HINT_PRIMARY_FULL_SCREEN . This hint says that the Primary should be sized to fill a large portion of the screen, though not all of it. (For example, OSF/Motif sets the Primary to fill the whole screen except for a narrow space for icons at the bottom.) If this hint is not present, the Primary will be just large enough to accommodate its children.

A GenPrimary normally creates a File menu within its menu bar. To suppress creation of this file menu, add HINT_PRIMARY_NO_FILE_MENU . GenPrimarys, by default, are also added to the list of active applications within the system's express menu. Add HINT_PRIMARY_NO_EXPRESS_MENU if you wish to avoid adding the launched GenPrimary to the express menu.

When a Primary is minimized, it is displayed as an icon with a caption beneath it. The icon and caption will be taken from the Primary's moniker list (GI _visMoniker ). If the Primary lacks either a text moniker or a graphic moniker, the missing moniker will be read from the Application object's GI _visMoniker field. Most applications will not set GI _visMoniker in the Primary object, since it would usually mean duplicating the monikers already in the Application object. However, some applications will set this (e.g. because they have several Primary objects and want each one to have a different icon when minimized).

When the Primary is expanded from a minimized state, its minimized location is stored in the hint HINT_PRIMARY_OPEN_ICON_BOUNDS. The next time the Primary is minimized, it will be returned to that location.

GPI_longTermMoniker contains a secondary moniker for the Primary object. This moniker is displayed along with the Primary moniker, in a way which depends on the specific UI. (In OSF/Motif, the Primary's text monikers are shown in its title bar: first the text moniker from GI _visMoniker , then a dash, then the moniker from GPI_longTermMoniker .) If the GenPrimary has a GenDisplayGroup as a child, the GenDisplayGroup will automatically set this field to contain the moniker of the top-most GenDisplay. The application can override this by sending the Primary MSG_GEN_PRIMARY_USE_LONG_TERM_MONIKER, described below.

Most Primary objects will have help text; under most specific UIs, the Primary will have a "help" button to bring up this text. If you don't want to provide help text, you should set the hint HINT_PRIMARY_NO_HELP_BUTTON .

MSG_GEN_PRIMARY_GET_LONG_TERM_MONIKER
ChunkHandle 	MSG_GEN_PRIMARY_GET_LONG_TERM_MONIKER();

Use this message to find out the moniker of a GenPrimary object. The message returns the chunk handle of the moniker; the moniker is in the same block as the GenPrimary object.

Source: Unrestricted.

Destination: GenPrimary.

Return: Returns the chunk handle of the primary's long-term moniker. The chunk is in the same object block as the GenPrimary.

Interception: This message is not ordinarily intercepted.

MSG_GEN_PRIMARY_USE_LONG_TERM_MONIKER
void 	MSG_GEN_PRIMARY_USE_LONG_TERM_MONIKER(
        ChunkHandle		moniker);			/* must be in same object block as
                     * primary */

This message instructs a primary window to change its long-term moniker. The new long-term moniker must already be in a chunk in the same object block as the Primary. The chunk containing the obsolete long-term moniker will not be freed; you must do this manually.

Source: Unrestricted.

Destination: GenPrimary.

Parameters: moniker ChunkHandle of chunk in same object block as the GenPrimary. The chunk should contain the new long-term moniker.

Interception: This message is not ordinarily intercepted.

MSG_GEN_PRIMARY_REPLACE_LONG_TERM_MONIKER
void	MSG_GEN_PRIMARY_REPLACE_LONG_TERM_MONIKER(@stack
        VisUpdateMode 		updateMode,
        word 		height,
        word 		width,
        word 		length,
        VisMonikerDataType 		dataType,
        VisMonikerSourceType 		sourceType,
        dword 		source);

This message is used to replace a primary's long term moniker with a new one. This message's arguments are precisely like those to the message MSG_GEN_REPLACE_VIS_MONIKER . Note that a long term moniker is ordinarily a simple text string. For more information, see the GenClass chapter.

Source: Unrestricted.

Destination: GenPrimary.

Parameters: The parameters are the same as those for MSG_GEN_REPLACE_VIS_MONIKER .

Return: Returns the chunk handle of the new long-term moniker. The moniker will be allocated in the Primary's object block.

Interception: This message is not ordinarily intercepted.

| Up: GEOS SDK TechDocs | Up | Prev: 1 A First Look at GenDisplay | Next: 3 Using Multiple Displays


Using Multiple Displays

Window Objects: 3 Using Multiple Displays

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Using the GenPrimary | Next: 3.1 GenDisplayGroup

Many applications will need to have several similar user interface areas. For example, a word processor might have several documents open at once; each of these would need its own text area. GEOS provides a facility for this.

An application can have several GenDisplay objects, each of which must be a child of a GenDisplayGroup object. The user can then switch back and forth between the displays using the GenDisplayControl (which is usually a child of a "Window" menu). The switching is transparent to the application.

If the application uses one display for each document, it should use the Document Control objects to create the displays. The Document Control can automatically duplicate a resource containing a generic object tree headed by a GenDisplay and make that GenDisplay a child of the GenDisplayGroup each time a document is opened or created. For details, see the Documents Objects chapter.

GenDisplayGroup

GenDisplayControl

Using GenDisplayClass Objects

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Using the GenPrimary | Next: 3.1 GenDisplayGroup


GenDisplayGroup

Window Objects: 3.1 Using Multiple Displays: GenDisplayGroup

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Multiple Displays | Next: 3.2 GenDisplayControl

If an application uses GenDisplay objects, it must have a GenDisplayGroup object. This object makes sure there is space in the GenPrimary for the displays.

The GenDisplayGroup must be either a child of the GenPrimary (in which case the specific UI will decide where to put the display area) or a child of a GenInteraction which is a child of the GenPrimary (if the application wants the display area in a specific part of the GenPrimary). The GenDisplayGroup should be run by the UI thread.

GenDisplayGroupClass has no instance data which may be set or examined by the application. However, GenDisplayGroupClass is a subclass of GenClass , and inherits all of its instance data. When you declare a GenDisplayGroup, you may specify its GenClass instance data normally; you may also include any of the hints described in the following sections.

The GenDisplayGroup Instance Data

HINT_DISPLAY_GROUP_SEPARATE_MENUS, HINT_DISPLAY_GROUP_ARRANGE_TILED, HINT_DISPLAY_GROUP_FULL_SIZED_ON_STARTUP, HINT_DISPLAY_GROUP_OVERLAPPING_ON_STARTUP, HINT_DISPLAY_GROUP_FULL_SIZED_IF_TRANSPARENT_DOC_CTRL_MODE, HINT_DISPLAY_GROUP_TILE_HORIZONTALLY, HINT_DISPLAY_GROUP_TILE_VERTICALLY, HINT_DISPLAY_GROUP_SIZE_INDEPENDENTLY_OF_DISPLAYS

GenDisplayGroupClass is a subclass of GenClass. Other than vardata, this class adds no other instance data. There are several hints defined for GenDisplayGroupClass . Most of these hints specify how displays should be arranged on startup.

Code Display 4-3 GenDisplayGroup Instance Data

/* GenDisplayGroupClass adds no instance fields. It does modify the default
 * GI_attrs settings, however. */
    @default GI_attrs = @default | GA_TARGETABLE;
/* This hint allows each GenDisplay to contain its own menu bar. */
    @vardata void			HINT_DISPLAY_GROUP_SEPARATE_MENUS;
/* These hints specify how a GenDisplayGroup will arrange its GenDisplays. */
    @vardata void			HINT_DISPLAY_GROUP_ARRANGE_TILED;
    @vardata void			HINT_DISPLAY_GROUP_FULL_SIZED_ON_STARTUP;
    @vardata void			HINT_DISPLAY_GROUP_OVERLAPPING_ON_STARTUP;
    @vardata void			HINT_DISPLAY_GROUP_FULL_SIZED_IF_TRANSPARENT_DOC_CTRL_MODE;
    @vardata void			HINT_DISPLAY_GROUP_TILE_HORIZONTALLY;
    @vardata void			HINT_DISPLAY_GROUP_TILE_VERTICALLY;
    @vardata void			HINT_DISPLAY_GROUP_SIZE_INDEPENDENTLY_OF_DISPLAYS;
/* These attributes affect the availability of overlapping and
 * full-sized states. */
    @vardata void			ATTR_GEN_DISPLAY_GROUP_NO_FULL_SIZED;
    @vardata void			ATTR_GEN_DISPLAY_GROUP_NO_OVERLAPPING;
    @vardata void			ATTR_GEN_DISPLAY_OVERLAPPING_STATE;

In some specific UIs (such as OSF/Motif), menus which are children of a GenDisplay object may appear in two ways: they may be drawn on the Primary's menu bar (the default in OSF/Motif), or they may appear in a menu bar on the display itself. HINT_DISPLAY_GROUP_SEPARATE_MENUS indicates that each display should be given its own menu bar (if the specific UI permits this).

There are several hints which specify how the displays should be configured when the GenDisplayGroup is built.

HINT_DISPLAY_GROUP_FULL_SIZED_ON_STARTUP indicates that the GenDisplayGroup should be in full-size mode on startup; that is, all of its children should be maximized.

HINT_DISPLAY_GROUP_OVERLAPPING_ON_STARTUP indicates that the GenDisplayGroup should be in overlapping mode on startup; that is, its children should be non-maximized.

HINT_DISPLAY_GROUP_FULL_SIZED_IF_TRANSPARENT_DOC_CTRL_MODE forces a GenDisplayGroup to start full-sized if the application is in "transparent document control" mode, which is set by the user level of the application. This hint overrides HINT_DISPLAY_GROUP_OVERLAPPING_ON_STARTUP , if present.

HINT_DISPLAY_GROUP_ARRANGE_TILED indicates that the GenDisplayGroup should be in overlapping mode on startup, and further that the displays should be tiled; that is, they should be non-maximized and arranged in a non-overlapping way to fill the display area.

You can specify a preference for how the displays should be tiled by setting HINT_DISPLAY_GROUP_TILE_HORIZONTALLY or HINT_DISPLAY_GROUP_TILE_VERTICALLY . HINT_DISPLAY_GROUP_TILE_HORIZONTALLY indicates that you want tiled displays to be arranged horizontally, with each display tall enough to fill the display area. Similarly, HINT_DISPLAY_GROUP_TILE_VERTICALLY indicates that you want tiled displays to be arranged vertically, with each display wide enough to fill the display area. If both hints are set, the result varies depending on the specific UI.

HINT_DISPLAY_GROUP_SIZE_INDEPENDENTLY_OF_DISPLAYS sizes a GenDisplayGroup by what its parent wants rather than what any of its children GenDisplays want. This may improve geometry performance in a complex GenPrimary/GenDisplay combination.

Arranging Displays in the Display Group

MSG_GEN_DISPLAY_GROUP_SET_OVERLAPPING, MSG_GEN_DISPLAY_GROUP_SET_FULL_SIZED, MSG_GEN_DISPLAY_GROUP_GET_FULL_SIZED, MSG_GEN_DISPLAY_GROUP_TILE_DISPLAYS, ATTR_GEN_DISPLAY_GROUP_NO_FULL_SIZED, ATTR_GEN_DISPLAY_GROUP_NO_OVERLAPPING, ATTR_GEN_DISPLAY_GROUP_OVERLAPPING_STATE

The GenDisplayGroup can be in "full-sized" or "overlapping" mode. If the GenDisplayGroup is in "full-size" mode, all of its children are maximized (except any displays which are set "non-maximizable"). If it is not in full-sized mode, it is said to be in "overlapping" mode; that is, none of its children are maximized. When a user maximizes any display which belongs to a GenDisplayGroup, the GenDisplayGroup automatically goes into "full-size" mode and maximizes all of its children.

If you include ATTR_GEN_DISPLAY_GROUP_NO_FULL_SIZED, the GenDisplayControl will not be able to go into full-size mode; it will always be in overlapping mode. Similarly, if set ATTR_GEN_DISPLAY_GROUP_NO_OVERLAPPING , the GenDisplayControl will not be able to go into overlapping mode; it will always be in full-sized mode, and all displays will always be maximized. Naturally, you may not include both of these attributes at once; if you do, results are undefined.

Messages are provided which switch the GenDisplayGroup into one or another of these modes. You might not need to use any of these messages. If you use a GenDisplayControl object, the user will be able to switch from overlapping to full-size and also to tile the displays by using that object. However, you can also send the following messages directly.

You can set a GenDisplayGroup to full-sized mode by sending it the message MSG_GEN_DISPLAY_GROUP_SET_FULL_SIZED. This message causes the GenDisplayGroup to maximize every one of its children. Children which cannot be maximized will be unaffected. The window layering and focus/target settings are not changed.

You can set a GenDisplayGroup to overlapping mode by sending it the message MSG_GEN_DISPLAY_GROUP_SET_OVERLAPPING. This message causes a GenDisplayGroup object to de-maximize all of its children. Children which are not restorable will be unaffected. The window layering and focus/target settings are not changed.

You can find out whether a GenDisplayGroup object is in full-sized mode by sending it MSG_GEN_DISPLAY_GROUP_GET_FULL_SIZED . If the GenDisplayGroup is in full-sized mode, this message will return true .

You can also put a GenDisplayGroup into "tiled" mode. This is a special case of overlapping mode. When a GenDisplayGroup is put in tiled mode, it first puts itself in overlapping mode. It then attempts to arrange and resize its display children so they fill the display area without overlapping. To put a GenDisplayGroup into tiled mode, send it the message MSG_GEN_DISPLAY_GROUP_TILE_DISPLAYS .

The GenDisplayGroup keeps track of its overlapping state across shutdowns. It does this by setting ATTR_GEN_DISPLAY_GROUP_OVERLAPPING_STATE. Applications may not set or change this attribute directly.

MSG_GEN_DISPLAY_GROUP_SET_FULL_SIZED

void	MSG_GEN_DISPLAY_GROUP_SET_FULL_SIZED();

This message instructs a GenDisplayGroup to put itself in "full-sized" mode; that is, all of its maximizable children will be maximized. This message is ignored if the GenDisplayGroup has the vardata attribute ATTR_GEN_DISPLAY_GROUP_NO_FULL_SIZED.

Source: Unrestricted.

Destination: GenDisplayGroup.

Interception: Not generally intercepted.

MSG_GEN_DISPLAY_GROUP_SET_OVERLAPPING

void	MSG_GEN_DISPLAY_GROUP_SET_OVERLAPPING();

This message instructs a GenDisplayGroup to put itself in "overlapping" mode. This message is ignored if the GenDisplayGroup has the vardata attribute ATTR_GEN_DISPLAY_GROUP_NO_OVERLAPPING.

Source: Unrestricted.

Destination: GenDisplayGroup.

Interception: Not generally intercepted.

MSG_GEN_DISPLAY_GROUP_GET_FULL_SIZED

Boolean	MSG_GEN_DISPLAY_GROUP_GET_FULL_SIZED();

This message finds out whether a GenDisplayGroup is in "full-sized" mode.

Source: Unrestricted.

Destination: GenDisplayGroup.

Return: Returns true (i.e. non-zero) if the GenDisplayGroup is in full-sized mode; otherwise, it returns false (i.e. zero).

Interception: Not generally intercepted.

MSG_GEN_DISPLAY_GROUP_TILE_DISPLAYS

void	MSG_GEN_DISPLAY_GROUP_TILE_DISPLAYS;

This message instructs a GenDisplayGroup to put itself in "tiled" mode. That is, it should first put itself in "overlapping" mode; it should then arrange and resize the displays so they fill the display area without overlapping. The message is ignored if the GenDisplayGroup has the vardata attribute ATTR_GEN_DISPLAY_GROUP_NO_OVERLAPPING.

Source: Unrestricted.

Destination: GenDisplayGroup.

Interception: Not generally intercepted.

Selecting a Display

MSG_GEN_DISPLAY_GROUP_SELECT_DISPLAY

Ordinarily, the user switches from one display to another in one of two ways. The user may use the specific UI's way of switching displays (e.g. clicking on the display); or he may use the GenDisplayControl (described below) to switch displays. The application can also force the Display Group to bring a certain display to the top by sending it MSG_GEN_DISPLAY_GROUP_SELECT_DISPLAY . However, this is not usually done; applications should generally let the user switch displays with the GenDisplayControl.

MSG_GEN_DISPLAY_GROUP_SELECT_DISPLAY

void	MSG_GEN_DISPLAY_GROUP_SELECT_DISPLAY(
        word	displayNum);

This message instructs a display group to select a certain display, bringing it to the top and making it the focus. Applications should not ordinarily need to send this.

Source: Usually GenDisplayControl or its associated objects; however, any object can send this.

Destination: GenDisplayGroup.

Parameters: displayNum The display to select. This is an integer specifying the position of the desired display among the GenDisplayGroup's children; that is, its first child is number zero, its next child is number one, and so on.

Interception: This message is not ordinarily intercepted.

Tips: You can find a display's position number by sending MSG_GEN_FIND_CHILD to the GenDisplayGroup, passing the optr of the desired display. See the GenClass chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Using Multiple Displays | Next: 3.2 GenDisplayControl


GenDisplayControl

Window Objects: 3.2 Using Multiple Displays: GenDisplayControl

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 GenDisplayGroup | Next: 3.3 Using GenDisplayClass Objects

The GenDisplayGroup object does half the job of managing display objects: it creates a space for the displays and manages them as its children. However, the display group does very little interaction with the user. Instead, the user works mainly with the GenDisplayControl object.

The GenDisplayControl is usually a child of a "Window" GenInteraction, which is itself a child of the primary. GenDisplayControlClass is a subclass of GenControlClass , and it has all the functionality of that class. For more information, see the GenControlClass chapter.

GenDisplayControlClass is based very closely on GenControlClass . The differences between it and GenControlClass are shown below in Instance Data of GenDisplayControlClass .

Code Display 4-4 Instance Data of GenDisplayControlClass

/* GDCII_attrs is a byte-length flag field. There is currently only one flag,
 * namely GDCA_MAXIMIZED_NAME_ON_PRIMARY; this specifies that if the active
 * display is maximized, its name should be shown as the primary's long-term
 * moniker. */
    @instance 		GenDisplayControlAttributes				GDCII_attrs =
                        (GDCA_MAXIMIZED_NAME_ON_PRIMARY);
@default		GI_states = @default | GS_ENABLED;
@default 		GCI_output = {TO_APP_TARGET};
  typedef WordFlags GDCFeatures;
#define GDCF_OVERLAPPING_MAXIMIZED					0x0004
#define GDCF_TILE					0x0002
#define GDCF_DISPLAY_LIST					0x0001
  typedef WordFlags GDCToolboxFeatures;
#define GDCTF_OVERLAPPING_MAXIMIZED					0x0004
#define GDCTF_TILE					0x0002
#define GDCTF_DISPLAY_LIST					0x0001
#define GDC_DEFAULT_FEATURES 				(GDCF_OVERLAPPING_MAXIMIZED | GDCF_TILE
                | GDCF_DISPLAY_LIST)
#define GDC_DEFAULT_TOOLBOX_FEATURES (GDCF_DISPLAY_LIST)
/* A GenDisplayControl also features a single hint which affects the display of
 * the features list. */
    @vardata void  HINT_DISPLAY_CONTROL_NO_FEATURES_IF_TRANSPARENT_DOC_CTRL_MODE;

The GDCII _attrs field contains a set of GenDisplayControlAttributes . There is currently only one flag defined among these attributes:

GDCA_MAXIMIZED_NAME_ON_PRIMARY
If this attribute is set and the active display is maximized, the name of the selected display will be shown in the long term moniker of the GenPrimary.

HINT_DISPLAY_CONTROL_NO_FEATURES_IF_TRANSPARENT_DOC_CTRL_MODE suppresses display of the features list if the application is running in "transparent document control" mode, as selected by the user level of the application.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 GenDisplayGroup | Next: 3.3 Using GenDisplayClass Objects


Using GenDisplayClass Objects

Window Objects: 3.3 Using Multiple Displays: Using GenDisplayClass Objects

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 GenDisplayControl

All GenDisplay objects must be children of a GenDisplayGroup object. GenDisplay objects can be created in several ways: an application can declare them in its code; it can instantiate them at run-time and make them children of the GenDisplayGroup; or, if the application uses the Document Control objects, it can have the Document Control create a new display automatically whenever a document is opened. (For details about using a Document Control to create GenDisplay objects, see the Documents Objects chapter.)

Closing GenDisplays

MSG_GEN_DISPLAY_CLOSE

Most specific UIs provide a way for the user to close windows. For example, OSF/Motif lets a user close a window by double-clicking the control button. When the user uses the specific UI's close mechanism, the Display or Primary is sent MSG_GEN_DISPLAY_CLOSE.

GenDisplayClass does only one thing when it receives MSG_GEN_DISPLAY_CLOSE: it sends MSG_GEN_DOCUMENT_CLOSE to the document specified by GDI _document . The GenDisplayClass handler for MSG_GEN_DISPLAY_CLOSE does not, in fact, destroy the display. If the display is linked to a GenDocument, the GenDocument will respond to MSG_GEN_DOCUMENT_CLOSE by closing the application and removing the GenDisplay. Otherwise, you will have to remove the GenDisplay yourself by writing a handler for MSG_GEN_DISPLAY_CLOSE.

The GenPrimaryClass handler for MSG_GEN_DISPLAY_CLOSE closes the application. If you want to add to or replace this behavior, you may have your Primary subclass this message.

MSG_GEN_DISPLAY_CLOSE

void	MSG_GEN_DISPLAY_CLOSE();

This message is sent to a Display to close it. The system sends it when the user uses the specific UI's way of closing a window. The GenDisplayClass handler does nothing but send a MSG_GEN_DOCUMENT_CLOSE to the Document object specified in GDI _document . The GenPrimaryClass handler closes the application.

Source: Unrestricted.

Destination: GenDisplay.

Interception: If the Display is not associated with a GenDocument, you will need to subclass this message for it to have any effect at all. If the display is associated with a GenDocument object, you should probably subclass your Document object's MSG_GEN_DOCUMENT_CLOSE instead. Primary objects may subclass this message if they want to alter or replace the default behavior (of closing the application).

Menu Bar PopOuts

ATTR_GEN_DISPLAY_MENU_BAR_POPPED_OUT

Some objects contain the ability to "pop out" of their sub-group locations and become floating dialog boxes.The menu bar of a GenDisplay is one such GenInteraction. If the menu bar of a GenDisplay is currently in the "popped-out" state, it will contain the vardata entry ATTR_GEN_DISPLAY_MENU_BAR_POPPED_OUT.

Messages sent to GenDisplays

MSG_GEN_DISPLAY_UPDATE_FROM_DOCUMENT, MSG_GEN_DISPLAY_GET_DOCUMENT

Many of the messages which can be sent to GenDisplay objects have already been discussed above in A First Look at GenDisplay . However, there are a few messages which are ordinarily sent to Displays but not to Primaries. These messages are discussed here.

GenDisplay objects often work in close conjunction with the Document Control objects. It is common for every open document to have its own GenDisplay object as well as its own GenDocument. The two objects work in conjunction, sending messages back and forth to communicate. You can send or intercept these messages yourself to add functionality.

When the document object changes in certain significant ways, the Display has to be brought into accord with it. For example, if the name of the file changes, the GenDisplay's moniker will have to be changed to reflect this. Whenever a significant change takes place, the Document Control objects send a MSG_GEN_DISPLAY_UPDATE_FROM_DOCUMENT to the appropriate Display. The Display then requests all necessary information from the GenDocument and makes any necessary changes to its own instance data. You can force this updating by sending the message directly to the Display. You can also subclass this message if you want to add special updating behavior; however, you should be sure to pass this message to the superclass' handler.

You can find out which Document object is associated with a given Display by sending MSG_GEN_DISPLAY_GET_DOCUMENT to the Display. The message will return an optr to the corresponding Document object.

MSG_GEN_DISPLAY_UPDATE_FROM_DOCUMENT

void	MSG_GEN_DISPLAY_UPDATE_FROM_DOCUMENT();

This message instructs a GenDisplay to update its instance data from its associated GenDocument object (if any). This message is ordinarily sent by the Document Control objects.

Source: Unrestricted--ordinarily sent only by Document Control objects.

Destination: GenDisplay.

Interception: Normally not intercepted. If you subclass this message to add special updating behavior, be sure to end with an @callsuper.

MSG_GEN_DISPLAY_GET_DOCUMENT

optr	MSG_GEN_DISPLAY_GET_DOCUMENT();

This message returns the optr of the GenDocument associated with a given GenDisplay. This is equal to the value of the GenDisplay's GDI _document field.

Source: Unrestricted--ordinarily sent only by Document Control objects.

Destination: GenDisplay.

Interception: Normally not intercepted.

Traveling Objects

ATTR_GEN_DISPLAY_TRAVELLING_OBJECTS

If you use multiple GenDisplay objects, it is sometimes useful to set up a group of "traveling objects." Traveling objects are children of whichever display is active. When a different GenDisplay is brought to the top, all traveling objects will be set "not usable" and removed from the Generic tree. They will then be added as children of the new top display and set "usable." (Any children of the traveling objects will naturally move with them.) Traveling objects are most commonly Toolbox Interactions, but they can be any kind of generic object.

Traveling objects can only be used under certain circumstances. Every display must belong to its own object block, and all of these object blocks must be copies of the same original. This is because the traveling objects are added as children to a specified chunk in whichever object block contains the new top display. If you want to use traveling objects, you should declare a special "template" object block which contains a GenDisplay and its children. Whenever you need to create a GenDisplay, you should duplicate this object block. The traveling objects should be in another resource altogether. If you use the Document Control objects to create displays, the objects will use this technique, duplicating an object block for each new display; this will let you use traveling objects.

Every traveling object is indicated by a TravelingObjectReference structure (see TravelingObjectReference ). To attach traveling objects to the active display, create a chunk which contains a list of TravelingObjectReference structures; this chunk must be in the same object block as the active display. Then set the Display's ATTR_GEN_DISPLAY_TRAVELING_OBJECTS to the ChunkHandle of the list. The list will automatically be moved to the block of the active display whenever the traveling objects are moved.

Code Display 4-5 TravelingObjectReference

typedef struct {
    optr		TIR_travelingObject;			/* optr to traveling object whose
                     * reference this is */
    ChunkHandle		TIR_parent;			/* Chunk Handle of object in Display's
                     * block that will be the parent of this
                     * object */
    word		TIR_compChildFlags;			/* CompChildFlags to use when
                     * adding the traveling object */
} TravelingObjectReference;

The TravelingObjectReference structure has the following three fields:

TIR _travelingObject
This field is an optr to the traveling object whose reference this is.
TIR _parent
This field holds the chunk handle of an object in the display block. When the traveling object is added to a display block, it will be made a child of the object whose chunk handle this is.
TIR _compChildFlags
This is the set of CompChildFlags which will be used when attaching this object to its new parent.
| Up: GEOS SDK TechDocs | Up | Prev: 3.2 GenDisplayControl


The GenProcess Object

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Starting and Stopping
| One-page Version of this Chapter for Easy Printing

GenProcessClass is the class that you will use to define the Process object of your applications. This class includes some functionality for opening and closing geodes as well as saving to and restoring from state files. Typically, your application will define its own subclass of GenProcessClass ; this subclass will be used as your Process object and will receive all messages destined for the Process.

In this subclass you can alter the GenProcessClass , ProcessClass , or MetaClass messages you need. More often, however, the subclass will be used to define new messages that are application-global or that should be handled by your Process object.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Starting and Stopping
| One-page Version of this Chapter for Easy Printing


Starting and Stopping

The GenProcess Object: 1 Starting and Stopping

| Up: GEOS SDK TechDocs | Up | Prev: The GenProcess Object | Next: 2 Undo Mechanism

For information about the steps involved in stopping, starting, or restoring an application (and to get context information about the messages described below), see the GEOS Programming chapter.

Many of the following messages need AppAttachFlags to tell them how the process is attaching:

typedef WordFlags AppAttachFlags;
#define AAF_RESTORING_FROM_STATE						0x8000
#define AAF_STATE_FILE_PASSED						0x4000
#define AAF_DATA_FILE_PASSED						0x2000
#define AAF_RESTORING_FROM_QUIT						0x1000
  • AAF_RESTORING_FROM_STATE indicates that the application is coming up from a previous state, either by re-launching using a state file, or re-entering application mode from engine mode in the same session. The UI trees will be in whatever state they were left in, which may be different than the statically declared UI tree, depending on what occurred in the application when the state file was written out (for example, a dialog box may have been on-screen). If this flag is false , the application is starting up fresh. If this flag is true , the UI does not call MSG_META_LOAD_OPTIONS and ignores any "ON_STARTUP" hints or attributes.
  • AAF_STATE_FILE_PASSED indicates that the application is restoring from the state file passed in an AppLaunchBlock , presumably passed as an argument in the message also. If this flag is set, AAF_RESTORING_FROM_STATE will also be set.
  • AAF_DATA_FILE_PASSED indicates that the passed AppLaunchBlock contains the name of a data file that should be opened.
  • AAF_RESTORING_FROM_QUIT indicates that the application was in the process of quitting, reached engine mode, and is now being started back up into application mode. If set, will also be set, and we will be brought up in whatever state we originally entered engine mode.
MSG_GEN_PROCESS_RESTORE_FROM_STATE
void 	MSG_GEN_PROCESS_RESTORE_FROM_STATE(
        AppAttachFlags 		attachFlags,
        MemHandle 		launchBlock,
        MemHandle 		extraState);

This message is sent by the User Interface when an application is being loaded from a state file. This is sent to the process itself from MSG_META_ATTACH , whenever the application is being invoked as in MSG_GEN_PROCESS_RESTORE_FROM_STATE mode. Data passed is the same as that in MSG_META_ATTACH . The default handler fetches the application mode message, either MSG_GEN_PROCESS_OPEN_APPLICATION or MSG_GEN_PROCESS_OPEN_ENGINE , as saved in the application object, and sends that message to the process.

Note that the blocks passed need not be freed, as this is done by the caller upon return.

Source: Default GenProcessClass handler for MSG_META_ATTACH.

Destination: Self.

Interception: Intercepted generally only so that application can retrieve previously saved data out of the state block passed.

Parameters: attachFlags Flags with information about the state and data files.

launchBlock
Handle of AppLaunchBlock , or zero if none. This block contains the name of any document file passed into the application on invocation.
extraState
Handle of extra state block, or zero if none. This is the same block as returned from MSG_GEN_PROCESS_CLOSE_APPLICATION or MSG_GEN_PROCESS_CLOSE_ENGINE , in some previous MSG_META_DETACH . Process objects often use this extra block to save global variables to state files.

Return: Nothing.

MSG_GEN_PROCESS_OPEN_APPLICATION
void	MSG_GEN_PROCESS_OPEN_APPLICATION(
        AppAttachFlags		attachFlags,
        MemHandle		launchBlock,
        MemHandle 		extraState);

This message is sent by the User Interface when an application is being loaded from its resource blocks. Applications will often intercept this message to set up certain things before being put on-screen. This is the handler in which, for example, you would register for certain notifications such as the quick-transfer notification.

This is sent to the process itself from MSG_META_ATTACH , whenever the application is being restored to application mode (as opposed to engine mode), or whenever it is being invoked as in application mode. Data passed is the same as that in MSG_META_ATTACH . The default handler sets the GenApplication object GS_USABLE, and brings the UI up on screen.

This message may be intercepted to open up any data file passed, before the UI for the application is actually set GS_USABLE. Note that the blocks passed need not be freed, as this is done by the caller upon return.

If you intercept this message and need to send MSG_META_QUIT from within the handler, make sure to either send the quit after calling the superclass, or send the quit via MSG_META_FLUSH_INPUT_QUEUE. (If this is not done, the system will think that an IACP connection is open, aborting the quit.)

Source: GenProcessClass default handler for MSG_META_ATTACH only.

Destination: Same object.

Interception: Frequently intercepted by an application's own process class to find out when an application is first coming alive in the system. You must pass this message on to the superclass, or the application will never come up. Be aware that the entire UI tree for the application is the equivalent of not usable (~GS_USABLE) before the superclass is called, and is usable and up on screen visually after it is called. Thus, it is best to do non-UI related things, and changing of generic attributes and hints before calling the superclass. You must wait until after calling the superclass to perform any operations which require that objects be fully usable (e.g. bringing up a dialog box). Make sure that if you send MSG_META_QUIT from within your handler that you follow the guidelines mentioned above.

Parameters: attachFlags State information about the state and data files.

launchBlock
Handle of AppLaunchBlock , or zero if none. This block contains the name of any document file passed into the application on invocation.
extraState
Handle of extra state block, or zero if none. This is the same block as returned from MSG_GEN_PROCESS_CLOSE_APPLICATION or MSG_GEN_PROCESS_CLOSE_ENGINE , in some previous MSG_META_DETACH .
Is freed by caller--subclasses should not free the extra state block.

Return: Nothing.

MSG_GEN_PROCESS_CLOSE_APPLICATION
MemHandle MSG_GEN_PROCESS_CLOSE_APPLICATION();

This message is sent by the User Interface whenever the application is being shut down (during a detach) and it had been launched in application (as opposed to engine) mode.

Source: GenProcessClass default handler for MSG_META_DETACH only

Destination: Self

Interception: Convenient place for code that needs to be executed before application exits, for non-engine mode cases. Superclass must be called.

Parameters: None.

Return: Handle of block to save (or NULL for none). Process objects often save global variables to a state file in an extra block. This is the handle of that block.

MSG_GEN_PROCESS_OPEN_ENGINE
void 	MSG_GEN_PROCESS_OPEN_ENGINE(
        AppAttachFlags 		attachFlags,
        MemHandle 		launchBlock);

This is sent to the process itself from MSG_META_ATTACH , whenever the application is being restored to engine mode, or whenever it is being invoked as in engine mode. Data passed is the same as that in MSG_META_ATTACH . There is no default handler.

This message may be intercepted to open up any data file passed, before any engine commands are delivered to the process. Note that the blocks passed need not be freed, as this is done by the caller upon return.

Source: GenProcessClass default handler for MSG_META_ATTACH only.

Destination: Self.

Interception: Generally unnecessary, though can be intercepted if notification of going into this mode is necessary.

Parameters: attachFlags State of state and data files.

launchBlock
Handle of AppLaunchBlock , or NULL if none. This block contains the name of any document file passed into the application on invocation.

Return: Nothing.

MSG_GEN_PROCESS_CLOSE_ENGINE
void 	MSG_GEN_PROCESS_CLOSE_ENGINE();

This message is sent by the User Interface whenever the application is being shut down (during a detach) and it had been launched in "engine" mode.

Source: GenProcessClass default handler for MSG_META_DETACH only.

Destination: Self.

Interception: Convenient place for code that needs to be executed before the application exits, for engine mode cases. Superclass must be called.

MSG_GEN_PROCESS_CLOSE_CUSTOM
MemHandle MSG_GEN_PROCESS_CLOSE_CUSTOM();

This message is sent by the User Interface whenever the application is being shut down (during a detach) and it had been launched in some custom mode (not application or engine) that GenProcessClass doesn't know about.

Source: Subclass of GenProcessClass .

Destination: Self.

Interception: Convenient place for code that needs to be executed before the application exits, for custom mode cases. Superclass must be called.

Parameters: None.

Return: Handle of block to save (or NULL for none).

MSG_GEN_PROCESS_ATTACH_TO_PASSED_STATE_FILE
MemHandle 	MSG_GEN_PROCESS_ATTACH_TO_STATE_FILE(
        AppAttachFlags		attachFlags,
        MemHandle		launchBlock);

This message is sent by the User Interface whenever the application is being attached to a state file. This message is sent when either restoring from state or detaching. May be subclassed to provide forced state behavior.

Source: GenProcessClass default handler for MSG_META_ATTACH .

Destination: Self.

Interception: May be intercepted to force use of a particular state file (by changing the name of the state file to use before calling superclass).

Parameters: attachFlags AppAttachMode (matches that in AppLaunchBlock ).

launchBlock
Block of structure AppLaunchBlock .

Return: Handle of extra block of state data (zero for none).

MSG_GEN_PROCESS_CREATE_NEW_STATE_FILE
word MSG_GEN_PROCESS_CREATE_NEW_STATE_FILE(
         MemHandle appInstanceReference);

This replaces MSG_GEN_PROCESS_ATTACHED_TO_PASSED_STATE_FILE if no state file had been specified in that message. This message's default handler will create a new state file and attach it normally. Can be subclassed to provide forced state file usage (e.g. use a different naming scheme).

Source: GenProcessClass default handler for MSG_META_DETACH, if not quitting.

Destination: Self.

Interception: May be intercepted to change name of state file to create.

Parameters: appInstanceReference
Block handle to block of structure AppInstanceReference .

Return: VM file handle (NULL if you want no state file).

MSG_GEN_PROCESS_DETACH_FROM_STATE_FILE
void 	MSG_GEN_PROCESS_DETACH_FROM_STATE_FILE(
        MemHandle 		extraState,
        word 		appStates);

This message is sent by the User Interface when the application is detaching or quitting (may or may not be attached to a state file) and the detach is nearly complete.

Source: GenProcessClass default handler for MSG_META_DETACH , if not quitting.

Destination: Self.

Interception: Not generally done. Default behavior is what you want.

Parameters: extraState Block handle of extra block to be saved (as returned from MSG_GEN_PROCESS_CLOSE_APPLICATION , MSG_GEN_PROCESS_CLOSE_ENGINE or MSG_GEN_PROCESS_CLOSE_CUSTOM ). If the block is not transferred to the state file), it must be freed (if non-zero) by the handler for this message.

appStates
ApplicationStates record with information about the application state.

Return: Nothing.

MSG_GEN_PROCESS_INSTALL_TOKEN
void 	MSG_GEN_PROCESS_INSTALL_TOKEN();

This message is sent by the desktop to a process to get that process to install its token and moniker lists into the token database.

Source: Generally whatever geode launched this application in engine mode (e.g. GeoManager).

Destination: GenProcessClass object of any geode launched in engine mode.

Interception: May be intercepted to install additional tokens. Default behavior installs only application icon.

Parameters: None.

Return: Nothing.

MSG_GEN_PROCESS_GET_PARENT_FIELD
optr 	MSG_GEN_PROCESS_GET_PARENT_FIELD();

This message is sent by process-libraries (such as the Spool Object Library) to find out which field object is its parent. This message will return the field of the first client of the library.

Source: Unrestricted.

Destination: GenProcessClass object.

Interception: Not necessary, as default behavior implements utility.

Parameters: Nothing.

Return: Parent field.

MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST
void	MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST(@stack
        word		sendFlags,
        EventHandle 		event,
        MemHandle 		block,
        word		manufListType,
        ManufacturerID 		manufID);

This message sends the specified event to all the objects registered with the passed GCN list. This message should be subclassed by UI Controller objects.

The handler here merely sends the request on to GenApplication using MSG_META_GCN_LIST_SEND . Controllers should use this message, however, over direct communication with the application object, to ensure orderly updating of the list status event. One such failure case which is fixed is two target text objects, one run by the process thread in a view, the other a GenText run by the UI thread. If the GenText has the target and the user clicks quickly on view then GenText, the GenText may process both messages about the target being lost and gained before the process text object receives its gained and lost pair. If both objects sent MSG_META_GCN_LIST_SEND directly to the GenApplication object, the GenText's status would be wiped out by the subsequent reporting by the process text object. This problem is avoided by having both process and UI objects call here to pass status update info. This works because target changes start out ordered in the UI thread, and that order is passed on to the process thread in either of the two cases.

Source: Any object wishing to update an application GCN list. Don't use queue-order altering message flags such as MF_PLACE_IN_FRONT when sending this message. As a convention must be established for the flag MF_FORCE_QUEUE in order to ensure orderly results, the convention in use is this: Don't pass it.
A typical call should use only the MF_STACK, MF_FIXUP_DS, and/or MF_FIXUP_ES flags, if needed.

Destination: GenProcessClass (application process) only.

Interception: Should not generally be intercepted, as GenProcessClass provides correct behavior. If intercepted and not sent onto superclass, event passed must be destroyed and data block reference count decremented, to avoid leaving obsolete data on the heap.

Parameters: sendFlags Flags to pass on to GCNListSend() .

event
Classed event to pass on to the list.
block
Handle of extra data block, if used (otherwise NULL). Blocks of this type must have a reference count, which may be initialized with MemInitRefCount() and be incremented for any new usage with MemIncRefCount() . Methods in which they are passed are considered such a new usage, and must have MetaClass handlers which call MemDerefCount() . Current messages supported are MSG_META_NOTIFY_WITH_DATA_BLOCK and MSG_NOTIFY_FILE_CHANGE .
manufListType
This may be a GCNStandardListType or any other word which acts as a GCN list ID.
manufID
Manufacturer ID, which helps identify the GCN list.
| Up: GEOS SDK TechDocs | Up | Prev: The GenProcess Object | Next: 2 Undo Mechanism


Undo Mechanism

The GenProcess Object: 2 Undo Mechanism

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Starting and Stopping | Next: 2.1 Undo Mechanism

The Undo mechanism is implemented at the GenProcess level; this allows the undo mechanism to be applicable to any application or process. In general, Undo allows a process, usually within an application, but possibly within a library, to reverse changes made in the state of other objects. GEOS allows an almost unlimited number of stored and reversible Undo actions; the practical limit is somewhere around 100 actions.

Undo actions are stored within undo chains . These chains allow queued actions to be undone in reverse order. Each element in an undo chain is made up of an UndoActionStruct . These structures are usually added with an AddUndoActionStruct . This structure has several elements:

typedef struct {
    UndoActionStruct				AUAS_data;
    optr				AUAS_output;
    AddUndoActionFlags				AUAS_flags;
} AddUndoActionStruct

A chain of undo actions is stored for each object. If you want your object to recognize undo-able actions, you must add the undo actions yourself and intercept MSG_META_UNDO when those actions are played back. The object should be able to understand the data within the UndoActionStruct to perform the Undo action.

There are two AddUndoActionFlags which affect when and how undo notification occurs. If AUAF_NOTIFY_BEFORE_FREEING or AUAF_NOTIFY_IF_FREED_WITHOUT_BEING_PLAYED_BACK is set in the AddUndoActionFlags , it not only receives MSG_META_UNDO but also receives MSG_META_UNDO_FREEING_ACTION when the undo mechanism frees the action. You can check the flags in the AddUndoActionStruct passed with this message to decide what action to take.

The object wishing to register an action for undo sends the process MSG_GEN_PROCESS_UNDO_START_CHAIN. For each action in this undo chain (there may be multiple actions in a single chain) send MSG_GEN_PROCESS_UNDO_ADD_ACTION; pass this message the AddUndoActionStruct action of the action to add. Finally, send MSG_GEN_PROCESS_UNDO_END_CHAIN to mark the end of this undo chain. Undo chains may be nested within each other.

The messages following this section also describe supplemental behavior that you may find useful. In addition to these messages, GenProcessClass also provides the following routines:

GenProcessUndoGetFile() returns a file handle of a Huge Array or DB item to hold undo information. Use this routine to get a file to put such undo information into.

GenProcessUndoCheckIfIgnoring() allows an application or library to check whether an application is ignoring undo information; in this case, it can avoid creating unnecessary undo information.

MSG_GEN_PROCESS_UNDO_START_CHAIN
void 	MSG_GEN_PROCESS_UNDO_START_CHAIN(@stack
        optr 	title,
        optr 	owner);

This message notifies the process of the start of an undo-able action. Note that all this message does is increment a count--a new undo chain is created when the count goes from zero to one. This allows a function to perform a number of undo-able actions and have them all grouped as a single undo-able action.

Source: Any object wanting to register an action for undo.

Destination: GenProcessClass only.

Interception: In general, should not be intercepted.

Parameters: title The null-terminated title of this action. If NULL, then the title of the undo action will be the title passed with the next UNDO_START_CHAIN.

owner
The object which owns this action.

Return: Nothing.

MSG_GEN_PROCESS_UNDO_END_CHAIN
void 	MSG_GEN_PROCESS_UNDO_END_CHAIN(
        BooleanWord 		flushChainIfEmpty);

This message notifies the process of the end of an undo-able action. Note that all this message does is decrement a count--the current undo chain is terminated when the count goes from one to zero. This allows a function to perform a number of undo-able actions and have them all grouped as a single undo-able action.

Source: Any object wanting to register an action for undo.

Destination: GenProcessClass only.

Interception: In general, should not be intercepted.

Parameters: flushChainIfEmpty
Non-zero if you want to delete the chain if it has no actions; zero if the chain is OK without actions (actions will be added later).

Return: Nothing.

MSG_GEN_PROCESS_UNDO_ABORT_CHAIN
void	MSG_GEN_PROCESS_UNDO_ABORT_CHAIN();

This message aborts the current undo chain, destroying all actions in place on the current chain, and instructs the undo mechanism to ignore any undo information until the current undo chain is ended. This latter behavior is needed because the current chain may be nested within several chains, so we must ignore undo chain actions until the outermost chain is ended.

Source: Unrestricted.

Destination: GenProcess object.

Interception: Do not intercept.

MSG_GEN_PROCESS_UNDO_ADD_ACTION
VMChain 	MSG_GEN_PROCESS_UNDO_ADD_ACTION(
        AddUndoActionStruct 	*data);

This message adds a new undo action to the current undo chain.

Source: Any object wanting to register an action for undo.

Destination: GenProcessClass .

Interception: In general, should not be intercepted.

Parameters: data Structure containing information which may be used to undo action.

Return: Will return NULL if we are ignoring undo messages.
If the value passed in UAS_datatype was UADT_PTR or UADT_VMCHAIN, then will return a VMChain or DBItem which may be used to undo the action. If neither of the above cases is true, return value is meaningless.

MSG_GEN_PROCESS_UNDO_GET_FILE
VMFileHandle MSG_GEN_PROCESS_UNDO_GET_FILE();

This message returns a VM file handle to store undo actions. This message is useful to access undo data in either a huge array or DB item. You may also use GenProcessUndoGetFile() to retrieve this file instead.

Source: Any object wanting to access the undo file.

Interception: Should not be intercepted.

Parameters: None.

Return: File handle of VM file with undo information.

MSG_GEN_PROCESS_UNDO_FLUSH_ACTIONS
void 	MSG_GEN_PROCESS_UNDO_FLUSH_ACTIONS();

This message flushes the current undo chain (frees all undo actions, notifies edit control that there is no undo item).

Source: Any object using undo.

Interception: Should not be intercepted.

Parameters: None.

Return: Nothing.

MSG_GEN_PROCESS_UNDO_SET_CONTEXT
dword 	MSG_GEN_PROCESS_UNDO_SET_CONTEXT(
        dword 	context);

This message sets the current undo context. This allows the application to have separate undo chains associated with various documents or modes. This should be sent out before any other undo related messages. The document control automatically takes care of this when a document gets the model exclusive.

Passing NULL_UNDO_CONTEXT as the new context will trigger some zealous EC code if any other undo messages are sent while the context is null.

Source: Any object using undo.

Interception: Generally, should not be intercepted. Applications wanting to override the default behavior should at least flush out the current undo actions, as they will probably not be valid in the new context.

Parameters: context New context (this has no meaning to the undo mechanism--it's just a value).

Return: Old context.

Structures:

#define NULL_UNDO_CONTEXT 0
MSG_GEN_PROCESS_UNDO_GET_CONTEXT
dword 	MSG_GEN_PROCESS_UNDO_GET_CONTEXT();

This message gets the current undo context.

Source: Any object using undo.

Interception: Generally, should not be intercepted.

Parameters: None.

Return: Current context.

MSG_GEN_PROCESS_UNDO_PLAYBACK_CHAIN
void 	MSG_GEN_PROCESS_UNDO_PLAYBACK_CHAIN();

This message plays back the current undo chain, one action at a time. It simultaneously creates a "redo" chain, so the undone action can be redone.

Source: Edit control.

Interception: Generally, should not be intercepted.

Parameters: None.

Return: Nothing.

MSG_GEN_PROCESS_UNDO_IGNORE_ACTIONS
void 	MSG_GEN_PROCESS_UNDO_IGNORE_ACTIONS(
        Boolean		flushActions);

This message causes a process to reject any undo messages.

Source: Edit control.

Parameters: flushActions true to flush the queue.

Interception: Generally, should not be intercepted.

MSG_GEN_PROCESS_UNDO_ACCEPT_ACTIONS
void 	MSG_GEN_PROCESS_UNDO_ACCEPT_ACTIONS();

This message causes a process to accept undo messages again.

Source: Edit control.

Interception: Generally, should not be intercepted.

MSG_GEN_PROCESS_UNDO_CHECK_IF_IGNORING
Boolean 	MSG_GEN_PROCESS_UNDO_CHECK_IF_IGNORING();

This message checks to see if the system is currently ignoring undo actions.

Source: Edit control.

Interception: Generally, should not be intercepted.

Parameters: None.

Return: Will return true if ignoring actions.

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Starting and Stopping | Next: 2.1 Undo Mechanism


Undo Mechanism

The GenProcess Object: 2.1 Undo Mechanism: Undo Mechanism

| Up: GEOS SDK TechDocs | Up | Prev: 2 Undo Mechanism

The Undo mechanism is implemented at the GenProcess level; this allows the undo mechanism to be applicable to any application or process. In general, Undo allows a process, usually within an application, but possibly within a library, to reverse changes made in the state of other objects. GEOS allows an almost unlimited number of stored and reversible Undo actions; the practical limit is somewhere around 100 actions.

Undo actions are stored within undo chains . These chains allow queued actions to be undone in reverse order. Each element in an undo chain is made up of an UndoActionStruct . These structures are usually added with an AddUndoActionStruct . This structure has several elements:

typedef struct {
    UndoActionStruct				AUAS_data;
    optr				AUAS_output;
    AddUndoActionFlags				AUAS_flags;
} AddUndoActionStruct

A chain of undo actions is stored for each object. If you want your object to recognize undo-able actions, you must add the undo actions yourself and intercept MSG_META_UNDO when those actions are played back. The object should be able to understand the data within the UndoActionStruct to perform the Undo action.

There are two AddUndoActionFlags which affect when and how undo notification occurs. If AUAF_NOTIFY_BEFORE_FREEING or AUAF_NOTIFY_IF_FREED_WITHOUT_BEING_PLAYED_BACK is set in the AddUndoActionFlags , it not only receives MSG_META_UNDO but also receives MSG_META_UNDO_FREEING_ACTION when the undo mechanism frees the action. You can check the flags in the AddUndoActionStruct passed with this message to decide what action to take.

The object wishing to register an action for undo sends the process MSG_GEN_PROCESS_UNDO_START_CHAIN. For each action in this undo chain (there may be multiple actions in a single chain) send MSG_GEN_PROCESS_UNDO_ADD_ACTION; pass this message the AddUndoActionStruct action of the action to add. Finally, send MSG_GEN_PROCESS_UNDO_END_CHAIN to mark the end of this undo chain. Undo chains may be nested within each other.

The messages following this section also describe supplemental behavior that you may find useful. In addition to these messages, GenProcessClass also provides the following routines:

GenProcessUndoGetFile() returns a file handle of a Huge Array or DB item to hold undo information. Use this routine to get a file to put such undo information into.

GenProcessUndoCheckIfIgnoring() allows an application or library to check whether an application is ignoring undo information; in this case, it can avoid creating unnecessary undo information.

MSG_GEN_PROCESS_UNDO_START_CHAIN

void 	MSG_GEN_PROCESS_UNDO_START_CHAIN(@stack
        optr 	title,
        optr 	owner);

This message notifies the process of the start of an undo-able action. Note that all this message does is increment a count--a new undo chain is created when the count goes from zero to one. This allows a function to perform a number of undo-able actions and have them all grouped as a single undo-able action.

Source: Any object wanting to register an action for undo.

Destination: GenProcessClass only.

Interception: In general, should not be intercepted.

Parameters: title The null-terminated title of this action. If NULL, then the title of the undo action will be the title passed with the next UNDO_START_CHAIN.

owner
The object which owns this action.

Return: Nothing.

MSG_GEN_PROCESS_UNDO_END_CHAIN

void 	MSG_GEN_PROCESS_UNDO_END_CHAIN(
        BooleanWord 		flushChainIfEmpty);

This message notifies the process of the end of an undo-able action. Note that all this message does is decrement a count--the current undo chain is terminated when the count goes from one to zero. This allows a function to perform a number of undo-able actions and have them all grouped as a single undo-able action.

Source: Any object wanting to register an action for undo.

Destination: GenProcessClass only.

Interception: In general, should not be intercepted.

Parameters: flushChainIfEmpty
Non-zero if you want to delete the chain if it has no actions; zero if the chain is OK without actions (actions will be added later).

Return: Nothing.

MSG_GEN_PROCESS_UNDO_ABORT_CHAIN

void	MSG_GEN_PROCESS_UNDO_ABORT_CHAIN();

This message aborts the current undo chain, destroying all actions in place on the current chain, and instructs the undo mechanism to ignore any undo information until the current undo chain is ended. This latter behavior is needed because the current chain may be nested within several chains, so we must ignore undo chain actions until the outermost chain is ended.

Source: Unrestricted.

Destination: GenProcess object.

Interception: Do not intercept.

MSG_GEN_PROCESS_UNDO_ADD_ACTION

VMChain 	MSG_GEN_PROCESS_UNDO_ADD_ACTION(
        AddUndoActionStruct 	*data);

This message adds a new undo action to the current undo chain.

Source: Any object wanting to register an action for undo.

Destination: GenProcessClass .

Interception: In general, should not be intercepted.

Parameters: data Structure containing information which may be used to undo action.

Return: Will return NULL if we are ignoring undo messages.
If the value passed in UAS_datatype was UADT_PTR or UADT_VMCHAIN, then will return a VMChain or DBItem which may be used to undo the action. If neither of the above cases is true, return value is meaningless.

MSG_GEN_PROCESS_UNDO_GET_FILE

VMFileHandle MSG_GEN_PROCESS_UNDO_GET_FILE();

This message returns a VM file handle to store undo actions. This message is useful to access undo data in either a huge array or DB item. You may also use GenProcessUndoGetFile() to retrieve this file instead.

Source: Any object wanting to access the undo file.

Interception: Should not be intercepted.

Parameters: None.

Return: File handle of VM file with undo information.

MSG_GEN_PROCESS_UNDO_FLUSH_ACTIONS

void 	MSG_GEN_PROCESS_UNDO_FLUSH_ACTIONS();

This message flushes the current undo chain (frees all undo actions, notifies edit control that there is no undo item).

Source: Any object using undo.

Interception: Should not be intercepted.

Parameters: None.

Return: Nothing.

MSG_GEN_PROCESS_UNDO_SET_CONTEXT

dword 	MSG_GEN_PROCESS_UNDO_SET_CONTEXT(
        dword 	context);

This message sets the current undo context. This allows the application to have separate undo chains associated with various documents or modes. This should be sent out before any other undo related messages. The document control automatically takes care of this when a document gets the model exclusive.

Passing NULL_UNDO_CONTEXT as the new context will trigger some zealous EC code if any other undo messages are sent while the context is null.

Source: Any object using undo.

Interception: Generally, should not be intercepted. Applications wanting to override the default behavior should at least flush out the current undo actions, as they will probably not be valid in the new context.

Parameters: context New context (this has no meaning to the undo mechanism--it's just a value).

Return: Old context.

Structures:

#define NULL_UNDO_CONTEXT 0

MSG_GEN_PROCESS_UNDO_GET_CONTEXT

dword 	MSG_GEN_PROCESS_UNDO_GET_CONTEXT();

This message gets the current undo context.

Source: Any object using undo.

Interception: Generally, should not be intercepted.

Parameters: None.

Return: Current context.

MSG_GEN_PROCESS_UNDO_PLAYBACK_CHAIN

void 	MSG_GEN_PROCESS_UNDO_PLAYBACK_CHAIN();

This message plays back the current undo chain, one action at a time. It simultaneously creates a "redo" chain, so the undone action can be redone.

Source: Edit control.

Interception: Generally, should not be intercepted.

Parameters: None.

Return: Nothing.

MSG_GEN_PROCESS_UNDO_IGNORE_ACTIONS

void 	MSG_GEN_PROCESS_UNDO_IGNORE_ACTIONS(
        Boolean		flushActions);

This message causes a process to reject any undo messages.

Source: Edit control.

Parameters: flushActions true to flush the queue.

Interception: Generally, should not be intercepted.

MSG_GEN_PROCESS_UNDO_ACCEPT_ACTIONS

void 	MSG_GEN_PROCESS_UNDO_ACCEPT_ACTIONS();

This message causes a process to accept undo messages again.

Source: Edit control.

Interception: Generally, should not be intercepted.

MSG_GEN_PROCESS_UNDO_CHECK_IF_IGNORING

Boolean 	MSG_GEN_PROCESS_UNDO_CHECK_IF_IGNORING();

This message checks to see if the system is currently ignoring undo actions.

Source: Edit control.

Interception: Generally, should not be intercepted.

Parameters: None.

Return: Will return true if ignoring actions.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Undo Mechanism


Input

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Input Flow
| One-page Version of this Chapter for Easy Printing

Nearly all applications will in some way handle user input. User input can come from a keyboard, a mouse, or a stylus. (Other types of input are generally considered data input.) For the most part, GEOS hides the actual mechanics of input from the application; at the same time, it allows extreme flexibility of input handling.

Input to generic UI objects is handled by these objects--unless you have very specific needs and will subclass a generic gadget, you can leave its input handling up to GEOS. The same is true with most specialized system-provided objects such as the GrObj, the VisText, the Spreadsheet, and others. Other objects (such as basic visible objects you subclass yourself) will need more attention depending on the behavior you want them to have.

Read this chapter if your application will handle user input. Before reading this chapter, however, you should be relatively familiar with the GEOS system architecture, Goc syntax, and the GEOS object model and message passing. It is also suggested that you spend some time programming with generic and visible objects and getting used to the rest of the system before handling input events.

1 Input Flow
1.1 Devices and Drivers
1.2 Input Manager and GenSystem
1.3 Input Events
1.4 Input Hierarchies
2 Mouse Input
2.1 Mouse Events
2.2 Gaining the Mouse Grab
2.3 Large Mouse Events
2.4 Setting the Pointer Image
3 Keyboard Input
3.1 Keyboard Input Flow
3.2 Keyboard Events
4 Pen Input and Ink
4.1 Ink Data Structures
4.2 Ink Input Flow
5 Input Hierarchies
5.1 The Three Hierarchies
5.2 Common Hierarchy Basics
5.3 Using Focus
5.4 Using Target
5.5 Using Model
5.6 Extending the Hierarchies

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Input Flow
| One-page Version of this Chapter for Easy Printing


Input Flow

Input: 1 Input Flow

| Up: GEOS SDK TechDocs | Up | Down | Prev: Input | Next: 2 Mouse Input

User input must flow in an orderly way from the user's device to the proper GEOS object. In a multithreaded environment, just determining the appropriate input recipient can be a monumental task.

GEOS uses several layers of indirection to process user input and to distribute it to its proper destination so applications don't have to. The scheme used not only isolates applications from dealing with devices directly, but it provides convenient and easy ways to control input flow, both within an application and throughout the entire system.

The figure below shows the flow of input from the user's device to its destination. User input is managed primarily by device drivers acting in concert with the UI, and several threads are involved in the input flow. For example, the kernel provides a thread solely for input management that has a high priority; this insures that the pointer image always moves immediately with the mouse.

Input is registered by a device such as a keyboard, stylus, or mouse. The device sends signals to GEOS device drivers, which take the signals and translate them into raw input messages the UI understands. These raw messages are translated by the UI's Input Manager, which refines the events even further. The Input Manager then sends the events on to the topmost UI object in the system, the GenSystem object.

The GenSystem examines the events and their contexts and determines which window or application should receive them. Typically, events you will be interested in will flow from the GenSystem through your GenApplication object, its GenPrimary window, and finally through the appropriate GenView or GenDisplay. These window objects will pass the event on to a visible tree (through a VisContent), which will handle the events.

It is highly unlikely you will ever need to know anything about an input event before it gets to your GenApplication object. Typically, you will only handle mouse, pen, or Keyboard events in custom visible objects and can ignore them in all other cases.

Input events take the form of standard GEOS messages. These messages are defined by the UI but are actually part of MetaClass (they are allocated in MetaClass and exported to the UI). The reference entries for these events can therefore be found under MetaClass

Input events give information about the type of event (e.g., a mouse event), the action taking place (e.g., the right mouse button was pressed), and additional information (e.g., the location of the mouse at the time). Each device has its own set of events--for example, keyboard presses induce MSG_META_KBD_CHAR , and mouse moves induce MSG_META_MOUSE_PTR .

Devices and Drivers

Input Manager and GenSystem

Input Events

Input Hierarchies

| Up: GEOS SDK TechDocs | Up | Down | Prev: Input | Next: 2 Mouse Input


Devices and Drivers

Input: 1.1 Input Flow: Devices and Drivers

| Up: GEOS SDK TechDocs | Up | Prev: 1 Input Flow | Next: 1.2 Input Manager and GenSystem

GEOS supports a number of input devices through the use of device drivers. The basic GEOS system software supports mouse, keyboard, and stylus input. Other input devices may be supported by the system software in the future by the addition of new device drivers. Other devices may provide data input in one form or another--a scanner, for example--but these devices are not considered user input devices. These are considered data input devices and do not generate input events through the Input Manager.

The use of device drivers isolates your application and libraries from the specifics of the user's machine. GEOS includes drivers for most mice and keyboards (pen input uses a derivative of the mouse drivers), and drivers for new hardware may be added without requiring changes to your application.

A device driver takes raw input signals from the input device and translates it into raw input events that the Input Manager will understand. Your objects will never receive these events; they are only sent to the Input Manager for translation into pointer, button, or Keyboard events.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Input Flow | Next: 1.2 Input Manager and GenSystem


Input Manager and GenSystem

Input: 1.2 Input Flow: Input Manager and GenSystem

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Devices and Drivers | Next: 1.3 Input Events

The Input Manager is part of the User Interface and primarily assesses input events in the context of the system. It takes a raw input event from the device driver, passes it through various pre-set monitors (for the UI), and then adds contextual information if necessary. The Input Manager passes the refined input event on to the topmost system object, the GenSystem object.

The GenSystem object determines where the event should be passed. Typically, it will send the event on to whichever application is designated as the active application.

| Up: GEOS SDK TechDocs | Up | Prev: 1.1 Devices and Drivers | Next: 1.3 Input Events


Input Events

Input: 1.3 Input Flow: Input Events

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Input Manager and GenSystem | Next: 1.4 Input Hierarchies

GEOS uses three basic types of input events. These three types make up all the events necessary for mouse, keyboard, and pen input. They are Pointer events , Button events , and Keyboard events .

Pointer and Button events deal with mouse and pen input. Keyboard events provide keyboard key press information. Pointer and Button events are often referred to as "mouse" events and are dealt with together because most objects interested in one type will also be interested in the other.

A Pointer event details motion of the mouse pointer. If the user moves the mouse, a MSG_META_MOUSE_PTR will be generated by the Input Manager and sent to the proper application. The type of Pointer event (not always MSG_META_MOUSE_PTR ) is affected by the current keyboard state, mouse state, and other context information.

A Button event details the press or release of a particular mouse button. Because different Specific UIs have different meanings for each mouse button, the function of the button determines which message is sent out. In the OSF/Motif specific UI, the left mouse button is used for selection; therefore, presses on that button cause MSG_META_START_SELECT , and releases cause MSG_META_END_SELECT . The exact message sent out, like Pointer events, can also be affected by input state information.

Mouse events also carry with them several flags indicating various statistics about the event: whether the click is actually a double-click, whether any keys on the keyboard are being held down, etc.

A Keyboard event details the press or release of a particular key on the user's keyboard. Usually, objects interested in Keyboard events will only be interested in presses and can ignore releases. MSG_META_KBD_CHAR is the basic Keyboard event, and it carries flags detailing the state of the various modifier keys (e.g., Shift, Ctrl, Alt, NumLock). This message is always sent to the current focus.

Pen input comes in the form of a special data structure called Ink . Ink input is instigated in the same manner as Button events, but it is handled in a completely separate manner. An object expecting Ink input must be aware when Ink events are being passed as opposed to normal mouse events. Special flags are used for this; see Pen Input and Ink .

| Up: GEOS SDK TechDocs | Up | Prev: 1.2 Input Manager and GenSystem | Next: 1.4 Input Hierarchies


Input Hierarchies

Input: 1.4 Input Flow: Input Hierarchies

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Input Events | Next: 2 Mouse Input

Because the entire User Interface of GEOS is implemented as a hierarchy (tree) of objects, input messages naturally flow from the topmost object in the tree down to the object that should receive the input. At each level in the tree, the object decides whether to handle the event or pass it on to one of its children. Typically, the event will be passed all the way down the tree to a leaf node, where it will be handled or ignored.

To simplify and shorten this process, the UI maintains several special hierarchical input flow channels where special input should always flow. These hierarchies include the focus , the target , and the model . All three are described in full in Input Hierarchies .

The Focus is the object that should receive all Keyboard events. For example, a modal dialog box always has the focus; if the user presses Enter, the dialog box will take its default action. A text object into which the user is typing has the focus as long as the user does not click outside its bounds.

The Target is what is selected by the user: the object on which actions should be performed. Controller objects (of GenControlClass ) typically operate on the target. An example is a text object with selected text. When you set the text's style using a menu item (a style control object), that menu item sends its message to the current target object. The text object, set as the current Target, receives the message and sets the style of the selection.

An object is said to "have the focus" or "have the target" if it is the object specified by the hierarchy to receive that type of input. The system provides special travel options to allow you to send messages directly to the focus or target without knowing in advance which object is the destination.

The Model represents the non-visible model of the application's data. For example, a secondary selection in a database will not be acted on like the normal target, but it may still need to have similar properties. The Model hierarchy can be used to maintain this type of secondary or non-visible selection.

| Up: GEOS SDK TechDocs | Up | Prev: 1.3 Input Events | Next: 2 Mouse Input


Mouse Input

Input: 2 Mouse Input

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Input Flow | Next: 3 Keyboard Input

GEOS is built around the user's use of a mouse for most input. Although the system has extensive keyboard navigation capabilities, the mouse is still the preferred input tool for a GUI. Therefore, most applications will in some way accept and handle mouse input directly.

Typically, you will handle mouse input if you are using visible objects ( VisClass , etc.) that will react to the mouse. If you are using only generic objects in your application, you will not need to handle mouse events.

Mouse input comes in the form of pointer and Button events. Each event describes the mouse's location, the type of event (move, button press, etc.), whether any buttons on the mouse are pressed, and whether any modifier keys on the keyboard are pressed.

Objects can "grab" the mouse exclusive, taking all mouse events for themselves. This is useful when one object needs to get all mouse input for a period of time; when it no longer needs all mouse input, it releases the exclusive. Mouse events are dispatched directly to whichever object has the "active grab" (the exclusive).

If no object has the active grab, the event will be passed to the application under the pointer. The application will pass the event to the window or GenView under the pointer, and the window or view will pass it to its content. If the view displays a visible tree, the event will eventually reach the visible object under the mouse pointer, where it should be handled. Thus, the object under the mouse pointer is said to have the "implied grab."

The default behavior of the VisContent (passing the event to its first child with the appropriate bounds) is adequate for most input needs. If you are using large documents or visible layers, you will have to subclass the VisContent to handle these special cases.

Mouse Events

Gaining the Mouse Grab

Large Mouse Events

Setting the Pointer Image

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Input Flow | Next: 3 Keyboard Input


Mouse Events

Input: 2.1 Mouse Input: Mouse Events

| Up: GEOS SDK TechDocs | Up | Prev: 2 Mouse Input | Next: 2.2 Gaining the Mouse Grab

Each time the user moves the mouse or clicks a mouse button, GEOS generates a mouse event and passes it to the proper object. Mouse events are actually MetaClass messages that any object may intercept. Default handlers for these messages typically do nothing, so if you do not handle a particular event, it will likely be ignored.

GEOS uses three basic types of mouse events: button, pointer, and drag events. Button events indicate when the user has either pressed or released any mouse button (double-clicks are considered Button events). Pointer events indicate that the mouse has been moved. Drag events indicate the user has clicked and dragged the mouse. All of these types pass information about the mouse context and the operations currently in progress.

The Input Manager checks both the button state and the mouse movement before deciding on an event's type. For example, ifthe user clicks twice quickly, the Input Manager decides whether the user intended a double-click or a click-move-click. Objects receiving the events do not have to differentiate between the two; the events automatically contain the required information.

Pointer events are fairly straightforward; when the mouse is moved, the Input Manager generates the proper MSG_META_MOUSE_PTR indicating the move. This message also indicates the state of the mouse buttons and keyboard modifier keys.

Button events are quite a bit more complex. With each button press or release, the Input Manager generates a MSG_META_MOUSE_BUTTON which gets translated into the proper press/release/drag event before being passed on to the application. The final message generated depends on which button was pressed, whether large graphics coordinates are being used, and whether the event is being sent pre-passive or post-passive.

Among Button events, there are two basic types: the press and the release. All press events are of the form MSG_META_START_... , and all release events are of the form MSG_META_END_... . The last portion of the message name is the function of the button pressed or released. The various buttons are referred to by their meaning to the Specific UI, as follows:

SELECT
The button used for making selections. In OSF/Motif and many other Specific UIs, this is the left mouse button.
MOVE_COPY
The button used for quick-copies and quick-moves of data. In OSF/Motif, this is the right mouse button. For single-button mice, a key sequence plus a mouse click is often used.
FEATURES
The button used to bring up a "features" pop-up menu or dialog box. In some Specific UIs, this is the middle or the right mouse button. For single- or two-button mice, a key sequence plus a mouse click is often used.
OTHER
Any button not designated one of the three categories above. This category can also be used to indicate when the user presses more than one button at a time.

A press event indicates the user pressed down on the particular button. A release event indicates the user released the button. If the user presses a button and moves the mouse, a drag event will be sent after the initial press event. If the user double-clicks, a special flag will be sent with a single press event; it is up to the application to handle double-clicks differently.

Drag events are of the form MSG_META_DRAG_... , similar to Button events. Each of the above button types has a corresponding drag message. If the user presses a mouse button and quickly moves the mouse more than a specified distance, or if he holds a particular mouse button down more than a specified time, the Input Manager will send a drag event after the press event. A single release event signifies the user released the mouse button, just as with normal presses.

There is also a complete set of events used for large documents. If an object has large bounds, or if a GenView is set up to display a large content, large mouse events will be generated instead of normal mouse events. Large events take the form MSG_META_LARGE_... . For example, the large version of MSG_META_MOUSE_PTR is MSG_META_LARGE_PTR .

Below are listed all the standard mouse events your objects may be interested in handling. Most objects will be interested in only a small subset of these.

MSG_META_MOUSE_PTR
The standard Pointer event, generated whenever the mouse moves without a button down.
MSG_META_START_SELECT
Generated when the user presses the select button.
MSG_META_END_SELECT
Generated when the user releases the select button.
MSG_META_START_MOVE_COPY
Generated when the user presses the move/copy button.
MSG_META_END_MOVE_COPY
Generated when the user releases the move/copy button.
MSG_META_START_FEATURES
Generated when the user presses the features button.
MSG_META_END_FEATURES
Generated when the user releases the features button.
MSG_META_START_OTHER
Generated when the user presses a button combination not recognized as select, move/copy, or features.
MSG_META_END_OTHER
Generated at the release of the combination that generated the MSG_META_START_OTHER .
MSG_META_DRAG_SELECT
Generated between a press and release of the select button if the user holds the button down or if the user moves the mouse.
MSG_META_DRAG_MOVE_COPY
Generated between a press and release of the move/copy button if the user holds the button down or if the user moves the mouse.
MSG_META_DRAG_FEATURES
Generated between a press and release of the features button if the user holds the button down or if the user moves the mouse.
MSG_META_DRAG_OTHER
Generated between a press and release of a button not specified above if the user holds it down or if the user moves the mouse.

Listed below are the large equivalents of the above messages.

MSG_META_LARGE_PTR
MSG_META_LARGE_START_SELECT
MSG_META_LARGE_END_SELECT
MSG_META_LARGE_START_MOVE_COPY
MSG_META_LARGE_END_MOVE_COPY
MSG_META_LARGE_START_FEATURES
MSG_META_LARGE_END_FEATURES
MSG_META_LARGE_START_OTHER
MSG_META_LARGE_END_OTHER
MSG_META_LARGE_DRAG_SELECT
MSG_META_LARGE_DRAG_MOVE_COPY
MSG_META_LARGE_DRAG_FEATURES
MSG_META_LARGE_DRAG_OTHER

All the normal (as opposed to large) mouse events pass and return the same values. Each event differs based on the message itself; an object knows that MSG_META_START_SELECT is inherently different from MSG_META_START_MOVE_COPY , even though they may pass the exact same values.

Structure of Mouse Events

Each mouse event passes three items of data and one pointer to a return structure. The three parameters are listed below; the fourth, the return structure, is detailed in the next section.

xPosition
The horizontal position of the pointer in document coordinates in the window when the event was generated.
yPosition
The vertical position of the pointer in document coordinates in the window when the event was generated.
inputState
A word of flags indicating the state of the mouse buttons and the UI functions (such as quick-transfer) that were active when the event was generated.

The first two indicate the position of the mouse in the document. The third, inputState , consists of two bytes of flags. The first byte indicates the type of Button event and the state of the mouse buttons during the event. It is a record of type ButtonInfo and has seven flags:

BI_PRESS
Set if this is a press event rather than a release or drag.
BI_DOUBLE_PRESS
Set if this is actually a double-press (GEOS automatically detects double presses).
BI_B3_DOWN
Set if button number three is being held down.
BI_B2_DOWN
Set if button number two is being held down.
BI_B1_DOWN
Set if button number one is being held down.
BI_B0_DOWN
Set if button number zero is being held down.
BI_BUTTON
Set if this is a Button event, clear if a Pointer event.

The second byte of inputState is a record of UIFunctionsActive , which describes which of several UI functions are currently underway. The flags set in this byte are used primarily by the UI, and you will probably not have to check them. The flags allowed, however, are listed below.

UIFA_SELECT
Set if the basic mouse function is underway.
UIFA_MOVE_COPY
Set if a move/copy (quick-transfer) operation is underway.
UIFA_FEATURES
Set if the features popup menu or dialog is open.
UIFA_CONSTRAIN
Set if a modifier key set as a "constraint" key is pressed (e.g., the user holds the Shift key while grabbing an object control point).
UIFA_ADJUST
This is the same bit as UIFA_MOVE and UIFA_POPUP, below. When UIFA_SELECT is also set, this flag indicates that the select event should act as a toggle event.
UIFA_EXTEND
This is the same bit as UIFA_COPY and UIFA_PAN, below. When UIFA_SELECT is also set, this flag indicates that the select event should extend any selection already made (add the area/objects to the current selection).
UIFA_MOVE
This is the same bit as UIFA_ADJUST and UIFA_POPUP. When UIFA_MOVE_COPY is also set, this flag indicates that the operation should be considered a move rather than a copy.
UIFA_COPY
This is the same bit as UIFA_EXTEND and UIFA_PAN. When UIFA_MOVE_COPY is also set, this flag indicates the operation should be considered a copy rather than a move.
UIFA_POPUP
This is the same bit as UIFA_ADJUST and UIFA_MOVE, above. When UIFA_FEATURES is also set, this flag indicates the "features" button brings up a popup menu or dialog box.
UIFA_PAN
This is the same bit as UIFA_EXTEND and UIFA_COPY, above. When UIFA_FEATURES is also set, this flag indicates the "features" button has initiated pan-style scrolling.

Return Values for Mouse Events

One of the parameters of every mouse event is a pointer to a MouseReturnParams structure. This structure is passed empty; it is up to the event handler to fill it with the proper return values. The MouseReturnParams structure's definition is given below:

typedef struct {
    word                unused;  /* for alignment */
    MouseReturnFlags    flags;
    optr                ptrImage;
} MouseReturnParams;

Every time you handle a mouse event, you must fill in the flags field. This describes how the event was handled so the UI knows whether to pass it on to another object, change the pointer image, or treat the event as having been handled. The flags you can return are

MRF_PROCESSED
The event was handled. If you do not set this flag upon return, the window will think the event was not handled and may pass it on to another child.
MRF_REPLAY
The event may or may not have been handled, but it should be rebroadcast as if it had not been. This is used primarily when an object gives up the mouse grab because the mouse has strayed outside its bounds.
MRF_PREVENT_PASS_THROUGH
The event should not be passed through to the active or implied grab object. This is set only by pre-passive handlers which want to filter the event and keep the active or implied grabs from receiving it.
MRF_SET_POINTER_IMAGE
The pointer should be set to a new image. If this flag is returned, you must also return an optr in the ptrImage field (see below).
MRF_CLEAR_POINTER_IMAGE
The pointer image will be reset to the default. See Setting the Pointer Image .

If you return MRF_SET_POINTER_IMAGE, you must specify the new image to be set. An object may want to set the pointer to a new image when it is over the object's bounds or during a drag operation. For example, if the user selects a drawing tool, the tool might set the pointer to cross-hairs instead of the default arrow.

To set the pointer's image, you must return the handle and chunk handle of a chunk containing a PointerDef structure. Return it in the form of an optr in the ptrImage field of the return structure. For full information on PointerDef and defining pointer images, see Setting the Pointer Image .

If you are not setting the pointer image, return a NullOptr in ptrImage . You do not have to return anything in the unused field.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Mouse Input | Next: 2.2 Gaining the Mouse Grab


Gaining the Mouse Grab

Input: 2.2 Mouse Input: Gaining the Mouse Grab

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Mouse Events | Next: 2.3 Large Mouse Events

When no object has the active mouse grab, mouse events are passed to whichever object is directly under the pointer's image. If any object has the grab, however, mouse events will be passed only to the object with the grab, no matter where the pointer is on the screen.

When the user clicks inside a view, the GenView automatically grabs the mouse while the user holds the mouse button down. This allows the view to know when drag scrolling takes place (the user drags the pointer outside the view's bounds, but the mouse event is still passed to the GenView). Process objects acting as a GenView's content therefore do not have to worry about grabbing the mouse. If you are displaying visible objects within the GenView, however, your objects will likely want to grab the mouse for themselves.

Typically, a visible object will grab the mouse on a MSG_META_START_... event and release it on a MSG_META_END_... event. To grab the mouse, a visible object has to send itself the message MSG_VIS_GRAB_MOUSE ; to release the grab, it must send itself MSG_VIS_RELEASE_MOUSE .

The TicTac sample application shows an example of visible objects grabbing the mouse and handling several mouse events. This sample application is described in detail in the UI Overview.

There are two other types of mouse grabs besides the active grab. With very rare exceptions, application programmers can ignore these. They are the pre-passive and post-passive grabs.

If an object has a pre-passive grab, it will receive copies of all Button events before the events are passed on to their true destinations. Pre-passive grabs are used by window objects to bring themselves to the front when the user clicks in them. Any number of objects may have pre-passive grabs. To gain a pre-passive grab, a visible object must send itself MSG_VIS_ADD_BUTTON_PRE_PASSIVE ; to release it, the object must send itself MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE .

If an object has a post-passive grab, it will receive copies of all Button events after the events have been handled. Any number of objects may have post-passive grabs. To gain a post-passive grab, a visible object must send itself MSG_VIS_ADD_BUTTON_POST_PASSIVE ; to release the grab, the object must send itself MSG_VIS_REMOVE_BUTTON_POST_PASSIVE .

GEOS uses different messages to indicate that the event is a pre-passive or a post-passive event. All the normal Button events have pre-passive and post-passive counterparts which take the form MSG_META_PRE_PASSIVE_... and MSG_META_POST_PASSIVE_... . For example, the pre-passive version of MSG_META_START_SELECT is MSG_META_PRE_PASSIVE_START_SELECT .

Below are listed the pre-passive equivalents of the above messages. Note that the drag events and large events do not have pre-passive equivalents.

MSG_META_PRE_PASSIVE_START_SELECT
MSG_META_PRE_PASSIVE_END_SELECT
MSG_META_PRE_PASSIVE_START_MOVE_COPY
MSG_META_PRE_PASSIVE_END_MOVE_COPY
MSG_META_PRE_PASSIVE_START_FEATURES
MSG_META_PRE_PASSIVE_END_FEATURES
MSG_META_PRE_PASSIVE_START_OTHER
MSG_META_PRE_PASSIVE_END_OTHER

Listed below are the post-passive equivalents of the above messages. Note that the drag events and large events do not have post-passive equivalents.

MSG_META_POST_PASSIVE_START_SELECT
MSG_META_POST_PASSIVE_END_SELECT
MSG_META_POST_PASSIVE_START_ADJUST
MSG_META_POST_PASSIVE_END_MOVE_COPY
MSG_META_POST_PASSIVE_START_FEATURES
MSG_META_POST_PASSIVE_END_FEATURES
MSG_META_POST_PASSIVE_START_OTHER
MSG_META_POST_PASSIVE_END_OTHER
| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Mouse Events | Next: 2.3 Large Mouse Events


Large Mouse Events

Input: 2.3 Mouse Input: Large Mouse Events

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Gaining the Mouse Grab | Next: 2.4 Setting the Pointer Image

Applications and visible objects that use large documents (32-bit coordinate spaces) receive special versions of the mouse input messages. These special messages pass the pointer position differently; otherwise, they pass the same flags as passed by their normal counterparts.

Large mouse messages take the form MSG_META_LARGE_... . There is one large mouse event for each of the normal pointer, button, and drag events; note, however, that no large events exist for pre-passive or post-passive grabs. For the full list of large mouse events, see MSG_META_LARGE_PTR MSG_META_LARGE_START_SELECT MSG_META_LARGE_END_SELECT MSG_META_LARGE_START_MOVE_COPY MSG_META_LARGE_END_MOVE_COPY MSG_META_LARGE_START_FEATURES MSG_META_LARGE_END_FEATURES MSG_META_LARGE_START_OTHER MSG_META_LARGE_END_OTHER MS .

One special note about large events: Any object that handles large mouse events should also be prepared to handle normal mouse events. The visible parent of the large object can only send out one type (large or normal) of event at a time to its children; if it has any normal children at all, it will send out the normal event. If the large object should handle the normal event and doesn't, the event will be lost or passed on to another child.

For full information on handling large mouse events within a visible object tree, see the VisClass chapter. (This information also applies to Process objects acting as the contents of GenViews.)

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Gaining the Mouse Grab | Next: 2.4 Setting the Pointer Image


Setting the Pointer Image

Input: 2.4 Mouse Input: Setting the Pointer Image

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Large Mouse Events | Next: 3 Keyboard Input

The GEOS User Interface uses several default settings for the mouse pointer, all defined by the Specific UI.

Some applications will want to modify the mouse pointer's image in order to provide certain feedback to the user. For example, a chess game may set the pointer to an image of the piece being moved and then reset it to an arrow after the move is completed; a graphics application could set the pointer to an image of whatever tool is currently in use. Another time an application may change the mouse pointer is when a quick-transfer is in progress.

Defining the Pointer Image

The pointer is defined as a bitmap 16 pixels on each side. It has a "hot spot" of five pixels that acts as the active point of the image. When the user clicks, the area affected is the hot spot, not the entire image.

The pointer is stored in a PointerDef16 structure. This structure defines the image bitmap, the horizontal and vertical offsets to the hot spot, and the definition of how the image should mix with the background. This definition is known as the pointer's mask.

The definition of PointerDef16 is given below. When setting the pointer image, you will typically pass an optr (a combination handle and chunk handle) to a PointerDef16 structure; the structure can be set up in a sharable chunk beforehand, or it can be set in a local or global variable. Typically, you will set the entire pointer image in a resource chunk and compile it into your geode; this is much easier and faster to use than building it at run-time.

typedef struct {
    byte	   PD_width;
    byte	   PD_height;
    sbyte	   PD_hotX;
    sbyte	   PD_hotY;
    byte	   PD_mask[STANDARD_CURSOR_IMAGE_SIZE];
    byte	   PD_image[STANDARD_CURSOR_IMAGE_SIZE];
} PointerDef16;

The PD_hotX field defines the horizontal offset from the upper-left corner of the image to the center of the hot spot. Similarly, PD_hotY defines the vertical offset from the upper-left corner to the hot spot. Both are in pixels and must be less than 16 (since the image is only 16 pixels in each dimension).

The PD_mask field contains the 16 by 16 bitmap of the mask, while PD_image holds the bitmap of the image. Both are stored as 32-byte arrays, two bytes per row. For example, if the first line of the image was all black and the second all white, the first four bytes of PD_image would be 0xFF, 0xFF, 0x00, and 0x00.

Note that the block the pointer chunk is stored in must be declared sharable.

Setting the Pointer Image

You can set the pointer's image in three ways. First, you can send a message to the GenView to set the pointer image whenever the pointer is over the view's window (see the GenView chapter). Second, you can set the pointer to a default image type during a quick-transfer operation (see the Clipboard chapter). Third, you can set the pointer image after handling a mouse event. Only the third option is discussed here.

An object may wish to set the pointer image while it is selected, while the pointer is over its bounds, or in any number of other situations. For example, a chess piece currently being moved may set the pointer's image to the shape of the piece so the user knows exactly what type of piece is being moved.

Every mouse event you handle must return a MouseReturnParams structure, defined in Return Values for Mouse Events . Make sure to return the following to set the pointer image:

  • MRF_SET_POINTER_IMAGE in flags
     When the UI sees this flag in the returned structure, it looks for the address of a pointer definition in the ptrImage field. If you do not return this flag, the UI will not try to set the pointer.
  • The optr of the pointer image in ptrImage
     This field gives the handle and chunk handle of the PointerDef structure that defines the pointer image to be set. The structure must be in a sharable block; most likely you will have it set in a geode resource.
| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Large Mouse Events | Next: 3 Keyboard Input


Keyboard Input

Input: 3 Keyboard Input

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Mouse Input | Next: 4 Pen Input and Ink

Keyboard input is scanned by a device driver and passed on to the Input Manager, just as mouse input is. The keyboard driver and Input Manager check the state of the modifier keys (Shift, Ctrl, Alt, Num Lock, etc.) and parse each keypress as much as possible based on its context. Each keypress is called a Keyboard event and is passed in MSG_META_KBD_CHAR .

Keyboard Input Flow

Keyboard Events

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Mouse Input | Next: 4 Pen Input and Ink


Keyboard Input Flow

Input: 3.1 Keyboard Input: Keyboard Input Flow

| Up: GEOS SDK TechDocs | Up | Prev: 3 Keyboard Input | Next: 3.2 Keyboard Events

Each time the user presses a character (with or without modifier keys) on the keyboard, the Input Manager generates a single Keyboard event and passes it to the GenSystem object. The GenSystem object delivers the event to all the proper objects based on passive grabs (if any) and the current focus hierarchy.

As with mouse events, all Keyboard events are passed down the object hierarchy from the GenSystem object through an application's GenApplication object and on down to a leaf object. When handling keyboard input, you can choose to intercept the events at the GenApplication level or at any node below it where the event should be handled.

Unlike with mouse input, there is no "implied grab" for keyboard input. Keyboard input is always passed to the object that has the focus (for information on the Focus hierarchy, see Using Focus ). Therefore the focus is the keyboard's "active grab."

Objects can, however, set up passive grabs as they can with mouse input. A pre-passive grab allows the visible object to receive Keyboard events before the focus object gets them. A post-passive grab delivers events to the visible object after they have been sent to the focus. For more information about gaining and releasing the passive keyboard grabs, see the VisClass chapter.

If you intercept Keyboard events, you must be sure to return any events that you don't use. For example, if you only want to detect when the user hits the "a" key, you must return all keypresses that are not "a." Otherwise, your MSG_META_KBD_CHAR handler will eat all keypresses, and keyboard menu navigation will not work. If intercepting Keyboard events in a generic object, you can simply call the superclass for each unused character. If intercepting Keyboard events in a visible object, you must send MSG_META_FUP_KBD_CHAR for each unused character.

To ensure that a particular object (for example, a specific GenText object) automatically will receive keyboard input, you must set HINT_DEFAULT_FOCUS for that object and all its parents up to the GenPrimary containing it. Otherwise, the generic UI does not know which object should gain the default focus.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Keyboard Input | Next: 3.2 Keyboard Events


Keyboard Events

Input: 3.2 Keyboard Input: Keyboard Events

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Keyboard Input Flow | Next: 4 Pen Input and Ink

Each Keyboard event your application and objects receive has gone through preliminary parsing by the keyboard driver. Keyboard drivers are intelligent enough to know several things about different types of keystrokes depending on the character set and the modifier keys held down.

All Keyboard events pass two sets of information: the current character as interpreted by the keyboard driver, and the actual scan code of the character pressed. This allows you to do additional parsing or to ignore any extraneous information about the keypress that may have been included by the driver.

For example, if the user presses Ctrl-1 , the keyboard driver passes on the character "1" with a flag indicating the Ctrl key was pressed. If the user presses Shift-1 , the keyboard driver passes on the "!" character without a flag indicating the Shift key is pressed (the Shift key is eaten by the keyboard driver); in this case, it also passes on the scan code of the "1" key. If the user then presses Ctrl-Shift-1 , different keyboard drivers may pass different characters. Whether the driver passes "1" or "!," however, the scan code for the "1" key will also be passed.

The keyboard driver also understands special "extended" keypresses and "temporary accents". Some keyboard drivers may use two keystrokes to specify special characters; for example, some keyboard drivers may require the ö character to be entered as "Ctrl-q o" (this is called an extended keypress), and some may require it to be entered as two separate keys: the "o" and the umlaut (this is called a temporary accent).

In addition to the actual character and the scan code, every Keyboard event gives flags indicating the state of the modifier keys (Ctrl, Alt, Shift), the state of the toggle keys (Caps Lock, Num Lock, etc.), and what type of event it is (first press, repeated press, release, or first press of an extended sequence).

Standard Keyboard events come in MSG_META_KBD_CHAR . This message has the same parameters and return values as its pre-passive and post-passive counterparts, MSG_META_PRE_PASSIVE_KBD_CHAR and MSG_META_POST_PASSIVE_KBD_CHAR . The parameters are listed below:

character
The character value determined by the keyboard driver and Input Manager.
flags
A word value: The high byte is a record of ShiftState detailing the modifier keys pressed, and the low byte is a record of CharFlags giving information about the type of character passed. Both of these records are detailed below.
state
A word value: The high byte is the scan code of the key pressed (without modifiers), and the low byte is a record of ToggleState detailing the state of the toggle keys. ToggleState is detailed below.

Three different records of flags define the Keyboard event. The ShiftState record describes which modifier keys are pressed and has the following flags:

SS_LALT
The left Alt key is pressed.
SS_RALT
The right Alt key is pressed.
SS_LCTRL
The left Ctrl key is pressed.
SS_RCTRL
The right Ctrl key is pressed.
SS_LSHIFT
The left Shift key is pressed.
SS_RSHIFT
The right Shift key is pressed.
SS_FIRE_BUTTON_1
The first joystick-style "fire button" (if any) is pressed.
SS_FIRE_BUTTON_2
The second joystick-style "fire button" (if any) is pressed.

The ToggleState record describes which toggle keys are currently active. It has the following flags.

TS_CAPSLOCK
The Caps Lock is set.
TS_NUMLOCK
The Num Lock is set.
TS_SCROLLLOCK
The Scroll Lock is set.

The CharFlags record contains several flags indicating whether this event is a first press, a release, a repeat press, or an extended or temporary character. Its flags are listed below.

CF_STATE_KEY
Set if either a ShiftState key or a ToggleState key is being pressed along with the passed character.
CF_EXTENDED
Set if this event is part of an extended keystroke. This flag is generally only used by the keyboard driver during its parsing; you will not have to use it. (Extended keypresses are passed by the Input Manager to your application as a single event with the resultant character, not as two events.)
CF_TEMP_ACCENT
Set if this event is part of a temporary accent keystroke. Temporary accents are used only by the keyboard driver, like extended keypresses above.
CF_FIRST_PRESS
Set if the event represents the user's first press of the key. This is akin to a button press event and will, at some point, be followed by a CF_RELEASE event.
CF_REPEAT_PRESS
Set if the event is generated by the user holding down the key (as opposed to a first press, above). If you want the user to hit the key for each individual character, you should ignore this type of event.
CF_RELEASE
Set if the user lets up on a key. Applications typically are not interested in these events for text, and they can automatically be ignored by setting GVA_DONT_SEND_KBD_RELEASES in your GenView's instance data (this only affects handling Keyboard events in visible objects).

A sample handler for MSG_META_KBD_CHAR is shown in Sample MSG_META_KBD_CHAR Handler . It is used by a visible object that simply increments one of its instance fields each time the greater-than key (>) is pressed.

Code Display 11-1 Sample MSG_META_KBD_CHAR Handler

/* This method is used by a visible object of MyVisObjClass. It takes all
 * occurrences of the greater-than (>) key and increments the object's MVOCI_data
 * instance field, ignoring when the user holds down the key. Also, the GenView
 * is set to pass along key releases as well as presses; the method must also
 * ignore releases.
 *
 * Note that the object will only get keyboard input when it has the focus. */
/* The format of this message is
 * void (word character, word flags, word state) */
@method MyVisObjClass, MSG_META_KBD_CHAR {
    /* First, check if the character is the one we want. */
    if (character == `>') {
    /* If it is, make sure the user is not holding the key down and
     * that this is not a release event. Check that CF_REPEAT_PRESS and
     * CF_RELEASE are not set in the low byte of the flags parameter.
     * If either is set, ignore the character and send MSG_META_FUP_KBD_CHAR
     * so the UI may provide default keyboard navigation. */
    if ((flags & CF_REPEAT_PRESS) || (flags & CF_RELEASE)) {
        @call self::MSG_META_FUP_KBD_CHAR(character, flags, state);
    } else {
        /* If we get here, we know the character is what we want. Increment
         * the instance field and return. */
        (pself->MVOCI_data)++;
    }
    /* If the character is not a greater-than, we must send it on to the UI
     * with MSG_META_FUP_KBD_CHAR. If we do not, all other keyboard presses
     * we receive will never be handled; this will cause keyboard
     * accelerators and menu navigation not to work. */
    } else {
    @call self::MSG_META_FUP_KBD_CHAR(character, flags, state);
    }
}
| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Keyboard Input Flow | Next: 4 Pen Input and Ink


Pen Input and Ink

Input: 4 Pen Input and Ink

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Keyboard Input | Next: 5 Input Hierarchies

GEOS supports pen-based systems and applications by means of the Ink data structure formats. Ink is simply a standard way of representing raw pen input as a data structure.

At the lowest level, individual visible objects can receive Ink input directly, much like they receive mouse input normally. (In addition, the VisText object has some Ink handling and handwriting recognition built into it.)

The Pen Library exports InkClass, a visible object class which accepts and stores pen input. It stores the ink compactly. The ink object allows the pen to act as an eraser, removing part or all of a pen stroke, perhaps splitting some strokes in two. See the Pen Object Library chapter for more information.

If GEOS is running on a pen-based system, it will set a particular flag in its private data. It will also install a special input monitor to alter the events the Input Manager receives from the device driver. The Input Manager and the UI then react to input events in a different manner and do not send them directly as with mouse events.

Because Ink events are not passed on immediately by the UI, the UI actually takes care of drawing them directly to the screen. This provides the user direct, immediate feedback.

Ink Data Structures

Ink Input Flow

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Keyboard Input | Next: 5 Input Hierarchies


Ink Data Structures

Input: 4.1 Pen Input and Ink: Ink Data Structures

| Up: GEOS SDK TechDocs | Up | Prev: 4 Pen Input and Ink | Next: 4.2 Ink Input Flow

Ink input is stored in data blocks. The Input Manager stores up Ink events into a data block and then transfers the block to the proper window or application with MSG_META_NOTIFY_WITH_DATA_BLOCK . (This is a general change notification message.) The type of the data block passed is NT_INK, the manufacturer ID is MANUFACTURER_ID_GEOWORKS.

The data block is headed by an InkHeader structure (defined below). The header is followed by a list of points, all in screen coordinates (not points as with normal graphics commands). The block will contain as many points as are registered during the Ink input sequence.

The InkHeader structure has the following format:

typedef struct {
    word       IH_count;
    Rectangle  IH_bounds;
    optr       IH_destination;
    dword      IH_reserved;
    Point      IH_data;
} InkHeader;

The fields of the structure are listed below with the information they contain.

IH_count
The number of points stored in the data block.
IH_bounds
The bounds of the Ink input on the screen (in document coordinates).
IH_destination
The destination of the Ink input. An object can use this field to see if the Ink was sent directly to it or if it received the Ink simply due to an overlap.
IH_reserved
Internal data; do not use.
IH_data
Actually a label indicating the beginning of the list of points. Following this label will be a number of Point structures, each one detailing a single Ink point.
| Up: GEOS SDK TechDocs | Up | Prev: 4 Pen Input and Ink | Next: 4.2 Ink Input Flow


Ink Input Flow

Input: 4.2 Pen Input and Ink: Ink Input Flow

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Ink Data Structures | Next: 5 Input Hierarchies

When the Input Manager receives a Button event, it checks to see if that event should be treated as Ink input. It first holds up input, putting the Button event in the holdup queue, and then it queries the appropriate application with MSG_META_QUERY_IF_PRESS_IS_INK . Typically the application will pass the message down its object tree to the appropriate visible or generic object that should initially have received the Button event.

Determining if a Press Is Ink

It is up to the application or its visible object to determine whether the input should be treated as Ink. If you don't ever handle Ink, you do not need to do anything different. If any of your objects handles Ink, however, you should set up your GenView to pass Ink events on to its content. Depending how you set up your GenView, you may or may not have to write a handler for MSG_META_QUERY_IF_PRESS_IS_INK . The context and details of this message are described below.

To set up your GenView for passing Ink input, set the proper flag in its GVI_inkType

instance field. The possible flags are detailed in full in the GenView chapter but are reviewed below for convenience.

GVIT_PRESSES_ARE_NOT_INK
Objects running under this GenView never handle Ink input. This flag is set as the default. Your objects will not have to handle MSG_META_QUERY_IF_PRESS_IS_INK or MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK .
GVIT_PRESSES_ARE_INK
Objects running under this GenView always want Ink input. Your objects will not have to handle the message MSG_META_QUERY_IF_PRESS_IS_INK , but they will have to handle MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK .
GVIT_INK_WITH_STANDARD_OVERRIDE
Objects running under this GenView want Ink, but the user can override it to give mouse events. Your objects will not have to handle MSG_META_QUERY_IF_PRESS_IS_INK , but they will have to handle MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK .
GVIT_QUERY_OUTPUT
The Objects running under this GenView want Ink input, but only under certain circumstances. Your objects will have to handle both MSG_META_QUERY_IF_PRESS_IS_INK and MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK . Note that this option may cause the UI to query across threads, something that could cause performance lags on a busy system. Therefore, whenever possible, you should set one of the other three flags.

Depending on how you set up your GenView, you may need to subclass MSG_META_QUERY_IF_PRESS_IS_INK (see above). This message is sent by the Input Manager to see if the object clicked on can handle Ink input. Objects that handle Ink should return IRV_DESIRES_INK. (There is an analogous MSG_META_LARGE_QUERY_IF_PRESS_IS_INK for objects and contents using large documents.)

If the application sends the query message to a Process object acting as a view's content, the Process object should not return a value. Instead, it should respond by sending MSG_GEN_APPLICATION_INK_QUERY_REPLY to its GenApplication object; the application object will then pass the appropriate value on to the Input Manager.

Some objects may want both Ink and mouse input; the VisText object is a good example of this--it accepts Ink for handwriting recognition and mouse events for cursor movement and text selection. GEOS uses a convention for objects that want both types of input: quick-clicks and click-and-holds in the object are mouse events, but click-and-drags are Ink events.

To use both pen and mouse input, your object should return IRV_INK_WITH_STANDARD_OVERRIDE instead of IRV_DESIRES_INK in its MSG_META_QUERY_IF_PRESS_IS_INK handler. (Or if all objects running in the same GenView want this behavior, you can simply set the GenView's GVIT_INK_WITH_STANDARD_OVERRIDE flag.)

Controlling the Ink

When an object requests Ink input by returning IRV_DESIRES_INK or IRV_INK_WITH_STANDARD_OVERRIDE, it must also specify the eventual destination of the Ink data block. Additionally, it can set any characteristics in a GState for the Ink--clipping area, drawing color, etc.

These specifics are set in an InkDestinationInfo structure set up by your MSG_META_QUERY_IF_PRESS_IS_INK handler (through a call to UserCreateInkDestinationInfo() ); the handler for the routine returns the handle of a block containing the structure. Certain default values and behavior are implemented if you return a null handle or if you set the GenView flags to avoid the query (GVIT_PRESSES_ARE_INK or GVIT_INK_WITH_STANDARD_OVERRIDE).

If you choose not to set any of these specifics, Ink will have the following default behaviors:

  • The destination of the Ink will be the object that would have received the initial click event (typically the implied mouse grab) or the object that fielded the MSG_META_QUERY_IF_PRESS_IS_INK .
  • The Ink will not be clipped; the user may draw over the entire screen, overlapping generic objects and other windows. (All the Ink will still be sent to the same destination, however.)
  • The Ink will be drawn in a standard color and brush thickness.

All of the above behavior can be changed in the InkDestinationInfo structure. The structure should be created automatically with the routine UserCreateInkDestinationInfo() ; you can then pass the block handle of this structure blindly to routines that demand an InkDestinationInfo structure. Among the four fields of the structure, the following need to be passed to UserCreateInkDestinationInfo() :

dest
The optr of the object that will receive the Ink data block after all the Ink has been collected. An object may use this field to force the destination of the Ink to be any particular object.
gs
The handle of a Graphic State (GState) containing clipping, color, or other information about how the Ink should be drawn on the screen. To use the default values, set this to zero. This will allow the user to draw Ink all over the screen. See the Graphics Environment chapter for full information on GStates.
brushSize
The thickness of the brush used when drawing the Ink on the screen. This is the same as the brush thickness for polylines; see the Drawing Shapes chapter for more information on brush thickness.
callback
Virtual fptr to a callback routine to determine whether a stroke is a gesture or not. This callback routine will be passed to ProcCallFixedOrMovable() .

How Ink Is Stored and Passed On

When the user presses the pen to the screen and the input is determined to be Ink, the Input Manager generates a mouse button press event and begins storing up the subsequent mouse events. The UI then determines whether that press event should be treated as Ink or not (the default behavior is to treat them as normal mouse events).

If the events should not be treated as Ink, they are passed on as normal mouse events. If they are to be treated as Ink, they are stored and are not passed on. Instead, the UI stores them up in a special data block until the Ink input is stopped. Ink input is stopped when the user removes the pen from the screen for a user-specified amount of time. This amount of time is specified in the GEOS.INI file in the [system] category under the key inkTimeout .

After the user finishes entering the Ink input, the Input Manager determines which object will receive the data block (it may not be the original object that received the initial press). It first queries the original object with MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK . Any object handling Ink input must subclass this message and make sure that the top Ink point is not above its upper bound; it should then return its upper bound and its optr. GEOS provides a routine that does just this; if your object will always handle Ink input, you can set this routine up as the method to be used as follows:

@method VisObjectHandlesInkReply, YourClassName,
                MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK;

If the object handles Ink, the Input Manager sends the entire data block off to the GenSystem object using MSG_META_NOTIFY_WITH_DATA_BLOCK with the identifier NT_INK. (For information on this and other notification messages, see the General Change Notification chapter.)

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Ink Data Structures | Next: 5 Input Hierarchies


Input Hierarchies

Input: 5 Input Hierarchies

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Pen Input and Ink | Next: 5.1 The Three Hierarchies

As previously mentioned in this chapter, the Input Manager channels input events to particular objects. The objects are chosen based on the current input grabs, the position of the pointer, and the state of the application. Sometimes, though, input events must be sent to a particular object not specified by one of these means. One example is keyboard input: While an object can grab the keyboard exclusive, it should not; instead, it should make itself the focus . The object that has the focus receives all keyboard input ( MSG_META_KBD_CHAR ).

Similarly, your application or other objects may need to send messages to the object which is currently active. For example, a Text Sizes menu may need to know which of several text objects currently has an active selection. Then, when the user selects a new text size, the menu can send its message to the proper text object. The object that receives these messages is called the target object.

The menu in this case, however, should not have to explicitly name the destination of its message. The menu should be able to specify that its message should go to the current target object; the UI knows which object is the target, and it redirects the message automatically.

The GEOS UI uses three special hierarchical mechanisms to fulfill these two purposes (specialized input flow and specification of destinations). These hierarchical mechanisms are built into the basic structure of GEOS objects and are therefore universal and consistent for all applications and all objects. The three hierarchies used are the focus , the target , and the model . (There is a fourth, the controller, but it is used only internally by GEOS.) These hierarchies all function in the same manner but are used for different purposes.

The Three Hierarchies

Common Hierarchy Basics

Using Focus

Using Target

Using Model

Extending the Hierarchies

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Pen Input and Ink | Next: 5.1 The Three Hierarchies


The Three Hierarchies

Input: 5.1 Input Hierarchies: The Three Hierarchies

| Up: GEOS SDK TechDocs | Up | Prev: 5 Input Hierarchies | Next: 5.2 Common Hierarchy Basics

As stated above, GEOS offers three hierarchies you can use for input flow and specification of message destinations. These hierarchies, with their basic purposes, are listed below.

  • Focus
    The Focus hierarchy designates which object should currently receive keyboard input. Essentially, the focus object has the active keyboard grab. The text objects (VisText and GenText) manage the focus automatically; if you use only these objects for keyboard input, you will not need to understand the focus. The Focus hierarchy is described in full in Using Focus .
  • Target
    The Target hierarchy designates the active view, window, and visible selection which may be acted upon. In most cases, the target will be an object the user has selected and now wishes to alter based on menu and dialog selections. For example, a graphic object selected becomes the target object of the application; when the user selects "Rotate" or some other menu function, the menu sends its message to the selected object via the target hierarchy. The Target hierarchy is described in full in Using Target .
  • Model
    The Model hierarchy designates the model of the data the user can manipulate. This is provided for highly complex applications that need to extend the Target hierarchy for more specific handling of selections. It extends from the GenSystem through the GenApplication and the document control objects. The Model hierarchy is described in full in Using Model .
| Up: GEOS SDK TechDocs | Up | Prev: 5 Input Hierarchies | Next: 5.2 Common Hierarchy Basics


Common Hierarchy Basics

Input: 5.2 Input Hierarchies: Common Hierarchy Basics

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 The Three Hierarchies | Next: 5.3 Using Focus

All three of the input hierarchies function in a similar manner. Each relies on the basic tree structure of UI objects to build a path from the topmost system UI object (GenSystem) down to a leaf node that has the hierarchical properties (e.g., the target object).

Each composite object can have exactly one child with each hierarchical property. The composite determines which of its children currently has the exclusive of the hierarchy; for example, a composite with ten children may designate the fourth child as the Target node at its level. If that child is a composite with its own children, it will select one of them as the Target node at the next level.

A message sent to any object in the path will be passed down the tree until it gets to the active target leaf, the object that actually has the system's target exclusive.

A special feature of the hierarchical structure is that inactive paths may be maintained. The inactive path is stored in the objects themselves. The topmost object in the tree, however, has decided that object on the right has the target exclusive; if later the top object decides the object on the left has the exclusive, the target will automatically be given to the leaf node specified without actually changing the object tree (just the pointer stored in the top node).

Note that the other hierarchies operate in a similar manner. The behavior described is not unique to the target hierarchy.

Special Terminology

Before reading the in-depth sections on the individual hierarchies, you should know how several terms are defined and used. These terms are listed below.

Node
Any composite in the object tree that can be part of a path for one of the hierarchies.
Exclusive
The active child of a composite node in the hierarchy is said to have the "exclusive." Only one child of any composite may have the exclusive at that level.
Level
A group of siblings, all of which can potentially be part of a hierarchical path. All the nodes in a given level will have the same parent. Only one node at each level may have the exclusive for that level.
Grab
When an object grabs a hierarchy (e.g., "grabs the focus"), it notifies its parent that it wants to gain that hierarchy's exclusive for its level. Any other node at the same level will be forced to give up the exclusive.
Active Path
The path of exclusive nodes from the top object to the leaf having the active exclusive. Only objects which have a complete active path to the top node can exhibit the property of the hierarchy (e.g., a text object in a minimized application can not have the focus because its primary window does not have the focus exclusive).
Inactive Path
Any path of exclusive nodes that is broken from the top node. If the topmost node of an inactive path is given the exclusive at its level, the entire inactive path will automatically become the active path.

Modifying the Active Path

Each of the three hierarchies has messages understood by MetaClass that alter the hierarchy's active path. The Specific UI takes care of most of the modifications to the active path, however, so simple applications will not need to bother with it.

Any node can "grab" a hierarchical exclusive by sending the grab message to its parent. (Outside agents can alter the path by sending the grab message to the child's parent on behalf of the child.) The grab forces any other node on the child's level to give up the exclusive. Any node can release the exclusive with the appropriate message (typically done when forced to release because of another node's grab).

Whenever a node gains the exclusive of a hierarchy, it will be notified via a message. This is to notify objects which already had their level's exclusive that they now have the active hierarchical exclusive. Similarly, if another node grabs the exclusive at any level equal or above an exclusive node, that node will be notified that it has lost the exclusive at its level. See the discussions of the individual hierarchies for more detail on the messages used.

Sending Classed Messages

Any object can easily send messages to the object having the active exclusive of a given hierarchy using MSG_META_SEND_CLASSED_EVENT . This message passes a recorded event (message) to the first object of a specified class encountered within the hierarchy. (If the destination should be a leaf, no class should be specified.)

To pass events to the active member of a specific hierarchy, you should use a TravelOption with MSG_META_SEND_CLASSED_EVENT . TravelOption is a type which describes the destination for the specified event. There are three types of TravelOption for each hierarchy:

  • Direct
    The "direct" travel options are TO_FOCUS, TO_TARGET, and TO_MODEL. These send the classed event down the hierarchy's active path, treating the recipient of MSG_META_SEND_CLASSED_EVENT as the active path's top node.
  • Application-direct
    The "application-direct" travel options are TO_APP_FOCUS, TO_APP_TARGET, and TO_APP_MODEL. These send the classed event first to the GenApplication object associated with the recipient of MSG_META_SEND_CLASSED_EVENT ; the GenApplication object is taken as the top node, and the event is passed down the active path until it is handled.
  • System-direct
    The "system-direct" travel options are TO_SYS_FOCUS, TO_SYS_TARGET, and TO_SYS_MODEL. These send the classed event to the GenSystem object, the topmost object in the entire UI object tree. The classed event will be passed down the active path, using the GenSystem object as the path's top node.

There exist additional TravelOption types that can be used to direct messages to generically-defined locations. For a complete list of these types, see the GEOS Programming chapter.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 The Three Hierarchies | Next: 5.3 Using Focus


Using Focus

Input: 5.3 Input Hierarchies: Using Focus

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Common Hierarchy Basics | Next: 5.4 Using Target

The specific UI handles most of the manipulation of the focus hierarchy. Most specific UIs interpret keyboard and mouse events and know when to switch the focus from one object to another. For example, in OSF/Motif, clicking on a window will switch the focus to that window from the previous focus window. Before the mouse is clicked, however, the focus exclusive of that window will not change. In other specific UIs which incorporate real-estate cursor behavior, merely moving the cursor over a window will transfer the focus to that window.

By default, an application will come up with certain objects having the focus exclusive within their focus levels. This allows the user to immediately begin typing or operating on data. For an object to grab the default exclusive, it must first be focusable. A focusable object can be any child of a valid focus node. Valid nodes for the focus hierarchy are:

GenSystem
GenField
GenApplication
GenPrimary
GenDisplayControl
GenDisplay
GenView (with GVA_FOCUSABLE bit set)
GenInteraction (independently displayable)
VisContent

Note that a GenView must have the GVA_FOCUSABLE bit set in its GVI_ attrs field. If an object you need to act as a focus node is not in the above list (for example, a custom gadget), you must subclass the object and add instance fields and message handlers to handle focus functionality.

If an independently displayable object is focusable, the focus will be given to the object which is visually "on top" of all other objects. For all other objects within a focus exclusive level, the focus will be granted to the object with HINT_DEFAULT_FOCUS in its instance data. If no objects have this hint at a particular focus level, then the focus will be granted to the first focusable child.

Grabbing and Releasing the Focus

To grab the focus exclusive in its level, a node should send itself MSG_META_GRAB_FOCUS_EXCL . The default handler for this message grabs the focus exclusive for the object from its parent and forces any other object on the caller's level to give the focus up. The active exclusive will not be granted unless the caller is part of the active path after the grab.

When another node grabs the focus exclusive, the node currently having the exclusive on that level must give it up. MSG_META_RELEASE_FOCUS_EXCL releases the focus exclusive for the other node.

Note that these messages only modify the exclusive at a single level. Therefore, they only affect the focus optr of the parent's node; further up the focus hierarchy there is no effect. Changing the focus exclusive of an object will only change the direction of the focus path if all parents of the new exclusive are also focus exclusives.

MSG_META_GET_FOCUS_EXCL may be sent to any focusable composite node to get the optr of the node's child having the exclusive. This message may be used even on nodes in the inactive path.

Gaining and Losing the Focus

When an object gains the active focus exclusive and is a node in the active path, it receives MSG_META_GAINED_FOCUS_EXCL . This indicates to the object that it will receive all keyboard input as the active keyboard object. At some point later, when the object has lost the focus exclusive, it will receive MSG_META_LOST_FOCUS_EXCL .

Sending Classed Events to the Focus

Frequently, you may wish to send messages to objects in the active Focus path. The easiest way to deliver a message to an object in the focus hierarchy is to use MSG_META_SEND_CLASSED_EVENT with the TravelOption TO_APP_FOCUS. To send a message to the leaf node of the active path, send this message with a null class. The message will be sent down the hierarchy until it reaches the leaf object, where it will be processed. (See Delivering Messages to the Focus .)

This approach is desirable over using MSG_META_GET_FOCUS_EXCL to return an optr for later use, as the system may have corrupted the optr in the meantime.

Code Display 11-2 Delivering Messages to the Focus

@method MyProcessClass, MSG_SEND_MESSAGE_TO_FOCUS_INTERACTION {
    EventHandle event;
    /* The classed event is recorded to be handled by the first object of class
     * GenInteractionClass. */
    event = @record GenInteractionClass::MSG_TEST_MESSAGE();
    /* This message is then sent to the GenApplication object, from which it
     * will travel down the focus hierarchy until handled at the
     * GenInteraction level. */
    @call MyApplication::MSG_META_SEND_CLASSED_EVENT(event, TO_FOCUS);
}
/* You may also declare an object with the TravelOption TO_APP_FOCUS. This will
 * send the message to the application object and then down the focus hierarchy. */
@object GenTriggerClass MyFocusTrigger = {
    GI_visMoniker = "Send test message to the focus";
    GTI_actionMsg = MSG_TEST_MESSAGE;
    GTI_destination = (TO_APP_FOCUS);
    ATTR_GEN_DESTINATION_CLASS = { (ClassStruc *)&GenInteractionClass };
}
/* To send a message to the focus leaf node, use a null class. */
@method MyProcessClass, MSG_SEND_TEST_TO_FOCUS_LEAF {
    EventHandle event;
    /* A classed event with the class of null is recorded. */
    event = @record null::MSG_TEST_MESSAGE();
    /* This event is sent to the GenApplication, where it will travel down the
     * focus hierarchy to the leaf object, where it will be handled. */
    @call MyApplication::MSG_META_SEND_CLASSED_EVENT(event, TO_FOCUS);
}
/* As an alternative, an object could be set to deliver its message to the
 * leaf focus node of the application. */
@object GenTriggerClass MyFocusLeafTrigger = {
    GI_visMoniker = "Send test message to the focus leaf object";
    GTI_actionMsg = MSG_TEST_MESSAGE;
    GTI_destination = (TO_APP_FOCUS);
    /* An ATTR_GEN_DESTINATION_CLASS of zero specifies a null class. */
    ATTR_GEN_DESTINATION_CLASS = 0;
}
| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Common Hierarchy Basics | Next: 5.4 Using Target


Using Target

Input: 5.4 Input Hierarchies: Using Target

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Using Focus | Next: 5.5 Using Model

The specific UI handles most of the manipulation of the target hierarchy. Most specific UIs interpret keyboard and mouse events and know when to switch the target from one object to another. For example, in OSF/Motif, clicking on a window will switch the target to that window from the previous target window. Before the mouse is clicked, however, the target exclusive of that window will not change. In other specific UIs which incorporate real-estate cursor behavior, merely moving the cursor over a window will transfer the target to that window.

By default, an application will come up with certain objects having the target exclusive within their target levels. This allows the user to immediately begin typing or operating on data. For an object to grab the default exclusive, it must first be targetable. A targetable object can be any child of a valid target node. Valid nodes for the target hierarchy are:

GenSystem
GenField
GenApplication
GenPrimary
GenDisplayControl
GenDisplay
GenView (with GVA_TARGETABLE bit set)
GenInteraction (independently displayable)
VisContent

In addition, however, a generic object must be set GA_TARGETABLE in its GI_attrs field to become a valid target object. This bit is set by default for the following classes:

GenField
GenApplication
GenPrimary
GenDisplayControl
GenDisplay
GenView

If you wish a generic object to be targetable and it does not appear in this list, set it GA_TARGETABLE. If the object does not by default appear as a valid node in the Target hierarchy (such as for a custom gadget), you must subclass the object and add target instance fields and message handlers.

For all targetable objects within a target exclusive level, the target will be granted to the object with HINT_DEFAULT_TARGET in its instance data. If no object has this hint at a particular target level, then the target will be granted to the first targetable object at that level.

Grabbing and Releasing the Target

To grab the target exclusive in its level, a node should send itself MSG_META_GRAB_TARGET_EXCL . The default handler for this message grabs the target exclusive for the object from its parent and forces any other object on the caller's level to give the target up. The active exclusive will not be granted unless the caller is part of the active path after the grab.

When another node grabs the target exclusive, the node currently having the exclusive on that level must give it up. MSG_META_RELEASE_TARGET_EXCL releases the target exclusive for the other node.

Note that these messages only modify the exclusive at a single level. Therefore, they only affect the target optr of the parent's node; further up the target hierarchy there is no effect. Changing the target exclusive of an object will only change the direction of the target path if all parents of the new exclusive are also target exclusives.

MSG_META_GET_TARGET_EXCL may be sent to any targetable composite node to get the optr of the node's child having the exclusive. This message may be used even on nodes in the inactive path.

Gaining and Losing the Target

When an object gains the active target exclusive and is a node in the active path, it receives MSG_META_GAINED_TARGET_EXCL . This indicates to the object that it will receive all keyboard input as the active keyboard object. At some point later, when the object has lost the target exclusive, it will receive MSG_META_LOST_TARGET_EXCL .

Sending Classed Events to the Target

Frequently, you may wish to send messages to objects in the active Target path. The easiest way to deliver a message to an object in the target hierarchy is to use MSG_META_SEND_CLASSED_EVENT with the TravelOption TO_TARGET. To send a message to the leaf node of the active path, send this message with a null class. The message will be sent down the hierarchy until it reaches the leaf object, where it will be processed. (See Delivering Messages to the Target .)

This approach is desirable over using MSG_META_GET_TARGET_EXCL to return an optr for later use, as the system may have corrupted the optr in the meantime.

Code Display 11-3 Delivering Messages to the Target

@method MyProcessClass, MSG_SEND_MESSAGE_TO_TARGET_INTERACTION {
    EventHandle event;
    /* The classed event is recorded to be handled by the first object of class
     * GenInteractionClass. */
    event = @record GenTextClass::MSG_TEST_MESSAGE();
    /* This message is then sent to the GenApplication object, from which it
     * will travel down the target hierarchy until handled at the
     * GenInteraction level. */
    @call MyApplication::MSG_META_SEND_CLASSED_EVENT(event, TO_TARGET);
}
/* You may also declare an object with the TravelOption TO_APP_TARGET. This will
 * send the message to the application object and then down the target hierarchy. */
@object GenTriggerClass MyTargetTrigger = {
    GI_visMoniker = "Send test message to the target";
    GTI_actionMsg = MSG_TEST_MESSAGE;
    GTI_destination = (TO_APP_TARGET);
    ATTR_GEN_DESTINATION_CLASS = { (ClassStruc *)&GenTextClass };
}
/* To send a message to the target leaf node, use a null class. */
@method MyProcessClass, MSG_SEND_TEST_TO_TARGET_LEAF {
    EventHandle event;
    /* A classed event with the class of null is recorded. */
    event = @record null::MSG_TEST_MESSAGE();
    /* This event is sent to the GenApplication, where it will travel down the
     * target hierarchy to the leaf object, where it will be handled. */
    @call MyApplication::MSG_META_SEND_CLASSED_EVENT(event, TO_TARGET);
}
/* As an alternative, an object could be set to deliver its message to the
 * leaf target node of the application. */
@object GenTriggerClass MyTargetLeafTrigger = {
    GI_visMoniker = "Send test message to the target leaf object";
    GTI_actionMsg = MSG_TEST_MESSAGE;
    GTI_destination = (TO_APP_TARGET);
    /* An ATTR_GEN_DESTINATION_CLASS of zero specifies a null class. */
    ATTR_GEN_DESTINATION_CLASS = 0;
}
| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Using Focus | Next: 5.5 Using Model


Using Model

Input: 5.5 Input Hierarchies: Using Model

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Using Target | Next: 5.6 Extending the Hierarchies

In some cases, you may need to send or receive information from some object other than the focus or target. The Model hierarchy is provided as an alternative. In most cases, the model provides a means of pointing to data selections other than that pointed to by the Target hierarchy. Only use it if the target hierarchy cannot serve your needs.

The model of an application is used to identify objects that represent the actual data that can be selected by the user. Typically, these selections are either secondary selections or are not visible. The selections are frequently operated on with the use of menus, dialog boxes, or other UI gadgetry. Only one object at each model level may have the model "exclusive," and for an object to "have the Model" of an application, that object must have a continuous path of model exclusives up to the application object.

Only GenSystem, GenApplication, and the document control objects can be directly used in the Model hierarchy without any modifications. To use other objects in the Model hierarchy, you must first subclass those objects' classes and add an instance field and several message handlers pertaining to the Model hierarchy. Any object which is a valid target node can be subclassed in this manner.

By default, an application will come up with certain objects having the model exclusive within their model levels. This allows the user to immediately begin operating on data. For an object to grab the default exclusive, it must first be modelable. Only objects which are modelable may have the model exclusive. A modelable object can be any child of a valid model node. Valid nodes for the Model hierarchy are the same as for the Target hierarchy. However, a document control object must also be set GDCA_MODELABLE to grab the model exclusive. Remember that just because an object is a valid node does not mean it can exhibit the behavior of the Model hierarchy; you must subclass most objects for this to occur.

For objects within a model level, the model will be granted first to any object with HINT_DEFAULT_MODEL in its instance data. If no object has this hint at a particular model level, then the model will be granted to the first modelable object at that level.

The easiest way to deliver a message to an object in the active Model hierarchy is to use MSG_META_SEND_CLASSED_EVENT with the TravelOption TO_MODEL. To send a message to the leaf node in the Model hierarchy, send this message with a class of null. The message will be sent down the hierarchy until it reaches the leaf object, where it will be processed.

This approach is desirable over using MSG_META_GET_MODEL_EXCL to return an optr for later use as the system may have corrupted the optr in the meantime.

Changing the Model Exclusive

To modify the Model hierarchy, use MSG_META_GRAB_MODEL_EXCL . To remove the model exclusive from an object without setting it to another object, use MSG_META_RELEASE_MODEL_EXCL . Note that these messages only modify the model exclusive of a single object. Therefore, they only affect the model pointer of the parent; further up the Model hierarchy there is no effect. Changing the model exclusive of an object will only change the direction of the model path if all ancestors of the new model exclusive are also model exclusives.

To check which model exclusive a composite points to, send it a MSG_META_GET_MODEL_EXCL . This message will return the optr of the model child. The returned object may not have the model properties, though, for the reasons above. Never send a message directly to the object returned by this method as it may have been changed in the meantime. Use MSG_META_SEND_CLASSED_EVENT with the TravelOption TO_MODEL instead.

Intercepting the Model

Your application may also wish to be notified when an object either gains or loses its model properties. Whenever an object in GEOS gains the model of the application--not just the model exclusive--it sends a MSG_META_GAINED_MODEL_EXCL to that object. Similarly, if an object loses the model of an application--not just the model exclusive--the system sends a MSG_META_LOST_MODEL_EXCL to that object.

If your application needs to know when an object either gains or loses its model properties, intercept MSG_META_GAINED_MODEL_EXCL and MSG_META_LOST_MODEL_EXCL . Be sure to pass these messages on to the superclass, however, to perform necessary default functionality. Within your message handlers for these messages, you may add whatever additional behavior you require.

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Using Target | Next: 5.6 Extending the Hierarchies


Extending the Hierarchies

Input: 5.6 Input Hierarchies: Extending the Hierarchies

| Up: GEOS SDK TechDocs | Up | Prev: 5.5 Using Model

You may also extend or modify the hierarchies to add other objects that are not active nodes by default. If custom objects wish to use the hierarchies, you will need to add instance data and message handlers for their classes. The following steps must be taken to add a new node in a hierarchy:

  1. Add a hierarchical instance field of type HierarchicalGrab .
  2. Add a handler for MSG_META_MUP_ALTER_FTVMC_EXCL . This method alters the custom instance field for the grab and release messages for all hierarchies. You must call the superclass in your handler.
  3. Add methods to handle the GAINED and LOST messages pertaining to the particular hierarchy you are extending. These methods should call FlowUpdateHierarchicalGrab() . You can also add behavior exhibited when the object gains or loses the exclusive. Depending on what you are subclassing, your default behavior may already take care of this. For instance subclasses of VisClass automatically handle losing and gaining the target exclusive.
  4. Add a method to handle the GET message for the particular hierarchy you are extending. This method returns the optr of the object set in the new instance field. The method should return true to signal that it is capable of gaining the exclusive.
  5. Add the capability to handle the MSG_META_SEND_CLASSED_EVENT with the TravelOption of the particular hierarchy you are extending. The way you handle this message depends on the hierarchy affected, as discussed below.

TO_FOCUS and TO_TARGET requests should only travel down hierarchies of the same name. If no further travel is possible, the method should pass the event on to the superclass for handling. TO_MODEL requests should only travel down the Model hierarchy if it exists and the next optr is non-null.

| Up: GEOS SDK TechDocs | Up | Prev: 5.5 Using Model


MetaClass

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Special Messages
| One-page Version of this Chapter for Easy Printing

MetaClass is the ancestor class of every GEOS object. MetaClass is the location of basic messages and their handlers. Basic functionality for all objects--instantiation, initialization, detach, and destruction--is implemented within this class.

The only instance data field defined for MetaClass is the object's class pointer (the field is named MI_base ). MetaClass has no other inherent data fields. You will never need to access the class pointer directly.

MetaClass also serves as a sort of placeholder for certain messages. Application writers familiar with mouse input, for instance, know that MSG_META_START_SELECT signals a mouse click. This message is actually defined at the MetaClass level, though MetaClass

1 Special Messages
2 Utility Messages
2.1 Object Creation and Destruction
2.2 Class Messages
2.3 Object Management Messages
2.4 User Interface Utility Meta Messages
2.5 Event Messages
2.6 Variable Data Messages
2.7 Notification Messages
2.8 Options Messages
2.9 Suspending and Unsuspending
2.10 Help Files
3 Exported Message Ranges
3.1 Window Messages
3.2 Input Messages
3.3 UI Messages
3.4 Standard GCN Messages
3.5 IACP Meta Messages

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Special Messages
| One-page Version of this Chapter for Easy Printing


Special Messages

MetaClass: 1 Special Messages

| Up: GEOS SDK TechDocs | Up | Prev: MetaClass | Next: 2 Utility Messages
MSG_META_NULL, MSG_META_DUMMY

The following two messages are mainly place holders. They ensure that no message will have the value zero or one. (When a message is called, a value of zero equates to null.) These are not used by applications or objects in general.

MSG_META_NULL
void	MSG_META_NULL();

This message has no handler and is unused. It essentially ensures that no other message will ever have the value zero.

Interception: Don't.

MSG_META_DUMMY
void	MSG_META_DUMMY();

This message has no handler. You should not subclass this message to provide one. Certain object mechanisms, such as the resolution of a variant class, are activated by the object's receipt of MSG_META_DUMMY .

Interception: Don't.

| Up: GEOS SDK TechDocs | Up | Prev: MetaClass | Next: 2 Utility Messages


Utility Messages

MetaClass: 2 Utility Messages

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Special Messages | Next: 3 Exported Message Ranges

MetaClass also provides a number of other messages that are used throughout the system. These messages have been separated into loosely-defined categories and listed in the sections below.

Object Creation and Destruction

Class Messages

Object Management Messages

User Interface Utility Meta Messages

Event Messages

Variable Data Messages

Notification Messages

Options Messages

Suspending and Unsuspending

Help Files

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 Special Messages | Next: 3 Exported Message Ranges


Object Creation and Destruction

MetaClass: 2.1 Utility Messages: Object Creation and Destruction

| Up: GEOS SDK TechDocs | Up | Prev: 2 Utility Messages | Next: 2.2 Class Messages

These messages handle creation, destruction, and initialization of all objects. The function and use of many of these messages are given in .

MSG_META_INITIALIZE

void	MSG_META_INITIALIZE();

Every object class should provide a handler for this message which should call the superclass and then perform any initialization of the instance data required. This message is not delivered to statically declared objects because they are already initialized.

Note that GenClass and VisClass have a default handler that sets up the Gen and Vis parts automatically.

This message is not delivered to objects whose only master part is MetaClass itself.

Source: Object system itself, often in the middle of attempting to deliver another message to an object that hasn't yet been initialized.

Destination: Object whose instance data is not yet initialized.

Parameters: None.

Return: Nothing.

Interception: Any class wishing to have default instance data values other than all zeros should intercept this message to fill in the initial values for its instance data. For classes other than master classes, standard procedure is to call the superclass first, then perform any additional instance data initialization necessary. Master classes should not call the superclass, as MSG_META_INITIALIZE is unique among messages in that it is sent only to classes within the particular master group that needs to be initialized. Handlers of MSG_META_INITIALIZE should limit their activities to just stuffing instance data--specifically, object messaging is not allowed (though scanning vardata is OK). Object classes that inherit instance data (all but MetaClass ) should call MSG_META_INITIALIZE on their superclass to initialize that portion of their instance data. In addition, they must initialize their own portion of the instance data (start by assuming it's all zeros). The order won't matter, so long as the handler doesn't depend on the inherited instance data having any particular value. When in doubt, call the superclass first.

MSG_META_ATTACH

void 	MSG_META_ATTACH();

This message is used for two different purposes: It can be sent to any geode that has a process aspect when the geode is first loaded. It can also be sent in the object world to notify objects on an "active list" that the application has been brought back up from a state file. As the method is used for different purposes, the data passed varies based on usage. Because of this difference in parameters, normally C applications will use one of the aliases for this message ( MSG_META_ATTACH_PROCESS , MSG_META_ATTACH_THREAD , MSG_META_ATTACH_GENPROCESSCLASS , MSG_META_ATTACH_OBJECT, and MSG_META_ATTACH_GENAPPLICATION , each described below.)

MSG_META_ATTACH_PROCESS

@alias (MSG_META_ATTACH)
 void 	MSG_META_ATTACH_PROCESS(
        word 	value1,
        word 	value2);

This message is sent to any geode which has a process when the geode is first loaded. By default, the handler for this message does nothing.

Source: GeodeLoad() kernel routine.

Destination: Newly created Process object (but not GenProcess object).

Parameters: value1 Upper half of GeodeLoad() appInfo argument.

value2
Lower half of GeodeLoad() appInfo argument.

Return: Nothing.

Interception: No default handling provided, so if you are spawning an extra process and that process needs to do some initialization, then intercept this message.

MSG_META_ATTACH_GENPROCESSCLASS

@alias (MSG_META_ATTACH)
 void 	MSG_META_ATTACH_GENPROCESSCLASS(
        MemHandle appLaunchBlock);

This message is sent to the GenProcess object when the geode is first loaded. By default, the handler for this message calls MSG_PROCESS_STARTUP_UI_THREAD , which checks to see if there are any resources of the application which are marked as "ui-object" (they are marked this way in the .gp file), that is, to be run by a UI thread. If so, it then calls MSG_PROCESS_CREATE_UI_THREAD to create that thread, then marks the "ui-object" blocks as run by that thread.

The handler next calls one of the following messages:

MSG_GEN_PROCESS_OPEN_APPLICATION
For applications which are being started up regularly (that is, not restoring from a state file) and will appear on screen.
MSG_GEN_PROCESS_OPEN_ENGINE
For those applications that will operate in engine mode (i.e. non-visual).
MSG_GEN_PROCESS_RESTORE_FROM_STATE
For applications which are restoring from state. This is the case for applications that were running at the previous shutdown.

Source: GeodeLoad() kernel routine.

Destination: Newly created GenProcessClass (or subclass thereof) object.

Parameters: appLaunchBlock Block handle to block of structure AppLaunchBlock .

Return: Nothing.

Note that the passed AppLaunchBlock is destroyed.

Interception: No default handling provided, so if you are spawning an extra process and that process needs to do some initialization, then intercept this message.

MSG_META_ATTACH_GENAPPLICATION

@alias (MSG_META_ATTACH)
 void 	MSG_META_ATTACH_PROCESS(
        MemHandle 	bh1,
        MemHandle 	bh2);

This message is sent to the GenApplication object by GenProcessClass when the application starts up (either for the first time, or when being restored from a state file).

Source: GenProcess object.

Destination: GenApplication object.

Parameters: bh1 Block handle to block containing AppLaunchBlock parameters.

bh2
Extra state block from state file, or NULL if none. This is the same block as returned from MSG_GEN_PROCESS_CLOSE_APPLICATION in some previous detach operation.

Return: Nothing.

Note that the AppLaunchBlock is destroyed.

Interception: Not generally required, since the default handler broadcasts the message out to everything on the application's active lists. This act causes the interface for the application to come up on screen.

MSG_META_ATTACH_OBJECT

@alias (MSG_META_ATTACH)
 void 	MSG_META_ATTACH_OBJECT(
        word	 	flags,
        MemHandle 		appLaunchBlock,
        MemHandle 		extraState);

This message is sent to any object on the GenApplication object's active lists, or on one of those object's active lists. Note that this will not happen until the GenApplication is set usable by the GenProcess object.

Source: GenApplicationClass object.

Destination: Any object.

Parameters: flags Flags providing state information.

appLaunchBlock
Handle of AppLaunchBlock , or NULL if none.
extraState
Handle of extra state block, or NULL if none. This is the same block as returned from MSG_GEN_PROCESS_CLOSE_APPLICATION , in some previous detach.

Return: Nothing.

Interception: Standard UI objects defined as needing to be placed on an active list will intercept this message to do whatever it is that they needed to do when the application is first loaded. Objects intercepting this message should call the superclass, in case it expects to receive this notification itself.

Warnings: If the specific UI uses this mechanism, then the GenProcessClass will have already destroyed the AppLaunchBlock and extra state block by the time the MSG_META_ATTACH is sent to objects on its active list.

MSG_META_ATTACH_THREAD

@alias (MSG_META_ATTACH)
 void 	MSG_META_ATTACH_THREAD();

This message is sent to any thread spawned by MSG_PROCESS_CREATE_EVENT_THREAD .

Source: Kernel.

Destination: Newly created thread, specifically the class designated to handle the thread's messages (a subclass of ProcessClass ).

Parameters: None.

Return: Nothing.

Interception: No default handling provided, so if you are spawning an extra process and that process needs to do some initialization, then intercept this message.

MSG_META_APP_STARTUP

void	MSG_META_APP_STARTUP(
        MemHandle		appLaunchBlock);

This message is related to MSG_META_ATTACH; the message is sent by the generic UI to the GenApplication object before it sends MSG_META_ATTACH to it. MSG_META_ATTACH is only sent when the application is becoming available to the user; if an application should be opened as a server without presenting any UI to the user, MSG_META_APP_STARTUP will be the only message sent to the application object upon start-up.

The default handler for this message will pass it on to all members of the MGCNLT_APP_STARTUP list.

Source: GenProcessClass ; forwarded by GenApplicationClass to other objects. This message is sent upon application start-up before the UI for an application has been attached.

Destination: Any object that needs to be notified when the application is launched, regardless of whether the user will be interacting with the application.

Parameters: appLaunchBlock Handle of an AppLaunchBlock .

Return: The AppLaunchBlock is preserved.

Interception: Usually intercepted by any object on the MGCNLT_APP_STARTUP list.

MSG_META_UPDATE_WINDOW

void	MSG_META_UPDATE_WINDOW(
        UpdateWindowFlags		updateFlags,
        VisUpdateMode		updateMode);

This message is sent as part of the system's window update mechanism. Typically, this message is sent to windowed objects on the GAGNLT_WINDOWS list when the GenApplication object becomes GS_USABLE.

The message passes a bitfield of UpdateWindowFlags . These flags determine the type of action prompting the window update.

UWF_ATTACHING
If set, the message is being sent because the application is attaching.
UWF_DETACHING
If set, the message is being sent because the application is detaching.
UWF_RESTORING_FROM_STATE
If set, the application is restoring from state. This flag will only be set if UWF_ATTACHING is also set.
UWF_FROM_WINDOWS_LIST
If set, the message is being sent because the object is on the GAGCNLT_WINDOWS list, and not because it was later built on demand. This flag will only be set if UWF_ATTACHING is also set.

Source: Window update mechanism.

Destination: Entries on the Application's GAGCNLT_WINDOWS list.

Parameters: updateFlags UpdateWindowFlags .

updateMode
VisUpdateMode .

Interception: Generally not intercepted.

MSG_META_DETACH

void	MSG_META_DETACH(
        word	callerID,
        optr	caller);

This message severs the links between an object and the rest of the system. The exact way this is handled depends on the object being detached. For full information on detaching objects, see the GEOS Programming chapter.

The "state" of the object is left intact, in case an image of the object needs to be saved away in a state file for later re-creation. MSG_META_DETACH sent to an application's process will start the process by which it is detached from the system, and then exited. MSG_META_DETACH is asynchronous, in that it need not complete its job immediately upon being called. Rather, it may take as much time, invoking and waiting for the completion of subsidiary detaches (say of child objects needing to perform special actions to detach, or of threads created earlier), before it responds with MSG_META_ACK to let its caller know that the detach has completed.

Source: Kernel, other objects relaying detach message.

Destination: GenProcess, GenApplication, objects on active lists.

Parameters: callerID A number of your choosing. This number will be passed back to caller in MSG_META_ACK in the callID parameter.

caller
Object which should be sent a MSG_META_ACK when detaching object has finished.

Return: Nothing.

Interception: Intercepted as a means of finding out that the application is shutting down. Call the superclass in case it needs such notification.
If you create additional threads, or object classes which need to be notified when the application is about to be exited, you may need to extend the detach mechanism by intercepting MSG_META_DETACH in a subclass of an object already receiving that message, such as GenApplication, GenControl, GenInteraction dialogs, etc. You must make sure that all objects you've sent MSG_META_DETACH to have responded with a MSG_META_ACK before your object can reply with MSG_META_ACK . Remember that your superclass may be sending MSG_META_DETACH . The kernel provides some default behavior in MetaClass , and some utility routines, to make this a simpler task. The default handler for MSG_META_DETACH , for instance, at a leaf object (one which doesn't propagate the MSG_META_DETACH ), performs the required response (sending a MSG_META_ACK ). Thus, leaf objects can just intercept MSG_META_DETACH for notification purposes, then call the superclass, and worry no more. The utility routines ObjInitDetach() and ObjEnableDetach() work in conjunction with a default MSG_META_ACK handler in MetaClass to keep track of how many outstanding acknowledgments are being waited for, and call MSG_META_DETACH_COMPLETE on your object once all acknowledgments have returned (the count reaches zero). The default handler for MSG_META_DETACH_COMPLETE then generates the acknowledgment response required of your object to complete its detach. You may optionally call the superclass before sending the detach message to your children and dependents, depending on which order you want things to detach in. The call to the superclass must happen between the ObjInitDetach() and ObjEnableDetach() , however.

MSG_META_DETACH_COMPLETE

void	MSG_META_DETACH_COMPLETE();

This message is sent to an object being detached when all of its children and active participants have acknowledged the detach. For full information on detaching objects, see the GEOS Programming chapter.

MSG_META_DETACH_COMPLETE is sent to the object which called ObjInitDetach() . This will happen when as many acknowledgments have been received as ObjIncDetach() was called, and ObjEnableDetach() was called. The MetaClass handler for this message sends MSG_META_ACK to the OD passed to the ObjInitDetach() call. This message is provided so that an object will know when all of its children have detached. Note that this message is received only if ObjInitDetach() has been called for this object. Note also that your superclass may call ObjInitDetach() without your knowing.

Source: MetaClass handler for MSG_META_ACK , if detach count has dropped to zero (i.e. no outstanding requests), for objects that are detach nodes only (make use of ObjInitDetach() or ObjEnableDetach() ).

Destination: Self.

Parameters: None.

Return: Nothing.

Interception: This is a handy message to intercept when using the ObjInitDetach() mechanism and need to know when all objects asked to detach have responded. Calling the superclass at this point in time will cause an MSG_META_ACK to go back to whatever object sent the MSG_META_DETACH to this object originally. There is no requirement to call the superclass at this time, and in fact this is a way to prolong the detach cycle for this object-- by simply starting up another ObjInitDetach() sequence, for instance.

MSG_META_DETACH_ABORT

void	MSG_META_DETACH_ABORT();

This message causes a detach to be aborted. This can cause some very complex synchronization problems and should not be used lightly. You will find very little call to use it.

Source: Renegade object on active list, after having received MSG_META_DETACH , as an alternative to replying with MSG_META_ACK .

Destination: The optr passed in MSG_META_DETACH .

Interception: Handled by GenField to deal with applications that refuse to die, and GenSystem for Fields that have problem applications. Other than that, any detach node wishing to provide this service will have to figure out a way to do it itself.

Parameters: None.

Return: Nothing.

MSG_META_APP_SHUTDOWN

void	MSG_META_APP_SHUTDOWN(
        word		callerID,
        optr		ackOD);

This message is the complement to MSG_META_APP_STARTUP. This message is sent to objects on the MGCNLT_APP_STARTUP list before an application exits but after the UI for the application is detached. Essentially, it operates in the same manner as MSG_META_DETACH except that the receiving object sends MSG_META_SHUTDOWN_ACK when its shutdown is complete.

Source: Sent by GenProcessClass after detaching the UI but before exiting the application; if the UI was never attached (i.e. it handled MSG_META_APP_STARTUP but not MSG_META_ATTACH) the UI will obviously not be detached.

Destination: Any object that needs to be notified when the application is about to exit.

Parameters: callerID Word of data for caller's use.

ackOD
Optr of object to be sent MSG_META_SHUTDOWN_ACK.

Interception: Usually intercepted by objects on the MGCNLT_APP_STARTUP list.

MSG_META_SHUTDOWN_COMPLETE

void	MSG_META_SHUTDOWN_COMPLETE();

This message is sent to the object that initiated the detach sequence after it has received MSG_META_SHUTDOWN_ACK for each ObjIncDetach() that was previously called. This message is only sent if ObjInitDetach() was previously called passing the message MSG_META_APP_SHUTDOWN.

The default handler for this message sends MSG_META_SHUTDOWN_ACK to the object passed in the original ObjInitDetach() call.

Source: MetaClass handler for MSG_META_SHUTDOWN_ACK if detach count reaches zero (i.e. no outstanding requests), for objects that are shutdown nodes only (i.e. make use of ObjInitDetach() .)

Destination: The object sends this message to itself.

Interception: Intercept if you are using the ObjInitDetach() mechanism and need to be notified when all objects have been notified of the detach.

MSG_META_SHUTDOWN_ACK

void	MSG_META_SHUTDOWN_ACK(
        word		callerID,
        optr		ackOD);

This message is sent back in response to a MSG_META_APP_SHUTDOWN. This message serves to notify the object the object has fulfilled the request.

Source: Object having received MSG_META_APP_SHUTDOWN. The default handler will dispatch MSG_META_SHUTDOWN_ACK after MetaClass has processed MSG_META_APP_SHUTDOWN. (You could, of course, intercept MSG_META_APP_SHUTDOWN and send MSG_META_SHUTDOWN_ACK yourself in your handler.)

Destination: Optr passed in MSG_META_APP_SHUTDOWN.

Parameters: callerID Data passed in MSG_META_APP_SHUTDOWN.

ackOD
Object which has completed shutting down.

Interception: MetaClass provides default handling for this message when using the ObjInitDetach() mechanism. Objects not using this mechanism will want to intercept this message if there is a need to know when the object has completed shutting down.

MSG_META_ACK

void	MSG_META_ACK(
        word	callerID,
        optr	caller);

This message acknowledges a detach message. It is sent by objects that have been notified of another object's detach. The default handler for MSG_META_DETACH simply sends MSG_META_ACK back to the object that sent the detach message.

Source: Object having received MSG_META_DETACH (default handler in MetaClass will reflexively respond to any MSG_META_DETACH with a MSG_META_ACK , though you can change this behavior either by using ObjInitDetach() or by not letting the message get to the MetaClass handler, and responding yourself with a MSG_META_ACK sometime later).

Destination: The optr passed in MSG_META_DETACH.

Interception: MetaClass provides default handling of this message, for objects using the ObjInitDetach() mechanism. Objects not using this mechanism will want to intercept this if there is a need to know when the object asked to detach earlier has completed its detach.
MSG_META_ACK is normally inherited from MetaClass which calls ObjEnableDetach() . This routine decrements the detach count, and when that count reaches zero, sends a MSG_META_DETACH_COMPLETE to the object itself.

Warnings: If you are expecting a MSG_META_ACK back from anything, make sure you are using the mechanism initiated with ObjInitDetach() yourself, or you should handle MSG_META_ACK to prevent MetaClass from assuming you are using such a mechanism.

Parameters: callerID data passed to MSG_META_ACK

caller
object which has completed detaching

Return: Nothing.

MSG_META_BLOCK_FREE

void	MSG_META_BLOCK_FREE();

This message initiates a sequence which will free an entire object block when received by any object within that block. The block will be freed when its in-use count reaches zero and the message queues for the block have been cleared.

This is a fairly low-level operation, and should be performed only after the objects in the block have been removed from any tree(s) they are attached to, and are otherwise "shut down." For generic objects, this generally means first calling MSG_GEN_SET_NOT_USABLE , then MSG_GEN_REMOVE_CHILD before using this message. For Visible objects, MSG_VIS_REMOVE will both visually shut down the visible tree, and then remove it from its parent.

Source: Unrestricted.

Destination: Any object within a block that is ready to have a low-level delete performed on it (i.e. isn't on screen, isn't linked to objects in other blocks, etc.).

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: None.

Return: Nothing.

MSG_META_OBJ_FREE

void	MSG_META_OBJ_FREE();

This message initiates a sequence which will free an object. The object will be freed after its message queues have been flushed.

This is a fairly low-level operation, and should be performed only after the object has been removed from any tree it is attached to and is otherwise "shut down." Consider using MSG_GEN_DESTROY for generic objects, MSG_VIS_DESTROY for visible ones.

Source: Unrestricted.

Destination: Any object within a block that is ready to have a low-level delete performed on it (i.e. isn't on screen, isn't linked to objects in other blocks, etc.).

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: None.

Return: Nothing.

MSG_META_DEC_BLOCK_REF_COUNT

void	MSG_META_DEC_BLOCK_REF_COUNT(
        MemHandle		block1,
        MemHandle		block2);

This message is a utility message to call MemDecRefCount() on one or two memory handles.

This message is useful for IACP, which initializes the reference count to the number of servers returned by IACPConnect() and records this message as the message to be returned. After each server has processed its information, the reference count will return to zero and the handles will be freed.

Source: Unrestricted.

Destination: Any object.

Parameters: block1 Handle of a block whose reference count should be decremented, or 0 if none.

block2
Handle of a block whose reference count should be decremented, or 0 if none.

Interception: Generally not intercepted.

MSG_META_OBJ_FLUSH_INPUT_QUEUE

void	MSG_META_OBJ_FLUSH_INPUT_QUEUE(
        EventHandle event,
        ObjFlushInputQueueNextStop nextStop,
        MemHandle 	objBlock);

This message clears out the message queues associated with an object. This is rarely, if ever called from within an application, and there is little call to subclass it.

This queue-flushing mechanism is used in the Window, Object, and Object Block death mechanisms. Objects that implement their own "hold up input" queues must redirect this message through that queue, so that it is flushed as well.

Source: Kernel ( WinClose() , WinSetInfo() , ObjFreeObjBlock() , MSG_META_OBJ_FREE , or MSG_META_BLOCK_FREE).

Destination: Should first be sent to the Kernel's Input Manager (See the routine ImInfoInputProcess() ). The message is then relayed first to the System Input Object (usually the GenSystemClass object), then to the Geode Input Object (usually a GenApplicationClass object), and finally to the owning process, which dispatches the passed event.

Parameters: event Event to dispatch upon conclusion of flush.

objBlock
Block Handle that flushing is being performed for (generally the handle of the destination object in the above event). This is the block from which the "OWNING GEODE", as referenced in the ObjFlushInputQueueNextStop enumerated type, is determined.
nextStop
ObjFlushInputQueueNextStop (Zero should be passed in call to first object, from there is sequenced by default MetaClass handler)

Return: Nothing.

Structures:

  typedef enum {
    OFIQNS_SYSTEM_INPUT_OBJ,
    OFIQNS_INPUT_OBJ_OF_OWNING_GEODE,
    OFIQNS_PROCESS_OF_OWNING_GEODE,
    OFIQNS_DISPATCH
} ObjFlushInputQueueNextStop;

Interception: Default MetaClass handler implements relay of message from one object to the next, and dispatches the passed event. Must be intercepted by any input-flow controlling objects (System object, VisContent) which implement "hold-up" queues that hold up input-related messages. The handlers in such cases should pipe this method through the hold-up queue as it does with the other messages, and finish up when it comes out by sending this message, with all data intact, to the superclass for continued default processing.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Utility Messages | Next: 2.2 Class Messages


Class Messages

MetaClass: 2.2 Utility Messages: Class Messages

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Object Creation and Destruction | Next: 2.3 Object Management Messages

These messages are utilities that identify the class of a particular object. You should not subclass these. Their use is shown in the GEOS Programming chapter.

MSG_META_GET_CLASS

ClassStruct * MSG_META_GET_CLASS();

This message returns a pointer to the ClassStruct structure of the recipient object's class.

Source: Unrestricted.

Destination: Any object.

Parameters: None.

Return: The object's class.

Interception: Don't.

MSG_META_IS_OBJECT_IN_CLASS

Boolean	MSG_META_IS_OBJECT_IN_CLASS(
        ClassStruct * class);

This message determines whether the recipient object is a member of a given class (or a subclass of the given class). If the return is true , the object is in the class. If false , the object is not in the class. If a variant class is encountered (when checking to see if the object is an instance of a subclass of the passed class), the object will not be grown out past that class in the search. If you want to do a complete search past variant classes, send a MSG_META_DUMMY first.

Source: Unrestricted.

Destination: Any object.

Parameters: class Class to see if object is a member of.

Return: Returns true if object is a member of the passed class (or a subclass), false otherwise.

Interception: Don't.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 Object Creation and Destruction | Next: 2.3 Object Management Messages


Object Management Messages

MetaClass: 2.3 Utility Messages: Object Management Messages

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Class Messages | Next: 2.4 User Interface Utility Meta Messages

These messages fill in and resolve an object's instance data. They should usually not be subclassed, and will be sent by applications infrequently (if ever).

MSG_META_RESOLVE_VARIANT_SUPERCLASS

ClassStruct * MSG_META_RESOLVE_VARIANT_SUPERCLASS(
        word	MasterOffset);

This message is sent by the object system when it needs to know the run-time superclass of a particular object's variant master class. The system sends this message to the object when it first attempts to deliver a message to the superclass of a variant class. The object must examine itself and determine what its superclass for that master level should be.

Source: Object system.

Destination: Any object with a variant class in its class hierarchy.

Interception: Because variant master classes tend to be strictly administrative in nature, providing useful and very generic functionality to their subclasses, all immediate children of a variant master class will need to intercept this message and return the appropriate class pointer.

Parameters: MasterOffset Master offset of the level being resolved. If you know there's a variant class above your own, you will need to examine this to determine if it is your master level whose variant is being resolved, or the one above you.

Return: Superclass to use.

MSG_META_RELOCATE

Boolean 	MSG_META_RELOCATE(
        word		vMRelocType,
        word		frame);

This message is sent by the object system to evaluate and resolve all of the object's relocatable instance data fields (pointers, optrs, etc.). Note that this only applies if the class' CLASSF_HAS_RELOC flag is set.

NOTE: The calling of this method is non-standard in that it does not pass through the class's method table. Rather, the handler address is placed after the method table and a direct call is issued. This means a relocation routine should not be bound to MSG_META_RELOCATE but should rather be bound to @reloc , which Goc understands to mean the handler is for both MSG_META_RELOCATE and MSG_META_UNRELOCATE .

Note also that relocation-by-routine happens in addition to (but before) any relocation due to the class' relocation table. To suppress relocation-by-table, you should initialize the class record with the CLASSF_HAS_RELOC flag to prevent Goc from generating a table for the class.

Source: Kernel, when loading in object block, general resources, or object blocks stored in VM file format.

Destination: Individual object needing relocations beyond what the kernel can do automatically (or that simply request for this message to be sent by having their CLASSF_HAS_RELOC bit set)

Interception: Intercepted by any class needing to perform special relocations on its instance data. Superclass should be called, in case a superclass also needs to perform this operation on its own instance data.

Parameters: vmRelocType Type giving some context to the relocation.

frame
Frame to pass to ObjRelocOrUnRelocSuper() .

Return: If an error occurred, this will return true .

Structures:

  typedef enum {
    VMRT_UNRELOCATE_BEFORE_WRITE,
    VMRT_RELOCATE_AFTER_READ,
    VMRT_RELOCATE_AFTER_WRITE,
    VMRT_RELOCATE_FROM_RESOURCE,
    VMRT_UNRELOCATE_FROM_RESOURCE
} VMRelocType;

Warnings: This method may not call LMemAlloc() , LMemReAlloc() , or LMemFree() .

MSG_META_UNRELOCATE

Boolean 	MSG_META_UNRELOCATE(
        word		vMRelocType,
        word		frame);

This message causes an object to unresolve all its relocatable instance data fields, returning them to special index values.

Source: Kernel, when loading in object block, general resources, or object blocks stored in VM file format.

Destination: Individual object needing relocations beyond what the kernel can do automatically (or that simply request for this message to be sent by having their CLASSF_HAS_RELOC bit set).

Interception: Intercepted by any class needing to perform special relocations on its instance data. Superclass should be called, in case a superclass also needs to perform this operation on its own instance data.

Parameters: vmRelocType Type giving some context to the relocation.

frame
Frame to pass to ObjRelocOrUnRelocSuper() .

Return: If an error occurred, this will return true .

Structures:

typedef enum {
    VMRT_UNRELOCATE_BEFORE_WRITE,
    VMRT_RELOCATE_AFTER_READ,
    VMRT_RELOCATE_AFTER_WRITE,
    VMRT_RELOCATE_FROM_RESOURCE,
    VMRT_UNRELOCATE_FROM_RESOURCE
} VMRelocType;

Warnings: This method may not call LMemAlloc() , LMemReAlloc() , or LMemFree() .

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 Class Messages | Next: 2.4 User Interface Utility Meta Messages


User Interface Utility Meta Messages

MetaClass: 2.4 Utility Messages: User Interface Utility Meta Messages

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Object Management Messages | Next: 2.5 Event Messages

These messages are used primarily by the User Interface. You will have very little need to subclass or send them.

MSG_META_SET_FLAGS

void 	MSG_META_SET_FLAGS(
        ChunkHandle 	objChunk,
        ObjChunkFlags 	bitsToSet,
        ObjChunkFlags 	bitsToClear);

This message sets the chunk flags for an object. The chunk flags determine how the object is handled with regard to state saving, dirty state, etc.

Source: Unrestricted.

Destination: Any object.

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: objChunk chunk to set flags for.

bitsToSet
bits to set.
bitsToClear
bits to clear.

Return: Nothing.

MSG_META_GET_FLAGS

word 	MSG_META_GET_FLAGS( /* low byte = ObjChunkFlags */
        ChunkHandle 	ch);

This message returns the chunk flags for the object. This works just like the ObjGetFlags() routine, but can be used when the object queried is being run by a different thread.

Source: Unrestricted.

Destination: Any object.

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: objChunk chunk to get flags for.

Return: Word with ObjChunkFlags in low byte, zero in high byte.

MSG_META_QUIT

void 	MSG_META_QUIT();

This message, when sent to a GenApplication object, initiates the shutdown sequence for the application. All affected objects are notified.

GenApplication does some error checking for multiple quits or detaches and then starts this sequence by passing MSG_META_QUIT (QL_BEFORE_UI) to the process. The default process handler for MSG_META_QUIT varies depending on the QuitLevel , which is passed in, but only when sent to the process (see MSG_META_QUIT_PROCESS alias, below).

The method handler for each level of quit should then send MSG_META_QUIT_ACK with the same QuitLevel when it is done. The default behavior for a process' MSG_META_QUIT responses are:

QL_BEFORE_UI
Sends MSG_META_QUIT_ACK to self via queue.
QL_UI
Sends MSG_GEN_APPLICATION_INITIATE_UI_QUIT (0) to the GenApplication.
QL_AFTER_UI
Sends MSG_META_QUIT_ACK to self via queue.
QL_DETACH
Sends MSG_META_DETACH to self via queue.
QL_AFTER_DETACH
Sends MSG_META_QUIT_ACK to self via queue.

The generic UI objects are first asked to quit via MSG_GEN_APPLICATION_INITIATE_UI_QUIT when sent to a GenApplication (active list). It will cause MSG_META_QUIT to be sent to all objects on the active list that are marked as desiring them. These objects on the active list can handle the MSG_META_QUIT any way they please. The process will be notified by a MSG_META_QUIT_ACK with the QuitLevel set to QL_UI.

If you need to send MSG_META_QUIT from within a handler for MSG_GEN_PROCESS_OPEN_APPLICATION, make sure to send it after calling the superclass in your OPEN_APPLICATION handler. If this is undesirable, you may encapsulate MSG_META_QUIT and send it via MSG_META_FLUSH_INPUT_QUEUE. (This is needed to avoid the system thinking that a valid IACP connection is open, aborting the quit.)

Source: Unrestricted. If you need to send MSG_META_QUIT within your MSG_GEN_PROCESS_OPEN_APPLICATION handler, please follow the guidelines illustrated above.

Destination: GenApplication object (note that this message has aliases so that it may be sent to a Process object, or any object).

Parameters: None.

Return: Nothing.

Interception: Unlikely.

MSG_META_QUIT_PROCESS

@alias (MSG_META_QUIT)
 void 	MSG_META_QUIT_PROCESS(
        word		quitLevel,
        ChunkHandle 		ackODChunk);

For information about the quit mechanism, see MSG_META_QUIT , above.

The process's MSG_META_QUIT_ACK handler is what causes this walking down the QuitList ; It provides the following behavior for each QuitLevel :

QL_BEFORE_UI
Sends MSG_META_QUIT (QL_UI) to self.
QL_UI
Sends MSG_META_QUIT (QL_AFTER_UI) to self.
QL_AFTER_UI
Sends MSG_META_QUIT (QL_DETACH) to self.
QL_DETACH
Sends MSG_META_QUIT (QL_AFTER_DETACH) to self.
QL_AFTER_DETACH
Sends MSG_GEN_PROCESS_FINISH_DETACH to self.

Source: Unrestricted.

Destination: Process object.

Parameters: quitLevel What stage of quitting we are in.

ackODChunk
Acknowledgment OD to be passed on to MSG_META_QUIT_ACK .

Return: Nothing.

Interception: Unlikely.

Warnings: You cannot abort the quit at the QL_DETACH stage or later.

MSG_META_QUIT_OBJECT

@alias (MSG_META_QUIT)
 void 	MSG_META_QUIT_OBJECT(
        optr 	obj);

For information about the quit mechanism, see MSG_META_QUIT , above.

Source: Unrestricted.

Destination: Process object.

Parameters: obj Object to send MSG_META_QUIT_ACK to.

Return: Nothing.

Interception: Unlikely.

MSG_META_QUIT_ACK

void 	MSG_META_QUIT_ACK(
        word 		quitLevel,
        word 		abortFlag);

This message is sent to a Process object in response to a MSG_META_QUIT . The Process object handles this message by continuing the quit sequence.

Source: Any object having received MSG_META_QUIT

Destination: OD passed in MSG_META_QUIT .

Parameters: quitLevel QuitLevel acknowledging (if responding to a process).

abortFlag
(non-zero if you want to abort the quit).

Return: Nothing.

Warnings: For processes that subclass MSG_META_QUIT , you cannot abort the quit at the QL_DETACH stage or later.

MSG_META_FINISH_QUIT

void 	MSG_META_FINISH_QUIT(
        Boolean		abortFlag);

This message is sent to the object that initiated MSG_META_QUIT and has received MSG_META_QUIT_ACK from each party notified. This message informs the object that it has finished sending out all MSG_META_QUIT messages and can go on with quitting (or aborting the quit if that is the case).

Source: Object that initiated MSG_META_QUIT.

Destination: Any object.

Parameters: abortFlag (non-zero if you want to abort the quit).

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 Object Management Messages | Next: 2.5 Event Messages


Event Messages

MetaClass: 2.5 Utility Messages: Event Messages

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 User Interface Utility Meta Messages | Next: 2.6 Variable Data Messages

These messages are used to send classed events to other objects. A classed event is typically an event stored earlier with the Goc keyword @record .

MSG_META_DISPATCH_EVENT

Boolean	MSG_META_DISPATCH_EVENT(
        AsmPassReturn 	*retVals,
        EventHandle 	eventHandle,
        MessageFlags msgFlags););

This message causes an object to @send or @call a message of another object. This is useful for getting one object run by a different thread to call yet another object or to send a reply to the first object. This message can cause complex synchronization problems if not used with extreme care.

Source: Unrestricted.

Destination: Any object.

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: retValue structure to hold return values.

eventHandle
Event which will be sent.
msgFlags
flags which will determine how message is sent.

Return: If MF_CALL specified, then carry flag return value will be returned.

Structures:

 typedef struct {
    word 	ax;
    word 	cx;
    word 	dx;
    word 	bp;
} AsmPassReturn;

MSG_META_SEND_CLASSED_EVENT

void	MSG_META_SEND_CLASSED_EVENT(
        EventHandle	 	event,
        TravelOption 		whereTo);

This message sends a previously recorded classed event to a certain type of destination defined in the TravelOption argument whereTo .

This message's interesting behavior is actually added by the User Interface, which defines GenTravelOption types. The default behavior provided here in MetaClass is to destroy the event if TO_NULL is passed, else to deliver the event to itself if it is capable of handling it (the object is a member of the class stored with the event). The event is always freed, whether or not it is deliverable.

MetaClass recognizes the following TravelOption values:

TO_NULL
TO_SELF
TO_OBJ_BLOCK_OUTPUT
TO_PROCESS
TO_OBJ_BLOCK_OUTPUT sends the event to the object block's output set in its block header.

Source: Unrestricted.

Destination: Any object.

Interception: By default, MetaClass handlers deal with just the most primitive of the TravelOption values. Object classes can add new TravelOption types, but must then intercept this message to implement them (calling the superclass if it doesn't recognize the TravelOption passed).

Parameters: event Classed event, probably created using @record .

whereTo
TravelOption describing target of message.

Return: Nothing.

MSG_META_GET_OBJ_BLOCK_OUTPUT

optr	MSG_META_GET_OBJ_BLOCK_OUTPUT();

This message returns the output optr of an object block that contains the object sent the message.

Source: Unrestricted.

Destination: Any object (except a process object).

Return: Optr of the block's output field.

Interception: Generally not intercepted.

MSG_META_SET_OBJ_BLOCK_OUTPUT

void	MSG_META_SET_OBJ_BLOCK_OUTPUT(
        optr		output);

This message sets the object block output--the block containing the object sent the message-- to the passed optr.

Source: Unrestricted.

Destination: Any object (except a process object).

Parameters: output Optr of the object to act as the block's output.

Interception: Generally not intercepted.

MSG_META_GET_OPTR

optr	MSG_META_GET_OPTR();

This message returns the object's optr. This is useful when combined with MSG_GEN_GUP_CALL_OBJECT_OF_CLASS to get the optr of an object of a given class somewhere up in a Generic Tree.

Note: MSG_GEN_GUP_CALL_OBJECT_OF_CLASS dies if an object of the class doesn't exist. Use MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS before using MSG_GEN_GUP_CALL_OBJECT_OF_CLASS if there is some question as to whether an object of a given class exists.

Source: Unrestricted.

Destination: Any object.

Interception: Unlikely.

Parameters: None.

Return: The object's optr.

MSG_META_GET_TARGET_AT_TARGET_LEVEL

void	MSG_META_GET_TARGET_AT_TARGET_LEVEL(
        GetTargetParams *retValue,
        TargetLevel level);

This message returns the GetTargetParams structure containing, among other things, the current target object at a given target level. The MetaClass handler simply returns information about the current object since it is assumed to be the current target. GenClass and VisClass handlers will handle this message and pass it down the travel hierarchy. If you intercept this message, you should correctly pass this message down as well, either explicitly or by calling the superclass. See the Input chapter, for information on target.

Source: Unrestricted.

Destination: Any object.

Interception: Must be handled by target nodes to correctly pass the request on down to the next target below current node in hierarchy.

Parameters: level Zero for leaf, otherwise TargetLevel , as defined by UI.

retValue
Structure to hold return value.

Return: Nothing returned explicitly.

retValue
Filled with return values.

Structures:

  typedef struct {
    ClassStruct 				*GTP_class;
    optr 				GTP_target;
} GetTargetParams;
| Up: GEOS SDK TechDocs | Up | Prev: 2.4 User Interface Utility Meta Messages | Next: 2.6 Variable Data Messages


Variable Data Messages

MetaClass: 2.6 Utility Messages: Variable Data Messages

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 Event Messages | Next: 2.7 Notification Messages

Variable data is instance data that can appear or not appear within the object's instance chunk. For information on variable data and how these three messages are used, see the GEOS Programming chapter.

MSG_META_ADD_VAR_DATA

void	MSG_META_ADD_VAR_DATA(@stack
        word	dataType,
        word	dataSize,
        word	*data)

This message adds a variable data type to the recipient object's instance data. If the variable data field was already present, this will change its value. This is useful for adding hints to generic objects at run-time.

If it is possible that you are adding the object's first/only field of variable data, set the object's OCF_VARDATA_RELOC flag by means of the ObjSetFlags() routine or MSG_META_SET_FLAGS.

Note that the object will be marked dirty even if nothing was changed.

NOTE: The dataType should have VDF_SAVE_TO_STATE set as desired. VDF_EXTRA_DATA is ignored; it will be set correctly by this routine.

Source: Unrestricted.

Destination: Any object.

Interception: Generally not intercepted; default MetaClass handling performs the desired function.

Parameters: dataType Data type (e.g. ATTR_PRINT_CONTROL_APP_UI).

dataSize
Size of data, if any.
data
If no extra data, NULL. If dataSize is non-zero, then this may be a pointer to data to initialize data with.

Return: Nothing. Object marked dirty even if data type already exists.

MSG_META_DELETE_VAR_DATA

Boolean	MSG_META_DELETE_VAR_DATA(
        word	dataType);

This message removes a particular variable data entry from the recipient object's instance data. This is useful for removing hints from generic objects at run-time.

Source: Unrestricted.

Destination: Any object.

Interception: Generally not intercepted; default MetaClass handling performs the desired function.

Parameters: dataType Data type to delete. VarDataFlags ignored.

Return: Returns false if data deleted, true if data was not found. Object marked dirty if data type found and deleted.

MSG_META_INITIALIZE_VAR_DATA

word	MSG_META_INITIALIZE_VAR_DATA(
        word	dataType);

This message is sent to an object any time the ObjVarDerefData() routine is called and the data type is not found. It should be subclassed by any object that defines a variable type that will be used with ObjVarDerefData() . The object must create and initialize the data and return its offset.

Sent to an object having a variable data entry which code somewhere is attempting to access via ObjVarDerefData() . It is the object that defines the variable data entry type's responsibility to create the data entry and initialize it at this time, and to return a pointer to the extra data (if any), as returned by ObjVarAddData() .

Source: ObjVarDerefData() routine. Should not be used as a replacement for ObjVarAddData() , or MSG_ADD_VAR_DATA_ENTRY , but may be used any time code is ready to access a particular piece of variable data instance data, knows that the variable data has not yet been created, and wishes to ensure that it does exist.

Destination: Any object stored in an LMem Object block.

Interception: Required by any class which defines a variable data entry type that needs to be initialized before usage. Objects handling this message should first compare the passed data type against variable data types it understands, and pass any unknown types onto the superclass for handling.

Parameters: Variable data type.

Return: Offset to extra data created (or, if no extra data, the start of data entry plus the size of VarDataEntry ). Normally, this would just be the offset returned by the call to ObjVarAddData() .

MSG_META_GET_VAR_DATA

word MSG_META_GET_VAR_DATA( /* returns size of data returned in buf;
              * -1 if not found */
        word 	dataType,
        word 	bufSize,
        void 	*buf);

This message fetches variable data of a given type from an object.

Source: Unrestricted.

Destination: Any object.

Interception: Generally not intercepted; default MetaClass handling performs the desired function.

Parameters: dataType The variable data category to return.

bufSize
Size available to return data.
buf
Pointer to buffer to hold returned data.

Return: The size of the data returned. If the vardata entry was not found, then message will return -1.

buf
Filled with vardata's data, if any.
| Up: GEOS SDK TechDocs | Up | Prev: 2.5 Event Messages | Next: 2.7 Notification Messages


Notification Messages

MetaClass: 2.7 Utility Messages: Notification Messages

| Up: GEOS SDK TechDocs | Up | Prev: 2.6 Variable Data Messages | Next: 2.8 Options Messages

These messages are used by the various notification mechanisms throughout the system.

MSG_META_NOTIFY

void	MSG_META_NOTIFY(
        ManufacturerID manufID,
        word	notificationType,
        word 	data);

This message notifies the recipient that some change or action has taken place. The object must have registered for the notification. The type of change that has occurred depends on the notificationType argument.

One word of notification data is allowed, but this should not reference a handle which must at some point be destroyed. See MSG_META_NOTIFY_WITH_DATA_BLOCK for such requirements.

Source: Unrestricted.

Destination: Any object, or any of the GCNListSend() routines.

Interception: No general requirements, though particular notification types may place restrictions or requirements on such handling.

Parameters: manufID Manufacturer ID associated with notification type.

notificationType
What sort of notification is being announced. This value depends on the Manufacturer ID for that notification list. A list of valid types for the Geoworks Manufacturer ID is indicated by the GeoWorksNotificationType enumerated type.
data
One word of data, which will be placed in a vardata field. If more than one word is necessary, use MSG_META_NOTIFY_WITH_DATA_BLOCK , below.

Return: Nothing.

MSG_META_NOTIFY_WITH_DATA_BLOCK

void	MSG_META_NOTIFY_WITH_DATA_BLOCK(
        ManufacturerID manufID,
        word	notificationType,
        MemHandle data);

This message acts like MSG_META_NOTIFY , but it also carries a handle of a block of data. It is absolutely imperative that if this message is subclassed, the object call its superclass in the handler.

The data block must be set up to use the Block "reference count" mechanism, i.e. be sharable and initialized with MemInitRefCount() . Details on the count are noted below.

Source: Unrestricted.

Destination: Any object, or any of the GCNListSend... routines

Interception: Message must eventually arrive at the MetaClass handler, with the handle to the data block with the reference count intact, in order for the block to be freed when no longer referenced. Failure to do so will result in garbage being left on the heap, which will kill the system with repetitive occurrences.

Parameters: manufID Manufacturer ID associated with notification type.

notificationType
What sort of notification is being announced.
data
SHARABLE data block having a "reference count" initialized via MemInitRefCount() .

NOTE on data block reference counts:
The reference count should hold the total number of references of this data block. This count should be incremented before sending a message holding a reference to this block (using MemIncRefCount() ). Any messages passing such reference either must have a MetaClass handler which decrements this count and frees the block if it reaches zero or must call MemDecRefCount() (which does exactly that). GCNListSend() and similar functions add in the number of optrs in any list to which a message referring to this block is sent. Thus, when creating a block which will only be sent using GCNListSend() , this count should be initialized to zero. If the block is to be sent to one or more objects or GCNListSend() calls, the calling routine should call MemIncRefCount() before making the calls, being sure to call MemIncRefCount() additionally for any objects that the message is sent to, and then call MemDecRefCount () after the calls, to balance the increment call at the start.

Return: Nothing.

Warnings: This message must eventually reach the default MetaClass handler, so that the block can be freed when no longer referenced.

MSG_META_GCN_LIST_ADD

Boolean	MSG_META_GCN_LIST_ADD(@stack
        optr			dest,
        word 		listType,
        ManufacturerID 		listManuf);

This message adds the passed object to a particular notification list. It returns true if the object was successfully added, false otherwise. This message is the equivalent of GCNListAdd() for individual object GCN list.

Source: Unrestricted.

Destination: Object providing GCN services.

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: dest Object to be added to list.

listType
GCNListType to add object to.
listManuf
Manufacturer ID associated with GCN list.

Return: Returns true if optr added, false otherwise.

MSG_META_GCN_LIST_REMOVE

Boolean	MSG_META_GCN_LIST_REMOVE(@stack
        optr	dest,
        word	listType,
        ManufacturerID listManuf);

This message removes the passed object from a particular notification list. It returns true if the object was successfully removed, false otherwise. This message is the equivalent of GCNListRemove() for an individual object's GCN list.

Source: Unrestricted.

Destination: Object providing GCN services.

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: dest Object to be removed from list.

listType
Which list to remove object from.
listManuf
Manufacturer ID associated with GCN list.

Return: Returns true if optr found and removed, otherwise returns false .

MSG_META_GCN_LIST_SEND

void	MSG_META_GCN_LIST_SEND(@stack
        GCNListSendFlags 		flags,
        EventHandle		event,
        MemHandle 		block,
        word		listType,
        ManufacturerID 		listManuf);

This message sends the given event to all objects in a particular notification list. The event will be freed after being sent. This message is the equivalent of GCNListSend() for an individual object GCN list.

Parameters: flags Flags to pass on to GCNListSend().

event
Classed event to send to the list.
block
Handle of extra data block, if used, else NULL. This block must have a reference count, which may be initialized with MemInitRefCount() and incremented for any new usage with MemIncRefCount() . Methods in which they are passed are considered such a new usage, and must have MetaClass handlers which call MemDecRefCount() .
listType
Which GCN list to send event to.
listManuf
Manufacturer ID associated with GCN list.

Return: Nothing.

Structures:

  typedef WordFlags GCNListSendFlags;
/* These flags may be combined using | and &:
    GCNLSF_SET_STATUS,
    GCNLSF_IGNORE_IF_STATUS_TRANSITIONING */
GCNLSF_SET_STATUS
Additionally saves the message as the list's current "status." The "status" message is automatically sent to any object adding itself to the list at a later point in time.
GCNLSF_IGNORE_IF_STATUS_TRANSITIONING
Optimization bit used to avoid lull in status when transitioning between two different sources--such as when the source is the current target object, and one has just lost, and another may soon gain, the exclusive. (The bit should be set only when sending the "null," "lost," or "not selected" status, as this is the event that should be discarded if another non-null status comes along shortly). Implementation is not provided by the kernel primitive routines, which ignore this bit, but may be provided by objects managing their own GCN lists. GenApplication responds to this bit by delaying the request until after the UI and application queues have been cleared, and then only sets the status as indicated if no other status has been set since the first request. Other objects may use their own logic to implement this optimization as is appropriate. Mechanisms which can not tolerate the delayed status setting nature of this optimization, or require that all changes are registered, should not pass this bit set.

MSG_META_GCN_LIST_FIND_ITEM

Boolean	MSG_META_GCN_LIST_FIND_ITEM(@stack
        optr		dest,
        word		listType,
        ManufacturerID		listManuf);

This message checks whether an object is on a particular GCN list.

Source: Unrestricted.

Destination: Any object providing GCN services.

Parameters: dest Optr of object that we are checking.

listType
GCNListType .
listManuf
ManufacturerID .

Return: true if object is on the GCN list.

Interception: Unnecessary.

MSG_META_GCN_LIST_DESTROY

void	MSG_META_GCN_LIST_DESTROY();

This message completely destroys the GCN setup for the caller. It frees all GCN lists, cached events, and overhead data storage. This should only be used when the object is being freed. You will likely never handle or call this message.

Source: Object providing GCN services, often in handler for MSG_META_FINAL_OBJ_FREE .

Destination: Self.

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: Nothing.

Return: Nothing.

MSG_META_NOTIFY_OBJ_BLOCK_INTERACTIBLE

void	MSG_META_NOTIFY_OBJ_BLOCK_INTERICTABLE(
        MemHandle objBlock);

This message is sent to an object block's output object when the block changes from being not in-use to being in-use. An object may handle this message to monitor changes of in-use status.

Source: Kernel.

Destination: Object which is set as the output of an object block resource either by ObjBlockSetOutput() , or by being pre-defined in an application resource.

Interception: May be intercepted to learn about change in object block interactable status. No default handling is provided, though you may wish to pass the message onto the superclass in case it is interested in this data as well.

Parameters: objBlock Handle of object block.

Return: Nothing.

MSG_META_NOTIFY_OBJ_BLOCK_NOT_INTERACTIBLE

void	MSG_META_NOTIFY_OBJ_BLOCK_NOT_INTERACTIBLE(
        MemHandle objBlock);

This message is sent to an object block's output object when the block changes from being not in-use to being in-use. An object may handle this message to monitor changes of in-use status.

Source: Kernel.

Destination: Object which is set as the output of an Object Block resource either by ObjBlockSetOutput() , or by being pre-defined in an application resource.

Interception: May be intercepted to learn about change in object block interactable status. No default handling is provided, though you may wish to pass the message onto the superclass in case it is interested in this data as well.

Parameters: objBlock Handle of object block.

Return: Nothing.

MSG_META_VM_FILE_DIRTY

void	MSG_META_VM_FILE_DIRTY(
        FileHandle file);

This message is sent to all processes that have a VM file open when a block in the file becomes marked dirty for the first time. This is useful if many processes may be sharing a VM file. The VM file must be marked VMA_NOTIFY_DIRTY in its attributes.

Source: Kernel VM code.

Destination: ProcessClass object.

Interception: May be intercepted at process to do whatever is desired on this occurrence of this event. Default behavior in GenProcessClass sends notification to the current model GenDocumentGroupClass object.

Parameters: file Handle open to the VM file, from the receiving process's perspective.

Return: Nothing.

| Up: GEOS SDK TechDocs | Up | Prev: 2.6 Variable Data Messages | Next: 2.8 Options Messages


Options Messages

MetaClass: 2.8 Utility Messages: Options Messages

| Up: GEOS SDK TechDocs | Up | Prev: 2.7 Notification Messages | Next: 2.9 Suspending and Unsuspending

These messages are used by the User Interface when working with the GEOS.INI files. You will probably never need to subclass or call these messages.

MSG_META_SAVE_OPTIONS

void	MSG_META_SAVE_OPTIONS();

This message saves an object's options to the .INI file for an object. It is sent via the UI's active list mechanism.

Source: Unrestricted.

Destination: GenApplication object, which in turn broadcasts to everything on list of objects having options needing to be saved

Interception: Objects having options to save should intercept this. Superclass should be called in case any of the superclasses needs similar notification.

MSG_META_LOAD_OPTIONS

void	MSG_META_SAVE_OPTIONS();

This message loads the object's setting from the .INI file.

Source: Unrestricted.

Destination: Any object.

Interception: Any object that should load its options should intercept this. Behavior is currently implemented for Generic UI objects.

MSG_META_RESET_OPTIONS

void	MSG_META_RESET_OPTIONS();

This message resets the object's settings from the .INI file to their initial state.

Source: Unrestricted. Sent to all objects on the GAGCNLT_SELF_LOAD_OPTIONS and GAGCNLT_STARTUP_LOAD_OPTIONS lists.

Destination: Any object.

Interception: Any object that wants to reset its options should intercept this. Behavior is currently implemented for Generic UI objects.

MSG_META_GET_INI_CATEGORY

void	MSG_META_GET_INI_CATEGORY(
        char	*buf);

This message returns the .INI file category of the object.

Source: Unrestricted, though generally self.

Destination: Object having options.

Interception: Default handler walks up tree, eventually finding name of application. Can be intercepted at any level to change the category for a branch.

Parameters: buf The buffer for .INI category string. This buffer size cannot store more than 64 bytes.

Return: Nothing returned explicitly.

buf
String filled with category string.
| Up: GEOS SDK TechDocs | Up | Prev: 2.7 Notification Messages | Next: 2.9 Suspending and Unsuspending


Suspending and Unsuspending

MetaClass: 2.9 Utility Messages: Suspending and Unsuspending

| Up: GEOS SDK TechDocs | Up | Prev: 2.8 Options Messages | Next: 2.10 Help Files

MSG_META_SUSPEND and MSG_META_UNSUSPEND work together to allow objects to optimize recalculation when doing a series of actions. These messages are implemented by various objects in the system (such as the text object and the grobj body). This mechanism is used by GenControlClass to optimize recalculation stemming from multiple controller outputs.

An object typically implements these messages by keeping a suspend count and a record of the calculations that were aborted because the object was suspended. When the suspend count reaches zero, the object will perform the calculations.

An object implementing this mechanism should always call its superclass since multiple class levels could be implementing this mechanism.

MSG_META_SUSPEND

void 	MSG_META_SUSPEND();

Suspend calculation in an object.

Source: Normally sent by a controller object but can be sent by anything.

Destination: Any object that implements the mechanism described above.

Interception: An object that wants to implement the mechanism described above.

Parameters: None.

Return: Nothing.

MSG_META_UNSUSPEND

void 	MSG_META_UNSUSPEND();

Unsuspend calculation in an object.

Source: Normally sent by a controller object but can be sent by anything.

Destination: Any object that implements the mechanism described above.

Interception: An object that wants to implement the mechanism described above.

Parameters: None.

Return: Nothing.

| Up: GEOS SDK TechDocs | Up | Prev: 2.8 Options Messages | Next: 2.10 Help Files


Help Files

MetaClass: 2.10 Utility Messages: Help Files

| Up: GEOS SDK TechDocs | Up | Prev: 2.9 Suspending and Unsuspending | Next: 3 Exported Message Ranges
MSG_META_GET_HELP_FILE, MSG_META_SET_HELP_FILE, MSG_META_BRING_UP_HELP

These help messages are contained within MetaClass to allow help files within any object in the GEOS system.

MSG_META_GET_HELP_FILE

void	MSG_META_GET_HELP_FILE(
        char		*buf);

This message returns the name of the help file attached to the object sent this message. If no help file is found, the default MetaClass handler walks up the tree.

Source: Unrestricted.

Destination: Object in object tree containing help.

Parameters: buf Pointer to buffer to store the help file name.

Return: Buffer filled in.

Interception: The default handler walks up the tree; you may intercept to change the help file for a branch.

MSG_META_SET_HELP_FILE

void	MSG_META_SET_HELP_FILE(
        char		*buf);

This message sets the help file for the object sent this message.

Source: Unrestricted, though generally an object sends this to itself.

Destination: An object within a tree.

Parameters: buf Pointer to help file name. This buffer's size must be at least FILE_LONGNAME_BUFFER_SIZE.

Interception: Generally not intercepted.

MSG_META_BRING_UP_HELP

void	MSG_META_BRING_UP_HELP();

This message finds a help context for the current object tree and sends a notification to bring up help with that context.

Source: Unrestricted, though generally an object sends this to itself.

Destination: An object within a tree.

Interception: The default handler for this message walks up the visual tree (not the generic tree) eventually finding a GenClass object with ATTR_GEN_HELP_CONTEXT. You may intercept at any level to change the help context for a branch.

| Up: GEOS SDK TechDocs | Up | Prev: 2.9 Suspending and Unsuspending | Next: 3 Exported Message Ranges


Exported Message Ranges

MetaClass: 3 Exported Message Ranges

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Utility Messages | Next: 3.1 Window Messages

MetaClass exports a number of ranges of messages for use by its subclasses (such as GenClass and VisClass ) for various purposes. In most cases, you will not need to use any of these ranges for your own messages. The names of these ranges, however, are listed below. For information on exporting and importing message ranges, see the GEOS Programming chapter.

  • MetaWindowMessages
    These messages alert a window's input object and output descriptor to certain important events. For example, MSG_META_EXPOSED announces that part of a window has been exposed and needs to be redrawn.
  • MetaInputMessages
    These are very low-level messages, and will only be used by those geodes which wish to circumvent the Input Manager. To work with the Input Manager correctly, see the selection messages in the MetaUIMessages range.
  • MetaUIMessages
    These messages may be used by objects which will interact with the UI. While messages for working with the Input Manager and Clipboard are detailed in this chapter, information on other messages may be found in the Documents chapter.
  • MetaSpecificUIMessages
    These messages are internal.
  • MetaApplicationMessages
    These messages don't have any meaning attached to them; no class defined in the system or any library has a handler for any of these messages. Any object class defined within an application may have a handler for any of these messages.
    This message range was set up so that two or more classes defined within an application could agree on some message numbers.
    Thus, your application could contain the header listed below, and then write handlers for the message in two completely unrelated application-defined classes:
 @importMessage MetaApplicationMessages,
    type0 MSG_MYAPP_DO_SOMETHING(
        type1		arg1,
        type2		arg2);
/* ... */
@method MyProcessClass, MSG_MYAPP_DO_SOMETHING
/* ...Insert Handler here */
/* ... */
@method MyDocumentClass, MSG_MYAPP_DO_SOMETHING
/* ...Insert Handler here */
  • MetaGrObjMessages
    This message range is reserved for notification messages associated with the graphic object library.
  • MetaPrintMessages
    For information on these messages, see the Printing chapter.
  • MetaSearchSpellMessages
    These messages are sent out by the SearchReplace and Spell controllers. For information on these messages, see the Text Objects chapter.
  • MetaGCNMessages
    There are several system-defined General Change Notification lists which objects may belong to. This message range holds those messages which will be sent to objects on system-defined lists.
  • MetaTextMessages
    See the Text Objects chapter.
  • MetaStyleMessages
    See .
  • MetaColorMessages
    See the Controllers chapter.
  • MetaFloatMessages
    These messages are sent out by the FloatFormat controller. For information on these messages, see the Controllers chapter.
  • MetaIACPMessages
    These messages are used to communicate to other objects using the Inter-Application Communication Protocol. IACP is discussed in the Applications and Geodes chapter.

Window Messages

Input Messages

UI Messages

Standard GCN Messages

IACP Meta Messages

| Up: GEOS SDK TechDocs | Up | Down | Prev: 2 Utility Messages | Next: 3.1 Window Messages


Window Messages

MetaClass: 3.1 Exported Message Ranges: Window Messages

| Up: GEOS SDK TechDocs | Up | Prev: 3 Exported Message Ranges | Next: 3.2 Input Messages

Because many objects, both Generic UI objects and others, work together to control the behavior of the system windows, a number of messages have been set up in an exported range so that they may be shared among classes.

Window Update Messages

The following messages are sent to objects responsible for updating views, and if you subclass content objects, you may wish to intercept these messages.

MSG_META_EXPOSED

@importMessage MetaWindowMessages, void MSG_META_EXPOSED(
        WindowHandle win);

This message is sent to a Window's exposure object any time a portion of the window is visible on screen, has become invalid, and needs to be redrawn. Correct response is to create a GState on the passed window, call GrBeginUpdate() with it, redraw the window, and finish by calling GrEndUpdate() and freeing the GState. Drawing will be clipped to the invalid area of the window at the time that GrBeginUpdate() is called. Invalidations occurring during the redraw will result in the reduction in the size of the update region, and result in another MSG_META_EXPOSED being generated, to repair the new "invalid" area.

Source: Window system.

Destination: Individual window's exposure object; View's output descriptor.

Interception: Required, in order for window to be properly updated. Note that VisContentClass provides default handler which creates GState, calls GrBeginUpdate() , calls MSG_VIS_DRAW on itself, then calls GrEndUpdate() .

Parameters: win Window handle which may be passed to GrCreateGState() .

Return: Nothing.

Messages Sent to Objects Further Up the Input Hierarchy

The following messages are part of the high level windowing mechanism. Most of these messages are passed around at the GenSystem level, and most object classes defined by applications will not intercept them. Instead, system objects will intercept these messages and pass appropriate messages on to application objects.

MSG_META_WIN_CHANGE

@importMessage MetaWindowMessages, void 	MSG_META_WIN_CHANGE();

Sent to the System Input Object (Normally the UI's GenSystem obj), when the pointer position, as passed to the window system in calls to WinMovePtr() , has possibly moved outside of the window that it was in. The object should respond by calling WinChangeAck() , which will cause enter and leave events to be generated for all windows affected by the pointer's change.

Source: Window system ( WinMovePtr() ).

Destination: System Input object (usually the GenSystem object).

Interception: Must be handled via call to WinChangeAck() .

Parameters: None.

Return: Nothing.

MSG_META_IMPLIED_WIN_CHANGE

@importMessage MetaWindowMessages, void 	MSG_META_IMPLIED_WIN_CHANGE(
        optr 		inputObj,
        WindowHandle 		ptrWin);

Sent to the System Input Object (Normally the UI's GenSystem obj) in response to a call to WinChangeAck() , to inform it which window the mouse has moved into. The system input object is responsible for passing this message on to the Input object of affected geodes.

Source: Window system ( WinChageAck() ).

Destination: Initially System Input Object (usually the GenSystemClass object), though is relayed on to Geode Input Object (usually a GenApplicationClass object).

Interception: May be intercepted to learn when an implied window change has occurred, but subclasses should not change any default functionality.

Parameters: inputObj Window which has implied grab (or zero if there is no implied grab).

ptrWin
Window that pointer is in.

Return: Nothing.

MSG_META_RAW_UNIV_ENTER

@importMessage MetaWindowMessages, void 	MSG_META_RAW_UNIV_ENTER(
        optr 		inputObj,
        WindowHandle 		ptrWin);

This message is generated by the window system whenever the mouse crosses into a window. This message is sent to the window's input object. This is sent whenever the mouse pointer crosses a window boundary, regardless of any existing window grab.

Source: Window system ( WinChangeAck() ).

Destination: Initially System input object (usually the GenSystemClass object), though is relayed on to Geode Input Object (usually a GenApplicationClass object), and finally onto individual Window's Input Object.

Interception: May be intercepted to track current status of whether mouse is within window or not. Specific UIs rely on these messages to control auto-raise, click-to-raise arming, and correct implied and active mouse grab interaction behavior.

Parameters: inputObj Input Object of window method refers to.

ptrWin
Window that method refers to.

Return: Nothing.

MSG_META_RAW_UNIV_LEAVE

@importMessage MetaWindowMessages, void 	MSG_META_RAW_UNIV_LEAVE(
        optr 		inputObj,
        WindowHandle 		ptrWin);

This message is generated by the window system whenever the mouse crosses out of a window. This message is sent to the window's input object. This is sent whenever the mouse pointer crosses a window boundary, regardless of any existing window grab.

Source: Window system ( WinChangeAck() ).

Destination: Initially System Input Object (usually the GenSystemClass object), though is relayed on to Geode Input Object (usually a GenApplicationClass object), and finally onto individual Window's Input Object.

Interception: May be intercepted to track current status of whether mouse is within window or not. Specific UIs rely on these messages to control auto-raise, click-to-raise arming, and correct implied and active mouse grab interaction behavior.

Parameters: inputObj Input Object of window method refers to.

ptrWin
Window that method refers to.

Return: Nothing.

| Up: GEOS SDK TechDocs | Up | Prev: 3 Exported Message Ranges | Next: 3.2 Input Messages


Input Messages

MetaClass: 3.2 Exported Message Ranges: Input Messages

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Window Messages | Next: 3.3 UI Messages

These messages contain "raw" input events; events which have not yet been processed by the Input Manager. Most applications intercepting input events should intercept events which have been so processed, as described in UI Messages .

MSG_META_MOUSE_BUTTON

@importMessage MetaInputMessages, void MSG_META_BUTTON(
        word 	xPosition,
        word 	yPosition,
        word 	inputState);

This message is sent out on any button press or release.

Parameters: xPosition X-coordinate of mouse event.

yPosition
Y-coordinate of mouse event.
inputState
High byte is a ShiftState; low byte is ButtonInfo.

Return: Nothing.

MSG_META_MOUSE_PTR

@importMessage MetaInputMessages, void MSG_META_PTR(
        word 	xPosition,
        word 	yPosition,
        word 	inputState);

This message is sent out on any mouse movement.

Parameters: xPosition X-coordinate of mouse event.

yPosition
Y-coordinate of mouse event.
inputState
High byte is a ShiftState; low byte is ButtonInfo.

Return: Nothing.

MSG_META_KBD_CHAR

@importMessage MetaInputMessages, void MSG_META_KBD_CHAR(
        word 	character,
        word 	flags, /* low byte = CharFlags, high byte = ShiftState */
        word 	state);/* low byte = ToggleState, high byte = scan code */

This is the message sent out on any keyboard press or release. To determine whether the message is in response to a press or a release, check the CF_RELEASE bit of the flags field.

Parameters: character Low byte contains Char value of incoming character.

flags
High byte is ShiftState ; low byte is CharFlags .
state
High byte is raw PC scan code; low byte is ToggleState .

Return: Nothing.

MSG_META_MOUSE_DRAG

@importMessage MetaInputMessages, void MSG_META_MOUSE_DRAG(
        word 	xPosition,
        word 	yPosition,
        word 	inputState);

This is a very low-level message, signalling that the user is dragging the mouse.

Parameters: xPosition X-coordinate of mouse event.

yPosition
Y-coordinate of mouse event.
inputState
High byte is a ShiftState ; low byte is ButtonInfo .

Return: Nothing.

| Up: GEOS SDK TechDocs | Up | Prev: 3.1 Window Messages | Next: 3.3 UI Messages


UI Messages

MetaClass: 3.3 Exported Message Ranges: UI Messages

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Input Messages | Next: 3.4 Standard GCN Messages

The User Interface generates many messages which may alert objects to events which will allow them to work with the user. These events include those generated from the actions of input devices and clipboard-related events.

Clipboard Messages

The following messages are used to implement common clipboard functions.

MSG_META_CLIPBOARD_CUT

@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_CUT();

This message is sent to an object which is supposed to be the destination of a clipboard operation. MSG_META_CLIPBOARD_CUT should register the current selection with the UI as the new clipboard item, but also delete the current selection.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

MSG_META_CLIPBOARD_COPY

@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_COPY();

This message is sent to an object which is supposed to be the destination of a clipboard operation. MSG_META_CLIPBOARD_COPY should be handled by registering the current selection with UI as the new clipboard item.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

MSG_META_CLIPBOARD_PASTE

@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_PASTE();

This message is sent to an object which is supposed to be the destination of a clipboard operation. MSG_META_CLIPBOARD_PASTE should replace the current selection with the current clipboard item, which can be obtained from the UI.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK

@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK(
        ClipboardQuickNotifyFlags flags);

This message is sent to the source of a quick transfer item when a potential destination provides feedback to the user indicating whether a move, a copy or no operation will occur. The default behavior is determined by the destination, but the user may be able to override with the MOVE or COPY override keys.

Source: Sent by quick-transfer mechanism.

Destination: Sent to optr passed to ClipboardStartQuickTransfer() . Handled if the quick-transfer source needs to know what quick-transfer operation a potential destination will perform. Handler need not call superclass.

Interception: Message sent directly to destination, no need to intercept.

Parameters: flags Quick transfer cursor action specified by source (see ClipboardSetQuickTransferFeedback() ).

Return: Nothing.

MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED

@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK(
        ClipboardQuickNotifyFlags 	flags);

This message is sent to the source of a quick transfer item when the operation is completed. The ClipboardQuickNotifyFlags are set by any MSG_META_END_MOVE_COPY handler. This is only sent out if the source requests notification with the CQTF_NOTIFICATION flag passed to ClipboardStartQuickTransfer() .

Source: Sent by quick-transfer mechanism.

Destination: Sent to optr passed to ClipboardStartQuickTransfer() . Handled if the quick-transfer source needs to know what quick-transfer operation was performed. Handler need not call superclass.

Interception: Message sent directly to source of transfer; no need to intercept.

Parameters: flags Quick transfer cursor action specified by source (see ClipboardSetQuickTransferFeedback() ).

Return: Nothing.

MSG_META_CLIPBOARD_NOTIFY_TRANSFER_ITEM_FREED

@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_NOTIFY_TRANSFER_ITEM_FREED(
        VMFileHandle 	itemFile,
        VMBlockHandle 	itemBlock);

Sent to all ODs in Transfer Notify List to help maintain integrity of transfer items from VM files other than the UI's transfer VM file. Only sent if VM file handle of transfer item that is being freed is different from UI's transfer VM file handle. If a transfer item from a VM file other than the UI's transfer VM file is registered, the VM blocks in that transfer item cannot be freed and the VM file cannot be closed until notification is sent saying that the transfer item has been freed. Registrars of such transfer items should keep track of the VM file handle and VM block handle of the item to check against the info sent by this message.

Source: Sent by the clipboard mechanism.

Destination: Sent to optrs on transfer notification list, added with ClipboardAddToNotificationList() . Handled if clipboard changes need to be monitored.

Interception: Unlikely.

Parameters: itemFile File containing the transfer item.

itemBlock
Block containing the transfer item.

Return: Nothing.

MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED

@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED();

Sent to all ODs in Transfer Notify List to help with updating of Cut, Copy, and Paste button states. Recipients can call ClipboardQueryItem() to check if the new normal transfer item contains formats that the recipient supports. If not, Paste button can be disabled.

Source: Sent by the clipboard mechanism, relayed by GenEditControl.

Destination: Sent to optrs on transfer notification list, added with ClipboardAddToNotificationList() . Handled if clipboard changes need to be monitored.

Interception: Unlikely.

Parameters: None.

Return: Nothing.

Undo Messages

These messages implement the "undo" mechanism which allows objects to store a chain of actions which can later be undone.

MSG_META_UNDO

@importMessage MetaUIMessages, void 	MSG_META_UNDO(
        AddUndoActionStruct 	*data);

This message is sent to an object which is supposed to be the destination of a clipboard operation.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

MSG_META_UNDO_FREEING_ACTION

@importMessage MetaUIMessages, void 	MSG_META_UNDO_FREEING_ACTION(
        AddUndoActionStruct 	*data);

This message is sent to an object which is supposed to be the destination of a clipboard operation. This message is used to undo those actions which may free an important block of memory.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

MSG_META_SELECT_ALL

@importMessage MetaUIMessages, void 	MSG_META_SELECT_ALL();

This message is sent to an object which is supposed to be the destination of a clipboard operation.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

MSG_META_DELETE

@importMessage MetaUIMessages, void 	MSG_META_DELETE();

This message is sent to an object which is supposed to be the destination of a clipboard operation.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

Input Messages

These are perhaps the most often intercepted messages, allowing objects to detect input events.

MSG_META_GAINED_MOUSE_EXCL

@importMessage MetaUIMessages, void 	MSG_META_GAINED_MOUSE_EXCL();

The object will receive this message when it has received the mouse exclusive.

MSG_META_LOST_MOUSE_EXCL

@importMessage MetaUIMessages, void 	MSG_META_LOST_MOUSE_EXCL();

The object will receive this message when it has lost the mouse exclusive.

MSG_META_GAINED_KBD_EXCL

@importMessage MetaUIMessages, void 	MSG_META_GAINED_KBD_EXCL();

The object will receive this message when it has received the keyboard exclusive.

MSG_META_LOST_KBD_EXCL

@importMessage MetaUIMessages, void 	MSG_META_LOST_KBD_EXCL();

The object will receive this message when it has lost the keyboard exclusive.

MSG_META_GAINED_PRESSURE_EXCL

@importMessage MetaUIMessages, void 	MSG_META_GAINED_PRESSURE_EXCL();

The object will receive this message when it has received the pressure exclusive, meaning it will get certain low-level mouse events.

MSG_META_LOST_PRESSURE_EXCL

@importMessage MetaUIMessages, void 	MSG_META_LOST_PRESSURE_EXCL();

The object will receive this message when it has lost the pressure exclusive.

MSG_META_GAINED_DIRECTION_EXCL

@importMessage MetaUIMessages, void 	MSG_META_GAINED_DIRECTION_EXCL();

The object will receive this message when it has received the direction exclusive, meaning it will get certain low-level mouse events.

MSG_META_LOST_DIRECTION_EXCL

@importMessage MetaUIMessages, void 	MSG_META_LOST_DIRECTION_EXCL();

The object will receive this message when it has lost the direction exclusive.

Hierarchical Messages

These messages allow object to detect changes in the makeup of the three hierarchies which affect the paths of input and actions within the system: the Focus, Target, and Model hierarchies. These hierarchies are discussed in the Input chapter.

MSG_META_GRAB_FOCUS_EXCL

@importMessage MetaUIMessages, void 	MSG_META_GRAB_FOCUS_EXCL();

May be passed to any visible or generic object to cause it to become the active focus within its focus level. The leaf object in the hierarchy which gains the focus exclusive will automatically be given the keyboard exclusive, and will thereby receive MSG_META_KBD_CHAR events that follow.

Commonly sent to text objects and other gadgets to switch the current focus. May also be passed to GenPrimarys, GenDisplays, independently realizable GenInteractions, GenDisplayControl, GenViews, etc. (windowed things) to cause them to become the active focus window within their level of the focus hierarchy, if possible (specific UI's having real-estate focus, for instance, would ignore this request).

Note that the object will not actually gain the focus exclusive until all other nodes above it in the hierarchy also have the focus in their levels.

This is the message equivalent of HINT_DEFAULT_FOCUS on generic objects.

MSG_META_RELEASE_FOCUS_EXCL

@importMessage MetaUIMessages, void 	MSG_META_RELEASE_FOCUS_EXCL();

Opposite of MSG_META_GRAB_FOCUS_EXCL . If the object does not currently have the exclusive, nothing will be done.

MSG_META_GET_FOCUS_EXCL

@importMessage MetaUIMessages, Boolean 	MSG_META_GET_FOCUS_EXCL(
        optr 	*focusObject);

May be sent to any visible or generic object which is a focus node, to get current focus object directly below the node, if any, regardless of whether current node is active (has the exclusive itself).

Focus nodes in Generic UI library: GenSystem, GenField, GenApplication, GenPrimary, GenDisplayGroup, GenDisplay, GenView, GenInteraction (independently displayable only). Focus nodes in Visible UI library: VisContent.

Parameters: focusObject This will be filled with return value, the focus object below the object receiving the message.

Return: Will return true if message responded to. Will return false if the message was sent to an object which is not a focus node.

focusObject
The focus node under the object receiving the message.

Warnings: This is a bad way to go about sending a message to currently active objects. For example, if you call from the application thread to the UI thread to find out which is the current focus gadget and then send a message to it, it is possible for the active gadget to change between the two calls. Use MSG_META_SEND_CLASSED_EVENT for this type of operation if at all possible.

MSG_META_GRAB_TARGET_EXCL

@importMessage MetaUIMessages, void 	MSG_META_GRAB_TARGET_EXCL();

May be passed to any visible or generic object to cause it to become the active target within the target level that it is in. The active target hierarchy is the path for the transmission of messages via TO_TARGET request of MSG_META_SEND_CLASSED_EVENT .

Commonly sent to text objects and views to switch which is the current target. May also be passed to GenPrimarys, GenDisplays, independently realizable GenInteractions, GenDisplayControl, GenViews, etc. (windowed things) to cause them to become the active target window within their level f the target hierarchy.

The specific UI will automatically grab the Target exclusive for an object on any mouse press within the object if it is marked as GA_TARGETABLE.

Note that the object will not actually gain the target exclusive until all other nodes above it in the hierarchy also have the target exclusive within their levels. This is the message equivalent of HINT_DEFAULT_TARGET.

MSG_META_RELEASE_TARGET_EXCL

@importMessage MetaUIMessages, void 	MSG_META_RELEASE_TARGET_EXCL();

Opposite of MSG_META_GRAB_TARGET_EXCL . If the object does not currently have the exclusive, nothing will be done.

MSG_META_GET_TARGET_EXCL

@importMessage MetaUIMessages, void 	MSG_META_GET_TARGET_EXCL(
        optr targetObject);

May be sent to any visible or generic object which is a target node, to get the current target object directly below the node, if any, regardless of whether the current node is active (has the exclusive itself).

Target nodes in Generic UI library: GenSystem, GenField, GenApplication, GenPrimary, GenDisplay, GenView, GenInteraction (independently displayable only). Target nodes in Visible UI library: VisContent.

Parameters: targetObject This will be filled with return value, the target object below the object receiving the message.

Return: Will return true if message responded to. Will return false if the message was sent to an object which is not a target node.

targetObject
The target node under the object receiving the message.

Warnings: This is a bad way to go about sending a message to currently active objects. For example, if you call from the application thread to the UI thread to find out which is the current target display, and then send a message to it, it is possible for the active display to change between the two calls. Use MSG_META_SEND_CLASSED_EVENT for this type of operation if at all possible.

MSG_META_GRAB_MODEL_EXCL

@importMessage MetaUIMessages, void 	MSG_META_GRAB_MODEL_EXCL();

May be passed to any visible or generic object to cause it to become the active model within the model level that it is in. The active model hierarchy is the override path for the transmission of messages via TO_MODEL of MSG_META_SEND_CLASSED_EVENT . (If no model hierarchy exists, the messages will be sent down the Target hierarchy.)

Note that the object will not actually gain the model exclusive until all other nodes above it in the hierarchy also have the model exclusive within their levels. This is the message equivalent of HINT_MAKE_DEFAULT_MODEL.

MSG_META_RELEASE_MODEL_EXCL

@importMessage MetaUIMessages, void 	MSG_META_RELEASE_MODEL_EXCL();

Opposite of MSG_META_GRAB_MODEL_EXCL . If the object does not currently have the exclusive, nothing will be done.

MSG_META_GET_MODEL_EXCL

@importMessage MetaUIMessages, void 	MSG_META_GET_MODEL_EXCL(
        optr targetObject);

May be sent to any visible or generic object which is a model node, to get current model object directly below the node, if any, regardless of whether current node is active (has the exclusive itself).

Model nodes in Generic UI library: GenSystem, GenApplication, GenDocumentControl, GenDocumentGroup.

Parameters: modelObject This will be filled with return value, the target object below the object receiving the message.

Return: Will return true if message responded to. Will return false if the message was sent to an object which is not a target node.

modelObject
The target node under the object receiving the message.

Warnings: This is a bad way to go about sending a message to currently active objects. For example, if you call from the application thread to the UI thread to find out which is the current model display, and then send a message to it, it is possible for the active display to change between the two calls. Use MSG_META_SEND_CLASSED_EVENT for this type of operation if at all possible.

MSG_META_GAINED_FOCUS_EXCL

@importMessage MetaUIMessages, void 	MSG_META_GAINED_FOCUS_EXCL();

See description for this and other gained/lost exclusive messages below.

Special note on MSG_META_GAINED_FOCUS_EXCL and MSG_META_LOST_FOCUS_EXCL : If the object receiving MSG_META_GAINED_FOCUS_EXCL is the leaf object in the hierarchy, meaning that it is either not a node itself, or if it is a node, does not have any object below it which has grabbed the exclusive, then the object will automatically be granted the MSG_META_GAINED_KBD_EXCL as well, and thereby receive any MSG_META_KBD_CHAR messages which are generated. The object will receive MSG_META_LOST_KBD_EXCL before MSG_META_LOST_FOCUS_EXCL.

MSG_META_LOST_FOCUS_EXCL

@importMessage MetaUIMessages, void 	MSG_META_LOST_FOCUS_EXCL();

See description for this and other gained/lost exclusive messages below.

Special note on MSG_META_GAINED_FOCUS_EXCL and MSG_META_LOST_FOCUS_EXCL : If the object receiving MSG_META_GAINED_FOCUS_EXCL is the leaf object in the hierarchy, meaning that it is either not a node itself, or if it is a node, does not have any object below it which has grabbed the exclusive, then the object will automatically be granted the MSG_META_GAINED_KBD_EXCL as well, and thereby receive any MSG_META_KBD_CHAR messages which are generated. The object will receive MSG_META_LOST_KBD_EXCL before MSG_META_LOST_FOCUS_EXCL.

Tips and Tricks: If you intercept this message and call the superclass, be aware that the superclass' handler does some of the focus-releasing tasks by sending messages. This can lead to synchronization problems if your method's code looks like the following:

            @callsuper();
            call self::MSG_GEN_APPLY();

To make sure that the superclass is finished updating the focus before carrying out any operations which rely upon a valid focus, you would change the above code to read:

            @callsuper();
            @send ,forceQueue ,insertAtFront self::MSG_GEN_APPLY();

MSG_META_GAINED_SYS_FOCUS_EXCL

@importMessage MetaUIMessages, void MSG_META_GAINED_SYS_FOCUS_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_LOST_SYS_FOCUS_EXCL

@importMessage MetaUIMessages, void MSG_META_LOST_SYS_FOCUS_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_GAINED_TARGET_EXCL

@importMessage MetaUIMessages, void MSG_META_GAINED_TARGET_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_LOST_TARGET_EXCL

@importMessage MetaUIMessages, void MSG_META_LOST_TARGET_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_GAINED_SYS_TARGET_EXCL

@importMessage MetaUIMessages, void MSG_META_GAINED_SYS_TARGET_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_LOST_SYS_TARGET_EXCL

@importMessage MetaUIMessages, void MSG_META_LOST_SYS_TARGET_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_GAINED_MODEL_EXCL

@importMessage MetaUIMessages, void 	MSG_META_GAINED_MODEL_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_LOST_MODEL_EXCL

@importMessage MetaUIMessages, void MSG_META_LOST_MODEL_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_GAINED_SYS_MODEL_EXCL

@importMessage MetaUIMessages, void MSG_META_GAINED_SYS_MODEL_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_LOST_SYS_MODEL_EXCL

@importMessage MetaUIMessages, void MSG_META_LOST_SYS_MODEL_EXCL();

These paired gained/lost messages for the Focus, Target, and Model hierarchies are sent, always in the order GAINED, then at some point LOST, to objects on the hierarchy. The GAINED message is sent only when the object in question and all nodes in the hierarchy above that object have gained the exclusive from the node above them, all the way up to the application object.

In other words, just grabbing the exclusive from the next node up doesn't always guarantee you'll get a GAINED message; the node you're grabbing from must have itself received a GAINED message but not yet the LOST message. Your object will receive the LOST message if it has either released the exclusive, or the node from which you grabbed the exclusive itself received a LOST message.

The GAINED_SYS and LOST_SYS messages behave similarly, except that an object can only gain the SYS_EXCL (System exclusive) if it and all nodes above it to the GenSystem object have the grab from the next node up. An object will never receive a GAINED_SYS_EXCL message if it has not already received an (Application) GAINED_EXCL message also. Similarly, an object will always receive a LOST_SYS_EXCL message before it receives an (Application) LOST_EXCL message.

Source: Do not send these messages to objects yourself, unless you are implementing or extending the above mechanism. These messages should be sent only by the node object which is above the object receiving the message.

Destination: Any MetaClass object which has grabbed and not yet released the focus exclusive.

Interception: Generic UI objects, VisTextClass , and all node objects provide default behavior for processing this message. If you intercept above any of these levels, be sure to call the superclass to let these objects know the exclusive has been gained.

Miscellaneous Input Messages

MSG_META_GRAB_KBD

@importMessage MetaUIMessages, void MSG_META_GRAB_KBD();

This message grabs the keyboard for an object. The grab will not be taken away from another object if it currently has the keyboard grab. To forcefully grab the keyboard in this case, use MSG_META_FORCE_GRAB_KBD.

MSG_META_FORCE_GRAB_KBD

@importMessage MetaUIMessages, void MSG_META_FORCE_GRAB_KBD();

This message forcefully grabs the keyboard for an object, tasking the grab away from another object, if necessary.

MSG_META_RELEASE_KBD

@importMessage MetaUIMessages, void MSG_META_RELEASE_KBD();

This message releases the keyboard grab for an object.

MSG_META_RELEASE_FT_EXCL

@importMessage MetaUIMessages, void 	MSG_META_RELEASE_FT_EXCL();

This message releases exclusive(s) that the object may have on the Focus and Target hierarchies.

MSG_META_GAINED_DEFAULT_EXCL

@importMessage MetaUIMessages, void 	MSG_META_GAINED_DEFAULT_EXCL();

This message notifies a GenTrigger that it has gained the default exclusive.

MSG_META_LOST_DEFAULT_EXCL

@importMessage MetaUIMessages, void 	MSG_META_LOST_DEFAULT_EXCL();

This message notifies a GenTrigger that it has lost the default exclusive.

MSG_META_GAINED_FULL_SCREEN_EXCL

@importMessage MetaUIMessages, void MSG_META_GAINED_FULL_SCREEN_EXCL();

This message is sent to GenFields or GenApplications upon gain of the "full-screen" exclusive. The full-screen exclusive grants the object the top screen-dominating object at its level.

MSG_META_LOST_FULL_SCREEN_EXCL

@importMessage MetaUIMessages, void MSG_META_LOST_FULL_SCREEN_EXCL();

This message is sent to GenFields or GenApplications upon loss of the "full-screen" exclusive. The full-screen exclusive grants the object the top screen-dominating object at its level.

MSG_META_MOUSE_BUMP_NOTIFICATION

@importMessage MetaUIMessages, void 	MSG_META_MOUSE_BUMP_NOTIFICATION(
        sword 	xBump,
        sword 	yBump);

This message is an event that the input manager places in the input queue to notify the UI that it has bumped the mouse position past this point in the queue. This method is sent only when IMBumpMouse() is called.

Parameters: xBump Horizontal relative bump.

yBump
Vertical relative bump.

MSG_META_FUP_KBD_CHAR

@importMessage MetaUIMessages, Boolean 	MSG_META_FUP_KBD_CHAR(
        word 	character,
        word 	flags,
        word 	state);

When a leaf object in the focus hierarchy gets a MSG_META_KBD_CHAR, and does not care about the character, it sends this message to itself to see if a parent object wants to handle it.

Parameters: character The low byte contains a Char value.

flags
High byte is a ShiftState field; low byte is a CharFlags field.
state
High byte is the raw PC scan code; low byte is a ToggleState field.

Return: Will return true if the character was handled by someone (and should not be used elsewhere).

MSG_META_PRE_PASSIVE_KBD_CHAR

@importMessage MetaUIMessages, KbdReturnFlags 	MSG_META_PRE_PASSIVE_KBD_CHAR(
        word 	character,
        word 	flags,
        word 	state);

This message sends a keyboard character to any object requesting preview of the keyboard events.

Parameters: character The low byte contains a Char value.

flags
High byte is a ShiftState field; low byte is a CharFlags field.
state
High byte is the raw PC scan code; low byte is a ToggleState field.

Return: Flags field specifying what should happen to event.

Structures:

 typedef WordFlags 	KbdReturnFlags;
#define  KRF_PREVENT_PASS_THROUGH 0x8000
/* Set for passive keyboard routines if event should
 * be destroyed and not passed on to implied or
 * default grab. */

MSG_META_POST_PASSIVE_KBD_CHAR

@importMessage MetaUIMessages, KbdReturnFlags 	MSG_META_POST_PASSIVE_KBD_CHAR(
        word 	character,
        word 	flags,
        word 	state);

This message passes keyboard characters to all objects having registered interest in getting keyboard events after they have been handled.

Parameters: character The low byte contains a Char value.

flags
High byte is a ShiftState field; low byte is a CharFlags field.
state
High byte is the raw PC scan code; low byte is a ToggleState field.

Return: Flags field specifying what should happen to event.

Structures:

typedef WordFlags 	KbdReturnFlags;
#define KRF_PREVENT_PASS_THROUGH 0x8000
/* Set for passive keyboard routines if event should
 * be destroyed and not passed on to implied or
 * default grab. */

MSG_META_QUERY_IF_PRESS_IS_INK

@importMessage MetaUIMessages, void 	MSG_META_QUERY_IF_PRESS_IS_INK(
        InkReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition);

Return whether or not a MSG_META_START_SELECT should be passed on to the object, or whether it should be intercepted and turned into ink.

Source: Sent by any object (usually VisComp) to determine if one of its children wants ink.

Destination: Any object in the Vis linkage that may be clicked on with the mouse.

Interception: The default handler returns IRV_NO_INK (filling in the inkType field of the retVal argument). Objects that want presses to be turned into ink need to return IRV_DESIRES_INK. Some objects that need to do work on another thread (such as a GenView) to determine whether the press should be ink or not can return IRV_WAIT, which holds up the MSG_META_START_SELECT until a MSG_GEN_APPLICATION_INK_QUERY_REPLY is sent to the application object. By default, clicks on VisComp-derived objects will not be ink. To change this, set VisCompMakePressesInk() as the handler for this message.

Parameters: retVal Empty structure which will be filled with return value information.

xPosition
x -coordinate of selection start.
yPosition
y -coordinate of selection start.

Return: Indication whether object thinks the press was ink.

MSG_META_LARGE_QUERY_IF_PRESS_IS_INK

@importMessage MetaUIMessages, void MSG_META_LARGE_QUERY_IF_PRESS_IS_INK(
        InkReturnParams		*retVal,
        LargeMouseData		*largeMouseDataStruct);

This message is sent by the system to children with the VCNA_LARGE_DOCUMENT_MODEL bit set to determines whether or not a MSG_META_LARGE_START_SELECT should be processed as ink.

Source: Sent by any object (usually VisComp) to determine if one of its children wants ink.

Destination: Any object in the Vis linkage that may be clicked on with the mouse.

Parameters: retVal Pointer to an InkReturnParams structure that will be filled in by the handler for this message.

largeMouseDataStruct
Pointer to a LargeMouseData struct that stores information about the large mouse event.

Interception: The default handler returns IRV_NO_INK. Objects that want presses to be turned into ink need to return IRV_DESIRES_INK. Some objects that need to do work on another thread (such as a VisContent) to determine whether the press should be ink or not can return IRV_WAIT, which holds up the MSG_META_LARGE_START_SELECT until a MSG_GEN_APPLICATION_INK_QUERY_REPLY is sent to the application object.

Mouse Input Messages

The following messages allow an application to detect the nature and behavior of pointing devices within the system.

MSG_META_START_SELECT

@importMessage MetaUIMessages, void 	MSG_META_START_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_END_SELECT

@importMessage MetaUIMessages, void 	MSG_META_END_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_START_MOVE_COPY

@importMessage MetaUIMessages, void 	MSG_META_START_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_END_MOVE_COPY

@importMessage MetaUIMessages, void 	MSG_META_END_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_START_FEATURES

@importMessage MetaUIMessages, void 	MSG_META_START_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_END_FEATURES

@importMessage MetaUIMessages, void 	MSG_META_END_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_START_OTHER

@importMessage MetaUIMessages, void 	MSG_META_START_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_END_OTHER

@importMessage MetaUIMessages, void 	MSG_META_END_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_DRAG_SELECT

@importMessage MetaUIMessages, void 	MSG_META_DRAG_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_DRAG_MOVE_COPY

@importMessage MetaUIMessages, void 	MSG_META_DRAG_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_DRAG_FEATURES

@importMessage MetaUIMessages, void 	MSG_META_DRAG_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_DRAG_OTHER

@importMessage MetaUIMessages, void 	MSG_META_DRAG_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_BUTTON

@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_BUTTON(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_BUTTON

@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_BUTTON(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_START_SELECT

@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_START_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_START_SELECT

@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_START_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_END_SELECT

@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_END_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_END_SELECT

@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_END_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_START_MOVE_COPY

@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_START_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_START_MOVE_COPY

@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_START_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_END_MOVE_COPY

@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_END_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_END_MOVE_COPY

@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_END_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_START_FEATURES

@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_START_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_START_FEATURES

@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_START_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_END_FEATURES

@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_END_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_END_FEATURES

@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_END_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_START_OTHER

@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_START_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_START_OTHER

@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_START_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_END_OTHER

@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_END_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_END_OTHER

@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_END_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

The above messages are the standard button functions generated by the UI upon receiving MSG_META_BUTTON events from the Input Manager. These messages are sent out to whatever object has the implied grab (whichever window the mouse is over), until the mouse is "grabbed" by an object, after which the messages go there until the mouse is released (ungrabbed).

Parameters: retVal Structure to hold return values.

xPosition
x -coordinate of press.
yPosition
y -coordinate of press.
inputState
High byte is UIFunctionsActive structure; low byte is ButtonInfo structure.

Structures:

 typedef struct {
    word 			unused;
    MouseReturnFlags 			flags;
    optr 			ptrImage;
} MouseReturnParams;
  typedef WordFlags MouseReturnFlags;
/* These flags may be combined using | and &:
    MRF_PROCESSED,
    MRF_REPLAY,
    MRF_PREVENT_PASS_THROUGH,
    MRF_SET_POINTER_IMAGE,
    MRF_CLEAR_POINTER_IMAGE */

MSG_META_LARGE_PTR

@importMessage MetaUIMessages, void 	MSG_META_LARGE_PTR(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_START_SELECT

@importMessage MetaUIMessages, void 	MSG_META_LARGE_START_SELECT(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_END_SELECT

@importMessage MetaUIMessages, void 	MSG_META_LARGE_END_SELECT(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_START_MOVE_COPY

@importMessage MetaUIMessages, void 	MSG_META_LARGE_START_MOVE_COPY(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_END_MOVE_COPY

@importMessage MetaUIMessages, void 	MSG_META_LARGE_END_MOVE_COPY(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_START_FEATURES

@importMessage MetaUIMessages, void 	MSG_META_LARGE_START_FEATURES(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_END_FEATURES

@importMessage MetaUIMessages, void 	MSG_META_LARGE_END_FEATURES(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_START_OTHER

@importMessage MetaUIMessages, void 	MSG_META_LARGE_START_OTHER(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_END_OTHER

@importMessage MetaUIMessages, void 	MSG_META_LARGE_END_OTHER(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_DRAG_SELECT

@importMessage MetaUIMessages, void 	MSG_META_LARGE_DRAG_SELECT(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_DRAG_MOVE_COPY

@importMessage MetaUIMessages, void 	MSG_META_LARGE_DRAG_MOVE_COPY(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_DRAG_FEATURES

@importMessage MetaUIMessages, void 	MSG_META_LARGE_DRAG_FEATURES(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_DRAG_OTHER

@importMessage MetaUIMessages, void 	MSG_META_LARGE_DRAG_OTHER(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

Objects which have been set up with 32-bit coordinate spaces must be prepared to handle large mouse events along with regular mouse events.

These messages are available by request for use within 32-bit visible document models. Mouse position data is in full 32-bit integer, 16-bit fraction format, as generated by GenView.

Parameters: retVal Structure to hold return values.

largeMouseDataStruct
Structure to hold pass values.

Return: Nothing returned explicitly.

retVal
Filled with return values.

Structures:

  typedef struct {
    PointDWFixed 				LMD_location;
    byte 				LMD_buttonInfo;
    UIFunctionsActive 				LMD_uiFunctionsActive;
} LargeMouseData;
typedef struct {
    word 			unused;
    MouseReturnFlags 			flags;
    optr 			ptrImage;
    /* Pointer image to use, if MRF_SET_PTR_IMAGE
     * returned */
} MouseReturnParams;

MSG_META_ENSURE_MOUSE_NOT_ACTIVELY_TRESPASSING

@importMessage MetaUIMessages, MouseReturnFlags 	MSG_META_ENSURE_MOUSE_NOT_ACTIVELY_TRESPASSING();

Sent to the passive, active, or implied mouse grab chain whenever modality status changes within the system--any object receiving this message which has a window grabbed should make sure that it has a legitimate right to have the window grab active--if not, it should be released (along with the mouse). In particular, menus in stay-up mode should come down, any interaction between the mouse and primary, display, menu, or view windows should be terminated. MSG_GEN_APPLICATION_TEST_WIN_INTERACTABILITY is useful; this message will test any passed OD against the list of window(s) which the mouse is allowed to interact with (Generally, top most system modal window, else top most application modal window, else all windows), and return a flag indicating the result.

Parameters: None.

Return: Flags field which system normally ignores.

MSG_META_ENSURE_NO_MENUS_IN_STAY_UP_MODE

@importMessage MetaUIMessages, MouseReturnFlags 	MSG_META_ENSURE_NO_MENUS_IN_STAY_UP_MODE();

Sent to the passive, active/implied mouse grab chain whenever we want to make sure all of an application's menus are closed. Sent directly to the Flow object from the global shortcut code. Any menus receiving this message which are in stay-up mode should dismiss themselves.

Parameters: None.

Return: Flags field, normally ignored by system.

MSG_META_ENSURE_ACTIVE_FT

@importMessage MetaUIMessages, void 	MSG_META_ENSURE_ACTIVE_FT();

Makes sure that some object with the Focus/Target node to which this message may be sent has the Focus and Target exclusives. Called from within the UI, usually when windowed objects below the node have closed, or moved to the back, to give the Focus and/or Target to the most suitable window.

Behavior as implemented in GenApplicationClass :
Checks to make sure that something within the application has the Focus and Target exclusives. Called from within the UI, usually on the closure of a window, to give the Focus and/or Target to the next best location.

Typical click-to-type model is implemented using the following rules:
For Target, the priority order is:

  1. Anything already having the exclusive.
  2. Top targetable PRIO_STD priority level window.
  3. Top targetable PRIO_COMMAND priority level window.

For Focus, priority goes to:

  1. Anything already having the exclusive.
  2. Top system modal window.
  3. Top application modal window.
  4. Last non-modal window to have or request the exclusive.
  5. Window having Target exclusive.
  6. Top focusable PRIO_COMMAND priority level window.

Source: Most always internally from the UI, though is unrestricted.

Destination: Focus/Target node, specifically: GenSystem, GenField, or GenApplication object.

Interception: No reason to intercept. Default behavior is provided by above objects. Could possibly be replaced, but as default behavior varies by specific UI, results could be unpredictable.

Parameters: None.

Return: Nothing.

MSG_META_NOTIFY_NO_FOCUS_WITHIN_NODE

@importMessage MetaUIMessages, void 	MSG_META_NOTIFY_NO_FOCUS_WITHIN_NODE();

Notification from Focus node MSG_META_ENSURE_ACTIVE_FT handler that it was unable to keep/find an object below it suitable for being the focus. The most likely cause is that the last focusable geode/object running below this point has been shut down/closed.

Source: Focus node, MSG_META_ENSURE_ACTIVE_FT handler

Destination: Self

Interception: Intercepted to find something safe to do for the user, such as push this field/application to the back, or mark this object as no longer "focusable" and call MSG_META_ENSURE_ACTIVE_FT on the node above this one, in an attempt to find something for the user to access. If there's nothing left at all in the system, the last focusable application has exited, so it's time to shut down.

Parameters: None.

Return: Nothing.

Miscellaneous Meta Messages

MSG_META_UI_FORCE_CONTROLLER_UPDATE

@importMessage MetaUIMessages, void MSG_META_UI_FORCE_CONTROLLER_UPDATE(
        ManufacturerID		manufID,
        word		changeID);

This message forces an object to update one or all of the GCN notification lists that it communicates with.

Source: Usually sent by a controller to its output.

Destination: Any object.

Parameters: manufID ManufacturerID of GCN lists.

changeID Notification list ID.
This value may be 0xffffh if all notification lists should be updated or 0xfffeh to generate the standard notifications.

Interception: Objects that send notification for controllers should respond to this message.

MSG_META_GEN_PATH_RESTORE_DISK_PROMPT

@importMessage MetaUIMessages, Boolean MSG_META_GEN_PATH_RESTORE_DISK_PROMPT(
        DiskRestoreError				*error,
        GenPathDiskRestoreArgs				*args);

This message prompts the user to insert a particular disk into a particular drive when restoring a disk handle for the object's path.

Source: Sent by the callback passed to DiskRestore() when a disk handle saved in an object's path is being restored after a shutdown.

Destination: Any object possessing a path.

Parameters: error Pointer to store an error code. This error code will be returned to DiskRestore() .

args
GenPathDiskRestoreArgs .

Structures:

typedef struct {
    word			GPDRA_pathType;
    word			GPDRA_savedDiskType;
    char			*GPDRA_driveName;
    char			*GPDRA_diskName;
    DiskRestoreError			GPDRA_errorCode;
} GenPathDiskRestoreArgs;

GPDRA_ pathType stores the vardata tag holding the path.

GPDRA_ savedDiskType stores the vardata tag holding the saved disk handle.

GPDRA_ driveName and GPDRA_ diskName store pointers to the null-terminated drive and disk names.

GPDRA_ errorCode stores the error code that is returned to DiskRestore() .

Interception: May be intercepted if the object has more information to provide to the user, or if the object doesn't wish to prompt the user. If this message is intercepted, it should not call its superclass.

MSG_META_PAGED_OBJECT_GOTO_PAGE

@importMessage MetaUIMessages, void MSG_META_PAGED_OBJECT_GOTO_PAGE(
        word	page);

This message instructs a GenDocument to go to the passed page.

This message is sent out by the GenPageControl object and is handled by a GenApplication's subclassed GenDocument object.

Source: GenPageControl object.

Destination: GenDocument object.

Parameters: page Page to set the GenDocument to display.

Interception: You may intercept to provide custom paging behavior.

MSG_META_PAGED_OBJECT_NEXT_PAGE

@importMessage MetaUIMessages, void MSG_META_PAGED_OBJECT_NEXT_PAGE();

This message instructs a GenDocument to go to the next page.

This message is sent out by the GenPageControl object and is handled by a GenApplication's subclassed GenDocument object.

Source: GenPageControl object.

Destination: GenDocument object.

Interception: You may intercept to provide custom paging behavior.

MSG_META_PAGED_OBJECT_PREVIOUS_PAGE

@importMessage MetaUIMessages, void MSG_META_PAGED_OBJECT_PREVIOUS_PAGE();

This message instructs a GenDocument to go to the previous page.

This message is sent out by the GenPageControl object and is handled by a GenApplication's subclassed GenDocument object.

Source: GenPageControl object.

Destination: GenDocument object.

Interception: You may intercept to provide custom paging behavior.

MSG_META_DELETE_RANGE_OF_CHARS

@importMessage MetaUIMessages, void MSG_META_DELETE_RANGE_OF_CHARS(@stack
        VisTextRange		rangeToDelete);

This message instructs an object to delete a range of characters passed in a VisTextRange . Generally, this message is sent out when the user crosses out characters within a HWR grid.

Source: GenPenInputControl.

Destination: Any focused object.

Parameters: rangeToDelete VisTextRange of characters to delete. Objects that are not text objects will need to know how to interpret this value.

Interception: May intercept to provide custom deletion behavior.

MSG_META_NOTIFY_TASK_SELECTED

@importMessage MetaUIMessages, void MSG_META_NOTIFY_TASK_SELECTED();

This message is sent when a task list item of an application in the Express Menu is selected. The default behavior brings the application to the front and gives it the focus.

MSG_META_FIELD_NOTIFY_DETACH

@importMessage MetaUIMessages, void MSG_META_FIELD_NOTIFY_DETACH(
        optr		field,
        word		shutdownFlag);

This message is sent by the GenField object when it is detaching.

Source: GenField.

Destination: The notification destination of the GenField object.

Parameters: field Optr of the GenField sending notification.

shutdownFlag
true if the GenField is detaching because of a shutdown.

Interception: The object receiving notification may handle as desired. As this is a notification only, you should not call the superclass.

MSG_META_FIELD_NOTIFY_NO_FOCUS

@importMessage MetaUIMessages, void MSG_META_FIELD_NOTIFY_NO_FOCUS(
        optr		field,
        word		shutdownFlag);

This message is sent by the GenField when it no longer has any applications in the focus hierarchy.

Source: GenField.

Destination: The notification destination of the GenField object.

Parameters: field Optr of the GenField sending notification.

shutdownFlag
true if the GenField lost its focus applications because of a shutdown.

Interception: The object receiving notification may handle as desired. As this is a notification only, you should not call the superclass.

MSG_META_FIELD_NOTIFY_START_LAUNCHER_ERROR

@importMessage MetaUIMessages, void MSG_META_FIELD_NOTIFY_START_LAUNCHER_ERROR(
        optr		field);

This message is sent by the GenField when an error occurs while attempting to run the launcher for the field object.

Source: GenField.

Destination: The notification destination of the GenField object.

Parameters: field Optr of the GenField sending notification.

Interception: The object receiving notification may handle as desired. As this is a notification only, you should not call the superclass.

MSG_META_TEST_WIN_INTERACTIBILITY

@importMessage MetaUIMessages, Boolean MSG_META_TEST_WIN_INTERACTIBILITY(
        optr		inputOD,
        WindowHandle		window);

This message checks whether a pointing device (usually a mouse) can interact with the passed window.

Source:

Destination: A windowed object.

Parameters: inputOD Input optr of the windowed object to check.

window
Window to check.

Return: true if window is interactable.

MSG_META_CHECK_IF_INTERACTIBLE_OBJECT

@importMessage MetaUIMessages, Boolean MSG_META_CHECK_IF_INTERACTIBLE_OBJECT(
        optr	obj);

This message is sent o objects on the GAGCNLT_ALWAYS_INTERACTABLE_WINDOWS GCN list.

Source: GenApplication object.

Destination: Objects on the GAGCNLT_ALWAYS_INTERACTABLE_WINDOWS GCN list.

Parameters: obj Object whose interactable state is being checked.

Return: true if object is interactable.

Interception: May intercept.

| Up: GEOS SDK TechDocs | Up | Prev: 3.2 Input Messages | Next: 3.4 Standard GCN Messages


Standard GCN Messages

MetaClass: 3.4 Exported Message Ranges: Standard GCN Messages

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 UI Messages | Next: 3.5 IACP Meta Messages

There are several standard messages which objects adding themselves to the appropriate GCN lists may receive and handle.

MSG_NOTIFY_FILE_CHANGE

@importMessage MetaGCNMessages, void 	MSG_NOTIFY_FILE_CHANGE(
        MemHandle 		data);

This notification is sent out whenever the file system changes in any way.

Source: GCN mechanism.

Destination: Object on the GCNSLT_FILE_SYSTEM GCN list.

Parameters: data Handle of a FileChangeNotificationData block.

Return: Nothing.

Structures:

  typedef struct {
    PathName			FCND_pathname;
    DiskHandle			FCND_diskHandle;
    FileChangeType			FCND_changeType;
} FileChangeNotificationData;
  typedef ByteEnum FileChangeType;
/* These flags may be combined using | and &:
    FCT_CREATE
    FCT_DELETE
    FCT_RENAME
    FCT_CONTENTS
    FCT_DISK_FORMAT */

MSG_NOTIFY_DRIVE_CHANGE

@importMessage MetaGCNMessages, void 	MSG_NOTIFY_DRIVE_CHANGE(
        GCNDriveChangeNotificationType 						type,
        word 						driveNum);

This is sent to notify various system utilities that a drive has been created or destroyed or has changed ownership from one installable file system driver to another.

Note that during system initialization, the ownership of a drive may change several times as more-specialized drivers are loaded. This means the recipient should not be surprised if it's told a drive has been created that it thought already existed.

Source: The kernel issues this notification whenever a filesystem driver creates or destroys a drive it manages. This includes when a specialized filesystem driver takes control of an existing drive.

Destination: Any object that has added itself to the GCNSLT_FILE_SYSTEM GCN list. It is intended for system objects, such as the GenFileSelector.

Parameters: type GCNDriveChangeNotificationType .

driveNum
Number of the affected drive.

Return: Nothing.

MSG_NOTIFY_APP_STARTED

@importMessage MetaGCNMessages, void 	MSG_NOTIFY_APP_STARTED();

This message is sent out when an application attaches to the UI.

Source: GCN Mechanism.

Destination: Any object on the GCNSLT_APPLICATION system GCN list.

Parameters: None.

Return: Nothing.

MSG_NOTIFY_APP_EXITED

@importMessage MetaGCNMessages, void 	MSG_NOTIFY_APP_EXITED();

This message is sent out when an application thread exits.

Source: GCN Mechanism.

Destination: Any object on the GCNSLT_APPLICATION system GCN list.

Parameters: None.

Return: Nothing.

MSG_NOTIFY_DATE_TIME_CHANGE

@importMessage MetaGCNMessages, void 	MSG_NOTIFY_DATE_TIME_CHANGE();

This message is sent out when the date or time changes--whenever the system comes back or the system time is altered (e.g. by the User in Preferences).

Source: GCN Mechanism.

Destination: Any object on the GCNSLT_DATE_TIME system GCN list.

Parameters: None.

Return: Nothing.

MSG_NOTIFY_USER_DICT_CHANGE

@importMessage MetaGCNMessages, void 	MSG_NOTIFY_USER_DICT_CHANGE(
        MemHandle 		sendingSpellBox,
        MemHandle 		userDictChanged);

This message is sent out when an application attaches to the UI.

Source: GCN Mechanism.

Destination: Any object on the GCNSLT_DICTIONARY system GCN list.

Parameters: sendingSpellBox Handle of SpellBox that sent out the notification.

userDictChanged
Handle of user dictionary that changed.

Return: Nothing.

MSG_NOTIFY_KEYBOARD_LAYOUT_CHANGE

@importMessage MetaGCNMessages, void 	MSG_NOTIFY_KEYBOARD_LAYOUT_CHANGE();

This message is sent out when the keyboard layout is changing. Usually this involves a change in status of the floating keyboard. When passing this event to GCNListSend() , you must be sure to pass the GCNLSF_FORCE_QUEUE flag. (Otherwise, if you have a GenPenInputControl running on the same thread, it may try to remove itself from the list while you are sending this message.)

Source: GCN Mechanism.

Destination: Any object on the GCNSLT_KEYBOARD_OBJECT system GCN list.

Parameters: None.

Return: Nothing.

MSG_NOTIFY_EXPRESS_MENU_CHANGE

@importMessage MetaGCNMessages, void 	MSG_NOTIFY_EXPRESS_MENU_CHANGE(
        GCNExpressMenuNotificationTypes 	type,
        optr 		affectedField);

This message is sent to notify various system utilities that an express menu has been created or destroyed. The recipient receives the optr of the field to which the affected express menu belongs, as all access to the express menu is via messages sent to the field.

Source: The UI issues this notification whenever a GenField object creates or destroys its express menu.

Destination: Any object that has added itself to the GCNSLT_EXPRESS_MENU. GCN list. It is intended for system utilities, such as the print spooler or a task-switching driver, that need to add objects to each express menu in the system.

Parameters: type What happened to the field.

affectedField
Which field of the menu was affected. (This will not be the optr of the express menu itself.)

Return: Nothing.

Structures:

  typedef enum {
    GCNEMNT_CREATED,
    GCNEMNT_DESTROYED
} GCNExpressMenuNotificationTypes;

MSG_PRINTER_INSTALLED_REMOVED

@importMessage MetaGCNMessages, void 	MSG_PRINTER_INSTALLED_REMOVED();

This message is sent whenever a printer is installed or removed. The recipient of this message might call SpoolGetNumPrinters() to determine if any printers or fax machines are currently installed.

Source: GCN Mechanism.

Destination: Any object on the GCNSLT_INSTALLED_PRINTERS system GCN list.

Parameters: None.

Return: Nothing.

MSG_META_CONFIRM_SHUTDOWN

@importMessage MetaGCNMessages, void 	MSG_META_CONFIRM_SHUTDOWN(
        GCNShutdownControlType 	type);

This message is sent out when the system is about to shut down.

All applications which need to keep the system from shutting down must add themselves to GCNSLT_SHUTDOWN_CONTROL and handle this message.

Source: The task switch mechanism, through GCN.

Destination: Any object on the GCNSLT_SHUTDOWN_CONTROL system GCN list.

Parameters: type The type of shutdown occurring. See description under Interception for more information.

Return: Nothing.

Interception: If the system is about to be suspended or shut down (if the passed GCNShutDownControlType is GCNSCT_SUSPEND or GCNSCT_SHUTDOWN), then any object receiving this message must call SysShutdown(), passing SST_CONFIRM_START before it puts up any dialog box it uses to ensure the user isn't doing something foolish. If SysShutdown() returns true (indicating something has already denied the shutdown request), the caller should not put up its confirmation box, nor need it call SysShutdown() again.
Once the object has received a response from the user, either affirmative or negative, it must call SysShutdown(), passing SST_CONFIRM_END with either TRUE or FALSE as appropriate. This will allow any other confirmations to happen, as well as sending the final result to the original caller of SysShutdown().
If the passed control type is GCNSCT_UNSUSPEND, no response is required.

See Also: SysShutdown()

| Up: GEOS SDK TechDocs | Up | Prev: 3.3 UI Messages | Next: 3.5 IACP Meta Messages


IACP Meta Messages

MetaClass: 3.5 Exported Message Ranges: IACP Meta Messages

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Standard GCN Messages

IACP is fully discussed in the Applications and Geodes chapter.

MSG_META_IACP_PROCESS_MESSAGE

@importMessage MetaIACPMessages, void MSG_META_IACP_PROCESS_MESSAGE(
        EventHandle		msgToSend,
        TravelOption		topt,
        EventHandle		completionMsg);

This message dispatches an IACP message to its proper destination, sending a completion message back when that has finished.

Source: IACPSendMessage() .

Destination: Any object registered as an IACP server, or the GenApplication object of a geode that is a client of such a server.

Parameters: msgToSend EventHandle of recorded message that the other side of the connection is actually sending.

topt
TravelOption (or -1 if msgToSend should be dispatched via MessageDispatch() o r delivered via MSG_META_SEND_TO_CLASSED_EVENT.
completionMsg
EventHandle of recorded message to send when the message in msgToSend has been handled. If null, then no completion message will be sent.

Interception: if you have an object other than the GenApplication object that is an IACP server, you will need to intercept this message. You do not want to pass it on to the superclass in this case; usually, you will just want to call IACPProcessMessage() .

MSG_META_IACP_NEW_CONNECTION

@importMessage MetaIACPMessages, void MSG_META_IACP_NEW_CONNECTION(
        MemHandle		appLaunchBlock,
        Boolean		justLaunched,
        IACPConnection		connection);

This message informs servers that a new client has connected to the server.

Source: IACPConnect() .

Destination: Any object registered as an IACP server.

Parameters: appLaunchBlock Handle of AppLaunchBlock passed to IACPConnect() . Do not free this block.

justLaunched
true if the recipient was just launched (i.e. it received the AppLaunchBlock in its MSG_META_ATTACH call).
connection
IACPConnection that is now open.

Interception: Must intercept if you want to do anything about receiving the new client; there is not default handler for this message. If you do not intercept this message, no harm is done.

MSG_META_IACP_LOST_CONNECTION

@importMessage MetaIACPMessages, void MSG_META_IACP_LOST_CONNECTION(
        IACPConnection		connection,
        word		serverNum);

This message informs a server (or client) that one of its clients (or servers) has shut down.

Source: IACPShutdown() .

Destination: Any object registered as an IACP server, or the GenApplication object of a geode who is a client of such.

Parameters: connection IACPConnection being closed.

serverNum
Server number that shut down, or 0 if this was a client that shut down (and thus it is a server being notified through this message).

Interception: Must be intercepted to provide custom behavior upon losing a connection, as there is no default handler for this message. IACPLostConnection() is a good routine for servers to call to ensure that connections don't linger after a client has shut down its end.

MSG_META_IACP_SHUTDOWN_CONNECTION

@importMessage MetaIACPMessages, void MSG_META_IACP_SHUTDOWN_CONNECTION(
        IACPConnection		connection);

This message shuts down the appropriate side of the indicated connection.

Source: IACPLostConnection() , though after a delay.

Destination: Any IACP server object.

Parameters: connection IACPConnection to shutdown.

Interception: Must be intercepted to finish the work of a call to IACPLostConnection() . Call IACPShutdownConnection() to get default handling of this message.

MSG_META_IACP_DOC_OPEN_ACK

@importMessage MetaIACPMessages, void MSG_META_IACP_DOC_OPEN_ACK(
        IACPDocOpenAckParams			*params);

This message is sent when a document has been opened; the document must have previously been passed in the AppLaunchBlock when the IACP connection was made. The optr of the GenDocument object managing the document is passed so that messages can be sent to it explicitly, though these messages must always be sent via IACP (with a TravelOption of -1) to allow the application to exit at any time.

Source: GenDocumentGroup.

Destination: IACP client (usually the GenApplication object of the client application).

Parameters: params Pointer to an IACPDocOpenAckParams structure.

Structures:

typedef struct {
    optr			IDOAP_docObj;
    IACPConnection			IDOAP_connection;
    word			IDOAP_serverNum;
} IACPDocOpenAckParams;

IDOAP_ docObj stores the optr of the document object managing the document.

IDOAP_ connection stores the IACPConnection over which the open request was received.

IDOAP_ serverNum stores the server number of the GenApplication object acting as the document object's server, or zero if the connection is through some other object.

Interception: No default handler is defined. You must intercept this message to provide custom behavior.

MSG_META_IACP_DOC_CLOSE_ACK

@importMessage MetaIACPMessages, void MSG_META_IACP_DOC_CLOSE_ACK(
        IACPDocCloseAckParams				*params);

This message acts as the acknowledgment sent by a GenDocument object after it successfully processes MSG_GEN_DOCUMENT_CLOSE. Documents opened via IACP always operate in transparent mode; i.e. if you close a dirty file, it will be saved. If you don't want this behavior, you will have to send a message to revert the document.

Source: GenDocument object.

Destination: IACP client.

Parameters: params Pointer to a IACPDocCloseAckParams structure.

Structures:

typedef struct {
    optr			IDCAP_docObj;
    IACPConnection			IDCAP_connection;
    word			IDCAP_serverNum;
    word			IDCAP_status;
} IACPDocCloseAckParams;

IDCAP_ docObj stores the optr of the document object that was managing the document.

IDCAP_ connection stores the IACPConnection over which the close request was received.

IDCAP_ serverNum stores the server number of the GenApplication object acting as the document object's server, or zero if the connection is through some other object.

IDCAP_ status stores the DocQuitStatus of the close operation.

Interception: No default handler is defined. You must intercept this message to provide custom behavior.

| Up: GEOS SDK TechDocs | Up | Prev: 3.4 Standard GCN Messages


The Process Object

| Up: GEOS SDK TechDocs | Up

ProcessClass implements all the functionality for creating and managing the process aspect of a geode. It creates the process thread and associated message queues. ProcessClass is a subclass of MetaClass and is the superclass of GenProcessClass , below. You will probably not use ProcessClass directly, but you will almost certainly use GenProcessClass .

ProcessClass has no instance data fields of its own. The messages defined by it are listed below:

MSG_PROCESS_NOTIFY_PROCESS_EXIT

void	MSG_PROCESS_NOTIFY_PROCESS_EXIT(
        GeodeHandle 		exitProcess,
        word		exitCode);

This is sent to a Process object when one of its child processes exits. Many types of processes do not need to know when a child process exists; these processes need not handle this message.

Source: Kernel

Destination: Process of creating geode of child process exiting.

Parameters: This message is provided as notification only, i.e. there is no default handling of it. May be intercepted as desired.

Parameters: exitProcess Child process that exited.

exitCode
Exit code. May be an error code.

Return: Nothing.

MSG_PROCESS_NOTIFY_THREAD_EXIT

void	MSG_PROCESS_NOTIFY_THREAD_EXIT(
        ThreadHandle		exitProcess,
        word		exitCode);

This message is sent to a Process object when a thread owned by it (via the ThreadCreate() routine) exits.

Source: Kernel.

Destination: Process owning thread which is exiting.

Interception: This message is provided as notification only (i.e. there is no default handling of it). May be intercepted as desired.

Parameters: exitProcess Handle of thread that exited.

exitCode
Exit code (may be an error code).

Return: Nothing.

MSG_PROCESS_MEM_FULL

void	MSG_PROCESS_MEM_FULL(
        word 	type);

This message is sent to a Process object by the memory manager when the heap is getting full. A Process object receiving this message should try to free memory (or mark it discardable) if possible.

Source: Kernel's heap manager.

Destination: All processes.

Interception: Any process which can adjust the amount of memory that it is using should respond to this message by reducing its demands on the system heap. For instance, buffers or UI object trees kept purely for performance reasons could be freed or reduced in number.

Parameters: type HeapCongestion .

Return: Nothing.

Structures:

  typedef enum {
    /* HC_SCRUBBING:
     * Heap is being scrubbed. */
    HC_SCRUBBING,
    /* HC_CONGESTED:
     * Scrubber couldn't free a satisfactory
     * amount of memory. */
    HC_CONGESTED,
    /* HC_DESPERATE:
     * Heap is perilously close to overflowing. */
    HC_DESPERATE
} HeapCongestion;

MSG_PROCESS_CREATE_UI_THREAD

Boolean	MSG_PROCESS_CREATE_UI_THREAD(
        ThreadHandle		*newThread,
        ClassStruct 		*class,
        word 		stackSize);

This is a low-level utility message requesting that the process create the UI thread for an application. Does nothing more than MSG_PROCESS_CREATE_EVENT_THREAD , but is split out to allow for interception or to change class or stack size.

Source: First thread of application process if geode has one or more resources marked as "ui-object" blocks.

Destination: First thread of application process.

Interception: May be intercepted to change class or stack size before calling superclass, or to replace default handling completely.

Parameters: newThread Pointer to a ThreadHandle buffer to store the created thread handle.

class
Object class for the new thread. If you don't have any special messages to handle in this thread, besides those intended for objects run by the thread, you can just specify ProcessClass as the object class.
stackSize
Stack size for the new thread (3K bytes is probably reasonable).

Return: true if the thread was not created because of some problem.

Warnings: Be careful of deadlock situations.

MSG_PROCESS_CREATE_EVENT_THREAD

Boolean	MSG_PROCESS_CREATE_EVENT_THREAD(
        ThreadHandle		*newThread
        ClassStruct 		*class,
        word		stackSize);

This message is a utility that creates a new event-driven thread owned by the recipient Process object. Typically, a Process object will send this message to itself when it needs an additional event thread. (It cannot be used to create a non-event driven thread. Use ThreadCreate() for this purpose instead.) This is implemented at ProcessClass and takes care of all the details of creating a new event-driven thread. The thread will always receive a MSG_META_ATTACH as its first event.

Source: Unrestricted.

Destination: Any process. This process will own the thread.

Interception: Not necessary, as the default handler provides the message's utility.

Parameters: newThread Pointer to a ThreadHandle buffer to store the created thread handle.

class
Object class for the new thread. If you don't have any special messages to handle in this thread besides those intended for objects run by the thread, you can just specify ProcessClass as the object class.
stackSize
Stack size for the new thread. 512 bytes is probably reasonable. If the thread will be running any objects that can undergo keyboard navigation (like dialog boxes and triggers and so forth), you'll probably want to make it 1K. The kernel already adds some extra space for handling interrupts (about 100 bytes).

Return: true if the thread was not created because of some problem.

Warnings: Be careful of deadlock situations.

| Up: GEOS SDK TechDocs | Up


Tools

GEOS SDK Documentation: Tools

| Up: GEOS SDK TechDocs

The following documents contain information specific to the Nokia 9000i Communicator device.

Tools Usage
Usage of the compilation tools and some utilities.
GP File Keywords
Keywords for .gp files, used to specify the Glue link tool's behavior.
Introduction to Swat
Swat, the programmable GEOS debugger.
Swat Command Reference
Listing of commands written for Swat, the programmable GEOS debugger.
Swat Tcl
The language which underlies Swat is a flavor of Tcl.
INI File
Describes many of the categories and fields of the GEOS.INI file.
Resource Editor
This tool is used to localize a program's strings and graphics.
Icon Editor
This specialized graphics program writes out Goc source code for bitmap graphic monikers.
| Up: GEOS SDK TechDocs


Icon Editor

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Creating Icons
| One-page Version of this Chapter for Easy Printing

The Icon Editor tool allows you to create icons for your application, both file icons used by file manager applications and simple tool monikers which you may use to provide graphic monikers for a geode's UI gadgetry.

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Creating Icons
| One-page Version of this Chapter for Easy Printing


Creating Icons

Icon Editor: 1 Creating Icons

| Up: GEOS SDK TechDocs | Up | Prev: Icon Editor | Next: 2 Importing Icons

The icons produced by the icon editor are stored in an "icon database" so that they can be viewed and retrieved easily. The icon database is the Icon Editor's "document", so New/Open, Close, Save As, Make Backup and other Document-control commands will operate on the current database. "Save" actually saves the currently edited icon into the current icon database, and saves the database as well.

There are 3 standard kinds of icons in use in the system. Each standard type of icon has 3 default formats: One for VGA screens, one for MCGA (monochrome VGA) screens, and one for CGA screens.

Most icons are used in one of the following ways:

  • File Icons, which are used by GeoManager to display and launch applications. The sizes are 48x30, 48x30 (monochrome), 48x14, 32x20, and 32x20(monochrome).
  • Tool Icons, which are used by controllers in their toolbox UI. The format sizes are 15x15 and 15x10.
  • Mouse Pointers, which are used to provide a cursor tracking the mouse's movement. This format has one standard size:16 x 16.
  • Icons which will appear in the OmniGo's Home Screen application are 16x16 and monochrome.

You may also create custom icons, which start with 1 default format. Custom icons are limited to 1024 pixels wide or tall, and the formats cannot be larger than 64k.

The first time the icon editor is started it will create a blank file icon named "untitled." After that, if there is an icon database available to the icon editor, the icon editor will start with the last icon that was being edited before the editor was shut down.

To create a new icon, choose "Create New Icon" from the Icon menu. The dialog will present 4 options for the type of icon. If you decide to create a custom icon, you should enter the height, width and color scheme for the icon in the provided fields.

| Up: GEOS SDK TechDocs | Up | Prev: Icon Editor | Next: 2 Importing Icons


Importing Icons

Icon Editor: 2 Importing Icons

| Up: GEOS SDK TechDocs | Up | Prev: 1 Creating Icons | Next: 3 Editing Icons

There are 2 other ways to create icons. You can import a graphic such as a GIF, TIF, BMP or other supported file format for use as an icon, provided that it is less than 64k (which is the limit for GEOS monikers). You do this using the "Import Graphic" dialog in the Icon menu.

The other way to make an icon in the icon database is to import a moniker or moniker group from the token database, which is the cache of file monikers kept by GeoManager. Select "Token Database" from the Icon menu to get a list of monikers that you may import.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Creating Icons | Next: 3 Editing Icons


Editing Icons

Icon Editor: 3 Editing Icons

| Up: GEOS SDK TechDocs | Up | Prev: 2 Importing Icons | Next: 4 Writing Source Code

There are a number of features that are useful when editing icons:

  • You can create a format for the icon, and scale it to help create the other formats, using the "Transform Format" dialog under the Format menu.
  • The pixel-edit size can be set using the Options menu. It defaults to 8-bit-wide pixels.
  • The "Preview" dialog allows you to view a format in a generic object. If the format is intended for use in a generic object, you can see what it looks like inverted, and (in the case of triggers and tools) what it looks like with different background colors.
  • You can add your own formats, or delete existing ones, using commands from the Format menu. You might, for example, want to add a SuperVGA format for the icon (system supervga file-icons are 64x40).
  • 256-color icons are technically supported, but no special tools are provided for editing them. You can select the red, green, and blue components of the area or line color from the "Area Color" and "Line Color" dialogs. 256-color icons aren't much use unless either:
    • They use colors only from the system's default palette, or
    • They are being used in a 24-bit color environment.
  • You can select the aspect ratio for your icon. This will allow the system to make the best choice among formats when selecting from your moniker list. The choices are CGA, EGA, and VGA. The "VGA" aspect ratio should be used for VGA, MCGA (monochrome VGA), and SVGA formats.
  • You are not required to create multiple formats for your artwork; however we recommend that if you are creating art that could be used under many video modes that you create the three standard formats for the art (VGA, mono VGA, CGA). This shouldn't be too difficult, since there are several tools for working with several formats:
    • The "Transform Format" dialog can be used to create exact copies, or transformed copies, of one format to another.
    • The "Resize Format" dialog changes the size and shape of a single format.
    • The "Flip Format" dialog allows you to flip an icon format horizontally or vertically, or to rotate it 90 degrees.
| Up: GEOS SDK TechDocs | Up | Prev: 2 Importing Icons | Next: 4 Writing Source Code


Writing Source Code

Icon Editor: 4 Writing Source Code

| Up: GEOS SDK TechDocs | Up | Prev: 3 Editing Icons | Next: 5 Icon Databases

When the icon is finished, make sure it's saved (File->Save), and then choose "Write Source Code" from the Icon Menu. Follow these steps to get source code:

  1. Enter a DOS 8.3 filename for the source file
  2. To write only the current format, leave the "Formats" group at "Current Format"
  3. To write source for all the formats, choose "All Formats"
  4. Select GOC source (assembler is used in-house)

A dialog should appear when the source code has been written, saying "Source Code Written Successfully". If a problem is encountered in creating or writing a file, check the permissions on your document directory, and make sure you have enough disk space for the text file (only a few hundred bytes should be necessary).

The file should appear in your document directory. It's a text file with GOC moniker source code. This source code can be included in your application after you make the following changes:

  • The formats for the icon will have names like "Moniker0". You should give them appropriate names.
  • The monikers must be placed in a resource. You could place them between statements like:
    • @start APPMONIKERRESOURCE, data;
    @end APPMONIKERRESOURCE;

    In your .gp file, this resource should have the attributes "lmem, read-only, shared".

  • You may wish to add the line "style = tool;" for icons that are going to be used as toolbox UI, to assist the specific UI in deciding how to draw them.
  • Your .goc file must #include <gstring.h>, an include file in which the graphics string data structures used to describe visual monikers are described. (Esp programs include gstring.def .)
| Up: GEOS SDK TechDocs | Up | Prev: 3 Editing Icons | Next: 5 Icon Databases


Icon Databases

Icon Editor: 5 Icon Databases

| Up: GEOS SDK TechDocs | Up | Prev: 4 Writing Source Code | Next: 6 Exporting to Database

The icon editor can work with more than one icon database at a time via the Multiple Document Interface standard. Each database has its own window, each window having four views. The leftmost view is the database. The top-right view is the "Pixel View", the main editing area. The two remaining views are next to each other in the lower-right portion of the window. The left view is the actual-size view of the icon, and is editable. The right window is the format list, showing all formats of the icon currently being edited.

Double-clicking an icon in the database viewer will select it for editing. The currently-edited icon is important in the following ways:

  • It is (of course) the only icon being edited in the database associated with that window.
  • Its size determines the shapes of the various views, which will try to adjust themselves to fit in the window as best they can.
  • It is the icon that appears in the "Preview" dialog.
  • It is the icon used in the "Export to Token database" dialog (explained below).
  • It is the target of all operations in the Icon and Format menus (except "create new icon").

The database viewer supports all clipboard operations except for Undo. Icons may be selected and moved or copied between databases. This can be done via the Edit menu or quick-transfer.

The format list is used to switch editing to a different format in the same icon. This can only be done with the mouse.

To get more room for editing, use the Options menu to temporarily hide the database viewer or format list while editing.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Writing Source Code | Next: 6 Exporting to Database


Exporting to Database

Icon Editor: 6 Exporting to Database

| Up: GEOS SDK TechDocs | Up | Prev: 5 Icon Databases

This is the function that most people associate with an icon editor. The token database is a cache of moniker lists used by GeoManager and other applications. The token database exists so that every time an application's moniker is needed, it is not necessary to start the application in engine mode to request the moniker (a slow process).

The icon editor can change entries in the token database. You cannot change the "built in" moniker for a geode using the icon editor. If you make changes to the token database and then delete the database, then when it gets rebuilt your monikers will no longer be there.

Given that, it's still likely that people will want to set their own monikers in the token database; the icon editor has this ability.

To change an entry:

  • Make sure you are editing the icon you wish to change.
  • Open the File->Export to Token Database dialog.
  • One of the icon's formats should be visible under the "Change To:" text in the dialog. If not, then you are probably editing the wrong icon, or perhaps the icon hasn't been saved into the database.
  • Use the file selector to find the application, document, library, or other driver whose token database entry you wish to change. Otherwise, enter the four token characters for the geode into the Token Characters text field. Select the corresponding operation from the list at the lower-right of the dialog ("Use File Selector" or "Use Token Chars.")
  • The current token-db entry for the selected geode should appear under the "Current Icon" title at the upper-right corner of the dialog.
  • When you are satisfied with the edited icon, change the moniker with the "Change" trigger in the dialog's reply bar. The change will not take effect in GeoManager until you select Disk->Rescan Drives in GeoManager.
  • If you wish to revert a token-database entry to the original, built-in moniker for the geode, select the geode in the file selector and use the "Remove" trigger in the reply bar.
| Up: GEOS SDK TechDocs | Up | Prev: 5 Icon Databases


The INI File

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 How to Use the INI File
| One-page Version of this Chapter for Easy Printing

Most programmers are familiar with the structure and function of an initialization file, or INI file. GEOS uses one or more INI files depending on the setup (network users may have two or more INI files; standalone users may have only one).

The INI file for GEOS describes the drivers, fonts, and other items installed on the system. It also contains other system configuration information, such as the specific UI expected and information about the type of display and input devices used. Applications may also the INI file to store configuration information set by the user.

1 How to Use the INI File
2 Categories in the INI File
2.1 [cards]
2.2 [configure]
2.3 [diskswap]
2.4 [envelope]
2.5 [envel<>]
2.6 [expressMenuControl]
2.7 [fileManager]
2.8 [input]
2.9 [keyboard]
2.10 [label]
2.11 [label<>]
2.12 [link]
2.13 [localization]
2.14 [math]
2.15 [modem]
2.16 [<modem name>]
2.17 [mouse]
2.18 [net library]
2.19 [paper]
2.20 [paper<>]
2.21 [parallel]
2.22 [paths]
2.23 [printer]
2.24 [<printer device name>]
2.25 [screen 0]
2.26 [serial]
2.27 [sound]
2.28 [spool]
2.29 [system]
2.30 [text]
2.31 [ui]
2.32 [<specific ui name>]
2.33 [ui features]
2.34 [welcome]

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 How to Use the INI File
| One-page Version of this Chapter for Easy Printing


How to Use the INI File

The INI File: 1 How to Use the INI File

| Up: GEOS SDK TechDocs | Up | Prev: The INI File | Next: 2 Categories in the INI File

As a software developer, you will have two uses for the INI file. First, the INI file controls your system configuration to a certain extent. For example, if you are developing applications for a small-screen pen device, you should know the appropriate settings in the INI file to get your system to emulate such a device.

Second, you may need to have your application access the INI file to store or retrieve information. The kernel offers routines for just this purpose; these routines are detailed in Chapter 6 of the Concepts books.

This chapter describes the file itself as well as the categories and keys created and used by GEOS. Depending on the system you plan to develop for, you may need to set several different keys in certain combinations (e.g. setting certain keys emulates a Zoomer configuration). These special combinations are described in the special add-on documents that describe developing for each system. For more information, contact Geoworks Developer Support.

| Up: GEOS SDK TechDocs | Up | Prev: The INI File | Next: 2 Categories in the INI File


Categories in the INI File

The INI File: 2 Categories in the INI File

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 How to Use the INI File | Next: 2.1 [cards]

The GEOS.INI File shows a short list, without description, of all the categories and keys available in the INI file. Following the display are explanations of each category, with a description of each key and the values you can set.

Code Display 9-1 The GEOS.INI File

; This is a listing of many of the categories and keys in the GEOS.INI file.
; Each category is described in full in the following sections of this
; chapter, along with the values you can set for each key and what they do.
; The categories and keys are listed alphabetically.
[cards]
deckdir = <directory containing deckfile>
deckfile = <full name of deck>
[configure]
drive <letter> = <drive number>
helpEditor = <Boolean>
numberWS = <maximum number of calculator worksheets>>
[diskswap]
file = <path of swap file>
page = <size of swap page>
size = <size of swap file>
[envelope]
count = <number of user-defined size strings>
newSizes = <list of user-defined size strings>
order = <array of DefaultOrderEntry values>
[envel<num>]
name = <name string>
width = <width in points>
height = <height in points>
layout = <PageLayout structure>
[expressMenuControl]
floatingKeyboard = <Boolean>
otherAppSubMenu = <Boolean>
[fileManager]
dosAssociations = {<list of associations>}
dosLaunchers = <Boolean>
dosParameters = <Boolean>
filenameTokens = {<list of associations>}
fontID = <font ID of font in folder windows>
fontSize = <point size of folder window font>
options = <number>
startupDrivesLocation = <number>
[input]
blinkingCursor = <Boolean>
clickToType = <Boolean>
doubleClickTime = <number of ticks>
keyboardOnly = <Boolean>
left handed = <Boolean>
mouseAccelMultiplier = <number>
mouseAccelThreshold = <number>
noKeyboard = <Boolean>
numberOfMouseButtons = <number>
quickShutdownOnReset = <Boolean>
reboot on reset = <Boolean>
selectDisplaysMenu = <Boolean>
selectRaises = <Boolean>
[keyboard]
device = <full device name>
driver = <driver file name>
keyboardAltGr = <Boolean>
keyboardDoesLEDs = <Boolean>
keyboardShiftRelStr = <Boolean>
keyboardSwapCtrl = <Boolean>
keyboardTypematic = <number>
[label]
count = <number of user-defined size strings>
newSizes = <list of user-defined size strings>
order = <array of DefaultOrderEntry values>
[label<num>]
name = <name string>
width = <width in points>
height = <height in points>
layout = <PageLayout structure>
[link]
name = <machine name>
port = <number>
baudRate = <number>
drives = <list of drives>
[localization]
currencyDigits = <number of decimal digits for currency>
currencyLeadingZero = <Boolean>
currencySymbol = <character of currency symbol>
day
decimalDigits
decimalSeparator
hoursMins24HourTime
hoursMinsSecs24HourTime
hoursMinsSecsTime
hoursMinsTime
hoursTime
longCondensedDate
longDate
longDateNoWeekday
measurementSystem
minsSecsTime
month
monthDayLongDate
monthDayLongDateNoWeekday
monthDayShort
monthYearLong
monthYearShort
negativeSignBeforeNumber = <Boolean>
negativeSignBeforeSymbol = <Boolean>
quotes
shortDate
spaceAroundSymbol = <Boolean>
symbolBeforeNumber = <Boolean>
useNegativeSign = <Boolean>
weekday
year
zeroPaddedShortDate
[math]
coprocessor = <library name for coprocessor>
[modem]
modems = {<modem name list>}
numberOfModems = <number>
[<modem name>]
baudRate = <number>
handshake = <hardware, software>
parity = <none, even, odd, mark, space>
stopBits = <number>
stopLocal = <dsr, dcd, cts>
stopRemote = <dtr, rts>
toneDial = <Boolean>
wordLength = <number>
[mouse]
device = <full device name>
driver = <driver file name>
info = <number>
irq = <number>
port = <number>
[netLibrary]
    InitDrivers = {<list of driver geodes>}
[paper]
count = <number of user-defined size strings>
newSizes = <list of user-defined size strings>
order = <array of DefaultOrderEntry values>
[paper<num>]
name = <name string>
width = <width in points>
height = <height in points>
layout = <PageLayout structure>
[parallel]
port <number of parallel port> = <level of port>
[paths]
<standard path> = <other paths to merge>
ini = <additional .INI files to load>
inisaved = <path of saved .INI file>
sharedTokenDatabase = <path of shared token db file>
[printer]
count = <number>
defaultPrinter = <number>
numFacsimiles = <number>
numPrinters = <number>
printers = {<list of print devices>}
[<printer device name>]
baudRate = <speed of serial communication>
device = <full device name>
driver = <file name of driver>
handshake = <handshake for serial communication>
parity = <parity for serial communication>
port = <port name>
stopBits = <stop bits for serial communication>
type = <type of print device>
wordLength = <word size for serial communication>
[screen 0]
device = <full name of device>
driver = <file name of driver>
oldDevice = <full name of device formerly used>
oldDriver = <file name of drivr formerly used)
[serial]
port <number of serial port> = <level of port>
[sound]
sampleDriver = <driver file name>
synthDriver = <driver file name>
[spool]
uiOptions = <SpoolUIOptions>
[system]
autoCursorCentering = <Boolean>
continueSetup = <Boolean>
drive <letter> = <number>
font = <drivers to be loaded>
fontid = <font to be used as the default>
fontsize = <point size of default font>
fs = <drivers to be loaded>
handles = <number of handles>
inkTimeout = <ticks until ink is processed>
maxTotalHeapSpace = <memory size>
memory = <swap drivers to be loaded>
noFontDriver = <Boolean>
notes = <string>
noVidMem = <Boolean>
pda = <Boolean>
penBased = <Boolean>
power = <file name of power management driver>
serialNumber = <serial number of installed GEOS>
setupMode = <mode for graphical setup application>
splashcolor = <background color>
splashscreen = <Boolean>
splashtext = <text message>
[text]
autoCheckSelections = <Boolean>
autoSuggest = <Boolean>
dialect = <dialect code>
dictionary = <file name of dictionary used by spell checker>
hyphenationDictionary = <file name of dictionary>
hyphenationLanguage = <name of language>
language = <language code>
languageName = <name of language in use>
resetSkippedWordsWhenBoxCloses = <Boolean>
smartQuotes = <Boolean>
[ui]
autosave = <Boolean>
autosaveTime = <seconds between autosaves>
background = <file name of background graphic>
backgroundattr = <t, c, or x>
backgroundcolor = <color index of background>
confirmShutdown = <Boolean>
deleteStateFilesAfterCrash = <Boolean>
doNotDisplayResetBox = <Boolean>
execOnStartup = <list of programs to run on startup>
generic = <generic UI file name>
hardIconsLibrary = <string>
haveEnvironmentApp = <Boolean>
hwr = <file name of handwriting recognition library>
kbdAcceleratorMode = <Boolean>
noClipboard = <Boolean>
noSpooler = <Boolean>
noTaskSwitcher = <Boolean>
noTokenDatabase = <Boolean>
overstrikeMode = <Boolean>
password = <Boolean>
passwordText = <encrypted text>
penInputDisplayType = <number of display type>
productName = <name of the product>
screenBlanker = <Boolean>
screenBlankerTimeout = <number of minutes>
showTitleScreen = <Boolean>
sound = <Boolean>
specific = <specific UI file name>
tinyScreen = <Boolean>
unbuildControllers = <Boolean>
xScreenSize = <width of screen>
yScreenSize = <height of screen>
[<specific ui name>]
fontid = <font>
fontsize = <size in points>
[ui features]
backupDir = <directory for quick backup files>
defaultLauncher = <relative path of application launcher>
docControlFSLevel = <number>
docControlOptions = <number>
expressOptions = <number>
helpOptions = <number>
interfaceLevel = <number>
interfaceOptions = <number>
launchLevel = <number>
launchModel = <number>
launchOptions = <number>
quitOnClose = <Boolean>
windowOptions = <number>
[uiFeatures - intro]
[uiFeatures - beginner]
[uiFeatures - advanced]
[welcome]
enteredprofessionalroom = <Boolean>
startup = <application name to start>
startupRoom = <name of startup room>

[cards]

[configure]

[diskswap]

[envelope]

[envel<>]

[expressMenuControl]

[fileManager]

[input]

[keyboard]

[label]

[label<>]

[link]

[localization]

[math]

[modem]

[<modem name>]

[mouse]

[net library]

[paper]

[paper<>]

[parallel]

[paths]

[printer]

[<printer device name>]

[screen 0]

[serial]

[sound]

[spool]

[system]

[text]

[ui]

[<specific ui name>]

[ui features]

[welcome]

| Up: GEOS SDK TechDocs | Up | Down | Prev: 1 How to Use the INI File | Next: 2.1 [cards]


[cards]

The INI File: 2.1 Categories in the INI File: [cards]

| Up: GEOS SDK TechDocs | Up | Prev: 2 Categories in the INI File | Next: 2.2 [configure]

The cards category contains information used by the cards library. The cards library provides routines used by card games. The cards category contains information about how to access the file containing the bitmap to be used as the card back picture.

deckdir

deckdir = <deck directory>

This optional field shows the path which contains the file named in the deckfile key, described below. If this key is not given, the cards library will look for deck files in the USERDATA\DECK directory.

deckfile

deckfile = <deck name>

The deckfile key defines the full name of the deck to be used by the card library. This is most useful in cases like the Zoomer, which must have its own card artwork. By default, the cards library will look in the USERDATA\DECK directory.

deckfile = Zoomer Default Deck
| Up: GEOS SDK TechDocs | Up | Prev: 2 Categories in the INI File | Next: 2.2 [configure]


[configure]

The INI File: 2.2 Categories in the INI File: [configure]

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 [cards] | Next: 2.3 [diskswap]

The configure category contains miscellaneous configuration information for GEOS. The drive key is exactly like the drive key in the system category.

drive

drive <letter> = <number>

This key allows you to override the drive-map initialization done by the primary filesystem driver. You can not make a driver believe a nonexistent drive exists, but you can change the presumed media or make the driver ignore a drive. More than one drive may be remapped.

The letter argument is the drive letter of the drive to be remapped. The number argument defines the new drive definition and is one of the following values:

  -1		fixed disk
   0		ignore the drive
 360		360 K 5.25-inch disk
 720		720 K 3.5-inch disk
1200		1.2 meg 5.25-inch disk
1440		1.44 meg 3.5-inch disk
2880		2.88 meg 3.5-inch disk

Some examples of drive remappings are shown below:

drive d = 0			; ignore drive D:
drive a = 360			; make GEOS think drive A: is 360K

helpEditor

helpEditor = <Boolean>

If true, this key indicates that GeoWrite should add a new Help Editor feature. This must be on if you are planning on creating help files for your application. The Help Editor feature may be turned on by selecting "Fine Tune" in the user level dialog box in GeoWrite. More information on the Help Editor can be found in the chapter on the help system.

helpEditor = true
helpEditor = false

numberWS

numberWS = <number of calculator worksheets>

When users use the worksheets feature of the calculator, each worksheet they use will be loaded into memory. Normally, these worksheets are kept in memory so that the user may quickly go back to a worksheet they were using previously. Some low-memory devices may prefer that fewer worksheets are cached in this manner. The numberWS specifies a maximum number of worksheets that may be so cached.

| Up: GEOS SDK TechDocs | Up | Prev: 2.1 [cards] | Next: 2.3 [diskswap]


[diskswap]

The INI File: 2.3 Categories in the INI File: [diskswap]

| Up: GEOS SDK TechDocs | Up | Prev: 2.2 [configure] | Next: 2.4 [envelope]

The diskswap category defines swap information for the GEOS swap file. Generally, you won't set these keys individually; GEOS will set them as required.

file

file = <path of swap file>

This category defines the file used by the disk swap driver for swapping.

file = C:\GEOWORKS\SWAP\EXTRA

page

page = <size of swap page>

This key defines the page size of a swap page.

page = 2048

size

size = <size of swap file>

This key defines the maximum size of the swap file.

size = 2048
| Up: GEOS SDK TechDocs | Up | Prev: 2.2 [configure] | Next: 2.4 [envelope]


[envelope]

The INI File: 2.4 Categories in the INI File: [envelope]

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 [diskswap] | Next: 2.5 [envel<>]

This category keeps track of any customizations the user may have made to the list of envelope paper sizes.

count

count = <number>

This is the number of user-defined envelope sizes.

newSizes

newSizes = <list of paper size codes>

This list contains a list of all user-defined envelope sizes. The paper size information for each of these sizes will be stored in a category named [envelnum], where num is a three-digit number. A sample newSizes entry:

newSizes = {
    envel128
    envel129
}

order

order = <list of paper size codes>

This list contains a list of all envelope sizes in the order that the user wants them to appear in envelope size lists.

| Up: GEOS SDK TechDocs | Up | Prev: 2.3 [diskswap] | Next: 2.5 [envel<>]


[envel<>]

The INI File: 2.5 Categories in the INI File: [envel<num>]

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 [envelope] | Next: 2.6 [expressMenuControl]

This category contains size and layout for a user-defined envelope size.

height

height = <number>

This field holds the envelope's height in points.

layout

layout = <PageLayout value>

This field holds the envelope's layout information.

name

name = <string>

This field holds the envelope's full name.

width

width = <number>

This field holds the envelope's width in points.

| Up: GEOS SDK TechDocs | Up | Prev: 2.4 [envelope] | Next: 2.6 [expressMenuControl]


[expressMenuControl]

The INI File: 2.6 Categories in the INI File: [expressMenuControl]

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 [envel<>] | Next: 2.7 [fileManager]

The expressMenuControl category defines the configuration of the express menu; see the ui features category for more express menu controls.

floatingKeyboard

floatingKeyboard = <Boolean>

If true, this key adds an item to the express menu to bring up the floating keyboard (used for pen-based systems). The default is false.

floatingKeyboard = true
floatingKeyboard = false

maxNumDirs

maxNumDirs = <number>

If this field exists, then if there are more entries in the Other Apps section of the Express Menu than this, the Other Apps section will be forced into a submenu (and forced into a subgroup if less than this), regardless of `noSubMenus' and `otherAppSubMenu'. Defaults to 25 (the absolute maximum number of Other Apps entries).

noSubMenus

noSubMenus = <Boolean>

If true, the express menu will not allow forcing the main applications,other applications, or desk accessories into submenus.

otherAppSubMenu

otherAppSubMenu = <Boolean>

If true, this key turns the "other applications" section in the express menu into a submenu rather than a subgroup. The default is false.

otherAppSubMenu = true
otherAppSubMenu = false

runningAppSubMenu

runningAppSubMenu = <Boolean>

If true, this key causes the express menu include a list of currently running GEOS applications as a submenu. If false, the list will be placed directly in the express menu.

runSubMenu

runSubMenu = <Boolean>

If true, top level applications (those placed in the WORLD directory) and top level subdirectories (subdirectories of WORLD) in a submenu of the express menu.

| Up: GEOS SDK TechDocs | Up | Prev: 2.5 [envel<>] | Next: 2.7 [fileManager]


[fileManager]

The INI File: 2.7 Categories in the INI File: [fileManager]

| Up: GEOS SDK TechDocs | Up | Prev: 2.6 [expressMenuControl] | Next: 2.8 [input]

The fileManager category is used by file manager applications such as GeoManager. You will probably not find much cause to use these keys during your application development.

dosAssociations

dosAssociations = {<list of associations>}

This key allows a user to associate DOS data files with DOS executables so a particular DOS executable will be launched when the user double-clicks the data file.

dosAssociations = {
    *.ZIP = C:\PKUNZIP.EXE
}

dosLaunchers

dosLaunchers = <Boolean>

If true, this key allows DOS launchers to launch DOS programs. The default is true.

dosLaunchers = true
dosLaunchers = false

dosParameters

dosParameters = <Booeans>

If true, the file manager will allow the passing of parameters to DOS executables..

filenameTokens

filenameTokens = {<list of associations>}

This key allows the user to set icon associations with DOS files. It also allows certain text files to be opened by the text file editor. Certain associations are made by default and should always appear; these are listed below.

filenameTokens = {
    *.EXE = "gDOS",0
    *.COM = "gDOS",0
    *.BAT = "gDOS",0
    *.TXT = "FILE",0,"TeEd",0
    *.DOC = "FILE",0,"TeEd",0
    *.HLP = "FILE",0,"TeEd",0
}

fontID

fontID = <number>

This key sets the font used by GeoManager (or another file manager) when displaying the names and information of files in the folder window. The font ID is set the same way as for the system category; if no font ID is named, the default system font will be used.

fondID = berkeley

fontSize

fontSize = <number>

This key sets the font size used in a folder window. If not specified, it will default to the system font.

fontSize = 10

options

options = <number>

This key controls various file manager options. The options are set and cleared by the user using the file manager's Options menu; the number is a decimal number representing the bits set or cleared for various options.

startupDrivesLocation

startupDrivesLocation = <number>

This key controls the initial location of drive buttons; it is set with the Options menu in the file manager.

| Up: GEOS SDK TechDocs | Up | Prev: 2.6 [expressMenuControl] | Next: 2.8 [input]


[input]

The INI File: 2.8 Categories in the INI File: [input]

| Up: GEOS SDK TechDocs | Up | Prev: 2.7 [fileManager] | Next: 2.9 [keyboard]

The input category contains a number of keys that define how input is handled by the system. It affects the keyboard and mouse setup as well as how the UI responds to various user actions.

blinkingCursor

blinkingCursor = <Boolean>

If true, this key forces the text cursor to be a blinking cursor; it defaults to true. The screen dumper application requires a non-blinking cursor.

blinkingCursor = true
blinkingCursor = false

clickToType

clickToType = <Boolean>

If true, this key requires the user to click in a window before the focus will change to that window. When this key is false, the UI will invoke a "real estate" mode, wherein the user's typing will go to the window under the mouse, whether the mouse was clicked there or not. The default is true (click required).

clickToType = true
clickToType = false

doubleClickTime

doubleClickTime = <number of ticks>

This field specifies the time threshhold between clicks which should be recognized as a double-click. This time is expressed in 1/60th second "ticks". The default value is 20.

keyboardOnly

keyboardOnly = <Boolean>

If true, this key indicates that GEOS is running on a system with only a keyboard (no mouse) for input. The default is false.

keyboardOnly = true
keyboardOnly = false

left handed

left handed = <Boolean>

If true, this key switches the left and right mouse button significance. For single-button mice, there is no effect; for three-button mice, the middle button stays the same. The default is false.

left handed = true
left handed = false

mouseAccelMultiplier

mouseAccelMultiplier = <number>

This key gives a multiplier to allow mouse acceleration along the following rule: any single event pixel movement beyond a given threshold (see mouseAccelThreshold ) is multiplied by the multiplier. The multiplier defaults to one, which provides no acceleration. (A multiplier of three or four is fast.)

mouseAccelMultiplier = 1					; no acceleration
mouseAccelMultiplier = 4					; very fast

mouseAccelThreshold

mouseAccelThreshold = <number>

This key gives the mouse acceleration threshold used with mouseAccelMultiplier (above) to provide mouse acceleration. This threshold is the number of pixels the mouse must move before acceleration is invoked.

mouseAccelThreshold = 5

noKeyboard

noKeyboard = <Boolean>

If true, this key indicates that the system running GEOS has no keyboard. The default is false.

noKeyboard = true
noKeyboard = false

numberOfMouseButtons

numberOfMouseButtons = <number>

This key defines the number of buttons the mouse has. This may be the value one, two, or three. The default is three.

numberOfMouseButtons = 1
numberOfMouseButtons = 2
numberOfMouseButtons = 3

quickShutdownOnReset

quickShutdownOnReset = <Boolean>

If true, this key forces a Ctrl-Alt-Del sequence to force a dirty shutdown of GEOS. This defaults to true.

quickShutdownOnReset = true
quickShutdownOnReset = false

reboot on reset

reboot on reset = <Boolean>

If true, this key causes a Ctrl-Alt-Del sequence to warm-boot the machine rather than exit quickly to DOS. The default is false.

reboot on reset = true
reboot on reset = false

selectDisplaysMenu

selectDisplaysMenu = <Boolean>

If true, this key reverses the left and right mouse buttons with respect to menus in some specific UIs. (For example, the left (select) button will open the menu, but the right (features) button will show and execute the default menu item.) The default is false.

selectDisplaysMenu = true
selectDisplaysMenu = false

selectRaises

selectRaises = <Boolean>

If true, this key causes a select-button click to raise the window in which the click occurred, if the window was behind other windows. The window will not be raised above other windows that are always kept on top (e.g. the help window and desk accessory applications). The default is true.

selectRaises = true
selectRaises = false
| Up: GEOS SDK TechDocs | Up | Prev: 2.7 [fileManager] | Next: 2.9 [keyboard]


[keyboard]

The INI File: 2.9 Categories in the INI File: [keyboard]

| Up: GEOS SDK TechDocs | Up | Prev: 2.8 [input] | Next: 2.10 [label]

device

device = <full device name>

This key defines the keyboard in use. It must be the keyboard device's full name; in general, this should only be set by the Preferences manager application.

device = U.S. Keyboard

driver

driver = <driver file name>

This key goes with the device key and defines the driver file name to be loaded. This should be set by the Preferences manager.

driver = kbd.geo

keyboardTypematic

keyboardTypematic = <number>

This key defines both the repeat speed and delay before repeat for the keyboard. The number is an integer less than 128 (the high bit is ignored), and it is interpreted as three separate fields, as below:

bit 7		ignored
bit 6-5		DELAY (see below)
bit 4-3		PE (exponent portion of repeat period)
bit 2-0		PM (mantissa portion of repeat period)
The delay is calculated by the following formula:
delay = 1 second + (DELAY * 250 ms) +/- 20%
The period is calculated by the following formula
period = (8 + PM) * (2^PE) * 0.00417 seconds

If no typematic number is specified, GEOS sets the default to 44, which represents a medium delay and a medium repeat period.

keyboardTypematic = 0				; short delay, fast repeat
keyboardTypematic = 127				; long dalay, slow repeat

keyboardDoesLEDs

keyboardDoesLEDs = <Boolean>

If true, this key tells GEOS that the XT-class machine it's running on supports BIOS-updated LEDs for Num Lock, Caps Lock, and Scroll Lock. Most XT-class machines do not support updating these LEDs. This field is unnecessary on AT-class and more advanced machines.

keyboardDoesLEDs = true
keyboardDoesLEDs = false

keyboardAltGr

keyboardAltGr = <Boolean>

If true, this key makes the right Alt key function like Ctrl-Alt, as with many European setups.

keyboardAltGr = true
keyboardAltGr = false

keyboardShiftRelStr

keyboardShiftRelStr = <Boolean>

If true, this key makes the Shift keys release the Caps Lock, as with typewriters.

keyboardShiftRelStr = true
keyboardShiftRelStr = false

keyboardSwapCtrl

keyboardSwapCtrl = <Boolean>

If true, this key swaps the left Ctrl key with the Caps Lock key so the keyboard acts like many non-PC keyboards.

keyboardSwapCtrl = true
keyboardSwapCtrl = false
| Up: GEOS SDK TechDocs | Up | Prev: 2.8 [input] | Next: 2.10 [label]


[label]

The INI File: 2.10 Categories in the INI File: [label]

| Up: GEOS SDK TechDocs | Up | Prev: 2.9 [keyboard] | Next: 2.11 [label<>]

This category keeps track of any customizations the user may have made to the list of label paper sizes.

count

count = <number>

This is the number of user-defined label sizes.

newSizes

newSizes = <list of paper size codes>

This list contains a list of all user-defined label sizes. The paper size information for each of these sizes will be stored in a catgory named [labelnum]. A newSizes entry might read:

newSizes = {
    label128
    label129
}

order

order = <list of paper size codes>

This list contains a list of all label sizes in the order that the user wants them to appear in label size lists.

| Up: GEOS SDK TechDocs | Up | Prev: 2.9 [keyboard] | Next: 2.11 [label<>]


[label<>]

The INI File: 2.11 Categories in the INI File: [label<num>]

| Up: GEOS SDK TechDocs | Up | Prev: 2.10 [label] | Next: 2.12 [link]

This category contains size and layout for a user-defined label size.

height

height = <number>

This field holds the label's height in points.

layout

layout = <PageLayout value>

This field holds the label's layout information.

name

name = <string>

This field holds the label's full name.

width

width = <number>

This field holds the label's width in points.

| Up: GEOS SDK TechDocs | Up | Prev: 2.10 [label] | Next: 2.12 [link]


[link]

The INI File: 2.12 Categories in the INI File: [link]

| Up: GEOS SDK TechDocs | Up | Prev: 2.11 [label<>] | Next: 2.13 [localization]

These fields are used by the Remote File System Driver to describe the machine to other machines that wish to access its drives. You may specify a name for the machine by which others may identify it and also set up communications parameters.

baudRate

baudRate = <number>

This SerialBaud value defines the communication speed this machine supports for RFSD.

drives

baudRate = <list of drives>

Normally, all of your drives will be accessible by the remote machine. This field allows you to specify exactly which drives are accessible. For instance, to restrict remote machines to accessing your C: and E: drives, use the following entry:

drives = {
C: E:
}

name

name = <machine name>

A string by which remote machines may identify you. When the remote machine sees your drives, their names will be <machine-name>-<drive-letter>:.

port

port = <SerialPortNum>

This number specifies which serial port is to be used for RFSD.

| Up: GEOS SDK TechDocs | Up | Prev: 2.11 [label<>] | Next: 2.13 [localization]


[localization]

The INI File: 2.13 Categories in the INI File: [localization]

| Up: GEOS SDK TechDocs | Up | Prev: 2.12 [link] | Next: 2.14 [math]

The localization key defines various configuration aspects of the system as set by the user in the Preferences manager application. Each of the keys in this category specifies one aspect of the user's system, typically an aspect defined by the country the user lives in. Because all of these keys are set in the Preferences manager application, they are not listed here. In general, keys which encode characters or strings will do so using ASCII values (e.g. "decimalSeparator = 2E" means that `.' is the decimal separator). The kernel also provides a number of routines to get and set the localization parameters; for more information, see the chapter on Localization in the Concepts books.

| Up: GEOS SDK TechDocs | Up | Prev: 2.12 [link] | Next: 2.14 [math]


[math]

The INI File: 2.14 Categories in the INI File: [math]

| Up: GEOS SDK TechDocs | Up | Prev: 2.13 [localization] | Next: 2.15 [modem]
coprocessor = <driver name>

The math category allows the user to override the way GEOS normally treats math coprocessors. The single key ( coprocessor ) specifies the coprocessor library to use; the math library will load that particular library, and the library will check to ensure the proper coprocessor chip exists. If the chip is present, the library will be used; if the chip is absent, the math library will use software emulation of a coprocessor.

coprocessor = none				; use software emulation
coprocessor = intx87.geo					; Intel 80387, 80486
coprocessor = intx8087.geo					; Intel 80287, 8087
| Up: GEOS SDK TechDocs | Up | Prev: 2.13 [localization] | Next: 2.15 [modem]


[modem]

The INI File: 2.15 Categories in the INI File: [modem]

| Up: GEOS SDK TechDocs | Up | Prev: 2.14 [math] | Next: 2.16 [<modem name>]

The modem category defines the modems attached to the system. Each modem must have its name listed in the name list, and each modem named in the list must have its own category (see the modem name category, below).

modems

modems = {<modem name list>}

This key defines all the modems attached to the system. Each modem in the list must have its own category in the .INI file, as shown below.

modems = My Modem
modems = { My Slow Modem
         My Fast Modem }

numberOfModems

numberOfModems = <number>

This key defines the number of modems specified in the modems list (above).

numberOfModems = 1
numberOfModems = 2
| Up: GEOS SDK TechDocs | Up | Prev: 2.14 [math] | Next: 2.16 [<modem name>]


[<modem name>]

The INI File: 2.16 Categories in the INI File: [<modem name>]

| Up: GEOS SDK TechDocs | Up | Prev: 2.15 [modem] | Next: 2.17 [mouse]

Each modem listed in the modems keyword in the modem category must have its own category. The category is named for the modem name in the list. Thus, the following example shows that each modem in the system has its own category:

[modem]
numberOfModems = 2
modems = {My Slow Modem
         My Fast Modem }
[My Slow Modem]
port = COM1
baudRate = 300
toneDial = true
parity = none
wordLength = 8
stopBits = 1
handshake = software
[My Fast Modem]
port = COM3
baudRate = 19200
toneDial = true
parity = none
wordLength = 8
stopBits = 1
handshake = software

baudRate

baudRate = <number>

This key defines the modem`s baud rate.

baudRate = 2400
baudRate = 9600

handshake

handshake = <hardware, software>

This key indicates the type of handshake used by the modem. It must be one of the values specified.

handshake = hardware
handshake = software

parity

parity = <none, even, odd, mark, space>

This key indicates the modem's parity type. It must be one of the values shown above.

parity = none
parity = even

stopBits

stopBits = <number>

This key specifies the number of stop bits used by the modem. This should be 1, 1.5, or 2.

stopBits = 1
stopBits = 1.5
stopBits = 2

stopLocal

stopLocal = <dsr, dcd, cts>

If hardware handshaking is used, this key specifies which line the serial driver will watch for the stop signal.

stopLocal = dsr
stopLocal = dcd
stopLocal = cts

stopRemote

stopRemote = <dtr, rts>

If hardware handshaking is used, this key specifies which line the serial driver will use to make the remote side of the connection stop.

stopRemote = dtr
stopRemote = rts

toneDial

toneDial = <Boolean>

If true, this key indicates that the modem may use tone dialing. The default is true.

toneDial = true
toneDial = false

wordLength

wordLength = <number>

This key indicates the communication word length. This should be a number between five and eight inclusive.

wordLength = 8
| Up: GEOS SDK TechDocs | Up | Prev: 2.15 [modem] | Next: 2.17 [mouse]


[mouse]

The INI File: 2.17 Categories in the INI File: [mouse]

| Up: GEOS SDK TechDocs | Up | Prev: 2.16 [<modem name>] | Next: 2.18 [net library]

The mouse category defines the mouse driver and specifics of the mouse attached to the GEOS system. Both the device and driver keys are required; the others are optional.

device

device = <device name>

This key defines the type of mouse attached. It must be the full device name and is typically set during setup of the system.

device = Logitech Bus Mouse
device = No idea

driver

driver = <file name>

This key defines the file name of the mouse driver in use.

logibus.geo
logibuse.geo
genmouse.geo

info

info = <number>

This key defines the "extra word" of data for the mouse. This is an internal structure set by the mouse driver.

irq

irq = <number>

This key allows you to set the interrupt level of a mouse that needs it; most mice do not need to be told their interrupt level.

irq = 4

port

port = <number>

This key specifies the port of a serial mouse, if necessary. The port number is one, two, three, or four, appropriate to the COM port being used.

port = 3
| Up: GEOS SDK TechDocs | Up | Prev: 2.16 [<modem name>] | Next: 2.18 [net library]


[net library]

The INI File: 2.18 Categories in the INI File: [net library]

| Up: GEOS SDK TechDocs | Up | Prev: 2.17 [mouse] | Next: 2.19 [paper]
InitDrivers = { <list of driver geodes> }

This is a list of network drivers to use. The Net library will attempt to load these driver geodes.

| Up: GEOS SDK TechDocs | Up | Prev: 2.17 [mouse] | Next: 2.19 [paper]


[paper]

The INI File: 2.19 Categories in the INI File: [paper]

| Up: GEOS SDK TechDocs | Up | Prev: 2.18 [net library] | Next: 2.20 [paper<>]

This category keeps track of any customizations the user may have made to the list of paper sizes.

count

count = <number>

This is the number of user-defined paper sizes.

newSizes

newSizes = <list of paper size codes>

This list contains a list of all user-defined paper sizes. The paper size information for each of these sizes will be stored in a catgory named [papernum], where num is the three-letter code in this list. For example:

newSizes = {
    paper128
    paper129
}

order

order = <list of paper size codes>

This list contains a list of all paper sizes in the order that the user wants them to appear in paper size lists. There should be no spaces between the numbers. A sample entry:

order = 128,129,029,031,024,043,049,052,057,
| Up: GEOS SDK TechDocs | Up | Prev: 2.18 [net library] | Next: 2.20 [paper<>]


[paper<>]

The INI File: 2.20 Categories in the INI File: [paper<num>]

| Up: GEOS SDK TechDocs | Up | Prev: 2.19 [paper] | Next: 2.21 [parallel]

This category contains size and layout for a user-defined paper size.

name

name = <string>

This field holds the paper's full name. The string should not be included in quotes.

height

height = <number>

This field holds the page's height in points.

width

width = <number>

This field holds the paper's width in points.

layout

layout = <PageLayout value>

This field holds the paper's layout information. This needs to be entered as an integer value calculated by adding up the following values:

  • PageType (PAPER = 0, ENVELOPE = 2, LABEL = 4)
  • PaperOrientation (PORTRAIT = 0, LANDSCAPE = 8)
  • Number of labels per row = number * 8.
  • Number of labels per column = number * 512

A sample entry is shown below:

[paper128]
name	= small landscape 1x2 inch
width	= 72
height	= 144
layout	= 8

| Up: GEOS SDK TechDocs | Up | Prev: 2.19 [paper] | Next: 2.21 [parallel]


[parallel]

The INI File: 2.21 Categories in the INI File: [parallel]

| Up: GEOS SDK TechDocs | Up | Prev: 2.20 [paper<>] | Next: 2.22 [paths]
port <number> = <level>

The parallel category defines all the parallel ports available on the machine running GEOS. If a port is not defined in this category, GEOS will not recognize its existence. The single key in this category may be used numerous times, once for each available parallel port.

The port key defines the hardware interrupt level for the specified port. The normal entries for the three base parallel ports are shown in the examples below. If no level is provided for a port, the parallel driver will assign the values shown below; if no interrupt level is available for a port, GEOS will instead spawn a background thread for it. Setting any port's value will override other defaults (e.g. if you set port two to have level seven, port one will be set to level five, unless GEOS is on an XT- or PC-class machine).

port 1 = 7
port 2 = 5
port 3 = 0
| Up: GEOS SDK TechDocs | Up | Prev: 2.20 [paper<>] | Next: 2.22 [paths]


[paths]

The INI File: 2.22 Categories in the INI File: [paths]

| Up: GEOS SDK TechDocs | Up | Prev: 2.21 [parallel] | Next: 2.23 [printer]

The paths category defines other directories to add into a standard path as well as additional INI files and the location of the shared token database. Adding directories to standard paths is useful both in network situations and if you want your application installed in a special directory but linked to the WORLD directory. This category uses five different keys, as shown below.

standard paths

<standard path> = <additional paths>

Each standard path is its own key, and you can merge other directories into any standard path. Some examples are shown below.

top = C:\GEOWORKS C:\PCGEOS
world = E:\INSTALL\NEWAPP
userdata font = N:\NETFONTS F:\SPECFONT

ini

ini = <file names>

This key defines up to three additional INI files to be loaded read-only. When GEOS searches for a key, the local (current) INI file is scanned first, followed by the additional INI files in the order they're defined. The first occasion of the key will be used; thus, the local INI file can supersede other settings.

ini = personal.ini INI\mydevice.ini n:\shared.ini

inisaved

inisaved = <files>

This key is used only if GEOS is run with the /psaved argument. That is, if the user runs GEOS thus:

C>GEOS /psaved

then GEOS will look for the inisaved key rather than the ini key for additional INI files. Similarly, if the user instead runs

C>GEOS /pxxx

GEOS will look for a key called inixxx for the names of the files to be used.

inisaved = net.ini
inisaved = demo.ini net.ini

sharedTokenDatabase

sharedTokenDatabase = <path>

This key defines the location of the shared token database file. This key is most useful in network situations, when many users may be sharing a single token database.

sharedTokenDatabase = N:\NETFILES\TOKEN_DA.000
| Up: GEOS SDK TechDocs | Up | Prev: 2.21 [parallel] | Next: 2.23 [printer]


[printer]

The INI File: 2.23 Categories in the INI File: [printer]

| Up: GEOS SDK TechDocs | Up | Prev: 2.22 [paths] | Next: 2.24 [<printer device name>]

The printer category defines all the printers configured for the system running GEOS. The printers key within this category (see below) defines the printer names, each of which must then have its own category in the INI file (see the following section under printer device name ).

count

count = <number>

This key indicates the number of printers installed.

count = 0			; no printers installed
count = 2			; two printers installed

defaultPrinter

defaultPrinter = <number>

This key specifies the number of the installed printer that will act as the default device.

defaultPrinter = 2				; printer #2 is the default

numFacsimiles

numFacsimiles = <number>

This key specifies the number of installed print devices which are actually fax drivers rather than printer drivers.

numFacsimiles = 1

numPrinters

numPrinters = <number>

This key specifies the number of installed print devices which are actually printers (as opposed to faxes or other devices).

numPrinters = 2

printers

printers = {<list of devices>}

This key defines all the installed print devices. Each entry in the list is the device name; the list must be a blob, with one printer named per line as in the example below. Each entry in the list must also have its own category defining the driver, device name, port, and type.

printers = { My Printer
         My PostScript to file }
| Up: GEOS SDK TechDocs | Up | Prev: 2.22 [paths] | Next: 2.24 [<printer device name>]


[<printer device name>]

The INI File: 2.24 Categories in the INI File: [<printer device name>]

| Up: GEOS SDK TechDocs | Up | Prev: 2.23 [printer] | Next: 2.25 [screen 0]

Each printer defined in the printers key of the printer category (see above) must have its own category. The name of the category must be the same as the printer named in the installed printers list of the printer category. The keys below define the printer's characteristics.

baudRate

baudRate = <number>

This key defines the printer's communication rate, for those printers which communicate via a serial connection.

device

device = <device name>

This key defines the device name of the installed print device.

device = Apple LaserWriter Plus v38.0 (PostScript)

driver

driver = <file name>

This key defines the print driver used for the installed print device.

driver = PostScript driver

handshake

handshake = <hardware, software>

This key indicates the type of handshake used by the printer for those printers which communciate via a serial connection. It must be one of the values specified above.

handshake = hardware
handshake = software

parity

parity = <none, even, odd, mark, space>

This key indicates the printer's parity type for those printers which communicate via a serial connection. It must be one of the values shown above.

parity = none
parity = even

port

port = <port name>

This key specifies the port to which the device is attached. This must be one of the values shown in the following examples.

port = LPT1
port = LPT2
port = LPT3
port = COM1
port = COM2
port = COM3
port = COM4
port = FILE

stopBits

stopBits = <number>

This key specifies the number of stop bits used by the printer for those printers which communciate via a serial connection. This should be 1, 1.5, or 2.

stopBits = 1
stopBits = 1.5
stopBits = 2

type

type = <number>

This key defines the type of device this installed print device is. The number indicates the PrinterDriverType as defined in spool.goh .

type = 0		; PDT_PRINTER
type = 3		; PDT_CAMERA

wordLength

wordLength = <number>

This key indicates the communication word length for those printers which communciate via a serial connection. This should be a number between five and eight inclusive.

wordLength = 8
| Up: GEOS SDK TechDocs | Up | Prev: 2.23 [printer] | Next: 2.25 [screen 0]


[screen 0]

The INI File: 2.25 Categories in the INI File: [screen 0]

| Up: GEOS SDK TechDocs | Up | Prev: 2.24 [<printer device name>] | Next: 2.26 [serial]

The screen N category is used to define the characteristics of each screen in the system. Currently, GEOS completely supports only one screen, which is called screen 0. This screen's characteristics are defined by the screen 0 category.

device

device = <device name>

This key specifies the full device name of the screen's device. The standard devices are shown in the examples below.

device = Hercules HGC: 720x348 Mono
device = IBM MCGA: 640x480 Mono
device = CGA: 640x200 Mono
device = EGA: 640x350 16-color
device = VGA: 640x480 16-color

driver

driver = <file name>

This key specifies the file name of the driver used to run this screen.

driver = vga.geo

olddevice

olddevice = <device name>

When the user switches between video drivers, the system keeps track of the old device name in case the user made a mistake and wants to switch back. It will store the old value of the "device" field in this field.

olddriver

olddriver = <file name>

When the user switches between video drivers, the system keeps track of the old driver name in case the user made a mistake and wants to switch back. It will store the old value of the "driver" field in this field.

userdevice

userdevice = <device name>

GEOS doesn't use this field. The Debug utility uses this field to store the user's personal video device choice when simulating hardware devices that would not support the choice.

userdriver

userdriver = <file name>

GEOS doesn't use this field. The Debug utility uses this field to store the user's personal video driver choice when simulating hardware devices that would not support the choice.

| Up: GEOS SDK TechDocs | Up | Prev: 2.24 [<printer device name>] | Next: 2.26 [serial]


[serial]

The INI File: 2.26 Categories in the INI File: [serial]

| Up: GEOS SDK TechDocs | Up | Prev: 2.25 [screen 0] | Next: 2.27 [sound]
port <number> = <level>

The serial category defines all the serial ports available on the machine running GEOS. If a port is not defined in this category, GEOS will not recognize its existence. The single key in this category may be used numerous times, once for each available serial port.

The port key defines the hardware interrupt level for the specified port. The normal entries for the four base serial ports are shown in the examples below. If no value is specified, GEOS will try to generate an interrupt for the port and set the value itself. (It only checks levels three and four, though.) Note also that on an AT-class machine, level two for a card is actually level nine specified here.

port 1 = 4
port 2 = 3
port 3 = 4			; may not work if port 1 is in use
port 4 = 3			; may not work if port 2 is in use
| Up: GEOS SDK TechDocs | Up | Prev: 2.25 [screen 0] | Next: 2.27 [sound]


[sound]

The INI File: 2.27 Categories in the INI File: [sound]

| Up: GEOS SDK TechDocs | Up | Prev: 2.26 [serial] | Next: 2.28 [spool]

The sound category is used by the sound library to determine which sound driver is selected for the system running GEOS.

sampleDriver

sampleDriver = <driver file name>

This key specifies the sound driver that will be used to process all the sampled sounds produced by the system. If this key is not set, the standard sound driver ( standard.geo ) will be used.

sampleDriver = sblaster.geo

synthDriver

synthDriver = <driver file name>

This key specifies the sound driver that will be used for all synthesized sounds (beeps, UI sounds, etc.) If this key is not set, the standard sound driver ( standard.geo ) will be used.

synthDriver = standard.geo
| Up: GEOS SDK TechDocs | Up | Prev: 2.26 [serial] | Next: 2.28 [spool]


[spool]

The INI File: 2.28 Categories in the INI File: [spool]

| Up: GEOS SDK TechDocs | Up | Prev: 2.27 [sound] | Next: 2.29 [system]
simpleUI = <Boolean>

The spool category has a single key used by the print spooler to configure its user interface. If true, the simpleUI key will display only a simple UI scheme; this is especially useful for small-screen devices because it significantly reduces the size of the print control dialog box. The default is false.

simpleUI = true
simpleUI = false
| Up: GEOS SDK TechDocs | Up | Prev: 2.27 [sound] | Next: 2.29 [system]


[system]

The INI File: 2.29 Categories in the INI File: [system]

| Up: GEOS SDK TechDocs | Up | Prev: 2.28 [spool] | Next: 2.30 [text]

The system category defines system configuration and setup. Most of the keys in this category are set and maintained by the Preferences manager application. These keys, with their formats and possible values, are shown in the following sections.

autoCursorCentering

autoCursorCentering = <Boolean>

If this key is set true, the mouse cursor is re-positioned to the center of the current window whenever the pointer cursor changes and no button is down.

continueSetup

continueSetup = <Boolean>

If this key is set true, GEOS will begin by running the graphical setup program in the proper setup modes. If false, GEOS will bypass the graphical setup. After running, the graphical setup program will reset this field to false. This field overrides the execOnStartup key of the ui category.

continueSetup = true
continueSetup = false

drive

drive <letter> = <number>

This key allows you to override the drive-map initialization done by the primary filesystem driver. You can not make a driver believe a nonexistent drive exists, but you can change the presumed media or make the driver ignore a drive. More than one drive may be remapped.

The letter argument is the drive letter of the drive to be remapped. The number argument defines the new drive definition and is one of the following values:

  -1		fixed disk
   0		ignore the drive
 360		360 K 5.25-inch disk
 720		720 K 3.5-inch disk
1200		1.2 meg 5.25-inch disk
1440		1.44 meg 3.5-inch disk
2880		2.88 meg 3.5-inch disk

Some examples of drive remappings are shown below:

drive d = 0			; ignore drive D:
drive a = 360			; make GEOS think drive A: is 360K

font

font = <driver file names>

This key causes the named font driver to be loaded. If this key doesn't exist, nimbus.geo will automatically be loaded (the default driver). More than one driver may be specified on a single line or in a blob format, as shown in the examples below. (Note, though, that at current only nimbus.geo is recognized.)

font = nimbus.geo otherdrv.geo
font = { nimbus.geo
       otherdrv.geo }

fontid

fontid = <font name>

This key specifies the default font used in the event a requested font does not exist. This font will also be used when putting up system alert boxes (such as Abort/Retry boxes). The only available default font currently is Berkeley; typically, this will be a bitmap font rather than an outline font.

fontid = berkeley

fontmenu

fontmenu = <string of numbers>

This field specifies the order of fonts which should appear in font menus presented by font control objects. This is encoded as a string of numbers, four hex digits for each font, those four digits containing the font ID of the appropriate font. Thus, if the font ID's for the URW Roman and Berkeley fonts are 0x3000 and 0x0202, respectively, then if they are to be the first fonts in the font menu, the fontmenu field would read:

fontmenu = 30000202

fontsize

fontsize = <number>

This key specifies the point size of the default font. If an application requests a font that can't be found, the default point size specified here is used with the font specified with fontid . Berkeley supports 9, 10, 12, 14, and 18, though 18 is normally too large for many applications.

fontsize = 10

fonttool

fontmenu = <string of numbers>

This field specifies the order of fonts which should appear in font pop-up list presented by font control objects. This is encoded as a string of numbers, four hex digits for each font, those four digits containing the font ID of the appropriate font. Thus, if the font ID's for the URW Roman and Berkeley fonts are 0x3000 and 0x0202, respectively, then if they are to be the first fonts in the font pop-up list, the fontool field would read:

fonttool = 30000202

fs

fs = <driver file names>

This key defines the file system drivers to be loaded. The kernel will by default attempt to load the primary IFS driver for the detected version of DOS; if it can not determine the primary IFS driver, the proper driver must be specified in the INI file under this key. Multiple file system drivers may be specified either on a single line or in blob format. The current secondary IFS drivers available are

netware.geo
Used for Novell Netware systems.
msnet.geo
Used for LANtastic and other networks that support the standard DOS device redirection calls.
cdrom.geo
Used for CD-ROM drives accessed through MSCDEX.EXE.
fs = netware.geo
fs = { msnet.geo
     cdrom.geo }

handles

handles = <number>

This key specifies the number of handles GEOS should set as the maximum in the handle table. This should be set to something most likely 2000 or above, and it may be set in the Preferences manager application. If nothing is set in this key, the kernel will assume a default of 1000 handles.

handles = 2000

inkTimeout

inkTimeout = <number>

This key sets the number of ticks (60 ticks in a second) the system will wait after the user has stopped drawing before processing ink input. The default is nine tenths of a second, or 54.

inkTimeout = 54

maxTotalHeapSpace

maxTotalHeapSpace = <size of heap in paragraphs>

This field causes the GeodeLoad() routine to operate in transparent launch mode. The value given represents the overall size of the heap, in paragraphs, excepting system libraries that are always in memory. It should be determined on the target machine itself, by starting up, then running the TCL function "heapspace total". A common value for this field is around 31000.

memory

memory = <driver file names>

This key defines the swap drivers that should be loaded. Swap drivers allow GEOS to use memory above the conventional 640 K. The kernel attempts to determine what type of memory is available and load the appropriate swap driver. This key is settable by the user with the Preferences manager application. The four driver names recognized are

emm.geo
LIM 4.0 standard expanded memory driver. A DOS-level memory driver must be loaded (e.g. EMM.SYS), typically in CONFIG.SYS.
extmem.geo
80286 extended memory driver.
xms.geo
XMS/HIMEM.SYS driver. A DOS-level driver must also be loaded (e.g. HIMEM.SYS), typically in CONFIG.SYS.
disk.geo
Disk swap driver. This should be loaded in all cases where a disk swap file is desired.
memory = disk.geo
memory = { disk.geo
         xms.geo }

noFontDriver

noFontDriver = <Boolean>

If true, this key tells GEOS not to load the font driver; this is useful only when it is known beforehand that outline fonts are not available; it will reduce startup time of GEOS. If the key does not exist, it defaults to false. If used improperly, this key can cause bad things to happen in the system.

noFontDriver = true
noFontDriver = false

notes

notes = <string>

This field isn't used by GEOS proper. The Debug utility will search for this field when looking for text describing a platform which the .ini file simulates.

noVidMem

noVidMem = <Boolean>

If true, this key tells GEOS not to load the vidmem driver, which is used for printing; it will reduce startup time of GEOS and should be used only if it is known beforehand that printing will not be attempted. If the key does not exist, it defaults to false. If used improperly, this key can cause bad things to happen in the system.

noVidMem = true
noVidMem = false

pda

pda = <Boolean>

This field indicates whether GEOS is running on a PDA device. Currently this field is only used by the UI to provide alternate error strings.

penBased

penBased = <Boolean>

If true, this key tells GEOS that it is running on a pen-based system and that some objects will want to receive ink or other pen input. If the key is not set, it defaults to false.

penBased = true
penBased = false

power

power = <driver file name>

This key defines the power management drivers to be loaded, if any. If no driver is specified, the kernel will try to identify whether one is needed and then load it if necessary.

power = casio.geo

serialNumber

serialNumber = <number>

This key holds a predefined serial number for use by developers. This number will be given to you by Geoworks either directly or in the package you receive containing GEOS. Normally, this number is entered by the user when GEOS first finishes its graphical setup program.

setupMode

setupMode = <number>

This key indicates the mode of the graphical setup program. This should be a number from zero to three; for full graphical setup, set it to zero. Other modes are internal in nature and should not be set.

setupMode = 0

splashColor

splashColor = <Color value>

If the splashscreen option has been turned on, this field will determine the background color of any text splash screens shown.

splashscreen

splashscreen = <Boolean>

This permits the GEOS loader to display a message on one of the five simple graphics mode screens while GEOS is loading.

splashText

splashText = <string>

If the splashscreen option has been turned on, this field will determine the text of the message to display.

| Up: GEOS SDK TechDocs | Up | Prev: 2.28 [spool] | Next: 2.30 [text]


[text]

The INI File: 2.30 Categories in the INI File: [text]

| Up: GEOS SDK TechDocs | Up | Prev: 2.29 [system] | Next: 2.31 [ui]

The text category defines various characteristics of GEOS for the text objects, the spelling checker, and localization.

autoCheckSelections

autoCheckSelections = <Boolean>

If true, this key instructs the spelling checker to check the spelling of the selected text automatically when the user brings up the spell-check box. The default is true.

autoCheckSelections = true
autoCheckSelections = false

autoSuggest

autoSuggest = <Boolean>

If true, this key instructs the spelling checker to suggest other spellings automatically if a misspelling is found. The default is false.

autoSuggest = true
autoSuggest = false

dialect

dialect = <dialect code>

This key defines the dialect code used by the dictionary for spelling. Different dictionaries use different dialects within their own language. The Each dialect is represented by a number; the default setting is 128. The different dialects are listed below, by dictionary.

English		 32	IZE British		(realize/colour)
         64	ISE British		(realise/colour)
        128	American		(realize/color)
Dutch		 64	Standard and non-preferred forms
        128	Standard Dutch forms only
French		 64	Accents on uppercase characters
        128	No accents on uppercase characters
German		 64	German Doppel s
        128	German Scharfes s
Norwegian		 64	Nynorsk standard
        128	Bokmal standard
Portuguese		 64	Brazilian Portuguese
        128	Iberian Portuguese

Some examples of setting the dialect are shown below.

dialect = 64
dialect = 128

dictionary

dictionary = <file name>

This key allows the user or the Preferences manager application to set the dictionary used by the spelling checker. The dictionary is set by specifying the file name of the dictionary data file; the default value is that for the English dictionary.

dictionary = IDNF9111.DAT					; Danish
dictionary = IENC9121.DAT					; English
dictionary = IFRF9121.DAT					; French
dictionary = IGRF9112.DAT					; German
dictionary = IITF9110.DAT					; Italian
dictionary = IPOF9110.DAT					; Portuguese
dictionary = ISPF9110.DAT					; Spanish

language

language = <number>

This key specifies the language in use by GEOS. The number is a language code (as shown in the examples below), and the user may set the language with the Preferences manager application. The default is English, 16.

language = 5				; French
language = 6				; German
language = 7				; Swedish
language = 8				; Spanish
language = 9				; Italian
language = 10				; Danish
language = 11				; Dutch
language = 12				; Portuguese
language = 13				; Norwegian
language = 14				; Finnish
language = 15				; Swiss
language = 16				; English

languageName

languageName = <name of language>

This key specifies the name of the language in use; the default is American English. This key is normally set by the Preferences manager application.

languageName = American English

resetSkippedWordsWhenBoxCloses

resetSkippedWordsWhenBoxCloses = <Boolean>

If true, this key instructs the spelling checker to reset its list of skipped words when the user closes the spelling check box. The default is true.

resetSkippedWordsWhenBoxCloses = true
resetSkippedWordsWhenBoxCloses = false

smartQuotes

smartQuotes = <Boolean>

If true, this key instructs the text object to use "smart quotes," quotation marks that curl themselves appropriately to their positions when typed. If this key is false, standard typewriter-style quotation marks will be used. The default is false; this is settable by the user in the Preferences manager application.

smartQuotes = true
smartQuotes = false
| Up: GEOS SDK TechDocs | Up | Prev: 2.29 [system] | Next: 2.31 [ui]


[ui]

The INI File: 2.31 Categories in the INI File: [ui]

| Up: GEOS SDK TechDocs | Up | Prev: 2.30 [text] | Next: 2.32 [<specific ui name>]

autosave

autosave = <Boolean>

If true, this key tells GEOS to turn on the automatic backup feature; this may be set in the Preferences manager application.

autosave = true
autosave = false

autosaveTime

autosaveTime = <number>

This key indicates the number of seconds between autosave operations, if the autosave keyword is set to true. This may be set with the Preferences manager application.

autosaveTime = 300

background

background = <filename>

This key defines the file containing the picture to use as the background graphic. This is normally set by the Preferences manager application.

background = Bricks

backgroundattr

backgroundattr = <t, c, or x>

This key defines how the background picture should be displayed; it is normally set by the Preferences manager application.

t
Tile the picture.
c
Center the picture on the screen.
x
Place picture in upper-left corner of the screen.
backgroundattr = c
backgroundattr = t

backgroundcolor

backgroundcolor = <number>

This key defines the color of the background graphic. This is normally set by the Preferences manager application. The number is the color index of the color to be used.

backgroundcolor = 0
backgroundcolor = 12

deleteStateFilesAfterCrash

deleteStateFilesAfterCrash = <Boolean>

If true, this key tells GEOS to delete state files after every non-clean shutdown. If you set this, you will probably want to set the doNotDisplayResetBox key true as well.

deleteStateFilesAfterCrash = true
deleteStateFilesAfterCrash = false

doNotDisplayResetBox

doNotDisplayResetBox = <Boolean>

If true, this key tells GEOS not to display the system dialog box asking whether the user wants to reset the system or not after a crash. If you set this true, you should also set deleteStateFilesAfterCrash true, or some crashes may allow bad state files to keep GEOS from restarting properly.

doNotDisplayResetBox = true
doNotDisplayResetBox = false

execOnStartup

execOnStartup = <program list>

This key defines applications to be run when the UI is loaded (when GEOS starts up), named for their GEOS long names. The default is not to execute any additional programs.

execOnStartup = {				Lights Out Launcher
                CD Player Application }

generic

generic = <file name>

This key defines the generic UI library that is to be used by GEOS. You will not need to set this; it will default to ui.geo.

generic = ui.geo
generic = uiec.geo

hardIconsLibrary

hardIconsLibrary = <string>

This is the long name of the library which provides the hard icon UI for a PC-based demo of a PDA device.

haveEnvironmentApp

haveEnvironmentApp = <Boolean>

If true, this key indicates that GEOS is using an environment application such as Welcome. If an environment application is being used, that application must be specified in the defaultLauncher key in the uiFeatures category. During debugging, you may set this key false and set the execOnStartup key to the application you're debugging to have GEOS come up directly into your application.

haveEnvironmentApp = true
haveEnvironmentApp = false

hwr

hwr = <file name>

This key indicates the handwriting recognition library to be loaded, if any. If GEOS is not on a pen-based system ( penBased = true in the system category), then no handwriting recognition library will be loaded.

hwr = palm.geo

kbdAcceleratorMode

kbdAcceleratorMode = <Boolean>

If false , this key tells GEOS to ignore keyboard accelerators. By default, this is true and keyboard accelerators are allowed; this is independent of whether the accelerators are drawn or not. See also the uiFeatures category's windowOptions key.

kbdAcceleratorMode = true
kbdAcceleratorMode = false

noClipboard

noClipboard = <Boolean>

If true, this key prevents the UI from opening the clipboard file on startup. This is an optimization used when we want to open the clipboard only later on in a particular application.

noSpooler

noSpooler = <Boolean>

If true, this key prevents the UI from launching the spooler. This can be used to improve startup time if the system running GEOS knows beforehand that the spooler is not required. Very few systems will set this true.

noSpooler = true
noSpooler = false

noTaskSwitcher

noTaskSwitcher = <Boolean>

If true, this key prevents the UI from loading a task switch driver. This may be used to improve startup time if the system running GEOS knows in advance it will never use a task switcher.

noTaskSwitcher = true
noTaskSwitcher = false

noTokenDatabase

noTokenDatabase = <Boolean>

If true, this key prevents the token database from being initialized. This is useful as an optimization when GEOS will not need icons--that is, when GEOS starts up and runs just a single application.

noTokenDatabase = true
noTokenDatabase = false

overstrikeMode

overstrikeMode = <Boolean>

If false , this key prefents the user from switching into overstrike mode; it defaults to true, and it is settable in the Preferences manager application.

overstrikeMode = true
overstrikeMode = false

password

password = <Boolean>

This field turns the system password on or off.

passwordText

passwordText = <string>

Encrypted text of the password string, if any.

penInputDisplayType

penInputDisplayType = <number>

This key defines the PenInputDisplayType to be shown when the PenInputControl object is brought up. The PenInputControl object displays the floating keyboard in one of several display types. See the PenInputDisplayType enumerated type for definitions of its values.

penInputDisplayType = 1					; floating keyboard
penInputDisplayType = 7					; handwriting area

productName

productName = <name>

This key holds the string displayed in the GEOS shutdown dialog box; for example, it will put up a string similar to "Are you sure you want to exit <productName>?"

productName = GEOS

screenBlanker

screenBlanker = <Boolean>

If this field true, then the user wishes to save the screen after an idle time period specified by means of the screenBlankerTimeout field.

screenBlankerTimeout

screenBlankerTimeout = <number of minutes>

If the user has turned on screen blanking, this is the number of minutes the system will stand idle before screen-saving turns on.

showTitleScreen

showTitleScreen = <Boolean>

If true, this key instructs GEOS to put up a title screen. This defaults to false.

showTitleScreen = true
showTitleScreen = false

sound

sound = <Boolean>

If true, this key instructs GEOS to turn sound on. If it's false, sound will be off. This is settable by the Preferences manager application.

sound = true
sound = false

specific

specific = <file name>

This key defines specific UI libraries to be loaded by GEOS. It defaults to motif.geo.

specific = motif.geo

tinyScreen

tinyScreen = <Boolean>

If true, this key tells GEOS that it's running on a small-screened device such as the Zoomer; it defaults to false. You can use this key during development if you're working on applications for a small-screen platform; it affects certain characteristics of the UI.

tinyScreen = true
tinyScreen = false

unbuildControllers

unbuildControllers = <Boolean>

If true, the UI will destroy the child blocks of controllers when the controller's menu/dialog box is closed. The child block will have to be regenerated every time the menu/dialog is openned--this is a memory for time tradeoff.

xScreenSize

xScreenSize = <number>

This key tells GEOS the screen width, in GEOS coordinates. If this key isn't set explicitly, the kernel will set it to the default screen size. This key is used primarily when developing for small-screen platforms such as Zoomer.

xScreenSize = 256

yScreenSize

yScreenSize = <number>

This key tells GEOS the screen height, in GEOS coordinates. If this key isn't set explicitly, the kernel will set it to the default screen size. This key is used primarily when developing for small-screen platforms such as Zoomer.

yScreenSize = 344
| Up: GEOS SDK TechDocs | Up | Prev: 2.30 [text] | Next: 2.32 [<specific ui name>]


[<specific ui name>]

The INI File: 2.32 Categories in the INI File: [<specific ui name>]

| Up: GEOS SDK TechDocs | Up | Prev: 2.31 [ui] | Next: 2.33 [ui features]

Each specific UI may have a category with options; this category should be named after the specific UI, e.g. [motif].

fontid

fontid = <font>

This field allows the user to specify a font that the specific UI should use when drawing text monikers for gadgets such as menus and buttons.

fontsize

fontsize = <size in points>

This field allows the user to specify a font size that the specific UI should use when drawing text monikers for gadgets such as menus and buttons.

| Up: GEOS SDK TechDocs | Up | Prev: 2.31 [ui] | Next: 2.33 [ui features]


[ui features]

The INI File: 2.33 Categories in the INI File: [ui features]

| Up: GEOS SDK TechDocs | Up | Prev: 2.32 [<specific ui name>] | Next: 2.34 [welcome]

The ui features category defines the UI configuration used by the environment application (e.g. Welcome) and all applications on the execOnStartup list in the ui category. On systems with no environment application, this category defines the UI configuration for all applications.

Related categories, uiFeatures - intro , uiFeatures - beginner , and uiFeatures - advanced , support the same keys. Each of these categories defines the configuration for a specific "room" of the Welcome application. Other environment applications will also use these keys for different "rooms."

backupDir

backupDir = <relative path>

This key defines the directory in which the document control object will place quick-backup copies of document files. The default is PRIVDATA\BACKUP.

backupDir = DOCUMENT\BACKUP

defaultLauncher

defaultLauncher = <relative path>

This key defines the directory and application that acts as the default application launcher. This key should always have some application specified; otherwise, no application will start when GEOS loads. The path specified should be relative to the WORLD directory.

defaultLauncher = Utilities\GeoManager

docControlFSLevel

docControlFSLevel = <number>

This key specifies the document control's file selector user level. The file selector box has three different configurations; set the appropriate number (below) to determine which configuration is used.

0	No directories
1	Directories shown, simple UI configuration
2,3	Directories shown, complete UI configuration

An example of setting the file selector level is shown below.

docControlFSLevel = 2

docControlOptions

docControlOptions = <number>

This key turns on or off a number of other features in the document control object. The features are controlled by the bits set or clear in the number given. The five most significant bits of a 16-bit integer are used and have the following meanings, listed from most significant bit:

DCO_BYPASS_BIG_DIALOG (0x8000)
If set, this indicates that the big dialog box normally presented for New File/Open File/Use Template operations should be bypassed (not used). For advanced users, this bit should be clear. For novice users, this bit should be set.
DCO_TRANSPARENT_DOC (0x4000)
If set, this indicates that a "Switch Document" metaphor should be used in place of the New/Open/Close metaphor for document management. This will allow only a single document open at a time and will immediately prompt if no document is open. For introductory and novice users, this bit should be set.
DCO_HAVE_FILE_OPEN (0x2000)
If set, this indicates that there is an Open button in the File menu (subject to specific UI rules). This is typically not set.
DCO_FS_CANNOT_CHANGE (0x1000)
If set, this indicates that the file selector used by the document control object can not change configuration; that is, the file selector will not offer the option of switching between full and simple configurations.
DCO_NAVIGATE_ABOVE_DOC (0x0800)
If set, this indicates that the document control object's file selector will allow the user to navigate directories. If cleared, the user may not navigate above the default document directory.

expressOptions

expressOptions = <number>

This key defines the configuration of the express menu. It sets and clears features based on the least significant 11 bits of a 16-bit number. Each bit, from the most significant used (bit 10) down to the least significant, is detailed below.

UIEO_GEOS_TASKS_LIST
If set, this indicates that the express menu should contain a list of currently-running applications.
UIEO_DESK_ACCESSORY_LIST
If set, this indicates that the express menu should contain a list of applications in the World\Desk Accessories directory.
UIEO_MAIN_APPS_LIST
If set, this indicates that the express menu should contain a list of applications in the World directory.
UIEO_OTHER_APPS_LIST
If set, this indicates that the express menu should contain a hierarchical list of applications in subdirectories of the World directory.
UIEO_CONTROL_PANEL
If set, this indicates that the express menu should contain a control panel area.
UIEO_DOS_TASKS_LIST
If set, this indicates that the express menu should contain a list of available DOS tasks accessible by a task switcher.
UIEO_UTILITIES_PANEL
If set, this indicates that the express menu should contain a utilities panel area.
UIEO_EXIT_TO_DOS
If set, this indicates that the express menu should contain an "Exit to DOS" type of trigger.
UIEO_POSITION
This is a three-bit field indicating where the express menu should appear. Three different values are allowed:
    0	No express menu
    1	In the top of the Primary window
    2	In the lower left (just below the bottom
        of the screen)

Some examples of this key are shown below.

expressOptions = 617					; Introductory
    ;  617 = 0x0269 = 0000 0010 0110 1001
    ; The bits turned on are listed below:
    ; UIEO_DESK_ACCESSORY_LIST
    ; UIEO_CONTROL_PANEL
    ; UIEO_DOS_TASKS_LIST
    ; UIEO_EXIT_TO_DOS
    ; UIEO_POSITION = 1, upper left of window
expressOptions = 889					; Beginner
    ;  889 = 0x0379 = 0000 0011 0111 1001
expressOptions = 2041					; Advanced
    ; 2041 = 0x07F9 = 0000 0111 1111 1001

helpOptions

helpOptions = <number>

This key defines the configuration used by the help controller object. Specifically, it determines whether the help controller will automatically provide help triggers in the GenPrimary and in dialog boxes. The default is to allow help triggers to be created and displayed. Only the least significant bit of a 16-bit number is used, and that bit's significance is shown below.

UIHO_HIDE_HELP_BUTTONS
If set, this indicates that the help controller should not display help triggers in the GenPrimary or in dialog boxes.
helpOptions = 1				; hide help triggers
helpOptions = 0				; display help triggers

interfaceLevel

interfaceLevel = <number>

This key determines the interface level of applications that use the ui features category for their configurations. The four values allowed are shown in the examples below.

interfaceLevel = 0				; Introductory
interfaceLevel = 1				; Beginner
interfaceLevel = 2				; Intermediate
interfaceLevel = 3				; Advanced

interfaceOptions

interfaceOptions = <number>

This key determines two different features of the UI in general. It uses the two most significant bits of a 16-bit integer; the two bits have the following meanings.

UIIO_OPTIONS_MENU
If set, this indicates that an Options menu should exist.
UIIO_DISABLE_POPOUTS
If set, this indicates that the UI should not allow GIV_POPOUT GenInteraction objects to pop in an out.
interfaceOptions = 16384					; No Options menu
interfactOptions = 32768					; Popouts not allowed

launchLevel

launchLevel = <number>

This key controls the interface level of the applications allowed to be launched under the particular field ("room" of the environment application). It allows four values as shown in the examples below.

launchLevel = 0			; Introductory
launchLevel = 1			; Beginner
launchLevel = 2			; Intermediate
launchLevel = 3			; Advanced

launchModel

launchModel = <number>

This key controls how applications are started and exited. It allows four values, each of which defines a different level of user.

launchModel = 0			; Transparent (user does not
            ; realize he is starting an
            ; application
launchModel = 1			; Single instance only
launchModel = 2			; Multiple instances allowed
launchModel = 3			; Advanced features allowed

launchOptions

launchOptions = <number>

This key controls how applications are started and exited; specifically, it has a single flag which determines whether any applications are allowed to be in desk accessory mode. This defaults to true to allow desk accessories. The single flag is the most significant bit of a 16-bit integer.

UILO_DESK_ACCESSORIES
If set, this indicates that desk accessories should be allowed.
launchOptions = 32768				; allow desk accessories
launchOptions = 0				; do not allow them

quitOnClose

quitOnClose = <Boolean>

If true, this key forces the closure of all applications in a room before that room may be exited. This will cause state saving to be turned off. The default for this flag is false. One note: setting quitOnClose = true and launchModel = 0 can result in undesirable behavior.

quitOnClose = true
quitOnClose = false

windowOptions

windowOptions = <number>

This key controls different window system options. The precise interpretation of each flag is up to the specific UI. The high 8 bits form a mask of the bits to affect. The low 8 bits indicated whether the masked bits should be turned on or off. Of the eight bits, the high one is meaningless; the seven flags are listed below. You should not set these, however, unless you are familiar with the workings of the UI and the specific UI.

UIWO_MAXIMIZE_ON_STARTUP
If true, application primary windows will come up maximized. Desk accessory applications may override this behavior.
UIWO_COMBINE_HEADER_AND_MENU_IN_MAXIMIZED_WINDOWS
If true, the title bar and menu bar areas of maximized windows will be combined to save screen space. Only the window gadgetry and menus are retained; title strings are eliminated.
UIWO_PRIMARY_MIN_MAX_RESTORE_CONTROLS
If true, window gadgetry for maximizing, minimizing, and restoring the window will be included on the screen.
UIWO_WINDOW_MENU
If true, a Window menu for keyboard control of minimize, maximize, restore, move, resize, and close operations will be provided. If false, only a "close" button will appear in the menu's place.
UIWO_PINNABLE_MENUS
If true, menus will be pinnable.
UIWO_KBD_NAVIGATION
If true, keyboard accelerators and keyboard navigation will be enabled.
UIWO_POPOUT_MENU_BAR
If true, menu bars will be allowed to pop out to be dialog boxes. This should be used in limited situations because specific UIs may not provide gadgetry to restore the menu bar if the dialog is closed.
| Up: GEOS SDK TechDocs | Up | Prev: 2.32 [<specific ui name>] | Next: 2.34 [welcome]


[welcome]

The INI File: 2.34 Categories in the INI File: [welcome]

| Up: GEOS SDK TechDocs | Up | Prev: 2.33 [ui features]

The welcome category defines configuration and usage characteristics of the Welcome environment application. Its keys may be useful to you during development, though you will probably not need them for your applications.

startupRoom

startupRoom = <name of room>

This key defines the room in which Welcome will start when GEOS is run. This is settable in the Preferences manager application; you will probably want to set this to the room most appropriate for your application to speed startup when debugging. The default is no setting, which will cause Welcome to present its title screen.

startupRoom = 1				; Beginner
startupRoom = 2				; Intermediate
startupRoom = 3				; Advanced

| Up: GEOS SDK TechDocs | Up | Prev: 2.33 [ui features]


Resource Editor

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Glossary
| One-page Version of this Chapter for Easy Printing

The Resource Editor is a tool created by Geoworks to translate UI elements of an existing GEOS program into another language.

We've made ResEdit usable by non-programmers. Ideally, the only skills one should need to localize an application to a foreign language is knowledge of the source and target languages--the translator shouldn't have to work with the source code. You may wish to give just this piece of documentation to your translator instead of burdening them with a full documentation set.

1 Glossary
2 Getting Started
3 What Needs to be Translated?
4 Creating A Translation File
5 Translating Text
5.1 Translating Via an ASCII File
5.2 Translating With ResEdit
6 Creating a Translated Geode
7 Updating an Executable
8 Testing the Translated Geode
9 Resource Editor Menus
9.1 File Menu
9.2 Edit Menu
9.3 Project Menu
9.4 Filter Menu
9.5 Utilities Menu
9.6 Window Menu

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Glossary
| One-page Version of this Chapter for Easy Printing


Glossary

Resource Editor: 1 Glossary

| Up: GEOS SDK TechDocs | Up | Prev: Resource Editor | Next: 2 Getting Started

In explaining how to use the Resource Editor, we use some vocabulary to describe certain operations and program constructs.

Geode, Executable
A "geode" or "executable" is a GEOS program: a GEOS application, library, or driver.
Source Executable
The original-language program.
Destination Executable
The local-language executable, which is created by translating the source executable.
Resource
Geodes are divided into memory blocks called resources. Resources may contain code that will be executed, data that will be referred to, or text and bitmaps which will be displayed to users. The Resource Editor will only allow you to edit those resources which contain strings or bitmaps. Each resource has a unique number or extended name.
Chunk
Resources are, in turn, divided into chunks. Inside a resource, there are many different kind of chunks, each containing a unique piece of information, such as a text string, a bitmap, or an object structure. Each chunk has a unique number or name. Your translation will consist of new contents for these chunks.
Driver
A driver is a special sort of executable that performs a very specific function that may not be needed by every user. Many drivers are responsible for interactions with particular kinds of hardware peripherals. In GEOS, there are DOS, video, mouse, communications, and printer drivers, along with a few other types. Most drivers are designed to work with more than one device. For example, the EPSON9.GEO printer driver actually supports more than 50 printers--if you were to localize the driver, you might need to translate the names of those printers.
Localization File
When a geode is created, an extra file to be used by the Resource Editor is created at the same time. This file (which is named like the .geo file but with a .vm extension) contains localization information about the resources and chunks which you will be editing. You cannot localize a geode without this file.
A programmer may create a localization file much as they would compile a geode. Using the GEOS SDK, issue the command "pmake full" to compile a Localization file in addition to a geode.
Translation File
The file that the Resource Editor creates is called a Translation File. The information stored in this file will be merged with the source executable to create the destination executable. Internally, the file holds a copy of the original-language text or bitmap, along with your translation and may contain some translation instructions.
Moniker
Text or graphics which serves as the visual name for objects such as menu items, button labels, and dialog box options. E.g., the File menu's moniker is "File"; an OK button would have moniker "OK".
Text String
Text in the geode which is not a moniker.
Mnemonic
A key that, when pressed with the Alt key, will activate a specific UI gadget which has this letter or symbol underlined in its moniker.
GString
Graphics string. This data structure is used for describing many types of graphics. Most application icons and document icons are GString Monikers.
Bitmap
Bitmap image.
Keyboard Shortcut
A combination of keystrokes than when pressed will cause an object to perform a specific action. Keyboard shortcuts are stored in the chunk containing the object to which they are connected.
Updating
Unfortunately, software often changes. As you make changes to the original-language software, it might become necessary for you to take a Translation File you created with an older version of a particular geode and update it with the newer version. The newest software may contain new strings, may have modified existing strings, or may have eliminated some strings. The Resource Editor is however able to match these two executables and show the newest changes. The process of updating a file will be discussed in more detail later in these instructions.
| Up: GEOS SDK TechDocs | Up | Prev: Resource Editor | Next: 2 Getting Started


Getting Started

Resource Editor: 2 Getting Started

| Up: GEOS SDK TechDocs | Up | Prev: 1 Glossary | Next: 3 What Needs to be Translated?

Make sure that there are two GEOS installations on your computer: one to hold the original-language version, and one for the translated version. These are often referred to as the source directory and destination directory, respectively.

You will need original-language versions of the geodes you will translate. These should reside in a directory under the source directory. Most applications will be in the WORLD directory. The localization file for each geode you'll translate should be in the same directory as its associated geode.

You'll use ResEdit to generate a foreign-language version of the geode. ResEdit will place the translated geode in the destination directory, at the same relative position as the original language version.

For example, you might have source directory D:\GEOS20.NC and destination directory D:\LATIN. Your original-language application, Connect 4, might reside in D:\GEOS20.NC\WORLD\GAMES. You'd need to copy CONNECT_.VM to D:\GEOS20.NC\WORLD\GAMES. When finished with translating, the resulting translated geode, perhaps named Connect IV, would appear in D:\LATIN\WORLD\GAMES.

To specify the locations of the Source directory or the Destination directory, choose the appropriate item from the Project menu. The Destination directory should not be the same directory as the Source directory.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Glossary | Next: 3 What Needs to be Translated?


What Needs to be Translated?

Resource Editor: 3 What Needs to be Translated?

| Up: GEOS SDK TechDocs | Up | Prev: 2 Getting Started | Next: 4 Creating A Translation File

To create a localized version of your applications, you will need to translate only the applications, libraries, and drivers you have created. All Geoworks software will be translated by Geoworks.

Most applications will use GEOS system libraries whose UI is visible from the application. For example, an application which includes spell checking will have a dialog box containing all the information about the spell checking operation which is included from the Spell library. If the Spell library is not translated, that UI will always appear in its original form. Your application will work with the Spell library, whether or not it has been translated.

At this point you are ready to translate geodes into the target language. The following set of steps can be carried out by a non-programmer running the ResEdit tool. Of course, the translator should have a good idea of the geode's function and use.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Getting Started | Next: 4 Creating A Translation File


Creating A Translation File

Resource Editor: 4 Creating A Translation File

| Up: GEOS SDK TechDocs | Up | Prev: 3 What Needs to be Translated? | Next: 5 Translating Text

To begin creating new Translation Files, double click on the "Res Edit" icon in the Tools folder of the WORLD directory of the original-language version of Ensemble 2.0.

This brings up a dialog box which gives you three choices:

  • Create New Translation File
  • Open a Translation File
  • Import an ASCII Translation File

Choose the "Create New" option. Another dialog box appears, allowing you to select the localization (.vm) file for the geode you wish to edit.

The first time you start the Resource Editor, it assumes you will be editing geodes in the GEOS20.NC directory, or whatever installation of GEOS you started in. Once you have a Translation File open, you can change the top-level directory by clicking on the "Set source directory" in the Project Menu. This setting is saved in your .ini file, so the next time you start ResEdit, it will automatically set the top-level source directory to what it was when exited.

To create a new Translation File for a geode that is not in the GEOS20 installation (or whichever installation is currently set as the top-level source directory), change the top-level source directory first--because you cannot access the Project menu with no Translation File open, you may need to create a new Translation File and keep it around until you can access the Project menu to change the Source directory.

| Up: GEOS SDK TechDocs | Up | Prev: 3 What Needs to be Translated? | Next: 5 Translating Text


Translating Text

Resource Editor: 5 Translating Text

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Creating A Translation File | Next: 6 Creating a Translated Geode

There are two main approaches to translating a geode's text. Some people prefer to work with an ASCII text file. Others prefer to use ResEdit's graphical interface.

We will discuss both methods.

Translating Via an ASCII File

Translating With ResEdit

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Creating A Translation File | Next: 6 Creating a Translated Geode


Translating Via an ASCII File

Resource Editor: 5.1 Translating Text: Translating Via an ASCII File

| Up: GEOS SDK TechDocs | Up | Prev: 5 Translating Text | Next: 5.2 Translating With ResEdit

Choose Other from the File menu; in the submenu that appears, choose Export Document. In the dialog that appears, choose a file location and name for the ASCII Translation File (ATF).

If you have already gone through the translation process once, and only wish the ASCII Translation File to contain entries for those chunks which have been translated, select the Export only changed chunks box.

Press the OK button.

You may now edit the ASCII file to translate strings.

The important lines of the file are of the form:

[keyword   ] data

There should always be one space between the keyword and the data. The following keywords appear in the header:

[ source ]
This is the long name of the geode.
[ path ]
This is the geode's path, relative to the Source directory. Applications will typically be in the WORLD path.
[ version ]
The geode's version number.
[ DOS name ]
The geode's DOS filename.

The ASCII Translation File then lists all resources in the geode, by means of [resource ] lines and [endrsrc ] lines. Do not change the text of these lines. If, examining a resource, you realize it contains no chunk entries you wish to change, you may delete it to make it easier to find the resources you do wish to change. To do so, delete the [resource ] line, the [endrsrc ] line, and all lines in between.

Within each resource, there may be one or more chunks containing localizable strings. For each such chunk, there will be five lines:

[chunk     ] chunkname
[original  ] {original text}
[localize  ] {instructions} max, min, type
[translate ] {translated text}
[mnemonic  ] mnemonic [shortcut  ] mods,shortcut

You will leave most of these fields alone, translating only the translated text , mnemonic , mods , and shortcut fields.

chunkname
The chunkname is a unique identifier for the chunk. Do not change this.
original text
The original text in the chunk. Do not change this. The text is delimited by curly braces ("{" and "}"). If any curly braces occur in the string, they will be represented by two curly braces.
instructions
Instructions to the person translating. Do not change this. These instructions are taken from the original-language geode. The geode's programmers specified the instructions by means of the @localize keyword in the source code files.
max
Maximum number of characters you may use in the translated text. Do not change this. The geode's programmers specified this number by means of the @ localize keyword.
min
Minimum number of characters you may use in the translated text. Do not change this. The geode's programmers specified this number by means of the @localize keyword.
type
The text's purpose in the program. Do not change this. A Text Moniker is the visible name of a gadget. A piece of Text appears in the program, but not as the name of a gadget.
translated text
This is how the text will appear in the translated geode. You'll probably want to change this text. The text is delimited by curly braces. If you want the translated string to contain curly braces, you should double them--use "{{" and "}}". E.g., to have translated text "moustache: :-{)", the line would read
    [translate ] {moustache: :-{{)}
If the original text looks like a shortcut key (e.g., "{(Physical Ctrl u)}", then to translate the keyboard shortcut, work with mods and shortcut (described below) and leave this field alone.
mnemonic
This is the character the user will use when navigating the translated geode's UI with keyboard navigation. Only Text Monikers may have mnemonics. The mnemonic should be either a character which occurs in the moniker, the keyword ESC or the keyword NIL (which signals that there is no mnemonic).
You must be careful not to assign the same mnemonic to two monikers whose gadgets are both in the active window at the same time. If there is such a conflict, only one of the two gadgets will be activated by this mnemonic. When you have created the translated executable, you should check that there are no conflicting mnemonics, and change any which overlap with others at the same level.
mods , shortcut
Together, mods and shortcut form a keyboard shortcut which the user may use to activate a UI gadget in the translated geode. For instance, Ctrl-S typically activates the Save item of the File menu. This shortcut would be represented by
    [shortcut] PC,s

If, for some reason, the question mark were to be a shortcut, it would be represented by

    [shortcut	] ,?

Only monikers may have shortcuts. The lack of shortcut is represented by

    [shortcut] ,

Be careful when representing the lack of a shortcut: the carriage return or line feed at the end of the line must occur immediately following the comma. If there is an intervening space, ResEdit will think you want to use the space bar as a shortcut key.

The following mods are available: A (Alt), S (Shift), C (Ctrl), and P ("Physical"--Num Lock and Caps Lock state is disregarded). These modifiers may be combined as in the Ctrl-S example.
The shortcut should be a single character. If this character is a letter, pay attention to its case--it should probably be a lower-case letter.

If you will not need to translate a chunk, you may delete its entry to make it easier to find the chunks you do wish to translate. Delete the [chunk ] line and the [shortcut ] line and all lines in between.

To convert your ASCII Translation File into a regular Translation File, use ResEdit to import the file: either press the Import button in the dialog that appears when ResEdit starts up, or choose New/Open from the File menu, and then choose Import in the dialog that appears. In the dialog that appears, show ResEdit the location of both the ASCII Translation File and the Localization (.vm) File.

Press the OK button.

If your original-language geode has changed since the ASCII Translation File was created, ResEdit may present a warning dialog box at this point. Assuming the geode hasn't changed too much, it's probably safe to use the ASCII Translation File anyway. If you're hesitant, you can abort the import. You might then want to export another ASCII Translation File from the newer version of the geode.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Translating Text | Next: 5.2 Translating With ResEdit


Translating With ResEdit

Resource Editor: 5.2 Translating Text: Translating With ResEdit

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Translating Via an ASCII File | Next: 6 Creating a Translated Geode

After selecting a localization (.vm) file to translate, the Resource Editor will then load the information from this file and the corresponding geode into an untitled Translation File saved into the DOCUMENT directory. The file will be displayed in two views. The left portion of the screen is the Source File, while the right portion is the Translation File. When the file is first opened, the two sides look the same, since no translations have yet been made.

The Resource Editor brings up all the strings or bitmaps which need to be translated, regardless of their context. The only way you will be able to be completely sure that the translation is accurate is to perform the translation and then actually run the translated executable (to be discussed later) and see how the translations appear in the program.

Translating a Text String

Click on any text string in the right portion of the screen. The blinking text editing cursor should appear, and you will be able to edit the text in the right screen to reflect the changes necessary for the local language. The text in the left portion of the screen acts as a guide and will not change.

At the bottom of the main screen, there are three fields which may have important information about how you should translate the selected chunk.

  • The Minimum and Maximum Length fields show the size limits on the number of characters that the selected chunk may have.
  • The Instructions field shows special characteristics of the selected chunk, or may give a context where the specific chunk appears.

If there is nothing in these fields, there are no constraints on the size or content of the translation.

Changing a Mnemonic

This is the character the user will use when navigating the translated geode's UI with keyboard navigation. Only Text Monikers may have mnemonics. The mnemonic should be either a character which occurs in the moniker, the keyword ESC or the keyword NIL (which signals that there is no mnemonic).

You must be careful not to assign the same mnemonic to two monikers whose gadgets are both in the active window at the same time. If there is such a conflict, only one of the two gadgets will be activated by this mnemonic. When you have created the translated executable, you should check that there are no conflicting mnemonics, and change any which are found to overlap with others at the same level.

To choose a different mnemonic, use the Mnemonic gadget in the upper-right area of the ResEdit window. Use the up- or down- buttons to choose a letter from the text moniker, or type in your letter of choice. If you choose a letter which does not occur in the text moniker, it will appear in parentheses.

Changing a Keyboard Shortcut

Keyboard shortcuts, also known as keyboard accelerators, are key combinations which may be used to activate a piece of UI gadgetry. For instance, Ctrl-S is a common keyboard shortcut for activating the Save item under the File menu.

In ResEdit, keyboard shortcuts are displayed when there is a chunk of type Object that has a shortcut. The shortcut will appear as something like "(Physical Ctrl p)".

To translate the shortcut, press the Shortcut... button at the top center of the ResEdit window. This will bring up a dialog box allowing you to specify that there should be no shortcut, or to specify a keyboard combination for a translated shortcut.

The following modifier keys are available: Alt, Shift, Ctrl. The Physical option specifies that Num Lock and Caps Lock states are disregarded when matching the shortcut character.

Translating a Graphic

You may wish to translate a graphic. For example, a calendar application written for an English-speaking audience might have an icon depicting a desk calendar displaying the date "Apr 7"--but in a foreign language, "Apr" might not be anything a customer would see on a calendar.

To translate a graphic:

  1. Copy the graphic to be translated--select the displayed graphic in ResEdit, and choose Copy from the Edit menu.
  2. Start up the GeoDraw graphics editor program, creating a new GeoDraw document if necessary.
  3. Paste the graphic into the GeoDraw document--choose Paste from GeoDraw's Edit menu.
  4. Edit the bitmap, being careful not to change the dimensions of the bitmap.
  5. When you are done editing the bitmap, select the bitmap with the arrow tool, and choose Copy from GeoDraw's Edit menu.
  6. Return to the ResEdit application, select the graphic to translate, and choose Paste from ResEdit's Paste menu.

Choosing a Chunk

ResEdit will display the chunks of one resource at a time. To translate all the chunks in a geode, you'll need to work with chunks from several resources.

There are several ways to choose a different resource.

  • Type Ctrl-> (Ctrl-greater) to move to the next resource or Ctrl-< (Ctrl-less) to move to the previous resource.
  • Click on Next Resource/Previous Resource in the Utilities menu.
  • Click on the Resource pop-up list in the upper left corner of the screen and choose any resource.

Within a resource, there are several ways to select a chunk to edit.

  • Use the mouse to click on any chunk.
  • Type Ctrl-.(Ctrl-period) to move to the next chunk or Ctrl-, (Ctrl-comma) to move to the previous chunk.
  • Click on Next Chunk or Previous Chunk in the Utilities menu.
  • Click on the Chunk pop-up list in the upper left corner of the screen and choose any chunk.
| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Translating Via an ASCII File | Next: 6 Creating a Translated Geode


Creating a Translated Geode

Resource Editor: 6 Creating a Translated Geode

| Up: GEOS SDK TechDocs | Up | Prev: 5 Translating Text | Next: 7 Updating an Executable

After all of the chunks and resources in the selected .vm file have been translated and saved in a Translation File, a new local-language executable can be created in the target installation.

The first step to placing the new executable in the correct place is making sure that the localization target kit has been placed in another directory on your hard drive, for example C:\ESPANOL.

Then select the Destination directory option from the Project menu. It will ask you to choose the top level GEOS directory that will hold the modified executables. In this case, you would choose the C: drive from the Drives list, and choose \ESPANOL in the scrolling list of directories.

You need not have a full GEOS installation in the target directory. However, when new executables are created, they are placed into the same subdirectory as the original geode. For example, if you were to translate GeoWrite, which is in the WORLD directory, and you had the destination directory set to ESPANOL, the directory ESPANOL\WORLD must exist when you try to create the new executable. If it doesn't, then you will get an error message saying that the target could not be created.

To create the translated geode, choose Create New Executable... from the File menu.

This new geode will have the identical original-language long name as the original geode (unless you have changed the name in the File Menu's New Name and User Notes dialog). You may wish to rename several of the geodes, especially those in the WORLD directory and the Screen Savers, to reflect local-language names.If you change the name, the new geode will not overwrite the original one, and you will have to go back and delete the old one to avoid duplicates.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Translating Text | Next: 7 Updating an Executable


Updating an Executable

Resource Editor: 7 Updating an Executable

| Up: GEOS SDK TechDocs | Up | Prev: 6 Creating a Translated Geode | Next: 8 Testing the Translated Geode

Even after all the translations are complete, there may be a need to replace a certain executable with a newer version containing a bug fix or a feature enhancement. You will be able to do this quickly and effectively by using the Update feature in the File menu.

To make this feature work, you must first put the updated geode and .vm file in the proper source directory, overwriting the original files. Then, by clicking Update Translation File (in the Update submenu of the File menu), the existing Translation File will be compared with the new strings in the geode, showing the new or changed chunks, as well as the ones left unchanged. The deleted chunks will be grouped in a separate resource named "Deleted chunks" at the end of the resource list. You will then be able to make the necessary modifications to this file to bring it up to date with the newest version of the geode.

If the name of the original-language geode has changed, you will need to use the Update Geode Name function (in the Update submenu of the File menu) to tell ResEdit about the new name.

After updating the Translation File, you can then create an updated local-language executable as out lined above.

If there have been no changes to the geode that affect editable chunks (as when code changes for a bug fix), it is also possible to simply rebuild the new geode from the existing Translation File by choosing Create A New Executable from the File menu.

After viewing the updated Translation File and making any necessary changes, you can remove the updating information from the Translation File by choosing Commit the Update trigger from the File/Update menu. This will remove information which marks the chunks as "new" or "changed" as well as remove the Deleted Chunks resource.

| Up: GEOS SDK TechDocs | Up | Prev: 6 Creating a Translated Geode | Next: 8 Testing the Translated Geode


Testing the Translated Geode

Resource Editor: 8 Testing the Translated Geode

| Up: GEOS SDK TechDocs | Up | Prev: 7 Updating an Executable | Next: 9 Resource Editor Menus

You may wish to be able to test your translations as soon as you make them. At any point during a translation, you can create an executable, as described in Creating a Translated Geode .

Change directory to your Destination directory (e.g., \ESPANOL) start that installation, and test your new geode.

If your product will run under several video drivers (e.g., if it runs on desktop PCs), we suggest that you take a look at the localized UI under the CGA video driver to make sure that everything fits. You should also observe it under the EGA video mode with a large UI font size.

| Up: GEOS SDK TechDocs | Up | Prev: 7 Updating an Executable | Next: 9 Resource Editor Menus


Resource Editor Menus

Resource Editor: 9 Resource Editor Menus

| Up: GEOS SDK TechDocs | Up | Down | Prev: 8 Testing the Translated Geode | Next: 9.1 File Menu

The quick overview of the menu items below should give you the necessary information to use all of the features.

File Menu

Edit Menu

Project Menu

Filter Menu

Utilities Menu

Window Menu

| Up: GEOS SDK TechDocs | Up | Down | Prev: 8 Testing the Translated Geode | Next: 9.1 File Menu


File Menu

Resource Editor: 9.1 Resource Editor Menus: File Menu

| Up: GEOS SDK TechDocs | Up | Prev: 9 Resource Editor Menus | Next: 9.2 Edit Menu
New/Open :
Brings up the New/Open dialog box, allowing you to create a new Translation File, open an existing one, or import an ASCII Translation File.
Close :
Closes the active Translation File.
Save :
Saves the active Translation File.
Save As :
Saves the active Translation File under a different name.
Backup :
Makes a backup of the active Translation File or restores from a backup file.
Other :
Copy To :
Creates a copy of the Translation File and places it in the directory of your choice.
Move To :
Moves the Translation File to the directory of your choice.
Export Document :
Creates an ASCII Translation File. See Translating Via an ASCII File for details.
Discard Changes :
Discards all changes since last saved version of the Translation File.
Rename :
Gives the Translation File a new name.
Edit Document Notes :
Edits the notes for the Translation File.
Set Document Password :
Creates a document password.
Set Document Type :
Allows the user to choose between a normal, read-only, or public document.
New Name and User Notes :
Allows user to change geode name and make annotations to the file for later reference.
Create a New Executable :
Takes the Translation File, merges it with the source geode, creates a new translated geode, and puts the translated geode in the Destination directory.
Update :
Update Geode Name :
Use this to warn ResEdit that the name of the original-language geode has changed. (Otherwise, ResEdit will not be able to find the original-language geode.)
Update Translation File :
Using a new or updated geode, matches chunks in the old file with those in the new file, creating an updated Translation File which shows new or changed chunks and groups deleted chunks at the end.
Commit the Update :
Deletes intermediate matching information, deleting those chunks which have been marked for deletion, showing only the newest version of the Translation File.
Print :
Allows you to print out a report of the Translation File to paper. You must have configured your printer if this is to work.
Exit :
Exits the Resource Editor.
| Up: GEOS SDK TechDocs | Up | Prev: 9 Resource Editor Menus | Next: 9.2 Edit Menu


Edit Menu

Resource Editor: 9.2 Resource Editor Menus: Edit Menu

| Up: GEOS SDK TechDocs | Up | Prev: 9.1 File Menu | Next: 9.3 Project Menu
Cut :
Takes highlighted information and moves it to the clipboard.
Copy :
Takes highlighted information and copies it to the clipboard, leaving a copy in place.
Paste :
Pastes information from the clipboard to the location of the cursor.
Undo :
Reverts to previous saved version of individual chunk.
Find and Replace :
Allows user to search for designated words throughout the Translation File and replace text in the Translation File (as displayed in the right view) with local language translation. You can limit a forward or backward search by selecting filters for certain types of chunks from the Filters menu.
| Up: GEOS SDK TechDocs | Up | Prev: 9.1 File Menu | Next: 9.3 Project Menu


Project Menu

Resource Editor: 9.3 Resource Editor Menus: Project Menu

| Up: GEOS SDK TechDocs | Up | Prev: 9.2 Edit Menu | Next: 9.4 Filter Menu

The items in this menu keep track of how your project is organized.

Source directory :
Set the top level directory holding source geodes.
Destination directory :
Set the top level directory holding modified geodes.
Reset Source Geode Path:
Reset the path of the source geode if it moves to a different subdirectory.
| Up: GEOS SDK TechDocs | Up | Prev: 9.2 Edit Menu | Next: 9.4 Filter Menu


Filter Menu

Resource Editor: 9.4 Resource Editor Menus: Filter Menu

| Up: GEOS SDK TechDocs | Up | Prev: 9.3 Project Menu | Next: 9.5 Utilities Menu

Each of the "Don't show ..." menu items in the Filter menu is a radio button which allows you to turn on or off a filter. By turning on a filter, you ask that ResEdit not show chunks of a certain type. These options can be helpful when editing updated Translation Files, making it easier to find those items you want to work with.

Don't show Text:
If this filter is on, ResEdit won't display any text monikers or other text chunks.
Don't show Monikers:
If this filter is on, ResEdit won't display any monikers, whether graphic or text.
Don't show GStrings:
If this filter is on, ResEdit won't display any graphic strings. This will filter out graphic monikers.
Don't show Bitmap s:
If this filter is on, ResEdit won't display any bitmap graphics. This will filter out most graphic monikers, since most of these are simple graphics strings displaying bitmaps.
Don't show Objects
ResEdit will normally display the keyboard shortcuts associated with an object so that you may change the shortcut. If this filter is on, then ResEdit won't try to display the objects, and thus no keyboard shortcuts will appear.

Each of the "Show ..." menu items is a radio button which allows you to view specific chunks affected by updating the Translation File. At most one of these may be selected at a time. To show all chunks, make sure that none of these menu items are selected--choose the selected item to un-select it.

Show changed chunks
Show new chunks
Show deleted chunks
| Up: GEOS SDK TechDocs | Up | Prev: 9.3 Project Menu | Next: 9.5 Utilities Menu


Utilities Menu

Resource Editor: 9.5 Resource Editor Menus: Utilities Menu

| Up: GEOS SDK TechDocs | Up | Prev: 9.4 Filter Menu | Next: 9.6 Window Menu

The first four of these menu items allow for quick movement between chunks and resources:

Next Chunk
Previous Chunk
Next Resource
Previous Resource

The last item provides a count of words in the original-language geode. It provides a count both of the total number of words and of the number of unique words (i.e., not counting repeated words):

Word Count
| Up: GEOS SDK TechDocs | Up | Prev: 9.4 Filter Menu | Next: 9.6 Window Menu


Window Menu

Resource Editor: 9.6 Resource Editor Menus: Window Menu

| Up: GEOS SDK TechDocs | Up | Prev: 9.5 Utilities Menu

These menu items make manipulation of multiple Translation Files easy, by opening overlapping windows or tiling active windows.

Overlapping :
Choose this item to allow Translation File windows to overlap. This is probably only useful if you are working with more than one such file at a time.
Full-Sized :
Force each Translation File to take up all of Resource Editor's document display area.
Tile :
If you're working with multiple Translation Files, this will lay them out side by side.
| Up: GEOS SDK TechDocs | Up | Prev: 9.5 Utilities Menu


Swat Introduction

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 DOS Command Line Options
| One-page Version of this Chapter for Easy Printing

Most programmers are familiar with the process of debugging code. Many, however, will not be familiar with the issues of debugging programs in an entirely object-oriented, multithreaded system in which memory is often sharable by multiple programs. Because this type of system presents not only incredible power but also a new class of potential bugs, the GEOS tool kit provides a new class of symbolic debugger.

Swat is more than just a debugging program; it is an extensible debugging environment which you can tailor to your needs. Because all Swat commands are programmed in a special language called Tool Command Language (Tcl), you can write your own commands or extend existing commands. Tcl was originally developed by Professor John Ousterhout at the University of California in Berkeley, California. Swat itself was developed during a project headed by professor Ousterhout for a distributed multiprocessing operating system; Geoworks gained permission from the university to use and modify Swat. Since then, we have tailored it for use with GEOS; thus, it is the most appropriate debugger for any GEOS programmer.

Swat is essentially a system debugger, as opposed to an application debugger. This is an important distinction, due to the multithreaded nature of individual GEOS applications--each application may have more than one thread, and a system debugger greatly eases the debugging process for such applications. Swat also has many other features that make it preferable to other debuggers:

  • Ideal for multithreaded environment
    Because Swat was initially designed for debugging multithreaded system software, it is ideal for use on multithreaded applications in the GEOS environment.
  • Extremely flexible
    Nearly every part of Swat you will use is written in Tcl. Swat allows you to create your own commands or extend existing commands by using the Tcl language; you can examine, print, and modify just about any data structure using various Tcl commands. For large applications and projects, the customization this offers can be a tremendous asset.
  • Extensive access to data structures
    You can examine any byte in the test PC's memory while Swat is attached. You can examine any basic or complex data structure. For example, the "pobject" command ("print object") prints the entire instance data of the specified object. You can specify memory locations with symbols, registers, addresses, pointers, or a number of other ways.
  • Easy modification of code and data
    Using Tcl commands, you can change the contents of a register or memory location. You can also create patches to be executed at certain points in your program; this speeds up debugging by reducing the fix-compile-download-attach-debug cycle to a simple fix-debug cycle.
  • Interactive single-step facility
    By using Swat's single-step command, you can execute a single instruction at a time. Swat shows you all the inputs going in to the instruction so you can more easily keep track of what's going on.
  • Facilities for watching messages and objects
    Swat has several "watch" facilities. For example, you can let GEOS continue executing while watching a particular object; Swat will display all messages sent to the object, along with the data passed. You can also watch a particular message; Swat will display the destination and passed data each time the message is sent.
  • On-line help system
    Swat commands are documented on-line. The help facility is available from the basic Swat command prompt, and you can use it either as an interactive help shell or to get help on one particular topic.

Swat also offers many other features that both novices and experts will use. As you gain familiarity with Swat while using Swat with the sample applications and with your own programs, you will discover your own preferred methods of debugging.

1 DOS Command Line Options
2 Notation
3 Address Expressions
4 On-line Help
5 Essential Commands
5.1 Cycle of Development
5.2 Attaching and Detaching
5.3 Breakpoints and Code Stepping
5.4 Examining and Modifying Memory
5.5 Other Important Commands
6 Additional Features

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 DOS Command Line Options
| One-page Version of this Chapter for Easy Printing


DOS Command Line Options

Swat Introduction: 1 DOS Command Line Options

| Up: GEOS SDK TechDocs | Up | Prev: Swat Introduction | Next: 2 Notation

To use Swat, you must have the pccom tool running on the target machine. You may then invoke Swat on the development machine. Swat takes the following command-line flags:

-e
Start up the non-error-checking version of the loader on the target machine.
-C
Disable symbol file path caching.
-h
Displays a usage line.
-k
Use a non-standard kernel file.
-n
Start up the non-error-checking version of GEOS on the target machine.
-r
Start up the error-checking version of GEOS on the target machine.
-s
Start up the error-checking loader on the target machine.
-D
You will only need to use this flag if debugging Swat.

If you have set up your communications incorrectly, Swat will have problems. (Often these problems don't show up when sending or receiving files; Swat demands more from the communications programs than pcsend and pcget.)

One common problem arises when other devices are generating interrupts which are not being successfully masked out. If, for instance, you have a network card which is operating on IRQ 2, you must make sure that either the pccom tool or else Swat is called with the /i:a option. Swat will intercept IRQ level 5 by default. To determine what number to pass with the /i, take the IRQ level, add 8, and express the result in hexadecimal.

If Swat complains that it is "timing out" on some operation, you may have your communication speed set too high. Try changing the baud rate field in PTTY to a lower value.

Normally, Swat insists that any geodes it examines should have their symbolic information in the appropriate subdirectory of your root GEOSSDK development directory--the possible subdirectories are Appl, Library, Driver, and Loader. To ask Swat to look in different places for these sorts of files, change the SWAT.CFG file in your PCGEOS\BIN directory. You may also specify a fifth directory in which to look for geodes. You may specify absolute pathnames in this file; if you give relative paths, they will be assumed to start at the directory specified with your ROOT_DIR variable.

If you add a "default" line to your SWAT.CFG file, that tells Swat where to look for .SYM files as a last resort. To save hard disk space, some developers delete rarely-accessed .SYM files from their hard drive, leave their SDK CD in the CD-ROM drive, and specify the GEOSSDK directory as the default place to look for .SYM files:

default D:\GEOSSDK
| Up: GEOS SDK TechDocs | Up | Prev: Swat Introduction | Next: 2 Notation


Notation

Swat Introduction: 2 Notation

| Up: GEOS SDK TechDocs | Up | Prev: 1 DOS Command Line Options | Next: 3 Address Expressions

The rest of this chapter is devoted to interacting with Swat once you have it running. Most of this is done by means of commands typed at a prompt. Some Swat commands may have subcommands, some may have flag options, and some combine the two. Others may have special options; all, however, are documented with the following conventions.

  • command (alternative1|alternative2|...|alternativeN)
     The parentheses enclose a set of alternatives separated by vertical lines (in C, the bitwise OR operator character). For example, the expression quit (cont|leave) means that either quit cont or quit leave can be used.
  • command <type_of_argument>
     The angled brackets enclose the type of an argument rather than the actual string to be typed. For example, <addr> indicates an address expression and <argument> indicates some sort of argument.
  • command [optional_argument]
     The brackets enclose optional arguments to the command. For example, the command alias [<word>[<command>]] could have zero, one, or two arguments because the command and word arguments are optional. Another example would be the command objwalk [<addr>] , which may take zero arguments if it is meant to use the default address or one argument if the user gives it a particular address to look at.
  • * +
     An asterisk following any of the previous constructs indicates zero or more repetitions of the construct may be typed. A plus sign indicates one or more repetitions of the construct may be used. For example, unalias <word>* can be the unalias command by itself, or it can be followed by a list of words to be unaliased.
| Up: GEOS SDK TechDocs | Up | Prev: 1 DOS Command Line Options | Next: 3 Address Expressions


Address Expressions

Swat Introduction: 3 Address Expressions

| Up: GEOS SDK TechDocs | Up | Prev: 2 Notation | Next: 4 On-line Help

Address expressions are used as arguments to any Swat command that accesses memory. For example, the pobject command takes an address expression as an argument and prints out information about the object at that address. An address expression can be a symbol name, which is just the name of a pointer, or a symbol path . A symbol path has the following structure, where scope is the name of a patient, module, function, structure type, union type, enumerated type, or assembly record type:

(<scope>::)+<symbol>

Some of the more common symbol path formats are:

<module>::<symbol>
<patient>::<symbol>
<patient>::<module>::<symbol>
<structure name>::<field name>

The symbol path is used when there is more than one symbol of a given name or if a symbol of a different application is needed. A symbol can be represented in a variety of ways: the name of an object, a field of a structure, a register/number combination, a number from the address history, an element of an array, nested Tcl commands, or a Tcl variable. Array indexing is used as follows:

<addr> [<n>]

which will return the zero-based element n from the given addr , even if addr is not an array.

Another important way of representing the symbol is as a segment:offset pair. In this, the segment is a constant, a register, a module, or a handle ID given as ^h<id> where id is a constant or register.

There are several operators which are used to make memory examination and manipulation easier in Swat. These operators are shown below (in order of highest precedence to lowest):

  • ^h
     The carat-h is used to dereference a memory handle when representing an address as a handle:offset pair (this is also known as a "heap pointer" representation) or when accessing a particular block of memory. It is often used in the situation when a memory handle is in one register (such as BX) and the offset is in another register (such as SI). This is similar to the ^l operator (below), but it requires an offset into the block rather than a chunk handle. The ^h operator is used thus:
    • [hello3:0] 6 => print ^hdx:ALB_appRef
  • .
     The period is used to access a field in a structure. For example, to get the top bound part of a rectangle structure stored as a global variable:
    • [hello3:0] 8 => print theRect.R_top
  • + -
     The addition and subtraction operators are used to add and subtract symbols to and from other symbols and constants. If two symbols in the same segment are subtracted, a constant will be the result.
  • ^l
     The carat-l is used to dereference an optr, a pointer in the form handle:chunk-handle (this is also known as a "local memory pointer"). This is similar to the ^h operator, but ^l requires a chunk handle rather than an offset. If an optr is stored in CX:DX, for example, the ^l operator could be used to dereference it as follows:
    • [hello3:0] 11 => pobj ^lCX:DX
[hello3:0] 12 => pobj ^l0x43d0:0x022
  • :
     The colon is the segment/offset operator, used to separate the segment and offset in a segment:offset pair.
    • [hello3:0] 13 => pobj ^hCX:DX
[hello3:0] 14 => pobj 0x43d0:0x022
[hello3:0] 15 => pobj INTERFACE:HelloView
  • *
     The asterisk is a pointer-dereferencing operator, as in the C programming language:
    • [hello3:0] 16 => print SubliminalTone
@5: SubliminalTone = 7246h
    [hello3:0] 17 => print *(&SubliminalTone)
    @6: *(&SubliminalTone) = 7246h
  • ^v
     The carat-v is the virtual memory operator, used to get to the base of a block that is in a Virtual Memory file given the file handle and VM block handle. The correct usage of the ^v operator is:
    • ^v<file>:<VM_block>

Much of the time the type of data stored at the address given by the address expression is implicit in the expression. Sometimes in ambiguous situations (using code as data), however, the type of data must be explicitly stated in the address expression. This is done by indicating the type of the data followed by a space and then a normal address expression. For example, in the expression

dword ds:14h

the data at ds:14h will be treated as a double word.

| Up: GEOS SDK TechDocs | Up | Prev: 2 Notation | Next: 4 On-line Help


On-line Help

Swat Introduction: 4 On-line Help

| Up: GEOS SDK TechDocs | Up | Prev: 3 Address Expressions | Next: 5 Essential Commands

Swat provides on-line help, both for looking up Swat topics and GEOS reference material.

To get help on a specific Swat command, you simply type the following, where the command is the argument.

[hello3:0] 7 => help <cmd>

To use Swat's interactive, menu-based help system, simply type the following:

[hello3:0] 8 => help

The menu-based system provides a tree of help topics which are organized into subjects. If you are looking for Swat commands having to do with a subject not covered in the help tree, you might try using the apropos command.

To get information about a GEOS topic, use the doc functions.

apropos
apropos [<string>]

The apropos command searches the list of commands and command help for all entries containing <string>. It lists each command and its synopsis. The string may actually be a partial word.

help
help [<command>]

There are two different ways to use the help command. The first is to enter the interactive help mode using the help command with no arguments, and the second is to use the help command with a particular command as an argument.

Interactive help mode
The interactive help mode consists of a tree of commands and topics identified by different numbers. If one of the numbers is typed, information about that particular topic or command is displayed. Some of the topics have their own subtrees (indicated by the ellipses following the topic heading) which follow the same numbering format. The interactive help mode is used when looking for a certain style of command but the name of the command is not known (see The Help Tree ).

The Help Tree

(geos:0) 198 => help

top-most level of the help tree:

 0 FINISH 			6 memory... 			12 step...
 1 advanced... 			7 object... 			13 support...
 2 breakpoint... 			8 print... 			14 swat_navigation...
 3 crash... 			9 running... 			15 window...
 4 file... 			10 source...
 5 heap... 			11 stack...

Type "help" for help, "menu" to redisplay the menu, "0" to exit.
Type a topic (or its number) to display it.
help:top>
help < command >
When help is typed with another command as an argument, information about that command is displayed (the same information as in the interactive help mode). This command is frequently used in order to get fast help on a particular command. (See The help Command .)

The help Command

(geos:0) 200 => help help
Help for help:
Functions for manipulating/accessing the help tree
==============================================================================
This is the user-level access to the on-line help facilities for Swat. If
given a topic (e.g. "brk") as its argument, it will print all help strings
defined for the given topic (there could be more than one if the same name is
used for both a variable and a procedure, for instance). If invoked without
arguments, it will enter a browsing mode, allowing the user to work his/her
way up and down the documentation tree
==============================================================================
(geos:0) 201 =>
| Up: GEOS SDK TechDocs | Up | Prev: 3 Address Expressions | Next: 5 Essential Commands


Essential Commands

Swat Introduction: 5 Essential Commands

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 On-line Help | Next: 6 Additional Features

This section covers the function and usage of some of the most important Swat commands. These commands fall into the following command groups:

  • Cycle of Development
    Sending down a new copy of a geode and running it.
  • Attaching and Detaching Swat
    The commands used to control the link between Swat and GEOS.
  • Setting Breakpoints and Code Stepping
    The commands used to stop the execution of an application's code at pre-determined points and then examine the code, line by line if necessary.
  • Examination of Memory
    The commands used to examine memory from individual bytes to whole structures such as generic trees and objects.
  • Other Important Commands
    Other commands which are important to know but do not fit into the aforementioned groups.

A complete list of the Swat commands is contained in the Reference chapter.

Cycle of Development

Attaching and Detaching

Breakpoints and Code Stepping

Examining and Modifying Memory

Other Important Commands

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 On-line Help | Next: 6 Additional Features


Cycle of Development

Swat Introduction: 5.1 Essential Commands: Cycle of Development

| Up: GEOS SDK TechDocs | Up | Prev: 5 Essential Commands | Next: 5.2 Attaching and Detaching
send, run, exit, patient-default

These commands come in handy whenever you've edited and recompiled your application. You'll want to exit the application on the target machine. Use the send command to send down the new, compiled version of your application. Then use the run command to start up the program.

send

send [<geode-name>]

To send the latest compiled version of your program, type "send" followed by the application's patient name (the first part of the field on the "name" line of the .gp file).

run

run [<geode-name>]

To run your geode on the target machine, type "run" followed by the application's patient name (the first part of the field on the "name" line of the .gp file).

exit

exit <geode-name>

To exit a running application, type "exit" followed by the application's patient name. The exit command won't work if your application has encountered a fatal error.

patient-default

patient-default [<geode-name>]

Use this command to set a default patient to use with the send and run commands. The send and run commands will operate on this patient if they are not passed arguments.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Essential Commands | Next: 5.2 Attaching and Detaching


Attaching and Detaching

Swat Introduction: 5.2 Essential Commands: Attaching and Detaching

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Cycle of Development | Next: 5.3 Breakpoints and Code Stepping
attach, att, detach, quit, cont, Ctrl-C

This group of commands controls the state of the connection between Swat and GEOS when Swat is running. The attach and att commands are used to establish the connection while detach and quit are used to sever it. The most frequently used commands in this group are att with the -r flag and detach . Some related commands contained in the Reference are go , istep , sstep , and next .

The following is a typical but simplified debugging cycle using detach and att . It assumes you have already attached for the first time.

  • When a bug is encountered, determine where and what the bug is, then detach Swat by typing detach .
  • Edit the application to fix the bug, recompile it and download it to the target machine.
  • Re-attach Swat to the target PC using the att command by typing Scrolllock-Shift-s on the target machine and typing att on the host machine.
  • Continue debugging the application.
  • Repeat the detach, edit, attach, debug cycle until all of the bugs are fixed.

By themselves, the commands shown below can not do much except open and close the communication lines between GEOS and Swat. More commands to examine and modify the application's code as it runs are needed to start actual debugging.

Swat Display 3-3 Detaching and Attaching

(geos:0) 202 => det cont
PC detached
(loader:0) 203 => att
Re-using patient geos
Re-using patient ms4
Re-using patient vidmem
Re-using patient swap
Re-using patient xms
Re-using patient disk
Re-using patient kbd
Re-using patient nimbus
Re-using patient stream
Re-using patient sound
Re-using patient standard
Re-using patient ui
Re-using patient styles
Re-using patient color
Re-using patient ruler
Re-using patient text
Re-using patient motif
Re-using patient vga
Re-using patient spool
Re-using patient serial
Re-using patient msSer
Re-using patient nonts
Re-using patient welcome
Re-using patient shell
Re-using patient manager
Re-using patient math
Re-using patient borlandc
Re-using patient mess1
Thread 1 created for patient geos
Thread 2 created for patient geos
Thread 0 created for patient ui
Thread 0 created for patient spool
Thread 0 created for patient welcome
Thread 0 created for patient manager
Thread 1 created for patient manager
Thread 0 created for patient mess1
Attached to PC
Stopped in 0070h:0005h, address 0070h:0005h
DOS+773: JMP DOS+2963
(geos:0) 204 =>

In this example, we use the det cont command so that GEOS will keep running. We then re-attach with att . In the intervening time, the two machines are independent, and the serial line is unused. We could have taken advantage of this to send down a new copy of some application (as long as that application was not running on the target machine).

att

att

The att command is similar to the attach command, but has no bootstrap argument (as explained below).

attach

attach [(+b|-b)]

This command is used to attach Swat to the target PC when the Swat stub is already invoked. The -b argument is to bootstrap and the +b argument is not to bootstrap. Bootstrapping means that Swat will search for the symbol files of all of the geodes and threads as they are encountered rather than all at the beginning. This saves some time if you've just detached and need to re-attach, using only a few geodes while debugging. If no argument is given, the most recent bootstrap setting is used. The default bootstrap setting is +b .

cont

cont

The cont command continues the execution of GEOS after it has been stopped for some reason such as at a breakpoint or fatal error or by control-C. This command is often aliased as the letter c .

detach

detach [(cont|leave)]

The detach command will detach Swat from the target PC. By itself, the detach command will detach Swat and exit GEOS. This command is usually used after a bug is encountered and the source code needs to be modified and recompiled (see Detaching and Attaching ).

cont
The cont option will just detach Swat and allow GEOS to continue to run normally. This option is used when the debugging process is finished but GEOS is still needed to do other things (such as word processing) and you may need to re-attach later for further debugging.
leave
The leave option will detach Swat but keep GEOS stopped wherever it was when the detach leave command was given. This command is useful for passing debugging control to someone remotely logged in to the workstation or when Swat can not continue for some reason.

quit

quit [(cont|leave)]

The quit command is only used when Swat needs to be exited for good. It will detach Swat (if necessary), exit from Swat on the development station, and exit from GEOS.

cont
The cont option exits Swat on the development station but allows GEOS to continue running normally on the target PC. This option is used when the debugging process is finished but GEOS is still needed to do other things such as word processing.
leave
The leave option will exit Swat but will keep GEOS stopped wherever it was when the quit leave command was given.

Ctrl-C

Ctrl-C

The Control-C command is the command used to stop the execution of GEOS at any point. This command is executed by holding down the Ctrl key and pressing the c key. It is used to stop GEOS in order to set a breakpoint, examine memory, or to get a command line prompt.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Cycle of Development | Next: 5.3 Breakpoints and Code Stepping


Breakpoints and Code Stepping

Swat Introduction: 5.3 Essential Commands: Breakpoints and Code Stepping

| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Attaching and Detaching | Next: 5.4 Examining and Modifying Memory

The commands in this group are used to stop at specified breakpoints in an application's code and then step through the code line by line if necessary. These commands are often used with each other to examine critical areas in the application source code.

Breakpoints

stop, brk, go, cbrk, spawn

The stop , brk and cbrk commands are used to set breakpoints. The breakpoint commands have many subcommands controlling the actions and attributes of a particular breakpoint.

The cbrk command sets breakpoints to be evaluated by the Swat stub; brk sets them to be evaluated by Swat. The Swat stub can evaluate the conditions much faster than Swat, but cbrk has certain limitations: only a limited number of breakpoints can be set using cbrk , and these breakpoints can only compare registers when the breakpoint is hit with a given set of criteria.

stop

stop in <class>::<message> [if <expr>]
stop in <procedure> [if <expr>]
stop in <address-history-token> [if <expr>]
stop at [<file:]<line> [if <expr>]
stop <address> [if <expr>]

This is the main command to use when setting breakpoints in C programs. The "stop in" command will set a breakpoint at the beginning of a procedure, immediately after the procedure's stack frame has been set up. The "stop at" command will set a breakpoint at the first instruction of the given source line. If no <file> is specified, the source file for the current stack frame is used. If a condition is specified by means of an "if <expr>" clause, you should enclose the expression in curly braces to prevent any nested commands, such as a "value fetch" command, from being evaluated until the breakpoint is hit.

brk

brk [<sub-command>]

The brk (breakpoint) command is used for setting nearly all breakpoints in an application's code. The simplest way to use it is to type brk with a single addr argument. The address is usually a routine name for a suspect procedure, and when the breakpoint is reached the code-stepping commands can be used to examine it carefully. The brk command can also create conditional breakpoints which will only be taken if certain conditions are satisfied. Once set, a breakpoint is given an integer number which can be obtained using the list subcommand (see Breakpoints ).

brk <addr> [<command>]
The brk command without any subcommands sets an unconditional breakpoint at the address specified in addr . If the command argument is passed, the given swat command will be carried out when the breakpoint is hit.
brk delete <break>*
Deletes the given breakpoint(s), just as clear , above.
brk enable <break>*
Enables the given breakpoint(s). Has no effect on previously enabled breakpoints. If no breakpoint is given, all breakpoints for the current patient are enabled.
brk disable <break>*
Disables the given breakpoint(s). It has no effect on previously disabled breakpoints. If no breakpoint is given, it disables all breakpoints for the current patient.
brk list [<addr>]
Lists all the breakpoints, whether they are enabled, where they are set, their conditions, and what actions they will take if encountered. If addr is given, it returns the breakpoint numbers of all breakpoints set at the given address.

Breakpoints

(geos:0) 4 => brk list
Num S Address Patient Command/Condition
1   E loader::kcode::LoaderError all echo Loader death due to [penum
                    LoaderStrings [read-reg ax]]
                    expr 1
2   E kcode::FatalError all
                     why
                     assign kdata::errorFlag 0
                     expr 1
3   E kcode::WarningNotice all why-warning
4   E kcode::CWARNINGNOTICE all why-warning
(geos:0) 5 => stop in Mess1Draw
brk5
(geos:0) 6 => brk list
Num S Address Patient Command/Condition
1   E loader::kcode::LoaderError all echo Loader death due to [penum
                    LoaderStrings [read-reg ax]]
                     expr 1
2   E kcode::FatalError all
                     why
                     assign kdata::errorFlag 0
                     expr 1
3   E kcode::WarningNotice all why-warning
4   E kcode::CWARNINGNOTICE all why-warning
5   E <ss1::MESS1_TEXT::Mess1Draw+10 all halt
(geos:0) 7 => brk dis brk4
(geos:0) 8 => brk list
Num S Address Patient Command/Condition
1   E loader::kcode::LoaderError all echo Loader death due to [penum
                    LoaderStrings [read-reg ax]]
                     expr 1
2   E kcode::FatalError all
                     why
                     assign kdata::errorFlag 0
                     expr 1
3   E kcode::WarningNotice all why-warning
4   D kcode::CWARNINGNOTICE all why-warning
5   E <ss1::MESS1_TEXT::Mess1Draw+10 all halt
(geos:0) 9 =>

go

go [<address-expressions>]

The go command sets a one-time breakpoint and resumes execution on the target PC. The net effect of this is to let the target go until it hits a given address, then stop.

cbrk

cbrk [<sub-command>]

The cbrk (conditional breakpoint) command is used to set fast conditional breakpoints. This command is very similar to the brk command above, except that the condition is evaluated by the Swat stub--this increases the speed of the evaluation. There are, however, certain restrictions on the cbrk command: only a limited number of breakpoints can be set (eight), and the scope of the evaluation is limited to comparing word registers (or a single word of memory) to a given set of values.

In the following descriptions, criteria stands for a series of one or more arguments of the form:

<register> <op> <value>
register
One of the machine's registers or "thread," which corresponds to the current thread's handle.
op
One of the following ten comparison operators: = (equal), != (not equal), > (unsigned greater-than), < (unsigned less-than), >= (unsigned greater-or-equal), <= (unsigned less-or-equal), +> (signed greater-than), +< (signed less-than), +>= (signed greater-or-equal), +<= (signed greater-or-equal). These correspond to the 8086 instructions JE, JNE, JA, JB, JAE, JBE, JG, JL, JGE, JLE, respectively.
value
A standard Swat address expression. The resulting offset is the value with which the register will be compared when the breakpoint is hit.
cbrk <addr> <criteria>*
The basic cbrk command sets a fast conditional breakpoint at the address specified in addr .
cbrk cond <break> <criteria>*
Changes the criteria for the breakpoint. If no criteria is given the breakpoint becomes a standard, unconditional breakpoint.

spawn

spawn <patient-name> [<addr>]

The spawn command is used to set a temporary breakpoint in a process or thread which has not yet been created. The arguments are

patient-name
The permanent name, without extension, as specified by the name directive in the .gp file; this is the name of the patient in which to set a temporary breakpoint. A unique abbreviation is sufficient for this argument.
addr
A particular address at which to place the breakpoint. If no address is given, Swat will stop as soon as the given geode is loaded.

This command is used to stop the geode before any of its code can be run, allowing breakpoints to be set in the desired routines. If you could not stop the machine in this manner, the application could hit a buggy routine before a breakpoint could be set in that routine. The spawn command can also be used to catch the spawning of new threads which is useful to keep track of the threads being used by an application (see The spawn Command ).

Swat Display 3-5 The spawn Command

(geos:1) 12 => spawn mess1 Mess1Draw
Re-using patient math
Re-using patient borlandc
Re-using patient mess1
Thread 0 created for patient mess1
Interrupt 3: Breakpoint trap
Stopped in Mess1Draw, line 211, "C:OMNIGO/Appl/SDK_C/MESS1/MESS1.GOC"
Mess1Draw(GStateHandle gstate) 				/* GState to draw to */
(mess1:0) 13 =>

Code Stepping

srcwin, istep, sstep

Once an application is stopped at a breakpoint and you want to examine the code line by line, you can use the commands istep (instruction step) and sstep (source step). These enter the instruction step mode or source step mode to examine and execute the application code line by line.

The subcommands for both istep and sstep are nearly the same and are used for actions including stepping to the next line, skipping the next instruction, or exiting the step mode and continuing the execution of the application. The istep and sstep commands are very similar except that istep is used when stepping through assembly source code (thus stepping through instructions), and sstep is used for stepping through C source code.

srcwin

srcwin <numlines> [view]

The srcwin command will display the source code surrounding the presently executing code any time execution is stopped. The presently executing line will be highlighted. You may set breakpoints with the mouse by clicking on the line numbers which appear to the side. To scroll the srcwin buffer use the arrow keys, the <PgUp> key, and the <PgDn> key.

istep, sstep

istep [<default subcommand>]
sstep [<default subcommand>]

These two commands are used to single-step through code, executing one or more instructions at a time. The default subcommand argument determines the action taken by Swat when the Return key is pressed. For example, the command

[hello3:0] 7 => istep n

will enter instruction step mode, and subsequently pressing the Return key will have the same effect as pressing n . If no default command is given, pressing Return has the same effect as pressing s .

The subcommands to the istep and sstep commands are

s (single step)
Step one instruction. This is the most frequently used subcommand.
n, o (next, over)
Continue to the next instruction but do not display any procedure calls, repeated string instructions, or software interrupts. They will stop when GEOS returns to the same frame as the previous displayed instruction. The frame is the same when the stack pointer and current thread are the same as when the n subcommand was given. o differs from n in that it executes all instructions in a macro without further interpretation and can only be used with istep . If a breakpoint other than for the next instruction is hit, it will take effect as long as the above conditions are met.
N, O (Next, Over)
These are like n and o but will stop whenever a breakpoint is hit even if the frame is different. O will execute all instructions in a macro without further interpretation, and it can only be used with istep . If a breakpoint other than one for the next instruction is hit, it will take effect as long as the above conditions are met.
q, Esc, <space> (quit)
These stop istep/sstep and return to the command level. These subcommands are used when a point in the code is reached where another command needs to be used--to examine memory, for example.
c (continue)
This exits istep and continues the execution of the application. When GEOS next stops, Swat will return to the command prompt.
M (message)
This will continue until the next handled message is received. When the handler is invoked, Swat will return to step mode. This subcommand is often used with the ObjMessage() and ObjCallInstanceNoLock() assembly routines.
F (finish message)
This finishes the current message, stops when execution returns to a frame that is not part of the kernel, and remains in step mode.
f (finish frame)
This finishes the current stack frame, stops, and remains in step mode.
S (skip instruction)
This skips the current instruction, does not execute it, and goes on to the next instruction in step mode.
| Up: GEOS SDK TechDocs | Up | Prev: 5.2 Attaching and Detaching | Next: 5.4 Examining and Modifying Memory


Examining and Modifying Memory

Swat Introduction: 5.4 Essential Commands: Examining and Modifying Memory

| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Breakpoints and Code Stepping | Next: 5.5 Other Important Commands

The commands in this section all deal with the examination, manipulation, or modification of the memory used by an application. Memory from individual bytes to complex data structures such as objects can be displayed and examined. These commands fall into the following groups:

  • Simple Memory Examination.
    Examination of bytes, words, and double words with no modification.
  • Complex Memory Examination
    Examination of structures such as objects, generic trees, and handle tables with no modification.
  • Memory Examination with Modification
    Examination of memory with modification if desired. Some commands are used only for memory modification.

The commands in this section are often used with each other and with the code-stepping and breakpoint commands in order to pinpoint bugs in an application's code. Breakpoints can be set, code can be stepped through, and then the memory that the code uses can be examined.

Some related commands defined in the Reference chapter are down , func , handles , hgwalk , impliedgrab , penum , phandle , pinst , piv , precord , skip , systemobj , up , and where .

Simple Memory Examination

bytes, words, dwords, frame, backtrace, why, listi

The commands in this group are used to look at simple blocks of memory without modification. They are defined fully in the entries below.

bytes, words, dwords

bytes [<addr>] [<length>]
words [<addr>] [<length>]
dwords [<addr>] [<length>]

The bytes , words , and dwords commands are essentially the same except that each looks at a different sized piece of memory. These commands will display the memory as a pointer to a dump of bytes, words, or dwords using the given or most recent (if no address is given) address.

The bytes command additionally displays the dump as its ASCII character representation, if any. These three commands are used to examine memory on a very basic level and are useful only if the user knows what the bytes, words, or dwords should or should not be and so can spot any problems. For example, if a certain character string such as "Application" is supposed to be stored at the address given by fileType and the command

[hello3:0] 11 => bytes fileType

dumps the characters "noitacilppA", then there is most likely a problem.

These commands will automatically use the given addr as a pointer to the memory to be examined. If Return is hit many times in a row, the result will be to examine adjacent pieces of memory. (See The words Command .)

Swat Display 3-6 The words Command

(mess1:0) 15 => words themeSongBuf
Addr: +0 +2 +4 +6 +8 +a +c +e
0040h: 0004 0000 0049 0000 0004 0001 0083 0000
(mess1:0) 16 => words
Addr: +0 +2 +4 +6 +8 +a +c +e
004eh: 0000 0006 0004 0028 0000 0001 020b b800
(mess1:0) 17 => !!
words
Addr: +0 +2 +4 +6 +8 +a +c +e
005ch: b800 000c 0020 0002 0001 0000 0001 020b
(mess1:0) 18 => !!
words
Addr: +0 +2 +4 +6 +8 +a +c +e
006ah: 020b b800 000c 0010 0002 0001 000a 0010

Here the words command examines a buffer in memory. When this command is repeated without arguments, it will display memory continuing where the last command left off. Note the use of the !! command to repeat the previous command.

backtrace, frame

backtrace [<frames to list>]
frame <subcommand>

The backtrace and frame commands are used to examine data that has been pushed onto the stack. An application may crash in a routine that is correctly written but has been passed bad data.

The backtrace command prints out a list of all the active frames for the current patient. Then the user can choose a particular frame to examine using one of the frame subcommands. The frame command is used to access frames that have been pushed onto the stack, where a frame is the information for a routine that needs to be saved when it calls another routine.

The frame and backtrace commands can be used together to print the active frames with backtrace and then access data in these frames with frame . However, most of the frame subcommands expect a token for a frame, not the frame number given by the backtrace command. To get this token, the top , cur and next subcommands are used. Then the other frame subcommands can be used with the token to further examine the frame data. See also the reference information for the frame command.

Backtrace and Frame commands

Death due to SOUND_BAD_EVENT_COMMAND
Execution died in patient sound:
SoundHandleTimerEvent+63: MOV AX, 7 (0007h)
*** No explanation available ***
Interrupt 3: Breakpoint trap
Stopped in FatalError, address 1844h:0163h
SoundHandleTimerEvent+63: MOV AX, 7 (0007h)
(mess1:0) 2 => backtrace
  1: near FatalError(), 1844h:0163h
  2: far AppFatalError(), 1844h:0163h
* 3: far SoundHandleTimerEvent(), 2cb2h:003fh
  4: far SoundLibDriverPlaySimpleFM(), 6247h:0062h
  5: far ResourceCallInt(), 1844h:1492h
  6: far SoundLibDriverStrategy(), 2cb2h:0ab2h
  7: near SoundCallLibraryDriverRoutine(), 629ch:00feh
  8: far SoundPlayMusic(), 629ch:0028h
  9: far ResourceCallInt(), 1844h:1492h
  10: far SOUNDPLAYMUSICNOTE(mh = ^h42b0h (at 753ch), priority = 1eh, tempo = 4h
, flags = 80h), 62d6h:00f3h
  11: far ResourceCallInt(), 1844h:1492h
  12: far Mess1Draw(), MESS1.GOC:307
  13: far MESS1PROCESSMETA_EXPOSED(win = 3a60h, message = 69 (invalid), oself =
3ee0h:0000h), MESS1.GOC:362
  14: far ResourceCallInt(), 1844h:1492h
MSG_META_EXPOSED (3a60h 0000h 0000h) sent to Mess1ProcessClass (^l20f0h:0h)
  16: near ObjCallMethodTableSaveBXSI(), 1844h:9ea5h
  17: far SendMessage(), 1844h:9d9bh
  18: far ObjMessage(), 1844h:1d9ch
  19: far MessageDispatchDefaultCallBack(), 1844h:1c72h
  20: far MessageProcess(callBack = 1844h:1c68h (geos::kcode::MessageDispatchDef
aultCallBack)), 1844h:1c15h
  21: far MessageDispatch(), 1844h:1b31h
  22: far ThreadAttachToQueue(), 1844h:bd2ch
(mess1:0) 3 => frame 12
Mess1Draw+302: MOV AX, 100 (0064h)

Complex Memory Examination

print, hwalk, lhwalk, objwalk, pobject, gentree, vistree, vup, gup

The commands in this group are used to examine complex data structures in GEOS.

print

print <expression>

The print command is used to print out the value of the given expression argument. The expression argument is normally some sort of typed address. When there is no type for the expression , then its offset is printed.

The power of this command lies in its ability to print any type at any address; thus, it is used frequently to print out the values of important expressions such as registers or variables. The print command also takes many flags which control the way in which the value of the expression is displayed such as in decimal or hexadecimal. See the Reference chapter for more information on the flags for the print command.

hwalk

hwalk [<patient>]

Use the hwalk (heap walk) command to display blocks on the global heap. Its output can be tailored in various ways according to how the flags are set. If a patient is given, then hwalk will only print the blocks owned by that patient. There are many fields in the listing such as the handle, address, and type of each block. By examining these fields, the user can get an overall sense of how the global heap is being managed, whether any block looks too big or too small, and what the handles of important blocks are. (See The hwalk Command .)

Swat Display 3-8 The hwalk Command

(mess1:0) 6 => hwalk mess1
HANDLE ADDR SIZE FLAGS LOCK OWNER IDLE  OINFO 						TYPE
----------------------------------------------------------------
20f0h 41ebh 2272 FIXED  n/a mess1  n/a     1h 						R#1 (dgroup)
4160h 58eah  448 sDS  a   1 mess1 105eh    1h 						R#2 (MESS1_TEXT)
3a60h 59adh  784 s SL     0 mess1  0:03    1h 						WINDOW
4bb0h 6176h  560 s SL     0 mess1  0:05    1h 						WINDOW
3970h 6232h  336 s SL     0 mess1  0:03    1h 						GSTATE
3ee0h 633ch  160 s S  a   0 mess1  0:05 49c0h 						Geode
4950h 63beh 1280 s SL     0 mess1  0:05 49c0h 						OBJ(mess1:0)
4340h 640eh 1328   SL     0 mess1  0:05 49c0h 						R#3 (INTERFACE)
42b0h 753ch   96 s S      4 mess1 1249h    1h
4bd0h 7542h   96 s S      0 mess1  0:01    1h
41b0h 89d4h  896   SL     0 mess1  0:05 49c0h 						R#4 (APPRESOURCE)
4270h 99e1h   32 s S      0 mess1  0:05     1h

Total bytes allocated: 8288
(mess1:0) 7 =>

lhwalk, objwalk

lhwalk [<addr>]
objwalk [<addr>]

The lhwalk (local heap walk) command is used to display information about a local memory heap, and the objwalk command is used to print out information about an object block. After using hwalk to locate a specific block, lhwalk or objwalk can be used to print out information about that particular block. These commands also print out fields of information which include the local handle, the address, size, and type of data or object. See the Reference chapter for more information on the fields printed by lhwalk and objwalk . (See The objwalk Command .)

Swat Display 3-9 The objwalk Command

(mess1:0) 11 => objwalk ^h4340h

Heap at 640eh:0 (^h4340h), Type = LMEM_TYPE_OBJ_BLOCK
In use count = 3, Block size = 1328, Resource size = 59647 para (192 bytes)

HANDLE ADDRESS 	SIZE FLAGS CLASS (NAME)
------ ------- 	---- ----- ------------
 001ch     56h 		1eh  ---  *flags*
 001eh     76h 		c1h  D RO GenPrimaryClass (Mess1Primary)
 0020h    1aah 		ceh  D RO GenViewClass (Mess1View)
 0022h    166h 		32h ID  O OLGadgetAreaClass
 0024h    19ah 		 eh  I
 0026h    492h 		6bh  D  O GenValueClass
 0028h    2deh 		6bh  D  O GenValueClass
 002ah    37eh 		a6h ID  O GenInteractionClass
 002ch    44ah 		46h ID  O OLMenuBarClass
 002eh    13ah 		 bh ID
 0030h    426h 		22h ID  O OLMenuButtonClass

Free handles = 17, null handles = 0
Objects = 8, 4 of them marked ignoreDirty

(mess1:0) 12 =>

pobject

pobject [<addr>] [<print level>]

The pobject (print object) command (often abbreviated pobj ) is used to print out the entire instance data chunk of an object. You can use gentree , vistree , or hwalk and objwalk to get the handles for an object; once you have them, use pobj with the handles, as follows:

[hello3:0] 7 => pobj ^l0x43d0:0x0022

will print out the instance data chunk specified by that optr.

Any valid address expression, such as a dereferenced object name, may be used as an addr . Additionally, the print level can be changed to print just the headings to each of the master levels and an address history number. The pobject command is used to verify that the object is behaving correctly and that its instance variables (if any) are correct. (See The pobject Command .)

Swat Display 3-10 The pobject Command

[lesink:0] 10 => pobj ^l4710h:0020h
*UpTextView::UpTextViewClass (@7, ^l4710h:0020h)
master part: Gen_offset(123) -- UpTextViewInstance
@8: {UpTextViewInstance (^h18192:622)+123} = {
    MetaBase Gen = {
        ClassStruct _far *MB_class = 360ah:162fh (motif::dgroup::OLPaneClass)
    }
    LinkPart GI_link = {
        dword LP_next = 4710h:001fh
    }
    CompPart GI_comp = {
        dword CP_firstChild = 4710h:002ah
    }
    word GI_visMoniker = 0h
    word GI_kbdAccelerator = 0h
    byte GI_attrs = 2h
    byte GI_states = c0h
    PointDWFixed GVI_origin = {
        DWFixed PDF_x = {0.000000}
        DWFixed PDF_y = {0.000000}
    }
    RectDWord GVI_docBounds = {
        long RD_left = 0
        long RD_top = 0
        long RD_right = +480
        long RD_bottom = +480
    }
    PointDWord GVI_increment = {
        long PD_x = +20
        long PD_y = +15
    }
    PointWWFixed GVI_scaleFactor = {
        WWFixed PF_x = {1.000000}
        WWFixed PF_y = {1.000000}
    }
    ColorQuad GVI_color = {
        CQ_redOrIndex = fh, CQ_info = 0h, CQ_green = 0h, CQ_blue = 0h
    }
    word GVI_attrs = 10h
    byte GVI_horizAttrs = 88h
    byte GVI_vertAttrs = 88h
    byte GVI_inkType = 0h
    dword GVI_content = 4710h:0024h
    dword GVI_horizLink = 0000h:0000h
    dword GVI_vertLink = 0000h:0000h
}
Variable Data:
         *** No Variable Data ***
[lesink:0] 11 =>

In addition to printing information about the object at a given address, pobject can print information about certain objects in the application if passed certain flags:

pobject -i
Prints information about the windowed object under the mouse pointer.
pobject -c
Prints information about the content for the view over which the mouse is located.

There are more flags available, and it is also possible to ask for more or less instance data information. See the full reference for this command for details.

gentree

gentree [<addr>] [<instance field>]

The gentree (generic tree) command prints out a generic tree from the given addr and instance field . The addr must be the address of an object in the generic tree, and the instance field must be the offset into the Generic master part of the instance chunk or any instance data within the Generic master level which is to be printed. This command is used primarily to ensure correct structure of a generic tree and its instance data and to find a particular object in the generic tree. The -i (implied grab) option is used to find an object by placing the mouse over the window in which the object resides and typing the following:

[hello3:0] 7 => gentree -i

The default address that gentree examines is contained in *DS:SI. (See Gentree and Gup .) To examine objects more closely, pass the handles displayed by gentree to the pobject command.

vistree

vistree [<addr>] [<instance field>]

The vistree (visual tree) command prints out a visual tree from the given addr and instance field . The addr must be the address of an object in the visual tree, and the instance field must be the offset into the Vis master part of the object's instance data which is to be printed. This command is primarily used to examine the on-screen layout of the application and to ensure correct structure of the visual tree and its instance data. The vistree command can use the -i option (implied grab), which will use the window that the mouse is over as the first visual object in the printed tree. The default address that vistree examines is contained in *DS:DI. To examine objects more closely, pass the handles displayed by vistree to the pobject command.

gup

gup [<addr>] [<instance field>]

The gup (Generic UPward query) command is used to go up the generic tree from a particular object specified by the addr argument, the default *DS:SI, or the -i option. The -i option (implied grab) uses the windowed object under the mouse as the object from which to start the upward query. This command is used primarily to ensure correct generic class hierarchy and to determine the field of the given object.

Swat Display 3-11 Gentree and Gup

(mess1:0) 19 => gentree -i

GenViewClass (@5, ^l4340h:0020h)
 GenValueClass (@6, ^l4340h:0026h)
 GenValueClass (@7, ^l4340h:0028h)

(mess1:0) 20 => gup @5

GenViewClass (@11, ^l4340h:0020h)
GenPrimaryClass (@12, ^l4340h:001eh) "MESS #1"
GenApplicationClass (@13, ^l41b0h:0024h) *** Is Moniker List ***
GenFieldClass (@14, ^l4080h:001eh)
GenSystemClass (@15, ^l2460h:0020h)

(mess1:0) 21 => gentree ^l4340h:001eh

GenPrimaryClass (@16, ^l4340h:001eh) "MESS #1"
 GenViewClass (@17, ^l4340h:0020h)
 GenValueClass (@18, ^l4340h:0026h)
 GenValueClass (@19, ^l4340h:0028h)

vup

vup [<addr>] [<instance field>]

The vup (Visual UPward query) command is used to examine the visual ancestors of a particular object given by the addr argument, the default *DS:SI, or the -i option. The vup command can be used with the -i option (implied grab) to use the windowed object under the mouse as the object from which to start the upward query. This command is used primarily to ensure correct visual class hierarchy and to determine the field of the given object.

Memory Examination with Modification

assign, imem

The commands in this group are used to modify memory without detaching Swat and editing the application code. They are often used in conjunction with istep , sstep , and pobject to fix any small errors while the code is executing rather than detaching, modifying the actual code, and recompiling. These fixes are temporary, and you must change the source code to enact the real bug fixes.

assign

assign <addr> <value>

The assign command will assign the given value to the given addr , which can only have type byte , word , or dword . Both memory locations and registers may be assigned new values. This command is used to correct minor mistakes or test differing values at run-time without having to recompile.

imem

imem [<addr>] [<mode>]

The imem (inspect memory) command combines examination and modification of memory into one command. It can be used to search through areas of memory and modify problem areas selectively. The command is used to print out memory starting at either the given addr or at the default DS:SI in one of the following modes:

b (bytes)
Displays the memory in terms of bytes.
w (words)
Displays the memory in terms of words.
d (double words)
Displays the memory in terms of double words.
i (instructions)
Displays the memory in terms of instructions.

There are many subcommands to imem which are executed in the same manner as those for istep and sstep . These subcommands are as follows:

b, w, d, i
These will reset the mode to the given letter and redisplay the data in that mode.
n, j, <return> (next, jump)
This will advance to the next piece of data using the appropriate step size (dependent upon the display mode).
p, k, P (previous)
This will retreat to the preceding piece of data. While in instruction mode, if the displayed instruction is wrong, try again with the P subcommand.
<space>
This will clear the data being displayed and allow you to enter a new value in accordance with the current mode. This is exactly like the assign command except for singly and doubly quoted strings. A singly quoted string such as `hello' will have its characters entered into memory starting at the current address with no null byte at the end. A doubly quoted string such as "good-bye" will be entered into memory at the current address with the addition of a null byte at the end of the string. This subcommand may not be used in instruction mode.
q (quit)
Quits the imem mode and returns to the command level.
Ctrl-d
Control-d (down) displays ten successive memory elements in the current display mode.
Ctrl-u
Control-u (up) displays ten of the preceding memory elements in the current display mode.
| Up: GEOS SDK TechDocs | Up | Prev: 5.3 Breakpoints and Code Stepping | Next: 5.5 Other Important Commands


Other Important Commands

Swat Introduction: 5.5 Essential Commands: Other Important Commands

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Examining and Modifying Memory | Next: 6 Additional Features
alias, mwatch, objwatch, save, switch, sym-default, why

These commands are important to know but do not readily fall into any of the previous categories. This section will discuss each of these commands in relation to the debugging process.

alias

alias [<name> [<body>]]

This command is normally used to abbreviate a long command or series of commands with one single, descriptive command. If no arguments are given, then alias will just give a list of all the aliases and the commands they alias. The alias command is a convenient shortcut for oft used commands or for commands that take a long time to type.

If only one argument is given, then alias will try to match that argument to the command it is aliased to. For example, if the print command is aliased to p , then alias p will return print as a result. If two arguments are given, then alias will cause argument1 to be allowed as an alternative to typing argument2. For example, if the command print were to be aliased as p , the alias command would be used as below:

[hello:0] 5 => alias p print

Typing p will now have the same effect in Swat as typing print .

mwatch

mwatch [<message>+]

The mwatch (message watch) command watches a particular message and displays all deliveries of that message without stopping GEOS. This command can help to verify that a particular message is getting sent to all the right places and is not sent to any of the wrong places. Up to eight messages can be watched at once, and the mwatch command with no arguments clears all watched messages. Note that some message handlers will relay a message on to a superclass' handler; this may make it appear that the message is being delivered again, though this is not the case. (See The mwatch Command .)

Swat Display 3-12 The mwatch Command

(ui:0) 30 => mwatch MSG_VIS_DRAW MSG_META_EXPOSED
(ui:0) 31 => c
MSG_VIS_DRAW, ^l2860h:001eh, GenInteractionClass
 cx = 3f80h, dx = 0000h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:001eh, GenInteractionClass
 cx = 3f80h, dx = 0000h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:001eh, GenInteractionClass
 cx = 3f80h, dx = 0016h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:001eh, GenInteractionClass
 cx = 3f80h, dx = 0016h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:002ch, OLGadgetAreaClass
 cx = 3f80h, dx = 3950h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:002ch, OLGadgetAreaClass
 cx = 3f80h, dx = 3950h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:002ch, OLGadgetAreaClass
 cx = 3f80h, dx = 007ch, bp = 3950h
MSG_VIS_DRAW, ^l2860h:002ch, OLGadgetAreaClass
 cx = 3f80h, dx = 007ch, bp = 3950h
MSG_VIS_DRAW, ^l2860h:0020h, GenTextClass
 cx = 3f80h, dx = 3950h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:0020h, GenTextClass
 cx = 3f80h, dx = 3950h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:0020h, GenTextClass
 cx = 3f80h, dx = 3950h, bp = 3950h

objwatch

objwatch [<addr>]

The objwatch (object watch) command is used for displaying the messages that have reached a particular object. It is useful for verifying that messages are being sent to the object at addr. If no argument is given, then any current objwatch is turned off. (See The objwatch Command .)

Swat Display 3-13 The objwatch Command

mess1:0) 2 => objwatch Mess1View
brk5
(mess1:0) 3 => c
MSG_META_MOUSE_PTR, ^l44a0h:0020h, GenViewClass
 cx = 00afh, dx = 0013h, bp = 0000h
MSG_META_MOUSE_PTR, ^l44a0h:0020h, GenViewClass
 cx = 00afh, dx = 0013h, bp = 0000h
MSG_META_WIN_UPDATE_COMPLETE, ^l44a0h:0020h, GenViewClass
 cx = 4b90h, dx = 0000h, bp = 0000h
MSG_META_MOUSE_PTR, ^l44a0h:0020h, GenViewClass
 cx = 00b0h, dx = 0013h, bp = 0000h
MSG_META_RAW_UNIV_LEAVE, ^l44a0h:0020h, GenViewClass
 cx = 44a0h, dx = 0020h, bp = 4b90h
MSG_VIS_DRAW, ^l44a0h:0020h, GenViewClass
 cx = 4b80h, dx = 23c0h, bp = 23c0h
MSG_VIS_COMP_GET_MARGINS, ^l44a0h:0020h, GenViewClass
 cx = 4b80h, dx = 23c0h, bp = 0000h
MSG_VIS_DRAW, ^l44a0h:0020h, GenViewClass
 cx = 4b80h, dx = 0163h, bp = 23c0h
MSG_META_WIN_UPDATE_COMPLETE, ^l44a0h:0020h, GenViewClass
 cx = 4b90h, dx = 0000h, bp = 0000h

save

save <filename>

The save command, when passed a file name, saves the contents of Swat's main buffer to that file. Thus this command dumps Swat output to a file.

showcalls -L

showcalls -L
showcalls

The showcalls command is, for the most part, only useful under rare circumstances. However, showcalls with the -L option is useful for a fairly common situation: a library is failing to load but you can't find which one.

To find out which libraries an application is trying to load, use showcalls -L . Swat will tell you whenever a library is sought. Whichever library is being sought when the "missing system file" error message appears is the library that's causing your problems: perhaps you need to re-install it.

Use the showcalls command without any arguments to turn off library-loading reporting.

switch

switch [(<patient>:<thread-num>|<threadID>)]

The switch command is used to switch between applications or threads in Swat but does not physically change threads on the target PC. This allows the transfer of debugging control between threads of the same patient. If no argument is given, then switch will change to the thread executing when GEOS was halted. Another way to switch threads is to type the name of the patient on the command line. If a patient has more than one thread, type the name of the patient, a space, and then the thread number. To change thread numbers within a geode, type a colon followed by the thread number to change to (e.g. " :1")

sym-default

sym-default [<patient>]

The sym-default (symbol default) command is used to set the default patient to use when parsing an address expression which is not defined in the current patient. For example, if a breakpoint is hit in the kernel and an object in the application code needs to be examined, Swat will know to use the application as a patient and not the kernel. This command is useful when debugging a single patient, the most common way to debug. If no patient argument is given, then the name of the default patient will be displayed.

This command is normally aliased to sd .

why

why

The why command prints the error code for an occurrence of a fatal error. This command is useful because it can give a good idea of why GEOS crashed.

| Up: GEOS SDK TechDocs | Up | Prev: 5.4 Examining and Modifying Memory | Next: 6 Additional Features


Additional Features

Swat Introduction: 6 Additional Features

| Up: GEOS SDK TechDocs | Up | Prev: 5 Essential Commands

This section covers the features of Swat that make it easier to use when debugging an application.

  • Mouse support
    You can use the mouse to capture and paste text in the main Swat buffer. Capture text by click-dragging with the left mouse button. Pressing the right mouse button pastes the captured text to the Swat prompt line.
  • Navigating the Main Buffer
    To scroll the main buffer, use Ctrl-u (up), Ctrl-d (down), Ctrl-y (back one line), Ctrl-e (forward one line), Ctrl-b (backward page) and Ctrl-f (forward page).
  • Command History
    By pressing Ctrl-p several times, you can call previous commands up to the Swat prompt. If you go past the command that you want, use Ctrl-n to go forward in the history.
    The `!' character followed by a number repeats that command in the command history. (The standard Swat prompt includes a command number which may be used for this.) e.g. !184 will execute the 184th command of this session.
    The `!' character followed by a string will repeat the most recent command whose beginning is the same as the passed string. That is !b might invoke brk list if that was the most recent command that began with "b".
    Typing "!!" will repeat the previous command; "!$" is the last argument of the previous command.
  • Command Correction
    To repeat the previous command, but changing a piece of it, use the ^ command. This comes in handy when you've made a typo trying to enter the previous command.

Swat Display 3-14 Command Correction Using ^

(geos:0) 185 => wurds
Error: invoked "wurds", which isn't a valid command name
(geos:0) 186 => ^u^o
words
Addr:	 	 +0   +2   +4   +6   +8   +a   +c   +e
4b4bh: e800 01b1 0e00 60f6 0016 9800 6e02 a900
(geos:0) 187 => ddwords
Error: invoked "ddwords", which isn't a valid command name
(geos:0) 188 => ^d
dwords
Addr:  +0       +4       +8       +c
4b59h: 1d0aa900 001c400d 294bd000 6c0a8000
  • Address History
    Swat has an address history which is composed of tokens for address expressions previously used by commands such as print or pobj . The elements in the history can be accessed by typing @<number> where the number argument is the number of the item in the history. These elements can replace a full address expression (except constants) and are often used when traversing through fields of a previously printed structure. The default history keeps track of the last 50 items. (See The Address History .)

Swat Display 3-15 The Address History

(geos:0) 8 => gentree -i
GenPrimaryClass (@1, ^l44a0h:001eh) "MESS #1"
 GenViewClass (@2, ^l44a0h:0020h)
 GenValueClass (@3, ^l44a0h:0026h)
 GenValueClass (@4, ^l44a0h:0028h)

(geos:0) 9 => pinst @3
class = ui::dgroup::GenValueClass
master part: Gen_offset(53) -- ui::GenValueInstance
@5: {ui::GenValueInstance (^h17568:1170)+53} = {
 GenInstance GenValue_metaInstance = {
 MetaBase Gen_metaInstance = {
 ClassStruct _far *MB_class = 3573h:1867h (motif::dgroup::OLScrollbarClass)
 }
 LinkPart GI_link = {
 void _optr LP_next = ^l44a0h:0028h (ui::dgroup::GenValueClass@6244h:02deh)
 }
 CompPart GI_comp = {
 void _optr CP_firstChild = null
 }
 void _lptr GI_visMoniker = null
 KeyboardShortcut GI_kbdAccelerator = {
 KS_PHYSICAL = 0
 KS_ALT = 0
 KS_CTRL = 0
 KS_SHIFT = 0
 KS_CHAR_SET = 0
 KS_CHAR = C_NULL
 }
 GenAttrs GI_attrs = {}
 GenStates GI_states = {GS_USABLE, GS_ENABLED}
 }
 WWFixed GVLI_value = {0.000000}
 WWFixed GVLI_minimum = {0.000000}
 WWFixed GVLI_maximum = {0.007324}
 WWFixed GVLI_increment = {0.000229}
 GenValueStateFlags GVLI_stateFlags = {GVSF_INDETERMINATE}
 GenValueDisplayFormat GVLI_displayFormat = GVDF_INTEGER
 void _optr GVLI_destination = ^l44a0h:0020h (ui::dgroup::GenViewClass@6244h:01aah)
 word GVLI_applyMsg = 681ah
}
(geos:0) 10 =>
  • Abbreviations
    Another shortcut available in Swat is the abbreviation feature. Many commands can be specified by their first few characters up to and including the letter that makes them distinct from all other commands. For example, the pobject command can be specified pobj , pob , or even po , but not by just p because there are other commands (such as print ) beginning with the letter p . To get a list of all commands with a given prefix, type the prefix at the Swat prompt, then type Ctrl-D. To automatically complete a command name use the Escape key (if the prefix is unambiguous) or Ctrl-] to scroll through the list of possible command completions.
  • Initialization Files
    If there are certain Swat commands that always need to be executed when Swat is run, then they can be placed in an initialization file. (See An Initialization File srcwin 15 regwin save 500 patient-default mess1 run ) An initialization file contains a list of commands that will be executed just before the first prompt in Swat.
    The initialization file should be called SWAT.RC. Swat will look in the directory from which it was invoked for such a file. If it doesn't find one there, it will look for a file named SWAT.RC in a directory named in the HOME environment variable

Swat Display 3-16 An Initialization File

srcwin 15
regwin
save 500
patient-default mess1
run

This example shows a sample initialization file which sets up windows to display the source code and current register values, set the length of the save buffer to 500 lines, and continue running swat until the mess1 application has been loaded, at which point execution will automatically stop.

| Up: GEOS SDK TechDocs | Up | Prev: 5 Essential Commands


Swat Command Reference

 | Up: GEOS SDK TechDocs | Up | Down | Next: _print ...
| One-page Version of this Chapter for Easy Printing

Swat Reference

The descriptions of the Swat commands will follow the following notational conventions:

  • command (alternative1|alternative2|...|alternativeN)
    ()     The parentheses enclose a set of alternatives separated by a vertical line. For example, the expression quit (cont|leave) means that either quit cont or quit leave can be used.
  • command [optional_argument]
    []     The brackets enclose optional arguments to the command. For example, the command alias [<word[<command>]>] could have zero, one, or two arguments because the <command> and <word> arguments are optional. Another example would be the command objwalk [<addr>] , which may take zero arguments if it is meant to use the default address, and one argument if the user gives it a particular address to look at.
  • command <type_of_argument>
    < >     The angled brackets enclose the type of an argument rather than the actual string to be typed. For example, <addr> indicates an address expression and <argument> indicates some sort of argument, but (addr|type) means either the string addr or the string type .
  • * +
     An asterisk following any of the previous constructs indicates zero or more repetitions of the construct may be typed. An addition sign indicates one or more repetitions of the construct may be used. For example, unalias word * can be the unalias command by itself, or it can be followed by a list of words to be unaliased.

_print ...
addr-parse ...
alias ...
assign ...
bits ...
car ...
continue-patient ...
dbrk ...
debug ...
delassoc ...
drivewalk ...
elist ...
fatalerr_auto_explain ...
flagwin ...
fpstack ...
func ...
getenv ...
handsum ...
help ...
hwalk ...
imem ...
intr ...
keyboard ...
load ...
memsize ...
noStructEnum ...
objmessagebrk ...
patchin ...
pdisk ...
pgs ...
pinst ...
pobjmon ...
print-cell ...
printRegions ...
ptext ...
pvismon ...
read-reg ...
ret ...
scope ...
showcalls ...
smatch ...
stdpaths ...
stream ...
symbolCompletion ...
text-fixup ...
twalk ...
varwin ...
wclear ...

| Up: GEOS SDK TechDocs | Up | Down | Next: _print ...
| One-page Version of this Chapter for Easy Printing


Swat Command Reference: _print ...

 | Up: GEOS SDK TechDocs | Up | Prev: Swat Command Reference | Next: addr-parse ...

_print

 _print <expression>

Examples:

"_print ax-10"
print ax less 10 decimal.

Print the value of an expression.

The difference between this command and the "print" command is a subtle one: if one of the arguments contains square-brackets, the Tcl interpreter will attempt to evaluate the text between the brackets as a command before _print is given the argument. If the text between the brackets is intended to be an array index, the interpreter will generate an error before the Swat expression evaluator has a chance to decide whether the text is a nested Tcl command or an array index.

For this reason, this function is intended primarily for use by Tcl procedures, not by users.

See Also: print, addr-parse.

abort

abort [<frame-number>]
abort [<function>]

Examples:

"abort"
abort executing the current frame.
"abort 3"
abort executing up through the third frame.
"abort ObjMessage"
abort executing up through first ObjMessage.

Abort code execution up through a given frame or routine. By "abort", we me "do not execute". This can be quite dangerous, as semaphores may not be ungrabbed, blocks not unlocked, flags not cleared, etc., leaving the state of objects, and if executing system code, possibly the system itself in a bad state. This command should only be used when the only alternative is to to detach (i.e. in a fatal error) as a way to possibly prolong the usefulness of the debugging session.

  • If no argument is given, code through the current frame is aborted.
  • <frame num> are the numbers that appear at the left of the backtrace.

See Also: finish, backtrace.

abortframe

abortframe <frame-token>

Examples:

"abortframe $cur"
Abort all code execution through the frame whose token is in $cur.

Aborts code execution up through a particular stack frame. As no code is executed, the registers may be in a garbaged state.

  • The argument is a frame token, as returned by the "frame" command.
  • No FULLSTOP event is dispatched when the machine actually aborts executing in the given frame. The caller must dispatch it itself, using the "event" command. For information about FULLSTOP events, see the event Tcl command.
  • The command returns zero if the machine aborted executing in the given frame; non-zero if it was interrupted before that could happen.
| Up: GEOS SDK TechDocs | Up | Prev: Swat Command Reference | Next: addr-parse ...


Swat Command Reference: addr-parse ...

 | Up: GEOS SDK TechDocs | Up | Prev: _print ... | Next: alias ...

addr-parse

addr-parse <addr> [<addr-only>]

Examples:

"addr-parse *ds:si"
Parse the address "*ds:si" into its handle, offset and data-type components. In this case, the data-type will be "nil".
"addr-parse ILLEGAL_HANDLE 0"
Figures the value for the enumerated constant "ILLEGAL_HANDLE". The handle for this non-address will be "value".

This command parses the address expression into its components, returning a list {<handle> <offset> <type> } as its value.

  • This will generate an error if there's an error parsing the <addr>
  • <handle> is the token for the handle in which the address resides, or "nil" if the address is absolute. This token can be given to the "handle" command for further processing.
  • <offset> is a decimal number and is the offset of the address within the block indicated by the <handle> token. If <handle> is "nil", this can be a 32-bit linear address.
  • <type> is a type token for the data at the given address, if any could be determined. For example the address "ds:bx" has no type, as it's just a memory reference, but "ds:bx.VDE_extraData" will have whatever type the structure field "VDE_extraData" possesses. This token can be passed to the "type" or "value" commands for further processing.
  • If the expression doesn't refer to data that can be fetched from the patient (e.g. "foo*3") <handle> will be returned as the string "value" instead of a normal handle token. <offset> is then a value-list for the resulting value, and <type> is the type description by means of which the value list can be interpreted.
  • The optional <addr-only> argument is zero or non-zero to indicate the willingness or unwillingness, respectively, of the caller to receive a value list in return. If <addr-only> is absent or non-zero, any expression that can only be expressed as a value will generate an error. The single exception to this is if the expression involves pointer arithmetic. For example "pself+1" normally would be returned as a value list for a far pointer, as the result cannot be fetched from the PC. When <addr-only> is absent or non-zero, "addr-parse" pretends the expression was "*(pself+1)", allowing simple specification of an address by the user for those commands that just address memory.
  • The <offset> element of the returned list is very useful when you want to allow the user to give you anything, be it a register or a number or an enumerated constant or whatever. You can pass the argument you were given to [index [addr-parse $arg] 1] and end up with an appropriate decimal number. Be sure to pass <addr-only> as 0, however, or else you'll generate an error.

See Also: value, handle, type.

addr-preprocess

 addr-preprocess <addr> <seg-var> <off-var>

Examples:

"addr-preprocess $args s o"
Parse the address expression in $args, storing the segment portion in $s and the offset portion in $o in the current scope.

Preprocesses an address expression into a form that is easier to manipulate and faster to reparse.

  • <seg-var> is the name of a variable in the caller's scope in which the segment of the address is stored. It should be treated as opaque, as it may or may not be numeric.
  • <off-var> is the name of a variable in the caller's scope in which the offset of the address is stored. This will always be numeric.
  • Returns the 3-list returned by addr-parse, in case you have a use for the type token stored in the list.

See Also: addr-parse.

addr-with-obj-flag

addr-with-obj-flag

Examples:

"var addr [addr-with-obj-flag $addr]"
If $addr is "-i", returns the address of the current implied grab.

This is a utility routine that can be used by any command that deals with objects where the user may reasonably want to operate on the leaf object of one of the hierarchies, or the windowed object under the mouse. It can be given one of a set of flags that indicate where to find the address of the object on which to operate.

  • Special values accepted for <address>:
    • -a
    the current patient's application object
    -i
    the current "implied grab": the windowed object over which the mouse is currently located
    -f
    the leaf of the keyboard-focus hierarchy
    -t
    the leaf of the target hierarchy
    -m
    the leaf of the model hierarchy
    -c
    the content for the view over which the mouse is currently located
    -kg
    the leaf of the keyboard-grab hierarchy
    -mg
    the leaf of the mouse-grab hierarchy
  • If <address> is empty, this will return the contents of the local variable "oself" within the current frame, if it has one, or *ds:si
  • If <address> isn't one of the above, this just returns <address>.

See Also: impliedgrab, content, focusobj, targetobj, modelobj, keyboardobj, mouseobj.

| Up: GEOS SDK TechDocs | Up | Prev: _print ... | Next: alias ...


Swat Command Reference: alias ...

 | Up: GEOS SDK TechDocs | Up | Prev: addr-parse ... | Next: assign ...

alias

alias [<name> [<body>]]

Examples:

"alias p print"
Execute "print" when the user types the command "p". Any arguments to "p" get passed to "print" in the order they were given.
"alias while {for {} $1 {} $2}"
Executes an appropriate "for" loop when the "while" command is executed with its two arguments: a test expression and a body of commands to execute.
"alias"
Prints all the defined aliases.
"alias while"
Prints what the "while" command is aliased to.

This is a short-cut to allow you to make commands you commonly type easier to use, and to define simple new commands quickly.

  • If you give no arguments the current aliases are all displayed.
  • If you give a single argument, the name of an existing alias, the command that will be executed when you use the alias is printed.
  • The <body> string is usually in curly braces, as it usually involves whitespace and can contain newlines for the longer aliases.
  • You can use the pseudo-variables $1, $2, etc. in the <body> to represent the 1st, 2nd, etc. argument given when the alias is invoked. They are pseudo-variables as the "var" command will not operate on them, nor are they available to any procedure invoked by the alias.
  • You can also interpolate a range of the arguments using $<start>-<end>. If you do not give an <end>, then the arguments from <start> to the last one will be interpolated.
  • $* will interpolate all the arguments.
  • $# will interpolate the actual number of arguments.
  • If you do not use any of these pseudo-variables, all the arguments given to the alias will be appended to the <body>.
  • Interpolation of the values for these pseudo-variables occurs regardless of braces in the <body>.
  • It is an error to specify an argument number when there are fewer than that many arguments given to the alias.

See Also: unalias.

alignFields

var alignFields [(0|1)]

Examples:

"var alignFields 1"
Sets the "print" command to align the values for all the fields of a given structure.

Determines whether structure-field values follow immediately after the field name or if all values are indented to the same level. The "print" command and other display commands use this variable when formatting their output.

  • Having all values indented to the same level makes it easier for some people to locate a particular field in a structure. It is not without cost, however, in that Swat must determine the length of the longest field name before it can print anything.
  • The default value for this variable is zero.

See Also: print.

antifreeze

antifreeze <patient>
antifreeze :<n>
antifreeze <patient>:<n>
antifreeze <id>

Examples:

"antifreeze term"
Promotes the application thread for "term" to be the "most-runnable"
"antifreeze :1"
Does likewise for thread #1 of the current patient
"antifreeze 16c0h"
Does likewise the thread whose handle is 16c0h
"antifreeze"
Promotes the current thread to be the "most-runnable."

See Also: freeze.

antithaw

antithaw <patient>
antithaw :<n>
antithaw <patient>:<n>
antithaw <id>

Examples:

"antithaw term"
Allows the application thread for "term" to run normally.
"antithaw :1"
Allows thread #1 of the current patient to run normally.
"antithaw 16c0h"
Allow the thread whose handle is 16c0h to run normally.

See Also: thaw.

appobj

   appobj [<patient>]

Examples:

"pobj [appobj draw]"
prints the GenApplication object for draw.
"pobj [appobj]"
prints the GenApplication object for the current application (equivalent to "pobj -a").

Returns the address of the GenApplication object for the given patient, or the current one if you give no patient.

See Also: impliedgrab.

apropos

apropos [<string>]

Examples:

"apropos vis"
Find all commands related to vis
"apropos text"
Find all commands related to text

Search the list of commands and command help for all entries containing <string>. Lists each command and its synopsis.

<string> may actually be a pattern, as described in the help for the "string" command (under "string match"). It automatically has a leading and following * tacked onto it to cause it to match anywhere within a help string.

See Also: help.

aset

aset <array-name> <index> <value>

Examples:

"aset foo $i $n"
Sets the $i'th element (counting from zero) of the value stored in the variable foo to $n.

Allows you to treat a list stored in a variable as an array, setting arbitrary elements of that array to arbitrary values.

  • <array-name> is the name of the variable, not the value of the variable to be altered.
  • This command returns nothing.
  • The index must be within the bounds of the current value for the variable. If it is out of bounds, aset will generate an error.

See Also: index.

assoc

assoc <list> <key>

Examples:

"assoc $classes GenPrimaryClass"
Examines the sublists of $classes and returns the first one whose first element is the string GenPrimaryClass.

Searches an associative list to find an element with a particular key. The list is itself made up of lists, each of whose first element is a key.

  • A typical associative list is made of key/value pairs, like this:
    {{<key> <value>} {<key> <value>} ...}
  • If an element is found whose <key> matches the passed <key>, the entire element is returned as the result. If no <key> matches, nil is returned.

See Also: car, cdr, range, list, delassoc.

| Up: GEOS SDK TechDocs | Up | Prev: addr-parse ... | Next: assign ...


Swat Command Reference: assign ...

 | Up: GEOS SDK TechDocs | Up | Prev: alias ... | Next: bits ...

assign

assign <addr> <value>

Examples:

"assign ip ip+2"
Add 2 to the value of IP in the current frame.
"assign {word ds:si} 63h"
Store 63h in the word at ds:si

Performs an assignment to a patient variable or register (but not to an element of the value history). The first argument is the variable or register to be assigned and the second argument is the value to assign to it (which may be a regular address expression). If the first expression doesn't indicate a type, "word" is assumed. Only byte , word or dword types are supported.

  • When assigning to an sptr, the value assigned will be the segment of the block indicated by the <value>, unless <value> is an absolute address (or just a number), in which case the low 16 bits of the offset will be used instead.
  • Similar behavior occurs when assigning to an fptr, except if the <value> is an absolute address, in which case the linear address in the offset portion of the <value> will be decomposed into a segment and an offset.

See Also: imem, value.

att

att [<args>]

Examples:

"att"
attach Swat to GEOS.

Attach Swat to GEOS.

The args argument can be one of the following:

-s
reboot GEOS with error checking, attach, and stop
-sn
reboot GEOS without error checking, attach, and stop
-f
restart GEOS with error checking and attach after a pause
-r
restart GEOS with error checking and attach
-rn
restart GEOS without error checking and attach

See Also: detach, quit.

attach

attach [<boot>]

Examples:

"attach"
attach to the target PC

Attach swat to the target PC.

  • The boot argument is "-b" to bootstrap and "+b" to not. Normally, Swat will try to read symbolic information about all running geodes; bootstrapping specifies that Swat should only read symbolic information for these geodes when it must.
  • If you give no <boot> argument, swat will use the most-recent one.
  • By default, swat will locate the symbols for all geodes and threads active on the PC when it attaches.
  • If any geode has changed since you detached from the PC, its symbols are re-read.

See Also: att, detach, quit.

autoload

autoload <function> <flags> <file> [<class> <docstring>]

Examples:

"autoload cycles 1 timing"
load the file "timing.tcl" when the cycles command is first executed. The user must type the command completely.
"autoload print 2 print"
load the file "print.tcl" when the print command is first executed. The user may abbreviate the command and the Tcl interpreter will not evaluate its arguments.

This command allows the first invocation of a command to automatically force the transparent reading of a file of Tcl commands.

  • autoload takes 3 or 5 arguments: the command, an integer with bit flags telling how the interpreter should invoke the command, the file that should be read to define the command (this may be absolute or on load-path) and an optional help class and string for the command.
  • The help class and string need only be given if the file to be loaded isn't part of the system library (doesn't have its help strings extracted when Swat is built).
  • The <flags> argument has the following bit-flags:
0
User must type the command's name exactly. The command will be defined by "defsubr" or "defdsubr" when <file> is loaded.
1
The interpreter will not evaluate arguments passed to the command. All arguments will be merged into a single string and passed to the command as one argument. The command will use the special "noeval" argument when it is defined.

See Also: defsubr, defcommand, proc.

backtrace

backtrace [-r<reg>* ][<frames to list>]

Examples:

"backtrace"
print all the frames in the patient
"backtrace -rax"
print all the frames and the contents of AX in each one.
"where 5"
print the last five frames
"w 5"
print the last five frames

Print all the active stack frames for the patient.

  • The <frames to list> argument is the number of frames to print. If not specified, then all are printed.
  • If a numeric argument is not passed to backtrace then it attempts to display method calls in the form:
    •         MSG_NAME(cx, dx, bp) => className (^l####h:####h)

    Here <cx>, <dx>, and <bp> are the values passed in these registers. <className> is the name of the class which handled the message. ^l####h:####h is the address of the object (block, chunk handle) handling the message.

  • If a numeric argument is passed to backtrace then the attempt to decode the message is not done and the single line above expands into:
    • 	 far ProcCallModuleRoutine(), geodesResource.asm:476
     near ObjCallMethodTable(), objectClass.asm:1224

    This is generally less useful, but sometimes it's what you need.

See Also: up, down, func.

bin

bin <number> [<field-width>]

Examples:

"bin 5"
returns "0101" (five in binary)
"bin 10h"
returns "0001 0000"
"bin 10h 3"
returns "0000 0001 0000"

Prints numbers in binary.

  • The field width is in nibbles.
  • The default field width is the minium number of nibbles to display the number.

bindings

bindings

Shows all current key bindings

bind-key

bind-key <ascii_value> <function>

Examples:

"bind-key \321 scroll_srcwin_down"
Binds scroll-down key to the scroll_srcwin_down Tcl routine.

Binds an ASCII value to a function.

See Also: alias, unbind-key.

| Up: GEOS SDK TechDocs | Up | Prev: alias ... | Next: bits ...


Swat Command Reference: bits ...

 | Up: GEOS SDK TechDocs | Up | Prev: assign ... | Next: car ...

bits

bits [<address>] [<length>] [<type>]

Examples:

"bits"
lists 16 bytes worth of bits at ds:si
"bits ds:di 8"
lists 8 bytes worth of bits at ds:di
"bits ds:di 24 w"
lists 24 words worth of bits at ds:di

Examine memory as a dump of bits in sets of 8, 16, or 32 (byte,word, or dword).

  • The address argument is the address to examine. If not specified, the address after the last examined memory location is used. If no address has been examined then ds:si is used for the address.
  • The length argument is the number of units to examine. It defaults to 16.
  • They type argument is bytes, words or dwords. It defaults to bytes.
  • Pressing return after this command continues the list.

break-taken

 break-taken [<flag>]

Examples:

"break-taken"
Returns 1 if the machine stopped because of a breakpoint.
"break-taken 0"
Specify that no breakpoint was actually taken to stop the machine.

Obscure. This is used to determine if the machine stopped because a breakpoint was hit and taken.

Setting the break-taken flag is a rather obscure operation. It is useful primarily in complex commands that single-step the machine until a particular address is reached, or a breakpoint is taken when a breakpoint must be used to skip over a procedure call, or condense multiple iterations of an instruction with a REP prefix into 1. For an example of this use, refer to the "cycles" command.

See Also: brk, irq.

brkload

brkload [<handle>]

Examples:

"brkload Interface"
Stop the machine when the Interface resource is loaded or swapped in.
"brkload bx"
Stop the machine when the resource whose handle ID is in BX is loaded or swapped in.
"brkload"
Stop watching for the previously-specified resource to be loaded.

Stop the machine when a particular resource is loaded into memory.

  • Only one brkload may be active at a time; registering a second one automatically unregisters the first.
  • If you give no <handle> argument, the previously-set brkload will be unregistered.

See Also: handle.

byteAsChar

var byteAsChar [(0|1)]

Examples:

"var byteAsChar 1"
Print byte variables as characters.

Determines how unsigned character variables are printed: if set non-zero, they are displayed as characters, else they are treated as unsigned integers.

  • If $byteAsChar is 0, $intFormat is used.
  • The default value for this variable is 0.

bytes

bytes [<address>] [<length>]

Examples:

"bytes"
lists 16 bytes at DS:SI
"bytes ds:di 32"
lists 32 bytes at DS:SI

Examine memory as a dump of bytes and characters.

  • The <address> argument is the address to examine. If not specified, the address after the last examined memory location is used. If no address has been examined then DS:SI is used for the address.
  • The <length> argument is the number of bytes to examine. It defaults to 16.
  • Pressing <Return> after this command continues the list.
  • Characters which are not typical ASCII values are displayed as a period.

See Also: words, dwords, imem, assign.

call

 call <function> [<function args>]

Examples: "call MyFunc"
"call MyDraw ax 1 bx 1 cx 10h dx 10h"
"call FindArea box.bottom 5 box.right 5 push box"'

Call a function in the current thread.

  • The <function> argument is the function to call. If it is a NEAR function, the thread must already be executing in the function's segment.
  • The function arguments are in pairs <variable/register> <value>. These pairs are passed to the "assign" command. As a special case, if the variable is "push", the value (a word) is pushed onto the stack and is popped when the call finishes (if it completes successfully).
  • All current registers are preserved and restored when the call is complete. Variables are not.
  • Once the call has completed, you are left in a sub-interpreter to examine the state of the machine. Type "break" to get back to the top level.
  • If the machine stops for any other reason than the call's completion, the saved register state is discarded and you are left wherever the machine stopped. You will not be able to get a stack trace above the called function, but if the call eventually completes, and no registers have actually been modified, things will get back on track.
  • You may not call a function from a thread that has retreated into the kernel. This function also will not allow you to call ThreadExit() . Use the "exit" function to do that.

See Also: assign, call-patient, patch.

call-patient

call-patient <function> ((<reg>|push) <value>)*

Examples:

"call-patient MemLock bx $h"
Locks down the block whose handle ID is in $h.

This is a utility routine, not intended for use from the command line, that will call a routine in the PC after setting registers to or pushing certain values.

  • Returns non-zero if the call completed successfully.
  • If the call is successful, the registers reflect the state of the machine upon return from the called routine. The previous machine state is preserved and can be retrieved, by invoking restore-state, or thrown away, by invoking discard-state. The caller must invoke one of these to clean up.
  • Arguments after <function> are as for "call".
  • If the called routine is in movable memory, this will lock the containing block down before issuing the call, as you'd expect.
  • Calling anything that makes message calls while on the geos:0 thread is a hazardous undertaking at best.

See Also: call.

| Up: GEOS SDK TechDocs | Up | Prev: assign ... | Next: car ...


Swat Command Reference: car ...

 | Up: GEOS SDK TechDocs | Up | Prev: bits ... | Next: continue-patient ...

car

car <list>

Examples:

"car $args"
Returns the first element of $args.

Returns the first element of a list.

This is a lisp-ism for those most comfortable with that language. It can be more-efficiently implemented by saying [index <list> 0]

See Also: cdr.

cdr

cdr <list>

Examples:

"cdr $args"
Returns the remaining arguments yet to be processed.

Returns all but the first element of a list.

This is a lisp-ism for those most comfortable with that language. It can be more-efficiently implemented by saying [range <list> 1 end]

See Also: car.

classes

classes [<patient>]

Examples:

"classes "
Print list of classes in current patient.
"classes myapp"
Print list of classes in myapp patient.

Prints list of classes defined by the given patient.

Remember that "brk" will take address arguments of the form <class>::<message>, so you can use this function and set a breakpoint using "brk MyTextClass:: MSG_MY_TEXT_MESSAGE ". If you need a breakpoint that's limited to one object, use objbrk instead.

clrcc

clrcc <flag> [<value>]

Examples:

"clrcc c"
clear the carry flag

Clear a flag in the target computer.

The first argument is the first letter of the flag to clear. The following is a list of the flags:
t trap
i interrupt enable
d direction
o overflow
s sign
z zero
a auxiliary carry
p parity
c carry

See Also: setcc, compcc, getcc.

columns

columns

Examples:

"columns"
Return the number of columns on the screen.

Retrieves the width of the screen, if known, to allow various commands (most notably "print") to size their output accordingly.

compcc

compcc <flag>

Examples:

"compcc c"
complement the carry flag

Complement a flag in the target computer.

The first argument is the first letter of the flag to complement. The following is a list of the flags:
t trap
i interrupt enable
d direction
o overflow
s sign
z zero
a auxiliary carry
p parity
c carry
This command is handy to insert in a patch to flip a flag bit.

See Also: setcc, clrcc.

completion

completion <list-of-names>

Examples:

"completion {martial marital}"
Returns "mar," the common prefix.

Figures the common prefix from a set of strings. Used for the various forms of completion supported by top-level-read.

condenseSmall

var condenseSmall [(0|1)]

Examples:

"var condenseSpecial 0"
Force even small structures to be printed one field per line.

Controls whether "print" attempts to condense the output by printing small (< 4 bytes) structures (which are usually records in assembly language) as a list of <name> = <int>, where <name> is the field name and <int> is a signed integer.

The default value of this variable is one.

See Also: print, condenseSpecial.

condenseSpecial

var condenseSpecial [(0|1)]

Examples:

"var condenseSpecial 0"
Turns off the special formatting of various types of structures by "print".

Controls the formatting of certain structures in more-intuitive ways than the bare structure fields.

  • The default value of this variable is 1.
  • The current list of structures treated specially are: Semaphore, Rectangle, Output Descriptor, TMatrix, BBFixed, WBFixed, WWFixed, DWFixed, WDFixed, DDFixed, FileDate, FileTime, FloatNum, SpecWinSizeSpec.

See Also: print, condenseSmall.

cont

cont

Examples:

"cont"
continue execution
"c"
continue execution

Continue GEOS.

  • If the global variable waitForPatient is non-zero, this command waits for the machine to stop again before it returns.

See Also: go, istep, step, next, detach, quit.

content

content

Examples:

"vistree [content]"
print the visual tree of the content of the view under the mouse.

Print the address of the content under the view with the current implied grab.

  • This command is normally used with vistree to get the visual tree of a content by placing the mouse on the content's view window and issuing the command in the example.
  • If the pointer is not over a GenView object, this is the same as the "impliedgrab" command.

See Also: systemobj, gentree, impliedgrab.

| Up: GEOS SDK TechDocs | Up | Prev: bits ... | Next: continue-patient ...


Swat Command Reference: continue-patient ...

 | Up: GEOS SDK TechDocs | Up | Prev: car ... | Next: dbrk ...

continue-patient

continue-patient

Examples:

"continue-patient"
Allow the target machine to continue executing GEOS.

Tell the Swat stub to let the target machine continue where it left off.

This command does not wait for the machine to stop again before it returns; once the machine is running, you're free to do whatever you want, whether it's calling "wait" or examining memory periodically.

See Also: step-patient.

cup

cup <class>
cup <object>
cup <flags>

Examples:

"cup ui::GenDocumentControlClass"
Print class hierarchy of named class
"cup ^l2850h:0034h"
Print class hierarchy of object
"cup -f"
Print class hierarchy of focus object
"cup -p"
Print class hierarchy of process

Walks up the class hierarchy, starting at a given class, printing each class encountered. May be passed an object, in which case the class of the object will be used as a starting place.

current-level

current-level

Examples:

"var l [current-level]"
Store the current interpreter nesting level in $l.

Returns the number of invocations of "top-level" (i.e. the main command input loop) currently active.

  • This is currently used only to modify the command prompt to indicate the current nesting level.
  • The top-most command loop is level one.

See Also: top-level.

current-registers

current-registers

Examples:

"current-registers"
Returns a list of the current registers for the current thread.

Returns all the registers for the current thread as a list of decimal numbers.

  • The mapping from element number to register name is contained in the global variable "regnums", which is an assoc-list whose elements contain the name of the register, then the element number.
  • For your own consumption, the list is ordered ax, cx, dx, bx, sp, bp, si, di, es, cs, ss, ds, ip, flags. You should use the "regnums" variable when programming, however, as this may change at some point (e.g. to accommodate the additional registers in the 386).

cvtrecord

cvtrecord <type> <number>

Examples:

"cvtrecord [symbol find type HeapFlags] 36"
Return a value list for the number 36 cast to a HeapFlags record.

Creates a value list for a record from a number, for use in printing out the number as a particular record using fmtval.

  • <type> is a type token for a record (or a structure made up exclusively of bitfields).
  • <number> must be an actual number suitable for the "expr" command. It cannot be a register or variable or some such. Use "getvalue" to obtain an integer from such an expression.
  • Returns a value list suitable for "value store" or for "fmtval".

See Also: value, fmtval, expr, getvalue.

cycles

Count instruction cycles from now until the given address is reached. Prints out each instruction as it is executed, along with the cycles it took. If no address is given, executes until a breakpoint is hit. Takes the following (optional) flags:

-r
Print routines called, the total cycles for each routine, and a running total, not the cycles for each instruction.
-i
Same as -r, but indents to show calling level. Not recommended for counting cycles over deeply nested routines.
-I
Same as -i, except uses (#) to indicate call level
-f
Stop counting when this routine finishes
-n
Does not whine about interrupts being off
-x < routine >
Step over calls to <routine>
-x < routine >=< val >
Step over calls to <routine> and assume that the call takes <val> cycles for timing purposes

See Also: timingProcessor.

| Up: GEOS SDK TechDocs | Up | Prev: car ... | Next: dbrk ...


Swat Command Reference: dbrk ...

 | Up: GEOS SDK TechDocs | Up | Prev: continue-patient ... | Next: debug ...

dbrk

dbrk <addr> <num> [<command>|default]
dbrk set <break> <num> [<command>|default]
dbrk list [<break>]
dbrk {clear,delete} <break>
dbrk cur <break> <num>
dbrk max <break> <num>
dbrk cmd <break> [<command>]
dbrk reset <break>
dbrk enable <break>
dbrk disable <break>

Examples:

"dbrk ObjMessage 30"
Break at the 30th call to ObjMessage
"dbrk set 5 30"
Break after hitting brk5 30 times.
"dbrk set brk3 10 default"
Break after hitting brk3 10 times; Each time brk3 is hit, perform the default command.
"dbrk cmd brk2 {echo foo}"
Echo a string whenever brk2 is hit.
"dbrk cmd brk2 default"
Use the default command for brk2.
"dbrk disable 2"
Stop counting brk2; it will now always be taken.

This command creates "delayed" breakpoints, creating breakpoints that don't activate until they've been hit a specified nuber of times.

  • break may be a number, or a full breakpoint token (brk<n>) num is a number; and command is a string that will be evaluated. If command may also be the string "default", in which case a default command will be used. The first argument to dbrk may be abbreviated; only the first 3 characters are significant.
  • Deleting a breakpoint with "brk del" removes the dbrk also.
  • With the exception of brk, all the commands that take <break> must be given a breakpoint that has been delayed. The "dbrk list" command will show these.
  • "dbrk list" will show how many times a breakpoint has been hit, the maximum number of times to skip the breakpoint, and the command to evaluate whenever the breakpoint is hit. If no breakpoint is passed, the status of all dbrks will be shown.
  • "dbrk clear" and "dbrk delete" are synonyms. These commands will cause the delayed breakpoint to become normal again, and clear out the global variables used to store the dbrk's state: brk N cur, brk N max, brk N cmd where N is a number.
  • "dbrk cur", "dbrk max", and "dbrk cmd" set the current count, the maximum count, and the command of the specified breakpoint respectively.
  • "dbrk reset break " is a quick way of saying "dbrk cur break 0"
  • "dbrk enable" and "dbrk disable" are used to enable and disable a dbrk. Disabled dbrks will always be taken, and their counter will not be incremented.
  • Since this command is just a front-end to brk cmd, you can use dbrk and brk cond to create more complex breakpoints; for instance, you can "mwatch MSG_VIS_DRAW ", then "dbrk set brk 30 print-method" to break after MSG_VIS_DRAW has been received 30 times.

dcache

dcache bsize <blockSize>
dcache length <numBlocks>
dcache stats
dcache params
dcache (on|off)

Examples:

"dcache bsize 16"
Set the number of bytes fetched at a time to 16.
"dcache length 1024"
Allow 1024 blocks of the current block size to be in the cache at a time.
"dcache off"
Disables the Swat data cache.

Controls the cache Swat uses to hold data read from the target machine while the machine is stopped.

  • Data written while the machine is stopped actually get written to the cache, not the PC, and the modified blocks are written when the machine is continued.
  • The default cache block size is 32 bytes, with a default cache length of 64 blocks.
  • It is a very rare thing to have to turn the data cache off. You might need to do this while examining the changing registers of a memory-mapped I/O device.
  • The <blockSize> must be a power of 2 and no more than 128.
  • Changing the block size causes all cached blocks to be flushed (any modified cache blocks are written to the PC).
  • Changing the cache length will only flush blocks if there are more blocks currently in the cache than are allowed by the new length.
  • The "dcache stats" command prints statistics giving some indication of the efficacy of the data cache. It does not return anything.
  • The "dcache params" command returns a list {<blockSize> <numBlocks>} giving the current parameters of the data cache. There are some operations where you might want to adjust the size of the cache either up or down, but need to reset the parameters when the operation completes. This is what you need to do this.

See Also: cache.

dcall

dcall [<args>]

Examples:

"dcall Dispatch"
Display when the routine Dispatch is called
"dcall none"
stop displaying all routines

Display calls to a routine.

  • The <args> argument normally is the name of the routine to monitor. Whenever a call is made to the routine its name is displayed.
  • If `none' or no argument is passed, then all the routines will stop displaying.
  • Dcall uses breakpoints to display routine names. By looking at the list of breakpoints you can see which routines display their names and you can stop them individually by disabling or deleting their breakpoints.

See Also: showcalls, mwatch.

| Up: GEOS SDK TechDocs | Up | Prev: continue-patient ... | Next: debug ...


Swat Command Reference: debug ...

 | Up: GEOS SDK TechDocs | Up | Prev: dbrk ... | Next: delassoc ...

debug

debug <proc-name>*

Examples:

"debug"
Enter the Tcl debugger immediately.
"debug fooproc"
Enter the Tcl debuffer when the interpreter is about to execute the command "fooproc".

This command is used when debugging Tcl commands. It sets a breakpoint at the start of any Tcl command. Also serves as a breakpoint in the middle of a Tcl procedure, if executed with no argument.

  • The breakpoint for <proc-name> can be removed using the "undebug" command.
  • <proc-name> need not be a Tcl procedure. Setting a breakpoint on a built-in command is not for the faint-of-heart, however, as there are some commands used by the Tcl debugger itself. Setting a breakpoint on such a command will cause instant death.

See Also: undebug.

debugger

var debugger [<command-name>]

Name of the command when things go wrong. The function is passed two arguments: a condition and the current result string from the interpreter. The condition is "enter" if entering a command whose debug flag is set, "exit" if returning from a frame whose debug flag is set, "error" if an error occurred and the "debugOnError" variable is non-zero, "quit" if quit (^ is typed and the "debugOnReset" variable is non-zero, or "other" for some other cause (e.g. "debug" being invoked from within a function).

debugOnError

var debugOnError [(0|1)]

Examples:

"var debugOnError 1"
Turn on debugging when there's a Tcl error.

Enter debug mode when Swat encounters a Tcl error.

  • The 0|1 simply is a false|true to stop and debug upon encountering an error in a Tcl command.
  • If an error is caught with the catch command, Swat will not enter debug mode.

See Also: debugger.

defcmd

defcmd <name> <args> <help-class> <help-string> <body>

Examples: Look at almost any .tcl file in the system library for an example; a complete example set would be too large to give here.

This creates a new Tcl procedure with on-line help whose name the user may abbreviate when invoking.

  • <help-class> is a Tcl list of places in which to store the <help-string>, with the levels in the help tree separated by periods. The leaf node for each path is added by this command and is <name>, so a command "foo" with the <help-class> "prog.tcl" would have its <help-string> stored as "prog.tcl.foo."
  • Because the name you choose for a procedure defined in this manner can have an impact on the unique abbreviation for another command, you should use this sparingly.

See Also: defcommand, proc, help.

defcommand

defcommand <name> <args> <help-class> <help-string> <body>

Examples: Look at almost any .tcl file in the system library for an example.

This creates a new Tcl procedure with on-line help whose name must be given exactly when the user wishes to invoke it.

<help-class> is a Tcl list of places in which to store the <help-string>, with the levels in the help tree separated by periods. The leaf node for each path is added by this command and is <name>, so a command "foo" with the <help-class> "prog.tcl" would have its <help-string> stored as "prog.tcl.foo."

See Also: defcmd, proc, help.

defhelp

defhelp <topic> <help-class> <help-string>

Examples:

"defhelp breakpoint top {Commands relating to the setting of breakpoints}"
Sets the help for "breakpoint" in the "top" category to the given string.

This is used to define the help string for an internal node of the help tree (a node that is used in the path for some other real topic, such as a command or a variable).

  • This cannot override a string that resides in the /pcgeos/tcl/doc file.
  • You only really need this if you have defined your own help-topic category.
  • <help-class> is a Tcl list of places in which to store the <help-string>, with the levels in the help tree separated by periods. The leaf node for each path is added by this command and is <name>, so a command "foo" with the <help-class> "prog.tcl" would have its <help-string> stored as "prog.tcl.foo."

See Also: help.

defvar

defvar <name> <value> [<help-class> <help-string>]

Examples:

"defvar printRegions 0"
Define "printRegions" as a global variable and give it the value zero, if it didn't have a value already.

This command is used in .tcl files to define a global variable and give it an initial value, should the variable not have been defined before.

  • If the variable is one the user may want to change, give it on-line help using the <help-class> and <help-string> arguments.
  • <help-class> is a Tcl list of places in which to store the <help-string>, with the levels in the help tree separated by periods. The leaf node for each path is added by this command and is <name>, so a command "foo" with the <help-class> "variable.output" would have its <help-string> stored as "variable.output.foo."

See Also: var, help.

| Up: GEOS SDK TechDocs | Up | Prev: dbrk ... | Next: delassoc ...


Swat Command Reference: delassoc ...

 | Up: GEOS SDK TechDocs | Up | Prev: debug ... | Next: drivewalk ...

delassoc

delassoc <list> <key> [<foundvar> [<elvar>]]

Examples:

"delassoc $val murphy"
Returns $val without the sublist whose first element is the string "murphy."

Deletes an entry from an associative list.

  • <foundvar>, if given, is the name of a variable in the caller's scope that is to be set non-zero if an element in <list> was found whose <key> matched the given one. If no such element was found (and therefore deleted), the variable is set zero.
  • <elvar>, if given, is the name of a variable in the caller's scope that receives the element that was deleted from the list. If no element was deleted, the variable remains untouched.

See Also: assoc.

detach

detach [<options>]

Examples:

"detach cont"
continue GEOS and quit swat

Detach swat from the PC.

  • The <option> argument may be one of the following: continue : continue GEOS and detach swat; leave : keep GEOS stopped and detach swat. Anything else causes swat to just detach.

See Also: attach, quit.

dirs

dirs

Prints the directory stack for the current thread.

See Also: pwd, stdpaths.

discard-state

discard-state

Examples:

"discard-state"
Throw away the values for all the thread's registers as saved by the most recent call to save-state .

Throw away the state saved by the most-recent save-state command.

This is usually only used in response to an error that makes it pointless to return to the point where the save-state was performed.

See Also: save-state, restore-state.

diskwalk

diskwalk <drive>

Examples:

"diskwalk F"
Prints the disks registered in drive F.
"diskwalk"
Prints all the disks registered with the system.

Prints out the information on registered disks.

The Flags column is a string of single-character flags with the following meanings:

w
The disk is writable.
V
The disk is always valid, i.e. it's not removable.
S
The disk is stale. This is set if the drive for the disk has been deleted.
u
The disk is unnamed, so the system has made up a name for it.

See Also: drivewalk.

display

display <lines> <command>
display list
display del <num>

Examples:

"display list"
list all the commands displayed
"display 1 {piv Vis VCNI_viewHeight}"
always display the view height
"display del 2"
delete the second display command

Manipulate the display at the bottom of Swat's screen.

  • If you give a numeric <lines> argument, the next argument, <command>, is a standard Tcl command to execute each time the machine halts. The output of the command is directed to a window <lines> lines high, usually located at the bottom of the screen.
  • You can list all the active displays by giving "list" instead of a number as the first argument.
  • If the first argument is "del", you can give the number of a display to delete as the <num> argument. <num> comes either from the value this command returned when the display was created, or from the list of active displays shown by typing "display list".

See Also: wtop, wcreate.

dosMem

dosMem

Examples:

"dosMem"

Traverse DOS' chain of memory blocks, providing information about each.

down

down [<frame offset>]

Examples:

"down"
move the frame one frame down the stack
"down 4"
move the frame four frames down the stack

Move the frame down the stack.

  • The frame offset argument is the number of frames to move down the stack. If no argument is given then the current frame is moved down one frame.
  • This command may be repeated by pressing <Return>.

See Also: backtrace, up.

| Up: GEOS SDK TechDocs | Up | Prev: debug ... | Next: drivewalk ...


Swat Command Reference: drivewalk ...

 | Up: GEOS SDK TechDocs | Up | Prev: delassoc ... | Next: elist ...

drivewalk

drivewalk

Examples:

"drivewalk"
Prints the table of drives known to the system.

Prints out all disk drives known to the system, along with their current status.

  • The Flags column is a string of single character flags with the following meanings:
    • L
    The drive is accessible to the local machine only, i.e. it's not visible over a network.
    R
    The drive is read-only.
    F
    Disks may be formatted in the drive.
    A
    The drive is actually an alias for a path on another drive.
    B
    The drive is busy, performing some extended operation, such as formatting or copying a disk.
    r
    The drive uses disks that may be removed by the user.
    n
    The drive is accessed over the network.
  • The Locks column can reflect one of three states:
    • none
    The drive isn't being accessed by any thread.
    Excl
    The drive is locked for exclusive access by a single thread.
    <num>
    The drive is locked for shared access for a particular disk, whose handle is the number. This is followed by the volume name of the disk, in square brackets.

See Also: diskwalk.

dumpstack

dumpstack [<address>] [<length>]

Examples:

"dumpstack"
dump the stack at SS:SP
"ds ds:si 10"
dump ten words starting at DS:SI

Dump the stack and perform some simple interpretation upon it.

  • The <address> argument is the address of the list of words to dump. This defaults to SS:SP.
  • The <length> argument is the number of words to dump. This defaults to 50.
  • This dumps the stack and tries to make symbolic sense of the values, in terms of handles, segments, and routines.
  • After doing a dumpstack, if you just hit return without entering a new command, by default you will see a continuation of the dumpstack.

See Also: backtrace.

dwordIsPtr

var dwordIsPtr [(0|1)]

Examples:

"var dwordIsPtr 1"
Tells "print" to print all double-word variables as if they were far pointers (segment:offset).

Controls whether dword (a.k.a. long) variables are printed as 32-bit unsigned integers or untyped far pointers.

  • For debugging C code, a value of 0 is more appropriate, while 1 is best for debugging assembly language.
  • The default value for this variable is 1.

See Also: intFormat, print.

dwords

dwords [<address>] [<length>]

Examples:

"dwords"
lists 4 double words at DS:SI
"dwords ds:di 8"
lists 8 double words at DS:DI

Examine memory as a dump of double words (32 bit hex numbers).

  • The <address> argument is the address to examine. If not specified, the address after the last examined memory location is used. If no address has be examined then DS:SI is used for the address.
  • The <length> argument is the number of dwords to examine. It defaults to 4.
  • Pressing <Return> after this command continues the list.

See Also: bytes, words, imem, assign.

ec

ec [<args>]

Examples:

"ec"
list the error checking turned on
"ec +vm"
add vmem file structure checking
"ec all"
turn on all error checking (slow)
"ec save none"
save the current error checking and then use none
"ec restore"
use the saved error checking flags

Get or set the error checking level active in the kernel.

  • The following arguments may occur in any combination:
    • < flag >
    turn on <flag>
    +< flag >
    turn on <flag>
    -< flag >
    turn off <flag>
    all
    turn on all error checking flags
    ALL
    turn on all error checking flags
    none
    turn off all error checking flags
    sum < handle >
    turn on checksum checking for the memory block with the given handle ("ec sum bx"). The current contents of the block will be summed and that sum regenerated and checked for changes at strategic points in the system (e.g. when a call between modules occurs).
    - sum
    turn off checksum checking
    save
    save the current error checking
    restore
    restore the saved error checking flags where <flag> may be one of the following:
    analVM
    perform over-aggressive checking of vmem files
    graphics
    graphics checking
    heapFree
    heap free block checking
    lmemFree
    lmem free area checking
    lmemInternal
    internal lmem error checking
    lmemObject
    lmem object checking
    normal
    normal error checking
    region
    region checking segment extensive
    segment
    adds register checking to Swat. If this flag is turned on, Swat will check whether ds and es are valid memory handle at various points in the debugging cycle by calling ECCheckSegment() .
    lmemMove
    force lmem blocks to move whenever possible
    unlockMove
    forces unlocked blocks to move whenever possible. This ensures that your code isn't referencing into an unstable block location. I.e., a block which may move, but usually doesn't , therefore causing non-reproducible (and quite frustrating) errors.
    vm
    vmem file structure checking
    vmemDiscard
    force vmem blocks to be discarded if possible
  • If there isn't an argument, `ec' reports the current error checking flags.
  • Each time GEOS is run the ec flags are cleared. The saved flags are preserved between sessions. The ec flags may be saved and then restored after restarting GEOS so that the flag settings are not lost when restarting GEOS.

See Also: why.

echo

echo [-n] <string>+

Examples:

"echo -n yes?"
Prints "yes?" without a newline.
"echo hi mom"
Prints "hi mom" followed by a newline.

Prints its arguments, separated by spaces.

If the first argument is "-n", no newline is printed after the arguments.

See Also: flush-output.

| Up: GEOS SDK TechDocs | Up | Prev: delassoc ... | Next: elist ...


Swat Command Reference: elist ...

 | Up: GEOS SDK TechDocs | Up | Prev: drivewalk ... | Next: fatalerr_auto_explain ...

elist

elist [<patient>]

Examples:

"elist"
list the events for the current thread and patient
"elist ui"
list the events for the last thread of the ui patient
"elist :1"
list the events for the first thread of the current patient
"elist geos:2"
list the events for the second thread of the GEOS patient

Display all events pending for a patient.

The <patient> argument is of the form `patient:thread'. Each part of the patient name is optional, and if nothing is specified then the current patient is listed.

See Also: showcalls.

ensure-swat-attached

ensure-swat-attached

Examples:

"ensure-swat-attached"
Stop if Swat isn't attached to GEOS.

If Swat is not attached to GEOS, display an error and stop a command.

Use this command at the start of any other command that accesses the target PC. Doing so protects the user from the numerous warnings that can result from an attempt to read memory when not attached.

eqfind

eqfind [-p]

Examples:

"eqfind" list all event queues in the system.
"eqfind -p" list and print all event queues in the system.

Display all event queues in the system.

See Also: elist, eqlist, erfind.

eqlist

eqlist <queue handle> <name>

Examples:

"eqlist 8320 geos:2"
show the event list for geos:2

Display all events in a queue.

  • The queue handle argument is the handle to a queue.
  • The name argument is the name of the queue.

See Also: elist.

erfind

erfind [-p]

Examples:

"erfind" list all recorded event handles in the system.
"erfind -p" list and print all recorded event handles in the system.

Display all record event handles in the system. These are events that have been recorded but not necessarily sent anywhere, so they will not appear in the queue of any thread.

See Also: elist, eqlist, eqfind, pevent.

exit

exit <patient>

Examples:

"exit faxmon"
Causes the faxmon application to exit.

Sends messages required to make an application quit.

This command does nothing when you're stopped at FatalError , as it will wait until the machine is idle before attempting to send MSG_META_QUIT ; continuing from FatalError will cause the system to exit.

See Also: run.

exit-thread

exit-thread [<exit-code>]

Examples:

"exit-thread"
Exit the current thread, returning zero to its parent.
"exit-thread 1"
Exit the current thread, returning one to its parent.

Exit the current thread.

  • The exit code argument is the status to return to the current thread's parent, which defaults to zero.
  • Do not invoke this function for an event-driven thread; send it a MSG_META_DETACH instead.

See Also: quit.

explain

explain

Examples: "explain"

Print a more detailed description of why the system crashed, if possible.

  • This must be run from within the frame of the FatalError() function. Sometimes GEOS is not quite there. In this case, step an instruction or two and then try the "why" command again.
  • This simply looks up the enumerated constant for the error code in the AX register in the FatalErrors enumerated type defined by the geode that called FatalError() . For example, if a function in the kernel called FatalError() , AX would be looked up in geos::FatalErrors, while if a function in your application called FatalError() , this function would look it up in the FatalErrors type defined by your application. Each assembly application defines this enumerated type by virtue of having included ec.def .
  • This command also relies on programmers having explained their FatalErrors when defining them.
  • For certain fatal errors, additional information is provided by invoking the command <patient>::<error code name>, if it exists.

explode

explode <string> [<sep-set>]

Examples:

"explode $args"
Breaks the string stored in the variable "args" into a list of its individual letters.
"explode $file /"
Breaks the string stored in the variable "file" into a list of its components, using "/" as the boundary between components when performing the split.

Breaks a string into a list of its component letters, allowing them to be handled quickly via a foreach loop, or the map or mapconcat commands.

This is especially useful for parsing command switches.

See Also: foreach, index, range.

| Up: GEOS SDK TechDocs | Up | Prev: drivewalk ... | Next: fatalerr_auto_explain ...


Swat Command Reference: fatalerr_auto_explain ...

 | Up: GEOS SDK TechDocs | Up | Prev: elist ... | Next: flagwin ...

fatalerr_auto_explain

var fatalerr_auto_explain [(0|1)]

Examples:

"var fatalerr_auto_explain 0"
Turn off automatic generation of the explanation for any fatal-error hit.

Determines if the "why" command will automatically provide you with an explanation of any fatal error you encounter. If non-zero, they will be provided whenever FatalError is hit.

  • Explanations are loaded from <patient>.fei files stored in the system Tcl library directory when an error in <patient> is encountered.
  • You can also obtain an explanation of an error via the "explain" command.

fetch-optr

fetch-optr <handle> <offset>

Examples:

"fetch-optr $h $o.GI_comp.CP_firstChild"
Fetch the optr from the GI_comp.CP_firstChild field of the object at ^h$h:$o.

Extracts an optr from memory, coping with the data in the block that holds the optr not having been relocated yet.

  • <offset> is an actual offset, not a chunk handle, while <handle> is a handle ID, not a handle token.
  • Returns a two-list {<handle> <chunk>}, where <handle> is the handle ID from the optr, and <chunk> is the chunk handle (low word) from the optr.
  • We decide whether to relocate the optr ourselves based on the LMF_RELOCATED bit in the LMBH_flags field of the block's header. There are times, e.g. during the call to MSG_META_RELOCATE for an object, when this bit doesn't accurately reflect the state of the class pointer and we will return an error when we should not.

fhandle

fhandle <handle id>

Examples: "fhandle 3290h"

Print out a file handle.

  • The <handle id> argument is the handle number. File handles are listed in the first column of the `fwalk' command.

See Also: fwalk.

field

field <list> <field name>

Examples:

"field [value fetch ds:si MyBox] topLeft"
return the offset of the topLeft field in MyBox

Return the value for the field's offset in the structure.

  • The <list> argument is a structure-value list from the "value" command.
  • The <field name> argument is the field in the structure.

See Also: value, pobject, piv.

fieldwin

fieldwin

Print the address of the target machine's current top-most field window.

find

find [-ir] <string> [<filename>]

Examples:

"find FileRead"
Find next occurrence of string "FileRead" in currently viewed file
"find FI_foo poof.goc"
find first occurrence of string "FI_foo" in file poof.goc.
"find -ir myobject"
case-insensitive reverse search for most recent occurrence of string "myobject" in currently viewed file

Finds a string in a file and brings the line with that string to the middle of Swat's source window.

  • If no file argument is specified, find will find the next instance of the string in the already viewed file starting from the current file position.
  • There must already be a source window displayed for find to work.
  • Possible options to find are:
    • -r
    reverse search
    -i
    case insensitive search

find-opcode

find-opcode <addr> <byte>+

Locates the mnemonic for an opcode and decodesit. Accepts the address from which the opcode bytes were fetched, and one or more opcode bytes as arguments. Returns a list of data from the opcode descriptor:

{name length branch-type args modrm bRead bWritten inst}

length is the length of the instruction.

branch - type is one of:

1
none (flow passes to next instruction)
j
absolute jump
b
pc-relative jump (branch)
r
near return
R
far return
i
interrupt return
I
interrupt instruction

Any argument descriptor that doesn't match is to be taken as a literal. E.g. AX as a descriptor means AX is that operand.

modrm is the modrm byte for the opcode.

bRead is the number of bytes that may be read by the instruction, if one of its operands is in memory.

bWritten is the number of bytes that may be written by the instruction, if one of its operands is in memory.

inst is the decoded form of the instruction. If not enough bytes were given to decode the instruction, inst is returned as empty.

finish

finish [<frame num>]

Examples:

"finish"
finish executing the current frame
"finish 3"
finish executing up to the third frame

Finish the execution of a frame.

  • The <frame num> argument is the number of the frame to finish. If none is specified then the current frame is finished up. The number to use is the number which appears in a backtrace.
  • The machine continues to run until the frame above is reached.

See Also: backtrace.

finishframe

finishframe [<frame-token>]

Examples:

"finishframe $cur"
Run the machine to continue until it has returned from a particular stack frame.

Allows the machine to continue until it has returned from a particular stack frame.

  • No FULLSTOP event is dispatched when the machine actually finishes executing in the given frame. The caller must dispatch it itself, using the "event" command.
  • The command returns zero if the machine finished executing in the given frame; non-zero if it was interrupted before that could happen.
  • The argument is a frame token, as returned by the "frame" command.

See Also: event, frame, finish.

| Up: GEOS SDK TechDocs | Up | Prev: elist ... | Next: flagwin ...


Swat Command Reference: flagwin ...

 | Up: GEOS SDK TechDocs | Up | Prev: fatalerr_auto_explain ... | Next: fpstack ...

flagwin

flagwin [<on>|off]

Turns on or off a window providing a continuous display of the machine flags (e.g. zero, carry).

See Also: pflags.

flowobj

flowobj

Examples:

"pobject [flowobj]"
print out the flow object.

Prints out address of the uiFlowObj, which is the object which grabs the mouse.

This command is normally used with pobject to print out the object.

flush-output

flush-output

Examples:

"flush-output"
Forces pending output to be displayed.

Flushes any pending output (e.g. waiting for a newline) to the screen.

See Also: echo.

fmtoptr

fmtoptr <handle-id> <chunk>

Examples:

"fmtoptr 3160h o"
Prints a description of the object whose address is ^l3160h:0 (likely a thread/process).

Takes a global and a local handle and prints a description of the object described by that optr.

  • If the global handle is a thread or a process, the thread's name (process thread for a process handle) and the chunk handle (as an additional word of data for the message) are printed.
  • If the global handle is a queue handle, the queue handle and the chunk handle are printed, with a note that the thing's a queue.
  • If Swat can determine the object's class, the optr, full classname, and current far pointer are printed. In addition, if the chunk has its low bit set, the word "parent" is placed before the output, to denote that the optr likely came from a link and is the parent of the object containing the optr.

See Also: print.

fmtval

fmtval <value-list> <type-token> <indent> [<tail> [<one-line>]]

Examples:

"fmtval [value fetch foo] [symbol find type FooStruct] 0"
Prints the value of the variable foo, which is assumed to be of type FooStruct.

This is the primary means of producing nicely-formatted output of data in Swat. It is used by both the "print" and "_print" commands and is helpful if you want to print the value of a variable without entering anything into the value history.

  • <value-list> is the return value from "value fetch". You can, of course, construct one of these if you feel so inclined.
  • <type-token> is the token for the type-description used when fetching the value.
  • <indent> is the base indentation for all output. When "fmtval" calls itself recursively, it increases this by 4 for each recursive call.
  • <tail> is an optional parameter that exists solely for use in formatting nested arrays. It is a string to print after the entire value has been formatted. You will almost always omit it or pass the empty string.
  • <one-line> is another optional parameter used almost exclusively for recursive calls. It indicates if the value being formatted is expected to fit on a single line, and so "fmtval" should not force a newline to be output at the end of the value. The value should be 0 or 1.

See Also: print, _print, fmtoptr, threadname.

focus

focus [<object>]

Examples:

"focus"
print focus hierarchy from the system object down
"focus -i"
print focus hierarchy from implied grab down
"focus ^l4e10h:20h"
print focus hierarchy from ^l4e10h:20h down
"focus [content]"
print focus hierarchy from content under mouse.

Prints the focus hierarchy below an object.

  • If no argument is specified, the system object is used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • Remember that the object you start from may have the focus within its part of the hierarchy, but still not have the focus because something in a different part of the tree has it.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available.

    • This variable defaults to false.

See Also: target, model, mouse, keyboard, pobject.

focusobj

focusobj

Examples:

"focusobj"
print model hierarchy from system object down
"pobj [focusobj]"
Do a pobject on the focus object (equivalent to "pobj -f").

Returns the object with the focus.

See Also: focus, target, model, targetobj, modelobj.

fonts

fonts [<args>]

Examples:

"fonts"
summarize general font usage
"fonts -u"
list fonts currently in use

Print various font info.

  • The <args> argument may be any of the following:
    • -a
    list of fonts available
    -d
    list of font drivers available
    -u
    [<ID>] list of fonts currently in use. Optional font ID to match.
    -s
    summary of above information

    If no argument is specified the default is to show the summary.

  • When using other commands you probably need to pass them the handle in FIUE_dataHandle . When you don't have the font's handle ready, the best way is to use "fonts -u" to find the font at the right point size and then grab the handle from there.

See Also: pfont, pfontinfo, pusage, pfontinfo.

| Up: GEOS SDK TechDocs | Up | Prev: fatalerr_auto_explain ... | Next: fpstack ...


Swat Command Reference: fpstack ...

 | Up: GEOS SDK TechDocs | Up | Prev: flagwin ... | Next: func ...

fpstack

fpstack

Examples:

"fpstack"
Prints out the hardware and software floating point stacks for the patient.

Prints out the hardware and software floating point stacks for the patient.

See Also: fpu-state.

fpu-state

fpustate [<mode>]

Examples:

"fpustate"
Print out the state of the coprocessor.
"fpustate w"
Dumps actual words of the numbers.

Prints out the current state of the coprocessor, if any.

See Also: fpstack.

frame

frame top
frame cur
frame get <ss> <sp> <cs> <ip>
frame next <frame>
frame prev <frame>
frame function [<frame>]
frame funcsym [<frame>]
frame scope [<frame>]
frame info [<frame>]
frame patient [<frame>]
frame register <regName> [<frame>]
frame set [<frame>]
frame setreg <regName> <value> [<frame>]
frame +<number>
frame -<number>
frame <number>

Examples:

"var f [frame top]"
Fetches the token for the frame at the top of the current thread's stack and stores it in the variable "f"
"var f [frame next $f]"
Fetches the token for the next frame up the stack (away from the top) from that whose token is in $f
"frame register ax $f"
Returns the value of the AX register in the given frame.
"frame 1"
Sets the current frame for the current thread to be the top-most one.

This command provides access to the stack-decoding functions of swat. Most of the subcommands deal with frame tokens, but a few also handle frame numbers, for the convenience of the user.

  • Subcommands may be abbreviated uniquely.
  • Stack decoding works by a heuristic method, rather than relying on the presence of a created stack frame pointed to by BP in each function. Because of this, it can occasionally get confused.
  • Frame tokens are valid only while the target machine is stopped and are invalidated when it is continued.
  • Each frame records the address on the stack where each register was most-recently pushed (i.e. by the frame closest to it on the way toward the top of the stack). Register pushes are looked for only at the start of a function in what can be considered the function prologue.
  • "frame register" and "frame setreg" allow you to get or set the value held in a register in the given frame. For "setreg", <value> is a standard address expression, only the offset of which is used to set the register.
  • "frame register" returns all registers but "pc" as a decimal number. "pc" is formatted as two hex numbers (each preceded by "0x") separated by a colon.
  • "frame info" prints out information on where the register values for "frame register" and "frame setreg" are coming from/going to for the given or currently-selected frame. Because of the speed that can be gained by only pushing registers when you absolutely have to, there are many functions in GEOS that do not push the registers they save at their start, so Swat does not notice that they are actually saved. It is good to make sure a register value is coming from a reliable source before deciding your program has a bug simply because the value returned by "frame register" is invalid.
  • For any subcommand where the <frame> token is optional, the currently- selected frame will be used if you give no token.
  • "frame cur" returns the token for the currently-selected stack frame.
  • "frame set" is what sets the current frame, when set by a Tcl procedure.
  • "frame +<number>" selects the frame <number> frames up the stack (away from the top) from the current frame. "frame -<number>" goes the other way.
  • "frame <number>" selects the frame with the given number, where the top-most frame is considered frame number 1 and numbers count up from there.
  • "frame funcsym" returns the symbol token for the function active in the given (or current) frame. If no known function is active, you get "nil".
  • "frame scope" returns the full name of the scope that is active in the given (or current) frame. This will be different from the function if, for example, one is in the middle of an "if" that contains variables that are local to it only.
  • "frame function" returns the name of the function active in the given (or current) frame. If no known function is active, you get the CS:IP for the frame, formatted as two hex numbers separated by a colon.
  • "frame patient" returns the token for the patient that owns the function in which the frame is executing.

See Also: addr-parse, switch.

framewin

framewin [del]

Examples:

"framewin"
Creates a single-line window to display info about the current stack frame.
"framewin del"
Deletes the window created by a previous "framewin".

Creates a window in which the current stack frame is always displayed.

  • Only one frame window can be active at a time.

See Also: display, regwin, srcwin.

freeze

freeze [<patient>]
freeze :<n>
freeze <patient>:<n>
freeze <id>

Examples:

"freeze"
Freezes the current thread.
"freeze term"
Freezes the application thread for "term"
"freeze :1"
Freezes thread #1 of the current patient
"freeze 16c0h"
Freezes the thread whose handle is 16c0h.

Freezing a thread prevents a thread from running unless it's the only thread that's runnable in the entire system.

  • A frozen thread is not dead in the water, as it will still run if nothing else is runnable.
  • Freezing a thread is most useful when debugging multi-threaded applications where a bug appears to be caused by a timing problem or race condition between the two threads. Freezing one of the threads ensures a consistent timing relationship between the two threads and allows the bug to be reproduced much more easily.
  • The freezing of a thread is accomplished by setting its base and current priorities to as high a number as possible (255) thereby making the thread the least-favored thread in the system. The previous priority can be restored using the "thaw" command.

See Also: thaw.

fullscreen

fullscreen

Examples:

"fullscreen"

Prints the full screen hierarchy from the system object down.

| Up: GEOS SDK TechDocs | Up | Prev: flagwin ... | Next: func ...


Swat Command Reference: func ...

 | Up: GEOS SDK TechDocs | Up | Prev: fpstack ... | Next: getenv ...

func

func [<func name>]

Examples:

"func"
return the current function.
"func ObjMessage"
set the frame to the first frame for ObjMessage.

Get the current function or set the frame to the given function.

  • The <func name> argument is the name of a function in the stack frame of the current patient. The frame is set to the first occurrence of the function from the top of the stack.
  • If no <func name> argument is given then `func' returns the current function.

See Also: backtrace, up, down, finish.

fvardata

fvardata <token> [<address>]

Examples:

"fvardata ATTR_VIS_TEXT_STYLE_ARRAY *ds:si"

Locates and returns the value list for the data stored under the given token in the vardata of the given object.

  • If the data are found, returns a list {<token> <data>}, where <data> is a standard value list for the type of data associated with the specified token.
  • Returns an empty list if the object has no vardata entry of the given type.
  • If no <address> is given, the default is *ds:si.

fwalk

fwalk [<patient>]

Examples:

"fwalk"
list all open files.
"fwalk geos"
list all open files owned by the GEOS patient.

Print the list of files open anywhere in the system.

  • The patient argument may be used to restrict the list to a particular patient. The patient may be specified either as the patient name or as the patient's handle.
  • fwalk differs from sysfiles and geosfiles in that it deals primarily with GEOS data structures.
  • The `Other' column shows if there is a VM handle bound to the file.
  • The letters in the `Flags' column mean the following:
    • RW
    deny RW
    R
    deny R
    W
    deny W
    N
    deny none
    rw
    access RW
    r
    access R
    w
    access RW
    O
    override, used to override normal exclusion normally used by FileEnum() to check out file headers.
    E
    exclusive, used to prevent override. This is used by swap.geo

See Also: fhandle, geosfiles, sysfiles.

gc

gc [(off|register|<extensive-heap-checking-flag>]

Implements a simple garbage collector to scavenge unreferenced symbols and types. If given an argument other than "off" or "register," it turns on extensive heap checking, which slows things down enormously but ensures the heap is in good shape. The "gc register" command can be use to register a type created by "type make"as something that is being used for an extended period at the Tcl level, preventing the thing from being garbage-collected.

gentree

gentree [<address>] [<instance field>]

Examples:

"gentree"
print the generic tree starting at *DS:SI
"gentree -i"
print the generic tree under the mouse
"gentree [systemobj]"
print the generic tree starting at the system's root
"gentree @23 GI_states"
print the generic tree with generic states
"gentree *uiSystemObj"
start the generic tree at the root of the system

Print a generic tree.

  • The <address> argument is the address to an object in the generic tree. This defaults to *DS:SI. The `-i' flag for an implied grab may be used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • The <instance field> argument is the offset to any instance data within the GenInstance which should be printed out.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available. This variable defaults to false.

See Also: gup, vistree, impliedgrab, systemobj, pobject.

geosfiles

geosfiles

Examples: "geosfiles"

Print out all the files for which I/O is currently pending in GEOS.

  • This looks at the same dos structure as sysfiles but this prints only those files also listed in GEOS' job file table.

See Also: sysfiles, sftwalk, fwalk.

geos-release

This variable contains the major number of the version of GEOS running on the target PC.

geowatch

geowatch [<object>]

Examples:

"geowatch *MyObj"
Display geometry calls that have reached the object MyObj
"geowatch"
Display geometry calls that have reached *ds:si (asm) or oself (goc)

This displays geometry calls that have reached a particular object. Only one object at a time can be watched in this way.

  • Two conditional breakpoints are used by this function (see cbrk). The tokens for these breakpoints are returned.
  • The special object flags may be used to specify object . For a list of these flags, see pobject.

See Also: objwatch, mwatch, cbrk, pobject.

get-address

Used by the various memory-access commands. Takes one argument, ADDR, being the address argument for the command. Typically, the command is declared as

[defcmd cmd {{addr nil}} ... ]

allowing the address to be unspecified. This function will return the given address if it was, else it will return the last-accessed address (stored in the global lastAddr variable as a 3-tuple from addr-parse) in the form of an address expression. If no address is recorded ( lastAddr is nil), the default-addr argument is used. If it is not specified then CS:IP will be used.

getcc

getcc <flag>

Examples:

"getcc c"
Get the carry flag.

Get a flag from the target machine.

  • The first argument is the first letter of the flag to get. The following is a list of the flags:
    • t
    trap
    i
    interrupt enable
    d
    direction
    o
    overflow
    s
    sign
    z
    zero
    a
    auxiliary carry
    p
    parity
    c
    carry
  • This command is handy to run with a breakpoint to stop if a flag is set.

See Also: setcc, clrcc, compcc.

| Up: GEOS SDK TechDocs | Up | Prev: fpstack ... | Next: getenv ...


Swat Command Reference: getenv ...

 | Up: GEOS SDK TechDocs | Up | Prev: func ... | Next: handsum ...

getenv

getenv <NAME>

Examples:

"getenv PTTY"
Fetches the value of the host machine's PTTY environment variable.

Returns the value for a variable defined in Swat's environment.

If the variable isn't defined, this returns the empty string.

See Also: var, string.

get-key-binding

get-key-binding <char>

Examples:

"get-key-binding c"
Gets key binding for the character c.
"get-key-binding \045"
Gets key binding for the % key.

Gets key binding for given key.

See Also: alias, bind-key, unbind-key.

getvalue

getvalue <expr>

Examples:

"getvalue MSG_META_DETACH "
Returns the integer value of the symbol MSG_META_DETACH.

This is a front-end to the "addr-parse" command that allows you to easily obtain the integer value of any expression. It's most useful for converting something the user might have given you to a decimal integer for further processing.

If the expression you give does not evaluate to an address (whose offset will be returned) or an integer, the results of this function are undefined.

See Also: addr-parse, addr-preprocess.

go

go [<address expressions>]

Examples:

"go"
"go drawLeftLine"

Go until an address is reached.

  • The <address expressions> argument is as many address expressions as desired for breakpoints. Execution is continued until a breakpoint is reached. These breakpoints are then removed when the machine stops and are only active for the current patient.

See Also: break, continue, detach, quit.

gup

gup [<address>] [<instance field>]

Examples:

"gup"
print the generic object at *DS:SI and its ancestors
"gup @23 GI_states"
print the states of object @23 and its ancestors
"gup -i"
print the generic object under the mouse and the object's ancestors

Print a list of the object and all of its generic ancestors.

  • The address argument is the address to an object in the generic tree. This defaults to *DS:SI. The `-i' flag for an implied grab may be used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • The instance field argument is the offset to any instance data within the GenInstance which should be printed out.

See Also: gentree, vup, vistree, impliedgrab.

handles

handles [<flags>] [<patient>]

Examples: "handles"
"handles -f"
"handles ui"

Print all handles in-use.

  • The flags argument is a collection of flags, beginning with `-', from the following set:
    • s
    print summary only.
    e
    events only.
    p
    don't print prevPtr and nextPtr.
    f
    fast print-out - this doesn't try to figure out the block type.
    r
    reverse, i.e. starts at the end of the handle table.
    u
    print only those handles that are in-use.
  • The patient argument is a patient whose blocks are to be selectively printed (either a name or a core-block's handle ID). The default is to print all the blocks on the heap.
  • The following columns can appear in a listing:
    • HANDLE
    The handle of the block
    ADDR
    The segment address of the block
    SIZE
    Size of the block in bytes
    PREV
    The previous block handle (appears with the p flag)
    NEXT
    The next block handle (appears with the p flag)
    FLAGS The following letters appears in the FLAGS column:
    s sharable,
    S swapable,
    D discardable,
    L contains local memory heap,
    d discarded
    (by LMem module: discarded blocks don't appear here),
    a attached (notice given to swat whenever state changes)
    LOCK
    Number of times the block is locked or n/a if FIXED.
    OWNER
    The process which owns the block
    IDLE
    The time since the block has been accessed in minutes:seconds
    OINFO
    The otherInfo field of the handle (block type dependent)
    TYPE
    Type of the block, for example: R#1 (dgroup) resource number one
  • This only prints those handles in memory while `handles' prints all handles used.
  • The handles may be printed with lhwalk and phandle.

See Also: lhwalk, phandle, hgwalk.

| Up: GEOS SDK TechDocs | Up | Prev: func ... | Next: handsum ...


Swat Command Reference: handsum ...

 | Up: GEOS SDK TechDocs | Up | Prev: getenv ... | Next: help ...

handsum

handsum

Examples:

"handsum"
Summarize the use to which the handle table is being put.

This command analyzes the handle table and prints out a list of the number of handles being used by each geode, and for what purpose.

  • The columns of the output are labeled somewhat obscurely, owing to horizontal-space constraints. The headings, and their meanings are:
    • Res
    Resource handles (i.e. handles for data stored in the geode's executable)
    Mem
    Non-resource memory handles
    File
    Open files
    Thds
    Threads
    Evs
    Recorded events
    Qs
    Event queues
    Sems
    Semaphores
    EDat
    Data for recorded events
    Tim
    Timers
    SB
    Saved blocks (handles tracking memory/resource handles whose contents will go to an application's state file)
    VMF
    VM files
  • The "handles" command is good at printing out all the handles for a particular geode, but it's generally too verbose to use for the entire handle table. That's why this command exists.
  • It's a good idea to issue the command "dcache length 4096" before executing this command, as it ensures the entire handle table will end up in Swat's data cache, for quick access if you want to use the "handles" command immediately afterward.

hbrk

    hbrk <address> (byte|word) (match|mismatch) <value>

Examples:

"hbrk scrollTab+10 byte match 0"
print message handlers until a zero is written at scrollTab+10.
"hbrk OLScrollButton+3 word mismatch 0x654f"
Break when the word at OLScrollButton+3 is destroyed.

Break when a memory location changes.

  • The <address> argument is the address to watch for a change.
  • The (byte|word) argument indicates whether to watch a byte or a word for a change.
  • The (match|mismatch) argument indicates whether to break if the value at the address matches or mismatches the value hbrk is called with.
  • hbrk emulates a hardware breakpoint by checking at every message call to see if a location in memory has been written to. If so, swat breaks and tells between which two messages the write occurred. The information and the return stack will hopefully guide you to the offending line of code.
  • The command creates two breakpoints. Remove these to get rid of the hardware breakpoint.

See Also: brk, mwatch, showcalls.

heapspace

heapspace <geode>
heapspace total
heapspace syslib

Examples:

"heapspace geomanager"
print out "heapspace" value for geomanager
"heapspace total"
print out maxTotalHeapSpace
"heapspace syslib"
print out space being used by system libraries.

Prints out how much space the program requires on the heap. This value may then be used in a "heapspace" line of the program's .gp field. This command only determines present usage--to determine the most heapspace your geode will ever use requires that you make it allocate as much space as it ever will. This means pulling down all menus, opening all dialog boxes, and generally building out all UI gadgetry. The value this command prints is roughly the non-discardable heap usage by the app and any transient libraries that it depends on, plus an additional amount for thread activity.

When using this command, you should run your application on the device it is to run on. One or more system libraries may be XIP (running from ROM) on the device, but run from RAM on your target PC's emulator. Thus, to get a more accurate picture of memory usage, run the application on the planned device.

| Up: GEOS SDK TechDocs | Up | Prev: getenv ... | Next: help ...


Swat Command Reference: help ...

 | Up: GEOS SDK TechDocs | Up | Prev: handsum ... | Next: hwalk ...

help

help [<command>]

This is the user-level access to the on-line help facilities for Swat. If given a topic (e.g. "brk") as its argument, it will print all help strings defined for the given topic (there could be more than one if the same name is used for both a variable and a procedure, for instance). If invoked without arguments, it will enter a browsing mode, allowing the user to work his/her way up and down the documentation tree.

help-fetch

help-fetch <topic-path>

Examples: "help-fetch top.patient"

Fetches the help string for a given topic path in the help tree.

If there is more than one node with the given path in the help tree, only the string for the first node will be returned.

help-fetch-level

help-fetch-level

Examples:

"help-fetch-level top.prog.obscure"
Returns the topics within the "top.prog.obscure" level of the help tree.

Returns a list of the topics available at a given level in the help tree.

The result is a list of node names without leading path components.

See Also: help-fetch.

help-help

help-help

Provides help about using the help command (q.v.)

See Also: help.

help-is-leaf

help-is-leaf <topic-path>

Examples:

"help-is-leaf top.prog"
See if top.prog is a leaf node in the help tree (i.e. if it has no children).

Determines whether a given path refers to a help topic or a help category.

Returns one if the given path refers to a leaf node, zero if it is not.

See Also: help-fetch, help-fetch-level.

help-minAspect

var help-minAspect [<ratio-times-ten>]

If non-zero, contains the minimum aspect ratio to be maintained when displaying tables in the help browser. The ratio is expressed as the fraction

entries_per_column*10/number_of_columns

E.g. a minimum ratio of 1.5 would be 15. (We multiply by ten because Swat doesn't support floating point numbers.)

help-scan

help-scan <pattern>

Examples:

"help-scan break"
Looks for all nodes at any level of the help tree whose documentation includes the pattern "break".

Scans all nodes in the help tree for those whose documentation matches a given pattern.

  • The result is a list of topic-paths.

See Also: help-fetch.

help-verbose

var help-verbose [0|1)]

If non-zero, performs verbose prompting.

hex

hex <number>

Examples:

"hex 034"
print hex equivalent of octal 34.
"hex 12"
print hex equivalent of decimal 12.

Print hexadecimal equivalent of a number.

hgwalk

hgwalk

Examples:

"hgwalk"
print statistics on all geodes

Print out all geodes and their memory usage.

history

history [<args>]

Examples:

"history 10"
Prints the last 10 commands entered via the "history subst" command.
"history subst $line"
Performs history substitution on the string in $line, enters the result in the history queue and returns the result.
"var n [history cur]"
Stores the number of the next string to be entered via "history subst" in the variable n.
"history set 50"
Limit the number of entries in the queue to 50.
"history fetch 36"
Returns the string entered as command number 36 in the history queue.

This command manipulates the history list. Options are:

<number>
Prints the most-recent <number> commands
set <queue-size>
Sets the number of commands saved
subst <str>
Performs history substitution on <str> and enters it into the history queue.
cur
Returns the current history number. If no argument is given, all saved commands are printed.
fetch <n>
Returns the string entered as command number <n> in the history queue.
| Up: GEOS SDK TechDocs | Up | Prev: handsum ... | Next: hwalk ...


Swat Command Reference: hwalk ...

 | Up: GEOS SDK TechDocs | Up | Prev: help ... | Next: imem ...

hwalk

hwalk [<flags>] [<patient>]

Examples:

"hwalk"
display the heap
"hwalk -e"
display the heap and perform error checking
"hwalk -r ui"
display the heap owned by the ui in reverse order

Print the status of all blocks on the global heap.

  • The <flags> argument is a collection of flags, beginning with `-', from the following set:
    • r
    print heap in reverse order (decreasing order of addresses)
    p
    print prevPtr and nextPtr as well.
    e
    do error-checking on the heap.
    l
    just print out locked blocks
    f
    fast print-out--this doesn't try to figure out the block type
    F
    print out only fixed (or pseudo-fixed) resources.
    c
    Print out only code resources (discardable or fixed non-lmem non-dgroup resources).
    s < num >
    start at block <num>
  • The patient argument is a patient whose blocks are to be selectively printed (either a name or a core-block's handle ID). The default is to print all the blocks on the heap.
  • The following columns can appear in a listing:
    • HANDLE
    The handle of the block
    ADDR
    The segment address of the block
    SIZE
    Size of the block in bytes
    PREV
    The previous block handle (appears with the p flag)
    NEXT
    The next block handle (appears with the p flag)
    FLAGS The following letters appears in the FLAGS column:
    s sharable
    S swapable
    D discardable
    L contains local memory heap
    d discarded
    (by LMem module: discarded blocks don't appear here)
    a attached (notice given to swat whenever state changes)
    LOCK
    Number of times the block is locked or n/a if FIXED.
    OWNER
    The process which owns the block
    IDLE
    The time since the block has been accessed in minutes:seconds
    OINFO
    The otherInfo field of the handle (block type dependent)
    TYPE Type of the block, for example:
    R#1 (dgroup) Resource number one, named "dgroup"
    Geode Internal control block for a geode
    WINDOW, GSTATE,
    Internal structures of the given type
    GSTRING, FONT_BLK, FONT OBJ(write:0)
    Object block run by thread write:0
    VM(3ef0h)...
    VM block from VM file 3ef0h
  • This only prints those handles in memory while `handles' prints all handles used.
  • The handles may be printed with lhwalk and phandle.

See Also: lhwalk, phandle, handles, hgwalk.

iacp

iacp -ac
prints all connections

iacp -l
prints all lists without connections

iacp -d
prints all open documents

iacp <obj>
prints all connections to which <obj> is party

ibrk

Set a breakpoint interactively. At each instruction, you have several options:

q
Quit back to the command level.
n
Go to next instruction (this also happens if you just hit return).
p
Go to previous instruction.
P
Look for a different previous instruction.
^D
Go down a "page" of instructions. The size of the page is controlled by the global variable ibrkPageLen. It defaults to 10.
^U
Go up a "page" of instructions.
b
Set an unconditional breakpoint at the current instruction and go back to command level.
a
Like 'b', but the breakpoint is set for all patients.
t
Like 'b', except the breakpoint is temporary and will be removed the next time the machine stops.
B
Like 'b', but can be followed by a command to execute when the breakpoint is hit.
A
Like 'B', but for all patients.
T
Like 'B', but breakpoint is temporary.

ibrkPageLen

var ibrkPageLen [<number-of-lines>]

Number of instructions to skip when using the ^D and ^U commands of ibrk.

ignerr

ignerr

Examples:

"ignerr" ignore error and continue
"ignerr MyFunc::done" ignore error and continue at MyFunc::done.

Ignore a fatal error and continue.

  • The address argument is the address of where to continue execution. If not specified then CS:IP is taken from the frame.
  • The stack is patched so that execution can continue in the frame above the fatal error handling routine.

See Also: why, backtrace.

| Up: GEOS SDK TechDocs | Up | Prev: help ... | Next: imem ...


Swat Command Reference: imem ...

 | Up: GEOS SDK TechDocs | Up | Prev: hwalk ... | Next: intr ...

imem

imem [<address>] [<mode>]

Examples:

"imem"
enter imem mode at DS:SI
"imem ds:di"
enter imem mode at DS:SI

Examine memory and modify memory interactively.

  • The address argument is the address to examine. If not specified, the address after the last examined memory location is used. If no address has been examined then DS:SI is used for the address.
  • The mode argument determines how the memory is displayed and modified. Each of the four modes display the memory in various appropriate formats. The modes are:
  • The default mode is swat's best guess of what type of object is at the address.
  • imem lets you conveniently examine memory at different locations and assign it different values. imem displays the memory at the current address according to the mode. From there you can move to another memory address or you can assign the memory a value.
  • You may choose from the following single-character commands:
    • b , w , d , i
    Sets the mode to the given one and redisplays the data.
    n , j , < Return >
    Advances to the next data item. The memory address advances by the size of the mode.
    p , k
    Returns to the preceding data item. The memory address decreases by the size of the mode. When displaying instructions, a heuristic is applied to locate the preceding instruction. If it chooses the wrong one, use the `P' command to make it search again.
    < space >
    Clears the data display and allows you to enter a new value appropriate to the current display mode. The "assign" command is used to perform the assignment, so the same rules apply to it, with the exception of `- and "-quoted strings. A string with `s around it (`hi mom') has its characters poked into memory starting at the current address. A string with "s around it ("swat.exe") likewise has its characters poked into memory, with the addition of a null byte at the end. This command is not valid in instruction mode.
    q
    quit imem and return to command level. The last address accessed is recorded for use by the other memory-access commands.
    ^D
    Display a "page" of successive memory elements in the current mode.
    ^U
    Display a "page" of preceding memory elements in the current mode.
    h , ?
    This help list.
    For ^D and ^U, the size of a "page" is kept in the global variable imemPageLen , which defaults to 10.

See Also: bytes, words, dwords, assign.

imemPageLen

var imemPageLen [<numlines>]

Contains the number of elements to display when imem is given the ^D or ^U command.

impliedgrab

impliedgrab

Examples:

"gentree [impliedgrab]"
print the generic tree under the mouse

Print the address of the current implied grab, which is the screen object grabbing the mouse.

  • This command is normally used with gentree to get the generic tree of an application by placing the mouse on application's window and issuing the command.

See Also: systemobj, gentree.

impliedwin

impliedwin
"wintree [impliedwin]"
print the window tree of the window under the mouse

Print the address of the current implied window (the window under the mouse).

  • Note that a window handle is returned.
  • This command is normally used with wintree. One may also use the print command if they properly cast the handle.

int

int [<int level> <state>]

Examples:

"int"
report the interrupt statuses
"int 1:1 on"
allow keyboard interrupt while in swat

Set or print the state of the two interrupt controllers for when then machine is stopped in Swat.

  • If no arguments are given, the current state is printed.
  • The <int level> argument is specified by their names or their numbers with the form <controller>:<number>. <controller> is either 1 or 2, and <number> ranges from 0 to 7. The interrupts and their numbers are:
    • Timer 1:0
    System timer. Probably dangerous to enable.
    Keybd 1:1
    Keyboard input.
    Slave 1:2
    This is how devices on controller 2 interrupt. Disabling this disables them all.
    Com2 1:3
    This is the port usually used by Swat, so it can't be disabled.
    Com1 1:4
    The other serial port -- usually the mouse.
    LPT2 1:5
    The second parallel port
    Floppy 1:6
    Floppy-disk drive
    LPT1 1:7
    First parallel port
    Clock 2:0
    Real-time clock
    Net 2:1
    Network interfaces (?)
    FPA 2:5
    Coprocessor
    HardDisk 2:6
    Hard-disk drive
  • The <state> argument is either on or off.

intFormat

var intFormat [<format-string>]

Examples:

"var intFormat %d"
Sets the default format for printing unsigned integers to decimal.

$intFormat contains the string passed to the "format" command to print an integer.

The default value is {%xh}, which prints the integer in hexadecimal, followed by an "h".

See Also: print, byteAsChar.

| Up: GEOS SDK TechDocs | Up | Prev: hwalk ... | Next: intr ...


Swat Command Reference: intr ...

 | Up: GEOS SDK TechDocs | Up | Prev: imem ... | Next: keyboard ...

intr

Catch, ignore, or deliver an interrupt on the target PC. First argument is the interrupt number. Optional second argument is "catch" to catch delivery of the interrupt, "ignore" to ignore the delivery, or "send" to send the interrupt (the machine will keep going once the interrupt has been handled). If no second argument is given, the interrupt is delivered.

io

io [w] <port> [<value>]

Examples:

"io 21h"
Reads byte-sized I/O port 21h.
"io 20h 10"
Writes decimal 10 to byte-sized I/O port 20h.

Provides access to any I/O port on the PC.

  • If you give the optional first argument "w" then Swat will perform a 16-bit I/O read or write, rather than the default 8-bit access. Be aware that most devices don't handle this too well.
  • <port> must be a number (in any radix); it cannot be a register or other complex expression.
  • If you don't give a <value>, you will be returned the contents of the I/O port (it will not be printed to the screen).

irq

irq
irq (no|yes)
irq (set|clear)

Examples:

"irq"
Returns non-zero if an interrupt is pending.
"irq no"
Disable recognition and acting on a break request from the keyboard.
"irq set"
Pretend the user typed Ctrl-C.

Controls Swat's behavior with respect to interrupt requests from the keyboard.

  • Swat maintains an interrupt-pending flag that is set when you type Ctrl+C (it can also be set or cleared by this command). It delays acting on the interrupt until the start of the next or the completion of the current Tcl command, whichever comes first.
  • When given no arguments, it returns the current state of the interrupt-pending flag. This will only ever be non-zero if Swat is ignoring the flag (since the command wouldn't actually return if the flag were set and being paid attention to, as the interpreter would act on the flag to vault straight back to the command prompt).
  • If given "no" or "yes" as an argument, it causes Swat to ignore or pay attention to the interrupt-pending flag, respectively.
  • You can set or clear the flag by giving "set" or "clear" as an argument.

is-obj-in-class

is-obj-in-class <obj-addr> <class-name>

Examples:

"is-obj-in-class ^l4e10h:1eh GenPrimaryClass"
see if the object at ^l4e10h:1eh is in GenPrimaryClass .

Returns whether a given object is in the specified class.

  • Returns one if the object is in the specified class, zero otherwise. It will return one if the object's class is a subclass of the passed class.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.

See Also: psup.

istep

istep [<default command>]

Examples:

"is"
enter instruction step mode
"istep n"
enter instruction step mode, <ret> does a next command

Step through the execution of the current patient. This is THE command for stepping through assembly code.

  • The default command argument determines what pressing the <Return> key does. By default, <Return> executes a step command. Any other command listed below may be substituted by passing the letter of the command.
  • Istep steps through the patient instruction by instruction, printing where the ip is, what instruction will be executed, and what the instruction arguments contain or reference. Istep waits for the user to type a command which it performs and then prints out again where istep is executing.
  • This is a list of istep commands:
    • q , < Esc >, ` `
    Stops istep and returns to command level.
    b
    Toggles a breakpoint at the current location.
    c
    Stops istep and continues execution.
    n
    Continues to the next instruction, skipping procedure calls, repeated string instructions, and software interrupts. Using this procedure, istep only stops when the machine returns to the right context (i.e. the stack pointer and current thread are the same as they are when the "n" command was given). Routines which change the stack pointer should use "N" instead.
    o
    Like "n" but steps over macros as well.
    l
    Goes to the next library routine.
    N
    Like `n', but stops whenever the breakpoint is hit, whether you're in the same frame or not.
    O
    Like `N' but steps over macros as well.
    m, M
    Goes to the next method called. Doesn't work when the message is not handled anywhere.
    F
    Finishes the current message.
    f
    Finishes out the current stack frame.
    s , < Return >
    Steps one instruction.
    A
    Aborts the current stack frame.
    S
    Skips the current instruction
    B
    Backs up an instruction (opposite of "S").
    J
    Jump on a conditional jump, even when "Will not jump" appears. This does not change the condition codes.
    g
    Executes the `go' command with the rest of the line as arguments.
    e
    Executes a Tcl command and returns to the prompt.
    r
    Lists the registers (uses the regs command)
    R
    References either the function to be called or the function currently executing.
    h , ?
    Displays a help message.
  • If the current patient isn't the actual current thread, istep waits for the patient to wake up before single-stepping it.

See Also: sstep, listi.

| Up: GEOS SDK TechDocs | Up | Prev: imem ... | Next: keyboard ...


Swat Command Reference: keyboard ...

 | Up: GEOS SDK TechDocs | Up | Prev: intr ... | Next: load ...

keyboard

keyboard [<object>]

Examples:

"keyboard"
print keyboard hierarchy from system object down
"keyboard -i"
print keyboard hierarchy from implied grab down
"keyboard ^l4e10h:20h"
print keyboard hierarchy from ^l4e10h:20h down.

Prints the keyboard hierarchy below an object.

  • If no object is specified, the system object is used.

  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available.

    • This variable defaults to false.

See Also: target, focus, mouse, model, pobject.

keyboardobj

keyboardobj

Examples:

"keyboardobj"
return object with keyboard grab
"pobj [keyboardobj]"
do a pobject on the object with the keyboard grab (equivalent to "pobj -kg".

Returns the object with the keyboard grab.

See Also: target, focus, mouse, keyboard, mouseobj.

lastCommand

$lastCommand

Examples:

"var repeatCommand $lastCommand"
Set the current command as the command to execute next time.

$lastCommand stores the text of the command currently being executed.

This variable is set by top-level-read. Setting it yourself will have no effect, unless you call set-address or some similar routine that looks at it.

See Also: repeatCommand.

lhwalk

lhwalk [<address>]

Examples:

"lhwalk 1581h"
list the lm heap at 1581h:0

Prints out information about a local memory heap.

  • The address argument is the address of the block to print. The default is the block pointed to by DS.

See Also: hwalk, objwalk.

link

link <library> [<patient>]

Examples:

"link motif"
Makes the library "motif" a library of the current patient as far as Swat is concerned.

Allows you to link a patient to act as an imported library of another patient, even though the other patient doesn't actually import the patient. This is useful only for symbol searches.

  • sym-default is a much better way to have Swat locate symbols for libraries that are loaded by GeodeUseLibrary() .
  • Cycles are not allowed. I.e. don't link your application as a library of the UI, as it won't work--or if it does, it will make Swat die.
  • The link persists across detach/attach sequences so long as the <patient> isn't recompiled and downloaded.
  • If you don't give <patient>, then the current patient will be the one made to import <library>
  • Both <library> and <patient> are patient names , not tokens.

See Also: help-fetch.

listi

listi [<address>] [<length>]

Examples:

"l"
disassemble at the current point of execution
"listi geos::Dispatch"
disassemble at the kernel's dispatch routine
"listi DocClip:IsOut"
disassemble at the local label
"listi cs:ip 20"
disassemble 20 instructions from the current point of execution

Disassemble at a memory address.

  • The <address> argument is the address to examine. If not specified, the address after the last examined memory location is used. If no address has be examined then CS:IP is used for the address.
  • The <length> argument is the number of instructions to list. It defaults to 16.
  • Pressing <Return> after this command continues the list.

See Also: istep, sstep, skip.

| Up: GEOS SDK TechDocs | Up | Prev: intr ... | Next: load ...


Swat Command Reference: load ...

 | Up: GEOS SDK TechDocs | Up | Prev: keyboard ... | Next: memsize ...

load

load <file>

Load a file of Tcl commands.

  • If the file cannot be found as given, it is looked for in all the directories mentioned in the "load-path" variable. This variable is initialized from the SWATPATH environment variable, which is in the form <dir1>:<dir2>:...:<dirN>.
  • The Swat library directory is appended to this path so you need not include it yourself. The file need not end in ".tcl".
  • When searching, file , file .tcl, and file .tlc are searched for. If load finds a file .tlc file, that file will be used only if it is more recent than any corresponding file .tcl or file file.

loadapp

Load an application from swat. Single argument is the file name of the application to launch (application must reside in the appl subdirectory of the GEOS tree).

The application is opened in normal application mode. Note that the application will not be loaded until you continue the machine, as the loading is accomplished by sending a message to the UI.

loadgeode

Load a geode from swat. Mandatory first argument is the name of the file to load (with path from top-level GEOS directory, using / instead of \ as the path separator).

Second and third arguments are the data words to pass to the geode. The second argument is passed to the geode in cx, while the third argument is passed in dx.

Both the second and third arguments are optional and default to 0. They likely are unnecessary.

locals

locals [<func>]

Examples:

"locals"
Print the values of all local variables and arguments for the current frame.
"locals WinOpen"
Print the names of all local variables for the given function. No values are printed.

Allows you to quickly find the values or names of all the local variables of a function or stack frame.

See Also: print, frame, info.

localwin

localwin [<numlines>]

Examples:

"localwin"
Display local variables in a 10-line window
"localwin 15"
Display local variables in a 15-line window
"localwin off"
Turn off the local variable display

Turn on or off the continuous display of local variables.

  • Passing an optional numerical argument turns on display of that size. The default size is 10 lines.
  • Only one local variable display may be active at a time.

loop

Simple integer loop procedure. Usage is:
loop <loop-variable> <start>,<end> [step <step>] <body>

<start>, <end>, and <step> are integers. <body> is a string for Tcl to evaluate. If no <step> is given, 1 or -1 (depending as <start> is less than or greater than <end>, respectively) is used. <loop-variable> is any legal Tcl variable name.

map

map <var-list> <data-list>+ <body>

Examples:

"map {i j} {a b} {c d} {list $i $j}"
Executes the command "list $i $j" with i and j assigned to successive elements of the lists {a b} and {c d}, respectively, merging the results into the list {{a c} {b d}}

This applies a command string to the successive elements of one or more lists, binding each element in turn to a variable and evaluating the command string. The results of all the evaluations are merged into a result list.

  • The number of variables given in <var-list> must match the number of <data-list> arguments you give.
  • All the <data-list> arguments must have the same number of elements.
  • You do not specify the result of the <body> with the "return" command. Rather, the result of <body> is the result of the last command executed within <body>.

See Also: foreach, mapconcat.

mapconcat

mapconcat <var-list> <data-list>+ <body>

Examples:

"mapconcat {i j} {a b} {c d} {list $i $j}"
Executes the command "list $i $j" with i and j assigned to successive elements of the lists {a b} and {c d}, respectively, merging the results into a string.

map-method

map-method <number> <object>
map-method <number> <class-name> [<object>]

Examples:

"map-method ax ^lbx:si"
Prints the name of the message in ax, from the object at ^lbx:si's perspective.
"map-method 293 GenClass"
Prints the name of message number 293 from GenClass's perspective.

Maps a message number to a human-readable message name, returning that name. This command is useful both for the user and for a Tcl procedure.

  • When called from a Tcl procedure, the <class-name> argument should be the fullname of the class symbol (usually obtained with the obj-class function), and <object> should be the address of the object for which the mapping is to take place. If no <object> argument is provided, map-method will be unable to resolve messages defined by one of the object's superclasses that lies beyond a variant superclass.
  • If no name can be found, the message number, in decimal, is returned.
  • The result is simply returned, not echoed. You will need to echo the result yourself if you call this function from anywhere but the command line.

See Also: obj-class.

mcount

mcount [<args>]

Examples:

"mcount"
start the method count or print the count
"mcount reset"
restart the method count
"mcount stop"
stop the method count
"mcount MyAppRecalcSize"
count messages handled by MyAppRecalcSize

Keep a count of the methods called.

The args argument may be one of the following:

nothing
start the method count or print the current count
'reset'
reset the count to zero
'stop'
stop the method count and remove it's breakpoint
message handler
start the method count for a particular method

See Also: mwatch, showcalls.

| Up: GEOS SDK TechDocs | Up | Prev: keyboard ... | Next: memsize ...


Swat Command Reference: memsize ...

 | Up: GEOS SDK TechDocs | Up | Prev: load ... | Next: noStructEnum ...

memsize

memsize [<memory size>]

Examples:

"memsize"
"memsize 512"

Change the amount of memory that GEOS thinks that it has.

  • The <memory size> argument is the size to make the heap. If none is specified then the current memory size is returned.
  • Memsize can only be run at startup, before the heap has been initialized. Use this right after an `att -s'.
  • Memsize accounts for the size of the stub.

methods

methods <class>
methods <object>
methods <flags>

Examples:

"methods -p"
Print out methods defined for process
"methods ui::GenDocumentClass"
Print out GenDocumentClass methods
"methods 3ffch:072fh"
Print out methods for class at addr
"methods -a"
Print methods of top class of app obj

Prints out the method table for the class specified, or if an object is passed, for the overall class of the object. Useful for getting a list of candidate locations to breakpoint.

model

model [<object>]

Examples:

"model"
print model hierarchy from system object down
"model -i"
print model hierarchy from implied grab down
"model ^l4e10h:20h"
print model hierarchy from ^l4e10h:20h down.

Prints the model hierarchy below an object.

  • If no object is specified, the system object is used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • Remember that the object you start from may have the model exclusive within its part of the hierarchy, but still not have the exclusive because something in a different part of the tree has it.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available.

    • This variable defaults to false.

See Also: target, focus, mouse, keyboard, pobject.

modelobj

modelobj

Examples:

"modelobj"
print model hierarchy from system object down
"pobj [modelobj]"
Do a pobject on the object with the model grab (the equivalent of a "pobj -m").

Returns the object with the model grab.

See Also: target, focus, model, focusobj, targetobj.

mouse

mouse [<object>]

Examples:

"mouse"
print mouse hierarchy from system object down
"mouse -i"
print mouse hierarchy from implied grab down
"mouse ^l4e10h:20h"
print mouse hierarchy from ^l4e10h:20h down.

Prints the mouse hierarchy below an object.

  • If no object is specified, the system object is used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available.

    • This variable defaults to false.

See Also: target, focus, model, keyboard, pobject.

mouseobj

mouseobj

Examples:

"mouseobj"
return object with mouse grab
"pobj [mouseobj]"
do a pobject on the object with the mouse grab (equivalent to "pobj -mg").

Returns the object with the mouse grab.

See Also: target, focus, mouse, keyboard, keyboardobj.

mwatch

mwatch <msg>+
mwatch add <msg>+
mwatch list
mwatch clear

Examples:

"mwatch MSG_VIS_DRAW MSG_METAQUIT"
watch these messages
"mwatch add MSG_META_START_SELECT "
watch this message also
"mwatch"
clear all message watches

Display all deliveries of a particular message.

  • The msg argument is which messages to watch. Those specified replace any messages watched before. If none are specified then any messages watched will be cleared.
  • You may specify up to eight messages to be watched (fewer if you have other conditional breakpoints active). See cbrk for more information about conditional breakpoints.
  • "mwatch clear" will clear all message watches.
  • "mwatch add" will add the specified messages to the watch list.
  • "mwatch list" will return a list of breakpoints that have been set by previous calls to mwatch.

See Also: objwatch, objbrk, objmessagebrk, procmessagebrk.

next

next

Examples:

"next"
execute the next assembly instruction without entering it
"n"

Execute the patient by a single assembly instruction, skipping over any calls, repeated instructions, or software interrupts.

  • next does not protect against recursion, so when the breakpoint for the next instruction is hit, the frame of execution may be one lower.

See Also: step, istep.

| Up: GEOS SDK TechDocs | Up | Prev: load ... | Next: noStructEnum ...


Swat Command Reference: noStructEnum ...

 | Up: GEOS SDK TechDocs | Up | Prev: memsize ... | Next: objmessagebrk ...

noStructEnum

var noStructEnum [(0|1)]

Examples:

"var noStructEnum 1"
Don't put "struct" or "enum" before the data type for variables that are structures or enumerated types.

Structure fields that are structures or enumerated types normally have "struct" or "enum" as part of their type description. This usually just clutters up the display, however, so this variable shuts off this prepending.

The default value of this variable is one.

See Also: print.

null

null <val>

Examples:

"null $sym"
Sees if the symbol token stored in $sym is the empty string or "nil."

Checks to see if a string is either empty or "nil," special values returned by many commands when something isn't found or doesn't apply. Returns non-zero if <val> is either of these special values.

The notion of "nil" as a value comes from lisp.

See Also: index, range.

objbrk

objbrk [<obj address>] [<message>]

Examples:

"objbrk ds:si MSG_VIS_DRAW "
break when a MSG_VIS_DRAW reaches the object
"objbrk -p"
Break when any message is sent to the process object.

Break when a particular message reaches a particular object.

  • If you do not give a <message> argument after the <obj> argument, the machine will stop when any message is delivered to the object.
  • <obj> is the address of the object to watch.
  • The <objbrk> argument to "objbrk del" is the token/number returned when you set the breakpoint.

See Also: objwatch, objmessagebrk, mwatch.

obj-class

objclass <obj-addr>

Examples:

"var cs [obj-class ^lbx:si]"
Store the symbol token for the class of the object ^lbx:si in the variable $cs.

Figures out the class of an object, coping with unrelocated object blocks and the like.

  • The value return is a symbol token, as one would pass to the "symbol" command. Using "symbol name" or "symbol fullname" you can obtain the actual class name.
  • We decide whether to relocate the class pointer ourselves based on the LMF_RELOCATED bit in the LMBH_flags field of the object block's header. There are times, e.g. during the call to MSG_META_RELOCATE for an object, when this bit doesn't accurately reflect the state of the class pointer and we will return an error when we should not.

See Also: symbol.

objcount

objcount [-q] [-X] [-Y] [-b #] [-o #] [-p #]

Examples:

"objcount"
count all objects
"objcount -p welcome"
count all objects owned by welcome
"objcount -o *desktop::DiskDrives"
count this one object
"objcount -b 0x3270"
count all objects in this block.

Count up instances of various objects on the heap.

  • The first argument specifies the options:
    • q
    quiet operation - no progress output (not applicable with X, Y)
    o #
    check only object #
    b #
    check ONLY block #
    p #
    check only blocks for patient #
    c #
    check only objects of class #
    C #
    check only objects of top-level class #
    X
    show general verbose info
    Y
    show search verbose info
  • Output fields:
    • direct
    number of direct instances of this class
    indirect
    number if indirect instance of this class (i.e object's superclass is this class)
    size
    total size of instance data for this class (excludes instance data inherited from superclass)
  • Status output:
    • .
    processing heap block
    ,
    processing matching object's top-level class
    ;
    processing matching object's non-top-level class

See Also: hwalk, objwalk, lhwalk.

obj-foreach-class

obj-foreach-class <function> <object> [<args>]

Examples:

"obj-foreach-class foo-callback ^lbx:si"
calls foo-callback with each class in turn to which the object ^lbx:si belongs.

Processes all the classes to which an object belongs, calling a callback procedure for each class symbol in turn.

  • <function> is called with the symbol for the current class as its first argument, <object> as its second, and the arguments that follow <object> as its third and subsequent arguments.
  • <function> should return an empty string to continue up the class tree.
  • obj-foreach-class returns whatever <function> returned, if it halted processing before the root of the class tree was reached. It returns the empty string if <function> never returned a non-empty result.

See Also: obj-class.

| Up: GEOS SDK TechDocs | Up | Prev: memsize ... | Next: objmessagebrk ...


Swat Command Reference: objmessagebrk ...

 | Up: GEOS SDK TechDocs | Up | Prev: noStructEnum ... | Next: patchin ...

objmessagebrk

objmessagebrk [<address>]

Examples:

"objmessagebrk MyObj"
break whenever a message is sent to MyObj
"objmessagebrk"
stop intercepting messages

Break whenever a message is sent to a particular object via ObjMessage.

  • The <address> argument is the address to an object to watch for messages being sent to it. If no argument is specified then the watching is stopped.
  • This breaks whenever a message is sent (before they get on the message queue. This enables one to track identical messages to an object which can be removed.

See Also: objwatch, mwatch, procmessagebrk, pobject.

objwalk

objwalk [<address>]

Examples: "objwalk"

Prints out information about an object block.

  • The <address> argument is the address of the block to print. The default is the block pointed at by DS.

See Also: lhwalk, pobject.

objwatch

objwatch [<address>]

Examples:

"objwatch ds:si"
watch the messages which reach the object at DS:SI
"objwatch MyObject"
watch the messages which reach MyObject
"objwatch"
Watch the messages which reach the process object.

Display message calls that have reached a particular object.

  • The <address> argument is the address of the object to watch.
  • This returns the token of the breakpoint being used to watch message deliveries to the object. Use the "brk" command to enable, disable, or turn off the watching of the object.

See Also: brk, mwatch, objmessagebrk, procmessagebrk, pobject.

omfq

omfq <message> <object> <args>*

Examples:

"omfq MSG_META_QUIT *HelloApp"
Sends MSG_META_QUIT to the * HelloApp object.

Forces a message for an object onto its event queue.

  • This command calls ObjMessage, passing it
    di=mask MF_FORCE_QUEUE.
  • <args> is the set of additional parameters to pass to ObjMessage. It consists of <variable/register> <value> pairs, which are passed to the "assign" command. As a special case, if the variable is "push", the value (a word) is pushed onto the stack and is popped when the message has been queued.
  • The registers active before you issued this command are always restored, regardless of whether the call to ObjMessage completes successfully. This is in contrast to the "call" command, which leaves you where the machine stopped with the previous state lost.
  • This command will only work when execution halts in the thread that is to receive the message.

See Also: call.

pappcache

pappcache

Examples:

"pappcache"
Print out current state of the app-cache

Prints out the current state of the system application cache, for systems operating in transparent launch mode.

Specifically, this command prints out:

  • Applications in the cache (First choice for detaching)
  • Top full-screen App (Not detached except by another full screen app)
  • Desk accessories (detached only as last resort)
  • Application geodes in the process of detaching

patch

patch [<addr>]
patch del <addr>*

Patch assists in creating breakpoints that invisibly make small changes to code. This can help the programmer find several bugs without remaking and redownloading.

  • If you give no <addr> when creating a patch, the patch will be placed at the most-recently accessed address, as set by the command that most-recently accessed memory (e.g. bytes, words, listi, imem, etc.)
  • When creating a patch, you are prompted for its contents, each line of which comes from the following command set:

Patch Command Set

Form | Meaning | Example
<reg> = <value> | assign value to reg | ax = bx dl = 5
push <reg>|<value> | push value | push ax push 45
pop <reg>|<value> | pop value | pop ax pop 45
pop | pop nothing (sp=sp+2) | pop
jmp <address> | change ip | jmp UI_Attach+45
scall <address> <regs> | call routine (save) | scall MemLock ax = 3
mcall<address> <regs> | call routine (modify) | mcall MemLock ax = 3
xchg <reg> <reg> | swap two registers | xchg ax bx
set <flag> | set condition flag | set CF set ZF
reset <flag> | reset condition flag | reset CF reset ZF
if <flag> | if flag set then ... | if CF
if !<flag> | if flag reset then ... | if !ZF
if <expr> | if expr then ... | if foo == 4
else |  |
endif |  |
ret | make function return | ret
$ | terminate input |
a | abort |
<other> | tcl command | echo $foo

<flag> is taken from the set TF, IF, DF, OF, SF, ZF, PF, AF, CF and must be in upper-case.

The "scall" command has no effect on the current registers (not even for purposes of return values), while the "mcall" command changes whatever registers the function called modifies. See the "call" documentation for the format of <regs>.

| Up: GEOS SDK TechDocs | Up | Prev: noStructEnum ... | Next: patchin ...


Swat Command Reference: patchin ...

 | Up: GEOS SDK TechDocs | Up | Prev: objmessagebrk ... | Next: pdisk ...

patchin

Patchin undoes the work of patchout.

patchout

This command causes a RET to be placed at the start of a routine.

patient-default

patient-default [<geode-name>]

Examples:

"patient-default hello2"
Makes "hello2" the default patient.
"patient-default"
Prints the names of the current default patient.

Specifies the default patient. The send and run commands will use this as the default patient to operate on if none is passed to them.

pbitmap

pbitmap <address>

Print a bitmap graphically.

  • The address argument is the address of the Bitmap or CBitmap structure.
  • Color bitmaps are printed with a letter representing the color as well. The letters are index from the string (kbgcrvnAaBGCRVYW).

pcarray

pcarray [-eth] [<address>]

Examples:

"pcarray"
Print the chunk array at *DS:SI (header only)
"pcarray es:di"
Print the chunk array at ES:DI (header only)
"pcarray -e"
Print the chunk array at *DS:SI and print the elements in the array
"pcarray -tMyStruct"
Print the chunk array at *DS:SI and print the elements where the elements are of type MyStruct
"pcarry -tMyStruct -TMyExtraStruct"
Like above, but data after MyStruct is printed as an array of MyExtraStruct structures.
"pcarray -e3"
Print the chunk array at *DS:SI and print the third element
"pcarray -hMyHeader"
Print the chunk array at *DS:SI (header only) where the header is of type MyHeader

Print information about a chunk array.

  • The flags argument can be any combination of the flags "e", "t", and "h". The "e" flag prints all elements. If followed by a number (e.g. "-e3"), then only the third element will be printed.
  • The `t' flag specifies the elements' type. It should be followed
    immediately by the element type. You can also use "-tgstring" if
    the elements are GString Elements.
  • The `h' flag specifies the header type. It should be followed immediately by the element type.
  • The `l' flag specifies how many elements to print. It can be used in conjunction with the `e' flag to print a range of element numbers.
  • The `H' flag suppresses printing of the header.
  • All flags are optional and may be combined.
  • The address argument is the address of the chunk array. If not specified then *ds:si is used.

pcbitmap

pcbitmap <address> <width> <height> [<no space flag>]

Examples:

"pcbitmap *ds:si 64 64 t"
print the bitmap without spaces

Print out a one-bit deep packbits-compacted bitmap.

  • The <address> argument is the address to the bitmap data.
  • The <width> argument is the width of the bitmap in pixels.
  • The <height> argument is the height of the bitmap in pixels.
  • The <no space flag> argument removes the space normally printed between the pixels. Anything (like "t") will activate the flag.

See Also: pncbitmap.

pcelldata

pcelldata [<addr>]

Examples:

"pcelldata *es:di"
Print cell data for cell at *es:di.

Prints data for a spreadsheet data.

If no address is given, *es:di is used.

See Also: content, pcelldeps.

pcelldeps

pcelldeps <filehan> [<addr>]

Examples:

"pcelldeps 4be0h *es:di"
print dependencies of cell in file 4be0h.

Prints dependencies for a cell in the spreadsheet.

If no address is given, *es:di is used.

See Also: content, pcelldata.

pclass

pclass [<address>]

Examples:

"pclass"
prints the class of *DS:SI
"pclass ^l4ac0h:001eh"
Print the class of the object at the given address.

Print the object's class.

  • The <address> argument is the address of the object to find the class of. This defaults to *DS:SI.

pdb

Produces useful information about a DBase block. For now, only information about the map block of the DBase file is produced. First arg H is the SIF_FILE or SIG_VM handle's ID. Second arg B is the VM block handle for which information is desired.

pdgroup

pdgroup [<flags>] [<patient>]

Examples:

"pdgroup"
Prints out all dgroup variables of the current patient
"pdgroup -e *lock*"
Prints out all dgroup variables matching expression "*lock*" of the current patient
"pdgroup term"
Prints out all dgroup variables of the patient "term"
"pdgroup -e *lock* term"
Prints out all dgroup variables matching expression "*lock*" of the patient "term"

Prints out the variables defined in dgroup.

  • Flags:
    • -e <expression>
    Only prints out the variables matching <expression>. When -e flag is used, expression argument must be supplied. The expression syntax is the same as the one specified in "string match" command.

See Also: print, pscope.

| Up: GEOS SDK TechDocs | Up | Prev: objmessagebrk ... | Next: pdisk ...


Swat Command Reference: pdisk ...

 | Up: GEOS SDK TechDocs | Up | Prev: patchin ... | Next: pgs ...

pdisk

pdisk <disk-handle>

Examples:

"pdisk 00a2h"
Prints information about the disk whose handle is 00a2h.

Prints out information about a registered disk, given its handle.

The Flags column is a string of single-character flags with the following meanings:

w
The disk is writable.
V
The disk is always valid, i.e. it's not removable.
S
The disk is stale. This is set if the drive for the disk has been deleted.
u
The disk is unnamed, so the system has made up a name for it.

See Also: diskwalk, drivewalk.

pdrive

pdrive <drive-handle>
pdrive <drive-name>
pdrive <drive-number>

Examples:

"pdrive si"
Print a description of the drive whose handle is in SI
"pdrive al"
Print a description of the drive whose number is in AL
"pdrive C"
Print a description of drive C

Provides the same information as "drivewalk," but for a single drive, given the offset to its DriveStatusEntry structure in the FSInfoResource.

This is intended for use by implementors of IFS drivers, as no one else is likely to ever see a drive handle.

See Also: drivewalk.

pdw

pdr <register pair>

Examples:

"pdw dxax"
"pdw dx.ax"
"pdw dx:ax"
Prints the dword value of register pair dx:ax

Prints the dword value of a register pair.

The first register is the high register while the second is the low register.

See Also: print.

penum

penum <type> <value>

Examples:

"penum FatalErrors 0"
print the first FatalErrors enumeration

Print an enumeration constant given a numerical value.

  • The <type> argument is the type of the enumeration.
  • The <value> argument is the value of the enumeration in a numerical format.

See Also: print, precord.

pevent

pevent <handle>

Examples:

"pevent 39a0h"
Print event with handle.

Print an event given its handle.

See Also: elist, eqlist, eqfind, erfind.

pflags

pflags

Prints the current machine flags (carry, zero, etc.).

See Also: setcc, getcc.

pfont

pfont [-c] [<address>]

Examples:

"pfont"
print bitmaps of the characters of the font in BX.
"pfont -c ^h1fd0h"
list the characters in the font at ^h1fd0h.

Print all the bitmaps of the characters in a font.

  • The "-c" flag causes pfont to list which characters are in the font and any special status (i.e. NOT BUILT).
  • The <address> argument is the address of the font. If none is specified then ^hbx is used.

See Also: fonts, pusage, pfontinfo.

pfontinfo

pfontinfo <font ID>

Examples: "pfontinfo FID_BERKELEY"

Prints font header information for a font. Also lists all sizes built.

  • The <font ID> argument must be supplied. If not known, use `fonts -u' to list all the fonts with their IDs.

See Also: fonts, pfont.

pgen

pgen <element> [<object>]

Examples:

"pgen GI_states @65"
print the states of object 65
"pgen GI_visMoniker"
print the moniker of the object at *DS:SI
"pgen GI_states -i"
print the states of the object at the implied grab

Print an element of the generic instance data.

  • The <element> argument specifies which element in the object to print
  • The <object> argument is the address to the object to print out. It defaults to *DS:SI and is optional. The `-i' flag for an implied grab may be used.

See Also: gentree, gup, pobject, piv, pvis.

| Up: GEOS SDK TechDocs | Up | Prev: patchin ... | Next: pgs ...


Swat Command Reference: pgs ...

 | Up: GEOS SDK TechDocs | Up | Prev: pdisk ... | Next: pinst ...

pgs

pgs <address>

Examples:

"pgs"
List the graphics string at DS:SI
"pgs ^hdi"
Lift the graphics string whose handle is in DI, starting at the current position.
"pgs -s ^hdi"
List the graphics string whose handle is in DI, starting at the beginning of the graphics string.
"pgs -l3 ^hdi"
List three elements of the graphics string whose handle is in DI, starting at the current position.

List the contents of a graphics string.

  • The <address> argument is the address of a graphics string. If none is specified then DS:SI is used as a pointer to a graphics string.
  • The passed address may also be the base of a gstate (e.g. "^hdi"). In this case, the gstring that is associated with the gstate will be printed.
  • The -s option can be used to specify that the gstring should be listed from the beginning of the string. By default, gstrings will be listed starting at the current position.
  • The -g option can be used to specify that the passed address is the address of a GrObj (GStringClass) object-- the gstring for that object will be listed.

See Also: pbitmap.

phandle

phandle <handle ID>

Examples:

"phandle 1a8ch"
print the handle 1a8ch

Print out a handle.

  • The <handle ID> argument is just the handle number. Make sure that the proper radix is used.
  • The size is in paragraphs.

See Also: hwalk, lhwalk.

pharray

pharray [<flags>] [<vmfile> <dirblk>]

Examples:

"pharray"
Print the huge array at ^vbx:di (header only)
"pharray dx cx"
Print the huge array at ^vdx:cx (header only)
"pharray -e"
Print the huge array at ^vbx:di and print the elements in the array
"pharray -tMyStruct"
Print the huge array at ^vbx:di and print the elements where the elements are of type MyStruct
"pharray -e3"
Print the huge array at ^vbx:di and print the third element
"pharray -h"
Print the header of the HugeArray at ^vbx:di, using the default header type (HugeArrayDirectory).
"pharray -hMyHeader"
Print the huge array at ^vbx:di (header only) where the header is of type MyHeader
"pharray -d"
Print the directory elements of a HugeArray
"pharray -e5 -l8"
Print 8 HugeArray elements starting with number 5

Print information about a huge array.

  • The flags argument can be any combination of the flags `e', `t', and `h'. The `e' flag prints all elements. If followed by a number "-e3", then only the third element is printed.

    • The `t' flag specifies the elements' type. It should be followed immediately by the element type. You can also use "gstring", in which case the elements will be interpreted as GString Elements.

    The `h' flag specifies the header type. It should be followed immediately by the element type. If no options are specified, then "-hHugeArrayDirectory" is used. If any other options are specified, then the printing of the header is disabled. So, for example, if you want both the header and the third element, use "-h -e3".

    The `d' flag specifies that the HugeArray directory entries should be printed out.

    The `l' flag specified how many elements to print.

    The `s' flag requests that a summary table be printed.

    All flags are optional and may be combined.

  • The address arguments are the VM file handle and the VM block handle for the directory block. If nothing is specified, then bx:di is used

pini

pini [<category>]

Examples:

"pini Lights Out"
Print out the contents of the Lights Out category in each .ini file
"pini"
Print out each currently loaded .ini file.

Provides you with the contents of the .ini files being used by the current GEOS session.

<category> may contain spaces and other such fun things. In fact, if you attempt to quote the argument (e.g. "pini {Lights Out}"), this will not find the category.

| Up: GEOS SDK TechDocs | Up | Prev: pdisk ... | Next: pinst ...


Swat Command Reference: pinst ...

 | Up: GEOS SDK TechDocs | Up | Prev: pgs ... | Next: pobjmon ...

pinst

pinst [<address>]

Examples:

"pinst"
print the last master level of instance data of the object at *DS:SI
"pinst *MyObject"
print the last master level of instance data of MyObject.
"pinst -i"
print the last master level of the windowed object at the mouse pointer.

Print out all the instance data to the last level of the object.

  • The <address> argument is the address of the object to examine. If not specified then pinst will use a default address. If you are debugging a C method, then the oself value will be used. Otherwise, *DS:SI is assumed to be an object.
  • This command is useful for classes you've created and you are not interested in the data in the master levels which pobject would display.
  • The following special values are accepted for <address>:
    • -a
    the current patient's application object
    -i
    the current "implied grab": the windowed object over which the mouse is currently located.
    -f
    the leaf of the keyboard-focus hierarchy
    -t
    the leaf of the target hierarchy
    -m
    the leaf of the model hierarchy
    -c
    the content for the view over which the mouse is currently located
    -kg
    the leaf of the keyboard-grab hierarchy
    -mg
    the leaf of the mouse-grab hierarchy
  • pinst prints out the same information as "pobj l".

See Also: pobject, piv.

piv

piv <master> <iv> [<address>]

Examples:

"piv Vis VCNI_viewHeight"
print Vis.VCNI_viewHeight at *DS:SI

This prints out the value of the instance variable specified.

  • The <master> argument expects the name of a master level. The name may be found using pobject to print the levels, and then using the name that appears after "master part:" and before the "_offset".
  • The <iv> argument expects the name of the instance variable to print.
  • The <address> argument is the address of the object to examine. If not specified then *DS:SI assumed to be an object.
  • This command is useful for when you know what instance variable you want to see but don't want to wade through a whole pobject command.

See Also: pobject, pinst.

Prints a keyboard map in assembly-language format.

plines

plines <start> [<obj-address>]

Examples:

"plines 12"
Print lines starting at line 12.
"plines 12 ^l6340h:0020h"
Print lines starting at line 12 of object at given address.

Print information about the lines in a text object.

The printed line-starts are not correct.

See Also: ptext.

plist

Prints out a list of structures stored in an lmem chunk. It takes two arguments, the structure type that makes up the list, and the lmem handle of the chunk. e.g. plist FontsInUseEntry ds:di

pncbitmap

pncbitmap <address> <width> <height> [<no space flag>]

Examples:

"pncbitmap *ds:si 64 64 t"
print the bitmap without spaces

Print out a one-bitdeep noncompacted bitmap.

  • The <address> argument is the address to the bitmap data.
  • The <width> argument is the width of the bitmap in pixels.
  • The <height> argument is the height of the bitmap in pixels.
  • The <no space flag> argument removes the space normally printed between the pixels. Anything (like `t') will activate the flag.

See Also: pcbitmap.

pnormal

pnormal [-v]

Examples:

"pnormal -v"
Print out verbose information about the current normal transfer item.

Prints out information about the current "normal" transfer item on the clipboard.

If you give the "-v" flag, this will print out the contents of the different transfer formats, rather than just an indication of their types.

See Also: pquick, print-clipboard-item.

pobjarray

pobjarray [<address>]

Examples:

"pobjarray"
Print the array of ODs at *ds:si.

Print out an array of objects.

See Also: pbody.

pobject

pobject [<address>] [<detail>]

Examples:

"pobj"
print the object at *ds:si from Gen down if Gen is one of its master levels; else, print all levels
"pobj *MyGenObject"
print MyGenObject from Gen down
"pobj Gen"
print the Gen level for the object at *ds:si
"pobj last"
print the last master level for the object at *ds:si
"pobj *MyObject"
all print all levels of MyObject
"pobj -i sketch"
print the master level headings of the windowed object at the mouse pointer
"pobj *MyObject FI_foo"
print the FI_foo instance variable for MyObject
"pobj HINT_FOO"
print the HINT_FOO variable data entry for the object at *ds:si
"pobj v"
print the variable data for the object at *ds:si

Print all or part of an object's instance and variable data.

  • The <address> argument is the address of the object to examine. If not specified then oself is used, unless the current function is written in assembly, in which case *DS:SI .
  • The following flag values are accepted in lieu of an address:
    • -a
    the current patient's application object
    -i
    the current "implied grab": the windowed object over which the mouse is currently located.
    -f
    the leaf of the keyboard-focus hierarchy
    -t
    the leaf of the target hierarchy
    -m
    the leaf of the model hierarchy
    -c
    the content for the view over which the mouse is currently located
    -kg
    the leaf of the keyboard-grab hierarchy
    -mg
    the leaf of the mouse-grab hierarchy
  • The detail argument specifies what information should be printed out about the object. If none is specified, all levels of the object from the Gen level down will be printed if Gen is one of the object's master levels; else, the whole object will be printed.
  • The following values are accepted for detail :
    • all (or a )
    all master levels
    last (or l )
    last master level only
    sketch (or s )
    master level headings only
    vardata (or v )
    vardata only
    a master level name
    an instance variable name
    a variable data entry name

See Also: pinst, piv, pvardata.

| Up: GEOS SDK TechDocs | Up | Prev: pgs ... | Next: pobjmon ...


Swat Command Reference: pobjmon ...

 | Up: GEOS SDK TechDocs | Up | Prev: pinst ... | Next: print-cell ...

pobjmon

pobjmon [<address>] [<text only>]

Examples:

"pobjmon"
print the VisMoniker from the gentree object at *DS:SI
  • The <address> argument is the address of an object with a VisMoniker. If none is specified then *DS:SI is used.
  • The <text only> argument returns a shortened description of the structure. To set it use something other than `0' for the second argument.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.

See Also: pvismon, pobject, vistree, gup, gentree, impliedgrab, systemobj.

pod

pod <address>

Examples:

"pod ds:si"

Print in output descriptor format (^l<handle>:<chunk>) the address passed.

The address argument is the address of an object.

ppath

ppath (current|docClip|winClip) [<gstate>]

Examples:

"ppath"
print the current path of the GState in ^hdi
"ppath docClip ^hgstate"
print the document clip path of the GState with handle gstate .
"ppath winClip ds"
print the window clip path of the GState in the DS register.

Print the structure of a path.

Unique abbreviations for the path to be printed are allowed.

pquick

pquick [-v]

Examples:

"pquick -v"
Print out verbose information about the current quick transfer item.

Prints out information about the current "quick" transfer item on the clipboard.

If you give the "-v" flag, this will print out the contents of the different transfer formats, rather than just an indication of their types.

See Also: pnormal, print-clipboard-item.

precord

precord <type> <value> [<silent>]

Examples:

"precord GSControl c0h"
print the GSControl record with the top two bits set

Print a record using a certain value.

  • The <type> argument is the type of the record.
  • The <value> argument is the value of the record.
  • The <silent> argument will suppress the text indicating the record type and value. This is done by passing a non zero value like `1'. This is useful when precord is used by other functions.

See Also: print, penum.

preg

preg [-g] <addr>

Examples:

"preg *es:W_appReg"
Prints the application-defined clipping region for the window pointed to by es.
"preg -g ds:si"
Prints a "graphical" representation of the region beginning at ds:si

Decodes a graphics GEOS region and prints it out, either numerically, or as a series of x's and spaces.

  • This command can deal with parameterized regions. When printing a parameterized region with the -g flag, the region is printed as if it were unparameterized, with the offsets from the various PARAM constants used as the coordinates.
  • If no address is given, this will use the last-accessed address (as the "bytes" and "words" commands do). It sets the last-accessed address, for other commands to use, to the first byte after the region definition.

print

print <expression>

Examples:

"print 56h"
print the constant 56h in various formats
"print ax - 10"
print ax less 10 decimal
"print ^l31a0h:001eh"
print the absolute address of the pointer

Print the value of an expression.

  • The <expression> argument is usually an address that has a type or that is given a type by casting and may span multiple arguments. The contents of memory of the given type at that address is what's printed. If the expression has no type, its offset part is printed in both hex and decimal. This is used for printing registers, for example.
  • The first argument may contain the following flags (which start with `-'):
    • x
    integers (bytes, words, dwords if dwordIsPtr false) printed in hex
    d
    integers printed in decimal
    o
    integers printed in octal c bytes printed as characters (byte arrays printed as strings, byte variables/fields printed as character followed by integer equivalent)
    C
    bytes treated as integers
    a
    align structure fields
    A
    Don't align structure fields
    p
    dwords are far pointers
    P
    dwords aren't far pointers
    r
    parse regions
    R
    don't try to parse regions
  • These flags operate on the following Tcl variables:
    • intFormat
    A printf format string for integers.
    bytesAsChar
    Treat bytes as characters if non-zero.
    alignFields
    Align structure fields if non-zero.
    dwordIsPtr
    DWords are far pointers if non-zero.
    noStructEnum
    If non-zero, doesn't print the "struct", "enum" or "record" before the name of a structured/enumerated type -- just gives the type name.
    printRegions
    If non-zero, prints what a Region points to (bounds and so on).
    condenseSpecial
    If non-zero, condense special structures (Rectangles, OutputDescriptors, ObjectDescriptors, TMatrixes and all fixed-point numbers) to one line.
  • This does not print enumerations. Use penum for that.

See Also: precord, penum, _print.

| Up: GEOS SDK TechDocs | Up | Prev: pinst ... | Next: print-cell ...


Swat Command Reference: print-cell ...

 | Up: GEOS SDK TechDocs | Up | Prev: pobjmon ... | Next: printRegions ...

print-cell

print-cell [row column <cfp ds:si>]

Examples:

"print-cell 1 1"
print the cell <1,1>
"print-cell 1 2 *ds:si"
print the cell <1,2> given *DS:SI

Print information about a cell

See Also: print-row, print-row-block, print-cell-params, print-column-element.

print-cell-params

print-cell-params [<address>]

Examples:

"print-cell-params"
print the CellFunctionParameters at ds:si.
"print-cell-params ds:bx"
print the CellFunctionParameters at ds:bx.

Print a CellFunctionParameters block.

See Also: print-row, print-column-element, print-row-block, print-cell.

print-clipboard-item

print-clipboard-item [-v] <vmfile> <vmblock>
print-clipboard-item [-v] <memhandle>
print-clipboard-item [-v] <addr>

Examples:

"print-clipboard-item bx"
Print out info about the transfer item whose memory handle is in the BX register.

Prints out information about a transfer item.

  • If you give the "-v" flag, this will print out the contents of the different transfer formats, rather than just an indication of their types.
  • The -v flag will not work unless the transfer item is in a VM file.

See Also: pnormal, pquick.

print-column-element

print-column-element [<address>]

Examples:

"print-column-element"
Print the ColumnArrayElement at ds:si.
"print-column-element ds:bx" print the ColumnArrayElement at ds:bx

Print a single ColumnArrayElement at a given address.

print-db-group

print-db-group file group

Examples:

"print-db-group ax bx"
print the group at bx/ax.

Print information about a dbase group block.

See Also: print-db-item.

print-db-item

print-db-item file group item

Examples:

"print-db-item bx ax di"
print the item at bx/ax/di

Print information about a single dbase item

See Also: print-db-group.

print-eval-dep-list

print-eval-dep-list [<addr>]

Examples:

"print-eval-dep-list es:0"
Print dependency list at ES:0.

Prints a dependency list used for evaluation.

See Also: content, pcelldeps.

printNamesInObjTrees

var printNamesInObjTrees (0|1)

Examples:

"var printNamesInObjTrees 1"
Sets "gentree," "vistree," etc. commands to print object names (where available).

Determines whether object names are printed (where available) rather than class names when using the following commands: vistree, gentree, focus, target, model, mouse, keyboard.

The default value for this variable is zero.

See Also: gentree, vistree, focus, target, model, mouse, keyboard.

print-obj-and-method

print-obj-and-method <handle> <chunk> <message> [<cx> [<dx>
                                                   [<bp> [<class>]]]]

Examples:

"print-obj-and-method [read-reg bx] [read-reg si]"
Prints a description of the object ^lbx:si with the value stored and a hex representation.
"print-obj-and-method $h $c $m [read-reg cx] [read-reg dx] [read-reg bp]"
Prints a description of the object ^l$h:$c and the name of the message whose number is in $c. This is followed by the three words of data in cx, dx, and bp.

Prints a nicely formatted representation of an object, with option message, register data, label, hex address, & carriage return. The class indication may also be overriden.

  • You may specify anywhere from 0 to 5 arguments after the message number. These are interpreted as the value of the message, the registers CX, DX and BP, and the symbol token of the class to print, respectively.
  • All arguments must be integers, as this is expected to be called by another procedure, not by the user, so the extra time required to call getvalue would normally be wasted. (The user should call pobj , gup , or other such functions for this sort of print out.)

See Also: mwatch, map-method, objwatch.

| Up: GEOS SDK TechDocs | Up | Prev: pobjmon ... | Next: printRegions ...


Swat Command Reference: printRegions ...

 | Up: GEOS SDK TechDocs | Up | Prev: print-cell ... | Next: ptext ...

printRegions

var printRegions [(0|1)]

Examples:

"var printRegions 1"
If a structure contains a pointer to a region, "print" will attempt to determine its bounding box.

Controls whether "print" parses regions to find their bounding rectangle.

The default value for this variable is one.

See Also: print, condenseSpecial.

print-row

print-row [<address *DS:SI>]

Examples:

"print-row"
print the row at *DS:SI
"print-row ds:si"
print the row at DS:SI

Print a single row in the cell file given a pointer to the row.

See Also: print-column-element, print-cell-params, print-row-block, print-cell.

print-row-block

print-row-block [<address ds>]

Examples:

"print-row-block"
print the row-block at DS:0
"print-row-block es"
print the row-block at ES:0

Print a row-block.

See Also: print-row, print-cell-params, print-column-element, print-cell.

printStop

This variable controls how the current machine state is printed each time the machine comes to a complete stop. Possible values:

asm
Print the current assembly-language instruction, complete with the values for the instruction operands.
src
Print the current source line, if it's available. If the source line is not available, the current assembly-language instruction is displayed as above.
why
Print only the reason for the stopping, not the current machine state. "asm" and "src" modes also print this.
nil
Don't print anything.

procmessagebrk

procmessagebrk [<handle>]

Examples:

"procmessagebrk MyObj"
break whenever a message is sent to MyObj
"procmessagebrk"
stop intercepting messages

Break whenever a message is sent to a particular process via ObjMessage.

  • The <handle> argument is the handle to a process to watch for messages being sent to it. If no argument is specified then the watching is stopped. The process' handle may be found by typing "ps -p". The process's handle is the number before the process's name.
  • This command breaks whenever a message is sent (before they get on the message queue. This enables one to track identical messages to a process which can be removed.

See Also: objwatch, mwatch, objmessagebrk, pobject.

ps

ps [<flags>]

Examples:

"ps -t"
list all threads in GEOS.

Print out GEOS' system status.

The flags argument may be one of the following:

-t
Prints out info on all threads. May be followed by a list of patients whose threads are to be displayed.
-p
Prints out info on all patients. May be followed by a list of patients to be displayed.
-h
Prints out info on all handles. May be followed by a list of patients whose handles are to be displayed.
The default is `-p'.

See Also: switch, sym-default.

pscope

pscope [<scope-name> [<sym-class>]]

Examples:

"pscope WinOpen"
Prints out all the local labels, variables, and arguments within the WinOpen() procedure

This prints out all the symbols contained in a particular scope.

  • This can be useful when you want to know just the fields in a structure, and not the fields within those fields, or if you know the segment in which a variable lies, but not its name. Admittedly, this could be overkill.
  • sym-class can be a list of symbol classes to restrict the output. For example, "pscope Filemisc proc" will print out all the procedures within the Filemisc resource.

See Also: whatis, locals.

psize

psize <structure>

Examples: "psize FontsInUseEntry"

Print the size of the passed structure.

pssheet

pssheet [-isSfrcvd] <address>

Examples:

"pssheet -s ^l3ce0h:001eh"
print style attributes.
"pssheet -f -i 94e5h:0057h"
print file info from instance data.

Prints out information about a spreadsheet object.

  • If you are in the middle of debugging a spreadsheet routine and have a pointer to the Spreadsheet instance, the "-i" flag can be used to specify the object using that pointer.
  • If you simply have the OD of the spreadsheet object, use that.
  • Alternatively, you can do:
 pssheet <flags> [targetobj]

See Also: content, targetobj.

psup

psup [<object>]

Examples:

"psup"
print superclasses of object at *ds:si.
"psup -i"
print superclasses of object under mouse.
"psup ^l4e10h:20h"
print superclasses of object at ^l4e10h:20h.

Prints superclasses of an object.

If no object is specified, *ds:si is used.

See Also: is-obj-in-class.

| Up: GEOS SDK TechDocs | Up | Prev: print-cell ... | Next: ptext ...


Swat Command Reference: ptext ...

 | Up: GEOS SDK TechDocs | Up | Prev: printRegions ... | Next: pvismon ...

ptext

ptext [<options>] <addr>

Examples:

"ptext"
Prints the text in the object for the method being executed in the current stack frame.

Prints out the text and related structures for a text object

The flag may be one or more of the following:

- c
Print out the char attr structures.
- e
Print out elements in addition to runs.
- f < field >
Print out given field of each element (default is Meta part).
- g
Print out graphics structures.
- l
Print out line and field structures.
- p
Print out para attr structures.
- r
Print out region attr structures.
- s
Print out style structures.
- t
Print out type structures.
- E
Limit printout to just the elements of whatever arrays are requeste. Do not attempt to print out associated text.
- N
Print out associated names.
- R
Print out full region descriptions.

pthread

pthread <id>

Examples:

"pthread 16c0h"
Prints information about the thread whose handle is 16c0h.

Provides various useful pieces of information about a particular thread including its current priority and its current registers.

<id> is the thread's handle ID, as obtained with the "ps -t" or "threadstat" command.

See Also: threadstat.

ptimer

ptimer <handle>

Examples:

"ptimer bx"
Print out information about the timer whose handle is in the BX register.

Prints out information about a timer registered with the system: when it will fire, what it will do when it fires, etc.

<handle> may be a variable, register, or constant.

See Also: twalk, phandle.

ptrans

ptrans [<flags>] [<address>]

Examples:

"ptrans"
print the normal transform for the object at *ds:si.
"ptrans -s"
print the sprite transform for the GrObj object at *ds:si.
"ptrans ^lbx:cx"
print the normal transform for the object whose OD is ^lbx:cx.

Prints the ObjectTransform data structure as specified.

  • The -s flag can be used to print the "sprite" transform (the "sprite" is the shape's outline which is drawn to give feedback to the user when said user is moving/rotating/etc. the GrObj).
  • <address> defaults to *ds:si

See Also: pobject.

ptreg

ptreg <start> [<obj-addr>]

Examples:

"ptreg 12"
Print lines for region 12
"ptreg 12 ^lcx:dx"
Print lines for region 12 of object ^lcx:dx

Print information about the lines in a region.

See Also: ptext.

pusage

pusage [<address>]

Examples:

"pusage"
print the usage of characters in the font

List the characters in a font and when they were last used.

  • The <address> argument is the address of a font. If none is given then ^hbx is used.

See Also: fonts, pfont, pfontinfo, plist.

pvardata

pvardata [<entry>]

Examples:

"pvardata ds:si"
Prints vardata of object at *ds:si
"pvardata -i"
Prints vardata of object with implied grab.
  • The address argument is the address of an object with variable data. The default is *ds:si.

pvardentry

pvardentry <address> <object>

Examples:

"pvardentry ds:bx *ds:si"
  • The address argument is the address of a variable data entry in an object's variable data storage area. The default is ds:bx.
  • The <object> argument is required to determine the name of the tag for the entry, as well as the type of data stored with it.

pvis

pvis <element> [<object>]

Examples:

"pvis VI_bounds @65"
print the bounds of object 65
"pvis VI_optFlags"
print the flags of the object at *DS:SI
"pvis VI_attrs -i"
print the attributes of the object at the implied grab

Print an element of the visual instance data.

  • The <element> argument specifies which element in the object to print
  • The <object> argument is the address to the object to print out. It defaults to *DS:SI and is optional. The `-i' flag for an implied grab may be used.

See Also: vistree, vup, pobject, piv, pgen.

| Up: GEOS SDK TechDocs | Up | Prev: printRegions ... | Next: pvismon ...


Swat Command Reference: pvismon ...

 | Up: GEOS SDK TechDocs | Up | Prev: ptext ... | Next: read-reg ...

pvismon

pvismon [<address>] [<text only>]

Examples:

"pvismon"
print the moniker at *DS:SI
"pvismon -i 1"
print a short description of the implied grab object.

Print a visual moniker structure at an absolute address.

  • The <address> argument is the address to an object in the visual tree. This defaults to *DS:SI. The `-i' flag for an implied grab may be used.
  • The <text only> argument returns a shortened description of the structure. Pass a non-zero value to turn on this flag.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.

See Also: pobjmon, pobject, vistree, gup, gentree, impliedgrab, systemobj.

pvmb

Prints out the VMBlockHandle for a VM block given the file handle H and the VM block handle B .

pvmt

pvmt [-p] [-a] [-s] [-c] (<handle> | <segment>)

Examples:

"pvmt bx"
Print out all used blocks for the open VM file whose file handle is in BX.
"pvmt -as ds"
Print out all blocks for the open VM file the segment of whose header block is in DS.

Prints out a map of the VM block handles for a VM file.

  • The -p flag will only print out blocks that have the Preserve flag set. Useful for examining object blocks in GeoCalc files, for example.
  • The -a flag causes pvmt to print out all block handles, not just those that have been allocated. The other two types of block handles are "assigned" (meaning they're available for use, but currently are tracking unused space in the file) and "unassigned" (they're available for use).
  • The -s indicates the final argument is a segment, not a file handle. This is used only if you're inside the VM subsystem of the kernel.
  • The -c flag requests a count of the different types of blocks at the end of the printout.
  • The blocks are printed in a table with the following columns:
    • han
    VM block handle (in hex)
    flags D if the block is dirty,
    C if the block is clean,
    - if the block is non-resident,
    L if the block is LMem,
    B if the block has a backup,
    P if the preserve handle bit is set for the block,
    ! if the block is locked
    memhan
    Associated memory handle. Followed by "(d)" if the memory for the block was discarded but the handle retained. Followed by (s) if the memory has been swapped out.
    block type
    The type of block:
    VMBT_USED a normal in-use block,
    VMBT_DUP an in-use block that has been backed up or allocated since the last call to VMSave()
    VMBT_BACKUP a place-holder to keep track of the previous version of a VMBT_DUP block. The uid is the VM block handle to which the file space used to belong.
    VMBT_ZOMBIE a block that has been freed since the last VMSave() . The handle is preserved in case of a VMRevert() (a VMBT_BACKUP block retains the file space).
    uid
    The "used ID" bound to the block.
    size
    Number of bytes allocated for the block in the file.
    pos
    The position of those bytes in the file.

See Also: pgs.

pvsize

pvsize [<object>]

Examples:

"pvsize"
print the dimensions of the visual object at *ds:si.

Print out the dimensions of a visual object.

  • The object argument is the address to the object to print out. It defaults to *ds:si and is optional. The `-i' flag for an implied grab may be used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.

pwd

pwd

Examples: "pwd"

Prints the current working directory for the current thread.

See Also: dirs, stdpaths.

quit

quit [<options>]

Examples:

"quit cont"
continue GEOS and quit swat
"quit det"
detach from the PC and quit swat.

Stop the debugger and exit.

  • The <option> argument may be one of the following:
    continue : continue GEOS and exit swat;
    leave : keep GEOS stopped and exit swat.
  • Anything else causes swat to detach and exit.

See Also: detach.

read-char

read-char [<echo>]

Examples:

"read-char 0"
Read a single character from the user and don't echo it.

Reads a character from the user.

If <echo> is non-zero or absent, the character typed will be echoed.

See Also: read-line.

read-line

read-line [<isTcl> [<initial input> [<special chars>]]]

Examples:

"read-line"
reads a single line of text.
"read-line 1"
reads a Tcl command.
"read-line 1 {go}"
reads a Tcl command that starts with "go "
"read-line 1 {} {\e\4}"
reads a Tcl command, considering escape and control-d cause for immediate return, regardless of whether braces and brackets are balanced

Reads a single line of input from the user. If optional argument is non-zero, the line is interpreted as a Tcl command and will not be returned until all braces/brackets are balanced. The final newline is stripped. Optional second argument is input to be placed in the buffer first. This input must also be on-screen following the prompt, else it will be lost.

  • If <isTcl> is non-zero, the input may span multiple lines, as read-line will not return until all braces and brackets are properly balanced, according to the rules of Tcl. This behavior may be overridden by the <special chars> argument.
  • If <initial input> is given and non-empty, it is taken to be the initial contents of the input line and may be edited by the user just as if s/he had typed it in. The string is not automatically displayed; that is up to the caller.
  • <special chars> is an optional string of characters that will cause this routine to return immediately. The character that caused the immediate return is left as the last character of the string returned. You may use standard backslash escapes to specify the characters. This will return even if the user is entering a multi-line Tcl command whose braces and brackets are not yet balanced.
  • The user's input is returned as a single string with the final newline stripped off.
| Up: GEOS SDK TechDocs | Up | Prev: ptext ... | Next: read-reg ...


Swat Command Reference: read-reg ...

 | Up: GEOS SDK TechDocs | Up | Prev: pvismon ... | Next: ret ...

read-reg

read-reg <register>

Examples:

"read-reg ax"
return the value of AX
"read-reg CC"
return the value of the conditional flags

Return the value of a register in decimal.

  • The <register> argument is the two letter name of a register in either upper or lower case.

See Also: frame, assign, setcc, clrcc.

regs

regs

Print the current registers, flags, and instruction.

See Also: assign, setcc, clrcc, read-reg.

regwin

regwin [off]

Examples: "regwin"
"regwin off"

Turn the continuous display of registers on or off.

  • If you give the optional argument "off", you will turn off any active register display.
  • If you give no argument, the display will be turned on.
  • Only one register display may be active at a time.

See Also: display.

repeatCommand

var repeatCommand <string>

Examples:

"var repeatCommand [list foo nil]"
Execute the command "foo nil" if the user just hits <Enter> at the next command prompt.

This variable holds the command Swat should execute if the user enters an empty command. It is used by all the memory-referencing commands to display the next chunk of memory, and can be used for other purposes as well.

  • repeatCommand is emptied just before top-level-read returns the command the interpreter should execute and must be reset by the repeated command if it wishes to continue to be executed when the user just hits <Enter>.
  • The text of the current command is stored in lastCommand , should you wish to use it when setting up repeatCommand .

See Also: target, focus, mouse, keyboard.

require

require <name> [<file>]

Examples:

"require fmtval print"
Ensure the procedure "fmtval" is defined, loading the file "print.tcl" if it is not.

This ensures that a particular function, not normally invoked by the user but present in some file in the system library, is actually loaded.

If no <file> is given, a file with the same name (possibly suffixed ".tcl") as the function is assumed.

See Also: autoload.

resize

resize <window> <numLines>

Examples:

"resize varwin 5"
Resizes the varwin to 5 lines.
"resize va 5"
Resizes the varwin to 5 lines.

Resizes the source window or a varwin or localwin.

  • The window argument is the name of the window to be resized, or a unique abbreviation of that name. Possible names are: "varwin", "localwin", "view", "doc", and "srcwin"; the last three all refer to the source window.
  • The numLines argument is the desired window size.
  • This command will not resize a flagwin, regwin, or framewin, as the optimal size of those windows does not vary.

restore-state

restore-state

Examples:

"restore-state"
Set all registers for the current thread to the values saved by the most recent save-state.

Pops all the registers for a thread from the internal state stack.

  • This is the companion to the "save-state" command.
  • All the thread's registers are affected by this command.

See Also: save-state.

| Up: GEOS SDK TechDocs | Up | Prev: pvismon ... | Next: ret ...


Swat Command Reference: ret ...

 | Up: GEOS SDK TechDocs | Up | Prev: read-reg ... | Next: scope ...

ret

ret [<function name>]

Examples: "ret"
"ret ObjMessage"

Return from a function and stop.

  • The <function name> argument is the name of a function in the patient's stack after which swat should stop. If none is specified then Swat returns from the current function.
  • The function returned from is the first frame from the top of the stack which calls the function (like the "finish" command).
  • This command does not force a return. The machine continues until it reaches the frame above the function.

See Also: finish, backtrace.

return-to-top-level

return-to-top-level

Examples:

"return-to-top-level"
Returns to the top-level interpreter.

Forces execution to return to the top-level interpreter loop, unwinding intermediate calls (protected commands still have their protected clauses executed, but nothing else is).

See Also: top-level, protect.

rs

rs

Examples:

"rs"
restart GEOS without attaching

Restart GEOS without attaching.

See Also: att, attach.

run

run [<patient-name>]

Examples:

"run uki"
Run the application with patient name "uki".
"run -e uki"
run EC Uki
"run -n uki"
run non-EC Uki
"run -p games\ukiec.geo "
run games\ukiec.geo
"run"
run the default patient, as specified by the patient-default command.

"Runs" an application by loading it via a call to UserLoadApplication() and stopping when the app reaches the GenProcess handler for MSG_META_ATTACH . Return patient created, if any (In the examples shown, this would be "uki").

  • May be used even if stopped inside the loader, in which case GEOS will be allowed to continue starting up, and the specified app run after GEOS is Idle.
  • If the machine stops for any other reason other than the call's completion, you are left wherever the machine stopped.

See Also: patient-default, send, spawn, switch.

rwatch

rwatch [(on|off)]

Examples:

"rwatch on"
Watch text-recalculation as it happens
"rwatch off"
Turn output off
"rwatch"
See what the status is

Displays information about text recalculation. Specifically designed for tracking bugs in the rippling code.

See Also: ptext.

save

save (<#lines>|<filename>)

Examples:

"save 500"
Save the last 500 lines that scroll off the screen.
"save /dumps/puffball"
Save the contents of the entire scroll buffer to the file "puffball".

Controls the scrollback buffer Swat maintains for its main command window.

  • If the argument is numeric, it sets the number of lines to save (the default is 1,000).
  • If the argument is anything else, it's taken to be the name of a file in which the current buffer contents (including the command window) should be saved. If the <filename> is relative, it is taken relative to the directory in which the executable for the patient to which the current stack frame's function belongs is located. If the file already exists, it is overwritten.

save-state

save-state

Examples:

"save-state"
Push the current register state onto the thread's state stack.

Records the state of the current thread (all its registers) for later restoration by "restore-state".

  • Swat maintains an internal state stack for each thread it knows, so calling this has no effect on the target PC.
  • This won't save any memory contents, just the state of the thread's registers.

See Also: restore-state, discard-state.

sbwalk

sbwalk [<patient>]

Examples:

"sbwalk"
list the saved blocks of the current patient.
"sbwalk geos"
list the saved blocks of the GEOS patient.

List all the saved blocks in a patient.

  • The <patient> argument is any GEOS patient. If none is specified then the current patient is used.
| Up: GEOS SDK TechDocs | Up | Prev: read-reg ... | Next: scope ...


Swat Command Reference: scope ...

 | Up: GEOS SDK TechDocs | Up | Prev: ret ... | Next: showcalls ...

scope

scope [<scope-name>]

Examples:

"scope"
Returns the name of the current auxiliary scope.

This changes the auxiliary scope in which Swat looks first when trying to resolve a symbol name in an address expression.

  • This command isn't usually typed by users, but it is the reason you can reference local labels after you've listed a function unrelated to the current one.
  • You most likely want to use the set-address Tcl procedure, rather than this command.
  • If <scope-name> is "..", the auxiliary scope will change to be the lexical parent of the current scope.

See Also: set-address, addr-parse, whatis.

screenwin

screenwin

Print the address of the current top-most screen window.

send

send [-enpr] <geode-name>

Examples:

"send icon"
send EC Icon Editor if running in EC mode; send non-EC Icon Editor if running in non-EC mode.
"send -r icon"
send appropriate icon editor, then run it. (See documentation for "run" above.)
"send -e icon"
send EC Icon editor.
"send -n icon"
send non-EC Icon Editor
"send -p c:/pcgeos/appl/icon/icon.geo"
send c:/pcgeos/appl/icon/icon.geo
"send"
send the default patient (as set by the patient-default command).

Send a geode from the host to target machine.

send-file

send-file <file> <targfilename>

Examples:

"send-file /pcgeos/appl/sdk/mess1/mess1.geo WORLD/soundapp.geo"
Send the mess1.geo file on the host machine to the WORLD directory of the host machine, where it will be called soundapp.geo .

Sends a file from the host machine to the target.

set-address

Set the last-accessed address recorded for memory-access commands. Single argument is an address expression to be used by the next memory-access command (except via <return>).

setcc

setcc <flag> [<value>]

Examples:

"setcc c"
set the carry flag
"setcc z 0"
clear the zero flag

Set a flag in the computer.

  • The first argument is the first letter of the flag to set. The following is a list of the flags:
    • t
    trap
    i
    interrupt enable
    d
    direction
    o
    overflow
    s
    sign
    z
    zero
    a
    auxiliary carry
    p
    parity
    c
    carry
  • The second argument is the value to assign the flag. It defaults to one but may be zero to clear the flag.

See Also: clrcc, compcc.

set-masks

set-masks <mask1> <mask2>

Examples:

"set-masks 0xff 0xff"
Allow no hardware interrupts to be handled while the machine is stopped.

Sets the interrupt masks used while the Swat stub is active. Users should use the "int" command.

  • <mask1> is the mask for the first interrupt controller, with a 1 bit indicating the interrupt should be held until the stub returns the machine to GEOS. <mask2> is the mask for the second interrupt controller.
  • These masks are active only while the machine is executing in the stub, which usually means only while the machine is stopped.

See Also: int.

set-repeat

Sets the command to be repeated using a template string and the lastCommand variable. The variables $0...$n substitute the fields 0...n from the lastCommand variable. The final result is placed in repeatCommand which will be executed should the user type Enter.

set-startup-ec

set-startup-ec [<args>]

Examples:

"set-startup-ec +vm"
turn on VM error checking when starting up
"set-startup-ec none"
turn off all ec code when starting up

Executes the "ec" command upon startup, to allow one to override the default error checking flags.

See Also: ec.

sftwalk

sftwalk

Examples: "sftwalk"

Print the system file table out by blocks.

  • This is different than sysfiles in that it shows less details of the files and instead shows where the SFT blocks are and what files are in them.

See Also: sysfiles, geosfiles, fwalk.

| Up: GEOS SDK TechDocs | Up | Prev: ret ... | Next: showcalls ...


Swat Command Reference: showcalls ...

 | Up: GEOS SDK TechDocs | Up | Prev: scope ... | Next: smatch ...

showcalls

showcalls [<flags>] [<args>]

Examples:

"showcalls -o"
show all calls using ObjMessage and ObjCall*
"showcalls -ml"
show all calls changing global and local memory
"showcalls"
stop showing any calls

Display calls to various parts of GEOS.

  • The <flags> argument determines the types of calls displayed. Multiple flags must all be specified in the first argument such as "showcalls -vl". If no flags are passed then showcalls stops watching. The flags may be any of the following:
    • -p
    Modify all other flags to work for the current patient only
    -a
    Show calls associated with attaching, detaching, state-saving, restoring from state
    -b
    Monitors builds of Vis objects
    -d
    Show dispatching of threads
    -e
    Show FOCUS, TARGET, MODAL . exclusive grabs & releases
    -f
    Show certain file operations
    -g
    Show geometry manager resizing things (all sizes in hex)
    -h
    Show all hierarchical exclusive grabs and releases (associated with focus, target, and model).
    -i
    Show call far calls made to movable routines.
    -l
    Show local memory create, destroy, relocate
    -m
    Show global memory alloc, free, realloc
    -o
    Show ObjMessage() and ObjCall ... ()
    -s
    Monitors shutdown: MSG_DETACH , DETACH_COMPLETE , ACK , DETACH_ABORT
    -t
    Show clipboard activity
    -v
    Show video driver calls
    -w
    Show WinOpen() , WinClose() , WinMoveResize() , WinChangePriority() .
    -F
    Show file-change notifications produced by the system.
    -G
    Show GrCreateState() and GrDestroyGState() .
    -H
    Show heap space allocation (using .gp heapspace values, not actual allocation values)
    -I
    Show invalidation mechanism at work.
    -L
    Show library loading calls
    -N
    Show navigation calls (between fields, and between windows)
    -S
    Show stack borrowing activity.
    -T
    Show text-object related information.
    -V
    Show loading and unloading of resources to and from statefiles during shutdown.
  • The<args> argument is used to pass values for some of options.

See Also: mwatch, objwatch.

showMethodNames

var showMethodNames

If this variable is non-zero, Swat prints out the names of the method in the AX register when unassembling a message call.

skip

skip [<number of instructions>]

Examples:

"skip"
skip the current instruction
"skip 6"
skip the next six instructions

Skip one or more instructions.

  • The <number of instructions> argument defaults to one if not specified.

See Also: istep, sstep, patch.

sleep

sleep <seconds>

Examples:

"sleep 5"
Pauses Swat for 5 seconds.

This pauses Tcl execution for the given number of seconds, or until the user types Ctrl-C.

  • Messages from the PC continue to be processed, so a FULLSTOP event will be dispatched if the PC stops, but this command won't return until the given length of time has elapsed.
  • <seconds> is a real number, so "1.5" is a valid argument.
  • Returns non-zero if it slept for the entire time, or 0 if the sleep was interrupted by the user.

slist

slist [<args>]

Examples:

"slist"
list the current point of execution
"slist foo.asm::15"
list foo.asm at line 15
"slist foo.asm::15,45"
list foo.asm from lines 15 to 45

List source file lines in swat.

  • The args argument can be any of the following:
    • <address>
    Lists the 10 lines around the given address
    <line>
    Lists the given line in the current file
    <file>::<line>
    Lists the line in the given file
    <line1>,<line2>
    Lists the lines between line1 and line2, inclusive, in the current file
    <file>::<line1>,<line2>
    Lists the range from <file>
  • The default is to list the source lines around CS:IP.

See Also: listi, istep, regs.

| Up: GEOS SDK TechDocs | Up | Prev: scope ... | Next: smatch ...


Swat Command Reference: smatch ...

 | Up: GEOS SDK TechDocs | Up | Prev: showcalls ... | Next: stdpaths ...

smatch

Look for symbols of a given class by pattern. First argument <pattern> is the pattern for which to search (it's a standard Swat pattern using shell wildcard characters). Optional second argument <class> is the class of symbol for which to search and is given directly to the "symbol match" command. Defaults to "any".

sort

sort [-r] [-n] [-u] <list>

Examples:

"sort -n $ids"
Sorts the list in $ids into ascending numeric order.

This sorts a list into ascending or descending order, lexicographically or numerically.

  • If "-r" is given, the sort will be in descending order.
  • If "-u" is given, duplicate elements will be eliminated.
  • If "-n" is given, the elements are taken to be numbers (with the usual radix specifiers possible) and are sorted accordingly.
  • The sorted list is returned.

See Also: map, foreach, mapconcat.

spawn

spawn <processName> [<addr>]

Set a temporary breakpoint in a not-yet-existent process/thread, waiting for a new one to be created. First argument is the permanent name of the process to watch for. Second argument is an address expression specifying where to place the breakpoint. If no second argument is present, the machine will be stopped and Swat will return to the command level when the new thread is spawned by GEOS.

  • This can also be used to catch the spawning of a new thread.
  • If the machine stops before the breakpoint can be set, you'll have to do this again.

srcwin

srcwin <numLines>

Examples:

"srcwin 6"
Show 6 lines of source context around CS:IP
"srcwin 0"
Show no source lines, i.e. turn the display off.

Set the number of lines of source code to be displayed when the target machine stops.

  • Only one source display may be active at a time.

See Also: display, regwin.

sstep

sstep [<default command>]

Examples:

"ss"
enter source step mode
"sstep n"
enter source step mode, <ret> does a next command

Step through the execution of the current patient by source lines. This is THE command for stepping through high-level (e.g., C) code.

  • The <default> command argument determines what pressing the <Return> key does. By default, <Return> executes a step command. Any other command listed below may be substituted by passing the letter of the command.
  • Sstep steps through the patient line by line, printing where the instruction pointer is and what line is to be executed Sstep waits for the user to type a command which it performs and then prints out again where sstep is executing.
  • This is a list of sstep commands:
    • q , < Esc >,` `
    Stops sstep and returns to command level.
    b
    Toggles a breakpoint at the current location.
    c
    Stops sstep and continues execution.
    n
    Continues to the next source line, skipping procedure calls, repeated string instructions, and software interrupts. Only stops when the machine returns to the right context (i.e. the stack pointer and current thread are the same as they are when the `n' command was given).
    l
    Goes to the next library routine.
    N
    Like n, but stops whenever the breakpoint is hit, whether you're in the same frame or not.
    M
    Goes to the next message called. Doesn't work when the message is not handled anywhere.
    f
    Finishes out the current stack frame.
    s , < Ret >
    Steps one source line
    S
    Skips the current instruction
    J
    Jump on a conditional jump, even when "Will not jump" appears. This does not change the condition codes.
    g
    Executes the `go' command with the rest of the line as arguments.
    e
    Executes a Tcl command and returns to the prompt.
    R
    References either the function to be called or the function currently executing.
    h , ?
    A help message.
  • Emacs will load in the correct file executing and following the lines where sstep is executing if its server is started and if ewatch is on in swat. If ewatch is off emacs will not be updated.
  • If the current patient isn't the actual current thread, sstep waits for the patient to wake up before single-stepping it.

See Also: istep, listi.

| Up: GEOS SDK TechDocs | Up | Prev: showcalls ... | Next: stdpaths ...


Swat Command Reference: stdpaths ...

 | Up: GEOS SDK TechDocs | Up | Prev: smatch ... | Next: stream ...

stdpaths

stdpaths

Examples: "stdpaths"

Print out all paths set for standard directories

See Also: pwd, dirs.

step

step

Examples:

"step"
execute the next instruction
"s"

Execute the patient by a single machine instruction.

  • If waitForPatient is non-zero, step waits for the machine to stop again.
  • This doesn't do any of the checks for special conditions (XchgTopStack, software interrupts, etc.) performed by the `s' command in istep.

See Also: istep, next.

step-patient

step-patient

Examples:

"step-patient"
Execute a single instruction on the target PC.

Causes the PC to execute a single instruction, returning only when the instruction has been executed.

  • Unlike the continue-patient command, this command will not return until the machine has stopped again.
  • No other thread will be allowed to run, as timer interrupts will be turned off while the instruction is being executed.

See Also: help-fetch.

step-until

step-until expression [byte|word]

Examples:

"step-until ax=0"
Single-step until ax is zero.
"step-until ds:20h!=0 byte"
Single-step until byte at ds:20h is non-zero
"step-until ds:20h!=0 word"
Single-step until word at ds:20h is non-zero
"step-until c=0"
Single-step until the carry is clear
"step-until ax!=ax"
Step forever

This command causes Swat to step until a condition is met.

Useful for tracking memory or register trashing bugs.

stop

stop in <class>::<message> [if <expr>]
stop in <procedure> [if <expr>]
stop in <address-history-token> [if <expr>]
stop at [<file>:]<line> [if <expr>]
stop <address> [if <expr>]

Examples: "stop in main"
"stop in @3"
"stop at /staff/pcgeos/Loader/main.asm:36 if { joe_local ==22}"
"stop at 25"
"stop MemAlloc+3 if {ax==3}"

Specify a place and condition at which the machine should stop executing. This command is intended primarily for setting breakpoints when debugging a geode created in C or another high-level language, but may also be used when debugging assembly-language geodes.

  • "stop in" will set a breakpoint at the beginning of a procedure, immediately after the procedure's stack frame has been set up.
  • "stop at" will set a breakpoint at the first instruction of the given source line. If no <file> is specified, the source file for the current stack frame is used.
  • If a condition is specified, by means of an "if <expr>" clause, you should enclose the expression in {}'s to prevent any nested commands, such as a "value fetch" command, from being evaluated until the break-point is hit.
  • For convenience, "stop in" also allows address-history tokens. This is useful when used in conjunction with the "methods" command.

See Also: brk, ibrk.

stop-catch

stop-catch <body>

Examples:

"stop-catch {go ProcCallModuleRoutine}"
Let machine run until it reaches ProcCallModuleRoutine() , but do not issue a FULLSTOP event when it gets there.

Allows a string of commands to execute without a FULLSTOP event being generated while they execute.

A number of things happen when a FULLSTOP event is dispatched, including notifying the user where the machine stopped. This is inappropriate in something like "istep" or "cycles" that is single-stepping the machine, for example.

See Also: event, continue-patient, step-patient.

stop-patient

stop-patient

Examples:

"stop-patient"
Stops the target PC.

Stops the target PC, in case you continued it and didn't wait for it to stop on its own.

This is different from the "stop" subcommand of the "patient" command.

See Also: continue-patient.

| Up: GEOS SDK TechDocs | Up | Prev: smatch ... | Next: stream ...


Swat Command Reference: stream ...

 | Up: GEOS SDK TechDocs | Up | Prev: stdpaths ... | Next: symbolCompletion ...

stream

stream open <file> (r|w|a|r+|w+)
stream read (line|list|char) <stream>
stream print <list> <stream>
stream write <string> <stream>
stream rewind <stream>
stream seek (<posn>|+<incr>|-<decr>|end) <stream>
stream state <stream>
stream eof <stream>
stream close <stream>
stream flush <stream>
stream watch <stream> <what> <procName>
stream ignore <stream>

Examples:

"var s [stream open kmap.def w]"
Open the file "kmap.def" for writing, creating it if it wasn't there before, and truncating any existing file.
"stream write $line $s"
Write the string in $line to the open stream.

This allows you to read, write, create, and otherwise manipulate files on the host machine from Swat.

  • Subcommands may be abbreviated uniquely.
  • Streams are a precious resource, so you should be sure to always close them when you are done. This means stream access should usually be performed under the wings of a "protect" command so the stream gets closed even if the user types Ctrl+C.
  • Swat's current directory changes as you change stack frames, with the directory always being the one that holds the executable file for the patient to which the function in the current frame belongs. If the <file> given to "stream open" isn't absolute, it will be affected by this.
  • The global variable file-init-dir contains the absolute path of the directory in which Swat was started. It can be quite useful when forming the <file> argument to "stream open".
  • The second argument to "stream open" is the access mode of the file. The meanings of the 5 possible values are:
    • r
    read-only access. The <file> must already exist.
    w
    write-only access. If <file> doesn't already exist, it will be created. If it does exist, it will be truncated.
    a
    append mode. The file is opened for writing only. If <file> doesn't already exist, it will be created. If it does exist, writing will commence at its end.
    r+
    read/write. The <file> must already exist. A single read/write position is maintained, and it starts out at the start of the file.
    w+
    read/write. If <file> doesn't already exist, it will be created. If it does exist, it will be truncated. A single read/write position is maintained, and it starts out at the start of the file.
  • "stream read" can read data from the stream in one of three formats:
    • line
    Returns all the characters from the current position up to the first newline or the end of the file, whichever comes first. The newline, if seen, is placed at the end of the string as \n. Any other non-printable characters or backslashes are similarly escaped.
    list
    Reads a single list from the stream, following all the usual rules of Tcl list construction. If the character at the current read position is a left brace, this will read to the matching right brace, bringing in newlines and other whitespace. If there is whitespace at the initial read position, it is skipped. Standard Tcl comments before the start of the list are also skipped over (so if the first non-whitespace character encountered is #, the characters up to the following newline or end-of-file will also be skipped).
    char
    This reads a single character from the stream. If the character isn't printable ASCII, it will be returned as one of the regular Tcl backslash escapes.

    If there's nothing left to read, you will get an empty string back.

  • "stream write" writes the string exactly as given, without interpreting backslash escapes. If you want to include a newline or something of the sort in the string, you'll need to use the "format" command to generate the string, or place the whole thing in braces and have the newlines in there literally.
  • While the syntax for "stream print" is the same as for "stream write", there is a subtle difference between the two. "stream write" will write the string as it's given, while "stream print" is intended to write out data to be read back in by "stream read list". Thus the command
    •  stream write {foo biff} $s

    would write the string "foo biff" to the stream. In contrast,

     stream print {foo biff} $s

    would write "{foo biff}" followed by a newline.

  • To ensure that all data you have written has made it to disk, use the "stream flush" command. Nothing is returned.
  • "stream rewind" repositions the read/write position at the start of the stream. "stream seek" gives you finer control over the position. You can set the stream to an absolute position (obtained from a previous call to "stream seek") by passing the byte number as a decimal number. You can also move forward or backward in the file a relative amount by specifying the number of bytes to move, preceded by a "+", for forward, or a "-", for backward. Finally, you can position the pointer at the end of the file by specifying a position of "end".
  • "stream seek" returns the new read/write position, so a call of "stream seek +0 $s" will get you the current position without changing anything. If the seek couldn't be performed, -1 is returned.
  • "stream state" returns one of three strings: "error", if there's been some error accessing the file, "eof" if the read/write position is at the end of the file, or "ok" if everything's fine. "stream eof" is a shortcut for figuring if you've reached the end of the file.
  • "stream close" shuts down the stream. The stream token should never be used again.
  • "stream watch" and "stream ignore" are valid only on UNIX and only make sense if the stream is open to a device or a socket. "stream watch" causes the procedure <procName> to be called whenever the stream is ready for the access indicated by <what>, which is a list of conditions chosen from the following set:
    • read
    the stream has data that may be read.
    write
    the stream has room for data to be written to it.

    When the stream is ready, the procedure is called:

     <procName> <stream> <what>

    where <what> is the list of operations for which the stream is ready.

See Also: protect, source, file.

switch

switch <thread-id>
switch [<patient>] [:<thread-num>]

Examples:

"switch 3730h"
Switches swat's current thread to be the one whose handle ID is 3730h.
"switch :1"
Switches Swat's current thread to be thread number 1 for the current patient.
"switch parallel:2"
Switches Swat's current thread to be thread number 2 for the patient "parallel"
"switch write"
Switches Swat's current thread to be thread number 0 (the process thread) for the patient "write"
"switch"
Switches Swat's current thread to be the current thread on the PC.

Switches between applications/threads.

  • Takes a single argument of the form <patient>:<thread-num> or <threadID>. With the first form, :<thread-num> is optional -- if the patient has threads, the first thread is selected. To switch to another thread of the same patient, give just :<thread-num>. You can also switch to a patient/thread by specifying the thread handle ID. NOTE: The switch doesn't happen on the PC--just inside swat.
  • If you don't give an argument, it switches to the actual current thread in the PC.
| Up: GEOS SDK TechDocs | Up | Prev: stdpaths ... | Next: symbolCompletion ...


Swat Command Reference: symbolCompletion ...

 | Up: GEOS SDK TechDocs | Up | Prev: stream ... | Next: text-fixup ...

symbolCompletion

var symbolCompletion [(0|1)]

Examples:

"var symbolCompletion 1"
Enable symbol completion in the top-level command reader.

This variable controls whether you can ask Swat to complete a symbol for you while you're typing a command. Completion is currently very slow and resource-intensive, so you probably don't want to enable it.

  • Even when symbolCompletion is 0, file-name, variable-name, and command- name completion are always enabled, using the keys described below.
  • When completion is enabled, three keys cause the interpreter to take the text immediately before the cursor and look for all symbols that begin with those characters. The keys are:
    • Ctrl+D
    Produces a list of all possible matches to the prefix.
    Escape
    Completes the command as best possible. If the characters typed so far could be the prefix for more than one command, Swat will fill in as many characters as possible.
    Ctrl+]
    Cycles through the list of possible symbols, in alphabetical order.

sym-default

sym-default [<name>]

Examples:

"sym-default motif"
Make swat look for any unknown symbols in the patient named "motif" once all other usual places have been searched.

Specifies an additional place to search for symbols when all the usual places have been searched to no avail.

  • The named patient need not have been loaded yet when you execute this command.
  • A typical use of this is to make whatever program you're working on be the sym-default in your .swat file so you don't need to worry about whether it's the current one, or reachable from the current one, when the machine stops and you want to examine the patient's state.
  • If you don't give a name, you'll be returned the name of the current sym-default.

sysfiles

sysfiles

Examples:

"sysfiles"
Print out all open files from dos's system file table.

Normally SFT entries that aren't in-use aren't printed. If you give the optional argument "all", however, all SFT entries, including those that aren't in-use, will be printed.

See Also: geosfiles, sftwalk, fwalk.

systemobj

systemobj

Examples:

"gentree [systemobj]"
print the generic tree starting at the system's root
"pobject [systemobj]"
print the system object

Prints out the address of the uiSystemObj, which is the top level of the generic tree.

  • This command is normally used with gentree as shown above to print out the whole generic tree starting from the top.

See Also: gentree, impliedgrab.

target

target [<object>]

Examples:

"target"
print target hierarchy from the system object down
"target -i"
print target hierarchy from implied grab down
"target ^l4e10h:20h"
print target hierarchy from ^l4e10h:20h down
"target [content]"
print target hierarchy from content under mouse.

Prints the target hierarchy below an object.

  • If no argument is specified, the system object is used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • Remember that the object you start from may have the target within its part of the hierarchy, but still not have the target because something in a different part of the tree has it.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available.

    • This variable defaults to false.

See Also: focus, model, mouse, keyboard, pobject.

targetobj

targetobj

Examples:

"targetobj"
return object with target
"pobj [targetobj]"
do a pobject on the target object (equivalent to "pobj -t").

Returns the object with the target.

See Also: target, focus, focusobj, modelobj.

tbrk

tbrk <addr> <condition>*
tbrk del <tbrk>+
tbrk list
tbrk cond <tbrk> <condition>*
tbrk count <tbrk>
tbrk reset <tbrk>
tbrk address <tbrk>

Examples:

"tbrk ObjCallMethodTable"
Count the number of times ObjCallMethodTable() is called.
"tbrk count 2"
Find the number of times tally breakpoint number 2 was hit.
"tbrk reset 2"
Reset the counter for tbrk number 2 to 0.
"tbrk list"
Print a list of the set tally breakpoints and their current counts.

This command manipulates breakpoints that tally the number of times they are hit without stopping execution of the machine--the breakpoint is noted and the machine is immediately continued. Such a breakpoint allows for real-time performance analysis, which is nice.

  • If you specify one or more <condition> arguments when setting the tally breakpoint, only those stops that meet the conditions will be counted.
  • The condition argument is exactly as defined by the "brk" command, q.v..
  • When you've set a tally breakpoint, you will be returned a token of the form "tbrk<n>", where <n> is some number. You use this token, or just the <n>, if you're not a program, wherever <tbrk> appears in the Usage description, above.
  • There are a limited number of tally breakpoints supported by the stub. You'll know when you've set too many.
  • "tbrk address" returns the address at which the tbrk was set, as a symbolic address expression.

See Also: brk, cbrk.

tcl-debug

tcl-debug top
tcl-debug next <tcl-frame>
tcl-debug prev <tcl-frame>
tcl-debug args <tcl-frame>
tcl-debug getf <tcl-frame>
tcl-debug setf <tcl-frame> <flags>
tcl-debug eval <tcl-frame> <expr>
tcl-debug complete <tcl-frame>
tcl-debug next-call

Examples:

"var f [tcl-debug top]"
Sets $f to be the frame at which the debugger was entered.
"var f [tcl-debug next $f]"
Retrieves the next frame down (away from the top) the Tcl call stack from $f.

This provides access to the internals of the Tcl interpreter for the Tcl debugger (which is written in Tcl, not C). It will not function except after the debugger has been entered.

See Also: debug.

| Up: GEOS SDK TechDocs | Up | Prev: stream ... | Next: text-fixup ...


Swat Command Reference: text-fixup ...

 | Up: GEOS SDK TechDocs | Up | Prev: symbolCompletion ... | Next: twalk ...

text-fixup

  1. Run geos under swat, run swat on the development system
  2. Run GeoWrite
  3. Open the GeoWrite file that needs fixing
  4. Set the breakpoint in swat:
    5.  patch text::CalculateRegions
 => text-fixup

    This will set a breakpoint at the right spot

  5. Turn on the error-checking code in swat:
    6.  ec +text
  6. Enter a <space> into the document. This forces recalculation which will cause CalculateRegions() to be called which will cause text-fixup to be called.

    7. If it worked, this code should patch together the file. If it's not, you'll get a FatalError right now.

  7. Turn off the ec code and disable the fixup breakpoint.
    8.  ec none
     dis <breakpoint number>
     continue
  8. Delete the space and save the file.

To do another file, you can just enable the breakpoint once the new file is open and turn on the ec code.

Helps fix up trashed GeoWrite documents.

thaw

thaw [<patient>]
thaw :<n>
thaw <patient>:<n>
thaw <id>

Examples:

"thaw"
Thaw the current thread.
"thaw term"
Allows the application thread for "term" to run normally
"thaw :1"
Allows thread #1 of the current patient to run normally
"thaw 16c0h"
Allows the thread whose handle is 16c0h to run normally.

Thawing a thread restores its priority to what it was before the thread was frozen.

See Also: freeze.

threadname

threadname <id>

Examples:

"threadname 21c0h"
Returns the name of the thread whose handle id is 21c0h.

Given a thread handle, produces the name of the thread, in the form <patient>:<n>

If the handle is not one of those swat knows to be for a thread, this returns the string "unknown."

See Also: thread, patient.

threadstat

threadstat

Examples:

"threadstat"

Provides information about all threads and various thread queues and synchronization points in the system.

See Also: ps.

timebrk

timebrk <start-addr> <end-addr>+
 timebrk del <timebrk>+
 timebrk list
 timebrk time <timebrk>
 timebrk reset <timebrk>

Examples:

"timebrk LoadResourceData -f"
Calculate the time required to process a call to LoadResourceData() .
"timebrk time 2"
Find the amount of time accumulated for timing breakpoint number 2.
"timebrk reset 2"
Reset the counter for timebrk number two to zero.
"timebrk list"
Print a list of the set timing breakpoints and their current counts and time.

This command manipulates breakpoints that calculate the amount of time executing between their starting point and a specified ending point. The breakpoints also record the number of times their start is hit, so you can figure the average amount of time per hit.

  • You can specify a place at which timing should end either as an address or as "-f". If you use "-f", timing will continue until the finish of the routine at whose start you've placed the breakpoint. Such a breakpoint may only be set at the start of a routine, as the stub hasn't the wherewithal to determine what the return address is at an arbitrary point within the function.
  • You may specify more than one ending point. Timing will stop when execution reaches any of those points.
  • When you've set a timing breakpoint, you will be returned a token of the form "timebrk<n>", where <n> is some number. You use this token, or just the <n>, if you're not a program, wherever <timebrk> appears in the Usage description, above.

See Also: brk, cbrk, tbrk.

timingProcessor

var timingProcessor [i86|i88|i286|V20]

The processor for which to generate cycle counts.

See Also: cycles.

tmem

tmem

Examples:

"tmem"
turn on memory tracing.

Trace memory usage.

The tmem command catches calls to DebugMemory, printing out the parameters passed (move, free, realloc, discard, swapout, swapin, modify).

top-level

top-level

Examples:

"top-level"
Begin reading and interpreting Tcl commands in a nested interpreter.

This is the top-most read-eval-print loop of the Swat Tcl interpreter.

This command will only return if the user issues the "break" command. Otherwise it loops infinitely, reading and executing and printing the results of Tcl commands.

tundocalls

tundocalls [-acPCrR]

Examples:

"tundocalls -a" Print out all text undo calls
"tundocalls -r" Print run undo calls
"tundocalls -R" Print replace undo calls
"tundocalls -c" Print info when undo information is created
"tundocalls -cP" Print info about para attributes only
"tundocalls -cC" Print info about char attributes only
"tundocalls"

Prints out information about each undo call made to the text object.

See Also: ptext, showcalls.

| Up: GEOS SDK TechDocs | Up | Prev: symbolCompletion ... | Next: twalk ...


Swat Command Reference: twalk ...

 | Up: GEOS SDK TechDocs | Up | Prev: text-fixup ... | Next: varwin ...

twalk

twalk

Examples:

"twalk"
print all the timers in the system.
"twalk -o ui"
print all the timers in the system for the ui thread.
"twalk -a"
print all the timers with the "real" data for the time for time remaining rather than maintaining a total.

List all the timers in GEOS.

unalias

unalias <name>+

Examples:

"unalias p"
Removes "p" as an alias for print.

This removes an alias.

  • In fact, this actually can be used to delete any command at all, including Tcl procedures and Swat built-in commands. Once they're gone, however, there's no way to get them back.

See Also: alias.

unassemble

unassemble [<addr> [<decode-args>]]

Examples:

"unassemble cs:ip 1"
Disassemble the instruction at CS:IP and return a string that shows the values of the arguments involved.

This decodes data as machine instructions and returns them to you to display as you like. It is not usually typed from the command line.

  • The return value is always a four-element list:
    •  {<symbolic-addr> <instruction> <size> <args>}

    where <symbolic-addr> is the address expressed as an offset from some named symbol, <instruction> is the decoded instruction (without any leading whitespace), <size> is the size of the instruction (in bytes) and <args> is a string displaying the values of the instruction operands, if <decode-args> was given and non-zero (it is the empty string if <decode-args> is missing or 0).

  • If <addr> is missing or "nil", the instruction at the current frame's CS:IP is returned.

See Also: listi.

unbind-key

unbind-key <ascii_value>

Examples:

"unbind-key \321"
Unbinds scroll-down key on host machine.

Unbinds the passed ASCII value.

See Also: alias, bind-key, get-key-binding.

undebug

undebug <proc-name>+

Examples:

"undebug fooproc"
Cease halting execution each time "fooproc" is executing.

Removes a Tcl breakpoint set by a previous "debug" command.

See Also: debug.

up

up [<frame offset>]

Examples:

"up"
move the frame one frame up the stack
"up 4"
move the frame four frames up the stack

Move the frame up the stack.

  • The <frame offset> argument is the number of frame to move up the stack. If none is specified then the current frame is moved up one frame.
  • This command may be repeated by pressing <Return>.

See Also: backtrace, down.

value

value fetch <addr> [<type>]
value store <addr> <value> [<type>]
value hfetch <num>
value hstore <addr-list>
value hset <number-saved>
value log <addr> <stream> [<type>]

Examples:

"value fetch ds:si [type word]"
Fetch a word from ds:si
"value store ds:si 0 [type word]"
Store 0 to the word at ds:si
"value hfetch 36"
Fetch the 36th address list stored in the value history.
"value hstore $a"
Store the address list in $a into the value history.
"value hset 50"
Keep track of up to 50 address lists in the value history.
"value log ds:si $s [type word]"
Fetch a word from ds:si and dump the binary data to stream $s.

This command allows you to fetch and alter values in the target PC. It is also the maintainer of the value history, which you normally access via @<number> terms in address expressions.

  • "value fetch" returns a value list that contains the data at the given address. If the address has an implied data type (it involves a named variable or a structure field), then you do not need to give the <type> argument.

    • All integers and enumerated types are returned in decimal. 32-bit pointers are returned as a single decimal integer whose high 16 bits are the high 16 bits (segment or handle) of the pointer. 16-bit pointers are likewise returned as a single decimal integer.

    Characters are returned as characters, with non-printable characters converted to the appropriate backslash escapes (for example, newline is returned as \n).

    Arrays are returned as a list of value lists, one element per element of the array.

    Structures, unions and records are returned as a list of elements, each of which is a 3-element list: {<field-name> <type> <value>} <field-name> is the name of the field, <type> is the type token for the type of data stored in the field, and <value> is the value list for the data in the field, appropriate to its data type.

  • You will note that the description of value lists is recursive. For example, if a structure has a field that is an array, the <value> element in the list that describes that particular field will be itself a list whose elements are the elements of the array. If that array were an array of structures, each element of that list would again be a list of {<field-name> <type> <value>} lists.
  • The "field" command is very useful when you want to extract the value for a structure field from a value list.
  • As for "value fetch", you do not need to give the <type> argument to
  • "value store" if the <addr> has an implied data type. The <value> argument is a value list appropriate to the type of data being stored, as described above.
  • "value hstore" returns the number assigned to the stored address list. These numbers always increase, starting from 1.
  • If no address list is stored for a given number, "value hfetch" will generate an error.
  • "value hset" controls the maximum number of address lists the value history will hold. The value history is a FIFO queue; if it holds 50 entries, and the 51st entry is added to it, the 1st entry will be thrown out.
  • "value log" has the same functionality as value fetch except that data fetched from the PC are dumped into the stream as raw binary data.

See Also: addr-parse, assign, field.

| Up: GEOS SDK TechDocs | Up | Prev: text-fixup ... | Next: varwin ...


Swat Command Reference: varwin ...

 | Up: GEOS SDK TechDocs | Up | Prev: twalk ... | Next: wclear ...

varwin

varwin <num-lines> <var-name>

view

view [<args>]

Examples:

"view foo.goc"
Bring up foo.goc in the source window.

View a file in Swat.

See Also: view-default, srcwin.

view-default

view-default [patient]

Examples:

"view-default spool"
sets the default view to the spool patient.
"view-default"
turns off the view default.

If the view-default is set the view command will automatically look for source files from that patient. If it's not set then the view command will look for files from the current patient.

See Also: view, srcwin.

vistree

vistree [<address>] [<instance field>]

Examples:

"vistree"
print the visual tree starting at *DS:SI
"vistree -i"
print the visual tree under the mouse
"vistree @23 VI_optFlags"
print the visual tree with opt flags
"vistree *uiSystemObj"
starts the visual tree at the root of the system.

Print out a visual tree.

  • The <address> argument is the address to an object in the generic tree. This defaults to *DS:SI. The `-i' flag for an implied grab may be used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • The <instance field> argument is the offset to any instance data within the VisInstance which should be printed out.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available. This variable defaults to false.

See Also: vup, gentree, impliedgrab, pobject.

vup

vup [<address>] [<instance field>]

Examples:

"vup"
print the visual object at *DS:SI and its ancestors
"vup @23 VI_optFlags"
print the states of object @23 and its ancestors
"vup -i"
print the visual object under the mouse and the object's ancestors

Print a list of the object and all of its visual ancestors.

  • The <address> argument is the address to an object in the visual tree. This defaults to *DS:SI. The `-i' flag for an implied grab may be used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • The <instance field> argument is the offset to any instance data within the GenInstance which should be printed out.

See Also: vistree, gup, gentree, impliedgrab, pobject.

wait

wait

Examples:

"wait"
Wait for the target PC to halt.

This is used after the machine has been continued with "continue-patient" to wait for the machine to stop again. Its use is usually hidden by calling "cont" or "next".

  • This returns 0 if the patient halted naturally (because it hit a breakpoint), and 1 if it was interrupted (by the user typing Ctrl+C to Swat).
  • Most procedures won't need to use this function.

See Also: brk, ibrk.

waitForPatient

var waitForPatient [(1|0)]

Examples:

"var waitForPatient 0"
Tells Swat to return to the command prompt after continuing the machine.

Determines whether the command-level patient-continuation commands (step, next, and cont, for example) will wait for the machine to stop before returning.

  • The effect of this is to return to the command prompt immediately after having issued the command. This allows you to periodically examine the state of the machine without actually halting it.
  • The output when the machine does stop (e.g. when it hits a breakpoint) can be somewhat confusing. Furthermore, this isn't fully tested, so it should probably be set to 0 only in somewhat odd circumstances.

See Also: step, next, cont, int.

wakeup

Wait for a given patient/thread to wake up. WHO is of the same form as the argument to the "switch" command, ("help switch" to find out more). Leaves you stopped in the kernel in the desired thread's context unless something else causes the machine to stop before the patient/thread wakes up. WHO defaults to the current thread.

wakeup-thread

Subroutine to actually wake up a thread. Argument WHO is as for the "switch"command. Returns non-zero if the wakeup was successful and zero if the machine stopped for some other reason.

| Up: GEOS SDK TechDocs | Up | Prev: twalk ... | Next: wclear ...


Swat Command Reference: wclear ...

 | Up: GEOS SDK TechDocs | Up | Prev: varwin ...

wclear

wclear

Clears the current window.

wcreate

wcreate <height>

Create a window of the given height and return a token for it. The window is placed just above the command window, if there's room. If there aren't that many lines free on the screen, an error is returned.

wdelete

wdelete <window>

Delete the given window. All windows below it move up and the command window enlarges.

whatat

whatat [<address>]

Examples:

"whatat"
name of variable at *DS:SI
"whatat ^l2ef0h:002ah"
name of variable at the specified address

Print the name of the variable at the address.

  • The <address> argument specifies where to find a variable name for. The address defaults to *DS:SI.
  • If no appropriate variable is found for the address, `*nil*' is returned.

See Also: pobject, hwalk, lhwalk.

whatis

whatis (<symbol>|<addr>)

Examples: "whatis WinColorFlags"

This produces a human-readable description of a symbol, giving whatever information is pertinent to its type.

  • For type symbols (e.g. structures and enumerated types), the description of the type is fully displayed, so if a structure has a field with an enumerated type, all the members of the enumerated type will be printed as well. Also all fields of nested structures will be printed. If this level of detail isn't what you need, use the "pscope" command instead.
  • It's not clear why you'd need the ability to find the type of an address-expression, since those types always come from some symbol or other, but if you want to type more, you certainly may.

why

why

Examples: "why"

Print a description of why the system crashed.

  • This must be run from within the frame of the FatalError function. Sometimes GEOS is not quite there. In this case, step an instruction or two and then try the "why" command again.
  • This simply looks up the enumerated constant for the error code in AX in the "FatalErrors" enumerated type defined by the geode that called FatalError. For example, if a function in the kernel called FatalError, AX would be looked up in geos::FatalErrors, while if a function in your application called FatalError, this function would look it up in the FatalErrors type defined by your application. Each application defines this enumerated type by virtue of having included ec.def or ec.goh .
  • For certain fatal errors, additional information is provided by invoking the command <patient>::<error code name>, if it exists.

See Also: regs, backtrace, explain.

wintree

wintree <window handle> [<data field>]

Examples:

"wintree ^hd060h"
print a window tree starting at the handle d060h

Print a window tree starting with the root specified.

  • The <window address> argument is the address to a window.
  • The <data field> argument is the offset to any instance data within a window (like W_ptrFlags).

See Also: vistree, gentree.

winverse

winverse

Sets the inverse-mode of the current window (whether newly-echoed characters are displayed in inverse video) on or off, depending on its argument (1 is on).

wmove

wmove [(+|-)] <x-coord> [(+|-)] <y-coord>

Moves the cursor for the current window. Takes two arguments: the new x position and the new y position. These positions may be absolute or relative (absolute positions begin with + or -). If you attempt to move outside the current window, an error is generated. This command returns the new cursor position as { x y }.

words

words [<address>] [<length>]

Examples:

"words"
lists 8 words at DS:SI
"words ds:di 16"
lists 16 words starting at DS:DI

Examine memory as a dump of words.

  • The <address> argument is the address to examine. If not specified, the address after the last examined memory location is used. If no address has been examined then DS:SI is used for the address.
  • The <length> argument is the number of bytes to examine. It defaults to 8.
  • Pressing <Return> after this command continues the list.

See Also: bytes, dwords, imem, assign.

wpop

wpop

Revert the current window to its previously pushed value.

wpush

wpush <window>

Switch to a new window, saving the old current-window. Use wpop to go back to the previous window. All I/O goes through the current window.

wrefresh

wrefresh

Synchronizes the current window with the screen. This need only be performed if you don't echo a newline, as echoing a newline refreshes the current window.

wtop

wtop <flag>

Sets where windows go. If argument is non-zero, windows go at the top of the screen and work down. Else windows go at the bottom of the screen and work up

| Up: GEOS SDK TechDocs | Up | Prev: varwin ...


Tool Command Language

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Using This Chapter
| One-page Version of this Chapter for Easy Printing

This chapter is designed to provide information about the Tool Command Language, abbreviated Tcl, (the language in which Swat commands are written) so that new commands can be written and old commands modified. This chapter contains the following main sections:

  • Using This Chapter
    Discussion of the situations warranting the construction of a new command in the Tool Command Language.
  • Background and Description
    Discussion of history of Tool Command Language and general overview of the language.
  • Syntax and Structure
    Description of the syntax and structure of the Tool Command Language.
  • Commands
    List of all built-in commands for the language.
  • Coding
    Descriptions and examples of coding conventions, techniques, and tricks.
  • Installation
    Steps to take in order to be able to use a newly written command to help debug an application.

1 Using This Chapter
2 Copyright Information
3 Background and Description
4 Syntax and Structure
4.1 Basic Command Syntax
4.2 Expressions
4.3 Lists
4.4 Command Results
4.5 Procedures
4.6 Variables
5 Commands
5.1 Notation
5.2 Built-in Commands
6 Coding
6.1 Swat Data Structure Commands
6.2 Examples
7 Using a New Command
7.1 Compilation
7.2 Autoloading
7.3 Explicit Loading

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Using This Chapter
| One-page Version of this Chapter for Easy Printing


Using This Chapter

Tool Command Language: 1 Using This Chapter

| Up: GEOS SDK TechDocs | Up | Prev: Tool Command Language | Next: 2 Copyright Information

This chapter provides the information needed to write a new Swat command in Tcl. But, new commands need only be written in certain situations. Some of the situations in which it is advantageous to write a new Swat command in Tcl are:

  • There is complex task that is being repeated often. For example, if one is continually examining a certain piece of data in memory but has to go through many steps to do so, then it is helpful to write a single command to perform all of the needed steps.
  • When a new data-structure is created for an application. For example, if one creates a look-up table for the application, then a Tcl command should be written to examine that table in particular.

The existing Swat commands should take care of the bulk of debugging, but sometimes an extra command can help.

| Up: GEOS SDK TechDocs | Up | Prev: Tool Command Language | Next: 2 Copyright Information


Copyright Information

Tool Command Language: 2 Copyright Information

| Up: GEOS SDK TechDocs | Up | Prev: 1 Using This Chapter | Next: 3 Background and Description

The following sections of this chapter fall under the copyright below: Background and Description, Syntax and Structure, and Commands.

Copyright © 1987 Regents of the University of California

Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The University of California makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.

| Up: GEOS SDK TechDocs | Up | Prev: 1 Using This Chapter | Next: 3 Background and Description


Background and Description

Tool Command Language: 3 Background and Description

| Up: GEOS SDK TechDocs | Up | Prev: 2 Copyright Information | Next: 4 Syntax and Structure

The Tool Command Language is abbreviated as Tcl and is pronounced "tickle". It was developed and written by Professor John Ousterhout at the University of California at Berkeley. Tcl is a combination of two main parts: a language and a library.

Language
The Tcl language is a textual language intended primarily for issuing commands to interactive programs such as text editors, illustrators, shells, and most importantly debuggers. It has a set syntax and is programmable, thus allowing users to create more powerful commands than the built-in command set listed in the Swat Command Reference.
Library
Tcl also includes a library which can be imbedded in an application, as it is in Swat. This library includes a parser for the Tcl language, routines to implement the Tcl built-in commands, and procedures allowing an application to extend Tcl with additional commands.
| Up: GEOS SDK TechDocs | Up | Prev: 2 Copyright Information | Next: 4 Syntax and Structure


Syntax and Structure

Tool Command Language: 4 Syntax and Structure

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Background and Description | Next: 5 Commands

Tcl supports only one type of data: strings . All commands, all arguments to commands, all command results, and all variable values are strings. Where commands require numeric arguments or return numeric results, the arguments and results are passed as strings. Many commands expect their string arguments to have certain formats, but this interpretation is up to the individual commands. For example, arguments often contain Tcl command strings, which may get executed as part of the commands. The easiest way to understand the Tcl interpreter is to remember that everything is just an operation on a string. In many cases Tcl constructs will look similar to more structured constructs from other languages. However, the Tcl constructs are not structured at all; they are just strings of characters, and this gives them a different behavior than the structures they may look like.

Although the exact interpretation of a Tcl string depends on who is doing the interpretation, there are three common forms that strings take: commands , expressions , and lists . This section will have the following main parts:

  • Basic Command Syntax
    Description of the syntax common to all Tcl code: comments, argument grouping, command grouping, variable substitution, backslash substitution.
  • Expressions
    Details on interpretation of expressions by Tcl.
  • Lists
    Details on interpretation of lists by Tcl.
  • Command Results
    What type of results a command can return.
  • Procedures
    The structure and building of procedures in Tcl.
  • Variables
    Variable declaration and description.

Basic Command Syntax

Expressions

Lists

Command Results

Procedures

Variables

| Up: GEOS SDK TechDocs | Up | Down | Prev: 3 Background and Description | Next: 5 Commands


Basic Command Syntax

Tool Command Language: 4.1 Syntax and Structure: Basic Command Syntax

| Up: GEOS SDK TechDocs | Up | Prev: 4 Syntax and Structure | Next: 4.2 Expressions

The Tcl language has syntactic similarities to both Unix and Lisp. However, the interpretation of commands is different in Tcl than in either of those other two systems. A Tcl command string consists of one or more commands separated by newline characters. Each command consists of a collection of fields separated by white space (spaces or tabs). The first field must be the name of a command, and the additional fields, if any, are arguments that will be passed to that command. For example, the command:

var a 22

has three fields: the first, var , is the name of a Tcl command, and the last two, a and 22 , will be passed as arguments to the var command. The command name may refer to a built-in Tcl command, an application specific command, or a command procedure defined with the built-in proc command. Arguments are passed literally as text strings. Individual commands may interpret those strings in any fashion they wish. The var command, for example, will treat its first argument as the name of a variable and its second argument as a string value to assign to that variable. For other commands, arguments may be interpreted as integers, lists, file names, or Tcl commands.

Comments

If the first non-blank character in a command is # (a number sign), then everything from the # up through the next newline character is treated as comment and discarded by the parser.

Argument Grouping

Normally each argument field ends at the next white space (tabs or spaces), but curly braces ("{" and "}") may be used to group arguments in different ways. If an argument field begins with a left brace, then the argument is not terminated by white space; it ends at the matching right brace. Tcl will strip off the outermost layer of braces before passing the argument to the command. For example, in the command:

var a {b c}

the var command will receive two arguments: a and b c . The matching right brace need not be on the same line as the left brace; in this case the newline will be included in the argument field along with any other characters up to the matching right brace. In many cases an argument field to one command consists of a Tcl command string that will be executed later; braces allow complex command structures to be built up without confusion. For example, the eval command takes one argument, which is a command string; eval invokes the Tcl interpreter to execute the command string. The command:

eval {
       var a 22
       var b 33
}

will assign the value 22 to a and 33 to b .

Tcl braces act like quote characters in most other languages, in that they prevent any special interpretation of the characters between the left brace and the matching right brace.

When an argument is in braces, then command, variable, and backslash substitutions do not occur in the normal fashion; all Tcl does is to strip off the outer layer of braces and pass the contents to the command. Braces are only significant in a command field if the first character of the field is a left brace. Otherwise neither left nor right braces in the field will be treated specially (except as part of variable substitution).

Command Grouping

Normally, each command occupies one line (the command is terminated by a newline character). Thus, the string:

var a 22
var b 33

will be interpreted as two separate commands. However, brackets may be used to group commands in ways other than one-command-per-line. If the first character of a command is an open bracket, then the command is not terminated by a newline character; instead, it consists of all the characters up to the matching close bracket. Newline characters inside a bracketed command are treated as white space (they will act as argument separators for arguments that are not enclosed in braces). For example, the string:

[var a
22] [var b 33]

will have the same effect as the previous example.

Command Substitution

If an open bracket occurs in any of the fields of a command, then command substitution occurs. All of the text up to the matching close bracket is treated as a Tcl command and executed immediately. The result of that command is substituted for the bracketed text. For example, consider the command:

var a [var b]

When the var command has only a single argument, it is the name of a variable and var returns the contents of that variable. In this case, if variable b has the value test , then the command above is equivalent to the command:

var a test

Brackets can be used in more complex ways. for example, if the variable b has the value tmp and the variable c has the value val , then the command:

var a test[var b].[var c]

is equivalent to the command:

var a testtmp.val

If a field is enclosed in braces then the brackets and the characters between them are not interpreted specially; they are passed through to the argument verbatim.

Variable Substitution

The dollar sign ($) may be used as a special shorthand form for substituting variables. If $ appears in an argument that is not enclosed in braces then variable substitution will occur. The characters after the $, up to the first character that is not a number, letter, or underscore, are taken as a variable name and the string value of that variable is substituted for the name. Or, if the dollar sign is followed by an open curly brace, then the variable name consists of all the characters up to the next close curly brace. For example, if variable outfile has the value test , then the command:

var a $outfile.c

is equivalent to the command:

var a test.c

and the command:

var a abc${outfile}tmp

is equivalent to the command:

var a abctesttmp

Variable substitution does not occur in arguments that are enclosed in braces: the dollar sign and variable name are passed through to the argument verbatim.

The dollar sign abbreviation is simply a shorthand form. $a is completely equivalent to [var a] ; it is provided as a convenience to reduce typing.

Backslash Substitution

Backslashes may be used to insert non-printing characters into command fields and also to insert braces, brackets, and dollar signs into fields without them being interpreted specially as previously described. The backslash sequences understood by the Tcl interpreter are listed below. In each case, the backslash sequence is replaced by the given character.

For example, in the command:

var a \{x\[\ yz\141

the second argument to var is {x[ yza (note the <space> as part of the argument).

If a backslash is followed by something other than one of the options listed below, then the backslash is transmitted to the argument field without any special processing, and the Tcl scanner continues normal processing with the next character. For example, in the command:

var \*a \\\{test

the first argument will be \*a and the second \{test .

If an argument is enclosed in braces, then backslash sequences inside the argument are parsed but no substitution occurs. In particular, backslashed braces are not counted in locating the matching right brace that terminates the argument. for example, in the command:

var a {\{abc}

the second argument to var will be \{abc .

The backslash mechanism is not sufficient to generate any argument structure; it only covers the most common cases. To produce particularly complicated arguments it will probably be easiest to use the format command along with command substitution.

| Up: GEOS SDK TechDocs | Up | Prev: 4 Syntax and Structure | Next: 4.2 Expressions


Expressions

Tool Command Language: 4.2 Syntax and Structure: Expressions

| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Basic Command Syntax | Next: 4.3 Lists

The second major interpretation applied to strings in Tcl is as expressions . Several commands, such as expr , for , and if , treat some of their arguments as expressions and call the Tcl expression processor (Tcl_Expr) to evaluate them. A Tcl expression has C-like syntax and evaluates to an integer result. Expressions may contain integer values, variable names in $ notation (the variables' values must be integer strings), commands (embedded in brackets) that produce integer string results, parentheses for grouping, and operators. Numeric values, whether they are passed directly or through variable or command substitution, may be specified either in decimal (the normal case), in octal (if the first character of the value of the first character is 0 (zero)), or in hexadecimal (if the first two characters of the value are 0x). The valid operators are listed below, grouped in decreasing order of precedence.

See a C manual for more details on the results produced by each operator. All of the binary operators group left to right within the same precedence level. for example, the expression:

(4*2)<7

evaluates to zero. Evaluating the expression string:

($a+3)<[var b]

will cause the values of the variables a and b to be examined; the result will be 1 if b is greater than a by at least 3; otherwise the result will be 0.

In general it is safest to enclose an expression in braces when entering it in a command; otherwise, if the expression contains any white space then the Tcl interpreter will split it among several arguments. For example, the command:

expr $a + $b

results in three arguments being passed to expr : $a , + , and $b . In addition, if the expression is not in braces then the Tcl interpreter will perform variable and command substitution immediately (it will happen in the command parser rather than in the expression parser). In many cases the expression is being passed to a command that will evaluate the expression later (or even many times if, for example, the expression is to be used to decide when to exit a loop). usually the desired goal is to re-do the variable or command substitutions each time the expression is evaluated, rather than once and for all at the beginning. For an example of a mistake, the command:

for {var i 1} $i<=10 {var i [expr $i+1]} {body...}

is probably intended to iterate over all values of i from 1 to 10. After each iteration of the body of the loop, for will pass its second argument to the expression evaluator to see whether or not to continue processing. Unfortunately, in this case the value of i in the second argument will be substituted once and for all when the for command is parsed. If i was 0 before the for command was invoked then for 's second argument will be 0<=10 which will always evaluate to 1, even though i 's value eventually becomes greater than 10. In the above case the loop will never terminate. By placing the expression in braces, the substitution of i 's value will be delayed; it will be re-done each time the expression is evaluated, which is probably the desired result:

for {var i 1} {$i<=10} {var i [expr $i+1]} {body...}
| Up: GEOS SDK TechDocs | Up | Prev: 4.1 Basic Command Syntax | Next: 4.3 Lists


Lists

Tool Command Language: 4.3 Syntax and Structure: Lists

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Expressions | Next: 4.4 Command Results

The third major way that strings are interpreted in Tcl is a list . A list is just a string with a list-like structure consisting of fields separated by white space. For example, the string:

Al Sue Anne John

is a list with four elements or fields. Lists have the same basic structure as command strings, except that a newline character in a list is treated as a field separator just like a space or tab. Conventions for braces and backslashes are the same for lists as for commands. For example, the string:

a b\ c {d e {f g h}}

is a list with three elements: a , b c , and d e {f g h} . Note the space between the b and c . Whenever an element is extracted from a list, the same rules about backslashes and braces are applied as for commands. Thus in the above example when the third element is extracted from the list, the result is:

d e {f g h}

(when the field was extracted, all that happened was to strip off the outermost layer of braces). Command substitution is never made on a list (at least, not by the list-processing commands; the list can always be passed to the Tcl interpreter for evaluation).

The Tcl commands concat , foreach , index , length , list , and range allow you to build lists, extract elements from them, search them, and perform other list-related functions.

| Up: GEOS SDK TechDocs | Up | Prev: 4.2 Expressions | Next: 4.4 Command Results


Command Results

Tool Command Language: 4.4 Syntax and Structure: Command Results

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Lists | Next: 4.5 Procedures

Each command produces two results: a code and a string . The code indicates whether the command completed successfully or not, and the string gives additional information. The valid codes are defined as follows:

TCL_OK
This is the normal return code, and indicates that the command completed successfully. The string gives the commands's return value.
TCL_ERROR
Indicates that an error occurred; the string gives a message describing the error.
TCL_RETURN
Indicates that the return command has been invoked, and that the current procedure should return immediately. The string gives the return value that procedure should return.
TCL_BREAK
Indicates that the break command has been invoked, so the innermost loop should abort immediately. The string should always be empty.
TCL_CONTINUE
Indicates that the continue command has been invoked, so the innermost loop should go on to the next iteration. The string should always be empty.

Tcl programmers do not normally need to think about return codes, since TCL_OK is almost always returned. If anything else is returned by a command, then the Tcl interpreter immediately stops processing commands and returns to its caller. If there are several nested invocations of the Tcl interpreter in progress, then each nested command will usually return the error to its caller, until eventually the error is reported to the top-level application code. The application will then display the error message for the user.

In a few cases, some commands will handle certain "error" conditions themselves and not return them upwards. For example, the for command checks for the TCL_BREAK code; if it occurs, then for stops executing the body of the loop and returns TCL_OK to its caller. The for command also handles TCL_CONTINUE codes and the procedure interpreter handles TCL_RETURN codes. The catch command allows Tcl programs to catch errors and handle them without aborting command interpretation any further.

| Up: GEOS SDK TechDocs | Up | Prev: 4.3 Lists | Next: 4.5 Procedures


Procedures

Tool Command Language: 4.5 Syntax and Structure: Procedures

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Command Results | Next: 4.6 Variables

Tcl allows one to extend the command interface by defining procedures. A Tcl procedure can be invoked just like any other Tcl command (it has a name and it receives one or more arguments). The only difference is that its body is not a piece of C code linked into the program; it is a string containing one or more other Tcl commands. See the proc command for information on how to define procedures and what happens when they are invoked.

| Up: GEOS SDK TechDocs | Up | Prev: 4.4 Command Results | Next: 4.6 Variables


Variables

Tool Command Language: 4.6 Syntax and Structure: Variables

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Procedures | Next: 5 Commands

Tcl allows the definition of variables and the use of their values either through $-style variable substitution, the var command, or a few other mechanisms. Variables need not be declared: a new variable will automatically be created each time a new variable name is used. Variables may be either global or local. If a variable name is used when a procedure is not being executed, then it automatically refers to a global invocation of the procedure. Local variables are deleted whenever a procedure exits. The global command may be used to request that a name refer to a global variable for the duration of the current procedure (somewhat analogous to extern in C).

| Up: GEOS SDK TechDocs | Up | Prev: 4.5 Procedures | Next: 5 Commands


Commands

Tool Command Language: 5 Commands

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Syntax and Structure | Next: 6 Coding

The Tcl library provides the following built-in commands, which will be available to any application using Tcl. In addition to these built-in commands, there may be additional commands defined in Swat, plus commands defined as Tcl procedures.

Notation

Built-in Commands

| Up: GEOS SDK TechDocs | Up | Down | Prev: 4 Syntax and Structure | Next: 6 Coding


Notation

Tool Command Language: 5.1 Commands: Notation

| Up: GEOS SDK TechDocs | Up | Prev: 5 Commands | Next: 5.2 Built-in Commands

The descriptions of the Tcl commands will follow the following notational conventions:

  • command (alternative1|alternative2|...|alternativeN)
    ()     The parentheses enclose a set of alternatives separated by a vertical line. For example, the expression quit (cont|leave) means that either quit cont or quit leave can be used.
  • command [optional_argument]
    []     The brackets enclose optional arguments to the command. For example, the command alias [<word[<command>]>] could have zero, one, or two arguments because the <command> and <word> arguments are optional. Another example would be the command objwalk [<addr>] , which may take zero arguments if it is meant to use the default address, and one argument if the user gives it a particular address to look at.
  • command <type_of_argument>
    < >     The angled brackets enclose the type of an argument rather than the actual string to be typed. For example, <addr> indicates an address expression and <argument> indicates some sort of argument, but (addr|type) means either the string addr or the string type .
  • * +
     An asterisk following any of the previous constructs indicates zero or more repetitions of the construct may be typed. An addition sign indicates one or more repetitions of the construct may be used. For example, unalias word * can be the unalias command by itself, or it can be followed by a list of words to be unaliased.
| Up: GEOS SDK TechDocs | Up | Prev: 5 Commands | Next: 5.2 Built-in Commands


Built-in Commands

Tool Command Language: 5.2 Commands: Built-in Commands

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Notation | Next: 6 Coding

The built-in Tcl commands are as follows:

bc

bc list <proc>
bc disasm <proc>
bc compile <proc>
bc fcompile <file> [<nohelp>]
bc fload <file>
bc fdisasm <file>
bc debug [1|0]

Examples:

"bc compile poof"
Compiles the body of the procedure "poof" and replaces the existing procedure with its compiled form.
"bc fcomp bptutils.tcl"
Creates the file "bptutils.tlc" that contains a stream of compiled Tcl that will do exactly what sourcing bptutils.tcl does, except the resulting procedures will be compiled Tcl, not interpreted Tcl.
"bc fload bptutils.tlc"
Loads a file containing a stream of compiled Tcl code.

The "bc" command allows you to create and examine compiled Tcl code. Compiled Tcl is not nearly as readable or changeable as interpreted Tcl code, but it's 30-50% faster.

The "list" subcommand doesn't work.

See Also: source.

break

break

Examples:

"break"
Break out of the current loop.

Breaks out of the current loop or the current nested interpreter.

  • Only the closest-enclosing loop can be exited via this command.
  • This command may be invoked only inside the body of a loop command such as for or foreach . It returns a TCL_BREAK code to signal the innermost containing loop command to return immediately.
  • If you've entered a nested interpreter, e.g. by calling a function in the patient, use this to exit the interpreter and restore the registers to what they were before you made the call.

See Also: continue, for.

case

case <string> [in] [<pat> <body>]+

Examples:

"[case $c in
{[0-9]} {
# do something with digit
}
default {
# do something with non-digit
}
]"
Do one of two things depending on whether the character in $c is a digit.

Perform one of a set of actions based on whether a string matches one or more patterns.

  • Compares each of the <pattern> arguments to the given <string>, executing <body> following the first <pattern> to match. <pattern> uses shell wildcard characters as for the string match command, but may also contain alternatives, which are separated by a vertical bar, thus allowing a <body> to be executed under one of several circumstances. In addition, if one <pattern> (or element thereof) is the string default, the associated <body> will be executed if none of the other patterns matches. For example, the following:
    • [case $test in
 a|b {return 1}
 {default|[DE]a*} {return 0}
 ?c {return -1}]

    will return 1 if variable test contains a or b , -1 if it contains a two-letter string whose second letter is c , and 0 in all other cases, including the ones where test 's first two letters are either Da or Ea .

  • Each <pat> argument is a list of patterns of the form described for the "string match" command.
  • Each <pat> argument must be accompanied by a <body> to execute.
  • If a <pat> contains the special pattern "default," the associated <body> will be executed if no other pattern matches. The difference between "default" and "*" is a pattern of "*" causes the <body> to be executed regardless of the patterns in the remaining <pat> arguments, while "default" postpones the decision until all the remaining patterns have been checked.
  • You can give the literal "in" argument if you wish to enhance the readability of your code.

See Also: string, if.

catch

catch <command> [<varName>]

Executes a command, retaining control even if the command generates an error (which would otherwise cause execution to unwind completely).

  • The catch command may be used to prevent errors from aborting command interpretation. catch calls the Tcl interpreter recursively to execute <command>, and always returns a TCL_OK code, regardless of any errors that might occur while executing <command>. The return value from catch is a decimal string giving the code returned by the Tcl interpreter after executing <command>. This will be zero (TCL_OK) if there were no errors in command; otherwise it will have a non-zero value corresponding to one of the exceptional return codes. If the <varName> argument is given, then it gives the name of a variable; catch will set the value of the variable to the string returned from command (either a result or an error message).
  • This returns an integer that indicates how <command> completed:
    • 0
    Completed successfully; $<varName> contains the result of the command.
    1
    Generated an error; $<varName> contains the error message.
    2
    Executed "return"; $<varName> contains the argument passed to "return."
    3
    Executed "break"; $<varName> is empty.
    4
    Executed "continue"; $<varName> is empty.

See Also: protect.

concat

concat <arg1>+

Examples:

"concat $list1 $list2"
Merges the lists in $list1 and $list2 into a single list whose elements are the elements of the two lists.

Concatenates multiple list arguments into a single list.

  • This command treats each argument as a list and concatenates them into a single list. It permits any number of arguments. For example, the command
    • concat a b {c d e} {f {g h}}

    will return a b c d e f {g h} as its result.

  • There is a sometimes-subtle difference between this in the "list" command: Given two lists, "concat" will form a list whose n elements are the combined elements of the two component lists, while "list" will form a list whose 2 elements are the two lists. For example,
    •  concat a b {c d e} {f {g h}}

    yields the list

    a b c d e f {g h}

    but

    list a b {c d e} {f {g h}}

    yields

    a b {c d e} {f {g h}}

See Also: list.

continue

continue

Examples:

"continue"
Return to the top of the enclosing loop.

Skips the rest of the commands in the current loop iteration, continuing at the top of the loop.

  • Only the closest-enclosing loop can be continued via this command.
  • The <next> clause of the "for" command is not part of the current iteration, i.e. it will be executed even if you execute this command.
  • This command may be invoked only inside the body of a loop command such as for or foreach . It returns a TCL_CONTINUE code to signal the innermost containing loop command to skip the remainder of the loop's body but continue with the next iteration of the loop.

See Also: break, for.

defsubr

defsubr <name> <args> <body>

Examples:

"defsubr poof {arg1 args} {return [list $arg1 $args]}"
Defines a procedure poof that takes 1 or more arguments and merges them into a list of two elements.

This is the same as the "proc" command, except the new procedure's name may not be abbreviated when it is invoked.

  • Refer to the documentation for proc for more information.

error

error <message>

Examples:

"error {invalid argument}"
Generates an error, giving the not-so-helpful message "invalid argument" to the caller's caller.
  • Unless one of the procedures in the call stack has executed a "catch" command, all procedures on the stack will be terminated with <message> (and an indication of an error) being the result of the final one so terminated.
  • Any commands protected by the "protect" command will be executed.

See Also: return, catch.

eval

eval <body>

Examples:

"eval $mangled_command"
Evaluate the command contained in $mangled_command and return its result.

Evaluates the passed string as a command and returns the result of that evaluation.

  • eval takes one argument, which is a Tcl command (or collection of Tcl commands separated by newlines in the usual way). eval evaluates <body> by passing it to the Tcl interpreter recursively, and returns the result of the last command. If an error occurs inside <body> then eval returns that error.
  • This command is useful when one needs to cobble together a command from arguments or what have you. For example, if one of your arguments is a list of arguments to pass to another command, the only way to accomplish that is to say something like "eval [concat random-command $args]", which will form a list whose first element is the command to be executed, and whose remaining elements are the arguments for the command. "eval" will then execute that list properly.
  • If the executed command generates an error, "eval" will propagate that error just like any other command.

See Also: concat, list.

expr

expr <expression> [float]

Examples:

"expr 36*25"
Multiplies 36 by 25 and returns the result.
"expr $i/6 float"
Divides the number in $i by 6 using floating- point arithmetic; the result is a real number.
"expr 7.2*10 float"
Multiplies 7.2 by 10. Note that though the answer (72) is an integer, we need to pass the "float" keyword to make sure that the expression is interpreted correctly.

Evaluates an arithmetic expression and returns its value.

  • Most C operators are supported with the standard operator precedence.
  • If you use a Tcl variable in the expression, the variable may only contain a number; it may not contain an expression.
  • The result of any Tcl command, in square brackets ("[ ]") must be a number; it may not be an expression.
  • All the C and Esp radix specifiers are allowed.
  • Bitwise and boolean operators (!, &, ^, |, &&, ||, >>, <<, ~) are not permitted when the expression is being evaluated using floating-point arithmetic.

file

file dirname <name>
file exists <name>
file extension <name>
file isdirectory <name>
file isfile <name>
file readable <name>
file rootname <name>
file tail <name>
file writable <name>
file match <pattern>
file newer <name1> <name2>

Examples:

"file match /pcgeos/tcl/*.tcl"
Looks for all files/directories in /pcgeos/tcl whose name ends with ".tcl".
"file isdir $path"
See if the path stored in $path refers to a directory.
"file tail $path"
Return the final component of the path stored in $path

Performs various checks and manipulations of file and directory names.

  • The forward slash is the path separator for this command.
  • The predicate subcommands (executable, exists, isdirectory, isfile, owned, readable, and writable) all return 1 if the path meets the requirements, or 0 if it doesn't.
  • "file match" takes a pattern made from the same components as are described for "string match". It is not the same as the standard DOS wildcarding, where `.' serves to separate the root pattern from the extension pattern. For this command "*.*" would match only files that actually have an extension.
  • "file dirname" returns the directory portion of name . If name has no directory portion, this returns "."
  • "file rootname" returns all leading directory components of name , plus the text before its extension, without the "." that separates the name from the extension.
  • "file tail" returns all of the characters in name after the final forward slash, or name if it contains no forward slashes.
  • "file newer" returns 1 if name1 was modified after name2 . It returns 0 otherwise.

See Also: string.

for

for <start> <test> <next> <body>

Examples:

"for {var i 0} {$i < 10} {var i [expr $i+1]} {echo $i}"
Prints the numbers from 0 to 9.

This is Tcl's main looping construct. It functions similarly to the "for" in C.

  • <start> is a Tcl command string (which may involve multiple commands over multiple lines, if desired) that is executed once at the very start of the loop. It is always executed. If it returns an error, or contains a "break" command, no part of the loop will execute.
  • <test> is an arithmetic expression that is passed to the "expr" command. If the result is non-zero, the <body> is executed.
  • <next> is a Tcl command string (which may involve multiple commands over multiple lines, if desired) that is executed at the end of each iteration before <test> is evaluated again. If it returns an error, or contains a "break" command, no part of the loop will execute.
  • You can exit the loop prematurely by executing the "break" command in any of the three Tcl command strings (<start>, <next>, or <body>).
  • So long as there's no error, "for" always returns the empty string as its result.
  • If a continue command is invoked within <body> then any remaining commands in the current execution of <body> are skipped; processing continues by invoking the Tcl interpreter on <next>, then evaluating <test>, and so on. If a break command is invoked within <body>, then the for command will return immediately. The operation of break and continue are similar to the corresponding statements in C.

See Also: foreach, break, continue.

foreach

foreach <varname> <list> <body>

Examples:

"foreach el $list {echo poof = $el}"
Prints each element of the list $list preceded by the profound words "poof = "

This is a looping construct to easily iterate over all the elements of a list.

  • <body> is evaluated once for each element in <list>. Before each evaluation, the next element is placed in the variable <varName>.
  • You can exit the loop prematurely by executing the "break" command.
  • As long as there's no error, "foreach" always returns the empty string.
  • The break and continue statements may be invoked inside <body>, with the same effect as in the for command.

format

format <formatString> [<arg> ]*

This command generates a formatted string in the same way as the C sprintf procedure (it uses sprintf in its implementation). <formatString> indicates how to format the result, using % fields as in sprintf , and the additional arguments, if any, provide values to be substituted into the result. All of the sprintf options are valid; see the sprintf procedure in a C manual for more details. Each <arg> must match the expected type from the % field in <formatString>; the format command converts each argument to the correct type (floating, integer, etc.) before passing it to sprintf for formatting. The only unusual conversion is for %c; in this case the argument must be a decimal string, which will then be converted to the corresponding ASCII character value. format does backslash substitution on its <formatString> argument, so backslash sequences in <formatString> will be handled correctly even if the argument is in braces. The return value from format is the formatted string.

global

global <varname>+

Examples:

"global attached"
When next the "attached" variable is fetched or set, get it from the global scope, not the local one.

Declares the given variables to be from the global scope.

  • For the duration of the procedure in which this command is executed (but not in any procedure it invokes), the global variable of the given name will be used when the variable is fetched or set.
  • If no global variable of the given name exists, the setting of that variable will define it in the global scope.
  • This command is ignored unless a Tcl procedure is being interpreted. If so, then it declares the given <varname>'s to be global variables rather than local ones. For the duration of the current procedure (and only while executing in the current procedure), any reference to any of the <varname> values will be bound to a global variable instead of a local one.

See Also: var.

if

if <test> [then] <trueBody>

             (elif <test> [(then)] <trueBody>)*

             [[else] <falseBody>]

Examples:

"if {$v > 3} {echo yes} {echo no}"
Prints "yes" if $v is greater than 3, else it prints "no".
"if {$v > 3} then {echo yes} else {echo no}"
Ditto.
"if {$v > 3} then {echo yes} elif {$v == 3} {echo maybe} else {echo no}"

This is Tcl's conditional, as you'd expect from its name.

  • The "then" and "else" keywords are optional, intended to delineate the different sections of the command and make the whole easier to read.
  • The "elif" keyword is mandatory if you want to perform additional tests.
  • The <expr> arguments are normal Tcl expressions. If the result is non-zero, the appropriate <truebody> is executed. If none of the <expr> arguments evaluates non-zero, <falsebody> is executed.
  • If a <truebody> is empty and the test evaluated non-zero, "if" will return the result of the test. Otherwise "if" returns the result from last command executed in whichever <truebody> or <falsebody> argument was finally executed. It returns an empty string if no <expr> evaluated non-zero and no <falsebody> was given.
  • The if command evaluates <test> as an expression in the same way that expr evaluates its argument. If the result is non-zero then <trueBody> is called by passing it to the Tcl interpreter. Otherwise <falseBody> is executed by passing it to the Tcl interpreter. <falseBody> is also optional; if it isn't specified then the command does nothing if <test> evaluates to zero. The return value from if is the value of the last command executed in <trueBody> or <falseBody> or the empty string if <test> evaluates to zero and <falseBody> isn't specified. Alternative test conditions can be added by adding <elif> arguments.

See Also: expr.

index

index <value> <index> [chars]

Examples:

"index {a b c} 1"
Extracts "b" from the list.
"index {hi mom} 3 char"
Extracts "m" from the string.

"index" is used to retrieve a single element or character from a list or string.

  • Elements and characters are numbered from 0.
  • If you request an element or character from beyond the end of the <list> or <string>, you'll receive an empty list or string as a result.
  • If the <chars> keyword isn't specified, then index treats <value> as a list and returns the <index>'th field from it. In extracting the field, index observes the same rules concerning braces and backslashes as the Tcl command interpreter; however, variable substitution and command substitution do not occur. If the <chars> keyword is specified (or any abbreviation of it), then <value> is treated as a string and the command returns the <index>'th character from it (or the empty string if there aren't at least <index>+1 characters in the string).

info

info args <procname> [<pattern>]
info arglist <procname>
info body <procname>
info cmdcount
info commands [<pattern>]
info default <procname> <arg> <varname>
info globals [<pattern>]
info locals [<pattern>]
info procs [<pattern>]
info vars [<pattern>]

Examples:

"info args fmtval"
Retrieves the names of the arguments for the "fmtval" command so you know in what order to pass things.
"info body print-frame"
Retrieves the string that is the body of the "print-frame" Tcl procedure.
"info commands reg "
Retrieves a list of commands whose names contain the string "reg".

This command provides information about a number of data structures maintained by the Tcl interpreter.

  • All the <pattern> arguments are standard wildcard patterns as are used for the "string match" and "case" commands. See "string" for a description of these patterns.
  • "info args" returns the complete list of arguments for a Tcl procedure, or only those matching the <pattern>, if one is given. The arguments are returned in the order in which they must be passed to the procedure.
  • "info arglist" returns the complete list of arguments, and their default values, for a Tcl procedure.
  • "info body" returns the command string that is the body of the given Tcl procedure.
  • "info cmdcount" returns the total number of commands the Tcl interpreter has executed in its lifetime.
  • "info commands" returns the list of all known commands, either built-in or as Tcl procedures, known to the interpreter. You may also specify a pattern to restrict the commands to those whose names match the pattern.
  • "info default" returns non-zero if the argument named <arg> for the given Tcl procedure has a default value. If it does, that default value is stored in the variable whose name is <varname>.
  • "info globals" returns the list of all global variables accessible within the current variable scope (i.e. only those that have been declared global with the "global" command, unless you issue this command from the command-line, which is at the global scope), or those that match the given pattern.
  • "info locals" returns the list of all local variables, or those that match the given pattern.
  • "info procs" returns the list of all known Tcl procedures, or those that match the given pattern.
  • "info vars" returns the list of all known Tcl variables in the current scope, either local or global. You may also give a pattern to restrict the list to only those that match.

See Also: proc, defcmd, defcommand, defsubr.

length

length <value> [<chars>]

Examples:

"length $args"
Returns the number of elements in the list $args
"length $str char"
Returns the number of characters in the string $str

Determines the number of characters in a string, or elements in a list.

  • If (chars) isn't specified, length treats <value> as a list and returns the number of elements in the list. If <chars> is specified (or any abbreviation of it), then length treats <value> as a string and returns the number of characters in it (not including the terminating null character).

See Also: index, range.

list

list <arg>+

Examples:

"list a b {c d e} {f {g h}}"
Returns the list "a b {c d e} {f {g h}}"

Joins any number of arguments into a single list, applying quoting braces and backslashes as necessary to form a valid Tcl list.

  • If you use the "index" command on the result, the 0th element will be the first argument that was passed, the 1st element will be the second argument that was passed, etc.
  • The difference between "list" and "concat" is subtle. Given the above arguments, "concat" would return "a b c d e f {g h}".
  • This command returns a list comprised of all the <args>. It also adds braces and backslashes as necessary, so that the index command may be used on the result to re-extract the original arguments, and also so that eval may be used to execute the resulting list, with <arg1> comprising the command's name and the other <args> comprising its arguments.

See Also: concat, index, range.

proc

proc <name> <args> <body>

Examples:

"proc poof {{arg1 one} args} {return [list $arg1 $args]}"
Defines a procedure poof that takes 0 or more arguments and merges them into a list of two elements. If no argument is given, the result will be the list {one {}}

Defines a new Tcl procedure that can be invoked by typing a unique abbreviation of the procedure name.

  • Any existing procedure or built-in command with the same name is overridden.
  • <name> is the name of the new procedure and can consist of pretty much any character (even a space or tab, if you enclose the argument in braces).
  • <args> is the (possibly empty) list of formal parameters the procedure accepts. Each element of the list can be either the name of local variable, to which the corresponding actual parameter is assigned before the first command of the procedure is executed, or a two-element list, the first element of which is the local variable name, as above, and the second element of which is the value to assign the variable if no actual parameter is given.
  • If the final formal parameter is named "args", the remaining actual parameters from that position on are cobbled into a list and assigned to the local variable $args. This allows a procedure to receive a variable number of arguments (even 0, in which case $args will be the empty list).
  • If the only formal parameter is "noeval", all the actual parameters are merged into a list and assigned to $noeval. Moreover, neither command- nor variable-substitution is performed on the actual parameters.
  • The return value for the procedure is specified by executing the "return" command within the procedure. If no "return" command is executed, the return value for the procedure is the empty string.
  • Whenever the new command is invoked, the contents of <body> will be executed by the Tcl interpreter. <args> specifies the formal arguments to the procedure. It consists of a list, possibly empty, each of whose elements specifies one argument. Braces and backslashes may be used in the usual way to specify complex default values.
  • When <name> (or a unique abbreviation of same) is invoked, a local variable will be created for each of the formal arguments to the procedure; its value will be the value of corresponding argument in the invoking command or the argument's default value. Arguments with default values need not be specified in a procedure invocation. However, there must be enough actual arguments for all the formal arguments that don't have defaults, and there must not be any extra actual arguments (unless the "args" keyword was used).
  • When <body> is being executed, variable names normally refer to local variables, which are created automatically when referenced and deleted when the procedure returns. One local variable is automatically created for each of the procedure's arguments. Global variables can only be accessed by invoking the global command.
  • The proc command itself returns the null string.

See Also: defsubr, return.

protect

protect <body> <cleanup>

Examples:

"protect {
var s [stream open $file w]
# do stuff with the stream
} {

catch {stream close $s}

}"
Perform some random operations on a file making sure the stream gets closed, even if the user types control-C.

Allows one to ensure that clean-up for a sequence of commands will always happen, even if the user types control-C to interrupt the command.

  • Since the interrupt can come at any time during the <body>, the <cleanup> command string should not rely on any particular variables being set. Hence the "catch" command used in the <cleanup> clause of the example.
  • The <cleanup> clause will also be executed if any command in the <body> generates an error.

See Also: catch.

range

range <value> <first> <last> [chars]

Examples:

"range {a b c} 1 end"
Returns {b c} (element 1 to the end)
"range {hi mom} 3 end chars"
Returns "mom"

Extracts a range of characters from a string, or elements from a list.

  • If you give an ending index that is greater than the number of elements in the list (characters in the string), it will be adjusted to be the index of the last element (character).
  • If you give a starting index that is greater than the number of elements in the list (characters in the string), the result will be the empty list (string).
  • You can give <end> as "end" (without the quotation marks, of course) to indicate the extraction should go to the end of the list (string).
  • The range is inclusive, so "range {a b c} 0 0" returns "a".
  • Neither index may be less than 0 or "range" will generate an error.
  • Return a range of fields or characters from value. If the chars keyword, or any abbreviation of it, is specified, then range treats <value> as a character string and returns characters <first> through <last> of it, inclusive. If <last> is less than <first> then an empty string is returned. Note: range value first first does not always produce the same results as index value first (although it often does for simple fields that are not enclosed in braces); it does, however, produce exactly the same results as list [index value first] .

See Also: index.

return

return [<value>]

Examples:

"return $val"
Returns the string in $val as the value for the current Tcl procedure.

Causes an immediate return from the current Tcl procedure, with or without a value.

  • Every Tcl procedure returns a string for a value. If the procedure was called via command substitution (having been placed between square brackets as the argument to another command), the return value takes the place of the command invocation.
  • Execution of the current procedure terminates immediately, though any <cleanup> clause for a containing "protect" command will still be executed.
  • If no "return" command is invoked within a Tcl procedure, the procedure returns the empty string by default.
  • This command may be invoked only when a procedure call is in progress. It causes the current procedure to return immediately. If <value> is specified, it will be the return value from the procedure. Otherwise the current procedure will return the empty string.

See Also: error, proc, defsubr, defcommand, defcmd.

scan

scan <string> <format> [<varname1> ]*

Examples:

"scan $input {my name is %s} name"
Trims the leading string "my name is " from the string in $input and stores the rest of the string within the variable $name

"scan" parses fields from an input string, given the string and a format string that defines the various types of fields. The fields are assigned to variables within the caller's scope.

  • The <format> string consists of literal text, which must be matched explicitly, and field definitions. The <varName> arguments are names of variables to which each successive field value is assigned.
  • A single whitespace character (space or tab) will match any number of whitespace characters in the input string. Fields are specified as for the standard C library routine "sscanf":
    • %c
    A single character. The field value stored is the decimal number of the ASCII code for the character scanned. So if the character were a space, the variable would receive the string "32".
    %d
    A signed decimal integer is parsed and stored.
    %o
    An octal integer is parsed and stored, as a decimal number.
    %x
    A hexadecimal integer is parsed and stored, as a decimal number.
    %i
    A signed integer, following the standard C radix-specification standard, is parsed and stored as a decimal number.
    %f
    A floating-point number is parsed as a "float" and stored without exponent, unless the exponent is less than -4.
    %s
    A whitespace-terminated string is parsed and stored.
    %[<char-class>]
    A string consisting only of the characters in the given character class (see "string match" for details on character classes) is parsed and stored. The normal leading-whitespace skipping is suppressed.
    %%
    Matches a single percent sign in the input.
  • If the % of a field specifier is followed by an *, the field is parsed as usual, consuming characters from the string, but the result is not stored anywhere and you should not specify a variable to receive the value.
  • The maximum length of a field may be specified by giving a decimal number between the % and the field-type character. So "%10s" will extract out a string of at most 10 characters.
  • There is a limit of 5 fields.

See Also: format.

source

source <fileName>

Examples:

"source coolness"
Evaluates all commands within the file "coolness.tcl" in the current directory.

Reads and evaluates commands from a file.

  • If <file> has no extension and doesn't exist, "source" will append ".tcl" to the end and try and read that file.
  • The return value of source is the return value of the last command executed from the file. If an error occurs in executing the contents of the file, then the source command will return that error.

string

string compare<string1> <string2> [no_case]
string first<substring> <string> [no_case]
string last<substring> <string> [no_case]
string match<string> <pattern>
string subst <string> <search> <replace> [global]

Examples:

"if {[string c [index $args 1] all] == 0}"
Do something if the 2nd element of the list in $args is the string "all".
"while {[string m [index $args 0] -*]}"
Loop while the first element of the list in $args begins with a hyphen.

Examine strings in various ways.

  • "string subst" searches <string> for occurrences of <search> and replaces them with <replace>. If 5th argument is given as "global" (it may be abbreviated), then all (non-overlapping) occurrences of <search> will be replaced. If 5th argument is absent, only the first occurrence will be replaced.
  • "string compare" compares the two strings character-by-character. It returns -1, 0, or 1 depending on whether <string1> is lexicographically less than, equal to, or greater than <string2>. If the no_case parameter is passed than it does a case insensitive compare.
  • "string first" searches <string> for the given <substring>. If it finds it, it returns the index of the first character in the first such match. If <substring> isn't part of <string>, it returns -1. If the no_case parameter is passed it does the search ignoring case.
  • "string last" is much like "string first", except it returns the index of the first character of the last match for the <substring> within <string>. If there is no match, it returns -1.
  • "string match" compares <string> against <pattern> and returns 1 if the two match, or 0 if they do not. For the strings to match, their contents must be identical, except that the following special sequences may appear in <pattern> with the following results:
    • *
    Matches any sequence of characters, including none.
    ?
    Matches any single character
    [<char-class>]
    Matches a single character within the given set. The elements of the set are specified as single characters, or as ranges of the form <start>-<end>. Thus [0-9x] matches a single character that is a numeric digit or the letter x.
    [^<char-class>]
    Matches a single character not within the given set.
    \*
    Matches an asterisk.
    \?
    Matches a question mark.
    \[
    Matches an open-bracket.

See Also: case.

uplevel

uplevel <level> <body>

uplevel <function> <body>

Examples:

"uplevel print-frame {var found1}"
Sets $found to 1 within the variables belonging to the nearest invocation of print-frame on the call stack.
"uplevel 0 {var foo-table}"
Retrieves the value of the global variable foo-table.
"uplevel 1 {var found 1}"
Sets $found to 1 within the scope of the procedure that called the one executing the "uplevel" command.

Provides access to the variables of another procedure for fairly specialized purposes.

  • <level> is a signed integer with the following meaning:
    • > 0
    Indicates the number of scopes to go up. For example, if you say "uplevel 1 {var foo 36}", you would modify (or create) the variable "foo" in your caller's scope.
    <= 0
    Indicates the number of scopes to go down from the global one. "uplevel 0 <body>" will execute <body> in the top-most scope, which means that no local variables are involved, and any variables created by the commands in <body> persist as global variables.
  • <function> is the name of a function known to be somewhere on the call stack. If the named function isn't on the call stack anywhere, "uplevel" generates an error.
  • <body> may be spread over multiple arguments, allowing the command to be executed to use variables local to the current procedure as arguments without having to use the "list" command to form the <body>.

See Also: global.

var

var <varname>
var (<name> <value>)+

Examples:

"echo [var poof]"
Prints the value stored in the variable "poof"
"var a b c d"
Assigns the string "b" to the variable "a", and the string "d" to the variable "c".
"var yes $no no $yes"
Exchanges the values of the "yes" and "no" variables

This is the means by which variables are defined in Tcl. Less often, it is also used to retrieve the value of a variable (usually that's done via variable substitution).

  • If you give only one argument, the value of that variable will be returned. If the variable has never been given a value, the variable will be created and assigned the empty string, then the empty string will be returned.
  • You can set the value of a variable by giving the value as the second argument, after the variable name. No value is returned by the "var" command in this case.
  • You can assign values to multiple variables "in parallel" by giving successive name/value pairs.
  • If invoked in a procedure on a variable that has not been declared global (using the "global" command), this applies to the local variable of the given name, even if it has no value yet.

See Also: global.

| Up: GEOS SDK TechDocs | Up | Prev: 5.1 Notation | Next: 6 Coding


Coding

Tool Command Language: 6 Coding

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 Commands | Next: 7 Using a New Command

This section provides information about the features and commands of Tcl that are important to know when using Swat, and the features and commands of Swat that are important to know when using Tcl. These features should be kept in mind while programming in Tcl because, if used properly, they make programming, debugging, and understanding existing commands much easier.This section will contain the following parts:

  • Swat Data Structures
    Descriptions of the major data structures and the commands that access them.
  • Examples

Swat Data Structure Commands

Examples

| Up: GEOS SDK TechDocs | Up | Down | Prev: 5 Commands | Next: 7 Using a New Command


Swat Data Structure Commands

Tool Command Language: 6.1 Coding: Swat Data Structure Commands

| Up: GEOS SDK TechDocs | Up | Prev: 6 Coding | Next: 6.2 Examples
symbol, type, patient, handle, brk, cbrk, event, thread, src, cache, table

This section contains information about Swat's built-in data structures and the commands that access them. These commands examine and modify vital information about the state of GEOS while it is running under Swat.

brk

brk <addr> [<command>]
brk pset <addr> [<command>]
brk aset <addr> [<command>]
brk tset <addr> [<command>]
brk clear <break>*
brk delete <break>*
brk enable <break>*
brk disable <break>*
brk address <break>
brk list [<addr>]
brk debug [<flag>]
brk isset <addr>
brk cond <break> <condition>*
brk cmd <break> [<command>]
brk delcmd <break> [<command>]

Examples:

"brk WinOpen"
Sets the machine to stop unconditionally when any thread calls WinOpen.
"brk pset WinOpen"
Sets the machine to stop when any thread for the current patient calls WinOpen.
"brk tset WinOpen"
Sets the machine to stop when any thread for the current patient calls WinOpen, and deletes the breakpoint when the machine next stops.
"brk enable 1 3-5"
Re-enables breakpoints 1, 3, 4, and 5
"brk clear 2-"
Clears all breakpoints from number 2 onward.
"brk cond 3 cx=42"
Sets breakpoint 3 to be conditional, stopping when the machine reaches the breakpoint's address with CX being 42.
"brk cond 2 (ss:0)!=1b80h"
Sets breakpoint 2 to be conditional, stopping when the machine reaches the breakpoint's address with the word at ss:0 not being 1b80h. Note that the "ss" is the value of the ss register when the "brk cond" command is executed, not when the breakpoint is reached.

Allows you to specify that execution should stop when it reaches a particular point. These breakpoints can be conditional, and can execute an arbitrary Tcl command, which can say whether the machine is to remain stopped, or continue on its way.

  • Once you've set a breakpoint, "brk" will return to you a token for that breakpoint that begins with "brk" and ends with a number. When you refer to the breakpoint, you can use either the full name (as you'll usually do from a Tcl procedure), or just the number.
  • Breakpoints have four attributes: the address at which they are set, the condition set on their being recognized, the Tcl command string to execute when they are recognized, and the Tcl command string to execute when they are deleted.
  • The condition is set either when the breakpoint is set, using the "cbrk" command, or after you've set the breakpoint, by invoking the "brk cond" command.
  • A breakpoint's condition is evaluated (very quickly) on the PC and can check only word registers (the 8 general registers, the three segment registers other than CS, and the current thread; each register may be checked only once in a condition) and a single word of memory. Each <condition> argument is of the form "<reg><op><value>". <reg> is one of the 16-bit machine registers, "thread" (for the current thread), or the address of a word of memory to check, enclosed in parentheses. <op> is a relational operator taken from the following set:
    =
    equal-to
    !=
    not-equal-to
    > < >= <=
    unsigned greater-than, less-than, greater-or-equal, and less-or-equal
    +> +< +>= +<=
    signed greater-than, less-than, greater-or-equal, and less-or-equal

    <value> is a regular Swat address expression. If it is handle-relative, and the <reg> is one of the three non-CS segment registers, the condition will be for the segment of that handle and will change automatically as the handle's memory shifts about on the heap. Similar things will happen if you specify a number as the <value> for a segment register and the number is the current segment of a block on the heap.
  • If you give no <condition> argument to the "brk cond" command, you will remove any condition the breakpoint might have, making it, therefore, unconditional.
  • If a breakpoint is given an associated <command> string, it will be evaluated before the breakpoint is taken. If the result of the evaluation is an error, a non-numeric string, or a numeric string that's non-zero, the breakpoint will be taken. Otherwise, the machine will be allowed to continue (so long as no other breakpoint command or other part of Swat insists that it remain stopped). You can use this to simply print out information when execution reaches the breakpoint address without interrupting the machine's execution.
  • The global variable "breakpoint" contains the name of the breakpoint whose command is being evaluated while that command is being evaluated.
  • You can change the command associated with a breakpoint with the "brk cmd" command. If you give no <command> argument, then no command will be executed and the breakpoint will always be taken, so long as any associated condition is also met.
  • If a breakpoint has both a condition and a command, the command will not be executed until the condition has been met, unless there's another breakpoint at the same address with a different, or no, condition.
  • You can set a breakpoint to last only during the next continuation of the machine by calling "brk tset". The breakpoint thus set will be removed when next the machine comes to a full stop, regardless of why it stopped (i.e. if it hits a different breakpoint, the temporary breakpoint will still be removed). The breakpoint will only be taken if the thread executing when it is hit is owned by the patient that was current when the breakpoint was set.
  • Each <break> argument to the "brk clear", "brk enable" and "brk disable" commands can be either a single breakpoint token (or number), or a range of the form <start>-<end>, where either <start> or <end> may be absent. If <start> is missing, the command affects all breakpoints from number 1 to <end>. If <end> is missing, the command affects all breakpoints from <start> to the last one in existence.
  • If you give no <break> argument to "brk clear", "brk enable" or "brk disable", the command will apply to all breakpoints that are specific to the current patient, i.e. that were set with the "brk pset" command, unless the current patient is the kernel, in which case they will apply to all breakpoints that are specific to no patient (i.e. those set with the "brk aset" or "brk <addr>" commands).
  • "brk address" returns the address expression for where the breakpoint is set. This will usually be of the form ^h<handle-id>:<offset>, with both <handle-id> and <offset> in hex (followed by an "h", of course). If the breakpoint is set at an absolute address, you will get back only a single hex number, being the linear address at which the breakpoint is set.
  • If you type "brk list" with no argument, Swat will print out a listing of the currently-active breakpoints. If you give an <addr> (address expression) argument, however, you'll be returned a list of the breakpoints set at the given address. If there are no breakpoints there, the list will be empty.
  • As a shortcut, you can invoke "brk isset" to see if any breakpoints are set at the given address, if you're not interested in which ones they are.

cache

cache create (lru|fifo) <maxSize> [<flushProc>]
cache destroy <cache> [flush|noflush]
cache lookup <cache> <key>
cache enter <cache> <key>
cache invalone <cache> <entry>
cache invalall <cache> [flush|noflush]
cache key <cache> <entry>
cache size <cache>
cache maxsize <cache>
cache setmaxsize <cache> <maxSize>
cache getval <cache> <entry>
cache setval <cache> <entry> <value>

Examples:

"var cache [cache create lru 10]"
Creates a cache of 10 items that are flushed on a least-recently-used basis. The returned token is saved for later use.
"var entry [cache lookup $cache mom]"
Sees if an entry with the key "mom" is in the cache and saves its entry token if so.
"echo mom=[cache getval $cache $entry]"
Retrieves the value stored in the entry for "mom" and echoes it.
"cache invalone $cache $entry"
Flushes the entry just found from the cache.
"cache destroy $cache"
Destroys the cache.

The cache command, as the name implies, maintains a cache of data that is keyed by strings. When a new entry is added to an already-full cache, an existing entry is automatically flushed based on the usage message with which the cache was created: lru (last recently used) or fifo (first in, first out). If lru , the least-recently-used entry is flushed; if fifo , the oldest entry is flushed.

  • Unlike the "table" command, the "cache" command returns tokens for entries, not their values. This allows entries to be individually flushed or their values altered.
  • If a <flushProc> is specified when the cache is created, the procedure will be called each time an entry is flushed from the cache. It will be called "<flushProc> <cache> <entry>" where <cache> is the token for the cache, and <entry> is the token for the entry being flushed.
  • If the maximum size of a full cache is reduced, entries will be flushed from the cache to bring it down to the new maximum size. The <flushProc> will be called for each of them.
  • If the values stored in the cache entries should not be freed when the cache is destroyed, pass "noflush" to "cache destroy". The default is to flush (and hence call the <flushProc>) all entries from the cache before it is destroyed.
  • If the values stored in the cache entries should not be freed when the cache is flushed, pass "noflush" to "cache invalall". The default is to call the <flushProc> for each entry in the cache before it is actually flushed.
  • If an entry is not found in the cache, "cache lookup" will return an empty string.
  • When an entry is created, "cache enter" returns a 2-list containing the entry token as its first element, and an integer, as its second element, that is either non-zero or 0, to tell if the entry is new or was already present, respectively.

cbrk

cbrk <addr> <condition>*
cbrk aset <addr> <condition>*
cbrk tset <addr> <condition>*
cbrk clear <break>*
cbrk delete <break>*
cbrk enable <break>*
cbrk disable <break>*
cbrk address <break>
cbrk list [<addr>]
cbrk debug [<flag>]
cbrk isset <addr>
cbrk cond <break> <condition>*
cbrk cmd <break> [<command>]
cbrk delcmd <break> [<command>]

Examples:

"cbrk WinOpen di=1b80h"
Stops the machine when execution reaches WinOpen() with di set to 1b80h.

Allows you to set fast conditional breakpoints.

  • All these subcommands function the same as for the "brk" command, with the exception of the "aset" and "tset" commands, which expect the condition for the breakpoint, rather than an associated command.
  • There are a limited number of these sorts of breakpoints that can be set in the PC (currently 8), so they should be used mostly for heavily-travelled areas of code (e.g. inner loops, or functions like ObjCallMethodTable() in the kernel).
  • For more information on the subcommands and the format of arguments, see the documentation for the "brk" command.

event

event <subcommand>

The event command provides access to Swat's internal events. The subcommands are as follows:

handle <eventName> <handler> [<data>]
The <handler> procedure is invoked each time an event of type <eventName> is dispatched. The handler receives two arguments: an event-specific piece of data, and the given <data>. A handler procedure should be declared
proc <handler> {arg data} {<body>}

The handle subcommand returns an <event> for later use in deleting it. The <handler> should return one of event_handled , event_not_handled , or event_stop_handling . If it returns event_stop_handling , the event will not be dispatched to any other handlers of the event.

delete <event>
Deletes the given event handler given by the event handle command.
dispatch <eventName> <arg>
Dispatches the given event with the given <arg> to all handlers of that event. If <eventName> is a pre-defined event type, <arg> will be converted to the appropriate type before being dispatched. Otherwise it is passed as a string.
create
Returns a number that represents a new event type. Handlers may then be defined for and events dispatched of the new type.
list
Lists all Tcl-registered events by event-name and handler function.

The events which are currently defined are:

FULLSTOP
Generated when patient stops for a while. Argument is string telling why the patient stopped.
CONTINUE
Generated just before the patient is continued. The argument is non-zero if going to single-step.
TRACE
Generated when the execution of a source line completes and the patient is in line-trace mode.
START
Generated when a new patient/thread is created. Argument is patient token of the patient involved.
STACK
Current stack frame has changed. The argument is non-zero if the stack change comes from a change in patients/threads or zero if the change comes from actually going up or down the stack in the current patient.
DETACH
Detaching from the PC. The argument is always zero.
RESET
Returning to the top level. The argument is always zero.
ATTACH
Attached to the PC. The argument is always zero.
RELOAD
Kernel was reloaded. The argument is always zero.
CHANGE
Current patient has changed. The argument is the token for the previous patient.
STEP
Machine has stepped a single instruction. The argument is the value to pass to patient stop if you wish the machine to stay stopped.
STOP
Machine has hit a breakpoint. The argument is the value to pass to patient stop if you wish the machine to stay stopped.
INT
Machine has hit some other interrupt that's being caught. The argument is the interrupt number. The machine will remain stopped unless it is continued with continue-patient.

handle

handle lookup <id>
handle find <address>
handle all
handle nointerest <interest-record>
handle interest <handle> <proc> [<data>+]
handle segment <handle>
handle size <handle>
handle state <handle>
handle owner <handle>
handle patient <handle>
handle other <handle>
handle id <handle>
handle isthread <handle>
handle iskernel <handle>
handle isfile <handle>
handle isvm <handle>
handle ismem <handle>

Examples:

"handle lookup [read-reg bx]"
get the handle token for the handle whose ID is in the BX register.
"handle interest $h ob-interest-proc [concat si=$chunk $message]"
call ob-interest-proc, passing the list {si=$chunk $message}, whenever the state of the handle whose token is in $h changes.
"handle patient $h"
get the token for the patient that owns the handle whose token is in $h
"handle all"
get the list of the ID's of all handles currently in Swat's handle table.

The "handle" command provides access to the structures Swat uses to track memory and thread allocation on the PC.

  • As with most other commands that deal with Swat structures, you use this one by calling a lookup function (the "lookup" and "find" subcommands) to obtain a token that you use for further manipulations. A handle token is also returned by a few other commands, such as addr-parse.
  • Handle tokens are valid only until the machine is continued. If you need to keep the token for a while, you will need to register interest in the handle using the "interest" subcommand. Most handles tokens will simply be cached while the machine is stopped and flushed from the cache when the machine continues. Only those handles for which all state changes must be known remain in Swat's handle table. For example, when a conditional breakpoint has been registered with the stub using the segment of a handle, the condition for that breakpoint must be updated immediately should the memory referred to by the handle be moved, swapped or discarded. Keeping the number of tracked handles low reduces the number of calls the stub must make to tell Swat about handle-state changes.
  • The <id> passed to the "lookup" subcommand is an integer. Its default radix is decimal, but you can specify the radix to use in all the usual ways. The value returned is the token to use to obtain further information about the handle.
  • "handle size" returns the number of bytes allocated to the handle.
  • "handle segment" returns the handle's segment (if it's resident) in decimal, as it's intended for use by Tcl programs, not people.
  • "handle owner" returns the token of the handle that owns the given handle, not its ID.
  • "handle all" returns a list of handle ID numbers not a list of handle tokens. The list is only those handles currently known to Swat.
  • "handle interest" tells Swat you wish to be informed when the handle you pass changes state in some way. The procedure <proc> will be called with two or more arguments. The first is the token of the handle whose state has changed, and the second is the state change the handle has undergone, taken from the following set of strings:
    • swapin
    Block swapped in from disk/memory
    load
    Resource freshly loaded from disk
    swapout
    Block swapped to disk/memory
    discard
    Block discarded
    resize
    Block changed size and maybe moved
    move
    Block moved on heap
    free
    Block has been freed
    fchange
    Block's HeapFlags changed

    Any further arguments are taken from the <data>+ arguments provided when you expressed interest in the handle. This subcommand returns a token for an interest record that you pass to "handle nointerest" when you no longer care about the handle. When the block is freed (the state change is "free"), there is no need to call "handle nointerest" as the interest record is automatically deleted.

  • "handle state" returns an integer indicating the state of the handle. The integer is a mask of bits that mean different things:

    • When the integer is AND-ed with the mask for Type (0xf8000), the following values indicate the following types of handles:

  • "handle other" returns the handle's otherInfo field. Note: This isn't necessarily the otherInfo field from the PC. E.g., for resource handles, it's the symbol token of the module for the handle.

patient

patient find <name>
patient name [<patient>]
patient fullname [<patient>]
patient data [<patient>]
patient threads [<patient>]
patient resources [<patient>]
patient libs [<patient>]
patient path [<patient>]
patient all
patient stop [<addr>]

Examples:

"patient find geos"
Returns the patient token for the kernel, if it's been loaded yet.
"patient fullname $p"
Returns the permanent name for the patient whose token is stored in the variable p.
"patient stop $data"
Tells the dispatcher of the STEP event that it should keep the machine stopped when the STEP event has been handled by everyone.

This command provides access to the various pieces of information that are maintained for each patient (geode) loaded by GEOS.

  • Subcommands may be abbreviated uniquely.
  • Swat always has the notion of a "current patient", whose name is displayed in the prompt. It is this patient that is used if you do not provide a token to one of the subcommands that accepts a patient token.
  • "patient name" returns the name of a patient. The name is the non- extension portion of the geode's permanent name. It will have a number added to it if more than one instance of the geode is active on the PC. Thus, if two GeoWrites are active, there will be two patients in Swat: "write" and "write2".
  • "patient fullname" returns the full permanent name of the patient. It is padded with spaces to make up a full 12-character string. This doesn't mean you can obtain the non-extension part by extracting the 0th element of the result with the "index" command, however; you'll have to use the "range" command to get the first 8 characters, then use "index" to trim the trailing spaces off, if you want to.
  • "patient data" returns a three-element list: {<name> <fullname> <thread-number>} <name> and <fullname> are the same as returned by the "name" and "fullname" subcommands. <thread-number> is the number of the current thread for the patient. Each patient has a single thread that is the one the user looked at most recently, and that is its current thread. The current thread of the current patient is, of course, the current thread for the whole debugger.
  • "patient threads" returns a list of tokens, one for each of the patient's threads, whose elements can be passed to the "thread" command to obtain more information about the patient's threads (such as their numbers, handle IDs, and the contents of their registers).
  • "patient resources" returns a list of tokens, one for each of the patient's resources, whose elements can be passed to the "handle" command to obtain more information about the patient's resources (for example, their names and handle IDs).
  • "patient libs" returns a list of patient tokens, one for each of the patient's imported libraries. The kernel has all the loaded device drivers as its "imported" libraries.
  • "patient path" returns the absolute path of the patient's executable.
  • "patient all" returns a list of the tokens of all the patients known to Swat.
  • "patient stop" is used only in STEP, STOP and START event handlers to indicate you want the machine to remain stopped once the event has been dispatched to all interested parties. <addr> is the argument passed in the STEP and STOP events. A START event handler should pass nothing.
  • A number of other commands provide patient tokens. "patient find" isn't the only way to get one.

src

src line <addr>
src read <file> <line>
src cache [<max>]
src addr <file> <line> [<patient>]

Examples:

"src line cs:ip"
Returns a two-list holding the source-line number, and the absolute path of the file in which it lies (not in this order), that encompasses CS:IP.
"src read /pcgeos/appl/sdk_c/hello/hello.goc 64"
Reads the single given source line from the given file.
"src addr icdecode.c 279"
Returns an address-list for the start of the code produced for the given line.
"src cache 10"
Allow 10 source files to be open at a time. This is the default.

The "src" command allows the Tcl programmer to manipulate the source- line maps contained in all the geodes' symbol files.

  • The "src line" commands returns its list as {<file> <line>}, with the <file> being absolute. If no source line can be found, the empty list is returned.
  • The <file> given to the "src read" command must be absolute, as the procedure using this command may well be wrong as to Swat's current directory. Typically this name will come from the return value of a "src line" command, so you needn't worry.
  • The line returned by "src read" contains no tabs and does not include the line terminator for the line (the <lf> for UNIX, or the <cr><lf> pair for MS-DOS).
  • "src addr" returns an address-list, as returned from "addr-parse", not an address expression, as you would pass to "addr-parse". If the <file> and <line> cannot be mapped to an address, the result will be the empty list.
  • The <file> given to "src addr" must be the name that was given to the assembler/compiler. This includes any leading path if the file wasn't in the current directory when the assembler/compiler was run.
  • "src cache" returns the current (or new) number of open files that are cached.

symbol

symbol find <class> <name> [<scope>]
symbol faddr <class> <addr>
symbol match <class> <pattern>
symbol scope <symbol>
symbol name <symbol>
symbol fullname <symbol>
symbol class <symbol>
symbol type <symbol>
symbol get <symbol>
symbol patient <symbol>
symbol tget <symbol>
symbol addr <symbol>
symbol foreach <scope> <class> <callback> [<data>]

Examples:

"symbol find type LMemType"
Locate a type definition named LMemType
"symbol faddr proc cs:ip"
Locate the procedure in which CS:IP lies.
"symbol faddr {proc label} cs:ip"
Locate the procedure or label just before cs:ip.
"symbol fullname $sym"
Fetch the full name of the symbol whose token is in the $sym variable.
"symbol scope $sym"
Fetch the token of the scope containing the passed symbol. This will give the structure containing a structure field, or the procedure containing a local variable, for example.

Provides information on the symbols for all currently-loaded patients. Like many of Swat's commands, this operates by using a lookup function (the "find", "faddr", "match", or "foreach" subcommands) to obtain a token for a piece of data that's internal to Swat. Given this token, you then use the other subcommands (such as "name" or "get") to obtain information about the symbol you looked up.

  • There are many types of symbols that have been grouped into classes that may be manipulated with this command. For a list of the symbol types and their meaning, type "help symbol-types". The type of a symbol can be obtained with the "symbol type" command.
  • The symbol classes are as follows:
    • type
    describes any structured type: typedef, struct, record, etype, union. Symbols of this class may also be used in place of type tokens (see the "type" command).
    field
    describes a field in a structured type: field, bitfield.
    enum
    describes a member of an enumerated type: enum, message.
    const
    a constant defined with EQU: const .
    var
    describes any variable symbol: var, chunk, locvar, class, masterclass, variantclass.
    locvar
    describes any local variable symbol: locvar, locstatic.
    scope
    describes any symbol that holds other symbols within it: module, proc, blockstart, struct, union, record, etype.
    proc
    describes only proc symbols.
    label
    describes any code-related symbol: label, proc, loclabel.
    onstack
    describes only symbols created by the directive.
    module
    describes only segment/group symbols.
    profile
    describes a symbol that marks where profiling code was inserted by a compiler or assembler.
  • The <class> argument for the "find", "faddr" and "match" subcommands may be a single class, or a space-separated list of classes. For example, "symbol faddr {proc label} CS:IP" would find the symbol closest to CS:IP (but whose address is still below or equal to CS:IP) that is either a procedure or a label.
  • The "symbol find" command locates a symbol given its name (which may be a symbol path).
  • The "symbol faddr" command locates a symbol that is closest to the passed address.
  • A symbol's "fullname" is the symbol path, from the current patient, that uniquely identifies the symbol. Thus if a procedure-local variable belongs to the current patient, the fullname would be
    <segment>::<procedure>::<name>
    where <segment> is the segment holding the <procedure>, which is the procedure for which the local variable named <name> is defined.
  • You can force the prepending of the owning patient to the fullname by passing <with-patient> as a non-empty argument ("yes" or "1" are both fine arguments, as is "with-patient").
  • The "symbol get" commands provides different data for each symbol class, as follows:
    • var, locvar, chunk: {<addr> <sclass> <type>}
    <addr> is the symbol's address as for the "addr" subcommand, <sclass> is the storage class of the variable and is one of static (a statically allocated variable), lmem (an lmem chunk), local (a local variable below the frame pointer), param (a local variable above the frame pointer), or reg (a register variable; address is the machine register number -- and index into the list returned by the "current-registers" command).
    object class: {<addr> <sclass> <type> <flag> <super>}
    first three elements same as for other variables. <flag> is "variant" if the class is a variant class, "master" if the class is a master class, or empty if the class is nothing special. <super> is the symbol token of the class's superclass.
    label-class: {<addr> (near|far)}
    <addr> is the symbol's address as for the "addr" subcommand. The second element is "near" or "far" depending on the type of label involved.
    field-class: {<bit-offset> <bit-width> <field-type> <struct-type>}
    <bit-offset> is the offset of the field from the structure/union/record's base expressed in bits. <bit-width> is the width of the field, in bits. <field-type> is the type for the field itself, while <struct-type> is the token for the containing structured type.
    const: {<value>}
    <value> is just the symbol's value.
    enum-class: {<value> <etype>}
    <value> is the symbol's value. <etype> is the enumerated type's symbol.
    blockstart, blockend: {<addr>}
    <addr> is the address bound to the symbol.
    onstack: {<addr> <data>}
    <addr> is the address at which the ON_STACK was declared. <data> is the arguments given to the ON_STACK directive.
    module: {<patient>}
    <patient> is the token for the patient owning the module.
  • A related command, "symbol tget" will fetch the type token for symbols that have data types (var-, field- and enum-class symbols).
  • "symbol addr" can be used to obtain the address of symbols that actually have one (var-, locvar- and label-class symbols). For locvar symbols, the address is an offset from the frame pointer (positive or negative). For var- and label-class symbols (remember that a procedure is a label-class symbols), the returned integer is the offset of the symbol within its segment.
  • "symbol patient" returns the token of the patient to which the symbol belongs.
  • "symbol foreach" will call the <callback> procedure for each symbol in <scope> (a symbol token) that is in one of the classes given in the list <class>. The first argument will be the symbol token itself, while the second argument will be <data>, if given. If <data> wasn't provided, <callback> will receive only 1 argument. <callback> should return 0 to continue iterating, or non-zero to stop. A non-integer return is assumed to mean stop. "symbol foreach" returns whatever the last call to <callback> returned.
  • By default, "symbol scope" will return the physical scope of the symbol. The physical scope of a symbol is the symbol for the segment in which the symbol lies, in contrast to the lexical scope of a symbol, which is where the name of the symbol lies. The two scopes correspond for all symbols but static variables local to a procedure. To obtain the lexical scope of a symbol, pass <lexical> as a non-zero number.

See Also: type.

table

table create [<initBuckets>]
table destroy <table>
table enter <table> <key> <value>
table lookup <table> <key>
table remove <table> <key>

Examples:

"var kitchen [table create 32]"
Create a new table with 32 hash buckets initially.
"table enter $t tbrk3 {1 2 3}"
Enter the value "1 2 3" under the key "tbrk3" in the table whose token is stored in the variable t.
"table lookup $t tbrk4"
Fetch the value, if any, stored under the key "tbrk4" in the table whose token is stored in the variable t.
"table remove $t tbrk3"
Remove the data stored in the table, whose token is stored in the variable t, under the key "tbrk3"
"table destroy $t"
Destroy the table $t and all the data stored in it.
(mess1:0) 159 => var yearTable [table create]
(mess1:0) 160 => table enter $yearTable synclavier 1979
(mess1:0) 161 => table enter $yearTable moog 1966
(mess1:0) 162 => table lookup $yearTable synclavier
1979
(mess1:0) 163 => var yearTable
1403188
(mess1:0) 164 => table lookup 1403188 moog
1966
(mess1:0) 165 => table remove $yearTable synclavier
(mess1:0) 166 => table lookup $yearTable synclavier
nil
(mess1:0) 167 => table destroy $yearTable

The "table" command is used to create, manipulate and destroy hash tables. The entries in the table are keyed on strings and contain strings, as you'd expect from Tcl.

  • The < initBuckets > parameter to "table create" is set based on the number of keys you expect the table to have at any given time. The number of buckets will automatically increase to maintain hashing efficiency, should the need arise, so < initBuckets > isn't a number that need be carefully chosen. It's best to start with the default (16) or perhaps a slightly larger number.
  • If no data are stored in the table under < key >, "table lookup" will return the string "nil", for which you can test with the "null" command.

thread

thread id <thread>
thread register <thread> <regName>
thread handle <thread>
thread endstack <thread>
thread number <thread>
thread all

Examples:

"thread register $t cx"
Fetches the value for the CX register for the given thread.
"thread number $t"
Fetches number swat assigned to thread when it was first encountered.
(mess1:0) 145 => patient threads
2667104
(mess1:0) 146 => thread id 2667104
11184
(mess1:0) 147 => thread all
767532 756068 1348520 1348868 1349216 1349748 1350236 1402096 1079392 2667104
(mess1:0) 148 => thread handle 756068
880428
(mess1:0) 149 => thread number 756068
0

Returns information about a thread, given its thread token. Thread tokens can be obtained via the "patient threads" command, or the "handle other" command applied to a thread handle's token.

  • Subcommands may be abbreviated uniquely.
  • "thread id" returns the handle ID, in decimal, of the thread's handle. This is simply a convenience.
  • "thread register" returns the contents of the given register in the thread when it was suspended. All registers except "pc" are returned as a single decimal number. "pc" is returned as two hexadecimal numbers separated by a colon, being the cs:ip for the thread. Note that GEOS doesn't actually save the AX and BX registers when it suspends a thread, at least not where Swat can consistently locate them. These registers will always hold 0xadeb unless the thread is the current thread for the machine (as opposed to the current thread for swat).
  • "thread handle" returns the token for the thread's handle.
  • "thread endstack" returns the maximum value SP can hold for the thread, when it is operating off its own stack. Swat maintains this value so it knows when to give up trying to decode the stack.
  • "thread number" returns the decimal number Swat assigned the thread when it first encountered it. The first thread for each patient is given the number 0 with successive threads being given the highest thread number known for the patient plus one.
  • "thread all" returns a list of tokens for all the threads known to Swat (for all patients).

type

type <basic-type-name>
type make array <length> <base-type>
type make pstruct (<field> <type>)+
type make struct (<field> <type> <bit-offset> <bit-length>)+
type make union (<field> <type>)+
type make <ptr-type> <base-type>
type delete <type>
type size <type>
type class <type>
type name <type> <var-name> <expand>
type aget <array-type>
type fields <struct-type>
type members <enum-type>
type pget <ptr-type>
type emap <num> <enum-type>
type signed <type>
type field <struct-type> <offset>
type bfget <bitfield-type>

Examples:

"type word"
Returns a type token for a word (2-byte unsigned quantity).
"type make array 10 [type char]"
Returns a type token for a 10-character array.
"type make optr [symbol find type GenBase]"
Returns a type token for an optr (4-byte global/local handle pair) to a "GenBase" structure.

Provides access to the type descriptions by which all PC-based data are manipulated in Swat, and allows a Tcl procedure to obtain information about a type for display to the user, or for its own purposes. As with other Swat commands, this works by calling one subcommand to obtain an opaque "type token", which you then pass to other commands.

  • Type tokens and symbol tokens for type-class symbols may be freely interchanged anywhere in Swat.
  • There are 11 predefined basic types that can be given as the <basic-type-name> argument in "type <basic-type-name>". They are: byte (single-byte unsigned integer), char (single-byte character), double (eight-byte floating-point), dword (four-byte unsigned integer), float (four-byte floating-point), int (two-byte signed integer), long (four-byte signed integer), sbyte (single-byte signed integer), short (two-byte signed integer), void (nothing. useful as the base type for a pointer type), and word (two-byte unsigned integer)
  • Most type tokens are obtained, via the "symbol get" and "symbol tget" commands, from symbols that are defined for a loaded patient. These are known as "external" type descriptions. "Internal" type descriptions are created with the "type make" command and should be deleted, with "type delete" when they are no longer needed.
  • An internal structure type description can be created using either the "pstruct" (packed structure) or "struct" subcommands. Using "pstruct" is simpler, but you have no say in where each field is placed (they are placed at sequential offsets with no padding between fields), and all fields must be a multiple of 8 bits long. The "struct" subcommand is more complex, but does allow you to specify bitfields.
  • "type make pstruct" takes 1 or more pairs of arguments of the form "<field> <type>", where <field> is the name for the field and <type> is a type token giving the data type for the field. All fields must be specified for the structure in this call; fields cannot be appended to an existing type description.
  • "type make struct" takes 1 or more 4-tuples of arguments of the form "<field> <type> <bit-offset> <bit-length>". <field> is the name of the field, and <type> is its data type. <bit-offset> is the offset, in bits, from the start of the structure (starting with 0, as you'd expect). <bit-length> is the length of the field, in bits (starting with 1, as you'd expect). For a bitfield, <type> should be the field within which the bitfield is defined. For example, the C declaration:
    •  struct {
     word a:6;
     word b:10;
     word c;
  }

    would result in the command "type make struct a [type word] 0 6 b [type word] 6 10 c [type word] 16 16", because a and b are defined within a word type, and c is itself a word.

  • "type make union" is similar to "type make pstruct", except all fields start at offset 0. Like "pstruct", this cannot be used to hold bitfields, except by specifying a type created via "type make struct" command as the <type> for one of the fields.
  • "type make array <length> <base-type>" returns a token for an array of <length> elements of the given <base-type>, which may be any valid type token, including another array type.
  • "type make <ptr-type> <base-type>" returns a token for a pointer to the given <base-type>. There are several different classes of pointers in GEOS:
    • nptr
    a near pointer. 16-bits. points to something in the same segment as the pointer itself.
    fptr
    a far pointer. 32-bits. segment in high word, offset in the low.
    sptr
    a segment pointer. 16-bits. contains a segment only.
    lptr
    an lmem pointer. 16-bits. contains a local-memory "chunk handle". data pointed to is assumed to be in the same segment as the lptr itself, but requires two indirections to get to it.
    hptr
    a handle pointer. 16-bits. a GEOS handle.
    optr
    an object pointer. 32-bits. contains a GEOS memory handle in the high word, and a GEOS local-memory chunk handle in the low.
    vptr
    a VM pointer. Its 32 bits contain a GEOS file handle in the high word and a GEOS VM block handle in the low word.
    vfptr
    a virtual far pointer. Its 32 bits contain a virtual segment in the high word and an offset in the low.
  • "type delete" is used to delete a type description created by "type make". You should do this whenever possible to avoid wasting memory.
  • Any type created by the "type make" command is subject to garbage collection unless it is registered with the garbage collector. If you need to keep a type description beyond the end of the command being executed, you must register it. See the "gc" command for details.
  • "type size" returns the size of the passed type, in bytes.
  • "type class" returns the class of a type, a string in the following set:
    • char
    for the basic "char" type only.
    int
    any integer, signed or unsigned.
    struct
    a structure, record, or union.
    enum
    an enumerated type.
    array
    an array, of course,
    pointer
    a pointer to another type.
    void
    nothingness. Often a base for a pointer.
    function
    a function, used solely as a base for a pointer.
    float
    a floating-point number.
  • Each type class has certain data associated with it that can only be obtained by using the proper subcommand.
  • "type aget" applies only to an array-class type token. It returns a four-element list: {<base-type> <low> <high> <index-type>} <base-type> is the type token describing elements of the array. <low> is the lower bound for an index into the array (currently always 0), <high> is the inclusive upper bound for an index into the array, and <index-type> is a token for the data type that indexes the array (currently always [type int]).
  • "type fields" applies only to a struct-class type token. It returns a list of four-tuples {<name> <offset> <length> <type>}, one for each field in the structure. <offset> is the bit offset from the start of the structure, while <length> is the length of the field, again in bits . <type> is the token for the data type of the field, and <name> is, of course, the field's name.
  • "type members" applies only to an enum-class type token. It returns a list of {<name> <value>} pairs for the members of the enumerated type.
  • "type pget" applies only to a pointer-class type token. It returns the type of pointer ("near", "far", "seg", "lmem", "handle", or "object") and the token for the type to which it points.
  • "type bfget" returns a three-list for the given bitfield type: {<offset> <width> <is-signed>}
  • "type signed" returns non-zero if the type is signed. If the <type> is not an int-class type, it is considered unsigned.
  • "type emap" can be used to map an integer to its corresponding enumerated constant. If no member of the enumerated type described by <type> has the value indicated, "nil" is returned, else the name of the matching constant is returned.
  • "type field" maps an offset into the passed struct-class type into a triple of the form {<name> <length> <ftype>}, where <name> can be either a straight field name, or a string of the form <field>.<field>... with as many .<field> clauses as necessary to get to the smallest field in the nested structure <type> that covers the given byte <offset> bytes from the start of the structure. <length> is the bit length of the field, and <ftype> is its type.
  • "type name" produces a printable description of the given type, using C syntax. <varname> is the name of the variable to which the type belongs. It will be placed at the proper point in the resulting string. If <expand> is non-zero, structured types (including enumerated types) are expanded to display their fields (or members, as the case may be).

See Also: gc, symbol, value.

| Up: GEOS SDK TechDocs | Up | Prev: 6 Coding | Next: 6.2 Examples


Examples

Tool Command Language: 6.2 Coding: Examples

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 Swat Data Structure Commands | Next: 7 Using a New Command

This section will contain a few examples of Tcl code for Swat commands, showing the use of some of included Tcl commands. A good way to view the code for a particular procedure is to type:

info body <procname>

on the Swat command line. This will print out the body of the given <procname> . One thing to watch out for, however, is the case when a procedure has not been loaded into Swat yet (i.e. it has not been used yet). If this is the case, Swat will have no information about the procedure and will thus print nothing. The command must be loaded into Swat either with the load command, or by just typing the command name which will usually autoload the command. (See Using a New Command .) Then the info body <procname> command can be used.

Some code examples:

The Whatat Command

[ defcommand whatat {addr} output
{Given an address, print the name of the variable at that address}
{
  var a [ sym faddr var $addr]
     if {[ null $a]}{
      echo *nil*
    } else {
     echo [sym name $a]
 }
}]

This example shows the code of the whatat command. Note the use of the sym (an abbreviation for symbol ) command to find the address of the given variable <addr> of class <var>.

The Bytes Command

1	 var addr [ get-address $addr ds:si]
2	var base [ index [addr-parse $addr] 1]
3	 echo {Addr: +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f}
4	#fetch the bytes themselves
5	var bytes [ value fetch $addr [  type make array $num [type byte]]]
6	#
    # $s is the index of the first byte to display on this row, $e is the
    # index of the last one. $e can get > $num. The loop handles this case.
    #
    var s 0 e [ expr 16-($base&0xf)-1]
    #
    # $pre can only be non-zero for the first line, so set it once here.
    # We'll set it to zero when done with the first line.
    # $post can be non-zero only for the last line, but we can't just
    # set it to zero and let the loop handle it, as the first may be the
    # last, so...
    #
    var pre [expr 16-($e-$s)-1]
    if {$e > $num} {
    var post [expr $e-($num-1)]
    } else {
    var post 0
    }

    [ for {var start [expr {$base&~0xf}]}
    {$s < $num}
    {var start [expr $start+16]}
    {
28	#extract the bytes we want
29	var bs [ range $bytes $s $e]

30	echo [ format {%04xh: %*s%s%*s "%*s%s%*s"} $start
    [expr $pre*3] {}
    [ map i $bs {format %02x $i}]
    [expr $post*3] {}
    $pre {}
    [ mapconcat i $bs {
    if {$i >= 32 && $i < 127} {
    format %c $i
    } else {
    format .
    }
    }]
    $post {}]
    var s [expr $e+1] e [expr $e+16] pre 0
    if {$e >= $num} {
    var post [expr $e-($num-1)]
    }
    }]
     set-address $addr+$num-1
     set-repeat [format {$0 {%s} $2} $addr+$num]

This example shows the code for the bytes commands. Notice the use of the type command on the fifth line, and the range command on the twenty-ninth line.

| Up: GEOS SDK TechDocs | Up | Prev: 6.1 Swat Data Structure Commands | Next: 7 Using a New Command


Using a New Command

Tool Command Language: 7 Using a New Command

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Coding | Next: 7.1 Compilation

Once a new command is written, it needs to be loaded into Swat so that it can be used. Depending on how the command is to be used, you may be interested in any of the following topics:

  • Compilation
  • Autoloading
  • Explicit loading

Compilation

Autoloading

Explicit Loading

| Up: GEOS SDK TechDocs | Up | Down | Prev: 6 Coding | Next: 7.1 Compilation


Compilation

Tool Command Language: 7.1 Using a New Command: Compilation

| Up: GEOS SDK TechDocs | Up | Prev: 7 Using a New Command | Next: 7.2 Autoloading

It is possible to byte-compile a Tcl script. The bc Tcl command creates a .TLC file containing compiled Tcl code--this code will run faster than normal Tcl code. When loading, Swat will load a .TLC file instead of a .TCL file where possible. Making changes to compiled Tcl functions involves changing the source code and re-compiling.

| Up: GEOS SDK TechDocs | Up | Prev: 7 Using a New Command | Next: 7.2 Autoloading


Autoloading

Tool Command Language: 7.2 Using a New Command: Autoloading

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 Compilation | Next: 7.3 Explicit Loading

If the development environment has been set up properly, there should already exist the /pcgeos/Tools/swat/lib directory on the workstation. This directory will contain all of the code files for the built-in Swat commands. To autoload a new command, copy its code file to the /pcgeos/Tools/swat/lib directory and add its name to the autoload. tcl file in the directory. This will load the command into Swat every time Swat is started. For example, say the command blitzburp has just been written to examine a new data structure. First, copy the file containing its code (say blitz. tcl) into the /pcgeos/Tools/swat/lib directory. Next, edit the autoload. tcl file and add one of the following lines:

[autoload blitzburp 0 blitz]
[autoload blitzburp 1 blitz]

This will ensure that blitz.tcl will be loaded when the command blitzburp is first used. The 0 indicates that the command must be typed exactly, and the 1 indicates that the interpreter will not evaluate arguments passed to the command. (Full reference information is available for the autoload command.)

| Up: GEOS SDK TechDocs | Up | Prev: 7.1 Compilation | Next: 7.3 Explicit Loading


Explicit Loading

Tool Command Language: 7.3 Using a New Command: Explicit Loading

| Up: GEOS SDK TechDocs | Up | Prev: 7.2 Autoloading

Another way to load a command into Swat is to use the load command from the Swat command line. This command is simply load <path>/<filename> . If no path is given, then the <file> is loaded from the directories specified in the load-path variable. The load command will load the given file (a Tcl procedure or subroutine) into Swat for subsequent use, and it is mostly used to load infrequently accessed files. (See , for more information on the load command.)

| Up: GEOS SDK TechDocs | Up | Prev: 7.2 Autoloading


Using Tools

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Tools Summary
| One-page Version of this Chapter for Easy Printing

This chapter gives a reference of the tools included in this development kit. This chapter is not intended to teach you how to use the tools; it provides a reference only. To learn how to use most of the tools, see the Tutorial included in the development kit and the First Steps chapter in the Concepts book.

1 Tools Summary
2 Typical Development Session
3 File Types
4 Esp
5 Glue
6 Goc
7 Grev
8 mkmf
9 pccom
9.1 PCCOM Background
9.2 Running PCCOM on the Target
9.3 File Transfer Protocol of PCCOM
10 pcget
11 pcs
12 pcsend
13 pmake
13.1 Copyright Notice and Acknowledgment
13.2 How to Customize pmake
13.3 Command Line Arguments
13.4 Contents of a Makefile
13.5 Advanced pmake Techniques
13.6 The Way Things Work
14 Swat Stub
15 dumpgeo
16 cvtpcx
17 pvm

| Up: GEOS SDK TechDocs | Up | Down | Next: 1 Tools Summary
| One-page Version of this Chapter for Easy Printing


Tools Summary

Using Tools: 1 Tools Summary

| Up: GEOS SDK TechDocs | Up | Prev: Using Tools | Next: 2 Typical Development Session

The tools provided in this kit are listed below. Each is described in full in the following sections.

pmake
"Make" utility which has knowledge of Goc. The pmake tool manages geode compilation and linking, making appropriate calls to Goc and Glue.
Goc
C preprocessor which is aware of programming constructs specific to GEOS programming. The Goc tool looks for special keywords (such as @class and @object) and creates the proper GEOS data structures so that the geode will work correctly after being compiled and linked. Chances are you won't call Goc directly but will instead use pmake , which will call Goc in turn.
Esp
Assembler with awareness of GEOS programming constructs. Also includes support for creating enumerated types, complicated records, and more. Chances are you won't call Esp directly but will instead use pmake , which will call Esp in turn.
Glue
GEOS linker. This determines how the program's resources should be set up and how those resources can be relocated. It determines file offsets and how to access library functions. As with Goc, you probably won't call Glue directly but will instead use it through pmake .
grev
Revision number generator. This is a handy utility for generating revision numbers for geodes, which may be used to keep track of library protocols. The pmake program uses grev to keep track of incremental changes; if you make large changes to a geode at some point, you can use grev to modify your geode's revision file to reflect your changes.
mkmf
Makefile maker. Creates a MAKEFILE which will in turn be used by pmake to determine how to create the geode's files.
pccom
Communication manager. Sets up communication over serial lines. This manages data transfer and protocols when sending files between machines or debugging.
pcs
Geode downloader. Sends geodes from the development machine to their proper directory on the target machine.
pcsend
File downloader. Sends an arbitrary file from the development machine to an arbitrary directory on the target machine.
pcget
File uploader. Retrieves an arbitrary file on the target machine, transferring it to any directory on the development machine.
Swat
GEOS debugger. This program runs on the development machine, monitoring GEOS programs running on the target machine.
| Up: GEOS SDK TechDocs | Up | Prev: Using Tools | Next: 2 Typical Development Session


Typical Development Session

Using Tools: 2 Typical Development Session

| Up: GEOS SDK TechDocs | Up | Prev: 1 Tools Summary | Next: 3 File Types

The Tutorial shows in detail how to write, compile, download, and debug an application. As a quick guide, however, the following list recaps the steps in a typical development session:

  1. On the development machine, use a text editor (perhaps the one that came with your compiler) to write or edit the source (.c and .goc files, perhaps .asm or .ui files if you have the Esp assembler), header (.h and .goh files, perhaps .def files if you have the Esp assembler), and Glue parameters (.gp files) for your geode.The source files for the geode should be arranged within your development directory.
  2. If you have never compiled the geode before or have added or included new files since the last compilation, you must run mkmf to create or update the MAKEFILE.
  3. If you have added or included new files since the last compilation, you should execute pmake depend so that the compiler will know which files need to be remade if one is altered.
  4. Run pmake . This compiles and links the geode's source files, using directions provided by the MAKEFILE.
  5. To test the geode, either run it on the target machine or run swat from the development machine to debug it.
| Up: GEOS SDK TechDocs | Up | Prev: 1 Tools Summary | Next: 3 File Types


File Types

Using Tools: 3 File Types

| Up: GEOS SDK TechDocs | Up | Prev: 2 Typical Development Session | Next: 4 Esp

You may be curious to know what sorts of files you'll be working with. If you have to work with someone else's code, then being able to find your away around their files (knowing which are sources, which are objects, and which are chaff) can be very useful.

*.goc
These are Goc source files. You will write these files. They contain both standard C code and GEOS constructs (such as objects and messages).
*.goh
These are Goc header files. You will write these files and include others. They provide definitions used by your .goc files (in the same relation as between standard C source and header files). Unlike standard C header files, these are included using the @include Goc keyword.
*.poh , *.ph
These are files generated to optimize the @inclusion of .goh files. Goc will automatically generate these when they don't already exist.
*.doh , *.dh
These are files generated to optimize the @inclusion of .goh files. They contain dependency information.
*.c
These are standard C source files. You may write these as source files, using only standard ANSI C constructions. The Goc preprocessor will create .c files from .goc files. Thus, if you see two files with the same prefix, but one has the .c suffix and the other has the .goc suffix, then you know that the first was created from the second.
Note that Goc will create the generated .c file in the directory where it is invoked. Thus if your development tree contains files PROG\DIR1\CODE.GOC and PROG\DIR2\CODE.GOC, then if you convert these using Goc from the PROG directory, then one of the generated .c files will overwrite the other. Thus, you should never give .goc files the same prefix, even if they are in different directories.
*.h
These are standard C header files. You may write these and include them using the ANSI C #include directive.
*.asm
These are standard GEOS assembly source files, which may be assembled with the Esp assembler, if you have it. They may contain both standard assembly and Esp constructs.
*.def
These are standard GEOS assembly header files, which you may write or include if you have access to the Esp assembler.
*.mk, makefile
These are "makefiles," files which contain scripts which the pmake tool will interpret and use to automatically compile and link your geode. In a source directory there will be a file called MAKEFILE (created with the mkmf tool) and probably a file called DEPENDS.MK (created by calling pmake depend ). If you wish to customize how your geode is made, you will probably write a file called LOCAL.MK, containing your custom makefile script. The INCLUDE directory contains several .mk files, which will be #included by other makefiles.
*.gp
These are "Glue parameter" or "geode parameter" files, which will give the Glue linker information necessary when linking a geode. You will write this file. The pmake program assumes that a geode's .gp file will have name geode.GP, where geode is taken from the name of the directory containing the geode's source (e.g. in the example above, pmake would expect the .gp file to be named PROG.GP).
*.ldf
These are library definition files. Glue uses these files when linking your geode; they determine how your calls to a GEOS library will be encoded. If you are writing a library, then you will create one of these files by means of a pmake lib . The pmake program looks for .ldf files in the INCLUDE\LDF directory.
*.rev
This is a revision file, used to keep track of a program's revision and protocol levels (useful for tracking compatibility). The pmake tool will look for a file with name geode .REV, where geode is taken from the name of the directory containing the geode's source (e.g. in the example above, pmake would expect the .rev file to be named PROG.REV). The pmake program uses grev to create and maintain the .rev file; you should use grev yourself when you need to signal a major revision.
*.rsc
Localization resource file. This file contains information which will be used by the ResEdit localization tool.
*.obj
These are object files. These are files created by a C compiler or Esp which may be linked to form an executable. The pmake program uses Glue to link the object files.
*.ebj
These are error-checking object files. GEOS supports the notion of "error-checking code." When you write your programs, you can mark some commands as "error checking commands." These commands might make sure that a routine is passed valid arguments or perhaps purposefully destroy some information which was not guaranteed preserved by a routine. Such commands may prove time-consuming but are useful for making sure that an application is robust. The pmake program will create two versions of your application--one which includes the EC (Error Checking) code, and one which doesn't. Run the EC program to check for correctness, but use the non-EC version when the program should be fast (i.e. this is the version you should give to your customers).

The .obj files will be linked to form a non-EC executable; .ebj files to form an EC executable.

*.geo
This is a Geode, a GEOS executable (either an application, library, or driver), the end result of your efforts. These are the files containing the code for GEOS programs which the user will interact with. They are created by linking together a number of .obj files, with additional information provided by a .gp file. You will place these files in your GEOS testing directory on your target machine (along with *ec.geo files, described below).
*ec.geo
This is an error checking geode. (See above for quick descriptions of error checking code and geodes.) They are created by linking together a number of .obj and .ebj files, with additional information provided by a .gp file.
*.sym
This is a symbol file, containing symbolic debugging information which the Swat debugger can use to access the geode's data structures.
*ec.sym
This is the symbol file of the error checking version of a geode.
tmp*.*
These are temporary files which pmake will create and destroy while making your executable. The pmake program uses these files to pass arguments to the other tools. Thus, if you see a file of this name in your directory and you didn't create it, you can assume that pmake was interrupted in a recent make and was unable to erase the file (and thus it is safe for you to erase it).
.tcl , .tlc
These are Tcl files, files containing Tool Command Language source code, used by the Swat debugging tool. The .tcl files contain Tcl source code, the .tlc files contain compiled Tcl code. The source code may be edited by any text editor and Swat will interpret it; compiled code runs more quickly, but can only be changed by editing the source code and re-compiling.

If you are writing a GEOS C application, you will write the following types of files:

  • Source files: .goc files, optional .c files.
  • Glue parameters file: geode.GP.
  • Optional header files: .goh files, .h files.
  • Optional custom make file: LOCAL.MK.

After you have made your geode the first time (creating a makefile with mkmf , a dependencies file with pmake depend , and the geode itself with pmake ), your directory should contain the following additional file types:

  • Intermediary C files: .c files (made by transforming .goc files).
  • Makefile: MAKEFILE
  • Dependencies file: DEPEND.MK
  • Revision file: geode.REV.
  • Object files: .obj and .ebj files.
  • Symbol files: .sym files
  • Geodes: .geo files.
| Up: GEOS SDK TechDocs | Up | Prev: 2 Typical Development Session | Next: 4 Esp


Esp

Using Tools: 4 Esp

| Up: GEOS SDK TechDocs | Up | Prev: 3 File Types | Next: 5 Glue

Esp (pronounced "esp") is the GEOS assembler. It creates object files from Esp code--said code using a superset of MASM syntax. These object files may then be linked by means of the Glue tool.

Most users will never call Esp directly, instead going through pmake , which will make the proper calls to Esp for the most common cases.

Esp takes the following options:

-2
Code produced should be DBCS--characters will be two bytes instead of one.
-I directory
Specifies an additional directory in which to look for included files.
-o filename
Name to give to created object file.
-w warntype
- W warntype
Turn warnings off or on
unref
Warn if a symbol that can only be used during this assembly isn't.
field
Warn if structure field used with . operator when lhs isn't of the type that contains the field
shadow
Warn if a local variable or procedure overrides a definition in a larger scope
private
Warn if a private method or instance variable is used outside a method handler or friend function related to the class that defined the thing
unreach Warn about code that cannot be reached. This is very simplistic
unknown Warn if a far call is made to a routine whose segment is unknown
record
Warn if a record initializer doesn't contain all the fields of the record
fall_thru
Warn if a function falls into another one without a .fall_thru directive
inline_data
Warn if a variable is defined where execution can reach
unref_local
Warn if a local label isn't ever referenced
jmp
Warn if out-of-range jumps are transformed into short jumps around near jumps
assume
Warn when override is generated based on segment assumptions
all
Used to turn all warnings on or off.
-M
This assembly is just for the purpose of determining what the source file's dependencies is. Instead of creating an object file, Esp will create a temporary file which the dependencies maker will use to determine the dependencies.
-d
Activate's Esp debugging mode. Useful only when trying to track down a bug in Esp.
| Up: GEOS SDK TechDocs | Up | Prev: 3 File Types | Next: 5 Glue


Glue

Using Tools: 5 Glue

| Up: GEOS SDK TechDocs | Up | Prev: 4 Esp | Next: 6 Goc

Glue is the GEOS linker. It creates GEOS or DOS executables from object files. (It can also create GEOS VM and font files, if you have the appropriate tools.) These object files may have been created by a C compiler, or by the Esp assembler. To create the executables, Glue must create a combined file, resolve external declarations, determine how to call libraries, and apportion the code and data into resources. Glue will also create a .sym file--a file containing symbolic debugging information which the Swat debugger will use for viewing the geode's data.

Most users will never call Glue directly, instead going through pmake , which will make the proper calls to Glue for the most common cases. If you use the default makefiles, the variable LINKFLAGS will determine which arguments Glue receives; thus, by creating a local makefile that modifies this variable's value, you can pass more flags to glue. See How to Customize pmake for help in creating a local makefile.

The Glue application takes the following arguments:

glue @file
glue <flags> <objFile>+ [-l<objFile>]*
@file
The Glue linker should take its arguments from the file file in addition to those on the command line. This may come in handy if you often use the same long string of options. Since you may need to pass Glue more arguments than may be input on the command line, sometimes this option is necessary.

Note that if you use this option, then all arguments must be included in the file--there should be no others on the command line itself.

The pmake program uses this option to pass arguments to Glue.

-D var
Defines the variable var ; used with ifdef statements within the .gp file to allow for conditional linking (see the GP file keyword reference for the syntax of Glue's ifdef statements).
-L path
Specifies where Glue looks for .ldf (library definition) files. These files are placed in a standard directory by the system makefiles on a " pmake lib¨.
-N string
Specify a copyright notice. This string must be 32 characters or less.
-Oe
Creating DOS executable (".exe") file. Of course, this option is not valid if the object files contain GEOS directives which will not work outside GEOS.
-Oc
Creating DOS command (".com") file. Of course, this option is not valid if the object files contains GEOS directives which will not work outside of GEOS.
-Og file
Creating GEOS executable (".geo") file. You must provide the name of the geode's Glue parameters file (the .GP file). For information about setting up a parameter file, see the Hello World chapter.

When creating a .geo file, you may pass any of the following options:

-E
Link Error checking version of geode.
-R number
Specify release number of geode (e.g. 3.2.1.0).
-P number
Specify protocol number of geode (e.g. 1.0).
-T number
File type.
-l
Creating a library; Glue should create .ldf file.
-Ov
Creating GEOS Virtual Memory (.vm) file. Using this option, you may create .vm storage files as set up by the Esp assembler. You may pass the following options when creating .vm files (if you don't know the meaning of some of these terms, see the Virtual Memory chapter):
-A number
VMAttributes to use for the file.
-C number
Compaction threshold.
-M string
Map block segment name.
-P number
Protocol number (e.g. 2.5.0.3).
-R number
Release number (e.g. 12.3).
-i name
A .geo file from which to get the table of imported libraries. This allows the VM file to be opened by that geode and objects in the file to be used.
-t token
File token
-c token
Creator's manufacturer token.
-l string
File's long name.
-u notes
File's user notes.
-N string
Copyright notice which will be embedded in header of .GEO or .VM files.
-G number
You should never have to use this option. This specifies a non-standard GEOS release number (e.g. 1.3); if this release used a different VM header than GEOS 2.0, Glue will still construct the proper header as long as this option is passed. Since the default release number is 2, developers for 2.X do not need this option.
-Wall
Requests that Glue output all optional warnings.
-Wunref
Requests that Glue output optional unreferenced global symbol warnings.
-d
Dump memory. Normally used only when debugging Glue.
-m
Provide memory map in output. This information gives information about the sizes of various parts of the geode. This information proves especially helpful when making geodes work with small devices.
-nll
Disables the output of line numbers for local memory segments for any application with 163 resources or more.
-o file
Specify name of output file (e.g. WOROPRO.GEO).
-q
Leave the symbolic information behind even if an error was encountered. Normally, this flag is used only when debugging Glue.
-r
Maps segment relocations to non-shared resources to resource IDs. This is normally used only by multi-launchable C applications. When running more than one instance of a multi-launchable application, the system only uses one copy of the read-only portion of the application. The system makes separate copies of the writable data for each instance of the application. This can lead to conflict when the relocation instructions for the read-only data uses the handle or segment of a writable resource; which copy should be responsible for providing these addresses? If you don't pass this flag and Glue detects the above situation, Glue will simply refuse to make the application multi-launchable. This flag instructs Glue to instead use the resource ID of the writable resource where it would normally use the segment or handle of that resource.
If you use this option, make sure that if you use the address of a variable in a resource other than dgroup that you use GeodeGetOptrNS() and lock down or dereference the handle of the returned optr.
-s file
Specify name of symbol file (the .SYM file).
-z
Output localization information.
| Up: GEOS SDK TechDocs | Up | Prev: 4 Esp | Next: 6 Goc


Goc

Using Tools: 6 Goc

| Up: GEOS SDK TechDocs | Up | Prev: 5 Glue | Next: 7 Grev

Goc is the GEOS C preprocessor, which will turn your .goc file into something a regular C compiler can understand. It will traverse the .goc file, detect Goc keywords (e.g. @class, @object), turn these keywords into appropriate pieces of code, and write the resulting file out to a .c file. Note that Goc acts as a simple filter, and will only make changes where it detects Goc constructs; it won't touch your regular C code at all.

Under normal circumstances, you will not invoke Goc directly. Instead, pmake will make calls to Goc when compiling .goc files.

If for some reason you do need to invoke Goc directly, you may wish to know about its command line arguments:

goc @file
goc [args] <file>
@ file
Command-line arguments are stored in a file--Goc will read this file and treat the contents as its arguments. If you use this option, then all the arguments must be in the file--no others may appear on the command line itself.
-
Redirect output to standard out.
-C segment
Specify segment for class structures to reside.
-D macro
Set variable macro to one (e.g. "-D__GEOS__")
-D macro = value
Set variable macro to value.
-I dir
Specify additional include directory
-I-
Turn off relative inclusion.
-L name
Specify name of library being compiled. Must match argument to @deflib Goc keyword in library's .goh file, if it has one.
-M
Help pmake generate dependency information.
-cb
Generate information for Borland compiler.
-d
Turn on all debugging information.
-dd
Output @default debugging information. (See the GOC Language chapter to find out what @default means).
-dl
Output lexical analyzer debugging information.
-dm
Output message parameter definition debugging information.
-ds
Output symbolic debugging information.
-du
Output Goc-specific debugging information.
-dy
Output parser debugging information.
-l
Do localization work.
-o file
Specify name of output file.
-p
For Geoworks use only.
-X
Ignore @optimize flags
| Up: GEOS SDK TechDocs | Up | Prev: 5 Glue | Next: 7 Grev


Grev

Using Tools: 7 Grev

| Up: GEOS SDK TechDocs | Up | Prev: 6 Goc | Next: 8 mkmf

GEOS supports two version numbers for each geode. The first of these is the release number, used to uniquely identify the release of the geode. The protocol number tracks the external interface of the file. This is used to determine what versions of related geodes can be used together. The kernel will use these numbers to prevent loading of incompatible executable files.

The grev utility generates proper revision numbers. Normally, it is called automatically by pmake , so if you are just making a small change to a file, you need not call it directly. However, you may wish to. When using grev, you must think about how major a change you are making; a large change means that you should change an earlier number of the release number. A change from 1.2.3.4 to 2.0.0.0 should signal a larger step than a change to 1.2.3.5.

The pmake program uses grev to automatically create revision numbers for geodes; it passes these values to Glue, which in turn places the protocol and revision numbers in the .geo and .sym files.

There are three widely used methods for incrementing release numbers with respect to public releases (for which a specific release number is desired for marketing, say "2.0.0"). The problem comes because it is not known until after a release has been built whether it will be the release or not (since bugs may be found).

The first method is to keep separate public release numbers and internal release numbers. This is awkward and confusing and is generally done when it is too late to do anything else.

The second method is to number successive revisions "1.14.0.12", "1.14.0.13", "1.14.0.14" and so on until the final revision is made which is numbered "2.0.0.0". The problem with this is that one never quite knows whether or not a revision is the final one (since bugs may be found).

The third method is to number successive revisions "2.0.0.12", "2.0.0.13", "2.0.0.14" and so on. The released "2.0.0" revision is then the last engineering revision starting with "2.0.0.X". The disadvantage of this method is that it can seem non-obvious at first and requires a little bookkeeping to know the engineering number of the released version.

The protocol number is changed whenever the external interface for the file changes. For the kernel and for libraries the protocol reflects the order as well as the parameters and behavior of external entry points. For applications the protocol reflects the object names, types and attributes. Changes that do not affect the external interface (changing the implementation of a routine, changing the moniker or hints of an object) do not change the protocol number.

The major protocol number reflects incompatible changes in interface, such as rearranging the order of entry points. The minor protocol number reflects upwardly compatible changes in the protocol (such as adding an entry point at the end of a jump table or using a bit formerly marked as "reserved").

Each executable file contains protocol compatibility information (a protocol number) for all other executable files on which it depends. For example, a simple application might be compatible with kernel protocol "34.2" and UI protocol "19.7". Thus the application is compatible with kernels "34.2" through "34.65535" and with UIs "19.7" through "19.65535".

A protocol number is also stored with each state file to determine if the state can be recovered by the currently running application.

The grev tool uses a file (normally marked with a .rev suffix) in the geode's development directory to keep track of the revision number. The file is organized chronologically, with later entries at the beginning of the file. It contains

  • one line for each compilation, denoting the revision number (which is incremented on each compilation), optional user name, the date, and an optional comment. By default, pmake will only change the last part of the release number.
  • one line for each protocol change, denoting the protocol number, optional user name, the date, and an optional comment.

Using the grev utility:

In the Nokia 9000i SDK version 2 (refer to versions), the .rev file may be in the local or in the "Installed" branch. It is suggested that developers keep a local .rev file so that they don't have to make revisions to the branch by hand.

The grev utility uses the following syntax:

     grev <option> <rev filename> [<comment>|-P|-R] -s
It is necessary to pass the -s flag to save the protocol change. There are few circumstances where this flag is not needed.

<options> may be one of the following:

new file [" comment "|-P|-R]
Create a new revision record, listing comment as an initial revision for the base (0.0.0.0 release, 0.0 protocol). This command may only be executed in the geode's development directory. The -P option causes grev to give minimal output, printing only the protocol number. The -R option causes grev to print only the revision number. These last two options are normally used by pmake to extract the relevant numbers.
info file
Print the current release and protocol from the revision file.
getproto file
Print only the current protocol from the revision file.
newprotomajor file [" comment "|-P|-R]
NPM file [" comment "|-P|-R]
Increase the major protocol number by one, setting the minor number to zero. The comment argument is listed as the reason for the change in the file. The -P and -R options work as they do for grev new .
newprotominor file [" comment "|-P|-R]
npm file [" comment "|-P|-R]
Increase the minor protocol number by one. The comment string is listed as the reason for the change in the file. The -P and -R options work as they do for grev new .
newrev file number1.number2.number3 [" comment "|-P|-R]
Increase release number from A.B.C.D to number1.number2.number3.0. The comment is listed as the reason for the change. The -P and -R options work as they do for grev new .
newchange file [" comment "|-P|-R]
Up release number from A.B.C.D to A.B.C+1.0. The comment is listed as the reason for the change. The -P and -R options work as they do for grev new .
neweng file [" comment "|-P|-R]
Increase release number from A.B.C.D to A.B.C.D+1. The comment is listed as the reason for the change. The -P and -R options work as they do for grev new .
<rev filename> is the name of the revision file (ending in .REV).

<comment> is an optional comment embedded in quotes.

-P causes grev to give minimal output, printing only the protocol number. This flag is normally used by pmake to extract to protocol number.

-R causes grev to print only the revision number. pmake uses this flag to extract the version number.

-s, as noted above, causes the change to be saved to the revision file. If this flag is not passed, the change will only be displayed to the screen.

| Up: GEOS SDK TechDocs | Up | Prev: 6 Goc | Next: 8 mkmf


mkmf

Using Tools: 8 mkmf

| Up: GEOS SDK TechDocs | Up | Prev: 7 Grev | Next: 9 pccom

The mkmf tool exists to create a file named MAKEFILE. The pmake program will use this file as a sort of script, using it to determine how to compile and link the geode. However, makefiles can get rather complicated, so it is best to create them using mkmf instead of by hand.

For information about customizing this boilerplate makefile, see pmake .

The mkmf tool uses the following rules to build the makefile:

  • The geode name is taken from the name of the directory in which you run mkmf. Among other things, this means that if you change the name of your development directory, you should also run mkmf again.
  • Any file in the current directory or its subdirectories will be considered a source file if it has one of the following suffixes: .asm, .def, .ui, .c, .h, .goc, or .goh (except that .c files which are generated from .goc files will not be considered sources).
  • For each .asm, .c, or .goc file, an .obj file with corresponding name will be added to the makefile variable OBJS.
  • If there are subdirectories, then each subdirectory is considered to hold the source for a module of the program, and mkmf will work with the files in this subdirectory as a unit. The module's name will be added to the CMODULES variable if it contains .c or .goc files; if it contains .asm files, then it will be added to the MODULES list. When using pmake , each module will be considered something that can be made, a sort of intermediate step towards making the whole geode. If you do not wish the files in a subdirectory to be incorporated in the program, create a file in the directory called NO_MKMF. This file need have no contents.
| Up: GEOS SDK TechDocs | Up | Prev: 7 Grev | Next: 9 pccom


pccom

Using Tools: 9 pccom

| Up: GEOS SDK TechDocs | Up | Down | Prev: 8 mkmf | Next: 10 pcget

The pccom tool manages communication between the development and target machines. It assumes that the machines are connected by a single serial line. All I/O is interrupt-driven with XON/XOFF flow control active on the development machine and obeyed on the target machine.

Note that it is possible to use some features of pccom from within GEOS. For more information about this, see the PCCom Library documentation.

PCCOM Background

Running PCCOM on the Target

File Transfer Protocol of PCCOM

| Up: GEOS SDK TechDocs | Up | Down | Prev: 8 mkmf | Next: 10 pcget


PCCOM Background

Using Tools: 9.1 pccom: PCCOM Background

| Up: GEOS SDK TechDocs | Up | Prev: 9 pccom | Next: 9.2 Running PCCOM on the Target

PCCOM is used in two primary situations. First, it is used by GEOS software developers when transferring files or when debugging an application. In this situation, the developer runs PCCOM on the target machine and then runs PCS, PCSEND, or PCGET, on the host machine. All of these programs know how to interact with PCCOM.

Second, it is used by DOS programs when transferring files to and from Zoomer devices or other devices that require remote file manipulation. In this case, the host machine runs a program which copies escape character sequences to the appropriate serial port, prompting PCCOM to act.

| Up: GEOS SDK TechDocs | Up | Prev: 9 pccom | Next: 9.2 Running PCCOM on the Target


Running PCCOM on the Target

Using Tools: 9.2 pccom: Running PCCOM on the Target

| Up: GEOS SDK TechDocs | Up | Prev: 9.1 PCCOM Background | Next: 9.3 File Transfer Protocol of PCCOM

PCCOM is a DOS program that monitors the serial port and responds to commands received on the line. All I/O is interrupt driven with XON/XOFF flow control active on the host machine and obeyed on the target machine.

PCCOM uses the PTTY environment variable of DOS, if it exists. This variable contains communications settings detailing baud rate, COM port, and communications interrupt level. You can override the PTTY settings with command-line options to PCCOM when running it. The following command-line options are allowed:

/b:baud
Specify the baud used for file transfer and serial communications. The baud parameter may be one of the following values: 300, 1200, 2400, 9600, 19200, or 38400. Unambiguous abbreviations may be used (e.g. 9 for 9600 baud or 38 for 38400 baud). The default baud rate is 19200 bps.
/c:port Specify the COM port used for serial communications. The parameter may be one of 1, 2, 3, or 4. The COM ports for Zoomer are
COM 1 the built-in serial port (this is the default).
COM 2 the infrared transceiver.
/i:interrupt
Specify interrupts that should be ignored by PCCOM. This is useful if peripherals share an interrupt number that may confuse PCCOM. The interrupt parameter is one or more numbers of the interrupt(s) to be ignored, in hexadecimal.
/I:irq
Specify the IRQ level for serial line communications. This parameter is rarely required. The irq parameter is the number of the IRQ level to be used.

Quitting PCCOM

PCCOM may be quit either directly or remotely. To quit PCCOM directly, simply hit the Enter key (or the q key) on the machine on which PCCOM is running. If it does not quit on the first keystroke, hit the key again.

To quit PCCOM remotely, issue the quit escape sequence <Esc>EX through the serial line from the host machine. See below for a description of the commands that can be issued remotely.

Remote PCCOM Commands

PCCOM doesn't care what machine originates a remote command; its sole purpose is to evaluate and execute commands received through the serial port it's monitoring. Thus, a command sent by one Zoomer to another will exact the same response as a command sent by a development host machine to a target development machine.

On some computers (depending on BIOS), commands are copied from DOS to the serial port using the "echo" command. (If the computer executing remote commands has a different BIOS, you may get an error like "write fault error writing to device com#"; in this case, you must make sure that the characters sent to the serial port in the end are the same as those shown in the table below.) For example, to send the "quit" command to the remote machine, you could use the DOS command

C:>echo EscEX > com1

where Esc (in italics) represents the Escape character (0x1B).

No matter what method you use to send the character sequences to the serial port, the following commands may be executed remotely. Sending and receiving files remotely is more involved and is therefore discussed in the next section; it is not complicated, however.

All arguments to PCCOM remote commands must end with a delimiter character--an exlamation mark by default. Because of this, file operation commands will not work on files with exclamation points in their names by default. However, there is a PCCom command to use a different delimiter character.

Command | Sequence | Description
Send File | <Esc>XF<Ctrl-A> (<Esc>XF<0x01>) | Send a file from the host to the remote machine using the PCCOM file transfer protocol (see below).
Get File | <Esc>XF<Ctrl-D> (<Esc>XF<0x04>) | Retrieve a file from the remote machine using the PCCOM file transfer protocol (see below).
Turn EchoBack On | <Esc>EBon! | Turn EchoBack on.All text displayed on the target machine will also display on the host machine. Carriage returns are marked by semicolon (";") characters.
Turn EchoBack Off | <Esc>EBoff! | Turn EchoBack off.
Display EchoBack State | <Esc>EB! | Display a string signalling whether EchoBack is turned on. If it's turned on, "Echoback = off" appears on the target machine. If it's turned on, "Echoback = on" appears.
Turn Acknowledge On | <Esc>AKon! | Whenever a transaction is complete, the host will display "Ack Received." or "Nak Recieved." as appropriate.
Turn Acknowledge Off | <Esc>AKoff! | Turn off acknowledgement.
Display Acknowledge State | <Esc>AK! | Display a string signalling whether Acknowledge is turned on. If it's turned on, "ACK/NAK = on" appears on the target machine; otherwise "ACK/NAK = off" appears.
Send Ack Signal | <Esc>ACK | Sends an Ack signal, just as is sent by the target machine when acknowledgements are turned on.
Send NAK Signal | <Esc>NAK | Sends a NAK signal, just as is sent by the target machine when acknowledgements are turned on.
Copy File | <Esc>CPsrc!dest! | Copy the file named in the src argument to the file named in the dest argument. File name arguments may be full or relative paths with or without drive letters. This is equivalent to the DOS COPY command.
Move File | <Esc>MVsrc!dest! | Move the file named in the src argument to the file named in the dest argument. File name arguments may be full or relative paths; no drive letters are allowed. This is equivalent to the DOS MOVE command.
Delete File | <Esc>RFfile! | Remove the named file; the file argument may be a full path or a file in the current directory. This is equivalent to the DOS DEL command.
Change Drive | <Esc>CDdrive:! | Change the working volume to the drive named in the drive argument. This is equivalent to changing the drive in DOS by typing the drive letter followed by a colon (e.g. C:).
Change Directory | <Esc>CDdir! | Change the working directory to that named. The dir argument may be a full or relative path; no drive letter is allowed. This is the equivalent of the DOS CD command.
Show Current Path | <Esc>CD! | Print the current directory's path. This is equivalent to the DOS CD command with no arguments passed.
List Files in Dir | <Esc>LS | List files in the current working directory. This is equivalent to the DOS DIR command with no arguments.
List Files with Details | <Esc>LM | List files in the current working directory, together with the file's DOS attributes, the long name of each GEOS file, and the release number associated with each GEOS file. The DOS attributes are shown with the following flags: a: archive d: sub directory v: volume file s: system h: hidden r: read only
List Files, Full Details | <Esc>LL | List files in the current directory with all information returned by LM, described above, and the token of the creating geode and the document token. Tokens consist of four characters and a ManufacturerID --most ManufacturerID values will appear as a number, but PCCom will translate those values that it knows about into a mnemonic code: GEO: Geoworks APP: Application Local PLM: Palm Computing WIZ: SchoolView CLB: Creative Labs DSL: DOS Launcher AOL: America Online ITU: Intuit SDK: Default SDK AGD: Association of GEOS Developers
Display Free Space | <Esc>FSd:! | Display the free space on the drive named by the d argument.
 | <Esc>FS! | Display the free space on the current drive.
Display Drives | <Esc>AD | Display available drives. The target machine will display one line for each drive, showing the drive's letter and DOS name.
Create Directory | <Esc>MDdir! | Create a new directory according to the dir argument. The dir argument may be a full or relative path. This is the equivalent of the DOS MKDIR command.
Delete Directory | <Esc>RDdir! | Remove the directory named in the dir argument. The dir argument may be a full or relative path; this is equivalent to the DOS RMDIR and RD commands.
Clear Screen | <Esc>cl | Clear the screen of the target machine. This is equivalent to the DOS CLS command.
Display Delimiter | <Esc>DD | Displays the current delimiter character (`!' by default).
Change Delimiter | <Esc>DX <char><old delimiter> | Use a different character as the delimiter. By default, the exclamation mark (`!') is the delimiter character. The character delimiter must lie between the ascii values of 0x21 (exclamation mark) and 0x7e (tilde).
Exit PCCOM | <Esc>EX | Exit PCCOM on the remote machine.

Sending and Receiving Files

If you are using the GEOS SDK, you will do most of your file sending and receiving using the programs PCS, PCSEND, and PCGET. These programs send commands to the serial port, and then follow them by either providing or receiving packaged file data. These three programs are detailed below; following them is a section of the file transfer protocol of PCCOM if you are writing your own remote-access program(s).

PCGET

If PCCOM is running on the target machine, the PCGET program can be executed on the host to retrieve a file from the target. This simple program merely retrieves the file and copies it into the host's working directory under the same name.

PCGET takes the following arguments; only the file name is required. The other arguments are optional and may be used to override the settings in the host machine's PTTY environment variable (see above, under "Running PCCOM on the Target (or on the Zoomer)").

pcget [/b:baud][/c:port][/I:irq] file
/b:baud
Specify the baud used for file transfer. The baud parameter may be one of the following values: 300, 1200, 2400, 9600, 19200, or 38400. Unambiguous abbreviations may be used (e.g. 9 for 9600 baud or 38 for 38400 baud). The default baud rate is 19200 bps.
/c:port Specify the COM port used. The port parameter may be one of 1, 2, 3, or 4. The COM ports for Zoomer are
COM 1 the built-in serial port (this is the default),
COM 2 the infrared transceiver.
/I:irq
Specify the IRQ level for the transfer. This parameter is rarely required. The irq parameter is the number of the IRQ level to be used.
file
Specify the file to be retrieved. The file parameter may be a full or a relative path or a simple file name. The file will be copied from the target to the host into the host's current working directory, with the same name.

PCSEND

If PCCOM is running on the target (remote) machine, PCSEND may be executed on the host machine to download a file to the target. PCSEND will only send a single file, though it may send the file to any directory on the target. To send multiple files, or to download specific geodes to their proper locations in the GEOS 2.0 directory tree, use the PCS program instead.

The command line options of PCSEND are shown below. Only the file to be sent is required; if no other argument is passed, the file will be sent to the target's current working directory.

pcsend [/b:baud][/c:port][/I:irq] file [/d:dest]
/b:baud
Specify the baud used for file transfer. The baud parameter may be one of the following values: 300, 1200, 2400, 9600, 19200, or 38400. Unambiguous abbreviations may be used (e.g. 9 for 9600 baud or 38 for 38400 baud). The default baud rate is 19200 bps.
/c:port Specify the COM port used. The port parameter may be one of 1, 2, 3, or 4. The COM ports for Zoomer are
COM 1 the built-in serial port (this is the default),
COM 2 the infrared transceiver.
/I:irq
Specify the IRQ level for the transfer. This parameter is rarely required. The irq parameter is the number of the IRQ level to be used.
file
Specify the file to be sent. The file parameter may be a full or a relative path or a simple file name. The file will be downloaded to the target machine's current working directory, unless the /d parameter is also passed (see below).
/d:dest
Specify a full or relative destination path for the file.

PCS

If PCCOM is running on the target machine, PCS may be executed on the host machine to send multiple files to predetermined directories on the target. PCS is most often used by GEOS developers using the GEOS development kit, when they are downloading their recently-compiled geodes to the target for debugging.

The PCS program makes use of a list of constraints-tokens and their source and destination files and directories-located in the files ROOT_DIR\INCLUDE\PCS.PAT and ROOT_DIR\INCLUDE\SEND on the SDK host machine. (ROOT_DIR is a DOS environment variable set up by the SDK installation program indicating the top directory into which the SDK files were installed.) The format of these two files is described at the end of this section.

The command-line parameters of PCS are shown below. Note that a file name is not used by PCS; instead, if no token or directory is given, PCS will download all appropriate files in the current working directory. As with PCSEND and PCGET, the baud, COM port, and IRQ level arguments are all optional and may be used to override the settings in the PTTY environment variable.

pcs [/n][/Sf][/t][/b:b][/c:p][/I:i][dir|file|token]
/n
If /n is specified, PCS will send non-EC geodes only. Without this argument, PCS will send only EC geodes.
/Sf
Specify a file containing a list of files to be sent. The file argument is the name of the file.
/t
If the /t argument is used anywhere on the command line, file names specified at the end of the command (see the last argument) will be interpreted as tokens. A token may equate to numerous files as defined in the SEND file.
/b:b
Specify the baud used for file transfer. The baud parameter may be one of the following values: 300, 1200, 2400, 9600, 19200, or 38400. Unambiguous abbreviations may be used (e.g. 9 for 9600 baud or 38 for 38400 baud). The default baud rate is 19200 bps.
/c:p Specify the COM port used. The port parameter may be one of 1, 2, 3, or 4. The COM ports for Zoomer are
COM 1 the built-in serial port (this is the default),
COM 2 the infrared transceiver.
/I:i
Specify the IRQ level for the transfer. This parameter is rarely required. The irq parameter is the number of the IRQ level to be used.
dir|file|token
Specify a directory containing the geodes to be downloaded, the files to be downloaded, or tokens to be interpreted. If no directory, file, or token is specified, PCS will download the appropriate files in the current working directory. If a directory is specified, PCS will download all the appropriate files in that directory. If file names are specified (multiple files and/or directories may be specified), all affected files will be send.

If the /t argument appears anywhere in the command line (see above), this set of arguments will be interpreted as tokens. See directly below for token use and interpretation.

When using a token, PCS looks in the ROOT_DIR\INCLUDE\SEND file for the token to determine which files should be sent. Generally, all executables associated with an application, library, or mechanism are sent when the appropriate token is passed. Look in the SEND file to find out what the accepted tokens are and what they send.

For example, if the SEND file contained the lines

PC	DRIVER/VIDEO/DUMB/HGC/HGC					GEO
PCB	DRIVER/VIDEO/DUMB/HGC/HGC					GEO
PCB	DRIVER/MOUSE/LOGIBUS/LOGIBUS					GEO

then typing

pcs /t pc

would send just the HGCEC.GEO file to the proper directory on the target. Typing

pcs /t pcb

would download both HGCEC.GEO and LOGIBUSE.GEO to their proper directories. A listing of all the supported tokens can be found in the SEND file.

| Up: GEOS SDK TechDocs | Up | Prev: 9.1 PCCOM Background | Next: 9.3 File Transfer Protocol of PCCOM


File Transfer Protocol of PCCOM

Using Tools: 9.3 pccom: File Transfer Protocol of PCCOM

| Up: GEOS SDK TechDocs | Up | Prev: 9.2 Running PCCOM on the Target | Next: 10 pcget

If you need to create your own file transfer program or module, you can use the basic PCCOM commands and a special transfer protocol to send or receive files over the serial link. This is useful, for example, if you have an existing Windows or DOS program to which you would like the to add the ability to transfer files to or from the Zoomer (or another unit running PCCOM).

Sending a File to the Remote Machine

Sending a file to the remote machine involves the steps below. A file may be sent by any program that can access the serial port.

  1. Notify PCCOM that a file is on its way.
    Send the Send File escape character sequence to the serial port, notifying PCCOM that a file is about to be sent to it. The escape sequence is <Esc>XF<Ctrl-A> (<Esc>XF<0x01>).
  2. Send the destination file name.
    Send the name PCCOM should use for the file when saving it. The name is a string of sequential characters ending with a null byte. If sending the file to the target machine's target directory, this will just be the file's name; if sending to a different directory, this string should instead be the full pathname. Thus, to do the equivalent of "pcsend yuyuhack.sho /d:b:\geoworks\document", this string would consist of "b:\geoworks\document\yuyuhack.sho".
    If a timeout occurs while sending the filename, a NAK_QUIT will be returned and the remote machine will abort the file-send operation.
  3. Wait for acknowledgment of the name transfer.
    PCCOM will send a 0xFF character or a SYNC byte to acknowledge acceptance of the file name. If you do not receive this character, an error has likely occurred.
  4. Send the file size.
    The file size should be encoded as a dword value. Send the low byte first.
  5. Send a packet.
    Once you have received the SYNC or 0xFF byte, you can safely begin sending packets of data to PCCOM. A packet has the following format (sequence of bytes, with the first listed being the first sent):
    6. BLOCK_START				( = 1 )
data
BLOCK_END				( = 2 )
CRC				( checksum value )

    The data between BLOCK_START and the checksum value (CRC) may be up to 1 K. In order to avoid PCCOM confusion between a normal data byte and a BLOCK_START or BLOCK_END, a third element--BLOCK_QUOTE--is used.

    Any time you have a data byte equal to 1, 2, or 3, you must quote it by inserting a BLOCK_QUOTE byte before it and then adding three to its value. Thus, if you had a data sequence consisting of the following

    100, 42, 2, 3, 16

    you would send the following sequence of bytes to transfer the data:

    <Esc>FX1					( alert PCCOM a
                      file is coming )
<null-terminated file name>
<wait for SYNC byte>
BLOCK_START					( = 1 )
100
42
BLOCK_QUOTE					( = 3 )
5					( = 2 + 3 )
BLOCK_QUOTE					( = 3 )
6					( = 3 + 3 )
16
BLOCK_END					( = 2 )
CRC					( checksum value )

    The CRC word is two bytes of checksum value as calculated using the table and code shown in "Calculating Checksum Values," below. The CRC value is based upon the data bytes only. The low byte is transmitted first. You should use this code to ensure that your checksums will match PCCOM's.

  6. Optionally, the first packet may contain the filename and file size.
    In the first (and only the first) packet, it is possible to send a data block containing the file's name. If such a block is received by pccom or the PCCom library, then this filename will take precedence over the filename that was sent before the packets. Since the first filename may have been corrupted by noise, this provides a surer backup. The file size is passed as a dword value, low byte first.

    7. This data block should have the following data:

    "!PCCom File Transfer Filename Block! "  <no NULL>
<null-terminated file name> <null> <dword file size>

    The CRC for this block should be one higher than it would normally be--this signals that this block is of this special format.

  7. Wait for a SYNC byte acknowledgment.
    After each packet, you must make sure PCCOM responds positively with another SYNC byte. If the SYNC is not received, the packet likely failed. If the packet succeeded, continue sending packets as above (waiting for a SYNC after each) until done transferring the data.

    8. If, instead of receiving a SYNC value, you receive a NAK_QUIT value, the target machine has aborted to an unrecoverable error, and there is little point in continuing to send data over the serial line.

    A NAK byte signals that the target machine had an error but that it may be possible to continue the send operation by re-sending the block.

    When sending an optional filename packet, as described in step six, normally one does not re-send the packet it if it fails to send.

  8. Repeat steps five and seven as many times as necessary to transfer the entire file.
  9. Send two zero bytes.
    To make it absolutely clear that the file transfer has finished, send two zero bytes.

Retrieving a File Remotely

Retrieving a file from a machine running PCCOM is straightforward and uses the same file transfer protocol shown above for sending a file. The sequence of commands is different, however, and is listed below.

  1. Notify PCCOM that you're getting a file.
    Send the Get File escape character sequence to the serial port, notifying PCCOM that it should get ready to send a file. The escape sequence is <Esc>XF<Ctrl-D> (<Esc>XF<0x04>).
  2. Send the source file name.
    Send the name of the file to be retrieved. The name is a string of sequential characters ending with a null byte.
  3. Wait for SYNC byte signifying acknowledgment of the name transfer.
    PCCOM will send a 0xFF character or a SYNC byte to acknowledge acceptance of the file name. If you do not receive this character, an error has likely occurred (e.g., the file name wasn't successfully transmitted).
  4. Wait for another SYNC byte signifying file found.
    PCCOM will send a SYNC byte to acknowledge that the desired file was found.
  5. Wait for RECV_COMMAND byte.
    PCCOM will send a RECV_COMMAND byte signifying the file was successfully opened.
  6. Receive destination file name.
    This null-terminated string contains the place to write the file on the host machine. This path string may be up to size PATH_BUFFER_SIZE (defined in file.h).
  7. Send a SYNC byte to acknowledge receipt of the destination file name.
    By sending a SYNC byte, the host acknowledges that it has received the destination file name and is ready to continue the transfer.
  8. Receive the file size.
    The next data received will be the file's size, expressed as a dword, with the low byte sent first.
  9. Receive packets.
    After you receive thefile size from PCCOM, you will begin receiving data packets. Each packet you receive will follow the same format as described above for sending a file. Be aware of the BLOCK_QUOTE requirements when receiving the data (described in sending a file).

    10. You will receive a checksum word at the end of each block's data. If the sent checksum matches the one you calculate from the received data, send a SYNC byte to the serial port (0xFF); otherwise send a NAK value. Calculate all your checksum values with the table and code presented in "Calculating Checksum Values," below.

    When retrieving files, there will never be a file name packet such as described in step six of the file-sending procedure. Thus, you need not check to see whether a block whose CRC is off by one in fact contains a file name; you should not acknowledge this block.

  10. Receive two zero bytes.
    These make it clear that the file transfer has been completed.
  11. Send an ACK byte.
    This acknowledges the end of the file transfer.
  12. Repeat steps 4 through 11 until END_COMMAND byte received.
    If the filename passed to pcget contained wildcard characters and there is more than one matching file, PCCom will send all of them, using the protocol outlined in steps 4 through 11. If in step 4 an END_COMMAND byte is received instead of a SYNC byte, all files have been sent.

Calculating Checksum Values

The CRC word that accompanies each block of transferred data must be calculated using the same code as PCCOM or you will probably have only unsuccessful transmissions. The code used by PCCOM is shown on the next page, and you may include it in your own file transfer program. PCSEND, PCGET, and PCS also use the same checksum calculation code. This checksum should be based only on the data itself; do not include the BLOCK_START, BLOCK_QUOTE, or BLOCK_END characters in your calculations.

Code Display 9-1 PCCOM Checksums

/**********************************************************************
 *		CalcCRC						*
 **********************************************************************
 * SUMMARY:		Calculate the CRC on a block of data.
 * PASS:		char *buf		Pointer to the data buffer
 *		short size		Size of the data buffer
 *		short checksum		Previous checksum (0 at first)
 * RETURN:		CRC value (2 bytes)
 **********************************************************************/
short	crcTable[] = {
    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
    0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
    0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
    0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
    0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
    0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
    0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
    0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
    0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
    0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
    0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
    0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
    0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
    0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
    0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
    0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
    0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
    0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
    0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
    0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
    0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
    0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
    0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
    0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
    0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
    0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
    0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
    0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
    0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
    0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
    0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
    0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
unsigned short		IncCRC(unsigned short crc, char c){
    return ((crc << 8) ^ crcTable[((crc >> 8) ^ c) & 0xff]);
}
short		CalcCRC(char *buf, short size, short checksum){
/* The CRC is for the data part of the packet only.
 * The CRC value is passed low byte first. */
    for (;size > 0; size--){
     checksum = IncCRC(checksum, *buf++);
    }
    return checksum;
}

Testing the Connection

If the other side of a PCCom connection exits, you will get no notification. One way to test the connection to make sure that's still open is to send a <Esc>AKon! command. This requests that the other side of the connection send an <Esc>ACK at completion of each command. Note that the other side will try to acknowledge of the <Esc>AKon!, so that gives you a quick indicator that the other side is active. Assuming you don't want to stay in this mode, send an <Esc>AKoff!.

| Up: GEOS SDK TechDocs | Up | Prev: 9.2 Running PCCOM on the Target | Next: 10 pcget


pcget

Using Tools: 10 pcget

| Up: GEOS SDK TechDocs | Up | Prev: 9 pccom | Next: 11 pcs

Once you have pccom running on the target machine, you can invoke pcget on the development machine to yank a file from the target machine to the development machine.

Normally you would just type

pcget goemon.gif

to retrieve a file from the target machine's current directory or

pcget ..\clipart\jigen.gif

to retrieve it from another directory. To use different speed settings than the pccom tool would normally use (the pccom tool's settings are normally determined by the PTTY environment variable), you may pass /b, /c, and /I flags, as you did when invoking the pccom tool. The /b flag sets the baud rate (e.g. "/b:19400"); the /c: flag sets the com port (e.g. "/c:3"), and the /I flag sets the IRQ level (e.g. "/I:3"). Note that these flags may be indicated with "-" instead of "/").

| Up: GEOS SDK TechDocs | Up | Prev: 9 pccom | Next: 11 pcs


pcs

Using Tools: 11 pcs

| Up: GEOS SDK TechDocs | Up | Prev: 10 pcget | Next: 12 pcsend

Once you have pccom running on the target machine, invoke pcs on the development machine to send your geode's executable to the proper place on the target machine. The pcs tool figures out which files to send (and which directory to send them to) via a number of rules set up in the ROOT_DIR\INCLUDE\PCS.PAT file. Thus it knows to send applications (files that end in .GEO) to the target machine's WORLD directory. (Note that the "/" and "-" characters in the flags below are interchangeable--hyphens are used with the n, S, and t flags by tradition.)

/I: IRQ
Communicate at a different interrupt level than pccom is presently using (this is a dangerous option).
-S file
Send all files mentioned in the file file.
/b: baudRate
Communicate at a different speed than pccom is presently using (this is a dangerous option).
/c: portNum
Communicate via a different COM port than pccom is presently using (this is a dangerous option).
-h
Get help.
-n
Send non-Error Checking version instead of EC.
-t token
Send all files associated with the named token (see below).

When using a token, pcs looks up the token in the INCLUDE\SEND file to determine which files should be sent. Generally all executables associated with an application, library, or mechanism are sent when the appropriate token is passed. Look in the SEND file to find out what the accepted tokens are and what they send. Suppose your send file consisted of the following lines:

PC		DRIVER/VIDEO/DUMB/HGC/HGC 					GEO
HGCAT 		DRIVER/VIDEO/DUMB/HGC/HGC 					GEO
PCB 		DRIVER/VIDEO/DUMB/HGC/HGC 					GEO
PCB 		DRIVER/MOUSE/LOGIBUS/LOGIBUS 					GEO
PCS 		DRIVER/VIDEO/DUMB/HGC/HGC 					GEO
PCS 		DRIVER/MOUSE/LOGISER/LOGISER 					GEO

Typing

pcs -t pc

would send the file DRIVER\VIDEO\DUMB\HGC\HGCEC.GEO (HGC.GEO if sending non-EC). Typing "pcs -S pcb" would send that file, and also DRIVER\MOUSE\LOGIBUS\LOGIBUSE.GEO (or LOGIBUS.GEO).

| Up: GEOS SDK TechDocs | Up | Prev: 10 pcget | Next: 12 pcsend


pcsend

Using Tools: 12 pcsend

| Up: GEOS SDK TechDocs | Up | Prev: 11 pcs | Next: 13 pmake

The pcsend tool sends files from the development machine to the target machine (assuming that the target machine is running pccom). Normally you use pcs to send geodes between machines, as that tool has knowledge of where geodes belong; pcsend is for those cases where the pcs tool's automatic behavior is undesirable. With pcsend, you can specify source and destination explicitly.

Normally you will invoke pcsend by typing something like:

pcsend zenigata.pcx

or (to send to a directory other than the target PC's current directory):

pcsend zenigata.pcx /d:..\clipart

In addition to the /d option, you may also pass /b, /c, or /I options to override the speed, port, and/or IRQ values in the PTTY environment variable. (You may use "-" instead of "/" when passing these flags.)

pcsend sendslow.com /b:2400
| Up: GEOS SDK TechDocs | Up | Prev: 11 pcs | Next: 13 pmake


pmake

Using Tools: 13 pmake

| Up: GEOS SDK TechDocs | Up | Down | Prev: 12 pcsend | Next: 14 Swat Stub

The pmake program is a make utility. This means that it takes a directory of sources and a makefile which contains knowledge of how to turn these sources into geodes. If there were no pmake , then you would have to type goc, bcc, and glue each time.

The pmake program will work correctly if you have set up your files correctly. This means that you must have a makefile. You should run mkmf to generate a makefile that knows how to generate geodes. If you are an experienced C programmer, you may have come up with some customizations that you use with your make utility. We still suggest that you work from a standard makefile (which knows about Goc and Glue), but include your customizations in a local.mk file (see How to Customize pmake ).

When you have just added or removed source files from a geode, you will have to generate new dependency information, which pmake will use when doing other makes. You can use pmake to generate this information (it will make an appropriate call to the makedpnd program; it will store the result in depends.mk ):

 pmake depend

Once you have created your source and make files, all you need to do to invoke pmake is type

pmake

The pmake tool can be used to construct a specific target. Thus, if you need to generate an .OBJ file but do not need the rest of the geode, you may type something like:

pmake sdtrk.obj

The system makefiles have been set up with knowledge of some special targets. If you are making a library and wish to install it in the proper directory so that all applications may use it, then signal this to pmake :

 pmake lib

The pmake program does have command-line arguments, but these are used only rarely; they are detailed below.

Copyright Notice and Acknowledgment

How to Customize pmake

Command Line Arguments

Contents of a Makefile

Advanced pmake Techniques

The Way Things Work

| Up: GEOS SDK TechDocs | Up | Down | Prev: 12 pcsend | Next: 14 Swat Stub


Copyright Notice and Acknowledgment

Using Tools: 13.1 pmake: Copyright Notice and Acknowledgment

| Up: GEOS SDK TechDocs | Up | Prev: 13 pmake | Next: 13.2 How to Customize pmake

The pmake tool comes under the following copyright notice:

Copyright© 1988, 1989 by the Regents of the University of California
Copyright© 1988, 1989 by Adam de Boor
Copyright© 1989 by Berkeley Softworks

Permission to use, copy, modify, and distribute this software ( pmake ) and its documentation for any non-commercial purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies. The University of California, Berkeley Softworks, and Adam de Boor make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.

The pmake program for the PC uses the SPAWNO routines by Ralf Brown to minimize memory use while shelling to DOS and running other programs.

| Up: GEOS SDK TechDocs | Up | Prev: 13 pmake | Next: 13.2 How to Customize pmake


How to Customize pmake

Using Tools: 13.2 pmake: How to Customize pmake

| Up: GEOS SDK TechDocs | Up | Prev: 13.1 Copyright Notice and Acknowledgment | Next: 13.3 Command Line Arguments

For most applications, executing mkmf will generate a perfect makefile. However, you may be creating an unusual geode or have some makefile definitions which you want to include. Fortunately, there are ways to customize your make environment without having to build a MAKEFILE from scratch.

Makefiles can #include other makefiles. If you have a file named LOCAL.MK in your make directory, then the standard makefile generated by mkmf will include it; if you wish to customize your makes, you can create a LOCAL.MK file and fill it with appropriate make directives.

Depending on how much customization you need to do, you may wish to read on to find out about makefile syntax. However, there are several simple things you can do without learning too much about makefiles.

# Pass extra flags to Goc:
 GOCFLAGS		+= flag1 flag2
# Pass extra flags to your C compiler:
 CCOMFLAGS		+= flag1 flag2
# Pass extra flags to the Esp assembler (if you have
# that tool):
 ASMFLAGS		+= flag1 flag2
# Pass extra flags to the Glue linker:
 LINKFLAGS 		+= flag1 flag2
# Look somewhere special for .GOC files
# (This pattern applies to any suffix):
.PATH.GOC 		: $(ROOT_DIR)\DIR1 \DIR2
# Specify geode name:
 GEODE 		= NAME
# Set NO_EC variable (which signals that we don't
# want to make an Error Checking version):
 NO_EC 		= 1
# If your preprocessor is not reachable via the
# Path environment variable:
 CPP 		= vol:\path\name
# If your C compiler is not reachable via the Path
# environment variable:
 CCOM 		= vol:\path\name
# Include some other make file
#include "OTHERMF.MK"
# Include the standard makefile directives
# This will include INCLUDE\GEODE.MK:
#include <$(SYSMAKEFILE)>
Sample local.mk Files
---FINGER\LOCAL.MK
# Local Makefile for FPaint
#  FPaint is stored in a directory called FINGER. This would normally confuse
#  pmake, which expects the geode name to be the same as the directory name.
#  Let us, therefore, alert pmake to the geode's real name:
GEODE		= FPAINT
# This was the only thing we wanted to change, so include standard definitions:
#include <$(SYSMAKEFILE)>
---PROMO\LOCAL.MK
# Local Makefile for Promo
#  Promo uses some clip art that isn't in or below its source directory, so we
#  tell pmake where to look for it:
.PATH.GOH 	: $(CSOURCE_PATHS) $(CINCLUDE_DIR) $(ROOT_DIR)\LOGOART
# This program contains no Error Checking code (See
# the GOC Programming chapter for information
# about EC code. So we tell pmake not to bother making an Error Checking
# version:
NO_EC = 1
# Include the standard system makefile:
#include <$(SYSMAKEFILE)>
| Up: GEOS SDK TechDocs | Up | Prev: 13.1 Copyright Notice and Acknowledgment | Next: 13.3 Command Line Arguments


Command Line Arguments

Using Tools: 13.3 pmake: Command Line Arguments

| Up: GEOS SDK TechDocs | Up | Prev: 13.2 How to Customize pmake | Next: 13.4 Contents of a Makefile

The pmake program comes with a wide variety of flags to choose from. They must be passed in the following order: flags (if any), variable assignments (if any), target (if any).

pmake [flags] [variables] [target]

The flags are as follows:

-d info
This causes pmake to print out debugging information that may prove useful to you. The info parameter is a string of single characters that tell pmake what aspects you are interested in. Most of these options will make little sense to you unless you've dealt with Make before. Just remember where this table is and come back to it as you read on. The characters and the information they produce are as follows:
*
All debugging information.
c
Conditional evaluation.
d
The searching and caching of directories.
m
The making of each target: what target is being examined; when it was last modified; whether it is out-of-date; etc.
p
Makefile parsing.
r
Remote execution.
s
The application of suffix-transformation rules.
t
The maintenance of the list of targets.
v
Variable assignment.

Of these, the "m" and "s" flags will be most useful.

-f file
Specify a makefile to read different from the default (MAKEFILE). If file is "-", pmake uses the standard input. This is useful for making "quick and dirty" makefiles.
-h
Prints out a summary of the various flags pmake accepts. It can also be used to find out what level of concurrence was compiled into the version of pmake you are using (look at -J and -L) and various other information on how pmake is configured.
-i
If you give this flag, pmake will ignore non-zero status returned by any of its shells. It's like placing a "-" before all the commands in the makefile.
-k
This is similar to -i in that it allows pmake to continue when it sees an error, but unlike -i where pmake continues blithely as if nothing went wrong, -k causes it to recognize the error and only continue work on those things that don't depend on the target, either directly or indirectly (through depending on something that depends on it), whose creation returned the error. (The "k" is for "keep going".)
-n
This flag tells pmake not to execute the commands needed to update the out-of-date targets in the makefile. Rather, pmake will simply print the commands it would have executed and exit. This is particularly useful for checking the correctness of a makefile. If pmake doesn't do what you expect it to, it's a good chance the makefile is wrong.
-p number
You should never have to use this option; it is used for debugging pmake . This causes pmake to print its input in a reasonable form, though not necessarily one that would make immediate sense to anyone but a pmake expert. The number is a bitwise-or of 1 and 2 where 1 means it should print the input before doing any processing and 2 says it should print it after everything has been re-created. Thus "-p 3"would print it twice--once before processing and once after (you might find the difference between the two interesting).
-q
If you give pmake this flag, it will not try to re-create anything. It will just query to see if anything is out-of-date and exit non-zero if so.
-s
This silences pmake , preventing it from printing commands before they're executed. It is the equivalent of putting an "@" before every command in the makefile.
-t
Rather than try to re-create a target, pmake will simply "touch" it so as to make it appear up-to-date. If the target didn't exist before, it will when pmake finishes, but if the target did exist, it will appear to have been updated.
-D var
Allows you to define a variable to have 1 as its value. The variable is a global variable, not a command-line variable. This is useful mostly for people who are used to the C compiler arguments and those using conditionals, which are described in Conditional Statements , below.
-I directory
Tells pmake another place to search for included makefiles.
-W
Suppresses pmake 's warnings. Note that tools which pmake invokes (Goc, Glue, etc.) may still print out warnings of their own.
| Up: GEOS SDK TechDocs | Up | Prev: 13.2 How to Customize pmake | Next: 13.4 Contents of a Makefile


Contents of a Makefile

Using Tools: 13.4 pmake: Contents of a Makefile

| Up: GEOS SDK TechDocs | Up | Prev: 13.3 Command Line Arguments | Next: 13.5 Advanced pmake Techniques

The pmake program takes as input a file that tells

  • which files depend on which other files and
  • what to do about files that are "out-of-date."

This file is known as a "makefile" and is usually kept in the top-most directory of the system to be built. While you can call the makefile anything you want, pmake will look for MAKEFILE in the current directory if you don't tell it otherwise. To specify a different makefile, use the -f flag (e.g. "pmake -f program.mk").

A makefile has four different types of lines in it:

  • File dependency specifications
  • Creation commands
  • Variable assignments
  • Comments, include statements and conditional directives

Any line may be continued over multiple lines by ending it with a backslash ("\"). The backslash, following newline and any initial whitespace on the following line are compressed into a single space before the input line is examined by pmake.

Dependency Lines

In any system, there are dependencies between the files that make up the system. For instance, in a program made up of several C source files and one header file, the C files will need to be re-compiled should the header file be changed. For a document of several chapters and one macro file, the chapters will need to be reprocessed if any of the macros changes. These are dependencies and are specified by means of dependency lines in the makefile.

On a dependency line, there are targets and sources , separated by a one- or two-character operator. The targets "depend" on the sources and are usually created from them. Any number of targets and sources may be specified on a dependency line. All the targets in the line are made to depend on all the sources. Targets and sources need not be actual files, but every source must be either an actual file or another target in the makefile. If you run out of room, use a backslash at the end of the line to continue onto the next one.

Any file may be a target and any file may be a source, but the relationship between the two (or however many) is determined by the "operator" that separates them. Three types of operators exist: one specifies that the datedness of a target is determined by the state of its sources, while another specifies other files (the sources) that need to be dealt with before the target can be re-created. The third operator is very similar to the first, with the additional condition that the target is out-of-date if it has no sources. These operations are represented by the colon, the exclamation point and the double-colon, respectively, and are mutually exclusive (to represent a colon in a target, you must precede it with a backslash: "\:"). Their exact semantics are as follows:

:
If a colon is used, a target on the line is considered to be "out-of-date" (and in need of creation) if
  • the target doesn't exist or
  • any of the sources has been modified more recently than the target.

Under this operator, steps will be taken to re-create the target only if it is found to be out-of-date by using these two rules.

!
If an exclamation point is used, the target will always be re-created, but this will not happen until all of its sources have been examined and re-created, if necessary.
::
If a double-colon is used, a target is out-of-date if
  • the target has no sources,
  • any of the sources has been modified more recently than the target, or
  • the target doesn't exist.

If the target is out-of-date according to these rules, it will be re-created. This operator also does something else to the targets, as described in Shell Commands).

Suppose there are three C files ( a.c , b.c and c.c ) each of which includes the file defs.h . The dependencies between the files could then be expressed as follows:

PROGRAM.EXE 			: A.OBJ B.OBJ C.OBJ
A.OBJ B.OBJ C.OBJ 			: DEFS.H
A.OBJ 			: A.C
B.OBJ 			: B.C
C.OBJ 			: C.C

You may be wondering at this point, where A.OBJ, B.OBJ and C.OBJ came in and why they depend on defs.h and the C files don't. The reason is quite simple: PROGRAM.EXE cannot be made by linking together .c files--it must be made from .obj files. Likewise, if you change DEFS.H, it isn't the .c files that need to be re-created, it's the .obj files. If you think of dependencies in these terms--which files (targets) need to be created from which files (sources)-- you should have no problems.

An important thing to notice about the above example is that all the .obj files appear as targets on more than one line. This is perfectly all right: the target is made to depend on all the sources mentioned on all the dependency lines. For example, A.OBJ depends on both DEFS.H and A.C.

The order of the dependency lines in the makefile is important: the first target on the first dependency line in the makefile will be the one that gets made if you don't say otherwise. That's why PROGRAM.EXE comes first in the example makefile, above.

Both targets and sources may contain the standard C-Shell wildcard characters ({, }, *, ?, [, and ]), but the square braces may only appear in the final component (the file portion) of the target or source. The characters mean the following things:

{ }
These enclose a comma-separated list of options and cause the pattern to be expanded once for each element of the list. Each expansion contains a different element. For example,
SRC\{WHIFFLE,BEEP,FISH}.C

expands to the three words "SRC\WHIFFLE.C", "SRC\BEEP.C", and "SRC\FISH.C". These braces may be nested and, unlike the other wildcard characters, the resulting words need not be actual files. All other wildcard characters are expanded using the files that exist when pmake is started.

*
This matches zero or more characters of any sort.
SRC\*.C

will expand to the same three words as above as long as src contains those three files (and no other files that end in .c).

?
Matches any single character.
[ ]
This is known as a character class and contains either a list of single characters, or a series of character ranges ([a-z], for example means all characters between a and z), or both. It matches any single character contained in the list. E.g. [A-Za-z] will match all letters, while [0123456789] will match all numbers.

Shell Commands

At this point, you may be wondering how files are re-created. The re-creation is accomplished by commands you place in the makefile. These commands are passed to the shell to be executed and are expected to do what's necessary to update the target file. (The pmake program doesn't actually check to see if the target was created. It just assumes it's there.)

Shell commands in a makefile look a lot like shell commands you would type, with one important exception: each command in a makefile must be preceded by at least one tab.

Each target has associated with it a shell script made up of one or more of these shell commands. The creation script for a target should immediately follow the dependency line for that target. While any given target may appear on more than one dependency line, only one of these dependency lines may be followed by a creation script, unless the "::" operator was used on the dependency line.

If the double-colon was used, each dependency line for the target may be followed by a shell script. That script will only be executed if the target on the associated dependency line is out-of-date with respect to the sources on that line, according to the rules given earlier.

To expand on the earlier makefile, you might add commands as follows:

PROGRAM.EXE : A.OBJ B.OBJ C.OBJ
    BCC A.OBJ B.OBJ C.OBJ -o PROGRAM.EXE
A.OBJ B.OBJ C.OBJ : DEFS.H
A.OBJ : A.C
    bcc -c A.C
B.OBJ : B.C
    bcc -c B.C
C.OBJ : C.C
    bcc -c C.C

Something you should remember when writing a makefile is that the commands will be executed if the target on the dependency line is out-of-date, not the sources. In this example, the command "bcc -c a.c" will be executed if a.obj is out-of-date. Because of the ":" operator, this means that should a.c or defs.h have been modified more recently than a.obj , the command will be executed ( a.obj will be considered out-of-date).

There is another way in which makefile commands differ from regular shell commands. The first two characters after the initial whitespace are treated specially. If they are any combination of " @" and " -", they cause pmake to do things differently.

In most cases, shell commands are printed before they're actually executed. This is to keep you informed of what's going on. If an "@" appears, however, this echoing is suppressed. In the case of an echo command, perhaps "echo Linking index" it would be rather messy to output

echo Linking index
Linking index

The other special character is the dash ("-"). Shell commands finish with a certain "exit status." This status is made available by the operating system to whatever program invoked the command. Normally this status will be zero if everything went ok and non-zero if something went wrong. For this reason, pmake will consider an error to have occurred if one of the shells it invokes returns a non-zero status. When it detects an error, pmake 's usual action is to abort whatever it's doing and exit with a non-zero status itself. This behavior can be altered, however, by placing a "-" at the front of a command (e.g. "-copy index index.old") . In such a case, the non-zero status is simply ignored and pmake keeps going.

If the system call should be made through the DOS COMMAND.COM, precede the shell command with a backquote (`).

Variables

The pmake program has the ability to save text in variables to be recalled later at your convenience. Variables in pmake are used much like variables in the shell and, by tradition, consist of all upper-case letters. Variables are assigned using lines of the form

VARIABLE  = value

append using lines of the form

VARIABLE  += value

conditionally assigned (if the variable isn't already defined) by using lines of the form

VARIABLE  ?= value

and assigned with expansion (i.e. the value is expanded (see below) before being assigned to the variable--useful for placing a value at the beginning of a variable, or other things) by using lines of the form

VARIABLE  := value

Any whitespace before value is stripped off. When appending, a space is placed between the old value and the values being appended.

The final way a variable may be assigned is using lines of the form

VARIABLE  != shell-command

or, if the shell command requires the use of the command.com interpreter,

VARIABLE !=  `shell-command

In this case, shell-command has all its variables expanded (see below) and is passed off to a shell to execute. The output of the shell is then placed in the variable. Any newlines (other than the final one) are replaced by spaces before the assignment is made. This is typically used to find the current directory via a line like:

CURRENT_DIR != `cd

The value of a variable may be retrieved by enclosing the variable name in parentheses or curly braces and preceding the whole thing with a dollar sign. For example, to set the variable CFLAGS to the string "-I\NIHON\LIB\LIBC -O", you would place a line

CFLAGS = -I\NIHON\LIB\LIBC -O

in the makefile and use the expression

$(CFLAGS)

wherever you would like the string "-I\NIHON\LIB\LIBC -O" to appear. This is called variable expansion.

There are two different times at which variable expansion occurs: When parsing a dependency line, the expansion occurs immediately upon reading the line. Variables in shell commands are expanded when the command is executed. Variables used inside another variable are expanded whenever the outer variable is expanded (the expansion of an inner variable has no effect on the outer variable. That is, if the outer variable is used on a dependency line and in a shell command, and the inner variable changes value between when the dependency line is read and the shell command is executed, two different values will be substituted for the outer variable).

Variables come in four flavors, though they are all expanded the same and all look about the same. They are (in order of expanding scope)

  • local variables.
  • command-line variables.
  • global variables.
  • environment variables.

The classification of variables doesn't matter much, except that the classes are searched from the top (local) to the bottom (environment) when looking up a variable. The first one found wins.

Local Variables

Each target can have as many as seven local variables. These are variables that are only "visible" within that target's shell script and contain such things as the target's name, all of its sources (from all its dependency lines), those sources that were out-of-date, etc. Four local variables are defined for all targets. They are

.TARGET
The name of the target.
.OODATE
The list of the sources for the target that were considered out-of-date. The order in the list is not guaranteed to be the same as the order in which the dependencies were given.
.ALLSRC
The list of all sources for this target in the order in which they were given.
.PREFIX
The target without its suffix and without any leading path. For example, for the target ..\..\LIB\FSREAD.C , this variable would contain FSREAD.

One other local variable, .IMPSRC, is set only for certain targets under special circumstances. It is discussed below.

Two of these variables may be used in sources as well as in shell scripts. These are .TARGET and .PREFIX. The variables in the sources are expanded once for each target on the dependency line, providing what is known as a "dynamic source," allowing you to specify several dependency lines at once. For example,

$(OBJS) : $(.PREFIX).c

will create a dependency between each object file and its corresponding C source file.

Command-line Variables

Command-line variables are set when pmake is first invoked by giving a variable assignment as one of the arguments. For example,

pmake "CFLAGS = -I\NIHON\LIB\LIBC -O"

would make CFLAGS be a command-line variable with the given value. Any assignments to CFLAGS in the makefile will have no effect, because once it is set, there is (almost) nothing you can do to change a command-line variable. Command-line variables may be set using any of the four assignment operators, though only = and ?= behave as you would expect them to, mostly because assignments to command-line variables are performed before the makefile is read, thus the values set in the makefile are unavailable at the time. += is the same as = because the old value of the variable is sought only in the scope in which the assignment is taking place. The := and ?= operators will work if the only variables used are in the environment.

Global Variables

Global variables are those set or appended in the makefile. There are two classes of global variables: those you set and those pmake sets. The ones you set can have any name you want them to have, except they may not contain a colon or an exclamation point. The variables pmake sets (almost) always begin with a period and contain only upper-case letters. The variables are as follows:

.PMAKE
The name by which pmake was invoked is stored in this variable. For compatibility, the name is also stored in the MAKE variable.
.MAKEFLAGS
All the relevant flags with which pmake was invoked. This does not include such things as "-f" or variable assignments. Again for compatibility, this value is stored in the MFLAGS variable as well.

Two other variables, .INCLUDES and .LIBS, are covered in the section on special targets (See Special Targets ).

Global variables may be deleted using lines of the form:

 #undef variable

The "#" must be the first character on the line. Note that this may only be done to global variables.

Environment Variables

Environment variables are passed by the shell that invoked pmake and are given by pmake to each shell it invokes. They are expanded like any other variable, but they cannot be altered in any way.

One special environment variable, PMAKE, is examined by pmake for command-line flags, variable assignments, etc. that it should always use. This variable is examined before the actual arguments to pmake are. In addition, all flags given to pmake , either through the PMAKE variable or on the command line, are placed in this environment variable and exported to each shell pmake executes. Thus recursive invocations of pmake automatically receive the same flags as the top-most one.

Many other standard environment variables are defined and described in the Include\GEOS.MK included Makefile.

Using all these variables, you can compress the sample makefile even more:

OBJS = A.OBJ B.OBJ C.OBJ
PROGRAM.EXE : $(OBJS)
    BCC $(.ALLSRC) -o $(.TARGET)
$(OBJS) : DEFS.H
A.OBJ : A.C
    BCC -c A.C
B.OBJ : B.C
    BCC -c B.C
C.OBJ : C.C
    BCC -c C.C

In addition to variables which pmake will use, you can set environment variables which shell commands may use using the pmake_set directive.

.C.EBJ :
pmake_set CL = $(CCOMFLAGS) /Fo$(.TARGET)
$(CCOM) $(.IMPSRC)

You might use the above sequence to set up an argument list in the CL environment variable if your compiler (invoked with CCOM) needed its arguments in such a variable and was unable to take arguments in a file.

Comments

Comments in a makefile start with a "#" character and extend to the end of the line. They may appear anywhere you want them, except where they might be misinterpreted as a shell command.

Transformation Rules

As you know, a file's name consists of two parts: a base name, which gives some hint as to the contents of the file, and a suffix, which usually indicates the format of the file. Over the years, as DOS has developed, naming conventions, with regard to suffixes, have also developed that have become almost incontrovertible. For example, a file ending in .C is assumed to contain C source code; one with a .OBJ suffix is assumed to be a compiled, relocatable object file that may be linked into any program. One of the best aspects of pmake comes from its understanding of how the suffix of a file pertains to its contents and their ability to do things with a file based solely on its suffix. This ability comes from something known as a transformation rule. A transformation rule specifies how to change a file with one suffix into a file with another suffix.

A transformation rule looks much like a dependency line, except the target is made of two known suffixes stuck together. Suffixes are made known to pmake by placing them as sources on a dependency line whose target is the special target .SUFFIXES. For example:

.SUFFIXES 			: .obj .c
.c.obj 			:
    $(CCOM) $(CFLAGS) -c $(.IMPSRC)

The creation script attached to the target is used to transform a file with the first suffix (in this case, .c) into a file with the second suffix (here, .obj). In addition, the target inherits whatever attributes have been applied to the transformation rule. The simple rule above says that to transform a C source file into an object file, you compile it using your C compiler with the -c flag.

This rule is taken straight from the system makefile. Many transformation rules (and suffixes) are defined there; you should look there for more examples (type "pmake -h" to find out where it is).

There are some things to note about the transformation rule given above:

  1. The .IMPSRC variable. This variable is set to the "implied source" (the file from which the target is being created; the one with the first suffix), which, in this case, is the .c file.
  2. The CFLAGS variable. Almost all of the transformation rules in the system makefile are set up using variables that you can alter in your makefile to tailor the rule to your needs. In this case, if you want all your C files to be compiled with the -g flag, to provide information for Swat or CodeView, you would set the CFLAGS variable to contain -g ("CFLAGS = -g") and pmake would take care of the rest.

To give you a quick example, the makefile could be changed to this:

OBJS = A.OBJ B.OBJ C.OBJ
PROGRAM .EXE			: $(OBJS)
    $(CCOM) -o $(.TARGET) $(.ALLSRC)
$(OBJS) 			: DEFS.H

The transformation rule given above takes the place of the 6 lines.

A.OBJ : A.C
    BCC -c A.C
B.OBJ : B.C
    BCC -c B.C
C.OBJ : C.C
    BCC -c C.C

Now you may be wondering about the dependency between the .obj and .c files--it's not mentioned anywhere in the new makefile. This is because it isn't needed: one of the effects of applying a transformation rule is the target comes to depend on the implied source (hence the name).

For a more detailed example, Suppose you have a makefile like this:

A.EXE 			: A.OBJ B.OBJ
    $(CCOM) $(.ALLSRC)

and a directory set up like this:

total 4

MAKEFILE 		  34	09-07-89 		12:43a
A        C		 119	10-03-89 		 7:39p
A        OBJ 		201	09-07-89 		12:43a
B        C		  69	09-07-89 		12:43a

While just typing " pmake " will do the right thing, it's much more informative to type " pmake -ds " This will show you what pmake is up to as it processes the files. In this case, pmake prints the following:

Suff_FindDeps (A.EXE)
    using existing source A.OBJ
    applying .OBJ -> .EXE to "A.OBJ"
Suff_FindDeps (A.OBJ)
    trying A.C...got it
    applying .C -> .OBJ to "A.C"
Suff_FindDeps (B.OBJ)
    trying B.C...got it
    applying .C -> .OBJ to "B.C"
Suff_FindDeps (A.C)
    trying A.Y...not there
    trying A.L...not there
    trying A.C,V...not there
    trying A.Y,V...not there
    trying A.L,V...not there
Suff_FindDeps (B.C)
    trying B.Y...not there
    trying B.L...not there
    trying B.C,V...not there
    trying B.Y,V...not there
    trying B.L,V...not there
--- A.OBJ ---
bcc -c A.C
--- B.OBJ ---
bcc -c B.C
--- A.EXE ---
bcc A.OBJ B.OBJ

Suff_FindDeps is the name of a function in pmake that is called to check for implied sources for a target using transformation rules. The transformations it tries are, naturally enough, limited to the ones that have been defined (a transformation may be defined multiple times, by the way, but only the most recent one will be used). You will notice, however, that there is a definite order to the suffixes that are tried. This order is set by the relative positions of the suffixes on the .SUFFIXES line--the earlier a suffix appears, the earlier it is checked as the source of a transformation. Once a suffix has been defined, the only way to change its position is to remove all the suffixes (by having a .SUFFIXES dependency line with no sources) and redefine them in the order you want. (Previously-defined transformation rules will be automatically redefined as the suffixes they involve are re-entered.)

Another way to affect the search order is to make the dependency explicit. In the above example, a.exe depends on a.obj and b.obj. Since a transformation exists from .obj to .exe, pmake uses that, as indicated by the "using existing source a.obj" message.

The search for a transformation starts from the suffix of the target and continues through all the defined transformations, in the order dictated by the suffix ranking, until an existing file with the same base (the target name minus the suffix and any leading directories) is found. At that point, one or more transformation rules will have been found to change the one existing file into the target.

For example, ignoring what's in the system makefile for now, say you have a makefile like this:

.SUFFIXES : .EXE .OBJ .C .Y .L
.L.C :
    LEX $(.IMPSRC)
    MOVE LEX.YY.C $(.TARGET)
.Y.C :
    YACC $(.IMPSRC)
    MOVE Y.TAB.C $(.TARGET)
.C.OBJ :
    BCC -L $(.IMPSRC)
.OBJ.EXE :
    BCC -o $(.TARGET) $(.IMPSRC)

and the single file jive.l. If you were to type pmake -rd ms jive.exe , you would get the following output for jive.exe:

Suff_FindDeps (JIVE.EXE)
trying JIVE.OBJ...not there
trying JIVE.C...not there
trying JIVE.Y...not there
trying JIVE.L...got it
applying .L -> .C to "JIVE.L"
applying .C -> .OBJ to "JIVE.C"
applying .OBJ -> .EXE to "JIVE.OBJ"

The pmake tool starts with the target jive.exe, figures out its suffix (.exe) and looks for things it can transform to a .exe file. In this case, it only finds .obj, so it looks for the file JIVE.OBJ.

It fails to find it, so it looks for transformations into a .obj file. Again it has only one choice: .c. So it looks for JIVE.C and fails to find it. At this point it can create the .c file from either a .y file or a .l file. Since .y came first on the .SUFFIXES line, it checks for jive.y first, but can't find it, so it looks for jive.l. At this point, it has defined a transformation path as follows: .l->.c-> .obj-> .exe and applies the transformation rules accordingly. For completeness, and to give you a better idea of what pmake actually did with this three-step transformation, this is what pmake printed for the rest of the process:

Suff_FindDeps (JIVE.OBJ)
using existing source JIVE.C
applying .C -> .OBJ to "JIVE.C"
Suff_FindDeps (JIVE.C)
using existing source JIVE.L
applying .L -> .C to "JIVE.L"
Suff_FindDeps (JIVE.L)
Examining JIVE.L...modified 17:16:01 Oct 4,
 1987...up-to-date
Examining JIVE.C...non-existent...out-of-date
--- JIVE.C ---
LEX JIVE.L
...meaningless lex output deleted...
MV LEX.YY.C JIVE.C
Examining JIVE.OBJ...non-existent...out-of-date
--- JIVE.OBJ ---
bcc -c JIVE.C
Examining JIVE.EXE...non-existent...out-of-date
--- JIVE.EXE ---
bcc -o JIVE.EXE JIVE.OBJ

Including Other Makefiles

Just as for programs, it is often useful to extract certain parts of a makefile into another file and just include it in other makefiles somehow. Many compilers allow you to use something like

#include "defs.h"

to include the contents of defs.h in the source file. The pmake program allows you to do the same thing for makefiles, with the added ability to use variables in the filenames. An include directive in a makefile looks either like this

#include <file>

or like this

#include "file"

The difference between the two is where pmake searches for the file: the first way, pmake will look for the file only in the system makefile directory (to find out what that directory is, give pmake the -h flag).

For files in double-quotes, the search is more complex; pmake will look in the following places in the given order:

  1. The directory of the makefile that's including the file.
  2. The current directory (the one in which you invoked pmake ).
  3. The directories given by you using -I flags, in the order in which you gave them.
  4. Directories given by .PATH dependency lines.
  5. The system makefile directory.

You are free to use pmake variables in the filename-- pmake will expand them before searching for the file. You must specify the searching method with either angle brackets or double-quotes outside of a variable expansion. That is, the following

SYSTEM= <command.mk>
#include $(SYSTEM)

won't work; instead use the following:

SYSTEM= command.mk
#include <$(SYSTEM)>

Saving Commands

There may come a time when you will want to save certain commands to be executed when everything else is done, by inserting an ellipsis "..." in the Makefile. Commands saved in this manner are only executed if pmake manages to re-create everything without an error.

Target Attributes

The pmake tool allows you to give attributes to targets by means of special sources. Like everything else pmake uses, these sources begin with a period and are made up of all upper-case letters. By placing one (or more) of these as a source on a dependency line, you are "marking" the target(s) with that attribute.

Any attributes given as sources for a transformation rule are applied to the target of the transformation rule when the rule is applied.

.DONTCARE
If a target is marked with this attribute and pmake can't figure out how to create it, it will ignore this fact and assume the file isn't really needed or actually exists and pmake just can't find it. This may prove wrong, but the error will be noted later on, not when pmake tries to create the target so marked. This attribute also prevents pmake from attempting to touch the target if given the "-t" flag.
.EXEC
This attribute causes its shell script to be executed while having no effect on targets that depend on it. This makes the target into a sort of subroutine. EXEC sources don't appear in the local variables of targets that depend on them (nor are they touched if pmake is given the -t flag).
.IGNORE
Giving a target the .IGNORE attribute causes pmake to ignore errors from any of the target's commands, as if they all had "-" before them.
.MAKE
The .MAKE attribute marks its target as being a recursive invocation of pmake . This forces pmake to execute the script associated with the target (if it's out-of-date) even if you gave the -n or -t flag. By doing this, you can start at the top of a system and type
pmake -n

and have it descend the directory tree (if your makefiles are set up correctly), printing what it would have executed if you hadn't included the -n flag.

.NOTMAIN
Normally, if you do not specify a target to make in any other way, pmake will take the first target on the first dependency line of a makefile as the target to create. That target is known as the "Main Target" and is labeled as such if you print the dependencies out using the -p flag. Giving a target, this attribute tells pmake that the target is definitely not the Main Target. This allows you to place targets in an included makefile and have pmake create something else by default.
.PRECIOUS
When pmake is interrupted (by someone typing control-C at the keyboard), it will attempt to clean up after itself by removing any half-made targets. If a target has the .PRECIOUS attribute, however, pmake will leave it alone. A side effect of the "::" operator is to mark the targets as .PRECIOUS.
.SILENT
Marking a target with this attribute keeps its commands from being printed when they're executed, just as if they had an "@" in front of them.
.USE
By giving a target this attribute, you turn it into pmake 's equivalent of a macro. When the target is used as a source for another target, the other target acquires the commands, sources and attributes (except .USE) of the source. If the target already has commands, the .USE target's commands are added to the end. If more than one .USE-marked source is given to a target, the rules are applied sequentially.

The typical .USE rule will use the sources of the target to which it is applied (as stored in the .ALLSRC variable for the target) as its "arguments." Several system makefiles (not to be confused with the system makefile) make use of these .USE rules to make developing easier (they're in the default, system makefile directory).

Special Targets

There are certain targets that have special meaning to pmake . When you use one on a dependency line, it is the only target that may appear on the left-hand-side of the operator. As for the attributes and variables, all the special targets begin with a period and consist of upper-case letters only. The targets are as follows:

.BEGIN
Any commands attached to this target are executed before anything else is done. You can use it for any initialization that needs doing.
.DEFAULT
This is sort of a .USE rule for any target (that was used only as a source) that pmake can't figure out any other way to create. It's only "sort of" a .USE rule because only the shell script attached to the .DEFAULT target is used. The .IMPSRC variable of a target that inherits .DEFAULT's commands is set to the target's own name.
.END
This serves a function similar to .BEGIN, in that commands attached to it are executed once everything has been re-created (so long as no errors occurred). It also serves the extra function of being a place on which pmake can hang commands you put off to the end. Thus the script for this target will be executed before any of the commands you save with the ellipsis marker.
.IGNORE
This target marks each of its sources with the .IGNORE attribute. If you don't give it any sources, then it is like giving the -i flag when you invoke pmake --errors are ignored for all commands.
.INCLUDES
The sources for this target are taken to be suffixes that indicate a file that can be included in a program source file. The suffix must already be declared with .SUFFIXES. Any suffix so marked will have the directories on its search path (see .PATH, below) placed in the .INCLUDES variable, each preceded by a "-I" flag. This variable can then be used as an argument for the compiler in the normal fashion. The ".h" suffix is already marked in this way in the system makefile. For example, if you have
.SUFFIXES 			: .PCX
.PATH.PCX 			: \CLIPART
.INCLUDES 			: .PCX

pmake places "-I\CLIPART" in the .INCLUDES variable and you can say

bcc $(.INCLUDES) -c xprogram.c

(Note: the . INCLUDES variable is not actually filled in until the entire makefile has been read.)

.INTERRUPT
When pmake is interrupted, it will execute the commands in the script for this target, if it exists.
.LIBS
This does for libraries what .INCLUDES does for include files, except the flag used is "-L", as required by those linkers that allow you to tell them where to find libraries. The variable used is .LIBS.
.MAIN
If you didn't give a target (or targets) to create when you invoked pmake , it will take the sources of this target as the targets to create.
.MAKEFLAGS
This target provides a way for you to always specify flags for pmake when the makefile is used. The flags are just as they would be typed to the shell (except you can't use shell variables unless they're in the environment), though the -f and -r flags have no effect.
.PATH
If you give sources for this target, pmake will take them as directories in which to search for files it cannot find in the current directory. If you give no sources, it will clear out any directories added to the search path before.
.PATHsuffix
This does a similar thing to .PATH, but it does it only for files with the given suffix. The suffix must have been defined already. Look at Search Paths for more information.
.PRECIOUS
Similar to .IGNORE, this gives the .PRECIOUS attribute to each source on the dependency line, unless there are no sources, in which case the .PRECIOUS attribute is given to every target in the file.
.RECURSIVE
This target applies the .MAKE attribute to all its sources. It does nothing if you don't give it any sources.
.SILENT
When you use .SILENT as a target, it applies the .SILENT attribute to each of its sources. If there are no sources on the dependency line, then it is as if you gave pmake the -s flag and no commands will be echoed.
.SUFFIXES
This is used to give new file suffixes for pmake to handle. Each source is a suffix pmake should recognize. If you give a .SUFFIXES dependency line with no sources, pmake will forget about all the suffixes it knew.

In addition to these targets, a line of the form

attribute : sources

applies the attribute to all the targets listed as sources .

Modifying Variable Expansion

Variables need not always be expanded verbatim. The pmake program defines several modifiers that may be applied to a variable's value before it is expanded. You apply a modifier by placing it after the variable name with a colon between the two, like so:

${VARIABLE:modifier}

Each modifier is a single character followed by something specific to the modifier itself. You may apply as many modifiers as you want--each one is applied to the result of the previous and is separated from the previous by another colon.

There are several ways to modify a variable's expansion:

:M pattern
This is used to select only those words (a word is a series of characters that are neither spaces nor tabs) that match the given pattern. The pattern is a wildcard pattern like that used by the shell, where * means zero or more characters of any sort; ? is any single character; [abcd] matches any single character that is one of "a", "b", "c" or "d" (there may be any number of characters between the brackets); [0-9] matches any single character that is between "0" and "9" (i.e. any digit--this form may be freely mixed with the other bracket form), and `\' is used to escape any of the characters "*", "?", "[" or ":", leaving them as regular characters to match themselves in a word.
Remember that the pattern matcher requires you to prefix certain characters with a backslash, including backslash itself. this can lead to some impressive search strings, because pmake also requires that backslashes be preceded with backslashes:
    #if !empty(CURRENT_DIR:M*\\\\APPL\\\\*)

The above line checks to see if the current directory matches the form "*\APPL\*". (The pattern matcher is passed the string "*\\APPL\\*".)

:N pattern
This is identical to :M except it substitutes all words that don't match the given pattern.
:X pattern
This is like :M except that it returns only part of the matching string. You mark which part of the string you are interested in by enclosing it within backslashed square brackets ("\[" and "\]") (however, due to the backslash rules, you must actually use "\\[" and "\\]".)
    DEVEL_DIR 	:= \
     $(CURRENT_DIR:X \\[*\\\\$(ROOT_DIR:T)\\\\*\\]\\\\*)

The above line returns part of the CURRENT_DIR string, specifically the directory just under the root directory. Free of backslashes, it's searching for [*\$(ROOT_DIR:T)\*]\*. If there is a subdirectory below the development directory, then this will strip off the lower layers.

:S/ search-string / replacement-string /[g]
This causes the first occurrence of search-string in the variable to be replaced by replacement-string, unless the "g" flag is given at the end, in which case all occurrences of the string are replaced. The substitution is performed on each word in the variable in turn. If search-string begins with a caret ("^"), the string must match starting at the beginning of the word. If search-string ends with a dollar sign ("$"), the string must match to the end of the word (these two may be combined to force an exact match). If a backslash precedes these two characters, however, they lose their special meaning. Variable expansion also occurs in the normal fashion inside both the search-string and the replacement-string, except that a backslash is used to prevent the expansion of a "$", not another dollar sign, as is usual. Note that search-string is just a string, not a pattern, so none of the usual regular-expression/wildcard characters have any special meaning save "^" and "$". In the replacement string, the "&" character is replaced by the search-string unless it is preceded by a backslash. Thus, ":S/[A-D]/&&/" will find the string "[A-D]" and replace it with the string "[A-D][A-D]". You are allowed to use any character except colon or exclamation point to separate the two strings. This so-called delimiter character may be placed in either string by preceding it with a backslash.
:T
Replaces each word in the variable expansion by its last component (its "tail"). For example, given
        OBJS = ..\LIB\A.OBJ B \USR\LIB\LIBM.A
        TAILS = $(OBJS:T)

the variable TAILS would expand to "a.obj b libm.a" .

:H
This is similar to :T, except that every word is replaced by everything but the tail (the "head"). Using the same definition of OBJS, the string "$(OBJS:H)" would expand to "..\LIB \USR\LIB" Note that the final slash on the heads is removed and anything without a head is replaced by a single period.
:E
This replaces each word with its suffix ("extension"), so "$(OBJS:E)" would give you ".OBJ .A" .
:R
This replaces each word with everything but the suffix (thus returning the "root" of the word). "$(OBJS:R)" expands to "..\LIB\A B \USR\LIB\LIBM".

In addition, another style of substitution is also supported. This looks like:

$(VARIABLE:search-string=replacement)

It must be the last modifier in the chain. The search is anchored at the end of each word, so only suffixes or whole words may be replaced.

| Up: GEOS SDK TechDocs | Up | Prev: 13.3 Command Line Arguments | Next: 13.5 Advanced pmake Techniques


Advanced pmake Techniques

Using Tools: 13.5 pmake: Advanced pmake Techniques

| Up: GEOS SDK TechDocs | Up | Prev: 13.4 Contents of a Makefile | Next: 13.6 The Way Things Work

This section is devoted to those facilities in pmake that allow you to do a great deal in a makefile with very little work, as well as do some things you couldn't do in make without a great deal of work (and perhaps the use of other programs). The problem with these features is that they must be handled with care, or you will end up with a mess.

Search Paths

The pmake tool supports the dispersal of files into multiple directories by allowing you to specify places to look for sources with .PATH targets in the makefile. The directories you give as sources for these targets make up a "search path." Only those files used exclusively as sources are actually sought on a search path, the assumption being that anything listed as a target in the makefile can be created by the makefile and thus should be in the current directory.

There are two types of search paths in pmake : one is used for all types of files (including included makefiles) and is specified with a plain .PATH target (e.g. ".PATH : RCS"), while the other is specific to a certain type of file, as indicated by the file's suffix. A specific search path is indicated by immediately following the .PATH with the suffix of the file. For instance

.PATH.H : \GEOSDEV\DEVEL\APPL\WORPRO \GEOSDEV\DEVEL

would tell pmake to look in the directories \GEOSDEV\DEVEL\APPL\WORPRO and \GEOSDEV\DEVEL for any files whose suffix is .H.

The current directory is always consulted first to see if a file exists. Only if it cannot be found are the directories in the specific search path, followed by those in the general search path, consulted.

A search path is also used when expanding wildcard characters. If the pattern has a recognizable suffix on it, the path for that suffix will be used for the expansion. Otherwise the default search path is employed.

When a file is found in some directory other than the current one, all local variables that would have contained the target's name (.ALLSRC and .IMPSRC) will instead contain the path to the file, as found by pmake . Thus if you have a file ..\LIB\MUMBLE.C and a makefile

.PATH.c : ..\LIB
MUMBLE.EXE 			: MUMBLE.C
    $(CCOM) -o $(.TARGET) $(.ALLSRC)

the command executed to create MUMBLE.EXE would be "bcc -o MUMBLE ..\LIB\MUMBLE.C"

If a file exists in two directories on the same search path, the file in the first directory on the path will be the one pmake uses. So if you have a large system spread over many directories, it would behoove you to follow a naming convention that avoids such conflicts.

Something you should know about the way search paths are implemented is that each directory is read, and its contents cached, exactly once--when it is first encountered--so any changes to the directories while pmake is running will not be noted when searching for implicit sources, nor will they be found when pmake attempts to discover when the file was last modified, unless the file was created in the current directory.

Conditional Statements

Like a C compiler, pmake allows you to configure the makefile using conditional statements. A conditional looks like this:

#if <Boolean expression>
<lines>
#elif <another Boolean expression>
<more lines>
#else
<still more lines>
#endif

They may be nested to a depth of 30 and may occur anywhere (except in a comment, of course). The "#" must be the very first character on the line.

Each Boolean expression is made up of terms that look like function calls, the standard C Boolean operators &&, ||, and !, and the standard relational operators ==, !=, >, >=, <, and <=, with == and != being overloaded to allow string comparisons as well. The && operator represents logical AND; || is logical OR and ! is logical NOT. The arithmetic and string operators take precedence over all three of these operators, while NOT takes precedence over AND, which takes precedence over OR. This precedence may be overridden with parentheses, and an expression may be parenthesized to any level. Each Boolean term looks like a call on one of four functions:

make
The syntax is make(target) where target is a target in the makefile. This is true if the given target was specified on the command line or as the source for a .MAIN target (note that the sources for .MAIN are only used if no targets were given on the command line).
defined
The syntax is defined(variable) and is true if variable is defined. Certain variables are defined in the system makefile that identify the system on which pmake is being run.
exists
The syntax is exists(file) and is true if the file can be found on the global search path (i.e. that defined by .PATH targets, not by .PATHsuffix targets).
empty
This syntax is much like the others, except the string inside the parentheses is of the same form as you would put between parentheses when expanding a variable, complete with modifiers. The function returns true if the resulting string is empty. (Note: an undefined variable in this context will cause at the very least a warning message about a malformed conditional, and at the worst will cause the process to stop once it has read the makefile. If you want to check for a variable being defined or empty, use the expression
    !defined(var) || empty(var)
as the definition of || will prevent the empty() from being evaluated and causing an error, if the variable is undefined). This can be used to see if a variable contains a given word, for example:
        #if !empty(var:Mword)

The arithmetic and string operators may only be used to test the value of a variable. The left-hand side must contain the variable expansion, while the right-hand side contains either a string, enclosed in double-quotes, or a number. The standard C numeric conventions (except for specifying an octal number) apply to both sides. For example,

#if $(OS) == 4.3
#if $(MACHINE) == "sun3"
#if $(LOAD_ADDR) < 0xc000

are all valid conditionals. In addition, the numeric value of a variable can be tested as a Boolean as follows:

#if $(LOAD)

would see if LOAD contains a non-zero value and

#if !$(LOAD)

would test if LOAD contains a zero value.

In addition to the bare #if, there are other forms that apply one of the first two functions to each term. They are as follows:

ifdef		defined
ifndef		!defined
ifmake		make
ifnmake		!make

There are also the "else if" forms: elif, elifdef, elifndef, elifmake, and elifnmake.

| Up: GEOS SDK TechDocs | Up | Prev: 13.4 Contents of a Makefile | Next: 13.6 The Way Things Work


The Way Things Work

Using Tools: 13.6 pmake: The Way Things Work

| Up: GEOS SDK TechDocs | Up | Prev: 13.5 Advanced pmake Techniques | Next: 14 Swat Stub

When pmake reads the makefile, it parses sources and targets into nodes in a graph. The graph is directed only in the sense that pmake knows which way is up. Each node contains not only links to all its parents and children (the nodes that depend on it and those on which it depends, respectively), but also a count of the number of its children that have already been processed.

The most important thing to know about how pmake uses this graph is that the traversal is breadth-first and occurs in two passes.

After pmake has parsed the makefile, it begins with the nodes the user has told it to make (either on the command line, or via a .MAIN target, or by the target being the first in the file not labeled with the .NOTMAIN attribute) placed in a queue. It continues to take the node off the front of the queue, mark it as something that needs to be made, pass the node to Suff_FindDeps() (mentioned earlier) to find any implicit sources for the node, and place all the node's children that have yet to be marked at the end of the queue. If any of the children is a .USE rule, its attributes are applied to the parent, then its commands are appended to the parent's list of commands and its children are linked to its parent. The parent's unmade-children counter is then decremented (since the .USE node has been processed). This allows a .USE node to have children that are .USE nodes, and the rules will be applied in sequence. If the node has no children, it is placed at the end of another queue to be examined in the second pass. This process continues until the first queue is empty.

At this point, all the leaves of the graph are in the examination queue; pmake removes the node at the head of the queue and sees if it is out-of-date. If it is, it is passed to a function that will execute the commands for the node asynchronously. When the commands have completed, all the node's parents have their unmade-children counter decremented and, if the counter is then zero, they are placed on the examination queue. Only those parents that were marked on the downward pass are processed in this way. Thus pmake traverses the graph back up to the nodes the user instructed it to create. When the examination queue is empty and no shells are running to create a target, pmake is finished.

Once all targets have been processed, pmake executes the commands attached to the .END target, either explicitly or through the use of an ellipsis in a shell script. If there were no errors during the entire process but there are still some targets unmade ( pmake keeps a running count of how many targets are left to be made), there is a cycle in the graph. The pmake program does a depth-first traversal of the graph to find all the targets that weren't made and prints them out one by one.

| Up: GEOS SDK TechDocs | Up | Prev: 13.5 Advanced pmake Techniques | Next: 14 Swat Stub


Swat Stub

Using Tools: 14 Swat Stub

| Up: GEOS SDK TechDocs | Up | Prev: 13 pmake | Next: 15 dumpgeo

The swat stub runs on the target machine, passing information between a running GEOS session and Swat on the host machine. It has one flag:

/H : hardware
Specify special hardware for sending signals. The only possible argument is P for sending via a serial port in a PCMCIA card.
| Up: GEOS SDK TechDocs | Up | Prev: 13 pmake | Next: 15 dumpgeo


dumpgeo

Using Tools: 15 dumpgeo

| Up: GEOS SDK TechDocs | Up | Prev: 14 Swat Stub | Next: 16 cvtpcx.exe
Use dumpgeo to display core information about the content of a .GEO file.

Common usage

    dumpgeo mygeode.geo

dumpgeo takes the following optional flags:

-t
Will also print brief information about the library and resource tables in the geode.
-r[id]
Prints detailed relocation tables.
-d[id]
Prints a binary dump of the resources in the geode.
| Up: GEOS SDK TechDocs | Up | Prev: 14 Swat Stub | Next: 16 cvtpcx.exe


cvtpcx

Using Tools: 16 cvtpcx

| Up: GEOS SDK TechDocs | Up | Prev: 15 dumpgeo | Next: 17 pvm

cvtpcx.exe is an application that converts PCX graphics files to the GEOS icon format, either as a .ui or a .goh file. There are two kinds of possible conversions. The PCX file may be converted to a single icon resource. Or the PCX file may contain the same icon image in a variety of different formats arranged in a predefined grid arrangement. The cvtpcx utility will convert each of these formats into its own icon resource within a single .goh or .ui file. For more information, refer to the bottom of this document.

The source PCX file may have at most a 16-color (4 bit) color lookup table. Use a program like Paint Shop Pro to reduce the number of colors to 2-16 colors and save it in PCX format.

The syntax for cvtpcx is

     cvtpcx <options> <filename>
If no <options> are specified, then this utility will convert the PCX file <filename> to single GEOS icon in a file called "mkr.ui" and display information about the icon.

<options> for either single icons or icons in a grid

These options specify the parameters for converting and saving the icon file.

-o<filename>
The file name for the moniker(s).
-G
Produce .goh output instead of .ui.
-g
Do not put resulting bitmaps in gstrings. The bitmap is still in a chunk, but no gstring opcodes surround it.
-j
Only output the gstring (don't create a moniker or put the gstring in a chunk)
-2
Use 2.0 constants in the resulting gstring/bitmap.
-f
Uses GrFillBitmap instead of GrDrawBitmap for all monochrome bitmaps. Also implies the use of 2.0 constants, since this will not work prior to version 2.0 as GrFillBitmap didn't exist.
-t
Cause the bitmap to be drawn relative to the current pen position if the program decides to optimize the moniker by drawing the bitmap somewhere other than 0,0. All monikers destined for the token database should be created with this flag.
-m<pixel>
Pixel to be masked out. Any pixel containing the color <pixel> (a decimal number) will be given a 0 bit in the mask for the color bitmap. Defaults to none.
-u
Insist the resulting bitmaps remain uncompacted. By default, cvtpcx will determine if it's worthwhile and automatically compact each bitmap for you.

Converting single icons:

A rectangle of the PCX image plane is converted into a single GEOS icon. The resulting file will have no start/end resource directives. The possible options include:

-n<filename>
Name the moniker "<filename>Moniker"
-N
map the colors to the indices for the Nokia device.
-w <width>
width to make GEOS icon bitmap. The input will be trimmed or extended (but masked) as necessary to accommodate this. Defaults to Standard size (48)
-h<height>
height to make GEOS icon bitmap. The input will be trimmed or extended (masked) as necessary to accommodate this. Defaults to Standard size (30)
-S<style>
<style> is one of the defined moniker styles: text, abbrevText, graphicText, icon, or tool. It defaults to icon. (The "-S" option is only for 2.0 and above.)
-s<size>
<size> is one of the defined moniker sizes: large, standard, or tiny. It defaults to standard.
-a<aspect>
<aspect> is one of the defined aspectRatio values: normal (vga), squished (ega) or verySquished (cga). Defaults to squished.
-b<aspect>
Forces creation of a single bitplane (B&W) icon, even if the source is 16 colors. Any pixel that isn't black (pixel 0) is set to 0 in the resulting bitmap. Black pixels are, of course, set to 1.
-B
Similar to -b, but any pixel that isn't white (pixel 15) or the mask (set by "-m") is set to 1.

Icons in a grid:

A grid is a sequence of the same icon in a variety of formats which are laid out from the left to the right. These formats share a common top edge, have a one pixel margin between icons, and a one pixel margin around the grid as a whole. There are 11 formats and three predefined grids.

The 11 predefined icon formats are:
Abbr | Color | Aspect | Size | Style | Width | Height
LC | 4-bit | normal | large | icon | 64 | 40
LM | Mono | normal | large | icon | 64 | 40
SC | 4-bit | normal | standard | icon | 48 | 30
SM | mono | normal | standard | icon | 48 | 30
LCGA | Mono | verySquished | large | icon | 64 | 18
SCGA | Mono | verySquished | tiny | icon | 48 | 14
YC | 4-bit | normal | tiny | icon | 32 | 20
YM | Mono | normal | tiny | icon | 32 | 20
TC | 4-bit | normal | tiny | tool | 15 | 15
TM | Mono | normal | tiny | tool | 15 | 15
TCGA | Mono | verySquished | tiny | tool | 15 | 10

The three grid layouts are:

Grid Flag | Formats within Grid
-l | LC, LM, SC, SM, LCGA, SCGA
-L | LC, LM, SC, SM, LCGA, SCGA, YC, YM, TM, TCGA (the YC and YM icons are used for application icons on handheld devices, while the TM and TCGA icons are used for the Presentation Manager system menu)
-z | TC, TM, TCGA

When the cvtpcx utility converts a grid to a GEOS icon resource file, it creates one file which contains a series of monikers which correspond to the formats in the grid. For example, a PCX file which is a -l grid will be converted to a GEOS header file which has a moniker with a LC format, followed by a moniker in a LM format, followed by a moniker in a SC format... etc. Each moniker is created with its own start/end resource directives.

The following options modify where in the PCX image plane the grid starts, how to name the monikers, which formats to not use, and how the GEOS file will be created.

-x<xoffset>
Specifies the left edge of the grid in the PCX image plane (the X coordinate of the 1-pixel margin to the left of the left-most icon in the grid.)
-y<yoffset>
Specifies the top edge of the grid (the Y coordinate of the 1-pixel margin to the top of the top- most icon in the grid.)
-n<moniker name>
Allows specifying the core name to give the moniker. The name for each moniker in a grid is formed as:
<moniker name><format abbrev>Moniker
For instance -nHello would create "HelloLCMoniker" for the leftmost icon in the "-l" grid.
-d<format(s)>
This flag indicates that the no moniker for the formats in the comma-separated list <format(s)> should be created for that grid.
-r<resource>
Specifies the string (other than the default "App") to begin the name of each resource. The resource names are of the form
<resource><format abbrev>MonikerResource.
The resource name is all uppercase for GOC output.
-R
Don't put out resource start/end directives; just produce the monikers, one after another.

Examples of cvtpcx:

The following converts the single icon "old_icon.pcx" to a .goh icon called "test.goh." The options for this icon specify that this icon uses GEOS 2.0 constants, is defined with GrFillBitmap(), uses Nokia colors, and will be drawn relative to the pen's location.

         cvtpcx -2 -f -t -N -G -otest.goh old_icon.pcx
The following converts the grid "grid_icon.pcx" in the -l grid arrangement into a .goh file with monikers for all the formats in a -l grid, with the exception of the LC format. The "-m11" option specifies that yellow pixels (those with the color 11) will be masked out. The output has the name "mkrconverted_grid_icon.goh".
        cvtpcx -l -2 -f -t -g -G -dLC -m11 -nconverted_grid_icon grid_icon.pcx
| Up: GEOS SDK TechDocs | Up | Prev: 15 dumpgeo | Next: 17 pvm


pvm

Using Tools: 17 pvm

| Up: GEOS SDK TechDocs | Up | Prev: 16 cvtpcx.exe

Use pvm on a VM file to display information about the file's header and brief information about the blocks within the VM file. (In DOS, VM files will have the extension ".000".)

Common Usage:

    pvm myfile.000
| Up: GEOS SDK TechDocs | Up | Prev: 16 cvtpcx.exe


GEOS SDK Index: symbols

MailboxChangeToMailboxDir() x
MailboxGetBodyMboxRefBlock() x
!
in makefiles x x
!=
in makefiles x x
"
localizing x
#elif makefile directive x
#else makefile directive x
#endif makefile directive x
#if makefile directive x
#ifdef makefile directive x
#ifmake makefile directive x
#ifndef makefile directive x
#ifnmake makefile directive x
#include makefile directive x
#undef makefile directive x
$
in Tcl x
$(VAR) expressions x
&&
in makefiles x
?
in makefiles x
?=
in makefiles x
Escape key
command completion in Swat x
'
localizing x
(
in Swat usage documentation x
)
in Swat usage documentation x
*
in makefile x
in Swat address expressions x
in Swat usage documentation x
+
in Swat address expressions x
in Swat usage documentation x
+=
in makefiles x
| x
||
in makefiles x
- (Hyphen)
Goc command-line argument x
in makefiles x
in Swat address expressions x
.
in Swat address expressions x
... (Ellipsis) in makefiles x
.ALLSRC makefile local variable x
.asm files x
.BEGIN makefile special target x
.c files x
.CFG file (SWAT.CFG file) x
.def files x
.DEFAULT makefile special target x
.dh files x
.doh files x
.DONTCARE makefile attribute x
.ebj files x
.END makefile special target x
.EXEC makefile attribute x
.geo files x
.goc files x x
Goc syntax x
.goh files x x
Goc syntax x
.gp files x x
for libraries x
hello world sample x
in Tutorial example x
overview x
sample x
syntax x
.h files x x
.IGNORE makefile attribute x
.IGNORE makefile special target x
.IMPSRC makefile variable x
.INCLUDES makefile special target x
.INTERRUPT makefile special target x
.ldf files x
.LIBS makefile special target x
.MAIN makefile special target x
.MAKE makefile attribute x
.MAKEFLAGS global variable x
.MAKEFLAGS makefile special target x
.mk files x
.NOTMAIN makefile attribute x
.obj files x
.OODATE makefile local variable x
.PATH x
.PATH makefile special target x
.ph files x
.PMAKE makefile variable x
.poh files x x
.PRECIOUS makefile attribute x
.PRECIOUS makefile special target x
.PREFIX makefile local variable x
.RC file (SWAT.RC file) x
.RECURSIVE makefile special target x
.rev files x
.rsc files x
.SILENT makefile attribute x
.SILENT makefile special target x
.SUFFIXES makefile special target x
.SWAT file x
.sym files x
.TARGET makefile local variable x
.USE makefile attribute x
<
in makefiles x
in Swat usage documentation x
<=
in makefiles x
=
$(V:s=r) expressions x
in makefiles x
==
in makefiles x
>
in makefiles x
in Swat usage documentation x
>=
in makefiles x
@
Address history symbol in Swat x
Goc object pointer shortcut x
in makefiles x
@ Swat address shortcut
in Tutorial example x
@alias x x
@call x x x
@callsuper x x
in Tutorial example x
@chunk x x
@chunkArray x x
@class x x
in Tutorial example x x
@classdecl x x
in Tutorial example x x
@composite x x
@default x x
in Tutorial example x
@define x x
@deflib x x
@dispatch x x
@dispatchcall x x
@elementArray x x
@end x x
in Tutorial example x x
@endc x x
in Tutorial example x x
@endif x x
@endlib x x
@exportMessages x x
@extern x x
with methods x
@genChildren (messaging shortcut) x x
@genParent (messaging shortcut) x x
@gstring x
example x
@header x x
@if x x
@ifdef x x
@ifndef x x
@importMessage x x
@include x x
in Tutorial example x x
@instance x x
@kbdAccelerator x
@kbdAccelerator[kbdAccelerator] x
@link x x
@localize x x x
in Tutorial example x x
@message x x
@method x x
in Tutorial example x x
@noreloc x x
@object x x
in Tutorial example x x
@optimize x x
@protominor x x
@prototype x x
@record x x
@reloc x x
MSG_META_RELOCATE x
MSG_META_UNRELOCATE x
@reserveMessages x x
@send x x
@specificUI x x
@stack x x
@start x x
in Tutorial example x
@uses x x
@vardata x x
@vardataAlias x x
@visChildren (messaging shortcut) x x x
@visMoniker x x x
in Tutorial example x x
@visParent (messaging shortcut) x x
[
in makefiles x
in Swat usage documentation x
[uiFeatures] x
:
changing threads in Swat x
in makefiles x
in Swat address expressions x
:0 Swat command
in Tutorial example x
:1 Swat command
in Tutorial example x
:=
in makefiles x
:: in makefiles x
:E makefile variable expansion x
:H makefile variable expansion x
:M makefile variable expansion x
:N makefile variable expansion x
:R makefile variable expansion x
:S makefile string substitution x
:T makefile variable expansion x
:X makefile variable expansion x
\ backslash substitution
in Tcl x
Ctrl-]
command completion in Swat x
\\[*\\\\$(ROOT_DIR\:T)\\\\*\\]\\\\*) makefile example x
]
in makefiles x
in Swat usage documentation x
^
Command correction in Swat x
^h
in Swat address expressions x
^l
in Swat address expressions x
^v
in Swat address expressions x
_print Swat command x
_reloc x x
` (Backquote)
In shell commands x
with != x
{ in makefiles x
} in makefiles x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: A

AA_... x
AAF_... x x x
abort Swat command x
abortframe Swat command x
ABS parse library internal function x
AccessPointCommit() x
AccessPointCompareStandardProperty() x
AccessPointCreateEntry() x
AccessPointDestroyEntry() x
AccessPointDestroyProperty() x
AccessPointGetEntries() x
AccessPointGetIntegerProperty() x
AccessPointGetStringPropertyBlock() x
AccessPointGetStringPropertyBuffer() x
AccessPointGetType() x
AccessPointInUse() x
AccessPointIsEntryValid() x
AccessPointLock() x
AccessPointSetIntegerProperty() x
AccessPointSetStringProperty() x
AccessPointStandardProperty x
AccessPointType x
AccessPointUnlock() x
ACOS parse library internal function x
ACOSH parse library internal function x
ACT_... x
Active list x
and detaching x
GenControlBuildFlags relating to x
MSG_GEN_DESTROY_AND_FREE_BLOCK x
MSG_GEN_GUP_FINISH_QUIT x
MSG_GEN_QUERY_NEED_TO_BE_ON_ACTIVE_LIST x
MSG_META_ATTACH x
MSG_META_ATTACH_OBJECT x
MSG_META_DETACH x
MSG_META_QUIT x
MSG_META_SAVE_OPTIONS x
addr-parse Swat command x
addr-preprocess Swat command x
addr-with-obj-flag Swat command x
Address expressions in Swat
notation x
of breakpoints x
parsing x x
Address history in Swat x
get-address Swat command x
AddUndoActionFlags x x x
AddUndoActionStruct x x x
AIR_... x
ALB_... x
ALF_... x x
@alias x x
alias Swat command x
command reference x
alignFields Swat variable x
.ALLSRC makefile local variable x
alt (keyboard accelerator modifier) x
AND parse library internal function x
antifreeze Swat command x
antithaw Swat command x
AOF_MULTIPLE_INIT_FILE_CATEGORIES x
AppAttachFlags x x
AppInstanceReference x
appl
as geode type in .gp file x x
AppLaunchBlock x
AppLaunchFlags x
application (messaging shortcut) x x
Application object
accessing x
in Tutorial example x
Applications
Geode type x
launching x
multithreaded x
saving to state file x
shutting down x
structure x
Applications, Sample
Hello World x
TicTac x
TicTac:input messages x
ApplicationStates x x
appobj .gp file keyword x x
in Tutorial example x
appobj Swat command x
apropos Swat command x x
ArcCloseType x x
Arcs x
AreaAttr x
ArgumentStackElement x
ArrayQuickSort() x x
Arrays in Swat
creating x
examining x
setting elements x
AS_... x x
ASCII text x
number strings x
ASE_... x
aset Swat command x
ASIN parse library internal function x
ASINH parse library internal function x
.asm files x
ASMFLAGS makefile variable x
AsmPassReturn x
assign Swat command x
command reference x
assoc Swat command x
Associative lists in Swat
deleting elements x
searching x
ATAN parse library internal function x
ATAN2 parse library internal function x
ATANH parse library internal function x
att Swat command x
command reference x
attach Swat command x
command reference x
ATTR_CONTACT_LIST_ALTERNATE_NOTIFY_CONTACT_SELECTED_MSG x
ATTR_CONTACT_LIST_ALTERNATE_NOTIFY_MULTIPLE_CONTACTS_SELECTED_MSG x
ATTR_CONTACT_LIST_ALTERNATE_NOTIFY_NUMBER_ENTERED_MSG x
ATTR_CONTACT_LIST_NON_EXCLUSIVE_LIST x
ATTR_CONTACT_LIST_NOTIFY_NUMBER_ENTERED_MSG x
ATTR_EXPORT_CONTROL_APP_UI x
ATTR_FILE_OPEN_CREATE_TRIGGER_MONIKER x
ATTR_FILE_OPEN_DEFAULT_FILE_NAME_PREFIX x
ATTR_GEN_APPLICATION_PRINT_CONTROL x
ATTR_GEN_BOOLEAN_GROUP_INIT_FILE_BOOLEAN x
ATTR_GEN_BOOLEAN_GROUP_LINK x
ATTR_GEN_BOOLEAN_GROUP_STATUS_MSG x
ATTR_GEN_CONTROL_APP_TOOLBOX_UI x x x
ATTR_GEN_CONTROL_APP_UI x x
ATTR_GEN_CONTROL_DO_NOT_USE_LIBRARY_NAME_FOR_HELP x
ATTR_GEN_CONTROL_PROHIBIT_TOOLBOX_UI x
ATTR_GEN_CONTROL_PROHIBIT_UI x x
ATTR_GEN_CONTROL_REQUIRE_TOOLBOX_UI x
ATTR_GEN_CONTROL_REQUIRE_UI x x
ATTR_GEN_CUSTOM_WINDOW x
ATTR_GEN_DEFAULT_MONIKER x
ATTR_GEN_DESTINATION_CLASS x
ATTR_GEN_DISPLAY_... x
ATTR_GEN_DISPLAY_GROUP_NO_FULL_SIZED x
ATTR_GEN_DISPLAY_GROUP_NO_OVERLAPPING x
ATTR_GEN_FEATURE_LINK x
ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH x x
ATTR_GEN_FILE_SELECTOR_FILE_ATTR x x
ATTR_GEN_FILE_SELECTOR_GEODE_ATTR x x
ATTR_GEN_FILE_SELECTOR_NAME_MASK x x
ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH x x
ATTR_GEN_FILE_SELECTOR_VIRTUAL_ROOT x
ATTR_GEN_FOCUS_HELP x
ATTR_GEN_FOCUS_HELP_LIB x
ATTR_GEN_HELP_CONTEXT x
ATTR_GEN_HELP_FILE x
ATTR_GEN_HELP_FILE_FROM_INIT_FILE x
ATTR_GEN_HELP_TYPE x
ATTR_GEN_INIT_FILE_CATEGORY x x
ATTR_GEN_INIT_FILE_KEY x x
ATTR_GEN_INIT_FILE_PROPAGATE_TO_CHILDREN x x
ATTR_GEN_INTERACTION_ABIDE_BY_INPUT_RESTRICTIONS x
ATTR_GEN_INTERACTION_GROUP_TYPE x
ATTR_GEN_INTERACTION_OVERRIDE_INPUT_RESTRICTIONS x
ATTR_GEN_INTERACTION_POPOUT_NOT_CLOSABLE x
ATTR_GEN_INTERACTION_POPPED_OUT x
ATTR_GEN_ITEM_GROUP_CUSTOM_DOUBLE_PRESS x
ATTR_GEN_ITEM_GROUP_INIT_FILE_BOOLEAN x
ATTR_GEN_ITEM_GROUP_LINK x
ATTR_GEN_ITEM_GROUP_SET_MODIFIED_ON_REDUNDANT_SELECTION x
ATTR_GEN_ITEM_GROUP_STATUS_MSG x
ATTR_GEN_NOT_PROPERTY x
ATTR_GEN_OUTPUT_TRAVEL_START x
ATTR_GEN_PATH_DATA x x
with PrefTocListClass x
ATTR_GEN_POSITION x x
ATTR_GEN_POSITION_X x x
ATTR_GEN_POSITION_Y x x
ATTR_GEN_PROPERTY x
ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_ENABLED x x
ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED x x
ATTR_GEN_TEXT_ALLOW_COLUMN_BREAKS x
ATTR_GEN_TEXT_ALLOW_SPACES x
ATTR_GEN_TEXT_ALPHA x
ATTR_GEN_TEXT_ALPHA_NUMERIC x
ATTR_GEN_TEXT_CHAR_ATTR x
ATTR_GEN_TEXT_CURSOR_NO_FOCUS x
ATTR_GEN_TEXT_CUSTOM_FILTER x
ATTR_GEN_TEXT_DASHED_ALPHA_NUMERIC x
ATTR_GEN_TEXT_DATE x
ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR x
ATTR_GEN_TEXT_DEFAULT_PARA_ATTR x
ATTR_GEN_TEXT_DO_NOT_INTERACT_WITH_SEARCH_CONTROL x
ATTR_GEN_TEXT_DOES_NOT_ACCEPT_INK x
ATTR_GEN_TEXT_DOS_CHARACTER_SET x
ATTR_GEN_TEXT_EXTENDED_FILTER x
ATTR_GEN_TEXT_FLOAT_DECIMAL x
ATTR_GEN_TEXT_GRAPHIC_RUNS x
ATTR_GEN_TEXT_LEGAL_DOS_PATH x
ATTR_GEN_TEXT_LEGAL_DOS_VOLUME_NAMES x
ATTR_GEN_TEXT_LEGAL_FILENAMES x
ATTR_GEN_TEXT_MAKE_UPPERCASE x
ATTR_GEN_TEXT_MULTIPLE_CHAR_ATTR_RUNS x
ATTR_GEN_TEXT_MULTIPLE_PARA_ATTR_RUNS x
ATTR_GEN_TEXT_NEVER_MAKE_SCROLLABLE x
ATTR_GEN_TEXT_NO_SPACES x
ATTR_GEN_TEXT_NORMAL_ASCII x
ATTR_GEN_TEXT_NUMERIC x
ATTR_GEN_TEXT_PARA_ATTR x
ATTR_GEN_TEXT_REGION_ARRAY x
ATTR_GEN_TEXT_RUNS_ITEM_GROUP x
ATTR_GEN_TEXT_SELECTABLE x
ATTR_GEN_TEXT_SET_MODIFIED_ON_REDUNDANT_SELECTION x
ATTR_GEN_TEXT_SET_OBJECT_ENABLED_WHEN_TEXT_EXISTS x
ATTR_GEN_TEXT_SIGNED_DECIMAL x
ATTR_GEN_TEXT_SIGNED_NUMERIC x
ATTR_GEN_TEXT_STATUS_MSG x
ATTR_GEN_TEXT_STYLE_ARRAY x
ATTR_GEN_TEXT_TIME x
ATTR_GEN_TEXT_TYPE_RUNS x
ATTR_GEN_TEXT_UPPERCASE_ALPHA x
ATTR_GEN_TEXT_UPPERCASE_ALPHA_NUMERIC x
ATTR_GEN_TRIGGER_ACTION_DATA x
ATTR_GEN_TRIGGER_ACTION_OPTR_AND_WORD x
ATTR_GEN_TRIGGER_ACTION_THREE_WORDS x
ATTR_GEN_TRIGGER_ACTION_TWO_WORDS x
ATTR_GEN_TRIGGER_CUSTOM_DOUBLE_PRESS x
ATTR_GEN_TRIGGER_IMMEDIATE_ACTION x
ATTR_GEN_TRIGGER_INTERACTION_COMMAND x
ATTR_GEN_USES_HIERARCHICAL_INIT_FILE_CATEGORY x
ATTR_GEN_VALUE_... x
ATTR_GEN_VIEW_INK_DESTINATION_INFO x
ATTR_GEN_VIEW_PAGE_SIZE x
ATTR_GEN_VIEW_SCALE_TO_FIT_BASED_ON_X x
ATTR_GEN_VIEW_SCALE_TO_FIT_BOTH_DIMENSIONS x
ATTR_GEN_VISIBILITY_DATA x x
ATTR_GEN_VISIBILITY_DESTINATION x x
ATTR_GEN_VISIBILITY_MESSAGE x x
ATTR_GEN_WINDOW_ACCEPT_INK_EVEN_IF_NOT_FOCUSED x
ATTR_GEN_WINDOW_CUSTOM_LAYER_ID x
ATTR_GEN_WINDOW_CUSTOM_LAYER_PRIORITY x
ATTR_GEN_WINDOW_CUSTOM_PARENT x
ATTR_GEN_WINDOW_CUSTOM_WINDOW_PRIORITY x
ATTR_GEN_WINDOW_KBD_OVERRIDE x
ATTR_GEN_WINDOW_KBD_POSITION x
ATTR_HELP_CUSTOM_POINTER_IMAGE x
ATTR_HELP_INITIAL_HELP x x
ATTR_HELP_INITIAL_HELP_FILE x
ATTR_HELP_SUPPRESS_INITIATE x
ATTR_IMPORT_CONTROL_APP_UI x
ATTR_PAGE_SIZE_CONTROL_UI_CHANGES x
ATTR_PREF_... x
ATTR_PREF_ITEM_GROUP_... x
ATTR_PREF_TEXT_INIT_FILE_CATEGORY_TARGET x
ATTR_PREF_TOC_LIST_... x
ATTR_PREF_TRIGGER_ACTION x
ATTR_PREF_VALUE_... x
ATTR_PRINT_CONTROL_APP_UI x
ATTR_RECENT_CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG x x
ATTR_VIS_TEXT_ADD_SPACE_TO_ALL_LINES x
ATTR_VIS_TEXT_DO_NOT_INTERACT_WITH_SEARCH_CONTROL x
ATTR_VIS_TEXT_DOES_NOT_ACCEPT_INK x
ATTR_VIS_TEXT_DONT_BEEP_ON_INSERTION_ERROR x
ATTR_VIS_TEXT_DONT_SHOW_POSITION_ON_GAINED_TARGET_EXCL x
ATTR_VIS_TEXT_EXTENDED_FILTERS x
ATTR_VIS_TEXT_GRAPHIC_RUNS x
ATTR_VIS_TEXT_MINIMUM_SINGLE_LINE_HEIGHT x
ATTR_VIS_TEXT_REGION_ARRAY x
ATTR_VIS_TEXT_SELECTED_TAB x
ATTR_VIS_TEXT_SEND_CONTEXT_NOTIFICATIONS_EVEN_IF_NOT_FOCUSED x
ATTR_VIS_TEXT_SHOW_POSITION_ON_GAINED_TARGET_EXCL x
ATTR_VIS_TEXT_STYLE_ARRAY x
ATTR_VIS_TEXT_SUSPEND_DATA x
ATTR_VIS_TEXT_TYPE_RUNS x
ATTR_VIS_TEXT_UPDATE_VIA_PROCESS x
AUAF_... x x x
AUAS_... x x x
autoload Swat command x
command reference x
Automating Swat x
AVG parse library internal function x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: B

Ctrl-b Swat navigation x
B_... x
\ backslash substitution
in Tcl x
backtrace Swat command x
command reference x
Backtraces x
BBF_... x
BBFixed x x
BBFixedAsWord x x
bc Tcl command x
BC_DEFAULT_FEATURES x
BC_DEFAULT_TOOLBOX_FEATURES x
BCC_DEFAULT_FEATURES x
BCC_DEFAULT_TOOLBOX_FEATURES x
BCCToolboxFeatures x
BCFeatures x
BCToolboxFeatures x
Be zier curves x
.BEGIN makefile special target x
Bezier curves
GrDrawCurve() x
GrDrawSpline() x
BI_... x x
Binary search x
bind-key Swat command x
bindings Swat command x
Bitmap x
BitmapMode x x
Bitmaps x x
notification lists x
examining with Swat x x x
mouse pointer image x
VisTextGraphicCompressGraphic() x
BlockFromTransferBlockID() macro x x
BlockIDFromFileAndBlock() macro x x
BLTM_... x x
BLTMode x x x
BM_... x
BMC_... x
BMC_PACKBITS
uncompressing x
BMCompact x
BMD_... x x
BMDestroy x x x
BMF_... x
BMFormat x
BMT_... x
BMT_MASK
bitmaps with masks x
BMType x
Boolean x x
Boolean Group Links x
BorderColorControlClass x
BorderControlClass x
bottomMargin (MarginDimensions structure field) x
Bounds, visible
retrieving x
break Tcl command x
break-taken Swat command x
Breakpoints x x
conditional x x x
delayed x
deleting x
detecting x
disabling x
listing x
on handles x
setting x
setting interactively x
setting with mouse x
single use x
stop in Swat command x
tally breakpoints x
timing breakpoints x
brk Tcl structure x
brk delete in Tutorial example x
brk disable in Tutorial example x
brk list in Tutorial example x
command reference x
brkload Swat command x
bsearch() x x
Busy state x
mouse pointer x
Button x
in Tutorial example x
BUTTON_... x
ButtonInfo x x
byte x
byteAsChar Swat variable x
ByteEnum x
ByteFlags x
printing flag fields x
bytes Swat command x
command reference x
Tcl source x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: C

Ctrl-c
in Swat x x
.c files x
c-api
as geode type in .gp file x
C_... (Chars type) x
C_... (Color type) x x
cache Tcl structure x
Calendar API x
CALENDAR_ACCESS_EVENT_COMMON_CALLBACK_MSG x
CALENDAR_ADD_EVENT_CALLBACK_MSG x
CALENDAR_GET_EVENT_CALLBACK_MSG x
CalendarAlarmStruct x
CalendarEventDescType x
CalendarEventError x
CalendarEventParamStruct x
CalendarEventRepeatDuration x
CalendarEventRepeatDurationData x
CalendarEventRepeatInfo x
CalendarEventRepeatInterval x
CalendarEventRepeatIntervalType x
CalendarEventRepeatWeekday x
CalendarReturnedEventStruct x
@call x x x
call Swat command x
call-patient Swat command x
callback routines
CCB() macro x
CALLBACK_... x x
CallbackType x
calloc() x x
@callsuper x x
canDiscardIfDesperate message flag x x
car Swat command x
Card Library
notification list x
case Tcl command x
catch Tcl command x
CB_... x
CBGCC_DEFAULT_FEATURES x
CBitmap x
cbrk Swat command x
cbrk Tcl structure
command reference x
CCB() macro x
CCF_... x x
CCO_... x x
CCOM makefile variable x
CCOMFLAGS makefile variable x
CDBF_... x x
cdr Swat command x
cdrom library x
CDT_... x x
cell library x
CellDeref() x x
CellDirty() x x
CellFunctionParameterFlags x
CellFunctionParameters x x
CellFunctionsParameterFlags x
CellGetDBItem() x x
CellGetExtent() x x
CellLock() x x
CellLockGetRef() x x
CellRange x
CellReference x x
CellReplace() x x
CellRowColumn x
Cells
examining with Swat x
printing cell data with Swat x
printing cell dependencies with Swat x
printing ColumnArrayElement structures in Swat x
printing dependency lists with Swat x
printing paramaters in Swat x
printing row blocks in Swat x
printing spreadheets in Swat x
CellUnlock() x
CF_... (CharFlags) x x
CF_... (ColorFlag type) x x
CFatalError() x x
CFF_... x x
.CFG file (SWAT.CFG file) x
CFGCC_DEFAULT_FEATURES x
CFLAGS x
CFP_... x
CFPF_... x
CFPF_DIRTY x
CHAR parse library internal function x
CHAR_ATTR_FONT_SIZE() macro x
CHAR_ATTR_FONT_SIZE_STYLE() macro x
CHAR_ATTR_STYLE_FONT_SIZE_STYLE_COLOR() macro x
Character runs x
CharacterSet x
CharBGColorControlClass x
CharFGColorControlClass x
CharFlags x x
Chars x x
Charts
notification lists x
checkDuplicate message flag x x
checkLastOnly message flag x x
CHOOSE parse library internal function x
@chunk x x
Chunk arrays x x
checking validity x x
examining with Swat x
Chunk flags x x
Chunk handles x x
CHUNK_ARRAY_ENUM_INSERT_OR_DELETE_RUN_BY_MULTIPLE_THREADS x
@chunkArray x x
ChunkArrayAppend() x x
ChunkArrayAppendHandles() x x
ChunkArrayCreate() x x
ChunkArrayCreateAt() x x
ChunkArrayCreateAtHandles() x x
ChunkArrayDelete() x x
ChunkArrayDeleteHandle() x x
ChunkArrayDeleteRange() x x
ChunkArrayDeleteRangeHandles() x
ChunkArrayElementResize() x x
ChunkArrayElementResizeHandles() x x
ChunkArrayElementToPtr() x x
ChunkArrayElementToPtrHandles() x x
ChunkArrayEnum() x x
ChunkArrayEnumHandles() x x
ChunkArrayEnumRange() x x
ChunkArrayEnumRangeHandles() x
ChunkArrayGetCount() x x
ChunkArrayGetCountHandles() x x
ChunkArrayGetElement() x x
ChunkArrayGetElementHandles() x x
ChunkArrayHeader x x
with ChunkArrayAppend() x
with ChunkArrayCreate() x
ChunkArrayInsertAt() x x
ChunkArrayInsertAtHandle() x x
ChunkArrayPtrToElement() x x
ChunkArrayPtrToElementHandle() x x
ChunkArraySort() x x
ChunkArraySortHandles() x x
ChunkArrayZero() x x
ChunkArrayZeroHandles() x x
ChunkHandle x x
ChunkMapList x
Chunks x
@chunk keyword x
checking x x
declaring with @chunk x
declaring with @chunkArray x
declaring with @elementArray x
declaring with @object x
declaring with @visMoniker x
instance chunk structure x
relation to db item blocks x
CIF_... x x
CIF_QUICK x x
CIFI_... x
CIH_... x x
@class
in Tutorial example x
@class x x
in Tutorial example x
class .gp file keyword x
in Tutorial example x x
Class_... x x
@classdecl
in Tutorial example x
@classdecl x x
in Tutorial example x
Classed events
MetaClass dispatchers x
sending x
with generic objects x
with input heirarchies x
with visual objects x
Classes x
@class keyword x
@classdecl keyword x
checking class pointers x
class messages x
debugging in Tutorial example x
declaring in Tutorial example x
defining in Tutorial example x
exporting x
naming conventions x
classes Swat command x
in Tutorial example x
CLASSF_... x x
ClassFlags x
ClassStruct x x
CLEAN parse library internal function x
Clipboard x
MetaUIMessages x
MSG_META_CLIPBOARD_... x
print-clipboard-item Swat command x
printing transfer item with Swat x
undo chains x x
CLIPBOARD_ITEM_NAME_LENGTH x
CLIPBOARD_MAX_FORMATS x
ClipboardAbortQuickTransfer() x x
ClipboardAddToNotificationList() x x
ClipboardClearQuickTransferNotification() x x
ClipboardDoneWithItem() x x
ClipboardEndQuickTransfer() x x
ClipboardEnumItemFormats() x x
ClipboardGetClipboardFile() x x
ClipboardGetItemInfo() x x
ClipboardGetNormalItemInfo() x x
ClipboardGetQuickItemInfo() x x
ClipboardGetQuickTransferStatus() x x
ClipboardGetUndoItemInfo() x x
ClipboardItemFlags x
ClipboardItemFormat x x
ClipboardItemFormatID x x
TypeFromFormatID() macro x
ClipboardItemFormatInfo x x
ClipboardItemHeader x x
ClipboardItemNameBuffer x x
ClipboardQueryArgs x x x
ClipboardQueryItem() x x
ClipboardQuickNotifyFlags x
ClipboardQuickTransferFeedback x x
ClipboardQuickTransferFlags x x
ClipboardQuickTransferRegionInfo x x
ClipboardRegisterItem() x x
ClipboardRemoveFromNotificationList() x x
ClipboardRemoveItemFromNotificationList() x
ClipboardRequestArgs x x x
ClipboardRequestItemFormat() x x
ClipboardSetQuickTransferFeedback() x x
ClipboardStartQuickTransfer() x x
ClipboardTestItemFormat() x x
ClipboardUnregisterItem() x x
Clipping x x
finding visible part of view x
Close button
in Tutorial example x
clrcc Swat command x
CMI_topText
in Tutorial example x
CMM_... x
cmps
Compared to LocalCmpStrings() x
CMT_... (MapColorToMono types) x
CMT_CLOSE
in Tutorial example x
CMYKT_... x
CMYKTransfer x
code
conditional x
resource type in .gp file x
CODE parse library internal function x
CODE_PAGE_... x
Color x x x
display scheme of specific ui x
GenView background x
highlight color of tool group x
ink input x
ink input in VisInk x
view background color x
color selector
instance data x
messages x
ColorFlag x
ColorMapMode x
ColorQuad x x
with WinColorFlags x
ColorQuadAsDWord x x
ColorSelectorClass x x
ColorTransfer x
ColorTransferData x
ColorTransferType x
COLS parse library internal function x
columns Swat command x
Command completion in Swat x
abbreviations x
completion Swat command x
Command correction in Swat x
Command-line Variables x
COMMAND.COM x
Comments
In makefiles x
in Tcl code x
COMMON
load type in .gp file x
CommonParameters x
compcc Swat command x
CompChildFlags x x x
Compiling
in Tutorial example x
completion Swat command x
ComplexMoniker
in Tutorial example x
ComplexMonikerClass
in Tutorial example x
in Tutorial exercise x
overview x
@composite x x
Composite objects x
CompSizeHintArgs x x
concat Tcl command x
condenseSmall Swat variable x
condenseSpecial Swat variable x
Conditional statements in makefiles x
Config library x
conforming
resource type in .gp file x
Constants
Floating-Point;Constants:Operands;Operands:Floating-Point Routines x
Floating-Point;Constants:Routines;Routines:Floating-Point Routines x
in Tutorial example x
naming conventions x
ConstructOptr() macro x x
cont Swat command x
command reference x
CONTACT_LIST_ALTERNATE_NOTIFY_CONTACT_SELECTED_MSG x
CONTACT_LIST_ALTERNATE_NOTIFY_MULTIPLE_CONTACTS_SELECTED_MSG x
CONTACT_LIST_ALTERNATE_NOTIFY_NUMBER_ENTERED_MSG x
ContactCompareNumbers() x
ContactCreateRecordFromTemplate() x
ContactEnsureEmptyField() x
ContactEnsureField() x
description x
ContactExportDBToTextFile() x
ContactGetDBHandle() x
ContactGetDBHandleNoBlock() x
ContactGetName() x
ContactGetTruncatedName() x
ContactGetUserDataID() x
description x
ContactGetUserDataName()
description x
ContactImportDBFromTextFile() x
ContactMatchName() x
description x
ContactMatchNumber() x
ContactRecreateFieldLabel() x
ContactReleaseDBHandle() x
CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG x
ContactSaveRecord() x
ContactSetTemplateRecord() x
ContDB library x
ContdbTranslateSubFieldLabelToEnglish() x
Content
finding with Swat x x
GVI_content x
content Swat command x
continue Tcl command x
continue-patient Swat command x
ContLog library x
control (keyboard accelerator modifier) x
Control-]
Swat command completion x
Control-b Swat navigation x
Control-c
in Swat x
Control-d
Swat command completion x
Swat navigation x
Control-e Swat navigation x
Control-f Swat navigation x
Control-u Swat navigation x
Control-y Swat navigation x
controllers
adding and removing tools x
additional UI gadgetry x
custom controllers x
feature and tool sets x
feature record x
interactability x
message handling x
resource blocks x
tool feature record x
with GCN lists x
Coordinates x
parameterized x
Core block x
COS parse library internal function x
COSH parse library internal function x
CountryType x
CP_... x
CPP makefile variable x
CQ_... x
CQA_... x
CQNF_... x
CQTF_... x x
CQTF_CLEAR x
CQTRI_... x
CR_... (CellRange structure) x
CR_... (CellReference structure) x
CRA_... x
CRangeEnumParams x
CRC_... x
CreateVisMonikerLine() x
CREP_... x
CS_... x
CSFeatures x
CSI_... x
CT_... (CallbackType type) x
CT_... (CountryType type) x
CT_data[] x
CTD_... x
CTERM parse library internal function x
ctrl (keyboard accelerator modifier) x
Ctrl-]
command completion in Swat x
Ctrl-b Swat navigation x
Ctrl-c
in Swat x x
Ctrl-d
command completion in Swat x
Swat navigation x
Ctrl-e Swat navigation x
Ctrl-f Swat navigation x
Ctrl-u Swat navigation x
Ctrl-y Swat navigation x
CTT_... x
cup Swat command x
in Tutorial example x
Currency
formatting x
CURRENCY_SYMBOL_LENGTH x
currencyDigits (LocalCurrencyFormat structure format) x
CurrencyFormatFlags x x
currencyFormatFlags (LocalCurrencyFormat structure field) x
Current position (graphics) x
GrGetCurPos() x
GrMoveTo() x
current-level Swat command x
current-registers Swat command x
CustomDialogBoxFlags x x
CustomDialogType x x
customMsgHandle (TransErrorInfo structure field) x
cvtrecord Swat command x
CWarningNotice() x
cycles Swat command x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: D

Ctrl-d
command completion in Swat x
Swat navigation x
DA_... x x
DACPF_CATENATE x
DACPlayFlags x
DACRB_... x
DACReferenceByte x
DACSampleFormat x
DACSF_... x x
DAR_... x
DashPairArray x x x
data
resource type in .gp file x
data (@start flag) x
DataStoreAddField() x
DataStoreBuildIndex() x
DataStoreChangeNotification x
DataStoreChangeType x
DataStoreClose() x
DataStoreCreate() x
DataStoreDate x
DataStoreDelete() x
DataStoreDeleteField() x
DataStoreDeleteRecord() x
DataStoreDeleteRecordNum() x
DataStoreDiscardRecord() x
DataStoreFieldEnum() x
DataStoreFieldIDToName() x
DataStoreFieldNameToID() x
DataStoreFlags x
DataStoreGetExtraData() x
DataStoreGetField() x
DataStoreGetFieldChunk() x
DataStoreGetFieldCount() x
DataStoreGetFieldInfo() x
DataStoreGetFieldPtr() x
DataStoreGetFieldSize() x
DataStoreGetFlags() x
DataStoreGetNextRecordID() x
DataStoreGetNumFields() x
DataStoreGetOwner() x
DataStoreGetRecordCount() x
DataStoreGetRecordID() x
DataStoreGetTimeStamp() x
DataStoreGetVersion() x
DataStoreIndexCallbackParams x
DataStoreLoadRecord() x
DataStoreLoadRecordNum() x
DataStoreLockRecord() x
DataStoreMapRecordNumToID() x
DataStoreNewRecord() x
DataStoreOpen() x
DataStoreOpenFlags x
DataStoreRecordEnum() x
DataStoreRecordEnumFlags x
DataStoreRemoveFieldFromRecord() x
DataStoreRename() x
DataStoreRenameField() x
DataStoreSaveRecord() x
DataStoreSetExtraData() x
DataStoreSetField() x
DataStoreSetNextRecordID() x
DataStoreSetRecordID() x
DataStoreSetTimeStamp() x
DataStoreSetVersion() x
DataStoreStringSearch() x
DataStoreTime x
DataStoreUnlockRecord() x
DatatStoreStructureError x
Date
formatting x
Math Conversions x
DATE_TIME_BUFFER_SIZE x
DATE_TIME_FORMAT_SIZE x
DateTimeFormat x x
DayOfTheWeek x
DB groups x
DB items x
db library x
DBAlloc() x x
DBAllocUngrouped() x x
DBase
examining blocks with Swat x
examining items with Swat x
printing group blocks in Swat x
DBCombineGroupAndItem() macro x x
DBCopyDBItem() x x
DBCopyDBItemUngrouped() x x
DBDeleteAt() x x
DBDeleteAtUngrouped() x x
DBDeref() x x
DBDirty() x x
DBFree() x x
DBFreeUngrouped() x x
DBGetMap() x x
DBGroup x
DBGroupAlloc() x x
DBGroupAndItem x x
DBGroupFree() x x
DBGroupFromGroupAndItem() macro x x
DBInsertAt() x x
DBInsertAtUngrouped() x x
DBItem x
DBItemFromGroupAndItem() macro x x
DBLock() x x
DBLockGetRef() x x
DBLockGetRefUngrouped() x x x
DBLockMap() x x
DBLockUngrouped() x x
DBR_... x
DBReAlloc() x x
DBReAllocUngrouped() x x
DBReturn x x
dbrk Swat command x
DBSetMap() x x
DBSetMapUngrouped() x x
DBUnlock() x x
DC_... x x
DCA_class x
dcache Swat command x
dcall Swat command x
DCC_DEFAULT_FEATURES x
DCC_DEFAULT_TOOLBOX_FEATURES x
DCCF_DROP_CAP x
DCCFeatures x
DCCToolboxFeatures x
DDB parse library internal function x
Deadlock x x
avoidance x
Deadlock avoidance x
debug Swat command x
debugger Swat command x
Debugging x
in Tutorial example x
multithreaded applications x
with error checking code x
debugOnError Swat variable x
decimalDigits (LocalNumericFormat structure field) x
decimalSeperator (LocalCurrencyFormat structure field) x
decimalSeperator (LocalNumericFormat structure field) x
.def files x
DEF_... x
@default x x
.DEFAULT makefile special target x
default
line in SWAT.CFG x
DEFAULT_FONT_ID x
DEFAULT_FONT_SIZE x
DefaultPrintSizes x
DefaultTabControlClass x
defcmd Swat command x
defcommand Swat command x
in example x
defhelp Swat command x
@define x x
defined() conditional x
@deflib x x
defsubr Tcl command x
defvar Swat command x
DEIS_... x
DEIT_... x
delassoc Swat command x
Delayed breakpoints x
Delayed Mode x
and activation messages x
Delta... () macros x
Depend target x
Dependency lines (makefiles) x
DES_... x x
destination
in Tutorial example x
Destination class x
DestinationClassArgs x
DESTROY_ON_EOS (EndOfSongFlags flag) x
detach Swat command x
command reference x
DevicePresent x
DFF_... x x
DFIS_... x
DFR_... x x
dgroup
GeodeLoadDGroup() x
.dh files x
Dialog boxes x
UserCreateDialog() x
UserDoDialog() x
UserStandardDialog() x
Directories (file system) x
creation x
deletion x
Directory stack x
DirPathInfo x
dirs Swat command x
DIS_... (DiskInfoStruct structure) x
DIS_... (DriverInfoStruct structure) x x
DISCARD x
discard-only
resource type in .gp file x
discard-state Swat command x
discardable
resource type in .gp file x
Discardable memory x
geode resources x x
discardOnSave (class flag) x
DiskCheckInUse() x x
DiskCheckUnnamed() x x
DiskCheckWritable() x x
DiskCopy() x x
DiskCopyCallback x x x
DiskCopyError x x x
DiskFind() x x
DiskFindResult x x x
DiskForEach() x x
DiskFormat() x x
DiskFormatFlags x x
DiskGetDrive() x x
DiskGetVolumeFreeSpace() x x
DiskGetVolumeInfo() x x
DiskGetVolumeName() x x
DiskHandle x x
DiskInfoStruct x x
DiskRegisterDisk() x x
DiskRegisterDiskSilently() x x
DiskRestore() x x
DiskRestoreError x x x
Disks
handles x
DiskSave() x x
DiskSetVolumeName() x x
diskwalk Swat command x
@dispatch x x
@dispatchcall x x
Display scheme
accessing x
display Swat command x
Display windows in Swat x
clearing x
columns Swat command x
creating x
destroying x
displaying current Stack frame x
displaying registers x
displaying source code x
displaying variable x
flags register x
redisplaying x
wpop Swat command x
wpush Swat command x
DisplayAspectRatio x
DisplayClass x x
DisplaySize x
DisplayType x x x x
Distances
formatting x
DistanceUnit x x
Dithering
clustered vs. dispersed x
DMF_... x
DocQuitStatus x x
document... x
Documents
large model x
layers in x
management x
visible objects in;Objects:non-discardable x
DocumentSize x
.doh files x
Dollar sign
in Tcl x
DOMAIN_SERVER x
.DONTCARE makefile attribute x
DOS_DOT_DOS_FILE_NAME_SIZE x
DOS_DOT_FILE_NAME_LENGTH_ZT x
DOS_NO_DOT_DOS_FILE_NAME_SIZE x
DosCodePage x x
DosDotFileName x
DosExec() x x
DosExecFlags x
DosFileInfoStruct x
dosMem Swat command x
DosNoDotFileName x
DOTW_... x
down Swat command x
DP_... x
DPI_... x x
DQS_... x x
DR_SERIAL_GET_FORMAT x
DR_SERIAL_SET_FORMAT x
DR_STREAM_CLOSE
with serial port x
DR_STREAM_CREATE x
DR_STREAM_OPEN
with serial port x x
DrawFlags x
Drawing
in 16 bits x
in Tutorial example x
DrawMask x
DrawMonikerFlags x x
DRE_... x x
Drive number x
DRIVE_... x x
DriveGetDefaultMedia() x x
DriveGetExtStatus() x x
DriveGetName() x x
DriveGetStatus() x x
driver
as geode type in .gp file x
driver .gp file keyword x
DRIVER_TYPE_... x x
DriverAttrs x x
DriverExtendedInfoStruct x
DriverExtendedInfoTable x
DriverInfoStruct x x
Drivers x
finding number of x
geode attribute x
geode type x
handle checking x
standard paths x
type in .gp files x
DriverType x
DriveStatus x x
DriveTestMediaSupport() x x
DriveType x x
drivewalk Swat command x
DropCapControlClass x
DS_... x
DS_... (DisplaySize type) x
DS_... (DriveStatus) x x
DSFT_... x
DT_... x x x
DTC_DEFAULT_FEATURES x
DTC_DEFAULT_TOOLBOX_FEATURES x
DTCFeatures x
DTCToolboxFeatures x
DU_... x x
Dumping Swat output x
dumpstack Swat command x
Duplicating objects x
DWFixed x x
DWFixedReturn x
DWFR_... x
dword x
DWORD_CHILD_SPACING macro x
DWORD_WRAP_SPACING macro x
DWordFlags x
dwordIsPtr Swat variable x
dwords Swat command x
command reference x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: E

Ctrl-e Swat navigation x
:E makefile variable expansion x
EAH_... x
.ebj files x
EC files x
ec Swat command x
EC() macro x x
ec.geo files x
ec.sym files x
EC_BOUNDS() macro x x
EC_ERROR() macro x x
EC_ERROR_IF() macro x x
EC_WARNING() macro x
EC_WARNING_IF() macro x
ECA_... x
ECCheckBounds() x x
ECCheckChunkArray() x
ECCheckChunkArrayHandles() x
ECCheckClass() x
ECCheckDriverHandle() x
ECCheckEventHandle() x
ECCheckFileHandle() x
ECCheckGeodeHandle() x
ECCheckGStateHandle() x
ECCheckHandleNS() x
ECCheckHugeArray() x x
ECCheckLibraryHandle() x
ECCheckLMemChunk() x
ECCheckLMemHandle() x
ECCheckLMemHandleNS() x
ECCheckLMemObject() x
ECCheckLMemObjectHandles() x
ECCheckLMemOD() x
ECCheckLMemODHandles() x
ECCheckMemHandle() x
ECCheckObject() x
ECCheckObjectHandles() x
ECCheckOD() x
ECCheckODHandles() x
ECCheckProcessHandle() x
ECCheckQueueHandle() x
ECCheckResourceHandle() x
ECCheckStack() x
ECCheckThreadHandles() x
ECCheckWindowHandle() x
ECF_... x
ECHO x
echo Swat command x
in example x x
ECI_attrs x
ECI_dataClasses x
ECI_destination x
ECI_message x
ECLMemExists() x
ECLMemExistsHandles() x
ECLMemValidateHandle() x
ECLMemValidateHandleHandles() x
ECLMemValidateHeap() x
ECMemVerifyHeap() x
ECVMCheckMemHandle() x
ECVMCheckVMBlockHandle() x
ECVMCheckVMFile() x
EED_... x
EF_... (EvalFlags) x
EF_... (ExitFlags) x
EFD_... x
Element arrays x
@elementArray keyword x
@elementArray x x
ElementArrayAddElement() x x
ElementArrayAddElementHandles() x x
ElementArrayAddReference() x x
ElementArrayAddReferenceHandles() x x
ElementArrayCreate() x x
ElementArrayCreateAt() x x
ElementArrayCreateAtHandles() x x
ElementArrayDelete() x x
ElementArrayDeleteHandles() x x
ElementArrayElementChanged() x x
ElementArrayElementChangedHandles() x x
ElementArrayGetUsedCount() x x
ElementArrayGetUsedCountHandles() x x
ElementArrayHeader x x
ElementArrayRemoveReference() x x
ElementArrayRemoveReferenceHandles() x x
ElementArrayTokenToUsedIndex() x x
ElementArrayTokenToUsedIndexHandles() x x
ElementArrayUsedIndexToToken() x x
ElementArrayUsedIndexToTokenHandles() x x
#elif makefile directive x
elist Swat command x
Ellipses x
GrDrawEllipse() macro x
GrFillEllipse() x
else .gp file keyword x
#else makefile directive x
empty() conditional x
EMS x
@end x x
.END makefile special target x
@end
in Tutorial example x
END_... x
@endc
in Tutorial example x
@endc x x
in Tutorial example x
endDouble (LocalQuotes structure field) x
@endif x x
endif .gp file keyword x
#endif makefile directive x
@endlib x x
endSingle (LocalQuotes structure field) x
ensure-swat-attached Swat command x
entry .gp file field x
EntryPointRelocation x
Enumerated types x
EnvelopeOrientation x x
EnvelopePath x x
Environment variables within PMake x
EO_... x
EOD_... x
EOREG_HIGH x
EOREGREC x x
EOSF_... x
EP_... (EnvelopePath type) x
EP_... (EvalParameters structure) x
EPR_... x
eqfind Swat command x
eqlist Swat command x
ERD_... x
erfind Swat command x
ERR parse library internal function x
ERR_... x
ERR_... (DiskCopyError) x
Error checking x
MSG_VIS_VUP_EC_... x
SysGetECLevel() x
error Tcl command x
ERROR_... x x
ErrorCheckingFlags x x
SysGetECLevel() x
SysSetECLevel() x
Errors x
continuing after in Swat x x
continuing after with Swat x
continuing after, in Swat x
decoding FatalErrors values x x
determining cause of crash x x
error checking level x
fatalerr_auto_explain Swat variable x
Swat timeout errors x
Tcl errors x
ESAD_... x
ESAT_... x
Escape key
command completion in Swat x
ESD_... x
ESOD_... x
ESOT_... x
eval Tcl command x
EVAL_MAX_NESTED_LEVELS x
EvalErrorData x
EvalFlags x
EvalFunctionData x
EvalNameData x
EvalOperatorData x
EvalParameters x
EvalRangeData x
EvalStackArgumentData x
EvalStackArgumentType x
EvalStackOperatorData x
EvalStackOperatorType x
EvalStringData x
Evaluator (parse library) x
Event queues x x x
@call and @send flags x
accessing x
from thread handle x
handles, checking x
event Tcl structure x
event-driven x
EventHandle x
Events
dispatching recorded x x
displaying x
displaying with Swat x
finding recorded x
handles, checking x
printing with Swat x
recording x
EXACT parse library internal function x
Exclusive grab
MSG_META_GRAB_FOCUS_EXCL x
notification x
releasing all x
Exclusive lists x
.EXEC makefile attribute x
execOnStartup INI file field
UserAddAutoExec() x
UserRemoveAutoExec() x
exempt .gp file keyword x
exists()conditional x
exit x
exit Swat command
in Tutorial example x
exit-thread Swat command x
ExitFlags x
EXP parse library internal function x
Expanded memory x
explain Swat command x
explode Swat command x
export .gp file keyword x
in Tutorial example x x
EXPORTCF_BASIC x
ExportControlAttrs x
ExportControlClass x x
ExportControlFeatures x
ExportControlToolboxFeatures x
EXPORTCTF_DIALOG_BOX x
Exported messages x
@exportMessages keyword x
@exportMessages x x
expr Tcl command x
in example x
Extended memory x
ExtendedFileSelectorClass
overview x
@extern x x
with methods x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: F

Ctrl-f Swat navigation x
F_... x
FA_... x x
FA_... (FileAccess type) x
FA_... (FileAttrs) x
FA_... (FontAttrs) x
FAC_DEFAULT_FEATURES x
FAC_DEFAULT_TOOLBOX_FEATURES x
FACFeatures x
FACT parse library internal function x
FACToolboxFeatures x
FAF_... x
FAF_PUSH_RESULT x
FAF_STORE_NUMBER x
FALSE x x
parse library internal function x
fatalerr_auto_explain Swat variable x
FatalError() x x
Fax
ui gadgetry x
FC_... (FieldCategory) x
FC_DEFAULT_FEATURES x
FC_DEFAULT_TOOLBOX_FEATURES x
FCF_... x x
FCFeatures x
FCND_... x
FCT_... x
FCToolboxFeatures x
FDAT_... x x
FDATExtract... () macros x x
FE_... (FileExclude) x
FE_... (FloatExponent) x
FEA_... x
FEA_CREATION x
FEA_CREATOR x
FEA_CUSTOM x
FEA_DRIVE_STATUS x
FEA_FILE_ATTR x
FEA_FILE_TYPE x
FEA_FLAGS x
FEA_MODIFICATION x
FEA_MULTIPLE x
FEA_NAME x
FEA_NOTICE x
FEA_PASSWORD x
FEA_PROTOCOL x x
FEA_RELEASE x x
FEA_SIZE x
FEA_TOKEN x
FEA_USER_NOTES x
FEAD_... x x x
FEDosInfo x x
FEF_... x x
FEP_... x x
FES_... x
FESF_... x x
FESRT_... x x
fetch-optr Swat command x
FF_... x
FFA_... (FloatFloatToAsciiData structure) x
FFA_... (FloatFloatToDateTime structure) x
FFA_dateTime x
FFA_dateTimeParams x
FFA_float x
FFA_stackFrame x
FFAF_... x
FFDTT_... x
FFileSelectorClass
overview x
FG_... x
fhandle Swat command x
FID_... x
FID_NAME_LEN x
FIDR_... x
field Swat command x
FieldCategory x
FieldData x
FieldDescriptor x
FieldHeader x
FieldHeaderFixed x
FieldType x
fieldwin Swat command x
File position x
File system x
driver standard path x
File system drivers x
file Tcl command x
File transfer
PCCom library x
FILE_... x
FILE_ACCESS_... x
FILE_ATTR_... x
FILE_CREATE_... constants x
FILE_DENY_... x
FILE_DESKTOP_INFO_SIZE x
FILE_LONGNAME_BUFFER_SIZE x x
FILE_OWNER_NAME_SIZE x
FILE_PASSWORD_SIZE x
FILE_POS_... x x
FILE_RIGHTS_SIZE x
FileAccess x
FileAccessFlags x x
FileAccessRights x
FileAttrs x x x
FileChangeNotificationData x x
FileChangeType x x
FileClose() x x
FileCommit() x x
FileConstructFullPath() x x
FileCopy() x x
FileCopyrightNotice x
FileCreate() x x
FileCreateDir() x x
FileCreateFlags x x
FileCreateTempFile() x x
FileDate x
FileDateAndTime x x
Printing x
FileDelete() x x
FileDeleteDir() x x
FileDesktopInfo x
FileDirID x
FileDuplicateHandle() x x
FileEnum() x x
FileEnumLocateAttr() x x
FileEnumParams x x
FileEnumSearchFlags x x
FileEnumStandardReturnType x x
FileEnumWildcard() x x
FileExclude x
FileExtAttrDesc x x x
FileExtendedAttribute x
FileFromTransferBlockID() macro x x
FileGetAttributes() x x
FileGetCurrentPath() x x
FileGetDateAndTime() x x
FileGetDiskHandle() x x
FileGetHandleExtAttributes() x x
FileGetPathExtAttributes() x x
FileHandle x
FileID x
FileLockRecord() x x
FileLongName x x
FileMove() x x
FileOpen() x x
FileOpenControlClass x
FileOpenCreateNewMoniker x
FileOpenFeatures x
FileOwnerName x
FileParseStandardPath() x x
FilePassword x
FilePopDir() x x
FilePos() x x
FilePosMode x x
FilePushDir() x x
FileRead() x x
FileRename() x x
FileResolveStandardPath() x x
FileResolveStandardPathFlags x x
Files x x
displaying open files x
document control objects x
DOS files x
examining directory stack with Swat x
examining standard paths with Swat x
fhandle Swat command x
Formatting Date Strings x
handles x
handles, checking x
listing current disks with Swat x
listing Drives with Swat x
listing with Swat x x x
Loading Tcl files x
loading Tcl files x x
manipulating host machine files with Swat x
names:application name x
printing current directory in Swat x
printing disk information with Swat x
selector x
token x
FileSelectorAttrs x x
FileSelectorFileCriteria x x
FileSetAttributes() x x
FileSetCurrentPath() x x
FileSetDateAndTime() x x
FileSetHandleExtAttributes() x x
FileSetPathExtAttributes() x x
FileSetStandardPath() x x
FileSize() x
FileTime x
FileTruncate() x
FileUnlockRecord() x x
FileUserNotes x
FileWrite() x x
FilterListClass
overview x
FilterTextClass
overview x
FIND parse library internal function x
find Swat command x
find-opcode Swat command x
FindNoteHeader x
FINGER x
finish Swat command x
finishframe Swat command x
fixed
resource type in .gp file x
Fixed memory x
geode resources x
Fixed point numbers x
Flag records x
Flags
examining flags fields x
Flags register
clearing with Swat x
complementing flags with Swat x
getting flags with Swat x
in display window x
printing x
setting flags with Swat x
flagwin Swat command x
FlashingNoteClass
overview x
Flatfile
notification lists x
FLI_... x
FLOAT_EXPONENT x
FloatAdd() x
FloatAsciiToFloat() x x
FloatAsciiToFloatFlags x
FloatCompAndDrop() x
FloatDepth() x
FloatDup() x
FloatExit() x
FloatExponent x
FloatFloatIEEE64ToAscii_StdFormat() x
FloatFloatToAscii() x
FloatFloatToAscii_StdFormat() x
FloatFloatToAsciiData x
FloatFloatToAsciiFormatFlags x x
FloatFloatToAsciiParams x
FloatFloatToDateTime x
FloatFloatToDateTimeData x x
FloatFormatDelete() x
FloatFormatGetFormatTokenWithName() x
FloatFormatInitFormatList() x
FloatFormatNumber() x
FloatFormatProcessFormatSelected() x
FloatFormatToDateTimeFlags x
FloatFormatUserDefOK() x
Floating-Point Formats
notification list x
Floating-Point Formats;Format
Floating-Point x
FloatNum x x
FloatPopNumber() x
FloatPushNumber() x
FloatRandomN() x
FloatSetStackPointer() x
FloatSwap() x
FloatTimeNumberGetSeconds() x
flowobj Swat command x
flush-output Swat command x
FM_... (FontMaker) x
FM_... (FontMap) x
FMT_... x x
fmtoptr Swat command x
fmtval Swat command x
FNH_... x
FoamCancelIfNoFreeSpace() x
FoamCheckIfOutOfSpace() x
FoamDBAddFieldToRecord() x
FoamDBBinarySearch() x
FoamDBClose() x
FoamDBCreateEmptyRecord() x
FoamDBDeleteFieldFromRecord() x
FoamDBDeleteRecord() x
FoamDBDiscardRecord() x
FoamDBDuplicateRecord() x
FoamDBFieldEnum() x
in Contact DB example x
FoamDBGetCurrentRecordID() x
FoamDBGetFieldData() x
in Contact DB example x
FoamDBGetFieldName() x
in Contact DB example x
FoamDBGetFieldType() x
FoamDBGetNextPrevRecord() x
FoamDBGetNumVisibleRecords() x
FoamDBGetRecordFromID() x
FoamDBGetRecordID() x
FoamDBGetVisibleRecord() x
FoamDBLockedRecordEnum() x
FoamDBMapNameToToken() x
FoamDBMapTokenToName() x
FoamDBOpen() x
FoamDBResortDatabase() x
FoamDBResumeNotifications() x
FoamDBResumeUpdates() x
FoamDBSaveRecord() x
FoamDBSetCurrentRecordID() x
FoamDBSetFieldData() x
FoamDBSetFieldName() x
FoamDBSetNameForToken() x
FoamDBSuspendNotifications() x
FoamDBSuspendUpdates() x
FoamDBVisibleRecordEnum() x
FoamDBVisibleRecordEnumWithRange() x
FoamDisplayDeleteWarning() x
FoamDisplayDeleteWarningNoBlock() x
FoamDisplayError() x
FoamDisplayErrorNoBlock() x
FoamDisplayNote() x
FoamDisplayOkCancelWarning() x
FoamDisplayQuestion() x
FoamDisplayWarning() x
FoamDisplayWarningNoBlock() x
FoamDocCheckProtocol() x
FoamDocClose() x
FoamDocConvertFromDosAppend() x
FoamDocConvertFromDosDone() x
FoamDocConvertFromDosInit() x
FoamDocConvertToDosDone() x
FoamDocCreate() x
FoamDocDelete() x
FoamDocGetDosTextSmall() x
FoamDocGetHeaderData() x
FoamDocGetTextLength() x
FoamDocGetTextLengthFromField() x
FoamDocIsTextFormatted() x
FoamDocOpen() x
FoamDocSetAttrInfo() x
FoamDocSetBlockHandle() x
FoamDocSetHeaderData() x
FoamDocSetTextFormatted() x
FoamDocSetTextLength() x
FoamDocStoreDosTextSmall() x
FoamDocStoreTextAsVMChain() x
FoamFontControlClass
overview x
FoamGenerateUniqueFilename() x
FoamGetDocumentDir() x
FoamGetDocumentDirName() x
FoamGetFileType() x
FoamGetFreeDiskSpace() x
FoamGetHandleFileType() x
FoamGetLexicalOrder() x
FoamGetLowSoundFlag() x
FoamIsTextFormatted() x
FoamJustificationControlClass
overview x
FoamLaunchApplication() x
FoamPointSizeControlClass
overview x
FoamProgressDialogClass
overview x
FoamRoundUpAsMultipleOfClusterSize() x
FoamSetDocumentDir() x
FoamSetLowSoundFlag() x
FoamStandardDialogOptr() x
FoamStandardDialogOptrNoBlock() x
FoamStartSoundSequence() x
FoamStopSoundSequence() x
FoamSubApplicationClass
in Tutorial example x x
overview x
FoamTextStyleControlClass
overview x
FoamUTOffset() x
FoamWarnForSaving() x
FoamWarnIfNotEnoughSpace() x
FoamWarnIfOutOfSpace() x
FoamWarnSpaceAfterOperation() x
Focus x x x
AS_FOCUSABLE x
exclusive x
exclusive notification x
finding with Swat x x
focus Swat command x
focusobj Swat command x
FOF_... x
font
application display scheme x
Font drivers x
FontAttrControlClass x
FontAttrs x
FontControlClass x
FontEnumFlags x x
FontEnumStruct x x
FontFamily x x
FontGroup x
FontID x x
FontIDRecord x
FontMaker x x
FontMap x
Fonts
driver standard path x
examining with Swat x x
finding available x
in Tutorial example x
kernel graphics routines x
metrics x
metrics, GrGetTextBounds() and x
monitoring with Swat x
with impex x
fonts Swat command x
FontWeight x x
FontWeight type x
FontWidth x x
FontWidth type x
FooInterchangeFormat x
for Tcl command x
in example x
forceQueue message flag x x
foreach Tcl command x
format Tcl command x x
in example x
FormatArray x x
FormatEntry x
FormatError x x x
FormatIDFromManufacturerAndType() x
FormatIDFromManufacturerAndType() macro x
FormatIdType x
FormatInfoStruc x
FormatParams x
Formatting Swat output x x x x
formatting numbers x
integer values of expressions x
inverse type x
moving cursor x
fpstack Swat command x
fptr x
fpu-state Swat command x
FractionOf() macro x
frame Swat command x
command reference x
in Tutorial example x
framewin Swat command x
free() x
freeze Swat command x
Friday x
frontDouble (LocalQuotes structure field) x
frontSingle (LocalQuotes structure field) x
FRSPF_... x x
FSA_... x
FSFC_... x
FTI_... x
FTP x
FTP_DATA x
fullscreen x
func Swat command x
Function names
for methods x
FUNCTION_ID_... x
FunctionID x
FV parse library internal function x
fvardata Swat command x
FW_... x
fwalk Swat command x
FWI_... x x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: G

GA_... (GenAttrs) x
GA_... (GeodeAttrs) x x x
GA_TARGETABLE x
GAGCNLT_... x x
GAGCNLT_NOTIFY_HELP_CONTEXT_CHANGE x
GAGCNLT_SELF_LOAD_OPTIONS x
GAGCNLT_STARTUP_LOAD_OPTIONS x
GAGCNLT_WINDOWS
in Tutorial example x x
GAI_appMode x
GAI_appRef x
GAI_attachFlags x
GAI_launchFlags x
GAI_optFlags x
GAI_states x
GBGI_applyMsg x
GBGI_destination x
GBGI_indeterminateBooleans x
GBGI_modifiedBooleans x
GBGI_selectedBooleans x
GBI_identifier x
gc Swat command x
GCBF_... x
GCBI_... x
GCCF_... (GenControlChildFlags) x
GCCI_... x
GCFI_... x
GCI_... (GenContent Instance) x
GCI_... (GenControl Instance) x
GCI_output x
GCM_Info x
GCM_info x
GCMI_... x x
GCN list
in Tutorial example x
GCNDCNT_... x
GCNDriveChangeNotificationType x
GCNEMNT_... x
GCNExpressMenuNotificationType x
GCNExpressMenuNotificationTypes x
GCNLBH_... x
GCNLE_item[] x
GCNLH_... x
gcnList() keyword x x
in Tutorial example x x
GCNListAdd() x x
GCNListAddHandles() x
GCNListAddToBlock() x
GCNListBlockHeader x
GCNListCreateBlock() x
GCNListDestroyBlock() x
GCNListDestroyList() x
GCNListElement x
GCNListHeader x
GCNListOfListsElement x
GCNListOfListsHeader x
GCNListParams x
GCNListRelocateBlock() x
GCNListRemove() x x
GCNListRemoveFromBlock() x
GCNListRemoveHandles() x
GCNListSend() x x
GCNListSendFlags x x
with GCNListSend() x
GCNListSendToBlock() x
GCNListSendToList() x
GCNListSendToListHandles() x
GCNListType x
GCNListTypeFlags x
GCNListUnRelocateBlock() x
GCNLOL_meta x
GCNLOLE_... x
GCNLP_... x
GCNLSF_... x
GCNLT_... x
GCNLTF_... x
GCNSCT_... x
GCNShutdownControlType x x
GCNSLT_... x x
GCNStandardListType x x
GCP_... x
GCSI_... x
GCSR_... x
GDA_... x
GDA_USER_DISMISSABLE x
GDCA_... x
GDCA_MAXIMIZED_NAME_ON_PRIMARY x
GDCF_... (GenDocumentControlFeatures) x
GDCFeatures x
GDCI_attrs x
GDCI_defaultFile x
GDCI_documentGroup x
GDCI_documentToken x
GDCI_enableDisableList x
GDCI_features x
GDCI_newGroup x
GDCI_noNameText x
GDCI_openGroup x
GDCI_saveAsGroup x
GDCI_selectorType x
GDCI_targetDocName x
GDCI_templateDir x
GDCI_useTemplateGroup x
GDCM_... x
GDCT_... x
GDCToolboxFeatures x
GDDT_... x x
GDGA_... x
GDGI_attrs x
GDGI_documentClass x
GDGI_documentControl x
GDGI_genDisplay x
GDGI_genDisplayGroup x
GDGI_genDocument x
GDGI_genView x
GDGI_output x
GDGI_protocol x
GDGI_protocolMinor x
GDGI_untitledNameField x
GDI_attributes x
GDI_attrs x
GDI_diskHandle x
GDI_document x
GDI_fileHandle x
GDI_operation x
GDI_volumeName x
GDLI_numItems x
GDLI_queryMsg x
GDMT_HELP x
GDO_... x
GE_... x
GEN_BOOLEAN_GROUP_APPLY_MSG x
GEN_BOOLEAN_GROUP_STATUS_MSG x
GEN_DYNAMIC_LIST_QUERY_MSG x
GEN_ITEM_GROUP_APPLY_MSG x
GEN_ITEM_GROUP_STATUS_MSG x
GEN_TEXT_APPLY_MSG x
GEN_TEXT_STATUS_MSG x
GEN_TRIGGER_ACTION prototype x
GEN_VALUE_APPLY_MSG x
GEN_VALUE_STATUS_MSG x
GenAppGCNListTypes x
GenApplication x
generic upward queries x
GenApplication object x
GenApplicationClass x x
attaching x
in Tutorial example x
MSG_META_QUIT x
overview x
GenBooleanClass x
messages x
overview x
GenBooleanGroupClass x
overview x
@genChildren (messaging shortcut) x x
GenClass x x
variant behavior x
GenContentClass x
overview x
GenControlBuildFlags x x
GenControlBuildInfo x
example x
GenControlChildFlags x
GenControlChildInfo x x x x
example x
GenControlClass x
overview x
GenControlFeaturesInfo x x x x
example x
GenControlGetFeaturesReturn x
GenControlScanInfo x
GenCopyChunk() x
GenDisplayClass x
overview x
GenDisplayControlAttributes x
GenDisplayControlClass x
notification lists x
overview x
GenDisplayGroupClass x
overview x
GenDocumentClass x x x
overview x
GenDocumentControlClass x x
overview x
GenDocumentControlFeatures x
GenDocumentGroupClass x x
overview x
GenDocumentOperation x
GenDynamicListClass x
overview x
GenEditControlClass x x
General change notification x x
accessing list of lists x
goc syntax x
in Tutorial example x
standard messages x
GeneralEvent x
Generic objects x
accessing parents x
displaying ancestors x
displaying instance data with Swat x
displaying tree x x
Generic upward queries x
GenFilePath x x
GenFileSelectorClass x
overview x
GenFileSelectorFileAttrs x x
GenFileSelectorFilterRoutine() x
GenFileSelectorGeodeAttrs x x
GenFileSelectorGetFilterRoutineResults x
GenFileSelectorMask x
GenFileSelectorSelection x
GenFindObjectInTree() x
GenGlyphClass x
overview x
GenInsertChild() x
GenInteractionAttrs x
GenInteractionClass x
Interaction Commands x
default action x
GA_SIGNAL_INTERACTION_COMPLETE x
overview x
variant behavior x
GenInteractionType x
GenInteractionVisibility x
GenItemClass x
overview x
GenItemGroupBehaviorType x
GenItemGroupClass x
overview x
GenOptionsParams x
GenPageControlClass x x
@genParent (messaging shortcut) x x
GenPrimaryClass x
in Tutorial example x x
overview x
GenProcessAction() x
GenProcessClass x
attaching x
overview x
subclassing in Tutorial example x x
GenProcessGenAttrsAfterAction() x
GenProcessGenAttrsBeforeAction() x
GenProcessUndoCheckIfIgnoring() x
GenProcessUndoGetFile() x
GenRemoveDownwardLink() x
GenSetUpwardLink() x
GenSystemClass
generic upward queries x
GenTextAttrs x
GenTextClass x
overview x
GenTextStateFlags x
GenToolControlClass x x
notification list x
overview x
usage x
GenToolGroupClass x
GenTravelOption x x x
gentree Swat command x
command reference x
in Tutorial example x
GenTriggerClass x
in Tutorial example x x
overview x
triggers in dialog boxes x
variant behavior x
GenUpwardQueryType x
GenValueClass x
overview x
GenValueDisplayFormat x
GenValueStateFlags x
GenValueType x
GenViewClass x
and ink input x
in Tutorial example x x
interaction with content;VisContent:interaction with view x
interaction with VisContent;VisContent:messages received x
overview x x
subclassed in Tutorial example x
GenViewControlClass x x
notification lists x
GenViewDimensionAttrs x
GenViewInkType x
.geo files x
Geode files x
GEODE makefile variable x
Geode parameters files x
in Tutorial example x
GeodeAllocQueue() x x
GeodeAttrs x x x
GeodeDefaultDriverType x x
GeodeDuplicateResource() x x
GeodeFind() x x
GeodeFindResource() x x
GeodeFlushQueue() x x
GeodeFreeDriver() x x
GeodeFreeLibrary() x x
GeodeFreeQueue() x x
GeodeGetAppObject() x x
GeodeGetCodeProcessHandle() x x
GeodeGetDefaultDriver() x x
GeodeGetInfo() x x
GeodeGetInfoType x x x
GeodeGetOptrNS() x x
GeodeGetProcessHandle() x x
GeodeGetUIData() x
GeodeHandle x
GeodeHeapVars x
GeodeInfoDriver() x x
GeodeInfoQueue() x x
GeodeLoad() x x
GeodeLoadDGroup() x
GeodeLoadError x
GeodePrivAlloc() x x
GeodePrivFree() x x
GeodePrivRead() x x
GeodePrivWrite() x x
Geodes x
attaching x
displaying x
finding number of x
handles x x
handles, checking x
loading with Swat x x
name in .gp file x
private data x
type x
GeodeSetDefaultDriver() x x
GeodeSetUIData() x
GeodeToken x x x
in Tutorial example x
GeodeUseDriver() x x
GeodeUseLibrary() x x
GeoFree() x
GeoMalloc() x
Geometry x
generic geometry messages x
in 32 bits x
list of generic hints x
object flags x
object flags;justification flags x
object position x
object size x
VCI_geoAttrs x
VCI_geoDimensionAttrs x
VI_geoAttrs x
view and content x
VisClass messages x
with visible objects x
Geometry manager x
GeoReAlloc() x
geos-release Swat command x
GEOS.INI file x x x
categories x
examing with Swat x
GeosFileHeaderFlags x x
geosfiles Swat command x
GeosFileType x x
geowatch Swat command x
GeoWorksGenAppGCNListType x
GeoworksMediumID x
GeoWorksMetaGCNListType x
GeoWorksNotificationType x
GeoWorksvisContentGCNListType x
GestureCallback() prototype x
get-address Swat command x
in example x
get-key-binding Swat command x
GET_MATCH_ATTRS() macro x
GET_MATCH_FILE_ATTRS() macro x
GET_MISMATCH_ATTRS() macro x
GET_MISMATCH_FILE_ATTRS() macro x
GetActionParams x
getcc Swat command x
GetCenterParams x x
getenv Swat command x
GetMaskType x
GetPalType x x
GetPathType x
GetScaleParams x
GetTargetParams x
GetTokenCreatorParams x
getvalue Swat command x
GFBI_destinationMsg x
GFBI_notificationMsg x
GFH_NOTICE_SIZE x
GFH_USER_NOTES_BUFFER_SIZE x
GFHF_... x x
GFM_info x x
GFMI_... x
GFS_GET_ENTRY_FLAGS() macro x
GFS_GET_ENTRY_NUMBER() macro x
GFS_GET_FULL_SELECTION_PATH_DISK_HANDLE() macro x
GFSFA_... x
GFSGA_... x
GFSI_... x
GFSI_attrs x x x
GFSI_destination x
GFSI_fileCriteria x x
GFSI_notificationMsg x
GFSI_selection x x
GFST_... x
GFT_... x x
GGIT_... x x
GHV_... x
GI_... x
GI_attrs x
GI_comp x
in Tutorial example x x
GI_states x
GI_visMoniker x
in Tutorial example x
simple moniker list in Tutorial example x
GIA_... x
GIGBT_... x
GIGI_... x
GIGI_applyMsg x
GIGI_behaviorType x
GIGI_destination x
GIGI_numSelections x
GIGI_selection x
GIGT_OPTIONS_MENU x
GII_attrs x
GII_identifier x
GII_type x
GII_visibility x
GIT_... x
GIT_AFFIRMATION x
GIT_COMMAND x
GIT_MULTIPLE_RESPONSE x
GIT_NOTIFICATION x
GIT_ORGANIZATIONAL x
GIT_PROGRESS x
GIT_PROPERTIES x
GIV_... x
GIV_DIALOG x
GIV_docBounds
in Tutorial example x
GIV_POPUP x
GIV_SUB_GROUP x
GLE_... x
global Tcl command x
Global variables x
Glockenspiel x
Glue x
externally declared chunks x
Glue parameters files x
in Tutorial example x
GMID_... x
GMT_... x
go Swat command x
Goc x
syntax x x
.goc files x
GOC_POINT macro x
GOC_SW(), GOC_WORD() macros x
GOCFLAGS makefile variable x
.goh files x
.gp files x
for libraries x
hello world sample x
in Tutorial example x
overview x
GPC_DEFAULT_FEATURES x
GPC_DEFAULT_TOOLBOX_FEATURES x
GPCFeatures x
GPCToolboxFeatures x
GPI_longTermMoniker x
GPT_... x
GPT_CLIP x
GPT_CURRENT x
GPT_WIN_CLIP x
GR_... x
GraphicPattern x x
Graphics x x
in Tutorial example x
GrApplyRotation() x x
in Tutorial example x
GrApplyScale() x x
GrApplyTransform() x x
GrApplyTranslation() x x
GrApplyTranslationDWord() x x
in MSG_VIS_DRAW x
GrBeginPath() x x
GrBeginUpdate() x x
in Tutorial example x
GrBitBlt() x x
GrBrushPolyline() x x
GrCharMetrics() x x
GrCharWidth() x x
GrCheckFontAvail() x
GrCheckFontAvailID() x x
GrCheckFontAvailName() x x
GrClearBitmap() x x
GrCloseSubPath() x x
GrComment() x x
GrCompactBitmap() x
GrCopyGString() x x
GrCreateBitmap() x x
GrCreateGString() x x
GrCreatePalette() x x
GrCreateState() x x
in Tutorial example x
GrDeleteGStringElement() x x
GrDestroyBitmap() x x
GrDestroyGString() x x
GrDestroyPalette() x x
GrDestroyState() x x
in Tutorial example x
GrDrawArc() x x
GrDrawArc3Point() x x
GrDrawArc3PointTo() x x
GrDrawBitmap() x x
GrDrawBitmapAtCP() x x
GrDrawChar() x x
GrDrawCharAtCP() x x
GrDrawCurve() x x
GrDrawCurveTo() x x
GrDrawEllipse() x x
GrDrawGString() x x
GrDrawGStringAtCP() x x
GrDrawHLine() x x
GrDrawHLineTo() x x
GrDrawHugeBitmap() x x
GrDrawHugeBitmapAtCP() x x
GrDrawHugeImage() x x
GrDrawImage() x x
GrDrawLine() x x
GrDrawLineTo() x x
GrDrawPath() x x
GrDrawPoint() x x
GrDrawPointAtCP() x x
GrDrawPolygon() x x
GrDrawPolyline() x x
GrDrawRect() x x
GrDrawRectTo() x x
GrDrawRegion() x x
GrDrawRegionAtCP() x x
GrDrawRelArc3PointTo() x x
GrDrawRelLineTo() x x
GrDrawRoundRect() x x
GrDrawRoundRectTo() x x
GrDrawSpline() x x
GrDrawSplineTo() x x
GrDrawText() x x
in Tutorial example x
GrDrawTextAtCP() x x
GrDrawTextField() x
GrDrawVLine() x x
GrDrawVLineTo() x x
GrEditBitmap() x x
GrEditGString() x x
GrEndGString() x x
GrEndPath() x x
GrEndUpdate() x x
in Tutorial example x
GrEnumFonts() x x
GrEscape() x x
Grev x
GreyFrameClass
overview x
GreyGenInteractionClass
overview x
GrFillArc() x x
GrFillArc3Point() x x
GrFillArc3PointTo() x x
GrFillBitmap() x x
GrFillBitmapAtCP() x x
GrFillEllipse() x x
GrFillHugeBitmap() x x
GrFillHugeBitmapAtCP() x x
GrFillPath() x x
GrFillPolygon() x x
GrFillRect() x x
GrFillRectTo() x x
GrFillRoundRect() x x
GrFillRoundRectTo() x x
GrFindNearestPointsize() x x
GrFontMetrics() x x
GrGetAreaColor() x x
GrGetAreaColorMap() x x
GrGetAreaMask() x x
GrGetAreaPattern() x x
GrGetBitmap() x x
GrGetBitmapMode() x x
GrGetBitmapRes() x x
GrGetBitmapSize() x x
GrGetClipRegion() x x
GrGetCurPos() x x
GrGetCurPosWWFixed() x x
GrGetDefFontID() x x
GrGetExclusive() x x
GrGetFont() x x
GrGetFontName() x x
GrGetFontWeight() x x
GrGetFontWidth() x x
GrGetGStringBounds() x x
GrGetGStringBoundsDWord() x x
GrGetGStringElement() x x
GrGetGStringHandle() x
GrGetHugeBitmapSize() x x
GrGetInfo() x
GrGetLineColor() x x
GrGetLineColorMap() x x
GrGetLineEnd() x x
GrGetLineJoin() x x
GrGetLineMask() x x
GrGetLineStyle() x x x
GrGetLineWidth() x x
GrGetMaskBounds() x x
GrGetMaskBoundsDWord() x x
GrGetMiterLimit() x x
GrGetMixMode() x x
GrGetPalette() x x
GrGetPath() x x
GrGetPathBounds() x x
GrGetPathBoundsDWord() x x
GrGetPathPoints() x x
GrGetPathRegion() x x x
GrGetPoint() x x x
GrGetPtrRegBounds() x x
GrGetSubscriptAttr() x x
GrGetSuperscriptAttr() x x
GrGetTextBounds() x x
GrGetTextColor() x x
GrGetTextColorMap() x x
GrGetTextMask() x x
GrGetTextMode() x x
GrGetTextPattern() x x
GrGetTextSpacePad() x x
GrGetTextStyle() x x
GrGetTrackKern() x x
GrGetTransform() x x
GrGetWinBounds() x x
GrGetWinBoundsDWord() x x
GrGetWinHandle() x x
GrGrabExclusive() x x
Grids
provided by ruler library x
GridSpacing x
GrInitDefaultTransform() x x
GrInvalRect() x x
GrInvalRectDWord() x x
GrLabel() x x
GrLoadGString() x x x
GrMapColorIndex() x x
GrMapColorRGB() x x
GrMoveReg() x x
GrMoveTo() x x
GrMoveToWWFixed() x x
GrMulDWFixed() x
GrMulWWFixed() x
GrNewPage() x x
GrNullOp() x x
GrObjs
notification lists x
GRP_... x
GrParseGString() x x
GrQuickArcSine() x
GrQuickCosine() x
GrQuickSine() x
GrQuickTangent() x
GrReleaseExclusive() x x
GrRelMoveTo() x x
GrRestoreState() x x
GrRestoreTransform() x
GrSaveState() x x
GrSaveTransform() x
GrSDivDWFByWWF() x
GrSDivWWFixed() x
GrSetAreaAttr() x x
GrSetAreaColor() x x
GrSetAreaColorMap() x x
GrSetAreaMaskCustom() x x
GrSetAreaMaskSys() x x
GrSetAreaPattern() x x
GrSetBitmapMode() x x
GrSetBitmapRes() x x
GrSetClipPath() x x
GrSetClipRect() x x
GrSetCustomAreaPattern() x x
GrSetCustomTextPattern() x x
GrSetDefaultState() x
GrSetDefaultTransform() x x
GrSetFont() x x
in Tutorial example x
GrSetFontWeight() x x
GrSetFontWidth() x x
GrSetGStringBounds() x x
GrSetGStringPos() x x
GrSetLineAttr() x x x
GrSetLineColor() x x
GrSetLineColorMap() x x
GrSetLineEnd() x x
GrSetLineJoin() x x
GrSetLineMaskCustom() x x
GrSetLineMaskSys() x x
GrSetLineStyle() x x x
GrSetLineWidth() x x
GrSetMiterLimit() x x
GrSetMixMode() x x
GrSetNullTransform() x x
GrSetPalette() x x
GrSetPaletteEntry() x x
GrSetPrivateData() x
GrSetStrokePath() x x
GrSetSubscriptAttr() x x
GrSetSuperscriptAttr() x x
GrSetTextAttr() x x
GrSetTextColor() x x
GrSetTextColorMap() x x
GrSetTextMaskCustom() x x
GrSetTextMaskSys() x x
GrSetTextMode() x x
GrSetTextPattern() x x
GrSetTextSpacePad() x x
GrSetTextStyle() x x
GrSetTrackKern() x x
GrSetTransform() x x
GrSetVMFile() x
with bitmaps x
with GStrings x
GrSetWinClipPath() x x
GrSetWinClipRect() x x
GrSqrRootWWFixed() x
GrTestPath() x x
clipping paths x
GrTestPointInPath() x x
GrTestPointInPolygon() x x
GrTestPointInReg() x x
GrTestRectInMask() x x
GrTestRectInReg() x x
GrTextWidth() x x
GrTextWidthWBFixed() x x
GrTextWidthWWFixed() x
GrTransform() x x
GrTransformByMatrix() x
GrTransformDWFixed() x x
GrTransformDWord() x x
GrTransformWWFixed() x x
GrUDivWWFixed() x
GrUncompactBitmap() x x
GrUntransform() x x
GrUntransformByMatrix() x
GrUntransformDWFixed() x x
GrUntransformDWord() x x
GrUntransformWWFixed() x x
GS_... (GenStates) x
GSApplyRotation() macro x
GSApplyScale() macro x
GSApplyTransform() macro x
GSApplyTranslation() macro x
GSApplyTranslationDWord() macro x
GSBeginPath() macro x
GSBrushPolyline() macro x
GSC_... x
GSCloseSubPath() macro x
GSComment() macro x
GSControl x
GSCreatePalette() macro x
GSDestroyPalette() macro x
GSDrawArc() macro x
GSDrawArc3Point() macro x
GSDrawArc3PointTo() macro x
GSDrawBitmap() macro x
GSDrawBitmapAtCP() macro x
GSDrawBitmapOptr() macro x
GSDrawCBitmap() macro x
GSDrawChar() macro x
GSDrawCharAtCP() macro x
GSDrawCurve() macro x
GSDrawCurveTo() macro x
GSDrawEllipse() macro x
GSDrawHLine() macro x
GSDrawHLineTo() macro x
GSDrawLine() macro x
GSDrawLineTo() macro x
GSDrawPath() macro x
GSDrawPoint() macro x
GSDrawPointAtCP() macro x
GSDrawPolygon() macro x
GSDrawPolyline() macro x
GSDrawRect() macro x
GSDrawRectTo() macro x
GSDrawRelArc3PointTo() macro x
GSDrawRelCurveTo() macro x
GSDrawRoundRect() macro x
GSDrawRoundRectTo() macro x
GSDrawSpline() macro x
GSDrawSplineTo() macro x
GSDrawText() macro x
GSDrawTextAtCP() macro x
GSDrawTextOptr() macro x
GSDrawVLine() macro x
GSDrawVLineTo() macro x
GSEndPath() macro x
GSEndString() macro x
GSEscape() macro x
GSET_... x
GSFillArc() macro x
GSFillArc3Point() macro x
GSFillArc3PointTo() macro x
GSFillBitmap() macro x
GSFillBitmapAtCP() macro x
GSFillBitmapOptr() macro x
GSFillEllipse() macro x
GSFillPath() macro x
GSFillPolygon() macro x
GSFillRect() macro x
GSFillRectTo() macro x
GSFillRoundRect() macro x
GSFillRoundRectTo() macro x
GSInitDefaultTransform() macro x
GSKT_... x x
GSLabel() macro x
GSMoveTo() macro x
GSNewPage() macro x
GSNullOp() macro x
GSP_... x
GSRelMoveTo() macro x
GSRestoreState() macro x
GSRestoreTransform() macro x
GSRetType x x
GSRT_... x x
GSSaveState() macro x
GSSaveTransform() macro x
GSSetAreaAttr() macro x
GSSetAreaColor() macro x
GSSetAreaColorIndex() macro x
GSSetAreaColorMap() macro x
GSSetAreaMask() macro x
GSSetAreaPattern() macro x
GSSetClipPath() macro x
GSSetClipRect() macro x
GSSetCustomAreaMask() macro x
GSSetCustomAreaPattern() macro x
GSSetCustomLineMask() macro x
GSSetCustomLineStyle() macro x
GSSetCustomTextMask() macro x
GSSetCustomTextPattern() macro x
GSSetDefaultTransformation() macro x
GSSetFont() macro x
GSSetFontWeight() macro x
GSSetFontWidth() macro x
GSSetGStringBounds() macro x
GSSetLineAttr() macro x
GSSetLineColor() macro x
GSSetLineColorIndex() macro x
GSSetLineColorMap() macro x
GSSetLineEnd() macro x
GSSetLineJoin() macro x
GSSetLineMask() macro x
GSSetLineStyle() macro x
GSSetLineWidth() macro x
GSSetMiterLimit() macro x
GSSetMixMode() macro x
GSSetNullPath() macro x
GSSetNullTransform() macro x
GSSetPalette() macro x
GSSetPaletteEntry() macro x
GSSetStrokePath() macro x
GSSetSubscriptAttr() macro x
GSSetSuperScriptAttr() macro x
GSSetTextAttr() macro x
GSSetTextColor() macro x
GSSetTextColorIndex() macro x
GSSetTextColorMap() macro x
GSSetTextMask() macro x
GSSetTextMode() macro x
GSSetTextPattern() macro x
GSSetTextSpacePad() macro x
GSSetTextStyle() macro x
GSSetTrackKern() macro x
GSSetTransform() macro x
GSSetWinClipPath() macro x
GSSetWinClipRect() macro x
GSSPT_... x x
GST_... x x
GState x
GStateHandle x
checking x
in Tutorial example x x
GStates x
creation for drawing x
handles x
handles, checking x
in Tutorial example x x
with visible objects x
@gstring x
GStringElement x
GStringErrorType x
GStringKillType x x x
GStrings x
@gstring keyword x
examining with Swat x
GrSetGStringBounds() optimization x
GString type x
printing elements stored in a chunk array x
with impex x
GStringSetPosType x x x
GStringType x x
GT_... x
GTA_... x
GTCI_toolboxList x
GTCI_toolGroupList x
GTGI_controller x
GTI_actionMsg x
in Tutorial example x x
GTI_destination x
in Tutorial example x
GTP_... (GetTokenCreatorParams) x
GTSF_... x
GTXI_... x
GTXI_applyMsg x
GTXI_attrs x
GTXI_stateFlags x
Guidelines
provided by ruler library x
gup Swat command x
command reference x
GUQT_... x
GUQT_DELAYED_OPERATION x
GVA_... x
GVCD_BLUE() macro x
GVCD_FLAGS() macro x
GVCD_GREEN() macro x
GVCD_INDEX() macro x
GVCD_RED() macro x
GVCFeatures x
GVCTF_... x
GVDA_... x
GVDA_NO_LARGER_THAN_CONTENT
in Tutorial example x
GVDF_... x
GVI_attrs x
GVI_color x
GVI_content x
in Tutorial example x x
GVI_docBounds x
in Tutorial example x
GVI_horizAttrs x
in Tutorial example x x
GVI_horizLink x
GVI_inkType x
GVI_origin x
GVI_scaleFactor x
GVI_vertAttrs x
in Tutorial example x x
GVI_vertLink x
GVIT_... x x
GVLI_... x
GVLI_applyMsg x
GVLI_destination x
GVLI_displayFormat x
GVLI_increment x
GVLI_maximum x
GVLI_minimum x
GVLI_stateFlags x
GVLI_value x
GVSF_... x
GVT_... x
GWNT_... x
GWNT_HELP_CONTEXT_CHANGE x
GWNT_PCCOM_DISPLAY_CHAR x
GWNT_PCCOM_DISPLAY_STRING x
GWNT_PCCOM_EXIT_PCCOM x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: H

:H makefile variable expansion x
^h
in Swat address expressions x
.h files x
HAD_... x
HAF_... x x
HAL_COUNT() macro x
HAL_PREV() macro x
Handle x
handle Tcl structure x
HandleModifyOwner() x x
HandleP() x x
Handles x x
dereferencing in Swat expressions x
displaying information with Swat x
displaying the handle table x
displaying with Swat x
examining with Swat x
file handles:fhandles Swat command x
finding number of x
in breakpoint locations x
monitoring with Swat x
VM block handles x
handles Swat command x
in Tutorial example x
HandleToOptr() macro x
HandleV() x x
handsum Swat command x
Handwriting Recognition
library standard path x
has-gcm
as geode type in .gp file x
HatchDash x x
HatchLine x x
HatchPattern x x
hbrk Swat command x x
HC_... x
HC_DEFAULT_FEATURES x
HC_DEFAULT_TOOLBOX_FEATURES x
HCF_LIST x
HCFeatures x
HCI_helpType x
HCToolboxFeatures x
HD_o... x
@header x x
Header files x x x x
goc library files x
Heap x
checking memory handles x
error checking x
examining x
examining with Swat x
finding size of x
heapspace Swat command x
HeapAllocFlags x
with HeapAlloc() x
HeapCongestion x x
HeapFlags x x
heapspace .gp file keyword x x
in Tutorial example x
heapspace Swat command x
in Tutorial example x
height (DocumentSize structure) x
HeightJustifications x
Hello World sample application x
help x
data compression x
First Aid x
help contexts x
Help Files x
help triggers x
helpEditor x
hyperlinks x
Normal Help x
Sample Application x
Simple Help x
help Swat command x
command reference x
defining help for commands x x
defining help for variables x
defining new help topics x
help-fetch Swat command x
help-fetch-level Swat command x
help-help Swat command x
help-is-leaf Swat command x
help-minAspect Swat command x
help-scan Swat command x
help-verbose Swat variable x
HelpControlClass x
helpOptions x
HelpSendHelpNotification x
HelpSendHelpNotification() x
HelpType x
hex Swat command x
HF_... x x
hgwalk Swat command x
Hi tom x
HierarchicalGrabFlags x
HINT_ALIGN_BOTTOM_EDGE_WITH_OBJECT x x x
HINT_ALIGN_LEFT_EDGE_WITH_OBJECT x x x
HINT_ALIGN_LEFT_MONIKER_EDGE_WITH_CHILD x x
HINT_ALIGN_RIGHT_EDGE_WITH_OBJECT x x x
HINT_ALIGN_TOP_EDGE_WITH_OBJECT x x x
HINT_ALLOW_CHILDREN_TO_WRAP x x
HINT_ALWAYS_ADOPT_MENUS x x
HINT_AN_ADVANCED_FEATURE x x
HINT_AVOID_MENU_BAR x x
HINT_BOOLEAN_GROUP_CHECKBOX_STYLE x
HINT_BOOLEAN_GROUP_MINIMIZE_SIZE x
HINT_BOOLEAN_GROUP_SCROLLABLE x x
HINT_BOOLEAN_GROUP_TOOLBOX_STYLE x
HINT_BOTTOM_JUSTIFY_CHILDREN x x
HINT_CAN_CLIP_MONIKER_HEIGHT x x
HINT_CAN_CLIP_MONIKER_WIDTH x x
HINT_CENTER_CHILDREN_HORIZONTALLY x x
HINT_CENTER_CHILDREN_ON_MONIKERS x x
HINT_CENTER_CHILDREN_VERTICALLY x x
HINT_CENTER_MONIKER x x
HINT_CENTER_WINDOW x x
HINT_CUSTOM_CHILD_SPACING x x
HINT_CUSTOM_CHILD_SPACING_IF_LIMITED_SPACE x x
HINT_CUSTOM_SYS_MENU x
HINT_DEFAULT_DEFAULT_ACTION x x
HINT_DEFAULT_FOCUS x x x
HINT_DEFAULT_MODEL x x x
HINT_DEFAULT_TARGET x x x
HINT_DISMISS_WHEN_DISABLED x x
HINT_DISPLAY_... x
HINT_DISPLAY_CONTROL_NO_FEATURES_IF_TRANSPARENT_DOC_CTRL_MODE x
HINT_DISPLAY_GROUP_ARRANGE_TILED x
HINT_DISPLAY_GROUP_FULL_SIZED_ON_STARTUP x
HINT_DISPLAY_GROUP_OVERLAPPING_ON_STARTUP x
HINT_DISPLAY_GROUP_SEPARATE_MENUS x
HINT_DISPLAY_GROUP_TILE_HORIZONTALLY x
HINT_DISPLAY_GROUP_TILE_VERTICALLY x
HINT_DIVIDE_HEIGHT_EQUALLY x x
HINT_DIVIDE_WIDTH_EQUALLY x x
HINT_DO_NOT_PRESERVE_FOCUS x x
HINT_DO_NOT_USE_MONIKER x x
HINT_DONT_ALLOW_CHILDREN_TO_WRAP x x
HINT_DONT_FULL_JUSTIFY_CHILDREN x x
HINT_DONT_INCLUDE_ENDS_IN_CHILD_SPACING x x
HINT_DONT_KEEP_INITIALLY_ONSCREEN x x
HINT_DONT_KEEP_PARTIALLY_ONSCREEN x x
HINT_DONT_SHOW_SHORTCUT x x
HINT_DRAW_IN_BOX x x
HINT_DRAW_SHORTCUT_BELOW x x
HINT_ELSE x x
HINT_ENDIF x x
HINT_ENSURE_TEMPORARY_DEFAULT x x
HINT_EXPAND_HEIGHT_TO_FIT_PARENT x x
HINT_EXPAND_WIDTH_TO_FIT_PARENT x x
HINT_EXTEND_WINDOW_NEAR_BOTTOM_RIGHT x x
HINT_EXTEND_WINDOW_TO_BOTTOM_RIGHT x x
HINT_FILE_SELECTOR_FILE_LIST_WIDTH x
HINT_FILE_SELECTOR_NUMBER_OF_FILES_TO_SHOW x
HINT_FILE_SELECTOR_SCALABLE_UI_DATA x
HINT_FIXED_SIZE x x x
HINT_FREQUENTLY_USED x x
HINT_FULL_JUSTIFY_CHILDREN_HORIZONTALLY x x
HINT_FULL_JUSTIFY_CHILDREN_VERTICALLY x x
HINT_GADGET_BACKGROUND_COLORS x x
HINT_GADGET_TEXT_COLOR x x
HINT_GEN_CONTROL_MODIFY_INITIAL_UI x
HINT_GEN_CONTROL_TOOLBOX_ONLY x
HINT_GEN_CONTROL_USER_MODIFIED_UI x
HINT_GEN_CONTROL_USER_UI x
HINT_GENERAL_CONSUMER_MODE x x
HINT_HELP_NOT_RESIZABLE x x
HINT_HELP_TEXT_FIXED_SIZE x x
HINT_IF_SYSTEM_ATTRS x x
HINT_INCLUDE_ENDS_IN_CHILD_SPACING x x
HINT_INFREQUENTLY_USED x x
HINT_INITIAL_SIZE x x x
HINT_INTERACTION_CANNOT_BE_DEFAULT x
HINT_INTERACTION_COMPLEX_PROPERTIES x
HINT_INTERACTION_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD x
HINT_INTERACTION_DISCARD_WHEN_CLOSED x
HINT_INTERACTION_FAST_RESPONSE_PROPERTIES x
HINT_INTERACTION_FREQUENT_USAGE x
HINT_INTERACTION_INFREQUENT_USAGE x
HINT_INTERACTION_MAKE_RESIZABLE x
HINT_INTERACTION_MAXIMIZABLE x
HINT_INTERACTION_MODAL x x
HINT_INTERACTION_NO_DISTURB x
HINT_INTERACTION_POPOUT_HIDDEN_ON_STARTUP x
HINT_INTERACTION_RELATED_PROPERTIES x
HINT_INTERACTION_REQUIRES_VALIDATION x
HINT_INTERACTION_SIMPLE_PROPERTIES x
HINT_INTERACTION_SLOW_RESPONSE_PROPERTIES x
HINT_INTERACTION_UNRELATED_PROPERTIES x
HINT_ITEM_GROUP_DISPLAY_CURRENT_SELECTION x
HINT_ITEM_GROUP_MINIMIZE_SIZE x
HINT_ITEM_GROUP_MINIMIZE_SIZE_IF_VERTICAL_SCREEN x
HINT_ITEM_GROUP_RADIO_BUTTON_STYLE x
HINT_ITEM_GROUP_SCROLLABLE x
HINT_ITEM_GROUP_TOOLBOX_STYLE x
HINT_KEEP_ENTIRELY_ONSCREEN x x
HINT_KEEP_ENTIRELY_ONSCREEN_WITH_MARGIN x x
HINT_KEEP_INITIALLY_ONSCREEN x x
HINT_KEEP_PARTIALLY_ONSCREEN x x
HINT_LEFT_JUSTIFY_CHILDREN x x
HINT_LEFT_JUSTIFY_MONIKERS x x
HINT_MAKE_REPLY_BAR x x
HINT_MAXIMUM_SIZE x x x
HINT_MINIMIZE_CHILD_SPACING x x
HINT_MINIMUM_SIZE x x x
HINT_NAVIGATION_ID x x
HINT_NAVIGATION_NEXT_ID x x
HINT_NEVER_ADOPT_MENUS x x
HINT_NO_BORDERS_ON_MONIKERS x
HINT_NO_TALLER_THAN_CHILDREN_REQUIRE x x
HINT_NO_WIDER_THAN_CHILDREN_REQUIRE x x
HINT_NOT_MOVABLE x
HINT_ORIENT_CHILDREN_ALONG_LARGER_DIMENSION x x
HINT_ORIENT_CHILDREN_HORIZONTALLY x x
HINT_ORIENT_CHILDREN_VERTICALLY x x
HINT_PLACE_MONIKER_ABOVE x x
HINT_PLACE_MONIKER_ALONG_LARGER_DIMENSION x x
HINT_PLACE_MONIKER_BELOW x x
HINT_PLACE_MONIKER_TO_LEFT x x
HINT_PLACE_MONIKER_TO_RIGHT x x
HINT_POPS_UP_BELOW x x
HINT_POPS_UP_TO_RIGHT x x
HINT_POSITION_ICON_AS_RATIO_OF_FIELD x
HINT_POSITION_WINDOW_AT_MOUSE x x
HINT_POSITION_WINDOW_AT_RATIO_OF_PARENT x x
HINT_PRESERVE_FOCUS x x
HINT_PREVENT_DEFAULT_OVERRIDES x x
HINT_PRIMARY_FULL_SCREEN x
HINT_PRIMARY_NO_EXPRESS_MENU x
HINT_PRIMARY_NO_FILE_MENU x
HINT_PRIMARY_NO_HELP_BUTTON x x
HINT_PRIMARY_OPEN_ICON_BOUNDS x
HINT_RIGHT_JUSTIFY_CHILDREN x x
HINT_SAME_CATEGORY_AS_PARENT x x
HINT_SAME_ORIENTATION_AS_PARENT x x
HINT_SEEK_BOTTOM_OF_VIEW x x x
HINT_SEEK_LEFT_OF_VIEW x x x
HINT_SEEK_MENU_BAR x x
in Tutorial example x x
HINT_SEEK_REPLY_BAR x x
in Tutorial example x x
HINT_SEEK_RIGHT_OF_VIEW x x x
HINT_SEEK_SLOT
in Tutorial example x x
HINT_SEEK_TITLE_BAR_LEFT x x
HINT_SEEK_TITLE_BAR_RIGHT x x
HINT_SEEK_TOP_OF_VIEW x x x
HINT_SEEK_X_SCROLLER_AREA x x x
HINT_SEEK_Y_SCROLLER_AREA x x x
HINT_SHOW_ENTIRE_MONIKER x
HINT_SHOW_SHORTCUT x x
HINT_SIZE_WINDOW_AS_DESIRED x x
HINT_SIZE_WINDOW_AS_RATIO_OF_FIELD x x
HINT_SIZE_WINDOW_AS_RATIO_OF_PARENT x x
HINT_STAGGER_WINDOW x x
HINT_TEXT_... x
HINT_TILE_WINDOW x x
HINT_TOOLBOX x x
HINT_TOP_JUSTIFY_CHILDREN x x
HINT_TRIGGER_BRINGS_UP_WINDOW x
HINT_TRIGGER_DESTRUCTIVE_ACTION x
HINT_USE_ICONIC_MONIKER x x
HINT_USE_INITIAL_BOUNDS_WHEN_RESTORED x
HINT_USE_TEXT_MONIKER x x
HINT_VALUE_X_SCROLLER x
HINT_VALUE_Y_SCROLLER x
HINT_VIEW_LEAVE_ROOM_FOR_HORIZ_SCROLLER x
HINT_VIEW_LEAVE_ROOM_FOR_VERT_SCROLLER x
HINT_WINDOW_NO_CONSTRAINTS x x
HINT_WINDOW_NO_SYS_MENU x x
HINT_WINDOW_NO_TITLE_BAR x x
HINT_WRAP_AFTER_CHILD_COUNT x x
HINT_WRAP_AFTER_CHILD_COUNT_IF_VERTICAL_SCREEN x x
Hints
in Tutorial example x
History
Address, in Swat x
Command, in Swat x
history Swat command x
most recently accessed address x
history Swat command x
HJ_... x
HL_... x
HLOOKUP parse library internal function x
Hot spot (mouse pointer) x
HP_... (GraphicPattern structure) x
HP_numLines x
HPCFeatures x
Huge arrays
checking x
printing with Swat x
HugeArray x
HugeArrayAppend() x x
HugeArrayCompressBlocks() x x
HugeArrayContract() x x
HugeArrayCreate() x x
HugeArrayDelete() x x
HugeArrayDestroy() x x
HugeArrayDirectory x x
HugeArrayDirEntry x
HugeArrayDirty() x x
HugeArrayEnum() x x
HugeArrayExpand() x x
HugeArrayGetCount() x x
HugeArrayInsert() x x
HugeArrayLock() x x
HugeArrayNext() x x
HugeArrayPrev() x x
HugeArrayReplace() x x
HugeArrayResize() x x
HugeArrayUnlock() x x
hwalk Swat command x
command reference x
HyphenationControlClass x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: I

I/O manipulating with Swat x
IACP x
iacp Swat command x
IACPCF_... x
IACPConnect() x x
IACPConnectFlags x x
IACPCreateDefaultLaunchBlock() x x
IACPF_... x
IACPFinishConnect() x
IACPGetServerNumber() x x
IACPLostConnection() x
IACPProcessMessage() x
IACPRegisterDocument() x
IACPRegisterServer() x x
IACPS_... x
IACPSendMessage() x
IACPSendMessageAndWait() x
IACPSendMessageToServer() x
IACPServerFlags x
with IACPRegisterServer() x
IACPServerMode x x
IACPSF_... x
IACPShutdown() x x
IACPShutdownAll() x x
IACPSide x
IACPSM_... x x
IACPSM_...
with IACPRegisterServer() x
IACPUnregisterDocument() x
IACPUnregisterServer() x x
ibrk Swat command x
ibrkPageLen Swat variable x
IBS_... x
IBT_... x
IC_... x
IC_DEFAULT_FEATURES x
IC_DEFAULT_TOOLBOX_FEATURES x
IC_HELP x
ICA_IGNORE_INPUT x x
ICF_... (InkControlFeatures) x
ICF_... (InsertChildFlags) x
ICI_attrs x
ICI_destination x
ICI_message x
ICO_... x
IconIndicatorClass
overview x
ICTF_... x
IDBDI_... x
IDBF_... x
IDC_... x x
@if x x
#if makefile directive x
IF parse library internal function x
if Tcl command x
IF_... (ImageFlags) x x
IF_... (InkFlags) x
IFCC_... x
@ifdef x x
ifdef .gp file keyword x
#ifdef makefile directive x
#ifmake makefile directive x
@ifndef x x
#ifndef makefile directive x
#ifnmake makefile directive x
IFRF_... x
IFSD_... x
ignerr Swat command x
.IGNORE makefile attribute x
.IGNORE makefile special target x
ignoreDirty
object flag x
II_... x
ImageBitSize x
ImageFlags x x
IMC_... x
IMCFeatures x
imem Swat command x
command reference x
imemPageLen Swat variable x
IMF_... x
IMFIH_... x
Immediate mode x
impex library x x
drivers standard path x
ImpexCreateTempFile() x
ImpexDataClasses x x
ImpexDeleteTempFile() x
ImpexExportToMetafile() x
ImpexFileSelectionData x
ImpexImportExportCompleted() x
ImpexImportFromMetafile() x
ImpexMapControl
notification lists x
ImpexMapFileInfoHeader x
ImpexMapFlags x
ImpexTranslationParams x x
with export x
with import x
Implied source x
impliedgrab Swat command x
IMPORTCF_DIALOG_BOX x
ImportControlAttrs x x
ImportControlClass x x
ImportControlToolboxFeatures x
@importMessage x x
.IMPSRC makefile variable x
@include
in Tutorial example x
@include x x
in Tutorial example x
#include makefile directive x
.INCLUDES makefile special target x
incminor .gp file keyword x
INDEX parse library internal function x
index Tcl command x
in example x
IndexArrayBlockHeader x
IndicatorClass
overview x
IndicatorInteractionClass
overview x
info Tcl command x
InitFileCharConvert x
InitFileCommit() x x
InitFileDeleteCategory() x x
InitFileDeleteEntry() x x
InitFileDeleteStringSection() x x
InitFileEnumStringSection() x x
InitFileGetTimeLastModified() x x
InitFileMakeCanonicKeyCategory() x
InitFileReadBoolean() x x
InitFileReadDataBlock() x x
InitFileReadDataBuffer() x x
InitFileReadFlags x
InitFileReadInteger() x x
InitFileReadStringBlock() x x
InitFileReadStringBuffer() x x
InitFileReadStringSectionBlock() x x
InitFileReadStringSectionBuffer() x x
InitFileRevert() x x
InitFileSave() x x
InitFileWriteBoolean() x x
InitFileWriteData() x x
InitFileWriteInteger() x x
InitFileWriteString() x x
InitFileWriteStringSection() x x
Initialization file x
notification lists x
working with option x
Initialization file for Swat x
Ink
notification lists x
detecting x
MSG_GEN_APPLICATION_INK_QUERY_REPLY x
SysGetPenMode() x
view issues x
VisClass ink events x
VisObjectHandlesInkReply() x
Ink input x
INK_DB_MAX_NOTE_KEYWORDS_SIZE x
INK_DB_MAX_TITLE_SIZE x
InkBackgroundType x
InkClass x
InkControlClass x x
InkControlFeatures x x
InkControlToolboxFeatures x x
InkDBDisplayInfo x x
InkDBFrame x x
InkDBGetDisplayInfo() x x
InkDBGetHeadFolder() x x
InkDBInit() x x
InkDBSetDisplayInfo() x x
InkDestinationInfo x
UserCreateInkDestinationInfo() x
InkDestinationInfoParams x
InkFlags x x
InkFolderCreateSubFolder() x x
InkFolderDelete() x x
InkFolderDepthFirstTraverse() x x
InkFolderDisplayChildInList() x x
InkFolderGetChildInfo() x x
InkFolderGetChildNumber() x x
InkFolderGetContents() x x
InkFolderGetNumChildren() x x
InkFolderMove() x x
InkFolderSetTitle() x x
InkFolderSetTitleFromTextObject() x x
InkGetDocCustomGString() x
InkGetDocGString() x x
InkGetDocPageInfo() x x
InkGetParentFolder() x x
InkGetTitle() x x
InkHeader x
InkNoteCopyMoniker() x x
InkNoteCreate() x x
InkNoteCreatePage() x x
InkNoteDelete() x x
InkNoteFindByKeywords() x x
InkNoteFindByTitle() x x
InkNoteGetCreationDate() x x
InkNoteGetKeywords() x x
InkNoteGetModificationDate() x x
InkNoteGetNoteType() x x x
InkNoteGetNumPages() x x
InkNoteGetPages() x x
InkNoteLoadPage() x x
InkNoteMove() x x
InkNoteSavePage() x x
InkNoteSendKeywordsToTextObject() x x
InkNoteSetKeywords() x x
InkNoteSetKeywordsFromTextObject() x x
InkNoteSetModificationDate() x x
InkNoteSetNoteType() x x
InkNoteSetTitle() x x
InkNoteSetTitleFromTextObject() x x
InkReturnValue x
InkSendTitleToTextObject() x x
InkSetDocCustomGString() x
InkSetDocGString() x x
InkSetDocPageInfo() x x
InkTool x
Input x
gadget exclusive;Input:focus and target x
holding up x x x
ignoring x x
keyboard events;Keyboard:with visible objects x
large mouse events x
large mouse input;Mouse:in 32 bits x
mouse events with visible objects x
mouse status x
passive grabs;Input:mouse filters x
raw messages x
visible object grabs;Mouse:grabs x
with VisContents x
Input manager x
insertAtFront message flag x x
InsertChildFlags x
InsertChildOption x
@instance x x
Instance data
@instance keyword x
discardable x
displaying GenClass x
displaying VisClass x
examining with Swat x x x
Goc syntax x x
initializing x
master offsets x
naming conventions x
Naming conventions x
resolving x
structure x x
InstrumentPatch x
InstrumentTable x
INT parse library internal function x
int Swat command x
IntegerOf() x
Inter-Application Communication (IACP) x
InteractionCommand x
Intercepting messages x
.INTERRUPT makefile special target x
Interrupts x
manipulating with Swat x x
masking with Swat x
monitoring with Swat x
intFormat Swat variable x
intr Swat command x
Inverse drawing x
mouse pointer image x
I/O manipulating with Swat x
io Swat command x
IP_... x
irq Swat command x
IRR parse library internal function x
IRV_... x
is-obj-in-class Swat command x
isalnum() x
isalpha() x
iscntrl() x
isdigit() x
ISERR parse library internal function x
isgraphic() x
islower() x
ISNUMBER parse library internal function x
isprint() x
ispunct() x
isspace() x
ISSTRING parse library internal function x
istep Swat command x
command reference x
isupper() x
isxdigit() x
IT_STANDARD_TABLE x
ITP_... x x
ITP_...
with export x
with import x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: J

J_... x
JC_DEFAULT_FEATURES x
JC_DEFAULT_TOOLBOX_FEATURES x
JCFeatures x
JCToolboxFeatures x
JobStatus x x
JS_... x x
Justification x
JustificationControlClass x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: K

@kbdAccelerator x
KbdGrab x
KbdReturnFlags x
Key bindings in Swat x x x
removing x
Keyboard
allowing interrupts x
displaying keyboard hierarchy with Swat x
exclusive notification x
finding keyboard grab with Swat x x
focus x
grabbing x
Keyboard accelerators x
@kbdAccerlerator keyword x
@specificUI keyword x
goc syntax x x x
managing x
Keyboard drivers x
standard path x
Keyboard input x x
passing on x
Keyboard navigation x
Keyboard shortcuts
mnemonics x
keyboard Swat command x
keyboardobj Swat command x
KeyboardShortcut x
KeyboardType x
KEYMAP_... x
KeyMapType x
KRF_PREVENT_PASS_THROUGH x
KS_... x
KT_... x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: L

L_... x
LA_... x
Language type x
Large document model x x
coordinates x
handling mouse events x
mouse events x
LargeMouseData x x
LARGEST_VISIBLE_ROW x
LAST_LINE_END_TYPE x
LAST_LINE_JOIN_TYPE x
LAST_MAP_MODE x
lastCommand Swat command x
LAYER_PRIO_... x
LayerPriority x
LCSDTG_NO_CONVERT_STRING_... x
LCT_... x
.ldf files x
LDF_... x
LE_... x x x
LEF_WILDCARD_NUMBER
logging unknown phone numbers x
LEFT parse library internal function x
leftMargin (DocumentSize structure) x
leftMargin (MarginDimensions structure field) x
LENGTH parse library internal function x
length Tcl command x
LevelIndicatorClass
overview x
lhwalk Swat command x
command reference x
Lib target x
Libraries x
@deflib in library header files x
@deflib keyword x
C API libraries x
entry points x
finding number of x
geode attribute x
geode type x
handles, checking x
loading dynamically x
overview x
protocol numbers x
troubleshooting missing system files x
writing libraries x
library
as geode type in .gp file x
library .gp file keyword x
in Tutorial example x x
Library definition files x
LibraryCallType x
.LIBS makefile special target x
LineAttr x
LineEnd x x
LineJoin x x
Lines (graphics) x
LineStyle x x
@link x x
link Swat command x
LINKFLAGS makefile variable x
list Tcl command x
listi Swat command
command reference x
Lists in Swat
car Swat command x
cdr Swat command x
concat Tcl command x
length Tcl command x
list Tcl command x
range Tcl command x
Tcl syntax x
listSeparator (LocalNumericFormat structure field) x
listSeperator (LocalCurrencyFormat structure field) x
ListWithUserDefinedClass
overview x
LJ_... x x
LMBH_... x
LMD_... x
lmem
resource type in .gp file x
LMEM_TYPE_... x x
LMemAlloc() x x
LMemBlockHeader x
LMemContract() x x
LMemDeleteAt() x x
LMemDeleteAtHandles() x x
LMemDeref() x x
LMemDerefHandles() x x
LMemFree() x x
LMemFreeHandles() x x x
LMemGetChunkSize() x x
LMemGetChunkSizeHandles() x x
LMemInitHeap() x x
LMemInsertAt() x x
LMemInsertAtHandles() x x
LMemReAlloc() x x
LMemReAllocHandles() x x
LMemType x
with LMemInitHeap() x
LMF_... x x x
LN parse library internal function x
load .gp file keyword x
load Swat command x
command reference x
loadapp Swat command x
loadgeode Swat command x
Local memory x
address expressions in Swat x
detecting heaps x x
displaying x
error checking x
examaning with Swat x
examining chunk arrays with Swat x
examining with Swat x
handles, checking x x
monitoring with Swat x
program resources x
storing instance data x
Local variables x
displaying with Swat x
local.mk makefile x
LOCAL_DISTANCE_BUFFER_SIZE x x
LocalAsciiToFixed() x x
LocalCalcDaysInMonth() x x
LocalCmpChars() x
LocalCmpCharsNoCase() x
LocalCmpStrings() x x
LocalCmpStringsDosToGeos() x x
LocalCmpStringsDosToGeosFlags x x x
LocalCmpStringsNoCase() x x
LocalCmpStringsNoSpace() x x
LocalCmpStringsNoSpaceCase() x x
LocalCodePageToGeos() x x
LocalCodePageToGeosChar() x x
LocalCurrencyFormat x x
LocalCustomFormatDateTime() x x
LocalCustomParseDateTime() x x
LocalDistanceFlags x
LocalDistanceFromAscii() x x
LocalDistanceToAscii() x x
LocalDosToGeos() x x
LocalDosToGeosChar() x x
LocalDowncaseChar() x
LocalDowncaseString() x x
LocalFixedToAscii() x x
LocalFormatDateTime() x x
LocalFormatFileDateTime() x x
LocalGeosToCodePage() x x
LocalGeosToCodePageChar() x x
LocalGeosToDos() x x
LocalGeosToDosChar() x x
LocalGetCodePage() x x
LocalGetCurrencyFormat() x x
LocalGetDateTimeFormat() x x
LocalGetLanguage() x x
LocalGetMeasurementType() x x
LocalGetNumericFormat() x x x
LocalGetQuotes() x x
LocalIsCodePageSupported() x
LocalIsDateChar() x x
LocalIsDosChar() x x
LocalIsGraphic() x
LocalIsNumChar() x x
LocalIsPrintable() x
LocalIsSymbol() x x
LocalIsTimeChar() x x
Localization x
giving hints to translators x
in Tutorial example x
measurement type x
@localize
in Tutorial example x
@localize x x
in Tutorial example x
LocalLexicalValue() x x
LocalLexicalValueNoCase() x x
LocalMemoryFlags x x x
LocalNumericFormat x x
LocalParseDateTime() x x
LocalQuotes x x
locals Swat command x
LocalSetCurrencyFormat() x x
LocalSetDateTimeFormat() x x
LocalSetMeasurementType() x x
LocalSetNumericFormat() x x
LocalSetQuotes() x x
LocalStringLength() x x
LocalStringSize() x x
LocalUpcaseChar() x
LocalUpcaseString() x
localwin Swat command x
LOG parse library internal function x
LogAddEntry() x x
LogDeleteAllEntries() x
LogDestroy() x
LogEntry x
Logging communications x
longname .gp file keyword x
in Tutorial example x x
loop Swat command x
LOWER parse library internal function x
LS_... x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: M

:M makefile variable expansion x
macros x
@define keyword x
MailboxAcknowledgeMessageReceipt() x
MailboxBodyReformatted() x
MailboxChangeBodyFormat() x
MailboxCheckMediumAvailable() x
MailboxCheckMediumConnected() x
MailboxDeleteMessage() x
MailboxDoneWithBody() x
MailboxDoneWithVMFile() x
MailboxFreeDriver() x
MailboxGetAdminFile() x
MailboxGetBodyFormat() x
MailboxGetBodyRef() x
MailboxGetDestApp() x
MailboxGetEndBound() x
MailboxGetFirstMediumUnit() x
MailboxGetMessageFlags() x
MailboxGetNumTransAddrs() x
MailboxGetStartBound() x
MailboxGetStorageType() x
MailboxGetSubjectBlock() x
MailboxGetSubjectLMem() x
MailboxGetTransAddr() x
MailboxGetTransData() x
MailboxGetUserTransAddrLMem() x
MailboxGetVMFile() x
MailboxGetVMFileName() x
MailboxLoadDataDriver() x
MailboxLoadDataDriverWithError() x
MailboxLoadTransportDriver() x
MailboxOpenVMFile() x
MailboxPushToMailboxDir() x
MailboxRegisterMessage() x
MailboxSetTransAddr() x
MailboxSetTransData() x
MailboxStealBody() x
.MAIN makefile special target x
Main target x
.MAKE makefile attribute x
Make
in Tutorial example x
make() conditional x
MAKE_HORIZ_ATTRS() macro x
MAKE_SET_CLEAR_ATTRS() macro x
MAKE_SIZE_DWORD x
MAKE_SPACING_DWORD macro x
MAKE_VERT_ATTRS() macro x
Makefiles x
in Tutorial example x
.MAKEFLAGS makefile special target x
.MAKEFLAGS global variable x
MakeWWFixed() x
MakeWWFixed() macro x
in Tutorial example x
Making
in Tutorial example x
malloc() x x
Manufacturer ID x
in file x
MANUFACTURER_ID_... x
ManufacturerFromFormatID x
ManufacturerFromFormatID() macro x
ManufacturerID x
map Swat command x
in example x
map-method Swat command x
MapColorToMono x
mapconcat Swat command x
in example x
MapListBlockHeader x
MarginControlClass x
MarginDimensions x
master (class flag) x
Master classes x
class flag x x
in example x
offset x
Master groups x
Math x
Algebraic Routines;Routines:Floating-Point Algebra x
Comparison Routines;Routines:Floating-Point Comparison x
Conversion Routines;Routines:Floating-Point Conversion x
coprocessors x
Exponential Routines;Routines:Floating-Point Exponents x
Fractions and Integers;Routines:Floating-Point Fractions and Integers x
Functions, Algebraic x
Functions, Transcendental x
Parse Library Internal Functions x
Random Numbers;Routines:Random Numbers x
Trigonometric Routines;Routines:Floating-Point Trigonometry x
UtilAsciiToHex32() x
UtilHex32ToAscii() x
Math Library x
MAX parse library internal function x
MAX_DASH_ARRAY_PAIRS x
MAX_DAY_LENGTH x
MAX_FIELD_NAME_LENGTH x
MAX_FONTS x x
MAX_KERN_VALUE x
MAX_MONTH_LENGTH x
MAX_PAPER_STRING_LENGTH x
MAX_POINT_SIZE x
MAX_SEPARATOR_LENGTH x
MAX_TRACK_KERNING x
MAX_WEEKDAY_LENGTH x
MAX_YEAR_LENGTH x
MAXIMUM_NUMBER_OF_PRINTERS x
MAXIMUM_PAGE_... x
MAXIMUM_PRINTER_NAME_LENGTH x
MC_DEFAULT_FEATURES x
MC_DEFAULT_TOOLBOX_FEATURES x
MCFeatures x
mcount Swat command x
MCToolboxFeatures x
MEASURE_... x
MEASURE_METRIC x
MEASURE_US x
Measurement
LocalDistanceFromAscii() x
LocalDistanceToAscii() x
Measurement type x
formatting x
MeasurementType x x x
MEDIA_... x x
MediaType x x
MediumAndUnit x
MediumType x
MediumUnitType x
Melodic tom x
MemAlloc() x x
MemAllocLMem() x x
MemAllocSetOwner() x x
MemDecRefCount() x x
MemDeref() x x
MemDowngradeExclLock() x x
MemFree() x x
MemGetInfo() x x
MemGetInfoType x x
MemHandle x
MemIncRefCount() x x
MemInitRefCount() x x
MemLock() x x
MemLockExcl() x x
MemLockFixedOrMovable() x
MemLockShared() x x
MemModifyFlags() x x
MemModifyOtherInfo() x x
Memory x
80x86 addressing x
displaying usage with Swat x
examining DOS memory with Swat x
examining with Swat x x x x
finding size with Swat x
handles x
monitoring checksum with Swat x
monitoring with Swat x
MemOwner() x x
MemPLock() x x
MemPtrToHandle() x x
MemReAlloc() x x
memsize Swat command x
MemThreadGrab() x x
MemThreadGrabNB() x x
MemThreadRelease() x x
MemUnlock() x x
MemUnlockExcl() x x
MemUnlockFixedOrMovable() x
MemUnlockShared() x x
MemUnlockV() x x
MemUpgradeSharedLock() x x
Menus x
Message x
@message x x
message (method parameter) x
MESSAGE_NO_... x
MessageError x
MessageFlags x
MessageHandle x
MessageMethod() x
Messages x x
@message keyword x
calling x x x
calling parents;Messages:calling children x
classed events;Classes:sending messages to x
decoding message values x
defining in Goc x
dispatcher x
exporting x x
handles x
handling with variant classes x
importing x x
intercepting x
monitoring with Swat x x x x x x x x x
naming conventions x
prototyping x
ranges x
recorded events x
reserving ranges x x
sending x x
sending with Swat x
verifying receipt x
visible upward queries (VUP) x
with optional parameters x
MessageSetDestination() x
MetaApplicationMessages x
MetaClass x
MetaColorMessages x
Metafile
graphics x
MetaFloatMessages x
MetaGCNMessages x
MetaGrObjMessages x
MetaIACPMessages x
MetaInputMessages x
MetaPrintMessages x
MetaSearchSpellMessages x
MetaSpecificUIMessages x
MetaStyleMessages x
MetaTextMessages x
MetaUIMessages x
MetaWindowMessages x
@method x x
in Tutorial example x x
Method table x
Methods x
@extern keyword x
@method keyword x
@uses keyword and variant classes x
constructing function names from x
finishing stepping through x
goc syntax x x
stopping in with Swat x x
storage x
methods Swat command x
in Tutorial exercise x
MF_... x
MGCNLT_... x
MGCNLT_ACTIVE_LIST x x
MGIT_... x x
MI_base x
MID parse library internal function x
MIN parse library internal function x
MIN_KERN_VALUE x
MIN_MAP_CHAR x
MIN_POINT_SIZE x
MIN_TRACK_KERNING x
MINIMUM_PAGE_... x
MinIncrementType x x
MinMetricMeasure x x
MinPicaMeasure x x
MinPointMeasure x x
MinUSMeasure x x
MIT_... x
MixMode x x
.mk files x
Mkmf x
mkmf
in Tutorial example x
MLBH_... x
MM_... x x
MMM_... x
MOD parse library internal function x
Model x x
AS_MODELABLE x
exclusive x
finding with Swat x x x
model Swat command x
modelobj Swat command x
ModemAnswerCall() x
ModemAutoAnswer() x
ModemClose() x
ModemDial() x
ModemFactoryReset() x
ModemHangup() x
ModemInitModem() x
ModemOpen() x
ModemReset() x
ModemResultCode x
ModemSetMessageDataNotify() x
ModemSetMessageEndCallNotify() x
ModemSetMessageResponseNotify() x
ModemSetRoutineDataNotify() x
ModemSetRoutineResponseNotify() x
Monday x
Money
currency formats x
Monikers x
@visMoniker keyword x
application moniker x
changing x
displaying with Swat x
displaying with VisClass;Visual monikers:drawing and finding x
drawing x
examining with Swat x x
in Tutorial example x
localizing x
long term moniker x
simple moniker list in Tutorial example x
MonoTransfer x
Mouse x
accessing window under, with Swat x
constraining to line x
double-clicks on triggers x
driver standard path x
drivers x
filtering with GenView x
finding mouse grab with Swat x x
grabbing x x
ink input as mouse event x
input events x
moving the pointer;Input:moving the mouse x
object under, finding with Swat x
pointer image x
printing mouse hierarchy x
setting pointer image x
Swat usage x
WinSetPtrImage() x
mouse Swat command x
mouseobj Swat command x
MouseReturnFlags x x x
MouseReturnParams x x x
Moveable memory x
MPM (MinPicaMeasure type) x
MRF_... x x
MSG_... x
MSG_CALENDAR_ADD_EVENT x
MSG_CALENDAR_CHECK_IF_EVENT_EXISTS x
MSG_CALENDAR_DELETE_EVENT_BY_ID x
MSG_CALENDAR_GET_EVENT_BY_ID x
MSG_CALENDAR_MODIFY_EVENT_BY_ID x
MSG_COLOR_SELECTOR_GET_COLOR x
MSG_COLOR_SELECTOR_GET_DRAW_MASK x
MSG_COLOR_SELECTOR_GET_PATTERN x
MSG_COLOR_SELECTOR_SET_COLOR x
MSG_COLOR_SELECTOR_SET_DRAW_MASK x
MSG_COLOR_SELECTOR_SET_PATTERN x
MSG_CONTACT_LIST_PRESERVE_SEARCH_ON_NEXT_LOST_FOCUS x
MSG_EXPORT_CONTROL_GET_ATTRS x
MSG_EXPORT_CONTROL_GET_DATA_CLASSES x
MSG_EXPORT_CONTROL_SET_ACTION x
MSG_EXPORT_CONTROL_SET_ATTRS x
MSG_EXPORT_CONTROL_SET_DATA_CLASSES x
MSG_FILTER_LIST_GET_FOCUS_ITEM x
MSG_FILTER_LIST_GET_ITEM_OPTR x
MSG_FILTER_LIST_GET_MULTIPLE_SELECTIONS x
MSG_FILTER_LIST_GET_SELECTION x
MSG_FILTER_LIST_INITIALIZE x
MSG_FILTER_LIST_IS_ITEM_SELECTED x
MSG_FILTER_LIST_MAKE_ITEM_VISIBLE x
MSG_FILTER_LIST_MAP_PHYSICAL_TO_APP x
MSG_FILTER_LIST_REPLACE_ITEM_MONIKER x
MSG_FILTER_LIST_REPLACE_ITEM_MONIKER_OPTR x
MSG_FILTER_LIST_REPLACE_ITEM_TEXT x
MSG_FILTER_LIST_SET_FOCUS_ITEM x
MSG_FILTER_LIST_SET_ITEM_STATE x
MSG_FILTER_LIST_SET_MULTIPLE_SELECTIONS x
MSG_FILTER_LIST_SET_SINGLE_SELECTION x
MSG_FILTER_TEXT_RESET x
MSG_FILTER_TEXT_RESET_TEXT_IF_NOT_FOCUSED x
MSG_FILTER_TEXT_SEND_FILTER_MSG x
MSG_FLOAT_CTRL_FORMAT_APPLY x
MSG_FSA_RETURN_TO_LAUNCHER
in Tutorial example x
MSG_GEN_ACTIVATE x
MSG_GEN_ACTIVATE_INTERACTION_DEFAULT x
MSG_GEN_ADD_CHILD x
MSG_GEN_ADD_CHILD_UPWARD_LINK_ONLY x
MSG_GEN_ADD_GEOMETRY_HINT x
MSG_GEN_APPLICATION_ACCEPT_INPUT x
MSG_GEN_APPLICATION_BRING_WINDOW_TO_TOP x
MSG_GEN_APPLICATION_BUILD_STANDARD_DIALOG x
MSG_GEN_APPLICATION_DETACH_PENDING x
MSG_GEN_APPLICATION_DO_STANDARD_DIALOG x
MSG_GEN_APPLICATION_FIND_MONIKER x
MSG_GEN_APPLICATION_GET_APP_FEATURES x
MSG_GEN_APPLICATION_GET_APP_INSTANCE_REFERENCE x
MSG_GEN_APPLICATION_GET_APP_MODE_MESSAGE x
MSG_GEN_APPLICATION_GET_ATTACH_FLAGS x
MSG_GEN_APPLICATION_GET_DISPLAY_SCHEME x
MSG_GEN_APPLICATION_GET_GCN_LIST_OF_LISTS x
MSG_GEN_APPLICATION_GET_LAUNCH_FLAGS x
MSG_GEN_APPLICATION_GET_MEASUREMENT_TYPE x
MSG_GEN_APPLICATION_GET_STATE x
MSG_GEN_APPLICATION_HOLD_UP_INPUT x
MSG_GEN_APPLICATION_IGNORE_INPUT x
MSG_GEN_APPLICATION_INITIATE_UI_QUIT x
MSG_GEN_APPLICATION_INK_QUERY_REPLY x x
MSG_GEN_APPLICATION_INSTALL_TOKEN x
MSG_GEN_APPLICATION_LOWER_WINDOW_TO_BOTTOM x
MSG_GEN_APPLICATION_MARK_APP_COMPLETELY_BUSY x
MSG_GEN_APPLICATION_MARK_APP_NOT_COMPLETELY_BUSY x
MSG_GEN_APPLICATION_MARK_BUSY x
MSG_GEN_APPLICATION_MARK_NOT_BUSY x
MSG_GEN_APPLICATION_NOTIFY_MODAL_WIN_CHANGE x
MSG_GEN_APPLICATION_OPEN_COMPLETE x
MSG_GEN_APPLICATION_QUERY_UI x
MSG_GEN_APPLICATION_RESUME_INPUT x
MSG_GEN_APPLICATION_SEND_APP_INSTANCE_REFERENCE_TO_FIELD x
MSG_GEN_APPLICATION_SET_APP_FEATURES x
MSG_GEN_APPLICATION_SET_APP_INSTANCE_REFERENCE x
MSG_GEN_APPLICATION_SET_APP_MODE_MESSAGE x
MSG_GEN_APPLICATION_SET_ATTACHED_TO_STATE_FILE x
MSG_GEN_APPLICATION_SET_MEASUREMENT_TYPE x
MSG_GEN_APPLICATION_SET_NOT_ATTACHED_TO_STATE_FILE x
MSG_GEN_APPLICATION_SET_NOT_USER_INTERACTABLE x
MSG_GEN_APPLICATION_SET_TASK_ENTRY_MONIKER x
MSG_GEN_APPLICATION_SET_USER_INTERACTABLE x
MSG_GEN_APPLICATION_TEST_WIN_INTERACTABILITY x
MSG_GEN_APPLICATION_TOGGLE_CURSOR x
MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION x
MSG_GEN_APPLY x
MSG_GEN_BOOLEAN_GET_IDENTIFIER x
MSG_GEN_BOOLEAN_GROUP_GET_APPLY_MSG x
MSG_GEN_BOOLEAN_GROUP_GET_BOOLEAN_OPTR x
MSG_GEN_BOOLEAN_GROUP_GET_DESTINATION x
MSG_GEN_BOOLEAN_GROUP_GET_INDETERMINATE_BOOLEANS x
MSG_GEN_BOOLEAN_GROUP_GET_MODIFIED_BOOLEANS x
MSG_GEN_BOOLEAN_GROUP_GET_SELECTED_BOOLEANS x
MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_INDETERMINATE x
MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_MODIFIED x
MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_SELECTED x
MSG_GEN_BOOLEAN_GROUP_MAKE_BOOLEAN_VISIBLE x
MSG_GEN_BOOLEAN_GROUP_SEND_STATUS_MSG x
MSG_GEN_BOOLEAN_GROUP_SET_APPLY_MSG x
MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_INDETERMINATE_STATE x
MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_MODIFIED_STATE x
MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_STATE x
MSG_GEN_BOOLEAN_GROUP_SET_DESTINATION x
MSG_GEN_BOOLEAN_GROUP_SET_GROUP_MODIFIED_STATE x
MSG_GEN_BOOLEAN_GROUP_SET_GROUP_STATE x
MSG_GEN_BOOLEAN_SET_IDENTIFIER x
MSG_GEN_BRANCH_REPLACE_OUTPUT_OPTR_CONSTANT x
MSG_GEN_BRANCH_REPLACE_PARAMS x
MSG_GEN_BRING_TO_TOP x
MSG_GEN_CALL_APPLICATION x
MSG_GEN_CALL_PARENT x
MSG_GEN_CALL_SYSTEM x
MSG_GEN_CHANGE_ACCELERATOR x
MSG_GEN_CHECK_IF_FULLY_ENABLED x
MSG_GEN_CHECK_IF_FULLY_USABLE x
MSG_GEN_CONTENT_GET_ATTRS x
MSG_GEN_CONTENT_SET_ATTRS x
MSG_GEN_CONTROL_ADD_APP_TOOLBOX_UI x
MSG_GEN_CONTROL_ADD_APP_UI x
MSG_GEN_CONTROL_ADD_FEATURE x
MSG_GEN_CONTROL_ADD_TO_GCN_LISTS x
MSG_GEN_CONTROL_ADD_TOOLBOX_FEATURE x
MSG_GEN_CONTROL_DESTROY_TOOLBOX_UI x
MSG_GEN_CONTROL_DESTROY_UI x
MSG_GEN_CONTROL_ENABLE_DISABLE x
MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI x
MSG_GEN_CONTROL_GENERATE_UI x
MSG_GEN_CONTROL_GET_INFO x x
MSG_GEN_CONTROL_GET_NORMAL_FEATURES x
MSG_GEN_CONTROL_GET_TOOLBOX_FEATURES x
MSG_GEN_CONTROL_NOTIFY_INTERACTABLE x
MSG_GEN_CONTROL_NOTIFY_NOT_INTERACTABLE x
MSG_GEN_CONTROL_REBUILD_NORMAL_UI x
MSG_GEN_CONTROL_REBUILD_TOOLBOX_UI x
MSG_GEN_CONTROL_REMOVE_FEATURE x
MSG_GEN_CONTROL_REMOVE_FROM_GCN_LISTS x
MSG_GEN_CONTROL_REMOVE_TOOLBOX_FEATURE x
MSG_GEN_CONTROL_SCAN_FEATURE_HINTS x
MSG_GEN_CONTROL_UNBUILD_NORMAL_UI_IF_POSSIBLE x
MSG_GEN_CONTROL_UPDATE_UI x
MSG_GEN_COPY_TREE x x
MSG_GEN_COUNT_CHILDREN x
MSG_GEN_CREATE_VIS_MONIKER x
MSG_GEN_DESTROY x
MSG_GEN_DESTROY_AND_FREE_BLOCK x
MSG_GEN_DISPLAY_CLOSE x
MSG_GEN_DISPLAY_GET_ATTRS x
MSG_GEN_DISPLAY_GET_DOCUMENT x
MSG_GEN_DISPLAY_GET_MAXIMIZED x
MSG_GEN_DISPLAY_GET_MINIMIZED x
MSG_GEN_DISPLAY_GROUP_GET_FULL_SIZED x
MSG_GEN_DISPLAY_GROUP_SELECT_DISPLAY x
MSG_GEN_DISPLAY_GROUP_SET_FULL_SIZED x
MSG_GEN_DISPLAY_GROUP_SET_OVERLAPPING x
MSG_GEN_DISPLAY_GROUP_TILE_DISPLAYS x
MSG_GEN_DISPLAY_SET_ATTRS x
MSG_GEN_DISPLAY_SET_MAXIMIZED x
MSG_GEN_DISPLAY_SET_MINIMIZED x
MSG_GEN_DISPLAY_SET_NOT_MAXIMIZED x
MSG_GEN_DISPLAY_SET_NOT_MINIMIZED x
MSG_GEN_DISPLAY_UPDATE_FROM_DOCUMENT x
MSG_GEN_DOCUMENT_ATTACH_FAILED x
MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT x
MSG_GEN_DOCUMENT_AUTO_SAVE x x
MSG_GEN_DOCUMENT_CHANGE_PASSWORD x
MSG_GEN_DOCUMENT_CHANGE_TYPE x
MSG_GEN_DOCUMENT_CHECK_FOR_MODIFICATIONS x
MSG_GEN_DOCUMENT_CLEAR_EMPTY x x
MSG_GEN_DOCUMENT_CLOSE x
MSG_GEN_DOCUMENT_CLOSE_FILE x
MSG_GEN_DOCUMENT_CLOSE_IF_CLEAN_UNNAMED x
MSG_GEN_DOCUMENT_CLOSE_IF_OPEN_FOR_IACP_ONLY x
MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS x
MSG_GEN_DOCUMENT_CONTROL_GET_FEATURES x
MSG_GEN_DOCUMENT_CONTROL_GET_TOKEN x x
MSG_GEN_DOCUMENT_COPY_TO x
MSG_GEN_DOCUMENT_CREATE_UI_FOR_DOCUMENT x
MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT x
MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT x
MSG_GEN_DOCUMENT_DISABLE_AUTO_SAVE x
MSG_GEN_DOCUMENT_DOCUMENT_HAS_CHANGED x
MSG_GEN_DOCUMENT_EDIT_USER_NOTES x
MSG_GEN_DOCUMENT_ENABLE_AUTO_SAVE x
MSG_GEN_DOCUMENT_FILE_CHANGED_REINITIALIZE_CREATED_UI x
MSG_GEN_DOCUMENT_GET_ATTRS x
MSG_GEN_DOCUMENT_GET_FILE_HANDLE x
MSG_GEN_DOCUMENT_GET_FILE_NAME x
MSG_GEN_DOCUMENT_GET_OPERATION x
MSG_GEN_DOCUMENT_GET_VARIABLE x
MSG_GEN_DOCUMENT_GROUP_GET_ATTRS x
MSG_GEN_DOCUMENT_GROUP_GET_DEFAULT_NAME x
MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY x
MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY_GROUP x
MSG_GEN_DOCUMENT_GROUP_GET_DOC_BY_FILE x
MSG_GEN_DOCUMENT_GROUP_GET_OUTPUT x
MSG_GEN_DOCUMENT_GROUP_GET_PROTOCOL x
MSG_GEN_DOCUMENT_GROUP_GET_TOKEN x x
MSG_GEN_DOCUMENT_GROUP_GET_UI_ATTRS x
MSG_GEN_DOCUMENT_GROUP_GET_UI_FEATURES x
MSG_GEN_DOCUMENT_GROUP_GET_VIEW x
MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY x
MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY_BY_FILE x
MSG_GEN_DOCUMENT_GROUP_SAVE_AS_CANCELED x
MSG_GEN_DOCUMENT_GROUP_SET_OUTPUT x
MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE x
MSG_GEN_DOCUMENT_MOVE_TO x
MSG_GEN_DOCUMENT_NEW x
MSG_GEN_DOCUMENT_OPEN x
MSG_GEN_DOCUMENT_PHYSICAL_AUTO_SAVE x
MSG_GEN_DOCUMENT_PHYSICAL_REVERT x
MSG_GEN_DOCUMENT_PHYSICAL_SAVE x
MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE x
MSG_GEN_DOCUMENT_QUICK_BACKUP x
MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE x
MSG_GEN_DOCUMENT_RECOVER_QUICK_BACKUP x
MSG_GEN_DOCUMENT_RENAME x
MSG_GEN_DOCUMENT_REOPEN_FILE x
MSG_GEN_DOCUMENT_REVERT x
MSG_GEN_DOCUMENT_REVERT_NO_PROMPT x
MSG_GEN_DOCUMENT_SAVE x
MSG_GEN_DOCUMENT_SAVE_AS x
MSG_GEN_DOCUMENT_SAVE_AS_COMPLETED x
MSG_GEN_DOCUMENT_SAVE_AS_TEMPLATE x
MSG_GEN_DOCUMENT_SEARCH_FOR_DOC x
MSG_GEN_DOCUMENT_SET_DEFAULT x
MSG_GEN_DOCUMENT_SET_EMPTY x
MSG_GEN_DOCUMENT_TEST_FOR_FILE x
MSG_GEN_DOCUMENT_UPDATE x
MSG_GEN_DOCUMENT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT x
MSG_GEN_DOCUMENT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT x
MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE x
MSG_GEN_DRAW_MONIKER x
MSG_GEN_DYNAMIC_LIST_ADD_ITEMS x
MSG_GEN_DYNAMIC_LIST_GET_NUM_ITEMS x
MSG_GEN_DYNAMIC_LIST_INITIALIZE x
MSG_GEN_DYNAMIC_LIST_REMOVE_ITEM_LIST x
MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS x
MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER x
MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR x
MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT x
MSG_GEN_FILE_SELECTOR_END_SUSPEND x
MSG_GEN_FILE_SELECTOR_GET_ACTION x
MSG_GEN_FILE_SELECTOR_GET_ATTRS x
MSG_GEN_FILE_SELECTOR_GET_CREATOR x
MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS x
MSG_GEN_FILE_SELECTOR_GET_FILE_CRITERIA x
MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE x
MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH x
MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS x
MSG_GEN_FILE_SELECTOR_GET_MASK x
MSG_GEN_FILE_SELECTOR_GET_SELECTION x
MSG_GEN_FILE_SELECTOR_GET_TOKEN x
MSG_GEN_FILE_SELECTOR_OPEN_ENTRY x
MSG_GEN_FILE_SELECTOR_RESCAN x
MSG_GEN_FILE_SELECTOR_SET_ACTION x
MSG_GEN_FILE_SELECTOR_SET_ATTRS x
MSG_GEN_FILE_SELECTOR_SET_CREATOR x
MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS x
MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA x
MSG_GEN_FILE_SELECTOR_SET_FULL_SELECTION_PATH x
MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS x
MSG_GEN_FILE_SELECTOR_SET_MASK x
MSG_GEN_FILE_SELECTOR_SET_SELECTION x
MSG_GEN_FILE_SELECTOR_SET_TOKEN x
MSG_GEN_FILE_SELECTOR_SUSPEND x
MSG_GEN_FILE_SELECTOR_UP_DIRECTORY x
MSG_GEN_FIND_CHILD x
MSG_GEN_FIND_CHILD_AT_POSITION x
MSG_GEN_FIND_MONIKER x
MSG_GEN_FIND_OBJECT_WITH_TEXT_MONIKER x
MSG_GEN_FIND_PARENT x
MSG_GEN_GET_ATTRIBUTES x
MSG_GEN_GET_ENABLED x
MSG_GEN_GET_FIXED_SIZE x
MSG_GEN_GET_INITIAL_SIZE x
MSG_GEN_GET_KBD_ACCLERATOR x
MSG_GEN_GET_MAXIMUM_SIZE x
MSG_GEN_GET_MINIMUM_SIZE x
MSG_GEN_GET_MONIKER_POS x
MSG_GEN_GET_MONIKER_SIZE x
MSG_GEN_GET_USABLE x
MSG_GEN_GET_VIS_MONIKER x
MSG_GEN_GUP_CALL_OBJECT_OF_CLASS x
MSG_GEN_GUP_FIND_OBJECT_OF_CLASS x
MSG_GEN_GUP_FINISH_QUIT x
MSG_GEN_GUP_INTERACTION_COMMAND x
MSG_GEN_GUP_QUERY x
MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS x
MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS x
MSG_GEN_INTERACTION_ACTIVATE_COMMAND x
MSG_GEN_INTERACTION_GET_ATTRS x
MSG_GEN_INTERACTION_GET_TYPE x
MSG_GEN_INTERACTION_GET_VISIBILITY x
MSG_GEN_INTERACTION_INITIATE x
MSG_GEN_INTERACTION_INITIATE_NO_DISTURB x
MSG_GEN_INTERACTION_SET_ATTRS x
MSG_GEN_INTERACTION_SET_TYPE x
MSG_GEN_INTERACTION_SET_VISIBILITY x
MSG_GEN_INTERACTION_TEST_INPUT_RESTRICTABILITY x
MSG_GEN_ITEM_GET_IDENTIFIER x
MSG_GEN_ITEM_GROUP_GET_APPLY_MSG x
MSG_GEN_ITEM_GROUP_GET_BEHAVIOR_TYPE x
MSG_GEN_ITEM_GROUP_GET_DESTINATION x
MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM x
MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR x
MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS x
MSG_GEN_ITEM_GROUP_GET_NUM_SELECTIONS x
MSG_GEN_ITEM_GROUP_GET_SELECTION x
MSG_GEN_ITEM_GROUP_GET_UNIQUE_IDENTIFIER x
MSG_GEN_ITEM_GROUP_IS_INDETERMINATE x
MSG_GEN_ITEM_GROUP_IS_ITEM_SELECTED x
MSG_GEN_ITEM_GROUP_IS_MODIFIED x
MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE x
MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG x
MSG_GEN_ITEM_GROUP_SET_APPLY_MSG x
MSG_GEN_ITEM_GROUP_SET_BEHAVIOR_TYPE x
MSG_GEN_ITEM_GROUP_SET_DESTINATION x
MSG_GEN_ITEM_GROUP_SET_FOCUS_ITEM x
MSG_GEN_ITEM_GROUP_SET_INDETERMINATE_STATE x
MSG_GEN_ITEM_GROUP_SET_ITEM_STATE x
MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE x
MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS x
MSG_GEN_ITEM_GROUP_SET_NONE_SELECTED x
MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION x
MSG_GEN_ITEM_SET_IDENTIFIER x
MSG_GEN_LOAD_OPTIONS x
in Preferences modules x
MSG_GEN_LOWER_TO_BOTTOM x
MSG_GEN_MAKE_APPLYABLE x
MSG_GEN_MAKE_NOT_APPLYABLE x
MSG_GEN_MOVE_CHILD x
MSG_GEN_NAVIGATE_TO_NEXT_FIELD x
MSG_GEN_NAVIGATE_TO_PREVIOUS_FIELD x
MSG_GEN_NAVIGATION_QUERY x
MSG_GEN_OUTPUT_ACTION x x
MSG_GEN_PATH_GET x
MSG_GEN_PATH_GET_BLOCK x
MSG_GEN_PATH_GET_DISK_HANDLE x
MSG_GEN_PATH_SET x
MSG_GEN_POST_APPLY x
MSG_GEN_PRE_APPLY x
MSG_GEN_PRIMARY_GET_LONG_TERM_MONIKER x
MSG_GEN_PRIMARY_REPLACE_LONG_TERM_MONIKER x
MSG_GEN_PRIMARY_USE_LONG_TERM_MONIKER x
MSG_GEN_PROCESS_ATTACH_TO_PASSED_STATE_FILE x
MSG_GEN_PROCESS_CLOSE_APPLICATION x
MSG_GEN_PROCESS_CLOSE_CUSTOM x
MSG_GEN_PROCESS_CLOSE_ENGINE x
MSG_GEN_PROCESS_CREATE_NEW_STATE_FILE x
MSG_GEN_PROCESS_DETACH_FROM_STATE_FILE x
MSG_GEN_PROCESS_GET_PARENT_FIELD x
MSG_GEN_PROCESS_INSTALL_TOKEN x
MSG_GEN_PROCESS_OPEN_APPLICATION x x
MSG_GEN_PROCESS_OPEN_ENGINE x
MSG_GEN_PROCESS_RESTORE_FROM_STATE x
MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST x
MSG_GEN_PROCESS_UNDO_ABORT_CHAIN x x
MSG_GEN_PROCESS_UNDO_ACCEPT_ACTIONS x x
MSG_GEN_PROCESS_UNDO_ADD_ACTION x x
MSG_GEN_PROCESS_UNDO_CHECK_IF_IGNORING x x
MSG_GEN_PROCESS_UNDO_END_CHAIN x x
MSG_GEN_PROCESS_UNDO_FLUSH_ACTIONS x x
MSG_GEN_PROCESS_UNDO_GET_CONTEXT x x
MSG_GEN_PROCESS_UNDO_GET_FILE x x
MSG_GEN_PROCESS_UNDO_IGNORE_ACTIONS x x
MSG_GEN_PROCESS_UNDO_PLAYBACK_CHAIN x x
MSG_GEN_PROCESS_UNDO_SET_CONTEXT x x
MSG_GEN_PROCESS_UNDO_START_CHAIN x x
MSG_GEN_QUERY_NEED_TO_BE_ON_ACTIVE_LIST x
MSG_GEN_RELOC_MONIKER_LIST x
MSG_GEN_REMOVE x
MSG_GEN_REMOVE_CHILD x
MSG_GEN_REMOVE_GEOMETRY_HINT x
MSG_GEN_REPLACE_VIS_MONIKER x
MSG_GEN_REPLACE_VIS_MONIKER_OPTR x
MSG_GEN_REPLACE_VIS_MONIKER_TEXT x
MSG_GEN_RESET x
in Preferences modules x
MSG_GEN_RESET_TO_INITIAL_SIZE x
MSG_GEN_SAVE_OPTIONS x
in Preferences modules x
MSG_GEN_SEND_TO_CHILDREN x
MSG_GEN_SEND_TO_PARENT x
MSG_GEN_SEND_TO_PROCESS x
MSG_GEN_SET_ATTRS x
MSG_GEN_SET_ENABLED x
MSG_GEN_SET_FIXED_SIZE x
MSG_GEN_SET_INITIAL_SIZE x
MSG_GEN_SET_KBD_ACCELERATOR x
MSG_GEN_SET_KBD_OVERRIDE x
MSG_GEN_SET_KBD_POSITION x
MSG_GEN_SET_MAXIMUM_SIZE x
MSG_GEN_SET_MINIMUM_SIZE x
MSG_GEN_SET_NOT_ENABLED x
MSG_GEN_SET_NOT_USABLE x
MSG_GEN_SET_USABLE x
MSG_GEN_SET_WIN_CONSTRAIN x
MSG_GEN_SET_WIN_POSITION x
MSG_GEN_SET_WIN_SIZE x
MSG_GEN_TEXT_GET_APPLY_MSG x
MSG_GEN_TEXT_GET_ATTRS x
MSG_GEN_TEXT_GET_DESTINATION x
MSG_GEN_TEXT_IS_INDETERMINATE x
MSG_GEN_TEXT_IS_MODIFIED x
MSG_GEN_TEXT_SEND_STATUS_MSG x
MSG_GEN_TEXT_SET_APPLY_MSG x
MSG_GEN_TEXT_SET_ATTRS x
MSG_GEN_TEXT_SET_DESTINATION x
MSG_GEN_TEXT_SET_INDETERMINATE_STATE x
MSG_GEN_TEXT_SET_MODIFIED_STATE x
MSG_GEN_TOOL_GROUP_SET_HIGHLIGHT x
MSG_GEN_TRIGGER_GET_ACTION_MSG x
MSG_GEN_TRIGGER_GET_DESTINATION x
MSG_GEN_TRIGGER_MAKE_DEFAULT_ACTION x
MSG_GEN_TRIGGER_SEND_ACTION x
MSG_GEN_TRIGGER_SET_ACTION_MSG x
MSG_GEN_TRIGGER_SET_DESTINATION x
MSG_GEN_UPDATE_VISUAL x
MSG_GEN_USE_VIS_MONIKER x
MSG_GEN_VALUE_ADD_RANGE_LENGTH x
MSG_GEN_VALUE_DECREMENT x
MSG_GEN_VALUE_GET_APPLY_MSG x
MSG_GEN_VALUE_GET_DESTINATION x
MSG_GEN_VALUE_GET_DISPLAY_FORMAT x
MSG_GEN_VALUE_GET_INCREMENT x
MSG_GEN_VALUE_GET_INTEGER_VALUE x
MSG_GEN_VALUE_GET_MAXIMUM x
MSG_GEN_VALUE_GET_MINIMUM x
MSG_GEN_VALUE_GET_RANGE_LENGTH x
MSG_GEN_VALUE_GET_TEXT_FILTER x
MSG_GEN_VALUE_GET_VALUE x
MSG_GEN_VALUE_GET_VALUE_RATIO x
MSG_GEN_VALUE_GET_VALUE_TEXT x
MSG_GEN_VALUE_INCREMENT x
MSG_GEN_VALUE_IS_INDETERMINATE x
MSG_GEN_VALUE_IS_MODIFIED x
MSG_GEN_VALUE_SEND_STATUS_MSG x
MSG_GEN_VALUE_SET_APPLY_MSG x
MSG_GEN_VALUE_SET_DESTINATION x
MSG_GEN_VALUE_SET_DISPLAY_FORMAT x
MSG_GEN_VALUE_SET_INCREMENT x
MSG_GEN_VALUE_SET_INDETERMINATE_STATE x
MSG_GEN_VALUE_SET_INTEGER_VALUE x
MSG_GEN_VALUE_SET_MAXIMUM x
MSG_GEN_VALUE_SET_MINIMUM x
MSG_GEN_VALUE_SET_MODIFIED_STATE x
MSG_GEN_VALUE_SET_RANGE_LENGTH x
MSG_GEN_VALUE_SET_VALUE x
MSG_GEN_VALUE_SET_VALUE_FROM_TEXT x
MSG_GEN_VALUE_SET_VALUE_RATIO x
MSG_GEN_VALUE_SET_VALUE_TO_MAXIMUM x
MSG_GEN_VALUE_SET_VALUE_TO_MINIMUM x
MSG_GEN_VALUE_SUBTRACT_RANGE_LENGTH x
MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER x
MSG_GEN_VIEW_CALC_WIN_SIZE x
MSG_GEN_VIEW_CALL_WITHOUT_LINKS x
MSG_GEN_VIEW_GET_ATTRS x
MSG_GEN_VIEW_GET_COLOR x
MSG_GEN_VIEW_GET_CONTENT x
MSG_GEN_VIEW_GET_DIMENSION_ATTRS x
MSG_GEN_VIEW_GET_DOC_BOUNDS x
MSG_GEN_VIEW_GET_INCREMENT x
MSG_GEN_VIEW_GET_ORIGIN x
MSG_GEN_VIEW_GET_SCALE_FACTOR x
MSG_GEN_VIEW_GET_VISIBLE_RECT x
MSG_GEN_VIEW_GET_WINDOW x
MSG_GEN_VIEW_INITIATE_DRAG_SCROLL x
MSG_GEN_VIEW_MAKE_RECT_VISIBLE x
MSG_GEN_VIEW_REDRAW_CONTENT x
MSG_GEN_VIEW_RESET_EXTENDED_INK_TYPE x
MSG_GEN_VIEW_SCROLL x
MSG_GEN_VIEW_SCROLL_BOTTOM x
MSG_GEN_VIEW_SCROLL_DOWN x
MSG_GEN_VIEW_SCROLL_LEFT x
MSG_GEN_VIEW_SCROLL_LEFT_EDGE x
MSG_GEN_VIEW_SCROLL_PAGE_DOWN x
MSG_GEN_VIEW_SCROLL_PAGE_LEFT x
MSG_GEN_VIEW_SCROLL_PAGE_RIGHT x
MSG_GEN_VIEW_SCROLL_PAGE_UP x
MSG_GEN_VIEW_SCROLL_RIGHT x
MSG_GEN_VIEW_SCROLL_RIGHT_EDGE x
MSG_GEN_VIEW_SCROLL_SET_X_ORIGIN x
MSG_GEN_VIEW_SCROLL_SET_Y_ORIGIN x
MSG_GEN_VIEW_SCROLL_TOP x
MSG_GEN_VIEW_SCROLL_UP x
MSG_GEN_VIEW_SEND_TO_HLINK x
MSG_GEN_VIEW_SEND_TO_LINKS x
MSG_GEN_VIEW_SEND_TO_VLINK x
MSG_GEN_VIEW_SET_ATTRS x
MSG_GEN_VIEW_SET_COLOR x
MSG_GEN_VIEW_SET_CONTENT x
MSG_GEN_VIEW_SET_DIMENSION_ATTRS x
MSG_GEN_VIEW_SET_DOC_BOUNDS x
MSG_GEN_VIEW_SET_DRAG_BOUNDS x
MSG_GEN_VIEW_SET_EXTENDED_INK_TYPE x
MSG_GEN_VIEW_SET_INCREMENT x
MSG_GEN_VIEW_SET_INK_TYPE x
MSG_GEN_VIEW_SET_ORIGIN x
MSG_GEN_VIEW_SET_PTR_IMAGE x
MSG_GEN_VIEW_SET_SCALE_FACTOR x
MSG_GEN_VIEW_SETUP_TRACKING_ARGS x
MSG_GEN_VIEW_SUSPEND_UPDATE x
MSG_GEN_VIEW_TRACKING_COMPLETE x
MSG_GEN_VIEW_UNSUSPEND_UPDATE x
MSG_GEN_VIEW_UPDATE_CONTENT_TARGET_INFO x
MSG_HELP_CONTROL_FOLLOW_LINK x
MSG_HELP_CONTROL_GET_POINTER_IMAGE x x
MSG_IMPORT_CONTROL_GET_ACTION x x
MSG_IMPORT_CONTROL_GET_ATTRS x
MSG_IMPORT_CONTROL_GET_DATA_CLASSES x
MSG_IMPORT_CONTROL_SET_ACTION x
MSG_IMPORT_CONTROL_SET_ATTRS x
MSG_IMPORT_CONTROL_SET_DATA_CLASSES x
MSG_INK_DELETE_LAST_GESTURE x
MSG_INK_GET_FLAGS x
MSG_INK_GET_TOOL x
MSG_INK_LOAD_FROM_DB_ITEM x
MSG_INK_SAVE_TO_DB_ITEM x
MSG_INK_SET_DIRTY_AD x
MSG_INK_SET_FLAGS x
MSG_INK_SET_MAX_POINTS x
MSG_INK_SET_PEN_COLOR x
MSG_INK_SET_TOOL x
MSG_META_ACK x x
MSG_META_ADD_VAR_DATA x x
MSG_META_APP_SHUTDOWN x
MSG_META_APP_STARTUP x
MSG_META_ATTACH x
MSG_META_ATTACH_GENAPPLICATION x
MSG_META_ATTACH_GENPROCESSCLASS x
MSG_META_ATTACH_OBJECT x
MSG_META_ATTACH_PROCESS x
MSG_META_ATTACH_THREAD x
MSG_META_BLOCK_FREE x
MSG_META_BRING_UP_HELP x x
MSG_META_CHECK_IF_INTERACTIBLE_OBJECT x
MSG_META_CLIPBOARD_COPY x x
MSG_META_CLIPBOARD_CUT x x
MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED x x
MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED x x
MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK x x
MSG_META_CLIPBOARD_NOTIFY_TRANSFER_ITEM_FREED x
MSG_META_CLIPBOARD_PASTE x x
MSG_META_CLIPBOARD_UNDO x
MSG_META_COLORED_OBJECT_SET_COLOR x
MSG_META_COLORED_OBJECT_SET_DRAW_MASK x
MSG_META_COLORED_OBJECT_SET_PATTERN x
MSG_META_CONFIRM_SHUTDOWN x
MSG_META_CONTENT_SET_VIEW x
MSG_META_CONTENT_TRACK_SCROLLING x
MSG_META_CONTENT_VIEW_CLOSING x
MSG_META_CONTENT_VIEW_OPENING x
MSG_META_CONTENT_VIEW_ORIGIN_CHANGED x
MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED x
MSG_META_CONTENT_VIEW_SIZE_CHANGED x
MSG_META_CONTENT_VIEW_WIN_CLOSED x
MSG_META_CONTENT_VIEW_WIN_OPENED x
MSG_META_CONTEXT x
MSG_META_DEC_BLOCK_REF_COUNT x
MSG_META_DELETE x
MSG_META_DELETE_RANGE_OF_CHARS x
MSG_META_DELETE_VAR_DATA x x
MSG_META_DETACH x x
MSG_META_DETACH_ABORT x
MSG_META_DETACH_COMPLETE x x
MSG_META_DISPATCH_EVENT x
MSG_META_DISPLAY_OBJECT_FOR_SEARCH_SPELL x
MSG_META_DOC_OUTPUT_ATTACH_FAILED x
MSG_META_DOC_OUTPUT_ATTACH_UI_TO_DOCUMENT x
MSG_META_DOC_OUTPUT_CREATE_UI_FOR_DOCUMENT x
MSG_META_DOC_OUTPUT_DESTROY_UI_FOR_DOCUMENT x
MSG_META_DOC_OUTPUT_DETACH_UI_FROM_DOCUMENT x
MSG_META_DOC_OUTPUT_HAS_CHANGED x
MSG_META_DOC_OUTPUT_INITIALIZE_DOCUMENT_FILE x
MSG_META_DOC_OUTPUT_PHYSICAL_AUTO_SAVE x
MSG_META_DOC_OUTPUT_PHYSICAL_REVERT x
MSG_META_DOC_OUTPUT_PHYSICAL_SAVE x
MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE x
MSG_META_DOC_OUTPUT_READ_CACHED_DATA_FROM_FILE x
MSG_META_DOC_OUTPUT_SAVE_AS_COMPLETED x
MSG_META_DOC_OUTPUT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT x
MSG_META_DOC_OUTPUT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT x
MSG_META_DOC_OUTPUT_WRITE_CACHED_DATA_TO_FILE x
MSG_META_DRAG_... x
MSG_META_DRAG_FEATURES x
MSG_META_DRAG_MOVE_COPY x
MSG_META_DRAG_OTHER x
MSG_META_DRAG_SELECT x
MSG_META_DUMMY x
MSG_META_END_... x
MSG_META_END_FEATURES x
MSG_META_END_MOVE_COPY x
signalling quick-transfer x
MSG_META_END_OTHER x
MSG_META_END_SELECT x
MSG_META_ENSURE_ACTIVE_FT x
MSG_META_ENSURE_MOUSE_NOT_ACTIVELY_TRESPASSING x
MSG_META_ENSURE_NO_MENUS_IN_STAY_UP_MODE x
MSG_META_EXPOSED x
in Tutorial example x
MSG_META_FIELD_NOTIFY_DETACH x
MSG_META_FIELD_NOTIFY_NO_FOCUS x
MSG_META_FIELD_NOTIFY_START_LAUNCHER_ERROR x
MSG_META_FINAL_OBJ_FREE x
MSG_META_FINISH_QUIT x
MSG_META_FORCE_GRAB_KBD x x
MSG_META_FUP_KBD_CHAR x
MSG_META_GAINED_DEFAULT_EXCL x
MSG_META_GAINED_DIRECTION_EXCL x
MSG_META_GAINED_FOCUS_EXCL x x
MSG_META_GAINED_FULL_SCREEN_EXCL x
MSG_META_GAINED_KBD_EXCL x
MSG_META_GAINED_MODEL_EXCL x x
MSG_META_GAINED_MOUSE_EXCL x
MSG_META_GAINED_PRESSURE_EXCL x
MSG_META_GAINED_SYS_FOCUS_EXCL x
MSG_META_GAINED_SYS_MODEL_EXCL x
MSG_META_GAINED_SYS_TARGET_EXCL x
MSG_META_GAINED_TARGET_EXCL x x
MSG_META_GCN_LIST_ADD x x
MSG_META_GCN_LIST_DESTROY x
MSG_META_GCN_LIST_FIND_ITEM x
MSG_META_GCN_LIST_REMOVE x x
MSG_META_GCN_LIST_SEND x
MSG_META_GEN_PATH_RESTORE_DISK_PROMPT x
MSG_META_GET_CLASS x
MSG_META_GET_CONTEXT x
MSG_META_GET_FLAGS x
MSG_META_GET_FOCUS_EXCL x x
MSG_META_GET_HELP_FILE x
MSG_META_GET_HELP_TYPE x
MSG_META_GET_INI_CATEGORY x
MSG_META_GET_MODEL_EXCL x x
MSG_META_GET_OBJ_BLOCK_OUTPUT x
MSG_META_GET_OBJECT_FOR_SEARCH_SPELL x
MSG_META_GET_OPTR x
MSG_META_GET_TARGET_AT_TARGET_LEVEL x
MSG_META_GET_TARGET_EXCL x x
MSG_META_GET_VAR_DATA x x
MSG_META_GRAB_FOCUS_EXCL x x
MSG_META_GRAB_KBD x x
MSG_META_GRAB_MODEL_EXCL x x
MSG_META_GRAB_TARGET_EXCL x x
MSG_META_IACP_DOC_CLOSE_ACK x
MSG_META_IACP_DOC_OPEN_ACK x
MSG_META_IACP_LOST_CONNECTION x
MSG_META_IACP_NEW_CONNECTION x
MSG_META_IACP_PROCESS_MESSAGE x
MSG_META_IACP_SHUTDOWN_CONNECTION x
MSG_META_IMPLIED_WIN_CHANGE x
MSG_META_INITIALIZE x
MSG_META_INITIALIZE_VAR_DATA x x
MSG_META_IS_OBJECT_IN_CLASS x
MSG_META_KBD_CHAR x x
in Tutorial example x
MSG_META_LARGE_... x
MSG_META_LARGE_DRAG_FEATURES x
MSG_META_LARGE_DRAG_MOVE_COPY x
MSG_META_LARGE_DRAG_OTHER x
MSG_META_LARGE_DRAG_SELECT x
MSG_META_LARGE_END_FEATURES x
MSG_META_LARGE_END_MOVE_COPY x
MSG_META_LARGE_END_OTHER x
MSG_META_LARGE_END_SELECT x
MSG_META_LARGE_PTR x
MSG_META_LARGE_QUERY_IF_PRESS_IS_INK x
MSG_META_LARGE_START_FEATURES x
MSG_META_LARGE_START_MOVE_COPY x
MSG_META_LARGE_START_OTHER x
MSG_META_LARGE_START_SELECT x
MSG_META_LOAD_OPTIONS x
in Preferences modules x
PrefInteractionClass behavior x
MSG_META_LOST_DEFAULT_EXCL x
MSG_META_LOST_DIRECTION_EXCL x
MSG_META_LOST_FOCUS_EXCL x x
MSG_META_LOST_FULL_SCREEN_EXCL x
MSG_META_LOST_KBD_EXCL x
MSG_META_LOST_MODEL_EXCL x x
MSG_META_LOST_MOUSE_EXCL x
MSG_META_LOST_PRESSURE_EXCL x
MSG_META_LOST_SYS_FOCUS_EXCL x
MSG_META_LOST_SYS_MODEL_EXCL x
MSG_META_LOST_SYS_TARGET_EXCL x
MSG_META_LOST_TARGET_EXCL x x
MSG_META_MOUSE_BUMP_NOTIFICATION x
MSG_META_MOUSE_BUTTON x
MSG_META_MOUSE_DRAG x
MSG_META_MOUSE_PTR x
MSG_META_NOTIFY x x
With PCCom library x
MSG_META_NOTIFY_APP_EXITED x
MSG_META_NOTIFY_APP_STARTED x
MSG_META_NOTIFY_DRIVE_CHANGE x
MSG_META_NOTIFY_FILE_CHANGE x
MSG_META_NOTIFY_NO_FOCUS_WITHIN_NODE x
MSG_META_NOTIFY_OBJ_BLOCK_INTERACTIBLE x
MSG_META_NOTIFY_OBJ_BLOCK_NOT_INTERACTIBLE x
MSG_META_NOTIFY_TASK_SELECTED x
MSG_META_NOTIFY_USER_DICT_CHANGE x
MSG_META_NOTIFY_WITH_DATA_BLOCK x x
With PCCom library x
MSG_META_NULL x
MSG_META_OBJ_FLUSH_INPUT_QUEUE x x
MSG_META_OBJ_FREE x x
MSG_META_PAGED_OBJECT_GOTO_PAGE x x
MSG_META_PAGED_OBJECT_NEXT_PAGE x x
MSG_META_PAGED_OBJECT_PREVIOUS_PAGE x x
MSG_META_POST_PASSIVE_... (mouse events) x
MSG_META_POST_PASSIVE_BUTTON x
MSG_META_POST_PASSIVE_END_FEATURES x
MSG_META_POST_PASSIVE_END_MOVE_COPY x
MSG_META_POST_PASSIVE_END_OTHER x
MSG_META_POST_PASSIVE_END_SELECT x
MSG_META_POST_PASSIVE_KBD_CHAR x x
MSG_META_POST_PASSIVE_START_FEATURES x
MSG_META_POST_PASSIVE_START_MOVE_COPY x
MSG_META_POST_PASSIVE_START_OTHER x
MSG_META_POST_PASSIVE_START_SELECT x
MSG_META_PRE_PASSIVE_... (mouse events) x
MSG_META_PRE_PASSIVE_BUTTON x
MSG_META_PRE_PASSIVE_END_FEATURES x
MSG_META_PRE_PASSIVE_END_MOVE_COPY x
MSG_META_PRE_PASSIVE_END_OTHER x
MSG_META_PRE_PASSIVE_END_SELECT x
MSG_META_PRE_PASSIVE_KBD_CHAR x x
MSG_META_PRE_PASSIVE_START_FEATURES x
MSG_META_PRE_PASSIVE_START_MOVE_COPY x
MSG_META_PRE_PASSIVE_START_OTHER x
MSG_META_PRE_PASSIVE_START_SELECT x
MSG_META_PTR x
MSG_META_QUERY_IF_PRESS_IS_INK x x
MSG_META_QUIT x
MSG_META_QUIT_ACK x
MSG_META_QUIT_OBJECT x
MSG_META_QUIT_PROCESS x
MSG_META_RAW_UNIV_ENTER x
MSG_META_RAW_UNIV_LEAVE x
MSG_META_RELEASE_FOCUS_EXCL x x
MSG_META_RELEASE_FT_EXCL x
MSG_META_RELEASE_KBD x x
MSG_META_RELEASE_MODEL_EXCL x x
MSG_META_RELEASE_TARGET_EXCL x x
MSG_META_RELOCATE x
_reloc keyword x
MSG_META_RESET_OPTIONS x
MSG_META_RESOLVE_VARIANT_SUPERCLASS x
MSG_META_SAVE_OPTIONS x
in Preferences modules x
PrefInteractionClass behavior x
MSG_META_SELECT_ALL x
MSG_META_SEND_CLASSED_EVENT x x
to focus x
to model x
to target x
with input hierarchies x
with travel options x
MSG_META_SET_FLAGS x
MSG_META_SET_HELP_FILE x
MSG_META_SET_OBJ_BLOCK_OUTPUT x
MSG_META_SHUTDOWN_ACK x
MSG_META_SHUTDOWN_COMPLETE x
MSG_META_START_... x
MSG_META_START_FEATURES x
MSG_META_START_MOVE_COPY x
signalling quick-transfer x
MSG_META_START_OTHER x
MSG_META_START_SELECT x
MSG_META_SUSPEND x
MSG_META_TEST_WIN_INTERACTIBILITY x
MSG_META_TEXT_CR_FILTERED x
MSG_META_TEXT_EMPTY_STATUS_CHANGED x
MSG_META_TEXT_GAINED_FOCUS x
MSG_META_TEXT_GAINED_TARGET x
MSG_META_TEXT_LOST_FOCUS x
MSG_META_TEXT_LOST_TARGET x
MSG_META_TEXT_NOT_USER_MODIFIED x
MSG_META_TEXT_TAB_FILTERED x
MSG_META_TEXT_USER_MODIFIED x
MSG_META_UI_FORCE_CONTROLLER_UPDATE x
MSG_META_UNDO x
and GenEditControlClass x
MSG_META_UNDO_FREEING_ACTION x
MSG_META_UNRELOCATE x
_reloc keyword x
MSG_META_UNSUSPEND x
MSG_META_UPDATE_WINDOW x
MSG_META_VM_FILE_DIRTY x
MSG_META_WIN_CHANGE x
MSG_NOTIFY_APP_EXITED x
MSG_NOTIFY_APP_STARTED x
MSG_NOTIFY_DATE_TIME_CHANGE x
MSG_NOTIFY_DRIVE_CHANGE x
MSG_NOTIFY_EXPRESS_MENU_CHANGE x
MSG_NOTIFY_FILE_CHANGE x
MSG_NOTIFY_KEYBOARD_LAYOUT_CHANGE x
MSG_NOTIFY_USER_DICT_CHANGE x
MSG_PREF_BOOLEAN_GROUP_SET_ORIGINAL_STATE x
MSG_PREF_DIALOG_CONFIRM_REBOOT x
MSG_PREF_DIALOG_REBOOT x
MSG_PREF_DYNAMIC_LIST_BUILD_ARRAY x
MSG_PREF_DYNAMIC_LIST_FIND_ITEM x
MSG_PREF_GET_REBOOT_INFO x
PrefInteractionClass behavior x
MSG_PREF_GET_REBOOT_STRING x
MSG_PREF_HAS_STATE_CHANGED x
PrefInteractionClass behavior x
MSG_PREF_INIT x
PrefInteractionClass behavior x
MSG_PREF_ITEM_GROUP_GET_ITEM_MONIKER x
with PrefDynamicList x
MSG_PREF_ITEM_GROUP_GET_SELECTED_ITEM_TEXT x
MSG_PREF_ITEM_GROUP_SET_ORIGINAL_SELECTION x
MSG_PREF_ITEM_GROUP_UPDATE_TEXT x
MSG_PREF_NOTIFY_DIALOG_CHANGE x
MSG_PREF_SET_INIT_FILE_CATEGORY x
MSG_PREF_SET_ORIGINAL_STATE x
PrefInteractionClass behavior x
MSG_PREF_STRING_ITEM_CHECK_IF_IN_INIT_FILE_KEY x
MSG_PREF_STRING_ITEM_SET_INIT_FILE_STRING x
MSG_PREF_TOC_LIST_CHECK_DEVICE_AVAILABLE x
MSG_PREF_TOC_LIST_GET_SELECTED_DRIVER_NAME x
MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_INFO x
MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_PATH x
MSG_PREF_VALUE_SET_ORIGINAL_VALUE x
MSG_PRINT_CONTROL_CALC_DOC_DIMENSIONS x
MSG_PRINT_CONTROL_CHECK_IF_DOC_WILL_FIT x
MSG_PRINT_CONTROL_GET_ATTRS x
MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER x
MSG_PRINT_CONTROL_GET_DOC_MARGINS x
MSG_PRINT_CONTROL_GET_DOC_NAME_OUTPUT x
MSG_PRINT_CONTROL_GET_DOC_SIZE x
MSG_PRINT_CONTROL_GET_DOC_SIZE_INFO x
MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE x
MSG_PRINT_CONTROL_GET_OUTPUT x
MSG_PRINT_CONTROL_GET_PAPER_SIZE x
MSG_PRINT_CONTROL_GET_PAPER_SIZE_INFO x
MSG_PRINT_CONTROL_GET_PRINT_MODE x
MSG_PRINT_CONTROL_GET_PRINTER_MARGINS x
MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE x
MSG_PRINT_CONTROL_GET_TOTAL_PAGE_RANGE x
MSG_PRINT_CONTROL_INITIATE_OUTPUT_UI x
MSG_PRINT_CONTROL_INITIATE_PRINT x
MSG_PRINT_CONTROL_PRINT x
MSG_PRINT_CONTROL_PRINTING_CANCELLED x
MSG_PRINT_CONTROL_PRINTING_COMPLETED x
MSG_PRINT_CONTROL_REPORT_PROGRESS x
MSG_PRINT_CONTROL_REPORT_PROGRESS_STRING x
MSG_PRINT_CONTROL_SET_ATTRS x
MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER x
MSG_PRINT_CONTROL_SET_DOC_MARGINS x
MSG_PRINT_CONTROL_SET_DOC_NAME x
MSG_PRINT_CONTROL_SET_DOC_NAME_OUTPUT x
MSG_PRINT_CONTROL_SET_DOC_SIZE x
MSG_PRINT_CONTROL_SET_DOC_SIZE_INFO x
MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE x
MSG_PRINT_CONTROL_SET_OUTPUT x
MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE x
MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE x
MSG_PRINT_CONTROL_VERIFY_COMPLETED x
MSG_PRINT_GET_DOC_NAME x
MSG_PRINT_NOTIFY_PRINT_DB x
MSG_PRINT_NOTIFY_PRINT_JOB_CREATED x
MSG_PRINT_REPORT_PAGE_SIZE x
MSG_PRINT_START_PRINTING x
MSG_PRINT_VERIFY_PRINT_REQUEST x
MSG_PRINTER_INSTALLED_REMOVED x
MSG_PROCESS_CREATE_EVENT_THREAD x
MSG_PROCESS_CREATE_UI_THREAD x
MSG_PROCESS_MEM_FULL x
MSG_PROCESS_NOTIFY_PROCESS_EXIT x
MSG_PROCESS_NOTIFY_THREAD_EXIT x
MSG_PZC_GET_PAGE_SIZE x
MSG_PZC_SET_PAGE_SIZE x
MSG_REPLACE_ALL_OCCURRENCES x
MSG_REPLACE_ALL_OCCURRENCES_IN_SELECTION x
MSG_REPLACE_CURRENT x
MSG_SEARCH x
MSG_SOCKET_ADDRESS_CONTROL_... x
MSG_SPEC_GET_ATTRS x
MSG_SPEC_SET_ATTRS x
MSG_SPELL_CHECK x
MSG_VIS_ADD_BUTTON_POST_PASSIVE x
MSG_VIS_ADD_BUTTON_PRE_PASSIVE x
MSG_VIS_ADD_CHILD x
MSG_VIS_BOUNDS_CHANGED x
MSG_VIS_CALL_PARENT x
MSG_VIS_CLOSE x
MSG_VIS_CLOSE_WIN x
MSG_VIS_COMP_GET_CHILD_SPACING x
MSG_VIS_COMP_GET_GEO_ATTRS x
MSG_VIS_COMP_GET_MARGINS x
MSG_VIS_COMP_GET_MINIMUM_SIZE x
MSG_VIS_COMP_GET_WRAP_COUNT x
MSG_VIS_COMP_SET_GEO_ATTRS x
MSG_VIS_CONTENT_DISABLE_HOLDUP x
MSG_VIS_CONTENT_ENABLE_HOLDUP x
MSG_VIS_CONTENT_GET_ATTRS x
MSG_VIS_CONTENT_GET_WIN_SIZE x
MSG_VIS_CONTENT_HOLD_UP_INPUT_FLOW x
MSG_VIS_CONTENT_NOTIFY_ACTIVE_MOUSE_GRAB_WIN_CHANGED x
MSG_VIS_CONTENT_RECALC_SIZE_BASED_ON_VIEW x
MSG_VIS_CONTENT_RESUME_INPUT_FLOW x
MSG_VIS_CONTENT_SET_ATTRS x
MSG_VIS_CONTENT_SET_DOC_BOUNDS x
MSG_VIS_CONTENT_TEST_IF_ACTIVE_OR_IMPLIED_WIN x
MSG_VIS_CONTENT_UNWANTED_KBD_EVENT x
MSG_VIS_CONTENT_UNWANTED_MOUSE_EVENT x
MSG_VIS_COUNT_CHILDREN x
MSG_VIS_CREATE_CACHED_GSTATES x
MSG_VIS_CREATE_VIS_MONIKER x
MSG_VIS_DESTROY x
MSG_VIS_DESTROY_CACHED_GSTATES x
MSG_VIS_DRAW x x
MSG_VIS_DRAW_MONIKER x
MSG_VIS_FIND_CHILD x
MSG_VIS_FIND_CHILD_AT_POSITION x
MSG_VIS_FIND_MONIKER x
MSG_VIS_FIND_PARENT x
MSG_VIS_FORCE_GRAB_LARGE_MOUSE x
MSG_VIS_FORCE_GRAB_MOUSE x
MSG_VIS_FUP_QUERY_FOCUS_EXCL x
MSG_VIS_GET_ATTRS x
MSG_VIS_GET_BOUNDS x
MSG_VIS_GET_CENTER x
MSG_VIS_GET_GEO_ATTRS x
MSG_VIS_GET_MONIKER_POS x
MSG_VIS_GET_MONIKER_SIZE x
MSG_VIS_GET_OPT_FLAGS x
MSG_VIS_GET_POSITION x
MSG_VIS_GET_SIZE x
MSG_VIS_GET_TYPE_FLAGS x
MSG_VIS_GRAB_LARGE_MOUSE x
MSG_VIS_GRAB_MOUSE x x
MSG_VIS_INVAL_ALL_GEOMETRY x
MSG_VIS_INVAL_TREE x
MSG_VIS_INVALIDATE x
MSG_VIS_LAYER_GET_DOC_BOUNDS x
MSG_VIS_LAYER_INSERT_OR_DELETE_SPACE x
MSG_VIS_LAYER_SET_DOC_BOUNDS x
MSG_VIS_LOST_GADGET_EXCL x
MSG_VIS_MARK_INVALID x
MSG_VIS_MOVE_CHILD x
MSG_VIS_MOVE_RESIZE_WIN x
MSG_VIS_NOTIFY_GEOMETRY_VALID x x
MSG_VIS_OPEN x
MSG_VIS_OPEN_WIN x
MSG_VIS_POSITION_AND_INVAL_IF_NEEDED x
MSG_VIS_POSITION_BRANCH x
MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK x x
MSG_VIS_QUERY_WINDOW x
MSG_VIS_RECALC_SIZE x x
MSG_VIS_RECALC_SIZE_AND_INVAL_IF_NEEDED x
MSG_VIS_RECREATE_CACHED_GSTATES x
MSG_VIS_REDRAW_ENTIRE_OBJECT x
MSG_VIS_RELEASE_GADGET_EXCL x
MSG_VIS_RELEASE_MOUSE x x
MSG_VIS_REMOVE x
MSG_VIS_REMOVE_BUTTON_POST_PASSIVE x
MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE x
MSG_VIS_REMOVE_CHILD x
MSG_VIS_RESET_TO_INITIAL_SIZE x
MSG_VIS_RULER_ADD_HORIZONTAL_GUIDE x
MSG_VIS_RULER_ADD_VERTICAL_GUIDE x
MSG_VIS_RULER_CLEAR_CONSTRAIN_TRANSFORM x
MSG_VIS_RULER_COMBINE_GUIDE_INFO x
MSG_VIS_RULER_CONSTRAIN_TO_AXES x
MSG_VIS_RULER_CONSTRAIN_TO_DIAGONALS x
MSG_VIS_RULER_CONSTRAIN_TO_HORIZONTAL_AXIS x
MSG_VIS_RULER_CONSTRAIN_TO_VERTICAL_AXIS x
MSG_VIS_RULER_CONSTRAIN_X_TO_NEGATIVE_SLOPE_AXIS x
MSG_VIS_RULER_CONSTRAIN_X_TO_UNITY_SLOPE_AXIS x
MSG_VIS_RULER_CONSTRAIN_Y_TO_NEGATIVE_SLOPE_AXIS x
MSG_VIS_RULER_CONSTRAIN_Y_TO_UNITY_SLOPE_AXIS x
MSG_VIS_RULER_CREATE_GUIDE_ARRAY x
MSG_VIS_RULER_DELETE_HORIZONTAL_GUIDE x
MSG_VIS_RULER_DELETE_VERTICAL_GUIDE x
MSG_VIS_RULER_DESELECT_ALL_HORIZONTAL_GUIDES x
MSG_VIS_RULER_DESELECT_ALL_VERTICAL_GUIDES x
MSG_VIS_RULER_DRAW_GRID x
MSG_VIS_RULER_DRAW_GUIDE_INDICATORS x
MSG_VIS_RULER_DRAW_GUIDES x
MSG_VIS_RULER_DRAW_MOUSE_TICK x
MSG_VIS_RULER_GAINED_SELECTION x
MSG_VIS_RULER_GET_CONSTRAIN_STRATEGY x
MSG_VIS_RULER_GET_GRID_SPACING x
MSG_VIS_RULER_GET_GUIDE_INFLUENCE x
MSG_VIS_RULER_GET_ORIGIN x
MSG_VIS_RULER_GET_STRATEGIC_GRID_SPACING x
MSG_VIS_RULER_GET_TYPE x
MSG_VIS_RULER_HIDE_GRID x
MSG_VIS_RULER_HIDE_MOUSE x
MSG_VIS_RULER_INVALIDATE_WITH_SLAVES x
MSG_VIS_RULER_LOST_SELECTION x
MSG_VIS_RULER_SELECT_HORIZONTAL_GUIDE x
MSG_VIS_RULER_SELECT_VERTICAL_GUIDE x
MSG_VIS_RULER_SET_CONSTRAIN_STRATEGY x
MSG_VIS_RULER_SET_CONSTRAIN_TRANSFORM x
MSG_VIS_RULER_SET_GRID_SPACING x
MSG_VIS_RULER_SET_GUIDE_INFLUENCE x
MSG_VIS_RULER_SET_HORIZONTAL_GRID_SPACING x
MSG_VIS_RULER_SET_IGNORE_ORIGIN x
MSG_VIS_RULER_SET_MIN_INCREMENT x
MSG_VIS_RULER_SET_ORIGIN x
MSG_VIS_RULER_SET_REFERENCE x
MSG_VIS_RULER_SET_SCALE x
MSG_VIS_RULER_SET_TYPE x
MSG_VIS_RULER_SET_VECTOR x
MSG_VIS_RULER_SET_VERTICAL_GRID_SPACING x
MSG_VIS_RULER_SHOW_GRID x
MSG_VIS_RULER_SHOW_MOUSE x
MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE x
MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE_X x
MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE_Y x
MSG_VIS_RULER_SNAP_TO_GRID x
MSG_VIS_RULER_SNAP_TO_GRID_X x
MSG_VIS_RULER_SNAP_TO_GRID_Y x
MSG_VIS_RULER_SNAP_TO_GUIDES x
MSG_VIS_RULER_SNAP_TO_GUIDES_X x
MSG_VIS_RULER_SNAP_TO_GUIDES_Y x
MSG_VIS_RULER_TURN_GRID_SNAPPING_OFF x
MSG_VIS_RULER_TURN_GRID_SNAPPING_ON x
MSG_VIS_RULER_TURN_GUIDES_SNAPPING_OFF x
MSG_VIS_RULER_TURN_GUIDES_SNAPPING_ON x
MSG_VIS_RULER_UPDATE_CONTROLLERS x
MSG_VIS_RULER_UPDATE_GRID_CONTROLLER x
MSG_VIS_RULER_UPDATE_GUIDE_CONTROLLER x
MSG_VIS_RULER_UPDATE_TYPE_CONTROLLER x
MSG_VIS_RULER_VIEW_SCALE_FACTOR_CHANGED x
MSG_VIS_SEND_TO_CHILDREN x
MSG_VIS_SEND_TO_PARENT x
MSG_VIS_SET_ATTRS x
MSG_VIS_SET_GEO_ATTRS x
MSG_VIS_SET_POSITION x
MSG_VIS_SET_SIZE x
MSG_VIS_SET_TYPE_FLAGS x
MSG_VIS_TAKE_GADGET_EXCL x
MSG_VIS_TEXT_ADD_PARA_ATTR x
MSG_VIS_TEXT_APPEND_BLOCK x
MSG_VIS_TEXT_APPEND_DB_ITEM x
MSG_VIS_TEXT_APPEND_HUGE_ARRAY x
MSG_VIS_TEXT_APPEND_OPTR x
MSG_VIS_TEXT_APPEND_PTR x
MSG_VIS_TEXT_APPEND_VM_BLOCK x
MSG_VIS_TEXT_DELETE_ALL x
MSG_VIS_TEXT_DELETE_SELECTION x
MSG_VIS_TEXT_DO_KEY_FUNCTION x
MSG_VIS_TEXT_ENTER_INSERT_MODE x
MSG_VIS_TEXT_ENTER_OVERSTRIKE_MODE x
MSG_VIS_TEXT_FILTER_VIA_CHARACTER x
MSG_VIS_TEXT_GET_ALL_BLOCK x
MSG_VIS_TEXT_GET_ALL_DB_ITEM x
MSG_VIS_TEXT_GET_ALL_HUGE_ARRAY x
MSG_VIS_TEXT_GET_ALL_OPTR x
MSG_VIS_TEXT_GET_ALL_PTR x
MSG_VIS_TEXT_GET_ALL_VM_BLOCK x
MSG_VIS_TEXT_GET_AVERAGE_CHAR_WIDTH x
MSG_VIS_TEXT_GET_CHAR_ATTR x
MSG_VIS_TEXT_GET_FEATURES x
MSG_VIS_TEXT_GET_FILTER x
MSG_VIS_TEXT_GET_LINE_HEIGHT x
MSG_VIS_TEXT_GET_LR_MARGIN x x
MSG_VIS_TEXT_GET_MAX_LENGTH x
MSG_VIS_TEXT_GET_MIN_WIDTH x
MSG_VIS_TEXT_GET_ONE_LINE_WIDTH x
MSG_VIS_TEXT_GET_OUTPUT x
MSG_VIS_TEXT_GET_PARA_ATTR x
MSG_VIS_TEXT_GET_RANGE x
MSG_VIS_TEXT_GET_SELECTION_BLOCK x
MSG_VIS_TEXT_GET_SELECTION_DB_ITEM x
MSG_VIS_TEXT_GET_SELECTION_HUGE_ARRAY x
MSG_VIS_TEXT_GET_SELECTION_OPTR x
MSG_VIS_TEXT_GET_SELECTION_PTR x
MSG_VIS_TEXT_GET_SELECTION_RANGE x
MSG_VIS_TEXT_GET_SELECTION_VM_BLOCK x
MSG_VIS_TEXT_GET_SIMPLE_MIN_WIDTH x
MSG_VIS_TEXT_GET_STATE x
MSG_VIS_TEXT_GET_TEXT_SIZE x
MSG_VIS_TEXT_GET_USER_MODIFIED_STATE x
MSG_VIS_TEXT_LOAD_FROM_DB_ITEM x
MSG_VIS_TEXT_LOAD_FROM_DB_ITEM_WITH_STYLES x
MSG_VIS_TEXT_MODIFY_EDITABLE_SELECTABLE x
MSG_VIS_TEXT_RECALC_AND_DRAW x
MSG_VIS_TEXT_REPLACE_ALL_BLOCK x
MSG_VIS_TEXT_REPLACE_ALL_DB_ITEM x
MSG_VIS_TEXT_REPLACE_ALL_HUGE_ARRAY x
MSG_VIS_TEXT_REPLACE_ALL_OPTR x
MSG_VIS_TEXT_REPLACE_ALL_PTR x
MSG_VIS_TEXT_REPLACE_ALL_VM_BLOCK x
MSG_VIS_TEXT_REPLACE_SELECTION_BLOCK x
MSG_VIS_TEXT_REPLACE_SELECTION_DB_ITEM x
MSG_VIS_TEXT_REPLACE_SELECTION_HUGE_ARRAY x
MSG_VIS_TEXT_REPLACE_SELECTION_OPTR x
MSG_VIS_TEXT_REPLACE_SELECTION_PTR x
MSG_VIS_TEXT_REPLACE_SELECTION_VM_BLOCK x
MSG_VIS_TEXT_REPLACE_TEXT x
MSG_VIS_TEXT_SAVE_TO_DB_ITEM x
MSG_VIS_TEXT_SAVE_TO_DB_ITEM_WITH_STYLES x
MSG_VIS_TEXT_SELECT_ALL x
MSG_VIS_TEXT_SELECT_END x
MSG_VIS_TEXT_SELECT_RANGE x
MSG_VIS_TEXT_SELECT_RANGE_SMALL x
MSG_VIS_TEXT_SELECT_RELATIVE x
MSG_VIS_TEXT_SELECT_START x
MSG_VIS_TEXT_SET_CHAR_ATTR x
MSG_VIS_TEXT_SET_CHAR_ATTR_BY_DEFAULT x
MSG_VIS_TEXT_SET_FEATURES x
MSG_VIS_TEXT_SET_FILTER x
MSG_VIS_TEXT_SET_FONT_ID x
MSG_VIS_TEXT_SET_LR_MARGIN x
MSG_VIS_TEXT_SET_MAX_LENGTH x
MSG_VIS_TEXT_SET_NOT_USER_MODIFIED x
MSG_VIS_TEXT_SET_OUTPUT x
MSG_VIS_TEXT_SET_PARA_ATTR x
MSG_VIS_TEXT_SET_PARA_ATTR_BY_DEFAULT x
MSG_VIS_TEXT_SET_POINT_SIZE x
MSG_VIS_TEXT_SET_TB_MARGIN x
MSG_VIS_TEXT_SET_TEXT_STYLE x
MSG_VIS_TEXT_SET_USER_MODIFIED x
MSG_VIS_TEXT_SET_VM_FILE x
MSG_VIS_UPDATE_GEOMETRY x x
MSG_VIS_UPDATE_WIN_GROUP x
MSG_VIS_UPDATE_WINDOWS_AND_IMAGE x
MSG_VIS_VUP_ADD_RECT_TO_UPDATE_REGION x
MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER x
MSG_VIS_VUP_ALTER_INPUT_FLOW x
MSG_VIS_VUP_BUMP_MOUSE x
MSG_VIS_VUP_CALL_OBJECT_OF_CLASS x
MSG_VIS_VUP_CALL_WIN_GROUP x
MSG_VIS_VUP_CREATE_GSTATE x
MSG_VIS_VUP_EC_ENSURE_OBJ_BLOCK_NOT_REFERENCED x
MSG_VIS_VUP_EC_ENSURE_OD_NOT_REFERENCED x
MSG_VIS_VUP_EC_ENSURE_WINDOW_NOT_REFERENCED x
MSG_VIS_VUP_FIND_OBJECT_OF_CLASS x
MSG_VIS_VUP_GET_MOUSE_STATUS x
MSG_VIS_VUP_QUERY x
MSG_VIS_VUP_QUERY_FOCUS_EXCL x
MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS x
MSG_VIS_VUP_SEND_TO_WIN_GROUP x
MSG_VIS_VUP_SET_MOUSE_INTERACTION_BOUNDS x
MSG_VIS_VUP_TERMINATE_ACTIVE_MOUSE_FUNCTION x
MSG_VIS_VUP_TEST_FOR_OBJECT_OF_CLASS x
MSG_VIS_VUP_UPDATE_WIN_GROUP x
MSG_VIS_WIN_ABOUT_TO_BE_CLOSED x
MT_gray[] x
Multi-launchability
optrs and x
single .gp file geode type x
multiple inheritance
in Tutorial example x
Multithreading x
MUSM_... x
mwatch Swat command x x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: N

:N makefile variable expansion x
N parse library internal function x
N_ROW_BLOCKS x
NA parse library internal function x
NAAF_SET_DATA_ON_REPLACE x
NAE_meta x
NAH_... x
name .gp file keyword x
in Tutorial example x x
Name arrays x
NAME_... x
NAME_ARRAY_MAX_DATA_SIZE x
NAME_ARRAY_MAX_NAME_SIZE x
NAME_SERVER x
NameArrayAdd() x x
NameArrayAddFlags x
NameArrayAddHandles() x x
NameArrayChangeName() x x
NameArrayChangeNameHandles() x x
NameArrayCreate() x x
NameArrayCreateAt() x x
NameArrayCreateAtHandles() x x
NameArrayElement x
NameArrayFind() x x
NameArrayFindHandles() x x
NameArrayHeader x x
NameArrayMaxElement x
Naming conventions x
NavigationQueryParams x
NEC() macro x x
needs-coproc
as geode type in .gp file x
neverSaved (class flag) x x
next Swat command x
NFF_LEADING_ZERO x x
NIHT_... x
no-discard
resource type in .gp file x
no-swap
resource type in .gp file x
NO_EC makefile variable x
NO_ERROR_RETURNED x
noload
flag in parameter files x x
Non-exclusive lists x
@noreloc x x
nosort .gp file keyword x
noStructEnum Swat variable x
notDetachable (@start flag) x
NoteType x x
NotificationType x
NotifyHelpContextChange x
NotifyInkHasTarget x
NotifyViewStateChange x
.NOTMAIN makefile attribute x
NPSI_... x
NPV parse library internal function x
NT_... (NoteType type) x x
NT_... (NotificationType structure) x
NT_... (NumberType type) x
NULL x x
null Swat command x
in example x
NullChunk x
NullClass x
NullHandle x x
NullOptr x
NumberFormatFlags x x
numberFormatFlags (LocalNumericFormat structure field) x
Numbers
Floating-Point x
formatting x
NumberType x
NumPaperSizesInfo x
NVSC_... x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: O

OAGP_... x
.obj files x
obj-class Swat command x
obj-foreach-class Swat command x
ObjBlockGetOutput() x x
ObjBlockSetOutput() x x
objbrk Swat command x
ObjChunkFlags x
ObjCompAddChild() x x
ObjCompFindChildByNumber() x x
ObjCompFindChildByOptr() x x
ObjCompMoveChild() x x
ObjCompProcessChildren() x x
ObjCompRemoveChild() x x
objcount Swat command x
ObjDecInteractibleCount() x
ObjDecInUseCount() x x
ObjDeref() x
ObjDeref1() x
ObjDeref1Handles() x
ObjDeref2() x
ObjDeref2Handles() x
ObjDerefGen() x
with pself x
ObjDerefHandles() x
ObjDerefVis() x
with pself x
ObjDispatchMessage() x
ObjDoRelocation() x x
ObjDoUnRelocation() x x
ObjDuplicateMessage() x x
ObjDuplicateResource() x x
@object
in Tutorial example x
@object x x
in Tutorial example x
object
resource type in .gp file x
Object blocks x
geode resources x
Object files x
Object oriented programming x
object pointer
in Tutorial example x
ObjectAndGrabParams x x
Objects x
@object x
@object keyword x
address checking x
application object x
as Tcl symbols x
child objects x
composite vis x
creating x x
creating;Objects:destroying x
defining x
destroying x
destruction x
detaching x
displaying generic instance data x
displaying object blocks x
displaying text objects with Swat x
displaying variable data with Swat x x
examining instance data x
examining with Swat x x x x x
finding variable data x
goc syntax x x x
in 32 bits x
layered objects x
lmem object error checking x
monitoring process object x
monitoring with Swat x x x x x x
naming conventions x
optr x
overlapping x
printing class x
printing class hierarchy in Swat x
printing visual bounds with Swat x
relocating x
testing class with Swat x
visible trees;VisClass:managing object trees x
ObjEnableDetach() x x
ObjFlushInputQueueNextStop x
ObjFreeChunk() x x
ObjFreeChunkHandles() x x
ObjFreeDuplicate() x x
ObjFreeMessage() x x
ObjFreeObjBlock() x x
ObjGetFlags() x x
ObjGetFlagsHandles() x x
ObjGetMessageInfo() x x
ObjIncDetach() x x
ObjIncInteractibleCount() x
ObjIncInUseCount() x x
ObjInitDetach() x x
ObjInitializeMaster() x x
ObjInitializeMasterHandles() x
ObjInitializePart() x x
ObjInitializePartHandles() x
ObjInstantiate() x x
ObjInstantiateForThread() x
ObjIsClassADescendant() x x
ObjIsObjectInClass() x x
ObjIsObjectInClassHandles() x
ObjLinkFindParent() x x
ObjLMemBlockHeader x
ObjLockObjBlock() x x
ObjMapSavedToState() x
ObjMapStateToSaved() x
ObjMarkDirty() x x
ObjMarkDirtyHandles() x
objmessagebrk Swat command x
ObjProcBroadcastMessage() x
ObjRelocateEntryPoint() x
ObjRelocation x
ObjRelocationSource x
ObjRelocationType x
ObjRelocOrUnRelocSuper() x
ObjResizeMaster() x x
ObjResizeMasterHandles() x
ObjSaveBlock() x x
ObjSetFlags() x x
ObjSetFlagsHandles() x x
ObjTestIfObjBlockRunByCurThread() x x
ObjUnrelocateEntryPoint() x
ObjVarAddData() x x
ObjVarAddDataHandles() x
ObjVarCopyDataRange() x x
ObjVarDeleteData() x x
ObjVarDeleteDataAt() x x
ObjVarDeleteDataAtHandles() x
ObjVarDeleteDataHandles() x
ObjVarDeleteDataRange() x x
ObjVarDeleteDataRangeHandles() x
ObjVarDerefData() x x
ObjVarDerefDataHandles() x
ObjVarFindData x
ObjVarFindData() x x
ObjVarFindDataHandles() x
ObjVarScanData x
ObjVarScanData() x x
ObjVarScanDataHandles() x
objwalk Swat command x x
objwatch Swat command x x
Ocarina x
OCF_... x
ODD_EVEN (RegionFillRule type) x x
offsetof() x
OLButtonClass x
OLMBH_... x
omfq Swat command x
.OODATE makefile local variable x
OP_... (OperatorType) x x
OperatorStackElement x
OperatorType x x
Optimization
tally breakpoints x
@optimize x x
optr x x x
accessing x
and multiply launchable geodes x
checking x
ConstructOptr() macro x
dereferencing in Swat address expressions x
extracting from memory with Swat x
from global and local handle x
HandleToOptr() macro x
in Tutorial example x
returning Tcl type token for x
OptrToChunk() x
OptrToChunk() macro x
OptrToHandle() x
OptrToHandle() macro x
OR parse library internal function x
OR_... x
ORS_... x
OSE_... x
oself x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: P

P_... x
PA_... (PrefAttributes structure) x
PAC_DEFAULT_FEATURES x
PAC_DEFAULT_TOOLBOX_FEATURES x
PACFeatures x
PackBits compression x
PACToolboxFeatures x
page controller
features and tools x
messages x
PageEndCommand x x
PageLayout x x
PageLayoutEnvelope x x
PageLayoutLabel x x
PageLayoutPaper x
PageLayoutPapers x
PageSize x
PageSizeControlAttrs x
PageSizeControlChanges x
PageSizeControlClass x x
PageSizeControlFeatures x
PageSizeCtrlAttrs x
PageSizeCtrlFeatures x
PageSizeReport x x
PageType x x
paper... x
PaperOrientation x x
PaperSizeInfo x
PaperSizeOrderInfo x
pappcache x
PARA_ATTR_STYLE_JUST_LEFT_RIGHT_PARA() macro x
ParaAttrControlClass x
ParaBGColorControlClass x
Paragraph runs x
Parallel ports x
PARALLEL_LPT... x
ParallelClose() x x
ParallelGetError() x x
ParallelOpen() x x
ParallelSetError() x x
ParallelUnit x
ParallelWrite() x x
ParallelWriteByte() x x
Parameters files x
ParaSpacingControlClass x
parse library x
PARSER_TOKEN_... x x
ParserEvalExpression() x x
ParserFlags x
ParserFormatExpression() x x
ParserParameters x
ParserParseString() x x
ParserReturnStruct x
ParserScannerEvaluatorError x
ParserToken x
ParserTokenCellData x
ParserTokenData x
ParserTokenFunctionData x
ParserTokenNameData x
ParserTokenNumberData x
ParserTokenOperatorData x
ParserTokenStringData x
ParserTokenType x
PASC_DEFAULT_FEATURES x
PASC_DEFAULT_TOOLBOX_FEATURES x
PASCFeatures x
PASCToolboxFeatures x
Passive grab
keyboard events x
mouse events x
patch Swat command x
patchin Swat command x
patchout Swat command x
.PATH makefile special target x
.PATH x
PATH_BUFFER_SIZE x
PathCombineType x
PathName x
Paths (graphics) x x
displaying with Swat x
patient Tcl structure x
patient-default Swat command x
Patients
application object x x
displaying handles x
displaying status with Swat x
listing active frames of x
name x
name in symbols x
patient Tcl structure x
setting the default x
specific breakpoints x
switching x x
waking up with Swat x x
PatternType x x x
PBGCC_DEFAULT_FEATURES x
PBGI_... x
pbitmap Swat command x
PCA_... x x
pcarray Swat command x
PCB() x
pcbitmap Swat command x
Pccom x
PCCom library x
PCCOMABORT() x x
PCCOMEXIT() x x
PCCOMINIT() x x
PCDocSizeParams x x
PCDSP_... x x
pcelldata Swat command x
pcelldeps Swat command x
Pcget x
PCI_attrs x
PCI_defPrinter x
PCI_docNameOutput x
PCI_docSizeInfo x
PCI_endPage x
PCI_endUserPage x
PCI_output x
PCI_startPage x
PCI_startUserPage x
pclass Swat command x
PCMarginParams x x
PCMP_... x x
PCProgressType x x
PCPT_... x x
Pcs x
PCS.PAT file x
PCS_... (PrintControlStatus) x
PCS_PRINT_BOX_... x
Pcsend x
PCT_... (PathCombineType type) x
PCT_... (SpecSizeType Percent constants) x
PD_... (PointDWord structure) x
PD_... (PointerDef32 structure) x
PDAT&ldots x
PDAType x
pdb Swat command x
PDCT_... (PrefDialogChangeType structure) x
PDF_... x
pdgroup Swat command x
pdisk Swat command x
pdrive x
PDT_... x
pdw x
PEC_... x x
PED_... (PrefEnableData structure) x
PEF_... (PrefEnableFlags structure) x
Pen input x
SysGetPenMode() x
VisObjectHandlesInkReply() x
with VisClass;Input:pen events x
Pen Input Drivers x
pen library x
penum Swat command x
Performance
CPU speed x
SysStatistics() x
pevent Swat command x
PF_... (ParserFlags) x
PF_... (PointWWFixed structure) x
pflags Swat command x
pfont Swat command x
pfontinfo Swat command x
pgen Swat command x
pgs Swat command x
.ph files x
phandle Swat command x
pharray Swat command x
PI parse library internal function x
PI_attrs x
PIA_... (PrefInteractionAttrs structure) x
PIFF_... (PrefInitFileFlags structure) x
PIGI_... x
PII_attrs x
pini Swat command x
pinst Swat command x
piv Swat command x
PL_... x
platform .gp file keyword x
in Tutorial example x x
PLE_... x
plines Swat command x
plist Swat command x
PLL_... x
PLP_... x
PMake x
pmake
in Tutorial example x
PMAKE environment variable x
.PMAKE makefile variable x
PMF_... (PrefMgrFeatures structure) x
PMI_... (PrefModuleInfo structure) x
PMT parse library internal function x
PN_... x
pncbitmap Swat command x
pnormal Swat command x
PO_... x
pobjarray Swat command x
pobject Swat command x
command reference x
in Tutorial example x
pobjmon Swat command x
pod Swat command x
.poh files x x
Point x
PointDWFixed x x
PointDWord x x
PointerDef x x
PointerDef32 x
Pointers x
bounds checking x x
dereferencing in Swat address expressions x
far pointers x
segment/offset pairs in Swat address expressions x
PointSizeControlClass x
PointWWFixed x
Polygons x
GrDrawPolygon() x
GrFillPolygon() x
Polylines x
GrDrawPolyline() x
POM_... x x
Postpassive grab x
keyboard events x
Power management
drivers x
drivers standard path x
waking up by timer x
PP_... x
ppath Swat command x
PPP x
PQT_ x
PQT_... x
pquick Swat command x
.PRECIOUS makefile attribute x
.PRECIOUS makefile special target x
precord Swat command x
PrefAttributes structure x
PrefBooleanGroupClass x
PrefClass x
PrefControlClass x
PrefDialogChangeType structure x
PrefDialogClass x
PrefDynamicListClass x
PrefEnableData structure x
PrefEnableFlags structure x
Preferences modules x
PrefInitFileFlags structure x
PrefInteractionAttrs structure x
PrefInteractionClass x
PrefItemGroupClass x
.PREFIX makefile local variable x
PrefMgrFeatures structure x
PrefModuleInfo structure x
PrefStringItemClass x
PrefTextClass x
PrefTimeDateControlClass x
PrefTocExtraEntry structure x
PrefTocListClass x
PrefTriggerAction structure x
PrefTriggerClass x
PrefValueClass x
preg Swat command x
preload
resource type in .gp file x
Prepassive grab x
keyboard events x
print Swat command x
command reference x
print-cell Swat command x
print-cell-params Swat command x
print-clipboard-item Swat command x
print-column-element Swat command x
print-db-group Swat command x
print-db-item Swat command x
print-eval-dep-list Swat command x
print-obj-and-method Swat command x
print-row Swat command x
print-row-block Swat command x
PRINT_... x
PRINT_GRAPHICS x
PRINT_TEXT x
PRINTCF_... x
PrintControlAttrs x x
PrintControlClass x x
gcnList x
PrintControlFeatures x x
PrintControlStatus x x
PrintControlToolboxFeatures x
PRINTCTF_... x
Printer drivers x
PrinterDriverType x
PrinterOutputModes x x
Printing x x
drivers standard path x
escape codes x
form feeds x
ui gadgetry x
visible objects x
printNamesInObjTrees Swat variable x
PrintQualityEnum x x
printRegions Swat variable x
PrintStatusFlags x
printStop Swat command x
PRIORITY_... x x
PRIVATE
load type in .gp file x
Private data x
GeodePrivAlloc() x
proc Tcl command x
ProcCallFixedOrMovable_cdecl() x x
ProcCallFixedOrMovable_pascal() x x
process
as geode type in .gp file x x
Process
in Tutorial example x
specifying in .gp file x
process (messaging shortcut) x x
Process object x
checking x x
ProcessClass x
attaching x
generic upward queries x
MSG_META_QUIT_PROCESS x
oself and pself x
ProcGetLibraryEntry() x
ProcInfo() x x
procmessagebrk Swat command x
PRODUCT parse library internal function x
PROPER parse library internal function x
Properties gadgets
activating x
GIT_PROPERTIES x
protect Tcl command x
ProtocolNumber x x
Protocols
geode x
@protominor x x
@prototype x x
ps Swat command x
PS_... x
PSC_DEFAULT_FEATURES x
PSC_DEFAULT_TOOLBOX_FEATURES x
PSCA_... x
PSCC_... x
PSCFeatures x
pscope Swat command x
PSCText Sample Application x x
PSCToolboxFeatures x
PSEE_... x
pself x
PSF_... x
PSI_... x
PSII_initFileString x
psize Swat command x
PSIZECF_... x
PSOI_... x
PSR_... x
pssheet Swat command x
psup Swat command x
PT_ (PatternType type) x
PT_... (PageType type) x
PT_... (ParserToken structure) x
PT_... (PatternType type) x
PT_SYSTEM_HATCH x
PTA_... (PrefTriggerAction structure) x
PTCD_... x
PTD_... x
PTEE_... (PrefTocExtraEntry structure) x
ptext Swat command x
PTFD_functionID x
pthread Swat command x
PTI_originalText x
ptimer Swat command x
PTLI_... x
PTND_name x
PTND_value x
PTOD_operatorID x
ptrans Swat command x
ptreg Swat command x
PtrToOffset() x x
PtrToSegment() x x
PTSD_length x
PUBLIC
load type in .gp file x
publish .gp file keyword x
pusage Swat command x
PutUpDialogViaUIThread() x
PV parse library internal function x
pvardata Swat command x
pvardentry Swat command x
PVI_originalValue x
pvis Swat command x
pvismon Swat command x
pvmb Swat command x
pvmt Swat command x
pvsize Swat command x
pwd Swat command x
PZCA_... x
PZCI_... x
PZCMD_... x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: Q

QL_... x
qsort x
QSP_... x
QueueGetMessage() x x
QueueHandle x
QueuePostMessage() x x
Queues, event
displaying events in x
finding with Swat x
handles x
in optrs x
Quick-transfer x
MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER x
MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_... x
QuickSortParameters x
quit Swat command x
command reference x
QuitLevel x
Quotation marks
localizing x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: R

:R makefile variable expansion x
R_... x
RANDOM parse library internal function x
RANDOMN parse library internal function x
range Tcl command x
in example x
RangeEnum() x x
RangeEnumCallbackParams x x
RangeEnumFlags x
RangeEnumParams x
RangeExists() x x
RangeInsert() x x
RangeInsertParams x x x
RangeSort() x
RangeSortCellExistFlags x
RangeSortError x
RangeSortFlags x
RangeSortParams x
RATE parse library internal function x
.RC file (SWAT.RC file) x
RCD_... x
RD_... x
read-char Swat command x
read-line Swat command x
read-only
resource type in .gp file x
read-reg Swat command x
realloc() x x
RecentContactsControlClass
RecentContactsSMSControl in an example x
RecentContactsData x
RecentContactsEraseStack() x
RecentContactsGetCount() x
RecentContactsGetEntry() x
RecentContactsGetMissedCallsTotalCount() x
RecentContactsGetNewCount() x
RecentContactsResetMissedCallsTotalCount() x
RecentContactsResetNewCount() x
RecentContactsSMSControlClass x
@record x x
RecordHeader x
RecordID x
RecordNum x
RECP_... x
Rectangle x
Rectangle data structure x
Rectangles x
GrDrawRect() x
GrFillRect() x
RectDWord x x x
RectRegion x
.RECURSIVE makefile special target x
REF_... x
RefElementHeader x
Region x
RegionFillRule x x
Regions, graphics x
examining with Swat x
GrGetPtrRegBounds() x
GrMoveReg() x
printing in Swat x
printRegions Swat variable x
Registers x
accessing with Swat x
current-registers Swat command x
displaying with Swat x x
error checking x
flag register:clearing flags with Swat x
flags register:complementing flags with Swat x
flags register:displaying x
flags register:getting flags with Swat x
flags register:printing x
flags register:setting flags with Swat x
monitoring with Swat x
setting value with Swat x
value within a frame x
value within thread x
regs Swat command x
regwin Swat command x
REH_refCount x
Release number x
in file x
ReleaseNumber x x
@reloc x x
_reloc x
RELOC_... x
Relocatable data x
@noreloc keyword x
@reloc keyword x
_reloc keyword x
goc syntax x
information in ClassStruct x
kernel routines x
VM operations x
REP_... x
REPEAT parse library internal function x
repeatCommand Swat variable x
RepeatingGenGlyphClass
overview x
replace message flag x x
REPLACE parse library internal function x
require Swat command x
@reserveMessages x x
RESOURCE
load type in .gp file x
resource .gp file keyword x
in Tutorial example x x
Resources x
@end Goc keyword x
@start x x
goc syntax x x
handles, checking x
RespGetPDAHardwareInfo() x
restore-state Swat command x
ret Swat command x
return Tcl command x
return-to-top-level Swat command x
.rev files x
Revision files x
RGB_... x
RGB_... () macros x
RGBColorAsDWord x
RGBD_... x
RGBDelta x
RGBT_... x
RGBTransfer x
RGBValue x
RGC_DEFAULT_... constants x
RGCF_... x x
RGCFeatures x
RID_SOURCE_OFFSET x
RIGHT parse library internal function x
rightMargin (MarginDimensions structure field) x
RIP_... x x
RN_... x
ROUND parse library internal function x
Routines
calling from Swat x x
current routine x
current within frame x
exporting x
local variables x
monitoring with Swat x
naming conventions x
ROWS parse library internal function x
RR_... x
rs Swat command x
.rsc files x
RSCA_SHOW_... x
RSCC_DEFAULT_... constants x
RSCCF_... x
RSCCFeatures x
RSCCToolboxFeatures x
RSCEF_... x
RSCI_... (RulerShowControl instance) x
RSE_... x
RSF_... x
RSP_... x
RTC_DEFAULT_FEATURES x
RTC_DEFAULT_TOOLBOX_FEATURES x
RTCF_... x
RTCFeatures x
RTCTF_... x
RTCToolboxFeatures x
RTNB_type x
ruler library x x
RULER_GUIDE_CONTROL_DEFAULT_FEATURES constant x
RulerContentClass x
RulerGridControlClass x
RulerGuideControlClass x
RulerGuideControlFeatures x
Rulers x
notification lists x x
RulerShowControlAttributes x
RulerShowControlClass x
RulerTypeControlClass x
RulerTypeNotificationBlock x
RulerViewClass x
run Swat command x
in Tutorial example x
rwatch Swat command x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: S

:S makefile string substitution x
SA_... x x
SACA_opaqueSize x
SACAddress x
SampleFormat x
SampleFormatDescription x
SansFace x
Saturday x
save Swat command x
save-state Swat command x
SBF_REUSE_PORT x
sbwalk Swat command x
sbyte x
SC_... x x
SC_DEFAULT_FEATURES x
SC_DEFAULT_TOOLBOX_FEATURES x
scan Tcl command x
SCANNER_TOKEN_... x
ScannerToken x
ScannerTokenCellData x
ScannerTokenData x
ScannerTokenIdentifierData x
ScannerTokenNumberData x
ScannerTokenOperatorData x
ScannerTokenStringData x
ScannerTokenType x
SCD_... x
SCF_... x
scope Swat command x
SCR_... x
Screen Update;Update Mode;Drawing
VisUpdateMode;Redrawing:VisUpdateMode x
screenwin Swat command x
ScriptAttrAsWord x
ScriptFace x
ScrollAction x
ScrollFlags x
sd Swat command x
SD1RTT_... x
SD2RTT_... x
SD3RTT_... x
SD4RTT_... x
SDBT_... x
SDC_... x
SDEC_... x
SDF_DRAW_... x
SDM_... (SysDrawMask) x
SDM_... (SystemDrawMask type) x x
SDNC_... x
SDOP_... x
SDP_... x
SDRTE_... x
SDT_... x x
SDTC_... x
SDWFC_... x
sdword x
SE_... x x
Search paths in makefiles x
SearchFlags x
Searching for text with Swat x
SearchOptions x
SearchParams x
SearchReplaceControlClass x
SearchType x
SEF_... (SupportedEnvelopeFormat type) x
SEF_DISPLAY_IN_TOOLBOX x
segment x
SEH_... x
self (messaging shortcut) x x
Self-modifying code x
SelfDestroyingDialogClass
overview x
SemaphoreError x
SemaphoreHandle x
Semaphores
HandleP() x
handles x
HandleV() x
threads x
@send x x
SEND file x
send Swat command
in Tutorial example x
reference x
send-file x
Serial port x
SERIAL_BAUD_... x
SERIAL_COM... x
SERIAL_FORMAT_... x
SERIAL_MODEM_... x
SerialBaud x
SerialClose() x x
SerialCloseWithoutReset() x x
SerialFlush() x x
SerialFormat x
SerialGetError() x x
SerialGetFormat() x x
SerialGetModem() x x
SerialMode x
SerialModem x
SerialOpen() x x
SerialPortNum x
SerialQuery() x x
SerialRead() x x
SerialReadByte() x x
SerialSetError() x x
SerialSetFlowControl() x x
SerialSetFormat() x x
SerialSetModem() x x
SerialWrite() x x
SerialWriteByte() x x
SerifFace x
SERROR_... x
set-address Swat command x
in example x
set-masks Swat command x
set-repeat Swat command x
in example x
set-startup-ec Swat command x
SET_CUSTOM_PATTERN x
SET_TOKEN_CHARS() macro x
setcc Swat command x
SetPalType x
SF_... (ScriptFace) x
SF_... (SerifFace) x
SF_... (SpecialFunctions type) x
SF_A_... x
SFD_... x
SFN_... x
sftwalk Swat command x
SGC_MACHINE() macro x
SGC_PROCESSOR() macro x
SGIT_... x
SH_... (SpecHeight flags) x
SH_... (SystemHatch type) x x
ShadowAnchor x
shared
resource type in .gp file x
Shared files
VM files x
Shared memory
geode resources x
Shell commands in makefiles x
shift (keyboard accelerator modifier) x
ShiftState x x
showcalls
-L option x
showcalls Swat command x
showMethodNames Swat command x
SIFD_... x
SIJP_... x
.SILENT makefile attribute x
.SILENT makefile special target x
SIN parse library internal function x
single
as geode type in .gp file x x
SINH parse library internal function x
SIP_... x
SIQP_... x
SIT_... x
SizeAsDWord x
skip Swat command x
sleep Swat command x
slist Swat command x
SLN parse library internal function x
SLT_... x
SM_... x
SM_...
with SerialSetFlowControl() x
with SerialSetFormat() x
with SeriealGetFormat() x
smatch Swat command x
SMC_... x
SMID_... (SampleFormat structure) x
SMT_... x
SNAAF_... x
SNF_... x
SO_... x
Socket x x x
Socket library x
SocketAccept() x x
SocketAddLoadOnMsg() x x
SocketAddLoadOnMsgInDomain() x x
SocketAddress x x
SocketAddressControllerClass x
SocketBind() x x
SocketBindFlags x
SocketBindInDomain() x x
SocketCheckListen() x x
SocketCheckMediumConnection() x
SocketCheckReady() x x
SocketCheckRequest x x
SocketClose() x x
SocketCloseDomainMedium() x x
SocketCloseSend() x x
SocketCondition x x x
SocketConnect() x x
SocketCreate() x x
SocketCreateResolvedAddress() x
SocketDeliveryType x x
SocketError x x
SocketGetAddressController() x x
SocketGetAddressMedium() x x
SocketGetAddressSize() x x
SocketGetDomainMedia() x x
SocketGetDomains() x x
SocketGetIntSocketOption x
SocketGetIntSocketOption() x
SocketGetMediumAddress() x x
SocketGetPeerName() x x
SocketGetSocketName() x x
SocketGetSocketOption() x
SocketInterrupt() x x
SocketInterruptResolve() x x
SocketListen() x x
SocketLoadType x
SocketOpenDomainMedium() x x
Creating PPP connections in TCP/IP domain x
SocketOption x x
SocketPort x
SocketRecv() x x
SocketRecvFlags x
SocketRemoveLoadOnMsg() x x
SocketRemoveLoadOnMsgInDomain() x x
SocketResolve() x x
SocketSend() x x
SocketSendFlags x
SocketSetIntSocketOption() x x
SocketSetSocketOption() x
sort Swat command x
SortedNameArrayAddFlags x
SortOrder x
sound library
parameters file x
SoundAllocMusic() x
SoundAllocMusicNote() x
SoundAllocMusicStream() x
SoundAllocSampleStream() x
SoundDisableSampleStream() x
SoundDriverCapability x
SoundDriverEnvelopeCapability x
SoundDriverNoiseCapability x
SoundDriverTimbreCapability x
SoundDriverWaveFormCapability x
SoundEnableSampleStream() x
SoundFreeMusic() x
SoundFreeMusicNote() x
SoundFreeMusicStream() x
SoundFreeSampleStream() x
SoundGetExclusive() x
SoundGetExclusiveNB() x
SoundInitMusic() x
SoundPlayFlags x
SoundPlayMusic() x
SoundPlayMusicNote() x
SoundPlayToMusicStream() x
SoundPlayToMusicStreamNB() x
SoundPlayToSampleStream() x
SoundPriority x
SoundReallocMusic() x
SoundReallocMusicNote() x
SoundReleaseExclusive() x
SoundSampleDriverInfo() x
SoundStopMusic() x
SoundStopMusicNote() x
SoundStopMusicStream() x
SoundStreamDeltaTimeType x
SoundStreamEvents x
SoundStreamSize x
SoundSynthDriverInfo() x
Source
meaning in makefiles x
Source code
displaying x
displaying with Swat x
listing with Swat x
multiple source files x
overview x
src Tcl structure x
stepping through x
source code
viewing area in Swat x
Source code
viewing with Swat x
source Tcl command x
SP_... x x
SP_... (SoundPriority type) x
SP_... (StandardPath type) x
spawn Swat command x x
SpecHeight x
Special targets in makefiles x
SpecialFunctions x
Specific UI
creation x
Specific user interface
variant classes x
@specificUI x x
SpecSizeSpec x x
SpecSizeTypes x
SpecSocketDrError x
SpecSocketDrError values x
SpecWidth x
SpecWinSizePair x
SpecWinSizeSpec x
SpeedDialGetNumber() x
SpeedDialGetRecord() x
SpeedDialGetSlot() x
SpeedDialSetRecord() x
SpellControlClass x
SpellFeatures x
SpellToolboxFeatures x
SPF_HIGH_PRIORITY x
Splines x
notification lists x
GrDrawSpline() x
SpollCreatePaperSize() x
spool library x
Spool Library
notification lists x
SPOOL_... x
SpoolConvertPaperSize() x x
SpoolCreatePaperSize() x
SpoolCreatePrinter() x x
SpoolCreateSpoolFile() x
SpoolDelayJob() x x
SpoolDeletePaperSize() x x
SpoolDeletePrinter() x x
SpoolDelJob() x x
Spooler x
SpoolError x
SpoolFileName x
SpoolGetDefaultPageSizeInfo() x x
SpoolGetDefaultPrinter() x x
SpoolGetNumPaperSizes() x x
SpoolGetNumPrinters() x x
SpoolGetPaperSize() x
SpoolGetPaperSizeOrder() x x
SpoolGetPaperString() x x
SpoolGetPrinterInfo() x
SpoolGetPrinterString() x x
SpoolHurryJob() x x
SpoolInfo() x
SpoolInfoJobParams x
SpoolInfoParams x
SpoolInfoQueueParams x
SpoolInfoType x
SpoolModifyPriority() x x
SpoolOpStatus x
SpoolSetDefaultPageSizeInfo() x x
SpoolSetDefaultPrinter() x x
SpoolSetDocSize() x x
SpoolSetPaperSizeOrder() x x
SpoolTimeStruct x
SpoolVerifyAction x
SpoolVerifyPrinterPort() x x
SpreadsheetDrawFlags x
SpreadsheetInitFile() x
SpreadsheetInitFileData x
Spreadsheets
notification lists x
examining with Swat x x
printing in Swat x
with impex x
SPT_... (SetPalType type) x
SPT_... (SysProcessorType type) x
SQRT parse library internal function x
src Tcl structure x
SRC_DEFAULT_FEATURES x
SRC_DEFAULT_TOOLBOX_FEATURES x
SRCFeatures x
SRCToolboxFeatures x
srcwin Swat command x x
in Tutorial example x
SRF_... x
SRF_URGENT x
SS_... (ShiftState) x x
SS_... (SysStats struct) x
SSDE_... x
SSDTT_... x
SSE_... x
SSEH_... x
SSF_... x
SSF_URGENT x
SSI_... x
SSS_... (SoundStreamSize type) x
SSS_... (SpecSizeSpec flags) x
SST_... (SpecSizeType type) x x
SST_... (StandardSoundType type) x
SST_... (SysShutdownType) x
sstep Swat command x x
in Tutorial example x
ST_... x
@stack x x
Stack
changing frames x x
checking x
examining x
examining backtrace x
examining data on x
examining with Swat x
Floating-Point x
STACK
load type in .gp file x
Stack
thread stack size x x
stack .gp file keyword x
Standard paths x
examining with Swat x
FCNT_ADD_SP_DIRECTORY x
FCNT_DELETE_SP_DIRECTORY x
StandardDialog1ResponseTriggerTable x
StandardDialog2ResponseTriggerTable x
StandardDialog3ResponseTriggerTable x
StandardDialog4ResponseTriggerTable x
StandardDialogBoxType x
StandardDialogOptrParams x
StandardDialogParams x
StandardDialogResponseTriggerEntry x
StandardPath x x
associated FileChangeNotificationType types x
StandardSoundType x x
@start x x
in Tutorial example x
State files x x
STCD_cellRef x
STD parse library internal function x
STD_... x
STD_LMEM_OBJECT_FLAGS x
STDP parse library internal function x
stdpaths Swat command x
step Swat command x
step-patient Swat command x
step-until Swat command x
Stepping through code x
Asm x
Assembly code x x x
assembly code x
C code x
finishing a frame x x
go Swat command x
swat events x x
STID_start x
STND_value x
STOD_operatorID x
stop Swat command x
in Tutorial example x
stop-catch Swat command x
stop-patient Swat command x
stream Swat command x
STREAM_... (StreamBlocker type) x
STREAM_... (StreamError type) x
STREAM_OPEN_... x x
STREAM_ROLES_... x
StreamBlocker x x
StreamClose() x x
StreamError x x
with ParallelOpen() x
with SerialOpen() x
with StreamRead() x
with StreamReadByte() x
with StreamWrite() x
StreamFlush() x x
StreamGetError() x x
StreamOpen() x x
StreamOpenFlags x x
StreamQuery() x x
StreamRead() x x
StreamReadByte() x x
StreamRoles x
with StreamGetError() x
with StreamQuery() x
with StreamSetError() x
Streams x x
StreamSetError() x x
StreamToken x
with StreamClose() x
with StreamGetError() x
with StreamOpen() x
with StreamRead() x
with StreamSetError() x
with StreamWrite() x
StreamWrite() x x
StreamWriteByte() x x
STRING parse library internal function x
string Tcl command x
Strings
case x
code pages x
comparing x x
size x
Strings in Swat
common prefix x
computing length x
explode Swat command x
Structures
accessing fields in Swat address expressions x
creating x
displaying size in Swat x
examining with Swat x
field Swat command x
formatting enumerated type values with Swat x
formatting Swat output x x x x x
STS_... x
STSD_... x
StyleChunkDesc x
StyleElementFlags x
StyleElementHeader x
StyleSheetElementHeader x
Suff_FindDeps() routine x
.SUFFIXES makefile special target x
SUM parse library internal function x
Sunday x
Superclass
sending messages to x
SupportedEnvelopeForamt x
SVA_... x
SW_... x
Swap drivers
standard path x
swap-only
resource type in .gp file x
Swapable memory x
geode resources x
Swat x
abbreviations x
address expressions x
address history x
attaching and detaching x
command completion x
command correction x
command line options x
in Tutorial example x
launching x
mouse navigation x
online help x x
SWAT.CFG file x
Tcl (Tool Command Language) x
.SWAT file x
SWAT.CFG file x
SWAT.RC file x
switch Swat command x
command reference x
sword x
SWSP_... x
SWSS_... x
SYD parse library internal function x
.sym files x
sym-default Swat command x
command reference x
Symbol files x
Symbol paths in Swat address expressions x
symbol Tcl structure x
in example x
symbolCompletion Swat variable x
SysConfigFlags x
SysDrawMask x x
sysfiles Swat command x
SysGetConfig() x x
SysGetDosEnvironment() x x
SysGetECLevel() x x
SysGetInfo() x x
SysGetInfoType x x
SysGetPenMode() x x
SysLocateFileInDosPath() x
SysMachineType x
SysNotify() x x
SysNotifyFlags x x
SysProcessorType x
SysRegisterScreen() x
SysSetECLevel() x x
SysSetExitFlags() x
SysShutdown() x x
SysShutdownType x x
SysStatistics() x x
SysStats x x
SysSwapInfo x
system
as geode type in .gp file x
System architecture x
SystemDrawMask x x
SystemHatch x x
systemobj Swat command x
SysUnlockBIOS() x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: T

:T makefile variable expansion x
TAB x
TabAttributes x
TabControlClass x
table Tcl structure x
TabLeader x
TabType x
TakeDownDialogViaUIThread() x
TAN parse library internal function x
TANH parse library internal function x
TAPEA_... x
TAPRA_... x
Target x x
.TARGET makefile local variable x
Target
accessing x
accessing hierarchy with Swat x
exclusive x
finding with Swat x
GA_TARGETABLE x
when making x
Target attributes in makefiles x
Target selections
notification list x
target Swat command x
TargetLevel x
targetobj Swat command x
TargetReference x
Targets
.TARGET local variable x
Targets in makefiles x
Task switch drivers x
standard path x
tbrk Swat command x
TC_DEFAULT_FEATURES x
TC_DEFAULT_TOOLBOX_FEATURES x
TCFeatures x
TCI_... x
Tcl
comments x
copyright information x
data structures x
debugging x x
defining procedures x x x
defining variables x
loading command files x
syntax x
tcl-debug Swat command x
TCP/IP
Socket domain x
TcpAccPntExtendedAddress x
TcpAccPntExtendedAddress structure x
TcpAccPntResolvedAddress x
TCPIP_GEOS x
TcpNonAccPntExtendedAddress x
TcpNonAccPntExtendedAddress structure x
TcpOnlyExtendedAddress x
TcpOnlyExtendedAddress structure x
TcpOnlyResolvedAddress x
TCS_... (TocCategoryStruct structure) x
TCToolboxFeatures x
TDAT_... x
TE_... (ThreadException type) x x x
TE_... (TokenEntry structure) x
TE_... (TransError type) x
TELNET_SERVER x
TEMP_GEN_CONTROL_INSTANCE x x
TEMP_GEN_DISPLAY_MENU_BAR_HIDDEN x
TEMP_GEN_TOOL_CONTROL_INSTANCE x
TEMP_PAGE_SIZE_CONTROL_MAX_DIMENSIONS x
TEMP_PRINT_COMPLETION_EVENT x
TEMP_PRINT_CONTROL_INSTANCE x
TEMP_TOOL_GROUP_HIGHLIGHT x
TempGenControlInstance x
TempGenToolControlInstance x x
TempPrintCompletionEventData x
TempPrintCtrlInstance x
TERM parse library internal function x
TestRectReturnType x x x
Text
notification lists x
displaying bytes as x
displaying text monikers with Swat x
displaying text objects with Swat x
monitoring line ripple activity with Swat x
monitoring undo activity with Swat x
objects x
rendering with graphics x
Text Attributes
notification lists x
Text Names
notification list x
Text rulers
notification list x
text-fixup Swat command x
TextAttr x
TextMode x
TextReference x x
TextReferenceBlock x
TextReferenceBlockChunk x
TextReferenceDBItem x
TextReferenceHugeArray x
TextReferencePointer x
TextReferenceSegmentChunk x
TextReferenceType x x
TextReferenceUnion x x
TextReferenceVMBlock x
TextRulerControlAttributes x
TextRulerControlClass x
TextSearchInHugeArray() x x
TextSearchInString() x x
TextStyle x
TextStyleControlClass x
TextStyleSheetControlClass x
TextTransferBlockHeader x
TF_NEED_RELOCATION x
TGI_PRIORITY() macro x
TGI_RECENT_CPU_USAGE() macro x
TGIT_... x x
thaw Swat command x
thousandsSeparator (LocalNumericFormat structure field) x
thousandsSeperator (LocalCurrencyFormat structure field) x
thread Tcl structure x
ThreadAllocSem() x x
ThreadAllocThreadLock() x x
ThreadAttachToQueue() x x
ThreadCreate() x x
ThreadDestroy() x x
ThreadException x x x
ThreadFreeSem() x x
ThreadFreeThreadLock() x x
ThreadGetError() x
ThreadGetInfo() x x
ThreadGetInfoType x x x
ThreadGrabThreadLock() x x
ThreadHandle x
ThreadHandleException() x x
ThreadLockHandle x
ThreadModify() x x
ThreadModifyFlags x
threadname Swat command x
ThreadPrivAlloc() x
ThreadPrivFree() x
ThreadPSem() x x
ThreadPTimedSem() x x
ThreadReleaseThreadLock() x x
Threads x x x
blocking x
breakpoints in not yet created x
creating event threads x
creation and destruction messages x
current within patient x
displaying name in Swat x
displaying status with Swat x
displaying with Swat x
event queues x
examining registers x
examining with Swat x
finding handle x
finding priority x
freezing x
handles x
handles, checking x
in Tutorial example x x
lock handles x
monitoring with Swat x
specific breakpoints x
switching x x
switching in Tutorial example x
thawing x
thread Tcl structure x
waking up with Swat x x
threadstat Swat command x
ThreadVSem() x x
ThreeWordArgs x
Thursday x
TI_... (ToolboxInfo) x
TicTac sample application x
TIF_NORMAL x
Time
formatting x
Math Conversions x
timebrk Swat command x
TIMER_... x
TimerCompressedDate x
TimerDateAndTime x
TimerGetCount() x x
TimerGetDateAndTime() x x
TimerGetFileDateTime() x x
TimerHandle x
Timers x
displaying with Swat x
handles x
interrupt x
printing with Swat x
TimerSetDateAndTime() x x
TimerSleep() x x
TimerStart() x x
TimerStop() x x
TimerType x x
Timing with Swat x x
timingProcessor Swat variable x
Tinkle bell x
TIP_... (TocItemPath structure) x
TIR_... x
TitledGlyphClass x
TL_... x
TM_e... x
tmem Swat command x
TMF_... x
TMI_... x
tmp files x
TNAPEA_... x
TO_... x x
TO_VIS_PARENT x
TocCategoryStruct structure x
TocDBLock() x x
TocDBLockGetRef() x x
TocFindCategory() x x
TocGetFileHandle() x x
TocItemPath structure x
TocNameArrayAdd() x x
TocNameArrayFind() x x
TocNameArrayGetElement() x x
TocSortedNameArrayAdd() x x
TocSortedNameArrayFind() x x
TocUpdateAddCallback() prototype x
TocUpdateCategory() x x
TocUpdateCategoryFlags structure x
TocUpdateCategoryParams() x
TOEA_... x x
ToggleState x x
token .gp file keyword
in Tutorial example x
Token databases x
TOKEN_CHARS() macro x
TOKEN_CHARS_LENGTH x
TOKEN_DA.000 file x
TOKEN_LENGTH x
TokenChars x
tokenchars .gp file keyword x
in Tutorial example x x
TokenCloseLocalTokenDB() x x
TokenDBItem x
TokenDefineToken() x x
TokenEntry x
TokenFlags x
TokenGetTokenInfo() x x
tokenid .gp file keyword x
in Tutorial example x
TokenListTokens() x x
TokenListTokensCountFromDWord() x
TokenListTokensHandleFromDWord() x
TokenLoadMonikerBlock() x
TokenLoadMonikerBuffer() x x
TokenLoadMonikerChunk() x x
TokenLoadTokenBlock() x x
TokenLoadTokenBuffer() x x
TokenLoadTokenChunk() x x
TokenLockMonikerBlock() x
TokenLockTokenMoniker() x x
TokenLookupMoniker() x x
TokenMonikerInfo x
TokenOpenLocalTokenDB() x x
TokenRangeFlags x
TokenRemoveToken() x x
Tokens
database x
icons x
in Parameter file x
TokenUnlockTokenMoniker() x x
tool boxes x
ToolboxInfo x
tools x
top-level Swat command x
topMargin (DocumentSize structure) x
topMargin (MarginDimensions structure field) x
TPCED_... x
TPCI_... x
TR_... x
TR_... (TargetReference structure) x
TR_... (TextReference structure) x
TrackScrollingParams x
TransError x
transError (TransErrorInfo structure field) x
TransErrorInfo x
Transfer VM file x
TransferBlockID x x
extracting file handle from x
Transformation rules in makefiles x
TransMatrix x x
Traveling objects x
TravelingObjectReference x
TravelOption x x x x
with input heirarchies x
TRB_handle x
TRBC_ref x
TRCC_DEFAULT_FEATURES x
TRCC_DEFAULT_TOOLBOX_FEATURES x
TRCCFeatures x
TRCCToolboxFeatures x
TRDBI_... x
Trees
displaying generic ancestors x
displaying visual ancestors with Swat x
displaying visual, with Swat x
Displaying window, with Swat x
generic x
generic:accessing top of, with Swat x
generic, displaying with Swat x
visual x
TRF_... x
TRHA_... x
TRIM parse library internal function x
TRP_pointer x
TRRT_... x
TRSC_... x
TRT_... x
TRT_... (TextReferenceType type) x
TRU_... x
TRU_... (TextReferenceUnion) x
TRUE x x
TRUE parse library internal function x
TRUNC parse library internal function x
TRVMB_... x
TS_... (TextStyle) x
TS_... (ToggleState) x x
TSC_DEFAULT_FEATURES x
TSC_DEFAULT_TOOLBOX_FEATURES x
TSCFeatures x
TSCToolboxFeatures x
TSP_... x
TUCF_... (TocUpdateCategoryFlags structure) x
TUCP_... x
Tuesday x
tundocalls Swat command x
Tutorial x
twalk Swat command x
TwoLevelTriggerClass
overview x
TwoWordArgs x
type .gp file keyword x
in Tutorial example x x
type Tcl structure x
in example x
TypeFromFormatID() x
TypeFromFormatID() macro x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: U

Ctrl-u Swat navigation x
UADF_... x
UADP_... x
UADT_... x
UADU_... x
UADVMC_... x
UAS_... x
UATH_... x
UHTAF_... x
UI Thread
in Tutorial example x
ui-object
resource type in .gp file x x
UIFA_... x x
UIFunctionsActive x x
UIIL_... x
UIInterfaceLevel x x
unalias Swat command x
unassemble Swat command x
unbind-key Swat command x
undebug Swat command x
#undef makefile directive x
UnderlinedGenTextClass
overview x
UnderlinedVisTextClass
overview x
Undo x x
notification list x
MSG_META_UNDO_... x
UndoActionDataFlags x
UndoActionDataPtr x
UndoActionDataType x
UndoActionDataUnion x
UndoActionDataVMChain x
UndoActionStruct x
Unions in Swat
creating x
UNLOCK_ON_EOS (EndOfSongFlags flag) x
up Swat command x
uplevel Tcl command x
UPPER parse library internal function x
.USE makefile attribute x
User interface x
User notes x x
UserAddAutoExec() x
UserAllocObjBlock x
UserCreateDialog() x x
UserCreateInkDestinationInfo() x x
UserDestroyDialog() x x
UserDoDialog() x x
UserGetInterfaceLevel() x
UserLoadApplication() x x
usernotes .gp file keyword x
UserRemoveAutoExec() x
UserStandardDialog() x x
UserStandardDialogOptr() x
UserStandardSound() x
UserStopStandardSound() x
@uses x x
uses-coproc
as geode type in .gp file x
UtilAsciiToHex32() x
UtilAsciiToHexError x x
UtilHex32ToAscii() x
UtilHexToAsciiFlags x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: V

VA_... x
VALUE parse library internal function x
value Swat command x
var Tcl command x
@vardata x x
@vardataAlias x x
VarDataCHandler x
VarDataCHandler tables x
VarDataEntry x
VarDataFlags x x
VarDataFlagsPtr() macro x
VardataKey x
VarDataSizePtr() macro x
VarDataTypePtr() macro x
Variable data x
@vardata keyword x
Goc syntax x x
with varying amounts of extra data x
Variables
assign Swat command x
naming conventions x
Variables in makefiles x
variant (class flag) x
Variant classes x x
@uses keyword x
class flag x x
detecting x
resolving x x x
VarObjRelocation x
varwin Swat command x
VC_... x
VC_IS... x
VCGA_... x
VCGCNLT_TARGET_NOTIFY_TEXT_PARA_ATTR_CHANGE x
VChar x
VCI_comp x x
VCI_gadgetExcl x
VCI_gadgetExcl;Input
gadget exclusive;gadget exclusive x
VCI_geoAttrs x x
VCI_geoDimensionAttrs x x
VCI_window x x
VCNA_... x
VCNI_activeMouseGrab x
VCNI_attrs x x
VCNI_docOrigin x x
VCNI_focusExcl x
VCNI_holdUpInputCount x
VCNI_holdUpInputFlags x
VCNI_holdUpInputQueue x
VCNI_impliedMouseGrab x
VCNI_kbdGrab x
VCNI_postPassiveMouseGrabList x
VCNI_prePassiveMouseGrabList x
VCNI_scaleFactor x x
VCNI_targetExcl x
VCNI_view x x
VCNI_viewHeight x x
VCNI_viewWidth x x
VCNI_window x x
VDE_... x
VDF_... x
VDF_EXTRA_DATA x
VDF_SAVE_TO_STATE x
VGA_NOTIFY_GEOMETRY_VALID x
VI_attrs x x
VI_bounds x x
Viewing with vistree in Swat x
VI_geoAttrs x x
VI_link x x
VI_optFlags x x
VI_specAttrs x x
VI_typeFlags x x x
Video drivers x x
blitting x
screen dumps x
standard path x
view Swat command x
view-default Swat command x
Views x
ViewTargetInfo x
VIFGF_... x
VIFGT_... x
Virtual memory x
error checking x x
examining with Swat x x
handles x
handles in Swat address expressions x
handles, checking x x
notification x
printing huge arrays x
VirtualSerialClose() x
VirtualSerialFlush() x
VirtualSerialOpen() x
VirtualSerialRead() x
VirtualSerialReadByte() x
VirtualSerialSendDTR() x
VirtualSerialSetFlowControl() x
VirtualSerialSetFormat() x
VirtualSerialSetNotify() x
VirtualSerialWrite() x
VirtualSerialWriteByte() x
VIS_TEXT_INITIAL_CHAR_ATTR x
VisAddRectFlags x
VisAttrs x
VisCallChildrenInBoundsFrame x
@visChildren (messaging shortcut) x x
VisClass x
displaying x
drawing x
geometry x
in Tutorial exercise x
input x
instance data x
overview x
redrawing;Redrawing:visible objects;Drawing:causing redraws x
tree management x x
window handle x
VisCompClass x
overview x
VisCompGeoAttrs x
VisCompGeoDimensionAttrs x
VisContent
interaction with document x
internal instance data x
VisContentAttrs x
VisContentClass x
in Tutorial exercise x
overview x
VisGeoAttrs x
Visible Class Tree x
VisInputFlowGrabFlags x x
VisInputFlowGrabType x x
@visMoniker
in Tutorial example x
@visMoniker x x
in Tutorial example x
VisMonikerDataType x x
VisMonikerSearchFlags x x
VisMonikerSourceType x x
VisMouseGrab x
VisObjectHandlesInkReply x
VisObjectHandlesInkReply() x
VisOptFlags x
@visParent (messaging shortcut) x x
VisRulerAttributes x
VisRulerClass x
VisRulerConstrainStrategy x
VisRulerNotifyGuideChangeBlockHeader x
VisRulerType x
VisTextBorderFlags x
VisTextCharAttr x
VisTextClass x
VisTextDefaultCharAttr x
VisTextDefaultDefaultTab x
VisTextDefaultParaAttr x
VisTextDefaultSize x
VisTextDropCapInfo x
VisTextExtendedStyles x
VisTextFeatures x
VisTextGraphicCompressGraphic() x
VisTextHyphenationInfo x
VisTextKeepInfo x
VisTextMaxParaAttr x
VisTextParaAttr x
VisTextParaAttrAttributes x
VisTextParaNumberType x
VisTextRange x
VisTextStates x
VisTextStorageFlags x
VisTextVariableType x
VisTravelOption x x x x
vistree Swat command x x
VisTypeFlags x
Visual monikers
goc syntax x
Visual objects x
displaying ancestors with Swat x
displaying instance data x
displaying tree with Swat x x
Visual updates
MSG_GEN_UPDATE_VISUAL x
Visual upward queries x
VisUpdateMode x x
VM_... x
VMA_... x x
VMAccessFlags x x
VMAF_... x x
VMAlloc() x x
VMAllocLMem() x x
VMAttach() x x
VMAttributes x x
VMBlockHandle x
VMC_next x
VMChain x x
VMCHAIN_GET_VM_BLOCK() macro x
VMCHAIN_IS_DBITEM() macro x
VMCHAIN_MAKE_FROM_VM_BLOCK() macro x
VMChainLink x
VMChainTree x x
VMCheckForModifications() x
VMClose() x x
VMCompareVMChains() x x
VMCopyVMBlock() x x
VMCopyVMChain() x x
VMCT_... x x
VMDetach() x x
VMDirty() x x
VMFileHandle x
VMFind() x x
VMFree() x
VMFreeVMChain() x x
VMGetAttributes() x x
VMGetDirtyState() x x
VMGetMapBlock() x x
VMGrabExclusive() x x
VMInfo() x x
VMInfoStruct x x
VMLock() x x x x
VMMemBlockToVMBlock() x x
VMModifyUserID() x x
VMO_... x x
VMO_... (VMOpenType type) x
VMO_... (VMOperation type) x
VMOpen() x x
VMOpenType x x
VMOperation x x
VMPreserveBlocksHandle() x x
VMReleaseExclusive() x x
VMRelocType x x
VMRevert() x x
VMRT_... x
VMSave() x x
VMSaveAs() x x
VMSERV_... x
VMSetAttributes() x x
VMSetExecThread() x
VMSetMapBlock() x x
VMSetReloc() x x
VMSF_... x
VMStartExclusiveReturnValue x
VMStartExlusiveReturnValue x
VMStyle x
VMUnlock() x x
VMUpdate() x x
VMVMBlockToMemBlock() x x
VOF_... x
VOF_GEOMETRY_INVALID x
VOF_WINDOW_INVALID x
VOLUME_BUFFER_SIZE x
VolumeName x
Volumes (file system) x
finding number of x
VOR_... x
VRA_... x
VRCS_... x
VRI_... (VisRulerClass instance data) x
VRI_constrainStrategy x
VRI_desiredSize x
VRI_grid x
VRI_guideArray x
VRI_guideInfluence x
VRI_invalOD x
VRI_minIncrement x
VRI_mouseMark x
VRI_origin x
VRI_reference x
VRI_rulerAttrs x
VRI_slave x
VRI_transformGState x
VRI_type x
VRI_vectorSlope x
VRI_window x
VRNGCBH_... x
VRT_... x x
VTCA_... x
VTDCA_... x
VTDF_... x
VTDPA_... x
VTF_... x
VTI_... (ViewTargetInfo) x
VTI_... (VisText Instance) x
VTPA_... x
VTR_... x
VTSF_... x
VUM_... x x
vup Swat command x
command reference x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: W

WAAH_... x
wait Swat command x
waitForPatient Swat command x
wakeup Swat command x
wakeup-thread Swat command x
WBF_... x
WBFixed x x
WC_... x
wchar x
wclear Swat command x
wcreate Swat command x
wdelete Swat command x
Wednesday x
whatat Swat command x
Tcl source x
whatis Swat command x
where Swat command
in Tutorial example x x
WHOIS x
why Swat command x
command reference x
width (DocumentSize structure) x
WidthJustification x
WIF_... x
WildCard x
WIN_PRIO_... x
WinAckUpdate() x
WinApplyRotation() x
WinApplyScale() x
WinApplyTransform() x
WinApplyTranslation() x
WinApplyTranslationDWord() x
WinChangeAck() x
WinChangePriority() x
WinClose() x
WinColorFlags x
WinDecRefCount() x
WINDING (RegionFillRule type) x x
Window group x
WindowHandle x
Windows
accessing topmost with Swat x
accessing window with grab x
clipping region x
displaying,tree with Swat x
finding window groups x
handle with visible objects x
handles x
handles,checking x
imported messages x
maximizing x
minimizing x
monitoring with Swat x
notification list x
overview x
position x
size x
specific ui operations x
under mouse x
updating x
VTF_IS_WINDOW x
WinEnsureChangeNotification() x
WinGeodeGetInputObj() x
WinGeodeGetParentObj() x
WinGeodeSetActiveWin() x
WinGeodeSetInputObj() x
WinGeodeSetParentObj() x
WinGeodeSetPtrImage() x
WinGetInfo() x
WinGetTransform() x
WinGetWinScreenBounds() x
WinGrabChange() x
WinInfoType x
WinInvalFlag x
WinInvalReg() x
WinMove() x
WinOpen() x
WinPassFlags x
WinPriority x
WinReleaseChange() x
WinResize() x
WinScroll() x
WinSetInfo() x
WinSetNullTransform() x
WinSetPtrImage() x
WinSetTransform() x
WinSuspendUpdate() x
WinTransform() x
WinTransformDWord() x
wintree Swat command x
WinUnSuspendUpdate() x
WinUntransform() x
WinUntransformDWord() x
winverse Swat command x
WIT_... x
WJ_... x
wmove Swat command x
word x
WordAndAHalf x
WordFlags x
printing flag fields x
words Swat command x
command reference x
Working Directory
in Tutorial example x x
WPF_... x
wpop Swat command x
wpush Swat command x
wrefresh Swat command x
wtop Swat command x
WWF_... x
WWFixed x x x x
Formatting as Strings x
WWFixedAsDWord x x
WWFixedToFrac() macro x
WWFixedToInt x
WWFixedToInt() macro x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: X

:X makefile variable expansion x
XMS x
XYO_... x
XYOffset x
XYS_... x
XYSize x x
XYValueAsDWord x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Index: Y

Ctrl-y Swat navigation x
[symbols] A B C D E F G H I J K L M N O P Q R S T U V W X Y


GEOS SDK Message List

GEOS SDK: List of Messages

Choose the name of a class below to visit a page with links to messages of that class

  • CalendarClass
  • ColorSelectorClass
  • ContactListClass
  • ExportControlClass
  • FilterListClass
  • FilterTextClass
  • FloatFormatClass
  • FoamSubApplicationClass
  • GenApplicationClass
  • GenBooleanClass
  • GenBooleanGroupClass
  • GenClass
  • GenContentClass
  • GenControlClass
  • GenDisplayClass
  • GenDisplayControlClass
  • GenDisplayGroupClass
  • GenDocumentClass
  • GenDocumentControlClass
  • GenDocumentGroupClass
  • GenDynamicListClass
  • GenFileSelectorClass
  • GenInteractionClass
  • GenItemClass
  • GenItemGroupClass
  • GenPrimaryClass
  • GenProcessClass
  • GenTextClass
  • GenToolGroupClass
  • GenTriggerClass
  • GenValueClass
  • GenViewClass
  • GenViewControlClass
  • HelpControlClass
  • ImportControlClass
  • InkClass
  • MetaClass
  • PageSizeControlClass
  • PrefBooleanGroupClass
  • PrefClass
  • PrefDialogClass
  • PrefDynamicListClass
  • PrefItemGroupClass
  • PrefStringItemClass
  • PrefTocListClass
  • PrefValueClass
  • PrintControlClass
  • ProcessClass
  • SocketAddressControlClass
  • VisClass
  • VisCompClass
  • VisContentClass
  • VisRulerClass
  • VisTextClass
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: CalendarClass

GEOS SDK: List of Messages for CalendarClass

MSG_CALENDAR_ADD_EVENT x
MSG_CALENDAR_CHECK_IF_EVENT_EXISTS x
MSG_CALENDAR_DELETE_EVENT_BY_ID x
MSG_CALENDAR_GET_EVENT_BY_ID x
MSG_CALENDAR_MODIFY_EVENT_BY_ID x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: ColorSelectorClass

GEOS SDK: List of Messages for ColorSelectorClass

MSG_COLOR_SELECTOR_GET_COLOR x
MSG_COLOR_SELECTOR_GET_DRAW_MASK x
MSG_COLOR_SELECTOR_GET_PATTERN x
MSG_COLOR_SELECTOR_SET_COLOR x
MSG_COLOR_SELECTOR_SET_DRAW_MASK x
MSG_COLOR_SELECTOR_SET_PATTERN x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: ContactListClass

GEOS SDK: List of Messages for ContactListClass

MSG_CONTACT_LIST_PRESERVE_SEARCH_ON_NEXT_LOST_FOCUS x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: ExportControlClass

GEOS SDK: List of Messages for ExportControlClass

MSG_EXPORT_CONTROL_GET_ATTRS x
MSG_EXPORT_CONTROL_GET_DATA_CLASSES x
MSG_EXPORT_CONTROL_SET_ACTION x
MSG_EXPORT_CONTROL_SET_ATTRS x
MSG_EXPORT_CONTROL_SET_DATA_CLASSES x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: FilterListClass

GEOS SDK: List of Messages for FilterListClass

MSG_FILTER_LIST_GET_FOCUS_ITEM x
MSG_FILTER_LIST_GET_ITEM_OPTR x
MSG_FILTER_LIST_GET_MULTIPLE_SELECTIONS x
MSG_FILTER_LIST_GET_SELECTION x
MSG_FILTER_LIST_INITIALIZE x
MSG_FILTER_LIST_IS_ITEM_SELECTED x
MSG_FILTER_LIST_MAKE_ITEM_VISIBLE x
MSG_FILTER_LIST_MAP_PHYSICAL_TO_APP x
MSG_FILTER_LIST_REPLACE_ITEM_MONIKER x
MSG_FILTER_LIST_REPLACE_ITEM_MONIKER_OPTR x
MSG_FILTER_LIST_REPLACE_ITEM_TEXT x
MSG_FILTER_LIST_SET_FOCUS_ITEM x
MSG_FILTER_LIST_SET_ITEM_STATE x
MSG_FILTER_LIST_SET_MULTIPLE_SELECTIONS x
MSG_FILTER_LIST_SET_SINGLE_SELECTION x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: FilterTextClass

GEOS SDK: List of Messages for FilterTextClass

MSG_FILTER_TEXT_RESET x
MSG_FILTER_TEXT_RESET_TEXT_IF_NOT_FOCUSED x
MSG_FILTER_TEXT_SEND_FILTER_MSG x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: FloatFormatClass

GEOS SDK: List of Messages for FloatFormatClass

MSG_FLOAT_CTRL_FORMAT_APPLY x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: FoamSubApplicationClass

GEOS SDK: List of Messages for FoamSubApplicationClass

MSG_FSA_RETURN_TO_LAUNCHER
in Tutorial example x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenApplicationClass

GEOS SDK: List of Messages for GenApplicationClass

MSG_GEN_APPLICATION_ACCEPT_INPUT x
MSG_GEN_APPLICATION_BRING_WINDOW_TO_TOP x
MSG_GEN_APPLICATION_BUILD_STANDARD_DIALOG x
MSG_GEN_APPLICATION_DETACH_PENDING x
MSG_GEN_APPLICATION_DO_STANDARD_DIALOG x
MSG_GEN_APPLICATION_FIND_MONIKER x
MSG_GEN_APPLICATION_GET_APP_FEATURES x
MSG_GEN_APPLICATION_GET_APP_INSTANCE_REFERENCE x
MSG_GEN_APPLICATION_GET_APP_MODE_MESSAGE x
MSG_GEN_APPLICATION_GET_ATTACH_FLAGS x
MSG_GEN_APPLICATION_GET_DISPLAY_SCHEME x
MSG_GEN_APPLICATION_GET_GCN_LIST_OF_LISTS x
MSG_GEN_APPLICATION_GET_LAUNCH_FLAGS x
MSG_GEN_APPLICATION_GET_MEASUREMENT_TYPE x
MSG_GEN_APPLICATION_GET_STATE x
MSG_GEN_APPLICATION_HOLD_UP_INPUT x
MSG_GEN_APPLICATION_IGNORE_INPUT x
MSG_GEN_APPLICATION_INITIATE_UI_QUIT x
MSG_GEN_APPLICATION_INK_QUERY_REPLY x x
MSG_GEN_APPLICATION_INSTALL_TOKEN x
MSG_GEN_APPLICATION_LOWER_WINDOW_TO_BOTTOM x
MSG_GEN_APPLICATION_MARK_APP_COMPLETELY_BUSY x
MSG_GEN_APPLICATION_MARK_APP_NOT_COMPLETELY_BUSY x
MSG_GEN_APPLICATION_MARK_BUSY x
MSG_GEN_APPLICATION_MARK_NOT_BUSY x
MSG_GEN_APPLICATION_NOTIFY_MODAL_WIN_CHANGE x
MSG_GEN_APPLICATION_OPEN_COMPLETE x
MSG_GEN_APPLICATION_QUERY_UI x
MSG_GEN_APPLICATION_RESUME_INPUT x
MSG_GEN_APPLICATION_SEND_APP_INSTANCE_REFERENCE_TO_FIELD x
MSG_GEN_APPLICATION_SET_APP_FEATURES x
MSG_GEN_APPLICATION_SET_APP_INSTANCE_REFERENCE x
MSG_GEN_APPLICATION_SET_APP_MODE_MESSAGE x
MSG_GEN_APPLICATION_SET_ATTACHED_TO_STATE_FILE x
MSG_GEN_APPLICATION_SET_MEASUREMENT_TYPE x
MSG_GEN_APPLICATION_SET_NOT_ATTACHED_TO_STATE_FILE x
MSG_GEN_APPLICATION_SET_NOT_USER_INTERACTABLE x
MSG_GEN_APPLICATION_SET_TASK_ENTRY_MONIKER x
MSG_GEN_APPLICATION_SET_USER_INTERACTABLE x
MSG_GEN_APPLICATION_TEST_WIN_INTERACTABILITY x
MSG_GEN_APPLICATION_TOGGLE_CURSOR x
MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenBooleanClass

GEOS SDK: List of Messages for GenBooleanClass

MSG_GEN_BOOLEAN_GET_IDENTIFIER x
MSG_GEN_BOOLEAN_SET_IDENTIFIER x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenBooleanGroupClass

GEOS SDK: List of Messages for GenBooleanGroupClass

MSG_GEN_BOOLEAN_GROUP_GET_APPLY_MSG x
MSG_GEN_BOOLEAN_GROUP_GET_BOOLEAN_OPTR x
MSG_GEN_BOOLEAN_GROUP_GET_DESTINATION x
MSG_GEN_BOOLEAN_GROUP_GET_INDETERMINATE_BOOLEANS x
MSG_GEN_BOOLEAN_GROUP_GET_MODIFIED_BOOLEANS x
MSG_GEN_BOOLEAN_GROUP_GET_SELECTED_BOOLEANS x
MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_INDETERMINATE x
MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_MODIFIED x
MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_SELECTED x
MSG_GEN_BOOLEAN_GROUP_MAKE_BOOLEAN_VISIBLE x
MSG_GEN_BOOLEAN_GROUP_SEND_STATUS_MSG x
MSG_GEN_BOOLEAN_GROUP_SET_APPLY_MSG x
MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_INDETERMINATE_STATE x
MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_MODIFIED_STATE x
MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_STATE x
MSG_GEN_BOOLEAN_GROUP_SET_DESTINATION x
MSG_GEN_BOOLEAN_GROUP_SET_GROUP_MODIFIED_STATE x
MSG_GEN_BOOLEAN_GROUP_SET_GROUP_STATE x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenClass

GEOS SDK: List of Messages for GenClass

MSG_GEN_ACTIVATE x
MSG_GEN_ACTIVATE_INTERACTION_DEFAULT x
MSG_GEN_ADD_CHILD x
MSG_GEN_ADD_CHILD_UPWARD_LINK_ONLY x
MSG_GEN_ADD_GEOMETRY_HINT x
MSG_GEN_APPLY x
MSG_GEN_BRANCH_REPLACE_OUTPUT_OPTR_CONSTANT x
MSG_GEN_BRANCH_REPLACE_PARAMS x
MSG_GEN_BRING_TO_TOP x
MSG_GEN_CALL_APPLICATION x
MSG_GEN_CALL_PARENT x
MSG_GEN_CALL_SYSTEM x
MSG_GEN_CHANGE_ACCELERATOR x
MSG_GEN_CHECK_IF_FULLY_ENABLED x
MSG_GEN_CHECK_IF_FULLY_USABLE x
MSG_GEN_COPY_TREE x x
MSG_GEN_COUNT_CHILDREN x
MSG_GEN_CREATE_VIS_MONIKER x
MSG_GEN_DESTROY x
MSG_GEN_DESTROY_AND_FREE_BLOCK x
MSG_GEN_DRAW_MONIKER x
MSG_GEN_FIND_CHILD x
MSG_GEN_FIND_CHILD_AT_POSITION x
MSG_GEN_FIND_MONIKER x
MSG_GEN_FIND_OBJECT_WITH_TEXT_MONIKER x
MSG_GEN_FIND_PARENT x
MSG_GEN_GET_ATTRIBUTES x
MSG_GEN_GET_ENABLED x
MSG_GEN_GET_FIXED_SIZE x
MSG_GEN_GET_INITIAL_SIZE x
MSG_GEN_GET_KBD_ACCLERATOR x
MSG_GEN_GET_MAXIMUM_SIZE x
MSG_GEN_GET_MINIMUM_SIZE x
MSG_GEN_GET_MONIKER_POS x
MSG_GEN_GET_MONIKER_SIZE x
MSG_GEN_GET_USABLE x
MSG_GEN_GET_VIS_MONIKER x
MSG_GEN_GUP_CALL_OBJECT_OF_CLASS x
MSG_GEN_GUP_FIND_OBJECT_OF_CLASS x
MSG_GEN_GUP_FINISH_QUIT x
MSG_GEN_GUP_INTERACTION_COMMAND x
MSG_GEN_GUP_QUERY x
MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS x
MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS x
MSG_GEN_LOAD_OPTIONS x
in Preferences modules x
MSG_GEN_LOWER_TO_BOTTOM x
MSG_GEN_MAKE_APPLYABLE x
MSG_GEN_MAKE_NOT_APPLYABLE x
MSG_GEN_MOVE_CHILD x
MSG_GEN_NAVIGATE_TO_NEXT_FIELD x
MSG_GEN_NAVIGATE_TO_PREVIOUS_FIELD x
MSG_GEN_NAVIGATION_QUERY x
MSG_GEN_OUTPUT_ACTION x x
MSG_GEN_PATH_GET x
MSG_GEN_PATH_GET_BLOCK x
MSG_GEN_PATH_GET_DISK_HANDLE x
MSG_GEN_PATH_SET x
MSG_GEN_POST_APPLY x
MSG_GEN_PRE_APPLY x
MSG_GEN_QUERY_NEED_TO_BE_ON_ACTIVE_LIST x
MSG_GEN_RELOC_MONIKER_LIST x
MSG_GEN_REMOVE x
MSG_GEN_REMOVE_CHILD x
MSG_GEN_REMOVE_GEOMETRY_HINT x
MSG_GEN_REPLACE_VIS_MONIKER x
MSG_GEN_REPLACE_VIS_MONIKER_OPTR x
MSG_GEN_REPLACE_VIS_MONIKER_TEXT x
MSG_GEN_RESET x
in Preferences modules x
MSG_GEN_RESET_TO_INITIAL_SIZE x
MSG_GEN_SAVE_OPTIONS x
in Preferences modules x
MSG_GEN_SEND_TO_CHILDREN x
MSG_GEN_SEND_TO_PARENT x
MSG_GEN_SEND_TO_PROCESS x
MSG_GEN_SET_ATTRS x
MSG_GEN_SET_ENABLED x
MSG_GEN_SET_FIXED_SIZE x
MSG_GEN_SET_INITIAL_SIZE x
MSG_GEN_SET_KBD_ACCELERATOR x
MSG_GEN_SET_KBD_OVERRIDE x
MSG_GEN_SET_KBD_POSITION x
MSG_GEN_SET_MAXIMUM_SIZE x
MSG_GEN_SET_MINIMUM_SIZE x
MSG_GEN_SET_NOT_ENABLED x
MSG_GEN_SET_NOT_USABLE x
MSG_GEN_SET_USABLE x
MSG_GEN_SET_WIN_CONSTRAIN x
MSG_GEN_SET_WIN_POSITION x
MSG_GEN_SET_WIN_SIZE x
MSG_GEN_UPDATE_VISUAL x
MSG_GEN_USE_VIS_MONIKER x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenContentClass

GEOS SDK: List of Messages for GenContentClass

MSG_GEN_CONTENT_GET_ATTRS x
MSG_GEN_CONTENT_SET_ATTRS x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenControlClass

GEOS SDK: List of Messages for GenControlClass

MSG_GEN_CONTROL_ADD_APP_TOOLBOX_UI x
MSG_GEN_CONTROL_ADD_APP_UI x
MSG_GEN_CONTROL_ADD_FEATURE x
MSG_GEN_CONTROL_ADD_TOOLBOX_FEATURE x
MSG_GEN_CONTROL_ADD_TO_GCN_LISTS x
MSG_GEN_CONTROL_DESTROY_TOOLBOX_UI x
MSG_GEN_CONTROL_DESTROY_UI x
MSG_GEN_CONTROL_ENABLE_DISABLE x
MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI x
MSG_GEN_CONTROL_GENERATE_UI x
MSG_GEN_CONTROL_GET_INFO x x
MSG_GEN_CONTROL_GET_NORMAL_FEATURES x
MSG_GEN_CONTROL_GET_TOOLBOX_FEATURES x
MSG_GEN_CONTROL_NOTIFY_INTERACTABLE x
MSG_GEN_CONTROL_NOTIFY_NOT_INTERACTABLE x
MSG_GEN_CONTROL_REBUILD_NORMAL_UI x
MSG_GEN_CONTROL_REBUILD_TOOLBOX_UI x
MSG_GEN_CONTROL_REMOVE_FEATURE x
MSG_GEN_CONTROL_REMOVE_FROM_GCN_LISTS x
MSG_GEN_CONTROL_REMOVE_TOOLBOX_FEATURE x
MSG_GEN_CONTROL_SCAN_FEATURE_HINTS x
MSG_GEN_CONTROL_UNBUILD_NORMAL_UI_IF_POSSIBLE x
MSG_GEN_CONTROL_UPDATE_UI x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenDisplayClass

GEOS SDK: List of Messages for GenDisplayClass

MSG_GEN_DISPLAY_CLOSE x
MSG_GEN_DISPLAY_GET_ATTRS x
MSG_GEN_DISPLAY_GET_DOCUMENT x
MSG_GEN_DISPLAY_GET_MAXIMIZED x
MSG_GEN_DISPLAY_GET_MINIMIZED x
MSG_GEN_DISPLAY_SET_ATTRS x
MSG_GEN_DISPLAY_SET_MAXIMIZED x
MSG_GEN_DISPLAY_SET_MINIMIZED x
MSG_GEN_DISPLAY_SET_NOT_MAXIMIZED x
MSG_GEN_DISPLAY_SET_NOT_MINIMIZED x
MSG_GEN_DISPLAY_UPDATE_FROM_DOCUMENT x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenDisplayControlClass

GEOS SDK: List of Messages for GenDisplayControlClass

GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenDisplayGroupClass

GEOS SDK: List of Messages for GenDisplayGroupClass

MSG_GEN_DISPLAY_GROUP_GET_FULL_SIZED x
MSG_GEN_DISPLAY_GROUP_SELECT_DISPLAY x
MSG_GEN_DISPLAY_GROUP_SET_FULL_SIZED x
MSG_GEN_DISPLAY_GROUP_SET_OVERLAPPING x
MSG_GEN_DISPLAY_GROUP_TILE_DISPLAYS x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenDocumentClass

GEOS SDK: List of Messages for GenDocumentClass

MSG_GEN_DOCUMENT_ATTACH_FAILED x
MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT x
MSG_GEN_DOCUMENT_AUTO_SAVE x x
MSG_GEN_DOCUMENT_CHANGE_PASSWORD x
MSG_GEN_DOCUMENT_CHANGE_TYPE x
MSG_GEN_DOCUMENT_CHECK_FOR_MODIFICATIONS x
MSG_GEN_DOCUMENT_CLEAR_EMPTY x x
MSG_GEN_DOCUMENT_CLOSE x
MSG_GEN_DOCUMENT_CLOSE_FILE x
MSG_GEN_DOCUMENT_CLOSE_IF_CLEAN_UNNAMED x
MSG_GEN_DOCUMENT_CLOSE_IF_OPEN_FOR_IACP_ONLY x
MSG_GEN_DOCUMENT_COPY_TO x
MSG_GEN_DOCUMENT_CREATE_UI_FOR_DOCUMENT x
MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT x
MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT x
MSG_GEN_DOCUMENT_DISABLE_AUTO_SAVE x
MSG_GEN_DOCUMENT_DOCUMENT_HAS_CHANGED x
MSG_GEN_DOCUMENT_EDIT_USER_NOTES x
MSG_GEN_DOCUMENT_ENABLE_AUTO_SAVE x
MSG_GEN_DOCUMENT_FILE_CHANGED_REINITIALIZE_CREATED_UI x
MSG_GEN_DOCUMENT_GET_ATTRS x
MSG_GEN_DOCUMENT_GET_FILE_HANDLE x
MSG_GEN_DOCUMENT_GET_FILE_NAME x
MSG_GEN_DOCUMENT_GET_OPERATION x
MSG_GEN_DOCUMENT_GET_VARIABLE x
MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE x
MSG_GEN_DOCUMENT_MOVE_TO x
MSG_GEN_DOCUMENT_NEW x
MSG_GEN_DOCUMENT_OPEN x
MSG_GEN_DOCUMENT_PHYSICAL_AUTO_SAVE x
MSG_GEN_DOCUMENT_PHYSICAL_REVERT x
MSG_GEN_DOCUMENT_PHYSICAL_SAVE x
MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE x
MSG_GEN_DOCUMENT_QUICK_BACKUP x
MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE x
MSG_GEN_DOCUMENT_RECOVER_QUICK_BACKUP x
MSG_GEN_DOCUMENT_RENAME x
MSG_GEN_DOCUMENT_REOPEN_FILE x
MSG_GEN_DOCUMENT_REVERT x
MSG_GEN_DOCUMENT_REVERT_NO_PROMPT x
MSG_GEN_DOCUMENT_SAVE x
MSG_GEN_DOCUMENT_SAVE_AS x
MSG_GEN_DOCUMENT_SAVE_AS_COMPLETED x
MSG_GEN_DOCUMENT_SAVE_AS_TEMPLATE x
MSG_GEN_DOCUMENT_SEARCH_FOR_DOC x
MSG_GEN_DOCUMENT_SET_DEFAULT x
MSG_GEN_DOCUMENT_SET_EMPTY x
MSG_GEN_DOCUMENT_TEST_FOR_FILE x
MSG_GEN_DOCUMENT_UPDATE x
MSG_GEN_DOCUMENT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT x
MSG_GEN_DOCUMENT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT x
MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenDocumentControlClass

GEOS SDK: List of Messages for GenDocumentControlClass

MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS x
MSG_GEN_DOCUMENT_CONTROL_GET_FEATURES x
MSG_GEN_DOCUMENT_CONTROL_GET_TOKEN x x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenDocumentGroupClass

GEOS SDK: List of Messages for GenDocumentGroupClass

MSG_GEN_DOCUMENT_GROUP_GET_ATTRS x
MSG_GEN_DOCUMENT_GROUP_GET_DEFAULT_NAME x
MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY x
MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY_GROUP x
MSG_GEN_DOCUMENT_GROUP_GET_DOC_BY_FILE x
MSG_GEN_DOCUMENT_GROUP_GET_OUTPUT x
MSG_GEN_DOCUMENT_GROUP_GET_PROTOCOL x
MSG_GEN_DOCUMENT_GROUP_GET_TOKEN x x
MSG_GEN_DOCUMENT_GROUP_GET_UI_ATTRS x
MSG_GEN_DOCUMENT_GROUP_GET_UI_FEATURES x
MSG_GEN_DOCUMENT_GROUP_GET_VIEW x
MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY x
MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY_BY_FILE x
MSG_GEN_DOCUMENT_GROUP_SAVE_AS_CANCELED x
MSG_GEN_DOCUMENT_GROUP_SET_OUTPUT x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenDynamicListClass

GEOS SDK: List of Messages for GenDynamicListClass

MSG_GEN_DYNAMIC_LIST_ADD_ITEMS x
MSG_GEN_DYNAMIC_LIST_GET_NUM_ITEMS x
MSG_GEN_DYNAMIC_LIST_INITIALIZE x
MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS x
MSG_GEN_DYNAMIC_LIST_REMOVE_ITEM_LIST x
MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER x
MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR x
MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenFileSelectorClass

GEOS SDK: List of Messages for GenFileSelectorClass

MSG_GEN_FILE_SELECTOR_END_SUSPEND x
MSG_GEN_FILE_SELECTOR_GET_ACTION x
MSG_GEN_FILE_SELECTOR_GET_ATTRS x
MSG_GEN_FILE_SELECTOR_GET_CREATOR x
MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS x
MSG_GEN_FILE_SELECTOR_GET_FILE_CRITERIA x
MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE x
MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH x
MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS x
MSG_GEN_FILE_SELECTOR_GET_MASK x
MSG_GEN_FILE_SELECTOR_GET_SELECTION x
MSG_GEN_FILE_SELECTOR_GET_TOKEN x
MSG_GEN_FILE_SELECTOR_OPEN_ENTRY x
MSG_GEN_FILE_SELECTOR_RESCAN x
MSG_GEN_FILE_SELECTOR_SET_ACTION x
MSG_GEN_FILE_SELECTOR_SET_ATTRS x
MSG_GEN_FILE_SELECTOR_SET_CREATOR x
MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS x
MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA x
MSG_GEN_FILE_SELECTOR_SET_FULL_SELECTION_PATH x
MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS x
MSG_GEN_FILE_SELECTOR_SET_MASK x
MSG_GEN_FILE_SELECTOR_SET_SELECTION x
MSG_GEN_FILE_SELECTOR_SET_TOKEN x
MSG_GEN_FILE_SELECTOR_SUSPEND x
MSG_GEN_FILE_SELECTOR_UP_DIRECTORY x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenInteractionClass

GEOS SDK: List of Messages for GenInteractionClass

MSG_GEN_INTERACTION_ACTIVATE_COMMAND x
MSG_GEN_INTERACTION_GET_ATTRS x
MSG_GEN_INTERACTION_GET_TYPE x
MSG_GEN_INTERACTION_GET_VISIBILITY x
MSG_GEN_INTERACTION_INITIATE x
MSG_GEN_INTERACTION_INITIATE_NO_DISTURB x
MSG_GEN_INTERACTION_SET_ATTRS x
MSG_GEN_INTERACTION_SET_TYPE x
MSG_GEN_INTERACTION_SET_VISIBILITY x
MSG_GEN_INTERACTION_TEST_INPUT_RESTRICTABILITY x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenItemClass

GEOS SDK: List of Messages for GenItemClass

MSG_GEN_ITEM_GET_IDENTIFIER x
MSG_GEN_ITEM_SET_IDENTIFIER x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenItemGroupClass

GEOS SDK: List of Messages for GenItemGroupClass

MSG_GEN_ITEM_GROUP_GET_APPLY_MSG x
MSG_GEN_ITEM_GROUP_GET_BEHAVIOR_TYPE x
MSG_GEN_ITEM_GROUP_GET_DESTINATION x
MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM x
MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR x
MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS x
MSG_GEN_ITEM_GROUP_GET_NUM_SELECTIONS x
MSG_GEN_ITEM_GROUP_GET_SELECTION x
MSG_GEN_ITEM_GROUP_GET_UNIQUE_IDENTIFIER x
MSG_GEN_ITEM_GROUP_IS_INDETERMINATE x
MSG_GEN_ITEM_GROUP_IS_ITEM_SELECTED x
MSG_GEN_ITEM_GROUP_IS_MODIFIED x
MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE x
MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG x
MSG_GEN_ITEM_GROUP_SET_APPLY_MSG x
MSG_GEN_ITEM_GROUP_SET_BEHAVIOR_TYPE x
MSG_GEN_ITEM_GROUP_SET_DESTINATION x
MSG_GEN_ITEM_GROUP_SET_FOCUS_ITEM x
MSG_GEN_ITEM_GROUP_SET_INDETERMINATE_STATE x
MSG_GEN_ITEM_GROUP_SET_ITEM_STATE x
MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE x
MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS x
MSG_GEN_ITEM_GROUP_SET_NONE_SELECTED x
MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenPrimaryClass

GEOS SDK: List of Messages for GenPrimaryClass

MSG_GEN_PRIMARY_GET_LONG_TERM_MONIKER x
MSG_GEN_PRIMARY_REPLACE_LONG_TERM_MONIKER x
MSG_GEN_PRIMARY_USE_LONG_TERM_MONIKER x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenProcessClass

GEOS SDK: List of Messages for GenProcessClass

MSG_GEN_PROCESS_ATTACH_TO_PASSED_STATE_FILE x
MSG_GEN_PROCESS_CLOSE_APPLICATION x
MSG_GEN_PROCESS_CLOSE_CUSTOM x
MSG_GEN_PROCESS_CLOSE_ENGINE x
MSG_GEN_PROCESS_CREATE_NEW_STATE_FILE x
MSG_GEN_PROCESS_DETACH_FROM_STATE_FILE x
MSG_GEN_PROCESS_GET_PARENT_FIELD x
MSG_GEN_PROCESS_INSTALL_TOKEN x
MSG_GEN_PROCESS_OPEN_APPLICATION x x
MSG_GEN_PROCESS_OPEN_ENGINE x
MSG_GEN_PROCESS_RESTORE_FROM_STATE x
MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST x
MSG_GEN_PROCESS_UNDO_ABORT_CHAIN x x
MSG_GEN_PROCESS_UNDO_ACCEPT_ACTIONS x x
MSG_GEN_PROCESS_UNDO_ADD_ACTION x x
MSG_GEN_PROCESS_UNDO_CHECK_IF_IGNORING x x
MSG_GEN_PROCESS_UNDO_END_CHAIN x x
MSG_GEN_PROCESS_UNDO_FLUSH_ACTIONS x x
MSG_GEN_PROCESS_UNDO_GET_CONTEXT x x
MSG_GEN_PROCESS_UNDO_GET_FILE x x
MSG_GEN_PROCESS_UNDO_IGNORE_ACTIONS x x
MSG_GEN_PROCESS_UNDO_PLAYBACK_CHAIN x x
MSG_GEN_PROCESS_UNDO_SET_CONTEXT x x
MSG_GEN_PROCESS_UNDO_START_CHAIN x x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenTextClass

GEOS SDK: List of Messages for GenTextClass

MSG_GEN_TEXT_GET_APPLY_MSG x
MSG_GEN_TEXT_GET_ATTRS x
MSG_GEN_TEXT_GET_DESTINATION x
MSG_GEN_TEXT_IS_INDETERMINATE x
MSG_GEN_TEXT_IS_MODIFIED x
MSG_GEN_TEXT_SEND_STATUS_MSG x
MSG_GEN_TEXT_SET_APPLY_MSG x
MSG_GEN_TEXT_SET_ATTRS x
MSG_GEN_TEXT_SET_DESTINATION x
MSG_GEN_TEXT_SET_INDETERMINATE_STATE x
MSG_GEN_TEXT_SET_MODIFIED_STATE x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenToolGroupClass

GEOS SDK: List of Messages for GenToolGroupClass

MSG_GEN_TOOL_GROUP_SET_HIGHLIGHT x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenTriggerClass

GEOS SDK: List of Messages for GenTriggerClass

MSG_GEN_TRIGGER_GET_ACTION_MSG x
MSG_GEN_TRIGGER_GET_DESTINATION x
MSG_GEN_TRIGGER_MAKE_DEFAULT_ACTION x
MSG_GEN_TRIGGER_SEND_ACTION x
MSG_GEN_TRIGGER_SET_ACTION_MSG x
MSG_GEN_TRIGGER_SET_DESTINATION x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenValueClass

GEOS SDK: List of Messages for GenValueClass

MSG_GEN_VALUE_ADD_RANGE_LENGTH x
MSG_GEN_VALUE_DECREMENT x
MSG_GEN_VALUE_GET_APPLY_MSG x
MSG_GEN_VALUE_GET_DESTINATION x
MSG_GEN_VALUE_GET_DISPLAY_FORMAT x
MSG_GEN_VALUE_GET_INCREMENT x
MSG_GEN_VALUE_GET_INTEGER_VALUE x
MSG_GEN_VALUE_GET_MAXIMUM x
MSG_GEN_VALUE_GET_MINIMUM x
MSG_GEN_VALUE_GET_RANGE_LENGTH x
MSG_GEN_VALUE_GET_TEXT_FILTER x
MSG_GEN_VALUE_GET_VALUE x
MSG_GEN_VALUE_GET_VALUE_RATIO x
MSG_GEN_VALUE_GET_VALUE_TEXT x
MSG_GEN_VALUE_INCREMENT x
MSG_GEN_VALUE_IS_INDETERMINATE x
MSG_GEN_VALUE_IS_MODIFIED x
MSG_GEN_VALUE_SEND_STATUS_MSG x
MSG_GEN_VALUE_SET_APPLY_MSG x
MSG_GEN_VALUE_SET_DESTINATION x
MSG_GEN_VALUE_SET_DISPLAY_FORMAT x
MSG_GEN_VALUE_SET_INCREMENT x
MSG_GEN_VALUE_SET_INDETERMINATE_STATE x
MSG_GEN_VALUE_SET_INTEGER_VALUE x
MSG_GEN_VALUE_SET_MAXIMUM x
MSG_GEN_VALUE_SET_MINIMUM x
MSG_GEN_VALUE_SET_MODIFIED_STATE x
MSG_GEN_VALUE_SET_RANGE_LENGTH x
MSG_GEN_VALUE_SET_VALUE x
MSG_GEN_VALUE_SET_VALUE_FROM_TEXT x
MSG_GEN_VALUE_SET_VALUE_RATIO x
MSG_GEN_VALUE_SET_VALUE_TO_MAXIMUM x
MSG_GEN_VALUE_SET_VALUE_TO_MINIMUM x
MSG_GEN_VALUE_SUBTRACT_RANGE_LENGTH x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenViewClass

GEOS SDK: List of Messages for GenViewClass

MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER x
MSG_GEN_VIEW_CALC_WIN_SIZE x
MSG_GEN_VIEW_CALL_WITHOUT_LINKS x
MSG_GEN_VIEW_GET_ATTRS x
MSG_GEN_VIEW_GET_COLOR x
MSG_GEN_VIEW_GET_CONTENT x
MSG_GEN_VIEW_GET_DIMENSION_ATTRS x
MSG_GEN_VIEW_GET_DOC_BOUNDS x
MSG_GEN_VIEW_GET_INCREMENT x
MSG_GEN_VIEW_GET_ORIGIN x
MSG_GEN_VIEW_GET_SCALE_FACTOR x
MSG_GEN_VIEW_GET_VISIBLE_RECT x
MSG_GEN_VIEW_GET_WINDOW x
MSG_GEN_VIEW_INITIATE_DRAG_SCROLL x
MSG_GEN_VIEW_MAKE_RECT_VISIBLE x
MSG_GEN_VIEW_REDRAW_CONTENT x
MSG_GEN_VIEW_RESET_EXTENDED_INK_TYPE x
MSG_GEN_VIEW_SCROLL x
MSG_GEN_VIEW_SCROLL_BOTTOM x
MSG_GEN_VIEW_SCROLL_DOWN x
MSG_GEN_VIEW_SCROLL_LEFT x
MSG_GEN_VIEW_SCROLL_LEFT_EDGE x
MSG_GEN_VIEW_SCROLL_PAGE_DOWN x
MSG_GEN_VIEW_SCROLL_PAGE_LEFT x
MSG_GEN_VIEW_SCROLL_PAGE_RIGHT x
MSG_GEN_VIEW_SCROLL_PAGE_UP x
MSG_GEN_VIEW_SCROLL_RIGHT x
MSG_GEN_VIEW_SCROLL_RIGHT_EDGE x
MSG_GEN_VIEW_SCROLL_SET_X_ORIGIN x
MSG_GEN_VIEW_SCROLL_SET_Y_ORIGIN x
MSG_GEN_VIEW_SCROLL_TOP x
MSG_GEN_VIEW_SCROLL_UP x
MSG_GEN_VIEW_SEND_TO_HLINK x
MSG_GEN_VIEW_SEND_TO_LINKS x
MSG_GEN_VIEW_SEND_TO_VLINK x
MSG_GEN_VIEW_SETUP_TRACKING_ARGS x
MSG_GEN_VIEW_SET_ATTRS x
MSG_GEN_VIEW_SET_COLOR x
MSG_GEN_VIEW_SET_CONTENT x
MSG_GEN_VIEW_SET_DIMENSION_ATTRS x
MSG_GEN_VIEW_SET_DOC_BOUNDS x
MSG_GEN_VIEW_SET_DRAG_BOUNDS x
MSG_GEN_VIEW_SET_EXTENDED_INK_TYPE x
MSG_GEN_VIEW_SET_INCREMENT x
MSG_GEN_VIEW_SET_INK_TYPE x
MSG_GEN_VIEW_SET_ORIGIN x
MSG_GEN_VIEW_SET_PTR_IMAGE x
MSG_GEN_VIEW_SET_SCALE_FACTOR x
MSG_GEN_VIEW_SUSPEND_UPDATE x
MSG_GEN_VIEW_TRACKING_COMPLETE x
MSG_GEN_VIEW_UNSUSPEND_UPDATE x
MSG_GEN_VIEW_UPDATE_CONTENT_TARGET_INFO x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: GenViewControlClass

GEOS SDK: List of Messages for GenViewControlClass

GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: HelpControlClass

GEOS SDK: List of Messages for HelpControlClass

MSG_HELP_CONTROL_FOLLOW_LINK x
MSG_HELP_CONTROL_GET_POINTER_IMAGE x x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: ImportControlClass

GEOS SDK: List of Messages for ImportControlClass

MSG_IMPORT_CONTROL_GET_ACTION x x
MSG_IMPORT_CONTROL_GET_ATTRS x
MSG_IMPORT_CONTROL_GET_DATA_CLASSES x
MSG_IMPORT_CONTROL_SET_ACTION x
MSG_IMPORT_CONTROL_SET_ATTRS x
MSG_IMPORT_CONTROL_SET_DATA_CLASSES x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: InkClass

GEOS SDK: List of Messages for InkClass

MSG_INK_DELETE_LAST_GESTURE x
MSG_INK_GET_FLAGS x
MSG_INK_GET_TOOL x
MSG_INK_LOAD_FROM_DB_ITEM x
MSG_INK_SAVE_TO_DB_ITEM x
MSG_INK_SET_DIRTY_AD x
MSG_INK_SET_FLAGS x
MSG_INK_SET_MAX_POINTS x
MSG_INK_SET_PEN_COLOR x
MSG_INK_SET_TOOL x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: MetaClass

GEOS SDK: List of Messages for MetaClass

MSG_META_ACK x x
MSG_META_ADD_VAR_DATA x x
MSG_META_APP_SHUTDOWN x
MSG_META_APP_STARTUP x
MSG_META_ATTACH x
MSG_META_ATTACH_GENAPPLICATION x
MSG_META_ATTACH_GENPROCESSCLASS x
MSG_META_ATTACH_OBJECT x
MSG_META_ATTACH_PROCESS x
MSG_META_ATTACH_THREAD x
MSG_META_BLOCK_FREE x
MSG_META_BRING_UP_HELP x x
MSG_META_CHECK_IF_INTERACTIBLE_OBJECT x
MSG_META_CLIPBOARD_COPY x x
MSG_META_CLIPBOARD_CUT x x
MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED x x
MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED x x
MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK x x
MSG_META_CLIPBOARD_NOTIFY_TRANSFER_ITEM_FREED x
MSG_META_CLIPBOARD_PASTE x x
MSG_META_CLIPBOARD_UNDO x
MSG_META_COLORED_OBJECT_SET_COLOR x
MSG_META_COLORED_OBJECT_SET_DRAW_MASK x
MSG_META_COLORED_OBJECT_SET_PATTERN x
MSG_META_CONFIRM_SHUTDOWN x
MSG_META_CONTENT_SET_VIEW x
MSG_META_CONTENT_TRACK_SCROLLING x
MSG_META_CONTENT_VIEW_CLOSING x
MSG_META_CONTENT_VIEW_OPENING x
MSG_META_CONTENT_VIEW_ORIGIN_CHANGED x
MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED x
MSG_META_CONTENT_VIEW_SIZE_CHANGED x
MSG_META_CONTENT_VIEW_WIN_CLOSED x
MSG_META_CONTENT_VIEW_WIN_OPENED x
MSG_META_CONTEXT x
MSG_META_DEC_BLOCK_REF_COUNT x
MSG_META_DELETE x
MSG_META_DELETE_RANGE_OF_CHARS x
MSG_META_DELETE_VAR_DATA x x
MSG_META_DETACH x x
MSG_META_DETACH_ABORT x
MSG_META_DETACH_COMPLETE x x
MSG_META_DISPATCH_EVENT x
MSG_META_DISPLAY_OBJECT_FOR_SEARCH_SPELL x
MSG_META_DOC_OUTPUT_ATTACH_FAILED x
MSG_META_DOC_OUTPUT_ATTACH_UI_TO_DOCUMENT x
MSG_META_DOC_OUTPUT_CREATE_UI_FOR_DOCUMENT x
MSG_META_DOC_OUTPUT_DESTROY_UI_FOR_DOCUMENT x
MSG_META_DOC_OUTPUT_DETACH_UI_FROM_DOCUMENT x
MSG_META_DOC_OUTPUT_HAS_CHANGED x
MSG_META_DOC_OUTPUT_INITIALIZE_DOCUMENT_FILE x
MSG_META_DOC_OUTPUT_PHYSICAL_AUTO_SAVE x
MSG_META_DOC_OUTPUT_PHYSICAL_REVERT x
MSG_META_DOC_OUTPUT_PHYSICAL_SAVE x
MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE x
MSG_META_DOC_OUTPUT_READ_CACHED_DATA_FROM_FILE x
MSG_META_DOC_OUTPUT_SAVE_AS_COMPLETED x
MSG_META_DOC_OUTPUT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT x
MSG_META_DOC_OUTPUT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT x
MSG_META_DOC_OUTPUT_WRITE_CACHED_DATA_TO_FILE x
MSG_META_DRAG_... x
MSG_META_DRAG_FEATURES x
MSG_META_DRAG_MOVE_COPY x
MSG_META_DRAG_OTHER x
MSG_META_DRAG_SELECT x
MSG_META_DUMMY x
MSG_META_END_... x
MSG_META_END_FEATURES x
MSG_META_END_MOVE_COPY x
signalling quick-transfer x
MSG_META_END_OTHER x
MSG_META_END_SELECT x
MSG_META_ENSURE_ACTIVE_FT x
MSG_META_ENSURE_MOUSE_NOT_ACTIVELY_TRESPASSING x
MSG_META_ENSURE_NO_MENUS_IN_STAY_UP_MODE x
MSG_META_EXPOSED x
in Tutorial example x
MSG_META_FIELD_NOTIFY_DETACH x
MSG_META_FIELD_NOTIFY_NO_FOCUS x
MSG_META_FIELD_NOTIFY_START_LAUNCHER_ERROR x
MSG_META_FINAL_OBJ_FREE x
MSG_META_FINISH_QUIT x
MSG_META_FORCE_GRAB_KBD x x
MSG_META_FUP_KBD_CHAR x
MSG_META_GAINED_DEFAULT_EXCL x
MSG_META_GAINED_DIRECTION_EXCL x
MSG_META_GAINED_FOCUS_EXCL x x
MSG_META_GAINED_FULL_SCREEN_EXCL x
MSG_META_GAINED_KBD_EXCL x
MSG_META_GAINED_MODEL_EXCL x x
MSG_META_GAINED_MOUSE_EXCL x
MSG_META_GAINED_PRESSURE_EXCL x
MSG_META_GAINED_SYS_FOCUS_EXCL x
MSG_META_GAINED_SYS_MODEL_EXCL x
MSG_META_GAINED_SYS_TARGET_EXCL x
MSG_META_GAINED_TARGET_EXCL x x
MSG_META_GCN_LIST_ADD x x
MSG_META_GCN_LIST_DESTROY x
MSG_META_GCN_LIST_FIND_ITEM x
MSG_META_GCN_LIST_REMOVE x x
MSG_META_GCN_LIST_SEND x
MSG_META_GEN_PATH_RESTORE_DISK_PROMPT x
MSG_META_GET_CLASS x
MSG_META_GET_CONTEXT x
MSG_META_GET_FLAGS x
MSG_META_GET_FOCUS_EXCL x x
MSG_META_GET_HELP_FILE x
MSG_META_GET_HELP_TYPE x
MSG_META_GET_INI_CATEGORY x
MSG_META_GET_MODEL_EXCL x x
MSG_META_GET_OBJECT_FOR_SEARCH_SPELL x
MSG_META_GET_OBJ_BLOCK_OUTPUT x
MSG_META_GET_OPTR x
MSG_META_GET_TARGET_AT_TARGET_LEVEL x
MSG_META_GET_TARGET_EXCL x x
MSG_META_GET_VAR_DATA x x
MSG_META_GRAB_FOCUS_EXCL x x
MSG_META_GRAB_KBD x x
MSG_META_GRAB_MODEL_EXCL x x
MSG_META_GRAB_TARGET_EXCL x x
MSG_META_IACP_DOC_CLOSE_ACK x
MSG_META_IACP_DOC_OPEN_ACK x
MSG_META_IACP_LOST_CONNECTION x
MSG_META_IACP_NEW_CONNECTION x
MSG_META_IACP_PROCESS_MESSAGE x
MSG_META_IACP_SHUTDOWN_CONNECTION x
MSG_META_IMPLIED_WIN_CHANGE x
MSG_META_INITIALIZE x
MSG_META_INITIALIZE_VAR_DATA x x
MSG_META_IS_OBJECT_IN_CLASS x
MSG_META_KBD_CHAR x x
in Tutorial example x
MSG_META_LARGE_... x
MSG_META_LARGE_DRAG_FEATURES x
MSG_META_LARGE_DRAG_MOVE_COPY x
MSG_META_LARGE_DRAG_OTHER x
MSG_META_LARGE_DRAG_SELECT x
MSG_META_LARGE_END_FEATURES x
MSG_META_LARGE_END_MOVE_COPY x
MSG_META_LARGE_END_OTHER x
MSG_META_LARGE_END_SELECT x
MSG_META_LARGE_PTR x
MSG_META_LARGE_QUERY_IF_PRESS_IS_INK x
MSG_META_LARGE_START_FEATURES x
MSG_META_LARGE_START_MOVE_COPY x
MSG_META_LARGE_START_OTHER x
MSG_META_LARGE_START_SELECT x
MSG_META_LOAD_OPTIONS x
PrefInteractionClass behavior x
in Preferences modules x
MSG_META_LOST_DEFAULT_EXCL x
MSG_META_LOST_DIRECTION_EXCL x
MSG_META_LOST_FOCUS_EXCL x x
MSG_META_LOST_FULL_SCREEN_EXCL x
MSG_META_LOST_KBD_EXCL x
MSG_META_LOST_MODEL_EXCL x x
MSG_META_LOST_MOUSE_EXCL x
MSG_META_LOST_PRESSURE_EXCL x
MSG_META_LOST_SYS_FOCUS_EXCL x
MSG_META_LOST_SYS_MODEL_EXCL x
MSG_META_LOST_SYS_TARGET_EXCL x
MSG_META_LOST_TARGET_EXCL x x
MSG_META_MOUSE_BUMP_NOTIFICATION x
MSG_META_MOUSE_BUTTON x
MSG_META_MOUSE_DRAG x
MSG_META_MOUSE_PTR x
MSG_META_NOTIFY x x
With PCCom library x
MSG_META_NOTIFY_APP_EXITED x
MSG_META_NOTIFY_APP_STARTED x
MSG_META_NOTIFY_DRIVE_CHANGE x
MSG_META_NOTIFY_FILE_CHANGE x
MSG_META_NOTIFY_NO_FOCUS_WITHIN_NODE x
MSG_META_NOTIFY_OBJ_BLOCK_INTERACTIBLE x
MSG_META_NOTIFY_OBJ_BLOCK_NOT_INTERACTIBLE x
MSG_META_NOTIFY_TASK_SELECTED x
MSG_META_NOTIFY_USER_DICT_CHANGE x
MSG_META_NOTIFY_WITH_DATA_BLOCK x x
With PCCom library x
MSG_META_NULL x
MSG_META_OBJ_FLUSH_INPUT_QUEUE x x
MSG_META_OBJ_FREE x x
MSG_META_PAGED_OBJECT_GOTO_PAGE x x
MSG_META_PAGED_OBJECT_NEXT_PAGE x x
MSG_META_PAGED_OBJECT_PREVIOUS_PAGE x x
MSG_META_POST_PASSIVE_... (mouse events) x
MSG_META_POST_PASSIVE_BUTTON x
MSG_META_POST_PASSIVE_END_FEATURES x
MSG_META_POST_PASSIVE_END_MOVE_COPY x
MSG_META_POST_PASSIVE_END_OTHER x
MSG_META_POST_PASSIVE_END_SELECT x
MSG_META_POST_PASSIVE_KBD_CHAR x x
MSG_META_POST_PASSIVE_START_FEATURES x
MSG_META_POST_PASSIVE_START_MOVE_COPY x
MSG_META_POST_PASSIVE_START_OTHER x
MSG_META_POST_PASSIVE_START_SELECT x
MSG_META_PRE_PASSIVE_... (mouse events) x
MSG_META_PRE_PASSIVE_BUTTON x
MSG_META_PRE_PASSIVE_END_FEATURES x
MSG_META_PRE_PASSIVE_END_MOVE_COPY x
MSG_META_PRE_PASSIVE_END_OTHER x
MSG_META_PRE_PASSIVE_END_SELECT x
MSG_META_PRE_PASSIVE_KBD_CHAR x x
MSG_META_PRE_PASSIVE_START_FEATURES x
MSG_META_PRE_PASSIVE_START_MOVE_COPY x
MSG_META_PRE_PASSIVE_START_OTHER x
MSG_META_PRE_PASSIVE_START_SELECT x
MSG_META_PTR x
MSG_META_QUERY_IF_PRESS_IS_INK x x
MSG_META_QUIT x
MSG_META_QUIT_ACK x
MSG_META_QUIT_OBJECT x
MSG_META_QUIT_PROCESS x
MSG_META_RAW_UNIV_ENTER x
MSG_META_RAW_UNIV_LEAVE x
MSG_META_RELEASE_FOCUS_EXCL x x
MSG_META_RELEASE_FT_EXCL x
MSG_META_RELEASE_KBD x x
MSG_META_RELEASE_MODEL_EXCL x x
MSG_META_RELEASE_TARGET_EXCL x x
MSG_META_RELOCATE x
_reloc keyword x
MSG_META_RESET_OPTIONS x
MSG_META_RESOLVE_VARIANT_SUPERCLASS x
MSG_META_SAVE_OPTIONS x
PrefInteractionClass behavior x
in Preferences modules x
MSG_META_SELECT_ALL x
MSG_META_SEND_CLASSED_EVENT x x
to focus x
to model x
to target x
with input hierarchies x
with travel options x
MSG_META_SET_FLAGS x
MSG_META_SET_HELP_FILE x
MSG_META_SET_OBJ_BLOCK_OUTPUT x
MSG_META_SHUTDOWN_ACK x
MSG_META_SHUTDOWN_COMPLETE x
MSG_META_START_... x
MSG_META_START_FEATURES x
MSG_META_START_MOVE_COPY x
signalling quick-transfer x
MSG_META_START_OTHER x
MSG_META_START_SELECT x
MSG_META_SUSPEND x
MSG_META_TEST_WIN_INTERACTIBILITY x
MSG_META_TEXT_CR_FILTERED x
MSG_META_TEXT_EMPTY_STATUS_CHANGED x
MSG_META_TEXT_GAINED_FOCUS x
MSG_META_TEXT_GAINED_TARGET x
MSG_META_TEXT_LOST_FOCUS x
MSG_META_TEXT_LOST_TARGET x
MSG_META_TEXT_NOT_USER_MODIFIED x
MSG_META_TEXT_TAB_FILTERED x
MSG_META_TEXT_USER_MODIFIED x
MSG_META_UI_FORCE_CONTROLLER_UPDATE x
MSG_META_UNDO x
and GenEditControlClass x
MSG_META_UNDO_FREEING_ACTION x
MSG_META_UNRELOCATE x
_reloc keyword x
MSG_META_UNSUSPEND x
MSG_META_UPDATE_WINDOW x
MSG_META_VM_FILE_DIRTY x
MSG_META_WIN_CHANGE x
MSG_NOTIFY_APP_EXITED x
MSG_NOTIFY_APP_STARTED x
MSG_NOTIFY_DATE_TIME_CHANGE x
MSG_NOTIFY_DRIVE_CHANGE x
MSG_NOTIFY_EXPRESS_MENU_CHANGE x
MSG_NOTIFY_FILE_CHANGE x
MSG_NOTIFY_KEYBOARD_LAYOUT_CHANGE x
MSG_NOTIFY_USER_DICT_CHANGE x
MSG_PRINTER_INSTALLED_REMOVED x
MSG_PRINT_GET_DOC_NAME x
MSG_PRINT_NOTIFY_PRINT_DB x
MSG_PRINT_NOTIFY_PRINT_JOB_CREATED x
MSG_PRINT_REPORT_PAGE_SIZE x
MSG_PRINT_START_PRINTING x
MSG_PRINT_VERIFY_PRINT_REQUEST x
MSG_REPLACE_ALL_OCCURRENCES x
MSG_REPLACE_ALL_OCCURRENCES_IN_SELECTION x
MSG_REPLACE_CURRENT x
MSG_SEARCH x
MSG_SPELL_CHECK x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: PageSizeControlClass

GEOS SDK: List of Messages for PageSizeControlClass

MSG_PZC_GET_PAGE_SIZE x
MSG_PZC_SET_PAGE_SIZE x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: PrefBooleanGroupClass

GEOS SDK: List of Messages for PrefBooleanGroupClass

MSG_PREF_BOOLEAN_GROUP_SET_ORIGINAL_STATE x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: PrefClass

GEOS SDK: List of Messages for PrefClass

MSG_PREF_GET_REBOOT_INFO x
PrefInteractionClass behavior x
MSG_PREF_GET_REBOOT_STRING x
MSG_PREF_HAS_STATE_CHANGED x
PrefInteractionClass behavior x
MSG_PREF_INIT x
PrefInteractionClass behavior x
MSG_PREF_NOTIFY_DIALOG_CHANGE x
MSG_PREF_SET_INIT_FILE_CATEGORY x
MSG_PREF_SET_ORIGINAL_STATE x
PrefInteractionClass behavior x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: PrefDialogClass

GEOS SDK: List of Messages for PrefDialogClass

MSG_PREF_DIALOG_CONFIRM_REBOOT x
MSG_PREF_DIALOG_REBOOT x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: PrefDynamicListClass

GEOS SDK: List of Messages for PrefDynamicListClass

MSG_PREF_DYNAMIC_LIST_BUILD_ARRAY x
MSG_PREF_DYNAMIC_LIST_FIND_ITEM x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: PrefItemGroupClass

GEOS SDK: List of Messages for PrefItemGroupClass

MSG_PREF_ITEM_GROUP_GET_ITEM_MONIKER x
with PrefDynamicList x
MSG_PREF_ITEM_GROUP_GET_SELECTED_ITEM_TEXT x
MSG_PREF_ITEM_GROUP_SET_ORIGINAL_SELECTION x
MSG_PREF_ITEM_GROUP_UPDATE_TEXT x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: PrefStringItemClass

GEOS SDK: List of Messages for PrefStringItemClass

MSG_PREF_STRING_ITEM_CHECK_IF_IN_INIT_FILE_KEY x
MSG_PREF_STRING_ITEM_SET_INIT_FILE_STRING x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: PrefTocListClass

GEOS SDK: List of Messages for PrefTocListClass

MSG_PREF_TOC_LIST_CHECK_DEVICE_AVAILABLE x
MSG_PREF_TOC_LIST_GET_SELECTED_DRIVER_NAME x
MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_INFO x
MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_PATH x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: PrefValueClass

GEOS SDK: List of Messages for PrefValueClass

MSG_PREF_VALUE_SET_ORIGINAL_VALUE x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: PrintControlClass

GEOS SDK: List of Messages for PrintControlClass

MSG_PRINT_CONTROL_CALC_DOC_DIMENSIONS x
MSG_PRINT_CONTROL_CHECK_IF_DOC_WILL_FIT x
MSG_PRINT_CONTROL_GET_ATTRS x
MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER x
MSG_PRINT_CONTROL_GET_DOC_MARGINS x
MSG_PRINT_CONTROL_GET_DOC_NAME_OUTPUT x
MSG_PRINT_CONTROL_GET_DOC_SIZE x
MSG_PRINT_CONTROL_GET_DOC_SIZE_INFO x
MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE x
MSG_PRINT_CONTROL_GET_OUTPUT x
MSG_PRINT_CONTROL_GET_PAPER_SIZE x
MSG_PRINT_CONTROL_GET_PAPER_SIZE_INFO x
MSG_PRINT_CONTROL_GET_PRINTER_MARGINS x
MSG_PRINT_CONTROL_GET_PRINT_MODE x
MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE x
MSG_PRINT_CONTROL_GET_TOTAL_PAGE_RANGE x
MSG_PRINT_CONTROL_INITIATE_OUTPUT_UI x
MSG_PRINT_CONTROL_INITIATE_PRINT x
MSG_PRINT_CONTROL_PRINT x
MSG_PRINT_CONTROL_PRINTING_CANCELLED x
MSG_PRINT_CONTROL_PRINTING_COMPLETED x
MSG_PRINT_CONTROL_REPORT_PROGRESS x
MSG_PRINT_CONTROL_REPORT_PROGRESS_STRING x
MSG_PRINT_CONTROL_SET_ATTRS x
MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER x
MSG_PRINT_CONTROL_SET_DOC_MARGINS x
MSG_PRINT_CONTROL_SET_DOC_NAME x
MSG_PRINT_CONTROL_SET_DOC_NAME_OUTPUT x
MSG_PRINT_CONTROL_SET_DOC_SIZE x
MSG_PRINT_CONTROL_SET_DOC_SIZE_INFO x
MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE x
MSG_PRINT_CONTROL_SET_OUTPUT x
MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE x
MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE x
MSG_PRINT_CONTROL_VERIFY_COMPLETED x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: ProcessClass

GEOS SDK: List of Messages for ProcessClass

MSG_PROCESS_CREATE_EVENT_THREAD x
MSG_PROCESS_CREATE_UI_THREAD x
MSG_PROCESS_MEM_FULL x
MSG_PROCESS_NOTIFY_PROCESS_EXIT x
MSG_PROCESS_NOTIFY_THREAD_EXIT x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: SocketAddressControlClass

GEOS SDK: List of Messages for SocketAddressControlClass

MSG_SOCKET_ADDRESS_CONTROL_... x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: VisClass

GEOS SDK: List of Messages for VisClass

MSG_SPEC_GET_ATTRS x
MSG_SPEC_SET_ATTRS x
MSG_VIS_ADD_BUTTON_POST_PASSIVE x
MSG_VIS_ADD_BUTTON_PRE_PASSIVE x
MSG_VIS_ADD_CHILD x
MSG_VIS_BOUNDS_CHANGED x
MSG_VIS_CALL_PARENT x
MSG_VIS_CLOSE x
MSG_VIS_CLOSE_WIN x
MSG_VIS_COUNT_CHILDREN x
MSG_VIS_CREATE_CACHED_GSTATES x
MSG_VIS_CREATE_VIS_MONIKER x
MSG_VIS_DESTROY x
MSG_VIS_DESTROY_CACHED_GSTATES x
MSG_VIS_DRAW x x
MSG_VIS_DRAW_MONIKER x
MSG_VIS_FIND_CHILD x
MSG_VIS_FIND_CHILD_AT_POSITION x
MSG_VIS_FIND_MONIKER x
MSG_VIS_FIND_PARENT x
MSG_VIS_FORCE_GRAB_LARGE_MOUSE x
MSG_VIS_FORCE_GRAB_MOUSE x
MSG_VIS_FUP_QUERY_FOCUS_EXCL x
MSG_VIS_GET_ATTRS x
MSG_VIS_GET_BOUNDS x
MSG_VIS_GET_CENTER x
MSG_VIS_GET_GEO_ATTRS x
MSG_VIS_GET_MONIKER_POS x
MSG_VIS_GET_MONIKER_SIZE x
MSG_VIS_GET_OPT_FLAGS x
MSG_VIS_GET_POSITION x
MSG_VIS_GET_SIZE x
MSG_VIS_GET_TYPE_FLAGS x
MSG_VIS_GRAB_LARGE_MOUSE x
MSG_VIS_GRAB_MOUSE x x
MSG_VIS_INVALIDATE x
MSG_VIS_INVAL_ALL_GEOMETRY x
MSG_VIS_INVAL_TREE x
MSG_VIS_LAYER_GET_DOC_BOUNDS x
MSG_VIS_LAYER_INSERT_OR_DELETE_SPACE x
MSG_VIS_LAYER_SET_DOC_BOUNDS x
MSG_VIS_LOST_GADGET_EXCL x
MSG_VIS_MARK_INVALID x
MSG_VIS_MOVE_CHILD x
MSG_VIS_MOVE_RESIZE_WIN x
MSG_VIS_NOTIFY_GEOMETRY_VALID x x
MSG_VIS_OPEN x
MSG_VIS_OPEN_WIN x
MSG_VIS_POSITION_AND_INVAL_IF_NEEDED x
MSG_VIS_POSITION_BRANCH x
MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK x x
MSG_VIS_QUERY_WINDOW x
MSG_VIS_RECALC_SIZE x x
MSG_VIS_RECALC_SIZE_AND_INVAL_IF_NEEDED x
MSG_VIS_RECREATE_CACHED_GSTATES x
MSG_VIS_REDRAW_ENTIRE_OBJECT x
MSG_VIS_RELEASE_GADGET_EXCL x
MSG_VIS_RELEASE_MOUSE x x
MSG_VIS_REMOVE x
MSG_VIS_REMOVE_BUTTON_POST_PASSIVE x
MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE x
MSG_VIS_REMOVE_CHILD x
MSG_VIS_RESET_TO_INITIAL_SIZE x
MSG_VIS_SEND_TO_CHILDREN x
MSG_VIS_SEND_TO_PARENT x
MSG_VIS_SET_ATTRS x
MSG_VIS_SET_GEO_ATTRS x
MSG_VIS_SET_POSITION x
MSG_VIS_SET_SIZE x
MSG_VIS_SET_TYPE_FLAGS x
MSG_VIS_TAKE_GADGET_EXCL x
MSG_VIS_UPDATE_GEOMETRY x x
MSG_VIS_UPDATE_WINDOWS_AND_IMAGE x
MSG_VIS_UPDATE_WIN_GROUP x
MSG_VIS_VUP_ADD_RECT_TO_UPDATE_REGION x
MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER x
MSG_VIS_VUP_ALTER_INPUT_FLOW x
MSG_VIS_VUP_BUMP_MOUSE x
MSG_VIS_VUP_CALL_OBJECT_OF_CLASS x
MSG_VIS_VUP_CALL_WIN_GROUP x
MSG_VIS_VUP_CREATE_GSTATE x
MSG_VIS_VUP_EC_ENSURE_OBJ_BLOCK_NOT_REFERENCED x
MSG_VIS_VUP_EC_ENSURE_OD_NOT_REFERENCED x
MSG_VIS_VUP_EC_ENSURE_WINDOW_NOT_REFERENCED x
MSG_VIS_VUP_FIND_OBJECT_OF_CLASS x
MSG_VIS_VUP_GET_MOUSE_STATUS x
MSG_VIS_VUP_QUERY x
MSG_VIS_VUP_QUERY_FOCUS_EXCL x
MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS x
MSG_VIS_VUP_SEND_TO_WIN_GROUP x
MSG_VIS_VUP_SET_MOUSE_INTERACTION_BOUNDS x
MSG_VIS_VUP_TERMINATE_ACTIVE_MOUSE_FUNCTION x
MSG_VIS_VUP_TEST_FOR_OBJECT_OF_CLASS x
MSG_VIS_VUP_UPDATE_WIN_GROUP x
MSG_VIS_WIN_ABOUT_TO_BE_CLOSED x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: VisCompClass

GEOS SDK: List of Messages for VisCompClass

MSG_VIS_COMP_GET_CHILD_SPACING x
MSG_VIS_COMP_GET_GEO_ATTRS x
MSG_VIS_COMP_GET_MARGINS x
MSG_VIS_COMP_GET_MINIMUM_SIZE x
MSG_VIS_COMP_GET_WRAP_COUNT x
MSG_VIS_COMP_SET_GEO_ATTRS x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: VisContentClass

GEOS SDK: List of Messages for VisContentClass

MSG_VIS_CONTENT_DISABLE_HOLDUP x
MSG_VIS_CONTENT_ENABLE_HOLDUP x
MSG_VIS_CONTENT_GET_ATTRS x
MSG_VIS_CONTENT_GET_WIN_SIZE x
MSG_VIS_CONTENT_HOLD_UP_INPUT_FLOW x
MSG_VIS_CONTENT_NOTIFY_ACTIVE_MOUSE_GRAB_WIN_CHANGED x
MSG_VIS_CONTENT_RECALC_SIZE_BASED_ON_VIEW x
MSG_VIS_CONTENT_RESUME_INPUT_FLOW x
MSG_VIS_CONTENT_SET_ATTRS x
MSG_VIS_CONTENT_SET_DOC_BOUNDS x
MSG_VIS_CONTENT_TEST_IF_ACTIVE_OR_IMPLIED_WIN x
MSG_VIS_CONTENT_UNWANTED_KBD_EVENT x
MSG_VIS_CONTENT_UNWANTED_MOUSE_EVENT x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: VisRulerClass

GEOS SDK: List of Messages for VisRulerClass

MSG_VIS_RULER_ADD_HORIZONTAL_GUIDE x
MSG_VIS_RULER_ADD_VERTICAL_GUIDE x
MSG_VIS_RULER_CLEAR_CONSTRAIN_TRANSFORM x
MSG_VIS_RULER_COMBINE_GUIDE_INFO x
MSG_VIS_RULER_CONSTRAIN_TO_AXES x
MSG_VIS_RULER_CONSTRAIN_TO_DIAGONALS x
MSG_VIS_RULER_CONSTRAIN_TO_HORIZONTAL_AXIS x
MSG_VIS_RULER_CONSTRAIN_TO_VERTICAL_AXIS x
MSG_VIS_RULER_CONSTRAIN_X_TO_NEGATIVE_SLOPE_AXIS x
MSG_VIS_RULER_CONSTRAIN_X_TO_UNITY_SLOPE_AXIS x
MSG_VIS_RULER_CONSTRAIN_Y_TO_NEGATIVE_SLOPE_AXIS x
MSG_VIS_RULER_CONSTRAIN_Y_TO_UNITY_SLOPE_AXIS x
MSG_VIS_RULER_CREATE_GUIDE_ARRAY x
MSG_VIS_RULER_DELETE_HORIZONTAL_GUIDE x
MSG_VIS_RULER_DELETE_VERTICAL_GUIDE x
MSG_VIS_RULER_DESELECT_ALL_HORIZONTAL_GUIDES x
MSG_VIS_RULER_DESELECT_ALL_VERTICAL_GUIDES x
MSG_VIS_RULER_DRAW_GRID x
MSG_VIS_RULER_DRAW_GUIDES x
MSG_VIS_RULER_DRAW_GUIDE_INDICATORS x
MSG_VIS_RULER_DRAW_MOUSE_TICK x
MSG_VIS_RULER_GAINED_SELECTION x
MSG_VIS_RULER_GET_CONSTRAIN_STRATEGY x
MSG_VIS_RULER_GET_GRID_SPACING x
MSG_VIS_RULER_GET_GUIDE_INFLUENCE x
MSG_VIS_RULER_GET_ORIGIN x
MSG_VIS_RULER_GET_STRATEGIC_GRID_SPACING x
MSG_VIS_RULER_GET_TYPE x
MSG_VIS_RULER_HIDE_GRID x
MSG_VIS_RULER_HIDE_MOUSE x
MSG_VIS_RULER_INVALIDATE_WITH_SLAVES x
MSG_VIS_RULER_LOST_SELECTION x
MSG_VIS_RULER_SELECT_HORIZONTAL_GUIDE x
MSG_VIS_RULER_SELECT_VERTICAL_GUIDE x
MSG_VIS_RULER_SET_CONSTRAIN_STRATEGY x
MSG_VIS_RULER_SET_CONSTRAIN_TRANSFORM x
MSG_VIS_RULER_SET_GRID_SPACING x
MSG_VIS_RULER_SET_GUIDE_INFLUENCE x
MSG_VIS_RULER_SET_HORIZONTAL_GRID_SPACING x
MSG_VIS_RULER_SET_IGNORE_ORIGIN x
MSG_VIS_RULER_SET_MIN_INCREMENT x
MSG_VIS_RULER_SET_ORIGIN x
MSG_VIS_RULER_SET_REFERENCE x
MSG_VIS_RULER_SET_SCALE x
MSG_VIS_RULER_SET_TYPE x
MSG_VIS_RULER_SET_VECTOR x
MSG_VIS_RULER_SET_VERTICAL_GRID_SPACING x
MSG_VIS_RULER_SHOW_GRID x
MSG_VIS_RULER_SHOW_MOUSE x
MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE x
MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE_X x
MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE_Y x
MSG_VIS_RULER_SNAP_TO_GRID x
MSG_VIS_RULER_SNAP_TO_GRID_X x
MSG_VIS_RULER_SNAP_TO_GRID_Y x
MSG_VIS_RULER_SNAP_TO_GUIDES x
MSG_VIS_RULER_SNAP_TO_GUIDES_X x
MSG_VIS_RULER_SNAP_TO_GUIDES_Y x
MSG_VIS_RULER_TURN_GRID_SNAPPING_OFF x
MSG_VIS_RULER_TURN_GRID_SNAPPING_ON x
MSG_VIS_RULER_TURN_GUIDES_SNAPPING_OFF x
MSG_VIS_RULER_TURN_GUIDES_SNAPPING_ON x
MSG_VIS_RULER_UPDATE_CONTROLLERS x
MSG_VIS_RULER_UPDATE_GRID_CONTROLLER x
MSG_VIS_RULER_UPDATE_GUIDE_CONTROLLER x
MSG_VIS_RULER_UPDATE_TYPE_CONTROLLER x
MSG_VIS_RULER_VIEW_SCALE_FACTOR_CHANGED x
GEOS SDK List of Messages, Organized by Class


GEOS SDK Message List: VisTextClass

GEOS SDK: List of Messages for VisTextClass

MSG_VIS_TEXT_ADD_PARA_ATTR x
MSG_VIS_TEXT_APPEND_BLOCK x
MSG_VIS_TEXT_APPEND_DB_ITEM x
MSG_VIS_TEXT_APPEND_HUGE_ARRAY x
MSG_VIS_TEXT_APPEND_OPTR x
MSG_VIS_TEXT_APPEND_PTR x
MSG_VIS_TEXT_APPEND_VM_BLOCK x
MSG_VIS_TEXT_DELETE_ALL x
MSG_VIS_TEXT_DELETE_SELECTION x
MSG_VIS_TEXT_DO_KEY_FUNCTION x
MSG_VIS_TEXT_ENTER_INSERT_MODE x
MSG_VIS_TEXT_ENTER_OVERSTRIKE_MODE x
MSG_VIS_TEXT_FILTER_VIA_CHARACTER x
MSG_VIS_TEXT_GET_ALL_BLOCK x
MSG_VIS_TEXT_GET_ALL_DB_ITEM x
MSG_VIS_TEXT_GET_ALL_HUGE_ARRAY x
MSG_VIS_TEXT_GET_ALL_OPTR x
MSG_VIS_TEXT_GET_ALL_PTR x
MSG_VIS_TEXT_GET_ALL_VM_BLOCK x
MSG_VIS_TEXT_GET_AVERAGE_CHAR_WIDTH x
MSG_VIS_TEXT_GET_CHAR_ATTR x
MSG_VIS_TEXT_GET_FEATURES x
MSG_VIS_TEXT_GET_FILTER x
MSG_VIS_TEXT_GET_LINE_HEIGHT x
MSG_VIS_TEXT_GET_LR_MARGIN x x
MSG_VIS_TEXT_GET_MAX_LENGTH x
MSG_VIS_TEXT_GET_MIN_WIDTH x
MSG_VIS_TEXT_GET_ONE_LINE_WIDTH x
MSG_VIS_TEXT_GET_OUTPUT x
MSG_VIS_TEXT_GET_PARA_ATTR x
MSG_VIS_TEXT_GET_RANGE x
MSG_VIS_TEXT_GET_SELECTION_BLOCK x
MSG_VIS_TEXT_GET_SELECTION_DB_ITEM x
MSG_VIS_TEXT_GET_SELECTION_HUGE_ARRAY x
MSG_VIS_TEXT_GET_SELECTION_OPTR x
MSG_VIS_TEXT_GET_SELECTION_PTR x
MSG_VIS_TEXT_GET_SELECTION_RANGE x
MSG_VIS_TEXT_GET_SELECTION_VM_BLOCK x
MSG_VIS_TEXT_GET_SIMPLE_MIN_WIDTH x
MSG_VIS_TEXT_GET_STATE x
MSG_VIS_TEXT_GET_TEXT_SIZE x
MSG_VIS_TEXT_GET_USER_MODIFIED_STATE x
MSG_VIS_TEXT_LOAD_FROM_DB_ITEM x
MSG_VIS_TEXT_LOAD_FROM_DB_ITEM_WITH_STYLES x
MSG_VIS_TEXT_MODIFY_EDITABLE_SELECTABLE x
MSG_VIS_TEXT_RECALC_AND_DRAW x
MSG_VIS_TEXT_REPLACE_ALL_BLOCK x
MSG_VIS_TEXT_REPLACE_ALL_DB_ITEM x
MSG_VIS_TEXT_REPLACE_ALL_HUGE_ARRAY x
MSG_VIS_TEXT_REPLACE_ALL_OPTR x
MSG_VIS_TEXT_REPLACE_ALL_PTR x
MSG_VIS_TEXT_REPLACE_ALL_VM_BLOCK x
MSG_VIS_TEXT_REPLACE_SELECTION_BLOCK x
MSG_VIS_TEXT_REPLACE_SELECTION_DB_ITEM x
MSG_VIS_TEXT_REPLACE_SELECTION_HUGE_ARRAY x
MSG_VIS_TEXT_REPLACE_SELECTION_OPTR x
MSG_VIS_TEXT_REPLACE_SELECTION_PTR x
MSG_VIS_TEXT_REPLACE_SELECTION_VM_BLOCK x
MSG_VIS_TEXT_REPLACE_TEXT x
MSG_VIS_TEXT_SAVE_TO_DB_ITEM x
MSG_VIS_TEXT_SAVE_TO_DB_ITEM_WITH_STYLES x
MSG_VIS_TEXT_SELECT_ALL x
MSG_VIS_TEXT_SELECT_END x
MSG_VIS_TEXT_SELECT_RANGE x
MSG_VIS_TEXT_SELECT_RANGE_SMALL x
MSG_VIS_TEXT_SELECT_RELATIVE x
MSG_VIS_TEXT_SELECT_START x
MSG_VIS_TEXT_SET_CHAR_ATTR x
MSG_VIS_TEXT_SET_CHAR_ATTR_BY_DEFAULT x
MSG_VIS_TEXT_SET_FEATURES x
MSG_VIS_TEXT_SET_FILTER x
MSG_VIS_TEXT_SET_FONT_ID x
MSG_VIS_TEXT_SET_LR_MARGIN x
MSG_VIS_TEXT_SET_MAX_LENGTH x
MSG_VIS_TEXT_SET_NOT_USER_MODIFIED x
MSG_VIS_TEXT_SET_OUTPUT x
MSG_VIS_TEXT_SET_PARA_ATTR x
MSG_VIS_TEXT_SET_PARA_ATTR_BY_DEFAULT x
MSG_VIS_TEXT_SET_POINT_SIZE x
MSG_VIS_TEXT_SET_TB_MARGIN x
MSG_VIS_TEXT_SET_TEXT_STYLE x
MSG_VIS_TEXT_SET_USER_MODIFIED x
MSG_VIS_TEXT_SET_VM_FILE x
GEOS SDK List of Messages, Organized by Class


Access Point

The Access Point library acts as a sort of special address book--it keeps track of phone numbers and other information about Internet Service Providers. It can also store information about Terminal and Telnet access points. The library provides UI gadgetry by which the user may choose and edit access point entries; the library also provides routines by which programs may do the same.

1 Access Points
2 Letting The User Choose
2.1 Multiple-Selection Access Point Controllers
3 Searching For an Access Point
4 Locking and Unlocking
5 Adding an Access Point
6 Deleting an Access Point

Access Point: 1 Access Points

Access points, loosely defined, are collections of data useful for making communications connections. The Access Point library keeps track of access points by storing their information in the .INI file.

The library defines a few kinds of access points, identified by the AccessPointType enumerated type:

APT_INTERNET
This access point holds data useful for connecting to an ISP. Applications making a PPP connection can use this information to make the connection.
APT_TERMINAL
This access point holds data useful for connecting to a dialup terminal.
APT_TELNET
This access point holds data useful for making a telnet connection.

For each access point, the AccessPoint library may keep track of any number of properties. While you may store all sorts of information about any access point, there are a number of standard properties, enumerated as AccessPointStandardProperty values, including name, phone number, user ID, address, and password.

Access points are referenced by word-length ID numbers.

Access Point: 2 Letting The User Choose

The Talk sample application shows how an application can use an AccpntControl object to provide UI for picking an access point. This example is discussed in the Socket library documentation. The application also shows how to use the selected access point (in this case, an ISP) to establish a PPP connection; this part of the application is discussed further in the Socket chapter.

In this example, the controller is declared:

            @chunk char accpntMkr[] = "Access List";
            @object AccessPointControlClass AccpntControl = {
                GI_states = GS_USABLE | GS_ENABLED;
                ATTR_ACCESS_POINT_CONTROL_LIST_MONIKER = @accpntMkr;
                HINT_ACCESS_POINT_CONTROL_MINIMIZE_SIZE;
            }

To present the user with a different type of access points (telnet connections, for example) set the type in the APCI_type instance data field:

@object AccessPointControlClass AccpntControl = {
    ...
    APCI_type = APT_TELNET;
}

You may wish to set up your program's UI such that there is a gadget which will not be enabled (will be grayed out) if there are no access points of the proper type defined. To do this, turn off the object's GS_ENABLED flag in its declaration, and use the object's optr as the value of the APCI_enableDisable instance field:

@object ... MyObject = {
    ...
    GI_states = @default & ~GS_ENABLED;
}
@object AccessPointControlClass AccpntControl = {
    ...
    APCI_enableDisable = @MyObject;
}

To find out the ID number of the controller's currently selected access point, use MSG_ACCESS_POINT_CONTROL_GET_SELECTION .

To find out when the user has selected an access point (changed their selection), you have to be a bit trickier.

  • Subclass AccessPointControlClass .
  • Write a method for your subclass, intercepting MSG_ACCESS_POINT_CONTROL_UPDATE_SELECTION. Be sure to use @callsuper to invoke the default handler for this message.
  • Use an object of your subclass instead of a regular AccessPointControl object.

While the application is using the selected access point, it can "lock" the access point to prevent the access point's parameters from being modified while it's in use. (See Locking and Unlocking .)

Multiple-Selection Access Point Controllers

Access Point: 2 Letting The User Choose: 2.1 Multiple-Selection Access Point Controllers

Recent versions of the Access Point library support controllers which allow the user to select more than one access point at a time. This feature is available on devices with version numbers "Responder Build 4...." and higher. See Software Version Number for information about finding out the software version of the user's device.)

To create such an AccpntControl object, give it the hint HINT_ACCESS_POINT_CONTROL_MULTISELECTABLE. To get a list of the currently selected access points, use MSG_ACCESS_POINT_CONTROL_GET_MULTIPLE_SELECTIONS. The prototype for this message is:

@message word MSG_ACCESS_POINT_CONTROL_GET_MULTIPLE_SELECTIONS(
                          word    *buffer,
                          word     maxIDs);

buffer
A buffer in which to store the IDs of the currently selected access point entries.
maxIDs
The number of selections to place in buffer. Use MSG_ACCESS_POINT_CONTROL_GET_NUM_SELECTIONS to get the number of currently selected entries.

Access Point: 3 Searching For an Access Point

Perhaps instead of allowing the user to choose an access point, the program should use a known access point. The following code example shows how a program might search for an access point, given its type and name.

Code Display 23-1 Searching for an Access Point

@start  TemplateStringsResource, data;
    @chunk  TCHAR TargetName[] = "Stuckeys Sales Server";
@end    TemplateStringsResource;
@method TemplateProcessClass, MSG_TP_SEND {
    MemHandle     aBlock;
    ChunkHandle   theList;  /* Chunk Array of entry IDs of access points */
    word          theID = ACCESS_POINT_INVALID_ID ;
    aBlock = MemAllocLMem( LMEM_TYPE_GENERAL, 0 );
    MemLock( aBlock );
    theList = AccessPointGetEntries( aBlock , 0, APT_INTERNET );
    ChunkArrayEnumHandles( aBlock, theList, &theID, FunkyCallback);
    MemUnlock( aBlock );
    MemFree(aBlock);
    if (theID == ACCESS_POINT_INVALID_ID ) {
      /* didn't find the access point--deal with it */
    } else {
      /* found the access point--use it */
    }
}
Boolean _pascal FunkyCallback( void *el, void *retVal ) {
    char nomen[128];
    int  nomenSize;
    int  cmpResult;
    char *accessPointName;
    AccessPointGetStringPropertyBuffer( *((word *)el), APSP_NAME,
                                           nomen, &nomenSize );
    MemLock( OptrToHandle(@TargetName) );
    cmpResult = LocalCmpStrings( LMemDeref( @TargetName ) , nomen, 0);
    MemUnlock( OptrToHandle(@TargetName) );
    if (cmpResult) return(FALSE); 					/* if they don't match, return FALSE so
                       we can keep searching. */

    *(word *)retVal = *(word *)el;					/* We found a match. grab its ID */
    return(TRUE);				 	/* ...and we can stop searching now. */
}

The AccessPointGetEntries() routine returns a chunk array containing a list of access point IDs.

We then use the ChunkArrayEnumHandles() routine to call a callback routine to examine each ID in turn. There are many other ways we could have cycled through these entries--see the documentation of chunk arrays in the Local Memory chapter to find out which is best for your purpose.

Given an access point's ID number, we find out the name associated with that ID by calling AccessPointGetStringPropertyBuffer() . To specify that we're interested in the name, we pass APSP_NAME; we could also have passed some other AccessPointStandardProperty value, or even a pointer to a string to search for the value of some custom property.

While the application is using the selected access point, it can "lock" the access point to prevent the access point's parameters from being modified while it's in use. (See Locking and Unlocking .)

Access Point: 4 Locking and Unlocking

If something were to alter an access point's parameters while your application was using that access point, bad things might happen. To prevent anything from changing the access point's parameters, use the AccessPointLock() routine. When done using the access point, use the AccessPointUnlock() routine to unlock it. To check whether or not an access point is being used for a connection, use the routine AccessPointInUse().

Access Point: 5 Adding an Access Point

To add a new access point:

  • Use the AccessPointCreateEntry() routine to create the access point. To create an internet access point, use the syntax:
newID = AccessPointCreateEntry( 0, APT_INTERNET );
  • Using the ID returned by AccessPointCreateEntry() , set the appropriate properties for the access point.

Access Point: 6 Deleting an Access Point

To delete an existing access point:

  • Call AccessPointDestroyEntry() .

This document is a single-page version of a a multi-page document, suitable for easy printing.


Fax Sending

This chapter describes how an application can use the fax libraries and Mailbox library to compose and send a fax. Most GEOS devices, including the Nokia 9000i Communicator, come with an application that composes and sends simple fax documents; this chapter will show you how you can write a program that composes a custom fax using graphics commands.

This chapter assumes that you know the basics of GEOS programming. It also assumes that you know how to draw the image that you want to fax, given a GState to draw to. (Most visible objects draw themselves when sent a MSG_VIS_DRAW ; documentation of kernel drawing commands may be found in

A PrintControl object provides the API for drawing the contents of the fax; while this chapter will tell you what you need to know for simple faxes, more information about the PrintControl is available in .

Sending a fax may be broken down into the following tasks:

  • Finding out where to send the fax; and
  • Drawing the contents of the fax.

Ideally, these tasks are carried out using standard object classes that conform to the device's look and feel.

1 Destination Phone Number
2 Drawing the Contents
2.1 Declaring the Objects
2.2 Supplying the Document's Name
2.3 Supplying the Fax's Contents

Fax Sending: 1 Destination Phone Number

To prompt the user for the fax's destination phone number while employing the device's standard look and feel, we will use the Mailbox library and its fax adjuncts. Specifically, we will use a MailboxSendControl object, configured for use with fax.

Make sure the following lines appear in the application's .gp file:

library mailbox   # Defines MailboxSendControlClass
library spool     # Defines PrintControlClass

We will use a MailboxSendControl object to provide the UI that prompts the user for a phone number. When it comes time to draw the contents of our fax, we'll also need a PrintControl object.

We make sure that these controller objects are added to the correct GCN lists in the declaration of our application's Application object:

Code Display 4-1 Adding the Fax Controls to the Proper GCN Lists

In the declaration of the application's Application object, we make sure that the MailboxSendControl and PrintControl objects are placed on the proper GCN lists.

@object ... MyApp = {
    ...
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) =
                ... @MyPrintControl, @MySendControl;
    gcnList(MANUFACTURER_ID_GEOWORKS, MGCNLT_ACTIVE_LIST) = ... @MySendControl;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_MAILBOX_SEND_CONTROL) =
                            ... @MySendControl;
    ATTR_GEN_APPLICATION_PRINT_CONTROL = @MyPrintControl;
}

We make sure that the MailboxSendControl will be visible to the user; the straightforward way to do this is to add it to the application's generic UI tree. For example:

@object GenPrimaryClass MyPrimary = {
    GI_comp  = ..., @MySendControl;
}

Finally, we're ready to declare the MailboxSendControl object itself:

Code Display 4-2 MailboxSendControl Object Ready for Fax

@object MailboxSendControlClass MySendControl = {
    GI_visMoniker = "Send";
    GII_visibility = GIV_POPUP;
    GCI_output = process;
    MSCI_defBodyType = MOT_SELECTION;
    ATTR_MAILBOX_SEND_CONTROL_SEND_SELECTION;
    ATTR_GEN_INIT_FILE_CATEGORY = "fax" ;
    ATTR_MAILBOX_SEND_CONTROL_SINGLE_TRANSPORT = {
    {GMID_CELL_MODEM, MANUFACTURER_ID_GEOWORKS},
    {GMTID_FAX_SEND, MANUFACTURER_ID_GEOWORKS},0};
    ATTR_MAILBOX_SEND_CONTROL_TRANSPORT_HINT_OPTR =  {
      {GMTID_FAX_SEND, MANUFACTURER_ID_GEOWORKS}, 0,
      @MyPrintControl};
}

(If your application is to run on the Nokia 9000i Communicator, use ComplexMonikerClass to give your MailboxSendControl the standard Nokia 9000i Communicator look and feel. In the example above, you would replace

@object MailboxSendControlClass MySendControl = {
    GI_visMoniker = "Send";

...with...

@object ComplexMonikerClass MySendControl = {
    ComplexMoniker = MailboxSendControlClass;
    CMI_topText = CMT_SELECT_RECEIVER;
    HINT_SEEK_REPLY_BAR; /* make this button 0 */
    HINT_SEEK_SLOT = 0;

)

ATTR_MAILBOX_SEND_CONTROL_TRANSPORT_HINT_OPTR tells the MailboxSendControl that when it comes time to send a fax, it should look to the MyPrintControl object to provide the data. The FaxSendTD Mailbox transport driver knows how to interact with PrintControl objects; we will add code to our program to work with the PrintControl to draw our fax contents.

ATTR_GEN_INIT_FILE_CATEGORY tells the send control where to find the user's faxing preferences within the INI file--the "fax" category.

ATTR_MAILBOX_SEND_CONTROL_SINGLE_TRANSPORT specifies that this MailboxSendControl only sends data by fax. To allow your application to send data in additional ways, get rid of this ATTR.

The MailboxSendControl would ask the user what data they wish to send: the current selection, current page, all pages. In most fax programs, we don't want to give the user that choice. In our example, we use the ATTR_MAILBOX_SEND_CONTROL_SEND_SELECTION vardata field to prevent the controller from presenting the user with this choice--the MailboxSendControl will always try to send the "current selection," though our program ignores any idea of selection.

Fax Sending: 2 Drawing the Contents

We have configured the MailboxSendControl so that when the user has specified a recipient for the fax, the control will query a PrintControl object for the fax's contents.

Declaring the Objects

Supplying the Document's Name

Supplying the Fax's Contents

Fax Sending: 2.1 Drawing the Contents: Declaring the Objects

Again, make sure the following line is in the application's .gp file:

library spool    # Defines PrintControlClass

In the previous section, we saw that the PrintControl object needed to appear on certain GCN lists and in the MailboxSendControl 's ATTR_MAILBOX_SEND_CONTROL_TRANSPORT_HINT_OPTR field. (See Adding the Fax Controls to the Proper GCN Lists and MailboxSendControl Object Ready for Fax .)

Assuming that the PrintControl won't be used for anything else, the following declaration will suffice:

@object PrintControlClass MyPrintControl = {
    GI_states = @default & ~GS_ENABLED;
    PCI_docNameOutput = process;
    PCI_output = process;
}

Here we have turned off the GS_ENABLED flag because the PrintControl 's UI will never appear in the application; it isn't part of the application's generic UI tree. (To include the PrintControl's UI in your application, leave it enabled, add it to the generic UI tree, and see the Printing chapter for more information.)

The PCI_output line specifies that the application's process object will be responsible for drawing the fax's contents. The PCI_docNameOutput line specifies that the application's process will be responsible for providing the fax's name.

Fax Sending: 2.2 Drawing the Contents: Supplying the Document's Name

The process object (or whatever object is specified in the PrintControl 's PCI_docNameOutput field) must supply the fax's name when queried. (On the Nokia 9000i Communicator, this name is not especially important; this is just a required part of the PrintControl 's API.) The name need not be unique.

To fulfill its responsibility, the object must handle MSG_PRINT_GET_DOC_NAME , responding by sending MSG_PRINT_CONTROL_SET_DOC_NAME . If the message is not handled in this manner, the fax will never be sent.

The following is an acceptable handler:

Code Display 4-3 Handling MSG_PRINT_CONTROL_SET_DOC_NAME for faxing

The printCtrlOD value is an argument to the message.

@method MyProcessClass, MSG_PRINT_GET_DOC_NAME {
    @send printCtrlOD::MSG_PRINT_CONTROL_SET_DOC_NAME("Good Morning");
}

Fax Sending: 2.3 Drawing the Contents: Supplying the Fax's Contents

The process object (or whatever object is specified in the PrintControl 's PCI_output field) describes the fax's contents in the method for the imported message MSG_PRINT_START_PRINTING . It will receive this message after the user has specified a recipient for the fax. This message is defined:

void MSG_PRINT_START_PRINTING( \
            optr 			printCtrlOD , \
            GStateHandle 			gstate );

There are some things that the handler for this message must do:

  • It should draw the fax's contents.
  • After drawing the fax's contents, it should call GrNewPage() to generate a form feed. (It should also call GrNewPage() between the pages of a multi-page fax, of course.)
  • It should send the PrintControl MSG_PRINT_CONTROL_PRINTING_COMPLETED .

There are many things that the handler for this message may do. For information about some of them, see thePrinting chapter. (If you read that chapter, you might think it's important that the print job's page range is also determined; this is not important for faxing.)

The following example shows a handler that draws a fax's contents to contain some text and a rectangle:

Code Display 4-4 Handling MSG_PRINT_START_PRINTING when faxing

@method MyProcessClass, MSG_PRINT_START_PRINTING {
/* We draw the contents of the fax. */
    GrDrawText(gstate, 20,40, "Good Morning", 0);
    GrDrawRect(gstate, 10,30, 110,55);
/* Make sure that our last drawing command is a form-feed */
    GrNewPage(gstate, PEC_FORM_FEED);
/* Let the PrintControl know we're done */
    @send printCtrlOD::MSG_PRINT_CONTROL_PRINTING_COMPLETED();
}

In the example, we draw the fax's contents with a couple of kernel graphics commands. We might just have easily passed gstate as the graphic state argument to some Visible object's MSG_VIS_DRAW message (also passing the DrawFlag DF_PRINT).

We also need to tell the PrintControl how many pages we're printing. The first page that we draw on is page one. If there is a separate cover page, that will be page zero. To specify how many pages there are to print, we call MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE, and then MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE, passing the number of pages to print as that message's second argument.

It is possible that the fax cover page will appear on the same page on which drawing starts. To find out how much vertical space this cover page will take up on the first page, call the PrintControl with MSG_PRINT_GET_FIRST_PAGE_OFFSET ; the return value will be the height of the cover page. (To properly follow naming conventions, this message should be called MSG_PRINT_CONTROL_GET_FIRST_PAGE_OFFSET ; as of this writing it has not yet had its name fixed.)

This document is a single-page version of a a multi-page document, suitable for easy printing.


The Mailbox Library

One of the most important functions of a portable computing device is to exchange information with other devices. A portable computer might use a number of different physical means to transmit and receive information; it might also use a variety of communication protocols (some of which may not be invented yet!).

GEOS helps to insulate an application from the physical details of transmitting and receiving information. It does this with its Mailbox library, and associated drivers. The library lets each application transmit and receive information in more or less the same way, no matter how it uses the information, and no matter what it does with the information once it receives it (or how it assembles the information before transmitting it).

The Mailbox API relies heavily on a basic understanding of VMChain structures and routines. Please consult the Virtual Memory chapter of the Concepts book for more information.

NOTE: This is a preliminary document. Please consult the Mailbox definition files ( mailbox.def or mailbox.goh ) for current and complete information.

1 Design Philosophy
1.1 Mailbox Overview
1.2 Transport Driver Overview
2 Inbox/Outbox Structure
3 Mailbox Structures & Utilities
4 Data Formats
4.1 GMDFID_STREAM_GSTRING
4.2 GMDFID_VM_GSTRING
4.3 GMDFID_TRANSFER_ITEM
4.4 GMDFID_TEXT_CHAIN
4.5 GMDFID_TEXT_FILE
4.6 GMDFID_DOCUMENT
4.7 GMDFID_INK
4.8 GMDFID_VM_COMPOSITE
5 Data Drivers
6 Application Responsibilities
6.1 Providing a User-Interface
6.2 Sending a Message
6.3 Receiving and Processing Messages
7 Examples of Mailbox Usage
7.1 An Incoming Pager Email Message
7.2 Sending and Receiving an Address Book Page

The Mailbox Library: 1 Design Philosophy

The purpose of the Mailbox library is to let applications send and receive information to and from other computers, through a variety of communications media. It insulates the application as much as possible from the mechanics of transmitting and receiving the information.

An application might need to transmit information over several different kinds of connection. For example, it might send information over a modem or through a direct connection. It might need to use the same device in very different ways; for example, it might fax a document through a modem, or copy a file to a server through that same modem.

The Mailbox library helps to insulate applications from the details of the hardware and communication protocols. It does this by dividing up the communications tasks.

Mailbox Overview

Transport Driver Overview

The Mailbox Library: 1.1 Design Philosophy: Mailbox Overview

The Mailbox library implements an Inbox/Outbox system. This lets the user select an appropriate device through which to transport the information; it also lets the user select a delivery time (though most applications will always designate messages for immediate delivery, so it is possible that the first release of the Inbox/Outbox system will not support applications setting an early-bound for delivery time). (Most applications will do this by including the Mailbox Library's MailboxSendControl device, which the user can use to create an outgoing message.)

The application must, when requested, package up information in a format acceptable to a transport driver being used. (Most applications will only need to support one or a few different data formats, such as GStrings or raw text in a VM Chain.) It then registers this message with the Mailbox library.

The Mailbox library stores messages that have not yet been transmitted in its outbox . Messages are held in the outbox until the hardware connection necessary for transmission is available. For example, if the transport driver needed a direct serial link, the message would stay in the outbox until that link was available. An application can also specify that a message not be delivered until a specific time. In this case, the message will automatically remain in the outbox until that time. While a message is in the outbox, the device's user can examine information about the message, and (if he or she chooses) delay or cancel its delivery.

When the time comes to send the message, the Mailbox library activates the appropriate data and transport drivers. This activation is transparent to an application developer. It is helpful to understand how a transport driver operates, however.

The Mailbox Library: 1.2 Design Philosophy: Transport Driver Overview

The transport driver handles actually sending a message of the device. It interacts with the actual hardware used for sending the message. This document does not attempt to discuss creating transport drivers in detail.

Messages are received much the way they are delivered. The transport driver receives a message, registers it with the Mailbox library, and passes its data, block by block, to the appropriate data driver. The data driver translates this message into its own data format. The Mailbox library stores the message in its inbox until it can deliver the message. When the appropriate time for delivery is reached, the Mailbox library starts up an appropriate application (if it is not already running) and passes the message to the application. The application must notify the Mailbox library when it has finished with the message.

A user or application can address a message to the local machine. In this case, the message will not be sent to the transport driver; the Mailbox library will deliver the message simply by moving it from the machine's outbox to its inbox.

The Mailbox Library: 2 Inbox/Outbox Structure

The Mailbox library interacts directly with applications. It also manages drivers, which provide information about data formats, and also interact with the hardware links to the outside world. Thus, to support a new hardware device or data format, you just need to write a new driver.

The responsibilities of the Inbox/Outbox system are divided among four different agents:

  • The application packages outgoing data, and handles incoming data addressed to that application. It provides data in a format acceptable to the transport driver, and receives information in such a format. It generally uses the MailboxSendControl controller to let the user control the sending of messages.
  • The data driver describes how the information is stored; it thus lets the application know (indirectly) both how to assemble data for transport. (It is only concerned with the gross structure of the message, e.g. whether it's stored in a VM chain or a plain-text file. The structure above that level is opaque to the data driver.) It stores the message until the message is ready for transport. It also translates outgoing messages into a format appropriate for the transport driver, and translates incoming messages from such a format into its own internal format.
  • The transport driver interacts with the actual hardware device used to transmit or receive the data. It receives outgoingmessage data from the data driver, and sends it over the device; it receives incoming messages, and sends them to the appropriate data driver. In some cases, it may translate the message's data; for example, a fax transport driver might read a message containing a GString, and translate that into a standard fax format readable by a (non-GEOS) fax machine.
  • The Mailbox library manages the interaction between the other agents. It keeps track of messages that have been designated for transport at another time, and messages that have been received by the computer but not yet delivered to any application. It loads the appropriate drivers and applications when they are needed. It also provides UI, letting the user set options about how to handle outgoing and incoming messages.

The Mailbox Library: 3 Mailbox Structures & Utilities

MailboxGetVMFile(), MailboxMessageFlags, MailboxMessagePriority, MailboxDeliveryVerb

There are a few data types that are used at various parts of the message-transmission and reception process. There are also a few concepts related to these types that need to be discussed first.

First of all, we need to distinguish between two basic data types: data format types , and data storage types .

Every message has two data tokens associated with it. One is the data format type, and the other is the data storage type. The data storage type describes the gross structure of the message data. Every data driver can accept one data storage type. For example, the GEOS system includes data drivers for messages whose bodies are stored in VM trees, and messages whose bodies are stored in byte-files. The data driver is not concerned with the message on a lower level. If you send a message in a VM tree, the data drivers at both machines will make sure that the message arives in a valid VM tree (which may mean fixing up the VM handles that make up the links in the tree), but they won't care about the data inside the blocks of the tree.

On a higherlevel, every message has its own data format type. This type describes how the data is actually stored in the underlying structure. For example, a message might be stored as a GString in a VM chain. In this case, the message's data storage type would be "VM tree" (a VM chain is a special kind of VM tree), and the data format type would be "VM Gstring".

Every data format type implies a particular data storage type; all messages with data format type a will necessarily use data storage type x . The converse, however, is not necessarily true. For example, all messages with data format type "VM GString" have data storage type "VM tree"; as do all messages with data format "VM composite".

When an application passes a message to a data driver, it may designate the message body as either "volatile" or "non-volatile". If the data is marked "volatile", the application may alter or destroy the data as soon as it has registered the message. That is, when the application registers the message, the appropriate Data Driver copies all the message's information out of the passed message before execution returns to the application. You should pass "volatile" if, for example, you are passing a pointer to a VM file used by the application.

If you designate the data as non-volatile , you are not allowed to alter or delete the data in the message. If you ask it to, the data driver will take care of freeing the storage used for the message when it is done with it. Every data driver has its own conventions about what applications should do with non-volatile messages after sending them.

For example, if you send a message through the VM tree data driver, you should write non-volatile messages to a special VM file created by the routine MailboxGetVMFile() . This routine is passed a single argument, the number of blocks you expect to allocate in the VM block. The routine returns the handle of a VM file. You can write the message in this file, and register the message (designated "non-volatile"). The data driver will then take care of freeing the file's storage when the message has been safely delivered.

Each message is associated with a set of MailboxMessageFlags . These are a byte-length record. This record has the following flags:

MMF_BODY_DATA_VOLATILE
As discussed above, this flag indicates that the data in the message must be copied immediately, as it is subject to change or freeing without further notice.
MMF_SEND_WITHOUT_QUERY
This flag indicates that the message should be sent without asking the user for permission.
MMF_PRIORITY
This is a member of the MailboxMessagePriority enumerated type, discussed below.
MMF_VERB
This is a member of the MailboxDeliveryVerb enumerated type, discussed below.
MMF_DELETE_BODY_AFTER_TRANSMISSION
Set if the data driver should delete the message body after transmitting. Note that if MMF_BODY_DATA_VOLATILE is set, then the data driver will always delete the copy of the body it made at the time the message was registered, and leave the body of the original app-ref alone, regardless of how MMF_DELETE_BODY_AFTER_TRANSMISSION is set. If there is an error during transmission, the message body will still be deleted.

Every message can be assigned a priority. This priority is represented by a member of the MailboxMessagePriority enumerated type. The priority affects how the Mailbox library will handle the message at both the sending and the receiving end. This type has the following values:

MMP_EMERGENCY
The recipient user should be notified immediately, and not allowed to do anything before he or she receives the message.
MMP_URGENT
The user should be notified immediately, and told there is an urgent message awaiting. The user does not, however, have to read the message right away.
MMP_FIRST_CLASS
The user should be notified on the next periodic check of the Inbox. There are no other constraints on when the user should read the message.
MMP_THIRD_CLASS
The Inbox should not notify the user of the message's arrival; instead, the appropriate application should notify the user, if appropriate. For outgoing messages, the user must explicitly request that the message be sent; it will not happen automatically once a connection is made.

Every message has a "delivery verb" associated with it. This is useful in notifying the user of the message's arrival. Each delivery verb is represented by a member of the MailboxDeliveryVerb enumerated type. This type has the following members:

MDV_DELIVER
This is the default verb.
MDV_VIEW
The message is some sort of image that can be viewed, e.g. a received fax.
MDV_PLAY
The message is some kind of continuous message with a definite starting point and ending point, e.g. a voice recording or MPEG movie.
MDV_ACCEPT
Another generic verb.
MDV_READ
The message is mainly textual, and can be read (e.g. an old-fashioned EMail message).
MDV_FILE
The message is, or contains, one or more units that should be stored in a document that can contain many such units (e.g. an address-book card).

The Mailbox Library: 4 Data Formats

An application is largely insulated from the details of transmitting a message. It needs merely to write the message's data in a format which the chosen data driver can understand; the data driver will then translate this into an appropriate format for transmission.

GEOS provides two data drivers. These drivers use the data transport formats "File Data" and "VM tree data". Each data-format type provided by GEOS is built on top of one of these formats.

There are a few concerns in common to each data driver.

Every data driver will have its own format for accepting the data. In each case, the data driver is passed a pointer to a "data block" which tells it where the rest of the message is. Thus, every data driver is called in the same way: it is passed a single far pointer to some data block. How that data block stores the location of the message will vary from one data storage format to another.

GEOS will almost certainly define the following different data format types. An application should, if possible, support as many of these data types as is feasible. Each format is uniquely identified by a member of the GeoworksMailboxDataFormatID enumerated type. (The following sections will be expanded when the Inbox/Outbox suite is completed.)

GMDFID_STREAM_GSTRING

GMDFID_VM_GSTRING

GMDFID_TRANSFER_ITEM

GMDFID_TEXT_CHAIN

GMDFID_TEXT_FILE

GMDFID_DOCUMENT

GMDFID_INK

GMDFID_VM_COMPOSITE

The Mailbox Library: 4.1 Data Formats: GMDFID_STREAM_GSTRING

In this data type, the message is stored as a GString written to a byte file. This is useful for printing and faxing messages. Applications should make every effort to support this data type. These messages are handled by the GEOS file data driver.

The Mailbox Library: 4.2 Data Formats: GMDFID_VM_GSTRING

This is much like STREAM_GSTRING, except that the message is stored in a GString written to a VM Chain. These messages are handled by the GEOS VM-tree data driver.

The Mailbox Library: 4.3 Data Formats: GMDFID_TRANSFER_ITEM

This format is used when the user wishes to send the contents of the clipboard as a message. These messages are handled by the GEOS VM-tree data driver.

The Mailbox Library: 4.4 Data Formats: GMDFID_TEXT_CHAIN

The message is plain text, written to a VM chain. These messages are handled by the GEOS VM-tree data driver.

The Mailbox Library: 4.5 Data Formats: GMDFID_TEXT_FILE

The message is plain text, written to a byte file. These messages are handled by the GEOS file data driver.

The Mailbox Library: 4.6 Data Formats: GMDFID_DOCUMENT

The message is written to some kind of GEOS document with extended attributes and a full GEOS filename. The document's internal format is opaque to the data and transport drivers. These messages are handled by the GEOS file data driver.

The Mailbox Library: 4.7 Data Formats: GMDFID_INK

The message is stored as GEOS ink. These messages are handled by the GEOS VM-tree data driver.

The Mailbox Library: 4.8 Data Formats: GMDFID_VM_COMPOSITE

The message is passed as a VM tree. The first block of the tree holds an array of pointers to blocks that head up other VM-based data formats, followed by an array of MailboxDataFormat tokens which identifies the format of each chain. These messages are handled by the GEOS VM-tree data driver.

The Mailbox Library: 5 Data Drivers

A data driver specifies how the data in a transmitted message is arranged. An application that sends a message must provide it in the format required by the data driver. The data driver then translates the message into an appropriate format for the transport driver. When the transport driver receives a message, it determines which data driver can interpret the message, and then calls theMailbox library.

There are a number of default GEOS data types, and developers may write other data drivers of their own to support new data types.

A data driver has to perform a few different tasks:

  • When an application sends a message, the Mailbox library hands the message to the data driver. If the message-data is volatile, the data driver must copy the message's data to a safe place, so the application can free the storage it used to create the message. It gives the Mailbox library an opaque token to identify the message; the mailbox library will use this token when it requests the message for delivery.
  • When the Mailbox library requests it, it must provide message data (one "block" at a time) in a format appropriate for the transport driver.
  • When the Mailbox library receives a message in the driver's format, it must be able to handle data, one "block" at a time, provided by the transport driver in that driver's format; it must be able to assemble this into a message in the data driver's format. (As when transmitting a message, the data driver will give the Mailbox library an opaque token to identify this message.)
  • When it delivers a message, it must provide a reference to the received message in a format the receiving application will be able to understand.
  • The data driver must be able to provide, on request, the size of any message (outgoing or incoming) it's holding.
  • When the Mailbox library informs the data driver that a message has been handled (i.e. an outgoing message has been transmitted, or an incoming message has been deleted by the destination application), the data driver must free its copy of the message, reclaiming the space that message was using.

The Mailbox Library: 6 Application Responsibilities

The application is insulated from most of the mundane responsibilities of transmitting and receiving data. Its behavior is event-driven. It has to know how to respond to a variety of requests from the Mailbox library; however, it generally does not have to initiate action on its own.

There are a number of specific tasks an application must perform:

  • It must provide the right UI (usually in the form of controllers) for using the Inbox/Outbox functionality. It can do this by including the appropriate controllers.
  • It must package data into an acceptable format. When the user selects data to transmit (which could be a small message or an entire document), it must format the data appropriately.
  • It must be able to handle incoming messages. This consists of notifying the user that new data has been received, incorporating the data into the document (or adding a new document containing the data), delete the message it's been sent, and notify the Mailbox library that it has received the data.

Providing a User-Interface

Sending a Message

Receiving and Processing Messages

The Mailbox Library: 6.1 Application Responsibilities: Providing a User-Interface

To use the Mailbox library, an application will usually want to include a MailboxSendControl object in its UI. (Of course, you can create your own UI, but the MailboxSendControl is a convenient package.) Although most applications will use the UI provided by this controller other may want to use the

The settings of the MailboxSendControl will determine what formats the application can use when it sends data. There are a number of standard data types specified by the MailboxSendControl. If you wish to support a new format for transporting data, you must subclass the MailboxSendControl object and add any tools necessary for that new data type. If you wish to provide extra, application specific functionality, you must add tools to the controller yourself. For example, if you are writing a form-collection application, you might wish to add an icon that selects the certain fields of each form, assembles them into a message, and transmits them.

The Mailbox Library: 6.2 Application Responsibilities: Sending a Message

A user will generally send a message by using the application's MailboxSendControl object. This object lets the user send the selected material in the application. It lets the user select such things as the message's destination, transmission time, subject line, etc.

Once the user has selected the message, the MailboxSendControl sends the message MSG_META_MAILBOX_CREATE_MESSAGE to its output object (generally the application object). This message instructs the object to create an appropriate message.

When the application has been notified that it should create a message, it should then determine what data type the message will need. It does this by sending MSG_MAILBOX_SEND_CONTROL_CHOOSE_FORMAT to its MailboxSendControl. It can find out other information about the message to be sent by sending other messages to the MailboxSendControl.

The MailboxSendControl informs the application what data type it should use. The application must determine what data to send (generally the current selection); it then assembles this data into the format requested by the send control object. Finally, it registers the message by sending MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE to its MailboxSendControl. This message instructs the MailboxSendControl to register the message with the Mailbox library.

MSG_META_MAILBOX_CREATE_MESSAGE()
MetaMailboxMessage void MSG_META_MAILBOX_CREATE_MESSAGE(
        optr		sendControl,
        word		transHandle);

This message informs the output of a MailboxSendControl that it should create the body of the message. It is the obligation of that object to notify the controller that it has accomplished this task with MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE.

MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE
MailboxMessage 	MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE(
        word		TransID,
        MailboxStorage		bodyStorage,
        MailboxDataFormat		bodyFormat,
        const void *		bodyRef,
        unsigned word		bodyRefLen,
        MailboxMessageFlags		flags,
        const char *		subject,
        const GeodeToken *		destApp)

When an application wishes to send a message, it does this by registering a message with the Mailbox library. The Mailbox library then loads the appropriate data and transport drivers to actually deliver the message. The application registers a message by sending MSG_MAILBOX_SEND_CONTROL_REGISTER_MESSAGE to its MailboxSendControl object.

Source: The recipient of MSG_META_MAILBOX_CREATE_MESSAGE. Generally sent by the Application object.

Destination: The application's MailboxSendControl.

Parameters: TransID This is the token the MailboxSendControl passed with its MSG_META_MAILBOX_CREATE_MESSAGE .

bodyStorage
The 32-bit token (of type MailboxStorage ) identifying the data driver which should be used. (The message will be passed in a format appropriate to that data driver.)
bodyFormat
The 32-bit token (of type MailboxDataFormat ) identifying the kind of data in the message body. Each format token implies a particular storage type.
bodyRef
A pointer to the beginning of a data block. This block instructs the data driver where the message body is. (Each data driver may have its own conventions for interpreting the data in this block.)
bodyRefLen
The number of bytes at *bodyRef .
flags
A word of MailboxMessageFlags .
subject
A pointer to a null-terminated string containing the subject or summary line describing the message. (The string may contain newline characters.)
destApp
the GeodeToken of the application which will receive the message (on whichever machine receives the message). If this is a generic token, the recipient machine's Mailbox library will automatically convert it to the token of whichever application will actually receive the message.

Interception: If you define a subclass of the MailboxSendControl, you may wish to intercept this message to add functionality.

MSG_META_MAILBOX_MESSAGE_REGISTERED
MetaMailboxMessage void MSG_META_MAILBOX_MESSAGE_REGISTERED(
        MemHamdle		args);

This message is sent by the MailboxSendControl once it has attempted (via MailboxRegisterMessage() ) to register the message. The controller may call MailboxDoneWithVMFile() (or any other cleanup procedures) in response to this message. <

The Mailbox Library: 6.3 Application Responsibilities: Receiving and Processing Messages

MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE, MailboxAcknowledgeMessageReceipt(), MailboxGetSubjectBlock(), MailboxGetSubjectLMem(), MailboxGetMessageFlags(), MailboxGetBodyFormat(), MailboxGetBodyRef(), MailboxDoneWithBody(), MailboxStealBody(), MailboxDeleteMessage()

An application that uses the Inbox/Outbox technology must be able to handle incoming messages, as well as generate outgoing ones.

The Mailbox holds incoming messages in its Inbox until an appropriate time to deliver them. For example, it may choose to wait until the user starts up the application, or until the delivery deadline is reached. In any event, when the Inbox library needs to deliver the message it sends MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE to the application object of an appropriate application. This message has a single argument, a 32-bit MailboxMessage token which uniquely identifies the message.

If the application is going to handle the message, it must call the routine MailboxAcknowledgeMessageReceipt() . The routine is passed the MailboxMessage token for the message. (If you do not acknowledge the receipt of the message, your application will be bothered by repeated notifications.)

Once the application has been notified of an incoming message, it can call a number of routines to find out about the message. To find out the message's subject line, it should call either MailboxGetSubjectLMem() or MailboxGetSubjectBlock() . Both of these routines return the subject text for the message.

MailboxGetSubjectBlock() is passed a single parameter, namely the MailboxMessage token for the message. The routine allocates a global memory block, writes the subject line to that block (as a null-terminated string), and returns the handle of the block. If it fails, it returns zero and sets the thread's error value. (See ThreadGetError() .)

MailboxGetSubjectLMem() is very similar. It is passed two arguments: the MailboxMessage token for the message, and the global memory handle of an LMem heap. The routine allocates a chunk in that LMem heap, writes the subject line to that chunk (again, as a null-terminated string), and returns the chunk handle of that chunk. Again, if the routine is unable to comply, it returns zero and sets the thread's error value.

MailboxGetMessageFlags() is passed the MailboxMessage token for the message. It returns the MailboxMessageFlags for that message. It is often used to find the message's priority.

MailboxGetBodyFormat() is passed the MailboxMessage token for the message. It returns the MailboxDataFormat value for the message's body. The application can use this to decide if the message is written in a format the application can understand.

MailboxGetBodyRef() is used to actually retrieve the data for the message. The routine is passed the following arguments:

  • The MailboxMessage token for the message.
  • A pointer to a data buffer.
  • The size of the buffer, in bytes.

If the routine is successful, it returns true and writes a reference to the message body in the passed buffer. This reference is in the same format that would be passed to MailboxRegisterMessage() , i.e. it varies according to the data driver. If the routine is unsuccessful, it returns false and sets the thread's error value to an appropriate MailboxError value.

For every time the application successfully calls MailboxGetBodyRef() , it must call MailboxDoneWithBody() . This routine is passed three arguments:

  • The MailboxMessage token for the message.
  • A pointer to the data buffer passed to MailboxGetBodyRef() .
  • The size of the buffer, in bytes.

The routine notifies the Mailbox library that the application is done with the message body for the time being. The mailbox library is thus free to close the file holding the data, or take some similar action. (The message body will not be destroyed; it will still be available for future calls to MailboxGetBodyRef() .)

An application can also get the message body by calling MailboxStealBody() . This is passed the same arguments, and returns the same values, as MailboxGetBodyRef() . However, if the routine is successful, the storage area containing the message body will be transferred to the ownership of the application, and the data driver will free any other storage it used to hold the message body. After a successful call to MailboxStealBody() , any future calls to MailboxGetBodyRef() or MailboxStealBody() will fail; as far as the Mailbox library is concerned, the message has no body any more. When the application is finished with the message, it must free the storage used for the message. In particular, if the data reside in a VM file, the application must call MailboxDoneWithVMFile() to notify the Mailbox library to free that file.

When an application is done processing a message, it must call MailboxDeleteMessage() . This routine is passed the MailboxMessage token for the message. It instructs the mailbox library to delete the message and any associated data. The MailboxMessage token becomes invalid after this routine returns.

The Mailbox Library: 7 Examples of Mailbox Usage

It may be helpful to go through a step-by-step example of how a message might be sent, illustrating the function of the transport driver, and how much is done for you. The Mailbox library can be used for a great many different applications, so it's hard to describe a "typical" mailbox transaction; but there are certain elements most mailbox transactions have in common.

We present several different examples of how the Mailbox library might be used. Note that in the following discussion, the behavior of the transport driver (which is opaque to an application developer) is discussed. Unless you are writing a transport driver yourself, you will not need to worry about this.

An Incoming Pager Email Message

Sending and Receiving an Address Book Page

The Mailbox Library: 7.1 Examples of Mailbox Usage: An Incoming Pager Email Message

In this example, we consider a device that's receiving an Email message. The message was generated by some central server, which need not have been running any variety of GEOS, as long as the message conformed to the transport driver's expectations for the message format.

Arrival of The Message

The message is received via a wireless pager card that's plugged into the device. The transport driver for the card, which was loaded when the card was inserted, periodically polls the card to see if new messages have arrived. On the next poll, it finds that there is indeed a new message. It extracts enough of the message to determine it's something for the Email application to see.

Armed with that, it calls the Mailbox library to register a message, specifying:

  • The destination is the Email application (whose token it knows, in the same way it knows the message is for the Email application).
  • The transport mechanism is LOCAL (i.e. the message is destined for an application on this machine).
  • The user should be notified of the arrival of the message, when appropriate. (The priority may be extracted from the pager card's data structure, allowing some Email to be treated as urgent, while other is treated as junk mail)
  • The user should not be asked whether the message should be delivered when the connection is available (i.e. when the application is loaded). That is, when the application is loaded, the message will automatically be presented.
  • The delivery-verb of choice is "Read"; this is used when presenting the message to the user.
  • The storage format token is that for the data driver located on the pager card's file-system.
  • The data format is one the Email application expects. Note that in this case, the pager card data driver will end up converting from the format in which the message is stored on the card to a VM- or file-based format of some sort. In effect, the data driver is pretending the data are stored in the format appropriate to the data format token the pager driver has claimed they're in, when in fact they're in a very different format within the card's own memory.
  • The address of the data (a reference to the data on the card). This address is specific to the passed data driver. When registering a message, you must know the type of address the data driver you're specifying expects (and, of course, provide an address of data in that format).
  • The message data are not volatile, as far as the pager driver is concerned. (Data are volatile if they are in danger of deletion as soon as the Mailbox library returns from the store-message call. The indication of volatility is passed to the data driver so it knows if it should copy the data to a safer location.)
  • The subject line, which it has extracted from the message.
  • A starting time-bound of the current time. This allows the message to be delivered immediately, if the Email application is currently loaded.
  • An ending time-bound of one day from now. When the ending bound time is reached, the application will automatically be loaded and the message presented. The choice of an ending bound is peculiar to the card and what the driver writer thinks is a reasonable deadline for the user to view the message. It may also be intuited from something in the Email message itself, such as a special header in the message.

If the card will delete the message after a certain time, the pager driver will want to set an ending time-bound well before that deletion is scheduled to occur. If the card may delete the message after a while to free up space, as happens in the EMBARC card, the driver may well want to set an ending time-bound of, say, one day from when the message was received. The driver could also set the ending bound to match the starting bound, thus forcing the Email application to be loaded. The final choice, of course, is to set no ending bound.

For its part, the Mailbox library will load the data driver and call it, passing the data address the library was given and allowing the driver to copy the data (if required) and return its own version of the "opaque" data. (The message body is "opaque" to the Mailbox library, in that it never looks into this data for information; instead, it passes the data to the various drivers and applications, which may interpret and manipulate the data.) For example, the file data driver might well copy the file, whose name was passed, into a spool directory for safekeeping, returning the shorter name back to the Mailbox library as the opaque data to store with the message. The rest of the data will be copied into the message descriptor, after checking to make sure it's reasonable, and the descriptor will be placed at the head of the inbox queue.

Assuming the message is given standard First-Class priority and that the Email application isn't currently running in the foreground, the Mailbox library will catch the existence of the new message at its next (user-settable) check interval and will notify the user of its arrival. If the message has a higher priority, the user may be notified immediately, and may be forced to deal with the message immediately, as well. If the Email application is the foreground application, and has told the system it will take care of notifications itself, the message is immediately given to the application, which will presumably notify the user in its own way.

Delivery of The Message

When the delivery box is presented to the user, the user can decide how to proceed with the message.

If the user says to send it, the Mailbox library will establish an IACP connection to the application that is to receive the message and send it an appropriate notification. When the application acknowledges receipt of the message, the message is removed from the Mailbox library's "inbox" and any list displaying the message is updated appropriately.

In essence, the Mailbox library will use IACP to send a MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE to the application object, passing the token for the message.

Using the message token, the application calls the Mailbox library to acknowledge receipt of the message. This acknowledgment will remove the message from the inbox, but will not delete the message descriptor.

The application will then call the Mailbox library back to fetch the body of the message, its subject, time stamp, etc. When the user deletes the message, the Email application will notify the library that it is done with the message. This call will cause the message data and the message descriptor to both be deleted.

The Mailbox Library: 7.2 Examples of Mailbox Usage: Sending and Receiving an Address Book Page

This transmission is about as complex as it gets. In it, a user on one GEOS machine transmits an address book page to another user, on another GEOS machine. The transaction is broken into four parts, involving both machines.

Message Set-Up

First, the user selects the page he or she wishes to send. The user selects the means of transport from a Send menu or set of send tools (depending on the system and how the application is set up). The act of going to the page causes the application to notify the message-send controller of the context in which the application finds itself. It is this context that determines what type of data will be placed, by default, in the message body.

The message controller will bring up a dialog displaying the UI for setting transport options, including the address to which the message is to be sent. This UI is obtained by calling the transport driver.

When the user has chosen the address, the controller will send off a MSG_META_MAILBOX_CREATE_MESSAGE to its output, including the 16-bit token bound to the "Current Page" string in the message controller's table.

Having received the message, it becomes the application's responsibility to determine in what formats it can provide the data to be sent. It calls the message controller back, passing an array of data format descriptors that the controller then passes to the transport driver, to see which format it prefers. There will be transport drivers, like the one that uses the Socket library, where any data format will do, so long as the data driver supports the READ_NEXT_BLOCK and WRITE_NEXT_BLOCK calls. Other transport drivers will be more closely coupled with a data driver. For example, the Fax transport driver will require additional information about the fax being sent and will insist on a particular data driver being used.

The transport driver will select one (or none) of the formats and return it to the controller, which will return it to the application. If the transport driver indicates none of the formats is acceptable, the message controller will inform the user that the selected object (page, document, whatever) cannot be sent via the selected medium. The transaction will then be at an end.

Each data format descriptor consists of two 32-bit tokens (composed of the driver's manufacturer ID combined with a 16-bit constant). One token indicates the underlying storage format and refers to a single well-defined data driver. The other token defines the way data are stored in that underlying storage format. From an application's point of view, each data format token can only be used with one particular storage type token. Passing both allows things like the pager driver, described earlier, to be more flexible in how a message is stored. Such flexibility is not needed for the application; as far as it is concerned, a particular data driver is always associated with a particular data format.

If the Mailbox library is asked to load a data driver using a storage type descriptor with which it is not familiar, it will search the appropriate directory, loading each driver it's not seen before and asking it for its storage type token. This allows the work for a non-standard driver to be delayed as long as possible.

For argument's sake, let's say the transport driver indicated that its preferred format is that of a HugeArray with variable-sized elements. The application will ask the Mailbox library for a VM file to use, then convert the page into such an array, presumably placing each field of the address page into its own array element. Any references between the pieces are stored as indices into the array. For example, if the page is made up of the individual entries within the address card, plus a header that contains the names for each entry and a pointer to the entry data, the header must store the array index of the entry data, not a VM block + offset or even a DB group & item. Avoiding reference to individual VM blocks allows the data to be transmitted and stored in arbitrary VM blocks of a data file on the remote side.

The application would then pass a data format descriptor for an address book page stored in a HugeArray, and a storage type descriptor of a VM tree (HugeArrays are based on VM trees).

(If the data are just a stream, without internal pointers to different parts of it, the data can be put into a straight VM chain, rather than a HugeArray. For example, an application that sends just text across would have no need of a HugeArray, but could simply use a VM chain of the text blocks. It would then pass a data format descriptor indicating the thing is an address book page in a VM chain, but the storage type descriptor would still be for a VM tree.)

The matter of which VM file should be used bears a little bit of discussion, as well. If the data that make up the body of a message are already present in the application's document, by all means pass the address to them and mark the body data as volatile (you may not be about to free them, but you also can't guarantee the Mailbox library that they'll be around when the message needs to be sent). If you need to perform any conversion, however, you should use the Mailbox library's utility routines to obtain an appropriate VM file. Then you don't need to mark the body as volatile and the Mailbox library is saved the trouble and time of copying them into its own VM file for safekeeping.

Registering the Message

Having created the appropriate HugeArray, the application then calls the message controller back to finally register the message with the Mailbox library.

The controller will determine the time bounds, and the transport driver. It will call the address controller to fetch the block of data to be stored in the transport descriptor. The application provides the following information in its call to the message controller:

  • The destination is another copy of the same application (thus, it passes its own token).
  • The user should be notified of the arrival of the message, when appropriate. (The message is travelling First-Class.)
  • The storage format is a VM tree.
  • The data format is an address book page in a HugeArray.
  • The location of the data (the VM file & block handle of the HugeArray).
  • The message data are not volatile, as they are stored in a VM file provided by the Mailbox library.
  • The subject line, which is something like "Address book entry for name ", where name is the Name field of the entry.

As for any registered message, the Mailbox library will call the data driver, passing it the HugeArray's address and the data-is-volatile flag. The VM tree data driver will copy the array to a private VM file and return that file's name & block handle for storing in the message descriptor.

After the controller method returns, the application must remember to call the Mailbox library to tell it the application is done using the VM file in which it stored the message body. It must wait until after the message is registered to do this.

Transmitting the Message

Once the message is in the Mailbox library's Outbox, it still needs to be sent to the destination computer. When the message is safely in the outbox, the Mailbox library will put up a confirmation box that allows the user to send the message now or later. Assuming the user chooses to delay the message, the user will (at some point in the future) bring up the outbox control panel and choose to see the messages destined for InfraRed (if InfraRed is the only transport with messages stored for it, InfraRed will be brought up automatically). The user will then click the Beam All trigger in the main control panel. This will prompt the Mailbox library to load the InfraRed transport driver and tell it to create a connection to the address stored in the message.

If the connection is successful, the Mailbox library will instruct the transport driver to send each message in turn, passing in the message descriptor. The transport driver, having connected to the well-known mailbox port on the remote machine, will send across the message descriptor, indicating the data format and the destination application, followed by the blocks of the message. The data blocks are obtained by calling the data driver for the message.

On the receiving side, the receipt of the first bytes of the connection request will cause the socket driver for the InfraRed medium to be loaded. When the link is established and an attempt is made to connect to the mailbox socket, the InfraRed transport driver will be loaded to handle the connection. The transport driver will receive each block from the Socket library. The first block will allow it to load the appropriate data driver, which it will use to store the subsequent blocks. If it is unable to load the requisite data driver, it will reject the message and the sending user will be told why.

When all the data blocks have been received, the data driver will be told the message is complete, at which point it will attempt to write the data to disk (it may also have been writing it to disk block by block, or in batches of blocks if the message is particularly large; the driver is allowed to return an error from any block write call and the transport driver will call the data driver to delete what it had received so far and will discard any remaining blocks in this message as they are received, eventually returning an error to the sending transport driver). If this write fails (due to lack of disk space, for example), the receiving transport driver will return an error, which will be returned back to the Mailbox library on the sending machine. The user will be notified and the message will remain in the outbox.

Following the commitment of the message data, the destination machine's transport driver will store the message in the inbox. Again, if the descriptor cannot be committed to disk, an error will propagate back to the sending Mailbox library. Only when the message descriptor is safely stored will the remote transport driver indicate the message has been successfully transmitted.

When the receiving machine has accepted the message, the sending machine will delete the message from its outbox, freeing the message data, etc.

Delivering the Message

Once the message is stored in the inbox, it progresses exactly as for the Email message, except it's the Address Book application that will receive the data.

Depending on the priority of the message, the Mailbox library will, sooner or later, notify the user that the message has arrived. If the user chooses not to store the card right then, he will be asked again when the Address Book is next launched or another message arrives for it.

When the user gives the go-ahead for delivering the message, the application is notified, via IACP. When the application has acknowledged receipt of the message, the descriptor is removed from the inbox (though not from the file).

The application will call the Mailbox library to obtain the data format of the message body. If it can accept that format, the application will call the library again, telling it to ask the data driver for the appropriate reference to the data.

Armed with this reference, the application can then act on the message. The message body should be treated as read-only and not be modified by the application in any way.

When it's done with the message, it calls the library again to say it's done with the message (this call is passed on to the data driver to do with as it sees fit).

If the application has no further use for the message descriptor, it will call the Mailbox library to say the message should be deleted.

If the application is unable to understand the data format of the message body, it is responsible for informing the user and deleting the message.

In this case, the Address Book will learn the data are a HugeArray-based address book page, which it understands. It will ask for and receive the VM file and block handle of the HugeArray, extract the relevant pieces and merge them into the address book. In all likelihood, the application will want to confirm with the user that she really wants to do this (especially if it will be overwriting an existing entry) and to do it in the current document. If the user indicates she would rather place the record in another document, the application is free to open the other document; the Mailbox library sets no time limits on how long an application may keep a delivered message active.

Having applied the change (or not), the Address Book will tell the library it's done with the data and to delete the message.

This document is a single-page version of a a multi-page document, suitable for easy printing.


PCCom Library

The PCCom library provides a simple way to allow a geode to provide and monitor a PCCom connection. If you are familiar with the SDK, you probably think of pccom as a tool which allows the target machine to receive commands from the host machine. While the target machine runs the pccom tool, the host machine can upload and download files and otherwise manipulate the target machine.

The PCCom library allows a geode to start up a PCCom thread monitoring a serial port for purposes other than debugging. For instance, it allows the GEOS machine to receive files sent over a serial line by another machine running pcsend or another program using pccom's file transfer protocol. This allows file transfers and other pccom operations to go on in the background while the user continues to interact with GEOS.

If you're not familiar with the pccom tool, you should probably read the pccom section of the Using Tools chapter--perhaps not the whole section, but at least enough to understand basic usage.

1 PCCom Library Abilities
2 What To Do
3 Staying Informed

PCCom Library: 1 PCCom Library Abilities

Your geode needn't do much to support PCCom. The PCCom library acts as a passive pccom machine--it will only accept orders from a remote machine.

All you need to do to support PCCom is start up a PCCom process, ideally freeing it when done. Geodes using the PCCom library have the option of receiving notification when pccom would display text.

PCCom Library: 2 What To Do

PCCOMINIT(), PCCOMEXIT(), PCCOMABORT()

When you are ready to start monitoring a serial port for pccom-style communications, call PCCOMINIT(). This routine is an entry point for the library, accessible via ProcGetLibraryEntry () as the first entry point in the library, and it may also be called as a normal routine. PCCOMINIT() starts up a new thread which will monitor the passed serial port. If it cannot make the connection, it will return an error.

When you are done with PCCom, call PCCOMEXIT(), invocable as a library entry point or as a normal routine, which closes down the monitor thread and frees the serial port for other uses. After calling this routine, you must call PCCOMINIT() again if you wish to re-establish the pccom connection.

You may call the PCCOMABORT() routine at any time; this routine aborts any pccom file transfer that may be in progress, but leaves the PCCom connection intact.

PCCom Library: 3 Staying Informed

The sections above tell you everything you need to let your geode interact with pccom. It is possible to do more: your geode can receive notification whenever the pccom tool would display some text. The pccom tool displays text to show the user what's going on. Text signals the successful or unsuccessful completion of certain operations; a spinning baton shows that a file transfer is in progress. Your geode can also find out if the machine on the other side of the pccom link has quit pccom.

When calling PCCOMINIT(), your geode can specify an object which should receive notification when pccom has text to display or senses that the other side of the pccom link has quit. If an object is so specified, it will receive notification messages at these times. You must also set the PCComInitFlags argument to PCCOMINIT() such that the appropriate kinds of notification will be sent; there is one flag which asks for display text notification and another flag which asks for notification when the other side of the pccom connection exits.

Notification will come in the form of MSG_META_NOTIFY or MSG_META_NOTIFY_WITH_DATA_BLOCK . There are three possible forms of notification:

MSG_META_NOTIFY: GWNT_PCCOM_DISPLAY_CHAR
If the passed notification type is GWNT_PCCOM_DISPLAY_CHAR, then the notification's data word contains a character that pccom would display.
MSG_META_NOTIFY_WITH_DATA_BLOCK : GWNT_PCCOM_DISPLAY_STRING
If the passed notification type is GWNT_PCCOM_DISPLAY_STRING, then the data in the notification's data block is a string of characters.
MSG_META_NOTIFY : GWNT_PCCOM_EXIT_PCCOM
If the passed notification type is GWNT_PCCOM_EXIT_PCCOM, then the pccom process on the other side of the pccom connection has exited; the notification's data word contains a PCComReturnType indicating the success the other side had in exiting. The other side of the connection signals that it is exiting by sending an EX escape code over the pccom connection. A data value of PCCRT_NO_ERROR is a sign of a successful exit; other return values might signal that the exit was the result of an error.
This document is a single-page version of a a multi-page document, suitable for easy printing.


The Spool Library

Any geode can work with printers, fax machines, and other print devices by including the spool library and instantiating a PrintControl object to handle system interactions. The PrintControlClass provides the printing interface. It includes both user interface and an interface between the geode and the printing thread. There is also a PageSizeControlClass which provides UI concerning page layout.

Before reading this chapter, you should be familiar with the graphics system.

1 Introduction to Printing
2 Simple Printing Example
3 How Jobs Get Printed
3.1 Printing System Components
3.2 Chronology
4 Print Control Instance Data
4.1 Alerting the GenApplication
4.2 Attributes
4.3 Page Range Information
4.4 Document Size
4.5 Print Output Object
4.6 Document Name Output
4.7 The Default Printer
4.8 Adding UI Gadgetry
5 Print Control Messages
5.1 Common Response Messages
5.2 Flow of Control Messages
5.3 Working with Instance Data
6 Page Size Control
7 Other Printing Components
7.1 Spooler and Scheduling
7.2 Printer Drivers
7.3 Page Size Related Routines
8 Debugging Tips

The Spool Library: 1 Introduction to Printing

By including a PrintControl object, an application gains access to the powers of the GEOS printing system. Below are several of the features built into GEOS:

  • Single Imaging Model
    You'll use the same commands to describe print jobs as you did for screen drawings. These commands work for all supported printers. Thus, anything you can draw can be printed, and the application doesn't have to worry about what model of printer is being used.
  • Background Printing
    Background printing allows users to continue working while their documents print. This is an example of multitasking at work. A process known as the Spooler spawns a separate thread for each active printer. Since each printer's thread priority is low, printing will take place in the background, interfering little with the user's interactions. If you want printing to take place in the foreground, your application can boost the priority of a printer thread.
  • Standard UI Components
    The PrintControl is a full-featured control object and it provides a UI mechanism for finding out what a user wants to print. For user choices which depend on the printer (for example, some printers have no low-quality mode), the PrintControl will take printer abilities into consideration.
  • Control Over Scheduling
    The Spooler normally handles jobs in FIFO (first in, first out) order. Geodes can exercise a great deal of control over the Spooler's scheduling of jobs. Any scheduling options the user might request using the Printer Control Panel, the application can request using spool routines.
  • Managing the Printer Yourself
    If you can't or won't use standard GEOS commands to describe a print job, you can use Raw Mode to send a packet of commands in the printer's own language. For example, you could send escape codes or commands from a page description language. Since this is a standard printing mode, you still benefit from all the usual spool scheduling features.
  • Management of Multiple Printers
    The user may have multiple printing devices hooked up and installed at one time. The system handles this situation intelligently: the Spooler maintains a separate queue for each serial or parallel port. Thus, if two printers are installed on two different ports, both may print at once. If two or more printers are installed on the same port then they share a queue. Thus, jobs for one printer won't try to print at the same time as those for another on the same port. To make sure the job prints to the correct printer, the Spooler will put up a dialog box advising the user to put the correct device on line.

The Spool Library: 2 Simple Printing Example

Adding printing capability to an application is fairly simple, especially if it will be printing WYSIWYG. Somewhere in the application there is probably a message handler which is in charge of drawing the document. By including a Print Control object and writing handlers to some standard messages, it is a simple manner to redirect the document drawing commands to a printer.

Code Display 14-1 Printing Example

@include <spool.goh>
@object GenApplicationClass MyApp = {
    /* ...Much other GenApplication instance data omitted... */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) =
         @MyPrintControl;
    ATTR_GEN_APPLICATION_PRINT_CONTROL = @MyPrintControl;
}
@object PrintControlClass MyPrintControl = {
/* The PrintControlClass is a subclass of GenControlClass. Its "features"
 * correspond to the "Print" and "Fax" triggers in the File menu. Its instance
 * data contains information about the document to be printed, and links to
 * objects which will provide information. */
/* Single page documents would leave out page range UI from the Print dialog:
    PCI_attrs = 		(@default & ~(PCA_PAGE_CONTROLS| PCA_VERIFY_PRINT));
    PCI_startUserPage = 1;
    PCI_endUserPage = 1 */
/* The PCI_output, or Print Output object, is in charge of supplying the graphics
 * commands describing the print job whenever the user wants to print. This object
 * is expected to handle a MSG_PRINT_CONTROL_START_PRINTING. */
 * The print output object is normally either the model, target or process. */
    PCI_output = 		( TO_APP_TARGET ) ;
/* The PCI_docNameOutput object is supposed to provide the name of the document
 * on demand. This name is used to identify the document to the user in the Print
 * Control Panel. If this object is a GenDocumentGroup, it automatically does this.
 * If the object is not a GenDocumentGroup object, then * it must have a handler
 * for MSG_PRINT_GET_DOC_NAME that sends a nmae to the PrintControl via
 * MSG_PRINT_CONTROL_SET_DOC_NAME. */
    PCI_docNameOutput = MyGenDocumentGroup;
/* The PCI_docSize field specifies the size of the document to print (not to
 * be confused with the size of the printer's paper). */
/* Many simple applications only support one document size,
 * and may specify it in the PrintControl and never bother with it again.
 * We're setting the margins to all zero because they'll be updated via
 * MSG_PRINT_CONTROL_GET_MARGINS. */
    PCI_docSizeInfo = {			(15/2*72),
                (19/2*72),
                0,
                {0, 0, 0, 0}}; */
/* Many simple applications do not support multiple documents. These applications
 * should set the GS_ENABLED flag of the PrintControl's GI_states field. */
}
@method MVTStartPrinting, MyVisTargetedClass, MSG_PRINT_START_PRINTING
/* We've set up our Print Control to send this message to the Target, so whatever
 * object will have the target when the user wants to print needs a handler for
 * this message. We could have easily have put the Model or process in charge
 * of printing, changing PCI_output accordingly. It is often convenient to use
 * the same object to handle drawing to screen and to the printer. */
/* Arguments:		optr 		printControlOD,
        GStateHandle		gstate */
{
    PCMarginParams 	margins;
/*
 * 	Applications which allow the user to change the document size may handle
 *	the situation in more than one way. If the user has changed the page setup
 *	by working with a PageSizeControl, then the application has probably
 * 	already been alerted to the page size. If the application has its own
 *	way of computing document size, it should use it. Such applications should
 * 	send MSG_PRINT_CONTROL_SET_DOC_SIZE and MSG_PRINT_CONTROL_SET_DOC_MARGINS
 * 	to the PrintControl, either in this handler or else whenever the page size
 *	changes. Either time is fine, just so long as the document size is set
 * 	correctly by the time this MSG_PRINT_START_PRINTING handler is finished.
 *	Applications	 supporting only a single document size should probably set
 *	the size (and margins) in the PrintControl's instance data, as shown
 *	above. It is also possible to send a MSG_PRINT_CONTROL_SET_DOC_SIZE on
 *	every print, but if the size never changes, this is a bit wasteful.
 *	This application supports only one size of document. If the document
 *	doesn't fit on the page, the spooler will tile it onto multiple pages
 *	as necessary. It uses the printer's margins as the document margins: */
    @call 	MyPrintControl::MSG_PRINT_CONTROL_GET_PRINTER_MARGINS(
        &margins, 		/* Fill in structure with printer's margins */
        TRUE);		/* ...and automatically use printer margins
                 * as document margins */
    @call 	self::MSG_VIS_DRAW(DF_PRINT, gstate);
    /* If the MSG_VIS_DRAW handler didn't end with a form feed, put one in:
    GrNewPage(gstate, PEC_FORM_FEED); */
    @send 	MyPrintControl::MSG_PRINT_CONTROL_PRINTING_COMPLETED();
}
@method MVTDraw, MyVisTargetClass, MSG_VIS_DRAW
/* Chances are, whatever object this is has some sort of draw handler already.
 * However, if you're building up this class from scratch, you'll be glad to
 * know that commands of the following form work just as well drawing in
 * response to a print request as they do drawing anything else: */
{	GrSetLineColor(gstate, CF_INDEX, C_RED, 0, 0);
    GrDrawLine(gstate, 144, 144, 288, 288);
    GrNewPage(gstate, PEC_FORM_FEED);
}

The Spool Library: 3 How Jobs Get Printed

You may wonder how and when these various features get implemented. Simply stated, the application describes the print job, which gets placed onto a queue where it remains until it is fed to a printer. For many programmers, that's enough to know. If you want a more complete explanation, read on.

Printing System Components

Chronology

The Spool Library: 3.1 How Jobs Get Printed: Printing System Components

Printing involves the concerted effort of several objects running in different threads. The most important pieces of the printing system are

PrintControl
The printing system is big and would be difficult to interact with if not for the Print Control, which acts as a sort of intermediary. This object is a member of the PrintControlClass , a GenControl subclass provided by the spool library. It handles the UI, communication with the spool thread, and most other printing functions. Every geode which is going to print includes its own Print Control, placing the control object in its generic tree.
Print Output
The Print Output is an object (often the process object) chosen by the geode when creating the Print Control. This object is in charge of building print jobs and must be prepared to provide page descriptions whenever it receives a MSG_PRINT_START_PRINTING from the Print Control.
Spooler
The Spooler handles most of the important "behind the scenes" work of printing. As mentioned, it handles the scheduling of jobs and manages the operations by which generic print jobs are translated for specific printers.
Printer Driver
GEOS printer drivers handle the back end of printing. GEOS has many printer drivers, each of which serves one or more models of printer. The driver supplies the Spooler with information about the printer and makes some final adjustments to the translated print job to get it ready for the specific printer.

The Spool Library: 3.2 How Jobs Get Printed: Chronology

Now that you're somewhat familiar with these components, you're ready for the detailed account of how they work together to print a job. Note that the application is only involved at one point. The printing system takes care of the rest automatically.

  1. User Activates Trigger
     Printing normally begins when the user activates the User Print Trigger provided by the Print Control. This trigger sends a message to the Print Control telling it to put up the Print Dialog box.
  • User Interacts with the Print Dialog Box
     The Print Control responds to the Print Trigger's message by presenting a dialog box in which the user selects such things as page range and print quality (see. The user then either initiates printing or cancels. If the user wants to print, he'll confirm this by clicking on the appropriate trigger. The application will then get a chance to veto the user's choices. The Print Control asks the dialog box to remove itself, and it then gets ready for the upcoming print job.
  • Print Control Prepares for New Job
     Soon the Print Control is going to ask for the page descriptions, but first it needs a place to store the data. It creates a spool file in which to store the upcoming graphics commands. It also allocates a GString handle by which the Print Output can transmit the commands. It then sends a message to the application's Print Output, signalling that the Print Output should supply the job.
  • Application Supplies Job
     Having received the Print Control's message, the Print Output supplies graphics routines to the provided GString handle, using the same commands as when printing to any other GState. This string of graphics commands ends with a message saying the job is completed. The Print Control supplies some extra information to the spooler, and the print job is ready to enter the queue.
  • Job Traverses Queue
     At this point, the spool library places the print job on a printer queue where the job waits to be printed. (Actually, the Spooler uses a trick to save on overhead. If there are no jobs for a printer, the Spooler doesn't maintain a queue for that printer. When a new job comes in for a printer that has no queue so far, a new queue is prepared for that printer and the job placed on the new queue. When the last job of a queue is printed and there are no more jobs forthcoming, the queue is removed.) The Spooler's various scheduling powers, if exercised, affect jobs in the queue.
  • Printing
     Eventually, the job reaches the head of the queue. It is now ready to print. The Spooler works with the Printer Driver to transform the print job into a form that the printer can work with. This may involve building a bitmap depicting the job, or it might involve translating the GString's commands into the commands of some other page description language, such as PostScript.
  • Hard Copy
     The printer driver sends the appropriate printer commands to the printer. If the printer doesn't have sufficient memory to hold the whole job, the driver sends only part of the job at a time.

The Spool Library: 4 Print Control Instance Data

The PrintControl is a powerful and adaptable subclass of GenControl and contains many instance fields to fit the needs of different types of applications. Each field is described in greater detail later in this section; all are shown in Print Control Instance Data and Features .

Code Display 14-2 Print Control Instance Data and Features

        /* The following bitfield contains the PrintControl's attributes */
    @instance PrintControlAttrs 		PCI_attrs =
                (PCA_COPY_CONTROLS | PCA_PAGE_CONTROLS |
                 PCA_QUALITY_CONTROLS | PCA_USES_DIALOG_BOX |
                 PCA_GRAPHICS_MODE | PCA_TEXT_MODE );
        /* Possible PCI_attrs flags (may be combined using | and &):
         * PCA_MARK_APP_BUSY, 				PCA_VERIFY_PRINT,
         * PCA_SHOW_PROGRESS, 				PCA_PROGRESS_PERCENT,
         * PCA_PROGRESS_PAGE, 				PCA_FORCE_ROTATION
         * PCA_COPY_CONTROLS,				PCA_PAGE_CONTROLS,
         * PCA_QUALITY_CONTROLS, 				PCA_USES_DIALOG_BOX,
         * PCA_GRAPHICS_MODE,				PCA_TEXT_MODE
         * PCA_DEFAULT_QUALITY						*/
        /* The fields below are the complete and requested page ranges */
    @instance word 		PCI_startPage = 1;
    @instance word 		PCI_endPage = 1;
    @instance word 		PCI_startUserPage = 0;
    @instance word 		PCI_endUserPage = 0x7fff;
        /* The following field contains the default printer number */
    @instance word 		PCI_defPrinter = -1;
        /* The following fields deal with document dimensions */
    @instance PageSizeReport 		PCI_docSizeInfo = 0;
        /* Pointers to objects receiving vital messages */
    @instance optr 		PCI_output;
    @instance optr 		PCI_docNameOutput;
        /* The PrintControl's features determine whether to use standard
         * print and fax triggers to bring up the Print Dialog box. */
      typedef ByteFlags PrintControlFeatures;
    /* The following flags may be combined with | and &:
         PRINTCF_PRINT_TRIGGER,
         PRINTCF_FAX_TRIGGER */
    /* To provide a non-standard print trigger, use the GenControl vardata
     * field ATTR_GEN_CONTROL_APP_UI. */
    typedef ByteFlags PrintControlToolboxFeatures;
    /* The following flags may be combined with | and &:
        PRINTCTF_PRINT_TRIGGER
        PRINTCTF_FAX_TRIGGER */
    /* To include application-specific UI in the print dialog box. */
    @vardata optr ATTR_PRINT_CONTROL_APP_UI;
    /* This piece of temporary vardata is internal: */
    @vardata TempPrintCtrlInstance  TEMP_PRINT_CONTROL_INSTANCE;
    /* Its structures are defined as follows:
    typedef struct {
     optr 			TPCI_currentSummons; ( currently active summons )
     optr 			TPCI_progressBox; ( OD of progress dialog box */
     ChunkHandle 			TPCI_jobParHandle; ( handle to JobParamters )
     word 			TPCI_fileHandle; ( file handle (if printing) )
     word 			TPCI_gstringHandle; ( gstring handle if printing )
     word 			TPCI_printBlockHan; ( the printer block handle )
     PrintControlAttrs 			TPCI_attrs;
     PrintStatusFlags 			TPCI_status;
     byte 			 TPCI_holdUpCompletionCount;
    }  TempPrintCtrlInstance;
    typedef ByteFlags  PrintStatusFlags;
    #define  PSF_FAX_AVAILABLE 0x80 ( set if a fax driver is available )
    #define PSF_ABORT 0x08 ( user wants to abort printing )
    #define PSF_RECEIVED_COMPLETED 0x04 ( MSG_..._PRINTING_COMPLETED received )
    #define PSF_RECEIVED_NAME 0x02 ( MSG_PC_SET_DOC_NAME received )
    #define PSF_VERIFIED 0x01 ( PSG_PC_VERIFY_? received ) */
    @vardata TempPrintCompletionEventData  TEMP_PRINT_COMPLETION_EVENT;
    /* The TempPrintCompletionEventData structure is defined:
        typedef struct {
         MemHandle 		 TPCED_event;
         MessageFlags 		TPCED_messageFlags;
        }  TempPrintCompletionEventData;

Alerting the GenApplication

Attributes

Page Range Information

Document Size

Print Output Object

Document Name Output

The Default Printer

Adding UI Gadgetry

The Spool Library: 4.1 Print Control Instance Data: Alerting the GenApplication

PrintControl objects should be placed on the GenApplication's MANUFACTURER_ID_GEOWORKS/GAGCNLT_SELF_LOAD_OPTIONS general change notification list. Also, if the application is to allow printing of documents from the file from the file manager, then the PrintControl's optr should be specified in the GenApplication's ATTR_GEN_APPLICATION_PRINT_CONTROL field.

The Spool Library: 4.2 Print Control Instance Data: Attributes

PCI_attrs, MSG_PRINT_CONTROL_SET_ATTRS, MSG_PRINT_CONTROL_GET_ATTRS

The Print Control includes several attributes which are grouped together into a record of type PrintControlAttrs . These represent some choices made when instantiating the Print Control.

PCA_MARK_APP_BUSY
Describing large print jobs can take a fair amount of time. If this bit is set, the application will reassure the user that it is busy (by showing the busy cursor) while spooling long print jobs.
PCA_VERIFY_PRINT
If this bit is set, the Print Control will ask the application for confirmation before printing anything. This request will come in the form of a MSG_PRINT_VERIFY_PRINT_REQUEST sent to the PCI_output object. The Print Control sends this message after the user has finished interacting with the Print dialog box so that the application can make sure that the user has made valid choices.
PCA_SHOW_PROGRESS, PCA_PROGRESS_PERCENT, PCA_PROGRESS_PAGE
When describing a large print job, it is considerate to let the user know when the application is making some kind of progress. If the first of these bits is set, the Print Control will display a progress dialog box when spooling a print job. The next two bits determine whether progress will be reported as a percentage, number of pages completed, both, or neither. Regardless of whether percents or page number progress is reported, the progress box can display an application-supplied string.
PCA_FORCE_ROTATION
If this bit is set, the output will automatically be printed rotated on the page. Normally, this bit is not set so the Spooler will make a choice to make the job fit on the minimum number of pages. The Banner program sets this bit to make sure that fanfold paper printers will create continuous banners; if it didn't print sideways, then users would have to use a lot of tape to put their banners together (they have to anyhow, if using non continuous-feed paper).
PCA_COPY_CONTROLS
This flag determines whether the Print Control will allow the user to request that multiple copies be printed. If your application uses this option, then it is vital that the print control be updated whenever the total page count of the document changes.
PCA_PAGE_CONTROLS
If this bit is set, the user will be allowed to select specific page ranges to print. Even if it seems like users would have little reason to print short ranges, it's still nice to include this option: if the printer messes up one page, it's usually irritating to have to reprint the whole document.
PCA_QUALITY_CONTROLS
If this flag is on, the user will be allowed to select which quality to print with. Note that not all printers can print at all qualities, but most support more than one mode.
PCA_USES_DIALOG_BOX
This bit determines whether the Print Control will display a dialog box containing printing choices for the user. Specialty geodes and GCM applications might want to turn this flag off; it is set by default.
PCA_GRAPHICS_MODE, PCA_TEXT_MODE
These bits determine whether the Control should support graphic mode and/or text mode output.
PCA_DEFAULT_QUALITY
These two bits contain the default print quality to use. The enumerated type PrintQualityEnum defines the values available: PQT_HIGH, PQT_MEDIUM, and PQT_LOW.

Those attributes which are on by default are PCA_COPY_CONTROLS, PCA_PAGE_CONTROLS, PCA_QUALITY_CONTROLS, PCA_USES_DIALOG_BOX, PCA_GRAPHICS_OUTPUT, and PCA_TEXT_OUTPUT.

There are messages to get and set these attributes.

MSG_PRINT_CONTROL_SET_ATTRS
void	MSG_PRINT_CONTROL_SET_ATTRS(
        PrintControlAttrs 	attributes);

Use this message to change the values stored in the PCI_attrs structure. Pass a record of type PrintControlAttrs containing the desired values.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: attributes The new PrintControlAttrs .

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_ATTRS
PrintControlAttrs 	MSG_PRINT_CONTROL_GET_ATTRS();

Use this message to retrieve the values stored in the PCI_attrs structure. It will return a record of type PrintControlAttrs containing the desired values.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: The present print control attributes.

Interception: Unlikely.

The Spool Library: 4.3 Print Control Instance Data: Page Range Information

PCI_startPage, PCI_endPage, PCI_startUserPage, PCI_endUserPage, MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE, MSG_PRINT_CONTROL_GET_TOTAL_PAGE_RANGE, MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE, MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE

The PCI_startPage and PCI_endPage fields should contain the page numbers of the first and last pages possible to print, known as the total page range. The PCI_startUserPage and PCI_endUserPage contain the beginning and ending page numbers of the range the user wants to print, often called the user page range.

For documents whose page length may change (such as word processor documents), the page ranges should be updated whenever the Print dialog box goes up, at which time the application will be receiving a MSG_PRINT_NOTIFY_PRINT_DB . Normally, the user page range will start out being the same as the total page range. For most documents the first page of the total page range is page one, but applications which will include cover sheets might want the cover sheet to be page zero.

MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE
void	MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE(
        int 	firstPage,
        int	lastPage);

The application should send this message to set the first and last page numbers of the document. You might want to send this message every time the document length changes or just every time the Print dialog box is put up. The Print Control warns the PCI_output object of the occurrence of the latter event with a MSG_PRINT_NOTIFY_PRINT_DB .

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: firstPage The page number of the document's first page.

lastPage
The page number of the document's last page.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_TOTAL_PAGE_RANGE
dword	MSG_PRINT_CONTROL_GET_TOTAL_PAGE_RANGE();

This message returns two integers. These integers are the numbers of the first and last pages of the range of possible pages. These values aren't necessarily the first and last pages of the range the user wants to print. Use MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE for that information.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: A double word. The high word is the first page; the low word is the last page.

Interception: Unlikely.

MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE
void	MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE(
        int 	firstPage,
        int	lastPage);

This message takes the passed values and uses them as the first and last pages that the user wants to print.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: firstPage The page number of the first page to print.

lastPage
The page number of the last page to print.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE
dword	MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE();

This message returns the user's selected range of pages to print.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: A double word. The high word is the first page which the user wishes to print; the low word is the last page of this range.

Interception: Unlikely.

The Spool Library: 4.4 Print Control Instance Data: Document Size

 PCI_docSizeInfo, MSG_PRINT_CONTROL_SET_DOC_SIZE, MSG_PRINT_CONTROL_GET_DOC_SIZE, MSG_PRINT_CONTROL_SET_DOC_MARGINS, MSG_PRINT_CONTROL_GET_DOC_MARGINS, MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE, MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE, MSG_PRINT_CONTROL_SET_DOC_SIZE_INFO, MSG_PRINT_CONTROL_GET_DOC_SIZE_INFO

It is possible to specify the size of the document when creating the Print Control. Note that this is the size of the document, not the size of the piece of paper. Ideally, the document should fit on the paper, though obviously in the case of huge documents like some spreadsheets, this may not be the case. The document size includes the margin size; it is possible to set the margin size as well.

The document size must be set correctly before the document is finished printing. If all documents the application produces have the same dimensions (or if you want some size to be the default), you may specify dimensions for the document when instantiating the print control. You may also set up document margins at this time.

The messages listed above get and set the document and margin sizes. You must use MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE and MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE when working with the dimensions of 32-bit extended documents.

MSG_PRINT_CONTROL_SET_DOC_SIZE
void	MSG_PRINT_CONTROL_SET_DOC_SIZE(
        int 	width,
        int	height);

This message changes the values of the document size. It takes two integers, representing the new width and height for the document to use. It can only use 16-bit values, so use MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE when working with 32-bit extended graphics spaces.

Remember that the document size includes the document margins.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: width The document's width, in points.

height
The document's height, in points.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_DOC_SIZE
dword	MSG_PRINT_CONTROL_GET_DOC_SIZE();

Use this message to retrieve the present document size. The size is returned as a width and height, each expressed in points.

Note that if the size might be a 32-bit value (which might happen if the document uses an extended graphics space), you must use the MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE . If either dimension is a 32-bit number, using a regular MSG_PRINT_CONTROL_GET_DOC_SIZE will result in an error.

Remember that the document size includes the document margins.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: A double word. The high word is the width, the low word is the height.

Interception: Unlikely.

MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE
void	MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE(
        PCDocSizeParams 	*ptr);

This message changes the values of the document size using the two passed double integers as the new width and height to use. When working with normal 16 bit graphics spaces, use MSG_PRINT_CONTROL_SET_DOC_SIZE instead.

Remember that the document size includes the document margins.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to a PCDocSizeParams structure containing the document size.

Return: Nothing.

Interception: Unlikely.

Structures: The PCDocSizeParams structure has the following definition:

typedef struct {
    dword		PCDSP_width;
    dword		PCDSP_height;
} PCDocSizeParams;
MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE
void	MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE(
        PCDocSizeParams 	*ptr);

Use this message to retrieve the present document size. It returns two double integers representing the width and height, expressed in points. If the size is a 16 bit value, you can use MSG_PRINT_CONTROL_GET_DOC_SIZE instead.

Remember that the document size includes the document margins.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to a PCDocSizeParams structure to hold document parameters.

Return: Nothing is returned explicitly.

ptr
The structure is filled with document size.

Interception: Unlikely.

Structures: The PCDocSizeParams structure has the following definition:

typedef struct {
    dword		PCDSP_width;
    dword		PCDSP_height;
} PCDocSizeParams;
MSG_PRINT_CONTROL_SET_DOC_MARGINS
void	MSG_PRINT_CONTROL_SET_DOC_MARGINS(
        PCMarginParams 	*ptr);

Use this message to set new values for the document margins. It takes four arguments, the point values to use for the left, top, right, and bottom margins.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to a PCMarginParams structure with new document margins.

Return: Nothing.

Interception: Unlikely.

Structures: The PCMarginParams structure has the following definition:

typedef struct {
    word		 PCMP_left;
    word		PCMP_top;
    word		PCMP_right;
    word		PCMP_bottom;
} PCMarginParams;
MSG_PRINT_CONTROL_GET_DOC_MARGINS
void	MSG_PRINT_CONTROL_GET_DOC_MARGINS(
        PCMarginParams 	*ptr);

Use this message to get the present values for the document margins. It returns four integers. These integers represent the left, top, right, and bottom margins, expressed in typographer's points.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to a PCMarginParams structure which will hold return value.

Return: Nothing returned explicitly.

ptr
Structure filled in with document margins.

Interception: Unlikely.

Structures: The PCMarginParams structure has the following definition:

typedef struct {
    word		PCMP_left;
    word		PCMP_top;
    word		PCMP_right;
    word		PCMP_bottom;
} PCMarginParams;
MSG_PRINT_CONTROL_SET_DOC_SIZE_INFO
void	MSG_PRINT_CONTROL_SET_DOC_SIZE_INFO(
        PageSizeReport 	*ptr);

Use this message to set all of the information about the document size and orientation.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to a PageSizeReport structure.

Return: Nothing.

Interception: Unlikely.

Structures: The PageSizeReport structure has the following definition:

typedef struct {
    dword			PSR_width;
    dword			PSR_height;
    PageLayout			PSR_layout;
    PCMarginParams 			PSR_margins;
} PageSizeReport;
MSG_PRINT_CONTROL_GET_DOC_SIZE_INFO
void	MSG_PRINT_CONTROL_GET_DOC_SIZE_INFO(
        PageSizeReport 	*ptr);

Use this message to set all of the information about the document size and orientation.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to an empty PageSizeReport structure which the message handler will fill in.

Return: Nothing.

Interception: Unlikely.

Structures: The PageSizeReport structure has the following definition:

typedef struct {
    dword			PSR_width;
    dword			PSR_height;
    PageLayout			PSR_layout;
    PCMarginParams 			PSR_margins;
} PageSizeReport;

The Spool Library: 4.5 Print Control Instance Data: Print Output Object

PCI_output, MSG_PRINT_START_PRINTING, MSG_PRINT_VERIFY_PRINT_REQUEST, MSG_PRINT_NOTIFY_PRINT_DB

Some object is going to describe the print jobs to the Print Control. When instantiating the Print Control, set the chosen object's name in the PCI_output field:

PCI_output = <yourObject>;

There are three messages the Print Control may send to its output: MSG_PRINT_START_PRINTING, MSG_PRINT_VERIFY_PRINT_REQUEST, and MSG_PRINT_NOTIFY_PRINT_DB . The Print Output must respond to MSG_PRINT_START_PRINTING , which is the signal that it's time to describe a print job. The Print Output also must respond to MSG_PRINT_VERIFY_PRINTING if the PrintControl's PCA_VERIFY_PRINT bit has been set. Otherwise, it need not handle this message. Finally, the Print Output may have a handler for the MSG_PRINT_NOTIFY_DB , a message the PrintControl will send whenever the Print dialog box comes up or goes away.

The Print Method

Assuming your geode doesn't try to do anything fancy with its own printing UI or weird scheduling, probably the most complicated thing you'll have to do when adding printing capability to your geode is write a Print Method.

This message must be handled by the Print Output object, though the handler probably won't be too complicated. In its simplest form, a MSG_PRINT_START_PRINTING handler could just call some graphics commands and finish off by sending MSG_PRINT_CONTROL_PRINTING_COMPLETED to the PrintControl.

The message is accompanied by a pointer back to the Print Control and a GString handle. Any graphics commands drawn to the GString handle will be retained and will become part of the print job.

Before looking at examples, be warned that there are some requirements that every Print Method must meet:

  • Be sure the document size has been set correctly. Before the job is finished and you send MSG_PRINT_CONTROL_PRINTING_COMPLETED , you must be certain the document's size and margins have been set somewhere. This may have been set using the PCI_docSizeInfo field when defining the Print Control. If you haven't already let the Print Control know about the document size elsewhere, then you must send a MSG_PRINT_CONTROL_SET_DOC_SIZE , probably either in the MSG_PRINT_START_PRINTING or wherever your geode sets up document information in general.
  • Be certain the number of pages to be printed has been correctly set. If your application hasn't already set the page range (when the Print Control was created or when the Print dialog box goes up are popular times for this), send a MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE to your Print Control. Make sure you do it before sending in the MSG_PRINT_CONTROL_PRINTING_COMPLETED .
  • Be sure the document name has been set. If you use a GenDocumentGroup as your PCI_docNameOutput , this will be taken care of for you. Otherwise, some time before the MSG_PRINT_CONTROL_PRINTING_COMPLETED is sent, your PCI_docNameOutput object is going to be asked for the document name, and the job won't be spooled until your PCI_docNameOutput sends the correct name back to the Print Control.
  • The Print Method should end each page with a GrNewPage() ; the last thing drawn before the print job ends is a GrNewPage() .
  • Finally, when the Print Method is finished describing the print job, it must end with a MSG_PRINT_CONTROL_PRINTING_COMPLETED . Otherwise, the Print Control has no way of knowing the job is ready.

When the Print Control receives the message MSG_PRINT_CONTROL_PRINTING_COMPLETED , it responds by cleaning up and sending the print job to the spooler.

Your handler can send a MSG_PRINT_CONTROL_REPORT_PROGRESS to the Print Control if it is spooling a large job and wants to reassure the user. If something goes wrong, or if MSG_PRINT_CONTROL_REPORT_PROGRESS returns a signal indicating the user wishes to cancel, MSG_PRINT_START_PRINTING should send a MSG_PRINT_CONTROL_PRINTING_CANCELLED instead.

MSG_PRINT_START_PRINTING
void	MSG_PRINT_START_PRINTING(
        optr		printControlOD,
        GStateHandle		gstate);

The handler for this message should call a number of graphics routines, using the passed GState. When done with graphics routines, the handler should send a MSG_PRINT_CONTROL_PRINTING_COMPLETED (or MSG_PRINT_CONTROL_PRINTING_CANCELED ) to the Print Control. The handler may have to accomplish other tasks; see the above bulleted list (the one marked as "the most used and useful section of this chapter").

Source: PrintControl object.

Destination: The object specified in PCI_output .

Parameters: printControlOD The optr of the PrintControl object.

gstate
The GState handle to draw to.

Return: Nothing.

Interception: The Print output object must intercept this message, build the print job by drawing to the passed GState, then send MSG_PRINT_CONTROL_PRINTING_COMPLETED to the object in printControlOD .

Verifying User Choices

You may write a handler for MSG_PRINT_VERIFY_PRINT_REQUEST , which will be sent to the Print Output if the PCA_VERIFY_PRINT bit has been set. The message will be sent after the user has dismissed the Print dialog box, after the system has had a chance to make sure the document fits on the paper but before the document is actually spooled.

This message gives the application a chance to check out the state of the UI and make sure that the user's choices are valid. Note that the PrintControl does its own checking to make sure that the document will fit on the page. After examining the UI, the handler must send back a MSG_PRINT_CONTROL_VERIFY_COMPLETED , passing the argument to say whether it's okay to print.

MSG_PRINT_VERIFY_PRINT_REQUEST
void	MSG_PRINT_VERIFY_PRINT_REQUEST(
        optr 	printControlOD);

The handler for this message should make whatever checks are necessary to make sure that the user has made valid choices with the printing UI. The handler should then send back an indication of whether the user's choices are all right by means of a MSG_PRINT_CONTROL_VERIFY_COMPLETED.

Source: PrintControl object.

Destination: The object specified in PCI_output .

Parameters: printControlOD The optr of the PrintControl object.

Return: Nothing explicitly. However, the handler for this message must send a MSG_PRINT_CONTROL_VERIFY_COMPLETED back to the print control.

Interception: If you've set the PCA_VERIFY_PRINT flag, you must intercept this message correctly or else the print job will never start.

Dialog Box Notification

The Print Control will send the Print Output a MSG_PRINT_NOTIFY_DB every time the Print dialog box comes up or goes away. Note that this message allows the application to update the page range and print group's UI at only those times that updates are needed.

This message will arrive with one argument, a pointer back to the PrintControl object that sent it. The handler for this message might wish to update the page range information using MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE and MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE .

MSG_PRINT_NOTIFY_PRINT_DB
void	MSG_PRINT_NOTIFY_PRINT_DB(
        optr		printControlOD,
        PrintControlStatus		pcs);

The handler for this message can do any almost anything; this message signals that the Print dialog box has just come up or gone away.

Source: PrintControl object.

Destination: The object specified in PCI_output .

Parameters: printControlOD The optr of the PrintControl object.

pcs
The status of the print control; either PCS_PRINT_BOX_VISIBLE or PCS_PRINT_BOX_NOT_VISIBLE.

Return: Nothing.

Interception: Some Print Output objects will intercept this message to set page ranges.

The Spool Library: 4.6 Print Control Instance Data: Document Name Output

PCI_docNameOutput, MSG_PRINT_GET_DOC_NAME

The PCI_docNameOutput field must contain the optr of an object which can tell the Spooler the document's name. The document name is displayed on the Printer Control Panel and is how the user can figure out which job is which on the queue. If a GenDocumentGroup is set as the Print Control's PCI_docNameOutput , then that object will automatically do the right thing. If you use some other type of object, it must be prepared to supply document names on demand. This demand will come in the form of a MSG_PRINT_GET_DOC_NAME .

This message comes with a pointer back to the Print Control. The handler should respond by sending a MSG_PRINT_CONTROL_SET_DOC_NAME with a string containing the name.

MSG_PRINT_GET_DOC_NAME
void	MSG_PRINT_GET_DOC_NAME(
        optr	printControlOD);

If your PCI_docNameOutput object is not a GenDocumentGroup, then it must have a handler for this message.

Source: PrintControl object.

Destination: The object specified in PCI_docNameOutput .

Parameters: printControlOD The optr of the PrintControl object.

Return: Nothing returned explicitly, but should send a MSG_PRINT_CONTROL_SET_DOC_NAME back to the Print Control.

Interception: If receiving object is a GenDocument, unlikely. Otherwise, interception is necessary.

The Spool Library: 4.7 Print Control Instance Data: The Default Printer

PCI_defPrinter, MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER, MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER

As this documentation has already stressed, one of the printing system's more important features is that it is device independent. However, certain special-purpose applications could conceivably depend on the user's printer type, and those applications may have use for the Print Control's PCI_defPrinter field. When the Print Control first appears, it will normally have the system default printer selected. Using the PCI_defPrinter option, another printer might be selected. The user may of course override the default printer and select another.

You can set or retrieve the value in PCI_defPrinter with the messages listed above. For information on them, see Working with Instance Data .

There are utility routines to get information about the system default printer. There are commands to retrieve information about each of the installed printers so that the application may find out whether the desired type of printer is installed and, if so, what its printer number is. These routines are described in Printer Information and Manipulation .

The Spool Library: 4.8 Print Control Instance Data: Adding UI Gadgetry

ATTR_PRINT_CONTROL_APP_UI

If the Print Control supplies UI that meets your geode's needs, feel free to skip this section. On the other hand, if you've decided you want a non-standard print trigger or want some specialized gadgetry to appear in the application's Print dialog box, it is possible to create objects and tell the Print Control to work with them. Make sure that any objects you want to use in this fashion aren't part of a generic or visual tree already. Also, these objects must be set not usable (~ GS_USABLE).

Normally the user has two ways to print up a Print dialog box: the User Print Trigger or the User Fax Trigger, both appearing in the File menu. You may specify a special trigger to be used in place of the standard trigger provided by the Print Control. Normally the default trigger is not only sufficient but preferable, if only because it will automatically display with the correct appearance for the specific UI. To define your own trigger object, create the object(s), setting the correct instance data. Then, when instantiating your Print Control, include an ATTR_GEN_CONTROL_APP_UI:

ATTR_GEN_CONTROL_APP_UI = {yourTrigger};

When this option is used at all, normally the trigger object is a simple trigger. The action descriptor of this application-defined trigger should be

GTI_output = <your_PrintControl>;
GTI_method = MSG_PRINT_CONTROL_INITIATE_PRINT;

By working with the control's features, you may remove either or both of the default triggers.

The Print Control's vardata field ATTR_PRINT_CONTROL_APP_UI allows application-specific gadgetry to appear in the Print dialog box. Create the object(s) to be included and set their instance data, then use the ATTR_PRINT_CONTROL_APP_UI field to signal that the Print Control should include the gadgetry.

ATTR_PRINT_CONTROL_APP_UI = {yourPrintGroup};

When this option is used at all, usually a GenInteraction serves as the print group object, with children appropriate to the task at hand. This generic tree must not be destroyed unless the vardata field has been removed.

The Spool Library: 5 Print Control Messages

Now you have a rather good idea of what's required to set up a Print Control. Once it's been set up, your geode may send the following messages to it.

Common Response Messages

Flow of Control Messages

Working with Instance Data

The Spool Library: 5.1 Print Control Messages: Common Response Messages

MSG_PRINT_CONTROL_VERIFY_COMPLETED, MSG_PRINT_CONTROL_SET_DOC_NAME

These messages are normally used only to respond to messages sent out by the Print Control.

MSG_PRINT_CONTROL_VERIFY_COMPLETED
void	MSG_PRINT_CONTROL_VERIFY_COMPLETED(
        Boolean 	continue);

If you have decided to verify the user's Print dialog box choices, you must send this message back to the Print Control in response to MSG_PRINT_VERIFY_PRINT_REQUEST . If true is passed, the Print Control will continue preparing the job for printing. If the value passed is false , the print will be cancelled. It is up to the application to explain the problem to the user.

Source: Unrestricted, normally the PCI_output object.

Destination: Any PrintControl object.

Parameters: continue Flag signalling whether printing should continue or be cancelled.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_SET_DOC_NAME
void	MSG_PRINT_CONTROL_SET_DOC_NAME(
        char	* string);

This message must be sent by the PCI_docNameOutput before the print job can be spooled. It is sent in response to a MSG_PRINT_GET_DOC_NAME from the Print Control. The string argument should be something that will allow the user to recognize the print job in the Printer Control Panel.

Source: Unrestricted--normally the PCI_docNameOutput object.

Destination: Any PrintControl object.

Parameters: string The document's name, as a null-terminated string. Maximum length of this string is FILE_LONGNAME_LENGTH+1.

Return: Nothing.

Interception: Unlikely.

The Spool Library: 5.2 Print Control Messages: Flow of Control Messages

MSG_PRINT_CONTROL_PRINTING_COMPLETED, MSG_PRINT_CONTROL_REPORT_PROGRESS, MSG_PRINT_CONTROL_PRINTING_CANCELLED, MSG_PRINT_CONTROL_REPORT_STRING, MSG_PRINT_CONTROL_INITIATE_PRINT, MSG_PRINT_CONTROL_PRINT, MSG_PRINT_CONTROL_INITIATE_OUTPUT_UI

Sending these messages to the Print Control signals that a new stage of printing is ready to begin.

MSG_PRINT_CONTROL_PRINTING_COMPLETED
void	MSG_PRINT_CONTROL_PRINTING_COMPLETED();

This message signals the Print Control that the application is finished describing the print job. The application must send this message (or a MSG_PRINT_CONTROL_PRINTING_CANCELLED ) some time after receiving a MSG_PRINT_START_PRINTING .

Source: The object that received MGS_PRINT_START_PRINTING .

Destination: Any PrintControl object.

Parameters: None.

Return: Nothing.

Interception: Unlikely, unadvised.

MSG_PRINT_CONTROL_REPORT_PROGRESS
Boolean	MSG_PRINT_CONTROL_REPORT_PROGRESS(
        PCProgressType 	progress,
        int 	pageOrPercent);

This message may be sent between the time the Print Output receives a MSG_PRINT_START_PRINTING and the time it sends a MSG_PRINT_CONTROL_PRINTING_COMPLETED . Its arguments include a progress type (page number or percentage) and a number representing the progress. It returns a Boolean value, which will normally be true but will be false if the user wishes to cancel printing.

The application should send out this message periodically over the course of a long print job, probably about once per page. Progress may be reported as a page number, a percentage of the job completed, or as a null-terminated string.

If the return value is false , the application should stop drawing to the print GString immediately and send the Print Control a MSG_PRINT_CONTROL_PRINTING_CANCELLED . The return value may be safely ignored, but if the user wishes to cancel printing, it's polite to cancel immediately instead of making him wait until the MSG_PRINT_START_PRINTING has finished describing the entire job.

Source: The object that received MGS_PRINT_START_PRINTING when building a time-consuming job.

Destination: The PrintControl object specified by MSG_PRINT_START_PRINTING .

Parameters: progress How progress is being reported, by page (PCPT_PAGE) or percent (PCPT_PERCENT).

pageOrPercent
How much progress has been made.

Return: Flag signalling that printing should continue. If the flag is false (i.e. zero), then the user wants to cancel printing, and it would be polite to stop.

Interception: Unlikely.

Structures: The PCProgressType enumeration is defined:

 typedef enum {
 PCPT_PAGE,
 PCPT_UNUSED1, /* Unused type */
 PCPT_PERCENT,
 PCPT_UNUSED2, /* Unused type */
 PCPT_TEXT
} PCProgressType;
MSG_PRINT_CONTROL_REPORT_PROGRESS_STRING
@alias(MSG_PRINT_CONTROL_REPORT_PROGRESS) \
Boolean	MSG_PRINT_CONTROL_REPORT_PROGRESS_STRING(
        PCProgressType 	progress,
        Chars 	*progressString);

This message may be sent between the time the Print Output receives a MSG_PRINT_START_PRINTING and the time it sends a MSG_PRINT_CONTROL_PRINTING_COMPLETED . Its arguments include a text string giving some indication of progress. This message returns a Boolean value, which will normally be true but will be false if the user wishes to cancel printing.

The application should send out this message periodically over the course of a long print job, probably about once per page. Progress may be reported as a page number, a percentage of the job completed, or as a null-terminated string.

If the return value is false , the application should stop drawing to the print GString immediately and send the Print Control a MSG_PRINT_CONTROL_PRINTING_CANCELLED . The return value may be safely ignored, but if the user wishes to cancel printing, it's polite to cancel immediately instead of making him wait until the MSG_PRINT_START_PRINTING has finished describing the entire job.

Source: The object that received MGS_PRINT_START_PRINTING when building a time-consuming job.

Destination: The PrintControl object specified by MSG_PRINT_START_PRINTING .

Parameters: progress This must be PCPT_TEXT. The reason that this parameter has only one possible value is that this message is actually an alias of MSG_PRINT_CONTROL_REPORT_PROGRESS , handled by the same assembly routine.

progressString
A string of text, giving an indication of progress.

Return: Flag signalling that printing should continue. If the flag is false (i.e. zero), then the user wants to cancel printing, and it would be polite to stop.

Interception: Unlikely.

Structures: The PCProgressType enumeration is defined:

typedef enum {
 PCPT_PAGE,
 PCPT_UNUSED1, /* Unused type */
 PCPT_PERCENT,
 PCPT_UNUSED2, /* Unused type */
 PCPT_TEXT
} PCProgressType;
MSG_PRINT_CONTROL_INITIATE_OUTPUT_UI
void MSG_PRINT_CONTROL_INITIATE_OUTPUT_UI(
        PrinterDriverType type);

Display the output dialog box.

Source: Anything.

Destination: PrintControl object.

Parameters: type PrinterDriverType value specifying what sort of printer device.

Return: Nothing.

Interception: Rare. This messages is generally used internally only, as the PrintControl normally provides all the UI neededfor printing.

MSG_PRINT_CONTROL_PRINTING_CANCELLED
void	MSG_PRINT_CONTROL_PRINTING_CANCELLED();

The application may send this message to the PrintControl after receiving a MSG_PRINT_START_PRINTING . Do not send both this message and a MSG_PRINT_CONTROL_PRINTING_COMPLETED for a single print job.

The application may send this message to cancel a document while describing a print job, before it would send the MSG_PRINT_CONTROL_PRINTING_COMPLETED .

Source: Unrestricted, probably the PCI_output .

Destination: The PrintControl object specified by MSG_PRINT_START_PRINTING .

Parameters: None.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_INITIATE_PRINT
void	MSG_PRINT_CONTROL_INITIATE_PRINT();

This message, normally sent by the Print trigger in the File menu, is the signal that the Print Control should display the Print dialog box. Geodes with custom print triggers should make sure that those triggers send this message to the Print Control. Geodes using the Print Control's provided print trigger need not send this message.

Source: Unrestricted--typically the user print trigger (or fax trigger).

Destination: Any PrintControl object.

Parameters: None.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_PRINT
void	MSG_PRINT_CONTROL_PRINT();

This message, normally sent by the Print trigger in the Print dialog box, is the signal that the user has made his printing choices and is ready for the Print Control to verify those choices and spool the job.

Source: Unrestricted--typically the Print trigger in the Print Dialog Box.

Destination: Any PrintControl object.

Parameters: None.

Return: Nothing.

Interception: Unlikely.

The Spool Library: 5.3 Print Control Messages: Working with Instance Data

MSG_PRINT_CONTROL_SET_OUTPUT, MSG_PRINT_CONTROL_GET_OUTPUT, MSG_PRINT_CONTROL_SET_DOC_NAME_OUTPUT, MSG_PRINT_CONTROL_GET_DOC_NAME_OUTPUT, MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER, MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER, MSG_PRINT_CONTROL_GET_PRINT_MODE, MSG_PRINT_CONTROL_GET_PAPER_SIZE, MSG_PRINT_CONTROL_GET_PRINTER_MARGINS, MSG_PRINT_CONTROL_CALC_DOC_DIMENSIONS, MSG_PRINT_CONTROL_VERIFY_DOC_MARGINS, MSG_PRINT_CONTROL_VERIFY_DOC_SIZE

The Print Control handles many messages which allow the geode to retrieve and alter the values of the instance data.

MSG_PRINT_CONTROL_SET_OUTPUT
void 	MSG_PRINT_CONTROL_SET_OUTPUT(
        optr 	objectPtr);

This message, used very rarely, specifies that a different object should become the Print Output. Pass the optr of the object to use.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: objectPtr The optr of the new PCI_output .

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_OUTPUT
optr 	MSG_PRINT_CONTROL_GET_OUTPUT();

This message returns the optr of the Print Output.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: The optr of the current PCI_output object.

Interception: Unlikely.

MSG_PRINT_CONTROL_SET_DOC_NAME_OUTPUT
void 	MSG_PRINT_CONTROL_SET_DOC_NAME_OUTPUT(
        optr 	document);

This message, used very rarely, specifies that a different object should become the Document Name Output object. Pass the optr of the object to use.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: objectPtr The optr of the new PCI_docNameOutput .

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_DOC_NAME_OUTPUT
optr 	MSG_PRINT_CONTROL_GET_DOC_NAME_OUTPUT();

This message returns the optr of the Document Name Output object.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: The optr of the Document Name output object.

Interception: Unlikely.

MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER
void 	MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER(
        int 	printerNum);		/* -1 for system's default printer */

This message sets the application default printer, to be used the next time the print dialog box appears. Pass the printer number of the printer to use. Passing a value of -1 means that the system default printer should be used.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: printerNum Number of the new default printer.

Return: Nothing.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER
int 	MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER();

Use this message to retrieve the number of the present application default printer. You may not assume that this printer is the one the user is printing on, since the user may have changed to another printer. A return value of -1 means that the application will use the system default printer.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: The number of the default printer.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_PRINT_MODE
byte 	MSG_PRINT_CONTROL_GET_PRINT_MODE();

This message retrieves the current user-selected print mode, if any. Its possible return values include zero, meaning no mode has been selected; PM_GRAPHICS_LOW_RES; PM_GRAPHICS_MED_RES; PM_GRAPHICS_HI_RES; PM_TEXT_DRAFT; and PM_TEXT_NLQ.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: None.

Return: A PrinterOutputModes signalling the printer's quality.

Interception: Unlikely.

Structures: The PrinterOutputModes structure has the following definition:

typedef ByteFlags PrinterOutputModes;
#define  POM_GRAPHICS_LOW 0x10
#define POM_GRAPHICS_MEDIUM 0x08
#define POM_GRAPHICS_HIGH 0x04
#define POM_TEXT_DRAFT 0x02
#define POM_TEXT_NLQ 0x01

Each flag indicates the specified quality is available. Two useful masks which have been set up are PRINT_GRAPHICS and PRINT_TEXT.

MSG_PRINT_CONTROL_GET_PAPER_SIZE
void 	MSG_PRINT_CONTROL_GET_PAPER_SIZE(
        PCMargineParams 		*retVal);

Use this message to retrieve the Print Control's present paper size.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: retVal An empty PCMarginParams structure which the message handler will fill.

Return: Nothing returned explicitly; the retVal structure will be filled.

Interception: Unlikely.

MSG_PRINT_CONTROL_GET_PAPER_SIZE_INFO
void	MSG_PRINT_CONTROL_GET_PAPER_SIZE_INFO(
        PageSizeReport 	*ptr);

Use this message to retrieve all of the information about the document size and orientation.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr Pointer to an empty PageSizeReport structure which the message handler will fill in.

Return: Nothing.

Interception: Unlikely.

Structures: The PageSizeReport structure has the following definition:

typedef struct {
    dword			PSR_width;
    dword			PSR_height;
    PageLayout			PSR_layout;
    PCMarginParams 			PSR_margins;
} PageSizeReport;
MSG_PRINT_CONTROL_GET_PRINTER_MARGINS
void 	MSG_PRINT_CONTROL_GET_PRINTER_MARGINS(
        MarginDimensions 		*retVal,
        Boolean 		setMargins);

This message returns the margins enforced by the requested printer. If the boolean argument is true , the document margins will be set to be the same as the printer margins.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: setMargins Pass TRUE to set the document's margins equal to the printer's margins, FALSE to leave the margins as they are.

retVal
A pointer to an empty MarginDimensions structure to be filled in by the handler.

Return: The printer's enforced margins.

Structures: The following structure keeps track of all four of a document's margins.

 typedef struct {
    int 	leftMargin;		/* measured in points */
    int 	topMargin;
    int 	rightMargin;
    int 	bottomMargin;
} MarginDimensions;

Interception: Unlikely.

MSG_PRINT_CONTROL_CALC_DOC_DIMENSIONS
void 	MSG_PRINT_CONTROL_CALC_DOC_DIMENSIONS(
        PageSizeReport 	*ptr);

This message calculates the maximum printable area allowed by the user-selected paper size and printer-mandated margins. This message automatically resets the document size and margins to hold these values.

Note that this message correctly handles rotated pages.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: ptr A pointer to a PageSizeReport structure to hold return value.

Return: Nothing returned explicitly.

ptr
The pointer to the filled PageSizeReport structure.

Interception: Unlikely.

MSG_PRINT_CONTROL_CHECK_IF_DOC_WILL_FIT
Boolean 	MSG_PRINT_CONTROL_CHECK_IF_DOC_WILL_FIT(
        Boolean 	warning);

This message returns true if the passed document margins will fit on the selected printer.

Source: Unrestricted.

Destination: Any PrintControl object.

Parameters: warning If set, the PrintControl will deliver a warning to the user.

Return: Indication of whether the printer can work with the current margins ( true if so, false if not).

Interception: Unlikely.

The Spool Library: 6 Page Size Control

MSG_PRINT_REPORT_PAGE_SIZE, SpoolSetDocSize(), MSG_PZC_GET_PAGE_SIZE, MSG_PZC_SET_PAGE_SIZE,

Thanks to the Spooler, applications don't need to worry about what page size the user is working with. If a document is bigger than a printer's paper, then the Spooler will automatically tile the job onto as may sheets of paper as necessary. However, some applications will need to make the user decide on a page size. Page layout programs need to know what size page the user is working with. Spreadsheet programs need to make sure that cells aren't split in half by a page break.

Applications which ask the user to select a page size should incorporate a PageSizeControl into their generic tree. This generic control object provides a dialog box containing page setup choices for the user.

This dialog box contains UI allowing the user to specify what sort of paper the document is to be printed to: envelope, labels, or regular paper. The user may then specify a set of dimensions within that type; paper would have choices including letter sized, legal sized, and A4. The user can even set up a set of custom page dimensions.

Most applications which include a PageSizeControl will probably keep track of their own page size. In this case, use SpoolSetDocSize() to update the PageSizeControl's UI when changing the page size. When the user changes the paper size in the PageSizeControl, the control's output will receive a MSG_PRINT_REPORT_PAGE_SIZE .

Applications which do not keep track of the page size but want to include a PageSizeControl may do so. However, these applications will probably need to find out the page size at a specific time, not just receive notification every time the user changes the page size. This doesn't really fit the normal controller model, and in fact such applications are asking the PageSizeControl to act not like a controller, but like an ordinary piece of UI gadgetry. Setting the PZCA_ACT_LIKE_GADGET flag in the PageSizeControl's PZCI_attrs instance field will make the PageSizeControl act like a gadget.

If the PageSizeControl is to act as a controller, it must appear on the GenApplication's GAGCNLT_SELF_LOAD_OPTIONS GCN list, as does the PrintControl.

No matter how an application interacts with its PageSizeControl, it will work with the PageSizeReport structure.

  typedef struct {
    dword 			PSR_width;
    dword 			PSR_height;
    PageLayout 			PSR_layout;
    PCMarginParams 			PSR_margins;
} PageSizeReport;
 typedef WordFlags PageLayout;
  typedef enum {
    PT_PAPER,
    PT_UNUSED1, /* Unused type */
    PT_ENVELOPE,
    PT_UNUSED2, /* Unused type */
    PT_LABEL
} PageType;
/* Specifying a PageLayout is accomplished by
 * OR-ing together the parts of the layout.
 * Exactly what those parts are depends on the
 * PageType: */
/* Paper Layouts:
 * 	(PT_PAPER | (PO_orient << 3) ) */
  typedef ByteEnum 				PaperOrientation;
#define PO_PORTRAIT 					0x00
#define PO_LANDSCAPE 					0x01
  typedef WordFlags 				PageLayoutPaper;
#define PLP_ORIENTATION 					0x0008
#define PLP_TYPE 					0x0004 /* PT_PAPER */
/* Envelope Layouts:
 * (PT_ENVELOPE | (EP_path << 5) | (EO_ori << 3)) */
  typedef ByteEnum 				EnvelopePath;
#define EP_LEFT 					0x00
#define EP_CENTER 					0x01
#define EP_RIGHT 					0x02
  typedef ByteEnum 				EnvelopeOrientation;
#define EO_PORTAIT_LEFT 					0x00
#define EO_PORTAIT_RIGHT 					0x01
#define EO_LANDSCAPE_UP 					0x02
#define EO_LANDSCAPE_DOWN 					0x03
  typedef WordFlags 				PageLayoutEnvelope;
#define PLE_PATH 					0x0040
#define PLE_ORIENTATION 					0x0010
#define PLE_TYPE 					0x0004 /*PT_ENVELOPE*/
/* Label Layouts:
 * 	(PT_LABEL | (rows <<8) | (cols << 3)) */
  typedef WordFlags 				PageLayoutLabel;
#define PLL_ROWS 					0x7e00 /* # rows */
#define PLL_COLUMNS 					0x01f8 /* # cols */
#define 	PLL_TYPE 				0x0004 /* PT_LABEL */

Code Display 14-3 PageSizeControl Features

  typedef ByteFlags 	PageSizeControlFeatures;
/* The following flags may be combined with | and &:
    PSIZECF_MARGINS, 			( Set margin sizes )
    PSIZECF_CUSTOM_SIZE,			( Custom dimension ranges )
    PSIZECF_LAYOUT,
    PSIZECF_SIZE_LIST,
    PSIZECF_PAGE_TYPE 			( Choice of standard dimensions ) */
#define PSIZEC_DEFAULT_FEATURES (PSIZECF_PAGE_TYPE | PSIZECF_SIZE_LIST | \
                 PSIZECF_LAYOUT | PSIZECF_CUSTOM_SIZE)
MSG_PRINT_REPORT_PAGE_SIZE
void	MSG_PRINT_REPORT_PAGE_SIZE(
        PageSizeReport *psr);

The page size control's output object should have a handler for this message. The control will send this message whenever the user has changed the page size. The handler may wish to change the bounds of some appropriate object, store the new bounds somewhere, or take some other action.

Source: PageSizeControl object.

Destination: The GCI_output object.

Parameters: psr A pointer to a PageSizeReport structure containing the page type and size.

Return: Nothing.

Interception: The handler for this message should store the page size information. It may set the bounds of one or more visual objects.

In addition to the standard set of generic Control instance data, the Page Size control includes some of its own. This data, as you might expect, is largely concerned with paper size and setup. The fields are listed in Page Size Control Instance Data .

PageSizeControls use the PageLayout data structure to hold and pass layout information such as page orientation. One part of this structure gives the type of page being described: paper, envelope, or label. The other parts of the structure have different meanings depending on the type of page. For paper, the layout keeps track of the orientation. For envelopes, the structure contains both path and orientation information. For labels, this structure keeps track of how many labels are on the sheet in each direction; these numbers are bounded by MAXIMUM_LABELS_ACROSS and MAXIMUM_LABELS_DOWN.

Code Display 14-4 Page Size Control Instance Data

@instance PageSizeControlAttrs				PZCI_attrs = 0;
    /* Possible attributes:
     * PZCA_ACT_LIKE_GADGET
     * PZCA_PAPER_SIZE
     * PZCA_INITIALIZE				*/
    /* Current page dimensions and type/layout */
@instance dword 			PZCI_width = 0;
@instance dword 			PZCI_height = 0;
@instance PageLayout 			PZCI_layout = 0;
@instance PCMarginParams			PZCI_margins = { 0, 0, 0, 0 };
/* The following vardata field is internal: */
@vardata  PageSizeControlMaxDimensions  TEMP_PAGE_SIZE_CONTROL_MAX_DIMENSIONS;
/* Its structure is defined:
typedef struct {
     dword  PZCMD_width; ( maximum width )
     dword PZCMD_height; ( maximum height )
} PageSizeControlMaxDimensions; */
/* Attribute to allow applications to be made aware of every change made
 * to gadgetry in the PageSizeControl. This is especially useful for applications
 * that want to add UI that will be dependent upon the state of this
 * controller. No effort is made to eliminate the redundant output of data. */
@vardata  PageSizeControlChanges  ATTR_PAGE_SIZE_CONTROL_UI_CHANGES;
/* This field has structure:
    typedef struct {
     optr  PSCC_destination; ( destination for message )
     Message PSCC_message; ( message to be sent )
    }  PageSizeControlChanges; */
The message should follow the prototype: */
@prototype void PAGE_SIZE_UI_CHANGES_MSG(PageSizeReport _far *psr )
PZCI_width , PZCI_height
These integers represent the current page dimensions, measured in points. Note that these values may never go outside the bounds described by the constants MINIMUM_PAGE_WIDTH_VALUE , MINIMUM_PAGE_HEIGHT_VALUE, MAXIMUM_PAGE_WIDTH_VALUE , and MAXIMUM_PAGE_HEIGHT_VALUE.
PZCI_layout
This field contains a PageLayout structure describing the current page layout settings.
PZCI_margins
This field contains the page's margins.

The following messages allow applications to work with a PageSizeControl directly, like a regular UI gadget. There is one message which the PageSizeControl will send to its output, and several that any object may send to the controller.

Note that these messages are only useful if the PageSizeControl is operating in gadget mode (i.e. if its PZCA_ACT_LIKE_GADGET bit is set). Otherwise, the control's default behavior would override that requested by these messages.

The PageSizeControlAttrs record has the following flags:

PZCA_ACT_LIKE_GADGET
The PageSizeControl object will act like any other generic gadget and will not respond to normal controller notifications.
PZCA_PAPER_SIZE
The PageSizeControl object will display paper sizes instead of document sizes. Setting this would be very rare for applications.
PZCA_INITIALIZE
The PageSizeControl object will be initialized to system default values if PZCA_ACT_LIKE_GADGET is also set.
PZCA_LOAD_SAVE_OPTIONS
Allow the controller to load and save the user's options.
MSG_PZC_GET_PAGE_SIZE
void	MSG_PZC_GET_PAGE_SIZE(
        PageSizeReport		*psr);

This message returns the present page size in terms of width, height, and layout, stored in a PageSizeReport . Note that the PageSizeControl will be sending out a MSG_PRINT_REPORT_PAGE_SIZE every time the user applies changes to these values.

Only send this message if the PageSizeControl is operating in gadget mode.

Source: Unrestricted, as long as the PageSizeControl is in gadget mode.

Destination: PageSizeControl object.

Parameters: psr A pointer to a PageSizeReport structure in which the page size will be returned.

Return: The PageSizeReport structure pointed to by psr will be filled with the page information.

Interception: Unlikely.

MSG_PZC_SET_PAGE_SIZE
void 	MSG_PZC_SET_PAGE_SIZE(
        PageSizeReport *	psr);

This message changes the current page size. It takes a PageSizeReport data structure containing the new dimensions and layout. The Apply trigger of a Page Size Control normally sends this message, passing the present user page size as arguments.

Only send this message if the PageSizeControl is operating in gadget mode.

Source: Unrestricted, as long as the PageSizeControl is in gadget mode.

Destination: PageSizeControl object.

Parameters: psr A pointer to a PageSizeReport structure containing page information.

Return: Nothing.

Interception: Unlikely.

MSG_PZC_SET_MAXIMUM_HEIGHT
void MSG_PZC_SET_MAXIMUM_HEIGHT(
        dword height);

Set the maximum height for a page.

Source: Anything.

Destination: PageSizeControl object.

Parameters: height Height, in points.

Return: Nothing.

Interception: Generally should not be intercepted.

MSG_PZC_SET_MAXIMUM_WIDTH
void MSG_PZC_SET_MAXIMUM_WIDTH(
        dword width);

Set the maximum width for a page.

Source: Anything.

Destination: PageSizeControl object.

Parameters: width Width, in points.

Return: Nothing.

Interception: Generally should not be intercepted.

The Spool Library: 7 Other Printing Components

Most geodes can fulfill all their drawing needs by working with their Print Control. The system has two other main parts that work with printing: the first is the spool library, which controls scheduling; the second is a collection of printer drivers, which handle interactions between the printing devices and the rest of the system.

Spooler and Scheduling

Printer Drivers

Page Size Related Routines

The Spool Library: 7.1 Other Printing Components: Spooler and Scheduling

SpoolInfo(), SpoolHurryJob(), SpoolDelayJob(), SpoolModifyPriority(), SpoolVerifyPrinterPort(), SpoolCreateSpoolFile(), SpoolDelJob(), MSG_PRINT_NOTIFY_PRINT_JOB_CREATED

Geodes may invoke certain routines that affect the Spooler, but for the most part they should avoid this practice. These routines allow the geode to take advantage of some of the Spooler's scheduling capabilities, so the geode can hurry some jobs and delay others. Most applications should not be concerned with the scheduling of printer jobs. If the user thinks that some job should be given a higher priority, he can work with the Printer Control Panel. Many of the following routines require the print job's ID. Applications that will use these routines should have a handler for MSG_PRINT_NOTIFY_PRINT_JOB_CREATED , which has reference material at the end of this section.

For those applications that will be scheduling, the SpoolHurryJob() and SpoolDelayJob() routines may come in handy. Passed a job ID, these routines move the associated job to the front or back of the queue. SpoolModifyPriority() changes the priority of a spool thread so that it may print in the foreground or be moved further into the background. Each function returns a code describing the Spool's Operating Status, alerting the geode if the job wasn't found, the queue was empty, the port couldn't be verified, or even if the operation was successful.

The SpoolInfo() routine returns the list of jobs on a queue or information about any individual job. The other kind of information application writers might be interested in is the printer port status. It's sometimes unwise to spool a job when the printer is having problems, and SpoolVerifyPrinterPort() can give some warning about problems.

While the above routines are used only rarely in applications, there are others that are used even less often. SpoolCreateSpoolFile() creates a spool file, which may be used to create a pre-generated job for the printer kept handy. Such a file would only be useful to applications that can only print one thing and don't mind leaving around an extra spool file. SpoolDelJob() deletes jobs from the queue.

MSG_PRINT_NOTIFY_PRINT_JOB_CREATED
void 	MSG_PRINT_NOTIFY_PRINT_JOB_CREATED(
        optr	printControlOD,
        word 	jobID);

The PrintControl sends this message when it has sent the job to the print queue. Applications may intercept this message to retrieve the new job's ID number or for any other reason.

Source: PrintControl object.

Destination: GCI_output object.

Parameters: printControlOD The optr of the PrintControl object.

jobID
Print job's ID.

Return: Nothing.

Interception: Applications that want to know the ID of created jobs should intercept this message. Note that this behavior is not encouraged, and most applications will want to leave meddling with a print job's status up to the user's discretion.

The Spool Library: 7.2 Other Printing Components: Printer Drivers

Printer Drivers are very important but at the same time not something that any geode you write is going to interact with. Printer Drivers take care of translating the device-independent graphics commands into forms that individual models of printers can work with. Drivers also provide the Spooler with information about the printer and provide printer-specific UI to the Print Control.

As long as a geode uses only device-independent graphics commands, that geode need not worry about printer drivers. In fact, the only geodes that require knowledge of printer drivers are those that use Raw Mode (see below) printing and access the printer drivers directly. To incorporate Raw Mode printing into a geode (normally a bad idea), keep reading. Whether or not the geode you're writing will interact with Printer Drivers, you might want to continue reading this section to find out what printer drivers do.

Dumb Printers

When dealing with a printer that has no page description language, the Spooler and Printer Driver have to be clever. First the Spooler allocates an offscreen bitmap and plays back the contents of the spool file into the bitmap. It does this through the vidmem driver, which has the same interface as a video driver but draws to offscreen bitmaps instead of to a device. Once the bitmap is complete, the Spooler starts feeding the bitmap to the Printer Driver, which translates the bitmap into a series of commands to the printer. If (as is often the case) the printer doesn't have enough memory to absorb a whole page's worth of data at a time, the Printer Driver feeds in one horizontal band, or "swath" of the bitmap at a time. This also allows for better backgrounding: The driver can feed in a swath, then block and allow other threads to complete other tasks until the printer is ready for the next swath.

Smart Printers

When dealing with an "intelligent" printer, a printer that has its own page description language, the Spooler bypasses most of the work it has to do when dealing with dumb printers. Since an intelligent printer presumably has some commands with GEOS equivalents, the Spooler doesn't bother with creating a bitmap but just passes the GString on to the Printer Driver. The Printer Driver then builds a page description in the printer's native language based on the contents of the GString.

Raw Mode

If you want to use some printer-specific command that doesn't have an equivalent in the GEOS graphics system, you will use raw mode. Raw Mode printing expects that you know what sorts of commands the printer will be expecting. If you try to send PostScript commands to a printer that doesn't understand PostScript, probably the results won't be the desired output.

Printer Information and Manipulation

SpoolGetNumPrinters(), SpoolGetPrinterString(), SpoolGetPrinterInfo(), SpoolCreatePrinter(), SpoolDeletePrinter(), SpoolGetDefaultPrinter(), SpoolSetDefaultPrinter()

If you want to find out something about a printer without accessing the printer driver directly, the spool library provides a number of utility routines that work with printer drivers.

Most of these functions specify which of a user's printers they wish to work with by passing its printer number. The first installed printer will be printer number zero, the second printer will be number one, and so on. Use the SpoolGetNumPrinters() routine to find out how many printers have been installed. Once you have this number, you have an upper bound for printer numbers to test. There will always be fewer than MAXIMUM_NUMBER_OF_PRINTERS printers.

The SpoolGetPrinterString() and SpoolGetPrinterInfo() routines return information about the printer associated with the passed printer number. SpoolGetPrinterString() returns the a string containing the name of the printer, such as "HP DeskJet on COM1." The printer string will be of length at most MAXIMUM_PRINTER_NAME_LENGTH. SpoolGetPrinterInfo() returns various pieces of information helpful to geodes that work with printers directly.

The SpoolCreatePrinter() and SpoolDeletePrinter() routines can install and uninstall printers, tasks probably best left to the Preferences Manager. The SpoolDeletePrinter() routine uninstalls the printer corresponding to the passed printer number. It is easy to use but not something that most geodes should be doing. SpoolCreatePrinter() installs a new printer, returning the new printer's printer number. Note that any geode calling this latter function is going to be expected to provide considerable information about the printer being installed. Again, this is not a routine that many geodes should be using.

SpoolGetDefaultPrinter() returns the number of the system-default printer. SpoolSetDefaultPrinter() makes the printer associated with the passed printer number the new system-default printer. Note that these routines are concerned with the system-default printer as opposed to the application-default printer. Most applications won't specify an application specific default printer. For these applications, the system-default printer will be the default. Of course, the user will still be allowed to select any printer, ignoring the default.

The Spool Library: 7.3 Other Printing Components: Page Size Related Routines

SpoolGetNumPaperSizes(), SpoolGetPaperString(), SpoolConvertPaperSize(), SpoolCreatePaperSize(), SpoolDeletePaperSize(), SpoolGetPaperSizeOrder(), SpoolSetPaperSizeOrder(), SpoolGetDefaultPageSizeInfo(), SpoolSetDefaultPageSizeInfo()

Most geodes won't be too concerned with what sort of page size choice the user has made. If the document fits on the page, all's well. If the document doesn't fit, the Spooler will tile the job using only as many pieces of paper as necessary. If the document size should be the same as the page size, the geode can send a MSG_PRINT_CONTROL_CALC_DOC_DIMENSIONS to the print control.

There are several routines which deal with page sizes. Most applications have no reason to call these functions, and we will not discuss them in detail here. Some general notes are in order for those programmers who might work with these functions, though.

It takes two indexes to access a page size. The first is the PageType : paper, envelope, or label. Within each type, there are up to MAX_PAPER_SIZES page sizes. To find out the number of page sizes presently defined for a given type, use the SpoolGetNumPaperSizes() routine. To find the number of a paper size associated with a PageType and a given set of dimensions, use the SpoolConvertPaperSize() routine. For the text string describing the size of the page, call SpoolGetPaperString() (the buffer to hold the string should be of size MAX_PAPER_STRING_LENGTH.

To find out the order in which the page sizes of a given type are stored, call SpoolGetPaperSizeOrder() . You may give a new order by calling SpoolSetPaperSizeOrder() . This will affect the order in which page sizes are presented in a page size control.

To create a new page size, call SpoolCreatePaperSize() . This routine takes a page type, a set of dimensions, and a string to describe those dimensions to the user. It returns a new page size number. To destroy a page size, call SpoolDeletePaperSize() .

To find out or change the default page size, call SpoolGetDefaultPageSizeInfo() or SpoolSetDefaultPageSizeInfo() .

The Spool Library: 8 Debugging Tips

Though GEOS provides an easy-to-use printer interface, mistakes are sure to happen. You may find the following information useful in catching your mistakes or for testing rigorously.

Under Swat, you can see the various threads the Spooler works with. There is always one spool thread running in the background. When the first job is queued on a printer, you can see that a new thread is created for that printer. When the last job for a printer is completed, the thread exits again.

Here are the most common printing mistakes:

  • Forgetting to add the Print Control object to the application's Active List (the GAGCNLT_SELF_LOAD_OPTIONS GCN list).
  • Not setting the document name (see Document Name Output ).
  • Setting the number of pages (page range) incorrectly.
This document is a single-page version of a a multi-page document, suitable for easy printing.


Socket

This chapter describes interactions with the Socket library, used for managing communications between threads -- threads on different machines or two GEOS threads on the same machine (via the "loopback" communication domain). It provides an API which is mostly independent of the medium used for inter-machine communication.

It is common to spawn a separate thread to poll a socket for incoming data; See the Multithreading chapter to learn about managing threads. Sample applications are located in APPL\SDK_C\MultThrd. Some socket operations require knowledge of the communications network used.

1 Introduction
2 Simple Example
3 Addresses
3.1 Letting the User Choose an Address
3.2 Managing Addresses
3.3 Hardware Ports
4 Making a Connection
5 Accepting a Connection
6 Loading on Connection
7 Sending and Receiving Data
7.1 Urgent Data
7.2 SocketSend() and SocketRecv()
7.3 Options
8 Closing the Connection
9 Checking the Connection
10 Domain-Specific Information
10.1 TCP/IP--GEOS Specific
10.2 TCP/IP--Standard
10.3 Loopback

Socket: 1 Introduction

The Socket library manages communications both between machines and between threads on the same machine. It provides a level of abstraction above individual communication protocols ( e.g. , IrLMP, TCP/IP). Programs using the Socket library can send and receive information packets without knowing much about the communications protocol by which those packets are being transmitted. Also, programs using the Socket library need not know about the hardware communication ports used -- whether communication is being carried out over an IR serial port, a modem, or a network connection -- the API is the same.

The Socket library supports different "delivery types"-- different levels of reliability in transmitting data. There is a trade-off: the more reliable delivery types require more set-up work by the programs using them. The following delivery types are available:

Datagram
Unreliable transmission of discrete packets. Packets may arrive out of order or not at all. It is up to the data sender to determine if re-transmission is necessary. This is the least demanding delivery type to set up, because it doesn't require any two-way connection.
Sequenced Packet
Sequenced Packet delivery means reliable transmission of discrete packets. The packets arrive in the order sent. The Socket library manages error checking and re-transmissions, as necessary. The program using a sequenced packet socket is responsible for establishing a two-way communications connection.
Stream
Stream delivery means the reliable transmission of a stream of bytes, as opposed to other delivery types which require that data be broken up into packets. The Socket library manages a stream of bytes, breaking it up into packets, transmitting the packets, and reconstructing the byte stream from packets on the other side. The Socket library also manages error-checking and re-transmission, as necessary. The program using a stream socket is responsible for establishing a two-way communications connection.
Addresses

Each side of the connection is represented by an address. An address is made up of several parts:

Domain
The network over which the connection is being made. Domains include TCP/IP, IRLAP, and "loopback" (used for connections between two threads on the same machine).
Address Data
This data identifies a machine on a network domain. The format of the address depends on the domain. In the case of TCP/IP, a sample address might be the string "geoworks.com". IrLAP addresses are 32-bit numbers. If the domain is "loopback", then no address information is necessary -- the sending and receiving machines are one and the same.
Port
This token identifies one side of a connection within one machine. Ports allow multiple data sockets to operate over one connection. Thus, connecting to "geoworks.com", one might send mail messages using port 25 and file-transfer commands via port 21. Port numbers are either 16-bit or 32-bit, depending on the domain.

The amount of overhead associated with sending and receiving data depends upon the devices involved, the domain, and the delivery type. When testing, experiment with different domains (if available), delivery types, and packet sizes to find a good balance of reliability and rapidity.

For any domain, there may be more than one address format. For instance, the TCP/IP address "geoworks.com" is conveniently human-readable, but cannot be used at the socket level to actually make a connection. Instead, you must resolve "geoworks.com" to a 32-bit address number. All socket operations require "primitive" addresses -- addresses resolved to their simplest form. To resolve an address, call SocketResolve() . We will discuss the details of working with addresses in more detail below.

Errors

Many of the Socket library routines return error values. Sometimes these error values are return values of routines; if you call a routine that can return an error value, be sure to check the return value and react appropriately to errors. Some routines don't return an error value directly, instead returning a flag to indicate that something has gone wrong. To find out exactly what went wrong, call ThreadGetError() .

Those Socket library routines that return error values do not set the thread error value. Calling ThreadGetError() after calling such a routine would not detect any error encountered by that routine.

ThreadGetError() returns a word-sized value. The low byte of this word is a SocketError value; the high byte is a SpecSocketDrError value. If the returned word is zero, that means that there was no error.

In general, a SpecSocketDrError value indicates a low-level error, and a SocketError value indicates a high-level error. In many cases, there will be a SocketError in the low byte, but the high byte will be clear -- there was no problem with the low-level communication medium, but a protocol exception may have occurred.

In general, only the low byte (the SocketError value) is useful in determining how to handle the exception. The high byte might affect what error message is displayed to the user. For example, it probably doesn't matter to your program whether the high byte was SSDE_LINE_BUSY or SSDE_NO_DIALTONE. In either case, it just means that communication could not be established. However, users might want to know the nature of the problem so that they can figure out how fix it.

Some of the more common SocketError values are listed below. In addition, certain routines may make use of other SocketError values, which will be discussed with those routines.

SE_NORMAL
There is no error. SE_NORMAL is guaranteed to equal zero.
SE_OUT_OF_MEMORY
The Socket library ran out of memory. Perhaps it was trying to receive or send a data packet that was too large.
SE_SOCKET_IN_USE
The socket was busy doing something else. Perhaps you tried to connect it, but it was already connected.
SE_SOCKET_BUSY
The socket was temporarily busy doing something else. Perhaps you should try again a few times.
SE_MEDIUM_BUSY
The hardware port which would be used to carry out the requested operation was busy. You might wait for the hardware port to become free; you could call SocketCheckMediumConnection() to see if the port is being used for a socket connection.
SE_ALL_PORTS_BUSY
To carry out the requested operation, the Socket library tried to use a port number; however all port numbers were already busy.
SE_SOCKET_NOT_BOUND
The socket was not bound ( i.e. , associated with a port number), but needed to be bound to carry out the requested operation. To bind a socket, call SocketBind() .
SE_SOCKET_NOT_LISTENING
The socket wasn't listening for incoming connections, and needed to be in this state to carry out the requested operation. To make a socket listen, call SocketListen() .
SE_SOCKET_LISTENING
The socket was busy listening for incoming connections when you asked it to carry out another operation. Remember that you often want separate sockets to handle listening and data transfer.
SE_SOCKET_NOT_CONNECTED
The socket was not connected, but a connection is required for the operation you requested. To connect the socket, call SocketConnect() .
SE_SOCKET_CLOSED
The socket was closed, making the requested operation impossible.
SE_CONNECTION_CLOSED
The other side of your socket's connection has closed that connection; to close up your side of the connection, call SocketClose() .
SE_CONNECTION_FAILED
The connection was lost, probably due to some low-level link failure. There may have been a problem with the physical connection or with the domain-specific software.
SE_CONNECTION_RESET
The connection was lost, probably due to this side of the connection intentially making a low-level disconnection; e.g. , the sort of disconnection that results from SocketCloseDomainMedium() or the Socket's owning thread exiting.
SE_TIMED_OUT
The Socket library was unable to carry out the requested operation before the time-out time was reached. Beware: Depending on the domain, for certain phases of certain operations, there may be low-level time-outs which can occur even if you have specified a longer or an infinite time-out.
SE_CANT_LOAD_DRIVER
The Socket library was unable to load a driver necessary to carry out the requested operation.
SE_UNKNOWN_DOMAIN
The Socket library didn't recognize the domain name.
SE_DESTINATION_UNREACHABLE
The requested destination address was unreachable over the network.
SE_LINK_FAILED
The low-level link used for this connection failed or could not be established. There may have been a problem with the physical connection or with the domain-specific software.
SE_SYSTEM_SHUTDOWN
This error signals that GEOS is shutting down. Your program should probably either end its connections gracefully or attempt to delay the shutdown. Note that it is possible for a shutdown to occur without generating this signal.
SE_INFO_NOT_AVAILABLE
The requested information was not available.
SE_DOMAIN_REQUIRES_16BIT_PORTS
You tried to use a 32-bit port number in a domain that does not support them. Use MANUFACTURER_ID_SOCKET_16BIT_PORT as the ManufacturerID of your port number.
SE_INTERRUPT
The operation was stymied by SocketInterrupt() .
SE_INTERNAL_ERROR
The Socket library did something wrong. You should take advantage of whatever developer support to which you have access. They will want a back-trace and other information necessary to duplicate the error.

Socket: 2 Simple Example

The sample application Appl/SDK_C/Talk provides an example of a simple application using the Socket library.

To understand how the program works, you should, of course, look at its source code. The following is a high-level overview of what the Talk example illustrates: a simple client-server connection in which the server awaits connections, the client connects, and then the two exchange data. Note: this application assumes that the host device (the client) has already connected to the server. Remote connections are usually made through PPP or Point-to-Point Protocol. See SocketOpenDomainMedium() discussed in Hardware Ports .

The example performs the following steps:

  1. Allocate memory in which to store data.
     When sending or receiving data, you need to provide a pointer to a buffer containing the data to be sent or received. Thus, the buffer should be in a block which can either be locked down as needed or is in fixed memory. Programs often spawn a separate thread to receive data from the socket; if this thread needs to lock down a memory block in which to store the data, then make sure that the thread can lock down the memory block. See the Multithreading chapter to learn about managing threads.
    (If you follow the multi-threaded approach, be aware that when the Socket's owning thread exits, it frees the Socket. To allow a Socket to survive the exit of its thread, use SocketSetIntSocketOption() to change its owning thread.)
    For this simple example, we're using a small buffer to handle the receipt of data, so we declare it as a global variable so it resides in the application's fixed memory:
char recvBuf[16];
  • Client Side: Make Sure Connected to Domain
     Depending on the nature of the device and application, you may need to make sure that the device is connected to the communication domain. In the Talk sample application, we make sure that the device is dialed in to an ISP: an AccessPointControl object prompts the user to choose an ISP, and a call to SocketOpenDomainMedium() opens a PPP connection to that ISP.
  • Client Side: Get Address For Other Side of Connection.
     The Talk application allows the user to connect with another machine by specifying that machine's address. The Talk application creates a controller gadget by which the user can specify an address. SocketGetAddressController() returns the controller class' pointer; ObjInstantiate() creates an object of the class.
    The following code is called when the application opens. It creates the controller gadget, initializes its data, and places it in a dialog box.
ctrlClass =
  SocketGetAddressController(theAddress.SA_domain));
ctrlOb =
  ObjInstantiate(
    HandleOf(GeodeGetOptrNS(@AddressDialog)),
    ctrlClass);
@call ctrlOb::MSG_SOCKET_ADDRESS_CONTROL_SET_ACTION(
  ConstructOptr(0,TO_PROCESS), MSG_CTP_VALID);
@call
  GeodeGetOptrNS(@AddressDialog)::MSG_GEN_ADD_CHILD(
    ctrlOb, CCO_FIRST);
@call ctrlOb::MSG_GEN_SET_USABLE(VUM_NOW);

When the user enters an address, the following code is called. It gets the selected address from the address controller and packages it up into a form the Socket library can use.

MSG_SOCKET_ADDRESS_CONTROL_GET_ADDRESSES returns a chunk with raw address data based on the address entered by the user. SocketResolve() reduces this raw address data to a primitive form that can be used for making connections.

adArray = @call ctrlOb::MSG_SOCKET_ADDRESS_CONTROL_GET_ADDRESSES();
address1 = ChunkArrayElementToPtrHandles(
  HandleOf(ctrlOb), adArray, 0,0);
theAddress.SA_addressSize =
  SocketResolve(
    theAddress.SA_domain, (byte *)&address1[1],
    address1->SACA_opaqueSize, addressBuffer,
    MAX_ADDRESS_SIZE);
LMemFreeHandles(HandleOf(ctrlOb), adArray);
  • Client Side: Create and Connect Socket.
     The Client side now creates a socket for its side of the connection and attempts to connect to the address extracted from the address controller. The socket will be of the stream delivery type.
dataSocket = SocketCreate(SDT_STREAM);
SocketConnect(
  dataSocket, (SocketAddress *) &theAddress,
  SOCKET_NO_TIMEOUT);
  • Server Side: Create Socket and Accept Connection(s)
     On the server side, the application spawns a separate thread to handle incoming connections.

There are two "sockets" created on this, the server side of the connection. The first socket is bound to a particular port, and "listens" for incoming connections. It is possible to accept more than one connection over the listening socket--each accepted connection is represented by another socket to represent this side of the new particular connection.

listenSocket = SocketCreate(SDT_STREAM);
SocketBind(listenSocket, theAddress.SA_port, 0);
SocketListen(listenSocket, 5);
dataSocket = SocketAccept(
  listenSocket, SOCKET_NO_TIMEOUT);
  • Either Side: Send Data
     In this example, the data to send is taken from a text object. The program loads the text into a block of memory, locks the block down to get a pointer to it, then transmits the data.
textBlock = @call
  GeodeGetOptrNS(@InText)::
  MSG_VIS_TEXT_GET_ALL_BLOCK(NullHandle);
textPtr = MemLock(textBlock);
textSize = LocalStringSize(textPtr);
SocketSend(
  dataSocket, textPtr, textSize, 0,
  (SocketAddress *)0);
MemFree(textBlock);
  • Either Side: Wait For and Receive Data
     The application's listening thread waits for data to arrive and stores it away in a buffer when it does arrive. It processes the incoming data and gets ready for the next piece of data.
while ((
  datasize = SocketRecv(
    dataSocket, recvBuf, sizeof recvBuf,
    SOCKET_NO_TIMEOUT, 0, (SocketAddress *)0))
  != 0)
{ @call
  GeodeGetOptrNS(@OutText)::MSG_VIS_TEXT_APPEND_PTR(
    recvBuf, datasize); }
  • Either Side: Close Connection
     The application has a Close button. When the user presses this button, the application shuts down its side of the connection. The SocketCloseSend() routine partially shuts down the socket so that it can receive data, but cannot send it; also, the routine sends a signal to the other side that the connection is closing.
if (listenSocket) {
  SocketClose(listenSocket);
  listenSocket = 0; }
SocketCloseSend(dataSocket);
  • Either Side: Detect Closed Connection and Close Socket
     When receiving data, if either side seems to have received a zero-length packet, it checks to see if an exception has occurred, specifically to see if the connection has closed. (When SocketRecv() detects an error, it sets the thread error value, which may be retrieved via ThreadGetError() .) If the connection has closed, then Talk closes the sockets on this side of the connection.
if (ThreadGetError() == SE_CONNECTION_CLOSED) {
  if (listenSocket) {
    SocketClose(listenSocket);
    listenSocket = 0; }
  SocketClose(dataSocket); }
  • Both Sides: Sockets Freed When Thread Exits
     Lest you are worried about these Sockets you have created but not freed, know that they will be freed automatically when the owning thread exits. (To allow a Socket to survive its thread's exit, you must assign the Socket to a new thread.)

Socket: 3 Addresses

In the example above, the application queried the user for a connection address. This is a fairly common case. However, some programs need to use hard-wired addresses. Perhaps you want to allow connections over all available domains. Thus, a few words about addresses may be in order.

Addresses are usually represented via the SocketAddress structure:

typedef struct {
    SocketPort 		SA_port;
    word 		SA_domainSize;
    char *		SA_domain;
    word 		SA_addressSize;
} SocketAddress;
/* domain-specific address data here */
SA_port
Port numbers identify a particular line of communication within a machine. Port numbers may be 32-bit or 16-bit, depending on the domain. They are specified via the SocketPort structure, made up of a 16-bit ManufacturerID and 16-bit arbitrarily-chosen token number. If the domain uses 16-bit socket numbers, then use MANUFACTURER_ID_SOCKET_16BIT_PORT as the ManufacturerID . If the domain allows 32-bit socket numbers, then you may use MANUFACTURER_ID_SOCKET_16BIT_PORT or some other ManufacturerID , probably your own ID or that of some standard service provider. This numbering scheme helps to avoid overlapping port numbers.
SA_domain , SA_domainSize
The domain identifies the protocol of the network by which the addressed machine may be reached. The domain is specified by a string. In a SocketAddress structure, SA_domain is a pointer to the null-terminated domain name string and SA_domainSize is the size of the buffer containing the domain name string.
Many communications protocols are hard-wired to work with a particular domain. To find out all domains available to the user's device, call SocketGetDomain(). (See Letting the User Choose an Address .)
SA_addressSize , Address Data
The format of the address data used to identify a machine within a domain depends on that domain. The SA_addressSize field of the SocketAddress structure contains the size of the address data. The buffer containing the address data should fall immediately after the SocketAddress structure. Read the documentation for a given domain to find out the format for its addresses.
You'll want to make sure that space for this data is, in fact, allocated at the end of the structure. If you declare a socket address, you will define a struct, probably based on SocketAddress , that has the fields of SocketAddress , but also has a buffer to hold the resolved address data. More likely than not, you won't need to declare or define such a structure -- you're more likely to use SocketCreateResolvedAddress() or bypass the creation of this structure altogether.

Before using an address, make sure that it is in its primitive form. For instance, an IP address like "geoworks.com" is not in its primitive form -- the IP address must be translated into a four-byte number before it can be used. Use SocketResolve() (described below) to transform an address into its primitive form. Note that the address passed to SocketResolve() is not in a SocketAddress structure.

Letting the User Choose an Address

Managing Addresses

Hardware Ports

Socket: 3.1 Addresses: Letting the User Choose an Address

SocketAddressControllerClass, SocketGetDomains(), SocketGetAddressController(), SocketCreateResolvedAddress(), SocketResolve(), SocketInterruptResolve()

Given a domain, you can request the Socket library to provide UI gadgetry so that the user can choose an address within that domain. Many programs only support socket connections over one domain. However, if you need a list of all domains supported in the user's environment, call SocketGetDomains() .

The SocketGetDomains() routine takes the optr of a chunk array and fills in that array with elements. Each element contains a (non-null-terminated) string which is the name of a supported domain. This chunk array has a standard ChunkArrayHeader header and variable-sized elements.

If you want the routine to allocate the chunk array for you, pass an optr that contains a NullChunk ( i.e. , a null ChunkHandle):

MemHandle	myHandle;
Optr	myOptr;
myHandle = OptrToHandle(myHandle);
myOptr = ConstructOptr(myHandle, NULL);

For more information about working with chunk arrays, see the Local Memory chapter. In short, to manage the chunk array of domain names:

  1. Lock down the local memory heap with MemLock() .
  2. Use LMemDerefHandles() to get a pointer to the chunk array.
  3. To get the number of domains, call ChunkArrayGetCount() .
  4. To get a pointer to the nth string in the chunk array, call ChunkArrayElementToPtr() or ChunkArrayElementToPtrHandles() .
  5. When finished reading domain strings, call MemUnlock() on the local memory heap.

Given a domain name, you can create some UI gadgetry for choosing addresses within that domain. As shown in the Appl/SDK_C/Talk example, call SocketGetAddressController() to get the class of an appropriate controller. This class is guaranteed to support the messages shown in SocketAddressControllerClass .

Code Display 23-1 SocketAddressControllerClass

/* Domain-specific subclasses of SocketAddressControllerClass all support
 * the following messages. MSG_SOCKET_ADDRESS_CONTROL_GET_ADDRESSES is probably
 * the most commonly used message. */
/*
 * Build the block of data that holds the addresses selected by the user
 * to pass to the transport driver. If OK, returns ChunkArray of SACAddress
 * structures in same block as controller. Else, returns 0.
 * As of this writing, all address controllers return at most one address
 * in the list of addresses.
 * The returned address(es) are not in its primitive form; use
 * SocketResolve() to transform the address into something usable.
 */
 @message word MSG_SOCKET_ADDRESS_CONTROL_GET_ADDRESSES();
  typedef struct {
    word 		SACA_opaqueSize;
    /* label byte 		SACA_opaque;    Pass this to SocketResolve */
    /* after the opaque address, there is a null-terminated
     *  human-readable form of the address. */
} SACAddress;
/*
 * Inform the subclass of the type of medium selected by the user
 * so it can customize its appearance appropriately, should the controller
 * handle media that have different ways of addressing.
 */
@message void MSG_SOCKET_ADDRESS_CONTROL_SET_MEDIUM (
                MediumType mediumType);
/*
 * Set the current user-editable address to the passed address.
 */
@message void MSG_SOCKET_ADDRESS_CONTROL_SET_ADDRESSES(optr addr);
/*
 * Inform the controller of the message and destination object it should use to
 * notify the invoker that a valid address exists.
 */
@message void MSG_SOCKET_ADDRESS_CONTROL_SET_ACTION (
                optr dest,
                word actionMsg);
/*
 * Let the containing dialog know whether the address we've got is a
 * valid one, so it can decide whether to allow the user to send the
 * message.
 */
@message void MSG_SOCKET_ADDRESS_CONTROL_SET_VALID_STATE(Boolean valid);

You cannot use an address with Socket library routines unless that address is in its primitive form. This primitive form is created by taking a higher-level form of the address and passing it to the SocketCreateResolvedAddress() routine or the SocketResolve() routine. Exactly how the Socket library resolves the address depends upon the domain.

Recall that addresses returned by the address controller are not in their primitive form. The primitive form of an address can be volatile: feel free to re-use them if making several connections over a short period of time, but re-resolve the address if it's been a long time since it was last resolved. Note that there can be a large overhead when it comes to resolving addresses. Also note that IRLAP numeric addresses are especially volatile, changing every time the protocol connection starts, i.e., every time the IRLAP driver is loaded, either for the first time or after more than a minute of inactivity.

The SocketCreateResolvedAddress() routine takes a pointer to a domain name and raw address data and returns a buffer with a SocketAddress structure and the address data. Depending on what you're going to use the address for, you may need to fill in the structure's SA_port field.

SocketResolve() is the same as SocketCreateResolvedAddress() except that SocketResolve() takes a buffer to hold the resolved address data. If the buffer isn't big enough, SocketResolve() returns SE_BUFFER_TOO_SMALL. Depending on the domain, calling SocketResolve() on an address already in its primitive form may have undesired results.

The resolution process may or may not require network access, depending on the domain and the input format. Depending on the domain, the addressing system may be volatile. Under these conditions, put off resolving the address as long as possible, until just before using the address; there will be less time for the address to go bad. If the network goes down, the Socket library might not be able to resolve addresses. In this case, SocketCreateResolvedAddress() or SocketResolve() returns SE_TEMPORARY_ERROR.

If you wish to interrupt the resolution process (perhaps you allow the user to cancel the operation), use the SocketInterruptResolve() function. This function attempts to halt the resolution process. It is, however, unreliable. It may not succeed in interrupting the resolution process; it may also interrupt other resolves happening at the same time.

Socket: 3.2 Addresses: Managing Addresses

SocketGetAddressSize(), SocketGetSocketName(), SocketGetPeerName()

Most programs blindly use addresses supplied by the user or use only hard-wired addresses. Only a few programs need to access and manipulate addresses. The Socket library provides a few utility routines for this purpose.

To find out the maximum address data size for a given domain, call SocketGetAddressSize() . This can be helpful when allocating memory to handle a full address, such as that returned by the SocketGetSocketName() and SocketGetPeerName() functions.

If a socket is connected, call SocketGetPeerName() to get the address of the socket at the other end of the connection. To find out the address used to represent the socket's own side of the connection, call SocketGetSocketName() .

Socket: 3.3 Addresses: Hardware Ports

SocketGetDomainMedia(), SocketGetAddressMedium(), SocketCheckMediumConnection(), SocketGetMediumAddress(), SocketOpenDomainMedium(), SocketCloseDomainMedium()

Most socket connections involve the use of some hardware "port" -- some physical connection to the outside world. To determine which sort of hardware ports the user's machine uses to connect to a given domain, call SocketGetDomainMedia() . This returns a chunk array of MediumType structures. Given a choice of domains to make a given connection, consider which medium would be used to make the connection.

To find out which port the Socket library would use to connect to a given address, call SocketGetAddressMedium() . Depending on the medium, you might try to connect to the other site via some other domain or decide not to make the connection at all.

To check a given hardware port is being used for an active connection, call SocketCheckMediumConnection() . If a point-to-point connection exists, this routine returns the address of the remote site. If there is a connection, but not a specific address, then the routine returns a null address. If there is no connection through the hardware port, the routine returns a SocketError value.

To find out the address of the local machine on a connection over a particular medium, use the SocketGetMediumAddress() routine. This is the address another machine would use to connect to this machine via that medium.

The SocketOpenDomainMedium() routine opens a "raw" connection using the specified medium. Exactly what is meant by a "raw" connection depends upon the domain involved. In the case of TCP, this routine will dial the modem and create a PPP connection to the PPP server but will not send any TCP packets.

It is possible to "hang up" a hardware port. Call SocketCloseDomainMedium() to force a domain to close a medium. You may request that the medium only be closed if it is not being used ( i.e. , there are no active connections using the medium). If any socket connections are using the port, they will receive the error SE_CONNECTION_RESET. Later, we will discuss orderly ways to close a connection, being careful to send proper notification to the other side of the connection.

Socket: 4 Making a Connection

SocketCreate(), SocketConnect(), SocketInterrupt()

To make a connection, a program creates a socket and specifies an address to which to connect that socket.

To create a new socket, call SocketCreate() . You must specify the socket's delivery type: datagram, sequential packet, or stream.

Before sending or receiving data over a sequential packet or stream socket, you must "connect" it to the place it will be sending the data to or receiving it from.

Depending on the medium and device, you may need to make sure that the device is connected to a network. For example, for a Nokia 9000i Communicator to make a TCP/IP connection, it must first have made a PPP connection. Use the SocketOpenDomainMedium() routine to make the "raw" network connection.

To make a connection, call SocketConnect() . Pass the socket created via SocketCreate() , the address to connect to, and a time-out value. If it returns SE_NORMAL, then the connection was established successfully. If it returns SE_CONNECTION_REFUSED, the remote site wasn't prepared to accept the connection. If it returns SE_CONNECTION_ERROR, the connection didn't go through for some miscellaneous reason. If it returns SE_NON_UNIQUE_CONNECTION, then the attempted connection would have been identical to another connection: same source address, source port, destination address, destination port, and delivery type. There would be no way to tell these connections apart. If you try to open the same connection from both sides simultaneously, they may both fail with SE_NON_UNIQUE_CONNECTION.

To cancel the connection operation, call SocketInterrupt() .

Datagram sockets cannot form a true connection -- they are unreliable. Sockets must be able to send and receive datagram packets without relying on a permanent connection. However, using SocketConnect() with a datagram socket specifies a default destination address to use when sending packets.

Socket: 5 Accepting a Connection

SocketBind(), SocketBindInDomain(), SocketListen(), SocketAccept()

There's a bit of work involved in setting up a program to receive connections from other sockets. The program must create a socket and associate it with a port. If there is to be a sequenced-packet or stream delivery connection, then the program needs to do some more work to listen for and accept these connections.

If a program creates a socket that accepts connections, the program should probably spawn a separate thread for each accepted connection. If this is not practical, then see SocketCheckReady() for a way to monitor several sockets with a single thread.

Call SocketCreate() to create a socket for this side of the connection. If the socket is to receive datagram packets, then it must be of type datagram. If the other socket in the connection will be a sequenced packet or stream delivery type, then the socket accepting the connection must be either sequenced packet or stream delivery type; however, the two sockets need not be of the same type.

(If you follow the multi-threaded approach, be aware that when the Socket's owning thread exits, it frees the Socket. To allow a Socket to survive the exit of its thread, use SocketSetIntSocketOption() to change its owning thread.)

The other side of the connection attempts to hook up to a port number on this side of the connection. If a socket is to intercept connections to a certain port, it must somehow associate itself with that port. Use the SocketBind() or SocketBindInDomain() routines to associate a socket with a port number. SocketBind() associates the socket with a port number. SocketBindInDomain() associates the socket with a port number but only within a specific domain.

For any given domain, normally only one socket on the machine may be bound to any port number. Thus, if you call SocketBindInDomain() to bind socket A to port 3 in the TCP/IP domain, then:

  • You could use SocketBindInDomain() to bind socket B to port 3 in the IRLAP domain, but
  • You couldn't use SocketBindInDomain() to bind socket C to port 3 in the TCP/IP domain, and
  • You couldn't use SocketBind() to bind socket D to port 3.

There are some ways around this restriction:

  • To force SocketBind() or SocketBindInDomain() to re-use some port that another socket has bound, then pass the SBF_REUSE_PORT flag. Otherwise, be prepared to receive SE_PORT_IN_USE or SE_BIND_CONFLICT. Even the SBF_REUSE_PORT flag won't allow you to use both SocketBind() and SocketBindInDomain() on a single port. E.g., in the example above, you could use SocketBindInDomain() with SBF_REUSE_PORT to bind socket C to port 3 in the TCP/IP domain, but you could not use SocketBind() with SBF_REUSE_PORT to bind socket D to port 3. You may not use SBF_REUSE_PORT with datagram-based sockets.
  • If there are only non-datagram-based sockets bound to a port, you may bind a datagram-based socket to that port without setting SBF_REUSE_PORT. The reverse is also true. Recall that only a datagram socket can connect with another datagram socket; non-datagram sockets connect only with other non-datagram sockets.
  • A given socket may only be bound to one port. Trying to bind it to another port results in SE_SOCKET_ALREADY_BOUND. Create another socket to bind to the other port.

Always check for bind conflicts. Bind conflicts may occur even though a program is well-behaved: other programs may have bound ports to a socket. For reasons described in Loading on Connection , sometimes the Socket library itself may monitor a port.

When setting up a sequenced packet or stream delivery connection, a bit more set-up work is necessary. These delivery types require a "connection."

To listen for incoming sequenced packet or stream connections, call SocketListen() . The socket must be bound to a port before it can listen. To allow more than one connection through the port, specify the maximum number of connections as an argument to SocketListen() . The SocketListen() routine causes a socket to listen for incoming connections. If another socket is listening at the port, SocketListen() returns SE_PORT_ALREADY_LISTENING.

To make a socket wait for and accept a connection, call SocketAccept() . The socket must be listening for connections (processed by SocketListen() ). Because the thread blocks until a connection is made or SocketAccept() times out, programs normally don't call this routine in their main execution thread. As in the Appl/SDK_C/Talk sample application, a separate thread handles accepting a connection and receiving data from the connection.

If a connection is made, SocketAccept() returns a Socket used to represent the connection. Do not confuse this connected socket with the listening socket. You might be able to accept more connections from the listening socket.

If SocketAccept() returns SE_LISTEN_QUEUE_EMPTY, then there were no connections waiting to be accepted.

Socket: 6 Loading on Connection

SocketAddLoadOnMsg(), SocketAddLoadOnMsgInDomain(), SocketRemoveLoadOnMsg(), SocketRemoveLoadOnMsgInDomain()

It's all very well that you can tell a running program to accept packets and connections over sockets. However, it seems unreasonable to ask the user to run these programs by hand any time there might be a pending connection.

You can tell the Socket library to load a particular geode when it receives data on a particular port, or on a particular port via a particular domain. For an example, see the Appl/SDK_C/Talk sample application: if you run Talk and press the Autoload button, the Socket library knows to start up the Talk program because it senses that it is receiving an incoming Talk connection.

When the program starts up, it should probably create a socket, bind it to the appropriate port, and make that socket listen. If the listening socket is then destroyed, then any unaccepted connections are canceled.

The Socket library writes out an entry to the GEOS.INI file in the [socket] category and with key "LoadOnMsg". The Socket library consults these GEOS.INI file entries when it starts up and will act accordingly when it receives packets.

To make the Socket library load a program when receiving packets on a particular port, call SocketAddLoadOnMsg() . Specify the program by means of its path. To load a program when receiving packets on a particular port on a particular domain, call SocketAddLoadOnMsgInDomain() .

To remove the entries from the GEOS.INI file and tell the Socket library not to try to load the program when receiving packets, call either SocketRemoveLoadOnMsg() or SocketRemoveLoadOnMsgInDomain() .

To listen for incoming data in this manner, the Socket library effectively binds a socket to monitor the port. Thus

  • SocketAddLoadOnMsg() and SocketAddLoadOnMsgInDomain() can run into bind conflicts just as SocketBind() and SocketBindInDomain() do.
  • SocketAddLoadOnMsg() interferes with SocketAddLoadOnMsgInDomain() and SocketBindInDomain() on the same port.
  • SocketAddLoadOnMsgInDomain() interferes with SocketAddLoadOnMsg() and SocketBind() on the same port.

Socket: 7 Sending and Receiving Data

SocketSend(), SocketRecv(), SocketSetIntSocketOption(), SocketGetIntSocketOption(), SocketSetSocketOption(), SocketGetSocketOption()

Once both sides of a socket communication are set up, they're ready to send data back and forth. The SocketSend() and SocketRecv() routines, described below, allow a socket to transmit and receive data, respectively.

The amount of overhead associated with sending and receiving data depends on the devices involved, the domain, and the delivery type. Be certain that if just one byte of data is sent at a time, time performance will suffer.

Whenever possible, the sender should gather up small pieces of data and send them in one large packet. The optimal size of a packet depends on the device, domain, and delivery type. When designing a communications protocol, it would be worthwhile to figure out which sorts of data may be combined into larger blocks. Note that in some domains, the driver will try to do this gathering of small blocks into large packets for you.

Urgent Data

SocketSend() and SocketRecv()

Options

Socket: 7.1 Sending and Receiving Data: Urgent Data

The Socket library supports the notion of urgent data. This allows a short packet of data to cut in front of other packets. To send a packet marked urgent, pass the SSF_URGENT flag to SocketSend() .

In some domains, only one byte of data in a packet can be marked urgent. In such a domain, passing SSF_URGENT with a 32-byte packet to SocketSend() results in two packets being sent: a 1-byte urgent packet and a 31-byte non-urgent packet. The data of the 1-byte urgent packet is the last byte of the original 32-byte packet. Check your domain documentation for size limits on urgent packets.

Urgent data may be delivered out of order. It may arrive out of sync with non-urgent packets; it may arrive out of order with other urgent packets. There is no queue for urgent data. Each urgent data packet received causes the previous urgent data to be discarded.

The SocketRecv() routine, in charge of receiving incoming packets, may be instructed to receive only urgent data or only non-urgent data. If there is pending urgent data and SocketRecv() is told to poll for normal data, it will return an error value, SE_URGENT, signalling that the program should call SocketRecv() again, this time to receive urgent data. (If, instead, you call SocketRecv() telling it to receive normal data again, it will return any pending normal data; it will not generate the SE_URGENT exception for the pending piece of urgent data -- unless another piece of urgent data has arrived in the meantime.)

Datagram connections do not support urgent data; SocketRecv() generates an SE_URGENT error on a datagram connection, it's really generating a SE_EXCEPTION value. If you receive such an error, it is a sign that the network is having troubles and packets may not be getting through.

You may force incoming urgent data to be treated in the same manner as normal data by setting the socket's "in-line" option. See SO_INLINE: In-line urgent data To force urgent data to be treated the same as normal data. This allows you to poll for both urgent and normal data with a single SocketRecv() command. Of course, setting this option means that you will not receive for information about socket options.

Socket: 7.2 Sending and Receiving Data: SocketSend() and SocketRecv()

The SocketSend() routine sends data over a socket. It takes several arguments:

Socket
The socket through which to send data
Buffer
A buffer containing the data to send
Buffer Size
The size of the data buffer.
Optional Address
Address to which to send data. If you pass an address here, make sure that you set the Address flag in the Flags field. If you don't set the Address flag, then this Optional Address argument may be a null pointer.
For sequenced packet or stream delivery type sockets, optional address is ignored.
For datagram delivery type sockets, specify the destination address of the packet in this parameter unless the socket has a default destination address as described below.
Datagram sockets can use the SocketConnect() routine to specify a default destination address. If they have no default address, you must specify the destination address for the packet. If there is a default destination address and you pass this optional address, then the optional address overrides the default for this send.
The following flags are available:
SSF_ADDRESS
To pass an optional address.
SSF_URGENT
To mark the data packet as urgent.
SSF_OPEN_LINK
To open the link if it is closed or to close the current link if it is idle and open it to a different location. If you do not set this flag, the datagram will only be sent if the link is opened to the desired remote link address.

Note that in some domains, the driver may delay before sending small packets of data because it is waiting for more packets to combine into one large packet. In many cases, this leads to more efficient communication. You may turn off this behavior by setting the "no delay" option. See SO_NODELAY: No Delay for small packets To force a driver to send small packets without waiting for other packets to bundle together. Drivers often delay sending small packets, waiting for other packets to bundle together in one larger packet. Th for information about setting socket options.

The SocketRecv() routine receives data from the socket. After filling the passed buffer with data, the socket discards its copy of the received data to make room for the next piece of incoming data.

Sequenced packet or datagram delivery sockets can only receive whole packets. If you grab data from the socket but don't grab all the data in the packet, the remaining data is lost. Thus, it's a good idea to establish a maximum packet size for sockets using these delivery types. If you're not sure how much room you'll need to receive a packet, call SocketRecv() with the SRF_PEEK flag. This allows you to "peek" at the incoming data without causing the socket to discard its copy.

The SocketRecv() routine takes the following arguments:

Socket
The socket from which to grab the data.
Buffer
Buffer to fill with data.
Buffer Size
Size of the data buffer. A packet-based socket will not retain data that does not fit in the buffer. A stream socket, however, will.
Time-out
The number of 1/60 second ticks to wait for incoming data. Pass SOCKET_NO_TIMEOUT to wait forever.
Optional Address
If you pass the Address flag of the Flags argument, then pass an empty SocketAddress buffer. (The buffer's SA_domain , SA_domainSize , and SA_addressSize fields must be initialized.) Make sure the buffer has room for the address data after the SocketAddress structure. Note that if either the domain name buffer or the address buffer isn't long enough to hold its string, the resulting truncated string isn't null-terminated.
If you do not set the Address flag, this Optional Address argument may be a null pointer.
The following flags are available:
SRF_ADDRESS
Return the source address in the Optional Address buffer.
SRF_URGENT
Return the first packet which has been marked urgent. (See Urgent Data .)
SRF_PEEK
Force the socket to not discard the data received.

The SocketRecv() routine returns the size of the received buffer. If this size is zero, there may be an error in the connection. Use ThreadGetError() to check for an error. If ThreadGetError() returns SE_NORMAL, then there was no error and you received a zero-length packet. For a datagram connection, an SE_EXCEPTION error indicates that the network is having problems and some datagrams may not be getting through.

Socket: 7.3 Sending and Receiving Data: Options

To customize a socket's sending and receiving behavior, use SocketSetIntSocketOption() (or SocketSetSocketOption() if you are using Assembly language). The following options are available:

SO_RECV_BUF : Receive Buffer Size
To change the size of the buffer used to hold incoming data.
SO_SEND_BUF : Send Buffer Size
To change the size of the buffer used to hold outgoing data.
SO_INLINE : In-line urgent data
To force urgent data to be treated the same as normal data. This allows you to poll for both urgent and normal data with a single SocketRecv() command. Of course, setting this option means that you will not receive urgent data as soon as possible.
SO_NODELAY : No Delay for small packets
To force a driver to send small packets without waiting for other packets to bundle together. Drivers often delay sending small packets, waiting for other packets to bundle together in one larger packet. This is oftentimes more efficient.
SO_OWNER : Owning Thread for socket
To specify a new owning thread for a socket. When the owning thread detaches from the Socket library (this happens automatically when the thread exits), the socket will be freed; any attempts to use the freed socket will result in errors. To make a socket survive its thread, you must assign it to a new thread. This option does not actually affect how the socket sends or receives data.

You may find out the current options for a socket by calling SocketGetIntSocketOption() ( SocketGetSocketOption() if you're using Assembly language).

Socket: 8 Closing the Connection

SocketInterrupt(), SocketClose(), SocketCloseSend(), SocketReset(), SocketCloseDomainMedium()

To halt either an accept or receive operation in progress, call SocketInterrupt() . If no such operation is in progress but the socket is listening or connected, the next such operation will be interrupted. Look out for the error SE_SOCKET_NOT_INTERRUPTIBLE; this signals that a non-interruptible operation is taking place on the socket.

When it comes time to close the connection and/or free a socket, call SocketClose() or SocketCloseSend() .

SocketCloseSend() works only with connected sockets. It partially shuts down the socket so that it can no longer send data; it can, however, still receive data. (Passing a partially closed socket to SocketSend() results in an SE_SOCKET_CLOSED error.) SocketCloseSend() sends an SE_CONNECTION_CLOSED exception packet to the other side of the connection to let it know that it's starting to shut itself down. Upon receipt of this exception packet, the other side of the connection will probably want to begin closing itself down as well.

Not all domains support closing the "send" part of a socket. In this case, the routine will return SE_CLOSE_SEND_NOT_SUPPORTED and you must use SocketClose() instead to close the connection and free the socket.

SocketClose() closes the socket (shutting down both incoming and outgoing data) and frees it. This routine may be used on a socket in any state. You must use SocketClose() to close (and free) a datagram socket.

To close a connection with extreme prejudice, call SocketReset() . Use this routine with caution as it bypasses all normal protocol-level handshaking and unilaterally kills the connection. When using this routine, you should still call SocketClose() to free the socket data structures and flush any unread data.

The SocketCloseDomainMedium() routine can close all connections over a given medium without sending any notifications. It effectively hangs up the connection. You pass a flag to specify whether the routine should only hang up if there are no active connections through the medium, or hang regardless. For more information about socket media and this routine, see Hardware Ports .

Socket: 9 Checking the Connection

SocketCheckListen(), SocketCheckReady()

The Socket library provides utility routines that query the status of a connection. This can be helpful when figuring out why a connection may not be working or confirming that a connection is working.

Use SocketCheckListen() on a port to find out the domain and medium for the first connection request on the port; that is, the first connection request which has not yet been accepted. Think of this as a limited form of "caller ID" -- a chance to find out about the incoming connection before accepting it. If SocketCheckListen() returns SE_PORT_NOT_LISTENING, it means there is no socket that is bound to the port and listening.

To "peek" at the next packet of incoming data, call SocketRecv() and pass the SRF_PEEK flag. This allows you to get the size of the next packet of incoming data and to look at its contents without destroying it.

To check one or more sockets to see if they have received data, received connection requests, or are ready to write, call SocketCheckReady() . It can check connections for incoming data or just incoming urgent data.

For programs which need to poll many sockets, SocketCheckReady() provides a tidy means to do this without spawning a thread for each socket.

The SocketCheckReady() routine takes an array of SocketCheckRequest structures. Each one of these structures contains a socket and a condition. SocketCheckReady() looks at each SocketCheckRequest structure in the array and returns the index of the first structure whose socket meets the condition.

Thus, to determine if a given socket is properly set up so that you can send data through it, pass SocketCheckReady() a one-element array (represented in pseudo-code):

{ mySocket, SC_WRITE, 0 }

To check several sockets to see whether any of them had received any connection requests, pass to SocketCheckReady() an array of the form (represented in pseudo-code):

{ socket1, SC_ACCEPT, 0 },
{ socket2, SC_ACCEPT, 0 },
{ socket3, SC_ACCEPT, 0 }

For any socket, you may check for one or more of the following conditions, as represented by SocketCondition values:

SC_ACCEPT
If a socket is listening for a connection, this condition indicates that another socket is trying to connect to the listening socket.
SC_READ
If a socket is connected, this condition indicates that a packet of data has come in and is ready to be read.
SC_EXCEPTION
If a socket is connected, this condition indicates that the socket has discovered something wrong with its connection.
SC_URGENT
If a socket is connected, this condition indicates that it has received a packet of data that was marked urgent.
SC_WRITE
This condition indicates that data may be sent through the socket.

If you query a socket about a condition that does not apply to its current state ( e.g. , ask a non-listening socket if it is ready to accept connections), then SocketCheckReady() returns SE_IMPROPER_CONDITION.

Socket: 10 Domain-Specific Information

Some portions of the Socket library API are domain specific; depending on which domain you're using, the domain name, address formats, port numbering schemes, etc. may differ.The following sections describe information specific to particular domains. Not all domains are available on any given device.

TCP/IP--GEOS Specific

TCP/IP--Standard

Loopback

Socket: 10.1 Domain-Specific Information: TCP/IP--GEOS Specific

The TCP/IP domain is a popular standard used for internet communications. The GEOS-specific version supports 32-bit port numbers. GEOS TCP/IP data may be transmitted via a regular TCP/IP network, though both the sending and receiving machines must support GEOS TCP/IP.

Domain Name:
"TCPIP_GEOS "
Port Numbers:
32-bit values.
Opaque Address Format:
This address will be either a TcpAccPntExtendedAddress ,a TcpOnlyExtendedAddress , or a TcpNonAccPntExtendedAddress .
TcpAccPntExtendedAddress: Use this structure when referring to an address identified by an access point ID number.
TcpOnlyExtendedAddress: Use this structure to identify a TCP address by its address instead of by its ID.
TcpNonAccPntExtendedAddress: Use this structure to identify a TCP address that is not a known access point. You will need to specify a link address (a phone number) as well as an IP address.
Notes:
  • On some devices (including the Nokia 9000i Communicator), you cannot make a TCP/IP connection or send packets until the device has a "raw" connection to the network--a PPP connection. Calling SocketOpenDomainMedium() with the TCP medium creates a PPP connection. The modem will dial, and the machine will connect to the PPP server, but no TCP level packets will be sent. If the PPP connection is already made, this routine will return SE_NORMAL, just as if the connection had been just now made. If the modem is already in use, an SE_MEDIUM_BUSY error will be returned. Use the SocketCloseDomaninMedium() to hang up the phone when done.
  • The Talk sample application shows how a device can make a raw PPP connection with the Internet Service Provider (ISP), getting the necessary information about the ISP from the built-in Access Point library. (Notice how the object AccpntControl is declared; notice the sample's TalkAddressClass handler for MSG_GEN_GUP_INTERACTION_COMMAND , which queries the AccessPointControl for the selected ISP and extracts the useful information about that ISP; finally, the MSG_CTP_CONNECT handler dials the phone to make the PPP connection.)

Code Display 23-2 Making the Raw TCP/IP Connection

Here we see three snippets of code from the Talk sample application
The AccessPointControl allows the user to choose an ISP:

@chunk char accpntMkr[] = "Access List";
@object AccessPointControlClass AccpntControl = {
    GI_states = GS_USABLE|GS_ENABLED;
    HINT_ACCESS_POINT_CONTROL_MINIMIZE_SIZE;
    ATTR_ACCESS_POINT_CONTROL_LIST_MONIKER = @accpntMkr; }

In @method TalkAddressClass, MSG_GEN_GUP_INTERACTION_COMMAND, we get information we'll need about the ISP

point = @call \
GeodeGetOptrNS(@AccpntControl)::MSG_ACCESS_POINT_CONTROL_GET_SELECTION();
/* store link info into address buffer */
rawAddress.UTA_link.TAPEA_linkSize = 3;
rawAddress.UTA_link.TAPEA_linkType = LT_ID;
rawAddress.UTA_link.TAPEA_accPntID = point;
/* the text of the address follows the link info */
alen = @call GeodeGetOptrNS(@IPText)::MSG_VIS_TEXT_GET_ALL_PTR(
                        (char *)&(rawAddress.UTA_ip[0]));
if (alen > MAX_IP_ADDR_STRING_LENGTH) FatalError(0);  /* too much text */
/* resolve the raw address into a SocketAddress */
theAddress.SA_addressSize = SocketResolve(theAddress.SA_domain,
                    (byte *)(&rawAddress),
                    sizeof(TcpAccPntExtendedAddress)+alen,
                    (byte *)(&addressBuffer),
                    MAX_ADDRESS_SIZE);

In MSG_CTP_CONNECT's handler, we make the PPP connection:

rval = SocketOpenDomainMedium((SocketAddress *) &theAddress, SOCKET_NO_TIMEOUT);
  • TCP/IP only supports one byte of urgent data in a packet. If you send a multi-byte packet marked urgent, it will be divided into two packets (on the receiving side). For example, a 32-byte packet marked urgent would be broken up into a 31-byte normal packet and a one-byte urgent packet.

    • If you set the SSF_URGENT flag to SocketSend() when sending a multi-byte packet, the last byte of the packet will be marked urgent.

    Recall that you can set the in-line option to specify that urgent data should be treated as normal data. In this case, urgent data will be treated as a normal 1-byte packet.

  • If you're not familiar with TCP/IP, TCP/IP illustrated by W. Richard Stevens provides a good introduction.

    The TCP/IP standard is defined in a number of RFCs. The RFCs listed below may be of interest. If you research these documents, note that the first is an index to the others.

    1720 J. Postel, I. Architecture Board (IAB), "INTERNET OFFICIAL PROTOCOL STANDARDS", 11/23/1994. (Obsoletes RFC1610) (Obsoleted by RFC1780) (STD 1)

    1700 J. Reynolds, J. Postel, "ASSIGNED NUMBERS", 10/20/1994. (Obsoletes RFC1340)

    1122 R. Braden, "Requirements for Internet hosts - communication layers", 10/01/1989.

    1123 R. Braden, "Requirements for Internet hosts - application and support", 10/01/1989.

    0791 J. Postel, "Internet Protocol", 09/01/1981. (Obsoletes RFC0760)

    0950 J. Mogul, J. Postel, "Internet standard subnetting procedure", 08/01/1985.

    0919 J. Mogul, "Broadcasting Internet datagrams", 10/01/1984.

    0922 J. Mogul, "Broadcasting Internet datagrams in the presence of subnets", 10/01/1984.

    0792 J. Postel, "Internet Control Message Protocol", 09/01/1981. (Obsoletes RFC0777)

    0768 J. Postel, "User Datagram Protocol", 08/28/1980.

    0793 J. Postel, "Transmission Control Protocol", 09/01/1981. (Updates RFC0761)

    1034 P. Mockapetris, "Domain names - concepts and facilities", 11/01/1987. (Obsoletes RFC0973) (Updated by RFC1101)

    1035 P. Mockapetris, "Domain names - implementation and specification", 11/01/1987. (Obsoletes RFC0973) (Updated by RFC1348)

    0974 C. Partridge, "Mail routing and the domain system", 01/01/1986.

    1531 R. Droms, "Dynamic Host Configuration Protocol", 10/07/1993. (Obsoleted by RFC1541)

    1533 S. Alexander, R. Droms, "DHCP Options and BOOTP Vendor Extensions", 10/08/1993. (Obsoletes RFC1497)

    1534 R. Droms, "Interoperation Between DHCP and BOOTP", 10/08/1993.

    1144 V. Jacobson, "Compressing TCP/IP headers for low-speed serial links", 02/01/1990.

    1547 D. Perkins, "Requirements for an Internet Standard Point-to-Point Protocol", 12/09/1993.

    1662 W. Simpson, "PPP in HDLC-like Framing", 07/21/1994. (Obsoletes RFC1549)

    1334 B. Lloyd, W. Simpson, "PPP Authentication Protocols", 10/20/1992.

    1661 W. Simpson, "The Point-to-Point Protocol (PPP)", 07/21/1994. (Obsoletes RFC1548)

    1570 W. Simpson, "PPP LCP Extensions", 01/11/1994. (Updates RFC1548)

    1663 D. Rand, "PPP Reliable Transmission", 07/21/1994.

    1332 PS G. McGregor, "The PPP Internet Protocol Control Protocol (IPCP)", 05/26/1992. (Obsoletes RFC1172)
  • Some (16-bit) constants (defined in sockmisc.h ) are available with the port numbers for common TCP services:
#define  ECHO            7  /* TCP/UDP */
#define  DISCARD         9  /* TCP/UDP */
#define  FTP_DATA        20 /* TCP */
#define  FTP             21 /* TCP */
#define  TELNET_SERVER   23 /* TCP */
#define  NAME_SERVER     42 /* UDP */
#define  WHOIS           43 /* TCP */
#define  DOMAIN_SERVER   53 /* TCP/UDP */
#define  FINGER          79 /* TCP */
  • The address data information with a SocketAddress structure takes the following form for the TCP domain:
    • typedef struct {
    word     TOEA_linkSize; 				/* 0 */
    byte    TOEA_ipAddr[4]; /* IP address */
}  TcpOnlyResolvedAddress;

    A variation on this structure is used for storing the address information for a TCP access point address. Along with the address information, there are three bytes of information about the nature of the link: the first of these bytes should have value LT_ID, so that the driver will know that the next two bytes represent a link ID; the other two bytes should be the access point's ID.

    typedef struct {
    word	TAPRA_linkSize; /* 3 */
    byte	TAPRA_linkType; /* LinkType (LT_ID) */
    word	TAPRA_accPntID;
    byte	  TAPRA_ipAddr[4]; /* IP address */
}  TcpAccPntResolvedAddress;

Socket: 10.2 Domain-Specific Information: TCP/IP--Standard

This is the standard TCP/IP domain, popular protocol for internet communications.

Domain Name:
"TCPIP"
Port Numbers:
16-bit values.
Opaque Address Format:
This address will be either a TcpAccPntExtendedAddress ,a TcpOnlyExtendedAddress , or a TcpNonAccPntExtendedAddress .
TcpAccPntExtendedAddress: Use this structure when referring to an address identified by an access point ID number.
TcpOnlyExtendedAddress: Use this structure to identify a TCP address by its address instead of by its ID.
TcpNonAccPntExtendedAddress: Use this structure to identify a TCP address that is not a known access point. You will need to specify a link address (a phone number) as well as an IP address.
Notes:
  • The notes for GEOS TCP/IP (starting on If you're not familiar with TCP/IP, TCP/IP illustrated by W. Richard Stevens provides a good introduction. The TCP/IP standard is defined in a number of RFCs. The RFCs listed below may be of interest. If you research these documents, note that t ) apply to the TCP/IP standard as well.
  • You cannot use the sequential packets delivery type with standard TCP/IP.

Socket: 10.3 Domain-Specific Information: Loopback

The loopback domain can only be used for communications between sockets on the same machine.

Domain Name:
"loopback"
Port Numbers:
32-bit values.
Opaque Address Format:
There is no address data: The loopback domain is used for intra-machine communication.
This document is a single-page version of a a multi-page document, suitable for easy printing.


Using Streams

It is often useful for an application to be able to write out data in an orderly manner to be read by another program or sent to a device such as a printer or modem. GEOS provides a mechanism called a stream to allow an orderly flow of data between two programs, between two threads of a multi-threaded program, or between a program and a device such as a serial or parallel port. The stream interface includes various ways to notify a program that bytes are available to read, or that there is room to write additional data.

The GEOS parallel and serial port drivers use the stream mechanism, so programs that will use these ports must do so via this mechanism; however, libraries exist to handle some of the more common uses for port communications. Programs do not need to access the serial or parallel ports in order to print because the spooler does it for them. For more information about the spooler and printing, see .

This chapter may be read with only a general understanding of GEOS and Object Assembly. Messaging is used heavily in informing the application using a stream of the stream's status, so a good understanding of object-oriented programming will be helpful. You should also be familiar with the GEOS memory manager. The chapter is divided into three main sections:

  • A general description of a GEOS stream and how the two sides (writers and readers) access it.
  • A description of the specific routines and conventions for using streams to send data to and receive data from a serial port.
  • A description of the specific routines and conventions for using a stream to send data to a parallel port.

1 Using Streams: The Basics
1.1 Initializing a Stream
1.2 Blocking on Read or Write
1.3 Writing Data to a Stream
1.4 Reading Data from a Stream
1.5 Shutting Down a Stream
1.6 Miscellaneous Functions
2 Using the Serial Ports
2.1 Initializing a Serial Port
2.2 Communicating
2.3 Closing a Serial Port
3 Using the Parallel Ports
3.1 Initializing a Parallel Port
3.2 Communicating
3.3 Closing a Parallel Port

Using Streams: 1 Using Streams: The Basics

A stream is a path along which information flows in one direction. At one end of the stream is a writer , who puts data into the stream. At the opposite end of the stream is a reader , who receives data from the stream. The writer and reader can be GEOS programs or device drivers. Data flow in both directions can be achieved by using two streams, one in each direction; this is how the serial port driver is implemented.

Even though the stream driver is loaded automatically when a stream is created, you will need to initialize, configure, and destroy any streams you use. The specific steps involved in this process are

  1. Get the handle of the stream driver.
    You will need to get this handle to use most of the stream-library routines. You can get this handle by calling GeodeUseDriver() .
  2. Create the stream.
    You must create each stream you plan on using. When a stream is initialized, it is designated a token that is used when calling the stream driver's strategy routine.
  3. Configure the stream.
    Arrange how your geode will be notified by the stream driver when certain situations (error received or buffer full/empty) arise, and make sure that all geodes accessing the stream have been given the stream's token.
  4. Use the stream.
  5. Shut down the stream.
    Not a trivial task, shutting down a stream can involve several synchronization issues.

Streams are created and managed by the Stream Driver . Programs written in Object-Assembly can call the driver directly. Goc programs cannot do this; instead, they make calls to the Stream Library , which in turn calls the Stream Driver, and passes back any return values.

Initializing a Stream

Blocking on Read or Write

Writing Data to a Stream

Reading Data from a Stream

Shutting Down a Stream

Miscellaneous Functions

Using Streams: 1.1 Using Streams: The Basics: Initializing a Stream

A stream is essentially a first-in-first-out data buffer, in which the writer is different from the reader. When the writer writes data to the stream, the kernel stores it in the buffer; when the reader requests information from the stream, the kernel retrieves the oldest data not yet read. The data is stored in a memory block; this block may be either fixed or movable. If it is movable, both the reader and the writer must lock the block before calling any stream routines.

Note that the kernel does not enforce who is the reader or writer to a stream. Any geode may call the appropriate stream library routine, passing in the token for a stream, and read or write data. However, in practice, only those threads with a legitimate interest in a stream will know the stream's token.

The serial and parallel drivers are built on top of the stream driver. There are separate routines to access the serial and parallel ports; these routines are discussed in Using the Serial Ports for the serial driver and Using the Parallel Ports for the parallel driver.

Creating a Stream

StreamOpen()

To create and initialize a new stream, call the routine StreamOpen() . This routine takes five arguments:

  • The handle of the stream driver.
  • The size of the stream buffer, in bytes. This may not be larger than 32767.
  • The GeodeHandle of the geode that will own this stream. When this geode exits, the stream will be freed; however, you should call StreamClose() before this happens.
  • A set of HeapFlags . The routine will have to allocate a block to hold the stream. The HeapFlags specify whether that block will be fixed or movable. If it is fixed, this argument should contain the flag HF_FIXED; otherwise it should be blank.
  • The pointer to a StreamToken variable. StreamOpen() will create the stream and write its token to this variable. You will need this token whenever you access the stream, for reading or writing.

If the creation is successful, StreamOpen() will return zero and store the stream's token in the StreamToken variable. You must see to it that both the reader and the writer have this token. If the stream cannot be created, the strategy routine will set an error flag and return either STREAM_CANNOT_ALLOC (if the memory for the stream's buffer cannot be allocated) or STREAM_BUFFER_TOO_LARGE (if the requested stream size was greater than 32767).

Assigning Readers and Writers

Once a stream is created, you must make sure that both ends will be managed--a stream that has only a writer or only a reader is not a useful stream.

When communicating with a device such as a serial or parallel port, the port is considered to be the entity on the other end. However, if two threads are communicating via a stream, you must make sure the other thread can gain access to the stream. The best way to do this is to set up a message that will be sent by the creator to the other geode. This message should contain as an argument the token of the stream and probably the direction of the stream (whether the creator will be reading or writing).

Once both geodes have the stream's token, each can access the stream normally. The next several sections explain how to access a stream for writing and reading.

Using Streams: 1.2 Using Streams: The Basics: Blocking on Read or Write

StreamBlocker, StreamError, StreamGetError(), StreamSetError()

A stream is a data buffer of limited size. When a thread writes to the stream, there is a chance it could run out of space. Similarly, when a thread reads from the stream, there is a possibility that it will try to read more data than is available; for example, it might try to read 500 bytes, when only 250 bytes of data are sitting in the stream.

There are two ways you can deal with these situations. One way is, you can instruct the thread to block. For example, if you try to write 500 bytes to a stream and there is only 200 bytes of space available, the driver will write the first 200 bytes to that space, then have the writing thread block until more space is available (i.e. until the reading thread has read some data). The writing thread will not resume execution until all the data has been written. Similarly, a reading thread could block until the stream provided all the data it requested.

The other approach is to have the stream driver write or read all it can, then return an appropriate error code. This requires a little more work by the calling thread, as it cannot assume that all the data is always read or written; however, it avoids the risk of deadlock.

All read and write routines are passed a member of the StreamBlocker enumerated type. This type has two members: STREAM_BLOCK, indicating that the calling thread should block in the situations described above; and STREAM_NO_BLOCK, indicating that the routine should immediately return with an error if enough space is not available. A single thread may, if it wishes, pass STREAM_BLOCK sometimes and STREAM_NO_BLOCK sometimes.

If a stream routine returns an error, the error will be a member of the StreamError enumerated type. The possible error values are described in the section for each routine.

Sometimes the device or application at the other end of the stream will need to return a special error value. For example, it may have its own error enumerated type, and may wish to signal an error to the application on the other end of the stream. It can do this by calling StreamSetError() . This routine is passed four arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamRoles enumerated type. The only appropriate values here are STREAM_ROLES_WRITER or STREAM_ROLES_READER.
  • A word-sized error code.

If the routine is succesful, it returns zero. Otherwise, it returns a member of the StreamError enumerated type.

To read a an error value set by the other side of a stream, call StreamGetError() . This routine is passed four arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamRoles enumerated type. The only appropriate values here are STREAM_ROLES_WRITER or STREAM_ROLES_READER.
  • A pointer to a word-sized variable.

If successful, the routine returns zero, and writes the other side's error value to the passed word-sized argument. If it is unsuccessful, the routine returns a StreamError value.

Using Streams: 1.3 Using Streams: The Basics: Writing Data to a Stream

StreamWrite(), StreamWriteByte()

To write data into a stream, call the routine StreamWrite() . This routine takes six arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamBlocker enumerated type, as described in Blocking on Read or Write .
  • The amount of data to be written, in bytes.
  • A pointer to a data buffer; the data will be copied from that buffer to the stream.
  • A pointer to an integer. StreamWrite() will write the number of bytes actually copied to that integer.

If all the data was written successfully, StreamWrite() will return zero and write the number of bytes written (i.e. the size of the data buffer passed) to the integer pointed to by the sixth argument. If it could not successfully write all the data, it will return one of the following StreamError values:

STREAM_WOULD_BLOCK
STREAM_NO_BLOCK had been passed, and there was no room to write any data to the stream. The sixth argument will be set to zero.
STREAM_SHORT_READ_WRITE
If STREAM_NOBLOCK had been passed, this means there was not enough room to write all the data. If STREAM_BLOCK had been passed, this means the stream was closed before all the data could be written. The sixth argument will contain the number of bytes actually written to the stream.
STREAM_CLOSING
The stream is in the process of being closed; no writing is permitted while this is happening. The sixth argument will be set to zero.
STREAM_CLOSED
The stream has already been closed. The sixth argument will be set to zero.

You may often want to write a single byte to the stream. There is a special routine to do this, StreamWriteByte() . This routine takes four arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamBlocker enumerated type, as described in Blocking on Read or Write .
  • The byte to be written.

If the byte is written successfully, StreamWriteByte() will return zero. Otherwise, it will return one of the following error values:

STREAM_WOULD_BLOCK
STREAM_BLOCK had been passed, and there was no room to write any data to the stream.
STREAM_CLOSING
The stream is in the process of being closed; no writing is permitted while this is happening.
STREAM_CLOSED
The stream has already been closed.

Using Streams: 1.4 Using Streams: The Basics: Reading Data from a Stream

StreamRead(), StreamReadByte()

To read data in a stream, call the routine StreamRead() . This routine takes six arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamBlocker enumerated type, as described in Blocking on Read or Write .
  • The amount of data to be read, in bytes.
  • A pointer to a data buffer; the data will be read from the stream into that buffer.
  • A pointer to an integer. StreamRead() will write the number of bytes actually read to that integer.

If the requested amount of data was read successfully, StreamRead() will return zero and write the number of bytes read (i.e. the size of the data buffer passed) to the integer pointed to by the sixth argument. If it could not successfully read the requested amount of data, it will return one of the following StreamError values:

STREAM_WOULD_BLOCK
STREAM_BLOCK had been passed, and there was no data waiting in the stream. The sixth argument will be set to zero.
STREAM_SHORT_READ_WRITE
If STREAM_NOBLOCK had been passed, this means the stream did not have the amount of data requested. If STREAM_BLOCK had been passed, this means the stream was closed before the requested amount of data could be read. The sixth argument will contain the number of bytes actually read from the stream.
STREAM_CLOSING
The stream is in the process of being closed; no reading is permitted while this is happening. The sixth argument will be set to zero.

You may often want to read a single byte from the stream. There is a special routine to do this, StreamReadByte() . This routine takes four arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamBlocker enumerated type, as described in Blocking on Read or Write .
  • A pointer to a byte-sized variable; the data byte read will be written to this variable.

If the byte is written successfully, StreamReadByte() will return zero. Otherwise, it will return one of the following error values:

STREAM_WOULD_BLOCK
STREAM_BLOCK had been passed, and there was no data waiting in the stream.
STREAM_CLOSING
The stream is in the process of being closed; no reading is permitted while this is happening.

Using Streams: 1.5 Using Streams: The Basics: Shutting Down a Stream

StreamClose()

Either the writer or the reader can instigate stream shutdown by calling StreamClose() . When one of the two calls this routine, the shut-down process is started; it will not be completed until the other calls the routine.

If the writer calls StreamClose() , it may specify that the data already in the buffer be flushed (immediately cleared), or that it linger . If you specify that the data should linger, the data will be preserved as long as the reader has the stream open. The reader can continue to read data normally until it runs out of data. The last read-operation will most likely return STREAM_SHORT_READ_WRITE; after that, all attempts to read data will generate the error STREAM_CLOSING. At that point, the reader should call StreamClose() . (If the data was flushed by the writer, the next read attempt will return STREAM_CLOSING.)

To shut down the stream, call the routine StreamClose() . This routine is passed the following arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A Boolean value saying whether the data in the stream should be kept until it's read; false (i.e. zero) indicates it should be flushed.

If you are using the Serial or Parallel drivers (described later in this chapter), you do not have to coordinate the closure of a stream.

Using Streams: 1.6 Using Streams: The Basics: Miscellaneous Functions

StreamFlush(), StreamQuery()

To flush all the pending (written but unread) data from a stream, call the routine StreamFlush() . This routine is passed two arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.

To find out how much free space is available in a stream, or how much data is waiting to be read, call StreamQuery() . This routine is passed four arguments:

  • The GeodeHandle of the stream driver.
  • The StreamToken of the stream.
  • A member of the StreamRoles enumerated type. The only appropriate values here are STREAM_ROLES_WRITER (to find the amount of free space available for writing) or STREAM_ROLES_READER (to find the amount of data waiting to be read).
  • A pointer to an integer variable.

If the call is successful, StreamQuery() returns zero and writes its return value to the fourth argument. If you pass STREAM_ROLES_WRITER, StreamQuery() writes the number of bytes of free space available in the stream buffer. If you pass STREAM_ROLES_READER, StreamQuery() returns the number of bytes of data waiting to be read. If the call is unsuccessful, StreamQuery() returns a StreamError .

Using Streams: 2 Using the Serial Ports

The serial driver uses streams to control the flow of data to and from serial ports. The kernel automatically copies data from the serial port into one stream for reading, and sends data from another stream into the serial port. An application which wishes to use the serial port simply reads and writes data from those streams.

Initializing a Serial Port

Communicating

Closing a Serial Port

Using Streams: 2.1 Using the Serial Ports: Initializing a Serial Port

Like the stream driver, the serial driver is not accessed directly from Goc code. Instead, a Goc application makes calls to the Stream Library, which passes the requests through to the Serial Driver's strategy routine. Each serial-port command must be passed the GeodeHandle of the Serial Library; again, you can find this handle by calling GeodeGetInfo() .

The serial driver uses two streams, one for data going out to the serial port (outgoing) and one for data coming in from the serial port (incoming). Your program is the writer of the outgoing and the reader of the incoming. (In both cases, the port acts as the opposite user.)

Opening a Serial Port

SerialOpen()

To open a serial port, call the routine SerialOpen() . This routine is passed the following arguments:

  • The GeodeHandle of the serial-port driver.
  • A member of the SerialUnit enumerated type; this specifies which serial port is being opened. The type's members are SERIAL_COM1, SERIAL_COM2, and so on up to SERIAL_COM8.
  • A member of the StreamOpenFlags enumerated type, indicating what to do if the requested serial port is busy (either STREAM_OPEN_NO_BLOCK, indicating that the routine should return an error immediately; or STREAM_OPEN_TIMEOUT, indicating that the routine should wait a specified number of clock ticks to see if the port will free up).
  • The total size of the stream to be used as an input buffer, in bytes.
  • The total size of the stream to be used as an output buffer, in bytes.
  • The maximum number of ticks to wait for the serial port to become available (if STREAM_OPEN_TIMEOUT was passed).

A flag is returned to indicate whether the serial port could be opened; if not, a value of type StreamError will be returned to indicate the reason. Possible stream error values are STREAM_BUFFER_TOO_LARGE and STREAM_CANNOT_CREATE, and the additional values STREAM_NO_DEVICE (if the serial port does not exist) or STREAM_DEVICE_IN_USE (if the device is busy and the StreamOpenFlags passed indicate not to wait).

Note that when using the serial driver, you do not identify the stream by a stream token but rather by the serial port number, known as a unit number . When accessing a serial port, you simply pass the port's unit number along with either STREAM_READ (if reading from the stream) or STREAM_WRITE (if writing to the stream); because each port has two streams associated with it, you must specify both parameters. The serial driver will understand which stream you are accessing.

Configuring a Serial Port

SerialSetFormat(), SerialGetFormat(),SerialSetModem(), SerialGetModem(), SerialSetFlowControl()

Communication using a serial port requires that parity, speed, and flow control be properly set. To control these settings, call SerialSetFormat() , passing the following arguments:

  • The GeodeHandle of the serial-port driver.
  • The member of the SerialUnit enumerated type.
  • A one-byte record of type SerialFormat , specifying the parity, word length, and number of stop bits to be used on the serial line; this record is described below.
  • A member of the SerialMode enumerated type, set to indicate the level of flow control: SM_COOKED to indicate XON/XOFF flow control with characters stripped to seven bits, SM_RARE to indicate XON/XOFF flow control but incoming characters left alone, or SM_RAW to indicate no flow control.
  • The baud rate to use, a member of the enumerated type SerialBauds , which has the following members:
typedef	enum
{
    SERIAL_BAUD_115200				= 1,
    SERIAL_BAUD_57600				= 2,
    SERIAL_BAUD_38400				= 3,
    SERIAL_BAUD_19200				= 6,
    SERIAL_BAUD_14400				= 8,
    SERIAL_BAUD_9600				= 12,
    SERIAL_BAUD_7200				= 16,
    SERIAL_BAUD_4800				= 24,
    SERIAL_BAUD_3600				= 32,
    SERIAL_BAUD_2400				= 48,
    SERIAL_BAUD_2000				= 58,
    SERIAL_BAUD_1800				= 64,
    SERIAL_BAUD_1200				= 96,
    SERIAL_BAUD_600				= 192,
    SERIAL_BAUD_300				= 384
} SerialBaud;

SerialFormat is a byte-sized record that specifies the parity, word-length, and number of stop bits for the serial line. The record has the following fields:

SERIAL_FORMAT_DLAB
This is for internal use only; it must be set to zero.
SERIAL_FORMAT_BREAK
If set, this causes a BREAK condition to be asserted on the line. You must explicitly clear this bit again to resume normal operation.
SERIAL_FORMAT_PARITY
This three-bit field holds the parity to expect on receive and use on transmit. It uses the SerialParity enumerated type, which has the following members:
typedef		enum {
    SERIAL_PARITY_NONE				= 0,
    SERIAL_PARITY_ODD				= 1,
    SERIAL_PARITY_EVEN				= 3,
    SERIAL_PARITY_ONE				= 5,
    SERIAL_PARITY_MARK				= 5,
    SERIAL_PARITY_ZERO				= 7,
    SERIAL_PARITY_SPACE				= 7
} SerialParity;
SERIAL_FORMAT_EXTRA_STOP
If this is set, extra stop-bits will be sent. One stop bit is always sent. However, if you set this flag, an extra 1/2 stop bit will be sent if the word-length is 5 bits long; an extra 1 stop bit will be sent if the frame is 6, 7, or 8 bits long.
SERIAL_FORMAT_LENGTH
This two-bit field holds the length of each data word, minus five (i.e. a five-bit word is represented with a zero, a six-bit word with a one).

To find out the current settings of a serial port, call SerialGetFormat() . This routine is passed five arguments:

  • The GeodeHandle of the serial-port driver.
  • The member of the SerialUnit enumerated type.
  • A pointer to a SerialFormat variable. SerialGetFormat() will write the format data to this variable.
  • A pointer to a SerialMode variable. SerialGetFormat() will write the appropriate mode constant (SM_COOKED, XON/XOFF, or SM_RARE) to this variable.
  • A pointer to a SerialBaud variable. SerialFormat() will write the appropriate constant to this variable.

As with other serial port routines, if the routine is successful, it will return zero; if it is unsuccessful, it will return an error code.

If you are using a modem's hardware flow control, you will have to configure the modem appropriately. You can do this by calling SerialSetModem() . This routine is passed three arguments:

  • The GeodeHandle of the serial-port driver.
  • The member of the SerialUnit enumerated type.
  • A record of type SerialModem . This record has four fields: SERIAL_MODEM_OUT2, SERIAL_MODEM_OUT1, SERIAL_MODEM_RTS, and SERIAL_MODEM_DTR. Set these fields to indicate how the control-bits should be set.

To find out what flow control is being used, call SerialGetModem() . This routine is passed three arguments:

  • The GeodeHandle of the serial-port driver.
  • The member of the SerialUnit enumerated type.
  • A pointer to a record of type SerialModem . SerialGetModem() will set this record's SERIAL_MODEM_OUT2, SERIAL_MODEM_OUT1, SERIAL_MODEM_RTS, and SERIAL_MODEM_DTR bits appropriately.

You can also set the flow control without setting the other format options. Do this by calling SerialSetFlowControl() . This routine is passed the following arguments:

  • The GeodeHandle of the serial-port driver.
  • The member of the SerialUnit enumerated type.
  • A record of type SerialModem . This record has four fields: SERIAL_MODEM_OUT2, SERIAL_MODEM_OUT1, SERIAL_MODEM_RTS, and SERIAL_MODEM_DTR. Set these fields to indicate how the control-bits should be set.
  • A member of the SerialMode enumerated type, set to indicate the level of flow control: SM_COOKED to indicate XON/XOFF flow control with characters stripped to seven bits, SM_RARE to indicate XON/XOFF flow control but incoming characters left alone, or SM_RAW to indicate no flow control.
  • A record of type SerialModemStatus to indicate which lines (chosen from DCD, DSR, and CTS) should be used to control outgoing data (if hardware flow control is selected). When one of the selected lines is de-asserted by the remote system, the serial driver will not transmit any more data until the state changes.

Using Streams: 2.2 Using the Serial Ports: Communicating

SerialRead(), SerialReadByte(), SerialWrite(), SerialWriteByte(), SerialQuery(), SerialFlush(), SerialGetError(), SerialSetError(), SerialError

Communicating with a serial port is very much like using any other stream. Special versions of the stream routines are provided, but they function just like their stream counterparts.

To read data from a serial port, call SerialRead() or SerialReadByte() . These routines take the same arguments as their Stream...() counterparts, except that each one must be passed the handle of the Serial Driver, not the Stream Driver, and each routine is passed the SerialUnit for the appropriate port, instead of being passed a stream token. These routines behave exactly like their Stream...() counterparts.

To write data to a serial port, call SerialWrite() or SerialWriteByte() . Again, these routines behave like their Stream...() counterparts, and take similar arguments.

To find out if you can read or write data to the port, call SerialQuery() . Again, this routine behaves like its Stream...() equivalent. To flush any data from the input or output stream, call SerialFlush() .

To send an error to the other side of a serial connection, call SerialSetError() . This routine behaves much like StreamSetError() . The only difference is that the error code passed must be a member of the SerialError enumerated type. This type has the following members:

SE_BREAK
Break condition detected on line.
SE_FRAME
Framing error (e.g. improper number of stop bits).
SE_PARITY
Parity error in received character.
SE_OVERRUN
A new byte was received before the previous byte was read; the previous byte was discarded.

To read an error condition passed by the other side of a serial connection, call SerialGetError() . This routine behaves just like StreamGetError() . The error code returned will be a member of the SerialError enumerated type.

Using Streams: 2.3 Using the Serial Ports: Closing a Serial Port

SerialClose(), SerialCloseWithoutReset()

To close a serial port, call the routine SerialClose() . This routine is passed three arguments:

  • The GeodeHandle of the serial-port driver.
  • The member of the SerialUnit enumerated type.
  • Either STREAM_LINGER (to instruct the kernel to close the port after all outgoing data in the buffer has been sent), or STREAM_DISCARD (to instruct the kernel to close the port right away and discard all buffered data).

This function returns immediately whether the port was closed right away or not. However, if STREAM_LINGER is specified, the port may not be re-opened until all the data in the Serial Port's buffer has been dealt with.

You can also instruct the serial driver to close the stream to a port, without actually resetting the port. Do this by calling SerialCloseWithoutReset() . This routine is passed the same arguments as SerialClose() .

Using Streams: 3 Using the Parallel Ports

Using a parallel port is simpler than using a serial port since data goes in only one direction. GEOS does not currently support reading data from a parallel port.

Parallel ports are used primarily for printing, which is handled by the Spool Object Library. The information in this section is useful only to programmers whose applications will need to send data out through the parallel port without using the spooler. Most applications, however, will use the spooler for any and all parallel port use.

Initializing a Parallel Port

Communicating

Closing a Parallel Port

Using Streams: 3.1 Using the Parallel Ports: Initializing a Parallel Port

ParallelOpen()

To open a parallel port, call the routine ParallelOpen() . This routine is passed the following arguments:

  • The GeodeHandle of the parallel-port driver.
  • A member of the ParallelUnit enumerated type; this specifies which parallel port is being opened. The type's members are PARALLEL_LPT1, PARALLEL_LPT2, PARALLEL_LPT3, and PARALLEL_COM4.
  • A member of the StreamOpenFlags enumerated type, indicating what to do if the requested parallel port is busy (either STREAM_OPEN_NO_BLOCK, indicating that the routine should return an error immediately; or STREAM_OPEN_TIMEOUT, indicating that the routine should wait a specified number of clock ticks to see if the port will free up).
  • The unit number of the parallel port in question--this is a value of type ParallelPortNums .
  • The total size of the stream to be used as an output buffer, in bytes.
  • The maximum number of ticks to wait for the parallel port to become available (if STREAM_OPEN_TIMEOUT was passed).

A flag is returned to indicate whether the parallel port could be opened; if not, a value of type StreamError will be returned to indicate the reason. Possible stream error values are STREAM_BUFFER_TOO_LARGE and STREAM_CANNOT_CREATE, and the additional values STREAM_NO_DEVICE (if the parallel port does not exist) or STREAM_DEVICE_IN_USE (if the device is busy and the StreamOpenFlags passed indicate not to wait (or not to wait any longer)).

Note that when using the parallel driver, you do not identify the stream by a stream token but rather by the parallel port number, known as a unit number . When accessing a parallel port, you simply pass the port's unit number along with either STREAM_READ (if reading from the stream) or STREAM_WRITE (if writing to the stream); because each port has two streams associated with it, you must specify both parameters. The parallel driver will understand which stream you are accessing.

Once the port is selected, the PC will assert the SLCTIN signal, which usually will place the device on-line.

Using Streams: 3.2 Using the Parallel Ports: Communicating

ParallelWrite(), ParallelWriteByte()

Writing to a parallel port is much like writing to any other stream. To write data, call ParallelWrite() or ParallelWriteByte() . These routines take the same arguments as their Stream...() components, except that each one must be passed the handle of the Parallel Driver, not the Stream Driver, and each routine is passed the ParallelUnit for the appropriate port, instead of being passed a stream token. These routines behave exactly like their Stream...() counterparts.

To send an error to the other side of a serial connection, call ParallelSetError() . (This routine is generally not called by applications.) This routine behaves much like StreamSetError() . It is passed four arguments:

  • The GeodeHandle of the parallel-port driver.
  • The appropriate member of the ParallelUnit enumerated type.
  • The appropriate member of the StreamRoles enumerated type.
  • A ParallelError record. This word-sized record has the following flags:
PE_FATAL
Set if the driver encountered an error that means it can't continue.
PE_TIMEOUT
Set if there is no response from the printer, but no other (non-masked) error is present.
PE_NOPAPER
Set if the device is out of paper.
PE_OFFLINE
Set if the device is off-line.
PE_ERROR
Indicates a general error.

To read an error condition passed by the other side of a serial connection, call ParallelGetError() . This routine behaves much like StreamGetError() . The error code returned will be a member of the ParallelError enumerated type.

Using Streams: 3.3 Using the Parallel Ports: Closing a Parallel Port

ParallelClose()

To close a parallel port, ParallelClose() . This routine takes the following arguments:

  • The GeodeHandle of the parallel-port driver.
  • The member of the ParallelUnit enumerated type.
  • Either STREAM_LINGER (to instruct the kernel to close the port after all outgoing data in the buffer has been sent), or STREAM_DISCARD (to instruct the kernel to close the port right away and discard all buffered data).

This function returns immediately whether the port was closed right away or not. However, if STREAM_LINGER is specified, the port may not be re-opened until all the data in the parallel port's buffer has been dealt with.

This document is a single-page version of a a multi-page document, suitable for easy printing.


GEOS C Routine Reference

 This document is a single-page version of a a multi-page document, suitable for easy printing.

GEOS C Routines Reference

This document contains reference information for most of the GEOS system routines. You may look up information in this and other GEOS SDK documentation by consulting the index. If, instead, you wish to browse this reference, you may choose one of the links below; the text of each is the name of the first routine documented on its page of the document.

AccessPointCommit() ...
AccessPointGetStringPropertyBlock() ...
ArrayQuickSort() ...
CCB() ...
CFatalError() ...
ChunkArrayDelete() ...
ChunkArrayElementToPtr() ...
ChunkArrayGetCountHandles() ...
ChunkArraySort() ...
ClipboardClearQuickTransferNotification() ...
ClipboardQueryItem() ...
ClipboardSetQuickTransferFeedback() ...
ConstructOptr() ...
ContactGetDBHandle() ...
ContactMatchNumber() ...
DataStoreCreate() ...
DataStoreDiscardRecord() ...
DataStoreGetFieldChunk() ...
DataStoreGetRecordCount() ...
DataStoreNewRecord() ...
DataStoreSetField() ...
DBAlloc() ...
DBDeref() ...
DBLock() ...
DiskCheckInUse() ...
DiskForEach() ...
DiskGetDrive() ...
DiskSave() ...
DriveGetDefaultMedia() ...
EC() ...
ECCheckHugeArray() ...
ECCheckProcessHandle() ...
ECVMCheckVMBlockHandle() ...
ElementArrayDelete() ...
ElementArrayRemoveReference() ...
FatalError() ...
FileCreate() ...
FileDeleteDir() ...
FileEnum() ...
FileEnumLocateAttr() ...
FileGetDiskHandle() ...
FileOpen() ...
FileRename() ...
FileSetHandleExtAttributes() ...
FileTruncate() ...
FloatFloatToAscii() ...
FoamCancelIfNoFreeSpace() ...
FoamDBAddFieldToRecord() ...
FoamDBGetCurrentRecordID() ...
FoamDBResumeUpdates() ...
FoamDisplayDeleteWarning() ...
FoamDocCheckProtocol() ...
FoamDocCreate() ...
FoamDocSetBlockHandle() ...
FoamGenerateUniqueFilename() ...
FoamStandardDialogOptr() ...
FoamStartSoundSequence() ...
FormatIDFromManufacturerAndType ...
GCNListAdd() ...
GCNListSend() ...
GenCopyChunk() ...
GeodeAllocQueue() ...
GeodeGetAppObject() ...
GeodeLoadDGroup ...
GeoFree() ...
GrApplyTranslation() ...
GrClearBitmap() ...
GrDestroyBitmap() ...
GrDrawCurve() ...
GrDrawLine() ...
GrDrawRect() ...
GrDrawRoundRect() ...
GrEndGString() ...
GrFillEllipse() ...
GrGetAreaColor() ...
GrGetDefFontID() ...
GrGetLineMask() ...
GrGetPtrRegBounds() ...
GrInitDefaultTransform() ...
GrNewPage() ...
GrSDivDWFByWWF() ...
GrSetClipPath() ...
GrSetLineAttr() ...
GrSetPalette() ...
GrSetTextMode() ...
GrTestPointInPath() ...
GrUDivWWFixed() ...
GSCloseSubPath() ...
GSDrawEllipse() ...
GSDrawSpline() ...
GSFillBitmap() ...
GSMoveTo() ...
GSSetCustomLineMask() ...
GSSetLineEnd() ...
GSSetSubscriptAttr() ...
HAL_COUNT() ...
HandleV() ...
HugeArrayDestroy() ...
HugeArrayPrev() ...
IACPProcessMessage() ...
ImpexExportToMetafile() ...
InitFileEnumStringSection() ...
InitFileReadStringBlock() ...
InitFileRevert() ...
InkDBGetDisplayInfo() ...
InkFolderGetNumChildren() ...
InkNoteGetCreationDate() ...
InkNoteSetModificationDate() ...
isalnum() ...
LMemAlloc() ...
LMemGetChunkSize() ...
LMemReAlloc() ...
LocalAsciiToFixed() ...
LocalCustomParseDateTime() ...
LocalGeosToCodePage() ...
LocalLexicalValue() ...
MailboxAcknowledgeMessageReceipt() ...
MailboxGetBodyFormat() ...
MailboxGetVMFile() ...
MakeWWFixed() ...
MemAlloc() ...
MemDowngradeExclLock() ...
MemLock() ...
MemModifyFlags() ...
MemThreadGrab() ...
MemUnlock() ...
ModemAnswerCall() ...
ModemSetMessageDataNotify() ...
NameArrayAdd() ...
NameArrayFind() ...
ObjCompFindChildByOptr() ...
ObjCompProcessChildren() ...
ObjDeref() ...
ObjDoRelocation() ...
ObjFreeChunk() ...
ObjInitDetach() ...
ObjIsClassADescendant() ...
ObjResizeMaster() ...
ObjVarAddData() ...
ObjVarDeleteDataAt() ...
ObjVarScanData() ...
ParallelClose() ...
PCCOMABORT() ...
qsort ...
realloc() ...
SerialGetFormat() ...
SGC_MACHINE ...
SocketBind() ...
SocketClose() ...
SocketGetAddressController() ...
SocketInterrupt() ...
SocketRemoveLoadOnMsg() ...
SoundAllocMusic() ...
SoundFreeMusic() ...
SoundPlayToMusicStream() ...
SpeedDialGetNumber() ...
SpoolGetNumPaperSizes() ...
SpoolModifyPriority() ...
StreamOpen() ...
StreamWrite() ...
SysGetPenMode() ...
SysShutdown() ...
TextSearchInString() ...
ThreadAttachToQueue() ...
ThreadGetError() ...
ThreadPSem() ...
TimerSleep() ...
TocNameArrayFind() ...
TokenDefineToken() ...
TokenLoadMonikerBuffer() ...
TokenLoadTokenChunk() ...
UserAllocObjBlock ...
UserRemoveAutoExec() ...
UtilAsciiToHex32() ...
VirtualSerialSetFormat() ...
VMClose() ...
VMGetMapBlock() ...
VMPreserveBlocksHandle() ...
WinAckUpdate() ...
WinGeodeSetInputObj() ...
WinScroll() ...

AccessPointCommit()

void AccessPointCommit();

This routine commits any access point data changes to permanent storage. The AccPnt library normally calls this routine when the device shuts down; applications thus need not call it. If you're trying to track down a bug which is likely to cause the system to crash before the changes can be committed, you may find this routine useful.

Include: accpnt.goh

AccessPointCompareStandardProperty()

void AccessPointCompareStandardProperty(
        AccessPointStandardProperty 					prop,
        char 					*str);

This routine compares the passed string to the name of the passed property.

Include: accpnt.goh

AccessPointCreateEntry()

word AccessPointCreateEntry(
        word 		loc,
        AccessPointType 		apt);

This routine creates a new access point.

After calling this routine, you will probably want to use the AccessPointSet...Property() routines to provide some information about the new access point.

Pass:

loc
The access point before which to insert the new access point. Pass zero to append the new access point to the end of the list.
apt
The AccessPointType of the new access point. APT_INTERNET for an ISP, APT_TERMINAL for a dialup, etc.

Return: ID number of the new access point.

Include: accpnt.goh

See Also: AccessPointSetStringProperty(), AccessPointSetIntegerProperty().

AccessPointDestroyEntry()

Boolean AccessPointDestroyEntry(
        word id);

This routine deletes an access point's information. If it returns non-zero, there was an error. (Perhaps the access point was locked or did not exist.)

Include: accpnt.goh

AccessPointDestroyProperty()

Boolean AccessPointDestroyProperty(
        word id,
        char *prop);

This routine destroys one of an access point's properties.

Pass:

id
The access point's ID number.
prop
Either an AccessPointStandardProperty value or a pointer to a buffer containing the property's name.

Return: Zero if there was no error, true if there was (no such access point, no such property, access point locked).

Include: accpnt.goh

See Also: AccessPointSetStringProperty(), AccessPointSetIntegerProperty().

AccessPointGetEntries()

ChunkHandle AccessPointGetEntries(
        MemHandle 		mh,
        ChunkHandle 		chunk,
        AccessPointType 		apt);

This routine gets a chunk array of all the access point IDs corresponding to access points of a given AccessPointType . Each element of the arraay is word-sized.

Pass:

mh
The memory block in which the chunk array shall reside.
chunk
Either the ChunkHandle of a chunk array, or zero to request that the library allocate a chunk array.
apt
The AccessPointType we're interested in: APT_INTERNET, APT_TERMINAL, etc.

Return: The ChunkHandle of the chunk array.

Include: accpnt.goh

AccessPointGetIntegerProperty()

Boolean AccessPointGetIntegerProperty(
        word 	id,
        char 	*prop,
        int 	*val);

This routine retrieves the value of one of an access point's property, representing the value as an integer.

Pass:

id
The access point's ID number.
prop
Either an AccessPointStandardProperty value or a pointer to a buffer containing the property's name.
val
Pointer to an integer; the routine will fill in this integer with the value of the property.

Return: Zero if there was no error, true if there was (no such access point, no such property).

Include: accpnt.goh

See Also: AccessPointGetStringPropertyBuffer(), AccessPointGetStringPropertyBlock().

AccessPointGetStringPropertyBlock()

BooleanAccessPointGetStringPropertyBlock(
        word 		id,
        char 		*prop,
        MemHandle 		*data,
        int 		*datalen);

This routine retrieves the value of one of an access point's property, returning the value as a string in a block of memory.

Pass:

id
The access point's ID number.
prop
Either an AccessPointStandardProperty value or a pointer to a buffer containing the property's name.
data
MemHandle of block to fill with value (pass zero to request that the block be allocated).
dataLen
Pointer to integer; the routine will fill in this integer with the length of the value string, excluding the trailing NULL.

Return: Zero if there was no error, true if there was (no such access point, no such property).

Include: accpnt.goh

See Also: AccessPointGetStringPropertyBuffer(), AccessPointGetIntegerProperty().

AccessPointGetStringPropertyBuffer()

Boolean AccessPointGetStringPropertyBuffer(
        word 	id,
        char 	*prop,
        char 	*buf,
        int 	*datalen);

This routine retrieves the value of one of an access point's property, returning the value as a string in a buffer of memory.

Pass:

id
The access point's ID number.
prop
Either an AccessPointStandardProperty value or a pointer to a buffer containing the property's name.
buf
Pointer to buffer to fill with value.
dataLen
Pointer to integer; pass the size of buf. The routine will fill in this integer with the length of the value string.

Return: Zero if there was no error, true if there was (no such access point, no such property).

Include: accpnt.goh

See Also: AccessPointGetStringPropertyBlock(), AccessPointGetIntegerProperty().

AccessPointGetType()

AccessPointType AccessPointCommit(
        word id);

This routine returns the type of an access point: APT_INTERNET, APT_TELNET, etc.

Include: accpnt.goh

AccessPointInUse()

Boolean   AccessPointInUse (word id);

If the access point with the ID id is being used by a connection, then this routine returns TRUE (non-zero); otherwise, it returns FALSE (zero).

Include: accpnt.goh

AccessPointIsEntryValid()

Boolean AccessPointIsEntryValid(
        word id);

This routine returns zero if the passed access point does not exist; it returns non-zero if it does exist.

Include: accpnt.goh

AccessPointLock()

void AccessPointLock(
        word id);

This routine locks an access point, preventing changes to it.

Include: accpnt.goh

See Also: AccessPointUnlock().

AccessPointSetIntegerProperty()

Boolean AccessPointSetIntegerProperty(
        word 	id,
        char+ 	*prop,
        int 	val);

This routine sets the value of one of an access point's properties to an integer.

Pass:

id
The access point's ID number.
prop
Either an AccessPointStandardProperty value or a pointer to a buffer containing the property's name.
val
The new value for the property.

Return: Zero if there was no error, true if there was (no such access point, access point locked).

Include: accpnt.goh

See Also: AccessPointSetStringProperty().

AccessPointSetStringProperty()

Boolean AccessPointSetStringProperty(
        word id,
        char *prop,
        char *val);

This routine sets the value of one of an access point's properties to a string.

Pass:

id
The access point's ID number.
prop
Either an AccessPointStandardProperty value or a pointer to a buffer containing the property's name.
val
The new value for the property.

Return: Zero if there was no error, true if there was (no such access point, access point locked).

Include: accpnt.goh

See Also: AccessPointSetIntegerProperty().

AccessPointUnlock()

void AccessPointUnlock(
        word id);

This routine unlocks an access point that was previously locked.

Include: accpnt.goh

See Also: AccessPointLock().

ArrayQuickSort()

void	ArrayQuickSort(
        void	*array,	/* Pointer to start of array */
        word	count,	/* Number of elements in array */
        word	elementSize,	/* Size of each element (in bytes) */
        word	valueForCallback, 		/* Passed to callback routine */
        QuickSortParameters *parameters);

This routine sorts an array of uniform-sized elements. It uses a modified QuickSort algorithm, using an insertion sort for subarrays below a certain size. The routine calls a callback routine to actually compare elements.

ArrayQuickSort() is passed five arguments: A pointer to the first element of the array, the number of elements in the array, the size of each element in bytes, a word of data (which is passed to all callback routines), and a pointer to a QuickSortParameters structure.

Before ArrayQuickSort() examines or changes any element, it calls a locking routine specified by the QuickSortParameters structure. This routine locks the element, if necessary, and takes any necessary prepatory steps. Similarly, after ArrayQuickSort() is finished with a routine, it calls an unlocking routine in the QuickSortParameters . Each of these routines is passed a pointer to the element and the word of callback data which was passed to ArrayQuickSort() .

The sort routine does not compare elements. Rather, it calls a comparison callback routine specified by the QuickSortParameters . This callback routine should be declared _pascal. Whenever ArrayQuickSort() needs to compare two elements, it calls the callback routine, passing the addresses of the elements and the valueForCallback word which was passed to ChunkArraySort() . The callback routine's return value determines which element will come first in the sorted array:

  • If element el1 ought to come before el2 in the sorted array, the callback routine should return a negative integer.
  • If element el1 ought to come after el2 in the sorted array, the callback routine should return a positive integer.
  • If it doesn't matter whether el1 comes before or after el2 in the array, the callback routine should return zero.

Include: chunkarr.h

Tips and Tricks: You may need to sort an array based on different criteria at different times. The simplest way to do this is to write one general-purpose callback routine and have the valueForCallback word determine how the sort is done. For example, the same callback routine could sort the array in ascending or descending order, depending on the valueForCallback .

Be Sure To: Lock the array on the global heap (unless it is in fixed memory).

Warnings: Do not have the callback routine do anything which might invalidate pointers to the array. For example, if the array is in a chunk, do not resize the chunks or allocate other chunks in the same LMem heap.

See Also: QuickSortParameters, ChunkArraySort().

BlockFromTransferBlockID

VMBlockHandle BlockFromTransferBlockID(id);
        TransferBlockID	 	id;

This macro extracts the VMBlockHandle from a TransferBlockID .

BlockIDFromFileAndBlock

TransferBlockID BlockIDFromFileAndBlock(file, block);
        VMFileHandle 		file;
        VMBlockHandle 		block;

This macro creates the dword type TransferBlockID from a VMFileHandle and a VMBlockHandle.

bsearch()

extern void *_pascal bsearch(
        const void 		*key,
        const void 		*array,
        word 		count,
        word 		elementSize,
        PCB(int, compare, (const void *, const void *)));

This is a standard binary search routine. The callback routine must be declared _pascal.

calloc()

void *	calloc(
        word		n,			/* number of structures to allocate */
        size_t		size);			/* size of each structure in bytes */

The malloc() family of routines is provided for Standard C compatibility. If a geode needs a small amount of fixed memory, it can call one of the routines. The kernel will allocate a fixed block to satisfy the geode's malloc() requests; it will allocate memory from this block. When the block is filled, it will allocate another fixed malloc-block. When all the memory in the block is freed, the memory manager will automatically free the block.

When a geode calls calloc() , it will be allocated a contiguous section of memory large enough for the specified number of structures of the specified size. The memory will be allocated out of its malloc-block, and the address of the start of the memory will be returned. The memory will be zero-initialized. If the request cannot be satisfied, calloc() will return a null pointer. The memory is guaranteed not to be moved until it is freed (with free() ) or resized (with realloc() ). When GEOS shuts down, all fixed blocks are freed, and any memory allocated with calloc() is lost.

Tips and Tricks: You can allocate memory in another geode's malloc-block by calling GeoMalloc() . However, that block will be freed when the other geode exits.

Be Sure To: Request a size small enough to fit in a malloc-block; that is, the size of the structure times the number of structures requested must be somewhat smaller than 64K.

Warnings: All memory allocated with calloc() is freed when GEOS shuts down.

See Also: malloc(),
free(),
GeoMalloc(),
realloc().

CCB()

#define CCB(return_type, pointer_name, args) \
        return_type _cdecl (*pointer_name) args

This macro is useful for declaring pointers to functions that use the C calling conventions. For example, to declare a pointer to a function which is passed two strings and returns an integer, one could write

CCB(int, func_ptr, (const char *, const char *));

which would be expanded to

int _cdecl (*func_ptr) (const char *, const char *);

Different compilers have different syntax for the _cdecl keyword. Using this macro makes your callback compiler-independent.

See Also: PCB().

CellDeref()

void *	CellDeref(
        optr		CellRef);

This routine translates an optr to a cell into the cell's address. The routine is simply a synonym for LMemDeref() .

Include: cell.h

CellDirty()

void	CellDirty(
        void *		ptr);		/* pointer to anywhere in locked cell */

This routine marks a cell as "dirty"; i.e., the cell will have to be copied from memory back to the disk.

Include: cell.h

Tips and Tricks: All the cells in an item block are marked dirty at once; thus, you can call this routine just once for several cells in the same item block. Only the segment portion of the pointer is significant; thus, you can pass a pointer to anywhere in the cell. This is useful if you have incremented the pointer to the cell.

CellGetDBItem()

DBGroupAndItem 	CellGetDBItem(
        CellFunctionParameters *			cfp,
        word		row,			/* Get handles of cell in this row */
        byte		column);			/*...and this column */

All cells are stored as ungrouped DB items. If you wish to manipulate the cells with standard DB routines, you will need to know their handles. The routine is passed the address of the CellFunctionParameters and the row and column indices of the desired cell. It returns the DBGroupAndItem value for the specified cell. If there is no cell at the specified coordinates, it returns a null DBGroupAndItem . The routine does not lock the cell or change it in any way.

Include: cell.h

See Also: DBGroupAndItem.

CellGetExtent()

void	CellGetExtent(
        CellFunctionParameters *cfp,
        RangeEnumParams 		*rep); /* write boundaries in REP_bounds field */

This routine returns the boundaries of the utilized portion of the cell file. The routine is passed the address of the cell file's CellFunctionParameters structure.) It writes the results into the REP_bounds field of the passed RangeEnumParams structure. The index of the first row to contain cells is written into REP_bounds.R_top ; the index of the last occupied row is written to REP_bounds.R_bottom ; the index of the first occupied column is written to REP_bounds.R_left ; and the index of the last occupied row is written to REP_bounds.R_right . If the cell file contains no cells, all four fields will be set to -1..

Include: cell.h

CellLock()

void *	CellLock(
        CellFunctionParameters*			cfp,
        word			row,			/* Lock cell in this row... */
        word			column);			/* ... and this column */

This routine is passed the address of the CellFunctionParameters of a cell file, and the row and column indices of a cell. It locks the cell and returns a pointer to it.

Include: cell.h

See Also: CellLockGetRef().

CellLockGetRef()

void *	CellLockGetRef(
        CellFunctionParameters*			cfp,
        word			row,			/* Lock cell in this row... */
        word			column,			/* ... and this column */
        optr *			ref);			/* Write handles here */

This routine is passed the address of the CellFunctionParameters of a cell file, and the row and column indices of a cell. It locks the cell and returns a pointer to it. It also writes the locked cell's item-block and chunk handles to the optr. If the cell moves (e.g. because another cell is allocated), you can translate the optr structure into a pointer by passing it to CellDeref() .

Include: cell.h

Warnings: The optr becomes invalid when the cell is unlocked.

See Also: CellGetDBItem()., CellLock().

CellReplace()

void	CellReplace{
        CellFunctionParameters *			cfp,
        word		row,		/* Insert/replace cell at this row... */
        word		column,			/* ... and this column */
        const void *		cellData,			/* Copy this data into the new cell */
        word		size);			/* Size of new cell (in bytes) */

This routine is used for creating, deleting, and replacing cells in a cell file. To create or replace a cell, set cellData to point to the data to copy into the new cell, and set size to the length of the cell in bytes, and row and column the cell's coordinates. (As usual, cfp is a pointer to the cell file's CellFunctionParameters structure.) Any pre-existing cell at the specified coordinates will automatically be freed, and a new cell will be created.

To delete a cell, pass a size of zero. If there is a cell at the specified coordinates, it will be freed. (The cellData argument is ignored.)

Include: cell.h

Warnings: If a cell is allocated or replaced, pointers to all ungrouped items (including cells) in that VM file may be invalidated. The CellFunctionParameters structure must not move during the call; for this reason, it may not be in an ungrouped DB item. Never replace or free a locked cell; if you do, the cell's item block will not have its lock count decremented, which will prevent the block from being unlocked.

CellUnlock()

void	CellUnlock(
        void *	ptr); /* pointer to anywhere in locked cell */

This routine unlocks the cell pointed to by ptr . Note that a cell may be locked several times. When all locks on all cells in an item-block have been released, the block can be swapped back to the disk.

Include: cell.h

Tips and Tricks: The DB manager does not keep track of locks on individual items; instead, it keeps a count of the total number of locks on all the items in an item-block. For this reason, only the segment address of the cell is significant; thus, you can pass a pointer to somewhere within (or immediately after) a cell to unlock it. This is useful if you have incremented the pointer to the cell.

Be Sure To: If you change the cell, dirty it (with CellDirty() ) before you unlock it.

CFatalError()

void	CFatalError(
        word	code)

This routine generates a fatal error. It stores an error code passed for use by the debugger.

ChunkArrayAppend()

void *	ChunkArrayAppend(
        optr	array,				/* optr to chunk array */
        word	elementSize)				/* Size of new element (ignored if
                         * elements are uniform-sized) */

This routine adds a new element to the end of a chunk array. It automatically expands the chunk to make room for the element and updates the ChunkArrayHeader . It returns a pointer to the new element.

One of the arguments is the size of the new element. This argument is significant if the array contains variable-sized elements. If the elements are uniform-sized, this argument is ignored. The array is specified with an optr.

Include: chunkarr.h

Be Sure To: Lock the block on the global heap (if it is not fixed).

Warnings: This routine resizes the chunk, which means it can cause heap compaction or resizing. Therefore, all existing pointers to within the LMem heap are invalidated.

See Also: ChunkArrayInsertAt(), ChunkArrayDelete().

ChunkArrayAppendHandles()

void *	ChunkArrayAppendHandles(
        MemHandle		mh,				/* Handle of LMem heap's block */
        ChunkHandle		ch,				/* Handle of chunk array */
        word		size)				/* Size of new element (ignored if
                             * elements are uniform-sized) */

This routine is exactly like ChunkArrayAppend() , except that the chunk array is specified by its global and local handles instead of by an optr.

Include: chunkarr.h

Be Sure To: Lock the block on the global heap (if it is not fixed).

Warnings: This routine resizes the chunk, which means it can cause heap compaction or resizing. Therefore, all existing pointers to within the LMem heap are invalidated.

See Also: ChunkArrayInsertAt(), ChunkArrayDelete().

ChunkArrayCreate()

ChunkHandle	 ChunkArrayCreate(
        MemHandle 	mh,	/* Handle of LMem heap's block */
        word	elementSize,	/* Size of each element (or zero if elements are
             * variable-sized) */
        word	headerSize,	/* Amount of chunk to use for header (or zero for
              * default size) */
        ObjChunkFlags ocf);

This routine sets up a chunk array in the specified LMem heap. The heap must have already been initialized normally. The routine allocates a chunk and sets up a chunk array in it. It returns the chunk's handle. If it cannot create the chunk array, it returns a null handle.

If the chunk array will have uniform-size elements, you must specify the element size when you create the chunk array. You will not be able to change this. If the array will have variable-sized elements, pass an element size of zero.

The chunk array always begins with a ChunkArrayHeader . You can specify the total header size; this is useful if you want to begin the chunk array with a special header containing some extra data. However, the header must be large enough to accommodate a ChunkArrayHeader , which will begin the chunk. If you define a header structure, make sure that its first element is a ChunkArrayHeader . Only the chunk array code should access the actual ChunkArrayHeader . If you pass a headerSize of zero, the default header size will be used (namely, sizeof(ChunkArrayHeader) ). If you pass a non-zero headerSize , any space between the ChunkArrayHeader and the heap will be zero-initialized.

To free a chunk array, call LMemFree() as you would for any chunk.

Include: chunkarr.h

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

Warnings: Results are unpredictable if you pass a non-zero headerSize argument which is smaller than sizeof(ChunkArrayHeader) . Since the routine allocates a chunk, it can cause heap compaction or resizing; all pointers to within the block are invalidated.

ChunkArrayCreateAt()

ChunkHandle 	ChunkArrayCreateAt(
        optr	array,	/* Create chunk array in this chunk */
        word	elementSize,	/* Size of each element (or zero if elements are
             * variable-sized) */
        word	headerSize,	/* Amount of chunk to use for header (or zero for
             * default size) */
        ObjChunkFlags ocf);

This routine is exactly like ChunkArrayCreate() , except that you specify the chunk which will be made into a chunk array. The chunk is specified with an optr. Note that any data already existing in the chunk will be overwritten.

Warnings: The chunk may be resized, which invalidates all pointers to within the LMem heap.

Include: chunkarr.h

ChunkArrayCreateAtHandles()

ChunkHandle 	ChunkArrayCreateAtHandles(
        MemHandle 		mh,
        ChunkHandle 		ch,
        word		elementSize,
        word		headerSize,
        ObjChunkFlags		ocf);

This routine is exactly like ChunkArrayCreate() , except that the chunk is specified with its global and chunk handles instead of with an optr.

Tips and Tricks: If you pass a null chunk handle, a new chunk will be allocated.

Warnings: The chunk may be resized, which would invalidate all pointers to within the LMem heap.

Include: chunkarr.h

ChunkArrayDelete()

void	ChunkArrayDelete(
        optr	array,				/* optr to chunk array */
        void *	element);				/* Address of element to delete */

This routine deletes an element from a chunk array. It is passed the address of that element, as well as the optr of the array.

Since the chunk is being decreased in size, the routine is guaranteed not to cause heap compaction or resizing.

Include: chunkarr.h

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

Tips and Tricks: Only the chunk handle portion of the optr is significant; the memory block is determined from the pointer to the element.

Warnings: The addresses of all elements after the deleted one will change. No other addresses in the block will be affected. If the address passed is not the address of an element in the array, results are undefined.

See Also: ChunkArrayAppend(),
ChunkArrayInsertAt(),
ChunkArrayZero().

ChunkArrayDeleteHandle()

void	ChunkArrayDeleteHandle(
        ChunkHandle		ch,				/* Handle of chunk array */
        void *		el);				/* Address of element to delete */

This routine is exactly like ChunkArrayDelete() , except that the chunk array is specified with its chunk handle instead of with an optr. The global memory handle is not needed, as the memory block is implicit in the pointer to the element.

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

Include: chunkarr.h

ChunkArrayDeleteRange()

void	ChunkArrayDeleteRange(
        optr	array,		/* optr to chunk array */
        word	firstElement,		/* index of first element to delete */
        word	count);		/* # of elements to delete (-1 to delete to
                 * end of array) */

This routine deletes several consecutive elements from a chunk array. The routine is passed the optr of the chunk array, the index of the first element to delete, and the number of elements to delete. The routine is guaranteed not to cause heap compaction or resizing; thus, pointers to other elements in the array will remain valid.

ChunkArrayElementResize()

void	ChunkArrayElementResize(
        optr	array,				/* optr to chunk array */
        word	element,				/* Index of element to resize */
        word	newSize);				/* New size of element, in bytes */

This routine resizes an element in a chunk array. The chunk array must have variable-sized elements. The routine is passed an optr to the chunk array (which must be locked on the global heap), as well as the index of the element to resize and the new size (in bytes). It does not return anything.

If the new size is larger than the old, null bytes will be added to the end of the element. If the new size is smaller than the old, bytes will be removed from the end to truncate the element to the new size.

Warnings: If the element is resized larger, the chunk array may move within the LMem heap, and the heap itself may move on the global heap; thus, all pointers to within the LMem heap will be invalidated.

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

Include: chunkarr.h

ChunkArrayElementResizeHandles()

void	ChunkArrayElementResizeHandles(
        Memhandle		mh,				/* Global handle of LMem heap */
        ChunkHandle		ch,				/* Chunk handle of chunk array */
        word		el,				/* Index of element to resize */
        word		ns);				/* New size of element, in bytes */

This routine is exactly like ChunkArrayElementResize() except that the chunk array is specified with its global and chunk handles, instead of with its optr.

Warnings: If the element is resized to larger than the old, the chunk array may move within the LMem heap, and the heap itself may move on the global heap; thus, all pointers to within the LMem heap will be invalidated.

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

Include: chunkarr.h

ChunkArrayElementToPtr()

void *  ChunkArrayElementToPtr(
        optr           array,                                  /* optr to chunk array */
        word           elementNumber,                                  /* Element to get address of */
        void *         elementSize);                                   /* Write element's size here */

This routine translates the index of an element into the element's address. The routine is passed an optr to the chunk array, the index of the element in question, and a pointer to a word-sized variable. It returns a pointer to the element. If the elements in the array are of variable size, it writes the size of the element to the variable pointed to by the elementSize pointer. If the elements are of uniform size, it does not do this.

If the array index is out of bounds, the routine returns a pointer to the last element in the array. The routine will also do this if you pass the constant CA_LAST_ELEMENT.

Include: chunkarr.h

Tips and Tricks: If you are not interested in the element's size, pass a null pointer as the third argument.

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

Warnings: The error-checking version fatal-errors if passed the index CA_NULL_ELEMENT (i.e. 0xffff, or -1).

ChunkArrayElementToPtrHandles()

void *  ChunkArrayElementToPtrHandles(
        Memhandle              mh,                                     /* Handle of LMem heap's block */
        ChunkHandle            chunk,                                  /* Handle of chunk array */
        word           elementNumber,                                  /* Element to get address of */
        void *         elementSize);                                   /* Write element's size here */

This routine is just like ChunkArrayElementToPtr() , except that the chunk array is specified with its global and chunk handles, instead of with an optr.

Include: chunkarr.h

Tips and Tricks: If you are not interested in the element's size, pass a null pointer as the fourth argument.

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

See Also: ChunkArrayPtrToElement().

Warnings: The error-checking version fatal-errors if passed the index CA_NULL_ELEMENT (i.e. 0xffff, or -1).

ChunkArrayEnum()

Boolean ChunkArrayEnum(
        optr           array,                  /* optr to chunk array */
        void *         enumData,                       /* This is passed to callback routine */
        Boolean _pascal (*callback) (void *element, void *enumData));
        /* callback called for each element; returns TRUE to stop */

This routine lets you apply a procedure to every element in a chunk array. The routine is passed an optr to the chunk array, a pointer (which is passed to the callback routine), and a pointer to a Boolean callback routine. The callback routine, in turn, is called once for each element in the array, and is passed two arguments: a pointer to an element and the pointer which was passed to ChunkArrayEnum() . If the callback routine ever returns true for an element, ChunkArrayEnum() will stop with that element and return true . If it enumerates every element without being aborted, it returns false.

The callback routine can call such routines as ChunkArrayAppend(), ChunkArrayInsertAt(), and ChunkArrayDelete() . ChunkArrayEnum() will see to it that every element is enumerated exactly once. The callback routine can even make a nested call to ChunkArrayEnum() ; the nested call will be completed for every element before the outer call goes to the next element. The callback routine should be declared _pascal.

You can get into trouble if more than one thread of execution is accessing a single ChunkArray and one or both of these threads is using one of the ChunkArrayEnum...() routines. The error-checking version of the kernel detects this situation and generates a CHUNK_ARRAY_ENUM_INSERT_OR_DELETE_RUN_BY_MULTIPLE_THREADS warning if it detects such. If your code is getting this warning, you should add some synchronization between threads. While one thread is enumerating through chunks in the chunk array, the other should do nothing to enumerate the same array, nor alter the array in any way.

Include: chunkarr.h

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

ChunkArrayEnumHandles()

Boolean ChunkArrayEnumHandles(
        MemHandle              mh,                     /* Handle of LMem heap's block */
        ChunkHandle            ch,                     /* Handle of chunk array */
        void *         enumData,                       /* Buffer used by callback routine */
        Boolean _pascal (*callback) (void *element, void *enumData));
        /* callback called for each element; returns TRUE to stop */

This routine is exactly like ChunkArrayEnum() , except that the chunk array is specified by its global and chunk handles (instead of with an optr).

You can get into trouble if more than one thread of execution is accessing a single ChunkArray and one or both of these threads is using one of the ChunkArrayEnum...() routines. The error-checking version of the kernel detects this situation and generates a CHUNK_ARRAY_ENUM_INSERT_OR_DELETE_RUN_BY_MULTIPLE_THREADS warning if it detects such. If your code is getting this warning, you should add some synchronization between threads. While one thread is enumerating through chunks in the chunk array, the other should do nothing to enumerate the same array, nor alter the array in any way.

Include: chunkarr.h

ChunkArrayEnumRange()

Boolean         ChunkArrayEnumRange(
        optr   array,  /* optr to chunk array */
        word   startElement,   /* Start enumeration with this element */
        word   count,  /* Process this many elements */
        void * enumData,       /* This is passed to the callback routine */
        Boolean _pascal (*callback)            /* Return TRUE to halt enumeration */
               (void *element, void *enumData));

This routine is exactly like ChunkArrayEnum() (described above), except that it acts on a limited portion of the array. It is passed two additional arguments: the index of the starting element, and the number of elements to process. It will begin the enumeration with the element specified (remember, the first element in a chunk array has an index of zero). If the count passed would take the enumeration past the end of the array, ChunkArrayEnumRange() will automatically stop with the last element. You can instruct ChunkArrayEnumRange() to process all elements by passing a count of CA_LAST_ELEMENT.

You can get into trouble if more than one thread of execution is accessing a single ChunkArray and one or both of these threads is using one of the ChunkArrayEnum...() routines. The error-checking version of the kernel detects this situation and generates a CHUNK_ARRAY_ENUM_INSERT_OR_DELETE_RUN_BY_MULTIPLE_THREADS warning if it detects such. If your code is getting this warning, you should add some synchronization between threads. While one thread is enumerating through chunks in the chunk array, the other should do nothing to enumerate the same array, nor alter the array in any way.

Include: chunkarr.h

Warnings: The start element must be within the bounds of the array.

See Also: ChunkArrayEnum().

ChunkArrayEnumRangeHandles()

Boolean         ChunkArrayEnumRangeHandles(
        MemHandle      mh,     /* Handle of LMem heap's block */
        ChunkHandle    ch,     /* Handle of chunk array */
        word   startElement,   /* Start enumeration with this element */
        word   count,  /* Process this many elements */
        void * enumData,       /* This is passed to the callback routine */
        Boolean _pascal (*callback)            /* Return TRUE to halt enumeration */
               (void *element, void *enumData));

This routine is exactly like ChunkArrayEnumRange() , except that the chunk array is specified by its global and chunk handles (instead of with an optr).

You can get into trouble if more than one thread of execution is accessing a single ChunkArray and one or both of these threads is using one of the ChunkArrayEnum...() routines. The error-checking version of the kernel detects this situation and generates a CHUNK_ARRAY_ENUM_INSERT_OR_DELETE_RUN_BY_MULTIPLE_THREADS warning if it detects such. If your code is getting this warning, you should add some synchronization between threads. While one thread is enumerating through chunks in the chunk array, the other should do nothing to enumerate the same array, nor alter the array in any way.

ChunkArrayGetCount()

word    ChunkArrayGetCount(
        optr   array);                         /* optr of chunk array */

This routine returns the number of elements in the specified chunk array.

Include: chunkarr.h

Tips and Tricks: It is usually faster to examine the CAH _count field of the ChunkArrayHeader . This field is the first word of the ChunkArrayHeader (and therefore of the chunk). It contains the number of elements in the chunk array.

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed).

See Also: ChunkArrayHeader.

ChunkArrayGetCountHandles()

word	ChunkArrayGetCountHandles(
        MemHandle		mh,				/* Handle of LMem heap's block */
        ChunkHandle		ch);				/* Handle of chunk array */

This routine is just like ChunkArrayGetCount() , except that the chunk array is specified by its global and local handles (instead of with an optr).

Include: chunkarr.h

ChunkArrayGetElement()

void	ChunkArrayGetElement(
        optr	array,					/* optr to chunk array */
        word	elementNumber,					/* Index of element to copy */
        void *	buffer);					/* Address to copy element to */

This routine copies an element in a chunk array into the passed buffer. It is your responsibility to make sure the buffer is large enough to hold the element.

Include: chunkarr.h

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed). Make sure the buffer is large enough to hold the element.

See Also: ChunkArrayPtrToElement(), ChunkArrayElementToPtr().

ChunkArrayGetElementHandles()

void	ChunkArrayGetElementHandles(
        Memhandle		mh,					/* Handle of LMem heap's block */
        ChunkHandle		array,					/* Handle of chunk array */
        word		elementNumber,					/* Index of element to copy */
        void *		buffer);					/* Address to copy element to */

This routine is just like ChunkArrayGetElement() , except that the chunk array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

Be Sure To: Lock the LMem heap's block on the global heap (unless it is fixed). Make sure the buffer is large enough to hold the element.

See Also: ChunkArrayPtrToElement(), ChunkArrayElementToPtr().

ChunkArrayInsertAt()

void *	ChunkArrayInsertAt(
        optr		array,				/* Handle of chunk array */
        void *		insertPointer,				/* Address at which to insert
                             * element */
        word		elementSize);				/* Size of new element (ignored
                             * if elements are uniform-sized) */

This routine inserts a new element in a chunk array. You specify the location by passing a pointer to an element. A new element will be allocated at that location; thus, the element which was pointed to will be shifted, so it ends up immediately after the new element. The new element will be zero-initialized.

The routine is passed three arguments: the optr of the array, the address where the new element should be inserted, and the size of the new element. (If the array is of uniform-size elements, the size argument will be ignored.)

Include: chunkarr.h

Tips and Tricks: Only the chunk-handle portion of the optr is significant; the memory block is implicit in the pointer to the element.

Be Sure To: Lock the block on the global heap (if it is not fixed).

Warnings: If the address passed is not the address of an element already in the chunk array, results are undefined. The routine may cause heap compaction or resizing; all pointers within the block are invalidated.

See Also: ChunkArrayAppend(), ChunkArrayDelete().

ChunkArrayInsertAtHandle()

void *	ChunkArrayInsertAtHandle(
        ChunkHandle		chunk,				/* Handle of chunk array */
        void *		insertPointer,				/* Address at which to insert
                             * element */
        word		elementSize);				/* Size of new element (ignored
                             * if elements are uniform-sized) */

This routine is just like ChunkArrayInsertAt() , except that the chunk array is specified by its chunk handle. (The global block is implicit in the pointer passed.)

Include: chunkarr.h

ChunkArrayPtrToElement()

word	ChunkArrayPtrToElement(
        optr	array,						/* Handle of chunk array */
        void *	element);						/* Address of element */

This routine takes the address of an element in a chunk array, as well as an optr to the array. It returns the element's zero-based index.

Include: chunkarr.h

Tips and Tricks: Only the chunk-handle portion of the optr is significant; the memory block is implicit in the pointer to the element.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Warnings: If the address passed is not the address of the beginning of an element, results are unpredictable.

See Also: ChunkArrayElementToPtr().

ChunkArrayPtrToElementHandle()

word 	ChunkArrayPtrToElementHandle(
        ChunkHandle		array,			/* chunk handle of chunk array */
        void *		element);			/* Pointer to element to delete */

This routine is exactly like ChunkArrayPtrToElement() , except that the chunk array is indicated by its chunk handle. (The global block is implicit in the pointer passed.)

ChunkArraySort()

void	ChunkArraySort(
        optr		array,					/* optr to chunk array */
        word		valueForCallback,					/* Passed to callback routine */
        sword _pascal (*callback)				(void *el1,
                 void * el2,
                 word valueForCallback))
    /* Sign of return value decides order of elements */

This is a general-purpose sort routine for chunk arrays. It does a modified Quicksort on the array, using an insertion sort for subarrays below a certain size.

The sort routine does not compare elements. Rather, it calls a comparison callback routine passed in the callback parameter. Whenever it needs to compare two elements, it calls the callback routine, passing the addresses of the elements and the valueForCallback word which was passed to ChunkArraySort() . The callback routine should be declared _pascal. The callback routine's return value determines which element will come first in the sorted array:

  • If element el1 ought to come before el2 in the sorted array, the callback routine should return a negative integer.
  • If element el1 ought to come after el2 in the sorted array, the callback routine should return a positive integer.
  • If it doesn't matter whether el1 comes before or after el2 in the sorted array, the callback routine should return zero.

Include: chunkarr.h

Tips and Tricks: You may need to sort an array based on different criteria at different times. The simplest way to do this is to write one general-purpose callback routine and have the valueForCallback word determine how the sort is done. For example, the same callback routine could sort the array in ascending or descending order, depending on the valueForCallback .

Be Sure To: Lock the block on the global heap (unless it is fixed).

Warnings: Do not have the callback routine do anything which might invalidate pointers to the array (such as allocate a new chunk or element).

See Also: ArrayQuickSort().

ChunkArraySortHandles()

void	ChunkArraySortHandles(
        MemHandle		memHandle,					/* Handle of LMem heap's block */
        ChunkHandle		chunkHandle,					/* Handle chunk array */
        word		valueForCallback,					/* Passed to callback routine */
        sword _pascal(*callback)(void *el1, void * el2, word valueForCallback)
    /* Sign of return value decides order of elements */

This routine is exactly like ChunkArraySort() above, except that the chunk array is specified by its global and chunk handles (instead of by an optr).

Include: chunkarr.h

ChunkArrayZero()

void	ChunkArrayZero(
        optr	array);		/* optr to chunk array */

This routine destroys all the elements in an array. It does not affect the extra-space area between the ChunkArrayHeader and the elements. It is guaranteed not to cause heap compaction or resizing; thus, pointers to other chunks remain valid.

Include: chunkarr.h

Be Sure To: Lock the block on the global heap (unless it is fixed).

See Also: ChunkArrayDelete().

ChunkArrayZeroHandles()

void	ChunkArrayZeroHandles(
        MemHandle		mh		/* Global handle of LMem heap */
        ChunkHandle		ch);		/* Chunk handle of chunk array */

This routine is exactly like ChunkArrayZero() above, except that the chunk array is specified by its global and chunk handles (instead of by an optr).

Include: chunkarr.h

ClipboardAbortQuickTransfer()

void	ClipboardAbortQuickTransfer(void);

This routine cancels a quick-transfer operation in progress. It is typically used when an object involved in a quick-transfer is shutting down or when an error occurs in a quick-transfer. This routine is usually used only by the object or Process which initiated the quick-transfer.

Include: clipbrd.goh

ClipboardAddToNotificationList()

void	ClipboardAddToNotificationList(
        optr	notificationOD);

This routine registers the passed object or process for quick-transfer notification. This routine is typically called from within an object's MSG_META_INITIALIZE handler or within a Process object's MSG_GEN_PROCESS_OPEN_APPLICATION handler. Pass the optr of the object or the geode handle if the Process object should be registered.

Include: clipbrd.goh

See Also: ClipboardRemoveFromNotificationList().

ClipboardClearQuickTransferNotification()

void	ClipboardClearQuickTransferNotification(
        optr	notificationOD);

This routine removes an object or process from quick-transfer notification. It is typically used in the object's MSG_META_DETACH handler or in the Process object's MSG_GEN_PROCESS_CLOSE_APPLICATION to ensure that it is not notified after it has already detached.

Pass the optr of the object specified to receive notification in ClipboardStartQuickTransfer() (or the geode handle if a process).

Note that an object may also want to check if a quick-transfer is in progress when detaching and possibly abort it if there is one.

Include: clipbrd.goh

ClipboardDoneWithItem()

void	ClipboardDoneWithItem(
        TransferBlockID header);

This routine is called when an object or Process is done using a transfer item. It relinquishes exclusive access to the item's transfer VM file after the caller had previously called ClipboardQueryItem() .

Include: clipbrd.goh

ClipboardEndQuickTransfer()

void	ClipboardEndQuickTransfer(
        ClipboardQuickNotifyFlags 				flags);

This routine ends a quick-transfer operation by resetting the pointer image, clearing any quick-transfer region, clearing the quick-transfer item, and sending out any needed notification of the completed transfer.

Pass this routine a record of ClipboardQuickNotifyFlags . Pass the value CQNF_MOVE if the operation was completed and was a move; pass CQNF_COPY if the operation was completed and was a copy. If the operation could not be completed (e.g. incompatible data types), pass CQNF_NO_OPERATION or CQNF_ERROR.

The notification sent out by the UI will be in the form of the message MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED . This message notifies the originator of the transfer item of the type of operation; the originator can then respond if necessary.

Include: clipbrd.goh

ClipboardEnumItemFormats()

word	ClipboardEnumItemFormats(
        TransferBlockID 		header,
        word 		maxNumFormats,
        ClipboardFormatID *		buffer);

This routine returns a list of all the formats supported by the current transfer item. To see whether a particular format is supported, you can use ClipboardTestItemFormat() instead.

Pass this routine the following:

header
The transfer item header as returned by ClipboardQueryItem() .
maxNumFormats
The maximum number of formats that should be returned. You should set your return buffer (see below) large enough to support this size.
buffer
A pointer to a locked or fixed buffer into which the formats will be copied. Upon return, the buffer will contain the proper number of ClipboardFormatID structures, one for each format available. This buffer should be at least large enough to support the number of formats requested in maxNumFormats .

The word return value is the total number of formats returned. This number will be equal to or less than the number passed in maxNumFormats . The routine will also return the passed buffer filled with that number of ClipboardFormatID structures.

Include: clipbrd.goh

See Also: ClipboardTestItemFormat().

ClipboardGetClipboardFile()

VMFileHandle ClipboardGetClipboardFile(void);

This routine returns the VM file handle of the current default transfer VM file.

Include: clipbrd.goh

ClipboardGetItemInfo()

optr	ClipboardGetItemInfo(
        TransferBlockID header);

This routine returns the source identifier (CIH _sourceID ) of the current transfer item. Pass the transfer item's header returned by ClipboardQueryItem() .

Include: clipbrd.goh

ClipboardGetNormalItemInfo()

TransferBlockID ClipboardGetNormalItemInfo(void);

This routine returns information about the normal transfer item. It returns a TransferBlockID dword which contains the VM file handle of the transfer file and the VM block handle of the transfer item's header block.

To extract the file handle from the return value, use the macro FileFromTransferBlockID() . To extract the block handle, use the macro BlockFromTransferBlockID() .

Include: clipbrd.goh

ClipboardGetQuickItemInfo()

TransferBlockID ClipboardGetQuickItemInfo(void);

This routine returns information about the quick-transfer transfer item. It returns a TransferBlockID dword which contains the VM file handle of the transfer file and the VM block handle of the transfer item's header block.

To extract the file handle from the return value, use the macro FileFromTransferBlockID() . To extract the block handle, use the macro BlockFromTransferBlockID() .

Include: clipbrd.goh

ClipboardGetQuickTransferStatus()

Boolean	ClipboardGetQuickTransferStatus(void);

This routine returns true if a quick-transfer operation is in progress, false otherwise. It is often called when objects or Processes are shutting down in order to abort any quick-transfers originated by the caller.

Include: clipbrd.goh

ClipboardGetUndoItemInfo()

TransferBlockID ClipboardGetUndoItemInfo(void);

This routine returns information about the undo transfer item. It returns a TransferBlockID dword which contains the VM file handle of the transfer file and the VM block handle of the transfer item's header block.

To extract the file handle from the return value, use the macro FileFromTransferBlockID() . To extract the block handle, use the macro BlockFromTransferBlockID() .

Include: clipbrd.goh

ClipboardQueryItem()

void	ClipboardQueryItem(
        ClipboardItemFlags 			flags,
        ClipboardQueryArgs *			retValues);

This routine locks the transfer item for the caller's exclusive access and returns information about the current transfer item. You should call this routine when beginning any paste or clipboard query operation. For operations in which you will change the clipboard's contents, you should instead use the routine ClipboardRegisterItem() .

Pass the following values:

flags
A record of ClipboardItemFlags indicating the transfer item you want to query. Use CIF_QUICK to get information on the quick transfer item, and pass zero (or TIF_NORMAL) to get information on the normal transfer item.
retValues A pointer to an empty ClipboardQueryArgs structure into which return information about the transfer item will be passed. This structure is defined as follows:
typedef struct {
    word			CQA_numFormats;
    optr			CQA_owner;
    TransferBlockID			CQA_header;
} ClipboardQueryArgs;

The CQA_header field of ClipboardQueryArgs is used as a pass value to several other clipboard routines. It contains the VM file handle of the transfer VM file and the VM block handle of the transfer item's header block. The CQA_owner field is the optr of the object that originated the transfer item. The CQA_numFormats field specifies the total number of formats available for this transfer item. To see if a particular format is supported by the transfer item, call the routine ClipboardTestItemFormat() .

Be Sure To: You must call ClipboardDoneWithItem() when you are done accessing the transfer item. This routine relinquishes your exclusive access to the transfer VM file.

Include: clipbrd.goh

See Also: ClipboardRequestItemFormat(), ClipboardDoneWithItem().

ClipboardRegisterItem()

Boolean	ClipboardRegisterItem(
        TransferBlockID header,
        ClipboardItemFlags flags);

This routine completes a change to the transfer item. You should use this routine whenever copying or cutting something into the clipboard or whenever attaching something as the quick-transfer item.

This routine puts the item specified by header into the transfer VM file. It frees any transfer item that may already be in the file. Pass this routine the following:

header
Header information for the item, consisting of the transfer VM file handle and the VM block handle of the block containing the new transfer item. Create the TransferBlockID structure using the macro BlockIDFromFileAndBlock() .
flags
A record of ClipboardItemFlags indicating whether you're registering a clipboard item or a quick-transfer item. The flag CIF_QUICK indicates the item is a quick-transfer item; zero (or TIF_NORMAL) indicates the item is a normal clipboard item.

Include: clipbrd.goh

See Also: ClipboardRequestItemFormat().

ClipboardRemoveFromNotificationList()

Boolean	ClipboardRemoveFromNotificationList(
        optr	notificationOD);

This routine removes an object or Process from the clipboard's change notification list. It is typically called when the object or Process is being detached or destroyed. Pass it the same optr that was added to the notification list with ClipboardAddToNotificationList ().

This routine returns an error flag: The flag will be true if the object could not be found in the notification list, false if the object was successfully removed from the list.

Include: clipbrd.goh

See Also: ClipboardAddToNotificationList().

ClipboardRequestItemFormat()

void	ClipboardRequestItemFormat(
        ClipboardItemFormatID 			format,
        TransferBlockID 			header,
        ClipboardRequestArgs *			retValue);

This routine returns specific information about a particular transfer item. Because some of the passed information must be retrieved with ClipboardQueryItem() , you must call ClipboardQueryItem() before calling this routine.

Pass this routine the following:

format
The manufacturer ID and format type of the new transfer item being put into the transfer VM file. Create the ClipboardItemFormatID value with the macro FormatIDFromManufacturerAndType() .
header
Header information for the item, consisting of the transfer VM file handle and the VM block handle of the block containing the new transfer item. Create the TransferBlockID structure using the macro BlockIDFromFileAndBlock() using returned information from ClipboardQueryItem() .
retValue A pointer to an empty ClipboardRequestArgs structure that will be filled by the routine. This structure is defined as follows:
typedef struct {
    VMFileHandle		CRA_file;
    VMChain		CRA_data;
    word		CRA_extra1;
    word		CRA_extra2;
} ClipboardRequestArgs;

Upon return, the CRA_file field will contain the transfer VM file's VM file handle and the CRA_data field will contain the VM block handle of the transfer item's header block. If there is no transfer item, CRA_data will be zero.

Include: clipbrd.goh

See Also: ClipboardRegisterItem(), ClipboardQueryItem().

ClipboardSetQuickTransferFeedback()

void	ClipboardSetQuickTransferFeedback(
        ClipboardQuickTransferFeedback 						cursor,
        UIFunctionsActive 						buttonFlags);

This routine sets the image of the mouse pointer during a quick-transfer operation. Use this routine to provide visual feedback to the user during the quick-transfer. For example, an object that could not accept the quick-transfer item would set the "no operation" cursor while the mouse pointer was over its bounds.

Pass the two following values:

cursor
A value of ClipboardQuickTransferFeedback type indicating the type of cursor to set. The possible values are listed below.
buttonFlags
A record of UIFunctionsActive flags. These flags are defined in the Input Manager section and deal with user override of the move/copy behavior.

The cursor parameter contains a value of ClipboardQuickTransferFeedback . This is an enumerated type that defines the cursor to be set, and it has the following values:

CQTF_MOVE
This sets the cursor to the specific UI's move cursor.
CQTF_COPY
This sets the cursor to the specific UI's copy cursor.
CQTF_CLEAR
This clears the cursor and sets it to the specific UI's modal "no operation" cursor.

Include: clipbrd.goh

ClipboardStartQuickTransfer()

Boolean	ClipboardStartQuickTransfer(
        ClipboardQuickTransferFlags 							flags,
        ClipboardQuickTransferFeedback							initialCursor,
        word							mouseXPos,
        word							mouseYPos,
        ClipboardQuickTransferRegionInfo *							regionParams,
        optr							notificationOD);

This routine signals the beginning of a quick-transfer operation. Typically, an object or process will call this routine in its MSG_META_START_MOVE_COPY handler.

Pass it the following parameters:

flags
A record of ClipboardQuickTransferFlags indicating whether an addition graphic region will be attached to the cursor and whether the caller wants notification of transfer completion. The flags allowed are listed below, after the parameter list.
initialCursor
The initial cursor to use for visual feedback to the user. It is a value of ClipboardQuickTransferFeedback , either CQTF_MOVE or CQTF_COPY. If -1 is passed in this parameter, the initial cursor will be the default no-operation cursor (i.e. the transfer source may not also act as the transfer destination).
mouseXPos
This field is used only if CQTF_USE_REGION is passed in flags . It is the horizontal position of the mouse in screen coordinates.
mouseYPos
This field is used only if CQTF_USE_REGION is passed in flags . It is the vertical position of the mouse in screen coordinates.
regionParams
A pointer to a ClipboardQuickTransferRegionInfo structure defining the graphical region to be attached to the cursor during the transfer operation. This structure is only required if CQTF_USE_REGION is passed in flags . It is defined below.
notificationOD
The optr of the object to be notified upon transfer completion. The object specified will receive the notification messages MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED and MSG_..._FEEDBACK .

The allowed ClipboardQuickTransferFlags are listed below:

CQTF_COPY_ONLY
Source supports copying only (not cutting).
CQTF_USE_REGION
Source has passed the definition of a graphical region which will be attached to the tail of the quick-transfer cursor.
CQTF_NOTIFICATION
Source requires notification of completion of the transfer in order to cut original data or provide other feedback.

If a graphical region is to be attached to the quick-transfer cursor, you must pass a pointer to a ClipboardQuickTransferRegionInfo in the regionParams parameter. This structure is defined below.

typedef struct {
    word	CQTRI_paramAX;
    word	CQTRI_paramBX;
    word	CQTRI_paramCX;
    word	CQTRI_paramDX;
    Point	CQTRI_regionPos;
    dword	CQTRI_strategy;
    dword	CQTRI_region;
} ClipboardQuickTransferRegionInfo;

This structure is passed on the stack to the routine. The first four fields represent the region's definition parameters. CQTRI_regionPos is a Point structure indicating where (in screen coordinates) the region is to be located. CQTRI_strategy is a pointer to the region strategy routine. CQTRI_strategy should be a video driver strategy. To find out the strategy of the video driver associated with your window, send your object a MSG_VIS_VUP_QUERY with VUQ_VIDEO_DRIVER. Pass the handle thus gained to GeodeInfoDriver() , which will return the strategy.

This routine returns an error flag: If a quick-transfer is already in progress, the return will be true . If the quick-transfer is successfully begun, the error flag will be false .

Include: clipbrd.goh

ClipboardTestItemFormat()

Boolean	ClipboardTestItemFormat(
        TransferBlockID		header,
        ClipboardFormatID 		format);

This routine tests whether the given format is supported by the specified transfer item. It returns true if the format is supported, false if the format is not supported. Pass the following values:

header
A TransferBlockID specifying the VM file handle and VM block handle of the transfer item to be checked. This is returned by the routines ClipboardGetNormalItemInfo() , ClipboardGetQuickItemInfo() , ClipboardGetUndoItemInfo() , and ClipboardQueryItem() . Most often the proper routine to use is ClipboardQueryItem() .
format
A ClipboardFormatID specifying the type and manufacturer ID of the format to be checked. It is most appropriate to create this parameter from its individual parts using the macro FormatIDFromManufacturerAndType() .

Include: clipbrd.goh

ClipboardUnregisterItem()

void	ClipboardUnregisterItem(
        optr	owner);

This routine restores the transfer item to what it was before the last ClipboardRegisterItem() . Pass it the optr of the caller.

Only the object that last registered a transfer item is allowed to unregister it. If the transfer item is owned by a different object, or if there is no transfer item, nothing will be done. If the transfer item is owned by the caller, the transfer item will be unregistered and the clipboard will be restored to its previous state.

Include: clipbrd.goh

ConstructOptr()

optr	ConstructOptr(
        Handle 		han,
        ChunkHandle 		ch);

This macro constructs an optr type from the given handle (typically a MemHandle) and chunk handle.

See Also: HandleToOptr(),
OptrToHandle(),
OptrToChunk().

ContactCompareNumbers()

Boolean 	ContactCompareNumbers(
        TCHAR *number1,
        TCHAR *number2);

This function compares two phone number strings for equivalence. It ignores extraneous characters such as spaces. It returns zero if they are the same number, otherwise it returns a non-zero value.

Include: contdb.goh

ContactCreateRecordFromTemplate()

MemHandle	 ContactCreateRecordFromTemplate( void );

This function creates a new Contact record in the Contacts database. It returns the handle of the new record. Make any desired changes to the record and then call ContactSaveRecord() to save the record or FoamDBDiscardRecord() to discard changes.

Include: contdb.goh

See Also: ContactSaveRecord(), ContactSetTemplateRecord().

ContactEnsureEmptyField()

FieldID	ContactEnsureEmptyField(
        MemHandle 		record,
        optr 		fieldName,
        ContdbFieldType		fieldType);

This function creates a field in a record, and clears the dat from the existing field of that name if one already existed. This function takes the following arguemnts:

record
The handle of the record, as returned by FoamDBGetRecordFromID() or ContactCreateRecordFromTemplate() .
fieldName
optr of a chunk containing the null-terminated name string, or NullOptr to use the default name for the passed ContdbFieldType .
fieldType
The type of field to find. One of CFT_NAME, CFT_COMPANY CFT_TITLE, CFT_ADDRESS, CFT_PHONE, CFT_FAX, CFT_EMAIL, CFT_DTMF, CFT_NOTES.

Include: contdb.goh

See Also: ContactEnsureField(), FoamDBSetFieldData().

ContactEnsureField()

FieldID	ContactEnsureField(
        MemHandle 		record,
        optr 		fieldName,
        ContdbFieldType		fieldType);

This functionreturns the FieldID of a field within a record, or creates the field if it doesn't find it. This function takes the following arguemnts:

record
The handle of the record, as returned by FoamDBGetRecordFromID() or ContactCreateRecordFromTemplate() .
fieldName
optr of a chunk containing the null-terminated name string, or NullOptr to use the default name for the passed ContdbFieldType .
fieldType
The type of field to find. One of CFT_NAME, CFT_COMPANY CFT_TITLE, CFT_ADDRESS, CFT_PHONE, CFT_FAX, CFT_EMAIL, CFT_DTMF, CFT_NOTES.

Include: contdb.goh

See Also: ContactEnsureEmptyField(), FoamDBSetFieldData().

ContactExportDBToTextFile()

ContactExportStatus 	ContactExportDBToTextFile(
        hptr 	fileExport,
        dword 	startElement,
        dword 	numOfRecords);

This function exports the Contact database's data as an ASCII file. On success, the function returns CES_NO_ERROR, otherwise it returns CES_FILE_ERROR. This function takes the following arguments:

fileExport
The handle of the opened file to hold the exported text.
startElement
The RecordID of the first record to export.
numOfRecords
This argument determines the range of records to export. All records with RecordID at least startElement but less than startElement+numOfRecords will be exported.

To export all the records, pass a startElement of zero and a numOfRecords of CONTACT_EXPORT_ALL_RECORDS.

Include: contdb.goh

See Also: ContactExportDBToTextFile().

ContactGetDBHandle()

VMFileHandle 	ContactGetDBHandle( void );

This function returns the handle of the Contact database, the database that contains all of the "address book" data used by the contdb library; it also lets the system know that you're using the database. The database handle is necessary for various operations with the library. When you're done with the database, remember to call ContactReleaseDBHandle() .

Include: contdb.goh

See Also: ContactGetDBHandleNoBlock(), ContactReleaseDBHandle().

ContactGetDBHandleNoBlock()

VMFileHandle 	ContactGetDBHandleNoBlock( void );

This function behaves in the same manner as ContactGetDBHandle() , except that if it cannot get the handle, it returns zero instead of waiting until the handle is available.When you're done with the handle, remember to call ContactReleaseDBHandle() .

Include: contdb.goh

See Also: ContactGetDBHandle().

ContactGetName()

Boolean	ContactGetName(
        MemHandle 		record,
        TCHAR 		*dest);

This function gets the name associated with the record, filling in the dest buffer. If there was no name in the record, this function returns non-zero; otherwise, it returns zero.

Include: contdb.goh

See Also: ContactGetDBHandle().

ContactGetTruncatedName()

Boolean	ContactGetTrunactedName(
        MemHandle 		record,
        TCHAR 		*dest,
        word		destSize);				/* Size of dest buffer */

This function gets the name associated with the record, filling in the dest buffer. It will truncate the name, as necessary to make it fit inside the buffer. Remember to leave space for the null terminator when determining how big a buffer you'll need. If there was no name in the record, this function returns non-zero; otherwise, it returns zero.

Include: contdb.goh

See Also: ContactGetDBHandle().

ContactGetUserDataID()

RecordID	ContactGetUserDataID( void );

This functionreturns the RecordID of the special Contacts database record containing the user's own contact data.

Include: contdb.goh

See Also: FoamDBGetRecordFromID().

ContactImportDBFromTextFile()

void 	ContactImportDBFromTextFile(
        hptr 		fileImport);					/* file's handle */

This function imports contact database information from an ASCII text file. This file's format is described below. The function will compare imported records to those already in the Contacts database. Any identical records will be merged.

A short sample file is shown below.

[RECORD]
[FIELD]
Name
[FIELD-NAME]
[FIELD-DATA]
Hudson W.
[FIELD-END]
[FIELD]
Company
[FIELD-NAME]
[FIELD-DATA]
Space Marines
[FIELD-END]
[FIELD]
Title
[FIELD-NAME]
[FIELD-DATA]
Private
[FIELD-END]
[RECORD-END]
[RECORD]
[FIELD]
Name
[FIELD-NAME]
[FIELD-DATA]
Adams, John Quincy
[FIELD-END]
[FIELD]
Tel
[FIELD-NAME]
[FIELD-DATA]
1 617 438 1776
[FIELD-END]
[FIELD]
[FIELD-NAME]
Company
[FIELD-DATA]
[FIELD-END]
USA Government
[FIELD]
Notes
[FIELD-NAME]
Note(Birthday)
[FIELD-DATA]
July 11, 1767
Gift ideas: flowers
[FIELD-END]
[RECORD-END]

The ASCII file follows these rules:

  • The page is a DOS text file, using the "Latin 1" code page (number 819).
  • Each line of text may ends with a newline; the newline may be preceded by an optional carriage return.
  • The data corresponding to each record is delimited by a line reading [RECORD] at the start and another line reading [RECORD-END] at the end. Any text that appears outside of these delimiters is lost.
  • Each record consists of one or more fields. Each field is delimited by a line reading [FIELD] and a line reading [FIELD-END].
  • For each field, the first line after the [FIELD] line will consist of one of the following strings, specifying the field's type:
    Name, Company, Title, Address, Tel, Fax, Mail, DTMF, Note.
  • There may be a line of text between the [FIELD-NAME] and [FIELD-DATA] lines. If there is no such line of text, then the field's name is the default name for its data type. If there is such a line of text, then you may form the field name by placing the text of this line between parentheses and preceding it with the default name for the field. In the "Birthday" example, above, the full field name would be "Note(Birthday)".
    The exception to this rule is "GSM/PCN" in phone number fields. If the line of text is "GSM/PCN", treat it as if it were "GSM". The full field name will probably be "Tel(GSM)" or "Fax(GSM)".
  • The line(s) of text between the [FIELD-DATA] line and the [FIELD-END] line contain the field's data, if any. If there are no lines of text, that means that the field contains no data. There may be more than one line of text.

Include: contdb.goh

See Also: ContactExportDBToTextFile().

ContactMatchName()

RecordID	ContactMatchName(
        TCHAR		name,
        ContdbFieldType		type,
        Boolean		stopEarly,
        FieldID		*field,
        word		*numMatches );

This function takes aname, searches all fields of the specified type in all records of the Contact database, returning the RecordID and FieldID of the matching field, if any; if there was no match, it returns -1.

The comparison of names does not consider case.

This function takes the following arguments:

name
String containing the name to match.
type
The type of phone number to match; one of CFT_NAME, CFT_COMPANY CFT_TITLE, CFT_ADDRESS, CFT_PHONE, CFT_FAX, CFT_EMAIL, CFT_DTMF, CFT_NOTES.
stopEarly
Set this flag true to stop searching after finding the first matching name; set it false to find out the number of matching names.
field
Pointer to a FieldID buffer. The function will fill this buffer in with the FieldID of the matching field, if any.
numMatches
Pointer to a word buffer. The function will fill this buffer in with the number of matching records found. This number is only meaningful if the stopEarly flag is false .

Include: contdb.goh

See Also: ContactEnsureEmptyField(), FoamDBSetFieldData().

ContactMatchNumber()

RecordID	ContactMatchNumber(
        TCHAR		number,
        ContdbCallType		type,
        FieldID		*field,
        word		*numMatches );

This function takes a phone number, searches all fields of the specified type in all records of the Contact database, returning the RecordID and FieldID of the matching field, if any; if there was no match, it returns -1.

This function takes the following arguments:

number
String containing the phone number to match.
type
The type of phone number to match; one of CCT_PHONE, CCT_SMS, CCT_FAX, CCT_EMAIL, CCT_GSM_FAX, CCT_ANY.
field
Pointer to a FieldID buffer. The function will fill this buffer in with the FieldID of the matching field, if any.
numMatches
Pointer to a word buffer. The function will fill this buffer in with the number of matching records found.

Include: contdb.goh

See Also: ContactEnsureEmptyField(), FoamDBSetFieldData().

ContactRecreateFieldLabel()

void ContdbRecreateFieldLabel(
        TCHAR 		*subLabel,
        TCHAR 		*destination,
        int 		fieldType);

This utility function takes a field sub-label (e.g., "GSM/PCN") and field type and creates a label from them (e.g. "Tel(GSM)". It will translate standard labels from English to the user's language. (Some field sublabels are standard, such as the "GSM" in "Tel(GSM)" and will be translated; the user's custom sublabels will not be translated.) This function takes the following arguments:

subLabel
The sublabel string.
destination
String buffer to hold the field name.
fieldType
The ContdbFieldType value of the field.

Include: contdb.goh

See Also: ContactImportDBFromTextFile().

ContactReleaseDBHandle()

void	ContactReleaseDBHandle( void );

This function releases the Contact database's handle, signalling that the application is done with the database for now.

Include: contdb.goh

See Also: ContactGetDBHandle(), ContactReleaseDBHandle().

ContactSaveRecord()

dword	ContactSaveRecord(
        MemHandle 		record);

This functionsaves the passed record in the database, inserting it in the proper sort order. It returns the record's RecordID number.

Include: contdb.goh

See Also: FoamDBDiscardRecord(), FoamDBDeleteRecord().

ContactSetTemplateRecord()

void	ContactSetTemplateRecord(
        MemHandle 		record);

This functionmakes the passed record the Contact database's template record. It also frees that contact's handle.

Include: contdb.goh

See Also: ContactCreateRecordFromTemplate().

ContdbTranslateSubFieldLabelToEnglish()

void	ContdbTranslateSubFieldLabelToEnglish(
        TCHAR 		*subLabel,
        TCHAR 		*destination);

This function translates a foreign sub-label to its english equivalent. This function only recognizes certain "standard" sublabels; the user's custom labels will not be translated. This function takes two arguments: a buffer containing the string to translate and a string buffer to hold the translation.

See Also: DBAllocUngrouped().

CreateVisMonikerLine()

optr	CreateVisMonikerLine(
    word			pointSize,
    FontID			typeFace,
    word			numberOfFields,
    VisMonikerColumn *	vmc );

Creates a visMoniker that separates each element into a separate column. This is useful if you've got a GenDynamicList and you want to display columns of data. It uses an array of VisMonikerColumn structs, referred to as fields. All fields will have the same point size, but TextStyle can vary between fields.

You can also have "adaptable columns", which are sequential text columns in the VisMonikerColumn array whose VMC_just fields are set to J_FULL. Rather than the text being padded or clipped to fit within a fixed field width, the text of each column immediately follows the previous column. All of the adaptable columns are left-justified in an overall field width, specified by the VMC_width of the first J_FULL VisMonikerColumn structure (the others are set to 0 width). Adaptable fields are still clipped to fit within the overall field width. A group of adaptable fields ends at the first VisMonikerColumn structure that is not J_FULL or has non-zero width.

Example: Say you want to create a moniker that looks like:

    Field1    SEARCHable text   Field2
    |         |                 |     |
        01234567890123456789012345678901234
Where "SEARCH" is supposed to be underlined and bold. Here is the VisMonikerColumn array you would pass:
elt#  VMC_width  VMC_ptr       VMC_just  VMC_style
--------------------------------------------------
0     10         "Field1"      J_LEFT    0
1     18         "SEARCH"      J_FULL    TS_BOLD | TS_UNDERLINE
2     0          "able text"   J_FULL    0
3     7          "Field2"      J_LEFT    0
elements 1 and 2 are placed together in an overall field width of 18.

Pass:

pointSize
Point size to use when generating the text moniker.
typeFace
Font identifier for the font to use.
numberOfFields
The number of columns in the vmc array.
vmc
An array of VisMonikerColumn structs that contain the column information for building the visMoniker. Each VisMonikerColumn struct defines an element in the line.
typedef struct {
  byte           VMC_width;   /* Width of text in chars, 0 if bitmap */
  const void * 	 VMC_ptr;     /* Pointer to text or bitmap;
                               * if bitmap, points to Bitmap structure */
  Justification  VMC_just;    /* How to justify the column text */
  TextStyle      VMC_style;   /* Column text styles */
  ColumnBorder   VMC_border;  /* Draw vertical border */
} VisMonikerColumn;

Return: Optr to the newly created visMoniker.

Warning: Because the new visMoniker is created in its own new memory block, be sure to free the block when you're done with it.

Include: foam.goh

DataStoreAddField()

DataStoreStructureError	DataStoreAddField(
        word		dsToken,
        FieldDescriptor	*fieldDescPtr,
        FieldID		*fieldIDPtr);

Adds a field to the datastore and writes the new field's FieldID to fieldIDPtr. Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful). Takes the following arguments:

dsToken
Session token to the opened datastore.
fieldDescPtr
Pointer to a FieldDescriptor structure. Set FD _flags to FF_PRIMARY_KEY if the field is part of the key.
        typedef struct {
            FieldData		FD_data;
            TCHAR		*FD_name;
        } FieldDescriptor;
        typedef struct {
            FieldType		FD_type;
            FieldCategory		FD_category;
            FieldFlags		FD_flags;
        } FieldData;
fieldIDPtr
Pointer to the FieldID of the added field.

Include: datastor.h

DataStoreBuildIndex()

DataStoreStructureError	DataStoreBuildIndex(
        word					dsToken,
        MemHandle					*indexBlockHan,
        word					dataSize,
        DataStoreIndexCallbackParams					*paramsPtr,
        sword _pascal (*CallbackRoutine) (
            word	dsToken,
            DataStoreIndexCallbackParams	*paramsPtr));

Builds an index of RecordNum s (low word only) based on either a specified field or a callback function. Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful). Takes the following arguments:

dsToken
Session token to the opened datastore.
indexBlockHan
Pointer to the handle of the index block.
dataSize
Number of bytes of data to write to the index block.
paramsPtr
Pointer to a DataStoreIndexCallbackParams structure. To build an index on a field, specify the field in DSICP _indexField . Set DSICP _sortOrder to SO_ASCENDING or SO_DESCENDING. If you specify a callback, the caller will pass DSICP _cbData , DSICP _rec1 and DSICP _rec2 to the callback.
        typedef struct {
            FieldID		DSICP_indexField;
            SortOrder		DSICP_sortOrder;
            void		*DSICP_cbData;
            RecordHeader		*DSICP_rec1;
            RecordHeader		*DSICP_rec1;
        } DataStoreIndexCallbackParams;
CallbackRoutine
Subroutine to determine which of two passed records comes before the other. The function should return:

-1 if DSICP_rec1 comes before DSICP_rec2
 1 if DSICP_rec1 comes after DSICP_rec2

Warnings: Cannot call this routine on datastores that contain more than 4,000 records (will return DSSE_INDEX_RECORD_NUMBER_LIMIT_EXCEEDED).

Include: datastor.h

DataStoreClose()

DataStoreError	DataStoreClose(
        word		dsToken);	/* session token to opened datastore */

Closes the datastore if no other application has it open; otherwise, closes the "session" associated with dsToken . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Warnings: Returns DSE_CLOSE_WITH_LOCKED_RECORD if the calling session has a record locked.

Include: datastor.h

DataStoreCreate()

DataStoreError	DataStoreCreate(
        DataStoreCreateParams			*paramsPtr,
        word			dsToken);

Creates a new datastore, opens a session to it and writes the session token to dsToken . Returns a DataStoreError value (DSE_NO_ERROR if successful). Takes the following arguments:

paramsPtr
Pointer to a DataStoreCreateParams structure. DSCP_name contains the name of the datastore. You can pass DSF_PRIVATE, DSF_TIMESTAMP and/or DSF_NO_PRIMARY_KEY to DSCP_flags. DSCP_keyList and DSCP_keyCount contain the list and number of key field(s), respectively. Specify the object to receive notification of datastore changes in DSCP_notifObject .
        typedef struct {
            TCHAR			*DSCP_name;
            DataStoreFlags			DSCP_flags;
            FieldDescriptor			*DSCP_keyList;
            word			DSCP_keyCount;
            optr			DSCP_notifObject;
        } DataStoreCreateParams;
dsToken
Session token to the opened datastore.

Include: datastor.h

DataStoreDelete()

DataStoreError	DataStoreDelete(
        TCHAR		*dsNamePtr);

Deletes the named datastore if no other application(s) has(have) it open. Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreDeleteField()

DataStoreStructureError	DataStoreDeleteField(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		fieldID);

Deletes the passed field from the datastore. Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful).

You can reference a field by its name or FieldID . If referencing by name, pass "0" in fieldID ; if referencing by FieldID , pass NULL in fieldNamePtr .

Warnings: Cannot delete key fields.

Cannot delete a field if an application has locked any record in the datastore (will return DSSE_ACCESS_DENIED).

Include: datastor.h

See Also: DataStoreRemoveFieldFromRecord().

DataStoreDeleteRecord()

DataStoreDataError	DataStoreDeleteRecord(
        word		dsToken,	/* session token to opened datastore */
        RecordID		recordID);

Deletes the passed record (referenced by its RecordID ) and flushes it from the record buffer if it is the current record. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: Cannot delete a record that has been locked or loaded into memory (will return DSDE_RECORD_IN_USE).

Include: datastor.h

See Also: DataStoreDeleteRecordNum().

DataStoreDeleteRecordNum()

DataStoreDataError	DataStoreDeleteRecordNum(
        word		dsToken,	/* session token to opened datastore */
        RecordNum		recordNum);

Deletes the passed record (referenced by its RecordNum ) and flushes it from the record buffer if it is the current record. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: Cannot delete a record that has been locked or loaded into memory (will return DSDE_RECORD_IN_USE).

Include: datastor.h

See Also: DataStoreDeleteRecord().

DataStoreDiscardRecord()

DataStoreDataError	DataStoreDiscardRecord(
        word		dsToken);	/* session token to opened datastore */

Flushes the current record from the record buffer (without saving any changes). Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Include: datastor.h

See Also: DataStoreSaveRecord().

DataStoreFieldEnum()

DataStoreError	DataStoreFieldEnum(
        word		dsToken,
        RecordHeader		*recordPtr,
        void		*enumDataPtr,
        Boolean _pascal (*CallbackRoutine) (
            void		*fieldDataPtr,
            word		fieldDataSize,
            FieldType		fieldType,
            FieldCategory		fieldCat,
            FieldID		fieldID,
            FieldFlags		flags,
            void		*enumDataPtr));

Enumerates through the fields in the passed record. When the callback returns TRUE, stops enumeration and returns DSE_NO_ERROR. Takes the following arguments:recordPtr

dsToken
Session token to the opened datastore.
recordPtr
Pointer to a RecordHeader structure.
        typedef struct {
            RecordID		RH_id;
            word		RH_size;
            byte		RH_fieldCount;
        } RecordHeader;
enumDataPtr
Pointer to data that caller passes to the callback.
CallbackRoutine
Boolean subroutine to determine when enumeration should end.

Be Sure To: Lock the record first with DataStoreLockRecord() before calling this routine.

Warnings: Callback can modify data passed in enumDataPtr but cannot modify any actual field data.

Include: datastor.h

See Also: DataStoreRecordEnum().

DataStoreFieldIDToName()

DataStoreStructureError	DataStoreFieldIDToName(
        word		dsToken,	/* session token to opened datastore */
        FieldID		fieldID,
        TCHAR		*fieldNamePtr,
        word		*nameSizePtr);

Writes the field name corresponding to the passed FieldID ( fieldID ) to fieldNamePtr . Copies no more than nameSizePtr bytes and overwrites this same parameter with the number of bytes actually copied. Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful).

Include: datastor.h

See Also: DataStoreFieldNameToID().

DataStoreFieldNameToID()

DataStoreStructureError	DataStoreFieldNameToID(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		*fieldIDPtr);

Writes the FieldID corresponding to the passed field name (fieldNamePtr ) to fieldIDPtr . Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful).

Include: datastor.h

See Also: DataStoreFieldIDToName().

DataStoreGetExtraData()

DataStoreError	DataStoreGetExtraData(
        word		dsToken,	/* session token to opened datastore */
        void		*dataPtr, /* data read from file header */
        word		*dataSizePtr);

Writes any extra data for this datastore to dataPtr . Writes the number of bytes copied to dataSizePtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetField()

DataStoreDataError	DataStoreGetField(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		fieldID,
        void		**blockPtrPtr,
        word		*blockSizePtr,
        MemHandle		*blockHanPtr);

Writes contents of the passed field to a block. If blockPtrPtr is NULL, allocates a block and writes its handle to blockHanPtr and its size to blockSizePtr . If blockPtrPtr is not NULL and field data exceeds block size, copies blockSizePtr bytes to the block and returns DSDE_EXCEEDS_BUFFER_SIZE. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

If referencing field by name, pass "0" in fieldID ; if referencing field by FieldID , pass NULL in fieldNamePtr .

Warnings: If the passed field is not present, writes "0" to blockSizePtr and returns DSDE_NO_ERROR.

Include: datastor.h

See Also: DataStoreGetFieldChunk().

DataStoreGetFieldChunk()

DataStoreDataError	DataStoreGetFieldChunk(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		fieldID,
        MemHandle		blockHan,
        ChunkHandle		*chunkHanPtr,
        word		*dataSizePtr);

Writes contents of the passed field to a chunk in the passed block. If chunkHanPtr is NULL, allocates a chunk and writes its handle to chunkHanPtr . If chunkHanPtr is not NULL dataSizePtrand field data exceeds chunk size, resizes the chunk and writes the number of bytes copied to dataSizePtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

If referencing field by name, pass "0" in fieldID ; if referencing field by FieldID , pass NULL in fieldNamePtr .

Warnings: If the passed field is not present, writes "0" to dataSizePtr and returns DSDE_NO_ERROR.

Include: datastor.h

See Also: DataStoreGetField().

DataStoreGetFieldCount()

DataStoreError	DataStoreGetFieldCount(
        word		dsToken,	/* session token to opened datastore */
        word		*fieldCountPtr);

Writes the number of fields defined for this datastore to fieldCountPtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

See Also: DataStoreGetRecordCount().

DataStoreGetFieldInfo()

DataStoreStructureError	DataStoreGetFieldInfo(
        word		dsToken,	/* session token to opened datastore */
        FieldDescriptor		*fieldDescPtr,
        FieldID		fieldID,
        word		fieldNameSize);

Writes FieldDescriptor for passed field to fieldDescPtr. For FD_name , writes fieldNameSize bytes only; pass "0" (and set FD_name to NULL) if you don't need the field name. Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetFieldPtr()

DataStoreDataError	DataStoreGetFieldPtr(
        word		dsToken,	/* session token to opened datastore */
        RecordHeader		*recordPtr,
        FieldID		fieldID,
        void		**fieldContentPtrPtr,
        FieldType		*fieldTypePtr,
        word		*fieldSizePtr);

Gets a pointer to the field specified by fieldID . Writes the field's content to fieldContentPtrPtr , its FieldType to fieldTypePtr , and its size to fieldSizePtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetFieldSize()

DataStoreDataError	DataStoreGetFieldSize(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		fieldID,
        word		*fieldSizePtr);

Writes the number of bytes in the passed field to fieldSizePtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

If referencing field by name, pass "0" in fieldID ; if referencing field by FieldID , pass NULL in fieldNamePtr .

Warnings: If the field is not present, writes "0" to fieldSizePtr and returns DSDE_NO_ERROR.

Include: datastor.h

DataStoreGetFlags()

DataStoreError	DataStoreGetFlags(
        word		dsToken,	/* session token to opened datastore */
        DataStoreFlags		*FlagPtr);

Writes the DataStoreFlags set for this datastore to flagPtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetNextRecordID()

DataStoreError	DataStoreGetNextRecordID(
        word		dsToken,	/* session token to opened datastore */
        RecordID		*nextIDPtr);

Writes the next RecordID to be assigned to nextIDPtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetNumFields()

DataStoreDataError	DataStoreGetNumFields(
        word		dsToken,	/* session token to opened datastore */
        word		*fieldCountPtr);

Writes the number of fields in the current record to fieldCountPtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetOwner()

DataStoreError	DataStoreGetOwner(
        word		dsToken,	/* session token to opened datastore */
        GeodeToken		*tokenPtr);

Writes the GeodeToken of the application that created the datastore to tokenPtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetRecordCount()

DataStoreError	DataStoreGetRecordCount(
        word		dsToken,	/* session token to opened datastore */
        dword		*RecordCountPtr);

Writes the number of records in the datastore to recordCountPtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Warnings: Does not count new records that have not yet been saved.

Include: datastor.h

See Also: DataStoreGetFieldCount().

DataStoreGetRecordID()

DataStoreDataError	DataStoreGetRecordID(
        word		dsToken,	/* session token to opened datastore */
        RecordID		*recordIDPtr);

Writes the RecordID of the current record to recordIDPtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: If no record is loaded, returns DSDE_RECORD_BUFFER_EMPTY.

Include: datastor.h

DataStoreGetTimeStamp()

DataStoreError	DataStoreGetTimeStamp(
        word		dsToken,	/* session token to opened datastore */
        FileDateAndTime		*timestampPtr);

Writes the date and time that the datastore was last modified to timestampPtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreGetVersion()

DataStoreError	DataStoreGetVersion(
        word		dsToken,	/* session token to opened datastore */
        ProtocolNumber		*versionPtr);

Writes the protocol number of the datastore to versionPtr . (Protocol numbers are useful for keeping track of different versions of a datastore.) Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreLoadRecord()

DataStoreDataError	DataStoreLoadRecord(
        word		dsToken,	/* session token to opened datastore */
        RecordID		recordID,
        RecordNum		*recordNumPtr);

Loads the record referenced by recordID into memory and makes it the current record. Writes the RecordNum of the loaded record to recordNumPtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: Returns DSDE_RECORD_BUFFER_NOT_EMPTY if there is already a record loaded in the session's record buffer.

Returns DSDE_INVALID_RECORD_NUMBER if there is no such record.

Returns DSDE_RECORD_IN_USE if another session has already locked the record.

Include: datastor.h

See Also: DataStoreLoadRecordNum().

DataStoreLoadRecordNum()

DataStoreDataError	DataStoreLoadRecordNum(
        word		dsToken,	/* session token to opened datastore */
        RecordNum		recordNum,
        RecordID		*recordIDPtr);

Loads the record referenced by recordNum into memory and makes it the current record. Writes the RecordID of the loaded record to recordIDPtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: Returns DSDE_RECORD_BUFFER_NOT_EMPTY if there is already a record loaded in the session's record buffer.

Returns DSDE_INVALID_RECORD_NUMBER if there is no such record.

Returns DSDE_RECORD_IN_USE if another session has already locked the record.

Include: datastor.h

See Also: DataStoreLoadRecord().

DataStoreLockRecord()

DataStoreDataError	DataStoreLockRecord(
        word		dsToken,	/* session token to opened datastore */
        RecordHeader		**recordPtrPtr);

Locks the current record. Used primarily when calling DataStoreFieldEnum() . Also used when calling DataStoreGetFieldPtr() . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Be Sure To: Unlock the locked record with DataStoreUnlockRecord() when you are through with the record.

Warnings: Returns DSDE_RECORD_BUFFER_EMPTY if there is no record loaded in the buffer.

Include: datastor.h

See Also: DataStoreUnlockRecord().

DataStoreMapRecordNumToID()

DataStoreDataError	DataStoreMapRecordNumToID(
        word		dsToken,	/* session token to opened datastore */
        RecordNum		recordNum,
        RecordID		*recordIDPtr);

Writes the RecordID corresponding to the passed RecordNum to recordIDPtr . Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Include: datastor.h

DataStoreNewRecord()

DataStoreDataError	DataStoreNewRecord(
        word		dsToken);	/* session token to opened datastore */

Creates a new (empty) record and makes it the current record. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: A new record becomes a permanent part of the datatstore only after it is saved (with DataStoreSaveRecord() ).

Include: datastor.h

DataStoreOpen()

DataStoreError	DataStoreOpen(
        TCHAR		*dsNamePtr,
        optr		notifyObject,
        DataStoreOpenFlags		openFlag,
        word		*dsTokenPtr);

Opens the datastore referenced by dsNamePtr and writes the session token to dsTokenPtr . Passing DSOF_EXCLUSIVE in openFlag opens the datastore with access restricted to the caller; if another application already has the datastore open, returns DSE_ACCESS_DENIED. Passing NullOptr in notifyObject means no object will receive GWNT_DATASTORE_CHANGE notifications. Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreRecordEnum()

DataStoreError	DataStoreRecordEnum(
        word				dsToken,
        RecordNum				*startRecordPtr,
        DataStoreRecordEnumFlags				flags,
        void				*enumDataPtr,
        Boolean _pascal (*CallbackRoutine) (
            RecordHeader				record,
            void				*enumDataPtr));

Enumerates through the records of a datastore in storage order. When CallbackRoutine returns TRUE, stops enumeration, updates startRecordPtr with the RecordNum of the last record examined, and returns DSE_NO_ERROR. (To continue enumeration, increment startRecordPtr .) Returns DSE_NO_MORE_RECORDS if first or last record is reached.

Takes the following arguments:

dsToken
Session token to the opened datastore.
startRecordPtr
Pointer to the RecordNum at which to start enumeration (unless DSREF_START_AT_END is passed [in flags] in which case this argument is ignored).
flags
DataStoreRecordEnumFlags to pass. Passing DSREF_BACKWARDS causes enumeration to go backwards. Passing DSREF_START_AT_END with DSREF_BACKWARDS causes enumeration to go backwards starting at the last record. (Passing DSREF_START_AT_END without DSREF_BACKWARDS causes enumeration to start at the first record, ignoring whatever value is in startRecordPtr.)
enumDataPtr
Pointer to data that caller passes to the callback routine.
CallbackRoutine
Boolean subroutine. Caller passes in the RecordHeader of the current record and enumDataPtr. Returning TRUE ends enumeration.

Warnings: Callback can modify data passed in enumDataPtr but cannot modify any actual data in the record.

Datastore cannot be modified during enumeration.

Include: datastor.h

See Also: DataStoreFieldEnum(), DataStoreStringSearch().

DataStoreRemoveFieldFromRecord()

DataStoreDataError	DataStoreRemoveFieldFromRecord(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		fieldID);

Deletes the passed field from the current record. Returns a DataStoreError value (DSE_NO_ERROR if successful).

If referencing field by name, pass "0" in fieldID ; if referencing field by FieldID , pass NULL in fieldNamePtr .

Include: datastor.h

See Also: DataStoreDeleteField().

DataStoreRename()

DataStoreError	DataStoreRename(
        TCHAR		*oldNamePtr,
        TCHAR		*newNamePtr);

Changes the name of a datastore from oldNamePtr to newNamePtr . Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreRenameField()

DataStoreStructureError	DataStoreRenameField(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*newNamePtr,
        TCHAR		*oldNamePtr,
        FieldID		fieldID);

Changes the name of a field from oldNamePtr to newNamePtr . Returns a DataStoreStructureError value (DSSE_NO_ERROR if successful).

If referencing field by name, pass "0" in fieldID ; if referencing field by FieldID , pass NULL in oldNamePtr .

Warnings: Returns DSSE_FIELD_NAME_TOO_LONG if new name exceeds MAX_FIELD_NAME_LENGTH ( i.e. , 40 * sizeof(TCHAR)).

Include: datastor.h

DataStoreSaveRecord()

DataStoreDataError	DataStoreSaveRecord(
        word		dsToken,
        void		*dataPtr,
        sword _pascal (*CallbackRoutine) (
            RecordHeader		*Rec1Ptr,
            RecordHeader		*Rec2Ptr,
            word		dsToken,
            void		*dataPtr),
        RecordNum		*recordNumPtr,
        RecordID		*recordIDPtr);

Writes the current record to the datastore (saving any changes made to the record) and flushes the record from the buffer. If CallbackRoutine is "0", inserts saved record to the key (if a key exists) or appends the record to the end of the datastore if no key exists. Writes the RecordNum and RecordID of the saved record to recordNumPtr and recordIDPtr, respectively. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful). Takes the following arguments:

dsToken
Session token to the opened datastore.
dataPtr
Pointer to data that caller passes to the callback.
CallbackRoutine
Subroutine to determine where to insert saved record. Caller passes RecordHeader s of two records to compare. Caller can also pass extra data with dataPtr. The function should return:

-1if Rec1Ptr comes before Rec2Ptr
0 if Rec1Ptr equals Rec2Ptr
1 if Rec1Ptr comes after Rec2Ptr
recordNumPtr
Pointer to RecordNum of saved record.
recordIDPtr
Pointer to RecordID of saved record.

Include: datastor.h

See Also: DataStoreDiscardRecord().

DataStoreSetExtraData()

DataStoreError	DataStoreSetExtraData(
        word		dsToken,	/* session token to opened datastore */
        void		*extraDataPtr, /* data to write to file header */
        word		extraDataSize);

Writes extraDataSize bytes of extra data to the datastore file header, replacing any existing extra data. Passing "0" in extraDataSize deletes any existing extra data. Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreSetField()

DataStoreDataError	DataStoreSetField(
        word		dsToken,	/* session token to opened datastore */
        TCHAR		*fieldNamePtr,
        FieldID		fieldID,
        void		*dataPtr,
        word		dataSize);

Writes dataSize bytes of data (dataPtr) to the passed field. Passing "0" in dataSize deletes the field from the record. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

If referencing field by name, pass "0" in fieldID ; if referencing field by FieldID , pass NULL in fieldNamePtr .

Include: datastor.hblockHan

DataStoreSetNextRecordID()

DataStoreError	DataStoreSetNextRecordID(
        word		dsToken,	/* session token to opened datastore */
        RecordID		nextID);

Sets the next RecordID to be assigned to nextID. Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

See Also: DataStoreSetRecordID().

DataStoreSetRecordID()

DataStoreDataError	DataStoreSetRecordID(
        word		dsToken,	/* session token to opened datastore */
        RecordID		recordID);

Sets the RecordID of the current record to recordID; datastore must be opened for exclusive access. If the passed RecordID is greater than the next RecordID to be assigned, updates the latter with the passed RecordID + 1. Returns a DataStoreDataError value (DSDE_NO_ERROR if successful).

Warnings: Overwrites any existing record with the same RecordID as the passed value.

Include: datastor.h

See Also: DataStoreSetNextRecordID().

DataStoreSetTimeStamp()

DataStoreError	DataStoreSetTimeStamp(
        word		dsToken,	/* session token to opened datastore */
        FileDateAndTime		timestamp);

Sets date and time that datastore was last modified to timestamp. Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreSetVersion()

DataStoreError	DataStoreSetVersion(
        word		dsToken,	/* session token to opened datastore */
        ProtocolNumber		version);

Sets the datastore's protocol number; this is useful for keeping track of different versions of a datastore. Returns a DataStoreError value (DSE_NO_ERROR if successful).

Include: datastor.h

DataStoreStringSearch()

DataStoreError	DataStoreStringSearch(
        word		dsToken,	/* session token to opened datastore */
        SearchParams		*paramsPtr);

Performs a string search on the datastore. If it finds a match, updates SP _startRecord with the RecordNum of the matching record and returns DSE_NO_ERROR. If it reaches the first or last record without finding a match, returns DSE_NO_MORE_RECORDS. Takes the following arguments:

dsToken
Session token to the opened datastore.
paramsPtr
Pointer to a SearchParams structure.
        typedef struct {
            SearchType		SP_searchType;
            RecordNum		SP_startRecord;
            dword		SP_maxRecords;
            FieldID		SP_startField;
            FieldCategory	SP_category;
            TCHAR		*SP_searchString;
            SearchFlags		SP_flags;
        } SearchParams;

Warnings: Returns DSSE_INDEX_RECORD_NUMBER_LIMIT_EXCEEDED if you call this routine on a datastore with more than 4,000 records.

Include: datastor.h

See Also: DataStoreRecordEnum(), DataStoreFieldEnum().

DataStoreUnlockRecord()

void	DataStoreUnlockRecord(
        word		dsToken);	/* session token to opened datastore */

Unlocks the current record. If you lock a record with DataStoreLock() , you must unlock it with this routine.

Include: datastor.h

See Also: DataStoreLockRecord().

DBAlloc()

DBItem	DBAlloc(
        VMFileHandle		file,
        DBGroup		group,
        word		size);

This routine allocates an item in the specified file and group. It is passed the handles for the file and group which will contain the new item. It returns the new item's item-handle.

Warnings: All pointers to items in the group may be invalidated.

Include: dbase.h

See Also: DBAllocUngrouped().

DBAllocUngrouped()

DBGroupAndItem 	DBAllocUngrouped(
        VMFileHandle		file,
        word		size);

This routine allocates an ungrouped item in the specified file. It is passed the handle of the file which will contain the new item. It returns the item's DBGroupAndItem value.

Warnings: All pointers to ungrouped items may be invalidated.

Include: dbase.h

See Also: DBAlloc().

DBCombineGroupAndItem()

DBGroupAndItem 	DBCombineGroupAndItem(
        DBGroup 		group,
        DBItem 		item);

This macro combines group and item handles into a dword-sized DBGroupAndItem value.

Include: dbase.h

See Also: DBGroupFromGroupAndItem(), DBItemFromGroupAndItem().

DBCopyDBItem()

DBItem 	DBCopyDBItem(
        VMFileHandle		srcFile,
        DBGroup		srcGroup,
        DBItem		srcItem,
        VMFileHandle		destFile,
        DBGroup		destGroup);

This routine makes a duplicate of a DB item in the specified DB file and group. It is passed the file handle, group handle, and item handle of the source item, as well as the file handle and group handle of the destination group. It makes a copy of the DB item and returns its DBItem handle.

Warnings: All pointers to items in the destination group may be invalidated.

Include: dbase.h

See Also: VMCopyVMChain().

DBCopyDBItemUngrouped()

DBGroupAndItem 	DBCopyDBItemUngrouped(
        VMFileHandle		srcFile,
        DBGroupAndItem		srcID,				/* source item */
        VMFileHandle		destFile);

This routine makes a duplicate of a specified DB item. It is passed the file handle and DBGroupAndItem value specifying the source item, and the file handle of the destination file. It allocates the item as an ungrouped item in the specified file and returns its DBGroupAndItem value.

Tips and Tricks: If the source item is not ungrouped, you can combine the group and item handles into a DBGroupAndItem value by calling the macro DBCombineGroupAndItem() .

Warnings: All pointers to ungrouped items in the destination file may be invalidated.

Include: dbase.h

See Also: VMCopyVMChain().

DBDeleteAt()

void	DBDeleteAt(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item,
        word		deleteOffset,
        word		deleteCount);

This routine deletes a sequence of bytes from within an item. It does not invalidate pointers to other items. The routine is passed the file, group, and item handles specifying the item, as well as an offset within the item and a number of bytes to delete. It will delete the specified number of bytes from within the item, starting with the byte at the specified offset.

Include: dbase.h

DBDeleteAtUngrouped()

void	DBDeleteAtUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id,
        word		deleteOffset,
        word		deleteCount);

This routine is just like DBDeleteAt() , except it is passed a DBGroupAndItem value instead of separate group and item handles. It does not invalidate pointers to other items.

Include: dbase.h

DBDeref()

void *	DBDeref(
        optr		*ref);

This routine is passed an optr to a locked DB item. The routine returns the address of the item.

Warnings: The optr becomes invalid when the DB item is unlocked.

Include: dbase.h

DBDirty()

void	DBUnlock(
        const void *		ptr);

This routine marks a DB item as dirty; this insures that the VM manager will copy its item-block back to the disk before freeing its memory. The routine is passed a pointer to anywhere within the item.

Tips and Tricks: All the items in an item block are marked dirty at once; thus, you can call this routine just once for several items in the same item block. Only the segment portion of the pointer is significant; thus, you can pass a pointer to anywhere in the item. This is useful if you have incremented the pointer to the item.

Include: dbase.h

DBFree()

void	DBFree(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This routine frees the specified item. It does not invalidate pointers to other items in the group. It is passed the file, group, and item handles specifying the item; it does not return anything.

Never Use Situations:
Never call DBFree() on a locked item. If you do, the item-block's lock count will not be decremented, which will prevent the item block from ever being properly unlocked.

Include: dbase.h

See Also: DBFreeUngrouped().

DBFreeUngrouped()

void	DBFreeUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id);

This routine frees the specified item. It does not invalidate pointers to other ungrouped items. It is passed the file handle and DBGroupAndItem value specifying the item; it does not return anything.

Never Use Situations: Never call DBFreeUngrouped() on a locked item. If you do, the item-block's lock count will not be decremented, which will prevent the item block from ever being properly unlocked.

Include: dbase.h

See Also: DBFree().

DBGetMap()

DBGroupAndItem 	DBGetmap(
        VMFileHandle		file);

This routine returns the DBGroupAndItem structure for the passed file's map item. If there is no map item, it returns a null handle.

Include: dbase.h

See Also: DBSetMap(), DBLockMap().

DBGroupAlloc()

DBGroup	DBGroupAlloc(
        VMFileHandle		file);

This routine allocates a new DB group in the specified file and returns its handle. If the group cannot be allocated, DBGroupAlloc() returns a null handle.

Include: dbase.h

DBGroupFree()

void	DBGroupFree(
        VMFileHandle		file,
        DBGroup		group);

This routine frees the specified group. This deletes all items and item-blocks associated with the group. It is passed the file and group handle specifying the group. Note that you can free a group even if some of its items are locked; those locked items will also be freed.

Include: dbase.h

DBGroupFromGroupAndItem()

DBGroup	DBGroupFromGroupAndItem(
        DBGroupAndItem		id);

This macro returns the DBGroup portion of a DBGroupAndItem value.

Include: dbase.h

DBInsertAt()

void	DBInsertAt(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item,
        word		insertOffset,
        word		insertCount);

This routine inserts bytes at a specified offset within a DB item. The bytes are zero-initialized. It is passed the file, group, and item handles specifying a DB item, as well as an offset within the cell and a number of bytes to insert. It inserts the specified number of bytes beginning at the specified offset; the data which was at the passed offset will end up immediately after the inserted bytes.

Warnings: This routine invalidates pointers to other items in the same group.

Include: dbase.h

DBInsertAtUngrouped()

void	DBInsertAtUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id,
        word		insertOffset,
        word		insertCount);

This routine is just like DBInsertAt() , except it is passed a DBGroupAndItem value instead of separate group and item handles.

Warnings: This routine invalidates pointers to other ungrouped items.

Include: dbase.h

DBItemFromGroupAndItem()

DBItem	DBItemFromGroupAndItem(
        DBGroupAndItem		id);

This macro returns the DBItem portion of a DBGroupAndItem value.

Include: dbase.h

DBLock()

void *	DBLock(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This routine locks the specified item and returns a pointer to it. It is passed the file, group, and item handles specifying a DB item. If it fails, it returns a null pointer.

Include: dbase.h

See Also: DBLockGetRef(), DBLockUngrouped().

DBLockGetRef()

void *	DBLockGetRef(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item,
        optr *		ref);

This routine is just like DBLock() , except that it writes the item's optr to the passed address.

Include: dbase.h

Warnings: The optr is only valid until the DB item is unlocked.

DBLockGetRefUngrouped()

void *	DBLockGetRefUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id,
        optr *		ref);

This routine is the same as DBLockGetRef() , except that it is passed a DBGroupAndItem value.

Include: dbase.h

DBLockMap()

void *	DBLockMap(
        VMFileHandle		file);

This routine locks the specified file's map item and returns its address. To unlock the map item, call DBUnlock() normally.

Include: dbase.h

See Also: DBLockMap().

DBLockUngrouped()

void *	DBLockUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id);

This routine is the same as DBLock() , except that it is passed a DBGroupAndItem value.

Include: dbase.h

DBReAlloc()

void	DBReAlloc(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item,
        word		size);

This routine changes the size of a DB item. It is passed the file, group, and item handles specifying the DB item, and a new size for the item (in bytes). If the new size is larger than the old, space will be added to the end of the item; if the new size is smaller than the old, the item will be truncated to fit.

Warnings: If the new size is larger than the old, all pointers to items in the group are invalidated. Space added is not zero-initialized.

Include: dbase.h

DBReAllocUngrouped()

void	DBReAllocUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id,
        word		size);

This routine is just like DBReAlloc() , except it is passed a DBGroupAndItem value instead of separate group and item handles.

Warnings: If the new size is larger than the old, all pointers to ungrouped items are invalidated. Space added is not zero-initialized.

Include: dbase.h

DBSetMap()

void	DBSetMap(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This routine sets the DB map item. You can later retrieve a DBGroupAndItem structure identifying this item by calling DBGetMap() . The routine is passed the file, group, and item handles specifying the new map item; it does not return anything.

Include: dbase.h

DBSetMapUngrouped()

void	DBSetMapUngrouped(
        VMFileHandle		file,
        DBGroupAndItem		id);

This routine is just like DBSetMap() , except it is passed a DBGroupAndItem value instead of separate group and item handles.

Include: dbase.h

DBUnlock()

void	DBUnlock(
        void *	ptr); /* address of item to unlock */

This routine unlocks the DB item whose address is passed.

Tips and Tricks: Only the segment address of the pointer is significant. Thus, you can pass a pointer to somewhere within an item (or immediately after it) to unlock it.

Be Sure To: If the item has been changed, make sure you call DBDirty() before you unlock it.

Include: dbase.h

DiskCheckInUse()

Boolean	DiskCheckInUse(
        DiskHandle		disk);

This routine checks if a registered disk is being used. If a file on that disk is open, or if a path on that disk is on some thread's directory stack, the routine will return true (i.e. non-zero); otherwise it will return false (i.e. zero). Note that a disk may be "in use" even if it is not currently in any drive.

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file (which is guaranteed to be in use).

Include: disk.h

DiskCheckUnnamed()

Boolean	DiskCheckUnnamed( /* returns true if disk is unnamed */
        DiskHandle		disk);

This routine checks if a registered disk has a permanent name. If the disk does not have a name, the routine returns true (i.e. non-zero); otherwise it returns false . Note that GEOS assigns a temporary name to unnamed disks when they are registered. To find out a disk's temporary or permanent name, call DiskGetVolumeName() .

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file.

See Also: DiskGetVolumeName().

Include: disk.h

DiskCheckWritable()

Boolean	DiskCheckWritable(
        DiskHandle		disk);

DiskCheckWritable() checks if a disk is currently writable. It returns false (i.e. zero) if the disk is not writable, whether by nature (e.g. a CD-ROM disk) or because the write-protect tab is on; otherwise it returns true (i.e. non-zero).

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file.

Include: disk.h

DiskCopy()

DiskCopyError 	DiskCopy(
        word		source,
        word		dest,
        Boolean _pascal (*callback)
        (DiskCopyCallback					code,
         DiskHandle					disk,
         word					param));

This routine copies one disk onto another. The destination disk must be formattable to be the same type as the source disk. The first two arguments specify the source and destination drive. These drives may or may not be the same. If they are different, they must take compatible disks.

A disk copy requires frequent interaction with the user. For example, the copy routine must prompt the user to swap disks when necessary. For this reason, DiskCopy() is passed a pointer to a callback routine. This routine handles all interaction with the user. It must be declared _pascal. Each time it is called, it is passed three arguments. The first is a member of the DiskCopyCallback enumerated type; this argument specifies what the callback routine should do. The second argument is a disk handle; its significance depends on the value of the DiskCopyCallback argument. The third argument is a word-sized piece of data whose significance depends on the value of the DiskCopyCallback argument. Note that either of these arguments may be null values, depending on the value of the DiskCopyCallback argument.

The callback routine can abort the copy by returning true (i.e. non-zero); otherwise, it should return false (i.e. zero). The callback routine is called for several situations, identified by the value of DiskCopyCallback associated with them:

CALLBACK_GET_SOURCE_DISK
The callback routine should prompt the user to insert the source disk into the appropriate drive. The second argument is meaningless for this call. The third argument is the number identifying the drive; use DriveGetName() to find the name for this drive.
CALLBACK_GET_DEST_DISK
The callback routine should prompt the user to insert the destination disk into the appropriate drive. The second argument is meaningless for this call. The third argument is the number identifying the drive.
CALLBACK_REPORT_NUM_SWAPS
The second argument is meaningless for this call. The third argument is the number of disk swaps that will be necessary. The callback routine may wish to report this number to the user and ask for confirmation.
CALLBACK_VERIFY_DEST_DESTRUCTION
If the destination disk has already been formatted, the callback routine will be called with this parameter. The callback routine may wish to remind the user that the destination disk will be erased. The second argument is the handle of the destination disk; this is useful if, for example, you want to report the disk's name. The third argument is the destination drive's number. If the callback routine aborts the copy at this time by returning non-zero, the destination disk will not be harmed.
CALLBACK_REPORT_FORMAT_PCT
If the destination disk needs to be formatted, DiskCopy() will periodically call the callback routine with this parameter. The callback routine may wish to notify the user how the format is progressing. In this case, the second argument will be meaningless; the third parameter will be the percentage of the destination disk which has been formatted. The callback routine may wish to notify the user how the format is progressing.
CALLBACK_REPORT_COPY_PCT
While the copy is taking place, DiskCopy() will periodically call the callback routine with this parameter. The callback routine may wish to notify the user how the copy is progressing. In this case, the second parameter will be meaningless; the third parameter will be the percentage of the copy which has been completed.

If the copy was successful, DiskCopy() returns zero. Otherwise, it returns a member of the DiskCopyError enumerated type. That type has the following members:

ERR_DISKCOPY_INSUFFICIENT_MEM
This is returned if the routine was unable to get adequate memory.
ERR_CANT_COPY_FIXED_DISKS
ERR_CANT_READ_FROM_SOURCE
ERR_CANT_WRITE_TO_DEST
ERR_INCOMPATIBLE_FORMATS
The destination drive must be able to write disks in exactly the same format as the source disk. Note that the source and destination drives may be the same.
ERR_OPERATION_CANCELLED
This is returned if the callback routine ever returned a non-zero value, thus aborting the copy.
ERR_CANT_FORMAT_DEST

Include: disk.h

DiskFind()

DiskHandle 	DiskFind(
        const char *		fname,			/* Null-terminated volume name */
        DiskFindResult *		code);			/* DiskFindResult written here */

This routine returns the handle of the disk with the specified name. If there is no registered disk with the specified name, DiskFind() returns a null handle. Note that while disk handles are unique, volume names are not; therefore, there may be several registered disks with identical volume names. For this reason, DiskFind() writes a member of the DiskFindResults enumerated type (described below) into the space pointed to by the code pointer.

Structures: DiskFind() uses the DiskFindResult enumerated type, which has the following values:

DFR_UNIQUE
There is exactly one registered disk with the specified name; its handle was returned.
DFR_NOT_UNIQUE
There are two or more registered disks with the specified name; the handle of an arbitrary one of these disks was returned.
DFR_NOT_FOUND
There are no registered disks with the specified name; a null disk handle was returned.

Tips and Tricks: If you want to find all the disks with a given volume name, call DiskForEach() and have the callback routine check each disk's name with DiskGetVolumeName() .

See Also: DiskRegisterDisk().

Include: disk.h

DiskForEach()

DiskHandle 	DiskForEach(
        Boolean _pascal (* callback)			(DiskHandle				disk)) 		/* callback returns true
                                     * to cancel */

This routine lets you perform an action on every registered disk. It calls the callback routine once for each disk, passing the disk's handle. The callback routine must be declared _pascal. The callback routine can force an early termination by returning true (i.e. non-zero). If the callback routine ever returns true , DiskForEach() terminates and returns the handle of the last disk passed to the callback routine. If the callback routine examines every disk without returning true , DiskForEach() returns a null handle.

Tips and Tricks: DiskForEach() is commonly used to look for a specific disk. The callback routine checks each disk to see if it's the one; if it finds a match, the callback routine simply returns true , and DiskForEach() returns the disk's handle.

Include: disk.h

DiskFormat()

FormatError 	DiskFormat(
        word 		driveNumber,
        MediaType	media,			/* Format to this size */
        DiskFormatFlags	flags,			/* See flags below */
        dword		*goodClusters,		/* These are filled in at the */
        dword		*badClusters,		/* of the format */
    DiskHandle 	disk,			/* handle of disk, or
                         * 0 if disk unformatted, or
                         * 1 if drive state unknown */
    char 		*volumeName,		/* new name of disk */
        Boolean _pascal (*callback)
            (word percentDone));	/* Return true to cancel */

This routine formats a disk to the specified size. When it is finished, it fills in the passed pointers to contain the number of good and bad clusters on the disk. (To find out the size of each cluster, call DiskGetVolumeInfo() .) The routine returns a member of the FormatError enumerated type (whose members are described below).

DiskFormat() can be instructed to call a callback routine periodically. This allows the application to keep the user informed about how the format is progressing. The callback routine is passed either the percent of the disk which has been formatted, or the cylinder and head currently being formatted. The callback routine must be declared _pascal. The callback routine can cancel the format by returning true (i.e. non-zero); otherwise, it should return false (i.e. zero).

The third argument passed is a word-length flag field. Currently, only three flags are defined:

DFF_CALLBACK_PERCENT_DONE
A callback routine should be called periodically. The callback routine should be passed a single argument, namely the percentage of the format which has been done.
DFF_CALLBACK_CYL_HEAD
A callback routine should be called periodically. The callback routine should be passed a single argument, namely the cylinder head being formatted. If both DFF_CALLBACK_PERCENT_DONE and DFF_CALLBACK_CYL_HEAD are passed, results are undefined. If neither flag is set, the callback routine will never be called; a null function pointer may be passed.
DFF_FORCE_ERASE
A "hard format" should be done, i.e. the sectors should be rewritten and initialized to zeros. If this flag is not set, DiskFormat() will do a "soft format" if possible; it will check the sectors and write a blank file allocation table, but it will not necessarily erase the data from the disk.

DiskFormat() returns a member of the FormatError enumerated type. If the format was successful, it will return the constant FMT_DONE (which is guaranteed to equal zero). Otherwise, it will return one of the following constants:

FMT_DRIVE_NOT_READY
FMT_ERROR_WRITING_BOOT
FMT_ERROR_WRITING_ROOT_DIR
FMT_ERROR_WRITING_FAT
FMT_ABORTED
FMT_SET_VOLUME_NAME_ERROR
FMT_CANNOT_FORMAT_FIXED_DISKS_IN_CUR_RELEASE
FMT_BAD_PARTITION_TABLE
FMT_ERR_NO_PARTITION_FOUND
FMT_ERR_CANNOT_ALLOC_SECTOR_BUFFER
FMT_ERR_DISK_IS_IN_USE
FMT_ERR_WRITE_PROTECTED
FMT_ERR_DRIVE_CANNOT_SUPPORT_GIVEN_FORMAT
FMT_ERR_INVALID_DRIVE_SPECIFIED
FMT_ERR_DRIVE_CANNOT_BE_FORMATTED
FMT_ERR_DISK_UNAVAILABLE

Include: disk.h

DiskGetDrive()

word	DiskGetDrive(
        DiskHandle		dh);

This routine returns the drive number associated with a registered disk. Note that it will do this even if the drive is no longer usable (e.g. if a network drive has been unmapped).

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file.

See Also: DiskFind(), DiskRegisterDisk().

Include: disk.h

DiskGetVolumeFreeSpace()

dword	DiskGetVolumeFreeSpace(
        DiskHandle		dh);

This routine returns the amount of free space (measured in bytes) on the specified disk. If the disk is, by nature, not writable (e.g. a CD-ROM disk), DiskGetVolumeFreeSpace() returns zero and clears the thread's error value. If an error condition exists, DiskGetVolumeFreeSpace() returns zero and sets the thread's error value.

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file.

See Also: DiskGetVolumeInfo().

Include: disk.h

DiskGetVolumeInfo()

word	DiskGetVolumeInfo(  /* Returns 0 if successful */
        DiskHandle		dh,
        DiskInfoStruct		*info);			/* Routine fills this structure */

This routine returns general information about a disk. It returns the following four pieces of information:

  • The size of each disk block in bytes. When space is allocated, it is rounded up to the nearest whole block.
  • The number of free bytes on the disk.
  • The total number of bytes on the disk; this is the total of free and used space.
  • The disk's volume name. If the volume is unnamed, the current temporary name will be returned.

The information is written to the passed DiskInfoStruct . If an error condition occurs, DiskGetVolumeInfo() will return the error code and set the thread's error value; otherwise, it will return zero.

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file.

Include: disk.h

DiskGetVolumeName()

void	DiskGetVolumeName(
        DiskHandle		dh,
        char *		buffer);			/* Must be VOLUME_NAME_LENGTH_ZT bytes
                         * long */

This routine copies the disk's volume name (as a null-terminated string) to the passed buffer. If an error occurs, it sets the thread's error value. If the volume has no name, the routine returns the current temporary name.

Warnings: DiskGetVolumeName() does not check the size of the buffer passed. If the buffer is not at least VOLUME_NAME_LENGTH_ZT bytes long, the routine may write beyond its boundaries.

Tips and Tricks: If you pass a standard path constant, this routine will return information about the disk containing the main geos.ini file.

See Also: DiskGetVolumeInfo(), DiskSetVolumeName().

DiskRegisterDisk()

DiskHandle 	DiskRegisterDisk(
        word	driveNumber);

This routine registers a disk in the specified drive and assigns it a disk handle. (The disk handle persists only to the end of the current session of GEOS.) If the disk already has a handle, DiskRegisterDisk() will return it. If the disk does not have a name, GEOS will assign it a temporary name (such as "UNNAMED1") and display an alert box telling the user what the temporary name is. (This is done only the first time the disk is registered in each session.) Note that the temporary name is not written to the disk; thus, it persists only until the end of the current session of GEOS.

If this routine returns a disk handle, there's a disk in the drive; if it doesn't, there may still be a disk in the drive, but the disk is unformatted.

Tips and Tricks: There is no harm in registering the same disk several times. Thus, if you want to get the disk handle for the disk in a specific drive, you can simply call DiskRegisterDisk() .

See Also: DiskRegisterDiskSilently().

Include: disk.h

DiskRegisterDiskSilently()

DiskHandle 	DiskRegisterDiskSilently(
        word		driveNumber);

This routine is almost identical to DiskRegisterDisk() (described immediately above). There is only one difference: If GEOS assigns a temporary name to the disk, it will not present an alert box to the user.

See Also: DiskRegisterDisk().

Include: disk.h

DiskRestore()

DiskHandle 	DiskRestore(
        void *		buffer,			/* buffer written by DiskSave() */
        DiskRestoreError _pascal (*callback)
            (const char 						*driveName,
             const char 						*diskName,
             void 						**bufferPtr,
                 DiskRestoreError 						error);

DiskRestore() examines a buffer written by DiskSave() and returns the handle of the disk described by that buffer. If that disk is already registered, DiskRestore() will simply return its handle. If the disk is not registered and is not in the drive, DiskRestore() will call the specified callback routine. The callback routine should be declared _pascal. The callback routine is passed four arguments:

  • A null-terminated string containing the name of the drive for the disk.
  • A null-terminated string containing the disk's volume label.
  • A pointer to a variable in the DiskRestore() routine. This variable is itself a pointer to the opaque data structure provided by DiskSave() . If the callback routine takes any action which causes that structure to move (e.g. if it causes the global or local heap containing the buffer to be shuffled), it should update the pointer in DiskRestore() .
  • A member of the DiskRestoreError enumerated type. This is the error which DiskRestore() would have returned if there had not been a callback routine. This is usually DRE_REMOVABLE_DRIVE_DOESNT_HOLD_DISK.

The callback routine should prompt the user to insert a disk. If the callback routine was successful, it should return DRE_DISK_IN_DRIVE (which is guaranteed to be equal to zero). Otherwise, it should return a member of the DiskRestoreError enumerated type; usually it will return DRE_USER_CANCELLED_RESTORE. Note that the callback routine will not generally know if the user has inserted a disk; it generally just displays an alert box and returns when the user clicks "OK." After the callback routine returns, DiskRestore() registers the disk and makes sure that it's the correct one; if it is not, it calls the callback routine again.

You can pass a null function pointer to DiskRestore() instead of providing a callback routine. In this case, DiskRestore() will fail if the disk has not been registered and is not currently in the drive.

DiskRestore() returns the handle of the disk. If it fails for any reason, it returns a null handle and sets the thread's error value to a member of the DiskRestorenError enumerated type. This type has the following members:

DRE_DISK_IN_DRIVE
This is returned by the callback routine. This is guaranteed to equal zero.
DRE_DRIVE_NO_LONGER_EXISTS
The disk is associated with a drive which is no longer attached to the system.
DRE_REMOVABLE_DRIVE_DOESNT_CONTAIN_DISK
The disk is unregistered, and it is not currently in the drive associated with it. If a callback routine was provided, DiskRestore() will call it under these circumstances.
DRE_USER_CANCELLED_RESTORE
This is returned by the callback routine if the user cancels the restore.
DRE_COULDNT_CREATE_NEW_DISK_HANDLE
DiskRestore() was unable to register the disk in the appropriate drive because it couldn't create a new disk handle.
DRE_REMOVABLE_DRIVE_IS_BUSY
The appropriate drive is busy with a time-consuming operation (e.g. a disk format).
DRE_NOT_ATTACHED_TO_SERVER
The disk was from a network server to which we are not logged in.
DRE_PERMISSION_DENIED
The disk was on a network which is now denying access to it.
DRE_ALL_DRIVES_USED
The disk was on a network volume that isn't mounted, but there is no drive left to which it can be mapped.

See Also: DiskSave().

Include: disk.h

DiskSave()

Boolean	DiskSave(
        DiskHandle		disk,
        void *		buffer,				/* data will be written here */
        word *		bufferSize);				/* Size of buffer (in bytes) */

This routine writes information about a disk in the specified buffer. DiskRestore() can use this information to return the disk handle, even in another session of GEOS. The bufferSize argument should point to a word containing the size of the buffer (in bytes). If the buffer is large enough, DiskSave() will write an opaque data structure into the buffer, and change the value of *bufferSize to the actual size of the data structure; any extra buffer space can be freed or otherwise used. In this case, DiskSave() will return true (i.e. non-zero). If the buffer was too small, DiskSave() will return false (i.e. zero) and write the size needed into *bufferSize . Simply call DiskSave() again with a large enough buffer. If DiskSave() failed for some other reason, it will return false and set *bufferSize to zero.

See Also: DiskRestore().

Include: disk.h

DiskSetVolumeName()

word	DiskSetVolumeName(
        DiskHandle		dh,
        const char *		name);			/* Change the name to this */

This routine changes the disk's volume label. If it is successful, it returns zero; otherwise it returns an error code. It also sets or clears the thread's error value appropriately. The following error codes may be returned:

ERROR_INVALID_VOLUME
An invalid disk handle was passed to the routine.
ERROR_ACCESS_DENIED
For some reason, the volume's name could not be changed. For example, the volume might not be writable.
ERROR_DISK_STALE
The drive containing that disk has been deleted. This usually only happens with network drives.

Include: disk.h

DosExec()

word	DosExec(
        const char *		prog,
        DiskHandle 		progDisk,
        const char *		arguments,
        const char *		execDir,
        DiskHandle 		execDisk,
        DosExecFlags 		flags);

This routine shuts down GEOS to run a DOS program. It returns an error code if an error occurs or zero if successful. Its parameters are listed below:

prog
A pointer to a null-terminated character string representing the path of the program to be run. If a null string (not a null pointer), the system's DOS command interpreter will be run. The path string should not contain the drive name.
progDisk
A disk handle indicating the disk on which the program to be executed sits. If zero is passed, the disk on which GEOS resides will be used.
arguments
A pointer to a locked or fixed buffer containing arguments to be passed to the program being run.
execDir
A pointer to a null-terminated character string representing the path in which the program is to be run. The string should not contain the drive name. If a null pointer is passed and execDisk is zero, the program will be run in the directory in which GEOS was first started.
execDisk
The disk handle of the disk containing the directory in execDir .
flags
A record of DosExecFlags indicating whether the DOS program will give a prompt to the user to return to GEOS. The possible flags are DEF_PROMPT, DEF_FORCED_SHUTDOWN, and DEF_INTERACTIVE. For more information, see the entry for DosExecFlags .

If there was no error, DosExec() will return zero. Otherwise it will return one of the following error values: ERROR_FILE_NOT_FOUND, ERROR_DOS_EXEC_IN_PROGRESS, ERROR_INSUFFICIENT_MEMORY, or ERROR_ARGS_TOO_LONG.

Include: system.h

DriveGetDefaultMedia()

MediaType 	DriveGetDefaultMedia(
        word		driveNumber);

This routine returns the default media type for the specified drive. It returns a member of the MediaType enumerated type (described in the Data Structures reference). Note that a drive can be used for media types other than the default. For example, a high-density 3.5-inch drive will have a default media type of MEDIA_1M44, but it can read from, write to, and format 3.5-inch disks with size MEDIA_720K.

See Also: DriveTestMediaSupport().

Include: drive.h

DriveGetExtStatus()

word	DriveGetExtStatus(
        word		driveNumber);

This routine is much like DriveGetStatus() (described immediately below). However, in addition to returning all of the flags set by DriveGetStatus() , it also sets additional flags in the upper byte of the return value. It returns the following additional flags:

DES_LOCAL_ONLY
This flag is set if the device cannot be viewed over a network.
DES_READ_ONLY
This flag is set if the device is read only, i.e. no data can ever be written to a volume mounted on it (e.g., a CD-ROM drive).
DES_FORMATTABLE
This flag is set if disks can be formatted in the drive.
DES_ALIAS
This flag is set if the drive is actually an alias for a path on another drive.
DES_BUSY
This flag is set if the drive will be busy for an extended period of time (e.g., if a disk is being formatted).

If an error condition exists, DriveGetExtStatus() returns zero.

See Also: DriveGetStatus().

Include: drive.h

DriveGetName()

char *	DriveGetName(
        word	driveNumber,	/* Get name of this drive */
        char *	buffer,	/* Write name to this buffer */
        word	bufferSize);	/* Size of buffer (in bytes) */

This routine finds the name of a specified drive. You should use this name when prompting the user to take any action regarding this drive (e.g. to insert a disk). The routine writes the name, as a null terminated string, to the buffer passed. It returns a pointer to the trailing null. If the drive does not exist, or the buffer is too small, DriveGetName() returns a null pointer.

Include: drive.h

DriveGetStatus()

word	DriveGetStatus(
        word	driveNumber);

This routine returns the current status of a drive. The drive is specified by its drive number. The routine returns a word of DriveStatus flags. These flags are listed below:

DS_PRESENT
This flag is set if the physical drive exists, regardless of whether the drive contains a disk.
DS_MEDIA_REMOVABLE
This flag is set if the disk can be removed from the drive.
DS_NETWORK
This flag is set if the drive is accessed over a network (or via network protocols), which means the drive cannot be formatted or copied.
DS_TYPE
This is a mask for the lowest four bits of the field. These bits contain a member of the DriveType enumerated type.

If an error condition exists, DriveGetStatus() returns zero.

See Also: DriveGetExtStatus().

Include: drive.h

DriveTestMediaSupport()

Boolean	DriveTestMediaSupport(
        word		DriveNumber,
        MediaType		media);				/* Desired disk size */

This routine checks whether the specified drive can support disks in the specified size. It returns true (i.e., non-zero) if the drive supports the size.

See Also: DriveGetDefaultMedia().

Include: drive.h

EC()

void	EC(line);

This macro defines a line of code that will only be compiled into the error-checking version of the geode. The line parameter of the macro is the actual line of code. When the EC version of the program is compiled, the line will be treated as a normal line of code; when the non-EC version is compiled, the line will be ignored.

See Also: NEC().

EC_BOUNDS()

void	EC_BOUNDS(addr);

This macro adds an address check to the error-checking version of a program. When the EC version of the program is compiled, the address check will be included; when the non-EC version is compiled, the address check will be left out. The addr parameter is the address or pointer to be checked.

The macro expands to a call to ECCheckBounds() on the specified address or pointer. If the address is out of bounds, the program will stop with a call to FatalError() .

See Also: ECCheckBounds().

EC_ERROR()

void	EC_ERROR(code);

This macro inserts a call to FatalError() in the error-checking version of the program and does nothing to the non-EC version. When the program gets to this point, it will halt and put up an error message corresponding to the specified error code . If a condition should be checked before calling FatalError() , you can use EC_ERROR_IF() instead.

EC_ERROR_IF()

void	EC_ERROR_IF(test, code);

This macro inserts a conditional call to FatalError() in the error-checking version of a program; it does nothing for the non-EC version. The test parameter is a Boolean value that, if true , will cause the FatalError() call to be made. If test is false , FatalError() will not be called.

EC_WARNING()

EC_WARNING(word warningCode);

This macro generates a warning for the debugger when executed by error-checking code; it has no effect when in non-EC code.

Include: ec.h

EC_WARNING_IF()

EC_WARNING_IF(<expr>, word warningCode)

When this macro is executed in error-checking code, it tests <expr>; if <expr> is non-zero, it generates a warning with code warningCode for the debugger.

In non-EC code, the macro has no effect (and <expr> is not evaluated).

Include: ec.h

ECCheckBounds()

void	ECCheckBounds(
        void	*address);

This routine checks to see if the given pointer is within bounds of the block into which it points. If assertions fail, a fatal error will occur. If the "lmem" EC flag is turned on and the pointer points within an LMem block, then this routine will also check that the pointer points to a valid chunk handle or a chunk within the LMem block.

Include: ec.h

ECCheckChunkArray()

void	ECCheckChunkArray(
        optr	o);

This routine checks the validity of the specified chunk array. If the assertions fail, a fatal error will occur. The block must be locked before calling this routine.

Include: ec.h

ECCheckChunkArrayHandles()

void	ECCheckChunkArrayHandles(
        MemHandle mh,
        ChunkHandle ch);

This routine checks the validity of the specified chunk array. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckClass()

void	ECCheckClass(
        ClassStruct *class);

This routine checks that the given pointer actually references a class definition. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckDriverHandle()

void	ECCheckDriverHandle(
        GeodeHandle gh);

This routine checks that the passed handle actually references a driver. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckEventHandle()

void	ECCheckEventHandle(
        EventHandle eh);

This routine checks that the passed handle actually references a stored message. If the assertions fail, a fatal error will occur.

ECCheckFileHandle()

void	ECCheckFileHandle(
        FileHandle file);

This routine checks that the passed handle actually is a file handle and references a file. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckGeodeHandle()

void	ECCheckGeodeHandle(
        GeodeHandle gh);

This routine checks that the passed handle references a loaded geode. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckGStateHandle()

void	ECCheckGStateHandle(
        GStateHandle gsh);

This routine checks that the passed handle references a GState. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckHugeArray()

void	ECCheckHugeArray(
        VMFileHandle		vmFile,
        VMBlockHandle		vmBlock);

This routine checks the validity of the passed Huge Array. If the block passed is not the directory block of a Huge Array, the routine fails.

Include: ec.h

ECCheckLibraryHandle()

void	ECCheckLibraryHandle(
        GeodeHandle gh);

This routine checks that the passed handle references a library. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemChunk()

void	ECCheckLMemChunk(
        void * chunkPtr);

This routine checks the validity of the chunk pointed to by chunkPtr . If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemHandle()

void	ECCheckLMemHandle(
        MemHandle mh);

This routine checks that the passed handle is a memory handle and actually references a local memory block. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemHandleNS()

void	ECCheckLMemHandleNS(
        MemHandle mh);

This routine checks that the passed handle is a local memory handle; unlike ECCheckLMemHandle() , however, it does not check sharing violations (when threads are illegally using non-sharable memory). If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemObject()

void	ECCheckLMemObject(
        optr	obj); 		/* Object must be locked down. */

This routine checks the validity of an object to ensure that it is an object stored in an object block. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemObjectHandles()

void	ECCheckLMemObjectHandles(
        MemHandle mh,
        ChunkHandle ch);

This routine checks the validity of an object to ensure that it is an object stored in an object block. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemOD()

void	ECCheckLMemOD(
        optr	o);

This routine checks the validity of the given local-memory-based object. If assertions fail, a fatal error will occur.

Include: ec.h

ECCheckLMemODHandles()

void	ECCheckLMemODHandles(
        MemHandle objHan,
        ChunkHandle objCh);

This routine checks the validity of the given local-memory-based object. If assertions fail, a fatal error will occur.

Include: ec.h

ECCheckMemHandle()

void	ECCheckMemHandle(
        MemHandle mh);

This routine checks that the passed handle is a memory handle that references a memory block. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckMemHandleNS()

void	ECCheckMemHandleNS(
        MemHandle mh);

This routine checks that the passed handle references a memory block; unlike ECCheckMemHandle() , however, it will not check for sharing violations (when a thread illegally accesses a non-sharable block). If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckObject()

void	ECCheckObject(
        optr	obj);

This routine checks the validity of the given locked object. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckObjectHandles()

void	ECCheckObjectHandles(
        Memhandle mh,
        ChunkHandle ch);

This routine checks the validity of the given locked object. If the assertions fail, a fatal error will occur.

ECCheckOD()

void	ECCheckOD(
        optr	obj);

This routine checks the validity of the given object. Unlike ECCheckLMemObject() , however, it allows optrs of Process objects to be specified. If assertions fail, a fatal error will occur.

ECCheckODHandles()

void	ECCheckODHandles(
        MemHandle objHan,
        ChunkHandle objCh);

This routine checks the validity of the given object. Unlike ECCheckLMemObjectHandles() , however, it allows processes to be specified. If assertions fail, a fatal error will occur.

Include: ec.h

ECCheckProcessHandle()

void	ECCheckProcessHandle(
        GeodeHandle gh);

This routine checks that the passed handle actually references a process. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckQueueHandle()

void	ECCheckQueueHandle(
        QueueHandle qh);

This routine ensures the passed handle references an event queue. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckResourceHandle()

void	ECCheckResourceHandle(
        MemHandle mh);

This routine ensures that the passed handle references a geode resource. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckStack()

void	ECCheckStack();

This routine checks to make sure the current stack has not overflown (and is not about to). This routine also enforces a 100-byte gap between the stack bottom and the stack pointer. If assertions fail, a fatal error will occur.

Include: ec.h

ECCheckThreadHandle()

void	ECCheckThreadHandle(
        ThreadHandle th);

This routine checks that the passed handle actually references a thread. If the assertions fail, a fatal error will occur.

Include: ec.h

ECCheckWindowHandle()

void	ECCheckWindowHandle(
        WindowHandle wh);

This routine checks that the passed handle actually references a window. If the assertions fail, a fatal error will occur.

Include: ec.h

ECLMemExists()

void	ECLMemExists(
        optr	o);

This routine checks to see if the specified chunk exists. This routine should be called by applications to check the chunk handle's validity. If the assertions fail, a fatal error will occur.

Include: ec.h

ECLMemExistsHandles()

void	ECLMemExistsHandles(
        MemHandle mh,
        ChunkHandle ch);

This routine checks to see if the specified chunk exists. This routine should be called by applications to check the chunk handle's validity. If the assertions fail, a fatal error will occur.

Include: ec.h

ECLMemValidateHandle()

void	ECLMemValidateHandle(
        optr	o);

This routine checks that the passed optr points to a local memory chunk. If the assertions fail, a fatal error will occur.

Include: ec.h

ECLMemValidateHandleHandles()

void	ECLMemValidateHandleHandles(
        MemHandle mh,
        ChunkHandle ch);

This routine checks that the passed memory and chunk handles actually reference a local memory chunk. If the assertions fail, a fatal error will occur.

Include: ec.h

ECLMemValidateHeap()

void	ECLMemValidateHeap(
        MemHandle mh);

This routine does a complete error-check of the LMem heap. It is used internally and should not be needed by application programmers.

Include: ec.h

ECMemVerifyHeap()

void	ECMemVerifyHeap()

This routine makes sure the global heap is in a consistent state. If the assertions fail, a fatal error will occur. This routine should likely not be called by anything other than the EC kernel.

Include: ec.h

ECVMCheckMemHandle()

void	ECVMCheckMemHandle(
        MemHandle han);

This routine checks that the given memory handle is actually linked to a VM block handle. If assertions fail, a fatal error will occur.

Include: ec.h

ECVMCheckVMBlockHandle()

void	ECVMCheckVMBlockHandle(
        VMFileHandle file,
        VMBlockHandle block);

This routine checks the validity of the given VM file and block handles. If assertions fail, a fatal error will occur.

Include: ec.h

ECVMCheckVMFile()

void	ECVMCheckVMFile(
        VMFileHandle file);

This routine checks the validity of the given VM file handle. If assertions fail, a fatal error will occur.

Include: ec.h

ElementArrayAddElement ()

word	ElementArrayAddElement(
        optr	arr,		/* Handle of element array */
        void *	element,		/* Element to add (if necessary) */
        dword	callBackData,		/* This is passed to the Callback routine */
        Boolean _pascal (*callback) (void *elementToAdd,
            void *elementFromArray, 			dword valueForCallback));

This routine is used to add elements to an array. It is passed the address of a potential element. It compares the element with each member of an element array. If there are no matches, it adds the element to the array and sets the reference count to one. If there is a match, it increments the reference count of the matching element in the array and returns; it does not add the new element. When you pass the address of an element, make sure you pass the address of the data portion of the element (not the reference-count header).

You can pass a callback routine to ElementArrayAddElement() . ElementArrayAddElement() will call the callback routine to compare elements and see if they match. The callback routine should be declared _pascal. ElementArrayAddElement() passes the callback routine the address of the element you passed it, as well as the address of the data-portion of the element in the array (the part after the RefElementHeader structure). If the two elements match (by whatever criteria you use), return true ; otherwise, return false . If you pass a null function pointer, the default comparison routine will be called, which checks to see if every data byte matches.

Include: chunkarr.h

Tips and Tricks: If you know the element is already in the array, you can increment its reference count by calling ElementArrayAddReference() .

Be Sure To: Lock the block on the global heap before calling (unless it is fixed).

See Also: ElementArrayAddReference().

ElementArrayAddElementHandles()

word	ElementArrayAddElementHandles(
        MemHandle		mh,				/* Global handle of LMem heap */
        ChunkHandle		chunk				/* Chunk handle of element array */
        void *		element,				/* Element to add */
        dword		callBackData,		/* Passed to the Callback routine */
        Boolean _pascal (*callback) (void *elementToAdd,
            void *elementFromArray, 			dword valueForCallback));

This routine is exactly like ElementArrayAddElement() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

Tips and Tricks: If you know the element is already in the array, you can increment its reference count by calling ElementArrayAddReference() .

Be Sure To: Lock the block on the global heap before calling (unless it is fixed).

See Also: ElementArrayAddReference().

ElementArrayAddReference()

void	ElementArrayAddReference(
        optr	arr,				/* optr to element array */
        word	token);				/* Index number of element */

This routine increments the reference count of a member of an element array.

Be Sure To: Lock the block on the global heap before calling (unless it is fixed).

See Also: ElementArrayAddElement().

ElementArrayAddReferenceHandles()

void	ElementArrayAddReferenceHandles(
        MemHandle		mh,				/* Handle of LMem heap's block */
        ChunkHandle		ch,				/* Handle of element array */
        word		token);				/* Index number of element */

This routine is exactly like ElementArrayAddReference() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

ElementArrayCreate()

ChunkHandle ElementArrayCreate(
        MemHandle		mh,				/* Handle of LMem heap's block */
        word		elementSize,				/* Size of each element, or zero
                             * for variable-sized */
        word		headerSize);				/* Header size (zero for default) */

This routine creates an element array in the indicated LMem heap. It creates an ElementArrayHeader structure at the head of the chunk. If you want to leave extra space before the start of the array, you can pass a larger header size; if you want to use the standard header, pass a header size of zero.

You can specify the size of each element. Remember that the first three bytes of every element in an element array are the element's RefElementHeader ; structure, which contains the reference count; leave room for this when you choose a size. For arrays with variable-sized elements, pass a size of zero.

Include: chunkarr.h

Tips and Tricks: You may want to declare a structure for array elements; the first component should be a RefElementHeader . You can pass the size of this structure to ElementArrayCreate() .

If you want extra space after the ElementArrayHeader , you may want to create your own header structure, the first element of which is an ElementArrayHeader . You can pass the size of this header to ElementArrayCreate() , and access the data in your header via the structure.

Be Sure To: Lock the block on the global heap before calling this routine (unless it is fixed). If you pass a header size, make sure it is larger than sizeof(ElementArrayHeader) .

ElementArrayCreateAt()

ChunkHandle 	ElementArrayCreateAt(
        optr	arr,				/* optr of chunk for array */
        word	elementSize,				/* Size of each element, or zero
                         * for variable-sized */
        word	headerSize);				/* Header size (zero for default) */

This routine is just like ElementArrayCreate() above, except that the element array is created in a pre-existing chunk. The contents of that chunk will be overwritten.

Include: chunkarr.h

Warnings: If the chunk isn't large enough, it will be resized. This will invalidate all pointers to chunks in that block.

ElementArrayCreateAtHandles()

ChunkHandle 	ElementArrayCreateAtHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		ch				/* Create array in this chunk */
        word		elementSize,				/* Size of each element, or zero
                         * for variable-sized */
        word		headerSize);				/* Header size (zero for default) */

This routine is exactly like ElementArrayCreateAt() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

Warnings: If the chunk isn't large enough, it will be resized. This will invalidate all pointers to chunks in that block.

ElementArrayDelete()

void	ElementArrayDelete(
        optr	arr,					/* optr to element array */
        word	token);					/* index of element to delete */

This routine deletes an element from an element array regardless of its reference count. The routine is passed the element array's optr and the token for the element to delete.

Note that when an element is removed, it is actually resized down to zero size and added to a list of free elements. That way the index numbers of later elements are preserved.

Include: chunkarr.h

Be Sure To: Lock the block on the global heap before calling (unless it is fixed).

See Also: ElementArrayRemoveReference().

ElementArrayDeleteHandles()

void	ElementArrayDeleteHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		ch,				/* Chunk handle of element array */
        word		token);				/* Index of element delete */

This routine is exactly like ElementArrayDelete() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

Be Sure To: Lock the block on the global heap before calling (unless it is fixed).

See Also: ElementArrayRemoveReference().

ElementArrayElementChanged()

void	ElementArrayElementChanged(
        optr	arr,				/* optr to element array */
        word	token,				/* Index number of element */
        dword	callbackData,				/* This is passed along to callback */
        Boolean _pascal (*callback)			/* Returns true if elements identical */
                (void *			elementChanged,
                 void *			elementToCompare,
                 dword			valueForCallback));

This routine checks to see if an element is identical to any other elements in the same element array. This is used after an element has changed to see if it now matches another element. If the element matches another, it will be deleted, and the other element will have its reference count incremented.

The routine is passed an optr to the element array, the token of the element which is being checked, a dword of data (which is passed to the callback routine), and a pointer to a callback comparison routine. The callback routine itself is passed pointers to two elements and the callbackData argument passed to ElementArrayElementChanged() . The callback routine should be declared _pascal. If the two elements are identical, the callback should return true (i.e. non-zero); otherwise, it should return false .

If you pass a null function pointer, ElementArrayElementChanged() will do a bytewise comparison of the elements.

Include: chunkarr.h

ElementArrayElementChangedHandles()

void	ElementArrayElementChangedHandles(
        MemHandle		memHandle,				/* Handle of LMem heap's block */
        ChunkHandle		chunkHandle,				/* Chunk handle of element array */
        word		token,				/* Index number of element */
        dword		callbackData,					/* This is passed along to
                             * callback */
        Boolean _pascal (*callback)			/* Returns true if elements identical */
                (void *			elementChanged,
                 void *			elementToCompare,
                 dword			valueForCallback));

This routine is exactly like ElementArrayElementChanged() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

ElementArrayGetUsedCount()

word	ElementArrayGetUsedCount(
        optr	arr,			/* optr to element array */
        dword	callbackData,			/* This is passed to callback routine */
        Boolean _pascal (*callback)				/* return true to count this element */
                (void * element, dword cbData));

This routine counts the number of active elements in an element array; that is, elements which have a reference count of one or greater. It can be instructed to count every element, or every element which matches certain criteria. The routine is passed three parameters: the optr of the chunk array, a dword which is passed to the callback routine, and a callback routine which determines whether the element should be counted. The callback routine,which should be declared _pascal, is passed the dword an a pointer to an element. It should return true if the element should be counted; otherwise, it should return false . To count every element, pass a null callback pointer.

Include: chunkarr.h

See Also: ElementArrayTokenToUsedIndex(), ElementArrayUsedIndexToToken().

ElementArrayGetUsedCountHandles()

void	ElementArrayGetUsedCountHandles(
        MemHandle 		mh,			/* Handle of LMem heap's block */
        ChunkHandle		ch,			/* Chunk handle of element array */
        dword	callbackData,			/* This is passed to callback routine */
        Boolean _pascal (*callback)				/* return true to count this element */
                (void * element, dword cbData));

This routine is exactly like ElementArrayGetUsedCount() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

ElementArrayRemoveReference()

void	ElementArrayRemoveReference(
        optr		arr,				/* optr of element array */
        word		token,				/* Index of element to
                         * unreference */
        dword		callbackData,				/* Passed to callback routine */
        void _pascal (*callback)		(void *element, dword valueForCallback));
/* Routine is called if element is actually removed */

This routine decrements the reference count of the specified element. If the reference count drops to zero, the element will be removed. If an element is to be removed, ElementArrayRemoveReference() calls the callback routine on that element. The callback routine should perform any cleanup necessary; it is passed a pointer to the element and the callbackData argument. If you pass a null function pointer, no callback routine will be called.

Note that when an element is removed, it is actually resized down to zero size and added to a list of free elements. That way the index numbers of later elements are preserved.

Be Sure To: Lock the block on the global heap before calling (unless it is fixed).

See Also: ElementArrayDelete().

Include: chunkarr.h

ElementArrayRemoveReferenceHandles()

void	ElementArrayRemoveReferenceHandles(
        MemHandle		mh,			/* Handle of LMem heap */
        ChunkHandle		ch,			/* Chunk handle of element array */
        word		token,			/* Index of element to unreference */
        dword		callbackData,				/* Passed to callback routine */
        void _pascal (*callback)		(void *element, dword valueForCallback));
/* Routine is called if element is actually removed */

This routine is exactly like ElementArrayRemoveReference() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

ElementArrayTokenToUsedIndex()

word	ElementArrayTokenToUsedIndex(
        optr	arr,	/* Handle of element array */
        word	token,	/* Index of element to unreference */
        dword	callbackData,	/* Data passed to callback routine */
        Boolean 	_pascal (*callback)	/* Return true to count this element */
                (void *element, dword cbData));

This routine is passed the token of an element array. It translates the token into an index from some non-standard indexing scheme. The indexing scheme can either number the elements from zero, counting only those elements in use (i.e. those with a reference count greater than zero); or it can use a more restrictive scheme. If a callback routine is passed, the callback routine will be called for every used element; it should be declared _pascal and return true if the element should be counted. If a null callback pointer is passed, every used element will be counted.

Include: chunkarr.h

ElementArrayTokenToUsedIndexHandles()

word	ElementArrayTokenToUsedIndexHandles(
        MemHandle 		mh,		/* Handle of LMem heap */
        ChunkHandle 		ch,		/* Chunk handle of element array */
        word		token,		/* Index of element to unreference */
        dword		callbackData, /* Data passed to the
                         * callback routine */
        Boolean 	_pascal (*callback)	/* Return true to count this element */
                (void *element, dword cbData));

This routine is exactly like ElementArrayTokenToUsedIndex() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

ElementArrayUsedIndexToToken()

word	ElementArrayUsedIndexToToken(
        optr	arr,		/* optr to element array */
        word	index,		/* Find token of element with this index */
        dword	callbackData,		/* This is passed to the callback routine */
        Boolean _pascal (*callback)			/* Return true to count this element */
                (void *element, dword cbData));

This routine takes an index into an element array from some non-standard indexing scheme. The routine finds the element specified and returns the element's token. The indexing scheme can either number the elements from zero, counting only those elements in use (i.e. those with a reference count greater than zero); or it can use a more restrictive scheme. If a callback routine is passed, the callback routine will be called for every used element; it should should be declared _pascal return true if the element should be counted. If a null callback pointer is passed, every used element will be counted.

If no matching element is found, ElementArrayUsedIndexToToken() returns CA_NULL_ELEMENT.

Include: chunkarr.h

ElementArrayUsedIndexToTokenHandles()

word	ElementArrayUsedIndexToTokenHandles(
        MemHandle 		mh,		/* Handle of LMem heap's block */
        ChunkHandle 		ch,		/* Handle of element array */
        word		index,		/* Find token of element with this index */
        dword		callbackData, /* Data passed to the
                               * callback routine */
        Boolean 	_pascal (*callback)	/* Return true to count this element */
                (void *element, dword cbData));

This routine is exactly like ElementArrayUsedIndexToToken() above, except that the element array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

FatalError()

void	FatalError(
        word errorCode);

This routine causes a fatal error, leaving errorCode for the debugger.

FileClose()

word 	FileClose( /* returns error */
        FileHandle		fh,					/* File to close */
        Boolean		noErrorFlag);					/* Set if app. can't handle
                                 * errors */

This routine closes an open byte file. If the routine succeeds, it returns zero. If the routine fails and noErrorFlag is false (i.e., zero), FileClose() returns a member of the FileError enumerated type. If the routine fails and noErrorFlag is true (i.e., non-zero), the routine will fatal-error.

Warnings: The noErrorFlag parameter should be true only during debugging.

Include: file.h

FileCommit()

word	FileCommit( /* returns error */
        FileHandle		fh,
        Boolean		noErrorFlag);				/* set if can't handle errors */

FileCommit() forces the file system to write any cached information about a file to the disk immediately. If it is successful, it returns zero. If it fails, it returns an error code. If the routine fails and noErrorFlag is true (i.e. non-zero), the routine will fatal-error.

Warnings: The noErrorFlag parameter should be true only during debugging.

Include: file.h

FileConstructFullPath()

DiskHandle 	FileConstructFullPath(
        char		* * buffer,				/* Path string is written here */
        word		bufSize,			/* Length of buffer (in bytes) */
        DiskHandle		disk,			/* Disk or standard path; null for
                         * current path */
        const char		* tail,			/* Path relative to handle */
        Boolean		addDriveLetter);					/* Should path begin with drive
                                 * name? */

This routine translates a GEOS directory specification into a complete path string. It writes the string into the passed buffer. The directory is specified by two arguments: The first, disk , is the handle of a disk; this may also be a standard path constant. (If a null handle is passed, the current working directory is used.) The second, tail , is a pointer to the character string representing the tail end of the path. FileConstructFullPath() appends this relative path to the location indicated by the disk handle. It then constructs a full path string, beginning with that disk's root directory, and writes it to the buffer passed. If addDriveName is true (i.e. non-zero), the path string will begin with the drive's name and a colon. The pointer pointed to by buffer will be updated to point to the end of the constructed string.

Examples: The following call to FileConstructFullPath() might yield these results:

Sample call to FileConstructFullPath()

/* Here we find out the full path of a subdirectory of the DOCUMENT directory */
    DiskHandle		documentDisk;
    char		pathBuffer[256];			/* long enough for most paths */
    char		*pB = &pathBuffer;
    documentDisk = FileConstructFullPath(&pB,						/* pointer to pointer */
                    256, 		/* Length of buffer */
                    SP_DOCUMENT,		/* This can be a disk or
                             * standard path */
                    "MEMOS\\JANUARY", /* In C strings, the
                             * backslash must be
                             * doubled */
                    TRUE);		/* Prepend drive name */
/* If the standard paths are set up in the default configuration, "documentDisk"
 * would be the handle of the main hard drive, and pathBuffer would contain a
 * string like "C:\GEOWORKS\DOCUMENT\MEMOS\JANUARY" */

See Also: FileParseStandardPath().

Include: file.h

FileCopy()

word	FileCopy( /* returns error */
        const char		* source,				/* Source path and file name */
        const char		* dest,				/* Destination path and file name */
        DiskHandle		sourceDisk,				/* These handles may be Standard */
        DiskHandle		destDisk);				/* Path constants, or null to indi-
                         * cate current working directory */

This routine makes a copy of a file. The source and destination are specified with path strings. Each string specifies a path relative to the location specified by the corresponding disk handle. If the handle is a disk handle, the path is relative to that disk's root. If the disk handle is a standard path constant, the path string is relative to that standard path. If the disk handle is null, the path is relative to the current working directory.

If FileCopy() is successful, it returns zero. Otherwise, it returns one of the following error codes:

ERROR_FILE_NOT_FOUND
No such source file exists in the specified directory.
ERROR_PATH_NOT_FOUND
An invalid source or destination path string was passed.
ERROR_ACCESS_DENIED
You do not have permission to delete the existing copy of the destination file, or the destination disk or directory is not writable.
ERROR_FILE_IN_USE
Some geode has the existing destination file open.
ERROR_SHORT_READ_WRITE
There was not enough room on the destination disk.

See Also: FileMove().

Include: file.h

FileCreate()

FileHandle 	FileCreate( /* sets thread's error value */
        const char		* name,				/* relative to working directory */
        FileCreateFlags		flags,				/* see below */
        FileAttrs		attributes);				/* FileAttrs of new file */

This routine creates a byte file. The file may be a DOS file or a GEOS byte file. If the file is successfully opened, FileCreate() will return the file's handle; otherwise, it will return a null handle and set the thread's error value.

The second parameter is a word-length FileCreateFlags record. The third parameter, attributes , describes the FileAttrs record to be set for the new file.

If successful, FileCreate() returns the file's handle. If it is unsuccessful, it returns a null handle and sets the thread's error value (accessible via ThreadGetError() ). The following error values are commonly returned:

ERROR_PATH_NOT_FOUND
A relative or absolute path was passed, and the path included a directory which did not exist.
ERROR_TOO_MANY_OPEN_FILES
There is a limit to how many files may be open at once. If this limit is reached, FileCreate() will fail until a file is closed.
ERROR_ACCESS_DENIED
Either the caller requested access which could not be granted (e.g. it requested write access when another geode had already opened the file with FILE_DENY_W), or the caller tried to deny access when that access had already been granted to another geode (e.g. it tried to open the file with FILE_DENY_W when another geode already had it open for write-access).
ERROR_WRITE_PROTECTED
The caller requested write or read-write access to a file in a write-protected volume.
ERROR_FILE_EXISTS
Returned if FileCreate() was called with FILE_CREATE_ONLY and a file with the specified name already exists.
ERROR_FILE_FORMAT_MISMATCH
Returned if FileCreate() was called with FILE_CREATE_TRUNCATE or FILE_CREATE_NO_TRUNCATE and a file exists in a different format than desired; i.e. you passed FCF_NATIVE and the file already exists in the GEOS format, or vice versa.

Examples: An example of usage is shown below.

Example of FileCreate() usage

/* Here we create a DOS file in the current working directory. If the file already
 * exists, we open the existing file and truncate it.
 */
    FileHandle		newFile;
    newFile = 		FileCreate("NEWFILE.TXT",
                    ( (FILE_CREATE_TRUNCATE | FCF_NATIVE)
                     | (FILE_ACCESS_RW | FILE_DENY_RW)),
                    0); /* set no attribute bits */

See Also: FileCreateTempFile(), FileOpen().

Include: file.h

FileCreateDir()

word	FileCreateDir( /* Returns error & sets thread's error value */
        const char * name);				/* Relative path of new directory */

This routine creates a new directory. The parameter is a path string; the path is relative to the current directory. The last element of the path string must be the directory to create.

If FileCreateDir() is successful, it returns zero and clears the thread's error value. Otherwise, it returns an error code and sets the thread's error value (accessible via ThreadGetError() ). The following errors are returned:

ERROR_PATH_TOO_LONG
The path string was longer than is permitted by the file system for that device.
ERROR_FILE_EXISTS
A file or directory with the specified name already exists at the specified location.
ERROR_INVALID_NAME
The name passed was inappropriate for directories on that device.
ERROR_DISK_STALE
The drive that disk was on has been removed.
ERROR_DISK_UNAVAILABLE
The validation of the disk in that drive was aborted by the user.
ERROR_PATH_NOT_FOUND
The path string was in some way invalid; for example, it might have instructed FileCreateDir() to create the directory within a directory which does not exist.
ERROR_ACCESS_DENIED
The thread is not able to create directories in the specified location, or a directory with the specified name already exists.
ERROR_WRITE_PROTECTED
The volume is write-protected.

See Also: FileDeleteDir().

Include: file.h

FileCreateTempFile()

FileHandle FileCreateTempFile( /* Sets thread's error value */
        char		* dir,		/* directory, relative to working dir.;
                     * file name replaces 14 trailing null
                     * characters upon return */
        FileCreateFlags		flags,
        FileAttrs		attributes);

This routine creates and opens a temporary file in the directory specified. The routine automatically selects a name for the temporary file. No creation flags are needed, since the file will definitely be created anew and will be used only by this geode. The directory string must end with fourteen null bytes (enough to be replaced by the new file's name).

If FileCreateTempFile() is successful, it returns the file's handle as well as the string passed in dir , with the trailing null characters replaced by the file name. If it is unsuccessful, it returns a null handle and sets the thread's error value to a member of the FileError enumerated type. (This error value is accessible via ThreadGetError() .)

Tips and Tricks: Temporary files are usually created in a subdirectory of SP_PRIVATE_DATA.

See Also: FileCreate().

Include: file.h

FileDelete()

word	FileDelete( /* returns error */
        const char * name);			/* path relative to working directory */

This routine deletes a file. If it is successful, it returns zero; otherwise, it returns a FileError . Common errors include:

ERROR_FILE_NOT_FOUND
No such file exists in the specified directory.
ERROR_WRITE_PROTECTED
The volume is write-protected.
ERROR_PATH_NOT_FOUND
An invalid path string was passed.
ERROR_ACCESS_DENIED
You do not have permission to delete that file.
ERROR_FILE_IN_USE
Some geode has that file open.

Include: file.h

FileDeleteDir()

word	FileDeleteDir( /* Returns error & sets thread's error value */
        const char * name);			/* Relative path of directory to delete */

This argument deletes an existing directory. The parameter is a string which specifies the directory's position relative to the current working directory. The last element of the path string must be the name of the directory to delete.

If FileDeleteDir() is successful, it returns zero and clears the thread's error value. Otherwise, it returns an error code and sets the thread's error value (accessible via ThreadGetError() ). The following errors are returned:

ERROR_PATH_NOT_FOUND
The directory specified could not be found or does not exist.
ERROR_IS_CURRENT_DIRECTORY
This directory is some thread's current directory, or else it is on some thread's directory stack.
ERROR_ACCESS_DENIED
The thread does not have permission to delete the directory.
ERROR_WRITE_PROTECTED
The volume is write-protected.
ERROR_DIRECTORY_NOT_EMPTY
The directory specified is not empty. A directory must be empty before it can be deleted.

See Also: FileCreateDir().

Include: file.h

FileDuplicateHandle()

FileHandle FileDuplicateHandle( /* Sets thread's error value */
        FileHandle fh);

This routine duplicates the handle of an open file and returns the duplicate handle. The duplicate handle has the same read/write position as the original. Both handles will have to be closed for the file to be closed. If there is an error, FileDuplicateHandle() returns a null handle and sets the thread's error value (accessible via ThreadGetError() ).

Include: file.h

FileEnum()

word	FileEnum( /* returns number of files returned */
        FileEnumParams		* params,			/* described below */
        MemHandle		* bufCreated,			/* FileEnum will allocate a return-
                         * buffer block & write its handle
                         * here */
        word		* numNoFit);				/* Number of files not handled is
                         * written here */

This routine is used to examine all the files in a directory. The routine can filter the files by whether they have certain extended attributes. It creates a buffer and writes information about the files in this buffer. This routine can be called in many different ways; full details are available.

Structures: FileEnum() uses several structures and enumerated types. They are shown below; the detailed description of the structures follows.

            /* Types, values, and structures passed
 * to the FileEnum() routine: */
        typedef enum /* word */ {
FESRT_COUNT_ONLY,
FESRT_DOS_INFO,
FESRT_NAME,
FESRT_NAME_AND_ATTR
        } FileEnumStandardReturnType;
        typedef enum /* word */ {
FESC_WILDCARD
        } FileEnumStandardCallback;
            /* Types, values, and structures returned
 * by the FileEnum() routine: */
        typedef struct {
FileAttrs 			DFIS_attributes;
FileDateAndTime 			DFIS_modTimeDate;
dword 			DFIS_fileSize;
FileLongName 			DFIS_name;
DirPathInfo 			DFIS_pathInfo;
        } FEDosInfo;
        typedef struct _FileEnumCallbackData {
FileExtAttrDesc 			FECD_attrs[1];
        } FileEnumCallbackData;
        typedef struct _FileEnumParams {
FileEnumSearchFlags 				FEP_searchFlags;
FileExtAttrDesc *				FEP_returnAttrs;
word 				FEP_returnSize;
FileExtAttrDesc *				FEP_matchAttrs;
word 				FEP_bufSize;
word 				FEP_skipCount;
word _pascal (*FEP_callback) 						(struct _FileEnumParams *params,
                         FileEnumCallbackData *fecd,
                         word frame);
FileExtAttrDesc *				FEP_callbackAttrs;
dword 			FEP_cbData1;
dword 			FEP_cbData2;
word 			FEP_headerSize;
        } FileEnumParams;

Most of the information passed to FileEnum() is contained in a FileEnumParams structure. The fields of the structure are as follows:

FEP_searchFlags
This is a byte-length flag field. The flags are of type FileEnumSearchFlags (described below). These flags specify which files at the current location will be examined by FileEnum() . They also specify such things as whether a callback routine should be used.
FEP_returnAttrs
This is a pointer to an array of FileExtAttrDesc structures. The last structure should have its FEA_attr field set to FEA_END_OF_LIST. The array specifies what information will be returned by FileEnum() . The FileExtAttrDesc structure is used in a slightly different way than usual. Every file will have an entry in the return buffer; this entry will contain all the extended attribute information requested. Each FileExtAttrDesc structure will specify where in that entry its information should be written. The FEAD_value field should contain only an offset value; the extended attribute will be written at that offset into the entry. (You can specify an offset by casting an integer value to type void * .) The FEAD_size value specifies how long the return value can be. You can also request certain return values by setting FEP_returnAttrs to equal a member of the FileEnumStandardReturnType (again, by casting the FileEnumStandardReturnType value to type void * ). The FileEnumStandardReturnType enumerated type is described later in this section.
FEP_returnSize
This is the size of each entry in the returned buffer. If a standard return type or an array of FileExtAttrDesc structures was passed, each entry in the returned buffer will contain all the extended attribute information requested for that file.
FEP_matchAttrs
This is a pointer to an array of FileExtAttrDesc structures. The last structure should have its FEA_attr field set to FEA_END_OF_LIST. FileEnum() will automatically filter out and ignore all files whose attributes do not match the ones specified by this array. For attributes that are word-sized records, FEAD_value.offset holds the bits that must be set, and FEAD_value.segment holds the bits that must be clear. For byte-sized flags, FEAD_value.offset.low contains the flags that must be set, and FEAD_value.offset.high contains flags that must be clear. Byte- and word-sized non-flag values are stored in FEAD_value.offset . For all other values, FEAD_value holds a pointer to the exact value to match, and FEAD_size specifies the length of that value (in bytes). If you do not want to filter out any files in the working directory, or if you will use the callback routine to filter the files, pass a null pointer in this field.
FEP_bufS
ize
 This specifies the maximum number of entries to be returned in the buffer. If you do not want to set a limit, pass the constant FE_BUFSIZE_UNLIMITED. The buffer will be grown as necessary.
FEP_skipCount
This contains the number of matching files to be ignored before the first one is processed. It is often used in conjunction with FEP_bufSize to examine many files a few at a time. For example, if you only wanted to examine ten files at a time, you would set FEP_bufSize to ten and FEP_skipCount to zero. FileEnum() would return the data for the first ten files which match the search criteria. After processing the returned data, if there were any files left over, you could call FileEnum() again, this time with FEP_skipCount set to ten; FileEnum() would handle the next ten matching files and return the data about them. In this way you could walk through all the matching files in the directory. Note that if the FileEnumSearchFlags bit FESF_REAL_SKIP is set (in FEP _searchFlags ), the first files in the directory will be skipped before they are tested to see if they match. This is faster, since the match condition won't have to be checked for the first files in the directory.
FEP_callback
This holds a pointer to a Boolean callback routine. The callback routine can check to see if the file matches some other arbitrary criteria. The callback routine is called for any files which match all the above criteria. It should be declared _pascal. It is passed three arguments: a pointer to the FileEnumParams structure, a pointer to the current stack frame (which is used by some assembly callback routines), and a pointer to an array of FileExtAttrDesc structures. These structures are all the attributes required either for return, matching, or callback (see FEP _callbackAttrs below), with the information for the current file filled in; you can search through them directly for the information you want, or you can call FileEnumLocateAttr() to search through this array. If the file should be accepted by FileEnum() , the callback should return true ; otherwise it should return false . You can also instruct FileEnum() to use one of the standard callback routines by passing a member of the FileEnumStandardCallback enumerated type. In this case, FEP_callbackAttrs is ignored; FileEnum() will automatically pass the appropriate information to the callback routine. (Note that if the FESF_CALLBACK bit of the FEP_searchFlags field is not set, the FEP_callback field is ignored.)
FEP_callbackAttrs
This is a pointer to an array of FileExtAttrDesc structures. The last structure should have its FEA_attr field set to FEA_END_OF_LIST. The array will be filled in with the appropriate information for each file before the callback routine is called. Note that if the FESF_CALLBACK bit of the FEP_searchFlags is not set, the FEP_callbackAttrs is ignored. If you do not need any attributes passed to the callback routine, set this field to be a null pointer.
FEP_cbData1 , FEP_cbData2
These are dword-length fields. Their contents are ignored by FileEnum() ; they are used to pass information to the callback routine. If you do not call a standard callback routine, you may use these fields any way you wish.
FEP_headerSize
If the flag FESF_LEAVE_HEADER is set, FileEnum() will leave an empty header space at the beginning of the return buffer. The size of the header is specified by this field. If FESF_LEAVE_HEADER is clear, this field is ignored.

The first field of the FileEnumParams structure, FEP_searchFlags , is a word-length record containing FileEnumSearchFlags . The following flags are available:

FESF_DIRS
Directories should be examined by FileEnum() .
FESF_NON_GEOS
Non-GEOS files should be examined by FileEnum() .
FESF_GEOS_EXECS
GEOS executable files should be examined by FileEnum() .
FESF_GEOS_NON_EXECS
GEOS non-executable files (e.g., VM files) should be examined by FileEnum() .
FESF_REAL_SKIP
If a skip count of n is specified, the first n files will be skipped regardless of whether they matched the attributes passed. In this case, FileEnum() will return the number of files passed through in order to get enough files to fill the buffer; the return value can thus be the real-skip count for the next pass.
FESF_CALLBACK
FileEnum() should call a callback routine to determine whether a file should be accepted.
FESF_LOCK_CB_DATA
This flag indicates that the FileEnumParams fields FEP_callback1 and FEP_callback2 are far pointers to movable memory that must be locked before FileEnum() is called.
FESF_LEAVE_HEADER
If set, FileEnum() should leave an empty header space at the start of the return buffer. The size of this buffer is specified by the FEP_headerSize field.

The FileEnumStandardReturnType enumerated type has the following values; they are used in conjunction with the FEP_returnAttrs field of the FileEnumParams structure.

FESRT_COUNT_ONLY
FileEnum() will not allocate any memory and will not return data about files; instead, it will simply return the number of files which match the specified criteria.
FESRT_DOS_INFO
FileEnum() will return an array of FEDosInfo structures. These structures contain basic information about the file: its virtual name, size, modification date, DOS attributes, and path information (as a DirPathInfo record).
FESRT_NAME
FileEnum() will return an array of FileLongName strings, each one of which is FILE_LONGNAME_BUFFER_SIZE characters long; every one of these will contain a file's virtual name followed by a null terminator.
FESRT_NAME_AND_ATTR
FileEnum() will return an array of FENameAndAttr structures, each one of which contains a file's DOS attributes and virtual name.

The FEDosInfo structure includes a word-sized record ( DFIS_pathInfo ) which describes the file's position relative to the standard paths. It contains the following fields:

DPI_EXISTS_LOCALLY
This bit is set if the file exists in a directory under the primary tree.
DPI_ENTRY_NUMBER_IN_PATH
This is the mask for a seven-bit field whose offset is DPI_ENTRY_NUMBER_IN_PATH_OFFSET.
DPI_STD_PATH
This is the mask for an eight-bit field whose offset is DPI_STD_PATH_OFFSET. If the file is in a standard path, this field will contain a StandardPath constant for a standard path containing the file. This need not be the "closest" standard path; for example, if the file is in the "World" directory, this constant might nevertheless be SP_TOP.

See Also: FileEnumLocateAttr(), FileEnumWildcard().

Include: fileEnum.h

FileEnumLocateAttr()

void *	FileEnumLocateAttr( /* returns NULL if attr not found */
        FileEnumCallbackData*			fecd,		/* Passed to callback routine */
        FileExtendedAttribute			attr,		/* Search for this attribute */
        const char *			* name);			/* Attribute name (if second
                         * argument is FEA_CUSTOM) */

FileEnum() can be instructed to call a callback routine to decide which files to filter out. This callback routine is passed an array of FileExtAttrDesc structures. To find a particular extended attribute in this array, call FileEnumLocateAttr() . This routine will find the address of the value of the attribute desired, and return that address. If the attribute is not in the array, FileEnumLocateAttr() will return a null pointer.

Include: fileEnum.h

FileEnumWildcard()

Boolean	FileEnumWildcard(
        FileEnumCallbackData			* fecd,			/* Passed to callback routine */
        word			frame);			/* Inherited stack frame */

This routine is a utility used by FileEnum() and is rarely used by applications. It checks to see if the virtual name of the current file (the file currently being evaluated by FileEnum() ) matches the pattern in the FEP_cbData1 field of the FileEnumParams structure.

The fecd parameter is a pointer to the callback data of the FileEnum() routine. The frame parameter is a pointer to the FileEnum() stack frame: The first dword is the FEP_cbData1 field, and the second is the FEP_cbData2 field.

This routine returns true (non-zero) if the file name and pattern match. Otherwise, it returns false .

Include: fileEnum.h

FileFromTransferBlockID()

VMFileHandle	 FileFromTransferBlockID(id);
        TransferBlockID id;

This macro extracts a VMFileHandle from a value of type TransferBlockID .

FileGetAttributes()

FileAttrs 	FileGetAttributes( /* Sets thread's error value */
        const char * path);			/* file's path relative to current
             * working directory */

This routine returns the standard FileAttrs attributes for a file. The file may be a GEOS file or a plain DOS file. Note that you can also get a file's attributes by getting the file's FEA_FILE_ATTR extended attribute. If an error occurs, this routine sets the thread's error (accessible via ThreadGetError() ).

See Also: FileAttrs, FileSetAttributes().

Include: file.h

FileGetCurrentPath()

DiskHandle FileGetCurrentPath(
        char *	buffer,			/* Path string is written here */
        word	bufferSize);			/* Size of buffer in bytes */

This routine writes the current path string (without drive specifier) to the buffer provided. If the buffer is too small, it truncates the path to fit. It returns the handle of the disk containing the current path. If the current path was declared relative to a standard path, the standard path constant will be returned.

Include: file.h

FileGetDateAndTime()

FileDateAndTime 	FileGetDateAndTime( /* sets thread's error value */
        FileHandle fh);

This routine finds out the time a file was last modified. This routine can be called on GEOS or non-GEOS files. Note that you can also find out the modification time of a file by checking the extended attribute FEA_MODIFICATION. If unsuccessful, it sets the thread's error value (accessible via ThreadGetError() ).

See Also: FileDateAndTime, FileSetDateAndTime().

Include: file.h

FileGetDiskHandle()

DiskHandle FileGetDiskHandle( /* sets thread's error value */
        FileHandle fh);

This routine returns the handle of the disk containing an open file. If unsuccessful, it sets the thread's error value (accessible via ThreadGetError() ).

Include: file.h

FileGetHandleExtAttributes()

word	FileGetHandleExtAttributes(
        FileHandle			fh,				/* open file's handle */
        FileExtendedAttribute			attr,				/* attribute to get */
        void			* buffer,				/* attribute is written here */
        word			bufSize);				/* length of buffer in bytes */

This routine gets one or more extended attributes of an open file. (To get the attributes of a file without opening it, call FileGetPathExtAttributes() .) If a single attribute is requested, the attribute will be written in the buffer passed. If several attributes are requested, attr should be set to FEA_MULTIPLE, and buffer should point to an array of FileExtAttrDesc structures. In this case, bufSize should be the number of structures in the buffer, not the length of the buffer.

If FileGetHandleExtAttributes() is successful, it returns zero. Otherwise, it returns one of the following error codes:

ERROR_ATTR_NOT_SUPPORTED
The file system does not recognize the attribute constant passed.
ERROR_ATTR_SIZE_MISMATCH
The buffer passed was too small for the attribute requested.
ERROR_ATTR_NOT_FOUND
The file does not have a value set for that attribute.
ERROR_ACCESS_DENIED
You do not have read-access to the file.

Tips and Tricks: Note that the only way to recover a custom attribute is by passing FEA_MULTIPLE, and using a FileExtAttrDesc to describe the attribute.

See Also: FileGetPathExtAttributes().

Include: file.h

FileGetPathExtAttributes()

word	FileGetPathExtAttributes(
        const char			* path,				/* path relative to current
                             * working directory */
        FileExtendedAttribute			attr,				/* attribute to get */
        void			* buffer,				/* attribute is written here */
        word			bufSize);				/* length of buffer in bytes */

This routine gets one or more extended attributes of a GEOS file. If a single attribute is requested, the attribute will be written in the buffer passed. If several attributes are requested, attr should be set to FEA_MULTIPLE, and buffer should point to an array of FileExtAttrDesc structures. In this case, bufSize should be the number of structures in the buffer, not the length of the buffer.

If FileGetPathExtAttributes() is successful, it returns zero. Otherwise, it returns one of the following error codes:

ERROR_ATTR_NOT_SUPPORTED
The file system does not recognize the attribute constant passed.
ERROR_ATTR_SIZE_MISMATCH
The buffer passed was too small for the attribute requested.
ERROR_ATTR_NOT_FOUND
The file does not have a value set for that attribute.
ERROR_ACCESS_DENIED
You do not have read-access to the file.

Tips and Tricks: Note that the only way to recover a custom attribute is by passing FEA_MULTIPLE, and using a FileExtAttrDesc to describe the attribute.

See Also: FileGetHandleExtAttributes().

Include: file.h

FileLockRecord()

word	FileLockRecord( /* returns error */
        FileHandle		fh,
        dword		filePos,			/* lock starting at this position... */
        dword		regLength);				/* lock this many bytes */

This routine puts a lock on a part of a byte file. It first checks to make sure that there are no locks that overlap the region specified; if there are, it will fail and return ERROR_ALREADY_LOCKED. If there are no locks, it will place a lock on the region specified and return zero.

Warnings: Locking a region only prevents threads from locking part of the same region; it does not prevent them from reading from or writing to the region. If applications use this mechanism, they have to make sure to call FileLockRecord before trying to access a part of a file.

See Also: FileUnlockRecord(), HandleP().

FileMove()

word	FileMove( /* Returns error */
        const char		* source,				/* source path and file name */
        const char		* dest,				/* destination path and file name */
        DiskHandle		sourceDisk,				/* These handles may be Standard */
        DiskHandle		destDisk);				/* Path constants, or null to indi-
                         * cate current working directory */

This routine moves a file from one location to another. The source and destination are specified with path strings. Each string specifies a path relative to the location specified by the corresponding disk handle. If the handle is a disk handle, the path is relative to that disk's root. If the disk handle is a standard path constant, the path string is relative to that standard path. If the disk handle is null, the path is relative to the current working directory.

If FileMove() is successful, it returns zero. Otherwise, it returns one of the following error codes and sets the thread's error value.

ERROR_FILE_NOT_FOUND
No such source file exists in the specified directory.
ERROR_PATH_NOT_FOUND
An invalid source or destination path string was passed.
ERROR_ACCESS_DENIED
You do not have permission to delete the source file, or there is already a file with the same name as the destination file (and you do not have permission to delete it), or the destination disk or directory is not writable.
ERROR_FILE_IN_USE
Either the source file is in use, or there is already a file with the same name as the destination file, and it is in use.
ERROR_SHORT_READ_WRITE
There was not enough room on the destination disk.

See Also: FileCopy().

Include: file.h

FileOpen()

FileHandle FileOpen( /* sets thread's error value */
        const char		* name,			/* relative to working dir */
        FileAccessFlags		flags);			/* Permissions/exclusions */

This routine opens a file for bytewise access. The file may be a DOS file or a GEOS byte file. If the file is successfully opened, FileOpen() will return the file's handle; otherwise, it will return a null handle and set the thread's error value (accessible via ThreadGetError() ). Errors typically set by this routine are listed below:

ERROR_FILE_NOT_FOUND
No file with the specified name could be found in the appropriate directory.
ERROR_PATH_NOT_FOUND
A relative or absolute path had been passed, and the path included a directory which did not exist.
ERROR_TOO_MANY_OPEN_FILES
There is a limit to how many files may be open at once. If this limit is reached, FileOpen() will fail until a file is closed.
ERROR_ACCESS_DENIED
Either the caller requested access which could not be granted (e.g. it requested write access when another geode had already opened the file with FILE_DENY_W), or the caller tried to deny access when that access had already been granted to another geode (e.g. it tried to open the file with FILE_DENY_W when another geode already had it open for write-access).
ERROR_WRITE_PROTECTED
The caller requested write or read-write access to a file in a write-protected volume.

See Also: FileCreate(), FileAccessFlags.

Include: file.h

FileParseStandardPath()

StandardPath FileParseStandardPath(
        DiskHandle		disk,
        const char		** path);

This routine is passed a full path (relative to the passed disk or a standard path, if the disk handle is null) and finds the standard path which most closely contains that path. It updates the pointer whose address is passed so that it points to the trailing portion of the path string. For example, if you pass the path string "\GEOWORKS\DOCUMENT\MEMOS\APRIL", the pointer would be updated to point to the "\MEMOS\APRIL" portion, and the StandardPath SP_DOCUMENT would be returned. If the path passed does not belong to a standard path, the constant SP_NOT_STANDARD_PATH will be returned, and the pointer will not be changed.

Include: file.h

FilePopDir()

void	FilePopDir();

FilePopDir() pops the top directory off the thread's directory stack and makes it the current working directory.

See Also: FilePushDir().

Include: file.h

FilePos()

dword	FilePos( /* Sets thread's error value */
        FileHandle		fh,
        dword		posOrOffset,
        FilePosMode		mode);

This routine changes the current file position. The position can be specified in three ways, depending on the value of the mode argument:

FILE_POS_START
The file position is set to a specified number of bytes after the start of the file. Passing this mode with an offset of zero will set the file position to the start of the file.
FILE_POS_RELATIVE
The file position is incremented by a specified number of bytes; this number may be negative.
FILE_POS_END
The file position is set to a specified number of bytes after the end of the file; it is usually passed with a negative number of bytes. Passing this mode with an offset of zero will set the file position to the end of the file.

FilePos() returns a 32-bit integer. This integer specifies the absolute file position after the move (relative to the start of the file). On an error, it sets the thread's error value (accessible via ThreadGetError() ).

Tips and Tricks: To find out the current file position without changing it, call FilePos() with mode FILE_POS_RELATIVE and offset zero.

Include: file.h

FilePushDir()

void	FilePushDir();

FilePushDir() pushes the current working directory onto the thread's directory stack. It does not change the current working directory.

See Also: FilePopDir().

Include: file.h

FileRead()

word	FileRead(
        FileHandle		fh,			/* handle of open file */
        void		* buf,			/* copy data to this buffer */
        word		count,			/* Length of buffer (in bytes) */
        Boolean		noErrorFlag);					/* Set if app can't
                             * handle errors */

This routine copies data from a file into memory. It starts copying from the current position in the file. If possible, it will copy enough data to fill the buffer. If FileRead() is successful, it returns the number of bytes copied. If an error occurs, FileRead() returns -1 and sets the thread's error value (usually to ERROR_ACCESS_DENIED). The current file position will be changed to the first byte after the ones which were read.

In C, there is no way to determine whether an ERROR_SHORT_READ_WRITE error occurs. To check whether all of the data was actually copied into memory, you must compare the number of bytes actually read with the number requested to be read. If your read operation requires multiple FileRead() operations, you may should read until zero bytes are returned.

To retrieve the thread error value, use ThreadGetError() .

If the argument noErrorFlag is set to true (i.e. non-zero), FileRead() will fatal-error if an error occurs (including an ERROR_SHORT_READ_WRITE).

Warnings: Pass noErrorFlag true only during debugging.

Include: file.h

FileRename()

word	FileRename(
        const char * oldName,				/* Relative to working directory */
        const char * newName);				/* Name only, without path */

This routine changes a file's name. It cannot move a file to a different directory; to do that, call FileMove() . If the routine is successful, it returns zero; otherwise, it returns a FileError . Common errors include

ERROR_FILE_NOT_FOUND
No such file exists in the specified directory.
ERROR_PATH_NOT_FOUND
An invalid path string was passed.
ERROR_ACCESS_DENIED
You do not have permission to delete that file, or it exists on a read-only volume.
ERROR_FILE_IN_USE
Some geode has that file open.
ERROR_INVALID_NAME
The name was not a valid GEOS name; or the file is a non-GEOS file, and the name was not an appropriate native name.

See Also: FileMove().

Include: file.h

FileResolveStandardPath()

DiskHandle FileResolveStandardPath(
        char		** buffer,				/* Write path here; update pointer
                         * to point to end of path */
        word		bufSize,				/* Size of buffer (in bytes) */
        const char *		path,				/* Relative path of file */
        FileResolveStandardPathFlags flags,							/* Flags are described below */
        FileAttrs *attrsPtr);							/* Empty buffer, will be filled with attrs of passed file, if any */

This routine finds a file relative to the current location, then writes the full path to the file, starting at the root of the disk ( not at a standard path). It writes the path to the passed buffer, updating the pointer to point to the null at the end of the path string; it also returns the handle of the disk. If it cannot find the file it returns a null path.

Structures: A record of FileResolveStandardPathFlags is passed to FileResolveStandardPath() . The following flags are available:

FRSPF_ADD_DRIVE_NAME
The path string written to the buffer should begin with the drive name (e.g., "C:\GEOWORKS\DOCUMENT\MEMOS").
FRSPF_RETURN_FIRST_DIR
FileResolveStandardPath() should not check whether the passed path actually exists; instead, it should assume that the path exists in the first directory comprising the standard path, and return accordingly.

Include: file.h

FileSetAttributes()

word	FileSetAttributes( /* returns error value */
        const char		* path,			/* file's path relative to current
                     * working directory */
        FileAttrs		attr);			/* new attributes for the file */

This routine changes the standard DOS attributes of a DOS or GEOS file. Note that you can also change the attributes of a file by setting the extended attribute FEA_FILE_ATTR.

See Also: FileAttrs, FileGetAttributes().

Include: file.h

FileSetCurrentPath()

DiskHandle FileSetCurrentPath(
        DiskHandle		disk,			/* May be a standard path constant */
        const char		* path);			/* path string, null-terminated */

This routine changes the current path. It is passed two parameters: The first is the handle of the disk containing the new current path (this may be a standard path constant). The second is a null-terminated path string. It is specified with normal DOS conventions: directories are separated by backslashes; a period (".") indicates the current directory; and a pair of periods ("..") indicates the parent of the current directory. The string may not contain wildcard characters.

If disk is a disk handle, the path is relative to the root directory of that disk; if disk is a standard path constant, the path is relative to the standard path; if it is null, the path is relative to the current working directory. FileSetCurrentPath() returns the disk handle associated with the new current path; this may be a standard path constant. If FileSetCurrentPath() fails, it returns a null handle.

See Also: FileSetStandardPath().

Include: file.h

FileSetDateAndTime()

word	FileSetDateAndTime( /* returns error */
        FileHandle		fh,					/* handle of open file */
        FileDateAndTime		dateAndTime);					/* new modification time */

This routine changes a file's last-modification time-stamp. This routine can be called on GEOS or non-GEOS files. Note that you can also change the modification time of a file by changing the extended attribute FEA_MODIFICATION. If unsuccessful, this routine returns an error and sets the thread's error value (accessible via ThreadGetError() ).

See Also: FileDateAndTime, FileGetDateAndTime().

Include: file.h

FileSetHandleExtAttributes()

word	FileGetPathExtAttributes( /* returns error */
        FileHandle			fh,			/* handle of open file */
        FileExtendedAttribute		 	attr,			/* attribute to get */
        const void			* buffer,			/* attribute is read from here */
        word			bufSize);			/* length of buffer in bytes */

This routine sets one or more extended attributes of an open GEOS file. (To set the attributes of a file without opening it, call FileSetPathExtAttributes() .) If a single attribute is specified, the attribute's new value will be read from the buffer passed. If several attributes are to be changed, attr should be set to FEA_MULTIPLE, and buffer should point to an array of FileExtAttrDesc structures. In this case, bufSize should be the number of structures in the buffer, not the length of the buffer.

If FileSetHandleExtAttributes() is successful, it returns zero. Otherwise, it sets the thread's error value (accessible via ThreadGetError() ) and returns one of the following error codes:

ERROR_ATTR_NOT_SUPPORTED
The file system does not recognize the attribute constant passed.
ERROR_ATTR_SIZE_MISMATCH
The buffer passed was the wrong size for the attribute specified.
ERROR_ACCESS_DENIED
The caller does not have write-access to the file.
ERROR_CANNOT_BE_SET
The extended attribute cannot be changed. Such attributes as FEA_SIZE and FEA_NAME cannot be changed with the FileSet...() routines.

Tips and Tricks: Note that the only way to create or change a custom attribute is by passing FEA_MULTIPLE, and using a FileExtAttrDesc to describe the attribute.

See Also: FileSetPathExtAttributes().

Include: file.h

FileSetPathExtAttributes()

word	FileSetPathExtAttributes(
        const char			* path,			/* path relative to current
                         * working directory */
        FileExtendedAttribute			 attr,			/* attribute to get */
        const void			* buffer,			/* attribute is read from here */
        word			bufSize);			/* length of buffer in bytes */

This routine sets one or more extended attributes of a file. If a single attribute is specified, the attribute will be written in the buffer passed. If several attributes are to be changed, attr should be set to FEA_MULTIPLE and buffer should point to an array of FileExtAttrDesc structures. In this case, bufSize should be the number of structures in the buffer, not the length of the buffer.

If FileSetPathExtAttributes() is successful, it returns zero. Otherwise, it sets the thread's error value (accessible via ThreadGetError() ) and returns one of the following error codes:

ERROR_ATTR_NOT_SUPPORTED
The file system does not recognize the attribute constant passed.
ERROR_ATTR_SIZE_MISMATCH
The buffer passed was the wrong size for the attribute specified.
ERROR_ACCESS_DENIED
FileSetPathExtAttributes() returns this if any geode (including the caller) has the file open with "deny-write" exclusive access, or if the file is not writable.
ERROR_CANNOT_BE_SET
The extended attribute cannot be changed. Such attributes as FEA_SIZE and FEA_NAME cannot be changed with the FileSet...ExtAttributes() routines.

Tips and Tricks: Note that the only way to create or change a custom attribute is by passing FEA_MULTIPLE, and using a FileExtAttrDesc to describe the attribute.

See Also: FileSetHandleExtAttributes().

Include: file.h

FileSetStandardPath()

void	FileSetStandardPath(
        StandardPath path);			/* StandardPath to set */

This routine changes the current working directory to one of the system's StandardPath directories. Pass a standard path.

Include: file.h

FileSize()

dword	FileSize(
        FileHandle fh);		/* handle of open file */

This routine returns the size of the open file specified.

Include: file.h

FileTruncate()

word	FileTruncate(
        FileHandle		fh,			/* handle of open file */
        dword		offset,			/* offset at which to truncate */
        Boolean		noErrorFlag);

This routine truncates the specified file at the passed offset. The offset parameter can also be thought of as the desired file size. If the high bit of noErrorFlag is one, then no errors will be returned.

Include: file.h

FileUnlockRecord()

word	FileUnlockRecord( /* returns error */
        FileHandle		fh,				/* handle of open file
        dword		filePos,				/* Release lock that starts here */
        dword		regLength);				/* and is this long */

This routine releases a lock on a part of a byte-file. The lock must have been previously placed with FileLockRecord() .

See Also: FileLockRecord(), HandleV().

Include: file.h

FileWrite()

word	FileWrite( /* sets thread's error value */
        FileHandle		fh,				/* handle of open file */
        const void		* buf,				/* Copy from here into file */
        word		count,				/* # of bytes to copy */
        Boolean		noErrorFlag);				/* Set if can't handle errors */

This routine copies a specified number of bytes from a buffer to the file. The bytes are written starting with the current position in the file; any data already at that location will be overwritten. FileWrite() returns the number of bytes written. If FileWrite() could not write all the data (e.g. if the disk ran out of space), it will set the thread's error value (accessible via ThreadGetError() ) to ERROR_SHORT_READ_WRITE and return the number of bytes that were written. If it could not write the data to the file at all (e.g. if you do not have write-access to the file), it will return -1 and set the thread's error value to ERROR_ACCESS_DENIED. In any event, the file position will be changed to the first byte after the ones written.

If the argument noErrorFlag is set to true (i.e. non-zero), FileWrite() will fatal-error if an error occurs.

Warnings: Pass noErrorFlag true only during debugging.

Include: file.h

FloatAsciiToFloat()

Boolean	FloatAsciiToFloat( /* returns FALSE on success */
        word floatAtoFflags,
        word stringLength,
        void *string,
        void *resultLocation);

This routine is used to convert an ASCII string to a floating point number. It takes the following arguments:

floatAtoFflags
This FloatAsciiToFloatFlags field determines what will be done with the generated floating point number. At most one flag may be set. If FAF_PUSH_RESULT is set, then the number will be pushed on the floating-point stack. If the FAF_STORE_NUMBER flag is set, the number will be returned at the resultLocation address.
stringLength
This is the length of the buffer passed in string.
string
This buffer contains the ASCII string to convert. The string should be of the format "[|+|-] dddd.dddd [|E|e] [|+|-] dddd". The string is assumed to be legal. There may be at most a single decimal point. Spaces and thousands separators are ignored.
resultLocation
If you pass the FAF_STORE_NUMBER flag, the buffer pointed to by resultLocation will be filled with the floating point number. This buffer should be at least five words long.

FloatFloatToAscii()

word	FloatFloatToAscii( /* returns # of chars in ASCII string */
        FFA_stackFrame 		*stackFrame,
        char 		*resultString,
        FloatNum 		*number);

This routine converts a floating point number to an ASCII string. It uses a complicated data structure, FFA_stackFrame . To convert floating point numbers to ASCII without filling in this structure, use the FloatFloatToAscii_StdFormat() routine instead. Unless a special flag is passed, this routine will convert the top number of the floating point stack and pop it. FloatFloatToAscii() takes the following arguments:

stackFrame
This is an FFA_stackFrame structure. This structure contains a structure of parameters to FloatFloatToAscii() ; also, the routine will return some information in this structure. The structure is discussed in detail below.
resultString
Pointer to a buffer which will hold the generated ASCII string. This buffer must be either FLOAT_TO_ASCII_NORMAL_BUF_LEN or FLOAT_TO_ASCII_HUGE_BUF_LEN bytes, depending on the type of number (see below).
number
Specifies the format of the floating number. If it is FFAF_DONT_USE_SCIENTIFIC then the buffer resultString must be FLOAT_TO_ASCII_HUGE_BUF_LEN bytes long; otherwise, it must be FLOAT_TO_ASCII_NORMAL_BUF_LEN bytes.

If you pass the FFAF_FROM_ADDR flag, then instead of converting the number at the head of the floating-point stack, the routine will convert this number.

The routine explicitly returns the number of characters in the generated string, not counting the null terminator. It will return zero if an error occurred.

Structures:

typedef union {
    FloatFloatToAsciiData  	 				FFA_float;
        FloatFloatToDateTimeData				FFA_dateTime;
} FFA_stackFrame;

        typedef struct {
            FloatFloatToAsciiParams FFA_params;
            word                    FFA_startNumber;
            word                    FFA_decimalPoint;
            word                    FFA_endNumber;
            word                    FFA_numChars;
            word                    FFA_startExponent;
            word                    FFA_bufSize;
            word                    FFA_saveDI;
            word                    FFA_numSign;
            byte                    FFA_startSigCount;
            byte                    FFA_sigCount;
            byte                    FFA_noMoreSigInfo;
            byte                    FFA_startDecCount;
            byte                    FFA_decCount;
            word                    FFA_decExponent;
            word                    FFA_curExponent;
            byte                    FFA_useCommas;
            byte                    FFA_charsToComma;
            char                    FFA_commaChar;
            char                    FFA_decimalChar;
        } FloatFloatToAsciiData;

If you're passing a FloatFloatToAsciiData structure, you must fill in the FFA_params field. When FloatFloatToAscii() returns, some of the other fields will have been filled in with potentially useful information:

FFA_startNumber
This is an offset into the returned string, pointing to the start of the numeric characters.
FFA_decimalPoint
This is an offset into the returned string, pointing to the decimal point. If no decimal point appears in the string, this field will be zero.
FFA_endNumber
This is an offset into the returned string, pointing to the end of the numeric characters.
FFA_numChars
This is the number of characters in the returned string, or zero if there was an error. This is the same as FloatFloatToAscii()'s return value.
FFA_startExponent
This is an offset into the returned string, pointing to the "E" character. If there is no such character in the string, this field will be zero.
The other FloatFloatToAsciiData fields are internal.
        typedef struct {
            FloatFloatToAsciiFormatFlags 						formatFlags;
            byte    						decimalOffset;
            byte    						totalDigits;
            byte    						decimalLimit;
            char    						preNegative[SIGN_STR_LEN+1];
            char    						postNegative[SIGN_STR_LEN+1];
            char    						prePositive[SIGN_STR_LEN+1];
            char    						postPositive[SIGN_STR_LEN+1];
            char    						header[PAD_STR_LEN+1];
            char    						trailer[PAD_STR_LEN+1];
            byte    						FFTAP_unused;
        } FloatFloatToAsciiParams;

The FloatFloatToAsciiParams structure determines how FloatFloatToAscii() should format its generated string. It contains the following fields:

formatFlags
A FloatFloatToAsciiFormatFlags structure containing flags used to fine-tune the formatting.
decimalOffset
The number of decimal places to offset the decimal point. E.g. an offset of -6 will display numbers in terms of millions.
totalDigits
Maximum number of digits. This is normally MAX_DIGITS_FOR_NORMAL_NUMBERS. If you're using scientific notation, then this is probably MAX_DIGITS_FOR_HUGE_NUMBERS.
decimalLimit
Maximum number of decimal digits; the number will be rounded to meet this limit.
preNegative
The characters which precede a negative number. The string should be null-terminated. Thypical values are "-" and "(".
postNegative
The characters used to terminate a negative number. The string should be null-terminated. Typical values include the empty string and ")".
prePositive
The characters used to precede a postive number. The string should be null-terminated. Typical values include the null string and "+".
postPositive
The character used to terminate a positive number. The string is expected to be null terminated. Typically, this is the empty string.
header
The characters that precede the number. This string should be null terminated. This string might appear before or after the sign character, depending on whether the FFAF_SIGN_CHAR_TO_FOLLOW_HEADER flag is set. Typically, this string is empty; if it isn't, then set the FFAF_HEADER_PRESENT flag.
trailer
The characters that follow the number. This string should be null terminated. Whether this string follows or precedes the sign is determined by the FFAF_SIGN_CHAR_TO_PRECEDE_TRAILER flag. Typically, this string is empty; if it isn't, then set the FFAF_TRAILER_PRESENT flag.
        typedef WordFlags FloatFloatToAsciiFormatFlags;
        #define FFAF_FLOAT_RESERVED 									0x8000
        #define FFAF_FROM_ADDR 									0x4000
        #define FFAF_DONT_USE_SCIENTIFIC 									0x0200
        #define FFAF_SCIENTIFIC 									0x0100
        #define FFAF_PERCENT 									0x0080
        #define FFAF_USE_COMMAS 									0x0040
        #define FFAF_NO_TRAIL_ZEROS 									0x0020
        #define FFAF_NO_LEAD_ZERO 									0x0010
        #define FFAF_HEADER_PRESENT 									0x0008
        #define FFAF_TRAILER_PRESENT 									0x0004
        #define FFAF_SIGN_CHAR_TO_FOLLOW_HEADER 									0x0002
        #define FFAF_SIGN_CHAR_TO_PRECEDE_TRAILER									0x0001

The FloatFloatToAsciiFormatFlags field contains the following flags:

FFAF_FLOAT_RESERVED
This flag must be zero.
FFAF_FROM_ADDR
Normally, FloatFloatToAscii() will convert the number at the top of the floating point stack. By passing this flag, you ask that the routine instead convert the number pointed to by its number argument.
FFAF_DONT_USE_SCIENTIFIC
If set, this flag discourages the use of scientific notation. Zero padding will be used if possible.
FFAF_SCIENTIFIC
Set this flag to enforce scientific notation.
FFAF_PERCENT
Set this flag to display number as a percentage.
FFAF_USE_COMMAS
Set this flag to use comma separators.
FFAF_NO_TRAIL_ZEROS
Set this flag to pad the number with trailing zeros.
FFAF_NO_LEAD_ZERO
Set this flag to make a leading zero appear before fractional values. E.g. ".53" would appear as "0.53" instead.
FFAF_HEADER_PRESENT
If there is no header string, leave this flag clear--this results in a speed optimization.
FFAF_TRAILER_PRESENT
If there is no trailer string, leave this flag clear--this results in a speed optimization.
FFAF_SIGN_CHAR_TO_FOLLOW_HEADER
This flag determines the relative positions of the sign and header strings.
FFAF_SIGN_CHAR_TO_PRECEDE_TRAILER
This flag determines the relative positions of the sign and trailer strings.
        typedef struct {
            FloatFloatToDateTimeParams      FFA_dateTimeParams;
    } FloatFloatToDateTimeData;

        typedef struct {
                FloatFloatToDateTimeFlags 							FFA_dateTimeFlags;
                word    							FFA_year;
                byte 							FFA_month;
                byte 							FFA_day;
                byte 							FFA_weekday;
                byte 							FFA_hours;
                byte 							FFA_minutes;
                byte 							FFA_seconds;
        } FloatFloatToDateTimeParams;

The FloatFloatToDateTime structure is used to pass and return information for the formatting of date and time strings.

FFA_dateTimeFlags
This FloatFloatToDateTimeFlags field fine-tunes the way in which to format the date string.
FFA_year
FFA_month
FFA_day
FFA_weekday
FFA_hours
FFA_minutes
FFA_seconds
These fields will be used to return the various parts of the date/time.
        typedef WordFlags FloatFloatToDateTimeFlags;
        #define FFDT_DATE_TIME_OP 				0x8000
        #define FFDT_FROM_ADDR 				0x4000
        #define FFDT_FORMAT 				0x3fff

The FloatFloatToDateTimeFlags field determines how the date/time should be formated.

FFDTT_DATE_TIME_OP
Set this flag.
FFDTT_FROM_ADDR
Set this flag to convert the number passed as the number argument instead of the top number on the floating point stack.
FFDTT_FORMAT
This is a DateTimeFormat value.

See Also: FloatFloatToAscii_StdFormat().

FloatFloatToAscii_StdFormat()

word FloatFloatToAscii_StdFormat(
        char 					*string,
        FloatNum 					*number,
        FloatFloatToAsciiFormatFlags 					format,
        word 					numDigits,
        word 					numFractionalDigits)

This routine formats a floating point number as an ASCII string. It is similar in purpose to FloatFloatToAscii() . However, this routine does not require the use of the daunting FFA_stackFrame data structure. It has the following arguments:

string
Buffer to hold the converted string. This buffer must be at least FLOAT_TO_ASCII_NORMAL_BUF_LEN or FLOAT_TO_ASCII_HUGE_BUF_LEN.
number
If you pass FFAF_FROM_ADDR, this should be a pointer to a number to convert; otherwise the top number on the floating point stack will be popped and converted.
format
This is a flags field of type FloatFloatToAsciiFormatFlags . This data type is discussed under FloatFloatToAscii() . The following flags are permitted: FFAF_FROM_ADDR, FFAF_SCIENTIFIC, FFAFF_PERCENT, FFAF_USE_COMMAS, FFAF_NO_TRAIL_ZEROS.
numDigits
The number of significant digits desired.
numFranctionalDigits
The number of digits which may follow the decimal sign.

The routine returns the number of characters in the resulting string, or zero if there was an error.

See Also: FloatFloatToAscii().

FoamCancelIfNoFreeSpace()

Boolean FoamCancelIfNoFreeSpace();

Puts up an error dialog box if free disk space is below "full" level. This should be used beforeany editable view is opened (or before any operation that will consume disk space).

Pass: None.

Return: TRUE if out of disk space. FALSE if free space is below full level.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamCheckIfOutOfSpace()

FoamCheckIfOutOfSpace()

FoamDiskSpaceStatus    FoamCheckIfOutOfSpace();

Determines what level the free disk space has reached. Routines which call this can silently reject an operation if the level of disk space is insufficient.

Pass: None

Return: A FoamDiskSpaceStatus value that indicates how much free disk space is left. These values may be:

  • FDSS_NOT_FULL (free space > 50K)
  • FDSS_NEARLY_FULL (25K < free space < 50K)
  • FDSS_FULL (free space < 25K)

Include: foam.goh

See Also: FoamCancelIfNoFreeSpace()

FoamDBAddFieldToRecord()

 FieldID FoamDBAddFieldToRecord(
        VMFileHandle       file,
        MemHandle       record,
        byte       *fieldName,
        word       fieldType);

Adds a field to the record record and gives it the name fieldName. A unique FieldID is assigned to the new field and returned. The Foam Database uses the fieldType to determine the sort order of the records (i.e., a field of type "3" will be inserted after all the existing fields of type "3", but before the fields of type "4"). If you are using this routine within the context of a ContDB, then fieldType should be one of the ContdbFieldType values (see below). Otherwise, you may set your own field type.

    /*
     * The standard field types in the contact database
     */
    typedef ByteEnum ContdbFieldType;
    #define CFT_NAME        0
    #define CFT_COMPANY     1
    #define CFT_TITLE       2
    #define CFT_ADDRESS     3
    #define CFT_PHONE       4
    #define CFT_FAX         5
    #define CFT_EMAIL       6
    #define CFT_DTMF        7
    #define CFT_NOTES       8
    /* Only available in Communicator products after Feb 1997: */
    #define CFT_URL         9
    #define CFT_PASSWORD    10
    #define CFT_TEL_FAX     11
    #define CFT_TEL_DATA    12

Include: foamdb.h

FoamDBBinarySearch()

RecordID   FoamDBBinarySearch(
        VMFileHandle file,
        MemHandle record,
        PCB(int, callback,
            (void * passedRecord,	 /* Callback routine */
            void * curRecord )));

This routine performs a binary search of the Foam database file to determine the index of record. The database is sorted according to an application-defined callback routine, so FoamDBBinarySearch() can find record in a divide-and-conquer method using a callback routine based on the same sorting scheme. If this routine finds record, it returns the RecordID of this record in the database; otherwise, it returns where this record would belong in the database.

(PCB is a GEOS macro that defines a callback routine.) The prototype for this callback routine should be of the form:

      int  _pascal  MyCallBackRoutine(void * passedRecord, void * curRecord);
The callback routine should return +1 if the record passedRecord comes after curRecord, -1 if passedRecord comes before curRecord, and 0 if the items match.

Include: foamdb.h

FoamDBClose()

word FoamDBClose(
        VMFileHandle    file);

Closes the file. It is the same as VMClose(). If successful, returns non-zero; if an error occurred, then it returns FALSE and the error value must be retrieved by ThreadGetError().

Include: foamdb.h

FoamDBCreateEmptyRecord()

MemHandle FoamDBCreateEmptyRecord(
        VMFileHandle    file);

Creates a new record in the database, gives it a unique ID, and returns the handle of the record data. This record will contain no fields or data. This record does not get saved to the database unless FoamDBSaveRecord() is called.

Warning: If you want to free the memory associated with a record, do not use MemFree(); instead call FoamDBDiscardRecord() to discard any record changes or FoamDBSaveRecord() to commit them to the the database.

Include: foamdb.h

FoamDBDeleteFieldFromRecord()

void FoamDBDeleteFieldFromRecord(
        VMFileHandle    file,
        MemHandle       record,
        FieldID            id);     /* Field to delete */

Deletes field ID id from the record of the database file. id is the ID of the field as returned from FoamDBAddFieldToRecord(). While it's not a pretty solution, you can also get the FieldID of a field from the FH_id part of the FieldHeader struct.

Include: foamdb.h

FoamDBDeleteRecord()

Boolean FoamDBDeleteRecord(
    VMFileHandle 	file,
    MemHandle 	record);    /* Record to terminated */

This routine frees up the record data in record, and, if the record exists in the database, deletes it from the database as well.

Returns non-zero if the record did not exist in the database.

Include: foamdb.h

FoamDBDiscardRecord()

void FoamDBDiscardRecord(
    VMFileHandle 	file,
    MemHandle 	record);

This routine discards any changes to the passed record record and frees up the record data stored in the passed handle, but does nothing to the data stored in the database file.

Include: foamdb.h

FoamDBDuplicateRecord()

MemHandle FoamDBDuplicateRecord(
        VMFileHandle    file,
        MemHandle       record);

Duplicates an existing record record of FoamDB file, assigns a new ID to it, and returns the handle of the duplicate.

Warning: As with FoamDBCreateEmptyRecord(), the record must eventually be saved or destroyed by calling FoamDBDiscardRecord() or FoamDBSaveRecord().

Include: foamdb.h

FoamDBFieldEnum()

Boolean FoamDBFieldEnum(
        MemHandle record,
        void *enumData,
        PCB(Boolean, callback,  /* Non-zero to stop enum */
                (FieldHeader *field,  void *callbackEnumData)));      /* Callback routine */

This routine essentially "walks" through each field in the specified record and invokes the specified callback routine for each field. enumData can point to the initial value of an application-defined buffer or data structure if one is used by the callback routine, otherwise it can be ignored. This pointer is passed to the callback routine.

The callback routine accepts two parameters; a pointer to the current field field, and some application-defined data callbackEnumData. callbackEnumData may be used to store information between different calls to the callback routine such as storing the longest string yet found or performing complex filtering on the record (in which case callbackEnumData could point to an array of field IDs gathered from the field pointer). To end the enumeration before reaching the last field in the record, return TRUE (or any non-zero value) from the callback routine.

To use the callback routine:

/*
 * Prototype of callback routine
 */
Boolean _pascal ExampleCallback (FieldHeader *field, void *callbackEnumData);
...
/*
 * Call FoamDBFieldEnum()
 */
ok = FoamDBFieldEnum(recordHandle, &someDataStruct, ExampleCallback);

See Also: FoamDBLockedRecordEnum().

Include: foamdb.h

FoamDBGetCurrentRecordID()

RecordID FoamDBGetCurrentRecordID(
    VMFileHandle 	file);

Returns the RecordID that would be set for the next record in the database file.

Include: foamdb.h

FoamDBGetFieldData()

 word FoamDBGetFieldData(
        VMFileHandle    file,
        MemHandle       record,
        FieldID             id,
        char            *dest,      /* Buffer to store data */
        word            maxBytesToGet);      /* # bytes of data */

Copies the data from the specified field of record into the buffer dest. maxBytesToGet specifies the maximum number of bytes to copy into the buffer.

Returns the number of bytes actually copied.

Warning: The standard and most efficient way to store string data is without a null terminator, so when working with string data be sure to use the returned data size.

Include: foamdb.h

FoamDBGetFieldName()

Boolean FoamDBGetFieldName(
    VMFileHandle    file,
    MemHandle       record,
    FieldID            id,
    TCHAR   *dest,     /* Buffer to put name (null-terminated) */
    word            maxBytesToGet);     /* Number of characters to get */

Copies the name of the field ID id into the buffer dest.

Returns zero if field exists, non-zero if it doesn't.

Warning: Field names are null-terminated.

Include: foamdb.h

FoamDBGetFieldType()

 Boolean FoamDBGetFieldType(
        VMFileHandle    file,
        MemHandle       record,
        FieldID            id,
        byte            *type);

Gets the type of field ID id and puts it into type. Note that this type is a ContdbFieldType.

Returns zero if field exists, non-zero if it does not.

Include: foamdb.h

FoamDBGetNextPrevRecord()

dword FoamDBGetNextPrevRecord(
    VMFileHandle 	file,
    RecordID 	record,
    word 		count);

This routine returns the index into the huge array of a record count records away from the record with the RecordID record. count can be negative or positive, so this routine lets you move forwards or backwards through the database. Should the requested record be invalid, either because there is no record with RecordID record or the requested index is beyond the bounds of the database, this routine will return record.

Include: foamdb.h

FoamDBGetNumVisibleRecords()

dword	FoamDBGetNumVisibleRecords(
    VMFileHandle 	file);

This routine returns the number of visible records in the database file. This is often used when providing a list of records to a user.

Include: foamdb.h

FoamDBGetRecordFromID()

MemHandle FoamDBGetRecordFromID(
    VMFileHandle 	file,
    RecordID 	id);

This routine looks for a record in the database with the ID id, copies it into memory, and returns the handle. If no record in the database had the passed ID, this routine returns NullHandle.

Warning:
The handle returned should be freed using
FoamDBDeleteRecord(),
FoamDBDiscardRecord(), or
FoamDBSaveRecord().

Include: foamdb.h

FoamDBGetRecordID()

RecordID FoamDBGetRecordID(
        MemHandle       record);

Given a record handle, returns the RecordID of the record.

Include: foamdb.h

FoamDBGetVisibleRecord()

MemHandle FoamDBGetVisibleRecord(
    VMFileHandle 	file,
    dword 	index);

This routine finds the visible record with the index of index, copies the data into memory, and returns the handle of the block to the caller. If index is out of bounds, FoamDBGetVisibleRecord() returns NullHandle.

Warning:
The handle returned should be freed using
FoamDBDeleteRecord(),
FoamDBDiscardRecord(), or
FoamDBSaveRecord().

Include: foamdb.h

FoamDBLockedRecordEnum()

Boolean FoamDBLockedRecordEnum(
        MemHandle record,
        void *enumData,
        PCB(Boolean, callback,  /* Non-zero to stop enum */
                (FieldHeader *field,  void *callbackEnumData)));      /* Callback routine */

This routine calls a callback routine for each field in a locked down record. The callback routine is used the same way as in FoamDBFieldEnum(). It should be of the prototype

/*
 * Prototype of callback routine
 */
Boolean _pascal ExampleCallback (FieldHeader *field, void *callbackEnumData);

Include: foamdb.h

FoamDBMapNameToToken()

Boolean FoamDBMapNameToToken(
    VMFileHandle 	file,
    TCHAR 	*nameToLookFor,
    word 	*token);

This routine looks up the field named nameToLookFor in the database file and checks to see if any fields have that name.

Returns TRUE (-1) if found, FALSE (0) if not found. token will be the index to the element within the FieldName element array. This is not something that will normally be used by an application.

Include: foamdb.h

FoamDBMapTokenToName()

Boolean FoamDBMapTokenToName(
    VMFileHandle file,
    word nameToken,
    word maxBytesToCopy,
    TCHAR *dest);

This routine finds the field name associated with the token nameToken in the database file file, and copies at maximum maxBytesToCopy bytes of that name into the buffer dest. The routine returns non-zero if the token was out of bounds.

Warning: Field names are null-terminated.

Include: foamdb.h

FoamDBOpen()

VMFileHandle  FoamDBOpen(
        char *filename,
        word mapBlockSize,
        word majorProtocol,    /* Existing file protocol must match major and minor protocols */
        word minorProtocol);

Opens an existing database file filename, or creates a new one if it does not currently exist. Returns the handle of the database file or NullHandle if the file could not be opened. ThreadGetError() can be called to return the error type, which is the error returned from VMOpen(), or -1 if there was a protocol mismatch.

mapBlockSize is used to specify the size of the map block for the database. The default (minimum) size is sizeof(FoamDBMap), but more space can be allocated if the application needs to store extra data in the map block.

When creating a new file, the new file will be assigned the protocol number passed in majorProtocol and minorProtocol. When opening an existing file, the protocol number of the file is compared against the protocol number passed in majorProtocol and minorProtocol, and if they do not match, an error of -1 is returned.

Include: foamdb.h

FoamDBResortDatabase()

void FoamDBResortDatabase(
    VMFileHandle file,
    PCB(sword, callback,
        (RecordHeader *record1, RecordHeader *record2)));   /* Callback Routine */

This routine resorts the database file using the application-defined callback function to determine record order. As with the routine FoamDBSaveRecord(), the callback routine should return -1 if record1 should come before record2, or +1 if record1 should come after record2 in the database. The prototype for the callback routine should look like:

sword _pascal MyCallBackRoutine(RecordHeader *record1, RecordHeader *record2);

Include: foamdb.h

FoamDBResumeNotifications()

void FoamDBResumeNotifications(
    VMFileHandle 	file);

This routine resumes the generation of notifications for the database file. These notifications are FoamDBActionTypes.

See Also: FoamDBSuspendNotifications().

Include: foamdb.h

FoamDBResumeUpdates()

void FoamDBResumeUpdates(
    VMFileHandle 	file);

This routine resumes the generation of updates for the database file.

Include: foamdb.h

FoamDBSaveRecord()

dword FoamDBSaveRecord(
    VMFileHandle file,
    MemHandle record,
    PCB(sword, callback,
        (RecordHeader *record1,  RecordHeader *record2)));    /* Callback routine */

This routine saves the record record in the database file file, and frees up the passed memory block containing the record data. The callback routine determines where in the database the record should be stored. The Contdb library provides a front-end to this routine, ContactSaveRecord(), which should be used when saving records to a contact database.

PCB() is a GEOS macro which signifies that one of the parameters for FoamDBSaveRecord() will be a callback routine that will return a sword and accept two RecordHeaders. This callback routine should be declared as:

sword _pascal MyCallBackRoutine(RecordHeader *record1, RecordHeader *record2);

It should return -1 if record1 should come before record2, or +1 if record1 should come after record2 in the database.

Include: foamdb.h

FoamDBSetCurrentRecordID()

void FoamDBSetCurrentRecordID(
    VMFileHandle 	file,
    RecordID 	id);

Sets the record ID that will be used for the next created record.

Include: foamdb.h

FoamDBSetFieldData()

 void FoamDBSetFieldData(
        VMFileHandle    file,
        MemHandle       record,
        FieldID            id,
        char            *data,          /* Data to be stored */
        word            dataSize);      /* How many bytes of data */

Stores dataSize bytes of data in the field ID id of record.

Warning: When storing ASCII data (strings), the strings should not be null terminated. For example, if you want to store the string "ABCD", then let data be a pointer to the string and let dataSize equal 4.

Include: foamdb.h

FoamDBSetFieldName()

void FoamDBSetFieldName(
        VMFileHandle    file,
        MemHandle       record,
        FieldID            id,
        char            *name);

Changes the name of the field with the ID id of record to name.

Warning: name is a null-terminated string.

Include: foamdb.h

FoamDBSetNameForToken()

void FoamDBSetNameForToken(
    VMFileHandle 	file,
    word 	nameToken,
    TCHAR 	*name);

Sets the name for the field identified by nameToken token in the database's FieldName element array.

Warning: Field names are null-terminated.

Include: foamdb.h

FoamDBSuspendNotifications()

void FoamDBSuspendNotifications(
    VMFileHandle 	file);

This routine stops the generation of notifications to the database file. Calls to this routine must be matched by calls to FoamDBResumeNotifications(),

Include: foamdb.h

FoamDBSuspendUpdates()

void FoamDBSuspendUpdates(
    VMFileHandle	 file);

This routine stops the generation of updates for the database file. Calls to this routine must be matched by calls to FoamDBResumeUpdates().

Warning: It is dangerous to prevent updates.

Include: foamdb.h

FoamDBVisibleRecordEnum()

Boolean FoamDBVisibleRecordEnum(
    VMFileHandle file,
    void *enumData,
    PCB(Boolean, callback,     /* Non-zero to stop enum */
          (RecordHeader *record, void *callbackEnumData)));     /* Callback Routine */
This routine calls an application-specified callback routine for each visible record in the database. The callback routine can return non-zero to stop the enumeration. enumData is a pointer to any kind of data that your application needs to pass to the callback routine and that the callback routine can modify. The callback routine can return non-zero to stop the enumeration.

For a more complete explanation of using callback routines in a FoamDB, refer to the explanation given for the similar routine FoamDBFieldEnum(). The callback routine should be declared as

/*
 * Prototype of callback routine
 */
Boolean _pascal myCallBackRoutine(RecordHeader *record, void *callbackEnumData);

Include: foamdb.h

FoamDBVisibleRecordEnumWithRange()

 Boolean FoamDBVisibleRecordEnumWithRange(
    VMFileHandle file,
    void *enumData,
    dword startElement,
    dword numOfRecords,
    PCB(Boolean, callback,      /* Non-zero to stop enum */
        (RecordHeader *record, void *callbackEnumData)));     /* Callback routine */

This routine calls the application-specified callback routine for some visible records in the database, starting with the record number startElement, and continuing for numOfRecords records. enumData is a pointer to any kind of data that your application needs to pass to the callback routine and that the callback routine can modify. The callback routine can return non-zero to stop the enumeration.

For a more complete explanation of using callback routines in a FoamDB, refer to the explanation given for the similar routine FoamDBFieldEnum(). The callback routine should be declared as

/*
 * Prototype of callback routine
 */
Boolean _pascal myCallBackRoutine(RecordHeader *record, void *callbackEnumData);

Include: foamdb.h

FoamDisplayDeleteWarning()

InteractionCommand    FoamDisplayDeleteWarning(
   optr        preText,
   TCHAR *     midText,
   optr        postText );

Displays a notification box that warns the user that a file is about to be deleted.

Pass:

preText
Optr of the text chunk to display before the file name; NullOptr if no text is to be displayed.
midText
Pointer to the name of the file being deleted.
postText
Optr of the text chunk to display after the file name; NullOptr if no text is to be displayed.

Return: Input from the user: IC_YES if okay to delete, IC_NULL if not okay or if system couldn't create the dialog.

Warning: This routine may resize LMem and/or object blocks, moving themon the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayDeleteWarningNoBlock()

FoamDisplayDeleteWarningNoBlock()

void FoamDisplayDeleteWarningNoBlock(
   optr  bodyText,
   word  msg,
   optr  dest );

Displays a notification box that gives the user a delete warning. This routine differs from FoamDisplayDeleteWarning() because it does not block the caller. Instead, it displays the dialog, and upon closure by the user it will send the specified message to the given object.

Pass:

bodyText
Optr to text chunk to display in dialog.
msg
The message to send when the user selects the "Delete" option.
dest
The object to send the message specified in msg when the user selects the "Delete" option.

Return: None.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayDeleteWarning()

FoamDisplayError()

void   FoamDisplayError(
     optr    errorText );

Displays an error dialog box, with a single "Ok" trigger, displaying the text pointed to by errorText. Use FoamDisplayErrorNoBlock() if you do not want to block the calling thread.

Pass:

errorText
Optr to the text chunk to display in the dialog box.

Return: None.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayErrorNoBlock()

FoamDisplayErrorNoBlock()

void FoamDisplayErrorNoBlock(
      optr     errorText );

Same as the FoamDisplayError() routine (listed above) except this one does not block the calling thread.

Pass:

errorText
Optr to the text chunk to display in the dialog box.

Return: None.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayError()

FoamDisplayNote()

void FoamDisplayNote(
     optr    noteText);

Displays a "Note" dialog box, with a single "Ok" trigger. Use FoamDisplayNoteNoBlock() to not block the calling thread.

Pass:

noteText
Optr to text chunk of the note to display.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayNoteNoBlock()

FoamDisplayNoteNoBlock()

void FoamDisplayNoteNoBlock(
     optr  noteText );

Same as the FoamDisplayNote() routine (listed above) except this one does not block the calling thread.

Pass:

noteText
Optr to text chunk of the note to display.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayNote()

FoamDisplayOkCancelWarning()

InteractionCommand   FoamDisplayOkCancelWarning(
   optr  warningText );

Displays a warning dialog that allows the user to cancel out of an action. The dialog will have both an "Ok" and a "Cancel" trigger.

Pass:

warningText
Optr to the text chunk containg a message to display in the dialog. This will usually be a question like "Delete your data?"

Return: The user's input: IC_APPLY if user selected "Accept". IC_NULL if dialog could not be displayed.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also: FoamDisplayQuestion(), FoamDisplayWarning(), FoamDisplayWarningNoBlock().

FoamDisplayQuestion()

InteractionCommand   FoamDisplayQuestion(
   optr  questionText );

Ask the user a Yes/No question, such as "Save changes to 'foo' before quitting?". The associated text should normally end in a question mark.

Pass:

questionText
Optr to the text chunk of the question being asked.

Return: The user's input: IC_YES if user selected "Yes". IC_NULL if dialog could not be displayed.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also:
FoamDisplayOkCancelWarning(),
FoamDisplayWarning(),
FoamDisplayWarningNoBlock().

FoamDisplayWarning()

void    FoamDisplayWarning(
   optr  warningText );

Displays a notification box that will warn the user about an action. It will not allow the user to cancel the action. Use FoamDisplayWarningNoBlock() to not block the calling thread.

Pass:

warningText
Optr to the text chunk containing the warning message.

Return: None.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored pointers to them.

Include: foam.goh

See Also:
FoamDisplayOkCancelWarning(),
FoamDisplayQuestion(),
FoamDisplayWarningNoBlock().

FoamDisplayWarningNoBlock()

void    FoamDisplayWarningNoBlock(
   optr  warningText );

Same as the FoamDisplayWarning() routine (above) except this routine does not block the calling thread.

Pass:

warningText
Optr to the text chunk containing the warning message.

Return: None.

Include: foam.goh

See Also:
FoamDisplayOkCancelWarning(),
FoamDisplayQuestion(),
FoamDisplayWarning().

FoamDocCheckProtocol()

Boolean FoamDocCheckProtocol(
   DocumentHandle  docHandle,
   word            numCreatorChars,
   word            majorProtocol,
   word            minorProtocol );

Checks a document file for the correct protocol and creator token.

Pass:

docHandle
Document whose protocol will be checked.
numCreatorChars
Number of FEA_CREATOR token chars to check. You can pass two if you only want to check the first two characters.
majorProtocol
Major protocol number to check.
minorProtocol
Minor protocol number to check (pass -1 if you don't want to check the minor protocol).

Return: TRUE if the check failed, FALSE otherwise.

Include: foam.goh

See Also: None.

FoamDocClose()

void FoamDocClose(
   DocumentHandle  docHandle );

Closes a Foam document that was opened using FoamDocOpen().

Pass:

docHandle
Handle of the document to close, usually the one returned by FoamDocOpen().

Return: None.

Include: foam.goh

See Also:
FoamDocCreate(),
FoamDocDelete(),
FoamDocOpen().

FoamDocConvertFromDosAppend()

void FoamDocConvertFromDosAppend(
   optr     object,
   TCHAR *  buffer,
   word     bufferLength );

Append a buffer of DOS text to the temporary text object. The DOS text is converted to the GEOS character set; carriage return and Ctrl-Z are removed.

Pass:

object
Temporary text object to use for the conversion process. This is usually the optr returned by FoamDocConvertFromDosInit().
buffer
Pointer to the buffer of text to append.
bufferLength
Number of characters to append. Zero if buffer is null-terminated.

Return: None.

Include: foam.goh

See Also:
FoamDocConvertFromDosDone(),
FoamDocConvertFromDosInit(),
FoamDocConvertToDosDone(),
FoamDocConvertToDosInit(),
FoamDocConvertToDosRange(),
FoamDocGetDosTextSmall().

FoamDocConvertFromDosDone()

void   FoamDocConvertFromDosDone(
   DocumentHandle  docHandle,
   optr            object );

This function is called when you are done appending to the temporary text object. A text transfer item is created from the text object and stored in the specified document. The transfer item is stored in a VM block in the document, and its VMBlockHandle is stored in the document header (the document header is the VM file's map block; see FoamDocMapBlock in foam.goh for the structure of this map block) at the FDH_TEXT_TR_ITEM slot of the FoamDocHeader (see foam.goh for the definition of that enum).

The text object specified in "object" is then destroyed once the transfer is complete.

Pass:

docHandle
Temporary text object used in the conversion process. This is usually the optr returned by FoamDocConvertFromDosInit().

Return: None

Include: foam.goh

See Also:
FoamDocConvertFromDosAppend(),
FoamDocConvertFromDosInit(),
FoamDocConvertToDosDone(),
FoamDocConvertToDosInit(),
FoamDocConvertToDosRange(),
FoamDocGetDosTextSmall().

FoamDocConvertFromDosInit()

optr FoamDocConvertFromDosInit();

Initializes the DOS text file to Foam text document conversion. The function returns an optr of a temporary text object used in the conversion. The next step would be to use FoamDocConvertFromDosAppend to convert DOS text to GEOS text, then use FoamDocConvertFromDosDone to finish the conversion process.

Pass:

None

Return: Optr of the temporary text object to be used in the conversion process.

Include: foam.goh

See Also:
FoamDocConvertFromDosAppend(),
FoamDocConvertFromDosDone(),
FoamDocConvertToDosDone(),
FoamDocConvertToDosInit(),
FoamDocConvertToDosRange(),
FoamDocGetDosTextSmall().

FoamDocConvertToDosDone()

void    FoamDocConvertToDosDone(
   optr  object );

This function is called when you're done retrieving text from the temporary text object.

Pass:

object
Optr of the temporary text object used in the conversion process. This is usually the optr returned by FoamDocConvertToDosInit().

Return: None

Include: foam.goh

See Also: FoamDocConvertFromDosAppend(), FoamDocConvertFromDosDone, FoamDocConvertFromDosInit, FoamDocConvertToD

FoamDocCreate()

word FoamDocCreate(
   DocumentInfo *    docInfo,
   FileAttrInfo *    fileAttr,
   DocumentPrefix *  prefix );

This creates a Foam document file. It is simply created and then closed. You'll need to call FoamDocOpen() after using FoamDocCreate() in order to access the file.

The document that is created will have a default creator token of "RDOC", MANUFACTURER_ID_GEOWORKS. To change the document's creator token, call FoamDocSetAttrInfo() with FIA_token set to your application's GeodeToken.

Pass:

docInfo
Pointer to a DocumentInfo structure which contains the name of the document and the subdirectory it should go in under SP_DOCUMENT. Don't need DI_docName if prefix points to a name prefix since FoamDocCreate() will then make a default document name.
fileAttr
GeodeToken and ProtocolNumber to use for the document.
prefix
The prefix added to a document name. If null, no default name is made and name must be supplied in docInfo parameter; e.g. "NOTE 12.12.94 12:12 PM"

Return: Error value if something went wrong. This word is of type VMStatus, defined in vm.goh.

Include: foam.goh

See Also:
FoamDocClose(),
FoamDocDelete(),
FoamDocOpen().

FoamDocDelete()

Boolean FoamDocDelete(
   DocumentInfo *  docInfo );

This will delete a Foam document file, given a DocumentInfo struct. The document must be closed before you can delete it.

Pass:

docInfo
Pointer to a DocumentInfo struct, which specifies the file to be deleted.

typedef	struct {
    FileLongName DI_docName;  /* Document name */
    FDocumentDir DI_docDir;   /* Document directory */
} DocumentInfo;

typedef enum {
    FDD_PRIVATE,
    FDD_DOCUMENT,
    FDD_NONE,
    FDD_CREATED_TEXTS,
    FDD_RECEIVED_FAXES,
    FDD_RECEIVED_SM,
    FDD_RECEIVED_MAILS,
    FDD_MESSAGE_TEMPLATES,
    FDD_BUSINESS_CARDS,
    FDD_DOWNLOADED_FILES,
    FDD_WWW_HOTLIST,
    FDD_BROADCAST_MESSAGES,
    FDD_INTERNET,
    FDD_TONES
} FDocumentDir;

Return: TRUE if an error occurred, FALSE otherwise.

Include: foam.goh

See Also:
FoamDocClose(),
FoamDocCreate(),
FoamDocOpen().

FoamDocGetDosTextSmall()

word FoamDocGetDosTextSmall(
   DocumentHandle  docHandle,
   void *          buffer,
   word            count );

Returns the text stored in the text transfer item of the document in a character string format.

The text returned is converted from the GEOS character set to the native character set. Lines end with LF's and a NULL marks the end of text.

Pass:

docHandle
Handle of the Foam document from which to grab the text. This document must contain a text transfer item, similar to what is returned by FoamDocConvertFromDosDone().
buffer
Pointer to the buffer in which to put the DOS text.
count
The number of characters to convert. Use the FoamDocGetTextLength() routine to find the size of the Foam document.

Return: The number of characters converted.

Warning: Use this routine only if you know that the text in the document is less than a block in size (~10k).

Include: foam.goh

See Also:
FoamDocConvertFromDosAppend(),
FoamDocConvertFromDosDone(),
FoamDocConvertFromDosInit().

FoamDocGetHeaderData()

word FoamDocGetHeaderData(
   DocumentHandle  docHandle,
   FoamDocHeader   docHeader );

Retrieves the specified word of data from the Foam document's VM map block ("doc header"). The document header is simply a VMem map block. See the Concepts book for more information on Virtual Memory.

Pass:

docHandle
Handle of the Foam document from which to grab the header data.
docHeader
Specifies which word of data you want from the document header.
typedef enum {
    FDH_TEXT_TR_ITEM,
    FDH_ADDRESS_INFO,
    FDH_HANDLE1,
    FDH_HANDLE2,
    FDH_HANDLE3,
    FDH_HANDLE4,
    FDH_HANDLE5
} FoamDocHeader;

Return: The word of data from the document header.

Include: foam.goh

See Also: None.

FoamDocGetTextLength()

dword FoamDocGetTextLength(
   DocumentHandle  docHandle );

Returns the number of characters of text in the document.

Pass:

docHandle
Handle of the Foam document containing the text clipboard item.

Return: The length of the text in characters.

Include: foam.goh

See Also:
FoamDocGetDosSmall(),
FoamDocGetHeaderData(),
FoamDocGetTextLengthFromField().

FoamDocGetTextLengthFromField()

dword FoamDocGetTextLengthFromField(
   char *  field );

Returns the length of the text from a pointer to a field (User Notes). The pointer must be pointing to the "User notes" field of the file, as that is where the length of the text in the Foam document is stored.

Pass:

field
Pointer to the beginning of the "User notes" field.

Return: The length of the text in characters.

Include: foam.goh

See Also:
FoamDocGetDosSmall(),
FoamDocGetHeaderData(),
FoamDocGetTextLength().

FoamDocIsTextFormatted()

Boolean FoamDocIsTextFormatted(
   DocumentHandle      docHandle,
   DocumentFontInfo *  fontInfo );

This routine retrieves the FoamDocLengthAndFormatted structure from FEA_USER_NOTES and returns if the text of the document is formatted or not.

Pass:

docHandle
Handle of the Foam document containing a text clipboard item.
fontInfo
ointer to a DocumentFontInfo structure, which will be filled in with the font and point size of the unformatted text. Note that this structure is filled in only if the text is of a single point size and font id (i.e. FoamDocIsTextFormatted returns FALSE).
typedef struct {
    FontID	DFI_font;
    word	DFI_pointSize;
} DocumentFontInfo;

Return: TRUE if the text is formatted, FALSE otherwise. If FALSE, then fontInfo struture will be filled in with point size and font id of the text.

Include: foam.goh

See Also: FoamIsTextFormatted().

FoamDocOpen()

DocumentHandle FoamDocOpen(
   DocumentInfo *  docInfo,
   FDocAccess      docAccess );

Opens a Foam document and returns a file handle. If the return value is NullHandle, use ThreadGetError() to check for an error value.

Pass:

docInfo
Pointer to a DocumentInfo structure which contains the name of the document and the subdirectory it lives in under SP_DOCUMENT. The structure for DocumentInfo is defined as follows:
typedef	struct {
    FileLongName DI_docName;  /* Document name */
    FDocumentDir DI_docDir;   /* Document directory */
} DocumentInfo;

typedef enum {
    FDD_PRIVATE,
    FDD_DOCUMENT,
    FDD_NONE,
    FDD_CREATED_TEXTS,
    FDD_RECEIVED_FAXES,
    FDD_RECEIVED_SM,
    FDD_RECEIVED_MAILS,
    FDD_MESSAGE_TEMPLATES,
    FDD_BUSINESS_CARDS,
    FDD_DOWNLOADED_FILES,
    FDD_WWW_HOTLIST,
    FDD_BROADCAST_MESSAGES,
    FDD_INTERNET,
    FDD_TONES
} FDocumentDir;
docAccess
Indicates how to open the file - read only or read/write. Possible values are:
  • FDA_READ_WRITE
  • FDA_READ_ONLY
  • FDA_READ_ONLY_DENY_NONE

Return: Handle of the newly open document, or NullHandle if an error occurred.

Include: foam.goh

See Also:
FoamDocClose(),
FoamDocCreate(),
FoamDocDelete().

FoamDocSetAttrInfo

void FoamDocSetAttrInfo(
   DocumentHandle  docHandle,
   FileAttrInfo *  fileAttrInfo );

Initialize a file's extended attributes. This is done to allow detection of when a file that is being opened is actually a specific file type or not. Also, when major changes are made to the file structure we can change the protocol number to see if the file that we opened is compatible.

Pass:

docHandle
Handle of the FoamDocument being initialized.
fileAttrInfo
Pointer to a FileAttrInfo structure that will be used to set the files attributes. The structure definition is as follows:
typedef struct {
    GeodeToken	   FAI_token;     /* token of file */
    ProtocolNumber FAI_protocol;  /* protocol number of file */
} FileAttrInfo;

Return: None.

Include: foam.goh

See Also:
FoamDocSetBlockHandle(),
FoamDocSetHeaderData(),
FoamDocSetTextFormatted(),
FoamDocSetTextLength().

FoamDocSetBlockHandle()

void FoamDocSetBlockHandle(
   DocumentHandle  docHandle,
   VMBlockHandle   block,
   FoamDocHeader   docHeader );

Stores the handle of block in a field within the document's header block. The field is specified by docHeader. If the field already contains a VMBlock handle, that block will be freed by the system and your block handle will be placed in the field.

If the data you want to store in the document header (which is a FoamDocMapBlock structure, defined in foam.goh) isn't a VM handle, then use FoamDocSetHeaderData() instead.

Pass:

docHandle
Handle of the Foam document file which is being changed.
block
The VMBlockHandle being saved to the document header.
docHeader
Field within the document header where the block handle will be stored. FoamDocHeader is defined as:
typedef enum {
    FDH_TEXT_TR_ITEM,
    FDH_ADDRESS_INFO,
    FDH_HANDLE1,
    FDH_HANDLE2,
    FDH_HANDLE3,
    FDH_HANDLE4,
    FDH_HANDLE5
} FoamDocHeader;

Return: None.

Warning: Do not set docHeader to FDH_TEXT_TR_ITEM or FDH_ADDRESS_INFO as these two fields are used by the system.

Include: foam.goh

See Also:
FoamDocSetAttrInfo(),
FoamDocSetHeaderData(),
FoamDocSetTextFormatted(),
FoamDocSetTextLength().

FoamDocSetHeaderData()

void FoamDocSetHeaderData(
   DocumentHandle  docHandle,
   word            data,
   FoamDocHeader   docHeader );

Stores a word of data in a field within the specified document's header block. The field is specified by docHeader.

If the data you want to store in the document header is a VMBlock handle, use FoamDocSetBlockHandle() instead.

Pass:

docHandle
Handle of the Foam document that is being changed.
data
The word of data to store in the document header.
docHeader
Field within the document header where the "block" handle will be stored. FoamDocHeader is defined as follows.
typedef enum {
    FDH_TEXT_TR_ITEM,
    FDH_ADDRESS_INFO,
    FDH_HANDLE1,
    FDH_HANDLE2,
    FDH_HANDLE3,
    FDH_HANDLE4,
    FDH_HANDLE5
} FoamDocHeader;

Return: None

Warning: Do not set docHeader to FDH_TEXT_TR_ITEM or FDH_ADDRESS_INFO as these two fields are used by the system.

Include: foam.goh

See Also:
FoamDocSetAttrInfo(),
FoamDocSetBlockHandle(), FoamDocSetTextFormatted(),
FoamDocSetTextLength().

FoamDocSetTextFormatted()

void FoamDocSetTextFormatted(
   DocumentHandle        docHandle,
   FoamDocTextFormatted  fdtf,
   DocumentFontInfo *    fontInfo );

Stores information on the document text in the GeosFileHeader of the Document. This information can be retrieved by calling FoamDocIsTextFormatted().

Pass:

docHandle
Handle of the Foam document which is being changed.
fdtf
Set this to FDTF_FORMATTED if the text is formatted, otherwise set to FDTF_NOT_FORMATTED.
fontInfo
Pointer to a DocumentFontInfo structure. If the text does not contain multiple formats, then pass the font and point size of the text. See the description for FoamDocIsTextFormatted() for the definition of DocumentFontInfo.

Return: None.

Include: foam.goh

See Also:
FoamDocIsTextFormatted(),
FoamDocSetAttrInfo(),
FoamDocSetBlockHandle(),
FoamDocSetHeaderData(),
FoamDocSetTextLength().

FoamDocSetTextLength()

void FoamDocSetTextLength(
   DocumentHandle  docHandle,
   dword           length );

Sets the document's text length. This is usually used internally and you won't need to call this in most cases. This information is stored in the Foam document header and does not affect the text stored in the document.

Pass:

docHandle
Handle of the Foam document which is being changed.
length
Length that you want to store in the document header.

Return: None.

Include: foam.goh

See Also:
FoamDocSetAttrInfo(),
FoamDocSetBlockHandle(),
FoamDocSetHeaderData(),
FoamDocSetTextFormatted().

FoamDocStoreDosTextSmall()

void FoamDocStoreDosTextSmall(
   DocumentHandle  docHandle,
   void *          buffer,
   word            bufferLen );

Stores a character string in the document FDH_TEXT_TR_ITEM block. The text is converted from the native character set to the GEOS character set. Carriage return and Ctrl-Z are removed from the string.

Pass:

docHandle
Handle of the Foam document which is being changed.
buffer
Pointer to the string of text to store. Must be null-terminated.
bufferLen
Number of characters in the string pointed to by buffer.

Return: None.

Warning: Use this only if the text you are storing is less than a block in size (~10k). If the DOS text you want to store is larger than 10k, use the FoamDocConvertFromDos() routines listed below.

Include: foam.goh

See Also:
FoamDocConvertFromDosAppend(),
FoamDocConvertFromDosDone(),
FoamDocConvertFromDosInit(),
FoamDocStoreTextAsVMChain().

FoamDocStoreTextAsVMChain()

word FoamDocStoreTextAsVMChain(
   DocumentHandle  docHandle,
   optr            textObject );

This routine copies the text of a text object into a chain of text transfer items, each about 4000 bytes in size. The starting block of the chain is saved in FDH_TEXT_TR_ITEM of the document header. The next transfer item block handle is saved in the TTBH_reservedVM field of the TextTransferBlockHeader.

When it's time to read in the chain of text transfer items, first use the block in FDH_TEXT_TR_ITEM with MSG_VIS_TEXT_REPLACE_WITH_TEXT_TRANSFER_FORMAT then loop until the high word of TTBH_reservedVM is zero. To get at TTBH_reservedVM use VMLock() passing the the VMBlockHandle you used when calling MSG_VIS_TEXT_REPLACE_WITH_TEXT_TRANSFER_FORMAT.

Pass:

docHandle
Handle of the Foam document which is being changed.
textObject
Optr to the text object containing the text to store in the Foam document.

Return: VMBlockHandle of the first text transfer item. (This handle is also stored in FDH_TEXT_TR_ITEM.)

Include: foam.goh

See Also: FoamDocStoreDosTextSmall().

FoamGenerateUniqueFilename()

void FoamGenerateUniqueFilename(
   FileLongName *nameBuffer);

This routine will return a data file name that is unique. If you specify a prefix in nameBuffer, this routine will add the next available number to the prefix and return the document name in the form: "[prefix] #[number]".

Pass:

nameBuffer
Buffer into which the file name will be written.

Return: None.

Warning: Occasionally an invalid file name can be returned. If you try to create a file with the returned name and the create fails, call this routine again to get a new name.

Include: foam.goh

See Also: FoamDocCreate().

FoamGetDocumentDir()

FDocumentDir FoamGetDocumentDir();

Returns the FDocumentDir value that corresponds to the current path. If the current path is not one of the FDocumentDir, then FDD_NONE is returned. FDocumentDir is a list of standard subdirectories under the Document directory.

Pass: None.

Return: The appropriate FDocumentDir value.

Include: foam.goh

See Also: FoamGetDocumentDirName(), FoamSetDocumentDir().

FoamGetDocumentDirName()

void FoamGetDocumentDirName(
   FDocumentDir fDocDir,
   FileLongName *buffer);

Fills in the buffer with the directory name corresponding to the specified FDocumentDir. FDocumentDir is a list of standard subdirectories under the Document directory.

Pass:

fDocDir
The FDocumentDir enum whose name you want.
buffer
A buffer that will hold the name of the directory.

Return: None; *buffer is filled in.

Include: foam.goh

See Also: FoamGetDocumentDir(), FoamSetDocumentDir().

FoamGetFileType()

GeosFileType FoamGetFileType(
   TCHAR *filename);

Get the GeosFileType of the named file.

Pass:

filename
Name of the file whose GeosFileType you want to retrieve. The buffer need not be longer than FILE_LONGNAME_BUFFER_SIZE bytes.

Return: A GeosFileType will be returned. The value will be

  • GFT_VM (standard GEOS VM file)
  • GFT_DATA (a non-VM data file)
  • GFT_EXECUTABLE (file is a application, library or driver)
  • GFT_DIRECTORY (if the name was a directory rather than a file)
  • GFT_NOT_GEOS_FILE (the file was most likely created by DOS)

Include: foam.goh

See Also: FoamGetHandleFileType().

FoamGetFreeDiskSpace()

dword FoamGetFreeDiskSpace();

This routine returns the number of bytes available on the disk.

Pass: None.

Return: The number of free bytes on the disk.

Warning: Since Geos is multi-threaded, the amount of free disk space may change rapidly as other threads use the disk..

Include: foam.goh

See Also:
FoamRoundUpAsMultipleOfClusterSize(),
FoamCheckIfOutOfSpace(),
FoamWarnIfOutOfSpace(),
FoamWarnForSaving(),
FoamCancelIfNoFreeSpace(),
FoamWarnIfNotEnoughSpace(),
FoamWarnSpaceAfterOperation().

FoamGetHandleFileType()

GeosFileType FoamGetHandleFileType(
   DocumentHandle docHandle);

Get the GeosFileType of the file specified by docHandle.

Pass:

docHandle
DocumentHandle of the file whose GeosFileType you want to retrieve.

Return: A GeosFileType will be returned. The value will be

  • GFT_VM (standard GEOS VM file)
  • GFT_DATA (a non-VM data file)
  • GFT_EXECUTABLE (file is a application, library or driver)
  • GFT_DIRECTORY (if the name was a directory rather than a file)
  • GFT_NOT_GEOS_FILE (the file was most likely created by DOS)

Include: foam.goh

See Also: FoamGetFileType().

FoamGetLexicalOrder()

byte FoamGetLexicalOrder(
   char theChar);

Gets the "lexical order" of a character, for use in optimized case and accent-insensitive searching (all versions of a character, accented, unaccented, upper and lower case will have the same lexical order).

Pass:

theChar
The character whose lexical equivalent you want.

Return: A LexicalOrder for the character is returned. See LexicalOrder enumeration in Include/Internal/lexical.def.

Include: foam.goh

See Also: None.

FoamGetLowSoundFlag()

word FoamGetLowSoundFlag();

Returns the status of the low sound mode.

Low sound mode specifies that UserStandardSound() will ignore the standard sound type it is told to play. Instead it plays a low, monotone note.

Pass: None.

Return: Non-zero (TRUE) if the flag is set; zero (FALSE) if the flag is not set.

Include: foam.goh

See Also: UserStandardSound(), FoamSetLowSoundFlag().

FoamIsTextFormatted()

Boolean FoamIsTextFormatted(
   optr             textObject,
   DocumentFontInfo *fontInfo);

Determines if text in a text object is formatted (does it have multiple character attributes). If the text is not formatted the function will return the font and the point size of the text in *fontInfo.

Pass:

textObject
optr to the text object to examine.
fontInfo
buffer that will be filled in if the text isn't in multiple formats.

Return: TRUE if the text is formatted, FALSE if text is all in a single format. If FALSE, then *fontInfo will be filled in.

Include: foam.goh

See Also: FoamDocIsTextFormatted().

FoamLaunchApplication()

void FoamLaunchApplication(
   FApplication applicationConstant);

Launch the application designated by applicationConstant. This is intended to be used by applications launched by System, Extras, Services so that when they quit (rather, lower to bottom), the original launcher appears.

When this routine is called and the designated application launched, the current application is lowered to bottom.

Pass:

applicationConstant
One of the following FApplication types:
typedef enum {
    FA_PHONE_APP,
    FA_FAX_APP,
    FA_SMS_APP,
    FA_SERVICES_APP,
    FA_CONTACT_APP,
    FA_NOTEBOOK_APP,
    FA_CALENDAR_APP,
    FA_SYSTEM_APP,
    FA_EXTRAS_APP,
    FA_CALCULATOR_APP,
    FA_CLOCK_APP,
    FA_USER_DATA_APP,
    FA_WWW_INSTALL_APP,
} FApplication;

Return: None.

Include: foam.goh

See Also: UserLoadApplication(), IACPConnect().

FoamRoundUpAsMultipleOfClusterSize()

dword FoamRoundUpAsMultipleOfClusterSize(
   dword space);

Since files require a full cluster on the disk, whether they actually fill the entire cluster or not, this routine allows you to calculate the actual disk space a file will require. Simply submit the requested space and the routine will return a size that is a multiple of cluster sizes. This returned size will never be less than the requested space.

FoamWarnForSaving() and FoamWarnIfNotEnoughSpace() use this routine.

Pass:

space
The number of bytes of disk space you want.

Return: The number of bytes of disk space that will actually be used by a file of the specified size.

Include: foam.goh

See Also: None.

FoamSetDocumentDir()

void FoamSetDocumentDir(
   FDocumentDir fDocDir);

Sets the current path to one of the standard FDocumentDir directories.

Pass:

fDocDir
The desired FDocumentDir enum.

Return: None.

Include: foam.goh

See Also:
FileSetStandardPath(),
FileSetCurrentPath(),
FoamGetDocumentDir(),
FoamGetDocumentDirName().

FoamSetLowSoundFlag()

void FoamSetLowSoundFlag(
   word lowSndFlag);

This routine changes the sound played by UserStandardSound(). If the lowSndFlag is set to TRUE, UserStandardSound() will play one plain note no matter what StandardSoundType is passed.

Pass:

lowSndFlag
Set to TRUE to enable low sound mode. Set to FALSE to disable low sound mode (i.e., revert to standard sound mode).

Return: None.

Include: foam.goh

See Also: UserStandardSound(), FoamGetLowSoundFlag().

FoamStandardDialogOptr()

InteractionCommand FoamStandardDialogOptr(
   void                     *helpFile,
   void                     *helpContext,
   LayerPriority            layerPriority,
   word                     rejectTriggerMessage,
   optr                     rejectTriggerDestination,
   word                     acceptTriggerMessage,
   optr                     acceptTriggerDestination,
   optr                     triggerTopText,
   TCHAR                    *arg2,
   TCHAR                    *arg1,
   FoamCustomDialogBoxFlags dialogFlags,
   optr                     bodyText,
   optr                     titleText,
   optr                     titleIconBitmap);

This routine puts up a custom dialog box. It does not return until the user responds to the dialog box. The dialog is destroyed by the system when the routine returns.

Pass:

helpFile
The name of the helpfile to associate with this dialog. The helpfile must be in the Userdata\Help directory on the device.
helpContext
The context within help file to associate with this dialog.
layerPriority
Custom layer priority to use. Set to zero to give standard layer priority to the dialog.
rejectTriggerMessage
This parameter is not used.
rejectTriggerDestination
This parameter is not used.
acceptTriggerMessage
This parameter is not used.
acceptTriggerDestination
This parameter is not used.
triggerTopText
This text will be used in the standard affirmative response trigger instead of the default text. If triggerTopText is NULL the default text will be used. Setting triggerTopText is only valid for GIT_PROPERTIES and GIT_NOTIFICATION type dialogs. Note that the trigger will still return IC_APPLY for GIT_PROPERTIES and IC_OK or GIT_NOTIFICAITON.
arg2
Second string argument. Each '\002' (0x02) in the bodyText string will be replaced by *arg2.
arg1
First string argument. Each '\001' (0x01) in the bodyText string will be replaced by *arg1.
dialogFlags
Defines various functionality of the dialog box. Here are the valid flags.
  • FCDBF_SYSTEM_MODAL: Set if the dialog box should be modal.
  • FCDBF_DIALOG_TYPE: One of the CustomDialogTypes. This allows you to define the default title icon and text of the dialog as a CDT_QUESTION, CDT_NOTIFICATION, CDT_WARNING, or CDT_ERROR. The offset of FCDBF_DIALOG_TYPE is defined as FCDBF_DIALOG_TYPE_OFFSET. This allows you to easily set the dialog type like this:
             (customDialogType << FCDBF_DIALOG_TYPE_OFFSET)
  • FCDBF_INTERACTION_TYPE: Defines the standard response trigger to give to this dialog. The following GenInteractionTypes may be used: GIT_PROPERTIES, GIT_PROGRESS, GIT_COMMAND, GIT_NOTIFICATION, or GIT_AFFIRMATION. Example of setting the FCDBF_INTERACTION_TYPE flag:
             (genIntType << FCDBF_INTERACTION_TYPE_OFFSET)
  • FCDBF_DESTRUCTIVE_ACTION: This flag signals that the affirmative response trigger (IC_YES) cannot have the default focus. Usually, this is used for dialogs that have some destructive action on an affirmative response.
bodyText
An optr to a null-terminated string containing the body of the main text in the dialog. This optr cannot be NULL; it must reference an actual block of text.
titleText
An optr to a null-terminated string containing the text for the title bar. If titleText is NULL, the system will choose a standard text title depending on the CustomDialogType specified in the FCDBF_DIALOG_TYPE field of the dialogFlags parameter.
titleIconBitmap
An optr to a Bitmap struct (see graphics.h) that contains the icon for the dialogs' title bar. If titleIconBitmap is NULL, then the system will choose a standard dialog icon depending on the CustomDialogType specified in FCDBF_DIALOG_TYPE field of the dialogFlags parameter.

Return: The response trigger chosen by the user. This value is in the form of an InteractionCommand corresponding to that trigger.

Warning: This routine may resize LMem- and/or object blocks, moving them on the heap and invalidating pointers to those blocks.

Include: foam.goh

See Also:
FoamStandardDialogOptrNoBlock(),
FoamDisplayDeleteWarning(),
FoamDisplayDeleteWarningNoBlock(),
FoamDisplayError(),
FoamDisplayErrorNoBlock(),
FoamDisplayNote(),
FoamDisplayNoteNoBlock(),
FoamDisplayOkCancelWarning(),
FoamDisplayQuestion(),
FoamDisplayWarning(),
FoamDisplayWarningNoBlock().

FoamStandardDialogOptrNoBlock()

optr FoamStandardDialogOptrNoBlock(
   void                     *helpFile,
   void                     *helpContext,
   LayerPriority            layerPriority,
   word                     rejectTriggerMessage,
   optr                     rejectTriggerDestination,
   word                     acceptTriggerMessage,
   optr                     acceptTriggerDestination,
   optr                     triggerTopText,
   TCHAR                    *arg2,
   TCHAR                    *arg1,
   FoamCustomDialogBoxFlags dialogFlags,
   optr                     bodyText,
   optr                     titleText,
   optr                     titleIconBitmap);

This routine puts up a custom dialog box and returns so the thread is not blocked. The dialog is destroyed by the system after the user responds to it.

Pass:

helpFile
The name of the helpfile to associate with this dialog. The helpfile must be in the Userdata\Help directory on the device.
helpContext
The context within help file to associate with this dialog.
layerPriority
Custom layer priority to use. Set to zero to give standard layer priority to the dialog.
rejectTriggerMessage
Extra message to send when a "reject" trigger (IC_DISMISS, IC_STOP, IC_NO) is pressed. Make sure to set rejectTriggerDestination to a valid object.
rejectTriggerDestination
Optr of object to send rejectTriggerMessage to when a "reject" trigger is pressed. Make sure to set rejectTriggerMessage to a valid Message.
acceptTriggerMessage
Extra message to send when an "accept" trigger (IC_OK, IC_YES, IC_APPLY) is pressed. Make sure to set acceptTriggerDestination to a valid object.
acceptTriggerDestination
Optr of object to send acceptTriggerMessage to when an "accept" trigger is pressed. Make sure to set acceptTriggerMessage to a valid Message.
triggerTopText
This text will be used in the standard affirmative response trigger instead of the default text. If triggerTopText is NULL the default text will be used. Setting triggerTopText is only valid for GIT_PROPERTIES and GIT_NOTIFICATION type dialogs.
arg2
Second string argument. Each '\002' (0x02) in the bodyText string will be replaced by *arg2.
arg1
First string argument. Each '\001' (0x01) in the bodyText string will be replaced by *arg1.
dialogFlags
Defines various functionality of the dialog box. Here are the valid flags.
  • FCDBF_SYSTEM_MODAL: Set if the dialog box should be modal.
  • FCDBF_DIALOG_TYPE: One of the CustomDialogTypes. This allows you to define the default title icon and text of the dialog as a CDT_QUESTION, CDT_NOTIFICATION, CDT_WARNING, or CDT_ERROR. The offset of FCDBF_DIALOG_TYPE is defined as FCDBF_DIALOG_TYPE_OFFSET. This allows you to easily set the dialog type like this:
             (customDialogType << FCDBF_DIALOG_TYPE_OFFSET)
  • FCDBF_INTERACTION_TYPE: Defines the standard response trigger to give to this dialog. The following GenInteractionTypes may be used: GIT_PROPERTIES, GIT_PROGRESS, GIT_COMMAND, GIT_NOTIFICATION, or GIT_AFFIRMATION. Example of setting the FCDBF_INTERACTION_TYPE flag:
             (genIntType << FCDBF_INTERACTION_TYPE_OFFSET)
  • FCDBF_DESTRUCTIVE_ACTION: This flag signals that the affirmative response trigger (IC_YES) cannot have the default focus. Usually, this is used for dialogs that have some destructive action on an affirmative response.
bodyText
An optr to a null-terminated string containing the body of the main text in the dialog. This optr cannot be NULL; it must be a valid optr.
titleText
An optr to a null-terminated string containing the text for the title bar. If titleText is NULL, the system will choose a standard text title depending on the CustomDialogType specified in the FCDBF_DIALOG_TYPE field of the dialogFlags parameter.
titleIconBitmap
An optr to a Bitmap struct (see graphics.h) that contains the icon for the dialogs' title bar. If titleIconBitmap is NULL, then the system will choose a standard dialog icon depending on the CustomDialogType specified in FCDBF_DIALOG_TYPE field of the dialogFlags parameter.

Return: The optr to the dialog box. This optr is only valid while the dialog box is up. Once the user responds (by hitting a response trigger), the dialog is destroyed, making the optr invalid.

Warning: This routine may resize LMem- and/or object blocks, moving them on the heap and invalidating pointers to those blocks.

Include: foam.goh

See Also:
FoamStandardDialogOptr(),
FoamDisplayDeleteWarning(),
FoamDisplayDeleteWarningNoBlock(),
FoamDisplayError(),
FoamDisplayErrorNoBlock(),
FoamDisplayNote(),
FoamDisplayNoteNoBlock(),
FoamDisplayOkCancelWarning(),
FoamDisplayQuestion(),
FoamDisplayWarning(),
FoamDisplayWarningNoBlock().

FoamStartSoundSequence()

void FoamStartSoundSequence(
   StandardSoundType sound,
   word              duration,
   word              interval);

This routine is typically used to play an alarm type sound. It starts a sequence of sound that plays every interval ticks for duration ticks. If a sequence is currently sounding, the routine will not play your sequence; it will continue playing the current sequence.

Pass:

sound
The standard sound type to play. FOAM_FCLOCK_ALARM_SOUND may be used to play the clock alarm.
duration
The number of ticks at which to stop sounding the alarm.
interval
The number of ticks to pause between playing the sound.

Return: None.

Warning:

  • If another alarm is already playing, your alarm will not sound.
  • If low sound mode has been set, your alarm will play the low level sound instead of the StandardSoundType.
  • If you call FoamStartSoundSequence() and bring up a dialog, you should intercept MSG_META_KBD_CHAR for the dialog and call FoamStopSoundSequence() unless you use FoamStandardDialogOptr(), FoamStandardDialogOptrNoBlock(), or StandardDialogClass.

Include: foam.goh

See Also: FoamStopSoundSequence().

FoamStopSoundSequence()

void FoamStopSoundSequence();

Stop any alarm that was started by FoamStartSoundSequence().

Pass: None.

Return: None.

Include: foam.goh

See Also: FoamStartSoundSequence().

FoamUTOffset()

signed int FoamUTOffset();

Return the difference between the current home city (in the .ini file) and GMT (or universal time).

Pass: None.

Return: The number of minutes from GMT.

Include: foam.goh

See Also: None.

FoamWarnForSaving()

FoamSaveAction FoamWarnForSaving(
   dword  space );

Puts up a dialog box to warn the user of low disk space, but only if the disk space is approaching full. This routine should be used by any editable view (e.g., Contact Card, Notes editor).

Pass:

space
Disk space required, in bytes.

Return: The action you should take. Possible values are:

  • FSA_SAVE_DATA
  • FSA_THROW_AWAY_CHANGES
  • FSA_DO_NOTHING

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored segment pointers to them.

Include: foam.goh

See Also:
FoamWarnIfNotEnoughSpace(),
FoamWarnIfOutOfSpace(),
FoamWarnSpaceAfterOperation().

FoamWarnIfNotEnoughSpace()

FoamDiskSpaceStatus FoamWarnIfNotEnoughSpace(
   dword  space );

Puts up the appropiate dialogs depending on the disk space available. Should be called before performing a specific operation that may consume disk space. (e.g., receiving a fax, receiving mail, reading/fetching mail, downloading WWW files, printing).

Pass:

space
Disk space required, in bytes.

Return: How close to full the disk space will be. Possible values are:

  • FDSS_NOT_FULL (disk space > 50K)
  • FDSS_NEARLY_FULL (25K < disk space < 50K)
  • FDSS_FULL (disk space < 25K)

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored segment pointers to them.

Include: foam.goh

See Also:
FoamWarnForSaving(),
FoamWarnIfOutOfSpace(),
FoamWarnSpaceAfterOperation().

FoamWarnIfOutOfSpace()

Boolean FoamWarnIfOutOfSpace();

Puts up a warning dialog box depending on how much free disk space is available. This routine is used mainly by the system to check space after system booted/cover opened/system unlocked. This routine calls FoamCheckIfOutOfSpace() to determine the amount of space left.

Pass: None.

Return: FALSE if space is okay, TRUE if out of space.

Warning: This routine may resize LMem and/or object blocks, moving them on the heap and invalidating stored segment pointers to them.

Include: foam.goh

See Also:
FoamWarnForSaving(),
FoamWarnIfNotEnoughSpace(),
FoamWarnSpaceAfterOperation().

FoamWarnSpaceAfterOperation()

FoamDiskSpaceStatus FoamWarnSpaceAfterOperation();

Returns the available disk space status level. This should be called after executing an operation that has consumed disk space.

Pass: None.

Return: Status indicating the amount of available disk space. Possible values are:

  • FDSS_NOT_FULL (disk space > 50K)
  • FDSS_NEARLY_FULL (25K < disk space < 50K)
  • FDSS_FULL (disk space < 25K)

Include: foam.goh

See Also:
FoamWarnForSaving(),
FoamWarnIfNotEnoughSpace(),
FoamWarnIfOutOfSpace().

FormatIDFromManufacturerAndType

dword	FormatIDFromManufacturerAndType(mfr, type);
        ManufacturerIDs 			mfr;
        ClipboardItemFormat 			type;

This macro takes a manufacturer ID and a format type (e.g. CIF_TEXT) and combines them into a dword argument of the type ClipboardItemFormatID .

free()

void 	free(
        void * blockPtr);				/* address of memory to free */

The malloc() family of routines is provided for Standard C compatibility. The kernel will allocate a fixed block to satisfy the geode's malloc() requests; it will allocate memory from this block. When the block is filled, it will allocate another fixed malloc-block. When all the memory in the block is freed, the memory manager will automatically free the block.

When a geode is finished with some memory it requested from malloc() , it should free the memory. That makes it easier for malloc() to satisfy memory request. It can free the memory by passing the address which was returned by malloc() (or calloc() or realloc() ) when the memory was allocated. All of the memory will be freed.

The memory must be in a malloc-block assigned to the geode calling free() . If you want to free memory in another geode's malloc-block, call GeoFree() .

Include: stdlib.h

Warnings: Pass exactly the same address as the one returned to you when you allocated the memory. If you pass a different address, free() will take unpredictable actions, including possibly erasing other memory or crashing the system.

See Also: calloc(),
malloc(),
GeoFree(),
realloc().

FractionOf()

word	FractionOf(
        WWFixedAsDWord		wwf);

This macro returns the fractional portion of a WWFixedAsDWord value.

Include: geos.h

GCNListAdd()

Boolean	GCNListAdd(
        optr		OD,				/* optr to add to list */
        ManufacturerID		manufID,				/* manufacturer ID of list */
        word		listType);				/* list type */

This routine adds an object pointer (optr) to a GCN list interested in a particular change. The routine must be passed the optr to add, along with the manufID and the type of the list to add it to. If no list of the specified manufacturer and type currently exists, a new list will be created.

This routine will return true if the optr was successfully added to the GCN list and false if the optr could not be added. An optr cannot be added to a GCN list if it currently exists on that list.

Include: gcnlist.goh

GCNListAddHandles()

Boolean	GCNListAddHandles(
        MemHandle		mh,				/* handle of object to add */
        ChunkHandle		ch,				/* chunk of object to add */
        ManufacturerIDs		manufID,				/* manufacturer ID of list */
        word		listType);				/* list type */

This routine is exactly the same as GCNListAdd() , except it takes the memory and chunk handles of the object rather than a complete optr.

Include: gcnlist.goh

GCNListAddToBlock()

Boolean	GCNListAddToBlock(
        optr		OD,				/* optr of list to add */
        ManufacturerID		manufID,				/* manufacturer ID of list */
        word		listType,				/* list type */
        MemHandle		mh,				/* handle of block holding list */
        ChunkHandle		listOfLists);				/* chunk of list of lists
                             * in block */

This routine adds a new GCN list to a block containing the GCN lists. Pass it the optr of the chunk containing the new GCN list as well as the list's type and manufacturer ID. Pass also the memory handle and chunk handle of the chunk containing the GCN "list of lists" which will manage the new list.

This routine returns true of the new optr is added to the GCN mechanism, false if it could not be added (if it was already there).

Warnings: This routine may resize chunks in the block, so you should dereference any pointers after calling this routine.

Include: gcnlist.goh

GCNListCreateBlock()

ChunkHandle GCNListCreateBlock(
        MemHandle mh);			/* handle of the locked LMem block */

This routine creates a list of lists for the GCN mechanism. It is rarely, if ever, called by applications. Pass it the handle of the locked LMem block in which the list should be created.

Include: gcnlist.goh

GCNListDestroyBlock()

void	GCNListDestroyBlock(
        MemHandle		mh,					/* handle of locked block to
                             * be destroyed */
        ChunkHandle		listOfLists);					/* chunk of list of lists */

This routine destroys a GCN list of lists and all the GCN lists associated with it. Pass it the handle of the locked LMem block containing the lists as well as the chunk handle of the chunk containing the list of lists.

Include: gcnlist.goh

GCNListDestroyList()

void	GCNListDestroyList(
        optr	list);		/* optr of the GCN list to be destroyed */

This routine destroys the specified GCN list.

Include: gcnlist.goh

GCNListRelocateBlock()

void	GCNListRelocateBlock(
        MemHandle		mh,					/* handle of locked LMem block
                             * containing GCN lists */
        ChunkHandle		listOfLists,					/* chunk of list of lists */
        MemHandle		relocBlock);					/* handle of block containing
                             * relocation information */

This routine relocates the GCN list of lists in the specified block, updating all the optrs stored therein.

Warnings: This routine can resize and/or move the LMem block, so you should dereference pointers after calling it.

Include: gcnlist.goh

GCNListRemove()

Boolean	GCNListRemove(
        optr		OD,				/* the optr to be removed */
        ManufacturerID		manufID,				/* manufacturer ID of the list */
        word		listType);				/* list type */

This routine removes the passed optr from the specified GCN list. The routine must be passed the optr to remove along with the manufacturer ID and list type of the list to remove it from.

This routine will return true if the optr was successfully removed from the GCN list and false if the optr could not be found on the GCN list and therefore could not be removed.

Include: gcnlist.goh

GCNListRemoveFromBlock()

Boolean	GCNListRemoveFromBlock(
        optr		OD,				/* optr of GCN list to remove */
        ManufacturerID		manufID,				/* manufacturer of list to remove */
        word		listType,				/* type of list being removed */
        MemHandle		mh,				/* handle of locked LMem block
                         * containing the list of lists */
        ChunkHandle		listOfLists);				/* chunk of list of lists */

This routine removes a GCN list from a GCN list block and from the list of lists therein.

Include: gcnlist.goh

GCNListRemoveHandles()

Boolean	GCNListRemoveHandles(
        MemHandle		mh,
        ChunkHandle		ch,
        ManufacturerID		manufID,
        word		listType);

This routine is exactly the same as GCNListRemove() , except it specifies the object to be removed via handles rather than an optr.

Include: gcnlist.goh

See Also: GCNListRemove().

GCNListSend()

word	GCNListSend(
        ManufacturerID		manufID,						/* manufacturer of list */
        word		listType,						/* notification type */
        EventHandle		event,						/* event to be sent to list */
        MemHandle		dataBlock,						/* data block, if any */
        word		gcnListSendFlags);						/* GCNListSendFlags */

This routine sends a message to all objects in the specified GCN list. The message is specified in event , and the list is specified in manufID and listType . The message will be sent asynchronously (some time after the change has occurred) by the message queue.

The dataBlock parameter contains the memory handle of an extra data block to be sent with the notification, if any; this block should also be specified in the classed event. If no data block is required, pass a NullHandle. If a data block with a reference cound is used, increment the reference count by one before calling this routine; this routine decrements the count and frees the block if the count reaches zero.

The gcnListSendFlags parameter is of type GCNListSendFlags , which has only one meaningful flag for this routine:

GCNLSF_SET_STATUS
Causes the message sent to the GCN list to be set as the lists "status." The list's status message is then sent to any object adding itself to the list at a later time. If this flag is set, the event handle in event will be returned by the routine. If this flag is not set, the return value will be the number of messages sent out.

Include: gcnlist.goh

GCNListSendToBlock()

word	GCNListSendToBlock(
        ManufacturerID		manufID,				/* manufacturer id of list */
        word		listType,				/* notification type */
        EventHandle		event,				/* event to be sent to list */
        MemHandle		dataBlock,				/* data block, if any */
        MemHandle		mh,				/* handle of locked LMem block
                         * containing GCN list of lists */
        ChunkHandle		listOfLists,				/* chunk of list of lists */
        GCNListSendFlags		flags);				/* GCNListSendFlags */

This routine sends the specified event to the specified list, just as GCNListSend() . GCNListSendToBlock() , however, specifies a particular instance of the GCN list by specifying the appropriate list of lists in mh and listOfLists . Other parameters and return values are identical to GCNListSend() .

See Also: GCNListSend().

Include: gcnlist.goh

GCNListSendToList()

void	GCNListSendToList(
        optr		list,				/* optr of GCN list */
        EventHandle		event,				/* event to send to list */
        MemHandle		dataBlock,				/* handle of data block, if any */
        GCNListSendFlags		flags);				/* GCNListSendFlags */

This routine sends the specified event to the specified GCN list . The list is specified explicitly by optr as opposed to by manufacturer ID and type. The event will be sent via the proper queues to all objects registered on the list. After the notification is handled by all notified objects, the event will be freed, as will the data block passed. (If no data block, pass NullHandle in dataBlock )

The flags parameter can have one flag, GCNLSF_SET_STATUS. If this flag is set, the event passed will be set as the list's status message.

Include: gcnlist.goh

See Also: GCNListSend().

GCNListSendToListHandles()

void	GCNListSendToListHandles(
        MemHandle		mh,				/* handle of list's block */
        ChunkHandle		ch,				/* chunk of list */
        EventHandle		event,				/* event to send to list */
        MemHandle		dataBlock,				/* handle of data block, if any */
        GCNListSendFlags		flags);				/* GCNListSendFlags */

This routine is exactly the same as GCNListSendToList() ; the list is specified not by optr, however, but by a combination of its global and chunk handles.

See Also: GCNListSendToList().

Include: gcnlist.goh

GCNListUnRelocateBlock()

Boolean	GCNListUnRelocateBlock(
        MemHandle		mh,				/* handle of the locked lmem block
                         * containing the list of lists */
        ChunkHandle		listOfLists,				/* chunk of the list of lists */
        MemHandle		relocBlock);				/* handle of block containing
                         * relocation/unrelocation info */

This routine unrelocates the specified list of lists, updating all the optrs according to the information in relocBlock . This routine is rarely, if ever, used by applications; it is used primarily by the UI when shutting down to a state file.

It returns true if the specified list of lists has no lists saved to state and therefore is simply destroyed. The return value is false if the list of lists is saved to the state file normally.

Include: gcnlist.goh

GenCopyChunk()

word	GenCopyChunk(
        MemHandle		destBlock,			/* handle of locked LMem block into
                     * which chunk will be copied */
        MemHandle		blk,			/* handle of locked source LMem block */
        ChunkHandle		chnk,			/* chunk handle of chunk to be copied */
        word		flags);			/* CompChildFlags */

This is a utility routine that copies one LMem chunk into a newly created chunk. The routine will allocate the new chunk in the block passed in destBlock and will return the chunk handle of the new chunk. It is used primarily by the UI to duplicate generic object chunks.

The source chunk is specified by the global handle blk and the chunk handle chnk . The flags parameter contains a record of CompChildFlags , of which only the CCF_MARK_DIRTY flag is meaningful. If this flag is set, the new chunk will be marked dirty.

Warnings: This routine may resize and/or move chunks and blocks, so you must dereference pointers after calling it.

Include: genC.goh

GenFindObjectInTree()

optr	GenFindObjectInTree(
        optr	startObject,		/* optr of object at which to start search */
        dword	childTable);		/* pointer to table of bytes, each indicating
             * the position of the child at the given
             * level; -1 is the end of the table */

This utility routine finds the object having the optr startObject in the generic tree. Applications will not likely need this routine.

The childTable parameter points to a table of bytes, each byte representing the child number to be found at each level. The first byte indicates the child of startObject to get; the second byte indicates the child to get at the next level; the third byte indicates the child to get at the next level, and so on. A byte of -1 indicates the end of the table. The object found will be returned.

Include: genC.goh

GenInsertChild()

void	GenInsertChild(
        MemHandle		mh,					/* handle of parent */
        ChunkHandle		chnk,					/* chunk of parent */
        optr		childToAdd,					/* optr of new child */
        optr		referenceChild,					/* optr of reference child */
        word		flags);					/* CompChildFlags */

This utility routine adds a child object to a composite object. It is used almost exclusively by the UI for generic objects--applications will typically use MSG_GEN_ADD_CHILD .

See Also: MSG_GEN_ADD_CHILD.

Warnings: This routine may move or resize chunks and/or object blocks; therefore, you must dereference pointers after calling it.

Include: genC.goh

GenProcessAction()

This is an Esp-only routine. In most cases, Goc programmers will use MSG_GEN_OUTPUT_ACTION instead.

GenProcessGenAttrsAfterAction()

void	GenProcessGenAttrsAfterAction(
        MemHandle		mh,		/* handle of object calling the routine */
        ChunkHandle		chnk);		/* chunk of object calling the routine */

This utility routine processes various attributes for a generic object after the object's action message has been sent. It is used almost exclusively by the generic UI after MSG_GEN_OUTPUT_ACTION or GenProcessAction() .

Warnings: This routine may move or resize chunks and/or object blocks; therefore, you must dereference pointers after calling it.

Include: genC.goh

GenProcessGenAttrsBeforeAction()

void	GenProcessGenAttrsBeforeAction(
        MemHandle		mh,		/* handle of object calling the routine */
        ChunkHandle		chnk);		/* chunk of object calling the routine */

This utility routine processes various attributes for a generic object before the object's action message has been sent. It is used almost exclusively by the generic UI before MSG_GEN_OUTPUT_ACTION or GenProcessAction() .

Warnings: This routine may move or resize chunks and/or object blocks; therefore, you must dereference pointers after calling it.

Include: genC.goh

GenProcessUndoGetFile()

VMFileHandle GenProcessUndoGetFile();

This routine returns the handle of the file that holds the process' undo information.

Include: Objects/gProcC.goh

GenProcessUndoCheckIfIgnoring()

Boolean GenProcessUndoCheckIfIgnoring();

This routine returns true if the process is currently ignoring actions.

Include: Objects/gProcC.goh

GenRemoveDownwardLink()

void	GenRemoveDownwardLink(
        MemHandle		mh,			/* handle of calling object */
        ChunkHandle		chnk,			/* chunk of calling object */
        word		flags);			/* CompChildFlags */

This utility routine removes a child from the generic tree, preserving the child's upward link and usability flags. It is called primarily by the generic UI and is rarely used by applications. The flags parameter specifies whether the object linkage should be marked dirty by passing the CCF_MARK_DIRTY flag.

Warnings: This routine may move or resize chunks and/or object blocks; therefore, you must dereference pointers after calling it.

Include: genC.goh

GenSetUpwardLink()

void	GenSetUpwardLink(
        MemHandle		mh,			/* handle of calling object */
        ChunkHandle		chnk,			/* chunk of calling object */
        optr		parent);			/* optr of calling object's parent */

This utility routine converts the child/parent link to an upward-only link. Pass the handle and chunk of the locked child object and the optr of the parent composite.

Include: genC.goh

GeodeAllocQueue()

QueueHandle GeodeAllocQueue();

This routine allocates an event queue which can then be attached to a thread with ThreadAttachToQueue() . It returns the queue's handle if one is allocated; it will return zero otherwise. This routine is used outside the kernel only in exceptional circumstances.

Be Sure To: You must free the queue when you are done with it; use GeodeFreeQueue() .

Include: geode.h

GeodeDuplicateResource()

MemHandle GeodeDuplicateResource(
        MemHandle mh);		/* handle of geode resource to duplicate */

This routine reads a resource from a geode into a newly-allocated block (allocated by this routine). Any relocations on the resource to itself are adjusted to be the duplicated block. The handle of the duplicated block is returned.

Include: resource.h

GeodeFind()

GeodeHandle GeodeFind(
        const char	* name,			/* geode's permanent name */
        word		numChars,		/* number of characters to match:
                          * 8 for name, 12 for name.ext */
    GeodeAttrs	attrMatch,		/* GeodeAttrs that must be set */
        GeodeAttrs	attrNoMatch);		/* GeodeAttrs that must be off */

This routine finds a geode given its permanent name, returning the geode handle if found. If the geode can not be found, a null handle will be returned. Pass it the following:

name
A pointer to the null-terminated permanent name of the geode.
numChars
The number of characters to match before returning. Pass GEODE_NAME_SIZE to match the permanent name, (GEODE_NAME_SIZE + GEODE_NAME_EXT_SIZE) to match the name and extension.
attrMatch
A record of GeodeAttrs the subject geode must have set for a positive match.
attrNoMatch
A record of GeodeAttrs the subject geode must have cleared for a positive match.

Include: geode.h

GeodeFindResource()

word	GeodeFindResource(
        FileHandle	file,			/* geode's executable file */
        word		resNum,			/* resource number to find */
        word		resOffset,		/* offset to resource */
        dword		* base);		/* pointer to second return value */

This routine locates a resource within a geode's executable ( .geo ) file. It returns the size of the resource as well as the base position of the first byte of the resource in the file (pointed to by base ). Pass the following:

file
The file handle of the geode's executable file.
resNum
The number of the resource to be found.
resOffset
The offset within the resource at which to position the file's read/write position.
base
A pointer to a dword value to be filled in by the routine. This value will be the base offset from the beginning of the file to the first byte of the resource.

Structures: A geode's executable file is laid out as shown below.

0:	Geode file header
1:	Imported Library Table
2:	Exported Routine Table
3:	Resource Size Table
4:	Resource Position Table
5:	Relocation Table Size Table
6:	Allocation Flags Table
7+:	application resources

Include: geode.h

GeodeFlushQueue()

void	GeodeFlushQueue(
        QueueHandle	source,			/* source queue to flush */
        QueueHandle	dest,			/* queue to hold flushed events */
        optr		obj			/* object to handle flushed events */
        MessageFlags	flags);			/* MF_INSERT_AT_FRONT or zero */

This routine flushes all events from one event queue into another, synchronously. Pass it the following:

source
The queue handle of the source queue (the one to be emptied).
dest
The queue handle of the destination queue that will receive the flushed events.
obj
The object that will handle flushed events that were destined for the process owning the source queue. If the process owning the destination queue should be used, pass the destination queue handle in the handle portion of the optr and a null chunk handle.
flags
A record of MessageFlags . The only meaningful flag for this routine is MF_INSERT_AT_FRONT, which should be set to flush source queue's events to the front of the destination queue. If this flag is not passed, events will be appended to the queue.

Include: geode.h

GeodeFreeDriver()

void	GeodeFreeDriver(
        GeodeHandle gh);			/* handle of the driver */

This routine frees a driver geode that had been loaded with GeodeUseDriver() . Pass it the geode handle of the driver as returned by that routine.

Include: driver.h

GeodeFreeLibrary()

void	GeodeFreeLibrary(
        GeodeHandle gh);			/* handle of the library */

This routine frees a library geode that had been loaded with GeodeUseLibrary() . Pass it the geode handle of the library.

Include: library.h

GeodeFreeQueue()

void	GeodeFreeQueue(
        QueueHandle qh);			/* handle of queue being freed */

This routine frees an event queue allocated with GeodeAllocQueue() . Any events still on the queue will be flushed as with GeodeFlushQueue() . You must pass the handle of the queue to be freed.

Include: geode.h

GeodeGetAppObject()

optr	GeodeGetAppObject(
        GeodeHandle gh);			/* handle of the application geode */

This routine returns the optr of the specified geode's GenApplication object. The geode should be an application. Pass zero to get the optr of the caller's application object.

Include: geode.h

GeodeGetCodeProcessHandle()

GeodeHandle GeodeGetCodeProcessHandle();

This routine returns the geode handle of the geode that owns the block in which the code which calls this routine resides.

Include: geode.h

GeodeGetDefaultDriver()

GeodeHandle GeodeGetDefaultDriver(
        GeodeDefaultDriverType type);		/* type of default driver to get */

This routine returns the default driver's geode handle for the type passed. The type must be one of the values of GeodeDefaultDriverType .

Include: driver.h

GeodeGetInfo()

word	GeodeGetInfo(
        GeodeHandle	  gh,			/* handle of the subject geode */
        GeodeGetInfoType  info,			/* type of information to return */
        void	 	* buf);			/* buffer to contain returned info */

This routine returns information about the specified geode. The geode must be loaded already. The meaning of the returned word depends on the value passed in info ; the GeodeGetInfoType is shown below. Pass the following:

gh
The geode handle of the geode. If gh is null then the routine will return the information of the current geode.
info
The type of information requested; this should be one of the values listed below.
buf
A pointer to a locked or fixed buffer which will contain returned information for various types requested.

GeodeGetInfoType has the following enumerations (only one may be requested at a time):

GGIT_ATTRIBUTES
Get the geode's attributes. The return value will be a record of GeodeAttrs corresponding to those attributes set for the geode. Pass a null buffer pointer.
GGIT_TYPE
Get the type of the geode. The returned value will be a value of GeosFileType indicating the type of file storing the geode. Pass a null buffer pointer.
GGIT_GEODE_RELEASE
Get the release number of the geode. The returned word will be the size of the buffer pointed to by buf , and the buffer will contain the ReleaseNumber structure of the geode.
GGIT_GEODE_PROTOCOL
Get the protocol level of the geode. The returned word will be the size of the buffer pointed to by buf , and the buffer will contain the ProtocolNumber structure of the geode.
GGIT_TOKEN_ID
Get the token identifier of the geode. The returned word will be the size of the buffer pointed to by buf , and the buffer will contain a GeodeToken structure containing the token characters and token ID of the geode's token.
GGIT_PERM_NAME_AND_EXT
Get the permanent name of the geode, with the extension characters. The returned word will be the size of the buffer pointed to by buf , and the buffer will contain a null-terminated character string representing the geode's permanent name (as set in its geode parameters file). Note that the buffer must be at least 13 bytes.
GGIT_PERM_NAME_ONLY
Get the permanent name of the geode without the extension characters. The returned word will be the size of the buffer pointed to by buf, and the buffer will contain the null-terminated character string representing the geode's permanent name. The buffer must be at least nine bytes.

Include: geode.h

GeodeGetOptrNS()

optr	GeodeGetOptrNS(
        optr	obj);

This routine unrelocates an optr, changing the virtual-segment handle to an actual global handle.

Include: resource.h

GeodeGetProcessHandle()

GeodeHandle GeodeGetProcessHandle();

This routine returns the geode handle of the current executing process (i.e. the owner of the current running thread). Use it when you need to pass your application's geode handle or Process object's handle to a routine or message.

Include: geode.h

GeodeGetUIData()

word	GeodeGetUIData(
        GeodeHandle		gh);

Include: geode.h

GeodeInfoDriver()

DriverInfoStruct  * GeodeInfoDriver(
        GeodeHandle gh);	 /* handle of the driver to get information about */

This routine returns information about the specified driver geode. Pass the geode handle of the driver as returned by GeodeUseDriver() . It returns a pointer to a DriverInfoStruct structure, shown below.

typedef struct {
    void			(*DIS_strategy)();
    DriverAttrs			DIS_driverAttributes;
    DriverType			DIS_driverType;
} DriverInfoStruct;

For full information on this structure, see the DriverInfoStruct reference entry.

Include: driver.h

GeodeInfoQueue()

word	GeodeInfoQueue(
        QueueHandle qh);			/* queue to query */

This routine returns information about a specific event queue. Pass the handle of the queue; for information about the current process' queue, pass a null handle. This routine returns the number of events (or messages) currently in the queue.

Include: geode.h

GeodeLoad()

GeodeHandle GeodeLoad(
        const char *		name,				/* file name of geode */
        GeodeAttrs		attrMatch,				/* GeodeAttrs that must be set */
        GeodeAttrs		attrNoMatch,				/* GeodeAttrs that must be clear */
        word		priority,				/* priority of the loaded geode */
        dword		appInfo,				/* special load information */
        GeodeLoadError *		err);				/* returned error value */

This routine loads the specified geode from the given file and then executes the geode based on its type. It returns the geode handle of the loaded geode if successful; if unsuccessful, the returned value will be NullHandle and the err pointer will point to an error value. Pass this routine the following:

name
A pointer to the name of the geode's file. This is a null-terminated character string that represents the full path of the file (or a path relative to the current working directory).
attrMatch
A record of GeodeAttrs that must be set in the specified geode for the load to be successful.
attrNoMatch
A record of GeodeAttrs that must be cleared in the specified geode for the load to be successful. (That is, each bit which is set in attrNoMatch must be clear in the geode's GeodeAttrs field.)
priority
If the subject geode is a process, this is the priority at which its process thread will run.
appInfo
Two words of data to be passed directly to the loaded geode. For libraries and drivers, this should be a far pointer to a null-terminated string of parameters.
err
A pointer to an empty GeodeLoadError which will hold any returned error values.

Warnings: If you load a geode dynamically with GeodeLoad() , you must be sure to free it when you are done with GeodeFreeDriver() or GeodeFreeLibrary() .

Include: geode.h

See Also: UserLoadApplication().

GeodeLoadDGroup

void	GeodeLoadDGroup(
        MemHandle		mh);

This routine forces the dgroup segment into the data-segment register. This routine expects the MemHandle of the code resource, which can be retrieved with GeodeGetCodeProcessHandle() .

Include: resource.h

GeodePrivAlloc()

word	GeodePrivAlloc(
        GeodeHandle		gh,				/* handle of the owner of the
                         * newly-allocated private data */
        word		numWords);				/* number of words to allocate */

This routine allocates a string of contiguous words in all geodes' private data areas; each set of words will be owned by the geode specified in gh . The data allocated can be accessed with GeodePrivWrite() and GeodePrivRead() and must be freed with GeodePrivFree() . The return value will be the offset to the start of the allocated range, or zero if the routine could not allocate the space.

Each geode has a block of private data the is accessed using the GeodePriv...() routines. A specific geode's private data block is expanded only when a valid GeodePrivWrite() is performed for the geode. Space is "allocated" in the data blocks of all geodes (loaded or yet-to-be loaded) simultaneously via a call to GeodePrivAlloc() . Data that have never been written are returned as all zeros.

Include: geode.h

GeodePrivFree()

void	GeodePrivFree(
        word	offset,		/* offset returned by GeodePrivAlloc() */
        word	numWords);		/* number of words to free */

This routine frees a group of contiguous words from all geodes' private data areas. The space must previously have been allocated with GeodePrivAlloc() . Pass the offset to the words as returned by GeodePrivAlloc() as well as the number of words to be freed.

Include: geode.h

GeodePrivRead()

void	GeodePrivRead(
        GeodeHandle		gh,			/* handle of owner of private data */
        word		offset,			/* offset returned by
                     * GeodePrivAlloc() */
        word		numWords,			/* number of words to read */
        word		* dest);			/* pointer to buffer into which data
                     * will be copied */

This routine reads a number of words from the geode's private data area. Pass the following:

gh
The geode handle of the owner of the private data to be read.
offset
The offset to the private data as returned by GeodePrivAlloc() .
numWords
The number of words to read.
dest
A pointer to a locked or fixed buffer into which the words should be read. It must be at least numWords words long.

Include: geode.h

GeodePrivWrite()

void	GeodePrivWrite(
        GeodeHandle		gh,			/* handle of owner of private data */
        word		offset,			/* offset returned by
                         * GeodePrivAlloc() */
        word		numWords,			/* number of words to be written */
        word		* src);			/* buffer containing data */

This routine writes a number of words into a geode's private data area. The area being written must have been allocated previously with GeodePrivAlloc() . Pass the following:

gh
The geode handle of the owner of the private data space.
offset
The offset to begin writing to, as returned by GeodePrivAlloc() .
numWords
The number of words to be written. This should be no more than had been previously allocated.
src
A pointer to the locked or fixed buffer containing the data to be written.

Include: geode.h

GeodeSetDefaultDriver()

void	GeodeSetDefaultDriver(
        GeodeDefaultDriverType			type,		/* type of default driver to set */
        GeodeHandle			gh);		/* driver to set as the default */

This routine sets the default driver for the indicated driver type. Pass the type of default driver in type and the handle of the driver in gh . The type must be a value of GeodeDefaultDriverType .

Include: driver.h

GeodeSetUIData()

void	GeodeSetUIData(
        GeodeHandle		gh,
        word		data)

GeodeUseDriver()

GeodeHandle GeodeUseDriver(
        const	char	* name,				/* file name of driver to load */
        word		protoMajor,				/* expected major protocol */
        word		protoMinor,				/* expected minor protocol */
        GeodeLoadError		* err);				/* pointer to returned error */

This routine dynamically loads a driver geode given the driver's file name. It returns the geode handle of the driver if successful; if unsuccessful, it returns an error code of type GeodeLoadError pointed to by err . Pass this routine the following:

name
A pointer to the driver's null-terminated full path and file name.
protoMajor
The expected major protocol of the driver. If zero, any protocol is acceptable.
protoMinor
The expected minor protocol of the driver.
err
A pointer to a GeodeLoadError in which any error values will be returned.

Tips and Tricks: It is much easier to automatically load the drivers you need by noting them in your geode parameters file.

Be Sure To: If you use GeodeUseDriver() to dynamically load a driver, you must also use GeodeFreeDriver() to free it when you are done using it.

Include: driver.h

GeodeUseLibrary()

GeodeHandle GeodeUseLibrary(
        const char	 *	name,				/* file name of library to load */
        word		protoMajor,				/* expected major protocol */
        word		protoMinor,				/* expected minor protocol */
        GeodeLoadError *		err);				/* pointer to returned error */

This routine dynamically loads a library geode when given the library's file name. (The library must be in the thread's working directory.) It returns the geode handle of the loaded library if successful; if unsuccessful, it returns an error code ( GeodeLoadError ) pointed to by err . Pass this routine the following parameters:

name
A pointer to the library's null-terminated file name.
protoMajor
The expected major protocol of the library. If zero, any protocol is acceptable.
protoMinor
The expected minor protocol of the library.
err
A pointer to a GeodeLoadError which will contain any returned error values.

Be Sure To: If you dynamically load a library with GeodeUseLibrary() , you must manually free it when finished, with GeodeFreeLibrary() .

Include: library.h

GeoFree()

void	* GeoFree(
        void		* blockPtr,				/* address of memory to free */
        GeodeHandle		geodeHan);				/* owner of block to be used */

The routine malloc() can free only memory in the malloc-block belonging to the calling geode. If you want to free memory in another geode's malloc-block, call GeoFree() . Passing a null GeodeHandle will make GeoMalloc() act on memory in the calling geode's malloc-block.

Include: geode.h

Warnings: Pass exactly the same address as the one returned to you when you allocated the memory. If you pass a different address, GeoFree() will take unpredictable actions, including possibly erasing other memory or crashing the system.

See Also: free().

GeoMalloc()

void	* GeoMalloc(
        size_t		blockSize,				/* # of bytes to allocate*/
        GeodeHandle		geodeHan,				/* Owner of block to be used */
        word		zeroInit);				/* Zero-initialize memory? */

The routine malloc() automatically allocates memory in the malloc-block belonging to the calling geode. It does not zero-initialize the memory. If you want to zero-initialize the memory, or want to allocate it in another geode's malloc-block, call GeoMalloc() . Pass true (i.e., non-zero) in zeroInit to zero-initialize the memory.

Passing a null GeodeHandle will make GeoMalloc() allocate the memory in the calling geode's malloc-block. If "zeroInit" is true, the memory will be initialized to null bytes; otherwise, the memory will be left uninitialized.

Include: geode.h

Warnings: All memory allocated with malloc() is freed when GEOS shuts down.

See Also: malloc().

GeoReAlloc()

void	* GeoReAlloc(
        void		* blockPtr,				/* address of memory to resize */
        size_t		newSize,				/* New size in bytes */
        GeodeHandle		geodeHan);				/* Owner of block to be used */

The routine realloc() can resize only memory in the malloc-block belonging to the calling geode. If you want to resize memory in another geode's malloc-block, call GeoReAlloc() . Passing a null GeodeHandle will make GeoReAlloc() act on memory in the calling geode's malloc-block.

If the block is resized larger, the new memory will not be zero-initialized. Resizing a block smaller will never fail. If GeoReAlloc() fails, it will return a null pointer (zero). If you pass a newSize of zero, the passed block pointer is freed and the return pointer is a null pointer.

Include: geode.h

Warnings: Pass exactly the same address as the one returned to you when you allocated the memory. If you pass a different address, GeoReAlloc() will take unpredictable actions, including possibly erasing other memory or crashing the system.

See Also: realloc().

GrApplyRotation()

void	GrApplyRotation(
        GStateHandle		gstate,			/* GState to alter */
        WWFixedAsDWord		angle); 			/* degrees counterclockwise */

Apply a rotation to the GState's transformation matrix.

Include: graphics.h

GrApplyScale()

void	GrApplyScale(
        GStateHandle		gstate,			/* GState to alter */
        WWFixedAsDWord		xScale,			/* new x scale factor */
        WWFixedAsDWord		yScale);			/* new y scale factor */

Apply a scale factor to the GState's transformation matrix.

Include: graphics.h

GrApplyTransform()

void	GrApplyTransform(
        GStateHandle		gstate,			/* GState to draw to */
        const TransMatrix		*tm);			/* transformation matrix to apply */

Apply a transformation, expressed as a transformation matrix, to a GState's coordinate system.

Include: graphics.h

GrApplyTranslation()

void	GrApplyTranslation(
        GStateHandle		gstate,			/* GState to alter */
        WWFixedAsDWord		xTrans,			/* translation in x */
        WWFixedAsDWord		yTrans);			/* translation in y */

Apply a translation to the GState.

Include: graphics.h

GrApplyTranslationDWord()

void	GrApplyTranslationDWord(
        GStateHandle		gstate,			/* GState to alter */
        sdword		xTrans,			/* extended translation in x */
        sdword		yTrans);			/* extended translation in y */

Apply a 32-bit integer extended translation to the GState.

Include: graphics.h

GrBeginPath()

void	GrBeginPath(
        GStateHandle		gstate,			/* GState to alter */
        PathCombineType		params);			/* path parameters */

Starts or alters the path associated with a GState. All graphics operations that are executed until GrEndPath() is called become part of the path.

Depending on the value of the params field, the new path may replace the old path, or may be combined with the old path by intersection or union.

Include: graphics.h

GrBeginUpdate()

void 	GrBeginUpdate(
        GStateHandle gstate);			/* GState to draw to */

Called by an application to signal that it is about to begin updating the exposed region. This routine is normally called as part of a MSG_META_EXPOSED handler. Blanks out the invalid area.

Include: win.h

GrBitBlt()

void	GrBitBlt(
        GStateHandle		gstate,				/* GState to draw to */
        sword		sourceX,				/* original x origin */
        sword		sourceY,				/* original y origin */
        sword		destX,				/* new x origin */
        sword		destY,				/* new y origin */
        word		width,				/* width of area */
        word		height,				/* height of area */
        BLTMode		mode);				/* draw mode (see below) */

Transfer a bit-boundary block of pixels between two locations in video memory. This routine is useful for animation and other applications which involve moving a drawing around the screen.

Structures:

            typedef enum /* word */ {
    BLTM_COPY, 				/* Leave source region alone */
    BLTM_MOVE, 				/* Clear & invalidate source rect */
    BLTM_CLEAR				/* Clear source rectangle */
} BLTMode;

Include: graphics.h

GrBrushPolyline()

void	GrBrushPolyline(
        GStateHandle		gstate,			/* GState to draw to */
        const Point		* points,			/* array of Point structures to draw */
        word		numPoints,			/* number of points in array */
        word		brushH,			/* brush height */
        word		brushW);			/* brush width */

Draw a brushed connected polyline. Note that this routine ignores the GState's line width, and instead uses a brush height and width, measured in pixels.

Include: graphics.h

GrCharMetrics()

dword	GrCharMetrics(
        GStatehandle		gstate,			/* GState to get metrics for */
        GCM_info		info,			/* information to return */
        word		ch);			/* character of type Chars */

Returns metric information for a single character of a font. This information is used to determine the drawing bounds for a character. To find out how wide a character is (how much space to leave for it if drawing a line of text character-by-character), use GrCharWidth() instead.

Structures:

            typedef enum {
    GCMI_MIN_X, 						/* return = value << 16 */
    GCMI_MIN_X_ROUNDED,					 	/* return = value */
    GCMI_MIN_Y, 						/* return = value << 16 */
    GCMI_MIN_Y_ROUNDED,				 		/* return = value << 16 */
    GCMI_MAX_X, 						/* return = value << 16 */
    GCMI_MAX_X_ROUNDED, 						/* return = value << 16 */
    GCMI_MAX_Y, 						/* return = value << 16 */
    GCMI_MAX_Y_ROUNDED 						/* return = value << 16 */
} GCM_Info;

See Also: GrCharWidth().

Include: font.h

GrCharWidth()

dword	GrCharWidth( /* Returns width << 16 */
        GStateHandle		gstate,			/* GState to query */
        word		ch);			/* character of type Chars */

Return the width of a single character. Note that this routine does not take into account track kerning, pairwise kerning, space padding, or other attributes that apply to multiple characters.

Include: graphics.h

GrCheckFontAvailID()

FontID 	GrCheckFontAvailID(
        FontEnumFlags 		flags,
        word 		family,
        FontID 		id);

See if font (identified by ID) exists.

Include: graphics.h

GrCheckFontAvailName()

FontID 	GrCheckFontAvailName(
        FontEnumFlags 		flags,
        word 		family,
        const char 		* name);

See if font (identified by name) exists.

Include: graphics.h

GrClearBitmap()

void	GrClearBitmap(
        GStateHandle 	gstate);			/* GState to affect */

Clear out the content of a bitmap. Note that the part of the bitmap actually cleared depends on the bitmap mode. For the normal mode, the data portion of the bitmap is cleared. If the bitmap is in BM_EDIT_MASK mode, then the mask is cleared and the data portion is left alone.

Include: graphics.h

GrCloseSubPath()

void	GrCloseSubPath(
        GStateHandle gstate);			/* GState to affect */

Geometrically closes the currently open path segment. Note that you must still call GrEndPath() to end the path definition.

Include: graphics.h

GrComment()

void	GrComment(
        GStateHandle		gstate,			/* GState to affect */
        const void  		* data,			/* comment string */
        word		size);			/* Size of data, in bytes */

Write a comment out to a graphics string.

Include: graphics.h

GrCopyGString()

GSRetType GrCopyGString(
        GStateHandle		source,			/* GState from which to get GString */
        GStateHandle		dest,			/* GState to which to copy GString */
        GSControl 		flags);			/* flags for the copy */

Copy all or part of a Graphics String. The GSControl record can have the following flags:

 GSC_ONE			/* just do one element */
 GSC_MISC			/* return on MISC opcode */
 GSC_LABEL			/* return on GR_LABEL opcode */
 GSC_ESCAPE			/* return on GR_ESCAPE opcode */
 GSC_NEW_PAGE			/* return when we get to a NEW_PAGE */
 GSC_XFORM			/* return on TRANSFORMATIONopcode */
 GSC_OUTPUT:			/* return on OUTPUT opcode */
 GSC_ATTR			/* return on ATTRIBUTE opcode */
 GSC_PATH			/* return on PATH opcode */

The return value can be any one of GSRetType , a byte-size field:

GSRT_COMPLETE
GSRT_ONE
GSRT_MISC
GSRT_LABEL
GSRT_ESCAPE
GSRT_NEW_PAGE
GSRT_XFORM
GSRT_OUTPUT
GSRT_ATTR
GSRT_PATH
GSRT_FAULT

Include: gstring.h

GrCreateBitmap()

VMBlockHandle GrCreateBitmap(
        BMFormat 		initFormat,				/* color fomat of bitmap */
        word		initWidth,				/* initial width of bitmap */
        word		initHeight,				/* initial height of bitmap */
        VMFileHandle 		vmFile,				/* VM file to hold bitmap's data*/
        optr 		exposureOD,				/* optr to get MSG_META_EXPOSED */
        GStateHandle		* bmgs);				/* Draws to this GState
                             * will draw to the bitmap */

This routine allocates memory for a bitmap and creates an off-screen window in which to hold the bitmap. This routine takes the following arguments:

initFormat
The depth of the bitmap's color.
initWidth
Bitmap's width.
initHeight
Bitmap's height.
vmFile
File to hold the bitmap data; the routine will allocate a block within this file.
exposureOD Object which will receive the "exposed" message when the bitmap's window is invalidated. If this argument is zero, then no exposed message will be sent.
Remember that an off-screen window is created to house the bitmap. When this window is first created, it will be invalid, and it is conceivable that later actions could cause it to become invalid again. On these occasions, the object specified by this argument will receive a MSG_META_EXPOSED.
bmgs
The GStateHandle pointed to by this argument can start out as null; the routine will use it to return the GState by which the bitmap can be drawn to. Any graphics routines which draw to this returned GState will be carried out upon the bitmap.

The routine returns a VMBlockHandle , the handle of the block within the passed VM file which contains the bitmap's data. The block will be set up as the first block of a HugeArray. Its header area will be filled with the following:

Complex Bitmap Header
This is a CBitmap structure which contains some basic information about the bitmap.
Editing Mode
These flags can change how the bitmap is being edited.
Device Information Block
This internal structure contains information about and used by the video driver. (Don't worry that you don't know the size of this structure; remember that the CBitmap structure contains the offsets of the bitmap and palette data areas.)
Pallette Information (optional)
If the bitmap has its own pallette, this is where the palette data will be stored; it will consist of an array of 3-byte entries. Depending on how many colors the bitmap supports, there may be 16 or 256 entries in this array.

The bitmap's raw data is in the VM block, but outside of the header area.

Include: graphics.h

GrCreateGString()

GStateHandle GrCreateGString(
        Handle		han,			/* memory, stream, or VM file handle */
        GStringType		hanType,			/* type of handle in han parameter */
        word		* gsBlock);				/* returned for GST_MEMORY and
                         * GST_VMEM types only */

Open a graphics string and start redirecting graphics orders to the string. The hanType parameter must be GST_MEMORY, GST_STREAM, or GST_VMEM.

Include: gstring.h

GrCreatePalette()

word	GrCreatePalette( /* Returns # of entries in color table
            * or 0 for monochrome or 24-bit */
        GStateHandle gstate);

Create a color mapping table and associate it with the current window. Initialize the table entries to the default palette for the device.

Include: graphics.h

GrCreateState()

GStateHandle GrCreateState(
        WindowHandle win);			/* Window in which GState will be active */

Create a graphics state (GState) block containg default GState information.

If zero is passed, then the GState created will have no window associated with it.

Include: graphics.h

GrDeleteGStringElement()

void	GrDeleteGStringElement(
        GStateHandle		gstate,			/* GState containing GString */
        word		count);			/* number of elements to delete */

Delete a range of GString elements from the GString in the passed GState.

Include: graphics.h

GrDestroyBitmap()

void	GrDestroyBitmap(
        GStateHandle		gstate,			/* GState containing bitmap */
        BMDestroy 		flags);			/* flags for removing data */

Free the bitmap and disassociate it with its window. Depending on the passed flag, the bitmap's data may be freed or preserved. Thus, it is possible to remove the GString used to edit the bitmap while maintaining the bitmap in a drawable state.

Structures:

typedef ByteEnum BMDestroy;
/* 	BMD_KILL_DATA,
     BMD_LEAVE_DATA */

Include: graphics.h

GrDestroyGString()

void	GrDestroyGString(
        Handle		gstring,			/* Handle of GString */
        GStateHandle		gstate,			/* NULL, or handle of another
                     * gstate to free*/
        GStringKillType		type);			/* Kill type for data removal */

Destroys a GString. Depending on the GStringKillType argument, this either constitutes removing the GState from the GString data; or freeing both the GState and the GString's data. If you have been drawing the GString to a GState, you should pass the GState's handle as gstate , and this routine will do some cleaning up.

Structures:

typedef ByteEnum GStringKillType;
/* 	GSKT_KILL_DATA,
    GSKT_LEAVE_DATA */

Include: gstring.h

GrDestroyPalette()

void	GrDestroyPalette(
        GStateHandle gstate);			/* GState of palette to destroy */

Free any custom palette associated with the current window.

Include: graphics.h

GrDestroyState()

void	GrDestroyState(
        GStateHandle gstate);			/* GState to be destroyed */

Free a graphics state block.

Include: graphics.h

GrDrawArc()

void	GrDrawArc(
        GStateHandle		gstate,				/* GState to draw to */
        sword		left,				/* bounds of box outlining arc */
        sword		top,
        sword		right,
        sword		bottom,
        word		startAngle,				/* angles in degrees
        word		endAngle,				 * counter-clockwise */
        ArcCloseType		arcType);				/* how the arc is closed */

Draw an arc along the ellipse that is specified by a bounding box, from the starting angle to the ending angle.

Include: graphics.h

GrDrawArc3Point()

void	GrDrawArc3Point(
        GStateHandle		gstate,			/* GState to draw to */
        const ThreePointArcParams 	*params);

Draw a circular arc, given three points along the arc; both endpoints and any other point on the arc.

Include: graphics.h

GrDrawArc3PointTo()

void	GrDrawArc3PointTo(
        GStateHandle		gstate,			/* GState to draw to */
        const ThreePointArcToParams *params);

As GrDrawArc3Point() , above, except that the current position is automatically used as one of the endpoints.

Include: graphics.h

GrDrawBitmap()

void	GrDrawBitmap(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x,				/* x starting point */
        sword		y,				/* y starting point */
        const	Bitmap	* bm,				/* pointer to the bitmap */
        Bitmap * _pascal (*callback) (Bitmap *bm));	/* NULL for no callback */

Draw a bitmap. Note that if the bitmap takes up a great deal of memory, it is necessary to manage its memory when drawing. If the bitmap resides in a HugeArray (true of any bitmap created using GrCreateBitmap() ), then calling GrDrawHugeBitmap() will automatically take care of memory management. Otherwise, you may wish to provide a suitable callback routine. This routine should be declared _pascal and is passed a pointer into the passed bitmap and is expected to return a pointer to the next slice. This allows the bitmap to be drawn in horizontal bands, or swaths. The callback should signal it is done returning blocks by returning a null pointer.

Include: graphics.h

GrDrawBitmapAtCP()

void	GrDrawBitmapAtCP(
        GStateHandle		gstate,			/* GState to draw to */
        const	Bitmap	* bm,				/* pointer to the bitmap */
        Bitmap * _pascal (*callback) (Bitmap *bm));	/* NULL for no callback */

This routine is the same as GrDrawBitmap() , above, except that the bitmap is drawn at the current position.

Include: graphics.h

GrDrawChar()

void	GrDrawChar(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x,			/* x position at which to draw */
        sword		y,			/* y position at which to draw */
        word		ch);			/* character of type Chars */

Draw a character at the given position with the current text drawing attributes.

Include: graphics.h

GrDrawCharAtCP()

void	GrDrawCharAtCP(
        GStateHandle		gstate,			/* GState to draw to */
        word		ch);			/* character of type Chars */

Draw a character at the current position with the current text drawing attributes.

Include: graphics.h

GrDrawCurve()

void	GrDrawCurve(
        GStateHandle		gstate,				/* GState to draw to */
        const	Point	*points);				/* array of four Points */

Draw a Bezier curve.

Include: graphics.h

GrDrawCurveTo()

void	GrDrawCurveTo(
        GStateHandle		gstate,				/* GState to draw to */
        const	Point	*points);			/* array of three Points */

Draw a Bezier curve, using the current postion as the first point.

Include: graphics.h

GrDrawEllipse()

void	GrDrawEllipse(
        GStateHandle		gstate,			/* GState to draw to */
        sword		left,			/* bounding box bounds */
        sword		top,
        sword		right,
        sword		bottom);

Draw an ellipse, defined by its bounding box.

Include: graphics.h

GrDrawGString()

GSRetType GrDrawGString(
        GStateHandle		gstate,					/* GState to draw to */
        Handle		gstringToDraw,					/* GString to draw */
        sword		x,					/* point at which to draw */
        sword		y,
        GSControl 		flags,					/* GSControl record */
        GStringElement		* lastElement);					/* pointer to empty structure */

Draw a graphics string. The passed control flag allows drawing to stop upon encountering certain kinds of drawing elements. If this causes the drawing to stop in mid-string, then the routine will provide a pointer to the next GStringElement to be played.

  • You must provide a GState to draw to. You may wish to call GrSaveState() on the GState before drawing the GString (and call GrRestoreState() afterwards). If you will draw anything else to this GState after the GString, you must call GrDestroyGString() on the GString, and pass this GState's handle as the gstate argument so that GrDestroyGString() can clean up the GState.
  • You must provide a GString to draw. The GString must be properly loaded (probably by means of GrLoadGString() ).
  • You can provide a pair of coordinates at which to draw the GString. The graphics system will translate the coordinate system by these coordinates before carrying out the graphics commands stored in the GString.
  • You can provide a GSControl argument which requests that the system stop drawing the GString when it encounters a certain type of GString element. If the GString interpreter encounters one of these elements, it will immediately stop drawing. The GString will remember where it stopped drawing. If you call GrDrawGString() with that same GString, it will continue drawing where you left off.
  • You must provide a pointer to an empty GStringElement structure. GrDrawGString() will return a value here when it is finished drawing. If the GString has stopped drawing partway through due to a passed GSControl , the returned GStringElement value will tell you what sort of command was responsible for halting drawing. For instance, if you had instructed GrDrawGString() to halt on an `output' element (GrDraw...() or GrFill...() commands), then when GrDrawGString() returns, you would check the value returned to see what sort of output element was present.

Include: gstring.h

GrDrawGStringAtCP()

GSRetType GrDrawGStringAtCP(
        GStateHandle		gstate,					/* GState to draw to */
        GStringeHandle		gstringToDraw,					/* GString to draw */
        GSControl 		flags,					/* GSControl flags */
        GStringElement 		* lastElement);					/* last element to draw */

Draw a graphics string as GrDrawGString() , above, except that drawing takes place at the current position.

  • You must provide a GState to draw to. You may wish to call GrSaveState() on the GState before drawing the GString (and call GrRestoreState() afterwards). If you will draw anything else to this GState after the GString, you must call GrDestroyGString() on the GString, and pass this GState's handle as the gstate argument so that GrDestroyGString() can clean up the GState.
  • You must provide a GString to draw. The GString must be properly loaded (probably by means of GrLoadGString() ).
  • You can provide a GSControl argument which requests that the system stop drawing the GString when it encounters a certain type of GString element. If the GString interpreter encounters one of these elements, it will immediately stop drawing. The GString will remember where it stopped drawing. If you call GrDrawGString() with that same GString, it will continue drawing where you left off.
  • You must provide a pointer to an empty GStringElement structure. GrDrawGString() will return a value here when it is finished drawing. If the GString has stopped drawing partway through due to a passed GSControl , the returned GStringElement value will tell you what sort of command was responsible for halting drawing. For instance, if you had instructed GrDrawGString() to halt on an `output' element (GrDraw...() or GrFill...() commands), then when GrDrawGString() returns, you would check the value returned to see what sort of output element was present.

Include: gstring.h

GrDrawHLine()

void	GrDrawHLine(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x1,			/* first horizontal coordinate */
        sword		y,			/* vertical position of line */
        sword		x2);			/* second horizontal coordinate */

Draw a horizontal line.

Include: graphics.h

GrDrawHLineTo()

void	GrDrawHLineTo(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x);			/* ending horizontal coordinate */

Draw a horizontal line starting from the current position.

Include: graphics.h

GrDrawHugeBitmap()

void	GrDrawHugeBitmap(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x			/* Point at which to draw */
        sword		y,
        VMFileHandle 		vmFile,			/* VM File holding HugeArray */
        VMBlockHandle 		vmBlk);			/* VM block of HugeArray */

Draw a bitmap that resides in a HugeArray.

Include: graphics.h

See Also: GrDrawBitmap(),
GrDrawHugeBitmapAtCP(),
GrDrawHugeImage().

GrDrawHugeBitmapAtCP()

void	GrDrawHugeBitmapAtCP(
        GStateHandle		gstate,			/* GState to draw to */
        VMFileHandle	 	vmFile,			/* VM file containing HugeArray */
        VMBlockHandle 		vmBlk);			/* VM block containing HugeArray */

As GrDrawHugeBitmap() , above, except that the bitmap is drawn at the current position.

Include: graphics.h

See Also: GrDrawBitmapAtCP(), GrDrawHugeBitmap().

. Note that the bitmap will be

GrDrawHugeImage()

void	GrDrawHugeImage(
        GStateHandle 		gstate,			/* GState to draw to */
        sword		x			/* point at which to draw */
        sword		y,
        ImageFlags 		flags,
        VMFileHandle 		vmFile,			/* VM file holding HugeArray */
        VMBlockHandle 		vmBlk);			/* VM block holding HugeArray */

Draw a bitmap that resides in a HugeArray drawn on an assumption of one device pixel per bitmap pixel. The bitmap will not draw rotated or scaled. Depending on the value of the flags argument, the bitmap may be expanded so that a square of device pixels displays each bitmap pixel.

Include: graphics.h

See Also: GrDrawImage(), GrDrawHugeBitmapAtCP().

GrDrawImage()

void	GrDrawImage(
        GStateHandle 		gstate,			/* GState to draw to */
        sword		x			/* point at which to draw */
        sword		y,
        ImageFlags 		flags,
        const Bitmap 		* bm);			/* pointer to bitmap */

Draw a bitmap. Note that the bitmap will be drawn on an assumption of one device pixel per bitmap pixel. The bitmap will not draw rotated or scaled. Depending on the value of the flags argument, the bitmap may be expanded so that a square of device pixels displays each bitmap pixel.

Include: graphics.h

See Also: GrDrawHugeImage(), GrDrawBitmap().

GrDrawLine()

void	GrDrawLine(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x1,			/* First coordinate of line */
        sword		y1,
        sword		x2,			/* Second coordinate of line */
        sword		y2);

Draw a line.

Include: graphics.h

See Also: GrDrawLineTo(),
GrDrawHLine(),
GrDrawVLine().

GrDrawLineTo()

void	GrDrawLineTo(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x,			/* Second coordinate of line */
        sword		y);

Draw a line starting from the current position.

Include: graphics.h

See Also: GrDrawLine(),
GrDrawHLineTo(),
GrDrawVLineTo().

GrDrawPath()

void	GrDrawPath(
        GStateHandle gstate);			/* GState to draw to */

Draws the stroked version of the current path, using the current graphic line attributes.

Include: graphics.h

GrDrawPoint()

void	GrDrawPoint(
        GStateHandle		gstate,			/* GState to draw to */
        sword 		x,			/* Coordinates of point to draw */
        sword 		y);

Draw a pixel.

Include: graphics.h

GrDrawPointAtCP()

void	GrDrawPointAtCP(
        GStateHandle gstate);			/* GState to draw to */

Draw a pixel.

Include: graphics.h

GrDrawPolygon()

void	GrDrawPolygon(
        GStateHandle		gstate,				/* GState to draw to */
        const	Point 	* points,				/* array of points in polygon */
        word		numPoints);				/* number of points in array */

Draws a connected polygon.

Include: graphics.h

GrDrawPolyline()

void	GrDrawPolyline(
        GStateHandle		gstate,				/* GState to draw to */
        const	Point	* points,				/* array of points in polyline */
        word		numPoints);				/* number of points in array */

Draws a simple polyline.

Include: graphics.h

GrDrawRect()

void	GrDrawRect(
        GStateHandle		gstate,			/* GState to draw to */
        sword		left,			/* bounds of rectangle to draw */
        sword		top,
        sword		right,
        sword		bottom);

Draws the outline of a rectangle.

Include: graphics.h

GrDrawRectTo()

void	GrDrawRectTo(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x,			/* opposite corner of rectangle */
        sword		y);

Draws the outline of a rectangle, with one corner defined by the current position.

Include: graphics.h

GrDrawRegion()

void	GrDrawRegion(
        GStateHandle		gstate,			/* GState to draw to */
        sword		xPos,			/* Position at which to draw */
        sword		yPos,
        const	Region	* reg,			/* Region definition */
        sword		param0,			/* value to use with
                     * parameterized coordinates */
        sword		param1);			/* value to use with
                     * parameterized coordinates */

Draw a region. The area will be rendered filled with the GState's area attributes.

Include: graphics.h

GrDrawRegionAtCP()

void	GrDrawRegionAtCP(
        GStateHandle		gstate,			/* GState to draw to */
        const	Region 	* reg,			/* region definition */
        sword	param0,	/* Value to use with parameterized coordinates */
        sword	param1,	/* Value to use with parameterized coordinates */
        sword	param2,	/* Value to use with parameterized coordinates */
        sword	param3);	/* Value to use with parameterized coordinates */

Draw a region at the current pen position. The area will be rendered filled with the GState's area attributes.

Include: graphics.h

GrDrawRelArc3PointTo()

void 	GrDrawRelArc3PointTo(
        const ThreePointRelArcToParams *params);

Draw a circular arc relative to the current point given two additional points: the other endpoint and any other point on the arc, both described in relative coordinates.

Include: graphics.h

GrDrawRelLineTo()

void 	GrDrawRelLineTo(
        GStateHandle 		gstate,			/* GState to draw to */
        WWFixedAsDWord 		x,			/* horizontal offset of second point */
        WWFixedAsDWord 		y);			/* vertical offset of second point */

Draw a line from the current pen position, given a displacement from the current pen position to draw to.

Include: graphics.h

GrDrawRoundRect()

void	GrDrawRoundRect(
        GStateHandle		gstate,					/* GState to draw to */
        sword		left,					/* bounds of rectangle */
        sword		top,
        sword		right,
        sword		bottom,
        word		cornerRadius);					/* radius of corner rounding */

Draw the outline of a rounded rectangle.

Include: graphics.h

GrDrawRoundRectTo()

void	GrDrawRoundRectTo(
        GStateHandle		gstate,					/* GState to draw to */
        sword		x,					/* opposite corner of bounds */
        sword		y,
        word		cornerRadius);					/* radius of corner rounding */

Draw the outline of a rounded rectangle, where one corner of the bounding rectangle is the current position.

Include: graphics.h

GrDrawSpline()

void	GrDrawSpline(
        GStateHandle		gstate,				/* GState to draw to */
        const	Point	* points,				/* array of points */
        word		numPoints,); 				/* number of points in array */

Draw a Bézier spline.

Include: graphics.h

See Also: GrDrawCurve().

GrDrawSplineTo()

void	GrDrawSplineTo(
        GStateHandle		gstate,				/* GState to draw to */
        const	Point	*points,				/* array of points */
        word		numPoints);				/* number of points in array */

Draw a Bezier spline, using the current position as one endpoint.

Include: graphics.h

See Also: GrDrawCurveTo().

GrDrawText()

void	GrDrawText(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x,			/* point at which to draw */
        sword		y,
        const	Chars	* str,			/* pointer to character string */
        word		size);			/* length of string */

Draw a string of text. The string is represented as an array of characters. Note that the text will be drawn using the GState's font drawing attributes and that this routine does not accept any style run arguments.

If the passed size argument is zero, the string is assumed to be null-terminated.

Include: graphics.h

GrDrawTextAtCP()

void	GrDrawTextAtCP(
        GStateHandle		gstate,			/* GState to draw to */
        const	Chars	* str,			/* pointer to character string */
        word		size);			/* length of string */

As GrDrawText() , above, except that the text is drawn at the current position.

If the passed size argument is zero, the string is assumed to be null-terminated.

Include: graphics.h

GrDrawVLine()

void	GrDrawVLine(
        GStateHandle		gstate,			/* GState to draw to */
        sword		x,			/* horizontal position of line */
        sword		y1,			/* first vertical coordinate */
        sword		y2);			/* second vertical coordinate */

Draw a vertical line.

Include: graphics.h

GrDrawVLineTo()

void	GrDrawVLineTo(
        GStateHandle		gstate,			/* GState to draw to */
        sword		y);			/* second vertical position */

Draw a vertical line starting from the current position.

Include: graphics.h

GrEditBitmap()

GStateHandle GrEditBitmap(
        VMFileHandle 		vmFile,				/* VM file of bitmap */
        VMBlockHandle 		vmBlock,				/* VM block of bitmap */
        optr 		exposureOD);				/* optr to get MSG_META_EXPOSED */

This routine attaches a GState to the passed bitmap so that new drawings may be be sent to the bitmap.

Include: graphics.h

GrEditGString()

GStateHandle GrEditGString(
        Handle	vmFile,	/* VM file containing the GString */
        word	vmBlock);	/* VM block containing the GString */

This routine takes the location of a GString data block stored in a VM file. It will associate a GState with this GString data and returns the handle of this GState. Any graphics commands issued using this GStateHandle will be appended to the GString.

Include: graphics.h

GrEndGString()

GStringErrorType GrEndGString(
        GStateHandle gstate);			/* GState to draw to */

Finish the definition of a graphics string.

Structures:

            typedef enum {
        GSET_NO_ERROR,
        GSET_DISK_FULL
} GStringErrorType;

Include: graphics.h

GrEndPath()

void	GrEndPath(
        GStateHandle gstate);			/* GState to draw to */

Finish definition of a path. Further graphics commands will draw to the display, as normal.

Include: graphics.h

GrEndUpdate()

void	GrEndUpdate(
        GStateHandle gstate);			/* GState to draw to */

Unlocks window from an update.

Include: win.h

GrEnumFonts()

word	GrEnumFonts( /* Return value = number of fonts found */
        FontEnumStruct		* buffer,			/* buffer for returned values */
        word		size,			/* number of structures to return */
        FontEnumFlags		flags,			/* FontEnumFlags */
        word		family);			/* FontFamily */

Generate a list of available fonts. The font information includes both the font's ID and a string name.

Structures:

            typedef struct {
        FontID FES_ID;
        char FES_name[FID_NAME_LEN];
} FontEnumStruct;

Include: font.h

GrEscape()

void	GrEscape(
        GStateHandle		gstate,			/* GState to draw to */
        word		code,			/* escape code */
        const	void	* data,			/* pointer to the data */
        word		size);			/* Size of data, in bytes */

Write an escape code to a graphics string.

Include: graphics.h

GrFillArc()

void	GrFillArc(
        GStateHandle		gstate,				/* GState to draw to */
        sword		left,				/* bounding rectangle */
        sword		top,
        sword		right,
        sword		bottom,
        word		startAngle,				/* angles in degrees
        word		endAngle				 * counter-clockwise */
        ArcCloseType 		closeType);				/* OPEN, CHORD, or PIE */

Fill an elliptical arc. The arc is defined by the bounding rectangle of the base ellipse and two angles. Depending on how the arc is closed, this will result in either a wedge or a chord fill.

Include: graphics.h

GrFillArc3Point()

void	GrFillArc3Point(
        GStateHandle		gstate,				/* GState to draw to */
        const ThreePointParams *params);

Fill an arc. Depending on how the arc is closed, this will result in either a wedge or a chord fill. The arc is defined in terms of its endpoints and one other point, all of which must lie on the arc.

Include: graphics.h

GrFillArc3PointTo()

void	GrFillArc3PointTo(
        GStateHandle		gstate,				/* GState to draw to */
        const ThreePointArcParams 	*params);

As GrFillArc3Point() , above, except that one endpoint of the arc is defined by the current position.

Include: graphics.h

GrFillBitmap()

void 	GrFillBitmap (
        GStateHandle 		gstate,				/* GState to draw to */
        sword 		x,				/* point at which to draw */
        sword 		y,
        const Bitmap 		* bm,				/* pointer to bitmap */
        Bitmap * _pascal (*callback) (Bitmap *bm));

Fill a monochrome bitmap with the current area attributes. The arguments to this routine are the same as those for GrDrawBitmap() .

Include: graphics.h

GrFillBitmapAtCP()

void 	GrFillBitmapAtCP (
        GStateHandle 		gstate,					/* GState to draw to */
        const Bitmap 		* bm,					/* pointer to bitmap */
        Bitmap * _pascal (*callback) (Bitmap *bm));

Fill a monochrome bitmap with the current area attributes. The bitmap will be drawn at the current position. The arguments to this routine are the same as those for GrDrawBitmapAtCP() .

Include: graphics.h

GrFillHugeBitmap()

void 	GrFillHugeBitmap(
        GStateHandle 		gstate,
        sword 		x,
        sword 		y,
        VMFileHandle 		vmFile,
        VMBlockHandle 		vmBlk);

This routine fills a monochrom bitmap where said bitmap is stored in a huge array data structure. The bitmap is specified by means of the vmFile and vmBlk arguments which should reference the huge array.

Include: graphics.h

GrFillHugeBitmapAtCP()

void 	GrFillHugeBitmapAtCP(
        GStateHandle 		gstate,
        VMFileHandle 		vmFile,
        VMBlockHandle 		vmBlk);

This routine fills a monochrom bitmap where said bitmap is stored in a huge array data structure. It will draw at the GState's current position. The bitmap is specified by means of the vmFile and vmBlk arguments which should reference the huge array.

Include: graphics.h

GrFillEllipse()

void	GrFillEllipse(
        GStateHandle		gstate,				/* GState to draw to */
        sword		left,				/* Bounds of bounding rectangle */
        sword		top,
        sword		right,
        sword		bottom);

Draw a filled ellipse. The ellipse's dimensions are defined by its bounding box.

Include: graphics.h

GrFillPath()

void	GrFillPath(
        GStateHandle		gstate,				/* GState to draw to */
        RegionFillRule		rule);				/* ODD_EVEN or WINDING */

Fill an area whose outline is defined by the GState's path.

Include: graphics.h

GrFillPolygon()

void	GrFillPolygon(
        GStateHandle		gstate,					/* GState to draw to */
        RegionFillRule		windingRule,					/* ODD_EVEN or WINDING */
        const	Point	* points,					/* array of points in polygon */
        word		numPoints);					/* number of points in array */

This routine draws a filled polygon. The polygon is defined by the passed array of points.

Include: graphics.h

GrFillRect()

void	GrFillRect(
        GStateHandle		gstate,				/* GState to draw to */
        sword		left,				/* bounds of rectangle */
        sword		top,
        sword		right,
        sword		bottom);

Draw a filled rectangle.

Include: graphics.h

GrFillRectTo()

void	GrFillRectTo(
        GStateHandle		gstate,				/* GState to draw to */
        sword		x,				/* opposite corner of rectangle */
        sword		y);

Draw a filled rectangle. The current position will define one of the corners.

Include: graphics.h

GrFillRoundRect()

void	GrFillRoundRect(
        GStateHandle		gstate,					/* GState to draw to */
        sword		left,					/* bounds of rectangle */
        sword		top,
        sword		right,
        sword		bottom
        word 		cornerRadius);					/* radius of corner rounding */

Draw a filled rounded rectangle.

Include: graphics.h

GrFillRoundRectTo()

void	GrFillRoundRectTo(
        GStateHandle		gstate,				/* GState to draw to */
        sword		x,				/* opposite corner of rectangle */
        sword		y
        word 		cornerRadius);					/* radius of corner roundings */

Draw a filled rounded rectangle, using the current position to define one corner of the bounding rectangle.

Include: graphics.h

GrFindNearestPointsize()

Boolean	GrFindNearestPointsize( /* If false, then FontID invalid */
        FontID 		id,					/* fond ID */
        dword		sizeSHL16,					/* point size */
        TextStyle 		styles,					/* style */
        TextStyle 		* styleFound,						/* buffer for style */
        dword		* sizeFoundSHL16);						/* buffer for size */

Find the nearest available point size for a font. If the font passed in id exists, then styleFound will point to the styles available and sizeFoundSHL16 will point to the nearest point size to that passed. If the font is not found, the return valued will be true .

Include: font.h

GrFontMetrics()

dword	GrFontMetrics(
        GStateHandle		gstate,				/* subject GState */
        GFM_info		info);				/* Type of information to return */

Get metrics information about a font. It returns the requested information based on the info parameter.

Structures:

typedef enum /* word */ {
    GFMI_HEIGHT,				/* return = val << 16 */
    GFMI_MEAN,				/* return = val << 16 */
    GFMI_DESCENT,				/* return = val << 16 */
    GFMI_BASELINE,				/* return = val << 16 */
    GFMI_LEADING,				/* return = val << 16 */
    GFMI_AVERAGE_WIDTH,				/* return = val << 16 */
    GFMI_ASCENT,				/* return = val << 16 */
    GFMI_MAX_WIDTH,				/* return = val << 16 */
    GFMI_MAX_ADJUSTED_HEIGHT,				/* return = val << 16 */
    GFMI_UNDER_POS,				/* return = val << 16 */
    GFMI_UNDER_THICKNESS, 				/* return = val << 16 */
    GFMI_ABOVE_BOX,				/* return = val << 16 */
    GFMI_ACCENT,				/* return = val << 16 */
    GFMI_MANUFACTURER,				/* return = val */
    GFMI_KERN_COUNT, 				/* return = Char */
    GFMI_FIRST_CHAR, 				/* return = Char */
    GFMI_LAST_CHAR, 				/* return = FontMaker */
    GFMI_DEFAULT_CHAR,				/* return = Char */
    GFMI_STRIKE_POS,				/* return = Char */
    GFMI_BELOW_BOX, 				/* return = Char */
    GFMI_HEIGHT_ROUNDED				/* return = Char */
    GFMI_DESCENT_ROUNDED, 				/* return = Char */
    GFMI_BASELINE_ROUNDED, 				/* return = Char */
    GFMI_LEADING_ROUNDED, 				/* return = Char */
    GFMI_AVERAGE_WIDTH_ROUNDED,				/* return = Char */
    GFMI_ASCENT_ROUNDED, 				/* return = Char */
    GFMI_MAX_WIDTH_ROUNDED, 				/* return = Char */
    GFMI_MAX_ADJUSTED_HEIGHT_ROUNDED, /* ret = Char */
    GFMI_UNDER_POS_ROUNDED, 				/* return = Char */
    GFMI_UNDER_THICKNESS_ROUNDED, /* return = Char */
    GFMI_ABOVE_BOX_ROUNDED, 				/* return = Char */
    GFMI_ACCENT_ROUNDED=, 				/* return = Char */
    GFMI_STRIKE_POS_ROUNDED,				/* return = Char */
    GFMI_BELOW_BOX_ROUNDED				/* return = Char */
} GFM_info;

Include: font.h

GrGetAreaColor()

RGBColorAsDWord 	GrGetAreaColor(
        GStateHandle gstate);			/* GState of which to get color */

Get the color which is being used to fill areas.

Include: graphics.h

GrGetAreaColorMap()

ColorMapMode GrGetAreaColorMap(
        GStateHandle gstate);				/* GState of which to get area color map */

Get the mapping mode used for filling areas with unavailable colors.

Include: graphics.h

GrGetAreaMask()

SysDrawMask GrGetAreaMask(
        GStateHandle		gstate,			/* GState of which to get mask */
        DrawMask		* dm);			/* buffer for returned mask */

Get the draw mask used when filling areas. The dm argument should point to a buffer capable of holding at least eight bytes to get the bit-pattern of the mask; otherwise dm should be NULL. The returned buffer is the 8x8 bit pattern: each byte represents a row of the pattern, and the bytes are ordered from top row to bottom.

Include: graphics.h

GrGetAreaPattern()

GraphicPattern 	GrGetAreaPattern(
        GStateHandle 		gstate,					/* GState of area pattern */
        const MemHandle		* customPattern,					/* pointer to handle of block for
                              * returned custom pattern */
        word 		* customSize);					/* pointer to size of returned
                             * buffer */

Get the area pattern used when filling areas.

Include: graphics.h

GrGetBitmap()

MemHandle GrGetBitmap(
        GStateHandle		gstate,					/* GState containing bitmap */
        sword		x,					/* bitmap origin */
        sword		y,
        word		width,					/* bitmap width and height */
        word		height,
        XYSize	 	* sizeCopied);					/* buffer for returned size */

Dump an area of the display to a bitmap. The handle of a block containing the bitmap is returned; the sizeCopied pointer points to the actual size of the bitmap successfully copied.

Include: graphics.h

GrGetBitmapMode()

BitmapMode 	GrGetBitmapMode(
        GStateHandle gstate);				/* GState containing bitmap */

Get mode bits for an editable bitmap.

Include: graphics.h

GrGetBitmapRes()

XYValueAsDWord GrGetBitmapRes(
        const Bitmap		* bm);				/* pointer to the bitmap */

Get the resolution of a bitmap.

Include: graphics.h

GrGetBitmapSize()

XYValueAsDWord GrGetBitmapSize(
        const	Bitmap	* bm);				/* pointer to the bitmap */

Get the dimensions, in points, of a bitmap.

Include: graphics.h

GrGetClipRegion()

MemHandle GrGetClipRegion(
        GStateHandle		gstate,			/* subject GState */
        RegionFillRule		rule);			/* ODD_EVEN or WINDING */

Get the current clip region. A null handle (zero) will be returned if no clip paths are se for the GState.

Include: graphics.h

GrGetCurPos()

XYValueAsDWord GrGetCurPos(
        GStateHandle gstate);				/* subject GState */

Get the current pen position.

Include: graphics.h

GrGetCurPosWWFixed()

void GrGetCurPosWWFixed(
        GStateHandle gstate,				/* subject GState */
        PointWWFixed 	*cp);			/* buffer in which to return cur. pos. */

Get the current pen position.

Include: graphics.h

GrGetDefFontID()

FontID	GrGetDefFontID(
        dword	* sizeSHL16);		/* pointer to buffer for returned size */

Get the system default font (including size).

Include: font.h

GrGetExclusive()

GStateHandle 	GrGetExclusive(
        GeodeHandle videoDriver);

Use this routine to find out which GState, if any, has grabbed the video exclusive.

Include: graphics.h

GrGetFont()

FontID	GrGetFont(
        GStateHandle		gstate,				/* subject GState */
        WWFixedAsDWord		* pointSize);				/* pointer to buffer for
                         * returned point size */

Get the passed GState's current font, including point size.

Include: graphics.h

GrGetFontName()

FontID 	GrGetFontName(
        FontID 		id,			/* ID of font */
        const char 		* name);			/* buffer for returned name string */

Get the string name of a font. Note that if the returned FontID is zero, then the font was not found. The name string buffer should be a least FID_NAME_LEN in size.

Include: font.h

GrGetFontWeight()

FontWeight GrGetFontWeight(
        GStateHandle gstate);				/* GState containing the font */

Get the current font weight set for the passed GState.

Include: font.h

GrGetFontWidth()

FontWidth GrGetFontWidth(
        GStateHandle gstate);				/* GState containing the font */

Get the current font width set for the passed GState.

Include: font.h

GrGetGStringBounds()

void	GrGetGStringBounds(
        GStringHandle		source,				/* GString to be checked */
        GStateHandle		dest,				/* handle of GState to use */
        GSControl		flags,				/* GSControl flags */
        Rectangle		* bounds);				/* returned bounds of GState */

This routine returns the coordinate bounds of the source GString drawn at the current position in the GString. The dest GState will be used if passed; to have no GState restrictions, pass a null handle. The bounds of the smallest containing rectangle will be returned in the structure pointed to by bounds .

Include: gstring.h

GrGetGStringBoundsDWord

void	GrGetGStringBoundsDWord(
        Handle		gstring,				/* GString to be checked */
        GStateHandle		gstate,				/* handle of GState to use */
        GSControl		flags,				/* GSControl flags */
        RectDWord		* bounds);				/* returned bounds of GState */

This routine behaves as GrGetGStringBounds() , but has been alterred to work with 32-bit graphics spaces.

This routine returns the coordinate bounds of aGString drawn at the current position in the GString. The gstate GState will be used if passed; to have no GState restrictions, pass a null handle. The bounds of the smallest containing rectangle will be returned in the structure pointed to by bounds .

Include: gstring.h

GrGetGStringElement()

GStringElement GrGetGStringElement(
        GStateHandle		gstate,					/* handle of GString's GState */
        void 		* buffer,					/* pointer to return buffer */
        word 		bufSize,					/* size of return buffer */
        word 		* elementSize,					/* size of GString element */
        void		** pointerAfterData);							/* pointer to pointer to
                             * next element in GString */

Extract the next element from a graphics string. The opcode is returned explicitly. The routine's data can be returned in a buffer.

Include: gstring.h

GrGetHugeBitmapSize()

XYValueAsDWord GrGetHugeBitmapSize(
        VMFileHandle		vmFile,
        VMBlockHandle 		vmBlk);

Returns the dimensions (horizontal and vertical) of the passed huge bitmap, as referenced by a VM block and file.

Include: graphics.h

GrGetInfo()

void	GrGetInfo(
        GStateHandle		gstate,			/* GState to get information about */
        GrInfoTypes		type,			/* type of information to get */
        void	 	* data);			/* buffer for returned information */

Get the private data, window handle, or pen position associated with the GState.

Structures:

typedef enum {
    GIT_PRIVATE_DATA,
    GIT_WINDOW,
    GIT_PEN_POS
} GrInfoType

Include: graphics.h

GrGetLineColor()

RGBColorAsDWord GrGetLineColor(
        GStateHandle gstate);				/* subject GState */

Get the color used when drawing lines.

Include: graphics.h

GrGetLineColorMap()

ColorMapMode GrGetLineColorMap(
        GStateHandle gstate);				/* subject GState */

Get the mode used when drawing lines in an unavailable color.

Include: graphics.h

GrGetLineEnd()

LineEnd	GrGetLineEnd(
        GStateHandle gstate);				/* subject GState */

Get the end used when drawing lines.

Include: graphics.h

GrGetLineJoin()

LineJoin GrGetLineJoin(
        GStateHandle gstate);				/* subject GState */

Get the join used when drawing corners.

Include: graphics.h

GrGetLineMask()

SysDrawMask GrGetLineMask(
        GStateHandle		gstate,			/* subject GState */
        DrawMask		* dm);			/* buffer for returned custom mask */

Get the drawing mask used when drawing lines. The dm argument should point to a buffer capable of holding at least eight bytes to get the bit-pattern of the mask; otherwise dm should be NULL. The returned buffer is the 8x8 bit pattern: each byte represents a row of the pattern, and the bytes are ordered from top row to bottom.

Include: graphics.h

GrGetLineStyle()

LineStyle GrGetLineStyle(
        GStateHandle gstate);				/* subject GState */

Get the style, or "dottedness," used when drawing lines.

Include: graphics.h

GrGetLineWidth()

WWFixedAsDWord 	GrGetLineWidth(
        GStateHandle gstate);				/* subject GState */

Get the current line width.

Include: graphics.h

GrGetMaskBounds()

void	GrGetMaskBounds(
        GStateHandle		gstate,				/* subject GState */
        Rectangle		* bounds);				/* buffer for returned bounds */

Get the 16-bit bounds of the current clip rectangle.

Include: graphics.h

GrGetMaskBoundsDWord()

void	GrGetMaskBoundsDWord(
        GStateHandle		gstate,				/* subject GState */
        RectDWord		* bounds);				/* buffer for returned bounds */

Get the 16-bit bounds of the current clip rectangle, accurate to a fraction of a point.

Include: graphics.h

GrGetMiterLimit()

WWFixedAsDWord GrGetMiterLimit(
        GStateHandle gstate);						/* subject GState */

Get the miter limit to use when drawing mitered corners.

Include: graphics.h

GrGetMixMode()

MixMode GrGetMixMode(
        GStateHandle gstate);					/* subject GState */

Get the current mixing mode.

Include: graphics.h

GrGetPalette()

MemHandle GrGetPalette(
        GStateHandle		gstate,					/* subject GState */
        GetPalType		flag,					/* GPT_ACTIVE, GPT_CUSTOM, or
                             * GPT_DEFAULT */
        word	 	* numEntries);					/* number of entries in block */

Return all or part of the window's color lookup table. This routine returns the handle of a block containing all the returned palette entries.

Include: graphics.h

GrGetPath()

MemHandle 	GrGetPath(
        GStateHandle		gstate,				/* subject GState */
        GetPathType 		ptype);				/* Which path to retrieve */

Returns handle to block containing path data. Either the current path, the clipping path, or the window clipping path may be retrieved.

Include: graphics.h

GrGetPathBounds()

Boolean	GrGetPathBounds(
        GStateHandle		gstate,				/* subject GState */
        GetPathType 		ptype,
        Rectangle		* bounds);				/* buffer for returned bounds */

Returns the rectangular bounds that encompass the current path as it would be filled. A true return value indicates an error occurred or there was no path for the GState.

Include: graphics.h

GrGetPathBoundsDWord()

Boolean	GrGetPathBoundsDWord(
        GStateHandle		gstate,				/* subject GState */
        GetPathType		ptype,
        RectDWord		* bounds);				/* buffer for returned bounds */

Returns the rectangular bounds that encompass the current path as it would be filled. A true return value indicates an error occurred or there was no path for the GState.

Include: graphics.h

GrGetPathPoints()

MemHandle 	GrGetPathPoints(
        GStateHandle		gstate,					/* subject GState */
        word		resolution);					/* dots per inch */

Returns a series of points that fall along the current path. The returned points are in document coordinates.

Include: graphics.h

GrGetPathRegion()

MemHandle GrGetPathRegion(
        GStateHandle		gstate,				/* subject GState */
        RegionFillRule		rule);				/* ODD_EVEN or WINDING */

Get the region enclosed by a path.

Include: graphics.h

GrGetPoint()

RGBColorAsDWord GrGetPoint(
        GStateHandle		gstate,			/* subject GState */
        sword		x,			/* coordinates of pixel */
        sword		y);

Get the color of the pixel corresponding to the specified coordinates.

Include: graphics.h

GrGetPtrRegBounds()

word	GrGetPtrRegBounds( /* Returns size of Region data struct. */
        const	Region	* reg,				/* pointer to region */
        Rectangle		* bounds);				/* returned bounds of region */

Get the bounds of the passed region.

Include: graphics.h

GrGetSubscriptAttr()

ScriptAttrAsWord GrGetSubscriptAttr(
        GStateHandle gstate);					/* subject GState */

Get the GState's subscript drawing attributes. The high byte of the return value is the percentage of the font size for the subscript; the low byte is the percentage of the font size from the top at which the character gets drawn.

Include: font.h

GrGetSuperscriptAttr()

ScriptAttrAsWord GrGetSuperscriptAttr(
        GStateHandle gstate);					/* subject GState */

Get the GState's superscript drawing attributes. The high byte of the return value is the percentage of the font size for the superscript; the low byte is the percentage of the font size from the bottom at which the character gets drawn.

Include: font.h

GrGetTextBounds()

Boolean	GrGetTextBounds(
        GStateHandle		gstate,			/* subject GState */
        const char		* str,			/* text string */
        word 		xpos,			/* position where text would be drawn */
        word 		ypos,
        word 		count,			/* max number of characters to check */
        Rectangle 		* bounds);				/* returned bounding rectangle */

Get the bounds required to draw the passed text. If the passed count argument is zero, the string is assumed to be null-terminated.

Include: graphics.h

GrGetTextColor()

RGBColorAsDWord GrGetTextColor(
        GStateHandle gstate);				/* subject GState */

Get the color used when drawing text.

Include: graphics.h

GrGetTextColorMap()

ColorMapMode 	GrGetTextColorMap(
        GStateHandle gstate);				/* subject GState */

Get the mode used when drawing text in an unavailable color.

Include: graphics.h

GrGetTextMask()

SystemDrawMask 	GrGetTextMask(
        GStateHandle		gstate,			/* subject GState */
        DrawMask		* dm);			/* returned custom mask, if any */

Get the draw mask used when drawing text.The dm argument should point to a buffer capable of holding at least eight bytes to get the bit-pattern of the mask; otherwise dm should be NULL. The returned buffer is the 8x8 bit pattern: each byte represents a row of the pattern, and the bytes are ordered from top row to bottom.

Include: graphics.h

GrGetTextMode()

TextMode	GrGetTextMode(
        GStateHandle gstate);				/* subject GState */

Get the text mode, including information about the vertical offset used when drawing text.

Include: graphics.h

GrGetTextPattern()

GraphicPattern 	GrGetTextPattern(
        GStateHandle	 	gstate,						/* subject GState */
        const MemHandle		* customPattern,						/* pointer to returned handle
                                 * of block containing the
                                 * returned pattern */
        word		* customSize);						/* size of returned block */

Get the graphics pattern used when drawing text.

Include: graphics.h

GrGetTextSpacePad()

WWFixedAsDWord GrGetTextSpacePad(
        GStateHandle gstate);				/* subject GState */

Get the space pad used when drawing strings of text.

Include: graphics.h

GrGetTextStyle()

TextStyle 	GrGetTextStyle(
        GStateHandle gstate);					/* subject GState */

Get the style used when drawing text.

Include: graphics.h

GrGetTrackKern()

word 	GrGetTrackKern(
        GStateHandle 	gstate);				/* subject GState */

Get the track kerning used when drawing strings of text.

Include: graphics.h

GrGetTransform()

void	GrGetTransform(
        GStateHandle		gstate,			/* subject GState */
        TransMatrix		* tm);			/* pointer to returned TransMatrix */

Get the current coordinate transformation, expressed as a matrix.

Include: graphics.h

GrGetWinBounds()

void	GrGetWinBounds(
        GStateHandle		gstate,				/* subject GState */
        Rectangle		* bounds);				/* returned window bounds */

Get the bounds of the GState's associated window.

Include: graphics.h

GrGetWinBoundsDWord()

void	GrGetWinBoundsDWord(
        GStateHandle		gstate,				/* subject GState */
        RectDWord		* bounds);				/* returned window bounds */

Get the bounds of the GState's associated window, accurate to a fraction of a point.

Include: graphics.h

GrGetWinHandle()

WindowHandle GrGetWinHandle(
        GStateHandle gstate);					/* subject GState */

Get the handle of the GState's associated window.

Include: graphics.h

GrGrabExclusive()

GStateHandle GrGrabExclusive(
        GeodeHandle		videoDriver,				/* NULL for default */
        GStateHandle		gstate);				/* subject GState */

Start drawing exclusively to a video driver.

Include: graphics.h

GrInitDefaultTransform()

void	GrInitDefaultTransform(
        GStateHandle gstate);					/* subject GState */

Initialize the GState's default transformation to hold the value of the current transformation.

Include: graphics.h

GrInvalRect()

void	GrInvalRect(
        GStateHandle		gstate,				/* subject GState */
        sword		left,				/* bounds to be invalidated */
        sword		top,
        sword		right,
        sword		bottom);

Invalidate the passed rectangular area. This area will be redrawn.

Include: graphics.h

GrInvalRectDWord()

void	GrInvalRectDWord(
        GStateHandle		gstate,				/* subject GState */
        const	RectDWord	* bounds);				/* bounds to be invalidated */

Invalidate the passed rectangular area. This area will be redrawn.

Include: graphics.h

GrLabel()

void	GrLabel(
        GStringHandle		gstate,			/* subject GState */
        word			label);			/* label to write to GString */

Write the passed label into the passed GString.

Include: gstring.h

GrLoadGString()

Handle GrLoadGString(
        Handle		han,				/* handle of GString source */
        GStringType	hanType,			/* handle type */
        word		vmBlock);			/* if VM file, handle of VM block */

Load a graphics string from a file. Used with stream, VM, and pointer addressed GStrings.

When done with the GString, you may free it via GrDestroyGString().

Include: gstring.h

GrMapColorIndex()

RGBColorAsDWord GrMapColorIndex(
        GStateHandle		gstate,			/* GState to use for mapping */
        Color		c);			/* source color to be mapped */

Map a color index to its RGB equivalent using the color mapping scheme of the passed GState.

Include: graphics.h

GrMapColorRGB()

RGBColorAsDWord GrMapColorRGB(
        GStateHandle		gstate,			/* GState to use for mapping */
        word		red,			/* RGB values to map */
        word		green,
        word		blue);

Map an RGB color to an index.

Include: graphics.h

GrMoveReg()

void	GrMoveReg(
        Region	* reg,	/* pointer to region */
        sword	xOffset,	/* amount to shift horizontally */
        sword	yOffset);	/* amount to shift vertically */

Moves a region a given amount. Note that this operation affects only the region's data structure. The region must be redrawn or used in some other way for the changes to have any visible effect.

Include: graphics.h

GrMoveTo()

void	GrMoveTo(
        GStateHandle		gstate,			/* subject GState */
        sword		x,			/* new absolute pen position */
        sword		y);

Change the pen position.

Include: graphics.h

GrMoveToWWFixed()

void GrMoveToWWFixed(
        GStateHandle gstate,
        WWFixedAsDWord x,
        WWFixedAsDWord y);

This routine changes the pen position very precisely.

GrMulDWFixed()

void	GrMulDWFixed(
        const	DWFixed	* i,				/* first number */
        const	DWFixed	* j,				/* second number */
        DWFixed		* result);				/* pointer to returned result */

Multiply two fixed point numbers.

Include: graphics.h

GrMulWWFixed()

WWFixedAsDWord GrMulWWFixed(
        WWFixedAsDWord i,			/* first number */
        WWFixedAsDWord j);			/* second number */

Multiply two fixed point numbers.

Include: graphics.h

GrNewPage()

void	GrNewPage(
        GStateHandle 		gstate,
        PageEndCommand 		pageEndCommand);

Begin drawing a new page. Normally used when printing documents.

Include: graphics.h

GrNullOp()

void	GrNullOp(
        GStateHandle gstate);				/* subject GState */

Write a null operation element to a GString.

Include: graphics.h

GrParseGString()

void	GrParseGString(
        GStateHandle gstate,
        GStateHandle gstringToDraw,
        GSControl flags,
        Boolean (*callback) /* TRUE to stop */ (void *element));

This routine traverses a GString, calling a callback function on each GStringElement encountered.

Include: gstring.h

GrQuickArcSine()

WWFixedAsDWord GrQuickArcSine(
        WWFixedAsDWord		deltaYDivDistance,						/* delta y / distance */
        word		origDeltaX);						/* original delta x */

Compute a fixed point arcsine. Angles are given in degrees counterclockwise of the positive x axis.

Include: graphics.h

GrQuickCosine()

WWFixedAsDWord GrQuickCosine(
        WWFixedAsDWord angle);				/* angle to cosine */

Compute a fixed point cosine. Angles are given in degrees counterclockwise of the positive x axis.

Include: graphics.h

GrQuickSine()

WWFixedAsDWord GrQuickSine(
        WWFixedAsDWord angle);				/* angle to sine */

Compute a fixed point sine. Angles are given in degrees counterclockwise of the positive x axis.

Include: graphics.h

GrQuickTangent()

WWFixedAsDWord GrQuickTangent(
        WWFixedAsDWord angle);				/* angle to tangent */

Compute a fixed point tangent. Angles are given in degrees counterclockwise of the positive x axis.

Include: graphics.h

GrReleaseExclusive()

void 	GrReleaseExclusive( /* TRUE if system had to force a redraw */
        GeodeHandle		videoDriver,				/* handle of video driver */
        GStateHandle		gstate,				/* GState that was drawing */
        Rectangle 		*bounds);				/* Bounds of aborted drawings */

Stop drawing exclusively to a video driver.

Include: graphics.h

GrRelMoveTo()

void	GrRelMoveTo(
        GStateHandle		gstate,			/* subject GState */
        WWFixedAsDWord 		x,			/* offsets to new pen position */
        WWFixedAsDWord 		y);

Change the pen position to coordinate expressed relative to the current position.

Include: graphics.h

GrRestoreState()

void	GrRestoreState(
        GStateHandle gstate);				/* subject GState */

Restore the values of a saved GState.

Include: graphics.h

GrSaveState()

void	GrSaveState(
        GStateHandle gstate);				/* subject GState */

Save the values of a GState, so that they may be restored by GrRestoreState() .

Include: graphics.h

GrSDivDWFByWWF()

void GrSDivDWFByWWF(
        const DWFixed 		* dividend,
        const WWFixed 		* divisor,
        DWFixed 		* quotient)				/* returned value */

Divide two fixed point numbers.

Include: graphics.h

GrSDivWWFixed()

WWFixedAsDWord GrSDivWWFixed(
        WWFixedAsDWord dividend,
        WWFixedAsDWord divisor)

Divide two fixed point numbers.

Include: graphics.h

GrSetAreaAttr()

void	GrSetAreaAttr(
        GStateHandle		gstate,			/* subject GState */
        const AreaAttr 		* aa);			/* AreaAttr structure */

Set all of the attributes used when filling areas.

Include: graphics.h

GrSetAreaColor()

void	GrSetAreaColor(
        GStateHandle 		gstate,				/* GState to set color for */
        ColorFlag 		flag,				/* flag of how to set color */
        word		redOrIndex,				/* color index or red RGB value */
        word		green,				/* green RGB value or zero */
        word		blue);				/* blue RGB value or zero */

Set the color to use when filling areas. The flag parameter may be CF_RGB (to set RGB values), CF_INDEX (to set a palette index), CF_GRAY, or CF_SAME.

Include: graphics.h

GrSetAreaColorMap()

void	GrSetAreaColorMap(
        GStateHandle 		gstate,				/* subject GState */
        ColorMapMode 		colorMap);				/* color mapping mode */

Set mode to use when trying to fill an area with an unavailable color.

Include: graphics.h

GrSetAreaMaskCustom()

void	GrSetAreaMaskCustom(
        GStateHandle 		gstate,				/* subject GState */
        const	DrawMask  	* dm);				/* pointer to new custom mask */

Set the drawing mask to use when filling areas.

Include: graphics.h

GrSetAreaMaskSys()

void	GrSetAreaMaskSys(
        GStateHandle 		gstate,				/* subject GState */
        SystemDrawMask 		sysDM);				/* new system area mask */

Set the drawing mask to use when filling areas.

Include: graphics.h

GrSetAreaPattern()

void 	GrSetAreaPattern(
        GStateHandle 		gstate,				/* subject GState */
        GraphicPattern 		pattern);				/* new pattern */

Set the graphics pattern to use when filling areas.

Include: graphics.h

GrSetBitmapMode()

void	GrSetBitmapMode(
        GStateHandle  		gstate,				/* subject GState */
        word 		flags,		 /* BM_EDIT_MASK or BM_CLUSTERED_DITHER */
        MemHandle 		colorCorr);				/* handle of ColorTransfer */

Set the bitmap editing mode. This allows the editing of a bitmap's mask, or turning on clustered dithering.

Include: graphics.h

GrSetBitmapRes()

Boolean	GrSetBitmapRes(
        GStateHandle		gstate,				/* subject GState */
        word		xRes,				/* new resolutions */
        word		yRes);

Set a complex bitmap's resolution.

Include: graphics.h

GrSetClipPath()

void	GrSetClipPath(
        GStateHandle 		gstate,				/* subject GState */
        PathCombineType 		params,				/* how paths should be combined */
        RegionFillRule 		rule);				/* ODD_EVEN or WINDING */

Restrict the clipping region by intersecting it with the passed path.

Include: graphics.h

GrSetClipRect()

void	GrSetClipRect(
        GStateHandle 		gstate,				/* subject GState */
        PathCombineType		flags,				/* how paths should be combined */
        sword		left,				/* bounds of clipping rectangle */
        sword		top,
        sword		right,
        sword		bottom);

Restrict the clipping region by intersecting it with the passed rectangle.

Include: graphics.h

GrSetCustomAreaPattern()

void 	GrSetCustomAreaPattern(
        GStateHandle 		gstate,				/* subject GState */
        GraphicPattern 		pattern,				/* new area pattern */
        const void 	*	patternData,				/* pointer to pattern data */
        word		patternSize); /* size of pattern data buffer */

Set the graphics pattern to use when filling areas.

Include: graphics.h

GrSetCustomTextPattern()

void 	GrSetCustomTextPattern(
        GStateHandle 		gstate,					/* subject GState */
        GraphicPattern		pattern,					/* new pattern */
        const void 		* patternData		);			/* pointer to pattern data */

Set the graphic pattern used when drawing text.

Include: graphics.h

GrSetDefaultTransform()

void	GrSetDefaultTransform(
        GStateHandle gstate);				/* subject GState */

Replace the current coordinate transformation with the default transformation.

Include: graphics.h

GrSetFont()

void	GrSetFont(
        GStateHandle 		gstate,				/* subject GState */
        FontID 		id,				/* new font ID */
        WWFixedAsDWord 		pointSize);				/* new point size */

Set the font to use when drawing text.

Include: graphics.h

GrSetFontWeight()

void	GrSetFontWeight(
        GStateHandle 		gstate,				/* subject GState */
        FontWeight 		weight);				/* new font weight */

Set the font weight to use when drawing text.

Include: font.h

GrSetFontWidth()

void	GrSetFontWidth(
        GStateHandle 		gstate,				/* subject GState */
        FontWidth 		width);				/* new font width */

Set the font width to use when drawing text.

Include: font.h

GrSetGStringBounds()

void	GrSetGStringBounds(
        Handle		gstate,				/* GState or GString handle */
        sword		left,				/* new bounds of GString */
        sword		top,
        sword		right,
        sword		bottom);

Optimization routine which allows you to set bounds values for a GString. This bounds information will be returned by GrGetGStringBounds() whenever that routine is called upon the affected GString.

Include: graphics.h

GrSetGStringPos()

void	GrSetGStringPos(
        GStateHandle		gstate,				/* subject GState */
        GStringSetPosType		type,				/* how to set position */
        word		skip);				/* number of elements to skip */

Set a graphics strings' "playing position." Using this routine, it is possible to draw only selected elements of a GString.

Structures:

typedef ByteEnum GStringSetPosType;
/*	GSSPT_SKIP,
    GSSPT_RELATIVE,
    GSSPT_BEGINNING,
    GSSPT_END		*/

Include: gstring.h

GrSetLineAttr()

void	GrSetLineAttr(
        GStateHandle 		gstate,			/* subject GState */
        const LineAttr 		* la);			/* new line attributes */

Set all attributes to use when drawing lines and corners.

Include: graphics.h

GrSetLineColor()

void	GrSetLineColor(
        GStateHandle 		gstate,				/* subject GState */
        ColorFlag 		flag,				/* color flag */
        word		redOrIndex,				/* new index or red RGB value */
        word		green,				/* new green RGB value or zero */
        word		blue);				/* new blue RGB value or zero */

Set the color to use when drawing lines.

Include: graphics.h

GrSetLineColorMap()

void	GrSetLineColorMap(
        GStateHandle gstate,				/* subject GState */
        ColorMapMode colorMap);				/* new color map mode for lines */

Set the mode to use when trying to draw lines in an unavailable color.

Include: graphics.h

GrSetLineEnd()

void	GrSetLineEnd(
        GStateHandle 		gstate,				/* subject GState */
        LineEnd 		end);				/* new line end specification */

Set the end to use when drawing lines.

Include: graphics.h

GrSetLineJoin()

void	GrSetLineJoin(
        GStateHandle 		gstate,				/* subject GState */
        LineJoin 		join);				/* new line join specification */

Set the line join to use when drawing corners.

Include: graphics.h

GrSetLineMaskCustom()

void	GrSetLineMaskCustom(
        GStateHandle 		gstate,				/* subject GState */
        const	DrawMask  	* dm);				/* new line draw mask */

Set the drawing mask used when drawing lines.

Include: graphics.h

GrSetLineMaskSys()

void	GrSetLineMaskSys(
        GStateHandle 		gstate,				/* subject GState */
        SystemDrawMask 		sysDM);				/* the new system line mask */

Set the drawing mask used when drawing lines.

Include: graphics.h

GrSetLineStyle()

void	GrSetLineStyle(
        GStateHandle	 	gstate,					/* subject GState */
        LineStyle 		style,					/* new line style */
        word		skipDistance,					/* skip distance to first pair */
        const DashPairArray		* dpa,					/* dash definition */
        word		numPairs);					/* number of pairs */

Set the style, or "dottedness," to use when drawing lines.

Include: graphics.h

GrSetLineWidth()

void	GrSetLineWidth(
        GStateHandle 		gstate,				/* subject GState */
        WWFixedAsDWord 		width);				/* new line width */

Set the line width to use when drawing lines.

Include: graphics.h

GrSetMiterLimit()

void	GrSetMiterLimit(
        GStateHandle 		gstate,				/* subject GState */
        WWFixedAsDWord 		limit);				/* new miter limit */

Set the miter limit to use when drawing mitered corners.

Include: graphics.h

GrSetMixMode()

void	GrSetMixMode(
        GStateHandle 		gstate,				/* subject GState */
        MixMode 		mode);				/* new mix mode */

Set the GState's mix mode, used to determine what happens when something is drawn on top of an existing drawing.

Include: graphics.h

GrSetNullTransform()

void	GrSetNullTransform(
        GStateHandle gstate);				/* subject GState */

Clear the coordinate transformation. Most applications will actually want to replace the coordinate transformation with the default transformation using GrSetDefaultTransform() .

Include: graphics.h

GrSetPalette()

void	GrSetPalette(
        GStateHandle 		gstate,				/* subject GState */
        SetPalType 		type,				/* SPT_DEFAULT or SPT_CUSTOM */
        const RGBValue 		*buffer,				/* array of palette entries */
        word		index, 				/* First element to change */
        word		numEntries);				/* number of entries in array */

Set one or more entries in a palette, a window's color lookup table.

Include: graphics.h

GrSetPaletteEntry()

void	GrSetPaletteEntry(
        GStateHandle 		gstate,				/* subject GState */
        word		index,				/* index in palette to set */
        word		red,				/* new RGB color values for entry */
        word		green,
        word		blue);

Set one entry in a palette, a GState's color lookup table.

Include: graphics.h

GrSetPrivateData()

void	GrSetPrivateData(
        GStateHandle		gstate,				/* subject GState */
        word		dataAX,				/* data to set */
        word		dataBX,
        word		dataCX,
        word		dataDX);

Set the private data for a GState.

Include: graphics.h

GrSetStrokePath()

void	GrSetStrokePath(
        GStateHandle gstate);				/* subject GState */

Replace a GState's path with the path resulting from stroking the original path. Note that this stroked path may be drawn, but may not be used for clipping.

Include: graphics.h

GrSetSubscriptAttr()

void 	GrSetSubscriptAttr(
        GStateHandle 		gstate,				/* subject GState */
        ScriptAttrAsWord 		attrs);				/* new subscript percentages */

Get the attributes used when drawing subscript characters.

Include: font.h

GrSetSuperscriptAttr()

void 	GrSetSuperscriptAttr(
        GStateHandle 		gstate,				/* subject GState */
        ScriptAttrAsWord 		attrs);				/* new superscript percentages */

Get the attributes used when drawing superscript characters.

Include: font.h

GrSetTextAttr()

void	GrSetTextAttr(
        GStateHandle		gstate,				/* subject GState */
        const	TextAttr	* ta);				/* pointer to text attributes */

Set all attributes used when drawing characters and text strings.

Include: graphics.h

GrSetTextColor()

void	GrSetTextColor(
        GStateHandle		gstate,				/* subject GState */
        ColorFlag		flag,				/* color flag */
        word		redOrIndex,				/* palette index or red RGB value */
        word		green,				/* green RGB value or zero */
        word		blue);				/* blue RGB value or zero */

Set the color used when drawing text.

Include: graphics.h

GrSetTextColorMap()

void	GrSetTextColorMap(
        GStateHandle		gstate,				/* subject GState */
        ColorMapMode 		colorMap);				/* new color mapping mode */

Set the mode used when trying to draw text in an unavailable color.

Include: graphics.h

GrSetTextMaskCustom()

void	GrSetTextMaskCustom(
        GStateHandle		gstate,				/* subject GState */
        const	DrawMask	* dm);				/* pointer to custom mask */

Set the drawing mask used when drawing text.

Include: graphics.h

GrSetTextMaskSys()

void	GrSetTextMaskSys(
        GStateHandle		gstate,				/* subject GState */
        SystemDrawMask 		sysDM);				/* new system draw mask */

Set the drawing mask used when drawing text.

Include: graphics.h

GrSetTextMode()

void	GrSetTextMode(
        GStateHandle		gstate,					/* subject GState */
        TextMode 		bitsToSet,					/* TextMode flags to set */
        TextMode 		bitsToClear);					/* TextMode flags to clear */

Set the text mode associated with a GState. Using this routine, it is possible to change the vertical offset used when drawing text.

Include: graphics.h

GrSetTextPattern()

void 	GrSetTextPattern(
        GStateHandle 		gstate,				/* subject GState */
        GraphicPattern 		pattern);				/* new graphic pattern for text */

Set the graphic pattern used when drawing text.

Include: graphics.h

GrSetTextSpacePad()

void	GrSetTextSpacePad(
        GStateHandle		gstate,				/* subject GState */
        WWFixedAsDWord		padding);				/* new space padding */

Set the space pad used when drawing text strings.

Include: graphics.h

GrSetTextStyle()

void	GrSetTextStyle(
        GStateHandle		gstate,				/* subject GState */
        TextStyle 		bitsToSet,				/* TextStyle flags to set */
        TextStyle		bitsToClear);				/* TextStyle flags to clear */

Set the style to use when drawing text.

Include: graphics.h

GrSetTrackKern()

void	GrSetTrackKern(
        GStateHandle		gstate,				/* subject GState */
        word		tk);				/* degree of track kerning */

Set the track kerning to use when drawing text strings.

Include: graphics.h

GrSetTransform()

void	GrSetTransform(
        GStateHandle		gstate,				/* subject GState */
        const TransMatrix 		* tm);				/* new transformation matrix */

Set the GState's coordinate transformation.

Include: graphics.h

Update the VM file associated with a GState (this may apply when working

GrSetVMFile()

void	GrSetVMFile(
        GStateHandle		gstate,				/* subject GState */
        VMFileHandle 		vmFile);				/* new transformation matrix */

with certain kinds of bitmaps and GStrings).

Include: graphics.h

GrSetWinClipPath()

void	GrSetWinClipPath(
        GStateHandle		gstate,				/* subject GState */
        PathCombineType		params,				/* how paths are combined */
        RegionFillRule		rule);				/* ODD_EVEN or WINDING */

Restrict the window's clipping region by intersecting it with the passed path.

Include: graphics.h

GrSetWinClipRect()

void	GrSetWinClipRect(
        GStateHandle		gstate,				/* subject GState */
        PathCombineType		flags,				/* how paths are combined */
        sword		left,				/* new clipping rectangle bounds */
        sword		top,
        sword		right,
        sword		bottom);

Restrict the window's clipping region by intersecting it with the passed rectangle.

Include: graphics.h

GrSqrRootWWFixed()

WWFixedAsDWord GrSqrRootWWFixed(
        WWFixedAsDWord i);			/* number to get the square root of */

Compute the square root of a fixed point number.

Include: graphics.h

GrTestPath()

Boolean	GrTestPath(
        GStateHandle		gstate,				/* subject GState */
        GetPathType		ptype);				/* Type of path to check for */

Determine whether the GState has a path of the specified type.

Include: graphics.h

GrTestPointInPath()

Boolean	GrTestPointInPath(
        GStateHandle		gstate,				/* subject GState */
        word		xPos,				/* point to test */
        word		yPos,
        RegionFillRule		rule);				/* ODD_EVEN or WINDING */

Determine whether the passed point falls in the interior of the GState's path.

Include: graphics.h

GrTestPointInPolygon()

Boolean	GrTestPointInPolygon(
        GStateHandle		gstate,				/* subject GState */
        RegionFillRule		rule,				/* ODD_EVEN or WINDING */
        Point		* list,				/* array of points in polygon */
        word		numPoints,				/* number of points in array */
        sword		xCoord,				/* coordinates of point to test */
        sword		yCoord);

Determine whether the passed point lies in the interior of the passed polygon.

Include: graphics.h

GrTestPointInReg()

Boolean 	GrTestPointInReg(
        const	Region	* reg,				/* pointer to region */
        sword 		x,				/* coordinates of point to test */
        sword 		y,
        Rectangle		*boundingRect);					/* returned bounding rectangle,
                             * if point in region */

Determine whether a point lies within the passed region. If the point is not in the region, the return value is true .

Include: graphics.h

GrTestRectInMask()

TestRectReturnType GrTestRectInMask(
        GStateHandle 		gstate,
        sword 		left,
        sword 		top,
        sword 		right,
        sword 		bottom);

This routine determines whether the passed rectangular area falls within the clipping region.

Structures:

        typedef ByteEnum TestRectReturnType;
TRRT_OUT,			/* rectangle completely out of region */
TRRT_PARTIAL,			/* rectangle partially in region */
TRRT_IN 			/* rectangle completely in region */

Include: graphics.h

GrTestRectInReg()

TestRectReturnType GrTestRectInReg(
        const Region		* reg			/* pointer to region */
        sword		left,			/* bounds of rectangle to be tested */
        sword		top,
        sword		right,
        sword		bottom);

Determine whether a rectangle lies within the passed region.

Structures:

        typedef ByteEnum TestRectReturnType;
TRRT_OUT,			/* rectangle completely out of region */
TRRT_PARTIAL,			/* rectangle partially in region */
TRRT_IN 			/* rectangle completely in region */

Include: graphics.h

GrTextWidth()

word	GrTextWidth(
        GStateHandle		gstate,				/* subject GState */
        const	Chars	* str,				/* text string to check */
        word		size);				/* maximum number of
                         * characters to check */

Compute the space the passed text string would require in a line of text. Use GrGetTextBounds() to determine the area necessary to render the text.

Include: graphics.h

GrTextWidthWWFixed()

WWFixedAsDWord 	GrTextWidthWWFixed( /* returns width << 16 */
        GStateHandle		gstate,				/* subject GState */
        const	Chars	* str,				/* text string to check */
        word		size)				/* maximum number of
                         * characters to check */

Compute the spacing the passed text string would require in a line of text, accurate to a fraction of a point. Use GrGetTextBounds() to determine the area necessary to render the text.

Include: graphics.h

GrTransform()

XYValueAsDWord 	GrTransform(
        GStateHandle		gstate,				/* subject GState */
        sword 		xCoord,				/* coordinates to transform */
        sword 		yCoord);

Apply the device's transformation to the passed point.

Include: graphics.h

GrTransformDWFixed()

void	GrTransformDWFixed(
        GStateHandle		gstate,				/* subject GState */
        PointDWFixed		* coord);				/* coordinates to transform */

Apply the device's transformation to the passed point.

Include: graphics.h

GrTransformDWord()

void 	GrTransformDWord(
        GStateHandle		gstate,				/* subject GState */
        sdword		xCoord,				/* coordinates to transform */
        sdword		yCoord,
        PointDWord		* deviceCoordinates);
            /* pointer to returned devide coordinates */

Apply the device's transormation to the passed point.

Include: graphics.h

GrTransformWWFixed()

void	GrTransformWWFixed(
        GStateHandle		gstate,				/* subject GState */
        WWFixedAsDWord		xPos,				/* coordinates to transform */
        WWFixedAsDWord		yPos,
        PointWWFixed		* deviceCoordinates);
                    /* pointer to returned devide coordinates */

Apply the device's transormation to the passed point.

Include: graphics.h

GrUDivWWFixed()

WWFixedAsDWord GrUDivWWFixed(
        WWFixedAsDWord		dividend,
        WWFixedAsDWord		divisor);

Compute an unsigned division of two fixed point numbers.

Include: graphics.h

GrUncompactBitmap()

VMBlockHandle GrUncompactBitmap(
        VMFileHandle 		srcFile,
        VMBlockHandle 		srcBlock,
        VMFileHandle 		destFile);

This routine uncompacts the data of a huge bitmap.

GrUntransform()

XYValueAsDWord GrUnTransformCoord(
        GStateHandle		gstate,				/* subject GState */
        sword		xCoord,				/* coordinates to untransform */
        sword		yCoord);

Apply the reverse of the device's transformation to the passed point.

Include: graphics.h

GrUntransformDWFixed()

void	GrUnTransCoordDWFixed(
        GStateHandle		gstate,				/* subject GState */
        PointDWFixed		* coord);				/* coordinates to untransform */

Apply the reverse of the device's transformation to the passed point.

Include: graphics.h

GrUntransformDWord()

void	GrUnTransformExtCoord(
        GStateHandle		gstate,				/* subject GState */
        sdword		xCoord,				/* coordinates to untransform */
        sdword		yCoord,
        PointDWord		* documentCoordinates);
                    /* pointer to returned devide coordinates *

Apply the reverse of the device's transformation to the passed point.

Include: graphics.h

GrUntransformWWFixed()

void	GrUnTransCoordWWFixed(
        GStateHandle		gstate,				/* subject GState */
        WWFixedAsDWord		xPos,				/* coordinates to untransform */
        WWFixedAsDWord		yPos,
        PointWWFixed		* documentCoordinates);
                    /* pointer to returned devide coordinates *

Apply the reverse of the device's transformation to the passed point.

Include: graphics.h

GSApplyRotation()

#define GSApplyRotation(angle_f) \
        GR_APPLY_ROTATION,GOC_WWF(angle_f

This GString creation macro applies a passed WWFixed value angle to the coordinate transformation.

Include: gstring.h

GSApplyScale()

#define GSApplyScale(x_f,y_f) \
        GR_APPLY_SCALE,GOC_WWF(x_f),GOC_WWF(y_f)

This GString creation macro applies the passed scale (defined in terms of two WWFixed numbers, one each for the x and y scale factors) to the coordinate transformation.

Include: gstring.h

GSApplyTransform()

#define GSApplyTransform(e11_f,e12_f,e21_f,e22_f,e31_df,e32_df) \
        GR_APPLY_TRANSFORM,GOC_WWF(e11_f),GOC_WWF(e12_f), \
        GOC_WWF(e21_f),GOC_WWF(e22_f), \
        GOC_DWF(e31_df), GOC_DWF(e32_df)

This GString creation macro applies the passed translation matrix to the coordinate transformation. The translation matrix is determined in terms of six of its elements, four defined by WWFixed values and two by DWFixed values.

Include: gstring.h

GSApplyTranslation()

#define GSApplyTranslation(x_f,y_f) \
        GR_APPLY_TRANSLATION,GOC_WWF(x_f),GOC_WWF(y_f)

This GString creation macro applies the passed translation (defined in terms of two WWFixed numbers, one each for the x and y translations) to the coordinate transformation.

Include: gstring.h

GSApplyTranslationDWord()

#define GSApplyTranslationDWord(x_sdw,y_sdw) \
        GR_APPLY_TRANSLATION_DWORD,GOC_SDW(x_sdw), GOC_SDW(y_sdw)

This GString creation macro applies a large translation (defined in terms of two dword-sized values, one each for the x and y translations) to the transformation.

Include: gstring.h

GSBeginPath()

#define GSBeginPath(flags) GR_BEGIN_PATH,(flags)

This GString creation macro signals that the drawing commands that follow, up until the next GSEndPath() , describe a path. It takes one argument, a PathCombineType .

Include: gstring.h

GSBrushPolyline()

#define GSBrushPolyline(width_b,height_b,pts_b) \
        GR_BRUSH_POLYLINE, GOC_WORD(pts_b), (width_b), (height_b)

This GString creation macro brushes a polyline. It takes three arguments: a word containing the number of points in the polyline and the byte-size width and height of the brush rectangle. Following this macro should appear the points of the polyline, each point passed via a GOC_POINT macro.

Include: gstring.h

GSCloseSubPath()

#define GSCloseSubPath() GR_CLOSE_SUB_PATH

This GString creation macro geometrically closes the currently open path. You must still use GSEndPath() to signal that you are done defining the path.

Include: gstring.h

GSComment()

#define GSComment(size_b) \
        GR_COMMENT, GOC_WORD(size_b

This GString creation macro signals the start of a comment within the GString. It takes one argument, the length of the comment in bytes. Following this macro you should place the comment data.

Include: gstring.h

GSCreatePalette()

#define GSCreatePalette() GR_CREATE_PALETTE

This GString creation macro creates a custom palette.

Include: gstring.h

GSDestroyPalette()

#define GSDestroyPalette() GR_DESTROY_PALETTE

This GString creation macro destroys the custom palette, if any.

Include: gstring.h

GSDrawArc()

#define GSDrawArc(close_enum,x1_w,y1_w,x2_w,y2_w,ang1_w,ang2_w) \
        GR_DRAW_ARC,GOC_WORD(close_enum), \
GOC_SW(x1_w), GOC_SW(y1_w), \
GOC_SW(x2_w), GOC_SW(y2_w), \
GOC_SW(ang1_w),GOC_SW(ang2_w)

This GString creation macro draws an arc. It takes several arguments. First it takes an ArcCloseType value. Next it takes the boundaries of the arc's base ellipse. Finally, it takes starting and ending angles for the arc.

Include: gstring.h

GSDrawArc3Point()

#define GSDrawArc3Point(close, x1, y1, x2, y2, x3, y3) \
        GR_DRAW_ARC_3POINT,GOC_WORD(close),GOC_WWF(x1), \
GOC_WWF(y1), GOC_WWF(x2), GOC_WWF(y2), \
GOC_WWF(x3), GOC_WWF(y3)

This GString creation macro draws a three-point arc. It takes several arguments: an ArcCloseType and the coordinates of the three points defining the arc, each expressed as a WWFixed value.

Include: gstring.h

GSDrawArc3PointTo()

#define GSDrawArc3Point(close, x1, y1, x2, y2, x3, y3) \
        GR_DRAW_ARC_3POINT,GOC_WORD(close),GOC_WWF(x1), \
GOC_WWF(y1), GOC_WWF(x2), GOC_WWF(y2), \
GOC_WWF(x3), GOC_WWF(y3)

This GString creation macro draws a three-point arc with the current position acting as one of the endpoints. It takes several arguments: an ArcCloseType and the coordinates of the remaining defining points of the arc, each expressed as a WWFixed value.

Include: gstring.h

GSDrawBitmap()

#define GSDrawBitmap(x_w,y_w,w_w) \
        GR_DRAW_BITMAP, GOC_SW(x_w), \
GOC_SW(y_w), GOC_WORD(w_w)

This GString creation macro draws a bitmap. It takes three arguments: the coordinates at which to draw the bitmap and the size of the bitmap's data. This macro should be followed by the bitmap's data: a Bitmap structure and the mask and color data for the bitmap.

Include: gstring.h

GSDrawBitmapAtCP()

#define GSDrawBitmapAtCP(bsize_w) \
        GR_DRAW_BITMAP_CP, GOC_WORD(bsize_w)

This GString creation macro draws a bitmap at the current position. It takes one argument, the size of the bitmap data in bytes. This macro should be followed by the bitmap's data: a Bitmap structure and the mask and color data for the bitmap.

Include: gstring.h

GSDrawBitmapOptr()

#define GSDrawBitmapOptr(x_w,y_w,o_ptr) \
        GR_DRAW_BITMAP_OPTR,GOC_SW(x_w), \
GOC_SW(y_w),GOC_OPTR(o_ptr)

This GString creation macro draws a bitmap at the passed coordinates; the data for said bitmap should be stored in a chunk referenced by the passed optr.

Include: gstring.h

GSDrawCBitmap()

#define GSDrawCBitmap(x_w,y_w,slice1size_w,totalSize) \
        GR_DRAW_BITMAP,GOC_SW(x_w), GOC_SW(y_w), \
GOC_WORD(slice1size_w)

This GString creation macro draws a complex bitmap. It takes four arguments: the x and y coordinates at which to draw the bitmap, the size of one slice of the bitmap's mask and color data, and the total size of the bitmap's data. This macro should be followed by the bitmap's data: a CBitmap structure followed by the mask, color, and any other associated data.

Include: gstring.h

GSDrawChar()

#define GSDrawChar(c,x1_w,y1_w) \
        GR_DRAW_CHAR,(c),GOC_SW(x1_w),GOC_SW(y1_w)

This GString creation macro draws a text character. It takes three arguments: the Chars value of the character and the x and y coordinates at which to draw the character.

Include: gstring.h

GSDrawCharAtCP()

#define GSDrawCharAtCP(c) \
        GR_DRAW_CHAR_CP,(c)

This GString creation macro draws a single character at the current position. It takes one argument, the Chars value of the character to draw.

Include: gstring.h

GSDrawCurve()

#define GSDrawCurve(x1_sw,y1_sw,x2_sw,y2_sw,x3_sw,y3_sw,x4_sw,y4_sw) \
        GR_DRAW_CURVE,GOC_SW(x1_sw),GOC_SW(y1_sw), \
GOC_SW(x2_sw),GOC_SW(y2_sw), \
GOC_SW(x3_sw),GOC_SW(y3_sw),GOC_SW(x4_sw),GOC_SW(y4_sw)

This GString creation macro draws a Bezier curve. It takes eight arguments consisting of the coordinates for the curve's anchor and control points.

Include: gstring.h

GSDrawCurveTo()

#define GSDrawCurveTo(x2_sw,y2_sw,x3_sw,y3_sw,x4_sw,y4_sw) \
        GR_DRAW_CURVE_TO,GOC_SW(x2_sw),GOC_SW(y2_sw), \
GOC_SW(x3_sw),GOC_SW(y3_sw), \
GOC_SW(x4_sw),GOC_SW(y4_sw)

This GString creation macro draws a Bezier curve, using the current position as one endpoint. It takes six arugments, the coordinates of the remaining anchor and control points.

Include: gstring.h

GSDrawEllipse()

#define GSDrawEllipse(x1_w,y1_w,x2_w,y2_w) \
        GR_DRAW_ELLIPSE,GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws an ellipse with bounds defined by the passed coordinates.

Include: gstring.h

GSDrawHLine()

#define GSDrawHLine(x1_w,y1_w,x2_w) \
        GR_DRAW_HLINE,GOC_SW(x1_w), \
GOC_SW(y1_w),GOC_SW(x2_w)

This GString creation macro draws a horizontal line. It takes three word-length arguments: the coordinates of the first endpoint and the x coordinate of the second endpoint.

Include: gstring.h

GSDrawHLineTo()

#define GSDrawHLineTo(x2_w) \
        GR_DRAW_HLINE_TO,GOC_SW(x2_w)

This GString creation macro draws a horizontal line from the current position to the passed x coordinate.

Include: gstring.h

GSDrawLine()

#define GSDrawLine(x1_w,y1_w,x2_w,y2_w) \
        GR_DRAW_LINE,GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a line between the two passed points. The points are defined in terms of their x and y coordinates, passed as word values.

Include: gstring.h

GSDrawLineTo()

#define GSDrawLineTo(x2_w,y2_w) \
        GR_DRAW_LINE_TO,GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a line from the current postion to the passed point. The endpoint of the line is passed via two words describing its x and y coordinates.

Include: gstring.h

GSDrawPath()

#define GSDrawPath() GR_DRAW_PATH

This GString creation macro draws the current path.

Include: gstring.h

GSDrawPoint()

#define GSDrawPoint(x1_w,y1_w) \
        GR_DRAW_POINT,GOC_SW(x1_w),GOC_SW(y1_w)

This GString creation macro draws a dot at the passed coordinates.

Include: gstring.h

GSDrawPointAtCP()

#define GSDrawPointAtCP() \
                GR_DRAW_POINT_CP

This GString creation macro draws a dot at the current drawing position.

Include: gstring.h

GSDrawPolygon()

#define GSDrawPolygon(count_w) \
        GR_DRAW_POLYGON,GOC_WORD(count_w)

This GString creation macro draws the outline of a polygon. It takes one argument, the number of points in the polygon. Follow the macro with the points of the polygon, each passed via a GOC_POINT macro.

Include: gstring.h

GSDrawPolyline()

#define GSDrawPolyline(count_w) \
        GR_DRAW_POLYLINE,GOC_WORD(count_w)

This GString creation macro draws a polyline--a spline with corners. The macro takes one argument: the number of points in the polyline. The point data should follow the macro, each point passed via a GOC_POINT macro.

Include: gstring.h

GSDrawRect()

#define GSDrawRect(x1_w,y1_w,x2_w,y2_w) \
        GR_DRAW_RECT,GOC_SW(x1_w),GOC_SW(y1_w), \
        GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a rectangle with bounds defined by the passed coordinates, each coordinate a word-length value.

Include: gstring.h

GSDrawRectTo()

#define GSDrawRectTo(x2_w,y2_w) \
        GR_DRAW_RECT_TO,GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a rectangle with one corner at the current position and the other corner at the point defined by the passed coordinates.

Include: gstring.h

GSDrawRelArc3PointTo()

#define GSDrawRelArc3PointTo(close, x2, y2, x3, y3) \
        GR_DRAW_REL_ARC_3POINT_TO,GOC_WORD(close), \
GOC_WWF(x2), GOC_WWF(y2), GOC_WWF(x3), GOC_WWF(y3)

This GString creation macro draws a three-point arc with the current position acting as one endpoint and the other points coordinates specified as offsets from the coordinates of the current position. In addition to these offsets, this macro has one more argument: an ArcCloseType .

Include: gstring.h

GSDrawRelCurveTo()

#define GSDrawRelCurveTo(x2_sw,y2_sw,x3_sw,y3_sw,x4_sw,y4_sw) \
        GR_DRAW_REL_CURVE_TO,GOC_SW(x2_sw),GOC_SW(y2_sw), \
GOC_SW(x3_sw),GOC_SW(y3_sw), \
GOC_SW(x4_sw),GOC_SW(y4_sw)

This GString creation macro draws a Bezier curve. It uses the current position as one endpoint of the curve; it takes six arguments: the coordinates of the other points of the curve expressed as offsets from the coordinates of the current position.

Include: gstring.h

GSDrawRoundRect()

#define GSDrawRoundRect(x1_w,y1_w,x2_w,y2_w,r_w) \
        GR_DRAW_ROUND_RECT,GOC_WORD(r_w), \
GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a rounded rectangle with bounds specified by its first four arguments and corner radius specified by its final argument. All arguments are word-length values except the radius, which is a word.

Include: gstring.h

GSDrawRoundRectTo()

#define GSDrawRoundRectTo(x2_w,y2_w,r_w) \
        GR_DRAW_ROUND_RECT_TO,GOC_WORD(r_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a rounded rect with bounds defined by the coordinates of the current position and the coordinates of the passed point; the corner radius is specified by the macro's last argument.

Include: gstring.h

GSDrawSpline()

#define GSDrawSpline(count_w) \
        GR_DRAW_SPLINE,GOC_WORD(count_w)

This GString creation macro draws a spline. It takes one argument, the number of points in the spline. Follow this macro with the list of points for the spline; each point should be defined by means of a GOC_POINT macro.

Include: gstring.h

GSDrawSplineTo()

#define GSDrawSplineTo(count_w) \
                GR_DRAW_SPLINE_TO,GOC_WORD(count_w)

This GString creation macro draws a spline using the current postion as one endpoint. It takes one argument, the number of points in the spline. Follow this macro with the list of points for the spline; each point should be defined by means of a GOC_POINT macro.

Include: gstring.h

GSDrawText()

#define GSDrawText(x1_w,y1_w) \
        GR_DRAW_TEXT,GOC_SW(x1_w),GOC_SW(y1_w)

This GString creation macro draws a string of text. Follow the macro with the string of text like so:

GSDrawText(10,10),"foo",

Include: gstring.h

GSDrawTextAtCP()

#define GSDrawTextAtCP() GR_DRAW_TEXT_CP

This GString creation macro draws a string of text at the current position. Follow the macro with the text to draw like so:

GSDrawTextAtCP(),"bar",

Include: gstring.h

GSDrawTextOptr()

#define GSDrawTextOptr(x_w,y_w,o_ptr) \
        GR_DRAW_TEXT_OPTR, GOC_SW(x_w), \
GOC_SW(y_w),GOC_OPTR(o_ptr)

This GString creation macro draws a string of text. It takes three arguments: the x and y coordinates at which to start drawing the text and an optr to the chunk in which the text is stored.

Include: gstring.h

GSDrawVLine()

#define GSDrawVLine(x1_w,y1_w,y2_w) \
        GR_DRAW_VLINE,GOC_SW(x1_w), \
GOC_SW(y1_w),GOC_SW(y2_w)

This GString creation macro draws a vertical line. It takes three arguments: the coordinates of the first endpoint and the y coordinate of the second endpoint.

Include: gstring.h

GSDrawVLineTo()

#define GSDrawVLineTo(y2_w) \
        GR_DRAW_VLINE_TO,GOC_SW(y2_w)

This GString creation macro draws a vertical line from the current position to the passed y coordinate.

Include: gstring.h

GSEndPath()

#define GSEndPath()                             GR_END_PATH

This GString creation macro signals the end of path-defining commands; from here on, drawing commands will draw instead of specifying path strokes.

Include: gstring.h

GSEndString()

#define GSEndString() \
        GR_END_GSTRING

This GString definition macro marks the end of a graphics string.

Include: gstring.h

GSEscape()

#define GSEscape(esc_w,size_w) \
        GR_ESCAPE,GOC_WORD(esc_w),GOC_WORD(size_w)

This GString creation macro inserts an escape code into the GString. The macro's first argument is a word-length escape code. The second argument is the size of any additional escape code data which follows, in bytes. Following this macro, you may place any amount of additional data to associate with the escape code.

Include: gstring.h

GSFillArc()

#define GSFillArc(close_enum,x1_w,y1_w,x2_w,y2_w,ang1_w,ang2_w) \
        GR_FILL_ARC,GOC_WORD(close_enum), \
GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w),GOC_SW(ang1_w), \
GOC_SW(ang2_w)

This GString creation macro draws a filled arc. It takes several arguments: an ArcCloseType value, the bounding coordinates of the arc's base ellipse, and the arc's starting and ending angles.

Include: gstring.h

GSFillArc3Point()

#define GSFillArc3Point(close, x1, y1, x2, y2, x3, y3) \
        GR_FILL_ARC_3POINT,GOC_WORD(close),GOC_WWF(x1), GOC_WWF(y1),\
GOC_WWF(x2), GOC_WWF(y2), GOC_WWF(x3), GOC_WWF(y3)

This GString creation macro fills a three-point arc. It takes several arguments: an ArcCloseType and the coordinates of the points defining the arc.

Include: gstring.h

GSFillArc3PointTo()

#define GSFillArc3PointTo(close, x2, y2, x3, y3) \
        GR_FILL_ARC_3POINT_TO,GOC_WORD(close), \
GOC_WWF(x2), GOC_WWF(y2), GOC_WWF(x3), GOC_WWF(y3)

This GString creation macro fills a three-point arc. The current position acts as one of the arc's endpoints, with the macro's arguments specifying the coordinates of the other points. The macro has one other argument: an ArcCloseType .

Include: gstring.h

GSFillBitmap()

#define GSFillBitmap(x_w,y_w,bsize_w) \
        GR_FILL_BITMAP,GOC_SW(x_w), \
GOC_SW(y_w),GOC_WORD(bsize_w)

This GString creation macro draws a bitmap using the area-filling color. It will draw the bitmap at the passed coordinates; the macro's final argument is the size of the bitmap's data. Following the macro you should place the bitmap's data: first a Bitmap structure, then a number of bytes containing the mask and color values for the bitmap.

Include: gstring.h

GSFillBitmapAtCP()

#define GSFillBitmapAtCP(bsize_w) \
        GR_FILL_BITMAP_CP, GOC_WORD(bsize_w)

This GString creation macro draws a bitmap using the area-filling color at the current position. It takes one argument, the size of the bitmap's data. Following the macro you should place the bitmap's data: first a Bitmap structure, then a number of bytes containing the mask and color values for the bitmap.

Include: gstring.h

GSFillBitmapOptr()

#define GSFillBitmapOptr(x_w,y_w,o_ptr) \
        GR_FILL_BITMAP_OPTR,GOC_SW(x_w),GOC_SW(y_w),GOC_OPTR(o_ptr)

This GString creation macro draws a bitmap using the area-filling color. It does so at the passed coordinates; the bitmap's data should be stored in the chunk referenced by the passed optr.

Include: gstring.h

GSFillEllipse()

#define GSFillEllipse(x1_w,y1_w,x2_w,y2_w) \
        GR_FILL_ELLIPSE,GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a filled ellipse. It takes four arguments, the bounding coordinates of the ellipse.

Include: gstring.h

GSFillPath()

#define GSFillPath(region_fill_rule) \
        GR_FILL_PATH,(region_fill_rule)

This GString creation macro fills the current path using the passed RegionFillRule value.

Include: gstring.h

GSFillPolygon()

#define GSFillPolygon(count_w,fillrule_enum) \
        GR_FILL_POLYGON,GOC_WORD(count_w),(fillrule_enum)

This GString creation macro draws a filled polygon. It takes two arguments, the number of points in the polygon and a RegionFillRule value. This macro should be followed by the points of the polygon, each passed via a GOC_POINT macro.

Include: gstring.h

GSFillRect()

#define GSFillRect(x1_w,y1_w,x2_w,y2_w) \
        GR_FILL_RECT,GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a filled rectangle. The macro takes four arguments, the bounds of the rectangle.

Include: gstring.h

GSFillRectTo()

#define GSFillRectTo(x2_w,y2_w) \
        GR_FILL_RECT_TO ,GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro draws a filled rectangle with the current position defining one corner and the opposite corner defined by the passed coordinates.

Include: gstring.h

GSFillRoundRect()

#define GSFillRoundRect(x1_w,y1_w,x2_w,y2_w,r_w) \
        GR_FILL_ROUND_RECT,GOC_WORD(r_w),GOC_SW(x1_w), \
GOC_SW(y1_w),GOC_SW(x2_w), \
GOC_SW(y2_w)

This GString creation macro draws a filled rounded rectangle. It takes five arguments; the first four give the bounding coordinates of the rectangle; the final argument is the corner radius.

Include: gstring.h

GSFillRoundRectTo()

#define GSFillRoundRectTo(x2_w,y2_w,r_w) \
        GR_FILL_ROUND_RECT_TO,GOC_WORD(r_w),GOC_SW(x2_w), GOC_SW(y2_w)

This GString creation macro draws a filled rounded rectangle. The rectangle's bounding coordinates are defined by the the current position and the two passed coordinates; its corner radius is defined by the macro's final argument.

Include: gstring.h

GSInitDefaultTransform()

#define GSInitDefaultTransform() \
        GR_INIT_DEFAULT_TRANSFORM

This GString creation macro initializes the GState's default transformation to hold the value of the current coordinate transformation.

Include: gstring.h

GSLabel()

#define GSLabel(w) \
        GR_LABEL, GOC_WORD(w)

This GString definition macro creates a label in the GString. This macro takes one argument, a word which acts as the label's token. By passing GSC_LABEL to GrDrawGString() or another GString-traversing command, you may use this label as something analogous to the label used by a goto command.

Include: gstring.h

GSMoveTo()

#define GSMoveTo(x1_w,y1_w) \
        GR_MOVE_TO, GOC_SW(x1_w), GOC_SW(y1_w)

This GString creation macro updates the current position to the passed coordinates.

Include: gstring.h

GSNewPage()

#define GSNewPage(pageEndCommand) \
        GR_NEW_PAGE,(pageEndCommand)

This GString creation macro signals the end of a page. The pageEndCommand argument should be a PageEndCommand value.

Include: gstring.h

GSNullOp()

#define GSNullOp()
        GR_NULL_OP

This GString creation macro inserts a null-operation element into the GString.

Include: gstring.h

GSRelMoveTo()

#define GSRelMoveTo(x1_wwf,y1_wwf) \
        GR_REL_MOVE_TO, GOC_WWF(x1_wwf),GOC_WWF(y1_wwf)

This GString creation macro updates the current position to a set of coordinates specified as offsets from those of the old current position.

Include: gstring.h

GSRestoreState()

#define GSRestoreState() \
        GR_RESTORE_STATE

This GString creation macro causes a GState saved by GSSaveState() to be restored.

Include: gstring.h

GSRestoreTransform()

#define GSRestoreTransform() \
        GR_RESTORE_TRANSFORM

This GString creation macro restores a transformation matrix previously saved via GSSaveTransform() .

Include: gstring.h

GSSaveState()

#define GSSaveState()\
        GR_SAVE_STATE

This GString creation macro causes the GState to be saved so that it may be restored by a subsequent GSRestoreState() .

Include: gstring.h

GSSaveTransform()

#define GSSaveTransform() \
        GR_SAVE_TRANSFORM

This GString creation macro saves the current tranformation matrix which may be retrieved by means of a GSRestoreTransform() .

Include: gstring.h

GSSetAreaAttr()

#define GSSetAreaAttr(r,g,b,mode,mask) \
                GR_SET_AREA_ATTR,(CF_RGB),(r),(g),(b),(mask),(mode)

This GString creation macro sets the AreaAttr value to use when filling areas. It takes several arguments: the red, green, and blue components of the color; the ColorMapMode to use when trying to draw in an unavailable color, and the mask to use.

Include: gstring.h

GSSetAreaColor()

#define GSSetAreaColor(r,g,b) GR_SET_AREA_COLOR,(r),(g),(b)

This GString creation macro specifies a new area-filling color in terms of its red, green, and blue components.

Include: gstring.h

GSSetAreaColorIndex()

#define GSSetAreaColorIndex(index) GR_SET_AREA_COLOR_INDEX,(index)

This GString creation macro specifies the new area-filling color by means of its index in the palette.

Include: gstring.h

GSSetAreaColorMap()

#define GSSetAreaColorMap(mode) GR_SET_AREA_COLOR_MAP,(mode)

This GString creation macro sets the ColorMapMode to use when trying to fill an area using an unavailable color.

Include: gstring.h

GSSetAreaMask()

#define GSSetAreaMask(index) GR_SET_AREA_MASK, (index)

This GString creation macro sets the mask to use when filling areas.

Include: gstring.h

GSSetAreaPattern()

#define GSSetAreaPattern(pattern) \
        GR_SET_AREA_PATTERN,GOC_PATTERN(pattern)

This GString creation macro sets the GraphicPattern to use when filling areas.

Include: gstring.h

GSSetClipPath()

#define GSSetClipPath(flags) GR_SET_CLIP_PATH,(flags)

This GString creation macro combines the current path with the clipping path using the passed PathCombineType .

Include: gstring.h

GSSetClipRect()

#define GSSetClipRect(path,rl_sw,rt_sw,rr_sw,rb_sw) \
        GR_SET_CLIP_RECT,GOC_SW(rl_sw), \
GOC_SW(rt_sw),GOC_SW(rr_sw),GOC_SW(rb_sw)

This GString creation macro sets a clipping rectangle. It takes five arguments: a PathCombineType and the bounding coordinates of the rectangle.

Include: gstring.h

GSSetCustomAreaMask()

#define GSSetCustomAreaMask(m1,m2,m3,m4,m5,m6,m7,m8) \
        GR_SET_CUSTOM_AREA_MASK, (m1), (m2), (m3), (m4), (m5), (m6), (m7),
(m8)

This GString creation macro specifies a custom bitmask to use when filling areas. It takes eight arguments, each a byte representing the pixels of one row.

Include: gstring.h

GSSetCustomAreaPattern()

#define GSSetCustomAreaPattern(pattern,size) \
        GR_SET_CUSTOM_AREA_PATTERN, GOC_PATTERN(pattern),GOC_WORD(size)

This GString creation macro sets up a custom pattern to use when filling areas. It takes two arguments: a GraphicPattern value and the size of the pattern's data. Following this macro should appear the pattern's data.

Include: gstring.h

GSSetCustomLineMask()

#define GSSetCustomLineMask(m1,m2,m3,m4,m5,m6,m7,m8) \
        GR_SET_CUSTOM_LINE_MASK, (m1), (m2), (m3), (m4), (m5), (m6), (m7), \
(m8)

This GString creation macro specifies a new bitmap to use as the mask when drawing lines. It takes eight arguments, each a byte representing the pixels one one row.

Include: gstring.h

GSSetCustomLineStyle()

#define GSSetCustomLineStyle(index,count) \
        GR_SET_CUSTOM_LINE_STYLE,(index),(count)

This GString creation macro sets a custom style, or "dottedness," to use when drawing lines. It takes two arguments: the first is the index into the pattern to use, the second is the number of dash pairs in the line style. Following this macro should appear a DashPairArray containing the line style's data.

Include: gstring.h

GSSetCustomTextMask()

#define GSSetCustomTextMask(m1,m2,m3,m4,m5,m6,m7,m8) \
        GR_SET_CUSTOM_TEXT_MASK, (m1), (m2), (m3), (m4), (m5), (m6), (m7),
(m8)

This GString creation macro specifies a custom bitmask to use when rendering text. It takes eight arguments, each a byte representing one row of pixel data.

Include: gstring.h

GSSetCustomTextPattern()

#define GSSetCustomTextPattern(pattern,size) \
        GR_SET_CUSTOM_TEXT_PATTERN,GOC_PATTERN(pattern), GOC_WORD(size)

This GString creation macro sets a custom pattern to use when rendering text. It takes two arguments: a GraphicPattern value and the size of the pattern's data. Following this macro should appear the pattern's data.

Include: gstring.h

GSSetDefaultTransformation()

#define GSSetDefaultTransform() \
        GR_SET_DEFAULT_TRANSFORM

This GString creation macro replaces the current coordinate transformation with the default coordinate transformation.

Include: gstring.h

GSSetFont()

#define GSSetFont(id,size_wbf) \
                GR_SET_FONT,GOC_WBF(size_wbf),GOC_WORD(id)

This GString creation macro sets the font to use when rendering text. It takes two arguments: a FontID and a pointsize, expressed as a WBFixed number.

Include: gstring.h

GSSetFontWeight()

#define GSSetFontWeight(weight) GR_SET_FONT_WEIGHT,GOC_WORD(weight)

This GString creation macro sets the FontWeight to use when rendering text.

Include: gstring.h

GSSetFontWidth()

#define GSSetFontWidth(width) GR_SET_FONT_WIDTH,GOC_WORD(width)

This GString creation macro sets the FontWidth to use when rendering text.

Include: gstring.h

GSSetGStringBounds()

#define GSSetGStringBounds(x1_w,y1_w,x2_w,y2_w) \
        GR_SET_GSTRING_BOUNDS,GOC_SW(x1_w),GOC_SW(y1_w), \
GOC_SW(x2_w),GOC_SW(y2_w)

This GString creation macro is useful for optimizations. When drawing your GString , the graphics system will take time to traverse your GString and determine its drawing bounds. If you compute those bounds ahead of time, you may pass them as arguments to this macro--if the graphics system encounters this GString element, it won't bother to traverse the rest of the GString , but will use the pre-computed values. Thus, if you use this GString element, you should place it at the beginning of your GString . The macro takes four word-length arguments, the coordinates of the bounds of the GString .

Include: gstring.h

GSSetLineAttr()

#define GSSetLineAttr(r,g,b,mode,mask,width,end,join,style) \
        GR_SET_LINE_ATTR,(CF_RGB),(r),(g),(b),(mask),(mode),(end),(join), \
(style) ,GOC_WWF(width)

This GString creation macro sets the LineAttr to use when drawing lines. It takes several arguments: the red, green, and blue components of the color to use; the ColorMapMode to use when trying to draw in an unavailable color, the drawing mask to use, the width, the LineEnd , the LineJoin , and the LineStyle .

Include: gstring.h

GSSetLineColor()

#define GSSetLineColor(r,g,b) GR_SET_LINE_COLOR,(r),(g),(b)

This GString creation macro sets the line-drawing color in terms of its red, green, and blue components.

Include: gstring.h

GSSetLineColorIndex()

#define GSSetLineColorIndex(color) GR_SET_LINE_COLOR_INDEX, (color)

This GString creation macro sets the line-drawing color by specifying which entry in the palette to use.

Include: gstring.h

GSSetLineColorMap()

#define GSSetLineColorMap(mode) GR_SET_LINE_COLOR_MAP,(mode)

This GString creation macro sets the ColorMapMode to use when trying to draw lines with an unavailable color.

Include: gstring.h

GSSetLineEnd()

#define GSSetLineEnd(end) \
        GR_SET_LINE_END,(end)

This GString creation macro sets the LineEnd value to use when drawing endpoints of lines.

Include: gstring.h

GSSetLineJoin()

#define GSSetLineJoin(join) \
                GR_SET_LINE_JOIN,(join)

This GString creation macro sets the LineJoin value to use when drawing corners in polylines, polygons, and rectangles.

Include: gstring.h

GSSetLineMask()

#define GSSetLineMask(index) GR_SET_LINE_MASK, (index)

This GString creation macro sets the mask to use when drawing lines.

Include: gstring.h

GSSetLineStyle()

#define GSSetLineStyle(style,index) \
                GR_SET_LINE_STYLE,(style),(index)

This GString creation macro sets the style, or "dottedness," to use when drawing lines.

Include: gstring.h

GSSetLineWidth()

#define GSSetLineWidth(width_wwf) \
                GR_SET_LINE_WIDTH, GOC_WWF(width_wwf)

This GString creation macro sets the width to use when drawing lines. It takes a single argument, the width expressed as a WWFixed number.

Include: gstring.h

GSSetMiterLimit()

#define GSSetMiterLimit(limit_wwf) \
        GR_SET_MITER_LIMIT, GOC_WWF(limit_wwf)

This GString creation macro sets the miter limit to use when drawing mitered corners of rectangles, polylines, and polygons. This miter limit is expressed as a WWFixed value.

Include: gstring.h

GSSetMixMode()

#define GSSetMixMode(mode) GR_SET_MIX_MODE, (mode)

This GString creation macro sets the mix mode to use when drawing on top of existing drawings.

Include: gstring.h

GSSetNullPath()

#define GSSetNullPath() GR_SET_NULL_PATH

This GString creation macro clears the current path.

Include: gstring.h

GSSetNullTransform()

#define GSSetNullTransform() \
        GR_SET_NULL_TRANSFORM

This GString creation macro nullifies the coordinate transformation.

Include: gstring.h

GSSetPalette()

#define GSSetPalette(entry_count) GR_SET_PALETTE,(entry),(count)

This GString creation macro allows you to change several palette colors at once. It takes two arguments: the index of the first color entry to change, and the number of entries to change. Follow this macro with trios of bytes, each triplet specifying the red, green, and blue components of the colors.

GSSetPaletteEntry()

#define GSSetPaletteEntry(entry,r,g,b) \
        GR_SET_PALETTE_ENTRY GOC_WORD(entry),(r), (g),(b)

This GString creation macro changes a single entry in a custom palette. It takes four arguments: the index number of the entry and the red, green, and blue components of the new color for this entry.

Include: gstring.h

GSSetStrokePath()

#define GSSetStrokePath() GR_SET_STROKE_PATH

This GString creation macro replaces the current path with the path which results from stroking the current path with the current line attributes. This new path may be drawn, but may not be used for clipping.

Include: gstring.h

GSSetSubscriptAttr()

#define GSSetSubscriptAttr(pos,size) \
        GR_SET_SUBSCRIPT_ATTR,(pos),(size)

This GString creation macro sets the vertical offset and size to use when rendering subscript text.

Include: gstring.h

GSSetSuperscriptAttr()

#define GSSetSuperscriptAttr(pos,size) \
        GR_SET_SUPERSCRIPT_ATTR,(pos),(size)

This GString creation macro sets the vertical offset and size to use when rendering superscript text.

Include: gstring.h

GSSetTextAttr()

#define GSSetTextAttr(cflag,r,g,b,mask,sSet,sReset,mSet,mReset,
sPad,id,pSize,trk Krn,fWeight,fWidth) \
        GR_SET_TEXT_ATTR,(cflag),(r),(g),(b),(mask),(sSet), \
(sReset),(mSet),(mReset),GOC_WWF(sPad), \
GOC_WORD(id), GOC_WWF(pSize),GOC_SW(trkKrn),(fWeight),(fWidth)

This GString creation macro sets the TextAttr structure to use when rendering text. It takes several arguments: the four components of a ColorQuad , a drawing mask, TextStyle flags to set and clear, TextMode flags to set and clear, a spacing pad, a FontID , a pointsize expressed as a WWFixed number, a track kerning value, a FontWeight , and a FontWidth .

Include: gstring.h

GSSetTextColor()

#define GSSetTextColor(r,g,b) GR_SET_TEXT_COLOR,(r),(g),(b)

This GString creation macro sets the text-rendering color by means of red, green, and blue component values.

Include: gstring.h

GSSetTextColorIndex()

#define GSSetTextColorIndex(index) GR_SET_TEXT_COLOR_INDEX,(index)

This GString creation macro specifies the text-rendering color as an index of the palette.

Include: gstring.h

GSSetTextColorMap()

#define GSSetTextColorMap(mode) GR_SET_TEXT_COLOR_MAP,(mode)

This GString creation macro sets the ColorMapMode to use when trying to render text in an unavailable color.

Include: gstring.h

GSSetTextMask()

#define GSSetTextMask(index) GR_SET_TEXT_MASK, (index)

This GString creation macro specifies the mask to use when rendering text.

Include: gstring.h

GSSetTextMode()

#define GSSetTextMode(set,reset) \
                GR_SET_TEXT_MODE,(set),(reset)

This GString creation macro sets and clears TextMode flags. It takes two arguments: the flags to set and the flags to clear.

Include: gstring.h

GSSetTextPattern()

#define GSSetTextPattern(pattern) \
        GR_SET_TEXT_PATTERN,GOC_PATTERN(pattern)

This GString creation macro sets the GraphicPattern to use when rendering text.

Include: gstring.h

GSSetTextSpacePad()

#define GSSetTextSpacePad(pad_wbf) \
                GR_SET_TEXT_SPACE_PAD, GOC_WBF(pad_wbf)

This GString creation macro specifies the space pad to use when rendering text. It takes one argument: the space padding value, expressed as a WBFixed number.

Include: gstring.h

GSSetTextStyle()

#define GSSetTextStyle(set,reset) \
                GR_SET_TEXT_STYLE,(set),(reset)

This GString creation macro changes the TextStyle flags to use when drawing text. It takes two arguments, the flags to set and the flags to clear.

Include: gstring.h

GSSetTrackKern()

#define GSSetTrackKern(degree) GR_SET_TRACK_KERN, GOC_SW(degree)

This GString creation macro sets the track kerning value to use when rendering text strings.

Include: gstring.h

GSSetTransform()

#define GSSetTransform(e11_f,e12_f,e21_f,e22_f,e31_df,e32_df) \
        GR_SET_TRANSFORM,GOC_WWF(e11_f), \
GOC_WWF(e12_f),GOC_WWF(e21_f), \
GOC_WWF(e22_f),GOC_DWF(e31_df),GOC_DWF(e32_df)

This GString creation macro sets the coordinate transformation. The new transformation is defined in terms of the elements of a transformation matrix, four WWFixed values and two DWFixed values.

Include: gstring.h

GSSetWinClipPath()

#define GSSetWinClipPath(flags) GR_SET_WIN_CLIP_PATH,(flags)

This GString creation macro combines the current path with the window clipping path, using the passed PathCombineType .

Include: gstring.h

GSSetWinClipRect()

#define GSSetWinClipRect(path,rl_sw,rt_sw,rr_sw,rb_sw) \
        GR_SET_WIN_CLIP_RECT,GOC_SW(rl_sw), \
GOC_SW(rt_sw),GOC_SW(rr_sw),GOC_SW(rb_sw)

This GString creation macro combines a rectangle with the existing window clipping path. It takes five arguments: a PathCombineType and the bounding coordinates of the rectangle.

Include: gstring.h

HAL_COUNT()

word	HAL_COUNT(
        dword	val);

This macro is provided for use with HugeArrayLock() . It extracts the lower word of the HugeArrayLock() return value. This is the number of elements in the Huge Array block after the locked one (counting that locked one).

HAL_PREV

word	HAL_PREV(
        dword	val);

This macro is provided for use with HugeArrayLock() . It extracts the upper word of the HugeArrayLock() return value. This is the number of elements in the Huge Array block before the locked one (counting that locked one).

HandleModifyOwner()

void	HandleModifyOwner(
        MemHandle		mh,			/* Handle of block to modify */
        GeodeHandle		owner);			/* Handle of block's new owner */

This routine changes the owner of the indicated global memory block. Note that this routine can be called only by a thread belonging to the block's original owner; that is, you can only use this routine to transfer ownership of a block from yourself to some other geode.

Include: heap.h

Never Use Situations:
Never use this unless the block already belongs to you and you are giving up ownership.

See Also: MemGetInfo(),
MemModifyFlags(),
MemModifyOtherInfo().

HandleP()

void	HandleP(
        MemHandle		mh);		/* Handle of block to grab */

If several different threads will be accessing the same global memory block, they need to make sure their activities will not conflict. The way they do that is to use synchronization routines to get control of a block. HandleP() is part of one set of synchronization routines.

If the threads are using this family of routines, then whenever a thread needs access to the block in question, it can call HandleP() . This routine checks whether any thread has grabbed the block with HandleP() (or MemPLock() ). If no thread has the block, it grabs the block for the calling thread and returns (it does not lock the block on the global heap). If a thread has the block, HandleP() puts the thread on a priority queue and sleeps. When the block is free for it to take, it awakens, grabs the block, and returns.When the thread is done with the block, it should release it with MemUnlockV() or HandleV() .

Include: heap.h

Tips and Tricks: If you will be locking the block after you grab it, use the routine MemPLock() (which calls HandleP() and then locks the block with MemLock() ). You can find out if the block is being accessed by looking at the HM_otherInfo word (with MemGetInfo() ). If HM_otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all threads accessing the block use HandleP() and/or MemPLock() to access the block. The routines use the HM_otherInfo field of the handle table entry; do not alter this field. Release the block with HandleV() or MemUnlockV() when you are done with it.

Warnings: If a thread calls HandleP() when it already has control of the block, it will deadlock; HandleP() will put the thread to sleep until the thread releases the block, but the thread will not be able to release the block because it's sleeping. MemThreadGrab() avoids this conflict. If you try to grab a non-sharable block owned by another thread, HandleP() will fatal-error.

See Also: MemThreadGrab(),
MemThreadGrabNB(),
MemThreadRelease(),
HandleV(),
HandleP(),
MemPLock(),
MemUnlockV().

HandleToOptr()

optr	HandleToOptr(
        Handle 	han;

This macro casts any handle to an optr, leaving the chunk handle portion of the resultant optr to be zero.

See Also: ConstructOptr(),
OptrToHandle(),
OptrToChunk().

HandleV()

void	HandleV(
        MemHandle		mh);		/* Handle of block to grab */

HandleV() is part of a set of synchronization routines. If several different threads will be accessing the same global memory block, they need to make sure their activities will not conflict. The way they do that is to use synchronization routines to get control of a block. HandleV() is part of one set of synchronization routines.

If a block is being accessed via these synchronization routines, then a thread will not access a block until it has "grabbed" it with HandleP() or MemPLock() . When a thread is done with the block, it can release it for use by the other threads by calling HandleV() . Note that HandleV() does not unlock the block; it just changes the block's semaphore so other threads can grab it.

Include: heap.h

Tips and Tricks: If you need to unlock the thread just before releasing it, use the routine MemUnlockV() , which first unlocks the thread, and then calls HandleV() to release it. You can find out if the block is being accessed by looking at the HM_otherInfo word (with MemGetInfo() ). If HM_otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all threads accessing the block use HandleP() or MemPLock() to access the thread. The routines use the HM_otherInfo field of the handle table entry; do not alter this field.

Warnings: Do not use this on a block unless you have grabbed it. The routine does not check to see that you have grabbed the thread; it just clears the semaphore and returns.

See Also: MemThreadGrab(),
MemThreadGrabNB(),
MemThreadRelease(),
HandleV(),
HandleP(),
MemPLock(),
MemUnlockV().

HelpSendHelpNotification()

void 	HelpSendHelpNotification(
        word 		HelpType,
        const char 		*contextname,
        const char 		*filename);

Use this routine to bring up on-line help on the fly or to dynamically change the context presently showing in on-line help.

HugeArrayAppend()

dword	HugeArrayAppend(
        VMFileHandle		file,
        VMBlockhandle		vmBlock,				/* Handle of directory block */
        word		numElem,				/* # of elements to add to end of
                         * array */
        const void *		initData);				/* Copy into new element(s) */

This routine appends one or more elements to a Huge Array. If initData is a null pointer, the elements will be uninitialized.

If the Huge Array contains variable sized elements, this routine will append a single element; this element will be numElem bytes long. The single element will be initialized to hold initData .

If the Huge Array contains constant-sized elements, then initData will be treated as an array of element-sized structures. initData [0] will be the initial value of the first element, initData [1] will be the initial value of the second, and so on.

The return value is the element number of the element added. If more than one element was added; this will be the number of the first element added.

Include: hugearr.h

HugeArrayCompressBlocks()

void	HugeArrayCompressBlocks(
        VMFileHandle		vmFile,			/* File containing Huge Array */
        VMBlockHandle		vmBlock);			/* handle of directory block */

This routine compacts a Huge Array, resizing every block to be just as large as necessary to accommodate its elements. It does not change any of the data in the Huge Array.

Include: hugearr.h

HugeArrayContract()

word	HugeArrayContract(
        void **		elemPtr,				/* **elemPtr is first element to
                          * delete */
        word		numElem);				/* # of elements to delete */

Delete a number of elements starting at an address in a Huge Array. The routine will fix up the pointer so it points to the first element after the deleted elements. The routine automatically locks and unlocks Huge Array blocks as necessary.

Include: hugearr.h

HugeArrayCreate()

VMBlockhandle 	HugeArrayCreate(
        VMFileHandle		vmFile,				/* Create in this VM file */
        word		elemSize,				/* Pass zero for variable-size
                         * elements */
        word		headerSize);				/* Pass zero for default header */

This routine creates and initializes a Huge Array in the specified file. It returns the handle of the Huge Array's directory block.

Include: hugearr.h

HugeArrayDelete()

void	HugeArrayDelete(
        VMFileHandle		vmFile,
        VMBlockHandle		vmBlock,			/* handle of directory block */
        word		numElem,			/* # of elements to delete */
        dword		elemNum);			/* Index of first element to delete */

This routine deletes one or more elements from a Huge Array. It contracts and frees blocks as necessary.

Include: hugearr.h

HugeArrayDirty()

void	HugeArrayDirty(
        const void *		elemPtr);				/* Element in dirty block */

This routine marks a block in a Huge Array as dirty. The routine is passed a pointer to anywhere in a dirty element; that element's block will be dirtied.

Include: hugearr.h

Warnings: Be sure to call this routine before you unlock the element; otherwise, the block may be discarded before you can dirty it.

HugeArrayDestroy()

void	HugeArrayDestroy(
        VMFileHandle		vmFile,
        VMBlockHandle		vmBlock);			/* Handle of directory block */

This routine destroys a HugeArray by freeing all of its blocks.

Include: hugearr.h

HugeArrayEnum()

Boolean	HugeArrayEnum(
        VMFileHandle	vmFile,			/* Handle of Huge Array's VM file */
        VMBlockHandle	vmBlock,		/* Handle of the Huge Array's
                         * directory block */
        Boolean	 _pascal 	(*callback) (	/* return true to stop */
               void *			element,	/* element to examine */
               void * 			enumData),	/* pointer to data */
        dword		startElement,		/* first element to examine */
        dword		count,			/* examine this many elements */
        void *		enumData;		/* this pointer is passed to
                         * callback routine */

This routine lets you examine a sequence of elements in a Huge Array. HugeArrayEnum() is passed six arguments. The first two are a file handle and block handle; these specify the Huge Array to be examined. The third is a pointer to a Boolean callback routine. The fourth argument is the index of the first element to be examined (remember, the first element in the Huge Array has an index of zero). The fifth argument is the number of elements to examine, or -1 to examine through the last element. The sixth argument is a pointer which is passed unchanged to the callback routine; you can use this to pass data to the callback routine, or to keep track of a scratch space.

The callback routine, which must be declared _pascal, itself takes two arguments. The first is a pointer to an element in the huge array. The callback routine will be called once for each element in the specified range; each time, the first argument will point to the element being examined. The second argument is the pointer that was passed as the final argument to HugeArrayEnum() . The callback routine can make HugeArrayEnum() abort by returning true ; this is useful if you need to search for a specific element. Otherwise, the callback routine should return false . If the callback routine aborts the enumeration, HugeArrayEnum() returns true ; otherwise, it returns false .

HugeArrayEnum() is guaranteed to examine the elements in numerical order, beginning with startElement . The routine will automatically stop with the last element, even if count elements have not been enumerated. However, the starting element must be the index of an element in the array.

Include: hugearr.h

Warnings: The callback routine may not allocate, free, or resize any elements in the Huge Array. All it should do is examine or change ( without resizing) a single element.

The starting element must be an element in the array. If you pass a starting index which is out-of-bounds, the results are undefined.

HugeArrayExpand()

word	HugeArrayExpand(
        void **		elemPtr,				/* **elemPtr is element at location
                         * where new elements will be
                         * created */
        word		numElem,				/* # of elements to insert */
        const void *		initData);				/* Copy this into each new
                         * element */

This routine inserts a number of elements at a specified location in a HugeArray. The element pointed to will be shifted so it comes after the newly-created elements. The pointer will be fixed up to point to the first new element. The data pointed to by initData will be copied into each new element. If initData is null, the new elements will be uninitialized.

If the elements are of variable size, this routine will insert a single element; this element will be numElem bytes long.

Include: hugearr.h

HugeArrayGetCount()

dword	HugeArrayGetCount(
        VMFileHandle		vmFile,
        VMBlockHandle		vmBlock);			/* Handle of directory block */

This routine returns the number of elements in a Huge Array.

Include: hugearr.h

HugeArrayInsert()

void	HugeArrayInsert(
        VMFileHandle		vmFile,
        VMBlockHandle		vmBlock,			/* Handle of directory block */
        word		numElem,			/* # of elements to insert */
        dword		elemNum,			/* Index of first new element */
        const void *		initData);			/* Copy this into each new element */

This routine inserts one or more elements in the midst of a Huge Array. The first new element will have index elemNum ; thus, the element which previously had that index will now come after the new elements. The data pointed to by initData will be copied into each new element. If initData is null, the new elements will be uninitialized.

If the elements are of variable size, this routine will insert a single element; this element will be numElem bytes long.

Include: heap.h

HugeArrayLock()

dword	HugeArrayLock(
        VMFileHandle		vmFile,
        VMBlockhandle		vmBlock,			/* Handle of directory block */
        dword		elemNum,			/* Element to lock */
        void **		elemPtr,			/* Pointer to element is written here */
        word *		elemSize);

This routine locks an element in a Huge Array. It writes the element's address to *elemPtr . The dword returned indicates how many elements come before and after the element in that blok The upper word indicates how many elements come before the locked one, counting the locked element; the lower word indicates how many elements come after the locked element, again counting the locked one. If an error occured, then the lower word of the return value will be zero.

The routine also writes the size of the locked element (in bytes) to *elemSize . You may examine or change all the other elements in the block without making further calls to HugeArrayLock() .

Include: heap.h

See Also: HAL_COUNT(), HAL_PREV().

HugeArrayNext()

word	HugeArrayNext(
        void **		elemPtr,
        word *		size);

This routine increments a pointer to an element in a HugeArray to point to the next element. If the element was the last element in its block, HugeArrayNext() will unlock its block and lock the next one. The routine writes the pointer to *elemPtr; it returns the number of elements which come after the newly-locked one in its block, counting the newly-locked element. If this routine is passed a pointer to the last element in a HugeArray, it returns zero and elemPtr will point to the last element.

If the elements are variable-sized, then the word pointed to by size will be updated to reflect the size of the newly-locked element; otherwise, the value will be undefined.

Include: heap.h

Warnings: This routine may unlock the block containing the passed element. Therefore, if you need to mark the block as dirty, do so before making this call.

HugeArrayPrev()

word	HugeArrayPrev(
        void **		elemPtr1,				/* indicates current element */
        void **		elemPtr2,
        word * 		size);

This routine decrements a pointer to an element in a HugeArray to point to the previous element. If the element was the first element in its block, HugeArrayPrev() will unlock its block and lock the previous one. The routine writes the pointer to *elemPtr1, and writes a pointer to the first element in the block in *elemPtr2. It returns the number of elements which come before the newly-locked one in its block, counting the newly-locked element. If this routine is passed a pointer to the first element in a HugeArray, it returns zero and *elemPtr1 will point to the first element.

If the elements are variable-sized, then the word pointed to by size will be updated to reflect the size of the newly-locked element; otherwise, the value will be undefined.

Include: hugearr.h

Warnings: This routine may unlock the block containing the passed element. Therefore, if you need to mark the block as dirty, do so before making this call.

HugeArrayReplace()

void	HugeArrayReplace(
        VMFileHandle		file,
        VMBlockHandle		vmblock,				/* Handle of directory block */
        word		numElem,				/* # of elements to replace */
        dword		elemNum,				/* First element to replace */
        const void *		initData);				/* Copy this into each element

This routine replaces one or more elements with copies of the passed data. If initData is null, the elements will be filled with null bytes.

If the elements are of variable size, a single element will be resized; its new size will be numElem bytes long.

Include: hugearr.h

See Also: HugeArrayResize().

HugeArrayResize()

void	HugeArrayResize(
        VMFileHandle		vmFile,
        VMBlockHandle		vmBlock,			/* Handle of directory block */
        dword		elemNum,			/* Resize this element */
        word		newSize);			/* New size in bytes */

This routine resizes an element in a Huge Array. The array must contain variable-sized elements. If the new size is larger than the old, the extra space will be zero-initialized. If it is smaller, the element will be truncated.

Include: hugearr.h

HugeArrayUnlock()

void	HugeArrayUnlock(
        void *		elemPtr);

This routine unlocks the block of a HugeArray which contains the passed element.

Include: hugearr.h

Warnings: If you have changed any of the elements in the block, be sure to call HugeArrayDirty() before you unlock the block; otherwise the block might be discarded.

IACPConnect()

IACPConnection IACPConnect(
        GeodeToken 		*list,
        IACPConnectFlags 		flags,
        MemHandle 		appLaunchBlock,
        optr 		client,
        word 		*numServers);

This routine establishes a connection between a client object (by default the calling thread's application object) and one or more servers registered with the indicated list.

The client argument should be NullOptr unless the IACPCF_CLIENT_OD_SPECIFIED flag is set in the flags parameter.

Include: iacp.goh

IACPCreateDefaultLaunchBlock()

MemHandle IACPCreateDefaultLaunchBlock(
        word 		appMode);

This routine creates a memory block holding an AppLaunchBlock structure suitable for passing to IACPConnect() . The two valid values to pass in appMode are MSG_GEN_PROCESS_OPEN_APPLICATION and MSG_GEN_PROCESS_OPEN_ENGINE .

Include: iacp.goh

IACPFinishConnect()

void	IACPFinishConnect(
        IACPConnection		connection,
        optr		server);

Finishes a connection made to a server which had to change from non-interactible to interactible.

Include: iacp.goh

IACPGetServerNumber()

word	IACPGetServerNumber(
        IACPConnection		connection,
        optr		server);

This routine retrieves the server number for a particular server in a particular connection. It returns the server number.

Include: iacp.goh

IACPLostConnection()

void IACPLostConnection(
        optr 		oself,
        IACPConnection 		connection);

This routine is called by IACP server objects to handle when a client closes a connection.

Include: iacp.goh

Up | Prev: HugeArrayPrev() ... | Next: ImpexExportToMetafile() ...

IACPProcessMessage()

void IACPProcessMessage(
        optr           oself,
        EventHandle            msgToSend,
        TravelOption           topt,
        EventHandle            completionMsg);

This is a utility routine to dispatch an encapsulated message handed to an object by an IACP connection.

Include: iacp.goh

IACPRegisterDocument()

void IACPRegisterDocument(
        optr   server,
        word   disk,
        dword  fileID);

This routine registers an open document and the server object for it.

This routine is to be used only by servers, not by clients, and should only be used by the creator of the document. There is no provision for using IACP to connect to a server that is not the creator of the document in question.

Include: iacp.goh

IACPRegisterServer()

void    IACPRegisterServer(
        GeodeToken             *list,
        optr           server,
        IACPServerMode                 mode,
        IACPServerFlags                flags);

This routine registers an object as a server for the IACP server list specified by the passed token.

Include: iacp.goh

IACPSendMessage()

word IACPSendMessage(
    IACPConnection 	connection,
    EventHandle 		msgToSend,
    TravelOption 		topt,
    EventHandle 		completionMsg,
    IACPSide 		side);

This routine sends the message event via IACP to all the objects on the other side of an IACP connection.

Include: iacp.goh

IACPSendMessageAndWait()

word IACPSendMessageAndWait(
        IACPConnection                 connection,
        EventHandle            msgToSend,
        TravelOption           topt,
        IACPSide     side);

This routine sends a recorded message to all the objects on the other side of an IACP connection, and waits until the other application responds that it has received the message via IACP. This does not mean the message has been handled by the other application, only that the other application was given the EventHandle and will try to deal with it. This is useful when passing volatile data.

Include: iacp.goh

IACPSendMessageToServer()

word IACPSendMessageToServer(
        IACPConnection                 connection,
        EventHandle            msgToSend,
        TravelOption           topt,
        EventHandle            completionMsg,
        word           serverNum);

This routine sends a message to a specific server on the other side of an IACP connection.

Include: iacp.goh

IACPShutdown()

void IACPShutdown(
        IACPConnection                 connection,
        optr           serverOD);

This routine removes a server or client from an IACP connection.

Include: iacp.goh

IACPShutdownAll()

void IACPShutdownAll(
        optr obj);

This calls IACPShutdown() for all connections to which the passed object is a party. It's primarily used by GenApplicationClass when the application is exiting.

Include: iacp.goh

IACPUnregisterDocument()

void IACPUnregisterDocument(
        optr   server,
        word   disk,
        dword  fileID);

This routine unregisters an open document and the server object for it.

Include: iacp.goh

IACPUnregisterServer()

void IACPUnregisterServer(
        GeodeToken     *list,
        optr           server);

This removes the specified server object from the indicated IACP server list.

Include: iacp.goh

ImpexCreateTempFile()

TransError ImpexCreateTempFile(
        char *         buffer,
        word           fileType,
        FileHandle *           file,
        MemHandle *            errorString);

This routine creates and opens a unique temporary file to be used by translation libraries for file importing and exporting. The routine is called only by translation libraries.

The routine is passed the following arguments:

buffer
The file name will be written to the buffer pointed to by this argument. The buffer should be at least FILE_LONGNAME_BUFFER_SIZE bytes long.
fileType
This specifies what kind of temporary file should be created. If IMPEX_TEMP_VM_FILE is passed, a GEOS VM file will be created. If IMPEX_TEMP_NATIVE_FILE is passed, a temporary file in the native format will be created.
file
This is a pointer to a FileHandle variable. The temporary file's handle will be written to *file .
errString
If ImpexCreateTempFile fails with error condition TE_CUSTOM it will allocate a block containing an error string. It will write the block's handle to *errString . It is the caller's responsibility to free this block when it's done with it.

If ImpexCreateTempFile is successful, it returns TE_NO_ERROR (which equals zero). If it fails, it returns a member of the TransferErrors enumerated type (usually TE_METAFILE_CREATION_ERROR). When you're done with the temporary file, call FileClose() .

Include: impex.goh

Warnings: If you close this file, the system may delete it at any time. Ordinarily you should close it with ImpexDeleteTempFile() , which deletes the file immediately.

If the routine does not fail with condition TE_CUSTOM, *errString may contain a random value. Do not use *errString if the routine did not return TE_CUSTOM.

ImpexDeleteTempFile()

TransError ImpexDeleteTempFile(
        const char *           buffer,
        FileHandle             tempFile,
        word           fileType);

This routine closes, then deletes, a temporary file which was created by ImpexCreateTempFile() . It is passed the following arguments:

buffer
This is a pointer to a character buffer containing the name of the temporary file. You can just pass the address of the buffer which was filled by ImpexCreateTempFile() .
tempFile
This is the handle of the temporary file.
fileType
This specifies what type of file is being deleted. If the temporary file is a GEOS VM file, this will be IMPEX_TEMP_VM_FILE. If it is a native-format file, it will be IMPEX_TEMP_NATIVE_FILE.
errString
If ImpexDeleteTempFile fails with error condition TE_CUSTOM it will allocate a block containing an error string. It will write the block's handle to *errString . It is the caller's responsibility to free this block when it's done with it.

ImpexDeleteTempFile() closes the specified file, then deletes it. If it is successful, it returns TE_NO_ERROR (i.e. zero); otherwise, it returns an appropriate member of the TransError enumerated type.

Include: impex.goh

Warnings: If the routine does not fail with condition TE_CUSTOM, *errString may contain a random value. Do not use *errString if the routine did not return TE_CUSTOM.

ImpexExportToMetafile()

TransError 	ImpexExportToMetafile(
        Handle		xlatLib,
        VMFileHandle		xferFile,
        FileHandle		metafile,
        dword		xferFormat,
        word		arg1,
        word		arg2,
        MemHandle *		errString);

This routine is used by translation libraries. The routine calls an intermediate translation library to finish translating a given file into the GEOS Metafile format.

Include: impex.goh

Warnings: If the routine does not fail with condition TE_CUSTOM, *errString may contain a random value. Do not use *errString if the routine did not return TE_CUSTOM.

ImpexImportExportCompleted()

void 	ImpexImportExportCompleted(
        ImpexTranslationParams *			itParams);

The application should send this message when it is finished importing or exporting data. The routine will send an appropriate acknowledgment message to the ImportControl or ExportControl object, depending on the settings of ITP_impexOD and ITP _returnMsg .

If the application has just finished an import, it should not have changed the ImpexTranslationParams structure. If it had just finished preparing data for export, it should have set the ITP _transferVMChain field to contain the handle of the head of the VM chain.

Warnings: This routine, in essence, informs the ImportControl or ExportControl object that the application is finished with the transfer file. The ImportControl will respond by destroying the transfer file; the ExportControl will call the appropriate translation library to produce an output file. Therefore, an application should not call this routine until it is absolutely finished with the transfer file.

ImpexImportFromMetafile()

TransError 	ImpexExportToMetafile(
        Handle		xlatLib,
        VMFileHandle		xferFile,
        FileHandle		metafile,
        dword *		xferFormat,
        word		arg1,
        word		arg2,
        MemHandle *		errString);

This routine is used by translation libraries. The routine calls an intermediate translation library to translate a given file from the GEOS Metafile format to an intermediate format.

Include: impex.goh

Warnings: If the routine does not fail with condition TE_CUSTOM, *errString may contain a random value. Do not use *errString if the routine did not return TE_CUSTOM.

InitFileCommit()

void	InitFileCommit(void);

This routine commits any changes to the GEOS.INI file, removing and replacing its stored backup. It ensures that no other threads are working on the file during the commit operation.

Include: initfile.h

InitFileDeleteCategory()

void	InitFileDeleteCategory(
        const char *category);

This routine deletes the specified category, along with all its entries, from the GEOS.INI file. Pass it the following:

category
A pointer to the null-terminated string representing the category to be deleted. This string ignores white space and is case-insensitive.

Include: initfile.h

InitFileDeleteEntry()

void	InitFileDeleteEntry(
        const char *category,
        const char *key);

This routine deletes an entry in the GEOS.INI file. Pass it the following:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be deleted.

Include: initfile.h

InitFileDeleteStringSection()

void	InitFileDeleteStringSection(
        const char *		category,
        const char *		key,
        word		stringNum);

This routine deletes the specified string section from the given blob in the GEOS.INI file. Pass it the following:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be edited.
stringNum
The zero-based string section number.

Include: initfile.h

InitFileEnumStringSection()

Boolean	InitFileEnumStringSection(
        const char *		category,
        const char *		key,
        InitFileReadFlags		flags,
        Boolean _pascal (*callback)		(const char *				stringSection,
                 word 				sectionNum,
                 void *				enumData),
        void *		enumdata);

This routine enumerates a particular blob, allowing a callback routine to process each of the string sections in it. The routine will stop processing either after the last string section or when the callback routine returns true .

Pass this routine the following:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be enumerated.
flags
A record of InitFileReadFlags indicating the method of character conversion upon reading (upcase all, downcase all, do not change).
callback
A pointer to a Boolean callback routine. The callback routine is described below.
enumData
This pointer is passed unchanged to the callback routine. InitFileEnumStringSection() does not use it.

This routine returns a Boolean value. It returns true if the callback routine halted the enumeration by returning true ; otherwise, it returns false .

Callback Routine:

The callback routine may do anything it wants with the string section it receives. It must be declared _pascal. It must return a Boolean value: If it returns true , InitFileEnumStringSection() will stop processing the blob. If it returns false , processing will continue to the next string section, if any. The callback will receive the following parameters:

stringSection
A pointer to the null-terminated string section to be processed.
sectionNum
The zero-based number of the string section currently being processed.
enumData
A pointer passed through from the caller of InitFileEnumStringSection() .

Include: initfile.h

InitFileGetTimeLastModified()

dword	InitFileGetTimeLastModified(void);

This routine returns the time when the GEOS.INI file was last modified. The returned time is the value of the system counter when the file was last written.

Include: initfile.h

InitFileMakeCanonicKeyCategory()

void       InitFileMakeCanonicKeyCategory(
    char 		*keyCat,
    const TCHAR 	*src);

Converts a SBCS/DBCS (depending on GEOS version) string to an ASCIIX string without special INI characters. This is used to convert any type of string to one suitable for use as an INI file key or category.

Pass:

keyCat
The unconverted SBCS/DBCS string.
src
The buffer for the converted SBCS ASCII hex string. If keyCat is SBCS, then src should be the same length as keyCat (including the NULL terminator); otherwise, if keyCat is DBCS then src should point to a buffer which is twice as long as keyCat (including its NULL terminator).

Include: initfile.h

Version: This routine is available on builds 4 or greater of the Nokia 9000i Communicator (see the Versions chapter for more information).

InitFileReadBoolean()

Boolean	InitFileReadBoolean(
        const char *		category,
        const char *		key,
        Boolean *		bool);

This routine reads a Boolean entry in the GEOS.INI file, copying it into a passed buffer. It returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteBoolean() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
bool
A pointer to a Boolean variable in which the Boolean value will be returned.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Warnings: The return value of this function is not the Boolean stored in the GEOS.INI file. That value is returned in the Boolean pointed to by bool .

Include: initfile.h

InitFileReadDataBlock()

Boolean	InitFileReadDataBlock(
        const char *		category,
        const char *		key,
        MemHandle *		block,
        word *		dataSize);

This routine reads an entry in the GEOS.INI file, allocating a new block and copying the data into it. The routine returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteData() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
block
A pointer to a null memory handle. This pointer will point to the newly-allocated block handle upon return. The data read will be in the new block. It is your responsibility to free this block when you're done with it.
dataSize
The size of the read data. All the data will be read; the block will be as large as necessary.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

InitFileReadDataBuffer()

Boolean	InitFileReadDataBuffer(
        const char *		category,
        const char *		key,
        void *		buffer,
        word		bufSize,
        word *		dataSize);

This routine reads an entry in the GEOS.INI file, copying it into a passed buffer. It returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteData() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
buffer
A pointer to the buffer in which the data will be returned. This buffer must be in locked or fixed memory.
bufSize
The size of the passed buffer in bytes. If you are not sure what the data's size will be, you may want to use the (slightly less efficient) InitFileReadDataBlock() .
dataSize
A pointer to a word; on return, the word pointed to will contain the size (in bytes) of the data returned.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

InitFileReadInteger()

Boolean	InitFileReadInteger(
        const char *		category,
        const char *		key,
        word *		i);

This routine reads an integer entry in the GEOS.INI file, copying it into the passed variable. It returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteInteger() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
i
A pointer to a word in which the integer will be returned.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

InitFileReadStringBlock()

Boolean	InitFileReadStringBlock(
        const char *		category,
        const char *		key,
        MemHandle *		block,
        InitFileReadFlags		flags,
        word *		dataSize);

This routine reads a string entry in the GEOS.INI file, allocates a new block on the global heap, and copies the read string into the new block. It returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteString() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
block
A pointer to a memory block handle variable. Upon return, this variable will contain the handle of the newly allocated block; the block will contain the string read from the file. It is your responsibility to free this block when you're done with it.
flags
A record of InitFileReadFlags indicating the method of character conversion upon reading (upcase all, downcase all, do not change).
dataSize
A pointer to a word which, upon return, will contain the size of the string (in bytes) actually read from the file.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

InitFileReadStringBuffer()

Boolean	InitFileReadStringBuffer(
        const char *		category,
        const char *		key,
        char *		buffer,
        InitFileReadFlags		flags,
        word *		dataSize);

This routine reads a string entry in the GEOS.INI file, copying it into a passed, locked buffer. It returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteString() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
buffer
A pointer to a buffer into which the returned string will be written. This buffer must be in locked or fixed memory. If you don't know the approximate size of the data, you may want to use the (slightly less efficient) InitFileReadStringBlock() .
flags
A record of InitFileReadFlags indicating the size of the passed buffer as well as the method of character conversion upon reading (upcase all, downcase all, do not change).
dataSize
A pointer to a word which, upon return, will contain the size of the string (in bytes) actually read from the file.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

InitFileReadStringSectionBlock()

Boolean	InitFileReadStringSectionBlock(
        const char *		category,
        const char *		key,
        word		section,
        MemHandle *		block,
        InitFileReadFlags		flags,
        word *		dataSize);

This routine reads a string section from the specified entry in the GEOS.INI file, allocates a new block on the global heap, and copies the read string section into the new block. It returns the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteString() or InitFileWriteStringSection() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
section
The zero-based number of the string section to retrieved.
block
A pointer to a memory block handle. Upon return, this pointer will point to the handle of the newly allocated block; the block will contain the string section read from the file.
flags
A record of InitFileReadFlags indicating the method of character conversion upon reading (upcase all, downcase all, do not change).
dataSize
A pointer to a word which, upon return, will contain the size of the string section (in bytes) actually read from the file.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

InitFileReadStringSectionBuffer()

Boolean	InitFileReadStringSectionBuffer(
        const char *		category,
        const char *		key,
        word		section,
        char *		buffer,
        InitFileReadFlags		flags,
        word *		dataSize);

This routine reads a string section from the specified entry in the GEOS.INI file, copying it into a passed, locked buffer. It returns the indicated section in the first instance of the category/key combination it encounters, searching the local INI file first. Thus, local settings will always override system or network settings.

This routine is used for reading data written with InitFileWriteStringSection() . Pass it the following parameters:

category
A pointer to the null-terminated string representing the category in which the entry resides. This string ignores white space and is case-insensitive.
key
A pointer to the null-terminated string representing the key to be retrieved.
section
The zero-based number of the string section to be retrieved.
buffer
A pointer to a buffer into which the returned string section will be written. This buffer must be in locked or fixed memory. If you don't know the approximate size of the string section, you may want to use the (slightly less efficient) InitFileReadStringSectionBlock() .
flags
A record of InitFileReadFlags indicating the size of the passed buffer as well as the method of character conversion upon reading (upcase all, downcase all, do not change).
dataSize
A pointer to a word which, upon return, will contain the size of the string section (in bytes) actually read from the file.

The function's return value will be true if an error occurs or if the entry could not be found; it will be false otherwise.

Include: initfile.h

InitFileRevert()

Boolean	InitFileRevert(void);

This routine restores the GEOS.INI file from its saved backup version. It ensures that no other thread is operating on the file while it is being restored. This function returns an error flag: true represents an error in restoring the file; false indicates success.

Include: initfile.h

InitFileSave()

Boolean	InitFileSave(void);

This routine saves a backup copy of the GEOS.INI, synchronously updating the backup file (privdata\ini.bak) to be the current version. ( InitFileCommit() overwrites the GEOS.INI file itself.) It ensures that no other thread is operating on the file while it is being written out. This function returns an error flag: true represents an error in trying to save the file; false indicates success.

Include: initfile.h

InitFileWriteBoolean()

void	InitFileWriteBoolean(
        const char *		category,
        const char *		key,
        Boolean 		bool);

This integer writes a Boolean value into the specified category and key of the local GEOS.INI file. The Boolean will appear as "true" or "false" if the user looks at GEOS.INI with a text editor, but it will be an actual Boolean value to GEOS. Pass this routine the following:

category
A pointer to the null-terminated character string representing the INI category into which the data should be written.
key
A pointer to the null-terminated character string representing the INI key within category into which the data should be written.
bool
The Boolean value to be written.

Once written, the Boolean value can be read with InitFileReadBoolean() .

Include: initfile.h

InitFileWriteData()

void	InitFileWriteData(
        const char 		*category,
        const char 		*key,
        const void 		*buffer,
        word		bufSize);

This routine writes a given piece of data to the local GEOS.INI file. Pass it the following:

category
A pointer to the null-terminated character string representing the INI category into which the data should be written.
key
A pointer to the null-terminated character string representing the INI key within category into which the data should be written.
buffer
A pointer to a locked or fixed buffer containing the data to be written.
bufSize
The size of the buffer in bytes.

Once data has been written to the INI file, it can be read with InitFileReadDataBlock() or InitFileReadDataBuffer() .

Include: initfile.h

InitFileWriteInteger()

void	InitFileWriteInteger(
        const char 		*category,
        const char 		*key,
        word		value);

This routine writes an integer into the category and key specified for the local GEOS.INI file. Pass the following:

category
A pointer to the null-terminated character string representing the INI category into which the data should be written.
key
A pointer to the null-terminated character string representing the INI key within category into which the data should be written.
value
The integer to be written.

The integer, once written, can be read with InitFileReadInteger() .

Include: initfile.h

InitFileWriteString()

void	InitFileWriteString(
        const char *category,
        const char *key,
        const char *str);

This routine writes an entire string into the category and key specified for the local GEOS.INI file. Pass it the following:

category
A pointer to the null-terminated character string representing the INI category into which the data should be written.
key
A pointer to the null-terminated character string representing the INI key within category into which the data should be written.
str
A pointer to the null-terminated string to be written. If the string contains line feeds or carriage returns, it will automatically be parsed into string segments and be put within curly braces; if it contains curly braces, all closing braces will automatically have a backslash inserted before them.

To read a string written with this routine, use InitFileReadStringBlock() or InitFileReadStringBuffer() .

Include: initfile.h

InitFileWriteStringSection()

void	InitFileWriteStringSection(
        const char *category,
        const char *key,
        const char *string);

This routine appends a string section onto the blob specified by the category and key parameters. The string section will become part of the blob and will be its last section. The section may not contain any carriage returns or line feeds. Pass this routine the following:

category
A pointer to the null-terminated character string representing the INI category into which the data should be written.
key
A pointer to the null-terminated character string representing the INI key within category into which the data should be written.
string
A pointer to the string section to be written.

Once written, the segment may be read with InitFileReadStringSectionBlock() or InitFileReadStringSectionBuffer() .

Include: initfile.h

InkDBGetDisplayInfo()

void 	InkDBGetDisplayInfo(
        InkDBDisplayInfo *		retVal,
        VMFileHandle 		fh);

This routine returns the dword ID of the note or folder which is presently being displayed by the Ink Database. It also returns the ID of the parent folder, and the page number, if applicable.

Structures: It returns this information by filling in an InkDBDisplayInfo structure:

typedef struct {
    dword 	IDBDI_currentDisplay;
    dword 	IDBDI_parentFolder;
    word 	IDBDI_pageNumber;
} InkDBDisplayInfo;

Include: pen.goh

InkDBGetHeadFolder()

dword 	InkDBGetHeadFolder(
        VMFileHandle 		fh);

This routine returns the dword ID of the head folder of an Ink Database file.

Include: pen.goh

InkDBInit()

void 	InkDBInit(
        VMFileHandle 		fh);

This routine takes a new Ink Database file. It initializes the file for use, creating all needed maps and a top-level folder.

Include: pen.goh

InkDBSetDisplayInfo()

void 	InkDBSetDisplayInfo(
        VMFileHandle 		fh,
        dword 		ofh,		/* Parent Folder dword ID# */
        dword		note,		/* ID# of note or folder to display */
        word		page); 		/* If displaying note, page # to display*/

This routine sets the display information for an Ink Database file. This routine sets the user's location in the database. The caller must supply the dword ID number of the note or folder to display, the parent folder (0 if displaying the top level folder), and the page number to display if displaying a note.

Include: pen.goh

InkFolderCreateSubFolder()

dword 	InkFolderCreateSubFolder(
        dword 		tag, 	/* ID# of parent folder (0 for top-level) */
        VMFileHandle 		fh); 	/* Handle of Ink DB file */

This routine creates a subfolder within the passed folder. The new folder is automatically added to it's parent's chunk array. The return value is new folder's dword ID number.

Include: pen.goh

InkFolderDelete()

void 	InkFolderDelete(
        dword 		tag,		/* ID# of folder */
        VMFileHandle 		fh);		/* Handle of Ink DB file */

This routine removes an Ink Database folder.

Include: pen.goh

InkFolderDepthFirstTraverse()

word 	InkFolderDepthFirstTraverse(
        dword 		rfldr,		/* ID# of folder at root of search tree */
        VMFileHandle		fh, 		/* Handle of Ink DB file */
        Boolean 	_pascal	(*callback)( /* far ptr to callback routine */
            dword				fldr,
            VMFileHandle 				fh,
            word *				info),
        word *		info);		/* Extra data to pass to callback */

This routine does a depth-first traversal of a folder tree. The callback routine, which must be declared _pascal, can halt the search by returning true , in which case the search routine will immediately return true ; otherwise the search will return false .

Include: pen.goh

InkFolderDisplayChildInList()

void 	InkFolderDisplayChildInList(
        dword 		fldr, 		/* ID# of folder */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        optr 		list, 		/* GenDynamicList */
        word		entry, 		/* entry number of child to display */
        Boolean		displayFolders); /* Include monikers in count,
                            * return their monikers */

This routine requests that a dynamic list display the name of one of a folder's children. It is normally called in an applications GDLI_queryMsg handler.

Include: pen.goh

InkFolderGetChildInfo()

Boolean 	InkFolderDisplayChildInfo( /* true if folder; else note */
        dword 		fldr, 		/* ID# of folder */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        word		entry, 		/* entry number of child */
        dword *		childID);	/* Pointer to returned child ID # */

This routine returns information about one of a folder's children. The explicit return value will be true if the child is a folder, false if the child is a note. In addition, the passed dword pointer will point to the child's dword ID number.

Include: pen.goh

InkFolderGetChildNumber()

word 	InkFolderDisplayChildInList(
        dword 		fldr, 		/* ID# of folder */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        dword		note); 		/* ID# of child note or folder */

This routine returns the passed note or folder's entry number within its passed parent folder.

Include: pen.goh

InkFolderGetContents()

DBGroupAndItem 	InkFolderGetContents(
        dword 		tag, 				/* ID# of folder */
        VMFileHandle 		fh,				/* Handle of Ink DB file */
        DBGroupAndItem *		subFolders); 		/* pointer to return value */);

This routine returns the contents of a folder. It returns two chunk arrays, each of which is filled with dword ID numbers of the folder's children. The explicitly returned array holds the numbers of the folder's child notes. The routine also fills in a pointer with a DB item holding a chunk array with the ID numbers of the subfolders.

Include: pen.goh

InkFolderGetNumChildren()

dword 	InkFolderGetNumChildren( /* Subfolders:Notes */
        dword 		fldr, 		/* ID# of folder */
        VMFileHandle 		fh);		/* Handle of Ink DB file */

This message returns the number of children the Ink Database folder has. The high word of the return value holds the number of sub folders; the low word holds the number of notes.

Include: pen.goh

InkFolderMove()

void 	InkFolderMove(
        dword 		fldr, 		/* ID# of folder to move */
        dword 		pfldr);/* ID# of new parent folder */

This routine moves an Ink Database folder to a new location in the folder tree.

Include: pen.goh

InkFolderSetTitle()

void 	InkFolderSetTitle(
        dword 		tag, 		/* ID# of folder */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        const char *		name); 		/* Text object */);

This routine renames an Ink Database folder. The passed name should be null-terminated.

Include: pen.goh

InkFolderSetTitleFromTextObject()

void 	InkFolderSetTitleFromTextObject(
        dword 		tag, 		/* ID# of folder */
        FileHandle 		fh,		/* Handle of Ink DB file */
        optr		text); 		/* Text object */);

This routine sets the name of the passed Ink Database folder from the contents of the passed VisText object.

Include: pen.goh

InkGetDocPageInfo()

void 	InkGetDocPageInfo(
        PageSizeReport *		psr, 		/* Structure to fill with return value */
        VMFileHandle 		fh);

This routine returns the dword ID of the head folder of an Ink Database file.

Include: pen.goh

InkGetDocCustomGString()

GStateHandle 	InkGetDocCustomGString(
        VMFileHandle 		dbfh);

This routine returns the custom GString associated with the passed Ink Database file. Note that this custom background will only be used if the document's basic InkBackgroundType is IBT_CUSTOM. (This may be determined using the InkSetDocGString() routine.

Include: pen.goh

InkGetDocGString()

InkBackgroundType 	InkGetDocGString(
        VMFileHandle 		dbfh);

This routine returns the standard GString to use as a background picture with the passed Ink Database file. If the returned background type is custom, be sure to also call InkGetDocCustomGString() .

Include: pen.goh

InkGetParentFolder()

dword 	InkGetParentFolder(
        dword 		tag, 		/* ID# of folder or note */
        VMFileHandle 		fh);		/* Handle of Ink DB file */

This message returns the dword ID of the passed Ink Database note or folder.

Include: pen.goh

InkGetTitle()

word 	InkGetTitle(
        dword 		tag, 		/* ID# of folder or note */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        char *		dest); 		/* should be INK_DB_MAX_TITLE_SIZE +1 */);

This message fills the passed text buffer with the folder's or note's title, a null-terminated string. The routine's explicit return value is the length of the string (including the terminator).

Include: pen.goh

InkNoteCopyMoniker()

dword 	InkNoteCopyMoniker(
        dword 	title,	/* ID# of parent folder */
        optr 	list, 	/* Output list */
        word 	type, 	/* 1: text note
             * 0: ink note
             * -1: folder */
        word	entry);	/* Handle of Ink DB file */

This routine copies the icon nd title into the VisMoniker.

Include: pen.goh

InkNoteCreate()

dword 	InkNoteCreate(
        dword 		tag,		/* ID# of parent folder */
        VMFileHandle		fh);		/* Handle of Ink DB file */

This routine creates a note and adds it to the passed folder's child list. The new note's dword ID is returned.

Include: pen.goh

InkNoteCreatePage()

word 	InkNoteCreatePage(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh,		/* Handle of Ink DB file */
        word 		page); /* Page number to insert before,
                 * CA_NULL_ELEMENT to append */

This routine creates a new page within a note. It returns the new page number.

Include: pen.goh

InkNoteDelete()

void 	InkNoteDelete(
        dword 		tag, 		/* ID# of note */
        VMFileHandle 		fh);		/* Handle of Ink DB file */

This message deletes the passed note. All references to the note are deleted.

Include: pen.goh

InkNoteFindByKeywords()

ChunkHandle 	InkNoteFindByKeywords(
/* Return value is chunk array with elements:
 *  FindNoteHeader
 *  -dword tag-
 *  -dword tag-
 *   etc... */
        VMFileHandle 		fh,
        char *		strings,		/* strings to match (separated by
                    * whitespace or commas), can contain
                    * C_WILDCARD or C_SINGLE_WILDCARD */
        word 		opt,		/* true to match all keywords;
                 * false to match at least one keyword */

This routine returns a chunk array containing the dword ID numbers of all notes whose keywords match the passed search string, preceded by the number of matching notes. If no such notes are found, then the returned handle will be NULL.

Note that this routine will only return about 20K notes; if there are more that match, only the first 20K will be returned.

Include: pen.goh

InkNoteFindByTitle()

ChunkHandle 	InkNoteFindByTitle(
/* Return value is chunk array with elements:
 *  FindNoteHeader
 *  -dword tag-
 *  -dword tag-
 *   etc... */
        const char *		string,		/* string to match (can contain C_WILDCARD
                  * or C_SINGLE_WILDCARD */
        SearchOptions 		opt,		/* Search options */
        Boolean 		Body, 		/* true if you want to look in the body
                 * of text notes */
        VMFileHandle		fh);		/* Handle of Ink DB file */

This routine returns a chunk array containing the dword ID numbers of all notes whose titles match the passed search string, preceded by the number of matching notes. If no such notes are found, then the returned handle will be NULL.

Note that this routine will only return about 20K notes; if there are more that match, only the first 20K will be returned.

Include: pen.goh

InkNoteGetCreationDate()

dword 	InkNoteGetCreationDate(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh);		/* Handle of Ink DB file */

This routine gets a note's creation date.

Include: pen.goh

InkNoteGetKeywords()

void 	InkNoteGetKeywords(
        dword 		tag, 		/* ID# of note */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        char *		text); 		/* String to hold return value */);

This routine fills the passed buffer with the note's keywords. The target buffer should be of atleast length INK_DB_MAX_NOTE_KEYWORDS_SIZE +1. The string will be null-terminated.

Include: pen.goh

InkNoteGetModificationDate()

dword 	InkNoteGetModificationDate(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh);		/* Handle of Ink DB file */

This routine gets a note's modification date.

Include: pen.goh

InkNoteGetNoteType()

NoteType 	InkNoteGetNoteType( /* 0: Ink, 1: Text */
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh);		/* Handle of Ink DB file */

This routine gets a note's NoteType : NT_INK or NT_TEXT.

Include: pen.goh

InkNoteGetNumPages()

word 	InkNoteGetNumPages(
        dword 		tag);		/* ID# of note */

This routine returns the number of pages within the passed note.

Include: pen.goh

InkNoteGetPages()

DBGroupAndItem 	InkNoteGetPages(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh);		/* Handle of Ink DB file */

This routine returns a DB group and item containing a chunk array. The chunk array contains the page information of the note, either compressed pen data or text. Each array element holds one page of data.

Include: pen.goh

InkNoteLoadPage()

void 	InkNoteLoadPage(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh,		/* Handle of Ink DB file */
        word 		page, 		/* Page number */
        optr 		obj, 		/* an Ink or VisText object */
        word 		type);		/* note type 0: ink, 1: text */

This routine loads a visual object (Ink or Text) with the contents of the passed Ink Database page. Be sure to load only the correct type of data into an object.

Include: pen.goh

InkNoteMove()

void 	InkNoteMove(
        dword 		tag, 			/* ID# of note */
        dword 		pfolder, 			/* ID# of new parent folder */
        VMFileHandle 		fh);			/* Handle of Ink DB file */

This message moves the passed note to a new location. All references to the note are suitably altered.

Include: pen.goh

InkNoteSavePage()

void 	InkNoteSavePage(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh,		/* Handle of Ink DB file */
        word 		page, 		/* Page number */
        optr 		obj, 		/* an Ink or VisText object */
        word 		type);		/* note type 0: ink, 1: text */

This routine saves the contents of a visual object (Ink or Text) to the passed Ink Database page.

Include: pen.goh

InkNoteSendKeywordsToTextObject()

void 	InkNoteSendKeywordsToTextObject(
        dword 		tag, 		/* ID# of note */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        optr		text); 		/* Text object to set */);

This message replaces the passed VisText object's text with the keywords from the passed folder or note of an Ink Database file.

Include: pen.goh

InkNoteSetKeywords()

void 	InkNoteSetKeywords(
        dword 		tag, 			/* ID# of note */
        VMFileHandle 		fh,			/* Handle of Ink DB file */
        const char *		text); 			/* Keyword string */);

This message sets an Ink Database note's keywords. The passed string should be null-terminated.

Include: pen.goh

InkNoteSetKeywordsFromTextObject()

void 	InkNoteSetKeywordsFromTextObject(
        dword 		tag, 			/* ID# of note */
        VMFileHandle 		fh,			/* Handle of Ink DB file */
        optr *		text); 			/* Text object */);

This message sets an Ink Database note's keywords by copying them from the passed text object.

Include: pen.goh

InkNoteSetModificationDate()

void 	InkNoteSetModificationDate(
        word		tdft1, 		/* First two words of */
        word		tdft2,		/*   TimerDateAndTime structure */
        dword 		note,		/* ID# of note */
        FileHandle		fh);		/* Handle of Ink DB file */

This routine sets a note's modification date.

Include: pen.goh

InkNoteSetNoteType()

void 	InkNoteSetNoteType(
        dword 		tag,		/* ID# of note */
        VMFileHandle		fh,		/* Handle of Ink DB file */
        NoteType 		nt);		/* NT_INK or NT_TEXT */

This routine sets a note's type: text or ink.

Include: pen.goh

InkNoteSetTitle()

void 	InkNoteSetTitle(
        dword 		tag, 			/* ID# of note */
        VMFileHandle 		fh,			/* Handle of Ink DB file */
        const char *		name); 			/* Text object */);

This message renames an Ink Database note. The passed name should be null-terminated. The string may be up to INK_DB_MAX_NOTE_KEYWORDS_SIZE +1 in length.

Include: pen.goh

InkNoteSetTitleFromTextObject()

void 	InkNoteSetTitleFromTextObject(
        dword 		tag, 		/* ID# of note */
        FileHandle 		fh,		/* Handle of Ink DB file */
        optr		text); 		/* Text object */);

This message sets the name of the passed Ink Database note from the contents of the passed VisText object.

Include: pen.goh

InkSendTitleToTextObject()

void 	InkSendTitleToTextObject(
        dword 		tag, 		/* ID# of folder or note */
        VMFileHandle 		fh,		/* Handle of Ink DB file */
        optr		to); 		/* Text object to set */);

This message replaces the passed VisText object's text with the name from the passed folder or note of an Ink Database file.

Include: pen.goh

InkSetDocCustomGString()

void	InkSetDocCustomGString(
        VMFileHandle 		dbfh,
        Handle		gstring);

This routine sets the custom GString to use as a background for the passed Ink Database file. Note that this custom background will only be used if the document's basic InkBackgroundType is IBT_CUSTOM. (Set this using the InkSetDocGString() routine.)

Include: pen.goh

InkSetDocGString()

void	InkSetDocGString(
        VMFileHandle 		dbfh,
        InkBackgroundType		type);

This routine sets the standard GString to use as a background picture with the passed Ink Database file. If the passed background type is custom, be sure to also call InkSetDocCustomGString() .

Include: pen.goh

InkSetDocPageInfo()

void 	InkSetDocPageInfo(
        PageSizeReport *		psr,
        VMFileHandle 		fh);

Set the page information for an Ink Database file.

Include: pen.goh

IntegerOf()

word	IntegerOf(
        WWFixedAsDWord		wwf)

This macro returns the integral portion of a WWFixedAsDWord value.

Include: geos.h

isalnum()

int	LocalIsAlphaNumeric(
        int	__C);

This routine returns true if the passed character is alphanumeric.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isalpha()

int	isalpha(
        int	__C);

This routine returns true if the passed character is alphabetic.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

iscntrl()

int	iscntrl(
        int	__C);

This routine returns true if the passed character is a control character.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isdigit()

int	isdigit(
        int	__c);

This routine returns true if the passed character is a decimal digit.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isgraph()

int	isgraph(
        wint	__C);

This routine returns true if the passed character is displayable.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

islower()

int	islower(
        int	__C);

This routine returns true if the passed character is a lower case alphabetic character.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isprint()

int	isprint(
        int	__C);

This routine returns true if the passed character is printable (i.e. takes up a space when printing).

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

ispunct()

int	ispunct(
        int	__c);

This routine returns true if the passed character is a punctuation mark.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isspace()

int	isspace(
        int	__c);

This routine returns true if the passed character is whitespace.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isupper()

int	isupper(
        int	__c);

This routine returns true if the passed character is an upper case alphabetic character.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

isxdigit()

int	isxdigit(
        int	__C);

This routine returns true if the passed character is a hexadecimal digit.

Warnings: If you pass a char to this routine, beware: the routine takes an integer, and if the passed value gets sign-extended, the result may not be what you expect.

Include: localize.h

LMemAlloc()

ChunkHandle	 LMemAlloc(
        MemHandle		mh,			/* Handle of block containing heap */
        word		chunkSize);				/* Size of new chunk in bytes */

This routine allocates a new chunk in the LMem heap. The heap must be locked or fixed. It allocates a chunk, expanding the chunk table if enccessary, and returns the chunk's handle. The chunk is not zero-initialized. If the chunk could not be allocated, it returns a null handle. Chunks are dword-aligned, so the chunk's actual size may be slightly larger than you request.

Include: lmem.h

Be Sure To: Lock the block on the global heap (unless the block is fixed).

Warnings: The heap may be compacted; thus, all pointers to chunks are invalidated. If LMF_NO_EXPAND is not set, the heap may be resized (and thus moved), thus invalidating all pointers to that block. Even fixed blocks can be resized and moved.

See Also: LMemDeref(), LMemReAlloc().

LMemContract()

void	LMemContract(
        MemHandle		mh);		/* Handle of LMem heap */

This routine contracts an LMem heap; that is, it deletes all the free chunks, moves all the used chunks to the beginning of the heap (right after the chunk handle table), and resizes the block to free the unused space at the end. It's a good idea to call this routine if you have just freed a lot of chunks, since that will free up some of the global heap. The LMem heap is guaranteed not to move; however, all pointers to chunks will be invalidated.

Be Sure To: Lock the block on the global heap (if it isn't fixed).

Include: lmem.h

LMemDeleteAt()

void	LMemDeleteAt(
        optr	chunk,				/* Chunk to resize */
        word	deleteOffset,				/* Offset within chunk of first
                     * byte to be deleted */
        word	deleteCount);				/* # of bytes to delete */

This routine deletes a specified number of bytes from inside a chunk. It is guaranteed not to cause the heap to be resized or compacted; thus, pointers to other chunks remain valid.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Warnings: The bytes you delete must all be in the chunk. If deleteOffset and deleteCount indicate bytes that are not in the chunk, results are undefined.

Include: lmem.h

See Also: LMemReAlloc(),
LMemInsertAt(),
LMemDeleteAtHandles().

LMemDeleteAtHandles()

void	LMemDeleteAtHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		ch,				/* Handle of chunk to resize */
        word		deleteOffset,				/* Offset within chunk of first
                         * byte to be deleted */
        word		deleteCount);				/* # of bytes to delete */

This routine is exactly like LMemDeleteAt() above, except that the chunk is specified by its global and chunk handles.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Warnings: The bytes you delete must all be in the chunk. If deleteOffset and deleteCount indicate bytes that are not in the chunk, results are undefined.

Include: lmem.h

LMemDeref()

void *	LMemDeref(
        optr	chunk);	/* optr to chunk to dereference */

This routine translates an optr into the address of the chunk. The LMem heap must be locked or fixed on the global heap. Chunk addresses can be invalidated by many LMem routines, forcing you to dereference the optr again.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Include: lmem.h

See Also: LMemDerefHandles().

LMemDerefHandles()

void *	LMemDerefHandles(
        MemHandle		mh,				/* Handle of LMem heap's block */
        ChunkHandle		chunk);				/* Handle of chunk to dereference */

This routine is exactly like LMemDeref() above, except that the chunk is specified by its global and chunk handles.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Include: lmem.h

See Also: LMemDeref().

LMemFree()

void	LMemFree(
        optr	chunk);				/*optr of chunk to free */

This routine frees a chunk from an LMem heap. The chunk is added to the heap's free list. The routine is guaranteed not to compact or resize the heap; thus, all pointers within the block remain valid (except for pointers to data in the freed chunk, of course).

Be Sure To: Lock the block on the global heap (unless it is fixed).

Include: lmem.h

See Also: LMemFreeHandles().

LMemFreeHandles()

void	LMemFreeHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		chunk);				/* Handle of chunk to free */

This routine is just like LMemFree() above, except that the chunk is specified by its global and chunk handles (instead of by an optr).

Be Sure To: Lock the block on the global heap (unless it is fixed).

Include: lmem.h

LMemGetChunkSize()

word	LMemGetChunkSize(
        optr	chunk);					/* optr of subject chunk */

This routine returns the size (in bytes) of a chunk in an LMem heap. Since LMem chunks are dword-aligned, the chunk's size may be slightly larger than the size specified when it was allocated. The routine is guaranteed not to compact or resize the heap; thus, all pointers within the block remain valid.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Include: lmem.h

See Also: LMemGetChunkSizeHandles().

LMemGetChunkSizeHandles()

word	Routine(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		chunk);				/* Handle of chunk in question */

This routine is just like LMemGetChunkSize() above, except that the chunk is specified by its global and chunk handles (instead of by an optr).

Be Sure To: Lock the block on the global heap (unless it is fixed).

Include: lmem.h

See Also: LMemGetChunkSize().

LMemInitHeap()

void	LMemInitHeap(
        MemHandle		mh,				/* Handle of (locked or fixed)
                         * block which will contain heap 	*/
        LMemType		type,				/* Type of heap to create */
        LocalMemoryFlags		flags,				/* Record of LocalMemoryFlags */
        word		lmemOffset,				/* Offset of first chunk in heap */
        word		numHandles,				/* Size of starter handle table */
        word		freeSpace);				/* Size of first free chunk
                         * created */

This routine creates an LMem heap in a global memory block. The block must be locked or fixed in memory. The routine initializes the LMemBlockHeader , creates a handle table, allocates a single free chunk, and turns on the HF_LMEM flag for the block. The block will be reallocated if necessary to make room for the heap. The routine takes six arguments:

mh
The memory block's handle
type
A member of the LMemType enumerated type, specifying the kind of block to create. For most applications, this will be LMEM_TYPE_GENERAL.
flags
A record of LocalMemoryFlags , specifying certain properties of the heap. Most applications will pass a null record.
lmemOffset
The offset within the block at which to start the heap. This must be larger than the size of the LMemBlockHeader structure which begins every heap block. Any space between the LMemBlockHeader and the heap is left untouched by all LMem routines. Usually you can just pass sizeof( LMemBlockHeader ) as this argument, or sizeof( ObjLMemBlockHeader ) for object blocks.
numHandles
The number of entries to create in the block's chunk handle table. The chunk handle table will grow automatically when all entries have been used up. Applications should generally pass the constant STD_INIT_HANDLES; they should definitely pass a positive number.
freeSpace
The amount of space to allocate to the first free chunk. Applications should generally pass the constant STD_INIT_HEAP; they should definitely pass a positive number.

To destroy an LMem heap, call MemFree() to free the block containing the heap.

Structures: There are two special data types used by this routine: LMemType and LocalMemoryFlags .

LMem heaps are created for many different purposes. Some of these purposes require the heap to have special functionality. For this reason, you must pass a member of the LMemType enumerated type to specify the kind of heap to create. The following types can be used; other types exist but should not be used with LMemInitHeap() .

LMEM_TYPE_GENERAL
Ordinary heap. Most application LMem heaps will be of this type.
LMEM_TYPE_OBJ_BLOCK
The heap will contain object instance chunks.

When an LMem heap is created, you must pass a record of flags to LMemInitHeap() to indicate how the heap should be treated. Most of the LocalMemoryFlags are only passed by system routines; the flags available for this routine are: LMF_HAS_FLAGS, LMF_DETACHABLE, LMF_NO_ENLARGE, LMF_RETURN_ERRORS. The flags can be read by examining the LMemBlockHeader structure at the beginning of the block. Ordinarily, general LMem heaps will have all flags cleared.

Tips and Tricks: If you want a fixed data space after the header, declare a structure whose first element is an LMemBlockHeader and whose other fields are for the data you will store in the fixed data space. Pass the size of this structure as the LMemOffset argument. You can now access the fixed data area by using the fields of the structure.

Be Sure To: Pass an offset at least as large as sizeof(LMemBlockHeader) . If you pass an offset that is too small, the results are undefined. Lock the block on the global heap before calling this routine (unless the block is fixed).

Warnings: The block may be relocated, if its initial size is too small to accommodate the heap. This is true even for fixed blocks. If the flag LMF_NO_ENLARGE is set, the block will never be relocated; however, you must make sure it starts out large enough to accommodate the entire heap.

Include: lmem.h

See Also: LMemBlockHeader, LMemType, LocalMemoryFlags, MemAlloc(), MemAllocLMem(), MemFree(), VMAllocLMem().

LMemInsertAt()

void	LMemInsertAt(
        optr	chunk,				/* optr of chunk to resize */
        word	insertOffset,				/* Offset within chunk of first byte
                     * to be added */
        word	insertCount);				/* # of bytes to add */

This routine inserts space in the middle of a chunk and zero-initializes the new space. The first new byte will be at the specified offset within the chunk.

Be Sure To: Lock the block on the global heap (unless it is fixed). Make sure the offset is within the specified chunk.

Warnings: This routine may resize or compact the heap; thus, all pointers to data within the block are invalidated.

You must pass an insertOffset that is actually within the chunk; if the offset is out-of-bounds, results are undefined.

Include: lmem.h

See Also: LMemReAlloc(),
LMemDeleteAt(),
LMemInsertAtHandles().

LMemInsertAtHandles()

void	LMemInsertAtHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		chunk,				/* Chunk to resize */
        word		insertOffset,				/* Offset within chunk of first byte
                         * to be added */
        word		insertCount);				/* # of bytes to add */

This routine is just like LMemInsertAt() above, except that the chunk is specified by its global and chunk handles (instead of by an optr).

Be Sure To: Lock the block on the global heap (unless it is fixed). Make sure the offset is within the specified chunk.

Warnings: This routine may resize or compact the heap; thus, all pointers to data within the block are invalidated.

You must pass an insertOffset that is actually within the chunk; if the offset is out-of-bounds, results are undefined.

Include: lmem.h

LMemReAlloc()

Boolean	LMemReAlloc(
        optr	chunk,				/* optr of chunk to resize */
        word	chunkSize);				/* New size of chunk in bytes */

This routine resizes a chunk in an LMem heap. The heap must be in a locked or fixed block. If the routine succeeds, it returns zero. If it fails (because the heap ran out of space and could not be expanded), it returns non-zero.

If the new size is larger than the original size, extra bytes will be added to the end of the chunk. These bytes will not be zero-initialized. The heap may have to be compacted or resized to accommodate the request; thus, all pointers to data within the block are invalidated.

If the new size is smaller than the old, the chunk will be truncated. The request is guaranteed to succeed, and the chunk will not be moved; neither will the heap be compacted or resized. Thus, all pointers to other chunks remain valid. Reallocating a chunk to zero bytes is the same as freeing it.

Be Sure To: Lock the block on the global heap (unless it is fixed).

Warnings: As noted, if the new size is larger than the old, the heap may be compacted or resized, invalidating pointers.

Include: lmem.h

See Also: LMemReAllocHandles(),
LMemInsertAt(),
LMemDeleteAt().

LMemReAllocHandles()

Boolean	LMemReAllocHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle		chunk,				/* Handle of chunk to resize */
        word		chunkSize);				/* New size of chunk in bytes */

This routine is just like LMemReAlloc() above, except that the chunk is specified by its global and chunk handles (instead of by an optr).

Be Sure To: Lock the block on the global heap (unless it is fixed).

Warnings: As noted, if the new size is larger than the old, the heap may be compacted or resized, invalidating pointers.

Include: lmem.h

LocalAsciiToFixed()

WWFixedAsDWord LocalAsciiToFixed(
        const char *		buffer,
        char **		parseEnd);

This routines converts a string like "12.345" to a fixed point number. The pointer pointed to by parseEnd will be returned pointing into the buffer after the last character parsed.

Include: localize.h

LocalCalcDaysInMonth()

word 	LocalCalcDaysInMonth(
        word 	year,
        word 	month);

This routine computes the number of days in a given month.

LocalCmpStrings()

sword	LocalCmpStrings(
        const char *		str1,
        const char *		str2,
        word		strSize);

This routine compares two strings to determine which comes first in a lexical (i.e. alphabetic) ordering. If the return value is negative, then str1 is earlier than str2 . If the return value is positive, then str1 is later than str2 . If the return value is zero, then the strings appear at the same place in alphabetical order.

Include: localize.h

LocalCmpStringsDosToGeos()

sword	LocalCmpStringsDosToGeos(
        const char *					str1,
        const char *					str2,
        word					strSize,
        word					defaultChar,
        LocalCmpStringsDosToGeosFlags 					flags);

This routine compares two strings to determine which comes first in lexical ordering. Either or both of these strings may be a DOS string. If the return value is negative, then str1 is earlier than str2 . If the return value is positive, then str1 is later than str2 . If the return value is zero, then the strings appear at the same place in alphabetical order.

Structures:

        typedef ByteFlags LocalCmpStringsDosToGeosFlags;
/* The following flags may be combined using | and &:
 * 		LCSDTG_NO_CONVERT_STRING_2,
 * 		LCSDTG_NO_CONVERT_STRING_1 */

Include: localize.h

LocalCmpStringsNoCase()

sword	LocalCmpStringsNoCase(
        const char *		str1,
        const char *		str2,
        word		strSize);

This routine compares two strings to determine which comes first in a lexical (i.e. alphabetic) ordering. The comparison used is not case-sensitive. If the return value is negative, then str1 is earlier than str2 . If the return value is positive, then str1 is later than str2 . If the return value is zero, then the strings appear at the same place in alphabetical order.

Include: localize.h

LocalCmpStringsNoSpace()

sword 	LocalCmpStringsNoSpace(
        const char 		*str1,
        const char 		*str2,
        word 		strSize);

This routine compares two strings to determine which comes first in a lexical (i.e. alphabetic) ordering. The comparison ignores spaces. If the return value is negative, then str1 is earlier than str2 . If the return value is positive, then str1 is later than str2 . If the return value is zero, then the strings appear at the same place in alphabetical order.

LocalCmpStringsNoSpaceCase()

sword 	LocalCmpStringsNoSpaceCase(
        const char 		*str1,
        const char 		*str2,
        word 		strSize);

This routine compares two strings to determine which comes first in a lexical (i.e. alphabetic) ordering. The comparison ignores spaces and is case-insensitive. If the return value is negative, then str1 is earlier than str2 . If the return value is positive, then str1 is later than str2 . If the return value is zero, then the strings appear at the same place in alphabetical order.

LocalCodePageToGeos()

Boolean	LocalCodePageToGeos(
        char *		str,
        word		strSize,			/* Size of the string, in bytes */
        DosCodePage 		codePage,
        word		defaultChar);

This routine converts a DOS string to standard GEOS text using a specified code page. Any characters for which there is no GEOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalCodePageToGeosChar()

word	LocalCodePageToGeosChar(
        word	ch,
        DosCodePage codePage,
        word	defaultChar);

This routine converts a DOS character to standard GEOS text using a specified code page. Any character for which there is no GEOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalCustomFormatDateTime()

word	LocalCustomFormatDateTime(
        char *	str,			/* Buffer to save formatted text in */
        const char *	format,			/* Format string */
        const	TimerDateAndTime *dateTime);

This routine takes a date or time and constructs a string using a custom format.

Include: localize.h

LocalCustomParseDateTime()

word	LocalCustomParseDateTime(
        const char *		str,
        DateTimeFormat		format,
        TimerDateAndTime *		dateTime);

This routine parses a date and time string by comparing it with the passed DateTimeFormat . It fills in the fields of the TimerDateAndTime structure. Any fields which are not specified in the format string will be filled with -1.

If the string parses correctly, LocalCustomParseDateTime() returns -1. Otherwise it reutrns the offset to the start of the text which did not parse correctly.

Include: localize.h

LocalDistanceFromAscii()

WWFixedAsDword 	LocalDistanceFromAscii(
        const char *		buffer,
        DistanceUnit 		distanceUnits,
        MeasurementTypes		measurementType);

This routine takes a function like "72 pt" and returns a number representing the distance. The returned answer represents the measure in points, inches, centimeters, or some other measure as specified by the passed unit.

Include: localize.h

LocalDistanceToAscii()

word	LocalDistanceToAscii( /* Length of string, including NULL */
        char *		buffer,			/*Buffer to save formatted text in */
        word 		value,
        DistanceUnit 		distanceUnits,
        MeasurementType 		measurementType);

This routine takes a distance and a set of units and returns a string containing a properly formatted distance.

Include: localize.h

LocalDosToGeos()

Boolean	LocalDosToGeos(
        char *	str,
        word	strSize,
        word	defaultChar);

Convert a DOS string to GEOS text. Any characters for which there is no GEOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalDosToGeosChar()

word	LocalDosToGeosChar(
        word	ch,
        word	defaultChar);

Convert a DOS character to GEOS text. Any characters for which there is no GEOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalDowncaseChar()

word	LocalDowncaseChar(
        word	ch);

Return the lower case equivalent, if any, of the passed character.

Include: localize.h

LocalDowncaseString()

void	LocalDowncaseString(
        char *	str,
        word	size);		/* Size of string, in bytes */

Convert the passed string to its all lower case equivalent.

Include: localize.h

LocalFixedToAscii()

void	LocalFixedToAscii(
        char *	buffer,
        WWFixedAsDWord value,
        word	fracDigits);

This routine returns the ASCII expression of a fixed point number.

Include: localize.h

LocalFormatDateTime()

word	LocalFormatDateTime( /* Length of returned string */
        char *			str,
        DateTimeFormat 			format,
        const TimerDateAndTime *	dateTime);

This routine returns the string (e.g. "9:37") corresponding to the passed DateAndTime.

Include: localize.h

LocalFormatFileDateTime()

word 	LocalFormatFileDateTime(
        char 			*str,
        DateTimeFormat 			format,
        const FileDateAndTime 			*dateTime);

This utility routine provides a simple way to present a FileDateAndTime structure such as used with file-related operations.

Structures:

        typedef struct {
            byte        numberFormatFlags; /* NumberFormatFlags value */
            byte        decimalDigits;
            wchar       thousandsSeparator;
            wchar       decimalSeparator;
            wchar       listSeparator;
        } LocalNumericFormat;

The first byte is a NumberFormatFlags field specifying whether or not to use a leading zero. The second byte is the number of digits to show after the decimal point.

Include: localize.h

LocalGeosToCodePage()

Boolean	LocalGeosToCodePage(
        char *		str,
        word		strSize,
        DosCodePage 		codePage,
        word		defaultChar);

Convert a GEOS string to DOS text, using the specified code page. Any characters for which there is no DOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalGeosToCodePageChar()

word	LocalGeosToCodePageChar(
        word		ch,
        DosCodePage 		codePage,
        word		defaultChar);

Convert a GEOS character to DOS text, using the specified code page. Any character for which there is no DOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalGeosToDos()

Boolean	LocalGeosToDos(
        char *	str,
        word	strSize,
        word	defaultChar);

Convert a GEOS string to DOS text. Any characters for which there is no DOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalGeosToDosChar()

word	LocalGeosToDosChar(
        word	ch,
        word	defaultChar);

Convert a GEOS character to DOS text. Any character for which there is no DOS equivalent will be replaced by the passed default character.

Include: localize.h

LocalGetCodePage()

DosCodePage LocalGetCodePage(void);

This routine returns the current code page, used by DOS to handle international character sets.

Include: localize.h

LocalGetCurrencyFormat()

void	LocalGetCurrencyFormat(
        LocalCurrencyFormat *			buf,
        char *			symbol);

This routine returns the current currency format and symbol.

Include: localize.h

LocalGetDateTimeFormat()

void	LocalGetDateTimeFormat(
        char *		str,
        DateTimeFormat 		format);

This routine returns the user's preferred time and date formats.

Include: localize.h

LocalGetLanguage()

word 	LocalGetLanguage(void); /* Returns a StandardLanguage value */

This routine returns the user machine's language.

LocalGetMeasurementType()

MeasurementTypes LocalGetMeasurementType(void);

This routine returns the user preference between US and metric measurement systems.

Include: localize.h

LocalGetNumericFormat()

void	LocalGetNumericFormat(
        LocalNumericFormat *		buf);

This routine returns the user's preferred format for numbers.

Include: localize.h

LocalGetQuotes()

void	LocalGetQuotes(
        LocalQuotes *		quotes);

This routine returns the user's preferred quote marks.

Include: localize.h

LocalIsDateChar()

Boolean	LocalIsDateChar(
        word	ch);

This routine returns true if the passed character could be part of a date or time.

Include: localize.h

LocalIsDosChar()

Boolean	LocalIsDosChar(
        word	ch);

This routine returns true if the passed character is part of the DOS character set.

Include: localize.h

LocalIsNumChar()

Boolean	LocalIsNumChar(
        word	ch);

This routine returns true if the passed character is a number or part of the number format.

Include: localize.h

LocalIsSymbol()

Boolean	LocalIsSymbol(
        word	ch);

This routine returns true if the passed character is a symbol.

Include: localize.h

LocalIsTimeChar()

Boolean	LocalIsTimeChar(
        word	ch);

This routine returns true if the passed character is a number or part of the user's time format.

Include: localize.h

LocalLexicalValue()

word	LocalLexicalValue(
        word	ch);

This routine returns the passed character's lexical value, useful when trying to sort strings alphabetically.

Include: localize.h

LocalLexicalValueNoCase()

word	LocalLexicalValueNoCase(
        word	ch);

This routine returns the passed character's case-insensitive lexical value, useful when trying to sort strings alphabetically.

Include: localize.h

LocalParseDateTime()

Boolean	LocalParseDateTime(
        const char *		str,
        DateTimeFormat 		format,
        TimerDateAndTime *	dateTime);

This routine takes a string describing a date or time (e.g. "9:37") and parses it using the passed format. It returns TRUE (-1) on success. On failure it returns the offset to the start of the text that didn't match.

Include: localize.h

LocalSetCurrencyFormat()

void	LocalSetCurrencyFormat(
        const LocalCurrencyFormat *				buf,
        const char *				symbol);

This routine changes the stored preferred currency format.

Include: localize.h

LocalSetDateTimeFormat()

void	LocalSetDateTimeFormat(
        const char *		str,
        DateTimeFormat 		format);

This routine changes the stored preferred time and date format.

Include: localize.h

LocalSetMeasurementType()

void	LocalSetMeasurementType(
        MeasurementTypes meas);

This routine changes the stored preferred measurement type.

Include: localize.h

LocalSetNumericFormat()

void	LocalSetNumericFormat(
        const LocalNumericFormat *				buf);

This routine changes the stored preferred number format.

Include: localize.h

LocalSetQuotes()

void	LocalSetQuotes(
        const LocalQuotes *		quotes);

This routine changes the stored preferred quote marks.

Include: localize.h

LocalStringLength()

word	LocalStringLength(
        const char *		str);

This routine returns the length (in characters) of a null-terminated string (not counting the null), even for multibyte character sets.

Include: localize.h

LocalStringSize()

word	LocalStringSize(
        const char *		str);

This routine returns the size (in bytes) of a null-terminated string.

Include: localize.h

LocalUpcaseChar()

word	LocalUpcaseChar(
        word	ch);

This routine returns the upper case equivalent, if any, of the passed character.

Include: localize.h

LocalUpcaseString()

void	LocalUpcaseString(
        char *	str,
        word	size);

This routine converts the passed string to its all upper case equivalent.

Include: localize.h

LogAddEntry()

void 	LogAddEntry(
        LogEntry 		*logEntry);

This function adds an entry to the Contact log. (The Contact log keeps track of what calls the user has sent to and received from each of their contacts; this is the function that logs a call.) The function takes on argument, a LogEntry structure.

typedef struct {
  NameOrNumber 			LE_number;
  dword 			LE_contactID;
  LogEntryType 			LE_type;
  LogEntryDirection 			LE_direction;
  dword 			LE_duration;
  DateAndTime 			LE_datetime;
  LogEntryFlags 			LE_flags;
} LogEntry;

Normally, this function is invoked twice for each call--once at the start of the call, and again when the call is done.

When invlking the function at the start of the call, set up the LogEntry structure. Fill in the LE_number and/or LE_contactID fields if you know the contact's name, number, or RecordID number, Fill in the LE_type field with one of LET_CALL, LET_FAX, LET_SMS, LET_DATA_MAILBOX, LET_DATA_WWW, or LET_DATA_TELNET. Fill in the LE_direction field with one of LED_SENT, LED_RECEIVED, or LED_MISSED. The LE_duration should be zero. Fill in the LE_datetime field with the current date and time. (The TimerGetDateAndTime() function returns this information.) Leave the LE_flags field alone.

Keep the structure around after you call the routine; it will fill in some of the fields. Then when the call is complete, fill in the LE_duration field and invoke the function again. The LE_duration field should contain the call's duration in seconds. One way to compute this time is to call TimerGetCount() twice: once at the start of the call and once at the end. By subtracting the start time from the end time and dividing by 60, one can compute the number of seconds that passed.

Having invoked the function a second time, the program may safely free the LogEntry structure.

Include: contlog.goh

See Also: TimerGetDateAndTime(), TimerGetCount().

LogDeleteAllEntries()

void 	LogDeleteAllEntries();

This function deletes all entries in the contact log.

Include: contlog.goh

LogDestroy()

void 	LogDestroy();

This function deletes the contact log's log file.

Include: contlog.goh

MailboxAcknowledgeMessageReceipt()

 void 	MailboxAcknowledgeMessageReceipt(
    MailboxMessage 		msg);

This routine tells the Mailbox library that the application has taken control of the message msg. msg will be visually removed from the system inbox only when this function is called.

Warning: When the mailbox receives a message targeted for an application, it sends that application the message MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE. Should the application not acknowledge the message with MailboxAcknowledgeMessageReceipt() and the application exits and restarts, the application will again be notified of the message's availability.

Include: mailbox.h

MailboxBodyReformatted()

 MailboxError 	MailboxBodyReformatted(
    MailboxMessage 	msg,
    MailboxDataFormat 	newFormat,
    MailboxMessageFlags 	newBodyFlags);

This routine changes the format indication of the message msg to newFormat and sets the MailboxMessageFlags newBodyFlags to either MMF_BODY_DATA_VOLATILE or MMF_DELETE_BODY_AFTER_TRANSMISSION. This routine does not alter the contents of msg msg.If msg is invalid then a MailboxError will be generated.

The MailboxDataFormat structure is formatted as:

    typedef struct {		        word		MDF_id;     /*GeoworksMailboxDataFormatID*/        ManufacturerID	MDF_manuf;    } MailboxDataFormat;

Warning: MailboxBodyReformatted() is used to change the format of msg in-place. To create a copy of a message in a new format, use MailboxChangeBodyFormat() .

Include: mailbox.h

MailboxChangeBodyFormat()

 MailboxError	MailboxChangeBodyFormat(
    MailboxMessage 	msg,
    const MailboxChangeBodyFormatArgs 	*mcbfArgs);

This routine replaces the format and the body of the message msg with mcbfArgs. If msg is invalid or the body is in use, then a MailboxError will be generated.

Warning: MailboxChangeBodyFormat() should be used when creating a copy of a message in a new format. To reformat the message in-place, use MailboxBodyReformatted() .

Include: mailbox.h

MailboxChangeToMailboxDir()

void	MailboxChangeToMailboxDir(void);

This routine changes the application's current working directoryto the directory where the Mailbox library holds its message bodies and administrative files.

Warning: This is primarily intended for use by data and transport drivers.

Include: mailbox.h

MailboxCheckMediumAvailable()

Boolean  MailboxCheckMediumAvailable(
    MediumType 	mediumType,
    word 	unitNum,
    MediumUnitType 	unitType);

If the transmission medium mediumType exists, then this routine returns TRUE (non-zero); otherwise, it returns FALSE (zero). Since the Mailbox library cannot control the comings and goings of the different media, there is no guarantee that a medium which exists now will exist later.

For example, if a modem PCMCIA card is inserted, then the DATA_MODEM medium will be available (and the unit number will be the SERIAL_COMx constant by which the modem can be reached). When the PCMCIA card is removed, then that medium will no longer be available and MailboxCheckMediumAvailable() will return FALSE.

Warning: If unitType is a MUT_MEM_BLOCK (a handle to other data), it means that the Mailbox library will take responsibility for freeing the memory block. In this case, the application should no longer reference the handle it passed in as the unit number.

Include: mailbox.h

MailboxCheckMediumConnected()

Boolean 	MailboxCheckMediumConnected(
    MediumType 	mediumType,
    word 	unitNum,
    MediumUnitType 	unitType);

Sees if the Mailbox library has been told whether a connection over the specified transmission medium exists. The medium is not guaranteed to remain connected once this call returns, as the Mailbox library has no control over connections, usually. This routine returns non-zero if, while the call was in-progress, the medium using MediumType mediumType, unit number unitNum (ignored if unitType is set to MUT_NONE), and MediumUnitType unitType were connected; otherwise, the routine returns zero.

Warning: If unitType is a MUT_MEM_BLOCK (a handle to other data), it means that the Mailbox library will take responsibility for freeing the memory block. In this case, the application should no longer reference the handle it passed in as the unit number.

Include: mailbox.h

MailboxDeleteMessage()

 void 	MailboxDeleteMessage(MailboxMessage msg);

This routine lets the Mailbox library know that it is free to delete the message msg. This routine should only be called after the application has called MailboxAcknowledgeMessageReceipt() to acknowledge that it has finished handling the message.

Include: mailbox.h

MailboxDoneWithBody()

 void 	MailboxDoneWithBody(
    MailboxMessage	msg,
    const void 	*appRef,
    word 	appRefSize);

This routine indicates that the body reference appRef returned by an earlier call to MailboxGetBodyRef() will no longer be used, so the data driver is free to close the file, etc. appRef points to the body of the message msg, and appRefSize refers to the size of this body, in bytes.

Warning: After calling this routine, no further use of the body via appRef may be made.

Include: mailbox.h

MailboxDoneWithVMFile()

 void 	MailboxDoneWithVMFile(	VMFileHandle file);

Lets the Mailbox know that the application is done using the file file, a parameter which was returned from MailboxGetVMFile() . The Mailbox library is free to close file after this routine is called.

Include: mailbox.h

MailboxFreeDriver()

void	MailboxFreeDriver(
    GeodeHandle 	driverHandle);

This routine unloads the driver driverHandle previously loaded by the Mailbox library. Note that a call to MailboxLoad...Driver() should be balanced with a call to MailboxFreeDriver() .

See Also: MailboxLoadDataDriver()
MailboxLoadDataDriverWithError()
MailboxLoadTransportDriver()

Include: mailbox.h

MailboxGetAdminFile()

VMFileHandle	MailboxGetAdminFile(void);

Returns the handle of the main administrative VM file that is maintained by the Mailbox library.

Warning: The returned file should NOT be used for general storage of data. This routine is intended to allow applications that wish to register a message without the aid of a MailboxSendControl to create the transData argument required by the transport driver they've chosen, should the driver expect something to be allocated within the admin file.

Include: mailbox.h

This document is in Beta. Ride at your own risk. Mail us your comments and suggestions

MailboxGetBodyFormat()

 MailboxError	MailboxGetBodyFormat(
    MailboxMessage 	msg,
    MailboxDataFormat 	*dataFormat);

This routine gets the MailboxDataFormat token of the message msg and copies it into dataFormat. It returns a MailboxError if the routine is invalid.

Include: mailbox.h

MailboxGetBodyMboxRefBlock()

 MailboxError	MailboxGetBodyMboxRefBlock(
    MailboxMessage 	msg,
    MemHandle 	*mboxRefHandle);

Fetches the opaque mailbox reference to the message body of message msg and stores it in the block mboxRefHandle. This returned block contains a mailbox reference structure specific to the Data Driver. Examples are:

    /* for mailbox data drivers that use VMChains */
    typedef {
        dword VMTMR_vmChain;
        TCHAR VMTMR_filename[];
    } VMTreeMboxRef;

    /* for mailbox data drivers that use raw data files */
    typedef {
        Boolean FMR_deleteAfterTransmit;
        word FMR_diskDataOffset;
        word FMR_diskDataLen;
        word FMR_filenameLen;
        byte FMR_filenameAndDiskData[];
            /* start of null-terminated filename,
             * followed by diskData (at
             * FMR_diskDataOffset). */
    } FileDDMboxRef;
The routine returns ME_INVALID_MESSAGE if the message is invalid, or ME_NOT_ENOUGH_MEMORY if the block of data could not be created.

Note: This routine is normally used by the mailbox drivers, not by applications.

Include: mailbox.h

MailboxGetBodyRef()

 MailboxError	MailboxGetBodyRef(
    MailboxMessage 	msg,
    void 	*appRefBuf,
    word 	*appRefBufSize);

This routine retrieves the address of the message body of the message msg in the format understood and used by applications registering a message with the Mailbox library, the app-reference. The definitions of app-reference structures are defined in the documentation for each of the various communication methods, e.g. the SMS chapter. This reference to the body is copied into the buffer appRefBuf. appRefBufSize initially points to the size of this buffer in bytes, and then it is set to the number of bytes that were copied into the buffer. In the case of an error (msg is invalid, the library is unable to load the driver, there is insufficient memory, the app-ref buffer too small, there is no message body available) a MailboxError will be generated.

Warning: Each call to this routine must be matched by a call to MailboxDoneWithBody() .

Include: mailbox.h

MailboxGetDestApp()

 MailboxError	MailboxGetDestApp(
    MailboxMessage 	msg,
    GeodeToken 	*tokenBuf);

Gets the GeodeToken for the destination application from the message msg and copies it into tokenBuf. This routine returns ME_INVALID_MESSAGE if the passed MailboxMessage is invalid.

Include: mailbox.h

MailboxGetEndBound()

 MailboxError	MailboxGetEndBound(
    MailboxMessage 	msg,
    FileDateAndTime 	*dateTime);

Fetches the end bound of the message msg and copies it into dateTime. If the message is invalid, this routine returns ME_INVALID_MESSAGE.

See Also: MailboxGetStartBound()

Include: mailbox.h

MailboxGetFirstMediumUnit()

 word	 MailboxGetFirstMediumUnit(
    MediumType 	mediumType,
    MediumUnitType 	*unitType);

Puts the first available unit of the indicated MediumType mediumType into unitType. This should be used when a transport driver just wants to use a particular medium but doesn't care which one. This routine returns the unit number.

Warning: Do not call this for a medium that doesn't use unit numbers. You will generate a fatal-error in the error-checking version, and won't get any additional information in the non-error-checking version (since the return will be MUT_NONE whether there's an available unit or not). Use MailboxCheckMediumAvailable() instead. Also remember, if unitType is a handle to other memory because it is a MUT_MEM_BLOCK, it will be your responsibility to free the associated memory block.

Include: mailbox.h

MailboxGetMessageFlags()

 MailboxError	MailboxGetMessageFlags(
    MailboxMessage 	msg,
    MailboxMessageFlags 	*flags);

Retrieves the MailboxMessageFlags for the message msg and copies them into flags. If the message is invalid, ME_INVALID_MESSAGE is returned.

Include: mailbox.h

MailboxGetNumTransAddrs()

 MailboxError	MailboxGetNumTransAddrs(
    MailboxMessage 		msg,
    word 	*numAddresses);

Retrieves the number of transport addresses bound to the message msg and stores that number in numAddresses. If msg is in the inbox, then numAddressess is set to zero. If the number of addresses can't be obtained, then the routine returns ME_INVALID_MESSAGE.

See Also: MailboxGetTransAddr()

Include: mailbox.h

MailboxGetStartBound()

 MailboxError	MailboxGetStartBound(
    MailboxMessage 	msg,
    FileDateAndTime	*dateTime);

Fetches the start bound of the message msg and copies it into dateTime. If the message is invalid, this routine returns ME_INVALID_MESSAGE.

See Also: MailboxGetEndBound()

Include: mailbox.h

MailboxGetStorageType()

 MailboxError 	MailboxGetStorageType(
    MailboxMessage 	msg,
    MailboxStorage 	*storage);

This routine gets the MailboxStorage token for the message msg and copies it into storage. If the message is invalid, ME_INVALID_MESSAGE is returned.

Include: mailbox.h

MailboxGetSubjectBlock()

 MailboxError	MailboxGetSubjectBlock(
    MailboxMessage 	msg,
    MemHandle 	*subjHandle);

Retrieves the null-terminated subject/summary of the message msg and copies it into a global memory block, then puts the associated MemHandle into subjHandle. Returns ME_INVALID_MESSAGE if msg is invalid or ME_NOT_ENOUGH_MEMORY if there isn't sufficient memory to create the block.

Include: mailbox.h

MailboxGetSubjectLMem()

 MailboxError	MailboxGetSubjectLMem(
    MailboxMessage 	msg,
    MemHandle 	lmemBlock,
    ChunkHandle 	*subjectChunk);

Retrieves the subject/summary of the message msg and copies it into a local memory block lmemBlock, and puts the ChunkHandle for this null-terminated subject into subjectChunk. Returns ME_INVALID_MESSAGE if msg is invalid or ME_NOT_ENOUGH_MEMORY if there isn't sufficient memory to create the block.

Include: mailbox.h

MailboxGetTransAddr()

 Boolean	MailboxGetTransAddr(
    MailboxMessage 	msg,
    word 	addrNumber,
    void 	*addressBuffer,
    word 	*bufSizePtr);

This routine retrieves the addrNumber-th transport address from the message msg and copies the address into addressBuffer. bufSizePtr should initially point to the size of the buffer in bytes and, if the routine works successfully, it will be set to the number of bytes that were actually copied into addressBuffer. If the addrNumber is invalid because it is either too large or the message doesn't exist, then bufSizePtr will be set to zero. If the buffer isn't large enough to store the address, then bufSizePtr is set to the number of bytes required. The routine returns zero (FALSE) if an error occurred, otherwise it returns non-zero.

See Also: MailboxGetNumTransAddrs()
MailboxGetUserTransAddrLMem()
MailboxSetTransAddr()

Include: mailbox.h

MailboxGetTransData()

 MailboxError 	MailboxGetTransData(
    MailboxMessage 	msg,
    dword 	*transData);

Puts the 32-bit transport data registered with the message msg into transData. If msg is an invalid message then the routine returns ME_INVALID_MESSAGE.

See Also: MailboxSetTransData()

Include: mailbox.h

MailboxGetUserTransAddrLMem()

 MailboxError 	MailboxGetUserTransAddrLMem(
    MailboxMessage 	msg,
    word 	addrNumber,
    MemHandle 	lmemBlock,
    ChunkHandle 	*addrChunk);

Retrieves a user-readable transport address from a message. The input parameters are the message msg, the address number addrNumber, and the handle of the lmem block lmemBlock in which to place the string. The routine copies the null-terminated string into this block, and sets addrChunk to point to the string's chuck. There are three errors that could occur: if addrNumber is not valid, ME_ADDRESS_INVALID is returned; if msg is not valid, ME_INVALID_MESSAGE is returned; and if there is insufficient memory to create the chunk, ME_NOT_ENOUGH_MEMORY is returned;

See Also:MailboxGetTransAddr()

Include: mailbox.h

This document is in Beta. Ride at your own risk. Mail us your comments and suggestions

MailboxGetVMFile()

 VMFileHandle 	MailboxGetVMFile(
    word 	expectedNumBlocks,
    word 	*vmStatusp);

This routine obtains the handle of a writable VM file in which data can be stored. The file is managed by the Mailbox library, so the data stored in it can be passed off to the library without being marked as MMF_BODY_DATA_VOLATILE.

Parameters:

expectedNumBlocks
This parameter is your estimate of your file's size, in blocks. The library uses this estimate to rationally apportion data among various VM files without getting block sizes within one file so large that the system is in danger of running out of conventional memory. Poor estimates will usually not cause fatal problems since the library is rather cautious when deciding whether to start a new file. Still, you should do your best to come up with accurate information on your needs. You should request 1 block for every 4Kb of data to store. For example, SMS message would only require 1 block. If expectedNumBlocks is set to zero, then a system-assumed value will be used.
vmStatusp
This is a pointer to a word that, in the case of an error, will be set to the error type.

Returns: If successful, this routine returns a VMFileHandle to a writable VM File. If it fails, it returns NullHandle and sets vmStatusp.

Include: mailbox.h

MailboxGetVMFileName()

void 	MailboxGetVMFileName(
    VMFileHandle 	file,
    FileLongName 	*namep);

Gets the name of a VM file file which was previously opened by the Mailbox library. This file resides in the Mailbox library's spool directory. namep is a buffer in which to place the file's FileLongName, a null-terminated file name.

Warning: MailboxGetVMFileName() is used primarily by data storage drivers to obtain a persistent reference to the body of a message since file handles aren't preserved across shutdowns or file closes.

Include: mailbox.h

MailboxLoadDataDriver()

 GeodeHandle	MailboxLoadDataDriver(
    MailboxStorage 	storage,
    GeodeLoadError 	*error);

MailboxLoadDataDriver() loads the specified data storage driver.

Parameters:

storage is a MailboxStorage token of the requested data storage driver.
error is a buffer to store a GeodeLoadError should the driver not load properly.

Return: If this routine is successful, it returns the handle of the loaded driver. Otherwise, the error is put into error.

This driver should be unloaded using MailboxFreeDriver() .

Include: mailbox.h

MailboxLoadDataDriverWithError()

 GeodeHandle	MailboxLoadDataDriverWithError(
    MailboxStorage 	storage,
    GeodeLoadError 	*error);

MailboxLoadDataDriverWithError() loads the specified data storage driver. If the driver can't be loaded then the error message error will be used to prompt the user to make the driver available, with the option to retry the load.

Parameters:

storage is a MailboxStorage token of the requested data storage driver.
error should point to a notification string that the user will see if the driver cannot load properly. If error points to NULL, then this routine behaves just like MailboxLoadDataDriver() .

Return: If this routine is successful, it returns the handle of the loaded driver. Otherwise, the error is put into error.

This driver should be unloaded using MailboxFreeDriver().

Include: mailbox.h

MailboxLoadTransportDriver()

 GeodeHandle	MailboxLoadTransportDriver(
    MailboxTransport 	transport,
    GeodeLoadError 	*error);

MailboxLoadTransportDriver() loads the transport driver with the MailboxTransport token transport and returns the handle of the driver. If the driver can't be loaded, then the GeodeLoadError is put into error.

The driver should be unloaded using MailboxFreeDriver() .

Include: mailbox.h

MailboxOpenVMFile()

 VMFileHandle 	MailboxOpenVMFile(
    FileLongName 	*namep,
    word 	*vmStatusp);

Reopens a VM file that was previously returned by MailboxGetVMFileName() , and whose name namep was obtained through MailboxGetVMFileName. If successful, the routine returns a VMFileHandle to the VMFile. If there is an error, this routine returns NullHandle with vmStatusp filled in with the appropriate VMStatus value.

Warning: Any call to this routine must be matched with a call to MailboxDoneWithVMFile() .

Include: mailbox.h

MailboxPushToMailboxDir()

void 	MailboxPushToMailboxDir();

This routine performs a FilePushDir() followed by a MailboxChangeToMailboxDir() .

Include: mailbox.h

MailboxRegisterMessage()

MailboxError MailboxRegisterMessage(
    MailboxRegisterMessageArgs 	*mrmArgs,
    MailboxMessage 		*msg);

The routine registers a message with the system. A message consists of the information the system needs to properly process that message, the MailboxRegisterMessageArgs mrmArgs, and the body data of the message itself, msg. The body data are copied, if necessary, or simply taken-possession-of by the system.

You can find examples of filling in MailboxRegisterMessageArgs in the chapters that discuss the various communication methods, such as the SMS chapter.

Include: mailbox.h

MailboxSetTransAddr()

 MailboxError	MailboxSetTransAddr(
    MailboxMessage msg,
    word addrNumber,
    const void *newAddress,
    word addrSize);

Sets the opaque transport address of the addrNumber-th address for the message msg to newAddress, replacing the existing one. addrSzie is the size of the new address buffer, in bytes. This routine returns ME_INVALID_MESSAGE if msg is invalid or ME_NOT_ENOUGH_MEMORY if there is not enough memory.

Warning: The new address may not differ from the old address in its significant address bytes. This is not intended to allow arbitrary redirection of a message, but simply for transport drivers to record their progress for a particular address in the insignificant portion of the address.

See Also: MailboxGetTransAddr()

Include: mailbox.h

MailboxSetTransData()

 MailboxError 	MailboxGetTransData(
    MailboxMessage 	msg,
    dword 	transData);

This routine replaces the 32-bit transport data registered with the message msg with transData. If the message is invalid then the routine returns ME_INVALID_MESSAGE; if successful, it returns ME_SUCCESS.

Warning: The caller is responsible for freeing any resources referred to by the previous dword transport data since the Mailbox library places absolutely no interpretation on the transData, and thus cannot know what needs to be freed when one transData dword replaces another.

See Also: MailboxGetTransAddr()

Include: mailbox.h

MailboxStealBody()

 MailboxError	MailboxStealBody(
    MailboxMessage 	msg,
    void 	*appRefBuf,
    word 	*appRefBufSize);

This routine lets an application take possession of the body of the message msg. It puts the app-reference to the body in appRefBuf. The app-reference structure defined for each communication mode is defined in the documentation for that communication mode, e.g. the SMS chapter. The application is responsible for destroying the data associated with this app-reference. If the message body is within a VM file, the application is responsible for calling MailboxDoneWithVMFile() after freeing the data that make up the body. The parameters for this routine are the message msg, a pointer appRefBuf to an app-reference, and a word appRefBufSize that points to the size of appRefBuf, in bytes. appRefBufSize is then filled with the number of bytes that are put into appRefBuf.

The possible errors are ME_INVALID_MESSAGE, ME_NOT_ENOUGH_MEMORY, ME_CANNOT_LOAD_DATA_DRIVER, ME_CANNOT_LOAD_TRANSPORT_DRIVER, ME_APP_REF_BUF_TOO_SMALL, ME_CANNOT_OPEN_MESSAGE_FILE, and ME_CANNOT_READ_MESSAGE_FILE.

Include: mailbox.h

MakeWWFixed()

WWFixed MakeWWFixed(number);

This macro casts a floating-point or integer number to a WWFixed value.

Include: geos.h

malloc()

void	* malloc(
        size_t		blockSize);				/* # of bytes to allocate*/

The malloc() family of routines is provided for Standard C compatibility. If a geode needs a small amount of fixed memory, it can call one of the routines. The kernel will allocate a fixed block to satisfy the geode's malloc() requests; it will allocate memory from this block. When the block is filled, it will allocate another fixed malloc-block. When all the memory in the block is freed, the memory manager will automatically free the block.

When a geode calls malloc() , a section of memory of the size specified will be allocated out of its malloc-block, and the address of the start of the memory will be returned. The memory will not be zero-initialized. If the request cannot be satisfied, malloc will return a null pointer. The memory is guaranteed not to be moved until it is freed (with free() ) or resized (with realloc() ). When GEOS shuts down, all fixed blocks are freed, and any memory allocated with malloc() is lost.

Using too many fixed blocks degrades the memory manager's performance, slowing the whole system. For this reason, applications should not use malloc -family routines if they can possibly be avoided. They are provided only to simplify porting of existing programs; however, applications should make every effort to use the GEOS memory management and LMem routines instead. If you must use the malloc -family routines, use them sparingly, and free the memory as quickly as possible.

Tips and Tricks: You can allocate memory in another geode's malloc-block by calling GeoMalloc() . However, that block will be freed when the other geode exits.

Warnings: All memory allocated with malloc() is freed when GEOS shuts down.

Include: stdlib.h

See Also: calloc(),
free(),
GeoMalloc(),
realloc().

ManufacturerFromFormatID

word	ManufacturerFromFormatID(id);
        ClipboardItemFormatID id;

This macro extracts the word-sized manufacturer ID (of type ManufacturerIDs ) from a ClipboardInfoFormatID argument.

MemAlloc()

MemHandle MemAlloc(
        word		byteSize,				/* Size of block in bytes */
        HeapFlags		hfFlags,				/* Type of block */
        HeapAllocFlags		haFlags);				/* How to allocate block */

This routine allocates a global memory block and creates an entry for it in the global handle table. The properties of the block are determined by the HeapFlags record passed; the way the block should be allocated is determined by the HeapAllocFlags record. Both sets of flags are described below. The routine returns the block's handle. If it could not allocate the block, it returns a null handle. The block allocated may be larger than the size requested, as the block size is rounded up to the next even paragraph (one paragraph equals sixteen bytes).

HeapFlags are stored in the block's handle table entry. They can be retrieved with the routine MemGetInfo() ; some of them can be changed with the routine MemModifyFlags() . The following flags are available:

HF_FIXED
The block will not move from its place in the global heap until it is freed. If this flag is off, the memory manager may move the block while it is unlocked. If the flag is on, the block may not be locked, and HF_DISCARDABLE and HF_SWAPABLE must be off.
HF_SHARABLE
The block may be locked by threads belonging to geodes other than the block's owner.
HF_DISCARDABLE
The block may be discarded when unlocked.
HF_SWAPABLE
The block may be swapped to extended/expanded memory or to the disk swap space when it is unlocked.
HF_LMEM
The block contains a local memory heap. This flag is set automatically by LMemInitHeap() and VMAllocLMem() ; applications should not need to set this flag.
HF_DISCARDED
The memory manager turns this bit on when it discards a block. The bit is turned off when the block is reallocated.
HF_SWAPPED
The memory manager turns this bit on when it swaps a block to extended/expanded memory or to the disk swap space. It turns the bit off when it swaps the block back into the global heap.

HeapAllocFlags indicate how the block should be allocated and initialized. They are not stored and can not be retrieved. Some of the flags can be passed with MemReAlloc() . The following flags are available:

HAF_ZERO_INIT
The memory manager should initialize the block to null bytes. This flag may be passed to MemReAlloc() to cause new memory to be zero-initialized.
HAF_LOCK
The memory manager should lock the block after allocating it. To get the block's address, call MemDeref() . This flag may be passed to MemReAlloc() .
HAF_NO_ERR
The memory manager should not return errors. If it cannot allocate block, GEOS will crash. Use of this flag is strongly discouraged. This flag may be passed to MemReAlloc() .
HAF_UI
If both HAF_OBJECT_RESOURCE and HAF_UI are set, this block will be run by the application's UI thread.
HAF_READ_ONLY
The block's data will not be modified. Useful for the debugger.
HAF_OBJECT_RESOURCE
This block will be an object block.
HAF_CODE
This block contains executable code.
HAF_CONFORMING
If the block contains code, the code may be run by a less privileged entity. If the block contains data, the data may be accessed or altered by a less privileged entity.

If necessary, this routine will automatically compact the heap to find enough space to allocated the desired sized block.

Include: heap.h

See Also: MemAllocSetOwner(),
MemReAlloc(),
MemDeref().

MemAllocLMem()

MemHandle MemAllocLMem(
        LMemType		type,				/* type of LMem block */
        word		headerSize);				/* size of header structure */

This routine allocates and initializes a local memory block; it can be used to simplify this procedure from the two-step process of MemAlloc() followed by LMemInitHeap() . Pass an LMem type indicating what will be stored in the block, along with the size of the header structure to use. If the block is to have the standard header, pass zero in headerSize .

This routine returns the handle of the unlocked, newly allocated block. The block will contain two LMem handles and 64 bytes allocated for the LMem heap.

Include: lmem.h

See Also: LMemInitHeap().

MemAllocSetOwner()

MemHandle MemAllocSetOwner(
        GeodeHandle		owner,				/* Handle of block's owner */
        word		byteSize,				/* Size of block in bytes */
        HeapFlags		hfFlags,				/* Type of block */
        HeapAllocFlags		haFlags);				/* How to allocate block */

This routine is the same as MemAlloc() except that you can specify the owner of the global memory block created.

Include: heap.h

See Also: MemAlloc().

MemDecRefCount()

void	MemDecRefCount(
        MemHandle		mh);				/* handle of affected block */

This routine decrements the reference count of a global memory block (the reference count is stored in HM _otherInfo ). If the reference count reaches zero, MemDecRefCount() will free the block.

Warnings: This routine assumes that a reference count is stored in HM _otherInfo . You may only use this routine if the block has had a reference count set up with MemInitRefCount() .

Include: heap.h

MemDeref()

void	* MemDeref(
        MemHandle		mh);		/* Handle of locked block to dereference */

This routine takes one argument, the handle of a global memory block; it returns the address of the block on the global heap. If the block has been discarded, or if the handle is not a memory handle, it returns a null pointer. It gets this information by reading the block's handle table entry; it does not need to actually access the block.

Note that if the handle is of an unlocked, moveable block, MemDeref() will return the block's address with out any warning; however, the address will be unreliable, since the memory manager can move the block at any time.

Include: heap.h

Tips and Tricks: This is very useful when you allocate a fixed or locked block, and need to get the block's address without calling MemLock() .

Warnings: This routine, if given an unlocked, moveable block, will return the pointer without a warning, even though that block may move at any time.

See Also: MemGetInfo(), MemModifyFlags().

MemDowngradeExclLock()

void	MemDowngradeExclLock(
        MemHandle		mh);			/* handle of affected block */

An application that has an exclusive lock on a block may downgrade it to a shared lock with this routine. It does not otherwise affect the block.

Include: heap.h

MemFree()

void	MemFree(
        MemHandle		mh);			/* handle of block to be freed */

This routine frees a global memory block. The block can be locked or unlocked.

Include: heap.h

Warnings: The routine does not care whether other threads have locked the block. If you try to free a bad handle, routine may fatal-error.

MemGetInfo()

word	MemGetInfo( /* return value depends on flag passed */
        MemHandle		mh, 			/* Handle of block to get info about */
        MemGetInfoType		info);			/* Type of information to get */

MemGetInfo() is a general-purpose routine for getting information about a global memory block. It gets the information by looking in the block's handle table entry; it does not need to access the actual block. It returns a single word of data; the meaning of that word depends on the value of the MemGetInfoType variable passed. The following types are available:

MGIT_SIZE
Return value is size of the block in bytes. This may be larger than the size originally requested, as blocks are allocated along paragraph boundaries.
MGIT_FLAGS_AND_LOCK_COUNT
The upper byte of the return value is the current HeapFlags record for the block. The lower byte is the number of locks currently on the block.
MGIT_OWNER_OR_VM_FILE_HANDLE
If the block is part of a VM file, return value is the VM file handle. Otherwise, return value is the GeodeHandle of the owning thread.
MGIT_ADDRESS
Return value is block's segment address on the global heap, or zero if block has been discarded. If block is unlocked and moveable, address may change without warning. Ordinarily, MemDeref() is preferable.
MGIT_OTHER_INFO
Returns the value of the HM_otherInfo word. This word is used in different ways for different types of handles.
MGIT_EXEC_THREAD
Returns the ThreadHandle of the thread executing this block, if any.

Include: heap.h

Warnings: If the handle is not a global memory block handle, results are unpredictable (the routine will read inappropriate data from the handle table entry).

See Also: MemDeref(),
MemModifyFlags(),
MemModifyOtherInfo(),
HandleModifyOwner().

MemIncRefCount()

void	MemIncRefCount(
        MemHandle		mh);			/* handle of affected block */

This routine increments the reference count of a global memory block (the reference count is stored in HM _otherInfo ).

Warnings: This routine assumes that a reference count is stored in HM _otherInfo . You may only use this routine if the block has had a reference count set up with MemInitRefCount() .

Include: heap.h

MemInitRefCount()

void	MemInitRefCount(
        MemHandle		mh,				/* handle of affected block */
        word		count);				/* initial reference count */

This routine sets up a reference count for the specified global memory block. The passed count is stored in the HM _otherInfo field of the block's handle-table entry.

Warnings: This routine overwrites the HM _otherInfo field. Since the semaphore routines ( HandleP() and HandleV() and the routines which use them) use this field, you cannot use both the semaphore routines and the reference count routines on the same block.

Include: heap.h

MemLock()

void	* MemLock(
        MemHandle		mh);			/* Handle of block to lock */

This routine locks a global memory block on the global heap. If the block is swapped, the memory manager swaps it back into the global heap; it then increments the lock count (up to a maximum of 255). The block will not be moved, swapped, or discarded until the lock count reaches zero. This routine returns a pointer to the start of the block, or a null pointer if block has been discarded. To get the address of a block without locking it, use MemDeref() .

Include: heap.h

Warnings: If you try to lock a bad handle, the routine may fatal-error. This routine does not check for synchronization problems; if the block is used by several threads, you should use the synchronization routines.

Never Use Situations:
Never lock a fixed block. Never use MemLock() to lock a block containing objects. Instead use ObjLockObjBlock() and then use MemUnlock() to unlock the block.

See Also: MemDeref().

MemLockExcl()

void	* MemLockExcl(
        MemHandle		mh);			/* Handle of block to grab */

If several different threads will be accessing the same global memory block, they should use data-access synchronization routines. MemLockExcl() belongs to one such set of routines. Often, several threads will need access to the same block; however, most of the time, they will not need to change the block. There is no synchronization problem if several threads read the same block at once, as long as none of them alters the block (by resizing it, writing to it, etc.) However, if a thread needs to change a block, no other thread should have access at that time.

The routines MemLockExcl() , MemLockShared() , MemUnlockShared() , and MemUnlockExcl() take advantage of this situation. They maintain a queue of threads requesting access to a block. When the block is not being used, they awaken the highest priority thread on the queue. If that thread requested exclusive access, the other threads sleep until it relinquishes access (via MemUnlockExcl() ). If it requested shared access, the routines awaken every other thread which requested shared access; the other threads on the queue will sleep until every active thread relinquishes access (via MemUnlockShared() ).

MemLockExcl() requests exclusive access to the block. If the block is not being accessed, the routine will grab exclusive access for the block, lock the block, and return the block's address. If the block is being accessed, the routine will sleep on the queue until it can get access; it will then awaken, lock the block, and return its address.

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all routines accessing the block get access with MemLockExcl() or MemLockShared() . The routines use the block's HM_otherInfo word; you must not alter it. When you are done accessing the block, make sure to relinquish access by calling MemUnlockExcl() .

Warnings: If a thread calls MemLockExcl() when it already has shared or exclusive access, it will deadlock; it will sleep until access is relinquished, but it cannot relinquish access while it is sleeping. If you try to grab a block which is owned by a different geode and is non-sharable, the routine will fatal-error.

Never Use Situations:
Never use MemLockExcl() or MemLockShared() on a fixed block. It will attempt to lock the block, and fixed blocks cannot be locked. Instead, use the HandleP() and HandleV() routines.

See Also: MemLockShared(),
MemUnlockExcl(),
MemUnlockShared().

MemLockFixedOrMovable()

void	* MemLockFixedOrMovable(
        void	* ptr);		/* virtual segment */

Given a virtual segment, this routine locks it (if it was movable). A virtual segment is an opaque pointer to a block that an application views as locked or fixed--the memory manager can actually swap locked or fixed blocks and will designate them as virtual segments.

Include: heap.h

MemLockShared()

void	* MemLockShared(
        MemHandle		mh);			/* Handle of block to grab */

MemLockShared() requests shared access to the passed block. If the block is not being accessed, or if it is held for shared access and the queue is empty, the routine gets access, locks the block, and returns the block's address. Otherwise it sleeps on the queue until the shared requests are awakened; it then locks the block and returns the block's address.

Include: heap.h

Be Sure To: Make sure that all routines accessing the block get access with MemLockExcl() or MemLockShared() . The routines use the block's HM _otherInfo word; you must not alter it. When you are done accessing the block, make sure to relinquish access by calling MemUnlockExcl() .

Warnings: If a thread calls MemLockShared() when it already has exclusive access, it will deadlock; it will sleep until access is relinquished, but it cannot relinquish access while it is sleeping. The thread must be careful not to take actions which could change the block, such as resizing it or writing to it. The routine will not enforce this. If you try to grab a block which is owned by a different geode and is non-sharable, the routine will fatal-error.

Never Use Situations:
Never use MemLockExcl() or MemLockShared() on a fixed block. It will attempt to lock the block, and fixed blocks cannot be locked. Instead, use the HandleP() and HandleV() routines.

See Also: MemLockExcl(),
MemUnlockExcl(),
MemUnlockShared().

MemModifyFlags()

void	MemModifyFlags(
        MemHandle		mh,					/* Handle of block to modify */
        HeapFlags		bitsToSet,					/* HeapFlags to turn on */
        HeapFlags		bitsToClear);					/* HeapFlags to turn off */

MemModifyFlags() changes the HeapFlags record of the global memory block specified. Not all flags can be changed after the block is created; only the flags HF_SHARABLE, HF_DISCARDABLE, HF_SWAPABLE, and HF_LMEM can be changed.

The routine uses the handle table entry of the block specified; it does not need to look at the actual block. The routine performs normally whether or not the block is locked, fixed, or discarded.

Include: heap.h

Warnings: If the handle is not a global memory handle, results are unpredictable; the routine will change inappropriate bits of the handle table entry.

See Also: MemGetInfo(),
HandleModifyOwner(),
MemModifyOtherInfo().

MemModifyOtherInfo()

void	MemModifyOtherInfo(
        MemHandle		mh,				/* Handle of block to modify */
        word		otherInfo);				/* New value of HM_otherInfo word */

Use this routine to change the value of the global memory block's HM _otherInfo word. Some blocks need this word left alone; for example, data-access synchronization routines use this word. Object blocks use this space to store the handle of the burden thread.

Include: heap.h

See Also: MemGetInfo(),
MemModifyFlags(),
HandleModifyOwner().

MemOwner()

GeodeHandle MemOwner(
        MemHandle		mh);			/* handle of block queried */

This routine returns the owning geode of the passed block. If the block belongs to a VM file, the owner of the VM file will be returned (unlike MemGetInfo() , which returns the VM file handle).

Include: heap.h

MemPLock()

void	* MemPLock(
        MemHandle		mh);		/* Handle of block to lock */

If several different threads will be accessing the same global memory block, they need to make sure their activities will not conflict. The way they do that is to use synchronization routines to get control of a block. MemPLock() is part of one set of synchronization routines.

If the threads are using the MemPLock() family, then whenever a thread needs access to the block in question, it can call MemPLock() . This routine calls HandleP() to get control of the block; it then locks the block and returns its address. If the block has been discarded, it grabs the block and returns a null pointer; you can then reallocate the block. When the thread is done with the block, it should release it with MemUnlockV() .

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all threads accessing the block use MemPLock() and/or HandleP() to grab the block. These routines use the HM _otherInfo field of the block's handle table entry; do not alter this field. Release the block with HandleV() or MemUnlockV() when you are done with it.

Warnings: If a thread calls MemPLock() when it already has control of the block, it will deadlock. MemThreadGrab() avoids this conflict. If you try to grab a non-sharable block owned by another thread, MemPLock() will fatal-error.

Never Use Situations:
Never use MemPLock() with a fixed block. It will try to lock the block, and fixed blocks cannot be locked. Instead, use HandleP() .

See Also: HandleP(),
MemUnlockV(),
HandleV().

MemPtrToHandle()

MemHandle MemPtrToHandle(
        void	* ptr);		/* pointer to locked block */

This routine returns the global handle of the locked block.

Include: heap.h

MemReAlloc()

MemHandle 	MemReAlloc(
        MemHandle		mh,					/* Handle of block */
        word		byteSize,					/* New size of the block */
        HeapAllocFlags		heapAllocFlags);					/* How to reallocate block */

This routine reallocates a global memory block. It can be used to resize a block; it can also be used to reallocate memory for a discarded block. Locked and fixed blocks can be reallocated; however, they may move on the global heap, so all pointers within the block must be adjusted. Note, however, that if the new size is smaller than the old size, the block is guaranteed not to move. The reallocated block may be larger than the size requested, as the block size is rounded up to the next even paragraph (one paragraph equals sixteen bytes). If you request that the block be expanded, but this is impossible, then the routine will return NullHandle.

The routine is passed a record of HeapAllocFlags . Only the flags HAF_ZERO_INIT, HAF_LOCK, and HAF_NO_ERR may be passed.

If necessary, the routine will automatically compact the heap.

Include: heap.h

Warnings: If HAF_LOCK is passed, the lock count will be incremented even if the block is already locked by this thread. The routine does not care whether the block has been locked by another thread (possibly belonging to another geode); thus, if the block is being used by more than one thread, it is important to use the synchronization routines.

See Also: MemAlloc(), MemDeref().

MemThreadGrab()

void	* MemThreadGrab(
        MemHandle		mh);			/* Handle of block to grab */

MemThreadGrab() is used in conjunction with MemThreadGrabNB() and MemThreadRelease() to maintain data-access synchronization. If several threads will all have access to the same global memory block, they should use data-acess synchronization routines to make sure that their activities do not conflict. If a thread uses MemThreadGrab() and no other thread has grabbed the block in question, the routine will increment the "grab count," lock the block, and return its address. It can do this even if the calling thread has already grabbed the block. If another thread has grabbed the block, MemThreadGrab() will put the calling thread in a queue to get the block; the thread will sleep until it gets the block, then MemThreadGrab() will grab the block, lock it, and return its address.

If the block has been discarded, MemThreadGrab() grabs the block and returns a null pointer; you can then reallocate memory for the block.

Include: heap.h

Be Sure To: Make sure that all threads using the block use the MemThread...() routines to access it (not other data-acess synchronization routines). Do not change the HM _otherInfo word of the block's handle table entry (the routines use that word as a semaphore).

Warnings: If you try to grab a block which is owned by a different geode and is non-sharable, the routine will fatal-error.

Never Use Situations:
Never use MemThreadGrab() with a fixed block. It will try to lock the block, and fixed blocks cannot be locked. If you need data-access synchronization for a fixed block, use the HandleP() and HandleV() routines.

See Also: MemThreadGrabNB(), MemThreadRelease().

MemThreadGrabNB()

void	* MemThreadGrabNB(
        MemHandle		mh); /* handle of block to grab */

This is a data-synchronization routine to be used in conjunction with MemThreadGrab() and MemThreadRelease() . It is exactly the same as MemThreadGrab() , except that if it cannot grab the global memory block because another thread has it, the routine returns an error instead of blocking.

If successful, MemThreadGrabNB() returns a pointer to the block. If the block has been discarded, it grabs the block and returns a null pointer; you can then reallocate memory for the block. If the block has been grabbed by another thread, MemThreadGrab() returns the constant BLOCK_GRABBED.

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all threads using the block use the MemThread...() routines to access the block (not other data-access synchronization routines). Do not change the HM _otherInfo word of the block's handle table entry (the routines use that word as a semaphore).

Warnings: If you try to grab a block that is owned by a different geode and is non-sharable, the routine will fatal-error.

Never Use Situations:
Never use MemThreadGrabNB() with a fixed block. It will try to lock the block, and fixed blocks cannot be locked. If you need synchronization for a fixed block, use the HandleP() and HandleV() routines.

See Also: MemThreadGrab(), MemThreadRelease().

MemThreadRelease()

void	MemThreadRelease(
        MemHandle		mh); /* handle of locked block to release */

Use this routine to release a global memory block which you have grabbed with MemThreadGrab() or MemThreadGrabNB() . The routine decrements the grab count; if the grab count reaches zero, the routine unlocks the block.

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all threads using the block use the MemThread...() routines to access the block (not other data-access synchronization routines). Do not change the HM _otherInfo word of the block's handle table entry (the routines use that word as a semaphore). Make sure to release the block once for every time you grab it; the block is not unlocked until each of your grabs is released.

Warnings: If you try to release a block that you have not successfully grabbed, the routine will fatal-error.

See Also: MemThreadGrab(), MemThreadGrabNB().

MemUnlock()

void	MemUnlock(
        MemHandle		mh);		/* Handle of block to unlock */

This routine decrements the lock count of the indicated block. If the lock count reaches zero, the block becomes unlocked (it can be moved, swapped, or discarded). Do not try to unlock a block that has not been locked.

Include: heap.h

MemUnlockExcl()

void	MemUnlockExcl(
        memHandle		mh);			/* Handle of block to release */

If a thread has gained access to a block with MemLockExcl() , it should release the block as soon as it can. Until it does, no other thread can access the block for either shared or exclusive access. It can release the block by calling MemUnlockExcl() . This routine unlocks the block and releases the thread's access to it. If there is a queue for this block, the highest-priority thread waiting will be awakened, as described in MemLockExcl() .

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all routines accessing the block get access with MemLockExcl() or MemLockShared() . The routines use the block's HM _otherInfo word; you must not alter it. Call this routine while the block is still locked; it will call MemUnlock() to unlock the block.

Warnings: If you call this routine on a block which you have not gained access to, it may fatal-error.

See Also: MemLockExcl(),
MemLockShared(),
MemUnlockShared().

MemUnlockFixedOrMovable()

void	MemUnlockFixedOrMovable(
        void	* ptr);		/* virtual segment */

This routine unlocks a previously locked, movable virtual segment. Do not call this routine with normal locked or fixed blocks; only call it for those blocks locked with MemLockFixedOrMovable() .

Include: heap.h

MemUnlockShared()

void	MemUnlockShared(
        MemHandle		mh);		/* Handle of block to release */

If a thread has gained access to a block with MemLockShared() , it should release the block as soon as it can. Until it does, no thread can be awakened from the queue. It can release the block by calling MemUnlockShared() . This routine calls MemUnlock() , decrementing the block's lock count; it then releases the thread's access to it. If no other thread is accessing the block and there is a queue for this block, the highest-priority thread waiting will be awakened, as described in MemLockExcl() .

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all routines accessing the block get access with MemLockExcl() or MemLockShared() . These routines use the block's HM _otherInfo word; you must not alter it. Call this routine while the block is still locked; it will call MemUnlock() to unlock the block.

Warnings: If you call this routine on a block which you have not gained access to, it may fatal-error.

See Also: MemLockExcl(),
MemLockShared(),
MemUnlockExcl().

MemUnlockV()

void	MemUnlockV(
        MemHandle		mh);			/* Handle of block to release */

This routine unlocks a block with MemUnlock() , then releases its semaphore with HandleV() . Do not use this routine unless the block's semaphore was grabbed and the block locked (typically with the MemPLock() routine).

Include: heap.h

Tips and Tricks: You can find out if the block is being accessed by looking at the HM _otherInfo word (with MemGetInfo() ). If HM _otherInfo equals one, the block is not grabbed; if it equals zero, it is grabbed, but no threads are queued; otherwise, it equals the handle of the first thread queued.

Be Sure To: Make sure that all threads accessing the block use HandleP() or MemPLock() to access the thread. These routines use the HM _otherInfo field of the handle table entry; do not alter this field.

Warnings: Do not use this on a block unless you have grabbed it. The routine does not check to see that you have grabbed the thread; it just clears the semaphore and returns.

Never Use Situations:
Never use this routine to release a fixed block. It will try to unlock the block; fixed blocks cannot be locked or unlocked. Instead, call HandleV() directly.

See Also: MemPLock(),
HandleP(),
HandleV().

MemUpgradeSharedLock()

void	* MemUpgradeSharedLock(
        MemHandle		mh);			/* handle of locked block */

This routine upgrades a shared lock on the block to an exclusive lock, as if the caller had used MemLockExcl(). If other threads have access to the block, the caller will sleep in the access queue until it can gain exclusive access.

This routine returns the pointer of the locked block because, if the caller sleeps in the queue, the memory block could move between the call and the granting of access.

Include: heap.h

See Also: MemLockExcl(),
MemLockShared(),
MemDowngradeExclLock().

MessageSetDestination()

void	MessageSetDestination(
        EventHandle		event,			/* handle of the event to be modified */
        optr		dest);			/* new destination for the event */

This routine sets the destination of an event to the optr passed.

Include: object.h

ModemAnswerCall()

Boolean	ModemAnswerCall(
        SerialPortNum		port,			/* serial port number */
        ModemResultCode		*result);

Answers an incoming data call. Returns [???] if a connection has been established (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

See Also: ModemAutoAnswer().

ModemAutoAnswer()

Boolean	ModemAutoAnswer(
        SerialPortNum		port,		/* serial port number */
        word		number_of_rings,
        ModemResultCode		*result);

Sets modem to answer calls automatically. Does not wait for a call but returns [???] after successfully setting modem to auto-answer (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

Warnings: Modem must be in command mode when calling this routine.

See Also: ModemAnswerCall().

ModemClose()

void	ModemClose(
        SerialPortNum		port,			/* serial port number */
        StreamLingerMode		close_mode); /* manner in which to close stream */

Closes the serial port being used for a modem connection. Possible values for close_mode include STREAM_DISCARD or STREAM_LINGER.

Include: modemC.goh

Warnings: Use ModemHangup() to hang up an existing call.

See Also: ModemHangup().

ModemDial()

Boolean	ModemDial(
        SerialPortNum		port,			/* serial port number */
        word		dial_string_length,
        const char		*dial_string,	/* phone number to dial */
        ModemResultCode		*result);

Sends the modem's dial command to dial the specified number. Returns [???] if a connection has been established (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

See Also: ModemOpen().

ModemFactoryReset()

Boolean	ModemFactoryReset(
        SerialPortNum		port,			/* serial port number */
        ModemResultCode		*result);

Restores factory settings of modem. Returns [???] after successfully resetting (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

Warnings: Modem must be in command mode when calling this routine.

ModemHangup()

Boolean	ModemHangup(
        SerialPortNum		port,			/* serial port number */
        ModemResultCode		*result);

Hangs up an existing data call. Returns [???] after successfully hanging up (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

See Also: ModemClose().

ModemInitModem()

Boolean	ModemInitModem(
        SerialPortNum		port,			/* serial port number */
        word		init_string_length,
        const char		*init_string,	/* initialization string */
        ModemResultCode		*result);

Initializes modem with modem command string. A lead "AT" need not be supplied. Returns [???] after successfully initializing (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

Warnings: Modem must be in command mode when calling this routine.

ModemOpen()

Boolean	ModemOpen(
        GeodeHandle		drHandle,			/* handle of serial driver */
        SerialPortNum		port,			/* serial port number */
        StreamOpenFlags		flags,			/* manner in which to open stream */
        word		input_buffer_size,
        word		output_buffer_size,
        word		timeout);			/* timeout if SOF_TIMEOUT */

Opens the serial port to be used for a modem connection and places modem in command mode. Possible values for flags are SOF_TIMEOUT and SOF_NOBLOCK.

Include: modemC.goh

See Also: ModemDial().

ModemReset()

Boolean	ModemReset(
        SerialPortNum		port,			/* serial port number */
        ModemResultCode		*result);

Switches to command mode and resets the modem. Returns [???] after successfully resetting (and writes MRC_OK to result) or non-zero if an error has occurred (and [???]).

Include: modemC.goh

See Also: ModemFactoryReset().

ModemSetMessageDataNotify()

void	ModemSetMessageDataNotify(
        SerialPortNum		port,		/* serial port number */
        Message		msg,		/* msg to be sent upon notification */
        optr		destination);	/* object to receive msg */

Registers a message to be sent when when data is received from the modem during an active call. As with the serial driver's data notification, further notification will not be sent until the serial port has been read. [MEANING UNTIL THE STREAM HAS BEEN READ???]

Be Sure To: Use the prototype for the message you are registering.

@prototype void MODEM_DATA_NOTIFICATION();

Include: modemC.goh

See Also: ModemSetRoutineDataNotify().

ModemSetMessageEndCallNotify()

void	ModemSetMessageEndCallNotify(
        SerialPortNum		port,		/* serial port number */
        Message		msg,		/* msg to be sent upon notification */
        optr		destination);	/* object to receive msg */

Registers a message to be sent when a call has ended for any reason. To "un-register," pass NullOptr in destination.

Be Sure To: Use the prototype for the message you are registering.

@prototype void MODEM_END_CALL_NOTIFICATION();

Warnings: All platforms may not support this feature.

Include: modemC.goh

ModemSetMessageResponseNotify()

void	ModemSetMessageResponseNotify(
        SerialPortNum		port,		/* serial port number */
        Message		msg,		/* msg to be sent upon notification */
        optr		destination);	/* object to receive msg */

Registers a message to be sent when when a response is received from the modem (while the modem is in command mode). Response is passed in a memory block which the handler should free.

Be Sure To: Use the prototype for the message you are registering.

@prototype void MODEM_RESPONSE_NOTIFICATION();

Include: modemC.goh

See Also: ModemSetRoutineResponseNotify().

ModemSetRoutineDataNotify()

void	ModemSetRoutineDataNotify(
        SerialPortNum		port,		/* serial port number */
        word		data,		/* data passed to callback */
        void _pascal (*NotifyRoutine) (word data));

Registers a routine to be called when data is received from the modem during an active call. As with the serial driver's data notification, further notification will not be sent until the serial port has been read. [MEANING UNTIL THE STREAM HAS BEEN READ???]

Include: modemC.goh

See Also: ModemSetMessageDataNotify().

ModemSetRoutineResponseNotify()

void	ModemSetRoutineResponseNotify(
        SerialPortNum		port,		/* serial port number */
        word		data,		/* data passed to callback */
        void _pascal (*NotifyRoutine) (
                word		data,
                word		responseSize,
                char		*response));

Registers a routine to be called when a response is received from the modem (while modem is in command mode).

Include: modemC.goh

See Also: ModemSetMessageResponseNotify().

NameArrayAdd()

word	NameArrayAdd(
        optr 		arr,				/* optr of name array */
        const char		* nameToAdd,				/* Name of new element */
        word		nameLength,				/* Length of name; pass zero if
                         * name string is null-terminated */
        NameArrayAddFlags		flags,				/* Described below */
        const	void	* data);				/* Copy this data to new element */

This routine creates a new element in a name array, copying the passed name and data into the new element. If no element with the passed name exists, NameArrayAdd() will create the element and return its token. If an element with the same name already exists, the existing element's reference count will be incremented and its token will be returned. The routine takes the following arguments:

array
The optr of the name array.
nameToAdd
The name of the new element. This string may contain nulls.
nameLength
The length of the name string, in bytes. If you pass zero, NameArrayAdd() will assume the string is null-terminated.
flags
A record of NameArrayAddFlags , described below.
data
The data to copy into the new element.

Structures: The argument is passed a set of NameArrayAddFlags . Only one flag is currently defined:

NAAF_SET_DATA_ON_REPLACE
If an element with the specified name exists and this flag is set, the data passed will be copied into the data area of the existing element. If this flag is not set, the existing element will not be changed.

Warnings: This routine may resize the name array; therefore, all pointers to the LMem heap are invalidated.

Include: chunkarr.h

NameArrayAddHandles()

dword	NameArrayAddHandles(
        MemHandle 		mh,				/* Handle of LMem heap */
        ChunkHandle 		arr,				/* Chunk handle of name array */
        const char * 		nameToAdd,				/* Name of new element */
        word		nameLength,				/* Length of name; pass zero if
                         * name string is null-terminated */
        NameArrayAddFlags		flags,				/* Described below */
        const	void *	data);				/* Copy this data to new element */

This routine is exactly like NameArrayAdd() above, except that the name array is specified by its global and chunk handles (instead of with an optr).

Warnings: This routine may resize the name array; therefore, all pointers to within the LMem heap are invalidated.

Include: chunkarr.h

NameArrayChangeName()

void	NameArrayChangeName(
        optr 		array,			/* optr of name array */
        word		token,			/* Token of element to be changed */
        const char * 		newName,			/* New name for element */
        word		nameLength);				/* Length of name in bytes; pass
                         * zero if name string is
                         * null-terminated */

This routine changes the name of an element in a name array.

Warnings: If the new name is longer than the old, the chunk will be resized, invalidating all pointers to within the LMem heap.

Include: chunkarr.h

NameArrayChangeNameHandles()

dword	NameArrayChangeNameHandles(
        MemHandle 		mh,				/* Handle of LMem heap */
        ChunkHandle 		array,				/* Chunk handle of name array */
        word		token,				/* Token of element to be changed */
        const char * 		newName,				/* New name for element */
        word		nameLength);				/* Length of name in bytes; pass
                         * zero if name string is
                         * null-terminated */

This routine is exactly like NameArrayChangeName() above, except that the name array is specified by its global and chunk handles (instead of with an optr).

Warnings: If the new name is longer than the old, the chunk will be resized, invalidating all pointers to within the LMem heap.

Include: chunkarr.h

NameArrayCreate()

ChunkHandle 	NameArrayCreate(
        MemHandle 		mh,				/* Global handle of LMem heap */
        word		dataSize,				/* Size of data section for
                         * each element */
        word		headerSize);				/* Size of header; pass
                         * zero for default header */

This routine creates a name array in the indicated LMem heap. It creates a NameArrayHeader structure at the head of a new chunk. If you want to leave extra space before the start of the array, you can pass a larger header size; if you want to use the standard header, pass a header size of zero.

You must specify the size of the data portion of each element when you create the array. The name portion will be variable sized.

Include: chunkarr.h

Tips and Tricks: If you want extra space after the NameArrayHeader , you may want to create your own header structure, the first element of which is a NameArrayHeader . You can pass the size of this header to NameArrayCreate() and access the data in your header via the structure fields.

Be Sure To: Lock the block on the global heap before calling this routine (unless it is fixed). If you pass a header size, make sure it is larger than sizeof(NameArrayHeader) .

Include: chunkarr.h

NameArrayCreateAt()

ChunkHandle 	NameArrayCreateAt(
        optr	array,	/* Turn this chunk into a name array */
        word	dataSize,	/* Size of data section of each element */
        word	headerSize); 	/* Size of header; pass zero for default header */

This routine is just like NameArrayCreate() above, except that the element array is created in a pre-existing chunk. The contents of that chunk will be destroyed.

Include: chunkarr.h

Warnings: If the chunk isn't large enough, it will be resized. This will invalidate all pointers to chunks in that block.

Include: chunkarr.h

NameArrayCreateAtHandles()

ChunkHandle 	NameArrayCreateAtHandles(
        MemHandle 		mh,				/* Global handle of LMem heap */
        ChunkHandle 		chunk,				/* the chunk for the array */
        word		dataSize,				/* Size of data for each element */
        word		headerSize);				/* Size of header; pass
                         * zero for default header */

This routine is exactly like NameArrayCreateAt() above, except that the name array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

Warnings: If the chunk isn't large enough, it will be resized. This will invalidate all pointers to chunks in that block.

Include: chunkarr.h

NameArrayFind()

word	NameArrayFind(
        optr 		array,				/* optr to name array */
        const char		* nameToFind,				/* Find element with this name */
        word		nameLength,				/* Pass zero if name string is
                         * null-terminated */
        void *		returnData);				/* Copy data section into this
                         * buffer */

This routine locates the element with the specified name. It returns the token of the element and copies its data section into the passed buffer. If there is no element with the specified name, the routine will return CA_NULL_ELEMENT. The routine takes the following arguments:

array
The optr of the name array.
nameToAdd
The name of the element to find. This string may contain nulls.
nameLength
The length of the name string, in bytes. If you pass zero, NameArrayFind() will assume the string is null-terminated.
returnData
The data section of the element is written to this buffer.

Include: chunkarr.h

Warnings: You must make sure the returnData buffer is large enough to hold an element's data portion; otherwise, data after the buffer will be overwritten.

NameArrayFindHandles()

word	NameArrayFindHandles(
        MemHandle		mh,				/* Handle of LMem heap */
        ChunkHandle 		array,				/* Handle of name array */
        const char *		nameToFind,				/* Find element with this name */
        word		nameLength,				/* Pass zero if name string is
                         * null-terminated */
        void *		returnData);				/* Copy data section into this
                         * buffer */

This routine is exactly like NameArrayFind() above, except that the name array is specified by its global and chunk handles (instead of with an optr).

Include: chunkarr.h

NEC()

NEC(line)

This macro defines a line of code that will only be compiled into the non -error-checking version of the geode. The line parameter of the macro is the actual line of code. When the non-EC version of the program is compiled, the line will be treated as a normal line of code; when the EC version is compiled, the line will be ignored.

See Also: EC().

Include: ec.h

ObjBlockGetOutput()

optr	ObjBlockGetOutput(
        MemHandle mh);			/* handle of the subject object block */

This routine returns the optr of the output destination object set for the specified object block. The output object is stored in the object block's header in the OLMBH_output field. If the block has no output set, NullOptr will be returned.

Include: object.h

See Also: ObjLMemBlockHeader.

ObjBlockSetOutput()

void	ObjBlockSetOutput(
        MemHandle		mh,			/* handle of the subject object block */
        optr		o);			/* optr of the new output object */

This routine sets the OLMBH_output field in the specified object block's header. The optr passed in o will be set as the block's output.

Include: object.h

See Also: ObjLMemBlockHeader.

ObjCompAddChild()

void	ObjCompAddChild(
        optr	obj,		/* optr of parent composite */
        optr	objToAdd,		/* optr of new child */
        word	flags,		/* CompChildFlags */
        word	masterOffset,		/* offset to master part */
        word	compOffset,		/* offset to comp field in master part */
        word	linkOffset);		/* offset to link field in master part */

This routine adds the given object to an object tree as the child of another specified object. It returns nothing. You will not likely want to use this routine; instead, you will probably use the messages listed below under "See Also." The parameters of this routine are

obj
The optr of the parent composite object. The parent must be a composite; if it is not, an error will result.
objToAdd
The optr of the child object. The child must have a link instance field (defined with @link ).
flags
A record of CompChildFlags . These flags indicate whether the object should initially be marked dirty as well as where in the parent's child list the new child should be placed (first, second, last, etc.).
masterOffset
The offset within the parent's instance chunk to the master group's offset. (The value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure.)
compOffset
The offset within the parent's instance chunk to the composite field.
linkOffset
The offset within the parent's instance chunk to the link field.

Warnings: This routine may resize and move LMem and Object blocks on the heap, thereby invalidating all segment addresses and pointers.

Include: metaC.goh

See Also: MSG_VIS_ADD_CHILD, MSG_GEN_ADD_CHILD.

ObjCompFindChildByNumber()

optr	ObjCompFindChildByNumber(
        optr	obj,		/* parent's optr */
        word	childToFind,		/* zero-based child number */
        word	masterOffset,		/* offset to master part */
        word	compOffset,		/* offset to comp field in master part */
        word	linkOffset);		/* offset to link field in master part */

This routine returns the optr of the passed object's child; the child is specified based on its position in the object's child list. You will not often use this routine, but you will probably use the messages listed under "See Also" instead. The routine's parameters are listed below:

obj
The optr of the parent object.
childToFind
The zero-based number of the child to be found. For example, to return the first child's optr, pass zero or CCO_FIRST; to return the last child's optr, pass CCO_LAST.
masterOffset
The offset within the parent's instance chunk to the master group's offset. (The value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure.)
compOffset
The offset within the parent's instance chunk to the composite field.
linkOffset
The offset within the parent's instance chunk to the link field.

Include: metaC.goh

See Also: MSG_GEN_FIND_CHILD, MSG_VIS_FIND_CHILD.

ObjCompFindChildByOptr()

word	ObjCompFindChildByOptr(
        optr	obj,		/* parent's optr */
        optr	childToFind,		/* optr of child to find */
        word	masterOffset,		/* offset to master part */
        word	compOffset,		/* offset to comp field in master part */
        word	linkOffset);		/* offset to link field in master part */

This routine returns the zero-based child number of an object given its optr and its parent's optr. The returned number represents the child's position in its parent's child list. For example, a return value of zero indicates the object is the parent's first child. You will not likely use this routine; instead, you will probably use the messages shown below under "See Also."

The parameters for this routine are listed below:

obj
The optr of the parent object.
childToFind
The optr of the child whose number is to be returned. If the child is not found, the routine will return -1.
masterOffset
The offset within the parent's instance chunk to the master group's offset. (The value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure.)
compOffset
The offset within the parent's instance chunk to the composite field.
linkOffset
The offset within the parent's instance chunk to the link field.

Include: metaC.goh

See Also: MSG_GEN_FIND_CHILD, MSG_VIS_FIND_CHILD.

ObjCompMoveChild()

void	ObjCompMoveChild(
        optr	obj,		/* parent's optr */
        optr	objToMove,		/* optr of child to move */
        word	flags,		/* CompChildFlags */
        word	masterOffset,		/* offset to master part */
        word	compOffset,		/* offset to comp field in master part */
        word	linkOffset);		/* offset to link field in master part */

This routine moves the specified child within its parent's child list. This routine will not move a child from one parent to another, but it can reorganize a parent's children. You will not likely use this routine, but you may often use the messages listed under "See Also" below.

The parameters of this routine are shown below:

obj
The optr of the parent object.
objToMove
The optr of the child to be moved. If the optr does not point to a valid child, behavior is undefined and an error is likely.
flags
A record of CompChildFlags indicating the new position of the child and whether the link should be marked dirty.
masterOffset
The offset within the parent's instance chunk to the master group's offset. (The value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure.)
compOffset
The offset within the parent's instance chunk to the composite field.
linkOffset
The offset within the parent's instance chunk to the link field.

Warnings: This routine may cause LMem and/or Object Blocks to move or to shuffle their chunks, thereby invalidating any segment addresses or pointers.

Include: metaC.goh

See Also: MSG_GEN_MOVE_CHILD, MSG_VIS_MOVE_CHILD.

ObjCompProcessChildren()

Boolean	ObjCompProcessChildren(
        optr		obj,				/* parent's optr */
        optr		firstChild,				/* optr of first child to process */
        ObjCompCallType		stdCallback,				/* standard callback type */
        void 		* cbData,				/* data passed to callback */
        word		masterOffset,				/* offset to master part */
        word		compOffset,				/* offset to comp field */
        word		linkOffset);				/* offset to link field */
        Boolean _pascal (*callback) (optr parent, optr child, void *cbData));

This routine performs a specific set of actions on all or some of an object's children. It is very rare that you will use this routine; typically, you should send a message to all of the parent's children. If, however, you use this routine, you must also write a callback routine that will be executed once for each affected child.

ObjCompProcessChildren() returns true (nonzero) only if it was stopped before all children had been processed. The only two ways this could be returned is if an error occurs or if your callback returns true when called.

The parameters for this routine are

obj
The optr of the composite whose children are to be processed.
firstChild
The optr of the first child to be processed. This routine will begin with the passed child and continue with all subsequent children. Pass the optr of the composite's first child--retrieved with the routine ObjCompFindChildByNumber() --to process all children.
stdCallback
A value of ObjCompCallType indicating how the data in the buffer pointed to by cbData will be passed to your callback routine. These values are detailed below.
cbData
A pointer to a buffer in which data can be passed to your callback routine. This buffer can be altered by your callback.
masterOffset
The offset within the parent's instance chunk to the master group's offset. (The value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure.)
compOffset
The offset within the parent's instance chunk to the composite field.
linkOffset
The offset within the parent's instance chunk to the link field.
callback
A pointer to the actual callback routine that will be executed once for each child. The callback should be in your geode's fixed memory. The parameters and return values for the callback routine are given below.

The callback routine takes three parameters and returns a boolean value. It must be declared _pascal. The three parameters to the callback are listed below:

parent
The optr of the parent composite.
child
The optr of the current child being processed.
cbData
A pointer to the buffer passed by the original caller of ObjCompProcessChildren() . What is actually in this buffer may depend on the value in the original sdtCallback parameter; if the buffer is not saved and restored by ObjCompProcessChildren() between children, each child may receive data altered by the previous child.

The callback routine can access and alter the buffer pointed to by cbData , or it can query the child or do anything else with the exception of destroying the child. It should return a Boolean value: true if ObjCompProcessChildren() should be aborted, false if it should not.

The values you can pass to ObjCompProcessChildren() in stdCallback are of type ObjCompCallType . You can use one of the following values to specify how the buffer in cbData will be passed on to the next child's callback routine:

OCCT_SAVE_PARAMS_TEST_ABORT
Save the buffer passed in cbData before calling each child; abort the routine if the callback returns true .
OCCT_SAVE_PARAMS_DONT_TEST_ABORT
Save the buffer passed in cbData before calling each child; do not check the return value of the callback before proceeding to the next child.
OCCT_DONT_SAVE_PARAMS_TEST_ABORT
Do not save the buffer in cbData , and abort if the callback routine returns true .
OCCT_DONT_SAVE_PARAMS_DONT_TEST_ABORT
Do not save the buffer in cbData , and do not check the callback routine's return value.
OCCT_ABORT_AFTER_FIRST
Abort the processing after only one child (typically used to call the nth child).
OCCT_COUNT_CHILDREN
Counts the number of children rather than calling each.

Include: metaC.goh

See Also: @send, @call, MSG_META_SEND_CLASSED_EVENT.

ObjCompRemoveChild()

void	ObjCompRemoveChild(
        optr	obj,		/* parent's optr */
        optr	objToRemove		/* optr of child to be removed */
        word	flags,		/* CompChildFlags */
        word	masterOffset,		/* offset to master part */
        word	compOffset,		/* offset to comp field in master part */
        word	linkOffset);		/* offset to link field in master part */

This routine removes the given child from the specified parent composite. The child will be removed entirely from the object tree, but it will not be detached or freed. The parameters of this routine are listed below:

obj
The optr of the parent composite.
objToRemove
The optr of the child to be removed.
flags
A record of CompChildFlags indicating whether the parent and child should be marked dirty after the operation.
masterOffset
The offset within the parent's instance chunk to the master group's offset. (The value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure.)
compOffset
The offset within the parent's instance chunk to the composite field.
linkOffset
The offset within the parent's instance chunk to the link field.

Include: metaC.goh

ObjDecInteractibleCount()

void	ObjDecInteractibleCount(
        MemHandle mh);			/* subject object block */

This routine decrements the given object block's interactable count. Do not decrement the interactable count without first incrementing it with ObjIncInteractibleCount() . Visible objects automatically decrement the interactable count in their MSG_VIS_CLOSE handlers.

Include: object.h

See Also: ObjIncInteractibleCount(), MSG_VIS_CLOSE, ObjLMemBlockHeader.

ObjDecInUseCount()

void	ObjDecInUseCount(
        MemHandle mh);		/* subject object block */

This routine decrements the given object block's in-use count. When the in-use count reaches zero, the block may safely be freed. You should not decrement the in-use count of a block without first incrementing it at some point with ObjIncInUseCount() .

Warnings: Do not decrement the in-use count without incrementing it first. An error will result.

Include: object.h

See Also: ObjIncInUseCount(), ObjDecInteractibleCount(), ObjLMemBlockHeader.

ObjDeref()

void	* ObjDeref(
        optr	obj			/* optr to dereference */
        word	masterLevel);			/* specific master level to dereference */

This routine dereferences the given optr and master level to reset the message parameter pself . Because many routines and messages may cause the calling object's instance chunk to move, the pself parameter may become invalid. The two parameters to ObjDeref() are

obj
The optr of the object to be dereferenced; nearly always you will want to pass oself .
masterLevel
The master level of the part to be dereferenced. This is the offset into the instance chunk where the offset to the master part is stored. Since pself points to the first byte of a master part, you must specify which master part you are dereferencing.

For example, a visible object dereferencing its VisClass instance data would call this routine as follows:

pself = ObjDeref(oself, 4);

Note, however, the ObjDeref1() and ObjDerefVis() exist to dereference the Vis master part, and ObjDeref2() and ObjDerefGen() exist to dereference the Gen master part.

Include: object.h

See Also: ObjDeref1(), ObjDeref2().

ObjDerefHandles()

void	* ObjDerefHandles(
        MemHandle		mh,					/* handle portion of optr */
        ChunkHandle		ch,					/* chunk portion of optr */
        word		masterLevel);					/* master level to dereference */

This routine is exactly the same as ObjDeref() , above, except that the optr is specified as its separate handles.

Include: object.h

ObjDeref1()

void	* ObjDeref1(
        optr obj);			/* optr of object to be dereferenced */

This routine is a special version of ObjDeref() which dereferences the first master part of an object. Visible objects should use this routine or ObjDerefVis() instead of ObjDeref() .

Include: object.h

See Also: ObjDeref(), ObjDeref2().

ObjDeref1Handles()

void	*ObjDeref1Handles(
        MemHandle		mh,			/* handle portion of optr */
        ChunkHandle		ch,);			/* chunk handle portion of optr */

This routine is exactly like ObjDeref1() , above, except that the optr is specified as its separate handles.

Include: object.h

ObjDeref2()

void	* ObjDeref2(
        optr	obj);		/* optr of object to be dereferenced */

This routine is a specialized version of ObjDeref() which dereferences the second master part of an object. Generic objects should use this routine or ObjDerefGen() instead of ObjDeref() .

Include: object.h

See Also: ObjDeref(), ObjDeref1().

ObjDeref2Handles()

void	* ObjDeref2Handles(
        MemHandle		mh,/			/* handle portion of optr */
        ChunkHandle		ch);			/* chunk portion of optr */

This routine is exactly like ObjDeref2() , above, except that the optr is specified as its separate handles.

Include: object.h

ObjDerefGen()

void	* ObjDerefGen(
        optr	obj);			/* generic object to be dereferenced */

This routine is exactly the same as ObjDeref2() and dereferences the Gen master part of a generic object.

Include: object.h

See Also: ObjDeref(), ObjDeref2().

ObjDerefVis()

void	* ObjDerefVis(
        optr	obj);			/* visible object to be dereferenced */

This routine is exactly the same as ObjDeref1() and dereferences the Vis master part of a visible object or a visibly-realized generic object.

Include: object.h

See Also: ObjDeref(), ObjDeref1().

ObjDoRelocation()

Boolean	ObjDoRelocation( /* returns true if error */
        ObjRelocationType		type,				/* type of relocation */
        MemHandle		block,				/* handle of info block */
        void		* sourceData,				/* source of relocation */
        void		* destData);				/* relocated value */

This routine relocates a given word or dword argument and is used for resolving handles and pointers to movable objects. Most often, relocation and unrelocation occur when resources are loaded, swapped, or saved, and this is in most cases taken care of by the kernel.

ObjDoRelocation() takes four parameters:

type
The type of relocation to be performed ( RelocationType ). This can be one of the three values shown below.
block
The handle of the block containing the relocation.
sourceData
A pointer to the source of the relocation; this pointer should be cast to the proper type (word or dword) when calling the routine.
destData
A pointer to the value to be returned; this pointer should be cast appropriately when the routine is called. The exact type of return value depends on sourceData and type , above.

The type of relocation to be done affects the type of data passed in sourceData and destData . The relocation type is passed in the type parameter and must be one of the following enumerations of RelocationType :

RELOC_RELOC_HANDLE
The relocation will be from a resource ID to a handle. The sourceData pointer should be cast to type word, and the destData pointer should be cast to type Handle.
RELOC_RELOC_SEGMENT
The relocation will be from a resource ID to a segment address. The sourceData pointer should be cast to type word, and the destData pointer should be cast to type Segment.
RELOC_RELOC_ENTRY_POINT
The relocation will be from either a resource ID or an entry number to an entry point. Both the sourceData pointer and the destData pointer should be cast to type dword.

ObjDoRelocation() returns an error flag that will be true if an error occurs, false otherwise.

The relocation done by this routine can be undone with ObjDoUnRelocation() .

Include: object.h

ObjDoUnRelocation()

Boolean	ObjDoUnRelocation( /* returns true if error */
        ObjRelocationType		type,				/* type of relocation */
        MemHandle		block,				/* handle of info block */
        void		* sourceData,				/* source of relocation */
        void		* destData);				/* relocated value */

This routine unrelocates a given word or dword. It translates a handle, a segment address, or an entry point back into a resource ID. The translation done is the exact reverse of that done by ObjDoRelocation() . See that routine (above) for more information.

ObjDoUnRelocation() returns an error flag that will be true if an error occurs and false if the unrelocation is successful. The unrelocated resource ID will be returned pointed to by the destData pointer.

Include: object.h

See Also: ObjDoRelocation().

ObjDuplicateMessage()

EventHandle ObjDuplicateMessage(
        EventHandle msg);				/* event to duplicate */

This routine duplicates a prerecorded event, returning the event handle of the new event. Pass the handle of the event to be duplicated. You can then change information about the event with MessageSetDestination() .

Include: object.h

ObjDuplicateResource()

MemHandle ObjDuplicateResource(
        MemHandle		blockToDup,					/* handle of resource; must
                                 * not be loaded */
        GeodeHandle		owner,					/* owner of duplicate */
        ThreadHandle		burdenThread);					/* burden thread of duplicate */

This routine duplicates an entire object resource block. The new block will be put on the "saved blocks" list so it gets saved to the geode's state file. Usually this is used by the UI to make editable copies of an application's UI resources to ensure the proper state information gets saved. This routine takes three parameters:

blockToDup
The handle of the block to be duplicated. The block must not be resident in memory when ObjDuplicateResource() is called. Also, it can only be a "template" resource--a resource that does not get used by the UI or the application directly, but only gets copied via this routine.
owner
The owner geode of the new block. This should be the geode handle of the owning geode or zero to have the calling geode own it. If you pass an owner of -1, the new block will be owned by the same geode that owns the original.
burdenThread
The thread that will run the block and handle its messages. This should be a thread handle or zero to have the calling thread run the block. Passing a burdenThread of -1 makes the new resource have the same burden thread as the original.

ObjDuplicateResource() returns the handle of the newly created block, which will be unlocked and may or may not be resident in memory.

Include: object.h

See Also: ObjFreeDuplicate(), MSG_META_BLOCK_FREE, ObjLockObjBlock().

ObjEnableDetach()

void	ObjEnableDetach(
        optr	obj);		/* object calling this routine */

This routine acts as an object's handler for MSG_META_ACK . This handler decrements the acknowledgment count (incremented with ObjIncDetach() ) and, if the count is zero, enables the detach mechanism so the object can be fully detached. Because the detach mechanism is implemented in MetaClass , it is highly unlikely you will ever call this routine.

The lone parameter of this routine is the optr of the calling object (or, in the case of MSG_META_ACK , the object sending acknowledgment).

Warnings: This routine may resize and/or move chunks and object blocks, thereby invalidating all pointers and segment addresses.

Include: metaC.goh

See Also: MSG_META_DETACH, ObjInitDetach(), ObjIncDetach(), MSG_META_ACK.

ObjFreeChunk()

void	ObjFreeChunk(
        optr	o);		/* optr of chunk to be freed */

This routine frees the passed object's instance chunk. If the object came from a loaded resource, however, the object is resized to zero and marked dirty rather than actually freed.

Warnings: The object must be fully detached, and its message queues must be empty before it can safely be freed. All this is handled by MSG_META_DETACH and MSG_META_OBJ_FREE .

Include: object.h

See Also: MSG_META_DETACH, MSG_META_OBJ_FREE, ObjInstantiate().

ObjFreeChunkHandles()

void	ObjFreeChunkHandles(
        MemHandle		mh,			/* handle portion of optr */
        ChunkHandle		ch);			/* chunk portion of optr */

This routine is the same as ObjFreeChunk(); the chunk is specified by its handles rather than by an optr.

Include: object.h

ObjFreeDuplicate()

void	ObjFreeDuplicate(
        MemHandle mh);			/* handle of duplicate block to be freed */

This routine frees a block that had been saved with ObjSaveBlock() or created with ObjDuplicateResource() . It must be passed the memory handle of the duplicated resource.

Warnings: All objects in the duplicated resource must be properly detached to ensure that nothing tries to send messages to the objects in the block. Additionally, the block's in-use count and interactable count should be zero.

Include: object.h

See Also: ObjDuplicateResource(), ObjSaveBlock(), ObjLMemBlockHeader.

ObjFreeMessage()

void	ObjFreeMessage(
        EventHandle event);				/* event to be freed */

This routine frees an event handle and its associated event. This is rarely, if ever, used by anything other than the kernel. The kernel uses this routine to free events after they have been handled.

Include: object.h

ObjFreeObjBlock()

void	ObjFreeObjBlock(
        MemHandle block);			/* handle of the object block to be freed */

This routine frees the specified object block. It first checks the block's in-use count to see if any external references to the block are being kept. If the in-use count is nonzero, ObjFreeObjBlock() simply sets the block's auto-free bit and returns; the block will be freed the first time the in-use count reaches zero. If the in-use count is zero (no external references), the block will be freed immediately.

If the object block passed is not run by the calling thread, the operation will be handled by a remote call in the object block's thread.

Include: object.h

See Also: ObjFreeDuplicate(), MSG_META_BLOCK_FREE.

ObjGetFlags()

ObjChunkFlags ObjGetFlags(
        optr	o);		/* optr of subject object */

This routine returns the object flags associated with a given object. The object is specified by the passed optr, and the flags are stored in the object's ObjChunkFlags record.

Include: object.h

See Also: ObjSetFlags(), ObjChunkFlags.

ObjGetFlagsHandles()

ObjChunkFlags ObjGetFlagsHandles(
        Memhandle		mh,			/* handle portion of optr */
        ChunkHandle		ch);			/* chunk portion of optr */

This routine is the same as ObjGetFlags() , but the object is specified with its handles rather than with its optr.

Include: object.h

ObjGetMessageInfo()

Message	ObjGetMessageInfo(
        EventHandle		event,				/* event to be queried */
        optr	 	* dest);				/* buffer for destination optr */

This routine gets information about the specified event . The return value is the message number of the event. The dest parameter is a pointer to an optr. This routine will return with the optr represinting the event's destination object.

Include: object.h

ObjIncDetach()

void	ObjIncDetach(
        optr	obj);		/* optr of calling object */

This routine increments the number of detach acknowledgments an object must receive before it can safely be detached. Each time the detaching object sends notification of its detachment, it must call ObjIncDetach() to indicate that it must receive a corresponding detach acknowledgment ( MSG_META_ACK ).

The calling object must have previously called ObjInitDetach() . Since the detach mechanism is implemented in MetaClass , it is highly unlikely you will ever need to call this routine. ObjIncDetach() takes a single parameter: the optr of the calling object.

Include: metaC.goh

See Also: MSG_META_DETACH, ObjInitDetach(), ObjEnableDetach(), MSG_META_ACK.

ObjIncInteractibleCount()

void	ObjIncInteractibleCount(
        MemHandle mh);			/* handle of object block */

This routine increments the interactable count of the given object block. The interactable count maintains the number of objects currently visible to the user or about to be acted on by the user (e.g. via keyboard accelerator). The interactable count is maintained by the UI; only in extremely special cases may you need to increment or decrement the count. To decrement the count, use ObjDecInteractibleCount() .

Visible objects increment the interactable count in their MSG_VIS_OPEN handlers and decrement it in their MSG_VIS_CLOSE handlers. This is built into VisClass .

Include: object.h

See Also: ObjDecInteractibleCount(), MSG_VIS_OPEN, MSG_VIS_CLOSE, ObjLMemBlockHeader.

ObjIncInUseCount()

void	ObjIncInUseCount(
        MemHandle mh);			/* handle of object block */

This routine increments the given object block's in-use count. The in-use count maintains the number of outside references to this object block which are stored elsewhere and which need to be removed before the block can safely be freed. If you store an optr to an object block, you should increment the in-use count of the block.

When the reference to the block is removed, the in-use count should be decremented with ObjDecInUseCount() .

Include: object.h

See Also: ObjDecInUseCount(), ObjIncInteractibleCount(), ObjLMemBlockHeader.

ObjInitDetach()

void	ObjInitDetach(
        MetaMessages 		msg,
        optr		obj			/* object being detached */
        word		callerID,			/* an identifier token for the caller */
        optr		ackOD);			/* object to which ack is sent */

Initialize the detach sequence for the specified object. The detach sequence severs all ties between the system and the object, allowing it to be destroyed without other objects or geodes trying to contact it. It is highly unlikely you will ever call this routine; typically, you will instead use MSG_META_DETACH or one of the generic or visible object messages, which will call this routine. The parameters for this routine are

msg
The detach message.
obj
The optr of the object to be detached.
callerID
The caller object's ID.
ackOD
The optr of the caller object or another object which is to receive acknowledgment notification of the detach.

Include: metaC.goh

See Also: MSG_META_DETACH, MSG_GEN_DESTROY, MSG_VIS_REMOVE, ObjIncDetach(), ObjEnableDetach(), MSG_META_ACK.

ObjInitializeMaster()

void	ObjInitializeMaster(
        optr 		obj,				/* object to be initialized */
        ClassStruct		* class);				/* class in master group */

This routine initializes the appropriate master part of the passed object, resizing the instance chunk if necessary. It takes two parameters:

obj
The optr of the object whose master part is to be initialized.
class
A pointer to the class definition of a class in the appropriate master group. This does not have to be the master class; it must only be a class in the master goup.

Warnings: This routine may resize and/or move chunks or object blocks, thereby invalidating pointers and segment addresses.

Include: object.h

See Also: ObjResizeMaster(), ObjInitializePart(), ClassStruct.

ObjInitializeMasterHandles()

void	ObjInitializeMasterHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        ClassStruct		* class);				/* class in master group */

This routine is the same as ObjInitializeMaster() except it specifies the object via its handles rather than its optr.

Include: object.h

ObjInitializePart()

void	ObjInitializePart(
        optr 	obj,			/* object to have a part initialized */
        word	masterOffset);			/* offset to master offset in chunk */

This routine initializes all master parts of the given object down to and including the master part specified in masterOffset . It will resize the chunk if necessary and even resolve variant classes above the master group specified, if necessary. This routine takes two parameters:

obj
The optr of the object to be initialized.
masterOffset
The offset within the parent's instance chunk to the master group's offset (the value that would appear in the parent class' Class_masterOffset field in its ClassStruct structure).

Warnings: This routine may move and/or resize chunks or object blocks, thereby invalidating pointers and segment addresses.

Include: object.h

See Also: ObjResizeMaster(), ObjInitializeMaster(), MSG_META_RESOLVE_VARIANT_SUPERCLASS.

ObjInitializePartHandles()

void	ObjInitializePartHandles(
        Memhandle		mh,					/* handle portion of optr */
        ChunkHandle		ch,					/* chunk portion of optr */
        word		masterOffset);					/* master group offset */

This routine is the same as ObjInitializePart() except that it specifies the object via its handles rather than an optr.

Include: object.h

ObjInstantiate()

optr	ObjInstantiate(
        MemHandle		block,				/* block in which new object
                         * will be instantiated */
        ClassStruct		* class);				/* class of new object */

This routine instantiates a new object, allocating the proper size instance chunk. It returns the optr of the new object; this optr can then be used to send setup messages or other messages (such as adding the object to an object tree, setting it usable, etc.).

The new object's instance data will be initialized to all zeroes if it has no master parts (is a direct descendant of MetaClass ). If it is a member of some master group, only enough space for the base structure (the master offsets and the class pointer) will be allocated. In either case, initialization of the instance data will occur at a later time.

ObjInstantiate() takes two parameters:

block
The memory handle of an object block in which the object's instance chunk will be allocated. This block must be an object block, though it need not be run by the caller's thread. If the block is run by another thread, the routine will be executed as a remote call.
class
A pointer to the ClassStruct structure of the class of the new object. This pointer will be set in the object's class pointer (the first four bytes of the instance chunk).

Warnings: This routine, because it allocates a new chunk, may cause LMem and Object blocks to move or resize, thereby invalidating any pointers and segment addresses. Be sure to dereference pointers after calls to this routine.

Include: object.h

See Also: ObjInstantiateForThread().

ObjInstantiateForThread()

optr	ObjInstantiateForThread(
        ThreadHandle		thread,				/* thread to run new object */
        ClassStruct		*class);				/* class of new object */

This routine is similar to ObjInstantiate() except you pass the thread to run the new object rather than the memory handle to an object block (to store the new object). To specify the current thread, pass "0" in thread. This routine automatically allocates memory blocks as needed. It returns the optr to the new object.

Include: object.h

See Also: ObjInstantiate().

ObjIsClassADescendant()

Boolean	ObjIsClassADescendant(
        ClassStruct		* class1,				/* proposed ancestor */
        ClassStruct		* class2);				/* proposed descendant */

This routine checks if class2 is a descendand of class1 and returns true if it is.

Include: object.h

ObjIsObjectInClass()

Boolean	ObjIsObjectInClass(
        optr		obj,				/* object to check */
        ClassStruct	* class);			/* proposed class */

This routine checks to see if the passed object is a member of the specified class. It checks superclasses as well, but if an unresolved variant class is encountered, the variant will not be resolved. If you want to search past variant class links, you should send MSG_META_DUMMY to the object first. The two parameters for this routine are

obj
The optr of the object to be checked. Its object block must be locked down before this routine is called. (If the object's block might not be locked down, perhaps you should send MSG_META_IS_OBJECT_IN_CLASS to the object instead of calling this routine.)
class
A pointer to the subject class' ClassStruct definition.

ObjIsObjectInClass() returns true if the object is in the class, false (zero) if it is not.

Include: object.h

ObjIsObjectInClassHandles()

Boolean	ObjIsObjectInClassHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        ClassStruct		* class);				/* proposed class */

This routine is just like ObjIsObjectInClass() except the object is specified via its handles rather than its optr.

Include: object.h

ObjLinkFindParent()

optr	ObjLinkFindParent(
        optr	obj,		/* child's optr */
        word	masterOffset,		/* offset to master part with link field */
        word	linkOffset);		/* offset in master part to link field */

This routine returns the optr of the specified object's parent. It must be passed the following:

obj
The optr of the object whose parent is to be found.
masterOffset
The offset within the object's instance chunk to its master group's offset (the value that would appear in the Class_masterOffset field in its ClassStruct structure).
linkOffset
The offset within the object's instance chunk to the link field.

Include: metaC.goh

See Also: MSG_VIS_FIND_PARENT, MSG_GEN_FIND_PARENT.

ObjLockObjBlock()

void	* ObjLockObjBlock(
        MemHandle mh);			/* handle of object block */

This routine locks an object block, loading in the block if necessary. It must be passed the handle of the block, and it returns the locked block's segment address. When the caller is done using the block, it should unlock it with MemUnlock() .

Be Sure To: Always unlock the block when you are done with it, with MemUnlock() .

Include: object.h

See Also: MemLock(), MemUnlock().

ObjMapSavedToState()

VMBlockHandle ObjMapSavedToState(
        MemHandle mh);			/* handle of object block */

This routine returns the VM block handle of the state file block corresponding to the passed object block. If the specified object block has no state file equivalent, a null handle is returned.

Include: object.h

ObjMapStateToSaved()

MemHandle ObjMapStateToSaved(
        VMBlockHandle		vmbh,			/* VM block handle of state block */
        GeodeHandle		gh);			/* handle of geode owning block */

This routine takes a VM block handle and a geode handle and returns the memory block corresponding to the VM block, if any. The two parameters are

vmbh
The VM block handle of the VM block to be mapped.
gh
The geode handle of the owner of the block, or zero to use the calling geode's handle.

If the block is found, ObjMapStateToSaved() returns its handle. If the block is not found, it returns a null handle.

Include: object.h

ObjMarkDirty()

void	ObjMarkDirty(
        optr 	o);	/* object to be marked dirty */

This routine marks an object dirty, indicating that changes to it should be saved when its object block is saved. If you want changes to objects saved, you should mark the object dirty.

Tips and Tricks: Often you do not need this routine because parameters or flags to other routines will set the object dirty automatically. If there is any doubt, however, you should use this routine.

Include: object.h

See Also: ObjChunkFlags, ObjSetFlags().

ObjMarkDirtyHandles()

void	ObjMarkDirtyHandles(
        MemHandle		mh,			/* handle portion of optr */
        ChunkHandle		ch);			/* chunk portion of optr */

This routine is the same as ObjMarkDirty() except that it specifies the object via handles rather than an optr.

ObjProcBroadcastMessage()

void	ObjProcBroadcastMessage(
        EventHandle event);			/* the event to be broadcast */

This routine broadcasts the given event to all threads which have message queues. It must be passed an encapsulated event (usually recorded with @record ) and returns nothing. This is typically used for notification purposes.

Include: metaC.goh

ObjRelocateEntryPoint()

void *	ObjRelocateEntryPoint(
        EntryPointRelocation *			relocData);

ObjRelocOrUnRelocSuper()

void	ObjRelocOrUnRelocSuper(
        optr 		oself,
        ClassStruct		*class,
        word		frame);

Call this routine to relocate an object's superclass.

Include: object.h

ObjResizeMaster()

void	ObjResizeMaster(
        optr	obj,		/* object to have its master part resized */
        word	masterOffset,		/* master offset of proper master part */
        word	newSize);		/* new size for the master part */

This routine resizes a master part of an object's instance chunk. It is typically used to allocate space for a master part or to resize the master part to zero (as when the Vis part of a visible object is removed in MSG_VIS_CLOSE ). This routine must be passed the following three parameters:

obj
The optr of the object whose master part is to be resized.
masterOffset
The offset into the object's instance chunk where the offset to the master part is kept (this is the same offset held in the master class' Class_masterOffset field).
newSize
The new size of the master part. This can be found in the master class' Class_instanceSize field.

Warnings: This routine may resize and/or move chunks or object blocks, thereby invalidating stored segment addresses and pointers.

Include: object.h

See Also: ClassStruct, ObjInitializeMaster(), ObjInitializePart().

ObjResizeMasterHandles()

void	ObjResizeMasterHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        word		masterOffset,				/* offset to master part */
        word		newSize);				/* new size of master part */

This routine is the same as ObjResizeMaster() except that the object is specified with its handles rather than its optr.

Include: object.h

ObjSaveBlock()

void	ObjSaveBlock(
        MemHandle mh);			/*handle of block to be marked for saving */

This routine sets up an object or LMem block to be saved to its owner's state file. The block's handle must be passed in mh . The block must be an object block.

Include: object.h

See Also: ObjMapSavedToState(), ObjMapStateToSaved().

ObjSetFlags()

void	ObjSetFlags(
        optr		o,				/* object whose flags will be set */
        ObjChunkFlags 		bitsToSet,				/* flags to set */
        ObjChunkFlags		bitsToClear);				/* flags to clear */

This routine sets the chunk flags for the specified object. Flags that should be set are passed in bitsToSet , and flags that should be cleared are passed in bitsToClear . Typically, applications will not use this routine but will rather let the kernel maintain the object's flags.

Include: object.h

See Also: ObjGetFlags(), ObjChunkFlags.

ObjSetFlagsHandles()

void	ObjSetFlagsHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        ObjChunkFlags		bitsToSet,				/* flags to set */
        ObjChunkFlags		bitsToClear);				/* flags to clear */

This routine is the same as ObjSetFlags() except that the object is specified via its handles rather than its optr.

Include: object.h

ObjTestIfObjBlockRunByCurThread()

Boolean	ObjTestIfObjBlockRunByCurThread(
        MemHandle mh);			/* handle of object block */

This routine checks if the calling thread is running the specified object block. This routine can be used to determine if calls to objects in the block are across threads or internal to the calling thread. Pass this routine the handle of the object block to be checked--if the object block is a VM block, the thread for the VM file is checked rather than that for the block.

If the block is run by the calling thread, the return value is true . If a different thread runs the block, the return is false (zero).

Include: object.h

ObjUnrelocateEntryPoint()

void	ObjUnrelocateEntryPoint(
        EntryPointRelocation *			relocData,
        void *			entryPoint);

ObjVarAddData()

void	* ObjVarAddData(
        optr		obj,				/* object to add vardata to */
        VardataKey		dataType,				/* vardata type */
        word		dataSize);				/* vardata data size, if any */

This routine adds or alters a variable data entry for the specified object. If the data type does not currently exist in the instance chunk, it will be allocated and added to the chunk. If it does exist, the extra data of the entry will be re-initialized to all zeroes.

This routine returns a pointer to the extra data of the new or modified entry; if the entry has no extra data, an opaque pointer to the entry is passed, and you can use this pointer with ObjVarDeleteDataAt() . In either case, the object will be marked dirty.

If it is possible that you are adding the object's first/only field of variable data, set the object's OCF_VARDATA_RELOC flag by means of the ObjSetFlags() routine or MSG_META_SET_FLAGS.

The parameters of this routine are

obj
The optr of the object affected. This should be the caller's optr.
dataType
The VardataKey word declaring the data type and its flags. The VDF_SAVE_TO_STATE flag must be properly set; the VDF_EXTRA_DATA flag is ignored, however, as the routine will set it properly.
dataSize
The size of the extra data for this type. If the type has no extra data, pass zero.

Include: object.h

Warnings: This routine should be called only by the object whose vardata is affected. To operate on other objects' vardata remotely, use messages provided by MetaClass (see below under "See Also").

See Also: MSG_META_ADD_VAR_DATA, ObjVarDeleteDataAt().

ObjVarAddDataHandles()

void	* ObjVarAddDataHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        VardataKey		dataType,				/* vardata type */
        word		dataSize);				/* vardata data size, if any */

This routine is the same as ObjVarAddData() except that the object is specified via its handles rather than its optr.

Include: object.h

ObjVarCopyDataRange()

void	ObjVarCopyDataRange(
        optr	source,	/* the optr of the source object */
        optr	dest,	/* the optr of the destination (calling) object */
        word	rangeStart,	/* the smallest data type value to be copied */
        word	rangeEnd);	/* the largest data type value to be copied */

This routine copies all the vardata entries within the specified range from the source object to the dest object. The range to be copied is specified by data types and is between rangeStart and rangeEnd , inclusive. If any data entries are copied, the destination object will be marked dirty.

Warnings: This routine should be called only by the destination object; it is against OOP doctrine for one object to alter another's instance data.

Include: object.h

ObjVarDeleteData()

Boolean	ObjVarDeleteData(
        optr		obj,				/* object to delete from */
        VardataKey		dataType);				/* data type to delete */

This routine deletes a vardata entry from the specified object's instance chunk, if the entry exists. The entry is specified by its data type; to delete an entry specified by a pointer to it, use ObjVarDeleteDataAt() , below. It returns an error flag: true if the entry was not found, false if the entry was successfully deleted. The object will also be marked dirty by the routine.

The parameters for this routine are

obj
The optr of the object affected. This should be the caller's optr.
dataType
The VardataKey word declaring the data type and its flags. Both the VDF_SAVE_TO_STATE flag and the VDF_EXTRA_DATA flag are ignored.

Warnings: This routine should be called only by the object whose vardata is affected. To operate on other objects' vardata remotely, use messages provided by MetaClass (see below under "See Also").

Include: object.h

See Also: MSG_META_DELETE_VAR_DATA, ObjVarDeleteDataAt().

ObjVarDeleteDataHandles()

Boolean	ObjVarDeleteDataHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        VardataKey		dataType);				/* data type to delete */

This routine is the same as ObjVarDeleteData() except that the object is specified via its handles rather than its optr.

Include: object.h

ObjVarDeleteDataAt()

void	ObjVarDeleteDataAt(
        optr	obj,			/* object to delete from */
        word	extraDataOffset);			/* offset to extra data to delete */

This routine deletes the specified vardata entry from the given object's instance chunk. The vardata entry is specified by its pointer as returned by ObjVarAddData() , ObjVarFindData() , and ObjVarDerefData() . To delete an entry specified by its data type, use ObjVarDeleteData() , above.

Warnings: This routine should be called only by the object whose vardata is affected. To operate on other objects' vardata remotely, use messages provided by MetaClass (see below under "See Also").

Include: object.h

See Also: MSG_META_DELETE_VAR_DATA, ObjVarDeleteData().

ObjVarDeleteDataAtHandles()

void	ObjVarDeleteDataAtHandles(
        MemHandle		mh,			/* handle portion of optr */
        ChunkHandle		ch,			/* chunk portion of optr */
        word	extraDataOffset);				/* offset to extra data to delete */

This routine is the same as ObjVarDeleteDataAt() except that the object is specified via its handles rather than its optr.

Include: object.h

ObjVarDeleteDataRange()

void	ObjVarDeleteDataRange(
        optr 		obj,					/* object to delete from */
        word		rangeStart,					/* start of range */
        word		rangeEnd,					/* end of range */
        Boolean		useStateFlag);					/* save to state flag */

This routine deletes all data entries within a given range for the passed object. The range is specified by beginning and ending data types and is inclusive. The four parameters to this routine are

obj
The optr of the object whose data entries are to be deleted.
rangeStart
The lowest number data type to be deleted. Both the VDF_SAVE_TO_STATE flag and the VDF_EXTRA_DATA flag are ignored.
rangeEnd
The highest number data type to be deleted. Both the VDF_SAVE_TO_STATE flag and the VDF_EXTRA_DATA flag are ignored.
useStateFlag
A flag indicating whether entries with their VDF_SAVE_TO_STATE flags should be deleted. Pass true (nonzero) to take the state flag into account; pass false (zero) to delete all entries in the range.

Warnings: This routine should be called only by the object whose vardata is affected. To operate on other objects' vardata remotely, use messages provided by MetaClass (see below under "See Also").

Include: object.h

See Also: MSG_META_DELETE_VAR_DATA.

ObjVarDeleteDataRangeHandles()

void	ObjVarDeleteDataRangeHandles(
        MemHandle		mh,					/* handle portion of optr */
        ChunkHandle		ch,					/* chunk portion of optr */
        word		rangeStart,					/* start of range */
        word		rangeEnd,					/* end of range */
        Boolean		useStateFlag);					/* save to state flag */

This routine is the same as ObjVarDeleteDataRange() except that the object is specified via its handles rather than its optr.

Include: object.h

ObjVarDerefData()

void	* ObjVarDerefData(
        optr		obj,				/* object having data type */
        VardataKey		dataType);				/* data type to dereference */

This routine is exactly like ObjVarFindData() , below, except that it does not return a null pointer if the data type is not found. Do not use this routine unless you are absolutely sure the data type is in the object; otherwise, results are unpredictable.

Include: object.h

See Also: ObjVarFindData().

ObjVarDerefDataHandles()

void	* ObjVarDerefDataHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        VardataKey		dataType);				/* data type to dereference */

This routine is the same as ObjVarDerefData() except that the object is specified via its handles rather than its optr.

Include: object.h

ObjVarFindData()

void	* ObjVarFindData(
        optr		obj,				/* object to be checked */
        VardataKey		dataType);				/* data type to find */

This routine searches an object's variable data for a given data type. If the type is found, ObjVarFindData() returns a pointer to the entry's extra data; if the entry has no extra data, an opaque pointer is returned which may be used with ObjVarDeleteDataAt() . If the entry is not found, a null pointer is returned. The pointer returned by this routine must be used before any subsequent operations on the object's block; the pointer may be invalidated by other LMem or object operations.

The two parameters of this routine are

obj
The optr of the object affected. This should be the caller's optr.
dataType
The VardataKey word declaring the data type and its flags. Both the VDF_SAVE_TO_STATE flag and the VDF_EXTRA_DATA flag are ignored.

Warnings: This routine should be called only by the object whose vardata is affected. To operate on other objects' vardata remotely, use messages provided by MetaClass (see below under "See Also").

Include: object.h

See Also: MSG_META_GET_VAR_DATA.

ObjVarFindDataHandles()

void	* ObjVarFindDataHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        VardataKey		dataType);				/* data type to find */

This routine is the same as ObjVarFindData() except that the object is specified via its handles rather than its optr.

Include: object.h

ObjVarScanData()

void	ObjVarScanData(
        optr		obj,					/* object to be scanned */
        word		numHandlers,					/* number of handlers in table */
        VarDataCHandler		* handlerTable,					/* pointer to handler table */
        void		* handlerData);					/* pointer to handler data */

This routine scans an object's vardata and calls all the vardata handlers specified in the passed handler table. Pass it the following parameters:

obj
The optr of the object whose variable data table is to be scanned.
numHandlers
The number of handlers specified in the passed handler table.
handlerTable
A pointer to a list of VarDataCHandler structures. Each of these structures contains a vardata data type and a pointer to the routine that is to handle it. All the handler routines must be in the same segment as the handler table.
handlerData
A pointer to a buffer that is passed on to the handlers. This can contain any information of specific interest to the application or handlers.

Vardata Handler Format:
 A vardata handler routine must have the following format:

void _pascal (MemHandle mh, ChunkHandle chnk,
    VarDataEntry *extraData, word dataType,
    void *handlerData)

The handler should not free the object chunk or destroy the object; it can do anything else it pleases. The handler returns nothing and takes the following parameters:

mh:chnk
The memory handle and chunk handle of the object being referenced. Together, these comprise the optr of the object.
extraData
A pointer to the data type's extra data, if it has any. This pointer may be parsed with the macros VarDataTypePtr() , VarDataFlagsPtr() , and VarDataSizePtr() .
dataType
The data type of the data entry being handled. This is a record of type VardataKey .
handlerData
A pointer to a buffer passed through by ObjVarScanData() . This buffer may be used for passing additional data to the handlers.

Structures: The VarDataCHandler structure contains two elements:

typedef struct {
    word		VDCH_dataType;
    void	_pascal 	(*VDCH_handler) (
            MemHandle		mh,
            ChunkHandle		chnk,
            VarDataEntry		* extraData,
            word		dataType
            void		* handlerData);
} VarDataCHandler;

The first element is the data type, a record containing the data type and the vardata flags. The second element is a far pointer to the handler routine for the type.

Include: object.h

ObjVarScanDataHandles()

void	ObjVarScanDataHandles(
        MemHandle		mh,				/* handle portion of optr */
        ChunkHandle		ch,				/* chunk portion of optr */
        word		numHandlers,					/* number of handlers in table */
        VarDataCHandler		* handlerTable,					/* pointer to handler table */
        void		* handlerData);					/* pointer to handler data */

This routine is the same as ObjVarScanData() except that the object is specified via its handles rather than its optr.

Include: object.h

offsetof()

word	offsetof(struc, field);

This macro returns the offset of the specified field within the specified structure.

OptrToChunk()

ChunkHandle OptrToChunk(op);
        optr	op;

This macro extracts the chunk handle portion of the given optr.

See Also: ConstructOptr(), OptrToHandle().

OptrToHandle()

MemHandle OptrToHandle(op);
        optr	op;

This macro extracts the MemHandle portion of the given optr.

See Also: ConstructOptr(), OptrToChunk().

ParallelClose()

StreamError ParallelClose(
        GeodeHandle 		driver,
        ParallelUnit 		unit,
        Boolean 		linger);

Close the stream to a parallel port.

Include: streamC.h

ParallelGetError()

StreamError	 	ParallelGetError (
        Handle 		driver,
        ParallelUnit		unit,
        StreamRoles 		roles,
        ParallelError *		errorCode);

This routine returns any parallel-driver-specific error codes set by the last call to the parallel-driver.

ParallelOpen()

StreamError ParallelOpen(
        GeodeHandle 		driver,
        ParallelUnit 		unit,
        StreamOpenFlags 		flags,
        word		outBuffSize,
        word 		timeout);

This routine opens a stream to the specified parallel port. It is passed the following arguments:

driver
The GeodeToken of the parallel driver.
unit
The parallel port to open.
flags
This specifies whether the call should fail if the port is busy, or wait for a time to see if it will become free.
outBuffSize
The size of the stream buffer used for output to the parallel port.
timeout
The number of clock ticks to wait for the port to become free. (This argument is ignored if flags is not STREAM_OPEN_TIMEOUT.)

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

Include: streamC.h

ParallelSetError()

StreamError ParallelSetError(
        Handle 		driver,
        ParallelUnit 		unit,
        StreamRoles		roles,
        ParallelError *		errorCode);

This routine sets the device-specific error code for the specified parallel port.

ParallelWrite()

StreamError ParallelWrite(
        GeodeHandle 		driver,
        ParallelUnit 		unit,
        StreamBlocker 		blocker,
        word 		buffSize,
        const byte *		buffer,
        word *		numBytesWritten);

Write data to a parallel port.

Include: streamC.h

ParallelWriteByte()

StreamError ParallelWrite(
        GeodeHandle 		driver,
        ParallelUnit 		unit,
        StreamBlocker 		blocker,
        word 		buffSize,
        byte		dataByte);

Write one byte of data to a parallel port.

Include: streamC.h

ParserParseString()

ParserScannerEvaluatorError ParserParseString(
        char		*textBuffer,
        byte		*tokenBuffer,
        CParserStruct		*parserParams,
        CParserReturnStruct		*retVal);

Parse a string into a sequence of tokens.

Include: parse.h

ParserEvalExpression()

ParserScannerEvaluatorError ParserEvalExpression(
        byte		*scratchBuffer,
        byte		*resultBuffer,
        word		bufSize,
        CEvalStruct		*evalParams);

Evaluate a parsed expression.

Include: parse.h

ParserFormatExpression()

ParserScannerEvaluatorError ParserFormatExpression(
        byte		*tokenBuffer,
        char		*textBuffer,
        CFormatStruct		*formatParams);

Format an expression from parsed data.

Include: parse.h

PCB()

#define PCB(return_type, pointer_name, args) \
        return_type _pascal (*pointer_name) args

This macro is useful for declaring pointers to functions that use the pascal calling conventions. For example, to declare a pointer to a function which is passed two strings and returns an integer, one could write

PCB(int, func_ptr, (const char *, const char *));

which would be expanded to

int _pascal (*func_ptr) (const char *, const char *);

See Also: CCB().

PCCOMABORT()

void PCCOMABORT(void);

This routine aborts the current file transfer operation being carried out by the PCCom library. It is the third entry point in the PCCom library.

Include: pccom.goh

PCCOMEXIT()

PCComReturnType PCCOMEXIT();

This routine kills a pccom thread such as those started by PCCOMINIT(). It is the second entry point in the PCCom library.

Structures:

        typedef ByteEnum PCComReturnType;
        #define PCCRT_NO_ERROR 0
        #define PCCRT_CANNOT_LOAD_SERIAL_DRIVER 1
        #define PCCRT_CANNOT_CREATE_THREAD 2
        #define PCCRT_CANNOT_ALLOC_STREAM 3
        #define PCCRT_ALREADY_INITIALIZED 4

Include: pccom.goh

PCCOMINIT()

PCComReturnType PCCOMINIT(
        SerialPortNum 		port,
        SerialBaud 		baud,
        word 		timeout,
        optr 		callbackOptr,
        PCComInitFlags 		flags);

This entry point of the PCCom library spawns a new thread which monitors a serial port and acts as a passive pccom terminal. This routine is the first entry point in the PCCom library.

This routine takes the following arguments:

port
A SerialPortNum value specifying which serial port to use for the pccom connection. Pass -1 for the system default value: com1 for the Zoomer, com2 for the desktop product.
baud
A SerialBaud value specifying what speed to use. Pass -1 for the system default value: 19200 baud for the Zoomer, 38400 baud for the desktop product.
timeout
Number of clock ticks (one tick is 1/60 second) to allow for connection.
callbackOptr
An object which will receive notification messages of certain events. A value of zero means no notification will be sent.
flags
If an object will be receiving notification messages, these flags determine what sort of notifications will be sent.

Structures:

        typedef ByteEnum PCComReturnType;
        #define PCCRT_NO_ERROR 0
        #define PCCRT_CANNOT_LOAD_SERIAL_DRIVER 1
        #define PCCRT_CANNOT_CREATE_THREAD 2
        #define PCCRT_CANNOT_ALLOC_STREAM 3
        #define PCCRT_ALREADY_INITIALIZED 4

        typedef WordFlags PCComInitFlags;
            /* send notifications when text is available for display */
        #define PCCIF_NOTIFY_OUTPUT 0x8000
            /* send notification when the remote machine shuts down the
 * serial line */
        #define PCCIF_NOTIFY_EXIT 0x4000

Include: pccom.goh

ProcCallFixedOrMovable_cdecl()

dword	ProcCallFixedOrMovable_cdecl(
        void 	(*routine),
        ...)

This routine calls the routine pointed to, passing the other arguments through to the called routine. The called routine must use C calling conventions.

Include: resource.h

ProcCallFixedOrMovable_pascal()

dword	ProcCallFixedOrMovable_pascal(
        ...,
        void 	(*routine))

This routine calls the routine pointed to, passing the other arguments through to the called routine. The called routine must use Pascal calling conventions.

Include: resource.h

ProcGetLibraryEntry()

void *	ProcGetLibraryEntry(
        GeodeHandle		library,
        word		entryNumber)

This routine returns the pointer to a library's entry-point.

Include: resource.h

ProcInfo()

ThreadHandle ProcInfo(
        GeodeHandle gh);		/* handle of geode to check */

This routine returns the first thread of the process geode specified. If the geode is not a process, the routine will return a null handle.

Include: geode.h

PtrToOffset()

word	PtrToOffset(ptr);
        dword	ptr;

This macro returns just the lower 16 bits of the given dword. It is most useful for extracting the offset portion of a far pointer.

PtrToSegment()

word	PtrToSegment(ptr);
        dword	ptr;

This macro returns just the upper 16 bits of the given dword. It is most useful for extracting the segment address of a far pointer.

PutUpDialogViaUIThread()

optr PutUpDialogViaUIThread(
    optr	dialogBoxTemplate );

This routine displays a dialog using a template that you supply, and runs it on the system's UI thread. It sends MSG_GEN_INTERACTION_INITIATE to bring the dialog on screen. See the SDK_9000/Dialogs sample application for an example of how to use this routine.

Use the routine TakeDownDialogViaUIThread() to remove the duplicate dialog from the screen and from memory.

Pass:

dialogBoxTemplate
Optr of the dialog you want to display. The dialog must be set to GIA_SYS_MODAL and start off as not GS_USABLE.

Return: Optr of the duplicated dialog object. Pass this to TakeDownDialogViaUIThread() when you want to remove the dialog.

Include: foam.goh

See Also: TakeDownDialogViaUIThread(), UserCreateDialog()

qsort

extern void _pascal qsort(
        void *array,
        word count,
        word elementSize,
        PCB(int, compare, (const void *, const void *)));

This is a standard quicksort routine. The callback routine must be decared _pascal.

QueueGetMessage()

EventHandle QueueGetMessage(
        QueueHandle qh);			/* queue to query */

This routine returns the next message on the given queue, blocking if the queue is empty. When a new message is added to the empty queue, this routine will unblock the thread and return the message. This routine is used almost exclusively by the kernel.

Include: geode.h

QueuePostMessage()

void	QueuePostMessage(
        QueueHandle		qh,			/* queue to add event to */
        EventHandle		event,			/* event to be added to queue */
        MessageFlags		flags);			/* MF_INSERT_AT_FRONT or zero */

This routine adds the specified event to the passed queue . The only valid flag for this routine is MF_INSERT_AT_FRONT, which will put the event in the first spot of the queue.

Include: geode.h

RangeEnum()

Boolean	RangeEnum(
        CellFunctionParameters			* cfp,				/* cell function parameters */
        RangeEnumParams			* params);				/* special other parameters */

This routine calls a callback routine for each cell in a specified range. This routine is passed pointers to two structures, both of which are shown below. It returns false if all the cells were processed, true if any of the cells caused the routine to abort before the end of the range was reached.

Callback Parameters:

The callback routine, which must be declared _pascal, receives a RangeEnumCallbackParams structure, which has the following definition:

typedef struct {
    RangeEnumParams			*RECP_params;		/* see below */
   /* current row, column, and cell data of cell */
    word			RECP_row;
    word			RECP_column;
    word			RECP_cellData;
} RangeEnumCallbackParams;

The callback routine can do anything with the cell information. It should return false after successfully processing the cell; if an error occurs, or if it wants to abort the RangeEnum() , it should return true .

Include: cell.h

RangeExists()

Boolean	RangeExists( /* returns non-zero if there are cells in range */
        CellFunctionParameters			* cfp,				/* see RangeEnum() */
        word			firstRow,				/* range delimiters */
        byte			firstColumn,
        word			lastRow,
        byte			lastColumn);

This routine returns true if there are any cells in the specified range. It is passed a pointer to the CellFunctionParameters structure for the cell file, as well as the indices of the first and last row, and the first and last column, of the range to check.

Include: cell.h

RangeInsert()

void	RangeInsert(
        CellFunctionParameters			* cfp,				/* see RangeEnum() */
        RangeInsertParams			* rep);				/* parameters structure */

This routine shifts existing cells to make room for new ones. (It does not actually create new cells.) Which cells are shifted, and in what direction, is specified by the RangeInsertParams structure:

RIP_bounds
A Rectangle structure which specifies which cells should be shifted. The cells currently in this range will be shifted across or down, depending on the value of RIP_delta ; the shifted cells displace more cells, and so on, to the edge of the visible portion of the cell file. To insert an entire row (which is much faster than inserting a partial row), set RIP _bounds. R _left = 0 and RIP _bounds. R _right = LARGEST_COLUMN .
RIP_delta
A Point structure which specifies how far the cells should be shifted and in which direction. If the range of cells is to be shifted horizontally, RIP_delta. P _x should specify how far the cells should be shifted over, and RIP_delta. P _y should be zero. If the cells are to be shifted vertically, RIP_delta. P _y should specify how far the cells should be shifted over, and RIP_delta. P _x should be zero.
RIP_cfp
This is the address of the CellFunctionParameters structure. You don't have to initialize this; the routine will do so automatically.

Include: cell.h

Warnings: If cells are shifted off the "visible" portion of the cell file, you will be unable to access them by row or column numbers; but they will not be deleted. For this reason, you should free all such cells before calling RangeInsert() . (You can find out if there are any cells at the edges by calling RangeExists() .) For an explanation of the "visible" and "scratch-pad" portions of a cell file, see the Cell library documentation.

realloc()

void *	realloc(
        void *		blockPtr,				/* address of memory to resize */
        size_t		newSize);				/* New size of memory in bytes */

The malloc() family of routines is provided for Standard C compatibility. If a geode needs a small amount of fixed memory, it can call one of the routines. The kernel will allocate a fixed block to satisfy the geode's malloc() requests; it will allocate memory from this block. When the block is filled, it will allocate another fixed malloc-block. When all the memory in the block is freed, the memory manager will automatically free the block.

If a geode needs to change the size of a section of memory assigned to it by the malloc() family of routines, it should use realloc() . realloc() resizes the piece of memory specified and returns the memory's new base address.

If the new size is smaller then the previous size, bytes will be cut off from the end. The request is guaranteed to succeed. Furthermore, the memory will not be moved; the address returned will be the same as the address passed.

If the new size is larger than the previous size, realloc() may move the data to accommodate the request. If so, it will return the new address. The new memory added will not be zero-initialized. If realloc() cannot fulfill the request, it will return a null pointer, and the memory will not be altered.

Resizing a stretch of memory down to zero bytes is exactly the same as freeing it with free() . If you pass a null address to realloc() , it will allocate the memory the same way malloc() does.

The memory must be in a malloc-block assigned to the geode calling realloc() . If you want to resize memory in another geode's malloc-block, call GeoReAlloc() .

Warnings: Pass exactly the same address as the one returned to you when you allocated the memory. If you pass a different address, the results are undefined.

See Also: calloc(),
free(),
malloc(),
GeoReAlloc().

RecentContactsEraseStack()

BooleanRecentContactsEraseStack(
        RecentContactsType 			rcType);

This function erases all log entries of a certain type from the log. It returns zero if there was no error, non-zero otherwise. Pass one of RC_SENT_CALLS, RC_RECEIVED_CALLS, RC_MISSED_CALLS, RC_SENT_FAXES, RC_RECEIVED_FAXES, RC_SENT_SMS, or RC_RECEIVED_SMS.

Include: contlog.goh

RecentContactsGetCount()

word 	RecentContactsGetCount(
        RecentContactsType 			rcType);

This function returns the number of contacts of some type that have been made recently. Pass one of RC_SENT_CALLS, RC_RECEIVED_CALLS, RC_MISSED_CALLS, RC_SENT_FAXES, RC_RECEIVED_FAXES, RC_SENT_SMS, or RC_RECEIVED_SMS.

Include: contlog.goh

RecentContactsGetEntry()

word 	RecentContactsGetEntry(
        RecentContactsType 			rcType,
        int 			item,
        LogEntry 			*buffer);

This function retrieves the entry log for a call. The call is identified by its call type and an index number. The call type is one of RC_SENT_CALLS, RC_RECEIVED_CALLS, RC_MISSED_CALLS, RC_SENT_FAXES, RC_RECEIVED_FAXES, RC_SENT_SMS, or RC_RECEIVED_SMS. The index number should be at least zero and less than the number returned by RecentContactsGetCount() . The function fills in the passed LogEntry buffer with the entry's information. If there was an error, the function returns non-zero; otherwise, it returns zero.

Include: contlog.goh

RecentContactsGetMissedCallsTotalCount()

word	RecentContactsResetMissedCallsTotalCount();

This function returns the number of calls missed since the last time RecentContactsGetMissedCallsTotalCount() was called.

Include: contlog.goh

RecentContactsGetNewCount()

word RecentContactsGetNewCount(
        RecentContactsType 			rcType);

This function returns the number of contacts logged since the last time RecentContactsResetNewCount() was called.

Include: contlog.goh

RecentContactsResetMissedCallsTotalCount()

voidRecentContactsResetMissedCallTotalsCount( );

This function resets the number of missed calls as used by the RecentContactsGetMissedCallsTotalCount() function.

Include: contlog.goh

RecentContactsResetNewCount()

voidRecentContactsResetNewCount(
        RecentContactsType 			rcType);

This function resets the number of "new" contacts logged as used by the RecentContactsGetNewCount() function.

Include: contlog.goh

RespGetPDAHardwareInfo()

PDAHardwareInfo    RespGetPDAHardwareInfo(void);

This routine gets information about the current PDA hardware, specifically the type of PDA and whether or not it has a backlight. Use the following bit masks to extract the desired information from the PDAHardwareInfo bitfield:

typedef ByteFlags PDAHardwareInfo;
/* 5 bits unused */
#define PDAHI_BACKLIGHT_AVAILABLE       (0x04)
#define PDAHI_PDA_TYPE                  (0x02 | 0x01)    /* PDAType */

The PDAType is enumerated as:

typedef ByteEnum PDAType;
#define PDAT_PC_EMULATOR        0x0
#define PDAT_N9000              0x1
#define PDAT_N9000i             0x2

This routine is only available on builds 4 or newer of the Nokia 9000i Communicator (see Versions for more information).

Include: respondr.goh

SerialClose()

StreamError SerialClose(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        Boolean 		linger);

Close the stream to a serial port.

SerialCloseWithoutReset()

StreamError SerialClose(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        Boolean 		linger);

Close the stream to a serial port, without actually resetting the port.

SerialFlush()

StreamError SerialFlush(
        GeodeHandle 		driver,
        SerialUnit		unit,
        StreamRoles 		roles);

Flush all data pending in a serial port's input or output buffer (depending on the value of roles ).

SerialGetError()

StreamError	 	SerialGetError (
        Handle 		driver,
        SerialUnit		unit,
        StreamRoles 		roles,
        SerialError *		errorCode);

This routine returns error information pertaining to the last call to a serial driver. The SerialError codes returned may contain serial-driver-specific error codes not defined in the SerialError enumerated type.

SerialGetFormat()

StreamError SerialGetFormat(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        SerialFormat *		format,
        SerialMode *		mode,
        SerialBaud *		baud);

Get the format of a stream to a specified serial port.

SerialGetModem()

StreamError SerialGetModem(
        GeodeHandle 		driver,
        SerialUnit		unit,
        SerialModem *		modem);

Read a modem's hardware flow control bits.

SerialOpen()

StreamError SerialOpen(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        StreamOpenFlags 		flags,
        word 		inBuffSize,
        word		outBuffSize,
        word 		timeout);

This routine opens a stream to the specified serial port. It is passed the following arguments:

driver
The GeodeToken of the serial driver.
unit
The serial port to open.
flags
This specifies whether the call should fail if the port is busy, or wait for a time to see if it will become free.
inBuffSize
The size of the stream buffer used for input from the serial port.
outBuffSize
The size of the stream buffer used for output to the serial port.
timeout
The number of clock ticks to wait for the port to become free. (This argument is ignored if flags is not STREAM_OPEN_TIMEOUT.)

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

SerialQuery()

StreamError SerialQuery(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        StreamRoles role,
        word *		bytesAvailable);

Find out how much space is available in a serial buffer, or how much data is waiting to be read.

SerialRead()

StreamError SerialRead (
        Handle		driver,
        SerialUnit 		unit,
        StreamBlocker 		blocker,
        word 		buffSize,
        byte *		buffer,
        word *		numBytesRead);

Read data from a serial port and write it to a passed buffer.

SerialReadByte()

StreamError SerialReadByte (
        Handle		driver,
        SerialUnit 		unit,
        StreamBlocker 		blocker,
        byte		*dataByte);

Read a byte of data from a serial port and write it to a passed variable.

SerialSetError()

StreamError SerialSetError (
        Handle 		driver,
        SerialUnit 		unit,
        StreamRoles 		roles,
        SerialError 		errorCode);

This routine sets the serial-driver-specific error code for recovery with SerialGetError() .

SerialSetFlowControl()

StreamError 	SerialSetFlowControl (
        Handle 		driver,
        SerialUnit 		unit,
        SerialFlowControl 		flow,
        SerialModem 		modem,
        SerialModemStatus 		status);

This routine enables or disables software flow control. The flow argument has two flags, SFC_HARDWARE and SFC_SOFTWARE.

SerialSetFormat()

StreamError SerialSetFormat(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        SerialFormat 		format,
        SerialMode		mode,
        SerialBaud 		baud);

Set the format for a stream to a specified serial port.

SerialSetModem()

StreamError SerialSetModem(
        GeodeHandle 		driver,
        SerialUnit		unit,
        SerialModem 		modem);

Set a modem's hardware flow control bits.

SerialWrite()

StreamError SerialWrite(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        StreamBlocker 		blocker,
        word 		buffSize,
        const byte *		buffer,
        word *		numBytesWritten);

Write data to a serial port.

SerialWriteByte()

StreamError SerialWrite(
        GeodeHandle 		driver,
        SerialUnit 		unit,
        StreamBlocker 		blocker,
        word 		buffSize,
        byte		dataByte);

Write one byte of data to a serial port.

SGC_MACHINE

byte	SGC_MACHINE(val);
        dword	val;

This macro is used to extract the machine type from a SysGetConfig() return value.

Include: system.goh

SGC_PROCESSOR

byte	SGC_PROCESSOR(val);
        dword	val;

This macro is used to extract the processor type from a SysGetConfig() return value.

Include: system.goh

SocketAccept()

Socket SocketAccept(
        Socket 		s,
        int 		timeout);

To make a socket wait for and accept a connection, call SocketAccept() . The socket must be a listening socket (processed by SocketListen() ). Because the thread will block until a connection is made or SocketAccept() times out, programs will normally not call this routine in their main execution thread. Instead, a separate thread handles accepting a connection and receiving data from the connection.

If there are no connections to accept, SocketAccept() will set the SE_LISTEN_QUEUE_EMPTY error.

This routine takes the following arguments:

s
A listening socket (processed by SocketListen() ).
timeout
Time to wait for success, in 1/60 second ticks, or SOCKET_NO_TIMEOUT to try as long as possible.

It returns a socket which should be used to send and receive data over the connection. If the return value is zero, an error occured. Call ThreadGetError() to find out what the error value was.

See Also: SocketListen(),
SocketConnect(),
ThreadGetError().

Include: socket.goh

SocketAddLoadOnMsg()

SocketError SocketAddLoadOnMsg(
        SocketPort 		p,
        SocketLoadType 		slt,
        word 		disk,
        TCHAR 		*path);

To make the Socket library load a program when receiving packets on a particular port, call SocketAddLoadOnMsg() . The Socket library will write out an entry to the GEOS.INI file in the [socket] category and with key "LoadOnMsg". The Socket library consults these GEOS.INI file entries when it starts up and will act accordingly when it receives packets.

This routine takes the following arguments:

p
The port to monitor.
slt
How to load the application--either by UserLoadApplication() or GeodeLoad() .
disk
DiskHandle of application to load. This may be a StandardPath value.
path
Buffer holding path of application to load.

If successful, the routine will return SE_NORMAL (i.e., zero). Otherwise, it will return some other SocketError value.

See Also: SocketAddLoadOnMsgInDomain(),
SocketRemoveLoadOnMsg(),
UserLoadApplication(),
GeodeLoad().

Include: socket.goh

SocketAddLoadOnMsgInDomain()

SocketError SocketAddLoadOnMsgInDomain(
        SocketPort 		p,
        SocketLoadType 		slt,
        word 		disk,
        TCHAR 		*path,
        TCHAR 		*domain);

To make the Socket library load a program when receiving packets on a particular port within a particular domain, call SocketAddLoadOnMsgInDomain() . The Socket library will write out an entry to the GEOS.INI file in the [socket] category and with key "LoadOnMsg". The Socket library consults these GEOS.INI file entries when it starts up and will act accordingly when it receives packets.

This routine takes the following arguments:

p
The port to monitor.
slt
How to load the application--either by UserLoadApplication() or GeodeLoad() .
disk
DiskHandle of application to load. This may be a StandardPath value.
path
Buffer holding path of application to load.
domain
Buffer holding name of the domain to monitor.

If successful, the routine will return SE_NORMAL (i.e., zero). Otherwise, it will return some other SocketError value.

See Also: SocketAddLoadOnMsg(),
SocketRemoveLoadOnMsgInDomain(),
UserLoadApplication(),
GeodeLoad().

Include: socket.goh

SocketBind()

SocketError SocketBind(
        Socket 		s,
        SocketPort 		p,
        SocketBindFlags 		flags);

Use the SocketBind() routine to associate a socket with a port number on this side of the connection.

For any given domain, normally only one socket on the machine may be bound to any port number. If you have used SocketBind() to bind some other socket to this port, then you may only bind another socket to the port by setting the SBF_REUSE_PORT in the flags argument. If you have used SocketBindInDomain() to bind some other socket to this port, then you may not use SocketBind() to bind another socket to the port.

A given socket may only be bound to one port.

This routine takes the following arguments:

s
The socket to bind.
p
The port to monitor.
flags
Set the SBF_REUSE_PORT to bind the socket to the port even if another socket is bound to it. (You may not use this flag with datagram sockets.)

If successful, the routine will return SE_NORMAL (i.e., zero). Otherwise, it will return some other SocketError value. Be on the lookout for SE_SOCKET_ALREADY_BOUND, SE_PORT_IN_USE, or SE_BIND_CONFLICT.

See Also: SocketBindInDomain().

Include: socket.goh

SocketBindInDomain()

SocketError SocketBindInDomain(
        Socket 		s,
        SocketPort 		p,
        SocketBindFlags 		flags,
        TCHAR 		*domain);

Use the SocketBindInDomain() routine to associate a socket with a port number and domain on this side of the connection.

For any given domain, normally only one socket on the machine may be bound to any port number in any given domain. If you have used SocketBindInDomain() to bind some other socket to this port and domain, then you may only bind another socket to the port and domain by setting the SBF_REUSE_PORT in the flags argument. If you have used SocketBind() to bind some other socket to this port, then you may not use SocketBindInDomain() to bind another socket to the port.

A given socket may only be bound to one port.

This routine takes the following arguments:

s
The socket to bind
p
The port to monitor
flags
Set the SBF_REUSE_PORT to bind the socket to the port even if another socket is bound to it.
domain
The domain to monitor

If successful, the routine will return SE_NORMAL (i.e., zero). Otherwise, it will return some other SocketError value. Be on the lookout for SE_SOCKET_ALREADY_BOUND, SE_PORT_IN_USE, or SE_BIND_CONFLICT.

See Also: SocketBind().

Include: socket.goh

SocketCheckListen()

int SocketCheckListen(
        SocketPort 		p,
        TCHAR 		*domain,
        int 		bufsize);

Use the SocketCheckListen() command on a port to find out the domain for the first connect request on a listening socket--that is, the first connection request which has not yet been accepted. You might think of this as a limited form of "caller ID"--a chance to find out a bit about the incoming connection before you accept it. If SocketCheckListen() returns zero then you can call ThreadGetError() to get the SocketError value. If it is SE_PORT_NOT_LISTENING, it means that you have not passed it a port to which a socket is listening.

This routine takes the following arguments:

p
The port to check
domain
Buffer to hold the domain name
bufsize
The size of the domain buffer

It returns the length of the domain name. If the return value is zero, there was an error. Look out for SE_PORT_NOT_LISTENING, which signals that there is no socket bound to the port in question which is listening.

See Also: SocketBind(),
SocketBindInDomain(),
SocketListen().

Include: socket.goh

SocketCheckReady()

int SocketCheckReady(
        SocketCheckRequest 		*requests,
        int 		numRequests,
        int 		timeout);

To check one or more sockets to see if they have received data, received connection requests, or are ready to write, call SocketCheckReady() . It can check connections for incoming data or just incoming urgent data.

For programs which need to poll many sockets, SocketCheckReady() provides a tidy means to do this without spawning a thread for each socket.

The SocketCheckReady() routine takes an array of SocketCheckRequest structures. Each one of these structures contains a socket and a condition. SocketCheckReady() looks at each SocketCheckRequest structure in the array and returns the index of the first structure whose socket meets the condition.

Thus, to determine if a given socket is properly set-up so that you can send data through it, you would pass SocketCheckReady() a one-element array (represented in pseudo-code):

{ mySocket, SC_WRITE, 0 }

To check several sockets to see whether any of them had received any connection requests, pass SocketCheckReady() an array of the form (represented in pseudo-code):

{ socket1, SC_ACCEPT, 0 },
{ socket2, SC_ACCEPT, 0 },
{ socket3, SC_ACCEPT, 0 }

For any socket, you may check for one or more of the following conditions:

SC_ACCEPT
If a socket is listening for a connection, this condition indicates that another socket is in fact trying to connect to the listening socket.
SC_READ
If a socket is connected, this condition indicates that a packet of data has come in and is ready to be read.
SC_EXCEPTION
If a socket is connected, this condition indicates that the socket has figured out that something has gone wrong with its connection.
SC_URGENT
If a socket is connected, this condition indicates that it has received a packet of data that was marked urgent.
SC_WRITE
This condition indicates that data may be sent through the socket.

If you query a socket about a condition that does not apply to its current state (e.g., ask a non-listening socket if it is ready to accept), then SocketCheckReady() will return SE_IMPROPER_CONDITION.

This routine takes the following arguments:

requests
The array of SocketCheckRequests structures, as described above.
numRequests
Number of elements in the requests array.
timeout
Time to wait for success, in 1/60 second ticks, or SOCKET_NO_TIMEOUT to try as long as possible.

The routine returns the index of the first SocketCheckRequest to meet its conditions.

Include: socket.goh

SocketClose()

SocketError SocketClose(
        Socket 		s);

The SocketClose() routine closes a socket and frees it. Use it to close and free a datagram socket. It is the routine to call when responding to a SE_CONNECTION_CLOSED exception. SocketClose() may be used on a socket in any state, will shut down both outgoing and incoming data, cleans up, and frees the socket.

See Also: SocketCloseSend().

Include: socket.goh

SocketCloseDomainMedium()

SocketError 	SocketCloseDomainMedium(
        char 		*domain,
        MediumAndUnit 		*mau,
        Boolean		force);

This routine "hangs up" the hardware aspect of a socket connection. You may request that it only do so if there are no socket connections using the medium. If you force the medium closed with socket connections using the medium, the connections will receive no notification (they will just get SE_CONNECTION_RESET errors); therefore, you should force the medium closed only in extreme situations.

This routine takes the following arguments:

domain
Pointer to a string holding the domain name.
mau
Pointer to a MediumAndUnit structure specifying which medium to "hang up".
force
Zero to leave the medium alone if there are active connections using it; non-zero to force the medium closed even with active connections.

Include: socket.goh

SocketCloseSend()

SocketError SocketCloseSend(
        Socket 		s)

The SocketCloseSend() routine only works with connected sockets. (You should always use SocketClose() to close a datagram socket.) It partially shuts down the socket, so that the socket can no longer send data. SocketSend() will return SE_SOCKET_CLOSED if passed such a partially closed socket. However, you can still receive data via the socket--among other things, this means that you can use SocketRecv() to check for an SE_CONNECTION_CLOSED exception. SocketCloseSend() sends an SE_CONNECTION_CLOSED exception packet to the other side of the connection.

Include: socket.goh

SocketConnect()

SocketError SocketConnect(
        Socket 		s,
        SocketAddress 		*addr,
        int 		timeout);

To make a connection, call SocketConnect() . You will pass the socket created via SocketCreate() , the address to connect to, and a timeout value.

You cannot form a true connection using a datagram socket. However, you can use SocketConnect() with a datagram socket to specify a default destination address to use when sending packets.

This routine takes the following arguments:

s
The socket making the connection.
addr
The address to connect to. This must be a primitive address returned by SocketResolve() .
timeout
Time to wait for success, in 1/60 second ticks, or SOCKET_NO_TIMEOUT to try as long as possible.

The SocketConnect() routine returns a SocketError value. If the routine returns SE_NORMAL, then the connection was established successfully. If it returns SE_CONNECTION_REFUSED, the remote site wasn't prepared to accept the connection. If it returns SE_CONNECTION_ERROR, the connection didn't go through for some miscellaneous reason. If it returns SE_NON_UNIQUE_CONNECTION, then you're trying to create a connection that is identical to another connection: same source address, source port, destination address, destination port, and delivery type. There would be no way to tell these connections apart. If you try to open the same connection from both sides simultaneously, they may both fail with SE_NON_UNIQUE_CONNECTION.

Include: socket.goh

SocketCreate()

Socket SocketCreate(
        SocketDeliveryType 			delivery);

To create a new socket, call SocketCreate() . You will need to specify the socket's delivery type: SDT_DATAGRAM, SDT_SEQ_PACKET (sequential packets), or SDT_STREAM. If it returns zero, there was an error: call ThreadGetError() to get the error value, a SocketError .

Include: socket.goh

SocketGetAddressController()

ClassStruct * SocketGetAddressController(
        char 			*domain)

Given a domain name, you can create some UI gadgetry for choosing addresses within that domain. Call SocketGetAddressController() to get the class of an appropriate controller.

This routine takes a buffer containing the name of the domain for which to create a controller. It returns a ClassStruct pointer which may be passed to ObjInstantiate() .

For API information about address controllers, see XXX XREF Socket chapter XXX.

If the routine returns zero, there was an error. Call ThreadGetError() to find out what the error was (it will be a SocketError value). Look out for SE_UNKNOWN_DOMAIN.

See Also: SocketGetDomains(), ObjInstantiate().

Include: socket.goh

SocketGetAddressMedium()

SocketError SocketGetAddressMedium(
        SocketAddress 		*sa,
        MediumAndUnit 		*mau);

To find out which port would be used to connect to a given address, call SocketGetAddressMedium() . The socket need not actually be connected. Depending on the medium to be used, you might try to connect to the other site via some other domain or to not make the connection at all.

Include: socket.goh

SocketGetAddressSize()

int SocketGetAddressSize(
        char 		*domain);

To find out the maximum address data size for a given domain, call SocketGetAddressSize() . This can be helpful when allocating memory large enough to handle a full address.

This routine returns zero on error. Call ThreadGetError() to find out what the error was--if ThreadGetError() returns zero, then there wasn't really an error; this domain just doesn't require an address buffer.

Include: socket.goh

SocketGetDomainMedia()

ChunkHandle SocketGetDomainMedia(
        char 		*domain,
        optr 		mediaList);

If you're ever curious to know which sort of hardware ports the user's machine uses to connect up to a given domain, call SocketGetDomainMedia() . This returns a chunk array of MediumType structures. If you have a choice of domains to make a given connection, you might consider which medium would be used to make the connection.

Include: socket.goh

SocketGetDomains()

ChunkHandle SocketGetDomains(
        optr 	domainList);

The SocketGetDomains() routine takes the optr of a chunk array and fills in said array with elements, each of which contains a string, each string being the name of a supported domain. This chunk array has a standard ChunkArrayHeader header and variable sized elements.

If the return value is zero, there was an error. Call ThreadGetError() to find out what it was.

Include: socket.goh

SocketGetMediumAddress()

SocketError SocketGetMediumAddress(
        char 		*domain,
        bye 		*buffer,
        int 		*bufsize,
        MediumAndUnit 		*mau);

This routine determines the local address of the connection using a particular medium. If another machine were to connect to this machine via that medium, this is the address it would use.

This routine takes the following arguments:

domain
The domain name (a null-terminated string).
buffer
Buffer to hold the address information.
bufsize
Size of buffer, in bytes.
mau
The medium.

If the routine returns SE_NORMAL, then the address was computed successfully. The buffer buffer will be filled with as much of the address data as fit; the bufsize number will have changed to the total size of the address data--if this was larger than the size of buffer, then the address was truncated.

SocketGetPeerName()

SocketError SocketGetPeerName(
        Socket 		s,
        SocketAddress 		*addr);

If a socket is connected, call SocketGetPeerName() to get the address of the socket at the other end of the connection.

This routine takes the following arguments:

s
The connected socket to examine.
addr
Buffer holding hollow SocketAddress structure. The SA_domain , SA_domainSize , and SA_addressSize fields should be initialized. The buffer should be large enough to hold a SocketAddress structure and address data.

It returns SE_NORMAL (i.e., zero) if there was no error; otherwise it will return some SocketError value. The addr buffer will be filled in with address information. If either the domain name or address buffer isn't large enough to hold its string, the resulting truncated string will not be null-terminated.

Include: socket.goh

SocketGetSocketName()

SocketError SocketGetSocketName(
        Socket 		s,
        SocketAddress 		*addr);

To find out the address used to represent the socket's own side of the connection, call SocketGetSocketName() .

This routine takes the following arguments:

s
The connected socket to examine.
addr
Buffer holding hollow SocketAddress structure. The SA_domain , SA_domainSize , and SA_addressSize fields should be initialized. The buffer should be large enough to hold a SocketAddress structure and address data.

It returns SE_NORMAL (i.e., zero) if there was no error; otherwise it will return some SocketError value. The addr buffer will be filled in with address information. If either the domain name or address buffer isn't large enough to hold its string, the resulting truncated string will not be null-terminated.

Include: socket.goh

SocketGetIntSocketOption()

int 	SocketGetIntSocketOption(
        Socket		skt,
        SocketOption		opt);

This routine checks one of a socket's options, returning the current setting for that option. The return value's meaning depends upon the passed

This routine takes the following arguments:

skt
The socket to check.
opt
Which option to check. The following options are available:
SO_RECV_BUF: the maximum size of the socket's buffer for incoming data. A value of -1 means no limit.
SO_SEND_BUF: the maximum size of the socket's buffer for outgoing data. A value of -1 means no limit.
SO_INLINE: Non-zero if urgent data is forced into the same queue as regular data; zero to treat urgent data specially.
SO_NODELAY: Non-zero if domain driver should send all packets immediately; zero to allow the driver to save up packets to send as larger blocks.
SO_OWNER: Get the handle of the Socket's owning thread.

SocketInterrupt()

SocketError SocketInterrupt(
        Socket 		s);

This routine attempts to interrupt a SocketAccept() or a SocketRecv() pending on the passed socket s.

SocketInterruptResolve()

SocketError SocketInterruptResolve(
        TCHAR 		*domain,
        byte 		*address,
        int  		addrSize);

This routine attempts to interrupt the resolution of the specified address.

This routine takes the following arguments:

domain
The name of the domain.
address
The address which is being resolved.
addrSize
The size of the address buffer.

Warnings: This function is unreliable. It may not interrupt the desired resolve call,if it executes before the resolve call has begun to be processed. If may also cause other, parallel resolve in the same domain to be interrupted.

SocketListen()

SocketError SocketListen(
        Socket 		s,
        int 		qSize);

To listen for incoming sequenced packet or stream connections, call SocketListen() . The socket must be bound to a port before it can listen. You may allow more than one connection through the port-specify the maximum number of connections as an argument to SocketListen() .

This routine takes the following arguments:

s
The Socket which should listen for incoming connections. The socket should be bound to a port.
qSize
The number of incoming connections which may be accepted via the socket.

The SocketListen() routine causes the passed socket to "listen" for accepting connections. If another socket is already listening at the port, the SocketListen() routine will return SE_PORT_ALREADY_LISTENING.

See Also: SocketBind(),
SocketAccept(),
SocketCheckReady().

Include: socket.goh

SocketOpenDomainMedium()

SocketError SocketOpenDomainMedium(
        SocketAddress 		*addr,
        int 		timeout);

This routine opens a "raw" connection using the specified medium. Exactly what is meant by a "raw" connection depends upon the domain involved. In the case of TCP, this routine will dial the modem and create a PPP connection to the PPP server--but will not send any TCP packets.

If there is already a "raw" connection, this routine will return SE_NORMAL, just as if it had just opened the connection.

If the device's phone is busy, this routine will return SE_MEDIUM_BUSY.

This routine takes the following arguments:

addr
The address to connect to.
timeout
How many 1/60 second ticks to wait for a connection. Pass SOCKET_NO_TIMEOUT to wait forever.

When done with the connection, close it with SocketCloseDomainMedium() .

See Also: SocketCloseDomainMedium().

Include: socket.goh

SocketRecv()

int SocketRecv(
        Socket 		s,
        void 		*buffer,
        int 		bufSize,
        int 		timeout,
        SocketRecvFlags 		flags,
        SocketAddress 		*addr);

The SocketRecv() routine receives data from the socket. After filling the passed buffer with data, the socket will discard its copy of the received data to make room for the next piece of incoming data.

To "peek" at the next packet of incoming data, call SocketRecv() and pass the SRF_PEEK flag. This allows you to get the size of the next packet of incoming data and even to non-destructively look at the contents of that packet.

When using packet-based delivery type sockets (sequenced packet or datagram delivery sockets), you can only receive whole packets-if you grab data from the socket, but don't grab all the data in the packet, the rest of the data will be lost. Thus, it's a good idea to establish a maximum packet size for sockets using these delivery types.If you're not sure how much room you'll need to receive a packet, call SocketRecv() with the SRF_PEEK flag-this allows you to "peek" at the incoming data without causing the socket to discard its copy.

The SocketRecv() routine takes the following arguments:

s
The socket from which to grab the data.
buffer
Buffer to fill with data.
bufSize
Size of the data buffer. If this smaller than the amount of data, and the socket delivery type is packet-based, the data that doesn't fit in the buffer will be lost; if the socket is a stream socket, then socket will retain the remaining data.
timeout
How many 1/60 second ticks to wait for incoming data. Pass SOCKET_NO_TIMEOUT to wait forever.
flags
This flags field allows you to specify some options. Set the SRF_ADDRESS flag if you want the address of the sending machine in addr . Set the SRF_URGENT flag to only receive a packet marked urgent. Set the SRF_PEEK flag to "peek" at the incoming packet, leaving it in the socket.
addr
If you pass the SRF_ADDRESS_FLAG, then pass an empty SocketAddress buffer. Make sure the buffer has room for the address data after the SocketAddress structure. The structure's SA_domain , SA_domainSize , and SA_addressSize fields should be initialized. If either the domain or address buffer isn't long enough to hold its string, the resulting truncated string will not be null-terminated.

The SocketRecv() routine returns the size of the received buffer. If this size is zero, there may be an error in the connection.

Include: socket.goh

SocketRemoveLoadOnMsg()

SocketError SocketRemoveLoadOnMsg(
        SocketPort p);

If you've requested the Socket library to load a program when it senses packets coming to a port, you can undo that request by calling SocketRemoveLoadOnMsg() .

See Also: SocketAddLoadOnMsg().

Include: socket.goh

SocketRemoveLoadOnMsgInDomain()

SocketError SocketRemoveLoadOnMsgInDomain(
        SocketPort 		p,
        TCHAR 		*domain);

If you've requested the Socket library to load a program when it senses packets coming to a port in a given domain, you can undo that request by calling SocketRemoveLoadOnMsgInDomain() .

See Also: SocketAddLoadOnMsgInDomain().

Include: socket.goh

SocketResolve()

int SocketResolve(
        char 		*domainName,
        byte 		*rawAddr,
        int 		addrSize,
        byte 		*result,
        int 		resultBuffSize );

You cannot use an address with other Socket library routines unless that address is in its most primitive form. This primitive form is created by taking a higher-level form of the address and passing it to the SocketResolve() routine. You will recall that addresses returned by the address controller will not be in their most primitive form. The primitive form of an address can be volatile: feel free to re-use them if making several connections over a short period of time, but re-resolve the address if it's been a long time since it was last resolved. Note that there can be a large overhead when it comes to resolving addresses.

This routine takes the following arguments:

domainName
Buffer containing null-terminated domain name string.
rawAddr
Buffer containing the unresolved form of the address. Normally, this will be the buffer at the end of a SACAddress returned by a SocketAddressControl in response to MSG_SOCKET_ADDRESS_CONTROL_GET_ADDRESSES .
If you aren't resolving an address from a SocketAddressControl, but in fact need to construct a raw address from scratch, see the documentation for SACAddress .
addrSize
Size of the rawAddr buffer, normally the SACA_opaqueSize field from a SACAddress returned by a SocketAddressControl in response to MSG_SOCKET_ADDRESS_CONTROL_GET_ADDRESSES .
result
Buffer to hold the resolved address. This will normally be a buffer trailing a SocketAddress structure.
resultBufferSize
Size of the result buffer.

It returns the size of the resolved address. If it returns zero, there was an error. Call ThreadGetError() to find out what the error was. Typical errors to look out for are SE_BUFFER_TOO_SMALL, SE_DESTINATION_UNREACHABLE, and SE_TEMPORARY_ERROR.

Include: socket.goh

SocketSend()

SocketError SocketSend(
        Socket 		s,
        void 		*buffer,
        int 		bufSize,
        SocketSendFlags 		flags,
        SocketAddress 		*addr);

The SocketSend() routine sends data over a socket. It takes several arguments:

s
The socket through which to send the data
buffer
A buffer containing the data to send
bufSize
The size of the data buffer.
flags
Options to use when sending. Set SSF_ADDRESS if you're passing an address in addr . Set SSF_URGENT to mark the packet urgent.
addr
Address to which to send data.
If the socket is a sequenced packet or stream delivery type socket, then this optional address will be ignored.
If the socket is a datagram delivery type socket, specify the destination address of the packet here unless the socket has a default destination address as described below.
Datagram sockets can use the SocketConnect() routine to specify a default destination address. If they have no default address, then you must specify the destination address for the packet here. If there is a default destination address and you pass this optional address and those addresses do not match, then the Socket library will generate a fatal error.

Normally, SocketSend() will return SE_NORMAL. If it detects an error, it may return some other SocketError value.

Include: socket.goh

SocketSetIntSocketOption()

void	SocketSetIntSocketOption(
        Socket		skt,
        SocketOption		opt,
        int		newval);

This routine changes the setting for one of a socket's options.

This routine takes the following arguments:

skt
The socket.
opt
Which option to change. The following options are available:
SO_RECV_BUF: the maximum size of the socket's buffer for incoming data. A value of -1 means no limit.
SO_SEND_BUF: the maximum size of the socket's buffer for outgoing data. A value of -1 means no limit.
SO_INLINE: Non-zero if urgent data is forced into the same queue as regular data; zero to treat urgent data specially.
SO_NODELAY: Non-zero if domain driver should send all packets immediately; zero to allow the driver to save up packets to send as larger blocks.
SO_OWNER: Specify a new owning thread for the Socket.
newval
The new setting for the option. The meaning of this value depends on which option you are changing.

Include: socket.goh

SoundAllocMusic()

word	SoundAllocMusic( /* Returns a SOUND_ERROR_ value */
        const word 		*song,
        word 		voices,
        MemHandle		*control );

This routine takes a pointer to a fixed buffer of music and allocates a MemHandle which may then be passed to SoundPlayMusic() to play the music. The handle is returned in the address specified by control . SoundAllocMusic() returns a non-zero value if it encounters an error trying to allocate the music block.

If the music buffer is in a movable resource, you must initialize it using SoundInitMusic() instead of SoundAllocMusic() . The voices argument is the number of voices in the buffer.

SoundAllocMusicNote()

word SoundAllocMusicNote( /* Returns a SOUND_ERROR_... value. */
        InstrumentPatch				instrument,
        word				instTable,
        word 				frequency,
        word 				volume,
        SoundStreamDeltaTimeType				DeltaType,
        word 				duration,
        MemHandle				*control);

This routine allocates a MemHandle which may be passed to SoundPlayMusicNote() . You must provide all information about the note: its frequency, volume, and duration.

To play the note using a standard instrument, pass the instrument's value in instrument and zero in instTable . To use a custom instrument, instTable : instrument should be a pointer to the instrument.

Specify the frequency in Hertz or use one of the constants such as MIDDLE_C_b to specify a standard note frequency. Volume ranges from zero to 0xffff--you may wish to use a constant value such as DYNAMIC_FFF if you want help trying to choose a loudness. The note's duration is determined by its delta type, one of SSDTT_MSEC, SSDTT_TICKS, and SSDTT_TEMPO. If you pass SSDTT_MSEC or SSDTT_TICKS, the duration is measured in milliseconds or ticks (each tick is one sixtieth of a second). If you pass SSDTT_TEMPO, you may set the size of your time unit when you call SoundPlayMusicNote() . The duration determines how many time units the note should play. If the delta type is SSDTT_TICKS and duration is 30, then the note will sound for half a second.

The handle of the newly allocated note will be stored at the location specified by control . SoundAllocMusicNote() returns non-zero if there is an error allocating the note.

SoundAllocMusicStream()

word	SoundAllocMusicStream( /* Returns a SOUND_ERROR_... value */
        word 		streamType,
        word 		priority,
        word 		voices,
        word 		tempo,
        MemHandle		*control);

This routine allocates a handle suitable for passing to SoundPlayToMusicStream() . The handle will be returned at the address specified by control. SoundAllocMusicStream() returns a non-zero value if there is an error allocating the handle.

It is passed several arguments. The SoundStreamType determines how much space to allocate for the stream and will determine how much data can be written to the stream at one time. If you pass SST_ONE_SHOT, it indicates that the stream will not be explicitly destroyed, and that your stream should destroy the stream when the song is done. You must specify how many voices there are in the music buffer. You must also pass a starting tempo for the music stream.

SoundAllocSampleStream()

word SoundAllocSampleStream( /* Returns a SOUND_ERROR_... value */
        MemHandle		*control);

This routine allocates a sample stream handle. If the returned handle is null , the library was unavailable (i.e. some other thread has grabbed exclusive access).

SoundDisableSampleStream()

void 	SoundDisableSampleStream(
        MemHandle 		mh);

This routine disassociates the DAC player from the passed sample handle. Before you play more sounds using the handle, you will have to call SoundEnableSampleStream() again.

SoundEnableSampleStream()

word	SoundEnableSampleStream( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh,
        SoundPriority		priority,
        word 		rate,
        word 		manufacturerID,
        DACSampleFormat		format);

This routine associates a DAC player with the allocated sample handle. You must pass the sound handle, as returned by SoundAllocSampleStream() . You must also pass certain pieces of information about the sound you will be playing on the DAC device: the priority with which to grab the DAC player (e.g. SP_STANDARD), the sampling rate, and the format of the sample (as identified by a manufacturerID and a DACSampleFormat value).

SoundFreeMusic()

void 	SoundFreeMusic(
        MemHandle 		mh);

This routine frees up a music handle. The music must not be playing; call SoundStopMusic() if you are not sure. You may not use the music handle after calling this routine on it.

SoundFreeMusicNote()

void SoundFreeMusicNote(
        MemHandle 		mh);

This routine frees up the passed note handle. The note must not be playing when you call this routine; call SoundStopMusicNote() if you are not sure. You should not try to use the note's handle after freeing it.

SoundFreeMusicStream()

void SoundFreeMusicStream(
        MemHandle 		mh);

This routine frees up the music stream's token. No music must be playing via the stream; call SoundStopMusicStream() if you are not sure. Do not try to use the stream after calling this routine on it.

SoundFreeSampleStream()

void 	SoundFreeSampleStream(
        MemHandle 		mh);

This routine frees the passed sampled sound handle. You must not try to use this handle after calling this routine on it.

SoundGetExclusive()

void	SoundGetExclusive(void);

This routine grabs the exclusive semaphore for the sound library; if another thread has already grabbed the exclusive, this routine will wait until the exclusive is released. Sounds which are playing now will be permitted to finish, but from now on, only the thread calling this routine will be allowed to play new sounds. When done with the sound library exclusive, call SoundReleaseExclusive() .

SoundGetExclusiveNB()

Boolean	SoundGetExclusiveNB(void);

This routine grabs the exclusive semaphore for the sound library, doing so even if some other thread has already grabbed the exclusive. Sounds which are playing now will be permitted to finish, but from now on, only the thread calling this routine will be allowed to play new sounds. This routine will return true if another thread already has exclusive access.

When done with the sound library exclusive, call SoundReleaseExclusive() .

SoundInitMusic()

void SoundInitMusic(
        MemHandle 		mh,
        byte 		voices);

This routine initializes a pre-defined simple music buffer structure. If the music buffer is stored in a fixed block, you can call SoundAllocMusic() instead. This allows a music buffer stored in a block referenced by a pointer to be playable using SoundPlayMusic() .

SoundPlayMusic()

word	SoundPlayMusic( /* Returns SOUND_ERROR_... value */
        MemHandle 		mh,
        word		priority,
        word 		tempo,
        EndOfSongFlags		flags);

This routine plays a buffer of music previously initialized by SoundInitMusic() or allocated by SoundAllocMusic() . The priority value will determine whether your sound will play if other sounds are already occupying the voices--pass a value such as SP_STANDARD. The tempo value will be used to determine the length of a 1/128th note. If your music buffer contained any notes whose lengths were measured by SSDTT_TEMPO delta type, then you should set this value accordingly. The flags argument determines whether the music's handle should be automatically freed when the sound is done playing. You may pass either or both of the flags UNLOCK_ON_EOS or DESTROY_ON_EOS.

Include: sound.h

SoundPlayMusicNote()

word 	SoundPlayMusicNote( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh,				/* handle of note */
        word 		priority,
        word		tempo,
        EndOfSongFlags		flags);

This routine plays a buffer of music previously allocated by SoundAllocMusicNote() --the return value of that function is passed as mh . The priority value will determine whether your sound will play if other sounds are already occupying the voices--pass a value such as SP_STANDARD. The tempo value will be used to determine the length of a 1/128th note. If your note's delta type is SSDTT_TEMPO, then you should set this value accordingly. The flags argument determines whether the notes's handle should be automatically freed when the note is done playing. You may pass either or both of the flags UNLOCK_ON_EOS or DESTROY_ON_EOS.

This routine returns true if the library was unavailable (i.e. if some other thread had grabbed the sound exclusive).

Include: sound.h

SoundPlayToMusicStream()

word	SoundPlayToMusicStream( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh,
        const word		* song,
        word 		size);

This routine plays a music buffer to a stream. Specify which stream to play to by means of the token returned by SoundAllocMusicStream() . To play music to the buffer, pass the size of the buffer you are playing and a pointer to the start of the piece. This piece of buffer must be made up of whole events--it should not start or end in the middle of an event (e.g. you can't specify that you want to play a note but not give its frequency, even if you plan to play another buffer to the stream that might begin with a frequency).

If you don't know the size of the buffer, it may be all right--any data in the buffer after the GE_END_OF_SONG will be ignored.

SoundPlayToMusicStreamNB()

word	SoundPlayToMusicStreamNB( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh,
        const word		* song,
        word 		size,
        word		*bytesWritten);

This routine acts as SoundPlayToMusicStream() , except in the case where the routine would block. If the music stream fills up, SoundPlayToMusicStream() blocks, waiting until there is more room in the stream; SoundPlayToMusicStreamNB() will instead exit, returning the number of bytes successfully written to the stream.

SoundPlayToSampleStream()

word SoundPlayToSampleStream( /* Returns a SOUND_ERROR_... value */
        MemHandle 				mh,
        word _far				* sample,
        word 				size,
        SampleFormatDescription				* format);

This routine passes sampled sound data to a DAC player. You must pass a sample sound handle to this routine--to acquire such a handle, call SoundAllocSampleStream() . The sample sound handle must be associated with a DAC player--to so associate the handle, call SoundEnableSampleStream() . You must pass a pointer to the sample data, along with the size of the sample as measured in bytes. You may change the format information which will determine how the DAC player handles the data.

SoundReallocMusic()

word 	SoundReallocMusic( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh,
        word _far 		* song);

This routine allows you to associate a new music buffer with an existing music handle. The new music buffer must not have more voices than was originally requested with SoundAllocMusic() . Do not call this routine with the handle of a sound that may be playing; call SoundStopMusic() on the handle if you are not sure.

SoundReallocMusicNote()

word 	SoundReallocMusicNote( /* Returns a SOUND_ERROR_... value */
        MemHandle 				mh,
        word 				freq,
        word 				vol,
        SoundStreamDeltaTimeType				timer,
        word 				durat,
        word				instrum,
        word				instTable);

This routine allows you to associate new note values with an existing note handle. Do not call this routine with the handle of a note that may be playing; call SoundStopMusicNote() on the handle if you are not sure.

SoundReleaseExclusive()

void	SoundReleaseExclusive(void);

This routine releases the sound library exclusive semaphore. You will not need to call this routine unless your code calls SoundGetExclusive() or SoundGetExclusiveNB() . This routine allows other threads to play sounds. If another thread called SoundGetExclusive() while your thread had the exclusive, it will now grab the exclusive.

SoundSampleDriverInfo()

void 	SoundSampleDriverInfo(
        word _far 					*voices,
        SoundDriverCapability _far 					*capability);

This routine determines the machine's ability to play recorded sounds.

SoundStopMusic()

word 	SoundStopMusic( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh); 		/* Handle of music buffer */

This routine stops the playing of a simple music buffer. It returns true if the library was unavailable (i.e. some other thread has grabbed the exclusive).

SoundStopMusicNote()

word 	SoundStopMusicNote( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh);

This routine stops a note that is playing. Pass the handle of the note, as was returned by SoundAllocMusicNote() . This routine returns true if the sound library was unavailable (i.e. some other thread has grabbed the exclusive).

SoundStopMusicStream()

word 	SoundStopMusicStream( /* Returns a SOUND_ERROR_... value */
        MemHandle 		mh);

This routine stops any music being played to the stream. All sounds are flushed from the stream. It takes one argument, the token of the sound stream, as returned by SoundAllocMusicStream() .

SoundSynthDriverInfo()

void 	SoundSynthDriverInfo(
        word _far 					*voices,
        SupportedEnvelopeFormat _far 					*format,
        SoundDriverCapability  _far 					*capability)

This routine describes the machine's music playing ability.

SpeedDialGetNumber()

int 	SpeedDialGetNumber(
        int		slot,
        TCHAR		*buf);

This function gets the phone number associated with a Speed Dial number. The function fills in the passed buf string with the phone number. If there is no such Speed Dial number, the function returns -1.

Include: contdb.goh

SpeedDialGetRecord()

int 	SpeedDialGetRecord(
        int		slot,
        RecordID 		*rid,
        FieldID		*fid);

This function returns the Contact and field corresponding to the passed Speed Dial number. If there is no such Speed Dial number, the function returns -1. This function takes the following arguments:

slot
The Speed Dial number.
rid
Pointer to a RecordID buffer. The function will fill this in with the RecordID of the Contact record.
fid
Pointer to a FieldID buffer. The function will fill this buffer in with the FieldID of the contact's field with the selected phone number.

Include: contdb.goh

SpeedDialGetSlot()

int 	SpeedDialGetSlot(
        RecordID 		rid,
        FieldID		fid);

This function gets the Speed Dial number for the Contact phone number represented by the passed RecordID and FieldID numbers. If there is no such Speed Dial number, the function returns -1.

Include: contdb.goh

SpeedDialSetRecord()

int 	SpeedDialSetSlot(
        int 		slot,
        RecordID 		rid,
        FieldID		fid);

This function sets the Speed Dial number for the Contact phone number represented by the passed RecordID and FieldID numbers. If that Speed Dial slot was not previously empty, the function returns -1.

Include: contdb.goh

SpoolConvertPaperSize()

word 	SpoolConvertPaperSize(
        word		width,			/* width of paper */
        word		height,			/* height of paper */
        PageType 		pageType);			/* type of page */

This routine converts a width and height into a page size number.

Include: spool.goh

SpoolCreatePaperSize()

word	SpoolCreatePaperSize( /* Returns zero if failed */
        char	*paperSizeString,		/* descriptive name MAX_PAPER_STRING_LENGTH */
        word	width,		/* width of paper */
        word	height,		/* length of paper */
        PageLayout laytout,			/* page layout */
        PageType pageType);			/* page type */

This routine defines and stores a new paper size for later use by the user. If successful, it will return a number representing the new paper size, a value between 128 and 255; if not successful, it will return zero.

Include: spool.goh

SpoolCreatePrinter()

word	SpoolCreatePrinter( 	/* Returns printer number, or -1 if
                  there was an error */
        char 		*printerName,				/* name of printer */
        PrinterDriverType 		type); 				/* device type */

Adds the printer to the list of currently installed printers and returns the new printer number. This routine is normally called from within the Preferences manager. If the routine returns 0xffff, then the printer already exists and thus could not be created.

Include: spool.goh

SpoolDelayJob()

SpoolOpStatus 	SpoolDelayJob(
        word 	JobID);

This routine moves a print job to the end of the print queue.

SpoolDeletePaperSize()

Boolean 	SpoolDeletePaperSize(
        word 	paperSizeNum,		/* size number to delete */
        PageType pageType);			/* page type */

This routine deletes a user-defined paper size. It will return true (i.e., non-zero) if there was an error.

Include: spool.goh

SpoolDeletePrinter()

void 	SpoolDeletePrinter(
        int 	prtrNum);		/* printer number to delete */

Deletes the requested printer from the system.

Include: spool.goh

SpoolDelJob()

SpoolOpStatus 	SpoolDelJob(
        word jobID);

This routine deletes a printer job.

SpoolGetDefaultPageSizeInfo()

void	SpoolGetDefaultPageSizeInfo(
        PageSizeReport		*psr);

This routine gets the dimensions of the system default page size.

Include: spool.goh

SpoolGetDefaultPrinter()

int 	SpoolGetDefaultPrinter(); /* Returns printer number */

Returns the system-default printer, which is used (for example) by the PrintControlClass as the default printer to print to.

Include: spool.goh

SpoolGetNumPaperSizes()

void	SpoolGetNumPaperSizes(
        NumPaperSizesInfo		*sizesInfo,
        PageType 		type);

Use this routine to find the number of paper sizes, both pre-defined and user-defined, that should appear in a paper size list. The number of paper sizes, along with the index of the default paper size will be returned in a Num

Structures:

        typedef struct {
                word    NPSI_numSizes;
                word    NPSI_defaultSize;
        } NumPaperSizesInfo;

Include: spool.goh

SpoolGetNumPrinters()

word	SpoolGetNumPrinters(
        PrinterDriverType		type,				   /* driver type */
        byte		localOnlyFlag); /* non-zero if only
                                * locally-defined printers should
                                * be counted. */

This routine returns the number of installed printers with the given type.

Include: spool.goh

SpoolGetPaperSize()

void 	SpoolGetPaperSize(
        PaperSizeInfo		sizeInfo, 			/* This must be between 0 and the return
                          * value of SpoolGetNumPaperSizes() */
        word		paperSizeNum,	 		/* a paper size number between
                                0 and SpoolGetNumPaperSizes() */
        PageType 		*pageType);			/* page type*/

Use this routine to determine the dimensions of a paper size.

Structures:

        typedef struct {
            word        PSI_width;          /* width, in points */
            word        PSI_height;         /* height, in points */
            PageLayout  PSI_defaultLayout;
        } PaperSizeInfo;

Include: spool.goh

SpoolGetPaperSizeOrder()

void	SpoolGetPaperSizeOrder(
        byte 		*order, 			/* buffer of size MAX_PAPER_SIZES */
                        /* On return, this buffer will be
                         * filled with the page size numbers
                         * arranged in the order
                         * corresponding to their display */
        byte 		*userSizes,		 	/* buffer of size MAX_PAPER_SIZES */
                        /* On return, will hold ordered
                         * array of user paper sizes. */
        PaperSizeOrderInfo		*numBuf,			/* empty PaperSizeOrderInfo struct */
        PageType                    pageType);  /* What sort of paper we're interested in */

This routine retrieves the paper size order array.

Structures:

        typedef struct {
            word    PSOI_numOrdered;    /* number of ordered sizes */
            word    PSOI_numUnused;     /* number of unused sizes */
        } PaperSizeOrderInfo;

Include: spool.goh

SpoolGetPaperString()

word	SpoolGetPaperString( /* true if error*/
        char	   *stringBuf,		/* empty buffer MAX_PAPER_STRING_LENGTH */
        word	   paperSizeNum,		/* returned name string */
        PageType	  prtrNum);		/* printer number */

Use this routine to determine the string to be displayed for a specific paper size. Upon return, stringBuf will have been filled with a null-terminated string describing the paper. The routine's return value is the length of the paper string, not including the null terminator.

Include: spool.goh

SpoolGetPrinterString()

Boolean	SpoolGetPrinterString( /* Returns true if error */
        PrinterStringInfo	   *retInfo,					/* pass empty structure */
        word	  	printerNum);				/* printer number */

This routine fills a buffer with the requested null-terminated printer name string. If the printer could not be found, the return value will be true (set for error).

Include: spool.goh

SpoolHurryJob()

SpoolOpStatus 	SpoolHurryJob(
        word 	JobID);

This routine moves a print job to the head of the print queue.

SpoolInfo()

SpoolOpStatus 	SpoolInfo(
        SpoolInfoType 		infoType,
        SpoolInfoParams 		*params);

This routine returns various types of information about the print queue's status. You may request information about the queue or about any job on the queue.

Structures:

        typedef enum /* word */ {
            SIT_JOB_INFO    = 0,
            SIT_QUEUE_INFO  = 2
        } SpoolInfoType;

The passed SpoolInfoType specifies whether you seek information about the print queue or a single job on the queue.

        typedef union {
            SpoolInfoQueueParams    SIP_queueParams;
            SpoolInfoJobParams      SIP_jobParams;
        } SpoolInfoParams;

The type of parameters used depend on the passed SpoolInfoType .

        typedef struct {
            Boolean 			SIQP_activityQuery;
            PrintPortInfo 			*SIQP_portInfo;
            MemHandle 			SIQP_retBlock;
            word 			SIQP_retNumJobs;
        } SpoolInfoQueueParams;

This structure contains information about the print queue and the SIQP_retBlock field points to a block of JobStatus structures.

        typedef struct {
            word        SIJP_jobID;
            MemHandle   SIJP_retBlock;
        } SpoolInfoJobParams;

This structure is used to pass the ID of the job to describe. The passed block will hold la JobStatus on return.

        typedef struct {
            char JS_fname[13]; /* std DOS (8.3) spool filename */
            char JS_parent[FILE_LONGNAME_LENGTH+1]; /* parent app's name */
            char JS_documentName[FILE_LONGNAME_LENGTH+1]; /* document name */
            word JS_numPages;                   /* # pages in document */
            SpoolTimeStruct JS_time;            /* time spooled */
            byte JS_printing;                   /* TRUE/FALSE if we are printing */
        } JobStatus;

Each JobStatus structure describes a single print job.

SpoolModifyPriority()

SpoolOpStatus 	SpoolModifyPriority(
        word 		jobID,
        ThreadPriority 		priority);

Use this routine to modify the priority of the thread that processes a print job's print queue.

SpoolSetDefaultPageSizeInfo()

void	SpoolSetDefaultPageSizeInfo(
        PageSizeReport		*psr);

Set the dimensions of the default system page.

Include: spool.goh

SpoolSetDefaultPrinter()

void 	SpoolSetDefaultPrinter(
        int prtrNum);		/* printer number */

Sets the system-default printer, used (for example) by PrintControlClass as the default printer. This routine is normally called from within the Preferences manager.

Include: spool.goh

SpoolSetDocSize()

void 	SpoolSetDocSize(
        Boolean 		open;			/* false if document is closed */
        PageSizeInfo 		* psr);			/* NULL if document is closed */

This routine tells the application's PageSizeControl object the document's size.

Include: spool.goh

SpoolSetPaperSizeOrder()

void 	SpoolSetPaperSizeOrder(
        byte	*orderArray, 		/* Array of paper siaes. Each element is
                 * a byte signifying:
                 * 0-127  : a pre-defined paper size, or
                 * 128-255: a user-defined paper size. */
        word 	number, 		/* number of entries in array */
        PageType pageType);			/* page type (paper, envelope, label) */

This routine resets the order in which paper sizes are displayed to the user.

Include: spool.goh

SpoolVerifyPrinterPort()

SpoolOpStatus SpoolVerifyPrinterPort(
        PrintPortInfo *portInfo);

Use this routine to check for the existence of a printer port

SpreadsheetInitFile()

VMBlockHandle SpreadsheetInitFile(
        const SpreadsheetInitFileData					* ifd);

This routine initializes a VM file for use by the spreadsheet object. It allocates a spreadsheet map block in the file and initializes this block. The routine returns the map block's handle; applications will need to remember this handle. It does not change any existing blocks in the VM file.

The ifd parameter is pointer to a SpreadsheetInitFileData structure containing the file handle and the number of rows and columns to allocate.

Structures: The SpreadsheetInitFileData structure is defined as follows:

typedef struct {
    word				SIFD_file;
    word				SIFD_numRows;
    word 				SIFD_numCols;
    SpreadsheetDrawFlags				SIFD_drawFlags;
} SpreadsheetInitFileData;
/* SpreadsheetDrawFlags:
 * SDF_DRAW_GRAPHICS
 * SDF_DRAW_NOTE_BUTTON
 * SDF_DRAW_HEADER_FOOTER_BUTTON
 * SDF_DRAW_GRID				*/

Include: ssheet.goh

StreamClose()

StreamError StreamClose (
        GeodeHandle 		driver,
        StreamToken 		stream,
        Boolean 		linger);

This routine shuts down a stream. It is passed the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
linger
Set true (i.e., non-zero) if the data currently in the stream should be kept until it's read; set false to flush the data immediately.

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

StreamFlush()

StreamError StreamFlush (
        GeodeHandle 		driver,
        StreamToken 		stream);

This routine flushes all the data pending in a stream. It is passed the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

StreamGetError()

StreamError	 	StreamGetError (
        Handle 		driver,
        StreamToken 		stream,
        StreamRoles 		roles,
        word *		errorCode);

This routine returns any driver-specific error codes set by the last call to a stream routine. It takes the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
role
If this is STREAM_ROLES_WRITER, the routine will return the last error from a write operation. If it is STREAM_ROLES_READER, it will return the last error from a read operation.
errorCode
A pointer to a word-sized variable. The device's error code is written to this variable.

If the routine is successful, it returns zero and writes the device's last error code into the errorCode variable. If it is unsuccessful, it returns a member of the StreamError enumerated type.

StreamOpen()

StreamError	 StreamOpen (
        GeodeHandle 		driver,
        word 		buffSize,
        GeodeHandle		owner,
        HeapFlags 		heapFlags,
        StreamToken *		stream);

This routine opens a stream. It is passed the following:

driver
The GeodeToken of the stream driver.
buffSize
The size of the stream buffer, in bytes.
owner
The geode which will own the stream.
heapFlags
The flags for the creation of the buffer block.
*stream
The stream token will be written here.

If StreamOpen() is successful, it returns zero and writes the stream's token to *stream . If it is unsuccessful, it returns a member of the StreamError enumerated type.

StreamQuery()

StreamError StreamQuery (
        GeodeHandle 		driver,
        StreamToken 		stream,
        StreamRoles 		role,
        word *		bytesAvailable);

This routine finds out either how much free space is available in a stream's buffer, or how much data is waiting to be read. It is passed the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
role
If this is STREAM_ROLES_WRITER, the routine will return the amount of free space available in the stream buffer. If it is STREAM_ROLES_READER, it will return the amount of data waiting to be read.
*bytesAvailable
The routine will write the number of bytes available (for writing or reading) to this variable.

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

StreamRead()

StreamError StreamRead (
        GeodeHandle 		driver,
        StreamToken 		stream,
        StreamBlocker 		blocker,
        word 		buffSize,
        byte *		buffer,
        word *		numBytesRead);

This routine reads data from a stream. The routine takes the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
blocker
Specify whether to block if there is not enough data waiting to be read.
buffsize
Size of passed buffer (i.e. amount of data to read from stream).
buffer
Pointer to buffer where data from stream will be written.
*numBytesReadRead
StreamRead() will write to this variable the number of bytes actually read from the stream.

If StreamRead() is successful, it returns zero. If it is unsuccessful, or could not read all the data requested from the stream, it returns a member of the StreamError enumerated type.

StreamReadByte()

StreamError StreamWriteByte (
        GeodeHandle 		driver,
        StreamToken 		stream,
        StreamBlocker 		blocker,
        byte *		dataByte);

This routine reads a single byte from a stream. It takes the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
blocker
Specify whether to block if there is not enough room to write the data.
*dataByte
Read a byte from the stream, and write it to this variable.

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

StreamSetError()

StreamError StreamSetError(
        Handle 		driver,
        StreamToken 		stream,
        StreamRoles 		roles,
        word 		errorCode);

This routine sets the device-specific error code for a stream; this code can later be recovered by StreamGetError() . It takes the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
role
Either STREAM_ROLES_WRITER or STREAM_ROLES_READER.
errorCode
A 16-bit device-specific error code.

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

StreamWrite()

StreamError StreamWrite (
        GeodeHandle 		driver,
        StreamToken 		stream,
        StreamBlocker 		blocker,
        word 		buffSize,
        const byte *		buffer,
        word *		numBytesWritten);

This routine writes data to a stream. The routine takes the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
blocker
Specify whether to block if there is not enough room to write all the data.
buffsize
Size of passed data buffer (i.e. amount of data to write to stream).
buffer
Pointer to data to write to stream.
*numBytesWritten
StreamWrite() will write to this variable the number of bytes actually written to the stream.

If StreamWrite() is successful, it returns zero. If it is unsuccessful, or could not write all the data to the stream, it returns a member of the StreamError enumerated type.

StreamWriteByte()

StreamError StreamWriteByte (
        GeodeHandle 		driver,
        StreamToken 		stream,
        StreamBlocker 		blocker,
        byte 		dataByte);

This routine writes a single byte to a stream. It takes the following arguments:

driver
The GeodeToken of the stream driver.
stream
The StreamToken of the stream.
blocker
Specify whether to block if there is not enough room to write the data.
dataByte
Write this byte to the stream.

If the routine is successful, it returns zero. If it is unsuccessful, it returns a member of the StreamError enumerated type.

SysGetConfig()

dword	SysGetConfig();

This routine returns a set of values defining the system configuration. The returned dword contains four byte values, listed below from least significant byte to most significant byte:

configuration flags
This byte contains a record of SysConfigFlags reflecting the system status. This record includes information on how the system was started, whether Swat is running it, whether the system was restarted, etc.
reserved byte
This byte contains reserved information unusable by applications.
processor type
This byte contains a value reflecting the processor type of the machine running GEOS. This is of type SysProcessorType and is one of SPT_8088, SPT_8086, SPT_80186, SPT_80286, SPT_80386, or SPT_80486. Use the macro SGC_PROCESSOR() to extract this value from the returned dword.
machine type
This byte contains a value of SysMachineType indicating the type of the machine running GEOS. It may be one of the following values: SMT_UNKNOWN, SMT_PC, SMT_PC_CONV, SMT_PC_JR, SMT_PC_XT, SMT_PC_XT_286, SMT_PC_AT, SMT_PS2_30, SMT_PS2_50, SMT_PS2_60, SMT_PS2_80, or SMT_PS1. Use the macro SGC_MACHINE() to extract this value from the returned dword.

Include: system.h

SysGetDosEnvironment()

Boolean	SysGetDosEnvironment( /* true if error (not found) */
        const char		* variable,				/* environment variable */
        char		* buffer,				/* buffer for return value */
        word		bufSize);				/* maximum return string length */

This routine looks up a specified DOS environment variable in the environment buffer. It takes three parameters:

variable
A pointer to the null-terminated character string representing the name of the variable to be searched for.
buffer
A pointer to a locked or fixed buffer in which the variable's value will be returned.
bufSize
The size of the passed buffer in bytes (the maximum number of characters that can be returned including the terminating null character).

If the variable is not found, the error flag returned will be true .

Include: system.h

SysGetECLevel()

ErrorCheckingFlags SysGetECLevel(
        MemHandle * checksumBlock);

This routine checks the current error-checking level of the system. The returned record of ErrorCheckingFlags describes which levels of error checking are turned on and which are off. If checksum error checking (ECF_BLOCK_CHECKSUM) is on, pass a pointer to the handle of a block on which the checksum will be done.

Include: ec.h

SysGetInfo()

dword	SysGetInfo(
        SysGetInfoType info);			/* type of information to retrieve */

This routine returns general system information. Pass the type of information to be returned; the value returned depends on the type passed in info . Note that the largest returned value is a dword; many different return values should be cast to the appropriate type when calling SysGetInfo() .

The info parameter (of SysGetInfoType ) can have one of the following values:

SGIT_TOTAL_HANDLES
Returns the total number of handles in the kernel's handle table.
SGIT_HEAP_SIZE
Returns the total heap size in bytes.
SGIT_LARGEST_FREE_BLOCK
Returns the size (in bytes) of the largest possible block that may be allocated at the moment.
SGIT_TOTAL_COUNT
Returns the total number of clock ticks since the current session of GEOS started (subtracts the initial system clock value from the current time).
SGIT_NUMBER_OF_VOLUMES
Returns the total number of volumes registered with the system.
SGIT_TOTAL_GEODES
Returns the total number of geodes currently loaded.
SGIT_NUMBER_OF_PROCESSES
Returns the total number of processes currently loaded.
SGIT_NUMBER_OF_LIBRARIES
Returns the total number of libraries currently loaded.
SGIT_NUMBER_OF_DRIVERS
Returns the total number of drivers currently loaded.
SGIT_CPU_SPEED
Returns the CPU speed of the processor. The value returned will be ten times the ratio of the CPU speed relative to a base XT processor.
SGIT_SYSTEM_DISK
Returns the disk handle of the disk on which GEOS (the GEOS.INI file) resides.
SGIT_UI_PROCESS

Include: sysstats.h

SysGetPenMode()

Boolean	SysGetPenMode();

This routine returns true if GEOS is running on a pen-based system, false if it is not.

Include: system.h

SysLocateFileInDosPath()

DiskHandle SysLocateFileInDosPath( /* sets thread's error value */
        const char		* fname,				/* file name */
        char		* buffer);				/* returned path of file */

This routine searches for a specified file along the search path specified in the DOS environment variable PATH. The parameters are

fname
A pointer to the null-terminated file name to search for.
buffer
A pointer to a locked or fixed buffer into which the full path of the file will be placed.

This routine returns the disk handle of the disk on which the file resides as well as the file's full path (with drive name) in the buffer pointed to by buffer . The path returned is a null-terminated character string. If the file could not be found, a null disk handle will be returned. The error value can be retrieved with ThreadGetError() .

Include: system.h

SysNotify()

word	SysNotify(
        SysNotifyFlags		flags,				/* options to offer user */
        const char 		* string1,				/* first string to display */
        const char 		* string2);				/* second string to display */

This routine causes the kernel to put up a standard notification dialog box on the screen. This dialog box is white with a black border and is used nearly exclusively for error notification by the kernel. Pass this routine the following parameters:

flags
A record of SysNotifyFlags indicating the options the dialog presents to the user. These flags are shown below.
string1
A pointer to a null-terminated character string put up in the dialog box (may be a null pointer).
string2
A pointer to a second null-terminated string presented in the dialog box (may be a null pointer).

The returned word is the user's response, based on the SysNotifyFlags passed (see below).

Structures: SysNotifyFlags is a record of several flags; none, any, or all of the flags may be set at a time. The five flags are

SNF_RETRY
Allow the user to retry the operation that brought up the notification box. If the user selects this option, it will be returned by the routine.
SNF_EXIT
Allow the user to exit GEOS entirely. If the user selects this option, it will be returned by the routine after an SST_CLEAN_FORCED shutdown has been initiated.
SNF_ABORT
Allow the user to abort the operation that brought up the notification box. If the user selects this option, it will be returned by the routine.
SNF_CONTINUE
Allow the user to continue the operation. If the user selects this option, it will be returned by the routine.
SNF_REBOOT
Allow the user to shut down and reboot GEOS directly. If the user selects this option, the routine will not return.

Include: system.h

SysRegisterScreen()

void	SysRegisterScreen(
        GeodeHandle		driver,
        WindowHandle		root);

SysSetECLevel()

void	SysSetECLevel(
        ErrorCheckingFlags		flags,					/* level of error checking */
        MemHandle		checksumBlock);					/* block to check, if any */

This routine sets the error-checking level of the software. Pass it a record of ErrorCheckingFlags indicating which levels of error checking should be employed. If checksum checking (ECF_BLOCK_CHECKSUM) is turned on, also pass the handle of a block on which the checksum will be performed.

Include: ec.h

SysSetExitFlags()

word	SysGetExitFlags(
        ExitFlags		bitsToSet,
        ExitFlags		bitsToClear);

SysShutdown()

Boolean	SysShutdown(
        SysShutdownType type,
        ...);

This routine causes the system to shut down, exiting to the native operating system (typically DOS). It takes variable parameters depending on the first parameter. The first parameter is the type of shutdown requested, and it determines the calling format of the routine. SysShutdown() returns a Boolean value dependent on the type of shutdown.

The parameters and calling format for this routine depend on the value in the type parameter. The possible values ( SysShutdownType ) are listed below with the associated parameter and return information.

SST_CLEAN
Shut down all applications cleanly, allowing any that wish to to abort the shutdown. The routine will return true if a system shutdown is already in progress at the time of the call. This type of shutdown will send MSG_META_CONFIRM_SHUTDOWN to all objects registered on the MANUFACTURER_ID_GEOWORKS:GCNSLT_SHUTDOWN_CONTROL GCN list (but only if the shutdown is not cancelled). Each object on that list must return an acknowledgment of the shutdown. The parameter format and parameters are
Boolean SysShutdown(
    SysShutdownType			type,
    optr			notificationOD,
    Message			msg);
notificationOD
The optr of an object which will receive the message passed in msg after the shutdown has been acknowledged. Pass a null optr to use the default notification ( MSG_META_DETACH sent to the UI).
msg
The message to be sent to the object in notificationOD .
SST_CLEAN_FORCED
Shut down all applications cleanly without the possibility of cancellation. This type takes no additional parameters and does not allow other geodes to abort the shutdown. It will return, but the return value will be meaningless.
SST_DIRTY
Attempt to exit device drivers and close all files without shutting down applications. Does not return. The parameters of this type are
Boolean SysShutdown{
    SysShutdownType			type,		/* SST_DIRTY */
    const char			* reason);
The reason parameter is a pointer to a text string presented to the user as a reason for the dirty shutdown. The string is null-terminated. Pass -1 if no reason is to be given.
SST_PANIC
Exit system device drivers (GA_SYSTEM) without exiting applications or closing files. This can be bad for the system and should be used only in emergency situations. This type of shutdown takes no additional parameters and does not return.
SST_REBOOT
This is used by GEOS when the user hits Ctrl-Alt-Del . Applications should not call it.
SST_RESTART
This is like SST_CLEAN_FORCED above, but it reloads GEOS after shutting down rather than exit completely. It takes no additional parameters; it will return TRUE if the system could not be restarted, FALSE if the shutdown has been initiated.
SST_FINAL
Perform the final phase of a shutdown. This routine is called only by the UI when the SST_CLEAN_FORCED shutdown is complete. This type does not return, and it takes one additional parameter. The calling format and parameters of this type are
Boolean SysShutdown(
    SysShutdownType			type,
    const char			* reason);
The reason parameter is a character string explaining the reason (typically an error) for the final shutdown.
SST_SUSPEND
Suspend system operation in preparation for task switching, and broadcast MSG_META_CONFIRM_SHUTDOWN to all objects on the MANUFACTURER_ID_GEOWORKS:GCNSLT_SHUTDOWN_CONTROL GCN list (see MetaClass ). All notified objects must return acknowledgment of the shutdown. This type of SysShutdown() returns true if another system shutdown is already in progress. It takes two additional parameters:
Boolean SysShutdown(
    SysShutdownType			type,
    optr			notificationOD,
    Message			msg);
notificationOD
The optr of an object which will receive the message passed in msg after the shutdown has been acknowledged. Pass a null optr to use the default notification ( MSG_META_DETACH sent to the UI), though this is not usually the intent of the call.
msg
The message to be sent to the object in notificationOD .
SST_CONFIRM_START
Called by the recipient of MSG_META_CONFIRM_SHUTDOWN ; this allows shutdown confirmation dialog boxes to be presented in order to the user. The caller of this type will be blocked until all previous callers have finished their confirmation procedure. When SysShutdown() returns, the caller may present its confirmation dialog and continue or abort the shutdown. If SysShutdown() returns true from a call with this type, the caller should not present the confirmation dialog to the user and need not call SysShutdown() with SST_CONFIRM_END; another thread has already cancelled the shutdown. This type takes no additional parameters.
SST_CONFIRM_END
The counterpart of SST_CONFIRM_START, this ends the confirmation sequence in an object's MSG_META_CONFIRM_SHUTDOWN handler. It takes one additional parameter and returns nothing. The calling format is shown below:
void	SysShutdown(
    SysShutdownType			type,
    Boolean			confirm);
The confirm parameter should be TRUE if the shutdown is to be continued, FALSE if the shutdown should be aborted.

Include: system.h

Warnings: Most applications should not call SysShutdown() . Any that do should do so with extreme care.

SysStatistics()

void	SysStatistics(
        SysStats * stats);			/* returned statistics */

This routine returns system performance statistics. Pass it a pointer to an empty SysStats structure; the routine will fill in the appropriate fields. SysStats has the following structure:

typedef struct {
    dword			SS_idleCount;
    SysSwapInfo			SS_swapOuts;
    SysSwapInfo			SS_swapIns;
    word			SS_contextSwitches;
    word			SS_interrupts;
    word			SS_runQueue;
} SysStats;

Include: sysstats.h

SysUnlockBIOS()

void	SysUnlockBIOS(void);

TakeDownDialogViaUIThread()

void	TakeDownDialogViaUIThread(
    optr		dialogBox );

This will dismiss and destroy a dialog created with the routine PutUpDialogViaUIThread(). It dismisses the dialog by sending it MSG_GEN_GUP_INTERACTION_COMMAND with the parameter IC_DISMISS.

Pass:

dialogBox
Optr of the duplicate object to be dismissed and destroyed. This is returned by the routine PutUpDialogViaUIThread().

Include: foam.goh

See Also: PutUpDialogViaUIThread(), UserDestroyDialog()

TextSearchInString()

char *	TextSearchInSTring(
        const char		*str1,
        conat char		*startPtr,
        const char		*endPtr,
        word		strSize,
        const char		*str2,
        word		str2Size,
        word		searchOptions,
        word		*matchLen);

This routine searches in a single text chunk for a passed text string. If a match is found, a pointer to that match (and the length of the match) are returned in passed buffers.

str1 is a pointer to the main string you will be searching in.

startPtr and endPtr are pointers to locations within str1 to begin and end the search.

strSize stores the size of str1 , or zero if null-terminated.

str2 stores the match string, which may include wildcards (type WildCard ).

str2Size stores the size of str2 , or zero if null-terminated.

searchOptions stores the SearchOptions to use by the search mechanism. The high byte should be zeroed.

matchLen stores a buffer to store the size of the matched word. (The matched word itself is returned by the routine.)

Include: Objects/vTextC.goh

TextSearchInHugeArray()

dword	TextSearchInSTring(
        char		*str2,
        word		str2Size,
        dword		str1Size,
        dword		curOffset,
        dword		endOffset,
        FileHandle		hugeArrayFile,
        VMBlockHandle		hugeArrayBlock,
        word		searchOptions,
        word		*matchLen);

This routine searches in a huge array for a passed text string. If a match is found, a dword offset to the match (and the length of the match) are returned in passed buffers.

str2 stores the match string, which may include wildcards (type WildCard ).

str2Size stores the size of str2 , or zero if null-terminated.

str1Size stores the total length of the string being searched.

curOffset stores the offset from the start of str1 to the first character to check.

endOffset stores the offset from the start of str1 to the last character to check.

hugeArrayFile stores the file handle of the huge array.

hugeArrayBlock stores the VM block handle of the huge array.

searchOptions stores the SearchOptions to use by the search mechanism. The high byte should be zeroed.

matchLen stores a buffer to store the size of the matched word. (The matched word itself is returned by the routine.)

Include: Objects/vTextC.goh

TGI_PRIORITY()

byte	TGI_PRIORITY(val);
        word	val;

This macro extracts the thread priority from the value returned by ThreadGetInfo() .

TGI_RECENT_CPU_USAGE()

byte	TGI_RECENT_CPU_USAGE(val);
        word	val;

This macro extracts the recent CPU usage from the value returned by ThreadGetInfo() .

ThreadAllocSem()

SemaphoreHandle ThreadAllocSem(
        word	value);			/* allowable locks on the semaphore */

This routine allocates and initializes a new semaphore for private use by a multithreaded application. Pass the value with which to initialize the semaphore; this value represents the number of threads that can grab the semaphore before other grab attempts will block. Typically, the passed value will be one. The routine returns the handle of the new semaphore.

Include: sem.h

See Also: ThreadPSem(),
ThreadVSem(),
ThreadFreeSem(),
ThreadPTimedSem().

ThreadAllocThreadLock()

ThreadLockHandle ThreadAllocThreadLock();

This routine allocates a special semaphore called a thread lock. With a normal semaphore, a thread that grabs the semaphore twice without releasing it will deadlock; with a thread lock, a thread can grab it more than once in succession. The thread has to release it once for each time it grabs the thread lock, however.

In all other aspects, however, the thread lock resembles a normal semaphore. ThreadAllocThreadLock() returns the handle of the new thread lock.

Include: sem.h

See Also: ThreadGrabThreadLock(), ThreadReleaseThreadLock().

ThreadAttachToQueue()

void	ThreadAttachToQueue(
        QueueHandle		qh,				/* queue to attach */
        ClassStruct		* class);				/* primary class of thread */

This routine attaches the calling thread to the passed event queue. This is used only for event-driven threads. Typically, this routine is called when a thread is created; attaching to queues is automatic in nearly all cases, and you will rarely need this routine.

Pass the handle of the queue in qh and a class pointer in class . The class will be attached to the event queue and will handle all messages sent directly to the thread. This class should nearly always be a subclass of ProcessClass .

If a queue handle of zero is passed, the thread wants to "reattach" to the current queue. This is used typically during shutdown of event-driven threads, and it is nearly always taken care of automatically by ProcessClass .

Include: thread.h

ThreadCreate()

ThreadHandle ThreadCreate(
        word	priority,			/* Initial base priority of new thread */
        word	valueToPass,			/* Optional data to pass to new thread */
        word	(*startRoutine)(word valuePassed),
                /* Pointer to entry routine */
        word	stackSize,			/* Size of the stack for the new thread */
        GeodeHandle owner);				/* Geode that will own the new thread */

This routine creates a new procedural thread for a process. If you need a new event-driven thread, send MSG_PROCESS_CREATE_EVENT_THREAD to your process object instead.

Pass the following parameters to this routine:

priority
The priority of the new thread. Typically this will be one of the standard thread priorities (see below).
valueToPass
A word of optional data to be passed to the entry routine of the new thread. This can be used, for example, to indicate the thread's initial context or for initializing thread variables.
startRoutine
A pointer to the entry routine to be executed immediately for the thread. This may be in either fixed or movable memory. The segment must be a virtual segment. Note that if the routine is in movable memory, it may degrade heap performance for the life of the thread (its movable block will remain locked for extended stretches of time). The routine may return the thread's exit code or may call ThreadDestroy() directly.
stackSize
The stack size allocated for the thread. 512 bytes is typically enough for threads doing neither UI nor file system work; threads working with the file system will require 1 K. Threads working with UI objects will require 3 K.
owner
The geode handle of the geode that will own the thread. If the calling thread's geode will own the new thread, it can call GeodeGetProcessHandle() prior to calling ThreadCreate() .

ThreadCreate() returns the thread handle of the new thread. If an error occurs, the calling thread's error code will be set and a null handle returned; you should likely call ThreadGetError() to retrieve the error code after creating the new thread. A return of NO_ERROR_RETURNED from ThreadGetError() means no error occurred.

The standard thread priorities that may be passed in the priority parameter are listed below:

PRIORITY_TIME_CRITICAL
The highest priority of all; you should not use this in general because it will pre-empt nearly all other threads. (It may be useful, however, during debugging.)
PRIORITY_HIGH
A high priority; generally only used for highly important threads.
PRIORITY_UI
Another high priority; this is used for User Interface threads to provide quick response to user actions.
PRIORITY_FOCUS
A medium-level priority; this is used for whatever thread has the current input focus (whichever thread the user is currently working with).
PRIORITY_STANDARD
The standard application thread priority; you should typically use this when creating new threads.
PRIORITY_LOW
A low priority for tasks that can be done in the background.
PRIORITY_LOWEST
The lowest standard priority; it is used for threads that can take any amount of time to complete.

Include: thread.h

ThreadDestroy()

void	ThreadDestroy(
        word	errorCode,	/* Error code to indicate cause of destruction */
        optr	ackObject,	/* Object to receive destruction acknowledgment */
        word	ackData);	/* Additional word of data to pass (as the low
             * word of optr for source of MSG_META_ACK) */

This routine causes the current (calling) thread to exit and then destroy itself. The thread is responsible for ensuring that it has no leftover resources allocated or semaphores locked.

Pass it an error code or exit code meaningful to the application and the other threads in the application. This error code will be used by the debugger to determine the cause of the thread's exit; a null error code usually indicates successful completion of the thread's task.

Pass also the optr of the object to receive acknowledgement of the thread's destruction. The object specified will receive MSG_META_ACK after the calling thread is completely destroyed.

Be Sure To: Always clean up before exiting a thread. Unlock locked resources, free allocated memory, etc. You do not have to do these things for the application's primary thread; the process object (the primary thread) will automatically clean up after itself.

Include: thread.h

ThreadFreeSem()

void	ThreadFreeSem(
        SemaphoreHandle sem);			/* semaphore to be freed */

This routine frees the specified semaphore that had been allocated with ThreadAllocSem() . You must be sure that no threads are using the semaphore or will use it after it has been freed. Subsequent access attempts could cause illegal handle errors or worse.

Include: sem.h

See Also: ThreadAllocSem(),
ThreadPSem(),
ThreadVSem(),
ThreadPTimedSem().

ThreadFreeThreadLock()

void	ThreadFreeThreadLock(
        ThreadLockHandle sem);				/* thread lock to be freed */

This routine frees the specified thread lock that had been allocated with ThreadAllocThreadLock() . You must be sure that no threads are using or will use the thread lock after it has been freed. Subsequent attempts to grab or release the thread lock could cause illegal handle errors.

Include: sem.h

ThreadGetError()

word	ThreadGetError(void)

This routine returns the thread's current error value.

ThreadGetInfo()

word	ThreadGetInfo(
        ThreadHandle		th,			/* thread to get information about */
        ThreadGetInfoType 		info);			/* type of information to get */

This routine gets information about the specified thread. The information desired is specified in the info parameter; the subject thread is specified in the th parameter. If the thread handle passed is zero or a null handle, the routine will return information about the calling thread.

The info parameter is one of the following values of ThreadGetInfoType , specifying the type of information to be returned by ThreadGetInfo() :

TGIT_PRIORITY_AND_USAGE
The returned word will contain both the thread's priority and the thread's recent CPU usage. To extract the priority of the thread, use the macro TGI_PRIORITY; to extract the recent CPU usage, use the macro TGI_RECENT_CPU_USAGE.
TGIT_THREAD_HANDLE
Useful only when the th parameter is zero, this will return the thread handle of the subject thread. If th is zero, the handle of the calling thread will be returned.
TGIT_QUEUE_HANDLE
The returned word will contain the queue handle of the event-driven thread specified in th . If the thread specified is not event-driven, a null queue handle will be returned.

Include: thread.h

ThreadGrabThreadLock()

void	ThreadGrabThreadLock(
        ThreadLockHandle sem);				/* thread lock to grab */

This routine attempts to grab the thread lock for the calling thread. If the thread lock is currently held by another thread, the caller will block until the lock becomes available. If the caller already has the thread lock, it will grab the lock again and continue executing.

Be Sure To: Thread locks must be released with ThreadReleaseThreadLock() once for each time they are grabbed.

Warnings: This routine provides no deadlock protection for multiple threads. If multiple threads will be grabbing multiple thread locks, the locks should always be grabbed in the same order to minimize the potential for deadlock.

Include: sem.h

See Also: ThreadAllocThreadLock(), ThreadReleaseThreadLock().

ThreadHandleException()

void	ThreadHandleException(
        ThreadHandle		th,				/* thread to handle the exception */
        ThreadExceptions		exception,				/* exception to handle */
        void	(*handler)	());				/* pointer to handler */

This routine allows a thread to set up a handler for a processor exception. This can be useful for debugging purposes. Pass the following three parameters:

th
The handle of the thread to handle the exception. Pass zero for the current thread.
exception
A ThreadException type (see below).
handler
A pointer to a handler in fixed or locked memory. Pass a null pointer to use the GEOS default exception handler.

Structures: The ThreadException type has the following values:

TE_DIVIDE_BY_ZERO
TE_OVERFLOW
TE_BOUND
TE_FPU_EXCEPTION
TE_SINGLE_STEP
TE_BREAKPOINT

Include: thread.h

ThreadModify()

void	ThreadModify(
        ThreadHandle		th,					/* thread to modify */
        word		newBasePriority,					/* thread's new base priority */
        ThreadModifyFlags		flags);					/* flags (see below) */

This routine modifies the priority of the specified thread. Use it to either set the base priority of the thread or reset the current CPU usage to zero. The parameters should have the following values:

th
The thread handle; pass zero to change the priority of the calling thread.
newBasePriority
The new base priority of the thread. Use one of the standard priorities--see ThreadCreate() --or use a value between zero and 255.
flags
A record of ThreadModifyFlags ; pass TMF_BASE_PRIO to change the thread's base priority or TMF_ZERO_USAGE to reset the thread's recent CPU usage to zero.

Warnings: Unless the thread is timing-critical, you should not set the base priority to zero.

Include: thread.h

ThreadPrivAlloc()

word	ThreadPrivAlloc(
        word		wordsRequested,					/* number of words to allocate */
        GeodeHandle		owner);					/* handle of geode to own data */

This routine allocates a number of contiguous words in the private data of all geodes (loaded and yet-to-be loaded). It is exactly the same as GeodePrivAlloc() ; see the entry for that routine.

Include: thread.h

See Also: GeodePrivAlloc().

ThreadPrivFree()

void	ThreadPrivFree(
        word	range,			/* offset to first word to be freed */
        word	wordsRequested);			/* number of words to free */

This routine frees a number of contiguous private-data words previously allocated with ThreadPrivAlloc() . It is similar to GeodePrivFree() ; see the entry for that routine for full information.

Include: thread.h

See Also: GeodePrivFree().

ThreadPSem()

SemaphoreError ThreadPSem(
        SemaphoreHandle sem);				/* semaphore to grab */

This routine attempts to grab the passed semaphore via a "P" operation. If the semaphore has already been grabbed, the thread will block until the semaphore becomes available, even if it was grabbed by the same thread.

ThreadPSem() returns an error code of type SemaphoreError . The error code is intended to indicate abnormal return by the previous thread; if the semaphore never becomes available, the thread will block indefinitely and the routine will not return.

Be Sure To: When the thread no longer needs the semaphore, it should release it with ThreadVSem() .

Warnings: This routine provides no deadlock protection. If threads will grab multiple common semaphores, they should always grab/release them in the same order, minimizing the chances for deadlock.

A thread may not try to grab a particular semaphore twice without releasing it in between grabs. The thread will block on itself and will deadlock. If a thread may need to grab the semaphore twice in a row, it should use a thread lock instead (see ThreadAllocThreadLock() for more information).

Include: sem.h

See Also: ThreadAllocSem(),
ThreadVSem(),
ThreadFreeSem(),
ThreadPTimedSem().

ThreadPTimedSem()

SemaphoreError ThreadPTimedSem(
        SemaphoreHandle		sem,				/* semaphore to grab */
        word		timeout);				/* ticks before timeout */

This routine attempts to grab the passed semaphore via a "P" operation. If the semaphore has already been grabbed, the thread will block for at most the number of ticks specified in timeout .

Often timeout is passed as zero to indicate that if the semaphore isn't available right now, the thread will go on with some other action.

Be Sure To: When the thread no longer needs the semaphore, it should release it with ThreadVSem() .

Warnings: This routine provides no deadlock protection. If threads will grab multiple common semaphores, they should always grab/release them in the same order, minimizing the chances for deadlock.

A thread may not try to grab a particular semaphore twice without releasing it in between grabs. The thread will block on itself and will deadlock. If a thread may need to grab the semaphore twice in a row, it should use a thread lock instead, though there is no timeout equivalent for thread locks (see ThreadAllocThreadLock() for more information).

Include: sem.h

See Also: ThreadAllocSem(),
ThreadPSem(),
ThreadVSem(),
ThreadFreeSem().

ThreadReleaseThreadLock()

void	ThreadReleaseThreadLock(
        ThreadLockHandle sem);				/* threadlock to release */

This routine releases the specified thread lock previously grabbed with ThreadGrabThreadLock() . Pass the handle of the thread lock as returned by ThreadAllocThreadLock() .

Do not try to release a thread lock that has not previously been grabbed. The results are unpredictable.

Include: sem.h

See Also: ThreadAllocThreadLock(), ThreadGrabThreadLock().

ThreadVSem()

void	ThreadVSem(
        SemaphoreHandle sem);				/* semaphore to release */

This routine releases a semaphore that was grabbed with ThreadPSem() or ThreadPTimedSem() . Pass the handle of the semaphore as returned by ThreadAllocSem() .

Do not try to release a semaphore that has not previously been grabbed with one of the above routines. The results are unpredictable.

Include: sem.h

See Also: ThreadAllocSem(),
ThreadPSem(),
ThreadFreeSem(),
ThreadPTimedSem().

TimerGetCount()

dword	TimerGetCount();

This routine returns the value of the system counter. The returned value is the number of ticks since GEOS started.

Include: timer.h

TimerGetDateAndTime()

void	TimerGetDateAndTime(
        TimerDateAndTime * dateAndTime);						/* buffer for returned values */

This routine returns the current time and date. Pass it a pointer to an empty TimerDateAndTime structure to be filled in by the routine.

Include: timedate.h

TimerGetFileDateTime()

TimerFileDateTime TimerGetFileDateTime();

This routine returns the current time and date in a structure equivalent to a FileDateAndTime . As such, it is useful for constructing time stamps for files.

TimerSetDateAndTime()

void	TimerSetDateAndTime(
        word			flags,					/* which item to set */
        const TimerDateAndTime			* dateAndTime);					/* new values */

This routine sets the current date and/or time of the system. Pass it the following:

flags
A word of flags. Pass TIME_SET_DATE to set the day, month, and year; pass TIME_SET_TIME to set the hour, minute, and second. Pass both to set both.
dateAndTime
A pointer to a TimerDateAndTime structure containing the information to be set.

Include: timedate.h

TimerSleep()

void	TimerSleep(
        word	ticks);		/* number of ticks the thread should sleep */

This routine invokes a "sleep timer" that will put the calling thread to sleep for the given number of ticks. At the end of the time, the thread will continue executing with the next instruction.

Warnings: Do not use sleep timers as a substitute for semaphores for thread synchronization.

Include: timer.h

:

TimerStart()

TimerHandle TimerStart(
        TimerType		timerType,				/* type of timer to start */
        optr		destObject,				/* object to receive notification
                         * message when timer expires */
        word		ticks,				/* amount of time to run */
        Message		msg,				/* notification message */
        word		interval,				/* interval for continual timers */
        word		* id);				/* buffer for returned timer ID */

This routine starts a timer of any type. The timer will run for the specified number of ticks and then will send the given message to the destination object. The message is sent with the flags MF_FORCE_QUEUE, MF_CHECK_DUPLICATE and MF_REPLACE, so it will always be put in the recipient's queue and will always replace any duplicates already in the queue. Pass this routine the following:

timerType
A value of TimerType indicating the type of timer to start.
destObject
The optr of the object that will be sent the specified message when the time is up.
ticks
The number of ticks for the timer to run. (Sixty ticks equals one second.) For continual timers, this is the amount of time before the first timeout.
msg
The message to be sent to the destination object when time is up.
interval
For continual timers, the interval (number of ticks) at which to send out the message to the destination object. The timer will send the message once at the end of each interval. The first message will be sent ticks ticks after the timer is started. The second message will be sent interval ticks after that.
id
A pointer to a word in which the timer's ID will be returned. You will need this ID for TimerStop() .

This routine returns the handle of the timer as well as an ID pointed to by the id parameter. You will need the handle and the ID for TimerStop() .

Structures:

TimerType The TimerType enumerated type defines what type of timer should be initiated. It has the following values:

TIMER_ROUTINE_ONE_SHOT
Start a timer that will call a routine and then free itself when the time is expired. This type is supported in assembly but not in C.
TIMER_ROUTINE_CONTINUAL
Start a timer that will call a routine once per time interval (starting after ticks ticks) until TimerStop() is called.
TIMER_EVENT_ONE_SHOT
Start a timer that will send a message to a given object, then free itself when time is expired.
TIMER_EVENT_CONTINUAL
Start a timer that will send a message to a given object once per time interval (starting after ticks ticks) until TimerStop() is called.
TIMER_MS_ROUTINE_ONE_SHOT
Start a timer that has millisecond accuracy. For this timer, the number of ticks will actually be the number of milliseconds. The timer will call a specified routine and then free itself when time is expired. This type is supported in assembly but not in C.
TIMER_EVENT_REAL_TIME
Start a timer that will call a routine at some particular date and time. On devices that support such a timer, this event will wake a sleeping machine.

Include: timer.h

TimerStop()

Boolean	TimerStop(
        TimerHandle		th,			/* handle of timer to be stopped */
        word		id);			/* timer ID (returned by TimerStart() */

This routine stops and destroys a timer that had been started with TimerStart() . Pass it the timer handle and the ID as returned by that routine (the ID of a continual timer will always be zero).

The returned error flag will be true if the timer could not be found.

Warnings: If you call TimerStop() to stop a continual timer that sends its message across threads, there may be timer events left in the recipient's event queue. It is unsafe in this situation to assume that all timer events have been handled. To ensure the timer message has been handled, you can send the destination an "all-safe" message with the MF_FORCE_QUEUE flag.

Include: timer.h

TocDBLock()

void * TocDBLock(
        DBGroupAndItem 		thing);

Use this routine to lock a name array maintained by a PrefTocList object.

Include: config.goh

TocDBLockGetRef()

void * TocDBLockGetRef(
        DBGroupAndItem 		thing,
        optr		*refPtr);

This routine locks a name array maintained by a PrefTocList object, returning the item's pointer and optr.

Include: config.goh

TocFindCategory()

Boolean TocFindCategory(
        TocCategoryStruct 		*cat);

This routine searches a PrefTocList object's name lists for a given token.

Structures:

        typedef struct {
            TokenChars 		TCS_tokenChars;
            DBGroupAndItem 		TCS_files; 					/* file name array */
            DBGroupAndItem 		TCS_devices; 					/* device name array--only if
                                         * TCF_EXTENDED_DEVICE_DRIVERS
                                         * is set. */
        } TocCategoryStruct;

Include: config.goh

TocGetFileHandle()

word TocGetFileHandle();

Use this routine to get the handle of the file used by PrefTocLists to store their name array data.

Include: config.goh

TocNameArrayAdd()

word TocNameArrayAdd(
        DBGroupAndItem 		array,
        const char 		*nameToFind,
        const void 		*data);

Use this routine to add a name to a name array maintained by a PrefTocList object.

Include: config.h

TocNameArrayFind()

word TocNameArrayGetElement(
        DBGroupAndItem 		array,
        word 		element,
        void 		*buffer);

Use this routine to find a name in the name list maintained by a PrefTocList object.

Include: config.goh

TocNameArrayGetElement()

word TocNameArrayGetElement(
        DBGroupAndItem 		array,
        word 		element,
        void 		*buffer);

Use this routine to retrieve a given element from a name array maintained by a PrefTocList object.

Include: config.goh

TocSortedNameArrayAdd()

word TocSortedNameArrayAdd(
        word 		arr,
        const char 		*nameToAdd,
        NameArrayAddFlags 		flags,
        const void 		*data);

This routine adds a name to a sorted name array associated with a PrefTocList object.

Structures:

        typedef WordFlags NameArrayAddFlags;
        #define NAAF_SET_DATA_ON_REPLACE 0x8000

Include: config.goh

TocSortedNameArrayFind()

Boolean TocSortedNameArrayFind(
        word 				arr,
        const char 				*nameToFind,
        SortedNameArrayFindFlags 				flags,
        void 				*buffer,
        word 				*elementNum);

This routine looks up a name in a sorted name array associated with a PrefTocList object.

Structures:

        typedef WordFlags SortedNameArrayFindFlags;
        #define SNAFF_IGNORE_CASE 0x0080

Include: config.goh

TocUpdateCategory()

void TocUpdateCategory(
        TocUpdateCategoryParams *params);

Use this routine to update a PrefTocList object based upon the files in a given directory with a given token.

Structures:

        typedef struct {
            TocUpdateCategoryFlags 				TUCP_flags;
            TokenChars 				TUCP_tokenChars;
            byte 				TUCP_fileArrayElementSize;

            TocUpdateAddCallback 				*TUCP_addCallback;
            byte 				TUCP_pad; /* Wants to be word-aligned */
        } TocUpdateCategoryParams;

        typedef word _pascal TocUpdateAddCallback(
            const char *filename,
            optr chunkArray);
        /* Return 0 if add aborted, else return offset of new element within
         * block */

Include: config.goh

TOKEN_CHARS()

dword	TOKEN_CHARS(a, b, c, d)

This macro creates a single dword value from four given characters. This is useful when creating a token characters value for a specific token. The first two characters passed may be chars, the second two should be cast to dwords. A simple example:

TOKEN_CHARS(`S', `A', (dword) `M', (dword) `P');

TokenCloseLocalTokenDB()

void	TokenCloseLocalTokenDB()

This routine closes the local token database.

TokenDefineToken()

word	TokenDefineToken(
        dword		tokenChars,					/* four token characters */
        ManufacturerID		manufacturerID,					/* manufacturer ID for token */
        optr		monikerList,					/* optr of moniker list */
        TokenFlags		flags);					/* token flags */

This routine adds a new token and moniker list to the token database. If the token already exists in the token DB, the old will be replaced with the new. This routine must only be called by a thread that can lock the block in which the passed moniker or moniker list resides. This routine must be passed the following parameters:

tokenChars
The four token characters that identify this moniker or moniker list in the token database. Create this dword value from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
monikerList
The optr of the moniker list to be added to the token database.
flags
A record of TokenFlags indicating the relocation status of the moniker list.

Warnings: This routine may legally move locked LMem blocks (token database items), thereby invalidating all pointers to token database items.

Include: token.h

TokenGetTokenInfo()

Boolean	TokenGetTokenInfo(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        TokenFlags		* flags);					/* returned token flags */

This routine returns information about a specified token. Pass it the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
flags
A pointer to an empty flags record; the flags set (if any) for the specified token (if it exists) will be returned here.

This routine returns a non-zero value if the token was found in the token database. It returns zero if the token was not found.

Include: token.h

TokenListTokens()

dword	TokenListTokens(
        TokenRangeFlags 		tokenRangeFlags,
        word 		headerSize,
        ManufacturerID 		manufacturerID));

This routine lists all the tokens in the token database. It allocates a new block on the global heap and writes in it an array of GeodeToken structures. This routine returns the actual tokens, not the token groups.

The returned dword consists of two values: The high word represents the number of tokens in the returned block and may be extracted with the macro TokenListTokensCountFromDWord() . The low word represents the handle of the newly-allocated block and can be extracted with the macro TokenListTokensHandleFromDWord() .

Include: token.h

TokenListTokensCountFromDWord()

word	TokenListTokensCountFromDWord(d);
        dword	d;

This macro extracts the number of tokens from the value returned by TokenListTokens() .

TokenListTokensHandleFromDWord()

word	TokenListTokensHandleFromDWord(d);
        dword	d;

This routine extracts the MemHandle from the value returned by TokenListTokens() .

TokenLoadMonikerBlock()

Boolean	TokenLoadMonikerBlock(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        DisplayType		displayType,					/* type of display for token */
        VisMonikerSearchFlags searchFlags,							/* flags for finding token */
        word		* blockSize,					/* returned block size */
        MemHandle		* blockHandle);					/* returned block handle */

This routine loads a specified token's moniker, allocating a new global memory block for the moniker. The returned Boolean will be false if the moniker was found, true otherwise. Information about the moniker is returned in the values pointed to by blockSize (the size of the newly allocated block) and blockHandle (the handle of the new block). If the moniker is not found, both return pointers will be NULL and no block will be allocated.

Pass this routine the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
displayType
A value of DisplayType indicating the size of the display (used to indicate small-screen devices, primarily).
searchFlags
A record of VisMonikerSearchFlags indicating what type of moniker is being requested.
blockSize
A pointer to a word in which the new block's size will be returned.
blockHandle
A pointer to a handle in which the new block's handle will be returned.

Include: token.h

TokenLoadMonikerBuffer()

Boolean	TokenLoadMonikerBuffer(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        DisplayType		displayType,					/* type of display for token */
        VisMonikerSearchFlags searchFlags,							/* flags for finding token */
        void		* buffer,					/* pointer to buffer for token */
        word		bufSize,					/* size of passed buffer */
        word		* bytesReturned);						/* number of bytes returned */

This routine loads a specified token's moniker into a provided buffer. The return value will be false if the moniker was found, true otherwise. The size of the returned moniker will be returned in the word pointed to by the bytesReturned parameter.

Pass this routine the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
displayType
A value of DisplayType indicating the size of the display (used to indicate small-screen devices, primarily).
searchFlags
A record of VisMonikerSearchFlags indicating what type of moniker is being requested.
buffer
A pointer to a locked or fixed buffer into which the moniker will be copied.
bufSize
The size of the passed buffer; also the maximum size of the moniker that may be returned.
bytesReturned
The size of the moniker actually returned in the buffer.

Include: token.h

TokenLoadMonikerChunk()

Boolean	TokenLoadMonikerChunk(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        DisplayType		displayType,					/* type of display for token */
        VisMonikerSearchFlags searchFlags,							/* flags for finding token */
        MemHandle		lmemBlock,					/* locked block for new chunk */
        word		* chunkSize,					/* returned new chunk size */
        ChunkHandle		* chunkHandle);					/* returned new chunk handle */

This routine loads a specified token's moniker, allocating a new chunk in a local memory block for the moniker. The returned error flag will be true if the moniker was not found, false otherwise.

Pass this routine the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
displayType
A value of DisplayType indicating the size of the display (used to indicate small-screen devices, primarily).
searchFlags
A record of VisMonikerSearchFlags indicating what type of moniker is being requested.
lmemBlock
The MemHandle of the local memory block in which the new chunk will be allocated. If the block is locked, you must dereference the global handle after calling this routine.
chunkSize
A pointer to a word in which the size of the allocated chunk will be returned.
chunkhandle
A pointer to a chunk handle in which the handle of the newly allocated chunk will be returned.

Warnings: This routine can move chunks in the passed block, thereby invalidating pointers to any chunk in the block.

Include: token.h

TokenLoadTokenBlock()

Boolean	TokenLoadTokenBlock(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        word		* blockSize,					/* returned size of new block */
        MemHandle		* blockHandle);					/* returned handle of block */

This routine loads the specified token's TokenEntry structure into a newly-allocated global memory block. If the token is not found, the returned error flag will be true ; otherwise, it will be false .

Pass this routine the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
blockSize
A pointer to a word in which the size of the newly-allocated block will be returned.
blockHandle
A pointer to a global handle in which the handle of the newly-allocated block will be returned.

Include: token.h

TokenLoadTokenBuffer()

Boolean	TokenLoadTokenBuffer(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        TokenEntry		* buffer);					/* buffer for returned token */

This routine loads the specified token's TokenEntry structure into a passed buffer. The returned error flag will be true if the token was not found, false otherwise. Pass this routine the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
buffer
A pointer to a locked or fixed buffer into which the token entry will be copied.

Include: token.h

TokenLoadTokenChunk()

Boolean	TokenLoadTokenChunk(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        MemHandle		lmemBlock,					/* handle of block for chunk */
        word		* chunkSize,					/* returned size of new chunk */
        ChunkHandle		* chunkHandle);					/* returned chunk handle */

This routine loads the specified token's TokenEntry structure into a newly-allocated chunk. The returned error flag will be true if the token could not be found, false otherwise.

Pass this routine the following:

tokenChars
The four token characters that identify the token database entry. Create this dword from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
lmemBlock
The MemHandle of the local memory block in which the new chunk will be allocated. If the block is locked, you must manually dereference this handle after the routine call.
chunksize
A pointer to a word in which the size of the newly-allocated chunk will be returned.
chunkHandle
A pointer to a chunk handle in which the handle of the newly-allocated chunk will be returned.

Warnings: This routine can move chunks in the passed block, thereby invalidating pointers to any chunk in the block.

Include: token.h

TokenLockTokenMoniker()

void	* TokenLockTokenMoniker(
        TokenMonikerInfo		tokenMonikerInfo);				/* The DB group and item numbers
                         * as returned by TokenLookupMoniker() */

This routine locks a token's moniker so it may be drawn; it returns a pointer to the locked chunk containing the moniker information. Pass it the structure returned by TokenLookupMoniker() .

Be Sure To: Unlock the moniker with TokenUnlockTokenMoniker() after you have finished drawing it.

Include: token.h

TokenLookupMoniker()

Boolean	TokenLookupMoniker(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */
        DisplayType		displayType,					/* display type of token */
        VisMonikerSearchFlags searchFlags,							/* flags for finding token */
        TokenMonikerInfo *		tokenMonikerInfo);					/* DB group and item of token */

This routine finds and retrieves a pointer to the specific moniker for the specified token, given also the token's display type and other attributes. Pass the following:

tokenChars
The four token characters that identify this moniker or moniker list in the token database. Create this dword value from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.
displayType
A value of DisplayType indicating the size of the display (used to indicate small-screen devices, primarily).
searchFlags
A record of VisMonikerSearchFlags indicating what type of moniker is being requested.
tokenDBItem
A pointer to an empty TokenMonikerInfo structure, in which the token's group and item numbers will be returned.

The return value is an error flag: it will be true if the item could not be found in the token database, false otherwise.

Include: token.h

TokenOpenLocalTokenDB()

word	TokenOpenLocalTokenDB()

This routine opens the local token database. It returns zero on success, and a VMStatus error code on failure.

Include: token.h

TokenRemoveToken

Boolean	TokenRemoveToken(
        dword		tokenChars,					/* four characters of token */
        ManufacturerID		manufacturerID,					/* manufacturer ID of token */

This routine removes the specified token and its moniker list from the token database. It returns an error flag: if the token could not be found, the returned flag is true ; otherwise it is false . Pass the following:

tokenChars
The four token characters that identify this moniker or moniker list in the token database. Create this dword value from the four characters with the macro TOKEN_CHARS.
manufacturerID
The manufacturer ID number of the manufacturer responsible for the token database entry.

Include: token.h

TokenUnlockTokenMoniker()

void	TokenUnlockTokenMoniker(
        void * moniker);

This routine unlocks a moniker that had been locked with TokenLockTokenMoniker() . Pass a pointer to the locked moniker, as returned by the locking routine.

Include: token.h

TypeFromFormatID()

word	TypeFromFormatID(id);
        ClipboardItemFormatID id;

This macro extracts the word-sized format ID (of type ClipboardItemFormat ) from a ClipboardFormatID argument.

UserAllocObjBlock

MemHandle 	UserAllocObjBlock(
        ThreadHandle 		threadHandle);

This routine allocates a block of memory on the heap to be used for holding UI objects. It returns the handle of the block. It is passed one argument:

threadHandle
Handle of the thread which is to manage the block. Pass zero to use the current thread.

Include: ui.goh

UserAddAutoExec()

void	UserAddAutoExec(
        const char *		appName);

This routine adds an application to the list of those, like Welcome, that are automatically started by the UI when it loads. The application name will be added to the appropriate place in the GEOS.INI file. It is passed one argument:

appName
This is a pointer to a null-terminated string containing the name of the application. The application must be in SP_APPLICATION or SP_SYS_APPLICATION.

Include: ui.goh

UserCreateDialog()

optr	UserCreateDialog(
        optr	dialogBox);

This routine duplicates a template dialog box, attaches the dialog box to an application object, and sets it fully GS_USABLE so that it may be called with UserDoDialog() . Dialog boxes created in such a manner should be removed and destroyed with UserDestroyDialog() when no longer needed.

dialogBox
Optr to template dialog box (within a template object block). The block must be sharable, read-only and the top GenInteraction called with this routine must not be linked into any generic tree. The optr returned is a created, fully-usable dialog box.

See Also: UserDestroyDialog().

UserCreateInkDestinationInfo()

MemHandle 	UserCreateInkDestinationInfo(
        optr 		dest,
        GStateHandle 		gs,
        word		brushSize,
        GestureCallback		*callback);

This routine creates an InkDestinationInfo structure to be returned with MSG_META_QUERY_IF_PRESS_IS_INK. The callback routine must be declared _pascal.

Include: ui.goh

Structures:

        typedef Boolean _pascal GestureCallback (
            Point *arrayOfInkPoints,
            word numPoints,
            word numStrokes);

UserDestroyDialog()

void	UserDestroyDialog(
        optr	dialogBox);

This routine destroys the passed dialog box, usually created with UserCreateDialog() . This routine may only be used to destroy dialog boxes occupying a single block; the block must also hold nothing other than the dialog box to be destroyed. It is for this reason that it is wise to only use this routine to destroy dialogs created with UserCreateDialog() .

See Also: UserCreateDialog().

UserDoDialog()

InteractionCommand UserDoDialog(
        optr	dialogBox);

UserDoDialog() brings a pre-instantiated dialog box on-screen, blocking the calling thread until the user responds to the dialog. You must pass the optr of a GIV_DIALOG Interaction that is set both GIA_INITIATED_VIA_USER_DO_DIALOG and GIA_MODAL.

This routine returns the InteractionCommand of the particular response trigger selected by the user. This InteractionCommand may be either a predefined type (such as IC_YES) or a custom one defined using IC_CUSTOM_START.

The pre-defined InteractionCommand s are:

IC_NULL
IC_DISMISS
IC_APPLY
IC_RESET
IC_OK
IC_YES
IC_NO
IC_STOP
IC_EXIT
IC_HELP
IC_INTERACTION_COMPLETE

This routine may return IC_NULL for those cases in which a system shutdown causes the dialog to be dismissed before the user has entered a response.

Warnings: This routine blocks the calling thread until the dialog box receives a MSG_GEN_GUP_INTERACTION_COMMAND. Since the application thread is blocked, it cannot be responsible for sending this message or for handling messages from the response triggers.

See Also: UserStandardDialog(), UserStandardDialogOptr().

UserGetInterfaceLevel()

UIInterfaceLevel UserGetInterfaceLevel(void)

This routine returns the current UIInterfaceLevel . This is a word-sized enumerated type. It has the following values:

UIIL_NOVICE
UIIL_BEGINNING_INTERMEDIATE
UIIL_ADVANCED_INTERMEDIATE
UIIL_ADVANCED
UIIL_GURU

Include: ui.goh

UserLoadApplication()

extern GeodeHandle UserLoadApplication(
        AppLaunchFlags 		alf,
        Message 		attachMethod,
        MemHandle 		appLaunchBlock,
        char 		*filename,
        StandardPath 		sPath,
        GeodeLoadError 		*err);

Loads an application. Changes to standard application directory before attempting GeodeLoad on filename passed. Stores the filename being launched into the AppLaunchBlock , so that information needed to restore this application instance will be around later if needed.

UserRemoveAutoExec()

void	UserRemoveAutoExec(
        const char *		appName);

This routine removes an application from the list of those to be launched on start-up. It is passed one argument:

appName
This is a pointer to a null-terminated string containing the name of the application.

Include: ui.goh

UserStandardDialog()

word	UserStandardDialog(
        char *			helpContext,
        char *			customTriggers,
        char *			arg2,
        char *			arg1,
        char *			string,
        CustomDialogBoxFlags 	dialogFlags);

UserStandardDialog() creates and displays either a custom dialog box or one of several pre-defined standard dialog boxes.

Most often, you will use this routine to create a custom dialog box that conforms to a standardized dialog. In this case, pass the CustomDialogType of SDBT_CUSTOM as the routine's final argument. You must then supply other parameters to create the custom dialog box.

If instead you wish to use one of the pre-defined CustomDialogType types, you should pass that type as the final argument to this routine. Some of these standard types require you to pass string parameters. Other arguments should be passed as null.

For custom dialog boxes you must pass a CustomDialogType (CDT_WARNING, CDT_NOTIFICATION, CDT_QUESTION, or CDT_ERROR). This chooses the proper icon glyph to display within the dialog box. (For example, a CDT_WARNING dialog might contain a large exclamation-point glyph.) Make sure that you use CDBF_DIALOG_TYPE_OFFSET to pass this value. E.g.:

(GIT_AFFIRMATION << CDBF_INTERACTION_TYPE_OFFSET) | (CDT_WARNING << CDBF_DIALOG_TYPE_OFFSET)

You should also pass a valid GenInteractionType . In most cases, this will be either GIT_NOTIFICATION, GIT_AFFIRMATION, or GIT_MULTIPLE_RESPONSE. Make sure that you use CDBF_INTERACTION_TYPE_OFFSET to pass this value. E.g.:

(GIT_AFFIRMATION << CDBF_INTERACTION_TYPE_OFFSET) | (CDT_WARNING << CDBF_DIALOG_TYPE_OFFSET)

Also pass the routine a string to display to the user. This string may be either text or graphics based. The arg1 and arg2 strings may be used in conjunction with this string; if string contains any ctrl-A or ctrl-B characters (ASCII value 1 and 2, respectively), then the string pointed to by arg1 or arg2 will be inserted in that place.

If the CustomDialogType is GIT_MULTIPLE_RESPONSE, you must also set up a Response Trigger Table with several trigger parameters. The customTrigger argument should be a pointer to a structure containing these parameters. Which type of structure depends on how many triggers there are: StandardDialog1ResponseTriggerTable , StandardDialog2ResponseTriggerTable , StandardDialog3ResponseTriggerTable , StandardDialog4ResponseTriggerTable . In each case, you will need to fill in fields specifying how many triggers there are and a moniker and response value for each trigger.

The helpContext value is used by the help system; this is the "context" name of the page of the geode's help to go to when the user asks for help with this dialog. A null pointer signals that the default help context should be used.

This routine returns an InteractionCommand value. It will be IC_NULL if the interaction was terminated by the system (such as in a system shutdown).

UserStandardDialogOptr()

word	UserStandardDialogOptr(
        char *		helpContext,
        void *		customTriggers,
        optr		arg2,
        optr		arg1,
        optr		string
        CustomDialogBoxFlags		dialogFlags);

UserStandardDialogOptr() provides the same functionality as UserStandardDialog() except that optrs to strings and string parameters are passed instead of fptrs. This is useful for localized strings in resource blocks.

See Also: UserStandardDialog(), UserDoDialog().

UserStandardSound()

word	UserStandardSound(
        StandardSoundType		type,
        ...);

This routine plays a simple sequence of notes. It can be used to play a standard system sound, a single custom tone, or a sequence of tones.

The routine takes a variable number of arguments. The first argument is a member of the StandardSoundType enumerated type. This argument specifies what kind of tone or tones will be played. Depending on the StandardSoundType passed, zero, one, or two additional arguments may be needed. StandardSoundType contains the following members:

SST_ERROR
This is the sound played when an "Error" dialog comes up. No further arguments are needed.
SST_WARNING
This is a general warning sound. No further arguments are needed.
SST_NOTIFY
This is a general notification sound. No further arguments are needed.
SST_NO_INPUT
This is the sound played when a user's input is not going anywhere (e.g. when he clicks the mouse outside a modal dialog box).
SST_KEY_CLICK
This is the sound produced when the keyboard is pressed, or when the user clicks on a floating keyboard. No further arguments are required.
SST_CUSTOM_SOUND
Play a custom sampled sound. This requires one more argument, the memory handle of the sound to be played.
SST_CUSTOM_BUFFER
Play a custom buffer of instrumental sound. This requires one further argument, a pointer to the memory block containing the sound buffer. Note that the "tempo" value used to play this buffer will be one tick per thirty-second note, probably much faster than you would otherwise expect.
SST_CUSTOM_NOTE
By passing this argument, you can have a single custom note played. You must provide one further argument, the handle of the note (such as returned by SoundAllocMusicNote() ).

The function returns a word-length token value. This value is useful for stopping sounds with the UserStopStandardSound() routine.

UserStopStandardSound()

word	UserStandardSound(
        StandardSoundType		type,
        word				countID);

This routine stops a sound that was started by UserStandardSound.

The sound to stop is identified by its type and by the word-length token value returned by UserStandardSound.

This function will not stop SST_CUSTOM_... sounds.

UtilAsciiToHex32()

Boolean	UtilAsciiToHex32(
        const char *		string,
        dword *		value);

This routine converts a null-terminated ASCII string into a 32-bit integer. The string may begin with a hyphen, indicating a negative number. Aside from that, the string may contain nothing but numerals until the null termination. It may not contain whitespace.

If the routine is successful, it will return false and write an equivalent signed long integer to *value . If it fails, it will return true and write a member of the UtilAsciiToHexError enumerated type to *value . This type contains the following members:

UATH_NON_NUMERIC_DIGIT_IN_STRING
This string contained a non-numeric character before the trailing null (other than the allowed leading hyphen).
UATH_CONVERT_OVERFLOW
The string specified a number to large to be expressed as a signed 32-bit integer.

Include: system.h

UtilHex32ToAscii()

word	UtilHex32ToAscii(
        char *		buffer,
        dword		value,
        UtilHexToAsciiFlags 		flags);

This routine converts a 32-bit unsigned integer to its ASCII representation and writes it to the specified buffer. It returns the length of the string (not counting the nulll termination, if any). The routine is passed the following arguments:

buffer
This is a pointer to a character buffer. The buffer must be long enough to accommodate the largest string; that is, there must be ten bytes for the characters, plus one for the trailing null (if necessary).
value
This is the value to convert to ASCII.
flags
This is a record of UtilHexToAscii flags. The following flags are available:
UHTAF_INCLUDE_LEADING_ZEROS
Pad the string with leading zeros to a length of ten total characters.
UHTAF_NULL_TERMINATE
Add a null to the end of the string. If this flag is set, the buffer must be at least 11 bytes long. If it is clear, the buffer may be ten bytes long.

Include: system.h

VarDataFlagsPtr()

VarDataFlags 	VarDataFlagsPtr(
        void *	ptr);

This macro fetches the flags of a variable data type when given a pointer to the extra data for the type. The flags are stored in a VarDataFlags record. Only the flags VDF_EXTRA_DATA and/or VDF_SAVE_TO_STATE will be returned.

Include: object.h

Warnings: You must pass a pointer to the beginning of the vardata entry's extra data space.

VarDataSizePtr()

word	VarDataSizePtr(
        void *	ptr);

This macro fetches the size of a variable data entry when given a pointer to the extra data for the type.

Include: object.h

Warnings: You must pass a pointer to the beginning of the vardata entry's extra data space.

VarDataTypePtr()

word	VarDataTypePtr(
        void *	ptr);

This macro fetches the type of a variable data entry when given a pointer to the extra data of the entry. The type is stored in a VarDataFlags record. All flags outside the VDF_TYPE section will be cleared.

Include: object.h

Warning: You must pass a pointer to the beginning of the vardata entry's extra data space.

VirtualSerialClose()

VirtualSerialStatus 	VirtualSerialClose ( void  );

Waits until all outgoing data is sent to remote unit, then flushes incoming data buffer and closes connection to remote unit.

Returns VS_OK if data was successfully written.

Returns VS_CLOSED if stream was already closed.

Warning: This is a low-level routine whose use is discouraged in favor of ModemClose(). Do not call any Virtual Serial function except VirtualSerialOpen() after calling this function.

Include: vserlib.h

See Also: VirtualSerialOpen().

VirtualSerialFlush()

VirtualSerialStatus 	VirtualSerialFlush(
     word 		flushMode);

Flushes data from incoming buffer. The flushMode sets the operation direction. It may be:

STREAM_READ
Flushes the incoming data buffer.
STREAM_WRITE
Is ignored.
STREAM_BOTH
Same as STREAM_READ.

Returns VS_OK if the stream successfully flushed.

Returns VS_CLOSED if the stream is not open.

Include: vserlib.h

VirtualSerialOpen()

VirtualSerialStatus	 VirtualSerialOpen(
    VSOpenFlags 		stream_open_mode,    /* Fail or wait if stream is busy */
    word 		input_buffer_size,    /* # of bytes 1...4096 of input buffer */
    word 		output_buffer_size,    /* Ignore. Automatically set to 4352 bytes. */
    word 		timeout);    /* # of ticks to wait and retry if the stream is busy. */

This routine lets the client open a stream to the phone. Should the Virtual Phone data resource be in use and if stream_open_mode is set to SOF_NOBLOCK then the function fails immediately; otherwise, if stream_open_mode is set to SOF_TIMEOUT, the function will try again until it is either successful or timeout number of ticks (1 ... 32768) elapse. input_buffer_size is the size (in bytes 1...4096) of the input buffer. The output_buffer_size should be ignored, as the output buffer is set to 4352 bytes.

Returns VS_OK if successful.

Returns VS_DATA_RESOURCE_RESERVED if VP data resource is already in use.

Returns VS_STREAM_ALREADY_OPEN if stream has already been opened by another client.

Returns VS_CANNOT_ALLOC if cannot allocate memory for the streams buffer.

Warning: This is a low-level routine whose use is discouraged in favor of ModemOpen(). This routine must return VS_OK before client can start using other Virtual Serial functions. Outgoing data is not buffered in VirtualSerial library, but sent directly from calling client's buffer.

Include: vserlib.h

See Also: VirtualSerialClose().

VirtualSerialRead()

VirtualSerialStatus 	VirtualSerialRead(
        VSBlockingMode		blocking_mode, /* to block or not to block */
        word		data_size, /* # of bytes of data to be read */
        byte		*ptr_to_buffer, /* buffer to hold data */
        word		*bytes_read); /* # of bytes actually read */

Copies incoming data to buffer. If there is less than data_size number of bytes in the buffer and STREAM_BLOCK was specified in blocking_mode, will block until enough data becomes available; if there is less than data_size number of bytes in the buffer and STREAM_NO_BLOCK was specified, returns immediately with VS_SHORT_READ_WRITE.

Returns VS_OK if data was successfully written.

Returns VS_CLOSED if stream is not open.

Include: vserlib.h

See Also: VirtualSerialReadByte().

VirtualSerialReadByte()

VirtualSerialStatus 	VirtualSerialReadByte(
        VSBlockingMode		blockingMode, /* to block or not to block */
        byte		*dataByte); /* buffer to hold read data */

This routine reads a byte from the stream into dataByte. If the stream is empty and blockingMode is set to STREAM_BLOCK then the routine will wait for the request to be filled, or if STREAM_NO_BLOCK is specified the routine will immediately return an error.

Returns VS_OK if data was successfully written to dataByte.

Returns VS_SHORT_READ_WRITE if the data was not available and STREAM_NO_BLOCK was specified.

Returns VS_CLOSED if the stream is not open.

Warning: STREAM_BLOCK mode is dangerous to use: if the opposite end does not send data this function will block forever.

Include: vserlib.h

See Also: VirtualSerialRead(), VirtualSerialWriteByte().

VirtualSerialSendDTR()

VirtualSerialStatus 	VirtualSendDTR(
        byte		DTR_status);

Sends Data Terminal Ready status to modem.

Include: vserlib.h

VirtualSerialSetFlowControl()

VirtualSerialStatus 	VirtualSerialSetFlowControl(
        VSFlowControl		flow_control_mode); /* software or hardware */

Selects the flow control method used between the modem and the VirtualSerial Library. flow_control_mode can be set to either VSFC_SOFTWARE or VSFC_HARDWARE.

Returns VS_OK if successfully set or VS_CLOSED if stream is not open.

Include: vserlib.h

VirtualSerialSetFormat()

VirtualSerialStatus 	VirtualSetFormat(
        VSSerialFormat		format,/* data format */
        VSSerialBaud		baud); /* baud rate */

Sets the modem's data format and speed. Returns VS_OK if successfully set or VS_CLOSED if stream is not open.

Include: vserlib.h

VirtualSerialSetNotify()

VirtualSerialStatus 	VirtualSerialSetNotify(
    VSNotifyType		notifyTpe,   /* Which events, and how they are received */
    optr 		destination,   /* Destination object */
    word 		method);   /* Message to pass to destination object */

This routine registers a notifier for any of the possible relevant events. The parameter notifyType is set to specify which events should result in the object destination being sent the message method. The notifyType should be set by OR-ing one VSNT_, one VSNE_, and one VSNM_ parameter.

The set of these parameters is:

VSNT_READER
notify reader
VSNE_DATA
data/space available notification
VSNE_MODEM
notify when modem input signals change
VSNM_NONE
disable notifier
VSNM_MESSAGE
notify by sending a message cx:dx
For example, notifyType = VSNT_READER | VSNE_DATA | VSNM_MESSAGE: This will notify the reader for data available using messaging.

This routine returns VS_OK if the notification is successfully registered.

Returns VS_NOT_SUPPORTED if the notifier is not supported.

Returns VS_CLOSED if the stream is not open.

Warning: This is a low-level routine whose use is discouraged in favor of ModemSetRoutineDataNotify(),
    ModemSetMessageDataNotify(),
    ModemSetRoutineResponseNotify(),
    ModemSetMessageResponseNotify(), and
    ModemSetMessageEndCallNotify().

Include: vserlib.h

VirtualSerialWrite()

VirtualSerialStatus 	VirtualSerialWrite(
        VSBlockingMode		blocking_mode, /* to block or not to block */
        word		buffer_size, /* # of bytes of data to write */
        const byte		*ptr_to_buffer, /* data to write to stream */
        word		*bytes_written); /* # of bytes actually written */

Writes buffer_size number of bytes of data from the buffer to stream. If there is not enough space in the stream and STREAM_BLOCK was specified in blocking_mode, will block and wait until more space becomes available; if there is not enough space in the stream and STREAM_NO_BLOCK was specified, returns immediately with VS_SHORT_READ_WRITE.

Returns VS_OK if data was successfully written.

Returns VS_CLOSED if stream is not open.

Warning: STREAM_BLOCK is dangerous to use: if the opposite end does not read its data, stream may be constantly full and routine will continue to block for a maximum of two minutes. After two minutes, routine returns VS_SHORT_READ_WRITE and discards the data.

Include: vserlib.h

VirtualSerialWriteByte()

VirtualSerialStatus 	VirtualSerialWriteByte(
        VSBlockingMode		blockingMode, /* To block or not to block... */
        byte		dataByte); /* Data to write */

This routine writes the byte dataByte into the stream. If the stream is full and blockingMode is set to STREAM_BLOCK then the routine will wait for the request to be filled, or if STREAM_NO_BLOCK is specified the routine will immediately return an error.

Returns VS_OK if data was successfully written.

Returns VS_SHORT_READ_WRITE if the space was not available and STREAM_NO_BLOCK was specified.

Returns VS_CLOSED if the stream is not open.

Warning: STREAM_BLOCK mode is dangerous to use: if the opposite end does not read its data, the stream may be constantly full and this function will block for maximum of two minutes. After two minutes the routine returns VS_SHORT_READ_WRITE and discards the given data.

Include: vserlib.h

See Also: VirtualSerialWrite(), VirtualSerialReadByte().

VisObjectHandlesInkReply()

void	VisObjectHandlesInkReply(void);

VisTextGraphicCompressGraphic()

extern VMChain VisTextGraphicCompressGraphic(
        VisTextGraphic 		*graphic,
        FileHandle 		sourceFile,
        FileHandle 		destFile,
        BMFormat format,
        word xRes,
        word yRes);

This routine compresses the bitmaps in a VisTextGraphic.

VMAlloc()

VMBlockHandle 	VMAlloc(
        VMFileHandle		file,
        word		size,			/* Size of a file in bytes */
        word		userID);			/* ID # to associate with block */

This routine creates a VM block. The block is not initialized. Before you use the block, you must lock it with VMLock() . If you pass a size of zero bytes, the VM block will be given an entry in the VM handle table, but no space in memory or in the file will be used; a global memory block will have to be assigned with VMAttach() .

Include: vm.h

See Also: VMAllocLMem(), VMAttach().

VMAllocLMem()

VMBlockHandle 	VMAllocLmem(
        VMFileHandle		file,
        LMemType		ltype,				/* Type of LMem heap to create */
        word		headerSize);				/* Size to leave for LMem header...
                         * pass zero for standard header */

This routine allocates a VM block and initializes it to contain an LMem heap. You must pass the type of LMem heap to create. If you want a fixed data space, you must pass the total size to leave for a header (including the LMemBlockHeader ); otherwise, pass a zero header size, indicating that only enough space for an LMemBlockHeader should be left. You do not need to specify a block size, since the heap will automatically expand to accommodate chunk allocations.

The block's user ID number is undefined. You will need to lock the block with VMLock() before accessing the chunks.

Include: vm.h

Warning: When you access chunks, remember to pass the block's global memory handle to the LMem routines (not the block's VM handle).

See Also: LMemInitHeap(),
VMAlloc(),
VMAttach().

VMAttach()

VMBlockHandle 	VMAttach(
        VMFileHandle		file,
        VMBlockHandle		vmBlock,
        MemHandle		mh);

This routine attaches an existing global memory block to a VM block. It is passed the following arguments:

file
The file's VMFileHandle.
vmBlock
The handle of the VM block to which the memory block should be attached. Any data associated with that block will be lost. If you pass a null VMBlockHandle , a new VM block will be allocated.
mh
The handle of the global memory block to attach.

The routine returns the handle of the VM block to which the memory block was attached.

If you attach to a pre-existing VM block, its user ID will be preserved. If you create a new block (by passing a null vmBlock argument), the user ID will be undefined.

The block is automatically marked dirty.

Include: vm.h

VMCheckForModifications()

Boolean	VMCheckForModifications(
        VMFileHandle		file);

This routine returns true if the VM file has been dirtied or updated since the last full save.

Include: vm.h

VMClose()

word	VMClose(
        VMFileHandle	file,
        Boolean		noErrorFlag);

This routine updates and closes a VM file. If it is successful, it returns zero. If it fails, it returns a VMStatus error code. Note that the routine closes the file even if it could not successfully update the file; in this case, any changes since the last update will be lost. For this reason, it is safest to call VMUpdate() first, then (after the file has been successfully updated) call VMClose().

If noErrorFlag is TRUE, VMClose() will fatal-error if it could not succesfully update and close the file.

Include: vm.h

VMCompareVMChains()

Boolean	VMCompareVMChains(
        VMFileHandle		sourceFile,
        VMChain		sourceChain,
        VMFileHandle		destFile,
        VMChain		destChain);

This routine compares two VM chains or DB items. It returns true if the two are identical; otherwise it returns false .

Include: vm.h

VMCopyVMBlock()

VMBlockHandle 	VMCopyVMBlock(
        VMFileHandle		sourceFile,
        VMBlockHandle		sourceBlock,
        VMFileHandle		destFile);

This routine creates a duplicate of a VM block in the specified destination file (which may be the same as the source file). It returns the duplicate block's handle. The duplicate will have the same user ID as the original block.

Include: vm.h

VMCopyVMChain()

VMChain 	VMCopyVMChain(
        VMFileHandle		sourceFile,
        VMChain		sourceChain,
        VMFileHandle		destFile);

This routine creates a duplicate of a VM chain (or DB item) in the specified destination file (which may be the same as the source file). It returns the duplicate's VMChain structure. All blocks in the duplicate will have the same user ID numbers as the corresponding original blocks.

Include: vm.h

VMDetach()

MemHandle 	VMDetach(
        VMFileHandle		file,
        VMBlockHandle		block,
        GeodeHandle		owner);			/* Pass zero to have block owned by
                     * current thread's owner */

This routine detaches a global memory block from a VM block. If the VM block is not currently in memory, VMDetach() allocates a memory block and copies the VM block into it. If the VM block is dirty, VMDetach() will update the block to the file before detaching it.

Include: vm.h

VMDirty()

void	VMDirty(
        MemHandle		mh);

This routine marks a locked VM block as dirty.

Include: vm.h

VMFind()

VMBlockHandle 	VMFind(
        VMFileHandle		file,
        VMBlockHandle		startBlock,
        word		userID);

This routine finds a VM block with the specified user ID number. If the second argument is NullHandle the routine will return the matching block with the lowest handle. If the second argument is non-null, it will return the first matching block whose handle is larger than the one passed (in numerical order).

Include: vm.h

VMFree()

void	VMFree(
        VMFileHandle		file,
        VMBlockHandle		block);

This routine frees the specified VM block. If a global memory block is currently attached to the VM block, it is freed too.

Include: vm.h

VMFreeVMChain()

void	VMFreeVMChain(
        VMFileHandle		file,
        VMChain		chain);

This routine frees the specified VM chain or DB item. If a chain is specified, all blocks in the chain will be freed.

Include: vm.h

VMGetAttributes()

word	VMGetAttributes(
        VMFileHandle		file);

Each VM file contains a set of VMAttributes flags. These determine how the VM manager will treat the file. This routine returns the current flags.

Include: vm.h

Tips and Tricks: When the Document Control objects create files, they automatically initialize the attributes appropriately.

See Also: VMSetAttributes().

VMGetDirtyState()

word	VMGetDirtyState(
        VMFileHandle		file);

This routine finds out if a file has been dirtied. It returns a word-sized value. The upper byte of the return value is non-zero if the file has not been dirtied since the last save, auto-save, or update; the lower byte is non-zero if the file has not been dirtied since the last save. Thus, if the return value is zero, the file must be updated.

Include: vm.h

Tips and Tricks: VMUpdate() is optimized for updating clean files. For this reason, it is faster to call VMUpdate() then it is to first check the dirty state, then call VMUpdate() only if the file is dirty.

VMGetMapBlock()

VMBlockHandle 	VMGetMapBlock(
        VMFIleHandle		file);

This routine returns the VM block handle of the file's map block.

Include: vm.h

VMGrabExclusive()

VMStartExclusiveReturnValue VMGrabExclusive(
        VMFileHandle		file,
        word		timeout,
        VMOperation		operation,
        VMOperation *		currentOperation);

This routine gets exclusive access to a VM file for this thread.

Include: vm.h

VMInfo()

Boolean	VMInfo(
        VMFileHandle		file,
        VMBlockHandle		block,
        VMInfoStruct *		info

This routine writes the memory handle, block size, and user ID number of the block. It returns non-zero if the handle is invalid or free.

Include: vm.h

VMLock()

void *	VMLock(
        VMFileHandle		file,
        VMBlockHandle		block,
        MemHandle*		mh);

This routine locks a VM block into the global heap. It returns the block's base address.

Include: vm.h

VMMemBlockToVMBlock()

VMBlockHandle 	VMMemBlockToVMBlock(
        MemHandle		mh,
        VMFileHandle*		file);

This routine gets the VM block and file handles for a specified memory block. It returns the VM block handle and copies the VM file handle into *file .

The memory handle passed must be the handle of a block which is attached to a VM file. If it is not, the results are undefined.

Include: vm.h

VMModifyUserID()

void	VMModifyUserID(
        VMFileHandle		file,
        VMBlockHandle		block,
        word		userID);

This routine changes a VM block's user ID number.

Include: vm.h

VMOpen()

VMFileHandle 	VMOpen(
        char *		name,				/* Name of file to open/create */
        VMAccessFlags		flags,
        VMOpenType		mode,
        word		compression);				/* Compaction threshold percentage
                         * passed as an integer */

This routine opens or creates a VM file. It returns the handle of the opened file. If it is unable to open the file, it returns a null VMFileHandle and sets a VMStatus error code which may be retrieved with ThreadGetError() . VMOpen() looks for the file in the thread's working directory (unless a temporary file is being created, as described below). The routine takes four arguments:

name
A pointer to a string containing the name of the file to open. The file will be opened in the thread's current working directory. If a temporary file is being opened, this buffer should contain the full path of the directory in which to create the file, followed by fourteen null bytes (counting the string-ending null). VMOpen() will write the name of the temporary file in those trailing nulls.
flags
This specifies what kind of access to the file you need. The flags are described in the C Structures Reference.
mode
This specifies how the file should be opened. The types are described in the C Structures Reference.
compression
The compaction threshold percentage, passed as an integer. For example, to set a compaction threshold of 50%, pass the integer 50. When the percentage of used space in the file drops below the compaction threshold, the VM manager will automatically compact the file. To use the system default threshold, pass a threshold of zero. The compaction threshold is set only when the file is created; this argument is ignored if an existing file is opened.

You must also specify how the file should be opened. To do this, you pass a member of the VMOpenType enumerated type.

If for any reason VMOpen() is unable to open the requested file, it will returns a null file handle. It will also set the error value for ThreadGetError() . The possible error conditions are:

VM_FILE_EXISTS
VMOpen() was passed VMO_CREATE_ONLY, but the file already exists.
VM_FILE_NOT_FOUND
VMOpen() was passed VMO_OPEN, but the file does not exist.
VM_SHARING_DENIED
The file was opened by another geode, and access was denied.
VM_OPEN_INVALID_VM_FILE
VMOpen() was instructed to open an invalid VM file (or a non-VM file).
VM_CANNOT_CREATE
VMOpen() cannot create the file (but it does not already exist).
VM_TRUNCATE_FAILED
VMOpen() was passed VMO_CREATE_TRUNCATE; the file exists, but could not be truncated.
VM_WRITE_PROTECTED
VMOpen() was passed VMAF_FORCE_READ_WRITE, but the file was write-protected.
VM_CANNOT_OPEN_SHARED_MULTIPLE
VMOpen() attempted to open a file marked GFHF_SHARED_MULTIPLE while passing the VMAF_DISALLOW_SHARED_MULTIPLE flag.
VM_FILE_FORMAT_MISMATCH
VMOpen() attempted to create a VM file using a name that already belongs to a DOS file.

Include: vm.h

Tips and Tricks: If you use the document control objects, they will take care of opening files as necessary; you will not need to call VMOpen() .

See Also: FileOpen().

VMPreserveBlocksHandle()

void	VMPreserveBlocksHandle(
        VMFileHandle		file,
        VMBlockHandle		block);

Keep the same global memory block with this VM block until the block is explicitly detached or the VM block is freed.

Include: vm.h

VMReleaseExclusive()

void VMReleaseExclusive(
        VMFileHandle 		file);

This routine releases a thread's exclusive access to a VM file.

Include: vm.h

VMRevert()

void	VMRevert(
        VMFileHandle		file,);

This routine reverts a file to its last-saved state.

Include: vm.h

VMSave()

Boolean	VMSave(
        VMFileHandle		file);

This routine updates and saves a file, freeing all backup blocks. If it encounters an error in the save operation, it will return non-zero .

VMSaveAs()

VMFileHandle VMSaveAs(
        VMFileHandle		file,
        const char		*name,
        VMAccessFlags		flags.
        VMOpenType		mode,
        word		compression); /* compaction threshold */

This routine saves a file under a new name. The old file is reverted to its last-saved condition. If it encounters an error in the save operation, it will return a null VMFileHandle and set a VMStatus error code which may be retrieved with ThreadGetError() .

Include: vm.h

VMSetAttributes()

word	VMSetAttributes(
        VMFileHandle		file,
        VMAttributes		attrToSet,				/* Turn these flags on... */
        VMAttributes		attrToClear);				/* after turning these flags off */

This routine changes a VM file's VMAttributes settings. The routine returns the new attribute settings.

Include: vm.h

Tips and Tricks: When the Document Control objects create files, they automatically initialize the attributes appropriately.

Warnings: If you turn off VMA_BACKUP, make sure you do it right after a save or revert (when there are no backup blocks).

See Also: VMGetAttributes().

VMSetExecThread()

void	VMSetExecThread(
        VMFileHandle		file,
        ThreadHandle		thread);

Set which thread will execute methods of all objects in the file.

Include: vm.h

VMSetMapBlock()

void	VMSetMapBlock(
        VMFileHandle		file,
        VMBlockHandle		block);

This routine sets the map block for a VM file.

Include: vm.h

VMSetReloc()

void	VMSetReloc(
        VMFileHandle		file,
        void (*reloc) 		(VMFileHandle					file,
                 VMBlockHandle					block,
                 MemHandle					mh,
                 void					*data,
                 VMRelocTypes					type));

This routine sets a data-relocation routine for the VM file.

Include: vm.h

VMUnlock()

void	VMUnlock(
        MemHandle		mh);

This routine unlocks a locked VM block. Note that the block's global memory handle is passed (not its VM handle).

Include: vm.h

VMUpdate()

word	VMUpdate(
        VMFileHandle		file);

This routine updates dirty blocks to the disk.

Include: vm.h

Tips and Tricks: VMUpdate() is optimized for updating clean files to the disk. Therefore, it is faster to call VMUpdate() whenever you think it might be necessary, than it is to check the dirty state and then call VMUpdate() only if the file is actually dirty.

VMVMBlockToMemBlock()

MemHandle 	VMVMBlockToMemBlock(
        VMFileHandle		file,
        VmBlockHandle		block);

This routine returns the global handle of the memory block attached to a specified VM block. If no global block is currently attached, it will allocate and attach one.

Include: vm.h

WinAckUpdate()

void	WinAckUpdate(
        WindowHandle		win);

This routine acknowledges that the application has received MSG_META_EXPOSED for the specified window, but chooses not to do any updating.

Include: win.h

WinApplyRotation()

void	WinApplyRotation(
        WindowHandle		win,
        WWFixedAsDWord		angle,
        WinInvalFlag		flag);

This routine applies the specified rotation to the window's transformation matrix.

Include: win.h

WinApplyScale()

void	WinApplyScale(
        WindowHandle		win,
        WWFixedAsDWord		xScale,
        WWFixedAsDWord		yScale,
        WinInvalFlag		flag);

This routine applies the specified scale factor to the window's transformation matrix.

Include: win.h

WinApplyTransform()

void	WinApplyTransform(
        WindowHandle		win,
        const TransMatrix *		tm,
        WinInvalFlag		flag);

This routine concatenates the passed transformation matrix with the window's transformation matrix. The result will be the window's new transformation matrix.

Include: win.h

WinApplyTranslation()

void	WinApplyTranslation(
        WindowHandle		win,
        WWFixedAsDWord		xTrans,
        WWFixedAsDword		yTrans,
        WinInvalFlag		flag);

This routine applies the specified translation to the window's transformation matrix.

Include: win.h

WinApplyTranslationDWord()

void	WinApplyExtTranslation(
        WindowHandle		win,
        sdword		xTrans,
        sdword		yTrans,
        WinInvalFlag		flag);

This routine applies the specified translation to the window's transformation matrix. The translations are specified as 32-bit integers.

Include: win.h

WinChangeAck()

WindowHandle WinChangeAck(
        WindowHandle		win,
        sword		x,
        sword		y,
        optr *		winOD);

Include: win.h

WinChangePriority()

void	WinChangePriority(
        WindowHandle		win,
        WinPassFlags		flags,
        word		layerID);

This routine changes the priority for the specified window.

Include: win.h

WinClose()

void	WinClose(
        WindowHandle		win);

This routine closes and frees the specified window.

Include: win.h

WinDecRefCount()

void	WinDecRefCount(
        WindowHandle		win);

This routine is part of the window closing mechanism.

WinEnsureChangeNotification()

void	WinEnsureChangeNotification(void);

Include: win.h

WinGeodeGetInputObj()

optr	WinGeodeGetInputObj(
        GeodeHandle		obj);

This routine fetches the optr of the input object for the specified geode. If there is no such object, it returns a null optr.

Include: win.h

WinGeodeGetParentObj()

optr	WinGeodeGetParentObj(
        GeodeHandle		obj);

This routine fetches the optr of the parent object of the specified geode. If there is no such object, it returns a null optr.

Include: win.h

WinGeodeSetActiveWin()

void	WinGeodeSetActiveWin(
        GeodeHandle		gh,
        WindowHandle		win);

This routine sets the active window for the specified geode.

Include: win.h

WinGeodeSetInputObj()

void	WinGeodeSetInputObj(
        GeodeHandle		gh,
        optr		iObj);

This routine sets the input object for the specified geode.

Include: win.h

WinGeodeSetParentObj()

void	WinGeodeSetParentObj(
        GeodeHandle		gh,
        optr		pObj);

This routine sets the parent object for the specified geode.

Include: win.h

WinGeodeSetPtrImage()

void	WinGeodeSetPtrImage(
        GeodeHandle		gh,
        optr		ptrCh);

This routine sets the pointer image for the specified geode.

Include: win.h

WinGetInfo()

dword	WinGetInfo(
        WindowHandle		win,
        WinInfoTypes		type,
        void *		data);

This routine retrieves the private data from a GState.

Include: win.h

WinGetTransform()

void	WinGetTransform(
        WindowHandle		win,
        TransMatrix *		tm);

This routine retrieves the transformation matrix for the specified window. It writes the matrix to *tm .

Include: win.h

WinGetWinScreenBounds()

void	WinGetWinScreenBounds(
        WindowHandle		win,
        Rectangle *		bounds);

This routine returns the bounds of the on-screen portion of a window (specified in screen co-ordinates). It writes the bounds to *bounds .

Include: win.h

WinGrabChange()

Boolean	WinGrabChange(
        WindowHandle		win,
        optr		newObj);

This routine allows an object to grab pointer events. It returns zero if it was successful; otherwise it returns non-zero.

Include: win.h

WinInvalReg()

void	WinInvalReg(
        WindowHandle		win,
        const Region *		reg,
        word		axParam,
        word		bxParam,
        word		cxParam,
        word		dxParam);

This routine invalidates the specified region or rectangle.

Include: win.h

WinMove()

void	WinMove(
        WindowHandle		win,
        sword		xMove,
        sword		yMove,
        WinPassFlags		flags);

This routine moves a window. If the WPF_ABS bit of flags is set, the window's new position is specified relative to its parent's position. If it is clear, the window's new position is specified relative to its current position.

Include: win.h

WinOpen()

WindowHandle WinOpen(
        Handle		parentWinOrVidDr,
        optr		inputRecipient,
        optr		exposureRecipient,
        WinColorFlags		colorFlags,
        word		redOrIndex,
        word		green,
        word		blue,
        word		flags,
        word		layerID,
        GeodeHandle		owner,
        const Region *		winReg,
        word		axParam,
        word		bxParam,
        word		cxParam,
        word		dxParam);

This routine allocates and initializes a window and (optionally) an associated GState.

Include: win.h

WinReleaseChange()

void	WinReleaseChange(
        WindowHandle		win,
        optr		obj);

This routine releases an object's grab on the change OD.

Include: win.h

WinResize()

void	WinResize(
        WindowHandle		win,
        const Region *		reg,
        word		axParam,
        word		bxParam,
        word		cxParam,
        WinPassFlags		flags);

This routine resizes a window. It can move it as well.

Include: win.h

WinScroll()

void	WinScroll(
        WindowHandle		win,
        WWFixedAsDWord		xMove,
        WWFixedAsSWord		yMove,
        PointWWFixed *		scrollAmt);

This routine scrolls a window.

Include: win.h

WinSetInfo()

void	WinSetInfo(
        WindowHandle		win,
        WinInfoType		type,
        dword		data);

This routine sets some data for the specified window.

Include: win.h

WinSetNullTransform()

void	WinSetNullTransform(
        WindowHandle		win,
        WinInvalFlag		flag);

This routine changes a window's transformation matrix to the null (or identity) matrix.

Include: win.h

WinSetPtrImage()

void	WinSetPtrImage(
        WindowHandle		win,
        WinSetPtrImageLevel		ptrLevel,
        optr		ptrCh);

This routine sets the pointer image within the range handled by the specified window.

Include: win.h

WinSetTransform()

void	WinSetTransform(
        WindowHandle		win,
        const TransMatrix *		tm,
        WinInvalFlag		flag);

This routine replaces the window's transformation matrix with the one passed in *tm .

Include: win.h

WinSuspendUpdate()

void	WinSuspendUpdate(
        WindowHandle		win);

This routine suspends the sending of update messages to the window. The messages will be sent when WinUnSuspendUpdate() is called.

Include: win.h

WinTransform()

XYValueAsDWord 	WinTransform(
        WindowHandle		win,
        sword		x,
        sword		y);

This routine translates the passed document coordinates into screen coordinates.

Include: win.h

WinTransformDWord()

void	WinTransformDWord(
        WindowHandle		win,
        sdword		xCoord,
        sdword		yCoord,
        PointDWord *		screenCoordinates);

This routine translates the passed document coordinates into screen coordinates. The translated coordinates are written to *screenCoordinates .

Include: win.h

WinUnSuspendUpdate()

void	WinUnSuspendUpdate(
        WindowHandle		win);

This routine cancels a previous WinSuspendUpdate() call.

Include: win.h

WinUntransform

XYValueAsDWord 	WinUntransform(
        WindowHandle		win,
        sword		x,
        sword		y);

This routine translates the passed screen coordinates into document coordinates.

Include: win.h

WinUntransformDWord()

void	WinUntransformDWord(
        WindowHandle		win,
        sdword		xCoord,
        sdword		yCoord,
        PointDWord *		documentCoordinates);

This routine translates the passed screen coordinates into document coordinates. The translated coordinates are written to *documentCoordinates .

Include: win.h

WWFixedToFrac

word	WWFixedToFrac(WWFixed wwf)

This macro lets you address the fractional portion of a WWFixed value. It is legal to use this to assign a value to the fractional protion; that is,

WWFixedToFrac(myWWFixed) = 5;

is perfectly legal.

Include: geos.h

WWFixedToInt

word	WWFixedToInt(WWFixed wwf)

This macro lets you address the intetgral portion of a WWFixed value. It is legal to use this to assign a value to the integral protion; that is,

WWFixedToInt(myWWFixed) = 5;

is perfectly legal.

Include: geos.h

This document is a single-page version of a a multi-page document, suitable for easy printing.


GEOS C Structs Reference

This document contains reference information for most of the GEOS system structs. You may look up information in this and other GEOS SDK documentation by consulting the index. If, instead, you wish to browse this reference, you may choose one of the links below; the text of each is the name of the first struct documented on its page of the document.

AccessPointStandardProperty ...
AppLaunchFlags ...
BitmapMode ...
CellFunctionParameterFlags ...
Chars ...
ChunkArrayHeader ...
ClipboardItemHeader ...
Color ...
ColorFlag ...
CompSizeHintArgs ...
CustomDialogBoxFlags ...
DataStoreIndexCallbackParams ...
DateTimeFormat ...
DiskFindResult ...
DosNoDotFileName ...
EndOfSongFlags ...
EvalErrorData ...
FALSE ...
FileAttrs ...
FileDateAndTime ...
FileFromTransferBlockID ...
FontFamily ...
FontID ...
FontMaker ...
GCM_info ...
GCNStandardListType ...
GeodeToken ...
GeoworksMediumID ...
GeoWorksVisContentGCNListType ...
GStringElement ...
GStringErrorType ...
HeapCongestion ...
IMCFeatures ...
InkControlFeatures ...
InstrumentPatch ...
InstrumentTable ...
Language ...
LMemType ...
LocalNumericFormat ...
MediumUnitType ...
MixMode ...
NULL ...
OperatorType ...
ParserFlags ...
ParserTokenData ...
Point ...
ProtocolNumber ...
RangeSortFlags ...
ReleaseNumber ...
ScannerTokenCellData ...
SerialBaud ...
Socket ...
SocketError ...
SocketLoadType ...
SoundPlayFlags ...
SpecWidth ...
SpoolInfoType ...
StandardDialog4ResponseTriggerTable ...
StyleElementFlags ...
SystemDrawMask ...
TextMode ...
TextReferenceType ...
TimerHandle ...
TravelOption ...
UndoActionStruct ...
VChar ...
VisRulerType ...
VMOpenType ...

AccessPointStandardProperty

typedef enum {
        APSP_NAME = 0x0,
        APSP_PHONE = 0x2,
        APSP_USER = 0x4,
        APSP_SECRET = 0x6,
        APSP_ADDRESS = 0x8,
        APSP_MASK = 0xa,
        APSP_GATEWAY = 0xc,
        APSP_DNS1 = 0xe,
        APSP_DNS2 = 0x10,
        APSP_DATA_BITS = 0x12,
        APSP_STOP_BITS = 0x14,
        APSP_PARITY = 0x16,
        APSP_DUPLEX = 0x18,
        APSP_MODEM_INIT = 0x1a,
        APSP_BS = 0x1c,
        APSP_HOSTNAME = 0x1e,
        APSP_INTERNET_ACCPNT = 0x20,
        APSP_PROMPT_SECRET = 0x22,					/* This property is an integer */
        APSP_AUTOMATIC = 0x4000,
        APSP_UNDEFINED = 0xffff,

             /* The following are only available on build 4 or more recent
                of the Nokia 9000i Communicator. */

        APSP_USE_LOGIN_APP = 0x24,
        APSP_LOGIN_APP_NAME = 0x26,
        APSP_SCRIPT_NAME = 0x28,
        APSP_CCARD_NAME = 0x2a,
        APSP_CCARD_ACCESS = 0x2c,
        APSP_CCARD_ID = 0x2e,
        APSP_CCARD_PREFIX = 0x30,
        APSP_CCARD_SEQUENCE = 0x32,
        APSP_COMPRESSION = 0x34,
} AccessPointStandardProperty;

All of the property data associated with these standard properties are strings, except where noted.

Any AccessPointStandardProperty value can be combined (bitwise-OR'd) with APSP_AUTOMATIC to make an alternative form of the name. This library puts no special interpretation on automatic names, but they are intended to represent values which are obtained from automatic configuration and not from the user.

Include: accpnt.goh

AccessPointType

typedef enum {
    APT_INTERNET = 0x1,
    APT_TERMINAL,
    APT_TELNET,
    APT_APP_LOCAL,
} AccessPointType;
#define APT_ALL (0)

Include: accpnt.goh

AddUndoActionFlags

typedef WordFlags AddUndoActionFlags;
#define AUAF_NOTIFY_BEFORE_FREEING											 		  			0x8000
#define AUAF_NOTIFY_IF_FREED_WITHOUT_BEING_PLAYED_BACK		 0x4000

AddUndoActionStruct

typedef struct {
UndoActionStruct 				AUAS_data;
optr 				AUAS_output;
AddUndoActionFlags 				AUAS_flags;
} AddUndoActionStruct;

The "undo" structures work together to provide information vital to processes which will be working with undo events.

AppAttachFlags

typedef WordFlags AppAttachFlags;
#define AAF_RESTORING_FROM_STATE						0x8000
#define AAF_STATE_FILE_PASSED						0x4000
#define AAF_DATA_FILE_PASSED						0x2000

These flags are passed to the process when the application is launching or being restored from a state file. The flags indicate whether the application is being launched from a state file, has a state file, and/or has a data file.

Note that if AAF_RESTORING_FROM_STATE is set, then AAF_STATE_FILE_PASSED will also be set.

AppInstanceReference

typedef struct {
/* AIR_fileName:
 * Application being launched. Pathname is relative to application
 * directory (which, of course, may be overriden with a direct path
 * to the application). */
PathName 				AIR_fileName;
/* AIR_stateFile:
 * State filename. File is assumed to be in standard directory for
 * GEOS state files. If the first byte is "0", then there is no
 * state file for this application. This structure is copied into the
 * field as an aid in restarting applications, and if it comes across
 * one with this byte as 0, it will not restart it. */
FileLongName 				AIR_stateFile;
/* AIR_diskHandle:
 * Disk handle for app (passed in) IF 0, use System disk, if -1, use
 * AIR_diskName. In the field, if this is a placeholder structure, this
 * word is the handle of the application object we are waiting to detach. */
DiskHandle 				AIR_diskHandle;
/* AIR_savedDiskData:
 * Start of data stored by DiskSave when instance is saved to state file. */
byte 				AIR_savedDiskData[1];
} AppInstanceReference;

AppLaunchBlock

typedef struct {
/* ALB_appRef:
 * Instance reference. Contains full pathname to application, as
 * referenced from app directory, plus the name of a state file.
 * Is enough info to launch application again, restored. (State file
 * need not be passed to GeodeLoad) */
AppInstanceReference 				ALB_appRef;
/* ALB_appMode:
 * Application attach mode method. Should be one of the following:
 * MSG_GEN_PROCESS_RESTORE_FROM_STATE:
 * 	State file must be passed; no data file should be passed.
 * MSG_GEN_PROCESS_OPEN_APPLICATION:
 * 	State file normally should not be passed, although one could be to
 * 	accomplish ui templates. A data file may be passed into the
 * 	application as well.
 * MSG_GEN_PROCESS_OPEN_ENGINE:
 * 	State file normally should not be passed. The data file on which the
 * 	engine will operate must be passed. If zero, the default data file
 * 	should be used (enforced by app, not GenProcessClass).*/
Message 				ALB_appMode;
/* ALB_launchFlags:
 * Miscellaneous flags to specify desired application launch type. */
AppLaunchFlags 				ALB_launchFlags;
/* ALB_diskHandle:
 * Disk handle for data path. (Set as application's current path in
 * GenProcess' MSG_META_ATTACH handler.) */
MemHandle 				ALB_diskHandle;
/* ALB_path:
 * Data path for application to use as initial path. (Usually this is
 * a directory of any data file passed.) (Set as application current
 * path in GenProcess' MSG_META_ATTACH handler.)
char 				ALB_path[PATH_BUFFER_SIZE];
/* ALB_dataFile:
 * Name of data file passed in to be opened (0 if none). Pathname is
 * relative to above path. */
char 				ALB_dataFile[PATH_BUFFER_SIZE];
/* ALB_genParent:
 * Generic parent for new application (0 to put on default field). (Should
 * be passed NULL to MSG_GEN_FIELD_LAUNCH_APPLICATION).
optr 				ALB_genParent;
/* ALB_userLoadAckOutput, ALB_userLoadAckMessage:
 * Together, these form an Action Descriptor which will be activated when
 * the application has been launched (used in conjunction with
 * ALF_SEND_LAUNCH_REQUEST_TO_UI_TO_HANDLE). (Set to NULL/0 if you don't
 * want to send anything).
 * The acknowledgement will come with three arguments: the GeodeHandle
 * (non-NULL if successful), a word value which will be zero if there was
 * an error, and the word value set in ALB_userLoackAckID (below).*/
optr 				ALB_userLoadAckOutput;
Message 				ALB_userLoadAckMessage;
/* ALB_userLoadAckID:
 * ID sent out via above action descriptor, if any. */
word 				ALB_userLoadAckID;
/* ALB_extraData:
 * Extra data to send to process (possibly a handle to
 * block containing arguments). */
word 				ALB_extraData;
} AppLaunchBlock;

This structure is used when an application is first starting up. It is an argument of various messages which will be intercepted by system classes. The first fields ( ALB_appRef , ALB_appMode , ALB_launchFlags , and ALB_uiLevel ) are preserved in the application's state file. The other information must be set correctly on launch.

AppLaunchFlags

typedef ByteFlags AppLaunchFlags;
#define ALF_SEND_LAUNCH_REQUEST_TO_UI_TO_HANDLE											0x80
#define ALF_OPEN_IN_BACK											0x40

ApplicationStates

typedef ByteFlags ApplicationStates;
#define AS_QUITTING								0x80
#define AS_DETACHING								0x40
#define AS_FOCUSABLE 								0x20
#define AS_MODELABLE 								0x10
#define AS_NOT_USER_INTERACTABLE 								0x08
#define AS_RECEIVED_APP_OBJECT_DETACH 								0x04
#define AS_ATTACHED_TO_STATE_FILE 								0x02
#define AS_ATTACHING 								0x01

ArcCloseType

typedef enum /* word */ {
ACT_OPEN,
ACT_CHORD,
ACT_PIE
} ArcCloseType;

This structure is used when filling arcs.

AreaAttr

typedef struct {
byte 		AA_colorFlag;
RGBValue 		AA_color;
SysDrawMask 		AA_mask;
ColorMapMode		AA_mapMode;
} AreaAttr;

ArgumentStackElement

typedef struct {
EvalStackArgumentType ASE_type;
EvalStackArgumentData ASE_data;
} ArgumentStackElement;

BBFixed

typedef struct {
byte BBF_frac;
byte BBF_int;
} BBFixed;

This structure represents an 8.8 fixed point number.

BBFixedAsWord

typedef word BBFixedAsWord;

This structure represents an 8.8 fixed point number.

Bitmap

typedef struct {
word	B_width;				/* In bitmap pixels */
word	B_height;				/* In bitmap pixels */
byte	B_compact;				/* A BMCompact value */
byte	B_type;				/* A BMFormat | BMType value */
} Bitmap;

This data structure provides some information about a simple graphics bitmap. It normally acts as the header for a set of bitmap data.

The bitmap data itself is organized into scan lines. If the bitmap has a mask (if the BMT_MASK bit is set in the B_type field), the first information for the scan line will be its mask information. There will be one bit of mask information for each pixel in the scan line (i.e. a number of bits equal to the bitmap width). The actual bitmap data for the scan line starts at the next byte boundary. For each pixel there will be a number of bits of color data, said number depending on the BMFormat value in the B_type field. The data for the next scan line will begin at the next byte boundary.

Thus, a 7x7 bitmap depicting an inverse "x" might appear:

(Bitmap)		{7, 7, BMC_UNCOMPACTED, BMF_MONO };
(byte)[]		{0x82, 		/* 10000010 */
         0x44, 		/* 01000100 */
         0x28, 		/* 00101000 */
         0x10, 		/* 00010000 */
         0x28, 		/* 00101000 */
         0x44, 		/* 01000100 */
         0x82 }; 		/* 10000010 */

A 3x3 color "-" shape with a a "+" shaped mask might appear:

(Bitmap)		{ 3, 3, BMC_UNCOMPACTED,
         (BMF_4BIT | BMT_MASK)};
(byte) []		{/* scan line 1: */
         0x40, 		/* mask: 010 */
         0, 0		/* data: 000 */

         /* scan line 2: */
         0xE0, 		/* mask: 111 */
         0x43, 0x20, 		/* data: 432 */

         /* scan line 3: */
         0x40, 		/* mask: 010 */
         0, 0 };		/* data: 000 */

If standard BMC_PACKBITS compression is used, then the mask (if any) and color data for the bitmap is compressed using the Macintosh PackBits standard. Under this system, to uncompress the data for a scan line, follow the loop:

  1. Read a byte.
  2. If the byte read in step (1) is between -1 and -127, read the next byte and copy it into the target buffer from +2 to +128 times.
  3. If the byte read in step (1) is between +1 and +127, read the next 1 to 127 bytes and copy them into the target buffer.
  4. If the byte read in step (1) is -128, ignore it.
  5. You're ready to read in the next batch of data; go back to step (1).

Thus a 16x4 color "=" with a matching mask would appear:

(Bitmap) 	{15, 3, BMC_PACKBITS, BMF_4BIT | BMT_MASK } ;
(byte) []		{/* scan line 1: */
            /* mask: 2 repetitions of 0xff */
         0xff, 0xff,
            /* data: 16 repetitions of 0x14 */
         0xf0, 0x14,
        /* scan line 2: */
            /* mask: 2 repetitions of 0x00 */
            /* data: 16 repetitions of 0x00 */
            /* total: 18 repetitions of 0x00 */
         0xee, 0x00,
        /* scan line 3: */
            /* mask: 2 repetitions of 0x00 */
            /* data: 16 repetitions of 0x00 */
            /* total: 18 repetitions of 0x00 */
         0xee, 0x00,
        /* scan line 4: */
            /* mask: 2 repetitions of 0xff */
         0xff, 0xff,
            /* data: 16 repetitions of 0x14 */
         0xf0, 0x14};

See Also: CBitmap.

BitmapMode

typedef WordFlags BitmapMode;
#define BM_EDIT_MASK					0x0002
#define BM_CLUSTERED_DITHER					0x0001

BlockFromTransferBlockID

VMBlockHandle BlockFromTransferBlockID(id);
        TransferBlockID		id;

This macro extracts the block handle from the given TransferBlockID value.

BlockIDFromFileAndBlock

TransferBlockID BlockIDFromFileAndBlock(f, b);
        VMFileHandle		f;
        VMBlockHandle		b;

This macro creates a TransferBlockID value from the given file and block handles.

BLTMode

See GrBitBlt() .

BMCompact

typedef ByteEnum ByteCompact;
#define BMC_UNCOMPACTED				0
#define BMC_PACKBITS				1
#define BMC_USER_DEFINED				0x80

This data structure is used to specify what sort of compaction is used to store a graphics bitmap.

BMDestroy

See entry for GrDestroyBitmap() .

BMFormat

typedef ByteEnum BMFormat
#define BMF_MONO 0
#define BMF_4BIT 1
#define BMF_8BIT 2
#define BMF_24BIT 3
#define BMF_4CMYK 4

This enumerated type determines a graphics bitmap's depth.

BMType

typedef ByteFlags BMType;
#define BMT_PALETTE				0x40
#define BMT_HUGE 				0x20
#define BMT_MASK 				0x10
#define BMT_COMPLEX 				0x08
#define BMT_FORMAT 				0x07

This structure is used to store various facts about a graphics bitmap.

Boolean

typedef word Boolean;

Booleans represent true/false values. If the Boolean is false , it will evaluate to zero; otherwise, it will be non-zero.

Button

typedef ByteEnum Button;
#define BUTTON_0				0
#define BUTTON_1				1
#define BUTTON_2				2
#define BUTTON_3				3

ButtonInfo

typedef ByteFlags ButtonInfo;
#define BI_PRESS				0x80
#define BI_DOUBLE_PRESS 				0x40
#define BI_B3_DOWN 				0x20
#define BI_B2_DOWN 				0x10
#define BI_B1_DOWN 				0x08
#define BI_B0_DOWN 				0x04
#define BI_BUTTON 				0x03

This structure contains the state of a mouse's buttons.

byte

typedef unsigned char byte;

ByteEnum

typedef byte ByteEnum;

ByteFlags

typedef byte ByteFlags;

CallbackType

typedef ByteEnum CallbackType;
#define CT_FUNCTION_TO_TOKEN					0
#define CT_NAME_TO_TOKEN 					1
#define CT_CHECK_NAME_EXISTS 					2
#define CT_CHECK_NAME_SPACE 					3
#define CT_EVAL_FUNCTION 					4
#define CT_LOCK_NAME 					5
#define CT_UNLOCK 					6
#define CT_FORMAT_FUNCTION 					7
#define CT_FORMAT_NAME 					8
#define CT_CREATE_CELL					9
#define CT_EMPTY_CELL 					10
#define CT_NAME_TO_CELL 					11
#define CT_FUNCTION_TO_CELL 					12
#define CT_DEREF_CELL 					13
#define CT_SPECIAL_FUNCTION 					14

CBitmap

typedef struct {
Bitmap 	CB_simple;
word 	CB_startScan;
word 	CB_numScans;
word 	CB_devInfo;
word 	CB_data;
word 	CB_palette;
word 	CB_xres;
word	CB_yres;
} CBitmap;

The CBitmap structure contains the information for a "complex" bitmap. Use the CBitmap structure to hold bitmaps which need to keep track of resolution information, a palette, or a mask.

CellFunctionParameterFlags

typedef ByteFlags CellFunctionParameterFlags;
#define CFPF_DIRTY 0x80 /* apps may read or change this. */
#define CFPF_NO_FREE_COUNT 0x07

CellFunctionParameters

typedef struct {
CellFunctionParameterFlags				CFP_flags;
VMFileHandle				CFP_file;						/* File containing cells */
VMBlockHandle				CFP_rowBlocks[N_ROW_BLOCKS];
} CellFunctionParameters;

This structure is used to pass specifics about a cell file to the cell library routines. Some of the data in the CellFunctionParameters structure is opaque to the application; others may be examined or changed by the application. The CellFunctionParameters structure contains the following fields:

CFP_flags
The cell library uses this byte for miscellaneous bookkeeping. When you create the structure, initialize this field to zero. There is only one flag which you should check or change; that is the flag CFPF_dirty . The cell library routines set this bit whenever they change the CellFunctionParameters structure, thus indicating that the structure ought to be resaved. After you save it, you may clear this bit.
CFP_file
This field must contain the VM file handle of the cell file. This field must be set each time you open the file.
CFP_rowBlocks
This field is an array of VM block handles, one for every existing or potential row block. The length of this array is N_ROW_BLOCKS (defined in cell.h ). When you create a cell file, initialize all of these handles to zero; do not access or change this field thereafter.

Include: cell.h

Warnings: The cell library expects the CellFunctionParameters structure to remain motionless for the duration of a call. Therefore, if you allocate it as a DB item in the cell file, you must not have the structure be an ungrouped item.

CellRange

typedef struct {
CellReference			CR_start;
CellReference			CR_end;
} CellRange;

CellReference

typedef struct {
CellRowColumn			CR_row;
CellRowColumn			CR_column;
} CellReference;

CellRowColumn

typedef WordFlags CellRowColumn;
#define CRC_ABSOLUTE				0x8000
#define CRC_VALUE				0x7fff

CharacterSet

typedef ByteEnum CharacterSet;
#define CS_BSW				0
#define CS_CONTROL 				0xff
#define CS_UI_FUNCS 				0xfe
#define VC_ISANSI 				CS_BSW
#define VC_ISCTRL 				CS_CONTROL
#define VC_ISUI 				CS_UI_FUNCS

CharFlags

typedef ByteFlags CharFlags;
#define CF_STATE_KEY				0x80
#define CF_EXTENDED 				0x10
#define CF_TEMP_ACCENT 				0x08
#define CF_FIRST_PRESS 				0x04
#define CF_REPEAT_PRESS 				0x02
#define CF_RELEASE 				0x01

Chars

typedef ByteEnum Chars;
#define C_NULL				0x0 /* NULL */
#define C_CTRL_A				0x1 /* <ctrl>-A */
#define C_CTRL_B				0x2 /* <ctrl>-B */
#define C_CTRL_C				0x3 /* <ctrl>-C */
#define C_CTRL_D				0x4 /* <ctrl>-D */
#define C_CTRL_E				0x5 /* <ctrl>-E */
#define C_CTRL_F				0x6 /* <ctrl>-F */
#define C_CTRL_G				0x7 /* <ctrl>-G */
#define C_CTRL_H				0x8 /* <ctrl>-H */
#define C_TAB				0x9 /* TAB */
#define C_LINEFEED				0xa /* LINE FEED */
#define C_CTRL_K				0xb /* <ctrl>-K */
#define C_CTRL_L				0xc /* <ctrl>-L */
#define C_ENTER				0xd /* ENTER or CR */
#define C_SHIFT_OUT				0xe /* <ctrl>-N */
#define C_SHIFT_IN				0xf /* <ctrl>-O */
#define C_CTRL_P				0x10 /* <ctrl>-P */
#define C_CTRL_Q				0x11 /* <ctrl>-Q */
#define C_CTRL_R				0x12 /* <ctrl>-R */
#define C_CTRL_S				0x13 /* <ctrl>-S */
#define C_CTRL_T				0x14 /* <ctrl>-T */
#define C_CTRL_U				0x15 /* <ctrl>-U */
#define C_CTRL_V				0x16 /* <ctrl>-V */
#define C_CTRL_W				0x17 /* <ctrl>-W */
#define C_CTRL_X				0x18 /* <ctrl>-X */
#define C_CTRL_Y				0x19 /* <ctrl>-Y */
#define C_CTRL_Z				0x1a /* <ctrl>-Z */
#define C_ESCAPE				0x1b /* ESC */
#define C_NULL_WIDTH				0x19 /* null width character */
#define C_GRAPHIC				0x1a /* Graphic in text. */
#define C_THINSPACE				0x1b /* 1/4 width space */
#define C_ENSPACE				0x1c /* En-space, fixed width */
#define C_EMSPACE				0x1d /* Em-space, fixed width. */
#define C_NONBRKHYPHEN				0x1e /* Non breaking hyphen. */
#define C_OPTHYPHEN				0x1f /* Optional hyphen, only drawn at eol */
#define C_SPACE				` '
#define C_EXCLAMATION				`!'
#define C_QUOTE				`"'
#define C_NUMBER_SIGN				`#'
#define C_DOLLAR_SIGN				`$'
#define C_PERCENT				`%'
#define C_AMPERSAND				`&'
#define C_SNG_QUOTE				0x27
#define C_LEFT_PAREN				`('
#define C_RIGHT_PAREN				`)'
#define C_ASTERISK				`*'
#define C_PLUS				`+'
#define C_COMMA				`,'
#define C_MINUS				`-'
#define C_PERIOD				 '.'
#define C_SLASH				`/'
#define C_ZERO				`0'
#define C_ONE				`1'
#define C_TWO				`2'
#define C_THREE 				`3'
#define C_FOUR 				`4'
#define C_FIVE 				`5'
#define C_SIX 				`6'
#define C_SEVEN 				`7'
#define C_EIGHT 				`8'
#define C_NINE 				`9'
#define C_COLON 				`:'
#define C_SEMICOLON 				`;'
#define C_LESS_THAN 				`<'
#define C_EQUAL 				`='
#define C_GREATER_THAN 				`>'
#define C_QUESTION_MARK 				`?'
#define C_AT_SIGN				0x40
#define C_CAP_A 				`A'
#define C_CAP_B 				`B'
#define C_CAP_C 				`C'
#define C_CAP_D 				`D'
#define C_CAP_E 				`E'
#define C_CAP_F 				`F'
#define C_CAP_G 				`G'
#define C_CAP_H 				`H'
#define C_CAP_I 				`I'
#define C_CAP_J 				`J'
#define C_CAP_K 				`K'
#define C_CAP_L 				`L'
#define C_CAP_M 				`M'
#define C_CAP_N 				`N'
#define C_CAP_O 				`O'
#define C_CAP_P 				`P'
#define C_CAP_Q 				`Q'
#define C_CAP_R 				`R'
#define C_CAP_S 				`S'
#define C_CAP_T 				`T'
#define C_CAP_U 				`U'
#define C_CAP_V 				`V'
#define C_CAP_W 				`W'
#define C_CAP_X 				`X'
#define C_CAP_Y 				`Y'
#define C_CAP_Z 				`Z'
#define C_LEFT_BRACKET				`['
#define C_BACKSLASH				0x5c
#define C_RIGHT_BRACKET				`]'
#define C_ASCII_CIRCUMFLEX				`^'
#define C_UNDERSCORE				`_'
#define C_BACKQUOTE				``'
#define C_SMALL_A				`a'
#define C_SMALL_B				`b'
#define C_SMALL_C				`c'
#define C_SMALL_D 				`d'
#define C_SMALL_E 				`e'
#define C_SMALL_F 				`f'
#define C_SMALL_G 				`g'
#define C_SMALL_H 				`h'
#define C_SMALL_I 				`i'
#define C_SMALL_J 				`j'
#define C_SMALL_K 				`k'
#define C_SMALL_L 				`l'
#define C_SMALL_M 				`m'
#define C_SMALL_N 				`n'
#define C_SMALL_O 				`o'
#define C_SMALL_P 				`p'
#define C_SMALL_Q 				`q'
#define C_SMALL_R 				`r'
#define C_SMALL_S 				`s'
#define C_SMALL_T 				`t'
#define C_SMALL_U 				`u'
#define C_SMALL_V 				`v'
#define C_SMALL_W 				`w'
#define C_SMALL_X 				`x'
#define C_SMALL_Y 				`y'
#define C_SMALL_Z 				`z'
#define C_LEFT_BRACE 				`{'
#define C_VERTICAL_BAR 				`|'
#define C_RIGHT_BRACE 				`}'
#define C_ASCII_TILDE 				`~'
#define C_DELETE				0x7f
#define C_UA_DIERESIS				0x80
#define C_UA_RING				0x81
#define C_UC_CEDILLA				0x82
#define C_UE_ACUTE				0x83
#define C_UN_TILDE				0x84
#define C_UO_DIERESIS				0x85
#define C_UU_DIERESIS				0x86
#define C_LA_ACUTE				0x87
#define C_LA_GRAVE				0x88
#define C_LA_CIRCUMFLEX				0x89
#define C_LA_DIERESIS				0x8a
#define C_LA_TILDE				0x8b
#define C_LA_RING				0x8c
#define C_LC_CEDILLA				0x8d
#define C_LE_ACUTE				0x8e
#define C_LE_GRAVE				0x8f
#define C_LE_CIRCUMFLEX				0x90
#define C_LE_DIERESIS				0x91
#define C_LI_ACUTE				0x92
#define C_LI_GRAVE				0x93
#define C_LI_CIRCUMFLEX				0x94
#define C_LI_DIERESIS				0x95
#define C_LN_TILDE				0x96
#define C_LO_ACUTE				0x97
#define C_LO_GRAVE				0x98
#define C_LO_CIRCUMFLEX				0x99
#define C_LO_DIERESIS				0x9a
#define C_LO_TILDE				0x9b
#define C_LU_ACUTE				0x9c
#define C_LU_GRAVE				0x9d
#define C_LU_CIRCUMFLEX				0x9e
#define C_LU_DIERESIS				0x9f
#define C_DAGGER				0xa0
#define C_DEGREE				0xa1
#define C_CENT				0xa2
#define C_STERLING				0xa3
#define C_SECTION				0xa4
#define C_BULLET				0xa5
#define C_PARAGRAPH				0xa6
#define C_GERMANDBLS				0xa7
#define C_REGISTERED				0xa8
#define C_COPYRIGHT				0xa9
#define C_TRADEMARK				0xaa
#define C_ACUTE				0xab
#define C_DIERESIS				0xac
#define C_NOTEQUAL				0xad
#define C_U_AE				0xae
#define C_UO_SLASH				0xaf
#define C_INFINITY				0xb0
#define C_PLUSMINUS				0xb1
#define C_LESSEQUAL				0xb2
#define C_GREATEREQUAL				0xb3
#define C_YEN				0xb4
#define C_L_MU				0xb5
#define C_L_DELTA				0xb6
#define C_U_SIGMA				0xb7
#define C_U_PI				0xb8
#define C_L_PI				0xb9
#define C_INTEGRAL				0xba
#define C_ORDFEMININE				0xbb
#define C_ORDMASCULINE				0xbc
#define C_U_OMEGA				0xbd
#define C_L_AE				0xbe
#define C_LO_SLASH				0xbf
#define C_QUESTIONDOWN				0xc0
#define C_EXCLAMDOWN				0xc1
#define C_LOGICAL_NOT				0xc2
#define C_ROOT				0xc3
#define C_FLORIN				0xc4
#define C_APPROX_EQUAL				0xc5
#define C_U_DELTA				0xc6
#define C_GUILLEDBLLEFT				0xc7
#define C_GUILLEDBLRIGHT				0xc8
#define C_ELLIPSIS				0xc9
#define C_NONBRKSPACE				0xca
#define C_UA_GRAVE				0xcb
#define C_UA_TILDE				0xcc
#define C_UO_TILDE				0xcd
#define C_U_OE				0xce
#define C_L_OE				0xcf
#define C_ENDASH				0xd0
#define C_EMDASH				0xd1
#define C_QUOTEDBLLEFT				0xd2
#define C_QUOTEDBLRIGHT				0xd3
#define C_QUOTESNGLEFT				0xd4
#define C_QUOTESNGRIGHT				0xd5
#define C_DIVISION				0xd6
#define C_DIAMONDBULLET				0xd7
#define C_LY_DIERESIS				0xd8
#define C_UY_DIERESIS				0xd9
#define C_FRACTION				0xda
#define C_CURRENCY				0xdb
#define C_GUILSNGLEFT				0xdc
#define C_GUILSNGRIGHT				0xdd
#define C_LY_ACUTE				0xde
#define C_UY_ACUTE				0xdf
#define C_DBLDAGGER				0xe0
#define C_CNTR_DOT				0xe1
#define C_SNGQUOTELOW				0xe2
#define C_DBLQUOTELOW				0xe3
#define C_PERTHOUSAND				0xe4
#define C_UA_CIRCUMFLEX				0xe5
#define C_UE_CIRCUMFLEX				0xe6
#define C_UA_ACUTE				0xe7
#define C_UE_DIERESIS				0xe8
#define C_UE_GRAVE				0xe9
#define C_UI_ACUTE				0xea
#define C_UI_CIRCUMFLEX				0xeb
#define C_UI_DIERESIS				0xec
#define C_UI_GRAVE				0xed
#define C_UO_ACUTE				0xee
#define C_UO_CIRCUMFLEX				0xef
#define C_LOGO				0xf0
#define C_UO_GRAVE				0xf1
#define C_UU_ACUTE				0xf2
#define C_UU_CIRCUMFLEX				0xf3
#define C_UU_GRAVE				0xf4
#define C_LI_DOTLESS				0xf5
#define C_CIRCUMFLEX				0xf6
#define C_TILDE				0xf7
#define C_MACRON				0xf8
#define C_BREVE				0xf9
#define C_DOTACCENT				0xfa
#define C_RING				0xfb
#define C_CEDILLA				0xfc
#define C_HUNGARUMLAT				0xfd
#define C_OGONEK				0xfe
#define C_CARON				0xff
/*
 * common shortcuts for low 32 codes
 */
#define C_NUL				C_NULL
#define C_STX				C_CTRL_B
#define C_ETX				C_CTRL_C
#define C_BEL				C_CTRL_G
#define C_BS				C_CTRL_H
#define C_HT				C_CTRL_I
#define C_VT				C_CTRL_K
#define C_FF				C_CTRL_L
#define C_SO				C_CTRL_N
#define C_SI				C_CTRL_O
#define C_DC1				C_CTRL_Q
#define C_DC2				C_CTRL_R
#define C_DC3				C_CTRL_S
#define C_DC4				C_CTRL_T
#define C_CAN				C_CTRL_X
#define C_EM				C_CTRL_Y
#define C_ESC				C_ESCAPE
/*
 * Some alternative names
 */
#define C_CR				C_ENTER
#define C_CTRL_M				C_ENTER
#define C_CTRL_I				C_TAB
#define C_CTRL_J				C_LINEFEED
#define C_LF				C_LINEFEED
#define C_CTRL_N				C_SHIFT_OUT
#define C_CTRL_O				C_SHIFT_IN
#define C_FS				C_ENSPACE
#define C_FIELD_SEP				C_FS
#define C_HYPHEN				C_MINUS
#define C_GRAVE				C_BACKQUOTE
#define C_PARTIAL_DIFF				C_L_DELTA
#define C_SUM				C_U_SIGMA
#define C_PRODUCT				C_U_PI
#define C_RADICAL				C_ROOT
#define C_LOZENGE				C_DIAMONDBULLET

Text characters may be represented by the standard C type char or by the GEOS type Chars. The difference shows up in debugging. If printing the value of a string as char, then the debugger will output ASCII text. If the string is treated as Chars, then the debugger will print out the constant names.

Include: char.h

ChunkArrayHeader

typedef struct {
word	CAH_count;			/* # of elements in chunk array */
word	CAH_elementSize;			/* Size of each element (in bytes) */
word	CAH_curOffset;			/* For internal use only */
word	CAH_offset;			/* Offset from start of chunk to first element */
} ChunkArrayHeader;

Every chunk array begins with a ChunkArrayHeader . This structure contains information about the chunk array. Applications should never change the contents of the ChunkArrayHeader ; only the chunk array routines should do this. However, applications can examine the header if they wish.

Contents: There are four word-length fields in the ChunkArrayHeader:

CAH _count
This word contains the number of elements in the chunk array.
CAH _elementSize
This word contains the size of each element (in bytes). If the elements are variable-sized, CAH_elementSize will be zero.
CAH _curOffset
This word is used by ChunkArrayEnum() for bookkeeping.
CAH _offset
This is the offset from the start of the chunk to the first element in the array.
Chunk handles are offsets into a local memory heap. To find the current

ChunkHandle

typedef word ChunkHandle;

location of a chunk in an LMem heap, combine the segment address of the heap with the chunk handle. From this location you can read the current offset of the chunk itself.

See Also: optr, LMemDeref().

ChunkMapList

typedef struct {
word	CML_source;
word	CML_dest;
} ChunkMapList;

ClassFlags

typedef ByteFlags ClassFlags;
#define CLASSF_HAS_DEFAULT						0x80
#define CLASSF_MASTER_CLASS 						0x40
#define CLASSF_VARIANT_CLASS 						0x20
#define CLASSF_DISCARD_ON_SAVE 						0x10
#define CLASSF_NEVER_SAVED						0x08
#define CLASSF_HAS_RELOC 						0x04
#define CLASSF_C_HANDLERS 						0x02

This record is stored in the ClassStruct structure's Class_flags field. These flags are internal and may not be set or retrieved directly. See the entry on @class for more information about these flags.

ClassStruct

typedef	struct	_ClassStruct {
struct _ClassStruct *Class_superClass;		/* superclass pointer */
word		Class_masterOffset;					/* offset to master offset in chunk */
word 		Class_methodCount;					/* number of methods in this class */
word 		Class_instanceSize;					/* size of entire master group */
word 		Class_vdRelocTable;					/* offset to vardata relocation table */
word 		Class_relocTable;					/* offset to relocation table */
ClassFlags 		Class_flags;					/* a record of ClassFlags */
byte 		Class_masterMessages;					/* internal flags for optimization */
} ClassStruct;

This is the structure that defines a class. It is internal and used only very rarely by anything other than the kernel and the UI.

ClipboardItemFlags

typedef WordFlags ClipboardItemFlags;
#define CIF_QUICK				0x4000
#define TIF_NORMAL				0x0000

ClipboardItemFormat

typedef enum /* word */ {
CIF_TEXT,
CIF_GRAPHICS_STRING,
CIF_FILES,
CIF_SPREADSHEET,
CIF_INK,
CIF_GROBJ,
CIF_GEODEX,
CIF_BITMAP,
CIF_SOUND_SYNTH,
CIF_SOUND_SAMPLE
} ClipboardItemFormat;

ClipboardItemFormatID

typedef dword ClipboardItemFormatID;

ClipboardItemFormatInfo

typedef struct {
ClipboardItemFormatID				CIFI_format;
word				CIFI_extra1;
word				CIFI_extra2;
VMChain				CIFI_vmChain;
GeodeToken				CIFI_renderer;
} ClipboardItemFormatInfo;

ClipboardItemHeader

typedef struct {
optr				CIH_owner;
ClipboardItemFlags				CIH_flags;
ClipboardItemNameBuffer				CIH_name;
word				CIH_formatCount;
optr				CIH_sourceID;
FormatArray				CIH_formats;
dword				CIH_reserved;
} ClipboardItemHeader;

ClipboardItemNameBuffer

typedef char ClipboardItemNameBuffer[CLIPBOARD_ITEM_NAME_LENGTH+1];

ClipboardQueryArgs

See ClipboardQueryItem() .

ClipboardQuickNotifyFlags

typedef WordFlags ClipboardQuickNotifyFlags;
#define CQNF_ERROR						0x8000
#define CQNF_SOURCE_EQUAL_DEST 						0x4000
#define CQNF_MOVE 						0x2000
#define CQNF_COPY 						0x1000
#define CQNF_NO_OPERATION 						0x0800
#define CQNF_UNUSED 						0x04ff

These flags give information about the success or failure of a quick transfer operation.

ClipboardQuickTransferFeedback

typedef enum {
CQTF_SET_DEFAULT,
CQTF_CLEAR_DEFAULT,
CQTF_MOVE,
CQTF_COPY,
CQTF_CLEAR
} ClipboardQuickTransferFeedback;

ClipboardQuickTransferFlags

See ClipboardStartQuickTransfer() .

ClipboardQuickTransferRegionInfo

See ClipboardStartQuickTransfer() .

ClipboardRequestArgs

See entry for ClipboardRequestItemFormat() .

CMYKTransfer

typedef struct {
byte	CMYKT_cyan[256];
byte	CMYKT_magenta[256];
byte	CMYKT_yellow[256];
byte	CMYKT_black[256];
} CMYKTransfer;

Color

typedef ByteEnum Color;
#define C_BLACK				0
#define C_BLUE 				1
#define C_GREEN 				2
#define C_CYAN 				3
#define C_RED 				4
#define C_VIOLET 				5
#define C_BROWN 				6
#define C_LIGHT_GRAY 				7
#define C_DARK_GRAY 				8
#define C_LIGHT_BLUE 				9
#define C_LIGHT_GREEN 				10
#define C_LIGHT_CYAN 				11
#define C_LIGHT_RED 				12
#define C_LIGHT_VIOLET 				13
#define C_YELLOW 				14
#define C_WHITE 				15

#define C_GRAY_0 				0x10
#define C_GRAY_7 				0x11
#define C_GRAY_13				0x12
#define C_GRAY_20				0x13
#define C_GRAY_27 				0x14
#define C_GRAY_33 				0x15
#define C_GRAY_40 				0x16
#define C_GRAY_47 				0x17
#define C_GRAY_53 				0x18
#define C_GRAY_60 				0x19
#define C_GRAY_68 				0x1a
#define C_GRAY_73 				0x1b
#define C_GRAY_80 				0x1c
#define C_GRAY_88 				0x1d
#define C_GRAY_93 				0x1e
#define C_GRAY_100 				0x1f

#define C_UNUSED_0 				0x20
#define C_UNUSED_1 				0x21
#define C_UNUSED_2 				0x22
#define C_UNUSED_3 				0x23
#define C_UNUSED_4 				0x24
#define C_UNUSED_5 				0x25
#define C_UNUSED_6 				0x26
#define C_UNUSED_7 				0x27

#define C_R0_G0_B0 				0x28
#define C_R0_G0_B1 				0x29
#define C_R0_G0_B2 				0x2a
#define C_R0_G0_B3 				0x2b
#define C_R0_G0_B4 				0x2c
#define C_R0_G0_B5 				0x2d
#define C_R0_G1_B0 				0x2e
#define C_R0_G1_B1 				0x2f
#define C_R0_G1_B2 				0x30
#define C_R0_G1_B3 				0x31
#define C_R0_G1_B4 				0x32
#define C_R0_G1_B5 				0x33
#define C_R0_G2_B0 				0x34
#define C_R0_G2_B1 				0x35
#define C_R0_G2_B2 				0x36
#define C_R0_G2_B3 				0x37
#define C_R0_G2_B4 				0x38
#define C_R0_G2_B5 				0x39

#define C_R0_G3_B0 				0x3a
#define C_R0_G3_B1 				0x3b
#define C_R0_G3_B2 				0x3c
#define C_R0_G3_B3 				0x3d
#define C_R0_G3_B4 				0x3e
#define C_R0_G3_B5 				0x3f
#define C_R0_G4_B0 				0x40
#define C_R0_G4_B1 				0x41
#define C_R0_G4_B2 				0x42
#define C_R0_G4_B3 				0x43
#define C_R0_G4_B4 				0x44
#define C_R0_G4_B5 				0x45
#define C_R0_G5_B0 				0x46
#define C_R0_G5_B1 				0x47
#define C_R0_G5_B2 				0x48
#define C_R0_G5_B3 				0x49
#define C_R0_G5_B4 				0x4a
#define C_R0_G5_B5 				0x4b

#define C_R1_G0_B0 				0x4c
#define C_R1_G0_B1 				0x4d
#define C_R1_G0_B2 				0x4e
#define C_R1_G0_B3 				0x4f
#define C_R1_G0_B4 				0x50
#define C_R1_G0_B5 				0x51
#define C_R1_G1_B0 				0x52
#define C_R1_G1_B1 				0x53
#define C_R1_G1_B2 				0x54
#define C_R1_G1_B3 				0x55
#define C_R1_G1_B4 				0x56
#define C_R1_G1_B5 				0x57
#define C_R1_G2_B0 				0x58
#define C_R1_G2_B1 				0x59
#define C_R1_G2_B2 				0x5a
#define C_R1_G2_B3 				0x5b
#define C_R1_G2_B4 				0x5c
#define C_R1_G2_B5 				0x5d

#define C_R1_G3_B0 				0x5e
#define C_R1_G3_B1 				0x5f
#define C_R1_G3_B2 				0x60
#define C_R1_G3_B3 				0x61
#define C_R1_G3_B4 				0x62
#define C_R1_G3_B5 				0x63
#define C_R1_G4_B0 				0x64
#define C_R1_G4_B1 				0x65
#define C_R1_G4_B2 				0x66
#define C_R1_G4_B3 				0x67
#define C_R1_G4_B4 				0x68
#define C_R1_G4_B5 				0x69
#define C_R1_G5_B0 				0x6a
#define C_R1_G5_B1 				0x6b
#define C_R1_G5_B2 				0x6c
#define C_R1_G5_B3 				0x6d
#define C_R1_G5_B4 				0x6e
#define C_R1_G5_B5 				0x6f

#define C_R2_G0_B0 				0x70
#define C_R2_G0_B1 				0x71
#define C_R2_G0_B2 				0x72
#define C_R2_G0_B3 				0x73
#define C_R2_G0_B4 				0x74
#define C_R2_G0_B5 				0x75
#define C_R2_G1_B0 				0x76
#define C_R2_G1_B1 				0x77
#define C_R2_G1_B2 				0x78
#define C_R2_G1_B3 				0x79
#define C_R2_G1_B4 				0x7a
#define C_R2_G1_B5 				0x7b
#define C_R2_G2_B0 				0x7c
#define C_R2_G2_B1 				0x7d
#define C_R2_G2_B2 				0x7e
#define C_R2_G2_B3 				0x7f
#define C_R2_G2_B4 				0x80
#define C_R2_G2_B5 				0x81

#define C_R2_G3_B0 				0x82
#define C_R2_G3_B1 				0x83
#define C_R2_G3_B2 				0x84
#define C_R2_G3_B3 				0x85
#define C_R2_G3_B4 				0x86
#define C_R2_G3_B5 				0x87
#define C_R2_G4_B0 				0x88
#define C_R2_G4_B1 				0x89
#define C_R2_G4_B2 				0x8a
#define C_R2_G4_B3 				0x8b
#define C_R2_G4_B4 				0x8c
#define C_R2_G4_B5 				0x8d
#define C_R2_G5_B0 				0x8e
#define C_R2_G5_B1 				0x8f
#define C_R2_G5_B2 				0x90
#define C_R2_G5_B3 				0x91
#define C_R2_G5_B4 				0x92
#define C_R2_G5_B5 				0x93

#define C_R3_G0_B0 				0x94
#define C_R3_G0_B1 				0x95
#define C_R3_G0_B2 				0x96
#define C_R3_G0_B3 				0x97
#define C_R3_G0_B4 				0x98
#define C_R3_G0_B5 				0x99
#define C_R3_G1_B0 				0x9a
#define C_R3_G1_B1 				0x9b
#define C_R3_G1_B2 				0x9c
#define C_R3_G1_B3 				0x9d
#define C_R3_G1_B4 				0x9e
#define C_R3_G1_B5 				0x9f
#define C_R3_G2_B0 				0xa0
#define C_R3_G2_B1 				0xa1
#define C_R3_G2_B2 				0xa2
#define C_R3_G2_B3 				0xa3
#define C_R3_G2_B4 				0xa4
#define C_R3_G2_B5 				0xa5

#define C_R3_G3_B0 				0xa6
#define C_R3_G3_B1 				0xa7
#define C_R3_G3_B2 				0xa8
#define C_R3_G3_B3 				0xa9
#define C_R3_G3_B4 				0xaa
#define C_R3_G3_B5 				0xab
#define C_R3_G4_B0 				0xac
#define C_R3_G4_B1 				0xad
#define C_R3_G4_B2 				0xae
#define C_R3_G4_B3 				0xaf
#define C_R3_G4_B4 				0xb0
#define C_R3_G4_B5 				0xb1
#define C_R3_G5_B0 				0xb2
#define C_R3_G5_B1 				0xb3
#define C_R3_G5_B2 				0xb4
#define C_R3_G5_B3 				0xb5
#define C_R3_G5_B4 				0xb6
#define C_R3_G5_B5 				0xb7

#define C_R4_G0_B0 				0xb8
#define C_R4_G0_B1 				0xb9
#define C_R4_G0_B2 				0xba
#define C_R4_G0_B3 				0xbb
#define C_R4_G0_B4 				0xbc
#define C_R4_G0_B5 				0xbd
#define C_R4_G1_B0 				0xbe
#define C_R4_G1_B1 				0xbf
#define C_R4_G1_B2 				0xc0
#define C_R4_G1_B3 				0xc1
#define C_R4_G1_B4 				0xc2
#define C_R4_G1_B5 				0xc3
#define C_R4_G2_B0 				0xc4
#define C_R4_G2_B1 				0xc5
#define C_R4_G2_B2 				0xc6
#define C_R4_G2_B3 				0xc7
#define C_R4_G2_B4 				0xc8
#define C_R4_G2_B5 				0xc9

#define C_R4_G3_B0 				0xca
#define C_R4_G3_B1 				0xcb
#define C_R4_G3_B2 				0xcc
#define C_R4_G3_B3 				0xcd
#define C_R4_G3_B4 				0xce
#define C_R4_G3_B5 				0xcf
#define C_R4_G4_B0 				0xd0
#define C_R4_G4_B1 				0xd1
#define C_R4_G4_B2 				0xd2
#define C_R4_G4_B3 				0xd3
#define C_R4_G4_B4 				0xd4
#define C_R4_G4_B5 				0xd5
#define C_R4_G5_B0 				0xd6
#define C_R4_G5_B1 				0xd7
#define C_R4_G5_B2 				0xd8
#define C_R4_G5_B3 				0xd9
#define C_R4_G5_B4 				0xda
#define C_R4_G5_B5 				0xdb

#define C_R5_G0_B0 				0xdc
#define C_R5_G0_B1 				0xdd
#define C_R5_G0_B2 				0xde
#define C_R5_G0_B3 				0xdf
#define C_R5_G0_B4 				0xe0
#define C_R5_G0_B5 				0xe1
#define C_R5_G1_B0 				0xe2
#define C_R5_G1_B1 				0xe3
#define C_R5_G1_B2 				0xe4
#define C_R5_G1_B3 				0xe5
#define C_R5_G1_B4 				0xe6
#define C_R5_G1_B5 				0xe7
#define C_R5_G2_B0 				0xe8
#define C_R5_G2_B1 				0xe9
#define C_R5_G2_B2 				0xea
#define C_R5_G2_B3 				0xeb
#define C_R5_G2_B4 				0xec
#define C_R5_G2_B5 				0xed
#define C_R5_G3_B0 				0xee
#define C_R5_G3_B1 				0xef
#define C_R5_G3_B2 				0xf0
#define C_R5_G3_B3 				0xf1
#define C_R5_G3_B4 				0xf2
#define C_R5_G3_B5 				0xf3
#define C_R5_G4_B0 				0xf4
#define C_R5_G4_B1 				0xf5
#define C_R5_G4_B2 				0xf6
#define C_R5_G4_B3 				0xf7
#define C_R5_G4_B4 				0xf8
#define C_R5_G4_B5 				0xf9
#define C_R5_G5_B0 				0xfa
#define C_R5_G5_B1 				0xfb
#define C_R5_G5_B2 				0xfc
#define C_R5_G5_B3 				0xfd
#define C_R5_G5_B4 				0xfe
#define C_R5_G5_B5 				0xff

#define C_LIGHT_GREY				C_LIGHT_GRAY
#define C_DARK_GREY				C_DARK_GRAY
#define C_BW_GREY				0x84

The Color enumerated type is used to encode color index values. Normally, the constant name will give a hint as to the color's composition. If an application has carried out sophisticated palette operations, however, then C_RED might actually correspond to, say, a shade of green.

Include: color.h

ColorFlag

typedef ByteEnum ColorFlag;
#define CF_INDEX			0
#define CF_GRAY			1
#define CF_SAME			2
#define CF_RGB			0x80

Several color-related commands accept colors in a variety of formats. The ColorFlag enumerated type is used to specify how the color is being described. The ColorFlag is normally used as part of a ColorQuad . See ColorQuad for information about how to interpret color specifications using ColorFlag s.

ColorMapMode

typedef ByteFlags ColorMapMode;
#define CMM_ON_BLACK 0x04				/* Set this bit if you're drawing on black */
#define CMM_MAP_TYPE 0x01				/* Either CMT_CLOSEST or CMT_DITHER) */
#define LAST_MAP_MODE				(CMM_MAP_TYPE | CMM_ON_BLACK)

This structure defines how the system will try to simulate colors not in the palette. If the map type is CMT_CLOSEST, the closest available color will be used. If the map type is CMT_DITHER, the system will mix together two or more close colors in a dithered pattern. If you will be drawing against a black background, you may wish to set the CMM_ON_BLACK flag.

ColorQuad

typedef struct {
byte 		CQ_redOrIndex;
ColorFlag	 	CQ_info;
byte 		CQ_green;
byte 		CQ_blue;
} ColorQuad;

This structure represents a color. The CQ_info field determines how the color is being described.

If the info field is CF_INDEX, then the color is being specified by its index, its place in the window's palette. The index is in the CQ_redOrIndex field; the the CQ_green and CQ_blue fields are meaningless for this specification.

If the info field is CF_RGB, then the color is specified by RGB (red, green, and blue) components. CQ_redOrIndex contains the color's red component, a number ranging from 0 to 255. The CQ_green and CQ_blue fields contain the color's green and blue components, respectively.

If the info field is CF_GRAY, then the color is being expressed as a grey scale. This is basically an optimized way of describing RGB colors where the red, green, and blue components are equal. The CQ_redOrIndex field contains the brightess, a number between 0 and 255. The CQ_green and CQ_blue fields are ignored.

When defining hatch patterns, it is possible have a CF_SAME info field. This means that the hatch lines should use the "same" color when drawing. That is, when hatching text, the text color will be used; when filling an area, the area color will be used. The CQ_redOrIndex , CQ_green , and CQ_blue fields are all ignored.

ColorQuadAsDWord

typedef dword ColorQuadAsDWord;

ColorTransfer

typedef struct {
RGBDelta				CT_data[125];
} ColorTransfer;

This structure consists of a 5x5x5 matrix of RGBDelta structures. This and be used to specify what sorts of adjustments to make to the color when displaying to a specific device. For instance, some color printers will wipe out certain colors if they try to use the amounts of ink suggested by the raw RGB values. The ColorTransfer structure thus serves to hold an array of "fudge factors" to tell the printer to use more or less ink than the raw RGB values would suggest.

ColorTransferData

typedef union {
MonoTransfer		CTD_mono;
RGBTransfer		CTD_rgb;
CMYKTransfer		CTD_cmyk;
} ColorTransferData;

ColorTransferType

typedef ByteEnum ColorTransferType;
#define CTT_MONO				 0
#define CTT_RGB 				 1
#define CTT_CMYK 				 2

CommonParameters

typedef struct {
word	CP_row;
word	CP_column;
word	CP_maxRow;
word	CP_maxColumn;
void	* CP_callback;
void	* CP_cellParams; /* ptr to an instance of SpreadsheetClass */
} CommonParameters;

CompChildFlags

typedef WordFlags CompChildFlags;
#define CCF_MARK_DIRTY				0x8000
#define CCF_REFERENCE				0x7fff
#define 		CCO_FIRST			0x0000
#define		CCO_LAST			0x7FFF
#define CCF_REFERENCE_OFFSET 0

A record used when adding, moving, or removing children in an object tree. The record has one flag and a value, as follows:

CCF_MARK_DIRTY
A flag indicating whether the object should be marked dirty at the end of the operation.
CCF_REFERENCE
A child number; when adding or moving a child, this is the child number after which the new object should be inserted. It can be any number less than 32768, or it can be either of the two constants shown above (CCO_FIRST or CCO_LAST).

CompSizeHintArgs

typedef struct {
            SpecWidth   CSHA_width;
            SpecHeight  CSHA_height;
            sword       CSHA_count;
} CompSizeHintArgs;
typedef WordFlags SpecWidth;
        #define SW_TYPE         0x8c00
        #define SW_DATA         0x03ff

typedef WordFlags SpecHeight;
        #define SH_TYPE         0x8c00
        #define SH_DATA         0x03ff
typedef ByteEnum SpecSizeType;
        #define SST_PIXELS                                0x0000
        #define SST_COUNT                                 0x0400
        #define SST_PCT_OF_FIELD_WIDTH    0x0800
        #define SST_PCT_OF_FIELD_HEIGHT   0x0c00
        #define SST_AVG_CHAR_WIDTHS       0x1000
        #define SST_WIDE_CHAR_WIDTHS      0x1400
        #define SST_LINES_OF_TEXT                 0x1800

        #define PCT_0   0x000
        #define PCT_5   0x033
        #define PCT_10  0x066
        #define PCT_15  0x099
        #define PCT_20  0x0cc
        #define PCT_25  0x100
        #define PCT_30  0x133
        #define PCT_35  0x166
        #define PCT_40  0x199
        #define PCT_45  0x1cc
        #define PCT_50  0x200
        #define PCT_55  0x233
        #define PCT_60  0x266
        #define PCT_65  0x299
        #define PCT_70  0x2cc
        #define PCT_75  0x300
        #define PCT_80  0x333
        #define PCT_85  0x366
        #define PCT_90  0x399
        #define PCT_95  0x3cc
        #define PCT_100 0x3ff

This structure is used to define the sizes of various UI gadgets. CSHA_width determines how the gadget's width should be calculated; CSHA_height determines how the height should be calculated; CSHA_count is the number of children in a particular line of the composite object (if that object is wrapping its children).

This example sets the initial size of the composite (probably a GenPrimary) to be half the screen's height and half the screen's width.

HINT_INITIAL_SIZE = {
    SST_PCT_OF_FIELD_WIDTH | PCT_50,
    SST_PCT_OF_FIELD_HEIGHT | PCT_50,
    0 };

This example sets the size of the composite to be 100 pixels high and 200 pixels wide.

HINT_FIXED_SIZE = {
    SST_PIXELS | 200,
    SST_PIXELS | 100,
    0 };

This example sets the composite's minimum size to be 10 average characters wide and 20 percent of the screen height tall.

HINT_MINIMUM_SIZE = {
    SST_AVG_CHAR_WIDTHS | 10,
    SST_PCT_OF_FIELD_HEIGHT | PCT_20,
    0 };

CountryType

typedef enum /* word */ {
CT_UNITED_STATES=1,
CT_CANADA,
CT_UNITED_KINGDOM,
CT_GERMANY,
CT_FRANCE,
CT_SPAIN,
CT_ITALY,
CT_DENMARK,
CT_NETHERLANDS,
} CountryType;

CRangeEnumParams

typedef struct {
            RangeEnumParams         CREP_params;
            void                    *CREP_locals;
            PCB(RANGE_ENUM_CALLBACK_RETURN_TYPE, CREP_callback,
(RangeEnumCallbackParams));
} CRangeEnumParams;

The CREP_callback routine should be declared _pascal.

CurrencyFormatFlags

typedef ByteFlags CurrencyFormatFlags;
#define CFF_LEADING_ZERO									0x20
#define CFF_SPACE_AROUND_SYMBOL 									0x10
#define CFF_USE_NEGATIVE_SIGN 									0x08
#define CFF_SYMBOL_BEFORE_NUMBER 									0x04
#define CFF_NEGATIVE_SIGN_BEFORE_NUMBER 									0x02
#define CFF_NEGATIVE_SIGN_BEFORE_SYMBOL 									0x01

CustomDialogBoxFlags

typedef WordFlags CustomDialogBoxFlags;
#define CDBF_SYSTEM_MODAL						0x8000
#define CDBF_DIALOG_TYPE						0x6000
#define CDBF_INTERACTION_TYPE						0x1e00
#define CDBF_DESTRUCTIVE_ACTION 0x0100
#define CDBF_DIALOG_TYPE_OFFSET         13
#define CDBF_INTERACTION_TYPE_OFFSET    9

CustomDialogType

typedef ByteEnum CustomDialogType;
#define CDT_QUESTION				0
#define CDT_WARNING				1
#define CDT_NOTIFICATION 				2
#define CDT_ERROR 				3

DACPlayFlags

typedef ByteFlags DACPlayFlags;
#define DACPF_CATENATE 0x80

DACReferenceByte

typedef enum {
 DACRB_NO_REFERENCE_BYTE,
 DACRB_WITH_REFERENCE_BYTE
} DACReferenceByte;

DACSampleFormat

typedef enum {
 DACSF_8_BIT_PCM,
 DACSF_2_TO_1_ADPCM,
 DACSF_3_TO_1_ADPCM,
 DACSF_4_TO_1_ADPCM
} DACSampleFormat;

This structure specifies what sort of sampling should be used when recording or playing a sampled sound.

DashPairArray

See: LineStyle

DataStoreCreateParams

typedef struct {
TCHAR				*DSCP_name;
DataStoreFlags				DSCP_flags;
FieldDescriptor				*DSCP_keyList;
word				DSCP_keyCount;
optr				DSCP_notifObject;
DataStoreOpenFlags				DSCP_openFlags;
} DataStoreCreateParams;

Attributes to specify when creating a new datastore.

DatatStoreDataError

typedef enum {
DSDE_NO_ERROR,
DSDE_INVALID_TOKEN,
DSDE_DATASTORE_LOCKED,
DSDE_ACCESS_DENIED,
DSDE_WRITE_ERROR,
DSDE_MEMORY_FULL,
DSDE_INVALID_RECORD_ID,
DSDE_INVALID_FIELD_ID,
DSDE_INVALID_FIELD_NAME,
DSDE_INVALID_FIELD_TYPE,
DSDE_INVALID_FIELD_CATEGORY,
DSDE_INVALID_FIELD_FLAGS,
DSDE_RECORD_BUFFER_NOT_EMPTY,
DSDE_RECORD_BUFFER_EMPTY,
DSDE_INVALID_FIELD_DATA,
DSDE_DATASTORE_READ_ONLY,
DSDE_INVALID_RECORD_NUMBER,
DSDE_INVALID_DATA_SIZE,
DSDE_FIELD_DOES_NOT_EXIST,
DSDE_RECORD_READ_ONLY,
DSDE_RECORD_NOT_FOUND,
DSDE_READ_ERROR,
DSDE_EXCEEDS_BUFFER_SIZE,
DSDE_TOO_MANY_FIELDS,
DSDE_RECORD_IN_USE,
DSDE_INVALID_BLOCK_HANDLE,
DSDE_INVALID_CHUNK_HANDLE,
DSDE_RECORD_LOCKED,
} DataStoreDataError;

Error values returned by various DataStore routines.

DatatStoreError

typedef enum {
DSE_NO_ERROR,
DSE_INVALID_TOKEN,
DSE_DATASTORE_LOCKED,
DSE_ACCESS_DENIED,
DSE_WRITE_ERROR,
DSE_MEMORY_FULL,
DSE_INVALID_RECORD_ID,
DSE_INVALID_NAME,
DSE_INVALID_KEY_LIST,
DSE_INVALID_FLAGS,
DSE_DUPLICATE_FIELD_NAME,	    /* duplicate field in create params */
DSE_CREATE_ERROR,
DSE_OPEN_ERROR,
DSE_CLOSE_ERROR,
DSE_UPDATE_ERROR,
DSE_DATASTORE_EXISTS,
DSE_DATASTORE_NOT_FOUND,
DSE_DATASTORE_ALREADY_OPEN,
DSE_PROTOCOL_ERROR,
DSE_CLOSE_WITH_LOCKED_RECORD,
DSE_INVALID_DATASTORE_FILE,
DSE_PRIVATE_DATASTORE,
DSE_BAD_SEARCH_PARAMS,
DSE_CANNOT_SET_NEXT_RECORD_ID_SMALLER,
DSE_NO_MATCH_FOUND,
DSE_NO_MORE_RECORDS,	    /* last record reached */
} DataStoreError;

Error values returned by various DataStore routines.

DataStoreIndexCallbackParams

typedef struct {
FieldID			DSICP_indexField;
SortOrder			DSICP_sortOrder;
void			*DSICP_cbData;
RecordHeader			*DSICP_rec1;
RecordHeader			*DSICP_rec2;
} DataStoreIndexCallbackParams;

Set of parameters caller passes to callback in DataStoreBuildIndex() .

DatatStoreStructureError

typedef enum {
DSSE_NO_ERROR,
DSSE_INVALID_TOKEN,
DSSE_DATASTORE_LOCKED,
DSSE_ACCESS_DENIED,
DSSE_WRITE_ERROR,
DSSE_MEMORY_FULL,
DSSE_TOO_MANY_FIELDS,
DSSE_INVALID_FIELD_ID,
DSSE_INVALID_FIELD_NAME,
DSSE_INVALID_FIELD_TYPE,
DSSE_INVALID_FIELD_CATEGORY,
DSSE_INVALID_FIELD_FLAGS,
DSSE_RECORD_BUFFER_NOT_EMPTY,
DSSE_FIELD_NAME_TOO_LONG,
DSSE_FIELD_NAME_EXISTS,
DSSE_FIELD_NOT_FOUND,
DSSE_TIME_STAMP_CANNOT_BE_ADDED,
DSSE_TIME_STAMP_CANNOT_BE_DELETED,
DSSE_INVALID_SORT_ORDER,
DSSE_NO_RECORDS_IN_DATASTORE,
DSSE_PARTIAL_INDEX_RETURNED,
DSSE_DELETE_PRIMARY_KEY,
DSSE_INDEX_RECORD_NUMBER_LIMIT_EXCEEDED,
} DataStoreStructureError;

Error values returned by various DataStore routines.

DataStoreFlags

typedef WordFlags DataStoreFlags;
#define DSF_PRIVATE				0x8000
#define DSF_TIMESTAMP				0x4000
#define DSF_BACKUP				0x2000
#define DSF_ARCHIVE				0x1000
#define DSF_NO_PRIMARY_KEY				0x1000

Flags passed when creating a datastore.

DataStoreChangeNotification

typedef struct {
TCHAR				DSCN_name[FILE_LONGNAME_BUFFER_SIZE];
DataStoreChangeType				DSCN_action;
RecordID				DSCN_record;
FieldID				DSCN_field;
} DataStoreChangeNotification;

Structure sent with MSG_META_NOTIFY_WITH_DATA_BLOCK when the datastore has been modified.

DataStoreChangeType

typedef enum {
DSCT_RECORD_ADDED,
DSCT_RECORD_CHANGED,
DSCT_RECORD_DELETED,
DSCT_FIELD_ADDED,
DSCT_FIELD_RENAMED,
DSCT_FIELD_DELETED,
DSCT_DATASTORE_CHANGED,
DSCT_NAME_CHANGED,
DSCT_NEXT_RECORD_ID_CHANGED,
} DataStoreChangeType;

Datastore change notifications; see DataStoreChangeNotification above.

DataStoreDate

typedef struct {
word	DSD_year;
byte	DSD_month;
byte	DSD_day;
} DataStoreDate;

Structure used to get and set data for date fields in a datastore.

DataStoreOpenFlags

typedef ByteFlags DataStoreOpenFlags;
#define DSOF_EXCLUSIVE				0x80

Flag passed when opening a datastore; gives exclusive access to the caller.

DataStoreRecordEnumFlags

typedef WordFlags DataStoreRecordEnumFlags;
#define DSREF_BACKWARDS				0x8000
#define DSREF_START_AT_END	0x4000

Flags passed in DataStoreRecordEnum() .

DataStoreTime

typedef struct {
byte	DST_hour;
byte	DST_minute;
byte	DST_second;
} DataStoreTime;

Structure used to get and set data for time fields in a datastore.

DateTimeFormat

typedef enum /* word */ {
DTF_LONG,			/* Thursday, 7th August, 1997 */
DTF_LONG_CONDENSED,		/* Thu, 7 Aug, 1997           */
DTF_LONG_NO_WEEKDAY,		/* 7th August, 1997           */
DTF_LONG_NO_WEEKDAY_CONDENSED,	/* 7 Aug, 1997                */
DTF_SHORT,			/* 7.8.97                     */
DTF_ZERO_PADDED_SHORT,		/* 07.08.97                   */
DTF_MD_LONG,			/* Thursday, 7th August       */
DTF_MD_LONG_NO_WEEKDAY,		/* 7th August                 */
DTF_MD_SHORT,			/* 7.8                        */
DTF_MY_LONG,			/* August 1997                */
DTF_MY_SHORT,			/* 8.97                       */
DTF_MONTH,			/* August                     */
DTF_WEEKDAY,			/* Thursday                   */
DTF_HMS,			/* 02:05:06                   */
DTF_HM,				/* 02:05                      */
DTF_H,				/* 02                         */
DTF_MS,				/* 5:06                       */
DTF_HMS_24HOUR,			/* 14:05:06                   */
DTF_HM_24HOUR,			/* 14:05                      */
} DateTimeFormat;

DayOfTheWeek

typedef enum {
DOTW_SUNDAY,
DOTW_MONDAY,
DOTW_TUESAY,
DOTW_WEDNESDAY,
DOTW_THURSDAY,
DOTW_FRIDAY,
DOTW_SATURDAY
} DayOfTheWeek;

This enumerated type is used in the TimerDateAndTime structure.

DBGroup

typedef word DBGroup;

This is the handle of a DB group. It is the VM handle of a DB group block. DB group handles do not change when a file is copied, or when it is closed and reopened.

DBGroupAndItem

typedef	dword DBGroupAndItem;

This is a dword which contains the group and item handles of a database item. The high word is the item's Group handle; the low word is the item's Item handle.

Macros are provided to create and parse the DBGroupAndItem :

DBCombineGroupAndItem()
Creates a DBGroupAndItem from given group and item handles.
DBCombineGroupAndItem(group, item);
DBExtractGroupFromGroupAndItem()
Extracts the DBGroup from a given DBGroupAndItem .
DBExtractGroupFromGroupAndItem(groupAndItem);
DBExtractItemFromGroupAndItem()
Extracts the DBItem from a given DBGroupAndItem .
DBExtractItemFromGroupAndItem(groupAndItem);

Include: geos.h

DBItem

typedef word DBItem;

This is the handle of a DB item. The DBItem and DBGroup together uniquely identify a DB item in a specified file.

DBReturn

typedef struct {
word	DBR_group;
word	DBR_item;
word	unused1;
word	unused2;
} DBReturn;

DefaultPrintSizes

typedef struct {
word	paperWidth;
word	paperHeight;
word	documentWidth;
word	documentHeight;
} DefaultPrintSizes;

DevicePresent

typedef enum /* word */ {
DP_NOT_PRESENT=0xffff,
DP_CANT_TELL=0,
DP_PRESENT=1,
DP_INVALID_DEVICE=0xfffe
} DevicePresent;

DirPathInfo

See FileEnum() .

DiskCopyCallback

See DiskCopy() .

DiskCopyError

typedef enum /* word */ {
ERR_DISKCOPY_INSUFFICIENT_MEM=0xd0,
ERR_CANT_COPY_FIXED_DISKS,
ERR_CANT_READ_FROM_SOURCE,
ERR_CANT_WRITE_TO_DEST,
ERR_INCOMPATIBLE_FORMATS,
ERR_OPERATION_CANCELLED,
ERR_CANT_FORMAT_DEST,
} DiskCopyError;

DiskFindResult

See DiskFind() .

DiskHandle

typedef Handle DiskHandle;

DiskInfoStruct

typedef struct {
word		DIS_blockSize;
sdword		DIS_freeSpace;
sdword		DIS_totalSpace;
VolumeName		DIS_name;
} DiskInfoStruct;

DiskRestoreError

See DiskRestore() .

DisplayAspectRatio

typedef ByteEnum DisplayAspectRatio;
#define DAR_NORMAL				0
#define DAR_SQUISHED				1
#define DAR_VERY_SQUISHED				2

DisplayClass

typedef ByteEnum DisplayClass;
#define DC_TEXT				0
#define DC_GRAY_1 				1
#define DC_GRAY_2 				2
#define DC_GRAY_4 				3
#define DC_GRAY_8 				4
#define DC_COLOR_2 				5
#define DC_COLOR_4 				6
#define DC_COLOR_8                              7
#define DC_CF_RGB 				8

DisplaySize

typedef ByteEnum DisplaySize;
#define DS_TINY				0
#define DS_STANDARD				1
#define DS_LARGE				2
#define DS_HUGE				3

DisplayType

typedef ByteFlags DisplayType;
#define DT_DISP_SIZE					0xc0
#define DT_DISP_ASPECT_RATIO					0x30
#define DT_DISP_CLASS					0x0f

DistanceUnit

typedef ByteEnum DistanceUnit;
#define DU_POINTS							 0
#define DU_INCHES 							 1
#define DU_CENTIMETERS							 2
#define DU_MILLIMETERS 							 3
#define DU_PICAS 							 4
#define DU_EUR_POINTS 							 5
#define DU_CICEROS							 6
#define DU_POINTS_OR_MILLIMETERS 							 7
#define DU_INCHES_OR_CENTIMETERS 							 8
#define LOCAL_DISTANCE_BUFFER_SIZE 							32

DocQuitStatus

typedef enum /* word */ {
 DQS_OK,
 DQS_CANCEL,
 DQS_DELAYED,
 DQS_SAVE_ERROR
} DocQuitStatus;

DocumentSize

typedef struct {
int	leftMargin;
int	topMargin;
int	width;
int	height;
} DocumentSize;

DosCodePage

typedef enum /* word */ {
CODE_PAGE_US=437,
CODE_PAGE_MULTILINGUAL=850,
CODE_PAGE_PORTUGUESE=860,
CODE_PAGE_CANADIAN_FRENCH=863,
CODE_PAGE_NORDIC=865
} DosCodePage;

DosDotFileName

typedef char DosDotFileName[DOS_DOT_DOS_FILE_NAME_SIZE];

DosExecFlags

typedef ByteFlags DosExecFlags;
#define DEF_PROMPT					0x80			/* prompt user to return to GEOS */
#define DEF_FORCED_SHUTDOWN					0x40			/* force shutdown; no abort */
#define DEF_INTERACTIVE					0x20			/* program is interactive shell */

Flags used with DosExec() . DosExec() executes a DOS program based on these flags.

DosFileInfoStruct

typedef struct {
 byte 	DFIS_attributes;
 dword 	DFIS_modTimeDate;
 dword 	DFIS_fileSize;
 char 	DFIS_name[DOS_DOT_FILE_NAME_LENGTH_ZT];
 word 	DFIS_pathInfo;
} DosFileInfoStruct;

DosNoDotFileName

typedef char DosNoDotFileName[DOS_NO_DOT_DOS_FILE_NAME_SIZE];

DrawMask

typedef byte DrawMask[8];

The graphics system uses this structure for defining custom draw masks.

DriveType

typedef ByteEnum DriveType;
#define DRIVE_5_25				0
#define DRIVE_3_5				1
#define DRIVE_FIXED				2
#define DRIVE_RAM				3
#define DRIVE_CD_ROM				4
#define DRIVE_8				5
#define DRIVE_UNKNOWN				0xf
} DriveType;

Several routines (in particular, DriveGetStatus() ) provide information about drives used by the computer running GEOS. These routines return a member of the DriveTypes enumerated type. Note that while the type is byte-length, all of the values are guaranteed to fit in four bits; thus, routines like DriveGetStatus() can return a DriveTypes value in the low four bits and other flags in the high four bits of a single byte.

DriverAttrs

typedef WordFlags DriverAttrs;
#define DA_FILE_SYSTEM						0x8000
#define DA_CHARACTER						0x4000
#define DA_HAS_EXTENDED_INFO						0x2000

This record contains flags that indicate a given driver's attributes. This record is stored in the driver's DriverInfoStruct structure.

DriverExtendedInfoStruct

typedef struct {
DriverInfoStruct			DEIS_common;			/* The base driver info structure */
MemHandle			DEIS_resource;			/* Handle of driver's DriverExtendedInfo
                         * table. */
} DriverExtendedInfoStruct;

This structure is used by Preferences to locate the names of devices supported by a particular driver.

DriverExtendedInfoTable

typedef struct {
LMemBlockHeader			DEIT_common;
word			DEIT_numDevices;
ChunkHandle			DEIT_ChunkHandle;
word			DEIT_infoTable;
} DriverExtendedInfoTable;

DriverInfoStruct

typedef struct {
 void (*DIS_strategy)();							/* Pointer to strategy routine */
 DriverAttrs		DIS_driverAttributes;					/* driver's attribute flags */
 DriverType		DIS_driverType;					/* driver's type */
} DriverInfoStruct;

This structure defines the characteristics of a particular driver. In general, applications will not need to access this structure unless they use a driver directly.

DriverType

typedef enum {
DRIVER_TYPE_VIDEO = 1,					/* Video drivers */
DRIVER_TYPE_INPUT,					/* Input (keyboard, mouse) drivers */
DRIVER_TYPE_MASS_STORAGE,					/* Disk/Drive drivers */
DRIVER_TYPE_STREAM,					/* Stream and port drivers */
DRIVER_TYPE_FONT,					/* Font drivers */
DRIVER_TYPE_OUTPUT,					/* Output (not video and printer) drivers */
DRIVER_TYPE_LOCALIZATION,					/* Localization drivers */
DRIVER_TYPE_FILE_SYSTEM,					/* File system drivers */
DRIVER_TYPE_PRINTER,					/* Printer drivers */
DRIVER_TYPE_SWAP,					/* Swap drivers */
DRIVER_TYPE_POWER_MANAGEMENT,					/* Power management drivers */
DRIVER_TYPE_TASK_SWITCH,					/* Task switch drivers */
DRIVER_TYPE_NETWORK					/* Network file system drivers */
} DriverType;

This enumerated type has one value for each type of driver in the system. It is used primarily with GeodeUseDriver() and its associated routines. Each driver stores its type in its DriverInfoStruct structure.

DWFixed

typedef struct {
word WWF_frac;
dword WWF_int;
} DWFixed;

dword

typedef unsigned long dword;

DWordFlags

typedef dword DWordFlags;

ElementArrayHeader

typedef struct {
ChunkArrayHeader			EAH_meta;			/* chunk array header structure */
word			EAH_freePtr;			/* First free element */
} ElementArrayHeader;

Every element array must begin with an ElementArrayHeader . Since element arrays are special kinds of chunk arrays, the ElementArrayHeader must itself begin with a ChunkArrayHeader . The structure contains one additional field, EAH _freePtr . This is used to keep track of the freed elements in the element array. Applications should not examine or change this field.

EndOfSongFlags

typedef ByteFlags EndOfSongFlags;
        #define EOSF_UNLOCK 0x0080 /* unlock block at EOS ? */
        #define EOSF_DESTROY 0x0040 /* destroy block at EOS ? */

        #define UNLOCK_ON_EOS EOSF_UNLOCK
        #define DESTROY_ON_EOS EOSF_DESTROY

These flags determine what should be done with the memory block which stores a song structure after the song is done playing.

EntryPointRelocation

typedef struct {
char	EPR_geodeName[GEODE_NAME_SIZE];
word	EPR_entryNumber;
} EntryPointRelocation;

EnvelopeOrientation

typedef ByteEnum EnvelopeOrientation;
#define EO_PORTAIT_LEFT				0x00
#define EO_PORTAIT_RIGHT				0x01
#define EO_LANDSCAPE_UP				0x02
#define EO_LANDSCAPE_DOWN				0x03

EnvelopePath

typedef ByteEnum EnvelopePath;
#define EP_LEFT				0x00
#define EP_CENTER				0x01
#define EP_RIGHT				0x02

Errors

#define ERROR_UNSUPPORTED_FUNCTION										1
#define ERROR_FILE_NOT_FOUND 										2
#define ERROR_PATH_NOT_FOUND 										3
#define ERROR_TOO_MANY_OPEN_FILES 										4
#define ERROR_ACCESS_DENIED 										5
#define ERROR_INSUFFICIENT_MEMORY 										8
#define ERROR_INVALID_VOLUME 										15
#define ERROR_IS_CURRENT_DIRECTORY 										16
#define ERROR_DIFFERENT_DEVICE 										17
#define ERROR_NO_MORE_FILES 										18
#define ERROR_WRITE_PROTECTED 										19
#define ERROR_UNKNOWN_VOLUME 										20
#define ERROR_DRIVE_NOT_READY 										21
#define ERROR_CRC_ERROR 										23
#define ERROR_SEEK_ERROR 										25
#define ERROR_UNKNOWN_MEDIA 										26
#define ERROR_SECTOR_NOT_FOUND 										27
#define ERROR_WRITE_FAULT 										29
#define ERROR_READ_FAULT 										30
#define ERROR_GENERAL_FAILURE 										31
#define ERROR_SHARING_VIOLATION 										32
#define ERROR_ALREADY_LOCKED 										33
#define ERROR_SHARING_OVERFLOW 										36
#define ERROR_SHORT_READ_WRITE 										128
#define ERROR_INVALID_LONGNAME 										129
#define ERROR_FILE_EXISTS 										130
#define ERROR_DOS_EXEC_IN_PROGRESS 										131
#define ERROR_FILE_IN_USE 										132
#define ERROR_ARGS_TOO_LONG 										133
#define ERROR_DISK_UNAVAILABLE 										134
#define ERROR_DISK_STALE 										135
#define ERROR_FILE_FORMAT_MISMATCH 										136
#define ERROR_CANNOT_MAP_NAME 										137
#define ERROR_DIRECTORY_NOT_EMPTY 										138
#define ERROR_ATTR_NOT_SUPPORTED 										139
#define ERROR_ATTR_NOT_FOUND 										140
#define ERROR_ATTR_SIZE_MISMATCH 										141
#define ERROR_ATTR_CANNOT_BE_SET 										142
#define ERROR_CANNOT_MOVE_DIRECTORY 										143
#define ERROR_PATH_TOO_LONG 										144
#define ERROR_ARGS_INVALID 										145
#define ERROR_CANNOT_FIND_COMMAND_INTERPRETER 										146
#define ERROR_NO_TASK_DRIVER_LOADED 										147

ErrorCheckingFlags

typedef WordFlags ErrorCheckingFlags;
#define ECF_REGION					0x8000
#define ECF_HEAP_FREE_BLOCKS 					0x4000
#define ECF_LMEM_INTERNAL 					0x2000
#define ECF_LMEM_FREE_AREAS 					0x1000
#define ECF_LMEM_OBJECT 					0x0800
#define ECF_BLOCK_CHECKSUM 					0x0400
#define ECF_GRAPHICS 					0x0200
#define ECF_SEGMENT 					0x0100
#define ECF_NORMAL 					0x0080
#define ECF_VMEM 					0x0040
#define ECF_APP 					0x0020
#define ECF_LMEM_MOVE 					0x0010
#define ECF_UNLOCK_MOVE 					0x0008
#define ECF_VMEM_DISCARD 					0x0004

Error checking flags are used when setting the system's error-checking level with SysSetECLevel() . The flags above may be individually set or cleared. It is important to use error checking when debugging; it can help catch obscure bugs that might otherwise go unnoticed until after a product ships.

EvalErrorData

typedef struct {
byte		EED_errorCode;				/* ParserScannerEvaluatorError */
} EvalErrorData;

EvalFlags

typedef ByteFlags EvalFlags;
#define EF_MAKE_DEPENDENCIES						0x80
#define EF_ONLY_NAMES 						0x40
#define EF_KEEP_LAST_CELL 						0x20
#define EF_NO_NAMES 						0x10
#define EF_ERROR_PUSHED 						0x08
#define EVAL_MAX_NESTED_LEVELS 32

EvalFunctionData

typedef struct {
FunctionID		EFD_functionID;
word		EFD_nArgs;
} EvalFunctionData;

EvalNameData

typedef struct {
word 	END_name;
} EvalNameData;

EvalOperatorData

typedef struct {
OperatorType		EOD_opType;
} EvalOperatorData;

EvalParameters

typedef struct {
CommonParameters				EP_common;
EvalFlags				EP_flags;
word				EP_fpStack;
word				EP_depHandle;
word				EP_nestedLevel;
dword				EP_nestedAddresses[EVAL_MAX_NESTED_LEVELS];
} EvalParameters;

EvalRangeData

typedef struct {
CellReference			ERD_firstCell;
CellReference			ERD_lastCell;
} EvalRangeData;

EvalStackArgumentData

typedef union {
EvalStringData			ESAD_string;
EvalRangeData			ESAD_range;
EvalErrorData			ESAD_error;
} EvalStackArgumentData;

EvalStackArgumentType

typedef ByteFlags EvalStackArgumentType;
#define ESAT_EMPTY					0x80
#define ESAT_ERROR 					0x40
#define ESAT_RANGE 					0x20
#define ESAT_STRING 					0x10
#define ESAT_NUMBER 					0x08
#define ESAT_NUM_TYPE 					0x03
#define ESAT_TOP_OF_STACK 					0
#define ESAT_NAME 					(ESAT_RANGE | ESAT_STRING)
#define ESAT_FUNCTION 					(ESAT_NUMBER | ESAT_STRING)

EvalStackOperatorData

typedef union {
EvalOperatorData			ESOD_operator;
EvalFunctionData			ESOD_function;
} EvalStackOperatorData;

EvalStackOperatorType

typedef ByteEnum EvalStackOperatorType;
#define ESOT_OPERATOR				0
#define ESOT_FUNCTION 				1
#define ESOT_OPEN_PAREN 				2
#define ESOT_TOP_OF_STACK 				3

EvalStringData

typedef struct {
word	ESD_length;
} EvalStringData;

EventHandle

typedef Handle		EventHandle;

ExitFlags

typedef ByteFlags ExitFlags;
#define EF_PANIC				0x80
#define EF_RUN_DOS				0x40
#define EF_OLD_EXIT				0x20
#define EF_RESET				0x10
#define EF_RESTART				0x08

ExportControlFeatures

typedef ByteFlags ExportControlFeatures;
#define EXPORTCF_BASIC						0x01

ExportControlToolboxFeatures

typedef ByteFlags ExportControlToolboxFeatures;
#define EXPORTCTF_DIALOG_BOX						0x01

FALSE

#define FALSE		 0
#define TRUE		(~0)	/* use as return value, not for comparisons */

FieldCategory

typedef ByteEnum FieldType
#define FC_NONE				0x0
#define FC_NAME				0x1
#define FC_DATE				0x2
#define FC_TELEPHONE				0x3
#define FC_ADDRESS				0x4
#define FC_EMAIL				0x5

When you add a field to a datatstore, you define its FieldCategory . FieldCategory is the type of information the data represents (not to be confused with FieldType ; see below).

FieldData

typedef struct {
FieldType			FD_type;
FieldCategory			FD_category;
FieldFlags			FD_flags;
} FieldData;

Structure used to get and set field information (see FieldDescriptor below).

FieldDescriptor

typedef struct {
FieldData			FD_data;
TCHAR			*FD_name;
} FieldDescriptor;

Structure used to get and set field information (see FieldData above).

FieldHeader

typedef struct {
FieldID		FH_id;
word		FH_size;
} FieldHeader;

Structure containing metadata about a variable-sized field (such as a string field); field data follows this header.

FieldHeaderFixed

typedef struct {
FieldID		FHF_id;
} FieldHeaderFixed;

Structure containing metadata about a fixed-sized field; field data follows this header.

FieldType

typedef ByteEnum FieldType
#define DSFT_FLOAT				0x0
#define DSFT_SHORT				0x1
#define DSFT_LONG				0x2
#define DSFT_TIMESTAMP				0x3
#define DSFT_DATE				0x4
#define DSFT_TIME				0x5
#define DSFT_STRING				0x6
#define DSFT_BINARY				0x7
#define DSFT_GRAPHICS				0x8
#define DSFT_INK				0x9

When you add a field to a datatstore, you define its FieldType . FieldType is the type of data the field contains (not to be confused with FieldCategory ; see above). Note that fields of type DSFT_FLOAT expect data of type FloatNum .

FileAccess

typedef ByteEnum FileAccess
#define FA_READ_ONLY				0
#define FA_WRITE_ONLY				1
#define FA_READ_WRITE				2

FileAccessFlags

typedef ByteFlags FileAccessFlags;
#define FILE_DENY_RW 0x10
#define FILE_DENY_W 0x20
#define FILE_DENY_R 0x30
#define FILE_DENY_NONE 0x40
#define FILE_ACCESS_R 0x00
#define FILE_ACCESS_W 0x01
#define FILE_ACCESS_RW 0x02
#define FILE_NO_ERRORS 0x80

When you open a file for bytewise access, you must pass a record of FileAccessFlags . The FileAccessFlags record specifies two things: what kind of access the caller wants, and what type of access is permitted to other geodes. A set of FileAccessFlags is thus a bit-wise "or" of two different values. The first specifies what kind of access the calling geode wants and has the following values:

FILE_ACCESS_R
The geode will only be reading from the file.
FILE_ACCESS_W
The geode will write to the file but will not read from it.
FILE_ACCESS_RW
The geode will read from and write to the file.

The second part specifies what kind of access other geodes may have. Note that if you try to deny a permission which has already been given to another geode (e.g. you open a file with FILE_DENY_W when another geode has the file open for write-access), the call will fail. It has the following values:

FILE_DENY_RW
No geode may open the file for any kind of access, whether read, write, or read/write.
FILE_DENY_R
No geode may open the file for read or read/write access.
FILE_DENY_W
No geode may open the file for write or read/write access.
FILE_DENY_NONE
Other geodes may open the file for any kind of access.

Two flags, one from each of these sets of values, are combined to make up a proper FileAccessFlags value. For example, to open the file for read-only access while prohibiting other geodes from writing to the file, you would pass the flags "(FILE_ACCESS_R | FILE_DENY_W)".

FileAccessRights

typedef char FileAccessRights[FILE_RIGHTS_SIZE];

FileAttrs

typedef ByteFlags FileAttrs;
#define FA_ARCHIVE						0x20
#define FA_SUBDIR 						0x10
#define FA_VOLUME 						0x8
#define FA_SYSTEM	 					0x4
#define FA_HIDDEN 						0x2
#define FA_RDONLY 						0x1
#define FILE_ATTR_NORMAL 						0
#define FILE_ATTR_READ_ONLY	 					FA_RDONLY
#define FILE_ATTR_HIDDEN 						FA_HIDDEN
#define FILE_ATTR_SYSTEM 						FA_SYSTEM
#define FILE_ATTR_VOLUME_LABEL 						FA_VOLUME

Every DOS or GEOS file has certain attributes. These attributes mark such things as whether the file is read-only. With GEOS files, the attributes can be accessed by using the extended attribute FEA_FILE_ATTR. You can also access any file's standard attributes with the routines FileGetAttributes() and FileSetAttributes() ; these routines work for both GEOS files and plain DOS files.

The FileAttrs field contains the following bits:

FA_ARCHIVE
This flag is set if the file requires backup. Backup programs typically clear this bit.
FA_SUBDIR
This flag is set if the "file" is actually a directory. Geodes may not change this flag.
FA_VOLUME
This flag is set if the "file" is actually the volume label. This flag will be off for all files a geode will ever see. Geodes may not change this flag.
FA_SYSTEM
This flag is set if the file is a system file. Geodes should not change this bit.
FA_HIDDEN
This flag is set if the file is hidden.
FA_RDONLY
This flag is set if the file is read-only.

See Also: FileGetAttributes(), FileSetAttributes().

Include: file.h

FileChangeNotificationData

typedef struct {
PathName			FCND_pathname;
DiskHandle			FCND_diskHandle;
FileChangeType			FCND_changeType;
} FileChangeNotificationData;

FileChangeType

typedef ByteEnum FileChangeType;
#define FCT_CREATE				0
#define FCT_DELETE 				1
#define FCT_RENAME 				2
#define FCT_CONTENTS 				3
#define FCT_DISK_FORMAT 				4

FileCopyrightNotice

typedef char FileCopyrightNotice[GFH_NOTICE_SIZE];

FileCreateFlags

typedef WordFlags FileCreateFlags;
#define FCF_NATIVE				0x8000
#define FCF_MODE				0x0300 /* Filled with FILE_CREATE_* constant */
#define FCF_ACCESS				0x00ff /* Filled with FileAccessFlags */

The lower byte of this field is a FileAccessFlags record. This specifies the file's permissions and exclusions. Note that you must request write or read/write permission when you create a file. The upper byte specifies how the file should be created. It contains the following possible values:

FILE_CREATE_TRUNCATE
If a file with the given name exists, it should be opened and truncated; that is, all data should be deleted.
FILE_CREATE_NO_TRUNCATE
If the file exists, it should be opened without being truncated.
FILE_CREATE_ONLY
If the file exists, the routine should fail and set the thread's error value to ERROR_FILE_EXISTS.
FCF_NATIVE
This flag is combined with one of the above flags if the file should be created in the device's native format; e.g. if it should be a DOS file instead of a GEOS file. The name passed must be an acceptable native file name. If a GEOS file with the specified name already exists, FileCreate() will fail with error condition ERROR_FILE_FORMAT_MISMATCH. Similarly, if the flag isn't set and a non-GEOS file with this name exists, FileCreate() will fail and return this error.

FileDate

typedef WordFlags FileDate;
#define FD_YEAR                 0xfe00
#define FD_MONTH                0x01e0
#define FD_DAY                  0x001f
#define FD_YEAR_OFFSET          9
#define FD_MONTH_OFFSET         5
#define FD_DAY_OFFSET           0

A file's date stamp is stored in a 16-bit bitfield. This field contains entries for the year, month, and day. Each field is identified by a mask and an offset. To access a field, simply clear all bits except those in the mask, then shift the bits to the right by the number of the offset. (Macros are provided to do this; they are described below.) FileDate contains the following fields, identified by their masks:

FD_YEAR
This field records the year counting from the base year of 1980. It is offset by FD_YEAR_OFFSET bits.
FD_MONTH
This field records the months as integers, with January as 1. It is offset by FD_MONTH_OFFSET bits.
FD_DAY
This field records the day of the month. It is offset by FD_DAY_OFFSET bits.

Include: file.h

See Also: FileTime

FileDateAndTime

typedef DWordFlags FileDateAndTime;
#define FDAT_HOUR						0xf8000000
#define FDAT_MINUTE						0x07e00000
#define FDAT_2SECOND						0x001f0000
#define FDAT_YEAR 						0x0000fe00
#define FDAT_MONTH 						0x000001e0
#define FDAT_DAY 						0x0000001f
#define FDAT_HOUR_OFFSET					27
#define FDAT_MINUTE_OFFSET					21
#define FDAT_2SECOND_OFFSET					16
#define FDAT_YEAR_OFFSET 					9
#define FDAT_MONTH_OFFSET 					5
#define FDAT_DAY_OFFSET 					0
#define FDAT_BASE_YEAR 					1980

Every GEOS file has two date and time stamps. One of them records the time the file was created, and one records the time the file was last modified. These stamps are recorded with the file's extended attributes; they are labeled FEA_CREATION and FEA_MODIFICATION, respectively. Non-GEOS files have a single date/time stamp, which records the time the file was last modified.

The date/time stamps are stored in a 32-bit bitfield. This field contains entries for the year, month, day, hour, minute, and second. Each field is identified by a mask and an offset. To access a field, simply clear all bits except those in the mask, then shift the bits to the right by the number of the offset. (Macros are provided to do this; they are described below.) FileDateAndTime contains the following fields, identified by their masks:

FDAT_YEAR
This field records the year, counting from a base year of 1980. (The constant FDAT_BASE_YEAR is defined as 1980.) This field is at an offset of FDAT_YEAR_OFFSET bits from the low end of the value.
FDAT_MONTH
This field records the month as an integer, with January being one. It is located at an offset of FDAT_MONTH_OFFSET.
FDAT_DAY
This field records the day of the month. It is located at an offset of FDAT_DAY_OFFSET.
FDAT_HOUR
This field records the hour on a 24-hour clock, with zero being the hour after midnight. It is located at an offset of FDAT_HOUR_OFFSET.
FDAT_MINUTE
This field records the minute. It is located at an offset of FDAT_MINUTE_OFFSET.
FDAT_2SECOND
This field records the second, divided by two; that is, a field value of 15 indicates the 30th second. (It is represented this way to let the second fit into 5 bits, thus letting the entire value fit into 32 bits.) It is located at an offset of FDAT_2SECOND_OFFSET.

Macros are provided to extract values from each of the fields of a FileDateAndTime structure. The macros are listed below:

byte FDATExtractYear( /* returns year field, counted from 1980*/
        FileDateAndTime fdat);
word FDATExtractYearAD( /* returns year field + base year */
        FileDateAndTime fdat);
byte FDATExtractMonth( /* returns month field (1 = January, etc.) */
        FileDateAndTime fdat);
byte FDATExtractDay( /* returns day field */
        FileDateAndTime fdat);
byte FDATExtractHour( /* returns hour field */
        FileDateAndTime fdat);
byte FDATExtractMinute( /* returns minute field */
        FileDateAndTime fdat);
byte FDATExtract2Second( /* returns 2Second field */
        FileDateAndTime fdat);
byte FDATExtractSecond( /* returns number of seconds (2 * 2Second) */
        FileDateAndTime fdat);

Include: file.h

FileDesktopInfo

typedef char FileDesktopInfo[FILE_DESKTOP_INFO_SIZE];

FileDirID

typedef dword FileDirID;

FileFileID

typedef dword FileFileID;

FileExclude

typedef ByteEnum FileExclude;
#define FE_EXCLUSIVE				1
#define FE_DENY_WRITE 				2
#define FE_DENY_READ 				3
#define FE_NONE 				4

FileExtAttrDesc

typedef struct {
FileExtendedAttribute				FEAD_attr;			/* Attribute to get or set */
void			*FEAD_value;				/* Pointer to buffer/new value */
word			FEAD_size;				/* length of buffer/new value */
chr			*FEAD_name;				/* If FEAD_attr == FEA_CUSTOM,
                             * this points to null-
                             * terminated ASCII string with
                             * attribute's name; otherwise,
                             * this is ignored. */
} FileExtendedAttrDesc;

The routines to get and set extended attributes can be passed the attribute FEA_MULTIPLE. In this case, they will also be passed the address of an array of FileExtAttrDesc structures and the number of elements of the array. They will go through the array and read or write the appropriate information.

FileEnum() can also be passed arrays of FileExtAttrDesc structures. In this case, the number of elements in the array is not passed. Instead, each array ends with a FileExtAttrDesc with a FEAD _attr field set to FEA_END_OF_LIST.

See Also: FileExtendedAttribute.

Include: file.h

FileExtendedAttribute

typedef enum /* word */ {
FEA_MODIFICATION,
FEA_FILE_ATTR,
FEA_SIZE,
FEA_FILE_TYPE,
FEA_FLAGS,
FEA_RELEASE,
FEA_PROTOCOL,
FEA_TOKEN,
FEA_CREATOR,
FEA_USER_NOTES,
FEA_NOTICE,
FEA_CREATION,
FEA_PASSWORD,
FEA_CUSTOM,
FEA_NAME,
FEA_GEODE_ATTR,
FEA_PATH_INFO,
FEA_FILE_ID,
FEA_DESKTOP_INFO,
FEA_DRIVE_STATUS,
FEA_DOS_NAME,
FEA_OWNER,
FEA_RIGHTS,
FEA_MULTIPLE = 0xfffe,
FEA_END_OF_LIST = 0xffff,
} FileExtendedAttribute;

Every GEOS file has a set of extended attributes. These attributes can be recovered with FileGetPathExtAttributes() or FileGetHandleExtAttributes() . You can also use FileEnum() to search a directory for files with specified extended attributes.

The above extended attributes have been implemented. More may be added with future releases of GEOS. The attributes are discussed at length in the File System chapter.

See Also: FileExtAttrDesc.

Include: file.h

FileFromTransferBlockID

VMFileHandle FileFromTransferBlockID(id);
        TransferBlockID		id;

This macro extracts the file handle from the given TransferBlockID value.

FileHandle

typedef Handle FileHandle;

FileLongName

typedef char FileLongName[FILE_LONGNAME_BUFFER_SIZE];

FileOwnerName

typedef char FileOwnerName[FILE_OWNER_NAME_SIZE];

FilePassword

typedef char FilePassword[FILE_PASSWORD_SIZE];

FilePosMode

typedef ByteEnum FilePosMode;
#define FILE_POS_START				0
#define FILE_POS_RELATIVE				1
#define FILE_POS_END				2

FileTime

typedef WordFlags FileTime;
#define FT_HOUR                 0xf800
#define FT_MINUTE               0x07e0
#define FT_2SECOND              0x001f
#define FT_HOUR_OFFSET          11
#define FT_MINUTE_OFFSET        5
#define FT_2SECOND_OFFSET       0

A file's time stamp is stored in a 16-bit bitfield. This field contains entries for the hour, minute, and second. Each field is identified by a mask and an offset. To access a field, simply clear all bits except those in the mask, then shift the bits to the right by the number of the offset. FileTime contains the following fields, identified by their masks:

FT_HOUR
This field records the hour on a 24-hour clock, with zero being the hour after midnight.. It is offset by FT_HOUR_OFFSET bits.
FT_MINUTE
This field identifies the minutes. It is offset by FT_MINUTE_OFFSET bits.
FT_2SECOND
This field records the second, divided by two; that is, a field value of 15 indicates the 30th second. (It is represented this way to let the second fit into 5 bits.) It is located at an offset of FT_2SECOND_OFFSET bits.

Include: file.h

See Also: FileDate

FileUserNotes

typedef char FileUserNotes[GFH_USER_NOTES_BUFFER_SIZE];

FindNoteHeader

typedef struct {
word 	FNH_count;			/* The number of matching notes we've found */
} FindNoteHeader;

FloatExponent

typedef WordFlags FloatExponent;
#define FE_SIGN         0x8000
#define FE_EXPONENT     0x7fff

FloatNum

typedef struct {
        word    				F_mantissa_wd0;
        word    				F_mantissa_wd1;
        word    				F_mantissa_wd2;
        word    				F_mantissa_wd3;
        FloatExponent   				F_exponent;
} FloatNum;

FontAttrs

typedef ByteFlags FontAttrs;
#define FA_FIXED_WIDTH 				0x40
#define FA_ORIENT 				0x20
#define FA_OUTLINE 				0x10
#define FA_FAMILY 				0x0f
#define FA_FAMILY_OFFSET				0

Include: font.h

FontEnumFlags

typedef ByteFlags FontEnumFlags;
#define FEF_ALPHABETIZE				0x80		/* Alphabetize returned list of fonts */
#define FEF_FIXED_WIDTH				0x20		/* Return only fixed-width fonts */
#define FEF_FAMILY 				0x10
#define FEF_STRING 				0x08
#define FEF_DOWNCASE 				0x04		/* Returned font names will be lowercase */
#define FEF_BITMAPS 				0x02		/* Interested in bitmap fonts */
#define FEF_OUTLINES 				0x01		/* Interested in outline fonts */

Include: font.h

FontEnumStruct

typedef struct {
 FontIDs	 	FES_ID;
 char 		FES_name[FID_NAME_LEN];
} FontEnumStruct;

Include: font.h

FontFamily

typedef byte FontFamily;
#define FF_NON_PORTABLE					0x0007
#define FF_SPECIAL					0x0006
#define FF_MONO					0x0005
#define FF_SYMBOL					0x0004
#define FF_ORNAMENT					0x0003
#define FF_SCRIPT					0x0002
#define FF_SANS_SERIF					0x0001
#define FF_SERIF					0x0000

Include: fontID.h

FontGroup

typedef enum /* word */ {
#define FG_NON_PORTABLE					0x0e00
#define FG_SPECIAL					0x0c00
#define FG_MONO					0x0a00
#define FG_SYMBOL					0x0800
#define FG_ORNAMENT					0x0600
#define FG_SCRIPT					0x0400
#define FG_SANS_SERIF					0x0200
#define FG_SERIF					0x0000
} FontGroup;

Include: fontID.h

FontIDRecord

typedef WordFlags FontIDRecord;
#define FIDR_maker				0xf000
#define FIDR_ID				0x0fff
#define FIDR_maker_OFFSET					12
#define FIDR_ID_OFFSET					 0

Include: font.h

FontID

typedef word FontID;
#define FID_PRINTER_20CPI										0xfa05
#define FID_PRINTER_17CPI										0xfa04
#define FID_PRINTER_16CPI										0xfa03
#define FID_PRINTER_15CPI										0xfa02
#define FID_PRINTER_12CPI										0xfa01
#define FID_PRINTER_10CPI										0xfa00
#define FID_PRINTER_PROP_SANS										0xf200
#define FID_PRINTER_PROP_SERIF										0xf000
#define FID_BITSTREAM_LETTER_GOTHIC										0x3a03
#define FID_PS_LETTER_GOTHIC										0x2a03
#define FID_DTC_LETTER_GOTHIC										0x1a03
#define FID_BITSTREAM_PRESTIGE_ELITE										0x3a02
#define FID_PS_PRESTIGE_ELITE										0x2a02
#define FID_DTC_PRESTIGE_ELITE										0x1a02
#define FID_BITSTREAM_AMERICAN_TYPEWRITER										0x3a01
#define FID_PS_AMERICAN_TYPEWRITER										0x2a01
#define FID_DTC_AMERICAN_TYPEWRITER										0x1a01
#define FID_BITSTREAM_URW_MONO										0x3a00
#define FID_PS_COURIER										0x2a00
#define FID_DTC_URW_MONO										0x1a00
#define FID_BITSTREAM_FUN_DINGBATS										0x380d
#define FID_PS_FUN_DINGBATS										0x280d
#define FID_DTC_FUN_DINGBATS										0x180d
#define FID_BITSTREAM_CHEQ										0x380c
#define FID_PS_CHEQ										0x280c
#define FID_DTC_CHEQ										0x180c
#define FID_BITSTREAM_BUNDESBAHN_PI_3										0x380b
#define FID_PS_BUNDESBAHN_PI_3										0x280b
#define FID_DTC_BUNDESBAHN_PI_3										0x180b
#define FID_BITSTREAM_BUNDESBAHN_PI_2										0x380a
#define FID_PS_BUNDESBAHN_PI_2										0x280a
#define FID_DTC_BUNDESBAHN_PI_2										0x180a
#define FID_BITSTREAM_BUNDESBAHN_PI_1										0x3809
#define FID_PS_BUNDESBAHN_PI_1										0x2809
#define FID_DTC_BUNDESBAHN_PI_1										0x1809
#define FID_BITSTREAM_U_GREEK_MATH_PI										0x3808
#define FID_PS_U_GREEK_MATH_PI										0x2808
#define FID_DTC_U_GREEK_MATH_PI										0x1808
#define FID_BITSTREAM_U_NEWS_COMM_PI										0x3807
#define FID_PS_U_NEWS_COMM_PI										0x2807
#define FID_DTC_U_NEWS_COMM_PI										0x1807
#define FID_BITSTREAM_ACE_I										0x3806
#define FID_PS_ACE_I										0x2806
#define FID_DTC_ACE_I										0x1806
#define FID_BITSTREAM_SONATA										0x3805
#define FID_PS_SONATA										0x2805
#define FID_DTC_SONATA										0x1805
#define FID_BITSTREAM_CARTA										0x3804
#define FID_PS_CARTA										0x2804
#define FID_DTC_CARTA										0x1804
#define FID_BITSTREAM_MICR										0x3803
#define FID_PS_MICR										0x2803
#define FID_DTC_MICR										0x1803
#define FID_BITSTREAM_ZAPF_DINGBATS										0x3802
#define FID_PS_ZAPF_DINGBATS										0x2802
#define FID_DTC_ZAPF_DINGBATS										0x1802
#define FID_BITSTREAM_DINGBATS										0x3801
#define FID_PS_DINGBATS										0x2801
#define FID_DTC_DINGBATS										0x1801
#define FID_BITSTREAM_URW_SYMBOLPS										0x3800
#define FID_PS_SYMBOL										0x2800
#define FID_DTC_URW_SYMBOLPS										0x1800
#define FID_BITSTREAM_JUNIPER										0x367f
#define FID_PS_JUNIPER										0x267f
#define FID_DTC_JUNIPER										0x167f
#define FID_BITSTREAM_COTTONWOOD										0x367e
#define FID_PS_COTTONWOOD										0x267e
#define FID_DTC_COTTONWOOD										0x167e
#define FID_BITSTREAM_BANCO										0x367d
#define FID_PS_BANCO										0x267d
#define FID_DTC_BANCO										0x167d
#define FID_BITSTREAM_ARCADIA										0x367c
#define FID_PS_ARCADIA										0x267c
#define FID_DTC_ARCADIA										0x167c
#define FID_BITSTREAM_ZIPPER										0x367b
#define FID_PS_ZIPPER										0x267b
#define FID_DTC_ZIPPER										0x167b
#define FID_BITSTREAM_WEIFZ_RUNDGOTIFCH										0x367a
#define FID_PS_WEIFZ_RUNDGOTIFCH										0x267a
#define FID_DTC_WEIFZ_RUNDGOTIFCH										0x167a
#define FID_BITSTREAM_WASHINGTON										0x3679
#define FID_PS_WASHINGTON										0x2679
#define FID_DTC_WASHINGTON										0x1679
#define FID_BITSTREAM_VICTORIAN										0x3678
#define FID_PS_VICTORIAN										0x2678
#define FID_DTC_VICTORIAN										0x1678
#define FID_BITSTREAM_VEGAS										0x3677
#define FID_PS_VEGAS										0x2677
#define FID_DTC_VEGAS										0x1677
#define FID_BITSTREAM_VARIO										0x3676
#define FID_PS_VARIO										0x2676
#define FID_DTC_VARIO										0x1676
#define FID_BITSTREAM_VAG_RUNDSCHRIFT										0x3675
#define FID_PS_VAG_RUNDSCHRIFT										0x2675
#define FID_DTC_VAG_RUNDSCHRIFT										0x1675
#define FID_BITSTREAM_TRAJANUS										0x3674
#define FID_PS_TRAJANUS										0x2674
#define FID_DTC_TRAJANUS										0x1674
#define FID_BITSTREAM_TITUS										0x3673
#define FID_PS_TITUS										0x2673
#define FID_DTC_TITUS										0x1673
#define FID_BITSTREAM_TIME_SCRIPT										0x3672
#define FID_PS_TIME_SCRIPT										0x2672
#define FID_DTC_TIME_SCRIPT										0x1672
#define FID_BITSTREAM_THUNDERBIRD										0x3671
#define FID_PS_THUNDERBIRD										0x2671
#define FID_DTC_THUNDERBIRD										0x1671
#define FID_BITSTREAM_THOROWGOOD										0x3670
#define FID_PS_THOROWGOOD										0x2670
#define FID_DTC_THOROWGOOD										0x1670
#define FID_BITSTREAM_TARRAGON										0x366f
#define FID_PS_TARRAGON										0x266f
#define FID_DTC_TARRAGON										0x166f
#define FID_BITSTREAM_TANGO										0x366e
#define FID_PS_TANGO										0x266e
#define FID_DTC_TANGO										0x166e
#define FID_BITSTREAM_SYNCHRO										0x366d
#define FID_PS_SYNCHRO										0x266d
#define FID_DTC_SYNCHRO										0x166d
#define FID_BITSTREAM_SUPERSTAR										0x366c
#define FID_PS_SUPERSTAR										0x266c
#define FID_DTC_SUPERSTAR										0x166c
#define FID_BITSTREAM_STOP										0x366b
#define FID_PS_STOP										0x266b
#define FID_DTC_STOP										0x166b
#define FID_BITSTREAM_STILLA_CAPS										0x366a
#define FID_PS_STILLA_CAPS										0x266a
#define FID_DTC_STILLA_CAPS										0x166a
#define FID_BITSTREAM_STILLA										0x3669
#define FID_PS_STILLA										0x2669
#define FID_DTC_STILLA										0x1669
#define FID_BITSTREAM_STENTOR										0x3668
#define FID_PS_STENTOR										0x2668
#define FID_DTC_STENTOR										0x1668
#define FID_BITSTREAM_SQUIRE										0x3667
#define FID_PS_SQUIRE										0x2667
#define FID_DTC_SQUIRE										0x1667
#define FID_BITSTREAM_SPRINGFIELD										0x3666
#define FID_PS_SPRINGFIELD										0x2666
#define FID_DTC_SPRINGFIELD										0x1666
#define FID_BITSTREAM_SLIPSTREAM										0x3665
#define FID_PS_SLIPSTREAM										0x2665
#define FID_DTC_SLIPSTREAM										0x1665
#define FID_BITSTREAM_SINALOA										0x3664
#define FID_PS_SINALOA										0x2664
#define FID_DTC_SINALOA										0x1664
#define FID_BITSTREAM_SHELLEY										0x3663
#define FID_PS_SHELLEY										0x2663
#define FID_DTC_SHELLEY										0x1663
#define FID_BITSTREAM_SERPENTINE										0x3662
#define FID_PS_SERPENTINE										0x2662
#define FID_DTC_SERPENTINE										0x1662
#define FID_BITSTREAM_RUBBER_STAMP										0x3661
#define FID_PS_RUBBER_STAMP										0x2661
#define FID_DTC_RUBBER_STAMP										0x1661
#define FID_BITSTREAM_ROMIC										0x3660
#define FID_PS_ROMIC										0x2660
#define FID_DTC_ROMIC										0x1660
#define FID_BITSTREAM_RIALTO										0x365f
#define FID_PS_RIALTO										0x265f
#define FID_DTC_RIALTO										0x165f
#define FID_BITSTREAM_REVUE										0x365e
#define FID_PS_REVUE										0x265e
#define FID_DTC_REVUE										0x165e
#define FID_BITSTREAM_QUENTIN										0x365d
#define FID_PS_QUENTIN										0x265d
#define FID_DTC_QUENTIN										0x165d
#define FID_BITSTREAM_PRO_ARTE										0x365c
#define FID_PS_PRO_ARTE										0x265c
#define FID_DTC_PRO_ARTE										0x165c
#define FID_BITSTREAM_PRINCETOWN										0x365b
#define FID_PS_PRINCETOWN										0x265b
#define FID_DTC_PRINCETOWN										0x165b
#define FID_BITSTREAM_PRESIDENT										0x365a
#define FID_PS_PRESIDENT										0x265a
#define FID_DTC_PRESIDENT										0x165a
#define FID_BITSTREAM_PREMIER										0x3659
#define FID_PS_PREMIER										0x2659
#define FID_DTC_PREMIER										0x1659
#define FID_BITSTREAM_POST_ANTIQUA										0x3658
#define FID_PS_POST_ANTIQUA										0x2658
#define FID_DTC_POST_ANTIQUA										0x1658
#define FID_BITSTREAM_PLAZA										0x3657
#define FID_PS_PLAZA										0x2657
#define FID_DTC_PLAZA										0x1657
#define FID_BITSTREAM_PLAYBILL										0x3656
#define FID_PS_PLAYBILL										0x2656
#define FID_DTC_PLAYBILL										0x1656
#define FID_BITSTREAM_PICCADILLY										0x3655
#define FID_PS_PICCADILLY										0x2655
#define FID_DTC_PICCADILLY										0x1655
#define FID_BITSTREAM_PEIGNOT										0x3654
#define FID_PS_PEIGNOT										0x2654
#define FID_DTC_PEIGNOT										0x1654
#define FID_BITSTREAM_PAPYRUS										0x3653
#define FID_PS_PAPYRUS										0x2653
#define FID_DTC_PAPYRUS										0x1653
#define FID_BITSTREAM_PADDINGTION										0x3652
#define FID_PS_PADDINGTION										0x2652
#define FID_DTC_PADDINGTION										0x1652
#define FID_BITSTREAM_OKAY										0x3651
#define FID_PS_OKAY										0x2651
#define FID_DTC_OKAY										0x1651
#define FID_BITSTREAM_ODIN										0x3650
#define FID_PS_ODIN										0x2650
#define FID_DTC_ODIN										0x1650
#define FID_BITSTREAM_OCTOPUSS										0x364f
#define FID_PS_OCTOPUSS										0x264f
#define FID_DTC_OCTOPUSS										0x164f
#define FID_BITSTREAM_MOTTER_FEMINA										0x364e
#define FID_PS_MOTTER_FEMINA										0x264e
#define FID_DTC_MOTTER_FEMINA										0x164e
#define FID_BITSTREAM_MICROGRAMMA										0x364d
#define FID_PS_MICROGRAMMA										0x264d
#define FID_DTC_MICROGRAMMA										0x164d
#define FID_BITSTREAM_MACHINE										0x364c
#define FID_PS_MACHINE										0x264c
#define FID_DTC_MACHINE										0x164c
#define FID_BITSTREAM_LINOTEXT										0x364b
#define FID_PS_LINOTEXT										0x264b
#define FID_DTC_LINOTEXT										0x164b
#define FID_BITSTREAM_LIBERTY										0x364a
#define FID_PS_LIBERTY										0x264a
#define FID_DTC_LIBERTY										0x164a
#define FID_BITSTREAM_LAZYBONES										0x3649
#define FID_PS_LAZYBONES										0x2649
#define FID_DTC_LAZYBONES										0x1649
#define FID_BITSTREAM_LATIN_WIDE										0x3648
#define FID_PS_LATIN_WIDE										0x2648
#define FID_DTC_LATIN_WIDE										0x1648
#define FID_BITSTREAM_KNIGHTSBRIDGE										0x3647
#define FID_PS_KNIGHTSBRIDGE										0x2647
#define FID_DTC_KNIGHTSBRIDGE										0x1647
#define FID_BITSTREAM_KAPITELLIA										0x3646
#define FID_PS_KAPITELLIA										0x2646
#define FID_DTC_KAPITELLIA										0x1646
#define FID_BITSTREAM_KALLIGRAPHIA										0x3645
#define FID_PS_KALLIGRAPHIA										0x2645
#define FID_DTC_KALLIGRAPHIA										0x1645
#define FID_BITSTREAM_ICE_AGE										0x3644
#define FID_PS_ICE_AGE										0x2644
#define FID_DTC_ICE_AGE										0x1644
#define FID_BITSTREAM_ICONE										0x3643
#define FID_PS_ICONE										0x2643
#define FID_DTC_ICONE										0x1643
#define FID_BITSTREAM_HORNDON										0x3642
#define FID_PS_HORNDON										0x2642
#define FID_DTC_HORNDON										0x1642
#define FID_BITSTREAM_HORATIO										0x3641
#define FID_PS_HORATIO										0x2641
#define FID_DTC_HORATIO										0x1641
#define FID_BITSTREAM_HIGHLIGHT										0x3640
#define FID_PS_HIGHLIGHT										0x2640
#define FID_DTC_HIGHLIGHT										0x1640
#define FID_BITSTREAM_HADFIELD										0x363f
#define FID_PS_HADFIELD										0x263f
#define FID_DTC_HADFIELD										0x163f
#define FID_BITSTREAM_GLASER_STENCIL										0x363e
#define FID_PS_GLASER_STENCIL										0x263e
#define FID_DTC_GLASER_STENCIL										0x163e
#define FID_BITSTREAM_GILL_KAYO										0x363d
#define FID_PS_GILL_KAYO										0x263d
#define FID_DTC_GILL_KAYO										0x163d
#define FID_BITSTREAM_GALADRIEL										0x363c
#define FID_PS_GALADRIEL										0x263c
#define FID_DTC_GALADRIEL										0x163c
#define FID_BITSTREAM_FUTURA_DISPLAY										0x363b
#define FID_PS_FUTURA_DISPLAY										0x263b
#define FID_DTC_FUTURA_DISPLAY										0x163b
#define FID_BITSTREAM_FUTURA_C_BLACK										0x363a
#define FID_PS_FUTURA_C_BLACK										0x263a
#define FID_DTC_FUTURA_C_BLACK										0x163a
#define FID_BITSTREAM_FRANKFURTER										0x3639
#define FID_PS_FRANKFURTER										0x2639
#define FID_DTC_FRANKFURTER										0x1639
#define FID_BITSTREAM_FLORA										0x3638
#define FID_PS_FLORA										0x2638
#define FID_DTC_FLORA										0x1638
#define FID_BITSTREAM_FLANGE										0x3637
#define FID_PS_FLANGE										0x2637
#define FID_DTC_FLANGE										0x1637
#define FID_BITSTREAM_FLASH										0x3636
#define FID_PS_FLASH										0x2636
#define FID_DTC_FLASH										0x1636
#define FID_BITSTREAM_FLAMENCO										0x3635
#define FID_PS_FLAMENCO										0x2635
#define FID_DTC_FLAMENCO										0x1635
#define FID_BITSTREAM_FETTE_GOTILCH										0x3634
#define FID_PS_FETTE_GOTILCH										0x2634
#define FID_DTC_FETTE_GOTILCH										0x1634
#define FID_BITSTREAM_FETTE_FRAKTUR										0x3633
#define FID_PS_FETTE_FRAKTUR										0x2633
#define FID_DTC_FETTE_FRAKTUR										0x1633
#define FID_BITSTREAM_ENVIRO										0x3632
#define FID_PS_ENVIRO										0x2632
#define FID_DTC_ENVIRO										0x1632
#define FID_BITSTREAM_EINHORN										0x3631
#define FID_PS_EINHORN										0x2631
#define FID_DTC_EINHORN										0x1631
#define FID_BITSTREAM_ECKMANN										0x3630
#define FID_PS_ECKMANN										0x2630
#define FID_DTC_ECKMANN										0x1630
#define FID_BITSTREAM_DYNAMO										0x362f
#define FID_PS_DYNAMO										0x262f
#define FID_DTC_DYNAMO										0x162f
#define FID_BITSTREAM_DOM_CASUAL										0x362e
#define FID_PS_DOM_CASUAL										0x262e
#define FID_DTC_DOM_CASUAL										0x162e
#define FID_BITSTREAM_DAVIDA										0x362d
#define FID_PS_DAVIDA										0x262d
#define FID_DTC_DAVIDA										0x162d
#define FID_BITSTREAM_CROISSANT										0x362c
#define FID_PS_CROISSANT										0x262c
#define FID_DTC_CROISSANT										0x162c
#define FID_BITSTREAM_CRILLEE										0x362b
#define FID_PS_CRILLEE										0x262b
#define FID_DTC_CRILLEE										0x162b
#define FID_BITSTREAM_COUNTDOWN										0x362a
#define FID_PS_COUNTDOWN										0x262a
#define FID_DTC_COUNTDOWN										0x162a
#define FID_BITSTREAM_CORTEZ										0x3629
#define FID_PS_CORTEZ										0x2629
#define FID_DTC_CORTEZ										0x1629
#define FID_BITSTREAM_CONFERENCE										0x3628
#define FID_PS_CONFERENCE										0x2628
#define FID_DTC_CONFERENCE										0x1628
#define FID_BITSTREAM_COMPANY										0x3627
#define FID_PS_COMPANY										0x2627
#define FID_DTC_COMPANY										0x1627
#define FID_BITSTREAM_COLUMNA_SOLID										0x3626
#define FID_PS_COLUMNA_SOLID										0x2626
#define FID_DTC_COLUMNA_SOLID										0x1626
#define FID_BITSTREAM_CITY										0x3625
#define FID_PS_CITY										0x2625
#define FID_DTC_CITY										0x1625
#define FID_BITSTREAM_CIRKULUS										0x3624
#define FID_PS_CIRKULUS										0x2624
#define FID_DTC_CIRKULUS										0x1624
#define FID_BITSTREAM_CHURCHWARD_BRUSH										0x3623
#define FID_PS_CHURCHWARD_BRUSH										0x2623
#define FID_DTC_CHURCHWARD_BRUSH										0x1623
#define FID_BITSTREAM_CHROMIUM_ONE										0x3622
#define FID_PS_CHROMIUM_ONE										0x2622
#define FID_DTC_CHROMIUM_ONE										0x1622
#define FID_BITSTREAM_CHOC										0x3621
#define FID_PS_CHOC										0x2621
#define FID_DTC_CHOC										0x1621
#define FID_BITSTREAM_CHISEL										0x3620
#define FID_PS_CHISEL										0x2620
#define FID_DTC_CHISEL										0x1620
#define FID_BITSTREAM_CHESTERFIELD										0x361f
#define FID_PS_CHESTERFIELD										0x261f
#define FID_DTC_CHESTERFIELD										0x161f
#define FID_BITSTREAM_CAROUSEL										0x361e
#define FID_PS_CAROUSEL										0x261e
#define FID_DTC_CAROUSEL										0x161e
#define FID_BITSTREAM_CAMELLIA										0x361d
#define FID_PS_CAMELLIA										0x261d
#define FID_DTC_CAMELLIA										0x161d
#define FID_BITSTREAM_CABARET										0x361c
#define FID_PS_CABARET										0x261c
#define FID_DTC_CABARET										0x161c
#define FID_BITSTREAM_BUXOM										0x361b
#define FID_PS_BUXOM										0x261b
#define FID_DTC_BUXOM										0x161b
#define FID_BITSTREAM_BUSTER										0x361a
#define FID_PS_BUSTER										0x261a
#define FID_DTC_BUSTER										0x161a
#define FID_BITSTREAM_BOTTLENECK										0x3619
#define FID_PS_BOTTLENECK										0x2619
#define FID_DTC_BOTTLENECK										0x1619
#define FID_BITSTREAM_BLOCK										0x3618
#define FID_PS_BLOCK										0x2618
#define FID_DTC_BLOCK										0x1618
#define FID_BITSTREAM_BINNER										0x3617
#define FID_PS_BINNER										0x2617
#define FID_DTC_BINNER										0x1617
#define FID_BITSTREAM_BERNHARD_ANTIQUE										0x3616
#define FID_PS_BERNHARD_ANTIQUE										0x2616
#define FID_DTC_BERNHARD_ANTIQUE										0x1616
#define FID_BITSTREAM_BELSHAW										0x3615
#define FID_PS_BELSHAW										0x2615
#define FID_DTC_BELSHAW										0x1615
#define FID_BITSTREAM_BARCELONA										0x3614
#define FID_PS_BARCELONA										0x2614
#define FID_DTC_BARCELONA										0x1614
#define FID_BITSTREAM_BAUHAUS										0x3613
#define FID_PS_BAUHAUS										0x2613
#define FID_DTC_BAUHAUS										0x1613
#define FID_BITSTREAM_AUGUSTEA_OPEN										0x3612
#define FID_PS_AUGUSTEA_OPEN										0x2612
#define FID_DTC_AUGUSTEA_OPEN										0x1612
#define FID_BITSTREAM_AMERICAN_UNCIAL										0x3611
#define FID_PS_AMERICAN_UNCIAL										0x2611
#define FID_DTC_AMERICAN_UNCIAL										0x1611
#define FID_BITSTREAM_ULTE_SCHWABACHER										0x3610
#define FID_PS_ULTE_SCHWABACHER										0x2610
#define FID_DTC_ULTE_SCHWABACHER										0x1610
#define FID_BITSTREAM_ARNOLD_BOCKLIN										0x360f
#define FID_PS_ARNOLD_BOCKLIN										0x260f
#define FID_DTC_ARNOLD_BOCKLIN										0x160f
#define FID_BITSTREAM_ALGERIAN										0x360e
#define FID_PS_ALGERIAN										0x260e
#define FID_DTC_ALGERIAN										0x160e
#define FID_BITSTREAM_PUMP										0x360d
#define FID_PS_PUMP										0x260d
#define FID_DTC_PUMP										0x160d
#define FID_BITSTREAM_MARIAGE										0x360c
#define FID_PS_MARIAGE										0x260c
#define FID_DTC_MARIAGE										0x160c
#define FID_BITSTREAM_OLD_TOWN										0x360b
#define FID_PS_OLD_TOWN										0x260b
#define FID_DTC_OLD_TOWN										0x160b
#define FID_BITSTREAM_HOBO										0x360a
#define FID_PS_HOBO										0x260a
#define FID_DTC_HOBO										0x160a
#define FID_BITSTREAM_GOUDY_HEAVYFACE										0x3609
#define FID_PS_GOUDY_HEAVYFACE										0x2609
#define FID_DTC_GOUDY_HEAVYFACE										0x1609
#define FID_BITSTREAM_DATA_70										0x3608
#define FID_PS_DATA_70										0x2608
#define FID_DTC_DATA_70										0x1608
#define FID_BITSTREAM_LCD										0x3607
#define FID_PS_LCD										0x2607
#define FID_DTC_LCD										0x1607
#define FID_BITSTREAM_BALLOON										0x3606
#define FID_PS_BALLOON										0x2606
#define FID_DTC_BALLOON										0x1606
#define FID_BITSTREAM_BLIPPO_C_BLACK										0x3605
#define FID_PS_BLIPPO_C_BLACK										0x2605
#define FID_DTC_BLIPPO_C_BLACK										0x1605
#define FID_BITSTREAM_COOPER_C_BLACK										0x3604
#define FID_PS_COOPER_C_BLACK										0x2604
#define FID_DTC_COOPER_C_BLACK										0x1604
#define FID_BITSTREAM_COPPERPLATE										0x3603
#define FID_PS_COPPERPLATE										0x2603
#define FID_DTC_COPPERPLATE										0x1603
#define FID_BITSTREAM_STENCIL										0x3602
#define FID_PS_STENCIL										0x2602
#define FID_DTC_STENCIL										0x1602
#define FID_BITSTREAM_OLD_ENGLISH										0x3601
#define FID_PS_OLD_ENGLISH										0x2601
#define FID_DTC_OLD_ENGLISH										0x1601
#define FID_BITSTREAM_BROADWAY										0x3600
#define FID_PS_BROADWAY										0x2600
#define FID_DTC_BROADWAY										0x1600
#define FID_BITSTREAM_NUPITAL_SCRIPT										0x3430
#define FID_PS_NUPITAL_SCRIPT										0x2430
#define FID_DTC_NUPITAL_SCRIPT										0x1430
#define FID_BITSTREAM_MEDICI_SCRIPT										0x342f
#define FID_PS_MEDICI_SCRIPT										0x242f
#define FID_DTC_MEDICI_SCRIPT										0x142f
#define FID_BITSTREAM_CHARME										0x342e
#define FID_PS_CHARME										0x242e
#define FID_DTC_CHARME										0x142e
#define FID_BITSTREAM_CASCADE_SCRIPT										0x342d
#define FID_PS_CASCADE_SCRIPT										0x242d
#define FID_DTC_CASCADE_SCRIPT										0x142d
#define FID_BITSTREAM_LITHOS										0x342c
#define FID_PS_LITHOS										0x242c
#define FID_DTC_LITHOS										0x142c
#define FID_BITSTREAM_TEKTON										0x342b
#define FID_PS_TEKTON										0x242b
#define FID_DTC_TEKTON										0x142b
#define FID_BITSTREAM_VLADIMIR_SCRIPT										0x342a
#define FID_PS_VLADIMIR_SCRIPT										0x242a
#define FID_DTC_VLADIMIR_SCRIPT										0x142a
#define FID_BITSTREAM_VAN_DIJK										0x3429
#define FID_PS_VAN_DIJK										0x2429
#define FID_DTC_VAN_DIJK										0x1429
#define FID_BITSTREAM_SLOGAN										0x3428
#define FID_PS_SLOGAN										0x2428
#define FID_DTC_SLOGAN										0x1428
#define FID_BITSTREAM_SHAMROCK										0x3427
#define FID_PS_SHAMROCK										0x2427
#define FID_DTC_SHAMROCK										0x1427
#define FID_BITSTREAM_ROMAN_SCRIPT										0x3426
#define FID_PS_ROMAN_SCRIPT										0x2426
#define FID_DTC_ROMAN_SCRIPT										0x1426
#define FID_BITSTREAM_RAGE										0x3425
#define FID_PS_RAGE										0x2425
#define FID_DTC_RAGE										0x1425
#define FID_BITSTREAM_PRESENT_SCRIPT										0x3424
#define FID_PS_PRESENT_SCRIPT										0x2424
#define FID_DTC_PRESENT_SCRIPT										0x1424
#define FID_BITSTREAM_PHYLLIS_INITIALS										0x3423
#define FID_PS_PHYLLIS_INITIALS										0x2423
#define FID_DTC_PHYLLIS_INITIALS										0x1423
#define FID_BITSTREAM_PHYLLIS										0x3422
#define FID_PS_PHYLLIS										0x2422
#define FID_DTC_PHYLLIS										0x1422
#define FID_BITSTREAM_PEPITA										0x3421
#define FID_PS_PEPITA										0x2421
#define FID_DTC_PEPITA										0x1421
#define FID_BITSTREAM_PENDRY_SCRIPT										0x3420
#define FID_PS_PENDRY_SCRIPT										0x2420
#define FID_DTC_PENDRY_SCRIPT										0x1420
#define FID_BITSTREAM_PALETTE										0x341f
#define FID_PS_PALETTE										0x241f
#define FID_DTC_PALETTE										0x141f
#define FID_BITSTREAM_PALACE_SCRIPT										0x341e
#define FID_PS_PALACE_SCRIPT										0x241e
#define FID_DTC_PALACE_SCRIPT										0x141e
#define FID_BITSTREAM_NEVISON_CASUAL										0x341d
#define FID_PS_NEVISON_CASUAL										0x241d
#define FID_DTC_NEVISON_CASUAL										0x141d
#define FID_BITSTREAM_HILL										0x341c
#define FID_PS_HILL										0x241c
#define FID_DTC_HILL										0x141c
#define FID_BITSTREAM_LINOSCRIPT										0x341b
#define FID_PS_LINOSCRIPT										0x241b
#define FID_DTC_LINOSCRIPT										0x141b
#define FID_BITSTREAM_LINDSAY										0x341a
#define FID_PS_LINDSAY										0x241a
#define FID_DTC_LINDSAY										0x141a
#define FID_BITSTREAM_LE_GRIFFE										0x3419
#define FID_PS_LE_GRIFFE										0x2419
#define FID_DTC_LE_GRIFFE										0x1419
#define FID_BITSTREAM_KUNSTLERSCHREIBSCHRIFT										0x3418
#define FID_PS_KUNSTLERSCHREIBSCHRIFT										0x2418
#define FID_DTC_KUNSTLERSCHREIBSCHRIFT										0x1418
#define FID_BITSTREAM_JULIA_SCRIPT										0x3417
#define FID_PS_JULIA_SCRIPT										0x2417
#define FID_DTC_JULIA_SCRIPT										0x1417
#define FID_BITSTREAM_ISBELL										0x3416
#define FID_PS_ISBELL										0x2416
#define FID_DTC_ISBELL										0x1416
#define FID_BITSTREAM_ISADORA										0x3415
#define FID_PS_ISADORA										0x2415
#define FID_DTC_ISADORA										0x1415
#define FID_BITSTREAM_HOGARTH_SCRIPT										0x3414
#define FID_PS_HOGARTH_SCRIPT										0x2414
#define FID_DTC_HOGARTH_SCRIPT										0x1414
#define FID_BITSTREAM_HARLOW										0x3413
#define FID_PS_HARLOW										0x2413
#define FID_DTC_HARLOW										0x1413
#define FID_BITSTREAM_GLASTONBURY										0x3412
#define FID_PS_GLASTONBURY										0x2412
#define FID_DTC_GLASTONBURY										0x1412
#define FID_BITSTREAM_GILLIES_GOTHIC										0x3411
#define FID_PS_GILLIES_GOTHIC										0x2411
#define FID_DTC_GILLIES_GOTHIC										0x1411
#define FID_BITSTREAM_FREESTYLE_SCRIPT										0x3410
#define FID_PS_FREESTYLE_SCRIPT										0x2410
#define FID_DTC_FREESTYLE_SCRIPT										0x1410
#define FID_BITSTREAM_ENGLISCHE_SCHREIBSCHRIFT										0x340f
#define FID_PS_ENGLISCHE_SCHREIBSCHRIFT										0x240f
#define FID_DTC_ENGLISCHE_SCHREIBSCHRIFT										0x140f
#define FID_BITSTREAM_DEMIAN										0x340e
#define FID_PS_DEMIAN										0x240e
#define FID_DTC_DEMIAN										0x140e
#define FID_BITSTREAM_CANDICE										0x340d
#define FID_PS_CANDICE										0x240d
#define FID_DTC_CANDICE										0x140d
#define FID_BITSTREAM_BRONX										0x340c
#define FID_PS_BRONX										0x240c
#define FID_DTC_BRONX										0x140x
#define FID_BITSTREAM_BRODY										0x340b
#define FID_PS_BRODY										0x240b
#define FID_DTC_BRODY										0x140b
#define FID_BITSTREAM_BIBLE_SCRIPT										0x340a
#define FID_PS_BIBLE_SCRIPT										0x240a
#define FID_DTC_BIBLE_SCRIPT										0x140a
#define FID_BITSTREAM_ARISTON										0x3409
#define FID_PS_ARISTON										0x2409
#define FID_DTC_ARISTON										0x1409
#define FID_BITSTREAM_ANGLIA										0x3408
#define FID_PS_ANGLIA										0x2408
#define FID_DTC_ANGLIA										0x1408
#define FID_BITSTREAM_MISTRAL										0x3407
#define FID_PS_MISTRAL										0x2407
#define FID_DTC_MISTRAL										0x1407
#define FID_BITSTREAM_BALMORAL										0x3406
#define FID_PS_BALMORAL										0x2406
#define FID_DTC_BALMORAL										0x1406
#define FID_BITSTREAM_COMMERCIAL_SCRIPT										0x3405
#define FID_PS_COMMERCIAL_SCRIPT										0x2405
#define FID_DTC_COMMERCIAL_SCRIPT										0x1405
#define FID_BITSTREAM_KAUFMANN										0x3404
#define FID_PS_KAUFMANN										0x2404
#define FID_DTC_KAUFMANN										0x1404
#define FID_BITSTREAM_PARK_AVENUE										0x3403
#define FID_PS_PARK_AVENUE										0x2403
#define FID_DTC_PARK_AVENUE										0x1403
#define FID_BITSTREAM_BRUSH_SCRIPT										0x3402
#define FID_PS_BRUSH_SCRIPT										0x2402
#define FID_DTC_BRUSH_SCRIPT										0x1402
#define FID_BITSTREAM_VIVALDI										0x3401
#define FID_PS_VIVALDI										0x2401
#define FID_DTC_VIVALDI										0x1401
#define FID_BITSTREAM_ZAPF_CHANCERY										0x3400
#define FID_PS_ZAPF_CHANCERY										0x2400
#define FID_DTC_ZAPF_CHANCERY										0x1400
#define FID_BITSTREAM_AVANTE_GARDE_CONDENSED										0x323d
#define FID_PS_AVANTE_GARDE_CONDENSED										0x223d
#define FID_DTC_AVANTE_GARDE_CONDENSED										0x123d
#define FID_BITSTREAM_INSIGNIA										0x323c
#define FID_PS_INSIGNIA										0x223c
#define FID_DTC_INSIGNIA										0x123c
#define FID_BITSTREAM_INDUSTRIA										0x323b
#define FID_PS_INDUSTRIA										0x223b
#define FID_DTC_INDUSTRIA										0x123b
#define FID_BITSTREAM_DORIC_BOLD										0x323a
#define FID_PS_DORIC_BOLD										0x223a
#define FID_DTC_DORIC_BOLD										0x123a
#define FID_BITSTREAM_AKZINDENZ_GROTESK										0x3239
#define FID_PS_AKZINDENZ_GROTESK										0x2239
#define FID_DTC_AKZINDENZ_GROTESK										0x1239
#define FID_BITSTREAM_GROTESK										0x3238
#define FID_PS_GROTESK										0x2238
#define FID_DTC_GROTESK										0x1238
#define FID_BITSTREAM_TEMPO										0x3237
#define FID_PS_TEMPO										0x2237
#define FID_DTC_TEMPO										0x1237
#define FID_BITSTREAM_SYNTAX										0x3236
#define FID_PS_SYNTAX										0x2236
#define FID_DTC_SYNTAX										0x1236
#define FID_BITSTREAM_STONE_SANS										0x3235
#define FID_PS_STONE_SANS										0x2235
#define FID_DTC_STONE_SANS										0x1235
#define FID_BITSTREAM_SERIF_GOTHIC										0x3234
#define FID_PS_SERIF_GOTHIC										0x2234
#define FID_DTC_SERIF_GOTHIC										0x1234
#define FID_BITSTREAM_PRIMUS_ANTIQUA										0x3233
#define FID_PS_PRIMUS_ANTIQUA										0x2233
#define FID_DTC_PRIMUS_ANTIQUA										0x1233
#define FID_BITSTREAM_PRIMUS										0x3232
#define FID_PS_PRIMUS										0x2232
#define FID_DTC_PRIMUS										0x1232
#define FID_BITSTREAM_PRAXIS										0x3231
#define FID_PS_PRAXIS										0x2231
#define FID_DTC_PRAXIS										0x1231
#define FID_BITSTREAM_PANACHE										0x3230
#define FID_PS_PANACHE										0x2230
#define FID_DTC_PANACHE										0x1230
#define FID_BITSTREAM_OCR_B										0x322f
#define FID_PS_OCR_B										0x222f
#define FID_DTC_OCR_B										0x122f
#define FID_BITSTREAM_OCR_A										0x322e
#define FID_PS_OCR_A										0x222e
#define FID_DTC_OCR_A										0x122e
#define FID_BITSTREAM_NEWTEXT										0x322d
#define FID_PS_NEWTEXT										0x222d
#define FID_DTC_NEWTEXT										0x122d
#define FID_BITSTREAM_NEWS_GOTHIC										0x322c
#define FID_PS_NEWS_GOTHIC										0x222c
#define FID_DTC_NEWS_GOTHIC										0x122c
#define FID_BITSTREAM_NEUZEIT_GROTESK										0x322b
#define FID_PS_NEUZEIT_GROTESK										0x222b
#define FID_DTC_NEUZEIT_GROTESK										0x122b
#define FID_BITSTREAM_MIXAGE										0x322a
#define FID_PS_MIXAGE										0x222a
#define FID_DTC_MIXAGE										0x122a
#define FID_BITSTREAM_MAXIMA										0x3229
#define FID_PS_MAXIMA										0x2229
#define FID_DTC_MAXIMA										0x1229
#define FID_BITSTREAM_LUCIDA_SANS										0x3228
#define FID_PS_LUCIDA_SANS										0x2228
#define FID_DTC_LUCIDA_SANS										0x1228
#define FID_BITSTREAM_LITERA										0x3227
#define FID_PS_LITERA										0x2227
#define FID_DTC_LITERA										0x1227
#define FID_BITSTREAM_KABEL										0x3226
#define FID_PS_KABEL										0x2226
#define FID_DTC_KABEL										0x1226
#define FID_BITSTREAM_HOLSATIA										0x3225
#define FID_PS_HOLSATIA										0x2225
#define FID_DTC_HOLSATIA										0x1225
#define FID_BITSTREAM_HELVETICA_INSERAT										0x3224
#define FID_PS_HELVETICA_INSERAT										0x2224
#define FID_DTC_HELVETICA_INSERAT										0x1224
#define FID_BITSTREAM_NEUE_HELVETICA										0x3223
#define FID_PS_NEUE_HELVETICA										0x2223
#define FID_DTC_NEUE_HELVETICA										0x1223
#define FID_BITSTREAM_HELVETICA										0x3222
#define FID_PS_HELVETICA										0x2222
#define FID_DTC_HELVETICA										0x1222
#define FID_BITSTREAM_HAAS_UNICA										0x3221
#define FID_PS_HAAS_UNICA										0x2221
#define FID_DTC_HAAS_UNICA										0x1221
#define FID_BITSTREAM_GOUDY_SANS										0x3220
#define FID_PS_GOUDY_SANS										0x2220
#define FID_DTC_GOUDY_SANS										0x1220
#define FID_BITSTREAM_GOTHIC										0x321f
#define FID_PS_GOTHIC										0x221f
#define FID_DTC_GOTHIC										0x121f
#define FID_BITSTREAM_GILL_SANS										0x321e
#define FID_PS_GILL_SANS										0x221e
#define FID_DTC_GILL_SANS										0x121e
#define FID_BITSTREAM_GILL										0x321d
#define FID_PS_GILL										0x221d
#define FID_DTC_GILL										0x121d
#define FID_BITSTREAM_FUTURA										0x321c
#define FID_PS_FUTURA										0x221c
#define FID_DTC_FUTURA										0x121c
#define FID_BITSTREAM_FOLIO										0x321b
#define FID_PS_FOLIO										0x221b
#define FID_DTC_FOLIO										0x121b
#define FID_BITSTREAM_FLYER										0x321a
#define FID_PS_FLYER										0x221a
#define FID_DTC_FLYER										0x121a
#define FID_BITSTREAM_FETTE_MIDSCHRIFT										0x3219
#define FID_PS_FETTE_MIDSCHRIFT										0x2219
#define FID_DTC_FETTE_MIDSCHRIFT										0x1219
#define FID_BITSTREAM_FETTE_ENGSCHRIFT										0x3218
#define FID_PS_FETTE_ENGSCHRIFT										0x2218
#define FID_DTC_FETTE_ENGSCHRIFT										0x1218
#define FID_BITSTREAM_ERAS										0x3217
#define FID_PS_ERAS										0x2217
#define FID_DTC_ERAS										0x1217
#define FID_BITSTREAM_DIGI_GROTESK										0x3216
#define FID_PS_DIGI_GROTESK										0x2216
#define FID_DTC_DIGI_GROTESK										0x1216
#define FID_BITSTREAM_CORINTHIAN										0x3215
#define FID_PS_CORINTHIAN										0x2215
#define FID_DTC_CORINTHIAN										0x1215
#define FID_BITSTREAM_COMPACTA										0x3214
#define FID_PS_COMPACTA										0x2214
#define FID_DTC_COMPACTA										0x1214
#define FID_BITSTREAM_CLEARFACE_GOTHIC										0x3213
#define FID_PS_CLEARFACE_GOTHIC										0x2213
#define FID_DTC_CLEARFACE_GOTHIC										0x1213
#define FID_BITSTREAM_OPTIMA										0x3212
#define FID_PS_OPTIMA										0x2212
#define FID_DTC_OPTIMA										0x1212
#define FID_BITSTREAM_CHELMSFORD										0x3211
#define FID_PS_CHELMSFORD										0x2211
#define FID_DTC_CHELMSFORD										0x1211
#define FID_BITSTREAM_CASTLE										0x3210
#define FID_PS_CASTLE										0x2210
#define FID_DTC_CASTLE										0x1210
#define FID_BITSTREAM_BRITANNIC										0x320f
#define FID_PS_BRITANNIC										0x220f
#define FID_DTC_BRITANNIC										0x120f
#define FID_BITSTREAM_BERLINER_GROTESK										0x320e
#define FID_PS_BERLINER_GROTESK										0x220e
#define FID_DTC_BERLINER_GROTESK										0x120e
#define FID_BITSTREAM_BENGUIAT_GOTHIC										0x320d
#define FID_PS_BENGUIAT_GOTHIC										0x220d
#define FID_DTC_BENGUIAT_GOTHIC										0x120d
#define FID_BITSTREAM_AVANTE_GARDE										0x320c
#define FID_PS_AVANTE_GARDE										0x220c
#define FID_DTC_AVANTE_GARDE										0x120c
#define FID_BITSTREAM_ANZEIGEN_GROTESK										0x320b
#define FID_PS_ANZEIGEN_GROTESK										0x220b
#define FID_DTC_ANZEIGEN_GROTESK										0x120b
#define FID_BITSTREAM_ANTIQUE_OLIVE										0x320a
#define FID_PS_ANTIQUE_OLIVE										0x220a
#define FID_DTC_ANTIQUE_OLIVE										0x120a
#define FID_BITSTREAM_ALTERNATE_GOTHIC										0x3209
#define FID_PS_ALTERNATE_GOTHIC										0x2209
#define FID_DTC_ALTERNATE_GOTHIC										0x1209
#define FID_BITSTREAM_AKZIDENZ_GROTESK_BUCH										0x3208
#define FID_PS_AKZIDENZ_GROTESK_BUCH										0x2208
#define FID_DTC_AKZIDENZ_GROTESK_BUCH										0x1208
#define FID_BITSTREAM_AKZIDENZ_GROTESK										0x3207
#define FID_PS_AKZIDENZ_GROTESK										0x2207
#define FID_DTC_AKZIDENZ_GROTESK										0x1207
#define FID_BITSTREAM_AVENIR										0x3206
#define FID_PS_AVENIR										0x2206
#define FID_DTC_AVENIR										0x1206
#define FID_BITSTREAM_UNIVERS										0x3205
#define FID_PS_UNIVERS										0x2205
#define FID_DTC_UNIVERS										0x1205
#define FID_BITSTREAM_FRANKLIN_GOTHIC										0x3204
#define FID_PS_FRANKLIN_GOTHIC										0x2204
#define FID_DTC_FRANKLIN_GOTHIC										0x1204
#define FID_BITSTREAM_ANGRO										0x3203
#define FID_PS_ANGRO										0x2203
#define FID_DTC_ANGRO										0x1203
#define FID_BITSTREAM_EUROSTILE										0x3202
#define FID_PS_EUROSTILE										0x2202
#define FID_DTC_EUROSTILE										0x1202
#define FID_BITSTREAM_FRUTIGER										0x3201
#define FID_PS_FRUTIGER										0x2201
#define FID_DTC_FRUTIGER										0x1201
#define FID_BITSTREAM_URW_SANS										0x3200
#define FID_PS_URW_SANS										0x2200
#define FID_DTC_URW_SANS										0x1200
#define FID_BITSTREAM_GALLIARD_ROMAN_ITALIC										0x307e
#define FID_PS_GALLIARD_ROMAN_ITALIC										0x207e
#define FID_DTC_GALLIARD_ROMAN_ITALIC										0x107e
#define FID_BITSTREAM_GRANJON										0x307d
#define FID_PS_GRANJON										0x207d
#define FID_DTC_GRANJON										0x107d
#define FID_BITSTREAM_GARTH_GRAPHIC										0x307c
#define FID_PS_GARTH_GRAPHIC										0x207c
#define FID_DTC_GARTH_GRAPHIC										0x107c
#define FID_BITSTREAM_BAUER_BODONI										0x307b
#define FID_PS_BAUER_BODONI										0x207b
#define FID_DTC_BAUER_BODONI										0x107b
#define FID_BITSTREAM_BELWE										0x307a
#define FID_PS_BELWE										0x207a
#define FID_DTC_BELWE										0x107a
#define FID_BITSTREAM_CHARLEMAGNE										0x3079
#define FID_PS_CHARLEMAGNE										0x2079
#define FID_DTC_CHARLEMAGNE										0x1079
#define FID_BITSTREAM_TRAJAN										0x3078
#define FID_PS_TRAJAN										0x2078
#define FID_DTC_TRAJAN										0x1078
#define FID_BITSTREAM_ADOBE_GARAMOND										0x3077
#define FID_PS_ADOBE_GARAMOND										0x2077
#define FID_DTC_ADOBE_GARAMOND										0x1077
#define FID_BITSTREAM_ZAPF_INTERNATIONAL										0x3076
#define FID_PS_ZAPF_INTERNATIONAL										0x2076
#define FID_DTC_ZAPF_INTERNATIONAL										0x1076
#define FID_BITSTREAM_ZAPF_BOOK										0x3075
#define FID_PS_ZAPF_BOOK										0x2075
#define FID_DTC_ZAPF_BOOK										0x1075
#define FID_BITSTREAM_WORCESTER_ROUND										0x3074
#define FID_PS_WORCESTER_ROUND										0x2074
#define FID_DTC_WORCESTER_ROUND										0x1074
#define FID_BITSTREAM_WINDSOR										0x3073
#define FID_PS_WINDSOR										0x2073
#define FID_DTC_WINDSOR										0x1073
#define FID_BITSTREAM_WEISS										0x3072
#define FID_PS_WEISS										0x2072
#define FID_DTC_WEISS										0x1072
#define FID_BITSTREAM_WEIDEMANN										0x3071
#define FID_PS_WEIDEMANN										0x2071
#define FID_DTC_WEIDEMANN										0x1071
#define FID_BITSTREAM_WALBAUM										0x3070
#define FID_PS_WALBAUM										0x2070
#define FID_DTC_WALBAUM										0x1070
#define FID_BITSTREAM_VOLTA										0x306f
#define FID_PS_VOLTA										0x206f
#define FID_DTC_VOLTA										0x106f
#define FID_BITSTREAM_VENDOME										0x306e
#define FID_PS_VENDOME										0x206e
#define FID_DTC_VENDOME										0x106e
#define FID_BITSTREAM_VELJOVIC										0x306d
#define FID_PS_VELJOVIC										0x206d
#define FID_DTC_VELJOVIC										0x106d
#define FID_BITSTREAM_ADOBE_UTOPIA										0x306c
#define FID_PS_ADOBE_UTOPIA										0x206c
#define FID_DTC_ADOBE_UTOPIA										0x106c
#define FID_BITSTREAM_USHERWOOD										0x306b
#define FID_PS_USHERWOOD										0x206b
#define FID_DTC_USHERWOOD										0x106b
#define FID_BITSTREAM_URW_ANTIQUA										0x306a
#define FID_PS_URW_ANTIQUA										0x206a
#define FID_DTC_URW_ANTIQUA										0x106a
#define FID_BITSTREAM_TIMES_NEW_ROMAN										0x3069
#define FID_PS_TIMES_NEW_ROMAN										0x2069
#define FID_DTC_TIMES_NEW_ROMAN										0x1069
#define FID_BITSTREAM_TIMELESS										0x3068
#define FID_PS_TIMELESS										0x2068
#define FID_DTC_TIMELESS										0x1068
#define FID_BITSTREAM_TIFFANY										0x3067
#define FID_PS_TIFFANY										0x2067
#define FID_DTC_TIFFANY										0x1067
#define FID_BITSTREAM_TIEPOLO										0x3066
#define FID_PS_TIEPOLO										0x2066
#define FID_DTC_TIEPOLO										0x1066
#define FID_BITSTREAM_SWIFT										0x3065
#define FID_PS_SWIFT										0x2065
#define FID_DTC_SWIFT										0x1065
#define FID_BITSTREAM_STYMIE										0x3064
#define FID_PS_STYMIE										0x2064
#define FID_DTC_STYMIE										0x1064
#define FID_BITSTREAM_STRATFORD										0x3063
#define FID_PS_STRATFORD										0x2063
#define FID_DTC_STRATFORD										0x1063
#define FID_BITSTREAM_STONE_SERIF										0x3062
#define FID_PS_STONE_SERIF										0x2062
#define FID_DTC_STONE_SERIF										0x1062
#define FID_BITSTREAM_STONE_INFORMAL										0x3061
#define FID_PS_STONE_INFORMAL										0x2061
#define FID_DTC_STONE_INFORMAL										0x1061
#define FID_BITSTREAM_STEMPEL_SCHNEIDLER										0x3060
#define FID_PS_STEMPEL_SCHNEIDLER										0x2060
#define FID_DTC_STEMPEL_SCHNEIDLER										0x1060
#define FID_BITSTREAM_SOUVENIR										0x305f
#define FID_PS_SOUVENIR										0x205f
#define FID_DTC_SOUVENIR										0x105f
#define FID_BITSTREAM_SLIMBACH										0x305e
#define FID_PS_SLIMBACH										0x205e
#define FID_DTC_SLIMBACH										0x105e
#define FID_BITSTREAM_SERIFA										0x305d
#define FID_PS_SERIFA										0x205d
#define FID_DTC_SERIFA										0x105d
#define FID_BITSTREAM_SABON_ANTIQUA										0x305c
#define FID_PS_SABON_ANTIQUA										0x205c
#define FID_DTC_SABON_ANTIQUA										0x105c
#define FID_BITSTREAM_SABON										0x305b
#define FID_PS_SABON										0x205b
#define FID_DTC_SABON										0x105b
#define FID_BITSTREAM_ROMANA										0x305a
#define FID_PS_ROMANA										0x205a
#define FID_DTC_ROMANA										0x105a
#define FID_BITSTREAM_ROCKWELL										0x3059
#define FID_PS_ROCKWELL										0x2059
#define FID_DTC_ROCKWELL										0x1059
#define FID_BITSTREAM_RENAULT										0x3058
#define FID_PS_RENAULT										0x2058
#define FID_DTC_RENAULT										0x1058
#define FID_BITSTREAM_RALEIGH										0x3057
#define FID_PS_RALEIGH										0x2057
#define FID_DTC_RALEIGH										0x1057
#define FID_BITSTREAM_QUORUM										0x3056
#define FID_PS_QUORUM										0x2056
#define FID_DTC_QUORUM										0x1056
#define FID_BITSTREAM_PROTEUS										0x3055
#define FID_PS_PROTEUS										0x2055
#define FID_DTC_PROTEUS										0x1055
#define FID_BITSTREAM_PLANTIN										0x3054
#define FID_PS_PLANTIN										0x2054
#define FID_DTC_PLANTIN										0x1054
#define FID_BITSTREAM_PERPETUA										0x3053
#define FID_PS_PERPETUA										0x2053
#define FID_DTC_PERPETUA										0x1053
#define FID_BITSTREAM_PACELLA										0x3052
#define FID_PS_PACELLA										0x2052
#define FID_DTC_PACELLA										0x1052
#define FID_BITSTREAM_NOVARESE										0x3051
#define FID_PS_NOVARESE										0x2051
#define FID_DTC_NOVARESE										0x1051
#define FID_BITSTREAM_NIMROD										0x3050
#define FID_PS_NIMROD										0x2050
#define FID_DTC_NIMROD										0x1050
#define FID_BITSTREAM_NIKIS										0x304f
#define FID_PS_NIKIS										0x204f
#define FID_DTC_NIKIS										0x104f
#define FID_BITSTREAM_NAPOLEAN										0x304e
#define FID_PS_NAPOLEAN										0x204e
#define FID_DTC_NAPOLEAN										0x104e
#define FID_BITSTREAM_MODERN_NO_216										0x304d
#define FID_PS_MODERN_NO_216										0x204d
#define FID_DTC_MODERN_NO_216										0x104d
#define FID_BITSTREAM_MODERN										0x304c
#define FID_PS_MODERN										0x204c
#define FID_DTC_MODERN										0x104c
#define FID_BITSTREAM_MINISTER										0x304b
#define FID_PS_MINISTER										0x204b
#define FID_DTC_MINISTER										0x104b
#define FID_BITSTREAM_MESSIDOR										0x304a
#define FID_PS_MESSIDOR										0x204a
#define FID_DTC_MESSIDOR										0x104a
#define FID_BITSTREAM_MERIDIEN										0x3049
#define FID_PS_MERIDIEN										0x2049
#define FID_DTC_MERIDIEN										0x1049
#define FID_BITSTREAM_MEMPHIS										0x3048
#define FID_PS_MEMPHIS										0x2048
#define FID_DTC_MEMPHIS										0x1048
#define FID_BITSTREAM_MELIOR										0x3047
#define FID_PS_MELIOR										0x2047
#define FID_DTC_MELIOR										0x1047
#define FID_BITSTREAM_MARCONI										0x3046
#define FID_PS_MARCONI										0x2046
#define FID_DTC_MARCONI										0x1046
#define FID_BITSTREAM_MAGNUS										0x3045
#define FID_PS_MAGNUS										0x2045
#define FID_DTC_MAGNUS										0x1045
#define FID_BITSTREAM_MAGNA										0x3044
#define FID_PS_MAGNA										0x2044
#define FID_DTC_MAGNA										0x1044
#define FID_BITSTREAM_MADISON										0x3043
#define FID_PS_MADISON										0x2043
#define FID_DTC_MADISON										0x1043
#define FID_BITSTREAM_LUCIDA										0x3042
#define FID_PS_LUCIDA										0x2042
#define FID_DTC_LUCIDA										0x1042
#define FID_BITSTREAM_LUBALIN_GRAPH										0x3041
#define FID_PS_LUBALIN_GRAPH										0x2041
#define FID_DTC_LUBALIN_GRAPH										0x1041
#define FID_BITSTREAM_LIFE										0x3040
#define FID_PS_LIFE										0x2040
#define FID_DTC_LIFE										0x1040
#define FID_BITSTREAM_LEAWOOD										0x303f
#define FID_PS_LEAWOOD										0x203f
#define FID_DTC_LEAWOOD										0x103f
#define FID_BITSTREAM_KORINNA										0x303e
#define FID_PS_KORINNA										0x203e
#define FID_DTC_KORINNA										0x103e
#define FID_BITSTREAM_JENSON_OLD_STYLE										0x303d
#define FID_PS_JENSON_OLD_STYLE										0x203d
#define FID_DTC_JENSON_OLD_STYLE										0x103d
#define FID_BITSTREAM_JANSON										0x303c
#define FID_PS_JANSON										0x203c
#define FID_DTC_JANSON										0x103c
#define FID_BITSTREAM_JAMILLE										0x303b
#define FID_PS_JAMILLE										0x203b
#define FID_DTC_JAMILLE										0x103b
#define FID_BITSTREAM_ITALIA										0x303a
#define FID_PS_ITALIA										0x203a
#define FID_DTC_ITALIA										0x103a
#define FID_BITSTREAM_IMPRESSUM										0x3039
#define FID_PS_IMPRESSUM										0x2039
#define FID_DTC_IMPRESSUM										0x1039
#define FID_BITSTREAM_HOLLANDER										0x3038
#define FID_PS_HOLLANDER										0x2038
#define FID_DTC_HOLLANDER										0x1038
#define FID_BITSTREAM_HIROSHIGE										0x3037
#define FID_PS_HIROSHIGE										0x2037
#define FID_DTC_HIROSHIGE										0x1037
#define FID_BITSTREAM_HAWTHORN										0x3036
#define FID_PS_HAWTHORN										0x2036
#define FID_DTC_HAWTHORN										0x1036
#define FID_BITSTREAM_GOUDY										0x3035
#define FID_PS_GOUDY										0x2035
#define FID_DTC_GOUDY										0x1035
#define FID_BITSTREAM_GAMMA										0x3034
#define FID_PS_GAMMA										0x2034
#define FID_DTC_GAMMA										0x1034
#define FID_BITSTREAM_GALLIARD										0x3033
#define FID_PS_GALLIARD										0x2033
#define FID_DTC_GALLIARD										0x1033
#define FID_BITSTREAM_FRIZ_QUADRATA										0x3032
#define FID_PS_FRIZ_QUADRATA										0x2032
#define FID_DTC_FRIZ_QUADRATA										0x1032
#define FID_BITSTREAM_FENICE										0x3031
#define FID_PS_FENICE										0x2031
#define FID_DTC_FENICE										0x1031
#define FID_BITSTREAM_EXCELSIOR										0x3030
#define FID_PS_EXCELSIOR										0x2030
#define FID_DTC_EXCELSIOR										0x1030
#define FID_BITSTREAM_ESPRIT										0x302f
#define FID_PS_ESPRIT										0x202f
#define FID_DTC_ESPRIT										0x102f
#define FID_BITSTREAM_ELAN										0x302e
#define FID_PS_ELAN										0x202e
#define FID_DTC_ELAN										0x102e
#define FID_BITSTREAM_EGYPTIENNE										0x302d
#define FID_PS_EGYPTIENNE										0x202d
#define FID_DTC_EGYPTIENNE										0x102d
#define FID_BITSTREAM_EGIZIO										0x302c
#define FID_PS_EGIZIO										0x202c
#define FID_DTC_EGIZIO										0x102c
#define FID_BITSTREAM_EDWARDIAN										0x302b
#define FID_PS_EDWARDIAN										0x202b
#define FID_DTC_EDWARDIAN										0x102b
#define FID_BITSTREAM_EDISON										0x302a
#define FID_PS_EDISON										0x202a
#define FID_DTC_EDISON										0x102a
#define FID_BITSTREAM_DIGI_ANTIQUA										0x3029
#define FID_PS_DIGI_ANTIQUA										0x2029
#define FID_DTC_DIGI_ANTIQUA										0x1029
#define FID_BITSTREAM_DEMOS										0x3028
#define FID_PS_DEMOS										0x2028
#define FID_DTC_DEMOS										0x1028
#define FID_BITSTREAM_CUSHING										0x3027
#define FID_PS_CUSHING										0x2027
#define FID_DTC_CUSHING										0x1027
#define FID_BITSTREAM_CORONA										0x3026
#define FID_PS_CORONA										0x2026
#define FID_DTC_CORONA										0x1026
#define FID_BITSTREAM_CONGRESS										0x3025
#define FID_PS_CONGRESS										0x2025
#define FID_DTC_CONGRESS										0x1025
#define FID_BITSTREAM_CONCORDE_NOVA										0x3024
#define FID_PS_CONCORDE_NOVA										0x2024
#define FID_DTC_CONCORDE_NOVA										0x1024
#define FID_BITSTREAM_CONCORDE										0x3023
#define FID_PS_CONCORDE										0x2023
#define FID_DTC_CONCORDE										0x1023
#define FID_BITSTREAM_CLEARFACE										0x3022
#define FID_PS_CLEARFACE										0x2022
#define FID_DTC_CLEARFACE										0x1022
#define FID_BITSTREAM_CLARENDON										0x3021
#define FID_PS_CLARENDON										0x2021
#define FID_DTC_CLARENDON										0x1021
#define FID_BITSTREAM_CHELTENHAM										0x3020
#define FID_PS_CHELTENHAM										0x2020
#define FID_DTC_CHELTENHAM										0x1020
#define FID_BITSTREAM_CENTURY_OLD_STYLE										0x301f
#define FID_PS_CENTURY_OLD_STYLE										0x201f
#define FID_DTC_CENTURY_OLD_STYLE										0x101f
#define FID_BITSTREAM_CENTURY										0x301e
#define FID_PS_CENTURY										0x201e
#define FID_DTC_CENTURY										0x101e
#define FID_BITSTREAM_CENTENNIAL										0x301d
#define FID_PS_CENTENNIAL										0x201d
#define FID_DTC_CENTENNIAL										0x101d
#define FID_BITSTREAM_CAXTON										0x301c
#define FID_PS_CAXTON										0x201c
#define FID_DTC_CAXTON										0x101c
#define FID_BITSTREAM_ADOBE_CASLON										0x301b
#define FID_PS_ADOBE_CASLON										0x201b
#define FID_DTC_ADOBE_CASLON										0x101b
#define FID_BITSTREAM_CASLON										0x301a
#define FID_PS_CASLON										0x201a
#define FID_DTC_CASLON										0x101a
#define FID_BITSTREAM_CANDIDA										0x3019
#define FID_PS_CANDIDA										0x2019
#define FID_DTC_CANDIDA										0x1019
#define FID_BITSTREAM_BOOKMAN										0x3018
#define FID_PS_BOOKMAN										0x2018
#define FID_DTC_BOOKMAN										0x1018
#define FID_BITSTREAM_BASKERVILLE_HANDCUT										0x3017
#define FID_PS_BASKERVILLE_HANDCUT										0x2017
#define FID_DTC_BASKERVILLE_HANDCUT										0x1017
#define FID_BITSTREAM_BASKERVILLE										0x3016
#define FID_PS_BASKERVILLE										0x2016
#define FID_DTC_BASKERVILLE										0x1016
#define FID_BITSTREAM_BASILIA										0x3015
#define FID_PS_BASILIA										0x2015
#define FID_DTC_BASILIA										0x1015
#define FID_BITSTREAM_BARBEDOR										0x3014
#define FID_PS_BARBEDOR										0x2014
#define FID_DTC_BARBEDOR										0x1014
#define FID_BITSTREAM_AUREALIA										0x3013
#define FID_PS_AUREALIA										0x2013
#define FID_DTC_AUREALIA										0x1013
#define FID_BITSTREAM_NEW_ASTER										0x3012
#define FID_PS_NEW_ASTER										0x2012
#define FID_DTC_NEW_ASTER										0x1012
#define FID_BITSTREAM_ASTER										0x3011
#define FID_PS_ASTER										0x2011
#define FID_DTC_ASTER										0x1011
#define FID_BITSTREAM_AMERICANA										0x3010
#define FID_PS_AMERICANA										0x2010
#define FID_DTC_AMERICANA										0x1010
#define FID_BITSTREAM_AACHEN										0x300f
#define FID_PS_AACHEN										0x200f
#define FID_DTC_AACHEN										0x100f
#define FID_BITSTREAM_NICOLAS_COCHIN										0x300e
#define FID_PS_NICOLAS_COCHIN										0x200e
#define FID_DTC_NICOLAS_COCHIN										0x100e
#define FID_BITSTREAM_COCHIN										0x300d
#define FID_PS_COCHIN										0x200d
#define FID_DTC_COCHIN										0x100d
#define FID_BITSTREAM_ALBERTUS										0x300c
#define FID_PS_ALBERTUS										0x200c
#define FID_DTC_ALBERTUS										0x100c
#define FID_BITSTREAM_ACCOLADE										0x300b
#define FID_PS_ACCOLADE										0x200b
#define FID_DTC_ACCOLADE										0x100b
#define FID_BITSTREAM_PALATINO										0x300a
#define FID_PS_PALATINO										0x200a
#define FID_DTC_PALATINO										0x100a
#define FID_BITSTREAM_GOUDY_OLD_STYLE										0x3009
#define FID_PS_GOUDY_OLD_STYLE										0x2009
#define FID_DTC_GOUDY_OLD_STYLE										0x1009
#define FID_BITSTREAM_BERKELEY_OLD_STYLE										0x3008
#define FID_PS_BERKELEY_OLD_STYLE										0x2008
#define FID_DTC_BERKELEY_OLD_STYLE										0x1008
#define FID_BITSTREAM_ARSIS										0x3007
#define FID_PS_ARSIS										0x2007
#define FID_DTC_ARSIS										0x1007
#define FID_BITSTREAM_UNIVERSITY_ROMAN										0x3006
#define FID_PS_UNIVERSITY_ROMAN										0x2006
#define FID_DTC_UNIVERSITY_ROMAN										0x1006
#define FID_BITSTREAM_BEMBO										0x3005
#define FID_PS_BEMBO										0x2005
#define FID_DTC_BEMBO										0x1005
#define FID_BITSTREAM_GARAMOND										0x3004
#define FID_PS_GARAMOND										0x2004
#define FID_DTC_GARAMOND										0x1004
#define FID_BITSTREAM_GLYPHA										0x3003
#define FID_PS_GLYPHA										0x2003
#define FID_DTC_GLYPHA										0x1003
#define FID_BITSTREAM_BODONI										0x3002
#define FID_PS_BODONI										0x2002
#define FID_DTC_BODONI										0x1002
#define FID_BITSTREAM_CENTURY_SCHOOLBOOK										0x3001
#define FID_PS_CENTURY_SCHOOLBOOK										0x2001
#define FID_DTC_CENTURY_SCHOOLBOOK										0x1001
#define FID_BITSTREAM_URW_ROMAN										0x3000
#define FID_PS_TIMES_ROMAN										0x2000
#define FID_DTC_URW_ROMAN										0x1000
#define FID_WINDOWS										0x0a01
#define FID_BISON										0x0a00
#define FID_LED										0x0600
#define FID_PMSYSTEM										0x0203
#define FID_BERKELEY										0x0202
#define FID_UNIVERSITY										0x0201
#define FID_CHICAGO										0x0200
#define FID_ROMA										0x0001
#define FID_INVALID										0x0000

Fonts are normally referenced by FontID.

Include: fontID.h

FontMaker

typedef word FontMaker;
#define FM_PRINTER 				0xf000
#define FM_MICROLOGIC 				0xe000
#define FM_ATECH 				0xd000
#define FM_PUBLIC 				0xc000
#define FM_AGFA 				0x4000
#define FM_BITSTREAM 				0x3000
#define FM_ADOBE 				0x2000
#define FM_NIMBUSQ 				0x1000
#define FM_BITMAP 				0x0000

Include: fontID.h

FontMap

typedef byte FontMap;
#define FM_DONT_USE				0x00ff
#define FM_EXACT				0x0000

Include: fontID.h

FontWeight

typedef ByteEnum FontWeight;
#define FW_ULTRA_LIGHT					0
#define FW_EXTRA_LIGHT 					1
#define FW_LIGHT 					2
#define FW_BOOK 					3
#define FW_NORMAL 					4
#define FW_DEMI 					5
#define FW_BOLD 					6
#define FW_EXTRA_BOLD 					7
#define FW_ULTRA_BOLD 					8
#define FW_BLACK 					9

Include: font.h

FontWidth

typedef 	ByteEnum FontWidth;
#define FWI_NARROW 					0
#define FWI_CONDENSED 					1
#define FWI_MEDIUM 					2
#define FWI_WIDE 					3
#define FWI_EXPANDED 					4

Include: font.h

FormatArray

typedef ClipboardItemFormatInfo FormatArray[CLIPBOARD_MAX_FORMATS];

FormatError

typedef ByteEnum FormatError;
#define FMT_DONE												0
#define FMT_READY												1
#define FMT_RUNNING 												2
#define FMT_DRIVE_NOT_READY 												3
#define FMT_ERR_WRITING_BOOT 												4
#define FMT_ERR_WRITING_ROOT_DIR												5
#define FMT_ERR_WRITING_FAT 												6
#define FMT_ABORTED 												7
#define FMT_SET_VOLUME_NAME_ERR 												8
#define FMT_CANNOT_FORMAT_FIXED_DISKS_IN_CUR_RELEASE 												9
#define FMT_BAD_PARTITION_TABLE 												10
#define FMT_ERR_READING_PARTITION_TABLE 												11
#define FMT_ERR_NO_PARTITION_FOUND 												12
#define FMT_ERR_MULTIPLE_PRIMARY_PARTITIONS 												13
#define FMT_ERR_NO_EXTENDED_PARTITION_FOUND 												14
#define FMT_ERR_CANNOT_ALLOC_SECTOR_BUFFER 												15
#define FMT_ERR_DISK_IS_IN_USE 												16
#define FMT_ERR_WRITE_PROTECTED 												17
#define FMT_ERR_DRIVE_CANNOT_SUPPORT_GIVEN_FORMAT 												18
#define FMT_ERR_INVALID_DRIVE_SPECIFIED 												19
#define FMT_ERR_DRIVE_CANNOT_BE_FORMATTED 												20
#define FMT_ERR_DISK_UNAVAILABLE 												21

FormatIDFromManufacturerAndType

ClipboardItemFormatID FormatIDFromManufacturerAndType(man, typ)
        ManufacturerID		man;
        word		typ;

This macro creates a ClipboardItemFormatID dword value from the given manufacturer ID and format ID.

FunctionID

typedef enum /* word */ {
FUNCTION_ID_ABS,
FUNCTION_ID_ACOS,
FUNCTION_ID_ACOSH,
FUNCTION_ID_AND,
FUNCTION_ID_ASIN,
FUNCTION_ID_ASINH,
FUNCTION_ID_ATAN,
FUNCTION_ID_ATAN2,
FUNCTION_ID_ATANH,
FUNCTION_ID_AVG,
FUNCTION_ID_CHAR,
FUNCTION_ID_CHOOSE,
FUNCTION_ID_CLEAN,
FUNCTION_ID_CODE,
FUNCTION_ID_COLS,
FUNCTION_ID_COS,
FUNCTION_ID_COSH,
FUNCTION_ID_COUNT,
FUNCTION_ID_CTERM,
FUNCTION_ID_DATE,
FUNCTION_ID_DATEVALUE,
FUNCTION_ID_DAY,
FUNCTION_ID_DDB,
FUNCTION_ID_ERR,
FUNCTION_ID_EXACT,
FUNCTION_ID_EXP,
FUNCTION_ID_FACT,
FUNCTION_ID_FALSE,
FUNCTION_ID_FIND,
FUNCTION_ID_FV,
FUNCTION_ID_HLOOKUP,
FUNCTION_ID_HOUR,
FUNCTION_ID_IF,
FUNCTION_ID_INDEX,
FUNCTION_ID_INT,
FUNCTION_ID_IRR,
FUNCTION_ID_ISERR,
FUNCTION_ID_ISNUMBER,
FUNCTION_ID_ISSTRING,
FUNCTION_ID_LEFT,
FUNCTION_ID_LENGTH,
FUNCTION_ID_LN,
FUNCTION_ID_LOG,
FUNCTION_ID_LOWER,
FUNCTION_ID_MAX,
FUNCTION_ID_MID,
FUNCTION_ID_MIN,
FUNCTION_ID_MINUTE,
FUNCTION_ID_MOD,
FUNCTION_ID_MONTH,
FUNCTION_ID_N,
FUNCTION_ID_NA,
FUNCTION_ID_NOW,
FUNCTION_ID_NPV,
FUNCTION_ID_OR,
FUNCTION_ID_PI,
FUNCTION_ID_PMT,
FUNCTION_ID_PRODUCT,
FUNCTION_ID_PROPER,
FUNCTION_ID_PV,
FUNCTION_ID_RANDOM_N,
FUNCTION_ID_RANDOM,
FUNCTION_ID_RATE,
FUNCTION_ID_REPEAT,
FUNCTION_ID_REPLACE,
FUNCTION_ID_RIGHT,
FUNCTION_ID_ROUND,
FUNCTION_ID_ROWS,
FUNCTION_ID_SECOND,
FUNCTION_ID_SIN,
FUNCTION_ID_SINH,
FUNCTION_ID_SLN,
FUNCTION_ID_SQRT,
FUNCTION_ID_STD,
FUNCTION_ID_STDP,
FUNCTION_ID_STRING,
FUNCTION_ID_SUM,
FUNCTION_ID_SYD,
FUNCTION_ID_TAN,
FUNCTION_ID_TANH,
FUNCTION_ID_TERM,
FUNCTION_ID_TIME,
FUNCTION_ID_TIMEVALUE,
FUNCTION_ID_TODAY,
FUNCTION_ID_TRIM,
FUNCTION_ID_TRUE,
FUNCTION_ID_TRUNC,
FUNCTION_ID_UPPER,
FUNCTION_ID_VALUE,
FUNCTION_ID_VAR,
FUNCTION_ID_VARP,
FUNCTION_ID_VLOOKUP,
FUNCTION_ID_WEEKDAY,
FUNCTION_ID_YEAR,
FUNCTION_ID_FILENAME,
FUNCTION_ID_PAGE,
FUNCTION_ID_PAGES,
FUNCTION_ID_FIRST_EXTERNAL_FUNCTION=FUNCTION_ID_FIRST_EXTERNAL_FUNCTION_BASE
} FunctionID;

GCM_info

typedef enum /* word */ {
GCMI_MIN_X,
GCMI_MIN_X_ROUNDED,
GCMI_MIN_Y,
GCMI_MIN_Y_ROUNDED,
GCMI_MAX_X,
GCMI_MAX_X_ROUNDED,
GCMI_MAX_Y,
GCMI_MAX_Y_ROUNDED,
} GCM_info;

GCNDriveChangeNotificationType

typedef enum {
GCNDCNT_CREATED,
GCNDCNT_DESTROYED
} GCNDriveChangeNotificationType;

GCNExpressMenuNotificationType

typedef enum {
GCNEMNT_CREATED,
GCNEMNT_DESTROYED
} GCNExpressMenuNotificationType;

GCNListBlockHeader

typedef struct {
LMemBlockHeader			GCNLBH_lmemHeader;
ChunkHandle			GCNLBH_listOfLists;
} GCNListBlockHeader;

GCNListElement

typedef struct {
optr	GCNLE_item;
} GCNListElement;

GCNListHeader

typedef struct {
ChunkArrayHeader				GCNLH_meta;
word				GCNLH_statusEvent;
MemHandle				GCNLH_statusData;
word				GCNLH_statusCount;
/* Start of GCNListOfListElements */
} GCNListHeader;

GCNListOfListsElement

typedef struct {
GCNListType			GCNLOLE_ID;
ChunkHandle			GCNLOLE_list;
} GCNListOfListsElement;

GCNListOfListsHeader

typedef struct {
ChunkArrayHeader				GCNLOL_meta;
/* Start of GCNListOfListsElement's */
} GCNListOfListsHeader;

GCNListParams

typedef struct {
GCNListType		GCNLP_ID;
optr		GCNLP_optr;
} GCNListParams;

GCNListSendFlags

typedef WordFlags GCNListSendFlags;
#define GCNLSF_SET_STATUS										0x8000
#define GCNLSF_IGNORE_IF_STATUS_TRANSITIONING										0x4000

GCNListType

typedef struct {
word	GCNLT_manuf;
word	GCNLT_type;
} GCNListType;

GCNListTypeFlags

typedef WordFlags GCNListTypeFlags;
#define GCNLTF_SAVE_TO_STATE					0x8000

GCNShutdownControlType

typedef enum {
GCNSCT_SUSPEND,
GCNSCT_SHUTDOWN,
GCNSCT_UNSUSPEND
} GCNShutdownControlType;

GCNStandardListType

typedef enum {
GCNSLT_FILE_SYSTEM,
GCNSLT_APPLICATION,
GCNSLT_DATE_TIME,
GCNSLT_DICTIONARY,
GCNSLT_EXPRESS_MENU,
GCNSLT_SHUTDOWN_CONTROL
} GCNStandardListType;

GenAppGCNListTypes

In Goc, GenAppGCNListTypes values will be maintained other structures by other names. Standard GEOS list types are part of the GeoWorksGenAppGCNListType enumerated type.

GeneralEvent

typedef enum {
 GE_NO_EVENT=0, 				/* dummy event (NOP) */
 GE_END_OF_SONG=2, 				/* marks end of song */
 GE_SET_PRIORITY=4, 				/* changes sound priority */
 GE_SET_TEMPO=6, 				/* changes sound tempo */
 GE_SEND_NOTIFICATION=8, 				/* sends encoded message */
 GE_V_SEMAPHORE=10 				/* V's a specified semaphore*/
} GeneralEvent;

These represent some of the miscellaneous events which can make up a music buffer.

GenTravelOption

The GenClass defines some values meant to be used in the place of a TravelOption enumerated value. See TravelOption .

GeodeAttrs

typedef WordFlags GeodeAttrs;
#define GA_PROCESS 								0x8000
#define GA_LIBRARY 								0x4000
#define GA_DRIVER 								0x2000
#define GA_KEEP_FILE_OPEN 								0x1000
#define GA_SYSTEM 								0x0800
#define GA_MULTI_LAUNCHABLE 								0x0400
#define GA_APPLICATION 								0x0200
#define GA_DRIVER_INITIALIZED								0x0100
#define GA_LIBRARY_INITIALIZED 								0x0080
#define GA_GEODE_INITIALIZED 								0x0040
#define GA_USES_COPROC 								0x0020
#define GA_REQUIRES_COPROC 								0x0010
#define GA_HAS_GENERAL_CONSUMER_MODE								0x0008
#define GA_ENTRY_POINTS_IN_C 								0x0004

GeodeDefaultDriverType

typedef enum {
GDDT_FILE_SYSTEM = 0,					/* File system driver */
GDDT_KEYBOARD = 2,					/* Keyboard driver */
GDDT_MOUSE = 4,					/* Mouse driver */
GDDT_VIDEO = 6,					/* Video driver */
GDDT_MEMORY_VIDEO = 8,					/* Vidmem driver */
GDDT_POWER_MANAGEMENT = 10					/* Power management driver */
GDDT_TASK = 12					/* Task driver */
} GeodeDefaultDriverType;

The default driver type has one value for each default driver type in GEOS. This type is used with GeodeGetDefaultDriver() and GeodeSetDefaultDriver() .

GeodeGetInfoType

See GeodeGetInfo() .

GeodeHandle

typedef Handle GeodeHandle;

A standard handle that contains information about a loaded geode. When a geode has been loaded, it is referred to by its handle.

GeodeHeapVars

typedef struct {
 word		GHV_heapSpace;
} GeodeHeapVars;

GeodeLoadError

typedef enum {
GLE_PROTOCOL_IMPORTER_TOO_RECENT,
GLE_PROTOCOL_IMPORTER_TOO_OLD,
GLE_FILE_NOT_FOUND,
GLE_LIBRARY_NOT_FOUND,
GLE_FILE_READ_ERROR,
GLE_NOT_GEOS_FILE,
GLE_NOT_GEOS_EXECUTABLE_FILE,
GLE_ATTRIBUTE_MISMATCH,
GLE_MEMORY_ALLOCATION_ERROR,
GLE_NOT_MULTI_LAUNCHABLE,
GLE_LIBRARY_PROTOCOL_ERROR,
GLE_LIBRARY_LOAD_ERROR,
GLE_DRIVER_INIT_ERROR,
GLE_LIBRARY_INIT_ERROR,
GLE_DISK_TOO_FULL,
GLE_FIELD_DETACHING,
} GeodeLoadError;

These errors may be returned by routines that load geodes, including UserLoadApplication() , GeodeUseLibrary() , GeodeUseDriver() , and GeodeLoad() .

GeodeToken

typedef struct {
TokenChars			GT_chars;
ManufacturerID			GT_manufID;
} GeodeToken;

Defines a token identifier. The GT_chars field is four characters that identify the token; GT_manufID is the identifying number of the manufacturer of the item being referenced.

GeosFileHeaderFlags

typedef WordFlags GeosFileHeaderFlags;
#define GFHF_TEMPLATE						0x8000
#define GFHF_SHARED_MULTIPLE						0x4000
#define GFHF_SHARED_SINGLE						0x2000

GEOS

GeosFileType

typedef enum /* word */ {
GFT_NOT_GEOS_FILE,
GFT_EXECUTABLE,
GFT_VM,
GFT_DATA,
GFT_DIRECTORY,
GFT_LINK
} GeosFileType;

files are divided into several broad categories. You can find out a file's category by getting its FEA_FILE_TYPE extended attribute. This attribute is a member of the GeosFileType enumerated type. This type has the following values:

GFT_NOT_GEOS_FILE
The file is not a GEOS file. This constant is guaranteed to be equal to zero.
GFT_EXECUTABLE
The file is executable; in other words, it is some kind of geode.
GFT_VM
The file is a VM file.
GFT_DATA
The file is a GEOS byte file (see below).
GFT_DIRECTORY
The file is a GEOS directory (not yet implemented).
GFT_LINK
The file is a symbolic link (not yet implemented).

GeoWorksGenAppGCNListType

typedef enum /* word */ {
GAGCNLT_SELF_LOAD_OPTIONS = 0x6800,
GAGCNLT_GEN_CONTROL_NOTIFY_STATUS_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SELECT_STATE_CHANGE,
GAGCNLT_EDIT_CONTROL_NOTIFY_UNDO_STATE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_CHAR_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_PARA_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_TYPE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_SELECTION_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_COUNT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_STYLE_TEXT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_STYLE_SHEET_TEXT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_STYLE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FONT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_POINT_SIZE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FONT_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_JUSTIFICATION_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_FG_COLOR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_BG_COLOR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_PARA_COLOR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_BORDER_COLOR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SEARCH_SPELL_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SEARCH_REPLACE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_CHART_TYPE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_CHART_GROUP_FLAGS,
GAGCNLT_APP_TARGET_NOTIFY_CHART_AXIS_ATTRIBUTES,
GAGCNLT_APP_TARGET_NOTIFY_CHART_MARKER_SHAPE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_CURRENT_TOOL_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_BODY_SELECTION_STATE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_AREA_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_LINE_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_TEXT_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_STYLE_GROBJ_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_STYLE_SHEET_GROBJ_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_BODY_INSTRUCTION_FLAGS_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_GRADIENT_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_RULER_TYPE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_RULER_GRID_CHANGE,
GAGCNLT_TEXT_RULER_OBJECTS,
GAGCNLT_APP_TARGET_NOTIFY_BITMAP_CURRENT_TOOL_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_BITMAP_CURRENT_FORMAT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_FIELD_PROPERTIES_STATUS_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_FIELD_LIST_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_RCP_STATUS_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_FIELD_APPEARANCE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_DUMMY_CHANGE_2,
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_DUMMY_CHANGE_3,
GAGCNLT_APP_NOTIFY_DOC_SIZE_CHANGE,
GAGCNLT_APP_NOTIFY_PAPER_SIZE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_VIEW_STATE_CHANGE,
GAGCNLT_CONTROLLED_GEN_VIEW_OBJECTS,
GAGCNLT_APP_TARGET_NOTIFY_INK_STATE_CHANGE,
GAGCNLT_CONTROLLED_INK_OBJECTS,
GAGCNLT_APP_TARGET_NOTIFY_PAGE_STATE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_DOCUMENT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_DISPLAY_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_DISPLAY_LIST_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_MARKER_SHAPE,
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_POINT,
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_POLYLINE,
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_SMOOTHNESS,
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_OPEN_CLOSE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_ACTIVE_CELL_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_EDIT_BAR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_SELECTION_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_CELL_WIDTH_HEIGHT_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_DOC_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_CELL_ATTR_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_CELL_NOTES_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_DATA_RANGE_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_TEXT_NAME_CHANGE,
GAGCNLT_FLOAT_FORMAT_CHANGE,
GAGCNLT_DISPLAY_OBJECTS_WITH_RULERS,
GAGCNLT_APP_TARGET_NOTIFY_APP_CHANGE,
GAGCNLT_APP_TARGET_NOTIFY_LIBRARY_CHANGE,
GAGCNLT_WINDOWS,
GAGCNLT_STARTUP_LOAD_OPTIONS
} GeoWorksGenAppGCNListType;

GeoworksMediumID

typedef enum {
            GMID_INVALID = 0x0,
            GMID_SERIAL_CABLE, 				/* unit is SerialPortNum */
            GMID_INFRARED, 				/* unit is SerialPortNum */
            GMID_DATA_MODEM, 				/* unit is SerialPortNum */
            GMID_FAX_MODEM, 				/* unit is SerialPortNum */
            GMID_PRINTER, 				/* unit is printer name in mem block */
            GMID_PARALLEL_PORT, 				/* unit is ParallelPortNum */
            GMID_NETWORK, 				/* unit is NetID */
            GMID_LOOPBACK, 				/* no units */
            GMID_SM 				/* Small Message--no units */
} GeoworksMediumID;

These values, together with MANUFACTURER_ID_GEOWORKS, form MediumType values representing certain standard communications media.

Include: medium.h

GeoWorksMetaGCNListType

typedef enum /* word */ {
MGCNLT_ACTIVE_LIST = 0x00,
MGCNLT_APP_STARTUP = 0x02
} GeoWorksMetaGCNListType;

GeoWorksNotificationType

typedef enum {
GWNT_INK,
GWNT_GEN_CONTROL_NOTIFY_STATUS_CHANGE,
GWNT_SELECT_STATE_CHANGE,
GWNT_UNDO_STATE_CHANGE,
GWNT_STYLE_CHANGE,
GWNT_STYLE_SHEET_CHANGE,
GWNT_TEXT_CHAR_ATTR_CHANGE,
GWNT_TEXT_PARA_ATTR_CHANGE,
GWNT_TEXT_TYPE_CHANGE,
GWNT_TEXT_SELECTION_CHANGE,
GWNT_TEXT_COUNT_CHANGE,
GWNT_TEXT_STYLE_CHANGE,
GWNT_FONT_CHANGE,
GWNT_POINT_SIZE_CHANGE,
GWNT_FONT_ATTR_CHANGE,
GWNT_JUSTIFICATION_CHANGE,
GWNT_TEXT_FG_COLOR_CHANGE,
GWNT_TEXT_BG_COLOR_CHANGE,
GWNT_TEXT_PARA_COLOR_CHANGE,
GWNT_TEXT_BORDER_COLOR_CHANGE,
GWNT_SEARCH_REPLACE_ENABLE_CHANGE,
GWNT_SPELL_ENABLE_CHANGE,
GWNT_CHART_TYPE_CHANGE,
GWNT_CHART_GROUP_FLAGS,
GWNT_CHART_AXIS_ATTRIBUTES,
GWNT_GROBJ_CURRENT_TOOL_CHANGE,
GWNT_GROBJ_BODY_SELECTION_STATE_CHANGE,
GWNT_GROBJ_AREA_ATTR_CHANGE,
GWNT_GROBJ_LINE_ATTR_CHANGE,
GWNT_GROBJ_TEXT_ATTR_CHANGE,
GWNT_GROBJ_BODY_INSTRUCTION_FLAGS_CHANGE,
GWNT_GROBJ_GRADIENT_ATTR_CHANGE,
GWNT_RULER_TYPE_CHANGE,
GWNT_RULER_GRID_CHANGE,
GWNT_RULER_GUIDE_CHANGE,
GWNT_BITMAP_CURRENT_TOOL_CHANGE,
GWNT_BITMAP_CURRENT_FORMAT_CHANGE,
GWNT_FLAT_FILE_FIELD_PROPERTIES_STATUS_CHANGE,
GWNT_FLAT_FILE_FIELD_LIST_CHANGE,
GWNT_FLAT_FILE_RCP_STATUS_CHANGE,
GWNT_FLAT_FIELD_APPEARANCE_CHANGE,
GWNT_FLAT_FILE_DUMMY_CHANGE_2,
GWNT_FLAT_FILE_DUMMY_CHANGE_3,
GWNT_SPOOL_DOC_OR_PAPER_SIZE,
GWNT_VIEW_STATE_CHANGE,
GWNT_INK_HAS_TARGET,
GWNT_PAGE_STATE_CHANGE,
GWNT_DOCUMENT_CHANGE,
GWNT_DISPLAY_CHANGE,
GWNT_DISPLAY_LIST_CHANGE,
GWNT_SPLINE_MARKER_SHAPE,
GWNT_SPLINE_POINT,
GWNT_SPLINE_POLYLINE,
GWNT_SPLINE_SMOOTHNESS,
GWNT_SPLINE_OPEN_CLOSE_CHANGE,
GWNT_UNUSED_1,
GWNT_SPREADSHEET_ACTIVE_CELL_CHANGE,
GWNT_SPREADSHEET_EDIT_BAR_CHANGE,
GWNT_SPREADSHEET_SELECTION_CHANGE,
GWNT_SPREADSHEET_CELL_WIDTH_HEIGHT_CHANGE,
GWNT_SPREADSHEET_DOC_ATTR_CHANGE,
GWNT_SPREADSHEET_CELL_ATTR_CHANGE,
GWNT_SPREADSHEET_CELL_NOTES_CHANGE,
GWNT_SPREADSHEET_DATA_RANGE_CHANGE,
GWNT_FLOAT_FORMAT_CHANGE,
GWNT_MAP_APP_CHANGE,
GWNT_MAP_LIBRARY_CHANGE,
GWNT_TEXT_NAME_CHANGE,
GWNT_CARD_BACK_CHANGE,
GWNT_TEXT_OBJECT_HAS_FOCUS,
GWNT_TEXT_CONTEXT,
GWNT_TEXT_REPLACE_WITH_HWR,
GWNT_HELP_CONTEXT_CHANGE,
GWNT_FLOAT_FORMAT_INIT,
GWNT_HARD_ICON_BAR_FUNCTION,
GWNT_STARTUP_INDEXED_APP,
GWNT_SPOOL_PRINTING_COMPLETE,
GWNT_MODAL_WIN_CHANGE,
GWNT_SPREADSHEET_NAME_CHANGE,
GWNT_DOCUMENT_OPEN_COMPLETE,
GWNT_EMAIL_SCAN_INBOX,
GWNT_FOCUS_WINDOW_KBD_STATUS,
GWNT_TAB_DOUBLE_CLICK,
GWNT_PAGE_INFO_STATE_CHANGE,
GWNT_CURSOR_POSITION_CHANGE,
GWNT_FAX_NEW_JOB_CREATED,
GWNT_FAX_NEW_JOB_COMPLETED,
GWNT_EMAIL_DATABASE_CHANGE,
GWNT_EMAIL_STATUS_CHANGE,
GWNT_EMAIL_PAGE_PANEL_UPDATE,
GWNT_PCCOM_DISPLAY_CHAR,
GWNT_PCCOM_DISPLAY_STRING,
GWNT_PCCOM_EXIT
} GeoWorksNotificationType;

GeoWorksVisContentGCNListType

typedef enum {
VCGCNLT_TARGET_NOTIFY_TEXT_PARA_ATTR_CHANGE = 0x4a00,
 PADDING_VCGCNLT_INVALID_ITEM_000
} GeoWorksVisContentGCNListType;

GetMaskType

typedef ByteEnum GetMaskType;
#define GMT_ENUM				0
#define GMT_BUFFER				1

GetPalType

typedef ByteEnum GetPalType;
#define GPT_ACTIVE				0
#define GPT_CUSTOM				1
#define GPT_DEFAULT				2

GFM_info

See GrFontMetrics() .

GOC_POINT macro

#define GOC_POINT(x,y) GOC_SW(x), GOC_SW(y)

This macro is useful when statically declaring GStrings. A GString is basically an array of bytes; this macro takes two word-length values and breaks them up into four comma-seperated bytes. Thus, they help to make GString commands with variable number of point data appear readable. For example:

...
GSDrawPolyline(3),
  GOC_POINT(0,6), GOC_POINT(10,6), GOC_POINT(10,1),
GSFillPolygon(4,ODD_EVEN),
  GOC_POINT(5,1), GOC_POINT(9,0),
  GOC_POINT(9,5), GOC_POINT(5,6),
...

GOC_SW(), GOC_WORD() macros

#define GOC_SW(a)       GOC_WORD(a)
#define GOC_WORD(a)     ((a)&0xff),(((a)&0xff00)>>8)

These macros are useful when you're statically defining a byte array which contains word-length values. Each macro breaks up a word-length value into two bytes, seperated by a comma.

GraphicPattern

typedef struct {
PatternType 		HP_type;
byte 		HP_data;
} GraphicPattern;

GSControl

typedef WordFlags GSControl;
#define GSC_PARTIAL 				0x0200
#define GSC_ONE 				0x0100
#define GSC_MISC 				0x0080
#define GSC_LABEL 				0x0040
#define GSC_ESCAPE 				0x0020
#define GSC_NEW_PAGE 				0x0010
#define GSC_XFORM 				0x0008
#define GSC_OUTPUT 				0x0004
#define GSC_ATTR 				0x0002
#define GSC_PATH 				0x0001

GSRetType

typedef ByteEnum GSRetType;
#define GSRT_COMPLETE				0
#define GSRT_FORM_FEED				1
#define GSRT_ONE 				2
#define GSRT_ESCAPE 				3
#define GSRT_OUTPUT 				4
#define GSRT_ELEMENT 				5
#define GSRT_FAULT 				0xff

GState

GStates are always referenced by means of GStateHandles, and are documented there.

GStateHandle

typedef Handle GStateHandle;

GStates, or graphics states, are used to interpret graphics commands. Any graphics command that draws anything takes a GStateHandle as an argument. Each GState is associated with a window, and the graphics system uses the GState to determine which window the command should affect.

The GState also holds considerable information determining how drawing commands will be carried out. For instance, it holds the line color. To draw a green line, first one routine set's the GState's line color to green. From then on (or until the line color is changed again), all lines drawn using that GState will be green. Thus, all commands that set color, pattern, or other drawing attributes take a GStateHandle argument.

GStateHandles are also used when creating bitmaps and graphics strings. In this case, the associated window is fake; all drawing commands passed a GStateHandle representing a bitmap or graphics string will affect the data structure instead of being drawn to screen.

GString

typedef void GString;

A GString (short for "Graphics Strings") represents a string of graphics commands. Each GString is made up of one or more GString elements, each of which corresponds to some standard graphics command.

GStrings may be created by means of drawing to a GStateHandle returned by GrCreateState() , but quite often GStrings are declared explicitly. The GString's data is often set up using macros like GSDrawLine() . These macros will output an opcode (of type GStringElement ) and format their macro arguments into data expected with the opcode.

For instance,

GSDrawLine(72, 144, 216, 288);

...would expand to the data:

(GStringElement)			GR_DRAW_LINE
GOC_SW(72), GOC_SW(144), GOC_SW(216), GOC_SW(288);

...where GOC_SW() is a macro that breaks up a word-length value into two bytes. Fundamentally, a GString is just an array of bytes.

Thus, these macros just represent data, though they look like normal kernel graphics commands.

GStringElement

typedef ByteEnum GStringElement;
/* The following elements are defined :
    (Miscellaneous GString opcodes:)
GR_END_STRING,
GR_COMMENT,					(data: variable (word (length of code), code))
GR_NULL_OP,
GR_SET_GSTRING_BOUNDS,					(data: 8 bytes 	(4 swords))
GR_LABEL,					(data: 2 bytes 	(word))
GR_ESCAPE,					(data: variable (word (size of code), code))
GR_NEW_PAGE,
    (Coordinate Transform opcodes:)
GR_APPLY_ROTATION,					(data: 4 bytes 	(WWFixed))
GR_APPLY_SCALE,					(data: 8 bytes 	(2 WWFixed))
GR_APPLY_TRANSLATION,					(data: 8 bytes 	(2 WWFixed))
GR_APPLY_TRANSFORM,					(data: 26 bytes	(4 WWFixed, 2 DWFixed))
GR_APPLY_TRANSLATION_DWORD,					(data: 8 bytes 	(2 sdwords))
GR_SET_TRANSFORM,					(data: 26 bytes (4 WWFixed, 2 DWFixed))
GR_SET_NULL_TRANSFORM,
GR_SET_DEFAULT_TRANSFORM,
GR_INIT_DEFAULT_TRANSFORM,
GR_SAVE_TRANSFORM,
GR_RESTORE_TRANSFORM,
    (Output opcodes:)
GR_DRAW_LINE,					(data: 8 bytes 	(4 swords))
GR_DRAW_LINE_TO,					(data: 4 bytes 	(2 swords))
GR_DRAW_REL_LINE_TO					(data: 8 bytes 	(2 WWFixed))
GR_DRAW_HLINE,					(data: 6 bytes 	(3 swords))
GR_DRAW_HLINE_TO,					(data: 2 bytes 	(sword))
GR_DRAW_VLINE,					(data: 6 bytes 	(3 swords))
GR_DRAW_VLINE_TO,					(data: 2 bytes 	(sword))
GR_DRAW_POLYLINE,					(data: variable (word (# of points), points)
GR_DRAW_ARC,					(data: 14 bytes	(ArcCloseType, 6 swords))
GR_DRAW_ARC_3POINT, 					(data: 14 bytes	(ArcCloseType, 6 swords))
GR_DRAW_ARC_3POINT_TO, 					(data: 10 bytes	(ArcCloseType, 4 swords))
GR_DRAW_REL_ARC_3POINT_TO, 					(data: 18 bytes	(ArcCloseType, 4 WWFixed))
GR_DRAW_RECT,					(data: 8 bytes 	(4 swords))
GR_DRAW_RECT_TO,					(data: 4 bytes 	(2 swords))
GR_DRAW_ROUND_RECT,					(data: 10 bytes	(word, 4 swords))
GR_DRAW_ROUND_RECT_TO,					(data: 6 bytes 	(word, 2 swords))
GR_DRAW_SPLINE,					(data: variable (word (# of points), points))
GR_DRAW_SPLINE_TO,					(data: variable (word (# of points), points))
GR_DRAW_CURVE,					(data: 16 bytes	(8 swords))
GR_DRAW_CURVE_TO, 					(data: 12 bytes	(6 swords))
GR_DRAW_REL_CURVE_TO, 					(data: 24 bytes	(6 WWFixed))
GR_DRAW_ELLIPSE,					(data: 8 bytes 	(4 swords))
GR_DRAW_POLYGON,					(data: variable (word (# of points), points))
GR_DRAW_POINT,					(data: 4 bytes 	(2 words))
GR_DRAW_POINT_CP,
GR_BRUSH_POLYLINE,					(data: variable (word (# of points), 2 bytes,
                            points))
GR_DRAW_CHAR,					(data: 5 bytes) 	(Chars, 2 swords))
GR_DRAW_CHAR_CP,					(data: 1 byte) 	(Chars))
GR_DRAW_TEXT,					(data: variable (sword, sword,
                     word (length of string),
                            string (not null terminated)))
GR_DRAW_TEXT_CP,					(data: variable (word (length of string),
                             string (not null terminated)))
GR_DRAW_TEXT_PTR,					(data: 6 bytes 	(2 swords, (char *)))
GR_DRAW_TEXT_OPTR,					(data: 6 bytes 	(2 swords, optr))
GR_DRAW_PATH,
GR_FILL_RECT,					(data: 8 bytes 	(4 swords))
GR_FILL_RECT_TO,					(data: 4 bytes 	(2 swords))
GR_FILL_ROUND_RECT,					(data: 10 bytes	(4 swords, word))
GR_FILL_ROUND_RECT_TO,					(data: 6 bytes 	(2 swords, word))
GR_FILL_ARC,					(data: 14 bytes (ArcCloseType, 6 swords))
GR_FILL_POLYGON,					(data: variable (word (# of points),
                            RegionFillRule, points))
GR_FILL_ELLIPSE,					(data: 8 bytes 	(2 swords))
GR_FILL_PATH,					(data: 1 byte 	 (RegionFillRule))
GR_FILL_ARC_3POINT, 					(data: 14 bytes	(ArcCloseType, 6 swords))
GR_FILL_ARC_3POINT_TO 					(data: 10 bytes	(ArcCloseType, 4 swords))
GR_FILL_BITMAP, 					(data: 6 bytes 	(2 swords, word))
GR_FILL_BITMAP_CP, 					(data: 2 bytes 	(word))
GR_FILL_BITMAP_OPTR,
GR_DRAW_BITMAP,					(data: 6 bytes 	(2 swords, word))
GR_DRAW_BITMAP_CP,					(data: 2 bytes 	(word))
GR_DRAW_BITMAP_OPTR,					(data: 6 bytes 	(2 swords, optr))
GR_DRAW_BITMAP_PTR,					(data: 6 bytes 	(2 swords, *))
GSE_BITMAP_SLICE,					(data: variable)
    (Drawing Attribute opcodes:)
GR_SAVE_STATE,
GR_RESTORE_STATE,
GR_SET_MIX_MODE,					(data: 1 byte 	 (MixMode))
GR_MOVE_TO,					(data: 4 bytes 	(2 swords))
GR_REL_MOVE_TO,					(data: 8 bytes 	(2 WWFixed))
GR_CREATE_PALETTE,
GR_DESTROY_PALETTE,
GR_SET_PALETTE_ENTRY,					(data: 4 bytes 	(Color, 3 bytes))
GR_SET_PALETTE,					(data: variable (word (# of entries),
                            entries (3 bytes each)))
GR_SET_LINE_COLOR,					(data: 3 bytes 	(3 bytes))
GR_SET_LINE_MASK,					(data: 1 byte 	 (SysDrawMask))
GR_SET_LINE_COLOR_MAP,					(data: 1 byte 	 (ColorMapMode))
GR_SET_LINE_WIDTH,					(data: 4 bytes 	(WWFixed))
GR_SET_LINE_JOIN,					(data: 1 byte 	 (LineJoin))
GR_SET_LINE_END,					(data: 1 byte 	 (LineEnd))
GR_SET_LINE_ATTR,					(data: 9 bytes 	(CF_RGB, 3 bytes, SysDrawMask,
                        ColorMapMode, LineEnd, LineJoin, LineStyle)
GR_SET_MITER_LIMIT,					(data: 4 bytes 	(WWFixed))
GR_SET_LINE_STYLE,					(data: 2 bytes 	(LineStyle, index))
GR_SET_LINE_COLOR_INDEX,					(data: 1 byte 	 (Color))
GR_SET_CUSTOM_LINE_MASK,					(data: 8 bytes 	(8 bytes))
GR_SET_CUSTOM_LINE_STYLE,					(data: variable (word (index),
                            word (# of on-off dash pairs),
                            pairs (each pair is 2 bytes)))
GR_SET_AREA_COLOR,					(data: 3 bytes 	(3 bytes)
GR_SET_AREA_MASK,					(data: 1 byte 	 (SysDrawMask))
GR_SET_AREA_COLOR_MAP,					(data: 1 byte 	 (ColorMapMode))
GR_SET_AREA_ATTR,					(data: 6 bytes 	(CF_RGB, 3 bytes, SysDrawMask,
                            ColorMapMode))
GR_SET_AREA_COLOR_INDEX,					(data: 1 byte 	 (Color))
GR_SET_CUSTOM_AREA_MASK,					(data: 8 bytes 	(8 bytes))
GR_SET_AREA_PATTERN, 					(data: 2 bytes 	(GraphicPattern))
GR_SET_CUSTOM_AREA_PATTERN, 					(data: variable (GraphicPattern,
                            word (size of data)
                            pattern data))
GR_SET_TEXT_COLOR,					(data: 3 bytes 	(3 bytes))
GR_SET_TEXT_MASK,					(data: 1 byte 	 (SysDrawMask))
GR_SET_TEXT_COLOR_MAP,					(data: 1 byte 	 (ColorMapMode))
GR_SET_TEXT_STYLE,					(data: 2 bytes 	(2 TextStyles))
GR_SET_TEXT_MODE,					(data: 2 bytes 	(2 TextModes))
GR_SET_TEXT_SPACE_PAD,					(data: 3 bytes 	(WBFixed))
GR_SET_TEXT_ATTR,					(data: 20 bytes	(CF_RGB, 3 bytes, SysDrawMask,
                            ColorMapMode, 2 TextStyles,
                            2 TextModes, WBFixed, FontID, word))
GR_SET_FONT,					(data: 5 bytes 	(WBFixed, FontID))
GR_SET_TEXT_COLOR_INDEX,					(data: 1 byte 	 (Color))
GR_SET_CUSTOM_TEXT_MASK,					(data: 8 bytes 	()
GR_SET_TRACK_KERN,					(data: 2 bytes 	(sword))
GR_SET_FONT_WEIGHT, 					(data: 2 bytes 	(FontWeight))
GR_SET_FONT_WIDTH, 					(data: 2 bytes 	(FontWidth))
GR_SET_SUPERSCRIPT_ATTR, 					(data: 2 bytes 	(position, scale))
GR_SET_SUBSCRIPT_ATTR, 					(data: 2 bytes 	(position, scale))
GR_SET_TEXT_PATTERN, 					(data: 2 bytes 	(GraphicPattern))
GR_SET_CUSTOM_TEXT_PATTERN, 					(data: variable (GraphicPattern,
                            word (size of data),
                            pattern data))
    (Path opcodes:)
GR_BEGIN_PATH,					(data: 1 byte 	 (PathCombineParam))
GR_END_PATH,
GR_SET_CLIP_RECT,					(data: 8 bytes 	(4 swords))
GR_SET_WIN_CLIP_RECT,					(data: 8 bytes 	(4 swords))
GR_CLOSE_SUB_PATH,
GR_SET_CLIP_PATH,					(data: 1 byte 	 (flags))
GR_SET_WIN_CLIP_PATH,					(data: 1 byte 	 (flags))
GR_SET_STROKE_PATH												*/

Each Graphics String Element consists of a GStringElement value specifying the type of element and the data associated with the element's operation. For instance, GR_DRAW_CHAR operations need six bytes of data: two bytes holding the Chars value of the character to draw, and two signed words specifying the drawing coordinates.

A good place to find out about the data associated with a particular GStringElement type is the GS...() macros defined in gstring.h .

See Also: GOC_POINT, GOC_SW.

GStringErrorType

typedef enum /* word */ {
GSET_NO_ERROR,
GSET_DISK_FULL
} GStringErrorType;

GStringKillType

See GrDestroyGString() .

GStringSetPosType

See GrSetGStringPos() .

GStringType

typedef ByteEnum GStringType;
#define GST_CHUNK				0
#define GST_STREAM 				1
#define GST_VMEM 				2
#define GST_PTR 				3
#define GST_PATH 				4

Handle

typedef word Handle;

HatchDash

typedef struct {
WWFixed		HD_on;
WWFixed		HD_off;
} HatchDash;

HatchLine

typedef struct {
PointWWFixed		HL_origin;
WWFixed		HL_deltaX;
WWFixed		HL_deltaY;
WWFixed		HL_angle;
ColorQuad		HL_color;
word		HL_numDashes;
    /* array of HatchDash structures follows here */
} HatchLine;

HatchPattern

typedef struct {
word HP_numLines;
/* array of HatchLine structures follows here */
} HatchPattern;

HeapAllocFlags

typedef ByteFlags HeapAllocFlags;
#define HAF_ZERO_INIT					0x80
#define HAF_LOCK 					0x40
#define HAF_NO_ERR 					0x20
#define HAF_UI 					0x10
#define HAF_READ_ONLY 					0x08
#define HAF_OBJECT_RESOURCE 					0x04
#define HAF_CODE 					0x02
#define HAF_CONFORMING 					0x01
#define HAF_STANDARD					(0)
#define HAF_STANDARD_NO_ERR					(HAF_NO_ERR)
#define HAF_STANDARD_LOCK					(HAF_LOCK)
#define HAF_STANDARD_NO_ERR_LOCK (HAF_NO_ERR | HAF_LOCK)

HeapAllocFlags indicate how the block should be allocated and initialized. They are not stored and can not be retrieved. Only some of the flags can be passed with MemReAlloc() ; these are noted below. The following flags are available:

HAF_ZERO_INIT
The memory manager should initialize the block to null bytes. This flag may be passed to MemReAlloc() to cause new memory to be zero-initialized.
HAF_LOCK
The memory manager should lock the block after allocating it. To get the block's address, call MemDeref() . This flag may be passed to MemReAlloc() .
HAF_NO_ERR
The memory manager should not return errors. If it cannot allocate block, GEOS will tell the user that there is no memory available and crash. Use of this flag is strongly discouraged. This flag may be passed to MemReAlloc() .
HAF_UI
If both HAF_OBJECT_RESOURCE and HAF_UI are set, this block will be run by the application's UI thread.
HAF_READ_ONLY
The block's data will not be modified. Useful for the debugger.
HAF_OBJECT_RESOURCE
This block will be an object block.
HAF_CODE
This block contains executable code.
HAF_CONFORMING
If the block contains code, the code may be run by a less privileged entity. If the block contains data, the data may be accessed or altered by a less privileged entity.

HeapCongestion

typedef enum /* word */ {
HC_SCRUBBING,
HC_CONGESTED,
HC_DESPERATE
} HeapCongestion;

HeapFlags

typedef ByteFlags HeapFlags;
#define HF_FIXED				0x80
#define HF_SHARABLE 				0x40
#define HF_DISCARDABLE 				0x20
#define HF_SWAPABLE 				0x10
#define HF_LMEM 				0x08
#define HF_DISCARDED 				0x02
#define HF_SWAPPED 				0x01
#define HF_STATIC 				(HF_DISCARDABLE | HF_SWAPABLE)
#define HF_DYNAMIC 				HF_SWAPABLE

The following flags are available:

HF_FIXED
The block will not move from its place in the global heap until it is freed. If this flag is off, the memory manager may move the block while it is unlocked. If the flag is on, the block may not be locked, and HF_DISCARDABLE and HF_SWAPABLE must be off.
HF_SHARABLE
The block may be locked by threads belonging to geodes other than the block's owner.
HF_DISCARDABLE
The block may be discarded when unlocked.
HF_SWAPABLE
The block may be swapped to extended/expanded memory or to the disk swap space when it is unlocked.
HF_LMEM
The block contains a local memory heap. This flag is set automatically by LMemInitHeap() and VMAllocLMem() ; applications should not need to set this flag.
HF_DISCARDED
The memory manager turns this bit on when it discards a block. The bit is turned off when the block is reallocated.
HF_SWAPPED
The memory manager turns this bit on when it swaps a block to extended/expanded memory or to the disk swap space. It turns the bit off when it swaps the block back into the global heap.

HugeArrayDirectory

typedef struct {
LMemBlockHeader			HAD_header;
VMBlockHandle			HAD_data;
ChunkHandle			HAD_dir;
VMBlockHandle			HAD_xdir;
VMBlockHandle			HAD_self;
word			HAD_size;
} HugeArrayDirectory;

IACPConnectFlags

typedef WordFlags IACPConnectFlags;
#define IACPCF_OBEY_LAUNCH_MODEL 							0x0020
#define IACPCF_CLIENT_OD_SPECIFIED 							0x0010
#define IACPCF_FIRST_ONLY 							0x0008
#define IACPCF_SERVER_MODE 							0x0007

Include: iacp.goh

IACPServerFlags

typedef ByteFlags IACPServerFlags;
#define IACPSF_MULTIPLE_INSTANCES 						0x80

Include: iacp.goh

IACPServerMode

IACPServerMode;
#define IACPSM_NOT_USER_INTERACTIBLE 							0
#define IACPSM_IN_FLUX 							1
#define IACPSM_USER_INTERACTIBLE 							2

Include: iacp.goh

IACPSide

typedef enum {
IACPS_CLIENT,
IACPS_SERVER
} IACPSide;

Include: iacp.goh

ImageFlags

typedef ByteFlags ImageFlags;
#define IF_IGNORE_MASK 				0x10
#define IF_BORDER				0x08
#define IF_BITSIZE				0x07 /* Should hold an ImageBitSize */

#define IBS_1 			0
#define IBS_2 			1
#define IBS_4 			2
#define IBS_8 			3
#define IBS_16 			4

IMCFeatures

typedef ByteFlags IMCFeatures;
#define IMCF_MAP				0x01
#define IMC_DEFAULT_FEATURES							IMCF_MAP
#define IMC_DEFAULT_TOOLBOX_FEATURES							0
#define IMC_MAP_MONIKER_SIZE							1024

ImpexDataClasses

typedef WordFlags ImpexDataClasses;
#define IDC_TEXT				0x8000
#define IDC_GRAPHICS 				0x4000
#define IDC_SPREADSHEET 				0x2000
#define IDC_FONT 				0x1000

ImpexFileSelectionData

typedef struct {
FileLongName				IFSD_selection;
PathName				IFSD_path;
word				IFSD_disk;
GenFileSelectorEntryFlags				IFSD_type;
} ImpexFileSelectionData;

ImpexMapFlags

typedef ByteFlags ImpexMapFlags;
#define IMF_IMPORT				0x80
#define IMF_EXPORT				0x40

ImpexMapFileInfoHeader

typedef struct {
LMemBlockHeader				IMFIH_base;
word				IMFIH_fieldChunk;
word				IMFIH_numFields;
} ImpexMapFileInfoHeader;

ImpexTranslationParams

typedef struct {
optr		ITP_impexOD;
Message		ITP_returnMsg;
word 		ITP_dataClass;
FileHandle 		ITP_transferVMFile;
VMChain 		ITP_transferVMChain;
dword 		ITP_internal;
} ImpexTranslationParams;

ImportControlAttrs

typedef WordFlags ImportControlAttrs;
#define ICA_IGNORE_INPUT 0x8000						/* ignore input while import occurs */

ImportControlToolboxFeatures

typedef ByteFlags ImportControlToolboxFeatures;
#define IMPORTCTF_DIALOG_BOX					0x01

IndexArrayBlockHeader

typedef struct {
word	IALMBH_recCount;
word	IALMBH_array;
word	IALMBH_indexField;
word	IALMBH_sortOrder;
} IndexArrayBlockHeader;

Format of a block containing a datastore index of RecordNum s (low word only); see DataStoreBuildIndex() .

InitFileCharConvert

typedef ByteEnum InitFileCharConvert;
#define IFCC_INTACT				0		/* Leave all characters unchanged. */
#define IFCC_UPCASE				1		/* Make all characters upper case. */
#define IFCC_DOWNCASE				2		/* Make all characters lower case. */

This enumerated type describes how InitFileRead...() routines should handle incoming strings.

InitFileReadFlags

typedef WordFlags InitFileReadFlags;
#define IFRF_CHAR_CONVERT				0xc000			/* 2 bits: InitFileCharConvert type */
#define IFRF_READ_ALL	 			0x2000
#define IFRF_FIRST_ONLY	 			0x1000
#define IFRF_SIZE				0x0fff

This record is used with the InitFileRead...() routines. The IFRF_CHAR_CONVERT field is used to indicate whether strings being read should be upcased, downcased, or left unaltered--the type is designated by a value of InitFileCharConvert . The IFRF_SIZE field is used by routines that take a passed buffer; this field indicates the size of the buffer (the maximum number of bytes that can be returned by the routine).

When setting this record, make sure you shift the IFRF_CHAR_CONVERT value left an offset of IFRF_CHAR_CONVERT_OFFSET.

InkBackgroundType

typedef enum {
IBT_NO_BACKGROUND = 0,
IBT_NARROW_LINED_PAPER = 2,
IBT_MEDIUM_LINED_PAPER = 4,
IBT_WIDE_LINED_PAPER = 6,
IBT_NARROW_STENO_PAPER = 8,
IBT_MEDIUM_STENO_PAPER = 10,
IBT_WIDE_STENO_PAPER = 12,
IBT_SMALL_GRID = 14,
IBT_MEDIUM_GRID = 16,
IBT_LARGE_GRID = 18,
IBT_SMALL_CROSS_SECTION = 20,
IBT_MEDIUM_CROSS_SECTION = 22,
IBT_LARGE_CROSS_SECTION = 24,
IBT_TO_DO_LIST = 26,
IBT_PHONE_MESSAGE = 28,
IBT_CUSTOM_BACKGROUND = 30
} InkBackgrountType;

This enumerated type is a set of standard background pictures for use with the Ink Database routines.

InkControlFeatures

typedef ByteFlags InkControlFeatures;
#define ICF_PENCIL_TOOL				0x02
#define ICF_ERASER_TOOL				0x01

InkControlToolboxFeatures

typedef ByteFlags InkControlToolboxFeatures;
#define ICTF_PENCIL_TOOL				0x02
#define ICTF_ERASER_TOOL				0x01

InkDBDisplayInfo

See InkDBGetDisplayInfo() .

InkDBFrame

typedef struct {
Rectangle IDBF_bounds;							/* bounds of data to save or coord at
                             * which to load data */
VMFileHandle IDBF_VMFile;							/* VM File to write to/read from */
DBGroupAndItem IDBF_DBGroupAndItem;							/* DB item to save to/load from */
word IDBF_DBExtra;							/* space to skip at start of block */
} InkDBFrame;

InkFlags

typedef ByteFlags InkFlags;
#define IF_HAS_TARGET						0x20
#define IF_DIRTY 						0x10
#define IF_ONLY_CHILD_OF_CONTENT 						0x08
#define IF_CONTROLLED 						0x04
#define IF_INVALIDATE_ERASURES 						0x02
#define IF_HAS_UNDO 						0x01

InkReturnValue

typedef enum {
IRV_NO_REPLY,
/* VisComp objects use VisCallChildUnderPoint to send
 * MSG_META_QUERY_IF_PRESS_IS_INK to its children, and
 * VisCallChildUnderPoint returns this value (0) if there was not child
 * under the point. No object should actually return this value. */
IRV_NO_INK,
/* Return this if the object wants to treat incoming event as mouse data. */
IRV_INK_WITH_STANDARD_OVERRIDE,
/* Return this if the object normally wants ink (the text object does this),
 * but the user can force mouse events instead by pressing the pen and
 * holding for some user-adjustable amount of time. */
IRV_WAIT
/* Return this value if the object under the point is run by a different
 * thread and you want to hold up input (don't do anything with the incoming
 * MSG_META_START_SELECT) `til obj sends MSG_GEN_APPLICATION_INK_QUERY_REPLY
 * to the applicaiton object. */
} InkReturnValue;

This enumerated type is used by objects to let the system know whether incoming pointer events should be interpreted as mouse or pen data.

InsertChildFlags

typedef WordFlags InsertChildFlags
#define ICF_MARK_DIRTY				0x8000
#define ICF_OPTIONS				0x0003

This record specifies how children are to be added to an object tree.

InsertChildOption

typedef ByteEnum InsertChildOption
#define ICO_FIRST					0
#define ICO_LAST					1
#define ICO_BEFORE_REFERENCE					2
#define ICO_AFTER_REFERENCE					3

This enumerated type determines how a child is added and is used with the InsertChildFlags record. It has four enumerations, as shown above.

InstrumentPatch

typedef enum {
#define IP_ACOUSTIC_GRAND_PIANO 				0
#define IP_BRIGHT_ACOUSTIC_PIANO 				1
#define IP_ELECTRIC_GRAND_PIANO 				2
#define IP_HONKY_TONK_PIANO 				3
#define IP_ELECTRIC_PIANO_1 				4
#define IP_ELECTRIC_PIANO_2 				5
#define IP_HARPSICORD 				6
#define IP_CLAVICORD 				7
#define IP_CELESTA	 			8

#define IP_GLOCKENSPIEL 				9
#define IP_MUSIC_BOX 				10
#define IP_VIBRAPHONE 				11
#define IP_MARIMBA 				12
#define IP_XYLOPHONE 				13
#define IP_TUBULAR_BELLS 				14
#define IP_DULCIMER 				15

#define IP_DRAWBAR_ORGAN 				16
#define IP_PERCUSSIVE_ORGAN 				17
#define IP_ROCK_ORGAN 				18
#define IP_CHURCH_ORGAN 				19
#define IP_REED_ORGAN 				20
#define IP_ACCORDION 				21
#define IP_HARMONICA 				22
#define IP_TANGO_ACCORDION 				23

#define IP_ACOUSTIC_NYLON_GUITAR 				24
#define IP_ACOUSTIC_STEEL_GUITAR 				25
#define IP_ELECTRIC_JAZZ_GUITAR 				26
#define IP_ELECTRIC_CLEAN_GUITAR 				27
#define IP_ELECTRIC_MUTED_GUITAR 				28
#define IP_OVERDRIVEN_GUITAR 				29
#define IP_DISTORTION_GUITAR 				30
#define IP_GUITAR_HARMONICS 				31

#define IP_ACOUSTIC_BASS 				32
#define IP_ELECTRIC_FINGERED_BASS 33
#define IP_ELECTRIC_PICKED_BASS 34
#define IP_FRETLESS_BASS 				35
#define IP_SLAP_BASS_1 				36
#define IP_SLAP_BASS_2 				37
#define IP_SYNTH_BASS_1 				38
#define IP_SYNTH_BASS_2 				39

#define IP_VIOLIN 				40
#define IP_VIOLA 				41
#define IP_CELLO 				42
#define IP_CONTRABASS 				43
#define IP_TREMOLO_STRINGS 				44
#define IP_PIZZICATO_STRINGS 				45
#define IP_ORCHESTRAL_HARP 				46
#define IP_TIMPANI 				47

#define IP_STRING_ENSEMBLE_1 				48
#define IP_STRING_ENSEMBLE_2 				49
#define IP_SYNTH_STRINGS_1 				50
#define IP_SYNTH_STRINGS_2 				51
#define IP_CHIOR_AAHS 				52
#define IP_VOICE_OOHS 				53
#define IP_SYNTH_VOICE 				54
#define IP_ORCHESTRA_HIT 				55

#define IP_TRUMPET 				56
#define IP_TROMBONE 				57
#define IP_TUBA 				58
#define IP_MUTED_TRUMPET 				59
#define IP_FRENCH_HORN 				60
#define IP_BRASS_SECTION 				61
#define IP_SYNTH_BRASS_1 				62
#define IP_SYNTH_BRASS_2 				63

#define IP_SOPRANO_SAX 				64
#define IP_ALTO_SAX 				65
#define IP_TENOR_SAX 				66
#define IP_BARITONE_SAX 				67
#define IP_OBOE 				68
#define IP_ENGLISH_HORN 				69
#define IP_BASSOON 				70
#define IP_CLARINET 				71

#define IP_PICCOLO 				72
#define IP_FLUTE 				73
#define IP_RECORDER 				74
#define IP_PAN_FLUTE 				75
#define IP_BLOWN_BOTTLE 				76
#define IP_SHAKUHACHI 				77
#define IP_WHISTLE 				78
#define IP_OCARINA 				79

#define IP_LEAD_SQUARE 				80
#define IP_LEAD_SAWTOOTH 				81
#define IP_LEAD_CALLIOPE 				82
#define IP_LEAD_CHIFF 				83
#define IP_LEAD_CHARANG 				84
#define IP_LEAD_VOICE 				85
#define IP_LEAD_FIFTHS 				86
#define IP_LEAD_BASS_LEAD 				87

#define IP_PAD_NEW_AGE 				88
#define IP_PAD_WARM 				89
#define IP_PAD_POLYSYNTH 				90
#define IP_PAD_CHOIR 				91
#define IP_PAD_BOWED 				92
#define IP_PAD_METALLIC 				93
#define IP_PAD_HALO 				94
#define IP_PAD_SWEEP 				95

#define IP_FX_RAIN 				96
#define IP_FX_SOUNDTRACK 				97
#define IP_FX_CRYSTAL 				98
#define IP_FX_ATMOSPHERE 				99
#define IP_FX_BRIGHTNESS 				100
#define IP_FX_GOBLINS 				101
#define IP_FX_ECHOES 				102
#define IP_FX_SCI_FI 				103

#define IP_SITAR 				104
#define IP_BANJO 				105
#define IP_SHAMISEN 				106
#define IP_KOTO 				107
#define IP_KALIMBA 				108
#define IP_BAG_PIPE 				109
#define IP_FIDDLE 				110
#define IP_SHANAI 				111

#define IP_TINKLE_BELL 				112
#define IP_AGOGO 				113
#define IP_STEEL_DRUMS 				114
#define IP_WOODBLOCK 				115
#define IP_TAIKO_DRUM 				116
#define IP_MELODIC_TOM 				117
#define IP_SYNTH_DRUM 				118
#define IP_REVERSE_CYMBAL 				119

#define IP_GUITAR_FRET_NOISE 				120
#define IP_BREATH_NOISE 				121
#define IP_SEASHORE 				122
#define IP_BIRD_TWEET 				123
#define IP_TELEPHONE_RING 				124
#define IP_HELICOPTER 				125
#define IP_APPLAUSE 				126
#define IP_GUNSHOT 				127

#define IP_ACOUSTIC_BASS_DRUM 				128
#define IP_BASS_DRUM_1 				129
#define IP_SIDE_STICK 				130
#define IP_ACOUSTIC_SNARE 				131
#define IP_HAND_CLAP 				132
#define IP_ELECTRIC_SNARE 				133
#define IP_LOW_FLOOR_TOM 				134
#define IP_CLOSED_HI_HAT 				135

#define IP_HIGH_FLOOR_TOM 				136
#define IP_PEDAL_HI_HAT 				137
#define IP_LOW_TOM 				138
#define IP_OPEN_HI_HAT 				139
#define IP_LOW_MID_TOM 				140
#define IP_HI_MID_TOM 				141
#define IP_CRASH_CYMBAL_1 				142
#define IP_HIGH_TOM 				143

#define IP_RIDE_CYMBAL_1 				144
#define IP_CHINESE_CYMBAL 				145
#define IP_RIDE_BELL 				146
#define IP_TAMBOURINE 				147
#define IP_SPLASH_CYMBAL 				148
#define IP_COWBELL 				149
#define IP_CRASH_CYMBAL_2 				150
#define IP_VIBRASLAP 				151

#define IP_RIDE_CYMBAL_2 				152
#define IP_HI_BONGO 				153
#define IP_LOW_BONGO 				154
#define IP_MUTE_HI_CONGA 				155
#define IP_OPEN_HI_CONGA 				156
#define IP_LOW_CONGA 				157
#define IP_HI_TIMBALE 				158
#define IP_LOW_TIMBALE 				159

#define IP_HIGH_AGOGO 				160
#define IP_LOW_AGOGO 				161
#define IP_CABASA 				162
#define IP_MARACAS 				163
#define IP_SHORT_WHISTLE 				164
#define IP_LONG_WHISTLE 				165
#define IP_SHORT_GUIRO 				166
#define IP_LONG_GUIRO 				167

#define IP_CLAVES 				168
#define IP_HI_WOOD_BLOCK 				169
#define IP_LOW_WOOD_BLOCK 				170
#define IP_MUTE_CUICA 				171
#define IP_OPEN_CUICA 				172
#define IP_MUTE_TRIANGLE 				173
#define IP_OPEN_TRIANGLE 				174
} InstrumentPatch;

These are standard simulated instruments.

InstrumentTable

typedef enum {
         IT_STANDARD_TABLE=0 			/* default table */
} InstrumentTable;

The sound library uses this enumerated type to keep track of which table of simulated musical instruments to use.

JobStatus

typedef struct {
char			JS_fname[13];			/* std DOS (8.3) spool filename */
char			JS_parent[FILE_LONGNAME_LENGTH+1];
                        /* parent app's name */
char			JS_documentName[FILE_LONGNAME_LENGTH+1];
                        /* document name */
word			JS_numPages;			/* # pages in document */
SpoolTimeStruct			JS_time;			/* time spooled */
byte			JS_printing;			/* TRUE/FALSE if we are printing */
} JobStatus;

Justification

typedef ByteEnum Justification;
#define J_LEFT			0
#define J_RIGHT			1
#define J_CENTER			2
#define J_FULL			3

KeyboardShortcut

typedef ByteFlags KeyboardShortcut;
        #define KS_PHYSICAL     0x80    /* TRUE: match key, not character   */
        #define KS_ALT          0x40    /* TRUE: <ALT> must be pressed      */
        #define KS_CTRL         0x20    /* TRUE: <CTRL> must be pressed     */
        #define KS_SHIFT        0x10    /* TRUE: <SHIFT> must be pressed    */

        #define KS_CHAR_SET_PRINTABLE   (CS_BSW     & 0x0f)
        #define KS_CHAR_SET_CONTROL     (CS_CONTROL & 0x0f)

KeyboardType

typedef ByteEnum KeyboardType;
#define KT_NOT_EXTD				1
#define KT_EXTD				2
#define KT_BOTH				3

KeyMapType

typedef enum /* word */ {
KEYMAP_US_EXTD=1,
KEYMAP_US,
KEYMAP_UK_EXTD,
KEYMAP_UK,
KEYMAP_GERMANY_EXTD,
KEYMAP_GERMANY,
KEYMAP_SPAIN_EXTD,
KEYMAP_SPAIN,
KEYMAP_DENMARK_EXTD,
KEYMAP_DENMARK,
KEYMAP_BELGIUM_EXTD,
KEYMAP_BELGIUM,
KEYMAP_CANADA_EXTD,
KEYMAP_CANADA,
KEYMAP_ITALY_EXTD,
KEYMAP_ITALY,
KEYMAP_LATIN_AMERICA_EXTD,
KEYMAP_LATIN_AMERICA,
KEYMAP_NETHERLANDS,
KEYMAP_NETHERLANDS_EXTD,
KEYMAP_NORWAY_EXTD,
KEYMAP_NORWAY,
KEYMAP_PORTUGAL_EXTD,
KEYMAP_PORTUGAL,
KEYMAP_SWEDEN_EXTD,
KEYMAP_SWEDEN,
KEYMAP_SWISS_FRENCH_EXTD,
KEYMAP_SWISS_FRENCH,
KEYMAP_SWISS_GERMAN_EXTD,
KEYMAP_SWISS_GERMAN,
KEYMAP_FRANCE_EXTD,
KEYMAP_FRANCE,
} KeyMapType;

Language

typedef ByteEnum Language;
#define L_DEFAULT				0
#define L_GRAPHIC 				0
#define L_ENGLISH 				1
#define L_GERMAN 				2
#define L_FRENCH 				3
#define L_SPANISH 				4
#define L_ITALIAN 				5
#define L_DANISH 				6
#define L_DUTCH 				7

LargeMouseData

typedef struct {
PointDWFixed				LMD_location;
byte				LMD_buttonInfo;
UIFunctionsActive				LMD_uiFunctionsActive;
} LargeMouseData;

LayerPriority

typedef ByteEnum LayerPriority;
#define LAYER_PRIO_MODAL					 6
#define LAYER_PRIO_ON_TOP					 8
#define LAYER_PRIO_STD					12
#define LAYER_PRIO_ON_BOTTOM					14

LibraryCallType

typedef enum /* word */ {
LCT_ATTACH,				/* The library was just loaded. */
LCT_DETACH,				/* The library is about to be unloaded. */
LCT_NEW_CLIENT,				/* A new client of the library was just loaded. */
LCT_NEW_CLIENT_THREAD,				/* A new thread was just created for a
                 * current client of the library. */
LCT_CLIENT_THREAD_EXIT,				/* A thread was just exited for a current
                 * client of the library. */
LCT_CLIENT_EXIT,				/* Library's client is about to be unloaded. */
} LibraryCallType

This type is used by library entry point routines. Library entry point routines take a value of this enumerated type to determine what, if anything, is to be done.

LineAttr

typedef struct {
byte 		LA_colorFlag;
RGBValue 		LA_color;
SysDrawMask 		LA_mask;
ColorMapMode 		LA_mapMode;
LineEnd 		LA_end;
LineJoin	 	LA_join;
LineStyle 		LA_style;
WWFixed 		LA_width;
} LineAttr;

LineEnd

typedef ByteEnum LineEnd;
#define LE_BUTTCAP				0
#define LE_ROUNDCAP				1
#define LE_SQUARECAP 				2
#define LAST_LINE_END_TYPE				LE_SQUARECAP

Line ends determine how the graphics system will draw the end of a line segment.

LineJoin

typedef ByteEnum LineJoin;
#define LJ_MITERED					0
#define LJ_ROUND					1
#define LJ_BEVELED					2
#define LAST_LINE_JOIN_TYPE					LJ_BEVELED

This enumerated type determines how the graphics system will draw corners of rectangles and polylines.

LineStyle

typedef ByteEnum LineStyle;
#define LS_SOLID					0
#define LS_DASHED 					1
#define LS_DOTTED 					2
#define LS_DASHDOT 					3
#define LS_DASHDDOT 					4
#define LS_CUSTOM 					5
#define MAX_DASH_ARRAY_PAIRS 					5

The LineStyle type describes a line's "dottedness." Lines using custom dashes will work with the DashPairArray structure:

typedef word    DashPairArray[MAX_DASH_ARRAY_PAIRS*2];

LMemBlockHeader

typedef struct {
MemHandle		LMBH_handle;
word		LMBH_offset;
word		LMBH_flags;
LMemTypes		LMBH_lmemType;
word		LMBH_blockSize;
word		LMBH_nHandles;
word		LMBH_freeList;
word		LMBH_totalFree;
} LMemBlockHeader;

This structure is found at the beginning of every block which contains an LMem heap. You can examine any of the fields by locking the block and casting its address to a *LMemBlockHeader . You should not, however, change any of the fields yourself; they are managed by the LMem routines.

Contents: The header has the following fields:

LMBH _handle
The handle of this block.
LMBH _offset
The offset from the beginning of the block to the beginning of the heap.
LMBH _flags
The LocalMemoryFlags currently set for the block. The flags are described in the entry for LMemInitHeap() .
LMBH _lmemType
The type of LMem heap in this block. This field is a member of the LMemType enumerated type, described in the entry for LMemInitHeap() .
LMBH _blockSize
The total size of this block. This size may change in either direction as a result of chunk allocation and heap compaction.
LMBH _nHandles
The number of handles available in the chunk handle table. Not all of these chunks are necessarily allocated as owned or free chunks. The table grows automatically when necessary.
LMBH _freeList
The chunk handle of the first free chunk in the linked list of free chunks.
LMBH _totalFree
The total amount of free space in the LMem heap.

Warnings: Do not change the settings of the LMemBlockHeader . They are automatically maintained by the LMem routines.

Include: lmem.h

See Also: LMemInitHeap().

LMemType

typdef enum {
LMEM_TYPE_GENERAL,
LMEM_TYPE_WINDOW,
LMEM_TYPE_OBJ_BLOCK,
LMEM_TYPE_GSTATE,
LMEM_TYPE_FONT_BLK,
LMEM_TYPE_GSTRING,
LMEM_TYPE_DB_ITEMS
} LMemType;

LMem heaps are created for many different purposes. Some of these purposes require the heap to have special functionality. For this reason, when you create an LMem heap, you must specify what it will be used for. The following types are available:

LMEM_TYPE_GENERAL
The LMem heap will be used for general data storage, possibly including a chunk, name, or element array. When an application creates an LMem heap, it will almost always be of type "General" or "Object."
LMEM_TYPE_OBJ_BLOCK
Objects are stored in object blocks, which are LMem heaps. An object block has some extra header information and contains one chunk which contains only flags. All the objects in the block are stored as chunks on the heap. Applications can directly create object blocks.
LMEM_TYPE_WINDOW
Windows are stored in memory as LMem heaps. The header contains information about the window; each region in the window is stored as a chunk. Applications will not directly create Window heaps.
LMEM_TYPE_GSTATE
A GState is an LMem heap. The GState information is in the header, and the application clip-rectangle is stored in a chunk. Applications do not directly create GState blocks; rather, they call a GState creation routine, which creates the block.
LMEM_TYPE_FONT_BLOCK
Font blocks are stored as LMem heaps. Applications do not create font blocks directly.
LMEM_TYPE_GSTRING
Whenever a GString is created or loaded, a GString LMem heap is created, and elements are added as chunks. The heap is created automatically by the GString routines; applications should not create GString blocks.
LMEM_TYPE_DB_ITEMS
The Virtual Memory mechanism provides routines to create and manage database items, short pieces of data which are dynamically allocated and are saved with the VM file. Applications do not directly allocate DB blocks; rather, they call DB routines, which see to it that the blocks are created.

Include: lmem.h

LocalDistanceFlags

typedef WordFlags LocalDistanceFlags;
#define LDF_FULL_NAMES							0x8000
#define LDF_PRINT_PLURAL_IF_NEEDED							0x4000

LocalCmpStringsDosToGeosFlags

See LocalCmpStringsDosToGeos() .

LocalCurrencyFormat

typedef struct {
byte	currencyFormatFlags;
byte	currencyDigits;
word	thousandsSeparator;
word	decimalSeparator;
word	listSeparator;
} LocalCurrencyFormat;

LocalMemoryFlags

typedef WordFlags LocalMemoryFlags;
#define LMF_HAS_FLAGS				0x8000
#define LMF_IN_RESOURCE 				0x4000
#define LMF_DETACHABLE 				0x2000
#define LMF_DUPLICATED 				0x1000
#define LMF_RELOCATED 				0x0800
#define LMF_AUTO_FREE 				0x0400
#define LMF_IN_LMEM_ALLOC 				0x0200
#define LMF_IS_VM				0x0100
#define LMF_NO_HANDLES 				0x0080
#define LMF_NO_ENLARGE 				0x0040
#define LMF_RETURN_ERRORS 				0x0020
#define LMF_DEATH_COUNT 				0x0007
#define STD_LMEM_OBJECT_FLAGS						(LMF_HAS_FLAGS | LMF_RELOCATED)

When an LMem heap is allocated, certain flags are passed to indicate properties the heap should have. Some of the flags are passed only for system-created heaps. The flags are stored in a word-length record ( LocalMemoryFlags ); the record also contains flags indicating the current state of the heap. The LocalMemoryFlags are listed below:

LMF_HAS_FLAGS
Set if the block has a chunk containing only flags. This flag is set for object blocks; it is usually cleared for general LMem heaps.
LMF_IN_RESOURCE
Set if the block has just been loaded from a resource and has not been changed since being loaded. This flag is set only for object blocks created by the compiler.
LMF_DETACHABLE
Set if the block is an object block which can be saved to a state file.
LMF_DUPLICATED
Set if block is an object block created by the ObjDuplicateResource() routine. This flag should not be set by applications.
LMF_RELOCATED
Set if all the objects in the block have been relocated. The object system sets this when it has relocated all the objects in the block.
LMF_AUTO_FREE
This flag is used by several object routines. It indicates that if the block's in-use count drops to zero, the block may be freed. This flag should not be set by applications.
LMF_IN_MEM_ALLOC
This flag is used in error-checking code to prevent the heap from being validated while a chunk is being allocated. For internal use only--do not modify .
LMF_IS_VM
Set if the LMem heap is in a VM block and the block should be marked dirty whenever a chunk is marked dirty. This flag is automatically set by the VM code when an LMem heap is created in or attached to a VM file. This flag should not be set by applications.
LMF_NO_HANDLES
Set if block does not use chunk handles. A block can be set to simulate the C malloc() routine; in this case, chunks are not relocated after being created, so chunk handles are not needed. Ordinarily, these blocks are created by the malloc() routine, not by applications.
LMF_NO_ENLARGE
Indicates that the local-memory routines should not enlarge this block to fulfill chunk requests. This guarantees that the block will not be moved by a chunk allocation request; however, it makes these requests more likely to fail.
LMF_RETURN_ERRORS
Set if local memory routines should return errors when allocation requests cannot be fulfilled. If the flag is not set, allocation routines will fatal-error if they cannot comply with requests. This flag is generally clear for expandable LMem blocks, since many system routines (such as ObjInstantiate() ) are optimized in such a way that they cannot deal with LMem allocation errors.
LMF_DEATH_COUNT
This field occupies the least significant three bits of the flag field. It means nothing if the value is zero. If it is non-zero, it indicates the number of remove-block messages left which must hit BlockDeathCommon before it will free the block. This flag is used by error-checking code in the kernel.
STD_LMEM_OBJ_FLAGS
This is a constant which combines the LMF_HAS_FLAGS and LMF_RELOCATED flags. These flags should be set for all object blocks.

Include: lmem.h

LocalNumericFormat

typedef struct {
byte	numberFormatFlags;
byte	decimalDigits;
word	thousandsSeparator;
word	decimalSeparator;
word	listSeparator;
} LocalNumericFormat;

LocalQuotes

typedef struct {
word	frontSingle;
word	endSingle;
word	frontDouble;
word	endDouble;
} LocalQuotes;

ManufacturerFromFormatID

ManufacturerID ManufacturerFromFormatID(type);
        ClipboardItemFormatID			type;

This macro extracts the manufacturer ID from the given clipboard format ID and manufacturer value.

ManufacturerID

typedef word ManufacturerID
#define MANUFACTURER_ID_GEOWORKS									 0
#define MANUFACTURER_ID_APP_LOCAL									 1
#define MANUFACTURER_ID_DOS_LAUNCHER									 5
#define MANUFACTURER_ID_SDK									 8
#define MANUFACTURER_ID_GENERIC									10

While geode.h defines many other ManufacturerIDs for specific developers, the only one of these which is useful to know about when developing for the Nokia 9000i communicator is MANUFACTURER_ID_NOKIA.

Each company should have its own manufacturer ID which is unique to the company and ensures that its applications' GeodeTokens do not conflict with those of other manufacturers. If you don't have a manufacturer ID but think you should, contact Geoworks Developer Support.

MapColorToMono

typedef ByteEnum MapColorToMono;
#define CMT_CLOSEST				0
#define CMT_DITHER 				1

This type determines what the graphics system will do when trying to draw in an unavailable color. It will either draw in the closest color, or else mix two or more close colors to get as close as possible overall.

MapListBlockHeader

typedef struct {
LMemBlockHeader				MLBH_base;
word				MLBH_numDestFields;
word				MLBH_chunk1;
} MapListBlockHeader;

MarginDimensions

typedef struct {
int	leftMargin;
int	topMargin;
int	rightMargin;
int	bottomMargin;
} MarginDimensions;

MAX_FIELD_NAME_LENGTH

#define MAX_FIELD_NAME_LENGTH					40

Maximum size of a field name in a datastore. See DataStoreAddField() .

MeasurementType

typedef ByteEnum MeasurementType;
#define MEASURE_US				0
#define MEASURE_METRIC				1

MediaType

typedef enum /* byte */ {
#define MEDIA_NONEXISTENT				0
#define MEDIA_160K 				1
#define MEDIA_180K 				2
#define MEDIA_320K 				3
#define MEDIA_360K 				4
#define MEDIA_720K 				5
#define MEDIA_1M2 				6
#define MEDIA_1M44 				7
#define MEDIA_2M88 				8
#define MEDIA_FIXED_DISK 				9
#define MEDIA_CUSTOM 				10
} MediaType;

The MediaType enumerated type indicates how a disk is formatted. A member of this enumerated type is returned by some disk-information routines (e.g. DriveGetDefaultMedia() ). A MediaType value is also passed to DiskFormat() , indicating how the disk should be formatted.

MediumAndUnit

typedef struct {
            MediumType 		MU_medium;
            MediumUnitType 		MU_unitType;
            word 		MU_unit;
} MediumAndUnit;

This structure represents an instance of some communication medium-generally this corresponds to some hardware port or peripheral device.

Include: medium.h

MediumUnitType

typedef ByteEnum MediumUnitType;
        #define MUT_NONE      0x0
        #define MUT_INT       0x1
        #define MUT_MEM_BLOCK 0x2
        #define MUT_ANY       0x3

Include: medium.h

MediumType

typedef struct {
            word            MET_id;
            ManufacturerID  MET_manuf;
} MediumType;

This structure represents a communication medium. Several standard media may be represented with a MET_manuf of MANUFACTURER_ID_GEOWORKS and a GeoworksMediumID in MET_id .

Include: medium.h

MemGetInfoType

See MemGetInfo() .

MemHandle

typedef Handle MemHandle;

Message

typedef word Message;

MessageError

typedef enum /* word */ {
 MESSAGE_NO_ERROR,				/* no error was encountered */
 MESSAGE_NO_HANDLES				/* no handle could be allocated
                 * and MF_CAN_DISCARD_IF_DESPARATE
                 * was passed */
} MessageErrors;

A MessageError is returned by the assembly routine ObjMessage() in assembly to indicate whether the message was successfully sent. This is not encountered by C applications.

MessageFlags

typedef WordFlags MessageFlags;
#define MF_CALL							0x8000			/* @call */
#define MF_FORCE_QUEUE 							0x4000
#define MF_STACK							0x2000			/* @stack */
#define MF_CHECK_DUPLICATE 							0x0800
#define MF_CHECK_LAST_ONLY 							0x0400
#define MF_REPLACE 							0x0200
#define MF_CUSTOM							0x0100
#define MF_FIXUP_DS 							0x0080
#define MF_FIXUP_ES 							0x0040
#define MF_DISCARD_IF_NO_MATCH 							0x0020
#define MF_MATCH_ALL 							0x0010
#define MF_INSERT_AT_FRONT 							0x0008			/* puts at front of queue */
#define MF_CAN_DISCARD_IF_DESPERATE 							0x0004
#define MF_RECORD 							0x0002			/* @record */
#define MF_DISPATCH_DONT_FREE 							0x0002

MessageFlags are specified in the assembly routine ObjMessage() . Most of these flags are set properly by Goc and the kernel in C. See the reference entries for the Goc keywords @call and @send .

MessageHandle

typedef Handle MessageHandle;

MessageMethod

typedef void	 MessageMethod();

Goc uses this type when building up method tables for classes.

MinIncrementType

typedef union {
MinUSMeasure				MIT_US;
MinMetricMeasure				MIT_METRIC;
MinPointMeasure				MIT_POINT;
MinPicaMeasure				MIT_PICA;
} MinIncrementType;

MinMetricMeasure

typedef ByteEnum MinMetricMeasure;
#define MMM_MILLIMETER					0
#define MMM_HALF_CENTIMETER					1
#define MMM_CENTIMETER 					2

MinPicaMeasure

typedef ByteEnum MinPicaMeasure;
#define MPM_PICA					0
#define MPM_INCH					1

MinPointMeasure

typedef ByteEnum MinPointMeasure;
#define MPM_25_POINT					0
#define MPM_50_POINT					1
#define MPM_100_POINT					2

MinUSMeasure

typedef ByteEnum MinUSMeasure;
#define MUSM_EIGHTH_INCH					0
#define MUSM_QUARTER_INCH					1
#define MUSM_HALF_INCH 					2
#define MUSM_ONE_INCH 					3

MixMode

typedef ByteEnum MixMode;
#define MM_CLEAR			0	/* clear destination */
#define MM_COPY			1	/* new drawing is opaque */
#define MM_NOP 			2	/* no drawing */
#define MM_AND 			3	/* logical AND of new and old colors */
#define MM_INVERT 4				/* inverse of old color */
#define MM_XOR 			5	/* XOR of new and old colors */
#define MM_SET 			6	/* set destination black */
#define MM_OR 			7	/* logical OR of new and old colors */

The MixMode determines what the graphics system will do when drawing one thing on top of another.

ModemResultCode

typedef enum {
MRC_NOT_SUPPORTED = 1,
MRC_DRIVER_IN_USE,
MRC_TIMEOUT,
MRC_UNKNOWN_RESPONSE,
MRC_OK,
RC_ERROR,
MRC_BUSY,
MRC_NO_DIALTONE,
MRC_NO_ANSWER,
MRC_NO_CARRIER,
MRC_CONNECT,
MRC_CONNECT_1200,
MRC_CONNECT_2400,
MRC_CONNECT_4800,
MRC_CONNECT_9600,
MRC_BLACKLISTED,
MRC_DELAYED,
} ModemResultCode;

Error codes returned by modem routines.

MonoTransfer

typedef struct {
byte MT_gray[256];
} MonoTransfer;

MouseReturnFlags

typedef WordFlags MouseReturnFlags;
#define MRF_PROCESSED							0x8000
#define MRF_REPLAY 							0x4000
#define MRF_PREVENT_PASS_THROUGH 							0x2000
#define MRF_SET_POINTER_IMAGE 							0x1000
#define MRF_CLEAR_POINTER_IMAGE 							0x0800

These flags are used in various parts of the system that work with mouse input. Which values are appropriate to pass will vary based on context.

MouseReturnParams

typedef struct {
word 				unused;
MouseReturnFlags 				flags;
optr 				ptrImage;
} MouseReturnParams;

This structure is used in certain areas of the system which work with mouse input.

NameArrayAddFlags

typedef WordFlags NameArrayAddFlags;
#define NAAF_SET_DATA_ON_REPLACE 0x8000

NameArrayElement

typedef struct {
RefElementHeader NAE_meta;
} NameArrayElement;

NameArrayHeader

typedef struct{
ElementArrayHeader				NAH_meta;
word				NAH_dataSize;					/* Size of data section of
                                     * each element */
} NameArrayHeader;

Every name array must begin with a NameArrayHeader . Since name arrays are special kinds of element arrays, the NameArrayHeader must itself begin with an ElementArrayHeader . The structure contains one additional field, NAH_dataSize . This field specifies how long the data section of every element is. Applications may examine this field, but they must not change it.

NameArrayMaxElement

typedef struct {
RefElementHeader NAME_meta;
byte NAME_data[NAME_ARRAY_MAX_DATA_SIZE];
char NAME_name[NAME_ARRAY_MAX_NAME_SIZE];
} NameArrayMaxElement;

NO_ERROR_RETURNED

#define NO_ERROR_RETURNED				0

NoteType

typedef ByteEnum NoteType;
#define NT_INK			0
#define NT_TEXT			1

NotificationType

typedef struct {
ManufacturerID			NT_manuf;
word			NT_type;
} NotificationType;

NotifyInkHasTarget

typedef struct {
optr	NIHT_optr;
} NotifyInkHasTarget;

NULL

#undef NULL
#define NULL		0

NullChunk

#define NullChunk		((ChunkHandle) 0)

NullClass

#define NullClass		((ClassStruct *) 0)

NullHandle

#define NullHandle		((Handle) 0)

NullOptr

#define NullOptr		((optr) 0)

NumberFormatFlags

typedef ByteFlags NumberFormatFlags;
#define NFF_LEADING_ZERO				0x01

NumberType

typedef ByteEnum NumberType;
#define NT_VALUE				0
#define NT_BOOLEAN				1
#define NT_DATE_TIME				2

ObjChunkFlags

typedef ByteFlags ObjChunkFlags;
#define OCF_VARDATA_RELOC					0x10
#define OCF_DIRTY 					0x08
#define OCF_IGNORE_DIRTY 					0x04
#define OCF_IN_RESOURCE 					0x02
#define OCF_IS_OBJECT 					0x01

This record is stored at the beginning of each chunk and gives specific information about the chunk. The flags are internal.

ObjLMemBlockHeader

typedef struct {
LMemBlockHeader			OLMBH_header;					/* standard LMem block header */
word			OLMBH_inUseCount;
word			OLMBH_interactibleCount;
optr			OLMBH_output;
word			OLMBH_resourceSize;
} ObjLMemBlockHeader;

This is the standard Object Block header that begins every object block; you can set additional header fields with the @header Goc keyword. The fields of this structure are

OLMBH_header
The standard LMem block header. See the LMemBlockHeader structure type.
OLMBH_inUseCount
The "in use" count for the block. If not zero, then the block may not safely be freed.
OLMBH_interactibleCount
The "interactable" count for the block. If not zero, then one or more objects in the block are either visible to the user or about to be activated by the user (e.g. via keyboard shortcut). A block with a non-zero interactible count may not be swapped.
OLMBH_output
The optr of the object that will be notified about changes in resource status, such as in-use count changing to or from zero. Messages may also be sent to this output object via the TravelOption TO_OBJ_BLOCK_OUTPUT.
OLMBH_resourceSize
The size of the object block (resource).

ObjRelocation

typedef struct {
ObjRelocationType				OR_type;
word				OR_offset;
} ObjRelocation;

ObjRelocationSource

typedef ByteEnum ObjRelocationSource;
#define ORS_NULL								0
#define ORS_OWNING_GEODE 								1
#define ORS_KERNEL 								2
#define ORS_LIBRARY 								3
#define ORS_CURRENT_BLOCK 								4
#define ORS_VM_HANDLE 								5
#define ORS_OWNING_GEODE_ENTRY_POINT								6
#define ORS_NON_STATE_VM 								7
#define ORS_UNKNOWN_BLOCK 								8
#define ORS_EXTERNAL 								9
#define RID_SOURCE_OFFSET 								12

ObjRelocationType

typedef ByteEnum ObjRelocationType;
#define RELOC_END_OF_LIST						0
#define RELOC_RELOC_HANDLE						1
#define RELOC_RELOC_SEGMENT						2
#define RELOC_RELOC_ENTRY_POINT						3

OperatorStackElement

typedef struct {
EvalStackOperatorType OSE_type;
EvalStackOperatorType OSE_data;
} OperatorStackElement;

OperatorType

typedef ByteEnum OperatorType;
#define OP_RANGE_SEPARATOR									0
#define OP_NEGATION 									1
#define OP_PERCENT 									2
#define OP_EXPONENTIATION 									3
#define OP_MULTIPLICATION 									4
#define OP_DIVISION 									5
#define OP_MODULO 									6
#define OP_ADDITION 									7
#define OP_SUBTRACTION 									8
#define OP_EQUAL 									9
#define OP_NOT_EQUAL 									10
#define OP_LESS_THAN 									11
#define OP_GREATER_THAN 									12
#define OP_LESS_THAN_OR_EQUAL 									13
#define OP_GREATER_THAN_OR_EQUAL 									14
#define OP_STRING_CONCAT 									15
#define OP_RANGE_INTERSECTION 									16
#define OP_NOT_EQUAL_GRAPHIC									17
#define OP_DIVISION_GRAPHIC 									18
#define OP_LESS_THAN_OR_EQUAL_GRAPHIC 									19
#define OP_GREATER_THAN_OR_EQUAL_GRAPHIC 									20
#define OP_PERCENT_MODULO 									21
#define OP_SUBTRACTION_NEGATION 									22

optr

typedef dword optr;

PageEndCommand

typedef ByteEnum PageEndCommand;
        #define PEC_FORM_FEED 0
        #define PEC_NO_FORM_FEED 1

This enumerated type is used to specify whether there should be a form feed at the end of a page. A form feed is almost always desired; however, a program such as a banner-printer might suppress the form feed.

PageLayout

typedef union {
PageLayoutPaper				PL_paper;
PageLayoutEnvelope				PL_envelope;
PageLayoutLabel				PL_label;
} PageLayout;

PageLayoutEnvelope

typedef WordFlags PageLayoutEnvelope;
#define PLE_PATH				0x0040
#define PLE_ORIENTATION				0x0010
#define PLE_TYPE				0x0004

PageLayoutLabel

typedef WordFlags PageLayoutLabel;
#define PLL_ROWS				0x7e00			/* labels down */
#define PLL_COLUMNS				0x01f8			/* labels across */
#define PLL_TYPE				0x0004			/* PT_LABEL */

PageLayoutPaper

typedef WordFlags PageLayoutPaper;
#define PLP_ORIENTATION				0x0008
#define PLP_TYPE				0x0004

PageSize

typedef struct {
word		unused;
word		PS_width;
word		PS_height;
PageLayout		PS_layout;
} PageSize;

PageSizeCtrlAttrs

typedef WordFlags PageSizeCtrlAttrs;
#define PZCA_ACT_LIKE_GADGET					0x8000
#define PZCA_PAPER_SIZE					0x4000
#define PZCA_INITIALIZE					0x2000

PageSizeCtrlFeatures

typedef ByteFlags PageSizeControlFeatures;
#define PSIZECF_MARGINS				0x04
#define PSIZECF_ALL 				0x02
#define PSIZECF_PAGE_TYPE				0x01

PageSizeReport

typedef struct {
dword 			PSR_width;
dword 			PSR_height;
PageLayout 			PSR_layout;
PCMarginParams			PSR_margins;
} PageSizeReport:

PageType

typedef enum {
PT_PAPER,
PT_ENVELOPE,
PT_LABEL
} PageType;

PaperOrientation

typedef ByteEnum PaperOrientation;
#define PO_PORTRAIT				0x00
#define PO_LANDSCAPE				0x01

ParallelUnit

typedef	enum
{
    PARALLEL_LPT1	= 0,
    PARALLEL_LPT2	= 2,
    PARALLEL_LPT3	= 4,
    PARALLEL_LPT4	= 6,
} ParallelUnit;

ParserFlags

typedef ByteFlags ParserFlags;
#define PF_HAS_LOOKAHEAD						0x80
#define PF_CONTAINS_DISPLAY_FUNC 						0x40
#define PF_OPERATORS 						0x20
#define PF_NUMBERS 						0x10
#define PF_CELLS 						0x08
#define PF_FUNCTIONS 						0x04
#define PF_NAMES 						0x02
#define PF_NEW_NAMES 						0x01

ParserParameters

typedef struct {
CommonParameters			PP_common;
word			PP_parserBufferSize;
ParserFlags 			PP_flags;
dword			PP_textPtr;
ScannerToken			PP_currentToken;
ScannerToken			PP_lookAheadToken;
byte			PP_error;				/* ParserScannerEvaluatorError */
word			PP_tokenStart;
word			PP_tokenEnd;
} ParserParameters;

ParserScannerEvaluatorError

typedef ByteEnum ParserScannerEvaluatorError;
/*
 * Scanner errors
 */
#define PSEE_BAD_NUMBER									0
#define PSEE_BAD_CELL_REFERENCE 									1
#define PSEE_NO_CLOSE_QUOTE 									2
#define PSEE_COLUMN_TOO_LARGE 									3
#define PSEE_ROW_TOO_LARGE 									4
#define PSEE_ILLEGAL_TOKEN 									5
/*
 * Parser errors
 */
#define PSEE_GENERAL 									6
#define PSEE_TOO_MANY_TOKENS 									7
#define PSEE_EXPECTED_OPEN_PAREN 									8
#define PSEE_EXPECTED_CLOSE_PAREN 									9
#define PSEE_BAD_EXPRESSION 									10
#define PSEE_EXPECTED_END_OF_EXPRESSION 									11
#define PSEE_MISSING_CLOSE_PAREN 									12
#define PSEE_UNKNOWN_IDENTIFIER 									13
#define PSEE_NOT_ENOUGH_NAME_SPACE 									14
/*
 * Serious evaluator errors
 */
#define PSEE_OUT_OF_STACK_SPACE 									15
#define PSEE_NESTING_TOO_DEEP 									16
/*
 * Evaluator errors that are returned as the result of formulas.
 * These are returned on the argument stack.
 */
#define PSEE_ROW_OUT_OF_RANGE 									17
#define PSEE_COLUMN_OUT_OF_RANGE 									18
#define PSEE_FUNCTION_NO_LONGER_EXISTS 									19
#define PSEE_BAD_ARG_COUNT 									20
#define PSEE_WRONG_TYPE 									21
#define PSEE_DIVIDE_BY_ZERO 									22
#define PSEE_UNDEFINED_NAME 									23
#define PSEE_CIRCULAR_REF 									24
#define PSEE_CIRCULAR_DEP 									25
#define PSEE_CIRC_NAME_REF 									26
#define PSEE_NUMBER_OUT_OF_RANGE 									27
#define PSEE_GEN_ERR 									28
#define PSEE_NA 									29
/*
 * Dependency errors
 */
#define PSEE_TOO_MANY_DEPENDENCIES 									30
#define PSEE_SSHEET_BASE 								0xc0
#define PSEE_FLOAT_BASE 								250
#define PSEE_APP_BASE 								230
#define PSEE_FLOAT_POS_INFINITY 								PSEE_FLOAT_BASE
#define PSEE_FLOAT_NEG_INFINITY 								(PSEE_FLOAT_BASE + 1)
#define PSEE_FLOAT_GEN_ERR 								(PSEE_FLOAT_BASE + 2)

ParserToken

typedef struct {
ParserTokenType			PT_type;
ParserTokenData			PT_data;
} ParserToken;

ParserTokenCellData

typedef struct {
CellReference			PTCD_cellRef;
} ParserTokenCellData;

ParserTokenData

typedef union {
ParserTokenNumberData 				PTD_number;
ParserTokenStringData 				PTD_string;
ParserTokenNameData 				PTD_name;
ParserTokenCellData 				PTD_cell;
ParserTokenFunctionData 				PTD_function;
ParserTokenOperatorData				PTD_operator;
} ParserTokenData;

ParserTokenFunctionData

typedef struct {
word		PTFD_functionID;
} ParserTokenFunctionData;

ParserTokenNameData

typedef struct {
word		PTND_name;
} ParserTokenNameData;

ParserTokenNumberData

typedef struct {
FloatNum		PTND_value;
} ParserTokenNumberData;

ParserTokenOperatorData

typedef struct {
OperatorType			PTOD_operatorID;
} ParserTokenOperatorData;

ParserTokenStringData

typedef struct {
word		PTSD_length;
} ParserTokenStringData;

ParserTokenType

typedef ByteEnum ParserTokenType;
#define PARSER_TOKEN_NUMBER 								0
#define PARSER_TOKEN_STRING 								1
#define PARSER_TOKEN_CELL 								2
#define PARSER_TOKEN_END_OF_EXPRESSION 								3
#define PARSER_TOKEN_OPEN_PAREN								4
#define PARSER_TOKEN_CLOSE_PAREN 								5
#define PARSER_TOKEN_NAME 								6
#define PARSER_TOKEN_FUNCTION 								7
#define PARSER_TOKEN_CLOSE_FUNCTION								8
#define PARSER_TOKEN_ARG_END 								9
#define PARSER_TOKEN_OPERATOR 								10

PathCombineType

typedef enum /* word */ {
PCT_NULL,				/* wipe out old path */
PCT_REPLACE,				/* replace old path with upcoming path */
PCT_UNION,				/* union old path with new */
PCT_INTERSECTION 				/* intersect old path with new */
} PathCombineType;

PathName

typedef char PathName[PATH_BUFFER_SIZE];

PatternType

typedef ByteEnum PatternType;
#define PT_SOLID				0
#define PT_SYSTEM_HATCH 				1
#define PT_SYSTEM_BITMAP 				2
#define PT_USER_HATCH 				3
#define PT_USER_BITMAP 				4
#define PT_CUSTOM_HATCH 				5
#define PT_CUSTOM_BITMAP 				6

PCDocSizeParams

typedef struct {
dword 	PCDSP_width;
dword 	PCDSP_height;
} PCDocSizeParams;

Use this structure to communicate document sizes to a Print Control.

PCMarginParams

typedef struct {
word 	PCMP_left; 			/* left margin */
word 	PCMP_top; 			/* top margin */
word 	PCMP_right; 			/* right margin */
word 	PCMP_bottom; 			/* bottom margin */
} PCMarginParams

This structure holds information about a document's or printer's margins.

PCProgressType

typedef enum {
PCPT_PAGE,
PCPT_PERCENT,
PCPT_TEXT
} PCProgressType;

PDAType

typedef ByteEnum PDAType;
#define PDAT_PC_EMULATOR        0x0
#define PDAT_N9000              0x1
#define PDAT_N9000i             0x2

These values correspond to the different Nokia 9000 Communicator hardware platforms. They are only available on builds 4 or newer of the device (see Versions for more information).

Include: respondr.goh

See Also: RespGetPDAHardwareInfo()

Point

typedef struct {
sword P_x;
sword P_y;
} Point;

PointDWord

typedef struct {
sdword PD_x;
sdword PD_y;
} PointDWord;

PointDWFixed

typedef struct {
DWFixed PDF_x;
DWFixed PDF_y;
} PointDWFixed;

PointerDef32

typedef struct {
sbyte	PD_hotX;
sbyte	PD_hotY;
byte	PD_mask[CURSOR_IMAGE_SIZE_32];
byte	PD_image[CURSOR_IMAGE_SIZE_32];
} PointerDef32;
 STANDARD_CURSOR_IMAGE_SIZE = 32

This structure defines a mouse pointer.

PointWWFixed

typedef struct {
WWFixed 	PF_x;
WWFixed 	PF_y;
} PointWWFixed;

These structures are used to specify graphics point coordinates. Which point structure to use depends on size of the coordinate space and accuracy required.

PrintControlAttrs

typedef WordFlags PrintControlAttrs;
#define PCA_MARK_APP_BUSY					0x2000			/* mark busy while printing */
#define PCA_VERIFY_PRINT					0x1000			/* verify before printing */
#define PCA_SHOW_PROGRESS					0x0800			/* show print progress dialog box */
#define PCA_PROGRESS_PERCENT					0x0400			/* show progress by percentage */
#define PCA_PROGRESS_PAGE					0x0200			/* show progress by page */
#define PCA_FORCE_ROTATION					0x0100			/* Force rotation of output */
#define PCA_COPY_CONTROLS					0x0080			/* Copy controls are available */
#define PCA_PAGE_CONTROLS					0x0040			/* Page range controls available */
#define PCA_QUALITY_CONTROLS					0x0020			/* Quality controls available */
#define PCA_USES_DIALOG_BOX					0x0010			/* Dialog box should appear */
#define PCA_GRAPHICS_MODE					0x0008			/* Supports graphics mode output */
#define PCA_TEXT_MODE					0x0004			/* Supports text mode output */
#define PCA_DEFAULT_QUALITY					0x0002			/* default print quality */

PrintControlFeatures

typedef ByteFlags PrintControlFeatures;
#define PRINTCF_PRINT_TRIGGER					0x02			/* wants a print trigger */
#define PRINTCF_FAX_TRIGGER					0x01			/* wants a fax trigger */

PrintControlStatus

typedef enum {
PCS_PRINT_BOX_VISIBLE,
PCS_PRINT_BOX_NOT_VISIBLE
} PrintControlStatus;

PrintControlToolboxFeatures

typedef ByteFlags PrintControlToolboxFeatures;
#define PRINTCTF_PRINT_TRIGGER						0x02		/* wants a print tool trigger */
#define PRINTCTF_FAX_TRIGGER						0x01		/* wants a fax tool trigger */

PrinterDriverType

typedef enum PrinterDriverType;
PDT_PRINTER,
PDT_PLOTTER,
PDT_FACSIMILE,
PDT_CAMERA,
PDT_OTHER,
} PrinterDriverType;

This enumerated type indeicates the type of printer driver that we are dealing with.

PrinterOutputModes

typedef ByteFlags PrinterOutputModes;
#define POM_GRAPHICS_LOW					0x10
#define POM_GRAPHICS_MEDIUM					0x08
#define POM_GRAPHICS_HIGH					0x04
#define POM_TEXT_DRAFT					0x02
#define POM_TEXT_NLQ					0x01
#define PRINT_GRAPHICS =				(POM_GRAPHICS_LOW | POM_GRAPHICS_MEDIUM |
                 POM_GRAPHICS_HIGH )
#define PRINT_TEXT =				(POM_TEXT_DRAFT | POM_TEXT_NLQ)

PrintQualityEnum

typedef enum {
PQT_HIGH,
PQT_MEDIUM,
PQT_LOW
} PrintQualityEnum;

ProtocolNumber

typedef struct {
word	PN_major;
word	PN_minor;
} ProtocolNumber;

Defines the protocol level of a file, geode, or document. PN_major represents significant compatibility comparisons, and PN_minor represents less significant differences. If the major protocol is different between to items, they are incompatible. If the minor protocol is different, they may or may not be incompatible.

QueueHandle

typedef Handle QueueHandle;

QuickSortParameters

typedef struct _QuickSortParameters {
word _pascal (*QSP_compareCallback) 							(void *			el1,
                             void *			el2,
                              word 			valueForCallback));
void _pascal (*QSP_lockCallback) (void *el, word valueForCallback));
void _pascal (*QSP_unlockCallback) (void *el, word valueForCallback));
word 		QSP_insertLimit;
word 		QSP_medianLimit;

/* These are set internally by the quicksort algorithm and should not
 * be set by the caller: */
word 		QSP_nLesser;
word 		QSP_nGreater;

} QuickSortParameters;

This structure is passed to ArrayQuickSort . The fields have the following meanings:

* QSP _compareCallback
This routine is called to compare elements. It should be declared _pascal. It should return a positive value if *el1 ought to come before *e2 in the sorted list; a negative value if *el1 ought to come after *e2 in the sorted list; and zero if it doesn't matter which comes first.
* QSP _lockCallback
This routine is called before ArrayQuickSort examines or changes any element. It should be declared _pascal. You can pass a null function pointer, indicating that no locking callback routine should be called.
* QSP _lockCallback
This routine is called after ArrayQuickSort examines or changes any element. It should be declared _pascal. You can pass a null function pointer, indicating that no unlocking callback routine should be called.
QSP _insertLimit
If there are fewer than QSP _insertLimit elements in a sublist, ArrayQuickSort will use an insertion sort for that sublist, rather than a QuickSort.
QSP _medianLimit
If there are fewer than QSP _medianLimit elements in a sublist, ArrayQuickSort will use the first element as a partition, instead of searching for the median element.

RangeEnumCallbackParams

See RangeEnum() .

RangeEnumFlags

typedef ByteFlags RangeEnumFlags;
#define REF_ALL_CELLS							0x80
#define REF_NO_LOCK							0x40
#define REF_COLUMN_FLAGS							0x20
#define REP_MATCH_COLUMN_FLAGS							0x10
#define REF_CELL_ALLOCATED							0x08
#define REF_CELL_FREED							0x04
#define REF_OTHER_ALLOC_OR_FREE							0x02
#define REF_COLUMN_FLAGS_MODIFIED							0x01

These flags are used by RangeEnum() .

RangeEnumParams

typedef struct {
PCB(RANGE_ENUM_CALLBACK_RETURN_TYPE, REP_callback,
                                        (RangeEnumCallbackParams));
Rectangle				REP_bounds;
byte				REP_columnFlags;
word				*REP_columnFlagsArray;
CellFunctionParameters				*REP_cfp;
byte				REP_matchFlags;
word				*REP_locals;
} RangeEnumParams;

This structure is used by two routines, RangeEnum() and CellGetExtent() . When it is used by RangeEnum() , the structure specifies all the details about how RangeEnum() will function. CellGetExtent() is passed a blank RangeEnumParams structure; it fills in the REP_bounds field.

The callback routine, if any, should be declared _pascal.

Include: cell.h

RangeInsertParams

See RangeInsert() .:

RangeSortError

typedef enum /* word */ {
RSE_NO_ERROR,
RSE_UNABLE_TO_ALLOC,
} RangeSortError;

RangeSortCellExistFlags

typedef ByteFlags RangeSortCellExistsFlags;
#define RSCEF_SECOND_CELL_EXISTS 0x02
#define RSCEF_FIRST_CELL_EXISTS 0x01

RangeSortFlags

typedef ByteFlags RangeSortFlags;
#define RSF_SORT_ROWS						0x80
#define RSF_SORT_ASCENDING						0x40
#define RSF_IGNORE_CASE						0x20

RangeSortParams

typedef struct {
Rectangle		RSP_range;
Point		RSP_active;
dword		RSP_callback;
byte		RSP_flags; /* RangeSortFlags */
dword		RSP_cfp;
word		RSP_sourceChunk;
word		RSP_destChunk;
word		RSP_base;
dword		RSP_lockedEntry;
byte		RSP_cachedFlags;
} RangeSortParams;

RecordHeader

typedef struct {
RecordID		RH_id;
word		RH_size;
byte		RH_fieldCount;
} RecordHeader;

Structure containing metadata about a record; first FieldHeader follows.

RecordID

typedef dword RecordID;

Uniquely identifies a record in a datastore; this value stays constant and does not change when other records are added or deleted.

RecordNum

typedef dword RecordNum;

Specifies relative place in the datastore; this value may change when other records are added or deleted.

Rectangle

typedef struct {
sword	R_left;
sword	R_top;
sword	R_right;
sword	R_bottom;
} Rectangle;

This structure represents a graphics rectangle.

RectDWord

typedef struct {
sdword	RD_left;
sdword	RD_top;
sdword	RD_right;
sdword	RD_bottom;
} RectDWord;

This structure represents a graphics rectangle.

RectRegion

typedef struct {
word 	RR_y1M1;
word 	RR_eo1;	/* EOREGREC */
word 	RR_y2;
word 	RR_x1;
word 	RR_x2;
word 	RR_eo2;		/* EOREGREC */
word 	RR_eo3;		/* EOREGREC */
} RectRegion;

RefElementHeader

typedef struct {
 WordAndAHalf REH_refCount;
} RefElementHeader;

Region

typedef word Region;
#define EOREGREC				0x8000
#define EOREG_HIGH				 0x80

This structure represents a region of a graphics coordinate space.

Regions are described in terms of a rectangular array (thus the similarity to bitmaps). Instead of specifying an on/off value for each pixel, however, regions assume that the region will be fairly undetailed and that the data structure can thus be treated in the manner of a sparse array. Only the cells in which the color value of a row changes are recorded. The tricky part here is keeping in mind that when figuring out whether or not a row is the same as a previous row, the system works its way up from the bottom, so that you should compare each row with the row beneath it to determine whether it needs an entry.

The easiest region to describe is the null region, which is a special case described by a single word with the value EOREGREC (a constant whose name stands for E nd Of REG ion REC ord value). Describing a non-null region requires several numbers.

The first four numbers of the region description give the bounds of the region. Next come one or more series of numbers. Each series describes a row, specifying which pixels of that row are part of the region. The only rows which need to be described are those which are different from the row below. The first number of each row description is the row number, its y coordinate. The last number of each series is a special token, EOREGREC, which lets the kernel know that the next number of the description will be the start of another row. Between the row number and EOREGREC are the column numbers where the pixels toggle on and off. The first number after the row number corresponds to the first column in which the pixel is on; the next number is the first subsequent column in which the pixel is off; and so on.

RegionFillRule

typedef ByteEnum RegionFillRule;
#define ODD_EVEN				0
#define WINDING				1

This enumerated type determines how a path or region should be filled. Winding fill is more versatile, but requires that the path or polygon's edges run in the correct direction.

ReleaseNumber

typedef struct {
word	RN_major;
word	RN_minor;
word	RN_change;
word	RN_engineering;
} ReleaseNumber;

Used to record what version a file, document, or geode is. This represents the release level; the most significant numbers are RN_major and RN_minor . The other fields are typically used only internally to a manufacturer.

RGBColorAsDWord

typedef dword RGBColorAsDWord;
RGB_RED(val) ( val & 0xff)
RGB_GREEN(val) ( (val >> 8) & 0xff )
RGB_BLUE(val) ( (val >> 16) & 0xff )
RGB_INDEX(val) ( (val >> 24) & 0xff )

See the ColorQuad data structure to find out the meanings of the fields.

RGBDelta

typedef struct {
 byte RGBD_red;
 byte RGBD_green;
 byte RGBD_blue;
} RGBDelta;

RGBTransfer

typedef struct {
byte	RGBT_red[256];
byte	RGBT_green[256];
byte	RGBT_blue[256];
} RGBTransfer;

RGBValue

typedef struct {
byte	RGB_red;
byte	RGB_green;
byte	RGB_blue;
} RGBValue;

SACAddress

typedef struct {
        word                SACA_opaqueSize;
        /* label byte          SACA_opaque;    */
        /* user-readable form of address follows opaque address--use
         *   SACA_opaqueSize to compute the offset to this data. */
} SACAddress;

The SocketAddressControl uses SACAddress structures to provide information about addresses.

Most programs won't need to look at the innards of this structure: they will just pass the opaque address information to SocketResolve() to make sure that the address is valid and reduce it to its primitive form.

However, if you need to construct a "raw" address to pass to SocketResolve() , you'll need to know the structure of the opaque address information. It's an ExtendedSACAddress buffer followed by a null-terminated user-readable connection address string..

The ExtendedSACAddress buffer consists of:

  • A word giving the size of the optional link information (zero if there is no link information).
  • Link information, consisting of a LinkType value (one of LT_ADDR, LT_ID, or LT_DEFAULT), and parameters to identify the link. If the LinkType is LT_DEFAULT, then there are no further parameters. If the LinkType is LT_ID, then there is a word-length LinkID which acts as a permanent identifier for the link. If the LinkType is LT_ADDR, the parameter is a string which will be appended to the user-readable connection address string, surrounded by parentheses.

Include: sac.goh

SampleFormat

typedef struct {
         DACSampleFormat SMID_format:15;
         DACReferenceByte SMID_reference:1;
} SampleFormat;

SampleFormatDescription

typedef struct {
 word 	SFD_manufact;
 word 	SFD_format;
 word 	SFD_rate;
 word 	SFD_playFlags;
} SampleFormatDescription;

This structure acts as a header for a sampled sound, giving format information needed to properly interpret the sound data.

SansFace

typedef byte SansFace;
#define SF_A_CLOSED 0x0080
#define SF_A_OPEN 0x0000

sbyte

typedef char sbyte;

ScannerToken

typedef struct {
ScannerTokenType				ST_type;
ScannerTokenData				ST_data;
} ScannerToken;

ScannerTokenCellData

typedef struct {
CellReference			STCD_cellRef;
} ScannerTokenCellData;

ScannerTokenData

typedef union {
ScannerTokenNumberData					STD_number;
ScannerTokenStringData					STD_string;
ScannerTokenCellData					STD_cell;
ScannerTokenIdentifierData					STD_identifier;
ScannerTokenOperatorData					STD_operator;
} ScannerTokenData;

ScannerTokenIdentifierData

typedef struct {
word		STID_start;
} ScannerTokenIdentifierData;

ScannerTokenNumberData

typedef struct {
FloatNum		STND_value;
} ScannerTokenNumberData;

ScannerTokenOperatorData

typedef struct {
OperatorType			STOD_operatorID;
} ScannerTokenOperatorData;

ScannerTokenStringData

typedef struct {
word	STSD_start;
word	STSD_length;
} ScannerTokenStringData;

ScannerTokenType

typedef ByteEnum ScannerTokenType;
#define SCANNER_TOKEN_NUMBER 									0
#define SCANNER_TOKEN_STRING 									1
#define SCANNER_TOKEN_CELL 									2
#define SCANNER_TOKEN_END_OF_EXPRESSION 									3
#define SCANNER_TOKEN_OPEN_PAREN 									4
#define SCANNER_TOKEN_CLOSE_PAREN 									5
#define SCANNER_TOKEN_IDENTIFIER 									6
#define SCANNER_TOKEN_OPERATOR 									7
#define SCANNER_TOKEN_LIST_SEPARATOR									8

ScriptAttrAsWord

typedef word ScriptAttrAsWord;
/* 	High byte is a vertical offset, as a fraction of the font size.
    Low byte is a fractional scale to use.
    Thus, setting a subscript attr to 0x8020 would result in subscript
    characters being printed half a line down and at 1/4 normal size. */

This structure specifies the offset and scale factor with which sub- and superscript characters should draw.

ScriptFace

typedef byte ScriptFace;
#define SF_CURSIVE 0x0080
#define SF_CALLIGRAPHIC 0x0000

sdword

typedef long sdword;

SearchFlags

typedef ByteFlags SearchFlags;
#define	SF_BACKWARDS	 		0x80
#define	SF_START_AT_END	 		0x40
#define	SF_IGNORE_CASE	 		0x20
#define	SF_NO_WILDCARDS	 		0x10
#define	SF_PARTIAL_WORD	 		0x08

Flags to define search parameters; see SearchParams and DataStoreStringSearch() .

SearchParams

typedef struct {
SearchType		SP_searchType;
dword		SP_maxRecords;
FieldID		SP_startField;
FieldCategory	SP_category;
TCHAR		*SP_searchString;
SearchFlags		SP_flags;
} SearchParams;

Structure for specifying search conditions when calling DataStoreStringSearch() .

SearchType

typedef ByteEnum SearchType;
#define	ST_ALL	 		1	/* search all string fields */
#define	ST_FIELD			2	/* search by FieldID */
#define	ST_CATEGORY			3	/* search by FieldCategory */

Values to specify in search parameters; see SearchParams and DataStoreStringSearch() .

segment

typedef word segment;

SemaphoreError

typedef enum {
SE_NO_ERROR,					/* No error occurred */
SE_TIMEOUT,					/* The semaphore timed out before
                     * it could be grabbed by the thread */
SE_PREVIOUS_OWNER_DIED					/* The current holder of the semaphore
                     * exited abnormally */
} SemaphoreError;

Determines the error encountered by semaphore and threadlock routines such as ThreadPSem() and ThreadPTimedSem() .

SE_NO_ERROR
No error occurred and the semaphore was grabbed properly.
SE_TIMEOUT
The time elapsed and the semaphore was not grabbed. If this value is returned, the thread should not proceed with whatever protected operation was to happen. Instead, it should either attempt to grab the semaphore again or should proceed with other tasks.
SE_PREVIOUS_OWNER_DIED
The previous owner of the semaphore exited abnormally. If the thread currently holding the semaphore exited without releasing the semaphore, for example, this would be returned.

SerialBaud

typedef	enum
{
    SERIAL_BAUD_115200			= 1,
    SERIAL_BAUD_57600			= 2,
    SERIAL_BAUD_38400			= 3,
    SERIAL_BAUD_19200			= 6,
    SERIAL_BAUD_14400			= 8,
    SERIAL_BAUD_9600			= 12,
    SERIAL_BAUD_7200			= 16,
    SERIAL_BAUD_4800			= 24,
    SERIAL_BAUD_3600			= 32,
    SERIAL_BAUD_2400			= 48,
    SERIAL_BAUD_2000			= 58,
    SERIAL_BAUD_1800			= 64,
    SERIAL_BAUD_1200			= 96,
    SERIAL_BAUD_600			= 192,
    SERIAL_BAUD_300			= 384
} SerialBaud;

SerialFormat

typedef	ByteFlags SerialFormat;
#define	SERIAL_FORMAT_DLAB_OFFSET						(7)
#define	SERIAL_FORMAT_DLAB						(0x01 << SERIAL_FORMAT_DLAB_OFFSET)

#define	SERIAL_FORMAT_BREAK_OFFSET						(6)
#define	SERIAL_FORMAT_BREAK						(0x01 << SERIAL_FORMAT_BREAK_OFFSET)

#define	SERIAL_FORMAT_PARITY_OFFSET						(3)
#define	SERIAL_FORMAT_PARITY						(0x07 << SERIAL_FORMAT_PARITY_OFFSET)

#define	SERIAL_FORMAT_EXTRA_STOP_OFFSET (2)
#define	SERIAL_FORMAT_EXTRA_STOP						(0x01 <<	\
                                SERIAL_FORMAT_EXTRA_STOP_OFFSET)

#define	SERIAL_FORMAT_LENGTH_OFFSET						(0)
#define	SERIAL_FORMAT_LENGTH						(0x03 << SERIAL_FORMAT_LENGTH_OFFSET)

SerialMode

typedef	enum {
    SM_RAW,
    SM_RARE,
    SM_COOKED
} SerialMode;

#define SERIAL_MODEM_RTS (0x01 << SERIAL_MODEM_RTS_OFFSET)

SerialModem

typedef	ByteFlags SerialModem;
#define    SMC_RTS      0x02    /* Request-To-Send (1 asserts it) */
#define    SMC_DTR      0x01    /* Data-Terminal Ready (1 asserts it) */

#define SERIAL_MODEM_RTS_OFFSET						(1)

#define SERIAL_MODEM_DTR_OFFSET						(0)
#define SERIAL_MODEM_DTR						(0x01 << SERIAL_MODEM_DTR_OFFSET)

SerialPortNum

typedef	enum
{
    SERIAL_COM1			= 0,
    SERIAL_COM2			= 2,
    SERIAL_COM3			= 4,
    SERIAL_COM4			= 6,
    SERIAL_COM5			= 8,
    SERIAL_COM6			= 10,
    SERIAL_COM7			= 12,
    SERIAL_COM8			= 14
} SerialPortNum;

SemaphoreHandle

typedef Handle SemaphoreHandle;

SerifFace

typedef byte SerifFace;
#define SF_SLAB 0x00c0
#define SF_MODERN 0x0080
#define SF_TRANS 0x0040
#define SF_OLD 0x0000

SetPalType

typedef ByteEnum SetPalType;
#define SPT_DEFAULT				0
#define SPT_CUSTOM				1

ShiftState

typedef ByteFlags ShiftState;
#define SS_LALT				0x80
#define SS_RALT 				0x40
#define SS_LCTRL 				0x20
#define SS_RCTRL 				0x10
#define SS_LSHIFT 				0x08
#define SS_RSHIFT 				0x04
#define SS_FIRE_BUTTON_1 				0x02
#define SS_FIRE_BUTTON_2				0x01

Modifiers which will be incorporated into input information. Corresponds to alt keys, control keys, shift keys, or special system modifiers. Note that these bits will only be set if not already accounted for; that is, if you are passed the character "E", the shift modifiers of this structure will not be marked.

Socket

typedef word Socket;

A Socket represents one side of a communication managed by the Socket library.

Include: socket.goh

SocketAddress

typedef struct {
        SocketPort  SA_port;
        word        SA_domainSize; 					/*  size in bytes of SA_domain buffer */
        char        *SA_domain; 					/*  null terminated domain name */
        word        SA_addressSize; 					/*  size in bytes of address data */
} SocketAddress;

Addresses are made up of a port number, a domain, and address data:

SA_port
Port numbers identify a particular line of communication within a machine. Port numbers may be 32-bit or 16-bit, depending on the domain. They are specified via the SocketPort structure.
SA_domain , SA_domainSize
The domain identifies the protocol of the network by which the addressed machine may be reached. The domain is specified by a string. SA_domain is a pointer to the null-terminated domain name string and SA_domainSize is the size of the buffer containing the domain name string.
Some routines are passed an empty SocketAddress structure. This means that you should have allocated memory for SA_domain, and initialized SA_domain and SA_domainSize .
SA_addressSize , Address Data
The format of the address data used to identify a machine within a domain depends on that domain. The SA_addressSize field contains the size of the address data. The buffer containing the address data should fall immediately after the SocketAddress structure.
Some routines are passed an empty SocketAddress structure. This means that you should leave room for address data after the structure and initialize the SA_addressSize field.

Include: socket.goh

SocketBindFlags

typedef WordFlags SocketBindFlags;
        #define SBF_REUSE_PORT  (0x8000)

These flags are used to specify options when binding a socket to a port.

There is only one flag: SBF_REUSE_PORT, requesting that the socket be bound to the port even if another socket is already bound to it. You may not use this flag with datagram sockets.

SocketCheckRequest

typedef struct {
        word 		SCR_socket;
        SocketCondition 		SCR_condition;
        byte 		SCR_reserved;
} SocketCheckRequest;
typedef ByteEnum SocketCondition;
        #define SC_READ 			0x0 			/*  data arrived */
        #define SC_WRITE 			0x1 			/*  data can be written */
        #define SC_ACCEPT 			0x2 			/*  connect request arrived */
        #define SC_EXCEPTION 			0x3 			/*  an exception occurred */
        #define SC_URGENT 			0x4 			/*  urgent data arrived */

A SocketCheckRequest is used to query a socket about whether it is ready to carry out some operation. You will need to set two fields:

SCR_socket
The socket to query.
SCR_condition
The operation to query about.

Include: socket.goh

SocketCondition

See SocketCheckRequest .

typedef ByteEnum

SocketDeliveryType

SocketDeliveryType;
        #define SDT_DATAGRAM 			0x0 		/*  unreliable packets */
        #define SDT_SEQ_PACKET 			0x1 		/*  reliable packets */
        #define SDT_STREAM 			0x2 		/*  reliable bytes */

Include: socket.goh

SocketError

typedef enum {
        SE_NORMAL = 0x0,
        /*  operation completed successfully */

        SE_OUT_OF_MEMORY,
        /*
         *  the net library was unable to allocate memory
         *  either there is no more system memory available, or a net-library
         *  specific upper bound on memory usage has been exceeded
         */

        SE_PORT_IN_USE,
        /*
         *  Another socket is already bound to this port, and the
         *  SBF_REUSE_PORT flag was not set.
         */

        SE_PORT_ALREADY_LISTENING,
        /*  Another socket is already listening on this port. */

        SE_SOCKET_IN_USE,
        /*
         *  The socket is already being used in a way inconsistent with the
         *  requested operation
         */

        SE_SOCKET_ALREADY_BOUND,
        /*  The socket is already bound to a port. */

        SE_SOCKET_NOT_BOUND,
        /*  The socket must be bound to a port for the requested operation. */

        SE_SOCKET_NOT_LISTENING,
        /*  The socket must be listening on a port for the requested operation. */

        SE_SOCKET_LISTENING,
        /*  A listening socket cannot be used for outgoing connections */

        SE_CONNECTION_REFUSED,
        /*  Nobody is listening on the remote port. */

        SE_CONNECTION_FAILED,
        /*  The connection is no longer valid, probably due to a
         *  link failure. */

        SE_CONNECTION_CLOSED,
        /*  The connection has been closed from the other end. */

        SE_NON_UNIQUE_CONNECTION,
        /*  Another connection with the same endpoint addresses and ports exists. */

        SE_TIMED_OUT,
        /*
         *  The timeout period expired before the operation completed.
         *  In some cases the protocol may enforce a shorter timeout then
         *  the user-specified value, so this is possible even where
         *  SOCKET_NO_TIMEOUT is used.
         */

        SE_CANT_LOAD_DRIVER,
        /*  A driver required to establish the connection could
         *  not be loaded. */

        SE_UNKNOWN_DOMAIN,
        /*  The requested domain is not known to the libarary. */

        SE_SYSTEM_SHUTDOWN,
        /*  The operation was aborted because GEOS is exiting */

        SE_SOCKET_NOT_CONNECTED,
        /*  The operation requires a connected socket */

        SE_SOCKET_ALREADY_CLOSED,
        /*  The send side of the socket is already closed */

        SE_SOCKET_BUSY,
        /*  Another thread is operating on the socket */

        SE_IMPROPER_CONDITION,
        /*
         *  The requested condition for SocketCheckReady is not valid
         *  for a socket in the current state
         */

        SE_INFO_NOT_AVAILABLE,
        /*  The driver could not provide the requested information */

        SE_DOMAIN_REQUIRES_16BIT_PORTS,
        /*
         *  The requested domain only allows 16 bit port numbers.  The
         *  SP_manuf field must be set to MANUFACTURER_ID_SOCKET_16BIT_PORT
         */

        SE_CONNECTION_ERROR,
        /*
         *  The connection was not made for some reason which does not have
         *  it's own specific SocketError
         */

            SE_MEDIUM_BUSY,
        /*  The medium required for the connection is not available */

            SE_DESTINATION_UNREACHABLE,
        /*  The requested destination is not available */

            SE_LINK_FAILED,
        /*
         *  The low level link used for this connection failed or could not be
         *  established
         */

            SE_INTERNAL_ERROR,
        /*
         *  The socket library received an impossible error code from the driver.
         *  Either the library or the driver has a bug.
         */

            SE_CLOSE_SEND_NOT_SUPPORTED,
        /*
         *  It is not possible to close only the send side of a socket in this
         *  domain.  Use SocketClose instead.
         */

        SE_BIND_CONFLICT,
        /*
         *  The same port cannot have both domain restricted and
         *  domain unrestricted bindings.
         */

            SE_ALL_PORTS_BUSY,
        /*
         *  SocketConnect attempted to implicitly bind a socket,
         *  but failed because all port numbers in the range used by
         *  implicit binds were in use
         */

            SE_SOCKET_NOT_INTERRUPTIBLE,
        /*
         *  An operation which cannot be interrupted is in in progress on the
         *  socket
         */

            SE_INTERRUPT,
        /*  The operation was interrupted with SocketInterrupt */

            SE_PORT_NOT_LISTENING,
        /* No listening socket or LoadOnMsg request exists for this port */

            SE_LISTEN_QUEUE_EMPTY,
        /* No connections are pending for this port */

            SE_SOCKET_CLOSED,
        /* The socket must be open for sending to call either
         *  SocketGetSocketName or SocketGetPeerName */

        SE_BUFFER_TOO_SMALL,
        /*  Returned by SocketResolve to mean that buffer for resolved
         *  address is too small */

        SE_TEMPORARY_ERROR,
        /* Returned by SocketResolve to indicate that address resolution
         * is temporarily unavailable for external reasons( such as
         * network down or server failure )*/

        SE_EXCEPTION,
        SE_URGENT = SE_EXCEPTION,
        /* Returned by SocketRecv to indicate that some urgent data
         *  arrived on a reliable socket or an exception was received
         * for a datagram socket
         */

        SE_NOT_IMPLEMENTED,
        /* Returned by API functions which are not yet implemented */

        SE_CONNECTION_RESET
        /* The connection was reset by the peer, by the local driver (perhaps
         * in response to SocketCloseDomainMedium()), or by the Socket library
         * because the Socket is freed when its owning thread exits.
         */
} SocketError;

Certain socket routines generate error values or generate thread errors under certain conditions. The error value will consist of a word-length value: a SocketError value in the low byte, and perhaps a SpecSocketDrError value in the high byte.

SocketLoadType

typedef enum {
        SLT_USER_LOAD_APPLICATION = 0x0,
        SLT_GEODE_LOAD,
} SocketLoadType;

The Socket library has the ability to load an application when it senses packet coming in to a port. It uses the SocketLoadType to determine how to load the application: via UserLoadApplication() or GeodeLoad() .

Include: socket.goh

SocketOption

typedef enum {
        SO_RECV_BUF = 0x0,                   /* receive buffer size     */
        SO_SEND_BUF = 0x2,                   /* send buffer size        */
        SO_INLINE = 0x4,                     /* inline urgent data      */
        SO_NODELAY = 0x6,                    /* send data immediately   */
        SO_OWNER = 0x8,                      /* which app owns socket   */
} SocketOption;

Various options which may be changed on a per-Socket basis by means of the SocketSetIntSocketOption() routine.

Include: socket.goh

SocketPort

typedef struct {
        word		SP_port;				/*  16-bit port number */
        ManufacturerID		SP_manuf;				/*  16-bit manufacturer's ID */
} SocketPort;

Port numbers identify a particular line of communication within a machine. Port numbers may be 32-bit or 16-bit, depending on the domain. They are specified via the SocketPort structure, made up of a 16-bit ManufacturerID and 16-bit token number. If the domain uses 16-bit socket numbers, then use MANUFACTURER_ID_SOCKET_16BIT_PORT as the ManufacturerID . If the domain allows 32-bit socket numbers, then you may use MANUFACTURER_ID_SOCKET_16BIT_PORT or some other ManufacturerID .

Include: socket.goh

SocketRecvFlags

typedef WordFlags SocketRecvFlags;
        #define SRF_ADDRESS (0x8000) 					/*  return source address of data */
        #define SRF_URGENT (0x4000) 					/*  return urgent data, if any */
        #define SRF_PEEK (0x2000) 					/*  leave data in socket */

These flags allow you to set options when receiving data from a socket. SRF_ADDRESS requests the address of the machine sending the data. SRF_URGENT requests that only urgent data be returned, if any. SRF_PEEK asks that the data be left on the socket.

Include: socket.goh

SocketSendFlags

typedef WordFlags SocketSendFlags;
        #define SSF_ADDRESS (0x8000) 						/*  an address is supplied */
        #define SSF_URGENT (0x4000) 						/*  this is urgent data */
        #define SSF_OPEN_LINK (0x2000)					 	/*  this datagram should open the
                                 *    link, if it is closed       */

Use this structure to specify options when sending data over a socket. You may signal that you are specifying an address and/or that the packet sent should be marked urgent.

Include: socket.goh

SortOrder

typedef ByteEnum SortOrder;
#define SO_ASCENDING				0
#define SO_DESCENDING				1

SoundDriverCapability

typedef WordFlags SoundDriverCapability;
#define SDC_NOISE 				0x8000
#define SDC_WAVEFORM 				0x6000
#define SDC_TIMBRE 				0x1800
#define SDC_ENVELOPE 				0x0600

typedef WordFlags SoundDriverNoiseCapability;
#define SDNC_NO_NOISE 				0x0000
#define SDNC_WHITE_NOISE 				0x8000

typedef WordFlags SoundDriverWaveFormCapability
#define SDWFC_NONE 				0x0000
#define SDWFC_SELECT 				0x2000
#define SDWFC_GENERATE 				0x4000

typedef WordFlags SoundDriverTimbreCapability;
#define SDTC_TONE_GENERATOR 0x0000
#define SDTC_ADDITIVE 				0x0800
#define SDTC_MODULATOR 				0x1000
#define SDTC_SELECTIVE 				0x1800

typedef WordFlags SoundDriverEnvelopeCapability;
#define SDEC_NONE 				0x0000
#define SDEC_ADSR 				0x0200
#define SDEC_DSP 				0x0400

These fields encode information about what the sound driver is capable of in terms of music synthesis.

SoundPlayFlags

typedef WordFlags SoundPlayFlags;
#define SPF_HIGH_PRIORITY				0x8000

SoundPriority

typedef enum {
 SP_SYSTEM_LEVEL=10, 				/* most urgent */
 SP_ALARM=20,
 SP_STANDARD=30,
 SP_GAME=40,
 SP_BACKGROUND=50 				/* least urgent */
} SoundPriority;

#define SP_IMMEDIATE 			-1
#define SP_THEME 			+1

If the user's sound device can't play all requested sounds, it will use SoundPriority values to determine which sounds are the most important.

The highest priority sound you may construct using these values is (SP_SYSTME_LEVEL + SP_IMMEDIATE). The least priority sound would be (SP_BACKGROUND + SP_THEME).

SoundStreamDeltaTimeType

typedef enum {
 SSDTT_MSEC=8, 				/* wait for N mili seconds */
 SSDTT_TICKS=10, 				/* wait for N ticks */
 SSDTT_TEMPO=12, 				/* wait for N beats */
} SoundStreamDeltaTimeType;
/* The following macros may help when constructing music buffers */
#define DeltaTick(time)  				SSDTT_TICKS, time
#define DeltaMS(time)  				SSDTT_MSEC, time
#define DeltaTempo(time) 				SSDTT_TEMPO, time

These are the units by which you can specify a sound's duration: milliseconds, timer "ticks" (each 1/60 second), or by means of an independently supplied tempo.

SoundStreamEvents

typedef enum {
 SSE_VOICE_ON=0, 				/* turn on voice event */
 SSE_VOICE_OFF=2, 				/* turn off voice event */
 SSE_CHANGE=4, 				/* change instrument */
 SSE_GENERAL=6 				/* system-specific event */
} SoundStreamEvents;
/* The following macros may help when constructing music buffers */
#define General(command)  SSE_GENERAL, command

#define Rest(duration)  General(GE_NO_EVENT), DeltaTick(duration)

#define VoiceOn(voice,freq,attack)  SSE_VOICE_ON, voice, freq, attack
#define VoiceOff(voice) SSE_VOICE_OFF, voice
#define ChangeEnvelope(voice, instrument, table)  \
                    SSE_CHANGE, voice, instrument, table

#define SoundNote(voice,freq,duration,attack)  \
    VoiceOn(voice, freq, attack), DeltaTempo(duration), VoiceOff(voice)
#define Staccato(voice,freq,duration,attack) \
     VoiceOn(voice, freq, attack), DeltaTempo(((duration*0x03)/0x04)), \
     VoiceOff(voice), DeltaTempo((duration/0x4))
#define Natural(voice,freq,duration,attack) \
    VoiceOn(voice, freq, attack), DeltaTempo(((duration*0x07)/0x08)),
    VoiceOff(voice), DeltaTempo((duration/0x8))
#define Legato(voice,freq,duration,attack)  \
    SoundNote(voice, freq, duration, attack)

These are the "events" that make up a music buffer.

SoundStreamSize

typedef word SoundStreamSize;
#define SSS_ONE_SHOT 128 /* 128 bytes (very small) */
#define SSS_SMALL 256 /* 256 bytes */
#define SSS_MEDIUM 512 /* 512 bytes (nice size) */
#define SSS_LARGE 1024

SpecHeight

typedef WordFlags SpecHeight;
#define SH_TYPE				0x8c00
#define SH_DATA				0x03ff

SpecSizeSpec

typedef WordFlags SpecSizeSpec;
#define SSS_TYPE				0x8c00
#define SSS_DATA				0x03ff

SpecSizeType

typedef ByteEnum SpecSizeType;
#define SST_PIXELS						0x0000
#define SST_COUNT						0x0400
#define SST_PCT_OF_FIELD_WIDTH						0x0800
#define SST_PCT_OF_FIELD_HEIGHT						0x0c00
#define SST_AVG_CHAR_WIDTHS						0x1000
#define SST_WIDE_CHAR_WIDTHS						0x1400
#define SST_LINES_OF_TEXT						0x1800

SpecSocketDrError

typedef enum /*word*/ {
        SSDE_DEVICE_BUSY = 0x0100,      						/* serial port or modem
                               driver in use */
        SSDE_DIAL_ERROR = 0x0200,
        SSDE_LINE_BUSY = 0x0300,        /* modem got a busy signal */
        SSDE_NO_DIALTONE = 0x0400,
        SSDE_NO_ANSWER = 0x0500,
        SSDE_NO_CARRIER = 0x0600,
        SSDE_BLACKLISTED = 0x0700,      /* used in GSM network */
        SSDE_DELAYED = 0x0800,          /* used in GSM network */
        SSDE_CALL_FAILED = 0x0900, 						/* couldn't dial for some reason */
        SSDE_NEG_FAILED = 0x0a00,
        SSDE_AUTH_REFUSED = 0x0b00,
        SSDE_AUTH_FAILED = 0x0c00,
        SSDE_LQM_FAILURE = 0x0d00,
        SSDE_LOOPED_BACK = 0x0e00,
        SSDE_IDLE_TIMEOUT = 0x0f00,
        SSDE_DEVICE_NOT_FOUND = 0x1000,
        SSDE_DEVICE_TIMEOUT = 0x1100,
        SSDE_DEVICE_ERROR = 0x1200,
        SSDE_NO_USERNAME = 0x1300, 						/* no username and password
                               prompting used */
        SSDE_CANCEL = 0x1400,           /* user cancelled */
        SSDE_INVALID_ACCPNT = 0x1500    /* invalid access point */
} SpecSocketDrError;

Certain Socket library operations may return an error value as a SocketError value. If the root cause of the error was a low-level communications failure, then the high byte of the returned error word may contain a SpecSocketDrError value, with the SocketError value in the low byte.

SpecWidth

typedef WordFlags SpecWidth
#define SW_TYPE				0x8c00
#define SW_DATA				0x03ff

#define PCT_0				0x000
#define PCT_5				0x033
#define PCT_10				0x066
#define PCT_15				0x099
#define PCT_20				0x0cc
#define PCT_25				0x100
#define PCT_30				0x133
#define PCT_35				0x166
#define PCT_40				0x199
#define PCT_45				0x1cc
#define PCT_50				0x200
#define PCT_55				0x233
#define PCT_60				0x266
#define PCT_65				0x299
#define PCT_70				0x2cc
#define PCT_75				0x300
#define PCT_80				0x333
#define PCT_85				0x366
#define PCT_90				0x399
#define PCT_95				0x3cc
#define PCT_100				0x3ff

SpecWinSizePair

typedef struct {
SpecWinSizeSpec				SWSP_x;
SpecWinSizeSpec				SWSP_y;
} SpecWinSizePair;

SpecWinSizeSpec

typedef WordFlags SpecWinSizeSpec;
#define SWSS_RATIO				0x8000
#define SWSS_SIGN				0x4000
#define SWSS_MANTISSA				0x3c00
#define SWSS_FRACTION				0x03ff

SpecialFunctions

typedef enum /* word */ {
SF_FILENAME,
SF_PAGE,
SF_PAGES,
} SpecialFunctions;

SpoolError

typedef enum /* word */ {
SERROR_NO_SPOOL_FILE,
SERROR_NO_PRINT_DRIVER,
SERROR_NO_PORT_DRIVER,
SERROR_NO_PRINTERS,
SERROR_NO_MODE_AVAIL,
SERROR_CANT_ALLOC_BITMAP,
SERROR_NO_VIDMEM_DRIVER,
SERROR_MANUAL_PAPER_FEED,
SERROR_CANT_LOAD_PORT_DRIVER,
SERROR_PORT_BUSY,
SERROR_TEST_NO_PAPER,
SERROR_TEST_OFFLINE,
SERROR_TEST_PARALLEL_ERROR,
SERROR_MISSING_COM_PORT,
SERROR_PRINT_ON_STARTUP
} SpoolError;

SpoolFileName

typedef struct {
char	SFN_base[5];
char	SFN_num[3];
char	SFN_ext[5];
} SpoolFileName;

SpoolInfoType

typedef enum /* word */ {
SIT_JOB_INFO,
SIT_QUEUE_INFO
} SpoolInfoType;

SpoolOpStatus

typedef enum /* word */ {
SPOOL_OPERATION_SUCCESSFUL,
SPOOL_JOB_NOT_FOUND,
SPOOL_QUEUE_EMPTY,
SPOOL_QUEUE_NOT_EMPTY,
SPOOL_QUEUE_NOT_FOUND,
SPOOL_CANT_VERIFY_PORT,
SPOOL_OPERATION_FAILED
} SpoolOpStatus;

SpoolTimeStruct

typedef struct {
byte	STS_second;			/* second of the minute (0-59) */
byte	STS_minute;			/* minute of the hour (0-59) */
byte	STS_hour;			/* hour of the day (0-23) */
} SpoolTimeStruct;

SpoolVerifyAction

typedef enum {
SVA_NO_MESSAGE,
SVA_WARNING,
SVA_PRINTING
} SpoolVerifyAction;

StandardDialogBoxType

typedef enum {
SDBT_FILE_NEW_CANNOT_CREATE_TEMP_NAME,
SDBT_FILE_NEW_INSUFFICIENT_DISK_SPACE,
SDBT_FILE_NEW_ERROR,
SDBT_FILE_NEW_WRITE_PROTECTED,
SDBT_FILE_OPEN_SHARING_DENIED,
SDBT_FILE_OPEN_FILE_NOT_FOUND,
SDBT_FILE_OPEN_INVALID_VM_FILE,
SDBT_FILE_OPEN_INSUFFICIENT_DISK_SPACE,
SDBT_FILE_OPEN_ERROR,
SDBT_FILE_OPEN_READ_ONLY,
SDBT_FILE_OPEN_VM_DIRTY,
SDBT_FILE_OPEN_APP_MORE_RECENT_THAN_DOC,
SDBT_FILE_OPEN_DOC_MORE_RECENT_THAN_APP,
SDBT_FILE_SAVE_INSUFFICIENT_DISK_SPACE,
SDBT_FILE_SAVE_ERROR,
SDBT_FILE_SAVE_WRITE_PROTECTED,
SDBT_FILE_SAVE_AS_FILE_EXISTS,
SDBT_FILE_SAVE_AS_SHARING_DENIED,
SDBT_FILE_CLOSE_SAVE_CHANGES,
SDBT_FILE_CLOSE_ATTACH_DIRTY,
SDBT_FILE_REVERT_CONFIRM,
SDBT_FILE_REVERT_ERROR,
SDBT_FILE_ATTACH_DISK_NOT_FOUND,
SDBT_CANNOT_OPEN_VOLUME_SELECTED,
SDBT_QUERY_SAVE_AS_TEMPLATE,
SDBT_QUERY_SAVE_AS_EMPTY,
SDBT_QUERY_SAVE_AS_DEFAULT,
SDBT_QUERY_SAVE_AS_MULTI_USER,
SDBT_QUERY_SAVE_AS_PUBLIC,
SDBT_QUERY_RESET_EMPTY_FILE,
SDBT_QUERY_RESET_DEFAULT_FILE,
SDBT_CANNOT_OPEN_EMPTY_FILE
} StandardDialogBoxType;

StandardDialogParams

typedef struct {
word							 SDP_customFlags;
char							*SDP_customString;
char							*SDP_stringArg1;
char							*SDP_stringArg2;
StandardDialogResponseTriggerTable							*SDP_customTriggers;
} StandardDialogParams;

StandardDialogOptrParams

typedef struct {
word	SDOP_customFlags;
optr	SDOP_customString;
optr	SDOP_stringArg1;
optr	SDOP_stringArg2;
optr	SDOP_customTriggers;
} StandardDialogOptrParams;

StandardDialogResponseTriggerEntry

typedef struct {
optr	SDRTE_moniker;
word	SDRTE_responseValue;
} StandardDialogResponseTriggerEntry;

StandardDialog1ResponseTriggerTable

typedef struct {
 word							SD1RTT_numTriggers;
 StandardDialogResponseTriggerEntry							SD1RTT_trigger1;
} StandardDialog1ResponseTriggerTable;

StandardDialog2ResponseTriggerTable

typedef struct {
 word							SD2RTT_numTriggers;
 StandardDialogResponseTriggerEntry							SD2RTT_trigger1;
 StandardDialogResponseTriggerEntry							SD2RTT_trigger2;
} StandardDialog2ResponseTriggerTable;

StandardDialog3ResponseTriggerTable

typedef struct {
 word							SD3RTT_numTriggers;
 StandardDialogResponseTriggerEntry							SD3RTT_trigger1;
 StandardDialogResponseTriggerEntry							SD3RTT_trigger2;
 StandardDialogResponseTriggerEntry							SD3RTT_trigger3;
} StandardDialog3ResponseTriggerTable;

StandardDialog4ResponseTriggerTable

typedef struct {
 word								SD4RTT_numTriggers;
 StandardDialogResponseTriggerEntry 								SD4RTT_trigger1;
 StandardDialogResponseTriggerEntry 								SD4RTT_trigger2;
 StandardDialogResponseTriggerEntry 								SD4RTT_trigger3;
 StandardDialogResponseTriggerEntry 								SD4RTT_trigger4;
} StandardDialog4ResponseTriggerTable;

StandardPath

typedef enum /* word */ {
SP_NOT_STANDARD_PATH=0,
SP_TOP=1,
SP_APPLICATION=3,
SP_DOCUMENT=5,
SP_SYSTEM=7,
SP_PRIVATE_DATA=9,
SP_STATE=11,
SP_FONT=13,
SP_SPOOL=15,
SP_SYS_APPLICATION=17,
SP_PUBLIC_DATA=19,
SP_MOUSE_DRIVERS=21,
SP_PRINTER_DRIVERS=23,
SP_FILE_SYSTEM_DRIVERS=25,
SP_VIDEO_DRIVERS=27,
SP_SWAP_DRIVERS=29,
SP_KEYBOARD_DRIVERS=31,
SP_FONT_DRIVERS=33,
SP_IMPORT_EXPORT_DRIVERS=35,
SP_TASK_SWITCH_DRIVERS=37,
SP_HELP_FILES=39,
SP_TEMPLATE=41,
SP_POWER_DRIVERS=43,
SP_DOS_ROOM=45,
SP_HWR=47,
SP_WASTE_BASKET=49,
SP_BACKUP=51,
SP_PAGER_DRIVERS=53
SP_DUMMY=256
} StandardPath;

Most routines which are passed disk handles can also be passed members of the StandardPath enumerated type. Standard paths let applications access files in a disk-independent manner. Standard paths are usually arranged in a certain hierarchy; for example, the STATE directory usually belongs to the PRIVDATA directory. However, this is entirely at the user's discretion; applications may not make any assumption about how the standard paths are arranged.

StandardSoundType

See UserStandardSound() .

StreamBlocker

typedef	enum{
    STREAM_BLOCK			= 2,
    STREAM_NO_BLOCK			= 0
} StreamBlocker;

StreamError

typedef	enum{
    STREAM_WOULD_BLOCK,
    STREAM_CLOSING,
    STREAM_CANNOT_ALLOC,
    STREAM_BUFFER_TOO_LARGE,
    STREAM_CLOSED,
    STREAM_SHORT_READ_WRITE
} StreamError;

StreamOpenFlags

typedef	enum {
    STREAM_OPEN_NO_BLOCK				= 0x01,
    STREAM_OPEN_TIMEOUT				= 0x02
} StreamOpenFlags

StreamToken

typedef Handleword StreamToken;

StreamRoles

typedef	enum {
    STREAM_ROLES_WRITER				= 0,
    STREAM_ROLES_READER				= -1,
    STREAM_ROLES_BOTH				= -2
} StreamRoles;

StyleChunkDesc

typedef struct {
VMFileHandle			SCD_vmFile;
word			SCD_vmBlockOrMemHandle;
ChunkHandle			SCD_chunk;
} StyleChunkDesc;

StyleElementFlags

typedef WordFlags StyleElementFlags;
#define SEF_DISPLAY_IN_TOOLBOX						0x8000

StyleElementHeader

typedef struct {
NameArrayElement				SEH_meta;
word				SEH_baseStyle;
StyleElementFlags				SEH_flags;
dword				SEH_privateData;
} StyleElementHeader;

StyleSheetElementHeader

typedef struct {
 RefElementHeader				SSEH_meta;
 word				SSEH_style;
} StyleSheetElementHeader;

SupportedEnvelopeFormat

typedef enum {
 SEF_NO_FORMAT,
 SEF_SBI_FORMAT,
 SEF_CTI_FORMAT
} SupportedEnvelopeFormat;

These values specify how a sound device can simulate musical instruments, if it can at all.

sword

typedef signed short sword;

SysConfigFlags

typedef ByteFlags SysConfigFlags;
#define SCF_UNDER_SWAT				0x80
#define SCF_2ND_IC 				0x40
#define SCF_RTC 				0x20
#define SCF_COPROC 				0x10
#define SCF_RESTARTED 				0x08
#define SCF_CRASHED 				0x04
#define SCF_MCA 				0x02
#define SCF_LOGGING				0x01

The above flags indicate the system configuration. Any or all of these flags may be set at a time; if a flag is set, the description is true. These flags are used by the kernel and can be retrieved with SysGetConfig() .

SysDrawMask

typedef ByteFlags SysDrawMask;
#define SDM_INVERSE				0x80
#define SDM_MASK				0x7f

SysGetInfoType

See: SysGetInfo() .

SysMachineType

typedef ByteEnum SysMachineType;
#define SMT_UNKNOWN				0
#define SMT_PC 				1
#define SMT_PC_CONV 				2
#define SMT_PC_JR 				3
#define SMT_PC_XT 				4
#define SMT_PC_XT_286 				5
#define SMT_PC_AT 				6
#define SMT_PS2_30 				7
#define SMT_PS2_50 				8
#define SMT_PS2_60 				9
#define SMT_PS2_80 				10
#define SMT_PS1 				11

A byte-sized value indicating the type of machine running GEOS. This value can be retrieved with SysGetConfig() .

SysNotifyFlags

See: SysNotify() .

SysProcessorType

typedef ByteEnum SysProcessorType;
#define SPT_8088				0
#define SPT_8086 				0
#define SPT_80186 				1
#define SPT_80286 				2
#define SPT_80386 				3
#define SPT_80486 				4

This enumerated type is a byte that indicates the type of processor on the system running GEOS. It can be retrieved with SysGetConfig() .

SysShutdownType

See: SysShutdown() .

SysStats

typedef struct {
dword 		SS_idleCount;				/* Idle ticks in the last second. */
SysSwapInfo	 	SS_swapOuts;				/* Outward-bound swap activity. */
SysSwapInfo 		SS_swapIns;				/* Inward-bound swap actividy. */
word 		SS_contextSwitches;				/* Context switches in last second. */
word 		SS_interrupts;				/* Interrupts in the last second. */
word 		SS_runQueue;				/* Runnable threads at end of
                         * last second. */
} SysStats;

This structure is returned by SysStatistics() and represents the current performance statistics of GEOS.

SysSwapInfo

typedef struct {
word 	SSI_paragraphs;			/* Number of paragraphs swapped. */
word 	SSI_blocks;			/* Number of blocks swapped. */
} SysSwapInfo;

Structure used to represent current swap activity in SysStats structure.

SystemDrawMask

typedef ByteEnum SystemDrawMask;
#define SDM_TILE					0
#define SDM_SHADED_BAR 					1
#define SDM_HORIZONTAL 					2
#define SDM_VERTICAL 					3
#define SDM_DIAG_NE 					4
#define SDM_DIAG_NW 					5
#define SDM_GRID 					6
#define SDM_BIG_GRID 					7
#define SDM_BRICK 					8
#define SDM_SLANT_BRICK 					9
#define SDM_0 					89
#define SDM_12_5 					81
#define SDM_25 					73
#define SDM_37_5 					65
#define SDM_50 					57
#define SDM_62_5 					49
#define SDM_75 					41
#define SDM_87_5 					33
#define SDM_100 					25
#define SDM_CUSTOM 					0x7f
#define SET_CUSTOM_PATTERN 					SDM_CUSTOM

SystemHatch

typedef ByteEnum SystemHatch;
#define SH_VERTICAL				0
#define SH_HORIZONTAL 				1
#define SH_45_DEGREE 				2
#define SH_135_DEGREE 				3
#define SH_BRICK 				4
#define SH_SLANTED_BRICK 				5

TargetLevel

typedef enum /* word */ {
TL_TARGET				= 0,
TL_CONTENT,
TL_GENERIC_OBJECTS				= 1000,
TL_GEN_SYSTEM,
TL_GEN_FIELD,
TL_GEN_APPLICATION,
TL_GEN_PRIMARY,
TL_GEN_DISPLAY_CTRL,
TL_GEN_DISPLAY,
TL_GEN_VIEW,
TL_LIBRARY_LEVELS				= 2000,
TL_APPLICATION_OBJECTS				= 3000,
} TargetLevel;

TcpAccPntExtendedAddress

typedef struct {
        word    TAPEA_linkSize;             /* 3 */
        byte    TAPEA_linkType;             /* LinkType (LT_ID) */
        word    TAPEA_accPntID;
} TcpAccPntExtendedAddress;

This is the "opaque" representation of a TCP address; specifically, the form used to identify a TCP address by means of its access point ID.

Set TAPEA_linkSize to 3; set TAPEA_linkType to LT_ID; set TAPEA_accPntID to the access point ID number.

TcpNonAccPntExtendedAddress

typedef struct {
   word    TNAPEA_linkSize;
   byte    TNAPEA_linkType;            /* LinkType (LT_ADDR) */
/* label byte TNAPEA_addr;     link address immed. followed by IP address */
} TcpNonAccPntExtendedAddress;

This is the "opaque" representation of a TCP address; specifically, the form used to identify a TCP address which is not a known access point--you will need to specify a phone number to dial in addition to an IP address.

Set TNAPEA_linkSize to the size of the phone number string. Set TNAPEA_linkType to LT_ADDR. Follow the structure witha null-terminated string consisting of the phone number to dial followed by the human-readable form of the IP address; for example "15105551234acme.com".

TcpOnlyExtendedAddress

typedef struct {
            word    TOEA_linkSize;              /* 0 */
        /*  label byte TOEA_ipAddr;             IP address  */
} TcpOnlyExtendedAddress;

This is the "opaque" representation of a TCP address; specifically, the form used to identify a TCP address when a link address is not required.

Set TOEA_linkSize to 0; follow the structure with the IP address, expressed either as a null-terminated string; for example "204.156.128.1" or "acme.com".

TestRectReturnType

See GrTestRectInReg() .

TextAttr

typedef struct {
byte 			TA_colorFlag;
RGBValue 			TA_color;
SysDrawMask 			TA_mask;
GraphicPattern 			TA_pattern;
TextStyle 			TA_styleSet;
TextStyle 			TA_styleClear;
TextMode 			TA_modeSet;
TextMode 			TA_modeClear;
WBFixed 			TA_spacePad;
FontID			TA_font;
WBFixed 			TA_size;
sword 			TA_trackKern;
} TextAttr;

TextMode

typedef ByteFlags TextMode;
#define TM_TRACK_KERN						0x40
#define TM_PAIR_KERN 						0x20
#define TM_PAD_SPACES 						0x10
#define TM_DRAW_BASE 						0x08
#define TM_DRAW_BOTTOM 						0x04
#define TM_DRAW_ACCENT 						0x02
#define TM_DRAW_OPTIONAL_HYPHENS 						0x01

TextReference

typedef struct {
TR_type		TextReferenceType;
TR_ref		TextReferenceUnion;
} TextReference;

TextReferenceBlock

typedef struct {
MemHandle		TRB_handle;
} TextReferenceBlock;

This structure corresponds to a TextReferenceType of TRT_BLOCK. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRB_ handle stores the handle of the text buffer. No entries need to be filled in to allocate a destination buffer. The heap allocation request will be made with the HAF_NO_ERR flag. If VTGRF_RESIZE is passed then either the passed block or the allocated block will be resized to accommodate the text.

TextReferenceBlockChunk

typedef struct {
optr		TRBC_ref;
} TextReferenceBlockChunk;

This structure corresponds to a TextReferenceType of TRT_OPTR. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRBC_ ref stores the optr to a text buffer (a group of character)s. The handle field of TRBC _ref must be filled in.

It is assumed that the LMem heap will be able to accommodate this allocation. The caller is responsible for ensuring that this is the case.

If VTGRF_RESIZE is passed then either the passed block or the allocated block will be resized to accommodate the text.

TextReferenceDBItem

typedef struct {
FileHandle 		TRDBI_file;
DBItem		TRDBI_item;
DBGroup		TRDBI_group;
} TextReferenceDBItem;

This structure corresponds to a TextReferenceType of TRT_DB_ITEM. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRDBI__ file stores the VM file associated with this DB item.

TRDBI_ item stores the DB item itself.

TRDBI_ group stores the DB group the item belongs to.

Both TRDBI_ file and TRDBI_ group must be filled in if you want a VM block to be allocated.

If the TRDBI_ group field is set to DB_UNGROUPED then the item will be allocated ungrouped. TRDBI_group will hold the group in which the item was allocated on return.

If VTGRF_RESIZE is passed then either the passed block or the allocated block will be resized to accommodate the text.

TextReferenceHugeArray

typedef struct {
FileHandle		TRHA_file;
word		TRHA_array;
} TextReferenceHugeArray;

This structure corresponds to a TextReferenceType of TRT_HUGE_ARRAY. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRHA _file stores the VM file associated with this huge array.

TRHA_ array stores the Huge Array.

The TRHA_ file field must be set if you want a huge-array to be allocated.

If VTGRF_RESIZE is passed then either the passed block or the allocated block will be resized to accommodate the text.

TextReferencePointer

typedef struct {
char _far 		*TRP_pointer;
} TextReferencePointer;

This structure corresponds to a TextReferenceType of TRT_POINTER. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRP _pointer stores the pointer to the text. This field must be filled in.

VTGRF_RESIZE has no meaning with this sort of reference. VTGRF_ALLOCATE and VTGRF_ALLOCATE_ALWAYS are not valid flags to pass with this type of text reference.

This reference is the safest way to copy text out of a text object. Since the caller allocates the block it can also handle errors in the allocation.

TextReferenceSegmentChunk

typedef struct {
ChunkHandle			TRSC_chunk;
Segment			TRSC_segment;
} TextReferenceSegmentChunk;

This structure corresponds to a TextReferenceType of TRT_SEGMENT_CHUNK. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRSC_ segment stores the segment address of the text chunk. TRSC _chunk stores the chunk offset to the text.

It is assumed that the LMem heap will be able to accommodate this allocation. The caller is responsible for ensuring that this is the case.

If VTGRF_RESIZE is passed then either the passed block or the allocated block will be resized to accommodate the text.

TextReferenceType

typedef enum {
TRT_POINTER=0,				/* TextReferencePointer */
TRT_SEGMENT_CHUNK=2,				/* TextReferenceSegmentChunk */
TRT_OPTR=4,				/* TextReferenceBlockChunk */
TRT_BLOCK=6,				/* TextReferenceBlock */
TRT_VM_BLOCK=8,				/* TextReferenceVMBlock */
TRT_DB_ITEM=10,		/		* TextReferenceDBItem */
TRT_HUGE_ARRAY=12,				/* TextReferenceHugeArray */
} TextReferenceType;

TextReferenceUnion

typedef union {
TextReferencePointer 					TRU_pointer;
TextReferenceSegmentChunk				 	TRU_segChunk;
TextReferenceBlockChunk 					TRU_blockChunk;
TextReferenceBlock	 				TRU_block;
TextReferenceVMBlock					TRU_vmBlock;
TextReferenceDBItem	 				TRU_dbItem;
TextReferenceHugeArray					TRU_hugeArray;
} TextReferenceUnion;

TextReferenceVMBlock

typedef struct {
FileHandle 			TRVMB_file;
VMBlockHandle 			TRVMB_block;
} TextReferenceVMBlock;

This structure corresponds to a TextReferenceType of TRT_VM_BLOCK. It is used with MSG_VIS_TEXT_REPLACE_TEXT and MSG_VIS_TEXT_GET_TEXT_RANGE to reference text used by those messages.

TRVMB _file stores the VM file associated with this VM block. This entry must be filled in if you want a VM block to be allocated.

TRVMB _block stores the VM block itself.

If VTGRF_RESIZE is passed then either the passed block or the allocated block will be resized to accommodate the text.

TextStyle

typedef ByteFlags TextStyle;
#define TS_OUTLINE				0x40
#define TS_BOLD 				0x20
#define TS_ITALIC 				0x10
#define TS_SUPERSCRIPT 				0x08
#define TS_SUBSCRIPT 				0x04
#define TS_STRIKE_THRU 				0x02
#define TS_UNDERLINE 				0x01

ThreadException

typedef enum {
TE_DIVIDE_BY_ZERO=0,
TE_OVERFLOW=4,
TE_BOUND=8,
TE_FPU_EXCEPTION=12,
TE_SINGLE_STEP=16,
TE_BREAKPOINT=20
} ThreadException;

Processor exceptions used primarily for debugging, these are used with ThreadHandleException() .

ThreadGetInfoType

See ThreadGetInfo() .

ThreadHandle

typedef Handle ThreadHandle;

ThreadLockHandle

typedef Handle ThreadLockHandle;

, these flags determine what aspect of the thread

ThreadModifyFlags

typedef ByteFlags ThreadModifyFlags;
#define TMF_BASE_PRIO				0x80
#define TMF_ZERO_USAGE				0x40

Used with ThreadModify() is modified.

TimerCompressedDate

typedef WordFlags TimerCompressedDate;
#define TCD_YEAR 				0xfe00			/* years since 1980; e.g. 1988 is `8' */
#define TCD_MONTH				0x01e0			/* months (1 - 12) (0 illegal) */
#define TCD_DAY				0x001f			/* days (1-31) (0 illegal) */

TimerDateAndTime

typedef struct {
word			TDAT_year;			/* Year */
word			TDAT_month;			/* Number of month (1 through 12) */
word			TDAT_day;			/* Number of day in month (1 through 31) */
DaysOfTheWeek			TDAT_dayOfWeek;			/* DayOfTheWeek enumeration */
word			TDAT_hours;			/* Hour of the day (0 through 23) */
word			TDAT_minutes;			/* Minute in the hour (0 through 59) */
word			TDAT_seconds;			/* Second in the minute (0 through 59) */
} TimerDateAndTime;

This structure is used to keep track of the current time and date.

TimerHandle

typedef Handle TimerHandle;

TimerType

See: TimerStart() .

ToggleState

typedef ByteFlags ToggleState;
#define TS_CAPSLOCK				0x80
#define TS_NUMLOCK				0x40
#define TS_SCROLLLOCK				0x20

This structure describes the state of certain "toggles" which will affect how input is interpreted. These toggles correspond to the caps lock, num lock, and scroll lock keys.

TokenChars

typedef char TokenChars[TOKEN_CHARS_LENGTH]; /* TOKEN_CHARS_LENGTH=4 */

TokenDBItem

typedef DBGroupAndItem TokenDBItem;

TokenEntry

typedef struct {
GeodeToken			TE_token;			/* A GeodeToken structure for this file */
TokenDBItem			TE_monikerList;			/* A list of monikers for this token */
TokenFlags			TE_flags;			/* Flags indicating relocation status */
ReleaseNumber			TE_release;			/* Release number of the token DB */
ProtocolNumber			TE_protocol;			/* Protocol number of the toke DB */
} TokenEntry;

Used for the token entry in the map item of the token database, this structure identifies the structures and other information of each token. The TE_monikerList field points to a chunk containing the item numbers of the chunks of the token.

TokenFlags

typedef WordFlags TokenFlags;
#define TF_NEED_RELOCATION				0x8000

Used by token management routines, this flags record indicates whether the token has fields which must be relocated when the token is loaded or unloaded.

TokenMonikerInfo

typedef struct {
TokenDBItem 		TMI_moniker;
word 		TMI_fileFlag;			/* 0 if token is in shared token DB file;
                     * Non-0 if it's in local file */
} TokenMonikerInfo;

TokenRangeFlags

typedef WordFlags TokenRangeFlags;
#define TRF_ONLY_GSTRING	0x8000
#define TRF_ONLY_PASSED_MANUFID	0x4000
#define TRF_UNUSED		 0x3fff

TransError

typedef enum {
 TE_NO_ERROR, /* No error */
 TE_ERROR, /* General error */
 TE_INVALID_FORMAT, /* Format is invalid */
 TE_IMPORT_NOT_SUPPORTED, /* Format is not supported for export */
 TE_EXPORT_NOT_SUPPORTED, /* Format is not supported for export */
 TE_IMPORT_ERROR, /* General error during import */
 TE_EXPORT_ERROR, /* General error during export */
 TE_FILE_ERROR, /* Generic file error */
 TE_DISK_FULL, /* The disk is full */
 TE_FILE_OPEN, /* Error in opening a file */
 TE_FILE_READ, /* Error in reading from a file */
 TE_FILE_WRITE, /* Error in writing to a file */
 TE_FILE_TOO_LARGE, /* File is too large to process */
 TE_OUT_OF_MEMORY, /* Insufficient memory for import/export */
 TE_METAFILE_CREATION_ERROR, /* Error in creating the metafile */
 TE_EXPORT_FILE_EMPTY, /* File to be exported is empty */
 TE_CUSTOM /* Custom error message */
} TransError;

This enumerated type contains error values the impex library may wish to generate when translating.

TransErrorInfo

typedef struct {
 TransError transError;
 /* NOTE: customMsgHandle will be valid only if transError is TE_CUSTOM. */
word customMsgHandle;
} TransErrorInfo;

TransferBlockID

typedef dword TransferBlockID;
#define BlockIDFromFileAndBlock(f,b) 								(((dword)(f) << 16) | (b))
#define FileFromTransferBlockID(id) 								((VMFileHandle) ((id) >> 16))
#define BlockFromTransferBlockID(id) 								((VMBlockHandle) (id))

TransMatrix

typedef struct {
WWFixed 		TM_e11;
WWFixed 		TM_e12;
WWFixed 		TM_e21;
WWFixed 		TM_e22;
DWFixed 		TM_e31;
DWFixed 		TM_e32;
} TransMatrix;

The six variable elements of a coordinate transformation matrix.

TravelOption

typedef enum {
 TO_NULL,
 TO_SELF,
 TO_OBJ_BLOCK_OUTPUT,
 TO_PROCESS
} TravelOption;
/* VisClass defines one other travel option: */
typedef enum {
 TO_VIS_PARENT=_FIRST_VisClass
} VisTravelOption;
/* GenClass defines some more travel options: */
typedef enum /* word */ {
 TO_GEN_PARENT=_FIRST_GenClass,
 TO_FOCUS,
 TO_TARGET,
 TO_MODEL,
 TO_APP_FOCUS,
 TO_APP_TARGET,
 TO_APP_MODEL,
 TO_SYS_FOCUS,
 TO_SYS_TARGET,
 TO_SYS_MODEL
} GenTravelOption;

This enumerated type can be used to specify the recipient of a message. Note that the values set up in the TravelOption , VisTravelOption , and GenTravelOption have been set up as descrete values.

TRUE

#define TRUE		-1	/* use as return value, not for comparisons */
#define FALSE		 0

TypeFromFormatID

word	TypeFromFormatID(type);
        ClipboardItemFormatID			type;

This macro extracts the format ID from the given clipboard format ID and manufacturer value.

UIFunctionsActive

typedef ByteFlags UIFunctionsActive;
#define UIFA_SELECT				0x80
#define UIFA_MOVE_COPY 				0x40
#define UIFA_FEATURES 				0x20
#define UIFA_CONSTRAIN 				0x10
#define UIFA_PREF_A 				0x08
#define UIFA_PREF_B 				0x04
#define UIFA_PREF_C 				0x02
#define UIFA_IN 				0x01
#define UIFA_ADJUST 				0x08
#define UIFA_EXTEND 				0x04
#define UIFA_MOVE 				0x08
#define UIFA_COPY 				0x04
#define UIFA_POPUP 				0x08
#define UIFA_PAN 				0x04

These flags describe the context of the user's input, providing some modal information.

UIInterfaceLevel

See UserGetInterfaceLevel() .

UndoActionDataFlags

typedef struct {
dword 		UADF_flags;
word 		UADF_extraflags;
} UndoActionDataFlags;

UndoActionDataPtr

typedef struct {
void 		*UADP_ptr;
word 		UADP_size;
} UndoActionDataPtr;

UndoActionDataType

typedef enum /* word */ {
UADT_FLAGS,
UADT_PTR,
UADT_VM_CHAIN,
} UndoActionDataType;

UndoActionDataUnion

typedef union {
/* To find out the type of data stored in this
 * union, check the value of the UndoActionStruct's
 * UAS_dataType field. */
UndoActionDataFlags 				UADU_flags;
UndoActionDataPtr 				UADU_ptr;
UndoActionDataVMChain 				UADU_vmChain;
} UndoActionDataUnion;
#define NULL_UNDO_CONTEXT 0

UndoActionDataVMChain

typedef struct {
/* This structure is filled in by the code for
 * MSG_META_UNDO. VMChains passed to
 * MSG_GEN_PROCESS_UNDO_ADD_ACTION should lie in the undo
 * file (which can be obtained by sending
 * MSG_GEN_PROCESS_UNDO_GET_FILE). */
VMChain 			UADVMC_vmChain;
VMFileHandle 			UADVMC_file;
} UndoActionDataVMChain;

UndoActionStruct

typedef struct {
UndoActionDataType 				UAS_dataType;
UndoActionDataUnion 				UAS_data;
dword 				UAS_appType;
} UndoActionStruct;

UtilAsciiToHexError

See UtilAsciiToHex32() .

UtilHexToAsciiFlags

typedef WordFlags UtilHexToAsciiFlags;
#define UHTAF_INCLUDE_LEADING_ZEROS							0x0002
#define UHTAF_NULL_TERMINATE							0x0001

VarDataCHandler

typedef	struct {
word	VDCH_dataType;
void	(*VDCH_handler) (MemHandle mh, ChunkHandle ch,
                VarDataEntry *extraData,
                word dataType, void *handlerData);
} VarDataCHandler;

See ObjVarScanData() .

VarDataEntry

typedef struct {
word	VDE_dataType;			/* vardata data type */
word	VDE_entrySize;			/* size of extra data; this field only exists
                 * if the type has extra data. */
} VarDataEntry;
#define VDE_extraData				sizeof(VarDataEntry);

Structure of a variable data entry. If the data type has no extra data, there will be no VDE_entrySize field. The extra data begins at offset VDE_extraData , defined above.

VarDataFlags

typedef WordFlags VarDataFlags;
#define VDF_TYPE				0xfffc				/* 14-bit data type */
#define VDF_EXTRA_DATA				0x0002				/* set if has extra data */
#define VDF_SAVE_TO_STATE				0x0001				/* set if type saved to state */

This is a word record containing three fields. This word is stored in the vardata structure's VDE_dataType field (see VarDataEntry , above).

VarDataKey

typedef word VardataKey;

VarObjRelocation

typedef struct {
VarDataFlags			VOR_type;			/* type and tag */
word			VOR_offset;
} VarObjRelocation;

VChar

typedef ByteEnum VChar;
#define VC_NULL				0x0 /* NULL */
#define VC_CTRL_A 				0x1 /* <ctrl>-A */
#define VC_CTRL_B 				0x2 /* <ctrl>-B */
#define VC_CTRL_C 				0x3 /* <ctrl>-C */
#define VC_CTRL_D 				0x4 /* <ctrl>-D */
#define VC_CTRL_E 				0x5 /* <ctrl>-E */
#define VC_CTRL_F 				0x6 /* <ctrl>-F */
#define VC_CTRL_G 				0x7 /* <ctrl>-G */
#define VC_CTRL_H 				0x8 /* <ctrl>-H */
#define VC_CTRL_I 				0x9 /* <ctrl>-I */
#define VC_CTRL_J 				0xa /* <ctrl>-J */
#define VC_CTRL_K 				0xb /* <ctrl>-K */
#define VC_CTRL_L 				0xc /* <ctrl>-L */
#define VC_CTRL_M 				0xd /* <ctrl>-M */
#define VC_CTRL_N 				0xe /* <ctrl>-N */
#define VC_CTRL_O 				0xf /* <ctrl>-O */
#define VC_CTRL_P 				0x10 /* <ctrl>-P */
#define VC_CTRL_Q 				0x11 /* <ctrl>-Q */
#define VC_CTRL_R				0x12 /* <ctrl>-R */
#define VC_CTRL_S 				0x13 /* <ctrl>-S */
#define VC_CTRL_T 				0x14 /* <ctrl>-T */
#define VC_CTRL_U 				0x15 /* <ctrl>-U */
#define VC_CTRL_V 				0x16 /* <ctrl>-V */
#define VC_CTRL_W 				0x17 /* <ctrl>-W */
#define VC_CTRL_X 				0x18 /* <ctrl>-X */
#define VC_CTRL_Y 				0x19 /* <ctrl>-Y */
#define VC_CTRL_Z 				0x1a /* <ctrl>-Z */
#define VC_ESCAPE 				0x1b /* ESC */
#define VC_BLANK 				0x20 /* space */
/*
 * Numeric keypad keys
 */
#define VC_NUMPAD_ENTER 				0xd /* only on PS/2 keyboards */
#define VC_NUMPAD_DIV 				`/' /* only on PS/2 keyboards */
#define VC_NUMPAD_MULT 				`*'
#define VC_NUMPAD_PLUS 				`+'
#define VC_NUMPAD_MINUS 				`-'
#define VC_NUMPAD_PERIOD 				`.'
#define VC_NUMPAD_0 				`0'
#define VC_NUMPAD_1 				`1'
#define VC_NUMPAD_2 				`2'
#define VC_NUMPAD_3 				`3'
#define VC_NUMPAD_4 				`4'
#define VC_NUMPAD_5 				`5'
#define VC_NUMPAD_6 				`6'
#define VC_NUMPAD_7 				`7'
#define VC_NUMPAD_8				`8'
#define VC_NUMPAD_9				`9'
/*
 * Extended keyboard codes -- non-ASCII
 */
#define VC_F1 				0x80 /* Function keys */
#define VC_F2 				0x81
#define VC_F3 				0x82
#define VC_F4 				0x83
#define VC_F5				0x84
#define VC_F6 				0x85
#define VC_F7 				0x86
#define VC_F8 				0x87
#define VC_F9 				0x88
#define VC_F10 				0x89
#define VC_F11 				0x8a /* only on PS/2 keyboards */
#define VC_F12 				0x8b /* only on PS/2 keyboards */
#define VC_F13 				0x8c /* non-standard key */
#define VC_F14 				0x8d /* non-standard key */
#define VC_F15 				0x8e /* non-standard key */
#define VC_F16 				0x8f /* non-standard key */
#define VC_UP 				0x90 /* Cursor keys */
#define VC_DOWN 				0x91
#define VC_RIGHT 				0x92
#define VC_LEFT 				0x93
#define VC_HOME 				0x94 /* Scroll commands */
#define VC_END 				0x95
#define VC_PREVIOUS 				0x96
#define VC_NEXT 				0x97
#define VC_INS 				0x98 /* INS */
#define VC_DEL 				0x9a /* DEL */
#define VC_PRINTSCREEN				0x9b /* from <shift>-NUMPAD_MULT */
#define VC_PAUSE 				0x9c /* from <ctrl>-NUMLOCK */
#define VC_BREAK 				0x9e /* from <ctrl>- or <alt>-combo */
#define VC_SYSTEMRESET 				0x9f /* <ctrl>-<alt>-<del> combo */
/*
* Joystick control keys (0xa0 - 0xa9)
*/
#define VC_JOYSTICK_0 				0xa0        ; joystick 0 degrees
#define VC_JOYSTICK_45 	 			0xa1        ; joystick 45 degrees
#define VC_JOYSTICK_90 				0xa2        ; joystick 90 degrees
#define VC_JOYSTICK_135 				0xa3        ; joystick 135 degrees
#define VC_JOYSTICK_180 				0xa4        ; joystick 180 degrees
#define VC_JOYSTICK_225 				0xa5        ; joystick 225 degrees
#define VC_JOYSTICK_270 				0xa6        ; joystick 270 degrees
#define VC_JOYSTICK_315 				0xa7        ; joystick 315 degrees
#define VC_FIRE_BUTTON_1				0xa8        ; fire button #1
#define VC_FIRE_BUTTON_2 				0xa9        ; fire button #2

/*
 * Shift Keys			(0xe0 - 0xe7)
 */
#define VC_LALT 				0xe0
#define VC_RALT 				0xe1
#define VC_LCTRL 				0xe2
#define VC_RCTRL 				0xe3
#define VC_LSHIFT 				0xe4
#define VC_RSHIFT 				0xe5
#define VC_SYSREQ 				0xe6 /* Not on base PC keyboard */
#define VC_ALT_GR 				0xe7
/*
 * Toggle state keys (0xe8 - 0xef)
 */
#define VC_CAPSLOCK 				0xe8
#define VC_NUMLOCK 				0xe9
#define VC_SCROLLLOCK 				0xea
/*
 * Extended state keys (0xf0 - 0xf7)
 */
#define VC_INVALID_KEY 				0xff
#define VC_BACKSPACE 				VC_CTRL_H
#define VC_TAB 				VC_CTRL_I
#define VC_LF 				VC_CTRL_J
#define VC_ENTER 				VC_CTRL_M

VisRulerType

typedef ByteEnum VisRulerType;
#define VRT_INCHES				0
#define VRT_CENTIMETERS				1
#define VRT_POINTS				2
#define VRT_PICAS				3
#define VRT_CUSTOM				CUSTOM_RULER_DEFINITION
#define VRT_NONE				NO_RULERS
#define VRT_DEFAULT				SYSTEM_DEFAULT

VisTextVariableType

typedef enum {
 VTVT_PAGE_NUMBER,
 VTVT_PAGE_NUMBER_IN_SECTION,
 VTVT_NUMBER_OF_PAGES,
 VTVT_NUMBER_OF_PAGES_IN_SECTION,
 VTVT_SECTION_NUMBER,
 VTVT_NUMBER_OF_SECTIONS,
 VTVT_CREATION_DATE_TIME,
 VTVT_MODIFICATION_DATE_TIME,
 VTVT_CURRENT_DATE_TIME,
 VTVT_STORED_DATE_TIME,
} VisTextVariableType;

VisTravelOption

The VisClass defines an enumerated value to be used in the place of a standard TravelOption . See the entry for TravelOption to see all possible values.

VisUpdateMode

typedef ByteEnum VisUpdateMode;
#define VUM_MANUAL 							0
#define VUM_NOW 							1
#define VUM_DELAYED_VIA_UI_QUEUE 							2
#define VUM_DELAYED_VIA_APP_QUEUE							3

VMAccessFlags

typedef ByteFlags VMAccessFlags;
#define VMAF_FORCE_READ_ONLY										0x80
#define VMAF_FORCE_READ_WRITE 										0x40
#define VMAF_ALLOW_SHARED_MEMORY 										0x20
#define VMAF_FORCE_DENY_WRITE 										0x10
#define VMAF_DISALLOW_SHARED_MULTIPLE 										0x08
#define VMAF_USE_BLOCK_LEVEL_SYNCHRONIZATION 										0x04

The VMAccessFlags specify what kind of access to the file the caller wants. The following flags are available:

VMAF_FORCE_READ_ONLY
If set, the file will be opened read-only, even if the default would be to open the file read/write. Blocks in read-only files cannot be dirtied, and changes in memory blocks will not be updated to the disk VM blocks.
VMAF_FORCE_READ_WRITE
If set, the file will be opened for read/write access, even if the default would be to open the file for read-only access.
VMAF_SHARED_MEMORY
If set, the VM manager should try to use shared memory when locking VM blocks; that is, the same memory block will be used for a given VM block no matter which thread locks the block.
VMAF_FORCE_DENY_WRITE
If set, then open the file deny-write; that is, no other threads will be allowed to open the file for read/write access.
VMAF_DISALLOW_SHARED_MULTIPLE
If this flag is set, files with the file attribute GFHF_SHARED_MULTIPLE cannot be opened.
VMAF_USE_BLOCK_LEVEL_SYNCHRONIZATION
If set, the block-level synchronization mechanism of the VM manager is assumed to be sufficient; the more restrictive StartExclusive/EndExclusive mechanism is not used. This is primarily intended for system software.

VMAttributes

typedef ByteFlags VMAttributes;
#define VMA_SYNC_UPDATE								0x80
#define VMA_BACKUP 								0x40
#define VMA_OBJECT_RELOC 								0x20
#define VMA_PRESERVE_HANDLES 								0x10
#define VMA_NOTIFY_DIRTY 								0x08
#define VMA_NO_DISCARD_IF_IN_USE		 						0x04
#define VMA_COMPACT_OBJ_BLOCK 								0x02
#define VMA_SINGLE_THREAD_ACCESS		 						0x01
/*
 * Attributes that must be set for object blocks: */
#define VMA_OBJECT_ATTRS				(VMA_OBJECT_RELOC | VMA_PRESERVE_HANDLES |
                 VMA_NO_DISCARD_IF_IN_USE |
                 VMA_SINGLE_THREAD_ACCESS)

VMBlockHandle

typedef word VMBlockHandle;

VMChain

typedef dword VMChain;

VMChainLink

typedef struct {
VMBlockHandle			VMC_next;
} VMChainLink;

VMChainTree

typedef struct {
VMChainLink		VMCT_meta;
word		VMCT_offset;
word		VMCT_count;
} VMChainTree;

VMFileHandle

typedef Handle VMFileHandle;

VMInfoStruct

typedef struct {
MemHandle		mh;
word		size;
word		userId;
} VMInfoStruct;

VMOpenType

typedef ByteEnum VMOpenType;
#define VMO_OPEN							0
#define VMO_TEMP_FILE							1
#define VMO_CREATE							2
#define VMO_CREATE_ONLY							3
#define VMO_CREATE_TRUNCATE							4
#define VMO_NATIVE_WITH_EXT_ATTRS							0x80

The following types are available:

VMO_TEMP_FILE
If this is passed, the file will be a temporary data file. When you create a temporary file, you pass a directory path, not a file name. The path should be followed by fourteen null bytes, including the string's terminating null. The system will choose an appropriate file name and add it to the path string.
VMO_CREATE_ONLY
If this is passed, the document will be created. If a document with the specified name already exists in the working directory, VMOpen() will return an error condition.
VMO_CREATE
If this is passed, the file will be created if it does not already exist; otherwise it will be opened.
VMO_CREATE_TRUNCATE
If this is passed, the file will be created if it does not already exist; otherwise, it will be opened and truncated (all data blocks will be freed).
VMO_OPEN
Open existing file. If file does not exist, return an error condition.

VMOperation

typedef enum {
VMO_READ,
VMO_INTERNAL,
VMO_SAVE,
VMO_SAVE_AS,
VMO_REVERT,
VMO_UPDATE,
VMO_WRITE
} VMOperation;

VMRelocType

typedef enum {
VMRT_UNRELOCATE_BEFORE_WRITE,
VMRT_RELOCATE_AFTER_READ,
VMRT_RELOCATE_AFTER_WRITE,
VMRT_RELOCATE_FROM_RESOURCE,
VMRT_UNRELOCATE_FROM_RESOURCE,
} VMRelocType;

VMStartExclusiveReturnValue

typedef enum {
 VMSERV_NO_CHANGES,
 VMSERV_CHANGES,
 VMSERV_TIMEOUT
} VMStartExclusiveReturnValue;

VMGrabExclusive() returns a member of this enumerated type. It may have one of the following values:

VMSERV_NO_CHANGES
No other thread has changed this file since the last time this thread had access to the file.
VMSERV_CHANGES
The file may have been altered since the last time this thread had access to it; the thread should take appropriate actions (such as re-reading any cached data).
VMSERV_TIMEOUT
This call to VMGrabExclusive() failed and timed out without getting access to the file.

VolumeName

typedef char VolumeName[VOLUME_BUFFER_SIZE];

WBFixed

typedef struct {
byte WBF_frac;
word WBF_int;
} WBFixed;

wchar

typedef unsigned int wchar;

WindowHandle

typedef Handle WindowHandle;

WinInfoType

typedef enum /* word */ {
WIT_PRIVATE_DATA =0,
WIT_COLOR =2,
WIT_INPUT_OBJ =4,
WIT_EXPOSURE_OBJ =6,
WIT_STRATEGY =8,
WIT_FLAGS =10,
WIT_LAYER_ID =12,
WIT_PARENT_WIN =14,
WIT_FIRST_CHILD_WIN =16,
WIT_LAST_CHILD_WIN =18,
WIT_PREV_SIBLING_WIN =20,
WIT_NEXT_SIBLING_WIN =22,
WIT_PRIORITY=24,
} WinInfoType;

WinInvalFlag

typedef ByteEnum WinInvalFlag;
#define WIF_INVALIDATE					0
#define WIF_DONT_INVALIDATE					1

WinPassFlags

typedef WordFlags WinPassFlags;
#define WPF_CREATE_GSTATE						0x8000
#define WPF_ROOT 						0x4000
#define WPF_SAVE_UNDER 						0x2000
#define WPF_INIT_EXCLUDED 						0x1000
#define WPF_PLACE_BEHIND 						0x0800
#define WPF_PLACE_LAYER_BEHIND 						0x0400
#define WPF_LAYER 						0x0200
#define WPF_ABS 						0x0100
#define WPF_PRIORITY 						0x00ff

WinPriority

typedef ByteEnum WinPriority;
#define WIN_PRIO_POPUP					 4
#define WIN_PRIO_MODAL 					 6
#define WIN_PRIO_ON_TOP 					 8
#define WIN_PRIO_COMMAND 					10
#define WIN_PRIO_STD 					12
#define WIN_PRIO_ON_BOTTOM 					14

word

typedef unsigned int word;

WordAndAHalf

typedef struct {
word 	WAAH_low;
byte 	WAAH_high;
} WordAndAHalf;

WordFlags

typedef word WordFlags;

WWFixed

typedef struct {
word WWF_frac;
word WWF_int;
} WWFixed;

WWFixedAsDWord

typedef dword WWFixedAsDWord

XYOffset

typedef struct {
sword XYO_x;
sword XYO_y;
} XYOffset;

A graphics coordinate offset.

XYSize

typedef struct {
word XYS_width;
word XYS_height;
} XYSize;

A graphics size, in two dimensions.

XYValueAsDWord

typedef dword XYValueAsDWord;

A graphics size, in two dimensions, expressed as a DWord.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Cell Library

For some applications, it is natural to organize data in a two-dimensional array. The classic example of this is the spreadsheet, in which every entry (or cell ) can be uniquely identified by two integers: the cell's row and its column.

The cell library lets you arrange data this way. The cell library saves the cells as DB items in a VM file. It insulates the application from the actual DB mechanism, letting the application behave as if its data is stored in a two-dimensional array. The library also provides routines to sort the cells by row or by column and to apply a routine to every cell in a range of rows and/or columns.

A collection of cells arranged into rows and columns is termed a cell file . Every cell file is contained in a VM file. There is often a one-to-one correspondence between cell files and the VM files which contain them. However, this correspondence is optional. There is nothing to stop an application from maintaining several distinct cell files in a single VM file.

1 Structure and Design
2 Using the Cell Library
2.1 The CellFunctionParameters Structure
2.2 Basic Cell Array Routines
2.3 Actions on a Range of Cells

Cell Library: 1 Structure and Design

Most of the internal structure of a cell file is transparent to the geode which uses it. A geode can, for example, lock a cell with CellLock() , specifying the cell's row and column. The cell library will find the appropriate DB item and lock it, returning the locked item's address. For most operations, the geode does not need to know anything about the internal structure of the cell file. However, the internal structure does matter for some purposes. For this reason, we present a quick overview of the structure of a cell file.

A cell file can contain up to 16,512 rows, numbered from zero to 16,511. Of these 16,512 rows, the last 128 are "scratch-pad" rows. They are intended to be used for holding information or formulae that will not be displayed or associated with a specific cell. The scratch-pad rows are never shifted; if you create a cell in the first scratch-pad row, it will always stay in that row. All other rows are called "visible" rows. Visible rows can be shifted when rows are created or deleted. For example, if you insert a new row 10, all the cells in the old row 10 will now be in row 11, and so on. The first scratch-pad row will be unchanged. Be aware that the database will not delete cells from rows that are shifted off the spreadsheet. For example, if you insert a new row, the last visible row will be shifted off the spreadsheet; the references to cells in that row will be removed, but the cells themselves will stay as DB items in the file. This is not generally a problem, since few cell files will need to use the last visible rows. If you add a row that will cause cells to be shifted off, you should delete those cells first.

The first row has an index number of zero. The last visible row has an index equal to the preprocessor constant LARGEST_VISIBLE_ROW . The first scratch-pad row has an index equal to (LARGEST_VISIBLE_ROW + 1). The last scratch-pad row has an index equal to LARGEST_ROW (which equals (LARGEST_VISIBLE_ROW + 128) or 16,511). The constants are all defined in cell.h .

The basic data unit in a cell file is the cell . The cell library treats cells as opaque data structures; their internal structure is entirely up to the geode using them. Cells are stored as ungrouped DB items. This restricts cell size to the size of a DB item; that is, a cell can theoretically be as large as 64K, but in practice should not grow larger than around 8K (and ideally should be under a kilobyte in size). Remember, whenever a DB item is created or resized, pointers to all other items in the group are invalidated. Since cells are ungrouped items, whenever you create or resize a cell, you invalidate any pointers to all other ungrouped items in that VM file. In particular, you invalidate pointers to all other cells in that VM file (even if the cells belong to another cell file in the VM file).

Cells are grouped into rows . A row can have up to 256 cells, numbered from zero to 255. Within a row, cells are identified by their column index. The column index can fit into an unsigned byte variable. The cell library creates a column array for every row which contains cells. The column array contains one entry for each cell in the row. A row often contains just a few widely scattered elements. For this reason, the column array is implemented as a sparse array . Each cell in the row has an entry consisting of two parts, namely the cell's column number and its DBGroupAndItem structure. The advantage of this arrangement is that the column array need only contain entries for those cells which exist in the row (instead of maintaining entries for the blank spaces between cells). The disadvantage is that when you lock a cell, the cell manager has to make a search through the column array to find its reference; however, this is generally a small cost.

The column arrays themselves belong to row blocks . Each row block is an LMem heap stored in the VM file, and each of its column arrays is a chunk in that heap. Row blocks contain up to 32 rows. These rows are sequential; that is, any existing rows from row zero to row 31 will always belong to the same row block, and none of them will ever be in the same row block as row 32. Since the row blocks and column arrays are not kept in DB items, they can be accessed and altered without causing any locked items to move. To keep track of the row blocks, you must have a CellFunctionParameters structure for each cell file. That structure need not be kept in the VM file (although it often is); rather, you must pass the address of the structure to any cell library routine you call.

Owing to the structure of a cell file, some actions are faster than others. The essential thing to remember is that cells are grouped together in rows, which are themselves grouped together to form a cell file. This means that you can access several cells belonging to the same row faster than you could access cells belonging to different rows. Similarly, if you insert a cell, it is much more efficient to shift the rest of the row over (which involves accessing only that one row) than to shift the rest of the column down (which involves accessing every visible row). Similarly, you can access groups of cells faster if they belong to the same row block.

Cell Library: 2 Using the Cell Library

The cell library is versatile. The basic cell access routines are very simple, but more advanced utilities give you a wide range of actions. This section will explain the techniques used to set up and use a cell file, as well as the more advanced techniques available.

The CellFunctionParameters Structure

Basic Cell Array Routines

Actions on a Range of Cells

Cell Library: 2.1 Using the Cell Library: The CellFunctionParameters Structure

The cell library needs to have certain information about any cell file on which it acts; for example, it needs to know the handles of the VM file and of the row blocks. That information is kept in a CellFunctionParameters structure. The geode which uses a cell file is responsible for creating a CellFunctionParameters structure. The C definition of the structure is shown below.

Code Display 19-1 CellFunctionParameters

typedef	struct {
    CellFunctionParameterFlags
            CFP_flags;		/* Initialize this to zero. */
    VMFileHandle		CFP_file;		/* The handle of the VM file containing
                     * the cell file. Reinitialize this each
                     * time you open the file. */
    VMBlockHandle		CFP_rowBlocks[N_ROW_BLOCKS];			/* Initialize these to zero. */
} CellFunctionParameters;

In order to create a cell file, you must create a CellFunctionParameters structure. Simply allocate the space for the structure and initialize the data fields. When you call a cell library routine, lock the structure on the global heap and pass its address. Geodes will usually allocate a VM block in the same file as the cell file, and use this block to hold the CellFunctionParameters structure; this ensures that the structure will be saved along with the cell file. They may often declare this to be the map block, making it easy to locate (see the VM chapter). However, this is entirely at the programmer's discretion. All that the cell library requires is that the structure be locked or fixed in memory every time a cell library routine is called.

The CellFunctionParameters structure contains the following fields:

CFP_flags
The cell library uses this byte for miscellaneous bookkeeping. When you create the structure, initialize this field to zero. There is only one flag which you should check or change; that is the flag CFPF_DIRTY. The cell library routines set this bit whenever they change the CellFunctionParameters structure, thus indicating that the structure ought to be resaved. After you save it, you may clear this bit.
CFP_file
This field must contain the file handle of the VM file containing the cell file. A VM file can have a new file handle every time it is opened; thus, you must reinitialize this field every time you open the file.
CFP_rowBlocks
This field is an array of VM block handles, one for every existing or potential row block. If a row block exists in the cell file, its handle is recorded here. If it does not exist, a null handle is kept in the appropriate place. The length of this array is a number of words equal to the constant N_ROW_BLOCKS (defined in cell.h ). When you create a cell file, initialize all of these handles to zero; do not access or change this field thereafter.

One warning: The cell library expects the CellFunctionParameters structure to remain motionless for the duration of a call. Therefore, if you allocate it as a DB item in the same VM file as the cell file, you must not have the structure be an ungrouped item. Remember, all the cells are ungrouped DB items; allocating or resizing a cell can potentially move any or all of the ungrouped DB items in that file.

Cell Library: 2.2 Using the Cell Library: Basic Cell Array Routines

CellReplace(), CellLock(), CellLockGetRef(), CellDirty(), CellGetDBItem(), CellGetExtent()

The basic cell routines are simple to use. One argument taken by all of them is the address of the CellFunctionParameters structure. As noted, this structure must be locked or fixed in memory for the duration of a function call. You can also access cells in any of the ways you would access a DB item; for example, you can resize a cell with DBReAlloc() .

All of the routines use the VM file handle specified in the CellFunctionParameters structure.

To create, replace, or free a cell, call the routine CellReplace() . This routine takes five arguments:

  • The address of the CellFunctionParameters structure.
  • The element's row.
  • The element's column.
  • The address of the data to copy into the new cell. This must not move during the allocation; therefore, it must not be in an ungrouped DB item in the same VM file as the cell file. In particular, it must not be another cell. The data will not be changed.
  • The size of the new cell. If you pass a size of zero, the cell will be deleted if it already exists; otherwise, nothing will happen.

If the cell file already contains a cell with the specified coordinates, CellReplace() will free it. CellReplace() will then allocate a new cell and copy the specified data into it. The routine invalidates any existing pointers to ungrouped DB items in the file.

Once you have created a cell, you can lock it with CellLock() . This routine takes three arguments: the address of the CellFunctionParameters structure, the cell's row, and the cell's column. It locks the cell and returns its address (the assembly version returns the cell's segment address and chunk handle). Remember, the cell is an ungrouped DB item, so its address may change the next time another ungrouped DB item is allocated or resized, even if the cell is locked.

Like all DB items, cells can (under certain circumstances) be moved even while locked. For this reason, a special locking routine is provided, namely CellLockGetRef() . This routine is just like CellLock() except that it takes one additional argument, namely the address of an optr. CellLockGetRef writes the locked item's global memory handle and chunk handle into the optr. You can translate an optr to a cell into a pointer by calling CellDeref() ; this is another synonym for LMemDeref() , and is identical to it in all respects.

If you change a cell, you must mark it dirty to insure that it will be updated on the disk. To do this, call the routine CellDirty() . This routine takes two arguments, namely the address of the CellFunctionParameters structure and the address of the (locked) cell. The routine marks the cell's item block as dirty.

Sometimes you may need to get the DB handles for a cell. For example, you may want to use a DB utility to resize the cell; to do this, you need to know its handles. For these situations, call the routine CellGetDBItem() . The routine takes three arguments: the address of the CellFunctionParameters structure, the cell's row, and the cell's column. It returns the cell's DBGroupAndItem value. You can pass this value to any of the DB...Ungrouped() routines, or you can break this value into its component handles by calling DBGroupFromGroupAndItem() or DBItemFromGroupAndItem() .

If you want to find out the bounds of an existing cell file, call the routine CellGetExtent() . This routine takes two arguments: the address of the CellFunctionParameters , and the address of a RangeEnumParams structure. For the purposes of this routine, only one of its fields matters, namely the field REP_bounds . This field is itself a structure of type Rectangle , whose structure is shown below in Rectangle . CellGetExtent() writes the bounds of the utilized section of the cell file in the REP_bounds field. The index of the first row which contains a cell will be written in the rectangle's R_top field; the index of the last row will be written in R_bottom ; the index of the first column will be written in R_left ; and the index of the last column will be written in R_right . If the cell file contains no cells, all four fields will be set to -1.

Code Display 19-2 Rectangle

typedef	struct {
    sword		R_left;		/* Index of first column written here. */
    sword		R_top;		/* Index of first row written here. */
    sword		R_right;		/* Index of last column written here. */
    sword		R_bottom;		/* Index of last row written here. */
} Rectangle;

Cell Library: 2.3 Using the Cell Library: Actions on a Range of Cells

RangeExists(), RangeInsert(), RangeEnum(), RangeSort(), RangeInsertParams

The cell library provides a number of routines which act on a range of cells. All of these routines take the address of a CellFunctionParameters structure as an argument. Many of these routines also take the address of a special parameter structure; for example, RangeInsert() takes the address of a RangeInsertParams structure. In these cases, the structure should be in locked or fixed memory. If the routine might allocate or resize cells, the structure must not be in an ungrouped DB item.

You may want to find out if there are any cells in a specified section of the cell file. To do this, call the routine RangeExists() . This routine takes five arguments:

  • The address of the locked CellFunctionParameters structure
  • The index of the first row in the section to be checked
  • The index of the first column in the section to be checked
  • The index of the last row in the section to be checked
  • The index of the last column in the section to be checked

If any cells exist in that section, the routine returns true (i.e. non-zero). Otherwise, it returns false .

You may wish to insert several cells at once. For this reason, the cell library provides the routine RangeInsert() . This routine does not actually allocate cells; instead, it shifts existing cells to make room for new ones. You specify a section of the cell file to shift. Any cells in that section will be shifted over; the caller specifies whether they should be shifted horizontally or vertically. The routine takes two arguments, namely the address of the CellFunctionParameters and the address of a RangeInsertParams structure. It does not return anything. The definition of the RangeInsertParams structure is shown in The RangeInsertParams and Point structures . The calling geode should allocate it and initialize it before calling RangeInsert() .

Code Display 19-3 The RangeInsertParams and Point structures

typedef	struct {		/* defined in cell.h */
    Rectangle			RIP_bounds;		/* Range of cells to shift */
    Point			RIP_delta;		/* Specify which way to shift */
    CellFunctionParameters			*RIP_cfp;
} RangeInsertParams;
typedef	struct {		/* defined in graphics.h */
    sword		P_x;	/* Distance to shift horizontally */
    sword		P_y;	/* Distance to shift vertically */
} Point;

The RangeInsertParams structure has three fields. The calling geode should initialize the fields to determine the behavior of RangeInsert() :

RIP _bounds
This field specifies which cells should be shifted. The cells currently in this range will be shifted across or down, depending on the value of RIP_delta ; this shifts more cells, and so on, to the edge of the visible portion of the cell file. The field is a Rectangle structure. To insert an entire row (which is much faster than inserting a partial row), set RIP_bounds.R_left = 0 and RIP_bounds.R_right = LARGEST_COLUMN .
RIP _delta
This field specifies how far the cells should be shifted and in which direction. The field is a Point structure (see The RangeInsertParams and Point structures ). If the range of cells is to be shifted horizontally, RIP_delta.P_x should specify how far the cells should be shifted to the right, and RIP_delta.P_y should be zero. If the cells are to be shifted vertically, RIP_delta.P_y should specify how far the cells should be shifted down, and RIP_delta.P_x should be zero.
RIP _cfp
This is the address of the CellFunctionParameters structure. You don't have to initialize this; the routine will do so automatically.

You may need to perform a certain function on every one of a range of cells. For this purpose, the cell library provides the routine RangeEnum() . This routine lets you specify a range of cells and a callback routine; the routine will be called on each cell in that range.

You can sort a range of cells, by row or by column, based on any criteria you choose. Use the routine RangeSort() . This routine uses a QuickSort algorithm to sort the cells specified. You supply a pointer to a callback routine which is used to compare cells.

This document is a single-page version of a a multi-page document, suitable for easy printing.


DataStore Library

Personal Information Manager (PIM) applications such as address books and appointment calendars need fast, efficient data storage. Because these applications often share the same data, the data should be maintained on the system level and not by each application individually.

The DataStore Library provides a simple way for applications to store and access data in a centralized database. It shields programmers from low-level implementation of data storage.

To understand how to use the DataStore Library, you need only a basic understanding of GEOS programming.

1 Introduction
2 Creating a DataStore
2.1 Adding Fields
2.2 Adding Records
2.3 Deleting Records
2.4 Deleting Fields
3 Deleting a DataStore
4 Building an Index
5 String Search
6 Enumeration
7 Timestamping
8 Synchronization

DataStore Library: 1 Introduction

The DataStore Library provides a system for storing and manipulating collections of structured data. Data reside in numbered records with type-defined fields; each collection of records is called a datastore. An application can access different datastores simultaneously; similarly, multiple applications can access a single datastore concurrently.

The DataStore Manager manages synchronization by issuing "session tokens" whenever an application opens (or creates) a datastore. An application can have multiple sessions, each identified by a unique token.

The DataStore Library contains routines for creating and accessing data on the file level, record level, and field level. All the routines return an error value which is useful for error checking. For a full list of routines and their parameter lists, see the C Reference Book (routines are listed alphabetically and all DataStore routines begin with "DataStore").

DataStore Library: 2 Creating a DataStore

DataStoreCreate()

To create a new datastore, specify its attributes in a DataStoreCreateParams structure and call DataStoreCreate() .

typedef struct {
    TCHAR	*DSCP_name;
    DataStoreFlags	DSCP_flags;
    FieldDescriptor	*DSCP_keyList;
    word 	DSCP_keyCount;
    optr	DSCP_notifObject;
    DataStoreOpenFlags	DSCP_openFlags;
} DataStoreCreateParams;
DSCP _name
Name of the datastore. Used when opening, deleting or renaming a datastore. Any legal filename is acceptable.
DSCP _flags
Only the following flags may be passed:
  • DSF_PRIVATE
    Restricts access to the datastore to the owner only ( i.e. , the application that created the datastore).
  • DSF_TIMESTAMP
    Creates a timestamp field and makes it the first field of the datastore. This field is updated whenver a record is modified.
  • DSF_NO_PRIMARY_KEY
    Specifies that the datastore will contain no key field. When passing this flag, you must set DSCP _keyCount to zero.
DSCP _keyList
Pointer to an array of fields that make up the key. The term "key field" is not used in the relational database sense. In a datastore, the key field does not uniquely identify a record; it simply determines storage order of the records if no callback is passed in DataStoreSaveRecord() (see "Adding Records," Adding Records for a complete discussion of storage order).
    typedef struct {
        FieldData		FD_data;
        TCHAR		*FD_name;
    } FieldDescriptor;
    typedef struct {
        FieldType			FD_type;
        FieldCategory			FD_category;
        FieldFlags			CFD_flags;
    } FieldData;
Fields of FieldType DSFT_TIMESTAMP and DSFT_BINARY may not be part of the key and the only FieldFlag which may be passed is FF_DESCENDING (default sort order is ascending).
Once you create a datastore, you cannot redefine the key later (by adding, deleting or changing the key field(s)) nor can you add a key to a datastore that was created without one.
DSCP _keyCount
Number of fields that make up the primary key.If you pass zero, then you must pass DSF_NO_PRIMARY_KEY in DSCP_flags .
DSCP _notifyObject
Object to be notified when changes to the datastore occur. This object will receive MSG_META_NOTIFY_WITH_DATA_BLOCK which will pass a DataStoreChangeNotification structure. See the GCN chapter.

Passing NullOptr means no object will receive notification.
DSCP _openFlags
Passing zero allows multiple applications to open the datastore simultaneously. Passing DSOF_EXCLUSIVE gives exclusive access to the calling application.

Adding Fields

Adding Records

Deleting Records

Deleting Fields

DataStore Library: 2.1 Creating a DataStore: Adding Fields

DataStoreAddField()

Once you've created a new datastore, you can add (non-key) fields at any time with DataStoreAddField() . When adding fields, keep in mind:

  • The maximum size of a field name is MAX_FIELD_NAME_LENGTH or 40.
  • When defining fields, note the difference between FieldType and FieldCategory . FieldType specifies the type of data stored in the field, such as DSFT_STRING; FieldCategory defines what type of information that data represents, such as a phone number (FC_TELEPHONE). See the C Reference Book for a full list of FieldTypes and FieldCategories .

The following code sample shows how to create a datastore that contains three fields, one of which is the key.

Code Display 9-1 Creating a New DataStore

    /*
     * When a new datastore is created, the DataStore Manager
     * opens a "session" and returns a "session" token
     */
    word	dsToken;
    /*
     * DataStoreCreateParams contains information about
     * the new datastore, such as key field(s) and access level
     */
    DataStoreCreateParams	params;
    /*
     * FieldDescriptor contains information about
     * a field, such as its name and type
     */
    FieldDescriptor	field;
    /*
     * This example will be an "Exchange Rate" datastore with three fields
     */
    static TCHAR dsName[] = "Exchange Rates";
    static TCHAR field1[] = "country";
    static TCHAR field2[] = "currency";
    static TCHAR field3[] = "exchange rate";
    /*
     * Define the "country" field; this will become the key field
     */
    field.FD_name = field1;
    field.FD_data.FD_type = DSFT_STRING;
    field.FD_data.FD_category = FC_NAME;
    field.FD_data.FD_flags = 0;	/* use ascending sort order */
    /*
     * Set the parameters for the new DataStore file:
     *   - add a timestamp field (this becomes the first field)
     *   - define the key field (the key cannot be changed later)
     *   - designate which object is to receive notifications
     * 	when the DataStore is changed
     */
    params.DSCP_name = dsName;
    params.DSCP_flags = DSF_TIMESTAMP;
    params.DSCP_keyList = &field; /* "country" field defined above */
    params.DSCP_keyCount = 1;
    params.DSCP_notifObject = oself; /* "oself" refers to the object handling
            this message; in this case, it is the process object */
    params.DSCP_openFlags = 0; /* make the datastore sharable between apps */
    /*
     * Now create the new datastore file. If it is successfully created,
     * add the additional fields.
     */
    if(DataStoreCreate(&params, &dsToken) == DSE_NO_ERROR)
    {
        /*
         * DataStoreAddField() returns the FieldID of the newly
         * created field to the passed FieldID variable (i.e., fid).
         */
        FieldID		fid;
        /* add currency field */
        field.FD_name = field2;
        field.FD_data.FD_type = DSFT_STRING;
        field.FD_data.FD_category = FC_NONE;
        field.FD_data.FD_flags = 0;	/* use ascending sort order */
        DataStoreAddField(dsToken, &field, &fid);
        /* add exchange rate field */
        field.FD_name = field3;
        field.FD_data.FD_type = DSFT_FLOAT;
        field.FD_data.FD_category = FC_NONE;
        field.FD_data.FD_flags = 0;	/* use ascending sort order */
        DataStoreAddField(dsToken, &field, &fid);
    }

Below is a diagram of the "Exchange Rates" datastore created in the above code example. The following section discusses how to add records to the datastore.

Note that fields of type DSFT_FLOAT expect data of type FloatNum (a special GEOS data type that differs from the standard C float type; see the Math chapterfor additional information on FloatNum s).

DataStore Library: 2.2 Creating a DataStore: Adding Records

DataStoreOpen(), DataStoreNewRecord(), DataStoreSetField(), DataStoreSaveRecord()

To manage synchronization between applications accessing the same datastore, the DataStore Manager creates a "session" each time an application opens a datastore with DataStoreOpen() . Each session has its own "record buffer;" to modify a record, you must load it into the record buffer first. The loaded record is called the "current record." There can only be one record loaded in the buffer at a time. DataStoreNewRecord() adds a new record and makes that record the "current record."

To write to a record, call DataStoreSetField() . When you write to a record, you are actually writing to a copy of the record loaded in the record buffer. Any changes made to this copy do not become permanent until you call DataStoreSaveRecord() . To cancel changes, call DataStoreDiscardChanges() . Both routines flush the current record from the buffer.

Most routines that access fields (such as DataStoreSetField() ) take both a FieldID and a field name as parameters. You can reference the field by either parameter. To reference a field by its name, pass its name in the name parameter and zero in the FieldID parameter. To reference a field by its FieldID , pass its FieldID in the FieldID parameter andNULL in the name parameter. Fields are numbered from zero. (There are routines for obtaining the FieldID corresponding to a given field name and vice versa; see DataStoreFieldNameToID() and DataStoreFieldIDToName() in the C Reference Book.)

The following code example shows how to open an existing datastore, add a new record, and write data to two of its fields.

Code Display 9-2 Adding a Record to a DataStore

    /* Opening a datastore returns a token. */
    word			dsToken;
    /*
     * Saving changes to a record returns both the record's RecordNum
     * and RecordID
     */
    RecordID			recordID;
    RecordNum			recordNum;
    /*
     * Open the "Exchange Rates" datastore with sharable access
     * (i.e., no flag passed). "Oself" refers to the object which is to
     * receive change notifications; in this case, assume oself refers to
     * the process class.
     */
    if(DataStoreOpen("Exchange Rates", oself, 0, &dsToken) == DSE_NO_ERROR)
    {
        /* Add a new record. */
        if(DataStoreNewRecord(dsToken) == DSDE_NO_ERROR)
        {
            /*
             * Write data to the country and exchange rate fields.
             * You can refer to a field by its name or FieldID;
             * the examples below show both methods.
             */
            TCHAR			countryBuffer[] = "Albania";
            /* refer to a field by its name */
            DataStoreSetField(dsToken, "country", 0, countryBuffer,
                            strlen(countryBuffer));
            FloatNum			rateBuffer = .9234;
            /* refer to a field by its FieldID */
            DataStoreSetField(dsToken, NULL, 3, &rateBuffer,
                            sizeof(rateBuffer));
            /* Save the record. */
            DataStoreSaveRecord(dsToken, 0, 0, &recordNum, &recordID);
        }
        /* Close the datastore. */
        DataStoreClose(dsToken);
    }

DataStoreSaveRecord() writes the RecordNum and RecordID of the saved record to the passed variables. A record's RecordNum is its relative place in the datastore; this value may change when records are added or deleted. A record's RecordID is its unique identifier and does not change. RecordNums are numbered from zero; RecordIDs are numbered from one.

You can use a callback function with DataStoreSaveRecord() to specify where in the datastore the record is to be saved. The calling routine passes the record to be inserted (rec1) and a record from the datastore (rec2) to the callback; the callback decides which of the two records comes before the other. (The callback cannot modify the records, however.)

The callback should return a value greater than zero if rec1 comes before rec2; otherwise, a value less than zero. Declaration of Callback Function in DataStoreSaveRecord() shows the declaration of the callback.

Code Display 9-3 Declaration of Callback Function in DataStoreSaveRecord()

sword SortCallback(RecordHeader *rec1, RecordHeader *rec2,
                        word dsToken, void *cbData);

The actual data in the record follows the RecordHeader . Use DataStoreGetFieldPtr() or DataStoreFieldEnum() to access fields within the records.

typedef struct {
    RecordID	RH_uniqueID;
    byte	RH_fieldCount; /* # of fields */
    word	RH_size; /* # of bytes */
} RecordHeader;

If you do not specify a callback routine in DataStoreSaveRecord() , it will insert the record according to values in the key field(s). If two records have matching values in one key field, they will be inserted according to the first non-matching key field value. Records with matching key field(s) values are stored in the order they are added. Records with empty key fields are inserted at the beginning of the file.

If there is no callback or key, records are added to the end of the file.

DataStore Library: 2.3 Creating a DataStore: Deleting Records

DataStoreDeleteRecord(), DataStoreDeleteRecordNum()

To delete a record by its RecordID , call DataStoreDeleteRecord() . To delete a record by its RecordNum , call DataStoreDeleteRecordNum() . When deleting records, remember that RecordID s number from one and stay constant but that RecordNum s (like array elements) number from zero and change (whenever records are added or deleted). Be sure to use the correct value with the appropriate routine.

DataStore Library: 2.4 Creating a DataStore: Deleting Fields

DataStoreGetNumFields(), DataStoreRemoveFieldFromRecord(), DataStoreDeleteField()

To optimize storage, empty fields are not stored; thus, it is possible for a datastore to be defined with four fields but contain records with fewer than four fields.

DataStoreGetNumFields() returns the number of fields in the current record. DataStoreRemoveFieldFromRecord() deletes a field from the current record. (Note that passing zero in the size parameter in DataStoreSetField() will also cause the field to be removed from the record.)

To delete a field from the datastore itself, call DataStoreDeleteField() .

DataStore Library: 3 Deleting a DataStore

DataStoreDelete()

To delete a datastore, call DataStoreDelete() . This routine will delete the entire file or return DSE_DATASTORE_IN_USE if the datastore is in use or DSE_DATASTORE_NOT_FOUND if the datastore does not exist.

DataStore Library: 4 Building an Index

DataStoreBuildIndex()

You can create a secondary index ( i.e. , an index based on a non-key field) by calling DataStoreBuildIndex() . This routine builds an index based on a single field or on sort criteria specified in a callback function.

DataStoreBuildIndex() creates an array of RecordNum s (low word only) and stores the array in an LMem (local memory) block. The block contains an IndexArrayBlockHeader which holds data about the index (the number of records in the index, the offset to the beginning of the array, etc.). Following the block header is space for writing your own data, which is followed by the index itself. There is no limit to how much data you can write but the amount of data you write naturally decreases the amount of space available for the index. If the LMem block is too big to be allocated, the routine will return DSSE_MEMORY_FULL.

The following example shows how to build an index on a field and write data to the index's block header.

Code Display 9-4 Building a Secondary Index

    /* index parameters */
    DataStoreIndexCallbackParams	params;
    /* Building an index returns the handle of the allocated block */
    MemHandle	indexHandle;
    /* sample data to be added to the index block */
    TCHAR	indexData[]  = "Index created 7/1/96.";
    /*  Set up index parameters. */
    params.DSICP_indexField = 2; /* field on which to build index */
    params.DSICP_sortOrder = SO_DESCENDING; /* sort direction */
    params.DSICP_cbData = NULL; /* data to be passed to the callback
                    function if a callback is being used */
    /*
     * Open the datastore. (In this example, assume that dsToken
     * has been declared as a global variable.)
     */
    if(DataStoreOpen("Exchange Rates", oself, 0, &dsToken) == DSE_NO_ERROR)
    {
        /* Build the index. */
        if(DataStoreBuildIndex(dsToken, &indexHandle,
                sizeof(IndexArrayBlockHeader)+
                LocalStringSize(indexData)+sizeof(TCHAR),
                &params, NULL) == DSSE_NO_ERROR)
        {
            /* get a pointer to the block header */
            IndexArrayBlockHeader *pIndex;
            /* lock the block down */
            MemLock(indexHandle);
            /*
             * dereference the handle to get a pointer
             * to the block header
             */
            pIndex = MemDeref(indexHandle);
            /* increment the pointer past the block header */
            pIndex++;
            /* now copy the sample data into the block */
            strcpy((TCHAR *) pIndex,  indexData);
            /* Now that we're through with the block,unlock it. */
            MemUnlock(indexHandle);
        }
        /* Close the datastore. */
        DataStoreClose(dsToken);
    }

You can also build an index based on a custom callback routine. The calling routine passes the DataStoreCallbackParams to the callback; the callback decides which of the two records ( DSICP_rec1 or DSICP_rec2 ) should go first. (If you use a callback, DataStoreBuildIndex() will ignore DSICP _indexField and DSICP _sortOrder .)

The callback should return:
-1 if DSICP_rec1 comes before DSICP_rec2
 1 if DSICP_rec1 comes after DSICP_rec2

Code Display 9-5 Declaration of Callback Function in DataStoreBuildIndex()

sword SortCallback(word dsToken, DataStoreIndexCallbackParams *params);

DataStoreBuildIndex() works on datastores of 4,000 records or less. If you call this routine on a datastore larger than 4,000 records, the routine will return DSSE_INDEX_RECORD_NUMBER_LIMIT_EXCEEDED.

The application owns this index and is responsible for freeing the block. The DataStore Manager does not maintain the index in any way. Applications can synchronize a secondary index by rebuilding it whenever the application receives notification of a change that would affect the index.

DataStore Library: 5 String Search

DataStoreStringSearch()

To do a simple string search (on a specified field or field category), use DataStoreSearchString() . Starting at a specified record number, this routine searches through each record until it finds a match or until it runs out of records to search.

DataStoreSearchString() uses the following parameters:

typedef struct {
    SearchType	SP_searchType;
    RecordNum	SP_startRecord;
    dword	SP_maxRecords;
    FieldID	SP_startField;
    FieldCategory	SP_category;
    TCHAR	*SP_searchString;
    SearchFlags	SP_flags;
} SearchParams;
SP _searchType
Possible flags:
  • ST_ALL
    Search all fields of FieldType DFST_STRING.
  • ST_FIELD
    Search by FieldID . (Specify the FieldID in SP_startField , explained below.)
  • ST_CATEGORY
    Search by FieldCategory . (Specify the FieldCategory in SP_category , explained below.)
SP _startRecord
RecordNum of record to begin search. This routine updates this field with the RecordNum of the last record examined.
SP _maxRecords
Maximum number of records to search. Allows the program to break out of the search if no matches are found. (The search can be continued by incrementing the value of SP_startRecord .) Passing -1 causes the routine to search all records.
SP _startField
FieldID of field to search if SP_searchType is set to ST_FIELD. If you specify a non-string field, the routine will return DSE_BAD_SEARCH_PARAMS.

FieldID of the field to begin the search if SP_searchType is set to ST_CATEGORY. If you specify a field that is not of the specified category, the routine will start with the next field it finds of the specified category.
SP _category
FieldCategory to search if SP_searchTyp e is set to ST_CATEGORY (otherwise this parameter is ignored).
SP _searchString
Null-terminated string to search.
SP _flags
Possible flags:
  • SF_BACKWARDS
    Search backwards through the records.
  • SF_START_AT_END
    Begin search from first record (going forward) or from last record if SF_BACKWARDS is also passed. If set, SP_startRecord is ignored.
  • SF_IGNORE_CASE
    Ignore case when searching.
  • SF_NO_WILDCARDS
    Ignore wild card characters.

    Many libraries including DataStore support the use of wild card characters for searching. See the Text object chapter for a discussion of wild card searching; also, see /Appl/Datastore/datastore.goc for a code example.
  • SF_PARTIAL_WORD
    Treat search string as a substring. For example, if search string is "den," return "gardenia," "dentist," and "garden."

The following example shows how to set up a simple string search on a specified field.

Code Display 9-6 Searching a DataStore

    /* search conditions */
    SearchParams	params;
    /*  Specify search parameters. */
    params.SP_searchType = ST_FIELD; /* search by FieldID */
    params.SP_startRecord = 0; /* start search at first record */
    params.SP_maxRecords = -1; /* search all records until a match is found or
                    there are no more records to search */
    params.SP_startField = 1; /* search "country" field */
    params.SP_searchString = "Albania"; /* string to search for */
    params.SP_flags = SF_IGNORE_CASE; /* ignore case when searching */
    /* Open the datastore. */
    if(DataStoreOpen("Exchange Rates", oself, 0, &dsToken) == DSE_NO_ERROR)
    {
        /* Do the search. */
        if(DataStoreStringSearch(dsToken, &params) == DSDE_NO_ERROR)
        {
             /*
              * If a match is found, load the record and get the data
              * from the "exchange rate" field.
              * Note:  DataStoreStringSearch() returns the record
              * number of the last examined record in SP_startRecord.
              */
            if(DataStoreLoadRecordNum(dsToken, params.SP_startRecord,
                            &recordID) == DSDE_NO_ERROR)
            {
                /* variables used for retrieving field data */
                FloatNum		rateBuffer, *pRateBuffer;
                RecordID		recordID;
                MemHandle		dummy;
                word		size;
                pRateBuffer = &rateBuffer;
                size = sizeof(rateBuffer);
                DataStoreGetField(dsToken, "exchange rate", 0,
                    (void **)&pRateBuffer, &size, &dummy);
                /*
                  * Do something with the data then
                  * flush the record from the buffer.
                  */
                DataStoreDiscardRecord(dsToken);
            }
        DataStoreClose(dsToken);
        }
    }

If the routine returns DSE_NO_MORE_RECORDS, it has reached the last record in the file (either the first or last record depending on the direction of the search). If the routine returns DSE_NO_MATCH_FOUND, it did not find a match within the set of records it searched. If it returns DSE_NO_ERROR, it writes the RecordNum of the matching record in SP_startRecord .

The DataStore Library does not implement global searches ( i.e. , searches through multiple datastores), though it is possible to implement this type of search at the application level by opening each datastore file and calling DataStoreStringSearch() on each one.

DataStore Library: 6 Enumeration

DataStoreFieldEnum(), DataStoreRecordEnum()

DataStoreFieldEnum() enumerates through fields of a record. This routine uses a Boolean callback to determine whether to continue enumeration. If the callback returns TRUE, enumeration stops.

DataStoreRecordEnum() enumerates through records of a datastore in storage order, starting at the specified RecordNum in the specified direction. This routine uses a Boolean callback routine to determine whether to continue enumeration. If the callback returns TRUE, enumeration ends; if FALSE, enumeration continues until the callback returns TRUE or until the routine reaches the last record.

There are two DataStoreRecordEnumFlags that can be passed in this routine:

  • DSREF_BACKWARDS
    Enumerate backwards through the datastore.
  • DSREF_START_AT_END
    Enumerate from first record (going forward) or from last record if DSREF_BACKWARDS is also passed.

The following example enumerates through the a datastore looking for the maximum value in a particular field.

Code Display 9-7 Enumerating Through a Datastore

    /* data to be passed to the callback routine */
    FloatNum	enumData = 0;
    /* record at which to start enumeration */
    RecordNum	rec = 0;
    /*
     * Open the datastore. For this example, assume dsToken
     * is a global variable.
     */
    if(DataStoreOpen("Exchange Rates", oself, 0, &dsToken) == DSE_NO_ERROR)
    {
        /*
         * Enumerate through the datastore starting at the first record
         * (so pass zero in the flags parameter);
         * find the maximum value of the "exchange rate" field.
         */
        if(DataStoreRecordEnum(dsToken, &rec, 0, &enumData, EnumCallback)
                            == DSE_NO_MORE_RECORDS)
        {
            /* do something with the value */
        }
        DataStoreClose(dsToken);
    }
    /*
     * The callback compares the data in the "exchange rate" field to the value
     * passed in with enumData. If the field data is greater than that of
     * enumData, copy the field's data to enumData.
     */
    Boolean EnumCallback(RecordHeader *record, void *enumData)
    {
        /* parameters for getting field data */
        FloatNum	rateBuffer, *pRateBuffer;
        FieldType	type;
        word	size;
        if(DataStoreGetFieldPtr(dsToken, record, 3, (void **)&pRateBuffer,
                        &type, &size) == DSDE_NO_ERROR)
        {
            if(*pRateBuffer > *((FloatNum *)enumData))
            {
                *((FloatNum *)enumData) = *pRateBuffer;
            }
        }
        return FALSE; /* FALSE to continue enumeration */
    }

If the routine returns DSE_NO_MORE_RECORDS, it has reached the last record in the file. If it returns DSE_NO_ERROR, it writes the RecordNum of the last record examined in the startRecord parameter.

DataStore Library: 7 Timestamping

DataStoreGetTimeStamp(), DataStoreSetTimeStamp()

Record timestamping is important for synchronization and reconciliation of data between devices; between a portable device and a desktop PC, for example. Passing the FT_TIMESTAMP flag when creating a new datastore makes the first field a timestamp field. The DataStore Manager updates this field when the record has been modified.

You can read the data in the timestamp field by calling DataStoreGetField() and passing zero for the FieldID parameter and NULL for field name.

To retrieve the time and date a datastore was last changed, call DataStoreGetTimeStamp() . To modify a datastore's timestamp manually, call DataStoreSetTimeStamp() .

DataStore Library: 8 Synchronization

The DataStore Manager ensures synchronization between applications sharing the same datastore file by issuing a series of read and write locks. Routines that modify data request write locks while non-modifying routines request read locks. If the DataStore Manager cannot grant a lock because another session has locked the datastore, the routine will return DSE_DATASTORE_LOCKED.There can be up to thirty-two readers per datastore.

This document is a single-page version of a a multi-page document, suitable for easy printing.


The Import/Export Library

Most applications which write data files should be compatible with other applications. For example, applications will want to be able to import data, i.e. open a file written by another application and translate its data into a format the application can use. However, writing code to open another application's file and translate it into your own application's format can be difficult. Any application that wanted to properly support file-importing would have to provide a great many of these utilities. Furthermore, there would be a lot of duplicated effort; one application's routine for importing a particular file format would be much like another's. Applications would have similar problems exporting data, i.e. writing their data in another application's format.

The Impex (Import/Export) Library, along with specific translation libraries, solves these problems. They automatically translate files from non-GEOS formats into the Metafile (i.e. Clipboard) format and back. Applications just need to include the Import and Export objects; once they do this, importing and exporting data is just like using the Clipboard. Furthermore, extending the Impex Library to handle new formats is easy. All you have to do is write a new translation library for that one format and put the library in the SP_IMPORT_EXPORT_DRIVERS standard path. All applications which use the Impex objects will then be able to use that new format. Developers can write their own translation libraries; however, few will need to do this, since GEOS provides libraries for the most popular formats.

The Impex library currently supports import and export of text and graphic files. Geoworks is planning on extending it to allow the import and export of spreadsheet and font files as well. When these capabilities are available, they will be just like the already-supported text and graphic import and export.

1 Impex Basics
1.1 The Impex Objects
1.2 How the Impex Objects Work
2 Using Impex
2.1 Common Impex Concepts
2.2 The ImportControl Object
2.3 The ExportControl Object
3 Writing Translation Libraries
3.1 How Translation Libraries Work
3.2 Intermediate Formats

The Import/Export Library: 1 Impex Basics

The Impex library provides a uniform way of importing data from files written by non-GEOS applications, and of exporting data to files written in these formats. It also saves coding time. Every outside format needs a single translation library, which translates between the Metafile format and the format for that specific library. The Impex objects provide all the necessary user interface (though the libraries and applications can add more), and also do all necessary interaction with the translation libraries.

In order to import data, an application will need to have an ImportControl object. Similarly, in order to export data, an application will need an ExportControl. Most applications that produce data files will want to use both of these objects.

Some applications will create a new GEOS file for imported data; others will add it into the current document, as if they were pasting data from the clipboard. Similarly, some applications will export the entire current document; others will export only the current selection, if any. This is left entirely to the discretion of the application.

The Impex Objects

How the Impex Objects Work

The Import/Export Library: 1.1 Impex Basics: The Impex Objects

There are two Impex objects which applications need to know about: ImportControl and ExportControl. These objects are both subclassed off of ImportExportClass , which provides some functionality needed for both objects; however, all this functionality is internal, so you can ignore this class. (No one should ever create an ImportExport object, and it has no instance data which applications may set.) ImportExportClass is itself subclassed from GenControlClass ; therefore, ImportControl and ExportControl objects inherit all the functionality of controllers.

Applications will generally have one ImportControl and one ExportControl object. Applications may put these in different places; for example, some will place both of these on the File menu, while others may put them in the "Open" or "Save As" dialog boxes. Applications should decide this based on how they think the user will be using the import and export capabilities.

The ImportControl and ExportControl objects communicate with the application by sending messages. The ImportControl object sends a message to its recipient near the end of the Import process, when it has translated a file into a VM chain for the application to copy. The ExportControl object sends a message near the beginning of the export process, instructing the application to create a VM chain for the application to export. In both cases the message sent and the recipient are set by the application. Most applications will have the messages sent to their process arguments; however, applications which use the object model of document control may want to have the messages sent to the target object.

The Import/Export Library: 1.2 Impex Basics: How the Impex Objects Work

The Impex objects manage the user interface for importing and exporting data. They also open and create the appropriate files when necessary. If a user wants to import a file, the Import object lets the user select a source file and format. The import object will then call an appropriate library to translate the source file into a VM chain in the clipboard (i.e. MetaFile) format. At this point, it sends a message to the application, passing the handle of the VM chain. Similarly, when the user wants to export data, the Export object first lets the user select a name and format for the destination file. The Export object then sends a message to the application requesting a source VM chain in the Metafile format. The Export object then calls the appropriate translation library, which translates the MetaFile into a corresponding DOS file in the specified format.

It may help to give a blow-by-blow example. Let's suppose someone is using FooPaint, a graphics program for GEOS. FooPaint uses both the ImportControl and ExportControl objects to let the user import and export graphics. The user wants to import a graphic file written by BazDraw, a non-GEOS application. The user activates the "Import" trigger. The following things happen:

  1. The ImportControl checks what translation libraries are available. This tells it what formats can be imported.
  2. The ImportControl presents a dialog box to the user. This box contains a list of available formats. It also contains a file selector, which the user uses to select a file of the appropriate format. Assume the user chooses the BazDraw format, and the file FISH.BAZ.
  3. The ImportControl opens FISH.BAZ and a temporary VM file. It then starts up the BazDraw-to-Metafile library. It then sends a message to this library, passing both files' handles with the message.
  4. The library reads FISH.BAZ and writes an equivalent Metafile sequence in a VM chain in the temporary file. When it is done, it returns the VM chain handle to the ImportControl.
  5. The ImportControl sends a message to its destination object (often the application's Process object). This is the application's first direct involvement in the import. The message includes such information as the handles of the VM chain, the type of data being imported (in this case, a graphics metafile), and the message to send on completion.
  6. The application copies the information from the VM chain. This is almost identical to pasting data from the clipboard. FooPaint will probably respond by adding the graphic to the target document.
  7. When the application is finished copying the data, it calls the ImpexImportExportCompleted() utility routine. This routine sends an appropriate acknowledgment message to the ImportControl object. The ImportControl destroys the temporary VM file.

Note that almost all of this takes place without any action by the application. The only thing the application has to do is copy data from the VM chain to its own file, translating from the Metafile format to its own format. This is exactly what the application does whenever it pastes data from the clipboard; thus, applications which use the clipboard already have almost everything they need to use the Impex objects.

Exporting data is almost the same as importing it. The application is sent a message at the beginning of the operation, asking the application to write a VM chain in the Metafile format. The ExportControl will already have created a temporary VM file for the application to use; the application just has to allocate a chain in it and fill it with the appropriate information. The ExportControl presents a dialog box to the user, letting the user select a name, format, and location for the exported file; it then starts up the appropriate translation library to write the file.

The Import/Export Library: 2 Using Impex

The Impex objects are easy to use. Essentially, an application has to declare two objects and handle a single message from each of them. Applications which can use the clipboard already have most of the code they will need to use the Impex objects.

Common Impex Concepts

The ImportControl Object

The ExportControl Object

The Import/Export Library: 2.1 Using Impex: Common Impex Concepts

There are certain concepts and structures which are used by both of the Impex objects. Applications which use the Impex objects will have to be familiar with them.

Metafile Formats

ImpexDataClasses

The Impex objects are designed to translate into a few specific formats. These formats are known collectively as the Metafile formats. These formats (except for the "font" format) are identical to the standard "Clipboard Item Formats" described in the Clipboard chapter. When an application uses the Impex objects, it has to specify what types of data it is prepared to import or export. It does this by setting an ImpexDataClasses record in both the ImportControl and the ExportControl objects. ImpexDataClasses has the following fields:

IDC_TEXT
The data is in the Metafile Text format. This is identical to the Clipboard's CIF_TEXT format. In addition to text, this format records information about fonts, spacing, embedded graphics, etc.
IDC_GRAPHICS
The data is in the Metafile Graphics format. This is simply a GString in a VM chain. This format is identical to the Clipboard's CIF_GRAPHICS_STRING format.
IDC_SPREADSHEET
The data is in the Metafile Spreadsheet format. This corresponds to the Clipboard's CIF_SPREADSHEET format.
IDC_FONT
The data is in the Metafile Font format. This format is used to translate fonts between representations.

Every translation library translates a DOS file into a VM chain in one of these four formats, and vice versa. When an application declares an Impex object, it specifies what formats should be supported for that application. The user will be allowed to choose translation libraries which translate to or from a supported Metafile format.

ImpexTranslationParams

ImpexTranslationParams

The Import and Export objects have to pass information to the application and the translation libraries. The same sort of information gets passed in many situations; for example, the objects often have to pass the VMFileHandle and VMBlockHandle which specify the VM chain. For simplicity, the Impex objects just pass a pointer to a ImpexTranslationParams structure when they send messages to the translation libraries or the application. The library or application takes the appropriate action, changing the ImpexTranslationParams structure as necessary; it then sends a response message, which takes a pointer to the same ImpexTranslationParams . ImpexTranslationParams has the following structure:

typedef struct {
     optr			ITP_impexOD;
    Message			ITP_returnMsg;
    ImpexDataClasses			ITP_dataClass;
    VMFileHandle			ITP_transferVMFile;
    VMBlockHandle			ITP_transferVMChain;
    dword			ITP_internal;
    ManufacturerID			ITP_manufacturerID;
    ClipboardFormat			ITP_clipboardFormat;
} ImpexTranslationParams;
ITP_impexOD
This field holds the optr of whatever Impex object sent the message. The response message should be addressed to this optr.
ITP _returnMsg
This field holds the message which the library or application should send to the Impex object when it is finished. This message always takes a single argument, namely a pointer to the ImpexTranslationParams . The application should just pass the pointer to the ImpexTranslationParams to the routine ImpexImportExportCompleted() ; this routine will send the appropriate notification message.
ITP _dataClass
This is a ImpexDataClasses record. Exactly one of the flags will be set, indicating what sort of Metafile format is being used.
ITP_transferVMFile
This is the VMFileHandle of the transfer file. The transfer file is automatically created and destroyed by an Impex object.
ITP _transferVMChain
This is the VMBlockHandle of the first block in the transfer VM chain. In Import operations, the translation library creates the VM chain in the transfer file; in Export operations, the application creates it. The chain will be freed when the Impex object destroys the transfer VM file. For information about creating VM chains, see the VM chapter.

ITP_internal
 This field is for internal use by the Impex objects. You should not change it.

ITP_manufacturerID
 This field contains the manufacturer ID which should be used for the Metafile data; see the Clipboard chapter.

ITP_clipboardFormat

This field specifies what format should be used for the Metafile data; see the Clipboard chapter.

The meaning of each field can change, depending on the circumstances. For example, when the ImportControl sends its message to the application, the ITP _transferVMChain field will contain the handle of a VM chain containing the imported data. In contrast, when the ExportControl sends its message, ITP _transferVMChain contains a null handle; the application should allocate a VM chain, fill it with data, and write the VMBlockHandle of the chain to this field. When a field has a special meaning, the documentation will explain it.

The Import/Export Library: 2.2 Using Impex: The ImportControl Object

Applications which use the Impex library will generally have a single ImportControl object. This object is usually a child of the File menu; however, applications may put it wherever they want. They should also place it on the application object's GAGCNLT_SELF_LOAD_OPTIONS General Change Notification list. Applications should disable this object (with MSG_GEN_SET_NOT_ENABLED) whenever they are not prepared to accept imported data; for example, some applications will choose to disable file importing whenever they are unable to open a new document.

The ImportControl object is subclassed from GenControlClass (by way of ImportExportClass , as noted above). It thus has all the functionality of that class. It also has a few instance data fields of its own; they are shown in ImportControlClass Instance Data .

Code Display 16-1 ImportControlClass Instance Data

/* ICI_attrs is a word-length record which stores attribute information for the
 * ImportControl object. It has only one flag, ICA_IGNORE_INPUT. */
    @instance ImportControlAttrs					ICI_attrs = 0;
/* ICI_dataClasses is a word-length record which indicates what Metafile formats
 * are supported by the application. The application must set this field. */
    @instance ImpexDataClasses					ICI_dataClasses = 0;
/* ICI_destination and ICI_message indicate what message should be sent when the
 * ImportControl object has finished importing a file. The application must set
 * these fields. The message must take a single argument, namely a pointer to an
 * ImpexTranslationParams structure in ss:bp (on the stack). */
    @instance optr					ICI_destination;
    @instance word					ICI_message;
/* Applications may wish to add their own UI objects to the Import dialog box.
 * They can do so by defining a generic tree (the top object of which must be not
 * "usable"), and putting an optr to the top object in ATTR_IMPORT_CONTROL_APP_UI.
 */
    @vardata optr 					ATTR_IMPORT_CONTROL_APP_UI;
/* Controller features flags */
typedef ByteFlags 			ImportControlFeatures;
#define IMPORTCF_BASIC			0x01
typedef	ByteFlags 			ImportControlToolboxFeatures;
#define IMPORTCTF_DIALOG_BOX				0x01

ICI_attrs

ImportControlAttrs, MSG_IMPORT_CONTROL_GET_ATTRS, MSG_IMPORT_CONTROL_SET_ATTRS

ICI _attrs is a word-length record of type ImportControlAttrs . This record contains only one flag:

ICA_IGNORE_INPUT
If this flag is on, the ImportControl will consume all input to the application while the import occurs. By default, this flag is off.

To find out the current setting of this field, send MSG_IMPORT_CONTROL_GET_ATTRS to the ImportControl. To change this field, send MSG_IMPORT_CONTROL_SET_ATTRS to the ImportControl.

MSG_IMPORT_CONTROL_GET_ATTRS
ImportControlAttrs	 MSG_IMPORT_CONTROL_GET_ATTRS();

This message retrieves the current setting of the ImportControl's ICI _attrs field.

Source: Unrestricted.

Destination: Any ImportControl object.

Return: The ImportControl's ICI _attrs field.

Interception: This message should not be intercepted.

MSG_IMPORT_CONTROL_SET_ATTRS
void	MSG_IMPORT_CONTROL_SET_ATTRS(
        ImportControlAttrs		attrs);

This message changes the current settings of an ImportControl's ICI _attrs field.

Source: Unrestricted.

Destination: Any ImportControl object.

Parameters: attrs The new settings for the ICI _attrs field.

Interception: This message should not be intercepted.

ICI_dataClasses

MSG_IMPORT_CONTROL_GET_DATA_CLASSES, MSG_IMPORT_CONTROL_SET_DATA_CLASSES

When you declare an Import object, you must specify what kind of Metafiles your application is prepared to accept. You do this by setting the value of the ICI _dataClasses field. This field is a word-length record of type ImpexDataClasses (described in ImpexTranslationParams ). If (for example) only the IDC_TEXT bit is set, the ImportControl will use only those import libraries which produce text Metafile output. More than one bit may be set; when the ImportControl sends its notification, it will tell the application what type of data is being imported.

To find out the current settings of the ICI _dataClasses field, send MSG_IMPORT_CONTROL_GET_DATA_CLASSES. To change the settings of this field, send MSG_IMPORT_CONTROL_SET_DATA_CLASSES.

MSG_IMPORT_CONTROL_GET_DATA_CLASSES
ImpexDataClasses	 MSG_IMPORT_CONTROL_GET_DATA_CLASSES();

This message retrieves the current setting of the ImportControl's ICI _dataClasses field. This tells you what kind of data can be imported.

Source: Unrestricted.

Destination: Any ImportControl object.

Return: The ImportControl's ICI _dataClasses field.

Interception: This message should not be intercepted.

MSG_IMPORT_CONTROL_SET_DATA_CLASSES
void	MSG_IMPORT_CONTROL_SET_DATA_CLASSES(
        ImpexDataClasses 		dataClass);

This message changes the current settings of an ImportControl's ICI _dataClasses field.

Source: Unrestricted.

Destination: Any ImportControl object.

Parameters: dataClass The new settings for the ICI _dataClasses field.

Interception: This message should not be intercepted.

The ImportControl Action

MSG_IMPORT_CONTROL_GET_ACTION, MSG_IMPORT_CONTROL_SET_ACTION, ImpexImportExportCompleted()

The ImportControl does most of its work transparently to the application. It interacts with the rest of the application only when the user has selected a file to import and the appropriate translation library has produced a VM chain. At this point the ImportControl sends a notification message to the application. The application responds by copying the data from the VM chain and sending back an acknowledgment message. The ImportControl can then destroy the temporary VM transfer file.

The application determines what message will be sent, and to what object, by setting the ICI _destination and ICI _message fields. Whatever object will receive the message should define an appropriate message. The ImportControl will send this message with a single parameter: itp , a pointer to an ImpexTranslationParams structure (see ImpexTranslationParams ). The fields of the structure have the following meanings in this case:

ITP_impexOD
The object to which the application should send its acknowledgment message. In this case, it is the optr of the ImportControl.
ITP _returnMsg
The acknowledgment message to send when the import has been completed. In this case, it is MSG_IMPORT_CONTROL_IMPORT_COMPLETE.
ITP _dataClass
An ImpexDataClasses record with one flag set. This flag indicates what type of Metafile has been prepared.
ITP _transferVMFile
The VMFileHandle of the temporary transfer file.
ITP _transferVMChain
The VMBlockHandle of the lead block in the VM chain containing the imported data.
ITP _internal
For use by the ImportControl and should not be changed by the application.

The recipient of the message should take any appropriate action; usually this entails copying the data from the VM chain, as if it were pasting data from the Clipboard. When the application is finished, it should call ImpexImportExportCompleted() . This routine takes one parameter, namely the itp pointer which was passed to the object. (The ImpexTranslationParams structure should not have been changed.) ImpexImportExportCompleted() reads the appropriate message and destination from the ImpexTranslationParams and sends the proper acknowledgment message (which in this case is MSG_IMPORT_CONTROL_IMPORT_COMPLETE ).

Applications which use the object model of document control will often set ICI _destination to TO_APP_TARGET; this will make it send its messages to the target object. The application can find out the ImportControl's action by sending it MSG_IMPORT_CONTROL_GET_ACTION . The application can change the ImportControl's action by sending it MSG_IMPORT_CONTROL_SET_ACTION .

MSG_IMPORT_CONTROL_GET_ACTION
void	MSG_IMPORT_CONTROL_GET_ACTION(
        ImpexAction *		retValue);

This message retrieves the values of an ImportControl's ICI _destination and ICI _message fields. These fields indicate what action the ImportControl will take when it is finished preparing a file for import.

Source: Unrestricted.

Destination: Any ImportControl object.

Parameters: retValue A pointer to an ImpexAction structure.

Return: The value of ICI _message (i.e. the message sent by the ImportControl).

recipient
A pointer to an ImpexAction structure describing the message sent.

Interception: This message should not be intercepted.

Structures: The message and recipient are written to an ImpexAction structure:

typedef struct {
    word	message;		/* message sent */
    word	unused;
    optr	destOD;		/* Destination of message */
} ImpexAction;
MSG_IMPORT_CONTROL_SET_ACTION
void	MSG_IMPORT_CONTROL_SET_ACTION(
        optr	destOD,	/* Send messages to this object. */
        word	ICImsg);	/* Send this message to the above recipient. */

This message changes the values of an ImportControl's ICI _destination and ICI _message fields. These fields indicate what action the ImportControl will take when it is finished preparing a file for import.

Source: Unrestricted.

Destination: Any ImportControl object.

Parameters: destOD Set ICI _destination to this value.

ICImsg
Set ICI _message to this value.

Interception: This message should not be intercepted.

Adding to the Import Dialog Box

When the user selects the "Import" trigger or tool, the Import controller brings up a dialog box. The application can, if it wishes, add UI objects to this box. It does so by defining a tree of generic objects (the top object of which must be set "not usable"). It must place an optr to the top object in the tree in ATTR_IMPORT_CONTROL_APP_UI. When the ImportControl builds the dialog box, it will add that optr as one of the children in the tree and set it "usable".

The Import/Export Library: 2.3 Using Impex: The ExportControl Object

Applications which use the Impex library will generally have a single ExportControl object. This object is usually a child of the File menu; however, applications may put it wherever they want. They should also place it on the application object's GAGCNLT_SELF_LOAD_OPTIONS GCN list. Applications should disable this object (with MSG_GEN_SET_NOT_ENABLED) whenever they are not able to prepare data for export; for example, some applications will choose to disable file exporting whenever the "Cut" and "Copy" functions are disabled.

The ExportControl object is subclassed from GenControlClass (by way of ImportExportClass , as noted above). It thus has all the functionality of that class. It also has a few instance data fields of its own; they are shown in ExportControlClass Instance Data .

Code Display 16-2 ExportControlClass Instance Data

/* ECI_attrs is a word-length record which stores attribute information for the
 * ImportControl object. It has only one flag, ECA_IGNORE_INPUT. */
    @instance ExportControlAttrs					ECI_attrs = 0;
/* ECI_dataClasses is a word-length record which indicates what Metafile formats
 * are supported by the application. The application must set this field. */
    @instance ImpexDataClasses					ECI_dataClasses = 0;
/* ECI_destination and ECI_message indicate what message should be sent when the
 * ExportControl object is preparing to export a file. The application must set
 * these fields. The message must take a single argument, namely a pointer to an
 * ImpexTranslationParams structure in ss:bp (on the stack). */
    @instance optr					ECI_destination;
    @instance word					ECI_message;
/* Applications may wish to add their own UI objects to the Export dialog box.
 * They can do so by defining a generic tree (the top object of which must be not
 * "usable"), and putting an optr to the top object in ATTR_EXPORT_CONTROL_APP_UI.
 */
    @vardata optr 					ATTR_EXPORT_CONTROL_APP_UI;
/* Controller features flags */
typedef ByteFlags 			ExportControlFeatures;
#define EXPORTCF_BASIC			0x01
typedef	ByteFlags 			ExportControlToolboxFeatures;
#define EXPORTCTF_DIALOG_BOX				0x01

ECI_attrs

ExportControlAttrs, MSG_EXPORT_CONTROL_GET_ATTRS, MSG_EXPORT_CONTROL_SET_ATTRS

ECI _attrs is a word-length record of type ExportControlAttrs . This record contains only one flag:

ECA_IGNORE_INPUT
If this flag is on, the ExportControl will consume all input to the application while the import occurs. By default, this flag is off.

To find out the current setting of this field, send MSG_EXPORT_CONTROL_GET_ATTRS to the ImportControl. To change this field, send MSG_EXPORT_CONTROL_SET_ATTRS to the ImportControl.

MSG_EXPORT_CONTROL_GET_ATTRS
ExportControlAttrs	 MSG_EXPORT_CONTROL_GET_ATTRS();

This message retrieves the current setting of the ExportControl's ECI _attrs field.

Source: Unrestricted.

Destination: Any ExportControl object.

Return: The ExportControl's ECI _attrs field.

Interception: This message should not be intercepted.

MSG_EXPORT_CONTROL_SET_ATTRS
void	MSG_EXPORT_CONTROL_SET_ATTRS(
        ExportControlAttrs		attrs);

This message changes the current settings of an ExportControl's ECI _attrs field.

Source: Unrestricted.

Destination: Any ExportControl object.

Parameters: attrs The new settings for the ECI _attrs field.

Interception: This message should not be intercepted.

ECI_dataClasses

MSG_EXPORT_CONTROL_GET_DATA_CLASSES, MSG_EXPORT_CONTROL_SET_DATA_CLASSES

When you declare an Export object, you must specify what kind of Metafiles your application is able to create. You do this by setting the value of the ECI _dataClasses field. This field is a word-length record of type ImpexDataClasses (described in ImpexTranslationParams ). If (for example) only the IDC_TEXT bit is set, the ExportControl will use only those export libraries which expect text Metafile input. More than one bit may be set; when the ExportControl sends its notification, it will tell the application what type of data it expects to export.

To find out the current settings of the ECI _dataClasses field, send MSG_EXPORT_CONTROL_GET_DATA_CLASSES. To change the settings of this field, send MSG_EXPORT_CONTROL_SET_DATA_CLASSES.

MSG_EXPORT_CONTROL_GET_DATA_CLASSES
ImpexDataClasses	 MSG_EXPORT_CONTROL_GET_DATA_CLASSES();

This message retrieves the current setting of the ExportControl's ECI _dataClasses field. This tells you what kind of data can be exported.

Source: Unrestricted.

Destination: Any ExportControl object.

Return: The ExportControl's ECI _dataClasses field.

Interception: This message should not be intercepted.

MSG_EXPORT_CONTROL_SET_DATA_CLASSES
void	MSG_EXPORT_CONTROL_SET_DATA_CLASSES(
        ImpexDataClasses 		dataClasses);

This message changes the current settings of an ExportControl's ECI _dataClasses field.

Source: Unrestricted.

Destination: Any ExportControl object.

Parameters: dataClasses The new settings for the ECI _dataClasses field.

Interception: This message should not be intercepted.

The ExportControl Action

MSG_EXPORT_CONTROL_GET_ACTION, MSG_EXPORT_CONTROL_SET_ACTION

The ExportControl does most of its work transparently to the application. It interacts with the rest of the application after the user selects the name, location, and format of the exported file. At this point the ExportControl creates a temporary transfer file and sends a notification message to the application; the notification message passes the file handle and the format expected. The application responds by creating a VM chain in the transfer file and filling it with the data to export, formatted in the appropriate Metafile format. The ExportControl can then call the translation library to create the output file.

The application determines what notification message will be sent, and to what object, by setting the ECI _destination and ECI _message fields. Whatever object will receive the message should define an appropriate message. The ExportControl will send this message with a single parameter: itp , a pointer to an ImpexTranslationParams structure. The fields of the structure have the following meanings in this situation:

ITP_impexOD
The object to which the application should send its acknowledgment message. In this case, it is the optr of the ExportControl.
ITP _returnMsg
The acknowledgment message to send when the export has been completed. In this case, it is MSG_EXPORT_CONTROL_EXPORT_COMPLETE.
ITP _dataClass
An ImpexDataClasses record with one flag set. This flag indicates what type of Metafile should be prepared.
ITP _transferVMFile
The VMFileHandle of the temporary transfer file.
ITP _transferVMChain
A null handle. When the application has created the transfer VM chain, it should write the VMBlockHandle of the head of the chain to this field. If the application fails for any reason, it should leave this field as a null handle.
ITP _internal
This field is for use by the ExportControl and should not be changed by the application.

The recipient of the message should take any appropriate action; usually this entails translating the current selection into the Metafile format and writing it to a VM chain. When the application is finished, it should call ImpexImportExportCompleted() . This routine will send the appropriate acknowledgment message to the ExportControl object (in this case, MSG_EXPORT_CONTROL_EXPORT_COMPLETE ). This routine takes one parameter, namely the itp pointer which was passed to the object. The ITP _transferVMChain field of the ImpexTranslationParams structure should be set to the handle of the head block in the VM chain. If the application was unable to prepare the data for export, it should clear this field.

The ExportControl object will have created a temporary file for the application to use. This file will be entirely empty when the application gets it. The ExportControl will ignore everything in the file except for the VM chain indicated by ITP _transferVMChain ; thus, an application can feel free to allocate blocks in the VM file for scratch space. The ExportControl will destroy the file when the translation library has finished preparing the output file.

Applications which use the object model of document control will often set ECI _destination to TO_APP_TARGET; this will make it send its messages to the target object. The application can find out the ExportControl's action by sending it MSG_EXPORT_CONTROL_GET_ACTION . The application can change the ExportControl's action by sending it MSG_EXPORT_CONTROL_SET_ACTION .

MSG_EXPORT_CONTROL_GET_ACTION
void	MSG_EXPORT_CONTROL_GET_ACTION(
        ObjectState *		retValue);

This message retrieves the values of an ExportControl's ECI _destination and ECI _message fields. These fields indicate what action the ExportControl will take when it needs to have data prepared for export.

Source: Unrestricted.

Destination: Any ImportControl object.

Parameters: retValue A pointer to an ObjectState structure.

Return: The value of ICI _message (i.e. the message sent by the ExportControl).

recipient
A pointer to an ObjectState structure describing the message sent.

Interception: This message should not be intercepted.

Structures: The message and recipient are written to an ObjectState structure:

typedef struct {
    int	notUsed;
    word	message;		/* Message sent */
    optr	destOD;		/* Destination of message */
} ObjectState;
MSG_EXPORT_CONTROL_SET_ACTION
void	MSG_EXPORT_CONTROL_SET_ACTION(
        optr	destOD,	/* Send messages to this object. */
        word	ECImsg);	/* Send this message to the above recipient. */

This message changes the values of an ExportControl's ECI _destination and ECI _message fields. These fields indicate what action the ExportControl will take when it is finished preparing a file for export.

Source: Unrestricted.

Destination: Any ExportControl object.

Parameters: recipient Set ECI _destination to this value.

message
Set ECI _message to this value.

Interception: This message should not be intercepted.

Adding to the Import Dialog Box

When the user selects the "Export" trigger or tool, the Export controller brings up a dialog box. The application can, if it wishes, add UI objects to this box. It does so by defining a tree of generic objects (the top object of which must be set "not usable"). It must place an optr to the top object in the tree in ATTR_EXPORT_CONTROL_APP_UI. When the ExportControl builds the dialog box, it will add that optr as one of the children in the tree and set it "usable".

The Import/Export Library: 3 Writing Translation Libraries

GEOS comes with many translation libraries, and more are being added all the time. Geoworks is continually adding new translation libraries for popular formats. Nevertheless, we cannot guarantee to support every format. Developers may decide to write their own translation libraries. This section describes how the libraries work in enough detail that developers will be able to do preliminary work in writing the libraries.

Most applications will find the provided translation libraries sufficient for their needs. Therefore, most developers can skip this section. You should read this if you are planning on writing translation libraries, or just if you want more understanding of the importing and exporting process.

Remember: once a translation library has been written, all a user has to do to install it is copy it to the appropriate directory. All existing applications which use Impex will then automatically be able to import and export that format. Users will be able to buy translation library collections from third-party vendors the way they buy font collections now.

How Translation Libraries Work

Intermediate Formats

The Import/Export Library: 3.1 Writing Translation Libraries: How Translation Libraries Work

A translation library's task is easily stated. It has to do two things: read a native-format file and produce a Metafile translation, and read data in a Metafile and write corresponding data in a native-format file. How easy this is to do depends on the formats involved.

Every translation library specifies what format of Metafile it expects to work with. For example, the FooWrite translation library would translate FooWrite files into IDC_TEXT Metafiles and vice versa. When a user activates the "Import" trigger, he will be presented with a list of formats to use; those formats will correspond to all the libraries which can translate files into formats the application can accept. For example, if the application specified that it could accept text or graphics Metafiles, the user's choice of format would depend on which of the installed libraries could generate text or graphics Metafiles.

The translation library can also suggest a file mask. For example, the FooWrite translation library might specify that FooWrite data files meet the pattern "*.FOO". By default, the Import file selector will show only the files that match the library's mask. However, the user can override this mask, setting a different one or no mask at all.

When a user decides to import a file, the ImportControl opens the source file and creates a temporary transfer VM file. The ImportControl then starts up the appropriate translation library and passes the two file handles to it. The translation library should read the entire source file, translate it to the appropriate Metafile format, and write it to a VM chain in the transfer file. It then returns the VMBlockHandle of the head of the VMChain to the ImportControl. If it was unable to translate the file, it should return a null handle; the ImportControl then displays an appropriate error message. The ImportControl will close the source file automatically.

When a user decides to export a file, the ExportControl creates a temporary transfer file and opens an empty native-format file for the output. The ExportControl then calls the translation library. The library is passed the handles of the two files, as well as the handle of the Metafile VM chain. The library reads the Metafile and writes an appropriate data file. When it is finished, it notifies the ExportControl, which automatically closes the destination file and destroys the temporary transfer file.

The Import/Export Library: 3.2 Writing Translation Libraries: Intermediate Formats

Many libraries will want to make use of intermediate formats. For example, a company may have defined its own transfer format for its applications. The simplest way for it to translate files into the GEOS Metafile might be to translate the file into its own transfer format, then translate from this format into the Metafile format. GEOS supports this with its use of intermediate translation libraries.

An intermediate library is much like an ordinary translation library. Like other libraries, it must translate from its own format into the Metafile format. The only difference is that intermediate libraries are not called by the Impex objects; instead, they are called by other translation libraries.

For example, suppose FooWare, Inc., has a line of graphic FooApps which includes FooDraw, FooPaint, FooSketch, and FooScribble. FooWare has developed its own file-transfer format, FooInterchangeFormat (FIF); it has code written to translate any FooApp's files into FIF and back.

FooWare now wants to write translation libraries for GEOS. The first thing FooWare does is write an intermediate translation library which takes a DOS file containing FIF data and produces a VM chain containing a GEOS graphic Metafile, and vice versa. Once this is written, FooWare has an easy time writing the actual translation libraries. For example, the FooPaint translation library imports files by converting a FooPaint data file into the analogous FIF file. Since FooWare already has routines to do this translation, it just has to port existing code to the GEOS library. The FooPaint translation library then calls the FIF-to-Metafile intermediate translation library and gets the finished translation. Similarly, the FooPaint translation library exports data by calling the FIF-to-Metafile library to produce a FIF version of the data; it can then use ported code to produce an actual FooPaint file.

Note that any ordinary translation library can also be used as an intermediate translation library. For example, let's suppose that FooWare has code written to translate FIF files into PostScript files. This makes translating the documents even easier. The FIF-to-Metafile library can just use ported code to produce a PostScript version of the data; it can then call the PostScript-to-Metafile translation library, which is provided with GEOS. GEOS comes with translation libraries for many popular file-interchange formats; thus, many developers will be able to write translation libraries just by porting code from their pre-existing translators, then calling one of the GEOS translation libraries.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Fine Print

GEOS SDK Documentation: Fine Print

| Up: GEOS SDK TechDocs

Geoworks provides this publication "as is" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability or fitness for a particular purpose. Geoworks may revise this publication from time to time without notice. Geoworks does not promise support of this documentation. Some states or jurisdictions do not allow disclaimer of express or implied warranties in certain transactions; therefore, this statement may not apply to you.

Copyright © 1997 by Geoworks, Incorporated. All rights reserved. Published 1997.

Geoworks®, Geoworks Ensemble®, Ensemble®, GEOS®, PC/GEOS®, GeoDraw®, GeoManager®, GeoPlanner®, GeoFile®, GeoDex®, and GeoComm® are registered trademarks of Geoworks in the United States and other countries.

Geoworks® Pro, PEN/GEOS, Quick Start, GeoWrite, GeoBanner, GeoCrypt, GeoCalc, GeoDOS, Geoworks® Writer, Geoworks® Desktop, Geoworks® Designer, Geoworks® Font Library, Geoworks® Art Library, Geoworks® Escape, Lights Out, and Simply Better Software are trademarks of Geoworks in the United States and other countries.

Trademarks and service marks not listed here are the property of companies other than Geoworks. Every effort has been made to treat trademarks and service marks in accordance with the United States Trademark Association's guidelines. Any omissions are unintentional and should not be regarded as affecting the validity of any trademark or service mark.

| Up: GEOS SDK TechDocs


Navigating this Documentation

GEOS SDK Documentation: How to Find Your Way Around

| Up: GEOS SDK TechDocs

Most pages of this documentation set have green triangles at the top and bottom of the page. These are links to other pages.

Up to the top of the GEOS SDK Documentation

Up to this page's "parent"--the section that includes this subsection, the chapter that includes this section, or the top of the GEOS SDK Documenation.

Down to this page's first "child"--the first section in this chapter or the first subsection of this section

Next links sideways to this page's next "sibling"--the next subsection within the same section or the next section within the same chapter. If there is no next "sibling" but there are more pages in the chapter, this triangle will still appear, but at a slight angle:

Previous, like [image], but links to the previous "sibling".

Index links--you'll see these triangles in the index. Some index entries which have more than one triangle, such as

Files x x

In this case, each of the triangles links to a different place in the documentation. Try each one to find out which one you want.

| Up: GEOS SDK TechDocs


Documents

The GEOS document control objects let the programmer ignore most of the details of opening, closing, and saving files. The programmer just specifies the characteristics the application will expect of its documents. The document control presents all dialog boxes and notices to the user, and it maintains the "Save," "Save As," "Open," "New," and "Revert" triggers in the File menu.

There are three parts to the document control: the GenDocumentControl object, which maintains the user interface; the GenDocuments, each of which manages a single document; and the GenDocumentGroup object, which creates and manages GenDocument objects as needed. These classes can all be subclassed to add functionality.

1 Document Control Overview
1.1 The Document Control Objects
1.2 Document Control Interaction
1.3 Document Control Models
2 Document Control Data Fields
2.1 GenDocumentControl Data
2.2 GenDocumentGroup Data
2.3 GenDocument Attributes
3 Basic DC Messages
3.1 Other Document Group Messages
3.2 From the Doc Control Objects
4 Advanced DC Usage
4.1 Document Protocols
4.2 Multiple Document Model
4.3 Working with DOS files
4.4 Special-Purpose Messages
4.5 Forcing Actions
4.6 Some Other Internal Messages

Documents: 1 Document Control Overview

A program that creates or uses files--in short, almost all applications--has to deal with many tasks. It has to present dialog boxes to the user so he can choose which file to open; it must give alert messages when the user tries to do something dangerous, like quit without saving; and it must notice when GEOS is being shut down and make sure it saves appropriate data.

The document control objects make these tasks much easier. They take care of some of these tasks by themselves; for example, they maintain the File menu commands and display dialog and alert boxes as needed. The Document Control objects make other application jobs much easier by sending messages at appropriate times. They can also manage several documents at once, making it much easier for applications to manage multiple documents.

Many different document control sample applications are provided with the SDK. In most cases, you should be able to get your document control by copying over code from the appropriate sample application, then customizing it.

The Document Control Objects

Document Control Interaction

Document Control Models

Documents: 1.1 Document Control Overview: The Document Control Objects

There are three different classes of objects which together constitute the document control. These are GenDocumentControlClass , GenDocumentGroupClass , and GenDocumentClass . A document will need at least one of each to use the document control technology. The relationship between the objects is diagramed below.

GenDocumentControlClass

An application will have one object of class GenDocumentControlClass . This object manages the user interface: It presents some dialog and alert boxes, it manages the File Selector, it creates appropriate entries for the File menu, and it updates the enabled/disabled states of these items (e.g. it disables the Save trigger after the file has been saved).

The GenDocumentControl is generally made a child of the file menu, which is itself a child (or descendant) of the GenPrimary. The GenDocumentControl is on the GenPrimary's active list. It has no children, but it does have an optr to the GenDocumentGroup object. By convention, the GenDocumentControl object is in the same data segment as the GenPrimary object. Certain attributes of the GenDocumentControl object will determine the characteristics of the File Selector and other UI gadgets.

GenDocumentControlClass is a subclass of GenControlClass . This means that you can set up toolboxes to perform the "Save," "Open," etc., actions. For more details, see the Controllers chapter.

GenDocumentGroupClass

An application will have one object of class GenDocumentGroupClass . This object creates and manages the document objects. Ordinarily, the document objects belong to GenDocumentClass . However, if the program wishes to alter the behavior of the document object, it can create a subclass of GenDocumentClass . In this case, the GenDocumentGroup object will contain a pointer to the class definition of the document subclass, and will create document objects of this class as needed.

The GenDocumentGroup object is a child of any object. It does not have any children when it is declared; however, it will dynamically give itself document-object children at run-time. It is in its own data segment. Certain of its attributes determine what the attributes of its document children will be.

GenDocumentClass

Each GenDocument object manages a single open file. It keeps track of the volume, path, and filename for the document, the dirty state of the document, and other document-related information. It opens and closes files and presents file-related dialog boxes (e.g., "Save changes before closing?"). Programs often define a subclass of the GenDocumentClass which has additional, application-related functionality. In this case, objects of the subclass are used instead of objects of GenDocumentClass .

GenDocumentClass is a subclass of GenContentClass . Therefore, document objects can receive the output of GenView objects. GenDocument objects have all the functionality of GenContent objects. GenContent objects are themselves subclassed from GenClass . Very few applications will use the GenContent objects directly; for that reason, they are documented in this chapter.

Document objects are not declared in the source code; they are created at run-time by the GenDocumentGroup object. The document currently active is called the target document .

Documents: 1.2 Document Control Overview: Document Control Interaction

A simple user operation will usually involve all three types of objects. For example, suppose the user selects the Open trigger from the File menu. The following actions will be taken:

  1. The Open trigger sends a MSG_GEN_DOCUMENT_CONTROL_INITIATE_OPEN_DOC to the GenDocumentControl object. The GenDocumentControl object responds by displaying a file selector.
  2. When the user selects a file and clicks "OK," the GenDocumentControl object gets the selected file's path from the file selector object.
  3. The GenDocumentControl object sends a MSG_GEN_DOCUMENT_GROUP_OPEN_DOC to the GenDocumentGroup, passing the name and path of the file to open.
  4. The GenDocumentGroup creates a document object, either of class GenDocumentClass or of a programmer-specified class. The GenDocumentGroup object sends a MSG_GEN_DOCUMENT_OPEN to the document object, passing the file and path name to be opened.
  5. The document object opens the file specified and handles errors appropriately. It then sends messages to the application instructing it to create the UI and initialize the file.

Most of these steps are transparent to the application programmer. Typically, an application will intercept MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE ; the programmer needs only to write this handler, not all the code needed for the above steps.

Documents: 1.3 Document Control Overview: Document Control Models

GEOS allows two distinct models of document control, a Procedural model and an Object model. While the two models use the same objects, they embody different programming philosophies.

The Procedural model of document control is much like traditional, non-object-oriented programming. Under this model, whenever a situation arises that needs the application's attention, the document control objects will send a message to a single object (generally the Process object). This object handles all of these situations.

Under the Object model of document control, the application defines a subclass of GenDocumentClass . This new document class has methods to handle situations needing the application's attention. This model is based on the philosophy of object-oriented programming; each document object has code to handle situations arising for that document.

The main difference between the two models is where the messages are sent. Under the Procedural model, messages are sent to the Process object; under the Object model, messages are sent to the appropriate document object. Every message sent in the Procedural model corresponds to a message sent in the Object model.

The Procedural model is simpler to use; it does not require the application to subclass objects. It is thus well-suited for simple applications which will have only one file open at a time. It may also be an easier model for programmers who are new to object-oriented programming. The Object model, on the other hand, is best suited for applications which will have many documents open at once; the application can let every document object manage a single document without worrying about other open documents.

The Procedural Model

The Procedural model of document control is much like traditional, procedure-oriented programming. This model is simpler to implement than the object-model. It is well suited for simple applications which have only one document open at a time.

Under the Procedural model of document control, every time a situation arises which requires the application's attention, the document control objects will send an appropriate message to the GenDocumentGroup object's output. These messages are imported from MetaClass , so all objects can handle them. The application will generally use global variables for run-time data storage.

For example, when a new document needs to be initialized, the document control sends a MSG_META_DOC_OUTPUT_INITIALIZE_DOCUMENT_FILE to its output object. The output object takes any appropriate steps (e.g., storing the file handle, setting up the map block, etc.).

The Object Model

The Object model of document control is better suited to advanced applications and applications which will have more than one document open at a time. Under this model, the application defines a subclass of GenDocumentClass . This new document class has handlers for situations requiring the application's attention. It also has local variables (i.e., instance data fields) which store any information the application will need about this document.

Whenever a situation arises that needs the application's attention, the relevant document object will send a message to itself. This document object will then handle the message. For example, an application might define its own document class, MyAppDocumentClass (a subclass of GenDocumentClass) . Suppose a new document has been created and needs to be initialized. First, the GenDocumentGroup object will create a new document object by instantiating an object of MyAppDocumentClass . Next, the new document object will send itself a MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE. MyAppDocumentClass will have a handler for this message; the handler will initialize the file as well as the document object's data structures.

Messages Under the Two Models

The simplest way to show the difference between the two models is to see how a single event is handled. This section examines one specific case, in which a document needs to be initialized; other cases are handled analogously.

Suppose a situation arises needing the application's attention; for example, a document is created and needs to be initialized. First, the document object will send an appropriate message to itself. In this case, it would send itself the message MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE . If the application uses a subclass of GenDocumentClass and this subclass has a handler for MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE , the messager will call that method; otherwise, the messager will call the handler defined for this message by GenDocumentClass . The handler in GenDocumentClass will find out the GenDocumentGroup object's output optr. If this optr is non-null, the handler will send an appropriate message (in this case, MSG_META_DOC_OUTPUT_INITIALIZE_DOCUMENT_FILE ) to the output object.

Note that, under normal circumstances, the application will handle only one of the two messages. For example, if the application writes a handler for MSG_GEN_DOCUMENT_PHYSICAL_SAVE , the handler defined by GenDocumentClass will not be called; as a result, MSG_META_DOC_OUTPUT_PHYSICAL_SAVE will not be sent to the GenDocumentGroup object's output. This is not usually a problem, since the application will generally handle one message or the other. If, for some reason, it needs to have both messages sent, the handler for MSG_GEN_DOCUMENT_SAVE should contain a @callsuper instruction.

Documents: 2 Document Control Data Fields

This section describes the attributes of the two document control classes, GenDocumentControlClass and GenDocumentGroupClass , as well as of the GenDocumentClass . Note that GenDocument objects are created at run-time, and their attributes are initialized by the creating GenDocumentGroup object. However, their attributes can be changed with the appropriate messages.

Many of the messages to the GenDocumentControl have corresponding messages to the GenDocumentGroup. For example, to find out the GenDocumentControl's attributes, one can either send a MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS to the GenDocumentControl or send a MSG_GEN_DOCUMENT_GROUP_GET_UI_ATTRS to the GenDocumentGroup. In either case, the message will return the same result. It is sometimes more convenient to send a message to the GenDocumentGroup object; for example, a document object can do this with @call @genParent::<message> .

GenDocumentControl Data

GenDocumentGroup Data

GenDocument Attributes

Documents: 2.1 Document Control Data Fields: GenDocumentControl Data

The GenDocumentControl handles interaction between the document control and the user. It maintains the File menu entries and manages the file selector. Its attributes all relate to these duties. A complete list of the attributes follows in GenDocumentControl Instance Data along with comments and the default values.

Some of these data fields can be examined but not set by the application. Data fields for purely internal use (which are neither examined, nor set, by applications) are not listed.

Code Display 13-1 GenDocumentControl Instance Data

/* The GDCI_documentToken field specifies the token characters and token ID of
 * files managed by this document control. All files created by the document
 * control will have these token characters, and the File Selector object will be
 * set to allow only such files to be selected. This attribute is ignored if DOS
 * files are being opened. */
    @instance GeodeToken			GDCI_documentToken = {};
/* GDCI_selectorType determines which files will be displayed by the File Selector
 * generated by this object. Only one of the options may be set. By default, only
 * documents are visible. */
    @instance GenFileSelectorType				GDCI_selectorType = GFST_DOCUMENTS;
    /* Types available:
     *	GFST_DOCUMENTS, 			GFST_EXECUTABLES,
     * 	GFST_NON_GEOS_FILES, 			GFST_ALL_FILES */
/* GDCI_attrs specifies certain characteristics of the file to be opened. The
 * default setting is shown below. */
    @instance GenDocumentControlAttrs					GDCI_attrs =
            ((GDCM_SHARED_SINGLE << GDCA_MODE_OFFSET) |
             GDCA_VM_FILE |
             GDCA_SUPPORTS_SAVE_AS_REVERT |
             (GDCT_NEW << GDCA_CURRENT_TASK_OFFSET))
    /* Attributes available:
     * GDCA_MODE:
     *	GDCM_VIEWER, 			GDCM_SHARED_SINGLE,
     *	GDCM_SHARED_MULTIPLE
     * GDCA_CURRENT_TASK:
     * 	GDCT_NONE, 			GDCT_NEW,
     * 	GDCT_OPEN, 			GDCT_USE_TEMPLATE,
     *	GDCT_SAVE_AS,			GDCT_COPY_TO,
     *	GDCT_DIALOG,			GDCT_TYPE,
     *	GDCT_PASSWORD
     * Other fields:
     * 	GDCA_MULTIPLE_OPEN_FILES,				GDCA_DOS_FILE_DENY_WRITE,
     *	GDCA_VM_FILE, 				GDCA_NATIVE,
     * 	GDCA_SUPPORTS_SAVE_AS_REVERT,				GDCA_DOCUMENT_EXISTS,
     *	GDCA_DO_NOT_SAVE_FILES
     */
/* GDCI_features specifies certain extra features of the document control. The
 * default setting is shown below. */
    @instance GenDocumentControlFeatures					GDCI_features =
            (GDCF_READ_ONLY_SUPPORTS_SAVE_AS_REVERT |
             GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN |
             GDCF_SUPPORTS_TEMPLATES |
             GDCF_SUPPORTS_USER_SETTABLE_EMPTY_DOCUMENT |
             GDCF_SUPPORTS_USER_MAKING_SHARED_DOCUMENTS |
             GDCF_NAME_ON_PRIMARY);
    /* Flags available:
     *	GDCF_READ_ONLY_SUPPORTS_SAVE_AS_REVERT,
     *	GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN,
     * 	GDCF_SUPPORTS_TEMPLATES,
     *	GDCF_SUPPORTS_USER_SETTABLE_EMPTY_DOCUMENT,
     *	GDCF_SUPPORTS_USER_SETTABLE_DEFAULT_DOCUMENT,
     *	GDCF_SUPPORTS_USER_MAKING_SHARED_DOCUMENTS,
     *	GDCF_NAME_ON_PRIMARY */
/* GDCI_enableDisableList specifies objects which should be enabled whenever a
 * document is opened and disabled when all documents are closed. The field is the
 * handle of a chunk containing a list of optrs to the objects to be enabled and
 * disabled. The default value is a null handle.*/
    @instance ChunkHandle			 	GDCI_enableDisableList;
/* The GDCI_openGroup, GDCI_importGroup, GDCI_useTemplateGroup, GDCI_saveAsGroup,
 * GDCI_exportGroup, and GDCI_userLevelGroup attributes hold optrs to groups of UI
 * objects to be added to the "Open," "Import," "Use Template," "Save As,"
 * "Export," and "User Level", dialog boxes, respectively. The GDCI_dialogGroup
 * field holds an optr to objects to be added to the opening dialog box. The optr
 * is to the head of a tree of UI objects. The top object in the tree should be
 * set "not usable." Default values are all null optrs. */
    @instance optr				GDCI_openGroup;
    @instance optr				GDCI_importGroup;
    @instance optr				GDCI_useTemplateGroup;
    @instance optr				GDCI_saveAsGroup;
    @instance optr				GDCI_exportGroup;
    @instance optr				GDCI_dialogGroup;
    @instance optr				GDCI_userLevelGroup;
/* If the GDCI_features field includes "displayNameOnPrimary" but no document is
 * open, the Primary's moniker is set to the string pointed to by the
 * GDCI_noNameText attribute. The default value is a null chunk handle. */
    @instance ChunkHandle				GDCI_noNameText;
/* If the GDCA_currentTask section of the GDCI_attrs field is set to
 * GDCT_NONE on startup, then the file specified by GDCI_defaultFile will be
 * opened (and, if necessary, created). The file is specified by a chunk handle of
 * a null-terminated string; this string should specify the file's path relative
 * to the SP_DOCUMENT standard path.The default value is a null chunk handle,
 * indicating that if the startup value of GDCA_currentTask is GDCT_NONE, no
 * documents should be opened. */
    @instance ChunkHandle 				GDCI_defaultFile;
/* GDCI_templateDir is the chunk handle of a null-terminated text string which
 * specifies a directory to hold template documents. The directory is specified
 * relative to SP_TEMPLATE. If not set by you, this defaults to the SP_TEMPLATE
 * standard path. */
    @instance ChunkHandle				GDCI_templateDir;
/* GDCI_documentGroup is an optr to the GenDocumentGroup object. You must set
 * this field. */
    @instance optr			GDCI_documentGroup;
/* GDCI_targetDocName is a character array. It is set to contain the name of
 * the current target file. This field is automatically updated by the document
 * control. */
    @instance FileLongName				GDCI_targetDocName = "";
/* The Document Control automatically displays a big dialog box at startup which
 * lets the user choose to create, open, etc. a file. Each option has a button
 * (with a picture) and an explanatory text. You can override the default graphic
 * or text by setting any of the following fields:
 */
    @instance ChunkHandle				GDCI_dialogNewText;
    @instance ChunkHandle				GDCI_dialogTemplateText;
    @instance ChunkHandle				GDCI_dialogOpenDefaultText
    @instance ChunkHandle				GDCI_dialogImportText;
    @instance ChunkHandle				GDCI_dialogOpenText;
    @instance ChunkHandle				GDCI_dialogUserLevelText;
    @instance @visMoniker				GDCI_dialogNewMoniker;
    @instance @visMoniker				GDCI_dialogTemplateMoniker;
    @instance @visMoniker				GDCI_dialogOpenDefaultMoniker
    @instance @visMoniker				GDCI_dialogImportMoniker;
    @instance @visMoniker				GDCI_dialogOpenMoniker;
    @instance @visMoniker				GDCI_dialogUserLevelMoniker;

The Document Token

GDCI_documentToken, MSG_GEN_DOCUMENT_CONTROL_GET_TOKEN, MSG_GEN_DOCUMENT_GROUP_GET_TOKEN

The document control's file selector will display only those files whose document token matches the GenDocumentControl object's GDCI_documentToken attribute. All files created by the application will have the specified document tokens. There are no messages to alter the token attributes at run-time. (If the document control is used to manage DOS files, the file selector will show all non-GEOS files.)

A token is defined by a GeodeToken structure. The format of this structure is shown below. The first field, GT_chars , will vary for each document type. The second, GT_manufID , will be the same for the tokens of all applications and documents created by a given company.

typedef struct {
    char 		GT_chars[TOKEN_CHARS_LENGTH];
                /* TOKEN_CHARS_LENGTH = 4 */
    ManufacturerID 		GT_manufID;
                /* word-sized integer */
} GeodeToken;

The message MSG_GEN_DOCUMENT_CONTROL_GET_TOKEN instructs the GenDocumentControl object to write a copy of the document token to a specified address. The message has one argument: the address of a GeodeToken . MSG_GEN_DOCUMENT_GROUP_GET_TOKEN functions identically, but it is sent to the GenDocumentGroup object.

You can also find out the application's token by sending GEN_DOCUMENT_CONTROL_GET_CREATOR or GEN_DOCUMENT_GROUP_GET_CREATOR to the appropriate object. The application's token will be used as the "creator token" for any documents created by the document control.

MSG_GEN_DOCUMENT_CONTROL_GET_TOKEN
void 	MSG_GEN_DOCUMENT_CONTROL_GET_TOKEN(
        GeodeToken *		token); /* address to copy token to */

This message gets the document token values for all documents created by this document control.

Source: Unrestricted.

Destination: Any GenDocumentControl object.

Parameters: token A pointer to an empty GeodeToken structure.

Return: The document GeodeToken is written to the variable whose address is passed.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_GET_TOKEN
void 	MSG_GEN_DOCUMENT_GROUP_GET_TOKEN(
        GeodeToken *		token); /* address to copy token to */

This is the same as MSG_GEN_DOCUMENT_CONTROL_GET_TOKEN , except that it is sent to the GenDocumentGroup object instead of the GenDocumentControl object.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: token A pointer to an empty GeodeToken structure.

Return: The document GeodeToken is written to the variable whose address is passed.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_CONTROL_GET_CREATOR
void 	MSG_GEN_DOCUMENT_CONTROL_GET_CREATOR(
        GeodeToken *		token); /* address to copy token to */

This message gets the token for the application; this token is the "creator token" for all files created by the document control.

Source: Unrestricted.

Destination: Any GenDocumentControl object.

Parameters: token A pointer to an empty GeodeToken structure.

Return: The document GeodeToken is written to the variable whose address is passed.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_GET_CREATOR
void 	MSG_GEN_DOCUMENT_GROUP_GET_CREATOR(
        GeodeToken *		token); /* address to copy token to */

This is the same as MSG_GEN_DOCUMENT_CONTROL_GET_CREATOR , except that it is sent to the GenDocumentGroup object instead of the GenDocumentControl object.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: token A pointer to an empty GeodeToken structure.

Return: The document GeodeToken is written to the variable whose address is passed.

Interception: You should not subclass this message.

The GDCI_selectorType Field

GDCI_selectorType

The GDCI_selectorType field determines what files will be displayed by and can be opened with the file selector. The options are stored as a byte-sized enumerated type. The options are:

GFST_DOCUMENTS
This is the default option. The file selector will display those documents with the appropriate tokens.
GFST_EXECUTABLES
The file selector will display executable files as well as appropriate document files.
GFST_NON_GEOS_FILES
The file selector will display all non-GEOS files (and only non-GEOS files).
GFST_ALL_FILES
The file selector will display all files.

The GDCI_attrs Field

GDCI_attrs, MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS, MSG_GEN_DOCUMENT_GROUP_GET_UI_ATTRS

The GenDocumentControl object has eight attribute flags stored in the word-sized bitfield GDCI_attrs . They may be retrieved by sending MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS to the GenDocumentControl object or by sending MSG_GEN_DOCUMENT_GROUP_GET_UI_ATTRS to the GenDocumentGroup object. The attributes are set at coding time; there is no way for an application to change the attributes at run-time, although the GenDocumentControl will change some of the attributes to reflect its current state.

The attributes are

GDCA_MULTIPLE_OPEN_FILES
Allow several documents to be open at once. If this attribute is disabled, the "New" and "Open" triggers will be disabled when a document is open (however, see also the description of the flag GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN on GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN This feature is ignored if the attribute GDCA_MULTIPLE_OPEN_FILES is on. If GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN is on and GDCA_MULTIPLE_OPEN_FILES is off, the "New" and "Open" triggers are enabled when the doc ). This attribute defaults to off.
GDCA_MODE
This is a two-bit field. GDCA_MODE is a mask of all the bits in this field; the offset of this field is equal to the constant GDCA_MODE_OFFSET. The field has the following possible settings:
GDCM_VIEWER
All documents are opened in read-only mode; the New, Save, Save As, and Revert triggers are permanently disabled. Other applications can open the file for read/write access.
GDCM_SHARED_SINGLE
Documents are opened for reading and writing. When a document is open, it is marked "deny-write" so other applications can open the file only for read-only access. The user can mark a document as a "public" document, in which case the default is to open a file "read-only." The default GDCA_MODE setting is GDCM_SHARED_SINGLE.
GDCM_SHARED_MULTIPLE
This mode is designed for documents that can have multiple writers, such as multi-user databases. Documents are ordinarily opened as in GDCM_SHARED_SINGLE mode above; however, a user can designate a file as a "multi-user" file, which means that it can be opened by several applications at once for read/write access.
The default setting of the GDCA_MODE flag is GDCM_SHARED_SINGLE. If you want a different value, first clear the two-bit field, then set the new setting, like this:
GDCI_attrs = (@default & ~GDCA_MODE) \
    | (GDCM_VIEWER << GDCA_MODE_OFFSET);
Note that the GDCA_MODE attribute has a slightly different effect if the document control manages DOS files. For details, see GDCA_DOS_FILE_DENY_WRITE below.
GDCA_DOS_FILE_DENY_WRITE
This attribute does not matter for VM files. If a DOS file is opened while the GDCA_DOS_FILE_DENY_WRITE bit is set, no other application will be able to write to that file. This is true even if the DOS file was opened for read-only access; however, if the file is a multi-user document opened in "shared-multiple" mode, other applications will be able to write to it regardless of whether the GDCA_DOS_FILE_DENY_WRITE attribute is set. By default, GDCA_DOS_FILE_DENY_WRITE is off.
GDCA_VM_FILE
This attribute specifies whether the document control objects will open GEOS Virtual Memory files (if GDCA_VM_FILE is on), or DOS files (if GDCA_VM_FILE is off). The default value is on.
GDCA_NATIVE
If this bit is set and GDCA_VM_FILE is not set, documents will be stored in the format native to the file-system.
GDCA_SUPPORTS_SAVE_AS_REVERT
This attribute is ordinarily set only for GEOS files. If the attribute is on, the application will use the backup functionality of VM files to support "Save As" and "Revert" functionality. If the attribute is off, the file will be altered whenever it is updated to disk. The default value is on. DOS files do not normally support "Save As" and "Revert." Applications can implement "Save As" and "Revert" functionality for DOS files by defining a subclass of GenDocumentClass with handlers for MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS and MSG_GEN_DOCUMENT_PHYSICAL_REVERT , but this is not recommended. Ordinarily, this attribute should be off for DOS files.
GDCA_DOCUMENT_EXISTS
This attribute is set and maintained at run-time by the GenDocumentControl code. The attribute is on if at least one document is open.
GDCA_CURRENT_TASK
This three-bit attribute has a dual function: It determines the application's behavior at start-up, and it indicates what task the application is currently performing. The mask GDCA_CURRENT_TASK is a mask of all the bits in this field; the field's offset is equal to the constant GDCA_CURRENT_TASK_OFFSET. The possible settings are as follows:
GDCT_NONE
If a default file has been specified (see The GDCI_noNameText Field ), that file will be opened; otherwise, the application will start with no file opened.
GDCT_NEW
A new document will be created at startup. If the GDCF_DIALOG_BOX_FOR_NEW flag is set, a dialog box will be presented at startup.
GDCT_OPEN
The "Open File" dialog box will be presented at startup.
GDCT_USE_TEMPLATE
The "Use Template" dialog box will be presented at startup.
GDCT_SAVE_AS
This is not a valid initial setting. The GDCA_CURRENT_TASK field has this setting between when a user chooses the "Save As" command and when the document is saved.
GDCT_COPY_TO
This is not a valid initial setting. The GDCA_CURRENT_TASK field has this setting between when the user chooses the "Copy To" command and when the command has been fully executed.
GDCT_DIALOG
GDCT_TYPE
GDCT_PASSWORD
None of these are valid initial settings.

If a document is passed in to be opened at startup (as, for example, when a user launches an application by double-clicking a file created by the application), that file will be opened, and the initial setting of GDCA_CURRENT_TASK will be ignored. The document control automatically maintains this bitfield to correspond to whatever action the document control is currently taking. The application can find out what the document control is doing by reading the attributes and checking this field.

GDCA_DO_NOT_SAVE_FILES
If this bit is set, the application will not be able to save files. By setting this bit, you can turn your application into a fully-functioning demo.
GDCA_FORCE_DEMAND_PAGING
If this bit is set, the document will be paged with demand-paging, even if the system ordinarily forces all documents completely into memory.
MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS
GenDocumentControlAttrs 	MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS();

Use this message to find out what the GenDocumentControl object's GDCA_attrs flags are. The attributes cannot be changed by a message; they can only be read.

Source: Unrestricted.

Destination: Any GenDocumentControl object.

Parameters: None.

Return: Returns a word-length bitfield containing GDCA _attrs record.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_GET_UI_ATTRS
GenDocumentControlAttrs 	MSG_GEN_DOCUMENT_GROUP_GET_UI_ATTRS();

This message is the same as the MSG_GEN_DOCUMENT_CONTROL_GET_ATTRS message (see above) except that it is sent to the GenDocumentGroup object.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: None.

Return: A word-length record containing the GDCA _attrs field.

Interception: You should not subclass this message.

The GDCI_features Flags

GDCI_features, MSG_GEN_DOCUMENT_CONTROL_GET_FEATURES, MSG_GEN_DOCUMENT_GROUP_GET_UI_FEATURES, MSG_GEN_CONTROL_CONFIGURE_FILE_SELECTOR

The GDCI_features attribute specifies whether certain optional functionality of the Document Control technology is enabled. The features are determined at coding time; there is no message to change features at run-time. To retrieve the features, send MSG_GEN_DOCUMENT_CONTROL_GET_FEATURES .

GDCF_READ_ONLY_SUPPORTS_SAVE_AS_REVERT
If this feature is on, the "Save As" and "Revert" triggers are enabled when read-only documents are opened. Once the user chooses "Save As," the new file will no longer be opened as "Read Only"; the "Save" trigger will be enabled. If this feature is off, "Save As" and "Revert" triggers are disabled for read-only files. By default, this feature is on.
GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN
This feature is ignored if the attribute GDCA_MULTIPLE_OPEN_FILES is on. If GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN is on and GDCA_MULTIPLE_OPEN_FILES is off, the "New" and "Open" triggers are enabled when the document is "clean" (i.e., the document has not been marked "dirty" since it was opened/created or saved); that is, "New" and "Open" are enabled whenever "Save" is disabled. If the user chooses "New" or "Open" when the document is "clean," the target document is closed and the new document is opened or created. If both GDCA_MULTIPLE_OPEN_FILES and GDCF_SINGLE_FILE_CLEAN_CAN_NEW_OPEN are off, the "New" and "Open" triggers are disabled whenever a document is open. By default, this attribute is on.
GDCF_SUPPORTS_TEMPLATES
If this feature is enabled, the user can save files as templates. If the user chooses the command "Use Template," a copy of the template is opened as a "new" document, and the template is left unchanged. If this feature is disabled, the application cannot create templates. By default, this feature is on.
GDCF_SUPPORTS_USER_SETTABLE_EMPTY_DOCUMENT
If this feature is enabled, the user can designate a file to be the model for all new documents. When the user chooses "New," this "model" document will be copied and the copy will be opened. By default, this feature is on.
GDCF_SUPPORTS_USER_SETTABLE_DEFAULT_DOCUMENT
If this attribute is on, the user can choose a default document (one which will automatically be opened when the application is launched). (See The GDCI_defaultFile Field .)
GDCF_SUPPORTS_USER_MAKING_SHARED_DOCUMENTS
If this attribute is on, the user can save a document as "shared," allowing several processes to access it at once. By default, the attribute is on.
GDCF_NAME_ON_PRIMARY
If this attribute is on, the name of the target document is displayed at the top of the Primary window. The GenDocumentControl object does this by changing the moniker of the GenPrimary object to the name of the target document. If there is no open document, the GenPrimary will display the string specified by the attribute GDCI_noNameText . By default, this attribute is on.

For an added degree of control, you can use MSG_GEN_DOCUMENT_CONTROL_CONFIGURE_FILE_SELECTOR to change the attributes of the document control's file selector.

MSG_GEN_DOCUMENT_CONTROL_GET_FEATURES
GenDocumentControlFeatures 	MSG_GEN_DOCUMENT_CONTROL_GET_FEATURES();

Use this message to retrieve the current GDCI_features flags. The flags cannot be changed at run-time.

Source: Unrestricted.

Destination: Any GenDocumentControl object.

Parameters: None.

Return: The GenDocumentControl's GDCI_features flags.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_GET_UI_FEATURES
GenDocumentControlFeatures 	MSG_GEN_DOCUMENT_GROUP_GET_UI_FEATURES();

Use this message to retrieve the current GDCI_features flags. The flags cannot be changed at run-time.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: None.

Return: The GenDocumentControl's GDCI_features flags.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_CONTROL_CONFIGURE_FILE_SELECTOR
void 	MSG_GEN_DOCUMENT_CONTROL_CONFIGURE_FILE_SELECTOR(
        optr 		fileSelector,
        word 		flags); /* GenDocumentControlAttrs */

Configure file selector. This message can be sub-classed to modify the behavior of the file selectors that the document control uses.

The GDCI_enableDisableList Field

GDCI_enableDisableList

The GenDocumentControl can be set to enable certain UI objects when documents are open. This is done using the GDCI_enableDisableList attribute. This attribute is the chunk handle of a list of object-pointers. Each of the referenced objects should start as disabled. Whenever a document is opened, a MSG_GEN_SET_ENABLED is sent to each object in the list. When the last document is closed, a MSG_GEN_SET_NOT_ENABLED is sent to each object in the list.

Adding to the Dialog Boxes

GDCI_openGroup, GDCI_importGroup, GDCI_useTemplateGroup, GDCI_saveAsGroup, GDCI_exportGroup, GDCI_dialogGroup, GDCI_userLevelGroup

The GenDocumentControl object manages the dialog boxes for many different user actions. The programmer can specify a tree of UI objects to be included with each of these dialog boxes. For example, to add a group of objects to the "Use Template" dialog box, the programmer should put them all in a tree (perhaps by making them all children of a GenInteraction object) and store an object-pointer to the head of the tree in the GDCI_useTemplateGroup attribute. The top object in the tree should be set "not usable."

The GDCI_noNameText Field

GDCI_noNameText

The GenDocumentControl object can be set to display the name of the current target document in the moniker of the GenPrimary window. If this feature is enabled, and no document is opened, the GenPrimary will have its moniker change to the string specified by GDCI_noNameText . If the feature GDCF_DISPLAY_NAME_ON_PRIMARY is disabled, GDCI_noNameText is ignored.

The GDCI_defaultFile Field

GDCI_defaultFile

If the attribute GDCA_CURRENT_TASK is initially set to GDCT_NONE and a default file is specified, the default file is automatically opened at startup. If GDCA_CURRENT_TASK is not initially set to GDCT_NONE, this attribute is ignored. This field holds the chunk handle of a null-terminated string. The string specifies the file's path relative to the SP_DOCUMENT. If the feature GDCF_SUPPORTS_USER_SETTABLE_DEFAULT_DOCUMENT is enabled, this attribute can be changed by the user at run-time. If the file specified does not exist, it is created as an empty document; if the document exists but cannot be opened, no file is opened at startup. If the named document exists but is inappropriate (e.g. it was created by another application), no document is opened at startup.

The GDCI_templateDir Field

GDCI_templateDir

If templates are supported, this is the default directory for opening and saving them. This string specifies a subdirectory to the standard path SP_TEMPLATE. If not explicitly set in the source code, the template directory will default to SP_TEMPLATE.

The GDCI_documentGroup Field

GDCI_documentGroup

The GenDocumentControl and the GenDocumentGroup communicate with each other via messages. To do this, each needs the optr of the other. GDCI _documentGroup is the optr of the GenDocumentGroup object for this application. It is set in the source code and may not be changed at run time.

The GDCI_targetDocName Field

GDCI_targetDocName

This attribute contains the name of the target document. The document control automatically sets and updates this field when necessary.

Documents: 2.2 Document Control Data Fields: GenDocumentGroup Data

The GenDocumentGroup object creates and manages the document objects. In the "process" model of document control, it sends messages to the process object (or some other designated object) when the application needs to take some action. (It sends these messages even when the "object" model is being followed; however, the messages are ignored.)

A list of data fields for the GenDocumentGroup object follows in GenDocumentGroupClass instance data . Some of the data fields can be changed at run-time, and others cannot; a discussion of the data fields follows the listing. If a data field cannot be set or read by the application, it is not discussed.

Code Display 13-2 GenDocumentGroupClass instance data

/* GDGI_attrs is a record that specifies certain basic characteristics of the
 * documents to be managed. The attributes are set in the source code and are not
 * changed at run-time. The default settings are below. */
    @instance GenDocumentGroupAttrs				GDGI_attrs = (GDGA_VM_FILE |
                    GDGA_SUPPORTS_AUTO_SAVE |
                    GDGA_AUTOMATIC_CHANGE_NOTIFICATION |
                    GDGA_AUTOMATIC_DIRTY_NOTIFICATION |
                    GDGA_APPLICATION_THREAD |
                    GDGA_AUTOMATIC_UNDO_INTERACTION |
                    GDGA_CONTENT_DOES_NOT_MANAGE_CHILDREN);
    /* The following flags are available:
     * 	GDGA_VM_FILE,
     *	GDGA_NATIVE,
     *	GDGA_SUPPORTS_AUTO_SAVE,
     *	GDGA_AUTOMATIC_CHANGE_NOTIFICATION,
     * 	GDGA_AUTOMATIC_DIRTY_NOTIFICATION,
     * 	GDGA_APPLICATION_THREAD,
     *	GDGA_VM_FILE_CONTAINS_OBJECTS,
     *	GDGA_CONTENT_DOES_NOT_MANAGE_CHILDREN,
     * 	GDGA_LARGE_CONTENT,
     * 	GDGA_AUTOMATIC_UNDO_INTERACTION */
/* GDGI_untitledName is the name suggested when a new document is
 * first saved. */
    @instance ChunkHandle			GDGI_untitledName;
/* The GenDocumentGroup object creates a document object for each document
 * opened. The attribute GDGI_documentClass is a pointer to the class definition
 * which will be used for document objects. By default, it points to the
 * definition of GenDocumentClass, so document objects belong to GenDocumentClass.
 * If you use a subclass of GenDocumentClass, you must change this attribute to
 * point to the new class. */
    @instance ClassStruc *			GDGI_documentClass =
                    (ClassStruc *) &GenDocumentClass;
/* Ordinarily, the Document Group creates document objects by instantiating an
 * object of the class indicated by GDGI_documentClass. However, it can be
 * instructed instead to duplicate a specific document object for each new
 * document. To arrange this, set the GDGI_genDocument to point to the document
 * object to duplicate. */
    @instance optr 			GDGI_genDocument;
/* If the Procedural model is used, whenever the application needs to take an
 * action, messages will be sent to the output of the GenDocumentGroup.
 * Ordinarily, the output will be the process object. If the Object model is used,
 * this attribute is generally left as a null pointer. */
    @instance optr			GDGI_output;
/* The GenDocumentGroup object communicates with the GenDocumentControl
 * object through messages. To do this, each one needs an object-pointer to the
 * other. This is set in the source code. */
    @instance optr			GDGI_documentControl;
/* The GenDocument (or subclass) objects can behave as Content objects. The
 * document control can automatically connect GenDocument objects to the GenView
 * if told to do so. The GDGI_genView field is an object-pointer to a GenView
 * object. */
    @instance optr			GDGI_genView;
/* In a multiple-document model, the document control can be set up to work with
 * the display control. When this functionality is enabled, the
 * GenDocumentGroup will automatically duplicate a specified block (generally
 * one containing a GenDisplay object), attach the Display object to the specified
 * GenDisplayGroup object, and set the header for the GenDisplay to the name of
 * the document. When the document is closed, the block is freed. */
    @instance optr			GDGI_genDisplay;
/* GDGI_genDisplayGroup points to the GenDisplayGroup which manages the
 * GenDisplays. */
    @instance optr			GDGI_genDisplayGroup;
/* Each GEOS document has a protocol number, which identifies the version of
 * the application that created it. The GDGI_protocolMajor and
 * GDGI_protocolMinor attributes specify the protocol number to be assigned to
 * all documents created by the document control. */
    @instance word			GDGI_protocolMajor = 1;
    @instance word			GDGI_protocolMinor = 0;

The GDGI_attrs Field

GDGI_attrs, MSG_GEN_DOCUMENT_GROUP_GET_ATTRS

This attribute specifies certain characteristics of the documents to be opened. These attributes are generally set in the source code and can not be changed at run-time. They are stored in a word-sized bitfield.

GDGA_VM_FILE
This attribute is on if the documents to be opened are GEOS Virtual Memory files. By default, it is on.
GDGA_NATIVE
This attribute is on if the documents to be opened are in the file system's native format (i.e. they are not GEOS files). By default, it is off.
GDGA_SUPPORTS_AUTO_SAVE
If this attribute is on, the documents will be periodically auto-saved. It works only with VM files (unless you subclass GenDocument to handle MSG_GEN_DOCUMENT_PHYSICAL_UPDATE ; see Working with DOS files ). It works by periodically updating the file to disk. It should probably be turned off if "Save As" and "Revert" are disabled. By default, the attribute is on. The program can temporarily disable auto-save for a document by sending the document object MSG_GEN_DOCUMENT_DISABLE_AUTO_SAVE .
GDGA_AUTOMATIC_CHANGE_NOTIFICATION
If this attribute is on, the GenDocumentGroup object will periodically check all open documents to see if they have been changed by another process. If a document has changed, the document control will send MSG_META_DOC_OUTPUT_DOCUMENT_HAS_CHANGED to the application. This attribute is useful if the application may be reading multi-user files.
GDGA_AUTOMATIC_DIRTY_NOTIFICATION
This attribute is relevant only for GEOS files. If the attribute is on, whenever a file is marked "dirty," the file system will automatically notify the document control. The document control will then take appropriate actions (enable the "Save" trigger, etc.). The document control will also present a "Save changes before closing" dialog box if the document is closed before being saved. If GDGA_AUTOMATIC_DIRTY_NOTIFICATION is off, or if the documents are DOS files, the application will have to notify the document control when the document is dirty. It does this by sending a MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY to the GenDocumentGroup object (under the procedure model), or by sending a MSG_GEN_DOCUMENT_MARK_DIRTY to the document object (under the object model). By default, GDGA_AUTOMATIC_DIRTY_NOTIFICATION is on.
GDGA_APPLICATION_THREAD
If this attribute is on, the GenDocumentGroup object is run by the application thread, as are its (document-object) children. By default, it is on.
GDGA_VM_FILE_CONTAINS_OBJECTS
If the document control manages Virtual Memory Object files, this attribute should be set to on. By default, this attribute is off.
GDGA_CONTENT_DOES_NOT_MANAGE_CHILDREN
The application's main VisContent, if any, does not manage its children. By default, this attribute is on.
GDGA_LARGE_CONTENT
The application's main VisContent uses the large model. By default, this attribute is off.
GDGA_AUTOMATIC_UNDO_INTERACTION
The application sends out undo set-context messages automatically as necessary.
MSG_GEN_DOCUMENT_GROUP_GET_ATTRS
GenDocumentGroupAttrs 	MSG_GEN_DOCUMENT_GROUP_GET_ATTRS();

Use this message to find out the attributes of the GenDocumentGroup object. Note that the attributes cannot be changed at run-time; they can only be examined.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: None.

Return: Flags in GDGI _attrs bitfield.

Interception: You should not subclass this message.

The GDGI_untitledName Field

 GDGI_untitledName, MSG_GEN_DOCUMENT_GROUP_GET_DEFAULT_NAME

The first time a new document is saved, the document control presents a "Save As" dialog box. If the GDGI_untitledName field is set to point to a string, that string will be suggested as the name of the document. If the attribute is not set, no name will be suggested. The current default name can be retrieved by sending MSG_GEN_DOCUMENT_GROUP_GET_DEFAULT_NAME to the GenDocumentGroup object.

MSG_GEN_DOCUMENT_GROUP_GET_DEFAULT_NAME
GenDocumentGroupAttrs 	MSG_GEN_DOCUMENT_GROUP_GET_DEFAULT_NAME(
        char *buffer); /* Address to write default name */

This message instructs the GenDocumentGroup object to copy the GDGI_untitledName attribute to the specified address. In addition, the message will return the GDGI_attrs word of the GenDocumentGroup object. If you just want the attributes, use MSG_GEN_DOCUMENT_GROUP_GET_ATTRS .

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: buffer A pointer to a character buffer. This buffer should be of length FILE_LONGNAME_BUFFER_SIZE or greater.

Return: The record of flags stored in GDGI_attrs .

*buffer
Null-terminated name string.

Interception: You should not subclass this message.

Warnings: Make sure the buffer is long enough to hold any file name. Otherwise, the method may overwrite data after the buffer. The constant FILE_LONGNAME_BUFFER_SIZE, defined in file.def , is equal to the maximum file name length, counting the null terminator.

The GDGI_documentClass Field

GDGI_documentClass

Each time a document is opened, the GenDocumentGroup creates a document object. Ordinarily, the document object is a member of GenDocumentClass . However, sometimes the programmer will want to add functionality to the document objects, doing so by defining a subclass of GenDocumentClass . (For example, in the object model of document control, the program implements most functionality by defining new methods for the document class.) If this is the case, the programmer will have to make sure the GenDocumentGroup object creates document objects from the new class. One can do this by setting the GDGI_documentClass field to point to the class structure of the new document object class. By default, this field points to GenDocumentClass .

The GDGI_genDocument Field

GDGI_genDocument

Ordinarily, the document group creates new document objects by instantiating objects from the class specified in GDGI _documentClass . However, you can instead provide a document object for the document group to duplicate. To do this, set the GDGI _genDocument field to the optr of the "template" document object. This object should be marked "not usable."

The GDGI_output Field

GDGI_output, MSG_GEN_DOCUMENT_GROUP_GET_OUTPUT, MSG_GEN_DOCUMENT_GROUP_SET_OUTPUT

Every time something happens which needs to be handled by the application, the document control notifies the application in two ways: The relevant document object sends a message to itself, and the GenDocumentGroup object sends a message to its designated output object. In the Procedural model of document control, the document-object messages are ignored, and the GenDocumentGroup messages are sent to an object (usually the process object) which has handlers for the messages. In the Object model, the GDGI_output attribute is left as a null pointer, and GenDocumentClass is subclassed to handle the messages.

MSG_GEN_DOCUMENT_GROUP_GET_OUTPUT
optr	MSG_GEN_DOCUMENT_GROUP_GET_OUTPUT();

Under the Procedural model of document control, the GenDocumentGroup sends messages to a designated output object. To get the optr of that output object, send this message to the GenDocumentGroup object.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Return: Returns the optr of the document group's output object.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_SET_OUTPUT
void	MSG_GEN_DOCUMENT_GROUP_SET_OUTPUT(
        optr	output); /* The new recipient of the GenDocumentGroup's
        output messages */

Under the Procedural model of document control, the GenDocumentGroup object sends messages to a designated output object. Use this message to change the recipient of the output.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: output The optr of the object which will receive the output.

Interception: You should not subclass this message.

The GDGI_documentControl Field

GDGI_documentControl

The GenDocumentControl and the GenDocumentGroup communicate with each other via messages. To do this, each needs an optr to the other. GDCI_documentControl is an optr to the GenDocumentGroup object for this application. It is set in the source code and is not changed at run time.

Dynamically Creating Displays

GDGI_genDisplay, GDGI_genDisplayGroup, MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY, MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY_GROUP

In a multi-document application, each document will ordinarily have its own GenDisplay object and often many other UI objects as well. The document control can be instructed to dynamically create a number of objects for each new document and destroy these objects when the document is closed.

If an application is going to have the document control create and manage GenDisplay objects, it must declare a GenDisplayGroup object. The GenDisplayGroup should be declared normally; however, it should be given no children. In the source code, the GenDocumentGroup object's GDGI_genDisplayGroup data field should contain an optr to the GenDisplayGroup object.

The application should also declare a template resource. This resource should contain a single generic tree; the top object in this tree should be a GenDisplay object which is set "not usable." The GenDocumentGroup object's GDGI_genDisplay field should contain an optr to that GenDisplay.

When a new document object is created, the document control will automatically copy the resource containing the GenDisplay referenced by GDGI_genDisplay , make the new GenDisplay a child of the GenDisplayGroup referenced by GDGI_genDisplayGroup , and set the new GenDisplay as "usable." When the document object is destroyed (because the document is closed), the document control will automatically destroy that document's copy of the resource.

MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY
optr	MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY();

The GDGI_genDisplay field can be set to point to a GenDisplay object. If GDGI_genDisplay is not a null optr, then the document control will duplicate the resource containing the referenced GenDisplay whenever a new document object is created. The duplicate GenDisplay is made a child of the GenDisplayGroup object indicated by GDGI_genDisplayGroup . By using this message, you can get an optr to that "template" display object. Any changes made to that object will be copied whenever a new document object is created.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Return: Returns the optr of the "template" GenDisplay.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY_GROUP
optr	MSG_GEN_DOCUMENT_GROUP_GET_DISPLAY_GROUP();

If a GenDisplayGroup object is used to manage GenDisplay objects, the GenDocumentGroup object will contain an optr to the GenDisplayGroup. By using this message, you can get an optr to the GenDisplayGroup object.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Return: Returns the optr of the GenDisplayGroup object.

Interception: You should not subclass this message.

Connecting Documents with a GenView

GDGI_genView, MSG_GEN_DOCUMENT_GROUP_GET_VIEW

The document control can be instructed to automatically connect the output of a GenView to the document object associated with the view. That way, the document object can handle all the messages relating to the view. This is naturally only done when the application is using the Object model; if it is using the Procedural model, a GenView will most likely send its messages to the Process object.

There are two ways to enable this functionality. One way is appropriate only to single-document applications; the other is appropriate to multi-document applications.

A single-document application using the Object model should declare the GenView normally as part of the generic tree. (It might well be placed on the GenDocumentControl object's GDCI_enableDisableList .) The source code should set the GDGI_genView field to be an optr to the GenView. When a document is opened, the document control will automatically set the GenView object to direct its output to the document object.

A multi-document application using the Object model should use the document control's ability to create and manage GenDisplay objects. The application will have a resource which is duplicated for each open document. This resource will contain a generic tree, at the head of which is a GenDisplay. To use a GenView, all the application has to do is put a GenView in the tree headed by that GenDisplay, and set GDGI_genView to point to that GenView. When a document object is created, the document control will automatically have the new GenView (in the duplicate resource) send its output to the new GenDocument.

MSG_GEN_DOCUMENT_GROUP_GET_VIEW
optr MSG_GEN_DOCUMENT_GROUP_GET_VIEW();

The GenDocumentGroup object can be set to automatically link document objects to GenView objects. Use this message to find out what the designated GenView is. If there is no such GenView , this message will return a null optr.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Return: The optr of the GenView object (specified in GDGI _genView).

Interception: You should not subclass this message.

Document Protocols

GDGI_protocolMajor, GDGI_protocolMinor, MSG_GEN_DOCUMENT_GROUP_GET_PROTOCOL

Every GEOS file (and each application that creates such files) has a protocol associated with it. Protocols are used to make sure an application knows if a given document was created by a different version of the application. They are stored in the file's FEA_PROTOCOL extended attributes (see the File System chapter). The protocol is of the form "MAJOR.MINOR," where both "MAJOR" and "MINOR" are 16-bit unsigned integers. The application's protocol is specified by the GDGI_protocolMajor and GDGI_protocolMinor attributes of the GenDocumentGroup object.

All documents created by an application will have the application's protocol number. If a document has the same major protocol number as the application but a lower minor protocol number, the document is compatible with the application. If the document has a lower major protocol number, the document is incompatible with the application; it can be opened only if a routine has been defined to upgrade the document. If the document has a higher protocol than the application (i.e. its major protocol number is higher, or it has the same major protocol number and a higher minor protocol number), the document control will not open the file; it will present an error message. By default, the GenDocumentGroup object has a GDGI_protocolMajor of one and a GDGI_protocolMinor of zero.

When the user opens an earlier but compatible document, the GenDocumentGroup opens the file and attaches it to a document object. Then, the (newly-created) document object sends itself MSG_GEN_DOCUMENT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT; the GenDocumentGroup then sends its output object the message MSG_META_DOC_OUTPUT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT . If neither of these messages is handled, the document will be opened as if it were of the current protocol (since it is compatible). Often an application will not handle these messages.

If the user tries to open an earlier and incompatible document, the GenDocumentGroup opens the file and attaches it to a document object. Then, the document object sends a MSG_GEN_DOCUMENT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT to itself, and the GenDocumentGroup sends a MSG_META_DOC_OUTPUT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT to its output. If neither message is handled, the GenDocumentGroup closes the file unchanged and removes the document object, and the GenDocumentControl presents an error message (since the document is incompatible). If either message is handled, the document will be opened normally after the handler exits.

Note that the document control will not automatically change the protocol number for a file after it has been updated. If you wish this done, you should have the handler for the message call one of the routines to change the FEA_PROTOCOL extended attribute.

MSG_GEN_DOCUMENT_GROUP_GET_PROTOCOL
dword	MSG_GEN_DOCUMENT_GROUP_GET_PROTOCOL();

Use this message to get the protocol number associated with the GenDocumentGroup. A protocol number is composed of two parts, a major component and a minor component. This message returns a double-word; the high word is the major component, and the low word is the minor component.

Source: Unrestricted.

Destination: GenDocumentGroupClass.

Return: Returns a dword-sized value; the high word contains the major protocol number, and the low word contains the minor protocol number.

Interception: You should not subclass this message.

Documents: 2.3 Document Control Data Fields: GenDocument Attributes

There are very few GenDocumentClass attributes that you will need to be concerned with. The GenDocumentGroup object creates and updates document objects as needed. Ordinarily, the application will not look at the GenDocumentClass instance data. If the program defines a subclass of GenDocumentClass , the subclass's methods should use only the subclass's instance data.

There is only one attribute which the program should change at run-time, and that is the GDA_PREVENT_AUTO_SAVE bit of the GDI_attrs field. This bit can be changed with the messages MSG_GEN_DOCUMENT_DISABLE_AUTO_SAVE and MSG_GEN_DOCUMENT_ENABLE_AUTO_SAVE .

GenDocumentClass is a subclass of GenContentClass and has all the functionality of that class. Since GenContent objects are rarely used directly, the class does not have its own chapter; instead, it is documented in GenContentClass . The main thing to know about the GenContent is that, like a VisContent, it is displayed in a GenView and can have visible children. It can also have generic children, though it may not have both visible and generic children at the same time.

The GDI_attrs Field

GDI_attrs, MSG_GEN_DOCUMENT_GET_ATTRS, MSG_GEN_DOCUMENT_ENABLE_AUTO_SAVE, MSG_GEN_DOCUMENT_DISABLE_AUTO_SAVE, MSG_GEN_DOCUMENT_AUTO_SAVE

The GDI_attrs word contains flags indicating the status of the document. The application can read or change any of these attributes; however, only the attribute GDA_PREVENT_AUTO_SAVE should actually be changed at run-time.

GDA_READ_ONLY
This attribute is set if the document in question is opened for read-only access.
GDA_READ_WRITE
This attribute is set if the document is opened for read/write access.
GDA_FORCE_DENY_WRITE
If this attribute is set, while the document is open, no other process will be allowed to open that document for read/write access.
GDA_SHARED_MULTIPLE
The document is opened in "shared multiple" mode.
GDA_SHARED_SINGLE
The document is opened in "shared single" mode.
GDA_UNTITLED
The document is newly-created and has not yet been saved; it is still untitled.
GDA_DIRTY
The document has been marked dirty since the last time it was saved.
GDA_CLOSING
The document is in the process of being closed.
GDA_ATTACH_TO_DIRTY_FILE
The document object is being attached to a dirty file (e.g., when restarting GEOS).
GDA_SAVE_FAILED
The user attempted to save the document, and it could not be saved (e.g., someone else denied write access, or the volume was no longer accessible).
GDA_OPENING
The document is in the process of being opened.
GDA_AUTO_SAVE_STOPPED
Auto-save was stopped while in progress.
GDA_PREVENT_AUTO_SAVE
This bit can be changed by the application at run-time. While the bit is on, auto-save is disabled.
MSG_GEN_DOCUMENT_GET_ATTRS
GenDocumentAttrs 	MSG_GEN_DOCUMENT_GET_ATTRS();

Use this message to get the GDI_attrs flags for a given document. These attribute flags give information about the document's permissions as well as about any operations currently in progress.

Source: Unrestricted--objects subclassed from GenDocumentClass often send this message to themselves.

Destination: Any GenDocument object.

Return: The object's word-sized GDI_attrs field.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_DISABLE_AUTO_SAVE
void	MSG_GEN_DOCUMENT_DISABLE_AUTO_SAVE();

Sometimes an application needs to temporarily disable auto-save for a specific document (for example, if it is in the middle of making elaborate changes to the file). It can do this by sending this message to the document object. The document's GDA_PREVENT_AUTO_SAVE bit will be turned on, and auto-save will be disabled until the document receives a MSG_GEN_DOCUMENT_ENABLE_AUTO_SAVE .

Source: Unrestricted--objects subclassed from GenDocumentClass often send this message to themselves.

Destination: Any GenDocument object.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_ENABLE_AUTO_SAVE
void	MSG_GEN_DOCUMENT_ENABLE_AUTO_SAVE();

This message turns off a document's GDA_PREVENT_AUTO_SAVE bit. If the bit is already off (i.e., auto-save is enabled), the message has no effect.

Source: Unrestricted--objects subclassed from GenDocumentClass often send this message to themselves.

Destination: Any GenDocument object.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_AUTO_SAVE
void	MSG_GEN_DOCUMENT_AUTO_SAVE();

This message forces the document object to immediately auto-save its file.

Source: Unrestricted. The document object may send this message to itself.

Destination: Any GenDocument object.

Interception: This message is not generally subclassed.

The GDI_operation Attribute

GDI_operation, MSG_GEN_DOCUMENT_GET_OPERATION

A single user action can result in many routines being called and many messages being sent out. To help keep track of what's going on, GenDocumentClass has a byte-length field, GDI_operation . If the document control is in the midst of handling a user action for a given document, it will set the GDI_operation byte accordingly. The current operation is a member of the GenDocumentOperation enumerated type. This type has the following possible values:

GDO_NORMAL
This is the usual setting. If the document is not currently handling a user action, this is the setting.
GDO_SAVE_AS
When the user chooses "Save As," the byte is set to this value. It remains at this value until the new document has been opened and saved.
GDO_REVERT
The setting from the time the user chooses "Revert" until the file has been restored to its last-saved state.
GDO_REVERT_QUICK
The setting from the time the user chooses "restore from backup" until the file has been restored.
GDO_ATTACH
The setting while the UI is being created for and attached to a given document. When a file document is created or opened, the GDI_operation field is set to this after GEOS has opened the file, and it remains at this setting until the application finishes attaching the UI.
GDO_DETACH
The setting while the UI is being detached from a given document, but before the actual file is closed.
GDO_NEW
The setting while a new file is being created. When the file is created and initialized, the GDI_operation will change to GDO_ATTACH .
GDO_OPEN
The setting while an existing file is being opened. When the file is created and initialized, the GDI_operation will change to GDO_ATTACH .
GDO_SAVE
The setting while a document is being saved.
GDO_CLOSE
After the UI has been detached, the GDI_operation byte is set to this value until the document object is destroyed.
GDO_AUTO_SAVE
The setting while a file is being updated (i.e. auto-saved) to disk.
MSG_GEN_DOCUMENT_GET_OPERATION
GenDocumentOperation 	MSG_GEN_DOCUMENT_GET_OPERATION();

Use this message to find out what user action a given document object is in the midst of processing. This is useful if you are handling some message and want to find out the context in which that message was sent. Note that although the message returns a word-length value, the GDI_operation enumerated type is byte-length; it is thus safe to cast the return value to a byte-length variable.

Source: Unrestricted--objects subclassed from GenDocumentClass often send this message to themselves.

Destination: Any GenDocument object.

Return: Returns a member of the GenDocumentOperation enumerated type corresponding to the document object's current operation.

Interception: You should not subclass this message.

File Information

GDI_fileHandle, GDI_diskHandle, GDI_volumeName, GDI_fileName, MSG_GEN_DOCUMENT_GET_FILE_NAME, MSG_GEN_DOCUMENT_GET_FILE_HANDLE

The document object stores certain data about the file associated with it. In particular, the instance data records the document's path, its full file name, and the handles of the file and the disk volume containing the file. This data can be retrieved by sending messages to the document object.

MSG_GEN_DOCUMENT_GET_FILE_NAME
void	MSG_GEN_DOCUMENT_GET_FILE_NAME(
        char *buffer); /* Address to write file name to */

This message instructs a GenDocument to write the name of its file (without the path) to the specified address.

Source: Unrestricted--objects subclassed from GenDocumentClass often send this message to themselves.

Destination: Any GenDocument object

Parameters: buffer Buffer of length FILE_LONGNAME_BUFFER_SIZE.

Return: Writes file's virtual name into the passed buffer as a null-terminated string.

Warnings: Make sure the buffer passed is of length FILE_LONGNAME_BUFFER_SIZE; otherwise the method might overwrite other data.

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GET_FILE_HANDLE
FileHandle 	MSG_GEN_DOCUMENT_GET_FILE_HANDLE();

This message returns the handle of the file associated with a given GenDocument object.

Source: Unrestricted--objects subclassed from GenDocumentClass often send this message to themselves.

Destination: Any GenDocument object.

Return: Returns handle of file associated with that document object.

Interception: You should not subclass this message.

GenContentClass

The GenContent generic object is similar to the VisContent visible object in that it interacts directly with the GenView. While the VisContent allows an application to display a visible hierarchy of objects within the view, however, the GenContent allows either generic or visible object hierarchies to be displayed. This is the one case where you may ordinarily have visible objects be children of a generic object. Note that you should not have both visible and generic objects as children of the same GenContent; if you do so, results are undefined.

Any GenView whose content is a GenContent (or subclass) should have its GVA_GENERIC_CONTENTS attribute set.

GenContentClass is a subclass of GenClass and therefore inherits all the instance data, messages, and hints of all generic objects. The GenContent also has two other instance data fields, however; these are

@instance byte			GCI_attrs = 0;
@instance optr			GCI_genView;

The GCI_attrs field contains a record of VisContentAttrs and is used by document objects for visual updates and interaction with the GenView. This record may be retrieved with MSG_GEN_CONTENT_GET_ATTRS or set with MSG_GEN_CONTENT_SET_ATTRS .

The GCI_genView field contains the optr of the GenView object displaying the GenContent. This, too, is used by document objects to manage interaction with the GenView.

MSG_GEN_CONTENT_GET_ATTRS
byte	MSG_GEN_CONTENT_GET_ATTRS();

This message returns the record of VisContentAttrs set in the GenContent's GCI_attrs field.

Source: Unrestricted.

Destination: Any GenContent object

Parameters: None.

Return: The GCI_attrs settings.

Interception: Unlikely.

MSG_GEN_CONTENT_SET_ATTRS
void	MSG_GEN_CONTENT_SET_ATTRS(
        byte	attrsToSet,
        byte	attrsToClear);

This message sets the attributes in the GenContent's GCI_attrs record.

Source: Unrestricted.

Destination: Any GenContent object.

Parameters: attrsToSet A record of VisContentAttrs indicating which flags should be set. Those set in attrsToSet will be set in GCI_attrs .

attrsToClear
A record of VisContentAttrs indicating which flags should be cleared. Those cleared in attrsToClear will be cleared in GCI_attrs . Note that if a flag is set in both parameters, it will end up cleared.

Return: Nothing.

Interception: Unlikely.

Documents: 3 Basic DC Messages

The document control objects use messages for many things. Since well over half a dozen classes of objects (counting file selectors, GenDisplayGroup objects, menu triggers, etc.) and far more actual objects are involved in intricate tasks, many messages are continually sent back and forth. Most of these messages are transparent to the programmer. The programmer need only know about them if the program subclasses a message to add functionality to it; this is an advanced technique which few programs will ever need to use.

There are two basic types of messages the programmer needs to know about. First, there are messages which are sent to document control objects; these objects may query information, toggle some functionality, or otherwise instruct the DC objects to take some action. Second, there are messages the DC objects send when the programmer's code needs to take some action. Each type of message is treated in a separate section.

Other Document Group Messages

From the Doc Control Objects

Documents: 3.1 Basic DC Messages: Other Document Group Messages

The following are the messages a program might ordinarily send to the GenDocumentGroup object. Many of these messages request information about the GenDocumentControl object or the target document; others request information about the GenDocumentGroup object or instruct it to take actions. Many of the messages require, as an argument, an optr to a document object; however, a null object-pointer can be passed, thus indicating the target document. This is especially useful under single-document models; the application doesn't need to keep track of the document object's optr, since it is always the target document.

MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY
void	MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY(
        optr	document); /* document to mark dirty */

This message notifies the GenDocumentGroup object that the specified document has been dirtied. The GenDocumentGroup will enable and disable file menu triggers as appropriate. If the argument is a null pointer, the target document will be marked dirty.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: document optr of document to mark dirty. If a null optr is passed, the target document will be dirtied.

Interception: You should not subclass this message.

Tips: If the document is a VM file and the GenDocumentGroup attribute GDGA_AUTOMATIC_DIRTY_NOTIFICATION is set, the VM routines will notify the GenDocumentGroup that the document has been dirtied whenever the VMDirty() (or DBDirty() , CellDirty() , etc.) routine is called. However, if you change a data cache without changing the actual file, you should send this message (or MSG_GEN_DOCUMENT_GROUP_MA RK_DIRTY_BY_FILE) to insure that the changes to the cache will be saved.

MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY_BY_FILE
void	MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY_BY_FILE(
        FileHandle 	file); /* file handle of document to mark dirty */

This message notifies the GenDocumentGroup that the file with the specified handle has been dirtied. The GenDocumentGroup will enable and disable file menu triggers as appropriate. The document is specified by file handle, not document optr.

Source: Unrestricted

Destination: Any GenDocumentGroup object.

Parameters: file The handle of the file to dirty.

Interception: You should not subclass this message.

Tips: If the document is a VM file and the GenDocumentGroup attribute GDGA_AUTOMATIC_DIRTY_NOTIFICATION is set, the VM routines will notify the GenDocumentGroup that the document has been dirtied whenever the VMDirty() (or DBDirty() , CellDirty() , etc.) routine is called. However, if you change a data cache without changing the actual file, you should send this message (or MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY ) to insure that the changes to the cache will be saved.

MSG_GEN_DOCUMENT_GROUP_GET_DOC_BY_FILE
optr	MSG_GEN_DOCUMENT_GROUP_GET_DOC_BY_FILE(
        FileHandle 	file);

Use this message if you know the file handle of a document and you need to get an object-pointer to the document object corresponding to the file. In the inverse situation (you know the object, and need to get the file handle), send MSG_GEN_DOCUMENT_GET_FILE_HANDLE directly to the document object.

Source: Unrestricted.

Destination: Any GenDocumentGroup object.

Parameters: file The handle of file whose document object is needed.

Return: Returns the optr of document object

Interception: You should not subclass this message.

MSG_GEN_DOCUMENT_GROUP_SAVE_AS_CANCELLED
void	MSG_GEN_DOCUMENT_GROUP_SAVE_AS_CANCELLED();

If you are in the midst of handling a "Save As" operation, and you need to cancel it, send this message to the GenDocumentGroup object.

Source: Unrestricted

Destination: Any GenDocumentGroup object.

Interception: This message is not generally subclassed.

Documents: 3.2 Basic DC Messages: From the Doc Control Objects

Often the document control will need to notify the application to take an action. For example, when a document is created, the application needs to be told to initialize the document and the user interface. There are two basic models for handling these situations: the "Procedure" model and the "Object" model. (The differences between these models are discussed at more length in the section Document Control Models .) Each model has its own way of messaging.

Under the Procedure model, every time the application needs to be notified, the document control sends a message to the GenDocumentGroup's output object, which is ordinarily the process object. Under the Object model, the affected document object will send a message to itself; this message has no handler under GenDocumentClass , so the application must use a subclass of GenDocumentClass with handlers for these messages.

A single user action can generate several messages. For example, when the user opens a document, four messages are sent: MSG_..._PHYSICAL_OPEN , MSG_..._READ_CACHED_DATA_FROM_FILE , MSG_..._CREATE_UI_FOR_DOCUMENT , and MSG_..._ATTACH_UI_TO_DOCUMENT . Furthermore, a given message might be sent as the result of several different user actions; for example, the message MSG_..._CREATE_UI_FOR_DOCUMENT is sent when the user creates a new file or opens an existing one. If a handler needs to know what user action precipitated a given message, it can send a MSG_GEN_DOCUMENT_GET_OPERATION to the document object.

Messages Handled under the Procedure Model

Whenever the application needs to be notified to take an action, a message will be sent to the GenDocumentGroup's output object. Two arguments accompany such messages: A pointer to the relevant document object and the handle of the file associated with that document. All of these messages are exported from MetaClass , so they can be handled by objects of any class.

Each of these messages corresponds to a document-model message, all of which are described in Messages Handled under the Object Model . These are just the basic messages; for more advanced functionality, see the message listings in the advanced section.

MSG_META_DOC_OUTPUT_INITIALIZE_DOCUMENT_FILE
void	MSG_META_DOC_OUTPUT_INITIALIZE_FILE(
        optr		document, /* document object to initialize */
        FileHandle		file); /* Handle of file to initialize */

The GenDocumentGroup object sends this message out when a new document has been created and needs to be initialized. Applications which use VM files will allocate the map block and initialize it. If an application maintains data caches for its files, it should initialize the caches at this point.

Note that the handler for this message should not take any UI-related actions. These should be left to the handlers for MSG_META_DOC_OUTPUT_CREATE_UI_FOR_DOCUMENT and MSG_META_DOC_OUTPUT_ATTACH_UI_FOR_DOCUMENT .

Source: The GenDocumentGroup object.

Destination: The output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the document object which has just been created.

file
The FileHandle of the file which has just been created or opened.

Interception: If you are using the Procedure model, you must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_META_DOC_OUTPUT_CREATE_UI_FOR_DOCUMENT
void	MSG_META_DOC_OUTPUT_CREATE_UI_FOR_DOCUMENT(
        optr		document, /* Pointer to document object */
        FileHandle		file); /* Handle of file associated with
                 * document object */

The GenDocumentGroup object sends this message after a document has been created or opened. Before this message is sent, the GenDocumentControl object will enable those objects on its GDCI_enableDisableList , and the GenDocumentGroup object will copy the GenDisplay resource for the document (if one is defined).

Applications that use dynamic UI objects will commonly respond to this message by creating the objects for the newly-opened document. Applications that use static UI objects will commonly respond to this message by enabling the objects.

Source: The GenDocumentGroup object.

Destination: The output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: If you are using the Procedure model, you must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_META_DOC_OUTPUT_ATTACH_UI_TO_DOCUMENT
void	MSG_META_DOC_OUTPUT_ATTACH_UI_TO_DOCUMENT(
        optr		document, /* optr of document object */
        FileHandle 		file); /* handle of file for this document */

The GenDocumentGroup object sends this message when the UI for a newly-opened document has been created. It also sends this message when re-opening a document as part of restoring GEOS from a state file. Applications may respond to this by attaching dynamic UI objects and setting the values of static UI objects.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the document object.

file
The FileHandle of the appropriate file.
MSG_META_DOC_OUTPUT_DETACH_UI_FROM_DOCUMENT
void 	MSG_META_DOC_OUTPUT_DETACH_UI_FROM_DOCUMENT(
        optr		document, /* pointer to document object */
        FileHandle 		file); /* handle of file for this document */

The GenDocumentGroup sends this message when a document is being closed, whether because a user closes the file or because the application is being closed. It also sends this message when GEOS is in the process of saving itself to a state file prior to shutting down. Applications generally respond to this by detaching dynamic UI objects. Note that the GenDocumentControl object will automatically disable any objects in its GDCI_enableDisableList .

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: If you are using the Procedure model, you must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_META_DOC_OUTPUT_DESTROY_UI_FOR_DOCUMENT
void	MSG_META_DOC_OUTPUT_DESTROY_UI_FOR_DOCUMENT(
        optr	document, /* pointer to document object */
        FileHandle 	file); /* handle of file for this document */

The GenDocumentGroup object sends this message out when a document is being closed, whether because a user closes the file or because the application is being closed. Applications will generally disable static display objects and delete dynamic display objects. Note that the GenDocumentControl object will automatically disable all objects in its GDCI_enableDisableList , and the GenDocumentGroup will delete the display block it created for a document, if any.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: If you are using the Procedure model, you must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_META_DOC_OUTPUT_ATTACH_FAILED
void	MSG_META_DOC_OUTPUT_ATTACH_FAILED(
        optr	document, /* pointer to document object */
        FileHandle 	File); /* (former) handle of file for this document */

When GEOS restores itself from state, the document control tries to reattach all documents which were attached when GEOS was shut down. If this is impossible (as for example if a document was deleted after GEOS shut down), the GenDocumentGroup object will send this message to its output.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: If you are using the Procedure model, you must write a handler for this message in whatever class will be receiving it (usually the process class).

Messages Handled under the Object Model

If an application uses the Object model of document control, it will generally not handle the messages to the GenDocumentGroup's output. Instead, it will define a subclass of GenDocumentClass ; this subclass will have methods for those situations which require the application's attention. Note that GenDocumentClass does not have handlers for any of these messages; if the application does not define a method for a given message, that message will have no effect.

MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE
Boolean	MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE();

When a new document is created, the document object sends this message to itself. VM file based applications will generally respond to this message by allocating and initializing the map block. DOS file based applications will commonly initialize data structures for a default file. If an application maintains data caches for its files, it should initialize the caches at this point.

The application should not take any UI-related actions; those should be postponed until the messages MSG_GEN_DOCUMENT_CREATE_UI_FOR_DOCUMENT and MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT are received.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Parameters: Nothing.

Return: If the handler could not initialize the file, it should return true ; the document control will then destroy the new file.

MSG_GEN_DOCUMENT_CREATE_UI_FOR_DOCUMENT
void	MSG_GEN_DOCUMENT_CREATE_UI_FOR_DOCUMENT();

The GenDocument object sends this message to itself after a document has been created or opened. Before this message is sent, the GenDocumentControl object will enable those objects on its GDCI_enableDisableList , and the GenDocumentGroup object will copy the GenDisplay block for the document (if one is defined).

Applications that use dynamic UI objects will commonly respond to this message by creating the objects for the newly-opened document. Applications that use static UI objects will commonly respond to this message by enabling the objects.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT
void	MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT();

The document object sends this message to itself when the UI for a newly-opened document has been created. It also sends this message when re-opening a document as part of restoring GEOS from a state file. Applications may respond to this by attaching dynamic UI objects and setting the values of static UI objects.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
void	MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT();

The document object sends this message when a document is being closed, whether because a user closes the file or because the application is being closed. It also sends this message when GEOS is in the process of saving itself to a state file prior to shutting down. Applications generally respond to this by detaching dynamic UI objects. Note that the GenDocumentControl object will automatically disable any objects in its GDCI_enableDisableList .

Source: A GenDocument object.

Destination: The document object sends this message to itself.

MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT
void	MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT();

The GenDocumentGroup object sends this message out when a document is being closed, whether because a user closes the file or because the application is being closed. Applications will generally disable static display objects and delete dynamic display objects. Note that the GenDocumentControl object will automatically disable all objects in its GDCI_enableDisableList , and the GenDocumentGroup will delete the display block it created for a document, if any.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

MSG_GEN_DOCUMENT_ATTACH_FAILED
void	MSG_GEN_DOCUMENT_ATTACH_FAILED();

When GEOS restores itself from state, the document control tries to reattach all documents which were attached when GEOS was shut down. If this is impossible (as for example if a document was deleted after GEOS shut down), the document object will send this message to itself.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Messages Associated with Common User Actions

A single user action can precipitate several application-handled messages. This section lists the messages associated with each of several common user actions. Note that some messages are sent as the result of many user actions. If a handler needs to find out what user action caused a message to be sent, it should send MSG_GEN_DOCUMENT_GET_OPERATION to the document object.

If a message is not ordinarily handled, it is enclosed in [brackets] below. These messages are documented in the advanced usage section. Actions taken by the document control objects (other than messages sent) are listed in italics . The messages listed are sent by the appropriate GenDocument object to itself. If the message is not subclassed by the GenDocument object, it sends a corresponding procedural-model message (of the form MSG_META_DOC_OUTPUT...) to the GenDocumentGroup object's output. The one exception is MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS; as noted on MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE , this message does not have a corresponding MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS.

  • New document is created:
    [ MSG_GEN_DOCUMENT_PHYSICAL_CREATE ]
    new file is created
    VM files: initialize     VM attributes, token, protocol
     MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE
     if saveAs/Revert supported, save file so revert will return to this state
     [ MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE ]
    [ MSG_GEN_DOCUMENT_PHYSICAL_SAVE ]
    MSG_GEN_DOCUMENT_CREATE_UI_FOR_DOCUMENT
    MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT
  • Document is opened:
    [ MSG_GEN_DOCUMENT_PHYSICAL_OPEN ]
    [ MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE ]
    MSG_GEN_DOCUMENT_CREATE_UI_FOR_DOCUMENT
    MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT
  • Document is saved:
    [ MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE ]
    [ MSG_GEN_DOCUMENT_PHYSICAL_SAVE ]
    VM files: call made to VMSave
  • Document is "Saved As":
    [ MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE ]
    [ MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS ]
    [DOS files: MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE ]
    VM files: VMSaveAs called
     [ MSG_GEN_DOCUMENT_SAVE_AS_COMPLETED ]
  • Document is reverted to last-saved version:
    MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
     [ MSG_GEN_DOCUMENT_PHYSICAL_REVERT ]
    VM : VMRevert called
     [ MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE ]
    MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT
  • Document is about to be closed:
    [MSG_GEN_DOCUMENT_PHYSICAL_CHECK_FOR_MODIFICATIONS]
If document is modified & user wants to save changes:
[ MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE ]
[ MSG_GEN_DOCUMENT_PHYSICAL_SAVE ]
VM : VMSave called
 MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT
If document is modified and user does not want to save changes:
MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
 [ MSG_GEN_DOCUMENT_PHYSICAL_REVERT ]
VM : VMRevert called
 MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT
 [ MSG_GEN_DOCUMENT_PHYSICAL_CLOSE ]
If document is not modified:
MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT
 [ MSG_GEN_DOCUMENT_PHYSICAL_CLOSE ]
If document is not modified and untitled:
MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT
 [ MSG_GEN_DOCUMENT_PHYSICAL_CLOSE ]
[ MSG_GEN_DOCUMENT_PHYSICAL_REVERT ]
  • GEOS restoring from state, document being attached:
    [ MSG_GEN_DOCUMENT_PHYSICAL_OPEN ]
    [ MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE ]
    MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT
  • GEOS restoring from state, attach failed:
    MSG_GEN_DOCUMENT_ATTACH_FAILED
    MSG_GEN_DOCUMENT_DESTROY_UI_FOR_DOCUMENT
  • GEOS shutting down, document being detached:
    [ MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT ]
    [ MSG_GEN_DOCUMENT_PHYSICAL_UPDATE ]
    VM files: VMUpdate called
     MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
     [ MSG_GEN_DOCUMENT_PHYSICAL_CLOSE ]

Documents: 4 Advanced DC Usage

By now, you should know enough for most uses of the document control. For single-document applications which manage GEOS Virtual Memory files and use a generic interface, the above documentation should be sufficient. However, there are some needs which require more advanced techniques. This section details these techniques.

This section discusses the use of document protocols to smooth the process of upgrading software. It also discusses using the document control to manage multiple documents simultaneously and to manage DOS files. Finally, it discusses those messages which an application might need to handle but would not ordinarily need to know about.

Document Protocols

Multiple Document Model

Working with DOS files

Special-Purpose Messages

Forcing Actions

Some Other Internal Messages

Documents: 4.1 Advanced DC Usage: Document Protocols

MSG_META_DOC_OUTPUT_OPEN_EARLIER_COMPATIBLE_DOCUMENT, MSG_META_DOC_OUTPUT_OPEN_EARLIER_INCOMPATIBLE_DOCUMENT, MSG_GEN_DOCUMENT_OPEN_EARLIER_COMPATIBLE_DOCUMENT, MSG_GEN_DOCUMENT_OPEN_EARLIER_INCOMPATIBLE_DOCUMENT

One difficulty in upgrading software is that an obsolete program may have created many documents. If the new version can't read those documents, people who used the old version will be inconvenienced; however, if the new versions always use the same document formats as the old versions, options for improvement will be limited. Above all, if document formats change, the new version should detect this gracefully, without crashing or damaging the old file.

The header for a GEOS Virtual Memory file contains two words for protocol numbers. The document control objects use the protocol numbers to insure that a document is compatible with the version of the application which is opening it. There are two parts to the protocol number: the major protocol number, and the minor protocol number. (If a document has a major protocol number of 3 and a minor number of 11, it is referred to has having protocol 3.11.) By convention, versions of an application with entirely compatible document formats will have the same major protocol number; if a new version of an application cannot read older documents without converting them in some way, it will have a higher major protocol number, and the minor number will be reset to zero.

When the GenDocumentGroup object opens a file, it checks the major and minor protocol numbers. It will then take appropriate action:

  • If the document's major and minor protocol numbers match the protocol attributes of the GenDocumentGroup object, the document will be opened normally.
  • If the document has a higher protocol number than the GenDocumentGroup (i.e. either the document has a higher major protocol number, or the document and the GenDocumentGroup have the same major protocol number and the document has a higher minor protocol number), the document control will display an appropriate alert box, after which it will close the file and delete the document object. (It will do all of this automatically, without any attention from the application.)
  • If the document has lower major protocol number than the document control, the document control will send MSG_META_DOC_OUTPUT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT (and a corresponding MSG_GEN_DOCUMENT_... ). If neither message is handled, or if a handler returns an error, the document control will display an alert box and will close the file and delete the document object.
  • If the document has the same major but a lower minor protocol number than the GenDocumentGroup, the document control will send MSG_META_DOC_OUTPUT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT (and a corresponding MSG_GEN_DOCUMENT_... ). After this, it will proceed normally, whether the messages were handled or not (since the document is presumed to be compatible). If a handler returns an error, it will close the file and free the document object.

Note that the document control will not change the file under any of these circumstances. In particular, if it opens an earlier document, it will not change the document's protocol number. If the application wishes to do this, it should do it explicitly (generally in the handlers for the " UPDATE_..._DOCUMENT " messages). The protocol numbers are among a file's extended attributes. For information about changing extended attributes, see the File System chapter.

MSG_META_DOC_OUTPUT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT
Boolean	MSG_META_DOC_OUTPUT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT(
        word *		error, 			/* Return error code from FileError */
        optr 		document,			/* pointer to document object */
        FileHandle 		file); 			/* handle of file opened */

The GenDocumentGroup object sends this message to its output when the user tries to open a document with the same major protocol number as the document control and a lower minor protocol number. Applications will commonly respond to this message by changing the document's protocol number to bring it up-to-date. If the application can't use the document, it should return true and set *error . (File access error codes are members of the FileError enumerated type, defined in file.h .) With an error, the document control will close the document unchanged. If the application successfully updates the document, it should return zero and set *error to zero.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object)

Parameters: error A pointer to a word in which an error should be returned.

document
The optr of the appropriate document object.
file
The FileHandle of the appropriate file.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: You must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_GEN_DOCUMENT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT
Boolean	MSG_GEN_DOCUMENT_UPDATE_EARLIER_COMPATIBLE_DOCUMENT(
        word *	error); /* Return error code from FileError type */

The document object sends this message when the user tries to open a document with the same major protocol number as the document control and a lower minor protocol number. Applications will commonly respond to this message by changing the document's protocol number to bring it up-to-date. If the application can't use the document, it should return true and put an error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .) With an error, the document control will close the document unchanged. If the application successfully updates the document, it should return zero and set *error to zero.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Parameters: error A pointer to a word in which an error should be returned.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).
MSG_META_DOC_OUTPUT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT
Boolean	MSG_META_DOC_OUTPUT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT(
                                        /* Return true if error */
        word *error, /* Return error code from FileError enum. type */
        optr document, /* pointer to document object */
        FileHandle file); /* handle of file opened */

The GenDocumentGroup object sends this message to its output when the user tries to open a document with a lower major protocol number than the document control. Applications respond to the message by making any changes to the document necessary to make it compatible with the application. The application should also change the document's protocol numbers. If the application can't use the document, it should return true and put an error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .) With an error, the document control will close the document unchanged. If the application successfully updates the document, it should return zero and set *error to zero.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: error A pointer to a word in which an error should be returned.

document
The optr of the appropriate document object.
file
The FileHandle of the appropriate file.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: You must write a handler for this message in whatever class will be receiving it (usually the process class)

MSG_GEN_DOCUMENT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT
Boolean	MSG_GEN_DOCUMENT_UPDATE_EARLIER_INCOMPATIBLE_DOCUMENT(
        word *	error); /* Return error code from FileError type */

The document object sends this message when the user tries to open a document with a lower major protocol number than the document control. Applications respond to this message by making any changes to the document necessary to make it compatible with the application. The application should also change the document's protocol numbers. If the application can't use the document, it should return true and put an error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .) With an error, the document control will close the document unchanged. If the application successfully updates the document, it should return zero and set *error to zero.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Parameters: error A pointer to a word in which an error should be returned.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Documents: 4.2 Advanced DC Usage: Multiple Document Model

The Object model of document control makes it easy to manage several documents at once. Effectively, each document object acts as a special-purpose application dedicated to handling one file. The display control lets the user change documents at will. The document control and display control take care of most of the nuts and bolts of file switching, so the application doesn't have to worry about them.

Subclassing GenDocumentClass

Under the object model of document control, a document's functionality is implemented as methods in the document class. Much of the switching between documents is thus transparent to the application. When the target changes, all user actions will result in messages being sent to the new target document; whenever a document gets a message, it knows the message pertains to itself, not some other document (and thus knows that it is the target document).

In order to implement this functionality, the application must declare a subclass of GenDocumentClass . This subclass will have its own methods for application-handled messages.

Using the Display Group

The simplest way to manage multiple documents is to use the display control. The display control lets the user change documents transparently to the application. In order to do this, the application must define a resource of objects which should be copied each time a document is opened or created. The GenDocumentGroup object's GDGI_genDisplay attribute should be set to point to a GenDisplay object in that resource; the GenDisplay should be set "not usable" and should be the head of a tree of objects. Also, the application should define an object of GenDisplayGroupClass , and the GenDocumentGroup's GDGI_genDisplayGroup attribute should be set to point to it. The display control will then automatically switch displays whenever the user chooses an entry from the display control.

Documents: 4.3 Advanced DC Usage: Working with DOS files

MSG_META_DOC_OUTPUT_PHYSICAL_SAVE, MSG_GEN_DOCUMENT_PHYSICAL_SAVE, MSG_META_DOC_OUTPUT_PHYSICAL_UPDATE, MSG_GEN_DOCUMENT_PHYSICAL_UPDATE, MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE, MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE, MSG_META_DOC_OUTPUT_PHYSICAL_REVERT, MSG_GEN_DOCUMENT_PHYSICAL_REVERT

The document control can be used to handle DOS files. However, there are special issues to be aware of. When you use GEOS Virtual Memory files, the system takes care of swapping sections of the file in and out of memory as needed. You can use high-level commands to mark parts of the file as dirty, and when you need the document saved, only the dirty sections will be copied to the disk. The details of reading from and writing to the disk are transparent to the application.

When you use DOS files, on the other hand, you have to take care of all of these details yourself. It is usually impractical to keep all of a document in memory at one time, so you have to have some way of managing the data (perhaps by creating a temporary VM file and copying the DOS file into that).

For this reason, the document control sends out messages when it does many low-level things (such as save files). If the application needs to take special actions, it can define handlers for these messages. Most of these messages can be ignored if you are working with GEOS files.

If you want to implement "Save As" and "Revert" for DOS files, you will have to do most of it by hand. If you leave "Save As" and "Revert" enabled, the Document Control will do some of the work for you. For example, when the user chooses "Save As", the Document Control will first present a File Selector, letting the user choose a file name and location. The Document Control will then create the new file. After this it will send out MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE and MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE, passing the handle of the newly-created file. The application is responsible for writing the current version of the document to the new file, and reverting the original file to its last-saved state. The Document Control will automatically close the original file and update all Document Control instance data as necessary.

MSG_META_DOC_OUTPUT_PHYSICAL_SAVE
Boolean	MSG_META_DOC_OUTPUT_PHYSICAL_SAVE(
        word *		error,
        optr 		document,
        FileHandle 		file);

If you need to take special steps to save a file, you should have a handler for either this message or MSG_GEN_DOCUMENT_PHYSICAL_SAVE . The handler should write the file completely to the disk. If an error occurs, return true and write the error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .)

If, for example, you copy a DOS file into a temporary VM file while you work on it, you would probably respond to this message by copying the data from the temporary file back to the DOS file.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: error A pointer to a word in which an error should be returned.

document
The optr of the appropriate document object.
file
The FileHandle of the appropriate file.

Interception: DOS-based applications must handle either this message or MSG_GEN_DOCUMENT_PHYSICAL_SAVE . Applications which use GEOS data files will generally not intercept this message.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).
MSG_GEN_DOCUMENT_PHYSICAL_SAVE
Boolean	MSG_GEN_DOCUMENT_PHYSICAL_SAVE(
        word *	error); 		/* Error code from FileError type */

This message is sent when the user saves a file. If you need to take special steps to save a file, you should have a handler for either this message or MSG_META_DOC_OUTPUT_PHYSICAL_SAVE . The handler should write the file completely to the disk. If an error occurs, return true and write the error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .)

If, for example, you copy a DOS file into a temporary VM file while you work on it, you would probably respond to this message by copying the data from the temporary file back to the DOS file.

Source: The GenDocument object.

Destination: The document object sends this message to itself.

Parameters: error A pointer to a word in which an error should be returned.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: DOS-based applications must handle either this message or MSG_META_DOC_OUTPUT_PHYSICAL_SAVE . Applications which use GEOS data files will generally not intercept this message.

MSG_META_DOC_OUTPUT_PHYSICAL_UPDATE
Boolean	MSG_META_DOC_OUTPUT_PHYSICAL_UPDATE(occurred */
        word *		error, 			/* Error code from FileError type */
        optr 		document,			/* Pointer to document object */
        FileHandle 		file);			/* Handle of DOS file */

This message is sent when the file is auto-saved (if this is enabled), and when the document is detached as part of a GEOS shutdown. If you need to take special steps to save a file, you should have a handler for either this message or MSG_GEN_DOCUMENT_PHYSICAL_UPDATE . The handler should write the file completely to the disk. If an error occurs, return true and write the error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .)

If, for example, you copy a DOS file into a temporary VM file while you work on it, you would probably respond to this message by copying the data from the temporary file back to the DOS file.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: error A pointer to a word in which an error should be returned.

document
The optr of the appropriate document object.
file
The FileHandle of the appropriate file.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: DOS-based applications which will have auto-save capability must handle either this message or MSG_GEN_DOCUMENT_PHYSICAL_UPDATE . Applications which use GEOS data files will generally not intercept this message.

MSG_GEN_DOCUMENT_PHYSICAL_UPDATE
Boolean	MSG_GEN_DOCUMENT_PHYSICAL_UPDATE(
        word *	error); 		/* Error code from FileError type */

This message is sent when the file is auto-saved (if this is enabled), and when the document is detached as part of a GEOS shutdown. If you need to take special steps to save a file, you should have a handler for either this message or MSG_META_DOC_OUTPUT_PHYSICAL_UPDATE . The handler should write the file completely to the disk. If an error occurs, return true and write the error code in *error . (File access error codes are members of the FileError enumerated type, defined in file.h .)

If, for example, you copy a DOS file into a temporary VM file while you work on it, you would probably respond to this message by copying the data from the temporary file back to the DOS file.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Parameters: error A pointer to a word in which an error should be returned.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: DOS-based applications which will have auto-save capability must handle either this message or MSG_META_DOC_OUTPUT_PHYSICAL_UPDATE . Applications which use GEOS data files will generally not intercept this message.

MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE
Boolean	MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE(
        word *		error,
        optr		document,
        FileHandle		file);

This message is sent when the Document Control is ready to "save-as" a DOS file. The Document Control will have asked the user what the new file should be, and will have created an appropriate file. The handler for this message must write the current version of the document to the new file, and restore the original file to its last-saved state.

Source: The GenDocumentGroup object.

Destination: The output of GenDocumentGroup (usually the Process object).

Parameters: error A pointer to a word in which an error code should be returned.

document
The optr of the appropriate document object.
file
The handle of the newly-opened file. The current version of the document should be saved to this file.

Return: true if an error occurred.

*file
A member of the FileError enumerated type (if an error occurred).

Interception: DOS-file applications must intercept this message (or MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE) if they wish to implement save-as/revert functionality.

MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE
Boolean	MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE(
        word *		error,
        FileHandle		file);

This message is sent when the Document Control is ready to "save-as" a DOS file. The Document Control will have asked the user what the new file should be, and will have created an appropriate file. The handler for this message must write the current version of the document to the new file, and restore the original file to its last-saved state.

Source: A GenDocument.

Destination: The GenDocument object sends this message to itself.

Parameters: error A pointer to a word in which an error code should be returned.

file
The handle of the newly-opened file. The current version of the document should be saved to this file.

Return: true if an error occurred.

*error
A member of the FileError enumerated type (if an error occurred).

Interception: DOS-file applications must intercept this message (or MSG_META_DOC_OUTPUT_PHYSICAL_SAVE_AS_FILE_HANDLE) if they wish to implement save-as/revert functionality.

MSG_META_DOC_OUTPUT_PHYSICAL_REVERT
Boolean	MSG_META_DOC_OUTPUT_PHYSICAL_REVERT(
        word *		error,
        optr		document,
        FileHandle		file);

The Document Control sends this message to revert a DOS file to its last-saved state. The handler must restore the file to its condition as of the last time it was saved.

Source: The GenDocumentGroup object.

Destination: The output of GenDocumentGroup (usually the Process object).

Parameters: error A pointer to a word in which an error should be returned.

document
The optr of the appropriate document object.
file
The FileHandle of the appropriate file.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: DOS-file applications must intercept this message (or MSG_GEN_DOCUMENT_PHYSICAL_REVERT) if they wish to implement save-as/revert functionality.

MSG_GEN_DOCUMENT_PHYSICAL_REVERT
Boolean	MSG_GEN_DOCUMENT_PHYSICAL_REVERT(
        word *		error,
        FileHandle		file);

The Document Control sends this message to revert a DOS file to its last-saved state. The handler must restore the file to its condition as of the last time it was saved.

Source: The GenDocumentGroup object.

Destination: The output of GenDocumentGroup (usually the Process object).

Parameters: error A pointer to a word in which an error should be returned.

file
The FileHandle of the appropriate file.

Return: true if error occurs.

*error
FileError code (or zero if there is no error).

Interception: DOS-file applications must intercept this message (or MSG_META_DOC_OUTPUT_PHYSICAL_REVERT) if they wish to implement save-as/revert functionality.

Documents: 4.4 Advanced DC Usage: Special-Purpose Messages

In addition to the basic messages discussed above, there are messages the document control sends out which do not ordinarily need to be handled. Some of these messages have been discussed above; most of the rest are described here.

Caching Data in Memory

MSG_META_DOC_OUTPUT_WRITE_CACHED_DATA_TO_FILE, MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE, MSG_META_DOC_OUTPUT_READ_CACHED_DATA_FROM_FILE, MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE

Sometimes an application will want to keep frequently-accessed data in memory. For example, if you are managing Virtual Memory files, you may want to copy the map block to a fixed memory block instead of locking the block every time you need to read or change it. This is known as caching data.

If you cache data, you must make sure that the application's version of the data is consistent with the disk file. The document control helps keep track of this. Whenever the file (or the state) is saved, the document control will first send a message instructing the application to write the cache to the file, then it will save the file. Similarly, when the file is opened or GEOS is restarted from state, the document control will send a message instructing the application to reload the cached data from the file.

There is one special concern. The user cannot save a file unless it has been marked dirty; also, the document control does not send MSG_..._WRITE_CACHED_DATA_TO_FILE to documents which are not dirty. Therefore, if you change the data cache without actually altering the file, you should send a MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY to the GenDocumentGroup.

MSG_META_DOC_OUTPUT_READ_CACHED_DATA_FROM_FILE
void	MSG_META_DOC_OUTPUT_READ_CACHED_DATA_FROM_FILE(
        optr 		document,			/* optr of document object */
        FileHandle 		file);			/* FileHandle of associated file */

The GenDocumentGroup sends this message when the document needs to read cached data. In particular, it sends this when a document is opened, when a document is reverted to its last-saved state, and when a document is re-opened as GEOS restores from state. If the application maintains a data cache, it should read the data from the file at this point. If the document does not cache data, it can ignore this message.

Note that if the document control notices that the file has changed on disk, it will not send this message; it will, however, send a MSG_META_DOC_OUTPUT_DOCUMENT_HAS_CHANGED . The handler for that message should reread the cache or call the handler for this message.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: You must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE
void	MSG_GEN_DOCUMENT_READ_CACHED_DATA_FROM_FILE();

The document object sends this message to itself when the document needs to read cached data. In particular, it sends this when a document is opened, when a document is reverted to its last-saved state, and when a document is re-opened as GEOS restores from state. If the application maintains a data cache, it should read the data from the file at this point. If the document does not cache data, it can ignore this message.

Note that if the document control notices that the file has changed on disk, it will not send this message; it will, however, send a MSG_GEN_DOCUMENT_DOCUMENT_HAS_CHANGED . The handler for that message should reread the cache or call the handler for this message.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

MSG_META_DOC_OUTPUT_WRITE_CACHED_DATA_TO_FILE
void	MSG_META_DOC_OUTPUT_WRITE_CACHED_DATA_TO_FILE(
        optr 		document,			/* optr of document object */
        FileHandle 		file);			/* FileHandle of associated file */

The GenDocumentGroup object sends this message when the document needs to write cached data back to the file. In particular, it sends this message just before a document is saved, auto-saved, or "Saved As," and before the document is closed as GEOS shuts down. The document should write its cached data back to the file. If the document does not cache data, it can ignore this message.

Warnings: This message will not be sent if the document is clean. Therefore, if you change the data cache without changing the file, you should send a MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY_BY_FILE to the GenDocumentGroup object.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object)

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: You must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_GEN_DOCUMENT_WRITE_CACHED_DATA_TO_FILE
void	MSG_META_DOC_OUTPUT_WRITE_CACHED_DATA_TO_FILE();

The document object sends this message to itself when the document needs to write cached data back to the file. In particular, it sends this message just before a document is saved, auto-saved, or "Saved As," and before the document is closed as GEOS shuts down. The document should write its cached data back to the file. If the document does not cache data, it can ignore this message.

Warnings: This message will not be sent if the document is clean. Therefore, if you change the data cache without changing the file, you should send a MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY_BY_FILE to the GenDocumentGroup object.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Other Messages To Know About

There are a few other messages which are worth knowing about. These messages alert the application to special situations. Most applications can ignore these messages; however, for a few, these messages should be handled.

MSG_META_DOC_OUTPUT_SAVE_AS_COMPLETED
void	MSG_META_DOC_OUTPUT_SAVE_AS_COMPLETED(
        optr 		document,			/* optr of document object */
        FileHandle 		file);			/* FileHandle of associated file */

The GenDocumentGroup object sends this message when a "Save As" operation has been successfully completed.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: You must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_GEN_DOCUMENT_SAVE_AS_COMPLETED
void	MSG_GEN_DOCUMENT_SAVE_AS_COMPLETED();

The document object sends this message to itself when a "Save As" operation has been successfully completed.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

MSG_META_DOC_OUTPUT_DOCUMENT_HAS_CHANGED
void	MSG_META_DOC_OUTPUT_DOCUMENT_HAS_CHANGED(
        optr 		document,			/* optr of document object */
        FileHandle 		file);			/* FileHandle of associated file */

If the GDGA_AUTOMATIC_CHANGE_NOTIFICATION attribute of the GenDocumentGroup object is set to on, a timer will periodically check to see if any open documents have been changed by another application. If they have, the GenDocumentGroup object will send this message out. The application should respond by redisplaying the data on the screen and rereading any cached data from the file.

Source: The GenDocumentGroup object.

Destination: Output of GenDocumentGroup (usually the Process object).

Parameters: document The optr of the appropriate document object.

file
The FileHandle of the appropriate file.

Interception: You must write a handler for this message in whatever class will be receiving it (usually the process class).

MSG_GEN_DOCUMENT_DOCUMENT_HAS_CHANGED
void	MSG_GEN_DOCUMENT_DOCUMENT_HAS_CHANGED();

If the GDGA_AUTOMATIC_CHANGE_NOTIFICATION attribute of the GenDocumentGroup object is set to on, a timer will periodically check to see if any open documents have been changed by another application. If they have, the document object will send this message out. The application should respond by redisplaying the data on the screen and rereading any cached data from the file.

Source: A GenDocument object.

Destination: The document object sends this message to itself.

Documents: 4.5 Advanced DC Usage: Forcing Actions

MSG_GEN_DOCUMENT_CONTROL_DISPLAY_DIALOG, MSG_GEN_DOCUMENT_CONTROL_INITIATE_NEW_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_USE_TEMPLATE_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_USE_TEMPLATE_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_OPEN_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_IMPORT_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_AS_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_AS_TEMPLATE_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_MOVE_TO_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_COPY_TO_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_EXPORT_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_SET_TYPE_DOC, MSG_GEN_DOCUMENT_CONTROL_INITIATE_SET_PASSWORD_DOC

If you wish, you can force the document control to take certain actions as if the user had requested them. You do this by sending the message which would ordinarily trigger such an action. For example, when the user selects the "save" trigger, that trigger sends the message MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_DOC to the Document Control object. If you wish, you can force a save by sending this message manually; the document control will behave as if the user had selected that action.

MSG_GEN_DOCUMENT_CONTROL_DISPLAY_DIALOG
void	MSG_GEN_DOCUMENT_CONTROL_DISPLAY_DIALOG();

This message forces the document control to display the opening "New/Open" dialog box, as if the user had selected the "New/Open" trigger on the File menu.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_NEW_DOC
void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_NEW_DOC();

This message forces the document control to create a new file, exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_USE_TEMPLATE_DOC
void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_USE_TEMPLATE_DOC();

This message forces the document control to create a new file from a template (bringing up an appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_OPEN_DOC
void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_OPEN_DOC();

This message forces the document control to open a file (bringing up the appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_IMPORT_DOC
void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_IMPORT_DOC();

This message forces the document control to import a file (bringing up the appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_DOC
void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_DOC();

This message forces the document control to save the active file, exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_AS_DOC
void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_AS_DOC();

This message forces the document control to save the active file under a new name (bringing up the appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_AS_TEMPLATE_DOC
void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_SAVE_AS_TEMPLATE_DOC();

This message forces the document control to save the active file as a template (bringing up the appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_MOVE_TO_DOC
void MSG_GEN_DOCUMENT_CONTROL_INITIATE_MOVE_TO_DOC();

This message forces the document control to move the active file to a new name and/or location (bringing up the appropriate file selector), exactly as if the user had requested it.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_COPY_TO_DOC
void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_COPY_TO_DOC();

This message forces the document control to copy the active file to a new name (bringing up the appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_EXPORT_DOC
void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_EXPORT_DOC();

This message forces the document control to export the active file (bringing up the appropriate file selector), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_SET_TYPE_DOC
void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_SET_TYPE_DOC();

This message forces the document control to change the type (public, read-only, etc.) of the active file (bringing up the appropriate dialog box), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

MSG_GEN_DOCUMENT_CONTROL_INITIATE_SET_PASSWORD_DOC
void	MSG_GEN_DOCUMENT_CONTROL_INITIATE_SET_PASSWORD_DOC();

This message forces the document control to set the password of the active file (bringing up the appropriate dialog box), exactly as if the user had requested it.

Source: Unrestricted.

Destination: The GenDocumentControl object.

Parameters: None.

Interception: This message is not normally intercepted.

Documents: 4.6 Advanced DC Usage: Some Other Internal Messages

DocumentCommonParams, MSG_GEN_DOCUMENT_NEW, MSG_GEN_DOCUMENT_OPEN, MSG_GEN_DOCUMENT_CLOSE, MSG_GEN_DOCUMENT_QUICK_BACKUP, MSG_GEN_DOCUMENT_RECOVER_QUICK_BACKUP, MSG_GEN_DOCUMENT_SAVE, MSG_GEN_DOCUMENT_SAVE_AS, MSG_GEN_DOCUMENT_SAVE_AS_TEMPLATE, MSG_GEN_DOCUMENT_COPY_TO, MSG_GEN_DOCUMENT_REVERT, MSG_GEN_DOCUMENT_EDIT_USER_NOTES, MSG_GEN_DOCUMENT_CHANGE_TYPE, MSG_GEN_DOCUMENT_CHANGE_PASSWORD, MSG_GEN_DOCUMENT_RENAME, MSG_GEN_DOCUMENT_SET_EMPTY, MSG_GEN_DOCUMENT_CLEAR_EMPTY, MSG_GEN_DOCUMENT_SET_DEFAULT, MSG_GEN_DOCUMENT_CLEAR_DEFAULT, MSG_GEN_DOCUMENT_CLOSE_IF_CLEAN_UNNAMED, MSG_GEN_DOCUMENT_SEARCH_FOR_DOC, MSG_GEN_DOCUMENT_TEST_FOR_FILE, MSG_GEN_DOCUMENT_AUTO_SAVE, MSG_GEN_DOCUMENT_UPDATE, MSG_GEN_DOCUMENT_CHECK_FOR_MODIFICATIONS, MSG_GEN_DOCUMENT_CLOSE_FILE, MSG_GEN_DOCUMENT_REOPEN_FILE, MSG_GEN_DOCUMENT_GET_VARIABLE, MSG_GEN_DOCUMENT_REVERT_NO_PROMPT, MSG_GEN_DOCUMENT_FILE_CHANGED_REINITIALIZE_CREATED_UI, MSG_GEN_DOCUMENT_CLOSE_IF_OPEN_FOR_IACP_ONLY, MSG_GEN_DOCUMENT_MOVE_TO

The following messages are for internal use by the Document Control objects only. The GenDocumentControl object sends each of these messages to an appropriate GenDocument object to instruct it to perform a certain action; no intervention by the application is necessary, or appropriate. However, some applications may wish to subclass one or more of these messages, simply to find out when the message is being sent (and thus when a particular action is being performed). Any application which does this must make sure to call the superclass without changing any of the arguments.

Several of these messages pass, as an argument, a structure of type DocumentCommonParams . This structure contains information about the file associated with the document object. If you intercept any of these messages, you may wish to examine this structure. However, be sure that you do not alter any of its fields. The structure has the following definition:

 typedef struct {
    FileLongName			DCP_name;
    DiskHandle			DCP_diskHandle;
    PathName			DCP_path;
    GenDocumentAttrs			DCP_docAttrs;
    DocumentOpenFlags			DCP_flags;
    IACPConnection			DCP_connection;
} DocumentCommonParams;
DCP _name
The name of the document in question.
DCP _diskHandle
The dosk handle of the disk (or standard path) containing the document.
DCP _path
The path to the document (relative to the disk handle in DCP _diskHandle ).
DCP _docAttrs
The GenDocumentAttrs of the document in question.
DCP _flags
The DocumentOpenFlags of the document in question.
DCP _connection
If the document is opened for an IACP connection, this field specifies the IACP connection that requestged the document be opened. If the user opened the document directly, this field will be zero. (This field is only set when the structure is passed with MSG_GEN_DOCUMENT_OPEN or MSG_GEN_DOCUMENT_SEARCH_FOR_DOC; otherwise, the field's value is undefined.)
MSG_GEN_DOCUMENT_NEW
Boolean	MSG_GEN_DOCUMENT_NEW(
        word *			fileOrError,
        DocumentCommonParams *			params);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to create a new document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: params A pointer to a DocumentCommonParams structure. This structure specifies the name and location for the file.

Return: true (i.e., non-zero) if error.

*fileOrError
If return value is false , this will be the file handle of the new document. If return value is true , this will be a member of the FileError enumerated type.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

Structures: The DocumentCommonParams structure (described on typedef struct { FileLongName DCP_name; ).

MSG_GEN_DOCUMENT_OPEN
Boolean	MSG_GEN_DOCUMENT_OPEN(
        word *			fileOrError,
        DocumentCommonParams *			params);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to create a new document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: params A pointer to a DocumentCommonParams structure. This structure specifies the name and location for the file.

Return: true (i.e., non-zero) if error.

*fileOrError
If return value is false , this will be the file handle of the new document. If return value is true , this will be a member of the FileError enumerated type.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

Structures: The DocumentCommonParams structure (described on typedef struct { FileLongName DCP_name; ).

MSG_GEN_DOCUMENT_CLOSE
DocQuitStatus 				MSG_GEN_DOCUMENT_CLOSE(
        IACPConnection		connection);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to close a document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: IACPConnection The IACP connection requesting the close (or zero if the close was requested by a user).

Return: A member of the DocQuitStatus enumerated type (described below).

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

Structures: The return value is a member of the DocQuitStatus enumerated type. This type has the following members:

DQS_OK
The documentwas updated and closed successfully.
DQS_CANCEL
The close operation was canceled before it could be completed.
DQS_DELAYED
The close operation has been delayed.
DQS_SAVE_ERROR
The handler was unable to update the file to the disk.
MSG_GEN_DOCUMENT_QUICK_BACKUP
void	MSG_GEN_DOCUMENT_QUICK_BACKUP();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to make a quick backup of a document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_RECOVER_QUICK_BACKUP
void	MSG_GEN_DOCUMENT_RECOVER_QUICK_BACKUP();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to recover a document from a quick backup.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_SAVE
Boolean	MSG_GEN_DOCUMENT_SAVE();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to save a document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: Nothing.

Return: true if error.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_SAVE_AS
Boolean MSG_GEN_DOCUMENT_SAVE_AS(
        word *			fileOrError,
        DocumentCommonParams *			params);

The Document Control object sends this message to a Document object to instruct it to "Save As" its document. The DocumentCommonParams structure specifies the new name and location for the file.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: fileOrError A pointer to a word-sized variable.

params
A pointer to a DocumentCommonParams structure specifying the new name and location for the file.

Return: true if an error occurred.

*fileOrError
If the routine returned false , this contains the file handle of the new file. If it returned true , this contains a FileError code.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_SAVE_AS_TEMPLATE
Boolean MSG_GEN_DOCUMENT_SAVE_AS_TEMPLATE(
        word *			fileOrError,
        DocumentCommonParams *			params);

The Document Control object sends this message to a Document object to instruct it to save its document as a template. The DocumentCommonParams structure specifies the name and location for the template.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: fileOrError A pointer to a word-sized variable.

params
A pointer to a DocumentCommonParams structure specifying the new name and location for the file.

Return: true if an error occurred.

*fileOrError
If the routine returned false , this contains the file handle of the new file. If it returned true , this contains a FileError code.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_COPY_TO
Boolean	MSG_GEN_DOCUMENT_COPY_TO(
        DocumentCommonParams *			params);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to copy the current document to a new location, while keeping the original copy open. The DocumentCommonParams structure specifies the name and location for the copy.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: params A pointer to a DocumentCommonParams structure specifying the new name and location for the file.

Return: true if error.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_REVERT()
Boolean	MSG_GEN_DOCUMENT_REVERT();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to "Revert" its document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: true if error.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_EDIT_USER_NOTES
void	MSG_GEN_DOCUMENT_EDIT_USER_NOTES();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to perform an "Edit User Notes" operation.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CHANGE_TYPE
void	MSG_GEN_DOCUMENT_CHANGE_TYPE(
        GenDocumentType		type);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to change its document's type to the type specified in type .

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: type The new type of the document.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

Structures: A member of the GenDocumentType enumerated type. This type has the following members:

GDT_NORMAL
The document is a normal document.
GDT_READ_ONLY
The document is available for read-only access.
GDT_TEMPLATE
The document is a template.
GDT_READ_ONLY_TEMPLATE
The document is a template, and is opened read-only.
GDT_PUBLIC
The document is "public" (and thus is, by default, opened read-only).
GDT_MULTI_USER
The document can be read by several users at once.
MSG_GEN_DOCUMENT_CHANGE_PASSWORD
void	MSG_GEN_DOCUMENT_CHANGE_PASSWORD(
        char *		password);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to change the document's password.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: password A pointer to a null-terminated string containing the document's new password.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_RENAME
void	MSG_GEN_DOCUMENT_RENAME();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to rename its document.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_SET_EMPTY
void	MSG_GEN_DOCUMENT_SET_EMPTY();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to make its document the new model for "empty" documents.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CLEAR_EMPTY
void	MSG_GEN_DOCUMENT_CLEAR_EMPTY();

The GenDocumentControl object sends this message to the GenDocument object to inform it that there is no longer a file to serve as the model for "empty" documents.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_SET_DEFAULT
void	MSG_GEN_DOCUMENT_SET_DEFAULT();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to make its document the default document to open on startup.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CLEAR_EMPTY
void	MSG_GEN_DOCUMENT_CLEAR_EMPTY();

The GenDocumentControl object sends this message to the GenDocument object to inform it that there is no longer a default file to open on startup.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CLOSE_IF_CLEAN_UNNAMED
void	MSG_GEN_DOCUMENT_CLOSE_IF_CLEAN_UNNAMED();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to close its document if the document has not been changed or saved since it was created.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_SEARCH_FOR_DOC
Boolean	MSG_GEN_DOCUMENT_SEARCH_FOR_DOC(
        DocumentCommonParams *			params);

The GenDocumentControl object sends this message to the GenDocument object to check if it has the specified document open, and bring it to the front if that document exists.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: params A DocumentCommonParams structure specifying the document sought. The DCP_connection field specifies the IACP connection looking for that document (or zero if the request came directly from the user).

Return: true if the Document object has that document open.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

Structures: The DocumentCommonParams structure (described on typedef struct { FileLongName DCP_name; ).

MSG_GEN_DOCUMENT_TEST_FOR_FILE
Boolean	MSG_GEN_DOCUMENT_TEST_FOR_FILE(
        FileHandle		file,
        optr		*docFound);

The GenDocumentControl object sends this message to the GenDocument object to ask if that Document has the file with the specified handle open. If it does, the GenDocument is to write its optr to the passed variable.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: file The FileHandle of the file being sought.

docFound
A pointer to an optr-sized variable.

Return: true if the Document has the specified file open.

*docFound
The optr of the Document object, if the Document has the specified file open; otherwise, this variable is left unchanged.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_AUTO_SAVE
void	MSG_GEN_DOCUMENT_

The GenDocumentControl object sends this message to the GenDocument object to instruct it to

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_UPDATE
Boolean	MSG_GEN_DOCUMENT_UPDATE(
        word *		error);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to write changes in its document to the disk.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: error A pointer to a word-sized variable.

Return: true if an error occurred.

*error
A FileError code, or zero if no error occurred.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CHECK_FOR_MODIFICATIONS
void	MSG_GEN_DOCUMENT_CHECK_FOR_MODIFICATIONS();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to check if its file has been modified on the disk.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CLOSE_FILE
void	MSG_GEN_DOCUMENT_CLOSE_FILE(
        IACPConnection		connection);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to close its file, without removing the document object itself. (This is done, e.g., when the Document performs a "Move To"; it must close the file, move it, and reopen it.)

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: connection The IACP connection requesting that the file be closed, or zero if the request came directly from the user.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_REOPEN_FILE
void	MSG_GEN_DOCUMENT_REOPEN_FILE();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to reopen its file (after having handled a MSG_GEN_DOCUMENT_CLOSE_FILE).

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_GET_VARIABLE
void	MSG_GEN_DOCUMENT_GET_VARIABLE(
        VisTextGraphic *		graphic,
        char *		buffer);

The GenDocumentControl object sends this message to the GenDocument object to get information about a variable graphic. (This message is used by the text object to implement variable graphics.)

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: graphic A pointer to a VisTextGraphic structure.

char
A pointer to a 100-byte buffer.

Return: Both buffers filled in appropriately.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_REVERT_NO_PROMPT
void	MSG_GEN_DOCUMENT_REVERT_NO_PROMPT();

The GenDocumentControl object sends this message to the GenDocument object to instruct it to "Revert" its document without prompting the user.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: true if error.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_FILE_CHANGED_REINITIALIZE_CREATED_UI
void	MSG_GEN_DOCUMENT_FILE_CHANGED_REINITIALIZE_CREATED_UI();

The GenDocumentControl object sends this message to the GenDocument object to inform it that its associated file has changed, and it should reinitialize its UI and reload any cached data.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_CLOSE_IF_OPEN_FOR_IACP_ONLY
void	MSG_GEN_DOCUMENT_CLOSE_IF_OPEN_FOR_IACP_ONLY()

The GenDocumentControl object sends this message to the GenDocument object to instruct it to close its document if the document is open only for an application-mode IACP connection.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: None.

Return: Nothing.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

MSG_GEN_DOCUMENT_MOVE_TO
Boolean	MSG_GEN_DOCUMENT_MOVE_TO(
        DocumentCommonParams			params);

The GenDocumentControl object sends this message to the GenDocument object to instruct it to perform a "Move To" operation. The DocumentCommonParams structure specifies the new name and location for the file.

Source: The GenDocumentControl object.

Destination: A GenDocument object.

Parameters: params A pointer to a DocumentCommonParams structure. This structure specifies the new name and location for the file.

Return: true (i.e. non-zero) if error.

Interception: This message is not normally intercepted. If you intercept it, be sure to send the message to the superclass with arguments unchanged.

Structures: The DocumentCommonParams structure (described on typedef struct { FileLongName DCP_name; ).

This document is a single-page version of a a multi-page document, suitable for easy printing.


The File System

Every operating system needs a way to interact with files. Files are used to hold both data and executable code. They are also the simplest way of transferring data from one computer to another.

GEOS provides powerful file-management functionality. It runs on top of a disk-operating system, and uses that DOS to read files from different media. Applications which run under GEOS need only interact with the GEOS file-management system; they are insulated from the differences between versions of DOS. They are also insulated from the differences between various file-storage media: CD-ROM drives, network file servers, and floppy and hard disks all present the same interface.

The GEOS file system provides functionality that many versions of DOS do not have. It allows the use of virtual directories, so (for example) the system's FONT directory could actually comprise several physical directories. GEOS files have functionality which DOS files lack. For example, GEOS provides support for file-sharing and data-access synchronization across networks. Nevertheless, GEOS lets applications access standard DOS files and directories when desired.

You may not need to use much of the file system directly. The document control and file-selector objects can let the user select and open files transparently to the application. Many applications will never need to negotiate the directory structure.

1 Design Philosophy
2 File System Overview
3 Disks and Drives
3.1 Accessing Drives
3.2 Accessing Disks
4 Directories and Paths
4.1 Standard Paths
4.2 Current Path and Directory Stack
4.3 Creating and Deleting Directories
5 Files
5.1 DOS Files and GEOS Files
5.2 Files and File Handles
5.3 GEOS Extended Attributes
5.4 File Utilities
5.5 FileEnum()
5.6 Bytewise File Operations

The File System: 1 Design Philosophy

The GEOS file system was designed to meet two goals. First, the file system should insulate geodes from differences in hardware, making them device independent; and second, it should give geodes all the functionality they might plausibly want without being cumbersome.

One of the hallmarks of GEOS is its ability to insulate applications from differences in hardware. The GEOS file system plays a large role in this. Geodes which need access to files make their requests to the GEOS file system, which accesses the actual storage media. The virtues of this approach are several:

  • Geodes can rely on a consistent API for accessing media. When an application needs to open a file, it uses exactly the same techniques whether the file resides on a hard or floppy disk, a network drive, or some other medium.
  • GEOS can easily expand to make use of new technologies. To support a new storage medium, GEOS just needs a new device driver; all existing application binaries will automatically work with the new device.
  • Applications need not worry about what DOS the system might be running under. The GEOS file system does all interaction with the DOS and automatically takes advantages of each DOS' strengths. To support new versions of DOS, GEOS just needs new drivers.

GEOS is also flexible. Flexibility means more than just being able to use new technology in the same old ways; it means providing functionality to serve unforeseen purposes. GEOS provides this:

  • The file system allows such advanced functionality as virtual paths and directories. Files can thus be distributed across various media (for example, templates and fonts on CD-ROM drives, company files on a network drive, other documents on local hard disks) transparently to both user and application.
  • Other functionality can be added. For example, all GEOS files can have names up to 32 characters long, regardless of strictures on individual media. The file system maps the GEOS "virtual names" to device-dependent "native names" transparently to the user and application.

While GEOS provides this advanced functionality, it still lets geodes access the raw files. If a geode wants to access any file as a sequence of bytes (the DOS model), it may do so. Similarly, geodes may work with physical DOS directories if they wish to.

The File System: 2 File System Overview

The GEOS file system manages all access to files on any type of storage. Whenever a geode needs to access a data file, it calls a file system routine. The file system makes any necessary calls to the computer's DOS. Like much of the GEOS system, it is driver-based; this makes it easy to expand GEOS to accommodate new technologies.

When a geode needs to access a file, it makes a call to the file system. The file system sends requests through the appropriate driver. For example, if the geode needs access to a file on a local hard or floppy disk drive, the file system will send commands through a DOS driver, which will in turn issue appropriate commands to the DOS itself. Similarly, if the file resides on a network drive, the file system will send commands through an appropriate network driver, which will translate them into corresponding commands to the network server.

Every storage device is known as a drive. A drive can be either fixed-media or movable-media. Fixed media drives are always associated with a specific storage medium; in effect, the drive is the same as the storage. The primary example of this is the conventional hard disk. Movable-media drives use a potentially unlimited number of storage objects. Examples include floppy disk drives, CD-ROM drives, and cartridge hard drives.

One way of organizing storage objects is to divide them into volumes . Ordinarily, every disk (floppy or hard) is a single volume; however, hard disks are sometimes divided into several volumes. Network storage devices are frequently divided into several volumes. If a single drive is partitioned into several volumes, DOS and GEOS treat each volume as a separate drive. In this chapter, the words volume and disk will be used interchangeably.

Individual volumes are organized with directories. A directory may contain several different files or directories. There is not usually a limit on the number of files a directory may contain; however, no two files or directories belonging to a given directory may have the same name.

Every volume is organized in a directory tree . The topmost directory is known as the root ; it is unnamed, since every volume contains exactly one root directory. The root may contain files or directories. Those directories may themselves contain files or directories, and so on. The total number of files is usually limited only by the size of the storage device.

To specify a file, you need to specify three things: The volume the file resides on, the directory to which the file belongs, and the file's name. In most DOS environments, a volume is generally specified with a letter followed by a colon (for example, the first hard disk is generally specified as "C:"). For non-fixed media (e.g. floppy disks), the letter actually specifies the drive; the volume is presumed to be in that drive. In GEOS, volumes and drives have different identifying systems. Every drive is identified by a drive number . This is a positive integer; the first drive has a number of zero, the second is drive one, and so on. Every volume is identified by a token (the disk handle ).

Specifying the directory is a little trickier. There may be many directories with the same name on a given volume. For this reason, the directory is specified with a path. The path begins with the root directory; the root is followed by the name of one of the directories belonging to the root; that directory is followed by the name of one of its subdirectories; and so on, until the desired directory is reached. Since all the directories belonging to a given directory must have unique names, the path is guaranteed to uniquely specify a directory on a volume. For example, you might specify a directory as "\GEOWORKS\DOCUMENT\MEMOS"; this would indicate a directory named "MEMOS," which belongs to a directory named "DOCUMENT," which in turn belongs to a directory named "GEOWORKS" at the root level.

The file itself is identified by its name. Since its directory has been uniquely specified by the volume and path, the name is guaranteed to specify at most one file in that directory. Each disk-operating system has its own conventions about how file names can be formed; for example, MS-DOS requires file names to have a "name" portion of at most eight letters, followed by an "extension" of at most three letters (known as the "FILENAME.EXT" convention).

For convenience, most operating systems let you specify a "working directory." This is a combination of a volume and a path. If you have a working directory, you can omit either the volume or the path from a file specification, and the volume and/or path of the working directory will be used. This is called a "relative path," i.e. a path that is relative to the working directory instead of the root directory. (Note that if you specify a disk handle for any operation, you must pass an absolute path which begins at the root of that disk or standard path.) GEOS allows every thread to have its own working directory; each thread can also save paths on a stack, letting the thread switch directories easily. For more details, see Current Path and Directory Stack .

Some disk-operating systems, such as MS-DOS, allow a working directory for each drive. GEOS does not allow this; you may have only one working directory at a time, regardless of how many drives you have. If you need to switch back and forth between directories on different drives, you can use the directory stack (see Current Path and Directory Stack ).

The File System: 3 Disks and Drives

GEOS provides an easy interface to storage devices. Every drive (or analogous storage device) is identified by a token. Every volume is also identified by a token. This enables you to easily move from one volume to another.

You generally need only worry about disks and drives when you are opening a file. Once you have a file open, you can access the file by its file handle without paying attention to the disk it resides on. The GEOS file system will automatically prompt the user to change disks whenever necessary.

Note that if you use the document control object to open files, you will probably never have to worry about disks and drives. The document control automatically presents a File Selector dialog box to the user, letting the user navigate among disks and directories; when the user selects a file, the document control will automatically open the file and return its handle to the application.

Accessing Drives

Accessing Disks

The File System: 3.1 Disks and Drives: Accessing Drives

DriveGetStatus(), DriveGetExtStatus(), DriveGetDefaultMedia(), DriveTestMediaSupport(), DriveGetName(), DriveStatus, DriveType, MediaType

Most systems running GEOS have access to a number of different drives. With the exception of network drives, the drives available will usually not change during an execution of GEOS, although the volumes mounted on the drives can change. Every drive is accessed by its drive number . This token is a byte-length integer value.

When you wish to open a file, you must specify its volume, not its drive. This is because the volume mounted on a drive can change frequently and without warning.

GEOS provides routines to get information about a drive. To get general information about a drive, call the routine DriveGetStatus() . This routine takes the drive number and returns a word-length set of DriveStatus flags (defined in drive.h ). If an error condition exists, such as the drive you request not existing, it returns zero. The following flags may be returned:

DS_PRESENT
This flag is set if the physical drive exists, regardless of whether the drive contains a disk.
DS_MEDIA_REMOVABLE
This flag is set if the disk can be removed from the drive.
DS_NETWORK
This flag is set if the drive is accessed over a network (or via network protocols), which means the drive cannot be formatted or copied.
DS_TYPE
This is a mask for the lowest four bits of the field. These bits contain a member of the DriveType enumerated type.

The lowest four bits of the word contains a member of the DriveType enumerated type. The field can be accessed by masking out all the bits except for those set in DS_TYPE. DriveType comprises the following values:

DRIVE_5_25
Drive uses 5.25-inch floppy disks.
DRIVE_3_5
Drive uses 3.5-inch floppy disks.
DRIVE_FIXED
Drive uses some kind of fixed disk (e.g. a hard drive).
DRIVE_RAM
Drive is chip-based (either RAM or ROM).
DRIVE_CD_ROM
Drive uses read-only optical disks.
DRIVE_8
Drive uses 8-inch floppy disks.
DRIVE_UNKNOWN
Drive type is unknown.

DriveGetStatus() returns the information most often needed about a drive. However, you may sometimes need more obscure information. For this reason, GEOS provides the routine DriveGetExtStatus() . Like DriveGetStatus() , it takes the drive number as an argument and returns a word of flags; however, it returns additional flags. The flags returned by DriveGetStatus() are set in the lower byte of the returned word; special additional flags are set in the upper byte. Like DriveGetStatus() , DriveGetExtStatus() returns zero if the drive specified is invalid. The following flags are defined for the upper byte:

DES_LOCAL_ONLY
This flag is set if the device cannot be viewed over a network.
DES_READ_ONLY
This flag is set if the device is read only, i.e. no data can ever be written to a volume mounted on it (e.g., a CD-ROM drive).
DES_FORMATTABLE
This flag is set if disks can be formatted in the drive.
DES_ALIAS
This flag is set if the drive is actually an alias for a path on another drive.
DES_BUSY
This flag is set if the drive will be busy for an extended period of time (e.g., if a disk is being formatted).

Many disk drives can take a variety of disks. For example, high-density 3.5-inch drives can read and write to either 720K disks or 1.44M disks. Every drive has a "default" media type. When you format a disk in that drive, it will, by default, be formatted to the default size. To find out the default disk type, call the routine DriveGetDefaultMedia() . This routine takes one argument, namely the drive number. It returns a member of the MediaType enumerated type. MediaType has the following possible values:

MEDIA_160K, MEDIA_180K, MEDIA_320K, MEDIA_360K
These are all sizes used by 5.25-inch disks.
MEDIA_720K
This is the size of a regular 3.5-inch disk.
MEDIA_1M2
This is the size of a high-density 5.25-inch disk.
MEDIA_1M44
This is the size of a high-density 3.5-inch disk.
MEDIA_2M88
This is the size of an ultra-high-density 3.5-inch disk.
MEDIA_FIXED_DISK
This is returned for all fixed disks.
MEDIA_CUSTOM
This is returned if none of the other values is appropriate. For example, it is returned for CD-ROM drives.
MEDIA_NONEXISTENT
This is returned if the drive specified does not contain a disk. This value is defined to be equal to zero.

If you want to find out if a drive can accommodate a certain kind of disk, call the routine DriveTestMediaSupport() . This Boolean routine takes two arguments: a drive number and a member of the MediaType enumerated type. If the drive supports that medium, the routine returns true (i.e. non-zero); otherwise, it returns false (i.e. zero).

To find out the name of a given drive, call DriveGetName() . This routine is passed three arguments: a drive number, a pointer to a character buffer, and the size of that buffer. DriveGetName() writes the drive's name to the buffer as a null-terminated string; it returns a pointer to that trailing null. If the buffer was not large enough, or the drive does not exist, it returns a null pointer.

The File System: 3.2 Disks and Drives: Accessing Disks

Applications will work with disks more than they will work with drives. Once a geode knows a disk's handle, it can ignore such questions as whether the disk is in a drive; it need merely provide the disk's handle. If necessary, the system will prompt the user to insert the disk in the appropriate drive.

Registering Disks

DiskRegisterDisk(), DiskRegisterDiskSilently()

GEOS automatically keeps track of all disks used. The first time a disk is accessed in a session, it is registered . This means that it is assigned a disk handle .

The disk handle records certain information, such as the disk's volume name and whether the disk is writable. It also notes in which drive the disk was last inserted; if the system prompts the user to reinsert the disk, it will insist on that drive. A disk is automatically reregistered when certain actions are performed which might invalidate a disk's handle-table entry; for example, it is reregistered if it is formatted. It is also reregistered if someone tries to write a file to a disk which is marked read-only; the user may have ejected the disk and removed its write-protect tab. Note that reregistering a disk does not change its handle; it just brings GEOS's information about the disk up-to-date.

Note that the disk handle is not a reference to the global handle table; thus, Swat commands like phandle will not work with disk handles. Disk handles should always be treated as opaque 16-bit tokens.

You can specifically instruct the system to register a disk by calling the routine DiskRegisterDisk() . The routine is passed a single argument, namely the drive number. If the disk has an entry in the disk table, the routine will not reregister the disk; it will just return the disk's handle. If the disk has no entry in the table, the system will create an entry and register the disk. In this case, also, the routine will return the (new) disk handle. If the routine fails (for example, because there is no valid disk in the specified drive, or the drive itself does not exist), it returns a null handle.

When a disk is registered, the system notes the volume label. This label is used when the system has to prompt the user to insert a disk. If an unlabeled disk is inserted, the system will choose an arbitrary label for the volume (e.g. "UNNAMED1"). The system does not actually write this label to the disk; the label is used by the system and discarded when the session ends. Ordinarily, the system will present an alert box to inform the user about the temporary label. You can suppress this notification by calling the system routine DiskRegisterDiskSilently() . This routine has the same arguments and return values as DiskRegisterDisk() .

Getting Information about a Disk

DiskGetVolumeInfo(), DiskGetVolumeFreeSpace(), DiskGetDrive(), DiskGetVolumeName(), DiskFind(), DiskCheckWritable(), DiskCheckInUse(), DiskCheckUnnamed(), DiskForEach(), DiskInfoStruct, DiskFindResult

GEOS provides many routines to get information about disks. If geodes call disk routines at all, they are most likely to call these. Most of these routines are passed the handle of the disk. If you know the disk's volume label and need to find out its handle, call the routine DiskFind() (described below). If you know the disk is currently in a drive and you need to find out its handle, register the disk (see Registering Disks ). Note that any routine which is passed a disk handle can be passed a standard path constant; in this case, the routine will give you information about the disk containing the geos.ini file.

The basic disk-information routine is DiskGetVolumeInfo() . This returns information about the size of the disk and the amount of free space available. The routine is passed two arguments: the disk handle and a pointer to a DiskInfoStruct structure (shown below). The routine fills in the fields of the DiskInfoStruct structure and returns zero if it was successful. If it fails for any reason, it returns an error value and sets the thread's error value (which can be recovered with ThreadGetError() ). The usual error value returned is the constant ERROR_INVALID_VOLUME.

Code Display 17-1 The DiskInfoStruct Structure

typedef struct {
    word		DIS_blockSize;		/* # of bytes in a block; smallest size
                     * file system can allocate at once */
    sdword		DIS_freeSpace;		/* # of bytes free on disk */
    sdword		DIS_totalSpace;		/* Total size of the disk in bytes */
    char		DIS_name[VOLUME_BUFFER_SIZE];
                    /* Volume name; if disk is unnamed, this
                     * is the temporary name. String is
                     * null-terminated. */
} DiskInfoStruct;

If you just want to know a disk's name, call DiskGetVolumeName() . This routine takes two arguments: the disk handle and the address of a character buffer. (The buffer must be at least VOLUME_NAME_LENGTH_ZT characters long.) It writes the volume name to the buffer as a null-terminated string, and it returns the buffer's address. If the volume is unnamed, DiskGetVolumeName() writes the temporary volume name.

Note that all the routines which return a volume's name will return the temporary name if the volume is unnamed. For this reason, if you want to find out if a volume is unnamed, you must use a special purpose routine, namely DiskCheckUnnamed() . This Boolean routine is passed the disk's handle. If the volume does not have a permanent label, the routine returns true ; otherwise, it returns false .

If you want to know how much free space is available on a disk, call the routine DiskGetVolumeFreeSpace() . The routine is passed the disk handle; it returns (as a dword) the number of free bytes available. If the volume is currently read-only (e.g. a floppy disk with the write-protect tab set), it returns the amount of space that would be available if the volume were made read/write. If the volume is, by its nature, not writable (e.g. a CD-ROM disk), the routine will return zero. It will also return zero if an error condition occurs; in this case, it will also set the thread's error value.

If you want to know what drive a volume is associated with, call DiskGetDrive() . This routine takes one argument, namely the volume's disk handle. It returns the number of the drive which had that disk. Note that it will return this value even if that drive is no longer usable.

If you know the label of a volume which has been registered and you need to find out its handle, call the routine DiskFind() . The routine takes two arguments: the address of a null-terminated string containing the volume name and a pointer to a variable of the DiskFindResult enumerated type. It will return the disk's handle; if no disk with the specified name has been registered, it will return a null handle. DiskFindResult has the following possible values:

DFR_UNIQUE
Exactly one volume with the specified name has been registered. The handle of that volume was returned.
DFR_NOT_UNIQUE
Two or more volumes with the specified label have been registered. The handle of an arbitrary one of these volumes was returned. If you want to find the handles of all of these disks, call DiskForEach() , described below.
DFR_NOT_FOUND
No disk with the specified label has been registered. A null handle was returned.

To check if a volume is writable, call the Boolean routine DiskCheckWritable() . The routine takes one argument, the disk's handle. If the disk is writable, the routine returns true (i.e. non-zero). If the disk is non-writable, the routine returns false (i.e. zero).

To see if a disk is being used by any threads, call DiskCheckInUse() . The routine takes one argument: the disk's handle. It returns true (i.e. non-zero) if a file on the disk is open or if any thread has a directory on that disk in its directory stack (see Current Path and Directory Stack ). If neither condition applies, the routine returns false (i.e. zero).

If you want to perform an action on every disk, call DiskForEach() . This routine takes one argument, a pointer to a Boolean callback routine. The callback routine should take a single argument, the handle of a disk. DiskForEach() calls the callback routine once for every registered disk. It passes the disk handle to the callback routine, which can take any action it wants; for example, it could call one of the other disk-information routines. The callback routine can make DiskForEach() halt prematurely by returning a non-zero value. If the callback routine forced an early halt, DiskForEach() returns the last disk handle which had been passed to the callback routine; otherwise it returns a null handle. This routine is commonly called to look for a specific disk. To do this, simply have the callback routine check each disk to see if it is the one sought; if it is, simply return true , and DiskForEach() will return that disk's handle.

DiskForEach() does not need to examine the actual disks; it works from the information the file-system stores about all registered disks. This means that DiskForEach() will not have to prompt the user to insert any disks. Of course, the callback routine may need to examine the disks, in which case the user will be prompted when necessary.

Saving and Restoring a Disk Handle

DiskSave(), DiskRestore(), DiskRestoreError

A disk does not necessarily have the same handle from one execution of GEOS to another. This can pose a problem for an application which is restarting from a state file. In order to reopen a file, it has to know the file's location. If it knows the file's location relative to a standard path, there is no problem, since the application can use the standard path constant in the place of a disk handle. If the file is not in a standard path, the application will need some way of figuring out the disk's handle on restart.

For this reason, GEOS provides DiskSave() and DiskRestore() . DiskSave() saves information about a disk in an opaque data structure. DiskRestore() reads such a data buffer and returns the handle of the disk described; it even arranges to prompt the user if the disk has not been registered yet.

To save a disk handle, call DiskSave() . This routine takes three arguments:

  • The disk handle.
    This may be a standard path.
  • A pointer to a buffer.
    DiskSave() will write opaque data to that buffer; you will need to pass that data to DiskRestore() to restore the handle.
  • A pointer to an integer.
    When you call DiskSave() , that integer should contain the size of the buffer (in bytes). When DiskSave() exits, the integer will contain the size of the buffer needed or used (as described below).

If DiskSave() was successful, it will return true . The integer parameter will contain the size of the buffer actually needed; for example, if the buffer had been 100 bytes long and DiskSave() returns 60, you can safely free the last 40 bytes in the buffer. If DiskSave() failed, it will return false . If it failed because the buffer was too small, it will write the size needed into the integer passed; simply call DiskSave() again with a large enough buffer. If DiskSave() failed for some other reason (e.g. the disk belongs to a drive which no longer exists), it will write a zero value to the integer.

To restore a disk, call DiskRestore() . This routine takes two arguments:

  • A pointer to the opaque data structure written by DiskSave() .
  • A pointer to a callback routine. The callback routine is called if the user must be prompted to insert the disk. If you pass a null function pointer, DiskRestore() will fail in this situation.

If the disk in question has already been registered or is currently in its drive, DiskRestore() will return its handle. If the disk is not registered and is not in any drive, DiskRestore() will call the callback routine. The callback routine should accept the following four arguments:

  • A pointer to a null-terminated string containing the drive name, specified according to the DOS convention (e.g. "C:").
  • A pointer to a null-terminated string containing the disk volume label.
  • The DiskRestoreError (see below) which would be returned if the callback routine hadn't been called.
  • A pointer to a pointer to the opaque data structure. If the callback routine causes the data structure to move, it should fix up the pointer to point to the new location. Note that the two strings (the drive name and the disk label) are located within this data structure; thus, if you cause this structure to move, both of those pointers will be invalidated.

If the callback routine believes the user inserted the correct disk, it should return DRE_DISK_IN_DRIVE. Otherwise, it should return a DiskRestoreError constant. In this case, DiskRestore() will fail and set the thread's error value to the constant specified. If the callback routine returns an error, that error will generally be DRE_USER_CANCELLED_RESTORE.

If DiskRestore() is successful, it will return the disk handle; this may be different from the disk's handle in the previous execution. You may now free the data buffer, if you like. If DiskRestore() fails, it will return a null handle and set the thread's error value. There are several different DiskRestoreError values; they are listed below.

DRE_DRIVE_NO_LONGER_EXISTS
The disk had last been used in a drive which is no longer attached to the system (or the appropriate file-system driver is no longer present).
DRE_REMOVABLE_DRIVE_DOESNT_HOLD_DISK
The disk was in a removable-media drive, and that drive doesn't contain the disk.
DRE_USER_CANCELLED_RESTORE
A callback routine was called and returned this value to DiskRestore() .
DRE_COULDNT_CREATE_NEW_DISK_HANDLE
DiskRestore() found the disk in the drive but was for some reason unable to create the disk handle.
DRE_REMOVABLE_DRIVE_IS_BUSY
The appropriate drive exists but is unavailable due to some time-consuming operation (e.g. a disk format).

Other Disk Utilities

DiskSetVolumeName(), DiskFormat(), DiskCopy(), FormatError, DiskCopyCallback, DiskCopyError

GEOS provides several utilities for working with disks. These utilities allow geodes to copy disks, format them, and change their volume names. Most applications will never need to use these utilities; they can rely on the users to take care of disk formatting with an application like GeoManager. However, some applications will want to make use of them. For example, an archiving program might automatically format storage disks and give them appropriate labels.

Changing a Volume Name

If you want to set or change a volume's name, you should call DiskSetVolumeName() . This routine takes two arguments: the volume's handle and the address of a null-terminated string (containing the new volume name). If it is able to change the volume's name, it returns zero; otherwise, it returns an error code. It sets or clears the thread's error value appropriately. The following error codes may be returned:

ERROR_INVALID_VOLUME
An invalid disk handle was passed to the routine.
ERROR_ACCESS_DENIED
For some reason, the volume's name could not be changed. For example, the volume might not be writable.
ERROR_DISK_STALE
The drive containing that disk has been deleted. This usually only happens with network drives.
Formatting a Disk

If a geode needs to format a disk, it can call the routine DiskFormat() . This routine can do either low-level or high-level ("soft") formats. The routine does not interact with the user interface; instead, it calls a callback routine, which can arrange any such interaction. DiskFormat() takes seven arguments:

  • The number of the drive containing the disk to be formatted.
  • The address of a null-terminated string containing the disk's new volume name.
  • A member of the MediaType enumerated type (see MEDIA_160K, MEDIA_180K, MEDIA_320K, MEDIA_360K These are all sizes used by 5.25-inch disks. ).
  • Flags indicating how the format should be done. The following flags are available:
DFF_CALLBACK_PERCENT_DONE
A callback routine will be called periodically. The callback routine will be passed a single argument, namely the percentage of the format which has been done, expressed as an integer.
DFF_CALLBACK_CYL_HEAD
A callback routine will be called periodically. The callback routine will be passed a single argument, namely the cylinder head being formatted. If both DFF_CALLBACK_PERCENT_DONE and DFF_CALLBACK_CYL_HEAD are passed, results are undefined.
DFF_FORCE_ERASE
A "hard format" should be done; that is, the sectors should be rewritten and initialized to zeros. If this flag is not set, DiskFormat() will do a "soft format" if possible; it will check the sectors and write a blank file allocation table, but it will not necessarily erase the data from the disk.
  • A pointer to an unsigned integer variable; the number of good sectors on the disk will be written here.
  • A pointer to an unsigned integer variable; the number of bad sectors on the disk will be written here.
  • The address of a Boolean callback routine, as described above. The routine should be passed either the current cylinder and head or the percentage formatted, depending on the flag passed to DiskFormat() . It should return true to abort the format, or false (i.e. zero) to continue with the format. If neither DFF_CALLBACK_PERCENT_DONE nor DFF_CALLBACK_CYL_HEAD is passed, the callback routine will never be called, so this argument may be a null pointer.

DiskFormat() returns a member of the FormatError enumerated type. If the format was successful, it will return the constant FMT_DONE (which is guaranteed to equal zero). Otherwise, it will return one of the following constants:

FMT_DRIVE_NOT_READY
FMT_ERROR_WRITING_BOOT
FMT_ERROR_WRITING_ROOT_DIR
FMT_ERROR_WRITING_FAT
FMT_ABORTED
FMT_SET_VOLUME_NAME_ERROR
FMT_CANNOT_FORMAT_FIXED_DISKS_IN_CUR_RELEASE
FMT_BAD_PARTITION_TABLE,
FMT_ERR_READING_PARTITION_TABLE,
FMT_ERR_NO_PARTITION_FOUND,
FMT_ERR_MULTIPLE_PRIMARY_PARTITIONS,
FMT_ERR_NO_EXTENDED_PARTITION_FOUND
FMT_ERR_CANNOT_ALLOC_SECTOR_BUFFER
FMT_ERR_DISK_IS_IN_USE
FMT_ERR_WRITE_PROTECTED
FMT_ERR_DRIVE_CANNOT_SUPPORT_GIVEN_FORMAT
FMT_ERR_INVALID_DRIVE_SPECIFIED
FMT_ERR_DRIVE_CANNOT_BE_FORMATTED
FMT_ERR_DISK_UNAVAILABLE
Copying Disks

GEOS provides a routine for copying disks. This routine, DiskCopy() , maintains a balance between the two goals of limiting memory usage and minimizing disk swapping. It will reformat the destination disk if necessary. The routine does a sector-for-sector copy; therefore, the destination disk must either be of exactly the same type as the source disk (i.e., same medium and size), or it must be reformatable to be the same size. For this reason, neither the source nor the destination may be a fixed disk.

DiskCopy() does not interact with the user directly, even though the user may have to swap disks. Instead, it calls a callback routine whenever interaction with the user may be necessary. The routine takes the following arguments:

  • The drive number of the source drive.
  • The drive number of the destination drive. This may be the same as the source drive.
  • The address of a Boolean callback routine. The routine must take three arguments: a member of the DiskCopyCallback enumerated type (described below), a disk handle, and a word-sized parameter holding any other appropriate information. The routine should return non-zero to abort the copy; otherwise, it should return zero.

The callback routine is called under a variety of circumstances. When it is called, the first argument passed is a member of the DiskCopyCallback enumerated type, which specifies both why the callback routine was called and what the other arguments signify. DiskCopyCallback contains the following types:

CALLBACK_GET_SOURCE_DISK
The callback routine should prompt the user to insert the source disk. The second argument is meaningless for this call. The third argument is the number identifying the drive.
CALLBACK_GET_DEST_DISK
The callback routine should prompt the user to insert the destination disk. The second argument is meaningless for this call. The third argument is the number identifying the drive.
CALLBACK_REPORT_NUM_SWAPS
The second argument is meaningless for this call. The third argument is the number of disk swaps that will be necessary to copy the disk. The callback routine may wish to report this number to the user and ask for confirmation.
CALLBACK_VERIFY_DEST_DESTRUCTION
If the destination disk has already been formatted, the callback routine will be called with this parameter. The callback routine may wish to remind the user that the destination disk will be erased. The second argument is the handle of the destination disk; this is useful if, for example, you want to report the disk's name. The third argument is the destination drive's number. As in the other cases, the callback routine can abort the format by returning non-zero.
CALLBACK_REPORT_FORMAT_PERCT
If the destination disk needs to be formatted, DiskCopy() will periodically call the callback routine with this parameter. In this case, the second argument will be meaningless; the third parameter will be the percentage of the destination disk which has been formatted.
CALLBACK_REPORT_COPY_PERCT
While the copy is taking place, DiskCopy() will periodically call the callback routine with this parameter. In this case, the second parameter will be meaningless; the third parameter will be the percentage of the copy which has been completed.

If the copy was successful, DiskCopy() returns zero. Otherwise, it returns a member of the DiskCopyError enumerated type, which has the following members:

ERR_DISKCOPY_INSUFFICIENT_MEM
This is returned if the routine was unable to get adequate memory.
ERR_CANT_COPY_FIXED_DISKS
ERR_CANT_READ_FROM_SOURCE
ERR_CANT_WRITE_TO_DEST
ERR_INCOMPATIBLE_FORMATS
ERR_OPERATION_CANCELLED
This is returned if the callback routine ever returned a non-zero value, thus aborting the copy.
ERR_CANT_FORMAT_DEST

The File System: 4 Directories and Paths

Information, whether code or data, is always stored in files. However, storage volumes are not simply collections of files. Rather, they are organized into directories. There are two main reasons for this organization.

First, a large storage device can easily contain hundreds or thousands of files. Computer users need to be able to find a specific file quickly. If they were confronted with a list of all the files on a storage device, they would have a hard time finding the one they want. The directory structure solves this problem; users can navigate through the directory tree until they find the file they want.

The other reason for the directory structure is to expand the name-space for files. If every file on a volume were forced to have a unique name, users would find themselves severely restricted. Directories allow more flexibility; since a file is identified by both its name and its location, its name need only be unique within its directory.

A file can be uniquely specified by a full path . A full path specifies the volume containing the file as well as all the directories containing the file, starting with the root directory. The volume specification can usually be omitted, indicating that the file is on the same volume as the current working directory. Alternatively, the file can be specified with a relative path . A relative path specifies the file's position relative to the current working directory, instead of starting with the root.

In most implementations of DOS, there is a standard way of describing a path. These conventions are used in GEOS as well. A full path begins with a backslash, which represents the root directory. This may be followed by one or more directory specifications, separated by backslashes. The first directory listed would be a member of the root directory; each following directory would be a member of the preceding directory. A relative path is the same, except that it does not begin with a backslash. In either a full or a relative path, their are two special directory names. A single period (".") specifies the current directory; that is, the path "." indicates the current working directory, and the path "\GEOWORKS\DOCUMENT" is the same as the paths "\GEOWORKS\DOCUMENT\." and "\GEOWORKS\.\DOCUMENT". A double period ("..") indicates the parent of the current directory; thus, "\GEOWORKS\DOCUMENT\.." is equivalent to "\GEOWORKS".

The GEOS file management system allows each thread to have a working directory. Whenever a thread needs to open a file, it can rely on its working directory instead of passing a full path. GEOS provides an added mechanism: it defines certain system standard paths which can be reached with simple system commands. These paths provide a way to standardize directory usage; an application might, for example, keep a certain data file in the standard PRIVDATA directory, leaving the user to decide where that PRIVDATA directory may be. This is covered in detail in the following section.

Standard Paths

Current Path and Directory Stack

Creating and Deleting Directories

The File System: 4.1 Directories and Paths: Standard Paths

The GEOS system is designed to run on a wide variety of architectures. For example, it can be stored on a hard disk, recorded in ROM chips, or resident on a network server and run by members of the network. This presents a difficulty. The system uses many files, storing both code (such as libraries) and data (such as fonts). It can't assume that they are in any specific place. For example, a user might be running GEOS from a network server, but she might have several fonts for personal use on her own hard disk. The system has to be able to look in all the right places. Applications have a similar dilemma. An application needs to be able to choose an appropriate place to create data files, and needs to be able to find them later.

The solution to this is to use standard paths . There are a number of standard directories GEOS uses. Each one of these has a constant associated with it. The constants are treated as special disk handles. For example, the routine FileSetCurrentPath() (described in Current Path and Directory Stack ) takes two arguments, a disk handle and a path string. If the disk handle passed is the constant SP_DOCUMENT and the string passed is "Memos", the routine will look in the standard document path for a directory called "Memos" and will open it.

There are two advantages to standard paths. The first is that they give the system and the applications points of reference. An application does not need to worry where to write its internal data files; it can simply install them in the standard PRIVDATA directory.

The second advantage is that the standard paths do not need to correspond to any one specific directory. A standard path can actually be several different DOS directories. For example, the GEOS kernel looks for font files in the standard path SP_FONT. The user may define this path to be several directories; it might comprise a local directory for the most commonly used fonts, a network directory for some decorative fonts, etc. The file system would automatically locate all of these fonts. Similarly, the SP_DOCUMENT directory might comprise a directory on a ROM disk for read-only files, as well as a directory on a hard disk where files are written. A standard path is considered "read-only" if and only if all of the directories which make up the standard path are read-only. When you create a file directly in a standard path (as opposed to in a subdirectory of a standard path), the file system will write it to the directory on the disk containing the geos.ini file.

The standard paths have a default hierarchy. For example, the standard path SP_VIDEO_DRIVERS usually refers to a directory named VIDEO which belongs to the standard path SP_SYSTEM. However, the user is allowed to define each standard path however he or she wishes. For this reason, the programmer may not make any assumptions about where each standard path is located.

Below is a list of standard paths currently implemented. The paths are described as if they were single directories; remember, however, that each one of these may comprise several directories. Each path is accompanied by notes on how it is used and where it might commonly be found. The user can decide where each of these directories is, so make no assumptions about their locations. These paths will always be supported; future versions of GEOS may add new paths.

SP_TOP
The top level directory. This is the directory which contains GEOS.INI. This is often C:\GEOWORKS.
SP_APPLICATION
All non-system applications are in this directory or a directory belonging to it. This is often a WORLD directory under SP_TOP.
SP_DOCUMENT
All document files should go here. This is commonly a DOCUMENT directory under SP_TOP.
SP_SYSTEM
All libraries and drivers belong to this directory. All libraries must go in this directory, not in a subdirectory. Drivers are further grouped into subdirectories, one for each type of driver. This is commonly a SYSTEM directory under SP_TOP. Geodes should never need to directly access this directory, other than for installing drivers to it.
SP_PRIVATE_DATA
This contains data files which should not be accessed by users. By convention, each geode creates a subdirectory with its own data files. For example, the application HELLO.GEO would use a directory called HELLO in SP_PRIVATE_DATA. This is commonly a PRIVDATA directory under SP_TOP.
SP_DOS_ROOM
This directory contains the DOS-room launchers. It is commonly a DOSROOM directory under SP_TOP.
SP_STATE
This directory contains all state files. It is commonly a STATE directory under PRIVDATA.
SP_USER_DATA
This directory holds data files that the user may add to, delete, upgrade, or otherwise change. Each data file type should have its own subdirectory. This is commonly a USERDATA directory under SP_TOP.
SP_FONT
This directory contains all font data files, no matter what format they are. It is often USERDATA\FONT.
SP_SPOOL
This directory contains all application spooler files. It is commonly USERDATA\SPOOL.
SP_SYS_APPLICATION
This directory contains all system applications. For example, it contains the Geoworks Pro "Welcome" application. These applications should not be launched by the user from the desktop. This directory is commonly SYSTEM\SYSAPPL.
SP_MOUSE_DRIVERS
This directory contains all mouse drivers. It is commonly SYSTEM\MOUSE.
SP_PRINTER_DRIVERS
This contains all printer drivers. It is commonly SYSTEM\PRINTER.
SP_FILE_SYSTEM_DRIVERS
This directory contains drivers for file systems. It has both DOS and network drivers. It is commonly SYSTEM\FS.
SP_VIDEO_DRIVERS
This directory contains video drivers. It is commonly SYSTEM\VIDEO.
SP_SWAP_DRIVERS
This directory has all of the swap drivers. It is commonly SYSTEM\SWAP.
SP_KEYBOARD_DRIVERS
This directory has all of the keyboard drivers. It is commonly SYSTEM\KEYBOARD.
SP_FONT_DRIVERS
This directory has all of the font drivers. It is commonly SYSTEM\FONT.
SP_IMPORT_EXPORT_DRIVERS
This directory has all of the import/export libraries. It is commonly SYSTEM\IMPEX.
SP_TASK_SWITCH_DRIVERS
This directory contains all task-switching drivers. It is commonly SYSTEM\TASK.
SP_HELP_FILES
This directory contains all help files. It is commonly USERDATA\HELP.
SP_TEMPLATE
This directory contains document templates. It is commonly DOCUMENT\TEMPLATE.
SP_POWER_DRIVERS
This directory contains power-management drivers. It is commonly SYSTEM\POWER.
SP_HWR
This directory contains the handwriting recognition libraries. It is commonly SYSTEM\HWR.
SP_WASTE_BASKET
This directory contains files that have been dragged to the waste basket but have not yet been thrown out. All non-open files and directories in this path are periodically deleted. Temporary files should usually be created here; they will not be deleted until after they have been closed. This directory is commonly PRIVDATA\WASTE.
SP_BACKUP
This directory contains backup files made by the document control. It is commonly \PRIVDATA\BACKUP.

The File System: 4.2 Directories and Paths: Current Path and Directory Stack

FileSetCurrentPath(), FileGetCurrentPath(), FileConstructFullPath(), FileParseStandardPath(), FileResolveStandardPath(), FilePushDir(), FilePopDir(), FileResolveStandardPathFlags

Every thread has a current path . When the thread opens a file, it can pass just the name of the file; the file system combines this name with the current path to find the file. The path is a combination of a disk handle and a directory sequence. To set the thread's current path, call the routine FileSetCurrentPath() , which takes two arguments: a disk handle and a pointer to a null-terminated string. The string should contain a sequence of directories specified in the normal DOS convention. To change to a standard path, pass the path constant as the disk handle and a null string (i.e. a pointer to a null byte). To change to a subdirectory of a standard path, pass the path constant as the disk handle and a pointer to a relative or absolute path specification; for example, to change to the HELLO directory in PRIVDATA, pass the disk handle constant SP_PRIVATE_DATA and a pointer to the string "HELLO". FileSetCurrentPath() returns the handle of the disk. If you change to a standard path, it returns the path constant; if you change to a directory within a standard path, it returns the constant of the closest standard path. In the event of error, it returns a null handle and sets the thread's error value. The error most commonly returned is ERROR_PATH_NOT_FOUND, indicating that the specified directory could not be found or does not exist. To set the current path equal to a standard path, use the FileSetStandardPath() routine.

To find out the current path, call the routine FileGetCurrentPath() . This routine takes two arguments: the address of a character buffer and the size of the buffer. It returns the handle of the current path's disk and writes the path (without drive specifier) to the buffer, truncating the path if the buffer is too small. If the directory is a standard path or a subdirectory of one, FileGetCurrentPath() will return the disk-handle constant for that path and will write an absolute path to the buffer. If you want a full path, use FileConstructFullPath() , described below.

To translate a standard path into a full path, call FileConstructFullPath() , which takes five arguments:

  • A Boolean value indicating whether the drive name should be prepended to the path string returned.
  • The handle of a disk. A null argument indicates that the path string passed is relative to the current working directory. The handle may be a standard path constant, indicating that the path string passed is relative to that standard path.
  • A pointer to a string containing a path relative to the location indicated by the previous argument. This may be an empty string.
  • A pointer to a pointer to a character buffer. The path will be written to this buffer. The routine will update this to be a pointer to a pointer to the null terminator.
  • The length of that buffer.

The routine writes the full path to the buffer and returns the disk handle. If it is unable to construct a full path, it returns a null handle. The pointer to the path buffer will have been changed to point to the end of the string; you may want to keep a copy of the old value of the pointer around when using this routine.

To find the standard path to a given location, call the routine FileParseStandardPath() . This routine is passed two arguments:

  • The handle of the disk. Passing a null disk handle indicates that the path string contains a drive specifier.
  • A pointer to a pointer to a path string. This path should begin at the root of the disk specified.

FileParseStandardPath() returns the standard path constant. It also updates the pointer to point to the remaining portion of the path. For example, if you pass a pointer to a pointer to the string "\GEOWORKS\DOCUMENT\MEMOS\APRIL", the pointer would be updated to point to the "\MEMOS\APRIL" portion, and the handle SP_DOCUMENT would be returned. If the path passed does not belong to a standard path, the constant SP_NOT_STANDARD_PATH will be returned, and the pointers will not be changed.

Because each standard path is made up of one or more directories (possibly on different devices), it can be hard to know just where a file is. For that reason, GEOS provides FileResolveStandardPath() . This routine is passed a relative path to a file; it then constructs the full path of the file, starting from the root of the disk ( not from a standard path); it also returns the handle of the actual disk containing the file. FileResolveStandardPath() is passed several arguments:

  • A pointer to a pointer to a character buffer. The full path will be written to that buffer, and the pointer will be updated to point to the null terminator.
  • The length of the buffer, in bytes.
  • A pointer to a relative path string. This path is relative to the current working directory, which is usually a standard path.
  • A set of FileResolveStandardPathFlags.
  • A pointer to an empty FileAttrs buffer; the routine will fill in this buffer with the passed file's attributes, if any.

FileResolveStandardPath() writes the full, absolute path to the buffer specified. It also returns the handle of the disk containing that file. If it cannot find the file specified, it returns a null handle. There are two FileResolveStandardPathFlags available:

FRSPF_ADD_DRIVE_NAME
The path string written to the buffer should begin with the drive name (e.g., "C:\GEOWORKS\DOCUMENT\MEMOS").
FRSPF_RETURN_FIRST_DIR
FileResolveStandardPath() should not check whether the passed path actually exists; instead, it should assume that the path exists in the first directory comprising the standard path, and return accordingly.

In addition to having a current path, every thread has a directory stack . The stack is used for switching paths quickly. You can at any time push the current path onto the stack by calling FilePushDir() . This routine pushes the directory on the directory stack and returns nothing. You can change the current path to the one on top of the directory stack by calling FilePopDir() . This pops the top directory off the stack and makes it the current path. (If the directory stack is empty, the result is undefined.) These routines are very useful when you write subroutines which may need to change the current working directory; they can push the old directory at the start of the routine and pop it at the end.

Files are often specified "by their paths." This simply means specifying them with a string containing the directory path and ending with the file name. This path may be either relative or absolute.

The File System: 4.3 Directories and Paths: Creating and Deleting Directories

FileCreateDir(), FileDeleteDir()

You can create directories with FileCreateDir() . The routine takes a single argument, namely the address of a character string. If the string is simply a directory name, it will attempt to create that directory at the current location. If the string is a relative path, it will create the directory at the end of the path, if possible. For example, passing the string "Memos\September" will cause it to check if there is a directory called "Memos" at the current location. If there is, it will create the directory "September" inside the directory "Memos". If the string is an absolute path (i.e. there is a backslash before the first directory name), it will behave the same way, but it will start with the root directory. The routine returns zero if it was successful. If it was unsuccessful, it will return one of the following error codes:

ERROR_INVALID_NAME
The name passed was inappropriate for directories on that device.
ERROR_PATH_TOO_LONG
The path string was longer than is permitted by the file system for that device.
ERROR_FILE_EXISTS
A file or directory with the specified name already exists at the specified location.
ERROR_PATH_NOT_FOUND
The path string was in some way invalid; for example, it might have instructed FileCreateDir() to create the directory inside of a directory which does not exist.
ERROR_ACCESS_DENIED
The thread is not able to create directories in the specified location.
ERROR_DISK_STALE
The drive that disk was on has been removed.
ERROR_DISK_UNAVAILABLE
The validation of the disk in that drive was aborted by the user.
ERROR_WRITE_PROTECTED
The volume is write-protected.

Note that the directory name can be any acceptable GEOS file name; that is, it may be up to 32 characters long and can contain any characters other than backslashes, colons, asterisks, or question marks. For further information about GEOS file names, see DOS Files and GEOS Files .

You can delete directories with FileDeleteDir() . This routine takes a single argument, namely the address of a character string. This string can specify a relative or absolute path, as with FileCreateDir() . It attempts to delete the directory specified. (Note that you are not allowed to delete your current directory or a non-empty directory.) If it successfully removes the directory, it returns zero. Otherwise, it returns one of the following members of the FileError type:

ERROR_PATH_NOT_FOUND
The directory specified could not be found or does not exist.
ERROR_IS_CURRENT_DIRECTORY
This directory is some thread's current directory, or else it is on some thread's directory stack. Note that you cannot delete your own working directory.
ERROR_ACCESS_DENIED
The calling thread does not have permission to delete the directory. This is also returned if the directory was on a read-only device.
ERROR_DIRECTORY_NOT_EMPTY
The directory specified is not empty. Delete all the files before you attempt to delete the directory.
ERROR_DISK_STALE
The drive that disk was on has been removed.
ERROR_DISK_UNAVAILABLE
The validation of the disk in that drive was aborted by the user.

The File System: 5 Files

When data is not actually in a computer's memory, it needs to be grouped together in a manageable form. Most storage devices group data together into files . A file is a collection of information. It may be a program, either in machine language or in a higher-level language (as with batch files); or it may be data.

When an application is accessing a data file, it is said to have that file open . It may be open for read and write access, just for reading, or just for writing. If a file is open, it can not be moved or deleted (although it can be copied).

You will probably not have to read all of this section. Bytewise File Operations ( Bytewise File Operations ) is important only if you will be using DOS files or GEOS byte files. Most applications will work with GEOS Virtual Memory (VM) files.

DOS Files and GEOS Files

Files and File Handles

GEOS Extended Attributes

File Utilities

FileEnum()

Bytewise File Operations

The File System: 5.1 Files: DOS Files and GEOS Files

Most file systems have a simple convention of what a file is. Applications treat files as if they were a sequence of bytes. They can read the bytes in order from the file, copying a sequence of bytes into memory. They have a "position" in the file, indicating the next byte to be read. An application can copy data from memory to a file, replacing the data there.

GEOS allows geodes to access any type of DOS file using the normal DOS conventions. However, it also provides its own file format, the GEOS file. GEOS files are stored on the disk as normal DOS files. This means that they can be copied from one disk to another by any normal DOS procedure; they can be uploaded or downloaded, compressed, archived, or transferred to different devices exactly as if they were ordinary DOS files. Nevertheless, when a system is running GEOS, the files have added functionality. GEOS data files have special extended attributes , which keep track of such things as the file's tokens, protocol numbers, etc. For more information about extended attributes, see GEOS Extended Attributes .

Most GEOS data files are Virtual Memory files. VM files can automatically back up data; they allow their users to insert data at any point in the file, and when the file's user needs access to data the VM manager automatically allocates memory and swaps the data into it. These files are created and accessed using special VM routines; for more information, see the VM chapter. Note that you can have a GEOS file hold raw data instead of having VM capability. Such a file is known as a "byte" file since it is treated as a sequence of bytes with no structure except what is specifically added by the file's creator. All of the routines for working with DOS files can be used with GEOS byte files.

One basic difference between GEOS files and DOS files is in naming conventions. Each file system and disk-operating system has its own convention for how files may be named. By far, the most common convention is the one used by MS-DOS: each file is identified by a name of up to eight characters, followed by an optional extension of up to three characters. There are further restrictions on which characters may be part of a name; for example, none of the characters may be a period.

GEOS provides more versatility. Each file has two different names. It has a native name; this is the name used by the device holding the file and must meet all of its restrictions. For example, if the file is kept on a usual DOS-compatible disk, the native name would be in the FILENAME.EXT format. The other name is the virtual file name. This may contain a number of characters equal to FILE_LONGNAME_LENGTH and may contain any characters other than backslashes ("\"), colons (":"), asterisks ("*"), and question marks ("?"). Any time a file's name is needed, either the virtual or the native file name may be used. When a file is created, GEOS automatically maps its virtual name into an appropriate native name.

Special GEOS information, such as the file's virtual name and extended attributes, is stored in the body of the file itself. Thus, while applications should not access this information directly, they can still be assured that it will be preserved when the file is copied by any normal DOS techniques. This information is stored in a special header which is transparent to geodes. If you use one of the bytewise file operations, you will not be able to affect the header.

The File System: 5.2 Files: Files and File Handles

FileDuplicateHandle()

In order to read or change a file, you must open it with an appropriate routine. When you open a file, the file system sees to it that the file will not be erased or moved until you close it. When a geode exits, all files it has open are automatically closed. When GEOS exits, all files are closed. If an application restarts from a saved state, it will have to reopen all files.

When you open a file, the GEOS file system returns a file handle . This is a global handle associated with that file. Whenever you need to perform an action on a file you have opened, you specify the file by passing its handle. Note that there may be several handles which all refer to the same file. If more than one thread will need to use the same handle, they may synchronize their access by using HandleP() and HandleV() ; for details, see the Memory Management chapter. The Virtual Memory routines include data-access synchronization routines for use with VM files; for details, see the VM chapter.

It is sometimes useful to have two different handles which indicate the same file. GEOS provides the routine FileDuplicateHandle() . This routine takes one argument, namely the handle of an open file. It creates and returns another handle which indicates the same file. You will have to close both handles to close the file. FileDuplicateHandle() works on any type of file handle; that is, it can be used on the handles of DOS files, GEOS byte files, or VM files. Note that the duplicate handle will have the same read/write position as the original.

The File System: 5.3 Files: GEOS Extended Attributes

FileGetHandleExtAttributes(), FileGetPathExtAttributes(), FileSetHandleExtAttributes(), FileSetPathExtAttributes(), FileExtAttrDesc, FileDateAndTime, FileAttrs

All GEOS files, whether they contain code or data, have special extended attributes . Geodes cannot look at these directly; instead, they make calls to the file system when they want to examine or change the attributes. There are many different extended attributes; however, they are all accessed and changed in a uniform way. Some of the extended attributes are also supported for non-GEOS files. The following extended attributes are currently available:

FEA_MODIFICATION
FEA_FILE_ATTR
FEA_SIZE
FEA_FILE_TYPE
FEA_FLAGS
FEA_RELEASE
FEA_PROTOCOL
FEA_TOKEN
FEA_CREATOR
FEA_USER_NOTES
FEA_NOTICE
FEA_CREATION
FEA_PASSWORD
FEA_CUSTOM
FEA_NAME
FEA_GEODE_ATTR
FEA_PATH_INFO
FEA_FILE_ID
FEA_DESKTOP_INFO
FEA_DRIVE_STATUS
FEA_DISK
FEA_DOS_NAME
FEA_OWNER
FEA_RIGHTS

There are also two special constants, FEA_MULTIPLE and FEA_END_OF_LIST. These are also described below.

There are two different routines to read a file's extended attributes: FileGetHandleExtAttributes() and FileGetPathExtAttributes() . These routines are the same except in the way the file is specified: in one, the handle of an open file is passed, whereas in the other, the address of a path string is passed.

FileGetHandleExtAttributes() takes four arguments. The first is the handle of the file whose attributes are desired; this may be a VM file handle or a byte-file handle. The second is a constant specifying the attribute desired. All extended attributes which are currently supported are listed above; more may be added later. The third is a pointer to a buffer; the attribute's value will be written into that buffer. The fourth argument is the size of the buffer (in bytes). Before it returns, FileGetHandleExtAttributes() will write the value of the attribute into the buffer. If successful, it will return zero; otherwise, it will return one of the following error codes:

ERROR_ATTR_NOT_SUPPORTED
The file system does not recognize the attribute constant passed.
ERROR_ATTR_SIZE_MISMATCH
The buffer passed was too small for the attribute requested.
ERROR_ATTR_NOT_FOUND
The file does not have a value set for that attribute. This is returned if you try to get certain extended attributes of a non-GEOS file.
ERROR_ACCESS_DENIED
You do not have read-access to the file.

FileGetHandleExtAttrs() can also fetch several attributes at once. For details on this, see the section on FEA_MULTIPLE ( FEA_MULTIPLE ).

You can get a file's extended attributes without having it open by calling FileGetPathExtAttributes() . This routine takes a pointer to a null-terminated path string instead of a file handle. This makes it suitable for examining the attributes of an executable file or directory. Note that the file system will still have to open the file in order to get the attributes. If any geode (including the caller) has the file open with "deny-read" exclusive, the call will fail with error condition ERROR_ACCESS_DENIED. If it could not find the file specified, it will return ERROR_FILE_NOT_FOUND.

To change one of a file's extended attributes, make a call either to FileSetHandleExtAttributes() or to FileSetPathExtAttributes (). These routines take the same arguments as the FileGet...() routines above; however, they copy the data from the buffer into the attribute, instead of vice versa. These routines return zero if the operation was successful. Otherwise, they return one of the following error codes:

ERROR_ATTR_NOT_SUPPORTED
The file system does not recognize the attribute constant passed. This is returned if you try to set an extended attribute for a non-GEOS file.
ERROR_ATTR_SIZE_MISMATCH
The buffer passed was the wrong size for the attribute specified.
ERROR_ACCESS_DENIED
FileSetHandleExtAttributes() returns this if the caller does not have write-access to the file. FileSetPathExtAttributes() returns this if any geode (including the caller) has the file open with "deny-write" exclusive access, or if the file is not writable.
ERROR_CANNOT_BE_SET
The extended attribute cannot be changed. Such attributes as FEA_SIZE and FEA_NAME cannot be changed with the FileSet...() routines.
FEA_MULTIPLE

By passing this extended attribute, you can get or set several extended attributes at once. This is also the only way to get, set, or create a custom attribute. If you pass this, the other arguments are slightly different. The first argument is still the file specifier (handle or path), and the second argument is FEA_MULTIPLE. However, the third argument is the base address of an array of FileExtAttrDesc structures, and the fourth argument is the number of these structures in the array. The array has one element for each attribute you wish to get or set. Each FileExtAttrDesc structure has the following fields:

FEAD_attr
This is the numerical constant for the attribute to be read or set. If a custom attribute is being set, this should be FEA_CUSTOM.
FEAD_value
If the attribute is being set, this is the address of the new value. If the attribute is being read, this is the address of the buffer into which to copy the value.
FEAD_size
This is the size of the buffer or value pointed to by FEAD_value .
FEAD_name
If FEAD_attr is set to FEA_CUSTOM, this is the address of a null-terminated string containing the custom attribute's name. If FEAD_attr is set to anything else, this field is ignored.
FEA_CUSTOM

In addition to the system-defined extended attributes, any GEOS file may have any number of custom attributes. Each custom attribute is named by a null-terminated ASCII string. To create a custom attribute, call one of the FileSet...() routines, specifying the new attribute with a FileExtAttrDesc structure (as described immediately above). If you try to read a custom attribute which has not been defined for that file, the routine will fail with error condition ERROR_ATTR_NOT_FOUND.

Note that not all file systems support the use of custom extended attributes; therefore, you should write your applications so they can perform correctly without using them.

FEA_MODIFICATION

Every file has a "last modified" time. This is automatically updated whenever the file is written to. To find the modification time of a file, get the extended attribute FEA_MODIFICATION. The modification time is returned as a 32-bit FileDateAndTime value. The value has the following fields, each of which is small enough to fit in a signed-byte variable:

FDAT_YEAR
This field records the year, counting from a base year of 1980. (The constant FDAT_BASE_YEAR is defined as 1980.) This field is at an offset of FDAT_YEAR_OFFSET bits from the low end of the value.
FDAT_MONTH
This field records the month as an integer, with January being one. It is located at an offset of FDAT_MONTH_OFFSET.
FDAT_DAY
This field records the day of the month. It is located at an offset of FDAT_DAY_OFFSET.
FDAT_HOUR
This field records the hour on a 24-hour clock, with zero being the hour after midnight. It is located at an offset of FDAT_HOUR_OFFSET.
FDAT_MINUTE
This field records the minute. It is located at an offset of FDAT_MINUTE_OFFSET.
FDAT_2SECOND
This field records the second, divided by two; that is, a field value of 15 indicates the 30th second. (It is represented this way to let the second fit into 5 bits, thus letting the entire value fit into 32 bits.) It is located at an offset of FDAT_2SECOND_OFFSET.

The macros FDATExtractYear() , ...Month() , ...Day() , ...Hour() , ...Minute() , and ...2Second() all extract the specified field from a FileDateAndTime value. The macro FDATExtractSecond() extracts the FDAT_2SECOND field and doubles it before returning it. The FDATExtractYearAD() extracts the year field and adds the base year, thus producing a word-sized year value.

FEA_FILE_ATTR

There are certain attributes which all files have. These attributes specify such things as whether the file is hidden, whether it is read-only, and several other things. To get these attributes, call an extended attribute routine with argument FEA_FILE_ATTRIBUTES. The attributes are passed or returned in a FileAttrs record. This record has the following fields:

FA_ARCHIVE
This flag is set if the file requires backup. Backup programs typically clear this flag.
FA_SUBDIR
This flag is set if the "file" is actually a directory. Geodes may not change this flag.
FA_VOLUME
This flag is set if the "file" is actually the volume label. This flag will be off for all files a geode will ever see. Geodes may not change this flag.
FA_SYSTEM
This flag is set if the file is a system file. Geodes should not change this flag.
FA_HIDDEN
This flag is set if the file is hidden from normal directory searches. For example, a GenFileSelector, by default, does not list files that have this flag set.
FA_RDONLY
This flag is set if the file is read-only.

Many file systems (including DOS) require that files be closed when you set these attributes. For that reason, you cannot change these attributes with FileSetHandleExtAttributes() . You must use either FileSetPathExtAttributes() or FileSetAttributes() (described below in Getting and Setting Information about a Byte File ). If you try to set this field with FileSetHandleExtAttributes() , you will be returned ERROR_ATTR_CANNOT_BE_SET.

FEA_SIZE

This attribute is simply the size of the file in bytes. It is dword-sized (allowing for files as large as 4096 megabytes). The attribute can be read, but not directly changed.

FEA_FILE_TYPE

This attribute is a member of the GeosFileType enumerated type and should not be altered. The type has the following values:

GFT_NOT_GEOS_FILE
The file is not a GEOS file. This constant is guaranteed to be equal to zero.
GFT_EXECUTABLE
The file is executable; in other words, it is some kind of geode.
GFT_VM
The file is a VM file.
GFT_DATA
The file is a GEOS byte file (see below).
GFT_DIRECTORY
The file is a GEOS directory.
GFT_LINK
The file is a symbolic link (not yet implemented).
FEA_FLAGS

This attribute is a word-sized flag field, named GeosFileHeaderFlags . The following flags are implemented:

GFHF_TEMPLATE
The file is a document template.
GFHF_SHARED_SINGLE
The file can be opened for shared-single access.
GFHF_SHARED_MULTIPLE
The file can be opened for shared-multiple access.

Shared-single and shared-multiple access are described in the VM chapter. For more details, see the VM chapter.

FEA_RELEASE

This attribute is a ReleaseNumber structure. Generally, only geodes have release numbers. The structure has the following fields:

RN_major
The file's major release number. An increase in the major release number generally indicates a change which is not downward-compatible.
RN_minor
The file's minor release number. An increase in the minor release number generally indicates that the new version is compatible with previous versions.
RN _change
A field for use by individual manufacturers.
RN _engineering
A field for use by individual manufacturers.
FEA_PROTOCOL

This attribute contains the file's protocol numbers. A ProtocolNumber structure is returned. For a discussion of file protocols, see the Document Objects chapter.

FEA_TOKEN

This attribute is the file's token. It consists of a GeodeToken structure. For more information about tokens, see the Applications and Geodes chapter.

FEA_CREATOR

This attribute is the token of the document's creator. It consists of a GeodeToken structure. For more information about tokens, see the Applications and Geodes chapter.

FEA_USER_NOTES

This attribute is a null-terminated string. It is displayed in the file's "Info" box. Users can edit this string with GeoManager.

FEA_NOTICE

This attribute contains the file's copyright notice.

FEA_CREATION

This attribute is a FileDateAndTime structure. It contains the time when the file was created.

FEA_PASSWORD

This attribute contains the file's encrypted password, if any.

FEA_NAME

This attribute contains the file's virtual name. It is a null-terminated character string.

FEA_GEODE_ATTR

This attribute contains information about the geode. If the file is not a geode, this field's value will be zero. If it is a geode, it will contain a record of GeodeAttrs . This record has the following fields:

GA_PROCESS
This geode has a process thread.
GA_LIBRARY
This geode is a library.
GA_DRIVER
This geode is a driver.
GA_KEEP_FILE_OPEN
The GEOS kernel keeps this file open while GEOS is running.
GA_SYSTEM
This file is part of the kernel.
GA_MULTI_LAUNCHABLE
This geode can be loaded more than once simultaneously.
GA_DRIVER_INITIALIZED
The geode is a driver which has been opened, loaded and initialized.
GA_LIBRARY_INITIALIZED
The geode is a library which has been opened and loaded, and whose entry point has been called.
GA_GEODE_INITIALIZED
The geode has been opened and completely initialized.
GA_USES_COPROC
The geode uses a math coprocessor, if one is available.
GA_REQUIRES_COPROC
The geode can only run if a math coprocessor or emulator is present.
GA_HAS_GENERAL_CONSUMER_MODE
The geode is an application which can be run in GCM mode.
GA_HAS_ENTRY_POINTS_IN_C
This geode is a library or driver which can be called from C code.
FEA_PATH_INFO

This field contains information about the file's path. It is for internal use by the kernel.

FEA_FILE_ID

This field is for internal use by the kernel.

FEA_DESKTOP_INFO

This field is for use by the desktop manager.

FEA_DRIVE_STATUS

This attribute contains the DriveExtendedStatus word for the drive containing the file. The DriveExtendedStatus value is described in Accessing Drives .

The File System: 5.4 Files: File Utilities

FileDelete(), FileRename(), FileCopy(), FileMove(),FileGetDiskHandle()

Most of the time, such actions as copying, deleting, and renaming files are handled by desktop management programs like GeoManager. However, other geodes may need to perform these actions themselves. For example, if you use a temporary file, you may wish to delete it when you're done. The GEOS file system provides routines for these situations. One file utility, FileEnum() , is elaborate enough to be treated in its own section; for details, see FileEnum() .

To delete a file, call FileDelete() . This routine takes one argument, namely the address of a path string. If it can delete the file, it returns zero; otherwise, it returns an error code. Common error conditions include the following:

ERROR_FILE_NOT_FOUND
No such file exists in the specified directory.
ERROR_PATH_NOT_FOUND
An invalid path string was passed.
ERROR_ACCESS_DENIED
You do not have permission to delete that file, or it exists on a read-only volume.
ERROR_FILE_IN_USE
Some geode has that file open.

To change a file's name, call FileRename() . This routine takes two arguments: a pointer to the path string specifying the file, and a pointer to a string specifying the new name for the file. If successful, FileRename() returns zero; otherwise, it returns one of the above error codes.

To make a copy of a file, call FileCopy() . This routine takes four arguments: the handles of the source and destination disks (which may be the same), and the addresses of source and destination path strings. Passing a disk handle of zero indicates the current path's disk. Each string specifies a path relative to the location specified by the corresponding disk handle. If the handle is a disk handle, the path is relative to that disk's root. If the disk handle is a standard path constant, the path string is relative to that standard path. If the disk handle is null, the path is relative to the current working directory. FileCopy() will make a copy of the file in the specified location with the specified name. If a file with that name and location already exists, it will be overwritten. FileCopy() returns zero if successful. Otherwise it returns one of the following error codes:

ERROR_FILE_NOT_FOUND
No such source file exists in the specified directory.
ERROR_PATH_NOT_FOUND
An invalid source or destination path string was passed.
ERROR_ACCESS_DENIED
You do not have permission to delete the existing copy of the destination file, or the destination disk or directory is not writable.
ERROR_FILE_IN_USE
Some geode has the existing destination file open.
ERROR_SHORT_READ_WRITE
There was not enough room on the destination disk. The destination disk will be left unchanged (i.e. a partial copy of the file will not be made).

To move a file from one directory to another, either on the same disk or on different disks, call FileMove() . This routine takes four arguments: the handles of the source and destination disks (which may be the same), and pointers to source and destination path strings. Passing a null disk handle indicates the current working directory. Each string specifies a path relative to the location specified by the corresponding disk handle. If the handle is a disk handle, the path is relative to that disk's root. If the disk handle is a standard path constant, the path string is relative to that standard path. If the disk handle is null, the path is relative to the current working directory. If the copy is successful, FileMove() will return zero; otherwise, it will return one of the above error codes.

If you want to find out the handle of the disk containing an open file, call FileGetDiskHandle() . This routine is passed the file handle; it returns the disk handle. This is useful if the geode has to prepare for a shutdown; it can get the disk handle with FileGetDiskHandle() , then save that handle with DiskSave() (see Saving and Restoring a Disk Handle ). With this information (and the file name), the geode will be able to reopen the file when it restarts.

The File System: 5.5 Files: FileEnum()

FileEnum(), FileEnumLocateAttr(), FileEnumWildcard(), FileEnumAttrs, FileEnumSearchFlags, FileEnumStandardReturnType, FEDosInfo, FENameAndAttr, FileEnumCallbackData

You may sometimes need to perform an action on every file that matches certain criteria. For these situations, the file system provides the routine FileEnum() . FileEnum() can be called in two ways. The usual way is to have FileEnum() provide certain information about every file in a directory (or every file of a given type); when called, FileEnum() will allocate a buffer and fill it with data structures, one for each matching file. Less commonly, you can have FileEnum() call a callback routine for every file which matches certain criteria; this callback routine can take a broader range of actions.

You should not often need to use the FileEnum() routine. The most common time that applications would need this functionality is when they present a list of the files in a directory to the user. The GenFileSelector object provides this functionality, making all necessary calls to FileEnum() transparently to both the user and the geode. Note that the document control objects automatically create and manipulate a file selector when appropriate. For further information, see the FileSelector object chapter.

FileEnum() takes the following arguments:

  • A pointer to a FileEnumParams structure (see below). The data in this structure will tell FileEnum() what to do.
  • A pointer to a MemHandle variable. FileEnum() will allocate a memory block to hold information about the files, and will write the block's handle to this location.
  • A pointer to a word variable. If FileEnum() was unable to fit information about all the files into the block passed, it will write the number of files not handled into this variable.

FileEnum() returns the number of files which were returned in the buffer.

The FileEnumParams structure specifies how FileEnum() should perform. The structure is defined as follows. A summary of each field's role follows; for full details, see the reference entry for FileEnum() .

typedef struct _FileEnumParams {
    FileEnumSearchFlags 				FEP_searchFlags;
    FileExtAttrDesc *				FEP_returnAttrs;
    word 				FEP_returnSize;
    FileExtAttrDesc *				FEP_matchAttrs;
    word 				FEP_bufSize;
    word 				FEP_skipCount;
    word _pascal (*FEP_callback)
        (struct _FileEnumParams *					params,
         FileEnumCallbackData *					fecd,
         word 					frame);
    FileExtAttrDesc *				FEP_callbackAttrs;
    dword 				FEP_cbData1;
    dword 				FEP_cbData2;
    word 				FEP_headerSize;
} FileEnumParams;
FEP_searchFlags
This is a byte-length flag field. The flags are of type FileEnumSearchFlags (described below). These flags specify which files at the current location will be examined by FileEnum() . They also specify such things as whether a callback routine should be used.
FEP_returnAttrs
This field specifies what information is wanted about the files. It is a pointer to an array of FileExtAttrDesc structures (see FEA_MULTIPLE ). The attributes will be written to the return buffer; each file will have an entry, containing all the attributes requested for that file. You can also request certain return values by setting FEP_returnAttrs to equal a member of the FileEnumStandardReturnType (again, by casting the FileEnumStandardReturnType value to type void * ). The FileEnumStandardReturnType enumerated type is described later in this section.
FEP_returnSize
This is the size of each entry in the returned buffer. If a standard return type or an array of FileExtAttrDesc structures was passed, each entry in the returned buffer will contain all the extended attribute information requested for that file.
FEP_matchAttrs
This field specifies which files should be enumerated by FileEnum() . It is a pointer to an array of FileExtAttrDesc structures (see FEA_MULTIPLE ). These structures specify values for certain extended attributes. Only those files whose extended attributes match these will be enumerated. If you do not want to filter out any files in the working directory, or if you will use the callback routine to filter the files, pass a null pointer in this field.
FEP_bufsize
This field specifies the maximum number of entries to be returned in the buffer. If you do not want to set a limit, pass the constant FEP_BUFSIZE_UNLIMITED. The buffer will be grown as necessary.
FEP_skipCount
This field contains the number of matching files to be ignored before the first one is processed. It is often used in conjunction with FEP_bufSize to examine many files a few at a time. Each time FileEnum() is called, you can enumerate a certain number of files; by adjusting the skip count each time, you can start each enumeration where the previous one ended. In this way you could walk through all the matching files in the directory. Note that if the FileEnumSearchFlags bit FESF_REAL_SKIP is set (in FEP _searchFlags ), the first files in the directory will be skipped before they are tested to see if they match. This is faster, since the match condition won't have to be checked for the first files in the directory.
FEP_callback
This field holds a pointer to a Boolean callback routine. The callback routine can check to see if the file matches some other arbitrary criteria. The callback routine is called for any files which match all the above criteria. It can then decide whether to enumerate the file however it wishes. The callback routine should be declared _pascal . If the file should be accepted by FileEnum() , the callback should return true ; otherwise it should return false . You can also instruct FileEnum() to use one of the standard callback routines by passing a member of the FileEnumStandardCallback enumerated type. In this case, FEP_callbackAttrs is ignored; FileEnum() will automatically pass the appropriate information to the callback routine. (Note that if the FESF_CALLBACK bit of the FEP_searchFlags field is not set, the FEP_callback field is ignored.) The callback routine may not call any routines which are in movable memory at the time FileEnum() is called, except for routines which are in the same resource as the callback routine.
FEP_callbackAttrs
This field specifies what additional attributes should be read for the callback routine. This field is a pointer to an array of FileExtAttrDesc structures (see FEA_MULTIPLE ). The array will be filled in with the appropriate information for each file before the callback routine is called. Note that if the FESF_CALLBACK bit of the FEP_searchFlags is not set, the FEP_callbackAttrs is ignored. If you do not need any attributes passed to the callback routine, set this field to be a null pointer.
FEP_cbData1 , FEP_cbData2
These are dword-length fields. Their contents are ignored by FileEnum() ; they are used to pass information to the callback routine. If you do not call a standard callback routine, you may use these fields any way you wish.
FEP_headerSize
If the flag FESF_LEAVE_HEADER is set, FileEnum() will leave an empty header space at the beginning of the return buffer. The size of the header is specified by this field. If the flag FESF_LEAVE_HEADER is clear, this field is ignored.

The first field of the FileEnumParams structure, FEP_searchFlags , is a word-length record containing FileEnumSearchFlags . The following flags are available:

FESF_DIRS
Directories should be examined by FileEnum() .
FESF_NON_GEOS
Non-GEOS files should be examined by FileEnum() .
FESF_GEOS_EXECS
GEOS executable files should be examined by FileEnum() .
FESF_GEOS_NON_EXECS
GEOS non-executable files (e.g., VM files) should be examined by FileEnum() .
FESF_REAL_SKIP
If a skip count of n is specified, the first n files will be skipped regardless of whether they matched the attributes passed. In this case, FileEnum() will return the number of files passed through in order to get enough files to fill the buffer; the return value can thus be the real-skip count for the next pass.
FESF_CALLBACK
FileEnum() should call a callback routine to determine whether a file should be accepted.
FESF_LOCK_CB_DATA
This flag indicates that the FileEnumParams fields FEP_callback1 and FEP_callback2 are far pointers to movable memory that must be locked before FileEnum() is called.
FESF_LEAVE_HEADER
If set, FileEnum() should leave an empty header space at the start of the return buffer. The size of this buffer is specified by the FEP_headerSize field.

You can specify precisely which data you want about each file and in which arrangement it should be returned. However, for most purposes, you can get enough information by requesting one of the standard return types. If you pass a member of FileEnumStandardReturnType in FEP_returnAttrs , FileEnum() will write an array of structures to the return buffer; each file will have one such structure. The structures are shown below in Standard FileEnum() Return Structures . FileEnumStandardReturnType has the following values:

FESRT_COUNT_ONLY
FileEnum() will not allocate any memory or return data about files; instead, it will simply return the number of files which match the specified criteria.
FESRT_DOS_INFO
FileEnum() will return an array of FEDosInfo structures. These structures contain basic information about the file: its virtual name, size, modification date, DOS attributes, and path information (as a DirPathInfo record).
FESRT_NAME
FileEnum() will return an array of FileLongName strings, each one of which is FILE_LONGNAME_BUFFER_SIZE characters long; every one of these will contain a file's virtual name followed by a null terminator.
FESRT_NAME_AND_ATTR
FileEnum() will return an array of FENameAndAttr structures, each one of which contains a file's DOS attributes and virtual name.

The FEDosInfo structure includes a word-sized record which describes the file's position relative to the standard paths. It contains the following fields:

DPI_EXISTS_LOCALLY
This bit is set if the file exists in a directory under the primary tree.
DPI_ENTRY_NUMBER_IN_PATH
This is the mask for a seven-bit field whose offset is DPI_ENTRY_NUMBER_IN_PATH_OFFSET.
DPI_STD_PATH
This is the mask for an eight-bit field whose offset is DPI_STD_PATH_OFFSET. If the file is in a standard path, this field will contain a StandardPath constant for a standard path containing the file. This need not be the "closest" standard path; for example, if the file is in the "World" directory, this constant might nevertheless be SP_TOP.

Code Display 17-2 Standard FileEnum() Return Structures

/* These structures are the standard FileEnum() return types. You can also
 * instruct FileEnum() to return any arbitrary extended attribute information.
 */
typedef struct {			/* These are returned if you specify FESRT_DOS_INFO */
    FileAttrs 		FEDI_attributes;			/* File's DOS attributes;
                     * see  Getting and Setting Information about a Byte File */
    FileDateAndTime 		FEDI_modTimeDate			/* Last modification time; see  FEA_MODIFICATION */
    dword 		FEDI_fileSize;			/* Size of file (in bytes) */
    FileLongName		FEDI_name;			/* File's virtual name; null-terminated */
    DirPathInfo 		FEDI_pathInfo;			/* PathInfo structure, described above */
} FEDosInfo;
typedef struct{ 			/* These are returned if you specify FESRT_NAME_AND_ATTR */
    FileAttrs 		FENAA_attributes;			/* File's DOS attributes;
                     * see  Getting and Setting Information about a Byte File */
    FileLongName 		FENAA_name;			/* File's virtual name; null-terminated */
} FENameAndAttr;

There is currently one standard callback routine provided. This routine, FileEnumWildcard() , rejects all files whose names don't match a wildcard string. To call this routine, set FEP_callback as follows:

enumParams.FEP_callback = (void *) FESC_WILDCARD;

FEP_cbData1 should be a pointer to a null-terminated wildcard string. In this string, "*" denotes a sequence of zero or more of any character, and "?" denotes any one character; all other characters represent themselves. If a file's virtual name matches the wildcard string, information about it will be returned by FileEnum() . If the lowest byte of FEP_cbData2 is non-zero, the comparison will be case-insensitive; otherwise, it will be case-sensitive. (The other bytes of FEP_cbData2 are ignored.) This is different from standard DOS wildcarding; for example, the name "doc*." matches any file that begins with the letters "doc" and ends with a period, whereas a DOS wildcard string "doc*." would match only those files whose name start with "doc" and which have no extension.

If you're writing your own callback routine, you may wish to use the FileEnumLocateAttr() utility routine. This routine searches a FileExtAttrDesc array (as passed to FileEnum() callbacks), and will search the array for a file matching a particular attribute.

The File System: 5.6 Files: Bytewise File Operations

There are several routines designed for working with files as a string of bytes. These routines may be used to work with DOS files or with GEOS byte files. You can open any file (including an executable file or a VM file) for byte-level access. This may be useful for such things as file-compression routines; however, be aware that if you make any changes to such files, you could invalidate them. For this reason, if you open a VM or executable file for byte-level access, you should open it for read-only use.

Opening and Closing Files

FileOpen(), FileCreate(), FileCreateTempFile(), FileClose(), FileAccessFlags

The GEOS file system provides several routines for opening files for byte-level access. If you are working with GEOS Virtual Memory files, you should use the appropriate VM routines to open and close the files (see the VM chapter). You should use the byte-level routines only if you are working with DOS files or with GEOS byte files. You may occasionally want to read a VM file or an executable file as a string of bytes. In this rare case, you must use the routines in this section. Note, however, that you should not change the VM file with these routines; it is safe only to open it for read-only access.

To open a file, call FileOpen() . This routine takes two arguments: a set of FileAccessFlags and a pointer to a null-terminated string. The string should specify the name of the file (either the virtual name or the native name may be used). It may simply be a file name, or it may be a relative or absolute path. The FileAccessFlags record specifies two things: what kind of access the caller wants, and what type of access is permitted to other geodes. A set of FileAccessFlags is thus a bit-wise OR of two different values. The first specifies what kind of access the calling geode wants and has the following values:

FILE_ACCESS_R
The geode will only be reading from the file.
FILE_ACCESS_W
The geode will write to the file but will not read from it.
FILE_ACCESS_RW
The geode will read from and write to the file.

The second part specifies what kind of access other geodes may have. Note that if you try to deny a permission which has already been given to another geode (e.g. you open a file with FILE_DENY_W when another geode has the file open for write-access), the call will fail. The following permissions can be used:

FILE_DENY_RW
No geode may open the file for any kind of access, whether read, write, or read/write.
FILE_DENY_R
No geode may open the file for read or read/write access.
FILE_DENY_W
No geode may open the file for write or read/write access.
FILE_DENY_NONE
Other geodes may open the file for any kind of access.

Two flags, one from each of these sets of values, are combined to make up a proper FileAccessFlags value. For example, to open the file for read-only access while prohibiting other geodes from writing to the file, you would pass the flags FILE_ACCESS_R and FILE_DENY_W as follows:

myHandle = FileOpen("MyFile",
        (FILE_ACCESS_R | FILE_DENY_W));

If successful, FileOpen() returns the file's handle. If it is unsuccessful, it returns a null handle and sets the thread's error value. The following error values are commonly returned:

ERROR_FILE_NOT_FOUND
No file with the specified name could be found in the appropriate directory.
ERROR_PATH_NOT_FOUND
A relative or absolute path had been passed, and the path included a directory which did not exist.
ERROR_TOO_MANY_OPEN_FILES
There is a limit to how many files may be open at once. If this limit is reached, FileOpen() will fail until a file is closed.
ERROR_ACCESS_DENIED
Either the caller requested access which could not be granted (e.g. it requested write access when another geode had already opened the file with FILE_DENY_W), or the caller tried to deny access when that access had already been granted to another geode (e.g. it tried to open the file with FILE_DENY_W when another geode already had it open for write-access).
ERROR_WRITE_PROTECTED
The caller requested write or read-write access to a file in a write-protected volume.

Note that if you use the document control objects, they automatically make all appropriate calls to FileOpen() when the user requests it; you will automatically be passed the file's handle.

FileOpen() can only be called if the file already exists. In order to create a byte file, you must call FileCreate() . FileCreate() takes four arguments: a set of FileCreateFlags , a set of FileAccessFlags , a set of FileAttrs , and a pointer to a string containing a name for the file. As with FileOpen() , the name may be a name alone or a relative or absolute path. The FileCreateFlags specifies whether the file should be created if it already exists. The following flags are available:

FILE_CREATE_TRUNCATE
If a file with the given name exists, it should be opened and truncated; that is, all data should be deleted.
FILE_CREATE_NO_TRUNCATE
If the file exists, it should be opened without being truncated.
FILE_CREATE_ONLY
If the file exists, the routine should fail and set the thread's error value to ERROR_FILE_EXISTS.
FCF_NATIVE
This flag is combined with one of the above flags if the file should be created in the device's native format; e.g. if it should be a DOS file instead of a GEOS file. The name passed must be an acceptable native file name. If a GEOS file with the specified name already exists, FileCreate() will fail with error condition ERROR_FILE_FORMAT_MISMATCH.

The first three flags (FILE_CREATE_...) are mutually exclusive; exactly one of them must be passed to FileCreate() . That flag may or may not be combined with FCF_NATIVE.

The FileAccessFlags are the same as described in FileOpen() . Note, however, that you must request either write access or read/write access when you use FileCreate() .

Every file has a set of attributes. These record certain information about the file. If you create a file, you will need to specify values for these attributes. The attributes are described above in the section on FEA_FILE_ATTR .

If FileCreate() is successful, it will open the file and return its handle. If it fails, it will return a null handle and set the thread's error value. It may return any of the FileOpen() errors. It may also return the following errors:

ERROR_FILE_EXISTS
Returned if FileCreate() was called with FILE_CREATE_ONLY and a file with the specified name already exists.
ERROR_FORMAT_MISMATCH
Returned if FileCreate() was called with FILE_CREATE_TRUNCATE or FILE_CREATE_NO_TRUNCATE and a file exists in a different format than desired; i.e. you passed FCF_NATIVE and the file already exists in the GEOS format, or vice versa.

It is often useful to create temporary files which are not seen by the user. In these cases, you generally don't care about the file's name since you will most likely be deleting the file on exit. For these situations GEOS provides the routine FileCreateTempFile() . FileCreateTempFile() is passed a directory; it chooses a unique name for the file. This routine takes two arguments:

  • A set of FileAttrs , as described above.
  • A pointer to a null-terminated string specifying the path for the temporary file. This path may be relative or absolute. To create the temporary file in the current directory, pass the string "." This string should contain fourteen extra null bytes at the end, as FileCreateTempFile() will write the name of the temporary file at the end of the string. Temporary files are typically created in SP_WASTE_BASKET.

If successful, FileCreateTempFile() will open the temporary file and return its handle. It will also write the file's name to the end of the string passed. You will need to know the name to delete the file. The name is also useful if GEOS shuts down while a temporary file is open; the geode will need to know the temporary file's name in order to reopen it.

When you are done with a file, you should close it by calling FileClose() . This releases any restrictions you may have placed on the file and allows the file to be moved or deleted. It is passed two arguments: the file handle and a Boolean value which should be set to true (i.e. non-zero) if the geode cannot handle error messages; it will cause FileClose() to fatal-error if it cannot successfully close the file. (This should only be used during development; the flag should never be passed in a finished program.) The routine returns zero if successful; otherwise, it returns a FileError value.

Reading From and Writing To Files

FileRead(), FileWrite(), FilePos(), FileCommit()

There are a few specific operations you are allowed to perform on data in a byte-file. You can copy data from the file into memory; you can copy data from memory into the file, overwriting the file's contents; you can write data to the end of a file; and you can cut data from the end of the file. If you want to perform more elaborate manipulations on a byte-file, you may wish to create a temporary VM file and copy the data there (see the VM chapter).

Every file handle has a file position associated with it. All read and write operations begin at that position; they may also change the position. The first byte in a file is considered to be at position zero. If the file is a GEOS byte file, position zero is immediately after the GEOS header; thus, the header cannot be accessed or altered via the read and write operations.

To read data from a file, call FileRead() . This routine takes four arguments. The first is the file's handle. The second is the address of a buffer. FileRead() will copy the requested number of bytes from the file to the buffer. The third is the number of bytes to read. The fourth is a Boolean indicating whether the caller can handle errors. (This is true if the geode cannot handle error messages; it will cause FileRead() to fatal-error if it cannot successfully read the data. This should only be used during development; the flag should never be passed in a finished program.) FileRead() returns the number of bytes actually read. This may be less than the number requested, if the end of file is reached; in this case, the thread's error value will be set to ERROR_SHORT_READ_WRITE. If FileRead() was unable to gain access to the file, it will return -1 and set the thread's error value to ERROR_ACCESS_DENIED. In any event, the file position will be incremented by the number of bytes read; thus, it will point to the first byte after the data read.

To write data to a file, call FileWrite() . This routine takes four arguments. The first is the file's handle. The second is the address of a buffer in memory. The third is the number of bytes to write. The fourth is a Boolean indicating whether the caller can handle errors. FileWrite() will copy the specified number of bytes from the buffer to the file, starting at the current position and expanding the file as necessary. It will also increment the current position by the number of bytes written. If the current position is not at the end of the file, FileWrite() will overwrite the file's existing data. FileWrite() returns the number of bytes written. This may be less than the number requested, if the disk ran out of space; in this case, the thread's error value will be set to ERROR_SHORT_READ_WRITE. If FileWrite() could not get access to the file (as, for example, if the geode had read-only access to the file), it will return -1 and set the thread's error value to ERROR_ACCESS_DENIED.

If a file is on a removable disk, the kernel will make sure that the disk is in the appropriate drive before reading from or writing to it. If the disk is not in the drive, the kernel will prompt the user to insert it. The user will have the option of aborting the operation; this will result in the file-access routine failing with error condition ERROR_DISK_UNAVAILABLE.

When you write changes to a file, either the GEOS file system or the underlying DOS may choose to cache those changes to save time. All cached changes will be written to the disk when the file is closed. However, you can force the cached changes to be written immediately by calling FileCommit() . This routine takes two arguments. The first is the file's handle. The second is a Boolean indicating whether the caller can handle errors. The routine returns zero if the operation was successful; otherwise it returns an error code.

To change the current file position, call FilePos() . This routine takes three arguments. The first is the file handle. The second is a member of the FilePosMode enumerated type; this value indicates how the new position is specified. The third argument is a number of bytes; it specifies how far the file position will be moved. FilePosMode has the following possible values:

FILE_POS_START
The file position is set to a specified number of bytes after the start of the file. Passing this mode with an offset of zero will set the file position to the start of the file (i.e. immediately after the header information).
FILE_POS_RELATIVE
The file position is incremented or decremented by a specified number of bytes.
FILE_POS_END
The file position is set to a specified number of bytes before the end of the file. Passing this mode with an offset of zero will set the file position to the end of the file.

FilePos() returns a 32-bit integer. This integer specifies the file position after the move (relative to the start of the file). To find out the current file position without changing it, call FilePos() with mode FILE_POS_RELATIVE and offset zero.

Getting and Setting Information about a Byte File

FileGetDateAndTime(), FileSetDateAndTime(), FileGetAttributes(), FileSetAttributes()

GEOS provides several routines to get information about files. To get information about a GEOS file, you would ordinarily use one of the extended attributes routines (see GEOS Extended Attributes ). These routines are ordinarily used for non-GEOS files. Nevertheless, all of the following routines can be used on GEOS files.

FileGetDateAndTime() and FileSetDateAndTime() are used to get and set the file's modification time. To access a GEOS file's modification time, you would ordinarily call an extended attribute routine, passing FEA_MODIFICATION. However, special-purpose routines are provided specifically for changing a file's modification time. Note that these routines may be used for GEOS or non-GEOS files. Similarly, you can change the FEA_MODIFICATION attribute even for non-GEOS files. To find out the modification time, call FileGetDateAndTime() . This routine is passed the file's handle and returns a FileDateAndTime value (as described above on FEA_MODIFICATION ). To change the modification time, call FileSetDateAndTime() . This routine is passed the file's handle and a FileDateAndTime value. If successful, it returns zero; otherwise, it returns an error code. You must have write permission to change the modification time; otherwise, FileSetDateAndTime() will fail with condition ERROR_ACCESS_DENIED. The TimerGetFileDateTime() routine returns the current date and time in a FileDateAndTime structure.

To find out a DOS file's attributes, call FileGetAttributes() . This routine is passed a file's path. It returns the file's FileAttrs record (as described on FEA_FILE_ATTR ). To change the file's attributes, call FileSetAttributes() . This routine takes two arguments: the address of a null-terminated path string and a FileAttrs record. It returns zero if it was successful; otherwise, it returns an error condition. Note that a file's attributes cannot be changed if the file is open.

Data-Access Synchronization

FileLockRecord(), FileUnlockRecord()

GEOS provides routines to help threads synchronize file access. This functionality is very elaborate for VM files. For byte files it is less so. Several threads can synchronize their access to a single handle by using HandleP() and HandleV() , described in the Memory Management chapter. If they want to use the file at the same time, they should use FileLockRecord() and FileUnlockRecord() .

FileLockRecord() takes three arguments: the file handle, a dword specifying the start of the region to be locked, and a dword specifying the length (in bytes) of the region to be locked. If there are no locks on any part of that region, FileLockRecord() returns zero; otherwise, it returns the error code ERROR_ALREADY_LOCKED. Note that there is nothing to stop another thread or geode from reading or writing to that region. The lock simply prevents anyone from locking that region with FileLockRecord() . The file's users have to remember to lock any part of the file before accessing it.

To release a lock on a part of a file, call FileUnlockRecord() . This routine takes the same arguments as FileLockRecord() .

This document is a single-page version of a a multi-page document, suitable for easy printing.


The FileSelector Object

The GenFileSelector provides the user interface and functionality to allow a user to traverse directories and volumes (disks) and view the files contained therein. It can be customized to limit the displayed file names or to expand the listings to include special files.

This object is used by the GenDocumentControl object to provide most file dialog boxes. If your application uses the Document Control objects, you will probably not need to add a GenFileSelector.

1 File Selector Overview
2 File Selector Basics
2.1 Setting Up the File Selector
2.2 Supporting the File Selector
2.3 Messages to Handle
2.4 Some Common Customizations
3 File Selector Instance Data
3.1 The GFSI_attrs Field
3.2 The GFSI_fileCriteria Field
3.3 Matching a File's Token
3.4 Matching a File's Creator App
3.5 Matching a File's Geode Attributes
3.6 Masking File Names
3.7 Matching a File's File Attributes
3.8 Searching Via Callback Routine
3.9 Resetting a Filter
4 File Selector Use
4.1 When a User Selects a File
4.2 The Current Selection
4.3 Rescanning Directories
4.4 Setting Scalable UI Data

The FileSelector Object: 1 File Selector Overview

When you are not using the Document Control objects but need to allow the user to traverse directories and disks in order to locate a given file, you should use a GenFileSelector. The File Selector is designed to give tremendous flexibility for many different types of file searches, providing both directory scanning and user interface. An OSF/Motif File Selector is shown below.

The File Selector allows users to browse all the directories and disks readable by DOS, including readable network drives. This browsing is completely internal to the File Selector; until the user selects a file from the list, your application does not need to know what the File Selector is doing.

You can customize the searches the File Selector makes in several different ways:

  • Setting paths, volumes, and selections
    You may direct the File Selector to show any given directory on any given volume, and you may also set the selected file displayed. Additionally, you can navigate directories with messages sent to the File Selector. See The Current Selection .
  • Limiting a directory scan to certain files
    The File Selector can limit its directory scans to show only the files you request. You may limit the scan based on file types, file attributes, or filename extensions. See File Selector Instance Data .
  • Searching for associated files
    You can limit the File Selector's searches to only those files that match a given creator or token. That is, the File Selector will display only files created by a particular geode. See Matching a File's Token .
  • Customizing the searches
    If the available search options don't fit your needs, you can create a customized search. If this type of search is employed, the File Selector will send a special message to itself--to take advantage of this, you can easily subclass the GenFileSelector and write a custom handler for this special message. See Searching Via Callback Routine .

All of the directory scanning and path navigation is internal to the GenFileSelector. When a selection is made (a user single- or double-clicks on an entry, for example), the File Selector will send out a message indicating the selection. You can, however, force the File Selector to change to any directory or search criteria at any time.

The FileSelector Object: 2 File Selector Basics

The following sections describe how to set up and use the File Selector in a simple configuration and how to modify the configuration for common uses.

Most File Selectors will be put in dialog boxes and will be accompanied by at the least a Reply Bar. (For more information on dialog boxes and reply bars, see the Menus and Dialog Boxes chapter.) Typically, the dialog box will be brought up by a menu item such as Open... or Insert From Text File... .

The figure above shows a sample Insert From Text File dialog box. (Note that you would probably use the Impex object for this functionality; this is provided for example.) This box has three basic elements: The GenGlyphDisplay (the title at the top), the File Selector (the scrolling list of files and the disk and folder icons), and the Reply Bar. In nearly all cases, these three elements will be included with every File Selector in its dialog box.

Setting Up the File Selector

Supporting the File Selector

Messages to Handle

Some Common Customizations

The FileSelector Object: 2.1 File Selector Basics: Setting Up the File Selector

For a typical configuration, you will have to set up a GenInteraction as a dialog box. (For information dialog boxes, see the Menus and Dialog Boxes chapter.) The File Selector and other elements of the dialog should be placed as children of the Interaction.

A Basic File Selector shows the Goc code required to display and use a File Selector in this basic form. You can use this code to create a similar dialog box with the proper elements or modify it to gain the functionality you want. Specific permutations of this configuration will be described in Some Common Customizations ; however, the remainder of this section describes the basics of the File Selector.

Code Display 14-1 A Basic File Selector

/* Extra code for menus and other UI objects is left out. Only what is
 * required for these objects is included. */
/* This dialog box provides the organization needed to contain the File
 * Selector, the name of the box, and the Reply Bar. */
@object GenInteractionClass MyDialogBox = {
    GI_visMoniker = `I', "Insert From Text File";
    GI_comp = MyGlyph, MyFileSel, MyInsertTrigger;
    GII_visibility = (GIV_DIALOG);
    GII_type = (GIT_COMMAND);
};
/* The Glyph Display provides the label in the dialog box. Alternatively, the
 * moniker of the GenInteraction could be used for this functionality. */
@object GenGlyphClass MyGlyph = {
    GI_visMoniker = "Insert From Text File";
}
/* The File Selector is as basic as possible. */
@object GenFileSelectorClass MyFileSel = {
    GFSI_destination = process;					/* The object receiving notification. */
    GFSI_notificationMsg = MSG_MY_APP_FILE_SELECTED;							/* The message sent
                             * upon selection. */
};
@object GenTriggerClass MyInsertTrigger = {
    GI_visMoniker = "Insert";
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_APP_INSERT_TRIGGER_SELECTED;
    HINT_SEEK_REPLY_BAR;
};
/* Note that in most cases, this trigger will not be needed. Generally, the File
 * Selector will include an "OK" or "Use This File" trigger in its reply bar that
 * executes an IC_OK function. For your trigger above to demonstrate that
 * functionality, remove the GTI_... fields and add the line
 * ATTR_GEN_TRIGGER_INTERACTION_COMMAND = (IC_OK);
 */

The FileSelector Object: 2.2 File Selector Basics: Supporting the File Selector

To use the File Selector as shown in A Basic File Selector , your application must have some object (which may be the Process object) that can handle all the cases that may arise from a user's interaction with the dialog box. All of these cases are listed below:

  • The user clicks once on an entry in the File Selector's list.
    When this happens, the File Selector highlights the selected entry and sends its notification message to its destination object. With the message will come a record of flags indicating the action was a single click.
  • The user double-clicks on an entry in the File Selector's list.
    This action causes the File Selector to send its notification message to its destination object. The default action for the dialog box containing the File Selector will also occur (e.g. the default action is what happens when the "Insert" trigger is pressed). If the current selection is a directory, the application does not need to do anything; the File Selector will open the directory automatically. If the current selection is a file, the application must deal with the file properly and then close the dialog box containing the File Selector.
  • The user clicks on the "Insert" trigger.
    This action causes the trigger to send its notification message to its destination object. In most cases, the trigger with be an IC_OK trigger that will cause the File Selector to act as if a double-click had occurred. If the current selected entry in the list is a directory, the application should instruct the File Selector to open the directory and display its file list. If the current selection is a file, the application is responsible for dealing properly with the file; additionally, the application should then close the dialog box containing the File Selector.

The above list describes the basics of File Selector support. Different File Selectors (such as SaveAs dialogs that allow the user to type in a file name) can easily be defined by adding other gadgetry to the controller. All other actions possible are internal to the file selector (e.g. the "Go To Document" button is a feature of the File Selector which is invisible to the application).

The FileSelector Object: 2.3 File Selector Basics: Messages to Handle

The list above shows two different messages that your File Selector's basic configuration: The first results from the user clicking on an entry and comes from the File Selector object, and the second is sent by the "Insert" trigger when the user clicks on it. The following descriptions show what you must do when handling these messages. (Note that in many cases only one message, the File Selector's action message, will be used; this occurs when the "Insert" trigger is actually an IC_OK or similar response trigger.)

Handling a File Selector Selection shows a sample handler for the message sent by the File Selector. Handling a File Selector "OK" Button shows a sample handler for the message sent by the Reply Bar's Insert trigger. Both these handlers are written to go with the code in A Basic File Selector , and both handle the simplest case. Where application-specific code is required, comments have been inserted.

You should not, in general, have to handle any more than the File Selector's notification message. You may add your own gadgetry to add other functionality, however.

The File Selector's notification message is the message you set in GFSI_notificationMsg . This message should have the same definition as the prototype GEN_FILE_SELECTOR_NOTIFICATION_MSG , which is defined below. You can either define it with the same parameters, or you can define it exactly on the prototype. (The latter is recommended.) The prototype carries with the notification a word of GenFileSelectorEntryFlags ; this record contains the following flag fields:

GFSEF_TYPE
This is a two-bit field (at offset GFSEF_TYPE_OFFSET) that describes the type of the selection. This may be one of GFSET_FILE (selection is a file), GFSET_SUBDIR (selection is a subdirectory), or GFSET_VOLUME (selection is a volume or disk). These are values of GenFileSelectorEntryType .
GFSEF_OPEN
Set if the selection action was an "open" (i.e. double-click).
GFSEF_NO_ENTRIES
Set if no entries are in the list.
GFSEF_ERROR
Set if there was an error opening the selected entry on a double-click or a MSG_GEN_FILE_SELECTOR_OPEN_ENTRY .
GFSEF_TEMPLATE
Set if the file is a template (i.e. has GFHF_TEMPLATE set).
GFSEF_SHARED_MULTIPLE
Set if the file is shared with multiple writers (i.e. has GFHF_SHARED_MULTIPLE).
GFSEF_SHARED_SINGLE
Set if the file is shared and has a single writer (i.e. has GFHF_SHARED_SINGLE).
GFSEF_READ_ONLY
Set if the file is read-only (i.e. has FA_RDONLY).
GFSEF_PARENT_DIR
Set if the current selection is the parent directory (i.e. the first entry in the file selector's list).
GEN_FILE_SELECTOR_NOTIFICATION_MSG
void	GEN_FILE_SELECTOR_NOTIFICATION_MSG(
        word				entryNum,
        GenFileSelectorEntryFlags				entryFlags);

This prototype should be used for all notification messages sent out by the File Selector; that is, any message you set in GFSI_notificationMsg should be based on this prototype.

Source: The GenFileSelector object.

Destination: The object specified in GFSI_destination .

Parameters: entryNum The entry number selected in the list.

entryFlags
A GenFileSelectorEntryFlags record describing the selection. To get the entry type from the flags record, use the macro GFS_GET_ENTRY_TYPE, described after this reference entry.

Return: Nothing.

Structures: GenFileSelectorEntryFlags is a record with the following flags:

typedef WordFlags GenFileSelectorEntryFlags;
#define GFSEF_TYPE						0xc000
#define GFSEF_OPEN						0x2000
#define GFSEF_NO_ENTRIES 						0x1000
#define GFSEF_ERROR 						0x0800
#define GFSEF_TEMPLATE 						0x0400
#define GFSEF_SHARED_MULTIPLE 						0x0200
#define GFSEF_SHARED_SINGLE 						0x0100
#define GFSEF_READ_ONLY 						0x0080
#define GFSEF_PARENT_DIR 						0x0040
#define GFSEF_TYPE_OFFSET 					14

The GFSEF_TYPE field is two bits defined by one of the following GenFileSelectorEntryType constants. These two bits define the type of entry selected.

typedef ByteEnum GenFileSelectorEntryType;
#define GFSET_FILE				0
#define GFSET_SUBDIR				1
#define GFSET_VOLUME				2

Interception: The notification message must be intercepted and handled for the selection of a file to have any effect in your application. There is no default behavior, except in the document control objects (which create their own File Selectors).

GFS_GET_ENTRY_TYPE
byte	GFS_GET_ENTRY_TYPE(flags)
        word	flags;

This macro extracts the high two bits from a GenFileSelectorEntryFlags record delivered as a parameter of a File Selector's notification message. Compare the value to GFSET_FILE, GFSET_SUBDIR, and GFSET_VOLUME.

Code Display 14-2 Handling a File Selector Selection

/*
 * C handler for MSG_MY_APP_FILE_SELECTED. The message is sent by the File Selector
 * object (MyFileSel) to the application's Process object (MyProcessClass) when the
 * user clicks on an entry in the displayed file list. This code display is per the
 * setup in  A Basic File Selector.
 */
/*
 * Format of the message:
 * void MSG_MY_APP_FILE_SELECTED(word entryNum,
 *					GenFileSelectorEntryFlags entryFlags)
 */
@method	MyProcessClass, MSG_MY_APP_FILE_SELECTED {
    /* First check if this is an OPEN operation (double-click). If so,
     * then test whether the selection is a file. If so, simulate the "Insert"
     * trigger by sending the trigger's message to ourselves. If it is not a file,
     * or if the operation is not OPEN, we need do nothing. */
    if (GFS_GET_ENTRY_FLAGS(entryFlags) & GFSEF_OPEN) {    /* Is the operation
                              * a double-click? */
    if (GFS_GET_ENTRY_TYPE(entryFlags) & GFSET_FILE) {
                        /* Is the selection a file? */
        /* Execute application-specific code here. */
    }
    }
}
/* Note that we do not necessarily have to handle double-click operations in this
 * way. Since a double-click automatically activates the GenInteraction's default
 * element (typically an "Ok" trigger), we can simply handle the press of the
 * "Ok" trigger as shown in  Handling a File Selector "OK" Button. */

Code Display 14-3 Handling a File Selector "OK" Button

/* C handler for MSG_MY_APP_INSERT_TRIGGER_SELECTED, the message sent by the Insert
 * trigger to the application's Process object when the user clicks on it.
 * Format of this message: void MSG_MY_APP_INSERT_TRIGGER_SELECTED().
 * This code display is per the setup shown in  A Basic File Selector. */
@method	MyProcessClass, MSG_MY_APP_INSERT_TRIGGER_SELECTED {
    /* Declare a local dword of flags and entry number. */
    dword selFlags;
    /* First, retrieve the selection number and flags from the File Selector.
     * For now, we can ignore the selection name and path. To retrieve this
     * information, send the message MSG_GEN_FILE_SELECTOR_GET_SELECTION to the
     * File Selector. This message will return a dword (selFlags) that contains the
     * selection's entry number and a GenFileSelectorEntryFlags record. (To ignore
     * the selection name, pass a null pointer with the message.) */
    selFlags = @call MyFileSel::MSG_GEN_FILE_SELECTOR_GET_SELECTION(NULL);
    /* Next, determine whether the selection is a file. To do this, check the
     * returned GenFileSelectorEntryFlags record against GFSET_FILE. If the result
     * is true, the selection is a file. If false, it is a directory or volume. If
     * the selection is a file, we will operate on it appropriately (this is
     * specific to each application). If not, we will direct the File Selector to
     * open the entry. Note that we can assume an OPEN operation is in progress
     * because the only two ways to get to this point are through a double-click
     * on a selection and through a click on the "Insert" trigger.
     */
    /* Note the use of the two macros GFS_GET_ENTRY_FLAGS and GFS_GET_ENTRY_NUMBER.
     * The first extracts the GenFileSelectorEntryFlags record from the dword
     * selFlags, and the second extracts the entry number from selFlags. */
    if (GFS_GET_ENTRY_FLAGS(selFlags) == GFSET_FILE) {	/* the selection is a file */
    /* Execute application-specific code here. */
    } else {				/* the selection is a volume or directory */
    /* Now direct the File Selector to open the entry. Use the message
     * MSG_GEN_FILE_SELECTOR_OPEN_ENTRY, which returns a flag; if the flag is
     * set, then an error occurred. If the flag is clear, the operation
     * succeeded. */
    if (@call MyFileSel::MSG_GEN_FILE_SELECTOR_OPEN_ENTRY(
                    GFS_GET_ENTRY_NUMBER(selFlags))) {
        /* Provide a proper error message or beep sound. */
    }
}

The FileSelector Object: 2.4 File Selector Basics: Some Common Customizations

Although the basics are all covered under the previous sections and you need nothing more to add a File Selector to your application, you will probably want to customize it somewhat. Typical simple customizations include limiting the types of files displayed, restricting the search to a single directory or volume, limiting the display to directories only (no files), and limiting the search to only those files with a given filename extension.

Each of these is shown in the displays following this section. Notice that the only changes you need to make to implement any of these customizations is to the File Selector's Goc code. You will not need to handle any other messages or add other code to your application. These customizations are shown in Display Only Directories through Show Only Files with a Given Filename Extension and show how to do the following (you may apply any combination):

  • Show directories only.
    The File Selector will display no files, only directories.
  • Show all files in a single directory.
    The File Selector will show all files but will restrict directory navigation; the user can only select files in the current directory.
  • Show hidden files as well as normal files.
  • Show all files created by a given geode.
    The File Selector will restrict displayed files to only those that were created by the geode with the given tokenChars and tokenID .
  • Show all files with a given filename extension.
    The File Selector will restrict the files displayed to only those with the given filename extension.

These are not the only customizations you can make. Although they will satisfy the needs of the large majority of applications, you can change the File Selector's searches any way you want (you can even set up a callback routine to filter every single file or directory). The remainder of this chapter explains in detail the different things you can do to the File Selector.

Code Display 14-4 Display Only Directories

@object GenFileSelectorClass DirectorySelector = {
    GFSI_destination = process;
    GFSI_notificationMsg = MSG_MY_APP_DIRECTORY_SELECTED;
    GFSI_fileCriteria = FSFC_DIRS;
};

Code Display 14-5 Limit the Search to a Single Directory

@object GenFileSelectorClass SingleDirFileSelector = {
    GFSI_destination = process;
    GFSI_notificationMsg = MSG_MY_APP_FILE_SELECTED;
    GFSI_attrs = 			FSA_HAS_FILE_LIST;
    GFSI_fileCriteria =			FSFC_NON_GEOS_FILES | FSFC_GEOS_EXECUTABLES
            | FSFC_GEOS_NON_EXECUTABLES;
    ATTR_GEN_PATH_DATA = {0, DIRECTORY};
        /* Limit the display to this standard directory.
         * Note that paths may be given; relative paths are taken as
         * relative to the specified standard path. This attribute
         * is defined in GenClass. */
};

Code Display 14-6 Show Hidden Files As Well As Normal Files

@object GenFileSelectorClass HiddenFileSelector = {
    GFSI_destination = process;
    GFSI_notificationMsg = MSG_MY_APP_FILE_SELECTED;
    /* Specify that FA_SYSTEM files are the only ones not to be displayed. */
    ATTR_GEN_FILE_SELECTOR_FILE_ATTR = { 0, FA_SYSTEM};
};

Code Display 14-7 Show Only Files Created by a Given Geode

@object GenFileSelectorClass CreatorFileSelector = {
    GFSI_destination = process;
    GFSI_notificationMsg = MSG_MY_APP_FILE_SELECTED;
    ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH = {{"CRTR"}, 0};
};

Code Display 14-8 Show Only Files with a Given Filename Extension

@object GenFileSelectorClass ExtensionFileSelector = {
    GFSI_destination = process;
    GFSI_notificationMsg = MSG_MY_APP_FILE_SELECTED;
    ATTR_GEN_FILE_SELECTOR_NAME_MASK = {"*.BAT"};				/* Show files ending with BAT. */
    /* Note that the mask is case sensitive. This means that the mask must be
     * in upper case to match DOS files. */
};

The FileSelector Object: 3 File Selector Instance Data

The File Selector is diverse and flexible, and you can customize it in many different ways. Typically, you will only need to add or modify a few lines of Goc code to achieve the results you need; however, if you have changing needs, you can change the File Selector during execution by sending it various messages.

The File Selector has a number of attributes and variable data fields that determine its functionality. These are listed in File Selector Attributes , along with comments and the defaults that are set.

Code Display 14-9 File Selector Attributes

/* Following are several type definitions used in the instance data. */
 typedef FileLongName			GenFileSelectorSelection;
typedef WordFlags			 FileSelectorFileCriteria;
typedef WordFlags			 FileSelectorAttrs;
typedef FileLongName			GenFileSelectorMask;
typedef struct {
    FileAttrs		GFSFA_match;
    FileAttrs		GFSFA_mismatch;
}  GenFileSelectorFileAttrs;
typedef struct {
    GeodeAttrs		GFSGA_match;
    GeodeAttrs		GFSGA_mismatch;
}  GenFileSelectorGeodeAttrs;
     /* GFSI_selection
     * The selection field contains the name of the current selection being
     * displayed by the File Selector. */
    @instance GenFileSelectorSelection					 GFSI_selection = {0};
    /* GFSI_fileCriteria
     * The file criteria field contains flags that determine the search and
     * filter criteria used by the File Selector when scanning directories. */
    @instance FileSelectorFileCriteria					 GFSI_fileCriteria =
                        FSFC_DIRS | FSFC_NON_GEOS_FILES |
                        FSFC_GEOS_EXECUTABLES |
                        FSFC_GEOS_NON_EXECTUTABLES;
    /* Possible flags for GFSI_fileCriteria:
     * FSFC_DIRS				FSFC_NON_GEOS_FILES
     * FSFC_GEOS_EXECUTABLES				FSFC_GEOS_NON_EXECUTABLES
     * FSFC_MASK_CASE_INSENSITIVE				FSFC_FILE_FILTER
     * FSFC_FILTER_IS_C				FSFC_TOKEN_NO_ID
     * FSFC_USE_MASK_FOR_DIRS */
    /* GFSI_attrs
     * The attributes field determines what features of the File Selector are
     * to be implemented. */
    @instance FileSelectorAttrs				 GFSI_attrs =		FSA_ALLOW_CHANGE_DIRS |
                        FSA_HAS_CLOSE_DIR_BUTTON |
                        FSA_HAS_OPEN_DIR_BUTTON |
                        FSA_HAS_DOCUMENT_BUTTON |
                        FSA_HAS_CHANGE_DIRECTORY_LIST |
                        FSA_HAS_CHANGE_DRIVE_LIST |
                        FSA_HAS_FILE_LIST;
    /* Possible flags for  GFSI_attrs:
     * FSA_ALLOW_CHANGE_DIRS				FSA_SHOW_FIXED_DISKS_ONLY
     * FSA_SHOW_FILES_DISABLED				FSA_HAS_CLOSE_DIR_BUTTON
     * FSA_HAS_OPEN_DIR_BUTTON				FSA_HAS_DOCUMENT_BUTTON
     * FSA_HAS_CHANGE_DIRECTORY_LIST				FSA_HAS_CHANGE_DRIVE_LIST
     * FSA_HAS_FILE_LIST				FSA_USE_VIRTUAL_ROOT
    /* GFSI_destination and GFSI_notificationMsg
     * When a user selects a file, the File Selector sends the message
     * specified in GFSI_notificationMsg to the object specified in
     * GFSI_destination. */
    @instance optr				 GFSI_destination;
    @instance Message				 GFSI_notificationMsg;
    /* Token and creator matches
     * These two vardata fields filter files according to their tokens.
     * A file that has a token other than that set will not be displayed
     * by the File Selector. If you only want to match files with certain
     * token characters, you can set FSFC_TOKEN_NO_ID in GFSI_fileCriteria. */
    @vardata GeodeToken				 ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH;
    @vardata GeodeToken				 ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH;
    /* File attributes
     * This vardata field filters files according to the files' native
     * attributes. If a file does not have the attributes specified in
     * GFSFA_match, or if it has an attribute specified in GFSFA_mismatch,
     * it will not be displayed. */
    @vardata GenFileSelectorFileAttrs					 ATTR_GEN_FILE_SELECTOR_FILE_ATTR;
    /* Geode attributes
     * This vardata field filters files according to the files' geode
     * attributes. If a file does not have the attributes specified in
     * GFSGA_match, or if it has an attribute specified in GFSGA_mismatch,
     * it will not be displayed. */
    @vardata GenFileSelectorGeodeAttrs					 ATTR_GEN_FILE_SELECTOR_GEODE_ATTR;
    /* Mask
     * This vardata field filters files based on their names. The mask is
     * a text string matched against file names. Files which coincide with the
     * mask string are displayed; others are not. The mask string will also
     * be applied to volumes and directories if FSFC_USE_MASK_FOR_DIRS is
     * set in GFSI_fileCriteria. */
    @vardata GenFileSelectorMask				 ATTR_GEN_FILE_SELECTOR_NAME_MASK;
    /* Virtual Root
     * This vardata field defines a "virtual root" for the file selector. The
     * user will not be allowed to navigate above the "virtual root" except
     * with the Change Drives popup list, if available. If the Change Drives
     * list is used, the virtual root is afterwards ignored. */
     @vardata GenFilePath				 ATTR_GEN_FILE_SELECTOR_VIRTUAL_ROOT;
    /* Scalable UI Support
     * This hint is used to support scalable UI within the File Selector based
     * on the application's features (MSG_GEN_APPLICATION_GET_APP_FEATURES).
     * This hint takes an array of GenFileSelectorScalableUIEntry structures.
     * Typically, one element will have GFSSUIC_SET_FEATURES_IF_APP_FEATURE_ON,
     * and one element will have GFSSUIC_SET_FEATURES_IF_APP_FEATURE_OFF.
     * The structures are defined below. */
    @vardata GenFileSelectorScalableUIEntry  HINT_FILE_SELECTOR_SCALABLE_UI_DATA;
    /* The structures for the above hint are given below.
     * GenFileSelectorScalableUIEntry is a structure with three fields; each
     * element in the array is one of these structures.
     * GenFileSelectorScalableUICommand is the type of the first field.
     * If the data in GFSSUIE_appFeature is true according to the type in
     * GFSSUIE_command, then the features in GFSSUIE_fsFeatures will be set
     * accordingly. */
    typedef ByteEnum GenFileSelectorScalableUICommand;
    #define GFSSUIC_SET_FEATURES_IF_APP_FEATURE_ON							0
    #define GFSSUIC_SET_FEATURES_IF_APP_FEATURE_OFF							1
    #define GFSSUIC_ADD_FEATURES_IF_APP_FEATURE_ON							2
    #define GFSSUIC_SET_FEATURES_IF_APP_LEVEL							3
    #define GFSSUIC_ADD_FEATURES_IF_APP_LEVEL							4
typedef struct {
    GenFileSelectorScalableUICommand					GFSSUIE_command;
    WordFlags					GFSSUIE_appFeature;
    FileSelectorAttrs					GFSSUIE_fsFeatures;
} GenFileSelectorScalableUIEntry;
    /* Number of files
     * This hint determines the number of files that are visible at once. */
     @vardata word				HINT_FILE_SELECTOR_NUMBER_OF_FILES_TO_SHOW;
    /* File List Width
     * This hint defines the width of the file list. The data is a number of
     * average-width characters, up to a maximum of 255.
    @vardata word 				 HINT_FILE_SELECTOR_FILE_LIST_WIDTH;

The GFSI_attrs Field

The GFSI_fileCriteria Field

Matching a File's Token

Matching a File's Creator App

Matching a File's Geode Attributes

Masking File Names

Matching a File's File Attributes

Searching Via Callback Routine

Resetting a Filter

The FileSelector Object: 3.1 File Selector Instance Data: The GFSI_attrs Field

GFSI_attrs, MSG_GEN_FILE_SELECTOR_GET_ATTRS, MSG_GEN_FILE_SELECTOR_SET_ATTRS

The GenFileSelector has ten attributes in GFSI_attrs , shown below. They are stored as a word-sized record of type FileSelectorAttrs ; they may be retrieved with MSG_GEN_FILE_SELECTOR_GET_ATTRS and set with MSG_GEN_FILE_SELECTOR_SET_ATTRS .

FSA_ALLOW_CHANGE_DIRS
This flag allows the user to change directories by double clicking; it is set by default.
FSA_SHOW_FIXED_DISKS_ONLY
This flag does not allow removable volumes (e.g. floppy disks) to be shown in the File Selector volume list. By default, this flag is off.
FSA_SHOW_FILES_DISABLED
This flag instructs the File Selector to display files as disabled. This is useful for SaveAs operations, for example, when files must be displayed but should not be selectable by the user. By default, this flag is off. Setting this flag will turn all other search criteria off, so all filenames will be displayed.
FSA_HAS_CLOSE_DIR_BUTTON
This flag causes the File Selector to include a "Close Directory" button.
FSA_HAS_OPEN_DIR_BUTTON
This flag causes the File Selector to include an "Open Directory" button.
FSA_HAS_DOCUMENT_BUTTON
This flag causes the File Selector to include a "Go to Document" button that displays the DOCUMENT directory.
FSA_HAS_CHANGE_DIRECTORY_LIST
This flag causes the File Selector to include a list that allows the user to change directories.
FSA_HAS_CHANGE_DRIVE_LIST
This flag causes the File Selector to include a list that allows the user to change drives.
FSA_HAS_FILE_LIST
This flag causes the File Selector to include a file list.
FSA_USE_VIRTUAL_ROOT
This flag causes the File Selector to use the information in the ATTR_GEN_FILE_SELECTOR_VIRTUAL_ROOT attribute. This allows changing the File Selector without altering the vardata.
MSG_GEN_FILE_SELECTOR_SET_ATTRS
void	MSG_GEN_FILE_SELECTOR_SET_ATTRS(
        FileSelectorAttrs attributes);

This message sets the File Selector's GFSI_attrs record to a new set of attributes. The File Selector will rescan the current directory using the new attributes. If the File Selector is not visible when it receives this message, it will set the attributes but will not rescan the directory.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: attributes A record of FileSelectorAttrs to set into the File Selector's GFSI_attrs record.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_GET_ATTRS
FileSelectorAttrs MSG_GEN_FILE_SELECTOR_GET_ATTRS();

This message returns the current attributes set in the File Selector's GFSI_attrs instance data.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: None.

Return: A record of FileSelectorAttrs representing the flags set in the File Selector's GFSI_attrs field.

Interception: Generally not intercepted.

The FileSelector Object: 3.2 File Selector Instance Data: The GFSI_fileCriteria Field

GFSI_fileCriteria, MSG_GEN_FILE_SELECTOR_GET_FILE_CRITERIA, MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

Each File Selector can limit or extend its searches in several different ways. The File Selector's instance data contains information about which types of files to include in the display, which not to include, and other special criteria. (Note that if the proper GFSI_attrs are set, directories and volumes will be unaffected by these scan limitations.)

The attribute field GFSI_fileCriteria is a record that indicates which of several search limiters should be considered in a directory scan. Note that some limitations may be invoked without a corresponding GFSI_fileCriteria entry. There are several possible entries in GFSI_fileCriteria , and any or all may be turned on at once. Each flag in the field is shown below, along with the field that it affects:

FSFC_DIRS
This flag indicates that subdirectories should be displayed in the File Selector's directory scans. This flag is set by default.
FSFC_NON_GEOS_FILES
This flag indicates that non-GEOS files should be displayed. This flag is set by default.
FSFC_GEOS_EXECUTABLES
This flag indicates that GEOS executable files should be displayed. This flag is set by default.
FSFC_GEOS_NON_EXECUTABLES
This flag indicates that GEOS non-executable files (documents and other types) should be displayed. This flag is set by default.
FSFC_MASK_CASE_INSENSITIVE
This flag indicates that the filename mask (if any) should be case-insensitive. The mask applies only to files unless FSFC_USE_MASK_FOR_DIRS is also set (see below); the mask is set in ATTR_GEN_FILE_SELECTOR_NAME_MASK .
FSFC_FILE_FILTER
This flag makes the File Selector use an application-defined callback routine when scanning. The callback routine is used after all other filters have been applied. The callback routine must be defined in a subclass of GenFileSelectorClass .
FSFC_FILTER_IS_C
This flag indicates that the callback filter routine is written in C and follows the Pascal calling convention.
FSFC_TOKEN_NO_ID
This flag indicates that when using a token to match files, the File Selector should ignore the token ID. In other words, it will only match the token characters.
FSFC_USE_MASK_FOR_DIRS
This flag indicates that if a filename mask is used, the File Selector should apply the mask to directories as well as to files. By default, the mask is applied only to file names.

You should set the proper GFSI_fileCriteria attributes in your application's Goc code. However, you can change the GFSI_fileCriteria field at run-time with MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA . You can also get the flags with MSG_GEN_FILE_SELECTOR_GET_FILE_CRITERIA . Both of these messages are detailed below.

MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA
void	MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA(
        FileSelectorFileCriteria fileCriteria);

This message sets the File Selector's GFSI_fileCriteria record to a new set of flags. If the File Selector is visible when it receives this message, it will rescan the current directory with the new attributes. Otherwise, it will set the GFSI_fileCriteria record but will not rescan.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: fileCriteria A record of FileSelectorFileCriteria to set into the File Selector's GFSI_fileCriteria record.

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS , MSG_GEN_FILE_SELECTOR_SET_TOKEN , MSG_GEN_FILE_SELECTOR_SET_CREATOR , MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS , MSG_GEN_FILE_SELECTOR_SET_MASK

MSG_GEN_FILE_SELECTOR_GET_FILE_CRITERIA
FileSelectorFileCriteria MSG_GEN_FILE_SELECTOR_GET_FILE_CRITERIA();

This message returns the File Selector's current GFSI_fileCriteria record.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: None.

Return: A record of FileSelectorFileCriteria representing the File Selector's GFSI_fileCriteria flags.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS , MSG_GEN_FILE_SELECTOR_SET_TOKEN , MSG_GEN_FILE_SELECTOR_SET_CREATOR , MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS , MSG_GEN_FILE_SELECTOR_SET_MASK

The FileSelector Object: 3.3 File Selector Instance Data: Matching a File's Token

ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH, MSG_GEN_FILE_SELECTOR_SET_TOKEN, MSG_GEN_FILE_SELECTOR_GET_TOKEN

Since every GEOS file has a token associated with it, you can limit directory scans to include only those files with a given token. (Note that directories and volumes are not affected by this limitation.) Alternatively, because the token is made up of two items, the token chars and the token ID, you can arrange a search based on just the token characters (you can not search on only the ID).

Both the match token ID and the match token characters are stored in the File Selector's ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH instance field. This field is of type GeodeToken , the structure of which is shown below:

  typedef struct {
   TokenChars			GT_chars[TOKEN_CHARS_LENGTH];
   ManufacturerID			GT_manufID;
} GeodeToken;

The GT_chars field is four characters as defined in the geode's geode parameters ( .gp ) file. The GT_manufID field is the manufacturer ID number allotted to the particular developer who created the geode.

If the GFSI_fileCriteria attribute FSFC_TOKEN_NO_ID is set, only the token characters will be matched. The ID portion of the token will be ignored by the File Selector (until FSFC_TOKEN_NO_ID is turned off). See The GFSI_fileCriteria Field for more information on GFSI_fileCriteria .

If no token information is set, no filter will be applied based on tokens. To set the token at run-time, send MSG_GEN_FILE_SELECTOR_SET_TOKEN to the File Selector. To retrieve the token match information, send MSG_GEN_FILE_SELECTOR_GET_TOKEN .

MSG_GEN_FILE_SELECTOR_SET_TOKEN
void	MSG_GEN_FILE_SELECTOR_SET_TOKEN(
        dword		tokenChars,
        ManufacturerID		manufacturerID);

This message sets the ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH field to the two passed values. The token set with this message will be used to filter out certain files; directories and volumes are not affected by this filter.

If the File Selector is visible when it receives this message, it will rescan the current directory using the new token information. Otherwise, the File Selector will store the passed token.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: tokenChars The token characters of the token to match files against. Use the macro SET_TOKEN_CHARS (below) to set this dword value from the four individual characters.

manufacturerID
The token ID of the token to match files against.

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA , MSG_GEN_FILE_SELECTOR_SET_CREATOR

MSG_GEN_FILE_SELECTOR_GET_TOKEN
void	MSG_GEN_FILE_SELECTOR_GET_TOKEN(
        GetTokenCreatorParams *retValue);

This message returns the current token match information set in the File Selector's ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH field.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: retValue A pointer to a GetTokenCreatorParams structure, shown below. The structure may be empty when passed.

Return: The GetTokenCreatorParams structure pointed to by retValue will contain the token in ATTR_GEN_FILE_SELECTOR_TOKEN_MATCH .

Structures: The GetTokenCreatorParams structure consists of two elements: a GeodeToken structure containing the File Selector's token characters and manufacturer ID fields, and a reserved word. Its structure is shown below:

typedef struct {
    GeodeToken   GTP_Token;
    word         GTP_Unused;						/* reserved */
} GetTokenCreatorParams;

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA , MSG_GEN_FILE_SELECTOR_GET_CREATOR

SET_TOKEN_CHARS
dword	SET_TOKEN_CHARS(ch1, ch2, ch3, ch4);
        char	ch1, ch2, ch3, ch4;

This macro creates a single dword containing the four given characters. Use this macro to create the tokenChars parameter for MSG_GEN_FILE_SELECTOR_SET_TOKEN and MSG_GEN_FILE_SELECTOR_SET_CREATOR .

The FileSelector Object: 3.4 File Selector Instance Data: Matching a File's Creator App

ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH, MSG_GEN_FILE_SELECTOR_SET_CREATOR, MSG_GEN_FILE_SELECTOR_GET_CREATOR

Most applications will store their documents in a special type of file; each of these files will include the token of the creator geode. (This is a function of the Document Control objects.)

As a result, you may limit the File Selector's searches to only those files created by a given geode. Set the token to be matched in the File Selector's ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH variable data instance field. This search is employed in the same manner as a token search (above), so it is also subject to the FSFC_TOKEN_NO_ID attribute of GFSI_fileCriteria .

The ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH field is a GeodeToken structure that contains both the match ID and the match characters of the creator token. The GeodeToken structure is shown in the previous section. You can set the creator information in the File Selector's Goc code, or you can set it at run-time with MSG_GEN_FILE_SELECTOR_SET_CREATOR . To retrieve the creator token, use MSG_GEN_FILE_SELECTOR_GET_CREATOR .

MSG_GEN_FILE_SELECTOR_SET_CREATOR
void	MSG_GEN_FILE_SELECTOR_SET_CREATOR(
        dword		tokenChars,
        ManufacturerID		manufacturerID);

This message sets the ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH field to the two passed values. This field is a vardata instance record of type GeodeToken . The token set with this message will be used to filter out certain files; directories and volumes are not affected by this filter. Only files which have their creator token the same as that of the File Selector will be displayed.

If the File Selector is visible when it receives this message, it will rescan the directory using the new token as a filter. Otherwise, the File Selector will store the passed token for later use.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: tokenChars The token characters of the creator token to match files against. Use the macro SET_TOKEN_CHARS to set this dword value from the four individual characters. This macro is shown in the previous section.

manufacturerID
The token ID of the creator token to match files against.

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA , MSG_GEN_FILE_SELECTOR_SET_TOKEN

MSG_GEN_FILE_SELECTOR_GET_CREATOR
void	MSG_GEN_FILE_SELECTOR_GET_CREATOR(
        GetTokenCreatorParams *retValue);

This message returns the current creator match information set in the File Selector's ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH field.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: retValue A pointer to a GetTokenCreatorParams structure, shown below. The structure may be empty when passed.

Return: The GetTokenCreatorParams structure pointed to by retValue will contain the token in ATTR_GEN_FILE_SELECTOR_CREATOR_MATCH .

Structures: The GetTokenCreatorParams structure consists of two elements: a GeodeToken structure containing the File Selector's token characters and manufacturer ID fields, and a reserved word. Its structure is shown below:

typedef struct {
     GeodeToken   GTP_token;
    word         GTP_unused;						/* reserved */
} GetTokenCreatorParams;

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA, MSG_GEN_FILE_SELECTOR_GET_TOKEN

The FileSelector Object: 3.5 File Selector Instance Data: Matching a File's Geode Attributes

ATTR_GEN_FILE_SELECTOR_GEODE_ATTR, MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS, MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS

Every geode in GEOS has a structure of type GeodeAttrs stored in its file. As a result, you can limit the File Selector's searches to include only a certain set of geodes--those with the attributes set in the File Selector's ATTR_GEN_FILE_SELECTOR_GEODE_ATTR vardata instance field.

ATTR_GEN_FILE_SELECTOR_GEODE_ATTR is a structure of type GenFileSelectorGeodeAttrs , which consists of two parts. GFSGA_match represents the geode attributes an acceptable file must have on, and GFSGA_mismatch represents the geode attributes an acceptable file must have off. The structure's definition is shown below:

typedef struct {
     GeodeAttrs    GFSGA_match;
    GeodeAttrs    GFSGA_mismatch;
} GenFileSelectorGeodeAttrs;

To retrieve the current geode match information, send the message MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS to the File Selector object. To set them, send MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS . If you do not explicitly set the geode match information, the File Selector will not apply the geode match filter to its directory scans.

The possible geode attributes on which you can match or mismatch are shown below. They are all flags of the GeodeAttrs record, a system type that is not specific to GenFileSelectorClass

. Note that none of these flags is set by default. (Geode attributes are described in detail in the Applications and Geodes chapter.)

GA_PROCESS
This flag indicates the geode is a process (has an initial thread).
GA_LIBRARY
This flag indicates the geode is a library (exports routines).
GA_DRIVER
This flag indicates the geode is a driver (has a Driver Table).
GA_KEEP_FILE_OPEN
This flag indicates the .geo file must stay open (e.g. is a resource that is designated as discardable).
GA_SYSTEM
This flag indicates the geode is a GEOS system geode.
GA_MULTI_LAUNCHABLE
This flag indicates the geode may be loaded more than once.
GA_APPLICATION
This flag indicates the geode is a user-launchable application.
GA_DRIVER_INITIALIZED
Set on the fly by GEOS if the geode's driver aspect has been initialized (should not be matched).
GA_LIBRARY_INITIALIZED
Set on the fly by GEOS if the geode's library aspect has been initialized (should not be matched).
GA_GEODE_INITIALIZED
Set on the fly by GEOS if the geode's process aspect has been initialized (should not be matched).
GA_USES_COPROC
This flag indicates that the geode uses a coprocessor if one is available.
GA_REQUIRES_COPROC
This flag indicates the geode requires a coprocessor or coprocessor emulator.
GA_HAS_GENERAL_CONSUMER_MODE
This flag indicates the geode can be run in GCM mode.
GA_ENTRY_POINTS_IN_C
This flag indicates that the geode has library/driver entry points in C.
MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS
void	MSG_GEN_FILE_SELECTOR_SET_GEODE_ATTRS(
        GeodeAttrs		matchGeodeAttrs,
        GeodeAttrs		mismatchGeodeAttrs);

This message sets the File Selector's vardata instance field ATTR_GEN_FILE_SELECTOR_GEODE_ATTR to the two passed values.

If the File Selector is visible when it receives this message, it will automatically rescan the current directory, applying the new geode filter. Otherwise, the new geode match/mismatch records will be stored but no rescan will occur.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: matchGeodeAttrs A GeodeAttrs record indicating the flags that every acceptable file must have set. This record will be set into GFSGA_match .

mismatchGeodeAttrs
A GeodeAttrs record indicating the flags that every acceptable file must have clear. This record will be set into GFSGA_mismatch .

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS
dword	 MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS();

This message returns the File Selector's current geode attribute match information.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: None.

Return: The returned dword consists of the two GeodeAttrs records stored in the File Selector's ATTR_GEN_FILE_SELECTOR_GEODE_ATTR vardata field. To extract the match attributes ( GFSGA_match ), use the macro GET_MATCH_ATTRS . To extract the mismatch attributes ( GFSGA_mismatch ), use GET_MISMATCH_ATTRS.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

GET_MATCH_ATTRS
word	GET_MATCH_ATTRS(attr);
        dword	attr;

This macro extracts the GFSGA_match record ( GeodeAttrs ) from the given dword (returned by MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS ).

GET_MISMATCH_ATTRS
word	GET_MISMATCH_ATTRS(attr);
        dword	attr;

This macro extracts the GFSGA_match record ( GeodeAttrs ) from the given dword (returned by MSG_GEN_FILE_SELECTOR_GET_GEODE_ATTRS ).

The FileSelector Object: 3.6 File Selector Instance Data: Masking File Names

ATTR_GEN_FILE_SELECTOR_NAME_MASK, MSG_GEN_FILE_SELECTOR_SET_MASK, MSG_GEN_FILE_SELECTOR_GET_MASK

By setting up a filename mask, you can limit the File Selector's displays to only those files that conform to the mask characters. For example, to show only those files with the .BAT extender, you could set the mask to *.BAT.

Only one mask may be in use at any given time. If you need to mask for two different sets of characters (e.g. *.BAT and *.EXE), you will need to subclass the File Selector and modify its behavior. (See Searching Via Callback Routine for more information.)

The mask is stored in the File Selector's variable data instance attribute ATTR_GEN_FILE_SELECTOR_NAME_MASK and consists of a null-terminated character string. This field is defined as type GenFileSelectorMask , the same as FileLongName . This string may contain the DOS " * " and " ? " wildcard characters. The filter applied by the File Selector is the same as that applied by the FileEnum() routine detailed in the File System chapter. In short, the mask is taken as a character string matched to the entire file name (thus, a mask of *.* implies all files with a period in their names; the period separating a filename and an extension will not count as a period).

Normally, the mask is applied only to files and not to directories or volumes. However, if the GFSI_fileCriteria attribute FSFC_USE_MASK_FOR_DIRS is set, directories will also be subject to the mask. (Volumes are never subject to the mask filter.)

By default, the mask filter is case-sensitive. You can make the mask filter be case-insensitive, however, by setting FSFC_MASK_CASE_INSENSITIVE in the File Selector's GFSI_fileCriteria instance field.

You can set the mask in your application's Goc code or by sending the message MSG_GEN_FILE_SELECTOR_SET_MASK to the File Selector. To retrieve the current mask, use MSG_GEN_FILE_SELECTOR_GET_MASK . If you do not explicitly set a mask string, the File Selector will not make any filename mask comparisons.

MSG_GEN_FILE_SELECTOR_SET_MASK
void	MSG_GEN_FILE_SELECTOR_SET_MASK(
        char	*mask);

This message sets the ATTR_GEN_FILE_SELECTOR_NAME_MASK variable instance field of the File Selector. This field contains a null-terminated character string (of type GenFileSelectorMask ); during a directory scan, the File Selector checks all filenames against this mask, and only those files that contain the mask characters are displayed. The mask string may contain DOS wildcard characters (" * " and " ? ").

The mask works for both DOS and GEOS files. However, unless the FSFC_USE_MASK_FOR_DIRS flag is set in the GFSI_fileCriteria attribute, directories will be unaffected by the mask. Volumes are unaffected in any case.

If the File Selector is visible on the screen when it receives this message, it will automatically rescan the current directory with the new mask. Otherwise, the new mask will be stored for later use.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: mask A pointer to the mask string to be set. This is a null-terminated character string.

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

MSG_GEN_FILE_SELECTOR_GET_MASK
void	MSG_GEN_FILE_SELECTOR_GET_MASK(
        char	*mask);

This message returns the mask string in the File Selector's ATTR_GEN_FILE_SELECTOR_NAME_MASK field.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: mask A pointer to an empty character string. The string must be at least sizeof(GenFileSelectorMask) characters.

Return: The character string pointed to by mask will contain the mask string set in ATTR_GEN_FILE_SELECTOR_NAME_MASK .

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_MASK, MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

The FileSelector Object: 3.7 File Selector Instance Data: Matching a File's File Attributes

ATTR_GEN_FILE_SELECTOR_FILE_ATTR, MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS, MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS

All files have a number of attributes stored in a record of type FileAttrs . (For full information on FileAttrs , see the File System chapter.) To filter files based on the FileAttrs record, set your File Selector's variable data instance field ATTR_GEN_FILE_SELECTOR_FILE_ATTR to reflect the attributes that must be on and those that must be off.

ATTR_GEN_FILE_SELECTOR_FILE_ATTR is a structure of type GenFileSelectorFileAttrs , shown below. This structure contains two fields: GFSFA_match represents the attributes an acceptable file has on, and GFSFA_mismatch represents the attributes an acceptable file has off.

typedef struct {
     FileAttrs    GFSFA_match;
    FileAttrs    GFSFA_mismatch;
} GenFileSelectorFileAttrs;

The allowable file attributes are listed below and are discussed fully in the File System chapter. If you don't explicitly set the file attribute limitations, the File Selector will automatically filter out all files with either FA_SYSTEM or FA_HIDDEN.

FA_ARCHIVE
This flag indicates that the file requires a backup.
FA_SYSTEM
This flag indicates that the file used by DOS.
FA_HIDDEN
This flag indicates that the file not seen by normal searches.
FA_RDONLY
This flag indicates that the file is read-only.

Note: For directories not to be filtered out when FSFC_DIRS isn't set, you must mismatch FA_SYSTEM and FA_HIDDEN. That is, to show only those subdirectories which match your filter criteria, ensure FSFC_DIRS is not set, and set mismatch attributes thus:

ATTR_GEN_FILE_SELECTOR_FILE_ATTR = {
                0,
                FA_HIDDEN | FA_SYSTEM
}

You may retrieve the current file attributes by sending the message MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS to the File Selector object. You may set new file attribute limitations by sending it the message MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS .

MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS
void	MSG_GEN_FILE_SELECTOR_SET_FILE_ATTRS(
        FileAttrs setAttrs,
        FileAttrs clearAttrs);

This message sets the ATTR_GEN_FILE_SELECTOR_FILE_ATTR vardata instance field of the File Selector. If the File Selector is visible on the screen when it receives this message, it will rescan the directory immediately with the new attributes.

Source: Unrestricted.

Destination: Any GenFileSelector object

Parameters: setAttrs A FileAttrs record to be set into GFSFA_match . Only files with these flags set will pass the filter.

clearAttrs
A FileAttrs record to be set into GFSFA_mismatch . Only files with these flags cleared will pass the filter.

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS
word	MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS();

This message returns the ATTR_GEN_FILE_SELECTOR_FILE_ATTR of the File Selector. This attribute contains two byte-sized fields which represent the match and mismatch attributes (see above).

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: None.

Return: A word value: The high byte represents the mismatch attributes, and the low byte represents the match attributes. To extract these two fields from the returned value, use the macros GET_MATCH_FILE_ATTRS and GET_MISMATCH_FILE_ATTRS (below).

Interception: Generally not intercepted.

See Also: MSG_GEN_FILE_SELECTOR_SET_FILE_CRITERIA

GET_MATCH_FILE_ATTRS
byte	GET_MATCH_FILE_ATTRS(attr);
        word	attr;

This macro extracts the GFSFA_match portion of the word value returned by MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS . The extracted value is a record of FileAttrs .

GET_MISMATCH_FILE_ATTRS
byte	GET_MISMATCH_FILE_ATTRS(attr);
        word	attr;

This macro extracts the GFSFA_mismatch portion of the word value returned by MSG_GEN_FILE_SELECTOR_GET_FILE_ATTRS . The extracted value is a record of FileAttrs .

The FileSelector Object: 3.8 File Selector Instance Data: Searching Via Callback Routine

MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE, GenFileSelectorFilterRoutine

If the built-in search limitations do not provide the functionality you require, you can set up a callback routine that the File Selector will call for each file or subdirectory in a directory scan. This routine will serve as the final filter for each file; if a file passes all other filters, it will be subjected to your routine. This procedure adds overhead to any scan, of course, so you should try to use the built-in File Selector functions whenever possible.

To set the File Selector to use the callback functionality, you must set FSFC_FILTER_FILE in the GFSI_fileCriteria field. If your callback routine is written in C, you must also set FSFC_FILTER_IS_C; the routine must also follow the Pascal calling convention.

In order to take advantage of the callback functionality, you must first subclass GenFileSelectorClass to handle the message MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE , which the File Selector will send to itself before beginning the directory scan. Your handler should return the address of the callback routine along with a pointer to an array of attributes that will be passed to it.

The callback routine can assume that if it is being called, the file has passed all other filters--the callback is therefore the last word on acceptance or rejection of the file. In essence, the callback is asked whether the file should be rejected: It should return the constant TRUE if the file is to be rejected but FALSE if it is not to be rejected.

As mentioned above, you can designate an array of extended file attributes that will be passed to the callback routine. This array contains a number of FileExtAttrDesc structures, each of which defines a certain attribute and some extra data for the attribute. This structure is shown below:

typedef struct {
    FileExtendedAttribute FEAD_attr;
                /* file attribute to
                 * get or set */
    void		* FEAD_value;
                /* pointer to a buffer
                 * or new value */
    word		FEAD_size;		/* size of buffer or
                 * new value */
    char		* FEAD_name;
                /* if custom attribute,
                 * pointer to name */
} FileExtAttrDesc;

In the above context, FEAD_value is meaningless and FEAD_size is the number of bytes required to hold the value of the attribute. Note that this array is not passed to the callback routine; instead, it specifies the structure and composition of the values that will be.

An example of filtering files using a callback routine is shown in Filtering Files Via a Callback Routine . The FileExtAttrDesc structure is defined and detailed in the File System chapter.

Code Display 14-10 Filtering Files Via a Callback Routine

/*******************************************************************************
 * This is a sample handler for MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE.
 * You should be able to use this code in your subclass. Note that the routine
 * name "FilterFileSelectorRoutine" is application-specific; it can be whatever
 * you want. It must be declared as static Boolean, however. See below.
 * PARAMETERS:
 * void (GenFileSelectorGetFilterRoutineResults *filter)
 *******************************************************************************/
@method FilterFileSelectorClass, MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE {
    /* We need to return a virtual pointer to the filter routine so the
     * file selector can lock and unlock the routine's code resource as
     * appropriate. Unfortunately, the compiler optimizes the simple
     * implementation (just assigning filter->filterRoutine the address of
     * the routine) by storing CS for the segment, which is unhelpful. So
     * we need a static variable holding the routine's address instead. */
    static GenFileSelectorFilterRoutine *const filterRoutine =
                    &FilterFileSelectorRoutine;
    /* Specify the address of the routine to call. It need not be locked into
     * memory, as the GenFileSelector will do that for us. */
    filter->filterRoutine = filterRoutine;
    /* Specify the additional attributes we need to look at in our filter
     * routine. As with the filterRoutine, these need not be in fixed or
     * locked memory. */
    filter->filterAttrs = &filterFileSelectorFilterAttrs;
}
/*******************************************************************************
 * This is a sample File Selector callback routine. It may be called whatever
 * you want and must be declared static Boolean.
 * This routine is called once per file or directory that passes all other File
 * Selector filters. This routine is limited to examining the attributes of the
 * file. It may not do anything that could cause the File Selector's instance chunk
 * to move in memory.
 * Return: TRUE to reject the file, FALSE to accept the file.
 *******************************************************************************/
static Boolean FilterFileSelectorRoutine(optr oself,
                FileEnumCallbackData *fecd, word frame)
/* The code of the routine is not included here; to see it, look at the FSFilter
 * sample application. */
    /* Following is the array of attributes examined by our filter routine.
     * This array contains an arbitrary number of elements, the last of which
     * has FEA_END_OF_LIST as its FEAD_attr field. FEAD_value is unused in this
     * context. FEAD_size is set to the total number of bytes needed for each
     * attribute.
     * Each structure's fields are FEAD_attr, FEAD_value, FEAD_size,
     * and FEAD_name */
const FileExtAttrDesc filterFileSelectorFilterAttrs[] = {
    { FEA_NAME, 0, sizeof(FileLongName), NULL },
    { FEA_FILE_ATTR, 0, sizeof(FileAttrs), NULL },
    { FEA_FILE_TYPE, 0, sizeof(GeosFileType), NULL },
    { FEA_END_OF_LIST, 0, 0, NULL }
};
MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE
void	MSG_GEN_FILE_SELECTOR_GET_FILTER_ROUTINE(
        GenFileSelectorGetFilterRoutineResults *filter);

This message returns the address of the callback routine used by the File Selector as well as the array of FileExtAttrDesc structures specifying the callback's parameters.

For the callback function to be used, you must set FSFC_FILE_FILTER in the File Selector's GFSI_fileCriteria instance data field. If your callback routine is in C, you must also set FSFC_FILTER_IS_C. In addition, the callback routine must follow the Pascal calling convention.

Source: Sent by the File Selector object to itself before calling FileEnum() . Also may be used by other objects to retrieve the callback information for the File Selector.

Destination: Any GenFileSelector object.

Parameters: filter A pointer to an empty return value structure. This structure is detailed below.

Return: The structure pointed to by filter must be filled by this method.

Structures: The single parameter is a pointer to an empty structure of type GenFileSelectorGetFilterRoutineResults . This structure has two parts: filterRoutine is a pointer to your callback routine, and filterAttrs is a pointer to an array of FileExtAttrDesc structures. You must fill in this structure in your handler so the File Selector knows how to call your callback routine. The structure is shown below:

typedef struct {
    GenFileSelectorFilterRoutine						*filterRoutine;
    const FileExtAttrDesc						*filterAttrs;
} GenFileSelectorGetFilterRoutineResults;

Interception: If a callback routine is to be used, you must subclass and handle this message to return the proper structures.

Tips: If your callback has no requirements other than the file name, you can pass NULL in the filterAttrs field of filter .

Warnings: The handler for this message must return a virtual segment for the callback routine in filter 's filterRoutine field. This can cause problems with most compilers; to avoid these problems, set a static variable in your method as shown in Filtering Files Via a Callback Routine , below (taken from the FSFilter sample application).

See Also: FileEnum()

GenFileSelectorFilterRoutine()
Boolean	GenFileSelectorFilterRoutine(
        optr			oself,
        FileEnumCallbackData			*fecd,
        word			frame);

This routine is defined by your application and is called for each file in a directory scan. It may be of any name of your choice as shown in Filtering Files Via a Callback Routine . It serves as the final filter; when called, it can assume the subject file has passed all other filters applied. This routine is set up and executed in the same manner as the callback routine for FileEnum() ; see the File System chapterfor complete details.

Parameters: oself The optr of the File Selector object

fecd
A pointer to a FileEnumCallbackData structure.
frame
An inherited stack frame passed by FileEnum() to each of its helper routines.

Return: Your callback must return the constant FALSE if the file should be accepted, TRUE if the file should be rejected.

Warnings: Your routine must not do anything that may cause the File Selector or its object block to move in memory (adding variable data fields, for instance).

See Also: FileEnum()

The FileSelector Object: 3.9 File Selector Instance Data: Resetting a Filter

Once a filter is set (such as a filename mask or a file attribute match/mismatch filter), you may wish to delete the filter. To remove a filter designated by a flag in an instance field, simply clear the flag with the appropriate message.

If the filter is applied via a variable data field, however, you will have to use MSG_META_DELETE_VARDATA . This message is detailed in MetaClass and must be passed the name of the variable data field to be removed. Removal of the field equates to removal of the filter.

The FileSelector Object: 4 File Selector Use

Besides setting the File Selector's attributes and dynamically manipulating its instance data, you can change its display and current file lists using several messages. You can also retrieve its current state with other messages. These functions are detailed in the following sections.

When a User Selects a File

The Current Selection

Rescanning Directories

Setting Scalable UI Data

The FileSelector Object: 4.1 File Selector Use: When a User Selects a File

GFSI_destination, GFSI_notificationMsg, MSG_GEN_FILE_SELECTOR_GET_ACTION, MSG_GEN_FILE_SELECTOR_SET_ACTION

When a user selects an entry in the File Selector's displayed list, either by double-clicking or by clicking on an "Open" button, the File Selector will notify a predetermined object or process that a selection has been made. In addition, if the selection is a volume or a directory, the File Selector will automatically (on a double-click) open the selection and show the new file list.

When defining your GenFileSelector, you must designate an output object and a message that will be sent to it when the user makes a selection. The message is stored in the GFSI_notificationMsg instance field, and the object's optr is stored in the GFSI_destination instance field. Note that instead of an optr, you may use a TravelOption as the output destination (TO_TARGET, TO_FOCUS, etc.); you can also use ATTR_GEN_DESTINATION_CLASS to specify the class of the output object to ensure the notification message is only delivered to a class that can handle it.

When the user makes a selection, the File Selector will send the notification message to the destination object along with flags indicating whether the selection was a single or double click. The message is defined by the class that will receive it; for example, the File Selector definition in A Basic File Selector designates MSG_MY_APP_FILE_SELECTED as the notification message that will be sent to the application's process object (the destination object).

You must set both the notification message and the destination object in your File Selector's Goc code. If you don't, no message will be sent, and your application will ignore user input to the File Selector. (The File Selector will still allow the user to navigate throughout the file system, and your application can query the File Selector as to its current selection, however.)

You may retrieve or set the File Selector's notification message and destination object at run-time by sending the following messages:

MSG_GEN_FILE_SELECTOR_GET_ACTION
Returns the File Selector's current notification message and destination object.
MSG_GEN_FILE_SELECTOR_SET_ACTION
Sets the File Selector's GFSI_destination and GFSI_notificationMsg fields to new values.
MSG_GEN_FILE_SELECTOR_GET_ACTION
void	MSG_GEN_FILE_SELECTOR_GET_ACTION(
        GetActionParams *retValue);

This message returns the File Selector's output object and notification message.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: retValue A pointer to an empty GetActionParams structure (detailed below), which will be filled with the return data.

Return: The GetActionParams structure pointed to by retValue will contain the output optr and notification message set for the File Selector.

Structures: The GetActionParams structure is shown below:

  typedef struct {
    Message  GAP_message; /* GFSI_notificationMsg */
    word     GAP_unused;  /* Internal field */
    optr     GAP_output;  /* GFSI_destination */
} GetActionParams;

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_SET_ACTION
void	MSG_GEN_FILE_SELECTOR_SET_ACTION(
        optr		actionOD,
        Message		actionMessage);

This message sets the File Selector's GFSI_notificationMsg field to actionMessage and the GFSI_destination field to actionOD .

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: actionOD The optr of the new output object.

actionMessage
The new output notification message.

Return: Nothing.

Interception: Generally not intercepted.

The FileSelector Object: 4.2 File Selector Use: The Current Selection

GFSI_selection

Most directory and volume navigation is handled internally by the File Selector, and the application has no need to know what is happening until a file is actually selected.

The File Selector maintains the name of the current selection in its GFSI_selection instance data field. The current full path, volume name, and disk are stored in variable data entries defined by GenClass . The application can easily, at any time, retrieve or set the current selection, path, or volume. Volume names, paths, and file names are all stored as null-terminated character strings in the GEOS character set.

Current path and directory are supported by GenClass and are not specific to GenFileSelectorClass .

The File Selector's current path is stored in a variable data field called ATTR_GEN_PATH_DATA . This field is a GenFilePath structure, defined below. This structure stores both an absolute path and the handle of the disk on which the path resides. For the GenFileSelector, this structure represents the currently-displayed directory.

  typedef struct {
    DiskHandle  GFP_disk;
    PathName    GFP_path;
} GenFilePath;

To retrieve the current path, send the File Selector a MSG_GEN_PATH_GET . To set the path, send it MSG_GEN_PATH_SET . To retrieve just the disk handle, send MSG_GEN_PATH_GET_DISK_HANDLE . Each of these messages is detailed fully in the GenClass chapter.

Traversing the File System

MSG_GEN_FILE_SELECTOR_UP_DIRECTORY, MSG_GEN_FILE_SELECTOR_OPEN_ENTRY

To set the currently-displayed directory, you can send one of the following messages to the File Selector object (these are all, of course, subject to the File Selector's attributes and applied filters):

MSG_GEN_FILE_SELECTOR_UP_DIRECTORY
Cause the File Selector to go up one directory. If already at the root, switch to volume selection. Does nothing if FSA_ALLOW_CHANGE_DIRS is not set in GFSI_attrs .
MSG_GEN_FILE_SELECTOR_OPEN_ENTRY
Cause the File Selector to attempt to open the passed entry in the current directory. Nothing will be done if the passed entry is a file; this message opens only directories and volumes. Use this with MSG_GEN_FILE_SELECTOR_GET_SELECTION when a user clicks on an "Open" button (or something similar) in the File Selector's dialog box.
MSG_GEN_FILE_SELECTOR_UP_DIRECTORY
void	MSG_GEN_FILE_SELECTOR_UP_DIRECTORY();

This message causes the File Selector to go up one directory in the directory tree. If the File Selector is already in the root directory, it will switch to the volume list (if FSA_ALLOW_CHANGE_VOLUMES is set in GFSI_attrs ). This message is only valid when the File Selector is visible on the screen.

Source: Unrestricted.

Destination: Any GenFileSelector object that is on the screen.

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_OPEN_ENTRY
Boolean	MSG_GEN_FILE_SELECTOR_OPEN_ENTRY(
        word	entryNumber);

This message causes the File Selector to open and display the contents of the entry specified. It returns an error flag: if the entry opens successfully, the error flag is returned false ; if some error occurs, the flag is returned true .

The entry specified with this message must be in the current file list. First, retrieve the entry number from the File Selector with the message MSG_GEN_FILE_SELECTOR_GET_SELECTION . If the entry can be opened (i.e. it is in the current file list and it is a volume or directory), the File Selector will open it, scan the directory, and display the new file list. If the entry is not a directory or volume, the File Selector will do nothing. This message is only valid when the File Selector is visible on the screen.

Source: Unrestricted.

Destination: Any GenFileSelector object that is on the screen.

Parameters: entryNumber The number of the entry to be opened.

Return: An error flag: true if error, false if no error.

Interception: Generally not intercepted.

The Current Selection

MSG_GEN_FILE_SELECTOR_SET_SELECTION, MSG_GEN_FILE_SELECTOR_SET_FULL_SELECTION_PATH, MSG_GEN_FILE_SELECTOR_GET_SELECTION, MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH

The File Selector keeps track of which item is currently designated as the selection . The selection appears on the screen as the only highlighted entry in the current list (the highlighted entry may be scrolled out of the view but remains the selection). Additionally, the selection is the file or folder on which any operations (such as open) will be executed.

The GFSI_selection field of the GenFileSelector's instance data is a null-terminated character string representing the name of the selection. The format of the selection string depends on the context: If the File Selector is displaying the volume list and thus the selection is a volume label, the string will consist of a drive name followed by a colon. (For non-removable disks, the colon will be followed by [ volume name ].) If the File Selector is displaying a directory and the selection is a file or directory, the selection string will consist of the name of the file or directory selected. The selection does not contain any information about the current volume or directory.

By sending the following messages, you can retrieve or set the current selection in your application. These messages affect ATTR_GEN_PATH_DATA, managed by GenClass for the File Selector, as well as the GFSI_selection instance field.

MSG_GEN_FILE_SELECTOR_SET_SELECTION
Attempt to set the selection to the given volume, file, or directory name. The given file or subdirectory must be in the currently-displayed list.
MSG_GEN_FILE_SELECTOR_SET_FULL_SELECTION_PATH
Attempt to set the path and selection to the given volume, file, or directory name. The passed selection must be in the directory at the end of the given path.
MSG_GEN_FILE_SELECTOR_GET_SELECTION
Gives a null-terminated character string representing the name of the current selection. This may be a volume, directory, or file name.
MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH
Gives the disk handle as well as a null-terminated character string representing the full path name of the current selection, excluding the drive name.

The selection retrieval messages return two word values in a single dword argument. These two values are a record of GenFileSelectorEntryFlags , flags that indicate the type of selection and operation underway, and an integer that indicates the place of the selection in the current file list. Two macros allow you to extract these values from the dword argument:

GFS_GET_ENTRY_NUMBER
Extracts the entry number from the given dword.
GFS_GET_ENTRY_FLAGS
Extracts the selection's flags record.
GFS_GET_ENTRY_NUMBER
word	GFS_GET_ENTRY_NUMBER(arg);
        dword	arg;

This takes the return value of MSG_GEN_FILE_SELECTOR_GET_SELECTION and MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH and returns the entry number of the selection.

GFS_GET_ENTRY_FLAGS
word	GFS_GET_ENTRY_FLAGS(arg);
        dword	arg;

This takes the return value of MSG_GEN_FILE_SELECTOR_GET_SELECTION and MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH and returns the GenFileSelectorEntryFlags for the selection.

MSG_GEN_FILE_SELECTOR_SET_SELECTION
Boolean	MSG_GEN_FILE_SELECTOR_SET_SELECTION(
        char	*selection);

This message causes the File Selector to attempt to select a given file, directory, or volume from the currently-displayed file list. It sets the selection by setting the File Selector's GFSI_selection field to the passed string.

If the File Selector is not on the screen or is suspended when it receives this message, it will set the selection and hold it until it scans the directory. The validity of the selection is not determined until the directory is again scanned. If the directory is not scanned immediately, the message's return value will be true . If the File Selector is on the screen and is not suspended, it will determine the validity of the selection immediately and return a value appropriate to the file's validity.

If the selection is valid, the user will see it highlighted in the File Selector's display. The selection will not be opened by this message; it is only selected.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: selection A pointer to a character string giving the name of the file to be selected. The selection string is case-sensitive; if selecting a DOS file, it must be all upper case.

Return: An error flag: false if the selection is successfully made. The error condition can occur if the file is not found or if the File Selector can not currently be updated (it is not visible or is suspended).

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_GET_SELECTION
dword	MSG_GEN_FILE_SELECTOR_GET_SELECTION(
        char	*selection);

This message returns the name, the entry number, and the entry flags of the file or directory currently selected.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: selection A pointer to an empty character string at least sizeof(FileLongName) characters long. If just the entry number and entry flags are desired, pass a null pointer.

Return: The high word of the dword return value is the number of the selection in the File Selector's current file list. The low word is a record of type GenFileSelectorEntryFlags . Use GFS_GET_ENTRY_NUMBER to extract the entry number; use GFS_GET_ENTRY_FLAGS to extract the flags record.

selection
The character string pointed to will be the null-terminated name of the current selection.

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_SET_FULL_SELECTION_PATH
Boolean	MSG_GEN_FILE_SELECTOR_SET_FULL_SELECTION_PATH(
        char		*selection,
        DiskHandle		diskHandle);

This message causes the File Selector to set its path data and its GFSI_selection field according to the string passed in selection . The string's format is described below. The entry may be in any directory on any volume; the volume is specified by the passed disk handle.

If the File Selector is suspended or not currently on the screen when it receives this message, it will set the path and selection but will do nothing else until it becomes unsuspended or visible. Otherwise, the message will be handled immediately, and the File Selector will navigate to and scan the proper directory, setting the selection if possible.

If the path is valid but the selection is not, the File Selector will display the proper file list and set the selection to the first entry in the list. If any part of the path other than the selection is invalid, the File Selector will show the volume list.

Source: Unrestricted.

Destination: Any GenFileSelector object that is on the screen.

Parameters: selection A pointer to a character string containing the full path to be set as the File Selector's current path and selection. The path may be absolute or relative--if relative, it is considered relative to the File Selector's current directory and diskHandle will be ignored.

diskHandle
The disk handle specifying the volume on which the new selection resides. If zero, the File Selector's currently-displayed disk will be used. If a relative path is specified in selection , this parameter will be ignored.

Return: This message returns an error flag: If the selection is successfully made, the flag will be false . If an error occurs, the flag will be true .

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH
dword	MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH(
        char	*selection);

This message returns the disk handle and flags as well as the full pathname of the current selection.

Source: Unrestricted.

Destination: Any GenFileSelector object.

Parameters: selection A pointer to a character string at least PATH_BUFFER_SIZE characters long. Upon return, this string will contain the full selection path.

Return: The returned dword contains two word-sized fields: The high word represents the File Selector's current disk handle, and the low word is a GenFileSelectorEntryFlags record with the selection's flags. Use the macro GFS_GET_FULL_SELECTION_PATH_DISK_HANDLE to extract the disk handle, and use GFS_GET_ENTRY_FLAGS to extract the flags record. If sent when the File Selector is not visible on the screen, the message will return the disk handle and path of the last valid selection.

selection
The character string pointed to will contain the full path and name of the current selection.

Interception: Generally not intercepted.

GFS_GET_FULL_SELECTION_PATH_DISK_HANDLE
DiskHandle GFS_GET_FULL_SELECTION_PATH_DISK_HANDLE(arg);
        dword	arg;

This macro extracts the disk handle (the high word) from a dword argument returned by MSG_GEN_FILE_SELECTOR_GET_FULL_SELECTION_PATH .

The FileSelector Object: 4.3 File Selector Use: Rescanning Directories

MSG_GEN_FILE_SELECTOR_RESCAN, MSG_GEN_FILE_SELECTOR_SUSPEND, MSG_GEN_FILE_SELECTOR_END_SUSPEND

Each time the File Selector changes directories, it scans that directory and displays only the files it is supposed to. It looks into each file briefly to check the file against each criterion set in the File Selector. You can force the GenFileSelector to rescan the current directory with the message MSG_GEN_FILE_SELECTOR_RESCAN .

Occasionally you will want to scan the current directory only once after changing many parameters instead of rescanning after each change (search limitations, current volume, current selection, etc.). To optimize redrawing and rescanning, you can temporarily keep the GenFileSelector from scanning each time a MSG_GEN_FILE_SELECTOR_SET_... message is sent. To do this, send the following messages (both must be used, though not at the same time):

MSG_GEN_FILE_SELECTOR_SUSPEND
Notifies the File Selector that several operations in a row are coming and that it should suspend scanning the disk until the counterpart to this message (below) is received.
MSG_GEN_FILE_SELECTOR_END_SUSPEND
Indicates to the File Selector that directories may now be rescanned and displayed.
MSG_GEN_FILE_SELECTOR_RESCAN
void	MSG_GEN_FILE_SELECTOR_RESCAN();

This message causes the File Selector to rescan its current directory. The File Selector will re-build and re-display its entire file list. This message will only work when the File Selector is visible on the screen.

Source: Unrestricted.

Destination: Any GenFileSelector object that is on the screen.

Interception: Generally not intercepted.

MSG_GEN_FILE_SELECTOR_SUSPEND
Boolean	MSG_GEN_FILE_SELECTOR_SUSPEND();

This message causes the File Selector to suspend directory scans until a MSG_GEN_FILE_SELECTOR_END_SUSPEND is received. This provides the ability to avoid unnecessary directory rescans when several different attributes are being changed in succession (e.g. volume, mask, and file types).

No user action is affected by this message; only the following attribute-setting messages will be affected (their normal automatic rescan will not happen). Note that all messages have the prefix MSG_GEN_FILE_SELECTOR_ :

SET_SELECTION SET_FULL_SELECTION_PATH
SET_MASK SET_FILE_ATTRS
SET_TOKEN SET_CREATOR
SET_GEODE_ATTRS SET_ATTRS
SET_FILE_CRITERIA

In addition to the above messages, the GenClass message MSG_GEN_PATH_SET will also be suspended.

Source: Unrestricted.

Destination: Any non-suspended GenFileSelector object.

Parameters: None.

Return: An error flag: true if the File Selector is already suspended, false if the suspension is successful.

Interception: Generally not intercepted.

Warnings: After suspending a File Selector with this message, be sure to unsuspend it with MSG_GEN_FILE_SELECTOR_END_SUSPEND .

MSG_GEN_FILE_SELECTOR_END_SUSPEND
Boolean	MSG_GEN_FILE_SELECTOR_END_SUSPEND();

This message unsuspends a File Selector that had previously been suspended with MSG_GEN_FILE_SELECTOR_SUSPEND . This message automatically causes a directory rescan. Because multiple suspension attempts are disallowed, this message only needs to be sent once to unsuspend the File Selector.

Source: Unrestricted.

Destination: Any suspended GenFileSelector object.

Parameters: None.

Return: An error flag: true if the File Selector was not previously suspended, false if the File Selector is successfully unsuspended.

Interception: Generally not intercepted.

The FileSelector Object: 4.4 File Selector Use: Setting Scalable UI Data

HINT_FILE_SELECTOR_SCALABLE_UI_DATA

The File Selector lets you set which features you want based on the current application features, using HINT_FILE_SELECTOR_SCALABLE_UI_DATA . This hint will query the application object to get the current application features set, and it will set the File Selector's feature set ( GFSI_attrs ) accordingly.

The hint takes an array of GenFileSelectorScalableUIEntry structures, each of which has three fields:

GFSSUIE_command
This field is a GenFileSelectorScalableUICommand , which is an enumerated type describing the effect of this particular entry. The five commands are detailed below.
GFSSUIE_appFeature
This word represents the application's feature set or user level. The hint, when processed, will query the GenApplication object (the GAI_appFeatures value) and check those features against the features in this field. If they match, the hint will cause the features in GFSSUIE_fsFeatures to be set or cleared as appropriate.
GFSSUIE_fsFeatures
This field represents the File Selector attributes that should be turned on or off depending on the command and the application features (see both fields above). The commands are defined below.

Following are the GenFileSelectorScalableUICommand s. Each queries the GFSSUIE_appFeature field and then acts appropriately.

GFSSUIC_SET_FEATURES_IF_APP_FEATURE_ON
This command will set the features in GFSSUIE_fsFeatures if the feature(s) in GFSSUIE_appFeature are turned on.
GFSSUIC_SET_FEATURES_IF_APP_FEATURE_OFF
This command will set the features in GFSSUIE_fsFeatures if the feature(s) in GFSSUIE_appFeature are off.
GFSSUIC_ADD_FEATURES_IF_APP_FEATURE_ON
This command will turn on the additional features in GFSSUIE_fsFeatures if the features in GFSSUIE_appFeature are turned on.
GFSSUIC_SET_FEATURES_IF_APP_LEVEL
This command will set the features in GFSSUIE_fsFeatures if the application is at the user level specified in GFSSUIE_appFeature .
GFSSUIC_ADD_FEATURES_IF_APP_LEVEL
This command will add the additional features in GFSSUIE_fsFeatures if the application is at the user level specified in GFSSUIE_appFeature .
This document is a single-page version of a a multi-page document, suitable for easy printing.


Graphics Environment

The GEOS graphics system provides many powerful tools to enhance your geode's appearance and graphical capabilities.

The kernel automatically displays much of what appears on the screen. Generic and Visual objects display themselves, so if these objects can display everything your geode needs, you can probably skip this section for now. On the other hand, geodes with any kind of specialized display needs will need imaging code, as will any geodes that print.

This, the first chapter of the Imaging Section, explains the thinking behind the graphics system and the work you'll have to get done ahead of time to set up the space you're going to be drawing to. For actual drawing commands, see the Drawing Shapes chapter.

Before reading this chapter, you should be familiar with the basics of the generic UI and messaging. You may also want to review high school geometry.

1 Graphics Road Map
1.1 Chapter Structure
1.2 Vocabulary
2 Graphics Goals
3 Graphics Architecture
4 How To Use Graphics
5 Coordinate Space
5.1 Standard Coordinate Space
5.2 Coordinate Transformations
5.3 Precise Coordinates
5.4 Device Coordinates
5.5 Larger Document Spaces
5.6 Current Position
6 Graphics State
6.1 GState Contents
6.2 Working with GStates
7 Working With Bitmaps
8 Graphics Strings
8.1 Storage and Loading
8.2 Special Drawing Commands
8.3 Declaring a GString Statically
8.4 Creating GStrings Dynamically
8.5 Drawing and Scanning
8.6 Editing GStrings Dynamically
8.7 Parsing GStrings
9 Graphics Paths
10 Working With Video Drivers
10.1 Kernel Routines
10.2 Direct Calls to the Driver
11 Windowing and Clipping
11.1 Palettes
11.2 Clipping
11.3 Signalling Updates

Graphics Environment: 1 Graphics Road Map

Graphics is a big topic. It has many applications and thus many ways to apply it. If you will be writing graphics-intensive programs, you'll probably end up reading everything in these chapters. Chances are, however, that you'll only need to learn a few things about the graphics system.

This section is for those people who would like to skip around the GEOS imaging system. It contains an outline of the chapter as well as a list of definitions for terms that will be used throughout.

Chapter Structure

Vocabulary

Graphics Environment: 1.1 Graphics Road Map: Chapter Structure

This chapter is divided into sections. Which sections you'll want to read depend on what you're interested in.

1 Road Map
You're reading the road map now.
2 Goals
This section goes into the design philosophy behind the GEOS graphics system.
3 Architecture
The Graphics Architecture section describes the basics behind how the graphics system works.
4 How to Use Graphics
This section is for programmers new to GEOS who know what sort of graphics they want their geode to have but don't know how to get it. This section discusses the different contexts in which graphics appear and how you can work within each of those contexts. After going through the section, you may wish to return to this road map to find out where to read more about whatever sort of graphics you'll be working with.
5 Coordinate Space
The GEOS graphics system describes locations and distances using a rectangular grid coordinate system. This section explains how to work with and manipulate this space.
6 Graphics State
This section describes the GState, a data structure used in many contexts and for many purposes throughout the graphics system.
7 Bitmaps
It is possible to specify an image by using a data structure in which an array of color values is used to represent a rectangular area of the screen. This section describes how such a data structure can be manipulated.
8 Graphics Strings and Metafiles
This section describes how to create and draw Graphics Strings, also known as GStrings. (See the Vocabulary section, below, for a simple definition of GString.)
9 Graphics Paths
The graphics system allows you to specify an arbitrary area of the display which can be used in powerful graphics operations. This section explains how to set up a path, a data structure describing the outline of the area you want to specify.
10 Video Drivers
Most programmers can safely skip this section, but video game writers might want to read it. This section includes some advanced techniques for getting faster drawing rates.
11 Windowing and Clipping
This section explains some of the work the graphics system does to maintain windows in a Graphical User Interface. Included is an in-depth look at clipping, along with some commands by which you can change the way clipping works.

Graphics Environment: 1.2 Graphics Road Map: Vocabulary

Several terms will crop up again and again in your dealings with graphics. Many of these terms will be familiar to programmers experienced with other graphics systems, but those not familiar with any of these terms are encouraged to find out what they mean.

GState, Graphics State
The graphics system maintains data structures called Graphics States, or GStates for short. GStates keep track of how a geode wants to draw things: the colors to use, current text font, scaling information, and so on. Most graphics routines, including all routines that actually draw anything, take a GState as one of their arguments. Thus to draw the outline of a red rectangle, you would first call the GrSetLineColor() routine to change the color used for drawing lines and outlines to red. Only after thus changing the GState would you call the GrDrawRect() command.
Standard Coordinate Space, Document Coordinate Space
Drawing commands in GEOS use the standard coordinate space, also known as the document coordinate space. This is a rectangular grid used for expressing locations and distances. This grid uses units of 1/72nd of an inch. Thus, drawing a point to (72, 72) will draw the point one inch to the right and one inch below the top left corner of an ordinary Content or document.
GString, Graphics String, Metafile
GString is short for Graphics String. A GString is a data structure representing a sequence of graphics commands. Graphics Strings are used many places in the system. GStrings are used to pass graphics through the clipboard. They serve as the descriptors of graphical monikers, which are in turn used as application icons. The printing system uses GStrings to describe images to be printed. A graphics Metafile is just a file containing a GString, taking advantage of this data structure to store a graphics image to disk.
Bitmap
A bitmap is a picture defined as a rectangular array of color values. Note that since display devices normally have their display area defined as a rectangular grid for which each pixel can have a different color value, it is relatively easy to draw a bitmap to the screen, and in fact the graphics system normally works by constructing a bitmap of what is to be displayed and displaying it. Bitmaps are sometimes known as "raster images."
Path
Paths provide a precise way to specify arbitrary areas of the display area. Like GStrings, they are described by a sequence of drawing commands. Instead of defining a picture, these commands describe a route across the graphics space. The enclosed area can be used in a variety of operations.
Region
Regions provide another approach to describing arbitrary display areas. Regions use a compressed scan-line data structure to represent a pixel-based shape on the display. Though lacking the mathematical precision of paths, region-based operations are very fast and thus ideally suited to certain simple tasks.
Palette, RGB Values
Many display devices can display a wide variety of colors. Of these, most cannot display all their colors at once. Typical configurations can show 16 or 256 colors at a time out of a possible 256K. A palette is one of these subsets of the set of possible colors. When drawing something in color, normally the color to draw with is specified by the palette index, that color's place in the table.
The value stored in each entry is an RGB value, a color described in terms of its red, green, and blue components. Each of these three values ranges between 0 and 255. Geodes can change the RGB values associated with palette entries and thus change the available colors.
Video Driver
These geodes stand between the graphics system and the actual display device. They maintain device independence and do a lot of the "behind the scenes" work of the graphics system.
Windowing, clipping, "marked invalid"
The windowing and graphics systems are heavily intertwined. The graphics system controls how windows should be drawn, while the windows system keeps track of which parts of various displays are visible. For the most part graphics programmers don't have to worry too much about the windowing system, but there are some terms worth knowing. Clipping is the process of keeping track of what things drawn to a window are actually visible. If a geode draws something beyond the edge of a window, the system can't just ignore the drawing, as the user might later reveal the drawing by scrolling onto that area. The graphics system "clips" the graphic, being sure to show only those parts that are in the visible part of a window. Further clipping takes place when a window is obscured behind another window. Any drawing on the lower window must not show up until the upper window is moved. The "clipping region" is that area of a window which is visible--anything drawn to the window outside the region will be clipped (see the figure below). Programs can reduce the clipping areas of any of their associated windows.
The windowing system is able to work much more quickly by assuming that not too many things are going to be changing at once. Normally it can assume that a given area of the screen will look like it did a short while ago. When it's time to change an area of the screen, that area is said to be "marked invalid," since whatever is presently being shown there is no longer valid. The normal example is that if a pull-down menu has been obscuring part of a document, when the menu goes away, the part of the document that becomes visible must be redrawn.
MSG_VIS_DRAW, MSG_META_EXPOSED
These messages are sent to visible objects to instruct them to redraw themselves; if you are using graphics to display a visible object then you are probably intercepting MSG_VIS_DRAW. MSG_META_EXPOSED lets an object know that it has been marked invalid; either it has to be drawn for the first time, or part or all of it has been exposed (hence the name). The UI controller sends this message to the top object in the Vis hierarchy. MSG_VIS_DRAW, on the other hand, specifically instructs an object to redraw itself. Generally, a Vis object will respond to MSG_META_EXPOSED by sending MSG_VIS_DRAW to itself; it responds to MSG_VIS_DRAW by issuing the appropriate drawing commands, then sending MSG_VIS_DRAW to each of its children.
Graphic Objects
Graphic Objects provide the user with an interface for working with graphics in a manner similar to GeoDraw's. They are useful for programs which allow the users to construct some sort of graphical document or provide a sort of graphical overlay for a spreadsheet.

Graphics Environment: 2 Graphics Goals

The graphics system is in charge of displaying everything in GEOS. Thus, it is vital that the GEOS graphics system be both powerful and easy to use. Features such as outline font support and WYSIWYG printing, usually afterthoughts on other operating systems, have been incorporated into the GEOS kernel. The graphics system was designed to be state of the art and thus had to achieve several goals:

  • Fast Operation
    The GEOS graphics system is heavily optimized for common operations on low-end PCs. To allow a windowing system to run on an 8088 machine, it is vital that line drawing, clipping, and other common graphical operations run very quickly. They do.
  • Device Independence
    Applications are sheltered from the hardware. Coordinate system units are device independent so that all of your drawing commands use real-world measurements. These units are then translated into device coordinates by the kernel.
  • Complete Set of Drawing Primitives
    The graphics system must be able to draw a wide variety of shapes and objects to meet the needs of the UI and applications. It does.
  • Built In Support for Outline Fonts
    GEOS includes outline font technology supporting a variety of font formats. Outline fonts are text typefaces which are defined by their outline shape rather than by a bitmap representation. Outline fonts can be scaled with no apparent loss of smoothness. GEOS also supports bitmap-based fonts; however the very nature of these fonts makes them non-WYSIWYG.
  • Single Imaging Model for Screen and Hardcopy
    The graphics system uses the same high level graphics language for screen imaging as for printing. Because the GEOS system creates both screen and printed images through the same language, the screen can display a document just as it will be printed. Except for differences in resolution, what you see is what you get.

Graphics Environment: 3 Graphics Architecture

The graphics system involves many pieces of GEOS working together to turn an application's UI and graphics commands into drawings on a display.

The graphics portion of the kernel automatically makes any requested transformations to the coordinate space dealing with scaling, rotation, etc. It transforms complicated drawing commands into simpler ones supported by the video drivers.

The video driver can render these simplified commands directly on the device. The video driver checks for collisions, making sure that if whatever is being drawn coincides with the mouse pointer, the mouse pointer will be drawn on top. The video driver does clipping so a geode doesn't draw outside its window.

Commands and information flow in this manner from application to the Video Device, though information also flows in the opposite direction, so that the kernel can get information about the display device's resolution, size, and capabilities.

If the geode is going to draw text, the graphics system makes calls to the font manager. If the requested font isn't already in memory, the font manager loads it. The font manager then makes calls to the individual font driver for information about specific characters.

Graphics Environment: 4 How To Use Graphics

When looking at the source code of sample applications, it's usually not too hard to pick out the commands that do the actual drawing. Commands with names like GrDraw...() generally are self-explanatory. It's not so easy to pick out the commands that set up an area in which the drawing will take place. Part of the problem is that there are many ways to display graphics; each is well suited for different tasks. This section of the chapter provides some practical knowledge about the various ways to display graphics and which situations are appropriate for each.

When possible, the best way to learn how to perform a graphics action is to look at code which performs a similar action. The sample program presented in the Hello World chapter shows a simple graphics environment sufficient for many geodes. If you only need to change what is being displayed (as opposed to how it is displayed), you can work straight from the example, drawing different shapes using commands found in the Drawing Shapes chapter. Most basic graphics techniques are used in one sample program or another. By combining and adapting code from the sample programs, you can take care of most simple graphics needs.

If you can't find a sample geode to work from, there are several points to consider when deciding what sort of graphics environment to set up.

  • Sometimes the only graphics commands in a geode will be those used to define that geode's program icon. This is a common enough case that instructions for setting up your geode's icon are in the program topics section, .
  • Will existing generic UI gadgetry be sufficient for everything you want to display? If you're writing a utility, it might be. If you're writing an arcade game, it probably won't be.
  • If your geode will be displaying graphics, will the user ever interact directly with the graphics? A graphing program might draw a graph based on data the user types in. Such a program could draw the graph but might not actually allow the user to interact with the graph. On the other hand, an art program will probably expect the user to interact with the graphics directly.

Once you've figured out just what your geode's graphical needs are, you're ready to find out which pieces of graphics machinery are right for you.

For custom graphics that will appear in a view, the content object of the GenView must be prepared to issue graphics commands. A common tactic is to create a subclass of VisContentClass and let an object of this subclass act as the content for a view. The subclass would very likely have a specialized MSG_VIS_DRAW . The Process object is another popular choice for the view's output descriptor. In this case, the process must be prepared to intercept any messages the view is likely to send, with MSG_META_EXPOSED and MSG_VIS_DRAW of the most interest. Whichever object, process or content, is the content of a view can respond to MSG_META_EXPOSED or MSG_VIS_DRAW by calling kernel graphics routines. For more information on how to use these objects, see the Views chapter.

As you learn more advanced graphics concepts you may discover shortcuts. As you get deeper into graphics, you should keep a cardinal rule in mind. Any time the graphics space is obscured and then exposed, the geode must be able to draw correctly, no matter what changes have been made. If your geode only draws on a MSG_VIS_DRAW , it will automatically follow this rule. However, applications using shortcuts must take MSG_VIS_DRAW into account; it may be sent at any time, and what it draws may wipe out what was there before. An arcade game that moves a spaceship by blitting a bitmap may be fast; however, be sure that the spaceship will be drawn to the right place if the game's window is obscured and then exposed. Don't worry if this sounds confusing now, but keep these words in mind as you read on.

Graphics Environment: 5 Coordinate Space

The graphics system uses a rectangular coordinate grid to specify the size and position at which drawing commands will be carried out. This is a logical choice as most display devices use a rectangular grid of pixels. "Smart" devices with built in graphics routines tend to use coordinate grids to set place, size, and sometimes movement. "Dumb" devices, capable only of displaying bitmaps, always have these bitmaps described in terms of pixels on a square grid. The GEOS graphics system expects the geode to use the provided device-independent grid to describe graphics commands. The graphics system will then convert this information to device coordinates: coordinates set up for the specific display device, using a grid of pixels.

Standard Coordinate Space

Coordinate Transformations

Precise Coordinates

Device Coordinates

Larger Document Spaces

Current Position

Graphics Environment: 5.1 Coordinate Space: Standard Coordinate Space

The standard coordinate space is what the application uses to describe where things are to be drawn. It is device independent, based on real world units: typographer's points, about 1/72nd of an inch. The origin of the coordinate system is normally the upper left corner of the document, but this can change with context and with changes made by the kernel or even your geode. The coordinate space normally extends from -16384 to 16383 horizontally and vertically. These constants may be referenced as MIN_COORD and MAX_COORD. Note that if you're going to be printing your document, you should restrict yourself to coordinates between -4096 to +4096 to account for scaling to draw on high resolution printers. It is possible to define a 32-bit coordinate space, which gives more room but costs speed and memory. For details about 32-bit spaces, see Larger Document Spaces .

Whenever you draw something, you must specify the coordinates where that thing will be drawn. The coordinates you pass specify where in the coordinate plane that thing will be drawn; the plane, in turn, may be translated, scaled, and/or rotated from the standard window coordinate system (see Coordinate Transformations ).

The system also maintains a device coordinate system, with a device pixel defined as the unit of the grid. This is the type of coordinate system most programmers are used to, but it is certainly not device independent. The graphics system will do all the worrying about device coordinates so your program doesn't have to. (Note, however, that GrDrawImage() , GrDrawHugeImage() , and GrBrushPolyline() are more device-dependant than most routines; see the Drawing Shapes chapter, bitmap section and polyline section for information on these routines).

Standard GEOS coordinates depart from the device model, taking an approach closer to a pure mathematical Cartesian plane. Programmers used to working with device-based coordinates are especially encouraged to read Device Coordinates to learn about some of the differences.

Graphics Environment: 5.2 Coordinate Space: Coordinate Transformations

GEOS provides routines which "transform" the coordinate space. These commands can shift, magnify, or rotate the coordinate grid, or perform several of these changes at once. These transformations affect structures in the GState, and new transformations can be combined with or replace a GState's current transformation.

These transformations apply to the coordinate space, not to individual objects. As a result, if you apply a 200% scaling factor to a drawing not centered on the origin, not only will its size change, but its position will change as well. If you want these operations to affect an object but not its position, you should translate the coordinates so that the origin is at the desired center of scaling or rotation, apply the scaling or rotation, draw the object at the translated origin, then change the coordinates back.

Since they are stored in the GState, these transformations endure--they do not go away after you've drawn your next object. If you apply a 90 degree rotation, you will continue drawing rotated to 90 degrees until you either rotate to some other angle or use another Graphics State. Transformations are also cumulative. If you rotate your space 30°, then translate it up an inch, the result will be a rotated, translated coordinate space. If you want to nullify your last transformation, apply the opposite transformation.

When applying a new transformation to a space which has already been transformed, the old transformations will affect the new one. Be careful, therefore, of the order of your transformations when combining a translation with any other kind of transformation. If you make your transformations in the wrong order, you may not get what you expected.

Simple Transformations

GrApplyRotation(), GrApplyScale(), GrApplyTranslation(), GrSetDefaultTransform(), GrSetNullTransform(), GrInitDefaultTransform(), GrSaveTransform(), GrRestoreTransform()

If you find yourself using transformations at all, they will probably all be rotations, scalings, and translations. The GEOS graphics system includes commands to apply these kinds of transformations to your coordinate system, taking the form GrApplyTransformation() . These commands work with a transformation data structure associated with the Graphics State, so everything drawn in that Graphics State will be suitably transformed.

GrApplyRotation() rotates the coordinate space, turning it counterclockwise around the origin. All objects drawn after the rotation will appear as if someone had turned their drawing surface to a new angle. With a 90° rotation, a shape centered at (1, 1) would draw as if centered at (1, -1). Anything drawn centered at the origin would not change position but would be drawn with the new orientation.

GrApplyScale() resizes the coordinate space; this is useful for zooming in and out. After applying a scale to double the size of everything in the x and y directions, everything will be drawn twice as big, centered twice as far away from the origin. Applying a negative scale causes objects to be drawn with the scale suggested by the magnitude of the scaling factor but "flipped over" to the other side of the coordinate axes.

GrApplyTranslation() causes the coordinate system to be shifted over. After a translation, everything will be drawn at a new position, with no change in orientation or size.

To undo the effects of a transformation, you can apply the opposite transformation: rotate the other way, translate in the opposite direction, or scale with the inverse factor.

To undo the effects of all prior transformations, return to the default transformation matrix using the GrSetDefaultTransform() command. The routine GrSetNullTransform() sets the Graphics State transformation to the null transform--nullifying not only your transformations, but any the system may have imposed as well. For the most part, you should avoid using the GrSetNullTransform() command and use the GrSetDefaultTransform() instead. You can change the default transformation matrix using GrInitDefaultTransform() , but this is generally a bad idea since the windowing system works with the default transformation, and if a geode begins making capricious changes, this can produce strange images.

There are "push" and "pop" operations for transformations. To keep a record of the GState's current transformation, call GrSaveTransform() . To restore the saved transformation, call GrRestoreTransform() .

Complicated Transformations

GrApplyTransform(), GrSetTransform(), GrGetTransform(), GrTransformByMatrix(), GrUntransformByMatrix()

You may want to make some change to the coordinate system that has no simple correspondence to scaling, rotation, or translation. Perhaps you know some linear algebra and want to use your knowledge to combine several transformation functions into a single transformation (thus improving execution speed). All transformations on the coordinate system are expressed in the form of transformation matrices. A GEOS graphics system transformation consists of a matrix containing 6 variables and 3 constants (see figure, below). The six variables allow for standard coordinate transformations. The constants (0, 0, and 1 respectively) allow these transformation matrices to be composed. For example, multiplying a scaling matrix with a rotation matrix creates a matrix which represents a combined scaling and rotation. The six variable matrix elements are stored in a TransMatrix structure.

The GEOS system uses one matrix to store the Graphics State transformation and one to store the Window transformation. When told to apply a new transformation, the graphics system constructs a matrix to represent the requested transformation change and multiplies this matrix by the old transformation matrix. To combine these matrices, GEOS multiplies them together to get the cross-product

If you know that there's a particular combination of transformations you're going to be using a lot, you can do some math in advance to compute your own transformation matrix, then apply the raw matrix as a transformation using GrApplyTransform() . To replace the GState's current transformation matrix with the matrix of your choice, use GrSetTransform() . To find out the current transformation, call GrGetTransform() .

You could combine these steps to pre-compute a transformation that combined a translation and a scaling:

  1. Call GrApplyTranslation() to apply a translation to the GState's coordinate space.
  2. Call GrApplyScale() to apply the scale factor to the GState's coordinate space. The space is now both translated and scaled.
  3. Call GrGetTransform() to get the current GState transformation (stored in a TransMatrix structure).
  4. To apply this transformation to another GState, use the GrApplyTransform() command.

GrTransformByMatrix() returns the results of transforming a passed coordinate pair using an arbitrary transformation matrix. GrUntransformByMatrix() performs the reverse operation, returning the coordinates which would have transformed to the passed coordinates.

Sometimes you have to be careful about the order in which these transformations are supposed to occur. When multiplying transformation matrices together, the transformation that is applied later is the first in the multiplication pair. You can combine any number of rotations and scalings together without having to worry about order: the resulting matrices will be the same. When combining a translation with any other kind of operation, it makes a difference what order you make the transformations and thus makes a difference based on what order you multiply the matrices.

Graphics Environment: 5.3 Coordinate Space: Precise Coordinates

As has been previously stated, coordinates are normally given in typographer's points. Most graphics commands accept coordinates accurate to the nearest point. This should be more than sufficient for most geodes, but for those specialized programs, more precise drawing is possible.

For simple cases, it is possible to create precise drawings by scaling the drawings. To make drawings accurate to one fourth of a point, for example, scale by 25% and multiply all coordinates accordingly. However, this approach is limited and may result in confusing code.

Another way to make precise drawings is to use the graphics commands which have been specially set up to take more precise coordinates. These commands will not be described in detail here, but keep them in mind when planning ultra-high resolution applications. GrRelMoveTo() , GrDrawRelLineTo() , GrDrawRelCurveTo() , and GrDrawRelArc3PointTo() take WWFixed coordinates, and are thus accurate to a fraction of a point. To draw a precise outline, use these commands to draw the components of the outline. To fill an area, use the precise drawing commands to describe the path forming the outline of the area, then fill the path.

Graphics Environment: 5.4 Coordinate Space: Device Coordinates

Most programmers can work quite well within the document space regardless of how coordinates will correspond to device coordinates. However, some programmers might need to know about the device coordinates as well. The system provides clever algorithms for going from document to device space for all programmers, as well as routines to get device coordinate information from the device driver.

What the System Draws on the Device

Consider a device whose pixels are exactly 1/72nd of an inch, such that no scaling is required to map document units to device units. The relationship of the coordinate systems is illustrated below. Note that a pixel falls between each pair of document units. This is a further demonstration of the concept that document coordinates specify a location in the document coordinate space, not a pixel.

Next consider a device that has a resolution of 108 dpi, which is 1.5 times greater than our default 72 dpi. That is, there are 1.5x1.5 pixels on the device for each square document unit. The basic problem here is that the coordinates that are specified in document space map to non-integer pixels in device space. The graphics system would like the pixels to be half-filled along two edges of the rectangle (see the figure below). Unfortunately, a pixel must be either filled or empty, so the system needs a set of rules to follow in this situation. These rules are

  • If the midpoint of a pixel (i.e., the device coordinate of that pixel) falls inside the area's boundary, that pixel is filled.
  • Conversely, if the midpoint of a pixel falls outside the area's border, the pixel is not filled.
  • If the midpoint of the pixel falls exactly on the border of the area to be filled, the following rule is used:
    Pixels on the left or the top are not filled;
    Pixels on the right or the bottom are filled;
    Pixels in the left-bottom and top-right corners are not filled.

These rules might seem a little odd: Why not just fill all the pixels that would be touched by the area? One of the problems with this approach is that areas that did not overlap in the document space would overlap on the device. Or more specifically, they would overlap only on some devices (depending on the resolution), which is even worse. The rules have the property that adjoining areas in document space will not overlap in any device space.

Our next set of potential problems comes with lines. Lines can be very thin and thus might be invisible on some low-resolution devices. If the graphics system used the rules for filled objects then some thin lines would be only partially drawn on low resolution devices. GEOS uses Bresenham's algorithm for drawing straight thin lines, ensuring that a continuous group of pixels will be turned on for a line (see the figure below). This continuity is insured due to the behavior of the algorithm:

  • If the line is more horizontal then vertical, exactly one pixel will be turned on in each column between the two endpoints.
  • If the line is more vertical than horizontal, exactly one pixel will be turned on in each row.
  • If the line is exactly 45 degrees, exactly one pixel will be turned on in each column and row.

Since ellipses and Bézier curves are drawn as polylines, Bresenham's algorithm will work with them.

Converting Between Doc and Device Coordinates

GrTransform(), GrUntransform(), GrTransformWWFixed(), GrUntransformWWFixed()

Given a coordinate pair, at times it's convenient to know the corresponding device coordinates. Sometimes the reverse is true. Use these functions to convert a coordinate pair to device coordinates or vice versa. GrTransform() takes a coordinate pair and returns device coordinates. GrUntransform() does the reverse. If you want to be able to get a more exact value for these coordinates you can use GrTransformWWFixed() and GrUntransformWWFixed() . These return fixed point values so you can do other math on them before rounding off to get a whole number that the graphics system can use.

To transform points through an arbitrary transformation instead of to device coordinates, use the GrTransformByMatrix() or GrUntransformByMatrix() routines, described previously.

Graphics Environment: 5.5 Coordinate Space: Larger Document Spaces

GrApplyTranslationDWord(), GrTransformDWord(), GrUntransformDWord(), GrTransformDWFixed(), GrUntransformDWFixed()

Some applications may need a graphics space larger than the 19 foot square of the standard coordinate space (10 foot square for printed documents). Some spreadsheets can take up a lot of room, as can wide banners. Geodes that will need a large drawing space can request a 32-bit graphics space. Most applications will be able to get by with standard 16-bit coordinate spaces, and their authors may safely skip this section. Note, however, that the Graphic and Spreadsheet objects use 32-bit graphics spaces for display.

If a geode will use a 32-bit space in a view, it will need a VisContent object with the VCNA_LARGE_DOCUMENT_MODEL attribute.

For the most part, 32-bit documents use the same graphics commands as regular 16-bit documents. No single drawing command can cover more than a standard document area; each has been optimized to draw quickly in 16 bits. To draw to the outlying reaches of your document space, apply a special translation that takes 32-bit coordinates, then use normal drawing routines in your new location.

A model for working with 32-bit graphics spaces commonly used with spreadsheets is to break the graphics space into sections. To draw anything, the geode first uses a translation to get to the proper section, then makes the appropriate graphics calls to draw within that section.

If you wish to display this 32-bit coordinate graphics space to the screen, you'll probably want to do so in a 32-bit content.

The standard graphics commands can only draw to one 16 bit space at a time. You will need to translate the coordinate space to choose which 16-bit portion of the 32-bit space you want to draw to. To do so you need to use some special translation functions. GrApplyTranslationDWord() corresponds to the GrApplyTranslation() function normally used.You can use this routine to make the jumps necessary to access far away portions of the graphics space. Since a coordinate can now be in a much larger area than before, all routines that deal with a point's position have 32-bit equivalents.

GrTransformDWord() and GrUntransformDWord() take the place of GrTransform() and GrUntransform() . GrTransformDWFixed() and GrUntransformDWFixed() take the place of GrTransformWWFixed() and GrUntransformWWFixed() , with two words in the integer part of the number instead of one.

Graphics Environment: 5.6 Coordinate Space: Current Position

GrMoveTo(), GrMoveToWWFixed(), GrRelMoveTo(), GrGetCurPos(), GrGetCurPosWWFixed()

The graphics system supports the notion of a current position, sometimes called a pen position. Note that the pen position metaphor predates "pen computing"; please don't confuse these concepts. The concept behind the pen position is that all graphics commands are executed by a pen that ends up at the last place drawn. So if the last command was to draw a curve ending at (20, 20), the pen will still be there. There are special drawing commands that work with the pen position, so you could then draw a line from the current position to (30, 30). The line would extend from (20, 20) to (30, 30), and the pen would then be at (30, 30).

Calling GrMoveTo() moves the pen position to coordinates of your choice. GrMoveToWWFixed() moves the pen position to coordinates which may be specified with a great deal of precision. GrRelMoveTo() moves the pen to a location relative to its present position. GrRelMoveTo() takes coordinates accurate to a fraction of a point, allowing for very precise placement. Call GrGetCurPos() to get the current position, GrGetCurPosWWFixed() to do so with greater accuracy.

There are some guidelines to follow when figuring out where your pen position is going to end up after a draw operation. If you call the GrDrawLineTo() procedure, the pen will end up at the point drawn to. If you call any draw/fill procedure that takes two points as arguments, the pen position usually ends up at the second point passed; if it does not, the reference entry will note where the pen position ends. When text draws at the current position, the current position is used to place the left side of the text. When done drawing the text, the current position has moved to the right side of the text.

Graphics Environment: 6 Graphics State

The data structure known as the Graphics State, or GState, keeps track of changes your code makes about how it wants to draw things. If your geode changes its transformation, everything you draw from that point will be drawn transformed until you change the transformation again. If your geode sets the line width to two points, all lines to come will be drawn two points wide until the width is set to something else. Many graphics routines ask that you pass a GState along as one of the parameters so they know where and how to draw what you've requested.

GState Contents

Working with GStates

Graphics Environment: 6.1 Graphics State: GState Contents

Sometimes it's convenient to think of the GState as being analogous to the Properties boxes in GeoDraw. The GState keeps track of how the program wants to draw lines, text, and filled areas, just as the Line Properties, Text Properties, and Area Properties boxes keep track of how the GeoDraw user wants to draw these objects.

While handy, this analogy doesn't do the GState justice. The GState keeps track of many things:

  • Mix Mode
    The graphics system allows for different mix modes (sometimes known as copy modes). These drawing modes permit the geode to draw in such ways that it erases instead of drawing, draws using the inverse of whatever it's drawing on, or uses various other modes.
  • Current Position (also known as Pen Position)
    The graphics system keeps track of the position of the last thing drawn. If your geode wants to draw something else at this location, there are drawing commands that will draw at the current position.
  • Area Attributes
    The GState contains information that the graphics system will use when filling areas. This information includes the color and fill pattern to use.
  • Line attributes
    The graphics system uses information stored in the Graphics State to keep track of what color and pattern to use when drawing lines. The GState also keeps track of whether lines should be drawn as dotted, and if so what sort of dot-dash pattern to use. The GState contains the line width. It also contains line join information, which controls how lines will be drawn when they meet at an angle, as at the vertices of a polygon.
  • Text Attributes
    The Graphics State contains the ID of the current font, the font size, and the text style. It also contains a color and pattern to use, information about the font, and some esoteric text-drawing options.
  • Coordinate Space Transformations
    The GState keeps track of the current coordinate space transformation. If there is an associated window, the window's transformation matrix is maintained separately with the window's information.
  • Associated Window
    The GState knows the handle of the window associated with the GState. This is the window that will determine where drawings appear and how they are clipped.
  • Associated GString
    If the application is building a GString, the GState is aware of it. The GState contains a reference to the GString. When the graphics system turns graphics commands into GString elements, these elements will be appended to the referenced GString.
  • Associated Path
    If the application is building a path, the GState contains a pointer to the path along which new path elements are passed, similar to the way GString elements are passed. The GState also keeps track of whether the current path is to be combined with another path or should be used "as is."
  • Clipping Information
    The GState keeps track of clipping information in addition to that maintained by the window.

The function of many of these parts may be fairly intuitive to someone used to working with graphics programs. Some of the others may require additional explanation, especially when it comes to how to work with them.

Graphics Environment: 6.2 Graphics State: Working with GStates

GrCreateState(), GrDestroyState(), GrSaveState(), GrRestoreState()

As has been mentioned, most graphics routines require that a GState handle be provided as an argument. Beginning programmers are often unclear on just where to get the GState to use.

Many drawing routines are called by MSG_VIS_DRAW . This message provides a GState, and all routines in the handlers for this message should use the provided GState. Creating a new GState under these circumstances is unnecessary and wasteful. However, sometimes you will need to create a GState. GrCreateState() creates a GState with the default characteristics. You must specify the window with which the GState will be associated.

Commands which change drawing attributes or the current position change the GState.

GrDestroyState() is used to get rid of a GState, freeing the memory to be used by other things. If GStates are created but not destroyed, eventually they will take too much memory. Normally, for each call to GrCreateState() there is a corresponding GrDestroyState() . MSG_VIS_DRAW handlers don't need to destroy the passed GState. Graphics states are cached so that GrCreateState() and GrDestroyState() don't normally need to call MemAlloc() or MemFree() . When GStates are freed, their space is added to the cache. When the memory manager needs to find space on the heap, it flushes the cache.

A geode is most likely to call GrCreateState() when about to draw a piece of geode-defined UI. Other than that, you'll probably be using GStates provided to you by the system. You might want to create a GState if you wanted to calculate something (perhaps the length, in inches, of a text string) when you had no appropriate GState.

GrSaveState() provides a sort of "push" operation that works with GStates. When you call certain functions, like GrSetAreaColor() , new values will wipe out the values of the old GState. But if you've previously called GrSaveState() , then any time you call GrRestoreState() on your saved state, it will come back and displace the current state. Your application can save a GState to save a commonly used clipping region, which could then be restored by restoring the state instead of repeating all the operations needed to duplicate the region. GrSaveTransform() and GrRestoreTransform() are optimizations of GrSaveState() and GrRestoreState() , but they only preserve the GState's transformation.

Graphics Environment: 7 Working With Bitmaps

GrCreateBitmap(), GrDestroyBitmap(), GrEditBitmap() GrGetPoint(), GrSetBitmapMode(), GrGetBitmapMode(), GrSetBitmapRes(), GrGetBitmapRes(), GrClearBitmap(), GrGetBitmapSize(), GrCompactBitmap(), GrUncompactBitmap()

Bitmaps are useful for describing complicated pictures that don't have to be smooth at all resolutions. For example, coming up with all the lines and shapes necessary to describe a complicated photograph would be time-consuming and a waste of memory. It's much easier to set up a rectangular array of cells and to set a color value for each cell. Bitmaps are often used for defining program icons. GEOS includes a great deal of bitmap support. It has kernel routines to create, modify, and draw bitmaps.

There are three main ways to create a bitmap for an application to use. One often used is to embed the data of a desired bitmap directly into a graphics string. This is the way normally used for defining system icons. The other common way is to call the kernel graphics routine GrCreateBitmap() . Another way to create a bitmap, not used so often, is to manipulate memory directly: The formats used for describing bitmaps are public, and though it would be easier in most cases to work through GrCreateBitmap() , those with specialized needs might want to create their bitmap data structures from scratch.

The GrCreateBitmap() routine creates an offscreen bitmap and returns a Graphics State which can be drawn to; changes to this Graphics State become changes to the offscreen bitmap. For example, calling GrDrawLine() and passing the Graphics State provided with such a bitmap would result in a bitmap depicting a line. To display this bitmap, call the GrFillBitmap(), GrDrawHugeBitmap(), GrDrawImage(), or GrDrawHugeImage() commands in another graphics space (see the Drawing Shapes chapter).

When creating a bitmap, you must make choices about what sort of bitmap you want. Depending on your choices, GEOS will be able to use a variety of optimizations. For instance, it takes much less room to store a monochrome bitmap than a color bitmap the same size.

Whenever creating a bitmap, you must specify its dimensions and what sort of coloring it will use (monochrome, four bit, eight bit, or 24 bit).

You may store a mask with your bitmap. This mask works something like the mask used when drawing a view's mouse pointer. When the bitmap is drawn, blank areas of the bitmap will be drawn as black for those pixels where the mask is turned on. For pixels where the mask is turned off, whatever was underneath the bitmap will be allowed to show through.

By asking for a Complex bitmap, you can specify even more information. Complex bitmaps may include their own palette and may specify their own horizontal and vertical resolution. They are very useful for working with bitmaps that may have been captured on other systems or for working with display devices.

The GrDestroyBitmap() routine destroys some or all of the information associated with a bitmap. You may use this function to free all memory taken up by the bitmap. You may also use this function to free only the GState associated with a bitmap by GrCreateBitmap() , but leave the bitmap's data alone; the BMDestroy argument will determine exactly what is destroyed. This usage comes in handy for those times when a bitmap will not be changing, but will be drawn. Large bitmaps are stored in HugeArrays so they won't take up inordinate amounts of RAM; of course it's always wise to free the memory associated with a bitmap when that bitmap is no longer needed.

If you have freed the GState associated with a huge bitmap using GrDestroyBitmap() but want to make changes to the bitmap, all is not lost. Call GrEditBitmap() to associate a new GState with the bitmap. Be careful, however; the bitmap will not recall anything about the old GState, so you must set up colors, patterns, and other such information again. To update the VM file used to store a bitmap (if any), call GrSetVMFile() .

GrClearBitmap() clears the data from a bitmap.

GrGetPoint() can retrieve information from a bitmap, returning its color value for some location. It works with all sorts of display areas, not just bitmaps. It is mentioned here because of its usefulness for those who wish to be able to exercise effects on their bitmaps.

GrSetBitmapMode() gives you control over how drawing commands will affect the bitmap. If your bitmap has a mask, use this routine to switch between editing the mask and the bitmap itself; the BitmapMode argument will specify what is to be edited.

This routine also gives you control over how monochrome bitmaps should handle color. When you draw something in color to a monochrome bitmap, the system tries to approximate the color using dithering. It turns some pixels on and some pixels off in an attempt to simulate the color's brightness. A crimson area would appear with most pixels black, and thus rather dark. A pink area on the other hand would have mostly white pixels, and thus appear light. GrSetBitmapMode() can change which strategy GEOS will use when deciding which pixels to turn on. If you wish to use "dispersed" dithering, the pixels turned on will be spread out evenly, resulting in a smooth gray. However, some output devices (notably certain printers) have trouble drawing small, widely spaced dots accurately. Using "clustered" dithering causes the system to keep the pixels turned on close together, resulting in pictures reminiscent of newspaper photographs. Thus, devices that prefer a few big dots to lots of little dots will have an easier time with bitmaps so edited.

The bitmap mode information may also include a color transfer table. These tables are normally only used by printer drivers, but your geode is welcome to use them, if you can find a reason. Some models of printer have problems when mixing colors. Several printers have problems trying to mix dark colors, tending to end up with black. The table contains a byte value for each possible value of each color component. When mixing the colors, the graphics system will find the real color value to use in the table. Thus, if the reds in RGB bitmaps are looking somewhat washed out, you might set up a table so that reds would be boosted. RGBT_red[1] might be 16, RGBT_red[2] 23, so that the device would use more than the standard amount of the red component.

GrGetBitmapMode() returns the current bitmap editing mode.

GrSetBitmapRes() works with complex bitmaps, changing their resolution. Because simple bitmaps are assumed to be 72 dpi, their resolution cannot be changed unless they are turned into complex bitmaps. GrGetBitmapRes() returns the resolution associated with a complex bitmap.

GrGetBitmapSize() returns a bitmap's size in points. This function might be useful for quickly determining how much space to set aside when displaying the bitmap. GrGetHugeBitmapSize() retrieves the size of a bitmap stored in a HugeArray .

Use GrCompactBitmap() and GrUncompactBitmap() to compact and uncompact bitmaps. Compacted bitmaps take up less memory; uncompacted bitmaps draw more quickly. Note that the bitmap drawing routines can handle compacted and uncompacted bitmaps. These functions are here to aid programmers who wish more immediate control over their memory usage.

Graphics Environment: 8 Graphics Strings

A GString is a data structure which represents a series of graphics commands. This structure may be stored in a chunk or VM file so that it may be played back later. An application may declare a GString statically or may create one dynamically using standard kernel drawing commands. They are used for describing application icons and printer jobs, among other things.

Storage and Loading

Special Drawing Commands

Declaring a GString Statically

Creating GStrings Dynamically

Drawing and Scanning

Editing GStrings Dynamically

Parsing GStrings

Graphics Environment: 8.1 Graphics Strings: Storage and Loading

GrCreateGString(), GrDestroyGString(), GrLoadGString(), GrEditGString(), GrCopyGString(), GrGetGStringHandle(), GrSetVMFile()

GStrings may reside an a number of types of memory areas. Depending on the GString's storage, you will have to do different things to load it. One common case we have already discussed to some extent is when the GString is part of a visual moniker. In this case, the gstring will be stored in the gstring field of the @visMoniker 's implied structure. In this case the UI will do all loading and drawing of the GString.

The GString data itself consists of a string of byte-length number values. The graphics system knows how to parse these numbers to determine the intended drawing commands. You need not know the details of the format used--there are routines by which you may build and alter GStrings using common kernel graphics routines; however, macros and constants have been set up so that you may work with the data directly.

GString data may be stored in any of the following structures (corresponding to the values of the GStringType enumerated type):

Chunk
This is the storage structure of choice for GStrings which will be used as monikers.
VM Block
Virtual memory is normally used to store GStrings which may grow very large. GStrings residing in virtual memory may be dynamically edited.
Pointer-Referenced Memory
You may refer to GStrings by means of a pointer. However, this will only work for reading operations (i.e. you may not change the GString). This is the ideal way to reference a GString which is statically declared in a code resource.
Stream
Streams are not actually used to store data--they are used to transmit it between threads or devices. If you write a GString to a stream, it is assumed that some other application, perhaps on another device, will be reading the GString.

Note that a GString stored in a Stream, VM block, or in memory referenced only by a pointer is not quite ready to be drawn, only GStrings stored in a chunk may be drawn. Fortunately there is a routine which can load any type of GString into local memory so that it may be drawn.

If you are editing or creating the GString dynamically, it will have a GState associated with it. Any drawing commands made using this GState will be appended to the GString. This GState will not be stored with the GString; it is instead stored with the other GStates. You may destroy the GState when done editing, and hook up a new one if starting some other edit; this will not affect the GString's storage.

To dynamically create an empty GString, call the GrCreateGString() routine. You must decide where you want the GString to be stored--either in a chunk, a VM block, or a stream. If you wish to store the GString in a chunk or VM block, a memory unit of the appropriate type will be allocated for you. This routines will return the GState by which the GString may be edited and the chunk or VM block created.

The GrDestroyGString() routine allows you to free up the GState handle associated with your GString You may also destroy the GString's data if you wish; specify exactly what you want to destroy by means of the GStringKillType argument. In addition, you may destroy another GState. You must pass the global handle of the GString to destroy--this will be the handle returned by GrCreateGString() , GrEditGString() , or GrLoadGString() .

The GrLoadGString() command loads a GString into a global memory block so that it may be drawn. Actually, it doesn't load the entire GString into memory, but does initialize the data structure so that it may be referenced through the global memory handle which the routine returns.

The GrEditGString() command is very much like GrCreateGString() , except that instead of creating a new GString, it allows you to dynamically edit an existing GString. This command loads a VM-based GString into a special data structure. Like GrCreateGString() , it returns a GState to which you may make drawing commands. You may insert or delete drawing commands while in this mode, all using kernel drawing routines. For more information about using this routine, see Editing GStrings Dynamically .

The GrCopyGString() command copies the contents of one GString to another. At first you might think that you could do this by allocating the target GString with GrCreateGString() , then drawing the source GString to the provided GState. However, the GState may only have one GString associated with it, whether that GString is being used as a source or target.

To find the handle of the GString data associated with a GState, call GrGetGStringHandle() . To update the VM file associated with a GString (perhaps after calling VMSave() ), use GrSetVMFile() .

Graphics Environment: 8.2 Graphics Strings: Special Drawing Commands

GrEndGString(), GrNewPage(), GrLabel(), GrComment(), GrNullOp(), GrEscape(), GrSetGStringBounds()

There are certain kernel graphics commands which, though they could be used when drawing to any graphics space, are normally only used when describing GStrings. Most of these commands have no visible effect, but only serve to provide the GString with certain control codes.

The most commonly used of these routines is GrEndGString() . This signals that you are done describing the GString, writing a GR_END_GSTRING to the GString. This routine will let you know if there was an error while creating the GString (if the storage device ran out of space). Its macro equivalent is GSEndString() .

The GrNewPage() routine signals that the GString is about to start describing another page. GStrings are used to describe things to be sent to the printer, and unsurprisingly, this routine is often used in that context. An example of a prototype multi-page printing loop is shown in Multi-Page Printing Loop . Whether or not it is drawing to a GString, GrNewPage() causes the GState to revert to the default. When calling this routine, specify whether or not a form-feed signal should be generated by means of the PageEndCommand argument.

Code Display 23-1 Multi-Page Printing Loop

/* The application this code fragment is taken from stores several pages of
 * graphics in one coordinate space. The pages are arranged each below the other.
 * To display page #X, we would scroll down X page lengths. */
for (curPage=0; curPage < numberOfPages; curPage++) {
    GrSaveState(gstate);
    GrApplyTranslation(gstate, 0,MakeWWFixed(curPage*pageHeight));
    /* ...Draw current page... */
    GrRestoreState(gstate);
    GrNewPage(gstate, PEC_NO_FORM_FEED );}

The GrLabel() routine inserts a special element into the GString. This element does not draw anything. However, these GString labels, as they are called, are often used like labels in code. By using GString drawing routines with a certain option (described below), you may "jump" to this label and start drawing from that point in the GString. Alternately, you could start at some other part of the GString and automatically draw until you encountered that label.

The GrComment() routine inserts an arbitrary-length string of bytes into the GString which the GString interpreter will ignore when drawing the GString. You might use this to store anything from custom drawing commands which only your geode has to be able to understand to a series of blank bytes which could act as a sort of placeholder in memory.

Code Display 23-2 GSComment Example

static const byte MyGString[] = {
    GSComment(20), 'C','o','p','y','l','e','f','t',' ','1','9','9','3',
    ' ','P','K','i','t','t','y',
    GSDrawEllipse(72, 72, 144, 108),
    GSEndString()};

The GrNullOp() routine draws nothing and does nothing other than take up space in the GString (a single-byte element of value GR_NOP). You might use it as a placeholder in the GString.

The GrEscape() writes an escape code with accompanying data to the GString. Most geodes do not use this functionality; you might use it to embed special information in a TransferItemFormat based on GStrings.

Depending on what the system will do with your GString, the bounds of the GString may be used for many purposes. Normally the system determines the bounds of a GString by traversing the whole GString and finding out how much space it needs to draw. The GrSetGStringBounds() allows you to optimize this, setting up a special GString element whose data contains the bounds of the GString. You should call this routine early on in your GString definition so that the system won't have to traverse very much of your GString to discover the special element.

Graphics Environment: 8.3 Graphics Strings: Declaring a GString Statically

For most programmers, the first encounter with GStrings (often, in fact, their first encounter with any sort of graphics mechanism) is with a program icon. Often this program icon consists of one or more GStrings, each of which contains a bitmap. These monikers are often set up using the @visMoniker keyword. This automatically stores the GString to a chunk. For an example of a GString stored this way, see the appSMMoniker GString in GString in Visual Monikers .

Code Display 23-3 GString in Visual Monikers

@start AppResource;
@object GenApplicationClass AppIconApp = {
/* The visual moniker for this application (an icon) is created by selecting the
 * most appropriate moniker from the list below. Each moniker in this list is
 * suitable for a specific display type. The specific UI selects the moniker
 * according to the display type GEOS is running under. A text moniker is also
 * supplied if the specific UI desires a textual moniker. */
 GI_visMoniker = list {
    AppIconTextMoniker, /* simple text moniker */
    appLCMoniker, /* Large Color displays */
    appLMMoniker, /* Large Monochrome displays */
    appSCMoniker, /* Small Color displays */
    appSMMoniker, /* Small Monochrome displays */
    appLCGAMoniker, /* Large CGA displays */
    appSCGAMoniker /* Small CGA displays */ }
}
@visMoniker AppIconTextMoniker = "C AppIcon Sample Application";
@end AppResource
@start SAMPLEAPPICONAREASMMONIKERRESOURCE, data;
@visMoniker appSMMoniker = {
    style = icon;
/* Small monochrome icons use the standard size and 1 bit/pixel gray coloring. */
    size = standard; color = gray1; aspectRatio = normal; cachedSize = 48, 30;
/* The following lines hold GString data: */
  @gstring {
        /* GSFillBitmapAtCP:
         * This macro signals that we want to fill a bitmap. The next few
         * bytes should consist of information about the bitmap; the data
         * after that holds the bitmap data. The GString reader will know
         * when it's done reading the bitmap data based on the size stated
         * in the bitmap's info--it will thus know where to look for the
         * next command (in this case a GSEndString() macro. */
     GSFillBitmapAtCP(186),
        /* Bitmap():
         * This macro is used to write basic information about the bitmap
         * to the GString. In this case, that information consists of:
         * The bitmap is 48x30. It is compressed using PackBits. It is
         * monochrome. */
    Bitmap (48, 30, 0, BMF_MONO),
        /* 0x3f, 0xff, 0xff, ...:
         * These numbers are the bitmap data itself. */
    0x3f, 0xff, 0xff, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0,
    0x60, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x60,
    0x60, 0x00, 0x00, 0x80, 0x00, 0x60, 0x60, 0x00, 0x01, 0xc0, 0x00, 0x60,
    0x60, 0x00, 0x03, 0xe0, 0x00, 0x60, 0x60, 0x00, 0x07, 0xf0, 0x00, 0x60,
    0x60, 0x00, 0x03, 0xf8, 0x03, 0xfe, 0x60, 0x00, 0x01, 0xfc, 0x00, 0xf9,
    0x60, 0x06, 0x04, 0xfe, 0x01, 0xfd, 0x60, 0x38, 0x06, 0x1f, 0x01, 0xfd,
    0x60, 0x40, 0x03, 0x63, 0x81, 0x05, 0x60, 0x80, 0x01, 0xa3, 0xc1, 0x04,
    0x60, 0x40, 0x00, 0xc3, 0x81, 0x04, 0x60, 0x38, 0x00, 0x6f, 0x01, 0x04,
    0x60, 0x07, 0xfe, 0xf6, 0x00, 0x88, 0x60, 0x00, 0x01, 0xfc, 0x01, 0x8c,
    0x60, 0x00, 0x03, 0xfe, 0x03, 0xde, 0x60, 0x00, 0x07, 0xf5, 0x07, 0x77,
    0x60, 0x00, 0x03, 0xe2, 0x8f, 0xaf, 0x60, 0x00, 0x01, 0xc3, 0xdf, 0xdf,
    0x60, 0x00, 0x00, 0x81, 0xff, 0xff, 0x60, 0x00, 0x00, 0x00, 0xff, 0xff,
    0x60, 0x00, 0x00, 0x00, 0x7b, 0xff, 0x60, 0x00, 0x00, 0x00, 0x03, 0xff,
    0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff,
    0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff,
        /* GSEndString():
         * This macro lets the GString interpreter know we're done. */
    GSEndString() } }
@end SAMPLEAPPICONAREASMMONIKERRESOURCE;

Notice that this example uses macros to set up the data for the GString. We could have just written the GString data as a series of numbers, as shown in GString Declared Without Macros , but the macros are usually easier to read. Each macro's name, you will notice, is taken from the corresponding graphics command name. Thus the GSFillBitmap() macro corresponds to the GrFillBitmap() routine. There are no GSGet...() macros; GStrings have no return values or conditional statements, and thus have no use for retrieving this sort of information.

Code Display 23-4 GString Declared Without Macros

const char byte myGString[] = {
    GR_SET_LINE_STYLE,			/* First we set the line style. */
    LS_DASHED,			/* 	We want a dashed line. */
    0, 			/* 	Draw dashes starting with index 0 */
    GR_DRAW_RECT_TO,			/* Next we draw a rectangle from our current
                 * position to... */
    0, 72, 0, 72, 			/*	...the point (72, 72). (We need the zeroes
                 * 	because GStrings are arrays of bytes, but
                 * 	we need a word to describe each
                 * 	coordinate. */
    GR_END_GSTRING
}; /* myGString */

Just as all monikers are not GStrings, not all GStrings need be declared as monikers. See Statically Declared GString for an example of a statically declared GString taken from the Moniker sample application; here the declared GString is eventually used as a moniker, but could just as well have been passed to GrDrawGString() and drawn to an arbitrary graphics space.

Code Display 23-5 Statically Declared GString

static void _near			SetPreDefinedGStringMoniker(void)
{
/*
 * A predefined graphics string that draws a
 * MONIKER_WIDTH by MONIKER_HEIGHT * light-blue rectangle at 0, 0.
 */
static const byte gstring[] = {
    GSSetAreaColorIndex(C_LIGHT_BLUE),
    GSFillRect(0, 0, MONIKER_WIDTH, MONIKER_HEIGHT),
    GSEndString() };
@call CycleMonikerTrigger::MSG_GEN_REPLACE_VIS_MONIKER(
    VUM_NOW, 				/* Update appearance immediately */
    MONIKER_HEIGHT, MONIKER_WIDTH, 				/* Since source is a GString, we need to
                     * pass the height and width of the
                     * GString. */
    sizeof(gstring), 				/* Pass the size of sourceGString. */
     VMDT_GSTRING, 				/* Source is a gstring.... */
    VMST_FPTR, 				/* ...referenced by a far pointer */
    (dword) gstring); 				/* Pointer to gstring */
} /* End of SetPreDefinedGStringMoniker() */

For more examples of statically declared GStrings, see the Moniker, GSTest, and GSTest2 sample applications.

Graphics Environment: 8.4 Graphics Strings: Creating GStrings Dynamically

Sometimes it comes in handy to be able to create GStrings "on the fly." To add elements to a GString, issue normal kernel drawing commands, but use a GState which is associated with the GString.

To create a new, empty GString ready for editing (i.e. with an attached GState), call GrCreateGString() . At this point, you may draw to the GString using normal drawing commands. For an example of creating a GString in this manner, see Creating a GString Dynamically .

Code Display 23-6 Creating a GString Dynamically

#define LABEL_BOX    2
#define LABEL_CIRCLE 3
gstate = GrCreateGString(file, GST_VMEM, &myVMBlock);
GrSetLineColor(gstate, CF_INDEX, C_BLUE, 0, 0);
GrDrawRect(gstate, 0, 0, 620, 500);
GrLabel(gstate, LABEL_BOX);
GrSetAreaColor(gstate, CF_INDEX, C_RED, 0, 0);
GrFillRect(gstate, 10, 130, 610, 490);
GrSetLineWidth(gstate, MakeWWFixed(2));
GrLabel(gstate, LABEL_CIRCLE);
GrSetAreaColor(gstate, CF_INDEX, C_RED, 0, 0);
GrFillEllipse(gstate, 130, 10, 490, 370);
GrDrawEllipse(gstate, 130, 10, 490, 370);
GrEndGString(gstate);
GrDestroyGString(gstate, 0, GSKT_LEAVE_DATA);

Drawing to a GString in this manner is almost exactly like drawing in any other GEOS graphics environment. However, there are some important rules to keep in mind.

  • The GString must end with a GR_END_GSTRING element; when the GString interpreter encounters this element, it knows to stop drawing. When creating a GString dynamically, the normal way to assure this is to call GrEndGString() . (Actually, this rule is not strictly true--when you learn more about drawing GStrings, you will see that it is possible to stop GString drawing based on other cues. However, it's always safest to end the GString with a GR_END_GSTRING in case some other application tries to draw the same GString.)
  • Remember that you are creating a data structure which will be used later. The only commands which will affect the GString's contents are the kernel graphics routines, and only those which actually draw something or change the drawing properties. When creating a GString, it is tempting to include constructions like the following:
if (redFlag)
    {GrSetAreaColor(
        gstate, C_RED, CF_INDEX, 0, 0);}
else  {GrSetAreaColor(
        gstate, C_BLUE,CF_INDEX, 0, 0);}
GrFillRect(gstate, 0, 0, 72, 72);
  • You might be surprised when you found out that the following code fragments both drew rectangles which were the same color:
redFlag = FALSE;
GrDrawGString(			screenGState,
            myGString,
            0, 0, 0, elem);
redFlag = TRUE;
GrDrawGString(			screenGState,
            myGString,
            0, 0, 0, elem);
  • The factor which would determine the color of the drawing in this example would be the value of redFlag when you were creating the GString, not when you were drawing it.
  • Think carefully before making coordinate space transformations in GStrings. If you want to remove all transformation effects, you should always call GrSetDefaultTransform() , instead of GrSetNullTransform() . By using GrSetDefaultTransform() , an application that is including your GString can apply some other type of transformation and make that the default; your application will then appear transformed as intended. However, if you call GrSetNullTransform() , you ignore that default transform and will appear in a strange way.
  • If you use GrInitDefaultTransform() , you should probably bracket its use with calls to GrSaveTransform() and GrRestoreTransform() . This save/restore pair will also save the current default transformation, if there is one. By adding the save and restore, you will be preserving whatever default transform the application including yours has set up.
  • If you think your Graphics String will never show up included in some other application, consider the following: The print-to-file feature creates a graphics string that can be imported into several other applications.
  • If you think you have to use GrSetTransform() , try replacing it with a GrSetDefaultTransform() / GrApplyTransform() pair. This will most likely have the same effect, but will be more palatable to another application using the GString.
  • If you are including some other externally-created Graphics String into your document, you probably want to bracket it with GrSaveState() and GrRestoreState() .
  • If you're creating a multi-page GString which might be printed, make sure that each page is independent. There should be nothing assumed about the GState at the beginning of any page--you should instead assume that each will begin with the default GState. This applies to transformations, drawing properties, the GState's path, and so on.
    Keep in mind that each page should be able to print by itself if extracted from a multi-page document.
    The following pseudocode is an example of a "bad" idea:
for(curPage=0;curPage < numberOfPages; curPage++) {
 /* { draw current page } */
 GrNewPage();
 GrApplyTranslation(0, pageHeight); }
  • The following pseudocode provides a "better" way to do the same thing:
for (curPage=0; curPage < numberOfPages; curPage++) { GrSaveState();
  GrApplyTranslation(0,curPage*pageHeight);
  /* {draw current page} */
 GrRestoreState();
 GrNewPage(); }

Probably the most important piece of advice is to think about how the Graphics String will be used. If it will be used only under certain well-controlled circumstances, the above concerns may not affect you.

Graphics Environment: 8.5 Graphics Strings: Drawing and Scanning

GrDrawGString(), GrDrawGStringAtCP(), GrSetGStringPos(), GrGetGStringBounds(), GrGetGStringBoundsDWord()

There are several ways to use a GString. You've already seen how to use one as the visual moniker for a UI gadget. In that case, the UI is responsible for drawing the moniker. If you are learning about interfacing with printers, you probably know that you pass a GString to a PrintControl object to describe print jobs.

There is also a kernel graphics routine for drawing GStrings directly. The GrDrawGString() command draws a GString, or a part thereof. Remember that the GString must be loaded for drawing; you must call GrLoadGString() if you have not done so already (or if you have destroyed the GString since you last called GrLoadGString() ).

Code Display 23-7 GrDrawGString() in Action

@method StaticContentClass, MSG_VIS_DRAW {
    word 	lElem;
    static const byte gstringData[] = {
        GSSetAreaColorIndex(C_RED), GSFillRect(0, 0, 72, 72),
        GSEndString() };
    @callsuper();
    gstring = GrLoadGString 			(PtrToSegment(gstringData), GST_PTR,
                 PtrToOffset(gstringData));
    GrDrawGString(gstate, gstring, 0, 0, 0, &lElem); }
@method DynamicContentClass, MSG_VIS_DRAW{
    Handle 		gstring;
    VMFileHandle		file;
    char 		fileString[] = ".", 0;
    VMBlockHandle		gstringData;
    GStringElement		lElem;
    @callsuper();
    file = VMOpen(fileString,
           (VMAF_FORCE_READ_WRITE | VMAF_USE_BLOCK_LEVEL_SYNCHRONIZATION),
           VMO_CREATE_TRUNCATE,
           0);
    gstring = GrCreateGString(file, GST_VMEM, &gstringData);
    GrSetAreaColor(gstring, CF_INDEX, C_RED, 0, 0);
    GrFillRect(gstring, 0, 0, 72, 72);
    GrEndGString(gstring);
    GrDestroyGString(gstring, NULL, GSKT_LEAVE_DATA);
    gstring = GrLoadGString(file, GST_VMEM, gstringData);
    GrDrawGString(gstate, gstring, 0, 0, 0, &lElem);
    GrDestroyGString(gstring, NULL, GSKT_DESTROY_DATA);
    FileDelete(fileString);

The GrDrawGString() routine has several arguments. A simple usage of the routine is shown in GrDrawGString() in Action . To take advantage of some of the more powerful features of GrDrawGString() , you should know what the purpose of the arguments.

  • You must provide a GState to draw to. You may wish to call GrSaveState() on the GState before drawing the GString (and call GrRestoreState() afterwards). If you will draw anything else to this GState after the GString, you must call GrDestroyGString() on the GString, and pass this GState's handle as the gstate argument so that GrDestroyGString() can clean up the GState.
  • You must provide a GString to draw. The GString must be properly loaded (probably by means of GrLoadGString() ).
  • You can provide a pair of coordinates at which to draw the GString. The graphics system will translate the coordinate system by these coordinates before carrying out the graphics commands stored in the GString.
  • You can provide a GSControl argument which requests that the system stop drawing the GString when it encounters a certain type of GString element. If the GString interpreter encounters one of these elements, it will immediately stop drawing. The GString will remember where it stopped drawing. If you call GrDrawGString() with that same GString, it will continue drawing where you left off. Note that any time a GString-traversing function such as GrDrawGString() returns, it returns a GSRetType value which makes it clear exactly why it stopped traversing the GString.
  • You must provide a pointer to an empty GStringElement structure. GrDrawGString() will return a value here when it is finished drawing. If the GString has stopped drawing partway through due to a passed GSControl , the returned GStringElement value will tell you what sort of command was responsible for halting drawing. For instance, if you had instructed GrDrawGString() to halt on an `output' element (GrDraw...() or GrFill...() commands), then when GrDrawGString() returns, you would check the value returned to see what sort of output element was present.

Note that those last two arguments aren't very useful except when used in conjunction with some other GString routines which we will get to later.

The GrDrawGStringAtCP() routine functions in much the same way as GrDrawGString() , except that the current pen position will be used in the place of passed coordinate arguments.

The GrSetGStringPos() routine allows you to skip any number of GString elements, or go back to the beginning or end of the GString. You specify whether you want to skip to the beginning, end, or ahead by a few steps; this is specified by the GStringSetPosType argument. This routine is useful both when drawing and editing GStrings. Note that you may also use this routine to jump backwards in a GString, but this only works with VM-based GStrings. The GString must be loaded for drawing or editing, and you will pass in the GString's global handle, as supplied by GrLoadGString() or GrEditGString() .

Code Display 23-8 GrSetGStringPos() In Action

/* The following routine is used to allow creating an `overlay' effect. Normally,
 * multi-page GStrings will contain form feed elements that signal the break
 * between pages. Here we see a section of code which will filter out form feeds by
 * skipping those elements. The result will be a single page in which all the pages
 * of the original GString are drawn on top of each other. */
    for (	gsr = GrDrawGString(gstate, gstring, 0, 0, GSC_NEW_PAGE, gse);
        gsr == GSR_FORM_FEED;
        gsr = GrDrawGString(gstate, gstring, 0, 0, GSC_NEW_PAGE, gse) )
            {GrSetGStringPos(gstring, GSSPT_SKIP_1, 0); }

Because a GString remembers its place when you stop drawing partway through, if you wish to `reset' the GString position, you should use GrSetGStringPos() to set it back to the beginning.

Occasionally you may be curious to know how much space is necessary to draw a GString. The GrGetGStringBounds() routine determines this, returning the coordinates describing the GString's bounding rectangle. If the GString may have very large bounds, you should use the GrGetGStringBoundsDWord() routine instead.

Graphics Environment: 8.6 Graphics Strings: Editing GStrings Dynamically

GrEditGString(), GrDeleteGStringElement()

Applications may find cause to dynamically alter an existing GString. You might create a sort of template GString and want to fill in some parts at a later time. If you will draw several similar GStrings, it might be nice to use a single GString, but change only certain parts before drawing each time.

GrEditGString() allows you to edit an existing GString. It only works with VM-based GStrings. Calling GrEditGString() allocates a new GState and associates it with the GString. Any drawing commands to this GState will be appended to the GString. You may use GrDrawGString() (along with appropriate GSControl values) and GrSetGStringPos() to change position within the GString, allowing you to insert new commands into the middle of the GString.

The GrDeleteGStringElement() routine allows you to delete any number of GString elements. The elements deleted will be taken starting at your position in the GString. This command only works while editing the GString, and you must pass the GString's editing GState handle to this routine.

Graphics Environment: 8.7 Graphics Strings: Parsing GStrings

GrGetGStringElement(), GrParseGString()

For complicated GString operations, you may find the following advanced routines helpful.

GrGetGStringElement() returns the raw data associated with the current GString element. To understand this stream of bytes, you must know what sorts of data are associated with each kind of GString element. For example GrGetGStringElement () might return GR_DRAW_RECT with the following buffer of bytes:

GR_DRAW_RECT, 0x00, 0x48, 0x00, 0x24,
0x00, 0x90, 0x00, 0x84

You must know enough about GString element structures to know that this will draw a rectangle with bounds {72, 36, 144, 108}. To find out this sort of information, examine the GS...() macros, or search gstring.h for macros containing the appropriate GStringElement value.

Code Display 23-9 GrGetGStringElement() In Action

/* Our application allows for a second kind of spline, a B-spline. This spline
 * looks similar to a regular Bézier spline, but is somewhat different and uses
 * a different mathematical formula. When this app creates a GString, it will
 * output Bézier splines in the normal way.
 * When outputting a B-spline to a GString, it outputs the GString element for a
 * regular spline. That way, other applications will be able to draw the GString
 * mostly correctly. However, all B-spline elements will be preceded by a
 * GString comment 'B''s''p''l'.:
 *	GSComment(4), 'B','s','p','l',
 *	GSDrawSpline(...), ...
 * The following snippet of code will be used when this application draws a
 * GString. It will look for the significant comments. When it finds them, it will
 * know that the following GR_DRAW_SPLINE element should actually be treated
 * differently. */
    for (	gsr = GrDrawGString(gstate, gstring, 0, 0, GSC_MISC, gse);
        gsr == GSRT_MISC;
        gsr = GrDrawGString(gstate, gstring, 0, 0, GSC_MISC, gse) )
            {byte canonicalBuffer[] = {GR_COMMENT,'B','s','p','l'};
             byte buffer[20];
             int eSize;
              GrGetGStringElement(gstate, gstring,
                sizeof(buffer),&buffer, &eSize);
             /* First check to see if this is the
              * comment we're looking for: */
            if (strncmp(buffer, canonicalBuffer, 5)) {
                 /* Skip ahead to the GrDrawSpline element */
                GrSetGStringPos(gstring, GSSPT_SKIP_1, 0);
                GrGetGStringElement(gstate, gstring,
                    sizeof(buffer), &buffer, &eSize);
                 /* Draw spline using our routine */
                MyDrawBSpline(gstate, buffer+3,
                    (eSize-3)/sizeof(Point))
                 /* Advance GString so kernel won't draw a
                  * Bézier spline over our B-spline. */
                GrSetGStringPos(gstring, GSSPT_SKIP_1, 0) }
        }

The GrParseGString() command calls a callback routine on all elements of a GString which match some criterion. The routine may save information about the elements, draw to a GState, or something completely different. GrParseGString() takes the following arguments:

  • GString to parse.
  • GState handle. GrParseGString() itself will do nothing with this handle, and passing a NULL handle is permitted. However, this GState will be passed to the callback routine. If your callback routine will draw, it is thus convenient to pass a properly initialized GState to GrParseGString() which the callback routine may then draw to.
  • A record of type GSControl . This will determine which elements will be passed on to the callback routine. If you set GSC_OUTPUT, the callback routine will be called only for those GString elements which draw something. If you set GSC_ONE, the callback routine will be called upon all of the GString elements.
  • Far pointer to the callback routine itself.

The callback routine is passed a pointer to the GString element and the handle of the GState that was passed to GrParseGString() .

Graphics Environment: 9 Graphics Paths

GrBeginPath(), GrEndPath(), GrCloseSubPath(), GrSetStrokePath(), GrGetPathBounds(), GrTestPointInPath(), GrGetPathPoints(), GrGetPathRegion(), GrGetPath(), GrSetPath(), GrGetPathBoundsDWord(), GrTestPath()

A path is a data structure which describes a route across a graphics space. Normally, applications use the path to specify an arbitrary screen area, defining a path that describe's the area's borders. Like a GString, a path is created by calling an appropriate initiation routine followed by a series of drawing commands.

Paths don't have to be continuous. An unconnected path is said to be made up of more than one path element.

Paths can be created by taking the intersection or union of two or more paths. In this case, the paths which are combined to make the resulting path are called sub-paths .

Paths are normally used to describe regions. However, regions are basically bitmaps, whereas paths are described in terms of the standard coordinate space. Thus, while regions don't scale well at all, it is possible to scale a path perfectly, then use the transformed path to more correctly compute the desired region. If this comparison sounds similar to that between bitmap- and outline- based fonts, it should. The "outline" of an outline-based font character is, in fact, a specialized form of path.

Paths are also used to describe clipping regions. It is possible to combine any path with a window's clipping region to further restrict the clipping area. Thus it is possible to clip to an ellipse or to a Bézier curve, or even to use text as a clipping region.

When constructing a path, you should keep in mind what it will be used for. If the path is to be filled using the winding rule, it is important that all edges go the correct direction. The winding rule algorithm assumes that the region is described by edges that go around it counterclockwise. Edges going clockwise describe hollows. The odd/even fill rule will work independently of path direction. See the Drawing Shapes chapter for more information about winding rules.

Both the winding and odd/even fill rules demand closed path elements. If any path elements are not filled, the routine will treat them as if they were, connecting the ends of each open path element with a straight line.

It is also possible to alter an existing path by combining it with another. The new path thus formed preserves both paths. If drawn, both paths will appear. If the path is filled or used as a clipping region, the geode can specify how the regions described by the paths should be combined, whether the intersection or union should be taken. Any number of paths can be combined in this manner.

GrBeginPath() signals that the geode is about to start describing a path. All drawing commands directed at the GState will go into constructing the path until GrEndPath() is called. GrBeginPath() is also the routine used to combine a path with an existing path. Calling GrBeginPath() when already constructing a path signals that further graphics commands should describe a new path to be combined with the existing one. The new path can either replace the existing one or combine to find the intersection or the union.

GrCloseSubPath() closes the current path element, adding a straight line segment from the current position to the starting point if necessary.

GrGetPathBounds() returns the bounding coordinates of a rectangle that completely encloses the current path. GrTestPointInPath() tests whether a passed point falls within the current path. GrGetPathPoints() returns all the points along a path in the order visited. GrGetPathRegion() returns the region defined by the path. GrGetPathBoundsDWord() returns the bounds of the path and works in a 32-bit graphics space. If you just want to know whether or not a given path exists, then call GrTestPath() , passing the GetPathType GPT_CURRENT.

GrSetStrokePath() replaces the current path with the one that would result from "stroking" (drawing) the current path with the current line attributes. For example, if the current line style is a dotted line, the result will most likely be a set of many skinny regions. At this time, stroke paths cannot be used for clipping purposes. However, geodes can still draw and fill these paths.

Paths can be drawn or filled using the GrDrawPath() and GrFillPath() commands. For more information about these routines, see the Drawing Shapes chapter.

GrGetPath() retrieves the handle of a block containing the path's data. You may pass this handle to GrSetPath() and thus copy a path to another GState.

Graphics Environment: 10 Working With Video Drivers

The main benefit of the device independence is that the geode writer can issue graphics commands without worrying about the device. Working with the video driver is left to the graphics system.

Kernel Routines

Direct Calls to the Driver

Graphics Environment: 10.1 Working With Video Drivers: Kernel Routines

GrInvalRect(), GrInvalRectDWord(), GrGrabExclusive(), GrGetExclusive(), GrReleaseExclusive(), GrBitBlt(), GrGetBitmap(), GrGetWinHandle()

Sometimes the geode may want more power over the driver. It can see what device coordinates correspond to a set of standard GEOS coordinates or vice versa.

It is possible to update part of a drawing without exposing the whole window. Calling GrInvalRect() causes a passed rectangular area to be updated; the area outside the rectangle will be unaffected. GrInvalRectDWord() works the same way, but for large coordinate spaces.

Programs can also seize exclusive access to a video driver by calling the GrGrabExclusive() command, which allows only the passed GState to alter what is shown on the screen. This routine is useful for programs such as screen dumps who want to accomplish something concerning the screen without worrying that programs in other threads will change the screen in the meantime. GrReleaseExclusive() ends the exclusive access to the screen so that other GStates can update.

To find out if the video exclusive is presently grabbed, call GrGetExclusive() . This will return the handle of the GState presently in possession of the exclusive, or zero if there is no such GState.

GrBitBlt() is an advanced function used to quickly copy or move data within video memory. Effectively, it can copy or move a rectangular part of the document space to another part of that space--the passed BLTMode will determine whether the area is copied or moved. This might be used in an arcade game or animation program to move simple pictures around the screen very quickly. Pass this function the source and destination rectangles, and whether you are copying or moving the block. After calling GrBitBlt() , the changes have been made to video memory but have not actually been drawn to the screen. Make sure that the affected areas are redrawn by invalidating the appropriate area. If speed is a concern, and if you're using GrBitBlt() it probably is, you'll probably want to restrict the clipping area when the drawing is refreshed.

GrGetBitmap() basically returns a dump of an arbitrary display area. It is an advanced function and should be used with some caution. If asked to dump an area from a GState being displayed to screen, GrGetBitmap() won't check to see if the dumped area is obscured by another window, and so your dump might include a picture of that other window. However, if you're working with some sort of offscreen bitmap, this function provides a way to look at large portions of it a time. Note that to look at smaller areas, you might prefer to use GrGetPoint() , an optimized, easier to use function to find out the color of a pixel.

At some times, it may prove useful to know what window, if any, is associated with a GState. To find out, call GrGetWinHandle() .

Graphics Environment: 10.2 Working With Video Drivers: Direct Calls to the Driver

Rarely, a geode may wish to make direct calls to the video driver. In most cases, anything your code might want the video driver to do can be handled better by going through the appropriate graphics routine.

Graphics Environment: 11 Windowing and Clipping

Windows are the interface between the graphics commands and the GEOS user interface. In this section we will discuss some of the graphical mechanisms associated with windows in GEOS.

Palettes

Clipping

Signalling Updates

Graphics Environment: 11.1 Windowing and Clipping: Palettes

Each window has a color palette associated with it. For more information about manipulating palettes, see the Drawing Shapes chapter. The system will use the palette of whatever window is active. As a result, if two windows have different palettes, when one window is active, the other's colors will be distorted.

Graphics Environment: 11.2 Windowing and Clipping: Clipping

GrSetClipPath(), GrSetClipRect(), GrGetClipRegion(), GrTestRectInMask(), GrSetWinClipRect(), GrGetMaskBounds(), GrGetMaskBoundsDWord(), GrGetWinBounds(), GrGetWinBoundsDWord()

The graphics system provides some routines which allow geodes to work together with the windowing system to control clipping. A window's clipping region, you will recall, is that area of the window that must be redrawn. It corresponds to that area of the graphics space which is visible inside the window and not obscured by another window. The area outside the clipping region is supposed to be that area that doesn't need to be redrawn. Geodes which are certain that part of their graphics space doesn't need to be (or shouldn't be) redrawn can restrict their clipping region to exclude this portion.

Restricting the clipping region can lead to quicker redraws, since the graphics system doesn't have as much to redraw. Arcade games in which most of the action only takes place in one or two areas of the display can restrict their clipping region to speed redraws in the smaller, active areas. Also, the clip region can be used as a sort of stencil.

The clip region is defined by a path which describes the boundaries of the region. A window typically begins with a rectangular clip region. If it is partially obscured by other windows, this rectangle will have "bites" taken out of it. This is done using normal path routines used for combining paths. The clipping path of a newly obscured window is determined by computing its old clipping path with that of the obscuring window.

Since any number of paths may be combined, it is a simple matter for the graphics system to combine a window's clipping path with a geode's path chosen to restrict the clipping region. Thus the geode can define its restricted clipping area without knowing anything about the window's present status.

GrSetClipPath() sets the geode-defined path to use when restricting the window's clip path. GrSetClipRect() is an optimization of GrSetClipPath() to handle the most common case in which the new path element is a rectangle.

GrGetClipRegion() returns the Region data structure corresponding to the current clip path. GrGetMaskBounds() and GrGetMaskBoundsDWord() return the bounds of the current clipping path.To find out whether there is a clip path at all, just call GrTestPath() and pass GPT_CLIP.

GrTestRectInMask() determines whether a rectangular area lies fully, partially, or not at all within the clipping area. This routine is useful for optimizing redraws.

GrSetWinClipRect() and GrSetWinClipPath() set the clipping path associated with the window; you should never have occasion to use it. GrGetWinBounds() and GrGetWinBoundsDWord() return the bounds of the window clipping path. To find out whether there is a window clipping path at all, call GrTestPath() and pass GPT_WIN_CLIP.

Graphics Environment: 11.3 Windowing and Clipping: Signalling Updates

GrBeginUpdate(), GrEndUpdate()

The kernel provides two messages by which the geode may signal that it is updating the contents of a window. When updating a region (as when handling a MSG_META_EXPOSED ), the geode should call GrBeginUpdate() after creating the GState, and call GrEndUpdate() before destroying the GState.

This causes the system to store the GState for future comparisons during clipping region calculation. If you don't call these functions, the clipping region is likely to be wrong for this and other updates. Default system MSG_META_EXPOSED handlers call these routines.

Note that you only need call these routines when performing a requested update; if you are drawing to a window without being asked to do so, you need not call these routines.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Drawing Graphics

This chapter describes the commands used to draw things on a display. the Graphics Environment chapter, explains how to set up the display necessary to use these commands.

You must have read the previous chapter; in particular, you should be familiar with GStates. To use certain commands, you should also be familiar with the GEOS fixed point number formats.

1 Drawing Goals
2 Shapes
2.1 Dots
2.2 Lines
2.3 Rectangles
2.4 Ellipses
2.5 Elliptical Arcs
2.6 Three-Point Arcs
2.7 Rounded Rectangles
2.8 Polylines and Polygons
2.9 Bézier Curves and Splines
2.10 Drawing Bitmaps
2.11 Paths
2.12 Regions
2.13 Text
3 Shape Attributes
3.1 Color
3.2 Patterns and Hatching
3.3 Mix Mode
3.4 Masks
3.5 Line-Specific Attributes

Drawing Graphics: 1 Drawing Goals

The graphics system has a set of graphic commands capable of describing anything that might ever be drawn; at the same time, the command set should not be overwhelming. Common routines must be heavily optimized: if the line or text drawing routines are slow, the system will be slowed by a similar amount. Commands for drawing the various shapes must exist for all the contexts discussed in the previous chapter. In GEOS, you use the same graphics commands whether you are drawing to a display, describing a GString (which can be sent to a printer), drawing to a bitmap, or describing a path. This simplifies graphics programming a great deal.

Drawing Graphics: 2 Shapes

Depending on how much experience you have with the GEOS graphics system, you may have some idea already about what sorts of shapes can be drawn and the ways to draw them. Most of these commands have names like GrDrawShape() or GrFillShape() (e.g. GrDrawRect() , GrFillEllipse() ). Normally, a command of the form GrDrawShape() draws the outline of a shape, while GrFillShape() fills in the interior of the shape. Commands with names like GrSetAttribute() (e.g. GrSetAreaColor() ) change the color, fill pattern, and other attributes of shapes to be drawn. Most of these commands are passed a GState. The drawing commands are also passed coordinates at which to draw.

For many of these commands, there are GString opcodes which represent the command in a GString. Also, the arguments used when drawing these shapes often correspond to instance data specific to the Graphic Object which draws that shape.

Most of these routines work with standard coordinates, measured in typographer's points. For geodes that need to make drawings which are precise to a fraction of a point, four routines have been set up to use WWFixed coordinates, and are thus accurate to a fraction of a point. These routines are GrDrawRelLineTo() , GrRelMoveTo() , GrDrawRelCurveTo() , and GrDrawRelArc3PointTo() . Geodes may use these routines to draw the outline of any conceivable two-dimensional shape. To create a precise, filled shape, use these routines to describe a path and then fill the path.

Dots

Lines

Rectangles

Ellipses

Elliptical Arcs

Three-Point Arcs

Rounded Rectangles

Polylines and Polygons

Bézier Curves and Splines

Drawing Bitmaps

Paths

Regions

Text

Drawing Graphics: 2.1 Shapes: Dots

GrDrawPoint(), GrDrawPointAtCP()

A point is the smallest thing drawable; it will always appear as a single pixel. The point's position is defined by a standard coordinate pair. Points are drawn using line attributes. After drawing a point, the pen position will be at the point's coordinates.

GrDrawPoint() draws a point at the passed coordinate pair. GrDrawPointAtCP() draws a point at the current pen position.

Drawing Graphics: 2.2 Shapes: Lines

GrDrawLine(), GrDrawLineTo(), GrDrawRelLineTo(), GrDrawHLine(), GrDrawHLineTo(), GrDrawVLine(), GrDrawVLineTo()

A line is simply a line segment that connects two points. Lines are drawn using the current line attributes stored with the GState. The new pen position becomes the last passed coordinate pair.

GrDrawLine() draws a line between points. GrDrawLineTo() draws a line from the current pen position to the passed point. GrDrawHLine() draws a horizontal line between two points. These two points share the same y coordinate (thus the line is horizontal). GrDrawHLineTo() draws a horizontal line from the current pen position to a passed x coordinate. GrDrawVLine() draws a vertical line between two points which share a common x coordinate. GrDrawVLineTo() draws a vertical line from the current pen position to a passed y coordinate. The GrDrawH...() and GrDrawV...() routines save space compared to GrDrawLine() or GrDrawLineTo() , since fewer coordinates are necessary to define the line.

GrDrawRelLineTo() draws a line from the current pen position to the point at the specified x and y offset from the starting position. This routine takes very precise coordinates, and is useful for describing paths.

Drawing Graphics: 2.3 Shapes: Rectangles

GrDrawRect(), GrDrawRectTo(), GrFillRect(), GrFillRectTo()

Rectangles are defined by four coordinates which can be thought of as either defining the rectangle's left, top, right, and bottom bounds or as specifying two opposite corners of the rectangle.

GrDrawRect() draws the outline of a rectangle using the passed coordinates. GrDrawRectTo() draws the outline of a rectangle using the current pen position as one of the defining points; the pen position is unchanged by this operation. These functions draw the rectangle outline using the current line attributes. GrFillRect() draws a filled rectangle defined by the passed coordinates. GrFillRectTo() fills a rectangle of which the pen position is one corner. GrFillRect() and GrFillRectTo() use the GState's area attributes. They do not draw a border around the rectangle; if you want a bordered rectangle, call GrFillRect() and follow it with GrDrawRect() . Note that if the order of these operations if reversed, the fill may obscure the draw.

Drawing Graphics: 2.4 Shapes: Ellipses

GrDrawEllipse(), GrFillEllipse()

Ellipses are defined by their bounding rectangles. The pen position becomes the first coordinate pair passed. Circles are ellipses with heights equal to their widths.

GrDrawEllipse() draws the outline of an ellipse using the current line drawing attributes. GrFillEllipse() fills the ellipse's area using the current area attributes.

Drawing Graphics: 2.5 Shapes: Elliptical Arcs

GrDrawArc(), GrFillArc()

An arc is a partial ellipse. An arc is defined in terms of its base ellipse, the angle at which to start drawing the arc, and the angle at which to stop drawing. Angles are counted in degrees counter-clockwise with 0° corresponding to the positive x axis (i.e., "3 o'clock").

GrDrawArc() draws the outline of an elliptical arc, a curved line. It does so using the GState's current line attributes. GrFillArc() fills the arc. There are two ways to fill an arc: you can fill in the wedge described by the arc, or you can fill just the region between the arc and its chord; set the style with an ArcCloseType value.

Drawing Graphics: 2.6 Shapes: Three-Point Arcs

GrDrawArc3Point(), GrDrawArc3PointTo(), GrFillArc3Point(), GrFillArc3PointTo(), GrDrawRelArc3PointTo()

The graphics system allows another way to specify arcs. Given two endpoints and one arbitrary point, there is a unique circular arc which has those endpoints and passes through that arbitrary point. The closer the arbitrary point is to the line connecting the endpoints, the shallower the described arc.

GrDrawArc3Point() draws the three-point arc corresponding to the passed points. The second endpoint passed becomes the new pen position. GrDrawArc3PointTo() draws a three-point arc using the present pen position as one of the endpoints; the other endpoint becomes the new pen position. GrFillArc3Point() fills a three-point arc. GrFillArc3PointTo() fills an arc that has the present pen position as an endpoint.

The GrDrawRelArc3PointTo() routine draws a three-point arc where the pen position is the first point and the other two points are specified as offsets from that position. This routine takes WWFixed coordinates for precision drawing.

One time when programmers might especially want to use three point arcs is in the construction of paths. An important consideration when constructing paths is making sure that the various segments of the path are connected; that is, that they share endpoints. When specifying elliptical arcs, the endpoints are never explicitly defined. Thus, it is ambiguous in some cases whether an arc is supposed to be connected to something else. Because three-point arcs include their endpoints with their definition, there is no such ambiguity.

Drawing Graphics: 2.7 Shapes: Rounded Rectangles

GrDrawRoundRect(), GrDrawRoundRectTo(), GrFillRoundRect(), GrFillRoundRectTo()

Rounded rectangles are defined in terms of their bounding rectangle and the radius of the circle used to compute the rounded corners. The smaller the corner circle is, the sharper the rectangle's corners will be. A larger corner circle results in a more rounded corner with more area taken away.

GrDrawRoundRect() draws the outline of a rounded rectangle with the passed bounding rectangle and corner circle dimensions. The drawing position is set to the first passed coordinate pair. GrDrawRoundRectTo() draws the outline of the rounded rectangle for which the current position is one corner of the bounding rectangle. The current position is unaffected by this operation. GrDrawRoundRect() and GrDrawRoundRectTo() use the current line drawing attributes. GrFillRoundRect() fills a rounded rectangle with passed bounding rectangle and corner radius using the current area attributes. GrFillRoundRectTo() fills a rounded rectangle that has the current position as one corner of the bounding rectangle.

Drawing Graphics: 2.8 Shapes: Polylines and Polygons

GrDrawPolyline(), GrDrawPolygon(), GrFillPolygon(), GrBrushPolyline(), GrTestPointInPolygon()

Polylines and polygons are drawings made up of chains of connected lines. They are defined as lists of points, or corners. After drawing a polyline or polygon, the pen position will be at the last point of the shape.

GrDrawPolyline() draws a polyline using the current line attributes. GrDrawPolygon() draws a polygon using the current line attributes.

To draw a closed figure with GrDrawPolyline() , the first and last point must have the same coordinates. GrDrawPolygon() draws figures as closed automatically; having the same beginning and ending point is unnecessary.

GrFillPolygon() fills the interior of a polygon. It does so using the current area attributes. It also uses a fill rule, describing how the polygon should be filled. The odd/even fill rule decides whether a point is within a polygon by seeing how many times a ray drawn from the point to the exterior of the polygon crosses an edge of that polygon. If the result is odd, the point is considered to be in the interior and is filled. If the result is even, the point is outside the polygon and is left alone. The winding rule works in a similar manner, but whenever the ray crosses an edge, the rule looks at the direction of that edge: if it crosses the ray left to right, an accumulator is incremented; if it crosses the ray right to left, the accumulator is decremented. Those points for which the ray's accumulator is non-zero are considered inside the region.

GrBrushPolyline() is an optimized routine. It provides a fast, simple way to draw thick polylines. Instead of specifying a line width in points, the caller passes the dimensions of a rectangle of pixels. This rectangle will be used as a sort of brush, dragged along the course of the polyline. The result is a polyline drawn very quickly. However, this function is not display-independent, and is therefore not WYSIWYG. Because the rectangle dimensions are specified in pixels instead of points, the polyline will be thicker or thinner depending on the display's resolution. In the system software, this routine provides the "ink" feedback for pen-based systems, where speed is a top priority.

To find out whether a given point falls within a polygon, call GrTestPointInPolygon() .

Drawing Graphics: 2.9 Shapes: Bézier Curves and Splines

GrDrawCurve(), GrDrawCurveTo(), GrDrawSpline(), GrDrawSplineTo(), GrDrawRelCurveTo()

Bézier curves are mathematical constructs which provide a cheap and easy way to define smooth curves in a manner that computers can understand. There are other ways to define curves to computers, but the Bézier was chosen for the GEOS kernel because it is used in many standard font description formats. Splines, as implemented in GEOS, are created by drawing curves in sequence.

Bézier curves are defined in terms of four points. Two of these points are the endpoints, known as anchor points. The other two points are known as control points, one associated with each anchor point. The curve extends from anchor point to anchor point. The line between an anchor point and its control point determines the slope, or derivative, of the curve at the anchor point. The further the control point is from the anchor point, the further the curve wants to go along the straight line before curving off towards the other anchor point. A control point at zero distance from its anchor point won't affect the curve; if both control points are at zero distance from their anchors, the result will be a straight line segment.

GrDrawCurve() draws a Bézier curve. It takes four points as arguments, using the first and last as anchor points and the middle two as control points. GrDrawCurveTo() draws a curve but uses the current pen position as the first anchor point, setting the pen position to the second anchor point after drawing.

It would be possible to draw splines by drawing a number of curves which had common endpoints, but the graphics system provides the GrDrawSpline() routine by which a spline with an arbitrary number of spline segments may be drawn with one call. GrDrawSplineTo() draws a spline with the current position as the first anchor point. The spline drawing routines require the application to set up an array of points. When calling GrDrawSpline() , these points should be in the order: anchor, control, control, anchor, control, control,..., anchor. The total number of points should be equal to 3n+1, where n is equal to the number of spline segments. Since GrDrawSplineTo() uses the current position as the first anchor point, for this function the array should start with the first control point, and there should be 3n points passed.

For most programmers, that's probably enough to know. Those programmers who want to know more and don't mind a bit of math may feel free to continue this section.

A curve is defined in terms of four points. There is a formula to determine the coordinates of all points on a spline in terms of these four points. The formula uses two parameterized cubic equations. These equations determine the x and y coordinates of a point on the curve. By finding the points corresponding to various parameters, it is possible to approximate the spline as closely as necessary. See below for the equations.

Splines may be created by drawing curves which share endpoints. Given an anchor point which two curves of a spline share, if the control point of one lies in the exact opposite direction of the other control point, the resulting spline will be smooth. If the control points are not only in the exact opposite directions but are also the same distance from the anchor point, then not only will the resulting spline be smooth, but its derivative will be smooth as well.

We call smooth splines with smooth derivatives "very smooth," and this condition is analogous to C' continuity in functions. Smooth splines with non-smooth derivatives are called "semi smooth", analogous to G' continuity.

Drawing Graphics: 2.10 Shapes: Drawing Bitmaps

GrDrawBitmap(), GrDrawBitmapAtCP, GrFillBitmap(), GrFillBitmapAtCP(), GrDrawHugeBitmap(), GrDrawHugeBitmapAtCP(), GrDrawImage()

In this section only drawing bitmaps will be discussed. For information on creating and modifying bitmaps, see the Graphics Environment chapter.

GrDrawBitmap() draws a bitmap. This routine is very versatile. It can draw simple and complex bitmaps. It can draw compacted or uncompacted bitmaps, can use a bitmap-specific palette, handles strange resolutions intelligently, and generally does the right thing. If you're working with a large bitmap and want to manage its storage, you may provide a routine to pass in part of the bitmap at a time. If the bitmap is stored in a huge array (true of all bitmaps created with GrCreateBitmap() ) use GrDrawHugeBitmap() instead of GrDrawBitmap() , and it will manage memory for you. GrDrawBitmapAtCP() draws a bitmap at the current position.

If you just want to draw a monochrome bitmap, consider using the GrFillBitmap() command. This routine treats the bitmap like a mask, coloring the "on" pixels with the present area color, and leaving the "off" pixels alone so that whatever is underneath the bitmap can show through. This routine is heavily optimized and very fast. GrFillBitmapAtCP() works the same way, filling the bitmap at the current position.

Use GrDrawHugeBitmap() to draw a bitmap that has been stored in a HugeArray data structure. Remember that any bitmaps created by GrCreateBitmap() are stored in a HugeArray . GrDrawHugeBitmap() will automatically take care of memory management. GrDrawHugeBitmapAtCP() works the same way, drawing the bitmap at the current position. GrFillHugeBitmap() and GrFillHugeBitmapAtCP() fill huge bitmaps.

GrDrawImage() is less adaptable but faster than GrDrawBitmap() . GrDrawImage() has its own kind of scaling which doesn't work in the standard GEOS fashion. This routine ignores the resolutions of both device and bitmap and displays the bitmap so that each pixel of the bitmap corresponds to one pixel of the display. If the coordinate system has been scaled or rotated, GrDrawImage() will ignore the scale and rotation when drawing the bitmap. The bitmap may be magnified, but this is not quite the same as normal scaling: The bitmap's resolution is still ignored, but each pixel of the bitmap will receive a square of pixels on the display. GrDrawHugeImage() draws the image of a bitmap stored in a HugeArray .

The image-drawing routines take an ImageFlags structure which holds a flag to specify whether borders should be drawn between the pixels of the bitmap and a bit size field which specifies the magnification to use.

Drawing Graphics: 2.11 Shapes: Paths

GrDrawPath(), GrFillPath()

GrDrawPath() draws a path using the current line attributes. GrFillPath() fills a path using the current area attributes. GrFillPath() can fill the path using either an odd/even or winding fill rule. If the path is to be filled using the winding fill rule, the path must have been defined so that the segments forming the border of each region go around the region so that the interior of the region is to the left. That is, on convex parts of the border, edges should be in the counterclockwise direction. On concave parts of the border edges should go clockwise. For an illustration of a path following this rule, see the figure above. The fill rule is specified by means of a RegionFillRule value, which may be one of ODD_EVEN or WINDING.

When you define a path by combining two other paths, the result might not be exactly what you would expect.

Drawing Graphics: 2.12 Shapes: Regions

GrGetPathRegion(), GrDrawRegion(), GrDrawRegionAtCP(), GrMoveReg(), GrGetPtrRegBounds(), GrTestPointInReg(), GrTestRectInReg()

Sometimes it's useful to be able to describe an arbitrary area of a display. Regions provide a mechanism for doing so. Regions are ideal for delimiting large, blobby areas which are relatively free of detail. They are used by the system to describe many "filled shapes."

While it is possible to define a region directly, writers familiar with paths may define a path and then call the GrGetPathRegion() routine. To find out how to define a region directly, see below.

GrDrawRegion() draws a region at the passed position using the current area drawing attributes. GrDrawRegionAtCP() draws a region at the current pen position.

GrMoveReg() changes the coordinates stored within a region data structure by the specified x and y offsets.

GrGetPtrRegBounds() returns the coordinates of the passed region's bounding rectangle.

GrTestPointInReg() sees if the passed point falls within a region. GrTestRectInReg() tests to see whether the passed rectangle falls entirely or partially within a region; it returns a value of type TestRectReturnType specifying the degree of overlap. These two functions are very useful when using regions for clipping purposes; if a given point or rectangle is discovered to be outside the clipping region, there's no need to draw the point or rectangle.

Some application writers may wish to define regions directly without describing a path. Regions are described in terms of a rectangular array (thus the similarity to bitmaps). Instead of specifying an on/off value for each pixel, however, regions assume that the region will be fairly undetailed and that the data structure can thus be treated in the manner of a sparse array. Only the cells in which the color value of a row changes are recorded. The tricky part here is keeping in mind that when figuring out whether or not a row is the same as a previous row, the system works its way up from the bottom, so that you should compare each row with the row beneath it to determine whether it needs an entry.

The easiest region to describe is the null region, which is a special case described by a single word with the value EOREGREC (a constant whose name stands for E nd Of REG ion REC ord value). Describing a non-null region requires several numbers.

The first four numbers of the region description give the bounds of the region. (You may encounter certain visual objects which use region structures to describe their bounds. These regions do not contain any bounds numbers. The object's drawing bounds will be used instead.)

Next come one or more series of numbers. Each series describes a row, specifying which pixels of that row are part of the region. The only rows which need to be described are those which are different from the row below. The first number of each row description is the row number, its y coordinate. The last number of each series is a special token, EOREGREC, which lets the kernel know that the next number of the description will be the start of another row. Between the row number and EOREGREC are the column numbers where the pixels toggle on and off. The first number after the row number corresponds to the first column in which the pixel is on; the next number is the first subsequent column in which the pixel is off; and so on.

The figure below shows a simple region, along with the numbers used to define it. Those rows which are the same as the rows beneath them have no entry in this structure. Notice that rows four through seven, being the same as row eight, have no entries.

It is possible to customize a region by taking advantage of a feature of the coordinate system. Graphics routines take word-sized (16-bit) coordinate arguments. Normal coordinates only require 15 bits. When working with regions, the graphics system uses the extra bit to allow for coordinates that are described in terms of "parameters."

When you create a region you can specify coordinates as an offset from a parameter . When the region is initialized, up to four parameters may be defined. Coordinates may then be specified as 13-bit offsets from any one of these four parameters. When drawing the construct, new values may be passed for the parameters. In this way, it is possible to use a single region to describe a variety of shapes, just by changing the parameters.

Coordinates with the values shown below will be interpreted as offsets.

4000h-5FFFh: Offsets from parameter zero, with 5000h corresponding to the parameter exactly.
6000h-7FFFh: Offsets from parameter one, with 7000h corresponding to the parameter exactly.
8000h-9FFFh: Offsets from parameter two, with 9000h corresponding to the parameter exactly.
A000h-BFFFh: Offsets from parameter three, with B000h corresponding to the parameter exactly.

Thus, if a coordinate in a GString were 5000h, and the region were drawn with parameter zero equal to 72, then that drawing would take place at the coordinate 72. The coordinate 4FFFh would be interpreted as 71. Use the following constants to clarify parameterized coordinates:

/* Constants for DrawRegion */
#define PARAM_0 0x5000
#define PARAM_1 0x7000
#define PARAM_2 0x9000
#define PARAM_3 0xb000

Some or all coordinates of a region description may incorporate parameters. Note that the region code doesn't check regions for correctness. If the bounds of a region are set incorrectly, the rows are given out of order, or an incorrect (odd) number of on/off points is given for a row, the results are undefined.

Drawing Graphics: 2.13 Shapes: Text

Programs normally display text with UI gadgetry such as GenText, VisText, and GenGlyph objects. For those times when a geode will display text as part of a graphical display, sometimes it's best to display text using direct calls to the graphics system.

Displaying Text

GrDrawText(), GrDrawTextAtCP(), GrDrawChar(), GrDrawCharAtCP(), GrDrawTextField()

There are several functions that display text. The GrDrawText() routine displays a text string. The passed y position, adjusted by the text mode (see below), determines the vertical position. The passed x position determines where the text will start printing, as normal.

GrDrawText() draws a text string at the desired position using the GState's current text attributes. This text string should contain no carriage returns, line feeds, tabs, or other non-printing characters. GrDrawTextAtCP() draws a text string at the current position. GrDrawChar() and GrDrawCharAtCP() draw a single character, which should not be a non-printing character. GrDrawTextField() draws a field of text--however, this routine is only available in Assembly language.

Special Text Attributes

GrGetTextStyle(), GrSetTextStyle(), GrGetTextMode(), GrSetTextMode(), GrGetTextSpacePad(), GrSetTextSpacePad(), GrGetFont(), GrSetFont(), GrGetTrackKern() GrSetTrackKern(), GrGetFontWeight(), GrSetFontWeight(), GrGetFontWidth(), GrSetFontWidth(), GrGetSuperscriptAttr(), GrSetSuperScriptAttr(), GrGetSubscriptAttr(), GrGetSubscriptAttr()

Applications can display text in a number of ways. Thus the GState has many attributes it keeps track of solely for drawing text.

Text style is a collective set of attributes (bold, italic, etc.) that affects how the text is drawn by the graphics system. GrGetTextStyle() gets the current text style, and GrSetTextStyle() allows a new style to be specified. Styles are expressed as a TextStyle record. Note that some complicated styles which are offered by the text objects are not available here: these styles are available only from the text objects; if you wish to offer these styles without using a text object, you'll have to do the artwork yourself.

Depending on the text mode attribute, text may either be drawn from the bottom of the font box, top of the font box, baseline, or accent line. GrGetTextMode() gets the text mode, returning information about which offset to use when drawing text. GrSetTextMode() allows this information to be reset. The information is stored in a TextMode record. Note that if you will be drawing characters of more than one size or font, and if you want those characters to line up by baseline, you should use GrSetTextMode() to use the TM_DRAW_BASE text mode.

GrSetTextSpacePad()

 sets the special amount used to pad space characters;

GrGetTextSpacePad()
 retrieves the current space padding.

GrGetFont() returns the current font and type size. The font is identified by its FontID ; the default font has the ID DEFAULT_FONT_ID and size DEFAULT_FONT_SIZE ; these are the values which new GStates will start with. GrSetFont() sets a new font to use. The font's point size may be between MIN_POINT_SIZE and MAX_POINT_SIZE .

Track kerning adjusts the space between characters. A negative kerning value means that characters will be drawn closer together. A large negative kerning value can make characters draw overlapped. A positive kerning value causes characters to draw with more space between them. GrGetTrackKern() returns the present track kerning value. GrSetTrackKern() changes this value. The kerning value must be between MIN_TRACK_KERNING and MAX_TRACK_KERNING; values greater than MAX_TRACK_KERNING will be replaced by MAX_TRACK_KERNING, values less that MIN_TRACK_KERNING will result in MIN_TRACK_KERNING being used. The kerning value will be multiplied as a percentage by the font size to get a number of points to use for kerning; if this multiplied value is greater than the BBFixed (byte-byte fixed point) number MAX_KERN_VALUE or less than MIN_KERN_VALUE then it will be adjusted to fall at the end of this range.

A font's weight determines its boldness. For many fonts, there will be only two weights defined: plain and bold. However, some fonts allow for finer control of weight. To find out the current font weight, call GrGetFontWeight() . To use a different font weight, call GrSetFontWeight() . Some useful weight values are stored in the FontWeight enumerated type. The weight should definitely be between FW_MINIMUM and FW_MAXIMUM.

To make characters draw wider or narrower, adjust the font width. Some fonts come in wide or narrow versions. If the font does not support the requested width, GEOS will simulate the width as best it can. The GrGetFontWidth() routine retrieves the current width; GrSetFontWidth() changes it. Some helpful width values are stored in the FontWidth enumerated type. The width should definitely be between FWI_MINIMUM and FWI_MAXIMUM.

Geodes can control how text will be drawn in superscript and subscript styles. The super- and subscript attributes determine how to scale the characters and how far they should be displaced. There are several standard super- and subscript attributes available, including values for footnote numbers, and chemical inferiors. Typesetting enthusiasts who wish to adjust the width of these characters differently than the height (as in some standard super- and sub- script layouts) should work with the font width. Use GrGetSuperscriptAttr() and GrGetSubscriptAttr() to find out what the present values are. Use GrSetSuperscriptAttr() and GrSetSubscriptAttr() to change these values. Each of these routines works with a word sized value: the top byte is a percentage of the font size to offset sub- or superscript characters; the low byte is the percentage of font size to use for the sub- or superscript character 0x0064 (decimal 100) would be full-sized with no displacement.

Accessing Available Fonts

GrEnumFonts(), GrCheckFontAvail(), GrFindNearestPointsize(), GrGetDefFontID(), GrGetFontName()

To find out which fonts are available in the user's environment, use the GrEnumFonts() command. You specify what sorts of fonts you're interested in by setting a number of flags, and the routine will fill a buffer with the available fonts with their FontID s and names.

The FontEnumFlags record determines which fonts will be returned. At least one of the FEF_OUTLINES and FEF_BITMAPS flags must be set to determine whether outline, bitmap, or both kinds of fonts should be returned.Keep in mind that only outline fonts will result in true WYSIWYG printer output. The FEF_ALPHABETIZE flag will force the returned buffer of fonts to appear in lexical order. The FEF_DOWNCASE flag requests that the font names appear all in lowercase.

The FEF_FAMILY flag asks that the search be limited to a font family. To specify what sort of family you want, use the FontFamily enumerated type.

GrEnumFonts() ignores the FEF__STRING flag; other routines will use this flag to find out whether the font is specified by a FontID or its ASCII name.

The other flags narrow the search: if you set the FEF_FIXED_WIDTH flag, then only fixed-width fonts will be returned. If you set the FEF_USEFUL flag, only those fonts marked as "useful" will be returned.

The font information will be returned as an array of FontEnumStruct structures. Each of these structures will contain a FontID and the ASCII name of the associated font.

There may be up to MAX_FONTS available on the system. If you're not prepared to handle an array large enough to hold this many fonts, be sure to pass an argument specifying how large an array is being provided.

To find out if a given font is available in the user's environment, call either GrCheckFontAvailID() or GrCheckFontAvailName() , depending on whether the font is being identified by its FontID or its ASCII name. If programming in assembly language, use the GrCheckFontAvail() routine no matter how you're identifying the font. You may narrow the search by passing the appropriate FontEnumFlags . Make sure that the FEF_STRING bit is clear when searching by ID and set when searching by name.

Some fonts are available only in certain sizes and styles. Bitmap fonts are examples of this. The GrFindNearestPointsize() routine takes a typeface, size, and style set. It returns the closest available size and style (or returns FID_INVALID if the passed font isn't available).

To get the font which the Generic UI will use by default, call GrGetDefFontID() . This also returns the font size to use, useful if you wish to respect the user's choice of type size.

To find out the ASCII name of a string for which you have the FontID , call GrGetFontName() . If the font isn't available, this function will return false ; otherwise it will fill a passed buffer with the name of the font. The passed buffer should be FID_NAME_LEN bytes long.

Text Metrics

GrTextWidth(), GrTextWidthWWFixed(), GrTextWidthWBFixed(), GrCharWidth(), GrFontMetrics(), GrCharMetrics()

From time to time it may become important to know something about the size of a letter to be drawn. The graphics system has several routines for retrieving this information. GrTextWidth() returns the width, in points, of a string. GrTextWidthWWFixed() does the same thing, but returns a more accurate figure, including a fractional part; note that in assembly, this routine is called GrTextWidthWBFixed() . GrCharWidth() returns the width of a single character. Note that the width of a text string will normally be different than the sum of the widths of the component characters. GrTextWidth() takes track kerning and pairwise kerning into account.

GrFontMetrics() returns information pertaining to a font. The meanings of these metrics are listed below:

Height
The height of the "font box." This is how much vertical space should be allocated for a line of text. Note that when the text is drawn, some characters may go beyond these bounds. Though the height is still the vertical distance to set aside for a line of text, applications should be prepared for characters to have a vertical height equal to the maximum adjusted height, discussed below.
Maximum Adjusted Height
The maximum height required to draw the character. Some fonts contain characters that are meant to go beyond the normal vertical boundaries, perhaps extending into the space occupied by text on the next or previous line. This metric is the minimum height to guarantee being able to display such a character.
Above Box
Normally the top of the font box, the uppermost limit of a font, is 3/4 of the font's height above the baseline. For those fonts which do not follow this rule, the Above Box metric is the number of points by which the true font box extends beyond the normal font box.
Below Box
Normally, the bottom of the font box, the bottommost limit of a font, is 1/4 of the font's height below the baseline. For those fonts which do not follow this rule, the Below Box metric is the number of points by which the true font box extends below the normal font box.
Mean
The height of a typical lower case character. This metric is sometimes called the "x height," since this will be the height of a lower case "x."
Descent
How far descending characters (like "y" and "j") extend below the baseline.
Baseline
The vertical position of the bottoms of nondescending characters. The number referred to as the "baseline" is the distance between the baseline and the top of accent marks. This corresponds to the text mode TM_DRAW_BASE.
Accent
How much space to leave for accent marks. This distance is measured from the top of the accent mark area to the ascent height. This corresponds to the text mode TM_DRAW_ACCENT.
Ascent
Height of capital letters (and some tall lower-case letters, such as "f").
Underline Position
The distance from the top of the accent line to the top of the underline.
Underline Thickness
The width of the underline.
Strikethrough Position
The vertical position of the line used for "strikethrough" text.
Average Width
The average character width. This figure is normally computed as a weighted average width of lower-case characters, said average weighted based on the frequency distribution of the characters.
Maximum Width
Width of the font's widest character.
Leading
The height of the vertical gap between lines of text.
Kern Count
The number of specially kerned pairs in the font. Many fonts allow certain pairs of characters to have special spacing, known as "pair kerning." For example many fonts try to squish "To" so that the "T" and "o" are closer together than normal leading would dictate. Do not confuse kerned pairs with ligatures. A kerned pair just has strange spacing (e.g. "To" vs. "T o"); a ligature is a pair of characters which have been combined into one (e.g. "æ").
Driver
The font driver associated with the font, specified by a member of the FontMaker enumerated type.
First Character
The Char value of the first defined, drawable character.
Last Character
The Char value of the last defined, drawable character.
Default Character
The Char value of the drawable character which will be drawn as a substitute for any undrawable characters.

The GrCharMetrics() routine returns useful measurements for a character. For each of a font's characters, you may request the following information:

Width
While not a metric returned by the GrCharMetrics() routine, the character width is very important. It determines how much space to allow for the character. The metrics that follow don't affect how much space to allow; instead, they give the bounds of the area that will be drawn to. As shown, characters are free to draw beyond the area set aside for them by the width, into the space of the previous or next character.
Minimum X
The character's leftmost bound. If this value is positive, it means that the character will have some extra space before it. If the value is negative, the character may infringe on the space of the previous character. This metric is also called the "left side bearing."
Minimum Y
The character's bottommost bound. This is the character's "descent," the amount it hangs below the baseline.
Maximum X
The character's rightmost drawing bound. This value should be greater than the Minimum X. Do not confuse this metric with the character's width.
Maximum Y
The character's topmost bound. This is the distance the character extends above the baseline. It is sometimes called the character's "ascent."

The GrGetTextBounds() routine computes the drawing bounds for a string of text.

Drawing Graphics: 3 Shape Attributes

GrSetAreaAttr(), GrSetLineAttr(), GrSetTextAttr()

The graphics system keeps track of many attributes which affect how graphics will be drawn. By setting attributes in the graphics state, your geode can change the color, fill pattern, and other features that won't affect the shape of what you're drawing but will affect its appearance.

The following sections will explain how each of these attributes may be set individually. Note that if you wish to set all attributes to use when filling areas at once, call GrSetAreaAttr() . Call GrSetLineAttr() to set all attributes to use when drawing lines. Use GrSetTextAttr() to set all text-rendering attributes.

Color

Patterns and Hatching

Mix Mode

Masks

Line-Specific Attributes

Drawing Graphics: 3.1 Shape Attributes: Color

If your geode displays graphics, you can probably make its graphical display more effective by using color. Your geode can have as much control over color as it wants, ranging from saying what colors certain objects should be to choosing which colors should be available.

Most color controllers work with the concept of a palette, a subset of the colors your video device is capable of displaying. Most controllers can only use some of these colors at a time; these colors make up the palette. You can refer to colors by their color index (i.e. their place in the palette). You can also refer to colors by their RGB values.

An RGB value specifies a color by mixing intensities of red, green, and blue. The more of each component color added, the more of that color will show up in the result. If all components are equal, the result will be a shade of gray. If all components are zero, the result will be black. If all components are at the maximum (255), the result will be white.

The data structure used to store color information is known as the ColorQuad , shown in Color Data Structures .

Code Display 24-1 Color Data Structures

typedef struct {
    /* The ColorQuad data structure is used to represents a color. There are
     * many ways to describe a color. The CQ_info field determines how the
     * color is being specified, and the other fields will be interpreted
     * based on CQ_info's value. Colors may be referenced by palette index,
     * RGB value, or grey scale value. */
        /* CQ_redOrIndex
         * If CQ_info is CF_INDEX, then this is the palette index.
         * If CQ_info is CF_RGB, then this is the Red component.
         * If CQ_info is CF_GRAY, then this is the Gray scale */
    byte 	CQ_redOrIndex;
        /* CQ_info:
         * This ColorFlag determines how the other three fields of the
         * ColorQuad will be interpreted. The ColorFlag type is shown
         * below. */
    ColorFlag 	CQ_info;
        /* CQ_green:
         * If CF_RGB, then these fields are the Green and Blue components.
         * Otherwise, these fields are ignored. */
    byte 	CQ_green;
    byte 	CQ_blue;
} ColorQuad;
 typedef dword ColorQuadAsDWord;
    typedef ByteEnum 	ColorFlag;
    CF_INDEX,
        /* Color specified by palette index. The values of the first
         * 16 entries of the system palette are specified by Color values */
    CF_GRAY,
        /* Color specified by gray value; this is like CF_RGB, but the
         * value in CQ_redOrIndex will be used for the Green and Blue
         * fields as well. */
    CF_SAME,
        /* Used with hatch patterns, if this flag is set, hatches will draw
         * using the default color (the one set using GrSetXXXXColor()) */
    CF_RGB
        /* Color Set using RGB values */
/* Sample Colors:
 * To use the system palette's light green:						{ C_LIGHT_GREEN, CF_INDEX, 0, 0}
 * To use the 40th color in the palette:						{ 40, CF_INDEX, 0, 0}
 * To use a custom brown:						{ 150, CF_RGB, 90, 0}
 * To use a 75% Gray (75% *256 = 192):						{ 192, CF_GRAY, 0, 0}
 */

Using Available Colors

GrGetAreaColor(), GrSetAreaColor(), GrGetLineColor(), GrSetLineColor(), GrGetTextColor(), GrSetTextColor()

If a geode is to draw something in color, it first calls a routine to set which color to use, then calls the appropriate drawing routine. The color can be specified in terms of its palette index or its RGB value. The data specifying which color to use is stored in the GState, with separate color fields to keep track of how to draw lines, areas, and text.

GrSetLineColor() changes the color used when drawing lines. The GrSetAreaColor() routine sets the color to be used when filling areas. GrSetTextColor() sets the color to be used when rendering text. GrGetLineColor() , GrGetAreaColor() , and GrGetTextColor() return the color values and flags stored with the GState.

The default system palette includes several colors whose indexes have been set up so they may be referenced by descriptive constant names. The constants are members of the enumerated type Color , the most common shown in the figure above. Thus, instead of having to remember that the index 02 means green, the constant C_GREEN can be passed to the appropriate color setting command.

Other Color values include a 16-shade gray scale (the C_GRAY_...entries), some "unused" entries (the C_UNUSED_... entries), and a number of entries which have been set up to allow you to specify a color by its red, green, and blue components on a zero-to-five scale (the C_R..._G..._B... entries).

Programmers should use care when using these constants in conjunction with palette manipulation, as it is possible to change the RGB color value associated with a palette entry. Since the constant names are associated with palette indexes instead of RGB values, it is possible to change the RGB value of palette entry 02 so that the C_GREEN constant actually refers to, for example, a shade of magenta.

When the Color Isn't in the Palette

GrSetLineColorMap(), GrSetAreaColorMap(), GrSetTextColorMap(), GrGetLineColorMap(), GrGetAreaColorMap(), GrGetTextColorMap(), GrMapColorIndex(), GrMapColorRGB()

Not all users have video devices which can display 256 colors at a time. Even fewer have printers capable of doing so. Therefore, the graphics system makes allowances for drawings which use unavailable colors. Exactly what the system will do with an unavailable color depends on the color mapping mode used. By default, the system will choose the two (or more) closest available colors and dither them together in an attempt to form a mixture which will appear to be the correct color overall when seen from a distance.

GrSetLineColorMap() , GrSetAreaColorMap() , and GrSetTextColorMap() set the color mapping to use when drawing with an unavailable color. GrGetLineColorMap() , GrGetAreaColorMap() , and GrGetTextColorMap() return the current color mapping modes. The geode may draw either in the closest available color, or using a dithering of the closest colors. Often it's a good idea to have text map to solid colors since its detailed nature doesn't go too well with dithering.

When a geode wants to learn more about which colors are available, it can use the GrMapColorIndex() routine to find the RGB value associated with an index. The GrMapColorRGB() routine returns the index and true RGB color of the palette entry that most closely matches the values passed.

Custom Palettes

GrCreatePalette(), GrDestroyPalette(), GrSetPaletteEntry(), GrSetPalette(), GrGetPalette()

If the system default palette does not meet a geode's needs, the geode can change its own palette. Some bitmaps (such as those in GIF) have palettes associated with them, and a geode displaying such bitmaps might wish to display the bitmap in its true colors without settling for the closest defaults. Specialized tasks such as photo processing, anti-aliasing, and color cycle animation tend to depend on the ability to manipulate palettes. A geode can create a custom palette associated with a window, then change the color values of entries in that palette.

Since the palette is associated with a window, there can be as many custom palettes as there are windows. The video driver uses the palette associated with the active window. This means that all the inactive windows (and the background) will also be drawn with the palette of the active window; thus, if you give a window a special palette, that may make inactive windows look different. To avoid this, try to avoid changing palette entries 0-15, as the UI uses these (These are the entries representing C_RED, etc.).

GrCreatePalette() creates a custom palette and associates it with your graphic state's associated window. The custom palette starts with all entries having their original RGB values from the default palette. GrSetPaletteEntry() takes a palette entry and sets it to a new RGB value. GrSetPalette() allows you to specify new values for several palette entries at a time. GrSetPalette() can also set an entry back to its default value. GrDestroyPalette() destroys the custom palette, freeing the memory used for it.

Given a choice of palette entries to change, you might choose one of the C_UNUSED_... entries.

Call GrGetPalette() to get a handle to a memory block containing the palette data. Depending on the GetPalType passed, you will either get the palette information for your current palette or the default palette. This routine returns the handle of a memory block. The memory block contains an array with 256 entries (one for each color), each entry consisting of three bytes. The first byte of each entry is the color's red component, the second is the color's green component, and the third is the color's blue component.

Drawing Graphics: 3.2 Shape Attributes: Patterns and Hatching

GrSetAreaPattern(), GrSetAreaPatternCustom(), GrSetTextPattern(), GrSetTextPatternCustom(), GrGetAreaPattern(), GrGetTextPattern()

Fill patterns allow the application to tile an area with a repeating pattern of bits or lines helpful for suggesting textures. The graphics system supports two types of fill patterns. Bitmap patterns, familiar to most computer users, tile the filled area with a repeated bitmap. Hatch patterns fill the area with a repeated sequence of lines. Hatch patterns are defined in terms of families of parallel lines. Patterns are referenced by a PatternType and an index, stored in a GraphicPattern structure. The pattern types are

PT_SOLID
The lack of a pattern. Fills the area solid. This is the default.
PT_SYSTEM_HATCH
System-defined hatch pattern. These patterns are unchangeable and are available to all geodes.
PT_SYSTEM_BITMAP
System-defined tile bitmap pattern. These patterns are unchangeable and available to all geodes.
PT_USER_HATCH
User-defined hatch pattern. These patterns are available to all geodes.
PT_USER_BITMAP
User-defined tile bitmap pattern. These patterns are available to all geodes.
PT_CUSTOM_HATCH
Application-defined hatch pattern. These patterns are application-specific.
PT_CUSTOM_BITMAP
Application-defined tile bitmap pattern. These patterns are application-specific.

Use GrSetAreaPattern() and GrSetTextPattern() to use patterns defined outside the application (system- and user-defined patterns). To use the system's brick hatch pattern, for example, pass the PatternType PT_SYSTEM_HATCH and the SystemHatch SH_BRICK. To use a user-defined bitmap pattern, pass PatternType PT_USER_BITMAP and the number of the pattern. If you pass an invalid pattern (requesting a user hatch pattern when the user hasn't defined one, for instance), the area or text will be filled solid.

Applications may define their own patterns. Before adding custom patterns to an application, consider whether such an action is really necessary. Remember that the user may define his own patterns. The data associated with any application custom pattern may be at most 16Kbytes.

Hatch patterns are defined in terms of families of lines. The pattern designer specifies a series of families; each family consists of a set of equidistant parallel lines. Thus, by defining one family of lines, one could cover a surface with one set of parallel lines. By asking for two families, the region could be filled with a grid.

For each line family, the application must supply certain information. Your application will work with the HatchLine data type to specify

  • Origin
    Since each line family is drawn as a set of equidistant parallel lines, the origin might seem useless. Who cares where the lines start, since they fill the available space? In fact, the origin will only be meaningful if your hatch pattern contains more than one line family. The origin allows you to draw one line family at an offset from another. In the case of the example presented in the figure above, the third line family needed an origin offset from that of the first two families, since none of the lines of the third family pass through the point used as the origin by the first two families.
  • Delta offset between lines of the family
    Each family has a horizontal and vertical offset. When drawing the pattern, the first line will be drawn starting at the origin. The second line will be drawn at a perpendicular distance equal to the passed y offset. The second line may also be drawn at a parallel offset, but this will only affect dotted lines.
  • Angle at which to draw the lines
    Remember that angles are measured in degrees measured counterclockwise from the positive x axis.
  • Color
    You may draw the lines using the default color (area color if pattern is filling an area; text if rendering text), or you may draw using a specific color.
  • Dashes
    You may draw the lines using a custom dash pattern. See the Line Style attribute, below, to learn how to set up a dash pattern.

Custom bitmap patterns are defined in terms of simple bitmaps. To find out the structure of a bitmap, see the struct's documentation.

To use a custom pattern, call GrSetCustomAreaPattern() or GrSetCustomTextPattern() . Along with the usual information, you must include a pointer to a memory location which marks the beginning of some structures holding the pattern data. The commands and structures are detailed in the reference manual. For an example of some code using a custom hatch pattern, see Hatch Pattern Data .

Code Display 24-2 Hatch Pattern Data

/* This example shows how to implement the pattern illustrated in the figure above. */
    /* ... */
    GrSetPatternCustom(myGState, gp, hexHatchPatt);
    /* ... */
GraphicPattern 	gp = {PT_CUSTOM_HATCH, 0};
 static HatchPattern 	hexHatchPatt = { 3 };					/* Three HatchLine structures
                         * must follow */
static HatchLine line1 = { 			{MakeWWFixed(0) , MakeWWFixed(0)},			/* Origin */
             MakeWWFixed(0),			/* Delta X
                         *  dashes will be in alignment */
            MakeWWFixed(20.7846097),			/* Delta Y
                         *  lines will be 12*sqrt(3)
                         *  apart */
            MakeWWFixed(0),			/* Angle */
            (dword) (CF_SAME<<16),			/* Color
                         *  will use default color */
            1			/* Number of dashes
                         *  one HatchDash pattern
                         *  must follow */
        };
 static HatchDash dash1 = {			{12, 0},			/* On for 12 points */
            {24, 0}};			/* ...and Off for 24 points */
static HatchLine line2 = { 			{MakeWWFixed(0), MakeWWFixed(0)},			/* Origin */
             MakeWWFixed(0),			/* Delta X */
            MakeWWFixed(20.7846097),			/* Delta Y*/
            {120,0},			/* Angle */
            (dword) (CF_SAME<<16),			/* Color */
            1			/* Number of dashes */
        };
static HatchDash dash 2 = {			{12, 0},			/* On for 12 points */
            {24, 0}};			/* ...and Off for 24 points */
static HatchLine line3 = { 			{{12,0} , MakeWWFixed(0)},			/* Origin */
                         *  this line family will be at
                         *  a 12 pt. horizontal offset
                         *  from the other two families.
             MakeWWFixed(0),			/* Delta X */
            MakeWWFixed(20.7846097),			/* Delta Y*/
            {60,0},			/* Angle */
            (dword) (CF_SAME<<16),			/* Color */
            1			/* Number of dashes */
        };
static HatchDash dash3 = {			{12, 0},			/* On for 12 points */
            {24, 0}};			/* ...and Off for 24 points */

To find out the current area or text pattern, call GrGetAreaPattern() or GrGetTextPattern() .

Drawing Graphics: 3.3 Shape Attributes: Mix Mode

GrGetMixMode(), GrSetMixMode()

The kernel supports several mix modes. These modes control what will happen when something is drawn on top of something else. Normally when this occurs the new drawing covers up the old one. This is known as MM_COPY, since the new drawing is copied directly onto the graphics space.

There are many other mix modes available.

MM_COPY
MM_COPY is the most common mix mode as well as the default. It draws the new drawing on top of the old as though the new drawing where on opaque paper--nothing of the old drawing shows through the region defined by the new drawing.
MM_CLEAR
The region defined by the new drawing is blanked out. The color of the new drawing does not matter.
MM_SET
The region defined by the new drawing is washed black. The color of the new drawing does not matter.
MM_INVERT
The region defined by the new drawing is inverted. The color value displayed will be the logical inverse of the old color. The color of the new drawing does not matter.
MM_NOP
The old drawing remains; nothing is drawn of the new drawing. Just about the only thing likely to change is that the pen position will be updated.
MM_XOR
The color value to display is calculated by taking the bitwise XOR of the new and old colors. If painting in white, this acts as a sort of reverse paint. Note that this bitwise XOR is applied to the index numbers of the colors, not to their RGB values.
MM_OR
The color value to display is calculated by taking the bitwise OR of the new and old colors. On a monochrome display, this corresponds to drawing on a clear plastic overlay. The OR operation is applied to the indexes of the colors as opposed to their RGB values.
MM_AND
The color value to display is calculated by taking the bitwise AND of the new and old colors. Where either drawing is blank, the result will be blank. On a monochrome display, this results in a "clipping" effect. The AND operation is applied to the indexes of the colors.

GrGetMixMode() returns the current drawing mode. GrSetMixMode() tells the system to use a new mode.

Note that the drawing modes that use logical operators to compute the color have some nice effects when used with the default system colors. Due to the values of the indexes of the gray colors, MM_OR and MM_AND used with the gray colors can be thought of as lightening and darkening operations. For instance, AND-ing C_LIGHT_GRAY together with C_LIGHT_RED color results in the darker C_RED.

When using drawing modes, it often pays to be aware of where a drawing command is drawing something as blank as opposed to where it isn't drawing at all. Filling a rectangular area of the screen with a black and white checkerboard pattern isn't exactly like drawing only the corresponding black squares. When using MM_COPY, the white areas of the checkerboard will overwrite whatever was underneath. When drawing a checkerboard with individual black squares, the background would show through no matter what background because there's nothing drawn between the squares.

Drawing Graphics: 3.4 Shape Attributes: Masks

GrSetAreaMaskSys(), GrSetAreaMaskCustom(), GrGetAreaMask(), GrSetLineMaskSys(), GrSetLineMaskCustom(), GrGetLineMask(), GrSetTextMaskSys(), GrSetTextMaskCustom(), GrGetTextMask()

Masks are the pixellated equivalent of hatch patterns. Instead of specifying a set of dashed lines, the program uses an 8x8 bitmap array which will be tiled. The graphics system provides a number of standard mask patterns, including 65 percentage masks (for achieving, e.g., a 50% fill) and several shape patterns. It's simple to ask that the inverse of a system pattern be used instead of the pattern itself. The program may also specify and use custom draw masks by setting an 8 byte array.

The GrSetAreaMaskSys() routine specifies one of the standard draw masks to use when filling areas. GrSetAreaMaskCustom() allows the program to specify a custom mask by passing a pointer to an 8 byte. Each byte of the buffer represents one row of the mask; the rows are ordered from top to bottom. GrGetAreaMask() returns the current area mask.

There are similar routines which set the drawing mask to use when drawing lines: GrSetLineMaskSys() , GrSetLineMaskCustom() , and GrGetLineMask() . To work with the drawing mask used when rendering text, use GrSetTextMaskSys() , GrSetTextMaskCustom() , and GrGetTextMask() .

For the most part, it's probably not a good idea to use masks when drawing text, since the pixelated nature of the mask is likely to make the text less legible. However, when drawing text in very large point sizes, draw masks are probably fine.

Since several of the masks are associated with a fill percentage, some programmers may confuse masks with gray-scale fills. Grays should be implemented with colors, by drawing in a color with equal red, green, and blue values.

When you are building a document image, do not use draw masks just to achieve a lighter color. That is, if you are not making use of the "mask" property of draw masks (i.e., you don't care if you can see through it), then you should just specify a lighter color and let the kernel do the dithering. This is important especially because drawing masks in PostScript takes much longer than drawing a bitmap of the same size.

When constructing or interpreting system draw mask values, first remember that if the SDM_INVERSE flag is set, then the inverse of the mask will be used instead of the mask itself. The remaining bits should be a SystemDrawMask value, a value between zero and SDM_0. Draw mask values between zero and 24 are various graphic draw mask patterns. Values between 25 and 89 are percentage fill masks, 89 corresponding to the empty fill and 25 to a 100% fill. Several SDM_... constants have been set up with the more commonly used system draw patterns. For a list of constants, see the Routines manual.

Drawing Graphics: 3.5 Shape Attributes: Line-Specific Attributes

GrGetLineWidth(), GrSetLineWidth(), GrGetLineJoin(), GrSetLineJoin(), GrGetLineEnd(), GrSetLineEnd(), GrSetMiterLimit(), GrGetLineStyle(), GrSetLineStyle(), GrSetLineAttr()

Line attributes determine characteristics of lines drawn with the GrDraw...() routines. They do not affect anything drawn with the GrFill...() routines.

You may work with the width used when drawing lines by means of the GrGetLineWidth() and GrSetLineWidth() routines.

The graphics system makes it easy to draw dotted lines. The line style attribute controls the "dottedness" with which lines should be drawn; it may be changed or retrieved with the GrSetLineStyle() and GrGetLineStyle() routines. Lines can be drawn using any of a number of standard system LineStyle s or by defining a custom dot pattern.

Custom dot patterns are defined in terms of arrays of pairs of bytes. The first byte of each pair gives the distance that the line should be drawn with the pen down, the second number the distance with the pen up. The array as a whole is stored in a DashPairArray . Dash lengths will scale with the line width.

Line joins govern the behavior of angles and corners. Using the appropriate line join style, a geode can specify that angles should be blunt, pointed, or rounded. GrGetLineJoin() returns the current line join drawing technique, while GrSetLineJoin() sets a new line join to use. The join is specified by a member of the LineJoin enumerated type. The miter limit governs the maximum length of mitererd joins; use GrSetMiterLimit() and GrGetMiterLimit() to work with this value.

The GrSetLineEnd() and GrGetLineEnd() routines set and retrieve the style with which the graphics system will draw the ends of line segments. The ending style is one of the LineEnd values. It may be round, square and even with the end of the line, or square and extending past the mathematical end of the line.

To set all of the line-drawing attributes at once, call GrSetLineAttr() .

This document is a single-page version of a a multi-page document, suitable for easy printing.


Hello World

This chapter provides you with an application shell to which you can add as you learn more about GEOS programming. It describes each portion of the introductory (and compulsory) "Hello World" sample program. All programmers new to GEOS should read this chapter for a firm understanding of the basics of a GEOS application. The source code for Hello World is given in this chapter in pieces. It is also available as the Hello3 sample application.

1 Application Structure
2 Hello World
2.1 Features of Hello World
2.2 Strategy and Internals
2.3 Naming Conventions
3 Geode Parameters File
4 The Source File and Source Code
4.1 Inclusions and Global Variables
4.2 The Process Object
4.3 UI Objects
4.4 Code and Message Handlers
5 Exercises and Suggestions

Hello World: 1 Application Structure

Every GEOS application and library will have at least two files: The Geode Parameters file provides the Glue linker with necessary information about the geode being compiled and linked. The source file contains the source code. Source code files may be split up into modules of source and definition files--this allows the programmer to keep large applications in a series of smaller files for easy organization and easier debugging.

Essentially there are four different types of files your application may use:

  • Parameters File ( .gp )
    The Parameters file, as stated above, provides the Glue linker with necessary information. It details specifics about the geode being compiled that will be necessary for dynamically linking at runtime. Each geode may have only one Parameters file. The Parameters file for Hello World is detailed in Geode Parameters File . Full reference information for this file type can be found in the Routines reference book.
  • Source File ( .goc )
    The Source file contains a combination of GEOS UI code and standard C code. Many source code files can be used for a single geode; this can help you organize your application's functionality into manageable groups of routines and objects. An introduction to the basics of the source file is given in The Source File and Source Code .
  • C Header File ( .h )
    C Header files may be used to hold definitions of data structures, classes, macros, routines, and other items. There are several standard GEOS header files you must include in your geode for it to compile properly. These are outlined in The Source File and Source Code .
  • GEOS Header File ( .goh )
    This file is essentially the same as the C header files described above. It can contain class and routine definitions as well as constants and variables. The primary difference between these files and the C header files is that .goh files must be included before the geode is run through the Goc preprocessor. C header files do not have to go through the Goc preprocessor. Simple geodes might have none of their own header files. These files are also described in The Source File and Source Code .

Hello World: 2 Hello World

The Hello World sample application (Hello3) is very simple and yet accomplishes a great deal. With just a few simple steps, this program does the following:

  • Creates a complete primary window
  • Creates a scrollable window and scroller objects
  • Draws 48-point text into the scrollable window and redraws the text when the user changes a parameter
  • Handles scrolling and window resizes
  • Uses a pinnable menu to bring up a dialog box
  • Uses a dialog box to allow the user to change colors of the text
  • Uses keyboard mnemonics for both the menu and dialog box commands

The Hello World program uses some concepts and constructs with which you may not be familiar yet. This section will describe each of those constructs and will refer to other sections of the documentation for complete information.

Features of Hello World

Strategy and Internals

Naming Conventions

Hello World: 2.1 Hello World: Features of Hello World

To the user, the Hello World program is very simple. It consists of a primary window with a single menu and a scrollable window within the primary. In the scrollable window is a phrase of text: "Welcome to GEOS!"

The menu has a single item, "Color," which brings up a dialog box with two buttons in it. One button is labeled "Blue" and turns the text blue, and the other is labeled "Gold" and turns the text yellow. The menu is pinnable, like all GEOS menus, and the dialog box may be moved around the screen and closed like any other independently-displayable dialog box.

Hello World: 2.2 Hello World: Strategy and Internals

The code for Hello World, as you will see, is quite simple. It consists mainly of User Interface gadgetry and uses just a few message handlers.

The main component of the application is the Process object, an instance of HelloProcessClass (a subclass of GenProcessClass ). This object makes all drawing and color changes by handling messages sent from the window manager and the triggers in the dialog box. The Process object basically manages the application, keeping track of the relevant data and interacting with the UI gadgetry.

The Process object is event-driven, meaning it has no main() routine run when the program is launched. Instead, the program does nothing until an event occurs (such as the view window sending MSG_META_EXPOSED when first opened). When the event (message) is received, the Process object responds to it and then waits until the next event occurs. Note, however, that the vast majority of events a Process object will receive and handle are not generated by your code but by the kernel and the UI.

The Menu and Dialog Box

Both the menu and the dialog box, once defined in the source code as objects, are implemented automatically by the system software. The application does not have to draw or otherwise manage these objects; they will interact directly with the UI to do the proper thing.

The triggers within the dialog, however, request actions that must be carried out by the application (changing the color to blue and gold, respectively). Although the application does not have to instantiate, draw, or otherwise modify the trigger objects, it must handle messages sent out by them when they are pressed by the user. This is discussed below, under "Changing the Text Color."

The Scrolling View and Drawing the Text

Almost everything is handled automatically by the User Interface for the Hello World application. This includes implementation of the system menus for the primary window and the scrolling functionality of the scrollable View window.

The view object (of GenViewClass ) is powerful and provides a lot of what most applications need. It automatically handles all window resizes and scrolls, and it will cause proper redrawing when another object (such as a pinned menu) is moved across it. The only thing it does not do is actually draw the application's images.

When the view senses that some portion of its window has become invalid (through scrolling or when the view window is first opened, for example), it will send a MSG_META_EXPOSED message to the Hello World application's Process object. The Process object will respond by drawing the text appropriately--it does not, however, have to worry about what portion of the text is visible or what portion of the screen the view window occupies. The view will automatically clip the text properly and display it within the window's bounds.

Changing the Text Color

In all, the Process object can handle six events specific to this application: MSG_META_CONTENT_VIEW_WIN_OPENED (sent by the view when it first creates its window), MSG_META_CONTENT_VIEW_WIN_CLOSED (sent by the view when its window is being destroyed), MSG_META_EXPOSED (described above), MSG_HELLO_CHANGE_TO_BLUE (sent by the Blue trigger), MSG_HELLO_CHANGE_TO_GOLD (sent by the Gold trigger), and MSG_HELLO_REDRAW_DOCUMENT (sent by the Process object to itself).

The Process object maintains two global variables: helloTextColor contains the current color of the text, and winHan contains the window handle of the view's window. When it draws the text, the Process object checks helloTextColor before drawing. Therefore, the handlers for the change-color messages change the value of helloTextColor .

However, changing the text's color is not quite that easy. Because the view window does not have any way of knowing that the Process object has changed the text, the application must inform the UI of the change. Otherwise, the change will be made in the document but will not appear on the screen until the view window is moved or resized by the user.

Therefore, we must force a window invalidation when we change the color. This will cause the View window to generate a new MSG_META_EXPOSED that will force the redrawing of the text in the new color. The window handle is cached in winHan for just this purpose; when the text color changes, we must invalidate the window so the UI will redraw its contents. We invalidate the window by calling the special graphics routine GrInvalRect() , passing the window's handle. We get the window handle when the view first creates the window--it will send out a MSG_META_CONTENT_VIEW_WIN_OPENED . When the window closes, the view will send MSG_META_CONTENT_VIEW_WIN_CLOSED in which Hello World destroys the cached window handle.

Hello World: 2.3 Hello World: Naming Conventions

You might have noticed some of the names of variables, messages, and routines and seen a pattern of naming. Geoworks has developed a few naming conventions for various different items in the system. You don't have to follow these conventions, of course; it may help, however, if you keep to our conventions until you're familiar with most of the system. Some of the items that have special naming conventions are

Variables
Typically, variables begin with a lower case letter and have each subword in the name capitalized. The variable helloTextColor is a good example. Variables with just one word in the name are all lower case.
Classes
Class names should always have the suffix Class . Typically, the first portion of a class' name will describe the particular application, library, or module it is associated with. The second portion of the name should reflect the class' superclass. Each portion of the class name should be capitalized. Thus, HelloProcessClass conforms because it begins with the application's name (Hello), then has the superclass' name (Process), and finally has the suffix Class.
Constants
Constants are typically all upper case with underscores to delineate the portions of the name. C_BLUE is a good example; it is a set value that does not change. The structure of the name should reflect the use of the constant. (In this case, the enumerated type Color is reflected in the C_ portion of the constant's name.)
Routines
Kernel and UI routines, as well as method names, should have each portion capitalized. They should begin with some abbreviation relating to the module they belong to. For example, GrCreateState() is a graphics system routine, as is GrInvalRect() .
Messages
Message names should be all uppercase and begin with MSG_. Each portion of the name should be separated with underscores, and the first portion after MSG_ should reflect the class in which the message is defined. For example, MSG_HELLO_CHANGE_TO_BLUE follows all these conventions.
Objects
Object names are typically capitalized in the same manner as routines and classes. The object's name should reflect both the object's module and its class (or its function if not its class). HelloView is a good example.
Instance Data
Instance data fields typically have as their first portion an all-caps acronym for the class name with an I tacked on for "instance." The second portion is like a variable name, and the two portions are separated by an underscore. For example, GI_visMoniker is a field of GenClass (hence the GI_ ), and visMoniker is the variable name of the field.

Hello World: 3 Geode Parameters File

The Hello World Parameters File shows the Geode Parameters ( hello3.gp ) file for the Hello World sample application. Each of the components of the parameters file is described in detail in the C Reference book.

Code Display 2-1 The Hello World Parameters File

##############################################################################
#
#	     Copyright (c) GeoWorks 1991, 1993-- All Rights Reserved
#
# PROJECT:		GEOS V2.0
# MODULE:		Hello World (Sample GEOS application)
# FILE:		hello3.gp (Hello World Application Geode Parameters File)
#
# DESCRIPTION:		This file contains Geode definitions for the "Hello World" sample
#		application. This file is read by the Glue linker to
#		build this application.
#
##############################################################################
#
#	Permanent name: This is required by Glue to set the permanent name
#	and extension of the geode. The permanent name of a library is what
#	goes in the imported library table of a client geode (along with the
#	protocol number). It is also what Swat uses to name the patient.
#
name hello3.app
#
#	Long filename: This name can be displayed by GeoManager.
#
longname "Hello World"
#
#	Specify geode type: This geode is an application, will have its own
#	process (thread), and is not multi-launchable.
#
type	appl, process, single
#
#	Specify the class name of the application Process object: Messages
#	sent to the application's Process object will be handled by
#	HelloProcessClass, which is defined in hello3.goc.
#
class	HelloProcessClass
#
#	Specify the application object: This is the object in the
#	application's generic UI tree which serves as the top-level
#	UI object for the application. See hello3.goc.
#
appobj	HelloApp
#
#	Token: This four-letter name is used by GeoManager to locate the
#	icon for this application in the token database. The tokenid
#	number corresponds to the manufacturer ID of the program's author
#	for uniqueness of the token. Eight is Geoworks' manufacturer ID for
# 	sample applications.
#
tokenchars "HELO"
tokenid 8
#
#	stack: This field designates the number of bytes to set aside for
#	the process' stack. (The type of the geode must be process, above.)
#	The default stack size is 2000 bytes.
#
stack 1500
#
# Heapspace: This is roughly the non-discardable memory usage (in words)
# of the application and any transient libraries that it depends on,
# plus an additional amount for thread activity. To find the heapspace
# for an application, use the Swat "heapspace" command.
#
heapspace 3644

#
#	Resources: List all resource blocks which are used by the application.
#	(Standard discardable code resources do not need to be mentioned.)
#
resource APPRESOURCE ui-object
resource INTERFACE ui-object
resource MENURESOURCE ui-object
#
#	Libraries: List which libraries are used by the application.
#
library	geos
library	ui
#
#	User Notes: This field allows the geode to fill its usernotes field
#	(available to the user through GeoManager's File/Get Info function)
#	with meaningful text.
#
usernotes "Sample application for GEOS version 2.0."

Hello World: 4 The Source File and Source Code

The Hello World program's source code resides in a single file, hello3.goc . Portions of this file are presented throughout this section.

This sample application demonstrates the basics of all GEOS programs, and it's likely you will refer to it often until you become proficient with both the system and the reference documentation. Among the concepts and implementations it demonstrates are the following:

  • Declaration and use of resources
  • Standard inclusion files
  • Basic UI construction
  • Creation of the Primary window
  • Creation of a scrolling view and drawing to the view
  • Creation of a menu
  • Creation of a simple dialog box
  • Declaration of subclasses and messages
  • Definition of message handlers
  • Sending messages to another object

This discussion assumes you have a solid understanding of general programming concepts and C constructions. If you don't, you should most likely get to know the C programming language before continuing.

Inclusions and Global Variables

The Process Object

UI Objects

Code and Message Handlers

Hello World: 4.1 The Source File and Source Code: Inclusions and Global Variables

As Inclusion Files and Global Variables shows, the first thing in a .goc file is a list of other files and libraries that must be included. These are designated in the standard C protocol, with one exception (described below). All the inclusions and libraries a basic application will need are shown in the sample, and only the following inclusions is required of every GEOS application:

@include <stdapp.goh>

Other inclusions may be required for more complex applications and libraries, and these inclusions will be listed in the topics that require them. Goc accepts both the standard C #include for .h files and the GEOS @include for .goh files. The difference between them is that .h files may not include Goc constructs (e.g. @object ) whereas .goh files can.

After the inclusions are listed, you should declare any global variables used throughout your application. Be aware that global variables, though accessible by any object, are owned by the application's Process object and by the application's primary thread. Objects running in other threads (such as UI objects) should not access these global variables directly because this can cause synchronization problems between threads.

Whenever possible, you should avoid using too many global variables. Global variables are typically put in a fixed-block resource, and having too many can bog down a low-memory machine.

The Hello World application uses only two global variables. The first, helloTextColor , holds the value of the currently-displayed text color and is initialized to the value C_BLUE (dark blue). The second, winHan , contains the window handle of the scrollable view into which we draw our text. How these variables are used will be shown later.

Code Display 2-2 Inclusion Files and Global Variables

This is the first portion of the hello3.goc file.

/***********************************************************************
 *	     Copyright (c) GeoWorks 1991, 1993-- All Rights Reserved
 *
 * PROJECT:		GEOS
 * MODULE:		Hello World (Sample GEOS application)
 * FILE:		hello3.goc (Code file for Hello World Sample Application)
 *
 * DESCRIPTION:
 *	This file contains the source code for the Hello World application.
 *	This code will be processed by the goc C preprocessor and then
 *	compiled by a C compiler. After compilation, it will be linked
 *	by the Glue linker to produce a runnable .geo application file.
 *
 ***********************************************************************/
/***************************************************************************
 * Include files
 *	These files are the standard inclusion files to use the basics of
 *	the GEOS system and libraries. All applications should include
 *	at least the following files. Note that all inclusion files
 *	have the suffix .h or .goh indicating they are header files.
 ***************************************************************************/
@include <stdapp.goh>				/* Standard GEOS inclusion file */
/***********************************************************************
 * Global Variables
 ***********************************************************************/
word		helloTextColor = C_BLUE;
WindowHandle		winHan;

Hello World: 4.2 The Source File and Source Code: The Process Object

Every GEOS application has an object called the Process object. This object is run by the application's primary thread and is an instance of a subclass of GenProcessClass . Because the Process is an event-driven object, there is no main() routine that is executed when the program is launched. Instead, the object will wait until it receives messages (events), at which time it will execute the proper methods.

Applications can be of two basic models: The procedural model puts the entire functionality of the application within the Process object, not using any other objects in the application's thread (UI objects are run by the UI thread). All messages sent to the application are handled by the Process object, and most OOP issues can be avoided. The object-oriented model allows for other objects to be run by the application's primary thread; each of these objects will have its own instance data and be located in an object block (a resource) associated with its own message queue.

There is no command or directive that determines which model is used--the distinction is inherent within the format of message handlers and therefore can be virtually ignored. Hello World, for example, is simple enough to use the procedural model, handling all functionality with the Process object.

The Process object of Hello World is shown in Hello World's Process Object .

Code Display 2-3 Hello World's Process Object

This code display is part of hello3.goc and follows the previous display directly. */

/***********************************************************************
 *			Class & Method Definitions
 * This section contains the definition of the application's Process
 * class and its methods. Other classes can also be defined here,
 * along with the message each handles.
 ***********************************************************************/
/*
 * Here we define "HelloProcessClass" as a subclass of the system-provided
 * "GenProcessClass". As this application is launched, an instance of this class
 * will be created automatically to handle application-related events (messages).
 * The application thread will be responsible for running this object,
 * meaning that whenever this object handles a message, it will be executing
 * in the application's thread.
 */
/* You will find no object in this file declared to be of this class. Instead,
 * this class is bound to the application thread in hello3.gp.
@class HelloProcessClass, GenProcessClass;
/* The messages HelloProcessClass objects can handle that are not
 * system-defined are enumerated here. Each of these messages is sent
 * by one of the triggers in the dialog box. This is where class-
 * specific messages for this application (not system-defined messages)
 * are defined. */
@message void MSG_HELLO_CHANGE_TO_BLUE(); /* sent by Blue trigger */
@message void MSG_HELLO_CHANGE_TO_GOLD(); /* sent by Gold trigger */
@message void MSG_HELLO_REDRAW_DOCUMENT(); /* sent by Process to itself */
@endc /* signifies end of class definition */
/* Because this class definition must be stored in memory at runtime,
 * we must declare it here along with a flag indicating how it should
 * be loaded. The "neverSaved" flag is used because Process classes
 * are never saved to state files and therefore no relocation tables
 * need be built. */
@classdecl HelloProcessClass, neverSaved;

Hello World: 4.3 The Source File and Source Code: UI Objects

As stated earlier, the bulk of the Hello World application consists of User Interface objects. These objects are defined just after the Process object in your application's code, and they are given certain attributes and instance data. Once they have been defined, in general you will not have to bother with them again.

UI objects are arranged into resources. Each resource is allocated a block on the global heap when the program is launched; therefore, resources should be kept to reasonable sizes (2K-6K) whenever possible.

The application object resides within its own resource so the application takes up very little memory when iconified (minimized). Menus for complex applications are usually put in a menu resource. Most other UI gadgetry is put in a resource called "Interface" (though this name is not required).

The Application Object

Every application must have an application object, an instance of the class GenApplicationClass . The application object handles and manages many application-related things such as dispatching input sent by the input manager to the application. The application object must be the top-level generic object in every application. The name of the application object is also stated in the geode parameters file line appobj (see The Hello World Parameters File ).

Hello World's Application Object shows Hello World's application object definition. The comments in the code are extensive and explain the purpose of each line.

Code Display 2-4 Hello World's Application Object

This display is part of hello3.goc and follows the previous display directly.

/*
 *			Application Object
 * The very top-level generic object of an application MUST be a GenApplication
 * object. The hello3.gp file contains an "appobj" statement which indicates
 * that this "HelloApp" object is in fact the top-level UI object.
 * This object should be in its own resource so it takes up very little memory
 * when minimized. Note that the name of the resource may be whatever you choose;
 * it does not have to be AppResource.
 */
@start	AppResource;			/* Begin definition of objects in AppResource. */
@object GenApplicationClass HelloApp = {
    GI_comp = @HelloPrimary;
            /* The GI_comp attribute lists the generic children
             * of the object. The HelloApp object has just one
             * child, the primary window of the application. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = @HelloPrimary;
            /* This GCN list determines which of the application's
             * window objects must be made visible when the
             * application first starts up. */
}
@end	AppResource			/* End definition of objects in AppResource. */

The Primary Window and the View Window

Every application must have a primary window object of class GenPrimaryClass . This object will draw and manage the primary window and will work with the UI and the geometry manager to arrange all the children of the window properly. It will also automatically put up the system-controlled gadgets (such as the system window menu, the minimize/maximize buttons, and the Express menu).

The Hello World primary window has only two children, one of which is a menu. The other is the view object, which occupies the remaining space within the primary. The view object, as stated earlier, automatically handles all scrolling and clipping of documents. Its scrollable area is eight and a half inches by eleven inches. A description of what the view window does and how it interacts with the Process object to draw the text is given in The Scrolling View and Drawing the Text .

Hello World's Primary and View Objects shows the definition and attributes of each of these two objects. See the illustration above for a diagram of the two objects implemented under the OSF/Motif specification.

Code Display 2-5 Hello World's Primary and View Objects

This display is part of hello3.goc and directly follows the previous display.

@start	Interface;		/* This resource is for miscellaneous UI objects. */
@object GenPrimaryClass HelloPrimary = {
    GI_visMoniker = "Hello World Sample Application";
            /* This title will appear at the top of the primary
             * window as the name of the application. */
    GI_comp = @HelloView, @HelloMenu;
            /* This window has two children, the GenView object
             * and the GenInteraction menu object. */
    /* For simplicity, this application is not given an icon. Therefore, we must
     * prevent the user from being able to minimize the application. This is
     * done by applying the following attribute to the GenPrimary object. Note that
     * GenPrimaryClass is a subclass of GenDisplayClass, in which this attribute
     * is actually defined. */
    ATTR_GEN_DISPLAY_NOT_MINIMIZABLE;
    /* The following hint allows the primary window object to size itself
     * according to its children (the view object). */
    HINT_SIZE_WINDOW_AS_DESIRED;
   /*
    * When the specific UI permits, let's not show the menu bar on
    * startup. Some applications would find the extra space this leaves
    * helpful, in particular on the small screens of pen-based devices,
    * though for this simple application it doesn't really matter.
    */
    ATTR_GEN_DISPLAY_MENU_BAR_POPPED_OUT;
    HINT_DISPLAY_MENU_BAR_HIDDEN_ON_STARTUP;
}
/*		GenView object
 * This GenView object creates a window where the application can display portions
 * of a document as necessary. We want it to be scrollable, so the specific UI
 * (OSF/Motif) will create scroll bars which the user can interact with. Whenever a
 * portion of the window needs to be redrawn, the GenView object will invalidate
 * a portion of the window, causing a MSG_META_EXPOSED to be sent to the
 * application. The application will draw the document into the window. The
 * window keeps track of a mask which is used to clip the application's
 * drawing operations so only the invalid portion of the window is drawn. */
@object GenViewClass HelloView = {
    GVI_horizAttrs = @default | GVDA_SCROLLABLE | GVDA_NO_LARGER_THAN_CONTENT;
                /* This makes the View scrollable in the
                 * horizontal dimension and keeps it from
                 * growing larger than our document. */
    GVI_vertAttrs = @default | GVDA_SCROLLABLE | GVDA_NO_LARGER_THAN_CONTENT;
                /* This makes the View scrollable in the
                 * vertical dimension and keeps it from
                 * growing larger than our document. */
    GVI_docBounds = { 0, 0, 72*17/2, 72*11 };
                /* This sets the document size (scrollable
                 * bounds) of the GenView. */
    GVI_content = process;				/* This sets the output of the View--where it will
                 * send its MSG_META_EXPOSEDs--to be the
                 * application's Process object. */
 /*
  * This view will not take text input, so specify that no floating
  * keyboard should come up. Otherwise, we would get a floating
  * keyboard by default on pen-based systems.
  */
 ATTR_GEN_VIEW_DOES_NOT_ACCEPT_TEXT_INPUT;
}
@end	Interface		/* End definition of objects in this resource. */

The Hello World Menu

The Hello World program has one menu, called "Menu", located in the primary window's menu bar. Menus are instances of GenInteractionClass with the GIV_POPUP attribute set in the GII_visibility field. The moniker of the menu object appears on the menu bar (see The Hello World Menu for the definition of Hello World's menu).

The menu should have one child for each of its entries. In Hello World, the only child is the dialog box, whose moniker appears as the text of the menu item that brings up the box.

Code Display 2-6 The Hello World Menu

/This display is part of hello3.goc and follows the previous display directly.

/*			HelloMenu Menu
 * Menus are of GenInteractionClass. The moniker of a menu is used to show the menu
 * on the primary's menu bar (thus, "Menu" will show up in Hello World's menu bar).
 * Each of the menu's children (in the GI_comp field) will be an entry or a
 * collection of entries in the menu. To separate a menu from a dialog
 * box GenInteraction, you must apply the visibility GIV_POPUP. A dialog box will
 * have the visibility GIV_DIALOG. */
@start MenuResource;
@object GenInteractionClass HelloMenu = {
    GI_visMoniker = 'M', "Menu";				/* The moniker of the menu is used in
                 * the primary window's menu bar. */
    GI_comp = @HelloColorBox;				/* The only child of the menu (the only
                 * item in the menu) is the dialog box. */
    GII_visibility = GIV_POPUP;				/* This attribute designates the GenInteraction
                 * as a menu or a submenu. */
}
@end MenuResource;

The Dialog Box and Its Triggers

The Hello World Dialog Box and Its Triggers shows the code for the dialog box and its triggers.

Dialog boxes in GEOS may be of GenInteractionClass , or dialogs may be called up and instantiated during execution with kernel routines. Hello World uses a GenInteraction in order to have a "floating" dialog box that may be retained and moved around the screen.

When a dialog box is brought up by a menu item, the moniker of the dialog box object will be used as the text of the menu item. Thus, the word "Color" will appear both at the top of the dialog box and in the menu.

Children of a dialog box are arranged by the geometry manager. In general, children will be arranged top to bottom (or left to right, depending on the specific UI) as they are defined. Thus, the two triggers will appear "Blue" first and "Gold" second (on bottom). The dialog box will automatically size itself to fit all its children properly. Dialog boxes, unlike windows, are generally not resizable.

Each of the triggers will appear as a simple button big enough to hold its moniker. Each trigger, when pressed, sends a specified message to a specified object. The Blue trigger sends MSG_HELLO_CHANGE_TO_BLUE to the application's Process object, and the Gold trigger sends the message MSG_HELLO_CHANGE_TO_GOLD to the Process object. These messages are placed in the Process object's message queue and require no return values.

Code Display 2-7 The Hello World Dialog Box and Its Triggers

This display is part of hello3.goc and follows the previous display directly.

/*			HelloColorBox Dialog Box
 * Dialog boxes are of GenInteractionClass. The moniker of the dialog box will
 * appear at the top as the box's title. Additionally, if the GenInteraction
 * is a child of a menu interaction, the moniker will also serve as the text that
 * appears in the menu item that brings up the dialog box. The dialog will
 * automatically size itself to be large enough to hold all its children. Thus,
 * this dialog will be small (with just two children, each a trigger with a short
 * moniker). */
@object GenInteractionClass HelloColorBox = {
    GI_visMoniker = 'C', "Color";				/* The moniker will be displayed both as the
                 * dialog's title and as the menu item that
                 * brings the dialog up. */
    GI_comp = @HelloBlueTrigger, @HelloGoldTrigger;
                /* The two triggers are the only gadgets
                 * in the dialog box. */
    GII_visibility = GIV_DIALOG;				/* In order for this interaction to be
                 * a dialog box, this attribute must be set. */
}
/*			GenTriggers
 * Buttons are implemented by GenTriggerClass. When a trigger is pushed by the user
 * (clicked on with the mouse), it will send the specified message to the specified
 * output object. In both cases below, the trigger will send an application-defined
 * message to the application's Process object. A trigger's moniker is displayed
 * within the trigger. In this case, both are text, but any graphics could be used
 * instead to create graphical triggers easily. (E.g. a blue flag and a gold flag
 * rather than the words "Blue" and "Gold.") */
@object GenTriggerClass HelloBlueTrigger = {
    GI_visMoniker = 'B', "Blue";				/* The 'B' indicates the keyboard navigation
                 * character for this trigger. */
    GTI_destination = process;				/* Send the message to the Process object. */
    GTI_actionMsg = MSG_HELLO_CHANGE_TO_BLUE;	/* Send this message. */
}
@object GenTriggerClass HelloGoldTrigger = {
    GI_visMoniker = 'G', "Gold";				/* The 'G' indicates the keyboard navigation
                 * character for this trigger. */
    GTI_destination = process;				/* Send the message to the Process object. */
    GTI_actionMsg = MSG_HELLO_CHANGE_TO_GOLD;	/* Send this message. */
}

Hello World: 4.4 The Source File and Source Code: Code and Message Handlers

One of the first things a C programmer might notice when looking at the Hello World program is that it has no main() routine. This illustrates the primary distinction between an object-oriented system and a system that emulates object-orientedness.

Applications in GEOS consist of a Process object and, optionally, other objects in the same or different threads. The Process object of an application is event-driven . This means that until it receives a message, it does nothing; when it receives a message, however, it will automatically be woken up with the instruction pointer pointing at the proper routine's entry point.

GenProcessClass , the superclass of every Process object, handles many messages that most applications may never need to know about. For example, when the program is first launched, the Process object will receive a series of messages from the UI and the kernel telling it how it should start up. It automatically responds by setting up the proper message queues and executing the proper code. These are things you, as the programmer, do not need to know about to create a working GEOS application (though they are documented in the Geodes chapter).

Throughout the program's life, then, the Process object will receive and respond to messages as they are received. Each message has at most one corresponding method; if no method exists for a message, the message is ignored.

The Hello World Process object can handle six different messages, each of which is sent by a UI object. It also uses one routine defined internally and not available for use by other objects. Of the six messages it handles, three are specific to Hello World and three are universal to all applications using a GenView object: MSG_META_EXPOSED , MSG_META_CONTENT_VIEW_WIN_OPENED , and MSG_META_CONTENT_VIEW_WIN_CLOSED are sent by the view, and MSG_HELLO_CHANGE_TO_BLUE , MSG_HELLO_CHANGE_TO_GOLD , and MSG_HELLO_REDRAW_DOCUMENT are defined specific to HelloProcessClass and are sent by the triggers.

The function HelloDrawText() is internal to Hello World and is called by the MSG_META_EXPOSED handler. It is declared before the handler to satisfy normal C constraints.

Additionally, two constants are defined to determine the document size. These constants, along with the declaration of HelloDrawText() , are shown in Constant and Routine Definition .

Code Display 2-8 Constant and Routine Definition

This display is part of hello3.goc and follows the previous display directly.

/*************************************************************************
 *			Code for HelloProcessClass
 * Now that all the UI gadgetry has been defined, we must provide the
 * methods and routines used by the application. For simplicity, all
 * messages will be handled by the HelloProcessClass object.
 *************************************************************************/
/* Define constants used by the color-setting methods. Each of these
 * is a document size parameter in points. Therefore, the document is
 * 8.5 inches wide by 11 inches tall (one point is 1/72 of an inch). */
#define HORIZ_DOC_SIZE			(72*17/2)
#define VERT_DOC_SIZE			(72*11)
/* Declare that we will use the function HelloDrawText(), and define its
 * return and parameter values. It has no return value and has one parameter:
 * a graphics state handle called "gstate." */
void HelloDrawText(GStateHandle gstate);
/* The following constants are used by HelloDrawText(). */
#define TEXT_POINT_SIZE			 48	/* point size */
#define TEXT_X_POSITION			 30	/* x position, in document coords. */
#define TEXT_Y_POSITION			100	/* y position, in document coords. */

Handling the Window Messages

As stated earlier, the winHan global variable contains the window handle of the view's window. To set the variable, Hello World must intercept and handle the message MSG_META_CONTENT_VIEW_WIN_OPENED . This message passes the window handle, which HelloProcessClass simply stuffs into its winHan variable.

When the view window is destroyed, the application must make sure it forgets its window handle. Otherwise, we could try to draw to a nonexistent window, which is an error. This will not be a problem for Hello World because the only time the view can be destroyed is when the application is being shut down. For completeness, however, Hello World handles MSG_META_CONTENT_VIEW_WIN_CLOSED and sets winHan to zero.

Both of the methods for the above messages are shown in Messages from the View .

Code Display 2-9 Messages from the View

This display is part of hello3.goc and follows the previous display directly.

/* NOTE:
 * Because these are simple methods, the structure and syntax of methods are not
 * handled here. See the handler for MSG_META_EXPOSED, later in this chapter. */
/***********************************************************************
 * MSG_META_CONTENT_VIEW_WIN_OPENED for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Record the handle of the view window when the view
 *		creates it. This allows us to more-easily update the
 *		document when the user changes the text color.
 * PARAMETERS:		void (	word viewWidth,
 *			word viewHeight,
 *			WindowHandle viewWin)
 * SIDE EFFECTS: winHan is set to viewWindow
 ***********************************************************************/
@method HelloProcessClass, MSG_META_CONTENT_VIEW_WIN_OPENED {
    /* Get the window handle of the View. We need this handle in order to
     * force a window invalidation, causing the View to send a MSG_META_EXPOSED
     * to the Process object and thereby forcing a redraw of the window. */
    winHan = viewWindow;
}
/***********************************************************************
 * MSG_META_CONTENT_VIEW_WIN_CLOSED for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Take note that the view is now closed, so we don't
 *		try and draw to it or invalidate it any more (at
 *		least until it reopens)
 * PARAMETERS:		void (WindowHandle viewWindow)
 * SIDE EFFECTS:		winHan is set to 0
 *
 ***********************************************************************/
@method HelloProcessClass, MSG_META_CONTENT_VIEW_WIN_CLOSED {
    /* Set our window handle variable to zero. */
    winHan = 0;
}

Handling MSG_META_EXPOSED

As discussed in our HelloView object declaration (see Hello World's Primary and View Objects ), the view will send a MSG_META_EXPOSED to the Hello World Process object. Receipt of this message indicates that part of the scrollable view window has become invalid and must be redrawn.

Therefore, the class of the Hello World Process object ( HelloProcessClass ) must know how to draw the document in response to this message. Note that this message did not have to be defined specifically in the earlier definition of HelloProcessClass --this is because the message is already defined for MetaClass , the superclass of all GEOS classes.

MSG_META_EXPOSED Handler shows the method that handles MSG_META_EXPOSED for HelloProcessClass . Notice that the method calls the HelloDrawText() routine rather than drawing the text directly. While this may appear inefficient at first (and is for such a small, simple application), there are two main reasons why this is done:

First, it takes advantage of the GEOS single imaging model. The method simply creates the proper GState for drawing to the view window, then calls the drawing routine. A similar message for printing (i.e. when the user clicks on a "Print" trigger, a print message may be sent to the Process object) can use the same drawing routine--its handler would simply set up a GState for drawing to a Spool file and then call the drawing routine. Thus, one function is used for two purposes, cutting down code size.

Second, it allows more modularity in the testing of your code. If you need to make sure, for example, that the message is being received and handled, but you don't (yet) care if the drawing is done properly, you can set up HelloDrawText() as a dummy function. This would allow you to ensure the message is handled properly without having to debug all the drawing code.

Code Display 2-10 MSG_META_EXPOSED Handler

This display is part of hello3.goc and follows the previous display directly.

/***********************************************************************
 * MSG_META_EXPOSED for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Redraw the recently-exposed portion of the View
 * PARAMETERS:		void (WindowHandle win)
 * SIDE EFFECTS:		The invalid region of the window is cleared out
 *
 * STRATEGY:		This message is sent by the windowing system when a
 *		portion of the GenView has become invalid, either
 *		because a window that was obscuring it has been moved,
 *		or because some called GrInvalRect.
 *
 *		We redraw the entire document, after telling the
 *		graphics system we're drawing to the invalid portion
 *		of the window.
 *
 ***********************************************************************/
/* The method is declared with the goc keyword @method. This is followed by
 * the name of the class that knows how to handle the message (in this case,
 * the class is HelloProcessClass). Finally, the name of the message that
 * invokes this method is specified. Other items may also be specified (such
 * as a routine name that can be used instead of a message), but these are not
 * required. */
@method HelloProcessClass, MSG_META_EXPOSED {
    /* The local variable gstate will hold a GState handle. We will do
     * our drawing to this gstate. */
    GStateHandle gstate;
    /* Get a new, default graphics state that we can use while drawing.
     * We must allocate a new graphics state for the View window using
     * the kernel routine GrCreateState(). We pass the window handle of
     * the View window, which we received in a parameter called "win". */
    gstate = GrCreateState(win);
    /* Next, start a window update. This tells the windowing system that
     * we are in the process of drawing to this window. This is very
     * important--it ensures the window will be in a consistent state while
     * we're drawing. Specifically, it locks in the invalidated region to
     * which we'll be drawing; this makes sure that other threads drawing
     * to this window will not have any effect on our GState. A window
     * update is started by calling the kernel routine GrBeginUpdate()
     * with the GState handle. */
    GrBeginUpdate(gstate);
    /* If we had background graphics to draw, we could call the appropriate
     * graphics routines now. But we don't. */
    /* Draw our text into the window (pass the GState). This is done here
     * by calling the function HelloDrawText(), which knows how to draw
     * the appropriate document. (See below.) */
    HelloDrawText(gstate);				/* Special Hello World routine (below). */
    /* Now end the window update (unlock the GState and its window)
     * with the routine GrEndUpdate(), and free the GState handle by calling
     * the kernel routine GrDestroyState(). */
    GrEndUpdate(gstate);				/* Signal that we are done with
                 * the window update. */
    GrDestroyState(gstate);				/* Destroy the temporary GState. */
}
/***********************************************************************
 * MSG_HELLO_REDRAW_DOCUMENT for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Force the document to be redrawn by marking the
 *		entire document area in the view as invalid.
 * PARAMETERS:		void (void)
 * SIDE EFFECTS:		Any drawing to the document area between this
 *		message and the MSG_META_EXPOSED that it generates
 *		will not show up, as the entire window will be invalid.
 *
 ***********************************************************************/
@method HelloProcessClass, MSG_HELLO_REDRAW_DOCUMENT
{
    /* Now create a temporary GState to use for window invalidation
     * if the window handle is valid, then redraw the window. */
    if (winHan != 0) {
    GStateHandle gstate = GrCreateState(winHan);
    /* Call GrInvalRect using the GState. Invalidate the entire
     * document. This will cause the View to redraw itself and send
     * a MSG_META_EXPOSED to the Process object. */
    GrInvalRect(gstate, 0, 0, HORIZ_DOC_SIZE, VERT_DOC_SIZE);
    GrDestroyState(gstate);				/* Free the GState. */
    }
}
/***********************************************************************
 * HelloDrawText
 ***********************************************************************
 * SYNOPSIS:		Draw a single line of text onto the document. Note
 *		that it has no concept of the screen or the view --
 *		it is given a graphics state and draws through it.
 * CALLED BY:		(INTERNAL) HelloProcess::MSG_META_EXPOSED
 * RETURN:		nothing
 * SIDE EFFECTS:		attributes in the gstate are altered
 *
 * STRATEGY:		We separate the drawing from the exposure handler
 *		so the same routine can be used for both window
 *		refresh and for printing.
 *
 *		Set the font, point size and color, then draw the text.
 *
 ***********************************************************************/
/* Functions are declared as they would be in C. Parameters are defined using
 * the ANSI calling convention: The type of the parameter is given, followed
 * by the parameter name. Multiple parameters are separated by commas. This
 * function has a single parameter. */
void HelloDrawText(GStateHandle gstate) {
    /* First change some of the default GState values such as the font
     * and point size. This is done with the routine GrSetFont(). */
    GrSetFont(gstate, FID_DTC_URW_ROMAN, MakeWWFixed(TEXT_POINT_SIZE));
    /* Set the text color to the value in helloTextColor by calling the
     * graphics routine GrSetTextColor(). */
    GrSetTextColor(gstate, CF_INDEX, helloTextColor, 0, 0);
    /* Draw the text onto the document by using the GrDrawText() routine. */
    GrDrawText(gstate, TEXT_X_POSITION, TEXT_Y_POSITION,
            "Welcome to GEOS!", 0);
}

Handling Messages from the Triggers

When the user clicks on one of the two triggers in the Color dialog box, the pressed trigger sends off a message to the Hello World Process object. The Blue trigger sends MSG_HELLO_CHANGE_TO_BLUE , and the Gold trigger sends MSG_HELLO_CHANGE_TO_GOLD . The Process object must be able to handle both of these messages.

The methods that handle these messages are similar. Each sets the global variable helloTextColor , and each forces the view window to redraw itself (by sending MSG_HELLO_REDRAW_DOCUMENT to the Process) so the color is changed on the screen as well as in our variable. Handlers for MSG_HELLO_ shows the code and comments of both these methods.

Code Display 2-11 Handlers for MSG_HELLO_...

This display is part of hello3.goc and follows the previous display directly.

/***********************************************************************
 * MSG_HELLO_CHANGE_TO_BLUE for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Set the text color to dark blue and redraw the text.
 * PARAMETERS:		void (void)
 * SIDE EFFECTS:		helloTextColor is set to C_BLUE
 *
 ***********************************************************************/
@method HelloProcessClass, MSG_HELLO_CHANGE_TO_BLUE {
    helloTextColor = C_BLUE;				/* Set the helloTextColor variable to blue. */
    @call self::MSG_HELLO_REDRAW_DOCUMENT();
}
/***********************************************************************
 * MSG_HELLO_CHANGE_TO_GOLD for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Set the text color to yellow and redraw the text.
 * PARAMETERS:		void (void)
 * SIDE EFFECTS:		helloTextColor is set to C_YELLOW
 *
 ***********************************************************************/
@method HelloProcessClass, MSG_HELLO_CHANGE_TO_GOLD
{
    helloTextColor = C_YELLOW; /* Set the helloTextColor variable to gold. */
    @call self::MSG_HELLO_REDRAW_DOCUMENT();
}

Hello World: 5 Exercises and Suggestions

After studying the Hello World sample application and reading the System Architecture chapter, you may be ready to try some exercises before embarking on your application. It is strongly suggested that you spend some time modifying the Hello World program to get used to the programming environment and the Goc UI definition syntax. Some things you might try are outlined below--some are extremely easy; some are more difficult. Some will require you to read other chapters in this and the User Interface manual. In any case, you should go on to read the GEOS Programming chapter to understand the various keywords and constructs of the Goc programming language.

Some simple UI exercises you can incorporate into the Hello World program include the following suggestions:

  • Add a new menu item that brings up a dialog box letting the user select a point size for the text. In the dialog box you would use a GenValue object or perhaps a list of point sizes (or even one or more triggers) to take the user's input. The GenValue sends out a message with a new number when the user sets it, and your Process object would handle this message and set the point size. Your HelloDrawText() routine would then have to set the point size of the text as well as the color.
  • Add a feature that lets the user set the position and/or rotation of the text. This exercise will help you get used to the GEOS coordinate space.
  • Make HelloView scalable. To do this, you will create a new menu or dialog box that lets the user specify the scale. Most likely you will create a number of GenTriggers that apply a pre-specified scale; each of these triggers will send MSG_GEN_VIEW_SET_SCALE_FACTOR to HelloView. You may even try using a GenViewControlClass object.
  • Add background graphics to get used to the coordinate system. Perhaps draw a colored rectangle behind the text that changes colors to be the inverse of the text (i.e. when the text turns blue, the rectangle turns yellow).
  • Add printing. This is not nearly as difficult as you might think. To do this, you should read the Printing chapter. This exercise essentially consists of adding a trigger to invoke printing and a message handler for the printing message. This handler will create a printing GState and call the drawing routine. Instead of drawing to the screen, it draws to the Spooler's input.

Some of the exercises may have been implemented in the sample programs provided. You should look through the samples for insight into how the exercises may be completed. You are also encouraged to make up your own exercises to build on Hello World.

If you are new to GEOS programming, you should work through the Tutorial; otherwise, you will likely be overwhelmed by the complexity and depth of the system and not know where to begin.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Installing the SDK

Welcome to GEOS development. This document guides you through the CD-ROM installation procedure; it also describes your environment configuration, shows how to set up multiple NT machines, and lists the most common setup difficulties in a short troubleshooting guide.

If you are familiar with GEOS development, please review the SDK changes described in Compiling GEOS Applications , and Directory Structure , before you begin compiling and debugging geodes.

You can find complete information on the tools mentioned here (Swat, pmake, etc.) in the Tools book.

1 System Requirements
2 Installation and Setup
2.1 Installation Details
2.2 NT Configuration
2.3 Environment Configuration
2.4 Registry Configuration
3 Launching NT GEOS and Swat
4 Compiling GEOS Applications
5 Directory Structure
5.1 Branches
5.2 Main Directories
5.3 Target Directories
5.4 Developing In Your Working Branch
5.5 Multi-Developer Projects
6 Troubleshooting

Installing the SDK: 1 System Requirements

The GEOS SDK runs on a Windows NT workstation (version 3.51 or later). The SDK also requires a perl interpreter (a shareware copy is provided on the CD-ROM) and the Borland C/C++ compiler, version 4.0 or later. (We recommend versions 4.X over 5.0; see Compiling GEOS Applications .)

Installing the SDK: 2 Installation and Setup

You can install the GEOS NT SDK as either a standalone SDK for just one computer or as a server/client SDK on a network drive. The former is suited for single developers while the latter allows multiple developers to work on different aspects of the same project simultaneously. In either case, the steps for installation are similar. In summary,

  • Install the Borland C/C++ compiler (v4.5 recommended).
  • Run setup from the GEOS NT SDK CD-ROM.
    • Specify the type of installation: Server, or Client, or Standalone.
  • Install perl (if not already present) by clicking on the "Perl Install" icon in the GEOS SDK Program Group.

Some additional steps are required for server-client installations.

  • Run the installer, specifying the "Server" option.
  • Set the network directory for the network (shared) branch of the SDK.
  • On each NT development machine, run the NT GEOS SDK installer with a "Client" install.
    • Specify where the network (shared) SDK branch resides.
The following sections explain each of these steps in greater detail and show how the SDK modifies the system environment.

Installation Details

NT Configuration

Environment Configuration

Registry Configuration

Installing the SDK: 2.1 Installation and Setup: Installation Details

Steps for installing the GEOS NT SDK:

Install the Borland C/C++ compiler (v4.5 recommended).
Installing the Borland C++ compiler prior to the GEOS NT SDK allows the SDK installer to automatically set the environment variable pointing to the directory where it finds the compiler. Be sure to install the 32-bit Windows compiler; it is part of the "typical" installation.
Run setup from the GEOS NT SDK CD-ROM.
Install the GEOS NT SDK by launching setup.exe, found in the CD-ROM's topmost directory. The installer will prompt you to choose from one of three installation options: Server, Client, or Standalone.

A server installation installs the shared components of the SDK on a network server so that it accessed from multiple client workstations.

A client installation:

  • creates a local (client) working directory.
  • sets your environment variables and updates your NT configuration.
  • sets defaults in the variable registry.

A standalone installation:

  • installs both the client and server portions on one machine.
  • creates a private demo (target) tree.
  • sets your environment variables and updates your NT configuration.
  • sets defaults in the variable registry.

Note that the root for the client and server portions must be the same. For example, if the server SDK is in f:/prog/sdks/pcgeos, then the local working directory must look like c:/pcgeos/jdoe, where the two italicized components match. This applies to both standalone and client/server development. Typically, this root directory is "pcgeos", e.g. S:\PCGEOS. (For more information, see Directory Structure.)

Install perl.
If you do not have a perl interpreter, install the shareware copy provided by running pcgeos\bin\Pw32i302.exe or by clicking the "Perl Install" GEOS SDK Program Group icon. Leave checked the box to run install.bat after unzipping; reply "y" (the default) to the install options.
Verify that environment and registry variables have been properly set.
The modified environment variables are listed in Environment Configuration. You can modify registry variables with GeosSetup.exe, as described in Registry Configuration.

Installing the SDK: 2.2 Installation and Setup: NT Configuration

The installer completes the following for you automatically; they are included here for reference only.

  • Task Priorities
    Foreground and background task are set to run at the same priority. This is needed for Swat to run properly. This setting is found in the System module of the NT Control Panel.
  • Open File Count
    files = 100 (or more) specified in %SystemRoot%\system32\config.nt .
  • File System Driver
    fs = os2{ec}.geo specified under the [system] category in your demo's .ini file.

Installing the SDK: 2.3 Installation and Setup: Environment Configuration

The compiling and linking tools use the following environment variables to find executables and include files. These variables are set from the system module of the control panel, not an autoexec file.

setup sets these environment variables for you; they are included here for reference only.

ROOT_DIR
The root of the SDK installation, typically C:\PCGEOS (local development) or S:\PCGEOS (network development, substituting your particular network drive for S: ).
GOC_COMPILER_DIR
The root of the compiler directory, such as C:\BC45.
PATH
Your PATH must include entries for the bin\ directories of the Borland C++ compiler, PCGEOS, and your copy of perl. (More information on Borland C++ and perl is found in Compiling GEOS Applications .)
CCOM
Alternate compilation command for pmake . This must be set to @dosfront bcc for versions of the Borland compiler prior to 5.0.

Installing the SDK: 2.4 Installation and Setup: Registry Configuration

To check your registry settings, run GeosSetup.exe from the GEOS SDK Program Group. (Note: do not confuse GeosSetup.exe for setup.exe, which is the installer program.)

Swat (here and on its command line) prefers forward slashes in directory paths; backslashes are sometimes inadvertently interpreted as special characters (\tools as <TAB> ools, for instance).

SDK Root
C:/PCGEOS (local) or S:/PCGEOS (network).
Can be any path, but the last element is typically PCGEOS. This is the directory where you installed the SDK.
Local Root
S:/PCGEOS/username , for example.
Where Swat stores .symdir files; also the head of its search for an engineer's own .sym files. For pmake to run properly the penultimate element of Local Root must have the same name as the last element of SDK Root . (It need not be the actual SDK Root directory, however.) The last element may be named anything, but user names are typically used.
Branch
N9000V20
 or other platform as appropriate. Leave blank if your development tree does not have platform branches.
Object Source
Installed
 Directory under SDKRoot/Branch heading the search for shared or SDK-supplied .sym files.
Tcl Directory
Tools/swat/lib.new
 Location of Swat's .tcl files under SDK Root .
Tcl Override Directory
Location of additional .tcl files; typically blank.
Swat Init file
Any init file you would like Swat to execute upon startup. Typically blank or C:/PCGEOS/SWAT.RC .
Fast Video
Checked
 NT GEOS video mode selector. Fast Video artificially reduces the number of screen updates used by NT GEOS; therefore, you will see less flicker and faster execution response than on the actual device. Disable this option if you are testing animations or assessing relative flicker rates.

The GEOS Setup window also includes a selector for the communication mode. Choose Local (NT native) to instruct Swat to attach to your NT GEOS window, or choose Remote (Serial) to instruct Swat to attach to an external device, with appropriate Speed and Port settings.

You may store and recall a number of different setups by using the Current Setup selector. The Setup names have no intrinsic meaning.

Installing the SDK: 3 Launching NT GEOS and Swat

You will find example shortcuts for launching the NT GEOS demo in the GEOS SDK Program Group. You can define additional shortcuts to launch NT GEOS by right-clicking on the root window (NT 4.0) or selecting the menu item File -> New on the Program Manager (NT 3.51). Specify a command such as
c:\pcgeos\target\n9000.nc\swat.exe /s loader.exe (modified as desired); you must also specify a working directory (under target\ ) corresponding to the simulation you wish this icon to run. For command examples, refer to the batch files described below.

To launch a target device window from the command line, change-directory to your target area (for example, \pcgeos\target\n9000.ec\ ) and type start s at the prompt. ( s.bat is supplied in the target directory templates, and start tells NT to run the command in a new window.) s.bat is a batch file which executes the Swat stub; ss.bat forces the demo to wait for a host Swat debugger to attach. Execute start s (or ss ) in your non-EC directory to run the non-EC version of the Swat stub. To run the non-Swat demo, use start geos , which executes loader{ec}.exe without the Swat stub.

In summary, each of these demos is available in both the EC and non-EC target directories:

ss.bat		Demo with Swat stub, wait for host Swat
s.bat		Demo with Swat stub, do not wait
geos.bat		Demo without Swat

Launch the host Swat debugger from an icon in the GEOS SDK Program Group or from the command line with swat (or start swat ). (Be careful not to run a Swat stub if your current directory is a target directory; you want to run swat.exe from pcgeos\bin\ .) Debugging with Swat in the NT environment is the same as that in the old DOS one; use ^ C and c to interrupt and continue execution, att and det to attach and detach, and so forth.

The Swat window size may be increased by modifying the window-size property of your command windows in general or your Swat shortcut in particular. Any change made to a window after Swat is launched will not be recognized.

You can instruct Swat to attach to an external Communicator device or PC emulator by running GEOS Setup prior to launching Swat and selecting the serial Communication Mode and an appropriate Comm port .

You can stop NT GEOS by entering detach or quit when Swat is attached. Otherwise, you can stop the demo by clicking the kill (X) button on the right of the titlebar, double-clicking the top-left titlebar menu, or using NT's task manager.

.swat: Automating Swat

If you have a file called . swat in the root of your private directory and run the GEOS Setup program to change the "Swat Init File" field to point to that file, Swat will execute Swat commands in that directory when it starts up. Below is a short example of a .swat file:

srcwin 15
sd hello

The srcwin command sets up a source-code window at the bottom of the screen. The sd command changes the symbolic default to be the "hello" thread. Any time Swat does not recognize a name in the current thread, it will see if the name has meaning in the hello thread.

Installing the SDK: 4 Compiling GEOS Applications

To compile GEOS C code, you need the following third-party software installed on your system:

  • Borland C/C++ Compiler
  • Perl Interpreter

Your PATH variable must include the Borland compiler's bin directory and the directory containing the perl interpreter. If you installed Borland C++ after the NT SDK or if you set up a network environment, you may need to set the environment variable GOC_COMPILER_DIR to reflect the compiler's location. Refer to Environment Configuration .

Borland C++ is a separate product on its own CD-ROM. We strongly recommend that you install version 4.5 of the compiler, included with most distributions of version 5.0. A change in the compiler's handling of #pragma options creates errors (not reported at compile time) in geodes compiled with version 5.0. You must install the 32-bit Windows compiler, a part of the "typical" installation.

A shareware version of the perl installer has been included with the SDK; refer to Installation and Setup for installation instructions.

All of the tools needed to compile geodes (goc, glue, etc.) have been included in the \pcgeos\bin directory. Use mkmf and pmake to compile, as described in the chapter "Using Tools" of the Tools book. ( pmake depend is not needed in the NT environment.)

After compiling, copy resulting geodes directly to your target tree from the command line or use Swat's send command after attaching. world\extrapps is the appropriate demo directory for extras on the Nokia 9000i Communicator.

Changes From the DOS SDK

If you are familiar with GEOS development in the DOS environment, note the following changes:

  • You need not run pmake depend after mkmf ; use it only to re-make dependencies when you change your include-file statements.
  • You may use DOS copy to put compiled geodes in your target tree, although Swat's send command works as before.
  • Subdirectories under the main include-file directories must be explicitly stated, as in #include <Ansi/string.h> .
  • The "pause" and "break" keys do not stop the demo; kill the GEOS window using the menubar or use Swat's detach or quit command.
  • The development directories have been modified; see Directory Structure.

Installing the SDK: 5 Directory Structure

The directory structure in the NT SDK differs significantly from that of the DOS SDK. The change reflects standardization across platforms and facilitates multi-engineer development in a network environment.

Branches

Main Directories

Target Directories

Developing In Your Working Branch

Multi-Developer Projects

Installing the SDK: 5.1 Directory Structure: Branches

Every device that runs GEOS contains a specific version of the kernel, applications, and various ancillary libraries and drivers. The directory path that contains the specific version of the device software is called a Branch. (The "Trunk" is a collection of all the files from all the different branches. As a developer, you will never need to utilize any files on the Trunk.)

Branch directories are located directly under the PCGEOS directory ( i.e. , the main SDK directory) and were developed as a way to "lock in" a device-specific image of the development system. For example, the N9000V20 branch contains all the header and symbol files for Version 2.0 of the Nokia 9000 SDK. When you install an SDK for another GEOS device, you will find a new branch directory specific to that device under the PCGEOS directory.

The figure below shows the directory layout of the Nokia 9000i SDK (Version 1.1). This may appear slightly different on newer SDKs. This directory tree contains two Branch directories: N9000V11 and {Working_Branch} . ({ Working_Branch } is your personal development area; see Developing In Your Working Branch for a complete explanation of Working Branches)

If multiple developers are working on a project, then the branches should be separated into the local branches which reside on each engineer's machine and the shared network branch which all the project developers will access each time they compile. When performing a server install, the above directories marked "shared/network" are installed onto the network machine, and the client install places the directories marked "local" on each developer's machine. (See 5.5 Multi-Developer Projects.)

Installing the SDK: 5.2 Directory Structure: Main Directories

Each Branch contains the following directories:

CInclude
Contains all the *.H and *.GOH header files you need to compile GEOS applications written in GOC.
Include
Contains all the *.DEF and *.UIH header files you need to compile GEOS applications written in ESP (assembly).
Installed
Contains all the *.LDF ( i.e. , link definition files) files (used by Glue, our linker) and symbol files of all the GEOS applications, libraries, and drivers (used by Swat, our debugger).
Appl
Contains a subdirectory holding your application's source code; for instance, your CardGame application might reside in: appl\cardgame\cardgame.goc

Also contains the following subdirectories:

SDK_C
which holds general purpose sample applications; for example, the huge array sample application lives in: appl\sdk_c\harray\harray.goc
SDK_*
which holds device-specific sample applications (where * refers to the specific device); for example, the dialog box sample application for the Nokia 9000i Communicator lives in: appl\sdk_9000\dialogs\dialogs.goc
Library
Contains a subdirectory holding your library's source code; for example, the new utility library you're writing would live in: library\utility\utility.goc
Driver
Contains a subdirectory holding your driver's source code; for example, the new Zippy driver you're writing would live in: driver\zippy\zippy.goc

Installing the SDK: 5.3 Directory Structure: Target Directories

In addition to those directories listed above, your Working Branch contains a Target directory. This directory contains the executable and data files comprising an emulation (or "demo") of the actual GEOS device. The Target subtree mirrors the file system layout of the actual device.

Be sure to update the working directories of your GEOS SDK demo shortcuts so that they point to the Target directory in your Working Branch; for example, the shortcut to the error-checking demo for the Nokia 9000i Communicator should be \pcgeos\username\target\n9000.ec .

Network installations can make use of shared geode libraries if desired. When geodes are invoked during emulation, the current working (target) directory tree will be searched first, followed by trees specified by the top keyword under [paths] in your .ini file. For example, all of the geodes under the system subdirectory could be stored in a central, shared location in order to save disk space or allow easy updates. You can supersede shared geodes in your demo by putting custom versions (of the same name) in your private system subdirectory.

Each target directory has a geos{ec}.ini file. It can refer to files of standard .ini settings under its [paths] category; it also contains superseding information necessary for running NT GEOS. When editing .ini files, take care to end each line with a CR-LF pair or use an editor (such as DOS edit ) that does so transparently. More information on .ini files in general is found in the Tools book.

Installing the SDK: 5.4 Directory Structure: Developing In Your Working Branch

The Working Branch is your private development area. By doing all your work in your Working Branch (also called your Local Root ), the Branch files can remain untouched.

You specify the directory for your Working Branch during the Local SDK Install program. The default is your login name. For example, if your login is jdoe, then your Working Branch directory is c:\pcgeos\jdoe\ . (You should also make sure that the demo shortcuts point to the Target directory in your Working Branch.)

Within your Working directory is a file called BRANCH. This file specifies which Branch directory the tools will use when compiling and linking your source code. The tools will first look for files in your Working Branch. Whatever files it does not find in your Working Branch, it will look for in the Branch specified by the BRANCH file. You need only keep the file or files that you are working on in your Working Branch and system files (such as header files) can remain on the device Branch. So, for example, a typical development sequence might look like this:

  • You want to add another feature to the Zippy driver so you copy (or "check out" if you have a version control system) the zippy.goc file from the Branch to your Working Branch.
  • You edit the file and make your changes.
  • You compile the modified file.
  • You find and fix all the bugs.
  • You "install" the modified file by using version control or by simply copying it back to the Branch.

To specify which Branch the compiler and linker should use, place the name of the Branch directory in the BRANCH file. (Currently, setting the Branch field in GEOS Setup only affects which branch Swat uses; you must edit the actual BRANCH file yourself.) Initially, your BRANCH file contains N9000V20.

Installing the SDK: 5.5 Directory Structure: Multi-Developer Projects

The system of Branch files is most helpful when there are multiple developers working on the same project. Each developer would maintain her personal working files in her own Working Branch while shared files would remain on the device Branch. As development progresses, each developer can "install" files to the Branch directory as she reaches stable milestones thereby allowing her co-workers to use the "installed" versions of her files. (Note: "installing" can be as simple as copying the files by hand or can be handled by a version control system.)

Installing the SDK: 6 Troubleshooting

If you are having trouble starting the simulation or attaching Swat, consult the table below. The most common known difficulties are listed.

GEOS window dies immediately.
Run the EC version and attach Swat. Does it die with the error LS_CANNOT_LOCATE_KERNEL?
The working directory of the NT GEOS window must be a target directory with an .ini file and device subdirectories.

Does Swat report FSHOF_SET_FILE_NAME_PASSED_TO_SKELETON_DRIVER, ATTEMPTED_TO_PERFORM_UNSUPPORTED_FUNCTION_
BEFORE_APPROPRIATE_IFS_DRIVER_WAS_LOADED, or TOO_MANY_PRIMARY_FSDs?
There is a problem with the file system driver. Check .ini file settings and setver listing, referring to NT Configuration .

GEOS is not in a window.
It should have a title bar and look like a window.
Verify that you have a text loader ( loader{ec}.exe ).
Verify that geosvdd.dll is in your path.
Verify that you are using an NT video driver under [screen 0] in your .ini file (probably simp4bi{t/e} ).
GEOS cannot launch, reporting: Can't open named pipe
Are you already running NT GEOS?
Only one copy can run at a time.
Swat cannot connect, reporting: Can't contact GEOS using the Named Pipe: \\.\pipe\swatpipe
Are you running the demo with a Swat stub ( s.bat or ss.bat )? Did you immediately try to attach?
If you are running the correct demo, try to attach again. You may have attempted to attach before the Swat stub was ready.
The keyboard doesn't work.
Is the NT GEOS window active? It should have a blue title bar.
Click in the window to make it active.
GEOS isn't running a window.
Check your .ini file (and [paths] referents) for correct keyboard setting (or pini keyboard in swat); you must use the gdikbd keyboard driver.
The keyboard seems to fail suddenly.
Ctrl or Alt keys may be "stuck" in GEOS . Hit Ctrl, Alt, Caps Lock, and Shift a few times to clear them.
Extended-character-set dialog will not come up.
Verify that altGr = true under category [GDI keyboard] is set in your .ini file (or referent).
Swat's srcwin command fails, reporting: Window too small
The default NT command-window size is too small for the default srcwin. There are two fixes:
(1) Reduce the srcwin buffer size to match your window size with the Swat command srcwin 20.
 (2) Restart Swat from a larger window. (Size changes during a session are not recognized by Swat.) Your default window size can be increased by changing the window-size property of your command windows, or just your Swat shortcut.
Swat does not set mouse-click breakpoints.
Make sure quick-edit mode is off in your Swat window.
GEOS hangs until Swat window selected.
Have foreground/background task priorities been set equal?
Check under the System module of the Control Panel.
GEOS hangs or .ini file corrupt.
Run the EC version and attach Swat. Is the .ini file corrupt?
Open and re-save it using NT or DOS edit to restore CR-LF 's.
GEOS does not emulate sounds.
To test sounds in your application, you must use an actual device. (Sound has been disabled entirely to avoid the spurious sounds produced by the PC - DOS driver, in your . INI files, SOUND = OFF under [ UI ].)
pmake fails, reporting: Couldn't find geos.mk.
Set environment variable ROOT_DIR (to C:\PCGEOS , e.g.).
pmake fails, reporting: Couldn't find stddef.h
Set environment variable GOC_COMPILER_DIR (to C:\BC45 , e.g.).
This document is a single-page version of a a multi-page document, suitable for easy printing.


Overview

This chapter describes the structure of GEOS and the various system components and services used by applications. Nearly all programmers will want at least to browse this chapter before continuing with GEOS application development. The structures and architectures of the 8088 and 80x86 processors are reviewed in Appendix A.

1 GEOS Overview
2 The System Architecture
2.1 Applications
2.2 Libraries
2.3 The Kernel
2.4 Device Drivers
2.5 The User Interface
3 Object-Oriented Programming
3.1 Objects, Messages, and Methods
3.2 Classes and Inheritance
4 Multitasking and Multithreading
5 The GEOS User Interface
5.1 The Generic User Interface
5.2 The Scalable User Interface
5.3 Windows and Window Management
5.4 Input
5.5 Menus and Dialog Boxes
5.6 Scrolling Views
5.7 Visible Object Classes
5.8 Geometry Manager
5.9 Lists
5.10 Other Gadgets
5.11 Managing Documents and Files
5.12 Multiple Document Interface
5.13 Clipboard and Quick-Transfer
5.14 General Change Notification
5.15 Help Object
6 System Services
6.1 Memory
6.2 Virtual Memory
6.3 Local Memory and Object Blocks
6.4 Item Databases
6.5 Graphics System
6.6 Text
6.7 Print Spooler and Printing
6.8 Timers
6.9 Communications
6.10 Math support
6.11 International Support
7 Libraries
8 Device Drivers

Overview: 1 GEOS Overview

GEOS is a state-of-the-art graphical operating system combining the latest software technology with numerous innovations. Its tightly coded assembly language and object-oriented system turns the humblest 8088-based PCs with as little as 512 K of RAM into graphical workstations. On higher-end machines, GEOS is in a class by itself--performance on 80286, 80386, and 80486 machines with extended or expanded memory is exceptional.

Besides being coded entirely in object-oriented assembly language (a concept and language developed by Geoworks), GEOS is built on one of the most sophisticated operating system architectures available for PCs. Most of the GEOS kernel and system is composed of dynamically-linkable libraries; in addition, developers can create their own libraries to support entire generations of applications.

GEOS also includes true pre-emptive multitasking, multiple threads of execution within single applications, built-in outline font technology, and drivers for various devices such as printers, mice, video cards, file systems, keyboards, parallel and serial ports, task switchers, power management systems, and font engines. Development for GEOS is easy and quick; much code traditionally required by applications has been provided within the system software, thereby reducing time spent by application programmers on coding and debugging.

In addition, GEOS employs a breakthrough generic user interface. Applications specify their user interface needs, and the system dynamically chooses the proper manifestation of the UI at run-time through the use of specific UI libraries. Complex drawing and geometry are handled automatically by the UI library. The generic UI allows applications to work with various UI specifications without being recompiled. Users can choose the look and feel they want without burdening the application programmer with extra coding.

Among the other advanced features included in GEOS are a single imaging model providing true WYSIWYG (a single image is used both for drawing to the screen and for printing); automatic scaling and scrolling of displayed data; dynamic memory management and virtual memory; an item database manager; a graphic object library including support for resize, move and scale operations; complex and complete text formatting and editing; specialized generic controller objects providing standard user interface and functionality; and full network support. Several other mechanisms and libraries not mentioned here are also built into the system.

The following sections of this chapter describe many of these concepts and implementations, providing a quick-view of the GEOS system as a whole. More information may be found on individual topics elsewhere in this developer's kit. This chapter provides the background information necessary, however, to take full advantage of all that GEOS has to offer the application developer.

Overview: 2 The System Architecture

The GEOS system as a whole consists of several layers. The topmost layer contains applications. Users interact directly with these applications, which use dynamic libraries on the next layer. One of the major libraries is the User Interface Library, commonly known as the UI. Both the libraries and applications interact with the kernel, the heart of the operating system. The kernel communicates with device drivers, which function as the final GEOS interface to physical devices such as video screens, printers, and file systems.

Applications

Libraries

The Kernel

Device Drivers

The User Interface

Overview: 2.1 The System Architecture: Applications

A GEOS application can take advantage of the various kernel and UI mechanisms to provide some set of functionality to the user. An application is made up primarily of two parts: The first part defines the application's user interface using objects and functions provided by the User Interface Library. The second part consists of the code of the application including procedures, functions, and object-specific code.

Overview: 2.2 The System Architecture: Libraries

GEOS libraries are bodies of code or collections of objects that are dynamically loaded into memory when called for by applications or by other libraries. The use of libraries reduces the amount of code within application resources and increases application performance. In addition, library routines and objects may be shared by several different applications at once, reducing duplicated code in memory.

Many standard libraries are provided with GEOS and are available to all developers. Among these are a text object library, a graphic object library, a an import/export library and others. In addition, developers can easily create their own libraries with the developer kit tools.

Overview: 2.3 The System Architecture: The Kernel

The GEOS kernel is the core of the operating system, providing all the basic services needed by the system. It provides multitasking, memory management, object creation and manipulation, windowing, process management, file system access, inter-application communication, message dispatching, and scores of basic graphic primitives. The kernel provides nearly 1000 routines usable by applications and libraries, though most applications will use only a portion of them.

Overview: 2.4 The System Architecture: Device Drivers

GEOS device drivers isolate the system and applications from the specific needs and foibles of a user's hardware. Drivers translate the generic actions of an application (such as opening a file) into the specific needs of the hardware or system in use (such as the specific call to MS-DOS to open the file). By isolating the system from hardware specifics, drivers allow GEOS to be expanded in the future to use new devices and engines without recompiling applications.

Drivers are provided with the system for printers, video cards, mice, keyboards, serial and parallel ports, file systems, task switchers, power management hardware, and font engines. In the future, developers will be able to develop their own drivers for new hardware and file systems.

Most applications and libraries will access drivers through kernel calls. Drivers are also accessible to applications and libraries directly.

Overview: 2.5 The System Architecture: The User Interface

The GEOS User Interface (UI) is a special entity. While it consists primarily of dynamic libraries, it can be thought of as a user interface driver.

Applications define their UI needs in a high-level language (called Goc and developed by Geoworks) interpreted by a special C preprocessor. Applications in general do not have to manifest windows, scrollbars, buttons, menus, or dialog boxes; instead, they use generic objects from the provided UI object library. These generic objects have no inherent visual representation; instead, they have a strictly-defined functionality and API.

A Specific User Interface library, at run-time, determines the visual representation each object takes depending on its context. For example, a GenInteraction generic object may appear as a menu, a dialog box, or a merely a grouping of other generic UI gadgets. Additionally, a menu GenInteraction may be represented as a vertical menu in one Specific UI but as a horizontal bar of choices in another.

Although such a system may appear to take some control of the application's user interface away from the programmer, in actuality quite the opposite is true. The use of a generic UI frees the programmer to concentrate on other issues and features without worrying about whether the application conforms to one or more specifications. The programmer does not have to worry about window positioning or clipping areas (unless he or she wants to), and menus and dialog boxes are almost entirely automatic.

Additionally, the programmer can set hints for individual generic objects; hints can help the specific UI decide how the object should appear or be arranged on the screen. Not all hints are allowed in all specific UIs, so a particular specific UI may ignore certain hints while implementing others. For example, a dialog box that has three buttons within it may have the hint HINT_ORIENT_CHILDREN_VERTICALLY to indicate the buttons should appear in a column rather than a row.

Overview: 3 Object-Oriented Programming

Object-Oriented Programming (OOP) is a popular way of organizing programs, especially programs using graphical user interfaces. GEOS is an object-oriented system, programmed entirely in Object Assembly, a Geoworks innovation. This section describes the concepts behind OOP and how it is implemented in GEOS. If you are familiar with OOP concepts, you may skip this section; because terminology can differ from system to system, however, you will probably want to at least skim the rest of the section. (A full discussion of GEOS messaging and object manipulation can be found in the GEOS Programming chapter.)

Object-Oriented Programming is simply a way to organize code and data differently from traditional procedural programming. Anything done with procedural programming can be done in an object-oriented way and vice versa. However, OOP offers several significant advantages over procedural programming:

  • Modularity
    Especially in large systems, modularity can lead to cleaner, more manageable, and more easily expandable programs. OOP provides tight modularity with each component ( object ) having a strict API. This modularity also makes OOP systems ideal for the multithreaded environment of GEOS.
  • Simplicity
    People interact with things individually. Each object in our environment has a certain set of characteristics and certain behaviors. OOP systems use this point of view as a foundation for programs that are easy to understand.
  • Efficiency
    The efficient design of objects in an OOP system can lead to less repetition of reusable code. Several objects working independently may use the same code, thus reducing the impact on memory usage.
  • Reduction of code
    Because functionality (functions, routines, etc.) is inherited by object types from other object types (known as classes), it doesn't have to implemented again and again. Instead, one version of the code can exist in a particular class and be inherited by other classes, thus reducing coding and debugging time.

Objects, Messages, and Methods

Classes and Inheritance

Overview: 3.1 Object-Oriented Programming: Objects, Messages, and Methods

Procedural programming uses routines that act with globally-accessible or locally-defined data. Those routines must know about other routines in the program and must understand the data structure and organization used by other routines. Although some amount of isolation is applied to each routine, in essence they are all part of the greater whole of the program.

Objects, in their simplest sense, consist of data ( instance data ) and routines ( methods ) that act on that data. Objects may interact with each other but may not alter other objects' instance data; this rule leads to strict modularity and cleaner program design. Objects do not need to understand how other objects work or how another object's instance data is arranged; instead, only the interface to the other object is required.

Objects interact with each other via messages . Messages may indicate status, provide notification, pass data, or instruct the recipient to perform an action. When an object receives a message, it executes an appropriate method . A method may change the object's instance data, send messages to other objects, call kernel routines, or perform calculations--anything that can be done in a normal program procedure can be done in a method. (Note that occasionally the term message handler is used for method .)

Every object is represented by an Object Pointer (optr), a data structure that uniquely identifies the object's location in the system. This data structure is a combination of two special memory handles that provide abstraction of the object's location--this allows the object to be moved in memory or swapped to disk, or even saved to and retrieved from files. Object pointers are used to identify objects in many situations, the most common being when a message is sent. The intended recipient of the message is indicated by its optr.

Objects may interact with each other even if they are in different threads of execution. This is made possible by message queues and the kernel's message dispatcher . When a message is sent, it is first passed to the kernel's dispatcher with the recipient's optr. The dispatcher then puts the message in the recipient object's message queue. If other messages have been sent to the recipient but not handled yet, then the message will wait in the queue until the others have been handled. Otherwise, the message will be handled immediately. (For timing-critical messages, the sender can indicate that the message must be handled immediately; this is important in a multithreaded system.)

A thread is a single entity that runs a certain set of code and maintains a certain set of data. Only one thread may be using the processor at any given time; when a context switch occurs, one thread loses the processor and another takes it over. Each thread may run code for many objects, and an application can have several threads.

As an example, the figure below shows the conceptual model of a Counter object. The Counter maintains a single instance data field, currentCount . It also has three predefined actions:

  • CounterIncrementCount()
     This method handles the message MSG_COUNTER_INCREMENT . Its purpose is to increment the currentCount instance data field. When the count reaches the maximum (in this case, 100), then the counter resets itself.
  • CounterReturnCur()
     This method handles the message MSG_COUNTER_RETURN_CUR . It returns the value of the currentCount instance data field.
  • CounterReset()
     This method handles the message MSG_COUNTER_RESET . It sets the currentCount instance data field to zero.

Messages can carry data (parameters) with them and can also have return values. For example, if the Counter in the example could be set to an arbitrary value (rather than reset to zero), the object sending the set message would have to indicate the value to be set. This would be passed in the same way as a normal C parameter.

If a message is supposed to return a value to the sender, the sender must make sure the message is handled immediately. In this case, the message acts like a remote routine call, blocking the thread of the caller until the message has been handled and the value returned.

Objects can also share data when necessary. Rather than share instance data, however, each object can have an instance data field containing the handle of a sharable block of memory holding the subject data. Then each object could access the shared block through the normal memory manager. Handles are offsets into a table maintained by the kernel; this table contains pointers to the actual memory. This abstraction of the pointer allows the kernel to move the memory block around without forcing everything else to update their pointers. Handles can also be passed as message parameters, allowing large blocks of data to be passed along from object to object.

The figure above shows an example of two objects interacting via messages. In this example, the Requestor object requires the Calculator object to perform a calculation given two numbers. However, since the timing of the calculation is not critical, the result is not returned but is instead sent back by the Calculator object in a subsequent message. This allows the Requestor object to continue with other tasks until the result is ready. (Note that if the Calculator object could be used by more than one requestor, it would have to also be passed the requestor's object pointer. Otherwise, it would have no idea where to send the return message. This example assumes the Calculator object is used by only the one requestor and inherently knows the requestor's optr.)

Overview: 3.2 Object-Oriented Programming: Classes and Inheritance

If every object had to be coded and debugged once for each time it was used, OOP would provide few benefits over standard procedural programming. Many objects share similar functionality and could easily make use of the same code over and over again. The concepts of class and inheritance allow objects with similar data structures and methods to use common code.

The class is actually where the functionality of objects is defined. Every object is simply an instance of a class, a manifestation of the instance data and methods defined for the class. For example, the Counter object of Objects, Messages, and Methods could be an instance of a class called CounterClass . Other counters sharing the same characteristics would also be instances of CounterClass , each having its own value in its own instance data field.

A main benefit of the implementation of classes in GEOS is that objects can be created and destroyed during execution. Class definitions are stored separately from an individual instance's data; each instance knows where its class is located. Therefore, if two instances of a particular class exist, they both point to a single copy of the class definition. Since the code is stored in the class definition, the instances can use up only as much memory as their data fields require; each instance does not need to store its own method code.

Another, more powerful, benefit is gained through the implementation of inheritance. In many cases, objects will be similar but not identical. This means that their classes are also similar but not identical. Inheritance overcomes this problem through the use of superclasses and subclasses .

A class provides a certain set of instance data and methods. A subclass inherits all the instance data structures and methods of its superclass, modifying or enhancing them to provide some different functionality.

Take, for example, the class CounterClass . An instance of this class would provide a counter with certain features: The counter goes from zero to 100, it can be reset manually, it resets automatically when it reaches 100, and its value is retrieved with the use of a certain message. However, suppose we need a counter that does all those things and allows the counter to be set to an arbitrary value with the use of a new message.

Without inheritance, we would have to write all the code for CounterClass over again as a new class, adding the new method. However, by creating a subclass of CounterClass (let's call it CounterSetClass ), we can inherit all the methods and data structures of CounterClass without recoding them. Instead, we simply add the new message and the code for the new method. (It is also possible to modify inherited methods in a subclass.) This example of CounterClass and CounterSetClass is shown in the figure above.

The inheritance implemented in GEOS is actually much more complex and sophisticated than this simple example shows. You may never need to know more than the above concepts, however.

GEOS classes may be of a special type known as a variant . Variant classes do not initially know what their superclass is; instead, the superclass is determined by context for each instance of the variant class. This is a complicated topic that is discussed later in the documentation.

Overview: 4 Multitasking and Multithreading

Multitasking is the ability to have multiple processes running simultaneously on a single system. In actuality, PCs have only one processor and only one task can run at any given moment; the operating system must manage the processor and allocate time to all the different programs running at once.

A thread is a single entity that runs in the GEOS system. Threads may be event-driven or procedural. An event-driven thread runs objects and has a message queue (also called an event queue). The thread receives messages for its objects and is only active when the objects are handling the messages. If an event-driven thread never receives a message, it will never use the processor. Procedural threads execute sequential code such as functions or procedures in C. Procedural threads do not have a message queue and do not run objects.

Cooperative multitasking , used in some operating systems, requires all processes running to cooperate with the operating system. The system has a routine which each process must call periodically; this routine (called a Context Switch routine) checks if any other processes are waiting to run. If other processes are waiting, priorities are checked and the process with the highest priority takes over the processor.

GEOS uses preemptive multitasking , in which threads are given the illusion that they have nonstop access to the processor. A preemptive system periodically interrupts the thread running and checks for other waiting threads; if there are any, the system will switch context automatically. Programs do not have to call a context switch routine, a cumbersome requirement in many cases.

GEOS also maintains a knowledge of thread priority . Every thread (including the kernel and user interface) has a base priority and a current priority; the base priority rarely changes, while the current priority decreases with recent processor usage. GEOS maintains these priorities intelligently, making sure that no thread ever uses the CPU more than its share of the time.

Any application running in GEOS may have multiple threads. This is useful, for example, in a spreadsheet application, where complex and time-consuming calculations may be done by a background thread that wakes up only when calculations need to be made. Use of multiple threads can make the application appear to be extraordinarily fast; the user can continue to navigate menus and use other UI gadgetry while the application is also doing something else.

Overview: 5 The GEOS User Interface

The GEOS User Interface (UI) maintains the interaction between each application and the user. It provides several necessary and many extra services for applications, relieving programmers from building much of the basic UI functionality into their applications. The structure, components, and services of the GEOS UI are outlined below.

The Generic User Interface

The Scalable User Interface

Windows and Window Management

Input

Menus and Dialog Boxes

Scrolling Views

Visible Object Classes

Geometry Manager

Lists

Other Gadgets

Managing Documents and Files

Multiple Document Interface

Clipboard and Quick-Transfer

General Change Notification

Help Object

Overview: 5.1 The GEOS User Interface: The Generic User Interface

A User Interface (UI) specification is, essentially, a set of rules and conventions used to determine the interaction between a user and an application. Over the years, UI specifications have evolved from basic command-line shells to the standard graphical and pen-based systems in use today. No doubt you have come across several graphical UI specifications currently in use--Macintosh, Open Look, Presentation Manager, and OSF/Motif are a few examples.

Each of these specifications represents a particular implementation of the basic functionality required for application-user interaction. The basic functions are similar; only the implementation is different. Therefore, each of these specifications is referred to as a Specific UI .

GEOS employs a Generic UI --an implementation-independent way of defining an application's user interface. To the user, this means a single application executable can be used in any of several specific UI implementations. To the developer, this means relief from many of the UI definition tasks normally associated with graphical user interfaces.

The Generic UI can be thought of as a UI driver--a layer of abstraction between the application and the UI implementation. The programmer defines his needs in terms of basic functionality and user involvement, leaving the actual implementation (decisions about color, shape, menu structure, etc.) up to the system at run-time.

The application defines its UI by selecting certain objects from the Generic UI Library. Each of these objects has certain attributes that determine its specific implementation. In addition, each generic object may be assigned hints . Hints help the Specific UI Library determine the proper context and subjective information required to fully flesh out the proper manifestation of each generic object (for example, a GenPrimary window object can have special sizing restrictions suggested with various hints such as HINT_FIXED_SIZE and HINT_INITIAL_SIZE ).

Attributes

Attributes represent objective information which does not change with the specific UI in use. In general, attributes determine the basic functionality of the object regardless of its manifestation on the screen. For example, a GenView may have its scrollable attribute set; this means that in all Specific UIs, the View will have some sort of scrollers. However, one Specific UI might implement the scrollers as bars and another might implement them as dials or cranks.

Attributes are always allocated space in an object's instance data regardless of whether the attribute it set. For example, all GenView objects have a bit specifying whether it is scrollable or not. Attributes are used, therefore, primarily for determining the object's functionality rather than its appearance.

Hints

Hints represent subjective or contextual information about objects. Hints are different from attributes in two main aspects: First, hints may be ignored by certain Specific UIs. Suppose, for example, that a given Specific UI only allowed horizontal ordering of components within dialog boxes. This specific UI would ignore the hint HINT_ORIENT_CHILDREN_VERTICALLY applied to any GenInteraction object implemented as a dialog box.

Second, hints are dynamically added to each object's instance data in a special format known as variable data . Variable data fields are not allocated within the object's instance data unless they are used. Thus, objects with fewer hints use less memory. Attributes, on the other hand, must exist in the object's instance data whether they are used or not, and they therefore require a given amount of memory.

The Generic UI Classes

The Generic UI Library contains a number of object classes that implement nearly all the UI functions an application will ever need. For a full description of the API of each of these classes, see the Objects Book. Some of the more common implementations (e.g. menus, dialog boxes, and scrolling views) of these objects are outlined in the sections below.

Overview: 5.2 The GEOS User Interface: The Scalable User Interface

As application technology advances, applications gain more and more features. Despite recent improvements in user-friendliness, however, over-featured applications can sometimes intimidate and frustrate users.

GEOS therefore implements a Scalable User Interface , a system in which a single application's source code can be compiled into different versions of the application, each version at a different level of complexity. Scalability of applications is shown in Geoworks Pro--the appliances are all created from exactly the same source code as the advanced versions of those applications.

Overview: 5.3 The GEOS User Interface: Windows and Window Management

A window is an object that defines an area on the screen in which drawing and user interaction can occur. Windows may be resizable and movable, and automatically clip any drawing so it does not appear outside the window's edges.

The GEOS Window Manager maintains information about the size, shape, and visibility of all the windows in the system. In general, applications do not access the Window Manager directly; instead, generic UI objects and the kernel handle all window manipulation. Several different types of generic objects are "window" objects: GenPrimary is used as an application's primary window, GenInteraction is used for dialog boxes and menus, GenDisplay displays documents in a scrollable window, and GenView is used to create a scrollable graphics window into which an application can draw.

The Window Manager is closely tied to the GEOS Graphic System. These two parts of the system handle all window resizing, reshaping, drawing, and clipping automatically. They also interact with the User Interface's input mechanism to ensure that the proper windows receive the proper input. Applications do not need to know the shape, position, or size of a window when drawing inside it.

Applications can also allocate one or more windows in which they can present data (text, graphics, or other information). Each of these windows is called a View and is an instance of the GenView object (see below). The View manages all scrolling, scaling, sizing, and clipping automatically for the application; programmers don't have to worry about managing scrollbars or window size in order to ensure the proper portion of the data is drawn. Additionally, applications can modify any of the scrolling, scaling, or sizing behavior with a little effort to provide custom functionality.

Overview: 5.4 The GEOS User Interface: Input

GEOS has an input manager that tracks the mouse, gets keyboard input, and passes the input events on to the proper windows and objects on the screen. The input manager is part of the UI.

The UI keeps track of three different active objects in the system: the Focus, the Target, and the Model. The focus is the active object that should receive all keyboard input. The target is the currently selected object with which the user can interact. The model represents a non-visible "selection" that can be maintained by the application. These three active objects can be accessed directly by other objects (e.g. menu items); for example, a "change color" menu item may want to work on whatever object is currently active--it would want to change the color of the target object, and by contacting the target directly, it does not have to know what object is the target.

Any object in the system can grab mouse or keyboard events. When an object has been granted a mouse grab or keyboard grab, it will receive the events until it relinquishes the grab. This fact is most useful when dealing with visible object trees inside a View (see GenView, below)--generic objects handle input automatically (including the Text objects).

Applications may or may not need to handle input directly; complex applications likely will want to filter certain input or handle special cases. Simple applications or utilities will probably not have to deal with input (keyboard or mouse events) directly at all.

Overview: 5.5 The GEOS User Interface: Menus and Dialog Boxes

Among the generic objects of the GEOS UI library is GenInteraction. This object can be used to implement menus, dialog boxes, and error boxes, and it can also be used for grouping and arranging other generic objects (such as triggers, lists, etc.). It provides extreme flexibility and functionality, and experienced programmers can usually get a menu and dialog structure up on the screen quickly just by setting a few attributes.

Remember that because the Interaction is a generic object, the Generic UI and Specific UI libraries will translate it into its proper implementation at run-time. The Interaction serves the primary purpose of grouping objects; the grouping may be implemented in various ways (e.g. a menu or a dialog box) depending on the attributes and hints applied.

The sections below outline some of the practical functionality of these objects; for a full description of what these do and how they can be used, see the Object Reference Book.

Menus

Menus in GEOS are subject to the rules and conventions of the specific UI in use by the user. However, several basic concepts are supported.

  • Standard menus
    Some specific UIs have differing menu structures. Therefore, several standard generic menus (e.g. the File menu) can be implemented by giving the menu a standard set of attributes. The UI will automatically build and manage this menu according to the UI specification in use at the time.
  • Cascading or nested menus
    Because Interactions may be nested, cascading (or nested) menus are easy to implement. Cascading menus are useful if an application has many menu items that may logically be grouped into different areas of functionality.
  • Keyboard accelerators
    Because the UI manages both input and menus, you can easily specify accelerator keystrokes for each menu item. When the user hits the proper keystrokes, the same action is performed as if the user had clicked on the associated menu item. This happens automatically without additional application code.
  • Keyboard menu navigation
    Every menu item may have a character that the user may press during keyboard navigation. This is important because it allows users to use your entire application without having a mouse.
  • Lists and checkboxes within menus
    Menus may contain not only triggers but also lists of items. Lists are implemented with the GenItemGroup, GenItem, GenBooleanGroup, and GenBoolean generic objects. Lists may be exclusive or not, and each entry may have a checkbox indicating its on/off status. Again, all these functions are determined by the attributes set for the interaction and item objects.
  • Menu items that bring up dialog boxes
    By setting certain attributes in an interaction, you can create menu items that automatically bring up dialog boxes. The UI will put a trigger in the menu representing the dialog box, and when the trigger is activated, the UI will put up the box.
  • Pin-up menus
    In the OSF/Motif implementation of GEOS, all menus are automatically given the ability to be pinned in place and moved around the screen. Because this is a function of the specific UI, applications are completely unaware of the fact.

Dialog Boxes

Dialog boxes are standard ways of having an application interact with the user. For example, a dialog box may contain a number of controls that determine how the application displays its data.

Dialog boxes, like menus, are implemented through the use of GenInteraction objects. Applications may also call a kernel routine that will put up standard dialogs in certain situations (e.g. errors or warnings).

Several features of dialog boxes are implemented automatically with very little additional code in the application:

  • Automatic geometry management
    When a dialog box contains generic objects (e.g. triggers, lists, and text fields), all geometry is handled automatically. By using various hints, you can modify the normal organization, but sizing and placement of the generic objects and the dialog box are entirely automatic.
  • Standard response triggers and reply bars
    Just as there are standard menu types, UI specifications often include rules for dialog box response triggers (e.g., the "OK" button on the left and a "Cancel" button on the right). Standard response triggers normally appear in an area of the dialog box known as the "reply bar." You can set up a reply bar with standard attributes that will automatically be implemented properly by the UI. As an alternative, you can even use pre-defined dialog box types that have their own reply bars.
  • Modality
    Sometimes, dialog boxes require a user action before the application or system can continue with other tasks. Dialog boxes can have various modality states: system-modal, which does not allow the user to interact with anything but the dialog box; application-modal, which allows the user to switch to other applications but disallows interaction within the dialog's application (except for the dialog box); and non-modal, which allows the user to switch to any other part of the system.
  • Standard dialog types
    To increase the amount of standardization across all applications, GEOS includes several standard dialog box types accessible through the use of UI routines. These include errors, warnings, and questions. Also, you can call up custom dialog boxes with special kernel routines.

Overview: 5.6 The GEOS User Interface: Scrolling Views

Although the basic generic objects provided by GEOS are powerful and useful in many situations, applications still need a way to display their own specific data. In most systems, this consists of allocating a window for the application's use and then implementing additional functionality for that window--scrolling, scaling, and document management.

The GenView generic object does all these things for an application. The application can allocate any number of views for its own use, and each view can display anything from regular graphics to hierarchies of visible objects.

Most applications will use at least one view. Its power and flexibility give programmers the opportunity to concentrate on their own application's functionality without having to worry about display issues such as scaling, clipping, and scrolling--they are all handled automatically. Some of the features of a view are listed below (see the GenView chapter for more detailed information):

  • Automatic clipping and updating
    When an application uses a GenView, the programmer can forget the worries involved in figuring out which portions of his document need to be redrawn when. The GEOS Window Manager will automatically clip the document to the View's boundaries and will notify the application whenever a portion of the View has become invalid (whether by scrolling, sizing, moving, or being uncovered by movement of another object). The application simply must draw the document whenever notified that a portion has become invalid; it does not have to do any calculations regarding what is visible on the screen (though it might if the document is especially complex).
  • Automatic scrolling
    By setting two attributes in a GenView's definition, an application can automatically make its view scrollable. The UI will automatically create scroller objects that will interact directly with the view--the application never has to know how far or in what direction the view has been scrolled. It simply will receive a message saying that the window has been invalidated and needs to be redrawn.
  • Automatic scaling
    The view provides automatic scaling of the contents of its window. The application may request scalings or include a standard controller to let the user control this transparently to the application.
  • Automatic sizing
    The Geometry Manager will automatically size the view to fit within its parent window. When the parent is resized, the view will follow. This process is transparent to applications. However, applications can customize this behavior by setting a fixed or desired size or by altering the sizing behavior of the view. They can also override this to make the parent window follow the view's size.
  • Comprehensive input management
    Many applications will display objects within a view. In many cases, these objects will require input (e.g. the cards in Solitaire). The view gets input events from the User Interface and can pass them on to the proper objects in the hierarchy.
  • Flexibility
    All the functions described above may be customized. Scrolling, for example, can be tracked and altered. Custom scroller objects can be defined in place of the automatically-generated scrollers. Sizing behavior can be adjusted. If necessary, you could get the view's bounds in order to draw only the visible portion of your document (though this often does not increase drawing performance significantly).
  • Customizable background color
    Each view has a default background color determined by the specific UI. This can, however, be set to any RGB value or to any GEOS color index. This is used, for example, in Solitaire to create the green background (so the application does not have to draw green under all the other objects).

Overview: 5.7 The GEOS User Interface: Visible Object Classes

Many applications will need to create objects that draw themselves and interact directly with the user. Rather than forcing the programmer to create each of his objects from scratch, GEOS offers several visible object classes that already understand several system services and constructs. When an application needs a visible object, it simply defines a subclass of one of these visible classes, thus ensuring that his object will handle all the UI messages that may be sent to it.

Overview: 5.8 The GEOS User Interface: Geometry Manager

The GEOS User Interface includes a sophisticated Geometry Manager that does all the calculations for organization of objects on the screen. The Geometry Manager manages the positions of generic UI objects in dialog boxes and windows, for example, and it can be used to automatically position visible objects within a view.

Overview: 5.9 The GEOS User Interface: Lists

The GenItem, GenItemGroup, GenBoolean, GenBooleanGroup, and GenDynamicList objects can be used to provide several different types and styles of lists. Lists may appear within menus or dialog boxes. The actual visual implementation of a list depends on the specific UI in use; however, there are several basic types of lists available (for complete information, see the Lists chapter):

  • Dynamic lists
    Lists can be either static (a set number of elements) or dynamic (a varying number of elements).
  • Scrolling lists
    Scrolling lists are usually implemented for long lists or dynamic lists.
  • Exclusive lists
    Exclusive lists have one and only one element chosen at all times.
  • Non-exclusive lists
    Non-exclusive lists may have any number of elements set at a time. Each element is set or reset independently of the others.
  • Non-exclusive lists with override
    Non-exclusive lists with an override have a non-exclusive list with an additional element that overrides all the others (an example is the text styles menu--the "Plain" option overrides each of the others).

Overview: 5.10 The GEOS User Interface: Other Gadgets

Besides the objects listed above, the generic UI supplies a number of other gadgets. These include

  • Text Display and Edit Objects
    The GenText object provides a full-featured word processing object. It can be used for every text function from displaying a word to implementing a simple yet full-featured word processor. It supports multiple styles and paragraph attributes, character and paragraph spacing, and all the other features supported in the Text Library (see Text .)
  • Triggers
    The GenTrigger object implements all the functionality of a simple push-button. When a trigger is pressed, it sends a message to a pre-specified object.
  • Value
    The GenValue object allows the user to set a value within a given range.
  • Controllers and Tool Boxes
    The GenControl and GenToolControl objects allow an application to set up controllers that provide standard menu entries and their associated features. For example, the GenEditControl object creates the standard Edit menu with Cut, Copy, and Paste triggers. The GenToolControl object allows the user to specify which items in which controllers may be displayed within a floating tool box.
  • File Selector
    The GenFileSelector provides all the features of a file selector dialog box that allows the user to traverse their file system's directories and select a file.

Overview: 5.11 The GEOS User Interface: Managing Documents and Files

Most applications will save data files. GEOS provides a convenient storage format for data files and an even more convenient mechanism for managing the files.

The Document Control objects interact with the GEOS file system and virtual memory system to implement the Open, Close, Save, Save-As, and Revert functions of the File menu as well as many other common document-related features. In fact, the Document Control also controls user interaction for these basic functions, creating and updating the File menu and putting up the proper dialog boxes for each operation.

The Document Control objects, like GenView and other components of GEOS, take basic functionality common to most applications and implement it in the system software. Using a Document Control for your data file management can save weeks of coding and debugging time.

Overview: 5.12 The GEOS User Interface: Multiple Document Interface

Some applications may benefit from allowing the user to have multiple data files open at any given time. GeoWrite and GeoDraw, for example, allow the user to work with multiple documents at once. Each document appears within its own window; each of these windows is a GenDisplay object within a Display Control object.

The Display Control objects, like the Document Control objects, allow applications to use multiple data files with a minimum of extra coding. The Display Control works closely with the Document Control to ensure that the proper data files are being operated on at all times. The Display Control also creates and maintains the Window menu, just as the Document Control creates and maintains the File menu.

Overview: 5.13 The GEOS User Interface: Clipboard and Quick-Transfer

A common feature of GUIs is the Clipboard. Users can copy or cut data out of documents for pasting into other documents (or even the same document) later. When data is cut or copied, it is placed in a transfer item, a special data structure used by the Clipboard. All applications can access the Clipboard via Cut, Copy, and Paste; in addition, the Edit menu can be implemented automatically with the use of a GenEditControl object.

GEOS also offers another method of transferring data--the quick-transfer mechanism. In OSF/Motif, the user clicks the move/copy button (in most cases the right mouse button) over some selected data. He can then drag to another area and drop the selected data, either moving or copying it depending on certain contextual factors. Applications may or may not support the quick-transfer mechanism, though supporting it is not difficult and is strongly encouraged.

Both the Clipboard and the quick-transfer mechanism are supported automatically by all text edit and display objects. Applications may define their own custom data formats (transfer items) for use with both the Clipboard and the quick-transfer mechanism.

Overview: 5.14 The GEOS User Interface: General Change Notification

In a multithreaded system, one thread may change some information that another thread depends on. In this case, the thread that makes the change must notify all other users of the information that it has been changed and that they must update their status appropriately.

GEOS provides a mechanism for allowing all threads or objects to register for notification of certain events. For example, GeoManager is notified of all file system changes so it can update its display in real time. Although notification is automatically sent for certain system changes, applications can set up an automatic notification of custom changes. For example, if a multiuser network game depended on each user knowing a certain status flag, automatic notification could be set up to notify all the users whenever any user changed it.

Overview: 5.15 The GEOS User Interface: Help Object

On-line help is a convenient and powerful way to provide documentation to your users. Because graphical user interfaces are easy to understand, many users do not read documentation and prefer to discover an application's features on their own. On-line help can speed their learning process up.

GEOS provides an object that you can include in your User Interface that will automatically create a "Help" icon or menu item and display help text when that item is invoked. The help text displayed will be context-sensitive as defined by the application. The application simply has to provide the help text and a few other attributes of the help object, and the system will take care of displaying the help when the user requests it.

Overview: 6 System Services

GEOS provides a number of services useful or necessary to applications. These services range from dynamic memory management to an item database to a sophisticated graphics system and a print spooler. Many of the most useful or integral services are described in this section.

Memory

Virtual Memory

Local Memory and Object Blocks

Item Databases

Graphics System

Text

Print Spooler and Printing

Timers

Communications

Math support

International Support

Overview: 6.1 System Services: Memory

GEOS uses all RAM available to the system, even expanded and extended memory. Memory is managed by the kernel and is accessed in the segmented scheme implemented by the 8086 processor. (The protected mode of the more advanced processors is not supported in the Version 2.0 system software but is expected to be in future releases.)

The GEOS Memory Manager is sophisticated. It uses dynamic allocation and access of blocks on a global heap to provide high performance, optimized to run very efficiently even on systems with only 640 K.

Handles

GEOS maintains control of all the memory, objects, and other entities in the system through the Handle Table . The Handle Table is a section of memory set aside for kernel use, and it contains a number of entries, each 16 bytes that can contain various information about many different items in the system. These entries are accessed by handles , 16-bit offsets into the Handle Table. Applications and libraries may use handles but may not access the contents of a handle table entry. The data structure is opaque to all but the kernel.

Handles are used for many different things. They can reference threads, memory blocks, VM blocks, or files; they can represent data structures such as timers, queues, semaphores, or events (messages); and they can be used by the kernel for optimization (such as when several words of data are passed with a message).

Through the Preferences application, a user can set the number of handles in the Handle Table. However, the size of the table does not change during a single execution of GEOS. Applications and libraries may allocate handles dynamically for most of the above-mentioned purposes (e.g. memory and file reference).

The Global Heap

A certain amount of memory is set aside for the file system (DOS, typically), the GEOS kernel (approximately 64 K), and any TSRs the user may have running on his system. Above this is the GEOS handle table. All RAM left (up to 1 megabyte) after these requirements are satisfied is used for the global heap (RAM over 1 megabyte is used as preferred swap space). The global heap is the space available to all the applications running in the system.

Applications allocate dynamic blocks of memory on the heap. Blocks may be of arbitrary size between 16 bytes and 64 K. Each block is designated a handle, an offset into the handle table where information about the block (such as size and location) is stored. Because blocks may be shuffled in the heap by the Memory Manager, applications must use handles as indirect references to the blocks; applications should not save pointers to specific locations in memory.

GEOS uses four basic types of blocks:

  • Fixed
    A fixed block will never move or swap out to disk. Having many fixed blocks can seriously degrade system performance. Fixed blocks are generally used for an application's global variables and primary code.
  • Moveable
    Moveable blocks may be shuffled around the heap. Moveable blocks may also be designated discardable and/or swapable (below). These blocks must be locked into memory before they can be accessed (see below).
  • Discardable
    Discardable blocks may be flushed whenever the Memory Manager requires more memory.
  • Swapable
    Swapable blocks may be swapped at any time by the Memory Manager. When a subsequent access is made to a swapped block, the Memory Manager will automatically read the block back into memory.

Allocating Memory

The GEOS kernel provides several routines to allocate memory. Some memory is allocated automatically, such as memory for code resources as they are loaded in. Other memory, however, must be allocated as it is needed during execution--for example, as the user types more and more text into a word processor. There are three basic ways to allocate memory on the heap:

  • MemAlloc()
     This routine allocates a new block on the heap for an application's use.
  • MemReAlloc()
     This routine reallocates a given block; this is useful for adding memory to a block already allocated.
  • malloc()
     Although use of malloc() may help in porting previous C code to GEOS, it is discouraged. The malloc() routine will allocate small amounts of memory within a fixed resource. Extensive use of malloc() leads to large, fixed blocks on the heap, degrading system performance.

When an application is done with a memory block, it can free the block with the routine MemFree() . This will allow the Memory Manager to free up that memory space and re-use the block's handle if required.

Accessing Memory

If a block is allocated as fixed, an application can use a far pointer to access any byte within the block. However, because fixed blocks are not always allocated in the same portion of memory each time an application is loaded, applications should not save pointers as state information.

Non-fixed blocks, however, can not be accessed by far pointers without locking them into their position in memory. MemLock() will take a block's handle and lock the block, thereby assuring that the Memory Manager will not move it in the middle of an access. MemLock() provides its caller with a far pointer to the block. When access to the block is finished, the thread that locked the block must call MemUnlock() , which marks the block as unlocked so it may once again be moved or swapped.

Overview: 6.2 System Services: Virtual Memory

GEOS implements a powerful virtual memory concept to accomplish both memory block swapping and data file storage. Virtual memory (VM) can be thought of as a disk-based heap--a VM file is segmented into blocks, each of which is designated a VM handle analogous to a global memory handle. When a VM block is required by an application, it is locked into memory from the file with a call to the kernel routine VMLock() , which loads the VM block into memory and locks it on the global heap.

VM is extremely useful for data file storage; indeed, the Document Control objects use it extensively.

Overview: 6.3 System Services: Local Memory and Object Blocks

Often applications will want to store large numbers of very small data items. In these cases, it's cumbersome and inefficient to allocate a global handle and a block on the global heap. For these situations, GEOS offers the concept of local memory (LMem).

Local memory can be thought of as a mini-heap within a memory block. Each individual unit allocated in this mini-heap is known as a chunk , and each chunk is designated a chunk handle . Thus, to access any piece of information within an LMem block, you need to have the block's global handle and the item's chunk handle.

Chunks may be allocated or freed, and any chunk may be moved within an LMem heap at any time (e.g. when the LMem heap is compacted). Locked LMem heaps may have chunks shuffled only when space is allocated within the heap. Access to chunks is gained by locking the LMem block and dereferencing the chunk handle. Chunks may be resized, and bytes may be added to or removed from chunks.

LMem blocks are used primarily for two purposes: Database files and object blocks. Database files are managed by the Item Database Library (see below). Object blocks are used extensively by the UI and applications; object blocks store objects in memory and often are attached to VM files.

Each object in an object block occupies one chunk. Thus, the combination of global handle and chunk handle of an object is known as the Object Pointer (optr) of the object. The optr is used to identify the object uniquely in the system for all types of access.

Overview: 6.4 System Services: Item Databases

The GEOS database routines allows applications to use LMem heaps and VM files together easily and cleanly. The database routines allows applications to add a layer of abstraction to their data files.

An application may allocate database files, logical groups of items within those files, and individual items within the groups. The database library automatically manages the LMem and VM blocks that implement the database, keeping them desirable sizes and moving them into and out of memory as needed.

Additionally, items may be allocated as "ungrouped," in which case the database manager will intelligently distribute these items among the various VM blocks in the database file for efficient access.

Overview: 6.5 System Services: Graphics System

The graphics system provided in the GEOS kernel is extremely powerful and is designed to make creating graphics simple and fast for applications. It includes some advanced concepts and state-of-the-art technology that make GEOS rise well above most other GUIs.

The Coordinate Space

The GEOS graphics system uses a single imaging model like that used by PostScript. Applications and libraries draw their graphics on a generic rectangular grid with a resolution of 72 points per inch. When drawing to the screen or a printer, the graphics system will translate the graphics commands to the highest resolution of the output device, ensuring true WYSIWYG output. This system allows programmers to specify their graphics in real-world coordinates without worrying about the size of various resolution devices.

Normal graphics coordinates are represented by signed, 16-bit integers. Normal documents can be up to about nine feet on a side. Additionally, the graphics system allows applications to use an extended coordinate system (called the large document model ) of signed, 32-bit integers. Large documents can be up to 900 miles on a side! Large documents, however, can incur a certain amount of additional overhead; if you do not need to use large documents, you probably should not.

Graphic States

To simplify complex drawing, the graphics system maintains a graphics state , or GState . GStates can be created or destroyed dynamically to allow several different GStates for a given window (only one may be active at any given moment, however).

The GState is essentially a data structure that contains all the relevant information about the current state of a window. The GState's information includes current color, drawing position, angle of rotation, scale, translation in the coordinate space, line and fill attributes, clipping paths, text attributes, and other items.

Graphics commands are issued relative to a particular GState. For example, the command GrDrawEllipse() must be passed a GState so the kernel knows exactly how to draw the ellipse; if the current GState is rotated 45 degrees and has green as its current line color, a green-outlined ellipse will be drawn rotated 45 degrees.

Programs may apply transformations to GStates--rotation, translation, or scaling. You can also define custom transformation matrixes to apply to your GStates if complex operations (e.g. shearing) are required.

Graphic Primitives and Graphic Objects

GEOS provides a complete set of graphics drawing primitives including lines, arcs, Bézier curves, splines, outline-defined text, rectangles, ellipses, polylines, polygons, and bitmaps.

Paths

A graphics path in GEOS is a continuous trail that defines the outline of an area. A path is an outline description of an arbitrarily shaped area, useful when an application must define a mathematically-precise shape. Paths may also be filled, combined with other paths, and scaled to any size without loss of resolution.

One example of the powerful application of paths is creation of arbitrary clipping regions. It is possible, for example, to clip drawings to an ellipse, to a bézier curve, or even to text.

Regions

Regions perform essentially the same function as paths--definition of an arbitrarily shaped area or clipping region. However, because regions are defined as resolution-dependent, they are typically used only for optimized drawing of UI gadgetry.

Graphics Strings

A graphics string (or GString ) is a collection of graphics commands; GStrings are useful for saving complex graphic operations and playing them back later. GStrings are created by calling a special routine and are then filled by executing graphics commands just as if you were drawing to the screen. GStrings may be pre-defined in source code as data resources to be played during execution.

GStrings are extremely useful for sharing graphic data between processes. For example, applications use GStrings to define their program icons, which may then be displayed by File Manager applications. The Clipboard supports the GString data format for cut, copy, and paste operations. GStrings are also flexible--they may contain comments and may be set up to be executed with parameters.

Bitmaps

Bitmaps are pixel-by-pixel images defined at a specific resolution; they are automatically scaled to match the resolution of the display. Bitmaps are used in general to define complicated pictures, usually with many colors.

GEOS allows the creation of bitmaps with specified resolutions. Bitmaps can be created off-screen and may be edited with standard graphics system commands. Bitmaps are used mainly as monikers of generic objects and as icons for applications and data files.

Color

GEOS allows applications to specify colors in two ways: with the system palette and with RGB values. The palette contains 256 entries, each of which is given a standard RGB value. If an application only uses 256 colors, it can modify the palette so it has the colors it needs. If an application uses more than 256 colors, it can specify any RGB value for any given graphics operation. The palette may also be expanded to support other color models. If the system palette is not sufficient, each application may create its own.

Overview: 6.6 System Services: Text

Text in GEOS is handled by the Text Object, a sophisticated and powerful object that is the base for both the GenText and the VisText classes. The Text Object handles more situations than most applications will ever face; only powerful word processors and text-formatting programs may require more functionality. Those that require additional features can subclass the text object and add those features.

The Text Object implements text editing, manipulation, and display for GEOS. It represents a major portion of the system software and as such should be used by all applications that plan on providing any text-editing features. Among its many features are

  • Display and Text Wrapping Within Editable Bounds
    The Text Object is aware of its visible bounds and will wrap text appropriately to fit within the bounds. If the Text Object is given a set width, it will wrap text appropriately to that width.
  • "Editable" and "Selectable" Attributes
    The Text Object allows users to select and edit text. It can also act as a display-only object or a display-with-select object.
  • Character and Paragraph Formatting
    Sophisticated and extensive formatting options are included with the Text Object such as: Font selection, text styles (underline, bold, italic, etc.), text size (from four to 792 points), adjustable track kerning, text color, justification, adjustable margins, tabs with settable features (such as leaders, lines, and justification), paragraph borders, paragraph background colors, and paragraph spacing. All of these formatting options are settable and changeable.
  • Full Keyboard Editing
    The Text Object implements all the features necessary for editing text with the keyboard.
  • Embedded Graphics
    The Text Object can hold embedded graphics and graphics strings and display them properly.
  • Support for the Clipboard and Quick-Transfer
    The Text Object automatically supports the quick-transfer mechanism of the UI and handles the cut, copy, and paste commands of the Edit menu.
  • Many other powerful features
    For full feature listings and how to use the text object, see the Text chapter.

Overview: 6.7 System Services: Print Spooler and Printing

GEOS contains a print spooler that manages printing for all applications in the system. The spooler executes in its own threads and manages a print output queue for each printer connected to the system. The spooler also provides standard user interface components such as the print dialog box. Applications can customize the print dialog box by adding their own gadgetry for certain types of printing (such as the customizations in GeoDex and GeoPlanner).

For the application, printing is essentially the same task as drawing to the screen; all the same graphics commands are used. The only difference is that the output is sent to a spool file rather than to the screen.

If the paper size in a user's printer is different from the size of the document being printed, the spooler will do all the calculations and operations necessary to print the document properly. For example, a document eleven inches by seventeen inches will be rotated and printed on two standard-sized sheets of paper.

Overview: 6.8 System Services: Timers

The GEOS kernel allows applications and libraries to set up different types of timers that can be used for various purposes. When a timer ticks, it calls a routine specified by the geode that initiated it. The different types of timers that can be set up include

one-shot
A timer that executes the specified routine once.
continual
A timer that calls the routine once every given time period.
message
Either a one-shot or continual timer that sends a specified message rather than call a specified routine.
millisecond
A one-shot timer with millisecond accuracy.
sleep
A timer that puts the calling thread to sleep for a given length of time.
semaphore
A timer that provides a "time-out" for a given semaphore queue (if the thread does not gain the semaphore in the given length of time, it is removed from the queue).

Overview: 6.9 System Services: Communications

Communications is becoming an increasingly important area of system services. GEOS provides programs with a number of means of communication, whether communicating with peripheral devices, other computers, or with another GEOS program.

  • IACP, the Inter-Application Communication Protocol provides a way to send highly-structured messages between applications. It is quite versatile, but not simple.
  • The PCCom library allows a GEOS device to exchange data and files with another device over a serial link. The other device must also have access to PCCom--as of this writing, this means that the device must be running either DOS or GEOS.
  • Streams are like one-way pipes that allow a thread to send data to or receive data from another thread or I/O port. Typically, streams are used when dealing with serial or parallel ports; however, they may also be used for communication between two threads. GEOS has a Stream Driver that manages input to and output from streams. Additionally, a Parallel Driver manages output to a parallel port, and a Serial Driver manages input from and output to a serial port.
  • The Socket library, not present on all GEOS systems, provides a socket communication API--an API analogous to WinSock or Unix sockets. It provides a simple interface for communicating between machines or between programs running on the same machine.

Overview: 6.10 System Services: Math support

GEOS has a number of routines that accomplish complex mathematical calculations. There are also routines that do matrix manipulation and complicated linear algebra operations. In addition, the system provides a library of routines to implement simple fixed-point math and complicated floating-point operations.

Overview: 6.11 System Services: International Support

International markets are extremely important to Geoworks due to the large number of PCs in use in countries other than the United States. Therefore, localization of applications was made a priority when designing the system software.

GEOS provides a number of tools for localizing your code. The most important is the GEOS application ResEdit, which allows a programmer to edit data resources. When an application is programmed correctly, translation can easily be accomplished by a support team.

The GEOS kernel also contains a number of routines that allow applications to access different DOS character sets. These routines ensure that an English-language program will work on machines with different DOS character sets.

Overview: 7 Libraries

The use of dynamically-linkable libraries is key to the efficiency of GEOS applications. Several libraries are included in addition to the libraries provided by the kernel (such as the Graphic Object Library and the Database Library, discussed earlier):

  • Import/Export Library
    The Impex Library ("impex") contains a number of utilities to convert standard data files into GEOS data files and vice versa.
  • Sound Library
    GEOS provides a number of standard sounds and a small library ("sound") to play individual notes on the PC's internal speaker.
  • Ruler Object Library
    The Ruler library ("ruler") provides a ruler that applications can use along with a View. It can be set to different measurement units and scales and scrolls itself along with the scale and position of the View.
  • Cell Library
    The Cell Library ("cell") provides the lowest-level data manipulation and access routines required by a typical database or spreadsheet application.
  • Parse Library
    The Parse Library ("parse") provides a parser for mathematical expressions.
  • CD ROM Library
    The CD ROM Library ("cdrom") provides all the routines necessary to interface with standard Microsoft CD ROM extensions.

Overview: 8 Device Drivers

GEOS does as much as possible to isolate applications from the hardware in use. Not only does this allow applications to run with thousands of different system configurations, but it also allows them to work properly with any new technology not yet developed--when a new device is introduced and a driver written for it, applications will automatically work with it (in most cases).

Device drivers are used by the kernel for all of the following:

  • Video Cards
    Most video cards with graphics are supported including HGC, CGA, EGA, VGA, and Super VGA.
  • Keyboards
    International keyboards are supported for most languages using available character sets.
  • Pen Input Devices
    New input devices such as digitizers, touch screens, and other types of devices are or can be easily supported by drivers that translate the input into standard mouse-and-keyboard style input.
  • Printers
    Standard 9-pin and 24-pin dot matrix printers as well as most laser printers are supported. PostScript output is also supported.
  • Mice
    Dozens of different mice are supported.
  • Serial and Parallel Ports
    Serial and parallel ports are driven by special types of Stream Drivers and can be used to create fax and other peripheral drivers.
  • File Systems
    File systems (MS-DOS and DR DOS, for example) are supported through file system drivers. Future releases of file systems and alternatives will be supported through the addition of new file system drivers.
  • Task Switchers
    GEOS interacts with DOS task-switchers through task switch drivers.
  • Power Management Hardware
    Many portable computers (notebooks and palmtops) use power management hardware to extend the life of their batteries. GEOS works with the power management systems through a power management system driver.
  • Font Rasterizers
    GEOS uses a combination of outline and bitmap fonts. Different font engines are supported through font rasterizers.
This document is a single-page version of a a multi-page document, suitable for easy printing.


Tutorial

This chapter shows how you might get started writing a simple GEOS program, and it explains several fundamental GEOS programming concepts as it goes along. Although this tutorial explicitly refers to the Nokia 9000 SDK version 1.1, it is also applies to the Nokia 9000i SDK version 2.0.

Getting Started with GEOS Programming
Tutorial
Tutorial Source Code
Adding Code and Debugging with Swat
Tutorial Source Code With Changes

Getting Started with GEOS Programming

Before jumping into writing your own application, it might be useful to compile and run an existing sample application. This chapter will take you through the generic steps of checking out a source file, compiling it, then running the compiled application on the emulator with the Swat debugger attached. The next two chapters explore GEOS development in greater detail by stepping through every step in creating the application "Tutorial". We recommend that you step through this chapter to acquaint yourself with the generic process of compiling an application, then carefully follow the tutorial to get a flavor for GOC, Swat debugging, and Geode Parameter files.

Typically, your development sequence will follow these steps:

  1. Check out a file by copying it to your Working Branch. This is your private development area. By doing your work here, the Branch files remain untouched (see "Developing In Your Working Branch" for more information);
  2. Create a Makefile using the mkmf utility;
  3. Compile your application using the pmake utility;
  4. Run the application on the error-checking version of the target emulator;
  5. Debug your application using the Swat debugger; and
  6. Edit your source code and go back to Step 3.

The next section follows this sequence using the Dialogs sample application, though of course the same steps apply for any sample application. (Refer to the release notes in the README.TXT file or the Troubleshooting guide in the "Installing the SDK" chapter if you encounter problems.)

In the directory paths below, prepend your correct drive letter or path to PCGEOS and replace "workingdir" as appropriate. Also note that if you are using the Nokia 9000i SDK version 2.0, the directory labled here as N9000v11 will be N9000v20 in your development branch.

  1. Install the SDK by following the instructions in the "Installing the SDK" chapter;
  2. Start a command window.
  3. Go to the \APPL\ subdirectory of your Working Directory:
    4. CD PCGEOS\workingdir\APPL
  4. Create and go to the subdirectory \SDK_9000\DIALOGS\:
    5. MD SDK_9000\DIALOGS
CD SDK_9000\DIALOGS
  5. Copy the file DIALOGS.GOC from the N9000v11 Branch to this directory:
    6. COPY \PCGEOS\N9000V11\APPL\SDK_9000\DIALOGS\DIALOGS.GOC
  6. Type mkmf to create the makefile.
  7. Type pmake to compile and link the executable. This will create an error-checking version called DIALOGSEC.GEO .
  8. Launch a wait-for-swat EC demo. You can do this from the GEOS SDK program menu if the shortcuts were installed; otherwise, CD to \PCGEOS\workingdir\TARGET\N9000.EC, then type start ss .
  9. Launch Swat. You can also do this from the GEOS SDK program menu; otherwise, type start pcgeos\bin\swat . (The full path is only necessary if there is also a Swat stub in your current directory, which you do not want to run.)
    • If Swat pauses with a top-level prompt (=>), type c to continue.
    • If Swat cannot file a symbol file, press Ctrl-W to clear the "Where is it" default; then type i (lower-case) to ignore that file.
    • If Swat is searching a very long time for a particular symbol file (Looking for "such-and-such"...), you can abort the search by typing Ctrl-C , then using the Ctrl-W , i sequence described above to ignore the file.
  10. When the emulator has finished loading (usually after "serial" exits), press Ctrl-C in the Swat window to bring command control back to Swat.
  11. Copy your geode (your compiled executable) to the target emulator by typing send dialogs .
  12. Type c to continue Swat.
  13. Click inside the emulator window to regain focus in that window. Press Ctrl-F12 to launch the Extras folder.
  14. Use the arrow keys to highlight the EC Dialogs application. Press F1 to launch it. (The keys F1 - F4 correspond to the menu list on the right of the emulator window.)
  15. Play with the application.
  16. Select the Swat window, press Ctrl-C to bring command control back to Swat, then type quit to exit the emulator.

In this section, you learned how to check out a source code file by copying it from the Branch directory to your Working Directory, and then compile and run it on the emulator. Notice that it was not necessary to copy all the files from the \DIALOGS\ subdirectory in the N9000v11 Branch. When compiling and linking, the tools are smart enough to look for files in your Working Directory first and then look for whatever other files it needs in the Branch directory. This allows you to copy only the file or files you need to work on; shared files can remain in the Branch directory.

Now that you've gone through the basic steps of compiling and running a GEOS application, you can go on to the next chapter to create your own application from scratch.

Tutorial

In this tutorial, you will create a simple GEOS application that draws text and demonstrates the basic components of a GEOS application. You'll learn how to compile and link the application and explore its source code using the Swat debugger.

1 Creating "Tutorial"
1.1 Setting Up Your Working Directory
1.2 Compiling Your Source Code
1.3 Starting the Emulator and Swat
2 Learning GOC
2.1 TUTORIAL.GP: Geode Parameters File
2.2 TUTORIAL.GOC: Source Code
3 Exploring With Swat
4 Source Code

1: Creating "Tutorial"

The application that you'll be working on throughout this tutorial is called, appropriately enough, "Tutorial." In this section you'll create two files: TUTORIAL.GP and TUTORIAL.GOC. The .GP (or "geode parameters") file tells the linker how the application is organized; the .GOC file contains the actual source code. All GEOS applications require these two files.

1.1: Creating "Tutorial": Setting Up Your Working Directory

The first step in creating your application is to set up your Working Directory where you will do all your private development. Because your application is called "Tutorial," you need to create a \TUTORIAL\ subdirectory in your Working Directory to hold the source files (see "Developing In Your Working Branch" for more information).

  1. Create the \TUTORIAL\ subdirectory under \APPL\ in your Working Directory; for example: C:\PCGEOS\workingdir\APPL\TUTORIAL\ where "workingdir" refers to your personal working directory (usually the same as your login name).
  2. Type in the contents of TUTORIAL.GP and TUTORIAL.GOC. Put these files into the ...\TUTORIAL\ directory you just created. Typing source code by hand is often a good way to learn the feel for a language's grammar. If you don't want to type the source code by hand, follow the above links and copy the text into the files TUTORIAL.GOC and TUTORIAL.GP, as indicated.
1.2: Creating "Tutorial": Compiling Your Source Code

Now that you've created some source code, it's time to compile your application. First, you will need to create a Makefile and then run the "make" or compilation utility.

  1. Start up a command window and make sure you are in your Working Directory for this application; i.e., C:\PCGEOS\workingdir\APPL\TUTORIAL\.
  2. Type mkmf at the prompt; this utility creates a "makefile" which tells the pmake utility the steps with which to construct the application. It also creates a dependencies file, which tells pmake which files need to be remade whenever a source file has been modified.
  3. Type pmake to invoke the preprocessor, compiler, and linker to create the GEOS executable.

When you have completed these steps, your directory should contain some new files, including TUTORIALEC.GEO and your newly created GEOS executable or "geode." The "EC" in the name indicates that you've compiled the error-checking version of your application. The GEOS SDK tools have been set up with some optional compilation features to allow for error-checking code which is useful during development.

1.3: Creating "Tutorial": Starting the Emulator and Swat

Once you've successfully compiled your code, it's time to start up the GEOS emulator and attach Swat, the debugger.

  1. From the GEOS SDK program group, start the error-checking version of the emulator with Swat-wait.
  2. From the GEOS SDK program group, launch Swat (or type "swat" from a command prompt). The following should appear in the Swat window:
    3. Swat version 2.1 (Feb 14 1997 19:05:55).
Using the N9000V11 version of GEOS.
SDK version: ntsdk30
Looking for "loader"...c:/pcgeos/N9000V11/Installed/Loader/Text/loaderec.sym
Sourcing swat.tcl...done
GEOS Attached
Stopped in LoadGeos, address 0a06h:0007h
LoadGeos:            CLD                        ;DF=0
(loader:0) 1 =>

    At this point, Swat is telling you that it has successfully established communications with the target emulator, has started the process of loading the GEOS kernel, and is now waiting for you to type in a command. Type c and press the <Enter> key to continue loading the GEOS emulator. The following will appear:

    Swat version 2.1 (Feb 14 1997 19:05:55).
pcgeosGEOS Attached
Stopped in LoadGeos, address 0a06h:0007h
LoadGeos:            CLD                        ;DF=0
(loader:0) 1 => save screen1.txt
Saving to c:/pcgeos/nfiedler/screen1.txt: 1000 lines
(loader:0) 2 => c
Looking for "geos    Eker"...c:/pcgeos/N9000V11/Installed/Library/Kernel/N9000DE
MO/geosec.sym
Looking for "os2     Eifs"...c:/pcgeos/N9000V11/Installed/Driver/IFS/DOS/OS2/os2
ec.sym
Looking for "netware Eifs"...c:/pcgeos/N9000V11/Installed/Driver/IFS/DOS/NetWare
/netwareec.sym
netware exited.
Looking for "respondrElib"...c:/pcgeos/N9000V11/Installed/Library/Respondr/N9000
DEMO/respondrec.sym
Looking for "rspwr   Edrv"...c:/pcgeos/N9000V11/Installed/Driver/Power/Rspwr/N90
00DEMO/rspwrec.sym
Thread 1 created for patient geos
Thread 2 created for patient geos
Looking for "vidmem  Edrv"...c:/pcgeos/N9000V11/Installed/Driver/Video/Dumb/VidM
em/vidmemec.sym
Looking for "swap    Elib"...c:/pcgeos/N9000V11/Installed/Library/Swap/swapec.sy
m
Looking for "disk    Edrv"...c:/pcgeos/N9000V11/Installed/Driver/Swap/Disk/diske
c.sym
Looking for "xms     Edrv"...c:/pcgeos/N9000V11/Installed/Driver/Swap/XMS/xmsec.
sym
Looking for "gdi     Elib"...c:/pcgeos/N9000V11/Installed/Library/GDI/GenPC/WIN3
2/gdiec.sym
Looking for "gdiKbd  Edrv"...c:/pcgeos/N9000V11/Installed/Driver/Keyboard/GDI/gd
iKbdec.sym
Looking for "nimbus  Edrv"...c:/pcgeos/N9000V11/Installed/Driver/Font/Nimbus/nim
busec.sym
Looking for "stream  Edrv"...c:/pcgeos/N9000V11/Installed/Driver/Stream/streamec
.sym
Looking for "sound   Elib"...c:/pcgeos/N9000V11/Installed/Library/Sound/N9000/so
undec.sym
Looking for "standarddrvr"...c:/pcgeos/N9000V11/Installed/Driver/Sound/Standard/
N9000DEMO/standard.sym
Looking for "ui      Elib"...c:/pcgeos/N9000V11/Installed/Library/User/N9000/uie
c.sym
Thread 0 created for patient ui
Looking for "styles  Elib"...c:/pcgeos/N9000V11/Installed/Library/Styles/N9000/s
tylesec.sym
Looking for "color   Elib"...c:/pcgeos/N9000V11/Installed/Library/Color/N9000/co
lorec.sym
Looking for "ruler   Elib"...c:/pcgeos/N9000V11/Installed/Library/Ruler/N9000/ru
lerec.sym
Looking for "text    Elib"...c:/pcgeos/N9000V11/Installed/Library/Text/N9000/tex
tec.sym
Looking for "rtcm    Elib"...c:/pcgeos/N9000V11/Installed/Library/RTCM/rtcmec.sy
m
Looking for "ansic   Elib"...c:/pcgeos/N9000V11/Installed/Library/AnsiC/ansicec.
sym
Looking for "vp      Elib"...c:/pcgeos/N9000V11/Installed/Library/Foam/OEM/vp_st
b/vpec.sym
Looking for "foam    Elib"...c:/pcgeos/N9000V11/Installed/Library/Foam/Foam/foam
ec.sym
Looking for "foamdb  Elib"...c:/pcgeos/N9000V11/Installed/Library/Foam/DB/foamdb
ec.sym
Looking for "contdb  Elib"...c:/pcgeos/N9000V11/Installed/Library/Foam/Contdb/co
ntdbec.sym
WARNING(foamdb::FoamDBVisibleRecordEnumWithRange): DB_ENUM_START_ELEMENT_TOO_LAR
GE
Looking for "securitylib "...c:/pcgeos/N9000V11/Installed/Library/Foam/Security/
security.sym
Looking for "rudy    Espu"...c:/pcgeos/N9000V11/Installed/Library/SpecUI/Rudy/ru
dyec.sym
Looking for "simp4bitEdrv"...c:/pcgeos/N9000V11/Installed/Driver/Video/Dumb/Simp
4Bit/WIN32/simp4bitec.sym
WARNING(rudy::OLAppEnsureIndicatorCorrect): RUDY_INDICATOR_NOT_FOUND_IS_GEOS_BOO
TING_OR_SHUTTING_DOWN
Looking for "spool   Elib"...c:/pcgeos/N9000V11/Installed/Library/Spool/N9000/sp
oolec.sym
Thread 0 created for patient spool
Looking for "mailbox Elib"...c:/pcgeos/N9000V11/Installed/Library/Mailbox/N9000/
mailboxec.sym
Thread 0 created for patient mailbox
Looking for "nonts   Edrv"...c:/pcgeos/N9000V11/Installed/Driver/Task/NonTS/nont
sec.sym
Looking for "contlog Elib"...c:/pcgeos/N9000V11/Installed/Library/Foam/ContLog/c
ontlogec.sym
Looking for "indicatoEapp"...c:/pcgeos/N9000V11/Installed/Appl/FApps/OEM/indicat
o/indicate.sym
Thread 0 created for patient indicato
Looking for "faxfile Elib"...c:/pcgeos/N9000V11/Installed/Library/Fax/File/N9000
/faxfileec.sym
Looking for "viewer  Elib"...c:/pcgeos/N9000V11/Installed/Library/Foam/Viewer/vi
ewerec.sym
Looking for "clrfax  Egeo"...c:/pcgeos/N9000V11/Installed/Appl/FApps/ClrFax/clrf
axec.sym
Thread 0 created for patient clrfax
Looking for "math    Elib"...c:/pcgeos/N9000V11/Installed/Library/Math/mathec.sy
m
Looking for "ssset   Elib"...c:/pcgeos/N9000V11/Installed/Library/Foam/OEM/ssset
/sssetec.sym
Looking for "scm     Elib"...c:/pcgeos/N9000V11/Installed/Library/Foam/OEM/scm/s
cmec.sym
Thread 0 created for patient scm
Looking for "compose Elib"...c:/pcgeos/N9000V11/Installed/Library/Foam/OEM/compo
se/composee.sym
Looking for "borlandcElib"...c:/pcgeos/N9000V11/Installed/Library/Math/Compiler/
BorlandC/borlandcec.sym
Looking for "phone   Eapp"...c:/pcgeos/N9000V11/Installed/Appl/FApps/OEM/phone/p
honeec.sym
Thread 0 created for patient phone
Thread 1 created for patient indicato
Thread 1 of indicato exited 0
viewer exited.
faxfile exited.
clrfax exited.
Thread 0 of clrfax exited 0
Looking for "serial  Edrv"...c:/pcgeos/N9000V11/Installed/Driver/Stream/Serial/s
erialec.sym
serial exited.
GEOS Halted
Stopped in DOSIdleHook, address 2796h:1136h
DOSIdleHook+17:      MOV     AX, 5760 (1680h)
(geos:0) 3 =>

    Each line that begins "Looking for..." indicates that the system is loading a geode (a GEOS executable) and that Swat is looking for its symbol information. Swat also tells you whenever it creates (or exits) a thread for any geode.

    Now that the emulator is running with Swat attached, you need to send your source code to the emulator to run it.

  3. Type Ctrl-C in the Swat window. At this point, the Swat prompt should appear.
  4. Type send tutorial to copy the application to the emulator.
  5. Type run tutorial to run the application.
  6. Type c to allow execution to continue on the emulator.

After you've followed these steps, your emulator should look like the screen pictured below.

Figure 1: The "Tutorial" application screen.

This is how your application will look after you type in the first pieces of code. Notice that it consists only of a blank viewing window and a "Close" button; later you will add code that displays text in this window.

As you can see, your application consists of an empty window. In the next chapter, you will add procedural code that draws a text string in this window.

2: Learning GOC

By typing in the contents of the source files (both the .GOC and .GP files), you've probably become a little familiar with GOC syntax. GOC is a proprietary superset of standard ANSI C. It supports C routines and functionality and adds its own set of keywords and special syntax. This section will cover basic GOC syntax, including the usage and meaning of various GOC keywords, by giving a line-by-line explanation of the code you entered.

2.1: Learning GOC: TUTORIAL.GP: Geode Parameters File

The .GP file tells the Glue linker about an application's general organization. (You can get complete information about all of the possible fields in a .GP file by reading the GP File Keywords reference.) Let's take a look at the code in TUTORIAL.GP to find out what each line means.

name tutorial.app

The name field specifies the "patient" name by which Swat will identify the application. Swat thinks of each geode as a patient, so you'll see phrases in Swat like "patient died".

longname "Tutorial Sample Application"

The longname field is the name the system displays for this application. For example, if you press Ctrl-F12 on the emulator (to list the applications in the "Extras" folder), you will see the longname for this application displayed. GEOS supports names up to 32 characters in length.

tokenchars "TUTO"
tokenid 8

Each application has its own unique identifier to the system. This identifier is a special GEOS data type called a GeodeToken. A GeodeToken is a data structure comprised of two fields:

tokenchars
A string of four characters (normally taken from the name of the application); and
tokenid
The Manufacturer ID for your company; this value is unique to your company and ensures that your applications' GeodeTokens do not conflict with those of other manufacturers. All sample applications, including this tutorial application, use a tokenid of 8; for your "real" applications, you should use your company's assigned Manufacturer ID. (Send email to orders@geoworks.com to receive a Manufacturer ID.)
type appl, process, single

The type line tells Glue whether your geode is an application, a driver or a library. Tutorial, like most GEOS applications, uses the following keywords:

  • appl which tells Glue that this geode is an application;
  • process which specifies that the geode should be run in its own thread;
  • single which specifies that only one copy of the program may run at a time.

Because GEOS is a multi-tasking or multi-threaded operating system, it allows more than one thread of execution to run at a time. This enables programs to run in the background. By specifying that your application has a process, you are requesting that it have its own thread of execution. If you were writing a library, you probably wouldn't want to give it a process, figuring that other geodes would be executing the library's code.

class TutorialProcessClass

The class line specifies which class will run the process thread. In this case, the TutorialProcessClass will be in charge of the process thread. (You will declare TutorialProcessClass in the TUTORIAL.GOC file.)

appobj TutorialApp

The appobj line specifies the "application object" for this application. The application object is the interface between your geode and the rest of the system. (You will declare this object in your source file as well.)

platform n9000v20

Glue will use the platform line to make sure that your application doesn't rely on any geodes that aren't present on the GEOS device.

heapspace 3K

The heapspace line specifies the maximum amount of memory the application requires on the global memory heap. Note that the value 3K is a wild guess. Once your application is closer to completion, you can use Swat's heapspace command to find out how much heap space the application really needs.

library geos
library ui

The library lines tell Glue which libraries to load for your application. All GEOS libraries are dynamically linked and loaded.

resource APPRESOURCE ui-object
resource INTERFACE ui-object

The resource lines specify the blocks of memory that your application uses for objects. Breaking up an application into resources allows it to run more efficiently because the system will only load those resources it needs.

The ui-object keyword signals that the UI thread should run the specified resource. Specifying a UI thread ensures that your application will have a UI thread and thus be dual-threaded: the process will run in one thread and UI objects will run in the UI thread. If you don't want the application to have two threads, use the keyword object instead of ui-object .

2.2: Learning GOC: TUTORIAL.GOC: Source Code

Now let's take a look at the source code in TUTORIAL.GOC . (For more information about GOC syntax, see the GEOS Programming chapter.)

@include <stdapp.goh>
@include <foam.goh>

The first lines of code contain the @include directive. Note the '@' symbol. This symbol is used to denote GOC keywords and distinguish them from regular C syntax. @include behaves the same way as #include, except that the inclusion will take place when the GOC preprocessor is processing the source code. Generally, you @include .GOH files and #include regular .H files.

@class TutorialProcessClass, GenProcessClass;
@endc;

The @class and @endc keywords mark the beginning and end of a new class definition. Here, you're defining TutorialProcessClass as a subclass of GenProcessClass , which means that TutorialProcessClass will inherit all the behavior of its parent, GenProcessClass .

@classdecl TutorialProcessClass, neverSaved;

Once you define the class, you need to declare it so that the system will know how to build objects of that class. To declare the class, you use the @classdecl keyword and because this is the process object, (a special object with no instance data), you also use the neverSaved flag. This flag tells the system that no instance data need be saved when the system shuts down.

 @start AppResource;

The @start keyword defines a resource or block of memory. In this case, you're defining the APPRESOURCE resource. In the . GP file, you may recall that this resource holds objects, and that those objects will be run by the UI thread.

@object FoamSubApplicationClass TutorialApp = {

The first object to be declared in the APPRESOURCE resource is the application object, TutorialApp . In the .GP file, you specified TutorialApp as the application object in the appobj line. The application object is responsible for receiving and handling all sorts of system messages and notifications. This is a nontrivial task so the object will be of a class specifically set up to handle this task: FoamSubApplicationClass . (Note, on GEOS systems other than the Nokia 9000i Communicator, you would use a GenApplicationClass; FoamSubApplicationClass is a specialized subclass of GenApplicationClass.)

Because the application object provides important information to the system, it needs to live in its own resource; that way, the system can query the application object by loading only that resource which contains it.

The next set of fields contains the application object's instance data. Instance data are characteristics of an object, as defined by the object's class. (This should make more sense as you read on.)

     GI_visMoniker = list{ @TutorialTextMoniker };

The GI_visMoniker field specifies the object's label or name that appears to the user (not to be confused with TutorialApp, the name of the object). The "visMoniker" part of the field name is short for "visual moniker." In GEOS parlance, a "visual moniker" is a text string or graphic used to identify an object to the user. This field is commonly set to a "list" of monikers; the system uses the moniker list to determine which text or graphic to display for your application.

      GI_comp = @TutorialPrimary;

The GI_comp field specifies the children of a generic UI object. In this case, the only child of the TutorialApp object is the TutorialPrimary object (which you declare later). (For a detailed description of how objects keep track of their children, see the GEOS Programming chapter.)

Notice that both instance data field names begin with "GI_." The "GI_" stands for "Gen Instance." Because GenApplicationClass is a subclass of GenClass , it inherits the GI_visMoniker field from its parent. By Geoworks' naming conventions, instance data field names begin with the initials of their associated class followed by an "I" for "instance." Thus, any GenApplicationClass instance field names would begin "GAI_."

You may be wondering about the syntax for @TutorialPrimary. @TutorialPrimary is actually a reference to the TutorialPrimary object. Object references have their own GEOS-specified type called an optr.

    gcnList( MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS ) =@TutorialPrimary;
}

This line puts the TutorialPrimary object,which is the primary window of your application, on a General Change Notification (GCN) list so that it will receive notification when a system-level change has occurred. (For more information about GCN lists, see the gcnList entry in the GEOS Programming chapter.)

This completes the declaration of the TutorialApp object.

@visMoniker TutorialTextMoniker = "My Tutorial";

This line contains the string to which the TutorialApp 's GI_visMoniker field refers. The @visMoniker keyword stores the string in a GEOS data structure set up strictly for monikers.

@localize "Program Title";

This @localize statement doesn't actually affect the program; it simply acts as a hint for someone translating the program to a foreign language. The ResEdit program displays the information in @localize statements when it prompts the translator to translate a moniker or string chunk. In this case, for example, when ResEdit prompts the translator to translate the string, "My Tutorial," it will display the string "Program Title" to assist with the translation.

@end AppResource

This line signals the end of the APPRESOURCE declaration.

@start Interface;

Here begins the next resource definition. This resource is called INTERFACE and will hold the basic UI gadgetry for your application.

@object GenPrimaryClass TutorialPrimary = {

The first object to be declared in this resource is TutorialPrimary , your application's primary window and an object of GenPrimaryClass .

    GI_comp = @TutorialView, @TutorialCloseTrigger;
}

The first instance data field is GI_comp , which specifies the children of TutorialPrimary .

@object GenViewClass TutorialView = {

The next object is a GenViewClass object called TutorialView ; this object provides space on the screen for objects to draw themselves.

    GVI_horizAttrs = @default |
        GVDA_NO_LARGER_THAN_CONTENT;
    GVI_vertAttrs  = @default |
        GVDA_NO_LARGER_THAN_CONTENT;
    GVI_content = process;
}

The GVI_content field specifies the top-level object which will appear in the view's drawing space. The GVI_horizAttrs and GVI_vertAttrs fields specify how the view should size itself in relation to the content object's size. Setting the flag GVDA_NO_LARGER_THAN_CONTENT ensures that the view will be the same size as its content object.

Notice that the GVI_...Attrs fields also use the @default keyword. @default tells the system to expand to the default values for this field (as defined by GenViewClass ); additional flags are ORed in with the single pipe ("|").

@object GenTriggerClass TutorialCloseTrigger = {
    GI_visMoniker = "Close";

The last object declared in the INTERFACE resource is TutorialCloseTrigger, an object of GenTriggerClass. TutorialCloseTrigger provides the "Close" button for your application.

    GTI_destination = @TutorialApp;
    GTI_actionMsg = MSG_FSA_RETURN_TO_LAUNCHER;

The GTI_destination and GTI_actionMsg fields are GenTrigger instance data fields. GTI_actionMsg specifies the message that gets sent when the user presses the "Close" button; GTI_destination specifies the object who will handle the message. (We will discuss messages in more detail in the next chapter; suffice it to say that a message is a signal which may be handled by a set of procedural code defined for a class.)

    HINT_SEEK_MENU_BAR;
    HINT_SEEK_REPLY_BAR;
    HINT_SEEK_SLOT = 3;
}

These hints ensure that the button is placed on the right side of the screen so that it aligns with the buttons on the Nokia 9000i Communicator.

@end Interface;

This line signals the end of the INTERFACE resource definition.

3: Exploring With Swat

Now that you've successfully compiled your application, you can use Swat to "debug" it. In the Swat window, press Ctrl-C to halt the emulator and gain control back in the Swat window. Something similar to the following should appear in your Swat window:

GEOS Halted
Stopped in DOSIdleHook, address 277eh:1136h
DosIdleHook+17		MOVE	AX, 5760 (1680h)
(geos:0) 5 =>

"GEOS Halted" indicates that Swat has frozen the emulator. The next two lines of Swat output indicate the address of the current internal routine that is executing. If Swat stopped in a public routine, the address would have been given in relation to the routine name and would have been more readable.

The last line indicates the thread that was executing (on the emulator) when you hit Ctrl-C . In this example, the zeroth thread associated with the GEOS geode (the kernel) was executing. To examine the thread associated with your application, type "tutorial." (Recall that this is the value you set in the name field of your TUTORIAL.GP file [without the suffix].) This tells Swat which thread to switch to.

(geos:0) 5 => tutorial
[tutorial:0] 6 =>

The prompt indicates that you are now in the process thread of the tutorial geode. If you were in the UI thread, your prompt would read "[tutorial:1]." To change threads within a geode, simply type :0 or :1 .

Now try the where command to find out what this thread was doing when you stopped the system.

[tutorial:0] 6 => where
* 1: near BlockOnLongQueue(), 153dh:c22ah
2:  far QueueGetMessage(), 153dh:133ch
------------------------------------------------------------------------------
The event queue for "tutorial:0" is empty
==============================================================================
[tutorial:0] 7 =>

These lines tells you that the tutorial thread is idling; it's waiting for a message or something to happen.

Next, use the gentree command to examine your application's generic UI object tree.

[tutorial:0] 7 => gentree *TutorialPrimary
*TutorialPrimary::GenPrimary (@1, ^l5230h:001eh) "My Tutorial"
    *TutorialView::GenViewClass (@2, ^l5230h:0020h)
    ^143b0h:002ah(GenValueClass) (@3, ^15230h:002ah)
    *TutorialCloseTrigger(GenTriggerClass) (@4, ^15230h:0024h) "Close"
[tutorial:0] 8=>

The gentree command prints out a list of all the objects in the tree, along with their class, object references, and monikers. For an example, let's look at the information for the TutorialView object:

  • GenViewClass is its class;
  • @2 is a shortcut for its object reference; and
  • ^l5230h:0020h is its object reference.

You can refer to an object by a shortcut that Swat provides, namely, the "@number." To look at an object's instance data, use the Swat command pobj followed by the "@number" designation for the particular object. Try the pobj command with the TutorialView object.

[tutorial:0] 8 => pobj @2
*TutorialView{GenViewClass} (@5, ^l5230h:0020h)
master part: Gen_offset(131) -- ui::GenViewInstance
@6: {ui::GenViewInstance (^h21040:502)+131} = {
    GenInstance GenView_metaInstance = {
        MetaBase Gen_metaInstance = {
            ClassStruct _far *MB_class = 3916h:1816h (rudy::CommonUIClassStructu
res::OLPaneClass)
        }
         LinkPart GI_link = {
            void _optr LP_next = *TutorialCloseTrigger{GenTriggerClass} (@7, ^l5
230h:0024h)
        }
        CompPart GI_comp = {
            void _optr CP_firstChild = ^l5230h:002ah{GenValueClass} (@8, ^l5230h :002ah)
        }
        void _lptr GI_visMoniker = null
        KeyboardShortcut GI_kbdAccelerator = {
            KS_PHYSICAL = 0h
            KS_ALT = 0h
            KS_CTRL = 0h
            KS_SHIFT = 0h
            KS_CHAR_SET = 0h
            KS_CHAR = C_NULL
        }
        GenAttrs GI_attrs = {GA_TARGETABLE}
        GenStates GI_states = {GS_USABLE, GS_ENABLED}
    }
    PointDWFixed GVI_origin = {
        DWFixed PDF_x = {0.000000}
        DWFixed PDF_y = {0.000000}
    }
    RectDWord GVI_docBounds = {
        long RD_left = 0
        long RD_top = 0
        long RD_right = 0
        long RD_bottom = 0
    }
    PointDWord GVI_increment = {
        long PD_x = +20
        long PD_y = +15
    }
    PointWWFixed GVI_scaleFactor = {
        WWFixed PF_x = {1.000000}
        WWFixed PF_y = {1.000000}
    }
    ColorQuad GVI_color = {
        byte CQ_redOrIndex = fh
        ColorFlag CQ_info = CF_INDEX
        byte CQ_green = 0h
        byte CQ_blue = 0h
    }
    GenViewAttrs GVI_attrs = {GVA_NO_WIN_FRAME, GVA_FOCUSABLE}
    GenViewDimensionAttrs GVI_horizAttrs = {GVDA_DONT_DISPLAY_SCROLLBAR, GVDA_NO_LARGER_THAN_CONTENT}
    GenViewDimensionAttrs GVI_vertAttrs = {GVDA_NO_LARGER_THAN_CONTENT}
    GenViewInkType GVI_inkType = GVIT_PRESSES_ARE_NOT_INK
    void _optr GVI_content = tuto1:0{TutorialProcessClass}(0000h) (@9, ^l56c0h:0
000h)
    void _optr GVI_horizLink = null
    void _optr GVI_vertLink = null
}
Variable Data:
         *** No Variable Data ***
[tutorial:0] 9 =>

We certainly got a lot of data from that command!

*TutorialView{GenViewClass} (@5, ^l5230h:0020h)

The first line gives the object's class, shortcut and pointer information, similar to the output from gentree. The rest of the output consists of the object's instance data. For example:

GenViewDimensionAttrs GVI_horizAttrs = { GVDA_DONT_DISPLAY_SCROLLBAR,
GVDA_NO_LARGER_THAN_CONTENT}

These lines show which flags (of type GenViewDimensionAttrs) are set for the object's GVI_horizAttrs instance data field. Notice that Swat displays both the flag set in your source code, GVDA_NO_LARGER_THAN_CONTENT, as well as the default flag set by the system, GVDA_DONT_DISPLAY_SCROLLBAR.

You may recognize some of the instance data fields from your source code. Other fields you may not recognize because you didn't specify values for these fields, allowing the system to use default values instead.

With pobj , you took advantage of another Swat feature: command completion. There is no "pobj" Swat command; the command's name is really "pobject." Because "pobject" was the only Swat command that begins with p-o-b-j, Swat realized what you meant when you used "pobj."

Next let's try a series of exploratory Swat commands:

[tutorial:0] 9 => classes
TutorialProcessClass (@10, 42b8h:0040h), off ui::UserClassStructures::GenProcessClass
[tutorial:0] 10 =>

The classes command lists all classes created by the application. To view the class hierarchy for a particular class, use the cup ("class up") command.

[tutorial:0] 10 => cup @10
dgroup::TutorialProcessClass (@11, 42b8h:0040h)
ui::UserClassStructures::GenProcessClass (@12, 30c4h:11e6h)
geos::kcode::ProcessClass (@13, 153dh:a9d5h)
geos::kcode::MetaClass (@14, 153dh:a8edh)
[tutorial:0] 11 =>

As shown above, you can use the shortcut reference (the "@number") with the cup command or you can use the class name or reference address, e.g., cup TutorialProcessClass or cup 42b8h:0040h.

Some other Swat commands you might want to try out:

help
The help command provides documentation on Swat commands. Try help stop .
apropos
The apropos command allows you to search the Swat documentation by keyword. Try apropos instance .
heapspace
The heapspace command tells you the amount of memory your application requires; you may recall that this value goes in your . GP file on the heapspace line. Because this value should reflect a worse-case scenario, it's important to run this command several times towards the end of development so that the figure is accurate.
handles tutorial
The handles command scans the entire memory heap and returns information about all handles used by the application.

In the next chapter, you'll begin by editing the source code, so prepare to switch over to your editor window.

Tutorial Source Code

Here is the source code for the application discussed in this chapter. The source code is in two files:

  • TUTORIAL.GP, the Glue Parameters file
  • TUTORIAL.GOC, the Goc code

TUTORIAL.GP

# Permanent name: This is required by Glue to set the permanent name
# and extension of the geode. The permanent name of a library is what
# goes in the imported library table of a client geode (along with the
# protocol number). It is also what Swat uses to name the patient.
#
name     tutorial.app
# Long filename: this name can displayed by the system. "EC " is
# prepended to this when the error-checking version is linked by Glue.
#
longname "Tutorial Sample Application"
# Token: The four-letter name is used by the system to locate the
# icon for this application in the token database. The tokenid
# number corresponds to the manufacturer ID of the program's author
# for uniqueness of the token. Since this is a sample application, we
# use the manufacturer ID for the SDK, which is 8.
#
tokenchars "TUTO"
tokenid    8
# Specify geode type: This geode is an application, and will have
# its own process (thread).
#
type   appl, process, single
# Specify class name for application thread. Messages sent to the
# application thread (a.k.a. "process" when specified as the output
# of a UI object) will be handled by this process class.
#
class  TutorialProcessClass
# Specify application object. This is the object that serves as
# the top-level UI object in the application.
#
appobj TutorialApp
# Specify for which platform this application will be compiled.
# Glue uses this to make sure that the libraries we are using
# (listed below) are available for this platform.
# In this case, the specified platform is the Nokia 9000i Communicator
#
platform n9000v20
# Heapspace: This is roughly the non-discardable memory usage
# (in paragraphs (16 bytes/paragraph)) of the application and any
# transient libraries that it depends on, plus an additional
# amount for thread activity. To find the heapspace for an
# application, use the Swat "heapspace" command.
#
heapspace 3K
# Libraries: list which libraries are used by the application.
# The foam library is for the Nokia 9000i Communicator only.
#
library geos
library ui
library foam
# Resources: list all resource blocks which are used by the
# application whose allocation flags can't be inferred by Glue.
# Usually this is needed only for object blocks, fixed code
# resources, or data resources that are read-only. Standard
# discardable code resources do not need to be mentioned.
#
resource APPRESOURCE ui-object
resource INTERFACE   ui-object

TUTORIAL.GOC

/********************************************************************
 *		Include Files
 *******************************************************************/

@include <stdapp.goh>
@include <foam.goh>
/********************************************************************
 *		Class Definitions
 *******************************************************************/
    /*
     * Here we define "TutorialProcessClass" as a subclass of the
     * system provided "GenProcessClass". As this application is
     * launched, an instance of this class will be created, and
     * will handle all application-related events (messages). The
     * application thread will be responsible for running this
     * object, meaning that whenever this object handles a message,
     * we will be executing in the application thread.
     *
     * You will find no object in this file declared to be of this
     * class. Instead, it is specified as the class for the
     * application thread in "tutorial.gp".
     */

@class TutorialProcessClass, GenProcessClass;
    /*
     * Define messages for this class here. Most commonly, UI
     * objects will send these messages to the application thread
     * when they've been activated by the user.
     */

@endc; /* end of class definition */
/********************************************************************
 *		Class Declarations
 *******************************************************************/
    /*
     * Each object class requires a "class record" be stored in
     * fixed memory for it. The class record is used by the GEOS
     * message system for the delivery of messages sent to an
     * object of the class. The class record needs to be defined
     * in the file where the objects of that class are defined.
     * The @classdecl GOC directive tells GOC to create the class
     * record here.
     *
     * The neverSaved flag is necessary because ProcessClass
     * objects are hybrid objects. It tells the system that no
     * object of this class will ever be loaded from or stored to
     * a state file.
     */

@classdecl TutorialProcessClass, neverSaved;
/********************************************************************
 *		UI Object Resources
 *******************************************************************/
/********************************************************************
 *		AppResource Resource
 *******************************************************************/

@start AppResource;
/*
    *    Application Object
    *
    * The very top-level generic object of an application MUST be
    * a GenApplication object. The tutorial.gp file contains the "appobj"
    * statement which indicates that this "TutorialApp" object is in
    * fact the top-level UI object.
    *
    * This object should be in its own resource, as it is all that
    * need be in memory when the application is iconified. As such,
    * its resource should be as small as possible.
    *
    * FoamSubApplicationClass is for the Nokia 9000i Communicator
    * only. It adds special behavior for that platform.
    */

@object FoamSubApplicationClass TutorialApp = {

        /*
         * While the application object isn't shown on the screen,
         * it has a list of VisMonikers for three purposes:
         *   1) The GenPrimary (see below) inherits the moniker
         *      when it comes up on screen.
         *   2) When GeoManager needs to display an icon for the
         *      application, and there's none for it in the Token
         *      Database, it launches the app and sends a message
         *      to the application object to install its list of
         *      monikers in the token database under the
         *      application's token.
         *   3) When the GenPrimary is minimized, it uses the most
         *      appropriate graphical moniker from this list for the
         *      application's icon. If there's an abbreviated-text
         *      moniker in the list, that's used as the icon title.
         *      Else any regular-text moniker is used.
         */
    GI_visMoniker = list { @TutorialTextMoniker };
        /*
         * The GenApplication object has at least one child:
         * the main application window.
         */
    GI_comp = @TutorialPrimary;
        /*
         * The main window should appear on-screen when the application
         * is launched, so place it on the GAGCNLT_WINDOWS GCN list.
         * This causes the application object to bring it on-screen at
         * the appropriate time.
         */
    gcnList( MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS ) = @TutorialPrimary;
}
@visMoniker TutorialTextMoniker = "My Tutorial";
@localize "Program Title";
@end AppResource;
/********************************************************************
 *		Interface Resource
 * This resource is for miscellaneous UI objects.
 *******************************************************************/
@start Interface;
    /*
     *    Primary window
     *
     * Every application has at least one GenPrimary object.
     * This object serves as the main window of the application.
     * Inside this window, various UI components, such as menus
     * or lists, can be placed.
     */

@object GenPrimaryClass TutorialPrimary  = {
        /*
         * This window has two children:
         * the GenView object and the Close button (see below).
         */
    GI_comp = @TutorialView, @TutorialCloseTrigger;
}
    /*
     *    GenView object
     *
     * This GenView object creates a window where the application can
     * display portions of the document as necessary. As we want this
     * window to be scrollable, the specific UI will create scroll
     * bars with which the user can interact. Whenever a portion of
     * the window needs to be redrawn, the GenView object will
     * invalidate a portion of the window, causing a MSG_META_EXPOSED
     * to be sent to the application. The application will draw the
     * document into the window as requested. The window keeps track
     * of a mask, which is used to clip the application's drawing
     * operations, so that only the invalid portion of the window is
     * drawn to.
     */
@object GenViewClass TutorialView = {

        /*
         * Make sure the view never gets larger
         * than the thing being displayed within it.
         */
    GVI_horizAttrs = @default | GVDA_NO_LARGER_THAN_CONTENT;
    GVI_vertAttrs = @default | GVDA_NO_LARGER_THAN_CONTENT;

        /*
         * When the contents of this view (i.e. the document) must be
         * updated, the UI should send a MSG_META_EXPOSED to the
         * TutorialProcessClass object.
         */
    GVI_content = process;
};
    /*
     *    Close trigger
     *
     * On certain systems we should provide a way to exit the
     * application. On the Nokia 9000i Communicator we have a
     * close trigger like this one.
     */
@object GenTriggerClass TutorialCloseTrigger = {
    GI_visMoniker = "Close";

        /*
         * The next two instance fields are part of the
         * GenTriggerClass. They specify the recipient object
         * and the message that will be sent to that object
         * when this trigger is activated.
         */
    GTI_destination = @TutorialApp;
    GTI_actionMsg = MSG_FSA_RETURN_TO_LAUNCHER;

        /*
         * The specific UI uses this combination of hints
         * to place this trigger on the right side of the
         * Nokia 9000i Communicator's screen.
         */
    HINT_SEEK_MENU_BAR;
    HINT_SEEK_REPLY_BAR;
    HINT_SEEK_SLOT = 3;
}
@end Interface;

Adding Code and Debugging with Swat

Tutorial Source Code With Changes

Here is the source code for the application discussed in this chapter. Pieces of the code which have been added or changed from that in the previous chapter are called out in bold.

  • TUTORIAL.GP
  • TUTORIAL.GOC

TUTORIAL.GP

# Permanent name: This is required by Glue to set the permanent name
# and extension of the geode. The permanent name of a library is what
# goes in the imported library table of a client geode (along with the
# protocol number). It is also what Swat uses to name the patient.
#
name     tutorial.app
# Long filename: this name can displayed by the system. "EC " is
# prepended to this when the error-checking version is linked by Glue.
#
longname "Tutorial Sample Application"
# Token: The four-letter name is used by the system to locate the
# icon for this application in the token database. The tokenid
# number corresponds to the manufacturer ID of the program's author
# for uniqueness of the token. Since this is a sample application, we
# use the manufacturer ID for the SDK, which is 8.
#
tokenchars "TUTO"
tokenid    8
# Specify geode type: This geode is an application, and will have
# its own process (thread).
#
type   appl, process, single
# Specify class name for application thread. Messages sent to the
# application thread (a.k.a. "process" when specified as the output
# of a UI object) will be handled by this process class.
#
class  TutorialProcessClass
# Specify application object. This is the object that serves as
# the top-level UI object in the application.
#
appobj TutorialApp
# Specify for which platform this application will be compiled.
# Glue uses this to make sure that the libraries we are using
# (listed below) are available for this platform.
# In this case, the specified platform is the Nokia 9000i Communicator
#
platform n9000v20
# Heapspace: This is roughly the non-discardable memory usage
# (in paragraphs (16 bytes/paragraph)) of the application and any
# transient libraries that it depends on, plus an additional
# amount for thread activity. To find the heapspace for an
# application, use the Swat "heapspace" command.
#
heapspace 3K
# Libraries: list which libraries are used by the application.
# The foam library is for the Nokia 9000i Communicator only.
#
library geos
library ui
library foam
# Resources: list all resource blocks which are used by the
# application whose allocation flags can't be inferred by Glue.
# Usually this is needed only for object blocks, fixed code
# resources, or data resources that are read-only. Standard
# discardable code resources do not need to be mentioned.
#
resource APPRESOURCE ui-object
resource INTERFACE   ui-object
#
# Any classes that we define in our application must be exported
# here for glue to process them.
#
export TutorialViewClass

TUTORIAL.GOC

/********************************************************************
 *		Include Files
 *******************************************************************/
    @include <stdapp.goh>
    @include <foam.goh>
/********************************************************************
 *		Constants
 *******************************************************************/
      /*
       * These constants are used in the TutorialDraw function.
       */
    #define TEXT_POINT_SIZE  48.0 /* point size */
    #define TEXT_ROTATION   -15   /* angle of rotation (degrees) */
    #define TEXT_X_POSITION  30   /* x position, in document coords */
    #define TEXT_Y_POSITION   0   /* y position, in document coords */
/********************************************************************
 *		Class Definitions
 *******************************************************************/
      /*
       * Here we define "TutorialProcessClass" as a subclass of the
       * system provided "GenProcessClass". As this application is
       * launched, an instance of this class will be created, and
       * will handle all application-related events (messages). The
       * application thread will be responsible for running this
       * object, meaning that whenever this object handles a message,
       * we will be executing in the application thread.
       *
       * You will find no object in this file declared to be of this
       * class. Instead, it is specified as the class for the
       * application thread in "tutorial.gp".
       */
    @class TutorialProcessClass, GenProcessClass;
      /*
       * Define messages for this class here. Most commonly, UI
       * objects will send these messages to the application thread
       * when they've been activated by the user.
       */
    @endc; /* end of class definition */

      /*
       * We subclass GenViewClass here because we need to
       * intercept a message for our GenView object.
       */
    @class TutorialViewClass, GenViewClass;
    @endc; /* end of class definition */
/********************************************************************
 *		Class Declarations
 *******************************************************************/
      /*
       * Each object class requires a "class record" be stored in
       * fixed memory for it. The class record is used by the GEOS
       * message system for the delivery of messages sent to an
       * object of the class. The class record needs to be defined
       * in the file where the objects of that class are defined.
       * The @classdecl GOC directive tells GOC to create the class
       * record here.
       *
       * The neverSaved flag is necessary because ProcessClass
       * objects are hybrid objects. It tells the system that no
       * object of this class will ever be loaded from or stored to
       * a state file.
       */
    @classdecl TutorialProcessClass, neverSaved;
    @classdecl TutorialViewClass;
/********************************************************************
 *		UI Object Resources
 *******************************************************************/

/********************************************************************
 *		AppResource Resource
 *******************************************************************/
@start AppResource;

  /*
   *    Application Object
   *
   * The very top-level generic object of an application MUST be
   * a GenApplication object. The tutorial.gp file contains the "appobj"
   * statement which indicates that this "TutorialApp" object is in
   * fact the top-level UI object.
   *
   * This object should be in its own resource, as it is all that
   * need be in memory when the application is iconified. As such,
   * its resource should be as small as possible.
   *
   * FoamSubApplicationClass is for the Nokia 9000i Communicator
   * only. It adds special behavior for that platform.
   */
@object FoamSubApplicationClass TutorialApp = {
      /*
       * While the application object isn't shown on the screen,
       * it has a list of VisMonikers for three purposes:
       *   1) The GenPrimary (see below) inherits the moniker
       *      when it comes up on screen.
       *   2) When GeoManager needs to display an icon for the
       *      application, and there's none for it in the Token
       *      Database, it launches the app and sends a message
       *      to the application object to install its list of
       *      monikers in the token database under the
       *      application's token.
       *   3) When the GenPrimary is minimized, it uses the most
       *      appropriate graphical moniker from this list for the
       *      application's icon. If there's an abbreviated-text
       *      moniker in the list, that's used as the icon title.
       *      Else any regular-text moniker is used.
       */
    GI_visMoniker = list { @TutorialTextMoniker };

      /*
       * The GenApplication object has at least one child:
       * the main application window.
       */
    GI_comp = @TutorialPrimary;

      /*
       * The main window should appear on-screen when the application
       * is launched, so place it on the GAGCNLT_WINDOWS GCN list.
       * This causes the application object to bring it on-screen at
       * the appropriate time.
       */
    gcnList( MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS ) = @TutorialPrimary;
}

@visMoniker TutorialTextMoniker = "C Tutorial";
@localize "Program Title";

@end AppResource;
/********************************************************************
 *		Interface Resource
 * This resource is for miscellaneous UI objects.
 *******************************************************************/
@start Interface;

  /*
   *    Primary window
   *
   * Every application has at least one GenPrimary object.
   * This object serves as the main window of the application.
   * Inside this window, various UI components, such as menus
   * or lists, can be placed.
   */
@object GenPrimaryClass TutorialPrimary  = {
        /*
         * This window has two children:
         * the GenView object and the Close button (see below).
         */
    GI_comp = @TutorialView, @TutorialCloseTrigger;
}

  /*
   *    GenView object
   *
   * This GenView object creates a window where the application can
   * display portions of the document as necessary. As we want this
   * window to be scrollable, the specific UI will create scroll
   * bars with which the user can interact. Whenever a portion of
   * the window needs to be redrawn, the GenView object will
   * invalidate a portion of the window, causing a MSG_META_EXPOSED
   * to be sent to the application. The application will draw the
   * document into the window as requested. The window keeps track
   * of a mask, which is used to clip the application's drawing
   * operations, so that only the invalid portion of the window is
   * drawn to.
   */
@object TutorialViewClass TutorialView = {
      /*
       * Make view scrollable in X and Y and ensure that it never
       * gets larger than the thing being displayed within it.
       */
    GVI_horizAttrs = @default | GVDA_SCROLLABLE
                              | GVDA_NO_LARGER_THAN_CONTENT;
    GVI_vertAttrs  = @default | GVDA_SCROLLABLE
                              | GVDA_NO_LARGER_THAN_CONTENT;

      /*
       * When the contents of this view (i.e. the document) must be
       * updated, the UI should send a MSG_META_EXPOSED to the
       * TutorialProcessClass object.
       */
    GVI_content = process;

      /*
       * Specify the dimensions of the document being displayed
       * within the view.
       */

    GVI_docBounds = {
        0,
        0,
        1000,
        1000
    };
}
    /*
     *    Close trigger
     *
     * On certain systems we should provide a way to exit the
     * application. On the Nokia 9000i Communicator we have a
     * close trigger like this one.
     */
@object GenTriggerClass TutorialCloseTrigger = {
    GI_visMoniker = "Close";

        /*
         * The next two instance fields are part of the
         * GenTriggerClass. They specify the recipient object
         * and the message that will be sent to that object
         * when this trigger is activated.
         */
    GTI_destination = @TutorialApp;
    GTI_actionMsg = MSG_FSA_RETURN_TO_LAUNCHER;

        /*
         * The specific UI uses this combination of hints
         * to place this trigger on the right side of the
         * Nokia 9000i Communicator's screen.
         */
    HINT_SEEK_MENU_BAR;
    HINT_SEEK_REPLY_BAR;
    HINT_SEEK_SLOT = 3;
}
@end Interface;
/********************************************************************
 *		Function Definitions
 *******************************************************************/

/********************************************************************
 *              TutorialDraw
 ********************************************************************
 * SYNOPSIS:	 Redraws the entire document, given a graphics state
 *		 through which to draw.
 * CALLED BY:	 (INTERNAL) TutorialProcess::MSG_META_EXPOSED
 * RETURNS:      void
 * SIDE EFFECTS: Various aspects of the gstate are altered.
 * STRATEGY:     First set the font used by this gstate. Then
 *               set the text color. Using the constant angle lets
 *               rotate the gstate. To finish up, draw the text
 *               text to the gstate.
 *******************************************************************/
void

TutorialDraw( GStateHandle gstate )
  /*
   * gstate - GState to draw to.
   */
{

      /*
       * First change some of the default GState values,
       * such as font and point size.
       */
    GrSetFont( gstate, FID_DTC_URW_SANS, MakeWWFixed( TEXT_POINT_SIZE ) );

      /*
       * Apply a rotation to the transformation matrix,
       * so the text will be drawn at an angle.
       */
    GrApplyRotation( gstate, MakeWWFixed( TEXT_ROTATION ) );

      /*
       * Draw some text onto the document.
       */
    GrDrawText( gstate, TEXT_X_POSITION, TEXT_Y_POSITION,
                "The quick brown fox jumps over the lazy dog.",
                0 );
} /* TutorialDraw */
/********************************************************************
 *		Code for TutorialProcessClass
 *******************************************************************/

/********************************************************************
 *              MSG_META_EXPOSED
 ********************************************************************
 * SYNOPSIS:	 Redraw the recently-exposed portion of the View.
 *               See the Objects Reference book for more information
 *               on this message and its parameters.
 * PARAMETERS:	 WindowHandle win
 * RETURNS:      void
 * SIDE EFFECTS: The invalid region of the window is cleared out.
 * STRATEGY:	 This message is sent by the windowing system when a
 *	    	 portion of the GenView has become invalid, either
 *	    	 because a window that was obscuring it has been moved,
 *	    	 or because someone called GrInvalRect. We redraw the
 *               entire document, after telling the graphics system
 *               we're drawing to the invalid portion of the window.
 *
 *******************************************************************/

@method TutorialProcessClass, MSG_META_EXPOSED
{
      /*
       * gstate - This is a handle to the graphics state
       *          that we'll use to draw to the view.
       */
    GStateHandle gstate;

      /*
       * Get a default graphics state that we can use while drawing.
       */
    gstate = GrCreateState( win );

      /*
       * Start a window update. This tells the windowing system
       * that we are in the process of drawing to the invalid portion
       * of this window. GrBeginUpdate/GrEndUpdate are necessary only
       * when handling MSG_META_EXPOSED. For drawing at other times,
       * all you need do is create a graphics state and draw through it.
       */
    GrBeginUpdate( gstate );

      /*
       * Draw the pattern into the window (pass the GState). Breaking
       * the exposure and general drawing into two routines allows the
       * display to be updated by other routines without having to
       * invalidate the window.
       */
    TutorialDraw( gstate );

      /*
       * Now indicate that we are done drawing to the invalid area
       * and free the gstate.
       */
    GrEndUpdate( gstate );
    GrDestroyState( gstate );
} /* MSG_META_EXPOSED */

/* This is here to work around a bug in the Borland C 5.0 compiler. */
@method TutorialProcessClass, MSG_GEN_PROCESS_CLOSE_APPLICATION
{

    return( @callsuper() );
}
/********************************************************************
 *		Code for TutorialViewClass
 *******************************************************************/

/********************************************************************
 *              MSG_META_KBD_CHAR
 ********************************************************************
 * SYNOPSIS:     We intercept this message to add the scrolling
 *               behavior to the GenView. Normally this would work
 *               in GEOS but the Nokia 9000i Communicator has the
 *               scrolling disabled in GenViewClass.
 * PARAMETERS:   word character
 *               word flags
 *               word state
 * RETURNS:      void
 * SIDE EFFECTS: none
 * STRATEGY:     We first check the parameters to see if this
 *               keypress is one we are interested in. It has to be
 *               a "control" key, and must not be a key release
 *               (i.e. the user released the key after holding it).
 *               If both of these conditions are true we can then
 *               use a switch statement to see which key is being
 *               pressed and send the appropriate message to the
 *               GenView object.
 *******************************************************************/

@method TutorialViewClass, MSG_META_KBD_CHAR
{
      /*
       * theChar - A copy of the character with only the
       *           low byte. The high byte of character
       *           has some extra flags we don't need here.
       */
    byte        theChar;

      /*
       * Arrow keys and scroll buttons are control characters.
       * We handle first press and repeat presses of those keys.
       */
    if ( ( ( character >> 8 ) == CS_CONTROL ) &&
         ( flags & ( CF_FIRST_PRESS | CF_REPEAT_PRESS ) ) ) {
        theChar = character & 0xFF;

        switch ( theChar ) {

        case VC_PREVIOUS:
              /* This handles the Page Up key. */
            @send self::MSG_GEN_VIEW_SCROLL_PAGE_UP();
            break;

        case VC_UP:
              /* This handles the up arrow key. */
            @send self::MSG_GEN_VIEW_SCROLL_UP();
            break;

        case VC_NEXT:
              /* This handles the down arrow key. */
            @send self::MSG_GEN_VIEW_SCROLL_PAGE_DOWN();
            break;

        case VC_DOWN:
              /* This handles the up arrow key. */
            @send self::MSG_GEN_VIEW_SCROLL_DOWN();
            break;

        case VC_RIGHT:
              /* This handles the right arrow key. */
            @send self::MSG_GEN_VIEW_SCROLL_RIGHT();
            break;

        case VC_LEFT:
              /* This handles the left arrow key. */
            @send self::MSG_GEN_VIEW_SCROLL_LEFT();
            break;

        default:

              /*
               * Pass on other control chars to default handler.
               */
            @callsuper();
        } /* switch */
    } else {
          /*
           * Pass on all other chars to default handler.
           */
        @callsuper();
    }
} /* MSG_META_KBD_CHAR */
This document is a single-page version of a a multi-page document, suitable for easy printing.


General Change Notification

In a multitasking environment, threads may need to know of condition changes that might affect them. In most cases where shared resources or multiple threads of execution exist, processes and objects must be sure of the integrity of data that they depend on and must be sent notice when that data changes.

In GEOS, this functionality is provided through the General Change Notification (GCN) mechanism. Although one could set up messages between processes and objects manually, the GCN mechanism eliminates the need to keep track of all processes that depend on the particular change and to keep track of all messages sent out to the various processes and objects.

1 Design Goals
2 The Mechanics of GCN
3 System Notification
3.1 Registering for System Notification
3.2 Handling System Notification
3.3 Removal from a System List
4 Application Local GCN Lists
4.1 Creating Types and Lists
4.2 Registering for Notification
4.3 Handling Application Notification
4.4 Removal from Application Lists

General Change Notification: 1 Design Goals

General Change Notification allows you to keep track of both system and application events. Objects or processes interested in a particular change may request the GCN mechanism to notify them when that change occurs. That change may be system-wide (such as a file system change) or application-specific (such as a text style change within a word processor). The GCN mechanism allows objects or processes to sign up for such notification and intercept messages sent by the system (or the application) so that you may respond to different changes on a case by case basis.

The manners in which you sign up for these two types of notification differ although the functionality of the notification process is similar. The most straightforward way is to use a gcnList field in an application's application object; this is the usual approach used to add an application's GenPrimary object to the application's window list. We will assume that you know how to add an object to a GCN list in this manner, as it is shown in most if not all of the sample applications.

You can also sign up for system-wide notification through the use of certain routines and intercept system messages when the change occurs. Other objects may sign up for application-specific notification supported by GenApplicationClass . These application specific notifications should only be sent to the GenApplication object.

General Change Notification: 2 The Mechanics of GCN

The basic GCN functionality manages lists of objects that are interested in specific changes. For each particular change that needs to be monitored, a separate list is needed. A completely separate "list of lists" containing an inventory of all GCN lists is also created. This will serve as the "search" table, while the particular GCN list will serve as the "messaging" list. When an event is detected, the GCN mechanism will search through the list of lists, seeing if a notification list is interested in a particular change, and send the appropriate messages if the objects do indeed wish notification of the event.

There are several reasons why you would want to use GCN:

  • Ease of use
    The GCN mechanism eliminates the need to monitor and dispatch messages relating to system changes.
  • Commonality
    The GCN mechanism provides a common platform for communication between applications in certain cases.
  • The system expects you to
    Many messages sent by the system expect a GCN mechanism to intercept them. Although you can intercept these messages manually, it is easier to take advantage of GCN's built-in functions.

General Change Notification: 3 System Notification

GCNListAdd(), GCNListSend()

The system provides several lists for common system changes which might affect your application. After signing up on one of these lists (for example, the file change list) you will be notified by the kernel whenever the specified change occurs. In most cases, all you need to do is register for notification with GCNListAdd() and intercept the kernel's notification message.

The GCN mechanism performs its functions through a common series of steps. These steps are:

  1. The object registers for notification with GCNListAdd().
  2. The change occurs.
  3. The GCN mechanism is informed of the change by the acting party (in most cases this is the system itself, although a library may also send notifications). Applications do not send notifications at the system level.
  4. The GCN mechanism dispatches notification messages to all interested parties with GCNListSend() .
  5. The object is informed of the change.
    If you need to perform some work related to this change, you should have a message handler to intercept the system messages.

Registering for System Notification

Handling System Notification

Removal from a System List

General Change Notification: 3.1 System Notification: Registering for System Notification

Whenever an object or process needs to be notified of some system change, it must call the routine GCNListAdd() to add itself to the list for that particular change. GCNListAdd () finds the appropriate general change notification list--creating a new one if none currently exists--and adds the optr of the new object to the end of that list. You may add the optr to the GCN list at any time during the process' or object's life, but it is usually convenient for a process to be added in its MSG_GEN_PROCESS_OPEN_APPLICATION or for an object that is on the active list to be added in its MSG_META_ATTACH handler.

Each optr in a GCN list should have a notification ID attached to it. (The combination of a manufacturer ID and a notification type comprises an element's specific notification ID.) GCNListAdd() must be passed the optr of the object to add, along with a notification ID. For each separate notification ID, a separate GCN list is needed and will be created automatically.

The GCN routines use a word of data, GCNStandardListType , to represent the notification type. The currently recognized GCNStandardListType types for MANUFACTURER_ID_GEOWORKS are

  • GCNSLT_FILE_SYSTEM
    This GCNStandardListType is used for notification of a file system change. Parties on this list will receive the system messages MSG_NOTIFY_FILE_CHANGE and MSG_NOTIFY_DRIVE_CHANGE .
  • GCNSLT_APPLICATION
    This GCNStandardListType is used for notification of a starting or exiting application. Parties on this list will receive the system messages MSG_NOTIFY_APP_STARTED and MSG_NOTIFY_APP_EXITED .
  • GCNSLT_DATE_TIME
    This GCNStandardListType is used for notification of a date/time change in the system's internal clock. Note that this will not tell you about timer ticks--the only time changes that will come up are those resulting from system restarts and time changes by the user. Parties on this list will receive the system message MSG_NOTIFY_DATE_TIME_CHANGE. This message does not pass any further information, so your message handler should be able to take care of any changes by itself (such as calling the internal clock for an updated value).
  • GCNSLT_DICTIONARY
    This GCNStandardListType is used for notification of a user dictionary change. Parties on this list will receive the system message MSG_NOTIFY_USER_DICT_CHANGE.
  • GCNSLT_KEYBOARD_OBJECTS
    This list is used for notification when the user has chosen a different keyboard layout. Parties on this list will receive the system message MSG_NOTIFY_KEYBOARD_LAYOUT_CHANGE.
  • GCNSLT_EXPRESS_MENU_CHANGE
    This GCNStandardListType notifies various system utilities that an express menu has been created or destroyed. The recipient receives the optr of the Express Menu Control. This list should be used in conjunction with the GCNSLT_EXPRESS_MENU_OBJECTS list. Objects on this list receive MSG_NOTIFY_EXPRESS_MENU_CHANGE, which itself passes a GCNExpressMenuNotificationType (either GCNEMNT_CREATED or GCNEMNT_DESTROYED) and the optr of the Express Menu Control affected.
  • GCNSLT_INSTALLED_PRINTERS
    This list notifies objects when a printer is either installed or removed. The recipient of MSG_PRINTER_INSTALLED_REMOVED might want to call SpoolGetNumPrinters() to learn if any printer or fax drivers are currently installed.
  • GCNSLT_SHUTDOWN_CONTROL
    This GCNStandardListType is used for system shutdown control. Parties on a list of this type will receive the system message MSG_META_CONFIRM_SHUTDOWN which itself passes a GCNShutdownControlType (either GCNSCT_SUSPEND, GCNSCT_SHUTDOWN, or GCNSCT_UNSUSPEND). Shutdown Control is documented in the Applications and Geodes chapter.
  • GCNSLT_TRANSFER_NOTIFICATION
    This list notifies objects that a transfer item within the clipboard has changed (or been freed). Parties on this list will receive the system message MSG_META_CLIPBOARD_
    NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED and MSG_META_CLIPBOARD_NOTIFY_TRANSFER_ITEM_FREED.
  • GCNSLT_EXPRESS_MENU_OBJECTS
    This list contains all Express Menu Control objects in the system. Typically this list is used to add a control panel item or a DOS task list item to all express menu Control objects. This can be done by sending MSG_EXPRESS_MENU_CONTROL_CREATE_ITEM to the GCN list with GCNListSend() .
  • GCNSLT_TRANSPARENT_DETACH
    This list contains all application objects that may be transparently detached if the system runs short of heap space, in least recently used (LRU) order. This list should only be used if transparent launch mode is in use.
  • GCNSLT_TRANSPARENT_DETACH_DA
    This list contains a list of transparently detachable desk accessories if the system runs short of heap space. This list should only be used if transparent launch mode is in use. Objects should not be detached unless all detachable, full-screen applications have been detached.
  • GCNSLT_REMOVABLE_DISK
    This list is used to store all application and document control objects that originate from a removable drive. If the disk they originate on is removed, they will be notified to shut themselves down with MSG_META_DISK_REMOVED.

These pre-defined notification types are intended only for use with MANUFACTURER_ID_GEOWORKS. Other manufacturers wishing to intercept their own system changes must define their own change types under their respective manufacturer IDs if they are unable to use MANUFACTURER_ID_GEOWORKS.

Code Display 9-1 Adding a Process Object to a GCN List

@method MyProcessClass, MSG_GEN_PROCESS_OPEN_APPLICATION {
    optr			myThread;
    @callsuper;			/* Do default MSG_GEN_PROCESS_OPEN_APPLICATION */
/* Casts the return value for the process handle into an optr */
    myThread = ConstructOptr(GeodeGetProcessHandle(), NullChunk);
/* myThread (the process) is added to notification of file changes */

    GCNListAdd(myThread, MANUFACTURER_ID_GEOWORKS, GCNSLT_FILE_SYSTEM);
}

General Change Notification: 3.2 System Notification: Handling System Notification

MSG_NOTIFY_FILE_CHANGE, MSG_NOTIFY_DRIVE_CHANGE, MSG_NOTIFY_APP_STARTED, MSG_NOTIFY_APP_EXITED, MSG_NOTIFY_USER_DICT_CHANGE, MSG_NOTIFY_EXPRESS_MENU_CHANGE

When an identified change occurs, either the system (or a library) will call the routine GCNListSend() , passing it the appropriate notification message. This routine scans the list of all GCN lists and dispatches notification to all appropriate objects that had requested knowledge of the specified change. If any additional information relating to the change cannot be included in the message, the system allows GCNListSend() to pass data in a global heap block. For example, additional information about a file change (name of file, etc.) must be passed in a global heap block.

The object or process originally requesting notification of the change should be required to handle the appropriate message. If additional data about the change is passed in a global heap block, the process should access that information with MemLock() and MemUnlock() . You should always call the process's superclass in your message handler to make sure that the global heap block will be automatically freed by MetaClass . Therefore, do not free a global heap block manually in a notification handler.

The system provides several messages which you may want to handle. These messages provide notification of file system changes, application start-up or shut-down, system clock changes, etc. These messages are mentioned with the list they correspond to in Registering for System Notification . Messages which require more detailed explanation are also mentioned below.

The kernel sends MSG_NOTIFY_FILE_CHANGE whenever a change to the file system occurs. All objects signed up on the GCN list GCNSLT_FILE_CHANGE will receive this message.

MSG_NOTIFY_FILE_CHANGE passes a FileChangeNotificationType specifying the change that occurred. Some types indicate the presence of a data block (of type FileChangeNotificationData) containing, if applicable, the name of the file changed, the disk handle of the file changed, and the ID of the affected file.

The notification type should be one of the following:

FCNT_CREATE
This type indicates that a file or directory was created. FCND_ id stores the ID of the containing directory; FCND_ name contains the name of the new file or directory that was created.
FCNT_RENAME
This type indicates that a file or directory was renamed. FCND_ id stores the ID of the file or directory that was renamed; FCND_ name contains its new name.
FCNT_OPEN
This type indicates that a file was opened. FCND_ id stores the ID of the file. FCND_ name is undefined, and may or may not be present. (You can check the size of the block to see if it is indeed present.) This notification type is generated after a call to FileEnableOpenCloseNotification() .
FCNT_DELETE
This type indicates that a file or directory was deleted. FCND_ id stores the ID of the file or directory that was deleted. FCND_ name is undefined and may or may not be present.
FCNT_CONTENTS
This type indicates that a file's contents have changed. FCND_ id stores the ID of the file. FCND_ name is undefined and may or may not be present. This notification type is generated after a call to FileCommit() or FileClose() that results in a file modification.
FCNT_ATTRIBUTES
This type indicates that a file's attributes have changed. FCND_ id stores the ID of the file. FCND_ name is undefined and may or may not be present. This notification type is generated upon completion of all changes in a FileSetAttributes() , FileSetHandleExtAttributes() , or FileSetPathExtAttributes() call.
FCNT_DISK_FORMAT
This type indicates that a disk has been formatted. Both FCND_ id and FCND_ name are undefined and may not be present.
FCNT_CLOSE
This type indicates that a file has been closed. FCND _id stores the identifier of the file. FCND _name is undefined and may not be present. This notification type is generated only after a call to FileEnableOpenCloseNotification() .
FCNT_BATCH
This type indicates that this file change notification is actually a group of notifications batched together. In this case, MSG_NOTIFY_FILE_CHANGE passes the MemHandle of a FileChangeBatchNotificationData block instead. This data block stores a batch of FileChangeBatchNotificationItem structures, each referring to an operation (with its own notification type, disk handle, file name, and file ID). Note that in this batched case, you must assume that all file names and file IDs that are optional (i.e. are undefined) are not present.
FCNT_ADD_SP_DIRECTORY
This type indicates that a directory has been added as a StandardPath . FCND_ disk contains the StandardPath that was added. This notification type is generated after a call to FileAddStandardPathDirectory() .
FCNT_DELETE_SP_DIRECTORY
This type indicates that a directory has been deleted as a StandardPath . FCND_ disk contains the StandardPath that was deleted. This notification type is generated after a call to FileDeleteStandardPathDirectory() .

You may access this data (after locking the block) and perform whatever actions you need within your message handler.

The kernel also sends MSG_NOTIFY_DRIVE_CHANGE to GCN lists of type GCNSLT_FILE_CHANGE. This message passes a GCNDriveChangeNotificationType specifying whether a drive is being created or destroyed and the ID of the affected drive.

The kernel sends MSG_NOTIFY_APP_STARTED whenever any application starts up within the system and MSG_NOTIFY_APP_EXITED whenever an application shuts down. All objects signed up on the GCN list GCNSLT_APPLICATION will receive these messages after the change occurs. MSG_NOTIFY_APP_STARTED passes the MemHandle of the application starting up, which you may access to perform any required actions. In a similar manner, MSG_NOTIFY_APP_EXITED passes the MemHandle of the application shutting down.

The kernel sends MSG_NOTIFY_USER_DICT_CHANGE whenever the system changes the current user dictionary in use. All objects signed up for the GCN list GCNSLT_USER_DICT_CHANGE will receive this message after the change occurs. MSG_NOTIFY_USER_DICT_CHANGED passes the MemHandle of the Spell Box causing the change and the MemHandle of the user dictionary being changed, both of which you may access in your message handler.

General Change Notification: 3.3 System Notification: Removal from a System List

You should use GCNListRemove() to remove an object from a system GCN list. You must pass the notification ID ( GCNStandardListType and Manufacturer ID) and the optr of the object to be removed. The optr of the object in question will only be removed from the list of the particular change specified. If the optr is on several GCN lists, those other lists will remain unchanged.

An object or process in the course of dying must remove itself from all GCN lists that it is currently on. You should therefore keep track of all GCN lists you add a particular object to. It is usually convenient for a process to remove itself from these lists within its MSG_GEN_PROCESS_CLOSE_APPLICATION message handler or for an object to remove itself in its MSG_META_DETACH handler.

Code Display 9-2 Removing a Process from a GCN list

@method MyProcessClass, MSG_GEN_PROCESS_CLOSE_APPLICATION {
    optr 		myThread;
    myThread = ConstructOptr(GeodeGetProcessHandle(), NullChunk);
    GCNListRemove(myThread, MANUFACTURER_ID_GEOWORKS, GCNSLT_FILE_CHANGE);
    @callsuper;
}

General Change Notification: 4 Application Local GCN Lists

The GCN mechanism not only allows you to keep track of system changes but also allows you to keep track of changes within a specific application. These application-specific GCN lists operate in slightly different manners than the system-wide application lists. There are an extensive number of pre-defined application lists for MANUFACTURER_ID_GEOWORKS. You may use these if you like, but in most cases you will want to create your own list and notification types for your application.

The GenControl objects make extensive use of these types of GCN lists when implementing changes. For a complete discussion of using these lists within the context of a GenControl, see the Controllers chapter.

If you will be creating custom GenControl objects or just wish to set up a notification system within your application, you will want to create your own application GCN lists when using this mechanism. To do this, you must follow a few preliminary steps:

  • Create a new list of type YourCompanyName GenAppGCNListTypes within an appropriate yourCompanyName.h file.
  • Create an GCN notification type of YourCompanyName NotificationTypes for the above list type within the yourCompanyName.h file.

The GCN mechanism in this case performs its functions through a common series of steps. These steps are similar to the steps needed for system-wide notification:

  1. The object registers for notification with MSG_META_GCN_LIST_ADD .
  2. The change occurs within your application and invokes your own custom method. Because the change occurs within your application, you are responsible for detecting the change and sending out notification yourself.
  3. Record the notification event with MSG_META_NOTIFY or MSG_META_NOTIFY_WITH_DATA_BLOCK , the notification list type to use, and the data block to pass (if applicable).
  4. Use MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST to pass the event. You may have to pass some GCNListSendFlags with this message. This message acts as a dispatch routine, sending all interested parties the recorded event MSG_META_NOTIFY .
  5. The object is informed of the change with MSG_META_NOTIFY or MSG_META_NOTIFY_WITH_DATA_BLOCK . If you need to perform some work related to this change, you should have a message handler to intercept these messages.

Creating Types and Lists

Registering for Notification

Handling Application Notification

Removal from Application Lists

General Change Notification: 4.1 Application Local GCN Lists: Creating Types and Lists

It is a relatively simple matter to create your own notification types. Within an appropriate company-specific file merely create your own types and lists. (For example, all Geoworks application-local lists are within the file geoworks.h .)

Code Display 9-3 Creating New Notification Types and Lists

/* These types should be placed within an appropriate yourCompanyName.h file. */
/* First create a group of Notification types to use for your MANUFACTURER_ID. */
typedef enum {
    <yourCompanyName>_NT_CUSTOM_NOTIFICATION_NUMBER_ONE
    <yourCompanyName>_NT_CUSTOM_NOTIFICATION_NUMBER_TWO
    ...
} <yourCompanyName>NotificationTypes;
/* Then create whatever Notification list types you need. These list types
 * usually correspond one-to-one to the types enumerated above. It is possible,
 * however, for several lists to be interested in a single notification type. */
typedef enum {
    <yourCompanyName>_GAGCNLT_CUSTOM_LIST_TYPE_ONE
    <yourCompanyName>_GAGCNLT_CUSTOM_LIST_TYPE_TWO
    ...
} <yourCompanyName>GenAppGCNListTypes;

General Change Notification: 4.2 Application Local GCN Lists: Registering for Notification

MSG_META_GCN_LIST_ADD

Registering for application notification is simple once you have created your own custom notification lists. Whenever an object or process needs to be notified of an application change, you should call MSG_META_GCN_LIST_ADD to add that object or process to the list interested in that particular change. MSG_META_GCN_LIST_ADD finds the appropriate custom GCN list and adds the optr of the new object to the end of that list. (If no space for the list currently exists because it is empty, the message will allocate space for the list automatically.) You may add the interested optr at any time during the process' or object's life, but it is usually convenient for a process to be added in its MSG_GEN_PROCESS_OPEN_APPLICATION or for an object to be added in its MSG_META_ATTACH handler.

Each optr in a GCN list should have a notification ID attached to it. The combination of a manufacturer ID and a notification type comprises an element's specific notification ID. MSG_META_GCN_LIST_ADD must pass the optr of the object to add, along with a notification ID. For each separate notification ID, a separate GCN list is needed and will be created automatically.

Geoworks has several pre-defined GCN lists of type GeoWorksGenAppGCNListType for use by applications. You will probably have only limited use for these; these list types are used mostly by the UI controllers. For information on these types and how the various classes use them, see the Controllers chapter.

Code Display 9-4 Adding Yourself to a Custom GCN List

@method MyProcessClass, MSG_GEN_PROCESS_OPEN_APPLICATION {
    @callsuper;			/* Do default MSG_GEN_PROCESS_OPEN_APPLICATION */
    myThread = ConstructOptr(GeodeGetProcessHandle(), NullChunk);
/* myThread (the process) is added to notification of TYPE_ONE changes */

    @call MyApplication::MSG_META_GCN_LIST_ADD(myThread,
    yourCompanyName_GAGCNLT_CUSTOM_LIST_TYPE_ONE,
    MANUFACTURER_ID_yourCompanyName);
}

General Change Notification: 4.3 Application Local GCN Lists: Handling Application Notification

MSG_META_NOTIFY, MSG_META_NOTIFY_WITH_DATA_BLOCK, MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST

When a change occurs in the application that needs to send out notification, you must set up the notification message and send it to the interested list. You may attach a data block to this notification for use by the objects on the notification list. To send out these notifications, you should use MSG_META_NOTIFY or MSG_META_NOTIFY_WITH_DATA_BLOCK (when passing data).

In the simplest case without the need to pass data, you should encapsulate MSG_META_NOTIFY with the particular Notification ID (notification type and Manufacturer ID) that should be notified. You should then send MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST to your application object with this event and the particular GCN list interested in this change. (Note that you will have to keep track of which lists are interested in which notification types.) Make sure that you perform a send (not a call) when using this message as the message may cross threads.

Code Display 9-5 Using MSG_META_NOTIFY

@method MyProcessClass, MSG_SEND_CUSTOM_NOTIFICATION {
    MessageHandle event;
/* First encapsulate the MSG_META_NOTIFY with the type of list and manufacturer ID
 * interested in the change. Since this message is being recorded for no class in
 * particular, use NullClass.*/
    event = @record (optr) NullClass::MSG_META_NOTIFY(
    MANUFACTURER_ID_yourCompanyName,
    yourCompanyName_NT_CUSTOM_TYPE_ONE,
    NULL );
/* Then send this MSG_META_NOTIFY using MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST.
 * You must make sure to pass the particular GCN list interested in the changes
 * encapsulated in the above message. */
    @send MyProcess::MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST (
    (word) 0, 		/* GCNListSendFlags */
    event,			/* Handle to MSG_NOTIFY event above. */
    0,			/* No data passed, so no data block. */
    /* Pass the list interested in NT_CUSTOM_TYPE_ONE notification types. */
    yourCompanyName_GAGCNLT_APP_CUSTOM_LIST_ONE,
    /* Pass your manufacturer ID. */
    MANUFACTURER_ID_yourCompanyName);
}

If instead you need to pass a data block along with the notification, you should use MSG_META_NOTIFY_WITH_DATA_BLOCK . You should set up the structure to pass beforehand. You must also make sure to add a reference count to the data block equal to the number of lists (not objects) you wish to send the notification. To do this, call MemInitRefCount() with the data block and the total number of lists you are sending the notification to. (In most cases, you will only send notification to one list, although, of course, that list may have several objects.)

Code Display 9-6 MSG_META_NOTIFY_WITH_DATA_BLOCK

@method MyProcessClass, MSG_SEND_CUSTOM_NOTIFICATION {
    typedef struct {
    int number;
    char letterToLookFor;
    } MyDataStructure;
    MemHandle myDataBlock;
    MyDataStructure *myDataPtr;
    MessageHandle event;
/* Allocate and lock down a block for the data structure. This will be passed
 * along with the notification. NOTE: data blocks must be sharable! */
    myDataBlock = MemAlloc(sizeof(MyDataStructure), (HF_DYNAMIC | HF_SHARABLE),
             HAF_STANDARD);
    myDataPtr = MemLock(myDataBlock);
/* Load up the structure with pertinent information. */
    myDataPtr->count = 200;
    myDataPtr->letterToLookFor = `z';
/* Unlock it and set its reference count to 1 as we are only sending this to one
 * list. */
    MemUnlock(myDataBlock);
    MemInitRefCount(myDataBlock, (word) 1);
/* Now encapsulate a MSG_META_NOTIFY_WITH_DATA_BLOCK message. Since it is being
 * recorded for no particular class, use NullClass as its class type. */
    event = @record (optr) NullClass::MSG_META_NOTIFY_WITH_DATA_BLOCK(
            MANUFACTURER_ID_yourCompanyName,				/* Manufacturer ID */
            NT_CUSTOM_TYPE_ONE,				/* List type. */
            myDataBlock);				/* handle of data block */
/* Finally, send the message off to our process. The GCNListSendFlags depend on
 * the situation. */
    @send MyProcess::MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST(
            (word) 0,			/* GCNListSendFlags */
            event,			/* Handle to message */
            myDataBlock,			/* Handle of data block */
    /* Pass the type of list interested in NT_CUSTOM_TYPE_ONE notification. */
            GAGCNLT_APP_CUSTOM_LIST_ONE,
            MANUFACTURER_ID_yourCompanyName);
/* All done! myDataBlock will be MemFree()'d automatically. */
}

The object or process originally requesting notification of the change will want to provide a handler for the MSG_META_NOTIFY or MSG_META_NOTIFY_WITH_DATA_BLOCK . If additional data about the change is passed in a data block, the process should access that information with MemLock() and MemUnlock() . You should always call the process's superclass in your message handler, to make sure that the global heap block will be automatically freed by MetaClass . Therefore, do not free a notification data block manually in a notification handler.

Code Display 9-7 Intercepting an Application Notification Change

/* Code to implement when MyObjectClass receives MSG_META_NOTIFY with a certain
 * notification type. */
@method MyObjectClass, MSG_META_NOTIFY {
    MyDataStructure myData;				/* Stores the passed data block. */
/* Lock the data structure. */
    myData = MemLock(data);
/* Check the notification type and implement the changes you wish to occur in
 * response to the previous event. */
    if ((notificationType == yourCompanyName_NT_CUSTOM_TYPE_ONE) &
    (manufID == MANUFACTURER_ID_yourCompanyName)){
    /* Code to implement for your object. */
    }
    MemUnlock(data);
    @callsuper;				/* Important! Frees data block. */
}

General Change Notification: 4.4 Application Local GCN Lists: Removal from Application Lists

You should use MSG_META_GCN_LIST_REMOVE to remove an object from an application GCN list. You must pass the routine the notification ID ( yourCompanyName AppGCNListTypes and Manufacturer ID) and the optr of the object to remove. Note that the optr of the object in question will only be removed from the list of the particular change specified. If the optr is on several GCN lists, those other lists will remain unchanged.

An object or process in the course of dying must remove itself from all GCN lists that it is currently on, both from the system and from an application. You should therefore keep track of all GCN lists you add a particular object to. It is usually convenient for a process to remove itself from these lists within its MSG_GEN_PROCESS_CLOSE_APPLICATION message handler or for an object to remove itself at MSG_META_DETACH time.

Code Display 9-8 Removing from an Application GCN List

@method MyProcessClass, MSG_GEN_PROCESS_CLOSE_APPLICATION {
    @send MyApplication::MSG_META_GCN_LIST_REMOVE(
            MyObject,			/* optr to remove from list. */
            yourCompanyName_NT_CUSTOM_LIST_ONE,
                    /* list to remove object from. */
        /* Manufacturer ID of list to remove object from. */
            MANUFACTURER_ID_yourCompanyName);
    @callsuper;
}
This document is a single-page version of a a multi-page document, suitable for easy printing.


Applications and Geodes

This chapter discusses the life of an application as well as several topics most application programmers will want to cover at one time or another. Before reading this chapter, you should have read each of the previous chapters; this chapter builds on the knowledge you gained in those chapters.

This chapter details the components and features of a geode , a GEOS executable. The chapter is presented in the following five sections:

  • Geodes
    The Geodes section describes how libraries, drivers, and applications are launched, used, and shut down. It also discusses the general sections of a geode and the geode's executable file.
  • Creating Icons
    The Creating Icons section describes the structure of the Token Database, how icons are stored, managed, and created.
  • Saving User Options
    The Saving Options section describes how to work with the GEOS.INI file.
  • General System Utilities
    The General System Utilities section describes several different mechanisms provided by GEOS that you may find useful in your application or other geode.
  • The Error-Checking System Software
    The Error-Checking System Software describes the differences between the debugging and standard versions of the system software. There are two versions of nearly every part of the system software including the kernel, the UI, and most libraries and drivers.
  • IACP: Inter-Application Communication Protocol
    The IACP section describes how applications can communicate with each other and pass data back and forth. This protocol works for applications that are not necessarily loaded and running; thus, an application can change another application's data dynamically--the recipient application is automatically started if it is not already running. (This is an advanced topic; most programmers will not need to read this section.)

1 Geodes
1.1 Geode Components and Structures
1.2 Launching an Application
1.3 Shutting Down an Application
1.4 Saving and Restoring State
1.5 Using Other Geodes
1.6 Writing Your Own Libraries
1.7 Working with Geodes
1.8 Geode Protocols and Release Levels
1.9 Temporary Geode Memory
2 Creating Icons
2.1 The Token Database
2.2 Managing the Token Database File
3 Saving User Options
3.1 Saving Generic Object Options
3.2 The GEOS.INI File
4 General System Utilities
4.1 System Clock
4.2 Using Timers
4.3 System Statistics and Utilities
4.4 Shutting the System Down
5 The Error-Checking Version
5.1 Adding EC Code to Your Program
5.2 Special EC Routines
6 Inter-Application Communication
6.1 IACP Overview
6.2 GenApplicationClass Behavior
6.3 Messages Across an IACP Link
6.4 Being a Client
6.5 Being a Server

Applications and Geodes: 1 Geodes

Geode is the term used to describe a GEOS executable. Just as DOS has executables (programs) that reside in files on a disk, so too does GEOS. GEOS executables normally have the filename extension .GEO.

Each geode may have up to three different aspects:

  • process
     Most of your geodes will have this aspect. A geode that is a process has an initial event-driven thread started for it by the kernel. All applications and some libraries will have this aspect.
  • library
     This aspect indicates that the geode exports entry points for other geodes to use. Typically, these entry points describe where either object classes or code for routines is within the geode. A library has a special routine (the library's entry point) that is called by the system when the library or one of its clients is loaded or in the process of being unloaded.
  • driver
     This aspect indicates that the geode hides the details of some device or similarly changeable thing from the system. A driver has a special structure that defines its type, and it has a single entry point called a strategy routine . All calls to the driver pass through this strategy routine.

A geode can have any combination of these aspects. For example, the print spooler is a process-library (and therefore provides routines for other geodes while also having a thread of its own), but the sound library is actually a library-driver since it manipulates the machine's sound hardware.

Library and driver geodes that do not have the process aspect do not initially have event-driven threads. Therefore, typically they will not contain objects, just procedural code. They can contain objects, however, as any geode is free to create an event-driven thread for itself at any time. In fact, the parallel port driver does just that when it is printing to a port through DOS or BIOS.

Geodes are loaded either with a call to the kernel routine GeodeLoad() or as a side effect of a library's client being loaded (in that case, the library geode will be loaded as well). The generic UI supplies the special routine UserLoadApplication() , which you may use to load an application--a geode which has both a process aspect and its process class subclassed off of GenProcessClass (and therefore can put generic UI objects on the screen).

Once a geode has been loaded, it is identified by its geode handle , which is the memory handle of the block that holds all the geode's system-administrative data. This block is called the core block and should not be accessed by anything other than the kernel. The geode handle is also used to determine the owner of a particular block in memory; when queried for the owner of a particular block, the kernel will return the geode handle of the geode that owns that block. A geode is the only entity that may own a system resource. If the geode is a process, the geode handle may also be known as a process handle.

When a geode is loaded, its core block is connected to a linked list of the core blocks of other geodes running in the system. This linked list is chronological, with the first entry belonging to the first geode loaded and the last entry belonging to the most recent geode loaded. Each core block contains an entry for the handle of the next core block in the list; the kernel can follow these links to locate any geode in the system. (Only the kernel may do this.)

After the core block is appended to the list, GEOS scans the list for other instances of the same core block. If the geode has been loaded more than once, it will have multiple instances in the list (one instance of the core block for each time the geode is loaded; each core block references the same copy of the geode, however). GEOS then copies the shared-resource handles from an existing core block (if found) into the new core block, thus reducing the amount of work required to load a particular geode multiple times (the shared resources do not need to be reloaded or recreated). Non-shared resource handles are not copied; the resources are loaded or constructed as necessary.

Each geode's core block contains a reference count for that particular geode. When the geode is first loaded, the reference count is set to one. If the geode is a process, the act of initializing the process thread increments the reference count. Each time the geode is loaded again, the new core block will get its own reference count. If the geode is loaded implicitly (as a library, with GeodeUseLibrary() , or with GeodeUseDriver() ), or if it spawns a new thread, it will receive yet another reference count.

The reference count is decremented when a thread owned by the geode exits. If a client of a library geode exits, the library's reference count goes down by one.

When a geode's reference count reaches zero, all the geode's non-sharable resources are freed along with all the file, event, and timer handles owned by the geode. If a sharable resource is co-owned by another instance of the geode, ownership is transferred to the geode's next-oldest instance. (Shared resources are always owned by the oldest instance of their geode.) Once the resources have been freed or transferred, the core block is removed from the linked list and is freed.

To make sure no synchronization problems occur while updating the core block list (e.g. a geode is being loaded while it has just been freed), GEOS maintains an internal semaphore. The geode loading and freeing routines automatically maintain this semaphore.

Geode Components and Structures

Launching an Application

Shutting Down an Application

Saving and Restoring State

Using Other Geodes

Writing Your Own Libraries

Working with Geodes

Geode Protocols and Release Levels

Temporary Geode Memory

Applications and Geodes: 1.1 Geodes: Geode Components and Structures

A geode is simply a special type of GEOS file. It has a special file header that gets loaded in as the geode's core block. This file header contains the geode's type, attributes, release and protocol levels, and many other pieces of information necessary for GEOS to work with the geode. You never will have to know the exact structure of this header as the kernel provides routines necessary to access important portions of it.

Several important items contained in the header are listed below.

  • Core Block Handle
    The core block contains its own memory handle, filled in when the geode is loaded into memory.
  • Geode Attributes
    Each geode has a record of type GeodeAttrs . The geode attributes are described below in Geode Attributes .
  • Release and Protocol Levels
    Each geode can have release and protocol levels associated with it to ensure compatibility between different versions. Release and protocol levels are discussed in Protocol Numbers .
  • Geode Name
    Each geode has a geode name and extension specified in the geode parameters ( .gp ) file.
  • Geode Token
    Every geode has a token associated with it in the token database. The token describes the geode's icon and is a structure of type GeodeToken . Tokens and icons are discussed in Creating Icons .
  • Geode Reference Count
    The geode's reference count is stored in the core block. See above for a discussion of the reference count and how it's managed.
  • Geode File Handle
    The file handle of the geode identifies the open GEO file so the kernel can locate, load, and access its various resources.
  • Geode Process Handle
    Each geode that has a parent process will have the handle of the process in its core block. This is the handle of the parent process' core block. (The parent process is the owner of the thread that loaded the geode. It is notified when the geode exits, if the exiting geode is a process.)
  • Handle of the Next Geode
    Because geode core blocks are stored in a linked list, each core block must contain a reference to the next geode in the list.
  • Handle of Private Data Space
    Each geode that is not the kernel can have a "private data space." This private data space is discussed in Temporary Geode Memory .
  • Resource, Library, and Driver Information
    Each geode that imports or exports library or driver information must keep the import and export specifics available. Additionally, each geode must keep track of the resources it owns. All this information is stored in tables referenced from within the core block.

Geode Attributes

Each geode has in its core block a record of type GeodeAttrs . This record defines several things about the geode, including which aspects it uses and which of its aspects have been initialized. The GeodeAttrs record contains one bit for each of the following attributes.

GA_PROCESS
This geode has a process aspect and therefore an initial event-driven thread.
GA_LIBRARY
This geode has a library aspect and therefore exports routines (and possibly object classes).
GA_DRIVER
This geode has a driver aspect and therefore has a driver table, in which the strategy routine is specified.
GA_KEEP_FILE_OPEN
This geode must have its .GEO file kept open because its resources may be discardable or are initially discarded.
GA_SYSTEM
This geode is a privileged geode and is almost certainly a system-used driver. These geodes have special exit requirements.
GA_MULTI_LAUNCHABLE
This geode may be loaded more than once and therefore may have more than one instance of its core block in memory.
GA_APPLICATION
This geode is a user-launchable application.
GA_DRIVER_INITIALIZED
This flag is set if the geode has had its driver aspect initialized (if the driver's strategy routine has been initialized). This flag will be set dynamically by the kernel.
GA_LIBRARY_INITIALIZED
This flag is set if the geode has had its library aspect initialized (if the library's entry routine has been called). This flag will be set dynamically by the kernel.
GA_GEODE_INITIALIZED
This flag is set if all aspects of the geode have been initialized.
GA_USES_COPROC
This geode uses a math coprocessor if one is available.
GA_REQUIRES_COPROC
This geode requires the presence of a math coprocessor or a coprocessor emulator.
GA_HAS_GENERAL_CONSUMER_MODE
This geode may be run in the General Consumer (appliance) Mode.
GA_ENTRY_POINTS_IN_C
This geode has its library entry routine in C rather than assembly language.

Geode Token

As stated above, every geode is associated with a token in the token database. This token is defined by the use of a GeodeToken structure. This structure and its uses are discussed in Creating Icons .

Applications and Geodes: 1.2 Geodes: Launching an Application

GeodeLoad(), UserLoadApplication(), MSG_GEN_PROCESS_OPEN_APPLICATION

An application is a geode with its GA_APPLICATION attribute set. This type of geode may be launched by the user through GeoManager or some other means provided by the system or another application. For the most part, the system will invoke and carry out the launch; your responsibilities are limited.

An application may be loaded in essentially two ways: It may be launched, or it may be reloaded from a state file. In both cases, the kernel will load the proper resources and build out the UI properly according to the application.

Most of the procedure of launching is handled within GenProcessClass , a subclass of ProcessClass . An application should define its own subclass of GenProcessClass for its Process objects (event-driven threads not acting as Process objects should be subclassed from ProcessClass , not GenProcessClass ). The launch procedure may be invoked by any thread and by any geode in either of the following ways:

  • GeodeLoad()
     This loads a geode from a given file and begins executing it based on the geode's type. GeodeLoad() takes the name of the geode's file as well as a priority to set for the new geode's process thread, if it has one. GeodeLoad() first creates the process thread of the application, then sends this thread a message. The process thread (a subclass of GenProcessClass ) then creates a UI thread for the application.
  • UserLoadApplication()
     Used by most application launchers, this routine loads an application. (C programmers will generally use MSG_GEN_PROCESS_OPEN_APPLICATION instead.) This routine takes some additional parameters and can load a geode either in engine mode or from a state file as well as in the normal open mode (see below). The base functionality of opening and loading the geode is implemented in this routine by a call to GeodeLoad() . Note, however, that this routine may only open application geodes--geodes with the GA_APPLICATION attribute set.

Geodes may be launched in three modes:

  • Application mode launches the geode, loads all its active resources, builds its UI gadgetry, and sets it usable. The geode must be an application--that is, it must have its GA_APPLICATION attribute set.
  • Engine mode launches the geode but leaves its UI objects unusable (it never brings them on-screen). Engine mode is useful if you need to launch an application to grab information from it. GeoManager uses engine mode to launch an application, extract its icon, and put the icon in the token database. For efficiency, the application is never set usable, so its UI is never built.
  • Restore mode launches the geode from a saved state file, loading in resources and merging them with the default resources. This mode is invoked automatically by the UI if it is restoring the system or the geode from saved state. This mode is handled automatically by GenProcessClass (your Process object).

It is possible, however, for one application to launch another in a custom mode. If this is done, the application being launched is responsible for implementing the special mode.

When the launch process has been initiated with the above routines, a thread is created for the application and its Process object is loaded immediately. Also loaded is the application's GenApplication object. The Application and Process objects interact with the User Interface to load the objects on the application's active list and set them all usable, bringing them up on-screen.

Near the end of this procedure, just before the GenApplication is set usable, the Process object will receive a message based on the mode of launch. If the application must set up any special notification (such as for the quick-transfer mechanism) or must restore special state file data, it should intercept this message. Typically the message received will be MSG_GEN_PROCESS_OPEN_APPLICATION --two others are received (when restoring from state and when opening in engine mode), but they should not be intercepted.

Applications and Geodes: 1.3 Geodes: Shutting Down an Application

MSG_GEN_PROCESS_CLOSE_APPLICATION, MSG_GEN_PROCESS_CLOSE_ENGINE, MSG_GEN_PROCESS_CLOSE_CUSTOM, MSG_META_QUIT

Just as loading an application is handled almost entirely by the system and GEOS classes, application shutdown is also fairly automatic. If the application intercepted MSG_GEN_PROCESS_OPEN_APPLICATION for its own purposes on startup, it likely has to do a little cleanup; otherwise, it won't have to worry about shutting down. (See Saving and Restoring State for special information on using this message.)

Any object in the system may cause an application to shut down. Usually, shutdown occurs either when the system is being exited (when a user exits to DOS, for example) or when the user has closed the application. Therefore, the usual source of the shutdown directive is the User Interface.

An application begins shutting down when either its Process object or its Application object receives a MSG_META_DETACH . If you want to cause a shutdown manually, you should send MSG_META_QUIT to the application's GenApplication object; this will execute some default functions and then send the appropriate MSG_META_DETACH . Essentially, the same detach and destruction mechanisms used for any object are used for the entire application. The object receiving MSG_META_DETACH passes the message along to all of its children and to all the objects on its active list. (If a MSG_META_DETACH is used without MSG_META_QUIT , the application will create a state file.)

When they have all acknowledged the detach, the application acknowledges the detach and sets itself unusable. It automatically flushes its message queues before shutting down to avoid synchronization problems. You should not subclass the MSG_META_DETACH handler unless you have special needs for cleaning up or sending special detach messages to other objects or geodes. If you do subclass it, you must call the superclass at the end of your handler. Otherwise, the application will not finish detaching (see the GEOS Programming chapter).

Instead of intercepting MSG_META_DETACH , though, the application may intercept the mode-specific message it will also receive. Depending on the mode in which it was launched, the application will receive (via the Process object) either MSG_GEN_PROCESS_CLOSE_APPLICATION (for application mode) or MSG_GEN_PROCESS_CLOSE_ENGINE (for engine mode). There is no special shutdown message for shutting down to a state file; instead, MSG_GEN_PROCESS_CLOSE_APPLICATION is used.

When the system shuts down or task-switches, a different type of shutdown occurs. Applications (or other objects interested in this event) must register for notification on the notification list GCNSLT_SHUTDOWN_CONTROL (notification lists are described in the General Change Notification chapter). When the system shuts down or task-switches, the object will then receive a MSG_META_CONFIRM_SHUTDOWN , at which time the object must call SysShutdown() .

Applications and Geodes: 1.4 Geodes: Saving and Restoring State

ObjMarkDirty(), ObjSaveBlock()

Nearly all applications will save and restore their state so the user may shut down and return to precisely the same configuration he or she left. Saving of state is almost entirely contained within the system software; for the most part, only UI objects are saved to state files. You can, however, mark other object blocks and data for saving to a state file.

The state file for an application is a VM file containing object blocks. Only object blocks may be saved to the state file, though you can save LMem data by setting up object blocks with only data chunks in them. (Create the blocks with MemAllocLMem() , passing type LMEM_TYPE_OBJ_BLOCK, then simply use LMemAlloc() to allocate data chunks.) For more information about managing LMem and data chunks, see the Local Memory chapter. You can also save an extra data block to the state file using MSG_GEN_PROCESS_CLOSE_APPLICATION and MSG_GEN_PROCESS_OPEN_APPLICATION . In the close message, you can return the handle of an extra block to be saved to the state file; in the open message, the handle of the extra block is given to you, and you can restore this data as necessary. See the reference information for these messages under GenProcessClass for more information.

When a state file is saved, the system recognizes and saves only the dirty (modified) objects and chunks. Later, when state is restored, the system merges the changes in the state file with the original object blocks, resulting in the state that was saved.

For individual objects or entire object blocks to be saved to the state file, they must be marked dirty. Generic objects automatically mark themselves dirty at the appropriate times, so you don't have to worry about them. To mark other objects dirty, use the routine ObjMarkDirty() . Each object which has been marked dirty will be saved to a state file when appropriate. If you want to save an entire object block to the state file, you can call ObjSaveBlock() on the block; the system will save the entire block, not just the dirty chunks.

State files are dealt with at only two times: First, when the system starts up, it will check for the existence of application state files. If a state file exists, the system will attempt to load the application belonging to it; after loading the application's resources, it will merge the state changes with the default settings to restore the original state.

The second time state files are used is when the system shuts down. A simple shutdown (called a "detach") is invoked only by the UI and is not abortable. When a detach occurs, the system shuts down all geodes as cleanly and quietly as possible, saving them to state files. Only certain geodes will respond in extreme cases, offering the user the option of delaying the detach or cancelling an operation in progress. An example of this is the GEOS spooler; if one or more jobs are actively printing or queued for printing, the spooler will ask the user whether the job should continue and the detach be delayed, or whether the job should be aborted or delayed until the next startup. The spooler can not abort the detach in any case.

Another type of detach is called a "quit." Any geode may invoke a quit, which is actually a two-step detach. A quit will first notify all other geodes that the system will soon be detaching; other geodes then have the chance to abort the quit if they want. For example, if a terminal program were downloading a file and received a quit notification, it could ask the user whether she wanted to abort the quit or the download. If the user wanted to finish the download, she would abort the quit; if she wanted to quit, she would abort the download. The system would then either shut down via a normal detach or stop the quit sequence.

When a geode is first launched, no state file exists for it. The state file is not created until the geode is actually detached to a state file. If a geode is restored from a state file, the file will exist until the geode is detached again. A geode that gets closed (not detached to state) will remove any state file it may have created during a previous detach. A geode that is detached to state will create or modify its state file as appropriate.

The state of an application (how it was launched) is reflected in the GAI_states field of the GenApplication object. To retrieve the application's state, send it MSG_GEN_APPLICATION_GET_STATE . It will return a value of ApplicationStates . The most frequent use of this message is by applications that need to know whether a "quit" is underway when their process objects receive the MSG_GEN_PROCESS_CLOSE_APPLICATION message; the process object will query the GenApplication and see if it is in the AS_QUITTING state.

In addition to the above state-saving functionality, the kernel provides two routines that translate handles between the state file and memory. ObjMapSavedToState() takes the memory handle of an object block and returns its corresponding state file VM block handle. ObjMapStateToSaved() takes the state file VM block handle and returns the corresponding memory block handle, if any.

If your application's documents are VM files, it is a very simple matter to save document state. In fact, if you use the GenDocument and document control objects, they will take care of document state saving for you. Be sure that the VM file has the VMA_BACKUP flag set in its VMAttributes ; then you can simply call VMUpdate() on the document file. (Note--do not use VMSave() instead; it will erase the backup and lock in the user's changes to the document.) If you are not using GEOS VM files, it is up to you how and if you will save the document's state. For more information about GEOS VM files, see the VM chapter.

Applications and Geodes: 1.5 Geodes: Using Other Geodes

Often, geodes will have to use other geodes. For example, a communications program will use the Serial Driver, and a draw application will use the Graphic Object Library. Normally, this is taken care of by the compiler and the linker when you include a library or driver in your .goc and .gp files.

Other times, however, an application will have to load libraries or drivers on the fly and then free them some time later. This section describes how to load, use, and free libraries and drivers.

Using Libraries

GeodeUseLibrary(), GeodeFreeLibrary()

Libraries are always referenced by their file names or by their geode handles. It's easiest, however, to use the file name of the library when loading it--the system will locate the library for you. It's unusual to need to load a library for use with your geode; in almost all cases it's easiest to include the library in your .goc and .gp files and have the system load and link the library automatically. (To do this, include the library's interface definition file in your code file and list the library's geode name in your geode parameters file.)

If you need to load a library dynamically, though, use GeodeUseLibrary() . This routine takes the protocol numbers expected of the library (see Protocol Numbers ) and the library geode's filename. It will locate and load the library if not already loaded. If the library is already loaded, it will increment the library's reference count. When you are done using a library loaded with GeodeUseLibrary() , you must free the library's instance with GeodeFreeLibrary() .

Using Drivers

GeodeUseDriver(), GeodeInfoDriver(), GeodeGetDefaultDriver(), GeodeSetDefaultDriver(), GeodeFreeDriver()

Drivers are referenced by either their permanent names or their geode handles. Most drivers used by applications will be loaded automatically by the kernel; the application must have the driver's permanent name specified in its .gp file. Should an application need to use a driver not included in its parameters file, however, it can do so with the routines described below.

When you need to use a driver, the GeodeUseDriver() routine will locate and load it, adding it to the active geodes list. You must pass the desired driver geode's filename as well as the expected protocol levels of the driver. The routine will return the driver's geode handle. If you load a driver dynamically, you must free it with GeodeFreeDriver() when your geode shuts down or otherwise finishes using the driver.

If you know a driver's geode handle, you can easily retrieve information about it with the routine GeodeInfoDriver() . This returns a structure of type DriverInfoStruct , which contains the driver's type ( DriverType ), the driver's attributes, and a far pointer to the driver's strategy routine. Many driver types have an expanded information structure, of which DriverInfoStruct is just the first field. Video driver information structures, for example, also contain dimensions and color capabilities (among other things) of the particular devices they drive. The driver information structure is shown below.

typedef struct {
    void			(*DIS_strategy)();
    DriverAttrs			DIS_driverAttributes;
    DriverType			DIS_driverType;
} DriverInfoStruct;

The DIS_strategy field of the structure contains a pointer to the driver's strategy routine in fixed memory. After the driver has been loaded, its strategy routine is called directly with a driver function name.

The DIS_driverAttributes is an attribute record of type DriverAttrs , the flags of which are shown below:

DA_FILE_SYSTEM
This flag indicates that the driver is used for file access.
DA_CHARACTER
This flag indicates that the driver is used for a character-oriented device.
DA_HAS_EXTENDED_INFO
This flag indicates that the driver has a DriverExtendedInfo structure.

The DIS_driverType contains the type of driver described by the information structure. The types that may be specified in this field are listed below:

DRIVER_TYPE_VIDEO
This is used for video drivers.
DRIVER_TYPE_INPUT
This is used for input (mouse, keyboard, pen, etc.) drivers.
DRIVER_TYPE_MASS_STORAGE
This is used for storage device drivers.
DRIVER_TYPE_STREAM
This is used for stream and port (parallel, serial) drivers.
DRIVER_TYPE_FONT
This is used for font rasterizing drivers.
DRIVER_TYPE_OUTPUT
This is used for output drivers other than video and printer drivers.
DRIVER_TYPE_LOCALIZATION
This is used for drivers that facilitate internationalization.
DRIVER_TYPE_FILE_SYSTEM
This is used for file system drivers.
DRIVER_TYPE_PRINTER
This is used for printer drivers.
DRIVER_TYPE_SWAP
This is used for the system's memory-swapping drivers.
DRIVER_TYPE_POWER_MANAGEMENT
This is used for devices that have power management systems.
DRIVER_TYPE_TASK_SWITCH
This is used for devices or systems that have task switchers.
DRIVER_TYPE_NETWORK
This is used for special networks that require driver functionality.

When you want a driver to perform one of its functions, you must call its strategy routine. The strategy routine typically takes a number of parameters, one of which is the function the driver should perform. The DriverInfoStruct contains a far pointer to the strategy routine; your application should store this far pointer and call it directly any time one of the driver's functions is needed. However, because the driver may be put in a different location each time it's loaded, you should not save the pointer in a state file. Note that this scheme of accessing drivers directly can only be implemented in assembly language. Some drivers may provide library interfaces as well as their standard driver interface; this allows routines to be written in C.

GEOS maintains default drivers for the entire system. The types of default drivers are described by GeodeDefaultDriverType ; all the types are shown below. They are called default drivers because the default for each category of driver used by the system is stored in the GEOS.INI file. GEOS will, upon startup, load in the default driver of each category.

GDDT_FILE_SYSTEM
GEOS may use several file system drivers during execution. A file system driver allows GEOS to work on a given DOS (or substitute file access system). The primary driver is considered the default driver.
GDDT_KEYBOARD
The system may only use one keyboard driver during execution; typically, keyboards can differ from country to country. Not all systems will have a keyboard.
GDDT_MOUSE
The mouse should be usable when the system comes up on the screen; the user does not have to manually load a mouse driver with each execution of GEOS. Not all systems will have a mouse.
GDDT_VIDEO
GEOS must know what type of video driver to use prior to attempting to display itself.
GDDT_MEMORY_VIDEO
The Vidmem driver is a video driver that draws to memory (i.e. to bitmaps). It is used primarily for printing but also for editing bitmaps.
GDDT_POWER_MANAGEMENT
A few machines use hardware power management systems (most notably some palmtops and some notebook and laptop machines). In order for GEOS to handle the power management hardware properly, it must load the driver on startup. (Most machines will not use this type of driver.)
GDDT_TASK
Some systems will use a task switcher; in order for GEOS to be a task in one of these, it must have a default task driver.

To retrieve the default that GEOS is using, call the routine GeodeGetDefaultDriver() with the appropriate driver type (a member of the type GeodeDefaultDriverType ). This routine will return the geode handle of the default driver of that type. To set a new default driver for a specified driver type, use GeodeSetDefaultDriver() . This routine takes a geode handle and a driver type and sets the system default for that type. Typically, system defaults will be set only by the Preferences Manager application.

Applications and Geodes: 1.6 Geodes: Writing Your Own Libraries

Creating a library geode is a simple step beyond creating a normal application. Libraries can have their own process threads or not; most libraries do not, though some will. To create a library geode, you have to do four things:

  • Declare the geode to be a library.
    In the geode parameters ( .gp ) file, you must declare the type of the geode to be library.
  • Define an entry point routine.
    Create a routine that initializes the library; this routine is called the "entry point routine" and is called each time the library is loaded.
  • Export the entry point routine.
    Add another line in the geode parameters ( .gp ) file to export the library's entry point routine.
  • Set the geode's permanent name properly.
    Most libraries have the extension of their permanent name be "lib." This helps identify it as a library when using Swat. The permanent name is defined in the geode parameters ( .gp ) file.

Defining a Library-the sound.gp File shows an example of the three changes you must make to the geode's parameters file to make it a library.

Code Display 7-1 Defining a Library--the sound.gp File

 # The Sound Library has the following three lines in its geode parameters file
# (sound.gp) different from how they might appear in an application.
#
# The permanent name of the geode might normally be sound.app if it is an
# application. A library typically has "lib" as its name extension.
#
name sound.lib
#
# Declare the type of the geode to be a library. Applications typically have a
# "process" aspect; many libraries do not. Libraries must also be declared to
# have a "library" aspect. (Note that the Sound Library is exceptional in that
# it has both a driver aspect and a library aspect.)
#
type driver, library, single
#
# Export the geode's entry point routine so the kernel may call it when the
# library is loaded. This routine must be defined somewhere in the geode's code.
# Note that it is exported with the "entry" line rather than the standard
# "export" line; this is to distinguish the exported routine as the entry
# point rather than a typical routine.
#
entry SoundEntry

Applications and Geodes: 1.7 Geodes: Working with Geodes

The system provides a number of utility routines for getting information and setting attributes of geodes. These are loosely organized throughout the following sections.

Accessing the Application Object

GeodeGetAppObject()

GEOS offers a routine for retrieving the optr of an application's GenApplication object. GeodeGetAppObject() takes the process handle of the Process object of the application. It returns the optr of the application's GenApplication object.

General Geode Information

GeodeFind(), GeodeFindResource(), GeodeDuplicateResource(), GeodeGetInfo(), GeodeGetProcessHandle(), GeodeGetCodeProcessHandle(), ProcInfo()

GeodeFind() returns a geode's handle when given a permanent name and attributes to search on. GEOS will search the active geode list for any geode with the given name and the proper attributes set or clear.

GeodeFindResource() locates a given resource within a geode's file. It must be passed the file handle of the geode, the number of the resource in the file, and an offset within the resource at which the file read/write position should be placed. This routine may only be used on open geode files, and it returns the base offset and size of the resource. You will probably not need to use this routine. To load a resource into a new block of memory, you can call GeodeDuplicateResource() .

GeodeGetInfo() takes a geode handle, a GeodeGetInfoType parameter, and a buffer appropriate for the return information. It returns the appropriate values as specified in the GeodeGetInfoType parameter. (This parameter specifies what type of information is sought about the geode; the routine can return the geode's attributes, geode type, release level, protocol, token ID, or permanent name.) The possible values of GeodeGetInfoType are shown below:

GGIT_ATTRIBUTES
This indicates the geode's attributes should be returned.
GGIT_TYPE
This indicates the geode's type should be returned (type GeodeType ).
GGIT_GEODE_RELEASE
This indicates the geode's release numbers should be returned.
GGIT_GEODE_PROTOCOL
This indicates the geode's protocol numbers should be returned.
GGIT_TOKEN_ID
This indicates the geode's token information should be returned.
GGIT_PERM_NAME_AND_EXT
This indicates the geode's permanent name and extender should be returned.
GGIT_PERM_NAME_ONLY
This indicates the eight characters of the geode's permanent name only should be returned, without the extender characters.

GeodeGetProcessHandle() returns the geode handle of the current process (the owner of the current thread). Another routine, GeodeGetCodeProcessHandle() , returns the handle of the geode that owns the code block from which it was called.

ProcInfo() returns the thread handle of the first thread of a given process.

Managing Geode Event Queues

GeodeAllocQueue(), GeodeFreeQueue(), GeodeInfoQueue(), GeodeFlushQueue(), ObjDispatchMessage(), QueueGetMessage(), QueuePostMessage(), GeodeDispatchFromQueue()

The following routines allocate and manage event queues. These routines are rarely called by applications as event queues are automatically managed for each thread and application.

GeodeAllocQueue() allocates an event queue and returns its handle. GeodeInfoQueue() returns the number of events in a given event queue. GeodeFreeQueue() frees an event queue allocated with the routine GeodeAllocQueue() . It must be passed the handle of the queue to be freed (unhandled events still in the queue will be discarded).

GeodeFlushQueue() flushes all events from one queue and synchronously places them all in another queue (events may not simply be tossed out).

QueueGetMessage() combined with ObjDispatchMessage() removes the first event from the given event queue and handles it via a callback routine. A far pointer to the callback routine in memory must be passed. Typically these will be used only by the assembly ObjMessage() routine used by the kernel; some other applications of this routine may be used, though. For example, the sound driver uses a note queue unassociated with objects and messages. The callback routine therefore gets the "event" (note) and pretends it's handling a message.

QueuePostMessage() adds an event to the specified queue.

Applications and Geodes: 1.8 Geodes: Geode Protocols and Release Levels

Every GEOS geode and VM file has both a release level and a protocol level as extended attributes of the file. These two items help ease the transitions for both programmer and user when changes are made to applications, libraries, drivers, system software, etc. To control release and protocol numbers, use the GREV tool and a REV file as described in the Grev section of the Using Tools chapter.

Release Numbers

The release number is a ReleaseNumber structure which consists of four components: The RN_major and RN_ minor numbers are the most significant. The RN_ change and RN_ engineering numbers are less significant and are used primarily to indicate non-released or running upgrade types of changes to the geode. The ReleaseNumber of a geode or VM file is stored in the file's FEA_RELEASE extended attribute, and its structure is shown below:

typedef struct {
    word    RN_major;
    word    RN_minor;
    word    RN_change;
    word    RN_engineering;
} ReleaseNumber;

The contents of the release number are up to the particular geode and are product-specific. Release numbers are not used by GEOS for compatibility checking or any other validation of files, though they are used during installation procedures.

To retrieve the release number of a given geode, use the routine GeodeGetInfo() . Release levels should be set at compile time and are not changeable at run-time.

Protocol Numbers

The protocol number is a structure of type ProtocolNumber stored in the file's FEA_PROTOCOL extended attribute. Each GEOS geode and data file has a protocol level associated with it. The protocol level is used for compatibility checking for both geodes and documents.

The ProtocolNumber structure consists of two parts, the major protocol and the minor protocol. This structure is shown below:

typedef struct {
    word    PN_major;
    word    PN_minor;
} ProtocolNumber;

Differences in protocol levels indicate incompatibilities between two geodes, between a geode and its document format, or between a geode and its state file format. If the major protocols are different, the two items are not compatible at all (unless special provisions are made). If the minor protocol is greater than expected, some incompatibility may exist but should not affect the program. You should increment a geode's or document's protocol whenever a change is made.

If a change to a library is upward-compatible, only the minor protocol needs to be incremented. For example, if a library acquires a new function but the library's entry points are undisturbed, the minor protocol should be incremented and the major protocol left as is. If the new function causes relocation of the entry point numbers, however, the major protocol must also be incremented.

An application's protocol must be increased whenever a change will affect the application's state files. If you make a change to an application, for example, and the user has old state files, either the changes in the application can be replaced with the old information, or the state file will cause an incompatibility with unpredictable results. If the change to the application is simply functional, increment the minor protocol. If the change is to any part of a UI resource or to any other item saved to a state file, increment the major protocol. State files will be loaded if minor protocols are different and will be ignored if major protocols are different.

If an application's document format changes, you should make sure the new documents are not loaded by old applications or vice versa (unless you take the necessary conversion steps). When opening a document, you can check its protocol by checking the document file's extended attribute FEA_PROTOCOL. If the protocol level needs to be changed (after conversions have been done, of course), you can change them by setting FEA_PROTOCOL. (See the File System chapter.)

A few examples of when minor and major protocols should be incremented follow. Keep in mind that this list is by no means exhaustive.

  • If you add a data structure to a document format and older versions of the application will still be able to open the document, increment the document's minor protocol only.
  • If you change the instance data structure of an object that gets saved to the document, increment the major protocol, as the new methods will be using the wrong offsets to access data in the old object.
  • If you add a new class to your application and don't disturb any of the other classes' entry points (i.e. add the class at the end), increment the minor protocol only.
  • If you add a new class, disturbing entry points, increment the major protocol.
  • If you add or delete a resource, increment the major protocol.
  • If you add a chunk or object to a resource that gets saved to state files, increment the major protocol.
  • If you change an object's flags (e.g. mark it ignore-dirty), increment the major protocol. Otherwise, the flags will be restored from the state file and will override the changes you made.

Applications and Geodes: 1.9 Geodes: Temporary Geode Memory

GeodePrivAlloc(), GeodePrivFree(), GeodePrivWrite(), GeodePrivRead()

Every geode in the system has a "private data" area, a space set aside in its core block. This private data is used primarily by library geodes, when each of the library's clients uses its own copy of a particular data structure that gets manipulated by the library. The private data mechanism is used in the GEOS implementation of malloc() , for example (though you need not know this to use malloc() ).

Private memory may be allocated, written to, read from, and freed by the library. The library does not have to allocate a block for each geode and maintain its own handle table; the use of the GeodePriv...() routines automatically manages this.

GeodePrivAlloc() reserves a given number of contiguous words for the library within the private data of all geodes in the system. The memory space is reserved but is not actually allocated for a given geode until it is used (written to); this is done for optimization purposes. This routine will return a tag pointing to where the reserved words begin. This tag is used when reading, writing, or freeing the private data. If the memory could not be allocated, the routine will return zero.

GeodePrivWrite() and GeodePrivRead() write to and read from the private data space. They take similar parameters: a geode handle, the tag as returned by GeodePrivAlloc() , the total number of words to be written or read, and a pointer to a locked or fixed buffer. In GeodePrivWrite() , the buffer will be passed containing the words to be written; in GeodePrivRead() , the buffer will be passed empty and returned containing the words read.

Typically, the geode handle passed will be zero; this indicates that the current process (which will be the library's current client) will be the owner of the private data affected. Because the library code will be executing in the thread of a given application, the application geode will be the only one having its private data affected. Thus, a library can use the same code to store different data for each geode that uses it; neither the library nor the geode needs to know that other geodes are also using the same routines.

GeodePrivFree() frees a given number of words from all geodes' private data. It needs to be passed only the number of words to be freed and the tag as returned by GeodePrivAlloc() .

Applications and Geodes: 2 Creating Icons

Every geode can have an icon associated with it. Typically, only applications will have special icons; other geodes (libraries and drivers) generally use one of the system icons or the icon of the application they're primarily used by.

An application's icon is stored in two places: It is defined and stored within the application's extended attributes in its .GEO file. It is also stored in a database file maintained by the UI, called the token database .

The GEOS development kit includes an icon editor tool so you can easily create icons of various color and resolution characteristics and install them into your applications.

The Token Database

Managing the Token Database File

Applications and Geodes: 2.1 Creating Icons: The Token Database

TokenOpenLocalTokenDB(), TokenCloseLocalTokenDB()

The token database is stored in its own file. Each entry represents a single icon or series of icons that can be used with any number of files. The token database file is managed by the UI and the kernel; you should have no reason to access its internals directly, and most applications will never need to use any of the token routines except to install their document icons.

Some systems may have shared token database files; this is controlled by the INI file key sharedTokenDatabase in the paths category. Most often a shared database file exists on a network drive and may be supplemented with a local token database. By default, if a shared database file exists, it will be opened read-only in addition to the read/write local file. You can open and close only the local database file, however, with the routines TokenOpenLocalTokenDB() and TokenCloseLocalTokenDB() .

Every GEOS file has a token. The token is an index into the token database. When GeoManager scans a directory, it grabs the token from each file and searches through the token database file for it. If a match is found, GeoManager selects the proper icon and displays it; if no match is found or if the file's token is invalid, GeoManager will launch the application in engine mode and request that it install its token in the database.

For non-GEOS files, GeoManager uses the three extension characters of the file's name as a pseudo-index. For each extension (e.g. .COM, .EXE, .DOC, .BAT, etc.), GeoManager uses a single icon. Which icon is used can be set in the GEOS.INI file if a user wishes, but GeoManager will normally select the default DOS icon (of which there are two: one for executables and one for non-executables).

The index into the token database consists of two parts and is of type GeodeToken . This structure contains four text characters as well as the manufacturer ID number of the geode's manufacturer. The structure's definition is shown below:

typedef struct {
    TokenChars				GT_chars;
    ManufacturerID				GT_manufID;
} GeodeToken;
typedef char TokenChars[TOKEN_CHARS_LENGTH];

This structure is created and filled automatically in the geode's .geo file header by the Glue linker, which takes the values from the geode's geode parameters ( .gp ) file. The two fields used from the .gp file are tokenchars for the four characters and tokenid for the manufacturer ID.

A GeodeToken structure in the token database file can also be filled in when GeoManager scans a directory. If the header of a particular application's .geo file does not have a recognized token, GeoManager will launch the application in "engine" mode, loading its GenApplication object. It will request that the application object then install its icon into the token database file ( GenApplicationClass knows how to do this).

The token database file contains one entry for each token that has been installed. Each time a new token and icon is encountered, a new entry is added by the UI. This happens automatically when GeoManager scans a directory.

Applications and Geodes: 2.2 Creating Icons: Managing the Token Database File

TokenDefineToken(), TokenGetTokenInfo(), TokenLookupMoniker(), TokenLoadMonikerBlock(), TokenLoadMonikerChunk(), TokenLoadMonikerBuffer(), TokenRemoveToken(), TokenLoadTokenBlock(), TokenLoadTokenChunk(), TokenLoadTokenBuffer(), TokenLockTokenMoniker(), TokenUnlockTokenMoniker(), TokenGetTokenStats(), TokenListTokens()

In nearly all cases, you will create your application's icon with the icon tool and not worry about it again. However, the following routines allow you to add, change, access, and remove entries from the token database.

TokenLoadTokenBlock() , TokenLoadTokenChunk() , and TokenLoadTokenBuffer() load a TokenEntry structure into memory (a newly allocated block, a newly allocated chunk, or a locked buffer). The TokenEntry structure contains information about the token, the geode's release number, and the geode's protocol number. This structure does not actually contain the monikers used for the icon.

TokenLookupMoniker() gets the specific moniker of a token entry given the display type (CGA, EGA, VGA, etc.), the entry's GeodeToken structure, and search flags. If the moniker is found, the entry identifier (database group and ID numbers) of the moniker are returned. You can use these return values to lock the moniker into memory (see below).

TokenLockTokenMoniker() locks a moniker into memory given its entry identifier. This routine returns a pointer to a locked block and the chunk handle of the chunk containing the locked moniker. A moniker should always be locked before it is drawn; this keeps it from moving in memory while it is being accessed. The routine TokenUnlockTokenMoniker() unlocks a previously locked moniker when given the moniker's segment address. This unlocks the entire block, not just the individual moniker.

TokenLoadMonikerBlock() , TokenLoadMonikerChunk() , and TokenLoadMonikerBuffer() load a specific moniker into memory from the token database file (into a newly allocated block, a newly allocated chunk, or a locked buffer). It takes the same parameters as TokenLookupMoniker() but returns the handle and chunk handle of the loaded moniker. If using this routine, simply lock the memory block rather than using TokenLockTokenMoniker() .

TokenGetTokenInfo() finds a token when passed its tokenchars and tokenid and returns the token's flags. If no token exists with the passed characteristics, it will return an error flag.

TokenDefineToken() adds a new token and its moniker list to the token database. If the given token already exists, the new one will replace the old one. The token identifier ( tokenchars , tokenid ), handle and chunk handle of the moniker list, and flags of the new token must be passed.

TokenRemoveToken() removes a given token and its moniker list from the token database file. It returns only a flag indicating whether the token was successfully removed or not.

TokenListTokens() returns a list of the tokens in the token database. It is passed three arguments:

  • A set of TokenRangeFlags , which specifies which tokens should be returned. The following flags are available:
TRF_ONLY_GSTRING
Return only those tokens which are defined with a GString.
TRF_ONLY_PASSED_MANUFID
Return only those tokens which match the passed manufacturer's ID.
  • A number of bytes to reserve for a header.
  • A manufacturer ID. This field is ignored if TRF_ONLY_PASSED_MANUFID was not passed.

TokenListTokens() allocates a global memory block and copies all the specified tokens into that block. It leaves a blank space at the beginning of the block; this space is the size specified by the second argument. The rest of the block is an array of GeodeToken structures. TokenListTokens() returns a dword. The lower word of the return value is the handle of the global memory block; the upper word is the number of GeodeToken structures in that block.

Applications and Geodes: 3 Saving User Options

Almost all users enjoy configuring their systems to their own tastes, whether it's setting the background bitmap or choosing a default font. Most applications will, therefore, want to provide a way for the user to choose and save various options for the application.

Generic objects have this capability built in. If you set them up for saving options, they will automatically set and maintain the saved options, making sure the options are set properly whenever the application is launched. However, sometimes you will want to set additional options not managed by UI objects. This is not difficult to do in GEOS: You can have your application save its options directly to the local initialization file, GEOS.INI.

Saving Generic Object Options

The GEOS.INI File

Applications and Geodes: 3.1 Saving User Options: Saving Generic Object Options

All appropriate generic UI objects have the ability to save their options. For example, a properties GenInteraction could save which of its options are on and which are off when the user presses a "save options" trigger.

To save generic object options, you have to do two basic things:

  • Create an Options menu
    Typically, you will create a menu for user options. This menu should be declared of type GIGT_OPTIONS_MENU as shown in Saving Generic Object Options .
  • Use a special GCN list
    All objects that want to save options must be put on a special GCN list in the application's GenApplication object. The list type should be either GAGCNLT_STARTUP_LOAD_OPTIONS (if the options should be loaded at the time of application startup) or GAGCNLT_SELF_LOAD_OPTIONS (if the options should be loaded when the object deems it necessary, typically when the object is first displayed).

Saving Generic Object Options shows an example of how objects should be declared for saving their options.

Code Display 7-2 Saving Generic Object Options

/* The GenApplication object must declare a GCN list of the type appropriate for
 * the options being saved (or both types if the application uses both types).
 * This GenApplication declares a list of objects whose options do not have to be
 * loaded at startup. */
@object GenApplicationClass SampleApp = {
    GI_visMoniker = "Sample Application";
    GI_comp = SamplePrimary;				/* Primary window object is the only child. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = SamplePrimary;
        /* The above list is to declare windowed objects that must appear
         * when the application is opened and made usable. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) =
                    SampleController;
        /* The above list is used for generic objects that save their
         * options but do not need their options loaded at startup. */
}
/* Some applications that have generic objects save their own options might not
 * have a special Options menu but may just have a trigger somewhere for saving
 * options. In any case, the "save options" trigger sends MSG_META_SAVE_OPTIONS to
 * its GenApplication object. If you use an Options menu with GIGT_OPTIONS_MENU
 * set, this will automatically be built into the menu.
 * The SampleOptionsMenu is a child of SamplePrimary, not shown. */
@object GenInteractionClass SampleOptionsMenu = {
    GI_comp = SampleToolbox, SampleToolControl, SampleSaveOptsTrigger;
    GII_visibility = GIV_POPUP;					/* Make it a menu. */
    ATTR_GEN_INTERACTION_GROUP_TYPE = (GIGT_OPTIONS_MENU);
}
/* The other objects (controllers) are not shown here. Just the "save options"
 * trigger, which sends MSG_META_SAVE_OPTIONS to the GenApplication object. */
@object GenTriggerClass SampleSaveOptsTrigger = {
    GI_visMoniker = `S', "Save Options";
    GTI_destination = SampleApp;
    GTI_actionMsg = MSG_META_SAVE_OPTIONS;
}

Applications and Geodes: 3.2 Saving User Options: The GEOS.INI File

GEOS can use multiple initialization files in network situations, but only the local GEOS.INI is editable. The local GEOS.INI (referred to hereafter simply as GEOS.INI or "the INI file") is read first into a moveable, swappable buffer, and it contains the path names of any other INI files to be used. The other INI files are subsequently loaded into other buffers. When the kernel searches for a specific entry in one of the INI files, it looks first in the local INI's buffer and then in any others in the order they were loaded. When it reaches what it wants, it stops searching. Thus, multiple entries are allowed but are not used, and the local INI file has precedence over all others.

Configuration of the INI File

The GEOS.INI file has a very specific format and syntax. It is segmented into categories , each of which contains several keys that determine how GEOS will perform. (For an example of an INI file, see Example GEOS.INI File Entries .) Note that typically the GEOS.INI file should not be accessed directly because its format is subject to change.

A category is essentially a group of keys. Certain geodes will work with their own categories, and certain categories will be used by several geodes. For example, the system category is used by the kernel and the UI to determine several of the default system settings. Each category is set in GEOS.INI by putting its name within square brackets. The text within the brackets is case-insensitive and ignores white space, so [My category], [mycategory], and [MY CATEGORY] are all equivalent.

A key is any setting that the system or an application can recognize and assign a value to. A single key may exist in several different categories; since both category and key define an entry, the entries will be unique if either key or category is different. Keys may be text strings, integers, or Boolean values, and every key is identified by its name, which is an ASCII string. Data may also be read from and written to the file in binary form; the kernel will automatically convert this into ASCII hexadecimal for storage and will revert it to binary during retrieval.

Both category names and key fields are called "entries." Each entry may exist on a single line or may cover several lines. An entry that contains carriage returns is technically known as a "blob." Each blob will automatically be enclosed in curly braces when it is written into the file. Any blob that contains curly braces will automatically have backslashes inserted before the closing braces so GEOS doesn't mistake them for the blob delimiters.

Comments may be added to the INI file by putting a semicolon at the beginning of the line. The file has several standard categories and keys that can be set within them. These are detailed in the INI File chapter .

Code Display 7-3 Example GEOS.INI File Entries

; The category "system" is used by the kernel and the UI to set certain system
; defaults such as the number of handles, the default system font and size, and
; the types of memory drivers to be loaded.
[system]
; The handles key is assigned an integer that determines the number of handle
; spaces allocated for the system's Handle Table.
handles = 2500
; The font key is assigned a character string that represents a file name
; or a list of file names separated by spaces.
; The listed file(s) will be read in as the font driver geode.
font = nimbus.geo
; The memory key is assigned a blob of text containing all the names of the memory
; drivers available to the system.
memory = {
disk.geo
emm.geo
xms.geo
}
; The category "MyApp's Category" is set for example only. It is used by the MyApp
; application.
[My App's Category]
; The myappHiScore key is an integer key set by the MyApp application.
myappHiScore = 52
; The myappBoolean key is a Boolean value. Booleans are case-insensitive, so True,
; true, and TRUE are all equated to "true". This is actually a Boolean value and
; is translated by the read and write routines that work with Boolean values.
myappBoolean = true
; The myappHiName is a text string that, in this case, contains carriage returns,
; backslash characters, and curly brace characters. The original text looked like
; this:
;		this is a multi-line
;		blob of text with curly
;		brace ({,}) characters }} in it
; It is automatically given backslashes in front of the closing braces, and it
; is automatically surrounded with curly braces.
myappHiName = {this is a multi-line
blob of text with curly
brace ({,\}) characters \}\} in it}

Managing the INI File

InitFileSave(), InitFileRevert(), InitFileGetTimeLastModified(), InitFileCommit()

Because the INI file is common to all geodes and to all threads in the system, only one thread at a time may access it. This synchronization is handled by the kernel whenever a routine to read from or write to the INI file is used. Additionally, the INI file is loaded into a buffer when the system is first run; all operations on the INI file actually work on this buffer, and the buffer may be flushed to disk only by the kernel.

There are some general utility routines useful for managing the INI file. One saves a backup of the file, another reverts it from the last backup, the third checks the time the file was last modified, and the fourth commits any pending INI file changes to disk.

To update the backup copy of GEOS.INI, use the routine InitFileSave(). It requires no parameters and returns an error flag if the file could not be saved. InitFileRevert() reverts the GEOS.INI file to its state the last time it was backed up. This routine takes no parameters, and it returns an error flag if the revert can not be accomplished.

InitFileGetTimeLastModified() returns the system counter's value stored the last time GEOS.INI was modified.

InitFileCommit() takes all the changes made since the file was last modified and flushes them to disk. This commits all the changes and should be used only from the kernel. It should not be used by applications.

Writing Data to the INI File

InitFileWriteData(), InitFileWriteString(), InitFileWriteStringSection(),
InitFileWriteInteger(), InitFileWriteBoolean()

GEOS provides five routines to write to the INI file, one for each of the allowable data types. Each of these routines will first gain exclusive access to the local INI buffer, then locate the appropriate category for writing. After writing the key and its value, the routine will relinquish exclusive access to the buffer, allowing other threads to write into it. Writing a category or key that does not exist in the local INI file will add it to the file. If you use a routine to write keys or categories to the INI file, be sure to first convert the strings to the acceptable ASCII hex format using InitFileMakeCanonicKeyCategory().

Each of these routines takes at least three arguments: The category is specified as a null-terminated character string; a pointer to the string is passed. The key name is also specified as a null-terminated character string; again, a pointer to the string is passed. The third parameter is specific to the routine and contains the value to which the key will be set.

InitFileWriteData() writes a number of bytes into the INI buffer, and it takes four parameters. The additional parameter is the size of the data. The data will be converted into ASCII hexadecimal when the file is saved and will be converted back when the key is read.

InitFileWriteString() takes a pointer to the null-terminated character string to be written. If the character string contains carriage returns or line feeds, it will automatically be converted into a blob.

InitFileWriteStringSection() writes a new string section (a portion of a blob) into the specified entry. The specified entry must be a blob already, and the string section will be appended to the blob. A string section is a line of a blob delineated by line feeds or carriage returns.

InitFileWriteInteger() takes as its third argument the integer to be written.

InitFileWriteBoolean() takes a Boolean value. A zero value represents false, and any nonzero value represents true. When looking at the INI file with a text editor, the Boolean value will appear as a text string of either "true" or "false"; it will, however, be interpreted as a Boolean rather than a text string.

Getting Data from the INI File

InitFileReadDataBuffer(), InitFileReadDataBlock(), InitFileReadStringBuffer(), InitFileReadStringBlock(), InitFileEnumStringSection() InitFileReadStringSectionBuffer(), InitFileReadStringSectionBlock(), InitFileReadInteger(), InitFileReadBoolean()

When you want to check what a key is set to in the INI file, you should use one of the InitFileRead...() routines. These search the local INI file first and then each of the additional INI files in the order they were loaded. They will return the first occurrence of a given key, so if the key exists in both your local INI file and another INI file, these routines will return only the local value.

All of these routines take at least two parameters. The first is the category of the entry to be retrieved; this is stored as a null-terminated ASCII string, and a pointer to the string is passed. The second is the key of the entry. This, too, is stored as a null-terminated ASCII string, and a pointer to the string is passed.

InitFileReadBoolean() returns the Boolean value of the given key. If the key is set "false," a value of zero (FALSE) will be returned. If the key is set "true," a nonzero value will be returned (-1, the constant TRUE).

InitFileReadInteger() returns the integer value of the given key.

InitFileReadDataBuffer() and InitFileReadDataBlock() both return the data bytes stored in the given key. The first, however, takes the address of a buffer already allocated and puts the data into the buffer. The second allocates a new block on the heap and puts the data into it. If you don't know the size of the data, you should use InitFileReadDataBlock() .

InitFileReadStringBuffer() and InitFileReadStringBlock() both return the null-terminated string stored in the given key. In both cases, curly braces will be stripped off of blobs and backslash characters will be removed if appropriate. The first, however, takes the address of a buffer already allocated and puts the string in the buffer. The second allocates a new block on the heap and returns the string in it. If you don't know the approximate size of the string, use InitFileReadStringBlock() .

InitFileReadStringSectionBuffer() and its counterpart InitFileReadStringSectionBlock() both return a null-terminated section of the string stored in the given key. A string section is defined as any number of contiguous printable ASCII characters and is delimited by carriage returns or line feeds. These routines take the number of the string section desired and return the section (if it exists). InitFileReadStringSectionBuffer() takes the address of a buffer already allocated on the heap and returns the string section in the buffer. InitFileReadStringSectionBlock() allocates a new block on the heap and returns the string section in it. You should use this routine if you don't know the approximate size of the string section.

InitFileEnumStringSection() enumerates the specified blob, executing a specified callback routine on each string section within the blob.

Deleting Items from the INI File

InitFileDeleteEntry(), InitFileDeleteCategory(), InitFileDeleteStringSection()

Besides reading and writing data, you can delete categories and keys that were previously entered. InitFileDeleteEntry() takes pointers to both the null-terminated category name and the null-terminated key name and deletes the entry. InitFileDeleteCategory() takes only a pointer to the null-terminated category name and deletes the entire category, including all the keys stored under it.

In addition, you can delete a single string section from a specified blob. InitFileDeleteStringSection() takes the category and key names of the blob as well as the index of the string section, and it deletes the string section. If the string section does not exist or if either the key or category can not be found, the routine will return an error flag.

Applications and Geodes: 4 General System Utilities

The kernel provides a number of routines that fulfill general needs for system utilities. These range from setting the system's date and time to retrieving the amount of swap memory used to shutting down the system in order to execute a DOS-based program.

System Clock

Using Timers

System Statistics and Utilities

Shutting the System Down

Applications and Geodes: 4.1 General System Utilities: System Clock

TimerGetDateAndTime(), TimerSetDateAndTime(), TimerGetFileDateTime()

The system clock reflects the current date and time of day. TimerGetDateAndTime() takes a pointer to a TimerDateAndTime structure and fills it with the current year, month, day, day of week, hour, minute, and second. TimerGetFileDateTime() is a similar function--it gets the current date and time, returning them in structures used when working with files. TimerSetDateAndTime() sets the current date and time to the values in the passed structure. It is unusual for any application other than the Preferences Manager to change the system clock.

Applications and Geodes: 4.2 General System Utilities: Using Timers

TimerStart(), TimerStop(), TimerSleep(), TimerGetCount()

If your geode needs an action to happen on a timed basis, you will want to use a timer. GEOS lets you set up timers that will call a routine or send a message after a given interval has passed. GEOS offers four types of timers:

  • One-shot
    A one-shot timer counts a certain number of "ticks." (There are 60 ticks in a second.) It will call a routine or send a message after the specified number of ticks have been counted.
  • Continual
    A continual timer counts a certain number of ticks and then resets and counts again. Each time it reaches the specified number of ticks, it calls a routine or sends a message.
  • Millisecond
    A millisecond timer is a one-shot timer that counts in milliseconds rather than ticks and calls a routine when time is up (it does not send a message).
  • Sleep
    A sleep timer causes the thread that creates it to sleep for a certain number of ticks. The sleep timer is unlike the others in that it does not send a message or call a routine when time is up; it only awakens the sleeping thread.
  • Real-time
    A real-time timer is passed the number of days since 1980, the hour, and the minute of the event. On hardware platforms that support such a thing, GEOS will wake up when the real-time timer goes off. (All other timers are ignored under these conditions.)

All timers except sleep timers are created and started with the routine TimerStart() , and continual timers can be destroyed with TimerStop() . TimerStop() may also be used to prematurely stop a one-shot timer, though if the one-shot sent a message, the message could be in the recipient's queue even after TimerStop() was called. Sleep timers are created and started with TimerSleep() , and they do not need to be stopped.

An additional routine, TimerGetCount() , returns the current system counter. The system counter contains the number of ticks counted since GEOS was started.

Applications and Geodes: 4.3 General System Utilities: System Statistics and Utilities

SysStatistics(), SysGetInfo(), SysGetConfig(), SysGetPenMode(), SysGetDosEnvironment()

Occasionally, a geode will need to query the kernel about the state or configuration of the system. GEOS offers five routines for this:

SysStatistics() returns a structure of type SysStats . This structure contains information about how busy the CPU is, how much swap activity is going on, how many context switches occurred in the last second, how many interrupts occurred in the last second, and how many runnable threads exist. You can not set or otherwise alter this structure.

SysGetInfo() returns a particular statistic about the current system status. It can return a number of different statistics including the CPU speed, the total number of handles in the handle table, the total heap size, the total number of geodes running, and the size of the largest free block on the heap. Most of the information your application can request with this routine can be garnered from the Perf performance meter; it is easiest to use Perf when debugging, though a few geodes will need to know this information.

SysGetConfig() returns information about the current system's configuration including the processor type and machine type. It also returns flags about the particular session of GEOS including whether this session was started with Swat, whether a coprocessor exists, and other information.

SysGetPenMode() returns TRUE if GEOS is currently running on a pen-based machine.

SysGetDosEnvironment() returns the value of a DOS environment variable. It is passed a string representing the variable name and returns a string representing the value.

Applications and Geodes: 4.4 General System Utilities: Shutting the System Down

DosExec(), SysShutdown()

There are two ways for a geode to shut down GEOS. The first, DosExec() , shuts the system down to run a program under DOS, returning after the DOS program has finished--unless a task-switch driver is in use, in which case the system will create a new task and cause the task-switcher to switch to the new task. The second, SysShutdown() , forces the system to shut itself down completely. Neither of these routines is commonly used by anything other than the kernel, GeoManager, special "launcher" programs, or the UI. Their use by other libraries or applications is discouraged unless absolutely necessary.

DosExec() takes several parameters including the pathname of the DOS program to be run, arguments for the program, an optional disk handle of the disk that contains the program to be run, the optional directory and disk handle in which the program should be executed, and a record of DosExecFlags . If the return value is nonzero, an error occurred in loading the DOS program, and you can use ThreadGetError() to check what error occurred. Note that DosExec() always returns. Applications should not rely on DosExec() shutting the system down; if a task switcher is present, GEOS will be swapped out rather than shut down.

SysShutdown() causes GEOS to exit in one of several ways. This routine should be passed a shutdown mode. If the mode is SST_CLEAN, SST_RESTART, SST_SUSPEND, or SST_CLEAN_FORCED, the routine will return; otherwise, it will not return and the shutdown will commence. If SST_CLEAN is passed, the shutdown may be aborted after SysShutdown() returns. You can have SysShutdown() cause GEOS to reboot itself after shutting down (as the Preferences Manager application does for certain preferences settings), but this starts GEOS fresh. This routine is very rarely used by anything other than the UI, the kernel, or the Preferences Manager application.

If something else (typically the UI or task switcher) shuts the system down, objects that register for shutdown notification will receive MSG_META_CONFIRM_SHUTDOWN . The application should call SysShutdown() with the mode SST_CONFIRM_START; this allows the object to have exclusive rights for asking the user to confirm the shutdown (when the object is finished with the user interaction, it can call SysShutdown() with SST_CONFIRM_END to release exclusive access). This is useful if your application or library has an ongoing operation and wants to verify the shutdown with the user.

Applications and Geodes: 5 The Error-Checking Version

GEOS has two versions of its system software. The normal version as shipped retail is the "non-error-checking" version. The other, used for debugging applications and other geodes, is called the "error-checking" version, or EC version . Nearly all components of the system software exist in both versions--the kernel, the UI, libraries, drivers, etc.

Together, Swat and the EC version provide extensive and superb debugging power. The EC version allows you to call special error-checking routines to check the integrity of handles, resources, memory, files, and other things. These error-checking routines all begin with EC; for more information, see their entries in the Routine Reference Book. In addition to the full error checking provided by the EC versions of the system geodes, you can add your own error-checking code to your programs.

Adding EC Code to Your Program

Special EC Routines

Applications and Geodes: 5.1 The Error-Checking Version: Adding EC Code to Your Program

When compiling your code, you can include certain extra lines in either the EC version or the non-EC version of your program. For example, during debugging you want your program to check the validity of each memory handle before locking the memory block; for the final version of your program, however, you don't want this validity check because it slows down your program. You can easily add a few instructions that will be compiled only into the EC version.

GEOS provides five macros, listed below, for version-specific instructions. These macros must be treated as statements; they may not be used in expressions.

EC
This macro adds the specified line of code to the EC version only. When the non-EC version is compiled from the same code, the line will be left out.
EC_ERROR
This macro halts the execution of the EC version of a program by calling FatalError() with a specified error code. The call to FatalError() will not be included in the non-EC version.
EC_ERROR_IF
This macro works like EC_ERROR, above, but it also allows you to set a condition for whether FatalError() will be called. If the condition is met, FatalError() will be called.
NEC
This macro adds the specified line of code to the non-EC version but leaves it out of the EC version. (It is the converse of the EC macro.)
EC_BOUNDS
This macro checks the validity of a specified address (pointer) by calling the ECCheckBounds() routine. If the pointer is out of bounds, ECCheckBounds() will call FatalError() . This macro may only add the bounds check to the EC version.

An example of use of these macros is shown in EC Macros .

Code Display 7-4 EC Macros

/* This code display shows only the usage of these macros; assume that each
 * line shown below exists within a particular function or method. */
/* The EC macro adds a line of code to the EC version. Its format is
 *	EC(line)		where line is the line of code to be added.
 * Note that the NEC macro is similar. */
    EC( @call MyErrorDialogBox::MSG_MY_ERR_PRINT_ERROR(); )
    NEC( @call MyErrorDialogBox::MSG_MY_ERR_PRINT_NO_ERROR(); )
/* The EC_ERROR macro adds a call to FatalError() to the EC version. Its format is
 *	EC_ERROR(code)		where code is the error code to be called. */
    EC_ERROR(ERROR_ATTR_NOT_FOUND)
/* The EC_ERROR_If macro is similar to EC_ERROR but is conditional. Its format is
 *	EC_ERROR_IF(test, code)			where test is a Boolean value and code is the
 * error code to be called. */
    lockVariable = MyAppCheckIfLocked();					/* TRUE if inaccessible. */
    EC_ERROR_IF(lockVariable, ERROR_ACCESS_DENIED)						/* Error if inaccessible. */
/* The EC_BOUNDS macro adds a call to ECCheckBounds() to the EC version.
 * Its format is
 *	EC_BOUNDS(addr)		where addr is the address to be checked. */
    myPointer = MyAppGetMyPointer();
    EC_BOUNDS(myPointer)

Applications and Geodes: 5.2 The Error-Checking Version: Special EC Routines

SysGetECLevel(), SysSetECLevel(), SysNotify(), EC...(), CFatalError(), CWarningNotice()

While using Swat and the EC version, you can set and retrieve the current level of error checking employed. The level is set with a record of flags, each of which determines whether a certain type of checking is turned on. This record is of type ErrorCheckingFlags . You can retrieve it with SysGetECLevel() and set it with SysSetECLevel() .

SysNotify() puts up the standard error dialog box--white with a black frame. This routine is available in both the EC and the non-EC versions of the kernel. This is the box used by the kernel to present unrecoverable errors to the user. You can also call it up and allow the user any of five options: retry, abort, continue, reboot, or exit. Usually, this dialog box is used only for errors, but it can be used for other user notification as well. (Note that with the "exit" and "reboot" options, this routine will not return. Note also that only certain combinations of the five options are supported.)

The CFatalError() and CWarningNotice() routines provide run-time and compile-time error or warning messages. The CFatalError() routine calls the kernel's FatalError() function, which puts up an error dialog box and, in Swat, causes Swat to hit a breakpoint so you can debug the error. CWarningNotice() may be put in error-checking code to make the compiler put up a compile-time warning note.

Applications and Geodes: 6 Inter-Application Communication

Applications do not usually need to communicate with each other. Most applications will interact only with the kernel and with libraries; such applications don't even notice if any other applications are running. However, a few applications will need to communicate with other applications.

For example, many desktop-managers provide a "print file" command; the user selects a file's icon, then chooses this command to print out the appropriate file. The desktop manager will have no way of knowing how to print the file; after all, the file could have been created by an application which hadn't even been written when the desktop manager was installed. The desktop manager therefore causes the appropriate application to be started. It then instructs the application to print the file.

Because GEOS is an object-oriented system, there is a natural model for inter-application communication. When one application needs to contact another, it can simply send a message; thus, sending information or instructions to another application is not, in principle, different from sending it from one object to another within an application.

The GEOS Inter-Application Communications Protocol (IACP) specifies how to open communications with another application, and how to send messages back and forth.

IACP Overview

GenApplicationClass Behavior

Messages Across an IACP Link

Being a Client

Being a Server

Applications and Geodes: 6.1 Inter-Application Communication: IACP Overview

There is a major difference between sending a message within an application, and sending one to a different application. When you send a message from one object to another within an application, you know that the recipient exists, and you know the optr of that recipient. This makes it easy to send messages.

When you send a message to another application, however, you do not (at first) know any optrs to that application. In fact, you may not even know that the application is running. Often, all you will know is something like, "I want to send a message to SpiffyWrite ".

GEOS uses a client-server model of inter-application communication. Every GeodeToken corresponds to a server. Whenever an application is launched, GEOS checks to see if there is a server-list corresponding to the application's token. If there is, GEOS adds the app's Application object to the server-list; if there is not, GEOS creates a server-list and adds the Application object to that list.

For example, suppose the user launches a single copy of SpiffyWrite ; this application has a manufacturer-ID of MANUFACTURER_ID_SPIFFYWARE, and the token characters "SWRI". GEOS will check if there's a server-list for that GeodeToken . Let us suppose there isn't such a list; GEOS will automatically create one, and add SpiffyWrite 's Application object to that list. The Application object is now said to be a server for the list.

Now let us suppose another application needs to contact SpiffyWrite ; for example, perhaps a desktop program needs to print a SpiffyWrite file. It tells the kernel that it would like to be a client on the list for the token "{MANUFACTURER_ID_SPIFFYWARE, "SWRI"}". GEOS will check to see if a server-list for that token exists. If so, it will add the client to that list; this will cause a notification message (MSG_META_IACP_NEW_CONNECTION) to be sent to every server for that list.

Once a client is linked to a server, it can send a message to the server list. It does this by encapsulating a message, then passing the encapsulated message to the server-list. GEOS will dispatch the message to every server for the list; the server objects will receive it just like any ordinary message. (It actually passes the encapsulated message to the server object as an argument to MSG_META_IACP_PROCESS_MESSAGE ; the server can then dispatch the message to the final recipient.)

This establishes the link between the applications. The client can pass an optr to the servers by putting it in the encapsulated message; a server object can then send messages straight to a particular object.

When a client no longer needs to communicate with a server, it unregisters itself from the server list. GEOS then sends a notification message to every server object.

Customarily, whoever allocates a global resource must also free it. For example, if a client might pass information to the server by allocating a global block, writing the data to the block, and passing the block to the server. The server should notify the client when the server is finished with that data; the client can then free the block. Similarly, if a server allocates a block to pass information to a client, the server should free the block.

There may be several servers for a given server list. For example, if three copies of SpiffyWrite were running at once, each of their Application objects would be a server for the same server-list. Furthermore, any object can make itself a server for any list. All servers will receive copies of every message sent to the server list. To distinguish between different servers for a list, every server for a list is assigned a distinct ServerNumber . If it chooses to, a client can specify that a message be sent only to the server with a specific number.

Applications and Geodes: 6.2 Inter-Application Communication: GenApplicationClass Behavior

GenApplicationClass is built to support IACP automatically. If a server or client object is subclassed from GenApplicationClass , most of the work of supporting IACP is done transparently to the application writer. The following capabilities are built in:

  • The Application Object automatically registers itself as a server for the appropriate list when it is launched in application mode. If it is launched in engine mode, it registers itself when it receives MSG_META_APP_STARTUP. If the last client-connection to the application is closed, and the Application is not currently running in application mode, the Application object will shut down automatically.
  • When the Application object registers itself as a server for its own list, it sends itself MSG_GEN_APPLICATION_IACP_REGISTER. An application may subclass this if it wants to take other action at this time (e.g. registering itself for other lists). Similarly, when an Application object unregisters itself from its own server list, it sends itself MSG_GEN_APPLICATION_IACP_UNREGISTER.
  • The Application object automatically handles the MSG_META_IACP... messages appropriately. In particular, when the kernel passes an encapsulated message to an Application object with MSG_META_IACP_PROCESS_MESSAGE , the Application object automatically dispatches the message to the appropriate location.
  • An Application object will refuse to quit as long as any client has an open IACP connection to it. (It can, however, be forcibly detached; this happens when the system is shut down, as noted below.). In such a case, the Application object will automatically call IACPShutdownAll() to shut down all IACP links it has open, whether it is a client or a server on those links.
  • When a link is closed, IACP automatically sends MSG_META_IACP_LOST_CONNECTION to all objects on the other side of the link. When an Application object receives this message, it waits until all remaining messages from the link have been handled; it then calls IACPShutdown() for that connection. It also forwards this message to all Document objects, so a Document object will know to close itself if the IACP connection was the only reference to it. Again, the Application object does this whether it is a client or a server.
  • If the Application object is forcibly detached, it sends itself MSG_GEN_APPLICATION_IACP_SHUTDOWN_ALL_CONNECTIONS. The default handler for this message will call IACPShutdownAll() to shut down all IACP links the Application object has open, whether it is a client or a server on those links. You can subclass this message if you need to take some additional action when the IACP connections are severed.

Applications and Geodes: 6.3 Inter-Application Communication: Messages Across an IACP Link

IACPSendMessage(), IACPSendMessageToServer()

Either a client or a server may send messages over an IACP link. Both clients and servers use the same technique. The message sender encapsulates a message, and passes the encapsulated message to IACPSendMessage() . IACPSendMessage() dispatches the message to every object on the other side of the link. For example, if a client passes a message to IACPSendMessage() , that message will be dispatched to every server object for the specified list.

IACPSendMessage() is passed five arguments:

  • The token for the IACP link.
  • The EventHandle of an encapsulated message.
  • The TravelOption for that message.
  • An encapsulated completion message; this will be dispatched once each time the first message has been successfully handled.
  • A member of the IACPSide enumerated type. This tells whether the message is being sent by a client or a server. If you pass the value IACPS_CLIENT, the message will be dispatched to all servers; if you pass IACPS_SERVER, the message will be dispatched to all clients.

The message will be dispatched to all geodes on the other side of a link. Note that a client need not send the message to the server object per se. It can use the travel options field to direct the message anywhere within the server object's geode. It can also specify the optr of the recipient when it encapsulates the message; in this case, it should pass a TravelOption of -1.

Every time the encapsulated message is successfully handled, the "completion message" will be dispatched. Typically, the completion message is addressed to the object that called IACPSendMessage() , instructing it to free global resources that had been allocated for the message.

The routine returns the number of messages that were dispatched. This lets the sender know how many completion messages to expect, and lets it properly initialize all reference counts to global resources.

A client may choose to send a message to a specific server. It can do this by calling IACPSendMessageToServer() . This takes almost the same arguments as IACPSendMessage() . However, instead of being passed an IACPSide value, it is passed a server number. GEOS will dispatch a single copy of the message to the specified server. IACPSendMessageToServer() returns the number of times the message was dispatched. This will ordinarily be one; however, if the specified server is no longer registered, it will be zero.

Applications and Geodes: 6.4 Inter-Application Communication: Being a Client

Any object can register as a client for an IACP server list. When an object is a client, it can send messages to the server list, which will pass them along to the servers for that list.

Registering as a Client

IACPConnect(), IACPCreateDefaultLaunchBlock()

To register as a client for a list, call the routine IACPConnect() . When you call this routine, you specify which server list you are interested in. If there is no such server list running, you can instruct the kernel to start up the server list, as well as one of the default applications for that list.

IACPConnect() is passed five arguments:

  • The GeodeToken of the list for which you want to register.
  • A set of IACPConnectFlags . The following flags are available:
IACPCF_OBEY_LAUNCH_MODEL
This indicates that GEOS should follow the launch model, which will specify whether the user should be presented a dialog box, asking the user whether an existing application should be used as the server, or a new application launched. If you set this flag, you must pass an AppLaunchBlock , with the ALB _appMode field set to MSG_GEN_PROCESS_OPEN_APPLICATION; you must also set IACPCF_SERVER_MODE to IACPSM_USER_INTERACTIBLE.
IACPCF_CLIENT_OD_SPECIFIED
This flag indicates that you will specify what object will be the new client. If you do not set this flag, the sending geode's Application object will be registered as the client.
IACPCF_FIRST_ONLY
If you pass this flag, the client will be connected to only the first server on the server list. Any messages sent by the client to the server list will be passed only to that one server.
IACPCF_SERVER_MODE
This field is three bits wide; it holds a member of the IACPServerMode enumerated type. This type specifies how the client expects the server to behave. Currently, only two types are supported:
IACPSM_NOT_USER_INTERACTIBLE
This is equal to zero. It indicates that the server object need not interact with the user.
IACPSM_USER_INTERACTIBLE
This is equal to two. It indicates that the server should be able to interact with the user like any normal application.
  • The MemHandle of an AppLaunchBlock . If the server you specify is not running, GEOS will launch the application specified by the AppLaunchBlock . If you pass a null MemHandle , GEOS will return an error if no server is running.
  • The optr of the client object. This is ignored if IACPCF_CLIENT_OD_SPECIFIED was not passed.
  • A pointer to a word. IACPConnect() will write the number of servers on the list to that word.

IACPConnect() returns a word-sized IACPConnection token. You will need to pass that token when you call another IACP routine to use the connection. It will also return the number of server objects on the list; it returns this value by writing it to the address indicated by the pointer passed.

If the server list you indicate is not currently running, IACP may do one of two different things. If you pass a null handle as the third argument, IACPConnect() will fail. It will return the error value IACP_NO_CONNECTION, and indicate that there are no servers on the specified list.

If you pass an AppLaunchBlock , IACPConnect() will examine that launch block to see what application should be launched to act as a server. The AppLaunchBlock should specify the location and name of the application to open. If the ALB _appRef.AIR_diskHandle field is non-zero, IACPConnect() will look in the specified disk or standard path for an application with the right GeodeToken ; otherwise, it will look in the standard places for an application.

Note that if you pass a launch block to IACPConnect() , you may not alter or free it afterwards. If the application is created, the block you pass will be its launch block; if not, the kernel will free the block automatically. In any event, the caller no longer has access to the block.

If IACPConnect() launches an application, the caller will block until that application has been created and registers for the server list. If the application does not register for that list, the caller will never unblock. You must therefore make sure that you are launching the right application for the list. Note that every application object will automatically register for the server list which shares its token.

To create a launch block, you should call IACPCreateDefaultLaunchBlock() . This routine is passed a single argument, which specifies how the application will be opened. That argument must be MSG_GEN_PROCESS_OPEN_APPLICATION (the application will be opened as a standard, user-interactible application); MSG_GEN_PROCESS_OPEN_ENGINE (the application will be opened in engine mode, i.e. with no user interface); or MSG_GEN_PROCESS_OPEN_CUSTOM (which has an application-specified meaning).

IACPCreateDefaultLaunchBlock() allocates a launch block and sets up its fields appropriately. As created, the launch block will have the following characteristics:

  • The application's initial working directory (i.e. the launch block's ALB _diskHandle field) will be SP_DOCUMENT.
  • No application directory will be specified in the launch block (i.e. ALB _appRef. AIR _diskHandle will be zero); IACPConnect() will attempt to find the application on its own.
  • No initial data file will be specified (i.e. ALB _dataFile will be blank).
  • The application will determine its own generic parent (i.e. ALB _genParent will be null).
  • No extra data word will be passed to the application (i.e. ALB _extraData will be zero).
  • There will be no output descriptor (i.e. ALB _userLoadAckOutput and ALB _userLoadAckMessage will be null.)

IACPCreateDefaultLaunchBlock() returns the handle of the newly-created launch block. Once the block is created, you can alter any of its fields before passing the launch block to IACPConnect() . (Once you pass the launch block to IACPConnect() , you may not alter it any more.)

Often a client will want the server to open a specific document. For example, if a desktop-manager is implementing a "print-file" command, it will need to open a server application, instruct it to open the file to be printed, and then instruct it to print the file. To make the server open a document, pass the document's name in ALB _dataFile . The server will open the file when you register, and close it when you unregister.

Unregistering as a Client

IACPShutdown(), IACPShutdownAll()

If an application no longer needs to interact with a particular server list, it should call IACPShutdown() . This routine is also used by servers which wish to remove themselves from a server list. This section describes how the routine is used by clients; Being a Server describes how it is used by servers.

The routine is passed two arguments:

  • The IACPConnection token for the link which is being shut down.
  • An optr. This must be null if the routine is being called by a client.

IACPShutdown() sends MSG_META_IACP_LOST_CONNECTION to all objects on the other side of the link; that is, if a client calls IACPShutdown() , all servers on the list will be sent this message.

IACPShutdownAll() closes all IACP links for the application which calls it. The Application object automatically calls this routine when the application is exiting.

Applications and Geodes: 6.5 Inter-Application Communication: Being a Server

Every time an application is launched, its Application object automatically registers as a server for the server list that shares its GeodeToken . The Application class has default handlers for all the notification messages IACP sends to the server objects.

If you wish, you can have another object act as a server. However, if you do this, you will have to do more of the work yourself. While the notification messages are defined for MetaClass , and thus can be handled by any class of object, MetaClass does not come with handlers for these messages; if the server is not subclassed from GenApplicationClass , you will have to write the handlers yourself. This is discussed below.

Registering and Unregistering a Server

IACPRegisterServer(), IACPUnregisterServer(), IACPGetServerNumber()

You will not generally need to register and unregister a server object explicitly. As noted above, when an application is launched, the application object is automatically registered as a server for the list with its GeodeToken ; when the application exits, the Application object is automatically unregistered from that list.

However, you may wish to explicitly register an object as a server. For example, you might want your application object to be a server on a list with a different GeodeToken ; or you might want to register a non-Application object as a server. In this case, you will need to explicitly register and unregister the object.

To register an object as a server, call IACPRegisterServer() . This routine is passed the following arguments:

  • The GeodeToken of the list for which you are registering as a server.
  • The optr of the object which is registering as a server. This object must be able to handle the MSG_META_IACP... messages appropriately (this is built into GenApplicationClass ).
  • A member of the IACPServerMode enumerated type. This type specifies how the client expects the server to behave. Currently, only two types are supported:
IACPSM_NOT_USER_INTERACTIBLE
This is equal to zero. It indicates that the server object will not interact directly with users.
IACPSM_USER_INTERACTIBLE
This is equal to two. It indicates that the server will be able to interact with the user like any normal application.
  • A set of IACPServerFlags . Currently, only two flags are supported: IACPSF_MULTIPLE_INSTANCES indicates that multiple copies of the application might be running at once (Every multi-launchable application should set this flag.); IACPSF_MAILBOX_DONT_ASK_USER asks the Mailbox library not to notify the user of new messages for this application.

IACPRegisterServer() registers the object as a server for the appropriate list; it creates the server list if necessary.

To unregister an object as a server, call IACPUnregisterServer() . This routine is passed two arguments: the GeodeToken of the server list, and the optr of the server. The object will be removed immediately from the server list. Note, however, that the server list might have already dispatched some messages to the server being removed; these messages might be waiting on the server object's queue, and thus the server object might get some IACP messages even after it calls IACPUnregisterServer() . One way to deal with this is to have the server object send itself a message, via the queue, immediately after it calls IACPUnregisterServer() . When the object receives this message, it will know that it has no more IACP messages on its queue.

Every server object on a given server list has a unique server number . This server number will not change while the server is attached to the list. A server object can find out its server number by calling IACPGetServerNumber() . This routine takes two arguments: the IACPConnection and the optr to the server object. It returns the object's server number.

Non-Application Servers and Clients

MSG_META_IACP_PROCESS_MESSAGE, IACPProcessMessage(), MSG_META_IACP_NEW_CONNECTION, MSG_META_IACP_LOST_CONNECTION

Every server and client object must be able to handle certain messages. GenApplicationClass comes with handlers for these messages, so you need not write them yourself. However, if you will be using some other kind of object as the server, you must handle the messages yourself. You may also choose to have your application object subclass any of these messages; in that case, you should generally have your handler use @callsuper .

When a server or client sends an IACP message, the kernel passes the encapsulated message to each object on the other side of the link. It does this by sending the message MSG_META_IACP_PROCESS_MESSAGE to each object. This message comes with three arguments:

msgToSend
The EventHandle of the encapsulated message.
topt
The TravelOption for that message.
completionMsg
The EventHandle of any message to be sent after msgToSend has been dispatched. (This field may be set to zero, indicating that there is no completion message.)

The recipient of MSG_META_IACP_PROCESS_MESSAGE should call IACPProcessMessage() . This routine is passed four arguments: the optr of the object calling the routine, and the three arguments passed with MSG_META_IACP_PROCESS_MESSAGE. IACPProcessMessage() dispatches both encapsulated messages properly.

Remember, if the client or server is subclassed from GenApplicationClass , all of this is done for you. You need only write a handler for the message if the client or server object is not from a subclass of GenApplicationClass .

Whenever a client registers on an IACP list, the kernel sends MSG_META_IACP_NEW_CONNECTION to all servers on that list. This message comes with three arguments:

appLaunchBlock
This is the handle of the AppLaunchBlock which the server passed to IACPConnect() .
justLaunched
This is a Boolean value. If the server's application has just been launched specifically to be a server, this will be true (i.e. non-zero).
IACPConnection
This is the token for the IACP connection.

When an object (either client or server) removes itself from an IACP connection, the kernel sends MSG_META_IACP_LOST_CONNECTION to all objects on the other side of the link. This message has two parameters:

connection
The token for the IACP list.
serverNum
If a server removed itself from the list (and this message is being sent to clients), this field will have the number of the server that removed itself. If a client removed itself from the list, this field will be zero.

Whenever a new client is attached to a server list, MSG_META_IACP_NEW_CONNECTION is sent to every server object. This message comes with three arguments:

appLaunchBlock
This is the handle of the application's launch block. The default GenApplicationClass handler examines the launch block to see if the application should open a document.
justLaunched
If the application was just launched specifically to be a server, this field will be true (i.e. non-zero).
connection
This is the token for the IACP connection.

When a client is removed from a server list, every server object is sent MSG_META_IACP_LOST_CONNECTION . Similarly, when a server is removed, every client object is sent MSG_META_IACP_LOST_CONNECTION . The message comes with two arguments:

IACPConnection
This is the token for the IACP connection.
serverNum
If a server left the list (and the message is being sent to clients), this argument will hold its serverNum . If a client left the list (and the message is being sent to servers), this argument will be set to zero.
This document is a single-page version of a a multi-page document, suitable for easy printing.


Threads and Semaphores

One of the most impressive features of GEOS is its ability to perform several tasks simultaneously, even on the least powerful PCs. Of course, the PC has only one processor and can execute only one instruction at a time. GEOS keeps track of the various tasks, or threads , that are underway, and by switching from one thread to another many times per second creates the illusion that the PC is actually doing all the jobs at the same time.

This chapter covers

  • the basic concepts of multitasking,
  • the GEOS multitasking scheme,
  • the steps to creating a multi-threaded application, and
  • the use of semaphores to synchronize threads and avoid deadlock.

You will need to know the information in this appendix if you will be writing a multi-threaded application. The information in the appendix is not essential if your application will be single-threaded or if you will use the standard GEOS dual-thread architecture. For a dual-thread application, be careful not to send a message with @call from an object run by a user interface thread to an object run by any other thread of the application.

1 Multitasking Goals
2 Two Models of Multitasking
2.1 Cooperative Multitasking
2.2 Preemptive Multitasking
3 GEOS Multitasking
3.1 GEOS Threads
3.2 Context Switches
3.3 Thread Scheduling
3.4 Applications and Threads
4 Using Multiple Threads
4.1 How GEOS Threads Are Created
4.2 Managing Priority Values
4.3 Handling Errors in a Thread
4.4 When a Thread Is Finished
5 Synchronizing Threads
5.1 Semaphores: The Concept
5.2 Semaphores In GEOS

Threads and Semaphores: 1 Multitasking Goals

The GEOS multitasking system is one of the most sophisticated available for PCs today. It was designed with the latest available technology and was created to serve the following two primary goals:

  • Fast Response
    The GEOS multitasking system was designed with a strong emphasis on rapid response to user input. Prompt, visible reaction to user action is the single most important factor contributing to the perception of speed. For example, if a user changes an element in a spreadsheet (requiring the whole spreadsheet to be recomputed) and then pulls down a menu, he wants the menu to appear right away. If the menu does not appear until the computation is finished, the system will seem sluggish. If the computation takes a few seconds or longer, the user may wonder whether the system has crashed altogether.
  • Ease of Programming
    Making the programmer's job easy is another motive behind the design of GEOS multitasking. Ideally, the programmer should not need to be aware that his program will run in a multitasking environment. The program should proceed as though it were the only one running on the system. Besides certain "good citizen" rules, application programs are isolated from the multitasking environment. On the other hand, if a programmer wants to take advantage of the multitasking capability of GEOS (by designing a program to perform more than one task concurrently) the system is designed to make this as simple and efficient as possible.

Threads and Semaphores: 2 Two Models of Multitasking

Many operating systems provide the ability to carry out multiple tasks at the same time. While each operating system has its own unique way of managing this, there are two fundamental types of multitasking: cooperative (used by Microsoft Windows), and preemptive (used by GEOS).

Cooperative Multitasking

Preemptive Multitasking

Threads and Semaphores: 2.1 Two Models of Multitasking: Cooperative Multitasking

A cooperative multitasking system, as the name suggests, is one in which the various programs cooperate. They agree to share the system and its resources. Each program running under a system has complete control while it is actually running. Every so often, when it reaches a convenient place, it calls a special system routine (called a context-switch routine) to see if any other program has work do to. If so, that program takes control of the system until it in turn reaches a convenient stopping point and passes control on to the next waiting program. If several programs are ready to run, they wait in a queue so that each one gets a chance to run before the first program runs again. (It is also possible to implement a cooperative multitasking system where some programs have a higher priority than others. In this case, a more complicated algorithm might be used by the context-switch routine to determine which program gets to run next.)

Smooth operation of a cooperative multitasking system requires that all programs be written to call the context-switch routine frequently. When large calculations are being performed, programmers tend to find this requirement inconvenient. Writing well-behaved programs (i.e., programs that do not keep control of the processor for too long at a stretch) is especially difficult because most cooperative multitasking systems impose restrictions on when a context switch can take place.

Threads and Semaphores: 2.2 Two Models of Multitasking: Preemptive Multitasking

In a preemptive multitasking system, programs do not have to relinquish control of the system voluntarily. Instead of calling a context-switch routine, the program is written as though it were going to run continuously from start to finish. The hardware generates a timer interrupt a number of times each second, and that interrupt triggers the kernel's context-switch mechanism.

The context switch can also be triggered by other interrupts. For example, if the user moves the mouse in GEOS, the mouse will generate an interrupt. GEOS responds by marking the input thread runnable; the thread will then run after the interrupt is complete. This is how GEOS achieves its extraordinary response times to user input.

With preemptive multitasking, each program can have the illusion that it is running continuously and has complete control of the system. It also enables the system to interact quickly with the user even when applications are busily computing new results.

For example, a spreadsheet program can keep running until the timer interrupt causes a context switch. Other programs, including the one responsible for drawing menus, then get their turns to run. If a user clicks on a pull-down menu, the menu will appear. When the spreadsheet program regains control of the system, it can carry on from where it was interrupted, blissfully unaware that any of this has taken place.

While preemptive multitasking makes most things simpler for the user and application programmer, there are a few important issues to consider in writing programs for a preemptive multitasking system such as GEOS. When the context switches are controlled by a timer interrupt, they can occur between any two instructions. If a program is interrupted while it is updating a data structure, that data structure may be left in an inconsistent state while another thread is running. If the data structure is not accessed by any other process running on the system, there is no problem: the update will be completed when the program resumes. However, some data structures (including system resources) may be accessed by more than one program. It is important that two updates to the same data do not happen at the same time.

This problem is analogous to one often experienced by network users. If a text file is being edited at the same time by two different users and they both save their changes to the file, whoever saves first will have his version overwritten by the other. Many systems have a means of locking a file while you are editing it; no one else can begin editing the file while you have it locked. A preemptive multitasking system must have a similar locking scheme to prevent two accesses to the same data structure from happening at the same time. The locking mechanism should be as transparent as possible to the programmer. For example, the locking and unlocking of system resources should happen automatically so that application programmers need not concern themselves with it.

This is exactly how GEOS coordinates its resources, as you shall see in the following sections.

Threads and Semaphores: 3 GEOS Multitasking

GEOS implements a preemptive multitasking scheme. Application programs are required to follow certain "good citizen" rules and are otherwise given the illusion that they are alone on the system. The applications themselves are, for the most part, isolated from the multitasking environment.

GEOS Threads

Context Switches

Thread Scheduling

Applications and Threads

Threads and Semaphores: 3.1 GEOS Multitasking: GEOS Threads

The various units that take turns running in the system are called "threads" in GEOS. Threads can have different priorities; a thread that has a higher priority (indicated by a lower priority number) will generally get more processor time.

Keeping Track of Threads

In order to switch among threads, the system needs to keep track of certain things about each one. For each thread, the system keeps track of priority, the most recent values of the registers, and flags. The priorities are used to determine which thread is going to run next. When the thread is run, the appropriate registers are reset to the values they had the last time the thread was stopped. This allows the thread to resume execution as though it had never been interrupted.

Like other things in GEOS, each thread has a handle, a sixteen-bit value which programs use to refer to the thread. When calling thread-related routines (e.g., to set the thread's priority), programs use the handle to specify the thread.

Event-Driven and Procedural Threads

GEOS uses two different types of threads. The two types differ only in the way they run when their turn comes and in the way they are created. The discussions about priority, context switches, and synchronization elsewhere in this chapter apply equally to both types.

The first type of GEOS thread is an "event-driven" thread. An event-driven thread normally executes code for one or more objects. Each event-driven thread has an event queue; when a message is sent to any object created by the thread, the message is placed in the thread's event queue. The thread processes each event in the order received by executing the appropriate message handler from the object's class definition.

Messages can also be sent to the thread itself rather than to an object created by the thread. When the thread is created, it is assigned a class--normally a subclass of ProcessClass (for non-application threads) or GenProcessClass (for application threads)--to determine the handlers to use when messages are sent directly to the thread. In this sense, the thread can be considered an instance of the given class.

The second type of thread is procedural. Rather than running handlers for messages in an object-oriented scheme, it simply executes procedural code.
The system does not provide an event queue for a procedural thread, and messages cannot be sent to such a thread.

Threads and Semaphores: 3.2 GEOS Multitasking: Context Switches

Context switches are triggered in two ways under GEOS. The first is a timer or other hardware interrupt. The second occurs when the thread reaches a point where it cannot continue right away, such as when the thread exits or when it attempts to access a locked resource.

The PC hardware generates a timer interrupt sixty times per second. The time between timer interrupts is called a tick . Each thread is allowed to run for a specified number of ticks before the timer interrupt routine will transfer control to a different thread. This number of ticks is the same for all threads; it is called a time slice .

When a thread begins its turn, GEOS sets a counter to the number of ticks in a time slice. At each timer interrupt, GEOS decrements the counter. If the counter has not reached zero, control is immediately returned to the running thread. When the counter reaches zero, GEOS checks to see if some other thread has reached a higher priority than the current thread. If so, the current thread is placed in the system's list of runnable threads (called the run queue ), and the highest priority thread begins running. Otherwise the current thread gets to run for another time slice.

Sometimes a thread will try to access a system resource (or shared data object) which is currently in use by another thread. When this happens, the thread must wait until the desired resource is available. The thread is placed on a queue, and a new thread is selected from the run queue. Every thread that is not currently running is either in the run queue (waiting to be executed) or in another queue waiting for a needed resource to become available. When the resource becomes available, the thread is moved to the run queue and is ready to be run again. This process is described in greater detail in Synchronizing Threads .

Threads and Semaphores: 3.3 GEOS Multitasking: Thread Scheduling

When there is a context switch, GEOS must determine which thread will be executed next. It does this by examining the run queue and selecting the most important thread to run. A thread's importance is reflected in its priority: the lower the priority number, the more important the thread.

Base Priority

Each thread gets a base priority, a value from zero to 255. Applications generally have a priority between 128 and 191. Threads that are critical to quick system response (such as user input threads that manage such things as pull-down menus and dialog boxes) are given lower numbers. Higher numbers can be given to less time-critical threads such as those used for print spooling and other background activities. To provide faster response to the user, GEOS temporarily reduces an application's base priority by 32 (giving it a higher priority) when the user is interacting with it. When the user switches to interact with another application, the first application's base priority returns to normal, and the new one gets a reduced base priority number.

Recent CPU Usage

GEOS keeps track of a thread's recent CPU usage with a number that varies from zero to 60. Starting at zero, the number is incremented at every timer interrupt while the thread is running. Once each second, the recent CPU usage is halved, so that as a thread's CPU usage recedes into the past, its recent CPU usage number will diminish. The resulting number reflects how much time the thread has had control of the CPU and how long ago this time was.

Current Priority

Once each second, the current priority of each thread is recomputed by adding the base priority to the recent CPU usage. The resulting number is the one used in selecting a thread from the run queue.

When it is time for a context switch, GEOS selects the thread with the lowest current priority number from the run queue. If there is a tie, the selection is arbitrary. However, because recent CPU usage counts against a thread, two threads of equal priority will not stay that way. One will run, and its recent CPU usage (and thus its current priority number) will be increased. The other thread will therefore get its chance to run.

Threads and Semaphores: 3.4 GEOS Multitasking: Applications and Threads

There are two standard architectures for GEOS applications: single-thread and dual-thread. While the single-thread option is somewhat easier to program, there are distinct advantages to the dual-thread method.

In the dual-thread architecture, one thread manages the application's user interface while the other manages the rest of the application's functionality. Since both threads are event-driven, each has an event queue. Messages that are sent to user interface objects (resulting from mouse clicks, keyboard input, etc.) can be handled without waiting for other tasks in the application to be completed. This allows the application to respond to user input (by putting up menus, moving windows, and so on) without first completing the current non-user-interface task (which may involve a lot of computation).

The dual-thread architecture, however, poses a problem of synchronization: One thread can get ahead of the other. Threads that count on each other must keep track of each other's progress in order to avoid this; when potential problems are identified, use semaphores to keep the threads in line (see Synchronizing Threads ).

Threads and Semaphores: 4 Using Multiple Threads

It is possible for an application to create additional threads for a variety of purposes. For example, a terminal emulation program might have a thread whose sole purpose is to monitor the serial line for incoming characters. This might avert the danger of a serial input buffer or stream overflowing while the application is performing an involved task, such as loading a text file from disk, while not requiring a great deal of fixed memory for the serial driver's input buffer.

How GEOS Threads Are Created

Managing Priority Values

Handling Errors in a Thread

When a Thread Is Finished

Threads and Semaphores: 4.1 Using Multiple Threads: How GEOS Threads Are Created

GEOS threads can be created in three different ways. The first thread (or pair of threads, in the dual-thread architecture) for each application is created automatically when the application is launched. By calling the appropriate routines, the application can create additional threads to handle messages sent to certain objects or to run procedural code.

The Application's Primary Thread

The application's primary thread is created automatically by GEOS when the application is launched. (See the Applications and Geodes chapterfor information on launching applications.) For example, if a user double-clicks on your application's icon in a GeoManager window, GeoManager calls the library routine UserLoadApplication() , specifying the geode file and certain other parameters. This calls the GeodeLoad() routine in the GEOS kernel.

If the program is written using the single-thread model, GEOS creates an event-driven thread to handle messages sent to any object in the program. If the program is written using the dual-thread model, GEOS creates one event-driven thread to handle messages sent to the program's user interface objects and another to handle messages sent to other objects in the program.

If the program requires more than two threads, the extra thread(s) must be allocated manually on startup and destroyed before the application exits completely.

Event-Driven Threads

ThreadAttachToQueue()

To create an event-driven thread (one that handles messages sent to certain objects), send a MSG_PROCESS_CREATE_EVENT_THREAD to your application's primary thread, passing as arguments the object class for the new thread (a sublass of ProcessClass ) and the stack size for the new thread (1 K bytes is usually a good value, or around 3 K bytes for threads that will handle keyboard navigation or manage a text object). This message is detailed in the Process Object chapter.

GEOS will create the new thread, give it an event queue, and send it a MSG_META_ATTACH . Initially, the thread will handle only messages sent to the thread itself. If the thread creates any new objects, however, it will handle messages sent to those objects as well. To control the behavior of the new thread, define a subclass of ProcessClass and a new handler for MSG_META_ATTACH . The new handler can create objects or perform whatever task is needed. Be sure to start your new handler with @callsuper() so that the predefined initializations are done as well.

If you have a thread that you want attached to a different event queue, you can use ThreadAttachToQueue() . This routine is not widely used except when applications are shutting down and objects need to continue handling messages while not returning anything. It's unlikely you will ever use this routine.

Threads That Run Procedural Code

ThreadCreate()

To create a thread to run procedural code, first load the initial function into fixed memory. Then call the system routine ThreadCreate() , passing the following arguments: The base priority for the new thread, an optional sixteen-bit argument to pass to the new thread, the entry point for the code, the amount of stack space GEOS should allocate for the new thread, and the owner of the new thread.

Threads and Semaphores: 4.2 Using Multiple Threads: Managing Priority Values

ThreadGetInfo(), ThreadModify(), ThreadGetInfoType

You can ascertain and modify the priority of any thread in the system, given the thread's handle. The handle is provided by the routines that create threads, and it can be provided by one thread to another in a message. The following system routines relate to the priority of a thread:

ThreadGetInfo() returns information about a thread. When calling ThreadGetInfo() , pass the handle of the thread in question and a value of the type ThreadGetInfoType (see below). If zero is passed as the thread handle, ThreadGetInfo() returns information on whatever thread executed the call.

ThreadGetInfoType is an enumerated type with three possible values:

TGIT_PRIORITY_AND_USAGE
This requests the base priority and recent CPU usage of a thread. (To determine the current priority, simply add the base priority to the recent CPU usage.)
TGIT_THREAD_HANDLE
This requests the handle of the thread. Use this (with a thread handle of zero) to get the caller's own thread handle.
TGIT_QUEUE_HANDLE
This requests the handle of the event queue for an event-driven thread. It returns a zero handle if the thread is not event-driven.

T hreadModify() changes the priority of a thread. The arguments to pass include the handle of the thread to modify (zero for the thread executing the call), a new base priority for the thread, and two flags: One that indicates whether to change the thread's base priority and one that indicates whether to reset the thread's recent CPU usage to zero. If the flag to change the thread's base priority is not set, the new base priority argument is ignored. In general, you should only lower a thread's priority (i.e., raise its base priority number). Applications that raise their own priority damage the performance of the system as a whole. Keep in mind that GEOS already favors the thread with which the user is interacting.

There are several pre-defined priority levels you can use to set a thread's priority. You may wish to use these when debugging to raise the priority of a potentially buggy thread for efficient debugging. These are listed below, each a different constant.

  • PRIORITY_TIME_CRITICAL
    Threads should not be set time-critical unless they must own the processor exclusively for a certain amount of time. Excluding other threads can have undesirable side effects.
  • PRIORITY_HIGH
  • PRIORITY_UI
  • PRIORITY_FOCUS
  • PRIORITY_STANDARD
  • PRIORITY_LOW
  • PRIORITY_LOWEST

Threads and Semaphores: 4.3 Using Multiple Threads: Handling Errors in a Thread

ThreadHandleException(), ThreadException

Some threads in GEOS will want to handle certain errors in special ways. The errors a particular thread can intercept and handle are listed in an enumerated type called ThreadException , the elements of which are shown below:

  • TE_DIVIDE_BY_ZERO
  • TE_OVERFLOW
  • TE_BOUND
  • TE_FPU_EXCEPTION
  • TE_SINGLE_STEP
  • TE_BREAKPOINT

A thread can handle a particular exception by setting up a special handler routine and calling ThreadHandleException() when one of these exceptions occurs. This is useful if a number of objects are run by the same thread and all should handle a particular exception in the same way; the routine can be thread-specific rather than object-specific. ThreadHandleException() must be passed the thread's handle, the exception type, and a pointer to the handler routine's entry point.

Threads and Semaphores: 4.4 Using Multiple Threads: When a Thread Is Finished

ThreadDestroy()

Whenever an application creates an additional thread with MSG_PROCESS_CREATE_EVENT_THREAD or ThreadCreate() , it must be sure that the thread exits when it is finished. Simply exiting the application may not eliminate any additional threads, and these threads can cause GEOS to hang when shutting down the system.

When a thread exits, it should first release any semaphores or thread locks it has locked and free any memory or other resources that are no longer needed. Resources in memory do not have to be freed in the same thread that allocated them, but you should be sure that they are freed before the application exits.

A procedural thread exits by calling ThreadDestroy() with three arguments: an error code, an optr, and a word of data. When the thread exits, it sends (as its last act) a MSG_PROCESS_NOTIFY_THREAD_EXIT to the application's primary thread and a MSG_META_ACK to the object descriptor passed. (ThreadDestroy()'s word of data argument will be passed as the lower word of MSG_META_ACK's optr argument.) Each message has the error code as an argument. In designing a multi-threaded application, you can create methods for MSG_PROCESS_NOTIFY_THREAD_EXIT (in your primary thread's class) or MSG_META_ACK (in any class) for communication among threads, and you may use the error code for any data you choose. The convention is that an error code of zero represents successful completion of a thread's task.

An event-driven thread should not call ThreadDestroy() directly because its event queue must be removed from the system cleanly. Instead, send a MSG_META_DETACH to the thread, passing the same arguments as for ThreadDestroy() . The handler for MSG_META_DETACH in MetaClass cleanly removes the event queue and terminates the thread, sending the same messages as described above. You may write a special handler for MSG_META_DETACH when you subclass ProcessClass , but be sure to end the handler with @callsuper() so the thread exits properly.

Threads and Semaphores: 5 Synchronizing Threads

Because GEOS is a preemptive multitasking environment, it needs a way to prevent two threads from accessing system resources (or other shared data) simultaneously. Otherwise, data could be corrupted. GEOS uses semaphores to prevent to threads from performing conflicting operations at the same time.

Semaphores: The Concept

Semaphores In GEOS

Threads and Semaphores: 5.1 Synchronizing Threads: Semaphores: The Concept

A semaphore is a data structure on which three basic operations are performed. These operations allow threads to avoid conflicting with other threads. Think of a semaphore as a flag which programs can set to indicate that some resource is locked. Anyone else who wants to use the resource must wait in line until whoever set the flag resets it. The three basic operations on a semaphore are initialize, set, and reset.

Initialize

The "initialize" operation creates a semaphore and gives it a name. In its initial state, the semaphore is "unlocked," meaning the first process that attempts to access it will succeed right away. A semaphore must be initialized before it can be used, although the initialization can be handled by the operating system so that it is transparent to the programmer.

Set (the "P" Operation)

The "P" operation is what a program performs in order to make sure it is allowed to proceed. For example, if the program is about to access shared data, it performs the "P" operation on the semaphore protecting that data to make sure no other program is accessing it.

If the semaphore is unlocked and a thread performs the "P" operation on it, the thread simply marks the semaphore locked and proceeds normally. If the semaphore is locked, a thread performing the "P" operation will block . This means the thread will stop running and will wait in the thread queue associated with the semaphore. When its turn to perform the protected operation arrives, the thread will proceed.

Reset (the "V" Operation)

When a thread has finished a protected operation, it performs the "V" operation to unlock the semaphore. If there are other threads in the queue for this semaphore, one of them is restarted and takes over, keeping the semaphore locked. Thus only one thread at a time runs the protected operation. If a thread performs the "V" operation on a semaphore and there are no other threads waiting in the semaphore's queue, the thread simply marks the semaphore unlocked and proceeds.

Programs must always reset a semaphore when they are done with it. If you fail to reset a semaphore, other threads may wait forever.

Because only one thread at a time is performing the protected operation and this thread is responsible for unlocking the semaphore, it is sometimes said to "have" the semaphore. The "P" and "V" operations are often referred to as "grabbing" and "releasing" a semaphore, respectively.

The Dreaded Deadlock Problem

When semaphores are not used carefully, they can cause programs to stop running entirely. Suppose Thread A tries to grab a semaphore which Thread B has locked. Thread A stops running until Thread B releases the semaphore. Thread B then tries to grab a second semaphore, which Thread A has previously locked. Thread B waits for Thread A to release the second semaphore, but Thread A is waiting for Thread B to release the first semaphore. Neither thread will ever wake up. This situation is called "deadlock."

To avoid deadlock, follow these guidelines:

  • When possible, avoid having a thread attempt to lock one semaphore while it already has another one locked.
  • When two or more semaphores may be locked by the same thread at the same time, they should always be used in the same order. Semaphores are often arranged in a hierarchy, with the coarsest (the one controlling access to the most resources) at the top and the finest at the bottom. Any thread grabbing multiple semaphores in this hierarchy must always grab from top to bottom; that is, no thread should grab a semaphore "above" one it already has locked.
  • In certain situations, a semaphore is used "between" two threads. One thread needs to wait until another performs a specific action. The first thread is said to "block" on the second. Of course, two threads must never block on each other. To ensure this situation never arises, only one of the threads should use the @call keyword when sending messages to the other; the other should always use @send and, when getting return information, have some sort of notification message sent in response.
  • When using the GEOS messaging system to send a message with @call to an object in another thread, the sending thread automatically blocks on the receiving thread. Since a number of user interface objects must be sent messages with @call , the application thread sometimes blocks on the user interface thread. To avoid deadlock, code that runs in the user interface thread must never send messages with @call to objects in the application thread. (This is a particular example of the above rule being implemented.)

Threads and Semaphores: 5.2 Synchronizing Threads: Semaphores In GEOS

GEOS contains several system resources that are protected by semaphores. Since application programs can access these resources only through library routines, the programmer does not need to be aware of these semaphores; the required operations are performed by the library routines. For system resources (e.g. files, memory, handles), GEOS defines semaphores and provides special routines to set and reset them. The chapter that describes each system resource explains how to use the special semaphores that protect the resource.

The routines described in this section illustrate GEOS semaphores and can be used to create semaphores to protect resources defined within a multithreaded application. There are routines for each of the operations (initialization, P, and V), and there are special routines which simplify the use of a semaphore by multiple objects within the same thread.

Note that it is possible to create a semaphore with a starting value greater than one. That is, you can create a semaphore that will allow more than one thread to grab it at once. Typically, if only one thread may grab the semaphore, the thread is called a "mutual exclusion," or "mutex," semaphore, because it is normally used to make sure two threads don't mutually grab a particular resource.

Semaphores that can be grabbed by more than one thread are generally called "scheduling semaphores" because they allow easy manipulation of scheduled resources. The classic example of this is the "producer-consumer problem" wherein one thread produces buffers and another consumes them. Initially, no buffers exist, so the semaphore starts at zero. The consumer goes into a loop wherein it simply P's the semaphore (blocking until a buffer exists), takes the first buffer in the queue, processes the buffer, destroys the buffer, and then returns to the top of the loop. The producer, meanwhile, can produce any number of buffers, queue them, and V the semaphore once for each consumable buffer. The consumer thread will continue to process until all the buffers are consumed, and then it will block and wait for more buffers.

Operations on a Semaphore

ThreadAllocSem(), ThreadPSem(), ThreadPTimedSem(), ThreadVSem(), ThreadFreeSem()

To create a semaphore, simply call the routine ThreadAllocSem() , passing an initial value for the semaphore. This should normally be one to indicate the semaphore is unlocked. If you want the semaphore to be locked initially, pass an initial value of zero. In either case, the returned value will be the handle of the newly created semaphore. Use this handle with the routines described below.

Once a semaphore is created, a thread can lock it (i.e., perform the "P" operation) by calling the routine ThreadPSem() , passing the semaphore's handle as an argument. If the semaphore is unlocked, the thread locks it and proceeds; otherwise the thread waits in the semaphore's thread queue.

Another routine that performs the "P" operation is ThreadPTimedSem() . When calling this routine, pass as arguments the semaphore's handle and an integer representing a number of ticks. This integer is a time limit: If another thread has the semaphore locked and does not unlock it within the specified number of ticks, the routine will return with a flag indicating the lock was unsuccessful. Programs that use ThreadPTimedSem() must check this flag and must not perform the protected operation if it is set. The most common use of this routine is with a time limit of zero, meaning that the semaphore should be locked only if it is available right away--if it is not available, the thread will continue with some other action.

To release the semaphore (by performing the "V" operation), the thread calls ThreadVSem() , again passing the semaphore's handle. If there are other threads waiting for the semaphore, the one with the lowest current priority number takes over.

When a semaphore is no longer needed, it can be destroyed by calling ThreadFreeSem() with the semaphore's handle as an argument.

Operations on a Thread Lock

ThreadAllocThreadLock(), ThreadGrabThreadLock(), ThreadReleaseThreadLock(), ThreadFreeThreadLock()

At times it is convenient to have a program lock a semaphore that it has already locked. For example, one routine might lock a semaphore protecting a piece of memory and then call itself recursively. A thread lock is a semaphore that can be locked any number of times, as long as each lock is performed by the same thread. If another thread tries to grab the thread lock, it will wait until the first thread has performed the "V" operation once for each time it has run the "P" operation. It is possible to write reentrant routines using thread locks but not using regular semaphores.

A thread lock is initialized with the ThreadAllocThreadLock() routine, which takes no arguments. A thread lock is always created unlocked. To perform the "P" and "V" operation on a thread lock, use ThreadGrabThreadLock() and ThreadReleaseThreadLock() , respectively, and pass the semaphore's handle as an argument. These routines are analogous to ThreadPSem() and ThreadVSem() for semaphores. When a thread lock is no longer needed, it should be freed with a call to ThreadFreeThreadLock().

This document is a single-page version of a a multi-page document, suitable for easy printing.


Config Library

Not all users are the same; not all computing environments are the same. Often some part of an application allows the user to tailor the UI to his wishes. The user may wish to specify some preferences at a system level rather than on an application level. To do this, the user will look to the Preferences Manager application. The config library provides mechanisms for writing Preferences modules.

Normally the purpose of a Preferences module's gadgetry is to set up some information in the user's .INI file. If you are encouraging other developers to take advantage of the configuration done by your module, be sure to document the .INI file categories and keys affected.

There are three main tasks involved in writing a Preferences module:

  • Providing a tree of UI gadgetry which will act as the module's dialog box.
  • Providing information Preferences will use to construct the module's button.
  • Providing any underlying functionality other than writing information to the .INI file.

The first two routines exported in the module's .gp file will be responsible for carrying out the first two of these tasks. The final task is difficult to define--it will depend on the nature of your module.

Preferences modules written in C will need one more thing. To indicate that the preferences module is compatible with the standard GEOS 2.0 Preferences Manager applications, the module will need a local.mk makefile for compilation, and that local.mk file should include a line that reads:

_PROTO = 2.0

For information about local.mk files, see the Using Tools chapter.

Before you try to write a Preference module, there are some things you should already know. Each module is a library, so be sure to read the Libraries chapter. to find out how to write a GEOS library. It will let you know, among other things, that you must place the "c-api" flag in your library's GP file if its entry points are in C. .

1 Providing the UI
1.1 Designing the UI Tree.
1.2 UI Fetch Routine
2 Module Information Routine
3 Important Messages
4 Object Class Reference
4.1 PrefClass
4.2 PrefValueClass
4.3 PrefItemGroupClass
4.4 PrefStringItemClass
4.5 PrefBooleanGroupClass
4.6 PrefDynamicListClass
4.7 TitledGlyphClass
4.8 PrefInteractionClass
4.9 PrefDialogClass
4.10 PrefTextClass
4.11 PrefControlClass
4.12 PrefTimeDateControlClass
4.13 PrefTriggerClass
4.14 PrefTocListClass

Config Library: 1 Providing the UI

There are two steps to providing the UI for your preferences module. The first is to declare the objects you will use. The second is to write and export a routine which returns the head of the tree of UI gadgetry.

Designing the UI Tree.

UI Fetch Routine

Config Library: 1.1 Providing the UI: Designing the UI Tree.

Preferences modules present a dialog box to the user. You will need to design the dialog for your module. As you do so, you may find objects of the following classes useful. Each of the following classes is based upon one of the Generic UI classes.

GenClass provides mechanisms by which objects will read and write values to the .INI file. Pref- classes provide further functionality, such as storing an "original" value to revert to if the user decides to Cancel their changes.

The following Pref- classes are available:

PrefDialogClass
This class specializes in acting as the root of a Pref module's UI tree. It has been set up to provide "OK" and "Cancel" triggers by default. It has a mechanism to restart the system on an Apply if the module specifies that such a reset will be necessary to implement the user's changes.
Often a module will use a subclass of PrefDialogClass as the head of its UI tree--often the subclass intercepts MSG_GEN_APPLY to ensure valid user choices and to take other appropriate actions.
PrefValueClass
PrefValue objects act like objects of GenValueClass . However, these objects have the ability to reset their values to that originally stored in the .INI file (in case the user wants to cancel). They can handle only word-length values.
PrefItemGroupClass
Objects of this class act like GenItemGroups. In addition, this class has the ability to store an "original" value.
PrefStringItemClass
A PrefItemGroup normally writes data about selected items in the form of an integer. However, if its children are PrefStringItem objects, the PrefItemGroup can instead write out a special string associated with each PrefStringItem, resulting in a more readable .INI file.
PrefBooleanGroupClass
This class acts like GenBooleanGroupClass with the ability to store an "original" value.
PrefDynamicListClass
Use objects of this class where you might otherwise use a GenDynamicList. This class will not work together with PrefStringItem objects.
PrefTocListClass
This class presents an alphabetized list of files or driver names. It is used to provide lists of devices, such as the list of available printers. It can also be used to show a list of files with a given token in a given directory.
TitledGlyphClass
This specialized subclass of GenGlyph shows both an icon and a text moniker. It is used to present the icon and name at the top of a typical Preferences module dialog box.
PrefInteractionClass
This class acts like GenInteractionClass , but also will relay certain messages to its children which GenInteraction would not. Those objects which will need to work with the .INI file should receive these messages, so any Gen- objects which will work with the .INI file and any Pref objects should be grouped under PrefInteractions instead of GenInteractions.
PrefTextClass
This class behaves like GenTextClass , except that it will load and save its value based upon that stored in the .INI file.
PrefTriggerClass
This class acts as does GenTriggerClass, but has an extra action, so that two separate messages will be sent when the trigger is activated.
PrefControlClass
This class acts as a cross between PrefClass and GenControlClass .
PrefTimeDateControlClass
This class allows the user to set the system date and time.
PrefClass
PrefClass is something like GenClass --while never used directly, it is the superclass of all the other Pref- classes, and sets up several of the mechanisms which all will use.

There aren't any special restrictions on what sorts of objects appear in the UI tree of a Preferences module. However, the following rules will prove useful in constructing modules that correctly write their data to the .INI file and have a look and feel consistent with existing modules. For an example, see Pref Module UI Framework .

  • As a rule of thumb, the top object of the tree should be a PrefDialog.
  • For a consistent look, the upper portion of the dialog should contain the module's icon, name, and some brief help text. This is normally done using a TitledGlyph and a GenText object.
  • Generally the easiest way to specify the .INI file category and key to use is by means of ATTR_GEN_INIT_FILE_CATEGORY and ATTR_GEN_INIT_FILE_KEY. Assuming that the module will work with just one category, it is sufficient to declare an ATTR_GEN_INIT_FILE field for just the top object of the module's UI tree.
  • Because of the way messages are relayed, you should use PrefInteractions instead of GenInteractions if any objects under the interaction will be saving data to the .INI file.

Code Display 22-1 Pref Module UI Framework

@object MPMDialogClass MPMRoot = {
    GI_states = @default & ~GS_USABLE;
    GI_comp = @MPMTitleGroup, @MPMOtherStuff;
    HINT_INTERACTION_SINGLE_USAGE;
    HINT_INTERACTION_COMPLEX_PROPERTIES;
    HINT_ORIENT_CHILDREN_VERTICALLY;
    HINT_LEFT_JUSTIFY_CHILDREN;
    ATTR_GEN_HELP_CONTEXT = "myPrefModule";
    ATTR_GEN_INIT_FILE_CATEGORY = "myPref";
}
@object GenInteractionClass MPMTitleGroup = {
    GI_comp = @MPMTitle, @MPMHelp;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
}
@object TitledGlyphClass MPMTitle = {
    GI_visMoniker = list {
        @FontTextMoniker, @FontLCMoniker, @FontLMMoniker, @FontLCGAMoniker
     }
}
@object GenTextClass MPMHelp = {
    GI_attrs = @default | GA_READ_ONLY;
    GTXI_text = "Do such and such to configure your so and so.";
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    /* Might want HINT_MINIMUM_SIZE */
}

Config Library: 1.2 Providing the UI: UI Fetch Routine

Once you have constructed the UI tree, write a routine that returns the head of that tree. This should be the first routine exported in your module's .gp file. The Preferences Manager knows to call the first exported routine when it needs the UI tree for a module.

The routine should have the parameters

optr _pascal (void)

For an example of such a routine, see UI Fetch Routine .

Code Display 22-2 UI Fetch Routine

optr _pascal MPMGetPrefUITree(void) { return (@MPMRoot);}

Config Library: 2 Module Information Routine

The Preferences application needs to know some information about each module to present the trigger which allows the user into said module. Preferences does this by calling the second routine exported by the module library. Preferences expects this routine to fill in a blank PrefModuleInfo structure.

By filling in this structure, the routine describes the button that Preferences will use to represent the module. Also, you may specify a User Level for the module, so it will be presented only to users with enough expertise to use it. You may also ask that only users with certain privileges be given access to the module; without the proper privileges, they won't see it.

The Routine should have the parameters

void _pascal (*PrefModuleInfo)

For an example of such a routine, see Module Information Routine .

Code Display 22-3 Module Information Routine

void _pascal MPMGetModuleInfo(PrefModuleInfo *info)
{	/* We'll set up this structure for both system and user information */
     moduleInfo->PMI_requiredFeatures = 0;
     moduleInfo->PMI_prohibitedFeatures = 0;
     moduleInfo->PMI_minLevel = UIIL_ADVANCED;
     moduleInfo->PMI_maxLevel = UIIL_MAX_LEVEL;
     moduleInfo->PMI_monikerList = @MPMMonikerList;
     moduleInfo->PMI_monikerToken = moduleToken;
}
const GeodeToken moduleToken = { "MyPf", MANUFACTURER_ID_MINE };

The following information about the PrefModuleInfo structure may prove useful when writing the routine:

PMI_requiredFeatures
This field allows you to restrict the display of your module so that it will only appear to users which have certain privileges.
PMF_HARDWARE
These settings are for a user who has permissions to actually change the configuration of the workstation. In a network environment where users log in to different machines at different times, normal users would be prevented from changing the mouse drivers, video drivers, etc.
PMF_SYSTEM
These changes are more complex and potentially more damaging than the basic "user" changes, therefore, some users may be prevented from using these settings.
PMF_NETWORK
These are network settings. Generally only the system administrator should see these settings, as they affect the entire network.
PMF_USER
These are basic user changes. These settings are the most basic and least dangerous, controlling user preferences such as background color and screen saver types.
PMI_prohibitedFeatures
This flag field allows you to restrict the display of your module so that it will only appear to users who don't have certain privileges. If you had two modules, advanced UI and simple UI, one might be for the sysop and the other for normal users--this would keep the sysop's Preferences from being "cluttered" by the plain user module. The flags available are the same as those for PMI_requiredFeatures .
PMI_minLevel
This field allows you to specify the minimum User UI Level in which your module should appear. If the module's UI is very complicated and will confuse novice users, use this field to hide it from them.
PMI_maxLevel ;
This field allows you to specify the maximum User UI Level in which your module should appear. If you have two versions of your module--one for advanced users and one for novice users--use this field to hide the novice module from the advanced users.
PMI_monikerList
Like the moniker list for an application, this will provide the icon shown on the main Preferences screen. Create this using the Icon editor. You should have icon formats 64x40 color, 64x40 monochrome, and 64x18 monochrome.
PMI_monikerToken
A four character token and manufacturer ID by which to recognize the module.

Config Library: 3 Important Messages

If you're going to create a subclass for use in a Preferences module, the following messages will be of special interest, as they signal that the user is carrying out certain important actions.

MSG_PREF_INIT
When a dialog box containing Pref objects is brought up, each object will receive MSG_PREF_INIT. Developers can subclass MSG_PREF_INIT to perform basic initialization procedures.
MSG_META_LOAD_OPTIONS
Generic objects (and by inheritance, Pref- objects) respond to MSG_META_LOAD_OPTIONS by loading a value from the appropriate place in the .INI file. This place is determined by the object's ATTR_GEN_INIT_FILE_KEY field. If the object has an ATTR_GEN_INIT_FILE_CATEGORY field, that category will be used; otherwise the default handler will look up the Generic tree for an object with a ATTR_GEN_INIT_FILE_CATEGORY field defined and will use the value stored there.
Unlike a GenInteraction, a PrefInteraction will relay this message to its children. When it's time to load options from the .INI file, a MSG_META_LOAD_OPTIONS will be sent to the top level of the module's UI tree. This message should be propagated down the tree to reach all objects which should read their values from the .INI file. This is why it is suggested that you use PrefInteractions in place of GenInteractions if any Pref- objects will be underneath. If you create a new sort of object which should relay this message to other objects, be sure to do so.
MSG_META_SAVE_OPTIONS
Generic objects (and by inheritance, Pref- objects) respond to MSG_META_SAVE_OPTIONS by saving a value to the appropriate place in the .INI file. This place is determined by the object's ATTR_GEN_INIT_FILE_KEY field. If the object has an ATTR_GEN_INIT_FILE_CATEGORY field, that category will be used; otherwise the default handler will look up the Generic tree for an object with a ATTR_GEN_INIT_FILE_CATEGORY field defined and will use the value stored there.
Unlike a GenInteraction, a PrefInteraction will relay this message to its children. When it's time to save options to the .INI file, a MSG_META_SAVE_OPTIONS will be sent to the top level of the module's UI tree. This message should be propagated down the tree to reach all objects which should write their values to the .INI file. This is why it is suggested that you use PrefInteractions in place of GenInteractions if any Pref- objects will be underneath. If you create a new sort of object which should relay this message to other objects, be sure to do so.
Often, interceptors of this message will carry out whatever actions are necessary to carry out the user's wishes in those cases where writing something to the .INI file is not enough to bring the change about.
MSG_GEN_RESET
If the user clicks on a "Revert" or "Cancel" button, all values in UI gadgets should revert to those values originally stored in the .INI file. You will note that many Pref- objects have an extra field to store their "original" value. If you create your own kind of Preferences object, keep in mind that it will need some way to store the "original" value (probably initialized while handling MSG_META_LOAD_OPTIONS ) and should intercept MSG_GEN_RESET to replace its value with the "original" value.
MSG_GEN_LOAD_OPTIONS
Generic objects (and thus Pref- objects) respond to MSG_META_LOAD_OPTIONS by sending themselves this message. This message is only meant to be used for managing the .INI file; any other functionality should occur in the handling of MSG_META_LOAD_OPTIONS.
MSG_GEN_SAVE_OPTIONS
Generic objects (and thus Pref- objects) respond to MSG_META_SAVE_OPTIONS by sending themselves this message. This message is only meant to be used for managing the .INI file; any other functionality should occur in the handling of MSG_META_SAVE_OPTIONS .

If you are interested in detecting certain user actions, the following list may prove useful:

User Opens Dialog
Any objects which need to do some initialization when the user first opens the module's dialog box should intercept MSG_PREF_INIT . After receiving MSG_PREF_INIT , each object will receive a MSG_META_LOAD_OPTIONS .
User clicks Apply
Assuming the top object of the module's UI tree is a PrefDialog and that the dialog has been set up as a Properties dialog (as it is by default), then this object will receive a MSG_GEN_APPLY when the user activates the Apply trigger. The Pref dialog responds to this message by sending itself a MSG_META_SAVE_OPTIONS , which it will then pass on to its children.
User clicks Cancel or Reset
All Gen- and Pref- objects within the UI tree will receive a MSG_GEN_RESET .

Config Library: 4 Object Class Reference

If you will use any of the Pref- classes in your module, the following reference material may prove useful.

PrefClass

PrefValueClass

PrefItemGroupClass

PrefStringItemClass

PrefBooleanGroupClass

PrefDynamicListClass

TitledGlyphClass

PrefInteractionClass

PrefDialogClass

PrefTextClass

PrefControlClass

PrefTimeDateControlClass

PrefTriggerClass

PrefTocListClass

Config Library: 4.1 Object Class Reference: PrefClass

PrefClass defines several fields of instance data which will be used by all of its subclasses.

There are several variable data fields which make sure that a given Preference gadget will not be usable by those without the proper privileges; other fields make sure that the gadgets will not appear to those users whose UI levels indicate that they would be confused by the presence of such an object. Keep in mind that if all gadgets in your module demand some privilege or User level, you should reflect this in the routine which provide Preferences module information about your module.

Code Display 22-4 PrefClass Instance Data

@class PrefClass, GenClass, master, variant;
@instance PrefAttributes  PI_attrs = (PA_LOAD_IF_USABLE|PA_SAVE_IF_USABLE);
typedef ByteFlags  PrefAttributes;
#define  PA_REBOOT_IF_CHANGED 0x80
/* This bit signals that changes in the state of
 * this object requires a system reboot to take effect. */
#define PA_LOAD_IF_USABLE 0x40
 /* Load options only if this object is usable (this is ON by default). */
#define PA_SAVE_IF_USABLE 0x20
 /* Save options only if this object is usable (this is ON by default) */
#define PA_SAVE_IF_ENABLED 0x10
 /* Save options only if this object is enabled */
#define PA_SAVE_IF_CHANGED 0x08
 /* Save options only if this object has changed. */
@vardata optr  ATTR_PREF_REBOOT_STRING;
/* This is the string passed to MSG_PREF_MGR_DO_REBOOT_DIALOG -- the
 * string must be in a SHARABLE LMEM resource and is incorporated into the
 * middle of another sentence. In English, the sentence is "The system software
 * will shut down and restart to change the <reboot_string>. Do you wish
 * to proceed?"
 *
 * If an object has its PA_REBOOT_IF_CHANGED attribute set, it will scan for
 * this attribute, first in itself, then up the generic tree, until it
 * finds one. */
@reloc ATTR_PREF_REBOOT_STRING, 0, optr;
@vardata PrefMgrFeatures ATTR_PREF_REQUIRED_FEATURES;
/* features bits that must be set for this object to be usable. */
@vardata PrefMgrFeatures ATTR_PREF_PROHIBITED_FEATURES;
/* features bits that must not be set for this object to be usable. */
@vardata UIInterfaceLevel ATTR_PREF_MIN_LEVEL;
/* interface level below which this object will not be usable */
@vardata UIInterfaceLevel ATTR_PREF_MAX_LEVEL;
/* interface level above which this object will not be usable */

In addition to the instance data fields, there are messages which allow for the dynamic retrieval and updating of these fields.

MSG_PREF_SET_INIT_FILE_CATEGORY
void MSG_PREF_SET_INIT_FILE_CATEGORY(
        const char *category);

Pref objects automatically respond to this message by changing the value of the ATTR_GEN_INIT_FILE_CATEGORY variable data field. This is a utility message; similar functionality is available via MSG_META_ADD_VAR_DATA.

Source: Anywhere

Destination: Any PrefClass object.

Parameters: category Null-terminated category string.

Return: Nothing.

MSG_PREF_HAS_STATE_CHANGED
Boolean MSG_PREF_HAS_STATE_CHANGED();

This message determines whether the object or any of its children have changed since the last time options were loaded. This is used in conjunction with the PA_REBOOT_IF_CHANGED flag that can be set for an object--this flag signals that, if the object changes state, then GEOS should be restarted for the change to go into effect.

Source: PrefClass object sends this message to itself when handling MSG_PREF_GET_REBOOT_INFO .

Destination: Self.

Parameters: None.

Return: Should return true (i.e. non-zero) if state has changed, false (i.e. zero) otherwise.

Interception: New PrefClass subclasses will use this to compare the object's current state against the "original" state, assuming one was stored.

MSG_PREF_GET_REBOOT_INFO
optr MSG_PREF_GET_REBOOT_INFO();

This message asks whether the system will need to reboot as a result of changes. If a reboot is necessary, the dialog will then notify the user of the reboot, and give the user a chance to abort (since the system reset may be a lengthy process on some systems).

Source: PrefDialog sends this message to all children on an apply.

Destination: Any Pref- object.

Interception: Subclasses should check the PA_REBOOT_IF_CHANGED flag--if the flag is set, the object should compare its present value to its "original" value. If a reboot is never needed, then do nothing.
If a reboot is necessary, the method should return the string to use when rebooting. The standard way to do this is via MSG_PREF_GET_REBOOT_STRING .
If possible, developers should try to avoid situations where a system reset is necessary, and use mechanisms such as the General Change Notification instead.

Parameters: None.

Return: Zero if no reboot needed; otherwise the OD of string to insert in the confirm-shutdown dialog box. The string will be inserted into a sentence; the string should describe what is changing. You may wish to use the value of ATTR_PREF_REBOOT_STRING.

See Also: MSG_PREF_GET_REBOOT_STRING , ATTR_PREF_REBOOT_STRING.

MSG_PREF_GET_REBOOT_STRING
optr MSG_PREF_GET_REBOOT_STRING();

This message should return the optr of a string in a sharable local memory resource. This string will display in the dialog box which asks the user if it is all right to shut down. If a given object doesn't know of a good string to supply, it should forward the message on to its parent in the generic tree. The default PrefClass handler returns the value stored in ATTR_PREF_REBOOT_STRING.

Source: Pref- object sends this message to itself.

Destination: Any PrefClass object.

Parameters: Nothing.

Return: Object Pointer of string (or NULL if not found).

MSG_PREF_INIT
void MSG_PREF_INIT(
        PrefMgrFeatures 		features,
        UIInterfaceLevel 		level);

This message initializes the object. The object should perform any required set-up to appear on-screen. The PrefDialog object will send this object to all of its children when the dialog is first initiated.

Source: Generic Parent

Destination: Any Pref- object.

Parameters: features This structure tells you a bit what sorts of changes the user prepared to make.

level
The user's User Level.

Return: None.

Interception: Default behavior is to determine whether to make the object usable based on UI level and features (or will be, when implemented). Subclass should call superclass before handling, as subclass behavior may be different if the object is not usable.

MSG_PREF_NOTIFY_DIALOG_CHANGE
void MSG_PREF_NOTIFY_DIALOG_CHANGE(
        PrefDialogChangeType 			type);

This message goes out via PDGCNLT_DIALOG_CHANGE list bound to PrefDialog object telling interested parties of a change in the box's state. It is sent when the dialog box is being opened, closed, or destroyed.

Source: PrefDialog object.

Destination: Any Pref- object registered on PDCGCNLT_DIALOG_CHANGE.

Interception: This message has no default handler; it must be intercepted to do anything useful.

Structures:

typedef enum {
     PDCT_OPEN,
    PDCT_CLOSE,
    PDCT_DESTROY,
    PDCT_RESTART,
    PDCT_SHUTDOWN
}  PrefDialogChangeType;
MSG_PREF_SET_ORIGINAL_STATE
void MSG_PREF_SET_ORIGINAL_STATE();

This message copies the object's current state to the "original" value field, which will be used on a MSG_GEN_RESET .

Parameters: None.

Return: Nothing.

Interception: Subclasses should respond to this message by copying their current value to whatever instance field represents the "original" state of the object.

Config Library: 4.2 Object Class Reference: PrefValueClass

PrefValue objects behave like GenValue objects except that they have the ability to store an "original" value in the PVI_originalValue instance data field. Unlike GenValue objects, PrefValues can only handle single word values.

Code Display 22-5 PrefValueClass Instance Data

@class PrefValueClass, PrefClass;
@instance word  PVI_originalValue = 0;
@vardata void  ATTR_PREF_VALUE_ORIG_IS_INDETERMINATE;
@vardata word ATTR_PREF_VALUE_ROUND;
/* Specifies an integer value to which the value will be rounded on .INI file
 * reads/writes. For instance, with a value for 10 here, the value would always
 * round off to the nearest 10. Note that the user can still type in an illegal
 * value, but this value will be rounded before saving to the .INI file.*/
MSG_PREF_VALUE_SET_ORIGINAL_VALUE
void MSG_PREF_VALUE_SET_ORIGINAL_VALUE(
        word 		orig,
        Boolean 		indeterminate);

This message sets the "original" value of the object--it also sets the value stored by the superclass.

Config Library: 4.3 Object Class Reference: PrefItemGroupClass

This class acts like GenItemGroupClass , but has extended functionality for reading/writing strings to the .INI file. When the user triggers the dialog's Apply trigger (when the PrefItemGroup receives a MSG_META_SAVE_OPTIONS ), the PrefItemGroup will respond by writing out strings based upon which of its children items are presently selected. These strings can be taken from the items' monikers or from strings used with PrefStringItemClass objects.

When loading options, if either the PIFF_USE_ITEM_MONIKERS or PIFF_USE_ITEM_STRINGS is set for the item group, then it will select its children based on the strings stored in the .INI file.Otherwise, the item group will default to the GenItemGroupClass functionality, which uses the identifier of the items as a basis for selection.

Code Display 22-6 PrefItemGroupClass Instance Data

@instance PrefInitFileFlags  PIGI_initFileFlags = 0;
typedef ByteFlags  PrefInitFileFlags;
#define  PIFF_USE_ITEM_STRINGS 0x80
/*
 * If set, then the item group's children must be of class PrefStringItemClass,
 * and their strings will be used to interact with the .INI file. */
#define PIFF_USE_ITEM_MONIKERS 0x40
/* If set, the monikers of the items are used to interact with the .INI file. */
#define PIFF_APPEND_TO_KEY 0x20
/* If set, the strings in this list will be ADDED
 * to strings that may already exist for this key */
#define PIFF_ABSENT_KEY_OVERRIDES_DEFAULTS 0x10
/* If set, an absent key for the item group will cause it to
 * behave as if an empty key were in the .INI file, effectively
 * overriding any default values stored with the group when it was
 * compiled. Used primarily by those groups where one subclasses
 * MSG_PREF_STRING_ITEM_CHECK_IF_IN_INIT_FILE_KEY to determine the
 * initial setting, rather than looking in the .INI file. */
@instance word PIGI_originalSelection = 0;
@instance word PIGI_originalNumSelections = 0;
@instance byte PIGI_suspendCount = 0;
/* When suspend count is nonzero, the item group won't update text objects, nor
 * will it enable/disable objects when the selection changes. */
@vardata PrefEnableData ATTR_PREF_ITEM_GROUP_ENABLE;
/* allows setting up an object to be enabled/disabled when
 * settings are made in the item group */
typedef struct {
    word 		 PED_item; 		/* Identifier of the item that controls
                     * enabling/disabling of object. If this
                     * is GIGS_NONE, then the action will be
                     * performed if no items are selected. */
    ChunkHandle 		PED_lptr; 		/* Object to be enabled/disabled */
     PrefEnableFlags 		PED_flags;
}  PrefEnableData;
typedef ByteFlags  PrefEnableFlags;
#define  PEF_DISABLE_IF_SELECTED 0x80
/* Disable the object if the associated item is selected,
 * otherwise do the opposite. */
#define PEF_DISABLE_IF_NONE 0x40
/* If this flag is set, then the PED_item field is ignored.
 * Instead, the item group will disable the specified object if
 * no items are selected -- or if there are no items in the list. */
@vardata word  ATTR_PREF_ITEM_GROUP_OVERRIDE;
/* This attribute is used to specify an item which will act
 * as an OVERRIDE for all other items (in a non-exclusive
 * item group).
 *
 * When the specified item is SELECTED, then all other items are DE-SELECTED.
 * When any other item is SELECTED, the override item is DE-SELECTED.
 * If all items are DE-SELECTED, the override item becomes SELECTED */
@vardata ChunkHandle ATTR_PREF_ITEM_GROUP_TEXT_DISPLAY;
/* chunk handle of a GenText object that will be updated
 * with the moniker of the currently selected item on APPLY/RESET. */
@vardata ChunkHandle ATTR_PREF_ITEM_GROUP_STATUS_TEXT;
/* chunk handle of a GenText object that will be updated every
 * time this object receives a MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG. */
@vardata char ATTR_PREF_ITEM_GROUP_EXTRA_STRING_SECTION[];
/* If a string is given -- the string will always be written
 * out as the FIRST string section for the .INI file key. For
 * example, the "Memory" item group uses this to always insert
 * "disk.geo" whenever writing out the memory types. Note:
 * this item is only written if the item group has either the
 * PIFF_USE_ITEM_MONIKERS or PIFF_USE_ITEM_STRINGS set. */
MSG_PREF_ITEM_GROUP_SET_ORIGINAL_SELECTION
void 	MSG_PREF_ITEM_GROUP_SET_ORIGINAL_SELECTION(
word selection);

Set the "originalSelection" field of the item group. Also, sends MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION to superclass. This message can only be used with exclusive item groups--for other types, use the GenItemGroup messages, and then send the object MSG_PREF_SET_ORIGINAL_STATE .

Source: Anywhere.

Destination: An exclusive PrefItemGroup.

Parameters: selection Which item to select.

Return: Nothing.

Interception: Unlikely.

MSG_PREF_ITEM_GROUP_GET_SELECTED_ITEM_TEXT
word 	MSG_PREF_ITEM_GROUP_GET_SELECTED_ITEM_TEXT(
        char 	*buffer,
        word 	bufSize);

This message returns the text in the moniker of the selected item. The value returned is the number of characters in the string. If nothing is selected, the value returned is zero.

MSG_PREF_ITEM_GROUP_GET_ITEM_MONIKER
word MSG_PREF_ITEM_GROUP_GET_ITEM_MONIKER(@stack
        char 	*buffer,
        word 	bufSize,
        word 	identifier);

This message retrieves the text in the moniker of one of the items of a PrefItemGroup. This works for dynamic list subclasses as well. The message returns the number of characters in the returned text.

Parameters: buffer Buffer in which to return moniker text.

bufSize
Size of buffer.
identifier
Identifier of item whose moniker should be returned.

Return: Size of item's moniker.

MSG_PREF_ITEM_GROUP_UPDATE_TEXT
void MSG_PREF_ITEM_GROUP_UPDATE_TEXT(
        ChunkHandle 		textObject);

This message updates the specified text object with text of the currently selected item. The PrefItemGroup sends this to itself in conjunction with either ATTR_PREF_ITEM_GROUP_TEXT_DISPLAY or ATTR_PREF_ITEM_GROUP_STATUS_TEXT. By default, this message will also cause the text object to send out its status message.

Source: Self.

Destination: Self.

Interception: Default behavior is to fetch the moniker of the current selection, and sent that to the text. The subclasser can use different text, if desired.

Config Library: 4.4 Object Class Reference: PrefStringItemClass

Objects of this class, used together with a PrefItemGroup with its PIFF_USE_ITEM_STRINGS flag set, allow for objects which act like GenItems but can write an arbitrary string out to the .INI file if the user selects them.

Code Display 22-7 PrefStringItemClass Instance Data

@class PrefStringItemClass, GenItemClass;
@instance ChunkHandle  PSII_initFileString = 0;
MSG_PREF_STRING_ITEM_SET_INIT_FILE_STRING
void MSG_PREF_STRING_ITEM_SET_INIT_FILE_STRING(
        const char 		*str);

This message sets the string that is read and written in the .INI file.

Interception: Unlikely but possible. Default behavior sets the string and then checks again with the .INI file to make sure item should be selected.

MSG_PREF_STRING_ITEM_CHECK_IF_IN_INIT_FILE_KEY
Boolean MSG_PREF_STRING_ITEM_CHECK_IF_IN_INIT_FILE_KEY(
        PrefItemGroupStringVars 				*vars);

This message checks the passed string to see if the string bound to this item is in one of its pieces. A PrefItemGroup will send this object when loading options if it has the PIFF_USE_ITEM_STRINGS flag set.

Source: PrefItemGroup object.

Destination: PrefStringItem object.

Interception: May be intercepted if the subclass needs to check other things to decide whether it should be selected. If you also wish to have the default behavior, you may call the superclass either before or after you've made your own decision, as appropriate.

Parameters: vars Information about the string.

Return: Returns true (i.e. non-zero) if bound string is within the string stored in the local variable.

Config Library: 4.5 Object Class Reference: PrefBooleanGroupClass

This class behaves as GenBooleanGroup, except that it saves its original state and handles MSG_GEN_RESET appropriately. It can also use item strings or monikers when loading and saving options.

Code Display 22-8 PrefBooleanGroupClass Instance Data

@class PrefBooleanGroupClass, PrefClass;
@instance PrefInitFileFlags 				 PBGI_initFileFlags = 0;
@instance word 				PBGI_originalState = 0;
MSG_PREF_BOOLEAN_GROUP_SET_ORIGINAL_STATE
void MSG_PREF_BOOLEAN_GROUP_SET_ORIGINAL_STATE(
        word state);

This message sets the "original" state of the Boolean group.

Config Library: 4.6 Object Class Reference: PrefDynamicListClass

Objects of PrefDynamicListClass act like GenDynamicList objects, but with the intelligence to write their data to the .INI file. Developers will generally not use this class directory, but instead will subclass, intercepting the following messages:

MSG_PREF_DYNAMIC_LIST_BUILD_ARRAY
To build the data structures which will be used when later supplying monikers
MSG_PREF_ITEM_GROUP_GET_ITEM_MONIKER
To supply the moniker for a requested item on demand
MSG_PREF_DYNAMIC_LIST_FIND_ITEM
To find a given item when passed its moniker.

By handling these three messages, the developer will have a dynamic list that can properly load and save its selection and will scroll to the correct item in response to keyboard input.

The load and save mechanism is currently only configured for exclusive dynamic lists.

MSG_PREF_DYNAMIC_LIST_BUILD_ARRAY
void MSG_PREF_DYNAMIC_LIST_BUILD_ARRAY();

The PrefDynamicList sends itself this message in response to MSG_PREF_INIT.

Source: Self.

Destination: Self.

Interception: The subclasser should create an array of item strings, ideally sorted.

MSG_PREF_DYNAMIC_LIST_FIND_ITEM
 Boolean MSG_PREF_DYNAMIC_LIST_FIND_ITEM(
        word 		*itemPtr,
        const char 		*str,
        Boolean 		ignoreCase);

This message finds an item given a moniker. It is normally used in the context of loading options.

Source: Self.

Destination: Self.

Parameters: itemPtr Pointer to space in which to return a word.

str
String to search for.
ignoreCase
Flag requesting a case-insensitive search.

Return: Returns true (i.e. non-zero) if an item was found with a moniker matching the passed string; itmPtr will have been filled with the number of the matching item. If no such item was found, the message will return false (i.e. zero) and itmPtr will point to the first item after the requested item.

Config Library: 4.7 Object Class Reference: TitledGlyphClass

Each TitledGlyph displays two monikers, one icon and one text. TitledGlyphClass has no unique instance data or messages. You are expected to provide a list of monikers in the GI_visMoniker field, and this list should include several icons and a text moniker. Normally the same icon is used as by the routine providing Preferences module information.

Config Library: 4.8 Object Class Reference: PrefInteractionClass

If you have any Preferences objects which write out their values to the .INI file, and if you would normally have those objects appear under a GenInteraction, then you should have them appear under a PrefInteraction instead.

Objects of the class will forward the following messages on to their children:

MSG_PREF_INIT , MSG_PREF_SET_ORIGINAL_STATE
Object will send these messages to every child that is a subclass of PrefClass .
MSG_META_LOAD_OPTIONS
Object will send this to each child that satisfies the appropriate criteria (based on the child's PA_LOAD_IF_USABLE flag).
MSG_META_SAVE_OPTIONS
Object will send this to each child that satisfies the appropriate criteria (based on the child's PA_SAVE_IF_CHANGED, PA_SAVE_IF_ENABLED, and PA_SAVE_IF_USABLE flags).
MSG_PREF_HAS_STATE_CHANGED , MSG_PREF_GET_REBOOT_INFO
Propagates these messages to children, returning the value from the first child that returns a non-null response.

Code Display 22-9 PrefInteractionClass Instance Data

@class PrefInteractionClass, PrefClass;
@instance PrefInteractionAttrs  PII_attrs = 0;
typedef ByteFlags  PrefInteractionAttrs;
#define  PIA_LOAD_OPTIONS_ON_INITIATE 0x80
/* If set, then the dialog will send MSG_PREF_INIT,
 * followed by MSG_META_LOAD_OPTIONS to itself when it
 * receives a MSG_GEN_INTERACTION_INITIATE */
#define PIA_SAVE_OPTIONS_ON_APPLY 0x40
/* This flag is normally OFF to allow non-dialog prefInteractions to reside inside
 * other interactions without duplicate SAVE_OPTIONS messages being sent. */

Config Library: 4.9 Object Class Reference: PrefDialogClass

The top level object of any Preferences module UI tree should probably be a PrefDialog object. If the user's changes will necessitate a system reset, then this object will be responsible for rebooting the system.

On receiving MSG_GEN_APPLY , the dialog sends MSG_PREF_GET_REBOOT_INFO to itself (which is propagated to all children via PrefInteractionClass ). If any children require reboot, then the dialog sends MSG_PREF_DIALOG_CONFIRM_REBOOT to itself, which normally puts up a confirmation dialog box (this can be subclassed to provide other behavior). If user confirms reboot, then the dialog will send MSG_PREF_DIALOG_REBOOT to itself, and MSG_GEN_APPLY to its superclass to save the options (it being assumed that the PIA_SAVE_OPTIONS_ON_APPLY flag is set).

Code Display 22-10 PrefDialogClass Instance Data

@class PrefDialogClass, PrefInteractionClass;
@default GII_visibility = GIV_DIALOG;
@default GII_type = GIT_PROPERTIES;
@default GII_attrs = @default | GIA_NOT_USER_INITIATABLE | GIA_MODAL;
@default PII_attrs = @default | PIA_SAVE_OPTIONS_ON_APPLY;
MSG_PREF_DIALOG_CONFIRM_REBOOT
@message Boolean MSG_PREF_DIALOG_CONFIRM_REBOOT(
        optr 		string);

By default, the PrefDialog will respond to this message by putting up a dialog box asking the user if he wants to restart the system.

Parameters:

string
Object pointer of string which will be inserted in a sentence of the form "The system software will shut down and restart to change the string."

Return: Returns true (i.e. non-zero) to confirm the reboot; returns false (i.e. zero to deny the reboot.

MSG_PREF_DIALOG_REBOOT
void MSG_PREF_DIALOG_REBOOT();

This message causes GEOS to restart. This message does not return.

Config Library: 4.10 Object Class Reference: PrefTextClass

Objects of this class act like GenText objects, but have mechanisms to write their text string to the .INI file.

Code Display 22-11 PrefTextClass Instance Data

@class PrefTextClass, PrefClass;
@instance ChunkHandle  PTI_originalText = 0;
@vardata ChunkHandle  ATTR_PREF_TEXT_INIT_FILE_CATEGORY_TARGET;
/* PrefTextClass has the wonderful ability that it can set another
 * object's .INI. file category from its text. Category gets updated
 * whenever the text object receives MSG_GEN_TEXT_SEND_STATUS_MSG.
 * To make this happen, put the ChunkHandle of the object in this field. */

Config Library: 4.11 Object Class Reference: PrefControlClass

This subclass of PrefInteractionClass acts like GenControl, but bypasses some GenControl functionality. This class is assumed not to be on notification lists. Objects of this class will pass MSG_GEN_RESET to their children as a normal GenInteraction would. This class has no special instance data or messages.

Config Library: 4.12 Object Class Reference: PrefTimeDateControlClass

This controller allows the user to set the system time. This class' messages and instance data fields are internal. To get and set the date and time directly, use TimerGetDateAndTime() and TimerSetDateAndTime() .

Config Library: 4.13 Object Class Reference: PrefTriggerClass

PrefTriggerClass is not a subclass of PrefClass ; it is a subclass of GenTriggerClass . It acts like a normal trigger except that when activated it will send a second message if it has been set up with a ATTR_PREF_TRIGGER_ACTION field.

Code Display 22-12 PrefTriggerClass Instance Data

@class PrefTriggerClass, GenTriggerClass;
@vardata PrefTriggerAction  ATTR_PREF_TRIGGER_ACTION;
typedef struct {
     word  PTA_message;
     optr PTA_dest;
}  PrefTriggerAction;

Config Library: 4.14 Object Class Reference: PrefTocListClass

PrefTocListClass allows users to choose a device driver by selecting the appropriate device name. It is assumed that each driver can control one or more types of devices. When you set up each driver, you will set up strings to describe which devices that driver can control.

This class is a fully functional subclass of PrefDynamicListClass (i.e. it handles those messages one must intercept to create a viable Preferences dynamic list class). It can be used to scan a list of files (not necessarily device drivers), as long as all of those files are in the same directory and have the same characters in their token.

Code Display 22-13 PrefTocListClass Instance Data

@class PrefTocListClass, PrefDynamicListClass;
@instance TocUpdateCategoryFlags  PTLI_flags = 0;
typedef WordFlags  TocUpdateCategoryFlags;
#define  TUCF_EXTENDED_DEVICE_DRIVERS 0x8000
/* Files being enumerated are assumed to be extended device drivers. */
#define TUCF_CUSTOM_FILES 0x4000
/* The TUCP_fileArrayElementSize field will be used when creating the files array.
 * Otherwise, each element of the files array will be of size TocFileStruct.
 * NOTE: If this flag is used, the data structure used for each file
 * element MUST contain TocFileStruct as its first element. */
#define TUCF_ADD_CALLBACK 0x2000
/* TUCP_addCallback contains a fptr to a callback
 * routine that will be called when a file is added to the files array. */
#define TUCF_DIRECTORY_NOT_FOUND 0x1000
/* Don't actually scan the directory, because it doesn't exist.
 * Just create the category, and leave it empty. */
@instance TocCategoryStruct PTLI_tocInfo = {"", 0, 0};
typedef struct {
    TokenChars 		 TCS_tokenChars;
    DBGroupAndItem 		TCS_files; 		/* file name array */
    DBGroupAndItem 		TCS_devices; 		/* device name array--only if
                     * TCF_EXTENDED_DEVICE_DRIVERS is set. */
}  TocCategoryStruct;
@vardata PrefTocExtraEntry  ATTR_PREF_TOC_LIST_EXTRA_ENTRY_1;
@vardata PrefTocExtraEntry ATTR_PREF_TOC_LIST_EXTRA_ENTRY_2;
typedef struct {
    ChunkHandle 		 PTEE_item;
    /* lptr of item name. For device lists, this is
     * the device. For others, this is the file name */
    ChunkHandle 		PTEE_driver;
    /* Driver name (for device lists ONLY) */
    word 		PTEE_info;
    /* Extra word of information */
}  PrefTocExtraEntry;
@vardata char ATTR_PREF_TOC_LIST_INFO_KEY[];
/* This key is used to write the "info" word for device lists */

Use ATTR_GEN_PATH_DATA to tell the PrefTocList object in which directory to look for the device drivers. The PrefTocList will store the list of device drivers and device names in DB items referenced in the PTLI_tocInfo field's TCS_files and TCS_devices slots--it will do this automatically, so if you wish the default behavior, leave these as zero when declaring the object. If you will write your own handlers for keeping track of driver information, you may wish to work with these fields as there are some routines which have been set up to work with them.

Code Display 22-14 PrefTocList Framework

@object PrefTocListClass MyPrefToc = {
    PTLI_flags		= @default | TUCF_EXTENDED_DEVICE_DRIVERS;
    PTLI_tocInfo		= {"TOKN", 0, 0};
    HINT_ITEM_GROUP_SCROLLABLE;
    ATTR_GEN_INIT_FILE_KEY = "keyName";
    ATTR_GEN_PATH_DATA = { file path containing drivers };
}

The following routines are rather esoteric. You should only use them if you wish to subclass PrefTocListClass ' behavior and wish to continue using its existing data structures.

TocSortedNameArrayAdd()
Use this routine to add a new name to a sorted array. The new name will be inserted in its correct place in the alphabetically sorted array.
TocSortedNameArrayFind()
This routine determines whether a given name is in the TOC List's sorted name array, and will return the position of the name's element if found.
TocFindCategory()
Use this routine to find a category in the Toc file. The TCS_tokenChars field of the passed TocCategoryStruct should already be filled in; this routine will fill in the other fields.
TocNameArrayFind()
Use this routine to get back the token associated with a name in the name array. If the name is not found, the routine will return CA_NULL_ELEMENT.
TocNameArrayGetElement()
This routine takes a name array and a token and returns information about the name array element with the given token.
TocUpdateCategory()
This complicated routine will update a category using a callback routine which you must write. It updates the file lists by scanning the current directory.
TocNameArrayAdd()
Use this routine to add an element to a Toc name array.
TocDBLock()
Use this routine to lock one of the name arrays.
TocDBLockGetRef()
This C-only routine does the same thing as TocDBLock() and also returns the DB item's pointer and optr.
TocGetFileHandle()
Use this routine to get the handle of the file in which all Toc device driver information is stored.
MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_INFO
Boolean MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_INFO(
        word 		*infoPtr);

This message returns the information word about the currently selected item. This message can only be used with PrefTocList objects that have the TCF_EXTENDED_DEVICE_DRIVERS flag set.

Parameters: infoPtr Pointer to a word in which to return the info word.

Return: Returns false (i.e. zero) if item was found, returns true (i.e. non-zero) if item was not found.

MSG_PREF_TOC_LIST_GET_SELECTED_DRIVER_NAME
word MSG_PREF_TOC_LIST_GET_SELECTED_DRIVER_NAME(
        char 		*buf,
        word 		bufSize);

This message returns the name of the driver for the selected device. This message may only be used if TCF_EXTENDED_DEVICE_DRIVERS is set.

Parameters: buf Pointer to buffer to fill with the driver name.

bufSize
The size of buf.

Return: If the device name fits in buf, then buf is filled and the message returns the size of the device name. If the device name doesn't fit, then the message will return zero.

MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_PATH
void MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_PATH(
        TocItemPath 		*data);

This message returns the absolute path of the selected driver or file.

Structures:

typedef struct {
  word 			 TIP_disk; /* disk handle */
  MemHandle 			TIP_pathBlock;
    /* Handle of block holding path (locked) */
  char 			*TIP_path;
    /* Locked null-terminated absolute path */
}  TocItemPath;
MSG_PREF_TOC_LIST_CHECK_DEVICE_AVAILABLE
Boolean MSG_PREF_TOC_LIST_CHECK_DEVICE_AVAILABLE(
        word 		*retvalPtr);

This message determines if the device selected by the list is available in the machine. This message may only be called for PrefTocList objects that have the TCF_EXTENDED_DEVICE_DRIVERS flag set.

Parameters: retValPtr Pointer to a word which will be filled with the return value.

Return: Returns true (i.e. non-zero if the device is available; if the driver is a video driver, *retValPtr is set to the device's DisplayType . If the device is not available, returns false (i.e. zero); retValPtr is zero of the device doesn't exist; otherwise it will be the value of the appropriate GeodeLoadError plus one.

This document is a single-page version of a a multi-page document, suitable for easy printing.


The Math Library

The Math Library allows high precision computations not available through standard integer operations. Floating Point (FP) numbers use decimal representation to express fractional quantities. This format also allows scientific notation to represent very large and very small numbers.

To enable floating point operations, an application must include a GEOS Math Library. There are two Math Libraries: math.h and math.goh . The file math.h includes what you need to get floating point numbers to work within your application. The object file, math.goh (which includes math.h ), includes the float-format controller, an object that allows the user to control how floating point numbers are formatted within an application.

In most cases, the mere inclusion of the Math Library will eliminate the need to directly call Math Library routines. (The C compiler will parse the mathematical expressions into appropriate floating point routines.) You may, however, wish to call some of these routines directly. Developers using Object Assembly must use this latter approach. C developers may also want to directly manipulate the FP stack for involved computations that they would rather not leave to the compiler. This approach is discussed in the latter half of this chapter.

Use of math.goh is optional, depending on the purpose of the application. A spreadsheet application, for example, might want to allow the user to format FP numbers. Other applications might not want to include this file.

1 Basic Math Functions
1.1 Algebraic Functions
1.2 Transcendental Functions
1.3 Random Number Generation
2 Conversions to Other Types
3 Float Formats
3.1 System-defined Formats
3.2 User-defined Formats
4 Direct FP Operations
4.1 Floating Point Numbers
4.2 The Floating Point Stack
4.3 Floating Point Math Routines

The Math Library: 1 Basic Math Functions

The Math Library includes many routines and structures that make the manipulation of FP numbers possible. Most of these are transparent to a C programmer.

C includes the following floating point types: float , double , and long double . A floating point number in GEOS is of type FloatNum , which uses the IEEE 80 bit standard (equivalent to a long double) to represent floating point values. FloatNum has overloaded the float, double, long double, and all basic math functions.

A FloatNum consists of a one bit sign, a 15 bit exponent, and a 64 bit mantissa. The maximum exponent allowed is 7FFEh. An exponent of 7FFFh (FP_NAN) signals an underflow or overflow. You may check the value of this exponent using the macro FLOAT_EXPONENT to extract the exponent of a FloatNum .

Code Display D-1 Extracting an Exponent

FloatNum myNum;
if (FLOAT_EXPONENT(&myNum) == FP_NAN)
{
     return(ERROR);
}

If you use any floats, doubles, or long doubles in your application, GEOS will convert these types into FloatNum values automatically and call the appropriate GEOS functions to manipulate the numbers. As this is the case, it is usually easier to declare any floating point variables in your application as FloatNum types (or long doubles). This cuts down on conversion time.

The Math Library provides all the familiar mathematical functions to manipulate these GEOS FP numbers (addition, multiplication, computation of logarithms, etc.). In C, most of these functions will automatically be called when their corresponding C operation involving FP numbers take place. (For example, when using the `+' binary operator to add two FP numbers, the Math Library will use the corresponding FloatAdd() routine.)

You may, under special circumstances, wish to use these math routines directly. If you do so, you will need to manipulate the floating point stack manually, pushing numbers on the stack and making sure numbers are in the proper stack location to perform each operation. In most cases, the FP math routines operate on numbers already in place on the FP stack; they take and return no arguments of their own.

Two functions you will need to use if you take this approach are FloatPushNumber() and FloatPopNumber() . FloatPushNumber() takes the address of a variable (of type FloatNum ) to push onto the FP stack; FloatPopNumber() takes the address of a buffer to place an FP number popped off the FP stack. Other routines (for example, FloatAdd() ) can then be called to operate on the FP stack. (See Adding Two FP Numbers for an example using this approach.)

There are many other routines that perform stack manipulation by shifting locations of FP numbers on the stack ( FloatRoll() , FloatDrop() , etc.). These routines are covered in detail in the latter part of this chapter because they are seldom needed in a C applications.

Algebraic Functions

Transcendental Functions

Random Number Generation

The Math Library: 1.1 Basic Math Functions: Algebraic Functions

Algebraic routines perform algebraic operations on FP numbers. The Math Library provides all of the basic algebraic routines that operate on GEOS FP numbers (addition, subtraction, etc.).

If you wish to call these routines directly rather than rely on the C operations, you may manipulate the floating point stack directly. To add two numbers using FloatAdd() for example, you would use FloatPushNumber() twice to push the two values to add onto the FP stack, and then call FloatAdd() to operate on the FP stack. (See Adding Two FP Numbers .)

Code Display D-2 Adding Two FP Numbers

/*
 * The following two methods each add two FP numbers and return the result. The
 * first method is familiar C code. The second example uses the floating point
 * routines from math.h directly. Note that the C code will be assembled into code
 * that uses FloatAdd() also, but that this is transparent to the code.
 */
@method MyProcessClass, MSG_SUM_FLOATS {
    long double			number1, number2, number3;
    number1 = 1.0;
    number2 = 2.0;
    number3 = number1 + number2;
    return(number3);
}
@method MyProcessClass, MSG_SUM_FLOATS_MANUALLY {
    long double			number1, number2, number3;
    number1 = 1.0;
    number2 = 2.0;
    FloatPushNumber(&number1);				/* Push number1 onto the FP stack. */
    FloatPushNumber(&number2);				/* Push number2 onto the FP stack. */
    FloatAdd();				/* Add the top two numbers on the FP stack. The
                 * result will be placed on top of the FP stack.*/
    FloatPopNumber(&number3);				/* Pop the result into the number3 variable. */
    return(number3);
}

Several functions have no equivalent C operation. Consult Direct FP Operations for more details on using these functions.

Note that although direct comparisons between FP numbers are not allowed, you can use the FloatLt0(), FloatGt0(), and FloatEq0() functions to compare whether the the addition of two FP numbers is gretaer than, less than, or equal to zero.

The Math Library: 1.2 Basic Math Functions: Transcendental Functions

The Math Library provides an array of transcendental routines that operate on GEOS FP numbers. A transcendental function is one which cannot be derived through algebraic means. Examples of transcendental functions include the trigonometric functions (sine, cosine, etc.) and the logarithmic functions (log, natural log, etc.).

The table above lists the function names and the operations they perform. Typical functions in C are listed alongside. (The basic language of C itself includes no such transcendental functions but almost all compilers include a C math library that does.) As can be seen in the table, several functions have no equivalent C operation. Consult Float Formats for more details on using these functions.

The Math Library: 1.3 Basic Math Functions: Random Number Generation

 FloatRandomize(),  FloatRandom(),  FloatRandomN()

The Math Library also provides routines to create random numbers. Using any of these routines requires that you manually push and pop numbers on the FP stack.

FloatRandomize() primes the random number generator, in preparation for a call to FloatRandom() or FloatRandomN() . If FloatRandomize() is passed the flag RGIF_USE_SEED, the routine must also pass a developer supplied seed.

FloatRandom() returns a random value between 0 (inclusive) and 1 (exclusive). The number is placed on top of the FP stack. To assign that value to a variable, use FloatPopNumber() .

FloatRandomN() returns a random value between 0 (inclusive) and N (exclusive), where N is an integer. The integer value must be on top of the FP stack. The returned integer is pushed onto the FP stack.To assign that value to a variable, use FloatPopNumber() .

Code Display D-3 Creating a Random Number

/*
 * This method takes a passed seed (passedSeed) and returns a random integer
 * between 0 and 100.
 */
@method MyProcessClass, MSG_GET_RANDOM_FLOAT {
    long double			randomX;
    /* Prime the random number generator. */
    FloatRandomize(RGIF_USE_SEED, passedSeed);
    randomX = 100;
    FloatPushNumber(&randomX);
    FloatRandomN();
    FloatPopNumber(&randomX);
    return(randomX);
}

The Math Library: 2 Conversions to Other Types

In many cases, FP numbers will need to be converted to different types for use in different parts of an application. For example, floating point numbers may be involved in a complex function that returns an integer. FP numbers may also need to appear to the user as ASCII text.

Converting Into Other Floats

There are several routines which convert GEOS FP numbers into other float formats, compatible with the C types float and double . Typically in C, this conversion is accomplished by casting the FP numbers into the other type. It is therefore done automatically for you.

If you are working in Assembly or you wish to directly pass floats or doubles to C stubs, consult Direct FP Operations .

Converting ASCII to FP Numbers
 FloatAsciiToFloat()

FloatAsciiToFloat() converts a number represented in an ASCII text format into a FP number. The routine recognizes two flags:

  • FAF_PUSH_RESULT
    Pushes the result onto the FP stack.
  • FAF_STORE_NUMBER
    Stores the result in a specified address.

The routine must also be passed a pointer to the string to convert, the number of characters in the string to convert (starting at the address) and the buffer to store the FP number if passing FAF_STORE_NUMBER.

Converting FP Numbers to ASCII
 FloatFloatToAscii(),  FloatFloatToAscii_StdFormat(),  FloatFloatIEEE64ToAscii_StdFormat()

FloatFloatToAscii() converts an FP number into ASCII text format. The routine must be passed a stack frame, which may be set up by declaring a local variable of type FFA_stackFrame and moving data into the appropriate fields. You should also pass the routine a pointer to a buffer to store the resultant string. This buffer must be declared with a length of either:

FLOAT_FLOAT_TO_ASCII_NORMAL_BUF_LEN
FLOAT_FLOAT_TO_ASCII_HUGE_BUF_LEN

The FFA_stackFrame is a union of two structures: FloatFloatAsciiData or FloatFloatToDateTimeData . You will want to use the FloatFloatToAsciiData structure in most cases; FloatFloatToAsciiDateTimeData is used to format a FP number (representing a date and time) into a date-time format passed in the structure. The routine checks a bit in the structure to see which structure is being passed.

Normal Conversions

The FloatFloatToAsciiData structure is used most often in formatting FP numbers into ASCII. The structure is rather large and cumbersome to set up. You may wish to use the routine FloatFloatToAscii_StdFormat() which sets up many of these entries automatically. ( FloatFloatToAsciiData Structure lists the entries of the FloatFloatToAsciiData structure.)

Code Display D-4 FloatFloatToAsciiData Structure

typedef struct {
/*
 * FFA_params stores the entries that the caller must set up.
 */
    FloatFloatToAsciiParams			FFA_params;
/*
 * These entries store information returned by FloatFloatToAscii() that may be
 * examined.
 */
    word			FFA_startNumber;
    word			FFA_decimalPoint;
    word			FFA_endNumber;
    word			FFA_numChars;
    word			FFA_startExponent;
/*
 * The rest of the entries are for internal use only.
 */
    word			FFA_bufSize;
    word			FFA_saveDI;
    word			FFA_numSign;
    byte			FFA_startSigCount;
    byte			FFA_sigCount;
    byte			FFA_noMoreSigInfo;
    byte			FFA_startDecCount;
    byte			FFA_decCount;
    word			FFA_decExponent;
    word			FFA_curExponent;
    byte			FFA_useCommas;
    byte			FFA_charsToComma;
    char			FFA_commaChar;
    char			FFA_decimalChar;
} FloatFloatToAsciiData;

FFA_ params is a structure that stores the following entries of its own:

formatFlags
Flags specifying the look and feel of the ASCII text format (see below for allowed flags).
decimalOffset
Integer which specifies the number of decimal places to shift the output. For example, to display a number in terms of millions, a decimalOffset of -6 shifts the decimal point six places to the left; to display in terms of tenths would require a decimalOffset of 1.
totalDigits
Integer which specifies the maximum number of decimal places (integer and decimal portions) that the FP number may exhibit. The ASCII string is truncated if the length of the string is greater than this number.
decimalLimit
Integer which specifies the maximum number of digits to the right of the decimal point. For example a decimalLimit of 2 would print out 123.456789 as 123.46.
preNegative
Characters used to precede a negative number, in the format of a null terminated text string.
postNegative
Characters used to follow a negative number, in the format of a null terminated text string.
prePositive
Characters used to precede a positive number, in the format of a null terminated text string.
postPositive
Characters used to follow a positive number, in the format of a null terminated text string.
header
Characters used to precede the number, in the format of a null terminated text string. For example, a typical header for a currency format might be "$".
trailer
Characters used to follow a FP number, in the format of a null terminated text string.

The formatFlags record is a record of type FloatFloatToAsciiFormatFlags and defines the format of the ASCII output. Set the appropriate flags to get the desired output.

  • FFAF_FROM_ADDR
    Set if routine should take the FP number from a specified address, rather than the FP stack. This pointer (*number) should be passed to FloatFloatToAscii() if this flag is set.
  • FFAF_DONT_USE_SCIENTIFIC
    Set if the result should not be expressed in scientific notation under any condition. If this is set, the number will always be formatted in fixed format.
  • FFAF_SCIENTIFIC
    Set if the result should be expressed in scientific notation even if the number can be expressed in fixed point format. For example, the number `2' will be expressed as "2 x 10 0 ."
  • FFAF_PERCENT
    Set if the result should be expressed as a percentage.
  • FFAF_USE_COMMAS
    Set if the result should use commas to separate thousands.
  • FFAF_NO_TRAIL_ZEROS
    Set if extraneous zeros to the right of the decimal point should be ignored. For example, "123.67000000" will be reduced to "123.67."
  • FFAF_NO_LEAD_ZEROS
    Set to ignore the lead zero for a number less than one. That is, "0.123" is reduced to ".123."
  • FFAF_HEADER_PRESENT
    Set if a header is present; this speeds up conversion.
  • FFAF_TRAILER_PRESENT
    Set if a trailer is present; this speeds up conversion.
  • FFAF_SIGN_CHAR_TO_FOLLOW_HEADER
    Set if sign character(s) should follow the header.
  • FFAF_SIGN_CHAR_TO_PRECEDE_TRAILER
    Set if sign character(s) should precede the trailer.

The rest of the entries in FloatFloatToAsciiData store information filled in by FloatFloatToAscii() . These entries are described below:

FFA_ startNumber stores the offset to the start of numeric characters in the ASCII buffer.
FFA_ decimalPoint stores the offset to the decimal point character or zero if no decimal point is present.
FFA_ endNumber stores the offset to the end of the numeric characters in the ASCII buffer.
FFA_ numChars stores the total number of characters in the ASCII buffer (excluding the null terminator). This entry is set to zero if an error is encountered.
FFA_ startExponent stores the offset to the "E" character in the ASCII buffer or zero if no exponent is present. Applications can check this to see if the number was expressed in scientific notation using the `E' format.

Date-Time Conversions

FFA_stackFrame may contain FloatFloatToAsciiDateTimeData if FloatFloatToAscii() is being used to convert a FP number into a date-time format. In that case FFA_stackFrame contains the structure FloatFloatToDateTimeData instead of FloatFloatToAsciiData . ( FFA_stackFrame is a union.)

FloatFloatToDateTimeData contains one entry, FFA_ dateTimeParams . This structure contains several flags which specify how the date-time should be formatted and a number of entries which break down the date-time into its respective parts (year, month, day etc.) If none of these entries are filled in, the date-time is taken from the top of the FP stack.

Date-times are represented by FP numbers in GEOS. The integer portion represents dates as integers counted from Jan 1, 1900, which is designated as 1. The highest date allowed is 73049 (December 31, 2099). The fractional portion represents a fraction of the day between midnight (0.000000) and 11:59:59 p.m. (0.999988). This fractional value is derived from the hour, minute and second of the day.

Code Display D-5 DateTime Parameters

typedef struct {
    FloatFloatToDateTimeFlags				FFA_dateTimeFlags;
    word				FFA_year;
    byte				FFA_month;
    byte				FFA_day;
    byte				FFA_weekday;
    byte				FFA_hours;
    byte				FFA_minutes;
    byte				FFA_seconds;
} FloatFloatToDateTimeParams;
/*
 *	FloatFloatToDateTimeFlags record
 */
typedef WordFlags FloatFloatToDateTimeFlags;
#define		FFDT_DATE_TIME_OP			0x8000
#define		FFDT_FROM_ADDR			0x4000
#define		FFDT_FORMAT			0x3fff

The flag FFDT_DATE_TIME_OP is set to notify the FloatFloatToAscii() routine that this operation is a date-time format, not a normal float to ASCII conversion. This flag must be set if you want to convert the FP number into a date-time format using FloatFloatToAscii() .

FFDT_FROM_ADDR is set if the date-time FP number should not be taken from the FP stack (or passed directly in the FloatFloatToDateTimeParams structure) but should instead be taken from the address passed in FloatFloatToAscii() .

FFDT_FORMAT stores the DateTimeFormat that the routine will use to format the number into a date-time string.

If the date-time is directly passed in, and not taken from an FP date-time number either at a passed address or the top of an FP stack, FloatFloatToAscii() looks at the other passed parameters.

FFA_ year specifies the year. The value must be between 1900 and 2099. This is not a one-based year, as it is when presented as a date-time number.

FFA_ month is the month of the year, a value between 1 and 12.

FFA_ day is the day of a month, a value between 1 and 31.

FFA_ hour specifies the hour of the day, a value between 0 and 23. Zero specifies midnight.

FFA_ minutes specifies the minute of the hour, a value between 0 and 59.

FFA_ seconds specifies the second of the minute, a value between 0 and 59.

Using Standard Formats

FloatFloatToAscii_StdFormat() uses a pre-set stack frame, eliminating the need to set up the variables of the FloatFloatToAsciiData structure manually. The only flags recognized are FFAF_FROM_ADDR, FFAF_SCIENTIFIC, FFAF_PERCENT, FFAF_USE_COMMAS, and FFAF_NO_TRAIL_ZEROS. The developer must pass the number of total digits and the number of decimal digits desired. If the flag FFAF_FROM_ADDR is used, a pointer to a FP number (not on the FP stack) must also be passed.

The standard format sets the following elements of the stack frame to zero: decimalOffset, header, trailer, postNegative, prePositive , and postPositive . The structure element preNegative is set to the minus sign ("-").

FloatFloatIEEE64ToAscii_StdFormat() performs the same operation as FloatFloatToAscii_StdFormat() except that the FP number is passed (in 64 bit format) and is not taken from the stack. The entire FP number (not just a pointer to it) must be passed. All criteria for FloatFloatToAscii_StdFormat() applies to this routine, except that the flag FFAF_FROM_ADDR is not used.

Date and Time Routines

 FloatGetDateNumber(),  FloatDateNumberGetYear(),  FloatDateNumberGetMonthAndDay(),  FloatDateNumberGetWeekday(),  FloatGetTimeNumber(),  FloatTimeNumberGetHour(),  FloatTimeNumberGetMinutes(),  FloatTimeNumberGetSeconds()

FloatGetDateNumber() , when passed the month, day, and year, converts the data into an FP "date number" representation. This format represents dates as integers counted from Jan 1, 1900, which is designated as 1. The highest date allowed is 73050 (December 31, 2099).

FloatDateNumberGetYear(), FloatDateNumberGetMonthAndDay() and FloatDateNumberGetWeekday() all return the appropriate data, either the year, month and day, or weekday, given an FP "date number" as defined above. All data are returned as integers, not as FP numbers, and the original FP "date number" is popped off of the stack. Years are returned as integers between 1900 and 2099. Month and Days are returned as integers between 1 and 12, and 1 and 31, respectively. Weekdays are returned as integers between 1 and 7, where 1 is Sunday, 2 is Monday, etc.

FloatGetTimeNumber() when passed hours, minutes, and seconds returns an FP decimal representation between midnight (0.000000) and 11:59:59 p.m. (0.999988).

FloatTimeNumberGetHour(), FloatTimeNumberGetMinutes() and FloatTimeNumberGetSeconds() return the appropriate data given an FP "time number" as defined above. The original FP "time number" is popped off of the stack.

Note that both "date numbers" and "time numbers" can be added to specify a specific point in time. For example, 73050.999988 would specify December 31, 2099, 11:59:59. Since these formats are in FP format, they can be operated on with all standard functions in the FP library.

Miscellaneous Date Time Routines

 FloatGetDaysInMonth(),  FloatGetNumDigitsInIntegerPart(),  FloatFormatNumber()

FloatGetDaysInMonth() returns the total number of days in a specific month, for a specific year. The routine must be passed the appropriate month and year.

FloatGetNumDigitsInIntegerPart() returns the number of digits in the integer portion of an FP number. Numbers between zero and one will return one as the number of digits.

FloatStringGetDateNumber(), FloatStringGetTimeNumber()

The Math Library: 3 Float Formats

FP numbers can be displayed in many ways. For example, as we have seen, an FP number may actually represent a date-time. When we display the FP number 366.0000, we may want to show it (in text) as "Jan 1, 1901." The Math Library has a number of system-defined formats for your use. You may also allow users to define their own formats with the Float Format controller.

The underlying structures and routines to use and create float format options are explained in the next section. In most cases, however, the simple inclusion of a Float Format controller (of FloatFormatClass ) provides all of the UI and functionality to create and apply formats to FP numbers within text objects.

System-defined Formats

User-defined Formats

The Math Library: 3.1 Float Formats: System-defined Formats

A system-defined FP format is stored within a FormatParams structure. This structure defines whether the FP number is a number to be converted into numerical text or a date-time. These format parameters are stored within arrays managed by the format control code.

Code Display D-6 System-defined Float Formats

/*
 * System-defined float formats are stored in an array that is maintained and
 * accessed by the float controller code. Each element is made up of a
 * FormatParams structure.
 */
typedef struct {
    /*
     * The FloatFloatToAsciiParams_Union stores either a
     * FloatFloatToAsciiParams structure if the number is a `pure' FP number,
     * or a FloatFloatToDateTimeParams structure if the number is a date-time.
     * In this way, it is essentially the same as the FFA_stackFrame discussed
     * earlier.
     */
    FloatFloatToAsciiParams_Union				FP_params;
    /*
     * FP_formatName stores the name of this formatting option that will be
     * displayed in the float controller's dynamic list. This name is loaded
     * from the optr given in FP_nameHan and FP_nameOff.(The table where these
     * strings are kept is within a localizable resource and therefore will
     * have different text under different country setups.)
     */
    char				FP_formatName[FORMAT_NAME_LENGTH+1];
    word				FP_nameHan;		/* MemHandle */
    word				FP_nameOff;		/* ChunkHandle */
    /*
     * FP_listEntryNum stores the zero-based position of this FormatParams
     * entry within the table.
     */
    word				FP_listEntryNum;
    /*
     * FP_signature is an internal field used for error-checking.
     */
    word				FP_signature;
} FormatParams;

An application will never need to access this table of formats directly. GEOS contains several routines (in math.goh ) that can access this table and add, delete and modify table entries. Usually, it is easiest to include a Float Format controller in your application if you intend to allow the user to change float formats with these routines.

There are many system-defined float formatting options. These formats are identified by FormatIdType enumerations. Each type corresponds to a FormatParams structure.

Each FormatIdType enumeration is a direct offset into the float format lookup table. To distinguish between system-defined and user-defined formats, the high bit of a FormatIdType is set to indicate that the format is system-defined. Thus, 8000h refers to the first system-defined format, 8000h + (size( FormatParams )) refers to the second system-defined format, etc.

The format strings themselves are stored within a localizable resource, so that they may appear in a manner relevant to the particular country involved. For example, an FP number of 12.0 using the FormatIdType FORMAT_ID_CURRENCY might appear in the U.S. as $12.00, but will appear as £12.00 in Great Britain.

Code Display D-7 Float Format IDs

typedef	enum {
    FORMAT_ID_GENERAL	 	 	 		= 0x8000,
    FORMAT_ID_FIXED		 	 		= 0x8061,
    FORMAT_ID_FIXED_WITH_COMMAS	 	 			= 0x80c2,
    FORMAT_ID_FIXED_INTEGER	 				= 0x8123,
    FORMAT_ID_CURRENCY		 	 		= 0x8184,
    FORMAT_ID_CURRENCY_WITH_COMMAS 	 				= 0x81e5,
    FORMAT_ID_CURRENCY_INTEGER 	 				= 0x8246,
    FORMAT_ID_PERCENTAGE 	 	 			= 0x82a7,
    FORMAT_ID_PERCENTAGE_INTEGER 	 				= 0x8308,
    FORMAT_ID_THOUSANDS 	 	 			= 0x8369,
    FORMAT_ID_MILLIONS 	 	 			= 0x83ca,
    FORMAT_ID_SCIENTIFIC 	 	 			= 0x842b,
    FORMAT_ID_DATE_LONG 	 	 			= 0x848c,
    FORMAT_ID_DATE_LONG_CONDENSED 	 				= 0x84ed,
    FORMAT_ID_DATE_LONG_NO_WKDAY 					= 0x854e,
    FORMAT_ID_DATE_LONG_NO_WKDAY_CONDENSED 					= 0x85af,
    FORMAT_ID_DATE_SHORT 	 	 			= 0x8610,
    FORMAT_ID_DATE_SHORT_ZERO_PADDED 					= 0x8671,
    FORMAT_ID_DATE_LONG_MD 	 	 			= 0x86d2,
    FORMAT_ID_DATE_LONG_MD_NO_WKDAY 					= 0x8733,
    FORMAT_ID_DATE_SHORT_MD 					= 0x8794,
    FORMAT_ID_DATE_LONG_MY 	 	 			= 0x87f5,
    FORMAT_ID_DATE_SHORT_MY 	 				= 0x8856,
    FORMAT_ID_DATE_YEAR 	 	 			= 0x88b7,
    FORMAT_ID_DATE_MONTH 	 	 			= 0x8918,
    FORMAT_ID_DATE_DAY 	 				= 0x8979,
    FORMAT_ID_DATE_WEEKDAY 	 				= 0x89da,
    FORMAT_ID_TIME_HMS			 		= 0x8a3b,
    FORMAT_ID_TIME_HM					= 0x8a9c,
    FORMAT_ID_TIME_H					= 0x8afd,
    FORMAT_ID_TIME_MS			 		= 0x8b5e,
    FORMAT_ID_TIME_HMS_24HR		 			= 0x8bbf,
    FORMAT_ID_TIME_HM_24HR		 			= 0x8c20,
    FORMAT_ID_INDETERMINATE 	 				= 0xffff
} FormatIdType;

The Math Library: 3.2 Float Formats: User-defined Formats

Users may also design their own float formatting options. These user-defined formats are stored in FormatEntry structures. The array of these user-defined formats is kept separate from the system-defined formats. (user-defined formats are stored within their own VM block).

User-defined FormatIdType enums do not have the high bit (8000h) set in order to distinguish them from system-defined FormatIdType enums.

Code Display D-8 User-defined Formats

/*
 * User-defined formats are stored in a VM block that is created and maintained by
 * the float controller code. As each new format is added, space is made for a
 * FormatEntry structure. As formats are deleted, their entry's FE_used field is
 * set to zero to indicate that the entry is free for new formats to overwrite.
 */
typedef struct {
    /*
     * Each FormatEntry contains a corresponding FormatParams structure.
     */
    FormatParams 		FE_params;
    /*
     * FE_listEntryNumber is the zero-based position of the format counting
     * both previous user-defined formats and system-defined formats. For
     * example, if there are 10 system-defined formats, the first user-defined
     * format will have a FE_listEntryNumber of 10 (because positions are
     * zero-based).
     */
    word		FE_listEntryNumber;
    /*
     * FE_used, if non-zero, indicates that this entry within the user-defined
     * list is currently in use. If FE_used is zero, then this entry position
     * may be used to add a new user-defined entry without increasing the size
     * of the VM block.
     */
    byte		FE_used;
    /*
     * FE_sig is an error-checking field used internally.
     */
    word		FE_sig;
} FormatEntry;

The Math Library: 4 Direct FP Operations

The Math Library allows your application to use floating point (FP) numbers. C Developers will find little reason to make direct calls to functions in the Math Library as most of this functionality is taken care of for you in the C environment. (An exception is conversions from FP numbers to ASCII text characters and use of special date-time routines.) Assembly developers, however, will find direct use of the functions and structures in the Math Library essential.

The latter half of this chapter (from this point on) is provided for Assembly developers who need more complete information about the intricacies of the Math Library. Much of this information may also be useful for C developers who wish to make direct calls to the functions in this library for optimization purposes.

Floating Point Numbers

The Floating Point Stack

Floating Point Math Routines

The Math Library: 4.1 Direct FP Operations: Floating Point Numbers

The Math Library defines floating point numbers by using binary point representation. In this format the bit in the zeros place is multiplied by 20, the bit in the minus-ones place is multiplied by 2-1, etc. The exponent is in base 2. For example, the Binary Point representation for 5 is:.

Floating Point Numbers in GEOS follow the IEEE 754 standard used by Intel. In this format a floating point number is represented in an 80 bit (5 word) format. (In C, this type is known as a long double .) This format specifies that the 80 bits contain:

  • a 1 bit sign (the most significant bit).
  • a 15 bit exponent (0000h to 7FFFh).
  • a 64 bit mantissa.

The 15 bit exponent is biased by 3FFFh, so that an exponent of 1 would be represented by 4000h, and an exponent of -1 would be represented by 3FFEh. This produces a hexadecimal range for the exponent of 4000h, or a decimal range of 4932.

Infinity is represented by 7FFFh in the exponent and 1 followed by 63 zeros in the mantissa. Therefore, all exponent bits are set to 1. (Negative infinity is the same except that the sign bit is also set to 1).

Zero is a special case in which all 80 bits-- sign, exponent, and mantissa--are set to zero.

The mantissa is normalized so that the most significant bit is always 1 (except when the FP number is zero). The binary point follows this 1 bit. This produces a precision of approximately 19 decimal places, which is adequate for most needs. This most significant bit is not "assumed away"; it is always present in the mantissa.

Note that in most uses, the use of binary point representation is transparent to the application.

The Math Library: 4.2 Direct FP Operations: The Floating Point Stack

Floating Point (FP) numbers are placed and manipulated on an FP stack. Numbers can be rearranged, operated on, and removed from this stack through pushes, pops, and the use of special routines.

Initialization of the FP Stack

 FloatInit(),  FloatExit()

Before performing any floating point operations, a thread needs to call FloatInit() to create and initialize an FP stack. Each thread using floats must have its own unique FP stack. This call to FloatInit() is automatically performed by any application that includes the Math Library.

FloatInit() creates a swapable block of memory for the thread, initializes various stack pointers, and stores the handle for the block in the thread's data structure. FloatInit() must be passed the size of the stack to create (in bytes) and the type of stack ( FloatStackType ) to create.

The default FP stack holds 25 FP elements (250 bytes). An FP stack must be able to hold at least 5 FP elements.

The default floating point stack is FLOAT_STACK_GROW which instructs the system to increase the size of the stack whenever its bounds are reached. This is done automatically.

Other FloatStackType types are FLOAT_STACK_WRAP, which drops the FP numbers at the low end of the stack (effectively wrapping over that end) and FLOAT_STACK_ERROR which signals an error when the stack limit has been reached.

FloatExit() detaches the floating point stack for the current thread and frees its memory. FloatExit() only frees the FP stack associated with the current thread; other FP stacks in other threads remain unaffected. As is the case with FloatInit() , the call to FloatExit() is automatically performed by any application that includes the Math Library.

If FloatInit() is called twice before calling FloatExit() , the data on the original floating point stack will be lost.

Pushing and Popping on the FP Stack

 FloatPushNumber(),  FloatPopNumber(),  FloatDepth()

FloatPushNumber() pushes an FP number onto the top of the FP stack for the current thread from a passed buffer. The number must be already set up in 80 bit, FP format. The routine must be passed the pointer to the buffer storing the number.

Similarly, FloatPopNumber() pops an FP number from the top of the FP stack for the current thread into a passed buffer.

FloatDepth() returns the number of FP numbers currently in place on the stack.

Note: For clarity in diagrams within this chapter, stack locations will be numbered in order from the top position of the stack, S1 being first, S2 being second, etc. Variables will be numbered in the order they are pushed onto the stack, so that if X1, X2, and X3 are pushed onto the stack, the format illustrated below will result.

FP Stack Manipulation

 FloatRoll(),  FloatRollDown(),  FloatRot(),  FloatSwap()

Besides basic pushing and popping, the Float Library also includes many other routines which manage FP numbers on the stack. Unless otherwise specified, an operation that pushes, pops, or extracts an FP number on the stack affects all other FP numbers below the position of the operation by shifting their location in the stack either up or down, in standard stack fashion.

FloatRoll() pushes a selected FP number (S X ) onto the top of the stack (S1), removing it from location SX in the process. FloatRoll() passed with a value of 3 would move the FP number in S3 onto the top of the stack, pushing the stack in the process. All FP numbers below the extracted number remain unaffected by this routine.

FloatRollDown() performs the inverse operation of FloatRoll() . popping the top stack value (S1) into the specified location on the stack (S X ). FloatRollDown() passed with a value of 3 would move the FP number in S1 into location S3, shifting the stack in the process.

Both of these routines must be passed a stack location to move to or from.

FloatRot() rotates the top three numbers on the stack, placing S3 onto the top of the stack. This is equivalent to a FloatRoll() passed with a value of 3.

FloatSwap() exchanges S1 and S2.

Repetitious applications of these routines will return the stack to its former state.

Copying and Deleting Numbers on the FP Stack

 FloatPick(),  FloatOver(),  FloatDrop(),  FloatDup()

FloatPick() copies the contents of S X and pushes that value onto the FP stack. The entire stack is pushed in the process. FloatPick() passed with a value of 3 would copy the contents of S3 onto the FP stack.

FloatOver() copies S2 to the top of the stack, equivalent to a FloatPick() passed with a value of 2.

FloatDrop() drops (pops) the top number (S1) off the FP stack. This routine is different than FloatPopNumber() because the routine does not place the popped number into a memory address, and is therefore much faster.

FloatDup() duplicates the value at S1, pushing it onto the top of the stack. The stack is pushed in the process.

Comparing Numbers on the FP Stack

 FloatComp(),  FloatCompESDI(),  FloatCompAndDrop()

These routines essentially perform the same operation as the Assembly command cmp . FloatComp() performs a compare of the top two FP numbers (S1 and S2) and sets the appropriate flags in the flags register. The two FP numbers remain on the stack. FloatCompESDI() compares the contents of es:[di] with the value in S1 (and the FP number in S1 remains on the stack). FloatCompAndDrop() performs a compare of S1 and S2 and drops both from the FP stack.

Recovery of the FP Stack

 FloatGetStackPointer(),  FloatSetStackPointer()

FloatGetStackPointer() returns the current stack pointer value of the FP stack. FloatSetStackPointer() sets the stack pointer to a previous position. This routine must be passed a value that is greater than or equal to the current value of the stack pointer. (I.e. you must be throwing something, or nothing, away.)

These routines may be useful before the execution of involved routines that may push many numbers onto the stack. If an unrecoverable error is encountered, the programmer need not pop the intermediate values off the stack to return to the previous stack configuration. Only the stack pointer is saved; the state of the stack is not. If any numbers below the stack pointer are popped or altered, FloatSetStackPointer() will not recover the previous state of the stack.

The Math Library: 4.3 Direct FP Operations: Floating Point Math Routines

The Float Library provides a number of routines to modify data on the FP stack. These routines can be categorized in several major groups:

  • Constant Routines
  • Math Routines
  • Transcendental Routines
  • Conversion Routines
  • Date and Time Routines

Constant Routines

Float0(), FloatPoint5(), Float1(), FloatMinusPoint5(), FloatMinus1(),Float2(), Float5(), Float10(), Float3600(), Float16384(), Float86400()
FloatPi(), FloatPiDiv2(),FloatLg10(), FloatLn2(), FloatLn10(), FloatSqrt2()

The Constant Routines provide a means of quickly obtaining an often used number or an often used operation with a constant operand. Each of these functions pushes the constant FP value onto the top of the FP stack. (For example, FloatMinusPoint5() pushes -.5 onto the FP stack.)

FloatPi() , FloatPiDiv2() , FloatLg10() , FloatLn2() , FloatLn10() , and FloatSqrt2() each push the specified transcendental constant onto the FP stack: p,p/2, the log of 10, the natural log of 2, the natural log of 10,and the square root of 2, respectively. (See also Transcendental Routines.)

Constant Operands

FloatMultiply2(), FloatMultiply10(), FloatDivide2(), FloatDivide10(), Float10ToTheX()

FloatMultiply2(), FloatMultiply10(), FloatDivide2(), FloatDivide10() perform the specified operations on the contents of S1, either multiplying or dividing the contents of S1 by 2 or 10, and push the result onto the FP stack. The original value in S1 is popped off of the stack.

Float10ToTheX() pushes 10 to a passed exponent onto the FP stack.

Algebraic Routines

FloatAbs(), FloatAdd(), FloatSub(), FloatDivide(), FloatMultiply(), FloatDIV(), FloatMod(),FloatFactorial(), FloatNegate(), FloatInverse()

The Math Routines perform various operations on numbers already placed on the FP stack. All of the following routines pop the operated numbers off of the stack and push the result onto S1.

FloatAdd() adds the contents of S1 and S2, and pushes the result onto the top of the FP stack. The original values are popped off the stack.

FloatSub() subtracts the contents of S1 from the contents of S2, and pushes the result onto the top of the FP stack. The original values are popped off the stack.

Note that, in general, numbers will be pushed onto the stack in the order they would normally be operated on. For example, to obtain X1-X2, the programmer would first push X1, then X2, and call FloatSub , as in the following diagram:

since X1 would now be in location S2, while X2 is in location S1.

FloatMultiply() multiplies the contents of S1 and S2 and pushes the result onto the top of the FP stack. The original values are popped off the stack.

FloatDivide() divides the contents of S2 by the contents of S1 and pushes the result onto the top of the FP stack. The original values are popped off the stack.

FloatDIV() performs a FloatDivide() , truncating the fractional portion of the number and returning only the integer result. The original values are popped off the stack.

FloatMod() pushes S2 mod S1 (the remainder of FloatDivide() ) onto the top of the FP stack. The original values are popped off the stack.

FloatAbs() pushes the absolute value of S1 onto the top of the FP stack. The original value is popped off the stack.

FloatFactorial() pushes the factorial (x!) of the value in S1 onto the top of the FP stack. The original value is popped off the stack.

FloatNegate() negates the value in S1 and pushes the result onto the top of the FP stack. The original value is popped off the stack.

FloatInverse() pushes the inverse of the value in S1 (-S1) onto the top of the FP stack. The original value is popped off of the stack.

Comparison Routines

FloatMax(), FloatMin(), FloatLt0(), FloatEq0(), FloatGt0()

FloatMax() performs a compare of the FP numbers in S1 and S2 and, if necessary, swaps the greater number into S1.

FloatMin() performs a compare of the FP numbers in S1 and S2 and, if necessary, swaps the lesser number into S1.

FloatLt0(), FloatEq0(), FloatGt0() check whether the FP number in S1 is less than zero, equal to zero, or greater than zero. The carry bit is set to TRUE if the relationship is true. The original value is popped off of the stack.

Fractional and Integral Routines

FloatFrac(), FloatTrunc(), FloatInt(), FloatIntFrac(), FloatRound()

FloatFrac() pushes the fractional portion of S1 onto the FP stack. The original value is popped off of the stack.

FloatTrunc() pushes the integral portion of the contents of S1 onto the FP stack. This amounts to a rounding of the FP number toward zero, so that FloatTrunc() performed on -7.8 would return -7. The original value is popped off the stack.

FloatInt() rounds S1 down to its integral component, so that FloatInt() performed on -7.8 would return -8. Note that for negative numbers, this is different from FloatTrunc() . The original value is popped off the stack.

FloatIntFrac() splits a number into its fractional and integral parts, with the fractional part in S1 and the integral part in S2. The original value is popped off the stack.

FloatRound() rounds S1 to a given number of decimal places. FloatRound() passed with zero as an argument rounds S1 to the nearest integer, rounding up if greater than or equal to .5, rounding down if less than .5

Routines that Return Random Values

FloatRandom(), FloatRandomN(), FloatRandomize()

FloatRandom() pushes a random number between 0 (inclusive) and 1 (exclusive) onto the stack.

FloatRandomN() pushes a random integer between 0 (inclusive) and N (exclusive) onto the stack.

FloatRandomize() primes the random number generator. This routine expects a seed and some RandomGenInitFlags . If the flag RGIF_USE_SEED is passed, then a developer-supplied seed will be used. Otherwise, a seed based on the timer clock will be used.

FloatRandomize() should always be called before any of the FloatRandom() routines to ensure a high degree of randomness.

The random number generation method uses the linear congruential method, an algorithm which ensures a high degree of randomness in the computation method.

Transcendental Functions

Transcendental functions are functions that cannot be constructed using normal arithmetic routines. Care must be taken with the following routines to ensure that they are operating on a valid range of values. Otherwise, the routines will return an error.

Trigonometric Routines
FloatSin(), FloatCos(), FloatTan(), FloatArcSin(), FloatArcCos(), FloatArcTan(), FloatArcTan2(), FloatSinh(), FloatCosh(), FloatTanh(), FloatArcSinh(), FloatArcCosh(), FloatArcTanh()

FloatSin(), FloatCos() , and FloatTan() perform the given operation on the contents of S1, pushing the result onto the FP stack. S1 must be expressed in radians. The original value is popped off of the stack.

FloatArcSin(), FloatArcCos() , and FloatArcTan() perform the given inverse operation on S1, pushing the result onto the FP stack. The result is given in radians. The original value is popped off of the stack.

FloatArcTan2() calculates the arc tangent given cartesian coordinates x and y . The arctangent is calculated from the x-axis through the origin to the given point. The value returned is expressed in radians, between -p (exclusive) and +p (inclusive). The original values are popped off of the stack.

FloatSinh(), FloatCosh() , and FloatTanh() perform the given hyperbolic operations on S1, pushing the result onto the FP stack. The original value is popped off of the stack.

FloatArcSinh(), FloatArcCosh() , and FloatArcTanh() perform the given inverse hyperbolic operations on S1, pushing the result onto the FP stack. The original value is popped off of the stack.

Exponential Routines
FloatExp(), FloatExponential(), FloatLg(), FloatLog(), FloatLn(), FloatLn1plusX(), FloatSqr(), FloatSqrt()

FloatExp() performs the exponentiation of e to the power of S1, pushing the result onto the FP stack. The original value is popped off of the stack.

FloatExponential() performs the exponentiation of S2 to the power of S1, pushing the result onto the FP stack. The original values are popped off of the stack.

FloatLg() performs the logarithm to the base 2 on S1, pushing the result onto the FP stack. The original value is popped off of the stack.

FloatLog() performs the logarithm to the base 10 on S1. The original value is popped off the stack.

FloatLn() performs the natural logarithm (log base e ) on S1. The original value is popped off the stack.

FloatLn1plusX() performs the natural log of (1 + S1). The original value is popped off the stack.

FloatSqr() performs the square of S1, pushing the result onto the FP stack. The original value is popped off the stack.

FloatSqrt() performs the square root of S1, pushing the result onto the FP stack. The original value is popped off the stack.

Conversion Routines

At some point 80-bit FP numbers may need to be converted into different formats, or you may need to convert these differently formatted numbers into a floating point representation. The Math Library provides for this contingency.

Conversions Between Integers and FP Numbers
FloatDwordToFloat(), FloatWordToFloat(), FloatFloatToDword()

FloatDwordToFloat() converts a passed double-word signed integer into a floating point number, and pushes that number onto the FP stack.

FloatWordToFloat() converts a passed word signed integer into a floating point number, and pushes that number onto the FP stack.

FloatFloatToDword() converts the FP number in S1 into a double-word signed integer. The FP number is converted into an integer by rounding the FP number to zero decimal places.

You can convert a FP number into a word value by using FloatFloatToDword() and just using the low word.

Conversions Between 80 bit FP Numbers and Other Floats
FloatGeos80ToIEEE64(), FloatGeos80ToIEEE32(), FloatIEEE64ToGeos80(), FloatIEEE32ToGeos80()

FloatGeos80ToIEEE64() converts a GEOS FP number (80 bits) into a 64 bit floating point number (in C, a type of double ).

FloatGeos80ToIEEE32() converts a GEOS FP number (80 bits) into a 32 bit floating point number (in C, a type of float ).

FloatIEEE64ToGeos80() converts a 64 bit FP number into a GEOS 80 bit FP number (in C, a type of long double ).

FloatIEEE32ToGeos80() converts a 32 bit FP number into a GEOS 80 bit FP number (in C, a type of long double ).

This document is a single-page version of a a multi-page document, suitable for easy printing.


Parse Library

The Parse Library was originally created to provide a parser for a spreadsheet language. However, it will also fit the needs of a programmer who wants to implement a language based on mathematical expressions.

The Parse Library takes an expression as text, converts it to an expression using tokens, and evaluates the expression. When finished, it converts the result back into text and returns it. The Parse Library recognizes a special grammar and set of expressions that include an interface to the Cell Library's data structures. Therefore, you can use the Cell and Parse Libraries together to form the basic underlying engine of a spreadsheet application.

You may want to familiarize yourself with how compilers work before you read this section. In particular, you should understand how scanners use regular expressions to translate raw text into token streams; and you should be familiar with the parsing of context-free grammars. A good book to look at is Compilers: Principles, Techniques, and Tools by Aho, Sethi, and Ullman (a.k.a. "The Red Dragon Book").

1 Parse Library Behavior
1.1 The Scanner
1.2 The Parser
1.3 Evaluator
1.4 Formatter
2 Parser Functions
2.1 Internal Functions
2.2 External Functions
3 Coding with the Parse Library
3.1 Parsing a String
3.2 Evaluating a Token Sequence
3.3 Formatting a Token Sequence

Parse Library: 1 Parse Library Behavior

The Parse Library takes a string of characters and evaluates it. In many ways, it acts like a compiler; it translates a string into tokens, evaluates the tokens, and returns the result. It can also reverse the process, translating a sequence of tokens into the equivalent text string. Finally, it can simplify a string of tokens, performing arithmetic simplifications and calling functions. The parse library provides many useful functions; furthermore, applications can define their own functions.

The different functions are separated into different parts of the parse library. The parse library contains the following basic sections:

  • Scanner
    The scanner reads a text string and converts it into a series of tokens. It does not keep track of the context of the tokens. Its behavior is partially determined by the localization settings; for example, it uses the localization setting to tell whether the decimal separator is a period, a comma, or some other character or string. It is called by the parser; it is not used independently.
  • Parser
    The parser interprets the stream of tokens returned by the scanner. It initializes the scanner and uses it to read tokens from the input strings; it also makes sure that the string of tokens is legally formatted. It does not do any type-checking.
  • Evaluator
    The evaluator simplifies a token string. It does this by replacing arithmetic expressions with their results, by making function calls, by reading current values of cells, and by replacing identifiers with their values. The result is another token string; usually this string consists of a single token (a number or string).
  • Formatter
    The formatter translates a token string into a text string. It is used to display the evaluator's output. Its behavior is influenced by the localization settings.

For example, suppose an application used the parse library to evaluate the string "(5*6)+SUM(A2:C6)". The following steps would be taken:

  1. The parser would parse the string. It would do this by calling the scanner to read tokens from the string. It would then parse the token sequence to see that it evaluated to a well-formed expression. (It would not do any simplifying or type-checking.)
  2. The evaluator would simplify the expression. It would reduce the token sequence for "(5*6)" to the single token for "30". It would then call the SUM function, passing it the specifier for the range of cells "A2:C6". The SUM function would check the type of its arguments, then perform the appropriate action (in this case, adding the values of the cells together). The SUM function would return a value (e.g., it might return 999.9). The evaluator would thus be able to simplify the entire token sequence to the single token for the number 1029.9.
  3. When the application needed to display the result, it would call the formatter. The formatter would check the localization settings, finding out what the thousands separator and decimal point character are. It would create the string "1,029.9".

Token strings are usually more compact than the corresponding text strings. There are several reasons for this; for example, cell references are much more compact, functions are specified by an ID number instead of a string, and white space is removed. When translated into a token string, it is only three bytes long: one token byte to specify that this is a number, and two data bytes to store the value of the number. For this reason, applications which use the parse library will generally not store the text entered by the user; instead, they can store the equivalent token string, and use the formatter to display the string when necessary.

The parse library routines often need to request information from the calling application or instruct it to perform a task. For example, when the Parser encounters a name, it needs to get a name ID from the calling application. For this reason, every Parse Library routine is passed a callback routine. The library routine calls this callback routine when necessary, passing a code indicating what action the callback routine should take. The beginning section will just describe this in general terms; for example, "the Evaluator uses the callback to find out the value of a cell." The advanced section provides a more detailed explanation.

The Scanner

The Parser

Evaluator

Formatter

Parse Library: 1.1 Parse Library Behavior: The Scanner

The scanner translates a text string into a sequence of tokens. The tokens can then be processed by the parser. Every token is associated with some data.

The scanner can be treated as a part of the parser. It is never used independently; instead, the parser is called on to parse a string, and the parser calls the scanner to translate the string into tokens.

The scanner does not keep track of tokens after it processes them. For this reason, it will not notice if, for example, parentheses are not balanced. It returns errors only if it is passed a string which does not scan as a sequence of tokens.

Scanner Tokens

The scanner recognizes the tokens listed below. Note that applications will never directly encounter the scanner tokens; the tokens translates them into parser tokens before returning them. A complete list of parser tokens (with their names) is given in Parser Tokens .

NUMBER
This is some kind of numerical constant. The format in the string is determined by the localization settings. The data section of the token is a floating-point number (even if the string contained an integer).
STRING
This is a sequence of characters surrounded by "double-quotes." All characters within double quotes are translated into their ASCII equivalents, with the exceptions noted below in Strings . The data section is a pointer to the ASCII string specified.
CELL
This is a reference to a cell in a database. The format is described in Cell References .
END_OF_EXPRESSION
The scanner returns this token when it has examined and translated an entire text string and reached its end.
OPEN_PAREN
This is simply a left parenthesis character, i.e. "(". There is no data section associated with this token.
CLOSE_PAREN
This is simply a right parenthesis character, i.e. ")". There is no data section associated with this token.
OPERATOR
This is a unary or binary operator. The operators are described in Operators . The data section specifies which operator was encountered.
LIST_SEPARATOR
This is a comma, i.e. ",". It is used to separate arguments to functions. There is no data section associated with this token.
IDENTIFIER
This is a sequence of characters, not in quotation marks, which does not match the format for cell references. Identifiers may be functions (built-in or application-defined) or variables; see Identifiers . The data section is a string containing the identifier.

Strings

The string passed to the scanner may, itself, contain strings. These inner strings are not further analyzed; rather, their contents are associated with the string token. Strings are delimited by double-quotes. All characters within the double-quotes are copied directly into the token's data, with the exception of the backslash, i.e. "\". This character signals that the character (or characters) which immediately follow it are to be interpreted literally. Backslash-codes include the following:

\"
This code represents a double-quote character (i.e. ASCII 0x22, or "); it indicates that the double-quote should be copied into the string, instead of read as a string delimiter.
\n
This code represents a newline control code (i.e. ASCII 0x0A, or control-J).
\t
This code represents a hard-tab control code (i.e. ASCII 0x09, or control-I).
\f
This code represents a form-feed control code (i.e. ASCII 0x0C, or control-L).
\b
This code represents a backspace control code (i.e. ASCII 0x10, or control-H).
\\
This code represents a backslash character (i.e. ASCII 0x5C, or "\").
\ nnn
This code is a literal octal value. The backslash must be followed by three digits, making up an octal integer in the range 0-177o (i.e. 0-255). The byte specified is inserted directly into the string. Thus, for example, "\134" is functionally identical to "\\".

Cell References

The parse library is often used in conjunction with cell files; for example, the spreadsheet objects use the two libraries together. For this reason, the scanner recognizes cell references. Cell references are described by the regular expression [A-Z]+[0-9]+; that is, one or more capital letters, followed by one or more digits. The capital letters indicate the cell's column. The first column (the column with index 0) is indicated by the letter A; column 1 is B, column 2 is C, and so on, up to column 25 (which is Z). Column 26 is AA, followed by AB, AC, and so on to AZ (column 51); this column is followed by BA, and so on, to the largest column, IV (column 255). The rows are indicated by number, with the first row having number 1.

The data portion of a cell reference token is a CellReference structure. This structure records the row and column indices of the cell; the scanner translates the cell reference to these indices. For more information about the cell library, see the Cell Library chapter.

When the evaluator needs to get the value of a cell, it calls a callback routine, passing the cell's CellReference structure. The application is responsible for looking up the cell's value and returning it to the evaluator. If you manage a cell file with a Spreadsheet object, this work is done for you; the Spreadsheet will be called by the evaluator, returning the values of cells as needed. (The spreadsheet returns zero for empty or unallocated cells.)

Note that while the cell library numbers both rows and columns starting from zero, the Parse library numbers rows starting from one. This is because historically, spreadsheets have had the first row be row number 1. Therefore, if the parser encounters a reference to cell A1, it will translate this into a cell reference which specifies row zero, column zero.

Operators

The scanner recognizes a number of built-in operators. Neither the scanner nor the parser does any simplification or evaluation of operator expressions; this is done by the evaluator. All operators are represented by the token SCANNER_TOKEN_OPERATOR. The token has a one-byte data section, which is a member of the enumerated type OperatorType ; this value specifies which operator was encountered. This section begins with a listing of currently supported operators in order of precedence, from highest precedence to lowest; this is followed by a detailed description of the operators. All operators listed here will always be supported; other operators may be added in the future.

Note that neither the scanner nor the parser does any evaluation of arguments. All type-checking is done at evaluation time. Thus, if parse the text "(3 * "HELLO")", the parser will not complain; the evaluator, however, will return a "bad argument type" error.

The figure below lists the operators in order of precedence. Highest-precedence operators are listed first. Operators with the same precedence are listed together; a blank line implies a drop in precedence. Operators of the same precedence level are grouped from left to right; that is, "1 - 2 - 3" is the same as "(1 - 2) - 3".

:
This is a range separator. The range separator is a binary infix operator. The parser recognizes expressions of the format Cell1 : Cell2 as describing a rectangular range of cells, with the two specified cells being diagonally opposite corners. The data portion of this token is the constant OP_RANGE_SEPARATOR.
...
This is another range separator. It is functionally identical to the colon operator. The data portion of this token is the constant OP_RANGE_SEPARATOR. (The formatter will turn this back into a colon.)
-
This can be either of two different operators. It can be a negation operator. This is a unary prefix operator which reverses the arithmetic sign of the operand. It can also be a subtraction operator. This is a binary infix operator. The parser determines which operator is represented. For example, in "(-1)", the hyphen is a negation operator; in "(1-2)", it is a subtraction operator. The data portion of this token is either OP_NEGATION or OP_SUBTRACTION; the scanner assigns the neutral OP_SUBTRACTION_NEGATION, and the parser decides (from context) which value is appropriate.
%
This can be either of two operators. It can be a percent operator. This is a unary postfix operator which divides its operand by 100; that is, "50%" evaluates to 0.5. It can also be a modulo arithmetic operator. This is a binary infix operator which returns the remainder when its first operand is divided by its second operand; that is, "11%4" evaluates to 3.0. The parser determines which operator is represented. The data portion of this token is either OP_PERCENT or OP_MODULO; the scanner assigns the neutral OP_PERCENT_MODULO, and the parser decides (from context) which constant is appropriate.
^
This is the exponentiation operator. It is a binary infix operator; it raises its first operand to the power of the second operand (e.g. "2^3" evaluates to 8.0). The data portion of this token is the constant OP_RANGE_EXPONENTIATION.
*
This is the multiplication operator. It is a binary infix operator. It multiplies the two operands. The data portion of this token is the constant OP_MULTIPLICATION.
/
This is the division operator. It is a binary infix operator. It divides the first operand by the second. The data portion of this token is the constant OP_ DIVISION. The constant OP_DIVISION_GRAPHIC is functionally equivalent; however, the formatter will display the operator as "".
+
This is the addition operator. It is a binary infix operator. It adds the two operands. The data portion of this token is the constant OP_RANGE_ADDITION.

Several Boolean operators are also provided. In every case, if a Boolean expression is true, it evaluates to 1.0; if it is false, it evaluates to 0.0. (There is no Boolean negation operator; however, there is a Boolean negation function, NOT, which returns 1.0 if its argument is zero, and otherwise returns zero.) Boolean operators may be used for numbers or strings. They work in the conventional way for numbers. Strings are "equal" if they are identical. One string is said to be "less than" another if it comes first in lexical order. The parse library uses localized string comparison routines to compare strings; thus, the local lexical ordering is automatically used. (For more information, see the Localization chapter.)

=
This is the equality operator. It is a binary infix operator. An expression evaluates to 1.0 if both operands evaluate to identical values. The data portion of this token is the constant OP_EQUAL.
<>
This is the inequality operator. It is a binary infix operator. An expression evaluates to 1.0 if the two operands evaluate to different values. The data portion of this token is OP_NOT_EQUAL. The constant OP_NOT_EQUAL_GRAPHIC is functionally equivalent; however, the formatter will display the operator as "".
>
This is the "greater-than" operator. It is a binary infix operator. It returns 1.0 if the first operand evaluates to a larger number than the second operand. The data portion of this token is OP_GREATER_THAN.
<
This is the "less-than" operator. It is a binary infix operator. It returns 1.0 if the first operand evaluates to a smaller number than the second operand. The data portion of this token is OP_LESS_THAN.
>=
This is the "greater-than-or-equal-to" operator. It is a binary infix operator. It returns 1.0 if the first operator evaluates to a number that is greater than or equal to the value of the second operand. The data portion of this token is OP_GREATER_THAN_OR_EQUAL. The constant OP_GREATER_THAN_OR_EQUAL_GRAPHIC is functionally equivalent; however, the formatter will display the operator as "".
<=
This is the "less-than-or-equal-to" operator. It is a binary infix operator. It returns 1.0 if the first operator evaluates to a number that is less than or equal to the value of the second operand. The data portion of this token is OP_LESS_THAN_OR_EQUAL. The constant OP_LESS_THAN_OR_EQUAL_GRAPHIC is functionally equivalent; however, the formatter will display the operator as "£".

Some special-purpose operators are also provided:

&
This is the "string-concatenation" operator. It is a binary infix operator. The arguments must be strings. The result is a single string, composed of all the characters of the first string (without its null terminator) followed by all the characters of the second string (with its null terminator); for example, ("Franklin" & "Poomm") evaluates to "FranklinPoomm". The data portion of this token is OP_STRING_CONCAT.
#
This is the "range-intersection" operator. It is a binary infix operator. Both arguments must be cell ranges. The result is the range of cells which falls in both of the operand cell ranges. Note that cell ranges must be rectangular; there is, therefore, no "range-union" operator. The data portion of this token is OP_RANGE_INTERSECTION.

Identifiers

Any unbroken alphanumeric character sequence which does not appear in quotes, and which is not in the format for a cell reference, is presumed to be an identifier. Identifiers serve two roles: they may be function names, or they may be labels.

The scanner merely notes that an identifier has been found; it does not take any other action. The parser will find out what the identifier signifies. If the identifier's position indicates that it is a function (but the name is not that of a built-in function), the parser will prompt its caller for a pointer to a callback routine which will perform this function. If its position indicates that it is an identifier, the parser will request the value associated with the identifier; this may be a string, a number, or a cell reference.

Parse Library: 1.2 Parse Library Behavior: The Parser

Applications will never call the scanner directly. Instead, if they access the parse library directly (instead of through the spreadsheet objects), they will call the parser and pass it a string, and the parser will in turn call the scanner to process the string into tokens. This section will not discuss how to call the parser, since few applications will need to do that; it will instead describe the general workings of the parser.

The parser translates a well-formed string into a sequence of tokens. It calls the scanner to read tokens from the string. It then uses a context-free grammar to make sure the string is well formed. The context-free grammar is described below. The scanner outputs a sequence of parser tokens. The parser tokens are almost identical to the scanner tokens, with a few exceptions; those exceptions are noted below.

The parser is passed a callback routine. The parser calls this routine when it needs information about a token; for example, if it encounters a function it does not recognize, it calls the callback to get a pointer to the function. The details of this are provided in the advanced section.

If the parser is not passed a well-formed expression, or if it is unable to successfully parse the string for some other reason, it returns an error code. The error codes are described at length in the advanced section.

The Parser's Grammar

The parser uses a context-free grammar to make sure the string is well-formed. The grammar is listed below. The basic units of the grammar are listed in ALL-CAPS; higher-level units are listed in italics . The string must parse to a well-formed expression .

expression :
'(' expression ') '
 NEG_OP expression
 IDENTIFIER '(' function_args ')'
base_item more_expression
more_expression :
<empty>
PERCENT_OP more_expression
 BINARY_OP expression
function_args :
<empty>
arg_list
arg_list :
expression
expression ',' arg_list
base_item : NUMBER
STRING
CELL_REF
IDENTIFIER

Parser Tokens

The parser does not return scanner tokens; instead, it returns a sequence of parser tokens. The parser tokens are almost directly analogous to the scanner tokens. However, a few additional token types are added.

The parser tokens have the same structure as the scanner tokens. The first field is a constant specifying what type of token this is. The second field contains specific information about the token; this field may be blank. The parser has the following types of tokens:

PARSER_TOKEN_NUMBER
This is the same as the scanner NUMBER token.
PARSER_TOKEN_STRING
This is the same as the scanner STRING token.
PARSER_TOKEN_CELL
This is the same as the scanner CELL token.
PARSER_TOKEN_END_OF_EXPRESSION
This is the same as the scanner END_OF_EXPRESSION token.
PARSER_TOKEN_OPEN_PAREN
This usually replaces the scanner OPEN_PAREN token. However, it is not used if the parenthesis is delimiting function arguments; it is only used if the parenthesis is changing the order of evaluation.
PARSER_TOKEN_CLOSE_PAREN
This usually replaces the scanner CLOSE_PAREN token. However, it is not used if the parenthesis is delimiting function arguments; it is only used if the parenthesis is changing the order of evaluation.
PARSER_TOKEN_NAME
This replaces some occurrences of the scanner IDENTIFIER token; specifically, those where the identifier is not a function name. The data portion is the number for that name.
PARSER_TOKEN_FUNCTION
This replaces some occurrences of the scanner IDENTIFIER token, specifically those in which the identifier is a function name. The data portion is the function ID number.
PARSER_TOKEN_CLOSE_FUNCTION
This replaces some occurrences of the scanner CLOSE_PAREN token; specifically, those where the closing parenthesis delimits function arguments.
PARSER_TOKEN_ARG_END
The parser inserts this token after every argument to a function call; thus, it replaces occurrences of SCANNER_TOKEN_LIST_SEPARATOR, and also occurs after the last argument to a function.
PARSER_TOKEN_OPERATOR
This is the same as the parser's OPERATOR token. The data section is an operator constant, as described above in Operators . Note the parser replaces occurrences of OP_PERCENT_MODULO with either OP_PERCENT or OP_MODULO, as appropriate; similarly, it replaces OP_SUBTRACTION_NEGATION with either OP_SUBTRACTION or OP_NEGATION.

When the parser encounters an identifier that is in the appropriate place for a function name (that is, an identifier followed by a parenthesized argument list), it does not write an identifier token. Instead, it writes a "function" token, which has a one-word data section. This section is the function ID (described in Parser Functions ). If the function's name is not one of a built-in function, it will call the application's callback routine to find out what the function's ID number is; the evaluator will pass this ID when it needs to have the function called.

When the parser encounters an identifier, it asks its caller for an ID number for the identifier. It can then store the ID number instead of the entire string. The evaluator will use this ID number when requesting the value of the identifier. The formatter will use the ID number when requesting the original identifier string associated with the ID number.

When the parser encounters a scanner parenthesis token, it does not necessarily translate it into a parser parenthesis token. This is because parentheses fulfill two separate roles: they specify the order of evaluation, and they delimit function arguments. When the parser encounters parenthesis tokens which specify order of evaluation, it translates them into parser parenthesis tokens. If, however, it encounters argument-delimiting parentheses, it does not need to translate them literally; after all, the presence of a function token implies that it will be followed by an argument list. Thus, the parser does not need to copy the parenthesis tokens. Instead, it copies the tokens of the argument list. When it reaches a list separator, it replaces that with an "end-of-argument" token; when it reaches the closing parenthesis for the function call, it replaces that with a "close-function" token.

An Example of Scanning and Parsing

Suppose that you call the parser on the text string "3 + SUM(6.5, 3 ^ (4 - 1), C5...F9)". The parser will evaluate the string, one token at a time. When it needs to process a token, it will call the scanner to return the next token in the string. It will then replace these tokens with parser tokens, and write out the sequence of tokens to its output buffer.

For simplicity, this example treats the scanner as if it scanned the entire text stream at once, and returned the entire sequence of tokens to the scanner. In this case, the scanner would translate the text into the following sequence of tokens:

Token		Data			Comment
NUMBER		3.0			All numbers are floats
OPERATOR		OP_ADDITION
IDENTIFIER		"SUM"
OPEN_PAREN					delimits function args
NUMBER		6.5
LIST_SEPARATOR
NUMBER		3.0
OPERATOR		OP_EXPONENTIATION
OPEN_PAREN
NUMBER		4.0
OPERATOR		OP_SUBTRACTION_NEGATION
                    Parser figures out
                    which operator this is
NUMBER		1.0
CLOSE_PAREN
LIST_SEPARATOR
CELL		C5			Actually stored as
                     "4,2"; row index 4,
                    column index 2
OPERATOR		OP_RANGE_SEPARATOR
CELL		F9
CLOSE_PAREN
END_OF_EXPRESSION

The parser reads these tokens, one at a time, and writes out an analogous sequence of parser tokens:

Token		Data			Comment
NUMBER		3.0			All numbers are floats
OPERATOR		OP_ADDITION
FUNCTION		FUNCTION_ID_SUM
NUMBER		6.5
END_OF_ARG
NUMBER		3.0
OPERATOR		OP_EXPONENTIATION
OPEN_PAREN
NUMBER		4.0
OPERATOR		OP_SUBTRACTION
NUMBER		1.0
CLOSE_PAREN
END_OF_ARG
CELL		C5			Actually stored as
                     "4,2"; row index 4,
                    column index 2
OPERATOR		OP_RANGE_SEPARATOR
CELL		F9
END_OF_ARG
CLOSE_FUNCTION
END_OF_EXPRESSION

The application does not need to save the original text string. Instead, it can save the buffer containing the parser tokens, and use the formatter to translate the token sequence back into a character string.

Parse Library: 1.3 Parse Library Behavior: Evaluator

The evaluator simplifies a token string returned by the parser. If the input token sequence was well-formed (as are all token sequences generated by the Parser), the evaluator will produce a token sequence consisting of two tokens: a single "result" token (which may be an error token), followed by the "end-of-expression" token. It does this by doing two main things: simplifying arithmetic expressions, and making function calls.

The evaluator maintains two stacks, an Operator stack and an Argument stack. It reads the tokens from beginning to end. Each time it reads a token, it takes an action; this may involve pushing something onto a stack, or processing some of the tokens on the tops of the stacks.

If an error occurs, the parser may take two different actions. Some errors are pushed on the argument stack; these may be handled by functions. For example, if the result of an expression is too large to be represented, the evaluator will just push PSEE_FLOAT_POS_INFINITY on the argument stack. Any function or operator which is passed an error code as an argument can either handle the error, propagate the error, or return a different error. For example, if the division operator is passed PSEE_FLOAT_POS_INFINITY as the divisor, it will simply return zero.

The actual evaluation of tokens is straightforward. The evaluator pops the top token from the Operator stack. This is either a function or an operator. If the token is an operator, the evaluator pops either one or two arguments from the top of the argument stack, takes the appropriate action, and pushes the result on the argument stack. If the token is a function, the evaluator calls the function directly, passing it a pointer to the argument stack and the number of arguments to the function call. The function is responsible for popping off all of the arguments and pushing the return value on the argument stack.

Special actions have to be taken if an operand or argument is a cell reference. If the cell is an argument to a function, or an operand (and the operator is not a range-separator or range-intersection), the evaluator will call its callback routine to get the value contained by the cell; this value will be put on the argument stack in place of the cell reference. If the operand is a range-separator or range-intersection, the cells or ranges will be combined into a single range, which is pushed on the Argument stack.

The evaluator reads tokens, one at a time, from the buffer provided by the parser. For each token it takes an appropriate action:

OPEN_PAREN
Push an OPEN_PAREN token on the Operator stack.
CLOSE_PAREN
Evaluate tokens from the Operator stack until an OPEN_PAREN reaches the top of the operator stack; then pop the OPEN_PAREN off the stack.
OPERATOR
If the top token on the Operator stack is an OPERATOR of higher precedence than this OPERATOR, then evaluate top of Operator stack. Repeat until top of operator stack is either not an operator, or is an operator of lower precedence. Finally, push the operator token on the operator stack.
FUNCTION
Push FUNCTION token on the Operator stack. The evaluator FUNCTION token contains the function ID and the number of arguments to the function (starting at zero).
CLOSE_FUNCTION
Call function on top of Operator stack, passing it the pointer to the Argument stack and the number of arguments to the function call. (The arguments will be on the top of the argument stack.) The function should pop the arguments off the Argument stack, then push the return value (or error code) on the Argument stack.
ARG_END
Evaluate the Operator stack until a FUNCTION token is at the top of the Operator stack; then increment the argument count of that function.
NUMBER
Push number on Argument stack. (Actually, what is pushed is a reference to the thread's floating-point stack, which contains the number itself.)
STRING
Push string on Argument stack.
CELL_REF
Push the cell reference on the Argument stack.
NAME
Call the callback function to find the value associated with the name; act on the value appropriately.
END_OF_EXPRESSION
Evaluate the Operator stack until it is empty; the result will be on the top of the Argument stack.

Parse Library: 1.4 Parse Library Behavior: Formatter

In order to display a token sequence, you must call the Formatter. The formatter is very straightforward. It is passed a buffer containing a token sequence; it returns a character array containing the result. The formatter makes use of the localization routines to format the result according to the local language and the user's Preferences settings.

If the token sequence consists of an error token, the formatter will generate an appropriate error string.

Parse Library: 2 Parser Functions

The Parse library provides many built-in functions. Furthermore, each application can define its own functions. Every function is associated with a function ID number. Built-in functions have ID numbers assigned to them in the library code; application-defined functions are given ID numbers by the application. ID numbers are word-sized unsigned integers. All built-in ("internal") functions have ID numbers which are less than the constant FUNCTION_ID_FIRST_EXTERNAL_FUNCTION_BASE; all application-defined ("external") functions have ID numbers which are greater than this constant.

When the Parser reads an identifier token whose position indicates that it is a function, it converts the identifier to a function token (containing a function ID). The parser first checks to see if the identifier is the name of a built-in function. If so, it looks up the function's ID number and stores it in the function token.

If the identifier is not the name of a built-in function, the Parser calls the application's callback routine to get the function's ID number. The application must assign each function a word-sized ID which is greater than or equal to the constant FUNCTION_ID_FIRST_EXTERNAL_FUNCTION_BASE. This constant is defined as 0x8000.

When the Evaluator needs to evaluate a function, it checks to see if the function is external or internal. If the function is internal, it looks up the functions address and calls it. If the function is external, it calls the application's callback routine and passes the function ID. In either case, it passes a pointer to the argument stack and the number of arguments. The function is responsible for popping all the arguments off the stack and pushing the result. It can also push an error message on the stack. All of this is discussed at length in the advanced section.

Internal Functions

External Functions

Parse Library: 2.1 Parser Functions: Internal Functions

The Parse library provides many internal functions. Any application which uses the parse library automatically makes use of these functions. Some of these functions take a single argument; others take a set number of arguments or a variable number.

A listing of currently available functions follows, along with a short description of each one, and a parenthetical description of the function's arguments.

 ABS		Absolute value (One numeric argument)
 ACOS		Arc-cosine (One numeric argument)
 ACOSH		Hyperbolic arc-cosine (One numeric
        argument)
 AND		Boolean AND (Either a list of numbers
        or a cell range--there must be at least
        two numbers)
 ASIN		Arc-sine (One numeric argument)
 ASINH		Hyperbolic arc-sine (One numeric
        argument)
 ATAN		Arc-tangent (One numeric argument)
 ATAN2		Four-quadrant arc-tangent (XXX)
 ATANH		Hyperbolic arc-tangent (One numeric
        argument)
 AVG		Average of arguments (Either a list of
        numbers 	or a cell range--there must be
        at least 	two numbers)
 CHAR		Translates character-set code into
        character (One Chars argument)
 CHOOSE		Finds value in list at specified offset
        (XXX)
 CLEAN		Removes control characters from a string
        (One string argument)
 CODE		Translates character into character-set
        code (One string argument (first
        character will be converted))
 COLS		Returns # of columns in range (XXX)
 COS		Cosine (One numeric argument)
 COSH		Hyperbolic cosine (One numeric argument)
COUNT		Returns number of items in list (Any
        number of arguments)
 CTERM		Returns time for an investment to reach
        a specified value (XXX)
 DDB		Depreciation over a period (XXX)
DEGREES		Converts radians to degrees (XXX)
 ERR		Returns error PSEE_GEN_ERR (XXX)
 EXACT		Tests if two strings match (Two string
        arguments)
 EXP		Exponentiation (One numeric argument)
 FACT		Factorial (XXX)
 FALSE		Returns false (0.0) (XXX)
 FIND		Returns position in string where
        substring first occurs (Three
        arguments: string to find, string to
        search, starting offset (0 to start
        with the first character))
 FV		Future value of investment (XXX)
 HLOOKUP		Finds a value in a horizontal lookup
        table (XXX)
 IF		IF(<cond>,x,y) = x if <cond> is true,
        else y (like C's "<cond> ? x : y") (XXX)
 INDEX		Finds value at specified offset in a
        range (XXX)
 INT		Rounds to next lowest integer (One
        numeric argument)
 IRR		Internal rate of return (XXX)
 ISERR		True if argument is error (XXX)
 ISNUMBER		True if argument is number (XXX)
 ISSTRING		True if argument is string (XXX)
 LEFT		Returns first characters in string (Two
        arguments: string, number of letters)
 LENGTH		Returns length of string (One string
        argument)
 LN		Natural log (One numeric argument)
 LOG		Log to base 10 (One numeric argument)
 LOWER		Converts string to all-lowercase (One
        string argument)
 MAX		Returns largest of arguments (XXX)
 MID		Returns characters from middle of
        string (Three arguments: string,
        starting offset (0 is first character),
        desired string length)
 MIN		Returns smallest of arguments (XXX)
 MOD		Modulo arithmetic (XXX)
 N		Returns value of first cell in range
        (XXX)
 NA		Returns error PSEE_NA (XXX)
 NPV		Returns net present value of future
        cash flows (XXX)
 OR		Boolean OR (XXX)
 PI		Returns 3.1415926... (XXX)
 PMT		Calculates # of payments to pay off a
        debt (XXX)
 PRODUCT		Returns product of arguments (XXX)
 PROPER		Converts string to "Proper
        Capitalization" (One string argument)
 PV		Calculates present value of an
        investment (XXX)
 RADIANS		Converts degrees to radians (XXX)
RANDOM		Generates random number between 0 and 1
        (XXX)
 RANDOMN		Generates random integer below a
        specified ceiling (XXX)
 RATE		Calculates interest rate needed for
        investment to reach specified value
        (XXX)
 REPEAT		Returns string made of repeated
        argument string (Two arguments: string
        to repeat, number of times to repeat)
 REPLACE		Replaces characters in a string (Four
        arguments: original string, offset of
        first character to replace (0 is the
        first character), number of characters
        to replace, replacement string)
 RIGHT		Returns last characters in a string
        (Two arguments: original string,
        desired length of returned string)
 ROUND		Rounds number to specified precision
        (XXX)
 ROWS		Returns number of rows in range (XXX)
 SIN		Sine (One numeric argument)
 SINH		Hyperbolic-sine (One numeric argument)
 SLN		Calculates straight-line depreciation
        (XXX)
 SQRT		Square-root (One numeric argument)
 STD		Calculates standard deviation (XXX)
 STDP		Standard deviation of entire population
        (XXX)
 STRING		Converts number into string (Two
        numeric arguments: number to convert,
        number of decimal places (between 0 and
        DECIMAL_PRECISION))
 SUM		Returns sum of arguments (XXX)
 SYD		Sum-of-years'-digits depreciation (XXX)
 TAN		Tangent (sine/cosine) (One numeric
        argument)
 TANH		Hyperbolic tangent (sinh/cosh) (One
        numeric argument)
 TERM		Returns number of payments needed to
        reach future value (XXX)
 TRIM		Removes leading, trailing, and
        consecutive spaces from a string (One
        string argument)
 TRUE		Returns TRUE (1.0) (XXX)
 TRUNC		Removes fractional part; rounds towards
        zero (One numeric argument)
 UPPER		Converts all letters in string to
        uppercase (One string argument)
 VALUE		Converts string to number (One string
        argument)

Parse Library: 2.2 Parser Functions: External Functions

Applications which use the Parse library may write their own functions. Whenever the formatter encounters a function name which it does not recognize, it calls the application to get an ID for the function. When the evaluator needs to evaluate that function, it calls the application, passing the arguments and the function ID. The application should return a single value. If it cannot produce a value, it should return an error code. The error codes are described in Evaluating a Token Sequence .

Parse Library: 3 Coding with the Parse Library

This section describes how to use the Parser directly, instead of using intermediaries (like the Spreadsheet library). Most applications will not need to use these routines.

Parsing a String

Evaluating a Token Sequence

Formatting a Token Sequence

Parse Library: 3.1 Coding with the Parse Library: Parsing a String

ParserParseString()

To parse a string, call ParserParseString() . This routine takes four arguments:

  • a pointer to a null-terminated string
  • a pointer to a buffer
  • a pointer to a CParserStruct (This structure contains a pointer to a callback routine.)
  • a pointer to a CParserReturnStruct

ParserParseString() parses a string into a sequence of tokens and writes the tokens to the buffer. Whenever the parser encounters an identifier, it calls the callback routine and requests an ID number for the identifier. Similarly, when the parser encounters a function whose name it does not recognize, it calls the callback routine to get a function ID number. The ID numbers are stored in the token sequence. Note: it is best to parse strings that contain known function names and identifiers.

The Parser can return the following errors:

PSEE_BAD_NUMBER
The string contained a badly-formatted number.
PSEE_BAD_CELL_REFERENCE
The string contained a badly-formatted cell reference.
PSEE_NO_CLOSE_QUOTE
The string contained an opening quote with no matching closing quote.
PSEE_COLUMN_TOO_LARGE
The string contained a cell whose column index was out of bounds (greater than 255).
PSEE_ROW_TOO_LARGE
The string contained a cell whose row index was out of bounds.
PSEE_ILLEGAL_TOKEN
The string contained a character sequence which was not a legal token.
PSEE_TOO_MANY_TOKENS
The expression was too complex.
PSEE_EXPECTED_OPEN_PAREN
A function call lacked an open-parenthesis.
PSEE_EXPECTED_CLOSE_PAREN
A function call lacked a close-parenthesis.
PSEE_BAD_EXPRESSION
The string contained a badly-formed expression.
PSEE_EXPECTED_END_OF_EXPRESSION
An expression ended improperly.
PSEE_MISSING_CLOSE_PAREN
Parentheses were mismatched.
PSEE_UNKNOWN_IDENTIFIER
An identifier or external function name was encountered, and the callback routine would not provide an ID for it.
PSEE_GENERAL
General parser error.

Parse Library: 3.2 Coding with the Parse Library: Evaluating a Token Sequence

ParserEvalExpression()

To format an expression, call ParserEvalExpression() . This routine is passed a token sequence; it evaluates it and writes the resulting token sequence to a passed buffer. It calls a supplied callback routine to perform the following tasks:

  • Return the value of a specified cell
  • Return the value associated with a given identifier, specified by ID number
  • Evaluate an external function, given the arguments and the function ID number

The evaluator produces a sequence two tokens long, including the "end-of-expression" token. The first token might be an error token. Two errors are so serious that if they occur, the evaluation is immediately halted and the error is returned:

PSEE_OUT_OF_STACK_SPACE
The evaluator ran out of stack space. Evaluation was halted when this occurred.
PSEE_NESTING_TOO_DEEP
The nesting grew too deep for the evaluator. Evaluation was halted when this occurred.

The following errors may be propagated; that is, if an expression returns an error, that error would be passed, as a value, to outer expressions. For example, if the evaluator were evaluating "SUM(1, (PROD(1, 2, "F. T. Poomm"))", PROD would return PSEE_WRONG_TYPE, since it expects numeric arguments. SUM, in turn, would be passed two arguments: the number 1 and the error PSEE_WRONG_TYPE. That function might, in turn, propagate the error upward, return a different error, or return a non-error value. (SUM, as it happens, would propagate the error; that is, it would return PSEE_WRONG_TYPE.)

PSEE_ROW_OUT_OF_RANGE
A cell's row index was out of range.
PSEE_COLUMN_OUT_OF_RANGE
A cell's column index was out of range.
PSEE_FUNCTION_NO_LONGER_EXISTS
The callback routine did not recognize the function ID for an external function.
PSEE_BAD_ARG_COUNT
A function was passed the wrong number of arguments.
PSEE_WRONG_TYPE
A function was passed an argument of the wrong type.
PSEE_DIVIDE_BY_ZERO
A division by zero was attempted.
PSEE_UNDEFINED_NAME
The callback would not provide a value for an identifier ID.
PSEE_CIRCULAR_REF
A circular reference occurred. This error will only occur if it is returned by the callback routine.
PSEE_CIRCULAR_DEP
The value is dependant on a cell whose value is PSEE_CIRCULAR_REF.
PSEE_CIRC_NAME_REF
The expression uses a name which is defined circularly.
PSEE_NUMBER_OUT_OF_RANGE
The result was a number which could not be expressed as a float.
PSEE_GEN_ERR
General error; this is returned when no other error code is appropriate.
PSEE_NA
The value for a cell was not available.
PSEE_FLOAT_POS_INFINITY
A float routine returned the error FLOAT_POS_INFINITY.
PSEE_FLOAT_NEG_INFINITY
A float routine returned the error FLOAT_NEG_INFINITY.
PSEE_FLOAT_GEN_ERR
A float routine returned the error FLOAT_GEN_ERR.
PSEE_TOO_MANY_DEPENDENCIES
The formula contained too many levels of dependency. This is generally returned by the callback routine; the Parse library routines do not return this error, they merely propagate it.

The application may also define its own error codes, beginning with the constant PSEE_FIRST_APPLICATION_ERROR. All internal functions, and all operators, always propagate application-defined errors.

Parse Library: 3.3 Coding with the Parse Library: Formatting a Token Sequence

ParserFormatExpression()

The routine ParserFormatExpression() is passed a token buffer; it returns a character string. The formatter uses the localization routines to format numbers. The formatter also formats error codes as appropriate error messages. These error messages are stored in a localizable resource, so the formatter library will produce error messages in the appropriate language.

If the formatter encounters a token ID or an external function ID, it will call the callback routine to find out what character sequence is associated with that ID number. If it encounters an application-defined error code, it will request an appropriate error string. Applications should store these error strings in localizable resources; this will simplify translating the application into another language.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Ruler Object Library

The ruler library provides objects that act as rulers. These rulers may be linked up with a GenView object so that when the user scrolls around within the GenView, the rulers update accordingly. The rulers work with a variety of measurement systems and also provide grids and guidelines to help the user to figure out the spacial layout of a graphic space. To aid the user's mouse movement, the rulers can constrain the mouse's movement to certain lines.

To work with the basic ruler set-up, you don't need to know much about GEOS other than the basics of objects and messaging. However, for certain customizations, you will probably need to know something about GenViews and notifications they pass on to linked objects.

1 Ruler Features
2 Ruler Setup
3 VisRuler Instance Data
4 Managing Rulers
4.1 RulerShowControl
4.2 Mouse Tracking
4.3 Grid Spacing and Constraint
4.4 Guide Constraints and Guidelines
4.5 Other Mouse Constraints
4.6 Esoteric Messages

Ruler Object Library: 1 Ruler Features

Rulers are a "must have" feature for drawing programs or any geode concerned with layout-intensive tasks. By alerting the rulers to pointer events, the application may display marks in the ruler which track the mouse's position so that the user knows the exact mouse coordinates. The ruler library provides many more features to applications prepared to support those features:

  • Grids
    The ruler's tick marks are of course drawn inside the ruler. However, the ruler can draw a grid over the main view so that the user may see where major units fall on the document directly.
  • Guides
    While a regular grid is helpful, the user may want to set up guidelines to mark off areas important to a document's layout. These guides may appear both as marks on the ruler and lines across the main view itself.
  • Mouse Constraints
    The rulers can act as mouse constraint managers. The mouse location can snap to grid lines, guidelines, or lines at any angle through any point.
  • Large Document Support
    The rulers work with large (32-bit) documents. They respond correctly to large mouse and scrolling events. However, at this time there is no way using C to intercept the message by which the Ruler handles large pointer events.
  • Controller Support
    The ruler library includes a full suite of controller objects, including controllers for managing grids and guides.

Ruler Object Library: 2 Ruler Setup

For each ruler a geode displays, it will need to include three objects: an object of RulerViewClass to display the ruler, a RulerContentClass object to act as the top of the RulerView's visible tree, and a VisRulerClass object, the ruler itself. Also, the geode may wish to include some of the available ruler control objects.

The RulerViews should be alerted when the main GenView scrolls or scales, so set the GenView's GVI_horizLink and GVI_vertLinks to hold the optrs of the RulerViews. A RulerView to the side of the GenView should be linked by the horizontal link; a RulerView above or below the GenView should be linked vertically.

Each RulerView should have a RulerContent as its content. Set this up as you would a normal GenView/VisContent linkage. The RulerContent functions as a VisContent for most purposes; there is some subclassed behavior so that the VisRuler will be notified when the RulerView has detected a scroll or scale event.

A VisRuler should be the child of the RulerContent. If you have more than one VisRuler associated with a view, you should use the VRI_slave links to connect them. When one VisRuler receives certain messages, it will handle them and pass them on to whatever VisRuler has been designated in its VRI_slave field. If this slave ruler itself has a slave, then the message will be relayed again, and so on. Thus the application only has to send messages to one ruler, instead of to all of them. The ruler which is not the target of a VRI_slave link is the view's master ruler, and its VRA_MASTER bit should be set.

If the view only has one ruler associated with it, that ruler's VRA_MASTER bit should be set.

If you will use the ruler library's mouse constraint management support, then the VisRulers must be run in the same thread which manages the main content. The VisRuler messages which deal with mouse positions receive the mouse location parameters on the stack, and thus the handlers must be running in the same thread as the callers. This is not a major restriction: since mouse events are so common, sending them across threads to be processed would lead to slow, jerky responses.

Your application may also include other objects:

  • RulerTypeControl to allow the user to use different measurement units.
  • RulerGridControl to draw a grid on the main view to which the user may snap the mouse.
  • RulerGuideControl to allow the user to manage guidelines.
  • RulerShowControl to show or hide rulers. Because hidden rulers are set not usable, and thus do not work well with GCN, applications including a RulerShowControl need to do some extra work. This is not difficult, but exactly what needs to be done may vary between applications. For details, see RulerShowControl .
  • If you will be using any of the ruler controllers, you must provide a way to relay classed events to the VisRuler objects. Whichever object will have the target when the controllers send out their classed events must have a special handler for MSG_META_SEND_CLASSED_EVENT which determines if the message is intended for a member of VisRuler class, and if so passes it to the master VisRuler object. Note that if you are working with the grobj library, a targeted GrObjBody correctly relays classed events to rulers.

Finally, if you wish the VisRuler to take special action on certain mouse events (perhaps providing a mark on the ruler to track the mouse pointer's position), the GenView's content should intercept those mouse events and send messages to the VisRuler (again, the VisRuler at the top of the VRI_slave chain) with the requested action.

Ruler Object Library: 3 VisRuler Instance Data

The VisRuler's instance data fields are listed below. Depending on your setup, the management of most of these fields will probably be taken care of by the ruler itself or some of its associated controllers. However, many programs set values for the VRI_rulerAttrs , VRI_constrainStrategy , and VRI_slave fields when defining their rulers.

Code Display 19-1

@classVisRulerClass,VisClass;
@instance VisRulerAttributes VRI_rulerAttrs;
typedef ByteFlags VisRulerAttributes;
#define VRA_IGNORE_ORIGIN 				0x80
#define VRA_SHOW_GUIDES 				0x40
#define VRA_SHOW_GRID 				0x20
#define VRA_SHOW_MOUSE 				0x10
#define VRA_HORIZONTAL 				0x08
#define VRA_MASTER 				0x04
@instance VisRulerType VRI_type;
typedef ByteEnum VisRulerType;
  #define VRT_INCHES 			0x0
#define VRT_CENTIMETERS 			0x1
#define VRT_POINTS 			0x2
#define VRT_PICAS 			0x3
#define VRT_CUSTOM 			CUSTOM_RULER_DEFINITION
#define VRT_NONE 			NO_RULERS
#define VRT_DEFAULT 			SYSTEM_DEFAULT
@instance VisRulerConstrainStrategy VRI_constrainStrategy;
typedef WordFlags VisRulerConstrainStrategy;
#define VRCS_OVERRIDE 							0x8000
#define VRCS_SET_REFERENCE 							0x2000
#define VRCS_SNAP_TO_GRID_X_ABSOLUTE 							0x1000
#define VRCS_SNAP_TO_GRID_Y_ABSOLUTE 							0x0800
#define VRCS_SNAP_TO_GRID_X_RELATIVE 							0x0400
#define VRCS_SNAP_TO_GRID_Y_RELATIVE 							0x0200
#define VRCS_SNAP_TO_GUIDES_X 							0x0100
#define VRCS_SNAP_TO_GUIDES_Y 							0x0080
#define VRCS_CONSTRAIN_TO_HORIZONTAL_AXIS 							0x0040
#define VRCS_CONSTRAIN_TO_VERTICAL_AXIS 							0x0020
#define VRCS_CONSTRAIN_TO_UNITY_SLOPE_AXIS 							0x0010
#define VRCS_CONSTRAIN_TO_NEGATIVE_UNITY_SLOPE_AXIS 							0x0008
#define VRCS_CONSTRAIN_TO_VECTOR 							0x0004
#define VRCS_CONSTRAIN_TO_VECTOR_REFLECTION 							0x0002
#define VRCS_INTERNAL 							0x0001
@instance MinIncrementType 			VRI_minIncrement; /* minimum increment displayed */
  typedef union {
    MinUSMeasure 			MIT_US;
    MinMetricMeasure 			MIT_METRIC;
    MinPointMeasure 			MIT_POINT;
    MinPicaMeasure 			MIT_PICA;
} MinIncrementType;
typedef ByteEnum   MinUSMeasure;
#define MUSM_EIGHTH_INCH 				0x0
#define MUSM_QUARTER_INCH 				0x1
#define MUSM_HALF_INCH 				0x2
#define MUSM_ONE_INCH 				0x3
typedef ByteEnum   MinMetricMeasure;
#define MMM_MILLIMETER 				0x0
#define MMM_HALF_CENTIMETER 				0x1
#define MMM_CENTIMETER 				0x2
typedef ByteEnum   MinPointMeasure;
#define MPM_25_POINT 				0x0
#define MPM_50_POINT 				0x1
#define MPM_100_POINT 				0x2
typedef ByteEnum   MinPicaMeasure;
#define MPM_PICA 				0x0
#define MPM_INCH 				0x1
@instance WWFixed VRI_scale; /* scale factor */
@instance DWFixed VRI_origin; /* 0,0 of the ruler in document coordinates */
@instance PointDWFixed VRI_reference;
@instance sdword VRI_mouseMark;
@instance word VRI_window; /* actually a noreloc WindowHandle */
@instance optr VRI_slave;
@instance Grid VRI_grid;
@instance WWFixed VRI_vectorSlope; /* slope of vector */
@instance optr VRI_guideArray;
@instance word VRI_guideInfluence; /* influence of guides in pixels */
@instance word VRI_desiredSize;
@instance word VRI_reserved;
@instance dword VRI_invalOD; /* actually a noreloc optr */
@instance word VRI_transformGState; /* actually a noreloc GStateHandle */

The VRI_rulerAttrs field contains flags which determine whether the ruler will support given features. This field tends to vary between applications; put some thought into which flags to turn on.

VRA_IGNORE_ORIGIN
Using the VRI_origin field, it is possible to set an origin for the user to use based upon the coordinates of the graphic space within the main view. This can come in handy when the zero point for the ruler shouldn't correspond to the very left or top of the view; GeoWrite uses this when the user wants the ruler's origin to be based upon the right edge of a text object rather than the edge of the view. The VRA_IGNORE_ORIGIN flag effectively turns off this feature.
VRA_SHOW_GUIDES
This flag indicates that the user wants guidelines drawn over their main view.
VRA_SHOW_GRID
This flag indicates that the user wants gridlines drawn over the main view.
VRA_SHOW_MOUSE
This flag indicates that the ruler should draw a tick mark to show the mouse pointer's location.
VRA_HORIZONTAL
This flag indicates that the ruler is horizontal. Horizontal rulers should have this flag turned on; vertical rulers should not.
VRA_MASTER
If a given view has only one ruler associated with it, that ruler should have this flag set. If a given view has more than one ruler (perhaps having one horizontal and one vertical), one of these rulers should be set up as the master ruler. It should have the VRA_MASTER flag set and should have the second ruler's optr in its VRI_slave field. If there is a third ruler, the second ruler should have its optr in its (the second ruler's) VRI_slave field, and so on.

The VRI_constrainStrategy field contains the default mouse constraint strategy the ruler should use when constraining mouse movement.

VRCS_OVERRIDE
This flag isn't really meant to be part of the default mouse constraint; it will be ignored if in the VRI_constrainStrategy field. During some events other constrain strategies may be combined with the default mouse constrain strategy; if the other strategy's VRCS_OVERRIDE flag is set, then the default mouse constraint strategy field will be ignored.
VRCS_SET_REFERENCE
This flag signals that the next selection should signal that the user wishes the point clicked on to be the reference point, which is used with certain kinds of mouse constraint.
VRCS_SNAP_TO_GRID_X_ABSOLUTE
This flag signals that the mouse position should be snapped to the grid horizontally.
VRCS_SNAP_TO_GRID_Y_ABSOLUTE
This flag signals that the mouse position should be snapped to the grid vertically.
VRCS_SNAP_TO_GRID_X_RELATIVE
This flag signals that the mouse position should be horizontally snapped to a grid such as would be formed if the present grid spacing was used with the reference point as the grid's origin.
VRCS_SNAP_TO_GRID_Y_RELATIVE
This flag signals that the mouse position should be vertically snapped to a grid such as would be formed if the present grid spacing was used with the reference point as the grid's origin.
VRCS_SNAP_TO_GUIDES_X
This flag signals that the mouse positions should be horizontally snapped to guidelines. Note that guidelines are only effective within range of their "influence" in pixels.
VRCS_SNAP_TO_GUIDES_Y
This flag signals that the mouse positions should be vertically snapped to guidelines. Note that guidelines are only effective within range of their "influence" in pixels.
VRCS_CONSTRAIN_TO_HORIZONTAL_AXIS
This flag signals that the mouse position should be restrained to the horizontal line passing through the reference point.
VRCS_CONSTRAIN_TO_VERTICAL_AXIS
This flag signals that the mouse position should be restrained to the vertical line passing through the reference point.
VRCS_CONSTRAIN_TO_UNITY_SLOPE_AXIS
This flag signals that the mouse position should be restrained to the diagonal line with slope ( y = x ) passing through the reference point.
VRCS_CONSTRAIN_TO_NEGATIVE_UNITY_SLOPE_AXIS
This flag signals that the mouse position should be restrained to the diagonal line with slope ( y = -x ) passing through the reference point.
VRCS_CONSTRAIN_TO_VECTOR
This flag signals that the mouse position should be constrained to the line passing through the reference and vector points.
VRCS_CONSTRAIN_TO_VECTOR_REFLECTION
This flag signals that the mouse position should be constrained to the reflection of the line passing through the reference and vector points.

The VRI_type and VRI_minIncrement fields determine the measurement type and ruler increment of the ruler.Normally, the user will work with the ruler's type via a RulerTypeControl. RulerTypeControlClass 's features structures are shown below.

Code Display 19-2 RulerTypeControl Features

typedef WordFlags  RTCFeatures;
#define  RTCF_DEFAULT				(0x20)
#define RTCF_SPREADSHEET 				(0x10)
#define RTCF_INCHES 				(0x08)
#define RTCF_CENTIMETERS 				(0x04)
#define RTCF_POINTS 				(0x02)
#define RTCF_PICAS 				(0x01)
#define  RTC_DEFAULT_FEATURES \
    (RTCF_INCHES | RTCF_CENTIMETERS | RTCF_POINTS | RTCF_PICAS | RTCF_DEFAULT)
typedef WordFlags  RTCToolboxFeatures;
#define  RTCTF_DEFAULT 				(0x20)
#define RTCTF_SPREADSHEET 				(0x10)
#define RTCTF_INCHES 				(0x08)
#define RTCTF_CENTIMETERS 				(0x04)
#define RTCTF_POINTS 				(0x02)
#define RTCTF_PICAS 				(0x01)
#define  RTC_DEFAULT_TOOLBOX_FEATURES (RTCF_INCHES | RTCF_CENTIMETERS)

The RulerTypeControl uses a notification block with the following structure:

typedef struct RulerTypeNotificationBlock {
    VisRulerType  RTNB_type;
}  RulerTypeNotificationBlock;

The ruler must keep track of the main view's scale; if the main view has doubled in scale, the ruler should put its tick marks twice as far apart. This scale is stored in the VRI_scale field. The ruler automatically receives a MSG_VIS_RULER_VIEW_SCALE_FACTOR_CHANGED when the main view's scale changes. To set the scale independently of the main view scale, send a MSG_VIS_RULER_SET_SCALE .

The VRI_origin field allows you to set an origin for the ruler other than (0,0). The coordinates are given in terms of the main view's coordinate system.

The reference point, used for various types of mouse constraint, is stored in the VRI_reference field.

VRI_mouseMark contains the coordinate at which the mouse tick is currently being drawn.

The VRI_window field, initiated automatically, holds the window handle of the main view.

The VRI_slave field holds the ruler's slave ruler; see the documentation for VRA_MASTER, above, to learn about master and slave rulers.

The VRI_grid field contains the information used to maintain the grid associated with a ruler.

The VRI_vectorSlope defines the slope of a vector that may be used with vector mouse constraints.

The VRI_guideArray field keeps track of the location where guideline data is stored; this field is initiated automatically.

The VRI_guideInfluence contains the influence to use when constraining the mouse position to guidelines. Guidelines will only attract the mouse if it is within a certain distance of them--this distance is the "influence" of the guidelines. It is measured in pixels, not points; thus, the guide influence is independent of scale.

The VRI_desiredSize field contains the ruler's idea of its ideal thickness. By default, this is set so that the ruler will have room to draw its numbers and tick marks normally; if you will be subclassing the VisRuler to add more gadgetry, you may wish to use a different value here.

The VRI_invalOD field contains the object descriptor of the object which should be redrawn when the ruler needs to draw something differently to the main view; by the ruler's default behavior, this will be updated automatically via a MSG_VIS_RULER_GAINED_SELECTION .

The VRI_transformGState field contains the handle of a GState. This GState contains the transformation which is used to transform the coordinate system used with mouse constraints.

MSG_VIS_RULER_SET_TYPE
void	MSG_VIS_RULER_SET_TYPE(
        VisRulerType type);

This message sets the VRI_type field.

MSG_VIS_RULER_GET_TYPE
VisRulerType MSG_VIS_RULER_GET_TYPE();

This message returns the value in the VRI_type field.

MSG_VIS_RULER_SET_CONSTRAIN_STRATEGY
void MSG_VIS_RULER_SET_CONSTRAIN_STRATEGY(
        VisRulerConstrainStrategy 				setflags,
        VisRulerConstrainStrategy 				clearflags);

This message sets the default mouse constrain strategy for the ruler.

Parameters: setflags Flags to set.

clearflags
Flags to clear.

Return: Nothing.

MSG_VIS_RULER_GET_CONSTRAIN_STRATEGY
VisRulerConstrainStrategy
MSG_VIS_RULER_GET_CONSTRAIN_STRATEGY();

This message gets the current default constrain strategy for the ruler.

MSG_VIS_RULER_SET_IGNORE_ORIGIN
void MSG_VIS_RULER_SET_IGNORE_ORIGIN(
        Boolean 		ignore);

This message sets the "ignore origin" state.

Parameters: ignore Set true (i.e. non-zero) to ignore the origin. Set false (i.e. zero) not to ignore the origin.

Return: Nothing.

MSG_VIS_RULER_SET_ORIGIN
void MSG_VIS_RULER_SET_ORIGIN(
        sdword 		pointInt,
        word 		pointFrac);

Use this message to set the ruler's origin point.

MSG_VIS_RULER_GET_ORIGIN
void MSG_VIS_RULER_GET_ORIGIN(
        DWFixedReturn *retval);

Use this message to retrieve the ruler's origin point.

Structures:

typedef struct {
    word 		unused;
    word 		 DWFR_frac;
    sdword 		DWFR_int;
}  DWFixedReturn;
MSG_VIS_RULER_SET_REFERENCE
void MSG_VIS_RULER_SET_REFERENCE( @stack
        sdword 		yInt,
        word 		yFrac,
        sdword 		xInt,
        word 		xFrac);

Set the ruler's reference point.

MSG_VIS_RULER_SET_VECTOR
void MSG_VIS_RULER_SET_VECTOR( @stack
        sdword 		yInt,
        word 		yFrac,
        sdword 		xInt,
        word 		xFrac);

Set the point to use to define the "vector"--together with the reference point, this will form a slope which may be used for mouse constraints.

MSG_VIS_RULER_SET_MIN_INCREMENT
void	MSG_VIS_RULER_SET_MIN_INCREMENT(
        MinIncrementType min);

This message sets the VRI_minIncrement field to the passed value. It must be passed the proper MinIncrementType value.

MSG_VIS_RULER_SET_SCALE
void	MSG_VIS_RULER_SET_SCALE(
        WWFixedAsDWord scale);

This message sets the VRI_scale field.

Ruler Object Library: 4 Managing Rulers

Most ruler functions will happen automatically. However, certain pieces of functionality require that the geode provide certain message handlers. Some geodes may have use for subclassed ruler objects, and subclassing will require further extra code. This section provides information for those who would learn more about how to manage rulers.

RulerShowControl

Mouse Tracking

Grid Spacing and Constraint

Guide Constraints and Guidelines

Other Mouse Constraints

Esoteric Messages

Ruler Object Library: 4.1 Managing Rulers: RulerShowControl

The RulerShowControl allows the user to show or hide rulers. This control works by setting RulerViews not usable. However, objects which aren't usable don't work with GCN, and thus you will need to set up some other object to intercept the appropriate notifications. Don't try to include a RulerShowControl unless you know about GCN or are willing to learn.

When declaring your RulerShowControl, you may choose a GCN list which the control will work with. The default list is MANUFACTURER_ID_GEOWORKS:
GAGCNLT_DISPLAY_OBJECTS_WITH_RULERS. However, you may change this to be any list you like. You must also specify a message which some object will respond to.

Some object which will be usable when the user has access to the RulerShowControl should be on the control's GCN list. This object must be prepared to handle the message in the RSCI_message field. This message should take a RulerShowControlAttributes structure and set the ruler views usable or not usable accordingly. See Sample Handler for use with RulerShowControl for an example.

Code Display 19-3 RulerShowControl Instance Data and Features

typedef WordFlags  RSCCFeatures;
#define  RSCCF_SHOW_VERTICAL (0x04)
#define RSCCF_SHOW_HORIZONTAL (0x02)
#define RSCCF_SHOW_RULERS (0x01)
typedef WordFlags  RSCCToolboxFeatures;
/* There are no toolbox features. */
#define  RSCC_DEFAULT_FEATURES (RSCCF_SHOW_VERTICAL | RSCCF_SHOW_HORIZONTAL)
#define RSCC_DEFAULT_TOOLBOX_FEATURES (0)
@instance RulerShowControlAttributes 					RSCI_attrs;
typedef WordFlags  RulerShowControlAttributes;
#define  RSCA_SHOW_VERTICAL 				0x8000
#define RSCA_SHOW_HORIZONTAL 				0x4000
@instance GCNListType 					 RSCI_gcnList; /* object to notify */
@instance Message 					RSCI_message; /* message to notify with */

Code Display 19-4 Sample Handler for use with RulerShowControl

@method MyDisplayClass, MSG_MD_UPDATE_RULERS
    /* The RulerShowControl's RSCI_message field should be
     * MSG_MD_UPDATE_RULERS. The message should have been declared
     * using the RULER_SHOW_CONTROL_NOTIFY prototype. */
  if (attrs & RSCA_SHOW_VERTICAL) {
    @call LeftView::MSG_GEN_SET_USABLE(VUM_NOW);
  }
  else {
    @call LeftView::MSG_GEN_SET_NOT_USABLE(VUM_NOW);
  }
  if (attrs & RSCA_SHOW_HORIZONTAL) {
    @call TopView::MSG_GEN_SET_USABLE(VUM_NOW);
  }
  else {
    @call TopView::MSG_GEN_SET_NOT_USABLE(VUM_NOW);
  }
}

Ruler Object Library: 4.2 Managing Rulers: Mouse Tracking

The ruler has the ability to show a tick mark which tracks the mouse's movement. This can be a boon to users who wish to know the mouse's precise location.

MSG_VIS_RULER_SHOW_MOUSE
void MSG_VIS_RULER_SHOW_MOUSE();

Sets a flag such that the ruler will draw the mouse tick on pointer events.

MSG_VIS_RULER_HIDE_MOUSE
void MSG_VIS_RULER_HIDE_MOUSE();

Sets a flag such that the ruler won't draw the mouse tick on pointer events.

MSG_VIS_RULER_DRAW_MOUSE_TICK
void MSG_VIS_RULER_DRAW_MOUSE_TICK( @stack
        sdword 		yInt,
        word 		yFrac,
        sdword 		xInt,
        word 		xFrac);

This message indicates that the ruler should draw a line indicating the mouse position and informs the ruler of the mouse's new position.

Ruler Object Library: 4.3 Managing Rulers: Grid Spacing and Constraint

Applications which provide rulers often do so to allow the user to work with the mouse more accurately. Such applications might also provide a grid. A grid helps locate the mouse pointer's exact location on the document without the need to watch the ruler. The ruler can also constrain mouse movement to the grid.

To allow the user to change the grid spacing, include a RulerGridControl. The features structures for the RulerGridControl are shown below.

Code Display 19-5 RulerGridControlClass Features

typedef WordFlags  RGCFeatures;
#define  RGCF_GRID_SPACING 				(0x04)
#define RGCF_SNAP_TO_GRID 				(0x02)
#define RGCF_SHOW_GRID 				(0x01)
#define R GC_DEFAULT_FEATURES \
            (RGCF_GRID_SPACING | RGCF_SNAP_TO_GRID | RGCF_SHOW_GRID)
#define RGC_DEFAULT_TOOLBOX_FEATURES (0)
MSG_VIS_RULER_SHOW_GRID
void MSG_VIS_RULER_SHOW_GRID();

Use this message to request that the grid be drawn to the main view, setting the VRA_SHOW_GRID flag.

MSG_VIS_RULER_HIDE_GRID
void MSG_VIS_RULER_HIDE_GRID();

Use this message to request that the grid not be drawn to the main view, clearing the VRA_SHOW_GRID flag.

MSG_VIS_RULER_DRAW_GRID
void MSG_VIS_RULER_DRAW_GRID(
        GStateHandle gstate);

This message will draw the grid to the passed GState if the VRA_SHOW_GRID_FLAG is set.

MSG_VIS_RULER_TURN_GRID_SNAPPING_ON
void MSG_VIS_RULER_TURN_GRID_SNAPPING_ON();

Set the default VisRulerConstrainStrategy to support grid snapping, setting the VRCS_SNAP_TO_GRID_X_ABSOLUTE and VRCS_SNAP_TO_GRID_Y_ABSOLUTE flags.

MSG_VIS_RULER_TURN_GRID_SNAPPING_OFF
void MSG_VIS_RULER_TURN_GRID_SNAPPING_OFF();

Set the default VisRulerConstrainStrategy to not include grid snapping, turning off the VRCS_SNAP_TO_GRID_X_ABSOLUTE and VRCS_SNAP_TO_GRID_Y_ABSOLUTE flags.

MSG_VIS_RULER_SET_GRID_SPACING
void MSG_VIS_RULER_SET_GRID_SPACING(
        WWFixedAsDWord spacing);

Set the grid's horizontal and vertical spacing, working with the VRI_grid field.

MSG_VIS_RULER_SET_HORIZONTAL_GRID_SPACING
void MSG_VIS_RULER_SET_HORIZONTAL_GRID_SPACING(
        WWFixedAsDWord spacing);

Set the horizontal grid spacing, leaving the vertical alone.

MSG_VIS_RULER_SET_VERTICAL_GRID_SPACING
void MSG_VIS_RULER_SET_VERTICAL_GRID_SPACING(
        WWFixedAsDWord spacing);

Set the vertical grid spacing, leaving the horizontal alone

MSG_VIS_RULER_GET_GRID_SPACING
void MSG_VIS_RULER_GET_GRID_SPACING(
        GridSpacing* gridspace);

This message returns the current grid spacing.

Structures: This message works with the GridSpacing structure. Do not confuse this structure with the Grid structure.

typedef struct {
    WWFixed GS_y;
    WWFixed GS_x;
} GridSpacing;
MSG_VIS_RULER_GET_STRATEGIC_GRID_SPACING
void MSG_VIS_RULER_GET_STRATEGIC_GRID_SPACING(
        GridSpacing* gridspace);

This message returns a grid spacing that will look nice on the screen at the current scale factor.

Structures: This message works with the GridSpacing structure. Do not confuse this structure with the Grid structure.

typedef struct {
    WWFixed GS_y;
    WWFixed GS_x;
}   GridSpacing;
MSG_VIS_RULER_SNAP_TO_GRID
void MSG_VIS_RULER_SNAP_TO_GRID(
        PointDWFixed _far* point);

This message snaps the passed coordinate to the grid.

Parameters: point Point to snap to grid.

Return: Nothing returned explicitly, however structure in point modified so that point is snapped to grid.

MSG_VIS_RULER_SNAP_TO_GRID_X
void MSG_VIS_RULER_SNAP_TO_GRID_X(
        PointDWFixed _far* point);

Snap the passed point's x coordinate to the grid.

Parameters: point Point to snap to grid.

Return: Nothing returned explicitly, however structure in point modified so that point is snapped to grid.

MSG_VIS_RULER_SNAP_TO_GRID_Y
void MSG_VIS_RULER_SNAP_TO_GRID_Y(
        PointDWFixed _far* point);

Snap the passed point's y coordinate to the grid.

Parameters: point Point to snap to grid.

Return: Nothing returned explicitly, however structure in point modified so that point is snapped to grid.

MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE
void MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE(
        PointDWFixed _far* point);

Returns the point closest to the passed point that is an integral number of grid spacings from the reference point.

Parameters: point Point to snap.

Return: Nothing returned explicitly, however structure in point modified so that point is snapped.

MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE_X
void MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE_X(
        PointDWFixed _far* point);

Returns the point horizontally closest to the passed point that is an integral number of grid spacings from the reference point.

Parameters: point Point to snap.

Return: Nothing returned explicitly, however structure in point modified so that point is snapped.

MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE_Y
void MSG_VIS_RULER_SNAP_RELATIVE_TO_REFERENCE_Y(
        PointDWFixed _far* point);

Returns the point vertically closest to the passed point that is an integral number of grid spacings from the reference point.

Parameters: point Point to snap.

Return: Nothing returned explicitly, however structure in point modified so that point is snapped.

Ruler Object Library: 4.4 Managing Rulers: Guide Constraints and Guidelines

Sometimes the user will want to have some guidelines that do not conform to a regular grid. The user may set up their own guidelines. Normally to allow the use of guidelines, your application should include a RulerGuideControl object. The features structures for this controller class are shown below.

Code Display 19-6 RulerGuideControlClass Features

typedef WordFlags  RulerGuideControlFeatures;
#define  RGCF_HV 			(0x08)
#define RGCF_LIST 			(0x04)
#define RGCF_POSITION 			(0x02)
#define RGCF_DELETE 			(0x01)
#define  RULER_GUIDE_CONTROL_DEFAULT_FEATURES \
            (RGCF_HV | RGCF_LIST | RGCF_POSITION | RGCF_DELETE)

The following messages allow for the use and manipulation of guidelines.

MSG_VIS_RULER_TURN_GUIDES_SNAPPING_ON
void MSG_VIS_RULER_TURN_GUIDES_SNAPPING_ON();

Set the default VisRulerConstrainStrategy to include guides snapping, turning on the VRCS_SNAP_TO_GUIDES_X and VRCS_SNAP_TO_GUIDES_Y flags.

MSG_VIS_RULER_TURN_GUIDES_SNAPPING_OFF
void MSG_VIS_RULER_TURN_GUIDES_SNAPPING_OFF();

Set the default VisRulerConstrainStrategy to not include guides snapping, turning off the VRCS_SNAP_TO_GUIDES_X and VRCS_SNAP_TO_GUIDES_Y flags.

MSG_VIS_RULER_CREATE_GUIDE_ARRAY
void MSG_VIS_RULER_CREATE_GUIDE_ARRAY();

By default, the VisRuler stores its guides in a chunk array, known as a guide array. If the ruler has no guides defined, it doesn't bother keeping this data structure around. When the first guide is created, the VisRuler will send itself this message, to which it will respond by setting up the data region.

Subclasses might intercept this message if they have data structures which need initializing when the first guide is created.

MSG_VIS_RULER_ADD_HORIZONTAL_GUIDE
void MSG_VIS_RULER_ADD_HORIZONTAL_GUIDE( @stack
        sdword 		dwfInt,
        word 		dwfFrac);

Use this message to add a horizontal guide.

MSG_VIS_RULER_ADD_VERTICAL_GUIDE
void MSG_VIS_RULER_ADD_VERTICAL_GUIDE( @stack
        sdword 		dwfInt,
        word 		dwfFrac);

Use this message to add a vertical guide.

MSG_VIS_RULER_DRAW_GUIDES
void MSG_VIS_RULER_DRAW_GUIDES(
        GStateHandle gstate);

Use this message to draw guidelines to a document. The default VisRuler handler for this message will draw the lines at the appropriate coordinates to the passed GState.

MSG_VIS_RULER_DRAW_GUIDE_INDICATORS
void MSG_VIS_RULER_DRAW_GUIDE_INDICATORS();

This message asks the VisRuler to draw tick-marks on itself to show the positions of guides. The default VisRuler has no handler for this message; subclasses are welcome to intersect it and draw indicators appropriately.

MSG_VIS_RULER_SNAP_TO_GUIDES
void MSG_VIS_RULER_SNAP_TO_GUIDES(
        PointDWFixed _far* point);

Use this message to snap the passed point to the guides.

MSG_VIS_RULER_SNAP_TO_GUIDES_X
void MSG_VIS_RULER_SNAP_TO_GUIDES_X(
        PointDWFixed _far* point);

Use this message to horizontally snap the passed point to the guides.

MSG_VIS_RULER_SNAP_TO_GUIDES_Y
void MSG_VIS_RULER_SNAP_TO_GUIDES_Y(
        PointDWFixed _far* point);

Use this message to vertically snap the passed point to the guides.

MSG_VIS_RULER_GET_GUIDE_INFLUENCE
word MSG_VIS_RULER_GET_GUIDE_INFLUENCE();

Guidelines will only affect the mouse if the mouse pointer lies within the guide's area of influence. This message returns the present influence value.

MSG_VIS_RULER_SET_GUIDE_INFLUENCE
void MSG_VIS_RULER_SET_GUIDE_INFLUENCE(
        word influence);

Guidelines will only affect the mouse if the mouse pointer lies within the guide's area of influence. This message sets the influence distance, measured in pixels.

MSG_VIS_RULER_SELECT_HORIZONTAL_GUIDE
void MSG_VIS_RULER_SELECT_HORIZONTAL_GUIDE( @stack
        sdword 		dwfInt,
        word 		dwfFrac);

Use this message to select a horizontal guide at the passed coordinates, if any. Note that only one horizontal guide may be selected at any time.

MSG_VIS_RULER_SELECT_VERTICAL_GUIDE
void MSG_VIS_RULER_SELECT_VERTICAL_GUIDE( @stack
        sdword 		dwfInt,
        word 		dwfFrac);

Use this message to select a vertical guide at the passed coordinates, if any. Note that only one vertical guide may be selected at any time.

MSG_VIS_RULER_DESELECT_ALL_HORIZONTAL_GUIDES
void MSG_VIS_RULER_DESELECT_ALL_HORIZONTAL_GUIDES();

Use this message to deselect all of the ruler's horizontal guides.

MSG_VIS_RULER_DESELECT_ALL_VERTICAL_GUIDES
void MSG_VIS_RULER_DESELECT_ALL_VERTICAL_GUIDES();

Use this message to deselect all of the ruler's vertical guides.

MSG_VIS_RULER_DELETE_HORIZONTAL_GUIDE
void MSG_VIS_RULER_DELETE_HORIZONTAL_GUIDE( @stack
        sdword 		dwfInt,
        word 		dwfFrac);

Use this message to delete the horizontal guide at the passed coordinate, if any.

MSG_VIS_RULER_DELETE_VERTICAL_GUIDE
void MSG_VIS_RULER_DELETE_VERTICAL_GUIDE( @stack
        sdword 		dwfInt,
        word 		dwfFrac);

Use this message to delete the vertical guide at the passed coordinate, if any.

Ruler Object Library: 4.5 Managing Rulers: Other Mouse Constraints

Up until now, we have discussed various ways to constrain mouse movement to grid- or guide-lines. However, other constraints are possible, and may be accessed through the following messages.

MSG_VIS_RULER_CONSTRAIN_TO_AXES
void MSG_VIS_RULER_CONSTRAIN_TO_AXES(
        PointDWFixed _far* point);

Use this message to constrain the mouse to the axes; that is to force it to be in line either horizontally or vertically with the reference point (the point stored in VRI_reference ).

MSG_VIS_RULER_CONSTRAIN_TO_HORIZONTAL_AXIS
void MSG_VIS_RULER_CONSTRAIN_TO_HORIZONTAL_AXIS(
        PointDWFixed _far* point);

Use this message to constrain the mouse horizontally, forcing it to be in line horizontally with the reference point (the point stored in VRI_reference ).

MSG_VIS_RULER_CONSTRAIN_TO_VERTICAL_AXIS
void MSG_VIS_RULER_CONSTRAIN_TO_VERTICAL_AXIS(
        PointDWFixed _far* point);

Use this message to constrain the mouse vertically, forcing it to be in line vertically with the reference point (the point stored in VRI_reference ).

MSG_VIS_RULER_CONSTRAIN_TO_DIAGONALS
void MSG_VIS_RULER_CONSTRAIN_TO_DIAGONALS(
        PointDWFixed _far* point);

Use this message to constrain the mouse to the diagonal axes; that is to force it to be on a line 45 degrees through the reference point (the point stored in VRI_reference ).

MSG_VIS_RULER_SET_CONSTRAIN_TRANSFORM
void MSG_VIS_RULER_SET_CONSTRAIN_TRANSFORM(
        TransMatrix* tm)

Use this message to impose a transformation on the coordinate system used to constrain the mouse. You can see this at work in GeoDraw when resizing an object which has been rotated or skewed.

MSG_VIS_RULER_CLEAR_CONSTRAIN_TRANSFORM
void MSG_VIS_RULER_CLEAR_CONSTRAIN_TRANSFORM();

Use this message to remove any transformation on the coordinate system used to constrain the mouse.

MSG_VIS_RULER_CONSTRAIN_X_TO_UNITY_SLOPE_AXIS
void MSG_VIS_RULER_CONSTRAIN_X_TO_UNITY_SLOPE_AXIS(
        PointDWFixed _far* point);

Use this message to determine the coordinates resulting from horizontally snapping a given point to the Northeast diagonal line passing through the reference point.

MSG_VIS_RULER_CONSTRAIN_Y_TO_UNITY_SLOPE_AXIS
void MSG_VIS_RULER_CONSTRAIN_Y_TO_UNITY_SLOPE_AXIS(
        PointDWFixed _far* point);

Use this message to determine the coordinates resulting from vertically snapping a given point to the Northeast diagonal line passing through the reference point.

MSG_VIS_RULER_CONSTRAIN_X_TO_NEGATIVE_SLOPE_AXIS
void MSG_VIS_RULER_CONSTRAIN_X_TO_NEGATIVE_SLOPE_AXIS(
        PointDWFixed _far* point);

Use this message to determine the coordinates resulting from horizontally snapping a given point to the Southeast diagonal line passing through the reference point.

MSG_VIS_RULER_CONSTRAIN_Y_TO_NEGATIVE_SLOPE_AXIS
void MSG_VIS_RULER_CONSTRAIN_Y_TO_NEGATIVE_SLOPE_AXIS(
        PointDWFixed _far* point);

Use this message to determine the coordinates resulting from horizontally snapping a given point to the Southeast diagonal line passing through the reference point.

MSG_VIS_RULER_CONSTRAIN_TO_VECTOR
@message void MSG_VIS_RULER_CONSTRAIN_TO_VECTOR(
        PointDWFixed _far* point);

This message computes the point resulting from constraining the passed point using vector constraint. This means that the mouse's constrained location will end up at a certain angle from the reference angle. This angle is determined by means of a point set up my MSG_VIS_RULER_SET_VECTOR . This constraint will constrain the point either to the vector line or to that line's reflection.

MSG_VIS_RULER_CONSTRAIN_X_TO_VECTOR
@message void MSG_VIS_RULER_CONSTRAIN_X_TO_VECTOR(
        PointDWFixed _far* point);

This message computes the point resulting from horizontally constraining the passed point to the line going through the reference point with the present vector slope.

MSG_VIS_RULER_CONSTRAIN_X_TO_VECTOR_REFLECTION
@message void MSG_VIS_RULER_CONSTRAIN_X_TO_VECTOR_REFLECTION(
        PointDWFixed _far* point);

This message computes the point resulting from horizontally constraining the passed point to the line going through the reference point with the negative of the present vector slope.

MSG_VIS_RULER_CONSTRAIN_Y_TO_VECTOR
@message void MSG_VIS_RULER_CONSTRAIN_Y_TO_VECTOR(
        PointDWFixed _far* point);

This message computes the point resulting from vertically constraining the passed point to the line going through the reference point with the present vector slope.

MSG_VIS_RULER_CONSTRAIN_Y_TO_VECTOR_REFLECTION
@message void MSG_VIS_RULER_CONSTRAIN_Y_TO_VECTOR_REFLECTION(
        PointDWFixed _far* point);

This message computes the point resulting from vertically constraining the passed point to the line going through the reference point with the negative of the present vector slope.

Ruler Object Library: 4.6 Managing Rulers: Esoteric Messages

For the most part, the ruler will work together with the main view to coordinate updates. This is done by means of messages going to the ruler. Subclasses of VisRulerClass might conceivably intercept these messages to alter behavior.

MSG_VIS_RULER_GAINED_SELECTION
void MSG_VIS_RULER_GAINED_SELECTION(
        optr 		dest);

This message notifies the ruler that the ruled object (the object in the main view) is selected and the ruler should update its UI to reflect its own attributes.

Parameters: dest Object to send messages to whenever a change in ruler settings should result in the ruled object being redrawn (e.g. when the user wants to draw the grid).

Return: Nothing.

MSG_VIS_RULER_LOST_SELECTION
void MSG_VIS_RULER_LOST_SELECTION();

This message notifies the ruler that the ruled object is no longer selected.

MSG_VIS_RULER_UPDATE_CONTROLLERS
void MSG_VIS_RULER_UPDATE_CONTROLLERS();

This message signals that the ruler should update its associated controllers.

MSG_VIS_RULER_UPDATE_TYPE_CONTROLLER
void MSG_VIS_RULER_UPDATE_TYPE_CONTROLLER();

This message signals that the ruler should update its type controller, if any.

MSG_VIS_RULER_UPDATE_GRID_CONTROLLER
void MSG_VIS_RULER_UPDATE_GRID_CONTROLLER();

This message signals that the ruler should update its grid controller, if any.

MSG_VIS_RULER_UPDATE_GUIDE_CONTROLLER
void MSG_VIS_RULER_UPDATE_GUIDE_CONTROLLER();

This message signals that the ruler should update its guide controller, if any.

MSG_VIS_RULER_COMBINE_GUIDE_INFO
void MSG_VIS_RULER_COMBINE_GUIDE_INFO(
        MemHandle data); 		/* Handle of block containing
               VisRulerNotifyGuideChangeBlockHeader*/

This message allows the horizontal and vertical rulers to load their respective guide information into a single structure so that the information can be collated and passed on to a RulerGuideControl.

Structures:

typedef struct {
    LMemBlockHeader 			 VRNGCBH_header;
    word 			VRNGCBH_vertGuideArray;
    word 			VRNGCBH_horizGuideArray;
}  VisRulerNotifyGuideChangeBlockHeader;
MSG_VIS_RULER_VIEW_SCALE_FACTOR_CHANGED
void MSG_VIS_RULER_VIEW_SCALE_FACTOR_CHANGED( @stack
        WindowHandle 		viewWindow,
        WWFixedAsDWord 		yScaleFactor,
        WWFixedAsDWord 		xScaleFactor);

The ruler will receive this message when the main view's scale factor changes. Specifically, when the RulerContent `s scale is changing (which will happen because of the RulerView's link to the main view), its handler for MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED sends this message to the VisRuler.

MSG_VIS_RULER_GET_DESIRED_SIZE
@message word MSG_VIS_RULER_GET_DESIRED_SIZE();

This message returns how much space the ruler thinks it needs to draw the numbers and hatch marks.

Interception: This method should be subclassed if the subclassed ruler's size needs to vary with respect to scale factor, whether the ruler is horizontal or vertical, or whatever size seems appropriate.

MSG_VIS_RULER_INVALIDATE_WITH_SLAVES
void MSG_VIS_RULER_INVALIDATE_WITH_SLAVES();

The ruler's default behavior will result in it redrawing itself at appropriate times. Depending on what use you put your ruler to, you may have to force it to redraw itself. If so, you should probably use this message.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Database

Some applications need to keep track of many small pieces of data. For example, a database might use thousands of items of data, each of them only a paragraph long; in a spreadsheet, the data size might be only a few bytes. GEOS provides a Database (DB) library to make it easy to keep track of such data and store them conveniently in a GEOS Virtual Memory file.

The DB library manages Local Memory heaps in a VM file and uses these heaps to store items. It lets the geode associate items into groups; these groups can grow indefinitely, unlike LMem heaps.

1 Design Philosophy
2 Database Structure
2.1 DB Items
2.2 DB Groups
2.3 Allocating Groups and Items
2.4 Ungrouped DB Items
2.5 The DB Map Item
3 Using Database Routines
3.1 General Rules to Follow
3.2 Allocating and Freeing Groups
3.3 Allocating and Freeing Items
3.4 Accessing DB Items
3.5 Resizing DB Items
3.6 Setting and Using the Map Item
3.7 Routines for Ungrouped Items
3.8 Other DB Utilities

Database: 1 Design Philosophy

A database manager should be flexible, allowing applications to store a variety of data items. It should be efficient, with minimal overhead in data-access time as well as in memory usage (whether in main memory or in disk space). Ideally, it ought to insulate applications from the details of memory allocation and data referencing. The GEOS database manager meets all of these requirements and several more:

  • Flexible Data Formats
    The DB Manager does not care about the content of a DB item. Consequently, a DB item can be anything that can fit in an LMem chunk. An application can use one file to store many different sizes of database item.
  • Speed and Efficiency
    The GEOS DB library uses the powerful GEOS memory management, Virtual Memory, and Local Memory routines. These enable it to store and access many database items with a minimal overhead in access time and storage space.
  • Uniform Data-Access Format
    Database items are stored in standard GEOS Virtual Memory files. All of the file-access utilities (such as the document control objects) can work unchanged with database files. Furthermore, VM files can contain both ordinary VM blocks and DB items in any combination.
  • Full Group Management
    Applications can divide DB items into groups. Access time is improved when items from the same group are accessed in succession. As an alternative, applications can let the DB manager create and assign groups for the items.
  • Sharable Data
    Since DB items are stored in VM files, the files can be shared between applications. All of the standard VM synchronization routines work for DB files.

Database: 2 Database Structure

The Database routines use a Database Manager to access and create DB items. These items are stored in a standard VM file. This chapter will sometimes refer to a "Database File"; this simply means a VM file which contains DB items.

DB Items

DB Groups

Allocating Groups and Items

Ungrouped DB Items

The DB Map Item

Database: 2.1 Database Structure: DB Items

The basic unit of data is the item . Items are simply chunks in special LMem heaps which are managed by the DB Manager; these heaps are called item blocks . You will not need to use any of the LMem routines; the DB manager will create and destroy LMem heaps as necessary and will call the appropriate routines to lock DB items when needed.

Each DB item in a DB file is uniquely identified by the combination of a group-handle and an item-handle . Note that these handles are not the same as the item's LMem Heap handle and its chunk handle. You will not generally need to use the item's heap and chunk handles; the DB routines store and use these automatically. However, you can retrieve them if necessary (for example, if you want to use an LMem utility on a DB item).

The DB Manager does not keep track of allocated items. Once you allocate an item, you must store the group- and item-handles. If you lose them, the item will remain in the file, but you will not be able to find it again.

Since DB items are chunks, their addresses are somewhat volatile. If you allocate an item in a group, other items in that group may move even if they are locked. (See the Local Memory chapter.)

Database: 2.2 Database Structure: DB Groups

Each DB item is a member of a DB group. The DB group is a collection of VM blocks; the group comprises a single group block and zero or more item blocks.

The group block contains information about each item block and each item in the group. For each item block, it records the VM handle of the block and the number of DB items in the block. For each DB item, it records the VM handle of the item block in which the item is stored and the item's chunk handle within that item block. The item blocks are simply LMem heaps with a little extra information in the headers.

The item's group-handle is simply the VM handle of the group block for that group. The item's item-handle is an offset into the group block; the information about the item is stored at that offset. When you lock an item, the DB manager looks in that location in the group block and reads the handles of the item block and the chunk associated with that item; it then locks the item block and returns the address of the chunk. (In assembly code, it returns the segment address and the chunk handle.) The relationship between the different blocks and handles is shown in the figure below.

Whenever you access a DB item, the DB manager has to lock the block. If you access several items in a row, the overall access time is better if they belong to the same group since only one group block will need to be swapped in to memory. The items may also be in the same item-block since each item block contains items from only one group; again, this improves access time. Thus, it is a good idea to distribute items in groups according to the way they will be accessed; for example, an address-book database might group entries according to the first letter of the last name, thus speeding up alphabetical access. If you have no logical way to group items, see Ungrouped DB Items .

Database: 2.3 Database Structure: Allocating Groups and Items

When you need a new DB group, call the DB routine DBGroupAlloc() (see Allocating and Freeing Groups ). This routine creates and initializes a DB group block.

When you allocate a DB item, you specify which group the item will go in. The DB manager sets up an entry for the item in the group block. It then decides which item block to put the item in. It tries to keep all the item blocks at the right size to optimize speed. If all of the group's item blocks are too full, it allocates a new item block and allocates the new item in that block. In either case, it returns the new item's item-handle.

Once an item has been allocated, it will stay in the same item block (and have the same chunk handle) until it is freed or resized. If it is resized to a larger size, it may be moved to a different item block belonging to the same group.

Database: 2.4 Database Structure: Ungrouped DB Items

Sometimes there is no natural way to group DB items. For these situations, the DB manager allows you to allocate ungrouped items. These items actually belong to special groups which are automatically allocated by the DB manager. The DB manager tries to keep these groups at the right size for optimum efficiency.

When you allocate an ungrouped item, the DB manager allocates an item in one of its "ungrouped groups." If there are no such groups or if all of these groups have too many items already, the DB manager allocates a new "ungrouped" group.

For practical purposes, ungrouped DB items have a single, dword-sized handle. This "handle" is of type DBGroupAndItem . The upper word of this is the handle of the ungrouped group for this item; the lower word is the item's item-handle within that group. There are special versions of most database routines for use with ungrouped items. These routines take a DBGroupAndItem argument instead of separate group-handle and item-handle arguments. These routines are discussed in Routines for Ungrouped Items . This section also describes macros which combine a group-handle and item-handle into a DBGroupAndItem and which break a DBGroupAndItem into its constituent parts.

Database: 2.5 Database Structure: The DB Map Item

You can designate a "map item" for a VM file with the routine DBSetMap() . You can recover the map item's group and handle at will by calling DBGetMap() . This is entirely separate from the file's map block; indeed, a VM file can have both a map block and a map item, and they may be set, locked, and changed independently.

The map routines are described in detail in Setting and Using the Map Item .

Database: 3 Using Database Routines

GEOS provides a wide range of routines for working with databases. The routines all require that the calling thread have the VM file open. Most routines have to be passed the VMFileHandle of the appropriate VM file.

Almost all DB routines come in two forms. The standard form takes, among its arguments, the group-handle and the item-handle of an item to be affected. The other form is designed for use with "ungrouped" items. This form takes, as an argument, the item's DBGroupAndItem structure.

In addition to the routines listed here, all of the VM chain routines can work on DB items. Simply cast the DBGroupAndItem structure to type VMChain , and pass it in place of the chain argument(s). ( VMCopyVMChain() will allocate the duplicate item as "ungrouped.") For more information about VMChain routines, see the VM chapter.

General Rules to Follow

Allocating and Freeing Groups

Allocating and Freeing Items

Accessing DB Items

Resizing DB Items

Setting and Using the Map Item

Routines for Ungrouped Items

Other DB Utilities

Database: 3.1 Using Database Routines: General Rules to Follow

There are certain rules of "memory etiquette" you should follow when using DB files. For the most part, these rules are the same as the general rules of memory etiquette.

First and foremost, try to keep as few blocks locked as possible, and keep them locked for as short a time as possible. You should not usually need to keep more than one item locked at a time. If you need another item, unlock the first one first, even if they're in the same item block. (This will cost very little time since the item block is unlikely to be swapped to disk right away.) The main reason you should have two or more items open at once is if you are directly comparing them or copying data from one to another. In this case, you should unlock each item as soon as you're done with it.

Remember that items are implemented as chunks in LMem heaps. This means, for example, that when you allocate an item (or expand an existing one), the heap it resides in (i.e. the item block) may be compacted or moved on the global heap (even if it is locked). This will invalidate all pointers to items in that item block. As a general rule, you should not allocate (or expand) items if you have any items from that group locked. Do not allocate "ungrouped" items if you have any items from any of the "ungrouped" groups locked. If you must keep an item locked, keep track of the item's memory block and chunk handle so you can use DBDeref() to get the address again.

Finally, try to keep the blocks small. Most of this is done for you. When you allocate an item, the DB manager will put it in an uncrowded item block. If all item blocks are too large, it will allocate a new one. However, you should keep items from getting too large. If individual items get into the multi-kilobyte range, you should consider storing them a different way; for example, you could make each f the larger items a VM block or a VM chain.

Database: 3.2 Using Database Routines: Allocating and Freeing Groups

DBGroupAlloc(), DBGroupFree()

You can improve DB access time by assigning items to groups such that items from the same group will generally be accessed together. This will cut down on the number of times group and item blocks will have to be swapped into memory.

To allocate a group, call DBGroupAlloc() . This routine takes one argument, namely the handle of the VM file in which to create the group. It allocates the group and returns the group-handle (i.e., the VM handle of the group block). If it is unable to allocate the group, it will return a null handle.

If you are done with a DB group, call DBGroupFree() . This routine frees the group's group block and all of its item blocks. Any attached global memory blocks will also be freed. Naturally, all items in the group will be freed as well. You can free a group even if some of its items are locked; those items will be freed immediately.

Database: 3.3 Using Database Routines: Allocating and Freeing Items

DBAlloc(), DBFree()

To allocate a DB item, call DBAlloc() . This routine takes three arguments: the handle of the VM file, the DB Group in which to allocate the item, and the size of the item (in bytes). The routine will allocate an item in one of that group's item blocks (allocating a new item block if necessary); it returns the new item's item-handle.

Remember that when you allocate a DB item, the DB manager allocates a chunk in an LMem heap (the item block). This can cause the item block to be compacted or resized; this will invalidate all pointers to items in that block. For this reason, you should not allocate items in a group while other items in that group are locked. Similarly, you should not allocate "ungrouped" items while any "ungrouped" items are locked. Instead, unlock the items, allocate the new one, and then lock the items again.

When you are done with an item, free it with DBFree() . This routine takes three arguments: the file handle, the group-handle, and the item-handle. It frees the item, making appropriate changes in the group block. If the item was the only one in its item block, that item block will be freed as well. DBFree() does not return anything. Note that you should never free a locked item since the item-block's reference-count will not be decremented (and the block will never be unlocked). Always unlock an item before freeing it. (You need not, however, unlock items before freeing their group ; when a group is freed, all of its items are automatically freed, whether they are locked or not.)

Database: 3.4 Using Database Routines: Accessing DB Items

DBLock(), DBLockGetRef(), DBDeref(), DBUnlock(), DBDirty()

To access a database item, lock it with DBLock() . This routine takes three arguments: the handle of the VM file, the item's group-handle, and the item's item-handle. The routine locks the item-block on the global heap and returns the address of the element. If the block is already locked (generally because another item in the item-block is locked), it increments the lock count.

In some circumstances it might be useful to know the global handle of the locked item-block and the chunk handle of the item. For example, if you want to set up an item as a chunk array, you will need this information. For this reason, the library provides the routine DBLockGetRef() . This routine is just like DBLock() , except that it takes one additional argument: the address of a variable of type optr. DBLockItemGetRef() writes global and chunk handles to the optr and returns the address of the locked DB item. You can now use any of the LMem routines on the item, simply by passing the optr.

Note that the memory block attached to the item block may change each time the block is locked unless you have instructed the VM manager to preserve the handle (see the VM chapter). The chunk handle will not change, even if the file is closed and reopened, unless the chunk is resized larger. (When an item is resized larger, the DB manager may choose to move the item to a different item-block, thus changing its chunk handle.) In general, if you will need this information you should get it each time you lock the item instead of trying to preserve it from one lock to the next.

If you have an optr to a locked DB item, you can translate it to an address with the routine DBDeref() . This is useful if you have to keep one item locked while allocating or expanding another item in that group. Since the locked item might move as a result of the allocation, you can get the new address with DBDeref() . In general, however, you should unlock all items in a group before allocating or resizing one there. Note that DBDeref is simply a synonym for LMemDeref() ; the two routines are exactly the same.

When you are done accessing an item, call DBUnlock() . This routine takes one argument, the address of a locked item. The routine decrements the reference count of the item's item-block. If the reference count reaches zero, the routine unlocks the block. Thus, if you lock two different items in an item block, you should unlock each item separately. As noted above, you should always unlock an item before freeing it.

If you change a DB item, you should mark the item's block as dirty by calling DBDirty() . This ensures that the changes will be copied to the disk the next time the file is saved or updated. The routine takes one argument, a pointer to an address in an item block (generally the address of an item). It will dirty the item-block containing that item. As with VM blocks, you must dirty the item before you unlock it, as the memory manager can discard any clean block from memory as soon as it is unlocked.

Database: 3.5 Using Database Routines: Resizing DB Items

DBReAlloc(), DBInsertAt(), DBDeleteAt()

Database items may be resized after allocation. They may be expanded either by having bytes added to the end or by having bytes inserted at a specified offset within the item. Similarly, items may be contracted by having bytes truncated or by having bytes deleted from the middle of the item. When an item is resized, the DB manager automatically dirties the item block (or blocks) affected.

As noted above, when an item is expanded, its item block can be compacted or moved on the item heap (even if the item is locked). Thus, pointers to all items in that item block may be invalidated, even if they are locked. For that reason, you should unlock all items in the group before expanding any of them. If you must leave an item locked, be sure to get its new address with DBDeref() . If you decrease an item's size, the item-block is guaranteed not to move or be compacted. Thus, you can safely contract locked items (or items in the same block as locked items).

To set a new size for an item, call DBReAlloc() . This routine takes four arguments: the file handle, the group-handle, the item-handle, and the new size (in bytes). If the new size is smaller than the old, bytes will be truncated from the end of the item. If the new size is larger than the old, bytes will be added to the end of the item; these bytes will not be zero-initialized.

To insert bytes in the middle of an item, call the routine DBInsertAt() . This routine takes five arguments: the file handle, the group-handle, the item-handle, the offset (within the item) at which to insert the bytes, and the number of bytes to insert. The new bytes will be inserted beginning at that offset; they will be zero-initialized. Thus, if you insert ten bytes beginning at offset 35, the new bytes will be at offsets 35-44; the byte which had been at offset 35 will be moved to offset 45. To insert bytes at the beginning of an item, pass an offset of zero.

To delete bytes from the middle of an item, call DBDeleteAt() . This routine takes five arguments: the file handle, the group-handle, the item-handle, the offset (within the item) of the first byte to delete, and the number of bytes to delete. The routine does not return anything.

Database: 3.6 Using Database Routines: Setting and Using the Map Item

DBSetMap(), DBGetMap(), DBLockMap()

A VM file can have a map block and a map item . The map can be retrieved with a special-purpose routine, even if you don't know its handle (or handles); thus, the map usually keeps track of the handles for the rest of the file. The map can be retrieved even if the file is closed and re-opened. To set a map block, use the routine VMSetMap() (see the VM chapter). To set a map item , use the routine DBSetMap() . DBSetMap() takes three arguments: the file handle, the item's group-handle, and the item's item-handle. The routine sets the file's map item to the DB item specified. A VM file can have both a map block and a map item; these are set independently.

Once you have set a map item, you can retrieve its handles with the command DBGetMap() . This routine takes one argument, namely the file's handle. It returns a DBGroupAndItem value containing the map item's handles. You can break this value into its constituent handles with DBGroupFromGroupAndItem() and DBItemFromGroupAndItem() (see Routines for Ungrouped Items ). You can also lock the map directly without knowing its handles by calling the routine DBLockMap() . This routine takes one argument, namely the file handle. It locks the map item and returns the map's address. When you are done with the map item, unlock it normally with a call to DBUnlock() .

Database: 3.7 Using Database Routines: Routines for Ungrouped Items

DBAllocUngrouped(), DBFreeUngrouped(), DBLockUngrouped(), DBLockGetRefUngrouped(), DBReAllocUngrouped(), DBInsertAtUngrouped(), DBDeleteAtUngrouped(), DBSetMapUngrouped()

Special routines are provided for working with ungrouped items. These routines are very similar to their standard counterparts. The routine DBAllocUngrouped() allocates an ungrouped item. It takes two arguments, the file handle and the size of the item to allocate. The DB manager allocates the item in one of the "ungrouped" groups and returns a DBGroupAndItem value containing the group-handle and item-handle. You can break this value into its components by calling the macros described in Routines for Ungrouped Items , or you can pass this value directly to the other "ungrouped" routines.

The rest of the routines listed above are exactly the same as their counterparts with one exception: whereas their counterparts take, among their arguments, the item's group-handle and item-handle, the ungrouped routines take a DBGroupAndItem value. Each routine's other arguments are unchanged, as is the return value.

These routines are provided as a convenience. If you allocate an ungrouped item, you are perfectly free to break the DBGroupAndItem value into its component handles, and pass those handles to the standard DB routines. Conversely, if you allocate a normal "grouped" item, you are free to combine the two handles into a DBGroupAndItem token and pass that token to the "ungrouped" routines.

Database: 3.8 Using Database Routines: Other DB Utilities

DBCopyDBItem(), DBCopyDBItemUngrouped(), DBGroupFromGroupAndItem(), DBItemFromGroupAndItem(), DBCombineGroupAndItem()

You can duplicate a DB item with the routine DBCopyDBItem() . This routine takes five arguments: the handle of the source file, the source item's group-handle, the source item's item-handle, the handle of the destination file (which may be the same as the source file), and the handle of the destination group. The routine will allocate a new item in the specified file and group. It will then lock both items and copy the data from the source item to the destination. Finally, it will unlock both items and return the item-handle of the duplicate item.

The routine DBCopyDBItemUngrouped() is the same as DBCopyDBItem() , except that it allocates an ungrouped item in the specified file. It is passed the source file handle, the DBGroupAndItem value for the source item, and the destination file handle. It allocates an ungrouped item and returns its DBGroupAndItem value.

Remember, if you are allocating the duplicate in the same group as the source, you should only call this routine when the source item is unlocked (since its item-block may be compacted when the new item is allocated). If the destination is in another block, the source item may be locked or unlocked at your preference. If it is locked when you call DBCopyDBItem() , it will be locked when the routine returns.

All of the VM chain utilities work on DB items as well as VM chains. The routines are described in the VM chapter. To use a VM chain routine, pass the item's DBGroupAndItem value. For example, VMCopyVMChain() will allocate an "ungrouped" duplicate item in the specified file and return its DBGroupAndItem value.

To build a DBGroupAndItem value from the group-handle and item-handle, use the macro DBCombineGroupAndItem() . This macro takes the two handles and returns a DBGroupAndItem value. To extract the component handles from a DBGroupAndItem value, use the macros DBGroupFromGroupAndItem() and DBItemFromGroupAndItem() . These macros are passed a DBGroupAndItem value and return the appropriate component.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Handles

One of the most important data types in GEOS is the handle . There are many different types of handles serving many different purposes. However, they all have one or both of these traits: they provide an unchanging reference to a changing (or moving) thing, and they allow geodes to access and manipulate indirectly certain structures which are maintained as internal to the kernel.

1 Design Philosophy
2 The Global Handle Table
3 Local Handles

Handles: 1 Design Philosophy

GEOS handles play many different roles. The most fundamental of these is memory access. GEOS moves memory in the global heap and swaps memory to the disk. This makes memory available as needed and means that the total memory being used by geodes can be many times larger than the physical memory in the computer; however, it means also that geodes need some way to keep track of their memory. In addition, the system needs to perform memory "housekeeping," doing such things as freeing memory when the geode which owns it exits, making sure that memory is only locked by appropriate geodes, noting which memory sections can be swapped to the hard disk, etc. This means that the kernel needs to maintain a data area for each block of memory. Although the memory itself may move around, the data about the memory should remain in the same place so the kernel can access it.

The solution to both difficulties is the handle table . The table contains entries for each block of memory. These entries keep track of the block, noting such information as where the block is in the global heap, whether it has been swapped to the disk, who its owner is, etc. The handle table does not move in memory. Each entry is referenced by a handle , which is simply the address of the table entry. When a geode needs access to a block, it can call a memory-management routine and pass the handle associated with that block. The memory manager dereferences the handle to find the data about the block and then takes any appropriate steps.

The handle table is useful for many other things. Often a geode will need to perform an action on something which is managed by the kernel. For example, the kernel keeps track of the different disk volumes it has seen. A geode may need to access a disk but not want to keep track of the volume name; or it might want to find out details (such as the size available) of a given disk. Similarly, it might want to find out how large a file which it has opened has grown. All of this information is managed by the kernel, but applications may need to access it or change it. Therefore, each of these things is given an entry in the same handle table as the memory blocks; geodes can reference these things by their handles, the same way they do memory blocks.

The handle mechanism is useful in other places. Sometimes a geode will want to subdivide a block of memory into smaller parcels (or chunks ). GEOS provides a local memory library which implements this functionality. These chunks can be moved around within a block, so applications access them by handles. The handle in this case is an offset to a handle table within that block; dereferencing the handle gives the address of the chunk. Similar techniques are used to divide Virtual Memory files. The principle is the same as with global memory handles: an unchanging handle is used to find the address of a movable thing.

There are certain things all handles have in common. They are all sixteen bits long, allowing them to fit in an 80x86 register. They also all specify addresses. The address is of an entry in a handle table; this entry contains information about the thing whose handle this is. However, handles can be divided into two basic groups:

  • Those in the global handle table.
    The global handle table is kept in main memory below the global heap. The table is never accessed directly by geodes. Instead, geodes pass the handle as an argument to kernel routines that access the handle table.
  • Those in local handle tables.
    Such handles are offsets into handle tables which may be stored in memory blocks or VM files. These handles persist as long as the entity containing the handle table does.

Handles: 2 The Global Handle Table

Many handles refer to things which are managed by the kernel. These things include global memory blocks, disk volumes, files, and many other things. Each of these things has an entry in the global handle table. Only the GEOS kernel may directly access the global handle table. If a geode wants to access one of these things, it must call a system routine and pass the thing's handle.

Global handles may not be saved across sessions of GEOS. The global handle table has to be rebuilt each time GEOS is launched. For example, when GEOS shuts down, all open files are closed. When an application restores from state, it may choose to reopen a file; it will then be given the file's handle, which will almost certainly be different from the file's handle during the previous session.

Sometimes several threads will be using the same global handle, and they will need to synchronize their access to it. They can arrange this by using the HandleP() and HandleV() routines. These routines are most often used to synchronize access to a global memory block; for this reason, they are documented in the Memory Management chapter.

Handles: 3 Local Handles

Many handles are not kept in the global handle table. For example, a block of memory may be declared as a "local-memory (LMem) heap." An LMem heap can contain many different chunks of data; the LMem manager moves these chunks around to make room for new ones. Each chunk is referenced via a chunk handle . (For more details about LMem heaps, see the Local Memory chapter.)

Local handle tables are useful when a handle needs to persist across GEOS shutdowns. For example, data in VM files is accessed by its block handle. These handles therefore need to remain the same every time the file is opened.

Some local handle tables are intended to be directly accessible to geodes. For example, LMem Chunk handles are simply offsets into the block containing the LMem heap; the address specified by that offset contains another offset value, which is the offset to the chunk. This scheme is part of the LMem API. Geodes which are written in assembly generally look up chunk handles themselves, instead of calling a system routine to translate the chunk handle into an address. (Goc code generally dereferences a chunk handle with a call to LMemDeref() .) Some other local handle tables are intended to be opaque to geodes. For example, applications treat VM block handles as opaque 16-bit tokens; to dereference a VM handle, a geode must pass it to an appropriate system routine (such as VMLock() ).

Local handles are used by many different libraries; among them are the LMem, VM, Database, and Cell libraries. The details differ with each library; for more information, see the appropriate chapter. An object-descriptor is a combination of two handles: the handle of the global memory block containing the object, and the chunk handle of the object itself. For more information, see the GEOS Programming chapter.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Machine Architecture

The GEOS operating system is state-of-the art technology based on the Intel 80x86 microprocessor. This chapter discusses some of the elements of the 80x86 design and history.

Those unfamiliar with the 8086 architecture would do well not only to read this overview but also to pick up a book about the Intel chip series. There are dozens of good books about the 80x86 chips, and this section provides only a brief review.

1 History of the 80x86
2 8086 Architecture Overview
2.1 Memory
2.2 Registers
2.3 The Prefetch Queue
2.4 Inherent Optimizations

Machine Architecture: 1 History of the 80x86

The commercial microcomputer essentially began with Intel's introduction of the 8008 chip in 1972. This was an 8-bit machine that eventually led to the 8080 (in 1974), which was the precursor to the processors that now provide the power of today's PCs.

The 8080 was an 8-bit processor that used seven general-purpose registers and an external 8-bit bus. It addressed 64K of memory; such a small memory space necessitated optimization of each instruction in an attempt to keep as many instructions as possible under one byte in length. This optimization resulted in many instructions being forced to work on specific registers; for example, most arithmetic and logical operations used the accumulator as the destination register.

In 1978, the 8086 followed, bringing 16-bit architecture to microcomputers. The designers of this new chip, however, wanted to aid in quick software development, so they gave the 8086 an instruction set and architecture reminiscent of the 8080--this allowed software based on the earlier chip to be ported to the new hardware quickly and cheaply. The 8086 offered significant advances, most notably the ability to access up to one megabyte of memory in 64K segments.

A year later, in 1979, the 8088 was introduced. This chip had all the advanced features of the 16-bit 8086 except one: Rather than using a 16-bit external data bus, the 8088 used the same 8-bit bus used in the 8080. This step backwards made it possible for systems and peripherals designed for use with the 8080 to be used with a faster, more powerful chip. The 8088 was built into IBM's personal computers for just this reason.

Both the 8088 and the 8086 can run the same software; they use the same instruction set. However, the power of the data processing offered by the 8086 eventually won out as systems and software became more complex.

Machine Architecture: 2 8086 Architecture Overview

The 8086 uses a 16-bit architecture but can handle 8-bit data as well. It accesses up to one megabyte (1024K) of memory, sixteen times more than the 8-bit 8080 could. It has thirteen registers (each 16 bits) plus a status register containing nine flags.

Memory

Registers

The Prefetch Queue

Inherent Optimizations

Machine Architecture: 2.1 8086 Architecture Overview: Memory

The memory of the 8086 begins at hexadecimal address 0x00000 and continues (each increment representing one byte) to hexadecimal address 0xFFFFF. Any two consecutive bytes constitute a word. The address of a word is the same as the address of its low byte.

Both the 8088 and 8086 have instructions that access and manipulate words, and both have instructions that access and manipulate bytes. The 8088, however, always fetches a single byte from memory due to its 8-bit bus. The 8086 always fetches two bytes, or a word; if the instruction only operates on a byte, the excess 8 bits will be ignored. This information may be useful to assembly programmers who wish to optimize the performance of their code, but C programmers can ignore it.

As stated earlier, the 8086 can access up to one megabyte of memory. This translates to 2 20 bytes, or 2 20 addresses. However, because the 8086 is designed to do 16-bit arithmetic, it can not directly access all that memory. Instead, an additional mechanism, known as segmentation , is employed.

A segment is a contiguous set of bytes no larger than 64K (2 16 bytes). It must begin on a paragraph boundary (a paragraph is a contiguous block of 16 bytes, the first of which has an address divisible by 16--that is, its address must have zeros for its four least significant bits). This allows the processor to use just 16 bits (leaving off the four zero bits) to access the first byte of a given segment.

To access bytes further into a segment, instructions use not only the 16-bit segment pointer but also a 16-bit offset. Combined, the segment pointer and offset can specify any byte in memory.

Segments may be any size up to 64K. There is no way to specify the exact size of a segment; the processor does not police memory access. Segments may obviously overlap; it is possible, for example, to have two different segments begin 16 bytes apart. Since segments can be any size up to 64K, the two in this example may well overlap.

The addressing mode described above is used in the 8088 and 8086, a large portion of the GEOS market. However, later processors such as the 80386 also employ a protected mode of memory access, in which each process running is relegated a given portion of memory and memory access is policed by the processor. These later processors also can use the segmented mode of the 8086; however, because GEOS applications should be able to run on an 8086 or 8088 machine, they should adhere to the 8086 rules.

Machine Architecture: 2.2 8086 Architecture Overview: Registers

The Intel 80x86 processors have thirteen registers plus one register containing machine status flags. The thirteen registers are separated into four logical groups by their use (they are all 16 bits):

  • Instruction Pointer
    This single register maintains the address of the current instruction being executed. This is not accessed by applications.
  • Segment Registers
    These four registers contain segment pointers.
  • Index and Pointer Registers
    These four registers contain offsets into segments.
  • General registers
    These four registers can contain any general data. They may be operated on as words or bytes.

The Segment and Index registers are used in conjunction to access memory. A program may have four segments pointed to at once: A code segment (CS), a data segment (DS), a stack segment (SS), and an extra segment (ES). These segments have various uses and purposes described in most 8088/8086 books.

The four Index registers are used as offsets into the segments pointed to in the Segment registers. They are the Stack Pointer (SP), the Base Pointer (BP), the Source Index (SI), and the Destination Index (DI). Each of these index registers has special applications with certain instructions.

The General registers are four 16-bit registers that may be used for any purpose. However, due to the early restrictions of the 8080 that carried over into the later processors, some instructions place their results or take their source data from specific registers.

All four general registers may be accessed as a word or as two separate bytes. The four registers are AX (the accumulator), BX (the base register), CX (the counter), and DX (the data register). The high byte of any of these may be accessed by substituting "H" for "X," and the low byte may be accessed by substituting "L" for "X." (The "H" stands for "high" and the "L" for "low.")

Machine Architecture: 2.3 8086 Architecture Overview: The Prefetch Queue

Programmers who code in assembly language should be familiar with how the 8086 fetches data and instructions from memory. Good programmers can take advantage of the more efficient instructions to cut down on processor time for given operations.

The 8086 takes four clock cycles to fetch a single word from memory. To speed up instruction processing, the 8086 has a prefetch queue , a buffer of six bytes into which pending instructions are put. The 8086 is also broken into two separate processing units: The Execution Unit executes instructions while the Bus Interface Unit (BIU) fetches pending instructions and stuffs them into the prefetch queue.

The main goal of this separation is to make as much use of the bus as possible, even when an instruction that does not access memory is being executed. For example, if an instruction takes eight cycles to execute and does not access memory, the BIU could meanwhile fill four instructions into the prefetch queue. Therefore, while slow instructions are still slow, the instructions after them appear to be quicker.

However, jump and branch instructions negate this prefetch effect. When a branch or jump is executed, the prefetch queue is flushed and must again be filled.

Machine Architecture: 2.4 8086 Architecture Overview: Inherent Optimizations

The 8088 and 8086 instruction set was designed with many instructions manifested in two different forms: one that allowed a wide range of possible arguments, and another that worked with one of the arguments prespecified. Since the second form did not have to load both arguments from memory, those instructions were shorter than their counterparts.

For example, the instruction and has these two forms: "And with anything," which assembles into three bytes of machine code, and "And with AX or AL," which assembles into only two bytes of machine code. Thus, the instruction and al, ffh is more efficient than and bl, ffh .

Another optimization occurs in the set of string instructions. A string is simply a set of consecutive bytes or words in memory. To repeat an operation on each element of the string could normally take a long time due to branching and checking for final conditions (branching clears out the prefetch queue). However, the string operations act as single, nonbranching instructions, so the prefetch queue is not affected by them. This speeds up string operations considerably.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Memory Management

Managing memory in a multitasking system is complex because many different entities--the kernel, libraries, and applications--are all trying to use the same memory space. To ensure that each gets all the memory it needs, without having to worry about what other memory users are doing, GEOS provides a memory manager.

1 Design Philosophy
2 The Structure of Memory
2.1 Expanded/Extended Memory
2.2 Main Memory
3 Using Global Memory
3.1 Memory Etiquette
3.2 Requesting Memory
3.3 Freeing Memory
3.4 Accessing Data in a Block
3.5 Accessing Data: An Example
3.6 Data-Access Synchronization
3.7 Retrieving Block Information
3.8 The Reference Count
4 malloc()

Memory Management: 1 Design Philosophy

Some systems make each application maintain and manage its own memory. In a multitasking system, this is not only difficult and time-consuming but also potentially dangerous, as applications can easily start trashing memory used by other processes. GEOS protects and relieves applications by providing comprehensive memory management.

The GEOS memory management system is designed to meet rigorous demands. Some of the requirements are

  • Independence of memory location
    Ideally, an application should not have to keep track of the address of each of its data items; rather, the memory management system should allow the application to reference its memory virtually.
  • Hardware independence
    Applications are much easier to write and maintain if they can ignore hardware specifics. An application should be able to specify its memory requirements in generic terms ("I need a package this big which behaves in this way") and let the memory manager worry about where it comes from.
  • Efficient use of memory
    A good operating system should be able to rearrange memory in order to gather as much space as possible. It should also be able to discard certain data or copy it to mass-storage devices (like a hard disk) if more memory is needed.
  • Management of Shared Data
    Applications should be able to share common data or code. In a multitasking system, proper synchronization of shared resources is essential to maintain data integrity.

The GEOS memory manager meets all of these needs. Applications often take advantage of the memory manager without trying; for example, the memory manager swaps methods into memory when messages need to be handled. Applications can also request memory at run-time, either by requesting raw memory from the memory manager, or by creating Virtual Memory files through the VM library (see the VM chapter).

Memory Management: 2 The Structure of Memory

The GEOS memory manager currently uses real mode memory addressing, and is designed to run on PCs with as little as 512K RAM. Typically, between 30K and 160K of that is occupied by DOS. The remaining RAM is used for the global handle table and the global heap (described below). The GEOS kernel is kept in the global heap.

Because of the constraints of real-mode memory addressing, GEOS uses memory in segments of at most 64K. Each segment may be subdivided and parceled out to fill memory requests from applications. Sometimes an application will need a seemingly contiguous section of memory which is larger than 64K; in these situations, it should use the HugeArray routines (see the VM chapter).

Expanded/Extended Memory

Main Memory

Memory Management: 2.1 The Structure of Memory: Expanded/Extended Memory

While GEOS is designed to run on a standard system, it makes efficient use of expanded and extended memory, if it is available. GEOS treats extended memory as a fast and convenient RAM disk, swapping blocks into the extended memory rather than out to a slower disk drive. This incurs none of the usual overhead of a RAM disk such as a directory because it is treated as normal memory. Expanded memory is used in a similar manner.

Memory Management: 2.2 The Structure of Memory: Main Memory

Memory available to applications is organized in a data structure called the Global Heap. The size of the global heap may vary from machine to machine and can change from session to session, but during a single execution of GEOS, the heap size remains constant. Usually, the global heap occupies approximately 450K bytes of a 640K system.

When an application requests memory, it is allocated a "block" on the heap. Blocks may be of almost any size but may not be larger than 64K. (However, the heap is most efficient when blocks are 2K-6K in size; see Memory Etiquette .) Every block is allocated a unique handle (described below) from the Handle Table; by calling a memory manager routine, the application can translate a handle to a conventional pointer.

When GEOS shuts down, all the blocks on the global heap are freed, even if they are locked or fixed. If an application will need to store the data over a shutdown, it should make the block part of a VM file, which it can then reopen when it restores from state. The GEOS kernel attaches object blocks to system VM files and takes care of storing them to state and restoring them when GEOS restarts.

Blocks and Handles

GEOS segments memory into blocks. A block is simply a number of contiguous bytes on the global heap in which code or data may be stored. Any given block may be of any size but must be less than 64K, due to the segmented addressing scheme used by the 80x86 processors. A block's size is determined when it is allocated--the process that requests the memory must specify the desired size.

To facilitate efficient memory usage and to present all applications with enough memory to function, blocks are dynamic in nature. This means that unless a block is fixed or has been locked (see Accessing Data in a Block ), there is no telling its precise address on the heap, or indeed whether it is on the heap at all. For this reason, applications are not given the address of an allocated block; rather, they are given the block's handle.

Memory handles are indexes into the global handle table. Geodes may not access the table directly. When they want to access the memory indicated by a handle, they pass the handle to the memory manager and are returned the segment address of the block's current location on the heap.

In addition to storing the actual address of the block, the handle table entry records the block's attributes, such as whether the block is discardable, swapable, or fixed. The memory manager uses all these attributes to manipulate the block.

Enabling Block Access

Dynamic memory, while providing significant benefits, poses one major problem: What happens if a block is accessed while being moved, swapped, or discarded? GEOS responds to this problem with the implementation of a system for locking and unlocking blocks. When a block is locked on the heap, the Memory Manager may not move, swap, or discard it until it is unlocked. This locking mechanism allows applications to gain control over their memory during periods of access and relinquish it when it is not in active use. Applications, however, should not leave blocks locked for extended periods as this may interfere with heap compaction.

When a process wants to use a block, it instructs the memory manager to lock the block by calling MemLock() (see MemLock() locks a block on the heap. It is passed the handle of the block; it returns a pointer to the start of the block on the heap. If the block has been discarded, MemLock() returns a null pointer. ). The application passes the handle of the block, and the memory manager locks the block and returns a pointer to the block's area in the global heap. While a block is unlocked, the memory manager can, depending on the block's category, move the block on the heap, swap it to disk or extended/expanded memory, or discard it altogether.

Types of Blocks

When a geode requests memory, it may specify how that memory is to be treated by the memory manager. The memory request includes a set of HeapFlags (see HF_FIXED The block will not move from its place in the global heap until it is freed. If this flag is off, the memory manager may move the block when it is unlocked. If it is on, the block may not be locked. This flag cannot be changed after the ) which specifies how and when the block can be moved. Broadly speaking, memory blocks can be divided into four categories:

  • Fixed
    A fixed block has the flag HF_FIXED set to one, and HF_DISCARDABLE and HF_SWAPABLE set to zero. A fixed block is allocated on the global heap and stays exactly where it was created until it is freed. An application can therefore reference data within a fixed block with normal pointers; the application does not need to use the memory manager to translate a handle into a pointer. Accessing fixed blocks is very quick since the memory never needs to be read from disk and the handle does not need to be dereferenced. However, since the memory manager cannot relocate fixed blocks, they tend to degrade the memory manger's performance; therefore, applications should generally use small amounts of fixed memory. Note that if a fixed block is resized, it may be moved on the global heap; therefore, after resizing a fixed block, a geode should reset its pointers within the block by dereferencing the block handle.
  • Moveable
    A moveable block has the flag HF_FIXED set to zero. When a moveable block is not in use (i.e. unlocked), the memory manager may move it within the global heap. This lets the memory manager keep the global heap compacted (see Maximizing Free Space in Memory ). Accessing moveable blocks is slightly slower than accessing fixed blocks since the application needs to call the memory manager to lock the block and translate the memory handle to a pointer. In addition to being moveable, a block may also be swapable and/or discardable (as described below).
  • Swapable
    A swapable block has the flag HF_FIXED set to zero and HF_SWAPABLE set to one. If a block is marked "swapable," the memory manager has the option of swapping it out of main memory (either to the hard disk, or to extended or expanded memory) when it is unlocked. This keeps space on the global heap free for other requests. If an application tries to lock a block which has been swapped out of main memory, the memory manager will copy the entire block back to the global heap, then lock it normally. By using swapable memory, geodes can potentially use much more memory than the roughly 450K available in the global heap. However, accessing an unlocked swapable block can potentially be much slower than accessing a non-swapable block, as the block may have to be copied in from the disk.
  • Discardable
    A discardable block has HF_FIXED set to zero and HF_DISCARDABLE set to one. Sometimes it doesn't make sense to copy a block to the disk. For example, many memory blocks contain code or unchanging data which is read in from the disk. It would make no sense to swap these blocks back to disk, since the data is already there. Instead, one can mark a block "discardable." This indicates that when the block is unlocked, the memory manager can, at will, discard the block. If a process attempts to lock a block which has been discarded, the memory manager will return an error. The application can then "re-allocate" memory for that block (see MemReAlloc() on To change the size of a block, call the routine MemReAlloc(). This routine is also used to allocate memory for a block that has been discarded. The routine is passed the memory handle, the new size, and the HeapAllocFlags; it returns the block's ) and copy the data back from the disk. (The system takes care of reloading discarded code resources as necessary.) A block can be both discardable and swapable (indeed, discardable blocks are usually swapable). If a block has both HF_DISCARDABLE and HF_SWAPABLE set, the memory manager can either swap the block to extended/expanded memory or discard it; it will not swap the block to the disk.

Fixed blocks must be declared as such when they are allocated, and they remain so until they are freed. However, non-fixed blocks may become or cease to be discardable or swapable after they are created. To enable or disable these characteristics, call the routine MemModifyFlags() (see MemModifyFlags() is used to change a block's HeapFlags record. It takes three arguments: The handle of the block, the HeapFlags to turn on, and the HeapFlags to clear. It returns nothing. Not all HeapFlags can be changed after a block is created ).

Maximizing Free Space in Memory

Moveable, swapable, and discardable blocks are allocated from the top of the heap using a first-fit method. Fixed blocks are allocated from the bottom of the heap. If there is not enough contiguous free memory to satisfy an allocation request, the memory manager attempts to shuffle moveable blocks in order to place all free memory together in one large mass.

This shuffling is called heap compaction. If the free space resulting from compaction still is not enough, blocks are discarded or swapped to liberate more free space, and the heap is again compacted. Because of the multitasking nature of GEOS, compaction occurs in the background and is invisible to both the user and applications. The memory manager will also periodically compact memory during periods of low activity; this helps insure that there will be memory instantly available for a sudden large demand (e.g. when an application is launched).

The compaction is not arbitrary. The kernel decides which blocks to swap or discard based on recent usage patterns. This means, for example, that if you haven't used routines in a code resource for a while, that resource is more likely to be discarded than the resources you've accessed recently. (For this reason, geodes normally isolate their initialization code in one resource, which can be discarded later.)

A block left locked for extended periods could interfere with heap compaction. Suppose, for example, that the moveable locked block in the middle of the heap were left locked during an application's entire execution. Essentially, this would cause the heap to be fractured into two subheaps, making compaction more difficult and possibly slowing the system down.

All compaction, swapping, and discarding are functions of the Memory Manager. Applications need only indicate how much space is needed and when space can be freed. Applications may also resize blocks at will; if necessary, the memory manager will compact the heap to accommodate the request.

Block Attributes

HeapAllocFlags, HeapFlags

Blocks are allocated with certain flags that help the Memory Manager manipulate memory efficiently. These flags can be found in the GEOS file heap.h , which should be included in all applications that plan to allocate memory dynamically with the memory manager routines.

The flags fall into two categories: those used when the block is allocated (stored in a record called HeapAllocFlags ) and those used to describe the block as it is manipulated (stored in a record called HeapFlags ).

The HeapAllocFlags record is used to determine what qualities the memory manager should give the block when it is first allocated. Some of these flags are also relevant when memory is being reallocated. These qualities include:

HAF_ZERO_INIT
Upon allocation, initialize data in block to zeros.
HAF_LOCK
Upon allocation, the block should be locked on the global heap. Use MemDeref() ( MemDeref() is passed the handle of a block on the global heap; it returns the block's address on the global heap. As noted above, this routine is useful when you allocate a fixed or locked block. If the block has been discarded, it returns a nul ) to get a pointer to the block.
HAF_NO_ERR
Do not return error codes; system error if block cannot be allocated. Use of this flag is strongly discouraged.
HAF_OBJECT_RESOURCE
This block is an object-block. This is set by the system only .
HAF_UI
If both HAF_OBJECT_RESOURCE and HAF_UI are set, the memory manager will set the block to allow the application's UI thread to manipulate objects in the block. This is set by the system only .
HAF_READ_ONLY
This block's data will not be modified.
HAF_CODE
This block contains executable code.
HAF_CONFORMING
If this block contains code, the code may be run by a less privileged entity. If the block contains data, the data may be accessed or altered by a less privileged entity.

Once a block is allocated, it has certain properties that govern how the Memory Manager manipulates it. These properties are determined by the HeapFlags . The HeapFlags also contain data about whether the block has been swapped or discarded. These flags are stored in the block's handle-table entry, so they can be retrieved without locking the block. To retrieve the flags, call the routine MemGetInfo() with the flag MGIT_FLAGS_AND_LOCK_COUNT. (See MemGetInfo() on MemGetInfo() is a general-purpose block information routine. It is passed two arguments: the handle of the block, and a member of the MemGetInfoType enumerated type. The return value is always word-length; however, its significance depends on th .) Some of the flags can be changed after the block has been allocated; for details, see MemModifyFlags() on MemModifyFlags() is used to change a block's HeapFlags record. It takes three arguments: The handle of the block, the HeapFlags to turn on, and the HeapFlags to clear. It returns nothing. Not all HeapFlags can be changed after a block is created . The flags include

HF_FIXED
The block will not move from its place in the global heap until it is freed. If this flag is off , the memory manager may move the block when it is unlocked. If it is on , the block may not be locked. This flag cannot be changed after the block has been allocated.
HF_SHARABLE
The block may be locked by geodes other than the owner. This flag can be changed with MemModifyFlags() .
HF_DISCARDABLE
If the block is unlocked and space is needed, the memory manager may discard it. This flag can be changed with MemModifyFlags() .
HF_SWAPABLE
If the block is unlocked and space is needed, it may be swapped to expanded or extended memory or to the hard disk. This flag can be changed with MemModifyFlags() .
HF_LMEM
The block is a local-memory block, managed by the LMem module (see the Local Memory chapter). The flag is set automatically by LMemInitHeap() . It can be changed with MemModifyFlags() ; however, an application should not change this flag.
HF_DISCARDED
The block has been discarded by the memory manager. Only the system can set or clear this flag.
HF_SWAPPED
The block has been swapped to extended or expanded memory or to the hard disk. Only the system can set or clear this flag.

Memory Management: 3 Using Global Memory

When an application needs more raw memory at run-time, it can use the memory manager kernel routines to allocate, use, and free new blocks. An application can also create a Virtual Memory file to get memory with more advanced functionality, such as the ability to save to a disk file (see the VM chapter). However, one must understand the raw memory routines in order to use VM files.

If you will be storing small pieces of information in a block, you should create a local memory heap (a special kind of memory block). LMem heaps are also useful for storing arrays of information or for storing objects. For more information, see the Local Memory chapter.

Memory Etiquette

Requesting Memory

Freeing Memory

Accessing Data in a Block

Accessing Data: An Example

Data-Access Synchronization

Retrieving Block Information

The Reference Count

Memory Management: 3.1 Using Global Memory: Memory Etiquette

The GEOS memory manager tries to fulfill every memory request. It does not enforce many rules on how the geodes should use memory. This gives each geode maximum flexibility, but it also means that the geodes have to take responsibility for their behavior; they must see to it that their memory usage does not degrade the system's performance.

First and foremost, applications should never use memory which has not been assigned to them by the memory manager. Unlike some operating systems (e.g. UNIX), GEOS does not enforce divisions between memory spaces. This allows GEOS to run on PCs which do not provide protected-mode access. It means, however, that the system will not stop an application from loading a bad address into a pointer, accessing that address, and thus writing to another memory space. An application should access only that memory which the memory manager assigns to it directly (via calls to memory routines or data-space assigned at startup) or indirectly (through other libraries, such as the VM library). This also means that you should not save pointers to movable memory after unlocking the memory block. If you are not careful, you can accidently access memory which no longer contains your data, with sometimes-fatal results.

Another rule is to minimize the amount of memory you keep locked at a time. When memory is locked, the memory manager cannot move it on the global heap or swap it out of memory. This can result in the heap becoming fragmented, which in turn makes it more likely that memory requests will fail. Keep memory unlocked as much as possible. It's worthwhile to unlock memory even if you'll be locking it again very soon; this gives the memory manager a chance to compact the heap. If you have some data which you will need locked for long periods of time, it is best to put it in a fixed block, since fixed blocks are allocated from the bottom of the global heap and thus cause less fragmentation. Of course, you should free the fixed block as soon as possible.

Try to have as few blocks locked at a time as possible. Every locked block increases the danger that the heap will be unable to comply with a memory request. Try to organize your use of memory to minimize the number of blocks you will have to keep locked at a time.

In the same vein, try to keep the amount of memory you have on the global heap to a minimum. You should declare all of your non-fixed memory as swappable and/or discardable. Remember, you can use MemModifyFlags() to change these characteristics; during a timing-critical period, you could have a block set as non-swappable, and then make it swappable again as soon as timing becomes less important. If you use Virtual Memory files, the VM Manager does much of this for you.

Try to keep your memory blocks small. Although memory blocks can, in principle, grow to 64K, the memory manager is best suited for dealing with blocks in the 2K-6K range. If you need a contiguous data space which grows beyond 8K in size, you should use the Huge Array mechanism (see the VM chapter), which automatically (and almost transparently) divides a large data space across several smaller memory blocks. If you use many small data items, you should use the Database library, which automatically distributes data items among different memory blocks, keeping each block near the optimum size. (See the DB chapter).

Memory Management: 3.2 Using Global Memory: Requesting Memory

MemAlloc(), MemAllocSetOwner(), MemReAlloc()

When you need a block of raw memory, you must use one of the kernel's memory allocation routines. You also must use kernel memory routines to change the size of a block or to reallocate space for a discarded block.

MemAlloc() creates a block and assigns a handle to it. The routine must be passed the size (in bytes) of the block to be allocated, along with the HeapAllocFlags and HeapFlags for that block. MemAlloc() will set the block's owner as the owner of the thread that called it. It will return the handle of the newly-allocated block.

MemAllocSetOwner() is the same as MemAlloc() , except that the caller explicitly sets the owner of the new block by passing the handle of the owning geode. Like MemAlloc() , it returns the handle of the new block. This is commonly used by drivers and shared libraries, which allocate memory owned by the geode which calls them. When the block's owner exits, the block will be freed, even if the block's creator is still running.

If you request a fixed block or pass the flag HAF_LOCK, the block will be allocated locked on the heap. However, the routine will still return just the memory handle. To translate this handle to a pointer, call the routine MemDeref() . MemDeref() is passed a block's handle and returns a pointer to the block (or a null pointer if the block has been discarded).

To change the size of a block, call the routine MemReAlloc() . This routine is also used to allocate memory for a block that has been discarded. The routine is passed the memory handle, the new size, and the HeapAllocFlags ; it returns the block's memory handle. You can reallocate a fixed or locked block; however, the block may be moved on the global heap to satisfy the request. (This is the only way a fixed block can move.) As with MemAlloc() , you can request that the memory manager lock the block after reallocating it; you will then have to call MemDeref() to get the address of the block. Note that if the new size is smaller than the original size, the routine is guaranteed to succeed, and the block will not move from its current position. Reallocating a block to zero bytes discards the block but preserves its handle; the block can then be reallocated later.

If the memory manager is unable to accommodate a request, it will return an error condition. The requestor can prevent error messages by passing the flag HAF_NO_ERR; this will result in a system error if the memory cannot be allocated. Passing HAF_NO_ERR is therefore strongly discouraged.

Memory Management: 3.3 Using Global Memory: Freeing Memory

MemFree()

When you are done using a block, you should free it. Every block takes up space in the global handle table; if blocks are not freed, the handle table may fill up, causing a system error. Furthermore, non-swapable, non-discardable blocks take up space in the global heap until they are freed.

To free a block, call MemFree() and pass the handle of the block to be freed. The block will be freed even if it is locked. Therefore, if the block can be used by other threads, you should make sure no other thread has locked the block before you free it.

You can also set a reference count for a block. When a block's reference count drops to zero, the memory manager will automatically free it. This is useful if several threads will be accessing the same block. For more information, see The Reference Count .

When a geode exits, all blocks owned by it are automatically freed.

Memory Management: 3.4 Using Global Memory: Accessing Data in a Block

MemLock(), MemUnlock()

Because the memory manager is constantly reorganizing the global heap, applications must have a way of making sure a block stays put when they want to use it. Applications must also have a way of recalling swapped blocks when they are needed.

These requirements are met by the memory manager's locking scheme. Whenever you need to access data in a non-fixed block, you must first lock it. This will cause the memory manager to copy the block back into the global heap if it had been swapped; the memory manager will not move, swap, or discard a block while the block is locked.

Any block may be locked several times. Each lock increments the block's lock count (to a maximum of 255 locks per block), and each unlock decrements it. The memory manager can only move the block when the lock count is zero.

One warning about locking blocks: Do not try to lock a block which was allocated as fixed. Attempting to do so will result in a system error. If you need to translate a fixed-block handle to a pointer, call MemDeref() .

MemLock() locks a block on the heap. It is passed the handle of the block; it returns a pointer to the start of the block on the heap. If the block has been discarded, MemLock() returns a null pointer.

Immediately after you are done using a block, you should unlock it by calling MemUnlock() . It is better to lock and unlock the same block several times than to retain control of it for an extended period, as locked blocks degrade the performance of the heap compaction mechanism. To unlock a block, call MemUnlock() , passing the handle of the block to be unlocked. MemUnlock() decrements the lock count.

A block may be locked by any of the threads run by its creator; if the block is sharable, it may be run by any other thread as well. There is nothing in the MemLock() routine to prevent different threads from locking a block at the same time, causing potential synchronization problems. For this reason, if threads will be sharing a block, they should use the synchronization routines (see Data-Access Synchronization ).

Memory Management: 3.5 Using Global Memory: Accessing Data: An Example

Allocating and Using a Block shows how to allocate a block, lock it, access a word of data in the block, and unlock the block. This example shows the basic principles of using blocks.

Code Display 8-1 Allocating and Using a Block

/*
 * Variable Declarations
 */
MemHandle 	myBlockHandle;
char 	charArray[50], *blockBaseAddress;
/* First, we allocate a block of the desired size. Since we'll use the block right
 * away, we allocate the block already locked.
 */
myBlockhandle = MemAlloc(					/* MemAlloc returns the block handle */
            2048,		/* Allocate 2K of memory */
            HF_SWAPABLE,		/* HeapFlags: Make block swapable */
            HAF_ZERO_INIT|HAF_LOCK);			/* HeapAllocFlags: Initialize
                         * the memory to zero & lock it */

/* The block is already locked on the global heap. However, we do not have the
 * block's address; we just have its handle. Therefore, we need to call a routine
 * to dereference the handle. */
blockBaseAddress = (char *) MemDeref(myBlockHandle);						/* Returns a ptr to base of
                         * block */
/* Enter some data in the block */
strcpy(blockBaseAddress,
    "I can resist anything except temptation.\n   --Wilde"
/* We're done with the block for the moment, so we unlock it. */
MemUnlock(myBlockHandle); /* blockBaseAddress is now meaningless */
/* Here we do some other stuff . . . */
/* Now we want to use the block again. First we have to lock it. */
blockBaseAddress = (byte *) MemLock(myBlockHandle);						/* Returns a ptr to locked
                         * block */
/* Read a string from the block: */
strcpy(charArray, blockBaseAddress);
/* We're done with the block, so we free it. Note that we can free the block
 * without unlocking it first.
 */
MemFree(myBlockhandle); /* myBlockHandle is now meaningless */

Memory Management: 3.6 Using Global Memory: Data-Access Synchronization

MemThreadGrab(), MemThreadGrabNB(), MemThreadRelease(), MemLockShared(), MemUnlockShared(), MemLockExcl(), MemDowngradeExclLock(), MemUpgradeSharedLock(), MemUnlockExcl(), HandleP(), HandleV(), MemPLock(), MemUnlockV()

Blocks can have the property of being sharable--that is, the same block may be locked by threads owned by several different geodes. However, this can cause data synchronization problems; one application can be changing data while another application is trying to use it. To prevent this, GEOS provides semaphore routines. Only one thread can have the block's semaphore at a time. When an application wants to use a shared block, it should call a routine to set the semaphore. Once the routine returns, the application can use the block; when it is done, it should release the block's semaphore so other applications can use the block.

Note that use of semaphores is entirely voluntary by each application. Even if thread A has the semaphore on a block, thread B can call MemLock() on the block and start changing it. However, all threads using shared blocks ought to use the semaphore routines to prevent confusion.

There are several different sets of routines which can be used to control a block's semaphore. The different sets of routines make different trade-offs between faster operation and increased flexibility. Any one block should be accessed with only one set of routines; different threads should all be using the same routines to access a given block, and a thread should not switch from one set of routines to another for a particular block. If this rule isn't followed, results are undefined. All of these routines access the HM _otherInfo word of the handle table entry; if the block will be locked by any of these routines, you must not alter that word. None of these routines is used to access object blocks; instead, special object-block locking routines are provided.

Most geodes should use MemThreadGrab() , MemThreadGrabNB() , and MemThreadRelease() to access sharable blocks. These routines provide the maximum protection against deadlock in exchange for a slightly slower execution.

MemThreadGrab() gives the thread the semaphore for the block in question and locks the block. It is passed the handle of the block and returns the block's address on the global heap. If no thread has the block's semaphore, it gives the semaphore to the calling thread. If the calling thread already has the semaphore, a "semaphore count" is incremented; the thread will not release the semaphore until it has been released as many times as it has been grabbed. (For example, two different objects run by the same thread could each grab the semaphore; the semaphore would not be released until each object called MemThreadRelease() .) If another thread has the semaphore, MemThreadGrab() blocks until it can get the semaphore; it then increments the semaphore, locks the block, and returns the address.

MemThreadGrabNB() is the same as MemThreadGrab() , except that it never blocks. If you call MemThreadGrabNB() while another thread has the semaphore, the routine will immediately return an error. MemThreadGrabNB() takes the handle of the block; it increments the semaphore, locks the block, and returns the block's address on the heap.

MemThreadRelease() releases a block grabbed by either MemThreadGrab() or MemThreadGrabNB() . It is passed the block's handle. It unlocks the block and decrements the block's semaphore.

One common situation is that several threads may need to read a block but only once in a while will an application need to write to the block. In this case, there is no synchronization problem in having several readers at once; however, if any thread is writing, no other thread should be reading or writing. For this situation, GEOS provides this set of MemLock routines: MemLockShared() , MemUnlockShared() , MemLockExcl() , and MemUnlockExcl() .

These routines, like the others, maintain a queue of threads which have requested thread access. The difference is that any number of readers can have access at once. When a thread wants read access, it calls MemLockShared() . If the queue is empty and the block is unlocked or locked for reading, the routine returns and the thread is given shared access; otherwise, the thread is blocked, and the request goes on the queue. When a routine is finished reading the block, it should call MemUnlockShared() .

When a routine needs to write to a block, it should call MemLockExcl() . If nobody has locked the block (and thus the queue is empty), the thread will immediately be given exclusive access; otherwise, the thread will block, and the request will go on the queue. When the thread no longer needs write access, it should call MemUnlockExcl() .

When all threads with access to a block have released their locks, the queued thread with the highest priority will be awakened and given the lock on the block. If that thread had requested shared access, all other threads on the queue that had requested shared access will also be awakened and given locks.

A thread can change its lock from shared to exclusive or vice versa. If a thread has an exclusive lock on a block, it can change the lock to shared by calling MemDowngradeExclLock() . This routine takes one argument, namely the block's global handle. It changes the lock to "shared" and wakes up all sleeping threads which are waiting for shared access. For convenience, MemDowngradeExclLock() returns the address of the block; however, the block is guaranteed not to move.

If a thread has shared access and wants exclusive access, it can call MemUpgradeSharedLock() . If the thread has the only lock on the block, its lock will be changed to "exclusive" (even if there are writers on the queue). If any other threads have the block locked, the routine will release the thread's lock and put the thread on the queue. When the thread comes to the head of the queue, the routine will wake the thread and give it an exclusive lock. The routine returns the block's address on the global heap. Note that the block may be altered or moved during this call if the call blocked.

Once a thread has been given a shared lock, there is nothing to prevent it from altering (or even freeing) the block. The routines rely on good citizenship by the threads using them.Also, if a thread isn't careful, there is a great danger of deadlock. If (for example) a thread requests exclusive access to a block when it already has access, the thread will deadlock: it will block until the threads with access all release the block, but it can't release its own lock because it is blocked. If you may need to have multiple locks on a block, use the MemThread routines, which check for these situations.

There are other sets of routines which can be used to access a block's semaphore. As noted, a block should be accessed via just one set of routines. These routines provide less protection against deadlock than the MemThread routines do; however, they have a slightly faster response time.

A more primitive group of routines is HandleP() , HandleV() , MemPLock() , and MemUnlockV() . These routines function much like the MemThread routines. HandleP() grabs the block's semaphore and returns; it does not lock the block. This makes it very useful for working with fixed blocks (which cannot be locked). HandleV() releases the block's semaphore and returns; it does not unlock the block. Note, however, that HandleP() will block if any thread controls the semaphore, even the thread that called HandleP() . If a thread calls HandleP() while it controls the semaphore, it will block until the semaphore is released, but it can't release the semaphore because it has blocked. Thus, the thread will deadlock, and no other thread will be able to get the semaphore. Therefore, a thread should use HandleP() only if it is very confident that it will never try to double-set the semaphore.

Usually, when a thread grabs a block's semaphore, it needs to have the block locked on the heap. For this reason, GEOS provides the routines MemPLock() and MemUnlockV() . MemPLock() simply calls HandleP() and then calls MemLock() . MemUnlockV() , correspondingly, calls MemUnlock() and then calls HandleV() . These routines are completely compatible with HandleP() and HandleV() ; for example, a thread could grab and lock a block with MemPLock() , then unlock it with MemUnlock() and release it with HandleV() .

HandleP() and HandleV() are general-purpose handle routines. They can be called on any type of global handle. For example, if two threads need to synchronize their access to a file, they can lock and unlock the file handle with HandleP() and HandleV() . However, they are most commonly used with memory blocks.

Memory Management: 3.7 Using Global Memory: Retrieving Block Information

MemDeref(), MemPtrToHandle(), MemGetInfo(), MemOwner(), MemModifyFlags(), HandleModifyOwner(), MemModifyOtherInfo()

GEOS offers several routines to retrieve and change information about a block. Each of these routines has a full entry in the Routine Reference Book.

MemDeref() is passed the handle of a block on the global heap; it returns the block's address on the global heap. As noted above, this routine is useful when you allocate a fixed or locked block. If the block has been discarded, it returns a null pointer.

MemPtrToHandle() , passed a pointer to a locked block, finds the handle of that memory block.

MemGetInfo() is a general-purpose block information routine. It is passed two arguments: the handle of the block, and a member of the MemGetInfoType enumerated type. The return value is always word-length; however, its significance depends on the MemGetInfoType value passed:

MGIT_SIZE
Returns the size of the memory block (in bytes).
MGIT_FLAGS_AND_LOCK_COUNT
Upper byte is the number of locks on the block; lower eight bits are the block's HeapFlags record (see HF_FIXED The block will not move from its place in the global heap until it is freed. If this flag is off, the memory manager may move the block when it is unlocked. If it is on, the block may not be locked. This flag cannot be changed after the ).
MGIT_OWNER_OR_VM_FILE_HANDLE
If the block is attached to a GEOS Virtual Memory file, MemGetInfo() returns the VM file handle. Otherwise, it returns the GeodeHandle of the owning thread.
The MemOwner() routine also returns the owning geode of the passed block; however, if the block belongs to a VM file, the file's owner will be returned instead of the file handle.
MGIT_ADDRESS
Returns the block's segment address, if it is on the global heap; otherwise, it returns zero. If the block is resized or is not locked, the address may change without warning. Note that the segment address is returned as a word, not as a pointer; this is of limited utility in C.
MGIT_OTHER_INFO
Returns the HM_otherInfo word from the block's handle table entry. The usage of this word varies depending on the block's nature; for example, object blocks store the handle of their burden thread here.
MGIT_EXEC_THREAD
This is useful for object blocks only. Returns the handle of the thread executing methods for objects in the block.

MemModifyFlags() is used to change a block's HeapFlags record. It takes three arguments: The handle of the block, the HeapFlags to turn on, and the HeapFlags to clear. It returns nothing. Not all HeapFlags can be changed after a block is created; only HF_SHARABLE, HF_DISCARDABLE, HF_SWAPABLE, and HF_LMEM can be so changed.

HandleModifyOwner() changes the geode owning a given block. It takes two arguments, namely the handle of the block and the handle of the new owner. It returns nothing. If the block is not sharable, only the owner of a block can change the block's owner.

MemModifyOtherInfo() changes the HM_otherInfo word of the block's handle table entry. It takes two arguments: The handle of the block, and one word of data to store in the HM_otherInfo field. It returns nothing.

Memory Management: 3.8 Using Global Memory: The Reference Count

MemInitRefCount(), MemIncRefCount(), MemDecRefCount()

Sometimes several different threads will need to look at the same block of memory. For example, a single thread might need to send the same information to objects in several different threads. The simplest way to do that would be to write the information in a global memory block and pass the block's handle to the objects. However, it's a bad idea to allocate global blocks for longer than necessary, since this uses up handles. It therefore becomes important to free these blocks when everyone's done with them.

GEOS provides a simple mechanism for managing this. Every block can have a reference count . When a reference count for a block reaches zero, the block is automatically freed. That way, for example, if an object needed to send the same information to five objects, it could write the data to a block, give that data block a reference count of five, and send the block's handle to the objects. Each object, when it finished accessing the data, would decrement the data block's reference count. When all five objects had decremented the reference count, the block would be freed.

You must be careful about a few things while using the reference count mechanism. First of all, the reference count is kept in the HM _otherInfo field of the block's handle table entry. For this reason, you must not use the reference count routines if you will be using HM _otherInfo for any other purpose. In particular:

  • The block may not be an object block; the HM_otherInfo field for an object block stores the handle of the burden thread for that block.
  • You may not use any of the data-access synchronization routines described in Data-Access Synchronization , since all of those routines store the semaphore in HM _otherInfo . You should generally use the reference count only for blocks that will not be changed until they are freed, so that data synchronization will not be an issue.

Once the reference count is decremented to zero, the block is immediately freed. Once a block is freed, its handle may be used for something else. If you try to increment or decrement the reference count of the block, the results are undefined. For this reason, you should make sure that the reference count does not reach zero until all threads are done with the block. One way to arrange for this is to have a single thread do all the decrementing. For example, an object might set the reference count to five, and send the handle to five other objects. Each of these objects, when finished with the block, would send a message back to the first object, which would decrement the reference count. As an alternative, you could have each of the objects decrement the count itself when it was finished. In this case, the first object would have to assume that the block was freed as soon as it sent out all of the messages, since it would have no way of knowing when the other objects would be finished with the block.

Finally, since the reference count is stored in HM _otherInfo , it has a maximum value of (216-1). If you try to increment the reference count past this value, the results are undefined. This will not be a problem for most applications.

To set up a reference count for a block, call MemInitRefCount() . This routine takes two arguments: the handle of a global memory block, and the reference count for that block. The reference count must be greater than zero. MemInitRefCount() sets the block's HM _otherInfo field to the specified reference count. MemInitRefCount() does not return anything.

To increment the reference count, call MemIncRefCount() . This routine is passed a single argument, namely the handle of the global memory block. The routine simply increments HM _otherInfo . It does not return anything.

To decrement the reference count, call MemDecRefCount() . This routine is passed the handle of a global memory block. It decrements the block's HM _otherInfo field. If the field reaches zero, MemDecRefCount() will immediately free the block. The routine does not return anything.

Memory Management: 4 malloc()

malloc(), calloc(), realloc(), free()

GEOS provides support for the Standard C memory allocation routines. However, support is limited by the nature of the 80x86 and the GEOS memory management system.

A geode can request memory with malloc() or calloc() . When a geode does this for the first time, the memory manager will allocate a fixed block and return a pointer to within the fixed block. (This block is actually a special kind of LMem heap.) Because the memory is in a fixed block, the geode does not need to access it with handles; it can use the pointer directly. If the block fills up, the manager can allocate another fixed block for these requests.

However, there are some problems with this. The main problem is that fixed blocks degrade the memory manager's performance. The more a geode uses malloc() , the more memory is tied up in fixed blocks. And, as always, contiguous memory is limited to 64K by the 80x86 segmented addressing scheme.

Most of the time, geodes should use other types of memory allocation. For allocating small chunks of data, applications should use LMem routines or techniques built on top of them (database items, chunk arrays, etc.); for larger chunks of memory, applications should use memory manager routines or HugeArrays. However, to help writers port C code to GEOS, malloc() and its relatives are available.

To get a stretch of contiguous memory, use the routines malloc() or calloc() . malloc() takes one argument, a size in bytes; it returns a void pointer to that many bytes of fixed memory. calloc() takes two arguments: a number of structures, and the size of each such structure. It allocates enough memory for that many structures and returns a void pointer to the memory. Both malloc() and calloc() zero-initialize the memory when they allocate it.

If a routine wants to change the size of memory allocated with malloc() or calloc() it can use realloc() . realloc() takes two arguments: a pointer to a piece of memory allocated with malloc() or calloc() , and a new size in bytes. It returns a void pointer to the memory, which may have been moved to satisfy the request. If it could not satisfy the request, it returns a null pointer, and the original memory is untouched. Note that the pointer you pass realloc() must be the same pointer that was returned by malloc/calloc ; if (for example) you allocate 100 bytes and are returned 008Bh:30h, and try to resize it by passing 008Bh:40h to realloc() , inappropriate memory will be affected, and the results are undefined.

If you decrease the size of a memory section with realloc() , the routine is guaranteed to succeed. If you increase the size, it may fail; if it does succeed, the new memory will not be zero-initialized. Reallocating a block down to zero memory is the same as freeing it. You can pass a null pointer to realloc() along with the size; this makes realloc() function like malloc() .

When you are done with memory allocated by malloc -family routines, you should call free() to free the memory for other malloc() calls. As with realloc() , you must pass the same pointer that you were originally given.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Local Memory

The GEOS memory manager is well suited to dealing with blocks of memory in the 2K-6K range. However, for small amounts of memory, the manager's overhead becomes significant. For this reason, GEOS provides local memory (or LMem ) routines. These routines let an application allocate a block of memory and designate it as a local memory heap . The application can then request small amounts of memory from this heap. The LMem library automatically reorganizes the heap when necessary to make space available.

The local memory routines are also used to manipulate objects. All objects are special LMem chunks which are stored in a special type of LMem heap (called an object block ). Most of the routines which work on chunks can also be used on objects.

1 Design Philosophy
2 Structure of a Local Memory Heap
2.1 The Local Heap
2.2 Chunks and Chunk Handles
2.3 Types of LMem Heaps
3 Using Local Memory Heaps
3.1 Creating a Local Heap
3.2 Using Chunks
3.3 Contracting the LMem Heap
3.4 Example of LMem Usage
4 Special LMem Uses
4.1 Chunk Arrays
4.2 Element Arrays
4.3 Name Arrays

Local Memory: 1 Design Philosophy

The GEOS memory manager is designed to deal with blocks of memory which are measured in kilobytes. Every memory block needs an entry in the global handle table; each such entry takes up 16 bytes. This memory overhead is insignificant for a 10K code block but very significant for, for example, a 100 byte database entry. Furthermore, GEOS allows a limited number of global handles. If an application were to use global blocks for small amounts of data, it could use up too many handles.

On the other hand, if an application were to store many small pieces of data in a single global block, it might have to write elaborate memory-management routines. It would have to make sure that it could resize a piece of data at will and shuffle other data to make room. This could force programmers to spend a lot of their time writing support code.

For these reasons, GEOS provides local memory routines. Applications can designate a block of memory as a local-memory heap and use LMem routines to request small amounts ( chunks ) of data. The local memory library automatically shuffles chunks in that heap to make room for new requests. Applications can change the size of a chunk at will. The cost of using the LMem routines is one added layer of indirection and a small amount of memory overhead for each chunk.

The LMem routines have another advantage. They provide a uniform way of managing small pieces of memory. This lets GEOS add functionality which applications can use in a variety of ways. For example, GEOS implements an array-management scheme based on LMem chunks; this scheme comes complete with a modified Quicksort routine. Similarly, all GEOS objects are stored in object blocks, which are a special kind of local-memory heap. This makes it easy to add or delete objects dynamically.

Local Memory: 2 Structure of a Local Memory Heap

A local memory heap looks and acts much like the global heap. However, it is contained entirely within a single memory block. This block is initialized with a 16-byte LMemBlockHeader (described on The offset within the block where the local heap's handle table will begin. The local heap will fill the space to the end of the block; any data between the LMemBlockHeader and the indicated offset will be zero-initialized. If an application wil ), a local memory handle table, and a local memory heap. Optionally, a space for data may be allocated between the header and the handle table.

Each allocated section of memory within a local heap is called a chunk , and the handles of these chunks are called chunk handles .

Chunks comprise a chunk of data preceded by a word that contains the length of the data (in bytes). When the heap is created, a certain number of chunk handles will be allocated. If a chunk is requested after all of these chunks have been given out, the local memory routines will enlarge the LMem handle table, relocating chunks as necessary. Unused chunks are stored in a linked list.

The Local Heap

Chunks and Chunk Handles

Types of LMem Heaps

Local Memory: 2.1 Structure of a Local Memory Heap: The Local Heap

An application may designate any block as a local memory heap. The block may or may not be fixed, swapable, or discardable, as the application desires.

A block may have other data besides the local heap. When a local heap is created, an offset into the block can be specified. The heap will put its header structure at the beginning of the block and the handle table at the specified offset; everything in between will be left untouched by the LMem routines. The offset must be larger than the standard header or else may be zero, indicating that the default offset should be used.

Before performing any operations on a local memory heap, an application must lock the heap the way it would any other block. Some local memory routines may need to resize the block; this may cause the block to be moved on the global heap. Therefore, you should assume that these routines may invalidate all pointers to the relevant block. The descriptions of routines which may behave this way will contain a warning to that effect. When you initialize an LMem heap, you can specify that it should never be resized; this is advisable if the heap is in a fixed block.

A virtual-memory file block may contain an LMem heap. For details on this, see the Virtual Memory chapter.

Local Memory: 2.2 Structure of a Local Memory Heap: Chunks and Chunk Handles

Just as blocks on the local heap are accessed with handles, chunks are accessed via chunk handles. Each chunk handle is an offset into the block containing the local memory heap; thus, the segment address of the locked heap, combined with the chunk handle, make up a pointer to a location within the local memory heap's chunk handle table. That location contains another offset which, when combined with the segment address of the block, composes a far-pointer to the actual chunk.

Chunks are movable within the local heap; whenever a chunk is created or resized, the local memory manager may move any chunks in that heap. There is no locking mechanism for chunks; thus, creating or resizing a chunk can potentially invalidate pointers to all the other chunks, forcing the application to again dereference the handles for these chunks. Be warned that many message handlers can cause heap compaction. As a general rule, you should not save chunk addresses around message sends; instead, dereference the chunk handles to get the current address.

Chunks are aligned along dwords. This speeds up chunk moves and similar operations. This means that when you request a chunk, its size may be slightly larger than you request.

Objects are special kinds of LMem chunks. An optr is simply the global memory handle of an LMem heap followed by the chunk handle of an object. For this reason, many LMem routines come in two formats: one which is passed an optr, and one which is passed the global and chunk handles. There is also a macro, ConstructOptr() , which is passed a memory handle and a chunk handle and returns an optr constructed from the two.

Local Memory: 2.3 Structure of a Local Memory Heap: Types of LMem Heaps

LMemType, LMemFlags

In addition to being used for general memory needs, local memory heaps perform many specific functions in the GEOS system. When an LMem heap is created, a member of the LMemType enumerated type is passed, indicating to what use the LMem heap will be put. Several types are available:

LMEM_TYPE_GENERAL
The LMem heap will be used for general data storage, possibly including a chunk, name, or element array. When an application creates an LMem heap, it will almost always be of type "General" or "Object."
LMEM_TYPE_WINDOW
Windows are stored in memory as LMem heaps. The header contains information about the window; each region in the window is stored as a chunk. Applications will not directly create Window heaps.
LMEM_TYPE_OBJ_BLOCK
Objects are stored in object blocks, which are LMem heaps. An object block has some extra header information and contains one chunk which contains only flags. All the objects in the block are stored as chunks on the heap. Applications can directly create object blocks; for more information, see the GEOS Programming chapter.

LMEM_TYPE_GSTATE
A GState is an LMem heap. The GState information is in the header, and the application clip-rectangle is stored in a chunk. Applications do not directly create GState blocks; rather, they call a GState creation routine, which creates the block. (See the Graphics Environment chapter.)

LMEM_TYPE_FONT_BLOCK
Font blocks are stored as LMem heaps. Applications do not create font blocks directly.
LMEM_TYPE_GSTRING
Whenever a GString is created or loaded, a GString LMem heap is created, and elements are added as chunks. The heap is created automatically by the GString routines; applications should not create GString blocks. (See the Graphics Environment chapter.)

LMEM_TYPE_DB_ITEMS
The Virtual Memory mechanism provides routines to create and manage database items

, short pieces of data which are dynamically allocated and are saved with the VM file. These items are stored in special database LMem heaps, which are created in special database blocks in the VM file. Applications do not directly allocate DB blocks; rather, they call DB routines, which see to it that the blocks are created. (See the Database chapter.)

When an LMem heap is allocated, certain flags are passed to indicate properties the heap should have. Some of these flags are passed only for system-created heaps. The flags are stored in a word-length record ( LocalMemoryFlags ); the record also contains flags indicating the current state of the heap. The LocalMemoryFlags are listed below:

LMF_HAS_FLAGS
Set if the block has a chunk containing only flags. This flag is set for object blocks; it is usually cleared for general LMem heaps.
LMF_IN_RESOURCE
Set if the block has just been loaded from a resource and has not been changed since being loaded. This flag is set only for object blocks created by the compiler.
LMF_DETACHABLE
Set if the block is an object block which can be saved to a state file.
LMF_DUPLICATED
Set if block is an object block created by the ObjDuplicateResource() routine. This flag should not be set by applications.
LMF_RELOCATED
Set if all the objects in the block have been relocated. The object system sets this when it has relocated all the objects in the block.
LMF_AUTO_FREE
This flag is used by several object routines. It indicates that if the block's in-use count drops to zero, the block may be freed. This flag should not be set by applications.
LMF_IN_MEM_ALLOC
This flag is used in error-checking code to prevent the heap from being validated while a chunk is being allocated. For internal use only--do not modify .
LMF_IS_VM
Set if LMem heap is in a VM block and the block should be marked dirty whenever a chunk is marked dirty. This flag is automatically set by the VM code when an LMem heap is created in or attached to a VM file. This flag should not be set by applications.
LMF_NO_HANDLES
Set if block does not use chunk handles. A block can be set to simulate the C malloc() routine; in this case, chunks are not relocated after being created, so chunk handles are not needed. Ordinarily, these blocks are created by the malloc() routine, not by applications. (See the discussion of malloc() in the Memory Management chapter.)

LMF_NO_ENLARGE
Indicates that the local-memory routines should not enlarge this block to fulfill chunk requests. This guarantees that the block will not be moved by a chunk allocation request; however, it makes these requests more likely to fail.

LMF_RETURN_ERRORS
Set if local memory routines should return errors when allocation requests cannot be fulfilled. If the flag is not set, allocation routines will fatal-error if they cannot comply with requests. This flag is generally clear for expandable LMem blocks, since many system routines (such as ObjInstantiate() ) are optimized in such a way that they cannot deal with LMem allocation errors.
LMF_DEATH_COUNT
This field occupies the least significant three bits of the flag field. It means nothing if the value is zero. If it is non-zero, it indicates the number of remove-block messages left which must hit BlockDeathCommon before it will free the block. This flag is used some error-checking code in the kernel.
STD_LMEM_OBJECT_FLAGS
This is a constant which combines the LMF_HAS_FLAGS and LMF_RELOCATED flags. These flags should be set for all object blocks.

Local Memory: 3 Using Local Memory Heaps

Local memory heaps are much like the global heap and are accessed in much the same way. Local heaps are simple to create and manage.

Many applications will not need to use local heaps directly; instead, they will use more advanced data-management mechanisms based on local heaps, such as chunk arrays or database blocks. However, even if you use these mechanisms, you should be familiar with this section; this will help you understand how the other mechanisms work.

Remember that every local memory heap resides in a global memory block. All the rules for using memory blocks apply. (See the Memory Management chapter.)

Creating a Local Heap

Using Chunks

Contracting the LMem Heap

Example of LMem Usage

Local Memory: 3.1 Using Local Memory Heaps: Creating a Local Heap

LMemInitHeap(), MemAllocLMem()

Before you create a local heap, you must first allocate and lock a block on the global heap using MemAlloc() and MemLock() . (Alternatively, you can allocate a fixed block.) Then, you must call LMemInitHeap() . (If you are creating a local heap to hold objects, you should use the UserAllocObjBlock() routine instead; it's much simpler to use.)

LMemInitHeap() creates and initializes a local memory heap. It must be passed several arguments:

  • The handle of a locked or fixed block which will contain the local memory heap. It may be moveable, discardable, both, or neither; it should not have the flag HF_LMEM set. If the LMem heap will be resizable, the block may be of any size; the LMem routines will enlarge it if necessary. If the heap will not be resizable (i.e. LMF_NO_ENLARGE is passed), it must be created large enough to accommodate the heap.
  • The offset within the block where the local heap's handle table will begin. The local heap will fill the space to the end of the block; any data between the LMemBlockHeader and the indicated offset will be zero-initialized. If an application will not need a fixed data space, it should specify an offset of zero; the handle table will then be put immediately after the LMemBlockHeader . If the created block will be an object block, then it will need room for an ObjLMemBlockHeader structure. Often, when an application needs a fixed data space, it will define a special structure, the first element of which is an LMemBlockHeader , and will pass the size of that structure as the offset. It can then access the fixed data by using the fields of the structure. If the offset specified is less than the size of an LMemBlockHeader , LMemInitHeap() will return an error.
  • A member of the LMemType enumerated type, specifying the type of block to be created (see In addition to being used for general memory needs, local memory heaps perform many specific functions in the GEOS system. When an LMem heap is created, a member of the LMemType enumerated type is passed, indicating to what use the LMem heap wil ).
  • A word of LocalMemoryFlags for the heap. (See When an LMem heap is allocated, certain flags are passed to indicate properties the heap should have. Some of these flags are passed only for system-created heaps. The flags are stored in a word-length record (LocalMemoryFlags); the record also .)
  • A word specifying the number of chunk handles to leave room for in the starter handle table. When these chunks have all been allocated, the local memory manager will expand the LMem handle table to create more chunk handles. No matter how big the starter handle table is, the heap will initially contain just one chunk (whose size is specified by the next argument). Applications should generally pass the constant STD_INIT_HANDLES.
  • A word specifying the amount of space to be assigned to the chunk created when the heap is initialized. When more space is needed, the chunks will expand to the end of the block, and (if necessary) the block itself will be expanded. Applications should generally pass the constant STD_INIT_HEAP.

LMemInitHeap() creates the LMemBlockHeader and the chunk handle table. It also creates a single free chunk; more chunks will automatically be created as needed. It may resize the block passed (unless the flag LMF_NO_ENLARGE is passed); therefore, any pointers to the block may become invalid. It does not return anything.

If you want to create a memory block and initialize it as an LMem heap in one operation, call MemAllocLMem() . This routine takes two arguments: a member of the LMemType enumerated type, and the amount of space to leave for the header (again, a zero size indicates that the default header size should be used). MemAllocLMem() allocates a movable, swapable block in the global heap, then initializes an LMem heap in that block. If you specify an LMemType of LMEM_TYPE_OBJ_BLOCK, MemAllocLMem() will pass the STD_LMEM_OBJECT_FLAGS flags; otherwise, it will pass a clear LocalMemoryFlags record.

Local Memory: 3.2 Using Local Memory Heaps: Using Chunks

LMemAlloc(), LMemDeref(), LMemFree(), LMemGetChunkSize(), LMemReAlloc(), LMemInsertAt(), LMemDeleteAt(), LMemDerefHandles(), LMemFreeHandles(), LMemGetChunkSizeHandles(), LMemReAllocHandles(), LMemInsertAtHandles(), LMemDeleteAtHandles()

Once a local heap has been initialized, you can allocate, use, and free chunks at will. Chunks can only be manipulated while the block containing the LMem heap is fixed or locked on the global heap.

LMemAlloc() allocates a new chunk on the local heap. It is passed the handle of the block containing the heap and the size of the chunk needed. LMemAlloc() returns the handle of the new chunk (which must then be dereferenced before the chunk is used). The size requested will be rounded up as necessary to ensure that the chunks are dword-aligned. An additional two bytes will be allocated to store the size of the chunk; these bytes will be before the data. This routine may compact the chunks on the local heap, so all pointers to that heap will be invalidated; they will have to be dereferenced by their chunk handles. Furthermore, the block itself may be moved (if LMF_NO_ENLARGE is not set). Even fixed blocks may be moved if they need to expand to accommodate new chunks.

All of the following routines come in two forms. As noted, an optr is simply the handle of an object block, followed by the object's chunk handle. For this reason, most LMem routines come in two slightly different formats: one where the chunk is specified with an optr, and one where it is specified with the two handles. In all other ways, the two versions of each routine are identical. Indeed, in assembly there is only a single version of each routine; the only difference is in how the C routines take their parameters.

Once you have allocated a chunk, you must dereference its chunk handle in order to use it. You can do this with LMemDeref() . This routine takes a singe parameter, namely the optr. It returns a pointer to the data portion of the chunk (after the size word). This pointer will remain valid until the block is unlocked or until a routine is called which can cause block resizing or heap compaction (e.g. LMemAlloc() ). Since these routines can invalidate chunk-pointers, it is important that data-synchronization routines be used if more than one thread is accessing the heap; otherwise, one thread may cause the heap to be shuffled while another thread is trying to read from it. The version which takes handles is named LMemDerefHandles() .

When you are done using a chunk of memory, you should free it with LMemFree() . This routine is passed an optr; it does not return anything. It does not resize the block or shuffle chunks; therefore, pointers to other chunks will not be invalidated by LMemFree() . The version which takes handles is named LMemFreeHandles() .

You can find out the size of any chunk by calling the routine LMemGetChunkSize() . This routine is passed an optr; it returns the size of the chunk in bytes (not counting the chunk's size word). The version which takes handles is named LMemGetChunkSizeHandles() .

Chunks can be resized after creation. The Boolean routine LMemReAlloc() takes two arguments, namely an optr and the new size of the chunk. If the new size is larger than the old one, bytes will be added to the end of the chunk; chunks may be shuffled and the block may be resized, so all pointers to chunks will be invalidated. The new bytes will not be zero-initialized. If the new chunk size is smaller than the old one, the chunk will be truncated; pointers to chunks will not be invalidated. This routine will fail only if the LMem heap ran out of space and could not be resized. In this case, it will return non-zero without changing the chunk. If it succeeds, it returns zero. The version which takes handles is called LMemReAllocHandles() .

You can add bytes inside a chunk with the Boolean routine LMemInsertAt() . This routine takes three arguments: the optr, an offset within the chunk, and the number of bytes to add. The new space is added beginning at the specified offset; it is initialized to zeros. This may cause chunks to be shuffled and/or the block to be expanded; pointers to chunks are therefore invalidated. Note that it is your responsibility to make sure that the offset within the chunk really is in the chunk; otherwise, results are undefined. If LMemInsertAt() fails (because the LMem heap ran out of space and could not be expanded), it returns non-zero without changing the chunk; otherwise it returns zero. The version which takes handles is named LMemInsertAtHandles() .

You can delete bytes within a chunk with the routine LMemDeleteAt() . This routine takes three arguments: the optr, the offset within the chunk of the first byte to be deleted, and the number of bytes to delete. This routine does not invalidate pointers to chunks. The routine does not return anything. Note that it is your responsibility to make sure that all the bytes to be deleted are within the chunk, i.e. that the offset and number of bytes passed do not specify bytes that are beyond the end of the chunk. If you fail to do this, results are undefined. The version which takes handles is named LMemDeleteAtHandles() .

Local Memory: 3.3 Using Local Memory Heaps: Contracting the LMem Heap

LMemContract()

The local memory manager routines ordinarily take care of heap compaction. However, you can also order compaction at will.

The routine LMemContract() compacts the heap and then frees all the unused heap space (by truncating the block with the LMem heap). The routine takes one argument, namely the handle of the (locked or fixed) block containing the LMem heap. It shuffles all the chunks, thus invalidating pointers to chunks; however, it is guaranteed not to move the block on the global heap.

Local Memory: 3.4 Using Local Memory Heaps: Example of LMem Usage

At first, the local memory techniques can seem tricky. This section contains an example of LMem usage in Goc. The example shows the basic principles of LMem usage.

Code Display 16-1 LMem Usage in GOC

/*
 * Declarations
 */
/* We'll want to have a fixed data area at the start of the block. That area will
 * have to start with an LMemBlockHeader, but after that, we can put whatever we
 * want. To make it easy to access the fixed data, we define a structure.
 */
typedef struct {
    LMemBlockHeader		MLMBH_standardHeader;
    float		MLMBH_someData;
    float		MLMBH_someMoreData;
    char		MLMBH_someChars[10];
} MyLMemBlockHeader;
MyLMemBlockHeader 			*thisHeapsHeader;
MemHandle			thisHeapsHandle;
ChunkHandle			firstChH, secondChH;
char 			*firstChPtr, *secondChPtr;
int			i;
/*
 * Code
 */
/* We have to create the LMem heap. First, we create the block: */
thisHeapsHandle = MemAlloc(					/* MemAlloc returns the block handle */
            2048,		/* Allocate 2K; can grow as necessary */
            HF_SWAPABLE,		/* Make block swapable. LMemInitHeap()
                     * will add the flag HF_LMEM. */
            HAF_ZERO_INIT | HAF_LOCK); 				/* Zero & lock the block
                             * upon allocation */
LMemInitHeap(thisHeapsHandle,					/* Pass handle of locked block */
        LMEM_TYPE_GENERAL,			/* Allocate a general heap */
        0,			/* Don't pass any flags */
        sizeof(MyLMemBlockHeader), 	/* Offset to leave room for header */
        STD_INIT_HANDLES,			/* Standard # of starter handles */
        STD_INIT_HEAP);		/* Allocate standard amt. of empty heap */
/* The block is still locked; we can initialize the fixed data section. */
thisHeapsHeader = (MyLMemBlockHeader *) MemDeref(thisHeapsHandle);
thisHeapsHeader->MLMBH_someData = 3.1415926;
/* Now, we allocate some chunks. This invalidates pointers to this heap (such as
 * thisHeapsHeader), since chunk allocation may cause the heap to be resized (and
 * thus moved). The block must be locked when we do this.
 */
firstChH = LMemAlloc(					/* LMemAlloc returns a chunk handle */
        thisHeapsHandle,			/* Pass handle of block . . . */
        100);			/* . . . and number of bytes in chunk */
secondChH = LMemAlloc(thisHeapsHandle, 50);
/* Now, we start writing data to a chunk: */
firstChPtr = (char *) LMemDerefHandles(thisHeapsHandle, firstChH);
for(i = 0; i <= 30; i++)
   firstChPtr[i] = 'x';
/* We can insert 10 bytes into the middle of the second chunk. This may cause the
 * chunks or blocks to be shuffled; all pointers are invalidated
 */
LMemInsertAtHandles(thisHeapsHandle, secondChH, 						/* Block & chunk handles */
        20,				/* Insert after 20th byte */
        30);				/* Insert 30 null bytes */
/* If we want to access the first chunk, we need to dereference its handle again:
 */
firstChPtr = (char *) LMemDeref(thisHeapsHandle, firstChH);
for(i = 1; i <= 15; i++)
    firstChPtr[(i<<1)] = 'o';
/* When we're done with a chunk, we should free it. This does not invalidate
 * any pointers to other chunks.
 */
LMemFreeHandles(thisHeapsHandle, firstChH);
/* If we won't be using an LMem heap for a while, we should unlock it the way we
 * would any block: */
MemUnlock(thisHeapsHandle);
/* When we're done with the LMem heap, we can just free it. */
MemFree(thisHeapsHandle);

Local Memory: 4 Special LMem Uses

Local memory heaps are used for many purposes in GEOS. Objects are stored in special LMem heaps, called object blocks ; windows, GStrings, and many other things are implemented as LMem heaps; and LMem heaps can be used to manage arrays of data. Most of these things are done by the system, transparent to the user; however, some of these things can be called on directly by applications.

Most of these techniques are described in their own chapters. One use of LMem heaps will be described in this chapter; namely, the use of LMem chunks to store special arrays of data via the Chunk Array routines. The chapter will also describe special purpose variants of the Chunk Array: the Element Array and the Name Array.

Chunk Arrays

Element Arrays

Name Arrays

Local Memory: 4.1 Special LMem Uses: Chunk Arrays

Very often an application will need to keep track of many different pieces of data and access them by an index. An application can do this in the traditional way, i.e., allocate a block of memory and set it up as an array. However, GEOS provides a mechanism which is often more suitable: the chunk array . The chunk array routines let you dynamically insert or delete elements in the middle of an array; you can get a pointer to an arbitrary element (specified by its index number); you can sort the array based on any arbitrary criterion. The array can be specified as "uniform-size" (all elements the same size, specified when the chunk array is created), or "variable-size" (each element can be created at an arbitrary size and can be resized at will). Note that either type of array can grow or shrink dynamically; while the elements may be of a fixed size, the array need not be.

The chunk array is implemented on top of the LMem routines. The entire array is a single chunk in a local memory heap (hence the name). It therefore has a maximum total size of somewhat less than 64K, and memory efficiency drops significantly if it is larger than roughly 6K. If you need a larger array, you should use a Huge Array (see the VM chapter). If you will be using the chunk array routines, you should include chunkarr.h .

Structure of the Chunk Array

A chunk array is contained in a single chunk in an LMem heap. It begins with a special header structure which specifies certain characteristics of the chunk array: the number of elements in the array, the size of each element (or zero for variable sized arrays), the offset from the start of the chunk to the first element, and the offset from the first element to the end of the array. The header is a structure of type ChunkArrayHeader ; an application can examine the fields of this structure directly or by using chunk array routines. The creating application can request that the chunk array contain some blank space between the header and the first element; it can use that space however it likes.

Elements can be referenced by index number. The first element has index number zero. You can translate element numbers to pointers, and vice versa, by calling ChunkArrayElementToPtr() and ChunkArrayPtrToElement() (see Adding, Removing, and Accessing Elements ).

A uniform-size chunk array has a simple structure. After the header (and the extra space, if any) come the elements. They follow one after another, with no free space between them. An application can request a pointer to any element, specified by index; the chunk array routine multiplies the index by the size of each element and adds the product to the address of the first element, producing a pointer to the requested element. An application can also proceed through the array, going from one element to the next, without specifically requesting pointers.

The structure of a variable-size chunk array is a little bit more complicated. After the header (and extra space) is a lookup-table of two-byte entries, each containing the offset from the start of the chunk to the appropriate element. The chunk array routines maintain this table automatically. When an application wants to reference a given element, the chunk array routine doubles the index number and adds it to the offset to the start of this table; this produces the address of a table entry, which itself contains the offset to the actual element. Effectively, there are two arrays with the same number of elements; the first is a uniform-sized array of offsets to the entries in the second (variable-sized) array. However, this is transparent to the application, which merely requests a pointer to element n and is returned that pointer. It does not need to know or care about the extra table constructed by the chunk array routines.

Creating a Chunk Array

ChunkArrayCreate(), ChunkArrayCreateAt(), ChunkArrayCreateAtHandles(), ChunkArrayHeader

Chunk arrays are created in local memory heaps. An LMem heap can have several chunk arrays, and it can have other types of chunks besides chunk arrays; however, since an LMem heap is limited to 64K in total size, the more you put in the heap, the less room there is for a chunk array to grow.

The first step in creating a chunk array is to create an LMem heap. Create the heap the same way you would any general LMem heap. The heap should probably be left resizable, since that way it will be able to grow to accommodate the chunk array.

Once you have created the heap, use the routine ChunkArrayCreate() to create the chunk array. This routine will allocate a chunk for the chunk array, initialize the array, and return the chunk handle. Since the routine allocates a chunk, it can cause chunk shuffling or heap resizing; thus, all pointers to the heap are invalidated.

ChunkArrayCreate() takes four arguments:

  • The handle of a locked block containing the LMem heap;
  • The size of each element, or zero if the elements are variable-size; and
  • The size of the header for the chunk array. If you pass zero, the routine will automatically leave enough space for a ChunkArrayHeader structure; if you pass a non-zero argument, it must be larger than the size of a ChunkArrayHeader . If you will need a data space at the start of the array, it is a good idea to define a structure, the first element of which is a ChunkArrayHeader , and use that structure to access the fixed data area; you can then pass the size of that structure as this argument.
  • A field of ObjChunkFlags which act as those passed to LMemAlloc() .

There is another version of this routine which creates a chunk array in an existing chunk. This routine, ChunkArrayCreateAt() , takes three arguments, namely an optr indicating the chunk, the size of each element, and the size of the header. It allocates a chunk array in that chunk, resizing it if necessary, and returns the chunk's handle. Any data in the chunk may be overwritten (except for whatever data falls in the header area after the ChunkArrayHeader ). The version which takes handles is called ChunkArrayCreateAtHandles() .

When you are done with a chunk array, you can free it with LMemFree() the way you would any other chunk.

Adding, Removing, and Accessing Elements

ChunkArrayAppend(), ChunkArrayAppendHandles(), ChunkArrayInsertAt(), ChunkArrayInsertAtHandle(), ChunkArrayDelete(), ChunkArrayDeleteHandle(), ChunkArrayDeleteRange(), ChunkArrayDeleteRangeHandles(), ChunkArrayElementResize(), ChunkArrayElementResizeHandles(), ChunkArrayElementToPtr(), ChunkArrayElementToPtrHandles(), ChunkArrayPtrToElement(), ChunkArrayPtrToElementHandle(), ChunkArrayGetElement(), ChunkArrayGetElementHandles()

The chunk array library provides a high-level interface for working with the arrays. The application specifies a request in general terms (e.g., "Insert a new element before element five," "Give me a pointer to element 20"). The routines take care of the low-level memory management: inserting bytes, swapping chunks to make room, etc. These routines depend on the LMem routines; therefore, the block containing the LMem heap must be locked or fixed when you use these routines (or any other chunk array routines).

When you call a chunk array routine, you must pass the handles specifying the chunk. As with other routines which act on a chunk, these routines come in two formats: one in which the chunk is specified with an optr, and one in which it is specified with the global memory handle and the chunk handle.

Adding elements to a chunk array is easy. To add an element to the end of a chunk array, use the routine ChunkArrayAppend() . This routine automatically updates the ChunkArrayHeader (and the lookup-table, if elements are variable-sized). The routine takes two arguments, namely the optr and the size of the new element. The size must be greater than zero. If the array elements are uniform-sized, the size argument is ignored. The routine will resize the chunk to accommodate the new element, update its header table (and lookup table if necessary), and return a pointer to the element. Since the chunk is resized, all other chunk pointers (and pointers within the chunk array) are invalidated. The version which takes handles is named ChunkArrayAppendHandles() .

You can also add an element within the middle of an array. The routine ChunkArrayInsertAt() takes three arguments, namely the optr, a pointer to the location at which to insert the element, and the size of the element (ignored for uniform-sized arrays). The size must be greather than zero. The routine will insert the appropriate number of bytes at that location in the chunk, update the header and lookup-table, and return a pointer to the new element. Pointers to chunks are invalidated. The version which takes the chunk handle, ChunkArrayInsertAtHandle() , is slightly unusual in that it is passed the chunk handle but not the global memory handle; the routine gets the segment address of the chunk from the passed pointers.

When you are done with an element, free it with ChunkArrayDelete() . This routine takes two arguments, namely the optr and a pointer to the element to be deleted. It shrinks the chunk; thus, it is guaranteed not to shuffle chunks, so chunk pointers remain valid (though pointers to elements within the chunk array will be invalidated if the elements come after the deleted element). Again, the handle version, ChunkArrayDeleteHandle() , is passed the chunk handle but not the global handle.

If you need to delete several consecutive elements, call ChunkArrayDeleteRange() . This routine takes three arguments: the optr to the chunk array, the index of the first element to delete, and the number of elements to delete. The specified elements will be deleted. As with ChunkArrayDelete() , the global and local heaps will not be shuffled. The handle version, ChunkArrayDeleteRangeHandles() , is passed the global handle of the LMem heap and the chunk handle of the chunk array instead of the optr to the chunk array.

Elements in variable-sized arrays can be resized after creation with the routine ChunkArrayElementResize() . This routine takes three arguments: the optr, the element number, and the new size. The new size must be greater than zero. The routine resizes the element and updates the lookup table. If the new size is larger than the old, null bytes will be added to the end of the element; chunks may be shuffled, so all chunk pointers are invalidated. If the new size is smaller than the old, the element will be truncated. This is guaranteed not to shuffle chunks, so pointers to chunks remain valid, though pointers within the array may be invalidated. The version which takes handles, ChunkArrayElementResizeHandles() , is passed both the global memory handle and the chunk handle.

If you have the index of an element and you want to access that element, use the routine ChunkArrayElementToPtr() . It takes three arguments: the optr, the element number, and a pointer to a word-length variable. The routine writes the size of the element in the variable and returns a pointer to the element. (If you are not interested in the element's size, pass a null pointer.) It does not change the chunk in any way, so no pointers are invalidated. If you pass an index which is out-of-bounds, ChunkArrayElementToPtr() will treat it as the index of the last element. (The constant CA_LAST_ELEMENT is often used for this purpose.) However, the error-checking version will always fatal-error if passed the index CA_NULL_ELEMENT (i.e. 0xffff). The version which takes handles is named ChunkArrayElementToPtrHandles() .

If you know the address of an element and you need to find out its index, use the routine ChunkArrayPtrToElement() . This routine takes two arguments, namely the optr and a pointer to the element. It returns the index number of the element. The version which takes the chunk handle, ChunkArrayPtrToElementHandle() , is passed the chunk handle and the pointer but not the global memory handle.

You can copy an element to a specified location with the routine ChunkArrayGetElement() . This routine takes three arguments: the optr, the element number, and a pointer to a buffer big enough to hold the entire element. The routine will copy the element to the specified buffer. The version which takes handles is called ChunkArrayGetElementHandles() .

Chunk Array Utilities

ChunkArrayGetCount(), ChunkArrayGetCountHandles(), ChunkArrayZero(), ChunkArrayZeroHandles(), ChunkArrayEnum(), ChunkArrayEnumHandles(), ChunkArrayEnumRange(), ChunkArrayEnumRangeHandles(), ChunkArraySort(), ArrayQuickSort(), bsearch()

To find out how many elements are in a chunk array, use the routine ChunkArrayGetCount() . This routine takes one argument, namely the optr. It returns the number of elements in the array. It does not change the array; no pointers are invalidated. The version which takes handles is named ChunkArrayGetCountHandles() .

If you want to delete all elements in the array but you don't want to free the array itself, use the routine ChunkArrayZero() . This routine takes one argument, namely the optr. It does not return anything. This routine deletes all the elements in the array, updates the header and lookup tables, and resizes the chunk. Since the chunk is truncated, no chunks are swapped, so no chunk pointers are invalidated (though pointers to elements are, naturally, invalidated). The version which takes handles is named ChunkArrayZeroHandles() .

If you want to apply a function to every element in the array, use ChunkArrayEnum() . This routine is passed three arguments:

  • The optr.
  • A pointer to a Boolean callback routine. This routine will be called for each element in succession. This routine must be declared _pascal. If the callback routine ever returns true , ChunkArrayEnum() will immediately return with value true (without checking any more elements). If the callback routine returns false for every element, ChunkArrayEnum() will return with value false.
  • A pointer which is passed to the callback routine.

The callback routine should be written to take two arguments:

  • A pointer to the start of an element
  • The pointer passed to ChunkArrayEnum()

ChunkArrayEnum() can be used for many different purposes, depending on the nature of the callback routine. For example, it can perform some action on every element (in which case it ought always to return false ); it can analyze the data in the various elements; it can check to see if any element meets some criterion. If it needs to write its results, it might do so at the location indicated by the pointer. ChunkArrayEnum() will not cause heap shuffling unless the callback routine causes it; thus, if the callback routine avoids shuffling the heap, it can (for example) be passed a pointer to a chunk in the same LMem heap as the chunk array. The version which is passed handles is named ChunkArrayEnumHandles() .

There is another version of ChunkArrayEnum() which acts on a range of elements. This routine is called ChunkArrayEnumRange() . This routine takes the same arguments as ChunkArrayEnum() , plus two more: a start index, and a number of elements to enumerate. ChunkArrayEnumRange() calls the callback routine for the element with the specified index, and for every element thereafter until it has processed the specified number of elements. You can have it enumerate to the end of the chunk array by passing a count of 0xffff. There is a version which takes handles called ChunkArrayEnumRangeHandles() .

You can get into trouble if more than one thread of execution is accessing a single ChunkArray and one or both of these threads is using one of the ChunkArrayEnum...() routines. The error-checking version of the kernel detects this situation and generates a CHUNK_ARRAY_ENUM_INSERT_OR_DELETE_RUN_BY_MULTIPLE_THREADS warning if it detects such. If your code is getting this warning, you should add some synchronization between threads. While one thread is enumerating through chunks in the chunk array, the other should do nothing to enumerate the same array, nor alter the array in any way.

The chunk array library also provides a sorting routine, ChunkArraySort() . This routine performs a modified Quicksort on the array, using insertion sorts for subarrays below a certain size. The sort routine takes three arguments: the chunk array's optr, a pointer to a comparison function, and a word of data which is passed to the comparison function.

Whenever the sort routine needs to decide which of two elements comes first, it calls the comparison routine. The comparison routine takes two arguments, namely the optr and the word of data passed to ChunkArraySort() . It returns a signed word with the following significance: If the first of the elements should come first in the sorted array, it returns a negative number; if the first element ought to come after the second, it should return a positive number; and if it doesn't matter which comes first, it should return zero. You can write a general-purpose comparison routine which can compare based on any of several parts of the element, and you can use the data word to instruct it which part to sort on; or you can use the data word to tell it to sort in ascending or descending order. ChunkArraySort() does not cause heap shuffling as long as the comparison routine does not. The routine which takes handles is called ChunkArraySortHandles() .

ChunkArraySort() is based on a more general array sorting routine, ArrayQuickSort() . ChunkArraySort() reads data about the array from the array header and passes the information to ArrayQuickSort() . You can call ArrayQuickSort() directly for arrays which are not chunk arrays, provided all elements are of uniform size. ArrayQuickSort() takes five arguments: a pointer to an array (which should be locked or fixed in memory), the number of elements in the array, the size of each element, a pointer to a callback routine (which has exactly the same format as the ChunkArraySort() callback routine), and a data word to pass to that callback routine. It does not return anything.

Note that ChunkArraySort() is currently implemented only for chunk arrays with fixed-sized elements.

The bsearch() routine conducts a binary search on an a sorted array.

Example of Chunk Array Usage

This section contains an example of how a chunk array might be used. It shows several common chunk array actions, including sorting the array.

Code Display 16-2 Example of Chunk Array Usage

/*
 *	Declarations (not in any routine)
 */
/* We want to store some data right after the chunk array header, so we define our
 * own header structure. Data in this structure (except for the ChunkArrayHeader
 * proper) will not be affected by the chunk array routines. */
typedef struct {
    ChunkArrayHeader			standardChunkArrayHeader;
    int			someData;
    float			someMoreData;
} MyChunkArrayHeader;
/* For simplicity, we define a structure which will be used for each element in the
 * array. (This is entirely optional.) */
typedef struct {
    char	someText[80];
    int	anInteger;
    float	aFloat;
} MyElementStructure;
/* We define some values to pass to the sort routine. The routine will sort by a
 * different field depending on what value it's passed. */
#define	SORT_ARRAY_BY_STRING				0
#define	SORT_ARRAY_ASCENDING_BY_INT				1
#define	SORT_ARRAY_DESCENDING_BY_FLOAT				2
/* This is the routine we will use to sort the array. We pass the address of this
 * routine to ChunkArraySort(), which will call this routine to compare elements.
 * It returns a negative number if the first element should come before the second
 * in the sorted array, and a positive integer if the second should come before the
 * first. If the elements can be in either order, it returns zero. */
sword _pascal MyElementCompareRoutine(
    MyElementStructure 			*e1,	/* Address of first element */
    MyElementStructure 			*e2,	/* Address of second element */
    word		valueForCallback) 		/* Datum passed in to ChunkArraySort() */
{
    /* We sort differently depending on what the value of valueForCallback is.
     * That way, we can use this one routine for all our sorting.
     */
    switch(valueForCallback) {
        case SORT_ARRAY_ASCENDING_BY_INT:
        /* Compare the elements based on their integer fields. Smaller int
         * comes first.*/
        if (e1->anInteger < e2->anInteger)
            return(-1);
        else if (e1->anInteger > e2->anInteger)
            return(1);
        else return(0);
        break;
        case SORT_ARRAY_DESCENDING_BY_FLOAT:
        /* Compare the elements based on their float fields. Larger float
         * comes first.*/
        if (e1->aFloat > e2->aFloat)
            return(-1);
        else if (e2->aFloat < e2->aFloat)
            return(1);
        else return(0);
        break;
        case SORT_ARRAY_BY_STRING:
        /* In this case, we call the localization routine to compare the
         * two strings. The localization routine has the same return
         * conventions as this routine, so we return its result directly.
         */
            return(LocalCmpStrings(e1->someText, e2->someText, 40));
            break;
        default:
        /* If we get here, we were passed a bad callback word. The callback
         * routine therefore does not express a preference in ordering the
         * two elements; it shows this by returning zero.
         */
    } /* end of switch */
}
/* All of the above appears in some declaration section. The code below might
 * appear in any routine which creates a chunk array. First, the declarations:
 */
MemHandle			blockWithHeap;
chunkHandle			myChunkArray;
MyChunkArrayHeader			*chunkArrayAddress;
MyElementStructure			*currentElement;
/* Now the code. Here, blockWithHeap has already been set to hold the block handle
 * of an LMem heap.
 */
MemLock(blockWithHeap); /* Always lock LMem heap before acting on it */
myChunkArray = ChunkArrayCreate(blockWithHeap,
            sizeof(MyElementStructure), /* Size of each element */
            sizeof(MyChunkArrayHeader) /* Size of header */
            0);
/* Let's write some data into our part of the header. We need the array's address: */
chunkArrayAddress = LMemDerefHandles(blockWithHeap, myChunkArray);
chunkArrayAddress->someData = 42;						/* This data won't be affected */
chunkArrayAddress->someMoreData = 2.7182818;						/* by chunk array actions */
/* Now, let's create an element: */
currentElement = ChunkArrayAppendHandles(blockWithHeap, myChunkArray, 0);
    /* That invalidates chunkArrayAddress */
currentElement->anInteger = 1999;
currentElement->aFloat = 1.4142135;
strcpy(currentElement->someText, 				"Work is the curse of the drinking class.\n" \
                "  --Oscar Wilde")
/* We're done with the array for the moment, so we unlock it: */
MemUnlock(blockWithHeap);
/* Let's assume that several other elements are created now. */
/* . . . */
/* Now we need to sort the array: */
MemLock(blockWithHeap);
ChunkArraySortHandles(blockWithHeap, myChunkArray,
        SORT_ARRAY_ASCENDING_BY_INT, /* this is passed to comp. routine */
        MyElementCompareRoutine);
/* Array is now sorted! */

Local Memory: 4.2 Special LMem Uses: Element Arrays

Sometimes an application will create an array with a high duplication rate; that is, the array may contain many identical elements. This can be inefficient if the duplication rate is very high or elements are very large. For this reason, GEOS provides a special variant of the chunk array known as the element array . Every element in an element array has a reference count. When you insert an element, the insertion routine checks whether an identical element already exists in the array. If it does, the routine does not add another copy; instead, it increments the reference count of the element already in the array and returns its element index. If no such element exists, the routine copies the new element into the array, gives it a reference count of 1, and returns its element number. The application may specify a comparison routine which determines whether an element already exists in the array; or it may instruct the insertion routine to do a byte-level comparison.

Note that elements in an element array may be of fixed, uniform size, or they may be of variable size (just as with chunk arrays). When you create an element array, you must specify the size of each element; specifying a size of zero indicates that the elements are of variable size.

Members of an element array keep their index numbers until they are freed. If an element is deleted, the element array routines actually just resize the element to zero and add it to a free list. This means that an element with index 12 might not be the thirteenth element in the array, as it would in a chunk array (remember, indexes start with zero); there might be freed elements before it. For this reason, we speak of an element in an element array having a "token" instead of an index; you should generally consider a token to be an opaque value. Nevertheless, in most situations, element array tokens behave just like chunk array indexes.

When you delete a reference to an element, its reference count is decremented. If the reference count reaches zero, the routine calls an application-specified callback routine to delete the element itself.

Note that adding an element to an element array requires a linear search through the existing elements; thus, element arrays are inefficient for large numbers of elements, if elements will be continually added. Accessing elements, however, takes constant time, since the element array routines can quickly translate an element's token into the offset to that element. Thus, it takes no longer to access an element in an element array than it does to access one in a chunk array.

Creating an Element Array

ElementArrayCreate(), ElementArrayCreateAt(), ElementArrayCreateAtHandles(), ElementArrayHeader

To create an element array, call the routine ElementArrayCreate() . Like ChunkArrayCreate() , it takes three arguments: the LMem heap's handle, the size of each element (or 0 for variable-sized elements), and the size to leave for the array header. The routine allocates a chunk in the LMem heap and initializes it as an element array. There is one significant difference: Element arrays begin with an ElementArrayHeader , a structure whose first component is a ChunkArrayHeader . If you are allocating free space between the header and the array, make sure to leave enough room for an ElementArrayHeader . If you do not need to allocate free space, you can pass a header size of zero, as with ChunkArrayCreate() .

Note that when you specify the size of each element in the element array, you must include the size of each element's header. Thus, if each element's body were six bytes long, you would tell ElementArrayCreate() that the size of each element is "6 + sizeof( RefElementHeader )".

There is another version of this routine, ElementArrayCreateAt() , which creates the element array in a pre-existing chunk. This routine takes three arguments: an optr indicating the chunk, the size of each element, and the size of the header. It creates the element array in the specified chunk, resizing it if necessary. Any data in the chunk may be overwritten (except for whatever data falls in the header area after the ElementArrayHeader ). There is also a version which takes handles instead of an optr; it is called ElementArrayCreateAtHandles() .

The routine returns the handle of the newly-created element array. It can cause heap compaction or resizing; therefore, all pointers to the heap are invalidated.

Adding an Element

ElementArrayAddElement(), ElementArrayAddElementHandles(), ElementArrayAddReference(), ElementArrayAddReferenceHandles()

Adding an element to an element array is somewhat different from adding one to a chunk array. To add an element to a chunk array, you merely call the append routine, then write the element into the allocated space. If you want to add an element to an element array, you must first write out the data for the element in a buffer. You then pass the address of this data to ElementArrayAddElement() , which compares your new element with the elements already in the array, and copies it into the array if necessary.

ElementArrayAddElement() takes four arguments:

  • An optr indicating the element array;
  • The address of the element to copy into the array;
  • A pointer to a callback comparison routine (see below), or a null pointer to do a byte-wise comparison;
  • A dword of data to pass to the comparison routine.

You may have your own criteria for deciding whether an element should be copied into an array. For example, elements in the array may have three data fields; perhaps you count two elements as matching if the first two data fields match. For this reason, ElementArrayAddElement() lets you specify your own comparison routine. The callback routine should be a Boolean routine, declared _pascal, which takes three arguments:

  • The address of the element to add;
  • The address of an element in the array to compare the new element to;
  • The callback data dword passed to ElementArrayAddElement() .

ElementArrayAddElement() calls the callback routine to compare the new element to each element in the array. If the callback routine ever returns true , ElementArrayAddElement() has found a matching element in the array; it will increment that element's reference count and return its index. If the callback routine returns false for every element, ElementArrayAddElement() copies the new element into the array and gives it a reference count of 1. It returns the element's index; the element will keep that index until it is freed. Note that there is no way to specify where in an element array a new element should be added. If there are free spaces in the array, the new element will be created in the first free space; otherwise, it will be appended to the end of the array.

If you want to do a bytewise comparison, pass in a null pointer as the callback routine. ElementArrayAddElement() will then do a bytewise comparison of the elements, treating two elements as equal only if every byte in the element bodies matches. (It's okay if the two elements have different headers and reference counts; the bytewise comparison routine only compares the element bodies.) The bytewise comparison is implemented as a machine-language string instruction; it is therefore very fast.

If you know that the element you want to add is already in the array, call ElementArrayAddReference() . This routine simply increments the reference count of a specified element; it does no comparisons. It is therefore much faster than ElementArrayAddElement() .

Both of these routines have counterparts which are passed handles instead of an optr; these counterparts are named ElementArrayAddElementHandles() and ElementArrayAddReferenceHandles() .

Accessing Elements in an Element Array

ElementArrayElementChanged(), ElementArrayElementChangedHandles()

Elements in element arrays are accessed in almost the same way as elements in chunk arrays. There is one major difference. Each element in an element array begins with a RefElementHeader structure, which contains the element's reference count. For this reason, it is a good idea to declare special structures for your elements and have the first component of that structure be the RefElementHeader structure (as in the code sample below).

Code Display 16-3 Structure for Element Array Elements

/* We need to declare two different structures. One will have all the data fields
 * in the body of the element; it will be called "MyElementBody". The other
 * structure begins with the "RefElementHeader" structure, which must begin every
 * element in an element array; the other field of the structure is a
 * "MyElementBody" structure. When you create an element, you pass the address of
 * the MyElement structure to ElementArrayCreateElement().
 */
typedef struct {
    word		amount;		/* This has the element's data fields */
    float		interestRate;
    char		description[20];
} MyElementBody;
typedef struct {
    RefElementHeader			header; 	/* We won't use this--it holds ref count */
    MyElementBody		body;
} MyElement;

Note that if you change an element, this may make it identical to another element in the element array; in this case, the two could be combined into one. To check for this situation, call ElementArrayElementChanged() . This routine takes four arguments: the optr to the element array, the token for the element changed, a callback comparison routine, and a dword of data which is passed to the callback routine. ElementArrayElementChanged() checks to see if the element is identical to any other element in the array. It calls the comparison routine to compare elements. (You can force a bytewise comparison by passing a null function pointer; this comparison will make sure that all bytes in the element body match, though it's okay if the elements have different values in their RefElementHeader structures.) If it matches another element, the two elements will be combined; i.e., the element passed will be deleted, and the matching element will have its reference count increased appropriately. The matching element's token will be returned; you will have to change any references to the old element appropriately. If no match is found, the token which was passed will be returned. The version which takes handles is called ElementArrayElementChangedHandles() .

Removing An Element From An Element Array

ElementArrayRemoveReference(), ElementArrayRemoveReferenceHandles(), ElementArrayDelete(), ElementArrayDeleteHandles()

When you want to remove an element from an element array, you should ordinarily call ElementArrayRemoveReference() . This routine decrements the element's reference count; it does not, however, delete the element unless the reference count reaches zero (i.e. the last reference to the element has been deleted).

This routine takes four arguments: the optr of the array, the index of the element, a pointer to a callback routine, and a dword-sized constant to be passed to it. There may be certain bookkeeping tasks you want to perform when an element is actually being deleted but not when it is just having its reference count decremented. In this case, you can pass the address of a callback routine, which will be called on any element to be deleted just before the deletion occurs. After the callback routine returns, the element will be removed. If you do not need to have a callback routine called, pass a null function pointer. As noted, when an element is removed, it is actually just resized to zero; that way the index numbers of following elements are preserved.

If you want to delete an element regardless of its reference count, call ElementArrayDelete() . This routine takes two arguments, namely the optr indicating the array and the index of the element to be deleted. It does not take a callback routine; perform any necessary bookkeeping before you call it.

Both of these routines have counterparts which take handles; these counterparts are named ElementArrayRemoveReferenceHandles() and ElementArrayDeleteHandles() .

The "Used Index" and Other Index Systems

ElementArrayGetUsedCount(), ElementArrayGetUsedCountHandles(), ElementArrayUsedIndexToToken(), ElementArrayUsedIndexToTokenHandles(), ElementArrayTokenToUsedIndex(), ElementArrayTokenToUsedIndexHandles()

Sometimes its useful to have a special index system for element arrays. Perhaps you would like the used elements to be numbered sequentially, that is, the first "used" element would be element "zero," even if there were free elements before it. This would require setting up a second index system, besides the one used by the element array routines. GEOS provides routines with this functionality.

To find out the number of elements in an element array, call ElementArrayGetUsedCount() . This routine can return either the number of elements in use or the number of "in use" elements which satisfy any arbitrary criteria. The routine takes three arguments: the optr to the element array, a dword of data which is passed to a callback routine, and a pointer to a Boolean callback routine. That callback routine should itself take two arguments: a pointer to an element, and the dword passed to ElementArrayGetUsedCount() . The callback routine is called once for each "in use" element. The callback should return true if the element should be counted; otherwise, it should return false . For example, the callback routine might return true if the element is longer than 10 bytes; in this case, ElementArrayGetUsedCount() would return the number of elements which are longer than 10 bytes. To have every used element counted, pass a null function pointer. The version of this routine which takes handles is called ElementArrayGetUsedCountHandles() .

If you use a different indexing scheme, you will need a way to translate the index into the normal element array token. To do this, call the routine ElementArrayUsedIndexToToken() . This routine takes four arguments: the optr of the element array, the index count, a dword (which is passed to the callback routine), and a callback routine. The callback routine is of the same format as the callback routine passed to ElementArrayGetUsedCount() ; it should return true if the element meets some criterion. ElementArrayUsedIndexToToken() translates the index passed into the element array's token for that element. For example, if the callback routine returns true for elements which are longer than 10 bytes, and you pass an index of five, ElementArrayUsedIndexToToken() will return the token for the sixth element in the element array which is longer than 10 bytes. (Remember, all indexes are zero-based.) Again, passing a null function pointer makes the routine count all "in-use" elements. The version which takes the element array's handles is called ElementArrayUsedIndexToTokenHandles() .

To translate a token back into this kind of index, call ElementArrayTokenToUsedIndex() . This routine takes four arguments: the optr to the element array, an element token, a callback routine (as with the other routines in this section), and a dword which is passed along to the callback routine. The routine finds the element whose token was passed and returns the index it would have under the indexing system defined by the callback routine. Again, passing a null function pointer makes the routine count every "in-use" element. The routine which takes handles is called ElementArrayTokenToUsedIndexHandles() .

Local Memory: 4.3 Special LMem Uses: Name Arrays

Applications can build on chunk arrays and element arrays in many ways. The chunk array library includes one example of an elaboration on these structures: the name array . The name array is a special kind of element array in which elements can be accessed by a "name" label as well as by a token. Elements in a name array are of variable size. Each element is divided into three sections: The first is the RefElementHeader ; every element in an element array must begin with one of these (and the name array is a kind of element array). The second is the data section. The data section is the same size for every element in a given name array; this size may be anything from zero bytes up to a maximum of NAME_ARRAY_MAX_DATA_SIZE (64 bytes). The data section is followed by a "name" section. This section contains a sequence of bytes of any length up to a maximum of NAME_ARRAY_MAX_NAME_SIZE (256 bytes). The name may contain nulls and need not be null terminated. You can translate a name into the element's token by calling NameArrayFind() (described below).

Note that creating elements in a name array, as in any element array, requires a search through all elements; it thus takes linear time. Furthermore, translating a name into a token also requires a linear search through the elements, and thus also takes linear time. Name arrays thus become slow if they grow too large. Accessing an element by token, however, still takes constant time.

Creating a Name Array

NameArrayCreate(), NameArrayCreateAt(), NameArrayCreateAtHandles(), NameArrayAdd(), NameArrayAddHandles(), NameArrayHeader, NameArrayAddFlags

Creating a name array is much like creating an element array. Every name array must begin with a NameArrayHeader . This structure has the following definition:

typedef struct {
    ElementArrayHeader				NAH_meta;
    word				NAH_dataSize;
} NameArrayHeader;

This structure contains one new field, namely NAH _datasize . This field specifies the size of the data area of each element; the name area is of variable size. You may examine this field at will, but you may not change it. You may set up a fixed data area between the NameArrayHeader and the elements. The usual way to do this is to define a structure whose first element is a NameArrayHeader structure.

To create a name array, call the routine NameArrayCreate() . This routine is passed three arguments:

  • The global handle of an LMem heap. The name array will be created in this block.
  • The size of the data area in each element. The total size of the element will vary, depending on the size of the name. Remember, there is a three byte RefElementHeader at the start of every element (before the data section).
  • The size of the header structure for the name array. If you will not need a fixed data area, you can pass a size of zero, and enough space will automatically be left for a NameArrayHeader .

The routine allocates a chunk in the specified heap, initializes a name array in that chunk, and returns the chunk's handle. If it fails for any reason, it returns a null chunk handle. Since the routine allocates a chunk, all pointers to the LMem heap are invalidated.

If you want to create a name array in a specific chunk, call NameArrayCreateAt() . This routine is almost the same as NameArrayCreate() . However, instead of being passed a memory handle, NameArrayCreateAt() is passed an optr to a chunk. The name array will be created in that chunk. Any data in that chunk (outside of the fixed data area) will be destroyed. Note that if the chunk is too small for the name array, NameArrayCreateAt() will resize it; thus, pointers to the LMem heap may be invalidated. There is a version of this routine which takes the chunk's global and chunk handles instead of its optr; this routine is called NameArrayCreateAtHandles() .

To create an element, call NameArrayAdd() . This routine creates an element and copies the data and name into it. The routine takes five arguments:

  • The optr to the name array.
  • A pointer to an array of characters containing the element's name.
  • The length of the name, in bytes. This many characters will be copied into the name. If you pass a length of zero, bytes will be copied until a null byte is reached (the null will not be copied).
  • A word-length set of NameArrayAddFlags . Only one flag is currently defined, namely NAAF_SET_DATA_ON_REPLACE. This flag is described below.
  • A pointer to the data section. The data will be copied into the new element. (The length of the data portion was specified when the name array was created.)

NameArrayAdd() allocates the element, copies in the data and name, and returns the element's token. If an element with the specified name already exists, NameArrayAdd() will not create a duplicate. Instead, if the flag NAAF_SET_DATA_ON_REPLACE was passed, NameArrayAdd() will copy the new data section into the existing element; if the flag was not passed, it will leave the existing element unchanged. In either case, it will return the existing element's token and increment its reference count. If an element is added, the name array may have to be resized; therefore, pointers into the chunk array will be invalidated. There is a version in which the name array is specified by its global and chunk handles; this version is called NameArrayAddHandles() .

Code Display 16-4 Allocating a Name Array

/* We want a fixed data space, so we define our own header structure. */
typedef	struct {
    NameArrayHeader		MNAH_meta;		/* Must begin with a NameArrayHeader!!! */
    char *		MNAH_comments[32];
} MyNameArrayHeader;
/* The data section of the name array will be this structure: */
typedef struct {
    double		MDSS_myDataFloat;
    int		MDSS_myDataInts[20];
} MyDataSectionStruct;
/* Every element in the name array will have this structure: */
typedef struct {
    RefElementHeader			MES_header;		/* For internal use */
    MyDataSectionStruct			MES_data;
    char			MES_name[]; 		/* We don't know how long this
                         * will actually be */
} MyElementStruct;
MemHandle		myLMemHeap;		/* Assume this is initialized */
ChunkHandle		myNameArray;
/* Sample call to NameArrayCreate() */
myNameArray = NameArrayCreate(myLMemHeap, sizeof(MyDataSectionStruct),
                sizeof(MyNameArrayHeader));

Accessing Elements in a Name Array

NameArrayFind(), NameArrayFindHandles(), NameArrayChangeName(), NameArrayChangeNameHandles()

Name array routines can be accessed with all the routines used for accessing element arrays. However, a few special purpose routines are also provided.

If you know the name of an element and want a copy of its data, call NameArrayFind() . This routine is passed four arguments:

  • The optr to the name array.
  • A pointer to a character buffer. The buffer should contain the name of the element sought.
  • The length of the name. If a length of zero is passed, the name is considered to be null terminated (the trailing null is not part of the name).
  • A pointer to a return buffer. The data portion of the element will be copied to this location.

NameArrayFind() will do a linear search through the elements. If it finds one with the name specified, it will return that element's token and copy the data portion into the return buffer. If there is no element with the specified name, NameArrayFind() will return the constant CA_NULL_ELEMENT. The routine NameArrayFindHandles() is identical, except that the name array is specified by its global and chunk handles.

To change an element's name, call NameArrayChangeName() . This routine is passed four arguments:

  • The optr to the name array.
  • The token of the element whose name will be changed.
  • A pointer to a character buffer containing the new name for the element.
  • The length of the new name. If a length of zero is passed, the name is considered to be null terminated (the trailing null is not part of the name).

NameArrayChangeName() changes the element's name. If the new name is longer than the old, the element will have to be resized; this will invalidate pointers within that block. NameArrayChangeNameHandles() is identical, except that the name array is specified by its global and chunk handles.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Virtual Memory

Most disk-operating systems provide minimal file functionality. A file is simply treated as a sequence of bytes; applications can copy data from files to memory or vice versa, but nothing more elaborate. GEOS provides much more elaborate functionality with its Virtual Memory files.

Virtual Memory (VM) is very useful for two reasons. First, it is often impractical to read an entire disk file into contiguous memory; indeed, it is impossible to do so if the file is larger than 64K. The use of virtual memory allows each file to grow arbitrarily large. Second, each disk file is one long, cumbersome stream of data. By using virtual memory files, applications can break files down into smaller, more manageable blocks which the memory manager can handle more efficiently.

1 Design Philosophy
2 VM Structure
2.1 The VM Manager
2.2 VM Handles
2.3 Virtual Memory Blocks
2.4 VM File Attributes
3 Using Virtual Memory
3.1 How to Use VM
3.2 Opening or Creating a VM File
3.3 Changing VM File Attributes
3.4 Creating and Freeing Blocks
3.5 Attaching Memory Blocks
3.6 Accessing and Altering VM Blocks
3.7 VM Block Information
3.8 Updating and Saving Files
3.9 Closing Files
3.10 The VM File's Map Block
3.11 File-Access Synchronization
3.12 Other VM Utilities
4 VM Chains
4.1 Structure of a VM Chain
4.2 VM Chain Utilities
5 Huge Arrays
5.1 Structure of a Huge Array
5.2 Basic Huge Array Routines
5.3 Huge Array Utilities

Virtual Memory: 1 Design Philosophy

The GEOS Virtual Memory file management system is designed to provide features not easily available with standard file systems. Designed primarily for use by applications for long-term data storage, it is also used by the system for many other purposes, such as to provide state saving (in the UI). The main benefits it provides to applications include the following:

  • Convenient file structure
    GEOS divides VM files into VM blocks, each of which is accessed via a VM block handle. This structure is much like a "disk-based heap," and is analogous to (and compatible with) the memory heap. VM blocks are accessed the same way whether they are on disk or resident in memory. They can be independently resized and locked.
  • Ease of file manipulation
    Many file manipulation techniques are much simpler with VM files. For example, geodes do not have to keep track of which blocks they have changed; instead, when they change a block, they mark it as dirty, and when they update the file, the virtual memory routines will automatically copy just the changed blocks to the disk.
  • Sharing of Data Files
    GEOS maintains information about each VM file and knows when more than one thread is using it. This allows the system to notify all users when one thread has modified a file. The system provides data synchronization for individual blocks.
  • Integrity of Data Files
    GEOS provides several features to protect the integrity of VM files. For example, you can request that GEOS maintain a backup that can be used to restore the file to its last-saved state. In addition, you can have GEOS do all writing to the file synchronously to ensure that all the data in the file stays consistent even if GEOS is somehow interrupted.
  • Uniform File Structure
    Because all GEOS data files are based on VM files, utilities can be written which work for all VM files. For example, the Document Control objects can take care of opening, closing, and saving all data files which are based on VM files. Furthermore, data structures can be designed which can be added at will into any VM file.

Virtual Memory: 2 VM Structure

Virtual memory can be thought of as a heap stored in a disk file. Like the global heap, it is divided into blocks which are 64K or smaller; each block is accessed via a handle. Blocks can be locked, which brings them into main memory. If blocks are modified while in memory, they are copied back to the file when the file is updated.

The primary component of virtual memory is the VM file. The VM file consists of a VM File Header, a collection of VM blocks, and a special structure called a VM Header. The VM File Header is a standard GEOS file header, containing the file's extended attributes and system bookkeeping information. Geodes may not access it directly; instead, they can make calls to the extended attributes routines to access data in the header. The VM blocks and the VM Header do not occupy fixed places in the file. In particular, the VM Header does not necessarily come before all the blocks. Instead, the VM File Header stores the offset within the file to the VM Header, and the VM Header stores information about the blocks (such as their locations in the file). Furthermore, the blocks in a VM file are arranged in no particular order; they are not necessarily arranged in the order they were created, or in any other sequence.

The VM Header maintains all the bookkeeping information about the VM file. For example, it contains the VM Block Table. The block table is much like the global handle table. Block handles are offsets into the block handle table; a block's entry in the table contains information about the block, such as the block's location in the file. Usually, the Block Table contains entries for blocks that haven't been created yet; when all of these handles have been used, the VM Manager expands the block table. For details, see Virtual Memory Blocks .

The VM Manager

VM Handles

Virtual Memory Blocks

VM File Attributes

Virtual Memory: 2.1 VM Structure: The VM Manager

The VM manager can be thought of as a memory manager for a disk-based heap, providing all the services a memory manager would and more. The VM manager provides for allocation, use, and deallocation of virtual memory blocks. It manages the Block Table, enlarging it as necessary when more VM block handles are needed. It keeps track of which VM blocks have been loaded into memory, which are currently in use, and which have been "dirtied" (modified) since they were read from the disk; it also keeps track of how many threads are accessing a given VM file at any time. The VM manager also accomplishes all swapping and read/write operations involving VM files.

Virtual Memory: 2.2 VM Structure: VM Handles

There are several different types of handles associated with VM files. It is important not to get them confused.

  • File Handles
    When you open or create a VM file, it is assigned a file handle, as discussed in the File System chapter. Whenever you call a VM routine, you must specify the VM file by passing this handle. The file handle can change each time the file is opened. Furthermore, if two different network users have the same VM file open at the same time, each user might have a different file handle for the same file.
  • VM Block Handles
    The VM file has a block table in the VM Header. The block table contains the locations of blocks in the file. A given block has the same block handle every time the file is opened. If a file is duplicated, blocks in the new file will have the same VM handles as the corresponding blocks in the old file. In this chapter, references to "block handles" or "VM handles" are referring to VM block handles unless otherwise noted.
  • Memory Block Handles
    When a VM block is locked, it is copied into a memory block. This memory block has an entry in the global handle table. The memory block may persist even after the VM block has been unlocked. Ordinarily, you can refer to a VM block by its VM block handle whether or not it is resident in memory; however, in some cases, you may want to use the memory block handle instead of the VM block handle. This saves time when you know the block is resident in memory, because the VM manager doesn't have to translate the VM block handle into the corresponding global handle. It is also necessary if you need to resize or dirty a VM block. You can instruct the VM manager to use the same global handle each time a given VM block is locked until the file is closed; otherwise, the memory handle for a VM block may change each time the block is locked.
  • Chunk Handles and Database/Cell Handles
    A VM block can contain a local memory heap. If so, that block will have its own chunk handle table. Also, the Database and Cell libraries have been designed to let the VM file efficiently manipulate small pieces of data. These libraries are based on the LMem library. Each item of data has its own DB handle as well as a VM Block handle. Database items are discussed in depth in the Database chapter.

Virtual Memory: 2.3 VM Structure: Virtual Memory Blocks

Most file systems treat files as a string of bytes. A user can read a file sequentially or can start reading at a specified distance into the file. This makes it difficult to work with large files. Reading an entire large file into memory may be impractical, and it may be difficult to access different parts of the file at once.

For this reason, GEOS divides its Virtual Memory files into VM blocks. This division is entirely internal to GEOS. When the file is written to disk, it is still a sequence of bytes, and other operating systems can copy the file normally. However, GEOS geodes can access the file much more conveniently by specifying the blocks they wish to access.

The Nature of VM Blocks

A VM block is a sequence of bytes in a VM file. It must be small enough to fit in a global memory block (i.e. 64K or less, preferably 2K-6K). It may move about in the file; for this reason, it is accessed by a VM block handle. Blocks are either free or used . A used block has an entry in the block table and also a space allocated in the file. (This could be a block of free space, which will be freed the next time the file is compacted.) A free block has a slot in the file's handle table but no space in the file; it is available if a thread needs to allocate a block.

Blocks persist after a file has been closed and GEOS has been shut down. A given block is always accessed by the same block handle. There are utilities to copy blocks within a VM file or between files. Blocks in a VM file are in no particular order. If an application wants to set up a sequence of blocks, it can create a VM Chain, in which the first word of each block is the handle of the next block in the chain. However, even chained blocks will probably not be in order in the actual file.

Each VM block can be assigned a "user ID" number. You can request the handles of the VM blocks with any given ID number. You do not have to assign ID numbers to blocks, but it is sometimes convenient. The ID numbers are stored in the handles' entries in the block table, not in the blocks themselves; this makes it easy to find a block with a specified user ID. User IDs can be changed at will with the routine VMModifyUserID() . Note that all user IDs from 0xff00 to 0xffff are reserved for system use. You can find a block with a specific user ID by calling VMFind() ; see You can locate VM blocks by their user ID numbers. The routine VMFind() takes three arguments: the VM file handle, a VM block handle, and the user ID for which to look. The routine looks through the block table, starting with the handle after th .

Creating and Using VM Blocks

There are two ways you can create a VM block. The first is to request a VM block: You specify the size of the block, the block is created, and you are returned the handle. This is the method ordinarily used. The second method is to attach memory to a block: You create a global memory block, and instruct the VM manager to add it to the VM file. There are sometimes advantages to this technique; for example, you can create an LMem heap, and then attach it to the VM file; it will then be saved with the file. You can also attach a memory block to an existing VM block; this will destroy whatever used to be in the VM block and replace it with the contents of the new block.

You can dynamically resize a VM block by locking it into memory, resizing the memory block, and saving it back to the disk.

These techniques are described in detail in Creating and Freeing Blocks and Attaching Memory Blocks .

File Compaction

When a VM block is freed, the VM manager will note that there is empty space in the file. It will use that space when new blocks are allocated. However, since new blocks will probably not fit exactly in the spaces left by freed blocks, some space may be wasted.

In time, the percentage of wasted file space can grow unacceptably large. To prevent this, the Virtual Memory manager periodically compacts the files. When the ratio of data to free space drops below a certain threshold, the Virtual Memory manager copies the data in the file over the free space. While a file is being compacted, any requests for access to the file will block until compaction is finished. Note that the format of the data is not changed; the free space between data blocks is simply removed.

When a geode creates a VM file, it can specify a "compaction threshold." When the percentage of used space drops below this threshold, the VM manager will automatically compact the file without any attention from the application. The geode should take care in setting the threshold. If the threshold is too low, the file may grow unacceptably large before it is compacted; on the other hand, if the threshold is too high, the VM manager might spend too much time compacting the file for relatively low gains. The application can specify a threshold of zero; this will cause the system default threshold to be used.

Note that if a file is in "backup mode," the file will be compacted only on calls to VMSave() , VMSaveAs() , or VMRevert() . If the file is not in backup mode, it can be compacted on any call to VMUpdate() .

File Updating and Backup

When a block is locked into memory, the VM manager copies the data from the disk block to the global memory block. When the block is unlocked, the VM manager assumes that it can discard the memory block, since the data is already on the disk in the VM block.

If you alter the data in a block, you must notify the VM manager of this fact. You do this by marking the block as dirty . When a block has been marked dirty, the VM manager knows that the version in memory is more up-to-date than the version in the disk file. If the flag VMA_SYNC_UPDATE is off (the default), the block will be written back to the file as soon as possible. If the attribute is on , the block will not be copied back to the disk file until VMUpdate() is called; until then, the block will be copied to the disk swap space if memory is needed. The next time you lock the block, you will be given the new, changed version.

When you want to write the dirty blocks to the disk, you can instruct the VM manager to update the file. This copies all the dirtied blocks back to the disk and marks all blocks as clean . The VM manager also automatically updates the file when it is closed. The updating routines check if the file is currently clean; thus, very little time is lost if you try to update a clean file.

The VM manager can be instructed to notify all users of a file when the file changes from clean to dirty. This has two main uses: it helps maintain data synchronization if many geodes are using the same file, and it lets the document control objects know when to enable the "Save" and "Revert" triggers. (See the Documents Objects chapter.)

The VM manager can be instructed to maintain a backup of a file. If it is so instructed, it will not overwrite the original block when it updates it; instead, it will keep a copy of the original block as well as a copy of the new version. This is transparent to the application. When the VM Manager is instructed to save the file, it deletes all the backup blocks. If it is instructed to revert the file to its last-saved version, it replaces the new version of the changed blocks with the backup versions, thus restoring the file to its condition as of the last time it was saved. If the VM manager is instructed to "save-as" the file, it will create a new file, which does not contain the backup blocks; that is, it contains the file as it currently exists. It will then revert the original file to its last-saved version and close it.

Virtual Memory: 2.4 VM Structure: VM File Attributes

VMAttributes

Each VM file has a set of attributes which determine how the VM Manager treats the file. These attributes are specified by a set of VMAttributes flags. When a VM file is created, all of these attributes are off; after a file has been created, you can change the attributes with VMSetAttributes() (see Changing VM File Attributes ). The following flags are available:

VMA_SYNC_UPDATE
Allow synchronous updates only. Instructs VM Manager to update the file only when you call un updating routine ( VMUpdate() , VMSave() , etc.). This attribute is off by default (indicating that the VM manager should feel free to update blocks whenever they are unlocked). You should set this attribute if the file might not be in a consistent state every time a block is unlocked.
VMA_BACKUP
Maintain a backup copy of all data. The file can then be restored to its last stored state. This is described above.
VMA_OBJECT_RELOC
Use the built-in object relocation routines. This attribute must be set if the VM file contains object blocks.
VMA_NOTIFY_DIRTY
If this attribute is set, the VM Manager will notify all threads which have the VM file open when the file changes from clean to dirty. It notifies threads by sending a MSG_META_VM_FILE_DIRTY to each process that has the file open. (This message is defined for MetaClass , so any object can handle it.)
VMA_NO_DISCARD_IF_IN_USE
If this attribute is set, the VM manager will not discard LMem blocks of type LMEM_TYPE_OBJ_BLOCK if OLMBH_inUseCount is non-zero. This attribute must be set if the file contains object blocks. If this attribute is set, each object block will be kept in memory as long as any thread is using an object in the block.
VMA_COMPACT_OBJ_BLOCK
If set, the VM manager will unrelocate generic-object blocks before writing them. It does this by calling CompactObjBlock() . This allows a VM file to contain generic object blocks.
VMA_SINGLE_THREAD_ACCESS
Set this if only a single thread will be accessing the file. This allows optimizations in VMLock() . This attribute must be set if the VM file contains object blocks.
VMA_OBJECT_ATTRS
This is not, strictly speaking, a VM attribute. Rather, it is a mask which combines the flags VMA_OBJECT_RELOC, VMA_NO_DISCARD_IF_IN_USE, and VMA_SINGLE_THREAD_ACCESS. All of these attributes must be set if the file contains object blocks.

Virtual Memory: 3 Using Virtual Memory

The most common way applications will use Virtual Memory is for their data files. However, there are other uses; for example, VM provides a convenient way to maintain working memory. Some data structures (such as Huge Arrays) can only exist in VM files; an application may create a temporary VM file just for these structures.

How to Use VM

Opening or Creating a VM File

Changing VM File Attributes

Creating and Freeing Blocks

Attaching Memory Blocks

Accessing and Altering VM Blocks

VM Block Information

Updating and Saving Files

Closing Files

The VM File's Map Block

File-Access Synchronization

Other VM Utilities

Virtual Memory: 3.1 Using Virtual Memory: How to Use VM

There are five basic steps to using VM files. The steps are outlined below, and described in greater detail in the following sections.

  • Open (or create) the VM file
    Before you perform any actions on a VM file, you must open it with VMOpen() . This routine can be used to open an existing VM file or create a new one. If you use the document control objects, they will open and create files automatically. Once you have created a VM file, you may want to change its attributes with VMSetAttributes() .
  • Bring a VM block into the global memory heap
    After you open a VM file, you can bring blocks from the file into memory with VMLock() . You can also create new blocks with VMAlloc() and VMAttach() .
  • Access the data
    Once a VM block has been brought into memory, you can access it the way you would any other memory block. When you are done with the data, you should unlock it with VMUnlock() . If you change the memory, you should mark it dirty with VMDirty() before unlocking it. This ensures that the new version of the block will be written to the disk.
  • Update the VM file
    Use one of the several VM updating routines to copy the dirty blocks back to the disk. (If asynchronous update is allowed, the VM file manager will try to update blocks whenever they are unlocked.)
  • Close the VM file
    Use VMClose() when you are done with the file. It will update the file and close it.

Virtual Memory: 3.2 Using Virtual Memory: Opening or Creating a VM File

VMOpen(), VMOpenType, VMAccessFlags

To create or open a VM file, call the routine VMOpen() . You may not need to open and create files directly; if you use the document control objects, they automatically create and open files as the user requests. (See the Document Objects chapter.) VMOpen() looks for the file in the thread's working directory (unless a temporary file is being created, as described below). VMOpen() takes four arguments and returns the file handle. The arguments are:

  • File name
    This argument is a pointer to a string of characters. This string is a relative or absolute path specifying the file to open; if a temporary file is being created, the string is the path of the directory in which to place that file, followed by fourteen null bytes (counting the string-ending null). The name of the temporary file is appended to the path.
  • VMAccessFlags
     This argument is a set of flags which specifies how the file is accessed. The flags are described below.
  • VMOpenType enumerated type
    This argument specifies how the file should be opened. The VMOpenType are described below.
  • Compaction threshold
    This is the minimum percentage of a file which must be used for data at any given time. If the percentage drops below this threshold, the file is compacted. If you pass a threshold of zero, the system default threshold is used. The compaction threshold is set only when the file is created; this argument is ignored if an existing file is opened.

When you use VMOpen() , you must specify how the file should be opened. You do this by passing a member of the VMOpenType enumerated type. The types are as follows:

VMO_TEMP_FILE
If this is passed, the file will be a temporary data file. When you create a temporary file, you pass a directory path, not a file name. The path should be followed by fourteen null bytes, including the string's terminating null. The system will choose an appropriate file name and add it to the path string.
VMO_CREATE_ONLY
If this is passed, the document will be created. If a document with the specified name already exists in the working directory, VMOpen() will return an error condition.
VMO_CREATE
If this is passed, the file will be created if it does not already exist; otherwise it will be opened.
VMO_CREATE_TRUNCATE
If this is passed, the file will be created if it does not already exist; otherwise, it will be opened and truncated (all data blocks will be freed).
VMO_OPEN
Open an existing file. If the file does not exist, return an error condition.
VMO_NATIVE_WITH_EXT_ATTRS
The file will have a name compatible with the native filesystem, but it will have GEOS extended attributes. This flag can be combined with any of the other VMOpenType values with a bit-wise or .

You also have to specify what type of access to the file you would like. You do this by passing a record of VMAccessFlags . This is a byte-length bitfield. The following flags are available:

VMAF_FORCE_READ_ONLY
If set, the file will be opened read-only, even if the default would be to open the file read/write. Blocks in read-only files cannot be dirtied, and changes in memory blocks will not be updated to the disk VM blocks.
VMAF_FORCE_READ_WRITE
If set, the file will be opened for read/write access, even if the default would be to open the file for read-only access.
VMAF_SHARED_MEMORY
If set, the VM manager should try to use shared memory when locking VM blocks; that is, the same memory block will be used for a given VM block no matter which thread locks the block.
VMAF_FORCE_DENY_WRITE
If set, the file will be opened deny-write; that is, no other threads will be allowed to open the file for read/write access.
VMAF_DISALLOW_SHARED_MULTIPLE
If this flag is set, files with the file attribute GFHF_SHARED_MULTIPLE cannot be opened.
VMAF_USE_BLOCK_LEVEL_SYNCHRONIZATION
If set, the block-level synchronization mechanism of the VM manager is assumed to be sufficient; the more restrictive file-level synchronization is not used. This is primarily intended for system software. (See File-Access Synchronization .)

If you open a file with VMAF_FORCE_READ_ONLY, it's generally a good idea to also open it with VMAF_FORCE_DENY_WRITE. When you open a file VMAF_FORCE_READ_ONLY, if the file is writable, and is located on a writable device which can be used by other machines (e.g. a network drive), the kernel will load the entire file into memory and make the blocks non-discardable (even when they are clean); this keeps the file you see consistent, even if another user changes the version of the file on the disk. However, this can cause problems if the machine has limited swap space. If the file is opened with VMAF_FORCE_DENY_WRITE, no other device will be allowed to change the file while you have it open, which means the kernel can just load and discard blocks as necessary.

The routine VMOpen() returns the file handle. If it cannot satisfy the request, it returns a null handle and sets the thread error word. The error word can be recovered with the ThreadGetError() routine. The possible error conditions are:

VM_FILE_EXISTS
VMOpen() was passed VMO_CREATE_ONLY, but the file already exists.
VM_FILE_NOT_FOUND
VMOpen() was passed VMO_OPEN, but the file does not exist.
VM_SHARING_DENIED
The file was opened by another geode, and access was denied.
VM_OPEN_INVALID_VM_FILE
VMOpen() was instructed to open an invalid VM file (or a non-VM file).
VM_CANNOT_CREATE
VMOpen() cannot create the file (but it does not already exist).
VM_TRUNCATE_FAILED
VMOpen() was passed VMO_CREATE_TRUNCATE; the file exists but could not be truncated.
VM_WRITE_PROTECTED
VMOpen() was passed VMAF_FORCE_READ_WRITE, but the file or disk was write-protected.

Virtual Memory: 3.3 Using Virtual Memory: Changing VM File Attributes

VMGetAttributes(), VMSetAttributes()

When a VM file is created, it is given a set of VMAttributes (see VMA_SYNC_UPDATE Allow synchronous updates only. Instructs VM Manager to update the file only when you call un updating routine (VMUpdate(), VMSave(), etc.). This attribute is off by default (indicating that the VM manager should feel free to upd ). These attributes can be examined with the routine VMGetAttributes() . The routine takes one argument, namely the handle of the VM file (which is overridden if a default VM file is set). It returns the VMAttributes flags.

You can change the attributes by calling VMSetAttributes() . This routine takes three arguments: the file handle (which may be overridden), a set of bits which should be turned on, and a set of bits which should be turned off. It returns the new VMAttributes flags.

Virtual Memory: 3.4 Using Virtual Memory: Creating and Freeing Blocks

VMAlloc(), VMAllocLMem(), VMFree()

Once you have created a VM file, you have to allocate blocks in order to write data to the file. The usual way to do this is with VMAlloc() . This routine takes three word-sized arguments:

  • The file handle
    This argument is overridden if a default VM file is set.
  • A user-ID number
    This can be any word of data the application wants to associate with the VM block. The application can locate blocks with a given user ID by using VMFind() (see You can locate VM blocks by their user ID numbers. The routine VMFind() takes three arguments: the VM file handle, a VM block handle, and the user ID for which to look. The routine looks through the block table, starting with the handle after th ).
  • The number of bytes in the block
    This may be zero, in which case no memory is allocated; a memory block must be specifically attached with VMAttach() (see Attaching Memory Blocks ).

The routine returns the handle of the VM block. Before you can use the block, you have to lock it with VMLock() . The block is marked dirty when it is allocated.

There is a routine to allocate a block and initialize it as an LMem heap. This is useful if you are storing object blocks in a VM file. The routine, VMAllocLMem() , takes three arguments:

  • The VM file handle
    This is overridden if a default VM file is set.
  • A member of the LMemTypes enumerated type
    This specifies what kind of heap the LMem heap will be. (See the Local Memory chapter.)
  • The size of the block header
    Use this if you want to store extra data in the LMem block header. To use the standard LMem header, pass an argument of zero. (See the Local Memory chapter.)

The routine creates a VM block and allocates a global memory block to go with it. It initializes the heap in the global block and marks the block as dirty. The LMem heap will begin with two LMem handles and a 64-byte heap; this will grow as necessary. The VM block will have a user ID of zero; you can change this if you wish. The routine returns the handle of the new VM block.

There are two other ways to create LMem blocks in a VM file; these ways give you more control of the block's initialization. You can allocate a VM block normally, lock that block, then get the handle of the associated memory block and initialize an LMem heap in it; or you can allocate an LMem heap normally, and attach that memory block to the VM file using VMAttach() . For more details on LMem heaps, see the Local Memory chapter.

To free a VM block, call VMFree() . This routine is passed two arguments: the VM file handle, and the VM block handle. The handle will immediately be freed, even if it is locked. Any associated memory will also be freed. If you want to keep the memory, detach the global memory block from the file (with VMDetach() ) before you free the block.

Virtual Memory: 3.5 Using Virtual Memory: Attaching Memory Blocks

VMAttach(), VMDetach()

When you use VMAlloc() , the VM manager allocates a global memory block and attaches it to a VM block. However, sometimes you want to allocate the block yourself, or you may have an existing memory block which you want to copy into the VM file. To do this, you call the routine VMAttach() .

VMAttach() takes three arguments:

  • The VM file handle
    The handle of the file to attach.
  • The VM block handle
    If you pass a null handle, a free VM block will be allocated and attached to the global memory block. If you pass the handle of an existing block, the data in the VM block will be replaced with the contents of the global memory block.
  • The global memory handle
    The memory block must be swappable. After the block is attached, the VM manager may discard or free it, as with any other global blocks used by the VM file.

VMAttach() attaches the global memory block to the VM block. The VM Manager becomes the owner of the memory block. The next time the file is updated, the memory block will be copied to the file. VMAttach() returns the handle of the VM block. If it could not perform the attach, it returns a null handle and leaves the global memory block unchanged.

You can also detach the global memory block from a VM block. The routine VMDetach() disassociates a global memory block from its VM block. The routine takes three arguments: the VM file handle; the VM block handle; and the GeodeHandle of the geode which will be made the owner of the memory block. (Passing a null GeodeHandle will make the calling geode the block's owner.) The VM manager disassociates the memory block from the VM block, changes the memory block's owner, marks it "non-discardable," and returns its handle. If the VM block is not currently in memory, VMDetach() will automatically allocate a memory block, copy the VM block's data into it, and return the memory block's handle. If the VM block was dirty, the block will be updated to the file before it is detached. The next time the VM block is locked, a new global memory block will be allocated for it.

Virtual Memory: 3.6 Using Virtual Memory: Accessing and Altering VM Blocks

VMLock(), VMUnlock(), VMDirty(), VMFind(), VMModifyUserID(), VMPreserveBlocksHandle()

Once you have opened a VM file and allocated blocks, you will need to access blocks. The VM library provides many routines for doing this.

If you need to access the data in a VM file, you can use the routine VMLock() . This routine moves a VM block onto the global heap. It does this by allocating a global memory block (if the VM block is not already associated with a memory block), reallocating the global block if it had been discarded, locking the memory block on the global heap, and copying the VM block into the global block, if necessary. (It will copy the VM block to memory only if necessary, i.e. if the memory block is newly-allocated, or had been discarded and reallocated.) VMLock() takes three arguments: the handle of the VM file, the VMBlockHandle of the block to lock, and a pointer to a memHandle variable. It returns a pointer to the start of the block, and writes the global block's handle into the memHandle variable. You can now access the block the same way you would any other block, with one exception: When you are done with the block, you do not call MemUnlock() ; instead, call the routine VMUnlock() , passing it the handle of the global memory block (not the handle of the VM block). This will unlock the global block on the heap.

If you alter the global memory block, you will need to notify the VM manager of this so it will know to copy the changes back to the VM file. You do this by calling the routine VMDirty() . VMDirty() takes one argument, the handle of the global memory block ( not the VM block). It is important to dirty the block while it is still locked on the heap; as soon as you unlock a clean block, the VM manager may choose to discard it. Dirty blocks are copied back to the VM file when it is updated. Note that if an object in a VM block is marked dirty (via ObjMarkDirty() ), the block is automatically dirtied. Similarly, if you attach a global memory block to a VM block (via VMAttach() ), the VM block is automatically dirtied.

You can dynamically resize VM blocks. To do this, lock the VM block with VMLock() ; then resize the global memory block with MemReAlloc() . Be sure to mark the block dirty so the changes will be copied to the disk file. Note that although the global memory block will remain locked, it may move on the global heap when it is resized. You will therefore need to dereference the global memory handle (with MemDeref() ) before accessing the memory.

You can locate VM blocks by their user ID numbers. The routine VMFind() takes three arguments: the VM file handle, a VM block handle, and the user ID for which to look. The routine looks through the block table, starting with the handle after the one passed, until it finds a block with the specified user ID. If it does not find such a block, it returns a null handle; otherwise, it returns the block's VMBlockHandle. Thus, by passing in a block handle of zero, you will get the handle of the first block with the specified ID; by passing back in that block's handle, you will get the next block with that ID; and so on, until you get all the blocks (after which you will be returned a null handle).

You can change a block's user ID number by calling the routine VMModifyUserID() . This routine takes three arguments: the VM file handle, the VM block handle, and the new user ID number. Since user IDs are maintained in the block table, not in the blocks themselves, it doesn't matter whether the block is locked, or even whether it is associated with data in the file. (For example, a block allocated with a size of zero can have its user-ID changed.)

Ordinarily, the VM manager can free any unlocked, clean global block if the space is needed. However, you can instruct the VM manager not to free the global block associated with a specific block by calling the routine VMPreserveBlocksHandle() . The routine takes two arguments, namely the VM file handle and the VM block handle. It sees to it that the specified VM block will remain attached to the same global block until the VM block is specifically detached (or reattached).

Virtual Memory: 3.7 Using Virtual Memory: VM Block Information

VMVMBlockToMemBlock(), VMMemBlockToVMBlock(), VMInfo()

Several utilities are provided to give you information about VM blocks.

If you know the handle of a VM block, you can find out the handle of the associated global block by calling the routine VMVMBlockToMemBlock() . This routine takes two arguments, namely the VM file handle and the VM block handle. It returns the global memory handle of the associated block; however, note the caveats regarding global handles in the above section. If the VM block is not currently associated with a global memory block, the routine will allocate a memory block, copy the VM block into it, and return its handle. If the VM handle is not associated with any data in the file and is not attached to a global memory block, VMVMBlockToMemBlock() returns a null handle.

Conversely, if you know the handle of a global memory block and want to find out the VM file and block to which it is attached, call the routine VMMemBlockToVMBlock() . This routine takes two arguments: the global memory handle, and a pointer to a VMFileHandle variable. It returns the VM block handle of the associated VM block, and writes the handle of the VM file to the address passed. If the global memory block is not attached to a VM file, it returns null handles.

The Boolean routine VMInfo() is an omnibus information routine. It takes three arguments: the handle of a VM file, the handle of a VM block, and a pointer to a VMInfoStruct structure (described below in VMInfoStruct ). If the VM block is free, out of range, or otherwise invalid, it returns false ; otherwise, it returns true (i.e. non-zero) and fills in the fields of the VMInfoStruct .

Code Display 18-1 VMInfoStruct

/* This is the definition of the VMInfoStruct. A pointer to a VMInfoStruct is
 * passed to the routine VMInfo(). The routine fills in the structure's fields.
 */
typedef struct {
    MemHandle		mh;	/* Null handle returned if no block is attached */
    word		size;	/* Size of VM block in bytes */
    word		userID;	/* User ID (or zero if no user ID was specified) */
} VMInfoStruct;

Virtual Memory: 3.8 Using Virtual Memory: Updating and Saving Files

VMUpdate(), VMSave(), VMSaveAs(), VMRevert(), VMGetDirtyState() VMSave()

When you dirty a memory block, that action notifies the VM manager that the block will need to be written back to the file. If the attribute VMA_SYNC_UPDATE is off , the VM manager will try to update the block to the disk file as soon as the block is unlocked, and will then mark the block as clean . However, if the flag is on , the manager does not write the block until it is specifically told to update the file. At this point, it copies any dirty blocks back over their attached VM blocks, then marks all blocks as clean . If you use the document control objects, they will take care of updating and saving the file. However, you may need to call the updating routines specifically.

The routine VMUpdate() instructs the VM manager to write all dirty blocks to the disk. It takes one argument, the VM file handle (which is overridden if a thread file has been set). It returns zero if the update proceeded normally; otherwise, it returns either one of the FileErrors or one of the three VMUpdate() status codes:

VM_UPDATE_NOTHING_DIRTY
All blocks were clean, so the VM disk file was not changed.
VM_UPDATE_INSUFFICIENT_DISK_SPACE
The file has grown since the last update, and there is not enough room on the disk to accommodate it.
VM_UPDATE_BLOCK_WAS_LOCKED
Some of the VM blocks were locked by another thread, so they could not be updated to the disk.

VMUpdate() is optimized for updating clean files; thus, it costs very little time to call VMUpdate() when you are not sure if the file is dirty. If a file is auto-saved, VMUpdate() is used.

A VM file can maintain backup copies of updated blocks. If so, updating the file will write changes to the disk, but will not alter those backup blocks. To finalize the changes, call the routine VMSave() . This routine updates the file, then deletes all the backup blocks and compacts the file. If the file does not have backup capability, VMSave() acts the same as VMUpdate() .

If a file has the backup capability, you cannot directly access the backup blocks. However, you can instruct the VM manager to restore the file to its last-saved state. The command VMRevert() causes the VM manager to check the VM file for blocks which have backups. It then deletes the non-backup block, and changes the backup block into a regular block. It also discards all blocks in memory that were attached to the blocks which just reverted. The file will then be in its condition as of the last time it was saved. The routine may not be used on files which do not have the flag VMA_BACKUP set.

You can save a file under a new name with the routine VMSaveAs() . If the file has backup capability, the old file will be restored to its last-saved condition (as if VMRevert() had been called); otherwise, the old file will be left in the file's current state. The routine is passed the name of the new file. VMSaveAs() copies all the blocks from the old file to the new one. If a block has a backup copy, the more recent version is copied. The new file will thus have the file in its current state; block handles will be preserved. After the new file has been created, if the file has backup-capability, VMSaveAs() reverts the original file to its last-saved state. It then closes the old file and returns the handle of the new file.

If you manage VM files with the document control objects, you generally don't have to call the update or save routines. The document control objects will set up a file menu with appropriate commands ("Save," "Save As," etc.), and will call the appropriate routines whenever the user chooses a command.

If you need to find out whether a file is dirty, call the routine VMGetDirtyState() . This routine returns a two-byte value. The more significant byte is non-zero if any blocks have been dirtied since the last update or auto-save. The less significant byte is non-zero if any blocks have been dirtied since the last save, save-as, or revert action. If the file does not have backup-capability, both bytes will always be equal. Note that VMUpdate() is optimized for clean files, so it is generally faster to call VMUpdate() even if the file might be clean, rather than checking the dirty-state with VMGetDirtyState() .

Virtual Memory: 3.9 Using Virtual Memory: Closing Files

VMClose()

When you are done with a VM file for the time being, you should close it with VMClose() . This routine updates all the dirty blocks, frees all the global memory blocks attached to the file, and closes the file (thus freeing its handle). The routine is passed two arguments. The first is the handle of the file to close. The second is a flag, noErrorFlag . If this flag is FILE_NO_ERROR, VMClose() will not return error conditions; if it could not successfully close the file, it will fatal-error.

If noErrorFlag is false (i.e., zero), VMClose() will update the file and close it. If the file could not be updated, it will return an error condition. Be warned, however, that if for some reason VMClose() could not finish updating a file (for example, because the disk ran out of space), VMClose() will return an error message, but will close the file and free the memory anyway. Thus, the most recent changes will be lost. For this reason, it is usually safer to first update the file (and handle any error messages returned) and then close it.

When GEOS shuts down, all files are closed. When it restarts, you can open the files manually.

If the file is backup-enabled, the backup blocks will be preserved until the file is next opened. That means, for example, that the next time you open the file, you can continue working on it normally, or you can immediately revert it to its condition as of the last time it was saved (in the earlier GEOS session).

Virtual Memory: 3.10 Using Virtual Memory: The VM File's Map Block

VMGetMapBlock(), VMSetMapBlock()

When they're created, the blocks of a VM file are in no particular order. You will need some way to keep track of VM block handles so you can find each block when you need it. The usual way to do this is with a map block .

A map block is just like any other VM block. Like other blocks, it can be a standard block, an LMem heap, etc. It is different in only one way: the VM manager keeps track of its handle. By calling the routine VMGetMapBlock() , you can get the VM handle of the map block. You can then look inside the map block to get information about the other blocks.

Note that the structure of the VM map block is entirely the concern of the creating geode. The VM manager neither requires nor specifies any internal structure or information content.

To create a map block, allocate a VM block through any of the normal techniques, then pass its VM handle as an argument to VMSetMapBlock() . That block will be the new map block. If there already was a map block, the old block will become an ordinary VM block.

In addition to setting a map block, you can set a map database item with the command DBSetMap() . For details, see the Database chapter.

Virtual Memory: 3.11 Using Virtual Memory: File-Access Synchronization

VMGrabExclusive(), VMReleaseExclusive()

Sometimes several different geodes will need access to the same VM file. Generally, these will be several different copies of the same application, perhaps running on different machines on a network. GEOS provides three different ways shared-access can be handled.

A VM file can be one of three different types: standard, "public," and "shared-multiple." By default, all new VM files are standard. The file's type is one of its extended attributes, and can be changed with the routine FileSetHandleExtendedAttributes() (see the File System chapter). The document control automatically lets the user select what kind of file to create, and changes its type accordingly. (See the Document Objects chapter.)

Only one geode may write to a standard GEOS VM file at a time. If a geode has the file open for read/write access, no other geode will be allowed to open that file. If a geode has the file open for read-only access, other geodes are allowed to open it for read-only access, but not for read-write access. If a file is opened for read-only access, blocks cannot be dirtied or updated. If a geode tries to open a file for writing when the file is already open, or if the geode tries to open it for reading when the file has already been opened for writing, VMOpen() will return an error.

In general, when a file is opened, it is by default opened for read-write access. For example, the document control objects present a dialog box which lets the file be opened for read-only access, but has this option initially turned off. However, some files are used mainly for reference and are infrequently changed. For example, a company might keep a client address book on a network drive. Most of the time, people would just read this file; the file would only occasionally be changed. For this reason, GEOS lets you declare VM file as "public." Public files are, by default, opened for read-only access. In all other respects the file is the same as a standard GEOS VM file; it can be opened by several readers at a time, but by only one geode at a time if the geode will be writing.

Sometimes several geodes will need to be able to write to a file at once. For example, a company might have a large customer database, and several users might be writing records to the database at the same time. For this reason, GEOS lets you create "shared-multiple" files. Several geodes can have a "shared-multiple" file open at once. However, a geode cannot access the file whenever it wants. Instead, it must get the file's semaphore to access the file's data. When it needs to access the file, it calls VMGrabExclusive() . This routine takes four arguments:

  • The handle of the VM file
  • A timeout value
    If a timeout value is passed, VMGrabExclusive() will give up trying to get the semaphore after a specified number of seconds has passed. If a timeout value of zero is passed, VMGrabExclusive() will block until it can get the file's semaphore.
  • A member of the VMOperation enumerated type
    This specifies the kind of operation to be performed on the locked file. The VMOperation values are described below.
  • A pointer to a word-length variable.
    If this call to VMGrabExclusive() fails and times out, the operation currently being performed will be written here.

The routine returns a member of the VMStartExclusiveReturnValue enumerated type. The following return values are possible:

VMSERV_NO_CHANGES
No other thread has changed this file since the last time this thread had access to the file.
VMSERV_CHANGES
The file may have been altered since the last time this thread had access to it; the thread should take appropriate actions (such as re-reading any cached data).
VMSERV_TIMEOUT
This call to VMGrabExclusive() failed and timed out without getting access to the file.

When a geode calls VMGrabExclusive() , it must pass a member of the VMOperation enumerated type. Most of the values are used internally by the system; while a geode should never pass these values, they may be returned by VMGrabExclusive() if the call times out. The following values are defined in vm.h :

VMO_READ
This indicates that the geode will not change the file during this access. This lets the kernel perform some optimizations.
VMO_INTERNAL
VMO_SAVE
VMO_SAVE_AS
VMO_REVERT
VMO_UPDATE
These values are set only by the kernel. Applications should never pass them.
VMO_WRITE
This indicates that the geode may write to the file during this access.

The application may also pass any value between VMO_FIRST_APP_CODE and 0xffff. The kernel treats all these values as synonymous with VMO_WRITE; however, the application may choose to associate meanings with numbers in this range (perhaps by defining an enumerated type whose starting value is VMO_FIRST_APP_CODE).

When a thread is done accessing a file, it should release its exclusive access by calling VMReleaseExclusive() . The routine takes one argument, namely the file handle. It does not return anything.

Virtual Memory: 3.12 Using Virtual Memory: Other VM Utilities

VMCopyVMBlock(), VMSetReloc()

If you would like to duplicate a VM block, or copy it to another file, call VMCopyVMBlock() . This routine is passed three arguments:

  • The VMFileHandle of the file containing the source block.
  • The VMBlockHandle of the source block.
  • The VMFileHandle of the destination file (which may be the same as the source file).

The routine makes a complete duplicate of the source block, copying it to the destination file. It returns the VMBlockHandle of the duplicate block. Note that the duplicate block will almost certainly have a different block handle than the source block. If the block contains a copy of its own handle, you should update it accordingly.

Sometimes you will need to perform special actions whenever loading a block into memory or writing it to the disk. For example, you may store data in a compressed format on the disk, and need to expand it when it's loaded into memory. For this reason, you can set a relocation routine. This routine will be called whenever the following situations occur:

  • A VM block has just been copied from the disk into memory (routine will be passed the flag VMRT_RELOCATE_AFTER_READ).
  • A block is about to be written from memory to the disk (routine is passed VMRT_UNRELOCATE_BEFORE_WRITE).
  • A block in memory has just been written to the disk, but is not being discarded (routine is passed VMRT_RELOCATE_AFTER_WRITE).
  • A VM block has just been loaded from a resource (routine is passed VMRT_RELOCATE_FROM_RESOURCE). This is called by the relocating object, not by the VM manager.
  • A VM block is about to be written to a resource (routine is passed VMRT_UNRELOCATE_FROM_RESOURCE). This is called by the unrelocating object, not by the VM manager.

Using the routine VMSetReloc() , you can instruct the VM manager to call your relocation routine whenever appropriate.

Virtual Memory: 4 VM Chains

A VM file is just a collection of blocks. These blocks are not kept in any particular order. If the application wants to keep the blocks in some kind of order, it must set up some mechanism to do this.

One common mechanism is the VM chain. This is simply a way of arranging blocks in a sequence, in which each block contains the handle of the next block. GEOS has a standard format for VM chains and provides utility routines which work with chains of this format.

In general usage, a "chain" is a simple tree in which each block has a link to at most one other block. However, the GEOS VM chain can also contain special "tree blocks," which may have links to any number of child blocks. By using these blocks, an application can set up VM trees of unlimited complexity.

Structure of a VM Chain

VM Chain Utilities

Virtual Memory: 4.1 VM Chains: Structure of a VM Chain

A VM chain is composed of two kinds of blocks: chain blocks (which are linked to at most one other block), and tree blocks (which may be linked to any number of other blocks). One block is the head of the chain; chain utility routines can be passed the handle of this block, and they will act on all the blocks in the chain. If a block is a "leaf" block, it should contain a null handle. An example of a VM chain with tree blocks is shown in the figure below.

Be warned that a VM chain must not contain any circuits. That is, by following links, you should not be able to go from any block back to itself; and there should not be two different routes from any one block to any other. If you create such a VM chain and pass it to a chain utility, the results are undefined. It is your responsibility to make sure no loops occur.

A VM chain block is the same as any other VM block, with one exception: The block must begin with a VMChainLink structure. This structure contains a single data field, VMC_next , which is the handle of the next block in the chain. If the block is in a chain but has no next link, VMC_next is a null handle. This means, for example, that LMem heaps cannot belong to a VM chain (since LMem heaps must begin with an LMemHeader structure).

In addition to chain blocks, a VM chain may contain a tree block. A tree block may have several links to blocks. A tree block begins with a VMChainTree structure. This structure has three fields:

VMCT_meta
This is a VMChainLink structure. Every block in a VM chain, including a tree block, must begin with such a structure. However, to indicate that this is a tree block, the VMC_next field must be set to the special value VM_CHAIN_TREE.
VMCT_offset
This is the offset within the block to the first link. All data in the tree block must be placed between the VMChainTree structure and the first link. If you do not put data in this block, set this field to sizeof(VMChainTree) .
VMCT_count
This is the number of links in the tree block.

Any of the links may be a null handle. To delete the last link in the block, just decrement VMCT_count (and, if you wish, resize the block). To delete a link in the midst of a block, just change the link to a null handle without decrementing VMCT_count . To add a new link to a VM tree block, you can either add the handle after the last link and increment VMCT_count ; or you can replace a null handle (if there are any) with the new handle, without changing VMCT_count .

Virtual Memory: 4.2 VM Chains: VM Chain Utilities

VMChainHandle, VMFreeVMChain(), VMCompareVMChains(), VMCopyVMChain(), VMCHAIN_IS_DBITEM(), VMCHAIN_GET_VM_BLOCK(), VMCHAIN_MAKE_FROM_VM_BLOCK()

Several utilities are provided for working with VM chains. They allow you to compare, free, or copy entire VM chains with a single command.

For convenience, all of these routines can work on either a VM chain or a database item. This is useful, because sometimes you will want to use the utility on a data structure without knowing in advance how large it will be. This way, if there is a small amount of data, you can store it in a DB item; if there is a lot, you can store it in a VM chain of any length. Whichever way you store the data, you can use the same chain utilities to manipulate it.

The routines all take, as an argument, a VMChain which is simply a dword. If the low word is null, the VMChain contains the VM block handle to a VM chain. If the low word is not null, the VMChain contains a DBGroupAndItem . Note that none of the blocks in the VM chain need be locked when the routine is called; the routine will lock the blocks as necessary, and unlock them when finished. Similarly, a DB item need not be locked before being passed to one of these routines. However, the VM file containing the structure must be open.

If you want to free an entire VM chain at once, call the routine VMFreeVMChain() . This routine takes two arguments, namely the VM file handle and a VMChain . It frees every block in the VM chain, and returns nothing.

You can compare two different VM chains, whether in the same or in different files, by calling VMCompareVMChains() . This Boolean routine takes four arguments, namely the handles of the two files (which may be the same) and the VMChain s ( i.e. , the block handles to the two chains). The geode must have both files open when it calls this routine. The routine returns true ( i.e. , non-zero) if the two chains are identical ( i.e. , the trees have the same structures and all data bytes are identical). Note that if the chains contain tree blocks which are identical except in the order of their links, the chains will not be considered identical and the routine will return false ( i.e. , zero).

You can make a copy of a VM chain with the routine VMCopyVMChain() . This routine copies the entire chain to a specified file, which may be the same as the source file. The blocks in the duplicate chain will have the same user ID numbers as the corresponding original blocks. The routine takes three arguments: the handle of the source file, the VMChain of the source chain or item, and the handle of the destination file. It copies the chain or item and returns the VMChain of the duplicate structure. As noted, if the low word of the VMChain is null, the high word will be the VM handle of the head of the chain. The geode must have both files open when it calls this routine.

Several utilities are provided for working with VMChain types. They are implemented as preprocessor macros, so they are very fast. The macro VMCHAIN_IS_DBITEM() is passed a VMChain and returns non-zero if the VMChain identifies a DB item ( i.e. , if the low word is non-zero) or zero if the VMChain identifies a VM chain. The macro VMCHAIN_GET_VM_BLOCK () is passed a VMChain containing the block handle to a VM chain. It returns the high word, i.e., the chain's VM block handle. Finally, the macro VMCHAIN_MAKE_FROM_VM_BLOCK() takes a VMBlockHandle value and casts it to type VMChain .

Virtual Memory: 5 Huge Arrays

Sometimes a geode needs to work with an array that can get very large. Chunk arrays are very convenient, but they are limited to the size of an LMem heap, which is slightly less than 64K; furthermore, their performance begins to degrade when they get larger than 6K. Similarly, if a geode uses raw memory for an array, it is limited to the maximum size of a global memory block, again 64K.

For this reason, GEOS provides the Huge Array library. A Huge Array is stored in a VM file. All the elements are stored in VM blocks, as is the directory information. The application can specify an array element by its index, and the Huge Array routine will lock the block containing that element and return its address. Array indices are dword-sized, meaning a Huge Array can have up to 232 elements. Since elements are stored in VM blocks, each element has a maximum size of 64K; however, the size of the entire array is limited only by the amount of disk space available. The blocks in a Huge Array are linked together in a VM chain, so the VM chain utilities can be used to duplicate, free, and compare Huge Arrays.

There are a couple of disadvantages to using Huge Arrays. The main disadvantage is that it takes longer to access an element: the routine has to lock the directory block, look up the index to find out which block contains the element, lock that block, calculate the offset into that block for the element, and return its address. (However, elements are consecutive within blocks; thus, you can often make many array accesses with a single Huge Array lookup command.) There is also a certain amount of memory overhead for Huge Arrays. Thus, if you are confident that the array size will be small enough for a single block, you are generally better off with a Chunk Array (see the Local Memory chapter).

Huge arrays may have fixed-size or variable-sized elements. If elements are variable-sized, there is a slight increase in memory overhead, but no significant slowdown in data-access time.

Structure of a Huge Array

Basic Huge Array Routines

Huge Array Utilities

Virtual Memory: 5.1 Huge Arrays: Structure of a Huge Array

The huge array has two different type of blocks: a single directory block, and zero or more data blocks. The blocks are linked together in a simple (i.e., non-branching) VM chain. The directory block is the head of the chain. A Huge Array is identified by the handles of the VM file containing the array and the directory block at the head of the array.

The directory block is a special kind of LMem block. It contains a header structure of type HugeArrayDirectory (which begins with an LMemBlockHeader structure), followed by an optional fixed data area, which is followed by a chunk array. The chunk array is an array of HugeArrayDirEntry structures. There is one such structure for each data block; the structure contains the handle of the data block, the size of the block, and the index number of the last element in the block.

Each data block is also a special LMem block. It contains a HugeArrayBlock structure (which begins with an LMemBlockHeader structure) and is followed by a chunk array of elements. If the Huge Array has variable-sized elements, so will each data block's chunk array.

When you want to look up a specific element, the Huge Array routines lock the directory block. They then read through the directory chunk array until they find the block which contains the specified element. At this point, the routine knows both which data block contains the element and which element it is in that block's chunk array. (For example, if you look up element 1,000, the Huge Array routine might find out that block x ends with element 950 and block y ends with element 1,020. The routine thus knows that element 1,000 in the Huge Array is in the chunk array in block y , and its element number is 50 in that block's array.)

The routine then unlocks the directory block, and locks the data block containing that element. It returns a pointer to that element. It also returns the number of elements occurring before and after that element in that chunk array. The calling geode can access all the elements in that block without further calls to Huge Array routines.

When you insert or delete an element, the Huge Array routines look up the element index as described above. They then call the appropriate chunk array routine to insert or delete the element in that data block. They then go through the directory and adjust the element numbers throughout. If inserting an element in a data block would bring the block's size above some system-defined threshold, the Huge Array routine automatically divides the data block.

When the VM routines resize an element block, they automatically make the block larger than necessary. This leaves extra space for future insertions in that block, so the block won't have to be resized the next time an element is added. This improves efficiency, since you may often be adding several elements to the same block. However, this also means that most Huge Arrays have a fair amount of unused space. If you won't be adding elements to a Huge Array for a while, you should compact the Huge Array with HugeArrayCompressBlocks (see Huge Array Utilities ).

Ordinarily, VM Chains may not contain LMem heaps. Huge Arrays are an exception. The reason LMem blocks cannot belong to VM chains is simple. Each block in a VM chain begins with the handle of the next block in the chain (or VM_CHAIN_TREE if it is a tree block). However, each LMem heap has to begin with an LMemBlockHeader structure, the first word of which is the global handle of the memory block. In order for these blocks to serve as both, special actions have to be taken. When a Huge Array block is unlocked, its first word is the handle of the next block in the chain. It is thus a VM chain block and not a valid LMem heap. When the Huge Array routine needs to access the block, it locks the block and copies the block's global handle into the first word, storing the chain link in another word. This makes the block a valid LMem heap, but it (temporarily) invalidates the VM chain.

Although VM chain utilities work on Huge Arrays, you must be sure that the Huge Array is a valid VM chain when you call the utility. In practice, this means you cannot use a VM chain utility when any block in the chain is locked or while any thread might be accessing the array. If more than one thread might be using the array, you should not use the chain utilities.

Virtual Memory: 5.2 Huge Arrays: Basic Huge Array Routines

HugeArrayCreate(), HugeArrayDestroy(), HugeArrayResize(), HugeArrayLock(), HugeArrayUnlock(), HugeArrayDirty(), HugeArrayAppend(), Huge ArrayInsert(), HugeArrayReplace(), HugeArrayDelete(), HugeArrayGetCount()

GEOS provides many routines for dealing with Huge Arrays. The basic routines are described in this section. Some additional routines which can help optimize your code are described in Huge Array Utilities .

Note that you should never have more than one block of a Huge Array locked at a time. Furthermore, when you call any routine in this section (except HugeArrayUnlock() , HugeArrayDirty() , and HugeArrayGetCount() ), you should not have any blocks locked. The next section contains several routines which may be used while a block is locked. Also, if you use any VM chain routines on a Huge Array, you should make sure that no blocks are locked.

To create a Huge Array, call HugeArrayCreate() . This routine allocates a directory block and initializes it. The routine takes three arguments:

  • The handle of the VM file in which to create the huge array. This argument is ignored if a default VM file has been set for this thread.
  • The size of each element. A size of zero indicates that arguments will be variable-sized.
  • The size to allocate for the directory block's header. If you want to have a fixed data area between the HugeArrayDirectory structure and the chunk array of directory entries, you can pass an argument here. The size must be at least as large as sizeof(HugeArrayDirectory) . Alternatively, you can pass an argument of zero, indicating that there will be no extra data area, and the default header size should be used.

HugeArrayCreate() returns the VM handle of the directory block. This is also the handle of the Huge Array itself; you will pass it as an argument to most of the other Huge Array routines.

When you are done with a Huge Array, destroy it by calling HugeArrayDestroy() . This routine frees all of the blocks in the Huge Array. It takes two arguments, namely the global handle of the VM file and the VM handle of the Huge Array. It does not return anything. You should make sure that none of the data blocks are locked when you call this since all of the VM chain links must be valid when this routine is called.

To access an element in the array, call HugeArrayLock() . This routine takes five arguments:

  • The global handle of the VM file which contains the Huge Array.
  • The VM handle of the Huge Array.
  • The (32-bit) index number of the element.
  • A pointer to a pointer to an element.
  • A pointer to a word-sized variable.

The routine figures out which block has the element specified (as described above). It then locks that block. It writes a pointer to that element in the location passed, and writes the size of the element in the variable pointed to by the other pointer (this is useful if the Huge Array has variable-sized elements). It returns a dword. The more significant word is the number of consecutive elements in that block, starting with the pointer returned; the less significant word is the number of elements in that block before (and including) the element specified. For example, suppose you lock element 1,000. Let's assume that this element is in block x ; block x has 50 elements, and element 1,000 in the huge array is the 30th element in block x . The routine would write a pointer to element 1,000 in the pointer passed; it would then return the dword 0x0015001e. The upper word (0x0015) indicates that element 1,000 is the first of the last 21 consecutive elements in the block; the lower word (0x001e) indicates that the element is the last of the first 30 consecutive elements. You thus know which other elements in the Huge Array are in this block and can be examined without further calls to HugeArrayLock() .

When you are done examining a block in the Huge Array, you should unlock the block with HugeArrayUnlock() . This routine takes only one argument, namely a pointer to any element in that block. It does not return anything. Note that you don't have to pass it the same pointer as the one you were given by HugeArrayLock() . Thus, you can get a pointer, increment it to work your way through the block, and unlock the block with whatever address you end up with.

Whenever you insert or delete an element, the Huge Array routines automatically mark the relevant blocks as dirty. However, if you change an element, you need to dirty the block yourself or the changes won't be saved to the disk. To do this, call the routine HugeArrayDirty() . This routine takes one argument, namely a pointer to an element in a Huge Array. It dirties the data block containing that element. Naturally, if you change several elements in a block, you only need to call this routine once.

If you want to add elements to the end of a Huge Array, call HugeArrayAppend() . If elements are of uniform size, you can add up to 216 elements with one call to this routine. You can also pass a pointer to a template element; it will copy the template into each new element it creates. This routine takes four arguments:

  • The global handle of the VM file containing the Huge Array. This argument is ignored if a default file has been set.
  • The VM handle of the Huge Array.
  • The number of elements to append, if the elements are of uniform size; or the size of the element to append, if elements are of variable size.
  • An optional pointer to a buffer containing initialization data for the new element(s).

The routine returns the index of the new element. If several elements were created, it returns the index of the first of the new elements. This index is a dword.

You can also insert one or more elements in the middle of a Huge Array. To do this, call the routine HugeArrayInsert() . As with HugeArrayAppend() , you can insert many uniform-sized elements at once, and you can pass a pointer to a template to initialize elements with. The routine takes five arguments:

  • The global handle of the VM file containing the Huge Array. This argument is ignored if a default file has been set.
  • The VM handle of the Huge Array.
  • The number of elements to insert, if the elements are of uniform size; or the size of the element to insert, if elements are of variable size.
  • The index of the first of the new elements (the element that currently has that index will follow the inserted elements).
  • A pointer to a template element to copy into each new element (pass a null pointer to leave the elements uninitialized).

It returns the index of the first of the new elements. Ordinarily, this will be the index you passed it; however, if you pass an index which is out of bounds, the new elements will be put at the end of the array, and the index returned will thus be different.

To delete elements in a Huge Array, call HugeArrayDelete() . You can delete many elements (whether uniform-sized or variable-sized) with one call to HugeArrayDelete() . The routine takes four arguments:

  • The global handle of the VM file containing the Huge Array. This argument is ignored if a default file has been set.
  • The VM handle of the Huge Array.
  • The number of elements to delete.
  • The index of the first element to delete.

You can resize one of the elements by calling HugeArrayResize() . If you make the element smaller, then data at the end will be truncated. If you make the element larger, then the new data will be zero-initialized.

You can erase or replace the data in one or more elements with a call to HugeArrayReplace() . This is also the only way to resize a variable-sized element. You can pass a pointer to a template to copy into the element or elements, or you can have the element(s) initialized with null bytes. The routine takes five arguments:

  • The global handle of the VM file containing the Huge Array. This argument is ignored if a default file has been set.
  • The VM handle of the Huge Array.
  • The number of elements to replace, if the elements are uniform-sized; or the new size for the element, if elements are variable-sized.
  • The index of the first of the elements to replace.
  • A pointer to a template element to copy into each new element (pass a null pointer to fill the elements with null bytes).

You can find out the number of elements in a Huge Array with a call to HugeArrayGetCount() . This routine takes two arguments, namely the file handle and the handle of the Huge Array. The routine returns the number of elements in the array. Since array indices begin at zero, if HugeArrayGetCount() returns x, the last element in the array has index x-1.

Virtual Memory: 5.3 Huge Arrays: Huge Array Utilities

HugeArrayNext(), HugeArrayPrev(), HugeArrayExpand(), HugeArrayContract(), HugeArrayEnum(), HugeArrayCompressBlocks(), ECCheckHugeArray()

The routines in the other section may be all you will need for using Huge Arrays. However, you can improve access time by taking advantage of the structure of a Huge Array. As noted above, you can use any of the VM Chain routines on a Huge Array, as long as none of the blocks are locked.

If you have been accessing an element in a Huge Array and you want to move on to the next one, you can call the routine HugeArrayNext(). The routine takes a pointer to a pointer to the element. It changes that pointer to point to the next element in the array, which may be in a different block. If the routine changes blocks, it will unlock the old block and lock the next one. It returns the number of consecutive elements starting with the element we just advanced to. If we were at the last element in the Huge Array, it returns zero and the pointer will point to the last element.

If you want to move to the previous element instead of the next one, call HugeArrayPrev(). It also takes a pointer to a pointer to an element. It changes that pointer to a pointer to the previous element; if this means switching blocks, it unlocks the current block and locks the previous one. It returns the number of elements which come before the newly-locked one in its block, counting the newly-locked element. If the pointer was pointing to the first element in the Huge Array, it returns zero and the pointer will point to the first element.

If you have a block of the Huge Array locked and you want to insert an element or elements at an address in that block, call the routine HugeArrayExpand() . It takes three arguments:

  • A pointer to a pointer to the location where we you want to insert the elements. This element must be in a locked block.
  • The number of elements to insert, if the elements are uniform-sized; or the size of the element to insert, if elements are variable-sized.
  • A pointer to a template to copy into each new element. If you pass a null pointer, elements will not be initialized.

The routine inserts the elements, dirtying any appropriate blocks. It writes a pointer to the first new element into the pointer passed. Since the data block may be resized or divided to accommodate the request, this address may be different from the one you pass. The routine returns the number of consecutive elements beginning with the one whose address was written. If the new element is in a different block from the address passed, the old block will be unlocked, and the new one will be locked.

If you have a block of a Huge Array locked and you want to delete one or more elements starting with one within the block, you can call HugeArrayContract() . This routine takes two arguments:

  • A pointer to the first element to be deleted. The block with that element must be locked.
  • The number of elements to delete. Not all of these elements need be in the same block as the first.

The routine deletes the elements, dirtying any appropriate blocks. It then changes the pointer to point to the first element after the deleted ones. If this element is in a different block, it will unlock the old block and lock the new one. It returns the number of consecutive elements following the one whose address was written.

You may wish to perform the same operation on a number of consecutive elements of a Huge Array. HugeArrayEnum() is a routine which lets you do this efficiently. HugeArrayEnum() takes six arguments:

  • The VMFileHandle of the VM file containing the Huge Array.
  • The VMBlockHandle of the Huge Array.
  • The index of the first element to be enumerated (remember, the first element has index zero).
  • The number of elements to enumerate, or -1 to enumerate to the end of the array.
  • A void pointer; this pointer will be passed to the callback routine.
  • A void pointer to a Boolean callback routine. This callback routine should take two arguments: a void pointer to an element and the void pointerthat was passed to HugeArrayEnum() . The callback routine can abort the enumeration by returning true ( i.e. , non-zero).

HugeArrayEnum() calls the callback routine for every element, in order, from the first element. It passes the callback a pointer to the element and the pointer passed to HugeArrayEnum() . The callback routine may not do anything which would invalidate any pointers to the Huge Array; for example, it may not allocate, delete, or resize any of the elements. The callback routine should restrict itself to examining elements and altering them ( without resizing them). The callback routine can abort the enumeration by returning true ( i.e. , non-zero); if it does so, HugeArrayEnum() will return true . If HugeArrayEnum() finishes the enumeration without aborting, it returns false ( i.e. , zero).

If the count is large enough to take HugeArrayEnum() past the end of the array, HugeArrayEnum() will simply enumerate up to the last element, then stop. For example, if you pass a start index of 9,000 and a count of 2,000, but the Huge Array has only 10,000 elements, HugeArrayEnum() will enumerate up through the last element (with index 9,999) then stop. However, the starting index must be the index of an element in the Huge Array. You can also pass a count of -1, indicatingg that HugeArrayEnum() should enumerate through the last element of the array. Therefore, to enumerate the entire array, pass a starting element of zero and a count of -1.

As noted above, most Huge Arrays contain a fair amount of unused space. This makes it much faster to add and remove elements, since blocks don't need to be resized very often. However, if you have a Huge Array that is not frequently changed, this is an inefficient use of space. You can free this space by calling HugeArrayCompressBlocks() . This routine is passed two arguments: the handle of the VM file, and the VMBlockHandle of the Huge Array. The routine does not change any element in the Huge Array; it simply resizes the directory and data blocks to be no larger than necessary to hold their elements. The routine does not return anything.

If you want to verify (in error-checking code) that a given VM block is the directory block of a Huge Array, you can call ECCheckHugeArray() . This routine is passed the VM file and block handles of the block in question. If the block is the directory block of a Huge Array, the routine returns normally; otherwise it causes a fatal error. The routine should not, therefore, be used in non-EC code.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Contacts

The Nokia 9110i Communicator uses Contact databases to store "address book" information. Every time the user enters information about how a place may be reached by phone, fax, or e-mail that information is stored in a Contact database, accessed via the contdb library.

Whenever you prompt the user for information about where to send something, you'll probably use a ContactList. This will allow the user to choose which of their contacts should be the recipient.

There are two libraries which exist to keep track of Contact databases and provide an API for the applications which share access to it.

  • The contdb library keeps track of the contact information, and may be used to look up or edit contact information. It also provides a controller which allows the user to choose a contact from a list of existing contacts.
  • The contLog library provides an API for logging what sorts of communication has been made with the entities described in the Contact database. It also provides a controller which allows the user to select a recently contacted contact.

Contact database are Foam databases, and some operations upon the database will make use of Foam database commands. We will not explore the meanings of these commands closely here, but will show how they are used in the context of Contact databases.

Before reading this chapter, you should be familiar with controllers, Object-Oriented Programming, and the GEOS messaging system. It helps if you try out the "Contacts" built-in application, which demonstrates much of the functionality we'll discuss here. The appl/SDK_9000/Contact/ sample application gives full source for the most useful Contact database operations.

Most programmers first encounter the contdb and ContLog libraries when they need to intercept a message associated with a ContactList or perhaps a RecentContactsControl object. These object classes provide the user with a standard way to choose a contact.

There are other controllers which allow the user to choose an SMS contact. SMAddressControlClass allows the user to choose a recipient for a SMS message. This class should cover some applications' needs when it comes to allowing the user to choose a contact. However, if you need a custom control, you may wish to use a ContactList and/or a RecentContactsSMSControl.

A ContactList presents the user with a list of known contacts. The user may choose a name from the list. In Setting up a Contact List, we show how to set up such an object and get the phone number of the contact the user selects.

The RecentContactsSMSControl presents the user with a list containing names and numbers associated with contacts that have recently had SMS transactions with the user. The user may choose a name or number from the list. In Choosing Contacts From a Log, we show how to set up such an object and get the phone number of the contact the user selects.

1 Setting up a Contact List
1.1 Multiple-Selection ContactLists and Groups
1.2 Remembering the Search
1.3 Detecting Unknown Phone Numbers
1.4 Alternate Messages
2 Choosing Contacts From a Log
3 Using a Dedicated Contact
4 Logging Calls
5 To Get More Info on a Contact
6 The User's Own Data

Contacts: 1 Setting up a Contact List

ContactListClass, CONTACT_LIST_NOTIFY_CONTACT_SELECTED_MSG, ContactGetDBHandle(), FoamDBGetRecordFromID(), FoamDBGetFieldData(), FoamDBDiscardRecord(), ContactReleaseDBHandle()

To present the user with a list of Contacts, do the following:

Make sure that your .gp file contains lines requiring the foamdb and contdb libraries:

library contdb
library foamdb

In your source code, create a ContactList object. The following ContactList object declaration allows the user to pick a contact. If the contact has more than one GSM phone number, the controller will make the user choose one of the fields.

  • When the user chooses a contact, the ContactList will send a message to an object. The ContactList's GCI_output and ATTR_CONTACT_LIST_NOTIFY_CONTACT_SELECTED_MSG specify the object and message. In the example below, the application's process object will handle a message. We will discuss the message in more detail below.
  • To make the ContactList SMS-specific, set its ATTR_CONTACT_LIST_CALL_TYPE to CCT_SMS as shown below. This will gray out contacts which do not have SMS phone numbers.
  • To include the Select button, set the CLF_SELECT_CONTACT_NUMBER feature flag in ATTR_GEN_CONTROL_REQUIRE_UI.
  • To include the ContactList's Search bar, set the CLF_SEARCH feature flag in ATTR_GEN_CONTROL_REQUIRE_UI; to exclude the Search bar, exclude the UI.
  • Optionally, you can use the standard Contact List bitmap by making your ContactList a ComplexMoniker and setting its CMI_iconBitmap field to CMB_CONTACT_LIST.

Putting this together, we end up with the following:

@object ContactListClass MyContactList = {
  GI_visMoniker = "I want to talk to...";
  GCI_output = (TO_PROCESS);
  ATTR_CONTACT_LIST_CALL_TYPE = CCT_SMS;
  ATTR_GEN_CONTROL_REQUIRE_UI = CLF_SEARCH |
                       CLF_SELECT_CONTACT_NUMBER ;
  HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
  ATTR_CONTACT_LIST_NOTIFY_CONTACT_SELECTED_MSG =
                        MSG_MYPROCESS_SEND_MESSAGE ;
}

With this configuration, the ContactList will send a MSG_MYPROCESS_SEND_MESSAGE message to your process object when the user chooses a contact. A sample handler for this message is shown below (Handling the ContactList's "Selected" Message). The message will conform to the prototype:

@prototype void
    CONTACT_LIST_NOTIFY_CONTACT_SELECTED_MSG(
                                   dword recordID,
                                   word fieldID);

The message handler should carry out the following steps:

  1. Get the current Contact database's handle. To do this, call ContactGetDBHandle(). This routine takes no arguments, and returns the Contact database's handle.
  2. Get the handle of the record by calling FoamDBGetRecordFromID(). This routine takes the Contact's database handle and the record's RecordID value. It allocates a block of memory to hold the record's data and returns the record's handle.
  3. To extract the contact's GSM phone number, call FoamDBGetFieldData(). Pass the database handle, the record handle, the FieldID associated with the GSM number field (this number is supplied by the ContactList when it sends this message), a buffer to write the name to, and the size of that buffer. The function fills in the buffer with the phone number string, and returns the length of that string. The returned string might not be null-terminated; thus you will either need to keep track of the returned string length, or else terminate the string.
  4. Now that you're done with the record, let the database know by calling FoamDBDiscardRecord(). This routine takes the Contact's database handle and the record's handle as arguments.
  5. Now that you're done with the database, release its handle by calling ContactReleaseDBHandle().

The example below shows a way of carrying out the above set of steps.

Code Display Handling the ContactList's "Selected" Message

@class MyProcessClass, GenProcessClass;
    @message (CONTACT_LIST_NOTIFY_CONTACT_SELECTED_MSG)
                        MSG_MYPROCESS_SEND_MESSAGE;
@endc
@method MyProcessClass, MSG_MYPROCESS_SEND_MESSAGE {
    TCHAR           theNumber[31];
    TCHAR           theName[MAX_NAME_DATA_LEN+1];
    word            numLen;
    VMFileHandle    CDBHandle;
    MemHandle       theRecord;
        /* Get the Contact database's handle */
    CDBHandle = ContactGetDBHandle();
        /* Get the handle to the record we're interested in */
    theRecord = FoamDBGetRecordFromID( CDBHandle, recordID );
        /* Get the GSM phone number string; null-terminate it. */
    FoamDBGetFieldData( CDBHandle, theRecord, fieldID, theNumber, 30);
    theNumber[numLen] = `\000'; /* add null terminator */
        /* We'll copy the name to theName buffer. */
    ContactGetName(theRecord, theName);

        /* If you need more information about the contact
         * (e.g., business name, job title), this is a good place
         * to retrieve it. */

    /* Do what you like with theNumber and theName */
        /* We're done with this record. */
    FoamDBDiscardRecord(CDBHandle, theRecord );
        /* We're done with the database. */
    ContactReleaseDBHandle();
}

Multiple-Selection ContactLists and Groups

Remembering the Search

Detecting Unknown Phone Numbers

Alternate Selection Messages

Contacts: 1.1 Setting up a Contact List: Multiple-Selection ContactLists

Recent versions of the ContDB library support ContactLists which allow the user to select more than one contact at a time, either by selecting contacts one by one or by choosing contact groups. This feature is available on devices with version numbers "Responder Build 4...." and higher. See Software Version Number for information about finding out the software version of the user's device.)

To create such a ContactList, give it the hint HINT_CONTACT_LIST_NON_EXCLUSIVE_LIST. If your application is running on a device recent enough to support contact groups, then ATTR_CONTACT_LIST_ALLOW_GROUP_AS_CALL_TYPE will allow the user to select a group.

There are two ways to obtain the list of selected contacts:

  • Set the flag CLF_SELECT_CONTACT_NUMBER in ATTR_GEN_CONTROL_REQUIRE_UI, and set ATTR_CONTACT_LIST_NOTIFY_MULTIPLE_CONTACTS_SELECTED_MSG to the message which will be sent whenever one or more contacts are selected from the contact list. This message must be of the prototype:
    •         @prototype void
            CONTACT_LIST_NOTIFY_MULTIPLE_CONTACTS_SELECTED_MSG(
                MemHandle block = cx,
                word num = dx);

    The passed memory handle block will contain the set of ContactListSelectedEntrys, and num will contain the number of these entries. Your method must free the passed memory block.

  • To get the current selection without relying on the Select button, you will need to query the ContactList directly. Call MSG_CONTACT_LIST_GET_IDS_OF_SELECTED_RECORDS; the ContactList will return a ContactListSelectedRecordsData structure containing the number of selected entries and the handle to a memory block containing the IDs of the selected entries stored as ContactListSelectedEntry structures.

Contacts: 1.2 Setting up a Contact List: Remembering the Search

Normally, a ContactList will lose track of the user's current search if the focus moves to another object. To force the list to remember the current search, send it a MSG_CONTACT_LIST_PRESERVE_SEARCH_ON_NEXT_LOST_FOCUS before it loses the focus. (This message is available on devices with version numbers "Responder Build 4...." and higher. See Software Version Number for information about finding out the software version of the user's device.)

Contacts: 1 Setting up a Contact List: 1.3 Detecting Unknown Phone Numbers

If the user enters a valid phone number in the search box of a ContactList, but that number is not found, an application may request that the ContactList send out a notification using a variable data field. (This variable data field is available on devices with version numbers "Responder Build 4...." and higher. See Software Version Number for information about finding out the software version of the user's device.)

Give the ContactList a ATTR_CONTACT_LIST_NOTIFY_NUMBER_ENTERED_MSG variable data field. This field's data is a message.

The message should have parameters:

  void MSG_MY_CL_NOTIFY( MemHandle phNumber = cx);

The passed block contains the phone number string. The message's recipient is responsible for freeing the passed block.

Contacts: 1 Setting up a Contact List : 1.4 Alternate Selection Messages

Build 4.3.7 of the Responder (see versions for more information about software versions) supports two methods for selecting contacts from a CCT_PHONE type of contact controller list. If the user quickly presses the Select trigger (F1 on the PC emulator), then, depending on the circumstances, the message specified by ATTR_CONTACT_LIST_NOTIFY_CONTACT_SELECTED_MSG, ATTR_CONTACT_LIST_NOTIFY_NUMBER_ENTERED_MSG, or ATTR_CONTACT_LIST_NOTIFY_MULTIPLE_CONTACTS_SELECTED_MSG is sent. If the user holds the Select trigger for a longer time, an alternate type of message analogous to the normal selection message is sent. These alternate messages include:

ATTR_CONTACT_LIST_ALTERNATE_NOTIFY_CONTACT_SELECTED_MSG
Set this to the message which will be called when the user selects a contact from the list after holding the Select trigger for a long duration. If more than one contact was selected then this message returns the first selection. This message must follow the prototype:
@prototype void CONTACT_LIST_ALTERNATE_NOTIFY_CONTACT_SELECTED_MSG(
                                dword recordID = cx:dx,
                                word fieldID = bp);
fieldID is the ID of the field containing the selected GSM phone number of the record with the ID recordID. This is the same API as that of the normal selection message; you may use the same handler for both messages.
ATTR_CONTACT_LIST_ALTERNATE_NOTIFY_NUMBER_ENTERED_MSG
Set this to the message which should be sent when the user has entered a valid phone number in the search field which does not match any record in the contact list and the user has held the trigger for a long duration. For this message to be sent, CLF_SELECT_CONTACT_NUMBER must be set. The message must follow the prototype:
@prototype void CONTACT_LIST_ALTERNATE_NOTIFY_NUMBER_ENTERED_MSG(
                                MemHandle block = cx);
block will contain the null-terminated phone number.
ATTR_CONTACT_LIST_ALTERNATE_NOTIFY_MULTIPLE_CONTACTS_SELECTED_MSG
Set this to the message which will be sent when the user selects one or more contacts from the list and holds the Select trigger for a long duration. Note that CLF_SELECT_CONTACT_NUMBER and HINT_NON_EXCLUSIVE_LIST must be present for multiple selection contact lists. This message must follow the prototype:
@prototype void CONTACT_LIST_ALTERNATE_NOTIFY_MULTIPLE_CONTACTS_SELECTED_MSG(
                                MemHandle block = cx,
                                word num = dx);
block will contain the num ContactListSelectedEntry selected items.

Contacts: 2 Choosing Contacts From a Log

RecentContactsSMSControlClass, CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG

For a list of recent SMS contacts, use a RecentContactsSMSController. You will need the following line in your .gp file:

library		contlog

The following setup would allow the user to pick a phone number from a list of recently called SMS numbers, sending a MSG_MYPROCESS_SEND_RECENT message to the application's process object:

    @object RecentContactsSMSControlClass MyLog = {
        GCI_output = ( TO_PROCESS );
        ATTR_RECENT_CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG =
            MSG_MYPROCESS_SEND_RECENT;
    }

With this setup, when the user chooses a phone number from the log, a MSG_MYPROCESS_SEND_RECENT message will be sent to the process object, which should handle the message. The message should conform to the CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG prototype.

@prototype void
    CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG(
                           RecentContactsData *data)
typedef struct {
    dword               RCD_contactID;
    NameOrNumber        RCD_number;
} RecentContactsData;

The data argument's RCD_number field contains the GSM phone number of the selected contact. The RCD_contactID contains either:

  • the ContactID of a record in the device's contact database (not of a database on a memory card), or
  • LECI_INVALID_CONTACT_ID

The code example below shows a handler that extract's the name associated with the selected contact. It makes use of functions described in Setting up a Contact List.

Code Display: Handling the RecentContacts "Selected" Message

@class MyProcessClass, GenProcessClass;
    @message (CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG)
                        MSG_MYPROCESS_SEND_RECENT;
@endc
@method MyProcessClass, MSG_MYPROCESS_SEND_RECENT {
        /* Note: the code in this example is only useful if you need
         * the contact's name. If all you need is the GSM phone number,
         * none of this code is necessary. The phone number string is in
         * data->RCD_number.
         *
         * If you do need the following code, you will need to make sure
         * that your .gp file includes the lines
                library contdb
                library foamdb
         */

        TCHAR           theName[MAX_NAME_DATA_LEN+1];
        VMFileHandle    CDBHandle;
        MemHandle       theRecord;
        word            oldOverride;

        /* If the user chose a phone number that doesn't have a contact
         * associated with it, the contact's RecordID value is
         * LECI_INVALID_CONTACT_ID, signalling an invalid contact. */

        if ( data->RCD_contactID != LECI_INVALID_CONTACT_ID) {
           oldOverride = ContactSetOverrideDB(CONTACT_DEVICE_DB_NAME);
           CDBHandle = ContactGetDBHandle();
           theRecord = FoamDBGetRecordFromID( CDBHandle,
                                              data->RCD_contactID );
           ContactGetName(theRecord, theName);

           /* If you need more information about the contact
            * (e.g., business name, job title), this is a good place
            * to retrieve it. */

         FoamDBDiscardRecord(CDBHandle, theRecord);
         ContactReleaseDBHandle();
         ContactRestoreOverrideDB(oldOverride);
       }

      /* do something useful with data->RCD_number and theName */
}

Recent versions of the Nokia 9000i Communicator allow fax and SMS contact lists to support multiple selections. Set the RCFCF_MULTIPLE_SELECTION or RCMCF_MULTIPLE_SELECTION bit to enable multiple selection for, respectively, a fax or SMS recent contact list and specify the message ATTR_RECENT_CONTACTS_CONTROL_NOTIFY_ENTRY_SELECTED_MSG which will be sent when the user chooses an item from the list.

Contacts: 3 Using a Dedicated Contact

ContactMatchName(), ContactEnsureField()

Perhaps you're writing a special-purpose application that communicates with just one contact. Perhaps a carry-out restaurant chain has contracted you to write an application that the user can use to place an order over the phone. The program would only need to make contact with one place: the local restaurant's GSM phone number.

In this case, you wouldn't want to provide UI for choosing a contact--your program is only interested in one dedicated contact. Still, it's worthwhile to store the contact in the Contact database--if the restaurant's phone number changes, the user can enter the corrected number using the Contact Manager built-in application.

To find the Contact record for a particular contact, you will need to make sure that your .gp file contains the following lines:

library contdb
library foamdb

Finding the "Stuckey's Snack Shack" Contact, below, shows how you could find a dedicated contact in a Contact database and extract the phone number associated with that contact.

To locate a dedicated contact and extract its phone number, carry out the following steps:

  1. Which of the user's Contact databases do you wish to search? If the user has a memory card installed, there may be several databases to search. Perhaps you want to search all of them. Perhaps you want to ignore databases on the memory card, and only search the device's own database.
  2. For each database, to get the RecordID of the contact's record, call ContactMatchName(). This routine takes a search string (pass the contact's name), a ContdbFieldType (pass CFT_NAME, since we're searching by name), a flag (pass a non-zero value to specify that you're only interested in one matching record), a pointer to a FieldID buffer to fill in and a pointer to a word value to fill in. It returns the RecordID of the contact (or -1 if the contact wasn't found). It also fills in the FieldID buffer with the contact's name field FieldID and fills in the word value with the number of matches found; neither of these values is important to our purpose.

    3. If you're searching multiple databases and the current database doesn't contain the record, go on to the next database.

  3. Get the Contact database's handle. To do this, call ContactGetDBHandle(). This routine takes no arguments and returns the database's handle.
  4. Get the record's handle by calling FoamDBGetRecordFromID(). This routine takes the Contact database's handle and the record's RecordID. It returns the record's memory handle.
  5. Get the FieldID of the record's GSM phone number field. In the example below, we assume that the record has only one phone number field, and that this is the field we want. For a more rigorous approach, we could examine each field to make sure it was named "Tel (GSM)". For an example of how one might examine all the fields of a record, see To Get More Info on a Contact.

    6. For our shortcut, we use the ContactEnsureField() function. This function takes as arguments a record handle, a field name string (in our example, we pass a null optr to specify that we should use the default field name, and the ContdbFieldType of the field to find ( pass CFT_PHONE). The function returns a FieldID.

  6. To extract the phone number data, call FoamDBGetFieldData(). Pass the database handle, the record handle, the FieldID, a buffer to write the name to, and the size of that buffer. The function fills in the buffer with the phone number string, and returns the length of that string. The returned string might not be null-terminated; thus you will either need to keep track of the returned string length, or else terminate the string.
  7. Now that you're done with the record, let the database know by calling FoamDBDiscardRecord(). This routine takes the Contact's database handle and the record's handle as arguments.
  8. Now that you're done with the database, release its handle by calling ContactReleaseDBHandle().

If the contact is not found, there are a couple of approaches the application can take:

  • Create a new record with the proper name and a guess at the phone number. The code example below illustrates this approach.
  • Signal to the user that an error has occured and halt the operation.

Code Display: Finding the "Stuckey's Snack Shack" Contact

@method ContactProcessClass, MSG_CONTACT_PROCESS_SEND_TO_STUCKEYS
{

    TCHAR        nameStr[] = "Stuckey's Snack Shack";
    TCHAR        numStr[] = "+1-800-788-2539";
    VMFileHandle hCDB;
    RecordID     recordID;
    FieldID      fieldID;
    word         numMatches;
    dword        recordIndex;
    word         len;
    MemHandle    hRecord;
    int          dbOverride = TRUE;
    FileLongName dbName;
    int          numDBs;
    int          dbNumber;
    word         oldOverride;

    /*
     * Check the database on the device and the databases in the memory card.
     */
    /*
     * First try the db on the device.
     */
    oldOverride = ContactSetOverrideDB(CONTACT_DEVICE_DB_NAME);
    hCDB = ContactGetDBHandle();   /* device DB */
    recordID = ContactMatchName( nameStr, CFT_NAME, TRUE,
                                 &fieldID, &numMatches );
    ContactReleaseDBHandle();    /* device DB */

    if ( recordID == LECI_INVALID_CONTACT_ID) {
        /*
         * Now try each db on the memory card.
         */
        numDBs = ContactGetNumDBsOnMemoryCard();
        for (dbNumber = 0 ; dbNumber < numDBs; dbNumber++) {
            ContactGetDBName(dbNumber, dbName);
            ContactSetOverrideDB(dbName);
            hCDB = ContactGetDBHandle();
            recordID = ContactMatchName( nameStr, CFT_NAME, TRUE,
                                     &fieldID, &numMatches );
            ContactReleaseDBHandle();
            if ( recordID != LECI_INVALID_CONTACT_ID) {
                break;
            }
            /* We didn't find it, loop up and try again. */
        }
        if ( recordID == LECI_INVALID_CONTACT_ID) {
            ContactRestoreOverrideDB(oldOverride);
            dbOverride = FALSE;
        }
    }

    hCDB = ContactGetDBHandle();

    if ( LECI_INVALID_CONTACT_ID == recordID ) {
        /* No such record in contact database -- let's add it. */
        dbOverride = FALSE;
        hRecord = ContactCreateRecordFromTemplate();
        fieldID = ContactEnsureField( hRecord, NullOptr, CFT_NAME );
        FoamDBSetFieldData( hCDB, hRecord, fieldID,
                            nameStr, strlen( nameStr ) );
        fieldID = ContactEnsureField( hRecord, @TelGSMStr, CFT_PHONE );
        FoamDBSetFieldData( hCDB, hRecord, fieldID,
                            numStr, strlen( numStr ) );
        recordIndex = ContactSaveRecord( hRecord );
        hRecord = FoamDBGetVisibleRecord( hCDB, recordIndex );

    }
    else {
        /* Contact exists: get record from ID. */
        hRecord = FoamDBGetRecordFromID( hCDB, recordID );
    }

    /* Record was either found or created: get the GSM phone number. */
    fieldID = ContactEnsureField( hRecord, @TelGSMStr, CFT_PHONE );
    len = FoamDBGetFieldData( hCDB, hRecord, fieldID,
                              numStr, MAX_NUMBER_FIELD_DATA_LEN );
    numStr[len] = '\0';

      /*
       * Here, the application can do something with the contact's data...
       */

    ContactReleaseDBHandle();
    if (dbOverride ) {
        ContactRestoreOverrideDB(oldOverride);
    }

} /* MSG_CONTACT_PROCESS_SEND_TO_STUCKEYS */

To allow the user to edit the contact information for this single contact, use a ContactEdit object. To specify the contact that the user will work with, you will need its handle. Pass the handle as an argument to the MSG_CONTACT_EDIT_DISPLAY_RECORD message. (Don't call ContactSaveRecord(), FoamDBDeleteRecord(), or FoamDBDiscardRecord() when done with this handle; the controller will do so.)

Contacts: 4 Logging Calls

LogAddEntry()

If you wish to log incoming or outgoing messages, you will use the LogAddEntry() function, provided by the contlog library. Make sure that you your.gp file contains the line

library contlog

To log a call, carry out the following steps:

  1. Before making the call, set up a LogEntry structure and call LogAddEntry(). The code shown in Logging an SMS Call would be appropriate for logging an outgoing message; to log an incoming message, substitute LED_RECEIVED for LED_SENT.
  2. Make the call.
  3. When the call is done, call LogAddEntry() again, passing the same LogEntry structure as before, after writing the duration (in seconds) in the LogEntry's LE_duration field. (The previous invokation of LogAddEntry() will have filled in the LogEntry's LE_flags field with correct values.)

Code Display: Logging an SMS Call

LogEntry            MyEntry ;
dword               duration;
TimerDateAndTime    tdat;
TimerGetDateAndTime(&tdat);
duration = TimerGetCount();

strcpy( MyEntry->LE_number, theNumber);
MyEntry.LE_contactID = recordID; /* This is useful only if recordID
                                  * refers to a record in the device contact
                                  * databse--not a database on a memory card.
                                  * If this is not the case, then use
                                  * LECI_INVALID_CONTACT_ID*/
MyEntry.LE_type = LET_SMS;
MyEntry.LE_direction = LED_SENT;
MyEntry.LE_duration = 1;
MyEntry.LE_dateTime.DAT_year = tdat.TDAT_year;
MyEntry.LE_datetime.DAT_month = tdat.TDAT_month;
MyEntry.LE_datetime.DAT_day = tdat.TDAT_day;
MyEntry.LE_datetime.DAT_hour = tdat.TDAT_hours;
MyEntry.LE_datetime.DAT_minute = tdat.TDAT_minutes;

LogAddEntry( &MyEntry );

/* Send the message, and when done...*/

MyEntry.LE_duration = TimerGetCount() - duration;
LogAddEntry( &MyEntry );

When using the Contact Log library to log a contact, if you don't know the phone number, you can pass LEF_WILDCARD_NUMBER to LogAddEntry() to specify that this call's number should match any phone number. This is only allowed on devices with version numbers "Responder Build 4...." and higher. See Software Version Number for information about finding out the software version of the user's device.

Contacts: 5 To Get More Info about a Contact

FoamDBFieldEnum(), FoamDBGetFieldType(), FoamDBGetFieldName()

In previous examples, we've shown how to get a selected contact's name and GSM phone number. It is possible to get more information about a contact given if you know which database it's in and its RecordID number.

In the examples shown in Handling the ContactList's "Selected" Message, Handling the RecentContacts "Selected" Message, and Finding the "Stuckey's Snack Shack" Contact there is a comment noting a good place to get more information about a contact. If, for some reason, you need to find out information about a Contact in a context other than shown in one of those examples,

  1. The database containing the contact must be the current contact database; in the contexts described above, this will be true. You will need the record's RecordID number.
    To query a ContactList object for the RecordID of its selected record, use the MSG_CONTACT_LIST_GET_ID_OF_SELECTED_RECORD message. This message takes no arguments, and returns a RecordID.
  2. Gain access to the current Contact database by calling ContactGetDBHandle(), storing the returned handle.
  3. To access the database record corresponding to the selected contact, call FoamDBGetRecordFromID(). This returns the record's handle.
  4. Get the contact information you need. Information about how you might do this is described in the paragraphs following this checklist.
  5. When you're done with the record handle, call FoamDBDiscardRecord() to free the handle.
  6. Be sure to call ContactReleaseDBHandle() to signal you're done with the database. You shouldn't keep the handle around for long; doing so prevents the file from being closed.

To find out any other sort of information about the contact, you'll need to access one of the fields of the record. Fields within a Foam DB record (the Contact DB is a Foam DB) are identified by a FieldID number.

The FoamDBFieldEnum() function provides a way to scan all the fields of a record. The following code snippet shows how you might use FoamDBFieldEnum() to search for all of a contact's voice phone numbers. FoamDBFieldEnum() calls a callback routine for each of the record's fields. FoamDBFieldEnum() takes as arguments a record handle, a pointer to a data buffer (this pointer will be passed to the callback routine; it gives you a way to pass data to the callback; in our example, we pass the record handle in the buffer), and a pointer to a callback function. The callback function is passed a FieldHeader structure and a pointer to the data buffer that was passed toFoamDBFieldEnum(). From the FieldHeader, it can get the FieldID from the FieldHeader's FH_id field. To get information about each field, use the FoamDBGetFieldType(), FoamDBGetFieldName(), and/or FoamDBGetFieldData() routines. Each of these routines takes the contact database handle, a record handle, a FieldID, and a pointer to a buffer to fill with a return value.

Code Display: Checking All Fields of a Contact

In this example, we check all fields of a contact and build up an array with the names and data for those fields that correspond to voice phone numbers. We know that the database containing the contact is the current contact database. We know the contact's RecordID value.

void BuildNumberList( RecordID recordID ) {
    /* Scan all fields of the Contact record */
    /* For each one that corresponds to a voice phone number, store
     * the field's name (e.g., "Phone") and data (e.g., "555-1212") in
     * the global Data array. */

    VMFileHandle 		CDBHandle;
    MemHandle		curRecord;
    CDBHandle = ContactGetDBHandle();
    curRecord = FoamDBGetRecordFromID( CDBHandle, recordID );
    NumberOfNumbers = 0			/* Global variable:
                    number of elements in Data array*/
    FoamDBFieldEnum( curRecord, &curRecord, &BuildNumberListCallback );
    FoamDBDiscardRecord( CDBhandle, curRecord) ; /* Done with the record */
    ContactReleaseDBHandle(); /* Done with the Contact DB for now */
}
Boolean _pascal BuildNumberListCallback( FieldHeader *field, void *enumData ) {
    VMFileHandle		CDBHandle;
    byte		fieldType;
    MemHandle		curRecord = *((MemHandle *) enumData );
    CDBandle = ContactGetDBHandle();
    FoamDBGetFieldType( CDBHandle, curRecord, field->FH_id, &fieldType);
    if (fieldType == CFT_PHONE ) { /* For each voice phone number */
                /* To look for something other than a phone
                 * number, we would use a different
                 * ContdbFieldType value. */
                    /* Grab the field name */
      FoamDBGetFieldName( CDBHandle, curRecord, field->FH_id,
            Data[NumberOfNumbers].fName, MAX_FIELD_LABEL_LEN);
                    /* Grab the field data */
                    /* If the data has zero length, func will
                     * return zero. */
      if (FoamDBGetFieldData( CDBHandle, curRecord, field->FH_id,
            Data[NumberOfNumbers].fData, MAX_FIELD_NUMBER_DATA_LEN)) {
                    /* If the data was not of zero length
                     * (wasn't just a blank field), then
                     * up our NumberOfNumbers. */
        NumberOfNumbers++;
      }
    }
    ContactReleaseDBHandle(); 	/* We're done with the database */
                /* if we return non-zero, we'll stop Enum'ing
                 * through the fields. Since we only have room
                 * for 10 fields in our array... */
    return( NumberOfNumbers > 10) ;
}

As shown above, to get the field data and field name associated with a field, use the FoamDBGetFieldData() and FoamDBFGetFieldName() functions. Many users will leave blank fields in their contact records; to test for a blank field, check FoamDBGetFieldData()'s return value; if it is zero, then the user has not filled in that field of the contact information.

Contacts: 6 The User's Own Data

ContactGetUserDataName(), ContactGetUserDataID()

Recent versions of the ContDB library provide routines which access the user's own contact information. These routines are only available on devices with version numbers "Responder Build 4...." and higher. See Software Version Number for information about finding out the software version of the user's device.

The ContDB library provides two routines useful for accessing the contact information the user has specified for themself. The ContactGetUserDataName() routine returns the user's name string. ContactGetUserDataID() returns the record ID number of a record which contains the user's contact information.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Distributing Your Application

Once you've finished coding and testing your application, you'll want to distribute it to users. You'll need to take some time to figure out exactly what files make up your package--what files does the user need to use your product? Is it just an application geode? Perhaps an application geode plus a data file? If one or more of the files is an application, make sure it's really ready for distribution. The files that make up your software are known as a software package.

Once you've figured out what files make up your software package, there are two approaches to making your package available:

PC Download
If all users will have access to a 486 (or better) PC running MS Windows 3.1 or MS Windows 95 and a serial or IR connection, and if you have some way to get files onto that PC, you can have users install your software with the built-in Install/Remove software application.
You will provide a package description file (described in the Package Description File section, below), and will need to set up server software on the PC (described in Setting Up a PC Server ).
Web Site
You can make the files available on a web site. Users with the ability to browse the web can download your program easily, thanks to installation savvy in the built-in WWW application.
You will need to provide a package description file (described in the Package Description File section, below), and a bit more to get your server and files ready (described in Setting Up a Web Server ). You will bundle each software package into an Add-On Software file (a specially tagged archive).

No matter how you're making your software available, you'll need to create its Package Description File.

1 Preparing Your Application
2 Package Description File
3 Setting Up a PC Server
4 Setting Up a Web Server

Distributing Your Application: 1 Preparing Your Application

Before you package your files up for distribution, first make sure that any applications in the package are ready.

The applications must be ready to shut down when prompted by the system. If the application is running on the device, the user won't be able to un-install the application or to install a newer version. To make your application shut down as necessary, make sure that its FoamSubApplication object has HINT_APPLICATION_QUIT_ON_IACP_ALLOW_FILE_ACCESS in its declaration.

Distributing Your Application: 2 Package Description File

No matter how you're making your program available, you'll need to provide a "package description file," sometimes known as the . INS file. This is an ASCII text file which specifies important information about the files that make up your program:

  • Name of the package, which the Install/Remove software application displays to the user
  • Description of the package, which the Install/Remove software application displays to the user
  • Order in which files should be installed.
  • Names and locations of the files on the host machine
  • Where the files should be copied to on the client phone

This installation order is important in case of interrupted installation--if file A depends upon file B , but installation was interrupted after file A was brought over, but before file B was, then file A won't work, since it depends on an absent file. If another package is relying upon an older, successfully installed version of file A , then you've just broken that other file.

Use the following format when creating the package description file:

  • The file name must have a . INS suffix.
  • Whitespace at the end of a line is ignored; blank lines are ignored.
  • The first line must say "GEOS Package Description File v1.0"
  • The second line must contain the package name. This is the name by which the package will be identified to the user in the Install/Remove software application. The application doesn't have space for a long description, though--about 20 characters.
  • The third line must contain a description of the package. When the user views the package in the Install/Remove software application, they may read this description before deciding whether to download/install the package. The application doesn't have space for a long description, though--about 16 characters.
  • The fourth line must contain the size of the package, (in bytes). This is the combined size of all files in the package. This string must be at most 14 characters long. (Hopefully, this will be sufficient!) When computing the size, don't forget to include the size of the package description file itself. It's all right to over-estimate the size.
  • The fifth line must consist of a period (.). (This delimiter separates information about the package as a whole from information about the individual files.)
  • The rest of the file consists of pairs of lines:
    The first line of each pair contains the path of a file on the host machine (or within the Add-On Software file, which will be described later);
    The second line of each pair contains the destination of the file on the client phone.

    The host-machine location is relative to the location of the package description file. The destination location is relative to the base of the GEOS tree.

The line-pairs describing file locations must be ordered based upon the order in which the files should be installed. Libraries, fonts, etc. must be listed before the applications that depend on them.

Sample Package Description File

This example shows a package description file for the Santa package. On the host machine, the package description file is in the C:\NSERVER directory, and the files making up the package are in the C:\NSERVER\SANTA directory. The Santa application depends on the Sleigh library, and the Rudy font; the Sleigh library depends on the Donner and Blitzen libraries.

GEOS Package Description File v1.0

Santa v1.0
Gives gifts.
15769
.

santa\rudy.fnt
userdata\font\rudy.fnt
santa\donner.geo
system\donner.geo
santa\blitzen.geo
system\blitzen.geo
santa\sleigh.geo
system\sleigh.geo
santa\santa.geo
world\extrapps\santa.geo

Distributing Your Application: 3 Setting Up a PC Server

The Nokia 9000i Communicator comes with a disk containing the software by which a PC may be made to act as a server machine for the Install/Remove software application. The phone can make either a serial or IR connection with the PC. (For an IR connection, the PC must be running MS Windows 95 and the IR monitoring software which comes with it.)

  1. From MS Windows 3.1 or Windows 95, run the program called server on the floppy disk to install a program called NServer onto the PC.
  2. Run NServer to turn your PC into a server for the Nokia 9000i Communicator. This program monitors one of the PC's COM ports.
  3. Make sure that the NServer program is monitoring the right COM port--check its Settings.

If the user makes a serial or IR connection between a Nokia 9000i Communicator and a PC running the NServer, then runs the Install/Remove software application on the phone, they can install packages from the PC to their phone.

The Install/Remove software application needs to know which directory to scan for Package Definition files. By default, the Install/Remove software application will look in the PC's A:\NSERVER directory--allowing for easy installation directly from floppy. If the files reside on the PC's hard drive, they should probably reside in C:\NSERVER; if they don't, make sure that the user knows where to look for them. To avoid clutter, you might want to put each "package" of files into its own directory under the directory containing the package description files.

Distributing Your Application: 4 Setting Up a Web Server

To make Nokia 9000i Communicator software packages available on a web site, the web site administrator will do some set-up work so that the server knows how to tag Add-On Software files. Each software package must be bundled up into an Add-On Software file--a special kind of archive.

Standard Add-On Software files will have a . AOS suffix, and should be identified by the MIME-type application/x-nokia-9000-communicator-add-on-software . Exactly how this is set up depends upon your server software. NCSA's httpd daemon may use a file called mime.types , to which you would add a line reading

            application/x-nokia-9000-communicator-add-on-software 							aos

Once the server has been set up correctly, each software package needs to be bundled up into an Add-On Software file.

  1. Pack the package descriptor file and all files in the software package into one tar archive file. (This is done using the tar program. Tar is a standard Unix program; there is a public domain DOS tar program. The host-file-location lines in the package description file should describe locations within the tar file's directory structure.) The . INS file must be in the top level directory of the tar file so that it may be found. Read your tar documentation to find out how to create the archive. An appropriate invocation for one version of tar might be:
    2. tar cvf santa2.aos santa2.ins santa2/santa2.geo
  2. Give the tar file's filename the suffix . AOS so that it will be recognized as an Add-On Software file.
  3. Put the file up on your web server.

Users of the Nokia 9000i Communicator using the WWW built-in application will be able to use WWW to install the software package from your site. The WWW program will download the Add-On Software file, extract the files from the archive, and put them into the proper directories as directed by the package description file.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Memory Levels

This chapter collects various utility routines and concepts that may not adequately be addressed within other chapters.

If you are writing an application that requires use of memory, you will want to keep track of how much memory is available on the Nokia 9000i/9110 Communicator's flash file system (FFS) or the N9110 Communicator's memory card.

1 Memory Levels
2 Modifying Files on the FFS
3 Operations that Consume FFS Space
4 Utility Space Routines

Memory Levels: 1 Memory Levels

Operations that require writing to the FFS should almost always check "disk space" both before and after the operation. There are three defined levels of available disk space on the device. These levels are:

  • Not full (more than 50kb available)
  • Nearly full (between 25 and 50kb available)
  • Full (less than 25 kb available)

You can check for these conditions by using the following constants:

  • FOAM_DISK_SPACE_NEARLY_FULL_LEVEL
  • FOAM_DISK_SPACE_FULL_LEVEL

FFS checking is usually needed at the following times:

  • Writing files to disk
  • Engaging in an operation that consumes memory

Memory Levels: 2 Modifying Files on the FFS

FoamWarnForSaving(), FoamSaveAction
On N9110: FoamWarnForSavingOnDisk()

Whenever you perform an operation that modifies a file on the FFS, you should make sure that you have enough space to save the file. For example, you may bring up a document, modify it, and then wish to close the app, saving the document as it now exists.

In those case, use FoamWarnForSaving() to make sure you can proceed with the operation. Pass this routine the number of bytes required for the operation. The routine checks if there is enough memory available for the operation. If there is, it returns FSA_SAVE_DATA. Otherwise, it initiates a dialog box that asks you whether you wish to either throw away the changes or cancel the operation. If you are using a N9110 Communicator and want to save to the memory card, use FoamWarnForSavingOnDisk() and pass the card's DiskHandle in addition to the number of required bytes.

FoamWarnForSaving() returns one of the following FoamSaveAction values:

FSA_SAVE_DATA
The operation is well within the limits.
FSA_THROW_AWAY_CHANGES
The operation could not proceed due to memory constraints, and the user chose to discard the changes. Proceed accordingly. (You might, for example, allow the app to close.)
FSA_DO_NOTHING
The operation could not proceed due to memory constraints, and the user chose to cancel the operation. Proceed accordingly. (You should not allow the app to close, for example.)

Memory Levels: 3 Operations that Consume FFS Space

FoamWarnIfNotEnoughSpace(), FoamCheckIfOutOfSpace(), FoamWarnSpaceAfterOperation(), FoamDiskSpaceStatus
On N9110: FoamWarnIfNotEnoughSpaceOnDisk(), FoamCheckIfOutOfSpaceOnDisk(), FoamWarnSpaceAfterOperationOnDisk()

Some operations may consume memory once they are begun, and it might be difficult to stop them once they start. For example, receiving a fax, reading or receiving e-mail, downloading something from the Web or printing a document may all consume FFS space.

FoamWarnIfNotEnoughSpace() takes a numeric value corresponding to the bytes required for the operation. If the operation would result in memory falling below the critical level, the routine will notify the user that it has insufficient memory to complete the operation (via a dialog box) and will return FDDS_FULL. Before writing to the N9110's memory card, use the analogous routine FoamWarnIfNotEnoughSpaceOnDisk().

If the operation can proceed otherwise, it will return the appropriate FoamDiskSpaceStatus value:

FDDS_NOT_FULL
FDDS_NEARLY_FULL
FDDS_FULL

FoamCheckIfOutOfSpace() also returns a FoamDiskSpaceStatus but does not initiate any dialog box (FoamCheckIfOutOfSpaceOnDisk() for memory card).

You should callFoamWarnSpaceAfterOperation() after any operation that consumes FFS space (FoamWarnSpaceAfterOperationOnDisk() for the memory card). If the operation resulted in an FDDS_NEARLY_FULL status, the routine brings up a dialog alerting the user that the device is nearly out of FFS space.

Memory Levels: 4 Utility Space Routines

FoamGetFreeDiskSpace(), FoamCheckIfOutOfSpace()
On N9110: FoamGetFreeDiskSpaceOnDisk(), FoamCheckIfOutOfSpaceOnDisk()

FoamGetFreeDiskSpace() returns the number of bytes of free space. Use FoamGetFreeDiskSpaceOnDisk() on a N9110 to get the number of free bytes on the disk (such as the memory card) with the passed DiskHandle.

FoamCheckIfOutOfSpace() also returns a FoamDiskSpaceStatus but does not initiate any dialog box (FoamCheckIfOutOfSpaceOnDisk() on N9110).

This document is a single-page version of a a multi-page document, suitable for easy printing.


Memory Cards

The Nokia 9110 Communicator supports an optional memory card for extra data storage. The memory card is about the size of a postage stamp and provides up to 4 MB of Flash memory. To install a memory card, shut down the Communicator, remove the main battery, and insert the card behind the battery. For more information on memory card technology, refer to its homepage.

When a user starts the Communicator with a memory card, GEOS merges the directories on the card with the corresponding Communicator directories. For example, if the file temp were in the memory card's NOKIA\DOCUMENT\ directory, the user would see temp in the phone's document directory. As another example, if the application test.geo were in the card's NOKIA\WORLD\EXTRAPPS\ directory, then the user could run test.geo from the "Extra Applications" menu.

To develop geodes that use the memory card, you should become acquainted with special API that lets you deal with files and directories on a card. Emulate a memory card on the emulator by using a floppy disk.

1 Emulating a Memory Card
2 Memory Card API

Memory Cards: 1 Emulating a Memory Card

The memory card is removable media, like a floppy disk. The emulator uses the floppy disk (in the A: drive) to emulate a memory card.

To set up a floppy disk that emulates a memory card:

  1. Format a floppy disk using a FAT file system
  2. Create the following directory structure:
    A:\NOKIA\DOCUMENT\MMC
A:\NOKIA\PRIVDATA
  3. You can also create these optional directories to hold additional information:
    A:\NOKIA\WORLD\SYSAPPS (System Applications geodes)
A:\NOKIA\WORLD\EXTRAPPS (Extra Applications geodes)
A:\INSTALL (For installable packages)
A:\BACKUP (For backup sets)

Insert the disk into the floppy drive and launch the emulator. At startup, the emulator checks for a disk with the correct format (directories). If the emulator finds such a "memory card," it merges the memory card directories with the N9110 directories. For example, any geodes in the memory card's NOKIA\WORLD\SYSAPPS directory appear in the list of the N9110's "System Applications".

Memory Cards: 2 Memory Card API

The Nokia 9110 Communicator features expanded file API for memory cards.

internal/respondr.goh

To use a memory card, first see if there is a memory card using RespGetMmcStatus() & MASK_MS_CARD. Then get its DiskHandle using RespGetMmcDisk(). Use this DiskHandle to specify paths to files and directories on the memory card.

The user interacts with the memory card as if it were a directory with the same name as the memory card. You can get this name with RespGetMmcName() and alter it with RespSetMmcName().

foam.goh

Use FoamPathFolderNameForDisplay() and FoamPathGetFullPathForDisplay() to display user-friendly paths to items on the memory card.

There are several new diskspace routines which extend the standard diskspace routines to apply the memory card. These routines have the same name as their counterparts but end in "...OnDisk" and take a DiskHandle parameter. For example, FoamCheckIfOutOfSpaceOnDisk() behaves like FoamCheckIfOutOfSpace() but can check the disk space on the Communicator or the memory card.

For code examples, refer to the MCUtils sample application (N9110\APPL\SDK_9000\MCUtils\).

This document is a single-page version of a a multi-page document, suitable for easy printing.


Modem Connections

This chapter discusses how to set up your Nokia 9000i Communicator to emulate a Hayes-compatible modem to establish a direct serial connection.

On the Nokia 9000i Communicator, you connect through a "virtual" serial port and use a stream to read and write data to this port just as you would with an actual serial port. (For more information on streams, see the Streams chapter of the Concepts Book.) The modem driver handles data notifications from the port.

The ModemC Library provides an interface to the modem driver while the VirtualSerial Library provides a stream interface to the "virtual" serial port. In addition, the modem.goc module provides a set of basic routines for establishing a modem connection. You can drop this module into any application that uses a modem.

1 Introduction
2 Connecting
3 Sending Data
4 Receiving Data
5 Disconnecting

Modem Connections: 1 Introduction

Sending and receiving data over a serial line is similar to sending and receiving data through a socket connection. You configure your hardware, make the connection, open streams for sending and receiving data, and close the connection ( i.e. , hang up).

This chapter uses the MTalk sample application to illustrate how to set up and use a modem connection. MTalk establishes a simple "talk" exchange between two devices over a modem connection. It provides user interface for taking text input (which it sends to the other end of the connection) and for displaying data received from the other end of the connection.

(Note: Both mtalk.goc, the sample application discussed in this chapter, and modem.goc, the module that provides its basic modem connectivity, live in \APPL\SDK_9000\MTALK\.)

Modem Connections: 2 Connecting

To establish a connection, MTalk does the following:

  • allocates a buffer to be used (later) for holding received data
  • prompts the user for the phone number to dial
  • calls ModemConnect() (defined in modem.goc) to make the connection

Code Display 9-1 Making A Serial Line Connection

    /*
     * phoneNum - Phone number to dial.
     * retVal - Return value of ModemConnect().
     */
    char        phoneNum[MAX_PHONE_NUM_LENGTH + 1];
    Boolean     retVal;
    /*
     * Allocate a global memory block to be used as a buffer to hold received data.
     * A local buffer variable in the read function would use up
     * precious stack space, and a global buffer variable
     * would increase the size of the application's fixed data segment,
     * which degrades overall memory manager performance.
     *
     * Allocating it to be DISCARDABLE has further advantages.
     * Because we don't care what happens to the contents after they
     * have been processed, we'll allow the memory manager to throw
     * the block out if it decides it needs the heap space. This
     * means a little extra work for us whenever we want to use the
     * block, since we have to check for this condition, and
     * reallocate if necessary.
     */
    recvBuffer = MemAlloc( INPUT_BUFFER_SIZE, HF_DISCARDABLE, 0 );
    /*
     * If the buffer was not successfully allocated, memory must be getting low
     * so shut down.
     */
    if ( recvBuffer == NullHandle )
    {
    @send application::MSG_META_QUIT();
    return;
    }
    /*
     * Get the phone number to dial.
     */
    @call MTalkPhoneNumberText::MSG_VIS_TEXT_GET_ALL_PTR( phoneNum );
    /*
     * Create the modem connection and register the object and messages
     * it will receive when data is received or when the call has ended.
     * Note: in this example, oself refers to the process object.
     * Remember that ModemConnect() returns FALSE if it successfully makes
     * the connection.
     */
    retVal = ModemConnect( phoneNum,
             oself,
             MSG_MTALK_PROCESS_READ_DATA,
             MSG_MTALK_PROCESS_CALL_ENDED );
    /*
     * If the connection failed, free the buffer and return. (Note: after freeing
     * any allocated memory, it is good practice to re-set handles and pointers to
     * Null so that you can test for a Null value later.)
     */
    if ( retVal )
    {
    MemFree( recvBuffer );
    recvBuffer = NullHandle;
    return;
    }

ModemConnect() takes care of the "housekeeping" details involved in making a connection. Specifically, this routine takes care of the following:

  • checks to make sure the phone is available for use
  • opens and configures the port (on the Nokia 9000i Communicator, the "virtual" serial port is COM1)
  • initializes the modem
  • sets up to receive notification when data arrives on the serial port or when the call ends
  • dials the phone number

ModemConnect() takes four parameters:

phoneNumber
Char pointer to the phone number to dial.
dataReceiver
Optr to the object that will receive data or call end notification.
dataReceiptMessage
Message sent when data arrives on the port.
endCallMessage
Message sent when the call has ended.

ModemConnect() returns FALSE if the attempt to connect is successful. If the attempt is unsuccessful, it displays a dialog box notifying the user of the error. Possible error messages include:

"Connectivity Problem:
Data calls cannot be made while another call is active."
"Connectivity Problem: Line is busy. Try again later."
"Connectivity Problem:
No answer. Check the phone number and try again later."
"Connectivity Problem: General Error."
"Connectivity Problem: Error initializing modem."

Modem Connections: 3 Sending Data

To send data, MTalk does the following:

  • retrieves the text the user has typed in and puts it into a memory block
  • locks the block down (to get a pointer to the text)
  • gets the size of the text
  • calls ModemSend() (defined in modem.goc) to send the data
  • unlocks the memory block

Code Display 9-2 Sending Data Over A Serial Connection

    /*
     * textBlock - Handle of block containing text to send.
     * textPtr - Pointer to text to send.
     * textSize - Size of text to send.
     */
    MemHandle		textBlock;
    char *		textPtr;
    int		textSize;
    /*
     * Get text that user has typed and put it into a newly created memory block.
     * Lock the block down on the heap and obtain a pointer to it.
     */
    textBlock = @call MTalkSendText::MSG_VIS_TEXT_GET_ALL_BLOCK( NullHandle );
    textPtr = MemLock( textBlock );
    /*
     * Get the size of the text then send it.
     */
    textSize = LocalStringSize( textPtr );
    if ( ModemSend( textSize, (byte*)textPtr ) != STREAM_NO_ERROR )
    {
    /* handle the error */
    }
    /*
     * Once the text has been sent, free its block.
     */
    MemFree( textBlock );

ModemSend() calls the VirtualSerial Library routine, VirtualSerialWrite() , to send the passed data. VirtualSerialWrite() writes the passed data to stream and, if necessary, blocks until enough space in the stream becomes available. (If you don't want to block, pass STREAM_NO_BLOCK in VirtualSerialWrite() .)

ModemSend() takes two arguments:

dataLength
Length of the string (not counting the Null character).
data
Pointer to the data to be sent.

ModemSend() returns a StreamError value:

STREAM_NO_ERROR
if it successfully sends the data.
STREAM_CLOSED
if the stream is not open.
STREAM_SHORT_READ_WRITE
if the required amount of space was not available and STREAM_NO_BLOCK was specified.

Modem Connections: 4 Receiving Data

When the modem driver receives notification that data has arrived on the port, it sends a pre-registered message to a specified object. (You specify both the object and the message when you call ModemConnect() .)

In the MTalk example, the driver sends MSG_MTALK_PROCESS_READ_DATA to the process object when data has arrived on the port. The handler for this message is shown in Receiving Data Over A Serial Connection .

To receive data, MTalk does the following:

  • checks to make sure the connection is still open
  • locks the input buffer down (to get a pointer to the text)
  • calls ModemReceive() (defined in modem.goc) to read incoming data
  • displays read data
  • unlocks the block

Code Display 9-3 Receiving Data Over A Serial Connection

@method MTalkProcessClass, MSG_MTALK_PROCESS_READ_DATA
{
    /*
     * recvDataSize - Amout of data actually read in.
     * recvDataPtr - Pointer to data to read in.
     * retVal - Return value of ModemReceive().
     */
    word		revDataSize;
    char *		recvDataPtr;
    StreamError		retVal;
    /*
     * Check to see if connection is still open. Because of message queueing
     * delays, we may have received this data notification after the connection
     * has been closed.
     *
     * ConnectionOpen - global flag that keeps track of whether
     * the connection is open or closed.
     */
    if ( ConnectionOpen == FALSE )
    {
    return;
    }
    /*
     * Lock the input buffer down on the heap, and obtain a pointer to it.
     * (Note: This buffer is a memory block that was allocated in the
     * "Connecting" example.)
     */
    recvDataPtr = MemLock( recvBuffer );
    /*
     * A NULL pointer means that the memory manager has gone ahead
     * and discarded the buffer block since the last time it was
     * unlocked.  So we need to reallocate it here.  Note that the
     * MemHandle is still in use; it just isn't bound to any block of memory.
     */
    if ( recvDataPtr == NULL )
    {
        /*
         * Reallocate the block, locking it immediately,
         * so it doesn't get discarded again. If the block cannot
         * be re-allocated, memory must be low so close the connection.
         */
    if (( MemReAlloc( recvBuffer, RECV_BUFFER_SIZE, HAF_LOCK ) == NullHandle)
    {
        @send self::MSG_MTALK_PROCESS_CLOSE();
        return;
    }
        /*
         * After re-allocating the locked block, dereference its handle
         * to obtain a pointer to it.
         */
    recvDataPtr = MemDeref( recvBuffer );
    }
    /*
     * Read available data up to the size of our buffer then display it.
     */
    if ( ModemReceive( RECV_BUFFER_SIZE, (byte*)recvDataPtr, &recvDataSize )
                            == STREAM_NO_ERROR )
    {
        @call MTalkOutText::MSG_VIS_TEXT_APPEND_PTR( recvDataPtr,
                             recvDataSize );
    }
    /*
     * If the read data filled the entire buffer, there's a good
     * chance more data is available so send the message through the
     * process object's queue again.  (We do not want to keep reading
     * in a loop in case the other side dumped a very large stream
     * of data; this would keep the process object from handling
     * other messages in its queue.)
     */
    if ( RECV_BUFFER_SIZE == recvDataSize ) {
    @send ,forceQueue self::MSG_MTALK_PROCESS_READ_DATA();
    }
    /*
     * Once all the data has been read, unlock the buffer block.
     */
    MemUnlock( recvBuffer );
} /* end of MSG_MTALK_PROCESS_READ_DATA */

Like ModemSend() , ModemReceive() calls a VirtualSerial routine, VirtualSerialRead() , to read incoming data. Because no blocking is specified, VirtualSerialRead() reads only data that is currently available in the stream. (If you want the routine to block until there is enough data in the stream to fill the entire buffer, pass STREAM_BLOCK in VirtualSerialRead() .)

ModemReceive() takes three arguments:

recvBufLength
Size of allocated buffer (in bytes).
recvBuf
Byte pointer to the buffer.
bytesReceived
Word pointer. ModemReceive() writes the number of bytes actually read in to this parameter.

ModemReceive() returns a StreamError value:

STREAM_NO_ERROR
if it successfully reads the data.
STREAM_CLOSED
if the stream is not open.
STREAM_SHORT_READ_WRITE
if the required amount of data was not available and STREAM_NO_BLOCK was specified.

Modem Connections: 5 Disconnecting

When a call has ended, the modem driver sends a pre-registered message to a specified object. (You specify both the message and the receiving object in ModemConnect() .)

In the MTalk example, the modem driver sends MSG_MTALK_PROCESS_CALL_ENDED to the process object when the call has ended for any reason. This message checks if the connection is still open and if it is, calls yet another message, MSG_MTALK_PROCESS_CLOSE, to close it. The handler for MSG_MTALK_PROCESS_CLOSE is shown in Ending A Serial Line Connection .

Code Display 9-4 Ending A Serial Line Connection

@method MTalkProcessClass, MSG_MTALK_PROCESS_CLOSE
{
    /* close the connection */
    ModemDisconnect();
    /*
     * Free the buffer and re-set the (global) "connection" flag
     * to indicate the connection is closed.
     */
    MemFree( recvBuffer );
    recvBuffer = NullHandle;
    ConnectionOpen = FALSE;
} /* end of MSG_MTALK_PROCESS_CLOSE */

ModemDisconnect() , defined in modem.goc, takes care of the following:

  • hangs up the phone
  • closes the "virtual" serial port

ModemDisconnect() takes no parameters and returns void.

This document is a single-page version of a a multi-page document, suitable for easy printing.


SMS Guide

The Short Message Service (SMS) is a protocol designed to transmit short alpha-numeric messages via a dedicated cellular provider's "service center." SMS is built into the GSM (Global System for Mobile communications) standard that has gained general acceptance within Europe; SMS will also most likely be integrated into whatever digital cellular network becomes the de-facto standard within the United States.

The Nokia 9000i Communicator is compliant with the transmission and reception of SMS protocol messages according to the GSM standard. With SMS, it is possible to create applications and services that request and retrieve short text messages (up to 160 characters) to any server or user on the GSM network. The ability to send and receive short messages can enable a wide variety of services. For example, short messages can be used to retrieve stock quotes, place horse-racing bets at a paramutual facility, or play chess with another member of the GSM network. The possible uses for the SMS protocol are almost limitless. A service need only be connected to the GSM network.

Geoworks' Mailbox library includes built-in transmission and receipt of SMS messages. The elements of the Mailbox library that are of concern to SMS application developers have been excerpted and included in this chapter.You will not need to understand the inner workings of the Mailbox library to write an SMS application or service.

Before reading this chapter, you should be familiar with cellular technologies, the SMS standard and elementary GEOS programming--in particular, the use of GEOS Virtual Memory (VM) files.

1 SMS Overview
2 Using the Mailbox Library
2.1 Mailbox Overview
3 Sending an SMS Message
3.1 Sending and the Mailbox Library
3.2 Using the SMS Mailbox API
4 Receiving an SMS Message
4.1 SM Receive and the Mailbox Library
4.2 Receiving an SMS Message

SMS Guide: 1 SMS Overview

The Short Message Service protocol handles a "short message" of up to 160 characters. The SMS standard of 160 characters allows you to send a text message to any single node (address) on the GSM network by way of a dedicated cellular service provider.

The cellular service provider is responsible for managing incoming SMS messages and holding them until the recipient's phone becomes active. The length of time that an SMS message should be held can be specified by the sending party. This is known as a validity period .

The cellular provider may also elect to deliver the message in a format other than SMS. This is done at the server through a message conversion into the alternative format (e.g., as a fax message, as an email, as a page, etc.) This alternative method of delivery may also be specified by the sending party (though, of course, the recipient must have the means to handle such a format).

Because the SMS message is so short, it is guaranteed to be delivered as a single packet of information.

SMS Guide: 2 Using the Mailbox Library

The Nokia 9000i Communicator comes with built-in capabilities to send and receive SMS messages. This is enabled through use of Geoworks' Mailbox library. Because the Mailbox library includes all Inbox/Outbox functionality (email, faxing, etc.), it contains a large amount of information that is not relevant to an SMS service developer. This section will only detail general information about the Mailbox library and API specific to SMS application developers.

Mailbox Overview

SMS Guide: 2.1 Using the Mailbox Library: Mailbox Overview

The purpose of the Mailbox library is to let applications send and receive information to and from other computers, through a variety of communications media. The mailbox insulates the application as much as possible from the mechanics of transmitting and receiving the information.

In order to use the Mailbox library, an application must package information in a format acceptable to both a data driver (which stores the message) and a transport driver (which sends the message) -- in this case the SMS transport driver. The application then registers this message with the Mailbox library. During the registration process, you specify in which data format the message is stored.

The Mailbox library stores messages that have not yet been transmitted in its outbox . Messages are held in the outbox until the hardware connection necessary for transmission is available. An application can also specify that a message not be delivered until a specific time. In this case, the message will automatically remain in the outbox until that time. While a message is in the outbox, the device's user can examine information about the message and delay or even cancel its delivery.

When the time comes to send the message, the Mailbox library activates the appropriate transport driver. This activation is transparent to an application developer.

SMS Guide: 3 Sending an SMS Message

There are three pieces of information that you will need to know when you send an SMS message. These are rather self-evident:

  • The text message.
  • The recipient's GSM phone number.
  • The cellular provider's phone number. All SMS messages are routed through the cellular provider.

If you wish the SMS message to be sent to a particular application on the receiving Nokia 9000i Communicator, you will also need the following information:

  • The application token ( TokenChars ) of the receiving app.
  • The ManufacturerID of the receiving app.

In GEOS, these two pieces of information are referred to as a GeodeToken . If the application is your own, the GeodeToken is easy enough to determine (since you will have defined it yourself). If the application is not your own, you will need to contact that manufacturer or cellular provider for this information.

Sending and the Mailbox Library

Using the SMS Mailbox API

SMS Guide: 3.1 Sending an SMS Message: Sending and the Mailbox Library

Any SMS message that is sent out to the cellular provider is routed through the mailbox's outbox . There are three basic paradigms involved in packaging up information for use by the outbox. Although knowledge of how they work is not essential to sending an SMS message, it may be important when troubleshooting problems.

Apart from any parameters sent to the mailbox, the three basic pieces of information the outbox needs to know in order to send a message are:

  • In what data format the message is "wrapped." The mailbox can handle any data format as long as it is wrapped (contained) in a format it can understand. The mailbox calls an appropriate "data driver" when storing the message before it is sent.
  • The type of data being sent. (The mailbox, itself, does not need to know the actual data; it only needs to know how the data is wrapped.) Telling the mailbox what the data is provides a black box abstraction.
  • The transport driver to use when sending the message.

When a message is placed in the outbox, the mailbox calls the appropriate data driver to store the data. When a connection is made, the mailbox calls the appropriate transport driver and sends the message. Note that the mailbox itself does not need to know the message's contents, or even its format.

SMS Guide: 3.2 Sending an SMS Message: Using the SMS Mailbox API

library mailbox
library foam
library ansic

To send an SMS message to the outside world, an application will need to perform the following steps:

  1. Set up the text message. In many cases, the text will reside in a simple text buffer. If you wish to address the SMS message to a particular application, you will need to prepend a twelve-character prefix to the text message. The message itself will then be, at most, 148 characters in length. Without a prefix, you may send a 160-character SMS message. (See Setting Up the Text Message .)
  2. Copy the text into a VM file. The Mailbox library may store an SMS text message in a variety of formats (indicated by type GeoworksMailboxDataFormatID ), but a VM file is the easiest to manipulate. This VM file will be accessed by a Mailbox data driver. (See Storing the Text Within a VM File .)
  3. Set up the SMSendingOptions parameters in a DB item. These will be used by the SMS transport driver when the message is delivered. (See Setting Up the SMSending Options .)
  4. Set up the MailboxRegisterMessageArgs structure. (See Setting Up the MailboxRegisterMessageArgs .)
  5. Call MailboxRegisterMessage() . (See Registering the Message .)
  6. Upon success, notify the Mailbox library that we are done with the VM file.

These steps are detailed in the sections following.

Setting Up the Text Message

foam.goh, Ansi/string.h

The Short Message Service protocol is able to handle a "short message" of up to 160 characters. The Foam library defines this character limit:

#define FOAM_MAX_SMS_TEXT_SIZE 160

The Nokia 9000i Communicator also allows you to further specify a particular application within the Nokia 9000i Communicator by adding a prefix to the SMS message (thus reducing the amount of useful text).

This prefix consists of two backslashes ("//"), a 4-byte GEOS application token ( TokenChars), a 5-byte GEOS ManufacturerID, and a carriage return ("/r", ASCII 13, or 0x0D hex). The Mailbox library will correctly read all characters up to the carriage return.

For example:

//SKAA11\r

is a valid prefix.

The application TokenChars and ManufacturerID should match those of the intended receiving application. If you define your own application, and only wish to communicate with its counterpart running on another device, you will define this string in the application's .gp file. The string is constructed from the tokenchars and tokenid lines of the .gp file.

You may also send a message to a different application, as long as you know its TokenChars and ManufacturerID .

This prefix should leave at least 148 characters to store the alpha-numeric text message. When concatenating two strings, be sure that you do not overflow the bounds of the array.

Code Display 3-1 Setting Up an SMS Text Message

/*
 * Declare the buffer to store the text message and write the prefix to the buffer.
 */
TCHAR smsMessage[FOAM_MAX_SMS_TEXT_SIZE + 1] = "//SKAA11\r";
TCHAR smsText[] = "Here's my test message. I better make sure it fits.";
/*
 * Add the body of the text to the buffer but first check the bounds.
 */
if ( strlen( smsMessage ) + strlen( smsText ) <= FOAM_MAX_SMS_TEXT_SIZE )
{
    strcat( smsMessage, smsText );
}

Storing the Text Within a VM File

mailbox.goh, Mailbox\vmtree.h, MailboxGetVMFile()

Text within an SMS message needs to be stored by the Mailbox until it connects with a cellular service provider. When you register a message with the Mailbox library, you indicate what storage format you have selected. The Mailbox uses a data driver to store and retrieve messages. There are a number of data drivers available, but we only need to know a few.

SMS messages may either be stored as VM (Virtual Memory) trees or as GEOS files, within the Mailbox itself. VM trees offer a lower overhead, and are simpler to use, so that is the format we have documented. (It is possible that future data drivers will become available as well.) If you are unfamiliar with GEOS VM trees, consult the Virtual Memory chapter of the Concepts book.

The mailbox data driver stores its data in what is called a VM tree. This tree is referenced by a VMTreeAppRef structure.

typedef struct {
    VMChain			VMTAR_vmChain;
    VMFileHandle			VMTAR_vmFile;
} VMTreeAppRef;
VMTAR_vmChain stores the VMChain to pass to the data driver. For SMS messages, simply use the VMCHAIN_MAKE_FROM_VM_BLOCK macro to construct a VMChain from a single VMBlockHandle .
VMTAR_ vmFile stores the VMFileHandle which contains the VM block.

The Mailbox stores these VM trees locally within an administation file that it maintains. To store the text within a Mailbox-compatible VM file, perform the following steps:

  1. Call MailboxGetVMFile() to retrieve a VM file handle within the Mailbox. This routine expects an argument indicating the expected number of blocks to be added to the file; because the SMS message can be contained in a single block, pass 1. Also pass a buffer to store the returned VMStatus.

    This routine returns a VMFileHandle.
  2. Allocate a VM block within this file, using VMAlloc().
  3. Lock the block down using VMLock(). This routine returns the segment address of the locked block. You will also receive a temporary memory handle that you can use to manipulate the block.
  4. Copy the text from your text buffer into the locked block starting at its segment address. (You will need to skip two bytes for the VMChainLink that begins the block.)
  5. Dirty the block using VMDirty().
  6. Unlock the block using VMUnlock().
  7. Store the VM file and VM block within a VMTreeAppRef structure. This format is understood by the mailbox data driver.

Code Display 3-2 Storing the Text In a VM File

VMFileHandle		vmFile;
VMBlockHandle		vmBlock;
TCHAR		*textPtr;
MemHandle		mh;
word		vmStatus;
VMTreeAppRef		vmRef;
/*
 * Obtain the handle of a writable VM file from the Mailbox library. We pass one
 * since we do not plan to add additional blocks to this file (because SMS messages
 * are so short).
 */
vmFile = MailboxGetVMFile(1, &vmStatus);
/*
 * Allocate a VM block and store the text within this block. The block will need
 * to fit a header (VMChainLink), the SMS message itself, and a null terminator.
 */
vmBlock = VMAlloc(vmFile, (sizeof(VMChainLink) + strlen(smsMessage) +1), 0);
textPtr = VMLock(vmFile, vmBlock, &mh);
/*
 * We advance past the VMChainLink contained at the beginning of the VM
 * block and copy the text to that location.
 */
strcpy(textPtr + sizeof(VMChainLink), smsMessage);
/*
 * Dirty the block and unlock it.
 */
VMDirty(mh);
VMUnlock(mh);
/*
 * Store the VM file and VM block within a VMTreeAppRef structure. This is the
 * format that the mailbox library understands. Essentially, it ia a VM "chain"
 * of a single VM file.
 */
vmRef.VMTAR_vmChain = VMCHAIN_MAKE_FROM_VM_BLOCK(vmBlock);
vmRef.VMTAR_vmFile = vmFile;

Setting Up the SMSending Options

internal\resp\smdefine.goh, MailboxGetAdminFile(), SMSendingOptions, ReadCSNumberFromIniFile()

When registering a mailbox message, the application must specify a data driver to use when storing the message. It will also need to specify which transport driver to use when the message is to be delivered. The GEOS SMS transport driver expects a number of parameters indicating how the SMS message should be sent. These parameters are passed as a SMSendingOptions structure within a GEOS DBItem.

To store these parameters within a compatible DB item, perform the following steps:

  1. Call MailboxGetAdminFile() to retrieve the VMFileHandle of the Mailbox administration file. This is where you will copy your SMS parameters.
  2. Allocate an ungrouped DB item within this file, using DBAllocUngrouped().
  3. Lock the DB item using DBLockUngrouped().
  4. Copy your basic SMSendingOptions parameters into this DB item.
  5. Read the service center number from the .INI file using InitFileReadStringBuffer(). Copy that number into the DB item.
  6. Dirty the block using DBDirty().
  7. Unlock the block using DBUnlock().
The SMSendingOptions structure

The SMSendingOptions structure is defined within smdefine.goh and is shown below:

typedef struct {
    SMReplyPath			SMSO_replyPath;
    SMValidityPeriod			SMSO_validityPeriod;
    SMMessageConversion			SMSO_messageConversion;
    TCHAR		SMSO_scAddress[MAX_ADDRESS_SIZE];
} SMSendingOptions;

#define MAX_ADDRESS_SIZE 22
SMSO_ replyPath contains an SMReplyPath value which indicates whether the recipient may reply to the message through the sender's service provider. Possible values are:
SMRP_NO
This flag is set by default and indicates that the recipient must reply through the recipient's service provider.
SMRP_YES
Setting this flag allows the recipient to reply through the sender's service provider.
SMSO _validityPeriod contains an SMValidityPeriod value which indicates the length of time that the message is considered valid for delivery. The cellular provider should interpret this value and if it is unable to connect with the recipient, drop the message and inform the sender that the message was not delivered. Possible values on pre-N9110 devices are:
SMVP_1_HOUR SMVP_6_HOURS
SMVP_24_HOURS
SMVP_1_WEEK
SMVP_MAX (The actual value of SMVP_MAX will vary by service provider.)

On the N9110 Communicator these values are:
SMVP_ONE_HOUR
SMVP_SIX_HOURS
SMVP_ONE_DAY
SMVP_ONE_WEEK
SMVP_MAX

SMSO_ messageConversion contains an SMMessageConversion value, indicating that you wish to have the SMS message delivered to the recipient in a different format than that of an SMS message (provided that the cellular provider offers this service). Possible values are:
SMMC_NORMAL
Deliver the message as a normal SMS message. This is the default value.
SMMC_FAX_GROUP_3
Deliver the message as a fax according to the fax group 3 standard.
SMMC_FAX_GROUP_4
Deliver the message as a fax according to the fax group 4 standard.
SMMC_VOICE
Deliver the message as a voice mail message.
SMMC_ERMES
Deliver the message according to the European Radio Messaging System standard.
SMMC_PAGING
Deliver the message as an alpha-numeric page.
SMMC_EMAIL
Deliver the message as an Internet e-mail message.
SMMC_X400
Deliver the mesage according to the X400 e-mail standard.
SMSO_ scAddress stores the address of the service center (cellular provider). This number is stored as an ASCII text string and is limited to 22 characters. This number should be retrieved from the Nokia 9000i Communicator's .INI file; the number is stored under the scNumber field within the SMS category. This "address" in this case is really just a numerical phone number.
Reading a String From the .INI File
initfile.h, InitFileReadStringBuffer()

To read a string from the .INI file, call InitFileReadStringBuffer() . Pass "SMS" as the category and "scNumber" as the key. The routine will read the entry into a buffer passed to the routine. It will also read the size of the string into another buffer.

Code Display 3-3 Setting the SMSendingOptions Parameters

SMSendingOptions			*ptrSendingOptions;
VMFileHandle			adminFile;
DBGroupAndItem			smsParams;
word			sizeOfBuffer;
adminFile = MailboxGetAdminFile();
smsParams = DBAllocUngrouped(adminFile, sizeof(SMSendingOptions));
ptrSendingOptions = (SMSendingOptions *) DBLockUngrouped(adminFile, smsParams);
/* set desired sending options */
ptrSendingOptions->SMSO_replyPath = SMRP_NO;
ptrSendingOptions->SMSO_validityPeriod = SMVP_ONE_HOUR;
ptrSendingOptions->SMSO_messageConversion = SMMC_NORMAL;
/* be sure to initialize these fields as well, otherwise the app will crash! */
ptrSendingOptions->SMSO_dataCodingScheme = 0;
ptrSendingOptions->SMSO_userDataLength = 0;
ptrSendingOptions->SMSO_userDataLength = 0;
InitFileReadStringBuffer("SMS",
            "scNumber",
            ptrSendingOptions->SMSO_scAddress,
            0,
            &sizeOfBuffer);
DBDirty(ptrSendingOptions);
DBUnlock(ptrSendingOptions);

Setting Up the MailboxRegisterMessageArgs

MailboxRegisterMessageArgs, MailboxTransAddr

By now, you have created a reference to the SMS message itself (within a VMTreeAppRef structure); you have also placed the SMS sending parameters within the mailbox administration file. You are now ready to load the MailboxRegisterMessageArgs structure with parameters. (Two of these parameters will be the location of the SMS message and its sending parameters.)

  1. Indicate the format in which you wish the SMS message stored while it resides in the outbox. (The mailbox will call the appropriate mailbox data driver when the message is registered.)
  2. Indicate the format of the actual message. This is where you tell the mailbox that this message is an SMS message.
  3. Indicate the VM file in which the SMS message is stored.
  4. Tell the mailbox to use the SMS transport driver when the mailbox sends the message.
  5. Give the mailbox the address of the recipient.
  6. Indicate the DB item where the SMSendingOptions are located.
  7. Tell the mailbox when to begin delivery of the message and when to give up delivery attempts if unsuccessful.

All of these steps are accomplished by simply filling in a MailboxRegisterMessageArgs structure. This structure is large, but it is not altogether complicated. Each field is described in the following sections, though only SMS relevant information is shown. It is shown below:

typedef struct {
    MailboxStorage				MRA_bodyStorage;
    MailboxDataFormat				MRA_bodyFormat;
    const void 				*MRA_bodyRef;
    word				MRA_bodyRefLen;
    MailboxTransport				MRA_transport;
    MailboxTransportOption				MRA_transOption;
    MailboxTransAddr				*MRA_transAddrs;
    word				MRA_numTransAddrs;
    dword				MRA_transData;
    MailboxMessageFlags				MRA_flags;
    const char				*MRA_summary;
    GeodeToken				MRA_destApp;
    FileDateAndTime				MRA_startBound;
    FileDateAndTime				MRA_endBound;
} MailboxRegisterMessageArgs;
Data Format for Storage

As we mentioned, the Mailbox will need to store a message in a format that it can understand. Rather than have the Mailbox understand every data format that is (or will ever be) developed, it is easier to "package" the message in a small number of standard formats. You specify in what format the message will be wrapped in the MRA _bodyStorage field.

MRA_ bodyStorage indicates under what format (of type MailboxStorage ) the message will be stored in the internal Mailbox administration file. MailboxStorage is defined as follows:
typedef struct {
    word			MS_id;
    ManufacturerID			MS_manuf;
} MailboxStorage;
MS_id
...stores a word of data indicating the type of data driver that will be used to store the message. For SMS messages, we will use a Geoworks data driver referenced by a GeoworksMailboxStorageID. The following types are valid:
    GMSID_FILE
    GMSID_VM_TREE
As we indicated earlier, SMS messages should be stored in VM trees. You do have the option of storing them as GEOS files as well, but VM trees are easier to manipulate.
MS_manuf
...stores the ManufacturerID of the data driver being used. Since we will be using Geoworks VM trees, we will store MANUFACTURER_ID_GEOWORKS in this location.
Data Format of the Message
MRA_bodyFormat
...indicates the format of the actual data being stored (in its original source). This field takes an argument of type MailboxDataFormat . This is defined below:
typedef struct {
    word			MDF_id;
    ManufacturerID			MDF_manuf;
} MailboxDataFormat;
MDF_id
...stores a word of data indicating the data stored in the message. We will use a Geoworks data format referenced by a GeoworksMailboxDataFormatID. Pass GMDFID_SHORT_MESSAGE in this field to indicate that the message is a Geoworks SMS message.
MDF_manuf
...stores the ManufacturerID of the format. Since we will be using Geoworks SMS , we will store MANUFACTURER_ID_GEOWORKS in this location.
References to the VM Tree Structure
MRA _bodyRef stores a pointer to the reference of the SMS message. Since we will be storing the message in a VM tree, we will reference that message with a VMAppTreeRef .
MRA_ bodyRefLen stores the length of the reference indicated in MRA_ bodyRef . In this case, we will store the size of the VMAppTreeRef structure.
The Transport Driver
The Mailbox uses an applicable transport driver when it wants to deliver a message to the outside world. You specify this transport driver in the MRA_ transport field.
MRA _transport takes an argument of type MailboxTransport . This is defined below:
typedef struct {
    word			MT_id;
    ManufacturerID			MT_manuf;
} MailboxTransport;
MT_id
...stores a word of data indicating the type of transport driver that will be used to deliver the message. For SMS messages, we will use a Geoworks transport driver referenced by a GeoworksMailboxTransportID.
To send an SMS message, we will use the SM transport driver referenced by GMTID_SM.
MT_manuf
...stores the ManufacturerID of the transport driver being used. Since we will be using Geoworks SM transport driver, we will store MANUFACTURER_ID_GEOWORKS in this location.
MRA_transOption
...stores an unsigned value that the the transport driver uses. For SMS, set this to zero.
MRA_transAddrs
stores the address of the eventual recipient. This address is stored as a pointer to a MailboxTransAddr structure; it is only important that the transport driver understand the format for this address.

If there are multiple recipients, this pointer references multiple MailboxTransAddr structures. This structure is defined below:
typedef struct {
    const void			*MTA_transAddr;
    unsigned			MTA_transAddrLen;
    const char			*MTA_userTransAddr;
} MailboxTransAddr;
MTA_transAddr
...stores a pointer to the recipient address, understood by the transport driver. (For SMS this is a simple ASCII text string of the recipient's phone number.)
MTA_transAddrLen
...stores the length of the address stored in MTA_transAddr.
MTA_userTransAddr
...stores a user-readable version of the address, as a null-terminated text string. Because the SMS address is already stored as an ASCII text value, this field will be the same as MTA_transAddr.
MRA_numTransAddrs
...stores the number of recipient addresses stored in MRA_transAddrs.
MRA_transData
...stores data understood by the transport driver. As mentioned in Setting Up the SMSending Options , the SMS transport driver expects a DBItem containing a structure of type SMSendingOptions .
MRA_flags
...stores a field of MailboxMessageFlags.
For SMS messages, the only flag you may care about is MMF_DELETE_BODY_AFTER_TRANSMISSION, which will delete the VM file containing the SMS message after it is delivered. Do not set any other flags.
MRA_summary
stores a text string that the Outbox displays to indicate the type of message being delivered. For example, if you are sending a chess move for a game in progress, you might set this field to display "Chess Move."
MRA_destApp
...stores the GeodeToken of the destination application.
MRA_startBound
...indicates a time and date (of type FileDateAndTime ) to begin transmission of the message. You may also pass the value MAILBOX_NOW (0) for immediate delivery.
MRA_endBound
...indicates a time and date (of type FileDateAndTime ) to end attempts to transmit the message to the service provider. You may also pass the value MAILBOX_ETERNITY (-1) to indicate attempt until successful.

Code Display 3-4 Setting Up the Mailbox Registration Arguments

MailboxRegisterMessageArgs				registerArgs;
MailboxTransAddr				addressList;
TCHAR				number[MAX_ADDRESS_SIZE];
/*
 * We tell the mailbox that the data is stored within a VMTreeAppRef.
 */
registerArgs.MRA_bodyStorage.MS_id = GMSID_VM_TREE;
registerArgs.MRA_bodyStorage.MS_manuf = MANUFACTURER_ID_GEOWORKS;
/*
 * We tell the mailbox that this is an SMS message.
 */
registerArgs.MRA_bodyFormat.MDF_id = GMDFID_SHORT_MESSAGE;
registerArgs.MRA_bodyFormat.MDF_manuf = MANUFACTURER_ID_GEOWORKS;
/*
 * We tell the mailbox where the body text is stored.
 */
registerArgs.MRA_bodyRef = &vmRef;
registerArgs.MRA_bodyRefLen = sizeof(VMTreeAppRef);
/*
 * We tell the mailbox to use the SM transport driver when sending the message.
 */
registerArgs.MRA_transport.MT_id = GMTID_SM;
registerArgs.MRA_transport.MT_manuf = MANUFACTURER_ID_GEOWORKS;
registerArgs.MRA_transOption = 0;
/*
 * Normally, we would retrieve the phone number from an outside source (such as
 * the contact database). Here, we just set it to a specific value.
 */
strcpy(number, "13125551212");
/*
 * We need to stuff each address (in this case, a phone number) into a
 * MailboxTransAddr structure.
 */
addressList.MTA_transAddr = &number;
addressList.MTA_transAddrLen = strlen(number);
addressList.MTA_userTransAddr = &number;
registerArgs.MRA_transAddrs = &addressList;
registerArgs.MRA_numTransAddrs = 1;
/*
 * We pass the previously set up SMSendingOptions in the DBGroupItem to the
 * transport driver.
 */
registerArgs.MRA_transData = smsParams;
registerArgs.MRA_flags = MMF_DELETE_BODY_AFTER_TRANSMISSION;
registerArgs.MRA_summary = "Chess Move";
strcpy(registerArgs.MRA_destApp.GT_chars, "SKAA");
registerArgs.MRA_destApp.GT_manufID = MANUFACTURER_ID_NOKIA;
registerArgs.MRA_startBound = MAILBOX_NOW;
registerArgs.MRA_endBound = MAILBOX_ETERNITY;

Registering the Message

MailboxRegisterMessage(), MailboxDoneWithVMFile()

Finally, you have everything set up. To send the message, all you need to do is register the message with the mailbox library.

MailboxRegisterMessage() takes an address (of type MailboxMessage ) as a second parameter. The routine will write a token value into this address so that future mailbox routines can refer to this message by its token.

Do not confuse this token with the actual message (which is sent through the MailboxRegisterMessageArg ).

After you have successfully registered the message, call MailboxDoneWithVMFile() to tell the mailbox to get rid of the temporary VM file it had created.

Code Display 3-5 Using MailboxRegisterMessage

MailboxMessage				msgToken;
MailboxError				mailboxError;
/* registerArgs and vmFile have been declared previously as:
 *    MailboxRegisterMessageArgs		registerArgs;
 *    VMFileHandle				vmFile;
 */
mailboxError = MailboxRegisterMessage(&registerArgs, &msgToken);
if (mailboxError == ME_SUCCESS) {
    /* If we want to do anything special upon success, here it would reside ... */
};
MailboxDoneWithVMFile(vmFile);

There is a possibility that MailboxRegisterMessage() may return an error (of type MailboxError ) if something is not working correctly (or if the message was not set up correctly). The routine returns ME_SUCCESS if the registration was successful.

The following are valid mailbox error codes:

ME_SUCCESS = 0
ME_NOT_ENOUGH_MEMORY
ME_CANNOT_LOAD_DATA_DRIVER
ME_CANNOT_LOAD_TRANSPORT_DRIVER
ME_UNABLE_TO_CREATE_TRANSMIT_THREAD
ME_CANNOT_ENQUEUE_MESSAGE
ME_USER_CANCELED
ME_LOST_CONNECTION
ME_CANNOT_CREATE_MESSAGE_FILE
ME_CANNOT_RESIZE_MBOX_REF
ME_CANNOT_SAVE_MESSAGE_FILE
ME_CANCELLED_BY_RECEIVER
ME_MESSAGE_BLOCKS_ARE_MISATCHED
ME_DATA_DRIVER_CANNOT_STORE_MESSAGE_BODY
ME_INSUFFICIENT_DISK_SPACE
ME_CANNOT_CONNECT

The following are unrecoverable MailboxError error codes. (The high bit is set if this is the case.)

#define ME_UNRECOVERABLE 0x8000
ME_ADDRESS_INVALID
ME_DATA_DRIVER_CANNOT_ACCESS_MESSAGE_BODY
ME_UNSUPPORTED_BODY_FORMAT
ME_MESSAGE_BODY_INVALID
ME_INVALID_MESSAGE
ME_REPLY_ADDRESS_NOT_AVAILABLE
ME_DESTINATION_APPLICATION_UNKNOWN
ME_UNKNOWN_DISK_ERROR
ME_APP_REF_BUF_TOO_SMALL
ME_CANNOT_OPEN_MESSAGE_FILE
ME_CANNOT_READ_MESSAGE_FILE
ME_CAN_NEVER_CONNECT

SMS Guide: 4 Receiving an SMS Message

There are two ways the Nokia 9000i Communicator may receive an SMS message: standard, or targeted.

As a developer, you do not need to concern yourself with standard messages since a "standard" message is handled by the Nokia 9000i Communicator's built-in SMS application. This application handles standard messages that are either sent without an SMS prefix or that are sent specifically to the SM Receive application.

Targeted messages are those messages sent using the twelve character prefix. These messages may not necessarily be sent using another Nokia 9000i Communicator; any device that uses the correct SMS prefix can contact a specific application if it so desires. In any case, the twelve character prefix (containing the GeodeToken ) will specify the application that is meant to receive the message.

SM Receive and the Mailbox Library

Receiving an SMS Message

SMS Guide: 4.1 Receiving an SMS Message: SM Receive and the Mailbox Library

When the GSM network informs the unit that is has an SMS message pending for receipt, it launches the SM Receive application. This application retrieves the SMS message(s). If the message is a standard SMS message, it routes the message to the application's "Received messages" folder.

If the message is targeted to a specific application, the SM Receive application contacts the mailbox and places the message in the mailbox's inbox . The mailbox then contacts the application by sending it a message ( MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE ). If the mailbox cannot contact the application, it re-routes the message back to the SM Receive application.

You do not need to know how the SM Receive application works, but it is useful to know the process which occurs when an SMS message is received.

SMS Guide: 4.2 Receiving an SMS Message: Receiving an SMS Message

When the inbox receives a message that it determines is destined for a specific application, the mailbox will contact that application's process class with MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE . This message passes a token identifer of the SMS message.

If you wish your application to handle an SMS message, you will need to write a message handler for MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE . It should perform the following steps:

  1. Call MailboxAcknowledgeReceipt() to inform the mailbox that we're handling the message. Pass the token identifer of the SMS message that was passed with MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE . (See Acknowledging Receipt of the Message .)
  2. Retrieve the VM reference to the message with MailboxGetBodyRef() . (See Retrieving the Text from the VM Block .)
  3. Lock down the VM block and retrieve a pointer to the text.
  4. Copy the text to a local buffer.
  5. Unlock the VM block.
  6. Call MailboxDoneWithBody() to instruct the mailbox to release the VM reference to the text. (See Cleaning Up .)
  7. Call Mailbox DeleteMessage() , passing the token referring to this message.

These specific steps are described in more detail below:

Acknowledging Receipt of the Message

MailboxAcknowledgeMessageReceipt()

When your application receives an SMS message with MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE , your first order of business should be informing the mailbox that you have the message. To do this, send the mailbox MailboxAcknowledgeMessageReceipt() , passing it the message token.

Code Display 3-6 Acknowledging Receipt of an SMS Message

/*
 * Each application which wishes to receive SMS messages should create a
 * MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE handler in the app's main prcoess
 * class.
 */
@method MyProcessClass, MSG_META_MAILBOX_NOTIFY_MESSAGE_AVAILABLE {
    /*
     * First we inform the mailbox that we've received the message. We pass the
     * `msg' token that was passed to this message in the first place.
     */
    MailboxAcknowledgeMessageReceipt(msg);
    /* To be continued ... */

Retrieving the Text from the VM Block

MailboxGetBodyRef()

As we indicated in Storing the Text Within a VM File , it is convenient for the SMS message itself to be wrapped in a small VMChain structure. In the following section, we assume that is indeed what was done on the sending end.

Use MailboxGetBodyRef() to retrieve this reference to the actual text. Pass the message token, a pointer to a buffer to store the body reference, and the length of that buffer.

Code Display 3-7 Retrieving the Text

    MailboxError 			errorCode;
    VMTreeAppRef			vmRef;
    word 			refSize;
    MemHandle			handleToText;
    TCHAR			smsText[FOAM_MAX_SMS_TEXT_SIZE+1];
    char			*ptrToText;
    /*
     * First, retrieve the reference to the body of the SMS text from the mailbox
     * library. Place that reference into our vmRef variable.
     */
    refSize = sizeof(VMTreeAppRef);
    errorCode = MailboxGetBodyRef(msg, &vmRef, &refSize);
    if (errorCode == ME_SUCCESS) {
    /*
     * Lock the block and retrieve a pointer to the SMS text.
     */
    ptrToText = (char *) VMLock(vmRef.VMTAR_vmFile,
                 VMCHAIN_GET_VM_BLOCK(vmRef.VMTAR_vmChain),
                 &handleToText);
    /*
     * Advance the pointer beyond the carriage return to get beyond the
     * prefix.
     */
    while ( *ptrToText != '\r')
        ptrToText++;
    /* Advance over the '\r' ...*/
        ptrToText++;
    strcpy(smsText, ptrToText);
    VMUnlock(handleToText);
    /* To be continued ... */

Cleaning Up

MailboxDoneWithBody(), MailboxDeleteMessage()

Finally, we need to notify the mailbox that it can delete both the text of the message (and the reference to that text) and the message itself. To perform each of these steps, call MailboxDoneWithBody() and MailboxDeleteMessage() .

Code Display 3-8 Cleaning Up the Mailbox

    /* Now remove the external text and the reference to that text. */
    MailboxDoneWithBody(msg, &vmRef, refSize);
    /*
     * Finally, delete the "message" including the message token and any
     * information included with it.
     */
    MailboxDeleteMessage(msg);
    }
}
This document is a single-page version of a a multi-page document, suitable for easy printing.


Communicator UI

The Nokia 9000i contains has a very specific look and feel which is created through the use of a specific user interface developed for the device. The specific UI contains not only visual representations for GEOS objects, but also additional objects that are particularly tailored for the Nokia 9000i device.

Most of these objects have been grouped into the "Foam" library. This library is a special collection of classes, objects and routines specifically applicable to the Nokia 9000i device. Because of its multi-faceted nature, the Foam library is not a coherent library so much as a "toolbox" of useful widgets.

We will not attempt to cover every piece of the Foam library within this chapter. Instead, we provide a brief overview of the foam UI classes, then delve into the objects which are particularly useful for creating your application.

These objects are:

  • FoamSubApplicationClass
  • ComplexMonikerClass
  • FlashingNoteClass
  • RepeatingGenGlyphClass
  • Special Foam dialog box routines
  • Special routines for presenting lists

0 Foam UI Classes Overview
1 FoamSubApplicationClass
1.1 FoamSubApplicationClass Instance Data
1.2 Using a FoamSubApplication Object
2 ComplexMonikerClass
2.1 ComplexMoniker Instance Data
2.2 ComplexMoniker Usage
2.3 ComplexMoniker Triggers
2.4 ComplexMoniker Messages
3 Convenient Dialog Routines
4 FlashingNoteClass
4.1 FlashingNote Instance Data
4.2 Setting Up the Template
4.3 Initiating and Destroying the Flashing Note
5 FoamProgressDialog
5.1 FoamProgressDialog Instance Data
5.2 Setting Up the Template
5.3 Using a FoamProgressDialog
6 Routines for Lists
7 Zoom Levels UI
8 FilterList, FilterText
8.1 FilterList and FilterText Usage
8.2 FilterList Instance Data
8.3 FilterList Messages
8.4 FilterText Instance Data
8.5 FilterText Messages

Communicator UI: 0 Foam UI Classes Overview

The foam library includes many new UI classes. This page lists these classes, briefly describes each class' utility, and displays an annotated snapshot of each UI class.

  • FFileSelectorClass
  • ExtendedFileSelectorClass
  • ComplexMonikerClass
  • TwoLevelTriggerClass
  • FoamSubApplicationClass
  • GreyFrameClass
  • ListWithUserDefinedClass
  • RepeatingGenGlyphClass
  • Dialogs
    • FlashingNoteClass
    • SelfDestroyingDialogClass
    • FoamProgressDialogClass
  • Indicators
    • GreyGenInteractionClass
    • IndicatorClass
    • IconIndicatorClass
    • LevelIndicatorClass
    • IndicatorInteractionClass
  • Filter List
    • FilterListClass
    • FilterTextClass
  • Underlined Text
    • UnderlinedGenTextClass
    • UnderlinedVisTextClass
  • Text Style Controllers
    • FoamTextStyleControlClass
    • FoamFontControlClass
    • FoamPointSizeControlClass
    • FoamJustificationControlClass

FFileSelectorClass

Inherits: GenFileSelectorClass

Use a FFileSelectorClass when you do not need a controller and the associated ComplexMoniker UI to select a file but only want to display a list of files (if you do want a full controller with complete UI, use FileOpenControlClass). FFileSelectorClass objects allow you to get and set the directory path using the FDocumentDir enum values for the directories which are specific to the Nokia 9000i Communicator.

An example of a FFileSelectorClass file selector.

Include: foam.goh

Reference: FileOpenControlClass, FFileSelectorClass.

ExtendedFileSelectorClass

Inherits: GenFileSelectorClass, but is a variant class.

This class is not available on build 4.8.8 or earlier versions of the Communicator.

The ExtendedFileSelectorClass expands the functionality of any subclass of GenFileSelectorClass, usually FFileSelectorClass. It allows multiple files to be selected, optionally provides the user with a search box that narrows the list of files to match the text typed in the search box, and can find files in any directory or subdirectory, not just the directories represented by the enumerated type FDocumentDir.

Include: foam.goh

Reference: ExtendedFileSelectorClass, FFileSelectorClass.

ComplexMonikerClass

Inherits: GenClass, but is a variant class.

The ComplexMonikerClass allows developers to create a moniker that contains both text and a bitmap icon. Since the text and icon are referred to generically, it is easy to localize the moniker. A complex moniker is a variant class - its parent class is dynamically created at runtime - so it can assume a variety of forms. The superclass of a ComplexMonikerClass object may be any child of GenClass. This allows complex monikers to be:

  • Triggers
  • Dialog boxs
  • Lists (items, grouped items, boolean items, contacts, or a dynamic list)
While each of the above items behaves quite differently, they all share the ComplexMonikerClass behavior of displaying a text and icon moniker.

Include: foam.goh

Reference: ComplexMonikerClass, GenTriggerClass, GenInteractionClass, GenItemGroupClass, GenItemClass, GenBooleanGroupClass, GenDynamicListClass, or ContactListClass.

TwoLevelTriggerClass

Inherits: ComplexMonikerClass

Applications that require more than four triggers may establish two panes of triggers. The user can toggle between these two panes using two TwoLevelTriggerClass trigger objects which usually have the monikers "More" or "Back". They are typically put in the fouth slot (slot 3). Since the "More" and "Back" triggers take up two slots, an application which uses two panes of triggers may define up to six of its own triggers.

Include: foam.goh

Reference: TwoLevelTriggerClass, ComplexMonikerClass, Nokia 9000i Communicator Topics: UI Conventions

FoamSubApplicationClass

Inherits: GenApplicationClass

Since the Nokia 9000i Communicator does not have a file manager per se, applications added to the device must be launched through a currently existing application. The FoamSubApplicationClass specifies in which pre-existing application an add-on application should reside.

Include: foam.goh

Reference: FoamSubApplicationClass, GenApplicationClass.

GreyFrameClass

Inherits: GenInteractionClass

Draws a gray beveled box around its children. It can be used with the ComplexMoniker variant class to add a moniker label (see FilterListClassexample).

Include: foam.goh

Reference: GreyFrameClass, GenInteractionClass.

ListWithUserDefinedClass

Inherits: GenItemGroupClass

GenItemClass and GenItemGroupClass work together to display a list of static monikers that the user may choose from. ListWithUserDefinedClass expands this to allow the user to navigate to an object and input information. The additional item may be a UnderlinedTextWithListClass, TextWithListClass, or GenValueWithListClass.

Include: foam.goh

Reference: ListWithUserDefinedClass, GenItemGroupClass.

RepeatingGenGlyphClass

Inherits: GenGlyphClass

RepeatingGenGlyphClass is used to create animations. It works be replacing the visMoniker of GenGlyphClass with the application-defined sequence of bitmapped icons, which it then cycles through. One common use of RepeatingGenGlyphClass is in dialog boxes which are opened with PutUpDialogViaUIThread(). This is done so that the animation is processed by another thread and thus doesn't burden the application.

Include: foam.goh

Reference: RepeatingGenGlyphClass, FoamProgressDialogClass.

Dialogs

FlashingNoteClass

Inherits: GenInteractionClass

The FlashingNoteClass is used to flash a note on the screen for a set length of time. The application specifies the minimum and maximum time the note will remain on the screen. If the user presses a key after the note has been on screen for the minimum duration, the note dialog box is dismissed. This dialog runs on its own thread and takes the responsibility for dismissing and destroying itself.

Include: foam.goh

Reference: FlashingNoteClass, GenInteractionClass.

SelfDestroyingDialogClass

Inherits: GenInteractionClass

The SelfDestroyingDialogClass is used to put up a dialog box that self-destructs upon dismissal.

Include: foam.goh

Reference: SelfDestroyingDialogClass, GenInteractionClass.

FoamProgressDialogClass

Inherits: GenControlClass

When an application starts a process that may take a while, it is good practice to put up a FoamProgressDialogClass dialog which notifies the user that the application is working and which indicates the status of the process. This dialog is put up on its own thread. The process is indicated by an animation of gears grinding away and by the status text which the application may append. If the user chooses the provided "Cancel" trigger a message is sent which indicates that the process should be terminated.

Include: foam.goh

Reference: FoamProgressDialogClass, GenControlClass.

Indicator Classes

The indicator classes exist in the foam library to facilitate drawing the status indicator at the left of the Nokia 9000i Communicator screen. As such, they are usually not as useful as Gen classes. Below is a brief listing of these classes followed by a screen shot of several UI objects which instantiate these classes.

GreyGenInteractionClass

Inherits: GenPrimaryClass

This class creates a primary window with a gray background. It is an inappropriate base for most applications because it only draws properly when placed against the left edge of the screen and it puts a gray background behind the trigger menu, thus making them look different from all other Communicator applications.

Include: foam.goh

Reference: GreyGenInteractionClass, GenPrimaryClass .

IndicatorClass

Inherits: GenGlyphClass

An IndicatorClass object is a GenGlyph that draws an etched border around itself.

Include: foam.goh

Reference: IndicatorClass, GenGlyphClass .

IconIndicatorClass

Inherits: IndicatorClass

This class is a GenGlyph that can draw one or two icons within etched borders from a list of bitmaps. The displayed icons can be changed dynamically.

Include: foam.goh

Reference: IconIndicatorClass, IndicatorClass.

LevelIndicatorClass

Inherits: IndicatorClass

The LevelIndicatorClass draws a bitmap followed by a sequence of bitmaps. This is used for displaying a level indicator such as the battery charge. The number of levels or the bitmap label can be changed dynamically.

Include: foam.goh

Reference: LevelIndicatorClass, IndicatorClass.

IndicatorInteractionClass

Inherits: IndicatorClass

This makes a GenInteraction with an etched border.

Include: foam.goh

Reference: IndicatorInteractionClass, GenInteractionClass.

Filter Lists

FilterListClass

Inherits: GenDynamicListClass

See below for description.

FilterTextClass

Inherits: TextWithListClass, which in turn inherits GenTextClass.

FilterListClass and FilterTextClass objects work together to provide the appropriate UI for interactive searches.

Include: foam.goh

Reference: FilterListClass, FilterTextClass, TextWithListClass, GenTextClass.

Underlined Text

UnderlinedGenTextClass

Inherits: GenTextClass

See below for description.

UnderlinedVisTextClass

Inherits: VisTextClass

UnderlinedGenTextClass and UnderlinedVisTextClass objects behave like the associated TextClass objects except that the text area has a dotted underline. These classes are used extensively for text-entry fields on the Nokia 9000i Communicator.

Include: foam.goh

Reference: UnderlinedGenTextClass, UnderlinedVisTextClass, GenTextClass, VisTextClass.

Text Style Controllers

FoamTextStyleControlClass

Inherits: GenControlClass

See below for description.

FoamFontControlClass

Inherits: GenControlClass

See below for description.

FoamPointSizeControlClass

Inherits: GenControlClass

See below for description.

FoamJustificationControlClass

Inherits: GenControlClass

The controllers FoamTextStyleControlClass, FoamFontControlClass, FoamPointSizeControlClass, and/or FoamJustificationControlClass attach to a text object and allow the user to change the displayed text styles. These controllers are functionally equivalent to the standard text object controllers (TextStyleControl, FontControl, etc.), but they have UI and features customized for the Nokia 9000i Communicator.

Include: foam.goh

Reference: FoamTextStyleControlClass, FoamFontControlClass, FoamPointSizeControlClass, FoamJustificationControlClass, GenControlClass.

Communicator UI: 1 FoamSubApplicationClass

Applications may be written to be incorporated into the Nokia 9000i Communicator as part of a special release. The Nokia 9000i Communicator does not have a file manager, per se, to enable launching these applications. The applications must be launched through a currently existing application. FoamSubApplicationClass allows you to indicate under which pre-existing application the new application should reside.

"Add-on" applications for the Nokia 9000i Communicator device, whether included on the device as part of a value-added release, or developed as an aftermarket product, will fall into one of three categories:

  • Service applications that communicate to the outside world.
  • System applications that perform a system function. (PC connectivity tools fall under this category, for example.)
  • "Extra" applications that don't fall into either of the two prior cases.

For each of these cases, there is an application already on the device that launches the sub-application. Service applications are launched using the SVCAPPS launcher; system applications use the SYSAPPS launcher; extra applications use the EXTRAPPS launcher.

Add-on applications are declared using the FoamSubApplication object, and an instance data field is set within the object indicating the "main" application it should be tied with.

Because a sub-application is not a separate application, special care needs to be taken when the sub-application is closed. This is discussed in Using a FoamSubApplication Object .

FoamSubApplicationClass Instance Data

Using a FoamSubApplication Object

Communicator UI: 1.1 FoamSubApplicationClass: FoamSubApplicationClass Instance Data

There is only one instance data field in a FoamSubApplicationClass object. This field is noted below.

Code Display 2-1 FoamSubApplicationClass Instance Data

@instance FApplication 	FSAI_launcherApp;

FSAI _launcherApp stores an FApplication type referring to the application it should reside under.

For FoamSubApplication objects, the following FApplication values are the only values that are relevant:

FA_SERVICES_APP
FA_SYSTEM_APP
FA_EXTRAS_APP

Communicator UI: 1.2 FoamSubApplicationClass: Using a FoamSubApplication Object

MSG_FSA_RETURN_TO_LAUNCHER

FoamSubApplication objects do not close like other applications. Because of this, they need to provide their own special "Close" trigger. However, FoamSubApplication objects should not simply "close" when the user presses this trigger. Instead, they should send MSG_FSA_RETURN_TO_LAUNCHER to this FoamSubApplication object.

This message changes the [hardIconBar] entry of the .INI file for the sub-application to be the launcher application (stored in FSAI_ launcherApp ). This allows the sub-application to seemingly close without closing the parent application. It is the responsibility of the application to provide a "Close" trigger that sends this message to the FoamSubApplicationClass object.

The "Close" trigger is declared as a ComplexMonikerClass object of type CMT_CLOSE. The ComplexMoniker is discussed more fully in ComplexMonikerClass .

Code Display 2-2 Using the FoamSubApplicationClass Object

/*
 * Declare the FoamSubApplication object as you would a normal GenApplication
 * object. This sub-app will be launched from the Extras application.
 */
@start   AppResource
@object FoamSubApplicationClass MyApp = {
    GI_comp = @MyPrimary;
    FSAI_launcherApp = FA_EXTRAS_APP;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = @MyPrimary;
    GI_visMoniker = list { @MyTextMoniker };
}
@visMoniker MyTextMoniker = "Nokia UI\rSample App";
@end     AppResource;
/*
 * Somewhere in your sub-app, create a CMT_CLOSE trigger.
 */
@object ComplexMonikerClass CloseTrigger = {
    ComplexMoniker = GenTriggerClass;
    CMI_topText = CMT_CLOSE;
    GTI_actionMsg = MSG_FSA_RETURN_TO_LAUNCHER;
    GTI_destination = @TemplateApp;
    HINT_SEEK_MENU_BAR;
    HINT_SEEK_REPLY_BAR;
    /*
     * Close Command triggers should always be in the fourth position (3 because
     * HINT_SEEK_SLOT refers to zero-based slot positions).
     */
    HINT_SEEK_SLOT = 3;
}

Communicator UI: 2 ComplexMonikerClass

ComplexMonikerClass is a versatile class that provides powerful behavior with little overhead. A complex moniker serves two purposes. First, it allows a developer to easily create a moniker containing both graphics and text. Second, it allows both text and graphics to be easily localized by referring to both items "generically."

A complex moniker may take a variety of forms. ComplexMonikerClass is what's known in GEOS as a "variant" class, which means that the object's parent class is dynamically assigned when the object is visually built.

A complex moniker may contain both a text string and a bitmap. The text string may be either a single line or two lines (separated by a `\r' character in the text buffer). If the text is two lines, it will be right-justified; this allows text strings in the command bar to align properly.

ComplexMoniker Instance Data

ComplexMoniker Usage

ComplexMoniker Triggers

ComplexMoniker Messages

Communicator UI: 2.1 ComplexMonikerClass: ComplexMoniker Instance Data

ComplexMonikerClass contains the following instance data:

Code Display 2-3 ComplexMonikerClass Instance Data

ComplexMoniker			ClassName
@instance TextStyle			CMI_textStyle = (TS_BOLD);
@instance word 			CMI_topText;
@instance word 			CMI_iconBitmap;
@instance byte 			CMI_fontSize;

The ComplexMoniker Keyword

Because ComplexMonikerClass is a variant class, it needs to have its superclass assigned when it is built. The ComplexMoniker keyword represents the parent class of the complex moniker. By setting this keyword to a class name the parent class is changed from the default superclass GenClass to the specified class. Any child of GenClass may be a complex moniker superclass. The following are commonly used superclasses:

GenTriggerClass
GenInteractionClass
GenItemGroupClass
GenItemClass
GenBooleanGroupClass
GenDynamicListClass
ContactListClass

Each of these classes will behave quite differently, but all will exhibit the same characteristics in displaying their text and icon monikers that are gained through using ComplexMonikerClass. Note that using a ComplexMoniker object will cause the specific UI to ignore any moniker stored in the GI_ visMoniker instance data field in the above classes.

The ComplexMoniker Text

CMI _topText stores the "text" to display. This may be either a Chunkhandle to a text string, or a ComplexMonikerText identifier. If the value is a ComplexMonikerText identifier, the specific UI will determine what exact text is displayed.

As noted, the text may contain two lines. The top text is limited to COMPLEX_MONIKER_MAX_TOP_TEXT_LENGTH while the bottom is limited to COMPLEX_MONIKER_MAX_BOTTOM_TEXT_LENGTH.

Below is a complete list of types; in most cases the (English) text on the Nokia 9000i device is similar, if not identical, to the text alluded to by the identifier's name. Those cases where the actual text is noticeably different are noted. Using such an identifier is beneficial for localization purposes.

CMT_NULL
Never set a moniker to this value. It exists purely as a placeholder.
Basic strings:
CMT_OK
CMT_CLOSE
CMT_CANCEL
CMT_YES
CMT_NO
Command strings:
CMT_CLEAR
CMT_CLEAR_ALL
CMT_CHANGE
CMT_RENAME
CMT_COPY
CMT_SETTINGS
CMT_OPEN
CMT_CREATE_NEW "New"
CMT_DELETE
CMT_ROTATE
CMT_SELECT
CMT_START
CMT_GO
CMT_INSTALL
CMT_DEINSTALL "Remove"
CMT_NEXT
CMT_PREVIOUS
CMT_USER_DEFINED "User defined:"
CMT_ZOOM
CMT_ZOOM_IN
CMT_ZOOM_OUT
CMT_VIEWING_TOOLS
CMT_OPTIONS
Communication strings:
CMT_PHONE "On"
CMT_CALL
CMT_CALL_LOWERCASE "Call"
CMT_SEND
CMT_SEND_OPTIONS
CMT_CONNECT
CMT_CALLS
CMT_FAXES
CMT_DATA_CALLS
CMT_MESSAGES
CMT_RECENT_CALLS
CMT_RECENT_FAXES "Recent numbers"
CMT_RECENT_MESSAGES "Recent numbers"
CMT_RECEIVED_CALLS
CMT_RECEIVED_FAXES "Received fax numbers"
CMT_RECEIVED_MESSAGES "Received message numbers"
CMT_MISSED_CALLS
CMT_LAST_DIALED_CALLS "Dialed calls"
CMT_TO_COLON "To:"
CMT_FROM_COLON "From:"
CMT_TEXT_SEND "Send text"
CMT_TEXT_CAPTURE "Capture text"
CMT_CAPTURE
CMT_STOP_CAPTURE
CMT_FORWARD_FAX "Forward"
CMT_SELECT_RECEIVER "Recipient"
CMT_HANGUP
CMT_FORWARD
CMT_REPLY
Miscellaneous commands:
CMT_CLEAR_LIST
CMT_NO_CONTACT_INFORMATION "No log information"
CMT_CONTACT_CARD
CMT_NONE
CMT_CLEAR_ALL_COMMAND
CMT_ACCEPT
CMT_RESET
CMT_STOP
CMT_ACTIVATE
CMT_DISABLE
CMT_HELP
CMT_BACK
CMT_SPECIAL_KEYS_COMMAND "Keys"
CMT_SPECIAL_KEYS
CMT_TEXT
CMT_TEXTS
CMT_ZOOM_PLUS "Zoom in"
CMT_ZOOM_MINUS "Zoom out"
CMT_INSERT
CMT_OFF
CMT_ON
CMT_MENU /* Not available on build 4.8.8 or earlier versions of the Communicator */
CMT_SEARCH /* Not available on build 4.8.8 or earlier versions of the Communicator */

Code Display 2-4 Setting a Complex Moniker's Text

/*
 * The text in a complex moniker is stored within the CMI_topText instance field.
 * This text may either be referenced by a ChunkHandle ...
 */
@object ComplexMonikerClass MyMoniker = {
    ComplexMoniker = GenTriggerClass;
    CMI_topText = @MyText;
}
/* Use the TCHAR type to ensure the text is DBCS-compliant. */
@chunk TCHAR MyText[] = "Sample Text";
/*
 * ... or CMI_topText can store a ComplexMonikerText type which indicates the text
 * generically.
 */
@object ComplexMonikerClass MyClearTrigger = {
    ComplexMoniker = GenTriggerClass;
    CMI_topText = CMT_CLEAR;
}

The ComplexMoniker Icon Bitmap

CMI_ iconBitmap stores the bitmap to display within this moniker. This may either be a ChunkHandle to a Bitmap structure or a member of the ComplexMonikerBitmap types. These types are enumerated below:

CMB_LEFT_FINGER
CMB_RIGHT_FINGER
CMB_CONTACT_CARD
CMB_CONTACT_LIST
CMB_SPEED_DIAL
CMB_SETTINGS_TITLE_ICON
CMB_SERVICES_VIEW
CMB_SERVICES_DEFINE
CMB_QUESTION_ICON
CMB_OPEN_FOLDER
CMB_DRAWER
CMB_RENAME_COPY
CMB_FOLDER
CMB_SECURITY_SUCCESS
CMB_SECURITY_ERROR
CMB_PC_ICON
CMB_INPUT_TITLE_ICON
CMB_WARNING_TITLE_ICON
CMB_SEARCH /* Not available on build 4.8.8 or earlier versions of the Communicator */
CMB_SMS_CARD_ICON /* Not available on build 4.8.8 or earlier versions of the Communicator */

Again, using a generic identifier eases localization.

You can create your own bitmaps by using the GEOS Icon Editor. To do so, perform the following steps:

  • Start GEOS (C:\PCGEOS\GEOS2XNC\LOADER.EXE
  • Launch the Icon Editor application.
  • Select either the 5th format (32x20 monochrome) or the 1st format (48x30 color). The first is used to create a monochrome bitmap; the second is used to create a 4-bit grayscale bitmap.
  • Edit the bitmap as desired.
  • The Nokia 9000i Communicator is a grayscale device. It is capable of handling 4-bit grayscale bitmaps ( BMFormat of BMF_4BIT). You may also create simple monochrome bitmaps ( BMFormat of BMF_MONO).
  • The GEOS Icon Editor only creates color bitmaps. The following colors in the Icon Editor will be mapped to the matching grey-scale shades on the device to create a grayscale bitmap:

    Icon Editor Color

    Nokia 9000i

    WHITE

    WHITE

    BLACK

    BLACK

    LIGHT_VIOLET

    LIGHT GRAY

    DARK_GREEN

    DARK GRAY
    (For more information about mapping colors to Nokia 9000i Communicator grays, refer to the N9000i Topics Sheet.)
  • Write the file out as filename .goh using the following options:
    Current Format
    Vismoniker source code
    Compacted
    Write mask (default)
  • Edit the first line of the filename .goh file and replace this line:
@visMoniker Moniker ={
  • with:
@chunk byte YourIconName[] = {
  • Delete the following lines from the beginning of the file:
size = <...>;
style = <...>;
color = <...>;
aspectRatio = <...>;
cachedSize = <...,...>;
gstring { GSFillBitmapAtCP(<...>),
  • Delete the following lines from the end of the file:
GSEndString()
}
  • Finally, add a semi-colon to the end of the file.

Your resulting bitmap, ready for inclusion in your app, should look like this:

@chunk byte MyIconName[] = {
    Bitmap (32,20,BMC_PACKBITS,
            (BMT_MASK|BMF_4BIT)),
    0xfd, 0xff, 0x03, 0xbf, ...
};

Text Styles

CMI_ textStyle stores a GEOS TextStyle , defined in graphics.h . By default, text is shown as TS_BOLD. The full list of styles appears below:

TS_BOLD (default)
TS_OUTLINE
TS_ITALIC
TS_SUPERSCRIPT
TS_SUBSCRIPT
TS_STRIKE_THRU
TS_UNDERLINE

Font Sizes

CMI _fontSize stores a font size (in points). There are four pre-established font sizes that you should choose from:

#define FOAM_LARGE_FONT_SIZE					20
#define FOAM_NORMAL_FONT_SIZE					18
#define FOAM_LIGHT_FONT_SIZE					17
#define FOAM_SMALL_FONT_SIZE					16

The large font size is best reserved for commands and title bar monikers. In most other cases, use the normal font size.

The default font can be obtained by those routines or objects that demand a font name by using the VisTextDefaultFont value VTDF_RESPONDER.

ComplexMoniker Vardata

There are several vardata attributes that you may attach to the Complex Moniker. These are noted below:

Code Display 2-5 ComplexMoniker Vardata

@vardata		void	ATTR_COMPLEX_MONIKER_PLACE_BITMAP_AT_LEFT;
@vardata		void	HINT_COMPLEX_MONIKER_DRAW_SEPARATOR;
@vardata		void	HINT_COMPELX_MONIKER_DONT_DRAW_SEPARATOR;

ATTR_COMPLEX_MONIKER_PLACE_BITMAP_AT_LEFT will position the icon bitmap (if any) to the left of the text.

HINT_COMPLEX_MONIKER_DRAW_SEPARATOR draws a single-line separator below the complex moniker.

HINT_COMPLEX_MONIKER_DONT_DRAW_SEPARATOR prevents the drawing of a single-line separator below the complex moniker.

Communicator UI: 2.2 ComplexMonikerClass: ComplexMoniker Usage

MSG_COMPLEX_MONIKER_GET_MONIKER, MSG_COMPLEX_MONIKER_REPLACE_MONIKER

ComplexMonikers are used for constructing lists, dialog boxes, and command triggers. Below are examples of a dialog box and a dynamic list. The next section will discuss command triggers. If you are not creating a command trigger, set CMI_fontSize to something other than FOAM_LARGE_FONT_SIZE.

Code Display 2-6 Complex Moniker Example (GenInteraction)

@object ComplexMonikerClass AboutBox = {
/* This object will behave as a GenInteration (grouping) object */
    ComplexMoniker = GenInteractionClass;
    CMI_topText = @AboutBoxTitle;
    CMI_iconBitmap = @AboutIcon;
    GI_comp = @AboutText;
/* Geometry Hints */
    HINT_DRAW_IN_BOX;
    HINT_DRAW_SHADOW;
    HINT_PLACE_MONIKER_TO_LEFT;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
/* ComplexMoniker Hints */
    HINT_COMPLEX_MONIKER_DRAW_SEPARATOR;
    ATTR_COMPLEX_MONIKER_PLACE_BITMAP_AT_LEFT;
}
@chunk TCHAR AboutBoxTitle[] = "CmplxMon Sample Application";

Code Display 2-7 ComplexMoniker Example (GenDynamicList)

@object ComplexMonikerClass CMTextsList = {
    ComplexMoniker = GenDynamicListClass;
    CMI_topText = @CMTextsListTitle;
    CMI_fontSize = FOAM_NORMAL_FONT_SIZE;
    CMI_iconBitmap = CMB_OPEN_FOLDER;
    GIGI_destination = process;
    GIGI_applyMsg = MSG_CMPLXMON_PROCESS_SHOW_CURRENT_CMTEXT;
    GDLI_queryMsg = MSG_CMPLXMON_PROCESS_GET_CMTEXT;
/* GDLI_numItems will be set at app-launch time. */
    HINT_DRAW_IN_BOX;
    HINT_DRAW_SHADOW;
    HINT_PLACE_MONIKER_ABOVE;
    HINT_PLACE_MONIKER_TO_LEFT;
    HINT_COMPLEX_MONIKER_DRAW_SEPARATOR;
    HINT_MINIMIZE_CHILD_SPACING;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
    HINT_ITEM_GROUP_SCROLLABLE;
    ATTR_COMPLEX_MONIKER_PLACE_BITMAP_AT_LEFT;
    ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED;
    ATTR_GEN_ITEM_GROUP_STATUS_MSG =
    MSG_CMPLXMON_PROCESS_SHOW_CURRENT_CMTEXT;
    }
}

Communicator UI: 2.4 ComplexMonikerClass: ComplexMoniker Messages

MSG_COMPLEX_MONIKER_GET_MONIKER, MSG_COMPLEX_MONIKER_REPLACE_MONIKER

To retrieve a complex moniker, send the ComplexMoniker MSG_COMPLEX_MONIKER_GET_MONIKER . This message takes a single argument, a pointer to a structure of type GetComplexMoniker . The structure will be filled in with the relevant information by the message.

typedef struct {
    TextStyle				GCM_textStyle;
    byte				GCM_fontSize;
    ComplexMonikerText			GCM_topText;
    ComplexMonikerBitmap		GCM_iconBitmap;
} GetComplexMoniker;

Send MSG_COMPLEX_MONIKER_REPLACE_MONIKER to replace a complex moniker with a new moniker.

This message takes two arguments: a ReplaceComplexMoniker structure and a ReplaceComplexMonikerChunkHandles structure.

The ReplaceComplexMoniker structure should hold the parameters for the new moniker.

typedef struct {
    TextStyle				RCM_textStyleSet;
    TextStyle				RCM_textStyleClear;
    word				RCM_fontSize;
    dword				RCM_topTextSource;
    dword				RCM_iconBitmapSource;
    ComplexMonikerSourceType		RCM_topTextSourceType;
    ComplexMonikerSourceType		RCM_iconBitmapSourceType;
    word				RCM_iconBitmapSize;
    RCMOverwrite			RCM_overwrite;
} ReplaceComplexMoniker;

RCM_textStyleSet stores a TextStyle record of styles that should be set (masked) on top of any currently existing styles.

RCM_textStyleClear stores a TextStyle record of styles that should be cleared if they are set among the currently existing styles. Note: Currently these bits are inverted rather than cleared.

RCM_fontSize stores the new font size. Pass 0 to indicate no change.

RCM_topTextSource stores a dword value containing, or pointing to, the source for the new text. The type of value stored here is indicated in RCM_topTextSourceType.

RCM_iconBitmapSource stores a dword value containing, or pointing to, the source for the new bitmap. The type of value stored here is indicated in RCM_iconBitmapSourceType.

RCM_topTextSourceType stores the type of text reference contained in RCM_topTextSource.

RCM_iconBitmapSourceType stores the type of bitmap reference contained in RCM_iconBitmapSource.

RCM_iconBitmapSize stores the size of the icon bitmap, in bytes.

RCM_overwrite indicates whether the existing complex moniker chunks should be overwritten. Pass TRUE if you wish this occur.

MSG_COMPLEX_MONIKER_REPLACE_MONIKER also passes a ReplaceComplexMonikerChunkHandles structure. This structure is used to place returned chunk handles of the text and bitmap.

typedef struct {
    ChunkHandle			RCMCH_topText;
    word			RCMCH_unusedCX;
    ChunkHandle			RCMCH_iconBitmap;
    word			RCMCH_unusedBP;
} ReplaceComplexMonikerChunkHandles;

RCMCH_topText contains the ChunkHandle of the text portion of the new complex moniker.

RCMCH_iconBitmap contains the ChunkHandle of the bitmap (graphics) portion of the new complex moniker.

Communicator UI: 3 Convenient Dialog Routines

The Foam library provides a number of routines useful for displaying simple dialog boxes to the user. Each of these displays a dialog box. The dialog box will have one or more buttons. Most of the functions have a return value; the function's return value will be an InteractionCommand value corresponding to which button the user pressed to dismiss the dialog. The FoamDisplay...NoBlock() routines don't have an explicit return value; they send a message if the user presses the OK button.

The following routines are available:

FoamDisplayWarning() , FoamDisplayWarningNoBlock()
These routines present a dialog box notifying the user about an action. The user may dismiss the dialog, but may not cancel the action. The function takes one argument, the optr of a string buffer to display. This function has no return value.
FoamDisplayError() , FoamDisplayErrorNoBlock()
These routines present a dialog box notifying the user that an error has occurred. The user may dismiss the dialog via the OK button. The function takes one argument: the optr of the string buffer containing the text to display. The function has no return value.
FoamDisplayOkCancelWarning()
This function presents the user with a warning in an OK/Cancel dialog. The function takes one argument: the optr of the string buffer to display. This function returns an InteractionCommand value; if the user presses the OK button, the function returns IC_APPLY.
FoamDisplayQuestion()
This routine presents a Yes/No dialog. It takes one argument, the optr of the text buffer containing the string to display. If the user presses the Yes button, the function will return IC_YES.
FoamDisplayNote() , FoamDisplayNoteNoBlock()
These functions display an OK dialog box displaying a text message to the user. The function takes one argument, the optr to a string buffer with the text to display.
FoamDisplayDeleteWarning()
This routine presents an OK/Cancel dialog box asking for a file deletion confirmation. This function takes three arguments: a pointer to a string buffer containing the file name, and optrs to two string chunks: one to display before the file name, and one to display after. If the function returns IC_YES, the user has confirmed that the file should be deleted.
FoamDisplayDeleteWarningNoBlock()
This routine presents an OK/Cancel dialog box asking for a file deletion confirmation. This function takes three arguments: the optr of the text string asking the user for confirmation, the message to send if the Delete button is pressed, and the object to send that message to. This function has no explicit return value. If the user Cancels the dialog, it won't send any message.

For dialog box types not provided by these functions, you will have to set up your UI, using GenInteraction objects in the usual way--or you may use the FlashingNoteClass or FoamProcessDialogClass , described later in this chapter.

Communicator UI: 4 FlashingNoteClass

Sometimes, you may want to send a notification to a user without requiring the user to respond. Ideally, you would like a dialog box to appear on screen for a few seconds, and then go away, or automatically disappear once any key is pressed. You can get this behavior with a FlashingNoteClass object.

You can provide the following parameters to a Flashing Note object:

  • A minimum duration to stay on-screen. After the minimum duration, the dialog can be dismissed by pressing any key.
  • A maximum duration to stay on-screen. (The dialog will dismiss itself if this time is reached.)

A Flashing Note should be placed as the root of an unattached tree within its own UI block (resource). In this manner, it will act as a template. To initiate the flashing note, call the routine PutUpDialogViaUIThread(), passing the optr of the dialog box template. This routine will copy the template resource, attach it to the application tree, and manage its thread of execution. The dialog will be run within the specific UI thread to avoid blocking the application thread.

FlashingNote Instance Data

Setting Up the Template

Initiating and Destroying the Flashing Note

Communicator UI: 4.1 FlashingNoteClass: FlashingNote Instance Data

FlashingNoteClass only contains one piece of instance data, but it also changes the defaults for its superclass, GenInteractionClass .

Code Display 2-9 FlashingNoteClass Instance Data

#define FLASHING_NOTE_DEFAULT_DURATION						(3*60) /* 3 seconds */
#define FLASHING_NOTE_DEFAULT_MINIMUM_DURATION						(1*60) /* 1 second */
@instance 	word		FNI_duration;
@default GII_attrs =				GIA_NOT_USER_INITIATABLE | GIA_SYS_MODAL);
@default GII_visibility =				GIV_DIALOG;
@default GII_type = 				GIT_MULTIPLE_RESPONSE;
@default FNI_duration = 				FLASHING_NOTE_DEFAULT_DURATION;
@vardata word		HINT_FLASHING_NOTE_DESTROY_SELF_AFTER_DISMISS;
@vardata word		ATTR_FLASHING_NOTE_MINIMUM_DURATION;

FNI _duration stores the maximum duration (in ticks -- 1/60ths of a second) for the dialog to remain on-screen. Do not set this to zero; doing so will make the dialog box "permanent" and incapable of dismissal.

Communicator UI: 4.2 FlashingNoteClass: Setting Up the Template

Usually, a FlashingNoteClass object (and its children) should be contained within their own resource. The resource should remain unattached to the application tree. The routine PutUpDialogViaUIThread() will take care of copying and attaching the group of objects.

To set up your template, perform the following steps:

  • Declare the template's resource and make it notDetachable .
  • Define the root object as a FlashingNoteClass object.
  • Set the root object not usable (~GS_USABLE).
  • Set the FNI_ duration instance field to the number of ticks (1/60ths of a second) you wish to have the dialog box on-screen. By default, this is 180 ticks (3 seconds).
  • If you want a minimum time for the dialog box to remain on-screen, add ATTR_FLASHING_NOTE_MINIMUM_DURATION and set it to the number of ticks (1/60ths of a second) to remain on-screen. By default, this is 60 ticks (one second).
  • Add HINT_FLASHING_NOTE_DESTROY_SELF_AFTER_DISMISS if you don't want to manually destroy it.
  • Add the hints HINT_WINDOW_NOT_TITLE_BAR and HINT_DRAW_IN_BOX .

Code Display 2-10 FlashingNote Template

@start FlashingNoteTemplateResource, notDetachable;
@object FlashingNoteClass FlashingNoteTemplate = {
    GI_states = @default & ~GS_USABLE;
    GI_comp = @DialogBoxTemplate;
    FNI_duration = 600;						/* 10 seconds */
    HINT_DRAW_IN_BOX;
    HINT_FLASHING_NOTE_DESTROY_SELF_AFTER_DISMISS;
    HINT_WINDOW_NO_TITLE_BAR;
    ATTR_FLASHING_NOTE_MINIMUM_DURATION = 300;						/* 5 seconds */
}
/*
 * We want a separator, so we will create a sub-group interaction as a complex
 * moniker.
 */
@chunk TCHAR DialogTemplateText[] = "Flashing Note";
@object ComplexMonikerClass DialogBoxTemplate = {
    ComplexMoniker = GenInteractionClass;
    CMI_topText = @DialogTemplateText;
    CMI_fontSize = FOAM_NORMAL_FONT_SIZE;
    GI_comp = @DialogText;
    HINT_PLACE_MONIKER_ABOVE;
    HINT_COMPLEX_MONIKER_DRAW_SEPARATOR;
    HINT_FIXED_SIZE = { SST_PIXELS | FOAM_DEFAULT_BUBBLE_WIDTH, 0 };
}
@chunk TCHAR DialogTextText[] = "This particular flashing note will stay up a
                minimum \r
                of 5 seconds, and will dismiss itself if you
                press any key after that.\r\r\
                It will dismiss itself automatically after 10
                seconds.\r";
@object GenTextClass DialogText = {
    GTXI_text = @DialogTextText;
    GI_attrs = @default | GA_READ_ONLY;
}
@end FlashingNoteTemplateResource;

Communicator UI: 4.3 FlashingNoteClass: Initiating and Destroying the Flashing Note

Initiating the flashing note is simple. Simply call PutUpDialogViaUIThread() , passing the optr of the root Flashing Note object.

If that object contains HINT_FLASHING_NOTE_DESTROY_AFTER_DISMISS , the dialog will be automatically detached and destroyed after it disappears from view (though the template block will remain untouched, of course).

Communicator UI: 5 FoamProgressDialog

When performing a time-consuming task, it is wise to prevent user frustration and use a FoamProcessDialogClass which informs the user of the task's progress and offers much-appreciated feedback.

The FoamProgress dialog provides the following functionality:

  • A default animation of gears in motion, informing the user that an action is taking place.
  • A "status indicator" consisting of a series of five dots. These five dots get displayed as the task gets closer to completion. Next to this status indicator, you may include status text, if desired.
  • Text which describes the task.
  • A default Cancel trigger.

As with a Flashing Note, a FoamProgress dialog should be placed as the root of an unattached tree within its own UI block (resource). To initiate the Foam Progress dialog, call the routine PutUpDialogViaUIThread(), passing the optr of the dialog box template. This routine will copy the template resource, attach it to the application tree, and manage its thread of execution. The dialog will be run within the specific UI thread, avoiding blocking the application thread.

The dialog can be taken down with TakeDownDialogViaUIThread() or by placing HINT_FOAM_PROGRESS_DIALOG_DESTROY_SELF_AFTER_DISMISS on the object.

FoamProgressDialog Instance Data

Setting Up the Template

Using a FoamProgressDialog

Communicator UI: 5.1 FoamProgressDialog: FoamProgressDialog Instance Data

FoamProgressDialogClass contains several pieces of instance data, and also alters the defaults of its superclasses, GenControlClass and GenInteractionClass .

Code Display 2-11 FoamProgressDialogClass Instance Data

@instance		ChunkHandle		FPDI_descriptionText			= NullChunk;
@instance		ChunkHandle		FPDI_statusText			= NullChunk;
@instance		optr		FPDI_cancelDestination			= NullOptr;
@instance		word		FPDI_cancelActionMsg			= 0;
@vardata void		HINT_FOAM_PROGRESS_DIALOG_DESTROY_SELF_AFTER_DISMISS;
@vardata void		ATTR_FOAM_PROGRESS_DIALOG_DONT_DISMISS_ON_CANCEL;
@default		GII_visibility		= GIV_POPUP;
@default		GII_type		= GIT_MULTIPLE_RESPONSE;
@default		GII_attrs		= (GIA_NOT_USER_INITIATABLE | GIA_MODAL);
@default		GI_attrs		= GA_SIGNAL_INTERACTION_COMPLETE;
@default		GI_states		= (@default | GS_ENABLED);

FPDI _descriptionText contains text to display to the user, informing them of the task being performed.
FPDI_ statusText contains text to display next to the status indicator.
FPDI _cancelDestination contains the object that should receive notification (via FPDI _cancelActionMsg ) if the action is cancelled by the user. The object must be capable of handling cancellation in a clean manner.
FPDI_ cancelActionMsg stores the message to send if the user cancels the operation. This message will be sent to the FPDI_ cancelDestination , where it should be handled appropriately.
HINT_FOAM_PROGRESS_DIALOG_DESTROY_SELF_AFTER_DISMISS indicates that the resource block containing this dialog should be destroyed after the dialog is dismissed. This hint indirectly calls UserDestroyDialog(); do not use this hint if you will use TakeDownDialogViaUIThread() instead.
ATTR_FOAM_PROGRESS_DIALOG_DONT_DISMISS_ON_CANCEL indicates that the dialog will not be dismissed if the user presses "Cancel." The application is responsible for handling this situation, and eventually dismissing the dialog.

Communicator UI: 5.2 FoamProgressDialog: Setting Up the Template

A FoamProgressDialogClass object (and its children) should be contained within its own resource. The resource should remain unattached to the application tree. The routine PutUpDialogViaUIThread() will take care of copying and attaching the group of objects.

To set up your template, perform the following steps:

  • Declare the template's resource and make it notDetachable .
  • Define the root object as a FoamProgressDialogClass object.
  • Set the root object not usable (~GS_USABLE).
  • Set the object GIA_SYS_MODAL (and make sure to turn off its default GIA_MODAL flag).
  • Set the FPDI_ descriptionText instance field with text describing the action.
  • Set FPDI_ statusText with text to reside along the status indicator.
  • Set HINT_DRAW_IN_BOX.

Code Display 2-12 FoamProgressDialog Template

@start FoamProgressDialogTemplate, notDetachable;
@chunk TCHAR DescriptionText[] = "While, we're waiting a series of dots will appear below."
@chunk TCHAR StatusText[] = "Here are the dots: ";
@object FoamProgressDialogClass ProgressSample = {
    FPDI_descriptionText = @DescriptionText;
    FPDI_statusText = @StatusText;
    GII_attrs = (@default | GIA_SYS_MODAL) & ~GIA_MODAL;
    GI_states = (@default) & ~GS_USABLE;
    HINT_DRAW_IN_BOX;
}
@end FoamProgressDialogTemplate;

Communicator UI: 5.3 FoamProgressDialog: Using a FoamProgressDialog

MSG_FOAM_PROGRESS_DIALOG_APPEND_STATUS_TEXT

Code Display 2-13 Using a FoamProgressDialog

static ShowProgressDialog ()
{
    optr			progressDialog;
    word			i;
    SemaphoreError			semResult;
    TCHAR			statusAppendStr[] = " . ";
    progressDialog = PutUpDialogViaUIThread(@FoamProgressDialogTemplate);
    for ( i=0; i<5; i++) {
    semResult = ThreadPTimedSem(cancelSemaphore, 0);
    if (semResult == SE_TIMEOUT) {
        @send ProgressDialog::MSG_MY_PROGRESS_DIALOG_CANCEL_RECEIVED();
    break;
    }
    else (
        ThreadVSem(cancelSempahore);
        TimerSkeep(30);
        @call progressDialog::MSG_FOAM_PROGRESS_DIALOG_APPEND_STATUS_TEXT(
                statusAAppendStr);
    }
    }
    TimerSleep(30);
    TakeDownDialogViaUIThread(progressDialog);
}
/* Handler for MSG_MY_PROGRESS_DIALOG_CANCEL_TRIGGERED */
@method MyProgressDialogClass, MSG_MY_PROGRESS_DIALOG_CANCEL_TRIGGERED
{
    SemaphoreError			semResult;
    semResult = ThreadPSem(cancelSemaphore);
    EC_ERROR_IF(
    (semResult == SE_TIMEOUT), CANNOT_GRAB_SEMAPHORE_CANCELPROGDIALOG);
}
/* Handler for MSG_MY_PROGRESS_DIALOG_CANCEL_RECEIVED */
@method MyProgressDialogClass, MSG_MY_PROGRESS_DIALOG_CANCEL_RECEIVED
{
    ThreadVSem(cancelSemaphore);
}

Communicator UI: 6 Routines for Lists

There are two routines useful for presenting the user with a list that is alphabetically sorted and/or presents has multi-column items.

The FoamGetLexicalOrder() routine is used for alphabetically ordering strings. It takes a char value and returns a byte-size number. This number represents that character's position in a sorted multi-language "alphabet". If you are presenting the user with a sorted list of strings, this is the routine to use to sort the list.

The CreateVisMonikerLine() routine puts together a multi-column visual moniker. It puts the moniker together from an array of strings and/or bitmaps you supply. It returns the optr of the new moniker. It takes the following arguments:

  • a font point size: All text will use this point size.
  • a font: All text will use this font.
  • the number of columns
  • an array of VisMonikerColumn structures: This data contains the data which will be combined into the multi-column moniker.

The VisMonikerColumn structure is defined:

typedef struct {
    byte            VMC_width; /* 0 for bitmaps */
    const void *    VMC_ptr;
    Justification   VMC_just;
    TextStyle       VMC_style;
    ColumnBorder    VMC_border;
} VisMonikerColumn;

For each field, pass a VMC_width to specify the number of pixels to allow to draw the text, or 0 to signal that this column is a bitmap. VMC_ptr should point to a string buffer if the column contains text; it should point to a simple bitmap if the column contains a bitmap.

When you are done using the multi-column moniker, it is up to you to free its memory. Get the memory block handle from the optr and pass that handle to MemFree() .

Communicator UI: 7 Zoom Level

Versions 4.8.8 or later of the 9000i Communicator allow the user to press an off-screen "Zoom" button to toggle between three text size settings -- "Zoom" levels -- and adjust the text size for more comfortable reading.

Normal zoom level.

Medium zoom.

High zoom.

Note that only some parts of the UI are enlarged as the user chooses a higher zoom level. This is done because it would be impossible to increase the size of some UI components, i.e. larger triggers would not fit into four slots. You should determine how you want your UI components to zoom, then specify the maximum zoom level (size) allowed for each UI component with the hint HINT_MAXIMUM_ZOOM_LEVEL = ... The three zoom levels are of the enumerated type ZoomLevel, which is defined as

typedef ByteEnum ZoomLevel;
#define ZL_ZOOM_1	0
#define ZL_ZOOM_2	1
#define ZL_ZOOM_3	2

HINT_MAXIMUM_ZOOM_LEVEL = ZL_ZOOM_1;, the default setting, means that a UI component cannot be zoomed larger than the normal size -- that is, it cannot be zoomed at all. HINT_MAXIMUM_ZOOM_LEVEL = ZL_ZOOM_3; implies that a UI component can be zoomed at the normal, medium, or maximum zoom levels.

The higher the zoom level, the larger the font size, and the fewer the number of characters that will fit into a fixed pixel length field. Thus, if you use a CreateVisMoniker...() routine, you will need to know how to size the VisMoniker so that it holds the same number of characters at any zoom level. To do this, first check the version number and make sure that it is software version 4.8.8 or later. If it is, then determine the new size of your font as follows:

Code Display 2-14 Determining the Point Size of a Zoomed Font

        /* Assume we have a word originalPointSize that is the original point size
           of the font. */

        /* The point size of the font, taking zoom level into consideration. */
        word zoomedSize;

        /* How 'zoomed in' we are. */
        ZoomLevel zoomLevel;

        zoomLevel  = FoamZoomGetLevel();
        zoomedSize = FoamConvertToZoomedSize( zoomLevel, originalPointSize );

        /* Now create the VisMoniker using zoomedSize as the font size. */

Ctrl-F1 is mapped to the "Zoom" button on the emulator.

Communicator UI: 8. FilterList, FilterText

FilterListClass and FilterTextClass objects provide the UI for interactive searches on lists of objects. When the user types a string into a FilterTextClass object, the FilterTextClass object calls a method that uses this text to create a filter for the FilterListClass object. This filter controls which monikers the FilterList displays, and in what order.

For example, the Composer application has a FilterListClass object that displays a long list of songs, and a FilterTextClass "Search" field. When the user types a string in the "Search" field, the FilterList displays only items that start with that string.

Composer Application FilterList, FilterTest.

A filter is an array of unique words (16-bit numbers) that controls which application item identifiers (the input to GDLI_queryMsg) are used at a given position on the displayed list (physical item identifiers). Filters let you map the internal list of monikers to any list configuration you want, including a list with multiple entries of the same moniker, an empty list, or a list in a new order.

For example, suppose we have a list of six colors (red, yellow, blue, green, gray, and orange) and a search box that narrows down the list of displayed colors. When the search field is empty, there is no filter and the list is displayed normally.

FilterList, FilterText Example: No Filter.

When we type 'g' into the search field, we only want to display colors that start with 'g'; green and gray. Since 'green' and 'gray' are the 3rd and 4th items (counting from zero) of the unfiltered list and we want to display them in the 0th and 1st entries of the filter list, we create the following filter, a word array with two entries:

Filter for FilterText 'g':
Entry # | Value
0 | 3
1 | 4

This filter is then passed to the filter list.

FilterList, FilterText Example: Filter for 'g'.

Here is a diagram of how filters map the application item identifier to the physical item identifier in the previous examples:

FilterList, FilterText: Filter Diagram.

If the search text filters out all items in the list then the moniker FLI_noMatchMoniker, which is optionally specified by the FilterListClass object, is shown.

FilterList, FilterText Example: No match for 'mauve'.

FilterList and FilterText Usage

FilterList Instance Data

FilterList Messages

FilterText Instance Data

FilterText Messages

Communicator UI: 8.1 FilterList and FilterText: FilterList, FilterText Usage

The following example shows:

  • A FilterListClass object.
  • A FilterTextClass object.
  • A query message handler that determines the moniker for a given application item.
  • A filter message handler that uses the text of the FilterTextClass to create a filter for the FilterListClass.

Create a FilterListClass object as you would create a GenDynamicList. Fill in the necessary GenItemGroup and GenDynamicClass data and specify a query message handler GDLI_queryMsg that will get the moniker corresponding to an application item identifier (a word). You can also specify the monikers to show when the list is empty either because it has no items (FLI_noItemsMoniker) or because it filters out all items (FLI_noMatchMoniker).

Code Display: Creating a FilterList object

/* In the definition of your destination object's class, define a message based
 * on the prototype GEN_DYNAMIC_LIST_QUERY. This prototype passes two arguments:
 * list (the optr of the list sending info) and item (the application item
 * identifier). */

    @message (GEN_DYNAMIC_LIST_QUERY_MSG) MSG_QUERY_COLOR_MONIKER;

/* Set up the selections, just as you would in a normal GenItemGroup. */

typedef enum {
    MC_RED = 0, MC_YELLOW, MC_BLUE, MC_GREEN, MC_GRAY, MC_ORANGE
} MyColors;
#define NUM_COLORS MC_ORANGE+1

/* In the object declaration, include normal instance data for a GenItemGroup and
 * GenDynamicList, making sure to also fill in GDLI_numItems and GDLI_queryMsg with
 * the proper information. The list will retrieve and display monikers automatically
 * through the query message as they are needed, just like a GenDynamicList.
 * The visMoniker FLI_noItemsMoniker is displayed if GDLI_numItems is zero.
 * FLI_noMatchMoniker is displayed when GDLI_numItems is not zero
 * but the filter eliminates all items from the list.  */

@visMoniker ListTestNoItems = "< No Colors >";
@visMoniker ListTestNoMatch = "< No Matching Colors >";
@object FilterListClass ListTestFilterList = {
    GIGI_destination = process;
    GIGI_selection = MC_BLUE;
    GIGI_applyMsg = MSG_NOTIFY_COLOR_CHANGE;
    GIGI_numSelections = 1;
    GDLI_numItems = NUM_COLORS;
    GDLI_queryMsg = MSG_QUERY_COLOR_MONIKER;
    GIGI_behaviorType = GIGBT_NON_EXCLUSIVE;

    FLI_noItemsMoniker = @ListTestNoItems;
    FLI_noMatchMoniker = @ListTestNoMatch;

    HINT_ITEM_GROUP_SCROLLABLE;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
    HINT_ITEM_GROUP_SHOW_SELECTION_EVEN_WHEN_NOT_FOCUS;
}

Create a FilterTextClass object, a text field that behaves like TextWithListClass (its parent class) except that when the user types in the text field, this object sends out a filter message FTI_filterMsg after an adjustable timout period FTI_filterTimeout. It is common practice to put FilterListClass objects in a gray box with a "Search" moniker and search icon.

Code Display: Creating a FilterText object

/* Make a gray complex moniker box with the text "Search" and a search icon that holds
 * the FilterTextClass object. */

@object ComplexMonikerClass ListTestSearchBarGroup = {
    ComplexMoniker = GrayFrameClass;
    CMI_iconBitmap = CMB_SEARCH;
    CMI_topText = CMT_SEARCH;
    CMI_textStyle = @default & ~TS_BOLD;
    CMI_fontSize = FOAM_NORMAL_FONT_SIZE;
    GI_comp = @ListTestSearchBarTextEdit;

    HINT_CENTER_CHILDREN_VERTICALLY;
    HINT_CENTER_MONIKER;
    ATTR_COMPLEX_MONIKER_PLACE_BITMAP_AT_LEFT;
    HINT_PLACE_MONIKER_TO_RIGHT;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
}

/* In the FilterText object declaration, be sure to set TWLC_listObj to the
 * FilterList object, set FTI_filterMsg to the filter message, and
 * give a timeout FTI_filterTimeout between when the user enters text
 * and the filter message is send. */

@object FilterTextClass ListTestSearchBarTextEdit = {
    GTXI_maxLength = 30;
    GTXI_attrs = @default | GTA_SINGLE_LINE_TEXT;
    GTXI_destination = process;
    TWLC_listObj = @ListTestFilterList;
    FTI_filterMsg = MSG_LTSBG_SEARCH_COLORS;
    FTI_filterTimeout = 0;                   /* Use a small timeout because
                                                  * the search is so fast. */

    HINT_DEFAULT_FOCUS;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
}

Create a query message handler for the filter list. This handler is passed the application item identifier (position) of the item to supply a moniker for. Write it as you would write a query message handler for a GenDynamicList, but substitute for the following functions:

  • MSG_FILTER_LIST_REPLACE_ITEM_MONIKER instead of MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER
  • MSG_FILTER_LIST_REPLACE_ITEM_MONIKER_OPTR instead of MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR
  • MSG_FILTER_LIST_REPLACE_ITEM_TEXT instead of MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT

For more information on the query handler message, refer to DynamicList Basics.

Code Display: Creating a FilterList query message handler

/* Create the list of data in the form of text strings. */

@start ItemText, data;

@chunk char *listMonikers[NUM_COLORS] = {
    "red", "yellow", "blue", "green", "gray", "orange"
};

@end ItemText

/* Create the query message handler. Each time the FilterList needs to display
 * an item, it will call this querying message with the application item
 * identifier of the item it needs to display.
 * DEFINITION: void MSG_QUERY_COLOR_MONIKER(optr list, word item) */

@method ListTestProcessClass, MSG_QUERY_COLOR_MONIKER {
     char ** listMonikersDeref;

     MemLock(HandleOf(@listMonikers));
     listMonikersDeref= LMemDeref(@listMonikers);
     @call ListTestFilterList::MSG_FILTER_LIST_REPLACE_ITEM_TEXT(item, listMonikersDeref[item]);
     MemUnlock(HandleOf(@listMonikers));
}

The filter message takes no arguments and returns nothing. It is called when the user types something into the FilterTextClass object. A standard filter message retrieves the filter text (for example, with MSG_VIS_TEXT_GET_ALL_PTR), performs some search, and then sends MSG_FILTER_LIST_INITIALIZE to the FilterList, passing the number of items in the filtered list (a word) and a filter (an array of unique words).

Code Display: Creating a FilterText filter message handler

    #include <Ansi/string.h>    /* To use strcmp() in MSG_LTSBG_SEARCH_COLORS.
                                 * To access the ANSI library, be sure to include
                                 * the line "library ansic" in the .gp file.  */

/* Define a message that has no parameters and returns nothing. It will
 * be sent by the FilterText object to search the FilterList. */

    @message void MSG_LTSBG_SEARCH_COLORS();

/* Create the filter message handler. MSG_LTSBG_SEARCH_COLORS
 * filters out all color monikers from the list that do not
 * start with the text of text of ListTestSearchBarTextEdit.
 * DEFINITION: void MSG_LTSBG_SEARCH_COLORS(void) */

@method ListTestProcessClass, MSG_LTSBG_SEARCH_COLORS
{
    word filter[NUM_COLORS];      /* The filter is a word array */
    word filterListSize = 0;      /* Number of items in filtered list */
    char searchBuffer[30];        /* Stores ListTestSearchBarTextEdit text */
    word searchBufferLength;      /* Size of ListTestSearchBarTextEdit text */
    char ** listMonikersDeref;    /* Stores array of color monikers */
    word item = 0;                /* Counter for loop through list items */

        /* Put text of ListTestSearchBarTextEdit into searchBuffer */
    @call ListTestSearchBarTextEdit::MSG_VIS_TEXT_GET_ALL_PTR(searchBuffer);

    searchBufferLength = strlen(searchBuffer);

    /* If the search text is empty, then pass a NULL filter to the filter list */
    if(searchBufferLength == 0) {
        @call ListTestFilterList::MSG_FILTER_LIST_INITIALIZE(NUM_COLORS, NULL);
        return;
    }

    MemLock(HandleOf(@listMonikers));
    listMonikersDeref = LMemDeref(@listMonikers);

        /* Loop to check whether the filter should include each list item */
    for (item = 0; item < NUM_COLORS; item++)  {
        if (strncmp(searchBuffer, listMonikersDeref[item], searchBufferLength) == 0) {
                filter[filterListSize] = item;
                filterListSize++;
        }
    }
    MemUnlock(HandleOf(@listMonikers));

      @call ListTestFilterList::MSG_FILTER_LIST_INITIALIZE(filterListSize, filter);
}

Although filter lists and filter texts are usually used to search on a list and narrow the results, you can use them for many other tasks, such as re-ordering the display of a list or displaying an item multiple times in a list.

You can store more information in a filter than just an application item identifier. If you want the query to highlight the part of a moniker that matches the search text, you could store the item number in the lower byte of the word and the position of the match in the upper byte. If you use unused bits to encode extra information about the item, this information is significant to the list because the entire word is part of the item ID. When the application wants to pass an item ID to the list, for example with MSG_FILTER_LIST_SET_SINGLE_SELECTION, it must pass the entire word including whatever extra information you encoded into the identifier.

Note that a FilterList does not require a corresponding FilterText. An application could generate a filter rather than waiting to be prompted by a FilterText object. But in most situations you will use a FilterList object together with a FilterText object.

Communicator UI: 8.4 FilterList, FilterText: FilterText Instance Data

Code Display: FilterTextClass Instance Data

@default GTXI_attrs = (@default | GTA_SINGLE_LINE_TEXT);

@instance word FTI_filterTimeout = 30;
@instance Message FTI_filterMsg;

FilterTextClass inheirits from TextWithListClass. When the user modifies the text in the text box, the user-defined message FTI_filterMsg is sent to the FilterTextClass's GIGI_destination. This message takes no arguments and returns nothing. This message's handler should use the text in the FilterText object to filter a FilterList object.

The user can specify a timeout delay FTI_filterTimeout (in ticks) between the time the text is modified and the time the message is sent. If your filter message will take a long time to process, then give a longer timeout to avoid long, pausing searches before the user is done typing in the full filter text. On the other hand, if your filter message is very fast then give a small timeout to make the UI very responsive. The default delay is half a second (30 ticks).

Communicator UI: 8.5 FilterList, FilterText: FilterText Messages

FilterTextClass behaves like its parent class TextWithListClass, but sends out the message FTI_filterMsg to filter a FilterList whenever text is entered into it.

MSG_FILTER_TEXT_SEND_FILTER_MSG
 void   MSG_FILTER_TEXT_SEND_FILTER_MSG(void);

This message causes the filter timeout to be reset and the FTI_filterMsg message to be sent. It is sent internally by the FilterText's timeout or by the application to cause an immediate filter.

Source: Unrestricted.

Destination: Any FilterTextClass object.

Parameters: None.

Return: Nothing.

MSG_FILTER_TEXT_RESET
 void   MSG_FILTER_TEXT_RESET(void);

Clears the text currently in the FilterText box and immediately sends out FTI_filterMsg, causing the destination to clear the search. This is the equivalent of sending MSG_VIS_TEXT_DELETE_ALL followed by MSG_FILTER_TEXT_SEND_FILTER_MSG.

Source: Unrestricted.

Destination: Any FilterTextClass object.

Parameters: None.

Return: Nothing.

MSG_FILTER_TEXT_RESET_TEXT_IF_NOT_FOCUSED
 void   MSG_FILTER_TEXT_RESET_TEXT_IF_NOT_FOCUSED(void);

This message erases the text in the FilterText box if the focus is lost.

Source: Unrestricted.

Destination: Any FilterTextClass object.

Parameters: None.

Return: Nothing.

Communicator UI: 8.2 FilterList, FilterText: FilterList Instance Data

A FilterList could have an empty list for two reasons: the unfiltered list might be empty, or the filter might not match any list items. FilterListClass instance data specifies the monikers to show when the list object is empty for either of these conditions. These monikers are optional. If they are not specified then the list will display nothing under the respective conditions.

Code Display: FilterListClass Instance Data

@instance @visMoniker FLI_noItemsMoniker;
@instance @visMoniker FLI_noMatchMoniker;
FLI_noItemsMoniker is the moniker to display when the list is empty and there is no filter.
FLI_noMatchMoniker is the moniker to display when the list is empty and there is a filter.

Communicator UI: 8.3 FilterList, FilterText: FilterList Messages

A FilterListClass object behaves like a GenDynamicList (its parent class) except that the application can supply a filter (a word array) that the FilterListClass object uses to map physical item identifiers to application item identifiers.

Changing the Filter:

MSG_FILTER_LIST_INITIALIZE
 void   MSG_FILTER_LIST_INITIALIZE(
        word numItems,
        word *filter );

Use the message MSG_FILTER_LIST_INITIALIZE whenever you change the filter or the number of items in a FilterList. This message corresponds to the GenDynamicListClass message MSG_GEN_DYNAMIC_LIST_INITIALIZE, but should be used instead of MSG_GEN_DYNAMIC_LIST_INITIALIZE. This message notifies the dynamic list to display a new list of items. If the list is already usable, it invalidates and deselects all the current items and re-requests all the monikers. The list will not scroll back to the top explicitly -- to do so, send a MSG_FILTER_LIST_MAKE_ITEM_VISIBLE message to get to the top of the list.

Source: Unrestricted.

Destination: Any FilterList object.

Parameters:

numItems
The new number of displayed items.
filter
The new list filter. filter is an array of application item identifiers to display in the list. If filter is NULL, then physical IDs and application IDs are the same, and no mapping between them takes place. If the filter is not null and numItems is 0, then there are no matching items, and FLI_noMatchMoniker will be displayed.

Return: Nothing.

Other Messages:

FilterListClass defines several messages to deal with application item identifiers that are analogous to several GenItemGroupClass and GenDynamicListClass messages:

  • MSG_FILTER_LIST_SET_SINGLE_SELECTION
  • MSG_FILTER_LIST_SET_MULTIPLE_SELECTIONS
  • MSG_FILTER_LIST_GET_SELECTION
  • MSG_FILTER_LIST_GET_MULTIPLE_SELECTIONS
  • MSG_FILTER_LIST_GET_ITEM_OPTR
  • MSG_FILTER_LIST_SET_ITEM_STATE
  • MSG_FILTER_LIST_IS_ITEM_SELECTED
  • MSG_FILTER_LIST_MAKE_ITEM_VISIBLE
  • MSG_FILTER_LIST_REPLACE_ITEM_MONIKER
  • MSG_FILTER_LIST_REPLACE_ITEM_MONIKER_OPTR
  • MSG_FILTER_LIST_REPLACE_ITEM_TEXT
  • MSG_FILTER_LIST_GET_FOCUS_ITEM
  • MSG_FILTER_LIST_SET_FOCUS_ITEM

The application must use the FilterListClass version of the message instead of the GenItemGroupClass or GenDynamicListClass messages to query and set item states. The semantics and parameters of each message are the same as the GenItemGroup/GenDynamicList counterpart, except where noted.

In method descriptions the word "identifier" is taken to mean application item identifier.

MSG_FILTER_LIST_SET_SINGLE_SELECTION
void    MSG_FILTER_LIST_SET_SINGLE_SELECTION(
        word identifier,
        Boolean indeterminate );

Corresponds to the GenItemGroupClass method MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION. This method sets exactly one selection for a list.

Parameters:

identifier
The identifier of the item to select.

indeterminate
Pass TRUE if the group should be marked indeterminate.

Return: Nothing.

MSG_FILTER_LIST_SET_MULTIPLE_SELECTIONS
 void   MSG_FILTER_LIST_SET_MULTIPLE_SELECTIONS(
        word *selectionList,
        word numSelections );

Corresponds to the GenItemGroupClass method MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS. This message sets multiple selections for a list. The caller must pass a pointer to a list of identifiers that should be selected, along with the number of selections to set. .

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters:

selectionList
A pointer to the buffer containing a list of item identifiers.
numSelections
The number of selections in the passed list.

Return: Nothing.

MSG_FILTER_LIST_GET_SELECTION
 word   MSG_FILTER_LIST_GET_SELECTION(void);

Corresponds to the GenItemGroupClass method MSG_GEN_ITEM_GROUP_GET_SELECTION. This message returns the current selection identifier for the list object, or the first selection in the item group if there are multiple selections in a non-exclusive or extended-selection-exclusive item group.

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters: None.

Return: The current selection stored in the FilterListClass's GIGI_selection instance field (or GIGS_NONE if there are no selections).

MSG_FILTER_LIST_GET_MULTIPLE_SELECTIONS
 word   MSG_FILTER_LIST_GET_MULTIPLE_SELECTIONS(
       word *selectionList,
       word maxSelections );

Corresponds to the GenItemGroupClass method MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS. This message returns the current list of selections for non-exclusive and extended-selection lists. The caller must allocate a buffer for the entries and pass the size of that buffer. If there is insufficient space in the passed buffer, no entries will be filled in. You should call MSG_FILTER_LIST_GET_NUM_SELECTIONS beforehand in order to allocate the correct maximum size for the buffer.

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters:

selectionList
Buffer to hold the selections.
maxSelections
Maximum number of selections to place in the buffer.

Return: Number of selections. selectionList is filled in with the identifiers of the selections (the list will not be null-terminated).

MSG_FILTER_LIST_GET_ITEM_OPTR
 optr   MSG_FILTER_LIST_GET_ITEM_OPTR(
        word identifier );

Corresponds to the GenItemGroupClass method MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR. This message returns the optr of a GenItem within a FilterList.

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters:

identifier
The identifier of the item to search for.

Return: The optr of the GenItem with the requested identifier (or a null optr if none is found).

MSG_FILTER_LIST_SET_ITEM_STATE
 void   MSG_FILTER_LIST_SET_ITEM_STATE(
        word identifier,
        Boolean state );

Corresponds to the GenItemGroupClass message MSG_GEN_ITEM_GROUP_SET_ITEM_STATE. This message sets the state (selected or unselected) of a particular GenItem within a FilterList.

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters:

identifier
The identifier of the item to change state.
state
TRUE to mark item selected, FALSE to mark item unselected.

Return: Nothing.

MSG_FILTER_LIST_IS_ITEM_SELECTED
 Boolean   MSG_FILTER_LIST_IS_ITEM_SELECTED(
        word identifier );

Corresponds to the GenItemGroupClass message MSG_GEN_ITEM_GROUP_IS_ITEM_SELECTED. This message checks whether an item within a FilterList is selected, even if that item is not usable, not enabled, or not within the FilterList. The message returns TRUE if the item is selected, FALSE if it is not selected.

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters:

identifier
The identifier of the item being queried.

Return: TRUE (non-zero) if the item identifier is selected.

MSG_FILTER_LIST_MAKE_ITEM_VISIBLE
 void   MSG_FILTER_LIST_MAKE_ITEM_VISIBLE(
        word identifier );

Corresponds to the GenItemGroupClass method MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE. This message ensures that an item within a scrolling FilterList is visible, by scrolling the list if necessary.

Source: Unrestricted.

Destination: Any FilterListClass object.

Parameters:

identifier
The identifier of the item to be made visible.

Return: Nothing.

MSG_FILTER_LIST_REPLACE_ITEM_MONIKER
 void   MSG_FILTER_LIST_REPLACE_ITEM_MONIKER(
        word item,
        ReplaceItemMonikerFlags flags,
        word height,
        word width,
        word length,
        VisMonikerDataType dataType,
        VisMonikerSourceType sourceType,
        dword source );

Corresponds to the GenDynamicListClass message MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER. This message sends a moniker to use for a particular list item.

Source: The object handling a FilterList's query message..

Destination: The FilterList specified in the query message.

Parameters:

item
The position of the item needing a moniker.
flags
RIMF_NOT_ENABLED if the item's moniker should be shown disabled.
height
If the item is a GString, the height in points.
width
If the item is a GString, the width in points.
length
Size of moniker data, in bytes. This value is ignored for VMST_OPTR. If the dataType is VMDT_TEXT and length is zero, the text moniker is assumed to be null-terminated. If the dataType is VMDT_GSTRING and the length is zero, the GString is assumed to end with a GR_END_GSTRING element.
dataType
The VisMonikerDataType of the actual moniker.
sourceType
The type of pointer referencing the moniker.
source
A pointer to the moniker data.

Return: Nothing.

MSG_FILTER_LIST_REPLACE_ITEM_MONIKER_OPTR
 void   MSG_FILTER_LIST_REPLACE_ITEM_MONIKER_OPTR(
        word item,
        optr moniker );

Corresponds to the GenDynamicListClass message MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR. This message is a simplified version of MSG_FILTER_LIST_REPLACE_ITEM_MONIKER. The moniker must be of type VisMoniker and referenced by an optr.

Source: The object handling a dynamic list's query message.

Destination: The FilterList object specified by the query message.

Parameters:

item
The position of the item to display.
optr
The optr of the VisMoniker to use.

Return: Nothing.

MSG_FILTER_LIST_REPLACE_ITEM_TEXT
 void   MSG_FILTER_LIST_REPLACE_ITEM_TEXT(
        word item,
        char *text );

Corresponds to the GenDynamicListClass message MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT. This message is a simplified version of MSG_FILTER_LIST_REPLACE_ITEM_MONIKER.

Source: The object handling a dynamic list's query message.

Destination: The FilterList object specified by the query message.

Parameters:

item
Identifier of item to replace.
text
Far pointer to a null-terminated text string.

Return: Nothing.

MSG_FILTER_LIST_GET_FOCUS_ITEM
 word   MSG_FILTER_LIST_GET_FOCUS_ITEM(void);

Corresponds to the GenItemGroupClass message MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM. This message gets the focus item of a FilterList.

Source: Anywhere.

Destination: Any FilterList.

Parameters: None.

Return: The identifier of the item which currently has the focus, or GIGS_NONE if none.

MSG_FILTER_LIST_SET_FOCUS_ITEM
 void   MSG_FILTER_LIST_SET_FOCUS_ITEM(
        word identifier );

Corresponds to the GenItemGroupClass message MSG_GEN_ITEM_GROUP_SET_FOCUS_ITEM. This message scrolls the FilterList list so that the specified item will be the visible focus.

Source: Anywhere.

Destination: Any FilterList.

Parameters:

identifier
The ID of the item which will be the focus of the item group.

Return: Nothing.

MSG_FILTER_LIST_MAP_PHYSICAL_TO_APP
word    MSG_FILTER_LIST_MAP_PHYSICAL_TO_APP(
        word    id);

Use MSG_FILTER_LIST_MAP_PHYSICAL_TO_APP to get the application ID that corresponds to a physical item location id on a FilterList from the filter.

Source: While this message can be called at any time, it is normally not necessary because the other FilterList methods automatically handle this mapping task.

Parameters:

id
The physical identifier of a list item.

Return: Returns the application item ID that corresponds to the physical item ID, or GIGS_NONE if the current filter doesn't contain the specified item.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Versions

This chapter discusses various new API features that have been added in recent versions of the Nokia 9000i and Nokia 9110 Communicators.

1 Software Version Number
2 User's Name
3 IMEI Code Number
4 New SMS Header
5 New Standard Monikers, Bitmaps
6 Non-exclusive Lists
7 Filter Lists/Filter Text
8 Incremental Searches
9 Contact's URL and Password
10 ContactList Searches
11 Unknown Phone Numbers
12 Detecting Hardware Type
13 Menu Trigger
14 Zoom Level
15 Memory Cards

Versions: 1 Software Version Number

The INI file contains the version number. The [ui] category's productName field will be one of the following strings:

  • Responder Build 3.8.8 (original release, does not support features described in this document)
  • Responder Build 4.3.7 (update)
  • Responder Build 4.8.? (later update)

There is also a software serial number, in the swSerialNumber field. This field has the format: 000000/00/AAABBCCDDLLLFKK/0

AAA : hardware version
1st digit: hardware major protocol (2)
2nd digit: hardware minor protocol (a)
3rd digit: hardware variant number (0)
This is not the actual hardware version, but rather
which version the software was compiled for. All
released images are for 2.10.0 ("2a0" hex).
BB :
build major number (03,04)
CC :
build minor number (08,03)
DD : build variant number (08,07)
Same as for productName, but padded with extra zeros.
LLL :
localization country code (044 = English, 033 = French, 049 = German)
F :
0=all current releases 1=certain future releases
KK :
keyboard mapping version (01 = English, 02 = French, 03 = German, 04 = Scandinavian)

Use one of the the IniFileReadString...() routines to find out the value of these INI file fields.

Code Display 13-1 Getting the Software Version Number

@start StringRes;
  @chunk TCHAR UICategory[] = "ui";
  @chunk TCHAR SWSerialNumber[] = "swSerialNumber";
@end StringRes;
...
@method TestProcessClass, MSG_TEST_PROCESS_GET_VERSION
{
    TCHAR version[28] = "";
    TCHAR *category;
    TCHAR *key;
    word  value;
    MemLock( OptrToHandle( @UICategory ) );
    category = (TCHAR *) LMemDeref( @UICategory );
    key = (TCHAR *) LMemDeref( @SWSerialNumber );
    /*
     * Use one of the InitFileReadString...() routines.
     * Extract the information you want from version string.
     */
    MemUnlock( OptrToHandle( @UICategory ) );
}

Further complicating matters, the swSerialNumber does not follow the format described above in the PC Emulator. On the PC emulator, the version string will look something like "Build X.X", where the Xs are numbers. These correspond to the build's major number.

Versions: 2 User's Name

The ContDB library has a new function, ContactGetUserName() , which returns the user's name.

Versions: 3 IMEI Code Number

To read the phone's IMEI number, you will need to use the EciImeiGet() function, provided by the eci library. This function takes one parameter, a pointer to a buffer to fill with the code number string. The buffer must be at least 17 bytes long, to hold the null-terminated 16-character code string. The function returns zero if the IMEI code was read succesfully, non-zero otherwise.

Code Display 13-2 Reading the IMEI Code

@include <eci.goh>
...
@method TestProcessClass, MSG_TEST_PROCESS_GET_IMEI
{
    TCHAR imei[17] = "";
    if ( EciGetImei( imei) == 0)
    {
      /*
       * We got IMEI code, do any check you want.
       */
    }
    else
    {
      /*
       * For some reason query failed.
       */
    }
}

Versions: 4 New SMS Header

In addition to the old way to send messages to application using their GeodeToken in front of the actual data, a new user data header is now introduced. Otherwise sending and receiving messages has not changed. The new header adopts the concept from Narrow Band Socket specification.

The new header format is //SCKddoo or //SCKLddddoooo where dd (and dddd) represent "destination port" and oo (and oooo) represent "originator port". Both these headers are terminated with space character.These ports are ascii coded hexadecimal numbers.

Short header covers ports from 0 to 255 (00 to FF in hex) and the longer form from 0 to 65535 (0000 to FFFF in hex).

The range of port numbers is divided to reserved port address range and to dynamic and/or private port address range. Usage of port address range 0 to 49151 is restricted and assignment of a port in this range requires contacting port address assignment authority. The reserved port address range is further divided to ports for "well-known protocols" (range 0 to 239 and 256 to 1024) and to registered ports (range 1025 to 49151). Nokia has reserved ports 5500 to 5599 for company-specific protocols. Port addresses in the dynamic address range 240 to 255 or in the dynamic and/or private address range 49152 to 65535 may be used freely by any vendor.

In order to receive these messages application must register themselves to SMS receive application. This can be done using smreg library. This library offers functions for registering and unregistering.

SMRegRegisterApplication() takes there parameters: GeodeToken of your application, a pointer to buffer containing "port numbers" you register for and number of messages you are registering. Port numbers must be ASCII coded hexadecimal numbers.

Code Display 13-3 Registering to receive "destination port" SMS Messages

@method TestProcessClass,  MSG_TEST_PROCESS_REGISTER
{
    GeodeToken appl;
    TCHAR messageProtocol[5];
    word protocol;
/* We register ourselves to listen port 43962 that is ABBA in hex.  */
    protocol = 43962;
    appl.GT_chars[0] = `T';
    appl.GT_chars[1] = `E';
    appl.GT_chars[2] = `S';
    appl.GT_chars[3] = `T';
    appl.GT_manufID = 11;
/* Let's ask smreg library to convert 43962 to ascii coded hex for us. */
    SMRegIntegerToHex( protocol, messageProtocol,
                         SMRITH_NULL_TERMINATE |SMRITH_WORD_LENGTH);
/* Now we have messageProtocol = "ABBA" */
    SMRegRegisterApplication( appl, messageProtocol, 1);
}

You can register more than one port by calling the function:

SMRegRegisterApplication( appl, "ABBADADA0055", 3);

Then you will receive messages ABBA, DADA and 55.

The function returns a SMRegResult value: SMRR_OK if your registration was succesful; otherwise it will be SMRR_FAIL.

SMRegIntegerToHex() function converts passed decimal integers to ascii coded hexadecimal strings. You pass a word containing integer to be converted, a character pointer where the result is stored and flags how the conversion is done. You can convert either byte length integers i.e. 0 to 255 and the result is two characters to word length i.e. 0 to 65535 and the result is four characters. By default the destination string is not null terminated, but if you pass flag SMRITH_NULL_TERMINATE null is added at the end of the string.

If you want to stop receiving messages you can unregister yourself. This can be done using SMRegUnregisterApplication() function. The parameters are identical to SMRegRegisterApplication. In addition SMMR_OK and SMRR_FAIL, this unregister function may return SMRR_APPLICATION_NOT_REGISTERED to indicate that you were not registered to listen this message.

The message routing table is stored to .INI file. You may freely modify it yourself, but it is not recommended. More than one application may want to listen same messages, so writing .INI file direct may cause problems to other applications. The category is SMSRouteTable and port numbers represent keys underneath.

Code Display 13-4 INI file: SMSRouteTable example

Here TEST application wants to receive messages 00F0 and ABBA and TES2 wants to receive message ABBA.

[SMSRouteTable]
00F0 = "TEST",11
ABBA = { "TEST",11
          "TES2",11 }

Versions: 5 New Standard Monikers, Bitmaps

There are new ComplexMonikerType values and a new ComplexMonikerBitmap:

  • CMT_MENU Useful for "Menu" buttons.
  • CMT_SEARCH
  • CMB_SEARCH

Versions: 6 Non-exclusive Lists

GenItemGroups may now be of type GIGBT_NON_EXCLUSIVE, allowing the user to select more than one item at a time (available in software versions 4.8.8 or later). For a complete discussion of non-exclusive lists, refer to GenItemGroup basics.

GenItemGroupClass now supports two messages which allow you to get and set which item has the "focus"--which single item the user currently has highlighted, as opposed to the selected items:

  • MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM
  • MSG_GEN_ITEM_GROUP_SET_FOCUS_ITEM

Code Display 13-5 Using MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM

@chunk TCHAR NoteSanFrancisoFocus[] = "San Franciso has the city list focus.";
@method TestProcessClass, MSG_TEST_PROCESS_GET_CITY_GROUP_FOCUS
{
    word city_selected;
    city_selected = @call CityGroup::MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM();
    if (city_selected == CG_san_francisco)
    {
        FoamDisplayNote(@NoteSanFrancisoFocus);
    }
}
ContactLists:

If you are using a ContactList (from the Contact DB library), you may set it up for multiple selections by giving it a ATTR_CONTACT_LIST_NON_EXCLUSIVE_LIST variable data field. To find out which records are selected in such a list, use MSG_CONTACT_LIST_GET_IDS_OF_SELECTED_RECORDS.

Versions: 7 Filter List/Filter Text

Responder Versions 4.8.8 and later support Filter List and Filter Text objects which together provide the UI for interactive searches on lists of objects. The Filer List object is a modified GenDynamicList and the filter text object is a modified TextWithList. When the user types a string into a FilterTextClass object, the FilterTextClass object calls a method that uses this text to create a filter for the FilterListClass object. This filter controls which monikers the FilterList displays, and in what order.

For example, the Composer application has a FilterListClass object that displays a long list of songs, and a FilterTextClass "Search" field. When the user types a string in the "Search" field, the FilterList displays only items that start with that string.

To create FilterList/FilterText pair:

  • Create a FilterListClass object.
  • Create a query message handler for the filter list. The Filter List calls this message to retrieve every item on the list.
  • Create a FilterTextClass object.
  • Create a Filter Text message handler that uses the text in the Filter Text object to create a filter. This filter determines which items in the Filter List will be displayed.

For more details, refer to the Filter List, Filter Text section of the Communicator UI chapter.

Versions: 8 Incremental Searches

To allow the user to narrow the choices shown in a list when they type into a text entry, use two new classes in the Foam library: FilterListClass and FilterTextClass. Their API may be found in the include file foam.goh.

These classes are only available after Build 4. Check the " F " field of the version number, as described above.

Versions: 9 Contact's URL and Password

The Contact DB Library has four new ContactFieldType values: CFT_TEL_FAX, CFT_TEL_DATA, CFT_URL and CFT_PASSWORD.

These values are only available after Build 4. Check the " F " field of the version number, as described above.

Versions: 10 ContactList Searches

Normally, a ContactList will lose track of the user's current search if the focus moves to another object. To force the list to remember the current search, send it a MSG_CONTACT_LIST_PRESERVE_SEARCH_ON_NEXT_LOST_FOCUS before it loses the focus.

This message is only available after Build 4. Check the " F " field of the version number, as described above.

Versions: 11 Unknown Phone Numbers

If the user enters a valid phone number in the search box of aContactList, but that number is not found, an application mayrequest that the ContactList send out a notification using a new variable data field.

Give the ContactList a ATTR_CONTACT_LIST_NOTIFY_NUMBER_ENTERED_MSG variable data field. This field's data is a message.

The message should have parameters:

  void MSG_MY_CL_NOTIFY( MemHandle phNumber = cx);

The passed block contains the phone number string. The message's recipient is responsible for freeing the passed block.

When using the Contact Log library to log a contact, if you don't know the phone number, you can pass LEF_WILDCARD_NUMBER to LogAddEntry() to specify that this call's number should match any phone number.

Versions: 12 Detecting Hardware Type

The Responder Library has a new routine RespGetPDAHardware() that lets an application determine the type of hardware it is running on, specifically the type of PDA and whether or not it has a backlight. This routine returns a PDAHardwareInfo byte flag. The bit PDAHI_BACKLIGHT_AVAILABLE (0x04) is zero if there is no backlight, or one if there is. The bits PDAHI_PDA_TYPE (0x02 | 0x01) are set to the type of PDA. These enumerated values may be PDAT_PC_EMULATOR (0x0), PDAT_N9000 (0x1), or PDAT_N9000i (0x2), corresponding to, respectively, the PC emulator, the Nokia 9000 released in 1996, and the Nokia 9000 released in the second half of 1997.

Versions: 13 Menu Trigger

Software versions 4.8 or later of the Communicator allow for a fifth "Menu" trigger that behaves just like a regular trigger except that it is invisible and does not take up a slot at the right side of the screen. It is activated whenever the user pushes the "Menu" button present on newer versions of the Communicator.

A "Menu" command trigger is defined just like any other command trigger, except it lacks a HINT_SEEK_SLOT and you must set CMI_topText = CMT_MENU;. The "Menu" button on the Communicator maps to Ctrl-F2 on the emulator.

For more information, refer to the Communicator Fact Sheet and ComplexMoniker Triggers.

Versions: 14 Zoom Level

Versions 4.8.8 or later of the 9000i Communicator allow the user to press an off-screen "Zoom" button to toggle between three text size settings -- "Zoom levels" -- and adjust the text size for more comfortable reading.

If you want a UI component to increase font size when the user increases the zoom level, then pass it HINT_MAXIMUM_ZOOM_LEVEL = ZL_ZOOM_3; (or ...ZL_ZOOM_2; if you want it be limited to medium-size).

The higher the zoom level, the larger the font size, and the fewer the number of characters that will fit into a fixed pixel length field. Thus, if you use a CreateVisMoniker...() routine, you will need to know how to size the VisMoniker appropriately for the current zoom level. For more details, refer to the Zoom Level UI chapter.

Versions: 15 Memory Cards

The Nokia 9110 Communicator has a slot for an optional memory card. The card is about the size of a postage stamp and provides up to 4 MB of extra Flash memory or ROM.

New API lets you read and write to a memory card. For more details, refer to the Memory Cards chapter.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Viewer-Compatible Documents

The user of a Nokia 9000i Communicator can create various kinds of documents, including mail, SMS messages, and notes. Most of these documents can be shared between applications. The SMS application can open short Mail documents, the Fax program can send text from Notes, the Notes application can read and edit many kinds of documents.

The Viewer library makes this document-sharing possible. An application can instantiate an object of FileOpenControlClass, a class defined by the Viewer library, which allows the user to select a document. The Viewer library will then start up another library which will allow the user to view and edit the document--a VILFile library exists for each document type. If you are defining a new kind of document to be compatible with the Viewer library, you will need to write a corresponding VILFile library.

Before embarking on this task, consider if it's appropriate. Do you really need to make your documents Viewer Library-compatible? Consider the "Tones" files created and edited by means of the Composer application. These are documents, but it seems unlikely that the user would want to view or edit these files from other applications--users probably don't want to send their Tones via Fax. Instead of creating a VILFile library to work with these documents, the programmers wrote a specialized application to work with the documents; this task was more straightforward than writing a VILFile library.

To define a new type of Viewer-library compatible document, you will need to do the following things:

Choose a four-character token.
When the Viewer library determines which VILFile library to use to view a document, it identifies the document's type by means of a four-character token. In later examples we will use the token 'TABV'.
Create a document browser application with a FileOpenControl.
Write an application that lets the user create documents of your type. To allow this application to work with the Viewer library, add a FileOpenControl object to the application, and configure the controller appropriately.
Associate the token with your VILFile Library.
The Viewer library uses a table in the device's INI file to figure out which VILFile library to use when viewing a document. Before the user can view one of your files, you set up the proper entry in the INI file. Assuming that you're writing a document browser application that creates these documents, this application could add the appropriate entry to the INI file.
Create a VILFile Library.
Set up the framework of your VILFile Library. There should be a UI resource containing a ViewerDialog and four entry point routines.
Decide on your document's file format.
Document data is stored in VM files. When the user creates or opens a document, the Viewer library will tell your VILFile library the file's directory and name. It's then up to your code to store data in and retrieve data from the file.
Decide on your UI's state block format.
If the device is turned off or is running low on memory, your VILFile library will have to store its state in a heap block; later on, it will have to restore its state from the data in that block. Your code should be able to store the name and location of the current document, along with the current state of UI gadgetry.
Customize the UI that displays and edits the document.
A ViewerDialog object is a a dialog which provides some standard triggers and behavior. To display the document's data, create a ViewerDialog and then place other objects within it.
Write the four entry-point routines for the library.
The Viewer library will call routines of your VILFile library when it's time to open a document, close a document, save to state, or switch between documents. You will need to write code for these four routines.
Install your geodes in the proper place.
Your application(s) probably belong in the device's WORLD\EXTRAPPS directory. Your VILFile library goes in the SYSTEM directory.

The rest of the chapter discusses how to carry out these tasks.

1 Document Browser Application
2 VILFile Library

Viewer-Compatible Documents: 1 Document Browser Application

You'll probably want to write some sort of document browser application to handle two tasks involved with supporting a viewer-compatible document type. (It's possible to carry out these tasks in other ways; in most cases, it's simplest to write an application.)

  • Associate the VILFile library with the document type token
  • Allow the user to create documents of the document type

Associating the VILFile Library with the Document Type Token

The system will recognize your document's type by its four-byte token. As soon as possible, you will need to tell the system which VILFile library to use to work with files of your type. If your software package includes some setup or install application, it can set up the association by editing the device's GEOS.INI file. If your package contains no such set-up application, but does contain some other application, make that application edit the INI file; warn users that the system will only recognize the document type after they've run the application.

The following code snippet shows how a GEOS program could edit the GEOS.INI to associate the token "TABV" with the tablev library. A program using this snippet would write a line to the vilFileTable section of the INI file reading "TABV = tablev.geo" ; the system will look for this line.

#include <initfile.h> /* header for InitFileWriteString() */

#define TV_FILE_TOKEN     "TABV"

@method MyProcessClass, MSG_GEN_PROCESS_OPEN_APPLICATION
{
  @callsuper();
  NEC( InitFileWriteString("vilFileTable", TV_FILE_TOKEN, "tablev.geo"); )
  EC(  InitFileWriteString("vilFileTable", TV_FILE_TOKEN, "tablevec.geo"); )
} /* MSG_GEN_PROCESS_OPEN_APPLICATION */

The application's process object will receive MSG_GEN_PROCESS_OPEN_APPLICATION soon after the application is run.

When we're testing on the error-checking version of the system, we'll use the error-checking version of the tablev library. The filename of the library differs between the normal and error-checking versions; we use the EC() and NEC() conditional compilation macros to make sure that we write out the correct file name.

Allowing the User To Create Documents of Your Type

The common way to allow the user to create Viewer-compatible documents of a given type is by means of a subclassed FileOpenControl object. Let's take a look at how one might configure such an object, then see how to integrate it into an application's UI.

[FileOpenControl screen shot]

The following code snippet shows a fairly feature-rich instance of a FileOpenControl object set up to create documents of the "TABV" type.

@include <viewer.goh> /* header for the Viewer library */

#define TV_FILE_TOKEN     "TABV"
#define TV_MAJOR_PROTOCOL 0
#define TV_MINOR_PROTOCOL 0

@class MyFileOpenControlClass, FileOpenControlClass;
@endc;

@classdecl MyFileOpenControlClass;

@object MyFileOpenControlClass MyFileOpen = {
  CMI_topText = @FCMoniker;
  ATTR_GEN_CONTROL_REQUIRE_UI = { FOF_MOVE | FOF_CREATE_FOLDER |
                                  FOF_COPY | FOF_OPEN |
                                  FOF_DELETE | FOF_RENAME_COPY |
                                  FOF_RENAME | FOF_CLOSE };

  ATTR_FILE_OPEN_CREATE_TRIGGER_MONIKER = @CreateTriggerMoniker;
  HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
  ATTR_FILE_OPEN_DEFAULT_FILE_NAME_PREFIX = @VFFPrefix;
  ATTR_FILE_OPEN_FILE_ATTR_INFO = {
    TV_FILE_TOKEN, 26, TV_MAJOR_PROTOCOL, TV_MINOR_PROTOCOL
  };
}
@chunk TCHAR FCMoniker[] = "CalcuWriter";
@chunk TCHAR CreateTriggerMonikerText[] ="New\rCalc";
@chunk FileOpenCreateNewMoniker CreateTriggerMoniker = {
  @CreateTriggerMonikerText,
  0
} ;

@method MyFileOpenControlClass, MSG_FILE_OPEN_CONTROL_GET_VIEWER_LIBRARY_NAME {
    GeodeToken           theToken;
    ViewerInterfaceError error;

    if ( error = ViewerGetFileToken( docInfo, &theToken ) ) {
      return( error );
    }

    if ( theToken.GT_chars[0] ) { /* check for null token */
      return( @callsuper() );
    }
    else {
      return( VIE_NO_VIEWER_LIBRARY );
    }

}

The sizing hint HINT_EXPAND_HEIGHT_TO_FIT_PARENT is fairly important. Without any sizing hint, the object is likely to change size over its lifetime.

The ATTR_FILE_OPEN_CREATE_TRIGGER_MONIKER variable data field allows you to specify a custom moniker for the control's "Create" trigger, if one exists. As shown in this example, this field should hold the chunk handle of a FileOpenCreateNewMoniker structure in the same resource as the FileOpenControl.

ATTR_FILE_OPEN_DEFAULT_FILE_NAME_PREFIX specifies a default file name for documents created with this controller (if it supports the feature of creating documents). This field should hold the chunk handle of a string in the same resource as the FileOpenControl.

If the FileOpenControl is to create new documents, specify the document type of those documents with ATTR_FILE_OPEN_FILE_ATTR_INFO. This field should contain a GeodeToken structure holding the document type's token, your ManufacturerID, and a protocol number. The ManufacturerID and token identify the type; the protocol number allows you to specify different versions of the type.

The ATTR_GEN_CONTROL_REQUIRE_UI (not used in this example) variable data field allow you to turn on some of the FileOpenControl's features. (Use ATTR_GEN_CONTROL_PROHIBIT_UI, not shown in this example, to turn off some of these features.)

FOF_RENAME
Allows the user to rename a file.
FOF_COPY
Allows the user to copy a file to a different directory.
FOF_RENAME_COPY
Allows the user to copy a file to another directory, specifying a new name for the copy.
FOF_DELETE
Allows the user to delete a file.
FOF_CREATE_NEW
Allows the user to create a new file. You will need to make sure that the FileOpenControl knows what kind of document to create, using the ATTR_FILE_OPEN_FILE_ATTR_INFO field.
FOF_OPEN
Allows the user to open an existing file.
FOF_CLOSE
Allows the user to close a subdirectory.
FOF_CREATE_FOLDER
Allows the user to create a subdirectory.
FOF_MOVE
Allows the user to move a file to a different directory.
FOF_COPY_TO_SOUNDS
Allows the user to copy the file to the Tones directory. A FileOpenControl does not normally display this directory.

The method for MSG_FILE_OPEN_CONTROL_GET_VIEWER_LIBRARY_NAME is a work-around for a bug. Without this work-around, the system will crash if the user uses the FileOpenControl to open a file that has a null token.

Fitting the FileOpenControl into your Application

In addition to configuring the application's FileOpenControl, you will need to do some things to allow the FileOpenControl to coexist with the rest of your application's objects.

  • Make sure the FileOpenControl object is on the MGCNLT_ACTIVE_LIST and GAGCNLT_VIEWER_NOTIFICATIONS notification lists. The FileOpenControl, like most controllers needs to be on these lists to get certain system notifications.
  • Add FileOpenControl to the application's UI tree. Typically, the FileOpenControl is the child of the application's primary window.
  • Fix overloaded button slots. Usually, a FileOpenControl will show additional triggers when it's displaying a subdirectory. For instance, it brings up a "Close" trigger.

    • An application might have some triggers in the same window that contains the FileOpenControl. For instance, there might be a "Close" trigger to exit the application. This can lead to overlaps when the FileOpenControl tries to put its own triggers in the occupied slots. To work around this, intercept MSG_FILE_OPEN_CONTROL_SUB_DIR_OPEN and MSG_FILE_OPEN_CONTROL_SUB_DIR_CLOSE and set the extra triggers not-usable or usable.

[FileOpenControl screen shot]

Figure: The "Close" trigger is covering another trigger in the same slot.

The following code snippets show how an application might carry out these tasks.

/* Put the FileOpenControl on the necessary notification lists */
@object FoamSubApplicationClass VFApp = {
  ...
  gcnList( MANUFACTURER_ID_GEOWORKS, MGCNLT_ACTIVE_LIST ) =
        @MyFileOpen;
  gcnList( MANUFACTURER_ID_GEOWORKS, GAGCNLT_VIEWER_NOTIFICATIONS ) =
    @MyFileOpen;
}

/* Make the controller a child of the primary window. */
@object GenPrimaryClass VFPrimary = {
  GI_comp = ... , @MyFileOpen ;
}

/* Disable and re-enable triggers whose slots overlap
   the FileOpenControl's when it views a subdirectory. */
@method MyFileOpenControlClass, MSG_FILE_OPEN_CONTROL_SUB_DIR_OPEN {
  @send VFCloseTrigger::MSG_GEN_SET_NOT_USABLE( VUM_DELAYED_VIA_APP_QUEUE );
}

@method MyFileOpenControlClass, MSG_FILE_OPEN_CONTROL_SUB_DIR_CLOSE {
  @send VFCloseTrigger::MSG_GEN_SET_USABLE( VUM_DELAYED_VIA_APP_QUEUE );
}

Viewer-Compatible Documents: 2 VILFile Library

Each viewer-compatible document type needs a VILFile library responsible for reading, writing, and displaying documents of that type. Writing such a library requires some UI work, VM file operations, and an understanding of the actions which the Viewer library requests of VILFile libraries.

Each VILFile library must have at least four entry points (exported routines); the Viewer library will call these routines when it needs the VILFile library to carry out certain tasks:

  1. Open:
    Purpose: The Viewer library needs the VILFile document to open a document.
    The user may have just now selected the document;
    the user may have just now created the document;
    the user may have opened the document a while ago, the application may have been shut down to state (and the document closed), and the application may now be opening again, so that the document must be re-opened.

    2. Action: Typically, the Open routine displays UI and caches information that will be needed later, including the document file's path. If the document is new, the routine is responsible for initializing the document.

  2. Detach (Quit to State):
    Purpose: The device must close the document though the user hasn't explicitly closed it. Perhaps the device is running low on memory and must shut down the viewing application, or perhaps the device is powering down.

    3. Action: The routine is responsible for collecting all state information and encoding it into a block of memory which it returns to the Viewer library. If the document file is open, this routine should close it. This routine is normally responsible destroy the UI which displays the document.

  3. Change Document (Switch Documents):
    Purpose: The user is jumping from document to document. The Viewer library needs the VILFile library to close the current document and open another.

    4. Action: The routine should close the current document and attempt to open the new document. This document's location is passed as an argument. It may update the displayed UI. It will probably need to update cached information useful for other tasks.

  4. Close Document:
    Purpose: The user is done with the current document. The Viewer library needs to close the document.

    5. Action: The routine is responsible for closing the current document. Typically, it will also destroy the UI used to display the document.

Below, we'll see source code for a simple VILFile library which carries out these duties.

GP File

Before discussing the VILFile library's procedural code, it's worthwhile to note directives needed in the library's GP file.

  • You will need to link the viewer library.
  • The first four exported routines correspond to the four routines which each VILFile library must support.
  • Typically, your library will create UI by making a copy of a template resource. This template resource should be marked read-only and shared in the GP file. We'll discuss this UI template in greater detail below.
  • Your library will almost certainly have to declare a subclass of ViewerDialogClass; export this class.

Code Display: Typical .GP file for a VILFile library

library viewer
library ansic    # Not all VILFile libraries will use ansic, but
                 # our example code does.  (It uses strncpy())

resource THEVIEWERUI           object read-only shared

export TVOPEN
export TVDETACH
export TVCHANGEDOCUMENT
export TVCLOSE

export MyViewerDialogClass

Declaring UI

The typical VILFile library will use UI gadgetry to display the contents of a document. The top object of this gadgetry should be a ViewerDialog object. Because it's an easy way to take care of some VILFile library duties, libraries often use a subclass of ViewerDialogClass.

Typically, the VILFile library creates the document display gadgetry by making a copy of a template UI resource. This resource should be marked as read-only and shared in the GP file. It should be declared notDetachable.

When it comes time to discard the UI, we'll send a MSG_VD_CLOSE to the ViewerDialog object. It will do some clean-up work before closing down.

[ViewerDialog screen shot]
@include <viewer.goh>

@class MyViewerDialogClass, ViewerDialogClass;

/* The following messages are utilities; we added them to help out
   with some VILFile library duties */
  @message word			MSG_MVD_OPEN_DOCUMENT( DocumentInfo *doc );
  @message void                 MSG_MVD_CLOSE_DOCUMENT();
  @message word			MSG_MVD_RESTORE_STATE( MemHandle stateBlock );
  @message MemHandle            MSG_MVD_SAVE_STATE();

/* In our dialog UI, we added a "Close" button.   It sends this message. */
  @message void 		MSG_MVD_CLOSE();

/* The following instance data fields contain cached information.
   The information is cached when the document is opened; the information
   is useful for accessing the document file and for closing it. */
  @instance VMFileHandle	MVDI_fileStash;
  @instance DocumentInfo        MVDI_docStash;

/* The dialog is a handy place to cache information.  This example
   displays the sum of two user-entered numbers.  It caches the3
   value of this sum in an instance data field;
   no doubt your library will have various data best cached in the dialog. */
  @instance WWFixedAsDWord 	MVDI_sumNum;
@endc;

@classdecl MyViewerDialogClass;

/* Here is the template UI block; the library will make a copy
   of this template whenever it needs to display a document. */
@start TheViewerUI, notDetachable;

/* At the top of the template's UI tree is a subclassed ViewerDialog */
@object MyViewerDialogClass TVDialog = {
  GI_comp = @TVLabel, @TVCloseTrigger,
            @TVAddend0, @TVAddend1, @TVSum ;
  GI_states = @default & ~GS_USABLE;
}

/* A GenGlyph to display the document name. */
@object GenGlyphClass TVLabel = {
  GI_visMoniker = "docName";
  @localize not;
}

/* In this example, the document's data consists of two numbers,
   which the program adds together.  These two GenValues show
   the numbers, and allow the user to change them. */
@object GenValueClass TVAddend0 = {
  GI_visMoniker = "add";
  GVLI_maximum = MakeWWFixed( 16000 );
}

@object GenValueClass TVAddend1 = {
  GI_visMoniker = "to";
  GVLI_maximum = MakeWWFixed( 16000 );
}

/* This GenGlyph displays the sum. */
@object GenGlyphClass TVSum = {
  GI_visMoniker = "0";
}

/* This is the Close trigger for the dialog. */
@object ComplexMonikerClass TVCloseTrigger = {
  ComplexMoniker = GenTriggerClass;
  CMI_topText = CMT_CLOSE;
  CMI_textStyle = TS_BOLD;

  GTI_destination = @TVDialog;
  GTI_actionMsg = MSG_MVD_CLOSE;

  HINT_SEEK_REPLY_BAR;
  HINT_SEEK_SLOT = 3;
}

@end TheViewerUI;

Four Entry Points

Your VILFile library must provide four entry points (exported routines). The order in which these routines are exported (in the GP file) is important; they should correspond to the values of the ViewerLibraryRoutine enumerated type (defined in viewer.goh). You will need to write these routines.

Open
This routine is responsible for opening the document or restoring from state. There is a number of other tasks which typically take place here.
  • Create UI to display the document. This is normally done by means of a call to UserCreateDialog(), passing the optr of a subclassed ViewerDialog object in a template resource. However you go about creating the UI, follow up by sending a MSG_VD_SET_VIEWER_PARAMS to the dialog.
  • The new UI is going to take up memory. The document browser application will probably need more heap space to allow this. The ViewerDialogClass has a mechanism to help with this. If you call GeodeRequestSpace() and pass the returned token to MSG_VD_SET_HEAPSPACE_TOKEN, the ViewerDialog knows to release this heapspace when it is dismissed. The upshot of all this: now is a good time to call GeodeRequestSpace().
  • Do some correctness checking on the document. Check its protocol number. If your document format has some "magic number" or checksum, this would be a good time to check it.
  • If there's a problem, alert the user and discard the UI by sending a MSG_VD_CLOSE to the ViewerDialog.

The routine is passed a ViewerParams structure which provides either state information or information about the document to be opened. The routine should return an optr (null if there was a problem). The Viewer library will pass this optr as an argument to subsequent routines.

If you're restoring from state, you'll be passed a block of memory which you previously filled with state information.

Detach (Save to State)
This routine is responsible for allocating and filling a block of memory with state information--information which may later be used to restore from state. This routine should also close the document file and discard the document-display UI.
Change Document (Switch Documents)
This routine is responsible for switching between two documents by closing the current document and opening another. The routine should check the new document for correctness (as described for "Open," above). It should update the document-display UI so that it corresponds to the new document. If there's a problem, it should discard the document-display UI.
Close
This routine is responsible for closing the current document. It should close the document file and discard the document-display UI.

It's a good idea to keep the document file open as long as the the document is open--this marks the file as "in use" so that the user won't try to open it and change it in another application.

Code Display: Sample handlers for the four entry points

/* VLR_OPEN       optr( ViewerParams *vp ) */
optr _pascal TVOpen( ViewerParams *vp ) {
  optr   		theTableUI;
  ReservationHandle     moreSpace;

  /* We want to request more heapspace for the app to hold all
     the memory we're going to need. */

  moreSpace = GeodeRequestSpace( 14, GeodeGetProcessHandle() );
    /* We got that 14 by using the 'heapspace' Swat command on an
       app that uses this library, trying to figure out how many
       more k the application needed when it was using this library. */

  if ( moreSpace == 0 ) {
    /* We should alert the user that we're out of memory,
       perhaps with a SysNotify().  But I won't do that for
       now because I'm lazy.  */

    return( NullHandle );
  }

  /* Create the UI to display the document by copying the template resource */
  theTableUI = UserCreateDialog( @TVDialog );

  /* Tell the ViewerDialog about the heapspace we reserved.  The dialog will
     free it when closed. */
  @call theTableUI::MSG_VD_SET_HEAPSPACE_TOKEN( moreSpace );

  @call theTableUI::MSG_VD_SET_VIEWER_PARAMS( vp );

  /* Are we restoring from state or just opening a document? */
  if (vp->VP_state == FALSE ) {

    /* We're opening a document.  Is it new?  */
    if ( vp->VP_attr & VA_NEW_DOCUMENT ) {

      /* Initialize the new document -- we'll see this code later */

    }
    /* Call the code we wrote that opens and displays the document */
    if ( 0 == @call theTableUI::MSG_MVD_OPEN_DOCUMENT( &vp->VP_documentInfo )) {
      /* If there was a problem, discard the UI. */
      @send theTableUI::MSG_VD_CLOSE(GeodeGetCodeProcessHandle(), VCS_CLOSED );
      return( NullHandle );
    }
  }

  else {  /* We're restoring from state. */
      /* Call the code we wrote that restores from state. */
      if (0 == @call theTableUI::MSG_MVD_RESTORE_STATE( vp->VP_state )) {
        /* If there was a problem, discard the UI. */
        @send theTableUI::MSG_VD_CLOSE( GeodeGetCodeProcessHandle(), VCS_CLOSED );
        return( NullHandle );
      }
  }

  /* Whether we're opening a document or restoring from state, we've
     initialized the UI correctly.  Bring up the dialog box. */
  @call theTableUI::MSG_GEN_INTERACTION_INITIATE();

  return( theTableUI );
}

/* VLR_DETACH hptr( optr optrUI );  */
hptr _pascal TVDetach( optr optrUI ) {
  /* We will save our data to state, close the document, and discard UI */
  MemHandle theState;

  theState = @call optrUI::MSG_MVD_SAVE_STATE();
  @call optrUI::MSG_MVD_CLOSE_DOCUMENT();
  @call optrUI::MSG_VD_CLOSE( GeodeGetCodeProcessHandle(), VCS_CLOSED );
  return( theState );
}

/* VLR_CHANGE_DOCUMENT   void( ViewerChangeDocumentStruct *vcds) */
void _pascal TVChangeDocument( ViewerChangeDocumentStruct *vcds) {
  /* Switch between documents--close the current document and open
     the passed document. */
  @call vcds->VCD_viewerOD::MSG_MVD_CLOSE_DOCUMENT();
  if ( 0 == @call vcds->VCD_viewerOD::MSG_MVD_OPEN_DOCUMENT( &vcds->VCD_documentInfo )) {
    @send vcds->VCD_viewerOD::MSG_VD_CLOSE(GeodeGetCodeProcessHandle(), VCS_CLOSED );
  }
}

/* VLR_CLOSE  void (optr optrUI) */
void _pascal TVClose( optr optrUI ) {
  /* Close the current document and discard UI */
  @call optrUI::MSG_MVD_CLOSE_DOCUMENT();
  @call optrUI::MSG_VD_CLOSE( GeodeGetCodeProcessHandle(), VCS_CLOSED );
}

Supporting Other Document UI

Not all document-manipulation requests need come in via the four entry points described above. For example, you might want to add a "Close" trigger to the document display dialog. Pressing the button corresponding to this trigger might result in closing the document and discarding the document display UI. The following code snippet shows how your program might handle a Close trigger's action.

@method Close, MyViewerDialogClass, MSG_MVD_CLOSE {
  /* The user pressed the "Close" button.  Close the document
     file and discard the document display UI */
  @call self::MSG_MVD_CLOSE_DOCUMENT();
  @send self::MSG_VD_CLOSE( GeodeGetCodeProcessHandle(), VCS_CLOSED );
}

Creating, Opening, and Saving Document Files

As discussed, VILFile will, in response to requests from the system or requests generated internally, want to act upon the document file. We haven't talked about how a program goes about doing so.

When the Viewer library requests that the VILFile library open the document file, it will pass the file's path, encoded as a FoamPathHandle, which is equivalent to a FDocumentDir. It will also pass the document file's name. Using a combination of Foam and kernel library routines, your library can open the file and manipulate its contents as shown in the code examples below. The file is a normal GEOS VM File; you may use normal VM routines with it.

Because the Viewer library only supplies a reference to the document file when that file is to be opened, your library will probably need to cache a reference to the file for future operations. It's a good idea to keep the file open while the user is working with the document, so that the system's file-sharing protection will keep other programs from trying to change the file.

Code Display: Manipulating the Document file

#include <Ansi/string.h>

  /* This snippet of code for creating new documents is taken
     from the VLR_OPEN handler. */
  /* This code makes use of "vp", the ViewerParameters structure passed
     to the Open routine */

      VMFileHandle      theFile;
      VMBlockHandle 	theBlock;
      WWFixedAsDWord 	*theBuffer;
      MemHandle         dummyHandle;

      /* Open the document */
      /* Save the current directory */
      FilePushDir();
      FoamSetDocumentDir( vp->VP_documentInfo.DI_docDir );
      theFile = VMOpen( vp->VP_documentInfo.DI_docName,
                        VMAF_FORCE_READ_WRITE | VMAF_FORCE_DENY_WRITE,
                        VMO_OPEN, 0 );

      /* Now that we've opened the file, initialize its data.
         In our example, document files are pretty simple,
         containing two WWWFixedAsDWord values. */
      theBlock = VMAlloc( theFile, 2*sizeof(WWFixedAsDWord), 0 );
      VMSetMapBlock( theFile, theBlock );
      theBuffer = VMLock( theFile, theBlock, &dummyHandle );
      theBuffer[0] = 0;
      theBuffer[1] = 0;

      /* We've written the data.  Mark the file dirty and close it. */
      VMDirty( dummyHandle );
      VMUnlock( dummyHandle );
      VMClose( theFile, FALSE );

      FilePopDir();
  /* So ends the snippet from the VLR_OPEN handler */

  ---------

/* word	MSG_MVD_OPEN_DOCUMENT( DocumentInfo *doc ); */
@method OpenDocument, MyViewerDialogClass, MSG_MVD_OPEN_DOCUMENT {
  /* The entry point routines send this message when they want to open a
     document.
     The handler checks the document's version (to make sure it wasn't
     created by a later, incompatible version of this library) opens the
     document file, reads its contents, and updates the document
     display UI. */

  VMBlockHandle 	theBlock;
  WWFixedAsDWord 	*theBuffer;
  MemHandle         	dummyHandle;
  optr   		someObject;
  ProtocolNumber 	protocol;

  /* Cache the document file's path. */
  pself->MVDI_docStash.DI_docDir = doc->DI_docDir;
  strncpy( pself->MVDI_docStash.DI_docName, doc->DI_docName, FILE_LONGNAME_BUFFER_SIZE );

  /* Open the document file. */
  FilePushDir();  /* Save the old directory */
  FoamSetDocumentDir( doc->DI_docDir );
  pself->MVDI_fileStash = VMOpen( doc->DI_docName,
                                  VMAF_FORCE_READ_WRITE | VMAF_FORCE_DENY_WRITE,
                                  VMO_OPEN, 0 );

  /* Check to see if something went wrong. */
  if ( NullHandle == pself->MVDI_fileStash ) {

    /* Something went wrong--we couldn't open the file. */
    /* We should put in some code here to alert the user
       that something went wrong when we tried to open
       the file.  Maybe someone else has it open. */
     /* For this simple example, we just return an error.
        The code which called us will make the dialog
        go away.  Which is fine.  But it would be better
        if we let the user know what was going on. */
    return( 0 ) ;
  }

  /* We successfully opened the file.  */
  /* Now we check its protocol number.  Perhaps this document was
     created by another, later version of this same library--and
     it might not be compatible with this library.  Normally,
     incompatible versions use different major protocol numbers. */
  /* If we encounter a document with the wrong protocol number,
     we reject it and return an error.  Of course, if we encountered
     an old-version document, we could take measures to extract its
     information and save it out again in the new format.  We would
     then use FileSetHandleExtAttributes() to mark the file with
     its new protocol number. */
  FileGetHandleExtAttributes( pself->MVDI_fileStash,
                              FEA_PROTOCOL,
                              &protocol,
                              sizeof( ProtocolNumber ) );
  if ( protocol.PN_major != 0 ) {
     /* We should put in some code here to alert the user
        that this document is older or newer than we
        know how to deal with. */
     /* For this simple example, we just return an error.
        The code which called us will make the dialog
        go away.  Which is fine.  But it would be better
        if we let the user know what was going on. */
    VMClose( pself->MVDI_fileStash, FALSE );
    return( 0 ) ;
  }

  FilePopDir();

  /* We've successfully opened the file and it looks correct.
     Read its data out and update the document display UI. */
  theBlock = VMGetMapBlock( pself->MVDI_fileStash );
  theBuffer = VMLock( pself->MVDI_fileStash, theBlock, &dummyHandle );

  /* We reuse the "someObject" variable to refer to each of the
     document display objects in turn. */
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend0 ) );
  @call someObject::MSG_GEN_VALUE_SET_VALUE( theBuffer[0], FALSE );
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend1 ) );
  @call someObject::MSG_GEN_VALUE_SET_VALUE( theBuffer[1], FALSE );
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVLabel ) );
  @call someObject::MSG_GEN_REPLACE_VIS_MONIKER_TEXT( doc->DI_docName,
                                                      VUM_NOW );

  /* We're done with the data.  Unlock the data block, but keep the
     file open to discourage other programs from opening it. */
  VMUnlock( dummyHandle );

  /* Return success */
  return( 1 ) ;
}

/* Save and Close the file */
@method CloseDocument, MyViewerDialogClass, MSG_MVD_CLOSE_DOCUMENT {
  /* Our code calls this message when it wants to close a document.  */
  /* The handler gets the current data from the document display UI,
     saves this data to the document file, and closes the document file. */
  WWFixedAsDWord *theBuffer;
  VMBlockHandle  theBlock;
  MemHandle      dummyHandle;
  optr           someObject;

  /* We've previously stashed the handle of the document file. */
  theBlock = VMGetMapBlock( pself->MVDI_fileStash );
  theBuffer = VMLock( pself->MVDI_fileStash, theBlock, &dummyHandle );

  /* Retrieve the data from the document display UI.
     We re-use the "someObject" variable to reference
     each appropriate display object in turn.  */
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend0 ) );
  theBuffer[0] = @call someObject::MSG_GEN_VALUE_GET_VALUE( );
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend1 ) );
  theBuffer[1] = @call someObject::MSG_GEN_VALUE_GET_VALUE( );

  /* We've written the data.  Mark the file dirty and close it. */
  VMDirty( dummyHandle );
  VMUnlock( dummyHandle );
  VMClose( pself->MVDI_fileStash, FALSE );
}

Saving and Restoring State

Sometimes the document viewer might have to shut down even though the user hasn't explicitly closed the document. The user might power down the device, the device might run low on power, or the device might run low on memory while the viewer application is in the background and decide to shut down the viewer application.

Under these circumstances, the VILFile library should save the information needed to restore the document display UI state. As shown below, there is likely to be more state information than there was information in the document itself. The state information will almost certainly need to include the path and filename information for the document file.

#include <Ansi/string.h>

typedef struct {
  WWFixedAsDWord TVS_addend0;
  WWFixedAsDWord TVS_addend1;
  WWFixedAsDWord TVS_sumNum;
  DocumentInfo   TVS_doc;
} TVState;

@method RestoreState, MyViewerDialogClass, MSG_MVD_RESTORE_STATE {
  /* Our code calls this message when it wants to restore from state */
  /* The handler reads data from the passed memory block and updates
     the dialog's instance data and the document display UI's data.
     It also opens the document file, though it ignores the file's
     contents. */
  TVState *theBuffer;
  optr    someObject;
  char	  sumString[LOCAL_DISTANCE_BUFFER_SIZE];

  /* Lock down the passed block */
  theBuffer = MemLock( stateBlock );

  /* Update the document display UI and the data we cache in the
     dialog's instance data. */
  /* We re-use the "someObject" variable to refer to each of the
     document display UI objects in turn. */
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend0 ) );
  @call someObject::MSG_GEN_VALUE_SET_VALUE( theBuffer->TVS_addend0, FALSE );
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend1 ) );
  @call someObject::MSG_GEN_VALUE_SET_VALUE( theBuffer->TVS_addend1, FALSE );
  pself->MVDI_docStash.DI_docDir = theBuffer->TVS_doc.DI_docDir;
  strncpy( pself->MVDI_docStash.DI_docName,
           theBuffer->TVS_doc.DI_docName,
           FILE_LONGNAME_BUFFER_SIZE );

  /* Open the document file. */
  FilePushDir();
  FoamSetDocumentDir( theBuffer->TVS_doc.DI_docDir );
  pself->MVDI_fileStash = VMOpen( theBuffer->TVS_doc.DI_docName,
                                  VMAF_FORCE_READ_WRITE | VMAF_FORCE_DENY_WRITE,
                                  VMO_OPEN, 0 );

  if ( NullHandle == pself->MVDI_fileStash ) {
    /* Something went wrong when we attempted to open the file.
       In the time after we shut down to state, any of a number
       of things could have happened.  The file may have been
       deleted, or it may have been opened by another application.
       We should get the error and do something with it. */

     /* In this simple example, we just return an error.
        The code which called us will make the dialog
        go away.  Which is fine.  But it would be better
        if we let the user know what was going on. */
    return( 0 );
  }

  FilePopDir();

  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVLabel ) );
  @call someObject::MSG_GEN_REPLACE_VIS_MONIKER_TEXT( theBuffer->TVS_doc.DI_docName,
                                                      VUM_NOW );
  pself->MVDI_sumNum = theBuffer->TVS_sumNum;
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVSum ) );
  LocalFixedToAscii( sumString, pself->MVDI_sumNum, 0 );
  @call someObject::MSG_GEN_REPLACE_VIS_MONIKER_TEXT( sumString, VUM_NOW );

  return( 1 ) ;
}

@method SaveState, MyViewerDialogClass, MSG_MVD_SAVE_STATE {
/* Our code sends this message when it needs to shut down to state.
   The handler allocates a memory block and fills it with the
   information we'll need to restore to state later--data from
   the document display UI objects and cached information from
   the dialog. */
  MemHandle 	stateBlock;
  TVState   	*theBuffer;
  optr 		someObject;

  /* Allocate the block */
  stateBlock = MemAlloc( sizeof( TVState ),
                         (HF_DYNAMIC | HF_SHARABLE) ,
                         HAF_LOCK );
  theBuffer = MemDeref( stateBlock );

  /* Retrieve data from objects and write it to the block */
  theBuffer->TVS_doc.DI_docDir = pself->MVDI_docStash.DI_docDir;
  strncpy( theBuffer->TVS_doc.DI_docName,
           pself->MVDI_docStash.DI_docName,
           FILE_LONGNAME_BUFFER_SIZE );
  theBuffer->TVS_sumNum = pself->MVDI_sumNum;
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend0 ) );
  theBuffer->TVS_addend0 = @call someObject::MSG_GEN_VALUE_GET_VALUE( );
  someObject = ConstructOptr( OptrToHandle( oself ),
                              OptrToChunk( @TVAddend1 ) );
  theBuffer->TVS_addend1 = @call someObject::MSG_GEN_VALUE_GET_VALUE( );

  /* We're done.  Unlock the block and return it. */
  MemUnlock( stateBlock );
  return( stateBlock );
}
This document is a single-page version of a a multi-page document, suitable for easy printing.


GEOS Programming

Because GEOS implements its own messaging and object system, standard C programming must be supplemented with GEOS-specific programming. This chapter describes the syntax and commands available to GEOS programmers in the Goc preprocessor.

1 Basic Data Types and Structures
1.1 Records and Enumerated Types
1.2 Handles and Pointers
1.3 Fixed Point Structures
2 Goc and C
2.1 Goc File Types
2.2 Conditional Code in Goc
2.3 Macros in Goc
2.4 Using Routine Pointers in Goc
2.5 Esoteric Source-Code Management
3 The GEOS Object System
3.1 GEOS Terminology
3.2 Object Structures
3.3 The GEOS Message System
4 Using Classes and Objects
4.1 Defining a New Class or Subclass
4.2 Non-relocatable Data
4.3 Defining Methods
4.4 Declaring Objects
4.5 Sending Messages
4.6 Managing Objects

GEOS Programming: 1 Basic Data Types and Structures

In addition to the standard data types available in C, the Goc preprocessor handles several other types specific to GEOS. These are all defined in the file geos.h . Some of these types, shown below, were carried over from the world of assembly language.

The Boolean type behaves as most Boolean types--any nonzero value represents a true state, and zero represents the false state. Throughout the documentation, true and false are taken to be these meanings. Note that the constants TRUE and FALSE are defined and may be used as return values from your functions and methods. Do not compare Boolean variables, however, against these constants. A Boolean may be true without actually equaling the TRUE value.

Records and Enumerated Types

Handles and Pointers

Fixed Point Structures

GEOS Programming: 1.1 Basic Data Types and Structures: Records and Enumerated Types

GEOS objects and routines make extensive use of flag records and enumerated types. A flag record is a byte, word, or dword in which each bit represents the state (on or off) of a particular attribute or function. An enumerated type is a byte or word in which each enumeration has a unique constant value.

To define a flag record, you should use one of these types and then define the flags to be bits within the record. To set flags, OR them (bitwise OR) with the record; to clear them, AND their bitwise inverses (bitwise AND) with the record. Creating and working with flag records is shown in Flag Records and ByteEnums .

There are two basic enumerated types: The standard enumerated type supported by your C compiler uses word-sized values. GEOS also allows byte-sized enumerated types with the ByteEnum type. Use of this type is shown in Flag Records and ByteEnums .

Code Display 5-1 Flag Records and ByteEnums

/* Define flag records to be the optimized length for the number of flags. For
 * example, the sample type MyFlag has six flags and therefore should be a byte.
 * Flag values should be constants equivalent to having a single bit set in the
 * flag record. */
typedef ByteFlags MyFlag;
#define MF_FIRST_FLAG			0x01
#define MF_SECOND_FLAG			0x02
#define MF_THIRD_FLAG			0x04
#define MF_FOURTH_FLAG			0x08
#define MF_FIFTH_FLAG			0x10
#define MF_SIXTH_FLAG			0x20
/* In a section of code, to set a flag, bitwise OR it with the record. To clear the
 * flag, bitwise AND its inverse with the record. You can set any number of flags
 * at a time as shown in the following examples. */
    ...
    MyFlag		myFlagsRecord;		/* Set up a variable of the flag record type */
    /* Set the second and fourth flag. */
    myFlagsRecord = MF_SECOND_FLAG | MF_FOURTH_FLAG;
    /* Set the first flag and then clear the fifth and sixth flags. */
    myFlagsRecord = (myFlagsRecord | MF_FIRST_FLAG) & ~(MF_FIFTH_FLAG |
                            MF_SIXTH_FLAG);
/* The ByteEnum type can be used instead of the standard enumerated type, which
 * is implemented by most compilers as a word type. To define a ByteEnum, define
 * the type and then a unique constant value for each enumeration as in the
 * following example. */
typedef ByteEnum USCity;
#define USC_HARTFORD			0x00
#define USC_CHARLOTTE			0x01
#define USC_WICHITA			0x02
#define USC_PIERRE			0x03
#define USC_ORLANDO			0x04

GEOS Programming: 1.2 Basic Data Types and Structures: Handles and Pointers

Handles and pointers are present everywhere in GEOS--they are the essential elements that make dynamic linking and efficient memory management possible.

GEOS pointers are all far pointers; that is, they are 32-bit addresses that reference specific locations in memory. They are normal C pointers and can be used as such. Two other pointer types are also used by GEOS: Object pointers (optrs) and segment pointers. Object pointers are described below; segment pointers are 16-bit addresses described in the Memory Management chapter.

Handles

Handles are 16-bit, unsigned values used for several purposes. They provide abstraction when the exact address of a data structure or other item is not known or is an inconsistent state. The kernel maintains a handle table to keep track of many of the handles in the system. Each entry in the handle table is 16 bytes that contains information about the item referenced by the handle; these 16 bytes are opaque to applications and libraries and can not be accessed or altered except by the kernel. Other handle types are managed in other tables by the kernel.

Handles are used for the following primary purposes. For a full description of how handles are used, see the Handles chapter.

  • Memory reference
    Entries for memory handles contain pointers to memory blocks; when the blocks move, the pointers are updated. However, the handle's integrity is preserved, and applications keep track of just the handle value.
  • Virtual Memory reference
    Entries for VM handles indirectly reference VM blocks similar to the way memory handles reference blocks of memory. VM handles may be used whether the VM block is resident in memory or not.
  • File and disk reference
    Entries for file and disk handles contain information about the location and status of the referenced file/disk. They provide indirect access to files and disks in a manner similar to memory handles.
  • Data structure implementation
    Certain frequently-used system data structures require a storage format that provides for quick and convenient access at all times. These data structures (for example, threads, event queues, messages, timers, and semaphores) are stored in Handle Table entries.
  • Optimization
    The kernel will, if space permits, sometimes use Handle Table entries for optimized temporary storage. (For example, when passing parameters on the stack with messages, the kernel will occasionally use handles for storing the parameters.)

The NullHandle value (zero) is used to indicate a null handle.

There are over a dozen different types of handles that can be used by any sort of geode. These are listed in the GEOS Programming chapter. All are 16-bit unsigned integers.

Chunk Handles and Object Pointers

Objects and small data structures are stored in small memory pieces called chunks. Chunks are stored in memory blocks known as local memory heaps, and each local memory heap can contain several chunks. Each chunk is referenced by a combination of two handles: The MemHandle handle locates the local memory heap, and the ChunkHandle locates the chunk within the block. A null chunk handle value is specified by NullChunk .

Objects are referenced in the same way as chunks, but the handle and chunk handle are combined into a single structure called an Object Pointer, or optr. Each optr uniquely identifies a particular object in the system. Note that optrs are often used to reference non-object chunks and data structures. A null value is specified by NullOptr .

GEOS provides several macros, all defined in geos.h , for creating and parsing optrs.

  • ConstructOptr()
     This macro constructs an optr from a MemHandle and a ChunkHandle.
  • OptrToHandle()
     This macro extracts the MemHandle portion of the given optr.
  • OptrToChunk()
     This macro extracts the chunk handle portion of a given optr.

Pointers

Pointers can be used normally as in C. All Goc-generated pointers are far pointers; that is, they are 32-bits long, composed of a 16-bit segment and a 16-bit offset.

GEOS provides macros for extracting the segment and handle portions of pointers.

  • PtrToSegment()
     This macro returns the segment portion of the given pointer (returned as type "word").
  • PtrToOffset()
     This macro returns the offset portion of the given pointer (returned as type "word").

GEOS automatically loads code resources when needed. However, when you call routines through pointers, you must take special measures to see to it that the routine is properly loaded into memory. This is discussed below in Using Routine Pointers in Goc .

GEOS Programming: 1.3 Basic Data Types and Structures: Fixed Point Structures

When you want to represent non-integral numbers (i.e., real numbers), you can use either the standard C floating-point format or the following special structures of GEOS for fixed point math. Note that fixed-point calculations are faster than the corresponding floating-point math, so if you want to optimize your code, you should use the GEOS fixed-point structures with the GEOS math routines.

Code Display 5-2 GEOS Data Structures

 /* Fixed-Point Structures
 *    The following structures are used to represent fixed-point numbers:
 *    numbers with a fractional portion and an integral portion. Notice that
 *    there are several formats of fixed-point numbers; each uses a different
 *    number of bits for the parts of the number. Choose whichever is most
 *    appropriate (for optimization). */
     /* BBFixed
     * One byte integer, one byte fraction */
typedef struct {
    byte		BBF_frac;		/* fractional portion */
    byte		BBF_int;		/* integral portion */
} BBFixed;
    /* BBFixedAsWord
     * Sometimes it is convenient to refer to a BBFixed value as type word.
     * The BBFixedAsWord type is used for this purpose. */
 typedef word		BBFixedAsWord;
    /* WBFixed
     * One word integer, one byte fraction */
 typedef struct {
    byte		WBF_frac;		/* fractional portion */
    word		WBF_int;		/* integral portion */
} WBFixed;
    /* WWFixed
     * One word integer, one word fraction */
 typedef struct {
    word		WWF_frac;		/* fractional portion */
    word		WWF_int;		/* integral portion */
} WWFixed;
    /* WWFixedAsDWord
     * Sometimes it is convenient to refer to a WWFixed value as type dword.
     * The WWFixedAsDWord type is used for this purpose. */
 typedef dword		WWFixedAsDWord;
    /* DWFixed
     * two words (one dword) integer, one word fraction */
typedef struct {
    word		DWF_frac;		/* fractional portion */
    sdword		DWF_int;		/* integral portion */
 } DWFixed;
/* Three-byte structure
 * The WordAndAHalf structure is used when you need a 24-bit value and you want to
 * optimize and avoid using a 32-bit value. */
typedef struct {
    word		WAAH_low;		/* the low 16 bits */
    byte		WAAH_high;		/* the high 8 bits */
} WordAndAHalf;

Three special macros are also available to work with the WWFixed type. These are listed below:

  • MakeWWFixed
     This macro creates a WWFixed structure from a given floating-point number or dword number.
  • WWFixedToFrac
     This macro produces the fractional portion of a WWFixed structure.
  • WWFixedToInt
     This macro produces the integral portion of a WWFixed structure.

Two other macros are provided for use with WWFixedAsDword structures:

  • IntegerOf()
     This macro returns the integral portion of a WWFixedAsDword structure.
  • FractionOf()
     This macro returns the fractional portion of a WWFixedAsDword structure.

GEOS Programming: 2 Goc and C

Goc is a superset of the standard C programming language. Goc actually acts as a sort of preprocessor before the code is run through a standard C compiler. There are several differences you must be aware of, though. These differences are covered in the following sections as well as throughout the documentation.

Goc File Types

Conditional Code in Goc

Macros in Goc

Using Routine Pointers in Goc

Esoteric Source-Code Management

GEOS Programming: 2.1 Goc and C: Goc File Types

@include, @optimize

When programming in Goc, you will use several different types of code files. Files ending in .goh are the Goc equivalent of C .h files--they contain routine headers, constants, and other included data and structures necessary for the program. Files ending in .goc are the Goc equivalent of C .c files--they contain code and data for the program. You should keep any Goc-specific code or header information strictly in the Goc files, and standard C code should be kept in C files. C code can also be put in .goc and .goh files, but for consistency, you should try to keep it separate.

Not all of your program's header files need be .goh files--if the header file contains only C constructions (structures, routine definitions, and so on), then you may leave it as a standard C .h file, included by means of the #include directive.

The rule of thumb is that if a header file contains any Goc code or includes a .goh file, then it must be a .goh file. Note also that .goh files are allowed to contain simple standard C code; if you are not sure, then, you can make all your header files .goh .

Standard C programs use the #include directive to include header ( .h ) files. When using Goc, you can use this directive in standard .c and .h files; when including .goh files in .goc files, though, you have to use the @include directive, which is Goc-specific. It has the same syntax as #include .

An example of using @include comes from the Hello World application, which includes the stdapp.goh file. (All GEOS applications will need to include this file to compile properly; it must be @include d before any standard C headers are #include d.) The line from Hello World is

@include <stdapp.goh>

The syntax of this directive, as stated above, is the same as for the C directive #include . One exception is that the @include directive will include a file just once, even if it is included by an included file--there is no need to conditionally include a file (checking first to make sure it hasn't already been included).

If you will be including a Goc file in many different applications, or if it is very long and elaborate, it is a good idea to put the keyword @optimize at the top of the file. This instructs the Goc preprocessor to generate a special stripped-down version of the file with a .poh suffix. The compiler will then automatically keep the .poh file up to date, and use it in compilations instead of the .goh file. The .poh file contains all the data of the .goh file, but is somewhat faster to compile into an application; thus, by using the @optimize keyword, you incur a longer compilation whenever you make a change to the .goh file, but a shorter compilation time when the .goh file is unchanged since the last compilation. You may choose to leave the @optimize directive out while the header is being developed, then put it in when the header is fairly stable.

GEOS Programming: 2.2 Goc and C: Conditional Code in Goc

@if, @ifdef, @ifndef, @endif

Many C programs use the directives #if , #ifdef , #ifndef , and #endif to define conditional code--code that should be compiled into the program only if certain conditions are met. When working with standard C code in your GEOS applications, you should still use these directives; when working with Goc code, however (in .goh and .goc files), you should use the Goc directives @if , @ifdef , @ifndef , and @endif .

Goc conditionals are more limited than C conditionals. Conditional expressions may be based on numbers, names of macros, and the Boolean operators OR ( || ) and AND ( && ). Some examples of Goc conditional expressions are shown below:

@ifdef (MyMacro)
    /* code compiled if MyMacro is defined */
@endif
@if 0
    /* code that will not be compiled at all */
@endif
@if defined(MyMacro) || MY_CONSTANT
    /* code compiled if either MyMacro is
     * defined or MY_CONSTANT is not zero */
@endif
@ifndef 0
    /* code always compiled */
@endif

GEOS Programming: 2.3 Goc and C: Macros in Goc

@define

The C programming language allows definition and use of macros, and most programmers use macros extensively. You can use the #define directive in standard C code in your GEOS programs to define macros that use only standard C code.

Similarly, you can use the @define Goc directive to create macros in Goc. (Macros must be defined with @define ; otherwise, the Goc processor will skip the #define directive and process the macro as if it were standard code to be processed normally.)

Macros in Goc have a somewhat different syntax than standard C macros though they are very similar. Some examples of simple Goc macros follow below:

@define mlply(val1,val2)					val1 * val2
@define defChunk(a)				@chunk char a[] = "text"

When using Goc macros in your code, you must preface them with the " @ " Goc marker, indicating to the processor that it is a macro. If you do not preface the macro with " @ ", then Goc will pass over it and will not process it, leaving it up to the C compiler--which will likely give an error. For example, using the second macro defined above (defChunk), you could create a number of chunks easily:

@defChunk(firstText)
@defChunk(secondText)
@defChunk(thirdText)

The above would equate to the following:

@chunk char[] firstText = "text";
@chunk char[] secondText = "text";
@chunk char[] thirdText = "text";

Using "defChunk" without the " @ " marker would most likely result in a compilation error in the C compiler.

GEOS Programming: 2.4 Goc and C: Using Routine Pointers in Goc

ProcCallFixedOrMovable_cdecl(), ProcCallFixedOrMovable_pascal()

Most GEOS code is kept in movable resources. If you call a routine explicitly from source code the Goc preprocessor generates appropriate directives to see to it that the resource is loaded into memory when it is called. However, if you call a routine with a routine-pointer, GEOS cannot take these precautions. Accordingly, when you are calling a routine with a pointer, you must either see to it that the resource is loaded, or use one of the two ProcCallFixedOrMovable routines to instruct the kernel to lock the appropriate resource.

If you know the routine is in a resource which is locked or fixed in memory, you can use the routine pointer exactly the way you would in standard C. This is usually because the calling routine is in the same resource as the routine or routines which may be called.

If you are not sure that the resource is loaded, you should call the routine with either ProcCallFixedOrMovable_cdecl() or ProcCallFixedOrMovable_pascal() . Each of these routines is passed the following arguments:

  • A pointer to the routine to be called
  • All the arguments passed to the routine, in exactly the order which the routine expects.

Both routines return whatever the called routine returns.

If the routine to be called was defined with standard C calling conventions (the default), you should use ProcCallFixedOrMovable_cdecl() . If the routine was declared with the keyword _pascal , it uses Pascal's calling conventions; you must then use the routine ProcCallFixedOrMovable_pascal() . Most kernel and system-library routines are declared with Pascal's calling conventions.

Code Display 5-3 Using ProcCallFixedOrMovable_cdecl()

extern int
SomeRoutineCalledViaAPointer(int anArg, int anotherArg, const char *someText);
int (*funcPtr) (int, int, const char *);					/* A function pointer */
funcPtr = SomeRoutineCalledViaAPointer;
/* We want to do
 *	SomeRoutineCalledViaAPointer(1, 2, "Franklin T. Poomm");
 * but we want to call it through the pointer, even though it's in another
 * resource:
 */
ProcCallFixedOrMovable_cdecl(funcPtr,					/* The pointer to the routine */
                1, 2, "Franklin T. Poomm");

GEOS Programming: 2.5 Goc and C: Esoteric Source-Code Management

@optimize, @protominor

There is a small number of Goc keywords which are used only to ease the burden of maintaining header files which will be used and shared often.

The @optimize keyword is used to speed compilation of Goc header files. It may be placed at the top of a .goh file. The directive instructs Goc to generate a specially processed .poh file which contains all the information of the .goh file, but is somewhat faster to compile. This .poh file is automatically regenerated if the corresponding .goh file has been changed since the last compilation. Its syntax is simple:

@optimize

The @protominor keyword is used so that a single Goc header file may serve multiple versions of a Goc library. When creating a new version of an existing library, use the @protominor keyword to declare new messages and variable data fields for a class. Suppose your original class declaration looked like so:

@class MyClass, SuperClass;
    @message void MSG_M_DO_THIS(void);
    @vardata void TEMP_M_DONE_FLAG;
@endc

Having released this version of your class, you wished to release another version in which this class handled another message. You wanted to specify that this new message would only work with this new version of the library. This would be set up like so:

@class MyClass, SuperClass;
    @message void MSG_M_DO_THIS(void);
    @vardata void TEMP_M_DONE_FLAG;

    @protominor MyVersion20
    @message void MSG_M_DO_THAT(void);
@endc

This keyword has the syntax:

@protominor <name>
name
Name of the minor protocol. This name should correspond to an incminor entry in the geode's .gp file.

To do the equivalent version control with routines, use the incminor .gp file directive.

GEOS Programming: 3 The GEOS Object System

GEOS is almost entirely object-oriented. Its object system supports true object-oriented principles such as encapsulation, inheritance, and message dispatching through the kernel.

The following section describes the class and object structures of GEOS, how to declare and define classes and objects, and how the messaging system and the kernel's message dispatcher work.

GEOS Terminology

Object Structures

The GEOS Message System

GEOS Programming: 3.1 The GEOS Object System: GEOS Terminology

Though you should be familiar with general object-oriented programming terms, there are quite a few for which the meaning is slightly different in GEOS, and there are others which are entirely new to GEOS. This section is divided into four categories: General Terms, Class Terms, Object Terms, and Messaging Terms.

General Terms

chunk
A chunk is a small section of memory located in a Local Memory Heap. Object instance data is stored in a chunk, one chunk per object. Local Memory and chunks are described fully in the Local Memory chapter.

fptr An fptr is a "far pointer"--a 32-bit pointer to a specific location of memory. It is a standard C pointer.

handle A handle is a 16-bit index into a Handle Table and is used to reference memory blocks. For more information, see the Handles chapter.

object block
 An object block is a specific type of Local Memory block that contains object chunks.

optr An optr is a unique identifier for an object and is therefore also referred to as an "Object Pointer." An optr is used to locate and access any object in the system, and because it is made up of handles and chunk handles, it will always stay the same even when the object moves in memory.
thread
A thread is a single executable entity that runs either procedural code or one or more objects. If a thread is "event-driven," it executes code for a given set of objects, receiving messages and dispatching them to the proper objects.

Class Terms

class
A class is the definition of a set of instance data structures and the methods that work on those structures. An object is called an "instance" of its class.
class tree
A class tree represents the hierarchy of inheritance from superclass to subclass. If a message is not handled by a given class, it will be automatically passed up the class tree until it is handled or the root of the tree is reached, after which the message is discarded.
inheritance
Inheritance is the term given to the way an object of a particular class has all the same instance variables and methods as an instance of the object's superclasses.
initialize
Initialization of an object is when a master part (or master group) of the object's instance data is filled out. This occurs whenever a class in the master group not yet initialized in the object receives its first message.
master
The term "master" is used in several cases, all related. A master class is generally considered the top class in a single class tree. Although the master class may have superclasses, it provides a conceptual break and creation of a new subtree.
master group
The section of an object's instance data belonging to a particular master class and all its subclasses is called a master group. A master group is initialized when the master class (or one of its subclasses) receives its first message.
resolve
Resolution of a variant class occurs when the variant's superclass is determined. Each instance of a variant class must be resolved individually.
subclass
The term "subclass" is used to show relationships between classes. A subclass is defined on another class, from which it inherits instance data and methods. This other class is known as a "superclass," below.
superclass
The term "superclass" is used to show relationships between classes. A superclass passes on its instance data and methods to all classes defined as subclasses of it.
variant
A variant class may have different superclasses. However, an instance of a variant class may have only one superclass at any given moment. The use of variant classes can provide much the same functionality as the multiple inheritance found in some other object systems.

Object Terms

child
A child object is one that sits below another object in an object tree. The terms child, parent, and sibling are used only to show relationships between objects.
composite
A composite object is one that can have children. The composite has a "composite link" (an optr) to its first child and a "sibling link" to its next sibling. If it has no next sibling, the sibling link instead points to the object's parent object.
instance
An instance is a particular manifestation of a class. This term is almost always interchangeable with "object," though sometimes it specifically refers to the chunk containing the object's instance data rather than to the object as a whole.
link
A link is typically an optr pointing to an object's next sibling in an object tree. It is also used more generally to refer to any optr linking two objects in an object tree (parent and child, or last sibling and parent).
object
An object is a specific manifestation of a class. Typically, this term is interchangeable with "instance"; however, sometimes the term "object" refers to the combination of an object's methods and instance data whereas the term "instance" refers to just the object's instance data chunk.
object tree
An object tree is a means of organizing objects into a hierarchy for display or organizational purposes. Do not confuse it with the "class tree," the structure which represents class relationships. An object tree is made up of composite objects, each of which may have children or be the child of another object. The topmost object in the tree is called the "root," and the bottommost objects are called the "leaves" or "nodes." Non-composite objects may be placed in the tree as leaves but may not have children.
parent
A parent object is one that has children in an object tree. The parent contains a composite link (an optr) to its first child and is pointed to by its last child.
state file
A state file is a Virtual Memory file used to store the state of objects. Typically, object blocks will be written to or extracted from the state file. Generic UI objects have this functionality built in automatically; other objects may manage their own state saving by managing the state file.

Messaging Terms

blocking
A thread "blocks" when it must wait for resources or return values from messages sent to objects in another thread. Specifically, a thread blocks when one of its objects sends a message to another thread with the "call" command; if the "send" command is used, the thread will continue executing normally.
call
A message sent with the call command causes the calling thread to block until the message is handled by the recipient. If the recipient is in the calling thread, the code will be executed immediately.
dispatcher
The GEOS dispatcher is internal to the kernel and passes messages on to their proper recipients. The dispatcher will dynamically locate the proper object and method and will invoke the method.
message
A message is a directive, query, or other instruction sent from one object to another. Messages may take parameters and may return information.
method
A method, also called a "message handler," is the code invoked by a message. A method may do anything a normal function or procedure may do, including alter instance data. It is poor style and highly discouraged for one object's method to alter another object's instance data directly.
send
A message sent with the send command will be placed in the recipient's event queue and will not cause the sender to block. Messages that return information or pass pointers should never be dispatched with the send command; use the call command in those cases.

GEOS Programming: 3.2 The GEOS Object System: Object Structures

You do not need to know what data structures are used to store objects and classes; understanding them can make programming GEOS much easier, however.

Each object is implemented in two parts: the instance data chunk and the class definition. Although both are integral parts of the object and they are interconnected, they are stored in different places.

An object's instance data is stored in an instance chunk. This instance chunk is sometimes referred to as the object itself, but this isn't quite accurate--the instance chunk contains only the object's data along with a pointer to its class structure. The structure of the instance chunk is given in Instance Chunk Structures .

An object's class structure contains all the code for the class. Since the class code may be accessed by many objects, the class definition resides in a geode's fixed memory resource. Every class (except the root, MetaClass ) has a pointer to its superclass so it can inherit that class' methods and structures.

All objects of a given class use the same code--the class' code--for their functions. They dynamically access this code so the code blocks need to be in memory only once, no matter how many objects are actively using them. Additionally, each class dynamically accesses its superclass' code, so any class may be accessed by all the objects of the subclasses as well. Class structures are shown in Class Structure and Class Trees .

Instance Chunk Structures

Each object's instance data is stored in a Local Memory chunk. Several chunks are stored in one memory block, called a local memory heap. (See the Local Memory chapter.) This local memory heap, containing objects, is known as an object block .

Each object block has a special header type that distinguishes it from normal local memory heaps. After the header in the block is the chunk handle table: a table containing offsets to each object in the block. Following the chunk handle table are the objects.

Each object is referenced by an object pointer, or optr. The optr contains two items: the global memory handle of the object block and the chunk handle of the object. Note that because the optr is made up of handles, an object must be locked before its instance data can be accessed. The GEOS kernel takes care of this automatically when executing methods.

Only the object's instance data is stored in the chunk pointed to by the optr; the method table and code used by the object are stored in the class' data structures, not the object's. To reconcile this separation of code and data, every object's first four bytes of instance data are a pointer to the object's class definition. This pointer is traversed by the kernel automatically and should not be altered or accessed by applications.

Included in an object's instance chunk are certain fields generated and filled by either Goc or the kernel. Following these fields is the object's instance data, grouped by master part. It's unlikely you'll ever have to know the actual structures used in the instance chunk because the kernel automatically calculates the proper offsets to individual instance data fields. However, understanding the underlying structures may help in understanding how the object system of GEOS works.

Instance data within an instance chunk is stored in "master parts" or "master groups." A master group is simply a number of instance data fields grouped according to their appropriate master class levels. Master classes are detailed in Master Classes .

A class designated as a master class resembles a normal class in all respects save one: it determines how instance data is grouped in a chunk. Each master class is the head of a class subtree; all the classes below it in the class tree (down to the next master class) are considered to be in that master class' group. Instance data for all classes in the master group are lumped together in the instance chunk; each master group's instance data within the chunk is accessed via a special offset stored within the chunk.

The first four bytes of an object's chunk contain a pointer to the object's class structure. The class structure (described in Class Structure and Class Trees ) resides in fixed memory. (A variant-class object has a slightly different structure; this is detailed in Variant Classes .)

An object that has no master classes in its class ancestry (unusual) has its instance data directly following its class pointer. Objects belonging to master classes or their subclasses, however, are somewhat more complex. This distinction can be mostly ignored by application and library programmers (with the exception of deciding which classes should be master classes and which should not).

Each master part of the chunk is located by an offset inserted directly after the object's class pointer in the chunk. The position of the word containing this offset is then stored in the master class structure so the class can find its instance data later. The combination of the class pointer and the various master offsets make up the object's "base structure." When a typical object is instantiated, the base structure is all that is created.

Each master part is left unallocated (for efficiency) until it is first accessed via a message sent to a class in the master group. When a class in the master group receives its first message, the entire master part of the chunk is allocated and initialized. This means that an object's chunk remains as small as possible until it absolutely must grow larger. Some classes even detect when a master part of the object will no longer be needed and actually remove (shrink to zero) the unwanted instance data from the chunk ( GenClass does this by shrinking the Vis master part to zero size when a gadget is taken off the screen).

Any object may have "variable data" instance data fields; these are fields that may be added or removed dynamically to keep from having unused space in the instance chunk. Generic UI hints are "variable data" (also called vardata )--if an object has the hint, it appears in its instance chunk, if the object does not have the hint, the chunk does not have unused space in it.

Vardata entries are stored all together at the end of the instance chunk, regardless of their master groups. An object with two master groups and three variable data fields, for example, would look like that shown in the figure below. Variable data and its use are discussed in full in Defining and Working With Variable Data Fields .

Master Classes

A master class provides a conceptual break between levels within a class tree. Each master class is the head of a class subtree, and all its subclasses are considered to be in its "master group."

The purpose of making a class a master class is to separate its instance data from that of its superclass. Each master group's instance data is lumped together in one section of the object's instance chunk and is not initialized until a class in the master group is accessed. The initialization (allocation of extra memory within the instance chunk) occurs automatically.

As shown in the figures below, an object of RookClass would have an instance chunk with two master groups, one for the PieceClass master class and one for the GamePcClass master class. The first of the two master parts represents the instance data for PieceClass only; the second master part represents the object's instance data for all of GamePcClass , ChessClass , and RookClass .

The functionality of master classes is required to implement GEOS variant classes (see Variant Classes ). A variant class allows a single class to have a version of "multiple inheritance" in that it can have different superclasses depending on the system context.

Class Structure and Class Trees

For the most part, you won't ever need or want to know the internal structure of a class as implemented in memory. The class structure is created and partially filled by the Goc preprocessor and Glue linker; the remainder is filled by the kernel when the class is loaded. It's unlikely you will need to know the actual class structures; you won't ever have to manually build a class unless your program dynamically creates it (not a common procedure for typical applications).

This section will describe how the class is implemented and how class trees are structured and managed. However, it will not discuss creating new classes during execution.

Classes are implemented with special data structures and code blocks. Each class is defined within and exported by a particular geode; when the geode is loaded the class definition and its code are loaded into the geode's fixed memory. All references to the class are then relocated by the kernel into pointers. For example, if a class is defined by a library, that library's "core block" (the special information kept about it by the kernel) contains an absolute pointer to the class' definition in a fixed memory resource owned by the library. Any applications then using that class load the library. The kernel examines the library's core block for the proper pointer and uses it each time the application references that class.

Because of this, each class is loaded into memory just once; all objects that use the class use the same class structure and code. Each object has a pointer in its instance chunk directly to the class structure; each class contains a pointer to its superclass' class structure. Using these pointers, the kernel can travel up an object's class tree to access any appropriate code.

A class is a combination of data structure and code. The data structure ( ClassStruct ) contains information about the class, its superclass, its methods, and the structure and size of its instance data. The code consists of methods (message handlers). A diagram of the data structure is given below.

Class_superClass

Every class has as its first four bytes a pointer to its superclass. This points to the superclass' ClassStruct structure in all cases except two: The root of any class tree has a null superclass pointer, indicating that the root has no superclass. Variant classes have the integer 1 (one) always, indicating that the superclass is determined in a special manner. For more information on variant classes, see Variant Classes .

Class trees are constructed when classes are defined; a new class is created as the subclass of some existing class, and its Class_superClass pointer is automatically set to point to the superclass. There is no need to point down the tree; messages are always passed to superclasses and never to subclasses.

Class_masterOffset

Class_masterOffset stores the offset indicating how far into the instance chunk the object's offset to this class' master part is. Note that use of this offset is entirely internal; individual classes do not have to figure out where their instance data is within the chunk (they may, however, have to know what master level each class is).

The master offset is used primarily because an object can have some of its master parts initialized and others uninitialized. If only one master part of the object had been initialized, the location of the instance data in the chunk may be different than if all master parts had been initialized.

Class_methodCount

Class_methodCount stores the total number of methods referenced in the class' method table. This is the total number of methods defined for this class only; other methods defined in other classes (even in the same master group) are stored in the method tables of those classes.

Class_instanceSize

Class_instanceSize holds the number of bytes to be allocated whenever an object of this class is instantiated. If the class is a master class or a subclass of a master class, this is the size of the master part. If the class has no master class above it, this is the number of bytes to allocate for the entire object (including superclass pointer).

Class_vdRelocTable

Class_vdRelocTable is a near pointer (16 bit offset) to the variable-data relocation information. The relocation information contains the type of relocation to be done for each data type. There is one entry in the variable data relocation table for each relocatable field in each particular variable-data type. Variable data (also called vardata ) is described in full in Defining and Working With Variable Data Fields .

Class_relocTable

Class_relocTable is a near pointer (16 bit offset) to the relocation information for the non-variable data instance fields of the class. The relocation information contains the type of relocation done for each relocatable instance field (other than variable-data entries). A relocatable instance field is one which must be updated when the object is loaded--pointers, offsets, etc. The entry in the relocation table is defined with the @reloc keyword, described on @reloc <iname>, [(<count>, <struct>)] <ptrType>; .

Class_flags

Class_flags contains seven flags (shown below) that determine the characteristics of the class. Declarers for these flags are used in the @classdecl declaration (see Defining a New Class or Subclass ).

CLASSF_HAS_DEFAULT
This flag indicates that the class has a special default method to handle unrecognized messages (typically, this handler simply passes the unrecognized message on to the superclass). This flag is not implemented in C. This flag is set by declaring the class as hasDefault .
CLASSF_MASTER_CLASS
This flag is set if the class is a master class. This flag is set by declaring the class as master .
CLASSF_VARIANT_CLASS
This flag is set if the class is a variant class. This flag is set by declaring the class as variant (all variants must also be declared as masters).
CLASSF_DISCARD_ON_SAVE
This flag indicates the instance data for the class can be discarded when the object is saved. This flag applies only to master classes and will be heeded only when the master group is immediately above a variant-master group. This flag is set by declaring the class discardOnSave .
CLASSF_NEVER_SAVED
This flag indicates objects of this class are never saved to a state file or loaded in from a resource. Goc will not build a relocation table for a class with this flag set. This flag is set by declaring the class neverSaved .
CLASSF_HAS_RELOC
This flag indicates that the class has a special routine for relocating and unrelocating objects of the class when they are loaded from or written to disk. This flag is set by declaring the class hasReloc .
CLASSF_C_HANDLERS
This flag indicates the class' methods are written in C rather than in assembly. This flag is set by the compiler and should not be set by applications.
Class_masterMessages

Class_masterMessages contains a number of flags set by Goc indicating whether this class has methods for messages defined within a given master level. It is used to optimize the search for a method to handle a message. These flags are internal and should not be used by programmers.

The Method Table

Every class has a method table, a table that indicates the location of the code to be executed when a certain message is received. The method table is in three parts and begins at the byte labelled Class_methodTable (this is simply a label, not a specific data field).

The first part of the method table is a list of messages the class can handle. Each entry in this list is two bytes and contains the message number of a message handled by the class.

The second part of the method table is a list of pointers to methods. Each entry in this list is a pointer (four bytes) which points to a specific routine in a code block. If the code is in a fixed block, the pointer will be a far pointer; if the code is in a moveable or discardable block, the pointer will be a far pointer containing a virtual segment . (A virtual segment, something you do not need to know about, is a handle shifted right four bits with the top four bits set. Since this represents an illegal segment address, GEOS recognizes it as a virtual segment and will take the necessary actions to lock the block into memory before access and unlock it after access. Manipulation of the bits in the virtual segment is completely internal to GEOS.)

The kernel searches the message list until it comes across the appropriate message number and notes the message's position in the table. It then looks at the corresponding position in the pointer list. If the pointer there is a virtual segment and offset, it will load the appropriate code block, lock it, and execute the code. If the pointer points to fixed memory, the code will be executed immediately. (If the message number is not found in the table, the kernel will either execute the class' default handler or pass the message on to the class' superclass.)

Variant Classes

A variant class is one which has no set superclass. The variant's superclass is determined at run-time based on context and other criteria. Note that objects may not be variant--only classes may be variant. An object always has a specific class to which it belongs, and its class pointer always points to that class' ClassStruct structure. In addition, every variant class must also be a master class.

A variant class, however, may have different superclasses at different times. This functionality provides a form of "multiple inheritance": the class may inherit the instance data and functions of different classes depending on its attributes and desired features. Note, however, that a variant class may have only one superclass at any given moment.

The most visible example of a variant class is GenClass and how a generic object is resolved into its specific UI's appropriate representation. Each generic object (for example, a GenTrigger), is a subclass of the master class GenClass . All the instance data belonging to GenTriggerClass and GenClass , therefore, is stored in the Gen master part of the instance chunk.

GenClass , however, is a variant class, meaning that it does not know its superclass when the object is instantiated. Each generic object's class will be linked directly to another class provided by the specific UI in use: the specific UI's class provides the visual representation while the generic UI class provides the object's functionality. In this way, the object can actually perform many of its generic functions without having a visual representation.

The resolution of the superclass comes when the generic object is displayed on the screen: the kernel sees that the object has no superclass and looks into its instance data and class structure. The kernel then determines what the appropriate specific UI class will be for the object's class and provides the superclass link necessary. It also then initializes the superclass' master part of the object (in this case, the master part belonging to VisClass ), updating all the master part offsets in the instance chunk's master offset fields.

You can see from the above discussion that GenClass must know at least something about its potential superclasses. In fact, all variant classes must know at least the topmost class of all its potential superclasses. The definition of GenClass is

@class GenClass, VisClass, master, variant;

The @class keyword declares the new class, GenClass . GenClass is to be a variant class and therefore must also be a master class. All the superclasses of GenClass will be related to VisClass ; this means that all specific UI classes which may act as Gen's superclass must be subclassed from VisClass . (Another way of looking at the definition is that GenClass is an eventual subclass of VisClass --you have no way of knowing beforehand how many class layers may be between the two, however.)

The variant must specify an eventual superclass so the kernel knows how many master offset fields it must allocate when an instance of the variant is created. For example, a GenTrigger has two master groups: that of GenClass , and that of VisClass . Because the GenClass master group is necessarily below the VisClass master group in the class hierarchy (after the superclass link has been resolved), the GenClass master offset in the instance chunk must be after the VisClass master offset. If the definition did not specify VisClass as an eventual superclass, no master offset field would be allocated for it, and the Class_masterOffset field of GenClass ' Class structure would not be able to hold any particular value.

As stated at the beginning of this section, there are no "variant objects." Every object belongs to a specific class, and the object's class can never change. All instances of a variant class, however, can be resolved to different superclasses due to the way the superclass of each variant is resolved. One example of this is the generic-to-specific mapping of the GenInteraction object.

All GenInteractions are of class GenInteractionClass ; this never changes. GenInteractionClass , however, is a subclass of GenClass , a variant class. This means that the class tree of the GenInteraction object is only partially completed; before the GenInteraction is resolved, it looks like the simplified diagram below.

The GenInteraction object may be resolved into one of several different specific UI classes. For example, the motif.geo library has several classes for GenInteractions; among them are the classes that implement menus, dialog boxes, and grouping interactions. These classes are all specialized subclasses of VisClass , the eventual superclass of GenClass .

The class tree of the GenInteraction is not complete. A class tree must have links all the way back to MetaClass for it to be complete; this only goes to GenClass . GenClass has a special value in its Class_superClass field, 0001h:0000h. This represents a reserved "pointer" that indicates to the kernel that the class is a master class.

The superclass of the variant can be different for every instance because the superclass pointer is actually stored in the object's instance chunk rather than in the class' ClassStruct structure. This also allows a class tree to have more than one variant class in its hierarchy; for example, one variant could be resolved to be the subclass of another variant. The tree must always be headed by MetaClass .

The superclass pointer for the variant is stored in the variant's master group instance data. Not all master groups have superclass pointers; only those for variant classes. After the GenInteraction is resolved, the pointer (the first four bytes of the Gen master part) points to the proper superclass for this object (in this case, OLMenuWinClass ). The object, with its full class tree, is shown in the figure above.

An In-Depth Example

This section gives an example of a GenTrigger object after its variant part has been resolved. This example provides in-depth diagrams of the class and instance structures for those programmers who wish to understand them. There is no need to know them, however; you will not likely ever need to access the internals of either a class structure or an instance structure.

The GenTrigger taken as an example is the "New Game" trigger of the TicTac sample application. This trigger is the only child of the Game menu GenInteraction; it is shown below. The code defining the trigger is given in TicTac's New Game Trigger .

Code Display 5-4 TicTac's New Game Trigger

/* The TicTacNewTrigger has a moniker and an output. All its other instance data
 * is set to the GenClass defaults. The content of these fields is not important
 * for this example, however. */
@object GenTriggerClass TicTacNewTrigger = {
    GI_visMoniker = "New Game";
    GTI_destination = TicTacBoard;					/* Send the action message to the
                     * TicTac game board object. */
    GTI_actionMsg = MSG_TICTAC_NEW_GAME; 	/* The action message. */
}
The GenTrigger's Instance Chunk

The GenTrigger object has two master parts, just like the GenInteraction object shown in Variant Classes : the Gen master part holds the instance data for GenClass and GenTriggerClass . The Vis master part holds the instance data for VisClass and OLButtonClass . The MetaClass instance data consists only of the object's class pointer and has no master part.

The trigger's instance chunk's basic structure consists of the class pointer (four bytes) followed by two words of offset into the chunk. The first offset gives the location of the Vis master part, and the second gives the location of the Gen master part. After the offsets are the master parts themselves, and if the trigger had any variable data, it would appear at the end of the chunk.

Each master part has the master class' instance fields first, followed by those of its subclasses. All the fields that belong to VisClass begin VI_..., all those that belong to OLButtonClass begin OLBI_..., etc.

Notice also the first four bytes of the Gen master part: they contain a pointer to the "superclass" of GenClass for the trigger. Although the trigger typically does not have different forms in any given specific UI (as the GenInteraction does), it will have a different class for each specific UI it encounters. For example, the OSF/Motif class is OLButtonClass ; another specific UI will use a different class for GenTriggers.

The GenTrigger's Full Class Tree

Since GenClass is a variant, it has a superclass pointer of 0001h:0000h. This special value (with an illegal segment address) indicates to the kernel that this object's GenClass superclass is stored in the instance chunk itself. The superclass is stored in the first four bytes of the Gen master part, as shown in the previous section.

GenTriggerClass' ClassStruct Structure

Because all classes have the same class structure, only GenTriggerClass will be examined here. The class structure and the instance chunk structure are closely linked in several ways.

As shown in the diagram, the instance chunk points directly to the class. The class points to its superclass, thereby providing inheritance of all the methods and structures of classes higher in the class tree such as GenClass .

The class structure contains some information about the instance chunk's format, specifically Class_masterOffset and Class_instanceSize . Class_masterOffset gives the offset into the instance chunk where the offset to the master part is stored. Class_instanceSize contains the size of the master part so the kernel can quickly allocate the needed space when the master part is initialized.

The method table resides at the end of the class, and it has entries for each message handled by the class. GenTriggerClass handles seven messages (stored in Class_methodCount ); any message received by this trigger and not recognized by GenTriggerClass is passed up the class tree for handling. Thus, a MSG_GEN_SET_NOT_ENABLED sent to the trigger will be passed on to GenClass and will be handled there.

The method table has two parts: The first part is a listing of message numbers, and the second part is a listing of pointers to the method code. When the object receives a message, the kernel scans the first part to see if the class handles the message. If it does, the kernel then scans the second part of the table to get the pointer to the code. It then executes the code there as if that code were a function called by the object's code.

How a Message Is Handled

Most aspects of messages and messaging are described in the following section. This section, however, describes how the kernel finds and executes the code when a message is sent to the GenTrigger. The message is MSG_GEN_SET_USABLE (handled by GenClass ).

Messages are sent directly to an object using its optr. That is, when you send a message to this particular GenTrigger, you send it directly to TicTacNewTrigger, not to some monolithic "case" statement run by your application. Since the object's optr uniquely identifies the location of the object's instance chunk in memory, the kernel can quickly access the code for the handler.

When MSG_GEN_SET_USABLE is sent to the TicTacNewTrigger, for example, the kernel looks in the object's instance chunk for its class pointer. It follows this pointer and then looks in GenTriggerClass ' ClassStruct structure. It scans the first part of the class' method table for MSG_GEN_SET_USABLE . If the message is not there (and it isn't), the kernel will then follow the class' Class_superClass pointer and look in GenClass ' ClassStruct structure. It then scans the first part of GenClass ' method table for the message. GenClass has an entry for MSG_GEN_SET_USABLE , and therefore the kernel checks the second part of the method table for the code pointer. It follows this pointer to the method's entry point and begins executing the code there.

GEOS Programming: 3.3 The GEOS Object System: The GEOS Message System

Because objects are independent entities, they must have some means of communicating with other objects in the system. As shown in the example of the calculator and requestor objects in the Overview chapter, communication is implemented through the use of messages and methods.

The Messaging Process

When an object needs to notify another object of some event, retrieve data from another object, or send data to another object, it sends a message to that object. Sending a message is similar to calling a function in C in that the message can take parameters (including pointers) and give return values. However, messages are also quite different from function calls in the multithreaded environment of GEOS.

Each object block in the system is associated with a single thread of execution. Each thread that runs objects (some run only procedural code) has an event queue , a queue in which messages are stored until they can be handled. Every message sent to an object from another thread gets put in the object's thread's event queue. (Messages sent between objects within the same thread are not handled via the queue unless forced that way.) Thus, a single thread's event queue can have messages for many different objects. For most single-thread applications, the programmer will not have to worry about synchronization issues.

The sender of a message has to be aware of synchronization issues raised by having multiple threads in the system. Essentially, you can send a message two ways: The first, "calling" the message, allows the use of return values and acts almost exactly like a function call in C. This places the message in the recipient's event queue and then halts the sender until the return values are received. The sender "goes to sleep" until the message has been processed and is then awoken by the kernel, thus ensuring the message is handled before the sender executes another line of code. The call option should also be used when passing pointers; otherwise, the item pointed to may move before the message can be handled, invalidating the pointer.

The second, "sending" the message, is used primarily when synchronization is not an issue. For example, if the message merely notifies the recipient of some condition or event, or if it sends data with no expectation of a return value, the send option can be used. Essentially, the sender will send off the message and then forget about it, continuing on with its own business. (The exception to this is an object sending a message to another object in the same thread; then the message is handled immediately, as if it had been called.)

When an object sends a message, the message actually first gets passed to the kernel (this is all automatic). The kernel will put the message into the proper thread's event queue and, if necessary, put the sender's thread temporarily to sleep. When the recipient's thread is ready to handle the message, the kernel pulls it from the event queue.

The kernel then locates and loads the recipient object into memory (if necessary). The recipient's object block will be loaded and locked, and the object will be locked while processing the event. (Note, however, that it is possible for the object to move if the recipient makes a call to LMemAlloc() or does something else that can cause shuffling of the object chunks.) The kernel will follow the object's class pointer to its class and will scan the method table. If the class can handle the message, the proper method code will be executed. If the class can not handle the message, the kernel will follow the superclass pointer and check the method table there. The message will continue up the class tree like this until either it is handled or it reaches the root and returns unprocessed.

After the method code has been executed, the kernel collects any return values and wakes up the caller thread again if necessary. To the caller, it's as if the message were handled instantaneously (with the call option). Senders are never blocked; only messages called (with the call option) may block the caller's thread. If a message is sent to an object in the same thread, however, it will be executed as a call and will be handled immediately, unless the sender explicitly states that it should go through the message queue.

Be careful, though, if you are writing code in multiple threads (for example, if you subclass UI objects and write new method code for them). You have to make sure that two threads never call each other; this can lead to deadlock if the calls happen to overlap. The easiest way to deal with this is to have one thread always send a message requesting a return message with any needed return values. The other thread will then send off a return message with the data. For example, a UI object may require information from an application's object. The UI object sends MSG_REQUEST_INFORMATION (or something similar). The application's object then receives that message and responds with a MSG_RETURNING_REQUESTED_INFORMATION (or something similar). With this scheme, the application's object is free to use call whenever it wants, but the UI object must always use send.

Message Structures and Conventions

A message is simply a 16-bit number determined at compile time. Specifically, it is an enumerated type--this ensures that no two messages in the same class can have the same number.

An event is an opaque structure containing the message number and information about the recipient, the sender, parameters, and return values. When an object sends a message, the kernel automatically builds out the event structure (generally stored in the handle table for speed and efficiency). You will never have to know the structure of an event.

GEOS Programming: 4 Using Classes and Objects

The previous sections dealt with the internals of the GEOS object system. This section describes how you can create classes and objects and manage them during execution using Goc keywords and kernel routines. Almost all Goc keywords begin with " @ " (one notable exception is gcnList ).

All the most useful keywords available in Goc are shown in Goc Keywords . This display is for initial reference; all the keywords are detailed in depth in the following sections of this chapter and in the Routines Book.

Code Display 5-5 Goc Keywords

    /* Including .goh files */
@include <fname>;
    /* Defining New Classes and Subclasses */
@class	<cname>, <super> [, master [, variant]];
@endc
    /* Declaring a class */
@classdecl <cname> [, <cflags>];
    /* Defining messages for a class */
@message <retType> <mname>([@stack] <param>*);
@reserveMessages <num>;
@exportMessages <expName>, <num>;
@importMessage <expName>, <messageDef>;
@alias(<protoMsg>) <messageDef>;
@prototype <messageDef>;
    /* Defining instance data fields for a class */
@instance <insType> <iname> [ = <default>];
gcnList(<manufID>, <ltype>) = <oname> [, <oname>]*;
@instance @composite <iname> [ = <linkName>];
@instance @link <iname> [ = <default>];
@instance @visMoniker <iname> [ = <default>];
@instance @kbdAccelerator <iname> [ = <default>];
@reloc <iname>, [ (<count>, <struct>), ] <ptrType>;
@noreloc <iname>;
@default <iname> = <default>;
    /* Defining vardata fields for a class */
@vardata <type> <vname>;
@vardataAlias (<origName>) <newType> <newName>;
@reloc <vname>, <fn>, [ (<count>, <struct>), ] <ptrType>;
    /* Defining methods (message handlers) */
@method [ <hname>, ] <cname>[, <mname>+];
@method [ <hname>, ] <cname>, _reloc;
    /* Defining library code */
@optimize
@deflib <libname>
@endlib
    /* Defining resources */
@start <segname> [ , <flags> ];
@header <type> [ = <init> ];
@end <segname>
@chunk <type> <name> [ = <init> ];
@localize <string>;
@localize { <string> <length>};
@localize { <string> <minLength>-<maxLength> };
@localize not;
@chunkArray <stype> <aname> [ = {<init>} ];
@elementArray <stype> <aname> [ = {<init>} ];
@extern <type> <name>;
@gstring <name> = {[<command> [, <command>]+]} ;
    /* Declaring an object */
@object <class> <name> <flags>* = {
    [<fieldName> = <init>];*
    [<varName> [ = <init> ]]*;
}
    /* Accessing an object's instance data from one of its methods */
@self-><iname>
    /* Sending and calling messages */
@send [<flags>,+] <obj>::[{<cast>}] <msg>(<params>*);
<ret> = @call [<flags>,+] [{<cast>}] <obj>::[{<cast2>}]<msg>(<params>*);
@callsuper();
@callsuper <obj>::<class>::<msg>(<params>*) [<flags>,];
<event> = @record <obj>::<msg>(<params>*);
@dispatch [noFree] <nObj>::<nMsg>::<event>;
<ret> = @dispatchcall [noFree] [{<cast>}] <nobj>::<nMsg>::<event>;
    /* Using conditional code */
@if <cond>
@if defined(<item>)
@ifdef <cond>
@ifndef <cond>
    /* Creating Goc macros */
@define <mname> <macro>
    /* Esoteric Source-Code Management */
@optimize
@protominor <name>

Defining a New Class or Subclass

Non-relocatable Data

Defining Methods

Declaring Objects

Sending Messages

Managing Objects

GEOS Programming: 4.1 Using Classes and Objects: Defining a New Class or Subclass

@class, @classdecl, @endc, @default, @uses

You can create new classes in GEOS by using the Goc keywords @class and @endc . These frame the class definition as shown in Defining Classes ; the @endc keyword takes no parameters, but @class takes the following parameters:

@class    <cname>, <super> [, master [, variant]];
cname
This is the name of the new class.
super
This is the class name of the superclass.
master
When included, this word makes the new class a master class.
variant
When included, this word makes the new class a variant class. All variant classes must also be declared master classes.

Every class must have a class structure ( ClassStruct ) in memory. This is created and filled automatically by Goc and the kernel; however, you must use the @classdecl keyword to make sure the structure gets built. Only one @classdecl statement may be used for each class, however--Goc will give an error if the class is declared twice. This is also shown in Defining Classes , and its parameters are as follows:

@classdecl    <cname> [, <cflags>];
cname
This is the name of the class being declared.
cflags
These are optional flags, described below.

The optional flags that can be used with a class declaration determine how objects of the class get shut down (see Class_flags ). The flags you can use with @classdecl are

neverSaved
This flag indicates that objects of this class will neither be written to a state file nor be loaded in from a resource. This flag should only be used for classes whose objects will only be created at run-time (not declared in the .goc file) and for process classes.
discardOnSave
This flag applies only to master classes. Objects of this class will never be saved to a state file and must be re-initialized each time they are loaded. If you want an object simply to revert to its default configuration each time it is loaded, use the flag ignoreDirty instead in the object's declaration (see @object , in Declaring an Object ).

In addition, any variant class can have a default superclass. The variant is resolved at compile-time to have the default superclass as its superclass. To set a default superclass for a variant class, add a line with the following structure in the class definition:

@default <varRoot> = <super>;
varRoot
The name of the variant class with "Class" removed. (For example, GenClass would be specified as "Gen.")
super
The name of the superclass to set as the default.

Sometimes a variant class will know that it will be the subclass of a specific class, though it doesn't know (at compile time) just how that ancestry will be traced. You can use the @uses directive to let the compiler know this; that way, the variant class can define handlers for the "used" class. For example, if you know that variant class MyVariantClass will always be resolved as a descendant of MyAncestorClass , you can put the directive

@uses MyAncestorClass;

in the definition of MyVariantClass . The general format is

@uses <class>;
class
The class which will always be an ancestor to this class.

Code Display 5-6 Defining Classes

/* The @class keyword defines a new class or subclass. @endc ends the class
 * definition, and @classdecl must be put somewhere in the code to make sure Glue
 * will link the class structure into the compiled geode.*/
@class	MyNewClass, VisClass;
    /* Message declarations would go here. See @message.
     * Instance data field declarations would go here. See @instance
     * 						and @vardata. */
@endc
@classdecl MyNewClass, neverSaved;
@class	MyTriggerClass, GenTriggerClass;
    /* New messages for this subclass are defined here. */
    /* New instance data fields for this subclass are defined here. */
@endc
@classdecl MyTriggerClass;
/* When defining a variant class (which must also be a master class), you can
 * set a superclass for the variant at compile-time using @default. */
@class	MyNewVariantClass, MetaClass, master, variant;
    @default		MyNewVariant = VisClass;
@endc	MyNewVariantClass
@classdecl MyNewVariantClass;

Defining New Messages for a Class

@message, @stack, @reserveMessages, @exportMessages, @importMessage, @alias, @prototype

As discussed in The GEOS Message System , messages are simply 16-bit numbers allocated as an enumerated type. When a new class is defined, a constant is automatically created representing the first message number for the class. This constant is then used as the first number in the enumeration of messages.

The constant is built off the class' superclass. MetaClass has the first 16384 messages reserved for its use. Each master level gets 8192, and the first master class of a level gets 2048 of these. All other classes are allocated 512 message spots. Thus, a master class subclassed directly off MetaClass would have 2048 messages beginning with number #16384 (since the numbering is zero-based). A subclass of this would have 512 messages beginning with number #18432.

This numbering scheme ensures that no two classes at different levels in the class hierarchy will have the same message number. Specifically, a class will never have the same message number as one of its sub- or superclasses.

New messages must be defined in the class definition between the @class and @endc keywords (see above). They are defined much like normal function calls and follow the normal C calling conventions (see Defining Messages for examples). If your class uses messages from its superclass, you do not have to declare these messages in your class definition--they are inherited automatically. This is true even if you are subclassing the method to alter its functionality.

To define a new message, use the @message keyword. This keyword takes the following parameters:

@message   <retType> <mname>(<param>*);
retType
This is the data type of the return value of the message. It can be any standard C or GEOS data type (excluding structures), or a pointer to a structure. If this message has multiple return values, you must do as in C function calls and pass pointers to buffers for the return information.
mname
This is the name of the message. By convention, it will be MSG_ followed by a shortened version of the name of the class and then some useful name (e.g., MSG_META_INITIALIZE ).
param
This represents one or more parameters. Messages may have no parameters, one parameter, or several parameters. Parameter definition is essentially the same as definition of function parameters; see Defining Messages for examples.

NOTE: When defining a function with no parameters, it is best to declare it with "void" between the parentheses. This will make sure Goc gives an error if the function is called with arguments.

Messages for Use with Assembly Code

The @stack keyword indicates that parameters are passed on the stack; it is important to note that because of calling conventions, parameters passed on the stack must be listed in the message definition in reverse order from the way the handler pops them from the stack. This keyword is used only when the message may be handled by an assembly language method; its format is shown below:

@message <retType> <mname>(@stack <param>*);

All the parameters shown in the formats are the same as in the normal @message format.

Code Display 5-7 Defining Messages

    /* Each message is defined for a class within the class definition. */
@class	MyTriggerClass, GenTriggerClass;
    /* All the new messages MyTriggerClass can handle are defined here. */
@message void MSG_MYTRIG_SET_COLOR(colors colorIndex);
@message optr MSG_MYTRIG_RETURN_OPTR( void );
@message void MSG_MYTRIG_COLLECT_PARAMS(byte bParam, word wParam, char * string);
    /* Instance data fields would be defined here. */
@endc
@classdecl MyTriggerClass;
Exporting, Importing, and Reserving Message Ranges

As discussed above, message numbers are assigned based on the class' location in the class tree. No message number will ever conflict with messages defined in the class' superclasses. However, the assignment scheme opens up the possibility that classes on the same level in the class tree could have conflicting message numbers, as shown below.

Normally, this is not a problem. If subclasses are to receive the same message and handle it differently, the message can typically be defined in the superclass and simply be intercepted by the subclasses. Sometimes, however, different subclasses will need to have different definitions for the same messages. For example, a class supplied by a library may be used by several applications; if the applications each create a subclass, these subclasses can import particular messages that will be the same for all the subclasses in all the applications.

Goc therefore allows a class to export a range of message numbers which subclasses can import and create specific definitions for. This allows you greater control over what aspects of the class you can define.

To export a range of messages, use the @exportMessages keyword. This will set aside several message numbers which can then be imported by subclasses using the @importMessage keyword.

Another potential problem is upgrading your program from release to release. If you create classes that may grow in the future, you may want to reserve a number of message spots to ensure that those spots can be filled in later. Nothing is done with the spots; they are simply place holders for future upgrades. You can use the @reserveMessages keyword to reserve a range of any size. The parameters of these three keywords are shown below:

@reserveMessages    <num>;
@exportMessages     <expName>, <num>;
@importMessage	      <expName>, <messageDef>;
num
This is the number of messages in the exported range.
expName
This is the name of the exported range. This is used when importing messages to ensure that the proper numbers are used.
messageDef
This is a standard message definition line, the same as would be found with the @message keyword (though @message is left out).

Note that you do not need to reserve messages for upgrades; any class can always have messages tacked on to the end of its class definition. If you want to group the messages logically, however, you should reserve ranges where you expect additions to be made.

Aliasing Messages

The @alias keyword allows a single message to have more than one pass/return format. The @prototype keyword allows quick, clean, and convenient repetition of a single format for many different messages; it also allows a class to create a prototype so users of a message can have their own messages with the same format.

@alias is used when a single method takes conditional parameters. For example, a method may take a word value in a certain case and a dword value in another (dependent upon a passed flag). Each condition must be accounted for in its own message format. Rather than create a message and a method for each case, you can create a single assembly-language method for all the different pass/return formats; then, you can use @alias to make several messages refer to the same method, each using a different format.

@alias(<protoMsg>) <msgDef>;
protoMsg
The name of the original message. The new message may have different pass/return values but will invoke the same method code and will have the same message number.
msgDef
The new message definition. It follows the same format as messages defined with the @message keyword (with @message left off).

In addition, if you have a single pass/return format for many messages, you can use the @prototype keyword as coding shorthand. For example, if an object has ten messages that all take two parameters and return a single value, you can set up the format with the @prototype keyword and then use a simpler format for definition of your messages. An example is shown in Aliasing Messages , and the parameters of this keyword are shown below.

@prototype <msgDef>;
msgDef
This is the standard message definition. For the message name, use something like " MY_PROTOTYPE " that you can insert later into the definitions of your real messages. All other parts of the message definition are the same as would be declared with @message (with @message left off).

Code Display 5-8 Aliasing Messages

    /* MyClass in this example uses both prototype and aliased messages. */
@class MyClass, MetaClass;
    /* The following is a normal message declaration. The register that this
     * parameter uses is specified because the handler (method) is written in
     * assembly language. */
@message void MSG_MESSAGE_WITH_WORD(byte flag = cl, word value = dx);
    /* The following message invokes the same method as the alias above.
     * It has the same message number but passes a different sized parameter
     * in different registers. */
@alias(MSG_MESSAGE_WITH_WORD) void MSG_MESSAGE_WITH_DWORD(byte flag = cl,
                            dword value = dx:bp);
    /* The following message is not used. Its pass and return values can
     * be used elsewhere, however, to ensure that all handlers of this message
     * type are given the same format. */
@prototype int MSG_MYCLASS_PROTO(int a, int b);
    /* The following have the same return values and parameters
     * as the prototype above. */
@message(MSG_MYCLASS_PROTO) MSG_MY_CLASS_ADD;
@message(MSG_MYCLASS_PROTO) MSG_MY_CLASS_SUBTRACT;
@message(MSG_MYCLASS_PROTO) MSG_MY_CLASS_MULTIPLY;
@endc
@classdecl MyClass;

Defining Instance Data Fields

@instance, @composite, @link, @visMoniker, @kbdAccelerator

Instance data fields are all defined with the @instance keyword. Other keywords may be included in the @instance declaration for special types of data. All instance data definitions must appear between the class' @class and @endc keywords (see above under class definition).

The @instance keyword is used to define normal instance data. If you have data that must be added or removed dynamically (such as hints), use the @vardata keyword, described in Defining and Working With Variable Data Fields . Also, if you have data that requires relocation (such as pointers to fixed data) when the object is loaded, use the @reloc keyword.

The format of the @instance keyword is as follows:

@instance    <insType>    <iname> = <default>;
insType
A standard C or GEOS data or structure type representing the data type of the instance field.
iname
The name of the instance field.
default
The default value of the instance field if it is not filled in when an object of this class is instantiated. The value must, of course, be appropriate for the data type.

Goc has several special types of instance data fields that you can declare along with @instance to make object definition easier. The format for using one of the special types is shown below (with examples in Declaring Instance Data Fields ). Each of the types is also described below.

@instance    <specType> <iname>;
specType
This is the keyword (one of those shown in the list below) that defines the special type of this field.
iname
This is the name of the instance field.

The special types are given here:

@composite
This field is used when objects of the class being defined are allowed to have children. The @composite field will actually contain an optr to the first child object in an object tree. Since most objects in object trees are subclassed from VisClass or GenClass , you will most likely never use the @composite keyword. Both VisCompClass and GenClass have @composite fields predefined. The @composite type has a special format, shown below:
@instance  @composite <iname> = <linkName>;
where iname is the name of the instance field and linkName is the name of the field designated as @link (below). Note that there must be a @link field in every class that has a @composite field. See Managing Object Trees for more information on object trees and the composite and link fields.
@link
This field is used by objects that can be children in an object tree. Where the @composite field points to the first child, the @link field points to the next sibling. If there is no next sibling, this field will point back to the parent object. Since most objects in object trees are subclassed from VisClass or GenClass , you will most likely never use the @link keyword. Both VisClass and GenClass have @link fields predefined.
@visMoniker
This field is designated as holding a pointer to a visual moniker chunk for the object. It is used in GenClass --see the GenClass chapterfor information on the GI_visMoniker field. The moniker or moniker list must be in the same resource as the generic object using that moniker since only the chunk's handle is stored. A moniker list can store full object pointers to its monikers, so monikers referenced by a list need not be in the same resource as that list; thus if an object's moniker is specified via a list, then while the list must be in the same resource as the object, the monikers themselves need not be.
@kbdAccelerator
This field contains a character sequence that, when typed by the user, causes the object to execute its default operation. For example, a keyboard accelerator could invoke a trigger implemented as a menu item. It is used in GenClass only.

Code Display 5-9 Declaring Instance Data Fields

/* GenClass is a good example of many of the different types of fields. */
@class	GenClass, VisClass, master, variant;
    /* The GenClass messages are defined here. */
    @instance @link GI_link;
    @instance @composite GI_comp = GI_link;
    @instance @visMoniker GI_visMoniker;
    @instance @kbdAccelerator GI_kbdAccelerator;
    @instance byte GI_attrs = 0;
    @instance byte GI_states = (GS_USABLE|GS_ENABLED);
    /* Hints and other variable data fields are defined with @vardata. */
@endc

New Defaults for Subclassed Instance Data Fields

@default

Recall that when defining an instance data field you can set up a default value for that field. When creating a subclass, you may wish to specify that the subclass should have a different default value for a given field than the superclass does. Use the @default keyword to do this:

@default <iname> = <default>;
iname
The name of the instance field.
default
The new default value of the instance field if it is not filled in when an object of this class is instantiated. The value must, of course, be appropriate for the data type. You may use @default as part of this value; this @default will be treated as the value of the superclass. (If this seems confusing, try looking at the example.)

For example, a subclass of GenInteraction could set GIV_DIALOG as its default value for the GenInteraction instance field GII_visibility :

@default GII_visibility = GIV_DIALOG;

A generic class might want to have the same value for its GI_states field as its superclass, except with the GS_USABLE flag turned off:

@default GI_states = @default & ~GS_USABLE;

Defining and Working With Variable Data Fields

@vardata, @vardataAlias, ObjVarAddData(), ObjVarDeleteData(), ObjVarDeleteDataAt(), ObjVarScanData(), ObjVarFindData(), ObjVarDerefData(), ObjVarDeleteDataRange(), ObjVarCopyDataRange(), MSG_META_ADD_VAR_DATA, MSG_META_DELETE_VAR_DATA, MSG_META_INITIALIZE_VAR_DATA, MSG_META_GET_VAR_DATA

Most classes will have well-defined instance data fields; each object in the class will have the same data structures, and all the instance chunks will look relatively similar and will be the same size.

Many classes, however, will also use "variable data," or instance fields that may be added or removed dynamically. This allows objects within the same class to have more or less instance data than other objects in the class. One example of variable data is the use of hints in generic UI objects. Because each object in a given class may or may not have hints, the objects can actually have different instance sizes. Variable data instance fields are defined with the use of the @vardata keyword.

Using variable data, however, is somewhat more complex than using standard instance data. You must use special kernel routines or messages to get a pointer to the data; then you can use the pointer to access the field.

Variable data is stored together at the end of the instance chunk in "data entries." Each entry consists of a primary word and optional extra data. The primary word represents a data type defined by the keyword @vardata . This type is created automatically by Goc when the @vardata keyword is used.

Each data entry is associated with a master class level and is considered part of the instance data for that master level (despite being stored at the end of the instance chunk). Thus, when a master part of an object is destroyed, the variable data entries associated with that master class will also be destroyed. For example, when a UI object is set not usable (taken off the screen), its Vis master part is removed from the instance chunk; any variable data entries associated with VisClass will also be destroyed.

Variable data may also be tagged for saving to the state file. That is, you can set up individual data entries to be saved to a state file and to be reinstated when the object is loaded from the state file. For more information about state saving, see Saving Object State .

Variable data may be defined in an object's declaration in your .goc file or may be added and removed during execution. This gives the potential for using variable data as temporary storage in an object's instance chunk; however, temporary data used in this manner should be kept small to avoid slowing down the system--constantly resizing instance chunks to add and remove vardata fields makes more work for the memory manager.

To define a variable data type in a given class, use the @vardata keyword as follows (an example is given in Defining Variable Data ):

@vardata    <type> <vname>;
type
This is the data type of any extra data associated with the variable data. It must be a standard C or GEOS data type. If the type void is specified, no extra data will be added to the data entry when it is created. (An instance data field may be declared as an array, just as in standard C.)
vname
This is the name of the variable data type. This name is used whenever referring to the vardata entry. Note that no two variable data types should have the same name, even if they're in different classes. Doing so will cause a compilation error. It's a good practice to put the class name within the data type name.

Code Display 5-10 Examples of Instance Data Declarations

/* These are some data fields for MyDataClass.
 */
    @instance		ChunkHandle		MDI_aChunk;
    @instance		HelloInfoFlags		MDI_flags;
    @instance		byte		MDI_lotsOfNumbers[32];

Some vardata types may have varying amounts of extra data. For example, one type may have either a word or a dword of extra data. To allow this, you can set up an alias with the new type attached using the keyword @vardataAlias :

@vardataAlias  (<origName>) <newType> <newName>;
origName
This is the name of the original variable data field already defined with @vardata .
newType
This is the data type of the new variable data field, a standard C or GEOS data type.
newName
This is the name of the new variable data field. In essence, the original and new fields will have the same data type word but will have different extra data size.

As noted earlier, the data type field in the data entry has two flags associated with it. These flags are each one bit:

VDF_EXTRA_DATA
This flag indicates that this data type carries extra data.
VDF_SAVE_TO_STATE
This flag indicates that this particular data entry should be saved along with all the other object's instance data when the state is saved. It should likewise be restored when the object is restored from the state file. Unless set off explicitly, this flag will be set for every data type defined in a .goc or .goh file.

The bitmask VDF_TYPE is a bitwise OR of VDF_EXTRA_DATA and VDF_SAVE_TO_STATE. You can use it to mask out all but those bits.

Code Display 5-11 Defining Variable Data

/* Hints are defined with the @vardata command, as is shown in GenClass. Only a
 * small portion of the hints for GenClass are shown here. Those with structures
 * or data types (not "void") have extra data fields associated with them. */
@class GenClass, VisClass, master, variant;
    /* Messages are defined here. */
    /* Followed by instance data defined with @instance. */
    @vardata void HINT_CENTER_MONIKER;
    @vardata SpecSizeSpec HINT_CUSTOM_CHILD_SPACING;
    @vardata char[] ATTR_GEN_INIT_FILE_KEY;
    /* Relocatable instance fields (see the next section) are defined with
     * @reloc. This field contains an object pointer that must be resolved
     * when the GenClass object is loaded. */
    @instance @link GI_link;
    @reloc GI_link, optr;
    @vardata DestinationClassArgs ATTR_GEN_DESTINATION_CLASS;
    @reloc ATTR_GEN_DESTINATION_CLASS, 0, optr;
@endc

The kernel provides a number of routines an object may use to add, remove, and change its own vardata entries. Note that all these routines must be called from within the object containing the variable data entries; since variable data is instance data, it is against OOP doctrine for one object to alter another object's variable data directly.

Instead, MetaClass provides vardata messages that can be sent by one object to another to add, remove, change, or retrieve vardata entries of another object. The kernel routines and MetaClass messages are outlined below:

  • ObjVarAddData()
     This routine adds an new entry for the passed data type or replaces the extra data associated with a particular data type entry.
  • ObjVarDeleteData()
     This routine deletes the entry for a particular data type when passed the data type.
  • ObjVarDeleteDataAt()
     This routine deletes a particular data entry when passed the entry's pointer as returned by ObjVarAddData() , ObjVarFindData() , or ObjVarDerefData() .
  • ObjVarScanData()
     This routine causes the kernel to scan all data entries in an object's variable data and call any "handler routines" listed for them. This process is described below.
  • ObjVarFindData()
     This routine searches for and returns (if possible) a pointer to a data entry of the passed data type.
  • ObjVarDerefData()
     This routine returns a pointer to a data entry when passed the object's optr and the data type. If the entry does not exist, this routine will call on the object to create and initialize the entry. Such variable data then behaves much like instance data. The object containing the vardata is responsible for creating the entry and then initializing it upon receipt of a MSG_META_INITIALIZE_VAR_DATA , described below.
  • ObjVarDeleteDataRange()
     This routine deletes all data entries with types in the passed range.
  • ObjVarCopyDataRange()
     This routine copies all data entries within the passed range from one object's instance chunk to another`s. If any entries are copied, the destination object will be marked dirty for saving. This routine must be called by the destination object; it is bad policy for one object to alter another object's instance data. This routine is primarily for copying hints from one UI object to another and is not commonly used by applications.

The four messages (in MetaClass ) that can be used to add, delete, and alter variable data entries remotely are listed below. Classes will never need to intercept and subclass these messages because the proper functionality is implemented in MetaClass .

MSG_META_ADD_VAR_DATA
Adds a new vardata type to the recipient object. If the type already exists, the passed type replaces the old one.
MSG_META_DELETE_VAR_DATA
Deletes a vardata type from the recipient's instance data. If the type does not exist, nothing is done.
MSG_META_INITIALIZE_VAR_DATA
Used when something is trying to access an object's vardata field remotely but the field has not yet been added to the object or initialized. The object must create and/or initialize the vardata field at this point.
MSG_META_GET_VAR_DATA
Returns the extra data set for the passed data type.

In addition to supporting variable data structures, GEOS allows you to set up "handlers" for different variable data types. Handlers are routines that process a given data entry; for example, each generic UI object stores a number of hints. Specific UI classes, when attached to the generic object, have a specific routine to handle each hint supported. Some specific UIs do nothing with certain hints; these specific UIs do not have handlers for those hints.

Handlers are associated with data types through the use of a VarDataCHandler table. This is a table that you set up in your .goc file that contains pairings of routine names with @vardata field names. An example of the VarDataCHandler table is shown in Variable Data Handlers .

A handler is simply a normal C routine or function and is defined as such. The handler should be declared as an _pascal routine.The table pairs the handler with the @vardata data type, and when ObjVarScanData() is called, all handlers for all data types are called in order. This is true for the object's class and all its superclasses since variable data is inherited just as normal instance data is. The handler can do almost anything appropriate with the exception of destroying the object or adding or deleting variable data from the object.

Code Display 5-12 Variable Data Handlers

    /* This example is taken from the C VarData sample application. */
/* This is a VarDataCHandler. It is called by the ObjVarScanData() routine when
 * the data type corresponding to this routine in the VarDataCHandlerTable
 * is encountered. The parameters indicated are passed.
 *
 * This particular handler is actually used for several different data types
 * (see VarDataCHandlerTable below). The data type can be distinguished by
 * the `dataType' parameter.
 *
 * NOTE: VarDataInteractionHintHandler, like any handler used in a
 * VarDataCHandler structure, must be declared _pascal. */
void _pascal VarDataInteractionHintHandler(MemHandle mh, ChunkHandle chnk,
        void *data, word dataType, HandlerData *handlerData) {
    if (dataType == HINT_ORIENT_CHILDREN_HORIZONTALLY) {
    handlerData->HD_flags.has_horiz = 1;
    } else if (dataType == HINT_ORIENT_CHILDREN_VERTICALLY) {
    handlerData->HD_flags.has_vert = 1;
    } else if (dataType == HINT_ALLOW_CHILDREN_TO_WRAP) {
    handlerData->HD_flags.has_allow_wrap = 1;
    } else if ((dataType == HINT_WRAP_AFTER_CHILD_COUNT) &&
        (((WrapAfterChildCountData *) data)->WACCE_childCount == 2)) {
    handlerData->HD_flags.has_wrap_after = 1;
    }
}
/* This is the VarDataCHandler Table. It consists of data type/VarDataCHandler
 * pairs. The VarDataCHandlers are far routines. */
static VarDataCHandler varDataInteractionHandlerTable[] = {
    {HINT_ORIENT_CHILDREN_HORIZONTALLY, VarDataInteractionHintHandler},
    {HINT_ORIENT_CHILDREN_VERTICALLY, VarDataInteractionHintHandler},
    {HINT_ALLOW_CHILDREN_TO_WRAP, VarDataInteractionHintHandler},
    {HINT_WRAP_AFTER_CHILD_COUNT, VarDataInteractionHintHandler}
};

Defining Relocatable Data

@reloc

Some objects and classes may have instance data fields that must be resolved when the object is loaded and linked at run-time. For example, if the object contains an optr to another object, that optr must be updated when the object is loaded and resolved since the global memory handle can't be known at compile-time.

For some special instance fields, this happens automatically. For example, the @composite and @link fields as well as optrs are automatically resolved. However, if you add your own instance fields requiring relocation, you will have to set them up with the @reloc keyword. This is true for both static and variable data.

This keyword uses two formats. The first listed here is for normal, static instance data, and the second is used with variable data.

 @reloc    <iname>, [(<count>, <struct>)] <ptrType>;
iname
This is the name of the relocatable instance field.
count
If the instance variable is an array of relocatable data or structures containing relocatable fields, this is the number of elements in the array.
struct
If the relocatable data is an array of structures, this represents the name of the field within each structure that requires relocation.
ptrType
This is the type of relocatable data contained in the field. It may be one of optr , ptr , or handle .
@reloc <vname>, <fn>, [(<count>, <struct>)] ptrType;
vname
This is the name of the variable data type.
fn
This is the name of the field within the variable data's extra data. If there is no extra data with this data type, put a zero rather than a name.
count
If the instance variable is an array of relocatable data or structures containing relocatable fields, this is the number of elements in the array.
struct
If the relocatable data is an array of structures, this represents the name of the field within each structure that requires relocation.
ptrType
This is the type of relocatable data contained in the field. It may be one of optr , ptr , or handle .

GEOS Programming: 4.2 Using Classes and Objects: Non-relocatable Data

 @noreloc

To force an instance data field which would normally be relocatable (e.g., an optr) to not be relocatable, use the @noreloc keyword. Use this keyword together with the name of the field to be marked non-relocatable directly after defining the instance field itself as shown in Use of the @noreloc Keyword .

Code Display 5-13 Use of the @noreloc Keyword

    @instance optr MCI_ruler;				/* Normally MCI_ruler would be reloc... */
    @noreloc MCI_ruler;				/* ...but now it isn't. */

GEOS Programming: 4.3 Using Classes and Objects: Defining Methods

@method, @extern

Methods are the routines executed when an object receives a message. Each class understands a certain set of messages; each of these has a place in the class' method table and corresponds to one method.

Although methods are class-specific, they are not defined between the @class and @endc of class definition. Instead, their declaration line links them to a single class and to a specific message. Goc, Glue, and GEOS build each class' method table automatically; you do not have to create the table yourself.

To define a method, use the @method keyword. This has the following structure:

@method    [<hname>,] <cname>[, <mname>]+;
hname
The handler name, if any. If you wish to use the method as a function, it must have a handler name. If you do not provide a handler name, Goc will create one for you. This name is useful for setting breakpoints when debugging. If you do not provide a name, Goc constructs the name by concatenating the class name (with the -Class suffix, if any, removed) with the message name (with the MSG_- prefix, if any, removed). For example, Goc would call MyClass ' handler for MSG_DO_SOMETHING "MyDO_SOMETHING".
cname
The name of the class to which this method belongs. Each method may belong to only one class.
mname
The name of the message that invokes this method. The plus symbol indicates that one method may be invoked by more than one message as long as they all have the same parameters and return values. At least one message must be specified.
There is a special Goc keyword, _reloc , used in place of a message name when writing a method for MSG_META_RELOC and MSG_META_UNRELOC . A method handling _reloc will handle both MSG_META_RELOC and MSG_META_UNRELOC ; the method code can check the value of its message argument to find out which message is being handled.

Note that the name of the method (the handler name) is optional. Parameters and return values are not put in the method declaration--these are defined with @message as discussed in The GEOS Message System .

If you will wish to call the method as a routine occasionally, your compiler will probably require that you provide a prototype for the routine. If your @message declaration looks like

@message word MSG_MC_DO_SOMETHING(word thing);

and your @method declaration looks like

@method DoSomething, MyClass, MSG_MC_DO_SOMETHING {
/* Code Here */ }

Then your protoype should look like

extern word _pascal DoSomething(optr oself,
                      MyMessages message,
                      word thing);

The name of the type MyMessages is constructed automatically by taking the name of the class, removing the "Class" suffix, and replacing said suffix with "Messages".

Normally, all of a class' methods will be coded in the same code file in which the class is declared with @classdecl . If, however, you find you need to declare a class' methods in a different file, you can use the @extern keyword to indicate a method is defined and/or used in a different object file. Goc will give no error if @extern is used and the method exists nowhere; Glue, however, will give a linking error in such a case. There is no such requirement, however, if you are putting only the class definition (the definitions between @class and @endc ) in a different file. In this case, you can put the class definition in a .goh header file and the method code in the same .goc file as the @classdecl statement; you must @include the .goh file, but you won't need to use the @extern directive (as long as the method code is in the same file as the @classdecl directive).

The format for using @extern is as follows:

/* In the file in which the class is declared with  * @classdecl:
 */
    @extern method <cname>, <mname>+;
/* In the file containing the method code: */
    @extern method <cname>, <mname>+ {
        ...method code goes here...
    }
cname
The name of the class for which the method is defined.
mname
The name of the message which invokes the method. Note that external method definitions, like normal method definitions, can handle more than one message.

Three parameters are passed automatically with messages and do not have to be declared in the @message definition. They are important to know when writing methods, however, because they can greatly simplify your code. These are standard parameters for all classes except ProcessClass and its subclasses below:

pself
A far pointer to the object's instance data. pself points to the master group for the class for which the handler is defined. Note that this pointer may be invalidated by message calls so it is preferable to use the GOC directive, @self , instead. Unlike pself, @self is always valid.
oself
An optr to the object's instance data. It contains the global memory handle and chunk handle of the instance chunk. This can be used for routines that act on the object's instance data.
message
The message number of the message being handled.

As mentioned, ProcessClass is a special type of class. It has no true instance data because it uses the standard PC structure of an application (idata, udata, etc.). It only has one standard parameter to each of its methods: the message that was sent to it. This is because the "instance data" of ProcessClass includes all the global variables of your program. They are accessed automatically, no oself or @self is required.

Code Display 5-14 A Class Definition

/* The class ValClass defines four messages that invoke four different methods. The
 * entire class is shown in this example; it will function properly if coded this
 * way. Note that the methods have the class name in their declaration line
 * and thus do not appear within the class definition. */
@class ValClass, MetaClass;
@instance int value;			/* instance data value: an uninitialized integer */
    /* message declarations
     * All four messages will be handled by this class. They return
     * the types shown and take the parameters defined. */
@message int MSG_VAL_GET_VALUE();
@message void MSG_VAL_SET_VALUE(int newValue);
@message void MSG_VAL_NEGATE_VALUE();
@message Boolean MSG_VAL_IS_VALUE_BIGGER_THAN(int newValue);
@endc
@classdecl ValClass;			/* the class structure must be put in memory */
    /* Method Declarations
     * Each of the four methods is a single line of code. Note that the
     * parameters are automatically defined in the message definition and do
     * not need to be restated in the method definition. The same is true of
     * the return type. Note also that the class and message names appear in
     * the @method line. */
@method	ValGetValue, MyClass, MSG_VAL_GET_VALUE {
    return(@self->value);
}
@method	ValSetValue, MyClass, MSG_VAL_SET_VALUE {
    @self->value = newValue;
}
@method	ValNegateVal, MyClass, MSG_VAL_NEGATE_VALUE {
    @self->value *= -1;
}
@method	 ValClass, MSG_VAL_IS_VALUE_BIGGER_THAN {
    /* This handler's name will automatically be created to be
     * ValVAL_IS_VALUE_BIGGER_THAN. You can use this name as a
     * C function call from within the same thread. */
    return(@self->value > newValue);
}

You may sometimes wish to call a method with normal C call-and-return conventions, rather than by sending a message. To do so, you will have to declare the method as a routine as well as a method. The declaration should have the following format:

extern <type> _pascal <MethodName>(
    optr					oself,
    <TruncatedClassName>Messages					message,
    <type1>					<arg1>,
    <type2>					<arg2>)
type
This is the type returned by the method. It may be any data type.
MethodName
This is the name of the method. If you will be calling a method as a routine, you must give the method a name when you declare it (see To define a method, use the @method keyword. This has the following structure: ). Use the same name here.
TruncatedClassName
This is the name of the class, without the word "Class". The type of this argument is the truncated class name followed (with no space) by the word "Messages". Thus, for "HelloCounterClass", the truncated class name would be "HelloCounter", and the type of this field would be "HelloCounterMessages".
type n , arg n
Use these fields to declare each of the arguments passed to the message. Be sure to use exactly the same arguments, and in the same order, as in the message declaration.

Code Display 5-15 Declaring a Method As a Routine

@message int MSG_HELLO_COUNTER_RECALCULATE_VALUE( \
                HelloPriority 		priority, \
                word 		randomDatum, \
                char 		aLetter);
extern int _pascal HelloCounterRecalculateValue(
            optr			oself,
            HelloCounterMessages			message,
            HelloPriority			priority,
            word			randomDatum,
            char			aLetter);
@method	HelloCounterRecalculate, HelloCounterClass, \
        MSG_HELLO_COUNTER_RECALCULATE_VALUE {
    /* method code goes here... */
}

GEOS Programming: 4.4 Using Classes and Objects: Declaring Objects

In GEOS programs, you can instantiate objects in two ways: You can declare them in your source code with the @object keyword, or you can instantiate them and fill in their instance data during execution. In most cases, you will probably do the former, especially with generic UI objects.

Additionally, you can create resources and chunks with the @start, @end , @header , and @chunk keywords. GEOS libraries also need an additional set of declarations in their definition ( .goh ) files; these declarations ( @deflib and @endlib ) indicate that the code contained between them is part of the specified library.

Defining Library Code

@deflib, @endlib

If your geode is a library, it will likely have a number of .goh files. Each of these files contains some portion of the library's code and is included by applications that use the library. The library code must be delimited by the two keywords @deflib and @endlib , which have the following formats:

@deflib <libname>
@endlib
libname
The permanent name of the library with the extender stripped off. For example, the UI library's name is ui.lib, and the format would then be
@deflib ui
    /* library code here */
@endlib

Note that these two keywords are only necessary in files that define classes in the library. Files that have just code or data used in the library do not require them (though they are allowed).

Declaring Segment Resources and Chunks

@start, @end, @header, @chunk, @gstring, @chunkArray, @elementArray, @extern

There are essentially three types of resources in GEOS: code resources containing routines and methods, object blocks containing object instance chunks (and often data chunks), and data resources containing only data chunks. Code resources are created automatically, and no declaration is required for them (unless you require special segments; then you should use the pragmas appropriate for your C compiler).

Object blocks or other LMem resources are declared with @start and @end . You can set a special header on a local memory resource with the @header keyword. These are described below, and an example of declaring the resource block is given in Declaring Data Resources . Note that the @header keyword must come between the @start and @end delimiters.

@start    <segname> [, <flags>];
@header   <type> [= <init>];
@end     <segname>
segname
This is the name of the resource segment.
flags
These are optional flags that determine two characteristics of the resource. If the flag data is set, the block will be set to a data resource--the default is an object block. If the flag notDetachable is set, the resource block will never be saved to a state file.
type
This is the name of a structure type that will act as the header structure for the resource block. It must be some permutation of either LMemBlockHeader (for non object blocks) or ObjLMemBlockHeader (for object blocks).
init
This is an initializer for the new header type. Typically, some data fields will be added on to the end of one of the standard LMem block headers. These fields may be filled in with initializer data with this optional argument.

The resource elements (objects or chunks, for example) are also declared within the @start and @end delimiters. The @chunk keyword declares a data chunk and is shown below. For the @object keyword, see the next section.

@chunk    <type> <name> [= <init>];
type
This is the data type that will be held in the chunk.
name
This is the name of the chunk. You may use this name as you would a variable name to reference the chunk.
init
This is initializer data in the standard C format. If initializing a structure, make sure you put the data within curly braces.

There is a special-case syntax for the declaration of a chunk which contains GString data. A GString is a stored series of graphics commands, discussed in detail in the Graphics Environment chapter. Its syntax may be summarized:

@gstring <name> = { <init> };
name
This is the name of the chunk. You may use this name as you would a variable name to reference the chunk.
init
The is the initializer data. It is a series of bytes. Normally, a GString's data set up in a @gstring chunk is set up by means of the GS...() macros.

Two other types of resource elements may also be defined, both of which are array types. The @chunkArray keyword declares a chunk array structure, and the @elementArray keyword declares an element array structure. See the Local Memory chapter, for information on the structure and usage of chunk and element arrays. These static arrays must not appear in a resource marked data; the resource may be marked notDetachable or may be unmarked. In the .gp file, the resource should be marked lmem, optionally read-only. The formats for the keywords are described below:

@chunkArray <stype> <aname> = {<init>};
@elementArray <stype> <aname> [ = {<init>} ];
stype
This is the type of structure stored in the array.
aname
This is the name of the array.
init
This is any initializer data. If there is no initializer data (i.e., you want to declare an empty chunk array), you'll want a code snippet like the following:
@chunk ChunkArrayHeader <aname> = {0,
    sizeof(<stype>), 0, sizeof(ChunkArrayHeader) };

Here is an example showing two statically declared chunk arrays:

#define CELL_DATA_LENGTH 15

typedef char CellDataStruct[CELL_DATA_LENGTH];

@chunkArray CellDataStruct headerDataArray = {
    "no", "Description",  "type", " $/lb"
};

@chunkArray CellDataStruct coffeeDataArray = {
    "0", "House Blend",  "PEET", "$ 6.95",
    "1", "Mocha-Java",   "PEET", "$10.75",
    "2", "Garuda Blend", "PEET", "$ 9.25",
    "3", "Italian Dark", "DARK", "$ 8.75",
    "4", "French Roast", "DARK", "$ 8.75",
    "5", "Sumatra",      "PACF", "$ 8.95",
    "6", "Sulawesi-Kal", "PACF", "$11.55",
    "7", "Java",         "PACF", "$ 8.75",
    "8", "Aged Java",    "PACF", "$ 9.75",
    "9", "Mexican",      "AMER", "$ 7.55"
};

One other keyword, @extern , allows you to use chunks from a different compilation session. The @extern keyword can be used to reference remote (external) chunks, objects, and visual monikers. If the item referenced by @extern is not actually defined anywhere, Glue will return an error when it tries to link the item. (Note also that Glue can not link through an external item; only one layer of external linkage is allowed. Thus, one object could not have an @extern to a chunk that had an @extern to a visMoniker.) Children in object trees may not be defined with @extern ; Goc will produce an error in this case. The format of this keyword is as follows:

@extern chunk <cname>;
@extern object <oname>;
@extern visMoniker <vmname>;
cname
This is the name of a chunk.
oname
This is the name of an object.
vmname
This is the name of a visMoniker chunk.

Code Display 5-16 Declaring Data Resources

/* This example declares a data block with three different chunks in it and
 * its own header type. */
typedef struct {
    LMemBlockHeader			MLMBH_meta;       /* basic header structure */
    int			MLMBH_numEntries;  /* the number of entries in the block */
} StudentBlockHeader;
@start StudentBlock, data;					/* data flag indicates LMem block */
@header StudentBlockHeader = 1;					/* initialize new header fields */
/* The three chunks are defined below. Each represents a single field associated
 * with a single student; that is why the header's MLMBH_numEntries field contains
 * one (rather than three). */
@chunk	char	firstName[] = "John";
@chunk	char	lastName[] = "Student";
@chunk	int	grade = 6;
@end StudentBlock					/* end of resource block */

Declaring an Object

@object, @default, @specificUI, gcnList

The @object keyword is much like the @chunk keyword in its use. It allocates a chunk in a resource segment and fills it with initializer data. However, it also does much more in that it creates an object of a specified class and fills it with default data if no initializer data is specified.

The @object keyword, like @chunk , must be used between the delimiters @start and @end (see above). However, it must be used in an object block (it can not be used in a data resource segment). Its format is below, and several examples are shown in Declaring Objects with @object .

@object   <class> <name> = <flags>* {
    <fieldName> = <init>;*
    <varName> [= <init>];*
};
class
The class of the object. It must be a valid class defined either by GEOS or by your own code.
name
The name of the object. The object's name will be used when sending messages to it.
flags
Currently only one flag is allowed for objects: ignoreDirty . When set, it will cause the object to ignore when changes have been made to its instance data; this means it will always be restored from a state file in the same way as it is defined in the code file. This flag should not be set for any generic objects.
fieldName
The name of an instance data field defined for the class.
varName
The name of a variable data instance field. If the variable data field has extra data, you can initialize it with the optional argument.
init
This is initializer data for the instance data field. If you want default values in the field, you can either leave the field out of the @object declaration or use the keyword @default for the initializer. When declaring variable data, though, be aware that if the extra data is a structure, the initializer must be enclosed in curly braces.

GEOS supports special instance data fields as described in Defining Instance Data Fields . These are declared as normal fields and are described below. Also, generic hints are implemented as variable data, so they are added to an object in the same way as other variable data types.

Object Trees

All objects declared in a static tree (e.g., your application's generic tree) should be in the same source file. If they are in different files, then they may be joined into a single tree only by dynamically adding objects from one file as children to objects of the other.

Note that if one file contains a tree of objects, then you may incorporate the whole tree by simply dynamically adding the top object in the file to the main tree. You won't have to add each object individually.

If an object declared in one source file will send a message to an object in another source file, you must include an @extern line in the source file containing the sending object:

@extern object ReceivingObjectName;

The message itself should be sent in the following manner (with variations possible if you will be using @call , passing arguments, or what have you):

optr ROOptr;
ROOptr = GeodeGetOptrNS(@ReceivingObjectName);
@send ROOptr::MSG_DO_SOMETHING(0, 0);

GeodeGetOptrNS() returns the object pointer for the object. In the case of a multiply-launchable program, Goc has no way of knowing which program's @ReceivingObjectName should receive the message--by using GeodeGetOptrNS() , you ensure that the object getting the message will be in the same geode as the object calling the routine.

Declaring Children

If an object is to be part of an object tree, its class (or one of its superclasses) must have at least an @link instance data field as does GenClass . If the object is to be allowed to have children, it must also have a field of type @composite . These allow Goc to automatically and correctly link the tree together.

As described in Defining Instance Data Fields , the @composite field points to the first child of an object, and the @link field points either to the object's next sibling or back to the parent if there is no next sibling. However, all child declaration is done completely in the composite field when using @object . The format is as follows:

<fname> = <childName> [, <childname>]* ;
fname
This is the name of the field defined with @composite in the class definition.
childName
This is the name of an object to be a child of this object. The star symbol indicates that one or more children may be included in the declaration line--they should be separated by commas, and each child must also be declared with an @object declaration.

There are many examples of this in the sample applications. Some simple examples are shown below.

GI_comp = @TicTacPrimary;
GI_comp = @TicTacView, @TicTacGameMenu;
VCI_comp = @TTX1, @TTX2, @TTX3, @TTX4, @TTX5, @TTO1,
         @TTO2, @TTO3, @TTO4, @TTO5;
Declaring Visual Monikers

For an object to have a visual moniker, it must have an instance data field of type @visMoniker as GenClass does (see Defining Instance Data Fields ). If you are in fact working with GenClass' GI_visMoniker field, you might want to consult its description in the GenClass chapter.

Visual monikers may take many forms, and the declaration line changes depending on which form you are using. The form ranges from a simple text field to a complex list of possible monikers based on video resolution. Each of these forms and the declaration line for it is given below.

The following form is used for simple text strings (shown with example):

<fname> = <string>;
GI_visMoniker = "One";
fname
The name of the moniker's instance data field.
string
A string of text enclosed in quotation marks. This string is the visual moniker for the object.

The following form is used for text strings that have a keyboard navigation character. A keyboard navigation character can be a character enclosed in single quotation marks, a numerical constant, or a text string representing some constant defined with #define . If it's a text string, the string is passed on for the C compiler to parse. The form is shown below with an example.

<fname> = <nav>, <string>;
GI_visMoniker = 'V', "View";
fname
The name of the moniker's instance data field.
nav
A keyboard navigation character represented as described above.
string
A text string enclosed in quotation marks.

The following form is used when a list of monikers is required. Most frequently, this form is used when defining an application's icons; one moniker for each potential screen resolution will be included in the list. The form is shown below, along with an example.

<fname> = list { <nameList> };
GI_visMoniker = list {
 @DocDirButtonSCMoniker,
 @DocDirButtonSMMoniker,
 @DocDirButtonSCGAMoniker
}
fname
This is the name of the moniker's instance data field.
nameList
This is a list of resource data chunk names separated by commas. Each chunk can be defined with the @chunk or @visMoniker keyword.

It is possible when declaring a list of visual monikers to have each moniker within a chunk or to declare each moniker with the @visMoniker keyword. If used on its own line, this keyword takes the form

@visMoniker <fname> = <init>;

The fields are the name of the moniker (same as that specified in the moniker list) and the moniker data. The visual moniker will be put in its own chunk just as if the moniker had been declared with the @chunk keyword, but using @visMoniker often is clearer and easier.

Visual monikers and strings presented by a program's UI are often accompanied by localization information. By placing a @localize keyword after a @visMoniker or @chunk construction, you can provide instructions for whoever uses the ResEdit program to localize your program (i.e., to translate its strings into a foreign language). This keyword can use any syntax from the following list:

@localize { <string> <min>-<max> };
@localize { <string> <length> };
@localize { <string> };
@localize <string>;
@localize not;
string
A string of explanatory text. It should describe the moniker or chunk's contents.
min
Minimum length of the moniker text or string.
max
Maximum length of the moniker text or string.
length
Length of the moniker text or string.
not
Keyword specifying that the moniker or chunk is not localizable.
Declaring GCN List Assignments

GEOS employs a "General Change Notification" (GCN) system which allows objects to register for notification of certain types of events. For example, some applications may want to notify for changes in the file system (file deletes, moves, copies, etc.); some objects may require notification each time the selection changes (e.g., a PointSizeControl object needs to know when the user changes a text selection). For further information, see the General Change Notification chapter.

While many objects will add themselves to and remove themselves from GCN lists dynamically, others will need to be placed on certain GCN lists in their definitions. For this, the gcnList keyword (the only one not preceded by the marker @ ) is used. Its format is shown below:

gcnList(<manufID>, <ltype>) = <oname>,+;
manufID
This is the manufacturer ID of the manufacturer responsible for creating the particular list type. It is used to differentiate different manufacturers who may use the same list type enumerator. In many cases, this will be the type MANUFACTURER_ID_GEOWORKS.
ltype
This is the name of the GCN list. Most that you will use are defined by GenApplication and begin GAGCNLT_.... All the list types are defined in the GenApplication chapter in the Objects book.
oname
This is a list of objects that should be on the GCN list. The objects are separated by commas.
Declaring Keyboard Accelerators

A keyboard accelerator acts as a "hot key," invoking the object's default action when pressed. The accelerator character is defined in an instance field declared with @kbdAccelerator as shown in Defining Instance Data Fields . The form of declaration follows.

<fname> = [@specificUI] <mod>* <key>;
fname
This is the name of the @kbdAccelerator instance data field.
@specificUI
This is a Goc keyword that, when used in this declaration, allows your program to use a key combination reserved for the specific UI. This can have undefined consequences and is strongly discouraged.
mod
This is a modifier key. You may put any or all of the modifiers here, separated by spaces. The four modifiers are alt , control , ctrl , and shift .
key
This is either a character enclosed in single quotes (e.g., `k') or a numeric key constant (e.g., C_CTRL_A or 0x57).
Using Default Values

Often an object will be declared with the default values of an instance data field. Other times, especially with generic UI object attributes, an object will have the default values with one or two modifications. In the first case, the instance data field does not need to be addressed at all; the default value will be used if no declaration is made.

In the second case, however, you must use the @default keyword to get the default values. If modifications are made and @default is not used, all default values will be lost. This is normally used only with bitfield-type attributes, and modifications are made with bitwise operators. The use of @default is shown below.

<fname> = @default [<op> [~]<attr>]*;
fname
This is the name of the instance data field.
op
This is a bitwise operator. If adding an attribute to the default, use the bitwise OR operator (|); if removing an attribute, use the bitwise AND operator (&) with the inverse of the attribute (see below). One operator is required for each attribute added or removed. The priorities of the operators are the same as in the standard C programming language.
~
This is the bitwise operator NOT. If removing an attribute using the bitwise AND operator, you should include the NOT symbol in front of the attribute.
attr
This is the name of the attribute being added or removed.

Code Display 5-17 Declaring Objects with @object

    /* This example shows the use of @start, @object, @visMoniker, and @end.
     * It is taken from the TicTac sample application. */
@start AppResource;
    /* The AppResource resource block contains the TicTacApp
     * object and its visual moniker chunk. */
@object GenApplicationClass TicTacApp = {
    GI_visMoniker = list { @TicTacTextMoniker };
    GI_comp = @TicTacPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS,GAGCNLT_WINDOWS) = @TicTacPrimary;
}
@visMoniker TicTacTextMoniker = "TicTacToe";
@end AppResource
@start Interface;
    /* The Interface resource declares TicTac's primary window and other UI
     * gadgetry. Only the GenView from this application is shown. */
@object GenViewClass TicTacView = {
    GVI_content = @TicTacBoard;					/* A relocatable optr field */
    GVI_docBounds = {0, 0, BOARD_WIDTH, BOARD_HEIGHT};
                    /* A Rectangle structure */
    GVI_color = { C_BLUE, 0, 0, 0 };					/* A ColorQuad structure */
    GVI_horizAttrs = @default | GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT
                & ~GVDA_SCROLLABLE;
        /* The NO_LARGER and NO_SMALLER attributes are set in the
         * field, and SCROLLABLE is cleared. The SCROLLABLE attribute
         * is not set by default for the GenView; it is shown here
         * for illustration. */
    GVI_vertAttrs = @default | GVDA_NO_LARGER_THAN_CONTENT
               | GVDA_NO_SMALLER_THAN_CONTENT;
}
@end Interface

GEOS Programming: 4.5 Using Classes and Objects: Sending Messages

@send, @call, @callsuper, @record, @dispatch, @dispatchcall, TravelOption, ObjDuplicateMessage(), ObjFreeMessage(), ObjGetMessageInfo()

Often you will have to send messages to objects throughout the system. You can send messages in several ways, but the two most basic and most frequently used involve the keywords @call and @send .

If a message is being sent across threads, you must be aware of synchronization issues. If the message does not cross thread boundaries, the kernel will link the code directly as if it were a function call. (This is an implementation difference only; you do not have to specify anything different in your code.)

The @send keyword causes the kernel to put the specified message into the recipient's event queue. Messages sent with @send may not give return values and may not take pointers to locked memory as arguments. The sender then continues executing without ever knowing whether the message was properly handled or not.

The @call keyword is used when the message being sent must return information to the sender. It is also used when the message must be handled immediately, before the sender is allowed to continue executing. In essence, the sender is "put to sleep" until the message has been processed, at which time the sender is woken up and may continue executing. If the message sent with @call is not handled (passed up the recipient's class tree and still not handled), it will return as if it had been; no error message will be returned.

The formats for @send and @call are similar. Use them like function calls. Their format is given below:

@send [,<flags>]+ \
        <obj>::[{<cast2>}]<msg>(<params>*);
<ret> = @call [,<flags>]+ [{<cast>}] <obj>::\
            [{<cast2>}]<msg>(<params>*);
flags
This is a list of flags separated by the commas. The allowed flags are shown below.
obj
This is the name of the object to which the message will be sent. It can also be an optr variable.
msg
This is the name of the message being sent.
params
This is a list of parameters, built exactly as it would be for a standard C function call.
ret
This is a variable that will hold the return value, if any. Note that this is valid only with @call because @send does not return anything.
cast
If a message name is put here, Goc will automatically cast the return type to whatever type is returned by cast .
cast2
If a message name is put here, Goc will assume that the message is passed the same arguments as message cast2 .

The flags allowed for these keywords are listed below. They are rarely used but are available.

forceQueue
This flag will cause the message to be placed in the recipient's event queue, even if it could have been handled by a direct call. Do not use this flag with @call .
checkDuplicate
This flag makes the kernel check if a message of the same name is already in the recipient's event queue. For this flag to work, forceQueue must also be passed. Events are checked from first (next-to-be-processed) to last. If the message is a duplicate, it will be dropped; however the replace flag, described below, can change this behavior.
checkLastOnly
This flag works with checkDuplicate , causing it to check only the last message in the event queue.
replace
This flag modifies checkDuplicate and checkLastOnly by superseding the duplicate (old) event with the new one. The new event will be put in the duplicate's position in the event queue. If a duplicate is found but the replace flag is not passed, the duplicate will be dropped.
insertAtFront
This puts the message at the front of the recipient's event queue.
canDiscardIfDesperate
This flag indicates that this event may be discarded if the system is running extremely low on handles and requires more space immediately.

The @call command can also be used within an expression in the same way a function call could. For example, the following conditional expression is valid:

if (@call MyObj::MSG_MYOBJ_TEST()) {
    /* conditional code */
}

The result of the message call will be evaluated in the if statement. Note that this may not be done with @send because it returns nothing.

Nested Message Calls

Because of the way Goc processes message calls, it is impossible to nest messages on a single line. For example, this call is illegal:

@send Obj1::MSG_THATS_PASSED_AN_INT(\
        @call Obj2::MSG_THAT_RETURNS_INT());

Any such call will generate a compile-time error. Instead, you should use temporary variables to break this up into several lines, e.g.:

int i;
i = @call Obj2::MSG_THAT_RETURNS_INT();
@send Obj2::MSG_THATS_PASSED_AN_INT(i);

Sending a Message to an Object's Superclass

Often you may wish to send a message directly to an object's superclass to ensure that default behavior is implemented. Use the @callsuper keyword with the following format:

@callsuper <obj>::<class>::<msg>(<pars>*) [<flgs>+];
obj
This is the object to send the message to, as in @call and @send . The object block must already be locked, and must be run by the current thread of execution. (Usually an object uses @callsuper() to send a message to itself.)
class
This is the class whose superclass should receive the message.
msg
This is the message name.
pars
This is the parameter list, same as @call and @send .
flgs
This is the flags list, same as @call and @send .

When used on a line by itself (with no parameters or return values), @callsuper() passes a received message on to the superclass. This is used quite often when a subclass wants to alter existing behavior rather than replace it.

Encapsulating a Message

By encapsulating messages, you can set up events to be sent out at a later time. An encapsulated message can include the message to be sent, the object it should be sent to, and the parameters that should be passed. Using encapsulated messages can sometimes simplify coding.

Messages can be encapsulated with the @record keyword and later dispatched with @dispatch and @dispatchcall . (Though the use of @record does not necessitate a later @dispatch --there are other uses for an encapsulated event.) In addition, when the event is dispatched, you can override the values set in the encapsulated event to change the destination or the message. You can also cast the return value to another type if necessary. The formats of these three keywords are as follows:

<event> = @record <obj>::[{<cast>}]<msg>(<params>*);
event
The handle of the recorded event, of type EventHandle .
obj
The object set to receive the message when it is dispatched. This field may be set to null if the destination is determined when the message is dispatched.
cast
The name of a message; if this is present, the event will have the same argument types as the specified message.
msg
The message set to be sent when the event is dispatched. This field may be set to null if the message is determined when it is dispatched.
params
The parameter list (same as in @call and @send ) that will be sent with the dispatched message.

You may create a copy of a recorded message by means of the ObjDuplicateMessage() routine. To free it, call ObjFreeMessage() . To discover the Message and destination object associated with a recorded event, call ObjGetMessageInfo() .

The @dispatch keyword is used to dispatch an encapsulated event to its destination. This is similar to @send in that it can have no return values. If the event has return values, use @dispatchcall (below).

@dispatch [noFree] \
        <nObj>::[{<cast>}]<nMsg>::<event>;
noFree
This flag indicates that the event's handle should not be freed after the event has been dispatched. This is useful if the same encapsulated event is to be used more than once.
nObj
This is an override destination. If the destination in the encapsulated event is null, then an object must be set here. Any object set will override that in the encapsulated message. If no override object is desired, set this to null .
nMsg
This is an override message. If set, this message will be sent rather than that in the encapsulated event. If no override message is desired, set this to null . Any override will be sent with the same parameters as set in the encapsulated event.
event
This is the handle of the encapsulated event. This may not be a classed event.

The @dispatchcall keyword works exactly like the @dispatch keyword above except that it allows the use of return values. The sender will be "put to sleep" if necessary while the recipient processes the message and will be "woken up" when the message returns.

<ret> = @dispatchcall [noFree] [{<cast>}] <nObj>::\
                                    <nMsg>::<event>;
ret
This is a variable that will contain the return value of the message.
other parameters
All the other parameters are the same as those in @dispatch .

Using Expressions with Messages

All message-sending keywords described in the previous sections-- @call , @send , @record , @dispatch , and @dispatchcall --can take expressions in place of a destination object's name. Additionally, the @dispatch and @dispatchcall keywords can take expressions in place of the message name. However, if an expression is used for the message, you must use a cast type to make sure Goc knows the return and parameter types. Note, however, that casts in this case use curly braces rather than parentheses.

Casting Message Call and Return Types

Goc allows you to cast a message's pass and return values. This is best explained by example:

{
 int swapInt;
 char c;
 c = @call {MSG_1} object:: {MSG_2} MSG_X(swapInt);
}

In this case, MSG_2 takes an integer argument and MSG_1 returns a char. The casts tell Goc how MSG_X will receive parameters and return results. Goc needs the casts in those cases where MSG_X doesn't appear explicitly (perhaps it has been stored as an integer), and thus Goc would not be able to parse the parameters or return values.

When Goc tries to determine proper parameters and returns, it will look to the following sources when available. When trying to figure out parameters, it will look first for MSG_2, then MSG_X, and MSG_1 last. The first one Goc finds will determine the parameters.

@send and @record don't support return values, but on a @call , Goc will figure out return values by looking at MSG_1, MSG_X, and finally MSG_2.

In this case, Goc will pass to fn's method like MSG_CAST_2 but will return values as MSG_CAST_1 does:

Message fn = GetMessageToCall();
c = @call {MSG_CAST_1} myObj:: {MSG_CAST_2} fn(x);

Now we pass to MSG_B like MSG_CAST_2, but return like MSG_B:

 c = @call myObj:: {MSG_CAST_2} MSG_B(swapInt);

Classed Events and Travel Options

MSG_META_SEND_CLASSED_EVENT, TravelOption

You do not always need to specifically include an object as a destination for your message. In many cases, you may be able to generically address your message using a classed event . A classed event consists of a pre-defined TravelOption enumerated type and an EventHandle recorded using the @record Goc construct. The event in this case does not contain a specific object, but rather an object class.

The TravelOption acts as a navigator, determining along what path the message should be delivered. The object class within the classed event acts as a filter, determining if the object sent the message is an object of that class. If the object first encountered along the path dictated by the TravelOption is not a matching class, the classed event is passed to the next object on that path. When the class finally matches, the message will be handled by that object.

The classed event is originally dispathed from the object by sending MSG_META_SEND_CLASSED_EVENT. This message is usually sent by an object to itself. The travel options available depend on the class(es) of the object receiving the message. That is, the object receiving the message must be a sub-class of one of the following:

  • MetaClass (Usable by all GEOS object classes)
TO_NULL
Indicates that no object is to receive the message. The default case in this case is to destroy the classed event that is dispatched,
TO_SELF
Indicates that the message should be delivered to itself. If it is not handled there, it will be destroyed.
TO_OBJ_BLOCK_OUTPUT
Each GEOS object block contains a default output stored in the object block header.This TravelOption indicates that the message should be delivered to the object block's output optr.
TO_PROCESS
Indicates that the message should be delivered to the application's ProcessClass object.
  • GenClass (Usable by all generic objects)
    For a discussion of the Focus, Target and Model hierarchies, see the Input chapter.
TO_GEN_PARENT
Indicates that the message should be delivered the object's generic parent.
TO_FOCUS
Indicates that the message should be delivered along the object's focus hierarchy, following the focus links from child to child.
TO_TARGET
Indicates that the message should be delivered along the object's target hierarchy, following the target links from child to child.
TO_MODEL
Indicates that the message should be delivered along the object's model hierarchy, following the model links from child to child.
TO_APP_FOCUS
Indicates that the message should be delivered along the focus hierarchy, starting at the root GenApplication object and following the focus links from child to child.
TO_APP_TARGET
Indicates that the message should be delivered along the target hierarchy, starting at the root GenApplication object and following the target links from child to child.
TO_APP_MODEL
Indicates that the message should be delivered along the model hierarchy, starting at the root GenApplication object and following the model links from child to child.
TO_SYS_FOCUS
Indicates that the message should be delivered along the focus hierarchy, starting at the root GenSystem object and following the focus links from child to child.
TO_SYS_TARGET
Indicates that the message should be delivered along the target hierarchy, starting at the root GenSystem object and following the target links from child to child.
TO_SYS_MODEL
Indicates that the message should be delivered along the model hierarchy, starting at the root GenSystem object and following the model links from child to child.
  • VisClass (Usable by all visible objects)
TO_VIS_PARENT
Indicates that the message should be delivered to the object's visible parent.
  • GenApplicationClass (Usable by all Application objects)
TO_PRINT_CONTROL
Indicates that the object should be delivered to the application object's print control object.

To send a classed event, you must first record the event, specifying the message to send and the object class to handle the message. Then you must send MSG_META_SEND_CLASSED_EVENT--usually to yourself-- passing that handle and a TravelOption .

<event> = @record <objClass>::<msg>
@send self::MSG_META_SEND_CLASSED_EVENT(<event>,
                        <TravelOption>);

Code Display 5-18 Sending a Classed Event

/*
 * First record the Classed event. In this case, we want an object of class
 * `MyFooClass' to handle MSG_FOO. We will send this classed event along the
 * target hierarchy. We want to begin the path at the application object, so we
 * will use the TO_APP_TARGET TravelOption.
 */
event = @record MyFooClass::MSG_FOO;
/*
 * Then dispatch the classed event using MSG_META_SEND_CLASSED_EVENT.
 */
@send self::MSG_META_SEND_CLASSED_EVENT(event, TO_APP_TARGET);

Message Shortcuts

All messages, when received, contain three basic parameters: the message number ( message ), the optr of the recipient ( oself ), and a far pointer to the recipient's locked instance chunk ( pself ). This allows several shortcuts and shorthand formats for use within methods:

@callsuper;

When used in a method as above, the @callsuper keyword passes the received message up to the object's superclass. Use this whenever subclassing a message when the default functionality must be preserved.

 <ret> = @call self::<msg>(<params>*);

Any object can send a message to itself using @call and "self" as the destination. The remainder of the command is the same as a normal @call .

 <ret> = @call process::<msg>(<params>*);

Any object can send a message to its Process object with @call and "process" as the destination. (The Process object is the object of class ProcessClass .) The remainder of the command is the same as a normal @call .

 <ret> = @call application::<msg>(<params>*);

Any object can send a message to its Application object (of class GenApplicationClass ) with @call and "application" as the destination. The remainder of the command is the same as a normal @call .

 <ret> = @call @visParent::<msg>(<params>*);

Any object in a visible tree can use @visParent as the destination of an @call command. The message will be sent to the object's parent in the visible object tree. The remainder of the command is the same as a normal @call .

 <ret> = @call @genParent::<msg>(<params>*);

Any object in a generic tree can use @genParent as the destination of an @call command. The message will be sent to the object's parent in the generic object tree. The remainder of the command is the same as a normal @call .

 @send @visChildren::<msg>(<params>*);

Any composite object in a visible object tree (therefore a subclass of VisCompClass ) can send a message that will be dispatched at once to all of its children. Any message sent with @visChildren as the destination must be dispatched with the @send keyword and therefore can have no return value.

 @send @genChildren::<msg>(<params>*);

Any composite object in a generic object tree (therefore a subclass of GenClass ) can send a message that will be dispatched at once to all of its children. Any message sent with @genChildren as the destination must be dispatched with the @send keyword and therefore can have no return value.

In addition to the above shortcuts, you may also pass the optr of an object using @<obj> , where <obj> represents the name of the object. This syntax gets translated by Goc into (optr)&<obj> ; this is similar to using the ampersand (&) to pass a pointer.

GEOS Programming: 4.6 Using Classes and Objects: Managing Objects

In addition to knowing how to declare objects and classes, you need to know how to manage objects during execution. This includes instantiating new objects, deleting objects, saving object state, and moving objects around object trees.

Both the kernel and MetaClass (the topmost class in any class hierarchy) have routines and methods to create, manage, and destroy objects. You will probably not have to or want to use all these routines and methods, but understanding what they do and how they work can help you understand the object system as a whole.

Creating New Objects

ObjDuplicateResource(), ObjInstantiate(), MSG_META_INITIALIZE, MSG_GEN_COPY_TREE

You can introduce objects to the system in four basic ways. Each of these has benefits and drawbacks, and each has an appropriate place and time for use. It is unlikely, however, that you will use all four different means.

Storing Objects in a Resource Block

This is a common and simple way to introduce objects to the system. The Hello World sample application uses this method of creating and loading objects. Resource blocks are contained in your geode's executable file (the .geo file) and are automatically loaded when accessed. These resources may also be tagged for saving to state files automatically.

Setting up an object resource is simply a matter of defining the resource and using the @object keyword to define each object in the resource. The object resource block is automatically created and put in your executable file. Each object defined with @object is allocated a chunk and a chunk handle within the resource block. Because both the chunk handle and the handle of the resource block are known, accessing individual objects in the resource is simple. In essence, when you set up an object resource, you don't need to worry about bringing the objects into the system or shutting them down.

Using a resource for objects has limitations. Objects loaded from resources are always loaded with the same characteristics. This can be a problem if you need to have several different copies of a certain set of objects, and each copy can be changed. In this case, you would duplicate the resource (see below) before accessing the objects within it.

For an example of objects defined by means of declaring them with the @object keyword within an object resource, see Declaring Objects with @object .

To define an object resource, you must know what objects you'll require before your geode is launched. Some complex programs will dynamically instantiate individual objects or entire trees without knowing previously what objects will be required. To do this, you'll need to use ObjInstantiate() (see below) for instantiating individual objects.

Duplicating an Object Block Resource

This is another common method employed by both the User Interface and applications. It provides the simplicity of the object resource model (above) while allowing you to have several different copies of the resource. Thus, it's perfect if you want to use templates for your object blocks (this is what the Document Control object does, as shown in the Tutorial application (see APPL\TUTORIAL\MCHRT4\MCHRT.GOC)).

First, you must set up an object resource in your code file with @start , @end , and @object . In the .gp file, mark the resource discard-only . The objects in such a "template" resource should not be linked to any object outside the block. Generic object branches created in this manner should have their topmost object marked as not usable (~GS_USABLE); this is because it is illegal for a generic object to be usable without having a generic parent. Instead of accessing these objects directly, you should duplicate the resource block. (A resource can not be both duplicated and used directly.) This is done with ObjDuplicateResource() , which allocates a new block on the heap, sets it up properly, and copies the resource directly into it.

You are returned a handle to the new object block, which you can then modify any way you like. Because all the chunk handles of all the objects will be the same as in the source block, you can easily access any object in the duplicate. Once copied, the duplicate objects may be added to your generic tree and then set GS_USABLE. And, by using ObjDuplicateResource() more than once on the same resource, you can have several different, possibly modified versions of the resource at once.

As with using resource blocks, however, you must know the configuration of all your template objects beforehand. You may still need to add new objects to the resource or dynamically create other objects. This is the primary drawback of this method.

Additionally, if you duplicate resource blocks, you should also free them when they're not needed any more. Generic objects in the block should be set not usable and then removed from the tree before the resource is freed. Freeing should be done by sending MSG_META_BLOCK_FREE to any object in the block or by calling ObjFreeObjBlock() . Use of the kernel routine ObjFreeDuplicate() is not recommended as it requires all objects in the block to be thoroughly clean of any ties to the system. ( MSG_META_BLOCK_FREE and ObjFreeObjBlock() ensure that the objects have had adequate time to relinquish these ties first.)

Instantiating an Individual Object

The most complex of these three options, this can provide the flexibility needed for all cases. The actual act of instantiating an object is not difficult or very complex. However, it is time and labor intensive and requires several steps that are not necessary when using object resources. In addition, cleaning up after objects created in this manner is more complex.

To create a new object on the fly, you first must set up a place to put it. To do this, allocate a memory block on the global heap (you can instead use an existing object block, of course) and set it up with the proper flags and header to be an object block. There is no need to lock the chosen block on the heap. The block is now set up to receive the new object.

To actually create the new object, call the kernel routine ObjInstantiate() . This will lock the block on the heap, create a chunk in the object block and zero the instance chunk. If the object is of a class with master parts, the instance chunk will remain uninitialized until the first time a class in the master group receives any message. If the class is a direct descendant of MetaClass , the instance chunk will be immediately initialized to default values with MSG_META_INITIALIZE . If you want to change or add to the default data of this type of object, subclass MSG_META_INITIALIZE ; be sure to call the superclass first. To initialize any master group of an object, send it a classed event that will be handled by a class in that master level.

After calling ObjInstantiate() , the block will be automatically unlocked. An example of instantiating a new object is shown in Instantiating an Object . Generic objects created this way may then be added to a generic tree and set usable. They may be destroyed using MSG_GEN_DESTROY .

Code Display 5-19 Instantiating an Object

/* This sample of code belongs to a sample GenInteraction object (the object would
 * have to be a subclass of GenInteractionClass). It does several things:
 *	1.	It instantiates a new GenTrigger object. The new
 *		GenTrigger will be added to the same object block
 *		containing the GenInteraction handling the message.
 *	2.	It adds the new GenTrigger as a child of the
 *		SampInteraction (the handling object).
 *	3.	It sets the GenTrigger usable and enabled. */
@method SampInteractionClass, MSG_SAMP_INTERACTION_CREATE_TRIGGER {
    optr 		newTrig;
    newTrig = ObjInstantiate(OptrToHandle(oself),
                (ClassStruct *)&GenTriggerClass);
    /* The two parameters are the handle of an object block and the
     * pointer to a class definition. The object block is the same
     * one containing the GenInteraction, whose optr is contained in
     * the standard oself parameter. The class structure pointer points
     * to the class definition of GenTriggerClass. */
    /* Now set the trigger as the last child of the GenInteraction. */
    @call self::MSG_GEN_ADD_CHILD(newTrig, (CCF_MARK_DIRTY | CCF_LAST);
    /* Now set the trigger usable and enabled. */
    @call newTrig::MSG_GEN_SET_USABLE(VUM_DELAYED_VIA_UI_QUEUE);
    @call newTrig::MSG_GEN_SET_ENABLED(VUM_NOW);
}
Copying a Generic Tree

The fourth way to create new objects is by using the message MSG_GEN_COPY_TREE . This, when sent to a generic object in a generic tree, copies an entire generic tree below and including the object into another, pre-existing object block.

This is an easy way to copy generic trees, one of the more common purposes of creating new objects. However, it only works with the generic objects (with a superclass GenClass ). Trees created using MSG_GEN_COPY_TREE can be destroyed with MSG_GEN_DESTROY .

For an example of MSG_GEN_COPY_TREE use, see the SDK_C\GENTREE sample application.

Working With Object Blocks

ObjIncInUseCount(), ObjDecInUseCount(), ObjLockObjBlock(), ObjFreeObjBlock(), ObjFreeDuplicate(), ObjTestIfObjBlockRunByCurThread(), ObjBlockSetOutput(), ObjBlockGetOutput()

Once you have an object block created, either with ObjDuplicateResource() or with the memory routines, there are several things you can do with it. It may be treated as a normal memory block, but there are also several routines for use specifically with object blocks:

ObjIncInUseCount() and ObjDecInUseCount() increment and decrement an object block's in-use count (used to ensure the block can't be freed while an object is still receiving messages). ObjLockObjBlock() locks the object block on the global heap. ObjFreeObjBlock() frees any object block. ObjFreeDuplicate() is the low-level routine which frees an object block created with ObjDuplicateResource() . ObjTestIfObjBlockRunByCurThread() returns a Boolean value indicating whether the calling thread runs a given object block. ObjBlockSetOutput() and ObjBlockGetOutput() set and return the optr of the object set to receive output messages (i.e., messages sent with travel option TO_OBJ_BLOCK_OUTPUT) from all the objects within the object block.

Working With Individual Objects

ObjIsObjectInClass(), ObjIsClassADescendant(), ObjGetFlags(), ObjGetFlagsHandles(), ObjSetFlags(), ObjSetFlagsHandles(), ObjDoRelocation(), ObjDoUnRelocation(), ObjResizeMaster(), ObjInitializeMaster(), ObjInitializePart()

The kernel supplies several routines for working with and modifying individual object chunks and object data. These are all described fully in the Routine Reference Book; most are not commonly used by applications.

ObjIsObjectInClass() takes a class and an optr and returns whether the object is a member of the class. A related message, ObjIsClassADescendant() determines whether a given class is a subclass of another. ObjGetFlags() returns the object flags for a given object instance chunk; ObjSetFlags() sets the flags to passed values. ObjGetFlagsHandles() and ObjSetFlagsHandles() refer to objects by means of handles instead of optrs. ObjDoRelocation() processes any passed instance data fields in the object declared as relocatable; ObjDoUnRelocation() returns the passed relocatable fields to their index values.

ObjInitializeMaster() causes the system to build out a particular master group's instance data for an object. ObjInitializePart() causes the system to build all master groups above and including the passed level. (This will also resolve variant classes.) ObjResizeMaster() resizes a given master part of the instance chunk, causing the chunk to be resized.

Managing Object Trees

ObjLinkFindParent(), ObjCompAddChild(), ObjCompRemoveChild(), ObjCompMoveChild(), ObjCompFindChildByOptr(), ObjCompFindChildByNumber(), ObjCompProcessChildren()

Many objects will be part of object trees. Nearly all generic UI and visible objects exist as members of trees for organizational purposes. Object trees can be useful, powerful, and convenient mechanisms for organizing your objects.

An object tree is made up of "composite" objects--objects which may or may not have children. The distinguishing characteristic of a composite object is that it has one instance data field declared with the @composite keyword and another declared with the @link keyword. The @composite field contains a pointer to the object's first child in the tree, and the @link field contains a pointer to the object's next sibling.

If you set up an object resource block containing composite objects, it's very easy to set up an object tree. Your generic UI objects are declared in a tree with the GenApplication object at its head. Additionally, it's easy to alter an object tree once it's been created. The kernel provides several routines, and MetaClass uses several messages for adding, removing, and moving objects to, from, and within trees.

  • ObjLinkFindParent()
     This routine finds the optr of the calling object's direct parent. The kernel traverses the link fields until it returns to the parent object.
  • ObjCompFindChildByOptr()
     This routine returns the number of the child (first, second, third, etc.) whose optr is passed. The child must exist and must be a child of the calling object.
  • ObjCompFindChildByNumber()
     This routine returns the optr of the child whose number (first, second, etc.) is passed.
  • ObjCompAddChild()
     This routine takes an object's optr and adds it to the caller's list of children. Depending on the flags passed, the child may be inserted in any child position (first, second, etc.).
  • ObjCompMoveChild()
     This routine takes a child object and moves it to a new position. However, it will still remain a child of the calling object. If you want to move the child to be a child of a different object, you must first remove it from the tree altogether and then add it to the other parent.
  • ObjCompRemoveChild()
     This routine removes a child object from the object tree.
  • ObjCompProcessChildren()
     This routine calls a callback routine for each child of the calling object in turn. The callback routine may do virtually anything (except destroy the object or free its chunk or something similar).

By using the above routines, you can fully manipulate any object tree and the objects within it.

Detaching and Destroying Objects

MSG_META_DETACH, MSG_META_DETACH_COMPLETE, MSG_META_ACK, MSG_META_OBJ_FLUSH_INPUT_QUEUE, MSG_META_OBJ_FREE, MSG_META_FINAL_OBJ_FREE, ObjInitDetach(), ObjIncDetach(), ObjEnableDetach(), ObjFreeChunk(), ObjFreeChunkHandles()

While creating objects is actually quite simple, detaching and destroying them can be quite involved. For this reason, GEOS does most of the work for you, and in most cases you won't have to worry about what happens when your application is shut down or saved to a state file. However, if you instantiate individual objects of your own classes, you should be very careful about how your objects are detached from the system and destroyed.

Detaching objects involves severing all of the object's ties with the rest of the system. Destruction of an object entails actually removing the object's instance chunk and related structures, making sure that it has handled all its waiting messages.

Throughout its life, an object is likely to become involved with a number of other things--other objects, drivers, files, streams, the memory manager, the kernel--and each of these things may send the object messages from time to time. The task, when detaching an object from the system, is to sever all the object's ties with the outside world, to make sure that no other entity will ever try to contact the object again.

To those unfamiliar with these problems, they can be overwhelming. However, GEOS takes care of them for you in most situations. All generic and visible objects, all objects in object trees, and all objects that maintain an "active list" will automatically (in nearly all cases) have the detach functionality built in by MetaClass .

The Detach Sequence

When an object receives MSG_META_DETACH , it begins the detach sequence to sever all its ties with other entities in the system. It must first notify all its children and all the objects on its various notification lists that it will be going away (most often, all its children, by association, will also be detached). It then must clear its message queues. Finally, it must acknowledge its detachment to the object that originally sent MSG_META_DETACH . Each of these phases is described in detail below and is implemented by MetaClass . You have to do none of this work unless your object is truly a special case.

Detaching in conjunction with destruction is somewhat intricate because not only must the object notify all other interested parties, but it must also receive acknowledgment of the notice. Otherwise, the notification and some other message could be sent at the same time, and the object could be destroyed before the notification can be handled. (Destruction is discussed in the following section.)

Because any object may be put in the position of being detached and then immediately destroyed, it must send out notification and then wait until all the notices have been acknowledged before continuing with other tasks. The kernel and MetaClass implement a mechanism for this using four kernel routines. Again, you do not need to do this since all classes have MetaClass as their root.

First the object being detached (in its MSG_META_DETACH handler) calls the routine ObjInitDetach() . This tells the kernel that the object is initiating a detach sequence and that the acknowledgment mechanism must be set up. The kernel will allocate a variable data entry to hold a count of the number of notices sent and acknowledgments received.

After this, the object must send a MSG_META_DETACH or its equivalent to each of its children and each of the objects on its "active list." With each notice sent, the object must call ObjIncDetach() , which increments the notice count.

After sending all the notices, the object then calls the kernel routine ObjEnableDetach() . This notifies the kernel that all the notices have been sent and that the object is waiting for the acknowledgments.

Acknowledgment comes in the form of MSG_META_ACK and is received by the object being detached. MSG_META_ACK is handled by MetaClass and will decrement the notice count, essentially saying there are one fewer notices left to be received. When the final MSG_META_ACK is received (setting the notice count to zero) and ObjEnableDetach() has also been called, the kernel will automatically send a MSG_META_DETACH_COMPLETE to the object. This assures the object that it will never receive another message from another entity in the system.

The final step in the detach sequence is acknowledging that the object has been detached. In its MSG_META_DETACH_COMPLETE handler, the object should send a MSG_META_ACK to the object that originated the detach sequence. This will allow that object to continue with its detach sequence if it was involved in one; without this step, only leaves of object trees could ever be detached. This final step is provided in default handlers in MetaClass and is inherited by all objects.

To free a chunk, perhaps a chunk used to store an object, use the ObjFreeChunk() routine. Normally this will free the chunk; however if the chunk is storing an object from a resource, the chunk will instead be dirtied and resized to zero. To free a chunk referenced by handles, call ObjFreeChunkHandles() instead.

The Destruction Sequence

The destruction sequence must be initiated from outside and will begin when the object receives a MSG_META_OBJ_FREE . Often, the MSG_META_OBJ_FREE will be sent by the object to itself.

The destruction sequence consists of three steps: First, the object must clear out its message queues; even though it is detached and can not receive new messages, there may be some left over in the queue (an error if it occurs). Second, it must finish executing its code and working with its instance data. Third, it must free its instance chunk. Each of these steps is described below.

Even though the object has notified the rest of the system that it is going away, it still must flush its message queues of any unhandled messages. These messages could have been received between the time the original MSG_META_OBJ_FREE was received and notification was sent out (due to interrupts or context switching). To clear its message queues, the object must send itself a MSG_META_OBJ_FLUSH_INPUT_QUEUE , which will ensure that any messages in the queues are handled appropriately before the object shuts down. This step is handled automatically by the MSG_META_OBJ_FREE handler in MetaClass . You should never have to send this message, and indeed its use is discouraged.

To the outside world, the second and third steps seem like a single step. However, MSG_META_OBJ_FREE can not simply free the instance chunk after the queues are cleared; it must be able to access the instance chunk until all the method code has been executed. So, MSG_META_OBJ_FREE sends the final message to the object, MSG_META_FINAL_OBJ_FREE , and then exits. MSG_META_FINAL_OBJ_FREE waits a short while and then frees the object's chunk. This ensures that MSG_META_OBJ_FREE has finished and the chunk is not being used by any code.

Possible Pitfalls

If you're not careful, you can cause the detach mechanism to fail by instantiating an object on the fly and saving that object's optr. If the object is then detached and you don't account for the saved optr, you could later send a message to a nonexistent object. This has undefined results and can be nearly impossible to track down.

Note that objects created within resources and by ObjDuplicateResource() will almost always automatically be taken care of by the detach mechanism. Objects you create with ObjInstantiate() are the ones to be careful with.

Saving Object State

ObjSaveBlock(), ObjMarkDirty(), ObjMapSavedToState(), ObjMapStateToSaved()

Object state saving is almost entirely contained within the system. For the most part, only UI objects are saved to state files; however, you can mark other object blocks for saving. State saving is described in full in the Applications and Geodes chapter.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Libraries

If you write more than one application, you may find yourself repeating a lot of effort. You may end up writing the same routines several times, or defining very similar classes for different applications.

For this reason, GEOS lets you define libraries . Libraries are much like applications. However, they don't do anything on their own; instead, they must be loaded by other geodes. They provide applications with routines and object classes. This lets applications share code easily and efficiently.

1 Design Philosophy
2 Library Basics
3 The Library Entry Point
4 Exported Routines and Classes
5 Header Files
6 Compiler Directives

Libraries: 1 Design Philosophy

GEOS libraries are designed to make code-sharing simple and efficient. They allow several different applications to make use of the same routines and classes while using the minimum amount of space.

Conventional libraries are fully included in each application. The usual technique is to write a header file which contains the code for the library's routines. Any application which needs to use the routines can then include this library. This has one main advantage: The code can be written and tested once, and applications can then rely on it to work. However, there is a severe drawback to this approach: every application which uses the library will contain identical code. This is not a problem for non-multitasking environments; if only a single application can run at a time, then there will be only a single copy of each library. In a multithreaded environment like GEOS, however, this is a very inefficient use of resources.

GEOS libraries solve this problem. Each geode specifies which libraries it will use, either at compile-time (in the .gp file) or at run-time (with GeodeUseLibrary() ). The kernel will see to it that the library is loaded when necessary. This means that if a dozen applications are all using the same library, the code needs only be loaded once.

Conventional libraries contain only routines. GEOS libraries, on the other hand, may contain both routines and object classes. There are several advantages to defining a class in a library instead of in an application. First, there is the same code-sharing benefit that routines have. If a class is defined in a library, the heap will contain at most one copy of each of the class's methods, no matter how many applications use objects from that class. There is another advantage as well; all applications which use that class can be sure that they are using objects whose definitions are identical. This makes it possible for applications to send messages to objects owned by other geodes.

Writing a library is very much like writing an application. There are only a few differences, which are covered in this appendix. You should already be familiar with writing applications before you try to write a library.

Libraries: 2 Library Basics

A library is a geode, much like an application geode. However, its behavior is slightly different. In particular, libraries do not have any threads of their own, unless they explicitly create them.

When a geode calls a routine which is exported from a library, the routine is run by the thread which made the call, not by the library's thread. This has several implications. First, it means that a library's response time is not dependent on the number of applications which use the library. An application which uses the library a lot will do so on its own time and may have its priority reduced accordingly. Indeed, a library with many users is likely to perform better than one with few users, since its code will be less likely to be swapped out of the global heap. Similarly, library routines will use the stack of the calling thread; this means that the same routine can be called by several different threads at once, with less danger of a synchronization problem.

Another consequence is that if a library routine allocates memory, that memory will belong to the owning geode. Thus, when the application exits, the memory will automatically be freed; on the other hand, if the library exits before the application does, the memory block will remain. If a library wants to have the block owned by the library geode, it must set the owner explicitly.

Geodes which use a library are said to be its "clients." A client may declare that it uses a library in its .gp file, or it may load the library at runtime by calling GeodeUseLibrary() . One library may be a client of another; in this case, when the first library is loaded, the second will be as well.

A library may have a single special routine, known as its entry point . The kernel calls this routine to inform the library when it is launched or freed, when it acquires a new client, or when a client is unloaded. The entry point routine is described more fully in The Library Entry Point .

A library may export object classes or routines. If a routine is exported, it may be called by any geode which uses the library. If an object class is exported, any geode which uses the library may instantiate objects of that class, and may define a subclass of it.

Every library should have a library header file. This header file contains declarations for all exported routines and classes, as well as definitions of any appropriate macros, constants, structures, etc. Every geode which uses the library will need to include this header file. If the library exports any object classes, the header file should be a Goc header file with the suffix .goh ; otherwise it should be a standard C header file with the suffix .h . The header file is described in more detail in Header Files .

Libraries: 3 The Library Entry Point

LibraryEntry(), LibraryCallType

A library may need to do bookkeeping when it is launched, when a client is attached, or at other times. For this reason, some libraries will have an entry point routine. The entry point routine is called by the kernel; it should never be called by other geodes. Some of the calls are made in the kernel thread, while others are made by a geode's thread. All of the calls are made automatically by the kernel.

An entry point routine must take two arguments. The format of an entry point is shown in A Library Entry Point .

Code Display C-1 A Library Entry Point

Boolean _pascal
    LibraryEntry(LibraryCallType				ty,
            GeodeHandle		client);

When the kernel calls the entry point routine, it passes the following arguments:

  • A member of the LibraryCallType enumerated type. This specifies why the kernel is calling the routine. This type is described below.
  • A geode handle. This parameter is valid only if certain LibraryCallType values are passed.

The entry point should return true if an error occurs; otherwise it should return false (i.e. zero).

LibraryCallType contains the following members:

LCT_ATTACH
This is passed when the library has just been launched. The client parameter is undefined. The call is made in the application's thread.
LCT_DETACH
This is passed when the library is about to be unloaded. The client parameter is undefined. The call is made in the application's thread.
LCT_NEW_CLIENT
A thread has just called GeodeUseLibrary() , or a geode which depends on the library is being launched. The client parameter contains the GeodeHandle of the new client. The call is made in the kernel thread.
LCT_NEW_CLIENT_THREAD
A geode which depends on the library has just spawned a new thread. The client parameter contains the GeodeHandle of the thread's owner. The call is made in the new thread.
LCT_CLIENT_THREAD_EXIT
A thread which uses the library is being destroyed. The client parameter contains the GeodeHandle of the thread's owner. The call is made in the soon-to-be destroyed thread.
LCT_CLIENT_EXIT
A client loaded this library with GeodeUseLibrary() has just called GeodeFreeLibrary() . The client parameter contains the GeodeHandle of the former client. The call is made in the kernel thread.

Sometimes a single action can prompt several calls to the entry point, each with a different LibraryCallType value. For example, suppose FooWrite is launched. This application's .gp file specifies that it uses the BarObj library. At the time FooWrite is launched, BarObj has not been loaded. The kernel will automatically launch BarObj and immediately call the entry point with parameter LCT_ATTACH. The kernel will then call the entry point again with parameter LCT_NEW_CLIENT, passing FooWrite's GeodeHandle . It will then call the entry point once for each FooWrite thread, passing LCT_NEW_CLIENT_THREAD; it will make these calls as each thread is started.

Some libraries will not need to take any actions when the entry point is called; these libraries need not have an entry point routine. On the other hand, some libraries will need to do bookkeeping chores. This is left entirely to the library's discretion.

The entry point should take care not to perform any actions with side effects outside the library. If the entry point allocates memory, it should make sure to make the library's geode the block's owner. Similarly, the entry point should not change the working directory; instead, it should use FilePushDir() and FilePopDir() to make temporary changes to the working directory.

Libraries: 4 Exported Routines and Classes

Writing routines for a library is very much like writing them for an application. Simply export the routine in the .gp file and any geode which uses the library will be able to call the routine.

It is important when writing routines for export to document the routines exhaustively. Remember that the library will probably be used by other programmers; they will rely on the routines to behave exactly as specified. Exported routines should also minimize side effects; for example, it is a bad idea for a library routine to change the working directory without changing it back, unless that is the routine's main purpose.

Most libraries will have a number of routines which are not for export, but are used by routines that are exported. These are simply written normally, and are not exported in the .gp or declared in the header file. Remember that programmers will not see these routines; their side effects should thus be fully documented with the exported routines which call them.

Some libraries will declare classes of objects. In this case, the library should specify in the .gp file that it uses whichever library defines the superclass of the object. For example, if a library defines a subclass of GenClass , it should specify that it uses the UI library. It should then export the new class.

Some libraries will declare classes that are not intended to be used by clients. For example, the Impex library declares ImportExportClass . This class is never instantiated; it contains code and instance data that are used by its subclasses ( ImportControlClass and ExportControlClass ). Such "hidden" classes need not be exported. However, the classes must be fully declared in the header files so the subclasses can be defined accurately and consistently.

Libraries: 5 Header Files

Every library should have at least one header file. This file contains declarations and definitions which are needed by each of the geodes which uses the library.

If a library exports routines but does not export object classes, its header will be a standard C header file. This file should contain declarations of every exported routine. It should also contain the definitions of any macros, constants, structures, etc., which clients might use.

If a library exports object classes, its header will be a Goc header with the suffix .goh . In addition to routine declarations, it must contain the complete declarations of each of the exported object classes, including all the message declarations. A .goh file should begin with the Goc directive " @deflib < libname>", and end with the directive " @endlib ". This ensures that the header will only be included once, even if the code tries to include it several times.

Large libraries may have several header files. For example, a library might declare several similar object classes. It is usually simpler to write a separate header for each class; a client can then include only the headers for classes which it will use. Note that the header for a class should use a Goc @include directive to include the header for that class's superclass.

It is of the utmost importance that the headers be kept in synchronization with the libraries they describe. As a rule, a library will include each one of those headers; that helps to keep all the files compatible. Nevertheless, you must be careful whenever changing a library.

Libraries: 6 Compiler Directives

Libraries have to be compiled slightly differently from applications. Since library routines are run under application threads, they must treat global variables differently than applications do. You must therefore add pragmas to ensure that the library is compiled correctly.

There are several steps to take:

  • The compiler should not to expect the ss register to be the same as ds .
  • The compiler must generate code to load the dgroup segment address into ds at the start of exported routines.
  • The compiler must set up semaphores or other data-synchronization structures for global variables.

Most compiler manuals have a section on compiling dynamically-linked libraries (DLLs) for Microsoft Windows; this section will describe how to set up these conditions. Note that you need only do this if your library will have its own global or static variables. If the library's routines and methods use only local, automatic variables, you need not perform these actions.

When you compile a library, you must pass the argument "-L <libname>" to Goc. Edit your local.mk file to make it insert this flag. For more information about the local.mk file, see Using Tools chapter.

Finally, if your library is written in C or Goc (or if its API is in C, even if some internal routines are written in assembly), you must indicate this by putting the flag c-api on the "type" line of the library's .gp file. This instructs the kernel to pass arguments on the stack (instead of in registers) when calling the entry point routine.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Localization

Localization is the means by which GEOS adapts to foreign environments. The kernel automatically accounts for such country-dependent items as currencies and keyboard layouts. Your geodes can work with foreign character sets and formatting preferences. Using the ResEdit tool, anyone can quickly translate an executable's text into other languages. You should look over this chapter whether or not you're planning on distributing your software internationally. The chapter includes documentation for some functions which you're likely to use regardless.

1 Localization Goals
2 How To Use Localization
3 Preparing for ResEdit
4 International Formats
4.1 Number and Measure
4.2 Currency
4.3 Quotation Marks
4.4 Dates and Times
4.5 Filters for Formats
5 Lexical Functions
5.1 Comparing Strings
5.2 String Length and Size
5.3 Casing
5.4 Character Categories
5.5 Lexical Values
5.6 DOS Text & Code Pages

Localization: 1 Localization Goals

The benefit of Geoworks' experience with previous international products was designed into GEOS from the start. The kernel was designed with the following three goals of localization in mind:

  • Ease of use
    Localization should be easy to use. A cumbersome system would slow programmers and leave them reluctant to use the system.
  • Integration with the kernel
    Functions used by many geodes should be part of the kernel. This allows for shared code, using less memory and making developers' jobs easier.
  • Economy
    Anyone should be able to translate an executable's text without having to understand or access the source code.

If you plan to release your software in other languages, you should code it for easy conversion later. Even if you have no such plans, there are still good reasons for using the localization features of GEOS.

  • Stay Generic and Adaptable
    By using localization code, your programs can work in a variety of environments without modification of source code. Even if you don't translate your geode into another language, users can still use it despite different keyboards and character sets.
  • Respect user's preferences
    Much of localization deals with user preferences which differ from country to country. Regardless of country, users may make changes using the Preferences desk tool and will no doubt appreciate having their preferences maintained.
  • Conform to the system
    You're probably going to end up using localization since much of GEOS is already localized. If your geodes are going to read DOS text files or alphabetize lists, you're going to use localized code; GEOS takes foreign character sets into account. Fortunately, localization functions are easy to use. In those situations where you don't absolutely have to use localized code, usually it's easier to use the localized functions than to write your own.

Localization: 2 How To Use Localization

Your localization workload will vary depending on whether you plan to release your software in other countries. If you don't plan on exporting your software, your only contact with localization will come when you use the common functions that are localized. If you plan on international distribution, you'll be using localization both when writing your geodes and later on, during the translation process, in the following steps:

  • Use provided routines.
    Several GEOS functions have been localized to handle foreign variations, and you should use these whether you plan international release or not. Most of these functions are concerned with "International formats" and character sets.
  • Plan ahead for ResEdit .
    You'll probably use the ResEdit tool if you translate your software into other languages. You'll have to be careful about how you write your programs if ResEdit is to work with them properly. Any text in your geode that the user may see will have to be translated. ResEdit looks only at chunk is in localizable resources. You should also be prepared for the translations of any string to be longer than the original. You may want to provide instructions for translators. You may include these instructions within the body of your source code, close to the strings that will be translated.
  • Use ResEdit .
    The ResEdit tool makes translation easy. It goes through an application's resources and checks all objects for strings and bitmaps, asking the translator to make any fitting changes. The translator never needs to see the source code and doesn't have to know how to program.

Localization: 3 Preparing for ResEdit

ResEdit is a GEOS program which speeds geode translations. This section won't tell you how to use the ResEdit tool but will explain how to write your code if you want ResEdit to work with it correctly.

The important thing to remember is to put all text and bitmaps to be localized in localizable resources. ResEdit looks for localizable objects only in non-code resources. The string or bitmap itself must be stored in a chunk, which you may insure by using either the @chunk or @visMoniker keywords. (If you're using assembly, you should use the LocalDefString macro when setting up localizable strings.) Thus, your application icons are probably stored in the following way:

@start APPSCMONIKERRESOURCE, data
/* Application moniker: */
@visMoniker MyAppSCMoniker = { /* ... */}
@end APPSCMONIKERRESOURCE

At first it might seem surprising that bitmaps should be localized, since pictures are supposed to be a sort of universal language. However, this attitude may seem less strange when you try to distribute software to a culture that finds pictures of yellow arrows offensive.

Your application could keep its strings in a resource in the style illustrated in Storing Strings in Localizable Resources .

Code Display 8-1 Storing Strings in Localizable Resources

@start ErrorStrings, data;
@chunk char NoMonkeyError[] =
    "FTPOOMM failed: No monkey present. Acquire a monkey and try again.";
@chunk char NoPeanutError[] =
    "FTPOOMM failed: No peanuts present. Please insert peanuts and try again.";
@end ErrorStrings;

When working with assembly language, this means that any object stored in a code resource, idata , or udata will be passed over in the search for localizable resources, and thus will not be localizable.

When you know you are storing your strings such that they are localizable, you can provide information which will be visible to the translator when they use the ResEdit tool. Use the @localize keyword ( localize in assembly) to set up this help text. The @localize directive should directly follow the chunk it applies to.

Remember the @localize syntax:

     @localize { <string> <min>-<max> };
     @localize { <string> <length> };
     @localize { <string> };
     @localize <string>;
 @localize not;

Code Display 8-2 Storing Strings in Localizable Resources

@start ErrorStrings, data
@chunk char NoMonkeyError[] =
    "FTPOOMM failed: No monkey present. Acquire a monkey and try again";
@localize "The phrase 'acquire a monkey' appears in another string. Both should \
be translated in the same way.";
@end ErrorStrings
@object GenGlyphClass BossMon = {
     GI_visMoniker = "Boss";
     @localize { "This means hide the game because a boss is coming" 3-6 }; }

Another thing to keep in mind is that when strings are translated, they are likely to grow 33% to 50%. You have to remember to leave room for larger strings, both in memory and in UI. If you use the usual generic UI gadgetry, the geometry manager should stretch the various gadgets to fit any larger names. You should be careful that it doesn't have to stretch so far that components get lost off the edge of the screen. If you decide to get around this problem by constraining the size of some gadgetry, keep in mind that if you don't allow it to stretch, your new strings may not fit.

Localization: 4 International Formats

"International Formats" generally refers to formats which differ from country to country. In GEOS, it signifies those formats which the user can set in the International section of the Preferences desk tool. GEOS provides functions to work with International Formats.

Number and Measure

Currency

Quotation Marks

Dates and Times

Filters for Formats

Localization: 4.1 International Formats: Number and Measure

LocalGetNumericFormat(), LocalSetNumericFormat()
LocalGetMeasurementType(), LocalSetMeasurementType(), LocalAsciiToFixed(), LocalFixedToAscii(), LocalDistanceToAscii(), LocalDistanceFromAscii()

In the USA, 3.142 is a little more than three. In some other countries, it's a little over three thousand. This discrepancy arises from the fact that the decimal and thousands separators are interchanged--in some countries the "." symbol takes the place of the "," symbol when expressing numbers. To allow for local number formats, GEOS provides functions for retrieving and setting the user's preferences.

The number format includes the thousands separator, decimal separator, list separator, and number of decimal digits, as shown in the figure below. The number format also contains a record, NumberFormatFlags , which holds one flag, NFF_LEADING_ZERO . This flag is on if the user wants a leading zero.

There is no overall automatic formatting command for numbers. GEOS supports many internal numeric formats such as fixed point and floating point. If the corresponding math library doesn't contain a formatting command for the number format you're using, you will need to work directly with the localization functions.

The LocalGetNumericFormat() routine returns the numeric format so you may use it to do your own formatting. LocalSetNumericFormat() allows you to reset the user's preferences, though this is inadvisable as it overrides and erases the user's original settings.

These routines use the LocalNumericFormat structure to store the number format data.

The LocalGetMeasurementType() and LocalSetMeasurementType() routines work with the enumerated type MeasurementType , of which there are two: MEASURE_US and MEASURE_METRIC. LocalGetMeasurementType() retrieves the user's current preferred measurement type; LocalSetMeasurementType() sets the user's preference to the passed value. As with any command that resets the user's preference, LocalSetMeasurementType() should never be used by most geodes.

The LocalAsciiToFixed() routine converts an ascii string (e.g. "12.7") to a fixed point number. LocalFixedToAscii() performs the reverse.

To convert between a string like "72 pt" and a number representing a distance, use the LocalDistanceFromAscii() routine. LocalDistanceToAscii() goes the other way, constructing the string corresponding to a given distance. These routines use the DistanceUnit enumerated type to specify the measurement units which are used as shown in the table below.

When allocating strings to use as the targets for Local...ToAscii() routines, allocating a buffer of size LOCAL_DISTANCE_BUFFER_SIZE will be sufficient.

Table 8-1 DistanceUnit types

DistanceUnit value # of Points Display Format Entry Format(s)

DU_POINTS 1.000 ###.### pt ###.### pt

DU_INCHES 72.000 ###.### in ###.### in ###.###"

DU_CENTIMETERS 28.346 ###.### cm ###.###cm

DU_MILLIMETERS 2.835 ###.### mm ###.###mm

DU_PICAS 12.000 ###.### pi ###.### pi

DU_EUR_POINTS 1.065 ###.### ep ###.### ep

DU_CICEROS 12.787 ###,### ci ###.### ci

DU_POINTS_OR_MILLIMETERS and DU_INCHES_OR_CENTIMETERS: special cases

Localization: 4.2 International Formats: Currency

LocalGetCurrencyFormat(), LocalSetCurrencyFormat()

These functions allow you to work with the currency format (see the figure below). This format consists of the currency symbol string, number of currency digits (which may be different from the number of digits for ordinary numbers), and the CurrencyFormatFlags record. The flags in this structure determine whether the currency format includes space around the symbol, a negative sign, or a leading zero. There are flags to determine the relative order of the negative sign, currency symbol, and number. The currency symbol string may be of up to length CURRENCY_SYMBOL_LENGTH including the null terminator. LocalGetCurrencyFormat() also returns the separator characters from the numeric formats, eliminating the need for a separate call to LocalGetNumericFormat() . LocalSetCurrencyFormat() allows the changing of the set preferences. Most parts of the currency format are stored in a LocalCurrencyFormat structure when passed to or returned by these functions.

Localization: 4.3 International Formats: Quotation Marks

LocalGetQuotes(), LocalSetQuotes()

In different countries, people use different types of quotation marks. Germans, for example, might use the » and « characters. Although traditionally, the only computer-generated quotation marks available are " and ", GEOS allows all geodes to use native quotation marks without extraordinary effort.

If a user types in a quotation mark, the keyboard knows which character to print; thus, if you're using ResEdit on a string, it's easy to have your translator type in the correct character. In these cases your geode will have no use for LocalGetQuotes() or LocalSetQuotes() . On the other hand, if your program is going to construct a string which contains quotation marks, use the LocalGetQuotes() routine; it will return the correct characters to use. If you want to reset the preferences, use the LocalSetQuotes() routine.

These routines work with the LocalQuotes structure, which simply holds four characters to use as the four kinds of quotation mark.

Localization: 4.4 International Formats: Dates and Times

LocalFormatDateTime(), LocalParseDateTime(),
LocalGetDateTimeFormat(), LocalSetDateTimeFormat(),
LocalCustomFormatDateTime(), LocalCustomParseDateTime(), LocalCalcDaysInMonth(), LocalFormatFileDateTime()

LocalFormatDateTime() and LocalParseDateTime() allow display and parsing of date and time strings in a variety of formats. Passed the appropriate data, LocalFormatDateTime() will return a string with the data appropriately formatted. LocalParseDateTime() performs the reverse function, reading a string and attempting to extract information from it. The strings these functions use should be long enough to hold a formatted date or time. The predefined constant DATE_TIME_BUFFER_SIZE is the minimum recommended number of characters for one of these strings.

Localization functions use DateTimeFormat s to keep track of what sort of format is desired. If told to parse "5:28" as hours and minutes, LocalParseDateTime() will (depending on the user's time format preferences) return 5 in the hours place and 28 in the minutes place. If told to parse "5/28" as hours and minutes, LocalParseDateTime() would return an error. There are a variety of DateTimeFormats() to choose from, and you should decide which format to use based on what information you want your application to display or read.

Each DateTimeFormat has a string associated with it that contains formatting information. This string consists of the characters of the format, with place-holding tokens for the fields of the date or time. These tokens are delimited by the special character "|". For example, the date/time format string "|HH|:|mm| |ap|" would correspond to the formatted string "10:37 am". The string for each DateTimeFormat can be accessed or altered with the LocalGetDateTimeFormat() and LocalSetDateTimeFormat() commands. The recommended minimum size of a date/time format string is the constant DATE_TIME_FORMAT_SIZE.

If you don't wish to work through one of the standard DateTimeFormat values, you can construct a string containing some date/time tokens and pass it as an argument to LocalCustomFormatDateTime() , along with the appropriate date and time information. To parse a date/time using a custom format, use LocalCustomParseDateTime() .

The LocalFormatFileDateTime() utility routine works in the same way as LocalFormatDateTime() , except it takes a FileDateAndTime argument. It is normally used to format the information returned by FileGetDateAndTime() or TimerGetFileDateTime() .

The LocalCalcDaysInMonth() utility routine takes a month number and returns the number of days in that month.

A number of constants have been set up to aid in the computation of the size of string necessary to hold a date/time or a date/time format string. These constants are DATE_TIME_BUFFER_SIZE, DATE_TIME_FORMAT_SIZE, MAX_MONTH_LENGTH , MAX_DAY_LENGTH, MAX_YEAR_LENGTH, MAX_WEEKDAY_LENGTH , MAX_SEPARATOR_LENGTH, and TOKEN_LENGTH .

Localization: 4.5 International Formats: Filters for Formats

LocalIsDateChar(), LocalIsTimeChar(), LocalIsNumChar()

The generic UI allows for filters on text objects to keep users from typing inappropriate characters. For example, if the user were expected to type in a number, the "~" character would be ignored as that isn't part of the number format. If you want to use generic text objects, you can give them hints to accept only characters from certain formats. If you want to use the filtering functions for your own purposes, they are available globally. When passed a character, these functions will tell whether the character is a legal part of a format. LocalIsDateChar() returns TRUE if the passed character is part of the short date format, DTF_SHORT. LocalIsTimeChar() returns TRUE when passed a number or time separator character. LocalIsNumChar() returns TRUE when passed a number character, a separator, or negative sign.

Localization: 5 Lexical Functions

Since different languages have different alphabets, GEOS has to allow for characters not in the standard English character set. You will find that character values are stored via the Chars enumerated type. Thus, many standard procedures have been localized. Most of these functions have to do with the lexical value of characters, their place in a lexical, or alphabetic, ordering. These lexical values take the place of ASCII standard character values that you may be used to.

Comparing Strings

String Length and Size

Casing

Character Categories

Lexical Values

DOS Text & Code Pages

Localization: 5.1 Lexical Functions: Comparing Strings

LocalCmpStrings(), LocalCmpStringsNoCase(), LocalCmpStringsNoSpace(), LocalCmpStringsNoSpaceCase(), LocalGetLanguage(), LocalCmpChars(), LocalCmpCharsNoCase()

LocalCmpStrings() takes two strings as arguments and says which, if either, comes first alphabetically. LocalCmpStringsNoCase() does the same thing but is not case sensitive. LocalCmpStrings() does a better job of ordering strings than assembly language instructions such as cmps that just compare ASCII values. Since the ASCII value of "a" places it after "Z", it is advisable to use LocalCmpStrings() , which uses the localized lexical values. When comparing two strings for equality, assembly instructions like cmps may be used safely.

The LocalCmpStringsNoSpace() and LocalCmpStringsNoSpaceCase() routines are like the LocalCmpStrings() and LocalCmpStringsNoCase() routines, except that spaces and punctuation marks are ignored. Note that if you are comparing only a certain number of characters that spaces and punctuation marks will not be included in this number.

The sort order of the strings will depend on the language used when sorting them. To find out the current language used for sorting, call LocalGetLanguage() .

There are two assembly routines LocalCmpChars() and LocalCmpCharsNoCase() which allow for the quick lexical comparison of two characters.

Localization: 5.2 Lexical Functions: String Length and Size

LocalStringLength(), LocalStringSize()

There are two routines which determine how long a string is. LocalStringLength() returns the number of characters making up the string, not including the null terminator, if any. LocalStringSize() returns the number of bytes in a string, again not counting any null terminators. Normally these two values will be the same, but any applications which want to support double byte character support will need separate functions to handle those characters that take more than one byte to represent.

Localization: 5.3 Lexical Functions: Casing

 LocalUpcaseChar(),  LocalDowncaseChar(),  LocalUpcaseString(),  LocalDowncaseString()

These functions take a character or string and, if possible, transform it to upper or lower case. They work only on alphabetic characters; other characters will be returned unchanged. These functions can deal with all languages, understanding "È" is the upper case for "è". If you're used to using ASCII, you might be tempted to write your own version of these casing functions by doing arithmetic operations with the lexical or ASCII values of characters, but chances are these will not work with other character sets and should be avoided.

Localization: 5.4 Lexical Functions: Character Categories

 isupper(), LocalIsUpper(),  islower(), LocalIsLower(), isalpha(),  LocalIsAlpha(),  ispunct(), LocalIsPunctuation(), isspace(),  LocalIsSpace(), isprint(),  LocalIsPrintable(),
iscntrl(),  LocalIsControl(), isdigit(),  LocalIsDigit(), isxdigit(),  LocalIsHexDigit(),  isgraph(), LocalIsGraphic(),
isalnum(),  LocalIsAlphaNumeric(),  LocalIsSymbol()

GEOS assembly provides some commonly used character predicates. C users use the standard C functions isupper() , islower() , etc. to access these predicates. Passed a character, each of these functions tells whether that character falls into a certain category. LocalIsUpper() returns true if the character is an uppercase alphabetic character; LocalIsLower() returns true if the character is lower case. LocalIsAlpha() approves any alphabetic character. LocalIsPunctuation() checks for punctuation marks. LocalIsSpace() looks for white space (including spaces, tabs, and carriage returns), and LocalIsSymbol() catches just about everything else. LocalIsControl() detects control characters (e.g. Control-A). LocalIsDigit() returns true for decimal digits. LocalIsHexDigit() approves hexadecimal digits (including the characters a-f, A-F). LocalIsAlphaNumeric() detects alphabetic characters and decimal digits. LocalIsPrintable() returns true when passed a character which takes up a space when printed, corresponding to the standard C function isprint() . LocalIsGraphic() checks for displayable characters, in the manner of the standard C function isgraphic() .

Localization: 5.5 Lexical Functions: Lexical Values

LocalLexicalValue(), LocalLexicalValueNoCase()

If for some reason you want to get the lexical value of a character, these functions will return it. LocalLexicalValue() , passed a character, returns its lexical value. LocalLexicalValueNoCase() does the same thing but ignores case. If the lexical value for one character is lower than another, that character comes first alphabetically. For instance, "a" would have a lower lexical value than "z." This ordering will be valid in any language using the same character set.

Localization: 5.6 Lexical Functions: DOS Text & Code Pages

LocalDosToGeos(), LocalGeosToDos(), LocalDosToGeosChar(), LocalGeosToDosChar(), LocalCmpStringsDosToGeos(), LocalIsDosChar(), LocalCodePageToGeos(), LocalGeosToCodePage(), LocalCodePageToGeosChar(), LocalGeosToCodePageChar(), LocalGetCodePage(), LocalIsCodePageSupported()

There are several functions which work with DOS-format text, converting it to and from GEOS format. Normally, converting DOS text files to GeoWrite documents is handled by Import/Export routines; for those occasions where your code needs to convert DOS text to GEOS format or vice versa, however, these functions should be sufficient. They are localized because DOS has its own version of localization which it implements as "code pages." Each code page is a table of characters. Countries or regions with unusual characters have different code pages. If you haven't worked with code pages up to this point, you probably won't have to start now but can just rely on GEOS to use the native code page correctly. If your geode is going to allow computers from one country to communicate with those of another, you might need to use specialized functions.

Assuming you want to use the code page native to the user's machine, you can use LocalDosToGeos() and LocalGeosToDos() to convert strings from one text format to the other. LocalDosToGeosChar() and LocalGeosToDosChar() work similarly, converting a single character.

LocalCmpStringsDosToGeos() takes two strings, converts one or both of them to GEOS format, then compares them, returning the same values as the regular string comparing functions. Pass a LocalCmpStringsDosToGeosFlags to specify which strings to convert before comparing.

Each of these functions also takes a character as an argument. When a character in one format has no corresponding character in the format it's being converted to, the character argument will be substituted. At the same time a flag will be set to let your geode know that there were some characters that couldn't be converted nicely. LocalIsDosChar() checks a GEOS character to see if it maps into a DOS character. The MIN_MAP_CHAR constant is the smallest value which may need to be mapped. If a character's value is below this constant, then it will be the same under any code page.

If your geode is interested in which code page it's using, the LocalGetCodePage() instruction will tell you what the default code page is. If you want to use a code page other than the native one, the LocalCodePageToGeos() , LocalGeosToCodePage() , LocalCodePageToGeosChar() , and LocalGeosToCodePageChar() behave as their default code page equivalents, except that each takes a code page as an argument to be used in conversion. A number of support code page values are enumerated in the DosCodePage type. To find out whether the user's environment supports a specific code page, call LocalIsCodePageSupported() .

This document is a single-page version of a a multi-page document, suitable for easy printing.


The Clipboard

For an application to survive in the GUI world today, it must support at least the ever-present "Cut, Copy, and Paste" functions to which users have grown so attached. GEOS makes this very simple through the Clipboard and Quick-Transfer mechanisms.

All applications should support and use these mechanisms in order to maintain consistency throughout the system. In addition, these mechanisms provide certain functionality for data transfer that can be easily used.

To understand this chapter fully, you should have a working knowledge of Virtual Memory management as well as some familiarity with the concepts of mouse input. However, the concepts of the Clipboard and quick-transfer mechanisms are simple and may be understood without having read those sections.

1 Overview
1.1 Cut, Copy, and Paste
1.2 Quick-Transfer
2 Transfer Data Structures
2.1 The Transfer VM File Format
2.2 ClipboardItemFormatInfo
2.3 Transfer Data Structures
2.4 Clipboard Item Formats
3 Using The Clipboard
3.1 Registering with the Clipboard
3.2 Managing the Edit Menu
3.3 The GenEditControl
3.4 Handling Cut and Copy
3.5 Handling Paste
3.6 Unregistering with the Clipboard
3.7 Implementing Undo
3.8 Transfer File Information
3.9 Undoing a Clipboard Change
4 Using Quick-Transfer
4.1 Supporting Quick-Transfer
4.2 Quick-Transfer Procedure
4.3 Quick-Transfer Data Structures
4.4 Source Object Responsibility
5 Shutdown Issues

The Clipboard: 1 Overview

The user understands two ways to cut and paste information: The first is to use the Edit menu and the Cut , Copy , and Paste functions. The second is to use the mouse by selecting and dragging text, graphics, or objects with the quick-copy and quick-move functions. For a full description of how these two operations are accomplished, see the user's guide provided in the Geoworks retail products.

Both these functions use similar data structures; a single flag determines whether the operation is a quick-transfer or a normal Clipboard operation. However, the procedures and rules of usage have significant differences.

You should be familiar with the features and uses of both the Clipboard and the quick-transfer mechanism. If you are, you can skip this section; however, if you are not familiar with the features and rules governing the Edit menu and the quick-transfer operations, you most likely will want to read this section.

Cut, Copy, and Paste

Quick-Transfer

The Clipboard: 1.1 Overview: Cut, Copy, and Paste

Even the simplest, text-using applications provide an Edit menu with the Cut , Copy , and Paste options.

The Edit menu works with a hidden data area called the Clipboard . The Clipboard has no visual representation, and it can contain any format of data from text to graphics strings to custom formats defined by applications. The Clipboard's structure and implementation are completely invisible to the user beyond the functions Cut , Copy , and Paste .

The most common use of the Edit menu is within a word processor or draw program. For example, when a user wants to place a graphic from GeoDraw into GeoWrite, or when he wants to rearrange text in his NotePad, he can use the cut, copy, and paste operations.

When a user selects an item such as the word "puddle," the Cut option becomes enabled. This indicates that the user can remove the selection from the document and it will be placed on the Clipboard (anything already on the Clipboard will be replaced with the selection). When nothing is selected, the Cut option is disabled.

In order to enable the Copy option, the user must select an item, just as with Cut . Copying, however, will not remove the selection from the document--it will simply create a copy of the selection and place it on the Clipboard, removing whatever had previously been on the Clipboard.

Pasting is allowed only when something in a compatible data format is already sitting on the Clipboard. The Paste operation copies what is on the Clipboard into a document at the current insertion point (in the case of text, this is where the cursor was last placed); if something is already selected, the selection will be replaced with the pasted material. If there is nothing on the Clipboard, then there is nothing to paste; therefore, the option is disabled.

The Clipboard: 1.2 Overview: Quick-Transfer

The user can copy and move selected items without using the Edit menu; he simply drags the item using the quick-transfer button of his mouse, and the item will be either copied or moved.

There are several rules which govern whether a move or copy is taking place. The user can override these rules with a certain set of keypresses to force either a copy or move. Copy is equivalent to using the Copy and Paste functions in succession, and move is equivalent to using the Cut and Paste functions in succession.

The operation of a quick transfer (move or copy), however, depends on whether the transfer is across documents or internal to a single document. If the transfer is internal, it should become a move. If the transfer occurs across documents, it should become a copy. (Note that, for the purposes of quick-transfer, the term document is used to refer to anything the user will conceptually view as a single data holder--for example, a GeoManager "document" consists of one disk.)

Because not all documents will support a given transfer operation (for example, a text file editor can not receive a GeoDraw graphic), the user is provided with immediate feedback about the type and validity of the transfer.

For example, if a GeoDraw ellipse was selected and a quick-transfer was begun, immediately the mouse pointer would show the transfer as a move operation. As the user moves the mouse outside the GeoDraw document and into another GeoDraw document, the cursor changes to indicate a copy operation. If the user then moves the mouse over a text-only document, a "no operation," or "invalid," cursor would be shown, indicating that the ellipse could not be received.

Some applications may implement an additional feature that provides feedback to the user: modifying the source object as the type of transfer changes. For example, a drawing program may wish to alter the shape or color of an object while it is being quick-moved but leave it normal during a quick-copy or no-operation transfer.

The Clipboard: 2 Transfer Data Structures

Both the Clipboard and quick-transfer mechanisms use similar structures to accomplish data transfer. These data structures are owned and managed by the GEOS User Interface; however, they are accessible to all geodes in the system through a number of routines and messages.

The Clipboard consists of a VM file known as the Transfer VM File. The quick-transfer mechanism uses the same file for its data transfer. Typically, this is a file designated and managed by the User Interface.

In addition, certain formats of data are supported automatically--GEOS text and graphics strings are special formats that are described below. Geodes can create their own specific formats; for example, GeoManager uses a special format for quick-transfer of files between disks and directories.

The Transfer VM File Format

ClipboardItemFormatInfo

Transfer Data Structures

Clipboard Item Formats

The Clipboard: 2.1 Transfer Data Structures: The Transfer VM File Format

The Transfer VM File is a normal VM file, managed by the UI. It contains several components, each of which is accessed through special routines that take care of nearly all the synchronization issues involved with the Clipboard and quick-transfer mechanisms. Because both the Clipboard and the quick-transfer mechanism use this file and its data structures, these structures are detailed here; the following section ( Using The Clipboard ) details how to use them for either the Clipboard or the quick-transfer mechanism.

The Transfer VM File has one header block for the Clipboard transfer item and another for the quick-transfer transfer item. These headers have the same structure, and they contain all the information necessary to work with the file's contents. They contain pointers to and information about each format available for both the Clipboard and the quick-transfer functions. The transfer item's header is shown in ClipboardItemHeader .

Code Display 7-1 ClipboardItemHeader

/* The ClipboardItemHeader structure details what formats, if any, exist in the
 * Transfer VM File for both the Clipboard and quick-transfer mechanisms.*/
typedef struct {
        /* The CIH_owner field is the optr of the object that put
         * the current transfer item into the Transfer VM File. */
    optr			CIH_owner;
        /* The CIH_flags field determines whether the transfer item is used by
         * the quick transfer mechanism or by the normal Clipboard. If this
         * field is equal to the constant CIF_QUICK, then the transfer item
         * belongs to the quick transfer mechanism. If it is any other value,
         * the transfer item belongs to the normal Clipboard. */
    ClipboardItemFlags			CIH_flags;
      /* The ClipboardItemFlags type has one predefined value:
     *	CIF_QUICK		0x4000
     * If this flag does not apply, use TIF_NORMAL, which is 0x0000. */
        /*  The CIH_name field is a 33-character buffer that should contain a
         * null-terminated character string representing the name of the given
         * transfer item. */
    ClipboardItemNameBuffer 			CIH_name;
        /* The CIH_formatCount field indicates the number of formats of the
         * transfer item currently available. For example, this field would be
         * 2 if CIF_TEXT and CIF_GRAPHICS_STRING formats were supported and
         * available in the Transfer VM File. */
    word			CIH_formatCount;
        /* The CIH_sourceID field contains information about the source
         * document of the transfer. Typically, this will be the optr of the
         * parent GenDocument object. */
    dword			CIH_sourceID;
        /* The CIH_formats field is actually an array of ten
         * ClipboardFormatInfo structures, each of which contains the
         * important details about a specific format of the transfer item. */
    FormatArray			CIH_formats;
        /* This field is reserved and should not be used. */
    dword			CIH_reserved;
} ClipboardItemHeader ;
/* The FormatArray type is an array of ClipboardItemFormatInfo structures. The
 * definition of this field is shown here: */
 typedef	ClipboardItemFormatInfo			FormatArray[CLIPBOARD_MAX_FORMATS];
    /* CLIPBOARD_MAX_FORMATS is a constant defining the maximum number of
     * formats allowed for any given transfer item. It is defined as 10. */

The rest of the Transfer VM File consists of VM blocks containing the data that is to be transferred. Each format supported will have its own VM block or series of VM blocks, pointed to by the ClipboardItemFormatInfo structure in the item's header.

The Transfer VM File actually contains two transfer items: One for the Clipboard and one for the quick-transfer mechanism (see the figure below). When calling ClipboardQueryItem() , the requesting geode must specify which item it wants. See Using The Clipboard and Using Quick-Transfer .

The Clipboard: 2.2 Transfer Data Structures: ClipboardItemFormatInfo

This structure contains information about a specific format of the transfer item. The Transfer VM File will support up to ten formats of a given item at once (for both the Clipboard and quick-transfer); each of these formats is stored in its own VM block or VM chain and is represented in the header by a ClipboardItemFormatInfo structure in the array CIH_formats .

The ClipboardItemFormatInfo structure contains other information about the specific format as well as space for two extra words of data. The structure is shown in ClipboardItemFormatInfo .

Each element in the CIH_formats array contains two items: One word represents the manufacturer ID of the geode responsible for the format (useful if a custom format is used within several applications from a single manufacturer), and the other represents the actual format number. To combine these words into a ClipboardItemFormatID record, or to extract either word from the record, use the macros shown after ClipboardItemFormatInfo .

Code Display 7-2 ClipboardItemFormatInfo

/* The ClipboardItemFormatInfo structure contains information about a given
 * format of the transfer item. */
typedef struct {
        /* CIFI_format contains a format ID as well as the
         * manufacturer ID of the geode responsible for the format. */
    ClipboardItemFormatID	 		CIFI_format;
        /* CIFI_extra1 and CIFI_extra2 are extra words provided for
         * format-specific use. */
    word		CIFI_extra1;
    word		CIFI_extra2;
        /* CIFI_vmChain is a VM handle pointing to the first block in the
         * specific format. */
    VMChain		CIFI_vmChain;
        /* CIFI_renderer contains the token of the geode that created
         * the format. */
    GeodeToken		CIFI_renderer;
} ClipboardItemFormatInfo ;

The Clipboard: 2.3 Transfer Data Structures: Transfer Data Structures

Two structures are used with specific routines when dealing with the transfer mechanisms. The ClipboardQueryArgs structure is returned by ClipboardQueryItem() , and the ClipboardRequestArgs structure is returned by ClipboardRequestItemFormat() . Both routines are used during a Paste operation, and both structures are shown in ClipboardQueryArgs and ClipboardRequestArgs .

Note that the CQA_header field is of type TransferBlockID . This type is a dword made up of two word-sized components: a VM file handle and a VM block handle. The three macros listed after ClipboardQueryArgs and ClipboardRequestArgs can be used to create the TransferBlockID argument and extract either of the components from the whole.

Code Display 7-3 ClipboardQueryArgs and ClipboardRequestArgs

/* ClipboardQueryArgs is filled by ClipboardQueryItem(), which is called when
 * determining whether a transfer item exists. */
typedef struct {
    word		CQA_numFormats;		/* the total number of formats available */
    optr		CQA_owner;		/* the optr of the originating object */
    TransferBlockID CQA_header;				/* The combined VM file handle and VM block
                 * handle of the block containing the
                 * ClipboardItemHeader */
} ClipboardQueryArgs;
/* ClipboardRequestArgs is filled by ClipboardRequestItemFormat(), which is called
 * when the application wants to retrieve the current transfer item. */
typedef struct {
    VMFileHandle		CRA_file;		/* The VM file handle of the transfer file */
    VMChain		CRA_data;		/* The handle of the VM chain containing the
                 * transfer item */
    word		CRA_extra1;		/* an extra word of data */
    word		CRA_extra2;		/* another extra word of data */
} ClipboardRequestArgs;

The Clipboard: 2.4 Transfer Data Structures: Clipboard Item Formats

There are several built-in transfer formats that many GEOS applications may support; each of these types is an enumeration of ClipboardItemFormat . Additionally, custom formats can be defined to allow special data structures to be cut, copied, pasted, or quick-transferred without translation into text or graphics strings. The Transfer VM File may contain up to ten formats of a given transfer item. ClipboardItemFormat is shown below.

typedef enum /* word */ {
    CIF_TEXT,
    CIF_GRAPHICS_STRING,
    CIF_FILES,
    CIF_SPREADSHEET,
    CIF_INK,
    CIF_GROBJ,
    CIF_GEODEX,
    CIF_BITMAP,
    CIF_SOUND_SYNTH,
    CIF_SOUND_SAMPLE
} ClipboardItemFormat;

A transfer item of CIF_TEXT format is headed by a TextTransferBlockHeader structure. The text follows this header in the VM chain. A transfer item of CIF_GRAPHICS_STRING format is simply the entire GString stored in the transfer VM file in the VM chain.

Because every format identifier has two components, it is highly unlikely that two different designers will create overlapping custom formats.

The format is defined as a ClipboardItemFormatID type, which is a dword composed of two word-sized pieces. The first piece is a constant representing the format ID number (such as CIF_TEXT or CIF_GRAPHICS_STRING). The second piece is a constant representing the Manufacturer ID number of the manufacturer responsible for creating the format.

To create a custom format, simply define these two items as appropriate (your Manufacturer ID should be set already). Then define your format to fit within the structures used by the Clipboard (shown above).

The Clipboard: 3 Using The Clipboard

ClipboardQueryItem(), ClipboardRegisterItem(), ClipboardDoneWithItem()

To use the Clipboard, your application must have an Edit menu (or a GenEditControl object) and an object which can implement the Cut, Copy, and Paste operations. This object is often the application's Process object or some other coordinating object. This object must be able to do each of the things in the following list:

  • Register with the Clipboard
    In a multitasking, multithreaded system, another application could change the Clipboard's contents. By registering with the Clipboard on application startup, the object will receive notification whenever the Clipboard's contents are changed (including when your application perpetrates the change). Registration is normally accomplished via the ClipboardAddToNotificationList() routine. The GCN notification list GCNSLT_TRANSFER_NOTIFICATION is used.Objects on this list can intercept MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED and MSG_META_CLIPBOARD_NOTIFY_TRANSFER_ITEM_FREED .
  • Maintain the Edit Menu
    In order to provide the user with the Cut , Copy , and Paste features, your application must have an Edit menu. Most programmers will simply include a GenEditControl object to create and maintain the Edit menu. Your clipboard-management object must update the triggers properly whenever the contents of the Clipboard are changed.
  • Handle MSG_META_CLIPBOARD_COPY, MSG_META_CLIPBOARD_CUT
     When the user issues a Cut or Copy order, the object must put the proper data into the Clipboard Transfer VM File.
  • Handle MSG_META_CLIPBOARD_PASTE
     When the user issues a Paste order, the object must query the Clipboard to ensure a proper format is available and then copy the information from the Clipboard.
  • Check out when shutting down
    Each object that registers with the Clipboard must unregister when shutting down. Otherwise, the UI might try to send notification to a defunct object or process, resulting in unpredictable behavior.

Because the Clipboard is constantly in use by many different threads, you must always gain exclusive access to the transfer VM file when you want to use it. After you're done with the transfer file, you should relinquish exclusive access so other threads can continue to use it.

For operations that involve changing the transfer item (cut and copy, for example), you must register your new transfer item with ClipboardRegisterItem() , which also allows other threads to use the file. For operations that involve looking at but not changing the transfer item, you should use ClipboardQueryItem() . Since you have no changes to register, you must later use ClipboardDoneWithItem() to give up your exclusive access to the transfer VM file.

Registering with the Clipboard

Managing the Edit Menu

The GenEditControl

Handling Cut and Copy

Handling Paste

Unregistering with the Clipboard

Implementing Undo

Transfer File Information

Undoing a Clipboard Change

The Clipboard: 3.1 Using The Clipboard: Registering with the Clipboard

ClipboardAddToNotificationList()

Because the Clipboard is a system entity available to all geodes, another thread may change it without your application noticing. The Clipboard therefore provides notification for this case. Because the Clipboard does not know which geodes are interested in its contents, however, applications must register when they first start up.

Calling ClipboardAddToNotificationList() allows an application to add an object to the list of those notified of changes to the Clipboard. This routine should be called by whichever object is going to be handling the Cut, Copy, and Paste operations, typically in the object's MSG_META_INITIALIZE handler. If the object handling the Clipboard operations is the application's Process object, however, it may call ClipboardAddToNotificationList() in its MSG_GEN_PROCESS_OPEN_APPLICATION handler.

The Clipboard: 3.2 Using The Clipboard: Managing the Edit Menu

MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED, ClipboardTestItemFormat()

The Edit menu is simply a normal menu with several standard triggers. Most applications will simply include a GenEditControl object in their UI, add a menu GenInteraction of type GIGT_EDIT_MENU, and leave the Edit menu construction up to them (see The GenEditControl ). Some, however, may want to create their own menu and triggers. A sample of this type of setup is shown in A Sample Edit Menu .

Code Display 7-4 A Sample Edit Menu

/* Other objects, including a GenPrimary as the parent of the GenInteraction, are
 * left out for clarity. */
/* The GenInteraction is the menu in which the three triggers will appear. */
@object GenInteractionClass EditMenu = {
    GI_visMoniker = `E', "Edit";
    GI_comp = EditCut, EditCopy, EditPaste;
    GII_visibility = GIV_POPUP;
}
/* The Cut trigger sends a MSG_META_CLIPBOARD_CUT to the Process
 * object when pressed. */
@object GenTriggerClass EditCut = {
    GI_visMoniker = `t', "Cut";
    GTI_destination = process;
    GTI_actionMsg = MSG_META_CLIPBOARD_CUT;
}
/* The Copy trigger sends a MSG_META_CLIPBOARD_COPY to the
 * Process object when pressed. */
@object GenTriggerClass EditCopy = {
    GI_visMoniker = `C', "Copy";
    GTI_destination = process;
    GTI_actionMsg = MSG_META_CLIPBOARD_COPY;
}
/* the Paste trigger is set up initially disabled. It sends a
 * MSG_META_CLIPBOARD_PASTE to the Process object when pressed. */
@object GenTriggerClass EditPaste = {
    GI_visMoniker = `P', "Paste";
    GTI_destination = process;
    GTI_actionMsg = MSG_META_CLIPBOARD_PASTE;
    GI_states = @default & ~GS_ENABLED;
}

Some Edit menus may also contain other triggers such as "Delete Event," or "Remove Item." These triggers, however, are not standard and must be implemented exclusively by the application.

Two main rules govern the maintenance of the Edit menu:

  • The Cut and Copy triggers are enabled only when some data is selected that may be cut or copied from the document into the Clipboard.
  • The Paste trigger is enabled only when data in a pasteable format exists in the Clipboard's Transfer VM File.

The first rule must be implemented entirely by the application; the Clipboard will not enable or disable the Copy or Cut triggers. The second rule, however, requires that the application be notified whenever the Clipboard's contents change--it is possible, for example, for the application to copy data to the Clipboard, enable its Paste trigger, and have another application then also copy some custom data to the Clipboard. In this case, the original application must disable its Paste trigger if it can not read the data.

Whenever the Clipboard's contents change, the UI will send notification to all objects that have registered with it. The notification will be in the message MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED . Use of this message can be found in the ClipSamp sample application.

Code Display 7-5 Handling Clipboard Changes

/* MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED is sent with no
 * parameters and requires no return value. */
/* The strategy of this message is to first check whether the CIF_TEXT format, the
 * only format supported by this sample application, is available on the Clipboard.
 * If so, the Paste trigger is enabled; if not, the Paste trigger is disabled. */
@method MyClipProcessClass, MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED
{
    ClipboardQueryArgs			query;			/* A structure of arguments */
    Boolean			endisable = FALSE;			/* The trigger is disabled */
    /* Call ClipboardQueryItem() to gain exclusive access to and information about
     * the current transfer item. Pass it zero indicating we're checking the
     * normal transfer item (not the quick-transfer item) and the empty arguments
     * structure. */
    ClipboardQueryItem(TIF_NORMAL, &query);
    /* If there are any formats, then test if CIF_TEXT is one of them. The routine
     * ClipboardTestItemFormat() tests a format against all those available and
     * returns true if it is supported, false if it is not. Use the macro
     * FormatIDFromManufacturerAndType to create the format argument. If CIF_TEXT
     * is not supported, then the enDisable argument is set to TRUE. */
    if (query.CQA_numFormats) {
    if (ClipboardTestItemFormat(query.CQA_header,
        FormatIDFromManufacturerAndType(MANUFACTURER_ID_ME, CIF_TEXT))) {
        endisable = TRUE;
    }
    }
    /* Because we've found out what we need to know, restore the Clipboard with a
     * call to ClipboardDoneWithItem(). This routine takes the transfer item's
     * header and returns nothing; it also relinquishes our exclusive
     * access to the Clipboard and is therefore very important. */
    ClipboardDoneWithItem(query.CQA_header);
    /* Now, if endisable is true, set the Paste trigger enabled. If endisable is
     * false, set it disabled. These operations are accomplished by sending the
     * appropriate message to the trigger object. */
    if (endisable) {
    @call EditPaste::MSG_GEN_SET_ENABLED(VUM_NOW);
    } else {
    @call EditPaste::MSG_GEN_SET_NOT_ENABLED(VUM_NOW);
    }
}

The Clipboard: 3.3 Using The Clipboard: The GenEditControl

As stated above, most applications will simply let a GenEditControl object create and maintain their Edit menu. GenEditControlClass is a subclass of GenControlClass (see the Controllers chapter for usage of controllers in general).

The GenEditControl object can provide triggers and/or tools for Undo, Cut, Copy, Paste, Select All, and Delete operations. These operations must all be handled by your application, of course, just as if you did not use a GenEditControl; using this controller, however, simplifies your UI programming and allows the Edit tools to be used by the GenToolControl.

The features of the GenEditControl are listed below (they are flags of the GECFeatures record type):

GECF_UNDO
This feature adds an "Undo" trigger to the Edit menu. It sends MSG_META_UNDO to the application's target.
GECF_CUT
This feature adds a "Cut" trigger to the Edit menu. When the user activates this, it sends MSG_META_CLIPBOARD_CUT to the application's target.
GECF_COPY
This feature adds a "Copy" trigger to the Edit menu. When the user activates this, it sends MSG_META_CLIPBOARD_COPY to the application's target.
GECF_PASTE
This feature adds a "Paste" trigger to the Edit menu. When the user activates this, it sends MSG_META_CLIPBOARD_PASTE to the application's target.
GECF_SELECT_ALL
This feature adds a "Select All" trigger to the Edit menu. It sends MSG_META_SELECT_ALL to the applications' target.
GECF_DELETE
This feature adds a "Delete" trigger to the Edit menu. When the user activates this, it sends MSG_META_DELETE to the application's target.

The GenEditControl also provides an equivalent set of tools. Each tool executes the exact same functions as the analogous feature; see GenEditControl Features and Tools for the listing of the features and tools as well as the standard settings.

The GenEditControl handles two different notification types: GWNT_SELECT_STATE_CHANGE, sent when the selection state changes, and GWNT_UNDO_STATE_CHANGE, sent when a state change in the Undo status occurs. In both cases, the GenEditControl will appropriately update the Cut, Copy, Paste, Delete, and Undo triggers (the Select All trigger will always be enabled).

Code Display 7-6 GenEditControl Features and Tools

/* This display shows the features and tools records of GenEditControlClass, as
 * well as the default settings and instance data. */
    /* GenEditControlClass features */
typedef WordFlags GECFeatures;
#define GECF_UNDO				0x0020		/* MSG_META_UNDO */
#define GECF_CUT				0x0010		/* MSG_META_CLIPBOARD_CUT */
#define GECF_COPY				0x0008		/* MSG_META_CLIPBOARD_COPY */
#define GECF_PASTE				0x0004		/* MSG_META_CLIPBOARD_PASTE */
#define GECF_SELECT_ALL				0x0002		/* MSG_META_SELECT_ALL */
#define GECF_DELETE				0x0001		/* MSG_META_DELETE */
#define GEC_DEFAULT_FEATURES				(GECF_UNDO | GECF_CUT | GECF_COPY | \
                 GECF_PASTE | GECF_SELECT_ALL | GECF_DELETE)
    /* GenEditControlClass tools */
typedef WordFlags GECToolboxFeatures;
#define GECTF_UNDO				0x0020		/* MSG_META_UNDO */
#define GECTF_CUT				0x0010		/* MSG_META_CLIPBOARD_CUT */
#define GECTF_COPY				0x0008		/* MSG_META_CLIPBOARD_COPY */
#define GECTF_PASTE				0x0004		/* MSG_META_CLIPBOARD_PASTE */
#define GECTF_SELECT_ALL				0x0002		/* MSG_META_SELECT_ALL */
#define GECTF_DELETE				0x0001		/* MSG_META_DELETE */
#define GEC_DEFAULT_TOOLBOX_FEATURES					(GECTF_UNDO | GECTF_CUT | GECTF_COPY |
                     GECTF_PASTE | GECTF_SELECT_ALL |
                     GECTF_DELETE)
    /* GenEditControlClass Instance Data Settings */
    @default GCI_output = (TO_APP_TARGET);						/* Send output to the target */
    @default GI_states = (@default | GS_ENABLED);
    @default GI_attrs = (@default | GA_KBD_SEARCH_PATH);

The Clipboard: 3.4 Using The Clipboard: Handling Cut and Copy

MSG_META_CLIPBOARD_CUT, MSG_META_CLIPBOARD_COPY

Cut and Copy are very similar in function; both put data onto the Clipboard. However, Cut causes the data to subsequently be deleted from the document, and Copy does not.

When the user starts either of these operations, the object that handles them must go through a series of specific steps to load the data into the Clipboard's VM file. (For simplicity of example, this chapter will assume that the Process object will handle all Clipboard operations; this may not be the case in complex programs.)

The steps are simple; each is enumerated below, and edited examples from the sample application ClipSamp are provided in MSG_META_CLIPBOARD_CUT and MSG_META_CLIPBOARD_COPY . Note that these examples do not use the default text object handlers for copy and paste; they treat the entire text flow as the current selection.

  1. Duplicate and attach the data
    You must create a duplicate of whatever data is being loaded into the Clipboard. This step includes allocating new VM blocks in the Transfer VM File with VMAlloc() . As an alternative, you may pre-duplicate the item in memory with MemAlloc() and then simply attach them to the Transfer VM File with VMAttach() .
  2. Complete the transfer item's header
    Fill in all information fields in the transfer item's header block including formats, owner, and flags.
  3. Register the transfer item
    Once the data has been attached and the header completed, you must register the transfer with the Clipboard. The UI will then delete any old data in the Clipboard and replace it with your new transfer item. To register the transfer item, use ClipboardRegisterItem() .

Code Display 7-7 MSG_META_CLIPBOARD_CUT

/* This is the same as MSG_META_CLIPBOARD_COPY except that after copying the
 * data to the Clipboard, it deletes the data from the document.
 *
 * MSG_META_CLIPBOARD_CUT has no parameters and no return value. The strategy
 * is as follows: First, copy the subject data into the Clipboard with
 * MSG_META_CLIPBOARD_COPY. Then, delete the data (which, in this case, is a single
 * memory block containing all the subject text). */
@method ClipSampProcessClass, MSG_META_CLIPBOARD_CUT {
    /* Use MSG_META_CLIPBOARD_COPY to copy the data to the clipboard. */
    @call self::MSG_META_CLIPBOARD_COPY();
    /* Delete the data. The data is contained entirely within a single memory
     * block and is just text. The block is referenced by the memory handle
     * textHandle. If textHandle is not null, then the block may be freed. */
    if (textHandle) {					/* If textHandle is valid, */
    MemFree(textHandle);				/* free the memory block */
    textHandle = 0;				/* and zero the handle. */
    }
    /* Redraw the view area to reflect the deleted text. */
    ResetViewArea();			/* Custom routine to redraw the view. */
}

Code Display 7-8 MSG_META_CLIPBOARD_COPY

/* This message handler goes through all the steps necessary for a Copy operation
 * that works with text data only.
 * MSG_META_CLIPBOARD_COPY has no parameters and requires no return.
 *
 * The strategy employed by this handler is as follows:
 * First, allocate memory for and create the duplicate data block, filling in all
 * the appropriate fields.
 * Next, retrieve the Transfer VM File and attach the data block to the file.
 * Next, allocate and construct the transfer item header VM block.
 * Finally, register and lock in the transfer item to the Clipboard.
 *
 * A single global variable named textHandle refers to the block of text owned and
 * used by the sample application. All other data structures are defined within the
 * message handler. */
@method ClipSampProcessClass, MSG_META_CLIPBOARD_COPY {
    char			*textText;		/* temporary string for the text */
    int			textLength;		/* length of string including null */
    MemHandle			headerMemHandle;		/* handle of ClipboardItemHeader block */
    VMFileHandle 			transferVMFile;		/* VM file handle of Transfer VM File */
    VMBlockHandle 			dataVMBlock;		/* VM handle of attached data block */
    VMBlockHandle 			headerVMBlock;		/* VM handle of attached header block */
    ClipboardItemHeader 			*headerMem;		/* ClipboardItemHeader for the VM file */
    optr			textObj;		/* temporary text object for transfer */
    /* First, lock the text string into memory and get its length, adding one for
     * the null character at the end. Then unlock the text string's block. */
    textText = (char *) MemLock(textHandle);
    textLength = (strlen(textText) + 1);
    MemUnlock(textHandle);
    /* Next, build the transfer item block by creating a temporary text object and
     * copying our text into it. Other formats may simply copy the text directly
     * into a VM block. */
    textObj = TextAllocClipboardObject(ClipboardGetClipboardFile(), 0, 0);
    @call textObj::MSG_VIS_TEXT_REPLACE_ALL_PTR((char *)MemLock(textHandle), 0);
    MemUnlock(textHandle);
    dataVMBlock = TextFinishedWithClipboardObject(
                    textObj, TCO_RETURN_TRANSFER_FORMAT);
    /* Now get the transfer VM file. */
    transferVMFile = ClipboardGetClipboardFile();
    /* Now, allocate and fill in the transfer item header block. */
    headerVMBlock = VMAlloc(				transferVMFile,
                sizeof(ClipboardItemHeader),
                MY_TRANSFER_ID);
    headerMem = (ClipboardItemHeader *)VMLock(transferVMFile, headerVMBlock,
                            &headerMemHandle);
    headerMem->CIH_owner = (optr) (((dword)GeodeGetProcessHandle()<<16) | 0);
    headerMem->CIH_flags = 0;					/* Normal transfer; no flags. */
    (void) strcpy(headerMem->CIH_name, "Sample Text");
    headerMem->CIH_formatCount = 1;
    headerMem->CIH_sourceID = 0;
    headerMem->CIH_formats[0].CIFI_format =
        FormatIDFromManufacturerAndType(MANUFACTURER_ID_ME, CIF_TEXT);
    headerMem->CIH_formats[0].CIFI_vmChain =
                VMCHAIN_MAKE_FROM_VM_BLOCK(dataVMBlock);
    headerMem->CIH_formats[0].CIFI_extra1 = 0;
    headerMem->CIH_formats[0].CIFI_extra2 = 0;
    VMUnlock(headerMemHandle);
    /* Now register the transfer item with the Clipboard. This will actually put
     * the transfer item and its header into the Clipboard. */
    ClipboardRegisterItem(BlockIDFromFileAndBlock(
                    transferVMFile, headerVMBlock),
            0);
}

The Clipboard: 3.5 Using The Clipboard: Handling Paste

ClipboardRequestItemFormat(), MSG_META_CLIPBOARD_PASTE

The Paste operation pulls data off the Clipboard and places it at the insertion point in the application's data. The Clipboard remains unchanged throughout the operation; the data is simply duplicated and passed on to the application.

The steps in handling a MSG_META_CLIPBOARD_PASTE are simple; each is enumerated below, and a sample method for pasting is shown in MSG_META_CLIPBOARD_PASTE .

  1. Query the Clipboard
    First, you must make sure that you have exclusive access to the clipboard item. To this end call ClipboardQueryItem() . You should also call ClipboardRequestItemFormat() to make sure that the present clipboard item is pasteable.
  2. Allocate memory if necessary
    If necessary, allocate the memory into which the transfer item will be duplicated. You can not simply reference handles to the transfer item in the clipboard because the transfer item may be changed by another thread at any time.
  3. Lock the Transfer VM File and grab the transfer item
    Lock the Transfer VM File with a call to ClipboardRequestItemFormat() . Finally, copy the transfer item into your pre-allocated memory.
  4. Unlock the Transfer VM File
    By calling ClipboardDoneWithItem() , relinquish your exclusive access to the Transfer VM File and to the clipboard item itself. The Paste operation can then be completed entirely by your application by assimilating the pasted data and displaying it properly.

Code Display 7-9 MSG_META_CLIPBOARD_PASTE

/* This message handler goes through the necessary steps to grab the transfer item
 * from the Clipboard and copy it into application's memory. This example uses a
 * single global variable called textHandle, a memory handle of the only data block
 * owned by the application. The memory block contains text.
 * MSG_META_CLIPBOARD_PASTE has no parameters and requires no return value. */
@method ClipSampProcessClass, MSG_META_CLIPBOARD_PASTE {
    ClipboardQueryArgs				query;		/* returned by
                         * ClipboardQueryItem() */
    ClipboardRequestArgs 				request;		/* returned by
                         * ClipboardRequestItemFormat() */
    TextTransferBlockHeader				*dataBlock		/* pointer to block header */
    MemHandle				dataBlockMemHandle; /* handle of locked block */
    word				charsAvail;		/* number of chars in block */
    int				textLength;		/* length of text */
    word				transferFlags = 0; /* flags for the transfer
                         * (normal transfer) */
        /* Call ClipboardQueryItem() to be sure that a normal
         * transfer item exists in the Clipboard. */
    ClipboardQueryItem(transferFlags, &query);
                    /* Fills ClipboardQueryArgs structure */
        /* If a transfer item exists, check for a CIF_TEXT
         * version, the only format we support. */
    if (query.CQA_numFormats) {				/* if more than zero formats available */
       if (ClipboardTestItemFormat(query.CQA_header,
                    FormatIDFromManufacturerAndType(
                            MANUFACTURER_ID_ME,
                            CIF_TEXT))) {
    /* A CIF_TEXT version exists. Now we grab that transfer item by calling
     * ClipboardRequestItemFormat(). This routine fills ClipboardRequestArgs,
     * which contains information about the Transfer VM File and the
     * ClipboardItemHeader block in that file. */
        ClipboardRequestItemFormat(FormatIDFromManufacturerAndType(
                    MANUFACTURER_ID_ME, CIF_TEXT),
                    query.CQA_header, &request);
    /* Now we have the VM file handle of the Transfer VM File and the VM block
     * handle of the ClipboardItemHeader structure. From this we can get the
     * data in the data block. To speed things up, we will copy the transfer
     * text directly into our already-allocated memory block; the handle to our
     * memory block is in textHandle. */
    dataBlock = (TextTransferBlockHeader *)VMLock(
                request.CRA_file,
                VMCHAIN_GET_VM_BLOCK(request.CRA_data),
                &dataBlockMemHandle);
    textHugeArray = VMCHAIN_GET_VM_BLOCK(dataBlock->TTBH_text);
    VMUnlock(dataBlockMemHandle);
    /* Since the data is CIF_TEXT, we know the data block is in the format of
     * TextTransferBlockHeader. We get the text by cycling through the
     * format's HugeArray. This code has been taken out of this example for
     * simplicity; you can look at the ClipSamp sample application source
     * code for it. */
    }
    /* After copying the text into our block, we signal we're done with the
     * transfer file. We do this by calling ClipboardDoneWithItem(). After
     * that, we update our view and return. */
    ClipboardDoneWithItem(query.CQA_header);
    ResetViewArea();				/* Routine defined in ClipSamp. */
}

The Clipboard: 3.6 Using The Clipboard: Unregistering with the Clipboard

ClipboardRemoveFromNotificationList()

Because the Clipboard sends notification out to all registered geodes, geodes must "unregister" when they are shutting down. Otherwise, the Clipboard will attempt to send a message to a defunct object, and this can cause problems for the operating system. Therefore, in your MSG_GEN_PROCESS_CLOSE_APPLICATION handler you should make a call to the routine ClipboardRemoveFromNotificationList() , which removes the passed object from the notification list.

The Clipboard: 3.7 Using The Clipboard: Implementing Undo

For the most part, implementation of Undo is left up to the application. This is due to the fact that operations that may be undone are typically very application-specific. The text objects and the Ink object are the only exceptions to this; they provide their own Undo functions in response to MSG_META_UNDO . For more information on Undo and how it works in GEOS, see the MetaClass chapter.

The Clipboard: 3.8 Using The Clipboard: Transfer File Information

ClipboardTestItemFormat(), ClipboardEnumItemFormats(), ClipboardGetItemInfo(), ClipboardGetNormalItemInfo(), ClipboardGetUndoItemInfo(), ClipboardGetClipboardFile()

With the following routines, you can get information about any of the transfer files in use.

ClipboardTestItemFormat()
Given a transfer format, test if the selected transfer item supports that format. Before using this routine, you must first call ClipboardQueryItem() to get the transfer item header.
ClipboardEnumItemFormats()
Return a list of supported transfer formats for the selected transfer item. Before using this routine, you must first call ClipboardQueryItem() to get the transfer item header.
ClipboardGetItemInfo()
Return the source identifier for the transfer item. Before using this routine, you must first call ClipboardQueryItem() to get the transfer item header.
ClipboardGetNormalItemInfo()
Return the VM file handle and VM block handle of the transfer item header for the "Normal" transfer item.
ClipboardGetUndoItemInfo()
Return the VM file handle and VM block handle of the transfer item header for the "Undo" transfer item.
ClipboardGetClipboardFile()
Return the VM file handle of the UI transfer file (the one typically used when copying, cutting, and pasting).

The Clipboard: 3.9 Using The Clipboard: Undoing a Clipboard Change

ClipboardUnregisterItem()

Using ClipboardUnregisterItem() , you can revert one level of clipboard changes. Note that this works for only one level; there is no way to back out more than one change to the clipboard. This routine can not "undo" itself; that is, calling this routine twice in a row will leave the clipboard in a state other than the original state.

The Clipboard: 4 Using Quick-Transfer

An application must understand the Clipboard and its structure before being able to support the quick-transfer feature of the UI. However, because quick-transfer is such a convenient feature for users, every appropriate application should support it. To use the quick-transfer mechanism, you will probably want to understand how mouse input is handled.

The quick-transfer mechanism makes extensive use of the Clipboard's data structures. However, this does not mean that when a quick-transfer is initiated, the Clipboard is altered. Instead, the Clipboard maintains a separate (but similar) data structure within the Transfer VM File.

When a transfer is in progress, the distinction between a normal transfer and a quick transfer is made with the flag CIF_QUICK. When passed to the transfer mechanism's routines, this flag indicates that the quick-transfer item should be accessed and the Clipboard data should remain intact.

Supporting Quick-Transfer

Quick-Transfer Procedure

Quick-Transfer Data Structures

Source Object Responsibility

The Clipboard: 4.1 Using Quick-Transfer: Supporting Quick-Transfer

In order for an application to support the quick-transfer mechanism, it must be able to handle several situations. The list below enumerates all the tasks the application must be ready to perform:

  • Recognize initiation of a move/copy operation
    When the user initiates a quick-transfer, the object under the mouse pointer will be notified the operation has begun.
  • Provide feedback
    Once a quick-transfer is initiated, every object on the screen becomes a potential destination for the transfer. When the mouse pointer moves over an object, that object must indicate what type of operation can be supported, if any.
  • Receive the transfer item
    If the move/copy operation is completed by the user when the mouse pointer is over an object, that object must be able to receive the item and provide feedback to the UI about the status of the quick-transfer.
  • Recognize completion of the operation
    When a quick-transfer is completed, the source object is notified and informed what type of operation took place.
  • Shut off quick-transfer feedback on shutdown
    When an object that can handle quick-transfers is shut down or destroyed, it must remove itself from the quick-transfer notification list. This takes care of any potential synchronization problems between object shutdown and the quick-transfer mechanism.

Applications should also understand the three rules that govern the behavior of a quick-transfer:

  • Transfers within a single document default to move operations.
  • Transfers across documents default to copy operations.
  • Transfers in a format not supported by the destination are "no operation" transfers.

The Clipboard: 4.2 Using Quick-Transfer: Quick-Transfer Procedure

Although applications must handle several situations to support the quick-transfer mechanism, the procedure involved in a quick-transfer is quite simple. The steps of how a quick-transfer operation is performed are outlined below:

  1. The user initiates a quick-transfer
    By pressing the right mouse button (in OSF/Motif), the user initiates a quick-transfer. The UI recognizes the mouse press and sends MSG_META_START_MOVE_COPY to the object under the pointer image.
  2. The source object builds the transfer item
    The object under the pointer image then becomes the "source" of the quick-transfer. It first calls ClipboardStartQuickTransfer() to initiate the quick-transfer mechanism. It then builds the transfer item just as it would if the user had clicked on the Copy trigger in the Edit menu. It then logs the transfer item with the quick-transfer mechanism.
  3. The source becomes a potential destination
    Immediately after logging the transfer item, the source object becomes a potential destination of the quick-transfer. It must immediately provide feedback to the UI indicating whether it can accept the transfer item and whether the operation would be a move or a copy. The feedback is provided by calling ClipboardSetQuickTransferFeedback() . If the source object is a visible object in a GenView, it must also send the message MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER to itself to allow the pointer events to be sent to other objects in other windows (because the GenView grabs the mouse on the press).
  4. The user moves the mouse
    When the user moves the mouse (continuing to hold down the move/copy button), the pointer image may cross over several objects. Each of these is a potential destination and as such must provide feedback similar to that described in (3) above until the pointer moves outside of its bounds. Each object that receives a MSG_META_PTR should check if a quick transfer is in progress by either checking the passed event flags or by calling ClipboardGetQuickTransferStatus() . The object should, in response, provide feedback as to whether it can accept the transfer item or not. It calls ClipboardSetQuickTransferFeedback() with the proper feedback signal.
  5. The user finishes the transfer
    When the user lets up the move/copy button, the object under the pointer image (if any) becomes the destination object (it will receive a MSG_META_END_MOVE_COPY from the UI).
  6. The destination receives the transfer item
    If the transfer item is in a receivable format, the destination will retrieve the item from the Transfer VM File just as if the user had selected the Paste trigger from the Edit menu (except the quick-transfer transfer item is received, not the Clipboard transfer item). The object first checks if it can take the item by calling ClipboardGetQuickItemInfo() on the transfer item. If it can handle the item, it calls ClipboardQueryItem() , grabs the transfer item, and finally calls ClipboardEndQuickTransfer() .
  7. The UI informs the source of the outcome
    After the transfer has been completed by the destination, the UI will send a MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED to the source object, informing it about the final outcome of the operation. Some source objects will change shape, shading, or color during a quick-transfer and must know when the transfer is concluded. If the operation is a quick-move, the source must delete the information or object that was moved.

The Clipboard: 4.3 Using Quick-Transfer: Quick-Transfer Data Structures

The quick-transfer mechanism uses the same structures as the Clipboard. However, there are special data structures that are used exclusively by the quick-transfer mechanism. These data structures are used by individual UI routines and are documented with those routines.

The Clipboard: 4.4 Using Quick-Transfer: Source Object Responsibility

MSG_META_START_MOVE_COPY, ClipboardStartQuickTransfer(),
MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK

When the user presses the move/copy button, the UI sends a MSG_META_START_MOVE_COPY to the object under the mouse pointer. The selected object can be either a gadget run in the UI thread (such as a GenText object) or a process-run visible object within a view.

If the object is a process-run visible object in a view, both the application object and the object under the mouse pointer will receive the notification message. If the object is UI-run, only it will receive the message.

Receipt of this message tells an object to begin a quick-transfer operation. This operation consists of several steps:

  1. Grab the mouse to track it so you can find out when it leaves your object's bounds. (When it leaves, your object is no longer the destination.) Note that only objects need to grab the mouse; if a process is the content of the view, it does not have to.
  2. Provide feedback to the quick-transfer mechanism immediately.
  3. Build and register the transfer item.
  4. Continue providing feedback as long as the mouse is in your bounds.
  5. If desired, provide visual feedback to the user as the type of operation changes when the user moves the mouse pointer.

The reason for providing feedback to the quick-transfer mechanism is simple: to indicate to the user what is going on. By giving information to the quick-transfer mechanism, objects allow the user to be informed immediately what type of operation is in progress--a move, a copy, or nothing at all.

Immediately after your object has grabbed the mouse, it should call the routine ClipboardStartQuickTransfer() . This routine allows the object not only to indicate which type of operation is in progress but also to attach a special graphical region to the cursor (though not required). This allows the application to provide additional information to the user as to what is going on (e.g. GeoManager attaches an image when a file transfer is initiated).

You must also indicate to ClipboardStartQuickTransfer() the object that will receive notification when the transfer has concluded. This is important because when a quick-move has been completed, the source object must ensure that the original copy of the item (usually the source object itself) is deleted.

After calling ClipboardStartQuickTransfer() , the source object should duplicate and register the transfer item with the Transfer VM File. To do this, register the item as you normally would for a cut or copy operation (see Registering with the Clipboard ); however, be sure to use the flag CIF_QUICK to ensure that the normal Clipboard data remains unaffected.

Once the transfer item has been registered, the source object becomes a potential destination and should act as such. However, you may wish to continue to provide source-related visual feedback to the user as long as the quick-transfer is going on: During a quick-transfer, the source will receive MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK each time the mode of the quick-transfer changes. This message will tell the source object what the current mode of transfer is in order for the source to give extra visual feedback to the user. This behavior is not required of the source object but can be beneficial to your application. It is also supplemental to the destination-related feedback that must be provided.

Responsibilities of a Potential Destination

ClipboardGetQuickTransferStatus(), ClipboardSetQuickTransferFeedback(), MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER, MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER

All objects that can potentially receive a transfer item are considered potential destinations during a quick-transfer operation. During the operation, the user will likely move the mouse pointer across the screen, entering and leaving several different potential destinations.

When the mouse first moves over the object, the object will receive a MSG_META_PTR . When your object receives this message, it must provide immediate feedback to the transfer mechanism to indicate whether a move, a copy, or no operation is to be performed (the object should provide this feedback with the understanding that the operation type is what would happen if the transfer were to conclude at that moment).

When the first MSG_META_PTR is received, the object should call the routine ClipboardGetQuickTransferStatus() . This routine returns whether a quick-transfer is in progress; if so, the object should acquire a mouse grab in order to provide feedback until the mouse pointer leaves its bounds.

The object should then check the quick-transfer Clipboard for supported formats. This is done just as with the Clipboard--with the routine ClipboardQueryItem() . If no supported formats are available, the object should provide the "no operation" feedback. However, if one or more is available, the object should determine whether the operation is a move or copy (call ClipboardGetItemInfo() ) and act accordingly.

To provide feedback, the object must call ClipboardSetQuickTransferFeedback() in its method for MSG_META_PTR . This routine sets the mode of the transfer to one of the enumerated type ClipboardQuickTransferFeedback . If the format is not supported, ClipboardSetQuickTransferFeedback() is passed CQTF_CLEAR.

When the mouse has left an object's bounds, the object must relinquish its mouse grab. Either a MSG_META_CONTENT_LOST_GADGET_EXCLUSIVE or a MSG_META_PTR with the UIFA_IN flag cleared will indicate this situation to the object. The former occurs when the mouse has moved onto a window or other object that is obscuring your object, and the latter is a result of the mouse moving outside of your bounds altogether.

At this point, the object must do two things: Reset the mouse cursor and re-transmit the last mouse pointer event.

To reset the mouse pointer, call ClipboardSetQuickTransferFeedback() and pass it CQTF_CLEAR. This will set the default cursor. To re-send the last pointer event received (you must do this because the last one occurred outside your object's bounds and might have been within another object's bounds), you simply have to return the flag MRF_REPLAY when releasing the mouse grab.

If the source object wishes a quick transfer to be able to be carried outside its view, it must send MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER to itself. Process objects acting as a content must send MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER to the GenView.

The object then becomes oblivious to future quick-transfer events until the pointer returns to its window (or unless it was registered for notification of quick-transfer conclusion).

Responsibilities of the Destination Object

MSG_META_END_MOVE_COPY, ClipboardEndQuickTransfer()

If, when the mouse pointer is within your object's bounds, the user releases the Move/Copy button, your object becomes the true destination of the transfer. You will be notified by a MSG_META_END_MOVE_COPY .

Upon receipt of this message, the object should first determine the move/copy/no-operation behavior as above, with one exception: If either of the flags CQNF_MOVE or CQNF_COPY is set for the transfer item, then the user has overridden the normal behavior and the destination object should respond with the appropriate operation.

After determining the proper action, the object should retrieve the transfer item (as it would from the Clipboard), passing one of CQNF_MOVE, CQNF_COPY, or CQNF_NO_OPERATION. This flag will cause the proper notification to be sent to the transfer's source and allow it to complete its actions properly. To finish the transfer, the object should call ClipboardEndQuickTransfer() .

Getting More Information

In addition to the routines above, you can use one other to retrieve information about a quick-transfer item. ClipboardGetQuickItemInfo() returns a set of handles for the transfer VM file and the file's header block.

When the Transfer Is Concluded

MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED

After the transfer has concluded, the original source of the transfer will receive MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED if it requested notification when it registered the original transfer. This message will be accompanied by a ClipboardQuickNotifyFlags record indicating what type of operation the transfer ended up being. The source object should then follow the rules of quick transfer and act appropriately (e.g. delete the source object on a quick-move operation).

The Clipboard: 5 Shutdown Issues

ClipboardClearQuickTransferNotification(), ClipboardAbortQuickTransfer(), ClipboardRemoveFromNotificationList()

It is possible for a quick-transfer source object to shut down (be destroyed) before the completion of a quick-transfer operation. If the object registered to be notified of transfer completion, it must un-register as it is shutting down. This is done with ClipboardClearQuickTransferNotification() or ClipboardAbortQuickTransfer() .

Additionally, any application that registers with the Clipboard must un-register when it is shutting down. This is done with the routine ClipboardRemoveFromNotificationList() .

This document is a single-page version of a a multi-page document, suitable for easy printing.


Generic UI Controllers

Many applications will create their menus and dialog boxes from scratch, using various GenInteraction and GenTrigger objects. Several system components, however, use a standard base format across all applications. For these components, GEOS provides a number of controller objects.

A controller object is one that provides UI gadgetry to control a certain set of features. For example, the GenViewControl object provides all the UI--including menus, dialogs, and tool bars, if appropriate--to alter a GenView's scaling and other features. Many libraries such as the text library provide controller objects that make the use of the library almost trivial.

To make inclusion of these controllers even easier, the UI offers a GenToolControl; this object provides all the functionality and UI gadgetry to allow the user to configure his own toolbars and menus from the tools provided by a controller.

Finally, developers writing libraries or suites of applications may wish to create their own controller classes. This is neither difficult nor complex, though it will be done primarily by programmers writing libraries for use by many applications.

This chapter describes what controllers are and how they work. It also discusses which controllers are available in the system, as well as the GenToolControl object and how it's used. Finally, it details how to create your own controller classes. In order to fully understand this chapter, you should be familiar first with the generic UI, menus and dialog boxes, and the General Change Notification mechanism.

1 Controller Features and Functions
1.1 Controller Features
1.2 How Controllers Work
1.3 Using Controllers
2 Standard Controllers
3 Using Controllers
3.1 Using a Basic GenControl Object
3.2 Using Tools
4 Creating Your Own Controllers
4.1 GenControlClass Instance Data
4.2 Subclassing GenControlClass
4.3 Advanced GenControlClass Usage
5 GenToolControlClass
6 GenToolGroupClass
7 Other Controllers
7.1 ColorSelectorClass
7.2 GenPageControlClass
7.3 The Float Format Controller

Generic UI Controllers: 1 Controller Features and Functions

Controller objects let you include a library's features in your applications with limited work. The controller provides the basic UI gadgetry and feature management functions so you don't have to.

Every library has a set of features that will be used by all or most of the library's users. For example, applications which use the text objects (GenText and VisText) are likely to allow the user to alter justification, font, and text style. Rather than forcing every application to create its own menus and dialogs and lists for managing these features, the text library exports a number of controllers that can be included in the application's UI; these controllers provide and manage the menu items and lists for the user. They also interact directly with the text objects to change the attributes affected.

Controller Features

How Controllers Work

Using Controllers

Generic UI Controllers: 1.1 Controller Features and Functions: Controller Features

Controller objects provide the following features and benefits to application and library programmers:

  • Ease of use
    Controllers are intended to be "plug-and-play" objects. Programmers simply include the controller to get all the appropriate UI gadgetry.
  • Consistency of appearance
    All applications that use a particular controller will appear consistent to the user. The controller knows the appropriate structure of its menus and dialogs, and all such controllers will appear the same to the user, increasing the application's ease of use.
  • Automatic tool management
    By using controllers along with a GenToolControl object, an application can allow the user to configure his own tool set. Controllers can have their UI placed along a display's edge, in the normal menus, or in a floating tool box; the GenToolControl allows users to decide where the tools will appear without affecting the application at all.
  • Extendability of features
    Any application that uses a controller can automatically include future features of a library. For example, if the GenView object were upgraded in the future to allow new features, the GenViewControl object would likely also be updated. Any application including the GenViewControl would automatically get the new UI and features without recompilation. Applications that do not use controllers will not automatically gain the benefits of upgraded libraries.

The GenControl is most useful for simple controllers. It is based on core UI objects (GenTrigger, GenInteraction, etc.) and is subclassed from GenInteractionClass . Applications that will have extremely complex controller-type functions may want to write their controllers from scratch--this is discussed in Creating Your Own Controllers .

Generic UI Controllers: 1.2 Controller Features and Functions: How Controllers Work

When you use a controller object, you are really using three different types of objects: The controller itself, the UI gadgetry it creates and manages, and the controlled object (sometimes called a "data item") it acts on. For the most part, you will not have to understand these relationships unless you are creating your own controllers with GenControlClass .

To explain how a controller works with both of these other objects, this section uses a simple application that uses a single controller. The controller used is a PointSizeControl object, exported by the text library and used with the GenText and VisText objects; in this example, it interacts with two GenText objects. The entire source code of the application's psctext.goc file is given in A Sample Controller Application (psctext.goc) . This code display does not show the psctext.gp file because it is similar to Hello World's parameters file.

A screen dump of the application is shown below and the entire object tree declared in the application is shown above. The controller object (PSCTSizeControl) declares and sets up the entire menu structure of the Sizes menu, so the menu items do not need to be declared in the application's .goc file.

The interaction between the controller, the text objects, and the controller's UI objects is simple. When the controller is first loaded in from the application's Interface resource block, it also loads in the associated UI gadgetry for the Sizes menu. The menu items are added as children of the controller without the programmer having to do anything extra.

The data item objects (in this example the GenText objects), when loaded or created, create and set up special General Change Notification (GCN) lists. In the PSCText sample application, the text library sets up a GCN list for the notification type GAGCNLT_APP_TARGET_NOTIFY_CHAR_ATTR_CHANGE. When changes to the text object occur, the text object will send a message to this GCN list.

The controller object knows inherently which GCN lists it should be added to. When it is loaded, it adds itself to these lists. In the sample application, the PSCTSizeControl object will add itself automatically to the GCN list for GAGCNLT_APP_TARGET_NOTIFY_CHAR_ATTR_CHANGE.

The interactions between the three components are summarized in the figure below. The application programmer needs to know very little about the interactions of these components; in fact, unless you are creating your own controller objects, you can get away with knowing only a very few things. Building your own controller is detailed in Creating Your Own Controllers , and the basics of controller use, along with the source code for psctext.goc , are shown in Using Controllers .

Generic UI Controllers: 1.3 Controller Features and Functions: Using Controllers

As stated above, for an application to use a controller (or several controllers), the application programmer needs to know very little about the controller itself. A Sample Controller Application (psctext.goc) shows the entire code for the PSCText sample application; note that the programmer has to add no code (just object declarations) to create two editable text objects which can have selectable and settable point sizes.

The application programmer must do two essential things to use a controller object: First, you must include and set up the controller properly. In the example application, the PSCTSizeControl object is a menu; it could easily have been set up as GIV_DIALOG (typically, though, this particular controller is implemented as a menu). Because GenControlClass is a subclass of GenInteractionClass , you can set up the controller exactly like any other GenInteraction.

Second, you must set up your data objects to interact with the controller. The data objects should be written for this automatically; for example, the GenText objects automatically send the proper notification to the GCN mechanism to ensure that all appropriate controllers are notified of all changes. Note, however, that the GenText objects have to be set targetable before the controller can operate on them. Some data objects will be set up to work with their controllers automatically; others may need to have certain attributes set. The GenText objects, for example, are not by default targetable; the controller, however, sends its messages to the application's target object. If the GenTexts are not targetable, they will never gain the application's target and therefore will never react to the controller's requests.

To use a particular controller, you should learn about how that controller's data object needs to be set up. To help you with this, Standard Controllers lists the various controllers included in the system and where they are documented.

Code Display 12-1 A S ample Controller Application (psctext.goc)

@include <stdapp.goh>
@include <ui.goh>
@include <Objects/Text/tCtrlC.goh>
/* The PSCText application's process class runs the application's primary
 * thread. For a description of GenProcessClass, see Hello World. */
@class PSCTextProcessClass, GenProcessClass;
@endc
@classdecl PSCTextProcessClass;
@start AppResource;
    /* The PSCTextApp object defines the application object for the
     * application. For full information, see Hello World.
     * The controller is also placed on the application's self-load-options
     * GCN list to ensure that it loads its options properly when returning
     * from saved state. This is shown below in the second of the two
     * GCN list declarations. */
@object GenApplicationClass PSCTextApp = {
    GI_visMoniker = "Point Size Control Sample Application";
    GI_comp = @PSCTPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS,GAGCNLT_WINDOWS) = @2PSCTPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS,GAGCNLT_SELF_LOAD_OPTIONS) = @PSCTSizeControl;
}
@end AppResource
@start Interface;
/* The PSCTPrimary object serves as the primary window of this sample application.
 * Its children are the Point Size Control object and the two GenText objects. */
@object GenPrimaryClass PSCTPrimary = {
    GI_comp = @PSCTSizeControl, @PSCTopTextObj, @PSCBotTextObj;
    HINT_SIZE_WINDOW_AS_DESIRED;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
/* The PSCTSizeControl object is the controller that provides all the point size
 * functionality. It will automatically create a menu called "Sizes" (due to the
 * GI_visMoniker) and all the entries of that menu.
 * This controller will work on whichever of the two GenText objects
 * (PSCGTopTextObj and PSCBotTextObj) is set the target; the controller's UI
 * objects (the Sizes menu) send their messages directly to the target via the
 * TravelOption TO_TARGET. If a point size is selected as the user's first action,
 * it will work on PSCTopTextObj because that is set up as the default target. */
@object PointSizeControlClass PSCTSizeControl = {
    GI_visMoniker = 'z', "Sizes";					/* Give the controller a name */
    GII_visibility = GIV_POPUP;					/* Make the controller a menu */
}
/* These two GenText objects are simple; they use only the defaults plus the
 * items shown here. Both must be set targetable (GA_TARGETABLE) to be included
 * in the target hierarchy; this is necessary when using controllers because of
 * the note in the above comment. The PSCTopTextObj is made the default focus
 * (to get keyboard input) and the default target (for controller operation). */
@object GenTextClass PSCTopTextObj = {
    GI_attrs = @default | GA_TARGETABLE;
    /* Initially, this text object uses the VisTextDefaultSize VTDS_12
     * (12 pts) and the VisTextDefaultFont VTDF_URW_ROMAN. You can use
     * the PointSizeControl object to change this point size. */
    ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR =					((VTDS_12 << VTDCA_SIZE_OFFSET) |
                     VTDF_URW_ROMAN);
    /* Set the font mapping to none to turn off the defaults. */
    HINT_DEFAULT_FOCUS;
    HINT_DEFAULT_TARGET;
}
@object GenTextClass PSCBotTextObj = {
    GI_attrs = @default | GA_TARGETABLE;
    ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR =					((VTDS_12 << VTDCA_SIZE_OFFSET) |
                     VTDF_URW_ROMAN);
}
@end Interface

Generic UI Controllers: 2 Standard Controllers

This section lists the various controller classes exported by system objects and libraries. Many are part of the GEOS UI; others are part of particular libraries. Some are system-wide (such as the ColorSelectorClass); others are object-specific (such as the InkControllerClass).

All of these controllers are set up to work with GenToolControlClass and GenToolGroupClass . When a GenToolControl object is also included in an application, users may set up menus and floating toolboxes however they like with the various tools from the controller's lists. For more information on the GenToolControl and how it is used, see Using Tools and GenToolControlClass .

  • GenToolControlClass
     This controller class provides the UI for the user to select where other controller tools appear. Controller tools must be specified by the individual controller classes; GenToolControlClass simply manages them according to the user's specifications. Individual tools may be placed in a floating tool box, in an application's tool bar, alongside the active display, alongside the primary display, or other places. GenToolControlClass is described in full in Using Controllers .
  • GenEditControlClass
     This controller class provides the Edit menu with the Undo, Cut, Copy, Paste, Select All, and Delete triggers. This controller will cooperate fully with most system objects such as the text, graphic object, spreadsheet, and Ink objects. The GenEditControl sends out MSG_META_CUT , MSG_META_COPY , etc., to these objects. GenEditControlClass is described in the Clipboard chapter.
  • GenViewControlClass
     This controller class provides a View menu with the functions for changing a GenView's scale, paging, scrolling, aspect ratio, and other features. It is described in full in the GenView chapter.
  • GenDisplayControlClass
     This controller class provides the UI for a multiple display interface. It works with the GenDisplayGroup and GenDisplay objects and is described in full in the GenDisplay chapter.
  • GenDocumentControlClass
     This controller class provides all the functions of the File menu including New, Open, Use Template, Close, Save, Save As, and Revert. It also interacts with the GenDocument and GenDocumentGroup objects to manage how the user interacts with documents in general. GenDocumentControlClass is described in full in the Documents chapter.
  • GenPageControlClass
     This controller class provides the UI for a user to go to a previous, next or specified page. It is described in full in GenPageControlClass .
  • PrintControlClass , PageSizeControlClass
     Both of these controller classes are exported by the spool object library. Both are described in full in the Printing chapter.
  • ImportControlClass , ExportControlClass
     These controller classes provide the UI and functions necessary for the user to select files for importing and exporting. They are exported by the Impex library are described in full in the Impex chapter.
  • InkControlClass
     This controller class provides the tool interface for working with Ink-related tools. It is exported by the pen library and is described in full in the Pen Object library chapter.
  • ColorSelectorClass
     This controller class provides the UI for a user to set a color using either the palette index or a set of RGB values, to set a draw mask, and to set an area fill pattern. ColorSelectorClass is described in ColorSelectorClass .
  • StyleSheetControlClass
     This controller class provides the UI and functions necessary for a user to define and change an application-specific style type and apply style changes. It works primarily with the Text library and graphic object library.
  • FloatFormatClass
     This controller class provides the UI and functions to allow the user to format numerical values into text (for example scientific notation, dates and times, etc.) This controller is described in The Float Format Controller .
  • Various Text Controller Classes
    The text library exports a number of controllers that are specific to text operations. These classes are described in the Text Object chapter.
  • Various Ruler Controller Classes
    The ruler library exports a number of controllers that manage the user's interactions and control over the ruler objects. These controller classes include RulerTypeControlClass , GuideCreateControlClass , and RulerGridControlClass . They are described in full in the Ruler chapter.

Generic UI Controllers: 3 Using Controllers

There are essentially two ways to use controller objects: The first, and the simplest, is to simply include an object of an existing controller class and the data object for which it's designed (e.g. using a PointSizeControl object with a GenText object). The second, and quite more complex, is to create either your own data objects or your own controllers.

This section describes the basic components of a controller object on the most general level--how to include a pre-existing controller along with its data object. For full information on how the GenControlClass works and how a data object interacts with it, see Creating Your Own Controllers . This section focuses on how you can use a controller object; nearly all of this will be academic if you use a GenToolControl object in your application.

Using a Basic GenControl Object

Using Tools

Generic UI Controllers: 3.1 Using Controllers: Using a Basic GenControl Object

To use a typical controller, you have to set up its instance data appropriately and set up the generic tree properly. This section describes the basics of GenControlClass instance data; for full information on GenControlClass , see Creating Your Own Controllers .

Every controller has a default set of features and a default set of tools. When you use a controller in your application, you can use the default configuration, the configuration appropriate to the application's user level, or a specific configuration. Most applications will want to use the second option; the controller queries the GenApplication object for its GAI_appFeatures record and determines from that which of its features and tools should be active.

Controllers can be manifested in any way the specific UI determines appropriate; three main ways, however, are menus or submenus, floating tool boxes (dialogs), and groupings along a tool bar in a window. These three modes correspond to the three manifestations of a typical GenInteraction object: GIV_POPUP (menus), GIV_DIALOG (dialogs and tool boxes), and GIV_SUB_GROUP (groupings of other generic objects). If you use a GenToolControl, you can let the user decide how the controller is displayed; otherwise, you will have to set it manually (as in the samples previously).

Every controller also has two sets of UI objects: The first set represents the UI objects used for menu entries (when the controller is in GIV_POPUP mode). The second set represents the tools that appear in a tool box (GIV_DIALOG mode) or tool bar (GIV_SUB_GROUP mode). Tools are almost always functionally redundant to the "menu" feature set. Because the tool and the menu UI resources contain different objects, the UI objects can exist in any combination of interactable states--for example, a particular feature could be in the menu only, in the tool box only, in both, or in neither. The feature can not, however, be in both a tool bar and a tool box at the same time because the set of tools can be grouped in only one location.

Using Normal Features

ATTR_GEN_CONTROL_REQUIRE_UI, ATTR_GEN_CONTROL_PROHIBIT_UI

GenControlClass has no controller features of its own; instead, each controller class must define the features it supports in both the menu implementation and the tool implementation. Any controller object is free to determine which of the features it will support and which it will not.

To set individual features on or off for a controller object in your application, use the vardata fields ATTR_GEN_CONTROL_REQUIRE_UI and ATTR_GEN_CONTROL_PROHIBIT_UI ; these specify which features will be on and which will be off. For listings of a controller class' features, you must see the description of the individual class.

The example in Declaring a Controller's Features extends the example from earlier in the chapter to turn on only the 10-, 12-, and 24-point as well as the "Larger" and "Smaller" features. It turns off all other features.

Code Display 12-2 Declaring a Controller's Features

/* This example is based on that of  A Sample Controller Application (psctext.goc). It shows what
 * would change in order to turn on only the 10-, 12-, and 24-point as well as the
 * "Larger" and "Smaller" features. Note that only the Controller object must
 * be altered. */
@object PointSizeControlClass PSCTSizeControl = {
    GI_visMoniker = 'z', "Sizes";					/* Give the controller a name */
    GII_visibility = GIV_POPUP;					/* Make the controller a menu */
    /* The following attribute defines which of the controller's
     * features are to be supported. These menu items will appear
     * in the controller's Size menu. */
    ATTR_GEN_CONTROL_REQUIRE_UI = (PSCF_10 | PSCF_12 | PSCF_24 |
                PSCF_SMALLER | PSCF_LARGER);
    /* The following attribute defines which of the controller's
     * features will not be supported. These menu items will not
     * appear in the controller's Size menu. */
    ATTR_GEN_CONTROL_PROHIBIT_UI = (PSCF_14 | PSCF_18 | PSCF_36 | PSCF_54 |
                 PSCF_72 | PSCF_CUSTOM_SIZE);
}

Adding Application-Specific UI Gadgetry

ATTR_GEN_CONTROL_APP_UI, ATTR_GEN_CONTROL_APP_TOOLBOX_UI

Occasionally, an application will want to add its own UI gadgetry to a controller. This is not the same as changing the controller's functionality--to do that, you would need to subclass the controller class. Rather, this entails simply specifying a group of generic UI objects (e.g. a GenInteraction and some GenTriggers) that will be included with the controller's UI objects.

The GenControlClass vardata attribute ATTR_GEN_CONTROL_APP_UI allows you to specify a generic tree that will be added as a child of the controller. The top node of this tree must be an object that can be a child of a GenInteraction object--typically, it will be a GenInteraction, a GenTrigger, or a GenValue.

For example, if you wanted the sample application to have two extra triggers added to the Size menu, you would use ATTR_GEN_CONTROL_APP_UI as shown in Adding UI to a Controller . This example adds two triggers that turn the bottom GenText object on and off. (Of course, you would not likely put such functionality in the Size menu; this is given for illustration.)

This attribute has no effect on the tools managed by the controller, only on its features. To add application-specific tools to a controller, you must use ATTR_GEN_CONTROL_APP_TOOLBOX_UI .

Code Display 12-3 Adding UI to a Controller

/* This display shows the modified PointSizeControl object and the additional
 * UI gadgetry required to add two triggers to it. Although the triggers are
 * shown here in the same resource block as the controller, they do not have to
 * be. They do, however, have to be run by the same thread as the controller. */
@object PointSizeControlClass PSCTSizeControl = {
    GI_visMoniker = 'z', "Sizes";					/* Give the controller a name */
    GII_visibility = GIV_POPUP;					/* Make the controller a menu */
    /* The following attribute specifies the top object of a generic
     * tree to be included with the controller's UI. This attribute does
     * not affect the toolbar implementation of the controller. */
    ATTR_GEN_CONTROL_APP_UI = (@PSCTSpecialTrigs);						/* must be an optr */
}
/* This GenInteraction and its children (the two GenTriggers) will be included in
 * the controller's default representation (typically a menu). The GenInteraction
 * will appear as a submenu in OSF/Motif. All of the objects must be set not
 * usable (~GS_USABLE); they will be made usable by the controller when it becomes
 * usable. */
@object GenInteractionClass PSCTSpecialTrigs = {
    GI_comp = @PSCTEnableTrig, @PSCTDisableTrig;
    GI_states = @default & ~GS_USABLE;
    GII_visibility = GIV_POPUP;					/* Appear as a submenu-type item */
}
/* The triggers set the bottom GenText object usable or not usable. The instance
 * data of the triggers is unimportant for this example, but it is shown here
 * to complete the example. */
@object GenTriggerClass PSCTEnableTrig = {
    GI_visMoniker = "Use Two Text Fields";
    GI_states = @default & ~GS_USABLE;
    GTI_actionMsg = MSG_GEN_SET_USABLE;
    GTI_destination = PSCBotTextObj;
    ATTR_GEN_TRIGGER_ACTION_DATA = (VUM_NOW);
}
@object GenTriggerClass PSCTDisableTrig = {
    GI_visMoniker = "Use One Text Field";
    GI_states = @default & ~GS_USABLE;
    GTI_actionMsg = MSG_GEN_SET_NOT_USABLE;
    GTI_destination = PSCBotTextObj;
    ATTR_GEN_TRIGGER_ACTION_DATA = (VUM_NOW);
}

Generic UI Controllers: 3.2 Using Controllers: Using Tools

HINT_GEN_CONTROL_TOOLBOX_ONLY, ATTR_GEN_CONTROL_REQUIRE_TOOLBOX_UI, ATTR_GEN_CONTROL_PROHIBIT_TOOLBOX_UI, ATTR_GEN_CONTROL_APP_TOOLBOX_UI

A s stated earlier, any GenControl object can be manifested either as menus and menu items or as a set of tools. In fact, the controller can potentially have both its menu and its tools usable at once. Creating and using a toolbox or tool bar is a bit more complex than simply including the controller, however.

You can add the use of tool bars and tool boxes to your application in two ways: First, you can simply add a GenToolControl object and associated GenToolGroups and let them do all the work for you. Second, you can interact directly with the controller object(s) to put up, take down, and otherwise manage the tools. In nearly all cases, the first is preferable.

This section focuses on the use of a GenToolControl and GenToolGroup objects to manage and place your tools and toolboxes. If you want to manage tools without using a GenToolControl, you will have to know more about GenControlClass ; see Creating Your Own Controllers for complete details.

Components of Tool Management

Tools are most often represented by specific buttons or popup lists presented in various toolbars for easy use. They are functionally redundant to the menu features represented by the normal features of the controller, but the tools and menus can both be visible and usable at the same time.

To provide and manage tools, you need to understand the following components:

  • Controller
    The controller is the same GenControl subclass object discussed and shown in the previous sections. You can set minimum and maximum tool sets just like setting minimum and maximum feature set.
  • Tool Group
    Each controller object should have exactly one corresponding GenToolGroup object. The GenToolGroup manages the controller's tools and is, in turn, managed by the GenToolControl object (below).
  • Tool Controller
    You should have exactly one GenToolControl object; this object is a controller that allows the user to turn on and off individual tools and entire tool sets. It also controls placement of each tool group--which tool bar the tool group appears in.
  • Tool Bars and Tool Boxes
    You can have any number of tool bars and floating tool boxes. A tool bar or tool box is simply a GenInteraction set up properly and accessible by the user. Examples of tool bars are given in the next sections.
  • Data Tables
    Because the GenToolControl needs to describe both the tool bars and the tool groups to the user, you must set up special data chunks containing tables of names for each tool bar and tool group you use.

Using a Basic Tool Box

The most basic tool configuration includes a floating tool box in which the controllers' tools appear. To use a tool box, you only need to define a toolbox GenInteraction, add a GenToolControl object, and put them both in a menu. Typically, this will be an "Options" menu.

Providing a Basic Tool Box shows the configuration required for providing a basic tool box as well as the controller's default menu. (You could prohibit the appearance of the menu by setting ATTR_GEN_CONTROL_TOOLBOX_ONLY in the controller's instance data.) This example provides all the tools by setting the application's UI level in the GenApplication object. All the tools will then appear in the toolbox and its associated popups as shown in the figure above.

Setting Up the Tool Box

The tool box must be a GenInteraction object; for a floating tool box (as opposed to a tool bar), set it to be a dialog box (GIV_DIALOG). It should also have a moniker and HINT_TOOLBOX . The tool box must also be given a name. This name is used by the GenToolControl to identify the tool box. The name is set in a separate chunk and is a simple character string.

At startup, the GenToolGroup for the point size controller is set as a child of the floating tool box. The GenToolControl will move the GenToolGroup to other toolbars if possible; the tools must be set somewhere at startup, though, and the tool box is the logical starting point.

Setting Up the Tool Controller

The GenToolControl object will be of GenToolControlClass , as shown. It must at least have the GTCI_toolboxList field set to the chunk handle of a table of tool locations. In this example, the only location available to tools is in the floating tool box; other locations may be specified as detailed in Tool Placement below.

The table must appear in its own chunk in the same resource block as the GenToolControl object. It is an array of ToolboxInfo structures, each of which contains two optrs. The first is the optr of a tool location (typically a GenInteraction that may contain tools), and the second is the optr of the associated name. The structure of ToolboxInfo is shown below:

typedef struct {
    optr	TI_object;		/* A GenInteraction that
                 * can contain tools */
    optr	TI_name;		/* The name chunk of the
                 * TI_object object */
} ToolboxInfo;

The table is defined as shown in Providing a Basic Tool Box . If you had other controllers to be managed by the tool control, you would add other entries separated by commas.

The GenToolControl also has a tool group list indicating the name of each GenToolGroup. It is set up in a similar form to the tool box table.

Code Display 12-4 Providing a Basic Tool Box

/* This code display shows the entire psctext.goc file, with changes noted. All
 * unchanged code has had its comments stripped. For descriptions, see earlier
 * displays in this chapter. */
@include <stdapp.goh>
@include <ui.goh>
@include <Objects/Text/tCtrlC.goh>
@class	PSCTextProcessClass, GenProcessClass;
@endc
@classdecl PSCTextProcessClass, neverSaved;
@start	AppResource;
@object GenApplicationClass PSCTextApp = {
    GI_visMoniker = "Point Size Control Sample Application";
    GI_comp = @PSCTPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = @PSCTPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) =
                     @PSCTSizeControl, @PSCTToolControl;
}
@end	AppResource
@start	Interface;
/*	GenPrimary Object
 * Typically, an Options menu will be set up as a child of the Primary and
 * the tool box and tool control will be children of that menu. */
@object GenPrimaryClass PSCTPrimary = {
    GI_comp =		@PSCTSizeControl, @PSCTopTextObj, @PSCBotTextObj, @PSCTOptionsMenu;
    HINT_SIZE_WINDOW_AS_DESIRED;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
/*	Options Menu
 * This menu is the parent for both the Tool Box and the tool controller. */
@object GenInteractionClass PSCTOptMenu = {
    GI_comp = @PSCTToolBox, @PSCTToolControl;
    GII_visibility = GIV_POPUP;
    ATTR_GEN_INTERACTION_GROUP_TYPE = (GIGT_OPTIONS_MENU);
}
/*	Tool Box Location Table
 * The Tool Box Location Table is used by the GenToolControl object
 * to associate tool locations with their names. The table is an array
 * of ToolboxInfo structures. Multiple entries would be separated with
 * commas. */
@chunk ToolboxInfo PSCTToolboxList[] = {
    {@PSCTToolBox, @PSCTToolBoxName}					/* The single tool location is the
                     * floating tool box PSCTToolBox. */
};
/*	Tool Group Information Table
 * The Tool Group Information Table is used by the GenToolControl to associate
 * tool groups with their names. The table is an array of ToolGroupInfo structures.
 * Multiple entries would be separated with commas. */
@chunk ToolGroupInfo PSCTToolGroupTable[] = {
    {@PSCTPointSizeToolGroup}
};
/*	Floating Tool Box
 * The Tool Box object is a GenInteraction dialog box. All toolboxes must
 * have HINT_TOOLBOX and may have any additional geometry hints you
 * deem necessary. Because every controller's GenToolGroup object must
 * be a child of some tool bar, the PSCTPointSizeToolGroup is set at
 * startup as a child of this floating toolbox. */
@object GenInteractionClass PSCTToolBox = {
    GI_visMoniker = `T', "Tools";
    GI_comp = @PSCTPointSizeToolGroup;
    GII_visibility = GIV_DIALOG;
    HINT_TOOLBOX;
    HINT_ALLOW_CHILDREN_TO_WRAP;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_FULL_JUSTIFY_CHILDREN_HORIZONTALLY;
}
/* The Tool Box Name must be specified for the tool control object. It
 * must be a character string, and there must be one for each entry in
 * the Tool Location table. */
@chunk char PSCTToolBoxName[] = "Floating Tool Box";
/*	GenToolControl Object
 * The Tool Control object must have just the GTCI_toolboxList and
 * GTCI_toolGroupList fields set; these contain lists of tool bars and
 * tool groups along with their names. */
@object GenToolControlClass PSCTToolControl = {
    GTCI_toolboxList = @PSCTToolboxList;
    GTCI_toolGroupList = @PSCTToolGroupTable;
    HINT_SAME_CATEGORY_AS_PARENT;
}
/*	PointSizeControl			*/
@object PointSizeControlClass PSCTSizeControl = {
    GI_visMoniker = `z', "Sizes";
    GII_visibility = GIV_POPUP;
}
/*	GenToolGroup Object
 * Each controller object has exactly one GenToolGroup object for managing
 * its tools and for management by the GenToolControl. The Tool Group has
 * a single instance field specifying the controller for which it works. */
@object GenToolGroupClass PSCTPointSizeToolGroup = {
    GTGI_controller = @PSCTSizeControl;
}
/*	GenText Objects			*/
@object GenTextClass PSCTopTextObj = {
    GI_attrs = @default | GA_TARGETABLE;
    HINT_DEFAULT_FOCUS;
    HINT_DEFAULT_TARGET;
    ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR = ((VTDS_12 << VTDCA_SIZE_OFFSET) |
                    VTDF_URW_ROMAN);
}
@object GenTextClass PSCBotTextObj = {
 GI_attrs = @default | GA_TARGETABLE;
 ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR =
 ((VTDS_12 << VTDCA_SIZE_OFFSET) | VTDF_URW_ROMAN);
}
@end Interface

Tool Placement

Tools are movable; this means that controller tools can appear in any GenInteraction you may specify in the Tool Location Table. For example, you may want the user to be able to specify where she or he wants the tools to appear: in the floating tool box, at the top of the primary window, at the left of the display, or between the text objects. To support these locations for the tools, all you need to do is set up empty GenInteraction objects in the appropriate locations and add entries to the Tool Location Table.

The setup described above requires quite a few GenInteraction objects to be added to the application's generic tree. A line drawing of the geometry with all the empty GenInteractions is given in the figure above and the new generic tree of the application is shown in the figure below. The code representing this configuration is shown in Movable Tools --pay particular attention to the Tool Location Table and to the fact that each GenInteraction must have the hint HINT_TOOLBOX set in order to receive the tools.

Code Display 12-5 Movable Tools

/* This code display builds on  Providing a Basic Tool Box to show how tools
 * may be moved around your application's window by the user. Although this is
 * not difficult to do from scratch, it is quite involved; if you want this
 * functionality, it is best to include a GenToolControl object.
 * This code display only shows those objects that are additional to or altered
 * from the previous display. */
/*	GenPrimary Object
 * Two GenInteractions are made children of the Primary for geometry purposes.
 * The first, LeftToolBar, is actually a tool bar; the second,
 * TextAndToolInteraction, is a grouper interaction for geometry purposes. */
@object GenPrimaryClass PSCTPrimary = {
    GI_comp = @LeftToolBar, @PSCTSizeControl, @PSCTOptMenu,
                    @TextAndToolInteraction;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) =
                    @PSCTSizeControl, @PSCTToolControl;
    HINT_SIZE_WINDOW_AS_DESIRED;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
/*	Tool Location Table
 * The Tool Location Table is updated with the new tool box information. Each of
 * the new tool boxes is given a name, and each must appear in this table.
 * Note that the ToolGroup Information Table does not change. */
@chunk ToolboxInfo PSCTToolboxList[] = {
    {@PSCTToolBox, @PSCTToolBoxName},
    {@LeftToolBar, @LeftToolBarName},
    {@TopToolBar, @TopToolBarName},
    {@MiddleToolBar, @MiddleToolBarName}
};
/*	TextAndToolInteraction Interaction
 * This GenInteraction is used solely as a place holder grouping object to allow
 * the LeftToolBar object to extend the full height of the Primary window. */
@object GenInteractionClass TextAndToolInteraction = {
    GI_comp = @TopToolBar, @PSCTopTextObj, @MiddleToolBar, @PSCBotTextObj;
    HINT_ORIENT_CHILDREN_VERTICALLY;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
}
/*	New Tool Box Interactions
 * These GenInteraction objects are all tool boxes that appear in the Tool Location
 * Table. None actually has tools in it on startup; the tool controller allows the
 * user to place the tools of each active controller in any of these tool boxes. */
@object GenInteractionClass LeftToolBar = {
    HINT_TOOLBOX;
    HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
    HINT_ALLOW_CHILDREN_TO_WRAP;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@chunk char LeftToolBarName[] = "Left of Text";
@object GenInteractionClass TopToolBar = {
    HINT_TOOLBOX;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_ALLOW_CHILDREN_TO_WRAP;
}
@chunk char TopToolBarName[] = "Above Text";
@object GenInteractionClass MiddleToolBar = {
    HINT_TOOLBOX;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_ALLOW_CHILDREN_TO_WRAP;
}
@chunk char MiddleToolBarName[] = "In Between Text";

Adding Application-Specific UI to the Tool Box

ATTR_GEN_CONTROL_APP_TOOLBOX_UI

Occasionally an application may want to add some additional UI gadgetry to a set of controller tools. ATTR_GEN_CONTROL_APP_TOOLBOX_UI is analogous to ATTR_GEN_CONTROL_APP_UI , described in Adding Application-Specific UI Gadgetry . This attribute specifies a generic object tree that can be attached to the controller's tools as if it were part of the controller normally. For an example of ATTR_GEN_CONTROL_APP_UI 's use, see Adding Application-Specific UI Gadgetry .

Generic UI Controllers: 4 Creating Your Own Controllers

To create your own controller classes, you will have to subclass GenControlClass and handle certain messages and data structures. Most applications, however, will find that the controller classes provided with GEOS are adequate for their needs. Some library programmers may want to create their own controller classes.

GenControlClass is a subclass of GenInteractionClass . All instance data, hints, and messages appropriate for a GenInteraction are also appropriate for a GenControl object.

Typically, when you create your own controller, you will create a library in which the controller will reside. The controller may be in a library with a particular data object (e.g. PointSizeControlClass is part of the text library), or it may be in a library all by itself. For this discussion, the controller is considered to be part of a larger library.

The files of this library are

psCtrl.gp
The global parameters file for the controller's library. Only those portions of this file that pertain to the controller are discussed in this section.
psCtrl.goh
The header file containing the class definition of the controller class. This is separated from the class' code so the user of the controller can include the .goh file and thereby the controller.
psCtrl.goc
The code and resource file of the controller class. This file contains the UI objects and the methods of the controller.

All of these files are detailed throughout the following subsections. Each code display explains in which of these files it belongs.

GenControlClass Instance Data

Subclassing GenControlClass

Advanced GenControlClass Usage

Generic UI Controllers: 4.1 Creating Your Own Controllers: GenControlClass Instance Data

GenControlClass has several instance data fields that determine the controller's features and supported tool set. These instance fields, along with the messages defined for the class, are shown in GenControlClass Instance Data .

Code Display 12-6 GenControlClass Instance Data

/* GenControlClass has one static instance data field and several dynamic (vardata)
 * fields. These are shown below. */
    /* The GCI_output field contains the optr of the object to which
     * the controller is currently sending its "apply" messages. This
     * field is typically set to a GenTravelOption (such as TO_APP_TARGET)
     * or a TravelOption (such as TO_OBJ_BLOCK_OUTPUT). */
    @instance		optr	 GCI_output;
    /* Controller class objects are set disabled by default. When the
     * controller is initialized, it will set itself enabled. */
    @default		GI_states = (@default & ~GS_ENABLED);
/* the following attributes and hints determine the controller's
 * feature set. All of these hints and attributes are described in
 *  Using Controllers. */
    @vardata void			 HINT_GEN_CONTROL_TOOLBOX_ONLY;
    @vardata WordFlags			 ATTR_GEN_CONTROL_REQUIRE_UI;
    @vardata WordFlags			 ATTR_GEN_CONTROL_PROHIBIT_UI;
    @vardata WordFlags			 ATTR_GEN_CONTROL_REQUIRE_TOOLBOX_UI;
    @vardata WordFlags			 ATTR_GEN_CONTROL_PROHIBIT_TOOLBOX_UI;
/* The following two attributes control additional UI gadgetry added to a
 * controller object. */
    @vardata optr			 ATTR_GEN_CONTROL_APP_UI;
    @reloc	ATTR_GEN_CONTROL_APP_UI, 0, optr;
    @vardata optr			ATTR_GEN_CONTROL_APP_TOOLBOX_UI;
    @reloc	 ATTR_GEN_CONTROL_APP_TOOLBOX_UI, 0, optr;
 /* The following two hints allow an application to specify the initial
 * state of a controller including its placement, features, and
 * additional UI objects. */
    @vardata GenControlUserData  HINT_GEN_CONTROL_MODIFY_INITIAL_UI;
    @vardata GenControlUserData  HINT_GEN_CONTROL_USER_MODIFIED_UI;
 /* The following temporary data field is used to determine the tool settings
 * for a controller's set of tools. */
    @vardata TempGenControlInstance  TEMP_GEN_CONTROL_INSTANCE;

In general, a controller class will not have to worry about the instance data fields in GenControlClass Instance Data . Instead, it will handle a particular message, set up some UI resources, and rely on the functionality built into GenControlClass .

Generic UI Controllers: 4.2 Creating Your Own Controllers: Subclassing GenControlClass

When creating your own controller, you must subclass GenControlClass . You must also follow the steps outlined below (each is described in more detail throughout this section):

  1. Define the features and tool records
    You must define records and flags for the entire feature set and tool set of the controller. Each bit in one of these records corresponds to a single feature or tool of the controller; by turning these bits on and off, the controller manages which features and tools are available to the user. See Defining a Controller's Feature and Tool Sets .
  2. Define the default UI configuration
    Define a resource block containing the objects that will be the controller's default UI representation (typically a menu structure). See Defining the Default UI and Tool Configurations .
  3. Define the tool configuration
    Define a resource block containing generic UI objects that will make up the controller's tool set. See Defining the Default UI and Tool Configurations .
  4. Handle MSG_GEN_CONTROL_GET_INFO
     Every controller must handle this message and return critical information about the controller, its features, and its tools. This is the most involved step of creating a controller. See Mandatory Message Handling .
  5. Intercept appropriate GenControlClass messages
    Different controller classes will intercept different GenControlClass messages depending on how much additional functionality they require. Most controllers will intercept MSG_GEN_CONTROL_UPDATE_UI .
  6. Define and handle controller-specific messages
    Some controllers will set up their features and tools so that the messages they generate require some translation. For example, if a controller has one feature that sets a value based on all the other values, that feature may send a "set" message to the controller, which will determine the appropriate value to set. The controller will then pass the result on to the output object.

Defining a Controller's Feature and Tool Sets

E very controller must have a definition of all its features and tools. This definition typically resides in the controller class header file (in this case, psCtrl.goh ). Applications that use the controller must be able to turn on and off individual tools and features. These definitions take the form of records in which each bit represents a particular feature or tool. No controller may have more than sixteen features or tools; the controller has just one word representing which features and tools are "on."

To define the feature set of your controller class, define a record type of type WordFlags and one flag for each feature. The record type you define may be named anything; typically, however, its name will consist of the acronym of the controller class with the suffix "Features." For example, the features type and flags of PointSizeControlClass are shown below:

 typedef WordFlags PSCFeatures;
#define PSCF_10				0x0400
#define PSCF_12				0x0200
#define PSCF_14				0x0100
#define PSCF_18				0x0080
#define PSCF_24				0x0040
#define PSCF_36				0x0020
#define PSCF_54				0x0010
#define PSCF_72				0x0008
#define PSCF_SMALLER				0x0004
#define PSCF_LARGER				0x0002
#define PSCF_CUSTOM_SIZE 0x0001

Each of the flags represents one feature of the controller. For example, the PSCF_10 flag represents the "10 Point" trigger in the Sizes menu, and the PSCF_SMALLER flag represents the "Smaller" trigger. When a flag is set, the feature it represents is turned on; when clear, its feature is off.

Each controller class must create a similar record type and flags for its tool set. Because the tools are independent of the default features, two different sets of flags must be defined. An example (the PSCToolboxFeatures record of PointSizeControlClass ) follows.

 typedef WordFlags PSCToolboxFeatures;
#define PSCTF_9				0x0400
#define PSCTF_10				0x0200
#define PSCTF_12				0x0100
#define PSCTF_14				0x0080
#define PSCTF_18				0x0040
#define PSCTF_24				0x0020
#define PSCTF_36				0x0010
#define PSCTF_54				0x0008
#define PSCTF_72				0x0004
#define PSCTF_SMALLER				0x0002
#define PSCTF_LARGER				0x0001

After you have defined your controller class' feature and tool sets, you should define the controller's default feature and tool sets. The definitions for PointSizeControlClass are shown below:

#define PSC_DEFAULT_FEATURES					(PSCF_9 | PSCF_10 |
        PSCF_12 | PSCF_14 | PSCF_18 | PSCF_24 |
        PSCF_36 | PSCF_72 | PSCF_CUSTOM_SIZE |
        PSCF_SMALLER | PSCF_LARGER)
#define PSC_DEFAULT_TOOLBOX_FEATURES (PSCTF_9 |
        PSCTF_10 | PSCTF_12 | PSCTF_14 |
        PSCTF_18 | PSCTF_24 | PSCTF_36 |
        PSCTF_72 | PSCTF_SMALLER | PSCTF_LARGER)

These values and flags will be used in your handler for the controller message MSG_GEN_CONTROL_GET_INFO .

Defining the Default UI and Tool Configurations

After you have determined which features and tools your controller class will support, you must create the UI objects that correspond to them. To do this, declare two separate resource segments--one to contain the feature objects and the other to contain the tool objects. Then declare a third that contains just chunks with text strings in it.

Both resources should be defined notDetachable , meaning that the feature and tool objects will not be saved to a state file. In the global parameters file for your controller, each resource must be declared with the ui-object , read-only , and shared flags as below:

resource SIZECTRLUI ui-object read-only shared
resource SIZECTRLTOOLUI ui-object read-only shared
resource CONTROLSTRINGS lmem read-only shared

The UI resources typically contain list objects and their corresponding items. As an alternative, they can contain triggers and dialogs. These objects are declared as a standard object resource with generic objects, as shown in Controller UI Resources . All these objects must be set not usable (~GS_USABLE).

Code Display 12-7 Controller UI Resources

/* This display contains only code that appears in the psCtrl.goc file. The first
 * elements of the file are other included files, followed by a class declaration
 * statement. The two UI resources are shown after that, simplified somewhat;
 * Only redundant objects are left out of the display. */
/* Include the controller class definition and declare the class structure. */
@include <psCtrl.goh>
@classdecl PointSizeControlClass;
@start	SizeCtrlUI, notDetachable;
/* Define the features UI resource. This resource can contain any objects that may
 * typically appear in a menu (e.g. GenInteractions, GenTriggers, and list
 * objects). This example shows a single list object and a few of its entries. */
@object GenItemGroupClass SizesList = {
    GI_states = @default & ~GS_USABLE;					/* Set the list not usable */
    /* The children of the list are defined below. Each entry in the
     * list will appear as a single menu item. */
    GI_comp =		@Size10Entry, @Size12Entry, @Size14Entry, @Size18Entry,
        @Size24Entry, @Size36Entry, @Size54Entry, @Size72Entry;
    /* The "apply" message will be sent to the destination specified
     * in the GIGI_destination field. */
    GIGI_applyMsg = MSG_PSC_SET_POINT_SIZE_FROM_LIST;
    /* The destination is defined as the TravelOption TO_OBJ_BLOCK_OUTPUT.
     * This will send the apply message to the controller's output object. */
    GIGI_destination = (TO_OBJ_BLOCK_OUTPUT);
}
/* An example of a GenItem for the above list. all the other children are similar
 * with different monikers and identifiers. The identifiers in this case are
 * equivalent to the point size setting for the feature. */
@object GenItemClass Size10Entry = {
    GI_visMoniker = `1', "1. 10 point";
    GII_identifier = 10;
}
/* A GenTrigger. Shown below is the "Smaller" menu entry of the Point Size
 * controller. Another trigger ("Larger") and a GenInteraction (the "Custom
 * Size" entry) are also declared. These objects do not have to be declared
 * as children of any object; they will automatically, like the list above,
 * be designated as children of the controller when it is initialized.
 * Note that all of these objects must also be set not usable. */
@object GenTriggerClass SmallerTrigger = {
    GI_states = @default & ~GS_USABLE;
    GI_visMoniker = `S', "Smaller";
    GI_kbdAccelerator = control `9';
    GTI_actionMsg = MSG_PSC_SMALLER_POINT_SIZE;
    GTI_destination = (TO_OBJ_BLOCK_OUTPUT);
}
@end	SizeCtrlUI
/* Define the Tools UI resource. This follows exactly the same rules as the
 * Features UI resource above, but it represents the UI gadgetry that will appear
 * in the controller's tool boxes rather than its default menus. */
@start	SizeCtrlToolUI, notDetachable;
@object GenItemGroupClass SizesToolList = {
    /* Same as SizesList above, but with the following hints applied: */
    HINT_ITEM_GROUP_MINIMIZE_SIZE;
    HINT_ITEM_GROUP_DISPLAY_CURRENT_SELECTION;
}
/* The list entry items have the exact configuration as above but different
 * names that reflect their tool usage.
 * The only objects allowed as tools for the Point Size controller are the
 * point size list entries, the larger trigger, and the smaller trigger. The
 * "Custom Size" entry is not allowed in the tool box as a matter of style. */
@object GenTriggerClass SmallerToolTrigger = {
    GI_states = @default & ~GS_USABLE;
    GI_visMoniker = "S";
    /* The moniker of a tool is typically a graphic. The moniker
     * specified here is text for simplicity. */
    GTI_actionMsg = MSG_PSC_SMALLER_POINT_SIZE;
    GTI_destination = TO_OBJ_BLOCK_OUTPUT;
}
@end	SizeCtrlToolUI
@start	ControlStrings;
/* In addition to the above two resources, you must also create a third that
 * contains name strings for the various tools and features. These name strings
 * will be used by the GenToolControl to identify the feature type in its
 * dialog box. */
@chunk char		PSCName[] = "Point Size";
@chunk char		Size10Name[] = "10 Point";
@chunk char		Size12Name[] = "12 Point";
    /* The rest of the point sizes are similar */
@chunk char		SmallerName[] = "Smaller Point Size";
@chunk char		LargerName[] = "Larger Point Size";
@chunk char		CustomSizeName[] = "Custom Point Size";
@end	ControlStrings

Mandatory Message Handling

 MSG_GEN_CONTROL_GET_INFO, GenControlBuildInfo

Every controller must handle MSG_GEN_CONTROL_GET_INFO . This message is sent to the controller in several circumstances, and it must return critical information about the controller's state and configuration. It takes a pointer to an empty GenControlBuildInfo structure and must fill in all the structure's fields before returning. This structure is shown in MSG_GEN_CONTROL_GET_INFO Handler with a description of each of its fields following.

Code Display 12-8 The GenControlBuildInfo Structure

 /* This structure must be filled and returned by the controller class. It details
 * general information as well as specific information about the controller, the
 * controller's features, and the controller's tools. */
typedef struct {
    GenControlBuildFlags				GCBI_flags;
    const char				*GCBI_initFileKey;
    const GCNListType				*GCBI_gcnList;
    word				GCBI_gcnCount;
    const NotificationType				*GCBI_notificationList;
    word				GCBI_notificationCount;
    optr				GCBI_controllerName;
    MemHandle				GCBI_dupBlock;
    const GenControlChildInfo				*GCBI_childList;
    word				GCBI_childCount;
    const GenControlFeaturesInfo				*GCBI_featuresList;
    word				GCBI_featuresCount;
    WordFlags				GCBI_features;
    MemHandle				GCBI_toolBlock;
    const GenControlChildInfo				*GCBI_toolList;
    word				GCBI_toolCount;
    const GenControlFeaturesInfo				*GCBI_toolFeaturesList;
    word				GCBI_toolFeaturesCount;
    WordFlags				GCBI_toolFeatures;
    char				*GCBI_helpContext;
    byte				GCBI_reserved[8];
} GenControlBuildInfo;

The following fields define general information about the controller.

GCBI_flags
A record of GenControlBuildFlags . These flags affect several UI-related and object storage related functions, and they are detailed on GenControlBuildFlags .
GCBI_initFileKey
A pointer to a text string indicating the controller's key in the GEOS.INI file. Controller options will be saved under this key.
GCBI_gcnList
A pointer to a list of GCN list types. Objects of this controller class will be added to these GCN lists and will receive notification from them. GCN lists are detailed in the GCN chapter.

GCBI_gcnCount
 The length of the list pointed to by GCBI_gcnList above. This length should be the number of lists specified.

GCBI_notificationList
 A pointer to a list of notification types supported by the controller.
GCBI_notificationCount
The size of the list pointed to by GCBI_notificationList above.
GCBI_controllerName
The optr of a chunk containing the text string that serves as the controller's name. This name string is displayed by the GenToolControl in its dialog box to identify the controller.

The following fields define information about the controller's features. These fields will be filled dependent on the features set in the object's instance data and the UI level of the controller.

GCBI_dupBlock
The handle of the resource block containing the controller's feature generic objects. In the example, this would contain the handle of the SizeCtrlUI resource.
GCBI_childList
A pointer to a list of GenControlChildInfo structures; each of these structures details which features are set and which should always be set for each of the controller's children. This structure is shown below:
    typedef struct {
        ChunkHandle			GCCI_object;
        WordFlags			GCCI_featureMask;
        GenControlChildFlags GCCI_flags;
    }  GenControlChildInfo;
Each structure contains the chunk handle of the given child in t he resource block, a feature mask indicating which features are possibly supported by the child, and a record indicating whether the child is a feature or not and whether the child is always added to the controller's UI. More specific information is shown on GenControlChildInfo .
GCBI_childCount
The number of children specified in GCBI_childList above.
GCBI_featuresList
A pointer to a list of GenControlFeaturesInfo structures, one for each child. These structures define the following:
    typedef struct {
        ChunkHandle			GCFI_object;
        optr			GCFI_name;
        GenControlFeatureFlags GCFI_flags;
    }  GenControlFeaturesInfo;
The three fields are the chunk handle of the child; the optr of the child's name string, as defined in the name string resource; and a record of GenControlFeatureFlags . This structure and its fields are described more fully on GenControlFeaturesInfo .
GCBI_featuresCount
The number of GenControlFeaturesInfo structures listed in GCBI_featuresList above.
GCBI_features
A features mask describing the features supported by the current UI level as specified in the GenApplication's GAI_appFeatures field.

The following fields describe information about the controller's tools and their configuration.

GCBI_toolBlock
The handle of the resource block containing the controller's tool generic objects. In the example, this would contain the handle of the SizeCtrlToolUI resource.
GCBI_toolList
A pointer to a list of GenControlChildInfo structures; each of these structures details which tools are set and which should always be set for each of the controller's children. This structure is shown below:
    typedef struct {
        ChunkHandle			GCCI_object;
        WordFlags			GCCI_featureMask;
        GenControlChildFlags GCCI_flags;
    }  GenControlChildInfo;
Each structure contains the chunk handle of the given child in t he resource block, a feature mask indicating which tools are possibly supported by the child, and a record indicating whether the child is a tool or not (e.g. a list) and whether the child is always added to the controller's tool box UI. More specific information is shown on GenControlChildInfo .
GCBI_toolCount
The number of children specified in GCBI_toolList above.
GCBI_toolFeaturesList
A pointer to a list of GenControlFeaturesInfo structures, one for each child. These structures define the following:
    typedef struct {
        ChunkHandle			GCFI_object;
        optr			GCFI_name;
        byte			GCFI_flags;
                /* GenControlFeatureFlags */
    }  GenControlFeaturesInfo;
The three fields are the chunk handle of the child; the optr of the child's name string, as defined in the name string resource; and a record of GenControlFeatureFlags . This structure and its fields are described more fully on GenControlFeaturesInfo .
GCBI_toolFeaturesCount
The number of GenControlFeaturesInfo structures in the list pointed to by GCBI_toolFeaturesList above.
GCBI_toolFeatures
A tools mask describing the tools supported for the UI level specified in the GenApplication's GAI_appFeatures field.

The following field is used by controllers that offer their own help files and help text.

GCBI_helpContext
A pointer to a character string giving the name of the controller's help context. If this is a non-null pointer, then the controller will automatically add ATTR_GEN_HELP_CONTEXT to itself with the specified string.

The structure also has eight bytes that are reserved for the use of GenControlClass, in the GCBI_reserved field.

GenControlBuildFlags

This flags record defines several UI-related things about the controller object. Set them appropriate to your controller. The flags are

GCBF_SUSPEND_ON_APPLY
Causes MSG_META_SUSPEND to be sent on feature activation and MSG_META_UNSUSPEND afterward. This is often set by controllers.
GCBF_USE_GEN_DESTROY
Ensures that unused objects can not be freed with LMemFree() . Not often set by controllers.
GCBF_SPECIFIC_UI
Indicates that the controller may be implemented in the specific UI and therefore some special action must be taken. Very rarely set by controllers.
GCBF_CUSTOM_ENABLE_DISABLE
Indicates that the controller uses a custom enable/disable mechanism rather than responding to GCN notifications.
GCBF_ALWAYS_UPDATE
Indicates that the controller should always undergo visual updates even if it appears unnecessary. Not often set by controllers.
GCBF_EXPAND_TOOL_WIDTH_TO_FIT_PARENT
Indicates that the tool width should be expanded to take full advantage of all the space available in the parent composite.
GCBF_ALWAYS_INTERACTABLE
Indicates that the controller should always be on its appropriate GCN lists, even if no part of it is visible. This flag requires GCBF_IS_ON_ACTIVE_LIST.
GCBF_ALWAYS_ON_GCN_LIST
Indicates that the controller should constantly be on the GCN lists rather than periodically adding and removing itself as is done in some optimization code. Not often set by controllers. This flag requires GCBF_IS_ON_ACTIVE_LIST.
GCBF_MANUALLY_REMOVE_FROM_ACTIVE_LIST
Indicates that the controller should not remove itself from the active list in its default detach handler.
GCBF_IS_ON_ACTIVE_LIST
Indicates that the controller is on the active list in its .goh file definition.
GCBF_IS_ON_START_LOAD_OPTIONS_LIST
Indicates this controller must be on the startup load options list.
GCBF_NOT_REQUIRED_TO_BE_ON_SELF_LOAD_OPTIONS_LIST
Indicates this controller is not required to be on any options-load list.
GenControlChildInfo

The GenControlChildInfo structure defines the features or tools appropriate to each object in a controller's UI resources. It has the following structure, and its fields are described below:

typedef struct {
    ChunkHandle				GCCI_object;
    WordFlags				GCCI_featureMask;
    GenControlChildFlags				GCCI_flags;
}  GenControlChildInfo;
GCCI_object
The chunk handle of the object in the appropriate resource.
GCCI_featureMask
The feature mask representing the feature set represented by the object.
GCCI_flags
A record of GenControlChildFlags , the flags of which are described below.

The GenControlChildFlags flags are

GCCF_NOTIFY_WHEN_ADDING
This flag indicates that the child will be notified before the feature is added and set usable with MSG_GEN_CONTROL_NOTIFY_ADDING_FEATURE .
GCCF_ALWAYS_ADD
This flag indicates that the child object should always be added to the controller's UI, even if it is not specified.
GCCF_IS_DIRECTLY_A_FEATURE
This flag indicates that the child is a feature in itself and thus on the feature list. This is typically set for most objects. (It is not set, for example, for list objects, whose children are the actual features.)
GenControlFeaturesInfo

The GenControlFeaturesInfo structure describes each UI feature's name and certain flags. The structure is defined below, and its fields are described following:

typedef struct {
    ChunkHandle				GCFI_object;
    optr				GCFI_name;
    GenControlFeatureFlags				GCFI_flags;
}  GenControlFeaturesInfo;
GCFI_object
The chunk handle of the child in the appropriate resource block.
GCFI_name
The optr of the chunk containing the object's name. This name is used by the GenToolControl to represent the particular feature or tool in its dialog box.
GCFI_flags
A record of GenControlFeatureFlags , reserved.
MSG_GEN_CONTROL_GET_INFO
void	MSG_GEN_CONTROL_GET_INFO(
        GenControlBuildInfo *info);

This message must be handled by all controllers. It takes an empty GenControlBuildInfo structure and fills it; this message is called in several circumstances by different objects and controller methods.

Source: Unrestricted--typically generated in GenControlClass methods.

Destination: Any controller object.

Parameters: info A pointer to an empty GenControlBuildInfo structure.

Return: The GenControlBuildInfo structure filled with the appropriate controller information.

Interception: Every controller class must intercept this message. There is no need to call the superclass anywhere in the handler.

Code Display 12-9 MSG_GEN_CONTROL_GET_INFO Handler

/* This method is a sample of how to handle MSG_GEN_CONTROL_GET_INFO. It is
 * specific to UICTextStyleControlClass. To handle this message, it is easiest to
 * set up a number of static local variables with the base information and set
 * the structure to these variables. */
/* Handler for MSG_GEN_CONTROL_GET_INFO
 * void (GenControlBuildInfo *info);					*/
@method UICTextStyleControlClass, MSG_GEN_CONTROL_GET_INFO {
    /* General information constants */
 static const char TSC_IniFileKey[] = "textStyleControl";
 static const GCNListType TSC_gcnList[] = {
     {MANUFACTURER_ID_GEOWORKS, GAGCNLT_APP_TARGET_NOTIFY_TEXT_CHAR_ATTR_CHANGE}
 };
 static const NotificationType TSC_notifyTypeList[] = {
     {MANUFACTURER_ID_GEOWORKS, GWNT_TEXT_CHAR_ATTR_CHANGE}
 };
    /* Features information constants */
    static const  GenControlChildInfo TSC_childList[] = {
    {@PlainTextList, TSCF_PLAIN, GCCF_IS_DIRECTLY_A_FEATURE},
    {@TextStyleList, TSCF_BOLD|TSCF_ITALIC|TSCF_UNDERLINE|TSCF_STRIKE_THRU|
     TSCF_SUBSCRIPT|TSCF_SUPERSCRIPT, 0}
    };
    /* The order of this list is actually backwards from the
     * record it reflects. */
    static const  GenControlFeaturesInfo TSC_featuresList[] = {
     {@SuperscriptEntry, @SuperscriptName, 0},
     {@SubscriptEntry, @SubscriptName, 0},
     {@StrikeThruEntry, @StrikeThruName, 0},
     {@UnderlineEntry, @UnderlineName, 0},
     {@ItalicEntry, @ItalicName, 0},
     {@BoldEntry, @BoldName, 0},
     {@PlainTextList, @PlainTextName, 0}
    };
    /* Tools information constants */
    static const GenControlChildInfo TSC_toolList[] = {
     {@PlainTextToolList, TSCTF_PLAIN, GCCF_IS_DIRECTLY_A_FEATURE},
     {@TextStyleToolList, TSCTF_BOLD|TSCTF_ITALIC|TSCTF_UNDERLINE|
     TSCTF_STRIKE_THRU|TSCTF_SUBSCRIPT|TSCTF_SUPERSCRIPT, 0} };
    };
    static const GenControlFeaturesInfo TSC_toolFeaturesList[] = {
     {@SuperscriptToolEntry, @SuperscriptName, 0},
     {@SubscriptToolEntry, @SubscriptName, 0},
     {@StrikeThruToolEntry, @StrikeThruName, 0},
     {@UnderlineToolEntry, @UnderlineName, 0},
     {@ItalicToolEntry, @ItalicName, 0},
     {@BoldToolEntry, @BoldName, 0},
     {@PlainTextToolList, @PlainTextName, 0}
    };
    /* Our constant for the GenControlBuildInfo structure.
     * Fields with a marker to the left of their names are
     * filled in dynamically by the handler following the
     * constant definition. */
    static const  GenControlBuildInfo TSC_dupInfo = {
    GCBF_SUSPEND_ON_APPLY, 					/* GCBI_flags */
     TSC_IniFileKey, 					/* GCBI_initFileKey */
     TSC_gcnList, 					/* GCBI_gcnList */
     ARRAY_LEN(TSC_gcnList,GCNListType), 					/* GCBI_gcnCount */
     TSC_notifyTypeList, 					/* GCBI_notificationList */
     ARRAY_LEN(TSC_notifyTypeList, NotificationType),
                        /* GCBI_notificationCount */
     @TSCName, 					/* GCBI_controllerName */

     /* ## */ NullHandle, 					/* GCBI_dupBlock */
     TSC_childList, 					/* GCBI_childList */
     ARRAY_LEN(TSC_childList, GenControlChildInfo),
                         /* GCBI_childCount */
     TSC_featuresList, 					/* GCBI_featuresList */
     ARRAY_LEN(TSC_featuresList, GenControlFeaturesInfo),
                         /* GCBI_featuresCount */
     TSC_DEFAULT_FEATURES, 					/* GCBI_features */
     /* ## */ NullHandle, 					/* GCBI_toolBlock */
     TSC_toolList, 					/* GCBI_toolList */
     ARRAY_LEN(TSC_toolList, GenControlChildInfo),
                         /* GCBI_toolCount */
     TSC_toolFeaturesList, 					/* GCBI_toolFeaturesList */
     ARRAY_LEN(TSC_toolFeaturesList, GenControlFeaturesInfo),
                         /* GCBI_toolFeaturesCount */
     TSC_DEFAULT_TOOLBOX_FEATURES 					/* GCBI_toolFeatures */
    };
    /* Here is the code that fills in the above missing fields and
     * returns the proper structure. */
    /* Copy the structure containing most of the correct information. */
    memcpy(info, MemLockFixedOrMovable(&TSC_dupInfo), sizeof(GenControlBuildInfo));
    MemUnlockFixedOrMovable(&TSC_dupInfo);
    /* Fill the remaining fields in manually. */
    info->GCBI_dupBlock = HandleOf(@PlainTextList);
    info->GCBI_toolBlock = HandleOf(@PlainTextToolList);
}

Generic UI Controllers: 4.3 Creating Your Own Controllers: Advanced GenControlClass Usage

Everything you need to create a basic custom controller class is detailed in the previous sections. GenControlClass , however, has a number of messages and structures that will be used by some subclasses, though this will be relatively rare. This section details these messages and structures.

Adding and Removing UI Gadgetry

MSG_GEN_CONTROL_GENERATE_UI, MSG_GEN_CONTROL_DESTROY_UI, MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI, MSG_GEN_CONTROL_DESTROY_TOOLBOX_UI, MSG_GEN_CONTROL_UPDATE_UI, MSG_GEN_CONTROL_ENABLE_DISABLE, MSG_GEN_CONTROL_ADD_APP_UI, MSG_GEN_CONTROL_ADD_APP_TOOLBOX_UI, MSG_GEN_CONTROL_REBUILD_NORMAL_UI, MSG_GEN_CONTROL_REBUILD_TOOLBOX_UI

GenControlClass has several messages that add, remove, and manipulate the controller UI gadgetry. Keep in mind also that, as a subclass of GenInteractionClass and thereby of GenClass , the GenControl also can use any of the generic UI messages for object tree manipulation. These messages are detailed in the following list.

MSG_GEN_CONTROL_GENERATE_UI
void	MSG_GEN_CONTROL_GENERATE_UI();

This message generates the UI gadgetry for the controller.

Source: Unrestricted--sent by self as part of specific UI build.

Destination: Any GenControl object

Interception: If subclassed to add functionality, the subclass must call the superclass at the beginning of the handler.

Warnings: If you intercept this message, you should also intercept MSG_GEN_CONTROL_DESTROY_UI , below.

MSG_GEN_CONTROL_DESTROY_UI
void	MSG_GEN_CONTROL_DESTROY_UI();

This message destroys the UI gadgetry for the controller.

Source: Unrestricted--sent by self when being taken off the screen.

Destination: Any GenControl object

Interception: Any subclass that intercepts MSG_GEN_CONTROL_GENERATE_UI must intercept this. The subclass must call the superclass at the end of the handler.

MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI
void	MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI(
        optr	parent);

This message generates the UI gadgetry for the controller's tool box.

Source: Sent by tool box object via the active list as part of its specific UI build mechanism.

Destination: The GenControl object that owns the tool box.

Parameters: parent The optr of GenInteraction that will be the parent of the controller's tools.

Return: Nothing.

Interception: A subclass may intercept this to add UI gadgetry to the toolbox when the toolbox is built. The subclass must call the superclass at the beginning of the handler.

Warnings: Any controller that subclasses this message must also subclass MSG_GEN_CONTROL_DESTROY_TOOLBOX_UI as well.

MSG_GEN_CONTROL_DESTROY_TOOLBOX_UI
void	MSG_GEN_CONTROL_DESTROY_TOOLBOX_UI();

This message destroys all toolbox UI associated with the controller.

Source: Sent by the tool box being destroyed as part of its specific UI destruction mechanism.

Destination: The GenControl object that owns the tool box.

Interception: Any controller class that subclasses the message MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI must also subclass this message. The subclass must call its superclass at the end of its handler.

MSG_GEN_CONTROL_UPDATE_UI
void	MSG_GEN_CONTROL_UPDATE_UI(@stack
        MemHandle		toolBlock,
        MemHandle		childBlock,
        WordFlags		toolboxFeatures,
        WordFlags		features,
        MemHandle		data,
        word		changeID,
        ManufacturerID		manufID);

This message updates all UI components for the recipient controller.

Source: Sent by GenControlClass in its default handler for MSG_META_NOTIFY_WITH_DATA_BLOCK.

Destination: The GenControl object being updated.

Parameters: toolBlock Handle of the object resource block containing the controller's tool UI gadgetry.

childBlock
Handle of the object resource block containing the controller's default UI gadgetry. This may be taken from the TEMP_GEN_CONTROL_INSTANCE vardata field.
toolboxFeatures
A record of flags indicating which tools are currently on for the controller. This may be taken from the TEMP_GEN_CONTROL_INSTANCE vardata field. If the toolbox UI is not interactible, then this field will be zero.
features
A record of flags indicating which features are currently on for the controller. This may be taken from the TEMP_GEN_CONTROL_INSTANCE vardata field. If the UI is not interactible, then this field will be zero.
data
The data block handle passed with the notification message.
changeID
The type of update to undergo, as passed with the notification message.
manufID
The manufacturer ID of the notification type passed with the notification message.

Interception: Controllers should intercept this message in order to properly update their UI gadgetry. There is no default handler for this message.

Structures: The TEMP_GEN_CONTROL_INSTANCE vardata field is of type TempGenControlInstance , which has the following structure:

  typedef struct {
    GenControlInteractableFlags
                TGCI_interactableFlags;
    MemHandle			TGCI_childBlock;
    MemHandle			TGCI_toolBlock;
    optr			TGCI_toolParent;
    WordFlags			TGCI_features;
    WordFlags			TGCI_toolboxFeatures;
    GCNListType			TGCI_activeNotificationList;
    GenControlInteractableFlags TGCI_upToDate;
} TempGenControlInstance;
TGCI_interactableFlags
This record describes which, if any, of a controller's UI parts is interactable (visible and usable). Its flags are listed below.
TGCI_childBlock
The handle of the resource block containing the controller's default UI objects.
TGCI_toolBlock
The handle of the resource block containing the tool objects.
TGCI_toolParent
The optr of the object passed with MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI , if any (tools will be added to this object).
TGCI_features
A record of flags indicating which controller features are currently active.
TGCI_toolboxFeatures
A record of flags indicating which controller tools are currently active.
TGCI_activeNotificationList
The notification type currently active.
TGCI_upToDate
The status of GenControlInteractableFlags at the time of the last notification; that is, which portions of the controller's UI were up to date. This is used by the specific UI for drawing optimizations.

The flags for the GenControlInteractableFlags record are listed below:

GCIF_CONTROLLER
This flag indicates the controller object is interactable.
GCIF_TOOLBOX_UI
This flag indicates that some or all of the controller's tool gadgetry is interactable.
GCIF_NORMAL_UI
This flag indicates that some or all of the controller's normal feature gadgetry is interactable.
MSG_GEN_CONTROL_ENABLE_DISABLE
void	MSG_GEN_CONTROL_ENABLE_DISABLE(
        Message		msg;
        VisUpdateMode		updateMode);

This message enables or disables the controller object as well as its default and tool box UI gadgetry.

Source: Sent by GenControlClass to the controller object when it receives MSG_META_NOTIFY_WITH_DATA_BLOCK .

Destination: Sent by controller to itself.

Parameters: msg Either MSG_GEN_SET_ENABLED or MSG_GEN_SET_NOT_ENABLED as appropriate.

updateMode
A VisUpdateMode indicating when the visual update should occur.

Interception: Unlikely--typically should not be intercepted.

MSG_GEN_CONTROL_ADD_APP_UI
void	MSG_GEN_CONTROL_ADD_APP_UI(
        optr	appUI);

This message adds the passed UI object to the controller's generic tree as if it had been originally defined in the default child block. By default, the new object is added as the last child of the controller.

Source: Unrestricted--generated as part of the default functionality of MSG_GEN_CONTROL_GENERATE_UI if the controller has ATTR_GEN_CONTROL_APP_UI set.

Destination: Any GenControl object.

Parameters: appUI The optr of the object to be added.

Interception: Should be intercepted by controllers that wish to add the new object somewhere other than as the last child.

MSG_GEN_CONTROL_ADD_APP_TOOLBOX_UI
void	MSG_GEN_CONTROL_ADD_APP_TOOLBOX_UI(
        optr	appUI);

This message adds the passed UI object to the controller's tool UI gadgetry as if it had been defined as a tool in the tool resource block. By default, the new object is added as the last child of the controller.

Source: Unrestricted--generated as part of the default functionality of MSG_GEN_CONTROL_GENERATE_TOOLBOX_UI if the controller has ATTR_GEN_CONTROL_APP_TOOLBOX_UI set.

Destination: Any GenControl object.

Parameters: appUI The optr of the object to be added.

Interception: Should be intercepted by controllers that wish to add the new object somewhere other than as the last child.

MSG_GEN_CONTROL_REBUILD_NORMAL_UI
void	MSG_GEN_CONTROL_REBUILD_NORMAL_UI();

This message forces the normal UI of the controller to be rebuilt; each component will be visually destroyed and rebuilt.

Source: Unrestricted.

Destination: Any GenControl object.

Interception: Should not be intercepted.

MSG_GEN_CONTROL_REBUILD_TOOLBOX_UI
void	MSG_GEN_CONTROL_REBUILD_TOOLBOX_UI();

This message forces the toolbox UI of the controller to be rebuilt; each component will be visually destroyed and rebuilt.

Source: Unrestricted.

Destination: Any GenControl object.

Interception: Should not be intercepted.

Adding and Removing Features and Tools

MSG_GEN_CONTROL_SCAN_FEATURE_HINTS, MSG_GEN_CONTROL_ADD_FEATURE, MSG_GEN_CONTROL_REMOVE_FEATURE, MSG_GEN_CONTROL_ADD_TOOLBOX_FEATURE, MSG_GEN_CONTROL_REMOVE_TOOLBOX_FEATURE, MSG_GEN_CONTROL_GET_NORMAL_FEATURES, MSG_GEN_CONTROL_GET_TOOLBOX_FEATURES

Besides being able to set and clear features of a given controller, both the controller class and outside agents can dynamically alter the features of a given controller object. GenControlClass has several messages that you can use for this purpose; these messages are detailed below.

MSG_GEN_CONTROL_SCAN_FEATURE_HINTS
void	MSG_GEN_CONTROL_SCAN_FEATURE_HINTS(
        GenControlUIType		type,
        GenControlScanInfo		*info);

This message scans the feature hints set for the controller object to set the required and prohibited features.

Source: Unrestricted--Sent by GenControlClass to itself in numerous circumstances.

Destination: The GenControl object to be scanned.

Parameters: type An indicator whether the normal or tool box UI hints are to be scanned. This should be GCUIT_NORMAL for the normal hints, GCUIT_TOOLBOX for the tool box hints.

info
A pointer to an empty GenControlScanInfo structure to be filled in by the handler. This structure is shown below.

Return: No value is returned directly.

info
The pointer to the GenControlScanInfo structure filled in by the method.

Interception: Should not be intercepted.

Structures: The GenControlScanInfo structure has the following fields:

typedef struct {
    WordFlags		GCSI_userAdded;
    WordFlags		GCSI_userRemoved;
    WordFlags		GCSI_appRequired;
    WordFlags		GCSI_appProhibited;
} GenControlScanInfo;
GCSI_userAdded
A record of features added by the user through the use of the GenToolControl object. This record also reflects the use of MSG_GEN_CONTROL_ADD_FEATURE .
GCSI_userRemoved
A record of features removed by the user through the use of the GenToolControl object.
GCSI_appRequired
A record of features required; this is set for the controller with ATTR_GEN_CONTROL_REQUIRE_TOOLBOX_UI or ATTR_GEN_CONTROL_REQUIRE_UI .
GCSI_appProhibited
A record of features prohibited; this is set for the controller with ATTR_GEN_CONTROL_PROHIBIT_TOOLBOX_UI or ATTR_GEN_CONTROL_PROHIBIT_UI .
MSG_GEN_CONTROL_ADD_FEATURE
void	MSG_GEN_CONTROL_ADD_FEATURE(
        WordFlags featureToAdd);

This message adds a feature to those currently supported by the controller. This is used for the default settings only, not for tool box UI. The controller is destroyed and then rebuilt with the new feature, causing it to be marked for saving to the state file in the new configuration.

Source: Unrestricted.

Destination: Any GenControl object.

Parameters: featureToAdd A feature record with the flag of the feature to be added set.

Interception: Should not be intercepted.

MSG_GEN_CONTROL_REMOVE_FEATURE
void	MSG_GEN_CONTROL_REMOVE_FEATURE
        WordFlags featureToRemove);

This message removes a feature from those currently supported by the controller. It does not affect the active tools. The controller will be marked for saving to the state file in the new configuration; it is destroyed and rebuilt with feature removed.

Source: Unrestricted.

Destination: Any GenControl object.

Parameters: featureToRemove A feature record with the flag of the feature to be removed set.

Interception: Should not be intercepted.

MSG_GEN_CONTROL_ADD_TOOLBOX_FEATURE
void	MSG_GEN_CONTROL_ADD_TOOLBOX_FEATURE(
        WordFlags featureToAdd);

This message adds a tool to those currently supported by the controller. This is used for the tool box settings only, not for the default UI features. The controller is destroyed and then rebuilt with the new tool, causing it to be marked for saving to the state file in the new configuration.

Source: Unrestricted.

Destination: Any GenControl object.

Parameters: featureToAdd A feature record with the flag of the to be added set.

Interception: Should not be intercepted.

MSG_GEN_CONTROL_REMOVE_TOOLBOX_FEATURE
void	MSG_GEN_CONTROL_REMOVE_TOOLBOX_FEATURE(
        WordFlags featureToRemove);

This message removes a tool from those currently supported by the controller. It does not affect the default active feature list. The controller will be marked for saving to the state file in the new configuration; it is destroyed and rebuilt with the tool removed.

Source: Unrestricted.

Destination: Any GenControl object.

Parameters: featureToRemove A feature record with the flag of the tool to be removed set.

Interception: Should not be intercepted.

MSG_GEN_CONTROL_GET_NORMAL_FEATURES
void MSG_GEN_CONTROL_GET_NORMAL_FEATURES(
        GenControlGetFeaturesReturn *return);

This message returns a structure indicating which of the default features of the controller are currently active, which are required, and which are prohibited.

Source: Unrestricted--typically GenToolControl.

Destination: Any GenControl object.

Parameters: return A pointer to an empty structure to be returned.

Return: The return parameter, upon return, points to a filled GenControlGetFeaturesReturn structure.

Structures: The GenControlGetFeaturesReturn structure is shown below:

typedef struct {
    WordFlags		GCSR_features;
    WordFlags		GCSR_required;
    WordFlags		GCSR_prohibited;
    WordFlags		GCSR_supported;
} GenControlGetFeaturesReturn;
GCSR_features
A record of flags indicating which features are currently active.
GCSR_required
A record of flags indicating which features, at the least, are required for the controller.
GCSR_prohibited
A record of flags indicating which features are prohibited to the controller.
GCSR_supported
A record of flags indicating the full range of supported features.
MSG_GEN_CONTROL_GET_TOOLBOX_FEATURES
void MSG_GEN_CONTROL_GET_TOOLBOX_FEATURES(
        GenControlGetFeaturesReturn *return);

This message returns a structure indicating which of the controller's tools are currently active, which are required, and which are prohibited.

Source: Unrestricted--typically GenToolControl.

Destination: Any GenControl object.

Parameters: return A pointer to an empty structure to be returned.

Return: The return parameter, upon return, points to a filled GenControlGetFeaturesReturn structure.

Structures: The GenControlGetFeaturesReturn structure is shown in MSG_GEN_CONTROL_GET_NORMAL_FEATURES , above.

Working with GCN Lists

MSG_GEN_CONTROL_ADD_TO_GCN_LISTS, MSG_GEN_CONTROL_REMOVE_FROM_GCN_LISTS

Because GenControlClass uses GCN lists, it has two messages that add itself to and remove itself from the lists. You can intercept or send these if you need to; applications generally will not, however. These two messages are detailed below.

MSG_GEN_CONTROL_ADD_TO_GCN_LISTS
void	MSG_GEN_CONTROL_ADD_TO_GCN_LISTS();

This message adds the controller object to the GCN lists specified by its return value of MSG_GEN_CONTROL_GET_INFO . It will force a status update to ensure that the controller updates itself.

Source: Generated internally on startup and at other times.

Destination: Sent to self.

Interception: Generally not intercepted. Subclasses should use this only to see when the controller is being added to its lists; it should call the superclass to do the actual addition.

MSG_GEN_CONTROL_REMOVE_FROM_GCN_LISTS
void	MSG_GEN_CONTROL_REMOVE_FROM_GCN_LISTS();

This message removes the controller from all its GCN lists.

Source: Generated internally on shutdown and at other times.

Destination: Sent to self.

Interception: Generally not intercepted. Subclasses should use this only to see when the controller is being removed from its lists; it should call the superclass to do the actual removal.

Working with Controller Visibility

MSG_GEN_CONTROL_NOTIFY_INTERACTABLE, MSG_GEN_CONTROL_NOTIFY_NOT_INTERACTABLE, MSG_GEN_CONTROL_UNBUILD_NORMAL_UI_IF_POSSIBLE

Because controllers are generic objects which may or may not be interactable, GenControlClass has two messages to handle the interactable state. It has an additional message to close the normal UI tools, if possible. These three messages are detailed below.

MSG_GEN_CONTROL_NOTIFY_INTERACTABLE
void	MSG_GEN_CONTROL_NOTIFY_INTERACTABLE(
        GenControlInteractableFlags flags);

This message instructs the controller to take any actions necessary before being put on the screen and made interactable. It causes the controller to add itself to its GCN lists (if it wasn't already) and take any final steps to get ready to go on the screen.

Source: Generated Internally--do not generate externally.

Destination: Sent to self.

Parameters: flags A single record of flags indicating which portions of the controller will be made interactable.

Interception: May be intercepted to circumvent default behavior.

MSG_GEN_CONTROL_NOTIFY_NOT_INTERACTABLE
void	MSG_GEN_CONTROL_NOTIFY_NOT_INTERACTABLE(
        GenControlInteractableFlags flags);

This message indicates that the UI has determined that the controller can no longer be seen or interacted with by the user. The default behavior of the controller is to remove itself from its GCN lists for optimization.

Source: Generated Internally--do not generate externally.

Destination: Sent to self.

Parameters: flags A single record of flags indicating which portions of the controller are no longer interactable.

Interception: May be intercepted to circumvent default behavior.

MSG_GEN_CONTROL_UNBUILD_NORMAL_UI_IF_POSSIBLE
void	MSG_GEN_CONTROL_UNBUILD_NORMAL_UI_IF_POSSIBLE();

This internal message requests that the normal default controller UI gadgetry be removed and destroyed if they are not in use. This is an optimization intended to save swap space and perhaps memory space; it causes the objects to be loaded back in again when needed, however. Thus, the optimization is only suited to machines with limited swap space (it originated due to these conditions on palmtop machines).

Source: Generated internally. Do not generate externally.

Destination: Sent to self.

Interception: May be intercepted to avoid the optimization. May also need to be intercepted if a subclass creates its own UI gadgetry by intercepting MSG_GEN_CONTROL_GENERATE_UI .

Generic UI Controllers: 5 GenToolControlClass

GenToolControlClass is a subclass of GenControlClass ; the tool control allows the user to manipulate and manage all the other controllers in an application and save the configuration he or she sets up.

Previous sections of this chapter explain how to include a GenToolControl object in your application; this section details the specific structures, messages, and internals of GenToolControlClass . Most application programmers will never need to know this information, though some library programmers will want to read the next section.

GenToolControlClass has two instance fields beyond those inherited from GenControlClass . One field is the chunk handle of the Tool Location Table, and the other is the chunk handle of the Tool Group List, both defined below:

@instance ChunkHandle				 GTCI_toolboxList;
@instance ChunkHandle				 GTCI_toolGroupList;
    @default GI_states = @default | GS_ENABLED;

The Tool Location Table must be set up for any application that uses a GenToolControl. The structure of this table is given in Using Tools . The total number of allowable entries in the Tool Location Table is MAX_NUM_TOOLBOXES, which is 25. Because the GenToolControl uses one of these entries, you can have at most 24 other locations for a controller's tools to appear.

The Tool Group List is a list of all the GenToolGroup objects associated with controllers in the application. Each controller may have exactly one GenToolGroup object for managing its tools, and all the ToolGroups must appear in this list if the GenToolControl is to manage them. The Tool Group List is described in Using Tools .

GenToolControlClass also uses a vardata field to store temporary information about a particular controller; the tool control uses this information when presenting the user with options of where the controller's features may be placed. This vardata field is shown below (both the definition of the field and the structure it uses):

@vardata TempGenToolControlInstance
             TEMP_GEN_TOOL_CONTROL_INSTANCE;
  typedef struct {
    optr		TGTCI_curController;
    word		TGTCI_features;
    word		TGTCI_required;
    word		TGTCI_allowed;
}  TempGenToolControlInstance;

The individual fields of the temporary structure are described below.

TGTCI_curController
The optr of the current controller whose tool options are being presented to the user. The GenToolControl allows the user to select and edit the features and location of this controller. If the user selects a new controller for editing, this field will be changed to the new controller's optr.
TGTCI_features
The mask of the currently active features of the controller.
TGTCI_required
The mask of the features which the controller object has defined as required--these must always be active and can not be "hidden" by the user. This mask is set by the controller with ATTR_GEN_CONTROL_REQUIRE_TOOLBOX_UI .
TGTCI_allowed
The mask of features that are allowed by both the controller and the application. Features set in this mask but not in TGTCI_features will be implemented but will be "hidden" from the user. Features not in either list will not be implemented by the controller for the application.

The GenToolControl uses a number of internal messages that should not be intercepted. You can, however, use the data structures of GenToolControlClass in a subclass and add functionality to the tool controller.

Generic UI Controllers: 6 GenToolGroupClass

The GenToolGroup object is used to group tools for an individual controller. It is highly unlikely you will ever need to subclass or interact directly with GenToolGroupClass , though you will often use GenToolControl objects.

GenToolGroupClass is subclassed from GenInteractionClass because its primary functions are grouping and geometry management. Each GenControl object in your application should have exactly one GenToolGroup associated with it, and all the GenToolGroups should appear in the Tool Group List of the GenToolControl.

GenToolGroupClass has one instance data field in addition to those inherited from GenInteractionClass :

@instance optr			 GTGI_controller;
    @default GI_states = (@default & ~GS_ENABLED);

This field contains the optr of the GenControl object whose tools are managed by this GenToolGroup. This field is nearly always set in your .goc file.

The GenToolGroup also has a temporary vardata field that indicates the color in which the tool group should be highlighted; this allows the user to better see which set of tools is being "discussed" (e.g. which set is currently being manipulated by the GenToolControl).

@vardata Color  TEMP_TOOL_GROUP_HIGHLIGHT;

To set the highlight type, GenToolGroupClass has a single message. MSG_GEN_TOOL_GROUP_SET_HIGHLIGHT sets the color of the group's highlight; if the group should not be highlighted, the color should be set to -1.

MSG_GEN_TOOL_GROUP_SET_HIGHLIGHT
void	MSG_GEN_TOOL_GROUP_SET_HIGHLIGHT(
        ToolGroupHighlightType			hlType);

This message sets the highlight color of the tool group. This is used almost exclusively by the GenToolControl.

Source: GenToolControl object; can also be sent by others.

Destination: The GenToolGroup whose highlight color is to be set.

Parameters: hlType The new highlight type to be used by the tool group. The values are TGHT_INACTIVE_HIGHLIGHT, TGHT_ACTIVE_HIGHLIGHT, and TGHT_NO_HIGHLIGHT.

Return: Nothing.

Interception: Should not be intercepted.

Generic UI Controllers: 7 Other Controllers

Most system objects provide their own controllers; those controllers are documented along with the objects they control. Some controllers, however, may be used by more than one system object; these are documented here. The three classes below are ColorSelectorClass , GenPageControlClass , and StyleSheetControlClass .

ColorSelectorClass

GenPageControlClass

The Float Format Controller

Generic UI Controllers: 7.1 Other Controllers: ColorSelectorClass

The ColorSelector controller provides all the UI necessary to allow the user to select a color, a draw mask, and a draw pattern. The color may be selected either as an index or an RGB value. The ColorSelector must be put on the GenApplication's GAGCNLT_SELF_LOAD_OPTIONS GCN list.

The ColorSelector has the following features:

  typedef WordFlags CSFeatures;
#define CSF_FILLED_LIST				0x10
#define CSF_INDEX				0x08
#define CSF_RGB				0x04
#define CSF_DRAW_MASK				0x02
#define CSF_PATTERN				0x01
#define CS_DEFAULT_FEATURES					(CSF_FILLED_LIST |
        CSF_INDEX | CSF_RGB | CSF_DRAW_MASK |
        CSF_PATTERN)

In addition, the ColorSelector has several instance fields, all shown below:

 @instance ColorQuad				CSI_color = {0, 0, 0, 0};
        /* currently selected color */
@instance byte				CSI_colorIndeterminate;
        /* true if color is indeterminate */
@instance SystemDrawMask CSI_drawMask = SDM_0;
        /* draw mask in use by the color */
@instance byte				CSI_drawMaskIndeterminate;
        /* true if mask is indeterminate */
@instance GraphicPattern CSI_pattern = {0, 0};
        /* pattern in use by the color */
@instance byte				CSI_patternIndeterminate;
        /* true if pattern indeterminate */
@instance ColorModifiedStates CSI_states = 0;
        /* indicates which aspects have changed:
         * CMS_COLOR_CHANGED
         * CMS_DRAW_MASK_CHANGED
         * CMS_PATTERN_CHANGED */
@instance ColorToolboxPreferences CSI_toolboxPrefs
                        = CTP_IS_POPUP;
        /* preferences for color selector:
         * CTP_INDEX_ORIENTATION
         * CTP_DRAW_MASK_ORIENTATION
         * CTP_PATTERN_ORIENTATION
         * CTP_IS_POPUP */
@vardata optr ATTR_COLOR_SELECTOR_DISABLE_OBJECT;
        /* when color selector is disabled, the
         * object named will also be disabled */

Messages Sent Out by the ColorSelector

MSG_META_COLORED_OBJECT_SET_COLOR, MSG_META_COLORED_OBJECT_SET_DRAW_MASK, MSG_META_COLORED_OBJECT_SET_PATTERN

The ColorSelector sends out three messages to its data objects. These messages each serve to set a particular attribute of the object it currently controls (the target object). The target object must be able to handle these messages if it is to use the ColorSelector.

MSG_META_COLORED_OBJECT_SET_COLOR
void	MSG_META_COLORED_OBJECT_SET_COLOR(
        ColorQuad color);

This message notifies the controlled object that it should set its color to the passed value.

Source: ColorSelector object.

Destination: The current Target object.

Parameters: color The ColorQuad structure describing the color to be set.

Return: Nothing.

Interception: Must be intercepted by the controlled object if it is to work with the ColorSelector controller.

MSG_META_COLORED_OBJECT_SET_DRAW_MASK
void	MSG_META_COLORED_OBJECT_SET_DRAW_MASK(
        SystemDrawMask mask);

This message notifies the controlled object that it should set its draw mask to the passed value.

Source: ColorSelector object.

Destination: The current Target object.

Parameters: mask The SystemDrawMask to be set as the object's draw mask.

Return: Nothing.

Interception: Must be intercepted by the controlled object if it is to work with the ColorSelector controller.

MSG_META_COLORED_OBJECT_SET_PATTERN
void	MSG_META_COLORED_OBJECT_SET_PATTERN(
        GraphicPattern pattern);

This message notifies the controlled object that it should sets its draw pattern to that passed.

Source: ColorSelector object.

Destination: The current Target object.

Parameters: pattern The GraphicPattern to be set as the object's draw pattern.

Return: Nothing.

Interception: Must be intercepted by the controlled object if it is to work with the ColorSelector controller.

Messages Handled by ColorSelectorClass

MSG_COLOR_SELECTOR_GET_COLOR, MSG_COLOR_SELECTOR_SET_COLOR, MSG_COLOR_SELECTOR_GET_DRAW_MASK, MSG_COLOR_SELECTOR_SET_DRAW_MASK, MSG_COLOR_SELECTOR_GET_PATTERN, MSG_COLOR_SELECTOR_SET_PATTERN

The ColorSelector handles a number of messages including the normal GenControlClass messages it inherits. These messages allow the setting or retrieval of the instance data of the controller.

MSG_COLOR_SELECTOR_GET_COLOR
void	MSG_COLOR_SELECTOR_GET_COLOR(
        ColorQuad		*retVal);

This message retrieves the controller's CSI_color field.

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: retVal A pointer to an empty ColorQuad structure.

Return: The ColorQuad structure pointed to by retVal will be the color set in CSI_color .

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_SET_COLOR
void	MSG_COLOR_SELECTOR_SET_COLOR(
        ColorQuad color);

This message sets the color in CSI_color .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: color The ColorQuad structure representing the new color for the CSI_color field.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_GET_DRAW_MASK
SystemDrawMask MSG_COLOR_SELECTOR_GET_DRAW_MASK();

This message retrieves the draw mask set in CSI_drawMask .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: None.

Return: The SystemDrawMask set in CSI_drawMask .

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_SET_DRAW_MASK
void	MSG_COLOR_SELECTOR_SET_DRAW_MASK(
        SystemDrawMask mask);

This message sets the draw mask stored in CSI_drawMask .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: mask The new SystemDrawMask to be set into CSI_drawMask .

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_GET_PATTERN
GraphicPattern MSG_COLOR_SELECTOR_GET_PATTERN();

This message returns the pattern set in CSI_pattern .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: None.

Return: The GraphicPattern currently set in CSI_pattern .

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_SET_PATTERN
void	MSG_COLOR_SELECTOR_SET_PATTERN(
        GraphicPattern pattern);

This message sets the drawing pattern stored in CSI_pattern .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: pattern The GraphicPattern value to be set.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_UPDATE_COLOR
See colorC.goh

Update the current color in the toolbox and normal UI. This message is normally sent from within a MSG_GEN_CONTROL_UPDATE_UI handler.

Source: Unrestricted--sent in a MSG_GEN_CONTROL_UPDATE_UI handler.

Destination: Any ColorSelector object.

Parameters: A ColorQuad structure and an indeterminate flag.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_APPLY_COLOR
See colorC.goh

Makes the color selector send MSG_META_COLORED_OBJECT_SET_COLOR .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: A Color enumeration value.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_UPDATE_FILLED_STATUS
See colorC.goh

Makes the color selector update the draw mask and associated UI.

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: A SystemDrawMask , an indeterminate flag, and a tools update flag.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_GET_FILLED_MONIKER
See colorC.goh

Returns the VisMoniker that should be used to represent the "do draw" item in the color selector.

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: None.

Return: The optr of the VisMoniker chunk or NullOptr to use the default.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_GET_UNFILLED_MONIKER
See colorC.goh

Returns the VisMoniker that should be used to represent the "don't draw" item in the color selector.

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: None.

Return: The optr of the VisMoniker chunk or NullOptr to use the default.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_UPDATE_DRAW_MASK
See colorC.goh

Update the current draw mask in the toolbox and the UI. This is often sent from within a MSG_GEN_CONTROL_UPDATE_UI handler.

Source: Unrestricted--sent in a MSG_GEN_CONTROL_UPDATE_UI handler.

Destination: Any ColorSelector object.

Parameters: A SystemDrawMask value and an indeterminate flag.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_APPLY_DRAW_MASK
See colorC.goh

Generates MSG_META_COLORED_OBJECT_SET_DRAW_MASK .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: A SystemDrawMask value.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_UPDATE_PATTERN
See colorC.goh

Update the current pattern in the toolbox and the UI. This is often sent from within a MSG_GEN_CONTROL_UPDATE_UI handler.

Source: Unrestricted--sent in a MSG_GEN_CONTROL_UPDATE_UI handler.

Destination: Any ColorSelector object.

Parameters: A GraphicPattern value and an indeterminate flag.

Return: Nothing.

Interception: Generally not intercepted.

MSG_COLOR_SELECTOR_APPLY_PATTERN
See colorC.goh

Generates MSG_META_COLORED_OBJECT_SET_PATTERN .

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: A GraphicPattern value.

Return: Nothing.

Interception: Generally not intercepted.

MSG_CS_SET_FILLED_STATUS
See colorC.goh

Tells the controller to disable itself and set the mask to zero or to re-enable itself and set the mask to 100, based on the passed flag.

Source: Unrestricted.

Destination: Any ColorSelector object.

Parameters: A SysDrawMask value.

Return: Nothing.

Interception: Generally not intercepted.

MSG_CS_SET_CF_INDEX
See colorC.goh

Sent by the color list to set a color via an index.

Source: The color list.

Destination: Any ColorSelector object.

Parameters: See colorC.goh .

Return: Nothing.

Interception: Generally not intercepted.

MSG_CS_SET_CF_RGB_RED
See colorC.goh

Sent by the color list to set a color's red value.

Source: The color list.

Destination: Any ColorSelector object.

Parameters: see colorC.goh .

Return: Nothing.

Interception: Generally not intercepted.

MSG_CS_SET_CF_RGB_GREEN
See colorC.goh

Sent by the color list to set a color's green value.

Source: The color list.

Destination: Any ColorSelector object.

Parameters: see colorC.goh .

Return: Nothing.

Interception: Generally not intercepted.

MSG_CS_SET_CF_RGB_BLUE
See colorC.goh

Sent by the color list to set a color's blue value.

Source: The color list.

Destination: Any ColorSelector object.

Parameters: see colorC.goh .

Return: Nothing.

Interception: Generally not intercepted.

MSG_CS_SET_DRAW_MASK
See colorC.goh

Sets the draw mask for the color selector.

MSG_CS_SET_PATTERN
See colorC.goh

Sets the pattern for the color selector .

Generic UI Controllers: 7.2 Other Controllers: GenPageControlClass

GenPageControlClass provides a controller object that allows the user to go to any page in a page range. This controller provides UI gadgetry for going to a specified page, going to the next page, or going to the previous page.

The GenPageControl must be put on the GAGCNLT_SELF_LOAD_OPTIONS GCN list in the GenApplication object. It responds to the notification type MANUFACTURER_ID_GEOWORKS.GWNT_PAGE_STATE_CHANGE (sent with a NotifyPageStateChange structure) and sends out three messages exported from MetaClass .

GenPageControlClass is subclassed from GenControlClass and has the features and tools shown below. It has none of its own instance data fields.

  typedef WordFlags GPCFeatures;
#define GPCF_GOTO_PAGE					0x0004
#define GPCF_NEXT_PAGE					0x0002
#define GPCF_PREVIOUS_PAGE					0x0001
  typedef WordFlags GPCToolboxFeatures;
#define GPCTF_PREVIOUS_PAGE					0x0004
#define GPCTF_GOTO_PAGE					0x0002
#define GPCTF_NEXT_PAGE					0x0001
#define  GPC_DEFAULT_FEATURES (GPCF_GOTO_PAGE |
        GPCF_NEXT_PAGE | GPCF_PREVIOUS_PAGE)
#define  GPC_DEFAULT_TOOLBOX_FEATURES
        (GPCTF_GOTO_PAGE | GPCTF_NEXT_PAGE |
         GPCTF_PREVIOUS_PAGE)
@default GCI_output = (TO_APP_TARGET);

The messages this controller sends out must be handled by any object controlled by it.

M SG_META_PAGED_OBJECT_GOTO_PAGE
void	MSG_META_PAGED_OBJECT_GOTO_PAGE(
        word	page);

This message causes the paged object to go to the specified page.

Source: The GenPageControl object.

Destination: The target paged object.

Parameters: page The page number to which the recipient should go.

Return: Nothing.

Interception: This message must be intercepted for the paged object to interact properly with the GenPageControl object.

MSG_META_PAGED_OBJECT_NEXT_PAGE
void	MSG_META_PAGED_OBJECT_NEXT_PAGE();

This message indicates that the recipient should go to the next page.

Source: The GenPageControl object.

Destination: The target paged object.

Interception: This message must be intercepted for the paged object to interact properly with the GenPageControl object.

MSG_META_PAGED_OBJECT_PREVIOUS_PAGE
void	MSG_META_PAGED_OBJECT_PREVIOUS_PAGE();

This message indicates that the recipient should go to the previous page.

Source: The GenPageControl object.

Destination: The target paged object.

Interception: This message must be intercepted for the paged object to interact properly with the GenPageControl object.

MSG_PC_GOTO_PAGE
see gPageCC.goh

Causes the page control object to go to the specified page.

Source: Unrestricted.

Destination: The page control object.

Parameters: The page to go to.

Return: Nothing.

Interception: Should not be intercepted.

MSG_PC_NEXT_PAGE
void	MSG_PC_NEXT_PAGE();

Causes the page controller to go to the next page.

Source: Unrestricted.

Destination: The page control object.

Interception: Should not be intercepted.

MSG_PC_PREVIOUS_PAGE
void	MSG_PC_PREVIOUS_PAGE();

Causes the page controller to go to the previous page.

Source: Unrestricted.

Destination: The page control object.

Interception: Should not be intercepted .

Generic UI Controllers: 7.3 Other Controllers: The Float Format Controller

Including the Float Format controller in your application allows the user to format FP numbers into any of the system-defined formats. The controller also allows the user to define his or her own formats.

The following messages, routines and data structures all manage arrays of system-defined and user-defined formats. If you include a Float Format controller, you will need to intercept these messages and call many of these routines.

Retrieving Parameters of the Current Entry

 FloatFormatGetFormatParamsWithListEntry(),  FloatFormatGetFormatParamsWithToken(),  FloatFormatGetFormatTokenWithName()

The Float Format controller routines use a FormatInfoStruc structure to hold information about a particular format. This structure is convenient to pass around to other float format routines. The Float Format controller contains one instance field, formatInfoStrucHan , that stores the handle to the current FormatInfoStruc , if one is being used by the controller.

Code Display 12-10 FormatInfoStruc

typedef struct {
    /*
     * FIS_signature is used internally for error-checking.
     */
    word		FIS_signature;
    /*
     * These two entries store the user defined format array for the
     * controller to work on. This array is created by FloatFormatInit().
     */
    FileHandle		FIS_userDefFmtArrayFileHan;
    VMBlockHandle 		FIS_userDefFmtArrayBlkHan;
    /*
     * These two entries store the object block and format list chunk that the
     * controller resides in.
     */
    word		FIS_childBlk;
    word		FIS_chooseFmtListChunk;
    /*
     * FIS_features stores the features list of the controller.
     */
    word		FIS_features;
    /*
     * FIS_editFlag is -1 if we are editing a current user-defined entry and 0
     * if we are creating a new user-defined entry.
     */
    byte		FIS_editFlag;
    /* FIS_curSelection stores the current selection in the format list. */
    word		FIS_curSelection;
    /* FIS_curToken stores the token of the selected item in the list. */
    word		FIS_curToken;
    /* FIS_curParams stores the FormatParams of the selected item. */
    FormatParams		FIS_curParams;
} FormatInfoStruc;

FloatFormatGetFormatParamsWithListEntry() fills in a FormatInfoStruc corresponding to the passed list entry position in the Float Format controller's dynamic list. The routine must be passed a FormatInfoStruc with the entry position in FIS_ curSelection already filled in. You must also have the FIS_ userDefFmtArrayFileHan and FIS_ userDefFmtArrayBlkHan filled in properly before calling this routine.

This routine is called whenever the user clicks on a new item, or whenever the dynamic list needs to retrieve new monikers for the list. You will also probably need to use this routine in your application code when intercepting many of the Float Format controller messages.

FloatFormatGetFormatParamsWithToken() fills in the FormatParams of a particular format entry when passed a format's token. The routine must be passed the FormatInfoStruc with FIS_ curToken already filled in. You must also have the FIS_ userDefFmtArrayFileHan and FIS_ userDefFmtArrayBlkHan filled in properly before calling this routine.

FloatFormatGetFormatTokenWithName() returns the format token of a particular format when passed its name (in a FormatInfoStruc ).

Initializing the UI

 MSG_FLOAT_CTRL_UPDATE_UI,  MSG_FLOAT_CTRL_REQUEST_MONIKER,  FloatFormatInit(),  FloatFormatInitFormatList()

FloatFormatInit() initializes a format array to serve as the storage space for user-defined formats. It must be passed the VM file handle to create the array within.

MSG_FLOAT_CTRL_UPDATE_UI is sent to the Float Format controller whenever the controller needs to perform a visual update. Your Float Format controller should intercept this message and call FloatFormatInitFormatList() .

FloatFormatInitFormatList() initializes the Float Format dynamic list whenever the list needs to display a new moniker. It extracts the moniker text of the selected format from the FormatInfoStruc structure. Make sure that this structure has the proper VM file and block handles filled in prior to calling this routine.

MSG_FLOAT_CTRL_REQUEST_MONIKER is sent to the Float Format controller whenever it needs to display the text within the controller's dynamic list. To extract the current format's moniker, intercept this message and call FloatFormatGetFormatParamsWithListEntry() . You can then pass the format's textual name (from FIS_ curParams .FP_ formatName ) to MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT.

Setting Up Selected Formats

 MSG_FLOAT_CTRL_FORMAT_SELECTED,  FloatFormatProcessFormatSelected()

MSG_FLOAT_CTRL_FORMAT_SELECTED is sent to the Float Format controller whenever the user makes a new selection. This message allows your application to set up a new FormatInfoStruc based on the new selection. Your handler for this message needs to call FloatFormatProcessFormatSelected() .

User Defined Format Creation

 MSG_FLOAT_CTRL_USER_DEF_INVOKE,  FloatFormatInvokeUserDefDB(),  MSG_FLOAT_CTRL_USER_DEF_OK,  FloatFormatUserDefOK()

MSG_FLOAT_CTRL_USER_DEF_INVOKE is sent to the Float Format controller whenever the user has defined a new format and wishes to add it to the controller's dynamic list. Your handler for this message should call FloatFormatInvokeUserDefDB() in turn. If the user attempts to create a new format when MAX_FORMATS already exist, the routine will invoke an error box.

MSG_FLOAT_CTRL_USER_DEF_OK is sent to the Float Format controller to check whether the user-defined format is legal or previously duplicated. Your handler for this message should call FloatFormatUserDefOK() to perform this check and apply the user-defined format to the list of format entries if it is successful.

Deleting Formats

 MSG_FLOAT_CTRL_FORMAT_DELETE,  FloatFormatDelete()

MSG_FLOAT_CTRL_FORMAT_DELETE is sent to the Float Format controller when the user attempts to delete a user-defined entry. Your handler for this message needs to call FloatFormatDelete() .

Applying Formats

 MSG_FLOAT_CTRL_FORMAT_APPLY

MSG_FLOAT_CTRL_FORMAT_APPLY is sent to the FloatFormat controller when the user attempts to apply a format selected in the controller to the appropriate text selection. Your handler will need to extract information from the FormatInfoStruc and call the appropriate text formatting routine (e.g. FloatFloatToAsciI() ).

This document is a single-page version of a a multi-page document, suitable for easy printing.


GenClass

GenClass provides the basic functionality, via message handlers and instance data, for all objects in the generic object library. All generic objects are subclassed off this master class. Therefore, all generic objects will contain common behavior in the instance fields and message handlers of GenClass . GenClass allows you to create generic trees, manage state data, and attach data such as visual monikers, keyboard accelerators, and even variable-length data to a specific object.

The Gen world, along with the Vis world, contains a large variety of pre-defined objects ready to install into your applications. GenClass itself is not a normally usable object, however. It should not be directly subclassed and used by your application. The functionality within this chapter will normally be accessed within one of GenClass ' subclasses.

All of these pre-defined objects will contain behavior explained in this chapter. The instance data and message handlers described in this chapter may be implemented differently under different specific UIs, but the basic functionality will remain the same. Therefore, it is important to fully understand the concepts explained in this chapter before using any generic object.

1 GenClass Features
2 GenClass Instance Data
2.1 Instance Fields
2.2 Vardata
3 GenClass Basics
3.1 Visual Monikers
3.2 Composite Links
3.3 Keyboard Accelerators
3.4 Attributes
3.5 States
4 Modifying GenClass Instance Data
4.1 Visual Monikers
4.2 Managing Visual Monikers
4.3 Managing Keyboard Accelerators
5 Generic Trees
5.1 Child/Parent Searches
5.2 Manipulating Children Directly
5.3 Branch Construction/Destruction
6 Vardata
6.1 Optional Attributes (ATTRs)
6.2 Hints to the Specific UI
6.3 Dynamically Managing VarData
7 Special Message Passing
7.1 Parent and Child Message Passing
7.2 Generic Upward Queries
7.3 Object-Specific Queries
8 Visual Refreshing
9 Setting Sizes
10 Activation Messages
11 Focus Modifications
12 Navigation Methods
13 Window Positions and Sizes

GenClass: 1 GenClass Features

GenClass provides the instance fields and message handlers common to all generic objects. You will need to understand and manipulate GenClass operations in almost every generic object you create. GenClass is very powerful and diverse, providing a number of wide-ranging functions within your user interface. GenClass in specific provides the following:

  • an abstract UI providing code common within any specific user interface.
  • an object-oriented generic tree structure, including dynamic creation and destruction of generic UI objects "on the fly."
  • an object-oriented messaging system, including generic upward queries, allowing the UI objects to pass messages without defining a specific destination.
  • pre-instantiation and persistence (state-saving) across sessions.
  • window sizing and positioning with an abstract geometry manager without involved coordinate space computations.
  • a generic visual update mechanism for your application, allowing you to ensure an accurate and visually up-to-date UI.
  • the ability to create custom UI gadgets of your own.

Most importantly, GenClass provides the common functionality that will be accessed by all generic objects from the simplest GenTrigger to the most complex GenView.

GenClass: 2 GenClass Instance Data

Generic objects, along with visual objects, are the main means through which a user can communicate with an application. All generic objects follow an object-oriented class hierarchy with GenClass acting as their superclass. Therefore, all generic objects will share the attributes and behavior of GenClass , although many may not make use of the entire utility provided.

Instance Fields

Vardata

GenClass: 2.1 GenClass Instance Data: Instance Fields

GenClass provides a set of instance fields common to all subclasses. These instance fields are listed in GenClass Instance Data Fields .

Code Display 2-1 GenClass Instance Data Fields

    @instance @link				GI_link;
    @instance @visMoniker				GI_visMoniker;
    @instance @composite				GI_comp;
    @instance @kbdAccelerator				GI_kbdAccelerator;
    @instance GenAttrs				GI_attrs = 0;
    @instance GenStates GI_states = GS_USABLE | GS_ENABLED;
/* GenAttrs store attributes of a generic object. */
typedef ByteFlags GenAttrs;
#define GA_SIGNAL_INTERACTION_COMPLETE					0x80
#define GA_INITIATES_BUSY_STATE					0x40
#define GA_INITIATES_INPUT_HOLD_UP					0x20
#define GA_INITIATES_INPUT_IGNORE					0x10
#define GA_READ_ONLY					0x08
#define GA_KBD_SEARCH_PATH					0x04
#define GA_TARGETABLE					0x02
#define GA_NOTIFY_VISIBILITY					0x01
/* GenStates store the usability states of a generic object. */
typedef ByteFlags GenStates;
#define GS_USABLE			0x80
#define GS_ENABLED			0x40*

GI_visMoniker chooses the title for the generic object, usually added in an appropriate location within the object by the specific UI. The moniker can be text, in the form of a null-terminated text string, or as a graphics string, usually set up separately. For more information on visual monikers, see Visual Monikers .

GI_comp assigns child objects to the current object (the parent). GI_comp takes a list of generic objects separated by commas as arguments in its instance field. It then sets each of those arguments as children of the current object. In the Goc preprocessor, the GI_comp arguments will be used to create a generic tree structure, reassigning present links using both the GI_comp instance field and an internal instance field, GI_link . The GI_link instance field is therefore an elemental instance field for every generic object but is rarely, if ever, accessed directly. For more information, see Composite Links .

GI_kbdAccelerator sets a given keystroke as a keyboard accelerator for the object. Usually, an object is activated in a manner relating to its specific UI implementation. Pressing the accelerator keystroke will activate the object in the same manner as if its default activation action had occurred. The instance field takes a modifier ( alt , shift , ctrl , or combinations of these) and a character (a valid keyboard character) as its arguments. You can further specify the specific keyboard set to use. For more information, see Keyboard Accelerators .

GI_attrs defines how actions performed by the generic object will affect user interface input and output. You may set any, all, or none of these attributes within your object declaration. For more information, see Attributes .

GI_states defines in what state a generic object will be represented. Default states are GS_USABLE and GS_ENABLED. You may set any, all, or none of these states within your object declaration. For more information, see States .

GenClass: 2.2 GenClass Instance Data: Vardata

GenClass provides a multitude of pre-defined vardata instance fields. You may add these vardata entries to a generic object directly within its object declaration. Vardata instance fields perform several key roles for a generic object. Specifically, they provide two distinct uses:

  • hints to the specific UI on its implementation of an object. Hints always appear in the form HINT_hintname.
  • an attribute data storage area considered part of an object's state data (which may therefore be saved to state). Vardata storage areas act as attributes attached to an object if they are explicitly declared within the object declaration. These data appear in the form ATTR_attributename. These data act in much the same way as fixed instance fields.

Any of these data may be accessed and altered using object system routines. For more information, see the GEOS Programming chapter.

Vardata Attributes

Vardata attributes may contain the same functionality as other instance fields. Variable attributes, however, will only be allocated space within an object declaration if they are actually declared; instance data fields always take up space within an object, whether used or not. Therefore, vardata attributes are ideal for data that should only take up space if actually used. Vardata attributes are prefaced with ATTR_ . Several ATTRs are included in GenClass . See ATTR vardata fields for a complete list of these vardata attributes.

Code Display 2-2 ATTR vardata fields

@vardata void 			ATTR_GEN_PROPERTY;
@vardata void 			ATTR_GEN_NOT_PROPERTY;
@vardata DestinationClassArgs				ATTR_GEN_DESTINATION_CLASS;
    @reloc ATTR_GEN_DESTINATION_CLASS, 0, optr;
@vardata char[]			ATTR_GEN_INIT_FILE_KEY;
@vardata char[]			ATTR_GEN_INIT_FILE_CATEGORY;
@vardata void			ATTR_GEN_INIT_FILE_PROPAGATE_TO_CHILDREN;
@vardata void			ATTR_GEN_USES_HIERARCHICAL_INIT_FILE_CATEGORY;
@vardata Point			ATTR_GEN_POSITION;
@vardata sword			ATTR_GEN_POSITION_X;
@vardata sword			ATTR_GEN_POSITION_Y;
@vardata void			ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED;
@vardata void			ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_ENABLED;
@vardata dword			ATTR_GEN_VISIBILITY_DATA;
@vardata word			ATTR_GEN_VISIBILITY_MESSAGE;
@vardata optr			ATTR_GEN_VISIBILITY_DESTINATION;
    @reloc ATTR_GEN_VISIBILITY_DESTINATION, 0, optr;
@vardata GenFilePath			ATTR_GEN_PATH_DATA;
@vardata ChunkHandle			ATTR_GEN_FEATURE_LINK;
@vardata GenDefaultMonikerType ATTR_GEN_DEFAULT_MONIKER;
@vardata optr 			ATTR_GEN_OUTPUT_TRAVEL_START;
    @reloc ATTR_GEN_OUTPUT_TRAVEL_START, 0, optr;
/* Generic Help attributes */
@vardata char[]			ATTR_GEN_HELP_FILE;
@vardata byte			ATTR_GEN_HELP_TYPE;
@vardata void			ATTR_GEN_HELP_FILE_FROM_INIT_FILE;
@vardata optr			ATTR_GEN_FOCUS_HELP;
@vardata optr			ATTR_GEN_FOCUS_HELP_LIB;
    @reloc ATTR_GEN_FOCUS_HELP_LIB, 0, optr;
@vardata char[]			ATTR_GEN_HELP_CONTEXT;
/* Generic Window attributes */
@vardata MemHandle			ATTR_GEN_WINDOW_CUSTOM_LAYER_ID;
    @reloc ATTR_GEN_WINDOW_CUSTOM_LAYER_ID, 0, optr;
@vardata WinPriority			ATTR_GEN_WINDOW_CUSTOM_WINDOW_PRIORITY;
@vardata LayerPriority			ATTR_GEN_WINDOW_CUSTOM_LAYER_PRIORITY;
@vardata MemHandle			ATTR_GEN_WINDOW_CUSTOM_PARENT;
@vardata void			ATTR_GEN_CUSTOM_WINDOW;
@vardata void			ATTR_GEN_WINDOW_ACCEPT_INK_EVEN_IF_NOT_FOCUSED;
@vardata KeyboardOverride ATTR_GEN_WINDOW_KBD_OVERRIDE;
@vardata Point			ATTR_GEN_WINDOW_KDB_POSITION;

ATTR_GEN_PROPERTY and ATTR_GEN_NOT_PROPERTY affect a generic object's ability to behave as a properties group. These attributes override the property group's default behavior. For more information, see Generic Properties .

ATTR_GEN_DESTINATION_CLASS specifies an object class that should handle all messages sent out along a destination path. The type DestinationClassArgs contains a pointer to a ClassStruct . Typically, this attribute is used with a TravelOption destination to form a search path. The message for that generic object travels down a path specified in the TravelOption until it encounters an object class of type ATTR_GEN_DESTINATION_CLASS ; at that point, the message will be handled. For more information, see Destination Classes .

ATTR_GEN_INIT_FILE_KEY , ATTR_GEN_INIT_FILE_CATEGORY , ATTR_GEN_INIT_FILE_PROPAGATE_TO_CHILDREN , and ATTR_GEN_USES_HIERARCHICAL_INIT_FILE_CATEGORY allow a generic object to load options from the .INI file upon startup (after receiving a MSG_META_ATTACH ). Generic objects with these attributes will receive a MSG_GEN_LOAD_OPTIONS (with the category and key of the proper entry in the .INI file) and may intercept that message to provide additional behavior. For more information, see Initialization File Management .

ATTR_GEN_POSITION , ATTR_GEN_POSITION_X , and ATTR_GEN_POSITION_Y override default geometry management of an object and set a precise position for the object to appear within the UI, offset from the parent object. ATTR_GEN_POSITION specifies both a horizontal and a vertical position to place the object. ATTR_GEN_POSITION_X and ATTR_GEN_POSITION_Y specify either a horizontal or vertical position to align the object along. You should avoid using these attributes because they override default behavior and may result in different (and potentially unsightly) appearances under different specific UIs. For more information, see Altering Default Geometry Management .

ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED and ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_ENABLED affect generic objects that contain an apply message--a message that an object sends out when it wishes to apply changes made within that object. By default, an apply message will not be sent out if the object's state has not changed since the last apply or if the object is not enabled; these attributes tell the object to send out its apply message regardless of these conditions. For more information, see Altering Delayed Mode Activity .

ATTR_GEN_VISIBILITY_DATA , ATTR_GEN_VISIBILITY_MESSAGE , and ATTR_GEN_VISIBILITY_DESTINATION set the data, message or destination to use with the visibility notification mechanism in GenClass . The visibility mechanism allows an application to know when an object becomes visible and not visible. An object provides this notification if its GA_NOTIFY_VISIBILITY bit is set. For more information, see Notification of Visibility .

ATTR_GEN_PATH_DATA stores an object's generic path information in the form of a GenFilePath structure. This structure contains disk and path information including path name and file name; generic objects can use this attribute to store current or default paths and files. For more information, see Generic Paths .

ATTR_GEN_FEATURE_LINK contains a link to the next feature used by the GenControl mechanism when a controller's features map to multiple objects. It is unlikely you will use this attribute. For more information, see Feature Links .

ATTR_GEN_DEFAULT_MONIKER specifies a default moniker for a generic object. This is generally used for graphic monikers that occur several places within the system. For more information, see Default Monikers .

ATTR_GEN_OUTPUT_TRAVEL_START allows you to set a specific object to begin the TravelOption . Normally, objects that contain a TravelOption in their destination field use their own object as the point of reference for sending messages along travel paths. (I.e sending a message TO_FOCUS would send a message along the focus path, starting at the sending object.) This can be used for optimization purposes, enabling you to skip several layers of hierarchies.

ATTR_GEN_HELP_FILE , ATTR_GEN_HELP_TYPE, ATTR_GEN_HELP_FILE_FROM_INIT_FILE, ATTR_GEN_FOCUS_HELP, ATTR_GEN_FOCUS_HELP_LIB, and ATTR_GEN_HELP_CONTEXT specify the configuration of the help files for a generic object. Focus help starts help notification when the generic object gains the focus. For more information on Help files, see Help Management .

ATTR_GEN_WINDOW_CUSTOM_LAYER_ID , ATTR_GEN_WINDOW_CUSTOM_WINDOW_PRIORITY, ATTR_GEN_WINDOW_CUSTOM_LAYER_PRIORITY, ATTR_GEN_WINDOW_CUSTOM_PARENT, and ATTR_GEN_CUSTOM_WINDOW specify a window's interaction and place within the window system. ATTR_GEN_WINDOW_ACCEPT_INK_EVEN_IF_NOT_FOCUSED, ATTR_GEN_WINDOW_KBD_OVERRIDE, and ATTR_GEN_WINDOW_KBD_POSITION affect how input flows within a generic window. For more information, see Window Management .

Hints

Hints to the specific UI suggest added functionality to your application, and are prefaced with HINT_ (such as HINT_SEEK_REPLY_BAR ). Hints may also have data structures attached to them. There are a large number of pre-defined hints with GenClass . See Hints to the Specific UI for details on the operation of hints.

Many of the hints provided in GenClass manage the geometry of generic objects. For complete information on these hints, see the Managing UI Geometry chapter.

Code Display 2-3 Geometry Hints

 /* These hints are explained within the Geometry Management chapter. */
@vardata void			 HINT_DONT_ALLOW_CHILDREN_TO_WRAP;
@vardata void			 HINT_ALLOW_CHILDREN_TO_WRAP;
@vardata word			 HINT_WRAP_AFTER_CHILD_COUNT;
@vardata word			 HINT_WRAP_AFTER_CHILD_COUNT_IF_VERTICAL_SCREEN;
@vardata void			 HINT_BOTTOM_JUSTIFY_CHILDREN;
@vardata void			 HINT_LEFT_JUSTIFY_CHILDREN;
@vardata void			 HINT_RIGHT_JUSTIFY_CHILDREN;
@vardata void			 HINT_TOP_JUSTIFY_CHILDREN;
@vardata void			 HINT_FULL_JUSTIFY_CHILDREN_HORIZONTALLY;
@vardata void			 HINT_FULL_JUSTIFY_CHILDREN_VERTICALLY;
@vardata void			 HINT_DONT_FULL_JUSTIFY_CHILDREN;
@vardata SpecSizeSpec			 HINT_CUSTOM_CHILD_SPACING;
@vardata SpecSizeSpec			 HINT_CUSTOM_CHILD_SPACING_IF_LIMITED_SPACE;
@vardata void			 HINT_DONT_INCLUDE_ENDS_IN_CHILD_SPACING;
@vardata void			 HINT_INCLUDE_ENDS_IN_CHILD_SPACING;
@vardata void 			 HINT_MINIMIZE_CHILD_SPACING;
@vardata void			 HINT_LEFT_JUSTIFY_MONIKERS;
@vardata void			 HINT_CENTER_MONIKER;
@vardata void			 HINT_DO_NOT_USE_MONIKER;
@vardata void			 HINT_ALIGN_LEFT_MONIKER_EDGE_WITH_CHILD;
@vardata void			 HINT_CENTER_CHILDREN_HORIZONTALLY;
@vardata void			 HINT_CENTER_CHILDREN_ON_MONIKERS;
@vardata void			 HINT_CENTER_CHILDREN_VERTICALLY;
@vardata optr			 HINT_ALIGN_LEFT_EDGE_WITH_OBJECT;
    @reloc HINT_ALIGN_LEFT_EDGE_WITH_OBJECT, 0, optr;
@vardata optr			 HINT_ALIGN_TOP_EDGE_WITH_OBJECT;
    @reloc HINT_ALIGN_TOP_EDGE_WITH_OBJECT, 0, optr;
@vardata optr			 HINT_ALIGN_RIGHT_EDGE_WITH_OBJECT;
    @reloc HINT_ALIGN_RIGHT_EDGE_WITH_OBJECT, 0, optr;
@vardata optr			 HINT_ALIGN_BOTTOM_EDGE_WITH_OBJECT;
    @reloc HINT_ALIGN_BOTTOM_EDGE_WITH_OBJECT, 0, optr;
@vardata CompSizeHintArgs			 HINT_FIXED_SIZE;
@vardata CompSizeHintArgs	 		HINT_INITIAL_SIZE;
@vardata CompSizeHintArgs	 		HINT_MAXIMUM_SIZE;
@vardata CompSizeHintArgs	 		HINT_MINIMUM_SIZE;
@vardata void			 HINT_SIZE_WINDOW_AS_DESIRED;
@vardata void			 HINT_DRAW_IN_BOX;
@vardata void			 HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
@vardata void			 HINT_EXPAND_WIDTH_TO_FIT_PARENT;
@vardata void			 HINT_MAKE_REPLY_BAR;
@vardata void			 HINT_NO_TALLER_THAN_CHILDREN_REQUIRE;
@vardata void			 HINT_NO_WIDER_THAN_CHILDREN_REQUIRE;
@vardata void			HINT_ORIENT_CHILDREN_HORIZONTALLY;
@vardata void			 HINT_ORIENT_CHILDREN_VERTICALLY;
@vardata void			 HINT_ORIENT_CHILDREN_ALONG_LARGER_DIMENSION;
@vardata void			 HINT_SAME_ORIENTATION_AS_PARENT;
@vardata void			 HINT_PLACE_MONIKER_ABOVE;
@vardata void			 HINT_PLACE_MONIKER_BELOW;
@vardata void			 HINT_PLACE_MONIKER_TO_LEFT;
@vardata void			 HINT_PLACE_MONIKER_TO_RIGHT;
@vardata void			 HINT_PLACE_MONIKER_ALONG_LARGER_DIMENSION;
@vardata void			HINT_NO_BORDER_ON_MONIKERS
@vardata void			 HINT_SEEK_MENU_BAR;
@vardata void			 HINT_AVOID_MENU_BAR;
@vardata void			 HINT_SEEK_REPLY_BAR;
@vardata void			 HINT_SEEK_X_SCROLLER_AREA;
@vardata void			 HINT_SEEK_Y_SCROLLER_AREA;
@vardata void			 HINT_SEEK_LEFT_OF_VIEW;
@vardata void			 HINT_SEEK_TOP_OF_VIEW;
@vardata void			 HINT_SEEK_RIGHT_OF_VIEW;
@vardata void			 HINT_SEEK_BOTTOM_OF_VIEW;
@vardata void			 HINT_SEEK_TITLE_BAR_LEFT;
@vardata void			 HINT_SEEK_TITLE_BAR_RIGHT;
@vardata void			 HINT_USE_INITIAL_BOUNDS_WHEN_RESTORED;
@vardata void			 HINT_DIVIDE_WIDTH_EQUALLY;
@vardata void			 HINT_DIVIDE_HEIGHT_EQUALLY;
@vardata void			 HINT_KEEP_INITIALLY_ONSCREEN;
@vardata void			 HINT_DONT_KEEP_INITIALLY_ONSCREEN;
@vardata void			 HINT_KEEP_PARTIALLY_ONSCREEN;
@vardata void			 HINT_DONT_KEEP_PARTIALLY_ONSCREEN;
@vardata void			 HINT_KEEP_ENTIRELY_ONSCREEN;
@vardata void			 HINT_KEEP_ENTIRELY_ONSCREEN_WITH_MARGIN;
@vardata void			 HINT_POPS_UP_TO_RIGHT;
@vardata void			 HINT_POPS_UP_BELOW;
@vardata void			 HINT_NOT_MOVABLE;
@vardata SpecWinSizePair			 HINT_POSITION_WINDOW_AT_RATIO_OF_PARENT;
@vardata void			 HINT_POSITION_WINDOW_AT_MOUSE;
@vardata void			 HINT_WINDOW_NO_TITLE_BAR;
@vardata void			 HINT_WINDOW_NO_SYS_MENU;
@vardata void			HINT_WINDOW_MINIMIZE_TITLE_BAR;
@vardata void			HINT_WINDOW_ALWAYS_DRAW_WITH_FOCUS;
@vardata void			 HINT_STAGGER_WINDOW;
@vardata void			 HINT_CENTER_WINDOW;
@vardata void			 HINT_TILE_WINDOW;
@vardata void			 HINT_EXTEND_WINDOW_TO_BOTTOM_RIGHT;
@vardata void			 HINT_EXTEND_WINDOW_NEAR_BOTTOM_RIGHT;
@vardata SpecWinSizePair			 HINT_SIZE_WINDOW_AS_RATIO_OF_PARENT;
@vardata SpecWinSizePair			 HINT_SIZE_WINDOW_AS_RATIO_OF_FIELD;
@vardata void			 HINT_WINDOW_NO_CONSTRAINTS;
@vardata SpecWinSizePair			 HINT_POSITION_ICON_AS_RATIO_OF_FIELD;

Other hints in GenClass do not affect geometry. These hints are listed below and are explained within this chapter. Within GenClass , some hints are internal and are not listed in this code display.

Code Display 2-4 Hints Explained Within This Chapter

/* These hints are explained within this chapter (or they are self-explanatory). */
@vardata void			 HINT_FREQUENTLY_USED;
@vardata void			 HINT_INFREQUENTLY_USED;
@vardata void			 HINT_AN_ADVANCED_FEATURE;
@vardata void			HINT_DEFAULT_DEFAULT_ACTION;
@vardata void			 HINT_ENSURE_TEMPORARY_DEFAULT;
@vardata void			 HINT_PREVENT_DEFAULT_OVERRIDES;
@vardata void			 HINT_SAME_CATEGORY_AS_PARENT;
@vardata void			 HINT_USE_TEXT_MONIKER;
@vardata void			 HINT_USE_ICONIC_MONIKER;
@vardata void			 HINT_DEFAULT_FOCUS;
@vardata void			 HINT_DEFAULT_TARGET;
@vardata void			 HINT_DEFAULT_MODEL;
@vardata void			 HINT_PRESERVE_FOCUS;
@vardata void			 HINT_DO_NOT_PRESERVE_FOCUS;
@vardata void			 HINT_GENERAL_CONSUMER_MODE;
@vardata void			 HINT_NEVER_ADOPT_MENUS;
@vardata void			 HINT_ALWAYS_ADOPT_MENUS;
@vardata void			 HINT_NAVIGATION_ID;
@vardata void			 HINT_NAVIGATION_NEXT_ID
@vardata void			 HINT_DISMISS_WHEN_DISABLED;
@vardata void			HINT_TOOLBOX;
@vardata void			 HINT_SHOW_SHORTCUT;
@vardata void			 HINT_DONT_SHOW_SHORTCUT;
@vardata void			HINT_DRAW_SHORTCUT_BELOW;
@vardata void			 HINT_CAN_CLIP_MONIKER_WIDTH;
@vardata void			 HINT_CAN_CLIP_MONIKER_HEIGHT;
@vardata void			 HINT_SHOW_ENTIRE_MONIKER;
@vardata BackgroundColors				 HINT_GADGET_BACKGROUND_COLORS;
@vardata word			 HINT_GADGET_TEXT_COLOR;
@vardata SystemAttrs			 HINT_IF_SYSTEM_ATTRS;
@vardata void			 HINT_ENDIF;
@vardata void			 HINT_ELSE;

HINT_FREQUENTLY_USED, HINT_INFREQUENTLY_USED, and HINT_AN_ADVANCED_FEATURE are descriptive hints that describe the generic object's role within the UI. It is, of course, left up to the specific UI to implement any special behavior based on these hints.

HINT_DEFAULT_DEFAULT_ACTION , HINT_ENSURE_TEMPORARY_DEFAULT, and HINT_PREVENT_DEFAULT_OVERRIDES affect the default activation of objects within the UI. Objects with default activation can be activated indirectly (e.g., pressing the ENTER key within a dialog box activates a default trigger). For more information, see Default Actions .

HINT_SAME_CATEGORY_AS_PARENT indicates to the specific UI that this object should be treated in the same manner as its parent. Currently, this hint only affects GenInteractions. By default, GIV_SUB_GROUP GenInteractions within a GIV_POPUP menu have separator lines drawn around them to set them apart from other children. This hint removes those lines.

HINT_USE_TEXT_MONIKER and HINT_USE_ICONIC_MONIKER indicates that the generic object should choose either a text moniker or an iconic moniker if there exists a choice within the object's moniker list. Currently, these hints are not implemented.

HINT_DEFAULT_FOCUS , HINT_DEFAULT_TARGET, and HINT_DEFAULT_MODEL specify that the generic object is the default focus, target, or model node at this level in the application. HINT_PRESERVE_FOCUS and HINT_DO_NOT_PRESERVE_FOCUS affect how the focus node changes after an object's activation. For a complete discussion of the focus and target hierarchies, see the Input chapter.

HINT_GENERAL_CONSUMER_MODE instructs the specific UI to show the object in GCM mode. This hint takes an argument of type GeneralConsumerModeFlags ; this record contains two three-bit flags: GCMF_LEFT_ICON and GCMF_RIGHT_ICON, which specify which icon to use on both the left and right sides of the title bar. Each icon must be of type GCMIcon ; current types are GCMI_NONE, GCMI_EXIT, and GCMI_HELP.

HINT_NEVER_ADOPT_MENUS and HINT_ALWAYS_ADOPT_MENUS affect the behavior of menus placed within a GenDisplay. Menus can either sit within the GenDisplay or appear in the associated GenPrimary when that GenDisplay is the target. Menus within GenDisplays are discouraged, however, as they are a fairly advanced UI metaphor.

HINT_NAVIGATION_ID and HINT_NAVIGATION_NEXT_ID allow the field navigation path within a windowed object. HINT_NAVIGATION_NEXT_ID sets the object to navigate to from the current object. An object with a matching HINT_NAVIGATION_ID must exist. For more information, see Keyboard Navigation Hints .

HINT_DISMISS_WHEN_DISABLED instructs the specific UI to dismiss the object when it is no longer fully enabled. This is typically useful for independently-displayable windows that should be dismissed in such cases.

HINT_TOOLBOX indicates that the generic object and all objects below it in the visual hierarchy are part of a "toolbox" and should draw themselves appropriately. Objects in toolboxes are typically much smaller than other components. For more information on toolboxes, see the Menus and Dialogs chapter.

HINT_SHOW_SHORTCUT , HINT_DONT_SHOW_SHORTCUT, and HINT_DRAW_SHORTCUT_BELOW affect the display of keyboard accelerators for generic objects. For more information, see Keyboard Accelerators .

HINT_CAN_CLIP_MONIKER_WIDTH and HINT_CAN_CLIP_MONIKER_HEIGHT instruct the specific UI to clip the object's moniker height or width if not enough space is provided for them. Objects with HINT_SHOW_ENTIRE_MONIKER will force the object to leave enough space to show its entire moniker, if possible.

HINT_GADGET_BACKGROUND_COLORS causes grouping objects in toolboxes to draw custom background colors. The hint takes an argument of type BackgroundColors . Two colors can each be chosen for both the unselected state and the selected state; they will be blended together depending on the state of the object. If both colors are set to the same value, a solid color results. HINT_GADGET_TEXT_COLOR selects the text color of a visual moniker, typically within a toolbox. This hint takes an argument of type TextColors .

HINT_IF_SYSTEM_ATTRS , HINT_ENDIF, and HINT_ELSE may be used together to conditionally add hints to an object based on certain system criteria. If the SystemAttrs set in the HINT_IF_SYSTEM_ATTRS field are true for the current system, then the hints that follow (until a HINT_ENDIF is encountered) are included. If no HINT_ENDIF is encountered, then only the next hint is included. If the SystemAttrs do not match the current system, the following group of hints is deleted. For more information, see System Attributes .

Remember that these vardata entries are hints; they can be ignored by the specific UI and their implementation varies from object to object. Some hints, for example, are only relevant for certain object classes. Your mileage may vary.

GenClass: 3 GenClass Basics

In most cases, you will set much of your generic object's instance data upon instantiation according to the values you include in your .goc file. You may also update and manipulate this instance data during execution with a variety of messages. The following section deals only with directly setting instance fields in your .goc file. For information on dynamically altering this instance data, see Modifying GenClass Instance Data .

Visual Monikers

Composite Links

Keyboard Accelerators

Attributes

States

GenClass: 3.1 GenClass Basics: Visual Monikers

A visual moniker is a text or graphics string attached to a generic object. This visual moniker displays the given string in a manner relevant to the specific implementation of an object. For example, a GenTrigger might create a button with text inserted as the object's main visual implementation. The visual moniker in this case might cover the object's total area. A GenPrimary, on the other hand, might only add such text in a title bar of the window. In all cases, the specific user interface has final control over how a visual moniker is displayed to the user.

Simple Visual Monikers

You may set the visual moniker of an object using the GI_visMoniker instance field. This instance field expects an argument of type @visMoniker . The visual moniker of an object is not actually stored within this instance field, however. The visual moniker of an object is stored within its own chunk, outside of the object itself. The GI_visMoniker instance field contains a chunk handle to this visual moniker chunk. Because a visual moniker can only be referenced by a chunk handle, visual monikers for an object must reside in the same block as the object that points to them. The UI will set up this chunk and chunk handle automatically.

Within the object's definition, set the GI_visMoniker field to the desired visual moniker (either a text or graphics string). This can be done either by directly setting the GI_visMoniker field to the string or by indirectly setting the field to a visual moniker chunk defined elsewhere within your user interface with the Goc keyword @visMoniker . (See Setting Visual Monikers .)

Code Display 2-5 Setting Visual Monikers

/* A visual moniker can be set directly within the object's definition. */
@object GenTriggerClass MyTrigger = {
    GI_visMoniker = "Push Me";
}
/* A visual moniker may also be defined indirectly within a .goc file. */
/* The data for MyMoniker is set using the Goc keyword @visMoniker. This moniker
 * must reside in the same resource block as the object using it. This moniker
 * should be declared before being used in an object declaration. */
@visMoniker MyMoniker = "Push Me";
@object GenTriggerClass MyTrigger = {
    GI_visMoniker = @MyMoniker;
}

Remember that the GI_visMoniker instance field only stores a chunk handle to the actual string. The visual moniker for the object is not contained in the object chunk itself.

Text monikers may also specify a character to act as a mnemonic. Mnemonics are keyboard shortcuts used to activate an object without use of the mouse. Menus, menu items, and buttons frequently have mnemonic characters attached. This allows a user to navigate quickly to other UI objects or to activate them. If the specified character is within the text moniker, that letter will be highlighted in whatever fashion the specific UI decides is relevant. For instance, in OSF/Motif, mnemonic characters are underlined.

Usually the first letter of the text moniker is used for the mnemonic, though any character may be used. The specific UI will underline the first such occurrence of the character within the text moniker. If the character is not within the text itself, the specific UI may place the character within parentheses at the end of the text moniker. Mnemonics are activated according to specifications in the specific UI. In OSF/Motif, pressing the ALT key puts the user interface into its keyboard navigation mode. In this mode, all mnemonics for objects in the focus are activated by pressing the character of the mnemonic without need of an additional keystroke.

Mnemonics are set by enclosing the desired character in single quotes before the text string in your instance data. Mnemonics are case specific when defined. For example, if a visual moniker is the text "File," only an uppercase mnemonic of F would highlight the first character in the text. Mnemonics are not case specific in their activation methods, however. For example, either alt -f or alt -F will activate a keyboard mnemonic of F.

Mnemonics are only valid for an object if that object is visually displayed and currently has the focus (see the Input chapter). Therefore, you may duplicate mnemonics for objects that will not exist at the same focus level. (An example of objects at the same focus level would be objects within the same menu.) Be careful not to duplicate the same mnemonic within the same focus level, as the UI will only associate the mnemonic with the first object that matches it.

The mnemonic may also store one of the following constants:

VMO_CANCEL corresponds to the specific UI defined cancel mnemonic. Use this rather than a special cancel mnemonic of your own choosing to keep the UI consistent.
VMO_MNEMONIC_NOT_IN_MKR_TEXT indicates that the mnemonic occurs after the null-terminator for the moniker text. This moniker is itself not null-terminated.
VMO_NO_MNEMONIC indicates that this moniker contains no mnemonic.

If you need other functionality that requires keyboard control of some form and mnemonics are not satisfactory, see Keyboard Accelerators .

Code Display 2-6 Setting Mnemonic Characters

/* Mnemonics are case specific. If the following example enclosed f instead of
 * F within single quotes, then the character f in parentheses would follow the
 * text "File." Note that this case specificity does not apply to the user's
 * activation of the object. That is, alt f will activate a mnemonic of F. */
@object MyTrigger GenTriggerClass {
    GI_visMoniker = `F', "File";
}
/* You can also specify the character to highlight with an actual numerical
 * position, counting from a zero-based (1st character is 0, 2nd character is 1,
 * etc.) character position in the text string. The "5" in the following example
 * will underline the F character (the sixth character in the text string.) */
    GI_visMoniker = 5, "Open File";

Graphics String Visual Monikers

The GI_ visMoniker instance field recognizes several keywords. These keywords are listed below and are usually only used in the construction of gstring visual monikers.

  • style = { text , abbrevText , graphicText , icon , tool }
    This keyword specifies the VMStyle in use by this visual moniker.

    text Normal text moniker
    abbrevText Abbreviated text moniker (i.e., a short textual
    description rather than the full title). This is used
    for the name under an icon of an iconified
    GenPrimary.
    graphicText Textual gstring
    icon Normal gstring moniker
    tool Moniker for a tool, usually smaller than a standard
    visual moniker
  • size = { tiny , standard , large , huge }
    This keyword specifies the DisplaySize that this moniker is intended for. It has nothing to do with the actual size of the moniker, which can be set with the keyword `cachedSize.' Each size corresponds to a display type's resolution level.

    tiny Tiny screens: CGA, 256x320.
    standard Standard screens: EGA, VGA, HGC, MCGA
    large Large screens: 800x680 SVGA
    huge Huge screens

    You should not, in general, use monikers whose sizes are larger than their intended display type. (I.e. using `huge' sized monikers on VGA screens.) You may use smaller monikers on larger screens, however.
  • color = { gray1 , gray2 , gray4 , gray8 , color2 , color4 , colorRGB }
    This keyword specifies the color capability of the display that this setting is intended for. Each setting corresponds to a DisplayClass .

    gray1 1 bit/pixel gray scale
    gray2 2 bit/pixel gray scale
    gray4 4 bit/pixel gray scale
    gray8 8 bit/pixel gray scale
    color2 2 bit/pixel color index
    color4 4 bit/pixel color index
    color RGB color with RGB values
  • aspectRatio = { normal , squished , verySquished }
    This keyword specifies the aspect ratio of the display that this moniker is intended for.

    normal VGA, MCGA
    squished EGA, HGC (though normal is usually used)
    verySquished CGA
  • cachedSize = { X , Y }
    This keyword sets the cached size (pixel by pixel) of the moniker.
  • gstring { < GString OpCodes > }
    This keyword stores the gstring operations used in creating the visual moniker. These operations may also specify the creation of a bitmap by using the Bitmap operator.

Code Display 2-7 Examples of GString Visual Monikers

/* A Graphics string consisting of GString opcodes. */
    GI_visMoniker = {
    size = tiny;
    color = color4;
    aspectRatio = normal;
    cachedSize = 15,15;
    gstring{
        GSSaveTransform(),
        GSApplyRotation(45),
        GSFillEllipse(0,0,10,15),
        GSRestoreTransform(),
        GSDrawEllipse(0,0,10,15),
        GSEndString()
    }
    }
/* A Graphics string containing a bitmap. */
@visMoniker MyBitmap = {
    style = icon;
    size = standard;
    color = color4;
    aspectRatio = normal;
    cachedSize = 64, 40;
    gstring {
        GSDrawBitmapAtCP(166),
        Bitmap (64,40,BMC_PACKBITS, (BMT_MASK|BMF_4BIT)),
        251, 0,
        233, 221,
        ...,
        GSEndString()
    }
    }

Visual Moniker Lists

In some cases, an application may wish to use different visual monikers under specific circumstances. For example, a GenApplication's icon may need separate graphics strings for different display types (VGA, SVGA, etc.) In such cases, the argument for the moniker instance field should be given as a list of separate and distinct visual monikers. (See Simple Lists .)

You may then place each of these monikers within its own separate resource. Because only one moniker will be selected from the list, only one resource will be loaded into the object block, thereby conserving memory. When the application selects its appropriate moniker, the list will be replaced with the specific moniker, copied into the object block. The system performs this function automatically.

Code Display 2-8 Simple Lists

/* A list of monikers. */
    GI_visMoniker = list {
            @moniker1,
            @moniker2,
            @moniker3
        }
/* If several monikers are specified in a list, they must be explicitly defined
 * somewhere else. If these monikers are complicated (as in the case of graphics
 * strings) they should each reside within their own resource because they will be
 * loaded in only once per application run. (Those that will be used together can
 * be within the same resource.) For example, in GEOS, several monikers
 * pertaining to different display types would be placed within separate resources.
 * When the appropriate moniker is selected, the list will be replaced with the
 * specific moniker. */
@start AppMonikerOneResource, notDetachable;
@visMoniker moniker1 = {
    size = large;
    color = color4;
    aspectRatio = normal;
    cachedSize = 64, 40;
    gstring {gstring data}
}
@end AppMonikerOneResource;
/* That moniker could then be declared within the object's instance data. */
@object GenPrimaryClass MyObject = {
    GI_visMoniker = list { @moniker1 }
}
/*
 * The GenApplication object usually contains a moniker list that allows the
 * specific UI to select a moniker based on the display.
 */
@object GenApplicationClass MyApplication = {
    GI_visMoniker = list {
    @TrigTextMoniker,			/* a simple text string */
    @TrigLCMoniker,			/* Large Color */
    @TrigLMMoniker,			/* Large Mono */
    @TrigSCMoniker,			/* Small Color */
    @TrigSMMoniker,			/* Small Mono */
    @TrigLCGAMoniker,			/* Large CGA */
    @TrigSCGAMoniker			/* Small CGA */
    }
}
@visMoniker TrigTextMoniker = "Push Me";
/* Graphics monikers might then appear within their own resource block. This
 * enables efficient memory management. */
@start AppMonikerResource, notDetachable;
@visMoniker TrigLCMoniker = {
    style = icon;
    size = large;
    color = color4;
    aspectRatio = normal;
    cachedSize = 64, 40;
    gstring {
    GSDrawBitmapAtCP(166),
    Bitmap (64,40,BMC_PACKBITS, (BMT_MASK|BMF_4BIT)),
    /*** insert Bitmap here ***/
    GSEndString()
    }
}
@end AppMonikerResource

The Inner Workings of Visual Monikers

The following section explains the inner workings of visual monikers within GEOS. It is not necessary to understand many of these concepts but it is illustrative of the system, and may aid in debugging and custom moniker use.

GI_visMoniker can take several types and combinations of arguments, all involving either text or graphics strings. Specifically, GI_visMoniker may indicate the following:

1) a simple text string
2) a text string with a mnemonic character
3) a graphics string consisting of GString opcodes
4) a graphics string containing a bitmap
5) a list of monikers of any type above.

Each of these arguments can be set up with the @visMoniker Goc keyword, but each will store their data in different manners. GEOS automatically sets up the visual moniker in the correct format.

All visual monikers make use of the VisMoniker structure. This basic structure indicates whether the moniker is text, a gstring, or a list of several types.

Code Display 2-9 The Basic VisMoniker Structure

/* The Basic VisMoniker structure contains a header which describes the type of
 * VisMoniker (VisMonikerType) and stores the cached width (width in pixels) of the
 * VisMoniker. The actual visual moniker data (either text or a gstring) follows
 * this header.
 *
 * If the visual moniker is text, this VisMoniker structure is contained within a
 * VisMonikerWithText structure. If the visual moniker is a gstring, this
 * VisMoniker structure is contained within a VisMonikerWithGString structure.*/
typedef struct {
    byte		VM_type;		/* VisMonikerType */
    word		VM_width;		/* Cached width of moniker */
} VisMoniker;
/* VisMonikerType specifies the type of moniker contained in the VisMoniker
 * structure.
 *
 * The flag VMT_MONIKER_LIST is actually a dummy flag. (This flag is never set
 * within a VisMoniker structure.) VisMonikerListEntryType has a matching flag
 * in the same location (VMLET_MONIKER_LIST). If that flag is set, it tells the
 * system that this isn't actually a VisMoniker structure but is instead a
 * VisMonikerListEntry. This is used in moniker lists (see below).
 *
 * The flag VMT_GSTRING is set if the visual moniker is in the form of a gstring
 * instead of a simple text string. If this flag is set, VMT_GS_ASPECT_RATIO and
 * VMT_GS_COLOR specify the DisplayAspectRatio and DisplayClass used by this
 * gstring.
 */
typedef ByteFlags VisMonikerType;
#define VMT_MONIKER_LIST				0x80
#define VMT_GSTRING				0x40
#define VMT_GS_ASPECT_RATIO				0x30	/* DisplayAspectRatio */
#define VMT_GS_COLOR				0x0f	/* Color */
#define VMT_GS_ASPECT_RATIO_OFFSET 4
#define VMT_GS_COLOR_OFFSET 0

If the visual moniker is a simple text string, the ChunkHandle within GI_ visMoniker will point to a chunk containing a VisMonikerWithText structure. This chunk will contain the basic VisMoniker header, along with the moniker's mnemonic character and a null-terminated text-string.

Code Display 2-10 VisMonikers With Text

/* If the VisMoniker contains simple text, the ChunkHandle within GI_visMoniker
 * points to a VisMonikerWithText structure. This structure contains the
 * basic VisMoniker header and the character of the mnemonic. (A value of -1 is
 * stored in VMWT_mnemonicOffset if there is no mnemonic for this visual moniker.)
 */
typedef struct {
    VisMoniker		VMWT_common;
    char		VMWT_mnemonicOffset;
} VisMonikerWithText;
/* The text, in the form of a null-terminated text string, follows this structure.
 * This text may be accessed with the VMWT_text offset. */
#define VMWT_text		(sizeof(VisMonikerWithText)) /* Start of text. */

If the visual moniker is a gstring, the ChunkHandle within GI_ visMoniker will point to a VisMonikerWithGString structure instead. This structure will contain the basic VisMoniker header, along with the moniker's cached height and the actual gstring. (The cached width is stored within the VisMoniker header.)

Code Display 2-11 VisMonikers With GStrings

/* If the VisMoniker contains a gstring, the ChunkHandle within GI_visMoniker
 * points to a VisMonikerWithGString structure. This structure contains the
 * basic VisMoniker header and the cached height of the gstring. (The cached width
 * is stored within the VisMoniker structure.
 */
typedef struct {
    VisMoniker		VMWGS_common;
    word		VMWGS_height;
} VisMonikerWithGSTring;
/* The gstring follows this structure.This gstring may be accessed (though it is
 * not recommended) with the VMWGS_gString offset. */
#define VMWGS_gString		(sizeof(VisMonikerWithGString)) /* Start of gstring. */

If instead of a single visual moniker, be it a text string or a gstring, GI_ visMoniker specifies a list of monikers, the case is more complex.

If GI_ visMoniker contains a list of monikers, that ChunkHandle will point to a group of VisMonikerListEntry structures (one for each moniker in the list). Each of these list entries will contain the type of moniker it references and the optr of the moniker it refers to. The actual moniker itself is not stored in that chunk. Also, because the moniker may be referred to by an optr, the actual visual monikers may reside in separate resources.

When the object containing the moniker list is first built, the system will select one of the monikers in the list (based on matching criteria in the VMLE_ type field) and replace the moniker list with the single selected visual moniker.

Code Display 2-12 VisMoniker Lists

/* If GI_visMoniker contains a list of monikers instead of a single moniker, the
 * ChunkHandle of that instance field actually points to a collection of
 * VisMonikerListEntry structures. (The total number can be calculated by dividing
 * the size of the chunk by sizeof(VisMonikerListEntry) if needed.)
 *
 * Each VisMonikerListEntry structure contains a header which describes the type of
 * VisMoniker stored in that list entry (VisMonikerListEntryType) and the optr of
 * the stored moniker. The actual moniker may reside in a different resource. */
typedef struct {
    word		VMLE_type;
    optr		VMLE_moniker;
} VisMonikerListEntry;
/* VisMonikerListEntryType specifies the type of moniker specified in the
 * VisMonikerListEntry structure.
 *
 * The flag VMLET_MONIKER_LIST must be set within this structure. This flag tells
 * the system that this isn't actually a VisMoniker structure but is instead a
 * VisMonikerListEntry.
 *
 * The system uses the other flags in this structure to determine the type of
 * moniker contained as the list entry. The system will use this information to
 * select the most appropriate moniker that will satisfy the system's needs. This
 * method is used most often in the selection of a GenPrimary's main application
 * moniker based on the DisplaySize of the system.
 *
 * VMLET_GS_SIZE stores the DisplaySize that this moniker is most appropriate for.
 * This DisplaySize is set using the "size" entry within the visual moniker
 * declaration.
 *
 * VMLET_STYLE stores the VMStyle that this moniker most closely matches. This
 * VMStyle is set using the "style" entry within the visual moniker declaration.
 *
 * VMLET_GSTRING specifies that this moniker list entry is in the form of a gstring
 * If this flag is set, VMLET_GS_ASPECT_RATIO and VMLET_GS_COLOR specify the
 * DisplayAspectRatio and DisplayClass used by this gstring.
 */
typedef ByteFlags VisMonikerListEntryType;
#define VMLET_GS_SIZE				0x0300 	/* DisplaySize. */
#define VMLET_STYLE				0x0f00	/* VMStyle */
#define VMLET_MONIKER_LIST				0x0080
#define VMLET_GSTRING				0x0040
#define VMLET_GS_ASPECT_RATIO				0x0030	/* DisplayAspectRatio */
#define VMLET_GS_COLOR				0x000f	/* DisplayClass */
#define VMLET_GS_SIZE_OFFSET	12
#define VMLET_STYLE_OFFSET 8
#define VMLET_GS_ASPECT_RATIO_OFFSET 4
#define VMLET_GS_COLOR_OFFSET 0
/* VMStyle specifies the style of the visual moniker. The system may select a
 * moniker based on the style it wishes to display. */
typedef ByteEnum VMStyle;
#define VMS_TEXT 0				/* Simple text */
#define VMS_ABBREV_TEXT 1				/* Abbreviated text */
#define VMS_GRAPHIC_TEXT 2				/* Textual graphics string */
#define VMS_ICON 3				/* Normal gstring */
#define VMS_TOOL 4				/* Tool-sized gstring */

For information on manipulating visual monikers dynamically using GenClass messages, see Managing Visual Monikers .

GenClass: 3.2 GenClass Basics: Composite Links

Composite links form the connections between parent objects and child objects within a generic tree (see the UI Overview). These links are set up using the GI_comp and GI_link instance fields. The GI_comp field points to an object's first child. The GI_link field points to an object's next sibling or to its parent if no next sibling exists. In Goc, however, this usage is greatly simplified. The developer only needs to set the GI_comp field equal to its complete list of children for the parent object. The Goc preprocessor will then create and reassign all necessary links.

Code Display 2-13 Using GI_comp to Add Children

/* The GenInteraction (MyInteraction) acts as the parent object for the three
 * child GenTriggers. All three GenTrigger children will be placed within the
 * GenInteraction object. */
@object GenInteractionClass MyInteraction = {
    GI_visMoniker = "Menu";						 /* Text Moniker */
    GII_visibility = GIV_POPUP;						 /* Creates a Menu */
    GI_comp = @MyFirstChild, @MySecondChild, @MyThirdChild;
                        /* list of children */
}
@object GenTriggerClass MyFirstChild= {
    GI_visMoniker = "Child 1";					/* Text Moniker */
}
@object GenTriggerClass MySecondChild = {
    GI_visMoniker = "Child 2";					/* Text Moniker */
}
@object GenTriggerClass MyThirdChild = {
    GI_visMoniker = "Child 3";					/* Text Moniker */
}

This simple functionality is all you need to know to add children to your generic objects (and thus to create generic trees). However, it is somewhat helpful in certain cases (as in debugging) to understand what takes place underneath the surface. When an object in GEOS is assigned children, the preprocessor actually only assigns one composite link ( GI_comp ) to the first child. Each additional child acquires a link from its previous sibling using the internal instance field GI_link . Therefore, the parent will have a GI_comp to its first child, the first child will have a GI_link to the next sibling (the parent's second child) and so forth. The last sibling (the parent's last child) will have a GI_link back to the parent. This GI_link will have the LF_IS_PARENT bit set to indicate that the child points to a parent and not to a sibling. This forms what amounts to a circular linked list rather than a branching tree structure. (See the figure below.)

This structure provides a simple and convenient usage. Any object will always have at most two links to other children or parent objects. Therefore the two instance fields GI_comp and GI_link provide the entire means of constructing a generic tree.

You can travel anywhere in the generic tree through these two links. For example, for MyInteraction to communicate with MyThirdChild, it follows the path of the GI_comp to the first child, MyFirstChild, and then continues through the two GI_link s to the third child. Conversely, a child can reach a parent by travelling along the GI_link s of siblings until it reaches the last sibling, whose GI_link points to its parent object.

GenClass message handlers provide several means of pointing to the proper parent/child object without needing to explicitly state the proper GI_comp and GI_link paths. In practice, you will never need to, and in fact should not, manipulate the GI_link field at all. This information is provided merely for your use in debugging your applications. In fact, you can usually assume that a conventional branching tree structure exists rather than the linked-list tree structure shown here.

For information on manipulating these links dynamically using GenClass messages, see Generic Trees .

GenClass: 3.3 GenClass Basics: Keyboard Accelerators

Keyboard accelerators permit the user to activate objects without the use of default activations in the specific UI. (Much of the default activations in most graphical user interfaces use the mouse, for example.) In most cases, these keyboard accelerators require the use of a modifier (the alt , shift or ctrl key) and another key. The keyboard accelerator may also be a function key (F10, etc.). In general, you should avoid using the alt key because of its special role as a keyboard mnemonic within a visual moniker (see Visual Monikers ).

Keyboard accelerators may be activated at any time; they are valid at any point within the application whether the generic object involved is being displayed currently or not. (The only exception to this is during the display of a modal dialog box, which will block input to any other part of the application.) The system will search through the generic tree for the object containing the proper key sequence. In this manner, accelerators differ from mnemonics, which are only valid for an active level. Therefore, only use keyboard accelerators for heavily utilized commands; this prevents a glut of modified keystrokes, which may confuse the user. In general, it is good practice to use the "control" key exclusively for keyboard accelerators to prevent overlapping with GEOS keyboard acclelators and mnemonics.

You can set up keyboard accelerators using the GI_kbdAccelerator attribute. A keyboard accelerator must contain a valid modifier such as alt , shift , control , or ctrl . You may use any alpha-numeric character as the action key for the modifier to act on. In addition to alpha-numeric characters, the action keys shown above are also available.

The specific UI has final control over how and whether a keyboard accelerator will be implemented. Currently, keyboard accelerators will only work for GenTriggers and GenItems. In most cases, a keyboard accelerator will be displayed alongside the text for the visual moniker of an object. In OSF/Motif, keyboard accelerators are shown to the right of menu items and to the left of other items). You may add HINT_DRAW_SHORTCUT_BELOW on non-menu items to draw the keyboard accelerator below the object's visual moniker.

Code Display 2-14 Using a Keyboard Accelerator

@object GenTriggerClass MyTrigger = {
    /* Case is ignored for keyboard accelerators. */
    GI_kbdAccelerator = ctrl `R';					/* `ctrl' and `control' are both valid. */
}
/ * Other examples:
    GI_kbdAccelerator = control `A';
   Case is ignored for keyboard accelerators (unlike mnemonics). If you need
   an uppercase `A', use `shift' as a modifier.
    GI_kbdAccelerator = control shift `A';
    GI_kbdAccelerator = alt `z';
    GI_kbdAccelerator = alt shift `Z';
    GI_kbdAccelerator = ctrl shift `G';
   If you know what specific keyboard set will be implemented, you can add such
   special characters with the @specificUI keyword, though this is discouraged.
    GI_kbdAccelerator = @specificUI alt shift DELETE;
    GI_kbdAccelerator = @specificUI F6;						*/

For information on manipulating keyboard accelerators dynamically, see Managing Keyboard Accelerators .

GenClass: 3.4 GenClass Basics: Attributes

GI_attrs, MSG_GEN_GET_ATTRIBUTES, MSG_GEN_SET_ATTRS

The GI_attrs attribute is a record specifying how an object will behave under various circumstances. In some cases, the GI_attrs instance field indicates that an object may initiate a busy state within the application. Several attributes only affect how an object will behave during activation. Prior to an object's activation, some of these attributes will not affect the object's behavior.

GA_SIGNAL_INTERACTION_COMPLETE
This flag instructs the UI that an Interaction has been completed. The specific UI may then decide whether to dismiss the Interaction or not. Most often, this attribute is attached to a GenTrigger within an independently displayable GenInteraction (dialog box). This attribute is useful for dismissing temporary dialogs that request information from the user. This attribute will cause the generic object to send a MSG_GEN_GUP_INTERACTION_COMMAND to its generic parent with the InteractionCommand IC_INTERACTION_COMPLETE. This message is then passed up the generic tree until it reaches an appropriate Interaction. See the Menus and Dialog Boxes chapter for more information.

GA_INITIATES_BUSY_STATE
This flag instructs the UI to mark the application as busy whenever the generic object is activated. You should only set this attribute for objects that may initiate long operations. This attribute requests that the UI visually change the cursor to show that the application is busy. (In OSF/Motif this is represented by an hourglass.) When the application finishes its operation, the cursor will revert to its former state. This attribute causes the object to send MSG_GEN_APPLICATION_MARK_BUSY to the object's application object along with MSG_GEN_APPLICATION_MARK_NOT_BUSY sent via the process' event queue. This allows the system to process the first message (showing the busy cursor) and only process the second message (removing the busy cursor) after the application finishes its current operation. (If the action is processed quickly, the cursor will often not appear.) The busy state cursor is only reflected in the current application; if the cursor roams outside the application bounds, the default behavior will occur.

GA_INITIATES_INPUT_HOLD_UP
This flag instructs the UI to mark the application as busy and to delay processing input messages until the UI and Application queues have been flushed. The application will then complete its current operation before beginning to process its input events. You should set this attribute when an object's activation may modify the UI, thereby preventing the user from clicking on objects that may become invalid after the operation. This attribute immediately sends a MSG_GEN_APPLICATION_HOLD_UP_INPUT to the application object along with a MSG_GEN_APPLICATION_RESUME_INPUT delayed via the process' event queue. This functionality is only reflected in the current application; if the cursor roams outside the application bounds, the default behavior will occur.
GA_INITIATES_INPUT_IGNORE
This flag instructs the UI to mark the application as busy and to completely ignore all subsequent input events to the application. The application will enter a modal state, meaning that all other application events will be ignored until the UI and Application queues are flushed. Usually, this state is conveyed to the user by broadcasting an audible beep whenever an input event is attempted. The generic object will immediately send a MSG_GEN_APPLICATION_IGNORE_INPUT to the application object along with a MSG_GEN_APPLICATION_ACCEPT_INPUT delayed via the application queue. This functionality is only reflected in the current application; if the cursor roams outside the application bounds, the default behavior will occur.
GA_READ_ONLY
This flag indicates that this object's only function is to display information to the user; the user will not be able to interact with this object. This attribute is set most often in lists or text objects.
GA_KBD_SEARCH_PATH
This flag indicates that this generic branch contains objects with keyboard accelerators and should therefore be searched when evaluating such events. This attribute bit is set internally by the system. There should be no need for your application to deal with this attribute directly.
GA_TARGETABLE
This flag indicates that this object is targetable and is eligible to receive the target exclusive within its target level. Most specific UIs will automatically grab the target for this object whenever the user interacts with it. This attribute is set by default within the following classes:
GenField
GenApplication
GenPrimary
GenDisplayControl
GenDisplay
GenView
GA_NOTIFY_VISIBILITY
This flag indicates that this object should send notification when it becomes visible and not visible. Objects thus will send MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION
to the GenApplication object whenever the state of their visibility changes (see the GenApplication chapter). You may alter this behavior by including one of the visibility vardata attributes.

Code Display 2-15 Using GI_attrs in a Dialog Box

@object GenInteractionClass MyDialogBox = {
    GI_comp = @MyButton, @MyOtherButton;
    GII_visibility = GIV_DIALOG;				/* build this Interaction as a dialog box.*/
}
@object GenTriggerClass MyButton = {
    GTI_actionMsg = MSG_MY_SPECIAL_MESSAGE;
    GTI_destination = process;
    /* MyButton, when activated, will send the message above to the
     * process object. Only when that happens will it activate the
     * behavior within the GI_attrs instance data below. */
    GI_attrs = @default |
    /* This flag will close the MyDialogBox object */
    GA_SIGNAL_INTERACTION_COMPLETE |
    /* This flag will set the application to ignore all input events while the
     * message above is processed. */
    GA_INITIATES_INPUT_IGNORE;
}
MSG_GEN_GET_ATTRIBUTES
byte	MSG_GEN_GET_ATTRIBUTES();

This message retrieves the GI_attrs instance data for the object the message is sent to. This message returns a byte length bitfield.

Source: Unrestricted.

Destination: Any generic object.

Return: Byte length GI_ attrs bitfield.

Interception: Generally not intercepted.

MSG_GEN_SET_ATTRS
void	MSG_GEN_SET_ATTRS(
        byte attrsToSet,
        byte attrsToClear);

This message sets the recipient's GI_attrs field. This message takes two arguments: the attributes to set and the attributes to clear. There is no need to repeat attributes that have been previously set. Note that these attributes will not take effect until the object is activated in the normal manner. (Sending this message does not in itself initiate the activity described).

You should only send this message while an object is not GS_USABLE, because these attributes are only checked when an object is specifically built. Setting the attributes of a GS_USABLE object may cause an error.

Source: Unrestricted.

Destination: Any non-usable generic object.

Parameters: attrsToSet GenAttributes to set in GI_ attrs .

attrsToClear GenAttributes to clear in GI_ attrs .

Interception: Generally not intercepted.

Code Display 2-16 Conditionally Altering the GI_attrs Field

@method MyProcessClass, MSG_DO_CUSTOM_ATTRS {
    byte MyAttrs;
    /* retrieve the GI_attrs field */
    MyAttrs = @call @MyObject::MSG_GEN_GET_ATTRIBUTES();
    /* If the GA_COMPLETES_INTERACTION bit is set, then set it
     * GA_INITIATES_BUSY_STATE also. Otherwise set it
     * GA_INITIATES_INPUT_IGNORE. */
    if (MyAttrs & GA_COMPLETES_INTERACTION){
    @call @MyObject::MSG_GEN_SET_NOT_USABLE();
    @call @MyObject::MSG_GEN_SET_ATTRS(GA_INITIATES_BUSY_STATE, 0);
    /* Note that setting an object's GA_INITIATES_BUSY_STATE attribute will
     * not in itself initiate a busy state. That object will only issue a busy
     * state when it is activated in the normal fashion. */
    @call @MyObject::MSG_GEN_SET_USABLE();
    } else {
    @call @MyObject::MSG_GEN_SET_NOT_USABLE();
    @call @MyObject::MSG_GEN_SET_ATTRS(GA_INITITATES_INPUT_IGNORE, 0);
    @call @MyObject::MSG_GEN_SET_USABLE();
    }
}

GenClass: 3.5 GenClass Basics: States

GI_states

The GI_states attribute is a record that holds the state of the generic object. These states affect the object's visual representation and its functionality. By default, all objects have both GS_USABLE and GS_ENABLED set when first built. There are two GI_states :

GS_USABLE
This state controls the usability of an object. Setting an object GS_USABLE will indicate that both the object and the entire generic branch below this object should be considered as an active part of the user interface and therefore should be visually represented. An object that is not set GS_USABLE cannot appear as part of the user interface. Before an object can be visually built and considered part of the user interface, however, it must satisfy two conditions: The object itself must be GS_USABLE, and all ancestors (parents) of the object must also be GS_USABLE.
This means that a continuous path of GS_USABLE objects must exist from the top node of your application to the object for it to be usable. In this case, an object is considered to be fully usable. Conversely, if an object is not GS_USABLE, then no object in the branch below it can be visually displayed either (even if it is GS_USABLE). Therefore, an object may be set GS_USABLE without being fully usable. This allows you to set an entire branch fully usable by setting just one node object usable, provided that all other objects below that node had previously been set GS_USABLE. This attribute is set by default.
GS_ENABLED
This state sets the object enabled. This state controls whether the object is available to the user for activity. An object must be enabled for the user to interact with it. If an object is set GS_USABLE but not GS_ENABLED, it will be visually displayed but will not accept input from the user. The object will also be unable to perform its normal activities. This is usually represented by "graying out" the object's visual moniker. Before an object can be "enabled" for user input and activation, however, it must satisfy two conditions: The object must be GS_ENABLED; all ancestors (parents) of the object must also be GS_ENABLED. Therefore, a continuous path of GS_ENABLED objects must exist from the top node of your application to the object in question for it to be enabled. In this case, an object is considered to be fully enabled.
Conversely, if an object is not GS_ENABLED, then no object in the branch below it can be interacted with either (even if it is GS_ENABLED). Therefore, an object may be GS_ENABLED without being fully enabled. This allows you to set an entire branch fully enabled by setting just one node object enabled, provided that all other objects below that node had previously been set GS_ENABLED.

These states not only affect the state of the current object but all of its descendants as well. When your user interface is first built (or rebuilt) it conducts a top-down search, building any objects that are declared GS_USABLE. If an object is not GS_USABLE, it will not be visually built. Furthermore, none of its descendants will be searched (or built). Therefore, if any single node is not GS_USABLE, no other object below that node will be fully usable.

In cases where you wish to alter any fundamental behavior of an object, you may have to set the object not usable, change its behavior, and then set the object GS_USABLE again. This ensures that the object is built out correctly (including visually) according to the new criteria.

Code Display 2-17 Setting GI_states

@object GenTriggerClass MyTrigger = {
        /* The default GI_states are GS_USABLE and GS_ENABLED.
         * This object will only be GS_USABLE */
    GI_states = @default & ~GS_ENABLED;
}

The Usable State

MSG_GEN_GET_USABLE, MSG_GEN_SET_USABLE, MSG_GEN_SET_NOT_USABLE, MSG_GEN_CHECK_IF_FULLY_USABLE

Setting an object GS_USABLE will incorporate the object into the generic tree and regard the object as part of the user interface. (It will not by itself enable an object for user input; this requires an object to be set GS_ENABLED also.) If an object is not GS_USABLE, it cannot be used in any manner including any visual implementation by the user interface. The following messages manipulate an object's usable state.

MSG_GEN_GET_USABLE
Boolean	MSG_GEN_GET_USABLE();

This message checks the GS_USABLE bit of the GI_states field.

Source: Unrestricted.

Destination: Any generic object.

Parameters: None.

Return: Will return true if the object is GS_USABLE, false if it is not usable.

Interception: Generally not intercepted.

MSG_GEN_SET_USABLE
void	MSG_GEN_SET_USABLE(
        VisUpdateMode updateMode);

This message sets an object GS_USABLE. (This message has no effect on an object already GS_USABLE.) Objects may be set GS_USABLE only after they have been attached to a generic tree. Setting an object usable forces that object to be specifically built. If the object's associated window is realized, the object will be visually built and updated. Setting an object GS_USABLE that is not attached to the generic tree will cause an error.

Setting an object usable allows that object to become part of the user interface. Before the object can be used, though, it must be fully usable . An object becomes fully usable only if all of its ancestors are GS_USABLE. If any ancestor is not GS_USABLE, then the entire branch below it will not be fully usable. You can check if an object is fully usable with the message MSG_GEN_CHECK_IF_FULLY_USABLE .

Conversely, setting a node GS_USABLE, in which every branch object below it is already GS_USABLE will make that entire branch fully usable. This is useful for bringing up an entire section of a generic tree by setting a single object usable.

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode VisUpdateMode to use when updating changes to the screen.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_SET_NOT_USABLE
void	MSG_GEN_SET_NOT_USABLE(
        VisUpdateMode updateMode);

This message sets an object not usable (clears the GS_USABLE bit). Because an object may be visually unbuilt by this message, the VisUpdateMode VUM_MANUAL is not allowed.

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode VisUpdateMode to use when updating changes to the screen. May not be VUM_MANUAL.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_CHECK_IF_FULLY_USABLE
Boolean	MSG_GEN_CHECK_IF_FULLY_USABLE();

This message checks whether an object is fully usable. The object and all of its parents must be GS_USABLE for the object to be fully usable.

Source: Unrestricted.

Destination: Any generic object.

Return: Will return true if the object is fully usable, false if it is not.

Interception: Generally not intercepted.

The Enabled State

MSG_GEN_GET_ENABLED, MSG_GEN_SET_ENABLED, MSG_GEN_SET_NOT_ENABLED, MSG_GEN_CHECK_IF_FULLY_ENABLED

An object that is GS_ENABLED is ready for user interaction. As in the case with GS_USABLE, all parents of the object in question must be GS_ENABLED to fully enable the object. An object may be fully enabled without being fully usable, but the object will not be visually represented. You can check if an object is fully enabled with MSG_GEN_CHECK_IF_FULLY_ENABLED .

An object that is not enabled but is set usable will be represented in the user interface; it will not, however, allow the user to interact with it. In many cases, the specific UI implements this by "graying out" the object. Setting a disabled object will visually update it.

MSG_GEN_GET_ENABLED
Boolean	MSG_GEN_GET_ENABLED();

This message returns the enabled state of the object the message is sent to.

Source: Unrestricted.

Destination: Any generic object.

Return: Will return true if the object is enabled, false if it is not.

Interception: Generally not intercepted.

MSG_GEN_SET_ENABLED
void	MSG_GEN_SET_ENABLED(
        VisUpdateMode updateMode);

This message sets an object GS_ENABLED. (This message has no effect on an object already GS_ENABLED.) You must pass this message a VisUpdateMode . Sending this message allows the object to receive user input. Only send this message if your application will be ready to interact with the object after the period specified in the VisUpdateMode has passed.

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode VisUpdateMode to use when updating changes to the screen.

Interception: Generally not intercepted.

MSG_GEN_SET_NOT_ENABLED
void	MSG_GEN_SET_NOT_ENABLED(
        VisUpdateMode updateMode);

This message sets the object not enabled (clears the object's GS_ENABLED bit.) You must pass the message a VisUpdateMode . In most specific UIs a disabled state is implemented by "graying out" the object. The user will be unable to interact with the object according to the VisUpdateMode passed.

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode VisUpdateMode to use when updating changes to the screen. May not be VUM_MANUAL.

Interception: Generally not intercepted.

MSG_GEN_CHECK_IF_FULLY_ENABLED
Boolean	MSG_GEN_CHECK_IF_FULLY_ENABLED();

This message checks whether an object is fully enabled. An object is only fully enabled (ready for user interaction) when both it and all of its ancestors are enabled.

Source: Unrestricted.

Destination: Any generic object.

Return: Will return true if fully enabled, false if not.

Interception: Generally not intercepted.

GenClass: 4 Modifying GenClass Instance Data

Instance data need not be set solely within your .goc file. You may also retrieve or alter this data dynamically with messages in GenClass . The following section details messages to modify the GI_visMoniker , GI_kbdAccelerator , GI_attrs and GI_states instance fields. This section also details ways of using the variable data object routines and messages to modify a generic object's vardata.

Remember that when accessing and modifying instance data, you may need to dereference the pointer to the object's instance data if the location of that instance data may have changed. This pointer ( pself ) is automatically set up correctly inside a method upon receipt of the corresponding message. If you perform actions within that method that may move the object, you can use ObjDerefGen () to dereference pself again.

If the instance fields you wish to modify are related to generic tree mechanisms ( GI_comp and GI_link ), see Special Message Passing .

Visual Monikers

Managing Visual Monikers

Managing Keyboard Accelerators

GenClass: 4.1 Modifying GenClass Instance Data: Visual Monikers

A visual moniker must reside in a local memory (LMem) chunk. That chunk should be located within the object block of the particular generic object it is associated with. Once monikers are defined, there are several messages in GenClass that you can use to change the visual moniker for an object.

The GI_visMoniker field contains a ChunkHandle of the VisMoniker structure. The VisMoniker structure contains three pieces of data: the type of moniker, the width of the moniker chunk, and the moniker data.

GenClass: 4.2 Modifying GenClass Instance Data: Managing Visual Monikers

MSG_GEN_GET_VIS_MONIKER, MSG_GEN_USE_VIS_MONIKER, MSG_GEN_CREATE_VIS_MONIKER, MSG_GEN_REPLACE_VIS_MONIKER, MSG_GEN_REPLACE_VIS_MONIKER_OPTR, MSG_GEN_REPLACE_VIS_MONIKER_TEXT, MSG_GEN_FIND_MONIKER, MSG_GEN_DRAW_MONIKER, MSG_GEN_GET_MONIKER_POS, MSG_GEN_GET_MONIKER_SIZE, MSG_GEN_RELOC_MONIKER_LIST, MSG_GEN_FIND_OBJECT_WITH_TEXT_MONIKER

Sending MSG_GEN_GET_VIS_MONIKER to an object returns the chunk handle of the visual moniker ( GI_visMoniker ) for that object. You can then inspect that visual moniker or use it within other objects (though this is not recommended). You must be careful when sharing monikers with other objects, as freeing one object (and therefore its associated moniker) will interfere with any other objects referencing that moniker.

Sending MSG_GEN_USE_VIS_MONIKER to an object sets a visual moniker for that object to use. The message must pass the chunk handle of the moniker desired for the object along with a VisUpdateMode . Valid VisUpdateMode s are described in the VisClass chapter. The moniker to use must reside in the same block as the object being set. If you need to set an object to use a new moniker from a location outside of its object block, use MSG_GEN_REPLACE_VIS_MONIKER to copy the moniker to the new location.

MSG_GEN_REPLACE_VIS_MONIKER copies a visual moniker from a source location into a destination. This message must pass parameters specifying the type of copy operation. The source for the moniker can be referenced using an optr or a simple text string. Use this message when you wish to copy a moniker from outside of the object's current object block.

MSG_GEN_REPLACE_VIS_MONIKER_OPTR and MSG_GEN_REPLACE_VIS_MONIKER_TEXT are simplified versions of the above message, allowing you to set a visual moniker from an optr or from a pointer to a null-terminated text string without having to pass many other arguments.

MSG_GEN_CREATE_VIS_MONIKER creates a visual moniker (within the resource block of the object this message is sent to), copying it from a source; it does not, however, attach this moniker to any object.

MSG_GEN_FIND_MONIKER searches for a specific type of moniker within a moniker list. You may then use the moniker directly. You can also replace the original moniker list with the specified moniker.

MSG_GEN_DRAW_MONIKER, MSG_GEN_GET_MONIKER_POS , and MSG_GEN_GET_MONIKER_SIZE all manipulate a currently existing moniker. These messages are most useful for custom gadgets. MSG_GEN_DRAW_MONIKER draws the moniker according to the criteria passed with the message. MSG_GEN_GET_MONIKER_POS and MSG_GEN_GET_MONIKER_SIZE return GEOS coordinates specifying the location and size of the moniker, respectively. You may then inspect and alter these coordinates before sending the moniker a MSG_GEN_DRAW_MONIKER to redraw the moniker according to the changed criteria.

The utility message MSG_GEN_RELOC_MONIKER_LIST relocates an object's moniker list.

To find an object with a particular visual moniker, send MSG_GEN_FIND_OBJECT_WITH_MONIKER_FLAGS to the object at which to start the top-down search, passing the text to perform the match.

MSG_GEN_GET_VIS_MONIKER
ChunkHandle	 MSG_GEN_GET_VIS_MONIKER();

This message retrieves the instance data in the object's current GI_visMoniker instance field. This message returns the ChunkHandle of the moniker data structure. You can then use that chunk handle to manipulate the visual moniker directly or to copy the moniker for use by other objects.

Source: Unrestricted.

Destination: Any generic object.

Return: The chunk handle of the visual moniker in use by this object.

Interception: Generally not intercepted.

MSG_GEN_USE_VIS_MONIKER
void	MSG_GEN_USE_VIS_MONIKER(
        ChunkHandle moniker,
        VisUpdateMode updateMode);

This message allows an object to reference a visual moniker; this message does not destroy the chunk of the object's current visual moniker. The moniker must reside in the same block as the object. Use MSG_GEN_REPLACE_VIS_MONIKER if you wish to set a moniker from a source outside the object block or if you wish to use a new moniker, overwriting the old one.

You cannot pass a moniker list's chunk handle using this message. See MSG_GEN_FIND_MONIKER for information on selecting monikers from within a list.

Source: Unrestricted.

Destination: Any generic object.

Parameters: moniker The chunk handle of the visual moniker to use

updateMode
VisUpdateMode to determine when the moniker will be redrawn.

Return: Nothing.

Interception: Generally not intercepted.

Code Display 2-18 Getting and Using Visual Monikers

/* This method effectively copies a visual moniker from ObjectOne into ObjectTwo.
 * Both objects must reside in the same object block. Note that in effect, both
 * ObjectOne and ObjectTwo will "share" the same moniker. This can be dangerous if
 * one object is freed, thereby causing the other object to become dereferenced.
 * For more complex copy operations, use MSG_GEN_REPLACE_VIS_MONIKER instead. */
@method MyProcessClass, MSG_MY_MONIKER_MESSAGE {
    /* Set up variable to store the chunk handle of the visual moniker. */
    ChunkHandle MyVisMonikerCH;
    /* Retrieve the visMoniker of ObjectOne and store it in the variable. */
    MyVisMonikerCH = @call @ObjectOne::MSG_GEN_GET_VIS_MONIKER();
    /* Set the visMoniker of Object Two to MyVisMonikerCH and update
     * immediately. Use @send if you don't expect a return value. */
    @send @ObjectTwo::MSG_GEN_USE_VIS_MONIKER(MyVisMonikerCH, VUM_NOW);
}
/* You can also declare visual monikers in your .goc file and set them later */
    @visMoniker OnMoniker = "On";
    @visMoniker OffMoniker = "Off";
    /* Within a method, the following routine will set the moniker to "On". The
     * OptrToChunk operator casts the object from an optr to a ChunkHandle. */
    @call @MyObject::MSG_GEN_SET_MONIKER(OptrToChunk(@OnMoniker), VUM_NOW);
    /* Within a method, the following routine will set the moniker to "Off" */
    @call @MyObject::MSG_GEN_SET_MONIKER(OptrToChunk(@OffMoniker), VUM_NOW);
MSG_GEN_REPLACE_VIS_MONIKER
ChunkHandle	 MSG_GEN_REPLACE_VIS_MONIKER(@stack
        VisUpdateMode			updateMode,
        word			height,
        word			width,
        word			length,
        VisMonikerDataType			dataType,
        VisMonikerSourceType			sourceType,
        dword			source);

This message copies a visual moniker from a source to the object sent the message. The message returns the chunk handle of the newly copied moniker. The object's current visual moniker is overwritten by this message; therefore, if more than one object shares the overwritten block, the other object should be updated with MSG_GEN_USE_VIS_MONIKER . This message can copy a visual moniker from a variety of sources.

This message is a general, all-purpose, moniker replacement device. If your visual moniker source is a visMoniker structure or a null-terminated text string, you may be able to use the simpler messages MSG_GEN_REPLACE_VIS_MONIKER_OPTR and MSG_GEN_REPLACE_VIS_MONIKER_TEXT .

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode Specifies the VisUpdateMode to use when visually updating the changes to the screen. See the VisClass Chapter for more information on VisUpdateMode types.

height
If moniker is a GString, this specifies the height of the moniker in points. Otherwise, set this to zero.
width
If the moniker is a GString, this specifies the width of the moniker in points. Otherwise, set this to zero.
length
If the moniker is a GString, this specifies the size of the moniker in bytes. If the moniker is text and this argument is zero, the string is assumed to be null-terminated.
dataType
Specifies the type of data referenced by the source . Valid VisMonikerDataType types include VMDT_VIS_MONIKER, VMDT_TEXT, VMDT_GSTRING, or VMDT_TOKEN.
sourceType
Specifies the type of pointer referencing the source . Valid VisMonikerSourceType types are VMST_FPTR, VMST_OPTR, and VMST_HPTR.
source
Specifies the source of the moniker to be used during the copy process. This source may be a pointer to a text string, an optr pointing to a visual moniker outside the current block, or a variety of other sources. The source type should be specified in sourceType .

Return: The chunk handle of the new visual moniker for the object.

Interception: Generally not intercepted.

MSG_GEN_REPLACE_VIS_MONIKER_OPTR
ChunkHandle	 MSG_GEN_REPLACE_VIS_MONIKER_OPTR(
        optr		source,
        VisUpdateMode		updateMode);

This message is a simplified version of MSG_GEN_REPLACE_VIS_MONIKER. If your visual moniker source is an existing visMoniker structure, you can use this message to replace an object's moniker.

Source: Unrestricted.

Destination: Any generic object.

Parameters: source The optr of the visMoniker structure to use in the replacement operation.

updateMode
Specifies the VisUpdateMode to use when visually updating the changes to the screen.

Return: The chunk handle of the new visual moniker for the object.

Interception: Generally not intercepted.

MSG_GEN_REPLACE_VIS_MONIKER_TEXT
ChunkHandle	 MSG_GEN_REPLACE_VIS_MONIKER_TEXT(
        const char		*source,
        VisUpdateMode		updateMode);

This message is a simplified version of MSG_GEN_REPLACE_VIS_MONIKER. If your visual moniker source is a pointer to a null-terminated text string, you can use this message to replace an object's moniker without having to pass the arguments of MSG_GEN_REPLACE_VIS_MONIKER .

Source: Unrestricted.

Destination: Any generic object.

Parameters: source A pointer to a null-terminated character string to use in the replacement operation.

updateMode
Specifies the VisUpdateMode to use when visually updating the changes to the screen.

Return: The chunk handle of the new visual moniker for the object.

Interception: Generally not intercepted.

MSG_GEN_CREATE_VIS_MONIKER
ChunkHandle MSG_GEN_CREATE_VIS_MONIKER(@stack
        CreateVisMonikerFlags			flags,
        word			height,
        word			width,
        word			length,
        VisMonikerDataType			dataType,
        VisMonikerSourceType			sourceType,
        dword			source);

This message creates a visual moniker chunk within the resource block of the object sent this message. The moniker is copied from the source specified but is not attached to any object in the new object block.

Source: Unrestricted

Destination: Any GenClass object

Parameters: flags Flags to use in the creation of the new visual moniker. CVMF_DIRTY marks the new moniker chunk OCF_DIRTY .

height
If moniker is a GString, this specifies the height of the moniker in points. Otherwise, set this to zero.
width
If the moniker is a GString, this specifies the width of the moniker in points. Otherwise, set this to zero.
length
If the moniker is a GString, this specifies the length of the moniker in points. Otherwise, set this to zero.
dataType
Specifies the type of data referenced by the source . Valid VisMonikerDataType types include VMDT_VIS_MONIKER, VMDT_TEXT, VMDT_GSTRING, or VMDT_TOKEN.
sourceType
Specifies the type of pointer referencing the source . Valid VisMonikerSourceType types are VMST_FPTR, VMST_OPTR, and VMST_HPTR.
source
Specifies the source of the moniker to be used during the copy process. This source may be a pointer to a text string, an optr pointing to a visual moniker outside the current block, or a variety of other sources. The source type should be specified in sourceType .

Return: The chunk handle of the new visual moniker for the object.

Interception: Generally not intercepted.

MSG_GEN_FIND_MONIKER
optr	MSG_GEN_FIND_MONIKER(
        Boolean		useAppMonikerList,
        word		searchFlags,					/* VisMonikerSearchFlags */
        MemHandle		destBlock);

This message scans a list of monikers and selects a specific moniker (or the most appropriate moniker). The moniker selected is determined according to the passed VisMonikerSearchFlags . This message is usually used by the specific UI to select an application moniker from a list.

If the first argument ( useAppMonikerList ) is not zero, the message will use the GenApplication's moniker list instead of the recipient's. This is useful for finding the icon used for an iconified application.

Note that this message returns an optr, not a chunk handle, as this message is most often used to find a moniker outside of the current object block. This message returns zero if no moniker is found.

Source: Unrestricted.

Destination: Any generic object.

Parameters: useAppMonikerList
 Non-zero to use the moniker list of the application associated with this object.

searchFlags
VisMonikerSearchFlags to use for the search criteria. If VMSF_COPY_CHUNK is specified in the searchFlags , a block handle must be supplied in destBlock . The selected moniker will be copied into that block.
destBlock
Block to copy a moniker into, if VMSF_COPY_CHUNK is specified in the searchFlags .

Return: The optr of the moniker found according to the search criteria. (This may be outside the current object block.)

Interception: Generally not intercepted.

MSG_GEN_DRAW_MONIKER
void	MSG_GEN_DRAW_MONIKER(@stack
        DrawMonikerFlags		monikerFlags,
        word		textHeight,
        GStateHandle		gState,
        word		yMaximum,
        word		xMaximum,
        word		yInset,
        word		xInset);

This message draws a moniker for an object according to the parameters passed. The moniker must currently exist. You can use this message to change the way you wish a visual moniker to appear within a generic object without actually changing the visual moniker itself.

Source: Unrestricted.

Destination: Any GS_USABLE generic object.

Parameters: monikerFlags Specifies the DrawMonikerFlags to use when drawing the moniker. The DrawMonikerFlags record specifies the conditions to draw the moniker under.

textHeight
Specifies the height of the system text. Passing this value speeds the processing of this message. If you do not know the height of the system text, pass zero.
gState
Specifies the GState, if any, to use when drawing the moniker.
yMaximum, xMaximum
These specify the maximum height and width of the moniker. Typically, these are used with DMF_CLIP_TO_MAX_WIDTH (in DrawMonikerFlags ) to perform a clipping operation on the moniker. Pass zero if no maximum width is desired.
yInset
Specifies the point to begin drawing the moniker if the moniker is right or left justified.
xInset
Specifies the point to begin drawing the moniker if the moniker is top or bottom justified.

Return: Nothing

Interception: Generally not intercepted. Custom gadgets may handle this if they are supplementing or replacing default functionality.

Structures: The DrawMonikerFlags record defines the parameters to use when drawing a currently existing visual moniker. This record is used by MSG_GEN_DRAW_MONIKER , MSG_GEN_GET_MONIKER_POS and MSG_GEN_GET_MONIKER_SIZE . Its flags are shown below:

DMF_UNDERLINE_ACCELERATOR
DMF_CLIP_TO_MAX_WIDTH
DMF_NONE
DMF_Y_JUST_MASK    /* of type Justifications */
DMF_X_JUST_MASK    /* of type Justifications */

Use DMF_UNDERLINE_ACCELERATOR to underline the keyboard accelerator of a text moniker. Use DMF_CLIP_TO_MAX_WIDTH to clip the visual bounds of the moniker to the width specified in maxWidth . Use DMF_NONE to draw the moniker at the current pen position.

The two justification masks are of type Justification , and they specify the vertical and horizontal justifications to use when drawing the moniker. The values you can use in the justification fields are

J_LEFT
J_RIGHT
J_CENTER
J_FULL
MSG_GEN_GET_MONIKER_POS
XYValueASDWord MSG_GEN_GET_MONIKER_POS(@stack
        DrawMonikerFlags		monikerFlags,
        word		textHeight,
        GStateHandle		gState,
        word		yMaximum,
        word		xMaximum,
        word		yInset,
        word		xInset);

This message returns the x and y coordinates of the moniker for the object as if the moniker were drawn according to the passed parameters (the same as for MSG_GEN_DRAW_MONIKER ). Use the macros DWORD_X and DWORD_Y to extract the appropriate coordinates from the returned value.

This message, along with MSG_GEN_DRAW_MONIKER and MSG_GEN_GET_MONIKER_SIZE is most useful in the construction and modification of custom gadgets.

Source: Unrestricted.

Destination: Any generic object.

Parameters: See MSG_GEN_DRAW_MONIKER .

Return: XYValueAsDWord which can be split up into x and y coordinates of the moniker position with the macros DWORD_X and DWORD_Y.

Interception: Generally not intercepted.

MSG_GEN_GET_MONIKER_SIZE
SizeAsDWord MSG_GEN_GET_MONIKER_SIZE(
        word		textHeight,
        GStateHandle		gState);

This message returns the width and height (in points) of the moniker for the object. Use the macros DWORD_WIDTH and DWORD_HEIGHT to extract the appropriate coordinates from the returned value. This message, along with MSG_GEN_DRAW_MONIKER and MSG_GEN_GET_MONIKER_POS , is most useful in the construction and modification of custom gadgets.

Source: Unrestricted.

Destination: Any generic object.

Parameters: textHeight The system text height, if known; otherwise, pass zero.

gstate
The GState handle of the GState to draw to.

Return: SizeAsDWord which can be split up into with and height with the macros DWORD_WIDTH and DWORD_HEIGHT.

Interception: Generally not intercepted.

MSG_GEN_RELOC_MONIKER_LIST
void 	MSG_GEN_RELOC_MONIKER_LIST(
        optr		monikerList,
        word 		relocFlag); /* 0: relocate; 1: unrelocate */

This method (un)relocates a passed moniker list.

Source: Anyone.

Destination: Any GenClass object.

Parameters: monikerList optr of moniker list.

reloc
Flag specifying whether relocation or unrelocation is desired.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_FIND_OBJECT_WITH_TEXT_MONIKER
optr	MSG_GEN_FIND_OBJECT_WITH_TEXT_MONIKER(
        char	*text,
        word	flags);

This message recursively searches through the generic tree (starting at the object sent the message) for an object whose visual moniker matches the passed text. This match does not need to be complete (unless the flag GFTMF_EXACT_MATCH is passed); the message will return the first object whose initial characters match the given text. The message must pass GenFindObjectWithMonikerFlags .

typedef WordFlags GenFindObjectWithMonikerFlags;
#define GFTMF_EXACT_MATCH 0x8000
#define GFTMF_SKIP_THIS_NODE 0x4000

If GFTMF_EXACT_MATCH is passed, the text and the visual moniker text must match exactly (null-terminated). If GFTMF_SKIP_THIS_NODE is passed, the object which sent the message will not be checked; only its children will be checked.

Source: Unrestricted.

Destination: Any generic object.

Parameters: text Null-terminated text.

flags
GenFindObjectWithMonikerFlags .

Return: optr of the object with the matching moniker, or null if no moniker was found.

Interception: Generally not intercepted.

GenClass: 4.3 Modifying GenClass Instance Data: Managing Keyboard Accelerators

MSG_GEN_GET_KBD_ACCELERATOR, MSG_GEN_SET_KBD_ACCELERATOR, MSG_GEN_CHANGE_ACCELERATOR

Keyboard Accelerators provide keyboard shortcuts for heavily used application functions. For complete information on valid GI_kbdAccelerator instance data and the functionality those data provide, see Keyboard Accelerators .

MSG_GEN_GET_KBD_ACCELERATOR returns a word of data specifying the current keyboard accelerator, if any, for the object the message is sent to.

MSG_GEN_SET_KBD_ACCELERATOR sets the keyboard accelerator of the object the message is sent to. The message must pass the keyboard accelerator to use along with a VisUpdateMode . If you wish to remove a keyboard accelerator from an object, pass this message with a keyboard acclerator of zero.

MSG_GEN_CHANGE_ACCELERATOR changes the keyboard accelerator using a low-level replace operation.

MSG_GEN_GET_KBD_ACCLERATOR
word	MSG_GEN_GET_KBD_ACCELERATOR();

This message returns the object's current GI_kbdAccelerator. The message returns a word-length data structure ( KeyboardShortcut ). You can then manipulate or use this word of data to set other object's keyboard accelerators. Make sure that you do not have two objects sharing the same keyboard accelerator.

Source: Unrestricted.

Destination: Any generic object.

Return: KeyboardShortcut of the generic object (in GI_ kbdAccelerator ).

Interception: Generally not intercepted.

MSG_GEN_SET_KBD_ACCELERATOR
void	MSG_GEN_SET_KBD_ACCELERATOR(
        word		accelerator,
        VisUpdateMode		updateMode);

This message sets the GI_kbdAccelerator of the object the message is sent to.

Source: Unrestricted.

Destination: Any generic object.

Parameters: accelerator KeyboardShortcut to set GI_ kbdAccelerator of the generic object to.

updateMode
VisUpdateMode to determine when redrawing occurs.

Interception: Generally not intercepted.

Code Display 2-19 Using a Keyboard Accelerator

@kbdAccelerator ShortcutOne = ctrl shift `k';
    /* @specificUI keyword needed to use DELETE key */
@kbdAccelerator ShortcutTwo = @specificUI ctrl DELETE;
@object GenTriggerClass MyTrigger = {
    GI_kbdAccelerator = ShortcutOne;				/* sets accel to "ctrl shift `k'" */
}
@object GenTriggerClass MyOtherTrigger = {
    GI_kbdAccelerator = ShortcutCopy;				/* Copies "ctrl shift `k'" to
                     * MyOtherTrigger */
}
MSG_GEN_CHANGE_ACCELERATOR
void	MSG_GEN_CHANGE_ACCELERATOR(
        word	bitsToClear,
        word	bitsToSet);

This message changes the keyboard accelerator using constants defined in the input library. In most cases, use MSG_GEN_SET_KBD_ACCELERATOR instead. MSG_GEN_CHANGE_ACCELERATOR is useful in some cases where you wish to tinker slightly with the keyboard shortcut's usage (as in a case where an accelerator of ctrl z should change to alt z in the duplication of a template object).

Source: Unrestricted.

Destination: Any generic object.

Parameters: bitsToClear KeyboardShortcut and/or VChar to clear.

bitsToSet
KeyboardShortcut and/or VChar to set.

Interception: Generally not intercepted.

GenClass: 5 Generic Trees

Generic trees are constructed in several ways. The most common is to add, remove, and move individual children. These children may be the top of a generic branch and will therefore add, remove, or move the entire branch below them. GenClass also provides other means of manipulating entire branches of generic trees. Each manner of generic tree construction and destruction has certain advantages and disadvantages. The various methods are detailed below:

  • The object may be declared in the .goc file, attached to the generic tree. In this case, there is no need to add the child as this is taken care of by the compiler. You can remove the object and any of its children with MSG_GEN_REMOVE (or MSG_GEN_REMOVE_CHILD ). After removal, any single object may be destroyed with MSG_META_OBJ_FREE . You may also destroy a tree of generic objects with MSG_GEN_DESTROY . You may add the removed object (and its children) to a different location in the generic tree with MSG_GEN_ADD_CHILD .
  • The object may exist in your .goc file but remain unattached to the generic tree. You may attach the object with MSG_GEN_ADD_CHILD . You can remove the object, and any children of this object, with MSG_GEN_REMOVE (or MSG_GEN_REMOVE_CHILD ). After removal, destroy any single object with MSG_META_OBJ_FREE . You can destroy a tree of generic objects with MSG_GEN_DESTROY .
  • ObjInstantiate(), ObjInstantiateForThread()
     These routines create a new instance of a single object. You can then add the object to your generic tree with MSG_GEN_ADD_CHILD . After a child is removed using MSG_GEN_REMOVE (or MSG_GEN_REMOVE_CHILD ), it can be destroyed using MSG_META_OBJ_FREE . The object destroyed in such a manner must not have any children attached to it at the time of destruction.
  • ObjDuplicateResource()
     This routine duplicates an instance of a previously defined object resource. That object block may contain one or more objects. In your .gp file, make sure the resource is marked discard-only . Only use this routine if you want to duplicate an instance of a predefined resource block. After a block duplicated in this manner is removed using MSG_GEN_REMOVE (or MSG_GEN_REMOVE_CHILD ), it can be destroyed using ObjFreeDuplicate() .
  • MSG_GEN_COPY_TREE
    This message copies an entire generic branch headed by the recipient. The branch will be set usable and added automatically without need for a MSG_GEN_ADD_CHILD . A generic branch created in this manner can be removed with MSG_GEN_DESTROY . These messages are useful because they can duplicate and destroy generic trees across object blocks. (The copied branch will reside in one block, however.)

Objects created in these ways must be added using MSG_GEN_ADD_CHILD (except for those created with MSG_GEN_COPY_TREE ).

  • MSG_GEN_ADD_CHILD
    This message adds a generic object that was previously created but not attached to the generic tree. The object may have been created in your .goc file but not attached as a child of any other object. The object may also have been created through ObjInstantiate() or ObjInstantiateForThread() or with ObjDuplicateResource() (see above). Use MSG_GEN_REMOVE (or MSG_GEN_REMOVE_CHILD ) to remove a child added in this manner.

Child/Parent Searches

Manipulating Children Directly

Branch Construction/Destruction

GenClass: 5.1 Generic Trees: Child/Parent Searches

MSG_GEN_FIND_CHILD, MSG_GEN_FIND_CHILD_AT_POSITION, MSG_GEN_FIND_PARENT, MSG_GEN_COUNT_CHILDREN

The following messages provide means of searching the generic tree. It is necessary (in most cases) to check whether a child exists for an object before attempting to delete or access the child. Note that it is bad practice to retrieve optrs using these messages and then some time later send messages manipulating these optrs directly because the tree might change. Instead, use the generic dispatch messages MSG_GEN_CALL_PARENT , MSG_GEN_SEND_TO_CHILDREN , etc.

MSG_GEN_FIND_CHILD
word	MSG_GEN_FIND_CHILD(
        optr child);

This message checks whether the object is a child of the recipient. If so, it returns the child's position. If you wish to find out if a specific child currently exists at a certain position, use MSG_GEN_FIND_CHILD_AT_POSITION .

Source: Unrestricted.

Destination: Any generic object.

Parameters: child The optr of child to search for.

Return: The numbered position (zero-based) of the child, or -1 if the specific child is not found.

Interception: Generally not intercepted.

MSG_GEN_FIND_CHILD_AT_POSITION
optr	MSG_GEN_FIND_CHILD_AT_POSITION(
        word position);

This message returns the optr of the child at position , if any. Pass the message the zero-based (zero indicates the first child) position to check. If no child is found, the message returns zero.

Source: Unrestricted.

Destination: Any generic object.

Parameters: position The position (zero-based) of the child to search for.

Return: The optr of child at position passed, if found. Otherwise returns zero.

Interception: Generally not intercepted.

MSG_GEN_FIND_PARENT
optr	MSG_GEN_FIND_PARENT();

This message returns the parent of the recipient object, if any.

Source: Unrestricted.

Destination: Any generic object.

Return: The optr of the parent object. If no parent is found, this message returns zero.

Interception: Generally not intercepted.

MSG_GEN_COUNT_CHILDREN
word	MSG_GEN_COUNT_CHILDREN();

This message returns the number of children of the recipient.

Source: Unrestricted.

Destination: Any generic object.

Return: The number of children the object has.

Interception: Generally not intercepted.

GenClass: 5.2 Generic Trees: Manipulating Children Directly

MSG_GEN_ADD_CHILD, MSG_GEN_REMOVE, MSG_GEN_REMOVE_CHILD, MSG_GEN_MOVE_CHILD, MSG_GEN_ADD_CHILD_UPWARD_LINK_ONLY

The following messages create, move, and remove objects set up directly in your .goc file or created with ObjInstantiate() , ObjInstantiateForThread() or ObjDuplicateResource() . These objects may have children, in which case those children will travel with their parent (and be moved and destroyed with the parent).

When adding or removing children, you typically have to use a CompChildFlags record. This record has two fields, one of which is a dirty flag; the other is a position number indicating a child's position. The record has the following structure:

typedef WordFlags CompChildFlags;
#define CCF_MARK_DIRTY				0x8000 /* high bit */
#define CCF_REFERENCE				0x7FFF /* low 15 bits */
#define CCF_REFERENCE_OFFSET					0
 /* The CCF_REFERENCE field may have any integral
 * number or may be set to one of the following
 * constants:
 *	CCO_FIRST			first child's position
 *	CCO_LAST			last child's position */

The CompChildFlags fields are

CCF_MARK_DIRTY
If set, this flag indicates that the operation in progress should mark the affected objects dirty. Any objects marked dirty will be saved to the state file upon shutdown.
CCF_REFERENCE
This field consists of the lower 15 bits of the word and is a zero-based integer representing the position of the child in its parent's child list. This number cannot be greater than 32767 (0x7fff hex). If the number given is greater than the number of current children, the child will be assumed to be last. For example, a CCF_REFERENCE of four would specify the fifth child of the parent object, or the last child if there are fewer than five children currently. When specifying a position for CCF_REFERENCE, use the CCF_REFERENCE_OFFSET (using the shift operator).

CCO_FIRST and CCO_LAST specify either the first or last child of the parent, respectively. There is no need to use the CCF_REFERENCE_OFFSET in these cases.

MSG_GEN_ADD_CHILD
void	MSG_GEN_ADD_CHILD(
        optr		child,
        CompChildFlags		flags);

This message adds the passed object as a child of the recipient. The child object must not be GS_USABLE before being added to the generic tree. Make sure not to add a child that is already a child of some other parent. It is also illegal to add an object that is already a child of the parent. If necessary, check first if the specific child currently exists using MSG_GEN_FIND_CHILD .

In most cases a routine that adds a new child will follow three phases: checking whether the child exists, adding the child, and setting the child GS_USABLE.

The child object, if already specifically initialized, must be unbuilt before being added to the parent. This ensures that the object will be built out correctly. The internal keyboard search path attribute, GA_KBD_SEARCH_PATH, is also cleared and reset for the child.

Pass this message the optr of the child object to add, along with the CompChildFlags to use. If CCF_MARK_DIRTY is specified, the new linkage will be saved to state when the application is detached. You must pass a CCF_REFERENCE in CompChildFlags to specify the position to add the child. The special constants CCO_FIRST and CCO_LAST, which are special cases of CCF_REFERENCE, will add the object as the first or last child of the parent, respectively.

Note that the object must currently exist. MSG_GEN_ADD_CHILD merely sets up the correct linkage and reconfigures your UI. Note also that successive additions of children using the flag CCO_FIRST will result in a "reverse order" of children (the last added will be the first child, the first added will be the last).

Source: Unrestricted.

Destination: Any generic object.

Parameters: child The optr of the object to add to the current object's children. This child must not be usable.

flags
CompChildFlags to use when adding the child.

Interception: Generally not intercepted. Custom gadgets may intercept to supplement or supersede default functionality.

Code Display 2-20 ObjDuplicateResource() with MSG_GEN_ADD_CHILD

/* This method duplicates a pre-instantiated version of MyMenu.
 * A duplicated object block may also be added using MSG_GEN_ADD_CHILD.*/
@method MyProcessClass, MSG_DUPLICATE_MY_MENU {
    MemHandle newBlock;				/* The handle of the Duplicate block. */
    optr newMenu;				/* The optr of the new menu. */
    GeodeHandle procHan;				/* The geode handle of the process. */
    procHan = GeodeGetProcessHandle();
    newBlock = ObjDuplicateResource(OptrToHandle(@MyMenu), procHan, -1);
            /* Pass the handle of MyMenu's resource as well as the
             * GeodeHandle of the process. Leave the burden thread
             * the same.*/
    /* The new optr is created from the newly created block. */
    newMenu = ConstructOptr(newBlock, OptrToChunk(MyMenu));
    /* Add the duplicated object tree (MyMenu) as the
     * first child of MyPrimary. */
    @call @MyPrimary::MSG_GEN_ADD_CHILD(newMenu, (CCF_MARK_DIRTY | CCO_FIRST));
    /* Then set it usable. Remember, you cannot add a child
     * that is already GS_USABLE. */
    @call @newMenu::MSG_GEN_SET_USABLE(VUM_NOW);
}
MSG_GEN_ADD_CHILD_UPWARD_LINK_ONLY
void	MSG_GEN_ADD_CHILD_UPWARD_LINK_ONLY(
        optr		child);

This message sets a parent link to a passed child object without adding a composite link from the parent to the child. This is a "one way" link in that the parent does not have knowledge of its new child. Therefore, it must be used with caution.

Source: Unrestricted.

Destination: Any generic object.

Parameters: child optr of the child to add with an upward link.

Interception: Generally not intercepted.

MSG_GEN_REMOVE
void 	MSG_GEN_REMOVE(
        VisUpdateMode 		updateMode,
        CompChildFlags 		flags)

This message removes the receiving object from the generic tree. The object to be removed need not be set not usable. Note that all objects below this object will also be removed.

Source: Anyone.

Destination: Any generic object.

Parameters: updateMode Visual update mode. VUM_MANUAL is not allowed.

flags
Set CCF_MARK_DIRTY to mark links dirty.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_REMOVE_CHILD
void	MSG_GEN_REMOVE_CHILD(
        optr		child,
        CompChildFlags		flags);

This message removes the passed object from the recipient. A child must be marked not GS_USABLE in order to be removed. The child must currently exist, so your routine should check this using MSG_GEN_FIND_CHILD .

Pass this message the optr of the child object to be removed along with a word of CompChildFlags . If CCF_MARK_DIRTY is specified, the updated linkage will be saved to state when the application is detached.

Source: Unrestricted.

Destination: Any generic object.

Parameters: child The optr of child to remove. This child must be non-usable in order to be removed. The child must also exist as a child of the recipient.

flags
CompChildFlags to use when removing the child.

Interception: Generally not intercepted. Custom gadgets may intercept to supplement or supersede default functionality.

Code Display 2-21 MSG_GEN_REMOVE_CHILD

/* This sample method removes the MyChild object from its parent, MyParent. */
@method MyProcessClass, MSG_REMOVE_MY_CHILD {
    /* If the child currently exists, mark it not usable and remove it. */
    if (@call @MyParent::MSG_GEN_FIND_CHILD(@MyChild) != -1) {
    @call @MyChild::MSG_GEN_SET_NOT_USABLE(VUM_NOW);
    @call @MyParent::MSG_GEN_REMOVE_CHILD(@MyChild, CCF_MARK_DIRTY);
    }
}
MSG_GEN_MOVE_CHILD
void	MSG_GEN_MOVE_CHILD(
        optr		child,
        CompChildFlags		flags);

This message moves the given object from the location it currently occupies among its siblings to another location among its siblings. The object will still remain a child of the same parent.

Pass this message the optr of the child to move along with a word of CompChildFlags . If you pass the flag CCF_MARK_DIRTY, the new linkage will be saved to state when the application is detached. You must also pass a CCF_REFERENCE so the object will be moved to the position specified. CCO_FIRST and CCO_LAST will move the object to the first or last position, respectively.

If no flags are specified, the object will be moved to the parent object's first position without marking the linkages dirty. Note that for successive moves of children this will result in a "reverse order" of the children.

Note that this message only moves a child among its siblings. To move an object from one parent of the generic tree to another (different) parent, you must use MSG_GEN_REMOVE (or MSG_GEN_REMOVE_CHILD ) and MSG_GEN_ADD_CHILD .

Source: Unrestricted.

Destination: Any generic object.

Parameters: child The optr of the child to move.

flags
CompChildFlags to use when moving the child.

Interception: Generally not intercepted.

GenClass: 5.3 Generic Trees: Branch Construction/Destruction

MSG_GEN_COPY_TREE, MSG_GEN_DESTROY, MSG_GEN_DESTROY_AND_FREE_BLOCK, MSG_GEN_BRANCH_REPLACE_PARAMS, MSG_GEN_BRANCH_REPLACE_OUTPUT_OPTR_CONSTANT

In addition to instantiating and manipulating generic objects one at a time, you can create, move, and destroy entire branches. One way of doing this is copying a resource using ObjDuplicateResource() and sending MSG_GEN_ADD_CHILD to the parent object, passing it the optr of the top object in the tree. This method has the advantage of retaining the same chunk offsets to objects within the duplicated block as in the source block.

Code Display 2-22 Using ObjDuplicateResource()

@start ResourceToCopy;
/* For simplicity, the GenItems in this listing contain no monikers or
 * identifiers. */
@object GenItemGroupClass MyList = {
    GI_comp = @AirplaneEntry, @TrainEntry, @CarEntry, @BicycleEntry;
}
@object GenItemClass AirplaneEntry = {}
@object GenItemClass TrainEntry = {}
@object GenItemClass CarEntry = {}
@object GenItemClass BicycleEntry = {}
@end ResourceToCopy;
@method MyProcessClass, MSG_COPY_MY_RESOURCE {
    MemHandle newBlock;
    newBlock = ObjDuplicateResource(OptrToHandle(@MyList),
        /* Block will be owned by the same geode. */
                    -1,
        /* Block will be run by the same thread. */
                    -1);
    @call MyPrimary::MSG_GEN_ADD_CHILD(ConstructOptr(newBlock,
                    OptrToChunk(@MyList));
}

Another way to duplicate groups of objects is with MSG_GEN_COPY_TREE . This message greatly simplifies generic tree construction. Those branches of the tree which are roughly similar can be duplicated with one message instead of several.

In many cases, it might be useful to create a UI resource template. This template should contain objects but should not contain object-specific information such as visual monikers, optrs, etc., as this information may be different for different instances of the branch. You can then duplicate these templates with MSG_GEN_COPY_TREE . The trees can then be updated to reflect their unique data either object by object or by using MSG_GEN_BRANCH_REPLACE_PARAMS , which will replace instance data within an entire branch.

To set up a template, create the generic tree you wish to function as a template, making sure that the top-level object of that tree is not usable (~GS_USABLE). Then use MSG_GEN_COPY_TREE to copy the tree to the proper location. You can then set the other instance data within the method. Finally, to make that tree appear on screen, set the top-level object GS_USABLE. You can remove any tree created with MSG_GEN_COPY_TREE with MSG_GEN_DESTROY . If you use ObjDuplicateResource() to copy a resource block, you can use ObjFreeDuplicate() to remove it. If the generic branch to be destroyed resides completely within a single block, you may send MSG_GEN_DESTROY_AND_FREE_BLOCK to the top object in that generic branch.

MSG_GEN_COPY_TREE
optr	MSG_GEN_COPY_TREE(
        MemHandle destBlock,
        ChunkHandle parentChunk,
        word flags);

This message copies an entire branch of a generic object tree, starting at the object first called. Pass this message the handle of the destination object block. The object sent the message must not be GS_USABLE.

You may pass parentChunk null if you wish the block to remain unattached to the generic tree. Note that if a generic tree is copied using MSG_GEN_COPY_TREE , the objects contained therein not only will have a new resource handle but will also have new chunk handles. Note that this is different from using ObjDuplicateResource() , which will keep the same chunk offsets into the object block.

Unlike ObjDuplicateResource() , this message will copy the entire branch desired, even if those objects reside in separate resources. This message is therefore more flexible than ObjDuplicateResource() , which only copies a given object block.

GenClass provides the default behavior of broadcasting this message down a generic tree, where each object will make a copy of itself in the destination block. It creates an object chunk the same size as the object chunk being copied, copies over the entire contents of any instance fields and creates an outside chunk for a visual moniker, if any.

If you have a subclass of a generic object which references (and therefore might need to create) a chunk outside the object, you must be sure to intercept this message and allocate, copy over, and update any references to this chunk.

Source: Unrestricted.

Destination: Any generic object.

Parameters: destBlock The block to copy the tree into. This block cannot be the same as the source object block.

parentChunk
The chunk handle of a generic object in the destination block. The recipient of this message will be copied into parentChunk . If zero, copy tree into block without linking it to a particular chunk.
flags
CompChildFlags to use when adding the tree.

Interception: This message is handled by all generic objects. If you wish to intercept this message, you should first call the superclass to copy over the GenClass instance data and then copy over any lmem chunks that are referenced by instance data of the intercepting class.

Code Display 2-23 MSG_GEN_COPY_TREE

/* Create a template resource block. For demonstration purposes, this block will
 * only contain one object, a GenItem. All declared resources must also be
 * declared in an application's corresponding .gp file. */
@start MyTemplate;
@object GenItemClass TemplateItem = {
    GI_states = @default & ~GS_USABLE;					/* Make sure it is NOT usable. */
}
@end MyTemplate;
/* Start the Interface resource block */
@start Interface;
@object GenItemGroupClass MyList = { }
                /* Object the template above will be added to. */
@end Interface;
@method MyProcessClass, MSG_COPY_A_LIST_ITEM {
    optr		newListEntry;
    /* This call copies TemplateItem to the location at MyList. The macros
     * OptrToHandle and OptrToChunk are used to cast the optr of MyList into
     * the proper type for MSG_GEN_COPY_TREE. This new linkage will be marked
     * dirty, and the child will become the first child of MyList. */
    newListEntry = @call TemplateItem::MSG_GEN_COPY_TREE(
                OptrToHandle(@MyList),
                OptrToChunk(@MyList),
                (CCF_MARK_DIRTY | CCO_FIRST));
    /* The newListEntry is set usable to bring it up on screen. */
    @call newListEntry::MSG_GEN_SET_USABLE(VUM_NOW);
}
MSG_GEN_DESTROY
void	MSG_GEN_DESTROY(
        VisUpdateMode		updateMode,
        word		flags);

This message destroys a generic branch starting at the object called. The message sets all required generic objects not usable, removes them from the generic tree, and destroys them, including any chunks associated with the specific object destroyed. This message is the safest way to destroy a generic branch.

The message will leave the block that the objects previously resided in intact (except for objects removed, of course), but any chunks coming from those resources will be marked dirty and will be resized to zero.

It is usually a good idea to destroy generic branches that are not needed to be saved to state when an application detaches or exits. This ensures that when an application is reattached, it will not contain the previous links. This keeps the state file more compact and avoids the possibility of a child not being saved to state while retaining the parent's child pointer.

The only valid flag in CompChildFlags is CCF_MARK_DIRTY, which will mark the links as dirty and save them to state.

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode VisUpdateMode to determine when the generic tree is to be visually updated. May not be VUM_MANUAL.

flags
CompChildFlags to use when destroying the branch (either CCF_MARK_DIRTY or zero).

Interception: This message is handled by all generic objects. If you wish to intercept this message, you should first destroy any chunks that are referenced by instance data of the intercepting class and then call the superclass to destroy the object's GenClass instance data.

MSG_GEN_DESTROY_AND_FREE_BLOCK
Boolean	MSG_GEN_DESTROY_AND_FREE_BLOCK();

This is a utility message used to destroy a generic branch which resides completely within one block. The block will be freed. The object called with this message must be the only top object in the block. This message is called by UserDestroyDialog() .

If any object within the block resides on the application active list, or if the object is not a window, a slow, non-optimized approach is taken: the branch is set not usable, removed from the generic tree, and sent MSG_META_BLOCK_FREE . If no object of the block can be found on this list, an optimized approach is taken: the window is taken off the screen; FTVMC, mouse, etc. exclusives released; the linkage changed to be one-way upward; and the block sent MSG_META_BLOCK_FREE . In other words, the whole unbuild process is carefully avoided.

Source: Unrestricted

Destination: Top object within a block to be freed.

Return: true if the message was handled, false otherwise.

Interception: Generally not intercepted.

MSG_GEN_BRANCH_REPLACE_PARAMS
void	MSG_GEN_BRANCH_REPLACE_PARAMS(@stack
        BranchReplaceParamType				type,
        dword				replaceParam,
        dword				searchParam);

This message travels down a generic branch to all of an object's children; the message replaces all instance data specified in type with replacement instance data. A typical way to implement this is to set up a template UI branch with MSG_GEN_COPY_TREE . Most often, you will use this message to search and replace optrs set within your template. This is easily done by setting these optrs to certain constant values and then searching for those values in type . If you only wish to replace your destination optrs, use MSG_GEN_BRANCH_REPLACE_OUTPUT_OPTR_CONSTANT , which is just a specialized case of this message.

When replacing optrs, the constant BRPT_DUMMY_OPTR_START should be used as the searchParam value. If multiple optrs are to be replaced, enumerated values based on that value should be used for other optrs. These values will be constants, and since the TravelOption types are also constants, care must be taken to replace these dummy values before anything else is done on the generic branch; otherwise, their output may be interpreted as a TravelOption .

This message may not be supported by several subclasses of GenClass .

Source: Unrestricted.

Destination: Any generic object.

Parameters: searchParam Specifies the search parameter. Typically this search parameter is a pre-set constant used to identify the instance data to replace. The search parameter must be a dword.

replaceParam
Specifies the replacement parameter. Any instance data in the generic branch that matches searchParam will be replaced with the data specified in replaceParam . The data must be a dword.
type
A value of BranchReplaceParamType . The only value supported is BRPT_OUTPUT_OPTR, which specifies that all optrs with values matching searchParam should be replaced by the value in replaceParam .

Interception: This message is handled by most generic objects (although not all). If you wish to intercept this message, you should first call the superclass to search and replace any GenClass instance data, if applicable, and then search and replace any instance data in the intercepting class.

MSG_GEN_BRANCH_REPLACE_OUTPUT_OPTR_CONSTANT
void	MSG_GEN_BRANCH_OUTPUT_OPTR_CONSTANT(
        optr	replacementOptr,
        word	searchConstant);

This message is a special case of MSG_GEN_BRANCH_REPLACE_PARAMS where the BranchReplaceParamType is BRPT_OUTPUT_OPTR with a search parameter of a constant value set up beforehand. You must pass this message the optr to replace the search constant found.

Source: Unrestricted.

Destination: Any generic object.

Parameters: replacementOptr The optr to replace all instance fields with destinations matching the searchConstant .

searchConstant
The constant to search for and replace with the replacementOptr .

Interception: Generally not intercepted. This message invokes MSG_GEN_BRANCH_REPLACE_PARAMS so intercept that instead.

GenClass: 6 Vardata

Within GenClass , much of your instance data will appear within the basic instance fields allocated with @instance . These instance data fields are fixed; each generic object you create will contain each one of these instance fields. You may also make use of special instance data (variable data, also called "vardata") using the object variable data storage mechanism. See the GEOS Programming chapter for complete information on variable data.

Vardata performs many of the same functions that fixed instance data does. However, vardata differs significantly from fixed instance data in that it will only occupy memory if it is in fact declared. You may also only access vardata entries through special kernel routines and messages. For generic objects, most vardata is either in the form of attributes (beginning with ATTR_) or hints (beginning with HINT_).

Optional Attributes (ATTRs)

Hints to the Specific UI

Dynamically Managing VarData

GenClass: 6.1 Vardata: Optional Attributes (ATTRs)

Vardata attribute entries begin with the form ATTR_attributename (as in ATTR_GEN_DESTINATION_CLASS). You can initialize this data within your Goc object declaration. ATTR s within GenClass are available for use by any generic object. Several subclasses of GenClass also contain their own ATTR vardata entries. In GenClass no vardata attributes or hints are set by default.

ATTRs often behave as additional instance data that just happens to be vardata (and therefore optionally added). For generic objects, these should always be considered part of the generic state of an object and therefore may be saved out to the state file. You may, of course, decide not to save any such fields to the state file.

Temporary vardata to be used by an object class can be specified using the format TEMP _tempname. Any data set up with TEMP in a generic object should not be saved to state and should not be considered part of the API but rather an internal field. In almost all cases, you will not need to access such vardata entries.

For information on manipulating the variable length instance fields dynamically, along with a list of valid hints for GenClass , see Dynamically Managing VarData .

Destination Classes

 ATTR_GEN_DESTINATION_CLASS

ATTR_GEN_DESTINATION_CLASS specifies the object class that should handle messages sent out by this object. Typically, an object marked with this ATTR will not have a specific destination optr, but instead a destination path defined by a TravelOption . The message for a generic object with a destination class travels down the generic tree along the path specified in the TravelOption until it encounters an object class of type ATTR_GEN_DESTINATION_CLASS ; at that point, the message will be handled.

ATTR_GEN_DESTINATION_CLASS takes an argument of type DestinationClassArgs . This type contains a pointer to a ClassStruct . You may have to cast the class name into a ClassStruct type when setting ATTR_GEN_DESTINATION_CLASS.

 typedef struct {
    ClassStruct		*  DCA_class;
} DestinationClassArgs;

Code Display 2-24 Using ATTR_GEN_DESTINATION_CLASS

/* This object will send MSG_META_CUT to the application object and follow the
 * target path until it encounters a VisTextClass object. At that point, the
 * message will be handled. */
@object GenTriggerClass MyTrigger = {
    GI_visMoniker = "This trigger cuts text from the target object."
    GTI_actionMsg = MSG_META_CUT;
    GTI_destination = (TO_APP_TARGET);
/* The class pointer points to a ClassStruct structure. */
    ATTR_GEN_DESTINATION_CLASS = { (ClassStruct *)&VisTextClass };
}
/* To set up a destination class as any generic object, set
 * ATTR_GEN_DESTINATION_CLASS to GenClass. This interaction sends
 * MSG_GEN_SET_NOT_USABLE to the first focus object below the GenInteraction. */
@object GenTriggerClass MyTrigger = {
    GI_visMoniker = "Remove the Focus object";
    GTI_actionMsg = MSG_GEN_SET_NOT_USABLE;
    GTI_destination = (TO_FOCUS);
    ATTR_GEN_DESTINATION_CLASS = { (ClassStruct *)&GenClass };
}
   typedef enum {
        /* These values may be used as normal
         * TravelOptions, and have been set up
         * so that they will have no value in
         * common with normal TravelOptions. */
    TO_GEN_PARENT=_FIRST_GenClass,
    TO_FOCUS,
    TO_TARGET,
    TO_MODEL,
    TO_APP_FOCUS,
    TO_APP_TARGET,
    TO_APP_MODEL,
    TO_SYS_FOCUS,
    TO_SYS_TARGET,
    TO_SYS_MODEL,
} GenTravelOption;

The TravelOption types provided with GenClass are additional enumerations of the types supplied with MetaClass . MetaClass provides the types TO_NULL, TO_SELF, TO_OBJ_BLOCK_OUTPUT and TO_PROCESS. Therefore, any generic object can use these types or the further enumerations included here.

These TravelOption types typically replace a generic object's destination with a path for a message to be delivered along. Most often, these TravelOption s are used in conjunction with the vardata attribute ATTR_GEN_DESTINATION_CLASS. Together, they allow a message to follow a path specified in TravelOption until it encounters an object of a class specified in ATTR_GEN_DESTINATION_CLASS . This allows an object to generically send a message down an output path without knowing a specific destination.

Initialization File Management

MSG_GEN_LOAD_OPTIONS, MSG_GEN_SAVE_OPTIONS, ATTR_GEN_INIT_FILE_KEY, ATTR_GEN_INIT_FILE_CATEGORY, ATTR_GEN_INIT_FILE_PROPAGATE_TO_CHILDREN, ATTR_GEN_USES_HIERARCHICAL_INIT_FILE_CATEGORY

Your object may request information from the GEOS.INI file upon startup. Each object on the appropriate GenApplication GCN list may receive a MSG_GEN_LOAD_OPTIONS upon receiving MSG_META_ATTACH (and a MSG_GEN_SAVE_OPTIONS upon MSG_META_SAVE_OPTIONS ). Various generic objects intercept this message and perform actions according to the information provided.

Your object may also access information in the GEOS.INI file as it is being attached by including ATTR_GEN_INIT_FILE_CATEGORY of the GEOS.INI category you are interested in and the ATTR_GEN_INIT_FILE_KEY of the specific keyword you want the data for. If a file key, but no file category, exists within an object declaration, then the system will query the application object for its ATTR_GEN_INIT_FILE_CATEGORY.

Normally only the GenApplication object and any objects in its GCN list will receive MSG_GEN_LOAD_OPTIONS and MSG_GEN_SAVE_OPTIONS . If you wish other children below these objects to also receive these messages (and therefore request or save information from the GEOS.INI file) you can add the attribute ATTR_GEN_INIT_FILE_PROPAGATE_TO_CHILDREN. Any objects with this attribute will send these messages on down the generic tree to their children. Any children may then also contain this attribute and pass the messages on down to their children, etc.

Normally, the object itself and then the GenApplication object is queried for an ATTR_GEN_INIT_FILE_CATEGORY. If instead, the object wants a generic upward query of the object's parents, you can attach ATTR_GEN_USES_HIERARCHICAL_INIT_FILE_CATEGORY to the object. In this case, the object will be queried for its file category, and then query up the generic tree for the first object encountered with an ATTR_GEN_INIT_FILE_CATEGORY. In this manner, one parent may contain a file category and several children may contain different file keys within that category.

Because this method is recursive, it is slow and therefore should be avoided. Most applications should only need one init file category.

MSG_GEN_LOAD_OPTIONS
void	MSG_GEN_LOAD_OPTIONS(
        GenOptionsParams		*params);

This message instructs the generic object to load a value from the GEOS.INI file. It is called automatically by the handler in GenClass for MSG_META_LOAD_OPTIONS . The message scans the object's vardata for an ATTR_GEN_INIT_FILE_KEY and an ATTR_GEN_INIT_FILE_CATEGORY to scan the .INI file. If no file category is found in the object's vardata, the handler will search up the generic tree for a parent with an ATTR_GEN_INIT_FILE_CATEGORY.

You may send this message, passing it the proper GenOptionsParams . The return value must be cast into the proper type of whatever the entry within the file key is. This is a null-terminated text string.

Source: Sent by GenClass on MSG_META_ATTACH . GenClass generates the GenOptionsParams structure by looking in the object's vardata (and by querying up the generic tree, if necessary).

Destination: Any generic object.

Parameters: params GenOptionsParams structure (automatically generated by GenClass ). See MSG_GEN_SAVE_OPTIONS .

Return: Return values must be cast into the proper type of whatever the entry of category and file key is.

Interception: Various generic classes intercept this and provide default behavior

MSG_GEN_SAVE_OPTIONS
void	MSG_GEN_SAVE_OPTIONS(
        GenOptionsParams		*params);

This message instructs the generic object to save its options to the initialization file.

Source: Sent by GenClass on MSG_META_SAVE_OPTIONS . GenClass generates the proper GenOptionsParams structure by looking in the object's vardata (and querying up the generic tree, if necessary).

Destination: Any generic object.

Parameters: params GenOptionsParams structure (automatically generated by GenClass ).

Return: Return values must be cast into the proper type of whatever the entry of category and file key is.

Interception: Various generic classes intercept this and provide default behavior

Structures: The GenOptionsParams structure is defined as follows:

  typedef struct {
    char	GOP_category[INIT_CATEGORY_BUFFER_SIZE];
    char	GOP_key[INIT_CATEGORY_BUFFER_SIZE];
} GenOptionsParams;

Altering Default Geometry Management

ATTR_GEN_POSITION, ATTR_GEN_POSITION_X, ATTR_GEN_POSITION_Y

You may wish, in rare cases, to hard-wire a generic object's position relative to its parent. You can do this by including one of these attributes with a position offset from the parent. These attributes supersede the geometry manager; because of this, you should exercise extreme caution whenever using these attributes. You should only use these attributes in the rare cases when the geometry manager (and hints) cannot accommodate your geometry concerns.

ATTR_GEN_POSITION takes a Point argument, specifying the x and y offset (in points) from the parent's top left corner. This point will become the object's top left corner.

ATTR_GEN_POSITION_X specifies the horizontal (x) offset (in points) from the left edge of the parent. The vertical (y) offset will be left to the specific UI (or ATTR_GEN_POSITION_Y) to determine.

ATTR_GEN_POSITION_Y specifies the vertical (y) offset (in points) from the top edge of the parent. The horizontal (x) offset will be left to the specific UI (or ATTR_GEN_POSITION_X) to determine.

Several hints also accomplish similar geometry overrides. HINT_ALIGN_TOP_EDGE_WITH_OBJECT aligns an object's top edge with the top edge of the object (optr) this hint is set to. Similarly, HINT_ALIGN_LEFT_EDGE_WITH_OBJECT , HINT_ALIGN_RIGHT_EDGE_WITH_OBJECT , and HINT_ALIGN_BOTTOM_EDGE_WITH_OBJECT all line up an object's edge with the same edge of the object these hints are set to. One of these hints can be used in each direction. The same concerns over superceding the default geometry manager in the above attributes are valid here as well. Therefore, avoid using these hints if possible.

Altering Delayed Mode Activity

ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED, ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_ENABLED

Most generic objects act on changes made by the user. Some generic objects may not immediately react to these changes; instead these objects allow the user to select a variety of settings and then later "apply" these changes. These generic objects operate in delayed mode and each has an "apply message" associated with it. For these objects, the user may alter the state of the object or its children, but the application only sends out the action to perform those changes when the "apply" message is sent out.

For example, a dialog box may contain a list of settings that represent the state of a paragraph in a word processor. If the user ever actually changes those settings, the dialog box is marked as modified, but the changes are not actually made until the object receives notification to apply its changes (send out its apply message).

By default, these generic objects check whether their state has been modified since the last apply message has been sent out. If their state has not been modified and they receive a request to apply changes made, they instead will ignore the request. Likewise, objects set not enabled will ignore the request. ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED and ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_ENABLED override this behavior, telling the generic object to send out its apply message regardless of the modified or enabled state of a generic object.

In the above example, this would mean that whenever the dialog box receives notification to apply its changes it would not check whether its settings have been altered since the last time it applied its changes. Instead it would apply these changes anyway.

Notification of Visibility

ATTR_GEN_VISIBILITY_DATA, ATTR_GEN_VISIBILITY_MESSAGE, ATTR_GEN_VISIBILITY_DESTINATION

If a generic object has its GA_NOTIFY_VISIBILITY bit set, it will notify the GenApplication object when it becomes visible or not visible. The object does this by sending MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION to the GenApplication object, passing the sending object's optr as data.

You can alter this default behavior by including one or more attributes. ATTR_GEN_VISIBILITY_MESSAGE specifies a different message than MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION ; ATTR_GEN_VISIBILITY_DESTINATION specifies a different destination to send visibility notification to than the GenApplication object; ATTR_GEN_VISIBILITY_DATA specifies a different word of data to send out than the default data (the optr of the sending object). These attributes will have no effect on objects not marked GA_NOTIFY_VISIBILITY.

Generic Properties

ATTR_GEN_PROPERTY, ATTR_GEN_NOT_PROPERTY

Objects that exhibit properties (List Objects, GenValues, GenTexts, etc.) sometimes do not exhibit immediate effects when the user interacts with the object. These objects act in what is known as delayed mode ; they only apply their properties when their object receives a MSG_GEN_APPLY.

Properties objects such as these are usually placed within a GIT_PROPERTIES GenInteraction object. The GIT_PROPERTIES dialog box provides an "Apply" trigger to get the objects to apply their properties. You can, however, change this default behavior, though it is uncommon to need to do so.

ATTR_GEN_PROPERTY, when added to an object that exhibits properties, allows an object to act like it is within a properties dialog box even if it normally operates in immediate mode. That is, it will not apply its changes until explicitly told to do so (using MSG_GEN_APPLY).

ATTR_GEN_NOT_PROPERTY allows an object within a GIT_PROPERTIES dialog box to act like it is not within such a dialog box. That is, any user interaction with the object will result in an immediate application of those changes.

Window Management

GenClass supplies several optional attributes that affect the display of windows within the user interface. Windowed objects include GenPrimarys, GenDisplays, and GenInteractions (as dialog boxes).

The optional attributes that follow are divided into two groups. Window layering affects how windowed objects are arranged on the screen; i.e., where they appear and whether they appear above or below other windowed objects. In general, this layering behavior is only determined for windowed objects having the same parent, i.e., windows having the same parent can be custom layered, and their parents will be layered among its siblings.

There also exist optional attributes to alter the manner in which input travels within a window. These optional attributes allow the application to provide floating keyboards for pen input.

Window Layering
ATTR_GEN_WINDOW_CUSTOM_LAYER_ID, ATTR_GEN_WINDOW_CUSTOM_WINDOW_PRIORITY, ATTR_GEN_WINDOW_CUSTOM_LAYER_PRIORITY, ATTR_GEN_WINDOW_CUSTOM_PARENT, ATTR_GEN_WINDOW_CUSTOM_WINDOW

ATTR_GEN_WINDOW_CUSTOM_LAYER_ID sets a unique layer identifier for the windowed object. All objects with a common identifier are considered part of the same window layer. These layers can then be manipulated as a unit. By convention, these IDs are usually set to the handles owned by the application; this avoids collisions with other application window layer IDs.

If a null layer ID is specified, the handle of the block the object lies in will be selected as the layer ID at run-time. This is the common method of assigning layer IDs.

ATTR_GEN_WINDOW_CUSTOM_LAYER_PRIORITY allows a windowed object to set its layer priority to a custom value. The optional attribute expects an argument of type LayerPriority . Note that all windowed objects with the same Layer ID must have the same layer priority; therefore, this attribute must be used with care. It should only be used if a layer ID declared with ATTR_GEN_WINDOW_CUSTOM_LAYER_ID is also used.

ATTR_GEN_WINDOW_CUSTOM_WINDOW_PRIORITY sets a custom window priority for this specific window among all other windows of the same layer ID. This optional attribute expects an argument of type WindowPriority .

ATTR_GEN_WINDOW_CUSTOM_PARENT allows windowed objects to set a custom parent window. Because window layering applies only to the ordering of windows having the same parent, a window with a custom parent will have its layer ID, layer priority, and window priority compared to other windows having the same parent as ATTR_GEN_WINDOW_CUSTOM_PARENT.

If ATTR_GEN_WINDOW_CUSTOM_PARENT is set null, this indicates that the system screen window should be the parent. Because this window is the parent window for all base windows in the system, an object setting the screen window as its custom parent will be able to appear on top of (or below) all other windows in the system (that do not have a matching layer ID). This is very low-level behavior and should be used with caution, however.

Window Input Management
ATTR_GEN_WINDOW_ACCEPT_INK_EVEN_IF_NOT_FOCUSED, ATTR_GEN_WINDOW_KBD_OVERRIDE, ATTR_GEN_WINDOW_KBD_POSITION, MSG_GEN_SET_KBD_OVERRIDE, MSG_GEN_SET_KBD_POSITION

ATTR_GEN_WINDOW_ACCEPT_INK_EVEN_IF_NOT_FOCUSED indicates that the object should accept ink even if it doesn't have the focus. In general, presses on non-focused windows will never turn into ink. If this attribute is present, then presses on the window can turn into ink. The normal mechanism for determining if a press should be ink is then followed.

ATTR_GEN_WINDOW_KBD_OVERRIDE indicates the manner in which a floating keyboard is provided for a given window. This optional attribute expects an argument of type of KeyboardOverride . This attribute specifies whether a floating keyboard is provided for text input via a pen device.

The following KeyboardOverride values are valid:

KO_NO_KEYBOARD
This type indicates that the window should act as if none of its children accepts text input; no floating keyboard will be made available.
KO_KEYBOARD_REQUIRED
This type indicates that the window should act as if one of its children requires text input; a floating keyboard will be brought on-screen whenever the window gains the focus.
KO_KEYBOARD_EMBEDDED
This type indicates that the application is directly providing the keyboard; no system-created floating keyboard will be provided.

You can change the KeyboardOverride exhibited by a windowed object by sending the object MSG_GEN_SET_KBD_OVERRIDE.

The following rules will ensure that a keyboard will be present whenever the user interacts with a display requiring a keyboard: Set the KO_KEYBOARD_REQUIRED bit for any GenDisplay which should be accompanied by a floating keyboard. If all GenDisplays in your geode require a floating keyboard, then set this bit for each of them. GenDisplays that contain embedded keyboards should have the KO_KEYBOARD_EMBEDDED bit set instead of the KO_KEYBOARD_REQUIRED bit.

If you want a keyboard to appear visible even when no GenDisplays are open, mark the GenPrimary KO_KEYBOARD_REQUIRED and mark each GenDisplay KO_NO_KEYBOARD.

ATTR_GEN_WINDOW_KBD_POSITION specifies the default position for the floating keyboard to appear when the window gains the focus. If the user moves the floating keyboard, this attribute is updated to reflect the new position and the keyboard will appear at that position when brought up in the future.

You can change this keyboard position by sending the object MSG_GEN_SET_KBD_POSITION, passing it the Point to position the keyboard at.

MSG_GEN_SET_KBD_OVERRIDE
void	MSG_GEN_SET_KBD_OVERRIDE(
        KeyboardOverride 		override);

This message sets an object's ATTR_GEN_WINDOW_KBD_OVERRIDE to the passed KeyboardOverride value.

Source: Unrestricted.

Destination: Any windowed generic object.

Parameters: override KeyboardOverride .

Interception: Generally not intercepted.

MSG_GEN_SET_KBD_POSITION
void	MSG_GEN_SET_KBD_POSITION(
        sword		xCoord,
        sword		yCoord);

This message sets an object's ATTR_GEN_WINDOW_KBD_POSITION to the passed Point values.

Source: Unrestricted.

Destination: Any windowed generic object.

Parameters: xCoord X coordinate to position the keyboard (relative to the window).

yCoord
Y coordinate to position the keyboard (relative to the window).

Interception: Generally not intercepted.

Help Management

The Help system is discussed in full in the Help chapter. You should read that chapter if you wish to include help within your application. The information provided here is mostly an overview of that information.

Help Files
ATTR_GEN_HELP_FILE, ATTR_GEN_HELP_TYPE, ATTR_GEN_HELP_FILE_FROM_INIT_FILE, ATTR_GEN_HELP_CONTEXT

ATTR_GEN_HELP_FILE stores the help file associated with this object. Help files are generated by the help editor (a modified GeoWrite) from GeoWrite documents and are located in USERDATA\HELP.If the help system queries this object for a help file and this optional attribute does not exist, it will query up the generic tree for the first object with an ATTR_GEN_HELP_FILE.

An object can use ATTR_GEN_HELP_FILE_FROM_INIT_FILE to specify that its help file should be taken from the GEOS.INI file. If this attribute is used, the help controller will look in the GEOS.INI file for a category of the same name as the application and a key named "helpfile." Thus, to set the help file to "My Own Help File" for the HelpSamp application, you could add the following to your GEOS.INI file:

[HelpSamp]
helpfile = My Own Help File

If, however, no object has ATTR_GEN_HELP_FILE_FROM_INIT_FILE , this init file entry will not be noticed by the help controller.

ATTR_GEN_HELP_TYPE stores the HelpType associated with this object. If the help system queries this object for a help type and this optional attribute does not exist, it will query up the generic tree for the first object with an ATTR_GEN_HELP_TYPE.

Each help file must have several contexts named within the file. These contexts are set up when the help file is written. When an object brings up help, it should reference the specific context for which the user needs help. This context is stored within the object's ATTR_GEN_HELP_CONTEXT field. Usually this optional attribute is placed on a windowed GenInteraction (dialog box).

If an object needs to set a different help context for itself at run-time, it can use MSG_META_DELETE_VAR_DATA and MSG_META_ADD_VAR_DATA to alter the contents of the object's ATTR_GEN_HELP_CONTEXT.

Focus Help
ATTR_GEN_FOCUS_HELP, ATTR_GEN_FOCUS_HELP_LIB

ATTR_GEN_FOCUS_HELP is currently unsupported. This help mechanism is a simple, scaled down version of the previously mentioned help system. A fixed area at the bottom of the screen on certain displays acts as a help text area. An object with ATTR_GEN_FOCUS_HELP indicates that this text area should display the its help text when this object gains the focus within the application. This field expects an optr pointing to the required text.

ATTR_GEN_FOCUS_HELP_LIB is similar to ATTR_GEN_FOCUS_HELP but only applies to objects exported from a library(e.g., controllers).

Default Monikers

ATTR_GEN_DEFAULT_MONIKER, GenDefaultMonikerType

ATTR_GEN_DEFAULT_MONIKER specifies a default moniker for this object. Default monikers are generally used for gstring monikers that occur several places within the system (i.e., they are stored outside of the application). ATTR_GEN_DEFAULT_MONIKER expects an argument of type GenDefaultMonikerType .

typedef enum {
    GDMT_LEVEL_0,
    GDMT_LEVEL_1,
    GDMT_LEVEL_2,
    GDMT_LEVEL_3,
    GDMT_HELP,
    GDMT_HELP_PRIMARY
} GenDefaultMonikerType;

The GDMT_LEVEL monikers correspond to the gstring monikers used to indicate the current user level. GDMT_HELP corresponds to the default help trigger moniker. GDMT_HELP_PRIMARY corresponds to the special help trigger for a GenPrimary object.

Feature Links

ATTR_GEN_FEATURE_LINK

This optional attribute indicates that a feature within a GenControl object maps to multiple generic trees within the child block. If a feature in a GenControl so marked is turned off, for example, the controller will remove the feature from the first generic tree associated with this feature and then will remove the feature from the link to the next generic tree. Controllers and features are discussed more fully in the Controllers chapter.

Generic Paths

ATTR_GEN_PATH_DATA, MSG_GEN_PATH_SET, MSG_GEN_PATH_GET, GenFilePath

GenClass is able to keep track of file path information. GenClass does not use this information itself, but maintains it so that various subclasses (e.g., the file selector and document control) will be able to use the same instance fields and messages.

Because handlers for all these messages are implemented in GenClass , it is possible to store a path with any generic object, whether the particular object normally does anything with the file system or not.

The path itself is stored in the object's vardata in the ATTR_GEN_PATH_DATA field. In the absence of such a field, GenClass will use SP_TOP as the standard path. Subclasses can change this default by intercepting MSG_META_INITIALIZE_VAR_DATA , filling in the GenFilePath structure after it has called the superclass (which will actually allocate the attribute).

The ATTR_GEN_PATH_DATA field contains the following structure;

 typedef struct {
    DiskHandle 		GFP_disk;
    PathName 		GFP_path;
} GenFilePath;

GFP_ disk stores the handle of the disk on which the path resides. This value may be initialized to a StandardPath constant. GFP_ path stores the absolute path to the directory; this path may be a relative path if GFP_ disk is a StandardPath directory.

You can retrieve an object's current path with MSG_GEN_PATH_GET. You can alter an object's path with MSG_GEN_PATH_SET.

MSG_GEN_PATH_SET
Boolean 	MSG_GEN_PATH_SET(
        char 		*path,
        DiskHandle 		disk);

This message sets the path associated with the object. Normally, a complete path name must be passed. Note that this path string should not include a drive specifier, as this is implied by the passed disk handle. If the passed DiskHandle is actually a StandardPath value, then the path string will be taken to be relative to this standard path.

Source: Unrestricted.

Destination: Generic object which has an associated path.

Parameters: path Null-terminated pathname; may not be in the same block as the object receiving this message. (If null, default handler will use root directory of disk handle or directory associated with standard path.)

disk
Disk handle of path, or StandardPath constant.

Return: Returns true if error.

Interception: Generally not intercepted. If the object is specifically grown, the message will be forwarded to the specific class after it has been acted on by GenClass .

MSG_GEN_PATH_GET
Boolean 	MSG_GEN_PATH_GET(
        char 		*buffer,
        word 		bufSize);

This message returns a null-terminated complete path (no drive letter--drive is implied by the disk handle) for the object. Note that if the object is within a standard directory, the disk handle will be a StandardPath constant and the path will appear relative.

Source: Unrestricted.

Destination: Generic object which has an associated path.

Parameters: buffer Pointer to character buffer which will be filled with return value.

bufSize
Size of the above buffer.

Return: Returns true if error (if the path won't fit in the passed buffer or is invalid).

buffer
Null-terminated pathname.

Interception: Generally not intercepted.

MSG_GEN_PATH_GET_BLOCK
@alias (MSG_GEN_PATH_GET) MemHandle 	MSG_GEN_PATH_GET_BLOCK(
        char 		*buffer, 	/* This must be NULL */
        word 		bufSize);

This message returns the handle of a block containing the path.

Source: Unrestricted.

Destination: Generic object which has an associated path.

Parameters: buffer This must be NULL.

bufSize
This argument is not used.

Return: Handle of block containing path.

Interception: Generally not intercepted.

MSG_GEN_PATH_GET_DISK_HANDLE
DiskHandle MSG_GEN_PATH_GET_DISK_HANDLE();

This message returns the disk handle for the path bound to the object.

Source: Unrestricted.

Destination: Generic object which has an associated path.

Parameters: None.

Return: Disk handle associated with path, or StandardPath if object is associated with a standard directory.

Interception: Generally not intercepted.

GenClass: 6.2 Vardata: Hints to the Specific UI

Hints permit the developer to add suggested behavior to the user interface without requiring the specific UI to implement that functionality. Hints therefore provide the specific UI with less stringent guidelines than other attributes. The specific UI has the option of completely ignoring a hint if it cannot understand it or implement it in the requested fashion.

For example, some hints deal with the spacial arrangement of objects and can be ignored if the specific UI cannot accommodate their requests. Developers should use hints to add suggested user interface behavior throughout their UI. This suggested use, however, should not be crucial to their application.

For example, the hint HINT_LIST_CHECKBOXES has no effect under OSF/Motif because the OSF/Motif specification does not include checkboxes. However, in some other specific UI (OpenLook, for example) that feature may be implemented.

Code Display 2-25 Setting Hints

@object GenInteractionClass MyInteraction = {
    GI_comp = @OneTrigger, @TwoTrigger, @ThreeTrigger;
    /* Hints are added directly in an object's declaration. Note that each
     * vardata entry will expand the size of the object in memory. */
     HINT_ORIENT_CHILDREN_HORIZONTALLY;
    /* This hint instructs the specific UI to arrange the object's children
     * in one or more horizontal rows, if possible. */
}
/* Hints are entered separately in C if multiple hints are desired. */
@object MyObjectClass MyObject = {
    GI_comp = @MyObjectChild;
    HINT_LIST_CHECKBOXES;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}

System Attributes

SystemAttrs, HINT_IF_SYSTEM_ATTRS, HINT_ELSE, HINT_ENDIF

The system attributes are global flags that describe what sort of environment is running the generic object. HINT_IF_SYSTEM_ATTRS is used to conditionally add hints to an object based on these SystemAttrs . If the SystemAttrs set in the HINT_IF_SYSTEM_ATTRS field are true for the current system, then the hints that follow (until a HINT_ENDIF is encountered) are included. If no HINT_ENDIF is encountered, then only the next hint is included. If the SystemAttrs do not match the current system, the following group of hints is instead deleted.

The following SystemAttrs are defined:

SA_TINY
If set, the screen must be either horizontally or vertically tiny.
SA_HORIZONTALLY_TINY
If set, the screen must be horizontally tiny.
SA_VERTICALLY_TINY
If set, the screen must be vertically tiny.
SA_COLOR
If set, the system must have a color screen.
SA_PEN_BASED
If set, the system must be pen-based.
SA_KEYBOARD_ONLY
If set, the system must be set keyboard-only.
SA_NO_KEYBOARD
If set, the system must be set no-keyboard.
SA_NOT
If this flag is set, any other set bits indicate criteria that must not match. This is the equivalent of a logical NOT operation.

The HINT_ELSE hint can be used between HINT_IF_SYSTEM_ATTRS and HINT_END_IF. This allows the inclusion of a separate set of hints to be included when the system conditions in HINT_IF_SYSTEM_ATTRS are not satisfied.

Code Display 2-26 System Attributes

/* The SA_NOT bit acts as a logical not operation. In this case, the hint
 * HINT_EXPAND_HEIGHT_TO_FIT_PARENT will only be included in the object
 * declaration if the system is not pen-based. In addition, if the system is tiny,
 * the other hints will be included. The HINT_ENDIF marks the end of these
 * conditional hints. */
    HINT_IF_SYSTEM_ATTRS = SA_NOT | SA_PEN_BASED;
    HINT_EXPAND_HEIGHT_TO_FIT_PARENT;
    HINT_IF_SYSTEM_ATTRS = SA_TINY;
    HINT_MINIMIZE_SIZE;
    HINT_DISPLAY_CURRENT_SELECTION;
    HINT_ENDIF;
/* Example within an object declaration. */
@object GenInteractionClass GroupingObject = {
    GI_comp = @One, @Two, @Three, @Four, @Five, @Six;
    HINT_IF_SYSTEM_ATTRS = SA_HORIZONTALLY_TINY;
        HINT_ORIENT_CHILDREN_VERTICALLY;
        HINT_WRAP_AFTER_CHILD_COUNT = 3;
    HINT_ELSE;
        HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_ENDIF;
}

Default Actions

HINT_DEFAULT_DEFAULT_ACTION, HINT_ENSURE_TEMPORARY_DEFAULT, HINT_PREVENT_DEFAULT_OVERRIDES

Normally, objects in the user interface are activated by direct action (e.g., clicking on them). Objects may also be indirectly activated, however. The object activated indirectly is known as the default action object. For example, within a window, pressing ENTER or sending the window MSG_GEN_ACTIVATE_INTERACTION_DEFAULT will activate the object marked as the default action.

HINT_DEFAULT_DEFAULT_ACTION marks an object as the default action for the window it appears in. This hint is only relevant for GenTriggers or dialog GenInteractions (that are brought up by an activation trigger). The default activation object is usually activated by pressing the ENTER key within the window.

Normally, whenever a trigger is activated within a window, it will become the default activation object the next time around. Even if a trigger has HINT_DEFAULT_DEFAULT_ACTION, if some other trigger is activated, that other trigger will become the default action in the future. To prevent these automatic default overrides, add HINT_PREVENT_DEFAULT_OVERRIDES to the object's instance data.

HINT_ENSURE_TEMPORARY_DEFAULT ensures that an object that can be navigated to (via the TAB key for example) will act as a default activation object even if the specific UI does not normally allow such behavior. (OSF/Motif does this automatically.)

Keyboard Navigation Hints

HINT_NAVIGATION_ID, HINT_NAVIGATION_NEXT_ID

Most specific UIs allow keyboard navigation within windows, usually through use of the TAB key. Normally, the navigation path follows the order of the children within the windowed object, and this is sufficient for most needs.

HINT_NAVIGATION_ID sets a navigation identifier for an object. Objects may "jump" to this object by including HINT_NAVIGATION_NEXT_ID in their instance data with a value equal to the matching navigation ID of the object to travel to.

Code Display 2-27 Navigation IDs

/* Essentially, this code allows keyboard navigation to skip the Two
 * trigger. Hitting the TAB key on the `One' trigger navigates the focus to the
 * Three trigger. */
@object GenTriggerClass One = {
    GI_visMoniker = "TAB here to get to Three";
    HINT_NAVIGATION_NEXT_ID = 3;
}
@object GenTriggerClass Two = {
    GI_visMoniker = "2";
}
@object GenTriggerClass Three = {
    GI_visMoniker = "3";
    HINT_NAVIGATION_ID = 3;
}

Note that an object with a matching navigation ID must exist, so this method is not recommended. If possible, your UI should be constructed in such a way to allow the default behavior.

GenClass: 6.3 Vardata: Dynamically Managing VarData

MSG_META_ADD_VAR_DATA, MSG_META_DELETE_VAR_DATA, MSG_GEN_ADD_GEOMETRY_HINT, MSG_GEN_REMOVE_GEOMETRY_HINT

You must use special kernel routines and MetaClass messages to access, add, or remove entries within an object's instance fields.

  • ObjVarScanData()
     This routine scans an object's vardata and calls routines listed in the variable data handler table. This routine is useful for processing several HINTs and ATTRs at one time.
  • MSG_META_ADD_VAR_DATA
     You can use this message to add a vardata entry to an object. The object must first be set not usable before adding the entry. Set the object usable again after you have added the vardata entry.
  • MSG_META_DELETE_VAR_DATA
     Use this message to delete a vardata entry from an object. The object must first be set not usable before removing the entry. Set the object usable again after you have removed the vardata entry.
  • ObjVarFindData()
     This routine checks the object for the passed type and returns a pointer to its data. The message returns true if the entry is present, false if it is not. This routine is useful for processing a single vardata entry.

In addition to these routines and messages, two messages specific to GenClass allow you to add or remove hints dynamically: MSG_GEN_ADD_GEOMETRY_HINT and MSG_GEN_REMOVE_GEOMETRY_HINT . These messages allow geometry and window positioning hints to be added or removed while the object is on-screen. Sending a GS_USABLE object one of these messages with a valid geometry hint forces that object to redraw itself according to the new geometry.

MSG_GEN_ADD_GEOMETRY_HINT
void	MSG_GEN_ADD_GEOMETRY_HINT(
        word		hint,
        word		hintArgument,
        VisUpdateMode		updateMode);

This message adds a geometry hint to a generic object. The object may be GS_USABLE at the time, in which case the object is redrawn according to the new geometry configuration.

Source: Unrestricted.

Destination: Any generic object.

Parameters: hint The hint to add to the object's instance data

hintArgument
A word of data for any hints that require an argument.
updateMode
VisUpdateMode to determine when the object will be redrawn.

Interception: Generally not intercepted.

MSG_GEN_REMOVE_GEOMETRY_HINT
void	MSG_GEN_REMOVE_GEOMETRY_HINT(
        word		hint,
        VisUpdateMode		updateMode);

This message removes a geometry hint from an object's instance data. The object may be GS_USABLE at the time, in which case it will be redrawn according to the new geometry configuration.

Source: Unrestricted.

Destination: Any generic object.

Parameters: hint The hint to remove.

updateMode
VisUpdateMode to determine when the object will be redrawn.

Interception: Generally not intercepted.

GenClass: 7 Special Message Passing

It is often useful to pass messages "generically" rather than having to specify the particular optr directly. For example, in many cases, retrieving and storing the optr of an object is cumbersome, and it would be much easier to say "deliver this message to my parent." GenClass provides many useful messages which handle cases such as these. These message passers are of three types:

  • Parent and Child Generic Message Passers
  • Generic Upward Queries
  • Object-Specific Upward Queries

In all cases, the message must be stored as a classed event . A classed event is a combination of a class and a message. The message itself may pass data. The classed event enables the object to specify which class it would like the message to be handled by. In Goc, a classed event is created by encapsulating the message and class using the @record keyword.

Parent and Child Message Passing

Generic Upward Queries

Object-Specific Queries

GenClass: 7.1 Special Message Passing: Parent and Child Message Passing

MSG_GEN_CALL_PARENT, MSG_GEN_SEND_TO_PARENT, MSG_GEN_SEND_TO_CHILDREN

Three messages enable you to pass other messages to a generic object's parents and children without having to know the proper optrs. Using these messages, you can perform operations on any generic object's parents and children. If you need return values from an operation being performed by the parent object, use MSG_GEN_CALL_PARENT . If no return values are needed, you may use MSG_GEN_SEND_TO_PARENT .

To pass a message to all of your generic children, use MSG_GEN_SEND_TO_CHILDREN . Note that no comparable MSG_GEN_CALL_CHILDREN exists because it is meaningless to expect return values from multiple objects for a single event.

You may also use the Goc macros genParent and genChildren to send a message to a parent or the generic children of an object.

MSG_GEN_CALL_PARENT
void	MSG_GEN_CALL_PARENT(
        EventHandle		event);

This message delivers an event to the generic parent of the recipient. This message must pass a classed event that the parent object will handle. You should use this message if return values are expected. Always make sure to cast the return (following the call) into the proper type. The most effective way to do this is by enclosing the actual message sent within parentheses. The event will be freed after it is sent.

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to parent of this object.

Return: The return value of the classed event (cast to the proper type).

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

Code Display 2-28 MSG_GEN_CALL_PARENT

/* The following method retrieves the visual moniker
 * of an object's parent. */
@method MyProcessClass, MSG_GET_MY_PARENTS_MONIKER {
    ChunkHandle parentMoniker;
    EventHandle myEvent;
    /* Encapsulate the message to be handled
     * by any generic (GenClass) object. */
    myEvent = @record GenClass::MSG_GEN_GET_MONIKER();
/* Calls the parent of EntryNumberTwo with the classed event specified above. Note
 * that the return value is cast to type (MSG_GEN_GET_MONIKER) because
 * MSG_GEN_CALL_PARENT itself returns void. */
    parentMoniker = @call (MSG_GEN_GET_MONIKER)
            @EntryNumberTwo::MSG_GEN_CALL_PARENT(myEvent);
    return(parentMoniker); /* return the parentMoniker. */
}
MSG_GEN_SEND_TO_PARENT
void	MSG_GEN_SEND_TO_PARENT(
        EventHandle event);

This message sends an encapsulated event to the parent but expects no return values. The event will be freed after it is sent.

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to parent of this object.

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_SEND_TO_CHILDREN
void	MSG_GEN_SEND_TO_CHILDREN(
        EventHandle event);

This message sends an encapsulated event to all children of the generic object receiving it. This message cannot return values. The event will be freed after it is sent.

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to all children.

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

GenClass: 7.2 Special Message Passing: Generic Upward Queries

MSG_GEN_GUP_CALL_OBJECT_OF_CLASS, MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS, MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS, MSG_GEN_GUP_FIND_OBJECT_OF_CLASS, MSG_GEN_GUP_QUERY, MSG_GEN_GUP_INTERACTION_COMMAND, MSG_GEN_GUP_FINISH_QUIT

GenClass provides the capability to search up the generic tree beyond the parent. Using the following messages, the UI can continue passing classed events up the generic tree until it reaches an object of the proper class. This behavior is known as a Generic UPward query (GUP).

For example, if you specify GenInteractionClass in your classed event and later send a GUP message to any object in the generic tree, the stored message will be handled at the first available GenInteraction object it encounters.

MSG_GEN_GUP_CALL_OBJECT_OF_CLASS performs a GUP, returning values from the passed message. You must cast the return values into the proper type based on the return values of the passed message. MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS performs a GUP but does not allow return values.

MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS performs a GUP whose sole function is to search for the existence of an object of the specified class among the object's parents.

MSG_GEN_GUP_FIND_OBJECT_OF_CLASS performs a GUP searching for any object of the passed class, but it also returns the optr of the object found. Note that it is unwise to later use this optr as the generic tree may have changed.

MSG_GEN_GUP_CALL_OBJECT_OF_CLASS
void	MSG_GEN_GUP_CALL_OBJECT_OF_CLASS(
        EventHandle event);

This message performs a generic upward query, passing the classed event upward until it reaches an object of the passed class. This message allows return values and should be cast into whatever return values are expected of the passed message. This is most easily done by enclosing the message sent within parentheses.

An object of the expected class should be present or the event will not be delivered to any object. To check for the existence of such a class, use MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS .

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to an object up the generic tree.

Return: The return values of the classed event (cast to the proper type).

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS
void	MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS(
        EventHandle event);

This message performs a generic upward query, passing the classed event upward until it reaches an object of the described class. Since this message performs a send, it does not allow return values. An object of the expected class should be present; otherwise, the event will not be delivered to any object. To check for the existence of such a class, use MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS .

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to an object up the generic tree.

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

Code Display 2-29 Nesting Classed Events

/* The following method can be used by any object in the generic tree below the
 * primary to set all menus and GenInteractions below the primary window enabled
 * all at once. */
@method MyProcessClass, MSG_ENABLE_ALL_MY_MENUS {
    EventHandle menuEvent, primaryEvent;
/* The first classed event (menuEvent) encapsulates MSG_GEN_SET_ENABLED for any
 * object of GenInteractionClass (typically menus). The @record keyword
 * encapsulates the message to be handled by GenInteractionClass. The
 * VisUpdateMode delays updates via the Application queue to avoid constant visual
 * updates and "flashing." */
    menuEvent = @record GenInteractionClass::MSG_GEN_SET_ENABLED(
                    VUM_DELAYED_VIA_APP_QUEUE);
/* The second classed event binds MSG_GEN_SEND_TO_CHILDREN to the GenPrimaryClass.
 * Sending this message to the GenPrimary will send the menuEvent to all
 * its children. In this case, sending this event to the Primary will send
 * MSG_GEN_SET_ENABLED to all its GenInteraction children. */
    primaryEvent = @record GenPrimaryClass::MSG_GEN_SEND_TO_CHILDREN(menuEvent);
/* Finally, to send this message to the GenPrimary from any object below the
 * GenPrimary, simply send a MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS with the
 * PrimaryEvent above. The GUP message will send the message
 * MSG_GEN_SEND_TO_CHILDREN up the generic tree until it encounters the GenPrimary,
 * at which point the Primary will send the MenuEvent MSG_GEN_SEND_TO_CHILDREN to
 * all its GenInteraction children. */
    @send @MyObject::MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS(primaryEvent);
}
MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS
Boolean	MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS(
        ClassStruct  *class);

This message searches up the generic tree for any object of the specific class. This message is useful for testing the existence of required classes before sending MSG_GEN_GUP_CALL_OBJECT_OF_CLASS and MSG_GEN_GUP_SEND_TO_OBJECT_OF_CLASS .

Source: Unrestricted.

Destination: Any generic object.

Parameters: class ClassStruct to search for among the object's parents.

Return: Will return true if the class is found, false if not.

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_GUP_FIND_OBJECT_OF_CLASS
optr	MSG_GEN_GUP_FIND_OBJECT_OF_CLASS(
        ClassStruct  *class);

This message performs a function similar to that of MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS , but it returns the optr of the class found, if any. This optr can then be used as the recipient of other messages. You should not use an optr returned by this message at a later time, as the object may have been removed.

Source: Unrestricted.

Destination: Any generic object.

Parameters: class ClassStruct to search for.

Return: The optr of the object of the requested class, or a null optr if no such object was found.

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_GUP_QUERY
void 	MSG_GEN_GUP_QUERY(
        GenReturnParams 		*retValue,
        word 		queryType);

This message is used to query up the generic composite tree.

Source: Anyone.

Destination: Any usable generic object, though only certain objects will answer certain queries.

Parameters: retValue Structure to hold return values.

queryType
GenUpwardQueryType .

The GenUpwardQueryType should be one of the following:

GUQT_DELAYED_OPERATION
This query type determines if a gadget should be operating in "delayed mode," i.e., whether it should wait for a MSG_GEN_APPLY before sending notification to its output, or whether it should send notification whenever it changes state.
GUQT_FIELD
Returns field object in GRP_ cx :GRP_ dx and window (if realized) handle in GRP_ bp . This is a reasonably safe thing to do, since the field window will remain open until all applications within the field are shut down. Only field objects or substitutes for them should intercept this message.
GUQT_SCREEN
Returns screen object in GRP_ cx :GRP_ dx and window (if any) handle in GRP_ bp . This is a reasonably safe thing to do, since the screen window will remain open until all applications within the field are shut down. Screen object being returned will be default screen for the field.

Return: Nothing returned explicitly. retValue struct filled with values which depend on query type.

Structures:

  typedef struct {
    word 	GRP_ax;
    word 	GRP_bp;
    word 	GRP_cx;
    word 	GRP_dx;
} GenReturnParams;
MSG_GEN_GUP_INTERACTION_COMMAND
Boolean 	MSG_GEN_GUP_INTERACTION_COMMAND(
        InteractionCommand 		command);

This message travels up the generic tree until it reaches a non-GIT_ORGANIZATIONAL GenInteraction. Once there, it performs the indicated InteractionCommand . See InteractionCommand declaration for the different command types and how specific user interfaces interpret the commands. If the GenInteraction is a dialog brought up with UserDoDialog() , InteractionCommand will be returned to the caller, after performing the command, via MSG_GEN_INTERACTION_RELEASE_BLOCKED_THREAD_WITH_RESPONSE .

Source: Unrestricted. Normally sent by a MSG_GEN_INTERACTION_ACTIVATE_COMMAND handler or by a custom trigger's action message handler to perform some default command after perform some application-specific function.

Destination: Any generic object. Will travel up generic tree, stopping at first non organizational interaction.

Parameters: command Standard InteractionCommand.

Return: Returns true if handled.

Interception: Not normally intercepted.

MSG_GEN_GUP_FINISH_QUIT
void 	MSG_GEN_GUP_FINISH_QUIT(
        Boolean 		abortFlag,
        Boolean 		notifyParent);

Message to be used by nodes that implement a GCN active list. This should be used if MSG_META_QUIT is handled and sent to the active list items by that node. When a node has finished sending a MSG_META_QUIT to the active list or if the quit is aborted, this allows the notification of the above node in the generic tree.

Source: Sent by active list nodes that implement quit mechanism.

Destination: Any generic object involved in quit mechanism.

Parameters: abortFlag Pass true to abort. This will be passed on to MSG_META_FINISH_QUIT .

notifyParent
Pass true to notify parent of finished quit sequence.

Interception: Generally not intercepted. Default handler sends MSG_META_FINISH_QUIT to self, then sends to generic parent if flag indicates such. MSG_META_FINISH_QUIT is normally the message to intercept to know when a quit is aborted or finished.

GenClass: 7.3 Special Message Passing: Object-Specific Queries

MSG_GEN_CALL_APPLICATION, MSG_GEN_SEND_TO_PROCESS, MSG_GEN_CALL_SYSTEM,MSG_GEN_OUTPUT_ACTION MSG_GEN_QUERY_NEED_TO_BE_ON_ACTIVE_LIST

Besides the Generic Upward Queries, GenClass also contains several object-specific queries. These messages behave in the same manner as the GUP messages except that there is no need to set up a classed event. The object-specific upward query automatically sets up the class to send the message to.

MSG_GEN_CALL_APPLICATION sends a message up the generic tree to the GenApplication object. This message allows return values and should be cast to whatever return values are expected of the passed message.

MSG_GEN_SEND_TO_PROCESS sends a message to the process object associated with this object. Because this message may cross threads, no return values are allowed.

MSG_GEN_CALL_SYSTEM sends a message up the generic tree to the GenSystem object. This message allows return values and should be cast into whatever return values are expected of the passed message.

MSG_GEN_QUERY_NEED_TO_BE_ON_ACTIVE_LIST

MSG_GEN_CALL_APPLICATION
void	MSG_GEN_CALL_APPLICATION(
        EventHandle event);

This message calls the GenApplication object associated with the recipient. This message allows return values and should be cast to whatever return values are expected of the passed message.

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to the object's GenApplication object.

Return: The return values of the classed event (cast to the proper type).

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_SEND_TO_PROCESS
void	MSG_GEN_SEND_TO_PROCESS(
        EventHandle event);

This message sends the event to the Process object of your application. Because the message may cross threads, return values are not allowed.

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to the object's GenProcess object.

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_CALL_SYSTEM
void	MSG_GEN_CALL_SYSTEM(
        EventHandle event);

This message calls the GenSystem object (the root object) associated with your application and sends the event. If you expect return values, make sure to cast them into the proper variable type.

Source: Unrestricted.

Destination: Any generic object.

Parameters: event The classed event to deliver to the object's GenSystem object.

Return: The return values of the classed event (cast to the proper type).

Interception: Generally not intercepted. Custom gadgets may handle to supplement or supersede default functionality.

MSG_GEN_OUTPUT_ACTION
 void	MSG_GEN_OUTPUT_ACTION(
        EventHandle		event,
        optr		dest);

This message sends an event via the destination optr (if any) of the object sent the message. The default handler (which is rarely subclassed) also scans the vardata looking for an ATTR_GEN_DESTINATION_CLASS and records that class in a classed event.

This message is usually sent at a low level and dispatches a message via the destination optr of the object. You may intercept MSG_GEN_OUTPUT_ACTION if you wish knowledge of when an object is being activated (although you may wish to intercept MSG_GEN_ACTIVATE in those cases). If you do send this message yourself, you must either pass a destination optr or leave it null, depending on the object being activated.

Source: Normally sent by an object to itself.

Destination: Normally sent by an object to itself.

Parameters: event A classed event.

optr
The destination optr to send event to or a TravelOption .

Interception: Generally not intercepted.

MSG_GEN_QUERY_NEED_TO_BE_ON_ACTIVE_LIST
Boolean	MSG_GEN_QUERY_NEED_TO_BE_ON_ACTIVE_LIST();

This message queries whether an object still needs to be on an active list. The active list mechanism sends this message if a mechanism no longer needs an object to remain on the active list; it checks whether any other mechanism also needs the object to be on the active list also. Windowed specific UI objects, for example, will return true if ATTR_INITIATE_ON_STARTUP is present on the object, because this optional attribute requires the object to be on the active list.

Source: Unrestricted, though it is generally called on itself. Specifically, if an object no longer needs to remain on the active list for a particular reason, this message should be called on the object to see if it should remain on the active list for any other reason. If so, the caller should not remove the object from the active list. The only exception to this would be if the object were being destroyed, in which the object should be removed in any case.

Destination: Any generic object.

Return: true if the object needs to remain on the active list for some other reason.

Interception: Object classes which add the object to the active list must intercept this message and return true if the class desires the object to remain on the active list. If the message is intercepted and this is not the case, it should be sent to the superclass for handling.

GenClass: 8 Visual Refreshing

MSG_GEN_UPDATE_VISUAL

MSG_GEN_UPDATE_VISUAL performs a visual update of the object sent the message. Use this message to force a visual update on objects previously updated using the VisUpdateMode VUM_MANUAL.

MSG_GEN_UPDATE_VISUAL
void	MSG_GEN_UPDATE_VISUAL(
        VisUpdateMode updateMode);

This message updates the visual tree at the node specified and is used for updating objects modified with VUM_MANUAL.

Source: Unrestricted.

Destination: Any generic object.

Parameters: updateMode VisUpdateMode to determine when the object should be redrawn.

Interception: Generally not intercepted.

GenClass: 9 Setting Sizes

MSG_GEN_SET_INITIAL_SIZE, MSG_GEN_SET_MINIMUM_SIZE, MSG_GEN_SET_MAXIMUM_SIZE, MSG_GEN_SET_FIXED_SIZE, MSG_GEN_GET_INITIAL_SIZE, MSG_GEN_GET_MINIMUM_SIZE, MSG_GEN_GET_MAXIMUM_SIZE, MSG_GEN_GET_FIZED_SIZE, MSG_GEN_RESET_TO_INITIAL_SIZE, CompSizeHintArgs

These messages all perform resizing operations on generic objects. In some cases, the geometry is not redone by sending these messages; the object will need to be set not usable and then set usable to force the new sizing scheme. In most cases, however, sending these messages will affect the current geometry of an object. These messages manipulate their associated hints, either by creating new hints or by altering old ones. As such, these messages will only affect an object if the associated hint does also.

These messages alter four size restrictions associated with an object: initial, maximum, minimum, and fixed. These restrictions are set up using the GenClass hints HINT_INITIAL_SIZE , HINT_MAXIMUM_SIZE , HINT_MINIMUM_SIZE , and HINT_FIXED_SIZE , all of which are described in the Managing UI Geometry chapter.

The size parameter required by each of these hints is a structure of type CompSizeHintArgs . This structure is shown below:

  typedef struct {
    SpecWidth			CSHA_width;
    SpecHeight			CSHA_height;
    sword			CSHA_count;
} CompSizeHintArgs;

Many of the messages pass a structure of GetSizeArgs to place size information retrieved from the message. This size information is used by the specific UI to visually construct the object, bypassing default sizes in the process. The structure is shown below:

typedef struct {
    word		GSA_width;
    word		GSA_height;
    word		GSA_unused;
    word		GSA_count;
} GetSizeArgs;
MSG_GEN_GET_INITIAL_SIZE
void	MSG_GEN_GET_INITIAL_SIZE(
        GetSizeArgs *initSize);

This message retrieves the initial size specifications of a generic object stored in the hint HINT_INITIAL_SIZE. The object must be passed a pointer to a structure of type GetSizeArgs to store the retrieved data. If the hint HINT_INITIAL_SIZE does not exist for this object , the structure passed to the object of size GetSizeArgs will remain empty.

Source: Unrestricted.

Destination: Any generic object.

Parameters: initSize A pointer to an empty GetSizeArgs structure.

Return: The GetSizeArgs structure will contain the object's initial size.

Interception: Generally not intercepted.

MSG_GEN_SET_INITIAL_SIZE
void	MSG_GEN_SET_INITIAL_SIZE(@stack
        byte		updateMode,
        word		count,
        word		height,				/* SpecHeight */
        word		width);				/* SpecWidth */

This message sets the initial size of a generic object (its size on being first built). This is only done by creating or modifying the hint HINT_INITIAL_SIZE ; there is no guarantee that the specific UI will implement this message in the expected fashion.

The message must pass a VisUpdateMode . This message will only affect the size of an object when it first opens; it will not modify the geometry of a currently built object. For the defined geometry to take effect on an already built object, the object must be set not usable and then set usable again.

Source: Unrestricted.

Destination: Any generic object.

Parameters: count The number of children (or zero, if not applicable).

height
The height of each child.
width
The width of the composite.

Interception: Generally not intercepted.

MSG_GEN_RESET_TO_INITIAL_SIZE
void	MSG_GEN_RESET_TO_INITIAL_SIZE(
        VisUpdateMode		updateMode);

This message forces the recipient and all of its children to be resized according to their initial size specifications. If the objects contain HINT_INITIAL_SIZE , they will be resized according to the hint; otherwise they will be resized according to their default geometry. This message is useful for activating sizes set with MSG_GEN_SET_INITIAL_SIZE .

Source: Unrestricted.

Destination: Any GS_USABLE generic object.

Parameters: updateMode VisUpdateMode to determine when the object will be redrawn.

Interception: Generally not intercepted.

MSG_GEN_GET_MINIMUM_SIZE
void	MSG_GEN_GET_MINIMUM_SIZE(
        GetSizeArgs  *minSize);

This message retrieves the recipient's minimum size specifications stored in the hint HINT_MINIMUM_SIZE . If HINT_MINIMUM_SIZE is not set , the structure passed to the object will remain empty.

Source: Unrestricted.

Destination: Any generic object.

Parameters: minSize A pointer to an empty GetSizeArgs structure.

Return: The GetSizeArgs structure will contain the object's minimum size.

Interception: Generally not intercepted.

MSG_GEN_SET_MINIMUM_SIZE
void	MSG_GEN_SET_MINIMUM_SIZE(@stack
        byte	updateMode,
        word	count,
        word	height,				/* SpecHeight */
        word	width);				/* SpecWidth */

This message sets the minimum allowable size of an object. An object with a minimum size is not allowed to shrink smaller than the bounds set in HINT_MINIMUM_SIZE . This message alters the minimum size by creating or modifying the hint HINT_MINIMUM_SIZE . Due to the nature of hints, there is no guarantee that the specific UI will implement this message in the expected fashion.

This message will modify the geometry of a currently built object, forcing that object to be unbuilt and then built again. Note that the minimum size of an object has nothing to do with the minimized state of an object (for GenDisplays, the iconification of an object) but merely the minimum allowable size of an object in its normal usable state.

Source: Unrestricted.

Destination: Any generic object.

Parameters: count The number of children (or zero, if not applicable).

height
The height of each child.
width
The width of the composite.

Interception: Generally not intercepted.

MSG_GEN_GET_MAXIMUM_SIZE
void	MSG_GEN_GET_MAXIMUM_SIZE(
        GetSizeArgs  *maxSize);

This message retrieves the recipient's maximum size stored in the hint HINT_MAXIMUM_SIZE . If HINT_MAXIMUM_SIZE is not set , the structure passed will remain empty.

Source: Unrestricted.

Destination: Any generic object.

Parameters: maxSize A pointer to an empty GetSizeArgs structure.

Return: The GetSizeArgs structure will contain the object's maximum size.

Interception: Generally not intercepted.

MSG_GEN_SET_MAXIMUM_SIZE
void	MSG_GEN_SET_MAXIMUM_SIZE(
        word	count,
        word	height,				/* SpecHeight */
        word	width);				/* SpecWidth */

This message sets the maximum allowable size of an object. An object with a maximum size is not allowed to grow larger than the bounds set with HINT_MAXIMUM_SIZE . This message alters the maximum size by creating or modifying the hint HINT_MAXIMUM_SIZE . Due to the nature of hints, there is no guarantee that the specific UI will implement this message in the expected fashion.

This message will modify the geometry of a currently built object, forcing that object to be unbuilt and then built again. Note that the maximum size of an object has nothing to do with the maximized state of an object but merely the maximum allowable size of an object in its normal usable state.

Source: Unrestricted.

Destination: Any generic object.

Parameters: count The number of children (or zero, if not applicable).

height
The height of each child.
width
The width of the composite.

Interception: Generally not intercepted.

MSG_GEN_GET_FIXED_SIZE
void	MSG_GEN_GET_FIXED_SIZE(
        GetSizeArgs *fixedSize);

This message retrieves the size stored in HINT_FIXED_SIZE . If HINT_FIXED_SIZE is not set, the structure passed will remain empty.

Source: Unrestricted.

Destination: Any generic object.

Parameters: fixedSize A pointer to an empty GetSizeArgs structure.

Return: The GetSizeArgs structure will contain the object's fixed size.

Interception: Generally not intercepted.

MSG_GEN_SET_FIXED_SIZE
void	MSG_GEN_SET_FIXED_SIZE(
        word	count,
        word	height,				/* SpecHeight */
        word	width);				/* SpecWidth */

This message sets the fixed size of an object. An object with a fixed size is forced to remain at the size set with HINT_FIXED_SIZE . This message alters this fixed size by creating or modifying HINT_FIXED_SIZE . Due to the nature of hints, there is no guarantee that the specific UI will implement this message in the expected fashion.

Source: Unrestricted.

Destination: Any generic object.

Parameters: count The number of children (or zero, if not applicable).

height
The height of each child.
width
The width of the composite.

Interception: Generally not intercepted.

GenClass: 10 Activation Messages

MSG_GEN_APPLY, MSG_GEN_PRE_APPLY, MSG_GEN_POST_APPLY, MSG_GEN_RESET, MSG_GEN_MAKE_APPLYABLE, MSG_GEN_MAKE_NOT_APPLYABLE, MSG_GEN_ACTIVATE, MSG_GEN_ACTIVATE_INTERACTION_DEFAULT

These messages only affect objects which contain properties. These property gadgets (such as GenItemGroups, GenValues, GenTexts, etc.) typically operate in one of two modes: immediate or delayed .

If an object is operating in immediate mode, any changes in the object's state will cause an immediate change to occur in the application (through the sending of an apply message in the object's instance data). For example, if a GenBooleanGroup is operating in immediate mode, every time the user makes a selection within the BooleanGroup, those changes will be applied to the application immediately, as they occur.

If instead an object is operating in delayed mode, any changes in the object's state will not be sent out immediately. They will only be sent out at a later time when the object receives instructions to apply its changes. The UI asks a generic object to apply its changes with MSG_GEN_APPLY .

Typically, each properties gadget operating in delayed mode has a modified state, indicating whether the object has changed since it last applied its changes. Upon receiving MSG_GEN_APPLY , the object checks whether it has been modified; if it has, it will send out its apply message in the object's instance data. Either you or the system may send out MSG_GEN_APPLY to force a properties gadget to apply its changes. Sending out MSG_GEN_APPLY clears any modified states associated with a properties gadget (after those properties are applied, of course).

If MSG_GEN_APPLY is sent to either a dialog GenInteraction or another GenInteraction within that dialog, it will be propagated to all property gadgets within that Interaction. In this manner, you can apply multiple properties within a dialog box all at once. A GIT_PROPERTIES GenInteraction automatically sends out MSG_GEN_APPLY upon receiving MSG_GEN_GUP_INTERACTION_COMMAND with the InteractionCommand IC_APPLY. (This is typically through an "Apply" trigger in the dialog box.) The dialog will also send MSG_GEN_PRE_APPLY and MSG_GEN_POST_APPLY messages before and after the apply, respectively.

MSG_GEN_RESET also exhibits this automatic propagation behavior; if this message is sent to a dialog GenInteraction, it will be sent to all property gadgets within that Interaction. However, generic property gadgets provide no default handling for MSG_GEN_RESET . If you wish for an object to reset its properties upon receiving MSG_GEN_RESET , you must subclass the object and handle this message yourself. A GIT_PROPERTIES GenInteraction automatically sends out MSG_GEN_RESET upon receiving MSG_GEN_GUP_INTERACTION_COMMAND with the InteractionCommand IC_RESET. (This is typically through a "Reset" trigger in the dialog box.)

MSG_GEN_MAKE_APPLYABLE instructs an object to enable any apply and reset mechanisms that may be disabled. The object must be in delayed mode.

MSG_GEN_MAKE_NOT_APPLYABLE instructs an object to disable any apply and reset mechanisms. The object must be in delayed mode.

MSG_GEN_ACTIVATE activates a generic object as if it had been activated by the user through the specific UI. For example, sending a GenTrigger MSG_GEN_ACTIVATE will cause the trigger to send out its action message in the same manner as if the user had clicked on the trigger.

MSG_GEN_ACTIVATE_INTERACTION_DEFAULT sends a MSG_GEN_ACTIVATE to the object marked as the default within a GenInteraction. This message travels up the generic tree to the first independently-displayable object (usually a dialog box) and then activates the default object for that windowed object.

MSG_GEN_APPLY
void	MSG_GEN_APPLY();

This message is sent to property gadgets to cause them to dispatch their "apply" message and at the same time reset any modified states associated with that object. If this message is sent to a GenInteraction within a dialog box, the GenInteraction will propagate this message to all of its children.

This message may be sent by a GIT_PROPERTIES Interaction upon receipt of MSG_GEN_GUP_INTERACTION_COMMAND with IC_APPLY. You may send this message yourself if you wish for changes in state within the dialog box to be reflected in your application.

Source: Usually a GIT_PROPERTIES Interaction (with an IC_APPLY trigger). You may send this message yourself to force generic objects to apply their changes.

Destination: Any GenInteraction within a dialog box, or any gadget with properties (GenItemGroup, GenValue, etc.).

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_PRE_APPLY
Boolean 	MSG_GEN_PRE_APPLY();

A properties dialog will send this message before MSG_GEN_APPLY . Geodes may use this to do validation of settings or to prepare for applying of changes.

Source: Normally sent by properties dialog before sending MSG_GEN_APPLY .

Destination: Same as MSG_GEN_APPLY .

Parameters: None.

Return: Return true if error.

Interception: Must be intercepted to handle properties validation.

MSG_GEN_POST_APPLY
void	MSG_GEN_POST_APPLY();

A properties dialog will send this message after MSG_GEN_APPLY . This can be used to clean up after application of changes.

Source: Normally sent by properties dialog after sending MSG_GEN_APPLY .

Destination: Same as MSG_GEN_APPLY .

Parameters: None.

Return: Nothing.

Interception: Can be intercepted to handle clean up work after MSG_GEN_APPLY .

MSG_GEN_RESET
void	MSG_GEN_RESET();

This message, if sent to a GenInteraction within a GIT_PROPERTIES dialog box, will propagate to all children below the receiving Interaction. This message provides a convenient means to reset properties within gadgets. There is no default handling of MSG_GEN_RESET by generic objects. You must provide the reset behavior yourself by subclassing individual objects.

Source: Usually a GIT_PROPERTIES GenInteraction (with an IC_RESET trigger), or your application.

Destination: Any GenInteraction within a dialog box, or any gadget subclassed to handle MSG_GEN_RESET .

Parameters: None.

Return: Nothing.

Interception: Your subclassed generic object should intercept this message to provide its own custom reset behavior.

MSG_GEN_MAKE_APPLYABLE
void	MSG_GEN_MAKE_APPLYABLE();

This message is typically sent to a dialog GenInteraction to enable its default "apply" and "reset" buttons. This message also sets up the proper user and actual states for the object. This message is useful in the implementation of custom gadgets.

Source: Unrestricted.

Destination: Any GS_USABLE GenClass object. (This message is only meaningful for objects within a GIT_PROPERTIES GenInteraction, however.)

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_MAKE_NOT_APPLYABLE
void	MSG_GEN_MAKE_NOT_APPLYABLE();

This message is typically sent to a dialog GenInteraction to disable its default "apply" and "reset" buttons. This message is useful in the implementation of custom gadgets.

Source: Unrestricted.

Destination: Any GS_USABLE GenClass object. (This message is only meaningful for objects within a GIT_PROPERTIES GenInteraction, however.)

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_ACTIVATE
void	MSG_GEN_ACTIVATE();

This message manually activates an object as if the user had activated it in the manner defined by the specific UI. For example, this message may activate GenTriggers without use of the mouse. This message causes all normal visual cues associated with the activation.

Source: Unrestricted.

Destination: Usually sent by an object to itself.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_ACTIVATE_INTERACTION_DEFAULT
Boolean	MSG_GEN_ACTIVATE_INTERACTION_DEFAULT();

This message acts on a default exclusive object within a window, forcing that object to activate as in MSG_GEN_ACTIVATE . This message travels up the visible tree to the first WIN_GROUP (windowed) object and sends a MSG_GEN_ACTIVATE to the default object selected there. (This default object is typically marked with the hint HINT_DEFAULT_DEFAULT_ACTION .)

Source: Unrestricted.

Destination: Any GS_USABLE GenClass object (usually within a dialog box).

Parameters: None.

Return: Will return true if the object was activated, false if it was not.

Interception: Generally not intercepted.

GenClass: 11 Focus Modifications

MSG_GEN_BRING_TO_TOP, MSG_GEN_LOWER_TO_BOTTOM

MetaClass provides the main means of changing the focus and target hierarchies within an application. GenClass also provides two messages to perform special cases with windowed objects.

MSG_GEN_BRING_TO_TOP brings a windowed object to the front of all overlapping windowed objects within its window layer. Depending on the specific UI, this message may alter the focus and target by sending MSG_META_GRAB_FOCUS_EXCL or MSG_META_GRAB_TARGET_EXCL to the window brought to the front.

MSG_GEN_LOWER_TO_BOTTOM lowers a windowed object to the bottom of all overlapping windowed objects within its window layer. Depending on the specific UI, this message may alter the focus and target hierarchies by sending a MSG_META_RELEASE_FOCUS_EXCL and MSG_META_RELEASE_TARGET_EXCL to the window lowered to the bottom.

MSG_GEN_BRING_TO_TOP
void	MSG_GEN_BRING_TO_TOP();

This message brings a windowed object to the front if it is in a maximized or overlapping arrangement with other window objects. This message may also grab the focus and target exclusives for the window involved, if the specific UI allows such behavior.

Source: Unrestricted.

Destination: Any GS_USABLE GenClass object. (Only GenDisplays, independently displayable GenInteractions, and GenPrimarys are affected.)

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_LOWER_TO_BOTTOM
void	MSG_GEN_LOWER_TO_BOTTOM();

This message lowers a windowed object to the bottom if it is in an overlapping arrangement with other windowed objects. This message may also release the focus and target exclusives for the window involved, if the specific UI allows such behavior.

Source: Unrestricted.

Destination: Any GS_USABLE GenClass object. (Only GenDisplays, independently displayable GenInteractions, and GenPrimarys are affected.)

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

GenClass: 12 Navigation Methods

MSG_GEN_NAVIGATE_TO_NEXT_FIELD, MSG_GEN_NAVIGATE_TO_PREVIOUS_FIELD, MSG_GEN_NAVIGATION_QUERY

These messages move the navigation cursor between fields at the same level. Typically, these messages alter the focus and target exclusives to implement this behavior. These messages are rarely used within applications.

MSG_GEN_NAVIGATE_TO_NEXT_FIELD
void	MSG_GEN_NAVIGATE_TO_NEXT_FIELD();

This message moves the navigation cursor from its current object to the next valid object. This message is useful if you are trapping keyboard characters using MSG_META_KBD_CHAR and wish to use special characters to navigate the cursor to various fields within a View.

Source: Normally sent by the specific UI, although your application could also send this.

Destination: Any GS_USABLE GenClass object.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted. To alter navigation functionality, intercept MSG_GEN_NAVIGATION_QUERY instead.

MSG_GEN_NAVIGATE_TO_PREVIOUS_FIELD
void	MSG_GEN_NAVIGATE_TO_PREVIOUS_FIELD();

This message moves the navigation cursor from its current object to the previous valid object. This message is useful if you are trapping keyboard characters using MSG_META_KBD_CHAR and wish to use special characters to navigate the cursor to various fields within a View.

Source: Normally sent by the specific UI, although your application could also send this.

Destination: Any GS_USABLE GenClass object.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted. To alter navigation functionality, intercept MSG_GEN_NAVIGATION_QUERY instead.

MSG_GEN_NAVIGATION_QUERY
Boolean	MSG_GEN_NAVIGATION_QUERY(
        NavigationQueryParams 		*retValue,
        optr 				queryOrigin,
        NavigationFlags			navFlags);

This message can be used by applications to initiate navigation-oriented queries, as the MSG_GEN_NAVIGATE_TO_ [ NEXT | PREVIOUS ] _FIELD messages do.

This structure's return values determine how it fits into the navigation network. If this message returns true , then the object which should receive the focus was found, and the NQP_nextObject field will hold the pointer of this object.

If the message returns false , then the focusable object has not yet been found. The NQP_nextObject field contains the field of the next object to try and the NQP_navFlags contains the updated navigation flags which should be passed in the next query.

Source: Unrestricted.

Destination: Any generic object.

Parameters: navFlags Flags giving state of navigation.

queryOrigin
Object which originated this query.
retValue
Structure to hold return values.

Return: true if the queried object should receive the focus; false otherwise.

Structures:

typedef struct {
    word		NQP_navFlags;
     byte 		NQP_backtrackFlag;
     byte 		NQP_unused;
    optr		NQP_nextObject;
} NavigationQueryParams;

GenClass: 13 Window Positions and Sizes

MSG_GEN_SET_WIN_POSITION, MSG_GEN_SET_WIN_SIZE, MSG_GEN_SET_WIN_CONSTRAIN

These messages allow window objects to override their default window behavior. Sending these messages may cause the objects involved to be specifically rebuilt.

You may also override the default constraints on a windowed object. To do so, send the windowed object MSG_GEN_SET_WIN_CONSTRAIN, passing it the WinConstrainType to set for the window.

MSG_GEN_SET_WIN_POSITION
void	MSG_GEN_SET_WIN_POSITION(
        word		modeAndType,
        SpecWinSizeSpec		xPosSpec,
        SpecWinSizeSpec		yPosSpec);

This message sets a window's position. The message must be passed a WinPositionType along with two x and y coordinates passed in SpecWinSizeSpec structures. The window's upper left corner will be drawn at the supplied coordinates.

Source: Unrestricted.

Destination: Any GS_USABLE GenDisplay, GenPrimary, or independently-displayable GenInteraction.

Parameters: modeAndType VisUpdateMode and WinPositionType . This message expects a VisUpdateMode and WinPositionType passed in the parameter modeAndType . Use the macro GET_MM_AND_TYPE to combine these two values into one word.

xPosSpec
The new x coordinates of the window.
yPosSpec
The new y coordinates of the window.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_SET_WIN_SIZE
void	MSG_GEN_SET_WIN_SIZE(
        word		modeAndType,
        SpecWinSizeSpec		xSizeSpec,
        SpecWinsizeSpec		ySizeSpec);

This message sets a window's size. The message must be passed a WinPositionType along with two x and y coordinates passed in SpecWinSizeSpec structures.

Source: Unrestricted.

Destination: Any GS_USABLE GenDisplay, GenPrimary, or independently-displayable GenInteraction.

Parameters: modeAndType VisUpdateMode and WinSizeType . Use the macro GET_MM_AND_TYPE to combine these two values into one word.

xSizeSpec
The new x coordinates of the window.
ySizeSpec
The new y coordinates of the window.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_SET_WIN_CONSTRAIN
void	MSG_GEN_SET_WIN_CONSTRAIN(
        VisUpdateMode		updateMode,
        byte		constrainType);

This message changes a window's constrain type to the passed WindowConstrainType .

Source: Unrestricted.

Destination: Any GS_USABLE GenPrimary or GenInteraction object.

Parameters: updateMode VisUpdateMode .

constrainType
WinConstrainType .
WCT_NONE
WCT_KEEP_PARTIALLY_VISIBLE
WCT_KEEP_VISIBLE
WCT_KEEP_VISIBLE_WITH_MARGIN

Interception: Generally not intercepted.

This document is a single-page version of a a multi-page document, suitable for easy printing.


The Application Object

The GenApplication object is used as the top object in every application geode. It acts as the head of the generic object tree, and it provides all the functionality necessary for launching and shutting down the application. It is a subclass of GenClass and therefore inherits all the instance data and messages of that class.

GenApplicationClass has no inherent visual representation. Instead, the main windows of an application are created and managed by one or more GenPrimary objects, which should be placed both as children of the Application object and on the application's GAGCNLT_WINDOWS notification list (if it should appear when the application starts up).

1 GenApplication Basics
1.1 Instance Data
1.2 Application GCN Lists
1.3 Application Instance Reference
1.4 Attach and Launch Flags
1.5 ApplicationStates
1.6 Application Features and Levels
1.7 IACP
2 Advanced GenApplication Usage
2.1 An Application's Life Cycle
2.2 Application Busy States
2.3 The GenApplication's Moniker
2.4 Measurement Type
2.5 Interaction with the UI

The Application Object: 1 GenApplication Basics

The top-level generic object of your application must be a GenApplication object. You should place this application object within its own resource; this ensures that your application will take up little memory when minimized. Your application tree should branch from this single node. HelloApp from Hello World shows a section of Hello World to illustrate the typical use of a GenApplicationClass object.

Code Display 3-1 HelloApp from Hello World

/*			Application Object
 * The hello.gp file contains an "appobj" statement which indicates that this
 * "HelloApp" object is the top-level UI object. Note that the name of the
 * resource you place the application object in may be whatever you choose;
 * it does not have to be AppResource. */
@start AppResource;				/* Begin definition of objects in AppResource. */
@object GenApplicationClass HelloApp = {
    GI_comp = @HelloPrimary;
            /* The GI_comp attribute lists the generic children
             * of the object. The HelloApp object has just one
             * child, the primary window of the application. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = HelloPrimary;
            /* The window's application GCN list determines which
             * windowable children should be launched on
             * startup. The primary window in most cases should be
             * launched on startup.*/
}
@end AppResource				/* End definition of objects in AppResource. */

Typically, you will not subclass GenApplicationClass . You may occasionally send messages to it, but otherwise it exists primarily to interact with the User Interface.

Note that the Application object you set up in your .goc file must be reflected in the Geode Parameters file (the .gp file). The name of the Application object should appear in the appobj field of the .gp file.

Instance Data

Application GCN Lists

Application Instance Reference

Attach and Launch Flags

ApplicationStates

Application Features and Levels

IACP

The Application Object: 1.1 GenApplication Basics: Instance Data

GenApplicationClass provides several instance data fields, most of which you will not use. All the instance fields of GenApplication are listed in GenApplication Instance Fields for reference, however.

Code Display 3-2 GenApplication Instance Fields

/* These fields will not be used directly. They can be accessed dynamically,
 * however, with the various messages that set and retrieve the instance data. */
@instance AppInstanceReference				GAI_appRef = {"","",NullHandle,{0}};
@instance word				GAI_appMode = 0;
@instance AppLaunchFlags				GAI_launchFlags = 0;
@instance byte				GAI_optFlags = 0;
@instance word				GAI_appFeatures = 0;
@instance Handle				GAI_specificUI = 0;
@instance ApplicationStates				GAI_states = AS_FOCUSABLE | AS_MODELABLE;
@instance AppAttachFlags				GAI_attachFlags = 0;
@instance UIInterfaceLevel				GAI_appLevel = UIIL_ADVANCED;
@instance ChunkHandle				GAI_iacpConnects = 0;
/* ApplicationStates */
typedef WordFlags ApplicationStates;
#define AS_HAS_FULL_SCREEN_EXCL					0x2000
#define AS_SINGLE_INSTANCE					0x1000
#define AS_QUIT_DETACHING					0x0800
#define AS_AVOID_TRANSPARENT_DETACH					0x0400
#define AS_TRANSPARENT_DETACHING					0x0200
#define AS_REAL_DETACHING					0x0100
#define AS_QUITTING					0x0080
#define AS_DETACHING					0x0040
#define AS_FOCUSABLE					0x0020
#define AS_MODELABLE					0x0010
#define AS_NOT_USER_INTERACTABLE					0x0008
#define AS_RECEIVED_APP_OBJECT_DETACH					0x0004
#define AS_ATTACHED_TO_STATE_FILE					0x0002
#define AS_ATTACHING					0x0001
/* Optimization Flags */
typedef ByteFlags AppOptFlags;
#define AOF_MULTIPLE_INIT_FILE_CATEGORIES	0x80
/* GenApplicationClass also modifies two GenClass instance fields. */
@default GI_states = @default & ~GS_USABLE;
@default GI_attrs = @default | GA_TARGETABLE;

GAI_ appRef is internal. It stores information needed to reload this application. If the application is detached, this instance field contains information necessary to reload this application to its state at detachment.

GAI_ appMode stores the message that should be sent to the application's Process object to bring the application back from a saved state. This is initially null and is set by the GenProcessClass object as soon as it is determined.

GAI_ launchFlags stores the AppLaunchFlags that govern how the application should be run. These flags are used internally and are set when the application is first launched.

GAI_ optFlags stores miscellaneous optimization flags.

GAI_appFeatures stores a word representing the application's features as determined by the user's level of expertise. This field is used primarily by hints in GenControl objects and is rarely used directly otherwise.

GAI_ specificUI stores the handle of the specific UI under which this application is running. This is determined and set by the system when the application is launched.

GAI_ states stores the ApplicationStates of the GenApplication. See ApplicationStates for full information on application states.

GAI_ attachFlags stores the AppAttachFlags relating to restoring the application from a state file when attached.

GAI_ appLevel stores an application's user interface level. This level controls the degree of complexity allowed in the application.

GAI_ iacpConnects stores the chunk handle to an array of active IACP connections.

Code Display 3-3 GenApplication Vardata Fields

@vardata void HINT_APP_IS_ENTERTAINING;
@vardata void HINT_APP_IS_EDUCATIONAL;
@vardata void HINT_APP_IS_PRODUCTIVITY_ORIENTED;
@vardata void HINT_APPLICATION_NO_INBOX_QUERY_WHEN_FOREGROUND_APP;
@vardata void HINT_APPLICATION_QUIT_ON_IACP_ALLOW_FILE_ACCESS;
@vardata optr ATTR_GEN_APPLICATION_PRINT_CONTROL;
    @reloc ATTR_GEN_APPLICATION_PRINT_CONTROL, 0, optr;
@vardata optr ATTR_GEN_APPLICATION_KBD_OBJ;
@vardata optr ATTR_GEN_APPLICATION_SAVE_OPTIONS_TRIGGER;
    @reloc ATTR_GEN_APPLICATION_SAVE_OPTIONS_TRIGGER, 0, optr;
@vardata GeodeToken ATTR_GEN_APPLICATION_ADDITIONAL_TOKENS;
/* GenApplication adds a TravelOption to communicate with the Print Control. */
typedef enum {
    TO_PRINT_CONTROL=_FIRST_GenApplicationClass
} GenApplicationTravelOption;

GenApplication also provides several hints that indicate the type of application. HINT_APP_IS_ENTERTAINING, HINT_APP_IS_EDUCATIONAL and HINT_APP_IS_PRODUCTIVITY_ORIENTED are provided for this purpose.

HINT_APPLICATION_NO_INBOX_QUERY_WHEN_FOREGROUND_APP is only applicable for devices that contain a system inbox for the receipt of messages. If this is the case and the application is set up to receive these messages, this hint indicates that the system should deliver the message to the application without prompting the user. (By default, the system initiates a dialog box asking the user whether to accept any newly received messages.)

HINT_APPLICATION_QUIT_ON_IACP_ALLOW_FILE_ACCESS indicates that the application should quit (send itself a MSG_META_QUIT ) if it receives a MSG_META_IACP_CLOSE_FILE . This message indicates that a client application is requesting access to a file that the current application is accessing.

ATTR_GEN_APPLICATION_PRINT_CONTROL stores the optr of the object to act as the destination for any messages sent to the GenApplicationTravelOption TO_PRINT_CONTROL. Specifically, this attribute is designed to allow remote printing capabilities.

ATTR_GEN_APPLICATION_KBD_OBJ stores the optr of the object to act as the application's floating keyboard. This object must be a subclass of GenInteractionClass and must be in the generic tree below the application object. MSG_GEN_APPLICATION_DISPLAY_FLOATING_KEYBOARD will display this keyboard.

ATTR_GEN_APPLICATION_SAVE_OPTIONS_TRIGGER contains the optr of the Save Options trigger within the options menu. If you have a custom Save Options trigger, you should add the optr of this object in this field.

ATTR_GEN_APPLICATION_ADDITIONAL_TOKENS

The Application Object: 1.2 GenApplication Basics: Application GCN Lists

The GCN mechanism is fully discussed in the General Change Mechanism chapter.

Your application may use application GCN lists to notify objects of certain events. For example, it is essential that any windowed objects that you wish to appear upon startup (including your GenPrimary) are added to the GAGCNLT_WINDOWS GCN list.

GenApplication uses its own GCN list types. The four most often used, and the four you will likely use in most of your applications, are listed below. The others can be found in the file geoworks.def and following this short list. Note that all of these GCN list types correspond to the manufacturer ID MANUFACTURER_ID_GEOWORKS.

  • MGCNLT_ACTIVE_LIST
    This GCN list keeps a record of all objects that need to be built upon application startup. Objects on this list will receive several system attach and detach messages. Several types of GenControl objects need to be on this list to function properly. In that case, these controllers must also appear on either the GAGCNLT_SELF_LOAD_OPTIONS or GAGCNLT_STARTUP_LOAD_OPTIONS lists as well. Objects that do not need to receive attach notification (but do need to receive detach notification) may be added dynamically to this list.
  • GAGCNLT_WINDOWS
    This GCN list keeps a record of windowed objects. All windowed objects that should be visible on startup must be added to this list. Any time a windowed object is visually initialized, it will be added to this list. It will not be removed until the object is visually closed. This is used to save window state across shutdown.
  • GAGCNLT_SELF_LOAD_OPTIONS
    This GCN list contains a record of all objects that save options upon receiving MSG_META_SAVE_OPTIONS . These objects will self-load their options; objects on this list will not receive MSG_META_LOAD_OPTIONS automatically. If they need to do so, they should be added to the GAGCNLT_STARTUP_LOAD_OPTIONS list instead. Objects on this list will be sent MSG_META_SAVE_OPTIONS when the GenApplication receives MSG_META_SAVE_OPTIONS .
  • GAGCNLT_STARTUP_LOAD_OPTIONS
    This GCN list contains a record of all objects that should both load their options upon startup and save their options upon receiving MSG_META_SAVE_OPTIONS . Objects on this list will receive MSG_META_LOAD_OPTIONS when they are first loaded.

Code Display 3-4 Sample GenApplication with Controllers

/* This application includes six controllers. One, the TabControl, must receive
 * MSG_META_ATTACH to work properly and is placed on the GCN active list. Another
 * controller, the GenViewController, must receive MSG_META_LOAD_OPTIONS at
 * startup and is therefore placed on the STARTUP_LOAD_OPTIONS list. All other
 * controllers are placed on the SELF_LOAD_OPTIONS list. Note that controllers
 * placed on the active list still need to be placed on one options list. */
@object GenApplicationClass MyApplication = {
    GI_comp = @MyPrimary;
    /* Windows GCN list. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = @MyPrimary;
    /* Active GCN list. All objects that should receive
     * MSG_META_ATTACH should be on this list. These controllers
     * should also be added to the appropriate LOAD_OPTIONS list. */
    gcnList(MANUFACTURER_ID_GEOWORKS, MGCNLT_ACTIVE_LIST) = @MyTabControl,
            @MyToolControl;
    /* Startup Load Options GCN list. This list must include
     * all objects that should receive MSG_META_LOAD_OPTIONS
     * at attach time. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_STARTUP_LOAD_OPTIONS) =
            @MyGenViewControl;
    /* Self Load Options GCN list. All objects that save
     * options and are not on the Startup Load Options list
     * should appear here. */
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) = @MyTabControl,
            @MyToolControl, @MyEditControl, @MyCharControl,
            @MyParaControl;
}

The other GenApplication-defined GCN lists are listed below with comments about their functions. Other GCN list types are also declared by other classes (e.g. MetaClass ).

GAGCNLT_GEN_CONTROL_NOTIFY_STATUS_CHANGE
Keeps the GenToolControl up-to-date on the status of all the GenControl objects. The data block passed with this list is of type NotifyGenControlStatusChange .
GAGCNLT_APP_TARGET_NOTIFY_SELECT_STATE_CHANGE
Notifies objects of changes in the selection state. The data block passed with this list is of type NotifySelectStateChange .
GAGCNLT_EDIT_CONTROL_NOTIFY_UNDO_STATE_CHANGE
Notifies objects of changes in the state of the undo item. The data block passed with this list is of type NotifyUndoStateChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_CHAR_ATTR_CHANGE
Notifies objects of changes in the text character attributes.The data block passed with this list is of type VisTextNotifyCharAttrChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_PARA_ATTR_CHANGE
Notifies objects of changes in the text paragraph attributes. The data block passed with this list is of type VisTextNotifyParaAttrChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_TYPE_CHANGE
Notifies objects of changes in the text type change.The data block passed with this list is of type VisTextNotifyTypeChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_SELECTION_CHANGE
Notifies objects of changes in the text selection. The data block passed with this list is of type VisTextNotifySelectionChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_COUNT_CHANGE
Notifies objects of changes in the text count. The data block passed with this list is of type VisTextNotifyCountChange .
GAGCNLT_APP_TARGET_NOTIFY_STYLE_TEXT_CHANGE
Notifies objects of possible changes in the style. The data block passed with this list is of type NotifyStyleChange .
GAGCNLT_APP_TARGET_NOTIFY_STYLE_SHEET_TEXT_CHANGE
Notifies objects of possible changes in the style sheet.The data block passed with this list is of type NotifyStyleSheetChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_STYLE_CHANGE
Notifies objects of changes in the current text style. The data block passed with this list is of type NotifyTextStyleChange .
GAGCNLT_APP_TARGET_NOTIFY_FONT_CHANGE
Notifies objects of changes in the font. The data block passed with this list is of type NotifyFontChange .
GAGCNLT_APP_TARGET_NOTIFY_POINT_SIZE_CHANGE
Notifies objects of changes in text point size. The data block passed with this list is of type NotifyPointSizeChange .
GAGCNLT_APP_TARGET_NOTIFY_FONT_ATTR_CHANGE
Notifies objects of changes in the font attributes. The data block passed with this list is of type NotifyFontAttrChange .
GAGCNLT_APP_TARGET_NOTIFY_JUSTIFICATION_CHANGE
Notifies objects of changes in the paragraph justification. The data block passed with this list is of type NotifyJustificationChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_FG_COLOR_CHANGE
Notifies objects of changes in the text foreground (character) color. The data block passed with this list is of type NotifyColorChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_BG_COLOR_CHANGE
Notifies objects of changes in the text background color. The data block passed with this list is of type NotifyColorChange .
GAGCNLT_APP_TARGET_NOTIFY_PARA_COLOR_CHANGE
Notifies objects of changes in the text paragraph color.The data block passed with this list is of type NotifyColorChange .
GAGCNLT_APP_TARGET_NOTIFY_BORDER_COLOR_CHANGE
Notifies objects of changes in text border color. The data block passed with this list is of type NotifyColorChange .
GAGCNLT_APP_TARGET_NOTIFY_SEARCH_SPELL_CHANGE
Notifies objects of changes in the search/spell objects.
GAGCNLT_APP_TARGET_NOTIFY_SEARCH_REPLACE_CHANGE
Notifies objects of changes in the search-and-replace mechanism.
GAGCNLT_APP_TARGET_NOTIFY_CHART_TYPE_CHANGE
Notifies objects of changes in chart type.
GAGCNLT_APP_TARGET_NOTIFY_CHART_GROUP_FLAGS
Notifies objects of changes in chart group flags.
GAGCNLT_APP_TARGET_NOTIFY_CHART_AXIS_ATTRIBUTES
Notifies objects of changes in chart axis attributes.
GAGCNLT_APP_TARGET_NOTIFY_CHART_MARKER_SHAPE
Notifies objects of changes in the chart marker shape.
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_CURRENT_TOOL_CHANGE
Notifies objects of changes in the selected GrObj tool.
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_BODY_SELECTION_STATE_CHANGE
Notifies objects of changes in the GrObj body selection state.
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_AREA_ATTR_CHANGE
Notifies objects of changes in the GrObj area attributes.
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_LINE_ATTR_CHANGE
Notifies objects of changes in the GrObj line attributes.
GAGCNLT_APP_TARGET_NOTIFY_GROBJ_TEXT_ATTR_CHANGE
Notifies objects of changes in the GrObj text attributes.
GAGCNLT_APP_TARGET_NOTIFY_STYLE_GROBJ_CHANGE
Notifies objects of changes in style changes relating to the GrObj.
GAGCNLT_APP_TARGET_NOTIFY_STYLE_SHEET_GROBJ_CHANGE
Notifies objects of changes in the style sheet.
GAGCNLT_APP_TARGET_NOTIFY_RULER_TYPE_CHANGE
Notifies objects of changes in ruler type.
GAGCNLT_APP_TARGET_NOTIFY_RULER_GRID_CHANGE
Notifies objects of changes in the ruler grid.
GAGCNLT_TEXT_RULER_OBJECTS
Notifies objects of changes in the active ruler.
GAGCNLT_APP_TARGET_NOTIFY_BITMAP_CURRENT_TOOL_CHANGE
Notifies objects of changes in the selected bitmap tool.
GAGCNLT_APP_TARGET_NOTIFY_BITMAP_CURRENT_FORMAT_CHANGE
Notifies objects of changes in the current bitmap format.
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_FIELD_PROPERTIES_STATUS_CHANGE
Notifies objects of changes in the flatfile database properties status.
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_FIELD_LIST_CHANGE
Notifies objects of changes in the flatfile field list.
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_RCP_STATUS_CHANGE
Notifies objects of changes in flatfile status.
GAGCNLT_APP_TARGET_NOTIFY_FLAT_FILE_FIELD_APPEARANCE_CHANGE
Notifies objects that a field within the flat file has changed its appearance.
GAGCNLT_APP_NOTIFY_DOC_SIZE_CHANGE
Notifies objects of changes in document size. The data block passed with this list is of type NotifyPageSetupChange .
GAGCNLT_APP_NOTIFY_PAPER_SIZE_CHANGE
Notifies objects of changes in chosen paper size. The data block passed with this list is of type NotifyPageSetupChange .
GAGCNLT_APP_TARGET_NOTIFY_VIEW_STATE_CHANGE
Notifies objects of changes in of view state. The data block passed with this list is of type NotifyViewStateChange .
GAGCNLT_CONTROLLED_GEN_VIEW_OBJECTS
A list of GenView objects controlled by the GenViewControl. (These GenViews will have ATTR_GEN_VIEW_INTERACT_WITH_CONTROLLER set in their instance data.)
GAGCNLT_APP_TARGET_NOTIFY_INK_STATE_CHANGE
Notifies objects of changes in Ink state.
GAGCNLT_CONTROLLED_INK_OBJECTS
A list of Ink objects controlled by the InkControl.
GAGCNLT_APP_TARGET_NOTIFY_PAGE_STATE_CHANGE
Notifies objects of changes in page state. The data block passed with this list is of type NotifyPageStateChange .
GAGCNLT_APP_TARGET_NOTIFY_DOCUMENT_CHANGE
Notifies objects of changes in a document. The data block passed with this list is of type NotifyPageStateChange .
GAGCNLT_APP_TARGET_NOTIFY_DISPLAY_CHANGE
Notifies objects of changes in a display. The data block passed with this list is of type NotifyDisplayChange .
GAGCNLT_APP_TARGET_NOTIFY_DISPLAY_LIST_CHANGE
Notifies objects of changes in the display list. The data block passed with this list is of type NotifyColorChange .
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_MARKER_SHAPE
Notifies objects of changes in a spline marker shape.
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_POINT
Notifies objects of changes in spline points.
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_POLYLINE
Notifies objects of changes in spline polylines.
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_SMOOTHNESS
Notifies objects of changes in spline smoothness.
GAGCNLT_APP_TARGET_NOTIFY_SPLINE_OPEN_CLOSE_CHANGE
Notifies objects of changes in a spline's open/close state.
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_ACTIVE_CELL_CHANGE
Notifies objects of changes in the spreadsheet's active cell range. The data block passed with this list is of type NotifySSheetActiveCellChanged .
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_EDIT_BAR_CHANGE
Notifies objects of changes in the spreadsheet's edit bar. The data block passed with this list is of type NotifySSheetEditBarChanged .
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_SELECTION_CHANGE
Notifies objects of changes in the spreadsheet's selection. The data block passed with this list is of type NotifySSheetSelectionChanged .
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_CELL_WIDTH_HEIGHT_CHANGE
Notifies objects of changes in the spreadsheet's cell width or height. The data block passed with this list is of type NotifySSheetCellWidthHeightChang e.
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_DOC_ATTR_CHANGE
Notifies objects of changes in the spreadsheet's document attributes. The data block passed with this list is of type NotifySSheetDocAttrChange .
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_CELL_ATTR_CHANGE
Notifies objects of changes in the spreadsheet's cell attributes. The data block passed with this list is of type NotifySSheetCellAttrChange .
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_CELL_NOTES_CHANGE
Notifies objects of changes in the notes of a cell within a spreadsheet.
GAGCNLT_APP_TARGET_NOTIFY_SPREADSHEET_DATA_RANGE_CHANGE
Notifies objects of changes in the spreadsheet's data range selection. The data block passed with this list is of type NotifySSheetDataRangeChange .
GAGCNLT_APP_TARGET_NOTIFY_TEXT_NAME_CHANGE
Notifies objects of changes in a text object's names run. The data block passed with this list is of type VisTextNotifyNameChange.
GAGCNLT_FLOAT_FORMAT_CHANGE
Notifies objects of changes in a particular float format within the float format controller.
GAGCNLT_DISPLAY_OBJECTS_WITH_RULERS
A list of GenDisplay objects that have rulers.
GAGCNLT_APP_TARGET_NOTIFY_APP_CHANGE
Notifies objects of changes in an application.
GAGCNLT_APP_TARGET_NOTIFY_LIBRARY_CHANGE
Notifies objects of changes in a library.
GAGCNLT_APP_TARGET_NOTIFY_CARD_BACK_CHANGE
Notifies objects that a card back has changed.
GAGCNLT_NOTIFY_FOCUS_TEXT_OBJECT
Notifies objects that a an editable text object has a gained the focus. This list is used by the floating keyboard to determine when it should be enabled or not.
GAGCNLT_NOTIFY_TEXT_CONTEXT
Notifies objects that a the selection or data in a focused text object has changed, if that text object has text contexts turned on. This list is used by hand-writing recognition.
GAGCNLT_NOTIFY_HELP_CONTEXT_CHANGE
Notifies objects that a help context has changed.
GAGCNLT_FLOAT_FORMAT_INIT
Notifies the float controller that it should re-initialize itself. This normally sent to the controller when the target document has changed.
GAGCNLT_ALWAYS_INTERACTABLE_WINDOWS
This list stores windows that should always remain interactable, even if modal windows are on-screen. Objects on this list will get messages even if GenInteractions invoked by UserDoDialog() are on-screen. These objects also receive MSG_META_CHECK_IF_INTERACTABLE_OBJECT to allow them to specify objects under them (such as objects in the child blocks) that should also receive messages.
GAGCNLT_USER_DO_DIALOGS
This list stores all dialog boxes initiated via UserDoDialog() .
GAGCNLT_MODAL_WIN_CHANGE
Notifies objects that modal window changes have occurred within the application.
GACGNLT_APP_TARGET_NOTIFY_SPREADSHEET_NAME_CHANGE
Notifies objects that a spreadsheet's name has changed.
GAGCNLT_CONTROLLERS_WITHIN_USER_DO_DIALOGS
This list stores objects (usually controllers) that will appear within the context of UserDoDialog() but will not be within the same block as the dialog box. Objects on this list will receive MSG_META_CHECK_IF_INTERACTABLE_OBJECT.
GAGCNLT_FOCUS_WINDOW_KBD_STATUS
Notifies floating keyboards when windows gain the focus on pen systems.

The Application Object: 1.3 GenApplication Basics: Application Instance Reference

GAI_appRef, GAI_appMode, GAI_optFlags, MSG_GEN_APPLICATION_SET_APP_MODE_MESSAGE, MSG_GEN_APPLICATION_GET_APP_MODE_MESSAGE, MSG_GEN_APPLICATION_SET_APP_INSTANCE_REFERENCE, MSG_GEN_APPLICATION_GET_APP_INSTANCE_REFERENCE, MSG_GEN_APPLICATION_SEND_APP_INSTANCE_REFERENCE_TO_FIELD

GAI_ appRef stores information (within an AppInstanceReference structure) that allows a GenApplication object to be reloaded from its former state. This structure contains a path name, long file name, and disk handle of a state file as well as an additional byte of disk data. The system automatically manages this state file and this instance field.

GAI_ appMode stores the application message that should be sent to the process to bring this application back from a saved state. This is initially null (unless previously saved to a state file); it is set by GenProcessClass when a mode is determined at MSG_META_ATTACH time. You should not alter this instance field.

GAI_ optFlags stores miscellaneous optimization flags. The only flag at this time--AOF_MULTIPLE_INIT_FILE_CATEGORIES--indicates that within this application there may be several different init file categories (marked with ATTR_GEN_INIT_FILE_CATEGORY). This allows MSG_META_GET_INI_CATEGORY to perform a full upward recursive search to find the appropriate init file category; by default, if an init file category is not found on an object, only the GenApplication object is queried.

MSG_GEN_APPLICATION_SET_APP_MODE_MESSAGE
void	MSG_GEN_APPLICATION_SET_APP_MODE_MESSAGE(
        Message modeMessage);

This message stores a message into the GenApplication's GAI_appMode field. Generally, this message indicates the current mode of the application. Should the application be shut down and restored, this message will be sent to the Process object to restore the state to the same mode.

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: modeMessage The message number to be stored in GAI_appMode .

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_GET_APP_MODE_MESSAGE
Message	MSG_GEN_APPLICATION_GET_APP_MODE_MESSAGE();

This message returns the message number stored in the GenApplication's GAI_appMode field.

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: None.

Return: The message number stored in GAI_appMode .

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_APP_INSTANCE_REFERENCE
void	MSG_GEN_APPLICATION_SET_APP_INSTANCE_REFERENCE(
        Handle	appInstance);

This message sets the GAI_appRef field to the passed structure.

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: appInstance The handle of the reference data block.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_GET_APP_INSTANCE_REFERENCE
Handle	MSG_GEN_APPLICATION_GET_APP_INSTANCE_REFERENCE();

This message retrieves the values in GAI_appRef .

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: None.

Return: The handle of the reference stored in GAI_appRef .

Interception: Do not intercept.

MSG_GEN_APPLICATION_SEND_APP_INSTANCE_REFERENCE_TO_FIELD
void	MSG_GEN_APPLICATION_SEND_APP_INSTANCE_REFERENCE_TO_FIELD();

This message causes the GenApplication to send the contents of its GAI_appRef field off to its parent GenField object.

Source: Rarely used.

Destination: Any GenApplication object.

Interception: Do not intercept.

The Application Object: 1.4 GenApplication Basics: Attach and Launch Flags

GAI_launchFlags, GAI_attachFlags, MSG_GEN_APPLICATION_GET_LAUNCH_FLAGS, MSG_GEN_APPLICATION_GET_ATTACH_FLAGS

GAI_ launchFlags stores flags that are passed when the application is first launched. These flags are never set within your object declaration but may be passed with other messages.

ALF_SEND_LAUNCH_REQUEST_TO_UI_TO_HANDLE
If this bit is set, the application will not immediately be launched but will instead wait for the UI to launch it. This flag should not be set by the application itself; it is only used by UserLoadApplication() .
ALF_OPEN_IN_BACK
If this bit is set, the application will be opened behind other applications, in an inactive state. This flag is only used with MSG_GEN_PROCESS_OPEN_APPLICATION .
ALF_DESK_ACCESSORY
If this bit is set, the application will be treated as a "desk accessory," in a layer above normal applications.
ALF_DO_NOT_OPEN_ON_TOP
If this bit is set, the application will be prevented from both gaining the focus and opening on top of other applications.
ALF_OVERRIDE_MULTIPLE_INSTANCE
If this bit is set, and UILM_MULTIPLE_INSTANCES is also set as the application's UILaunchModel , the application will not query the user if he or she attempts to initiate multiple instances of the same application; the application will be multiply launched without first checking whether the already running application should be used instead.
ALF_OPEN_FOR_IACP_ONLY
If this bit is set, the application will be opened only to facilitate a connection with IACPConnect() ; the application should close once that task is completed. If the application should remain open after such an IACP connection, this bit should be cleared. This behavior is used only for MSG_GEN_PROCESS_OPEN_APPLICATION connections. The application cannot be opened in engine mode.

GAI_ attachFlags stores flags related to an application attaching from a state file. These flags are never set within your object declaration but may be passed with other messages.

AAF_RESTORING_FROM_STATE
If this bit is set, the application was launched via MSG_GEN_PROCESS_RESTORE_FROM_STATE . AAF_STATE_FILE_PASSED will also be set.
AAF_STATE_FILE_PASSED
If this bit is set, the application is being restored from a state file.
AAF_DATA_FILE_PASSED
If this bit is set, a data file containing much of the instance data of the application (of type AppLaunchBlock ) is being passed to the launching message. This is internal and should not be used.
AAF_RESTORING_FROM_QUIT
If this bit is set, the application was in the process of quitting, reached engine mode, and is now being started up into application mode again before completely exiting. If set, then AAF_RESTORING_FROM_STATE will also be set. This bit ensures that certain clean-up operations that are done before an application is quit are un-done, and that the application returns to its former state.
MSG_GEN_APPLICATION_GET_LAUNCH_FLAGS
AppLaunchFlags MSG_GEN_APPLICATION_GET_LAUNCH_FLAGS();

This message retrieves the contents of the GenApplication's GAI_launchFlags field.

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: None.

Return: The AppLaunchFlags record stored in GAI_launchFlags .

Interception: Do not intercept.

MSG_GEN_APPLICATION_GET_ATTACH_FLAGS
AppAttachFlags MSG_GEN_APPLICATION_GET_ATTACH_FLAGS();

This message retrieves the contents of the GenApplication's GAI_attachFlags field.

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: None.

Return: The AppAttachFlags stored in GAI_attachFlags .

Interception: Do not intercept.

The Application Object: 1.5 GenApplication Basics: ApplicationStates

GAI_states, MSG_GEN_APPLICATION_GET_STATE, MSG_GEN_APPLICATION_SET_STATE, MSG_GEN_APPLICATION_SET_NOT_USER_INTERACTABLE, MSG_GEN_APPLICATION_SET_USER_INTERACTABLE, MSG_GEN_APPLICATION_SET_NOT_QUITTING, MSG_GEN_APPLICATION_SET_ATTACHED_TO_STATE_FILE, MSG_GEN_APPLICATION_SET_NOT_ATTACHED_TO_STATE_FILE

GAI_ states stores the ApplicationStates of the application. By default, a GenApplication is both AS_FOCUSABLE and AS_MODELABLE, therefore enabling those hierarchies for this application. Only under extremely rare conditions will you alter this behavior. The flags of ApplicationStates are listed below:

AS_HAS_FULL_SCREEN_EXCL
This bit is set if the application is currently the top screen object at its level. This bit may only be set if the application is between receiving a MSG_META_GAINED_FULL_SCREEN_EXCL and a MSG_META_LOST_FULL_SCREEN_EXCL .
AS_SINGLE_INSTANCE
This bit is set if the application is not capable of being launched multiple times. You should not need to set this.
AS_QUIT_DETACHING
If this bit is set, the detach sequence has been initiated as the result of a QUIT. This bit will only be set if AS_QUITTING is also set; the bit is set in the UI thread at the same time MSG_META_DETACH is sent to the process. Therefore, this bit represents an intermediate step between AS_QUITTING and AS_DETACHING.
AS_AVOID_TRANSPARENT_DETACH
This bit is set if the application should not be transparently detached. If the application is running within UILM_TRANSPARENT mode, then the application will not detach when another application is launched.
AS_TRANSPARENT_DETACHING
This bit is set if the application is being transparently detached. An application can be transparently detached if another application is started in this application's field and that field is marked UILM_TRANSPARENT.
AS_REAL_DETACHING
This bit is set if MSG_GEN_PROCESS_REAL_DETACH has been sent to the process, signalling the irreversible demise of the application. This bit is only set if the UI has finished detaching and the GS_USABLE bit on the application has been cleared.
AS_QUITTING
The application is currently quitting.
AS_DETACHING
The application object has received MSG_META_DETACH and is detaching.
AS_FOCUSABLE
The application may receive the focus exclusive from its field parent. When launched, if this flag is set, the application automatically grabs the focus. This flag is set by default.
AS_MODELABLE
The application may receive the model exclusive from its field parent. When launched, if this flag is set, the application will automatically grab the model exclusive. This flag is set by default.
AS_NOT_USER_INTERACTABLE
The application should not be interactable with the user. This prevents the user from navigating to non-visible applications or otherwise selecting the application.
AS_RECEIVED_APP_OBJECT_DETACH
The application has received a detach message.
AS_ATTACHED_TO_STATE_FILE
The application is currently attached to a state file.
AS_ATTACHING
The application is currently attaching (processing MSG_META_ATTACH ).
MSG_GEN_APPLICATION_GET_STATE
ApplicationStates MSG_GEN_APPLICATION_GET_STATE();

This message retrieves the current application state, stored in GAI_states .

Source: Rarely used.

Destination: Any GenApplication object.

Parameters: None.

Return: The ApplicationStates record stored in GAI_states .

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_STATE
void	MSG_GEN_APPLICATION_SET_STATE(
        ApplicationStates			set,
        ApplicationStates			clear);

This message alters a GenApplication's GAI_ states flags. This message should only be used to set flags that aren't set internally by the UI. Flags that can be altered are the AS_FOCUSABLE, AS_MODELABLE, AS_NOT_USER_INTERACTABLE and AS_AVOID_TRANSPARENT_DETACH state bits.

This message does not reject attempts to set internal bits; therefore, be careful in using this message and only use it to set the external bits mentioned above.

Source: Unrestricted. This message is also used internally.

Destination: Any GenApplication object.

Parameters: set ApplicationStates to set.

clear
ApplicationStates to clear.

Return: Nothing.

Warnings: Do not attempt to set any internal ApplicationStates bits with this message.

Interception: May intercept, but must pass to superclass at some point.

MSG_GEN_APPLICATION_SET_NOT_QUITTING
void	MSG_GEN_APPLICATION_SET_NOT_QUITTING();

This message clears the AS_QUITTING bit in the application's GAI_ states bitfield.

Source: Sent by the UI or the kernel.

Destination: A GenApplication object.

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_NOT_USER_INTERACTABLE
void	MSG_GEN_APPLICATION_SET_NOT_USER_INTERACTABLE();

This message sets the AS_NOT_USER_INTERACTABLE flag in the application's GAI_states field.

Source: Infrequently used.

Destination: The GenApplication to be made not interactable.

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_USER_INTERACTABLE
void	MSG_GEN_APPLICATION_SET_USER_INTERACTABLE();

This message clears the AS_NOT_USER_INTERACTABLE flag in the application's GAI_states field.

Source: Infrequently used.

Destination: The GenApplication to be made interactable.

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_ATTACHED_TO_STATE_FILE
void	MSG_GEN_APPLICATION_SET_ATTACHED_TO_STATE_FILE();

This message sets the AS_ATTACHED_TO_STATE_FILE in the GenApplication's GAI_states field.

Source: Sent by the UI or the kernel.

Destination: The GenApplication object that has been attached to a state file.

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_NOT_ATTACHED_TO_STATE_FILE
void	MSG_GEN_APPLICATION_SET_NOT_ATTACHED_TO_STATE_FILE();

This message clears the AS_ATTACHED_TO_STATE_FILE in the GenApplication's GAI_states field.

Source: Sent by the UI or the kernel.

Destination: The GenApplication object that has been detached to a state file.

Interception: Do not intercept.

The Application Object: 1.6 GenApplication Basics: Application Features and Levels

GAI_appFeatures, GAI_appLevel, MSG_GEN_APPLICATION_GET_APP_FEATURES, MSG_GEN_APPLICATION_SET_APP_FEATURES, MSG_GEN_APPLICATION_SET_APP_LEVEL, MSG_GEN_APPLICATION_UPDATE_APP_FEATURES, MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE, GenAppUsabilityTupleFlags, GenAppUsabilityTuple, GenAppUsabilityCommands

A GenApplication may store a word of features ( GAI_appFeatures ); these features correspond to groups of UI objects. Depending on a certain feature being set or not set, certain groups of UI objects may or may not appear, allowing you to customize your application for different users or criteria. It is up to your application to define both the features and the objects that these features correspond to.

GAI_ appFeatures is a word-length bitfield. Each bit corresponds to a certain group of features which you define. You may thus have up to 16 different feature groups for any application. (Note that each feature group may include several UI objects.) In general, you group these features together so that they correspond to a specific UIInterfaceLevel . If the application appears at a different User Interface level, the makeup of the UI will be different.

An application's user interface level is stored in the GenApplication's GAI_ appLevel instance data entry. Each UIInterfaceLevel corresponds to a certain group of features. Changing the UI level changes the group of features that may be displayed.

The features represented in the bitfield may be represented in hints added to GenControl objects. Most often, the controllers and the application will adjust their menus, tools, and other UI gadgetry to conform to the features specified in this record.

Code Display 3-5 Setting Up Features

/* Features are stored in a word-length bitfield. */
typedef WordFlags MyFeatures;
@define MF_EDIT_FEATURES				(0x8000)
@define MF_PASTE_FEATURES				(0x4000)
@define MF_FORMAT_FEATURES				(0x2000)
/* We might want to group certain features together based on the level of
 * expertise of the user. In this example, if the user level is "intermediate"
 * (which we will define later), we allow features for editing and pasting to the
 * UI. If the user level is "advanced" we allow the intermediate features and also
 * allow formatting features. */
@define INTRODUCTORY_FEATURES				(0)
@define INTERMEDIATE_FEATURES				(@MF_EDIT_FEATURES | @MF_PASTE_FEATURES)
@define ADVANCED_FEATURES				(@INTERMEDIATE_FEATURES | @MF_FORMAT_FEATURES)

MSG_GEN_APPLICATION_GET_APP_FEATURES returns the current application features and UIInterfaceLevel in use by an application.

You may set the application's GAI_ appFeatures by sending it MSG_GEN_APPLICATION_SET_APP_FEATURES. You may also change the application's user level by sending it MSG_GEN_APPLICATION_SET_APP_LEVEL. Each of these messages in turn generates a MSG_GEN_APPLICATION_UPDATE_APP_FEATURES.

This message is meant to be sub-classed so that you can alter the behavior for different features. In most cases, however, you will simply handle this message, fill in relevant parameters, and send the GenApplication MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE--the message which performs the actual work done in changing the UI. This message expects a number of arguments.

The most important argument is the table of GenAppUsabilityTuple entries that correspond to each feature. These entries define what sort of UI change is required, and what object is required to change.You must set up this table beforehand.

The types of usability commands available (in the bit positions set aside with GAUTF_COMMAND in the tuple's GenAppUsabilityTupleFlags ) are:

  • GAUC_USABILITY
    If set, the object should be made GS_USABLE if the feature is on. This is the default behavior.
  • GAUC_RECALC_CONTROLLER
    If set, the controller needs to have its features recalculated. The particular feature bits are stored with the controller itself; the application knows nothing more about the controller's features other than that they need to be changed.
  • GAUC_REPARENT
    If set, the object needs to be relocated to another part of the UI, underneath a different parent. This parent is passed in the reparentObject entry for MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE. Only one object may be re-parented for each application.
  • GAUC_POPUP
    If set, the object should be made a popup menu. Note that this allows a sub-group to become a menu without having to use GAUC_REPARENT.
  • GAUC_TOOLBAR
    If set, the object is a GenBoolean that corresponds to a toolbar state. Turning the feature on or off forces the GenBoolean to send an apply in addition to other behavior.
  • GAUC_RESTART
    If set, the object needs to be "kick started" by first setting it GS_NOT_USABLE and then GS_USABLE.

Because each feature may have multiple objects affected, the GenAppUsabilityTupleFlags entry GAUTF_END_OF_LIST indicates that there are no more commands for that feature. The flag GAUTF_OFF_IF_BIT_IS_ON indicates that a given command should be reversed for the object. (I.e. if the feature is on, the object should be removed, not added.)

Code Display 3-6 Setting Up the GenAppUsabilityTuple Tables

/* Each GenAppUsabilityTuple will refer to a specific set of features. */
/*
 * Since GUAC_USABILITY is the default setting (and is zero) setting any other
 * flags either supersedes or complements this behavior. In this case, setting the
 * EditToolEntry as a GUAC_TOOLBAR command supersedes the GUAC_USABILITY command.
 * Setting the GUATF_END_OF_LIST flag for the EditTrigger does not alter the
 * GUAC_USABILITY command, which is still implicit.
 */
static const GenAppUsabilityTuple editFeaturesList [] =
{
    {GUAC_TOOLBAR,			@EditToolEntry 		},
    {GUATF_END_OF_LIST			@EditTrigger		}
};
static const GenAppUsabilityTuple pasteFeaturesList [] =
{
    {GUAC_END_OF_LIST,			@PasteTrigger		}
};
static const GenAppUsabilityTuple formatFeaturesList [] =
{
    {
    GAUTF_END_OF_LIST | GUAC_RECALC_CONTROLLER,
    @FeatureController
    }
};
/* After each feature's GenAppUsabilityTuple is set up, you should set up a table
 * of these structures to pass to relevant messages. */
static const GenAppUsabilityTuple * const usabilityTable [] =
{
    editFeaturesList,
    pasteFeaturesList,
    formatFeaturesList
};
/*
 * Within your code, decide where you wish to set the application features
 * (usually within some sort of User level dialog box that passes a selection of
 * feature bits) and send either MSG_GEN_APPLICATION_SET_APP_FEATURES or
 * MSG_GEN_APPLICATION_SET_APP_LEVEL with the proper feature bits set.
 */
@method MyLevelApplicationClass, MSG_MY_APPLICATION_SET_USER_LEVEL
{
   @call oself::MSG_GEN_APPLICATION_SET_APP_FEATURES(selection);
}
/*
 * Then intercept MSG_GEN_APPLICATION_UPDATE_APP_FEATURES and set up the correct
 * parameters for MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE.
 */
@method MyLevelApplicationClass, MSG_GEN_APPLICATION_UPDATE_APP_FEATURES
{
    @call oself::MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE(
    NullOptr,
    @ObjectToReparent,			/* if any */
    levelTable,			/* if any */
    sizeof(usabilityTable) / sizeof(usabilityTable [0]),
    usabilityTable,
    appOpeningFlag,
    oldLevel,
    level,
    featuresChanged,
    featuresOn);
}
MSG_GEN_APPLICATION_GET_APP_FEATURES
dword	MSG_GEN_APPLICATION_GET_APP_FEATURES();

This message retrieves the set of features set for the application.

Source: Unrestricted--typically a GenControl object finding out the application's UI level.

Destination: The GenApplication running the controller.

Parameters: None.

Return: A dword containing the word of features stored in GAI_appFeatures and the UIInterfaceLevel for the application. The features are stored in the high word; the interface level is stored in the low word.

Interception: Generally not intercepted.

MSG_GEN_APPLICATION_SET_APP_FEATURES
void	MSG_GEN_APPLICATION_SET_APP_FEATURES(
        word	features);

This message sets a new set of features into the GenApplication's GAI_appFeatures record. This message in turn generates a MSG_GEN_APPLICATION_UPDATE_APP_FEATURES for your application object to intercept. (The message handler for that message must in turn send MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE to activate the UI changes specified in the features list.)

Source: Unrestricted--typically a system function.

Destination: The GenApplication having its features set.

Parameters: features The new word-sized record of application features to set.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_APPLICATION_UPDATE_APP_FEATURES
void	MSG_GEN_APPLICATION_UPDATE_APP_FEATURES(@stack
        optr		unReparentObject,
        optr		reparentObject,
        GenAppUsabilityTuple		*levelTable,
        word		tableLength,
        void		*table,
        word		appOpeningFlag,
        UIInterfaceLevel		oldLevel,
        UIInterfaceLevel		level,
        word		featuresChanged,
        word		featuresOn);

This message is sent by the application to itself when it is told to change either its features or its UIInterfacelevel . This message is passed a number of parameters, most of which should simply be passed to MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE. If you have reparent objects (or un-reparent objects), you must set them up here.

Source: Sent by an application object to itself in response to a MSG_GEN_APPLICATION_SET_APP_FEATURES or MSG_GEN_APPLICATION_SET_APP_LEVEL.

Destination: The GenApplication object.

Parameters: unReparentObject The optr of the object to be unreparented. If a null optr is passed, the object will by default be moved up and added as the next sibling of its current parent.

reparentObject
The optr of the object to be reparented to another UI location. You must supply this object if you have a GenAppUsabilityTuple entry that contains a GAUC_REPARENT entry.
levelTable
This table contains the GenAppUsabilityTuples corresponding to objects that contain their own features and that must be notified when the user level changes. This is so that those objects can generate their own feature updates. Typically, controllers are included among these objects.
tableLength
The number of table entries in table .
table
Table of GenAppUsabilityTuple entries that must be updated when the user level changes. This table is usually set up as global data and maps each user level feature to a GenAppUsabilityTuple .
appOpeningFlag
Set if the application is starting.
oldLevel
The previous UIInterfaceLevel .
level
The new UIInterfaceLevel .
featuresChanged
The set of features changed (deleted).
featuresOn
The set of features to set on.

Interception: To set an application's features, you must intercept this message and send MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE; there is no default message handler. This message is provided as a convenient point to intercept and change features before executing the changes.

MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE
void	MSG_GEN_APPLICATION_UPDATE_FEATURES_VIA_TABLE(
        optr		unReparentObject,
        optr		reparentObject,
        GenAppUsabilityTuple		*levelTable,
        word		tableLength,
        void		*table,
        word		appOpeningFlag,
        UIInterfaceLevel		oldLevel,
        UIInterfaceLevel		level,
        word		featuresChanged,
        word		featuresOn);

This message is called to update the application's features to reflect a new set of features to

Source: Typically, your message handler for MSG_GEN_APPLICATION_UPDATE_APP_FEATURES.

Destination: The GenApplication object.

Parameters: unReparentObject The optr of the object to be unreparented. If a null optr is passed, the object will by default be moved up and added as the next sibling of its current parent.

reparentObject
The optr of the object to be reparented to another UI location. You must supply this object if you have a GenAppUsabilityTuple entry that contains a GAUC_REPARENT entry.
levelTable
This table contains the GenAppUsabilityTuples corresponding to objects that contain their own features and that must be notified when the user level changes. This is so that those objects can generate their own feature updates. Typically, controllers are included among these objects.
tableLength
The number of table entries in table .
table
Table of GenAppUsabilityTuple entries that must be updated when the user level changes. This table is usually set up as global data and maps each user level feature to a GenAppUsabilityTuple .
appOpeningFlag
Set if the application is starting.
oldLevel
The previous UIInterfaceLevel .
level
The new UIInterfaceLevel .
featuresChanged
The set of features changed (deleted).
featuresOn
The set of features to set on.

Interception: Generally not intercepted. Intercept MSG_GEN_APPLICATION_UPDATE_APP_FEATURES instead.

The Application Object: 1.7 GenApplication Basics: IACP

GAI_iacpConnects, MSG_GEN_APPLICATION_IACP_REGISTER, MSG_GEN_APPLICATION_IACP_UNREGISTER, MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_CONNECTIONS, MSG_GEN_APPLICATION_IACP_
GET_NUMBER_OF_APP_MODE_CONNECTIONS, MSG_GEN_APPLICATION_IACP_SHUTDOWN_ALL_CONNECTIONS, MSG_GEN_APPLICATION_IACP_COMPLETE_CONNECTIONS, MSG_GEN_APPLICATION_APP_MODE_COMPLETE

IACP (the GEOS I nter A pplication C ommunication P rotocol) allows applications to communicate with each other. IACP is flexible enough to let applications know whether another application is open, closed, or in the process of attaching or detaching. IACP allows applications to convey information to one another, and could be used to support updating data (e.g. documents) across applications. The IACP mechanism is discussed more fully in the Applications and Geodes chapter. The information included below only discusses GenApplicationClass support of IACP mechanisms.

GAI_ iacpConnects stores the chunk handle to an array of active IACP connections. This chunk stores the IACP connection value referring to the remote application and the type of IACP connection (i.e. a connection that is enabled during a MSG_GEN_PROCESS_OPEN_APPLICATION, for example). These values are manipulated internally and there is no need to access them. You will instead use a variety of messages provided with GenApplicationClass to register and unregister for application notification.

A good deal of support has been added to GenApplicationClass to support IACP. The main things you need to know about this support are:

  • A GenApplication object will refuse to quit so long as there are IACP connections open to it. It can, however, be forcibly detached, as happens when the system shuts down. In such a case, it will call IACPShutdownAll() to shut down all remaining connections either to or from it.
  • When it receives MSG_META_IACP_LOST_CONNECTION sent to it as a server, it will eventually call IACPShutdown() for the connection when it is certain no more messages relating to the connection are in any relevant queue. It will forward this message to all GenDocument objects below any GenDocumentGroup object in the application, so they can close themselves if the lost connection was the last reference to them.
  • It will automatically register itself as a server for the application's token, either when it receives MSG_META_APP_STARTUP if the AppLaunchBlock indicates it's running in engine mode, or when it receives MSG_META_ATTACH and has attached all the various pieces of UI. It will unregister itself as a server when it loses its last IACP connection and is no longer functioning in application mode (either because the user quit the application long since, or because it was never functioning in application mode).
  • It registers and unregisters itself by sending MSG_GEN_APPLICATION_IACP_REGISTER and MSG_GEN_APPLICATION_IACP_UNREGISTER to itself, allowing an application to subclass these messages and register with other lists as appropriate.
  • To determine whether it has any IACP connections remaining, it invokes MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_CONNECTIONS on itself. Should an application register the GenApplication or any other object as a server for another list, it can determine how many connections remain from that list and augment the number returned by the default handler in GenApplicationClass . If this returns non-zero, GenApplication will not shut down the application voluntarily.
  • After unregistering, it will force-queue a message to itself that will check the number of connections again. If the number has become non-zero between the unregister and the check, it re-registers and does not shut down. If the number remains zero, however, the application will exit.
  • When forcibly detached, it will send MSG_GEN_APPLICATION_IACP_SHUTDOWN_ALL_CONNECTIONS to itself. The default handler will call IACPShutdownAll() , passing its own optr. A subclass can use this to perform a similar operation for any other server objects the application might have.
MSG_GEN_APPLICATION_IACP_REGISTER
void	MSG_GEN_APPLICATION_IACP_REGISTER();

This message is sent by a GenApplication object to itself when it registers for IACP. It is not a message meant to be sent externally to an application to register it for IACP. Instead, you can subclass this message and register the object with other lists.

Source: Sent by the GenApplication object to itself.

Destination: The GenApplication object to register with IACP.

Interception: May be intercepted if there are other lists with which you want to register the application, or other server objects. You must make sure to call the superclass, however.

MSG_GEN_APPLICATION_IACP_UNREGISTER
void	MSG_GEN_APPLICATION_IACP_UNREGISTER();

This message is sent by a GenApplication object to itself when it unregisters for IACP. It is not a message meant to be sent externally to an application to unregister it for IACP. Instead, you can subclass this message and unregister the object with other lists.

Source: Sent by the GenApplication object to itself.

Destination: The GenApplication object to unregister with IACP.

Interception: May be intercepted if there are other lists with which you need to unregister the application, or other server objects. You must make sure to call the superclass, however.

MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_CONNECTIONS
word	MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_CONNECTIONS();

This message returns the number of active engine or app-mode IACP connections for a given application. This message is used to check whether an application open only for IACP purposes may be closed.

Source: Called by GenProcessClass when the UI has finished processing MSG_META_QUIT for the application; this determines if the application should exit at this point or if there are client applications that need the application to stay open.

Destination: GenApplication object of the application.

Return: Number of open connections. If non-zero, application will remain open.

Interception: Only intercept if you have other server objects beside your GenApplication object. If intercepting, call the superclass first and then add the number of connections to the other objects onto the result returned by GenApplicationClass .

MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_APP_MODE_CONNECTIONS
word	MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_APP_MODE_CONNECTIONS();

This message retrieves the number of connections which require that the application be open in app-mode (as opposed to engine mode). This message is used to check whether an application can be closed down into engine mode even if some IACP connections are still open.

Source: This message is called by the GenProcessClass when the UI has finished processing MSG_META_QUIT to determine if the application should really close down to engine mode.

Destination: GenApplication object of application.

Return: Number of open app-mode connections.

Interception: Only intercept if you have other server objects besides your GenApplication object. You should call the superclass first and then add the number of connections to other objects onto the result returned by GenApplicationClass .

MSG_GEN_APPLICATION_IACP_SHUTDOWN_ALL_CONNECTIONS
void	MSG_GEN_APPLICATION_IACP_SHUTDOWN_ALL_CONNECTIONS();

This message shuts down all IACP connections for a given application, either on the server or the client side of the connection.

Source: Sent by the GenApplication object to itself.

Destination: GenApplication object of the application.

Interception: May be intercepted to allow connections to other server objects to be shut down. You must call the superclass at some point to ensure that application connections are shut down as well.

MSG_GEN_APPLICATION_APP_MODE_COMPLETE
void	MSG_GEN_APPLICATION_APP_MODE_COMPLETE();

This message is sent to the application when its life as a user-interactable app is complete. The default behavior is to continue shutting down the process if there are no IACP connections active.

Source: Sent by the GenProcess object after it receives a MSG_META_ACK from detaching the application.

Destination: GenApplication object.

Interception: Generally not intercepted; If you have other server connections which you want taken into account before shutting the application completely down, you should intercept MSG_GEN_APPLICATION_IACP_GET_NUMBER_OF_CONNECTIONS instead.

MSG_GEN_APPLICATION_IACP_COMPLETE_CONNECTIONS
void	MSG_GEN_APPLICATION_IACP_COMPLETE_CONNECTIONS();

This message completes all pending IACP connections, accepting any queued messages that have been waiting to be handled. If you subclass it, be sure to call the superclass at some point.

Source: Sent by the GenApplication object to itself in its default MSG_GEN_APPLICATION_OPEN_COMPLETE method, as we assume that the object should be able to handle IACP messages at this latter stage of opening the IACP mechanism.

Destination: GenApplication object of the application.

Interception: May be intercepted if there are other lists besides those connected to the application's token, if those other connections might be pending. If intercepting, you must call the superclass eventually.

The Application Object: 2 Advanced GenApplication Usage

Typically, you will merely set up a GenApplication object in your .goc file and then leave it alone. You may occasionally send it messages to invoke functions or to query the application. These messages are infrequently used, however, and you will likely not have need for them.

An Application's Life Cycle

Application Busy States

The GenApplication's Moniker

Measurement Type

Interaction with the UI

The Application Object: 2.1 Advanced GenApplication Usage: An Application's Life Cycle

For information on how an application is launched and closed, see the Applications and Geodes chapter.

The Application Object: 2.2 Advanced GenApplication Usage: Application Busy States

MSG_GEN_APPLICATION_MARK_BUSY, MSG_GEN_APPLICATION_MARK_NOT_BUSY, MSG_GEN_APPLICATION_HOLD_UP_INPUT, MSG_GEN_APPLICATION_RESUME_INPUT, MSG_GEN_APPLICATION_IGNORE_INPUT, MSG_GEN_APPLICATION_ACCEPT_INPUT, MSG_GEN_APPLICATION_MARK_APP_COMPLETELY_BUSY, MSG_GEN_APPLICATION_MARK_APP_NOT_COMPLETELY_BUSY

An application's busy state is reflected by its mouse pointer. An application may have several busy states, set appropriately for the action going on at the time. The messages below can set the application's busy state. You will not usually send any of these messages to your GenApplication object. Instead, you will usually set appropriate GI_ attrs to automatically send out these messages during times when the application will be busy.

MSG_GEN_APPLICATION_MARK_BUSY marks the application busy (usually by changing the cursor to an appropriate shape determined by the specific UI) until the current operation in the application thread completes. This message is sent by UI gadgets that have GA_INITIATES_BUSY_STATE set in their GI_attrs fields. It may also be called by any other object that wants to mark the application busy. When an application is busy, the user may continue to interact with it.

MSG_GEN_APPLICATION_MARK_NOT_BUSY removes the busy state marker. This message is automatically sent to the application object when the operation that initiated the busy state completes.

MSG_GEN_APPLICATION_HOLD_UP_INPUT instructs the User Interface to place all input events into a special "hold-up" queue until the input is resumed. This message also marks the application busy. Applications marked GA_INITIATES_INPUT_HOLD_UP will receive this message whenever they initiate an operation.

MSG_GEN_APPLICATION_RESUME_INPUT removes the input hold-up state, allowing normal input flow. This message flushes the "hold-up" event queue into the application's input queue, ensuring that all events during the "hold up" operation are handled before any new events.

MSG_GEN_APPLICATION_IGNORE_INPUT instructs the GenApplication object to ignore all input events it receives. This may be accompanied with an audible warning (beep). Applications marked GA_INITIATES_INPUT_IGNORE will receive this message whenever they initiate an operation.

MSG_GEN_APPLICATION_ACCEPT_INPUT removes the input ignore state and directs the GenApplication object to again receive input events and handle them.

All of these messages are cumulative. The application will keep track of how many times each of these messages is sent. For example, each MSG_GEN_APPLICATION_MARK_NOT_BUSY message will remove a MSG_GEN_APPLICATION_MARK_BUSY . When the count reaches zero, the busy state is removed.

MSG_GEN_APPLICATION_MARK_BUSY
void	MSG_GEN_APPLICATION_MARK_BUSY();

This message marks the application busy and changes the cursor image.

Source: Sent automatically by objects with GA_INITIATES_BUSY_STATE set.

Destination: The GenApplication object running the sender.

Interception: Do not intercept.

MSG_GEN_APPLICATION_MARK_NOT_BUSY
void	MSG_GEN_APPLICATION_MARK_NOT_BUSY();

This message marks the application not busy, removing the effect of a previous MSG_GEN_APPLICATION_MARK_BUSY .

Source: Sent automatically by objects with GA_INITIATES_BUSY_STATE set.

Destination: The GenApplication object running the sender.

Interception: Do not intercept.

MSG_GEN_APPLICATION_HOLD_UP_INPUT
void	MSG_GEN_APPLICATION_HOLD_UP_INPUT();

This message causes the GenApplication to mark itself busy and redirect input events to a special "hold-up" queue. When the application is ready to resume normal activity, it first handles the messages in the hold-up queue before handling new input messages.

Source: Used infrequently.

Destination: The GenApplication object to be held up.

Interception: Do not intercept.

MSG_GEN_APPLICATION_RESUME_INPUT
void	MSG_GEN_APPLICATION_RESUME_INPUT();

This message causes a GenApplication to resume normal input handling after it has been held up with MSG_GEN_APPLICATION_HOLD_UP_INPUT .

Source: Used infrequently.

Destination: The GenApplication object running the sender.

Interception: Do not intercept.

MSG_GEN_APPLICATION_IGNORE_INPUT
void	MSG_GEN_APPLICATION_IGNORE_INPUT();

This message causes the GenApplication to consume all input events it receives rather than handle them. This message may be used during debugging as a last resort to help find synchronization problems.

Source: Infrequently used.

Destination: The GenApplication object to consume input events.

Interception: Do not intercept.

MSG_GEN_APPLICATION_ACCEPT_INPUT
void	MSG_GEN_APPLICATION_ACCEPT_INPUT();

This message undoes a previous MSG_GEN_APPLICATION_IGNORE_INPUT , allowing the GenApplication to once again handle input events normally.

Source: Infrequently used.

Destination: The GenApplication object to resume input handling.

Interception: Do not intercept.

MSG_GEN_APPLICATION_MARK_APP_COMPLETELY_BUSY
void	MSG_GEN_APPLICATION_MARK_APP_COMPLETELY_BUSY();

This message is rarely used and forces a busy state over the application regardless of other states. It should be used only when a time-intensive task is going on in the UI and the program can not handle input during that time.

Source: Infrequently used.

Destination: The GenApplication object to be marked completely busy.

Interception: Do not intercept.

MSG_GEN_APPLICATION_MARK_APP_NOT_COMPLETELY_BUSY
void	MSG_GEN_APPLICATION_MARK_APP_NOT_COMPLETELY_BUSY();

This message undoes MSG_GEN_APPLICATION_MARK_COMPLETELY_BUSY , allowing the application to once again handle user input.

Source: Infrequently used.

Destination: The GenApplication object to be marked not busy.

Interception: Do not intercept.

The Application Object: 2.3 Advanced GenApplication Usage: The GenApplication's Moniker

MSG_GEN_APPLICATION_FIND_MONIKER, MSG_GEN_APPLICATION_SET_TASK_ENTRY_MONIKER

Every GenApplication object should be given a moniker; this moniker is displayed by the UI in its task list. (In OSF/Motif, the task list is manifested as the floating "Express" menu.) While you will set the moniker just like for any generic object, the GenApplication has two messages that can be used to get or set the moniker used in the task list.

MSG_GEN_APPLICATION_FIND_MONIKER
optr	MSG_GEN_APPLICATION_FIND_MONIKER(
        MemHandle		destBlock,
        word		searchFlags,
        DisplayType		displayType);

This message finds the specified moniker in the GenApplication's VisMonikerList and optionally copies it into a specified destination block.

Source: Infrequently used.

Destination: The GenApplication to get the moniker from.

Parameters: destBlock The handle of the destination block into which the chunk will be copied. For this to work, you must pass VMSF_COPY_CHUNK in searchFlags .

searchFlags
A record of VisMonikerSearchFlags indicating what type of moniker to find in the moniker list and what to do with it when it is found.
displayType
The display type of the moniker to search for.

Return: The optr of the GenApplication's VisMonikerList chunk.

Interception: Do not intercept.

MSG_GEN_APPLICATION_SET_TASK_ENTRY_MONIKER
void	MSG_GEN_APPLICATION_SET_TASK_ENTRY_MONIKER(
        optr	entryMoniker);

This message changes the moniker which is used in the GenField's task list. The task list menu will be updated if currently visible.

Source: Infrequently used.

Destination: The GenApplication to have its moniker changed.

Parameters: entryMoniker The optr of the chunk containing the moniker that will be set into the task list.

Return: Nothing.

Interception: Do not intercept.

The Application Object: 2.4 Advanced GenApplication Usage: Measurement Type

MSG_GEN_APPLICATION_SET_MEASUREMENT_TYPE, MSG_GEN_APPLICATION_GET_MEASUREMENT_TYPE, GET_APP_MEASUREMENT_TYPE

Each application has a "measurement type" associated with it. The measurement type indicates whether measurements should default to metric or standard US measurements.

MSG_GEN_APPLICATION_SET_MEASUREMENT_TYPE
void	MSG_GEN_APPLICATION_SET_MEASUREMENT_TYPE(
        byte	measurementType);

This message sets the measurement type used by the application.

Source: Infrequently used.

Destination: The GenApplication to have the new measurement type.

Parameters: measurementType
 A value of AppMeasurementType to set for the application. Can be AMT_US, AMT_METRIC, or AMT_DEFAULT.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_GET_MEASUREMENT_TYPE
word	MSG_GEN_APPLICATION_GET_MEASUREMENT_TYPE();

This message gets the measurement currently used by the application.

Source: Infrequently used.

Destination: The GenApplication whose measurement is to be retrieved.

Parameters: None.

Return: A word value, the low byte of which represents the application's measurement type. Use the macro GET_MEASUREMENT_TYPE to extract the measurement type from the return value.

Interception: Do not intercept.

The Application Object: 2.5 Advanced GenApplication Usage: Interaction with the UI

The GenApplication is an application's main point of contact with the UI. As such, it has several messages that are sent by the UI or by other objects to initiate certain UI-related functions. These messages will rarely, if ever, be used by application programmers, but they are documented here in case you find them useful.

Attaching and Detaching

MSG_GEN_APPLICATION_INITIATE_UI_QUIT, MSG_GEN_APPLICATION_INSTALL_TOKEN, MSG_GEN_APPLICATION_DETACH_PENDING, MSG_GEN_APPLICATION_OPEN_COMPLETE, MSG_GEN_APPLICATION_QUIT_AFTER_UI
MSG_GEN_APPLICATION_INITIATE_UI_QUIT
void	MSG_GEN_APPLICATION_INITIATE_UI_QUIT();

This message causes the GenApplication to begin quitting. The application will automatically go through the entire quit sequence.

Source: Infrequently used.

Destination: The GenApplication of the application to be shut down.

Interception: Do not intercept.

MSG_GEN_APPLICATION_INSTALL_TOKEN
void	MSG_GEN_APPLICATION_INSTALL_TOKEN();

This message instructs the GenApplication object to set its token into the token database file.

Source: Infrequently used.

Destination: The GenApplication to have its token installed.

Interception: Do not intercept.

MSG_GEN_APPLICATION_DETACH_PENDING
void	MSG_GEN_APPLICATION_DETACH_PENDING();

This message is sent to the specific UI library through the GenApplication to notify it that the application is about to be shut down. It is used to abort any application-modal dialog boxes so the application's Process object will be able to detach.

Source: The GenApplication object before it detaches.

Destination: The GenApplication of the application about to be detached.

Interception: Do not intercept.

MSG_GEN_APPLICATION_OPEN_COMPLETE
void	MSG_GEN_APPLICATION_OPEN_COMPLETE();

This message is sent by the GenApplication object to itself when it has finished opening (after it has set itself usable). It is sent via the queue and indicates that the application's UI is fully usable.

Source: A GenApplication after it has set itself GS_USABLE.

Destination: Sent to itself.

Interception: Do not intercept.

MSG_GEN_APPLICATION_QUIT_AFTER_UI
void	MSG_GEN_APPLICATION_QUIT_AFTER_UI();

This message is called from the MSG_META_QUIT handler in GenProcessClass, after the UI has finished its MSG_GEN_APPLICATION_INITIATE_UI_QUIT sequence. This message is the application's last chance to abort a quit before the DETACH sequence begins. The default behavior is to abort the QUIT if the application is still open for the user (i.e. not ALF_OPEN_FOR_IACP_CONNECTION_ONLY) or if an IACP connection remains that requires the application to remain open.

Source: GenProcessClass .

Destination: GenApplication object of the application.

Interception: May be intercepted and not sent to the superclass to abort the QUIT.

Queries of the UI

MSG_GEN_APPLICATION_GET_DISPLAY_SCHEME, MSG_GEN_APPLICATION_QUERY_UI, MSG_GEN_APPLICATION_NOTIFY_MODAL_WIN_CHANGE, MSG_GEN_APPLICATION_INK_QUERY_REPLY, MSG_GEN_APPLICATION_GET_GCN_LIST_OF_LISTS, MSG_GEN_APPLICATION_TEST_WIN_INTERACTABILITY, MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION, MSG_GEN_APPLICATION_GET_MODAL_WIN, MSG_GEN_APPLICATION_CHECK_IF_ALWAYS_INTERACTABLE_OBJECT
MSG_GEN_APPLICATION_GET_DISPLAY_SCHEME
void	MSG_GEN_APPLICATION_GET_DISPLAY_SCHEME(
        DisplayScheme * displayScheme);

This message gets the current display scheme used by the application.

Source: Infrequently used.

Destination: Any GenApplication.

Parameters: displayScheme A pointer to a structure of type DisplayScheme . This structure will be filled by the method and contains information about the color scheme, display type, font ID, and point size used by the application.

Return: The DisplayScheme structure pointed to by displayScheme will be filled upon return.

Interception: Do not intercept.

MSG_GEN_APPLICATION_QUERY_UI
Handle	MSG_GEN_APPLICATION_QUERY_UI();

This message is used to determine which UI should be used at a given point in the generic tree for a certain type of object.

Source: Infrequently used.

Destination: Any GenApplication object.

Parameters: None.

Return: The handle of the specific UI library geode.

Interception: Do not intercept.

MSG_GEN_APPLICATION_NOTIFY_MODAL_WIN_CHANGE
void	MSG_GEN_APPLICATION_NOTIFY_MODAL_WIN_CHANGE();

This message is called on a GenApplication object by the UI whenever the application should check to see if there is a change in modal status. The behavior is to look for the top system-modal window owned by the application and then the top application-modal window within the application's layer.

This message sets the AS_ATTACHED_TO_STATE_FILE in the GenApplication's GAI_states field.

Source: Sent by the UI.

Destination: Any GenApplication object.

Interception: Do not intercept.

MSG_GEN_APPLICATION_INK_QUERY_REPLY
void	MSG_GEN_APPLICATION_INK_QUERY_REPLY(
        InkReturnValue		inkReturnValue,
        word		inkGstate);

This message is sent to an Application object in reply to a MSG_META_QUERY_IF_PRESS_IS_INK . It indicates whether the object that was queried can or can not handle Ink presses. The GenApplication object responds by sending a message to the UI.

Source: Sent by an object in response to MSG_META_QUERY_IF_PRESS_IS_INK .

Destination: The GenApplication object associated with the sender.

Parameters: inkReturnValue A value indicating whether the object queried can handle Ink input or not. Will be one of IRV_NO_INK, IRV_INK_WITH_STANDARD_OVERRIDE, IRV_DESIRES_INK, or IRV_WAIT.

inkGstate
The GState, if any, to be used when drawing Ink.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_GET_GCN_LIST_OF_LISTS
ChunkHandle MSG_GEN_APPLICATION_GET_GCN_LIST_OF_LISTS();

This message retrieves the GenApplication's GCN list of lists chunk handle. This chunk handle may then be used with a number of kernel routines for GCN list management or to perform operations on individual GCN lists.

Source: Any object in the GenApplication's thread.

Destination: Any GenApplication object.

Parameters: None.

Return: The chunk handle of the GCN list of lists chunk; a null chunk handle will be returned if the chunk does not exist.

Interception: Do not intercept.

MSG_GEN_APPLICATION_TEST_WIN_INTERACTABILITY
Boolean	MSG_GEN_APPLICATION_TEST_WIN_INTERACTABILITY(
        optr	inputOD,
        Handle	window);

This message tests whether the passed window object is interactable.

Source: Unrestricted.

Destination: Any GenApplication object.

Parameters: inputOD The optr of the windowed object to be tested.

window
The window handle of the window to be tested.

Return: The return value will be false if there are no modal windows in the system or if the window object passed is the topmost active modal window. The return value will be true if an active modal window exists and is not the passed window object (in this case, if the passed object has any window grabs, it should release them). True is also returned if there is no modal window but the GenApplication is ignoring input.

Interception: Do not intercept.

MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION
void	MSG_GEN_APPLICATION_VISIBILITY_NOTIFICATION(
        optr		obj,
        Boolean 		opening);

Notifies the GenApplication object that it has become visible or not visible.

Source: The specific UI.

Destination: The GenApplication that has become visible or not visible.

Parameters: obj The optr of the object sending the notification message.

opening
A Boolean indicating the state of the object: true if open, false if closed.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_GET_MODAL_WIN
optr	MSG_GEN_APPLICATION_GET_MODAL_WIN();

This message returns the current top modal window for the application, if any is present.

Source: Unrestricted.

Destination: GenApplication object.

Return: optr of top modal windowed object.

MSG_GEN_APPLICATION_CHECK_IF_ALWAYS_INTERACTABLE_OBJECT
Boolean	MSG_GEN_APPLICATION_CHECK_IF_ALWAYS_INTERACTABLE_OBJECT(
        optr		objToCheck);

This message checks if the passed object should always remain interactable.

Source: Unrestricted.

Destination: GenApplication object.

Parameters: objToCheck optr of object to check the interactable state.

Return: true if the object is always interactable, false if not.

Alterations of Functionality

MSG_GEN_APPLICATION_BRING_WINDOW_TO_TOP, MSG_GEN_APPLICATION_LOWER_WINDOW_TO_BOTTOM, MSG_GEN_APPLICATION_BUILD_STANDARD_DIALOG, MSG_GEN_APPLICATION_DO_STANDARD_DIALOG, MSG_GEN_APPLICATION_TOGGLE_CURSOR, MSG_GEN_APPLICATION_BRING_UP_HELP, MSG_GEN_APPLICATION_TOGGLE_CURRENT_MENU_BAR, MSG_GEN_APPLICATION_TOGGLE_FLOATING_KEYBOARD, MSG_GEN_APPLICATION_TOGGLE_EXPRESS_MENU
MSG_GEN_APPLICATION_BRING_WINDOW_TO_TOP
void	MSG_GEN_APPLICATION_BRING_WINDOW_TO_TOP(
        optr	window);

This message brings the passed window to the front of the screen.

Source: Unrestricted

Destination: Any GenApplication object.

Parameters: window The optr of the window object to be brought to the front of the screen.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_LOWER_WINDOW_TO_BOTTOM
void	MSG_GEN_APPLICATION_LOWER_WINDOW_TO_BOTTOM(
        optr	window);

This message sends the specified window to the back of the screen, behind other window objects.

Source: Unrestricted.

Destination: Any GenApplication object.

Parameters: window The optr of the window object to be sent to the back of the screen.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_BUILD_STANDARD_DIALOG
optr	MSG_GEN_APPLICATION_BUILD_STANDARD_DIALOG(
        char	* customTriggers,
        char	* arg2,
        char	* arg1,
        char	* string,
        CustomDialogBoxFlags dialogFlags);

This message builds a standard dialog box for the application.

Source: Infrequently used.

Destination: The GenApplication to use the dialog box.

Parameters: customTriggers A pointer to a table of custom GenTrigger information. Each trigger given in the table will appear in the dialog box in the order declared. The table is made up of structures of type StandardDialogResponseTriggerTable .

arg 1
A pointer to a character string to be displayed in the dialog box.
arg 2
A pointer to a second string to be displayed in the dialog box.
string
A pointer to a custom character string to be displayed in the dialog box.
dialogFlags
A record of CustomDialogBoxFlags indicating what type of dialog box is to be created.

Return: The optr of the dialog box object.

Interception: Do not intercept.

MSG_GEN_APPLICATION_DO_STANDARD_DIALOG
void	MSG_GEN_APPLICATION_DO_STANDARD_DIALOG(@stack
        word	dialogMethod,
        optr	dialogOD,
        char	*helpContext,
        char	* customTriggers,
        char	* arg2,
        char	* arg1,
        char	* string,
        CustomDialogBoxFlags dialogFlags);

This message executes a standard dialog box and returns immediately. When the dialog box is shut down, the message passed in the dialogMethod parameter is sent to the object specified in dialogOD . Only one dialog box at a time may be displayed with this message.

Source: Infrequently used.

Destination: The GenApplication to use the created dialog box.

Parameters: dialogMethod The message to be sent out when the user is finished with the dialog. This message should be defined based on the prototype GEN_APP_DO_STANDARD_DIALOG_MSG .

dialogOD
The recipient of the message specified in dialogMethod above.
helpContext
The help context for this dialog box.
customTriggers
A pointer to a table of custom GenTrigger information. Each trigger given in the table will appear in the dialog box in the order declared. The table is made up of structures of type StandardDialogResponseTriggerTable .
arg2
A pointer to a second string to be displayed in the dialog box.
arg1
A pointer to a character string to be displayed in the dialog box.
string
A pointer to a custom character string to be displayed in the dialog box.
dialogFlags
A record of CustomDialogBoxFlags indicating what type of dialog box is to be created.

Return: Nothing.

Interception: Do not intercept.

MSG_GEN_APPLICATION_TOGGLE_CURSOR
void	MSG_GEN_APPLICATION_TOGGLE_CURSOR();

This message toggles the cursor for a text object.

Source: Infrequently used.

Destination: The GenApplication of the text object.

Interception: Do not intercept.

MSG_GEN_APPLICATION_BRING_UP_HELP
void	MSG_GEN_APPLICATION_BRING_UP_HELP();

This message brings up help for an application. Normally, this is accomplished by sending a message to the focus object telling it to bring up a help window with the focus' help context.

Source: Unrestricted, though generally from an application Help icon or <F1>.

Destination: GenApplication object.

Interception: Generally not intercepted, though it may be useful if for some reason you do not wish to bring up help (such as it doesn't exist for this application).

MSG_GEN_APPLICATION_TOGGLE_CURRENT_MENU_BAR
void	MSG_GEN_APPLICATION_TOGGLE_CURRENT_MENU_BAR();

This message toggles the GIV_POPOUT state of the current GenPrimary's menu bar. This message only takes effect if the menu bar is toggleable (i.e. if UIWO_POPOUT_MENU_BAR is set).

Source: Unrestricted.

Destination: GenApplication object.

MSG_GEN_APPLICATION_TOGGLE_FLOATING_KEYBOARD
void	MSG_GEN_APPLICATION_TOGGLE_FLOATING_KEYBOARD();

This message toggles the state of the floating keyboard within the current application. Applications may subclass this to bring up their own PenInputControl (or equivalent) object. Otherwise the application object will create its own.

Source: Unrestricted, though generally only supported on pen-based systems.

Destination: GenApplication object.

Interception: May be intercepted if the application has its own PenInputControl object.

MSG_GEN_APPLICATION_TOGGLE_EXPRESS_MENU
void	MSG_GEN_APPLICATION_TOGGLE_EXPRESS_MENU();

This message toggles (opens or closes) the parent field's express menu.

Source: Unrestricted.

Destination: GenApplication object.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Window Objects

Applications communicate with users through a "user interface." The system needs a way of grouping the user-interface components together. For this reason, most applications will have a GenPrimary object. This object serves as the top-level object in the user interface tree.

GenPrimaryClass is a subclass of GenDisplayClass . The GenDisplay object, like the GenPrimary object, manages other pieces of the user interface (menus, triggers, text objects, etc.). Furthermore, the GenDisplay object is a great boon to applications that need to perform several tasks at once (as, for example, a word processor which can have several files open at once). An application can have several GenDisplay objects, all of them children of a GenDisplayGroup object. Collectively, displays and primaries are called windows . All windows have certain functionality in common; that functionality can vary according to the specific UI.

The Document Control objects can be set to create GenDisplay objects when files are opened and to attach them automatically to a GenDisplayGroup. This enormously simplifies managing multiple documents. See the Documents chapter for information on the Document Control objects.

These objects are very simple to use. In particular, the GenPrimary is very easy to declare and use. Although all applications use the GenPrimary, many will not need the GenDisplay. However, because GenPrimaryClass is a subclass of GenDisplayClass , this chapter begins with an overview of GenDisplayClass , then continues with a full discussion of GenPrimaryClass , and concludes with advanced information about GenDisplayClass . Before reading this chapter, you should be familiar with the use and creation of generic user interface objects.

1 A First Look at GenDisplay
1.1 GenDisplay Object Structure
1.2 Minimizing and Maximizing
2 Using the GenPrimary
3 Using Multiple Displays
3.1 GenDisplayGroup
3.2 GenDisplayControl
3.3 Using GenDisplayClass Objects

Window Objects: 1 A First Look at GenDisplay

Not all applications will need to use GenDisplay objects. However, almost all applications will have a GenPrimary object. Since GenPrimaryClass is a subclass of GenDisplayClass , programmers should be acquainted with GenDisplayClass .

This section describes the data fields of GenDisplayClass and certain useful messages. It does not have all the information you will need to create these objects. If you will be using GenDisplay objects in your application, you will have to read Using Multiple Displays .

GenDisplay Object Structure

Minimizing and Maximizing

Window Objects: 1.1 A First Look at GenDisplay: GenDisplay Object Structure

The GenDisplay object is a subclass of GenClass and therefore inherits all the data fields and attributes of that class. It has few data fields that are set by the application; these fields are listed in Instance Data of GenDisplayClass .

Code Display 4-1 Instance Data of GenDisplayClass

/* There are only two instance fields specifically defined for GenDisplayClass.
 * Also, an instance field for GenClass, GI_attrs, has different defaults in
 * GenDisplayClass. */
/* GDI_attributes is a one-byte field for attributes flags. There is only one flag
 * defined for this field, namely GDA_USER_DISMISSABLE, which is on by default. */
    @instance GenDisplayAttrs				GDI_attributes = GDA_USER_DISMISSABLE;
/* The GenDisplay object has a datum for an optr to a document object. If a
 * Document Control is used to create display objects, it will associate each
 * display with a document object; each will have an optr to the other. */
    @instance optr				GDI_document;
/* The default setting of GI_attrs is different in GenDisplayClass than it is in
 * GenClass: */
    @default GI_attrs = 			(@default
             | GA_TARGETABLE
             | GA_KBD_SEARCH_PATH);
/* The following hints specify whether the display should be minimized or
 * maximized when it is built, and its appearance when minimized. */
    @vardata void  HINT_DISPLAY_MINIMIZED_ON_STARTUP;
    @vardata void HINT_DISPLAY_NOT_MINIMIZED_ON_STARTUP;
    @vardata void HINT_DISPLAY_MAXIMIZED_ON_STARTUP;
    @vardata void HINT_DISPLAY_NOT_MAXIMIZED_ON_STARTUP;
    @vardata void HINT_DISPLAY_USE_APPLICATION_MONIKER_WHEN_MINIMIZED;
/* The following hints and attributes indicate whether the user should be able to
 * minimize, maximize, or resize the window. */
    @vardata void  ATTR_GEN_DISPLAY_NOT_MINIMIZABLE;
    @vardata void ATTR_GEN_DISPLAY_NOT_MAXIMIZABLE;
    @vardata void HINT_DISPLAY_NOT_RESIZABLE;
/* ATTR_GEN_DISPLAY_NOT_RESTORABLE indicates that the user should not be able to
 * de-maximize the display once it is maximized. */
    @vardata void ATTR_GEN_DISPLAY_NOT_RESTORABLE;
/* ATTR_GEN_DISPLAY_TRAVELING_OBJECTS is the ChunkHandle of a list of "traveling
 * objects;" these objects are made the children of whichever GenDisplay is on top
 * in a given display region (see  Traveling Objects).*/
    @vardata ChunkHandle ATTR_GEN_DISPLAY_TRAVELING_OBJECTS;
/* The following hints and attributes specify whether the display's menu bar
 * appears and whether it appears as a "popped out" floating menu. */
    @vardata void HINT_DISPLAY_MENU_BAR_HIDDEN_ON_STARTUP;
    @vardata void  TEMP_GEN_DISPLAY_MENU_BAR_HIDDEN;
    @vardata void ATTR_GEN_DISPLAY_MENU_BAR_POPPED_OUT;
    @vardata void HINT_DISPLAY_USE_APPLICATION_MONIKER_WHEN_MENU_BAR_POPPED_OUT;
/* The GenDisplay uses the following vardata fields to store its
 * minimized/maximized state across a shutdown. You should not access these
 * fields. If you want to find out if a GenDisplay is minimized or maximized, send
 * it MSG_GEN_DISPLAY_GET_MINIMIZED or MSG_GEN_DISPLAY_GET_MAXIMIZED. */
    @vardata		void ATTR_GEN_DISPLAY_MINIMIZED_STATE;
    @vardata		void ATTR_GEN_DISPLAY_MAXIMIZED_STATE;
/* HINT_DISPLAY_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD specifies the default
 * action for the GenDisplay. */
    @vardata void HINT_DISPLAY_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD;

The GDI_attributes Field

MSG_GEN_DISPLAY_GET_ATTRS, MSG_GEN_DISPLAY_SET_ATTRS

The GenDisplay object has a one-byte record called GDI_attributes to store attribute flags. There is only one attribute flag, namely GDA_USER_DISMISSABLE . If this attribute is set, the user can dismiss a display through the UI (without choosing a command in the application). Details of this depend on the specific UI; for example, in OSF/Motif, the user could dismiss a display by double-clicking the "Control button."

MSG_GEN_DISPLAY_GET_ATTRS
GenDisplayAttrs 	MSG_GEN_DISPLAY_GET_ATTRS();

This message retrieves the GDI _attributes field from the destination object.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object.

Return: A GenDisplayAttrs record. The only flag defined is GDA_USER_DISMISSABLE.

Interception: This message is not normally intercepted.

MSG_GEN_DISPLAY_SET_ATTRS
void	MSG_GEN_DISPLAY_SET_ATTRS(
        GenDisplayAttrs		attrs);

This message changes the GenDisplayAttrs field of the destination object.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Parameters: attrs Field of GenDisplayAttrs flags. There is only one flag defined, namely GDA_USER_DISMISSABLE.

Interception: This message is not normally intercepted.

The GDI_document Field

Applications often use the Document Control objects to manage files. With this mechanism, every file is associated with a document object. Often, each file will have its own GenDisplay object as well. In this case, GDI _document will contain an optr to the GenDocument object associated with this GenDisplay. For more information on this, see Using Multiple Displays . The Document Control objects create and destroy the GenDisplays automatically, and set this field accordingly. The GenDisplay object uses this field only when the display is closed; see Closing GenDisplays . To retrieve the value of this field, send MSG_GEN_DISPLAY_GET_DOCUMENT to the display (see Messages sent to GenDisplays ).

Window Objects: 1.2 A First Look at GenDisplay: Minimizing and Maximizing

MSG_GEN_DISPLAY_SET_MINIMIZED, MSG_GEN_DISPLAY_SET_NOT_MINIMIZED, MSG_GEN_DISPLAY_GET_MINIMIZED, MSG_GEN_DISPLAY_SET_MAXIMIZED, MSG_GEN_DISPLAY_SET_NOT_MAXIMIZED, MSG_GEN_DISPLAY_GET_MAXIMIZED, HINT_DISPLAY_MINIMIZED_ON_STARTUP, HINT_DISPLAY_NOT_MINIMIZED_ON_STARTUP, HINT_DISPLAY_MAXIMIZED_ON_STARTUP, HINT_DISPLAY_NOT_MAXIMIZED_ON_STARTUP, ATTR_GEN_DISPLAY_NOT_MINIMIZABLE, HINT_DISPLAY_NOT_MAXIMIZABLE, ATTR_GEN_DISPLAY_NOT_RESTORABLE,
HINT_DISPLAY_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD

Windows (i.e. displays and "primary" windows) can be resized by the user. How resizing is done depends on the specific UI; for example, in OSF/Motif, a user resizes a window by dragging its edge. Most specific UIs also allow the user to "minimize" or "maximize" a window. When a window is maximized, it is expanded to fill all available space; that is, a primary window will fill the screen, while a GenDisplay will fill the display area. Windows can also be "minimized." A window's behavior when it is minimized depends on the specific UI. For example, in OSF/Motif, a minimized Primary is displayed as an icon at the bottom of the screen; a minimized Display is removed from the display area, but stays in the display control's display list.

Most of the mechanics of minimizing and maximizing windows is taken care of by the specific UI. For example, OSF/Motif provides minimize and maximize buttons on all Displays and Primaries which do not specifically disable the functionality. However, an application can send messages to Primary and Display objects to change their minimized/maximized state, or to find out what the current state is.

If you do not wish to have a window be minimizable, you can set the vardata flag ATTR_GEN_DISPLAY_NOT_MINIMIZABLE. Similarly, if you do not wish the window to be maximizable, you can set the flag ATTR_GEN_DISPLAY_NOT_MAXIMIZABLE. These instruct the specific UI not to provide the controls for minimizing and maximizing.

You can find out whether a window is currently minimized by sending it MSG_GEN_DISPLAY_GET_MINIMIZED. Similarly, you can find out whether the window is maximized by sending MSG_GEN_DISPLAY_GET_MAXIMIZED.

If a GenDisplay or GenPrimary has the vardata HINT_DISPLAY_MINIMIZED_ON_STARTUP , the object will be created in its minimized state. Similarly, if you set HINT_DISPLAY_NOT_MINIMIZED_ON_STARTUP, the display will be created in its non-minimized form. If you set HINT_DISPLAY_MAXIMIZED_ON_STARTUP, the specific UI will create the object in its maximized state; correspondingly, if you set HINT_DISPLAY_NOT_MAXIMIZED_ON_STARTUP, the specific UI will create the display in a non-maximized state. As with all hints, the specific UI may ignore these directives. If you set conflicting hints (for example, both HINT_DISPLAY_MINIMIZED_ON_STARTUP and HINT_DISPLAY_MAXIMIZED_ON_STARTUP), the results are undefined.

Most displays which can be maximized can also be "restored"; that is, a control is provided which de-maximizes the object, restoring it to the size it was before it was maximized. If the object has the attribute ATTR_GEN_DISPLAY_NOT_RESTORABLE, this control will not be provided; once a display is maximized, the user will not be able to un-maximize it. This hint is generally set only for GenDisplay objects that are maximized on startup.

If you do not want a user to be able to resize a GenDisplay or GenPrimary, set the vardata HINT_DISPLAY_NOT_RESIZABLE. The specific UI will not provide the means for the user to resize the window. This hint will not prevent the user from minimizing or maximizing the display.

If you want the user to be able to navigate from GenDisplays using TAB navigation, add HINT_DISPLAY_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD to the object's instance data.

MSG_GEN_DISPLAY_SET_MINIMIZED
void	MSG_GEN_DISPLAY_SET_MINIMIZED();

This message instructs a display or primary object to minimize itself. The result depends on the specific UI. Primary windows are usually iconified; display windows might be iconified or temporarily removed. If the window is already minimized, the message has no effect.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Interception: You should not change the behavior of this message. You may intercept this message to find out when a window is being minimized; however, you should make sure to pass this message on to the superclass.

MSG_GEN_DISPLAY_SET_MAXIMIZED
void	MSG_GEN_DISPLAY_SET_MAXIMIZED();

This message instructs a display or primary object to maximize itself. If the window is already maximized, the message has no effect.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Interception: You should not change the behavior of this message. You may intercept this message to find out when a window is being maximized; however, you should make sure to pass this message on to the superclass.

MSG_GEN_DISPLAY_SET_NOT_MINIMIZED
void	MSG_GEN_DISPLAY_SET_NOT_MINIMIZED();

This message instructs a display or primary object to de-minimize itself. It will generally be restored to its position and configuration as of the time it was minimized. If the window is not minimized, the message has no effect.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Interception: You should not change the behavior of this message. You may intercept this message to find out when a window is being de-minimized; however, you should make sure to pass this message on to the superclass.

MSG_GEN_DISPLAY_SET_NOT_MAXIMIZED
void	MSG_GEN_DISPLAY_SET_NOT_MAXIMIZED();

This message instructs a display or primary object to de-maximize itself. It will generally be restored to its position and configuration as of the time it was maximized. If the window is not maximized, the message has no effect.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Interception: You should not change the behavior of this message. You may intercept this message to find out when a window is being de-maximized; however, you should make sure to pass this message on to the superclass.

MSG_GEN_DISPLAY_GET_MINIMIZED
Boolean	MSG_GEN_DISPLAY_GET_MINIMIZED();

This message indicates whether the recipient is minimized.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Return: Returns true (i.e. non-zero) if recipient is minimized; otherwise, it returns false (i.e. zero).

Interception: You should not intercept this message.

MSG_GEN_DISPLAY_GET_MAXIMIZED
Boolean	MSG_GEN_DISPLAY_GET_MAXIMIZED();

This message indicates whether the recipient is maximized.

Source: Unrestricted.

Destination: Any GenDisplay or GenPrimary object .

Return: Returns true (i.e. non-zero) if recipient is maximized; otherwise, it returns false (i.e. zero).

Interception: You should not intercept this message.

Window Objects: 2 Using the GenPrimary

MSG_GEN_PRIMARY_GET_LONG_TERM_MONIKER, MSG_GEN_PRIMARY_USE_LONG_TERM_MONIKER, MSG_GEN_PRIMARY_REPLACE_LONG_TERM_MONIKER, HINT_PRIMARY_FULL_SCREEN, HINT_PRIMARY_NO_FILE_MENU, HINT_PRIMARY_NO_EXPRESS_MENU, HINT_PRIMARY_OPEN_ICON_BOUNDS, HINT_PRIMARY_NO_HELP_BUTTON

Almost every application will have a single GenPrimary object. GEOS uses the GenPrimary to create and manage the primary window of an application. A few applications may have several GenPrimary objects; a very few will have no GenPrimary at all. (Applications with no GenPrimary generally do not have any user interface; they often are intended to work with other applications, communicating via streams.)

The structure of the GenPrimary object is almost the same as that of GenDisplay. The instance data definitions for GenPrimaryClass are shown in Instance data for GenPrimaryClass below.

Code Display 4-2 Instance data for GenPrimaryClass

/* There is only one instance field specifically defined for GenPrimaryClass. */
/* A GenPrimary object can have a long moniker, which is displayed at the top of
 * the window. The moniker is stored in a chunk in the object block containing the
 * GenPrimary; the attribute GPI_longTermMoniker contains the handle of this
 * chunk. The long-term moniker is described below.*/
    @instance ChunkHandle				GPI_longTermMoniker;
/* GenPrimaryClass also modifies the default GI_attrs settings. */
    @default GI_attrs = @default | GA_TARGETABLE;
/* HINT_PRIMARY_FULL_SCREEN indicates that the primary object should be sized to
 * fill a large portion of the screen. If this hint is not present, the Primary
 * will be just large enough to accommodate its children.*/
    @vardata void 			HINT_PRIMARY_FULL_SCREEN;
/* Ordinarily, every Primary window is created with a File menu. You can suppress
 * this by including HINT_PRIMARY_NO_FILE_MENU. */
    @vardata void 			HINT_PRIMARY_NO_FILE_MENU;
/* Also by default, any launched Primary window gets added to the Express Menu. If
 * you wish to suppress this behavior, add HINT_PRIMARY_NO_EXPRESS_MENU. */
    @vardata void			HINT_PRIMARY_NO_EXPRESS_MENU;
/* If a primary object is minimizable, the location of the minimized primary is
 * stored in the vardata field HHINT_PRIMARY_OPEN_ICON_BOUNDS.
 */
    @vardata Rectangle			 HINT_PRIMARY_OPEN_ICON_BOUNDS
/* By default, all primary windows have a "help" button; when the user clicks on
 * it, the window's help text is brought up. If you don't want the primary to
 * provide help text, you can use the hint HINT_PRIMARY_NO_HELP_BUTTON.
 */
    @vardata void 		 HINT_PRIMARY_NO_HELP_BUTTON;

When a Primary window is created, it is usually sized to contain all of its components. However, you can suggest that it be sized to fill almost all the screen by setting the hint HINT_PRIMARY_FULL_SCREEN . This hint says that the Primary should be sized to fill a large portion of the screen, though not all of it. (For example, OSF/Motif sets the Primary to fill the whole screen except for a narrow space for icons at the bottom.) If this hint is not present, the Primary will be just large enough to accommodate its children.

A GenPrimary normally creates a File menu within its menu bar. To suppress creation of this file menu, add HINT_PRIMARY_NO_FILE_MENU . GenPrimarys, by default, are also added to the list of active applications within the system's express menu. Add HINT_PRIMARY_NO_EXPRESS_MENU if you wish to avoid adding the launched GenPrimary to the express menu.

When a Primary is minimized, it is displayed as an icon with a caption beneath it. The icon and caption will be taken from the Primary's moniker list (GI _visMoniker ). If the Primary lacks either a text moniker or a graphic moniker, the missing moniker will be read from the Application object's GI _visMoniker field. Most applications will not set GI _visMoniker in the Primary object, since it would usually mean duplicating the monikers already in the Application object. However, some applications will set this (e.g. because they have several Primary objects and want each one to have a different icon when minimized).

When the Primary is expanded from a minimized state, its minimized location is stored in the hint HINT_PRIMARY_OPEN_ICON_BOUNDS. The next time the Primary is minimized, it will be returned to that location.

GPI_longTermMoniker contains a secondary moniker for the Primary object. This moniker is displayed along with the Primary moniker, in a way which depends on the specific UI. (In OSF/Motif, the Primary's text monikers are shown in its title bar: first the text moniker from GI _visMoniker , then a dash, then the moniker from GPI_longTermMoniker .) If the GenPrimary has a GenDisplayGroup as a child, the GenDisplayGroup will automatically set this field to contain the moniker of the top-most GenDisplay. The application can override this by sending the Primary MSG_GEN_PRIMARY_USE_LONG_TERM_MONIKER, described below.

Most Primary objects will have help text; under most specific UIs, the Primary will have a "help" button to bring up this text. If you don't want to provide help text, you should set the hint HINT_PRIMARY_NO_HELP_BUTTON .

MSG_GEN_PRIMARY_GET_LONG_TERM_MONIKER
ChunkHandle 	MSG_GEN_PRIMARY_GET_LONG_TERM_MONIKER();

Use this message to find out the moniker of a GenPrimary object. The message returns the chunk handle of the moniker; the moniker is in the same block as the GenPrimary object.

Source: Unrestricted.

Destination: GenPrimary.

Return: Returns the chunk handle of the primary's long-term moniker. The chunk is in the same object block as the GenPrimary.

Interception: This message is not ordinarily intercepted.

MSG_GEN_PRIMARY_USE_LONG_TERM_MONIKER
void 	MSG_GEN_PRIMARY_USE_LONG_TERM_MONIKER(
        ChunkHandle		moniker);			/* must be in same object block as
                     * primary */

This message instructs a primary window to change its long-term moniker. The new long-term moniker must already be in a chunk in the same object block as the Primary. The chunk containing the obsolete long-term moniker will not be freed; you must do this manually.

Source: Unrestricted.

Destination: GenPrimary.

Parameters: moniker ChunkHandle of chunk in same object block as the GenPrimary. The chunk should contain the new long-term moniker.

Interception: This message is not ordinarily intercepted.

MSG_GEN_PRIMARY_REPLACE_LONG_TERM_MONIKER
void	MSG_GEN_PRIMARY_REPLACE_LONG_TERM_MONIKER(@stack
        VisUpdateMode 		updateMode,
        word 		height,
        word 		width,
        word 		length,
        VisMonikerDataType 		dataType,
        VisMonikerSourceType 		sourceType,
        dword 		source);

This message is used to replace a primary's long term moniker with a new one. This message's arguments are precisely like those to the message MSG_GEN_REPLACE_VIS_MONIKER . Note that a long term moniker is ordinarily a simple text string. For more information, see the GenClass chapter.

Source: Unrestricted.

Destination: GenPrimary.

Parameters: The parameters are the same as those for MSG_GEN_REPLACE_VIS_MONIKER .

Return: Returns the chunk handle of the new long-term moniker. The moniker will be allocated in the Primary's object block.

Interception: This message is not ordinarily intercepted.

Window Objects: 3 Using Multiple Displays

Many applications will need to have several similar user interface areas. For example, a word processor might have several documents open at once; each of these would need its own text area. GEOS provides a facility for this.

An application can have several GenDisplay objects, each of which must be a child of a GenDisplayGroup object. The user can then switch back and forth between the displays using the GenDisplayControl (which is usually a child of a "Window" menu). The switching is transparent to the application.

If the application uses one display for each document, it should use the Document Control objects to create the displays. The Document Control can automatically duplicate a resource containing a generic object tree headed by a GenDisplay and make that GenDisplay a child of the GenDisplayGroup each time a document is opened or created. For details, see the Documents Objects chapter.

GenDisplayGroup

GenDisplayControl

Using GenDisplayClass Objects

Window Objects: 3.1 Using Multiple Displays: GenDisplayGroup

If an application uses GenDisplay objects, it must have a GenDisplayGroup object. This object makes sure there is space in the GenPrimary for the displays.

The GenDisplayGroup must be either a child of the GenPrimary (in which case the specific UI will decide where to put the display area) or a child of a GenInteraction which is a child of the GenPrimary (if the application wants the display area in a specific part of the GenPrimary). The GenDisplayGroup should be run by the UI thread.

GenDisplayGroupClass has no instance data which may be set or examined by the application. However, GenDisplayGroupClass is a subclass of GenClass , and inherits all of its instance data. When you declare a GenDisplayGroup, you may specify its GenClass instance data normally; you may also include any of the hints described in the following sections.

The GenDisplayGroup Instance Data

HINT_DISPLAY_GROUP_SEPARATE_MENUS, HINT_DISPLAY_GROUP_ARRANGE_TILED, HINT_DISPLAY_GROUP_FULL_SIZED_ON_STARTUP, HINT_DISPLAY_GROUP_OVERLAPPING_ON_STARTUP, HINT_DISPLAY_GROUP_FULL_SIZED_IF_TRANSPARENT_DOC_CTRL_MODE, HINT_DISPLAY_GROUP_TILE_HORIZONTALLY, HINT_DISPLAY_GROUP_TILE_VERTICALLY, HINT_DISPLAY_GROUP_SIZE_INDEPENDENTLY_OF_DISPLAYS

GenDisplayGroupClass is a subclass of GenClass. Other than vardata, this class adds no other instance data. There are several hints defined for GenDisplayGroupClass . Most of these hints specify how displays should be arranged on startup.

Code Display 4-3 GenDisplayGroup Instance Data

/* GenDisplayGroupClass adds no instance fields. It does modify the default
 * GI_attrs settings, however. */
    @default GI_attrs = @default | GA_TARGETABLE;
/* This hint allows each GenDisplay to contain its own menu bar. */
    @vardata void			HINT_DISPLAY_GROUP_SEPARATE_MENUS;
/* These hints specify how a GenDisplayGroup will arrange its GenDisplays. */
    @vardata void			HINT_DISPLAY_GROUP_ARRANGE_TILED;
    @vardata void			HINT_DISPLAY_GROUP_FULL_SIZED_ON_STARTUP;
    @vardata void			HINT_DISPLAY_GROUP_OVERLAPPING_ON_STARTUP;
    @vardata void			HINT_DISPLAY_GROUP_FULL_SIZED_IF_TRANSPARENT_DOC_CTRL_MODE;
    @vardata void			HINT_DISPLAY_GROUP_TILE_HORIZONTALLY;
    @vardata void			HINT_DISPLAY_GROUP_TILE_VERTICALLY;
    @vardata void			HINT_DISPLAY_GROUP_SIZE_INDEPENDENTLY_OF_DISPLAYS;
/* These attributes affect the availability of overlapping and
 * full-sized states. */
    @vardata void			ATTR_GEN_DISPLAY_GROUP_NO_FULL_SIZED;
    @vardata void			ATTR_GEN_DISPLAY_GROUP_NO_OVERLAPPING;
    @vardata void			ATTR_GEN_DISPLAY_OVERLAPPING_STATE;

In some specific UIs (such as OSF/Motif), menus which are children of a GenDisplay object may appear in two ways: they may be drawn on the Primary's menu bar (the default in OSF/Motif), or they may appear in a menu bar on the display itself. HINT_DISPLAY_GROUP_SEPARATE_MENUS indicates that each display should be given its own menu bar (if the specific UI permits this).

There are several hints which specify how the displays should be configured when the GenDisplayGroup is built.

HINT_DISPLAY_GROUP_FULL_SIZED_ON_STARTUP indicates that the GenDisplayGroup should be in full-size mode on startup; that is, all of its children should be maximized.

HINT_DISPLAY_GROUP_OVERLAPPING_ON_STARTUP indicates that the GenDisplayGroup should be in overlapping mode on startup; that is, its children should be non-maximized.

HINT_DISPLAY_GROUP_FULL_SIZED_IF_TRANSPARENT_DOC_CTRL_MODE forces a GenDisplayGroup to start full-sized if the application is in "transparent document control" mode, which is set by the user level of the application. This hint overrides HINT_DISPLAY_GROUP_OVERLAPPING_ON_STARTUP , if present.

HINT_DISPLAY_GROUP_ARRANGE_TILED indicates that the GenDisplayGroup should be in overlapping mode on startup, and further that the displays should be tiled; that is, they should be non-maximized and arranged in a non-overlapping way to fill the display area.

You can specify a preference for how the displays should be tiled by setting HINT_DISPLAY_GROUP_TILE_HORIZONTALLY or HINT_DISPLAY_GROUP_TILE_VERTICALLY . HINT_DISPLAY_GROUP_TILE_HORIZONTALLY indicates that you want tiled displays to be arranged horizontally, with each display tall enough to fill the display area. Similarly, HINT_DISPLAY_GROUP_TILE_VERTICALLY indicates that you want tiled displays to be arranged vertically, with each display wide enough to fill the display area. If both hints are set, the result varies depending on the specific UI.

HINT_DISPLAY_GROUP_SIZE_INDEPENDENTLY_OF_DISPLAYS sizes a GenDisplayGroup by what its parent wants rather than what any of its children GenDisplays want. This may improve geometry performance in a complex GenPrimary/GenDisplay combination.

Arranging Displays in the Display Group

MSG_GEN_DISPLAY_GROUP_SET_OVERLAPPING, MSG_GEN_DISPLAY_GROUP_SET_FULL_SIZED, MSG_GEN_DISPLAY_GROUP_GET_FULL_SIZED, MSG_GEN_DISPLAY_GROUP_TILE_DISPLAYS, ATTR_GEN_DISPLAY_GROUP_NO_FULL_SIZED, ATTR_GEN_DISPLAY_GROUP_NO_OVERLAPPING, ATTR_GEN_DISPLAY_GROUP_OVERLAPPING_STATE

The GenDisplayGroup can be in "full-sized" or "overlapping" mode. If the GenDisplayGroup is in "full-size" mode, all of its children are maximized (except any displays which are set "non-maximizable"). If it is not in full-sized mode, it is said to be in "overlapping" mode; that is, none of its children are maximized. When a user maximizes any display which belongs to a GenDisplayGroup, the GenDisplayGroup automatically goes into "full-size" mode and maximizes all of its children.

If you include ATTR_GEN_DISPLAY_GROUP_NO_FULL_SIZED, the GenDisplayControl will not be able to go into full-size mode; it will always be in overlapping mode. Similarly, if set ATTR_GEN_DISPLAY_GROUP_NO_OVERLAPPING , the GenDisplayControl will not be able to go into overlapping mode; it will always be in full-sized mode, and all displays will always be maximized. Naturally, you may not include both of these attributes at once; if you do, results are undefined.

Messages are provided which switch the GenDisplayGroup into one or another of these modes. You might not need to use any of these messages. If you use a GenDisplayControl object, the user will be able to switch from overlapping to full-size and also to tile the displays by using that object. However, you can also send the following messages directly.

You can set a GenDisplayGroup to full-sized mode by sending it the message MSG_GEN_DISPLAY_GROUP_SET_FULL_SIZED. This message causes the GenDisplayGroup to maximize every one of its children. Children which cannot be maximized will be unaffected. The window layering and focus/target settings are not changed.

You can set a GenDisplayGroup to overlapping mode by sending it the message MSG_GEN_DISPLAY_GROUP_SET_OVERLAPPING. This message causes a GenDisplayGroup object to de-maximize all of its children. Children which are not restorable will be unaffected. The window layering and focus/target settings are not changed.

You can find out whether a GenDisplayGroup object is in full-sized mode by sending it MSG_GEN_DISPLAY_GROUP_GET_FULL_SIZED . If the GenDisplayGroup is in full-sized mode, this message will return true .

You can also put a GenDisplayGroup into "tiled" mode. This is a special case of overlapping mode. When a GenDisplayGroup is put in tiled mode, it first puts itself in overlapping mode. It then attempts to arrange and resize its display children so they fill the display area without overlapping. To put a GenDisplayGroup into tiled mode, send it the message MSG_GEN_DISPLAY_GROUP_TILE_DISPLAYS .

The GenDisplayGroup keeps track of its overlapping state across shutdowns. It does this by setting ATTR_GEN_DISPLAY_GROUP_OVERLAPPING_STATE. Applications may not set or change this attribute directly.

MSG_GEN_DISPLAY_GROUP_SET_FULL_SIZED
void	MSG_GEN_DISPLAY_GROUP_SET_FULL_SIZED();

This message instructs a GenDisplayGroup to put itself in "full-sized" mode; that is, all of its maximizable children will be maximized. This message is ignored if the GenDisplayGroup has the vardata attribute ATTR_GEN_DISPLAY_GROUP_NO_FULL_SIZED.

Source: Unrestricted.

Destination: GenDisplayGroup.

Interception: Not generally intercepted.

MSG_GEN_DISPLAY_GROUP_SET_OVERLAPPING
void	MSG_GEN_DISPLAY_GROUP_SET_OVERLAPPING();

This message instructs a GenDisplayGroup to put itself in "overlapping" mode. This message is ignored if the GenDisplayGroup has the vardata attribute ATTR_GEN_DISPLAY_GROUP_NO_OVERLAPPING.

Source: Unrestricted.

Destination: GenDisplayGroup.

Interception: Not generally intercepted.

MSG_GEN_DISPLAY_GROUP_GET_FULL_SIZED
Boolean	MSG_GEN_DISPLAY_GROUP_GET_FULL_SIZED();

This message finds out whether a GenDisplayGroup is in "full-sized" mode.

Source: Unrestricted.

Destination: GenDisplayGroup.

Return: Returns true (i.e. non-zero) if the GenDisplayGroup is in full-sized mode; otherwise, it returns false (i.e. zero).

Interception: Not generally intercepted.

MSG_GEN_DISPLAY_GROUP_TILE_DISPLAYS
void	MSG_GEN_DISPLAY_GROUP_TILE_DISPLAYS;

This message instructs a GenDisplayGroup to put itself in "tiled" mode. That is, it should first put itself in "overlapping" mode; it should then arrange and resize the displays so they fill the display area without overlapping. The message is ignored if the GenDisplayGroup has the vardata attribute ATTR_GEN_DISPLAY_GROUP_NO_OVERLAPPING.

Source: Unrestricted.

Destination: GenDisplayGroup.

Interception: Not generally intercepted.

Selecting a Display

MSG_GEN_DISPLAY_GROUP_SELECT_DISPLAY

Ordinarily, the user switches from one display to another in one of two ways. The user may use the specific UI's way of switching displays (e.g. clicking on the display); or he may use the GenDisplayControl (described below) to switch displays. The application can also force the Display Group to bring a certain display to the top by sending it MSG_GEN_DISPLAY_GROUP_SELECT_DISPLAY . However, this is not usually done; applications should generally let the user switch displays with the GenDisplayControl.

MSG_GEN_DISPLAY_GROUP_SELECT_DISPLAY
void	MSG_GEN_DISPLAY_GROUP_SELECT_DISPLAY(
        word	displayNum);

This message instructs a display group to select a certain display, bringing it to the top and making it the focus. Applications should not ordinarily need to send this.

Source: Usually GenDisplayControl or its associated objects; however, any object can send this.

Destination: GenDisplayGroup.

Parameters: displayNum The display to select. This is an integer specifying the position of the desired display among the GenDisplayGroup's children; that is, its first child is number zero, its next child is number one, and so on.

Interception: This message is not ordinarily intercepted.

Tips: You can find a display's position number by sending MSG_GEN_FIND_CHILD to the GenDisplayGroup, passing the optr of the desired display. See the GenClass chapter.

Window Objects: 3.2 Using Multiple Displays: GenDisplayControl

The GenDisplayGroup object does half the job of managing display objects: it creates a space for the displays and manages them as its children. However, the display group does very little interaction with the user. Instead, the user works mainly with the GenDisplayControl object.

The GenDisplayControl is usually a child of a "Window" GenInteraction, which is itself a child of the primary. GenDisplayControlClass is a subclass of GenControlClass , and it has all the functionality of that class. For more information, see the GenControlClass chapter.

GenDisplayControlClass is based very closely on GenControlClass . The differences between it and GenControlClass are shown below in Instance Data of GenDisplayControlClass .

Code Display 4-4 Instance Data of GenDisplayControlClass

/* GDCII_attrs is a byte-length flag field. There is currently only one flag,
 * namely GDCA_MAXIMIZED_NAME_ON_PRIMARY; this specifies that if the active
 * display is maximized, its name should be shown as the primary's long-term
 * moniker. */
    @instance 		GenDisplayControlAttributes				GDCII_attrs =
                        (GDCA_MAXIMIZED_NAME_ON_PRIMARY);
@default		GI_states = @default | GS_ENABLED;
@default 		GCI_output = {TO_APP_TARGET};
  typedef WordFlags GDCFeatures;
#define GDCF_OVERLAPPING_MAXIMIZED					0x0004
#define GDCF_TILE					0x0002
#define GDCF_DISPLAY_LIST					0x0001
  typedef WordFlags GDCToolboxFeatures;
#define GDCTF_OVERLAPPING_MAXIMIZED					0x0004
#define GDCTF_TILE					0x0002
#define GDCTF_DISPLAY_LIST					0x0001
#define GDC_DEFAULT_FEATURES 				(GDCF_OVERLAPPING_MAXIMIZED | GDCF_TILE
                | GDCF_DISPLAY_LIST)
#define GDC_DEFAULT_TOOLBOX_FEATURES (GDCF_DISPLAY_LIST)
/* A GenDisplayControl also features a single hint which affects the display of
 * the features list. */
    @vardata void  HINT_DISPLAY_CONTROL_NO_FEATURES_IF_TRANSPARENT_DOC_CTRL_MODE;

The GDCII _attrs field contains a set of GenDisplayControlAttributes . There is currently only one flag defined among these attributes:

GDCA_MAXIMIZED_NAME_ON_PRIMARY
If this attribute is set and the active display is maximized, the name of the selected display will be shown in the long term moniker of the GenPrimary.

HINT_DISPLAY_CONTROL_NO_FEATURES_IF_TRANSPARENT_DOC_CTRL_MODE suppresses display of the features list if the application is running in "transparent document control" mode, as selected by the user level of the application.

Window Objects: 3.3 Using Multiple Displays: Using GenDisplayClass Objects

All GenDisplay objects must be children of a GenDisplayGroup object. GenDisplay objects can be created in several ways: an application can declare them in its code; it can instantiate them at run-time and make them children of the GenDisplayGroup; or, if the application uses the Document Control objects, it can have the Document Control create a new display automatically whenever a document is opened. (For details about using a Document Control to create GenDisplay objects, see the Documents Objects chapter.)

Closing GenDisplays

MSG_GEN_DISPLAY_CLOSE

Most specific UIs provide a way for the user to close windows. For example, OSF/Motif lets a user close a window by double-clicking the control button. When the user uses the specific UI's close mechanism, the Display or Primary is sent MSG_GEN_DISPLAY_CLOSE.

GenDisplayClass does only one thing when it receives MSG_GEN_DISPLAY_CLOSE: it sends MSG_GEN_DOCUMENT_CLOSE to the document specified by GDI _document . The GenDisplayClass handler for MSG_GEN_DISPLAY_CLOSE does not, in fact, destroy the display. If the display is linked to a GenDocument, the GenDocument will respond to MSG_GEN_DOCUMENT_CLOSE by closing the application and removing the GenDisplay. Otherwise, you will have to remove the GenDisplay yourself by writing a handler for MSG_GEN_DISPLAY_CLOSE.

The GenPrimaryClass handler for MSG_GEN_DISPLAY_CLOSE closes the application. If you want to add to or replace this behavior, you may have your Primary subclass this message.

MSG_GEN_DISPLAY_CLOSE
void	MSG_GEN_DISPLAY_CLOSE();

This message is sent to a Display to close it. The system sends it when the user uses the specific UI's way of closing a window. The GenDisplayClass handler does nothing but send a MSG_GEN_DOCUMENT_CLOSE to the Document object specified in GDI _document . The GenPrimaryClass handler closes the application.

Source: Unrestricted.

Destination: GenDisplay.

Interception: If the Display is not associated with a GenDocument, you will need to subclass this message for it to have any effect at all. If the display is associated with a GenDocument object, you should probably subclass your Document object's MSG_GEN_DOCUMENT_CLOSE instead. Primary objects may subclass this message if they want to alter or replace the default behavior (of closing the application).

Menu Bar PopOuts

ATTR_GEN_DISPLAY_MENU_BAR_POPPED_OUT

Some objects contain the ability to "pop out" of their sub-group locations and become floating dialog boxes.The menu bar of a GenDisplay is one such GenInteraction. If the menu bar of a GenDisplay is currently in the "popped-out" state, it will contain the vardata entry ATTR_GEN_DISPLAY_MENU_BAR_POPPED_OUT.

Messages sent to GenDisplays

MSG_GEN_DISPLAY_UPDATE_FROM_DOCUMENT, MSG_GEN_DISPLAY_GET_DOCUMENT

Many of the messages which can be sent to GenDisplay objects have already been discussed above in A First Look at GenDisplay . However, there are a few messages which are ordinarily sent to Displays but not to Primaries. These messages are discussed here.

GenDisplay objects often work in close conjunction with the Document Control objects. It is common for every open document to have its own GenDisplay object as well as its own GenDocument. The two objects work in conjunction, sending messages back and forth to communicate. You can send or intercept these messages yourself to add functionality.

When the document object changes in certain significant ways, the Display has to be brought into accord with it. For example, if the name of the file changes, the GenDisplay's moniker will have to be changed to reflect this. Whenever a significant change takes place, the Document Control objects send a MSG_GEN_DISPLAY_UPDATE_FROM_DOCUMENT to the appropriate Display. The Display then requests all necessary information from the GenDocument and makes any necessary changes to its own instance data. You can force this updating by sending the message directly to the Display. You can also subclass this message if you want to add special updating behavior; however, you should be sure to pass this message to the superclass' handler.

You can find out which Document object is associated with a given Display by sending MSG_GEN_DISPLAY_GET_DOCUMENT to the Display. The message will return an optr to the corresponding Document object.

MSG_GEN_DISPLAY_UPDATE_FROM_DOCUMENT
void	MSG_GEN_DISPLAY_UPDATE_FROM_DOCUMENT();

This message instructs a GenDisplay to update its instance data from its associated GenDocument object (if any). This message is ordinarily sent by the Document Control objects.

Source: Unrestricted--ordinarily sent only by Document Control objects.

Destination: GenDisplay.

Interception: Normally not intercepted. If you subclass this message to add special updating behavior, be sure to end with an @callsuper.

MSG_GEN_DISPLAY_GET_DOCUMENT
optr	MSG_GEN_DISPLAY_GET_DOCUMENT();

This message returns the optr of the GenDocument associated with a given GenDisplay. This is equal to the value of the GenDisplay's GDI _document field.

Source: Unrestricted--ordinarily sent only by Document Control objects.

Destination: GenDisplay.

Interception: Normally not intercepted.

Traveling Objects

ATTR_GEN_DISPLAY_TRAVELLING_OBJECTS

If you use multiple GenDisplay objects, it is sometimes useful to set up a group of "traveling objects." Traveling objects are children of whichever display is active. When a different GenDisplay is brought to the top, all traveling objects will be set "not usable" and removed from the Generic tree. They will then be added as children of the new top display and set "usable." (Any children of the traveling objects will naturally move with them.) Traveling objects are most commonly Toolbox Interactions, but they can be any kind of generic object.

Traveling objects can only be used under certain circumstances. Every display must belong to its own object block, and all of these object blocks must be copies of the same original. This is because the traveling objects are added as children to a specified chunk in whichever object block contains the new top display. If you want to use traveling objects, you should declare a special "template" object block which contains a GenDisplay and its children. Whenever you need to create a GenDisplay, you should duplicate this object block. The traveling objects should be in another resource altogether. If you use the Document Control objects to create displays, the objects will use this technique, duplicating an object block for each new display; this will let you use traveling objects.

Every traveling object is indicated by a TravelingObjectReference structure (see TravelingObjectReference ). To attach traveling objects to the active display, create a chunk which contains a list of TravelingObjectReference structures; this chunk must be in the same object block as the active display. Then set the Display's ATTR_GEN_DISPLAY_TRAVELING_OBJECTS to the ChunkHandle of the list. The list will automatically be moved to the block of the active display whenever the traveling objects are moved.

Code Display 4-5 TravelingObjectReference

typedef struct {
    optr		TIR_travelingObject;			/* optr to traveling object whose
                     * reference this is */
    ChunkHandle		TIR_parent;			/* Chunk Handle of object in Display's
                     * block that will be the parent of this
                     * object */
    word		TIR_compChildFlags;			/* CompChildFlags to use when
                     * adding the traveling object */
} TravelingObjectReference;

The TravelingObjectReference structure has the following three fields:

TIR _travelingObject
This field is an optr to the traveling object whose reference this is.
TIR _parent
This field holds the chunk handle of an object in the display block. When the traveling object is added to a display block, it will be made a child of the object whose chunk handle this is.
TIR _compChildFlags
This is the set of CompChildFlags which will be used when attaching this object to its new parent.
This document is a single-page version of a a multi-page document, suitable for easy printing.


The GenProcess Object

GenProcessClass is the class that you will use to define the Process object of your applications. This class includes some functionality for opening and closing geodes as well as saving to and restoring from state files. Typically, your application will define its own subclass of GenProcessClass ; this subclass will be used as your Process object and will receive all messages destined for the Process.

In this subclass you can alter the GenProcessClass , ProcessClass , or MetaClass messages you need. More often, however, the subclass will be used to define new messages that are application-global or that should be handled by your Process object.

1 Starting and Stopping
2 Undo Mechanism
2.1 Undo Mechanism

The GenProcess Object: 1 Starting and Stopping

For information about the steps involved in stopping, starting, or restoring an application (and to get context information about the messages described below), see the GEOS Programming chapter.

Many of the following messages need AppAttachFlags to tell them how the process is attaching:

typedef WordFlags AppAttachFlags;
#define AAF_RESTORING_FROM_STATE						0x8000
#define AAF_STATE_FILE_PASSED						0x4000
#define AAF_DATA_FILE_PASSED						0x2000
#define AAF_RESTORING_FROM_QUIT						0x1000
  • AAF_RESTORING_FROM_STATE indicates that the application is coming up from a previous state, either by re-launching using a state file, or re-entering application mode from engine mode in the same session. The UI trees will be in whatever state they were left in, which may be different than the statically declared UI tree, depending on what occurred in the application when the state file was written out (for example, a dialog box may have been on-screen). If this flag is false , the application is starting up fresh. If this flag is true , the UI does not call MSG_META_LOAD_OPTIONS and ignores any "ON_STARTUP" hints or attributes.
  • AAF_STATE_FILE_PASSED indicates that the application is restoring from the state file passed in an AppLaunchBlock , presumably passed as an argument in the message also. If this flag is set, AAF_RESTORING_FROM_STATE will also be set.
  • AAF_DATA_FILE_PASSED indicates that the passed AppLaunchBlock contains the name of a data file that should be opened.
  • AAF_RESTORING_FROM_QUIT indicates that the application was in the process of quitting, reached engine mode, and is now being started back up into application mode. If set, will also be set, and we will be brought up in whatever state we originally entered engine mode.
MSG_GEN_PROCESS_RESTORE_FROM_STATE
void 	MSG_GEN_PROCESS_RESTORE_FROM_STATE(
        AppAttachFlags 		attachFlags,
        MemHandle 		launchBlock,
        MemHandle 		extraState);

This message is sent by the User Interface when an application is being loaded from a state file. This is sent to the process itself from MSG_META_ATTACH , whenever the application is being invoked as in MSG_GEN_PROCESS_RESTORE_FROM_STATE mode. Data passed is the same as that in MSG_META_ATTACH . The default handler fetches the application mode message, either MSG_GEN_PROCESS_OPEN_APPLICATION or MSG_GEN_PROCESS_OPEN_ENGINE , as saved in the application object, and sends that message to the process.

Note that the blocks passed need not be freed, as this is done by the caller upon return.

Source: Default GenProcessClass handler for MSG_META_ATTACH.

Destination: Self.

Interception: Intercepted generally only so that application can retrieve previously saved data out of the state block passed.

Parameters: attachFlags Flags with information about the state and data files.

launchBlock
Handle of AppLaunchBlock , or zero if none. This block contains the name of any document file passed into the application on invocation.
extraState
Handle of extra state block, or zero if none. This is the same block as returned from MSG_GEN_PROCESS_CLOSE_APPLICATION or MSG_GEN_PROCESS_CLOSE_ENGINE , in some previous MSG_META_DETACH . Process objects often use this extra block to save global variables to state files.

Return: Nothing.

MSG_GEN_PROCESS_OPEN_APPLICATION
void	MSG_GEN_PROCESS_OPEN_APPLICATION(
        AppAttachFlags		attachFlags,
        MemHandle		launchBlock,
        MemHandle 		extraState);

This message is sent by the User Interface when an application is being loaded from its resource blocks. Applications will often intercept this message to set up certain things before being put on-screen. This is the handler in which, for example, you would register for certain notifications such as the quick-transfer notification.

This is sent to the process itself from MSG_META_ATTACH , whenever the application is being restored to application mode (as opposed to engine mode), or whenever it is being invoked as in application mode. Data passed is the same as that in MSG_META_ATTACH . The default handler sets the GenApplication object GS_USABLE, and brings the UI up on screen.

This message may be intercepted to open up any data file passed, before the UI for the application is actually set GS_USABLE. Note that the blocks passed need not be freed, as this is done by the caller upon return.

If you intercept this message and need to send MSG_META_QUIT from within the handler, make sure to either send the quit after calling the superclass, or send the quit via MSG_META_FLUSH_INPUT_QUEUE. (If this is not done, the system will think that an IACP connection is open, aborting the quit.)

Source: GenProcessClass default handler for MSG_META_ATTACH only.

Destination: Same object.

Interception: Frequently intercepted by an application's own process class to find out when an application is first coming alive in the system. You must pass this message on to the superclass, or the application will never come up. Be aware that the entire UI tree for the application is the equivalent of not usable (~GS_USABLE) before the superclass is called, and is usable and up on screen visually after it is called. Thus, it is best to do non-UI related things, and changing of generic attributes and hints before calling the superclass. You must wait until after calling the superclass to perform any operations which require that objects be fully usable (e.g. bringing up a dialog box). Make sure that if you send MSG_META_QUIT from within your handler that you follow the guidelines mentioned above.

Parameters: attachFlags State information about the state and data files.

launchBlock
Handle of AppLaunchBlock , or zero if none. This block contains the name of any document file passed into the application on invocation.
extraState
Handle of extra state block, or zero if none. This is the same block as returned from MSG_GEN_PROCESS_CLOSE_APPLICATION or MSG_GEN_PROCESS_CLOSE_ENGINE , in some previous MSG_META_DETACH .
Is freed by caller--subclasses should not free the extra state block.

Return: Nothing.

MSG_GEN_PROCESS_CLOSE_APPLICATION
MemHandle MSG_GEN_PROCESS_CLOSE_APPLICATION();

This message is sent by the User Interface whenever the application is being shut down (during a detach) and it had been launched in application (as opposed to engine) mode.

Source: GenProcessClass default handler for MSG_META_DETACH only

Destination: Self

Interception: Convenient place for code that needs to be executed before application exits, for non-engine mode cases. Superclass must be called.

Parameters: None.

Return: Handle of block to save (or NULL for none). Process objects often save global variables to a state file in an extra block. This is the handle of that block.

MSG_GEN_PROCESS_OPEN_ENGINE
void 	MSG_GEN_PROCESS_OPEN_ENGINE(
        AppAttachFlags 		attachFlags,
        MemHandle 		launchBlock);

This is sent to the process itself from MSG_META_ATTACH , whenever the application is being restored to engine mode, or whenever it is being invoked as in engine mode. Data passed is the same as that in MSG_META_ATTACH . There is no default handler.

This message may be intercepted to open up any data file passed, before any engine commands are delivered to the process. Note that the blocks passed need not be freed, as this is done by the caller upon return.

Source: GenProcessClass default handler for MSG_META_ATTACH only.

Destination: Self.

Interception: Generally unnecessary, though can be intercepted if notification of going into this mode is necessary.

Parameters: attachFlags State of state and data files.

launchBlock
Handle of AppLaunchBlock , or NULL if none. This block contains the name of any document file passed into the application on invocation.

Return: Nothing.

MSG_GEN_PROCESS_CLOSE_ENGINE
void 	MSG_GEN_PROCESS_CLOSE_ENGINE();

This message is sent by the User Interface whenever the application is being shut down (during a detach) and it had been launched in "engine" mode.

Source: GenProcessClass default handler for MSG_META_DETACH only.

Destination: Self.

Interception: Convenient place for code that needs to be executed before the application exits, for engine mode cases. Superclass must be called.

MSG_GEN_PROCESS_CLOSE_CUSTOM
MemHandle MSG_GEN_PROCESS_CLOSE_CUSTOM();

This message is sent by the User Interface whenever the application is being shut down (during a detach) and it had been launched in some custom mode (not application or engine) that GenProcessClass doesn't know about.

Source: Subclass of GenProcessClass .

Destination: Self.

Interception: Convenient place for code that needs to be executed before the application exits, for custom mode cases. Superclass must be called.

Parameters: None.

Return: Handle of block to save (or NULL for none).

MSG_GEN_PROCESS_ATTACH_TO_PASSED_STATE_FILE
MemHandle 	MSG_GEN_PROCESS_ATTACH_TO_STATE_FILE(
        AppAttachFlags		attachFlags,
        MemHandle		launchBlock);

This message is sent by the User Interface whenever the application is being attached to a state file. This message is sent when either restoring from state or detaching. May be subclassed to provide forced state behavior.

Source: GenProcessClass default handler for MSG_META_ATTACH .

Destination: Self.

Interception: May be intercepted to force use of a particular state file (by changing the name of the state file to use before calling superclass).

Parameters: attachFlags AppAttachMode (matches that in AppLaunchBlock ).

launchBlock
Block of structure AppLaunchBlock .

Return: Handle of extra block of state data (zero for none).

MSG_GEN_PROCESS_CREATE_NEW_STATE_FILE
word MSG_GEN_PROCESS_CREATE_NEW_STATE_FILE(
         MemHandle appInstanceReference);

This replaces MSG_GEN_PROCESS_ATTACHED_TO_PASSED_STATE_FILE if no state file had been specified in that message. This message's default handler will create a new state file and attach it normally. Can be subclassed to provide forced state file usage (e.g. use a different naming scheme).

Source: GenProcessClass default handler for MSG_META_DETACH, if not quitting.

Destination: Self.

Interception: May be intercepted to change name of state file to create.

Parameters: appInstanceReference
Block handle to block of structure AppInstanceReference .

Return: VM file handle (NULL if you want no state file).

MSG_GEN_PROCESS_DETACH_FROM_STATE_FILE
void 	MSG_GEN_PROCESS_DETACH_FROM_STATE_FILE(
        MemHandle 		extraState,
        word 		appStates);

This message is sent by the User Interface when the application is detaching or quitting (may or may not be attached to a state file) and the detach is nearly complete.

Source: GenProcessClass default handler for MSG_META_DETACH , if not quitting.

Destination: Self.

Interception: Not generally done. Default behavior is what you want.

Parameters: extraState Block handle of extra block to be saved (as returned from MSG_GEN_PROCESS_CLOSE_APPLICATION , MSG_GEN_PROCESS_CLOSE_ENGINE or MSG_GEN_PROCESS_CLOSE_CUSTOM ). If the block is not transferred to the state file), it must be freed (if non-zero) by the handler for this message.

appStates
ApplicationStates record with information about the application state.

Return: Nothing.

MSG_GEN_PROCESS_INSTALL_TOKEN
void 	MSG_GEN_PROCESS_INSTALL_TOKEN();

This message is sent by the desktop to a process to get that process to install its token and moniker lists into the token database.

Source: Generally whatever geode launched this application in engine mode (e.g. GeoManager).

Destination: GenProcessClass object of any geode launched in engine mode.

Interception: May be intercepted to install additional tokens. Default behavior installs only application icon.

Parameters: None.

Return: Nothing.

MSG_GEN_PROCESS_GET_PARENT_FIELD
optr 	MSG_GEN_PROCESS_GET_PARENT_FIELD();

This message is sent by process-libraries (such as the Spool Object Library) to find out which field object is its parent. This message will return the field of the first client of the library.

Source: Unrestricted.

Destination: GenProcessClass object.

Interception: Not necessary, as default behavior implements utility.

Parameters: Nothing.

Return: Parent field.

MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST
void	MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST(@stack
        word		sendFlags,
        EventHandle 		event,
        MemHandle 		block,
        word		manufListType,
        ManufacturerID 		manufID);

This message sends the specified event to all the objects registered with the passed GCN list. This message should be subclassed by UI Controller objects.

The handler here merely sends the request on to GenApplication using MSG_META_GCN_LIST_SEND . Controllers should use this message, however, over direct communication with the application object, to ensure orderly updating of the list status event. One such failure case which is fixed is two target text objects, one run by the process thread in a view, the other a GenText run by the UI thread. If the GenText has the target and the user clicks quickly on view then GenText, the GenText may process both messages about the target being lost and gained before the process text object receives its gained and lost pair. If both objects sent MSG_META_GCN_LIST_SEND directly to the GenApplication object, the GenText's status would be wiped out by the subsequent reporting by the process text object. This problem is avoided by having both process and UI objects call here to pass status update info. This works because target changes start out ordered in the UI thread, and that order is passed on to the process thread in either of the two cases.

Source: Any object wishing to update an application GCN list. Don't use queue-order altering message flags such as MF_PLACE_IN_FRONT when sending this message. As a convention must be established for the flag MF_FORCE_QUEUE in order to ensure orderly results, the convention in use is this: Don't pass it.
A typical call should use only the MF_STACK, MF_FIXUP_DS, and/or MF_FIXUP_ES flags, if needed.

Destination: GenProcessClass (application process) only.

Interception: Should not generally be intercepted, as GenProcessClass provides correct behavior. If intercepted and not sent onto superclass, event passed must be destroyed and data block reference count decremented, to avoid leaving obsolete data on the heap.

Parameters: sendFlags Flags to pass on to GCNListSend() .

event
Classed event to pass on to the list.
block
Handle of extra data block, if used (otherwise NULL). Blocks of this type must have a reference count, which may be initialized with MemInitRefCount() and be incremented for any new usage with MemIncRefCount() . Methods in which they are passed are considered such a new usage, and must have MetaClass handlers which call MemDerefCount() . Current messages supported are MSG_META_NOTIFY_WITH_DATA_BLOCK and MSG_NOTIFY_FILE_CHANGE .
manufListType
This may be a GCNStandardListType or any other word which acts as a GCN list ID.
manufID
Manufacturer ID, which helps identify the GCN list.

The GenProcess Object: 2 Undo Mechanism

The Undo mechanism is implemented at the GenProcess level; this allows the undo mechanism to be applicable to any application or process. In general, Undo allows a process, usually within an application, but possibly within a library, to reverse changes made in the state of other objects. GEOS allows an almost unlimited number of stored and reversible Undo actions; the practical limit is somewhere around 100 actions.

Undo actions are stored within undo chains . These chains allow queued actions to be undone in reverse order. Each element in an undo chain is made up of an UndoActionStruct . These structures are usually added with an AddUndoActionStruct . This structure has several elements:

typedef struct {
    UndoActionStruct				AUAS_data;
    optr				AUAS_output;
    AddUndoActionFlags				AUAS_flags;
} AddUndoActionStruct

A chain of undo actions is stored for each object. If you want your object to recognize undo-able actions, you must add the undo actions yourself and intercept MSG_META_UNDO when those actions are played back. The object should be able to understand the data within the UndoActionStruct to perform the Undo action.

There are two AddUndoActionFlags which affect when and how undo notification occurs. If AUAF_NOTIFY_BEFORE_FREEING or AUAF_NOTIFY_IF_FREED_WITHOUT_BEING_PLAYED_BACK is set in the AddUndoActionFlags , it not only receives MSG_META_UNDO but also receives MSG_META_UNDO_FREEING_ACTION when the undo mechanism frees the action. You can check the flags in the AddUndoActionStruct passed with this message to decide what action to take.

The object wishing to register an action for undo sends the process MSG_GEN_PROCESS_UNDO_START_CHAIN. For each action in this undo chain (there may be multiple actions in a single chain) send MSG_GEN_PROCESS_UNDO_ADD_ACTION; pass this message the AddUndoActionStruct action of the action to add. Finally, send MSG_GEN_PROCESS_UNDO_END_CHAIN to mark the end of this undo chain. Undo chains may be nested within each other.

The messages following this section also describe supplemental behavior that you may find useful. In addition to these messages, GenProcessClass also provides the following routines:

GenProcessUndoGetFile() returns a file handle of a Huge Array or DB item to hold undo information. Use this routine to get a file to put such undo information into.

GenProcessUndoCheckIfIgnoring() allows an application or library to check whether an application is ignoring undo information; in this case, it can avoid creating unnecessary undo information.

MSG_GEN_PROCESS_UNDO_START_CHAIN
void 	MSG_GEN_PROCESS_UNDO_START_CHAIN(@stack
        optr 	title,
        optr 	owner);

This message notifies the process of the start of an undo-able action. Note that all this message does is increment a count--a new undo chain is created when the count goes from zero to one. This allows a function to perform a number of undo-able actions and have them all grouped as a single undo-able action.

Source: Any object wanting to register an action for undo.

Destination: GenProcessClass only.

Interception: In general, should not be intercepted.

Parameters: title The null-terminated title of this action. If NULL, then the title of the undo action will be the title passed with the next UNDO_START_CHAIN.

owner
The object which owns this action.

Return: Nothing.

MSG_GEN_PROCESS_UNDO_END_CHAIN
void 	MSG_GEN_PROCESS_UNDO_END_CHAIN(
        BooleanWord 		flushChainIfEmpty);

This message notifies the process of the end of an undo-able action. Note that all this message does is decrement a count--the current undo chain is terminated when the count goes from one to zero. This allows a function to perform a number of undo-able actions and have them all grouped as a single undo-able action.

Source: Any object wanting to register an action for undo.

Destination: GenProcessClass only.

Interception: In general, should not be intercepted.

Parameters: flushChainIfEmpty
Non-zero if you want to delete the chain if it has no actions; zero if the chain is OK without actions (actions will be added later).

Return: Nothing.

MSG_GEN_PROCESS_UNDO_ABORT_CHAIN
void	MSG_GEN_PROCESS_UNDO_ABORT_CHAIN();

This message aborts the current undo chain, destroying all actions in place on the current chain, and instructs the undo mechanism to ignore any undo information until the current undo chain is ended. This latter behavior is needed because the current chain may be nested within several chains, so we must ignore undo chain actions until the outermost chain is ended.

Source: Unrestricted.

Destination: GenProcess object.

Interception: Do not intercept.

MSG_GEN_PROCESS_UNDO_ADD_ACTION
VMChain 	MSG_GEN_PROCESS_UNDO_ADD_ACTION(
        AddUndoActionStruct 	*data);

This message adds a new undo action to the current undo chain.

Source: Any object wanting to register an action for undo.

Destination: GenProcessClass .

Interception: In general, should not be intercepted.

Parameters: data Structure containing information which may be used to undo action.

Return: Will return NULL if we are ignoring undo messages.
If the value passed in UAS_datatype was UADT_PTR or UADT_VMCHAIN, then will return a VMChain or DBItem which may be used to undo the action. If neither of the above cases is true, return value is meaningless.

MSG_GEN_PROCESS_UNDO_GET_FILE
VMFileHandle MSG_GEN_PROCESS_UNDO_GET_FILE();

This message returns a VM file handle to store undo actions. This message is useful to access undo data in either a huge array or DB item. You may also use GenProcessUndoGetFile() to retrieve this file instead.

Source: Any object wanting to access the undo file.

Interception: Should not be intercepted.

Parameters: None.

Return: File handle of VM file with undo information.

MSG_GEN_PROCESS_UNDO_FLUSH_ACTIONS
void 	MSG_GEN_PROCESS_UNDO_FLUSH_ACTIONS();

This message flushes the current undo chain (frees all undo actions, notifies edit control that there is no undo item).

Source: Any object using undo.

Interception: Should not be intercepted.

Parameters: None.

Return: Nothing.

MSG_GEN_PROCESS_UNDO_SET_CONTEXT
dword 	MSG_GEN_PROCESS_UNDO_SET_CONTEXT(
        dword 	context);

This message sets the current undo context. This allows the application to have separate undo chains associated with various documents or modes. This should be sent out before any other undo related messages. The document control automatically takes care of this when a document gets the model exclusive.

Passing NULL_UNDO_CONTEXT as the new context will trigger some zealous EC code if any other undo messages are sent while the context is null.

Source: Any object using undo.

Interception: Generally, should not be intercepted. Applications wanting to override the default behavior should at least flush out the current undo actions, as they will probably not be valid in the new context.

Parameters: context New context (this has no meaning to the undo mechanism--it's just a value).

Return: Old context.

Structures:

#define NULL_UNDO_CONTEXT 0
MSG_GEN_PROCESS_UNDO_GET_CONTEXT
dword 	MSG_GEN_PROCESS_UNDO_GET_CONTEXT();

This message gets the current undo context.

Source: Any object using undo.

Interception: Generally, should not be intercepted.

Parameters: None.

Return: Current context.

MSG_GEN_PROCESS_UNDO_PLAYBACK_CHAIN
void 	MSG_GEN_PROCESS_UNDO_PLAYBACK_CHAIN();

This message plays back the current undo chain, one action at a time. It simultaneously creates a "redo" chain, so the undone action can be redone.

Source: Edit control.

Interception: Generally, should not be intercepted.

Parameters: None.

Return: Nothing.

MSG_GEN_PROCESS_UNDO_IGNORE_ACTIONS
void 	MSG_GEN_PROCESS_UNDO_IGNORE_ACTIONS(
        Boolean		flushActions);

This message causes a process to reject any undo messages.

Source: Edit control.

Parameters: flushActions true to flush the queue.

Interception: Generally, should not be intercepted.

MSG_GEN_PROCESS_UNDO_ACCEPT_ACTIONS
void 	MSG_GEN_PROCESS_UNDO_ACCEPT_ACTIONS();

This message causes a process to accept undo messages again.

Source: Edit control.

Interception: Generally, should not be intercepted.

MSG_GEN_PROCESS_UNDO_CHECK_IF_IGNORING
Boolean 	MSG_GEN_PROCESS_UNDO_CHECK_IF_IGNORING();

This message checks to see if the system is currently ignoring undo actions.

Source: Edit control.

Interception: Generally, should not be intercepted.

Parameters: None.

Return: Will return true if ignoring actions.

The GenProcess Object: 2.1 Undo Mechanism: Undo Mechanism

The Undo mechanism is implemented at the GenProcess level; this allows the undo mechanism to be applicable to any application or process. In general, Undo allows a process, usually within an application, but possibly within a library, to reverse changes made in the state of other objects. GEOS allows an almost unlimited number of stored and reversible Undo actions; the practical limit is somewhere around 100 actions.

Undo actions are stored within undo chains . These chains allow queued actions to be undone in reverse order. Each element in an undo chain is made up of an UndoActionStruct . These structures are usually added with an AddUndoActionStruct . This structure has several elements:

typedef struct {
    UndoActionStruct				AUAS_data;
    optr				AUAS_output;
    AddUndoActionFlags				AUAS_flags;
} AddUndoActionStruct

A chain of undo actions is stored for each object. If you want your object to recognize undo-able actions, you must add the undo actions yourself and intercept MSG_META_UNDO when those actions are played back. The object should be able to understand the data within the UndoActionStruct to perform the Undo action.

There are two AddUndoActionFlags which affect when and how undo notification occurs. If AUAF_NOTIFY_BEFORE_FREEING or AUAF_NOTIFY_IF_FREED_WITHOUT_BEING_PLAYED_BACK is set in the AddUndoActionFlags , it not only receives MSG_META_UNDO but also receives MSG_META_UNDO_FREEING_ACTION when the undo mechanism frees the action. You can check the flags in the AddUndoActionStruct passed with this message to decide what action to take.

The object wishing to register an action for undo sends the process MSG_GEN_PROCESS_UNDO_START_CHAIN. For each action in this undo chain (there may be multiple actions in a single chain) send MSG_GEN_PROCESS_UNDO_ADD_ACTION; pass this message the AddUndoActionStruct action of the action to add. Finally, send MSG_GEN_PROCESS_UNDO_END_CHAIN to mark the end of this undo chain. Undo chains may be nested within each other.

The messages following this section also describe supplemental behavior that you may find useful. In addition to these messages, GenProcessClass also provides the following routines:

GenProcessUndoGetFile() returns a file handle of a Huge Array or DB item to hold undo information. Use this routine to get a file to put such undo information into.

GenProcessUndoCheckIfIgnoring() allows an application or library to check whether an application is ignoring undo information; in this case, it can avoid creating unnecessary undo information.

MSG_GEN_PROCESS_UNDO_START_CHAIN
void 	MSG_GEN_PROCESS_UNDO_START_CHAIN(@stack
        optr 	title,
        optr 	owner);

This message notifies the process of the start of an undo-able action. Note that all this message does is increment a count--a new undo chain is created when the count goes from zero to one. This allows a function to perform a number of undo-able actions and have them all grouped as a single undo-able action.

Source: Any object wanting to register an action for undo.

Destination: GenProcessClass only.

Interception: In general, should not be intercepted.

Parameters: title The null-terminated title of this action. If NULL, then the title of the undo action will be the title passed with the next UNDO_START_CHAIN.

owner
The object which owns this action.

Return: Nothing.

MSG_GEN_PROCESS_UNDO_END_CHAIN
void 	MSG_GEN_PROCESS_UNDO_END_CHAIN(
        BooleanWord 		flushChainIfEmpty);

This message notifies the process of the end of an undo-able action. Note that all this message does is decrement a count--the current undo chain is terminated when the count goes from one to zero. This allows a function to perform a number of undo-able actions and have them all grouped as a single undo-able action.

Source: Any object wanting to register an action for undo.

Destination: GenProcessClass only.

Interception: In general, should not be intercepted.

Parameters: flushChainIfEmpty
Non-zero if you want to delete the chain if it has no actions; zero if the chain is OK without actions (actions will be added later).

Return: Nothing.

MSG_GEN_PROCESS_UNDO_ABORT_CHAIN
void	MSG_GEN_PROCESS_UNDO_ABORT_CHAIN();

This message aborts the current undo chain, destroying all actions in place on the current chain, and instructs the undo mechanism to ignore any undo information until the current undo chain is ended. This latter behavior is needed because the current chain may be nested within several chains, so we must ignore undo chain actions until the outermost chain is ended.

Source: Unrestricted.

Destination: GenProcess object.

Interception: Do not intercept.

MSG_GEN_PROCESS_UNDO_ADD_ACTION
VMChain 	MSG_GEN_PROCESS_UNDO_ADD_ACTION(
        AddUndoActionStruct 	*data);

This message adds a new undo action to the current undo chain.

Source: Any object wanting to register an action for undo.

Destination: GenProcessClass .

Interception: In general, should not be intercepted.

Parameters: data Structure containing information which may be used to undo action.

Return: Will return NULL if we are ignoring undo messages.
If the value passed in UAS_datatype was UADT_PTR or UADT_VMCHAIN, then will return a VMChain or DBItem which may be used to undo the action. If neither of the above cases is true, return value is meaningless.

MSG_GEN_PROCESS_UNDO_GET_FILE
VMFileHandle MSG_GEN_PROCESS_UNDO_GET_FILE();

This message returns a VM file handle to store undo actions. This message is useful to access undo data in either a huge array or DB item. You may also use GenProcessUndoGetFile() to retrieve this file instead.

Source: Any object wanting to access the undo file.

Interception: Should not be intercepted.

Parameters: None.

Return: File handle of VM file with undo information.

MSG_GEN_PROCESS_UNDO_FLUSH_ACTIONS
void 	MSG_GEN_PROCESS_UNDO_FLUSH_ACTIONS();

This message flushes the current undo chain (frees all undo actions, notifies edit control that there is no undo item).

Source: Any object using undo.

Interception: Should not be intercepted.

Parameters: None.

Return: Nothing.

MSG_GEN_PROCESS_UNDO_SET_CONTEXT
dword 	MSG_GEN_PROCESS_UNDO_SET_CONTEXT(
        dword 	context);

This message sets the current undo context. This allows the application to have separate undo chains associated with various documents or modes. This should be sent out before any other undo related messages. The document control automatically takes care of this when a document gets the model exclusive.

Passing NULL_UNDO_CONTEXT as the new context will trigger some zealous EC code if any other undo messages are sent while the context is null.

Source: Any object using undo.

Interception: Generally, should not be intercepted. Applications wanting to override the default behavior should at least flush out the current undo actions, as they will probably not be valid in the new context.

Parameters: context New context (this has no meaning to the undo mechanism--it's just a value).

Return: Old context.

Structures:

#define NULL_UNDO_CONTEXT 0
MSG_GEN_PROCESS_UNDO_GET_CONTEXT
dword 	MSG_GEN_PROCESS_UNDO_GET_CONTEXT();

This message gets the current undo context.

Source: Any object using undo.

Interception: Generally, should not be intercepted.

Parameters: None.

Return: Current context.

MSG_GEN_PROCESS_UNDO_PLAYBACK_CHAIN
void 	MSG_GEN_PROCESS_UNDO_PLAYBACK_CHAIN();

This message plays back the current undo chain, one action at a time. It simultaneously creates a "redo" chain, so the undone action can be redone.

Source: Edit control.

Interception: Generally, should not be intercepted.

Parameters: None.

Return: Nothing.

MSG_GEN_PROCESS_UNDO_IGNORE_ACTIONS
void 	MSG_GEN_PROCESS_UNDO_IGNORE_ACTIONS(
        Boolean		flushActions);

This message causes a process to reject any undo messages.

Source: Edit control.

Parameters: flushActions true to flush the queue.

Interception: Generally, should not be intercepted.

MSG_GEN_PROCESS_UNDO_ACCEPT_ACTIONS
void 	MSG_GEN_PROCESS_UNDO_ACCEPT_ACTIONS();

This message causes a process to accept undo messages again.

Source: Edit control.

Interception: Generally, should not be intercepted.

MSG_GEN_PROCESS_UNDO_CHECK_IF_IGNORING
Boolean 	MSG_GEN_PROCESS_UNDO_CHECK_IF_IGNORING();

This message checks to see if the system is currently ignoring undo actions.

Source: Edit control.

Interception: Generally, should not be intercepted.

Parameters: None.

Return: Will return true if ignoring actions.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Input

Nearly all applications will in some way handle user input. User input can come from a keyboard, a mouse, or a stylus. (Other types of input are generally considered data input.) For the most part, GEOS hides the actual mechanics of input from the application; at the same time, it allows extreme flexibility of input handling.

Input to generic UI objects is handled by these objects--unless you have very specific needs and will subclass a generic gadget, you can leave its input handling up to GEOS. The same is true with most specialized system-provided objects such as the GrObj, the VisText, the Spreadsheet, and others. Other objects (such as basic visible objects you subclass yourself) will need more attention depending on the behavior you want them to have.

Read this chapter if your application will handle user input. Before reading this chapter, however, you should be relatively familiar with the GEOS system architecture, Goc syntax, and the GEOS object model and message passing. It is also suggested that you spend some time programming with generic and visible objects and getting used to the rest of the system before handling input events.

1 Input Flow
1.1 Devices and Drivers
1.2 Input Manager and GenSystem
1.3 Input Events
1.4 Input Hierarchies
2 Mouse Input
2.1 Mouse Events
2.2 Gaining the Mouse Grab
2.3 Large Mouse Events
2.4 Setting the Pointer Image
3 Keyboard Input
3.1 Keyboard Input Flow
3.2 Keyboard Events
4 Pen Input and Ink
4.1 Ink Data Structures
4.2 Ink Input Flow
5 Input Hierarchies
5.1 The Three Hierarchies
5.2 Common Hierarchy Basics
5.3 Using Focus
5.4 Using Target
5.5 Using Model
5.6 Extending the Hierarchies

Input: 1 Input Flow

User input must flow in an orderly way from the user's device to the proper GEOS object. In a multithreaded environment, just determining the appropriate input recipient can be a monumental task.

GEOS uses several layers of indirection to process user input and to distribute it to its proper destination so applications don't have to. The scheme used not only isolates applications from dealing with devices directly, but it provides convenient and easy ways to control input flow, both within an application and throughout the entire system.

The figure below shows the flow of input from the user's device to its destination. User input is managed primarily by device drivers acting in concert with the UI, and several threads are involved in the input flow. For example, the kernel provides a thread solely for input management that has a high priority; this insures that the pointer image always moves immediately with the mouse.

Input is registered by a device such as a keyboard, stylus, or mouse. The device sends signals to GEOS device drivers, which take the signals and translate them into raw input messages the UI understands. These raw messages are translated by the UI's Input Manager, which refines the events even further. The Input Manager then sends the events on to the topmost UI object in the system, the GenSystem object.

The GenSystem examines the events and their contexts and determines which window or application should receive them. Typically, events you will be interested in will flow from the GenSystem through your GenApplication object, its GenPrimary window, and finally through the appropriate GenView or GenDisplay. These window objects will pass the event on to a visible tree (through a VisContent), which will handle the events.

It is highly unlikely you will ever need to know anything about an input event before it gets to your GenApplication object. Typically, you will only handle mouse, pen, or Keyboard events in custom visible objects and can ignore them in all other cases.

Input events take the form of standard GEOS messages. These messages are defined by the UI but are actually part of MetaClass (they are allocated in MetaClass and exported to the UI). The reference entries for these events can therefore be found under MetaClass

Input events give information about the type of event (e.g., a mouse event), the action taking place (e.g., the right mouse button was pressed), and additional information (e.g., the location of the mouse at the time). Each device has its own set of events--for example, keyboard presses induce MSG_META_KBD_CHAR , and mouse moves induce MSG_META_MOUSE_PTR .

Devices and Drivers

Input Manager and GenSystem

Input Events

Input Hierarchies

Input: 1.1 Input Flow: Devices and Drivers

GEOS supports a number of input devices through the use of device drivers. The basic GEOS system software supports mouse, keyboard, and stylus input. Other input devices may be supported by the system software in the future by the addition of new device drivers. Other devices may provide data input in one form or another--a scanner, for example--but these devices are not considered user input devices. These are considered data input devices and do not generate input events through the Input Manager.

The use of device drivers isolates your application and libraries from the specifics of the user's machine. GEOS includes drivers for most mice and keyboards (pen input uses a derivative of the mouse drivers), and drivers for new hardware may be added without requiring changes to your application.

A device driver takes raw input signals from the input device and translates it into raw input events that the Input Manager will understand. Your objects will never receive these events; they are only sent to the Input Manager for translation into pointer, button, or Keyboard events.

Input: 1.2 Input Flow: Input Manager and GenSystem

The Input Manager is part of the User Interface and primarily assesses input events in the context of the system. It takes a raw input event from the device driver, passes it through various pre-set monitors (for the UI), and then adds contextual information if necessary. The Input Manager passes the refined input event on to the topmost system object, the GenSystem object.

The GenSystem object determines where the event should be passed. Typically, it will send the event on to whichever application is designated as the active application.

Input: 1.3 Input Flow: Input Events

GEOS uses three basic types of input events. These three types make up all the events necessary for mouse, keyboard, and pen input. They are Pointer events , Button events , and Keyboard events .

Pointer and Button events deal with mouse and pen input. Keyboard events provide keyboard key press information. Pointer and Button events are often referred to as "mouse" events and are dealt with together because most objects interested in one type will also be interested in the other.

A Pointer event details motion of the mouse pointer. If the user moves the mouse, a MSG_META_MOUSE_PTR will be generated by the Input Manager and sent to the proper application. The type of Pointer event (not always MSG_META_MOUSE_PTR ) is affected by the current keyboard state, mouse state, and other context information.

A Button event details the press or release of a particular mouse button. Because different Specific UIs have different meanings for each mouse button, the function of the button determines which message is sent out. In the OSF/Motif specific UI, the left mouse button is used for selection; therefore, presses on that button cause MSG_META_START_SELECT , and releases cause MSG_META_END_SELECT . The exact message sent out, like Pointer events, can also be affected by input state information.

Mouse events also carry with them several flags indicating various statistics about the event: whether the click is actually a double-click, whether any keys on the keyboard are being held down, etc.

A Keyboard event details the press or release of a particular key on the user's keyboard. Usually, objects interested in Keyboard events will only be interested in presses and can ignore releases. MSG_META_KBD_CHAR is the basic Keyboard event, and it carries flags detailing the state of the various modifier keys (e.g., Shift, Ctrl, Alt, NumLock). This message is always sent to the current focus.

Pen input comes in the form of a special data structure called Ink . Ink input is instigated in the same manner as Button events, but it is handled in a completely separate manner. An object expecting Ink input must be aware when Ink events are being passed as opposed to normal mouse events. Special flags are used for this; see Pen Input and Ink .

Input: 1.4 Input Flow: Input Hierarchies

Because the entire User Interface of GEOS is implemented as a hierarchy (tree) of objects, input messages naturally flow from the topmost object in the tree down to the object that should receive the input. At each level in the tree, the object decides whether to handle the event or pass it on to one of its children. Typically, the event will be passed all the way down the tree to a leaf node, where it will be handled or ignored.

To simplify and shorten this process, the UI maintains several special hierarchical input flow channels where special input should always flow. These hierarchies include the focus , the target , and the model . All three are described in full in Input Hierarchies .

The Focus is the object that should receive all Keyboard events. For example, a modal dialog box always has the focus; if the user presses Enter, the dialog box will take its default action. A text object into which the user is typing has the focus as long as the user does not click outside its bounds.

The Target is what is selected by the user: the object on which actions should be performed. Controller objects (of GenControlClass ) typically operate on the target. An example is a text object with selected text. When you set the text's style using a menu item (a style control object), that menu item sends its message to the current target object. The text object, set as the current Target, receives the message and sets the style of the selection.

An object is said to "have the focus" or "have the target" if it is the object specified by the hierarchy to receive that type of input. The system provides special travel options to allow you to send messages directly to the focus or target without knowing in advance which object is the destination.

The Model represents the non-visible model of the application's data. For example, a secondary selection in a database will not be acted on like the normal target, but it may still need to have similar properties. The Model hierarchy can be used to maintain this type of secondary or non-visible selection.

Input: 2 Mouse Input

GEOS is built around the user's use of a mouse for most input. Although the system has extensive keyboard navigation capabilities, the mouse is still the preferred input tool for a GUI. Therefore, most applications will in some way accept and handle mouse input directly.

Typically, you will handle mouse input if you are using visible objects ( VisClass , etc.) that will react to the mouse. If you are using only generic objects in your application, you will not need to handle mouse events.

Mouse input comes in the form of pointer and Button events. Each event describes the mouse's location, the type of event (move, button press, etc.), whether any buttons on the mouse are pressed, and whether any modifier keys on the keyboard are pressed.

Objects can "grab" the mouse exclusive, taking all mouse events for themselves. This is useful when one object needs to get all mouse input for a period of time; when it no longer needs all mouse input, it releases the exclusive. Mouse events are dispatched directly to whichever object has the "active grab" (the exclusive).

If no object has the active grab, the event will be passed to the application under the pointer. The application will pass the event to the window or GenView under the pointer, and the window or view will pass it to its content. If the view displays a visible tree, the event will eventually reach the visible object under the mouse pointer, where it should be handled. Thus, the object under the mouse pointer is said to have the "implied grab."

The default behavior of the VisContent (passing the event to its first child with the appropriate bounds) is adequate for most input needs. If you are using large documents or visible layers, you will have to subclass the VisContent to handle these special cases.

Mouse Events

Gaining the Mouse Grab

Large Mouse Events

Setting the Pointer Image

Input: 2.1 Mouse Input: Mouse Events

Each time the user moves the mouse or clicks a mouse button, GEOS generates a mouse event and passes it to the proper object. Mouse events are actually MetaClass messages that any object may intercept. Default handlers for these messages typically do nothing, so if you do not handle a particular event, it will likely be ignored.

GEOS uses three basic types of mouse events: button, pointer, and drag events. Button events indicate when the user has either pressed or released any mouse button (double-clicks are considered Button events). Pointer events indicate that the mouse has been moved. Drag events indicate the user has clicked and dragged the mouse. All of these types pass information about the mouse context and the operations currently in progress.

The Input Manager checks both the button state and the mouse movement before deciding on an event's type. For example, ifthe user clicks twice quickly, the Input Manager decides whether the user intended a double-click or a click-move-click. Objects receiving the events do not have to differentiate between the two; the events automatically contain the required information.

Pointer events are fairly straightforward; when the mouse is moved, the Input Manager generates the proper MSG_META_MOUSE_PTR indicating the move. This message also indicates the state of the mouse buttons and keyboard modifier keys.

Button events are quite a bit more complex. With each button press or release, the Input Manager generates a MSG_META_MOUSE_BUTTON which gets translated into the proper press/release/drag event before being passed on to the application. The final message generated depends on which button was pressed, whether large graphics coordinates are being used, and whether the event is being sent pre-passive or post-passive.

Among Button events, there are two basic types: the press and the release. All press events are of the form MSG_META_START_... , and all release events are of the form MSG_META_END_... . The last portion of the message name is the function of the button pressed or released. The various buttons are referred to by their meaning to the Specific UI, as follows:

SELECT
The button used for making selections. In OSF/Motif and many other Specific UIs, this is the left mouse button.
MOVE_COPY
The button used for quick-copies and quick-moves of data. In OSF/Motif, this is the right mouse button. For single-button mice, a key sequence plus a mouse click is often used.
FEATURES
The button used to bring up a "features" pop-up menu or dialog box. In some Specific UIs, this is the middle or the right mouse button. For single- or two-button mice, a key sequence plus a mouse click is often used.
OTHER
Any button not designated one of the three categories above. This category can also be used to indicate when the user presses more than one button at a time.

A press event indicates the user pressed down on the particular button. A release event indicates the user released the button. If the user presses a button and moves the mouse, a drag event will be sent after the initial press event. If the user double-clicks, a special flag will be sent with a single press event; it is up to the application to handle double-clicks differently.

Drag events are of the form MSG_META_DRAG_... , similar to Button events. Each of the above button types has a corresponding drag message. If the user presses a mouse button and quickly moves the mouse more than a specified distance, or if he holds a particular mouse button down more than a specified time, the Input Manager will send a drag event after the press event. A single release event signifies the user released the mouse button, just as with normal presses.

There is also a complete set of events used for large documents. If an object has large bounds, or if a GenView is set up to display a large content, large mouse events will be generated instead of normal mouse events. Large events take the form MSG_META_LARGE_... . For example, the large version of MSG_META_MOUSE_PTR is MSG_META_LARGE_PTR .

Below are listed all the standard mouse events your objects may be interested in handling. Most objects will be interested in only a small subset of these.

MSG_META_MOUSE_PTR
The standard Pointer event, generated whenever the mouse moves without a button down.
MSG_META_START_SELECT
Generated when the user presses the select button.
MSG_META_END_SELECT
Generated when the user releases the select button.
MSG_META_START_MOVE_COPY
Generated when the user presses the move/copy button.
MSG_META_END_MOVE_COPY
Generated when the user releases the move/copy button.
MSG_META_START_FEATURES
Generated when the user presses the features button.
MSG_META_END_FEATURES
Generated when the user releases the features button.
MSG_META_START_OTHER
Generated when the user presses a button combination not recognized as select, move/copy, or features.
MSG_META_END_OTHER
Generated at the release of the combination that generated the MSG_META_START_OTHER .
MSG_META_DRAG_SELECT
Generated between a press and release of the select button if the user holds the button down or if the user moves the mouse.
MSG_META_DRAG_MOVE_COPY
Generated between a press and release of the move/copy button if the user holds the button down or if the user moves the mouse.
MSG_META_DRAG_FEATURES
Generated between a press and release of the features button if the user holds the button down or if the user moves the mouse.
MSG_META_DRAG_OTHER
Generated between a press and release of a button not specified above if the user holds it down or if the user moves the mouse.

Listed below are the large equivalents of the above messages.

MSG_META_LARGE_PTR
MSG_META_LARGE_START_SELECT
MSG_META_LARGE_END_SELECT
MSG_META_LARGE_START_MOVE_COPY
MSG_META_LARGE_END_MOVE_COPY
MSG_META_LARGE_START_FEATURES
MSG_META_LARGE_END_FEATURES
MSG_META_LARGE_START_OTHER
MSG_META_LARGE_END_OTHER
MSG_META_LARGE_DRAG_SELECT
MSG_META_LARGE_DRAG_MOVE_COPY
MSG_META_LARGE_DRAG_FEATURES
MSG_META_LARGE_DRAG_OTHER

All the normal (as opposed to large) mouse events pass and return the same values. Each event differs based on the message itself; an object knows that MSG_META_START_SELECT is inherently different from MSG_META_START_MOVE_COPY , even though they may pass the exact same values.

Structure of Mouse Events

Each mouse event passes three items of data and one pointer to a return structure. The three parameters are listed below; the fourth, the return structure, is detailed in the next section.

xPosition
The horizontal position of the pointer in document coordinates in the window when the event was generated.
yPosition
The vertical position of the pointer in document coordinates in the window when the event was generated.
inputState
A word of flags indicating the state of the mouse buttons and the UI functions (such as quick-transfer) that were active when the event was generated.

The first two indicate the position of the mouse in the document. The third, inputState , consists of two bytes of flags. The first byte indicates the type of Button event and the state of the mouse buttons during the event. It is a record of type ButtonInfo and has seven flags:

BI_PRESS
Set if this is a press event rather than a release or drag.
BI_DOUBLE_PRESS
Set if this is actually a double-press (GEOS automatically detects double presses).
BI_B3_DOWN
Set if button number three is being held down.
BI_B2_DOWN
Set if button number two is being held down.
BI_B1_DOWN
Set if button number one is being held down.
BI_B0_DOWN
Set if button number zero is being held down.
BI_BUTTON
Set if this is a Button event, clear if a Pointer event.

The second byte of inputState is a record of UIFunctionsActive , which describes which of several UI functions are currently underway. The flags set in this byte are used primarily by the UI, and you will probably not have to check them. The flags allowed, however, are listed below.

UIFA_SELECT
Set if the basic mouse function is underway.
UIFA_MOVE_COPY
Set if a move/copy (quick-transfer) operation is underway.
UIFA_FEATURES
Set if the features popup menu or dialog is open.
UIFA_CONSTRAIN
Set if a modifier key set as a "constraint" key is pressed (e.g., the user holds the Shift key while grabbing an object control point).
UIFA_ADJUST
This is the same bit as UIFA_MOVE and UIFA_POPUP, below. When UIFA_SELECT is also set, this flag indicates that the select event should act as a toggle event.
UIFA_EXTEND
This is the same bit as UIFA_COPY and UIFA_PAN, below. When UIFA_SELECT is also set, this flag indicates that the select event should extend any selection already made (add the area/objects to the current selection).
UIFA_MOVE
This is the same bit as UIFA_ADJUST and UIFA_POPUP. When UIFA_MOVE_COPY is also set, this flag indicates that the operation should be considered a move rather than a copy.
UIFA_COPY
This is the same bit as UIFA_EXTEND and UIFA_PAN. When UIFA_MOVE_COPY is also set, this flag indicates the operation should be considered a copy rather than a move.
UIFA_POPUP
This is the same bit as UIFA_ADJUST and UIFA_MOVE, above. When UIFA_FEATURES is also set, this flag indicates the "features" button brings up a popup menu or dialog box.
UIFA_PAN
This is the same bit as UIFA_EXTEND and UIFA_COPY, above. When UIFA_FEATURES is also set, this flag indicates the "features" button has initiated pan-style scrolling.

Return Values for Mouse Events

One of the parameters of every mouse event is a pointer to a MouseReturnParams structure. This structure is passed empty; it is up to the event handler to fill it with the proper return values. The MouseReturnParams structure's definition is given below:

typedef struct {
    word                unused;  /* for alignment */
    MouseReturnFlags    flags;
    optr                ptrImage;
} MouseReturnParams;

Every time you handle a mouse event, you must fill in the flags field. This describes how the event was handled so the UI knows whether to pass it on to another object, change the pointer image, or treat the event as having been handled. The flags you can return are

MRF_PROCESSED
The event was handled. If you do not set this flag upon return, the window will think the event was not handled and may pass it on to another child.
MRF_REPLAY
The event may or may not have been handled, but it should be rebroadcast as if it had not been. This is used primarily when an object gives up the mouse grab because the mouse has strayed outside its bounds.
MRF_PREVENT_PASS_THROUGH
The event should not be passed through to the active or implied grab object. This is set only by pre-passive handlers which want to filter the event and keep the active or implied grabs from receiving it.
MRF_SET_POINTER_IMAGE
The pointer should be set to a new image. If this flag is returned, you must also return an optr in the ptrImage field (see below).
MRF_CLEAR_POINTER_IMAGE
The pointer image will be reset to the default. See Setting the Pointer Image .

If you return MRF_SET_POINTER_IMAGE, you must specify the new image to be set. An object may want to set the pointer to a new image when it is over the object's bounds or during a drag operation. For example, if the user selects a drawing tool, the tool might set the pointer to cross-hairs instead of the default arrow.

To set the pointer's image, you must return the handle and chunk handle of a chunk containing a PointerDef structure. Return it in the form of an optr in the ptrImage field of the return structure. For full information on PointerDef and defining pointer images, see Setting the Pointer Image .

If you are not setting the pointer image, return a NullOptr in ptrImage . You do not have to return anything in the unused field.

Input: 2.2 Mouse Input: Gaining the Mouse Grab

When no object has the active mouse grab, mouse events are passed to whichever object is directly under the pointer's image. If any object has the grab, however, mouse events will be passed only to the object with the grab, no matter where the pointer is on the screen.

When the user clicks inside a view, the GenView automatically grabs the mouse while the user holds the mouse button down. This allows the view to know when drag scrolling takes place (the user drags the pointer outside the view's bounds, but the mouse event is still passed to the GenView). Process objects acting as a GenView's content therefore do not have to worry about grabbing the mouse. If you are displaying visible objects within the GenView, however, your objects will likely want to grab the mouse for themselves.

Typically, a visible object will grab the mouse on a MSG_META_START_... event and release it on a MSG_META_END_... event. To grab the mouse, a visible object has to send itself the message MSG_VIS_GRAB_MOUSE ; to release the grab, it must send itself MSG_VIS_RELEASE_MOUSE .

The TicTac sample application shows an example of visible objects grabbing the mouse and handling several mouse events. This sample application is described in detail in the UI Overview.

There are two other types of mouse grabs besides the active grab. With very rare exceptions, application programmers can ignore these. They are the pre-passive and post-passive grabs.

If an object has a pre-passive grab, it will receive copies of all Button events before the events are passed on to their true destinations. Pre-passive grabs are used by window objects to bring themselves to the front when the user clicks in them. Any number of objects may have pre-passive grabs. To gain a pre-passive grab, a visible object must send itself MSG_VIS_ADD_BUTTON_PRE_PASSIVE ; to release it, the object must send itself MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE .

If an object has a post-passive grab, it will receive copies of all Button events after the events have been handled. Any number of objects may have post-passive grabs. To gain a post-passive grab, a visible object must send itself MSG_VIS_ADD_BUTTON_POST_PASSIVE ; to release the grab, the object must send itself MSG_VIS_REMOVE_BUTTON_POST_PASSIVE .

GEOS uses different messages to indicate that the event is a pre-passive or a post-passive event. All the normal Button events have pre-passive and post-passive counterparts which take the form MSG_META_PRE_PASSIVE_... and MSG_META_POST_PASSIVE_... . For example, the pre-passive version of MSG_META_START_SELECT is MSG_META_PRE_PASSIVE_START_SELECT .

Below are listed the pre-passive equivalents of the above messages. Note that the drag events and large events do not have pre-passive equivalents.

MSG_META_PRE_PASSIVE_START_SELECT
MSG_META_PRE_PASSIVE_END_SELECT
MSG_META_PRE_PASSIVE_START_MOVE_COPY
MSG_META_PRE_PASSIVE_END_MOVE_COPY
MSG_META_PRE_PASSIVE_START_FEATURES
MSG_META_PRE_PASSIVE_END_FEATURES
MSG_META_PRE_PASSIVE_START_OTHER
MSG_META_PRE_PASSIVE_END_OTHER

Listed below are the post-passive equivalents of the above messages. Note that the drag events and large events do not have post-passive equivalents.

MSG_META_POST_PASSIVE_START_SELECT
MSG_META_POST_PASSIVE_END_SELECT
MSG_META_POST_PASSIVE_START_ADJUST
MSG_META_POST_PASSIVE_END_MOVE_COPY
MSG_META_POST_PASSIVE_START_FEATURES
MSG_META_POST_PASSIVE_END_FEATURES
MSG_META_POST_PASSIVE_START_OTHER
MSG_META_POST_PASSIVE_END_OTHER

Input: 2.3 Mouse Input: Large Mouse Events

Applications and visible objects that use large documents (32-bit coordinate spaces) receive special versions of the mouse input messages. These special messages pass the pointer position differently; otherwise, they pass the same flags as passed by their normal counterparts.

Large mouse messages take the form MSG_META_LARGE_... . There is one large mouse event for each of the normal pointer, button, and drag events; note, however, that no large events exist for pre-passive or post-passive grabs. For the full list of large mouse events, see MSG_META_LARGE_PTR MSG_META_LARGE_START_SELECT MSG_META_LARGE_END_SELECT MSG_META_LARGE_START_MOVE_COPY MSG_META_LARGE_END_MOVE_COPY MSG_META_LARGE_START_FEATURES MSG_META_LARGE_END_FEATURES MSG_META_LARGE_START_OTHER MSG_META_LARGE_END_OTHER MS .

One special note about large events: Any object that handles large mouse events should also be prepared to handle normal mouse events. The visible parent of the large object can only send out one type (large or normal) of event at a time to its children; if it has any normal children at all, it will send out the normal event. If the large object should handle the normal event and doesn't, the event will be lost or passed on to another child.

For full information on handling large mouse events within a visible object tree, see the VisClass chapter. (This information also applies to Process objects acting as the contents of GenViews.)

Input: 2.4 Mouse Input: Setting the Pointer Image

The GEOS User Interface uses several default settings for the mouse pointer, all defined by the Specific UI.

Some applications will want to modify the mouse pointer's image in order to provide certain feedback to the user. For example, a chess game may set the pointer to an image of the piece being moved and then reset it to an arrow after the move is completed; a graphics application could set the pointer to an image of whatever tool is currently in use. Another time an application may change the mouse pointer is when a quick-transfer is in progress.

Defining the Pointer Image

The pointer is defined as a bitmap 16 pixels on each side. It has a "hot spot" of five pixels that acts as the active point of the image. When the user clicks, the area affected is the hot spot, not the entire image.

The pointer is stored in a PointerDef16 structure. This structure defines the image bitmap, the horizontal and vertical offsets to the hot spot, and the definition of how the image should mix with the background. This definition is known as the pointer's mask.

The definition of PointerDef16 is given below. When setting the pointer image, you will typically pass an optr (a combination handle and chunk handle) to a PointerDef16 structure; the structure can be set up in a sharable chunk beforehand, or it can be set in a local or global variable. Typically, you will set the entire pointer image in a resource chunk and compile it into your geode; this is much easier and faster to use than building it at run-time.

typedef struct {
    byte	   PD_width;
    byte	   PD_height;
    sbyte	   PD_hotX;
    sbyte	   PD_hotY;
    byte	   PD_mask[STANDARD_CURSOR_IMAGE_SIZE];
    byte	   PD_image[STANDARD_CURSOR_IMAGE_SIZE];
} PointerDef16;

The PD_hotX field defines the horizontal offset from the upper-left corner of the image to the center of the hot spot. Similarly, PD_hotY defines the vertical offset from the upper-left corner to the hot spot. Both are in pixels and must be less than 16 (since the image is only 16 pixels in each dimension).

The PD_mask field contains the 16 by 16 bitmap of the mask, while PD_image holds the bitmap of the image. Both are stored as 32-byte arrays, two bytes per row. For example, if the first line of the image was all black and the second all white, the first four bytes of PD_image would be 0xFF, 0xFF, 0x00, and 0x00.

Note that the block the pointer chunk is stored in must be declared sharable.

Setting the Pointer Image

You can set the pointer's image in three ways. First, you can send a message to the GenView to set the pointer image whenever the pointer is over the view's window (see the GenView chapter). Second, you can set the pointer to a default image type during a quick-transfer operation (see the Clipboard chapter). Third, you can set the pointer image after handling a mouse event. Only the third option is discussed here.

An object may wish to set the pointer image while it is selected, while the pointer is over its bounds, or in any number of other situations. For example, a chess piece currently being moved may set the pointer's image to the shape of the piece so the user knows exactly what type of piece is being moved.

Every mouse event you handle must return a MouseReturnParams structure, defined in Return Values for Mouse Events . Make sure to return the following to set the pointer image:

  • MRF_SET_POINTER_IMAGE in flags
     When the UI sees this flag in the returned structure, it looks for the address of a pointer definition in the ptrImage field. If you do not return this flag, the UI will not try to set the pointer.
  • The optr of the pointer image in ptrImage
     This field gives the handle and chunk handle of the PointerDef structure that defines the pointer image to be set. The structure must be in a sharable block; most likely you will have it set in a geode resource.

Input: 3 Keyboard Input

Keyboard input is scanned by a device driver and passed on to the Input Manager, just as mouse input is. The keyboard driver and Input Manager check the state of the modifier keys (Shift, Ctrl, Alt, Num Lock, etc.) and parse each keypress as much as possible based on its context. Each keypress is called a Keyboard event and is passed in MSG_META_KBD_CHAR .

Keyboard Input Flow

Keyboard Events

Input: 3.1 Keyboard Input: Keyboard Input Flow

Each time the user presses a character (with or without modifier keys) on the keyboard, the Input Manager generates a single Keyboard event and passes it to the GenSystem object. The GenSystem object delivers the event to all the proper objects based on passive grabs (if any) and the current focus hierarchy.

As with mouse events, all Keyboard events are passed down the object hierarchy from the GenSystem object through an application's GenApplication object and on down to a leaf object. When handling keyboard input, you can choose to intercept the events at the GenApplication level or at any node below it where the event should be handled.

Unlike with mouse input, there is no "implied grab" for keyboard input. Keyboard input is always passed to the object that has the focus (for information on the Focus hierarchy, see Using Focus ). Therefore the focus is the keyboard's "active grab."

Objects can, however, set up passive grabs as they can with mouse input. A pre-passive grab allows the visible object to receive Keyboard events before the focus object gets them. A post-passive grab delivers events to the visible object after they have been sent to the focus. For more information about gaining and releasing the passive keyboard grabs, see the VisClass chapter.

If you intercept Keyboard events, you must be sure to return any events that you don't use. For example, if you only want to detect when the user hits the "a" key, you must return all keypresses that are not "a." Otherwise, your MSG_META_KBD_CHAR handler will eat all keypresses, and keyboard menu navigation will not work. If intercepting Keyboard events in a generic object, you can simply call the superclass for each unused character. If intercepting Keyboard events in a visible object, you must send MSG_META_FUP_KBD_CHAR for each unused character.

To ensure that a particular object (for example, a specific GenText object) automatically will receive keyboard input, you must set HINT_DEFAULT_FOCUS for that object and all its parents up to the GenPrimary containing it. Otherwise, the generic UI does not know which object should gain the default focus.

Input: 3.2 Keyboard Input: Keyboard Events

Each Keyboard event your application and objects receive has gone through preliminary parsing by the keyboard driver. Keyboard drivers are intelligent enough to know several things about different types of keystrokes depending on the character set and the modifier keys held down.

All Keyboard events pass two sets of information: the current character as interpreted by the keyboard driver, and the actual scan code of the character pressed. This allows you to do additional parsing or to ignore any extraneous information about the keypress that may have been included by the driver.

For example, if the user presses Ctrl-1 , the keyboard driver passes on the character "1" with a flag indicating the Ctrl key was pressed. If the user presses Shift-1 , the keyboard driver passes on the "!" character without a flag indicating the Shift key is pressed (the Shift key is eaten by the keyboard driver); in this case, it also passes on the scan code of the "1" key. If the user then presses Ctrl-Shift-1 , different keyboard drivers may pass different characters. Whether the driver passes "1" or "!," however, the scan code for the "1" key will also be passed.

The keyboard driver also understands special "extended" keypresses and "temporary accents". Some keyboard drivers may use two keystrokes to specify special characters; for example, some keyboard drivers may require the ö character to be entered as "Ctrl-q o" (this is called an extended keypress), and some may require it to be entered as two separate keys: the "o" and the umlaut (this is called a temporary accent).

In addition to the actual character and the scan code, every Keyboard event gives flags indicating the state of the modifier keys (Ctrl, Alt, Shift), the state of the toggle keys (Caps Lock, Num Lock, etc.), and what type of event it is (first press, repeated press, release, or first press of an extended sequence).

Standard Keyboard events come in MSG_META_KBD_CHAR . This message has the same parameters and return values as its pre-passive and post-passive counterparts, MSG_META_PRE_PASSIVE_KBD_CHAR and MSG_META_POST_PASSIVE_KBD_CHAR . The parameters are listed below:

character
The character value determined by the keyboard driver and Input Manager.
flags
A word value: The high byte is a record of ShiftState detailing the modifier keys pressed, and the low byte is a record of CharFlags giving information about the type of character passed. Both of these records are detailed below.
state
A word value: The high byte is the scan code of the key pressed (without modifiers), and the low byte is a record of ToggleState detailing the state of the toggle keys. ToggleState is detailed below.

Three different records of flags define the Keyboard event. The ShiftState record describes which modifier keys are pressed and has the following flags:

SS_LALT
The left Alt key is pressed.
SS_RALT
The right Alt key is pressed.
SS_LCTRL
The left Ctrl key is pressed.
SS_RCTRL
The right Ctrl key is pressed.
SS_LSHIFT
The left Shift key is pressed.
SS_RSHIFT
The right Shift key is pressed.
SS_FIRE_BUTTON_1
The first joystick-style "fire button" (if any) is pressed.
SS_FIRE_BUTTON_2
The second joystick-style "fire button" (if any) is pressed.

The ToggleState record describes which toggle keys are currently active. It has the following flags.

TS_CAPSLOCK
The Caps Lock is set.
TS_NUMLOCK
The Num Lock is set.
TS_SCROLLLOCK
The Scroll Lock is set.

The CharFlags record contains several flags indicating whether this event is a first press, a release, a repeat press, or an extended or temporary character. Its flags are listed below.

CF_STATE_KEY
Set if either a ShiftState key or a ToggleState key is being pressed along with the passed character.
CF_EXTENDED
Set if this event is part of an extended keystroke. This flag is generally only used by the keyboard driver during its parsing; you will not have to use it. (Extended keypresses are passed by the Input Manager to your application as a single event with the resultant character, not as two events.)
CF_TEMP_ACCENT
Set if this event is part of a temporary accent keystroke. Temporary accents are used only by the keyboard driver, like extended keypresses above.
CF_FIRST_PRESS
Set if the event represents the user's first press of the key. This is akin to a button press event and will, at some point, be followed by a CF_RELEASE event.
CF_REPEAT_PRESS
Set if the event is generated by the user holding down the key (as opposed to a first press, above). If you want the user to hit the key for each individual character, you should ignore this type of event.
CF_RELEASE
Set if the user lets up on a key. Applications typically are not interested in these events for text, and they can automatically be ignored by setting GVA_DONT_SEND_KBD_RELEASES in your GenView's instance data (this only affects handling Keyboard events in visible objects).

A sample handler for MSG_META_KBD_CHAR is shown in Sample MSG_META_KBD_CHAR Handler . It is used by a visible object that simply increments one of its instance fields each time the greater-than key (>) is pressed.

Code Display 11-1 Sample MSG_META_KBD_CHAR Handler

/* This method is used by a visible object of MyVisObjClass. It takes all
 * occurrences of the greater-than (>) key and increments the object's MVOCI_data
 * instance field, ignoring when the user holds down the key. Also, the GenView
 * is set to pass along key releases as well as presses; the method must also
 * ignore releases.
 *
 * Note that the object will only get keyboard input when it has the focus. */
/* The format of this message is
 * void (word character, word flags, word state) */
@method MyVisObjClass, MSG_META_KBD_CHAR {
    /* First, check if the character is the one we want. */
    if (character == `>') {
    /* If it is, make sure the user is not holding the key down and
     * that this is not a release event. Check that CF_REPEAT_PRESS and
     * CF_RELEASE are not set in the low byte of the flags parameter.
     * If either is set, ignore the character and send MSG_META_FUP_KBD_CHAR
     * so the UI may provide default keyboard navigation. */
    if ((flags & CF_REPEAT_PRESS) || (flags & CF_RELEASE)) {
        @call self::MSG_META_FUP_KBD_CHAR(character, flags, state);
    } else {
        /* If we get here, we know the character is what we want. Increment
         * the instance field and return. */
        (pself->MVOCI_data)++;
    }
    /* If the character is not a greater-than, we must send it on to the UI
     * with MSG_META_FUP_KBD_CHAR. If we do not, all other keyboard presses
     * we receive will never be handled; this will cause keyboard
     * accelerators and menu navigation not to work. */
    } else {
    @call self::MSG_META_FUP_KBD_CHAR(character, flags, state);
    }
}

Input: 4 Pen Input and Ink

GEOS supports pen-based systems and applications by means of the Ink data structure formats. Ink is simply a standard way of representing raw pen input as a data structure.

At the lowest level, individual visible objects can receive Ink input directly, much like they receive mouse input normally. (In addition, the VisText object has some Ink handling and handwriting recognition built into it.)

The Pen Library exports InkClass, a visible object class which accepts and stores pen input. It stores the ink compactly. The ink object allows the pen to act as an eraser, removing part or all of a pen stroke, perhaps splitting some strokes in two. See the Pen Object Library chapter for more information.

If GEOS is running on a pen-based system, it will set a particular flag in its private data. It will also install a special input monitor to alter the events the Input Manager receives from the device driver. The Input Manager and the UI then react to input events in a different manner and do not send them directly as with mouse events.

Because Ink events are not passed on immediately by the UI, the UI actually takes care of drawing them directly to the screen. This provides the user direct, immediate feedback.

Ink Data Structures

Ink Input Flow

Input: 4.1 Pen Input and Ink: Ink Data Structures

Ink input is stored in data blocks. The Input Manager stores up Ink events into a data block and then transfers the block to the proper window or application with MSG_META_NOTIFY_WITH_DATA_BLOCK . (This is a general change notification message.) The type of the data block passed is NT_INK, the manufacturer ID is MANUFACTURER_ID_GEOWORKS.

The data block is headed by an InkHeader structure (defined below). The header is followed by a list of points, all in screen coordinates (not points as with normal graphics commands). The block will contain as many points as are registered during the Ink input sequence.

The InkHeader structure has the following format:

typedef struct {
    word       IH_count;
    Rectangle  IH_bounds;
    optr       IH_destination;
    dword      IH_reserved;
    Point      IH_data;
} InkHeader;

The fields of the structure are listed below with the information they contain.

IH_count
The number of points stored in the data block.
IH_bounds
The bounds of the Ink input on the screen (in document coordinates).
IH_destination
The destination of the Ink input. An object can use this field to see if the Ink was sent directly to it or if it received the Ink simply due to an overlap.
IH_reserved
Internal data; do not use.
IH_data
Actually a label indicating the beginning of the list of points. Following this label will be a number of Point structures, each one detailing a single Ink point.

Input: 4.2 Pen Input and Ink: Ink Input Flow

When the Input Manager receives a Button event, it checks to see if that event should be treated as Ink input. It first holds up input, putting the Button event in the holdup queue, and then it queries the appropriate application with MSG_META_QUERY_IF_PRESS_IS_INK . Typically the application will pass the message down its object tree to the appropriate visible or generic object that should initially have received the Button event.

Determining if a Press Is Ink

It is up to the application or its visible object to determine whether the input should be treated as Ink. If you don't ever handle Ink, you do not need to do anything different. If any of your objects handles Ink, however, you should set up your GenView to pass Ink events on to its content. Depending how you set up your GenView, you may or may not have to write a handler for MSG_META_QUERY_IF_PRESS_IS_INK . The context and details of this message are described below.

To set up your GenView for passing Ink input, set the proper flag in its GVI_inkType

instance field. The possible flags are detailed in full in the GenView chapter but are reviewed below for convenience.

GVIT_PRESSES_ARE_NOT_INK
Objects running under this GenView never handle Ink input. This flag is set as the default. Your objects will not have to handle MSG_META_QUERY_IF_PRESS_IS_INK or MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK .
GVIT_PRESSES_ARE_INK
Objects running under this GenView always want Ink input. Your objects will not have to handle the message MSG_META_QUERY_IF_PRESS_IS_INK , but they will have to handle MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK .
GVIT_INK_WITH_STANDARD_OVERRIDE
Objects running under this GenView want Ink, but the user can override it to give mouse events. Your objects will not have to handle MSG_META_QUERY_IF_PRESS_IS_INK , but they will have to handle MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK .
GVIT_QUERY_OUTPUT
The Objects running under this GenView want Ink input, but only under certain circumstances. Your objects will have to handle both MSG_META_QUERY_IF_PRESS_IS_INK and MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK . Note that this option may cause the UI to query across threads, something that could cause performance lags on a busy system. Therefore, whenever possible, you should set one of the other three flags.

Depending on how you set up your GenView, you may need to subclass MSG_META_QUERY_IF_PRESS_IS_INK (see above). This message is sent by the Input Manager to see if the object clicked on can handle Ink input. Objects that handle Ink should return IRV_DESIRES_INK. (There is an analogous MSG_META_LARGE_QUERY_IF_PRESS_IS_INK for objects and contents using large documents.)

If the application sends the query message to a Process object acting as a view's content, the Process object should not return a value. Instead, it should respond by sending MSG_GEN_APPLICATION_INK_QUERY_REPLY to its GenApplication object; the application object will then pass the appropriate value on to the Input Manager.

Some objects may want both Ink and mouse input; the VisText object is a good example of this--it accepts Ink for handwriting recognition and mouse events for cursor movement and text selection. GEOS uses a convention for objects that want both types of input: quick-clicks and click-and-holds in the object are mouse events, but click-and-drags are Ink events.

To use both pen and mouse input, your object should return IRV_INK_WITH_STANDARD_OVERRIDE instead of IRV_DESIRES_INK in its MSG_META_QUERY_IF_PRESS_IS_INK handler. (Or if all objects running in the same GenView want this behavior, you can simply set the GenView's GVIT_INK_WITH_STANDARD_OVERRIDE flag.)

Controlling the Ink

When an object requests Ink input by returning IRV_DESIRES_INK or IRV_INK_WITH_STANDARD_OVERRIDE, it must also specify the eventual destination of the Ink data block. Additionally, it can set any characteristics in a GState for the Ink--clipping area, drawing color, etc.

These specifics are set in an InkDestinationInfo structure set up by your MSG_META_QUERY_IF_PRESS_IS_INK handler (through a call to UserCreateInkDestinationInfo() ); the handler for the routine returns the handle of a block containing the structure. Certain default values and behavior are implemented if you return a null handle or if you set the GenView flags to avoid the query (GVIT_PRESSES_ARE_INK or GVIT_INK_WITH_STANDARD_OVERRIDE).

If you choose not to set any of these specifics, Ink will have the following default behaviors:

  • The destination of the Ink will be the object that would have received the initial click event (typically the implied mouse grab) or the object that fielded the MSG_META_QUERY_IF_PRESS_IS_INK .
  • The Ink will not be clipped; the user may draw over the entire screen, overlapping generic objects and other windows. (All the Ink will still be sent to the same destination, however.)
  • The Ink will be drawn in a standard color and brush thickness.

All of the above behavior can be changed in the InkDestinationInfo structure. The structure should be created automatically with the routine UserCreateInkDestinationInfo() ; you can then pass the block handle of this structure blindly to routines that demand an InkDestinationInfo structure. Among the four fields of the structure, the following need to be passed to UserCreateInkDestinationInfo() :

dest
The optr of the object that will receive the Ink data block after all the Ink has been collected. An object may use this field to force the destination of the Ink to be any particular object.
gs
The handle of a Graphic State (GState) containing clipping, color, or other information about how the Ink should be drawn on the screen. To use the default values, set this to zero. This will allow the user to draw Ink all over the screen. See the Graphics Environment chapter for full information on GStates.
brushSize
The thickness of the brush used when drawing the Ink on the screen. This is the same as the brush thickness for polylines; see the Drawing Shapes chapter for more information on brush thickness.
callback
Virtual fptr to a callback routine to determine whether a stroke is a gesture or not. This callback routine will be passed to ProcCallFixedOrMovable() .

How Ink Is Stored and Passed On

When the user presses the pen to the screen and the input is determined to be Ink, the Input Manager generates a mouse button press event and begins storing up the subsequent mouse events. The UI then determines whether that press event should be treated as Ink or not (the default behavior is to treat them as normal mouse events).

If the events should not be treated as Ink, they are passed on as normal mouse events. If they are to be treated as Ink, they are stored and are not passed on. Instead, the UI stores them up in a special data block until the Ink input is stopped. Ink input is stopped when the user removes the pen from the screen for a user-specified amount of time. This amount of time is specified in the GEOS.INI file in the [system] category under the key inkTimeout .

After the user finishes entering the Ink input, the Input Manager determines which object will receive the data block (it may not be the original object that received the initial press). It first queries the original object with MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK . Any object handling Ink input must subclass this message and make sure that the top Ink point is not above its upper bound; it should then return its upper bound and its optr. GEOS provides a routine that does just this; if your object will always handle Ink input, you can set this routine up as the method to be used as follows:

@method VisObjectHandlesInkReply, YourClassName,
                MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK;

If the object handles Ink, the Input Manager sends the entire data block off to the GenSystem object using MSG_META_NOTIFY_WITH_DATA_BLOCK with the identifier NT_INK. (For information on this and other notification messages, see the General Change Notification chapter.)

Input: 5 Input Hierarchies

As previously mentioned in this chapter, the Input Manager channels input events to particular objects. The objects are chosen based on the current input grabs, the position of the pointer, and the state of the application. Sometimes, though, input events must be sent to a particular object not specified by one of these means. One example is keyboard input: While an object can grab the keyboard exclusive, it should not; instead, it should make itself the focus . The object that has the focus receives all keyboard input ( MSG_META_KBD_CHAR ).

Similarly, your application or other objects may need to send messages to the object which is currently active. For example, a Text Sizes menu may need to know which of several text objects currently has an active selection. Then, when the user selects a new text size, the menu can send its message to the proper text object. The object that receives these messages is called the target object.

The menu in this case, however, should not have to explicitly name the destination of its message. The menu should be able to specify that its message should go to the current target object; the UI knows which object is the target, and it redirects the message automatically.

The GEOS UI uses three special hierarchical mechanisms to fulfill these two purposes (specialized input flow and specification of destinations). These hierarchical mechanisms are built into the basic structure of GEOS objects and are therefore universal and consistent for all applications and all objects. The three hierarchies used are the focus , the target , and the model . (There is a fourth, the controller, but it is used only internally by GEOS.) These hierarchies all function in the same manner but are used for different purposes.

The Three Hierarchies

Common Hierarchy Basics

Using Focus

Using Target

Using Model

Extending the Hierarchies

Input: 5.1 Input Hierarchies: The Three Hierarchies

As stated above, GEOS offers three hierarchies you can use for input flow and specification of message destinations. These hierarchies, with their basic purposes, are listed below.

  • Focus
    The Focus hierarchy designates which object should currently receive keyboard input. Essentially, the focus object has the active keyboard grab. The text objects (VisText and GenText) manage the focus automatically; if you use only these objects for keyboard input, you will not need to understand the focus. The Focus hierarchy is described in full in Using Focus .
  • Target
    The Target hierarchy designates the active view, window, and visible selection which may be acted upon. In most cases, the target will be an object the user has selected and now wishes to alter based on menu and dialog selections. For example, a graphic object selected becomes the target object of the application; when the user selects "Rotate" or some other menu function, the menu sends its message to the selected object via the target hierarchy. The Target hierarchy is described in full in Using Target .
  • Model
    The Model hierarchy designates the model of the data the user can manipulate. This is provided for highly complex applications that need to extend the Target hierarchy for more specific handling of selections. It extends from the GenSystem through the GenApplication and the document control objects. The Model hierarchy is described in full in Using Model .

Input: 5.2 Input Hierarchies: Common Hierarchy Basics

All three of the input hierarchies function in a similar manner. Each relies on the basic tree structure of UI objects to build a path from the topmost system UI object (GenSystem) down to a leaf node that has the hierarchical properties (e.g., the target object).

Each composite object can have exactly one child with each hierarchical property. The composite determines which of its children currently has the exclusive of the hierarchy; for example, a composite with ten children may designate the fourth child as the Target node at its level. If that child is a composite with its own children, it will select one of them as the Target node at the next level.

A message sent to any object in the path will be passed down the tree until it gets to the active target leaf, the object that actually has the system's target exclusive.

A special feature of the hierarchical structure is that inactive paths may be maintained. The inactive path is stored in the objects themselves. The topmost object in the tree, however, has decided that object on the right has the target exclusive; if later the top object decides the object on the left has the exclusive, the target will automatically be given to the leaf node specified without actually changing the object tree (just the pointer stored in the top node).

Note that the other hierarchies operate in a similar manner. The behavior described is not unique to the target hierarchy.

Special Terminology

Before reading the in-depth sections on the individual hierarchies, you should know how several terms are defined and used. These terms are listed below.

Node
Any composite in the object tree that can be part of a path for one of the hierarchies.
Exclusive
The active child of a composite node in the hierarchy is said to have the "exclusive." Only one child of any composite may have the exclusive at that level.
Level
A group of siblings, all of which can potentially be part of a hierarchical path. All the nodes in a given level will have the same parent. Only one node at each level may have the exclusive for that level.
Grab
When an object grabs a hierarchy (e.g., "grabs the focus"), it notifies its parent that it wants to gain that hierarchy's exclusive for its level. Any other node at the same level will be forced to give up the exclusive.
Active Path
The path of exclusive nodes from the top object to the leaf having the active exclusive. Only objects which have a complete active path to the top node can exhibit the property of the hierarchy (e.g., a text object in a minimized application can not have the focus because its primary window does not have the focus exclusive).
Inactive Path
Any path of exclusive nodes that is broken from the top node. If the topmost node of an inactive path is given the exclusive at its level, the entire inactive path will automatically become the active path.

Modifying the Active Path

Each of the three hierarchies has messages understood by MetaClass that alter the hierarchy's active path. The Specific UI takes care of most of the modifications to the active path, however, so simple applications will not need to bother with it.

Any node can "grab" a hierarchical exclusive by sending the grab message to its parent. (Outside agents can alter the path by sending the grab message to the child's parent on behalf of the child.) The grab forces any other node on the child's level to give up the exclusive. Any node can release the exclusive with the appropriate message (typically done when forced to release because of another node's grab).

Whenever a node gains the exclusive of a hierarchy, it will be notified via a message. This is to notify objects which already had their level's exclusive that they now have the active hierarchical exclusive. Similarly, if another node grabs the exclusive at any level equal or above an exclusive node, that node will be notified that it has lost the exclusive at its level. See the discussions of the individual hierarchies for more detail on the messages used.

Sending Classed Messages

Any object can easily send messages to the object having the active exclusive of a given hierarchy using MSG_META_SEND_CLASSED_EVENT . This message passes a recorded event (message) to the first object of a specified class encountered within the hierarchy. (If the destination should be a leaf, no class should be specified.)

To pass events to the active member of a specific hierarchy, you should use a TravelOption with MSG_META_SEND_CLASSED_EVENT . TravelOption is a type which describes the destination for the specified event. There are three types of TravelOption for each hierarchy:

  • Direct
    The "direct" travel options are TO_FOCUS, TO_TARGET, and TO_MODEL. These send the classed event down the hierarchy's active path, treating the recipient of MSG_META_SEND_CLASSED_EVENT as the active path's top node.
  • Application-direct
    The "application-direct" travel options are TO_APP_FOCUS, TO_APP_TARGET, and TO_APP_MODEL. These send the classed event first to the GenApplication object associated with the recipient of MSG_META_SEND_CLASSED_EVENT ; the GenApplication object is taken as the top node, and the event is passed down the active path until it is handled.
  • System-direct
    The "system-direct" travel options are TO_SYS_FOCUS, TO_SYS_TARGET, and TO_SYS_MODEL. These send the classed event to the GenSystem object, the topmost object in the entire UI object tree. The classed event will be passed down the active path, using the GenSystem object as the path's top node.

There exist additional TravelOption types that can be used to direct messages to generically-defined locations. For a complete list of these types, see the GEOS Programming chapter.

Input: 5.3 Input Hierarchies: Using Focus

The specific UI handles most of the manipulation of the focus hierarchy. Most specific UIs interpret keyboard and mouse events and know when to switch the focus from one object to another. For example, in OSF/Motif, clicking on a window will switch the focus to that window from the previous focus window. Before the mouse is clicked, however, the focus exclusive of that window will not change. In other specific UIs which incorporate real-estate cursor behavior, merely moving the cursor over a window will transfer the focus to that window.

By default, an application will come up with certain objects having the focus exclusive within their focus levels. This allows the user to immediately begin typing or operating on data. For an object to grab the default exclusive, it must first be focusable. A focusable object can be any child of a valid focus node. Valid nodes for the focus hierarchy are:

GenSystem
GenField
GenApplication
GenPrimary
GenDisplayControl
GenDisplay
GenView (with GVA_FOCUSABLE bit set)
GenInteraction (independently displayable)
VisContent

Note that a GenView must have the GVA_FOCUSABLE bit set in its GVI_ attrs field. If an object you need to act as a focus node is not in the above list (for example, a custom gadget), you must subclass the object and add instance fields and message handlers to handle focus functionality.

If an independently displayable object is focusable, the focus will be given to the object which is visually "on top" of all other objects. For all other objects within a focus exclusive level, the focus will be granted to the object with HINT_DEFAULT_FOCUS in its instance data. If no objects have this hint at a particular focus level, then the focus will be granted to the first focusable child.

Grabbing and Releasing the Focus

To grab the focus exclusive in its level, a node should send itself MSG_META_GRAB_FOCUS_EXCL . The default handler for this message grabs the focus exclusive for the object from its parent and forces any other object on the caller's level to give the focus up. The active exclusive will not be granted unless the caller is part of the active path after the grab.

When another node grabs the focus exclusive, the node currently having the exclusive on that level must give it up. MSG_META_RELEASE_FOCUS_EXCL releases the focus exclusive for the other node.

Note that these messages only modify the exclusive at a single level. Therefore, they only affect the focus optr of the parent's node; further up the focus hierarchy there is no effect. Changing the focus exclusive of an object will only change the direction of the focus path if all parents of the new exclusive are also focus exclusives.

MSG_META_GET_FOCUS_EXCL may be sent to any focusable composite node to get the optr of the node's child having the exclusive. This message may be used even on nodes in the inactive path.

Gaining and Losing the Focus

When an object gains the active focus exclusive and is a node in the active path, it receives MSG_META_GAINED_FOCUS_EXCL . This indicates to the object that it will receive all keyboard input as the active keyboard object. At some point later, when the object has lost the focus exclusive, it will receive MSG_META_LOST_FOCUS_EXCL .

Sending Classed Events to the Focus

Frequently, you may wish to send messages to objects in the active Focus path. The easiest way to deliver a message to an object in the focus hierarchy is to use MSG_META_SEND_CLASSED_EVENT with the TravelOption TO_APP_FOCUS. To send a message to the leaf node of the active path, send this message with a null class. The message will be sent down the hierarchy until it reaches the leaf object, where it will be processed. (See Delivering Messages to the Focus .)

This approach is desirable over using MSG_META_GET_FOCUS_EXCL to return an optr for later use, as the system may have corrupted the optr in the meantime.

Code Display 11-2 Delivering Messages to the Focus

@method MyProcessClass, MSG_SEND_MESSAGE_TO_FOCUS_INTERACTION {
    EventHandle event;
    /* The classed event is recorded to be handled by the first object of class
     * GenInteractionClass. */
    event = @record GenInteractionClass::MSG_TEST_MESSAGE();
    /* This message is then sent to the GenApplication object, from which it
     * will travel down the focus hierarchy until handled at the
     * GenInteraction level. */
    @call MyApplication::MSG_META_SEND_CLASSED_EVENT(event, TO_FOCUS);
}
/* You may also declare an object with the TravelOption TO_APP_FOCUS. This will
 * send the message to the application object and then down the focus hierarchy. */
@object GenTriggerClass MyFocusTrigger = {
    GI_visMoniker = "Send test message to the focus";
    GTI_actionMsg = MSG_TEST_MESSAGE;
    GTI_destination = (TO_APP_FOCUS);
    ATTR_GEN_DESTINATION_CLASS = { (ClassStruc *)&GenInteractionClass };
}
/* To send a message to the focus leaf node, use a null class. */
@method MyProcessClass, MSG_SEND_TEST_TO_FOCUS_LEAF {
    EventHandle event;
    /* A classed event with the class of null is recorded. */
    event = @record null::MSG_TEST_MESSAGE();
    /* This event is sent to the GenApplication, where it will travel down the
     * focus hierarchy to the leaf object, where it will be handled. */
    @call MyApplication::MSG_META_SEND_CLASSED_EVENT(event, TO_FOCUS);
}
/* As an alternative, an object could be set to deliver its message to the
 * leaf focus node of the application. */
@object GenTriggerClass MyFocusLeafTrigger = {
    GI_visMoniker = "Send test message to the focus leaf object";
    GTI_actionMsg = MSG_TEST_MESSAGE;
    GTI_destination = (TO_APP_FOCUS);
    /* An ATTR_GEN_DESTINATION_CLASS of zero specifies a null class. */
    ATTR_GEN_DESTINATION_CLASS = 0;
}

Input: 5.4 Input Hierarchies: Using Target

The specific UI handles most of the manipulation of the target hierarchy. Most specific UIs interpret keyboard and mouse events and know when to switch the target from one object to another. For example, in OSF/Motif, clicking on a window will switch the target to that window from the previous target window. Before the mouse is clicked, however, the target exclusive of that window will not change. In other specific UIs which incorporate real-estate cursor behavior, merely moving the cursor over a window will transfer the target to that window.

By default, an application will come up with certain objects having the target exclusive within their target levels. This allows the user to immediately begin typing or operating on data. For an object to grab the default exclusive, it must first be targetable. A targetable object can be any child of a valid target node. Valid nodes for the target hierarchy are:

GenSystem
GenField
GenApplication
GenPrimary
GenDisplayControl
GenDisplay
GenView (with GVA_TARGETABLE bit set)
GenInteraction (independently displayable)
VisContent

In addition, however, a generic object must be set GA_TARGETABLE in its GI_attrs field to become a valid target object. This bit is set by default for the following classes:

GenField
GenApplication
GenPrimary
GenDisplayControl
GenDisplay
GenView

If you wish a generic object to be targetable and it does not appear in this list, set it GA_TARGETABLE. If the object does not by default appear as a valid node in the Target hierarchy (such as for a custom gadget), you must subclass the object and add target instance fields and message handlers.

For all targetable objects within a target exclusive level, the target will be granted to the object with HINT_DEFAULT_TARGET in its instance data. If no object has this hint at a particular target level, then the target will be granted to the first targetable object at that level.

Grabbing and Releasing the Target

To grab the target exclusive in its level, a node should send itself MSG_META_GRAB_TARGET_EXCL . The default handler for this message grabs the target exclusive for the object from its parent and forces any other object on the caller's level to give the target up. The active exclusive will not be granted unless the caller is part of the active path after the grab.

When another node grabs the target exclusive, the node currently having the exclusive on that level must give it up. MSG_META_RELEASE_TARGET_EXCL releases the target exclusive for the other node.

Note that these messages only modify the exclusive at a single level. Therefore, they only affect the target optr of the parent's node; further up the target hierarchy there is no effect. Changing the target exclusive of an object will only change the direction of the target path if all parents of the new exclusive are also target exclusives.

MSG_META_GET_TARGET_EXCL may be sent to any targetable composite node to get the optr of the node's child having the exclusive. This message may be used even on nodes in the inactive path.

Gaining and Losing the Target

When an object gains the active target exclusive and is a node in the active path, it receives MSG_META_GAINED_TARGET_EXCL . This indicates to the object that it will receive all keyboard input as the active keyboard object. At some point later, when the object has lost the target exclusive, it will receive MSG_META_LOST_TARGET_EXCL .

Sending Classed Events to the Target

Frequently, you may wish to send messages to objects in the active Target path. The easiest way to deliver a message to an object in the target hierarchy is to use MSG_META_SEND_CLASSED_EVENT with the TravelOption TO_TARGET. To send a message to the leaf node of the active path, send this message with a null class. The message will be sent down the hierarchy until it reaches the leaf object, where it will be processed. (See Delivering Messages to the Target .)

This approach is desirable over using MSG_META_GET_TARGET_EXCL to return an optr for later use, as the system may have corrupted the optr in the meantime.

Code Display 11-3 Delivering Messages to the Target

@method MyProcessClass, MSG_SEND_MESSAGE_TO_TARGET_INTERACTION {
    EventHandle event;
    /* The classed event is recorded to be handled by the first object of class
     * GenInteractionClass. */
    event = @record GenTextClass::MSG_TEST_MESSAGE();
    /* This message is then sent to the GenApplication object, from which it
     * will travel down the target hierarchy until handled at the
     * GenInteraction level. */
    @call MyApplication::MSG_META_SEND_CLASSED_EVENT(event, TO_TARGET);
}
/* You may also declare an object with the TravelOption TO_APP_TARGET. This will
 * send the message to the application object and then down the target hierarchy. */
@object GenTriggerClass MyTargetTrigger = {
    GI_visMoniker = "Send test message to the target";
    GTI_actionMsg = MSG_TEST_MESSAGE;
    GTI_destination = (TO_APP_TARGET);
    ATTR_GEN_DESTINATION_CLASS = { (ClassStruc *)&GenTextClass };
}
/* To send a message to the target leaf node, use a null class. */
@method MyProcessClass, MSG_SEND_TEST_TO_TARGET_LEAF {
    EventHandle event;
    /* A classed event with the class of null is recorded. */
    event = @record null::MSG_TEST_MESSAGE();
    /* This event is sent to the GenApplication, where it will travel down the
     * target hierarchy to the leaf object, where it will be handled. */
    @call MyApplication::MSG_META_SEND_CLASSED_EVENT(event, TO_TARGET);
}
/* As an alternative, an object could be set to deliver its message to the
 * leaf target node of the application. */
@object GenTriggerClass MyTargetLeafTrigger = {
    GI_visMoniker = "Send test message to the target leaf object";
    GTI_actionMsg = MSG_TEST_MESSAGE;
    GTI_destination = (TO_APP_TARGET);
    /* An ATTR_GEN_DESTINATION_CLASS of zero specifies a null class. */
    ATTR_GEN_DESTINATION_CLASS = 0;
}

Input: 5.5 Input Hierarchies: Using Model

In some cases, you may need to send or receive information from some object other than the focus or target. The Model hierarchy is provided as an alternative. In most cases, the model provides a means of pointing to data selections other than that pointed to by the Target hierarchy. Only use it if the target hierarchy cannot serve your needs.

The model of an application is used to identify objects that represent the actual data that can be selected by the user. Typically, these selections are either secondary selections or are not visible. The selections are frequently operated on with the use of menus, dialog boxes, or other UI gadgetry. Only one object at each model level may have the model "exclusive," and for an object to "have the Model" of an application, that object must have a continuous path of model exclusives up to the application object.

Only GenSystem, GenApplication, and the document control objects can be directly used in the Model hierarchy without any modifications. To use other objects in the Model hierarchy, you must first subclass those objects' classes and add an instance field and several message handlers pertaining to the Model hierarchy. Any object which is a valid target node can be subclassed in this manner.

By default, an application will come up with certain objects having the model exclusive within their model levels. This allows the user to immediately begin operating on data. For an object to grab the default exclusive, it must first be modelable. Only objects which are modelable may have the model exclusive. A modelable object can be any child of a valid model node. Valid nodes for the Model hierarchy are the same as for the Target hierarchy. However, a document control object must also be set GDCA_MODELABLE to grab the model exclusive. Remember that just because an object is a valid node does not mean it can exhibit the behavior of the Model hierarchy; you must subclass most objects for this to occur.

For objects within a model level, the model will be granted first to any object with HINT_DEFAULT_MODEL in its instance data. If no object has this hint at a particular model level, then the model will be granted to the first modelable object at that level.

The easiest way to deliver a message to an object in the active Model hierarchy is to use MSG_META_SEND_CLASSED_EVENT with the TravelOption TO_MODEL. To send a message to the leaf node in the Model hierarchy, send this message with a class of null. The message will be sent down the hierarchy until it reaches the leaf object, where it will be processed.

This approach is desirable over using MSG_META_GET_MODEL_EXCL to return an optr for later use as the system may have corrupted the optr in the meantime.

Changing the Model Exclusive

To modify the Model hierarchy, use MSG_META_GRAB_MODEL_EXCL . To remove the model exclusive from an object without setting it to another object, use MSG_META_RELEASE_MODEL_EXCL . Note that these messages only modify the model exclusive of a single object. Therefore, they only affect the model pointer of the parent; further up the Model hierarchy there is no effect. Changing the model exclusive of an object will only change the direction of the model path if all ancestors of the new model exclusive are also model exclusives.

To check which model exclusive a composite points to, send it a MSG_META_GET_MODEL_EXCL . This message will return the optr of the model child. The returned object may not have the model properties, though, for the reasons above. Never send a message directly to the object returned by this method as it may have been changed in the meantime. Use MSG_META_SEND_CLASSED_EVENT with the TravelOption TO_MODEL instead.

Intercepting the Model

Your application may also wish to be notified when an object either gains or loses its model properties. Whenever an object in GEOS gains the model of the application--not just the model exclusive--it sends a MSG_META_GAINED_MODEL_EXCL to that object. Similarly, if an object loses the model of an application--not just the model exclusive--the system sends a MSG_META_LOST_MODEL_EXCL to that object.

If your application needs to know when an object either gains or loses its model properties, intercept MSG_META_GAINED_MODEL_EXCL and MSG_META_LOST_MODEL_EXCL . Be sure to pass these messages on to the superclass, however, to perform necessary default functionality. Within your message handlers for these messages, you may add whatever additional behavior you require.

Input: 5.6 Input Hierarchies: Extending the Hierarchies

You may also extend or modify the hierarchies to add other objects that are not active nodes by default. If custom objects wish to use the hierarchies, you will need to add instance data and message handlers for their classes. The following steps must be taken to add a new node in a hierarchy:

  1. Add a hierarchical instance field of type HierarchicalGrab .
  2. Add a handler for MSG_META_MUP_ALTER_FTVMC_EXCL . This method alters the custom instance field for the grab and release messages for all hierarchies. You must call the superclass in your handler.
  3. Add methods to handle the GAINED and LOST messages pertaining to the particular hierarchy you are extending. These methods should call FlowUpdateHierarchicalGrab() . You can also add behavior exhibited when the object gains or loses the exclusive. Depending on what you are subclassing, your default behavior may already take care of this. For instance subclasses of VisClass automatically handle losing and gaining the target exclusive.
  4. Add a method to handle the GET message for the particular hierarchy you are extending. This method returns the optr of the object set in the new instance field. The method should return true to signal that it is capable of gaining the exclusive.
  5. Add the capability to handle the MSG_META_SEND_CLASSED_EVENT with the TravelOption of the particular hierarchy you are extending. The way you handle this message depends on the hierarchy affected, as discussed below.

TO_FOCUS and TO_TARGET requests should only travel down hierarchies of the same name. If no further travel is possible, the method should pass the event on to the superclass for handling. TO_MODEL requests should only travel down the Model hierarchy if it exists and the next optr is non-null.

This document is a single-page version of a a multi-page document, suitable for easy printing.


MetaClass

MetaClass is the ancestor class of every GEOS object. MetaClass is the location of basic messages and their handlers. Basic functionality for all objects--instantiation, initialization, detach, and destruction--is implemented within this class.

The only instance data field defined for MetaClass is the object's class pointer (the field is named MI_base ). MetaClass has no other inherent data fields. You will never need to access the class pointer directly.

MetaClass also serves as a sort of placeholder for certain messages. Application writers familiar with mouse input, for instance, know that MSG_META_START_SELECT signals a mouse click. This message is actually defined at the MetaClass level, though MetaClass

1 Special Messages
2 Utility Messages
2.1 Object Creation and Destruction
2.2 Class Messages
2.3 Object Management Messages
2.4 User Interface Utility Meta Messages
2.5 Event Messages
2.6 Variable Data Messages
2.7 Notification Messages
2.8 Options Messages
2.9 Suspending and Unsuspending
2.10 Help Files
3 Exported Message Ranges
3.1 Window Messages
3.2 Input Messages
3.3 UI Messages
3.4 Standard GCN Messages
3.5 IACP Meta Messages

MetaClass: 1 Special Messages

MSG_META_NULL, MSG_META_DUMMY

The following two messages are mainly place holders. They ensure that no message will have the value zero or one. (When a message is called, a value of zero equates to null.) These are not used by applications or objects in general.

MSG_META_NULL
void	MSG_META_NULL();

This message has no handler and is unused. It essentially ensures that no other message will ever have the value zero.

Interception: Don't.

MSG_META_DUMMY
void	MSG_META_DUMMY();

This message has no handler. You should not subclass this message to provide one. Certain object mechanisms, such as the resolution of a variant class, are activated by the object's receipt of MSG_META_DUMMY .

Interception: Don't.

MetaClass: 2 Utility Messages

MetaClass also provides a number of other messages that are used throughout the system. These messages have been separated into loosely-defined categories and listed in the sections below.

Object Creation and Destruction

Class Messages

Object Management Messages

User Interface Utility Meta Messages

Event Messages

Variable Data Messages

Notification Messages

Options Messages

Suspending and Unsuspending

Help Files

MetaClass: 2.1 Utility Messages: Object Creation and Destruction

These messages handle creation, destruction, and initialization of all objects. The function and use of many of these messages are given in .

MSG_META_INITIALIZE
void	MSG_META_INITIALIZE();

Every object class should provide a handler for this message which should call the superclass and then perform any initialization of the instance data required. This message is not delivered to statically declared objects because they are already initialized.

Note that GenClass and VisClass have a default handler that sets up the Gen and Vis parts automatically.

This message is not delivered to objects whose only master part is MetaClass itself.

Source: Object system itself, often in the middle of attempting to deliver another message to an object that hasn't yet been initialized.

Destination: Object whose instance data is not yet initialized.

Parameters: None.

Return: Nothing.

Interception: Any class wishing to have default instance data values other than all zeros should intercept this message to fill in the initial values for its instance data. For classes other than master classes, standard procedure is to call the superclass first, then perform any additional instance data initialization necessary. Master classes should not call the superclass, as MSG_META_INITIALIZE is unique among messages in that it is sent only to classes within the particular master group that needs to be initialized. Handlers of MSG_META_INITIALIZE should limit their activities to just stuffing instance data--specifically, object messaging is not allowed (though scanning vardata is OK). Object classes that inherit instance data (all but MetaClass ) should call MSG_META_INITIALIZE on their superclass to initialize that portion of their instance data. In addition, they must initialize their own portion of the instance data (start by assuming it's all zeros). The order won't matter, so long as the handler doesn't depend on the inherited instance data having any particular value. When in doubt, call the superclass first.

MSG_META_ATTACH
void 	MSG_META_ATTACH();

This message is used for two different purposes: It can be sent to any geode that has a process aspect when the geode is first loaded. It can also be sent in the object world to notify objects on an "active list" that the application has been brought back up from a state file. As the method is used for different purposes, the data passed varies based on usage. Because of this difference in parameters, normally C applications will use one of the aliases for this message ( MSG_META_ATTACH_PROCESS , MSG_META_ATTACH_THREAD , MSG_META_ATTACH_GENPROCESSCLASS , MSG_META_ATTACH_OBJECT, and MSG_META_ATTACH_GENAPPLICATION , each described below.)

MSG_META_ATTACH_PROCESS
@alias (MSG_META_ATTACH)
 void 	MSG_META_ATTACH_PROCESS(
        word 	value1,
        word 	value2);

This message is sent to any geode which has a process when the geode is first loaded. By default, the handler for this message does nothing.

Source: GeodeLoad() kernel routine.

Destination: Newly created Process object (but not GenProcess object).

Parameters: value1 Upper half of GeodeLoad() appInfo argument.

value2
Lower half of GeodeLoad() appInfo argument.

Return: Nothing.

Interception: No default handling provided, so if you are spawning an extra process and that process needs to do some initialization, then intercept this message.

MSG_META_ATTACH_GENPROCESSCLASS
@alias (MSG_META_ATTACH)
 void 	MSG_META_ATTACH_GENPROCESSCLASS(
        MemHandle appLaunchBlock);

This message is sent to the GenProcess object when the geode is first loaded. By default, the handler for this message calls MSG_PROCESS_STARTUP_UI_THREAD , which checks to see if there are any resources of the application which are marked as "ui-object" (they are marked this way in the .gp file), that is, to be run by a UI thread. If so, it then calls MSG_PROCESS_CREATE_UI_THREAD to create that thread, then marks the "ui-object" blocks as run by that thread.

The handler next calls one of the following messages:

MSG_GEN_PROCESS_OPEN_APPLICATION
For applications which are being started up regularly (that is, not restoring from a state file) and will appear on screen.
MSG_GEN_PROCESS_OPEN_ENGINE
For those applications that will operate in engine mode (i.e. non-visual).
MSG_GEN_PROCESS_RESTORE_FROM_STATE
For applications which are restoring from state. This is the case for applications that were running at the previous shutdown.

Source: GeodeLoad() kernel routine.

Destination: Newly created GenProcessClass (or subclass thereof) object.

Parameters: appLaunchBlock Block handle to block of structure AppLaunchBlock .

Return: Nothing.

Note that the passed AppLaunchBlock is destroyed.

Interception: No default handling provided, so if you are spawning an extra process and that process needs to do some initialization, then intercept this message.

MSG_META_ATTACH_GENAPPLICATION
@alias (MSG_META_ATTACH)
 void 	MSG_META_ATTACH_PROCESS(
        MemHandle 	bh1,
        MemHandle 	bh2);

This message is sent to the GenApplication object by GenProcessClass when the application starts up (either for the first time, or when being restored from a state file).

Source: GenProcess object.

Destination: GenApplication object.

Parameters: bh1 Block handle to block containing AppLaunchBlock parameters.

bh2
Extra state block from state file, or NULL if none. This is the same block as returned from MSG_GEN_PROCESS_CLOSE_APPLICATION in some previous detach operation.

Return: Nothing.

Note that the AppLaunchBlock is destroyed.

Interception: Not generally required, since the default handler broadcasts the message out to everything on the application's active lists. This act causes the interface for the application to come up on screen.

MSG_META_ATTACH_OBJECT
@alias (MSG_META_ATTACH)
 void 	MSG_META_ATTACH_OBJECT(
        word	 	flags,
        MemHandle 		appLaunchBlock,
        MemHandle 		extraState);

This message is sent to any object on the GenApplication object's active lists, or on one of those object's active lists. Note that this will not happen until the GenApplication is set usable by the GenProcess object.

Source: GenApplicationClass object.

Destination: Any object.

Parameters: flags Flags providing state information.

appLaunchBlock
Handle of AppLaunchBlock , or NULL if none.
extraState
Handle of extra state block, or NULL if none. This is the same block as returned from MSG_GEN_PROCESS_CLOSE_APPLICATION , in some previous detach.

Return: Nothing.

Interception: Standard UI objects defined as needing to be placed on an active list will intercept this message to do whatever it is that they needed to do when the application is first loaded. Objects intercepting this message should call the superclass, in case it expects to receive this notification itself.

Warnings: If the specific UI uses this mechanism, then the GenProcessClass will have already destroyed the AppLaunchBlock and extra state block by the time the MSG_META_ATTACH is sent to objects on its active list.

MSG_META_ATTACH_THREAD
@alias (MSG_META_ATTACH)
 void 	MSG_META_ATTACH_THREAD();

This message is sent to any thread spawned by MSG_PROCESS_CREATE_EVENT_THREAD .

Source: Kernel.

Destination: Newly created thread, specifically the class designated to handle the thread's messages (a subclass of ProcessClass ).

Parameters: None.

Return: Nothing.

Interception: No default handling provided, so if you are spawning an extra process and that process needs to do some initialization, then intercept this message.

MSG_META_APP_STARTUP
void	MSG_META_APP_STARTUP(
        MemHandle		appLaunchBlock);

This message is related to MSG_META_ATTACH; the message is sent by the generic UI to the GenApplication object before it sends MSG_META_ATTACH to it. MSG_META_ATTACH is only sent when the application is becoming available to the user; if an application should be opened as a server without presenting any UI to the user, MSG_META_APP_STARTUP will be the only message sent to the application object upon start-up.

The default handler for this message will pass it on to all members of the MGCNLT_APP_STARTUP list.

Source: GenProcessClass ; forwarded by GenApplicationClass to other objects. This message is sent upon application start-up before the UI for an application has been attached.

Destination: Any object that needs to be notified when the application is launched, regardless of whether the user will be interacting with the application.

Parameters: appLaunchBlock Handle of an AppLaunchBlock .

Return: The AppLaunchBlock is preserved.

Interception: Usually intercepted by any object on the MGCNLT_APP_STARTUP list.

MSG_META_UPDATE_WINDOW
void	MSG_META_UPDATE_WINDOW(
        UpdateWindowFlags		updateFlags,
        VisUpdateMode		updateMode);

This message is sent as part of the system's window update mechanism. Typically, this message is sent to windowed objects on the GAGNLT_WINDOWS list when the GenApplication object becomes GS_USABLE.

The message passes a bitfield of UpdateWindowFlags . These flags determine the type of action prompting the window update.

UWF_ATTACHING
If set, the message is being sent because the application is attaching.
UWF_DETACHING
If set, the message is being sent because the application is detaching.
UWF_RESTORING_FROM_STATE
If set, the application is restoring from state. This flag will only be set if UWF_ATTACHING is also set.
UWF_FROM_WINDOWS_LIST
If set, the message is being sent because the object is on the GAGCNLT_WINDOWS list, and not because it was later built on demand. This flag will only be set if UWF_ATTACHING is also set.

Source: Window update mechanism.

Destination: Entries on the Application's GAGCNLT_WINDOWS list.

Parameters: updateFlags UpdateWindowFlags .

updateMode
VisUpdateMode .

Interception: Generally not intercepted.

MSG_META_DETACH
void	MSG_META_DETACH(
        word	callerID,
        optr	caller);

This message severs the links between an object and the rest of the system. The exact way this is handled depends on the object being detached. For full information on detaching objects, see the GEOS Programming chapter.

The "state" of the object is left intact, in case an image of the object needs to be saved away in a state file for later re-creation. MSG_META_DETACH sent to an application's process will start the process by which it is detached from the system, and then exited. MSG_META_DETACH is asynchronous, in that it need not complete its job immediately upon being called. Rather, it may take as much time, invoking and waiting for the completion of subsidiary detaches (say of child objects needing to perform special actions to detach, or of threads created earlier), before it responds with MSG_META_ACK to let its caller know that the detach has completed.

Source: Kernel, other objects relaying detach message.

Destination: GenProcess, GenApplication, objects on active lists.

Parameters: callerID A number of your choosing. This number will be passed back to caller in MSG_META_ACK in the callID parameter.

caller
Object which should be sent a MSG_META_ACK when detaching object has finished.

Return: Nothing.

Interception: Intercepted as a means of finding out that the application is shutting down. Call the superclass in case it needs such notification.
If you create additional threads, or object classes which need to be notified when the application is about to be exited, you may need to extend the detach mechanism by intercepting MSG_META_DETACH in a subclass of an object already receiving that message, such as GenApplication, GenControl, GenInteraction dialogs, etc. You must make sure that all objects you've sent MSG_META_DETACH to have responded with a MSG_META_ACK before your object can reply with MSG_META_ACK . Remember that your superclass may be sending MSG_META_DETACH . The kernel provides some default behavior in MetaClass , and some utility routines, to make this a simpler task. The default handler for MSG_META_DETACH , for instance, at a leaf object (one which doesn't propagate the MSG_META_DETACH ), performs the required response (sending a MSG_META_ACK ). Thus, leaf objects can just intercept MSG_META_DETACH for notification purposes, then call the superclass, and worry no more. The utility routines ObjInitDetach() and ObjEnableDetach() work in conjunction with a default MSG_META_ACK handler in MetaClass to keep track of how many outstanding acknowledgments are being waited for, and call MSG_META_DETACH_COMPLETE on your object once all acknowledgments have returned (the count reaches zero). The default handler for MSG_META_DETACH_COMPLETE then generates the acknowledgment response required of your object to complete its detach. You may optionally call the superclass before sending the detach message to your children and dependents, depending on which order you want things to detach in. The call to the superclass must happen between the ObjInitDetach() and ObjEnableDetach() , however.

MSG_META_DETACH_COMPLETE
void	MSG_META_DETACH_COMPLETE();

This message is sent to an object being detached when all of its children and active participants have acknowledged the detach. For full information on detaching objects, see the GEOS Programming chapter.

MSG_META_DETACH_COMPLETE is sent to the object which called ObjInitDetach() . This will happen when as many acknowledgments have been received as ObjIncDetach() was called, and ObjEnableDetach() was called. The MetaClass handler for this message sends MSG_META_ACK to the OD passed to the ObjInitDetach() call. This message is provided so that an object will know when all of its children have detached. Note that this message is received only if ObjInitDetach() has been called for this object. Note also that your superclass may call ObjInitDetach() without your knowing.

Source: MetaClass handler for MSG_META_ACK , if detach count has dropped to zero (i.e. no outstanding requests), for objects that are detach nodes only (make use of ObjInitDetach() or ObjEnableDetach() ).

Destination: Self.

Parameters: None.

Return: Nothing.

Interception: This is a handy message to intercept when using the ObjInitDetach() mechanism and need to know when all objects asked to detach have responded. Calling the superclass at this point in time will cause an MSG_META_ACK to go back to whatever object sent the MSG_META_DETACH to this object originally. There is no requirement to call the superclass at this time, and in fact this is a way to prolong the detach cycle for this object-- by simply starting up another ObjInitDetach() sequence, for instance.

MSG_META_DETACH_ABORT
void	MSG_META_DETACH_ABORT();

This message causes a detach to be aborted. This can cause some very complex synchronization problems and should not be used lightly. You will find very little call to use it.

Source: Renegade object on active list, after having received MSG_META_DETACH , as an alternative to replying with MSG_META_ACK .

Destination: The optr passed in MSG_META_DETACH .

Interception: Handled by GenField to deal with applications that refuse to die, and GenSystem for Fields that have problem applications. Other than that, any detach node wishing to provide this service will have to figure out a way to do it itself.

Parameters: None.

Return: Nothing.

MSG_META_APP_SHUTDOWN
void	MSG_META_APP_SHUTDOWN(
        word		callerID,
        optr		ackOD);

This message is the complement to MSG_META_APP_STARTUP. This message is sent to objects on the MGCNLT_APP_STARTUP list before an application exits but after the UI for the application is detached. Essentially, it operates in the same manner as MSG_META_DETACH except that the receiving object sends MSG_META_SHUTDOWN_ACK when its shutdown is complete.

Source: Sent by GenProcessClass after detaching the UI but before exiting the application; if the UI was never attached (i.e. it handled MSG_META_APP_STARTUP but not MSG_META_ATTACH) the UI will obviously not be detached.

Destination: Any object that needs to be notified when the application is about to exit.

Parameters: callerID Word of data for caller's use.

ackOD
Optr of object to be sent MSG_META_SHUTDOWN_ACK.

Interception: Usually intercepted by objects on the MGCNLT_APP_STARTUP list.

MSG_META_SHUTDOWN_COMPLETE
void	MSG_META_SHUTDOWN_COMPLETE();

This message is sent to the object that initiated the detach sequence after it has received MSG_META_SHUTDOWN_ACK for each ObjIncDetach() that was previously called. This message is only sent if ObjInitDetach() was previously called passing the message MSG_META_APP_SHUTDOWN.

The default handler for this message sends MSG_META_SHUTDOWN_ACK to the object passed in the original ObjInitDetach() call.

Source: MetaClass handler for MSG_META_SHUTDOWN_ACK if detach count reaches zero (i.e. no outstanding requests), for objects that are shutdown nodes only (i.e. make use of ObjInitDetach() .)

Destination: The object sends this message to itself.

Interception: Intercept if you are using the ObjInitDetach() mechanism and need to be notified when all objects have been notified of the detach.

MSG_META_SHUTDOWN_ACK
void	MSG_META_SHUTDOWN_ACK(
        word		callerID,
        optr		ackOD);

This message is sent back in response to a MSG_META_APP_SHUTDOWN. This message serves to notify the object the object has fulfilled the request.

Source: Object having received MSG_META_APP_SHUTDOWN. The default handler will dispatch MSG_META_SHUTDOWN_ACK after MetaClass has processed MSG_META_APP_SHUTDOWN. (You could, of course, intercept MSG_META_APP_SHUTDOWN and send MSG_META_SHUTDOWN_ACK yourself in your handler.)

Destination: Optr passed in MSG_META_APP_SHUTDOWN.

Parameters: callerID Data passed in MSG_META_APP_SHUTDOWN.

ackOD
Object which has completed shutting down.

Interception: MetaClass provides default handling for this message when using the ObjInitDetach() mechanism. Objects not using this mechanism will want to intercept this message if there is a need to know when the object has completed shutting down.

MSG_META_ACK
void	MSG_META_ACK(
        word	callerID,
        optr	caller);

This message acknowledges a detach message. It is sent by objects that have been notified of another object's detach. The default handler for MSG_META_DETACH simply sends MSG_META_ACK back to the object that sent the detach message.

Source: Object having received MSG_META_DETACH (default handler in MetaClass will reflexively respond to any MSG_META_DETACH with a MSG_META_ACK , though you can change this behavior either by using ObjInitDetach() or by not letting the message get to the MetaClass handler, and responding yourself with a MSG_META_ACK sometime later).

Destination: The optr passed in MSG_META_DETACH.

Interception: MetaClass provides default handling of this message, for objects using the ObjInitDetach() mechanism. Objects not using this mechanism will want to intercept this if there is a need to know when the object asked to detach earlier has completed its detach.
MSG_META_ACK is normally inherited from MetaClass which calls ObjEnableDetach() . This routine decrements the detach count, and when that count reaches zero, sends a MSG_META_DETACH_COMPLETE to the object itself.

Warnings: If you are expecting a MSG_META_ACK back from anything, make sure you are using the mechanism initiated with ObjInitDetach() yourself, or you should handle MSG_META_ACK to prevent MetaClass from assuming you are using such a mechanism.

Parameters: callerID data passed to MSG_META_ACK

caller
object which has completed detaching

Return: Nothing.

MSG_META_BLOCK_FREE
void	MSG_META_BLOCK_FREE();

This message initiates a sequence which will free an entire object block when received by any object within that block. The block will be freed when its in-use count reaches zero and the message queues for the block have been cleared.

This is a fairly low-level operation, and should be performed only after the objects in the block have been removed from any tree(s) they are attached to, and are otherwise "shut down." For generic objects, this generally means first calling MSG_GEN_SET_NOT_USABLE , then MSG_GEN_REMOVE_CHILD before using this message. For Visible objects, MSG_VIS_REMOVE will both visually shut down the visible tree, and then remove it from its parent.

Source: Unrestricted.

Destination: Any object within a block that is ready to have a low-level delete performed on it (i.e. isn't on screen, isn't linked to objects in other blocks, etc.).

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: None.

Return: Nothing.

MSG_META_OBJ_FREE
void	MSG_META_OBJ_FREE();

This message initiates a sequence which will free an object. The object will be freed after its message queues have been flushed.

This is a fairly low-level operation, and should be performed only after the object has been removed from any tree it is attached to and is otherwise "shut down." Consider using MSG_GEN_DESTROY for generic objects, MSG_VIS_DESTROY for visible ones.

Source: Unrestricted.

Destination: Any object within a block that is ready to have a low-level delete performed on it (i.e. isn't on screen, isn't linked to objects in other blocks, etc.).

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: None.

Return: Nothing.

MSG_META_DEC_BLOCK_REF_COUNT
void	MSG_META_DEC_BLOCK_REF_COUNT(
        MemHandle		block1,
        MemHandle		block2);

This message is a utility message to call MemDecRefCount() on one or two memory handles.

This message is useful for IACP, which initializes the reference count to the number of servers returned by IACPConnect() and records this message as the message to be returned. After each server has processed its information, the reference count will return to zero and the handles will be freed.

Source: Unrestricted.

Destination: Any object.

Parameters: block1 Handle of a block whose reference count should be decremented, or 0 if none.

block2
Handle of a block whose reference count should be decremented, or 0 if none.

Interception: Generally not intercepted.

MSG_META_OBJ_FLUSH_INPUT_QUEUE
void	MSG_META_OBJ_FLUSH_INPUT_QUEUE(
        EventHandle event,
        ObjFlushInputQueueNextStop nextStop,
        MemHandle 	objBlock);

This message clears out the message queues associated with an object. This is rarely, if ever called from within an application, and there is little call to subclass it.

This queue-flushing mechanism is used in the Window, Object, and Object Block death mechanisms. Objects that implement their own "hold up input" queues must redirect this message through that queue, so that it is flushed as well.

Source: Kernel ( WinClose() , WinSetInfo() , ObjFreeObjBlock() , MSG_META_OBJ_FREE , or MSG_META_BLOCK_FREE).

Destination: Should first be sent to the Kernel's Input Manager (See the routine ImInfoInputProcess() ). The message is then relayed first to the System Input Object (usually the GenSystemClass object), then to the Geode Input Object (usually a GenApplicationClass object), and finally to the owning process, which dispatches the passed event.

Parameters: event Event to dispatch upon conclusion of flush.

objBlock
Block Handle that flushing is being performed for (generally the handle of the destination object in the above event). This is the block from which the "OWNING GEODE", as referenced in the ObjFlushInputQueueNextStop enumerated type, is determined.
nextStop
ObjFlushInputQueueNextStop (Zero should be passed in call to first object, from there is sequenced by default MetaClass handler)

Return: Nothing.

Structures:

  typedef enum {
    OFIQNS_SYSTEM_INPUT_OBJ,
    OFIQNS_INPUT_OBJ_OF_OWNING_GEODE,
    OFIQNS_PROCESS_OF_OWNING_GEODE,
    OFIQNS_DISPATCH
} ObjFlushInputQueueNextStop;

Interception: Default MetaClass handler implements relay of message from one object to the next, and dispatches the passed event. Must be intercepted by any input-flow controlling objects (System object, VisContent) which implement "hold-up" queues that hold up input-related messages. The handlers in such cases should pipe this method through the hold-up queue as it does with the other messages, and finish up when it comes out by sending this message, with all data intact, to the superclass for continued default processing.

MetaClass: 2.2 Utility Messages: Class Messages

These messages are utilities that identify the class of a particular object. You should not subclass these. Their use is shown in the GEOS Programming chapter.

MSG_META_GET_CLASS
ClassStruct * MSG_META_GET_CLASS();

This message returns a pointer to the ClassStruct structure of the recipient object's class.

Source: Unrestricted.

Destination: Any object.

Parameters: None.

Return: The object's class.

Interception: Don't.

MSG_META_IS_OBJECT_IN_CLASS
Boolean	MSG_META_IS_OBJECT_IN_CLASS(
        ClassStruct * class);

This message determines whether the recipient object is a member of a given class (or a subclass of the given class). If the return is true , the object is in the class. If false , the object is not in the class. If a variant class is encountered (when checking to see if the object is an instance of a subclass of the passed class), the object will not be grown out past that class in the search. If you want to do a complete search past variant classes, send a MSG_META_DUMMY first.

Source: Unrestricted.

Destination: Any object.

Parameters: class Class to see if object is a member of.

Return: Returns true if object is a member of the passed class (or a subclass), false otherwise.

Interception: Don't.

MetaClass: 2.3 Utility Messages: Object Management Messages

These messages fill in and resolve an object's instance data. They should usually not be subclassed, and will be sent by applications infrequently (if ever).

MSG_META_RESOLVE_VARIANT_SUPERCLASS
ClassStruct * MSG_META_RESOLVE_VARIANT_SUPERCLASS(
        word	MasterOffset);

This message is sent by the object system when it needs to know the run-time superclass of a particular object's variant master class. The system sends this message to the object when it first attempts to deliver a message to the superclass of a variant class. The object must examine itself and determine what its superclass for that master level should be.

Source: Object system.

Destination: Any object with a variant class in its class hierarchy.

Interception: Because variant master classes tend to be strictly administrative in nature, providing useful and very generic functionality to their subclasses, all immediate children of a variant master class will need to intercept this message and return the appropriate class pointer.

Parameters: MasterOffset Master offset of the level being resolved. If you know there's a variant class above your own, you will need to examine this to determine if it is your master level whose variant is being resolved, or the one above you.

Return: Superclass to use.

MSG_META_RELOCATE
Boolean 	MSG_META_RELOCATE(
        word		vMRelocType,
        word		frame);

This message is sent by the object system to evaluate and resolve all of the object's relocatable instance data fields (pointers, optrs, etc.). Note that this only applies if the class' CLASSF_HAS_RELOC flag is set.

NOTE: The calling of this method is non-standard in that it does not pass through the class's method table. Rather, the handler address is placed after the method table and a direct call is issued. This means a relocation routine should not be bound to MSG_META_RELOCATE but should rather be bound to @reloc , which Goc understands to mean the handler is for both MSG_META_RELOCATE and MSG_META_UNRELOCATE .

Note also that relocation-by-routine happens in addition to (but before) any relocation due to the class' relocation table. To suppress relocation-by-table, you should initialize the class record with the CLASSF_HAS_RELOC flag to prevent Goc from generating a table for the class.

Source: Kernel, when loading in object block, general resources, or object blocks stored in VM file format.

Destination: Individual object needing relocations beyond what the kernel can do automatically (or that simply request for this message to be sent by having their CLASSF_HAS_RELOC bit set)

Interception: Intercepted by any class needing to perform special relocations on its instance data. Superclass should be called, in case a superclass also needs to perform this operation on its own instance data.

Parameters: vmRelocType Type giving some context to the relocation.

frame
Frame to pass to ObjRelocOrUnRelocSuper() .

Return: If an error occurred, this will return true .

Structures:

  typedef enum {
    VMRT_UNRELOCATE_BEFORE_WRITE,
    VMRT_RELOCATE_AFTER_READ,
    VMRT_RELOCATE_AFTER_WRITE,
    VMRT_RELOCATE_FROM_RESOURCE,
    VMRT_UNRELOCATE_FROM_RESOURCE
} VMRelocType;

Warnings: This method may not call LMemAlloc() , LMemReAlloc() , or LMemFree() .

MSG_META_UNRELOCATE
Boolean 	MSG_META_UNRELOCATE(
        word		vMRelocType,
        word		frame);

This message causes an object to unresolve all its relocatable instance data fields, returning them to special index values.

Source: Kernel, when loading in object block, general resources, or object blocks stored in VM file format.

Destination: Individual object needing relocations beyond what the kernel can do automatically (or that simply request for this message to be sent by having their CLASSF_HAS_RELOC bit set).

Interception: Intercepted by any class needing to perform special relocations on its instance data. Superclass should be called, in case a superclass also needs to perform this operation on its own instance data.

Parameters: vmRelocType Type giving some context to the relocation.

frame
Frame to pass to ObjRelocOrUnRelocSuper() .

Return: If an error occurred, this will return true .

Structures:

typedef enum {
    VMRT_UNRELOCATE_BEFORE_WRITE,
    VMRT_RELOCATE_AFTER_READ,
    VMRT_RELOCATE_AFTER_WRITE,
    VMRT_RELOCATE_FROM_RESOURCE,
    VMRT_UNRELOCATE_FROM_RESOURCE
} VMRelocType;

Warnings: This method may not call LMemAlloc() , LMemReAlloc() , or LMemFree() .

MetaClass: 2.4 Utility Messages: User Interface Utility Meta Messages

These messages are used primarily by the User Interface. You will have very little need to subclass or send them.

MSG_META_SET_FLAGS
void 	MSG_META_SET_FLAGS(
        ChunkHandle 	objChunk,
        ObjChunkFlags 	bitsToSet,
        ObjChunkFlags 	bitsToClear);

This message sets the chunk flags for an object. The chunk flags determine how the object is handled with regard to state saving, dirty state, etc.

Source: Unrestricted.

Destination: Any object.

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: objChunk chunk to set flags for.

bitsToSet
bits to set.
bitsToClear
bits to clear.

Return: Nothing.

MSG_META_GET_FLAGS
word 	MSG_META_GET_FLAGS( /* low byte = ObjChunkFlags */
        ChunkHandle 	ch);

This message returns the chunk flags for the object. This works just like the ObjGetFlags() routine, but can be used when the object queried is being run by a different thread.

Source: Unrestricted.

Destination: Any object.

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: objChunk chunk to get flags for.

Return: Word with ObjChunkFlags in low byte, zero in high byte.

MSG_META_QUIT
void 	MSG_META_QUIT();

This message, when sent to a GenApplication object, initiates the shutdown sequence for the application. All affected objects are notified.

GenApplication does some error checking for multiple quits or detaches and then starts this sequence by passing MSG_META_QUIT (QL_BEFORE_UI) to the process. The default process handler for MSG_META_QUIT varies depending on the QuitLevel , which is passed in, but only when sent to the process (see MSG_META_QUIT_PROCESS alias, below).

The method handler for each level of quit should then send MSG_META_QUIT_ACK with the same QuitLevel when it is done. The default behavior for a process' MSG_META_QUIT responses are:

QL_BEFORE_UI
Sends MSG_META_QUIT_ACK to self via queue.
QL_UI
Sends MSG_GEN_APPLICATION_INITIATE_UI_QUIT (0) to the GenApplication.
QL_AFTER_UI
Sends MSG_META_QUIT_ACK to self via queue.
QL_DETACH
Sends MSG_META_DETACH to self via queue.
QL_AFTER_DETACH
Sends MSG_META_QUIT_ACK to self via queue.

The generic UI objects are first asked to quit via MSG_GEN_APPLICATION_INITIATE_UI_QUIT when sent to a GenApplication (active list). It will cause MSG_META_QUIT to be sent to all objects on the active list that are marked as desiring them. These objects on the active list can handle the MSG_META_QUIT any way they please. The process will be notified by a MSG_META_QUIT_ACK with the QuitLevel set to QL_UI.

If you need to send MSG_META_QUIT from within a handler for MSG_GEN_PROCESS_OPEN_APPLICATION, make sure to send it after calling the superclass in your OPEN_APPLICATION handler. If this is undesirable, you may encapsulate MSG_META_QUIT and send it via MSG_META_FLUSH_INPUT_QUEUE. (This is needed to avoid the system thinking that a valid IACP connection is open, aborting the quit.)

Source: Unrestricted. If you need to send MSG_META_QUIT within your MSG_GEN_PROCESS_OPEN_APPLICATION handler, please follow the guidelines illustrated above.

Destination: GenApplication object (note that this message has aliases so that it may be sent to a Process object, or any object).

Parameters: None.

Return: Nothing.

Interception: Unlikely.

MSG_META_QUIT_PROCESS
@alias (MSG_META_QUIT)
 void 	MSG_META_QUIT_PROCESS(
        word		quitLevel,
        ChunkHandle 		ackODChunk);

For information about the quit mechanism, see MSG_META_QUIT , above.

The process's MSG_META_QUIT_ACK handler is what causes this walking down the QuitList ; It provides the following behavior for each QuitLevel :

QL_BEFORE_UI
Sends MSG_META_QUIT (QL_UI) to self.
QL_UI
Sends MSG_META_QUIT (QL_AFTER_UI) to self.
QL_AFTER_UI
Sends MSG_META_QUIT (QL_DETACH) to self.
QL_DETACH
Sends MSG_META_QUIT (QL_AFTER_DETACH) to self.
QL_AFTER_DETACH
Sends MSG_GEN_PROCESS_FINISH_DETACH to self.

Source: Unrestricted.

Destination: Process object.

Parameters: quitLevel What stage of quitting we are in.

ackODChunk
Acknowledgment OD to be passed on to MSG_META_QUIT_ACK .

Return: Nothing.

Interception: Unlikely.

Warnings: You cannot abort the quit at the QL_DETACH stage or later.

MSG_META_QUIT_OBJECT
@alias (MSG_META_QUIT)
 void 	MSG_META_QUIT_OBJECT(
        optr 	obj);

For information about the quit mechanism, see MSG_META_QUIT , above.

Source: Unrestricted.

Destination: Process object.

Parameters: obj Object to send MSG_META_QUIT_ACK to.

Return: Nothing.

Interception: Unlikely.

MSG_META_QUIT_ACK
void 	MSG_META_QUIT_ACK(
        word 		quitLevel,
        word 		abortFlag);

This message is sent to a Process object in response to a MSG_META_QUIT . The Process object handles this message by continuing the quit sequence.

Source: Any object having received MSG_META_QUIT

Destination: OD passed in MSG_META_QUIT .

Parameters: quitLevel QuitLevel acknowledging (if responding to a process).

abortFlag
(non-zero if you want to abort the quit).

Return: Nothing.

Warnings: For processes that subclass MSG_META_QUIT , you cannot abort the quit at the QL_DETACH stage or later.

MSG_META_FINISH_QUIT
void 	MSG_META_FINISH_QUIT(
        Boolean		abortFlag);

This message is sent to the object that initiated MSG_META_QUIT and has received MSG_META_QUIT_ACK from each party notified. This message informs the object that it has finished sending out all MSG_META_QUIT messages and can go on with quitting (or aborting the quit if that is the case).

Source: Object that initiated MSG_META_QUIT.

Destination: Any object.

Parameters: abortFlag (non-zero if you want to abort the quit).

MetaClass: 2.5 Utility Messages: Event Messages

These messages are used to send classed events to other objects. A classed event is typically an event stored earlier with the Goc keyword @record .

MSG_META_DISPATCH_EVENT
Boolean	MSG_META_DISPATCH_EVENT(
        AsmPassReturn 	*retVals,
        EventHandle 	eventHandle,
        MessageFlags msgFlags););

This message causes an object to @send or @call a message of another object. This is useful for getting one object run by a different thread to call yet another object or to send a reply to the first object. This message can cause complex synchronization problems if not used with extreme care.

Source: Unrestricted.

Destination: Any object.

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: retValue structure to hold return values.

eventHandle
Event which will be sent.
msgFlags
flags which will determine how message is sent.

Return: If MF_CALL specified, then carry flag return value will be returned.

Structures:

 typedef struct {
    word 	ax;
    word 	cx;
    word 	dx;
    word 	bp;
} AsmPassReturn;
MSG_META_SEND_CLASSED_EVENT
void	MSG_META_SEND_CLASSED_EVENT(
        EventHandle	 	event,
        TravelOption 		whereTo);

This message sends a previously recorded classed event to a certain type of destination defined in the TravelOption argument whereTo .

This message's interesting behavior is actually added by the User Interface, which defines GenTravelOption types. The default behavior provided here in MetaClass is to destroy the event if TO_NULL is passed, else to deliver the event to itself if it is capable of handling it (the object is a member of the class stored with the event). The event is always freed, whether or not it is deliverable.

MetaClass recognizes the following TravelOption values:

TO_NULL
TO_SELF
TO_OBJ_BLOCK_OUTPUT
TO_PROCESS
TO_OBJ_BLOCK_OUTPUT sends the event to the object block's output set in its block header.

Source: Unrestricted.

Destination: Any object.

Interception: By default, MetaClass handlers deal with just the most primitive of the TravelOption values. Object classes can add new TravelOption types, but must then intercept this message to implement them (calling the superclass if it doesn't recognize the TravelOption passed).

Parameters: event Classed event, probably created using @record .

whereTo
TravelOption describing target of message.

Return: Nothing.

MSG_META_GET_OBJ_BLOCK_OUTPUT
optr	MSG_META_GET_OBJ_BLOCK_OUTPUT();

This message returns the output optr of an object block that contains the object sent the message.

Source: Unrestricted.

Destination: Any object (except a process object).

Return: Optr of the block's output field.

Interception: Generally not intercepted.

MSG_META_SET_OBJ_BLOCK_OUTPUT
void	MSG_META_SET_OBJ_BLOCK_OUTPUT(
        optr		output);

This message sets the object block output--the block containing the object sent the message-- to the passed optr.

Source: Unrestricted.

Destination: Any object (except a process object).

Parameters: output Optr of the object to act as the block's output.

Interception: Generally not intercepted.

MSG_META_GET_OPTR
optr	MSG_META_GET_OPTR();

This message returns the object's optr. This is useful when combined with MSG_GEN_GUP_CALL_OBJECT_OF_CLASS to get the optr of an object of a given class somewhere up in a Generic Tree.

Note: MSG_GEN_GUP_CALL_OBJECT_OF_CLASS dies if an object of the class doesn't exist. Use MSG_GEN_GUP_TEST_FOR_OBJECT_OF_CLASS before using MSG_GEN_GUP_CALL_OBJECT_OF_CLASS if there is some question as to whether an object of a given class exists.

Source: Unrestricted.

Destination: Any object.

Interception: Unlikely.

Parameters: None.

Return: The object's optr.

MSG_META_GET_TARGET_AT_TARGET_LEVEL
void	MSG_META_GET_TARGET_AT_TARGET_LEVEL(
        GetTargetParams *retValue,
        TargetLevel level);

This message returns the GetTargetParams structure containing, among other things, the current target object at a given target level. The MetaClass handler simply returns information about the current object since it is assumed to be the current target. GenClass and VisClass handlers will handle this message and pass it down the travel hierarchy. If you intercept this message, you should correctly pass this message down as well, either explicitly or by calling the superclass. See the Input chapter, for information on target.

Source: Unrestricted.

Destination: Any object.

Interception: Must be handled by target nodes to correctly pass the request on down to the next target below current node in hierarchy.

Parameters: level Zero for leaf, otherwise TargetLevel , as defined by UI.

retValue
Structure to hold return value.

Return: Nothing returned explicitly.

retValue
Filled with return values.

Structures:

  typedef struct {
    ClassStruct 				*GTP_class;
    optr 				GTP_target;
} GetTargetParams;

MetaClass: 2.6 Utility Messages: Variable Data Messages

Variable data is instance data that can appear or not appear within the object's instance chunk. For information on variable data and how these three messages are used, see the GEOS Programming chapter.

MSG_META_ADD_VAR_DATA
void	MSG_META_ADD_VAR_DATA(@stack
        word	dataType,
        word	dataSize,
        word	*data)

This message adds a variable data type to the recipient object's instance data. If the variable data field was already present, this will change its value. This is useful for adding hints to generic objects at run-time.

If it is possible that you are adding the object's first/only field of variable data, set the object's OCF_VARDATA_RELOC flag by means of the ObjSetFlags() routine or MSG_META_SET_FLAGS.

Note that the object will be marked dirty even if nothing was changed.

NOTE: The dataType should have VDF_SAVE_TO_STATE set as desired. VDF_EXTRA_DATA is ignored; it will be set correctly by this routine.

Source: Unrestricted.

Destination: Any object.

Interception: Generally not intercepted; default MetaClass handling performs the desired function.

Parameters: dataType Data type (e.g. ATTR_PRINT_CONTROL_APP_UI).

dataSize
Size of data, if any.
data
If no extra data, NULL. If dataSize is non-zero, then this may be a pointer to data to initialize data with.

Return: Nothing. Object marked dirty even if data type already exists.

MSG_META_DELETE_VAR_DATA
Boolean	MSG_META_DELETE_VAR_DATA(
        word	dataType);

This message removes a particular variable data entry from the recipient object's instance data. This is useful for removing hints from generic objects at run-time.

Source: Unrestricted.

Destination: Any object.

Interception: Generally not intercepted; default MetaClass handling performs the desired function.

Parameters: dataType Data type to delete. VarDataFlags ignored.

Return: Returns false if data deleted, true if data was not found. Object marked dirty if data type found and deleted.

MSG_META_INITIALIZE_VAR_DATA
word	MSG_META_INITIALIZE_VAR_DATA(
        word	dataType);

This message is sent to an object any time the ObjVarDerefData() routine is called and the data type is not found. It should be subclassed by any object that defines a variable type that will be used with ObjVarDerefData() . The object must create and initialize the data and return its offset.

Sent to an object having a variable data entry which code somewhere is attempting to access via ObjVarDerefData() . It is the object that defines the variable data entry type's responsibility to create the data entry and initialize it at this time, and to return a pointer to the extra data (if any), as returned by ObjVarAddData() .

Source: ObjVarDerefData() routine. Should not be used as a replacement for ObjVarAddData() , or MSG_ADD_VAR_DATA_ENTRY , but may be used any time code is ready to access a particular piece of variable data instance data, knows that the variable data has not yet been created, and wishes to ensure that it does exist.

Destination: Any object stored in an LMem Object block.

Interception: Required by any class which defines a variable data entry type that needs to be initialized before usage. Objects handling this message should first compare the passed data type against variable data types it understands, and pass any unknown types onto the superclass for handling.

Parameters: Variable data type.

Return: Offset to extra data created (or, if no extra data, the start of data entry plus the size of VarDataEntry ). Normally, this would just be the offset returned by the call to ObjVarAddData() .

MSG_META_GET_VAR_DATA
word MSG_META_GET_VAR_DATA( /* returns size of data returned in buf;
              * -1 if not found */
        word 	dataType,
        word 	bufSize,
        void 	*buf);

This message fetches variable data of a given type from an object.

Source: Unrestricted.

Destination: Any object.

Interception: Generally not intercepted; default MetaClass handling performs the desired function.

Parameters: dataType The variable data category to return.

bufSize
Size available to return data.
buf
Pointer to buffer to hold returned data.

Return: The size of the data returned. If the vardata entry was not found, then message will return -1.

buf
Filled with vardata's data, if any.

MetaClass: 2.7 Utility Messages: Notification Messages

These messages are used by the various notification mechanisms throughout the system.

MSG_META_NOTIFY
void	MSG_META_NOTIFY(
        ManufacturerID manufID,
        word	notificationType,
        word 	data);

This message notifies the recipient that some change or action has taken place. The object must have registered for the notification. The type of change that has occurred depends on the notificationType argument.

One word of notification data is allowed, but this should not reference a handle which must at some point be destroyed. See MSG_META_NOTIFY_WITH_DATA_BLOCK for such requirements.

Source: Unrestricted.

Destination: Any object, or any of the GCNListSend() routines.

Interception: No general requirements, though particular notification types may place restrictions or requirements on such handling.

Parameters: manufID Manufacturer ID associated with notification type.

notificationType
What sort of notification is being announced. This value depends on the Manufacturer ID for that notification list. A list of valid types for the Geoworks Manufacturer ID is indicated by the GeoWorksNotificationType enumerated type.
data
One word of data, which will be placed in a vardata field. If more than one word is necessary, use MSG_META_NOTIFY_WITH_DATA_BLOCK , below.

Return: Nothing.

MSG_META_NOTIFY_WITH_DATA_BLOCK
void	MSG_META_NOTIFY_WITH_DATA_BLOCK(
        ManufacturerID manufID,
        word	notificationType,
        MemHandle data);

This message acts like MSG_META_NOTIFY , but it also carries a handle of a block of data. It is absolutely imperative that if this message is subclassed, the object call its superclass in the handler.

The data block must be set up to use the Block "reference count" mechanism, i.e. be sharable and initialized with MemInitRefCount() . Details on the count are noted below.

Source: Unrestricted.

Destination: Any object, or any of the GCNListSend... routines

Interception: Message must eventually arrive at the MetaClass handler, with the handle to the data block with the reference count intact, in order for the block to be freed when no longer referenced. Failure to do so will result in garbage being left on the heap, which will kill the system with repetitive occurrences.

Parameters: manufID Manufacturer ID associated with notification type.

notificationType
What sort of notification is being announced.
data
SHARABLE data block having a "reference count" initialized via MemInitRefCount() .

NOTE on data block reference counts:
The reference count should hold the total number of references of this data block. This count should be incremented before sending a message holding a reference to this block (using MemIncRefCount() ). Any messages passing such reference either must have a MetaClass handler which decrements this count and frees the block if it reaches zero or must call MemDecRefCount() (which does exactly that). GCNListSend() and similar functions add in the number of optrs in any list to which a message referring to this block is sent. Thus, when creating a block which will only be sent using GCNListSend() , this count should be initialized to zero. If the block is to be sent to one or more objects or GCNListSend() calls, the calling routine should call MemIncRefCount() before making the calls, being sure to call MemIncRefCount() additionally for any objects that the message is sent to, and then call MemDecRefCount () after the calls, to balance the increment call at the start.

Return: Nothing.

Warnings: This message must eventually reach the default MetaClass handler, so that the block can be freed when no longer referenced.

MSG_META_GCN_LIST_ADD
Boolean	MSG_META_GCN_LIST_ADD(@stack
        optr			dest,
        word 		listType,
        ManufacturerID 		listManuf);

This message adds the passed object to a particular notification list. It returns true if the object was successfully added, false otherwise. This message is the equivalent of GCNListAdd() for individual object GCN list.

Source: Unrestricted.

Destination: Object providing GCN services.

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: dest Object to be added to list.

listType
GCNListType to add object to.
listManuf
Manufacturer ID associated with GCN list.

Return: Returns true if optr added, false otherwise.

MSG_META_GCN_LIST_REMOVE
Boolean	MSG_META_GCN_LIST_REMOVE(@stack
        optr	dest,
        word	listType,
        ManufacturerID listManuf);

This message removes the passed object from a particular notification list. It returns true if the object was successfully removed, false otherwise. This message is the equivalent of GCNListRemove() for an individual object's GCN list.

Source: Unrestricted.

Destination: Object providing GCN services.

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: dest Object to be removed from list.

listType
Which list to remove object from.
listManuf
Manufacturer ID associated with GCN list.

Return: Returns true if optr found and removed, otherwise returns false .

MSG_META_GCN_LIST_SEND
void	MSG_META_GCN_LIST_SEND(@stack
        GCNListSendFlags 		flags,
        EventHandle		event,
        MemHandle 		block,
        word		listType,
        ManufacturerID 		listManuf);

This message sends the given event to all objects in a particular notification list. The event will be freed after being sent. This message is the equivalent of GCNListSend() for an individual object GCN list.

Parameters: flags Flags to pass on to GCNListSend().

event
Classed event to send to the list.
block
Handle of extra data block, if used, else NULL. This block must have a reference count, which may be initialized with MemInitRefCount() and incremented for any new usage with MemIncRefCount() . Methods in which they are passed are considered such a new usage, and must have MetaClass handlers which call MemDecRefCount() .
listType
Which GCN list to send event to.
listManuf
Manufacturer ID associated with GCN list.

Return: Nothing.

Structures:

  typedef WordFlags GCNListSendFlags;
/* These flags may be combined using | and &:
    GCNLSF_SET_STATUS,
    GCNLSF_IGNORE_IF_STATUS_TRANSITIONING */
GCNLSF_SET_STATUS
Additionally saves the message as the list's current "status." The "status" message is automatically sent to any object adding itself to the list at a later point in time.
GCNLSF_IGNORE_IF_STATUS_TRANSITIONING
Optimization bit used to avoid lull in status when transitioning between two different sources--such as when the source is the current target object, and one has just lost, and another may soon gain, the exclusive. (The bit should be set only when sending the "null," "lost," or "not selected" status, as this is the event that should be discarded if another non-null status comes along shortly). Implementation is not provided by the kernel primitive routines, which ignore this bit, but may be provided by objects managing their own GCN lists. GenApplication responds to this bit by delaying the request until after the UI and application queues have been cleared, and then only sets the status as indicated if no other status has been set since the first request. Other objects may use their own logic to implement this optimization as is appropriate. Mechanisms which can not tolerate the delayed status setting nature of this optimization, or require that all changes are registered, should not pass this bit set.
MSG_META_GCN_LIST_FIND_ITEM
Boolean	MSG_META_GCN_LIST_FIND_ITEM(@stack
        optr		dest,
        word		listType,
        ManufacturerID		listManuf);

This message checks whether an object is on a particular GCN list.

Source: Unrestricted.

Destination: Any object providing GCN services.

Parameters: dest Optr of object that we are checking.

listType
GCNListType .
listManuf
ManufacturerID .

Return: true if object is on the GCN list.

Interception: Unnecessary.

MSG_META_GCN_LIST_DESTROY
void	MSG_META_GCN_LIST_DESTROY();

This message completely destroys the GCN setup for the caller. It frees all GCN lists, cached events, and overhead data storage. This should only be used when the object is being freed. You will likely never handle or call this message.

Source: Object providing GCN services, often in handler for MSG_META_FINAL_OBJ_FREE .

Destination: Self.

Interception: Unnecessary, as MetaClass does the right thing.

Parameters: Nothing.

Return: Nothing.

MSG_META_NOTIFY_OBJ_BLOCK_INTERACTIBLE
void	MSG_META_NOTIFY_OBJ_BLOCK_INTERICTABLE(
        MemHandle objBlock);

This message is sent to an object block's output object when the block changes from being not in-use to being in-use. An object may handle this message to monitor changes of in-use status.

Source: Kernel.

Destination: Object which is set as the output of an object block resource either by ObjBlockSetOutput() , or by being pre-defined in an application resource.

Interception: May be intercepted to learn about change in object block interactable status. No default handling is provided, though you may wish to pass the message onto the superclass in case it is interested in this data as well.

Parameters: objBlock Handle of object block.

Return: Nothing.

MSG_META_NOTIFY_OBJ_BLOCK_NOT_INTERACTIBLE
void	MSG_META_NOTIFY_OBJ_BLOCK_NOT_INTERACTIBLE(
        MemHandle objBlock);

This message is sent to an object block's output object when the block changes from being not in-use to being in-use. An object may handle this message to monitor changes of in-use status.

Source: Kernel.

Destination: Object which is set as the output of an Object Block resource either by ObjBlockSetOutput() , or by being pre-defined in an application resource.

Interception: May be intercepted to learn about change in object block interactable status. No default handling is provided, though you may wish to pass the message onto the superclass in case it is interested in this data as well.

Parameters: objBlock Handle of object block.

Return: Nothing.

MSG_META_VM_FILE_DIRTY
void	MSG_META_VM_FILE_DIRTY(
        FileHandle file);

This message is sent to all processes that have a VM file open when a block in the file becomes marked dirty for the first time. This is useful if many processes may be sharing a VM file. The VM file must be marked VMA_NOTIFY_DIRTY in its attributes.

Source: Kernel VM code.

Destination: ProcessClass object.

Interception: May be intercepted at process to do whatever is desired on this occurrence of this event. Default behavior in GenProcessClass sends notification to the current model GenDocumentGroupClass object.

Parameters: file Handle open to the VM file, from the receiving process's perspective.

Return: Nothing.

MetaClass: 2.8 Utility Messages: Options Messages

These messages are used by the User Interface when working with the GEOS.INI files. You will probably never need to subclass or call these messages.

MSG_META_SAVE_OPTIONS
void	MSG_META_SAVE_OPTIONS();

This message saves an object's options to the .INI file for an object. It is sent via the UI's active list mechanism.

Source: Unrestricted.

Destination: GenApplication object, which in turn broadcasts to everything on list of objects having options needing to be saved

Interception: Objects having options to save should intercept this. Superclass should be called in case any of the superclasses needs similar notification.

MSG_META_LOAD_OPTIONS
void	MSG_META_SAVE_OPTIONS();

This message loads the object's setting from the .INI file.

Source: Unrestricted.

Destination: Any object.

Interception: Any object that should load its options should intercept this. Behavior is currently implemented for Generic UI objects.

MSG_META_RESET_OPTIONS
void	MSG_META_RESET_OPTIONS();

This message resets the object's settings from the .INI file to their initial state.

Source: Unrestricted. Sent to all objects on the GAGCNLT_SELF_LOAD_OPTIONS and GAGCNLT_STARTUP_LOAD_OPTIONS lists.

Destination: Any object.

Interception: Any object that wants to reset its options should intercept this. Behavior is currently implemented for Generic UI objects.

MSG_META_GET_INI_CATEGORY
void	MSG_META_GET_INI_CATEGORY(
        char	*buf);

This message returns the .INI file category of the object.

Source: Unrestricted, though generally self.

Destination: Object having options.

Interception: Default handler walks up tree, eventually finding name of application. Can be intercepted at any level to change the category for a branch.

Parameters: buf The buffer for .INI category string. This buffer size cannot store more than 64 bytes.

Return: Nothing returned explicitly.

buf
String filled with category string.

MetaClass: 2.9 Utility Messages: Suspending and Unsuspending

MSG_META_SUSPEND and MSG_META_UNSUSPEND work together to allow objects to optimize recalculation when doing a series of actions. These messages are implemented by various objects in the system (such as the text object and the grobj body). This mechanism is used by GenControlClass to optimize recalculation stemming from multiple controller outputs.

An object typically implements these messages by keeping a suspend count and a record of the calculations that were aborted because the object was suspended. When the suspend count reaches zero, the object will perform the calculations.

An object implementing this mechanism should always call its superclass since multiple class levels could be implementing this mechanism.

MSG_META_SUSPEND
void 	MSG_META_SUSPEND();

Suspend calculation in an object.

Source: Normally sent by a controller object but can be sent by anything.

Destination: Any object that implements the mechanism described above.

Interception: An object that wants to implement the mechanism described above.

Parameters: None.

Return: Nothing.

MSG_META_UNSUSPEND
void 	MSG_META_UNSUSPEND();

Unsuspend calculation in an object.

Source: Normally sent by a controller object but can be sent by anything.

Destination: Any object that implements the mechanism described above.

Interception: An object that wants to implement the mechanism described above.

Parameters: None.

Return: Nothing.

MetaClass: 2.10 Utility Messages: Help Files

MSG_META_GET_HELP_FILE, MSG_META_SET_HELP_FILE, MSG_META_BRING_UP_HELP

These help messages are contained within MetaClass to allow help files within any object in the GEOS system.

MSG_META_GET_HELP_FILE
void	MSG_META_GET_HELP_FILE(
        char		*buf);

This message returns the name of the help file attached to the object sent this message. If no help file is found, the default MetaClass handler walks up the tree.

Source: Unrestricted.

Destination: Object in object tree containing help.

Parameters: buf Pointer to buffer to store the help file name.

Return: Buffer filled in.

Interception: The default handler walks up the tree; you may intercept to change the help file for a branch.

MSG_META_SET_HELP_FILE
void	MSG_META_SET_HELP_FILE(
        char		*buf);

This message sets the help file for the object sent this message.

Source: Unrestricted, though generally an object sends this to itself.

Destination: An object within a tree.

Parameters: buf Pointer to help file name. This buffer's size must be at least FILE_LONGNAME_BUFFER_SIZE.

Interception: Generally not intercepted.

MSG_META_BRING_UP_HELP
void	MSG_META_BRING_UP_HELP();

This message finds a help context for the current object tree and sends a notification to bring up help with that context.

Source: Unrestricted, though generally an object sends this to itself.

Destination: An object within a tree.

Interception: The default handler for this message walks up the visual tree (not the generic tree) eventually finding a GenClass object with ATTR_GEN_HELP_CONTEXT. You may intercept at any level to change the help context for a branch.

MetaClass: 3 Exported Message Ranges

MetaClass exports a number of ranges of messages for use by its subclasses (such as GenClass and VisClass ) for various purposes. In most cases, you will not need to use any of these ranges for your own messages. The names of these ranges, however, are listed below. For information on exporting and importing message ranges, see the GEOS Programming chapter.

  • MetaWindowMessages
    These messages alert a window's input object and output descriptor to certain important events. For example, MSG_META_EXPOSED announces that part of a window has been exposed and needs to be redrawn.
  • MetaInputMessages
    These are very low-level messages, and will only be used by those geodes which wish to circumvent the Input Manager. To work with the Input Manager correctly, see the selection messages in the MetaUIMessages range.
  • MetaUIMessages
    These messages may be used by objects which will interact with the UI. While messages for working with the Input Manager and Clipboard are detailed in this chapter, information on other messages may be found in the Documents chapter.
  • MetaSpecificUIMessages
    These messages are internal.
  • MetaApplicationMessages
    These messages don't have any meaning attached to them; no class defined in the system or any library has a handler for any of these messages. Any object class defined within an application may have a handler for any of these messages.
    This message range was set up so that two or more classes defined within an application could agree on some message numbers.
    Thus, your application could contain the header listed below, and then write handlers for the message in two completely unrelated application-defined classes:
 @importMessage MetaApplicationMessages,
    type0 MSG_MYAPP_DO_SOMETHING(
        type1		arg1,
        type2		arg2);
/* ... */
@method MyProcessClass, MSG_MYAPP_DO_SOMETHING
/* ...Insert Handler here */
/* ... */
@method MyDocumentClass, MSG_MYAPP_DO_SOMETHING
/* ...Insert Handler here */
  • MetaGrObjMessages
    This message range is reserved for notification messages associated with the graphic object library.
  • MetaPrintMessages
    For information on these messages, see the Printing chapter.
  • MetaSearchSpellMessages
    These messages are sent out by the SearchReplace and Spell controllers. For information on these messages, see the Text Objects chapter.
  • MetaGCNMessages
    There are several system-defined General Change Notification lists which objects may belong to. This message range holds those messages which will be sent to objects on system-defined lists.
  • MetaTextMessages
    See the Text Objects chapter.
  • MetaStyleMessages
    See .
  • MetaColorMessages
    See the Controllers chapter.
  • MetaFloatMessages
    These messages are sent out by the FloatFormat controller. For information on these messages, see the Controllers chapter.
  • MetaIACPMessages
    These messages are used to communicate to other objects using the Inter-Application Communication Protocol. IACP is discussed in the Applications and Geodes chapter.

Window Messages

Input Messages

UI Messages

Standard GCN Messages

IACP Meta Messages

MetaClass: 3.1 Exported Message Ranges: Window Messages

Because many objects, both Generic UI objects and others, work together to control the behavior of the system windows, a number of messages have been set up in an exported range so that they may be shared among classes.

Window Update Messages

The following messages are sent to objects responsible for updating views, and if you subclass content objects, you may wish to intercept these messages.

MSG_META_EXPOSED
@importMessage MetaWindowMessages, void MSG_META_EXPOSED(
        WindowHandle win);

This message is sent to a Window's exposure object any time a portion of the window is visible on screen, has become invalid, and needs to be redrawn. Correct response is to create a GState on the passed window, call GrBeginUpdate() with it, redraw the window, and finish by calling GrEndUpdate() and freeing the GState. Drawing will be clipped to the invalid area of the window at the time that GrBeginUpdate() is called. Invalidations occurring during the redraw will result in the reduction in the size of the update region, and result in another MSG_META_EXPOSED being generated, to repair the new "invalid" area.

Source: Window system.

Destination: Individual window's exposure object; View's output descriptor.

Interception: Required, in order for window to be properly updated. Note that VisContentClass provides default handler which creates GState, calls GrBeginUpdate() , calls MSG_VIS_DRAW on itself, then calls GrEndUpdate() .

Parameters: win Window handle which may be passed to GrCreateGState() .

Return: Nothing.

Messages Sent to Objects Further Up the Input Hierarchy

The following messages are part of the high level windowing mechanism. Most of these messages are passed around at the GenSystem level, and most object classes defined by applications will not intercept them. Instead, system objects will intercept these messages and pass appropriate messages on to application objects.

MSG_META_WIN_CHANGE
@importMessage MetaWindowMessages, void 	MSG_META_WIN_CHANGE();

Sent to the System Input Object (Normally the UI's GenSystem obj), when the pointer position, as passed to the window system in calls to WinMovePtr() , has possibly moved outside of the window that it was in. The object should respond by calling WinChangeAck() , which will cause enter and leave events to be generated for all windows affected by the pointer's change.

Source: Window system ( WinMovePtr() ).

Destination: System Input object (usually the GenSystem object).

Interception: Must be handled via call to WinChangeAck() .

Parameters: None.

Return: Nothing.

MSG_META_IMPLIED_WIN_CHANGE
@importMessage MetaWindowMessages, void 	MSG_META_IMPLIED_WIN_CHANGE(
        optr 		inputObj,
        WindowHandle 		ptrWin);

Sent to the System Input Object (Normally the UI's GenSystem obj) in response to a call to WinChangeAck() , to inform it which window the mouse has moved into. The system input object is responsible for passing this message on to the Input object of affected geodes.

Source: Window system ( WinChageAck() ).

Destination: Initially System Input Object (usually the GenSystemClass object), though is relayed on to Geode Input Object (usually a GenApplicationClass object).

Interception: May be intercepted to learn when an implied window change has occurred, but subclasses should not change any default functionality.

Parameters: inputObj Window which has implied grab (or zero if there is no implied grab).

ptrWin
Window that pointer is in.

Return: Nothing.

MSG_META_RAW_UNIV_ENTER
@importMessage MetaWindowMessages, void 	MSG_META_RAW_UNIV_ENTER(
        optr 		inputObj,
        WindowHandle 		ptrWin);

This message is generated by the window system whenever the mouse crosses into a window. This message is sent to the window's input object. This is sent whenever the mouse pointer crosses a window boundary, regardless of any existing window grab.

Source: Window system ( WinChangeAck() ).

Destination: Initially System input object (usually the GenSystemClass object), though is relayed on to Geode Input Object (usually a GenApplicationClass object), and finally onto individual Window's Input Object.

Interception: May be intercepted to track current status of whether mouse is within window or not. Specific UIs rely on these messages to control auto-raise, click-to-raise arming, and correct implied and active mouse grab interaction behavior.

Parameters: inputObj Input Object of window method refers to.

ptrWin
Window that method refers to.

Return: Nothing.

MSG_META_RAW_UNIV_LEAVE
@importMessage MetaWindowMessages, void 	MSG_META_RAW_UNIV_LEAVE(
        optr 		inputObj,
        WindowHandle 		ptrWin);

This message is generated by the window system whenever the mouse crosses out of a window. This message is sent to the window's input object. This is sent whenever the mouse pointer crosses a window boundary, regardless of any existing window grab.

Source: Window system ( WinChangeAck() ).

Destination: Initially System Input Object (usually the GenSystemClass object), though is relayed on to Geode Input Object (usually a GenApplicationClass object), and finally onto individual Window's Input Object.

Interception: May be intercepted to track current status of whether mouse is within window or not. Specific UIs rely on these messages to control auto-raise, click-to-raise arming, and correct implied and active mouse grab interaction behavior.

Parameters: inputObj Input Object of window method refers to.

ptrWin
Window that method refers to.

Return: Nothing.

MetaClass: 3.2 Exported Message Ranges: Input Messages

These messages contain "raw" input events; events which have not yet been processed by the Input Manager. Most applications intercepting input events should intercept events which have been so processed, as described in UI Messages .

MSG_META_MOUSE_BUTTON
@importMessage MetaInputMessages, void MSG_META_BUTTON(
        word 	xPosition,
        word 	yPosition,
        word 	inputState);

This message is sent out on any button press or release.

Parameters: xPosition X-coordinate of mouse event.

yPosition
Y-coordinate of mouse event.
inputState
High byte is a ShiftState; low byte is ButtonInfo.

Return: Nothing.

MSG_META_MOUSE_PTR
@importMessage MetaInputMessages, void MSG_META_PTR(
        word 	xPosition,
        word 	yPosition,
        word 	inputState);

This message is sent out on any mouse movement.

Parameters: xPosition X-coordinate of mouse event.

yPosition
Y-coordinate of mouse event.
inputState
High byte is a ShiftState; low byte is ButtonInfo.

Return: Nothing.

MSG_META_KBD_CHAR
@importMessage MetaInputMessages, void MSG_META_KBD_CHAR(
        word 	character,
        word 	flags, /* low byte = CharFlags, high byte = ShiftState */
        word 	state);/* low byte = ToggleState, high byte = scan code */

This is the message sent out on any keyboard press or release. To determine whether the message is in response to a press or a release, check the CF_RELEASE bit of the flags field.

Parameters: character Low byte contains Char value of incoming character.

flags
High byte is ShiftState ; low byte is CharFlags .
state
High byte is raw PC scan code; low byte is ToggleState .

Return: Nothing.

MSG_META_MOUSE_DRAG
@importMessage MetaInputMessages, void MSG_META_MOUSE_DRAG(
        word 	xPosition,
        word 	yPosition,
        word 	inputState);

This is a very low-level message, signalling that the user is dragging the mouse.

Parameters: xPosition X-coordinate of mouse event.

yPosition
Y-coordinate of mouse event.
inputState
High byte is a ShiftState ; low byte is ButtonInfo .

Return: Nothing.

MetaClass: 3.3 Exported Message Ranges: UI Messages

The User Interface generates many messages which may alert objects to events which will allow them to work with the user. These events include those generated from the actions of input devices and clipboard-related events.

Clipboard Messages

The following messages are used to implement common clipboard functions.

MSG_META_CLIPBOARD_CUT
@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_CUT();

This message is sent to an object which is supposed to be the destination of a clipboard operation. MSG_META_CLIPBOARD_CUT should register the current selection with the UI as the new clipboard item, but also delete the current selection.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

MSG_META_CLIPBOARD_COPY
@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_COPY();

This message is sent to an object which is supposed to be the destination of a clipboard operation. MSG_META_CLIPBOARD_COPY should be handled by registering the current selection with UI as the new clipboard item.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

MSG_META_CLIPBOARD_PASTE
@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_PASTE();

This message is sent to an object which is supposed to be the destination of a clipboard operation. MSG_META_CLIPBOARD_PASTE should replace the current selection with the current clipboard item, which can be obtained from the UI.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK
@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK(
        ClipboardQuickNotifyFlags flags);

This message is sent to the source of a quick transfer item when a potential destination provides feedback to the user indicating whether a move, a copy or no operation will occur. The default behavior is determined by the destination, but the user may be able to override with the MOVE or COPY override keys.

Source: Sent by quick-transfer mechanism.

Destination: Sent to optr passed to ClipboardStartQuickTransfer() . Handled if the quick-transfer source needs to know what quick-transfer operation a potential destination will perform. Handler need not call superclass.

Interception: Message sent directly to destination, no need to intercept.

Parameters: flags Quick transfer cursor action specified by source (see ClipboardSetQuickTransferFeedback() ).

Return: Nothing.

MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED
@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK(
        ClipboardQuickNotifyFlags 	flags);

This message is sent to the source of a quick transfer item when the operation is completed. The ClipboardQuickNotifyFlags are set by any MSG_META_END_MOVE_COPY handler. This is only sent out if the source requests notification with the CQTF_NOTIFICATION flag passed to ClipboardStartQuickTransfer() .

Source: Sent by quick-transfer mechanism.

Destination: Sent to optr passed to ClipboardStartQuickTransfer() . Handled if the quick-transfer source needs to know what quick-transfer operation was performed. Handler need not call superclass.

Interception: Message sent directly to source of transfer; no need to intercept.

Parameters: flags Quick transfer cursor action specified by source (see ClipboardSetQuickTransferFeedback() ).

Return: Nothing.

MSG_META_CLIPBOARD_NOTIFY_TRANSFER_ITEM_FREED
@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_NOTIFY_TRANSFER_ITEM_FREED(
        VMFileHandle 	itemFile,
        VMBlockHandle 	itemBlock);

Sent to all ODs in Transfer Notify List to help maintain integrity of transfer items from VM files other than the UI's transfer VM file. Only sent if VM file handle of transfer item that is being freed is different from UI's transfer VM file handle. If a transfer item from a VM file other than the UI's transfer VM file is registered, the VM blocks in that transfer item cannot be freed and the VM file cannot be closed until notification is sent saying that the transfer item has been freed. Registrars of such transfer items should keep track of the VM file handle and VM block handle of the item to check against the info sent by this message.

Source: Sent by the clipboard mechanism.

Destination: Sent to optrs on transfer notification list, added with ClipboardAddToNotificationList() . Handled if clipboard changes need to be monitored.

Interception: Unlikely.

Parameters: itemFile File containing the transfer item.

itemBlock
Block containing the transfer item.

Return: Nothing.

MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED
@importMessage MetaUIMessages, void 	MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED();

Sent to all ODs in Transfer Notify List to help with updating of Cut, Copy, and Paste button states. Recipients can call ClipboardQueryItem() to check if the new normal transfer item contains formats that the recipient supports. If not, Paste button can be disabled.

Source: Sent by the clipboard mechanism, relayed by GenEditControl.

Destination: Sent to optrs on transfer notification list, added with ClipboardAddToNotificationList() . Handled if clipboard changes need to be monitored.

Interception: Unlikely.

Parameters: None.

Return: Nothing.

Undo Messages

These messages implement the "undo" mechanism which allows objects to store a chain of actions which can later be undone.

MSG_META_UNDO
@importMessage MetaUIMessages, void 	MSG_META_UNDO(
        AddUndoActionStruct 	*data);

This message is sent to an object which is supposed to be the destination of a clipboard operation.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

MSG_META_UNDO_FREEING_ACTION
@importMessage MetaUIMessages, void 	MSG_META_UNDO_FREEING_ACTION(
        AddUndoActionStruct 	*data);

This message is sent to an object which is supposed to be the destination of a clipboard operation. This message is used to undo those actions which may free an important block of memory.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

MSG_META_SELECT_ALL
@importMessage MetaUIMessages, void 	MSG_META_SELECT_ALL();

This message is sent to an object which is supposed to be the destination of a clipboard operation.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

MSG_META_DELETE
@importMessage MetaUIMessages, void 	MSG_META_DELETE();

This message is sent to an object which is supposed to be the destination of a clipboard operation.

Source: Sent by anyone to perform clipboard operation.

Destination: Object which will support clipboard operations. By default, a GenEditControl sends this message to the targeted object.

Interception: May be intercepted to add clipboard support to existing class that doesn't currently have clipboard support or to enhance or replace functionality of object that does support the clipboard.

Parameters: None.

Return: Nothing.

Input Messages

These are perhaps the most often intercepted messages, allowing objects to detect input events.

MSG_META_GAINED_MOUSE_EXCL
@importMessage MetaUIMessages, void 	MSG_META_GAINED_MOUSE_EXCL();

The object will receive this message when it has received the mouse exclusive.

MSG_META_LOST_MOUSE_EXCL
@importMessage MetaUIMessages, void 	MSG_META_LOST_MOUSE_EXCL();

The object will receive this message when it has lost the mouse exclusive.

MSG_META_GAINED_KBD_EXCL
@importMessage MetaUIMessages, void 	MSG_META_GAINED_KBD_EXCL();

The object will receive this message when it has received the keyboard exclusive.

MSG_META_LOST_KBD_EXCL
@importMessage MetaUIMessages, void 	MSG_META_LOST_KBD_EXCL();

The object will receive this message when it has lost the keyboard exclusive.

MSG_META_GAINED_PRESSURE_EXCL
@importMessage MetaUIMessages, void 	MSG_META_GAINED_PRESSURE_EXCL();

The object will receive this message when it has received the pressure exclusive, meaning it will get certain low-level mouse events.

MSG_META_LOST_PRESSURE_EXCL
@importMessage MetaUIMessages, void 	MSG_META_LOST_PRESSURE_EXCL();

The object will receive this message when it has lost the pressure exclusive.

MSG_META_GAINED_DIRECTION_EXCL
@importMessage MetaUIMessages, void 	MSG_META_GAINED_DIRECTION_EXCL();

The object will receive this message when it has received the direction exclusive, meaning it will get certain low-level mouse events.

MSG_META_LOST_DIRECTION_EXCL
@importMessage MetaUIMessages, void 	MSG_META_LOST_DIRECTION_EXCL();

The object will receive this message when it has lost the direction exclusive.

Hierarchical Messages

These messages allow object to detect changes in the makeup of the three hierarchies which affect the paths of input and actions within the system: the Focus, Target, and Model hierarchies. These hierarchies are discussed in the Input chapter.

MSG_META_GRAB_FOCUS_EXCL
@importMessage MetaUIMessages, void 	MSG_META_GRAB_FOCUS_EXCL();

May be passed to any visible or generic object to cause it to become the active focus within its focus level. The leaf object in the hierarchy which gains the focus exclusive will automatically be given the keyboard exclusive, and will thereby receive MSG_META_KBD_CHAR events that follow.

Commonly sent to text objects and other gadgets to switch the current focus. May also be passed to GenPrimarys, GenDisplays, independently realizable GenInteractions, GenDisplayControl, GenViews, etc. (windowed things) to cause them to become the active focus window within their level of the focus hierarchy, if possible (specific UI's having real-estate focus, for instance, would ignore this request).

Note that the object will not actually gain the focus exclusive until all other nodes above it in the hierarchy also have the focus in their levels.

This is the message equivalent of HINT_DEFAULT_FOCUS on generic objects.

MSG_META_RELEASE_FOCUS_EXCL
@importMessage MetaUIMessages, void 	MSG_META_RELEASE_FOCUS_EXCL();

Opposite of MSG_META_GRAB_FOCUS_EXCL . If the object does not currently have the exclusive, nothing will be done.

MSG_META_GET_FOCUS_EXCL
@importMessage MetaUIMessages, Boolean 	MSG_META_GET_FOCUS_EXCL(
        optr 	*focusObject);

May be sent to any visible or generic object which is a focus node, to get current focus object directly below the node, if any, regardless of whether current node is active (has the exclusive itself).

Focus nodes in Generic UI library: GenSystem, GenField, GenApplication, GenPrimary, GenDisplayGroup, GenDisplay, GenView, GenInteraction (independently displayable only). Focus nodes in Visible UI library: VisContent.

Parameters: focusObject This will be filled with return value, the focus object below the object receiving the message.

Return: Will return true if message responded to. Will return false if the message was sent to an object which is not a focus node.

focusObject
The focus node under the object receiving the message.

Warnings: This is a bad way to go about sending a message to currently active objects. For example, if you call from the application thread to the UI thread to find out which is the current focus gadget and then send a message to it, it is possible for the active gadget to change between the two calls. Use MSG_META_SEND_CLASSED_EVENT for this type of operation if at all possible.

MSG_META_GRAB_TARGET_EXCL
@importMessage MetaUIMessages, void 	MSG_META_GRAB_TARGET_EXCL();

May be passed to any visible or generic object to cause it to become the active target within the target level that it is in. The active target hierarchy is the path for the transmission of messages via TO_TARGET request of MSG_META_SEND_CLASSED_EVENT .

Commonly sent to text objects and views to switch which is the current target. May also be passed to GenPrimarys, GenDisplays, independently realizable GenInteractions, GenDisplayControl, GenViews, etc. (windowed things) to cause them to become the active target window within their level f the target hierarchy.

The specific UI will automatically grab the Target exclusive for an object on any mouse press within the object if it is marked as GA_TARGETABLE.

Note that the object will not actually gain the target exclusive until all other nodes above it in the hierarchy also have the target exclusive within their levels. This is the message equivalent of HINT_DEFAULT_TARGET.

MSG_META_RELEASE_TARGET_EXCL
@importMessage MetaUIMessages, void 	MSG_META_RELEASE_TARGET_EXCL();

Opposite of MSG_META_GRAB_TARGET_EXCL . If the object does not currently have the exclusive, nothing will be done.

MSG_META_GET_TARGET_EXCL
@importMessage MetaUIMessages, void 	MSG_META_GET_TARGET_EXCL(
        optr targetObject);

May be sent to any visible or generic object which is a target node, to get the current target object directly below the node, if any, regardless of whether the current node is active (has the exclusive itself).

Target nodes in Generic UI library: GenSystem, GenField, GenApplication, GenPrimary, GenDisplay, GenView, GenInteraction (independently displayable only). Target nodes in Visible UI library: VisContent.

Parameters: targetObject This will be filled with return value, the target object below the object receiving the message.

Return: Will return true if message responded to. Will return false if the message was sent to an object which is not a target node.

targetObject
The target node under the object receiving the message.

Warnings: This is a bad way to go about sending a message to currently active objects. For example, if you call from the application thread to the UI thread to find out which is the current target display, and then send a message to it, it is possible for the active display to change between the two calls. Use MSG_META_SEND_CLASSED_EVENT for this type of operation if at all possible.

MSG_META_GRAB_MODEL_EXCL
@importMessage MetaUIMessages, void 	MSG_META_GRAB_MODEL_EXCL();

May be passed to any visible or generic object to cause it to become the active model within the model level that it is in. The active model hierarchy is the override path for the transmission of messages via TO_MODEL of MSG_META_SEND_CLASSED_EVENT . (If no model hierarchy exists, the messages will be sent down the Target hierarchy.)

Note that the object will not actually gain the model exclusive until all other nodes above it in the hierarchy also have the model exclusive within their levels. This is the message equivalent of HINT_MAKE_DEFAULT_MODEL.

MSG_META_RELEASE_MODEL_EXCL
@importMessage MetaUIMessages, void 	MSG_META_RELEASE_MODEL_EXCL();

Opposite of MSG_META_GRAB_MODEL_EXCL . If the object does not currently have the exclusive, nothing will be done.

MSG_META_GET_MODEL_EXCL
@importMessage MetaUIMessages, void 	MSG_META_GET_MODEL_EXCL(
        optr targetObject);

May be sent to any visible or generic object which is a model node, to get current model object directly below the node, if any, regardless of whether current node is active (has the exclusive itself).

Model nodes in Generic UI library: GenSystem, GenApplication, GenDocumentControl, GenDocumentGroup.

Parameters: modelObject This will be filled with return value, the target object below the object receiving the message.

Return: Will return true if message responded to. Will return false if the message was sent to an object which is not a target node.

modelObject
The target node under the object receiving the message.

Warnings: This is a bad way to go about sending a message to currently active objects. For example, if you call from the application thread to the UI thread to find out which is the current model display, and then send a message to it, it is possible for the active display to change between the two calls. Use MSG_META_SEND_CLASSED_EVENT for this type of operation if at all possible.

MSG_META_GAINED_FOCUS_EXCL
@importMessage MetaUIMessages, void 	MSG_META_GAINED_FOCUS_EXCL();

See description for this and other gained/lost exclusive messages below.

Special note on MSG_META_GAINED_FOCUS_EXCL and MSG_META_LOST_FOCUS_EXCL : If the object receiving MSG_META_GAINED_FOCUS_EXCL is the leaf object in the hierarchy, meaning that it is either not a node itself, or if it is a node, does not have any object below it which has grabbed the exclusive, then the object will automatically be granted the MSG_META_GAINED_KBD_EXCL as well, and thereby receive any MSG_META_KBD_CHAR messages which are generated. The object will receive MSG_META_LOST_KBD_EXCL before MSG_META_LOST_FOCUS_EXCL.

MSG_META_LOST_FOCUS_EXCL
@importMessage MetaUIMessages, void 	MSG_META_LOST_FOCUS_EXCL();

See description for this and other gained/lost exclusive messages below.

Special note on MSG_META_GAINED_FOCUS_EXCL and MSG_META_LOST_FOCUS_EXCL : If the object receiving MSG_META_GAINED_FOCUS_EXCL is the leaf object in the hierarchy, meaning that it is either not a node itself, or if it is a node, does not have any object below it which has grabbed the exclusive, then the object will automatically be granted the MSG_META_GAINED_KBD_EXCL as well, and thereby receive any MSG_META_KBD_CHAR messages which are generated. The object will receive MSG_META_LOST_KBD_EXCL before MSG_META_LOST_FOCUS_EXCL.

Tips and Tricks: If you intercept this message and call the superclass, be aware that the superclass' handler does some of the focus-releasing tasks by sending messages. This can lead to synchronization problems if your method's code looks like the following:

            @callsuper();
            call self::MSG_GEN_APPLY();

To make sure that the superclass is finished updating the focus before carrying out any operations which rely upon a valid focus, you would change the above code to read:

            @callsuper();
            @send ,forceQueue ,insertAtFront self::MSG_GEN_APPLY();
MSG_META_GAINED_SYS_FOCUS_EXCL
@importMessage MetaUIMessages, void MSG_META_GAINED_SYS_FOCUS_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_LOST_SYS_FOCUS_EXCL
@importMessage MetaUIMessages, void MSG_META_LOST_SYS_FOCUS_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_GAINED_TARGET_EXCL
@importMessage MetaUIMessages, void MSG_META_GAINED_TARGET_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_LOST_TARGET_EXCL
@importMessage MetaUIMessages, void MSG_META_LOST_TARGET_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_GAINED_SYS_TARGET_EXCL
@importMessage MetaUIMessages, void MSG_META_GAINED_SYS_TARGET_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_LOST_SYS_TARGET_EXCL
@importMessage MetaUIMessages, void MSG_META_LOST_SYS_TARGET_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_GAINED_MODEL_EXCL
@importMessage MetaUIMessages, void 	MSG_META_GAINED_MODEL_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_LOST_MODEL_EXCL
@importMessage MetaUIMessages, void MSG_META_LOST_MODEL_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_GAINED_SYS_MODEL_EXCL
@importMessage MetaUIMessages, void MSG_META_GAINED_SYS_MODEL_EXCL();

See description for this and other gained/lost exclusive messages below.

MSG_META_LOST_SYS_MODEL_EXCL
@importMessage MetaUIMessages, void MSG_META_LOST_SYS_MODEL_EXCL();

These paired gained/lost messages for the Focus, Target, and Model hierarchies are sent, always in the order GAINED, then at some point LOST, to objects on the hierarchy. The GAINED message is sent only when the object in question and all nodes in the hierarchy above that object have gained the exclusive from the node above them, all the way up to the application object.

In other words, just grabbing the exclusive from the next node up doesn't always guarantee you'll get a GAINED message; the node you're grabbing from must have itself received a GAINED message but not yet the LOST message. Your object will receive the LOST message if it has either released the exclusive, or the node from which you grabbed the exclusive itself received a LOST message.

The GAINED_SYS and LOST_SYS messages behave similarly, except that an object can only gain the SYS_EXCL (System exclusive) if it and all nodes above it to the GenSystem object have the grab from the next node up. An object will never receive a GAINED_SYS_EXCL message if it has not already received an (Application) GAINED_EXCL message also. Similarly, an object will always receive a LOST_SYS_EXCL message before it receives an (Application) LOST_EXCL message.

Source: Do not send these messages to objects yourself, unless you are implementing or extending the above mechanism. These messages should be sent only by the node object which is above the object receiving the message.

Destination: Any MetaClass object which has grabbed and not yet released the focus exclusive.

Interception: Generic UI objects, VisTextClass , and all node objects provide default behavior for processing this message. If you intercept above any of these levels, be sure to call the superclass to let these objects know the exclusive has been gained.

Miscellaneous Input Messages

MSG_META_GRAB_KBD
@importMessage MetaUIMessages, void MSG_META_GRAB_KBD();

This message grabs the keyboard for an object. The grab will not be taken away from another object if it currently has the keyboard grab. To forcefully grab the keyboard in this case, use MSG_META_FORCE_GRAB_KBD.

MSG_META_FORCE_GRAB_KBD
@importMessage MetaUIMessages, void MSG_META_FORCE_GRAB_KBD();

This message forcefully grabs the keyboard for an object, tasking the grab away from another object, if necessary.

MSG_META_RELEASE_KBD
@importMessage MetaUIMessages, void MSG_META_RELEASE_KBD();

This message releases the keyboard grab for an object.

MSG_META_RELEASE_FT_EXCL
@importMessage MetaUIMessages, void 	MSG_META_RELEASE_FT_EXCL();

This message releases exclusive(s) that the object may have on the Focus and Target hierarchies.

MSG_META_GAINED_DEFAULT_EXCL
@importMessage MetaUIMessages, void 	MSG_META_GAINED_DEFAULT_EXCL();

This message notifies a GenTrigger that it has gained the default exclusive.

MSG_META_LOST_DEFAULT_EXCL
@importMessage MetaUIMessages, void 	MSG_META_LOST_DEFAULT_EXCL();

This message notifies a GenTrigger that it has lost the default exclusive.

MSG_META_GAINED_FULL_SCREEN_EXCL
@importMessage MetaUIMessages, void MSG_META_GAINED_FULL_SCREEN_EXCL();

This message is sent to GenFields or GenApplications upon gain of the "full-screen" exclusive. The full-screen exclusive grants the object the top screen-dominating object at its level.

MSG_META_LOST_FULL_SCREEN_EXCL
@importMessage MetaUIMessages, void MSG_META_LOST_FULL_SCREEN_EXCL();

This message is sent to GenFields or GenApplications upon loss of the "full-screen" exclusive. The full-screen exclusive grants the object the top screen-dominating object at its level.

MSG_META_MOUSE_BUMP_NOTIFICATION
@importMessage MetaUIMessages, void 	MSG_META_MOUSE_BUMP_NOTIFICATION(
        sword 	xBump,
        sword 	yBump);

This message is an event that the input manager places in the input queue to notify the UI that it has bumped the mouse position past this point in the queue. This method is sent only when IMBumpMouse() is called.

Parameters: xBump Horizontal relative bump.

yBump
Vertical relative bump.
MSG_META_FUP_KBD_CHAR
@importMessage MetaUIMessages, Boolean 	MSG_META_FUP_KBD_CHAR(
        word 	character,
        word 	flags,
        word 	state);

When a leaf object in the focus hierarchy gets a MSG_META_KBD_CHAR, and does not care about the character, it sends this message to itself to see if a parent object wants to handle it.

Parameters: character The low byte contains a Char value.

flags
High byte is a ShiftState field; low byte is a CharFlags field.
state
High byte is the raw PC scan code; low byte is a ToggleState field.

Return: Will return true if the character was handled by someone (and should not be used elsewhere).

MSG_META_PRE_PASSIVE_KBD_CHAR
@importMessage MetaUIMessages, KbdReturnFlags 	MSG_META_PRE_PASSIVE_KBD_CHAR(
        word 	character,
        word 	flags,
        word 	state);

This message sends a keyboard character to any object requesting preview of the keyboard events.

Parameters: character The low byte contains a Char value.

flags
High byte is a ShiftState field; low byte is a CharFlags field.
state
High byte is the raw PC scan code; low byte is a ToggleState field.

Return: Flags field specifying what should happen to event.

Structures:

 typedef WordFlags 	KbdReturnFlags;
#define  KRF_PREVENT_PASS_THROUGH 0x8000
/* Set for passive keyboard routines if event should
 * be destroyed and not passed on to implied or
 * default grab. */
MSG_META_POST_PASSIVE_KBD_CHAR
@importMessage MetaUIMessages, KbdReturnFlags 	MSG_META_POST_PASSIVE_KBD_CHAR(
        word 	character,
        word 	flags,
        word 	state);

This message passes keyboard characters to all objects having registered interest in getting keyboard events after they have been handled.

Parameters: character The low byte contains a Char value.

flags
High byte is a ShiftState field; low byte is a CharFlags field.
state
High byte is the raw PC scan code; low byte is a ToggleState field.

Return: Flags field specifying what should happen to event.

Structures:

typedef WordFlags 	KbdReturnFlags;
#define KRF_PREVENT_PASS_THROUGH 0x8000
/* Set for passive keyboard routines if event should
 * be destroyed and not passed on to implied or
 * default grab. */
MSG_META_QUERY_IF_PRESS_IS_INK
@importMessage MetaUIMessages, void 	MSG_META_QUERY_IF_PRESS_IS_INK(
        InkReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition);

Return whether or not a MSG_META_START_SELECT should be passed on to the object, or whether it should be intercepted and turned into ink.

Source: Sent by any object (usually VisComp) to determine if one of its children wants ink.

Destination: Any object in the Vis linkage that may be clicked on with the mouse.

Interception: The default handler returns IRV_NO_INK (filling in the inkType field of the retVal argument). Objects that want presses to be turned into ink need to return IRV_DESIRES_INK. Some objects that need to do work on another thread (such as a GenView) to determine whether the press should be ink or not can return IRV_WAIT, which holds up the MSG_META_START_SELECT until a MSG_GEN_APPLICATION_INK_QUERY_REPLY is sent to the application object. By default, clicks on VisComp-derived objects will not be ink. To change this, set VisCompMakePressesInk() as the handler for this message.

Parameters: retVal Empty structure which will be filled with return value information.

xPosition
x -coordinate of selection start.
yPosition
y -coordinate of selection start.

Return: Indication whether object thinks the press was ink.

MSG_META_LARGE_QUERY_IF_PRESS_IS_INK
@importMessage MetaUIMessages, void MSG_META_LARGE_QUERY_IF_PRESS_IS_INK(
        InkReturnParams		*retVal,
        LargeMouseData		*largeMouseDataStruct);

This message is sent by the system to children with the VCNA_LARGE_DOCUMENT_MODEL bit set to determines whether or not a MSG_META_LARGE_START_SELECT should be processed as ink.

Source: Sent by any object (usually VisComp) to determine if one of its children wants ink.

Destination: Any object in the Vis linkage that may be clicked on with the mouse.

Parameters: retVal Pointer to an InkReturnParams structure that will be filled in by the handler for this message.

largeMouseDataStruct
Pointer to a LargeMouseData struct that stores information about the large mouse event.

Interception: The default handler returns IRV_NO_INK. Objects that want presses to be turned into ink need to return IRV_DESIRES_INK. Some objects that need to do work on another thread (such as a VisContent) to determine whether the press should be ink or not can return IRV_WAIT, which holds up the MSG_META_LARGE_START_SELECT until a MSG_GEN_APPLICATION_INK_QUERY_REPLY is sent to the application object.

Mouse Input Messages

The following messages allow an application to detect the nature and behavior of pointing devices within the system.

MSG_META_START_SELECT
@importMessage MetaUIMessages, void 	MSG_META_START_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_END_SELECT
@importMessage MetaUIMessages, void 	MSG_META_END_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_START_MOVE_COPY
@importMessage MetaUIMessages, void 	MSG_META_START_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_END_MOVE_COPY
@importMessage MetaUIMessages, void 	MSG_META_END_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_START_FEATURES
@importMessage MetaUIMessages, void 	MSG_META_START_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_END_FEATURES
@importMessage MetaUIMessages, void 	MSG_META_END_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_START_OTHER
@importMessage MetaUIMessages, void 	MSG_META_START_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_END_OTHER
@importMessage MetaUIMessages, void 	MSG_META_END_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_DRAG_SELECT
@importMessage MetaUIMessages, void 	MSG_META_DRAG_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_DRAG_MOVE_COPY
@importMessage MetaUIMessages, void 	MSG_META_DRAG_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_DRAG_FEATURES
@importMessage MetaUIMessages, void 	MSG_META_DRAG_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_DRAG_OTHER
@importMessage MetaUIMessages, void 	MSG_META_DRAG_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_BUTTON
@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_BUTTON(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_BUTTON
@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_BUTTON(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_START_SELECT
@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_START_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_START_SELECT
@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_START_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_END_SELECT
@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_END_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_END_SELECT
@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_END_SELECT(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_START_MOVE_COPY
@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_START_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_START_MOVE_COPY
@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_START_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_END_MOVE_COPY
@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_END_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_END_MOVE_COPY
@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_END_MOVE_COPY(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_START_FEATURES
@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_START_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_START_FEATURES
@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_START_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_END_FEATURES
@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_END_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_END_FEATURES
@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_END_FEATURES(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_START_OTHER
@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_START_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_START_OTHER
@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_START_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_PRE_PASSIVE_END_OTHER
@importMessage MetaUIMessages, void 	MSG_META_PRE_PASSIVE_END_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

For description of this and other button messages, see below.

MSG_META_POST_PASSIVE_END_OTHER
@importMessage MetaUIMessages, void 	MSG_META_POST_PASSIVE_END_OTHER(
        MouseReturnParams 		*retVal,
        sword 		xPosition,
        sword 		yPosition,
        word 		inputState);

The above messages are the standard button functions generated by the UI upon receiving MSG_META_BUTTON events from the Input Manager. These messages are sent out to whatever object has the implied grab (whichever window the mouse is over), until the mouse is "grabbed" by an object, after which the messages go there until the mouse is released (ungrabbed).

Parameters: retVal Structure to hold return values.

xPosition
x -coordinate of press.
yPosition
y -coordinate of press.
inputState
High byte is UIFunctionsActive structure; low byte is ButtonInfo structure.

Structures:

 typedef struct {
    word 			unused;
    MouseReturnFlags 			flags;
    optr 			ptrImage;
} MouseReturnParams;
  typedef WordFlags MouseReturnFlags;
/* These flags may be combined using | and &:
    MRF_PROCESSED,
    MRF_REPLAY,
    MRF_PREVENT_PASS_THROUGH,
    MRF_SET_POINTER_IMAGE,
    MRF_CLEAR_POINTER_IMAGE */
MSG_META_LARGE_PTR
@importMessage MetaUIMessages, void 	MSG_META_LARGE_PTR(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_START_SELECT
@importMessage MetaUIMessages, void 	MSG_META_LARGE_START_SELECT(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_END_SELECT
@importMessage MetaUIMessages, void 	MSG_META_LARGE_END_SELECT(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_START_MOVE_COPY
@importMessage MetaUIMessages, void 	MSG_META_LARGE_START_MOVE_COPY(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_END_MOVE_COPY
@importMessage MetaUIMessages, void 	MSG_META_LARGE_END_MOVE_COPY(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_START_FEATURES
@importMessage MetaUIMessages, void 	MSG_META_LARGE_START_FEATURES(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_END_FEATURES
@importMessage MetaUIMessages, void 	MSG_META_LARGE_END_FEATURES(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_START_OTHER
@importMessage MetaUIMessages, void 	MSG_META_LARGE_START_OTHER(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_END_OTHER
@importMessage MetaUIMessages, void 	MSG_META_LARGE_END_OTHER(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_DRAG_SELECT
@importMessage MetaUIMessages, void 	MSG_META_LARGE_DRAG_SELECT(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_DRAG_MOVE_COPY
@importMessage MetaUIMessages, void 	MSG_META_LARGE_DRAG_MOVE_COPY(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_DRAG_FEATURES
@importMessage MetaUIMessages, void 	MSG_META_LARGE_DRAG_FEATURES(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

See below for information about this and other large mouse messages.

MSG_META_LARGE_DRAG_OTHER
@importMessage MetaUIMessages, void 	MSG_META_LARGE_DRAG_OTHER(
        MouseReturnParams 		*retVal,
        LargeMouseData 		*largeMouseDataStruct);

Objects which have been set up with 32-bit coordinate spaces must be prepared to handle large mouse events along with regular mouse events.

These messages are available by request for use within 32-bit visible document models. Mouse position data is in full 32-bit integer, 16-bit fraction format, as generated by GenView.

Parameters: retVal Structure to hold return values.

largeMouseDataStruct
Structure to hold pass values.

Return: Nothing returned explicitly.

retVal
Filled with return values.

Structures:

  typedef struct {
    PointDWFixed 				LMD_location;
    byte 				LMD_buttonInfo;
    UIFunctionsActive 				LMD_uiFunctionsActive;
} LargeMouseData;
typedef struct {
    word 			unused;
    MouseReturnFlags 			flags;
    optr 			ptrImage;
    /* Pointer image to use, if MRF_SET_PTR_IMAGE
     * returned */
} MouseReturnParams;
MSG_META_ENSURE_MOUSE_NOT_ACTIVELY_TRESPASSING
@importMessage MetaUIMessages, MouseReturnFlags 	MSG_META_ENSURE_MOUSE_NOT_ACTIVELY_TRESPASSING();

Sent to the passive, active, or implied mouse grab chain whenever modality status changes within the system--any object receiving this message which has a window grabbed should make sure that it has a legitimate right to have the window grab active--if not, it should be released (along with the mouse). In particular, menus in stay-up mode should come down, any interaction between the mouse and primary, display, menu, or view windows should be terminated. MSG_GEN_APPLICATION_TEST_WIN_INTERACTABILITY is useful; this message will test any passed OD against the list of window(s) which the mouse is allowed to interact with (Generally, top most system modal window, else top most application modal window, else all windows), and return a flag indicating the result.

Parameters: None.

Return: Flags field which system normally ignores.

MSG_META_ENSURE_NO_MENUS_IN_STAY_UP_MODE
@importMessage MetaUIMessages, MouseReturnFlags 	MSG_META_ENSURE_NO_MENUS_IN_STAY_UP_MODE();

Sent to the passive, active/implied mouse grab chain whenever we want to make sure all of an application's menus are closed. Sent directly to the Flow object from the global shortcut code. Any menus receiving this message which are in stay-up mode should dismiss themselves.

Parameters: None.

Return: Flags field, normally ignored by system.

MSG_META_ENSURE_ACTIVE_FT
@importMessage MetaUIMessages, void 	MSG_META_ENSURE_ACTIVE_FT();

Makes sure that some object with the Focus/Target node to which this message may be sent has the Focus and Target exclusives. Called from within the UI, usually when windowed objects below the node have closed, or moved to the back, to give the Focus and/or Target to the most suitable window.

Behavior as implemented in GenApplicationClass :
Checks to make sure that something within the application has the Focus and Target exclusives. Called from within the UI, usually on the closure of a window, to give the Focus and/or Target to the next best location.

Typical click-to-type model is implemented using the following rules:
For Target, the priority order is:

  1. Anything already having the exclusive.
  2. Top targetable PRIO_STD priority level window.
  3. Top targetable PRIO_COMMAND priority level window.

For Focus, priority goes to:

  1. Anything already having the exclusive.
  2. Top system modal window.
  3. Top application modal window.
  4. Last non-modal window to have or request the exclusive.
  5. Window having Target exclusive.
  6. Top focusable PRIO_COMMAND priority level window.

Source: Most always internally from the UI, though is unrestricted.

Destination: Focus/Target node, specifically: GenSystem, GenField, or GenApplication object.

Interception: No reason to intercept. Default behavior is provided by above objects. Could possibly be replaced, but as default behavior varies by specific UI, results could be unpredictable.

Parameters: None.

Return: Nothing.

MSG_META_NOTIFY_NO_FOCUS_WITHIN_NODE
@importMessage MetaUIMessages, void 	MSG_META_NOTIFY_NO_FOCUS_WITHIN_NODE();

Notification from Focus node MSG_META_ENSURE_ACTIVE_FT handler that it was unable to keep/find an object below it suitable for being the focus. The most likely cause is that the last focusable geode/object running below this point has been shut down/closed.

Source: Focus node, MSG_META_ENSURE_ACTIVE_FT handler

Destination: Self

Interception: Intercepted to find something safe to do for the user, such as push this field/application to the back, or mark this object as no longer "focusable" and call MSG_META_ENSURE_ACTIVE_FT on the node above this one, in an attempt to find something for the user to access. If there's nothing left at all in the system, the last focusable application has exited, so it's time to shut down.

Parameters: None.

Return: Nothing.

Miscellaneous Meta Messages

MSG_META_UI_FORCE_CONTROLLER_UPDATE
@importMessage MetaUIMessages, void MSG_META_UI_FORCE_CONTROLLER_UPDATE(
        ManufacturerID		manufID,
        word		changeID);

This message forces an object to update one or all of the GCN notification lists that it communicates with.

Source: Usually sent by a controller to its output.

Destination: Any object.

Parameters: manufID ManufacturerID of GCN lists.

changeID Notification list ID.
This value may be 0xffffh if all notification lists should be updated or 0xfffeh to generate the standard notifications.

Interception: Objects that send notification for controllers should respond to this message.

MSG_META_GEN_PATH_RESTORE_DISK_PROMPT
@importMessage MetaUIMessages, Boolean MSG_META_GEN_PATH_RESTORE_DISK_PROMPT(
        DiskRestoreError				*error,
        GenPathDiskRestoreArgs				*args);

This message prompts the user to insert a particular disk into a particular drive when restoring a disk handle for the object's path.

Source: Sent by the callback passed to DiskRestore() when a disk handle saved in an object's path is being restored after a shutdown.

Destination: Any object possessing a path.

Parameters: error Pointer to store an error code. This error code will be returned to DiskRestore() .

args
GenPathDiskRestoreArgs .

Structures:

typedef struct {
    word			GPDRA_pathType;
    word			GPDRA_savedDiskType;
    char			*GPDRA_driveName;
    char			*GPDRA_diskName;
    DiskRestoreError			GPDRA_errorCode;
} GenPathDiskRestoreArgs;

GPDRA_ pathType stores the vardata tag holding the path.

GPDRA_ savedDiskType stores the vardata tag holding the saved disk handle.

GPDRA_ driveName and GPDRA_ diskName store pointers to the null-terminated drive and disk names.

GPDRA_ errorCode stores the error code that is returned to DiskRestore() .

Interception: May be intercepted if the object has more information to provide to the user, or if the object doesn't wish to prompt the user. If this message is intercepted, it should not call its superclass.

MSG_META_PAGED_OBJECT_GOTO_PAGE
@importMessage MetaUIMessages, void MSG_META_PAGED_OBJECT_GOTO_PAGE(
        word	page);

This message instructs a GenDocument to go to the passed page.

This message is sent out by the GenPageControl object and is handled by a GenApplication's subclassed GenDocument object.

Source: GenPageControl object.

Destination: GenDocument object.

Parameters: page Page to set the GenDocument to display.

Interception: You may intercept to provide custom paging behavior.

MSG_META_PAGED_OBJECT_NEXT_PAGE
@importMessage MetaUIMessages, void MSG_META_PAGED_OBJECT_NEXT_PAGE();

This message instructs a GenDocument to go to the next page.

This message is sent out by the GenPageControl object and is handled by a GenApplication's subclassed GenDocument object.

Source: GenPageControl object.

Destination: GenDocument object.

Interception: You may intercept to provide custom paging behavior.

MSG_META_PAGED_OBJECT_PREVIOUS_PAGE
@importMessage MetaUIMessages, void MSG_META_PAGED_OBJECT_PREVIOUS_PAGE();

This message instructs a GenDocument to go to the previous page.

This message is sent out by the GenPageControl object and is handled by a GenApplication's subclassed GenDocument object.

Source: GenPageControl object.

Destination: GenDocument object.

Interception: You may intercept to provide custom paging behavior.

MSG_META_DELETE_RANGE_OF_CHARS
@importMessage MetaUIMessages, void MSG_META_DELETE_RANGE_OF_CHARS(@stack
        VisTextRange		rangeToDelete);

This message instructs an object to delete a range of characters passed in a VisTextRange . Generally, this message is sent out when the user crosses out characters within a HWR grid.

Source: GenPenInputControl.

Destination: Any focused object.

Parameters: rangeToDelete VisTextRange of characters to delete. Objects that are not text objects will need to know how to interpret this value.

Interception: May intercept to provide custom deletion behavior.

MSG_META_NOTIFY_TASK_SELECTED
@importMessage MetaUIMessages, void MSG_META_NOTIFY_TASK_SELECTED();

This message is sent when a task list item of an application in the Express Menu is selected. The default behavior brings the application to the front and gives it the focus.

MSG_META_FIELD_NOTIFY_DETACH
@importMessage MetaUIMessages, void MSG_META_FIELD_NOTIFY_DETACH(
        optr		field,
        word		shutdownFlag);

This message is sent by the GenField object when it is detaching.

Source: GenField.

Destination: The notification destination of the GenField object.

Parameters: field Optr of the GenField sending notification.

shutdownFlag
true if the GenField is detaching because of a shutdown.

Interception: The object receiving notification may handle as desired. As this is a notification only, you should not call the superclass.

MSG_META_FIELD_NOTIFY_NO_FOCUS
@importMessage MetaUIMessages, void MSG_META_FIELD_NOTIFY_NO_FOCUS(
        optr		field,
        word		shutdownFlag);

This message is sent by the GenField when it no longer has any applications in the focus hierarchy.

Source: GenField.

Destination: The notification destination of the GenField object.

Parameters: field Optr of the GenField sending notification.

shutdownFlag
true if the GenField lost its focus applications because of a shutdown.

Interception: The object receiving notification may handle as desired. As this is a notification only, you should not call the superclass.

MSG_META_FIELD_NOTIFY_START_LAUNCHER_ERROR
@importMessage MetaUIMessages, void MSG_META_FIELD_NOTIFY_START_LAUNCHER_ERROR(
        optr		field);

This message is sent by the GenField when an error occurs while attempting to run the launcher for the field object.

Source: GenField.

Destination: The notification destination of the GenField object.

Parameters: field Optr of the GenField sending notification.

Interception: The object receiving notification may handle as desired. As this is a notification only, you should not call the superclass.

MSG_META_TEST_WIN_INTERACTIBILITY
@importMessage MetaUIMessages, Boolean MSG_META_TEST_WIN_INTERACTIBILITY(
        optr		inputOD,
        WindowHandle		window);

This message checks whether a pointing device (usually a mouse) can interact with the passed window.

Source:

Destination: A windowed object.

Parameters: inputOD Input optr of the windowed object to check.

window
Window to check.

Return: true if window is interactable.

MSG_META_CHECK_IF_INTERACTIBLE_OBJECT
@importMessage MetaUIMessages, Boolean MSG_META_CHECK_IF_INTERACTIBLE_OBJECT(
        optr	obj);

This message is sent o objects on the GAGCNLT_ALWAYS_INTERACTABLE_WINDOWS GCN list.

Source: GenApplication object.

Destination: Objects on the GAGCNLT_ALWAYS_INTERACTABLE_WINDOWS GCN list.

Parameters: obj Object whose interactable state is being checked.

Return: true if object is interactable.

Interception: May intercept.

MetaClass: 3.4 Exported Message Ranges: Standard GCN Messages

There are several standard messages which objects adding themselves to the appropriate GCN lists may receive and handle.

MSG_NOTIFY_FILE_CHANGE
@importMessage MetaGCNMessages, void 	MSG_NOTIFY_FILE_CHANGE(
        MemHandle 		data);

This notification is sent out whenever the file system changes in any way.

Source: GCN mechanism.

Destination: Object on the GCNSLT_FILE_SYSTEM GCN list.

Parameters: data Handle of a FileChangeNotificationData block.

Return: Nothing.

Structures:

  typedef struct {
    PathName			FCND_pathname;
    DiskHandle			FCND_diskHandle;
    FileChangeType			FCND_changeType;
} FileChangeNotificationData;
  typedef ByteEnum FileChangeType;
/* These flags may be combined using | and &:
    FCT_CREATE
    FCT_DELETE
    FCT_RENAME
    FCT_CONTENTS
    FCT_DISK_FORMAT */
MSG_NOTIFY_DRIVE_CHANGE
@importMessage MetaGCNMessages, void 	MSG_NOTIFY_DRIVE_CHANGE(
        GCNDriveChangeNotificationType 						type,
        word 						driveNum);

This is sent to notify various system utilities that a drive has been created or destroyed or has changed ownership from one installable file system driver to another.

Note that during system initialization, the ownership of a drive may change several times as more-specialized drivers are loaded. This means the recipient should not be surprised if it's told a drive has been created that it thought already existed.

Source: The kernel issues this notification whenever a filesystem driver creates or destroys a drive it manages. This includes when a specialized filesystem driver takes control of an existing drive.

Destination: Any object that has added itself to the GCNSLT_FILE_SYSTEM GCN list. It is intended for system objects, such as the GenFileSelector.

Parameters: type GCNDriveChangeNotificationType .

driveNum
Number of the affected drive.

Return: Nothing.

MSG_NOTIFY_APP_STARTED
@importMessage MetaGCNMessages, void 	MSG_NOTIFY_APP_STARTED();

This message is sent out when an application attaches to the UI.

Source: GCN Mechanism.

Destination: Any object on the GCNSLT_APPLICATION system GCN list.

Parameters: None.

Return: Nothing.

MSG_NOTIFY_APP_EXITED
@importMessage MetaGCNMessages, void 	MSG_NOTIFY_APP_EXITED();

This message is sent out when an application thread exits.

Source: GCN Mechanism.

Destination: Any object on the GCNSLT_APPLICATION system GCN list.

Parameters: None.

Return: Nothing.

MSG_NOTIFY_DATE_TIME_CHANGE
@importMessage MetaGCNMessages, void 	MSG_NOTIFY_DATE_TIME_CHANGE();

This message is sent out when the date or time changes--whenever the system comes back or the system time is altered (e.g. by the User in Preferences).

Source: GCN Mechanism.

Destination: Any object on the GCNSLT_DATE_TIME system GCN list.

Parameters: None.

Return: Nothing.

MSG_NOTIFY_USER_DICT_CHANGE
@importMessage MetaGCNMessages, void 	MSG_NOTIFY_USER_DICT_CHANGE(
        MemHandle 		sendingSpellBox,
        MemHandle 		userDictChanged);

This message is sent out when an application attaches to the UI.

Source: GCN Mechanism.

Destination: Any object on the GCNSLT_DICTIONARY system GCN list.

Parameters: sendingSpellBox Handle of SpellBox that sent out the notification.

userDictChanged
Handle of user dictionary that changed.

Return: Nothing.

MSG_NOTIFY_KEYBOARD_LAYOUT_CHANGE
@importMessage MetaGCNMessages, void 	MSG_NOTIFY_KEYBOARD_LAYOUT_CHANGE();

This message is sent out when the keyboard layout is changing. Usually this involves a change in status of the floating keyboard. When passing this event to GCNListSend() , you must be sure to pass the GCNLSF_FORCE_QUEUE flag. (Otherwise, if you have a GenPenInputControl running on the same thread, it may try to remove itself from the list while you are sending this message.)

Source: GCN Mechanism.

Destination: Any object on the GCNSLT_KEYBOARD_OBJECT system GCN list.

Parameters: None.

Return: Nothing.

MSG_NOTIFY_EXPRESS_MENU_CHANGE
@importMessage MetaGCNMessages, void 	MSG_NOTIFY_EXPRESS_MENU_CHANGE(
        GCNExpressMenuNotificationTypes 	type,
        optr 		affectedField);

This message is sent to notify various system utilities that an express menu has been created or destroyed. The recipient receives the optr of the field to which the affected express menu belongs, as all access to the express menu is via messages sent to the field.

Source: The UI issues this notification whenever a GenField object creates or destroys its express menu.

Destination: Any object that has added itself to the GCNSLT_EXPRESS_MENU. GCN list. It is intended for system utilities, such as the print spooler or a task-switching driver, that need to add objects to each express menu in the system.

Parameters: type What happened to the field.

affectedField
Which field of the menu was affected. (This will not be the optr of the express menu itself.)

Return: Nothing.

Structures:

  typedef enum {
    GCNEMNT_CREATED,
    GCNEMNT_DESTROYED
} GCNExpressMenuNotificationTypes;
MSG_PRINTER_INSTALLED_REMOVED
@importMessage MetaGCNMessages, void 	MSG_PRINTER_INSTALLED_REMOVED();

This message is sent whenever a printer is installed or removed. The recipient of this message might call SpoolGetNumPrinters() to determine if any printers or fax machines are currently installed.

Source: GCN Mechanism.

Destination: Any object on the GCNSLT_INSTALLED_PRINTERS system GCN list.

Parameters: None.

Return: Nothing.

MSG_META_CONFIRM_SHUTDOWN
@importMessage MetaGCNMessages, void 	MSG_META_CONFIRM_SHUTDOWN(
        GCNShutdownControlType 	type);

This message is sent out when the system is about to shut down.

All applications which need to keep the system from shutting down must add themselves to GCNSLT_SHUTDOWN_CONTROL and handle this message.

Source: The task switch mechanism, through GCN.

Destination: Any object on the GCNSLT_SHUTDOWN_CONTROL system GCN list.

Parameters: type The type of shutdown occurring. See description under Interception for more information.

Return: Nothing.

Interception: If the system is about to be suspended or shut down (if the passed GCNShutDownControlType is GCNSCT_SUSPEND or GCNSCT_SHUTDOWN), then any object receiving this message must call SysShutdown(), passing SST_CONFIRM_START before it puts up any dialog box it uses to ensure the user isn't doing something foolish. If SysShutdown() returns true (indicating something has already denied the shutdown request), the caller should not put up its confirmation box, nor need it call SysShutdown() again.
Once the object has received a response from the user, either affirmative or negative, it must call SysShutdown(), passing SST_CONFIRM_END with either TRUE or FALSE as appropriate. This will allow any other confirmations to happen, as well as sending the final result to the original caller of SysShutdown().
If the passed control type is GCNSCT_UNSUSPEND, no response is required.

See Also: SysShutdown()

MetaClass: 3.5 Exported Message Ranges: IACP Meta Messages

IACP is fully discussed in the Applications and Geodes chapter.

MSG_META_IACP_PROCESS_MESSAGE
@importMessage MetaIACPMessages, void MSG_META_IACP_PROCESS_MESSAGE(
        EventHandle		msgToSend,
        TravelOption		topt,
        EventHandle		completionMsg);

This message dispatches an IACP message to its proper destination, sending a completion message back when that has finished.

Source: IACPSendMessage() .

Destination: Any object registered as an IACP server, or the GenApplication object of a geode that is a client of such a server.

Parameters: msgToSend EventHandle of recorded message that the other side of the connection is actually sending.

topt
TravelOption (or -1 if msgToSend should be dispatched via MessageDispatch() o r delivered via MSG_META_SEND_TO_CLASSED_EVENT.
completionMsg
EventHandle of recorded message to send when the message in msgToSend has been handled. If null, then no completion message will be sent.

Interception: if you have an object other than the GenApplication object that is an IACP server, you will need to intercept this message. You do not want to pass it on to the superclass in this case; usually, you will just want to call IACPProcessMessage() .

MSG_META_IACP_NEW_CONNECTION
@importMessage MetaIACPMessages, void MSG_META_IACP_NEW_CONNECTION(
        MemHandle		appLaunchBlock,
        Boolean		justLaunched,
        IACPConnection		connection);

This message informs servers that a new client has connected to the server.

Source: IACPConnect() .

Destination: Any object registered as an IACP server.

Parameters: appLaunchBlock Handle of AppLaunchBlock passed to IACPConnect() . Do not free this block.

justLaunched
true if the recipient was just launched (i.e. it received the AppLaunchBlock in its MSG_META_ATTACH call).
connection
IACPConnection that is now open.

Interception: Must intercept if you want to do anything about receiving the new client; there is not default handler for this message. If you do not intercept this message, no harm is done.

MSG_META_IACP_LOST_CONNECTION
@importMessage MetaIACPMessages, void MSG_META_IACP_LOST_CONNECTION(
        IACPConnection		connection,
        word		serverNum);

This message informs a server (or client) that one of its clients (or servers) has shut down.

Source: IACPShutdown() .

Destination: Any object registered as an IACP server, or the GenApplication object of a geode who is a client of such.

Parameters: connection IACPConnection being closed.

serverNum
Server number that shut down, or 0 if this was a client that shut down (and thus it is a server being notified through this message).

Interception: Must be intercepted to provide custom behavior upon losing a connection, as there is no default handler for this message. IACPLostConnection() is a good routine for servers to call to ensure that connections don't linger after a client has shut down its end.

MSG_META_IACP_SHUTDOWN_CONNECTION
@importMessage MetaIACPMessages, void MSG_META_IACP_SHUTDOWN_CONNECTION(
        IACPConnection		connection);

This message shuts down the appropriate side of the indicated connection.

Source: IACPLostConnection() , though after a delay.

Destination: Any IACP server object.

Parameters: connection IACPConnection to shutdown.

Interception: Must be intercepted to finish the work of a call to IACPLostConnection() . Call IACPShutdownConnection() to get default handling of this message.

MSG_META_IACP_DOC_OPEN_ACK
@importMessage MetaIACPMessages, void MSG_META_IACP_DOC_OPEN_ACK(
        IACPDocOpenAckParams			*params);

This message is sent when a document has been opened; the document must have previously been passed in the AppLaunchBlock when the IACP connection was made. The optr of the GenDocument object managing the document is passed so that messages can be sent to it explicitly, though these messages must always be sent via IACP (with a TravelOption of -1) to allow the application to exit at any time.

Source: GenDocumentGroup.

Destination: IACP client (usually the GenApplication object of the client application).

Parameters: params Pointer to an IACPDocOpenAckParams structure.

Structures:

typedef struct {
    optr			IDOAP_docObj;
    IACPConnection			IDOAP_connection;
    word			IDOAP_serverNum;
} IACPDocOpenAckParams;

IDOAP_ docObj stores the optr of the document object managing the document.

IDOAP_ connection stores the IACPConnection over which the open request was received.

IDOAP_ serverNum stores the server number of the GenApplication object acting as the document object's server, or zero if the connection is through some other object.

Interception: No default handler is defined. You must intercept this message to provide custom behavior.

MSG_META_IACP_DOC_CLOSE_ACK
@importMessage MetaIACPMessages, void MSG_META_IACP_DOC_CLOSE_ACK(
        IACPDocCloseAckParams				*params);

This message acts as the acknowledgment sent by a GenDocument object after it successfully processes MSG_GEN_DOCUMENT_CLOSE. Documents opened via IACP always operate in transparent mode; i.e. if you close a dirty file, it will be saved. If you don't want this behavior, you will have to send a message to revert the document.

Source: GenDocument object.

Destination: IACP client.

Parameters: params Pointer to a IACPDocCloseAckParams structure.

Structures:

typedef struct {
    optr			IDCAP_docObj;
    IACPConnection			IDCAP_connection;
    word			IDCAP_serverNum;
    word			IDCAP_status;
} IACPDocCloseAckParams;

IDCAP_ docObj stores the optr of the document object that was managing the document.

IDCAP_ connection stores the IACPConnection over which the close request was received.

IDCAP_ serverNum stores the server number of the GenApplication object acting as the document object's server, or zero if the connection is through some other object.

IDCAP_ status stores the DocQuitStatus of the close operation.

Interception: No default handler is defined. You must intercept this message to provide custom behavior.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Icon Editor

The Icon Editor tool allows you to create icons for your application, both file icons used by file manager applications and simple tool monikers which you may use to provide graphic monikers for a geode's UI gadgetry.

1 Creating Icons
2 Importing Icons
3 Editing Icons
4 Writing Source Code
5 Icon Databases
6 Exporting to Database

Icon Editor: 1 Creating Icons

The icons produced by the icon editor are stored in an "icon database" so that they can be viewed and retrieved easily. The icon database is the Icon Editor's "document", so New/Open, Close, Save As, Make Backup and other Document-control commands will operate on the current database. "Save" actually saves the currently edited icon into the current icon database, and saves the database as well.

There are 3 standard kinds of icons in use in the system. Each standard type of icon has 3 default formats: One for VGA screens, one for MCGA (monochrome VGA) screens, and one for CGA screens.

Most icons are used in one of the following ways:

  • File Icons, which are used by GeoManager to display and launch applications. The sizes are 48x30, 48x30 (monochrome), 48x14, 32x20, and 32x20(monochrome).
  • Tool Icons, which are used by controllers in their toolbox UI. The format sizes are 15x15 and 15x10.
  • Mouse Pointers, which are used to provide a cursor tracking the mouse's movement. This format has one standard size:16 x 16.
  • Icons which will appear in the OmniGo's Home Screen application are 16x16 and monochrome.

You may also create custom icons, which start with 1 default format. Custom icons are limited to 1024 pixels wide or tall, and the formats cannot be larger than 64k.

The first time the icon editor is started it will create a blank file icon named "untitled." After that, if there is an icon database available to the icon editor, the icon editor will start with the last icon that was being edited before the editor was shut down.

To create a new icon, choose "Create New Icon" from the Icon menu. The dialog will present 4 options for the type of icon. If you decide to create a custom icon, you should enter the height, width and color scheme for the icon in the provided fields.

Icon Editor: 2 Importing Icons

There are 2 other ways to create icons. You can import a graphic such as a GIF, TIF, BMP or other supported file format for use as an icon, provided that it is less than 64k (which is the limit for GEOS monikers). You do this using the "Import Graphic" dialog in the Icon menu.

The other way to make an icon in the icon database is to import a moniker or moniker group from the token database, which is the cache of file monikers kept by GeoManager. Select "Token Database" from the Icon menu to get a list of monikers that you may import.

Icon Editor: 3 Editing Icons

There are a number of features that are useful when editing icons:

  • You can create a format for the icon, and scale it to help create the other formats, using the "Transform Format" dialog under the Format menu.
  • The pixel-edit size can be set using the Options menu. It defaults to 8-bit-wide pixels.
  • The "Preview" dialog allows you to view a format in a generic object. If the format is intended for use in a generic object, you can see what it looks like inverted, and (in the case of triggers and tools) what it looks like with different background colors.
  • You can add your own formats, or delete existing ones, using commands from the Format menu. You might, for example, want to add a SuperVGA format for the icon (system supervga file-icons are 64x40).
  • 256-color icons are technically supported, but no special tools are provided for editing them. You can select the red, green, and blue components of the area or line color from the "Area Color" and "Line Color" dialogs. 256-color icons aren't much use unless either:
    • They use colors only from the system's default palette, or
    • They are being used in a 24-bit color environment.
  • You can select the aspect ratio for your icon. This will allow the system to make the best choice among formats when selecting from your moniker list. The choices are CGA, EGA, and VGA. The "VGA" aspect ratio should be used for VGA, MCGA (monochrome VGA), and SVGA formats.
  • You are not required to create multiple formats for your artwork; however we recommend that if you are creating art that could be used under many video modes that you create the three standard formats for the art (VGA, mono VGA, CGA). This shouldn't be too difficult, since there are several tools for working with several formats:
    • The "Transform Format" dialog can be used to create exact copies, or transformed copies, of one format to another.
    • The "Resize Format" dialog changes the size and shape of a single format.
    • The "Flip Format" dialog allows you to flip an icon format horizontally or vertically, or to rotate it 90 degrees.

Icon Editor: 4 Writing Source Code

When the icon is finished, make sure it's saved (File->Save), and then choose "Write Source Code" from the Icon Menu. Follow these steps to get source code:

  1. Enter a DOS 8.3 filename for the source file
  2. To write only the current format, leave the "Formats" group at "Current Format"
  3. To write source for all the formats, choose "All Formats"
  4. Select GOC source (assembler is used in-house)

A dialog should appear when the source code has been written, saying "Source Code Written Successfully". If a problem is encountered in creating or writing a file, check the permissions on your document directory, and make sure you have enough disk space for the text file (only a few hundred bytes should be necessary).

The file should appear in your document directory. It's a text file with GOC moniker source code. This source code can be included in your application after you make the following changes:

  • The formats for the icon will have names like "Moniker0". You should give them appropriate names.
  • The monikers must be placed in a resource. You could place them between statements like:
    • @start APPMONIKERRESOURCE, data;
    @end APPMONIKERRESOURCE;

    In your .gp file, this resource should have the attributes "lmem, read-only, shared".

  • You may wish to add the line "style = tool;" for icons that are going to be used as toolbox UI, to assist the specific UI in deciding how to draw them.
  • Your .goc file must #include <gstring.h>, an include file in which the graphics string data structures used to describe visual monikers are described. (Esp programs include gstring.def .)

Icon Editor: 5 Icon Databases

The icon editor can work with more than one icon database at a time via the Multiple Document Interface standard. Each database has its own window, each window having four views. The leftmost view is the database. The top-right view is the "Pixel View", the main editing area. The two remaining views are next to each other in the lower-right portion of the window. The left view is the actual-size view of the icon, and is editable. The right window is the format list, showing all formats of the icon currently being edited.

Double-clicking an icon in the database viewer will select it for editing. The currently-edited icon is important in the following ways:

  • It is (of course) the only icon being edited in the database associated with that window.
  • Its size determines the shapes of the various views, which will try to adjust themselves to fit in the window as best they can.
  • It is the icon that appears in the "Preview" dialog.
  • It is the icon used in the "Export to Token database" dialog (explained below).
  • It is the target of all operations in the Icon and Format menus (except "create new icon").

The database viewer supports all clipboard operations except for Undo. Icons may be selected and moved or copied between databases. This can be done via the Edit menu or quick-transfer.

The format list is used to switch editing to a different format in the same icon. This can only be done with the mouse.

To get more room for editing, use the Options menu to temporarily hide the database viewer or format list while editing.

Icon Editor: 6 Exporting to Database

This is the function that most people associate with an icon editor. The token database is a cache of moniker lists used by GeoManager and other applications. The token database exists so that every time an application's moniker is needed, it is not necessary to start the application in engine mode to request the moniker (a slow process).

The icon editor can change entries in the token database. You cannot change the "built in" moniker for a geode using the icon editor. If you make changes to the token database and then delete the database, then when it gets rebuilt your monikers will no longer be there.

Given that, it's still likely that people will want to set their own monikers in the token database; the icon editor has this ability.

To change an entry:

  • Make sure you are editing the icon you wish to change.
  • Open the File->Export to Token Database dialog.
  • One of the icon's formats should be visible under the "Change To:" text in the dialog. If not, then you are probably editing the wrong icon, or perhaps the icon hasn't been saved into the database.
  • Use the file selector to find the application, document, library, or other driver whose token database entry you wish to change. Otherwise, enter the four token characters for the geode into the Token Characters text field. Select the corresponding operation from the list at the lower-right of the dialog ("Use File Selector" or "Use Token Chars.")
  • The current token-db entry for the selected geode should appear under the "Current Icon" title at the upper-right corner of the dialog.
  • When you are satisfied with the edited icon, change the moniker with the "Change" trigger in the dialog's reply bar. The change will not take effect in GeoManager until you select Disk->Rescan Drives in GeoManager.
  • If you wish to revert a token-database entry to the original, built-in moniker for the geode, select the geode in the file selector and use the "Remove" trigger in the reply bar.
This document is a single-page version of a a multi-page document, suitable for easy printing.


The INI File

Most programmers are familiar with the structure and function of an initialization file, or INI file. GEOS uses one or more INI files depending on the setup (network users may have two or more INI files; standalone users may have only one).

The INI file for GEOS describes the drivers, fonts, and other items installed on the system. It also contains other system configuration information, such as the specific UI expected and information about the type of display and input devices used. Applications may also the INI file to store configuration information set by the user.

1 How to Use the INI File
2 Categories in the INI File
2.1 [cards]
2.2 [configure]
2.3 [diskswap]
2.4 [envelope]
2.5 [envel<>]
2.6 [expressMenuControl]
2.7 [fileManager]
2.8 [input]
2.9 [keyboard]
2.10 [label]
2.11 [label<>]
2.12 [link]
2.13 [localization]
2.14 [math]
2.15 [modem]
2.16 [<modem name>]
2.17 [mouse]
2.18 [net library]
2.19 [paper]
2.20 [paper<>]
2.21 [parallel]
2.22 [paths]
2.23 [printer]
2.24 [<printer device name>]
2.25 [screen 0]
2.26 [serial]
2.27 [sound]
2.28 [spool]
2.29 [system]
2.30 [text]
2.31 [ui]
2.32 [<specific ui name>]
2.33 [ui features]
2.34 [welcome]

The INI File: 1 How to Use the INI File

As a software developer, you will have two uses for the INI file. First, the INI file controls your system configuration to a certain extent. For example, if you are developing applications for a small-screen pen device, you should know the appropriate settings in the INI file to get your system to emulate such a device.

Second, you may need to have your application access the INI file to store or retrieve information. The kernel offers routines for just this purpose; these routines are detailed in Chapter 6 of the Concepts books.

This chapter describes the file itself as well as the categories and keys created and used by GEOS. Depending on the system you plan to develop for, you may need to set several different keys in certain combinations (e.g. setting certain keys emulates a Zoomer configuration). These special combinations are described in the special add-on documents that describe developing for each system. For more information, contact Geoworks Developer Support.

The INI File: 2 Categories in the INI File

The GEOS.INI File shows a short list, without description, of all the categories and keys available in the INI file. Following the display are explanations of each category, with a description of each key and the values you can set.

Code Display 9-1 The GEOS.INI File

; This is a listing of many of the categories and keys in the GEOS.INI file.
; Each category is described in full in the following sections of this
; chapter, along with the values you can set for each key and what they do.
; The categories and keys are listed alphabetically.
[cards]
deckdir = <directory containing deckfile>
deckfile = <full name of deck>
[configure]
drive <letter> = <drive number>
helpEditor = <Boolean>
numberWS = <maximum number of calculator worksheets>>
[diskswap]
file = <path of swap file>
page = <size of swap page>
size = <size of swap file>
[envelope]
count = <number of user-defined size strings>
newSizes = <list of user-defined size strings>
order = <array of DefaultOrderEntry values>
[envel<num>]
name = <name string>
width = <width in points>
height = <height in points>
layout = <PageLayout structure>
[expressMenuControl]
floatingKeyboard = <Boolean>
otherAppSubMenu = <Boolean>
[fileManager]
dosAssociations = {<list of associations>}
dosLaunchers = <Boolean>
dosParameters = <Boolean>
filenameTokens = {<list of associations>}
fontID = <font ID of font in folder windows>
fontSize = <point size of folder window font>
options = <number>
startupDrivesLocation = <number>
[input]
blinkingCursor = <Boolean>
clickToType = <Boolean>
doubleClickTime = <number of ticks>
keyboardOnly = <Boolean>
left handed = <Boolean>
mouseAccelMultiplier = <number>
mouseAccelThreshold = <number>
noKeyboard = <Boolean>
numberOfMouseButtons = <number>
quickShutdownOnReset = <Boolean>
reboot on reset = <Boolean>
selectDisplaysMenu = <Boolean>
selectRaises = <Boolean>
[keyboard]
device = <full device name>
driver = <driver file name>
keyboardAltGr = <Boolean>
keyboardDoesLEDs = <Boolean>
keyboardShiftRelStr = <Boolean>
keyboardSwapCtrl = <Boolean>
keyboardTypematic = <number>
[label]
count = <number of user-defined size strings>
newSizes = <list of user-defined size strings>
order = <array of DefaultOrderEntry values>
[label<num>]
name = <name string>
width = <width in points>
height = <height in points>
layout = <PageLayout structure>
[link]
name = <machine name>
port = <number>
baudRate = <number>
drives = <list of drives>
[localization]
currencyDigits = <number of decimal digits for currency>
currencyLeadingZero = <Boolean>
currencySymbol = <character of currency symbol>
day
decimalDigits
decimalSeparator
hoursMins24HourTime
hoursMinsSecs24HourTime
hoursMinsSecsTime
hoursMinsTime
hoursTime
longCondensedDate
longDate
longDateNoWeekday
measurementSystem
minsSecsTime
month
monthDayLongDate
monthDayLongDateNoWeekday
monthDayShort
monthYearLong
monthYearShort
negativeSignBeforeNumber = <Boolean>
negativeSignBeforeSymbol = <Boolean>
quotes
shortDate
spaceAroundSymbol = <Boolean>
symbolBeforeNumber = <Boolean>
useNegativeSign = <Boolean>
weekday
year
zeroPaddedShortDate
[math]
coprocessor = <library name for coprocessor>
[modem]
modems = {<modem name list>}
numberOfModems = <number>
[<modem name>]
baudRate = <number>
handshake = <hardware, software>
parity = <none, even, odd, mark, space>
stopBits = <number>
stopLocal = <dsr, dcd, cts>
stopRemote = <dtr, rts>
toneDial = <Boolean>
wordLength = <number>
[mouse]
device = <full device name>
driver = <driver file name>
info = <number>
irq = <number>
port = <number>
[netLibrary]
    InitDrivers = {<list of driver geodes>}
[paper]
count = <number of user-defined size strings>
newSizes = <list of user-defined size strings>
order = <array of DefaultOrderEntry values>
[paper<num>]
name = <name string>
width = <width in points>
height = <height in points>
layout = <PageLayout structure>
[parallel]
port <number of parallel port> = <level of port>
[paths]
<standard path> = <other paths to merge>
ini = <additional .INI files to load>
inisaved = <path of saved .INI file>
sharedTokenDatabase = <path of shared token db file>
[printer]
count = <number>
defaultPrinter = <number>
numFacsimiles = <number>
numPrinters = <number>
printers = {<list of print devices>}
[<printer device name>]
baudRate = <speed of serial communication>
device = <full device name>
driver = <file name of driver>
handshake = <handshake for serial communication>
parity = <parity for serial communication>
port = <port name>
stopBits = <stop bits for serial communication>
type = <type of print device>
wordLength = <word size for serial communication>
[screen 0]
device = <full name of device>
driver = <file name of driver>
oldDevice = <full name of device formerly used>
oldDriver = <file name of drivr formerly used)
[serial]
port <number of serial port> = <level of port>
[sound]
sampleDriver = <driver file name>
synthDriver = <driver file name>
[spool]
uiOptions = <SpoolUIOptions>
[system]
autoCursorCentering = <Boolean>
continueSetup = <Boolean>
drive <letter> = <number>
font = <drivers to be loaded>
fontid = <font to be used as the default>
fontsize = <point size of default font>
fs = <drivers to be loaded>
handles = <number of handles>
inkTimeout = <ticks until ink is processed>
maxTotalHeapSpace = <memory size>
memory = <swap drivers to be loaded>
noFontDriver = <Boolean>
notes = <string>
noVidMem = <Boolean>
pda = <Boolean>
penBased = <Boolean>
power = <file name of power management driver>
serialNumber = <serial number of installed GEOS>
setupMode = <mode for graphical setup application>
splashcolor = <background color>
splashscreen = <Boolean>
splashtext = <text message>
[text]
autoCheckSelections = <Boolean>
autoSuggest = <Boolean>
dialect = <dialect code>
dictionary = <file name of dictionary used by spell checker>
hyphenationDictionary = <file name of dictionary>
hyphenationLanguage = <name of language>
language = <language code>
languageName = <name of language in use>
resetSkippedWordsWhenBoxCloses = <Boolean>
smartQuotes = <Boolean>
[ui]
autosave = <Boolean>
autosaveTime = <seconds between autosaves>
background = <file name of background graphic>
backgroundattr = <t, c, or x>
backgroundcolor = <color index of background>
confirmShutdown = <Boolean>
deleteStateFilesAfterCrash = <Boolean>
doNotDisplayResetBox = <Boolean>
execOnStartup = <list of programs to run on startup>
generic = <generic UI file name>
hardIconsLibrary = <string>
haveEnvironmentApp = <Boolean>
hwr = <file name of handwriting recognition library>
kbdAcceleratorMode = <Boolean>
noClipboard = <Boolean>
noSpooler = <Boolean>
noTaskSwitcher = <Boolean>
noTokenDatabase = <Boolean>
overstrikeMode = <Boolean>
password = <Boolean>
passwordText = <encrypted text>
penInputDisplayType = <number of display type>
productName = <name of the product>
screenBlanker = <Boolean>
screenBlankerTimeout = <number of minutes>
showTitleScreen = <Boolean>
sound = <Boolean>
specific = <specific UI file name>
tinyScreen = <Boolean>
unbuildControllers = <Boolean>
xScreenSize = <width of screen>
yScreenSize = <height of screen>
[<specific ui name>]
fontid = <font>
fontsize = <size in points>
[ui features]
backupDir = <directory for quick backup files>
defaultLauncher = <relative path of application launcher>
docControlFSLevel = <number>
docControlOptions = <number>
expressOptions = <number>
helpOptions = <number>
interfaceLevel = <number>
interfaceOptions = <number>
launchLevel = <number>
launchModel = <number>
launchOptions = <number>
quitOnClose = <Boolean>
windowOptions = <number>
[uiFeatures - intro]
[uiFeatures - beginner]
[uiFeatures - advanced]
[welcome]
enteredprofessionalroom = <Boolean>
startup = <application name to start>
startupRoom = <name of startup room>

[cards]

[configure]

[diskswap]

[envelope]

[envel<>]

[expressMenuControl]

[fileManager]

[input]

[keyboard]

[label]

[label<>]

[link]

[localization]

[math]

[modem]

[<modem name>]

[mouse]

[net library]

[paper]

[paper<>]

[parallel]

[paths]

[printer]

[<printer device name>]

[screen 0]

[serial]

[sound]

[spool]

[system]

[text]

[ui]

[<specific ui name>]

[ui features]

[welcome]

The INI File: 2.1 Categories in the INI File: [cards]

The cards category contains information used by the cards library. The cards library provides routines used by card games. The cards category contains information about how to access the file containing the bitmap to be used as the card back picture.

deckdir
deckdir = <deck directory>

This optional field shows the path which contains the file named in the deckfile key, described below. If this key is not given, the cards library will look for deck files in the USERDATA\DECK directory.

deckfile
deckfile = <deck name>

The deckfile key defines the full name of the deck to be used by the card library. This is most useful in cases like the Zoomer, which must have its own card artwork. By default, the cards library will look in the USERDATA\DECK directory.

deckfile = Zoomer Default Deck

The INI File: 2.2 Categories in the INI File: [configure]

The configure category contains miscellaneous configuration information for GEOS. The drive key is exactly like the drive key in the system category.

drive
drive <letter> = <number>

This key allows you to override the drive-map initialization done by the primary filesystem driver. You can not make a driver believe a nonexistent drive exists, but you can change the presumed media or make the driver ignore a drive. More than one drive may be remapped.

The letter argument is the drive letter of the drive to be remapped. The number argument defines the new drive definition and is one of the following values:

  -1		fixed disk
   0		ignore the drive
 360		360 K 5.25-inch disk
 720		720 K 3.5-inch disk
1200		1.2 meg 5.25-inch disk
1440		1.44 meg 3.5-inch disk
2880		2.88 meg 3.5-inch disk

Some examples of drive remappings are shown below:

drive d = 0			; ignore drive D:
drive a = 360			; make GEOS think drive A: is 360K
helpEditor
helpEditor = <Boolean>

If true, this key indicates that GeoWrite should add a new Help Editor feature. This must be on if you are planning on creating help files for your application. The Help Editor feature may be turned on by selecting "Fine Tune" in the user level dialog box in GeoWrite. More information on the Help Editor can be found in the chapter on the help system.

helpEditor = true
helpEditor = false
numberWS
numberWS = <number of calculator worksheets>

When users use the worksheets feature of the calculator, each worksheet they use will be loaded into memory. Normally, these worksheets are kept in memory so that the user may quickly go back to a worksheet they were using previously. Some low-memory devices may prefer that fewer worksheets are cached in this manner. The numberWS specifies a maximum number of worksheets that may be so cached.

The INI File: 2.3 Categories in the INI File: [diskswap]

The diskswap category defines swap information for the GEOS swap file. Generally, you won't set these keys individually; GEOS will set them as required.

file
file = <path of swap file>

This category defines the file used by the disk swap driver for swapping.

file = C:\GEOWORKS\SWAP\EXTRA
page
page = <size of swap page>

This key defines the page size of a swap page.

page = 2048
size
size = <size of swap file>

This key defines the maximum size of the swap file.

size = 2048

The INI File: 2.4 Categories in the INI File: [envelope]

This category keeps track of any customizations the user may have made to the list of envelope paper sizes.

count
count = <number>

This is the number of user-defined envelope sizes.

newSizes
newSizes = <list of paper size codes>

This list contains a list of all user-defined envelope sizes. The paper size information for each of these sizes will be stored in a category named [envelnum], where num is a three-digit number. A sample newSizes entry:

newSizes = {
    envel128
    envel129
}
order
order = <list of paper size codes>

This list contains a list of all envelope sizes in the order that the user wants them to appear in envelope size lists.

The INI File: 2.5 Categories in the INI File: [envel<num>]

This category contains size and layout for a user-defined envelope size.

height
height = <number>

This field holds the envelope's height in points.

layout
layout = <PageLayout value>

This field holds the envelope's layout information.

name
name = <string>

This field holds the envelope's full name.

width
width = <number>

This field holds the envelope's width in points.

The INI File: 2.6 Categories in the INI File: [expressMenuControl]

The expressMenuControl category defines the configuration of the express menu; see the ui features category for more express menu controls.

floatingKeyboard
floatingKeyboard = <Boolean>

If true, this key adds an item to the express menu to bring up the floating keyboard (used for pen-based systems). The default is false.

floatingKeyboard = true
floatingKeyboard = false
maxNumDirs
maxNumDirs = <number>

If this field exists, then if there are more entries in the Other Apps section of the Express Menu than this, the Other Apps section will be forced into a submenu (and forced into a subgroup if less than this), regardless of `noSubMenus' and `otherAppSubMenu'. Defaults to 25 (the absolute maximum number of Other Apps entries).

noSubMenus
noSubMenus = <Boolean>

If true, the express menu will not allow forcing the main applications,other applications, or desk accessories into submenus.

otherAppSubMenu
otherAppSubMenu = <Boolean>

If true, this key turns the "other applications" section in the express menu into a submenu rather than a subgroup. The default is false.

otherAppSubMenu = true
otherAppSubMenu = false
runningAppSubMenu
runningAppSubMenu = <Boolean>

If true, this key causes the express menu include a list of currently running GEOS applications as a submenu. If false, the list will be placed directly in the express menu.

runSubMenu
runSubMenu = <Boolean>

If true, top level applications (those placed in the WORLD directory) and top level subdirectories (subdirectories of WORLD) in a submenu of the express menu.

The INI File: 2.7 Categories in the INI File: [fileManager]

The fileManager category is used by file manager applications such as GeoManager. You will probably not find much cause to use these keys during your application development.

dosAssociations
dosAssociations = {<list of associations>}

This key allows a user to associate DOS data files with DOS executables so a particular DOS executable will be launched when the user double-clicks the data file.

dosAssociations = {
    *.ZIP = C:\PKUNZIP.EXE
}
dosLaunchers
dosLaunchers = <Boolean>

If true, this key allows DOS launchers to launch DOS programs. The default is true.

dosLaunchers = true
dosLaunchers = false
dosParameters
dosParameters = <Booeans>

If true, the file manager will allow the passing of parameters to DOS executables..

filenameTokens
filenameTokens = {<list of associations>}

This key allows the user to set icon associations with DOS files. It also allows certain text files to be opened by the text file editor. Certain associations are made by default and should always appear; these are listed below.

filenameTokens = {
    *.EXE = "gDOS",0
    *.COM = "gDOS",0
    *.BAT = "gDOS",0
    *.TXT = "FILE",0,"TeEd",0
    *.DOC = "FILE",0,"TeEd",0
    *.HLP = "FILE",0,"TeEd",0
}
fontID
fontID = <number>

This key sets the font used by GeoManager (or another file manager) when displaying the names and information of files in the folder window. The font ID is set the same way as for the system category; if no font ID is named, the default system font will be used.

fondID = berkeley
fontSize
fontSize = <number>

This key sets the font size used in a folder window. If not specified, it will default to the system font.

fontSize = 10
options
options = <number>

This key controls various file manager options. The options are set and cleared by the user using the file manager's Options menu; the number is a decimal number representing the bits set or cleared for various options.

startupDrivesLocation
startupDrivesLocation = <number>

This key controls the initial location of drive buttons; it is set with the Options menu in the file manager.

The INI File: 2.8 Categories in the INI File: [input]

The input category contains a number of keys that define how input is handled by the system. It affects the keyboard and mouse setup as well as how the UI responds to various user actions.

blinkingCursor
blinkingCursor = <Boolean>

If true, this key forces the text cursor to be a blinking cursor; it defaults to true. The screen dumper application requires a non-blinking cursor.

blinkingCursor = true
blinkingCursor = false
clickToType
clickToType = <Boolean>

If true, this key requires the user to click in a window before the focus will change to that window. When this key is false, the UI will invoke a "real estate" mode, wherein the user's typing will go to the window under the mouse, whether the mouse was clicked there or not. The default is true (click required).

clickToType = true
clickToType = false
doubleClickTime
doubleClickTime = <number of ticks>

This field specifies the time threshhold between clicks which should be recognized as a double-click. This time is expressed in 1/60th second "ticks". The default value is 20.

keyboardOnly
keyboardOnly = <Boolean>

If true, this key indicates that GEOS is running on a system with only a keyboard (no mouse) for input. The default is false.

keyboardOnly = true
keyboardOnly = false
left handed
left handed = <Boolean>

If true, this key switches the left and right mouse button significance. For single-button mice, there is no effect; for three-button mice, the middle button stays the same. The default is false.

left handed = true
left handed = false
mouseAccelMultiplier
mouseAccelMultiplier = <number>

This key gives a multiplier to allow mouse acceleration along the following rule: any single event pixel movement beyond a given threshold (see mouseAccelThreshold ) is multiplied by the multiplier. The multiplier defaults to one, which provides no acceleration. (A multiplier of three or four is fast.)

mouseAccelMultiplier = 1					; no acceleration
mouseAccelMultiplier = 4					; very fast
mouseAccelThreshold
mouseAccelThreshold = <number>

This key gives the mouse acceleration threshold used with mouseAccelMultiplier (above) to provide mouse acceleration. This threshold is the number of pixels the mouse must move before acceleration is invoked.

mouseAccelThreshold = 5
noKeyboard
noKeyboard = <Boolean>

If true, this key indicates that the system running GEOS has no keyboard. The default is false.

noKeyboard = true
noKeyboard = false
numberOfMouseButtons
numberOfMouseButtons = <number>

This key defines the number of buttons the mouse has. This may be the value one, two, or three. The default is three.

numberOfMouseButtons = 1
numberOfMouseButtons = 2
numberOfMouseButtons = 3
quickShutdownOnReset
quickShutdownOnReset = <Boolean>

If true, this key forces a Ctrl-Alt-Del sequence to force a dirty shutdown of GEOS. This defaults to true.

quickShutdownOnReset = true
quickShutdownOnReset = false
reboot on reset
reboot on reset = <Boolean>

If true, this key causes a Ctrl-Alt-Del sequence to warm-boot the machine rather than exit quickly to DOS. The default is false.

reboot on reset = true
reboot on reset = false
selectDisplaysMenu
selectDisplaysMenu = <Boolean>

If true, this key reverses the left and right mouse buttons with respect to menus in some specific UIs. (For example, the left (select) button will open the menu, but the right (features) button will show and execute the default menu item.) The default is false.

selectDisplaysMenu = true
selectDisplaysMenu = false
selectRaises
selectRaises = <Boolean>

If true, this key causes a select-button click to raise the window in which the click occurred, if the window was behind other windows. The window will not be raised above other windows that are always kept on top (e.g. the help window and desk accessory applications). The default is true.

selectRaises = true
selectRaises = false

The INI File: 2.9 Categories in the INI File: [keyboard]

device
device = <full device name>

This key defines the keyboard in use. It must be the keyboard device's full name; in general, this should only be set by the Preferences manager application.

device = U.S. Keyboard
driver
driver = <driver file name>

This key goes with the device key and defines the driver file name to be loaded. This should be set by the Preferences manager.

driver = kbd.geo
keyboardTypematic
keyboardTypematic = <number>

This key defines both the repeat speed and delay before repeat for the keyboard. The number is an integer less than 128 (the high bit is ignored), and it is interpreted as three separate fields, as below:

bit 7		ignored
bit 6-5		DELAY (see below)
bit 4-3		PE (exponent portion of repeat period)
bit 2-0		PM (mantissa portion of repeat period)
The delay is calculated by the following formula:
delay = 1 second + (DELAY * 250 ms) +/- 20%
The period is calculated by the following formula
period = (8 + PM) * (2^PE) * 0.00417 seconds

If no typematic number is specified, GEOS sets the default to 44, which represents a medium delay and a medium repeat period.

keyboardTypematic = 0				; short delay, fast repeat
keyboardTypematic = 127				; long dalay, slow repeat
keyboardDoesLEDs
keyboardDoesLEDs = <Boolean>

If true, this key tells GEOS that the XT-class machine it's running on supports BIOS-updated LEDs for Num Lock, Caps Lock, and Scroll Lock. Most XT-class machines do not support updating these LEDs. This field is unnecessary on AT-class and more advanced machines.

keyboardDoesLEDs = true
keyboardDoesLEDs = false
keyboardAltGr
keyboardAltGr = <Boolean>

If true, this key makes the right Alt key function like Ctrl-Alt, as with many European setups.

keyboardAltGr = true
keyboardAltGr = false
keyboardShiftRelStr
keyboardShiftRelStr = <Boolean>

If true, this key makes the Shift keys release the Caps Lock, as with typewriters.

keyboardShiftRelStr = true
keyboardShiftRelStr = false
keyboardSwapCtrl
keyboardSwapCtrl = <Boolean>

If true, this key swaps the left Ctrl key with the Caps Lock key so the keyboard acts like many non-PC keyboards.

keyboardSwapCtrl = true
keyboardSwapCtrl = false

The INI File: 2.10 Categories in the INI File: [label]

This category keeps track of any customizations the user may have made to the list of label paper sizes.

count
count = <number>

This is the number of user-defined label sizes.

newSizes
newSizes = <list of paper size codes>

This list contains a list of all user-defined label sizes. The paper size information for each of these sizes will be stored in a catgory named [labelnum]. A newSizes entry might read:

newSizes = {
    label128
    label129
}
order
order = <list of paper size codes>

This list contains a list of all label sizes in the order that the user wants them to appear in label size lists.

The INI File: 2.11 Categories in the INI File: [label<num>]

This category contains size and layout for a user-defined label size.

height
height = <number>

This field holds the label's height in points.

layout
layout = <PageLayout value>

This field holds the label's layout information.

name
name = <string>

This field holds the label's full name.

width
width = <number>

This field holds the label's width in points.

The INI File: 2.12 Categories in the INI File: [link]

These fields are used by the Remote File System Driver to describe the machine to other machines that wish to access its drives. You may specify a name for the machine by which others may identify it and also set up communications parameters.

baudRate
baudRate = <number>

This SerialBaud value defines the communication speed this machine supports for RFSD.

drives
baudRate = <list of drives>

Normally, all of your drives will be accessible by the remote machine. This field allows you to specify exactly which drives are accessible. For instance, to restrict remote machines to accessing your C: and E: drives, use the following entry:

drives = {
C: E:
}
name
name = <machine name>

A string by which remote machines may identify you. When the remote machine sees your drives, their names will be <machine-name>-<drive-letter>:.

port
port = <SerialPortNum>

This number specifies which serial port is to be used for RFSD.

The INI File: 2.13 Categories in the INI File: [localization]

The localization key defines various configuration aspects of the system as set by the user in the Preferences manager application. Each of the keys in this category specifies one aspect of the user's system, typically an aspect defined by the country the user lives in. Because all of these keys are set in the Preferences manager application, they are not listed here. In general, keys which encode characters or strings will do so using ASCII values (e.g. "decimalSeparator = 2E" means that `.' is the decimal separator). The kernel also provides a number of routines to get and set the localization parameters; for more information, see the chapter on Localization in the Concepts books.

The INI File: 2.14 Categories in the INI File: [math]

coprocessor = <driver name>

The math category allows the user to override the way GEOS normally treats math coprocessors. The single key ( coprocessor ) specifies the coprocessor library to use; the math library will load that particular library, and the library will check to ensure the proper coprocessor chip exists. If the chip is present, the library will be used; if the chip is absent, the math library will use software emulation of a coprocessor.

coprocessor = none				; use software emulation
coprocessor = intx87.geo					; Intel 80387, 80486
coprocessor = intx8087.geo					; Intel 80287, 8087

The INI File: 2.15 Categories in the INI File: [modem]

The modem category defines the modems attached to the system. Each modem must have its name listed in the name list, and each modem named in the list must have its own category (see the modem name category, below).

modems
modems = {<modem name list>}

This key defines all the modems attached to the system. Each modem in the list must have its own category in the .INI file, as shown below.

modems = My Modem
modems = { My Slow Modem
         My Fast Modem }
numberOfModems
numberOfModems = <number>

This key defines the number of modems specified in the modems list (above).

numberOfModems = 1
numberOfModems = 2

The INI File: 2.16 Categories in the INI File: [<modem name>]

Each modem listed in the modems keyword in the modem category must have its own category. The category is named for the modem name in the list. Thus, the following example shows that each modem in the system has its own category:

[modem]
numberOfModems = 2
modems = {My Slow Modem
         My Fast Modem }
[My Slow Modem]
port = COM1
baudRate = 300
toneDial = true
parity = none
wordLength = 8
stopBits = 1
handshake = software
[My Fast Modem]
port = COM3
baudRate = 19200
toneDial = true
parity = none
wordLength = 8
stopBits = 1
handshake = software
baudRate
baudRate = <number>

This key defines the modem`s baud rate.

baudRate = 2400
baudRate = 9600
handshake
handshake = <hardware, software>

This key indicates the type of handshake used by the modem. It must be one of the values specified.

handshake = hardware
handshake = software
parity
parity = <none, even, odd, mark, space>

This key indicates the modem's parity type. It must be one of the values shown above.

parity = none
parity = even
stopBits
stopBits = <number>

This key specifies the number of stop bits used by the modem. This should be 1, 1.5, or 2.

stopBits = 1
stopBits = 1.5
stopBits = 2
stopLocal
stopLocal = <dsr, dcd, cts>

If hardware handshaking is used, this key specifies which line the serial driver will watch for the stop signal.

stopLocal = dsr
stopLocal = dcd
stopLocal = cts
stopRemote
stopRemote = <dtr, rts>

If hardware handshaking is used, this key specifies which line the serial driver will use to make the remote side of the connection stop.

stopRemote = dtr
stopRemote = rts
toneDial
toneDial = <Boolean>

If true, this key indicates that the modem may use tone dialing. The default is true.

toneDial = true
toneDial = false
wordLength
wordLength = <number>

This key indicates the communication word length. This should be a number between five and eight inclusive.

wordLength = 8

The INI File: 2.17 Categories in the INI File: [mouse]

The mouse category defines the mouse driver and specifics of the mouse attached to the GEOS system. Both the device and driver keys are required; the others are optional.

device
device = <device name>

This key defines the type of mouse attached. It must be the full device name and is typically set during setup of the system.

device = Logitech Bus Mouse
device = No idea
driver
driver = <file name>

This key defines the file name of the mouse driver in use.

logibus.geo
logibuse.geo
genmouse.geo
info
info = <number>

This key defines the "extra word" of data for the mouse. This is an internal structure set by the mouse driver.

irq
irq = <number>

This key allows you to set the interrupt level of a mouse that needs it; most mice do not need to be told their interrupt level.

irq = 4
port
port = <number>

This key specifies the port of a serial mouse, if necessary. The port number is one, two, three, or four, appropriate to the COM port being used.

port = 3

The INI File: 2.18 Categories in the INI File: [net library]

InitDrivers = { <list of driver geodes> }

This is a list of network drivers to use. The Net library will attempt to load these driver geodes.

The INI File: 2.19 Categories in the INI File: [paper]

This category keeps track of any customizations the user may have made to the list of paper sizes.

count
count = <number>

This is the number of user-defined paper sizes.

newSizes
newSizes = <list of paper size codes>

This list contains a list of all user-defined paper sizes. The paper size information for each of these sizes will be stored in a catgory named [papernum], where num is the three-letter code in this list. For example:

newSizes = {
    paper128
    paper129
}
order
order = <list of paper size codes>

This list contains a list of all paper sizes in the order that the user wants them to appear in paper size lists. There should be no spaces between the numbers. A sample entry:

order = 128,129,029,031,024,043,049,052,057,

The INI File: 2.20 Categories in the INI File: [paper<num>]

This category contains size and layout for a user-defined paper size.

name
name = <string>

This field holds the paper's full name. The string should not be included in quotes.

height
height = <number>

This field holds the page's height in points.

width
width = <number>

This field holds the paper's width in points.

layout
layout = <PageLayout value>

This field holds the paper's layout information. This needs to be entered as an integer value calculated by adding up the following values:

  • PageType (PAPER = 0, ENVELOPE = 2, LABEL = 4)
  • PaperOrientation (PORTRAIT = 0, LANDSCAPE = 8)
  • Number of labels per row = number * 8.
  • Number of labels per column = number * 512

A sample entry is shown below:

[paper128]
name	= small landscape 1x2 inch
width	= 72
height	= 144
layout	= 8

The INI File: 2.21 Categories in the INI File: [parallel]

port <number> = <level>

The parallel category defines all the parallel ports available on the machine running GEOS. If a port is not defined in this category, GEOS will not recognize its existence. The single key in this category may be used numerous times, once for each available parallel port.

The port key defines the hardware interrupt level for the specified port. The normal entries for the three base parallel ports are shown in the examples below. If no level is provided for a port, the parallel driver will assign the values shown below; if no interrupt level is available for a port, GEOS will instead spawn a background thread for it. Setting any port's value will override other defaults (e.g. if you set port two to have level seven, port one will be set to level five, unless GEOS is on an XT- or PC-class machine).

port 1 = 7
port 2 = 5
port 3 = 0

The INI File: 2.22 Categories in the INI File: [paths]

The paths category defines other directories to add into a standard path as well as additional INI files and the location of the shared token database. Adding directories to standard paths is useful both in network situations and if you want your application installed in a special directory but linked to the WORLD directory. This category uses five different keys, as shown below.

standard paths
<standard path> = <additional paths>

Each standard path is its own key, and you can merge other directories into any standard path. Some examples are shown below.

top = C:\GEOWORKS C:\PCGEOS
world = E:\INSTALL\NEWAPP
userdata font = N:\NETFONTS F:\SPECFONT
ini
ini = <file names>

This key defines up to three additional INI files to be loaded read-only. When GEOS searches for a key, the local (current) INI file is scanned first, followed by the additional INI files in the order they're defined. The first occasion of the key will be used; thus, the local INI file can supersede other settings.

ini = personal.ini INI\mydevice.ini n:\shared.ini
inisaved
inisaved = <files>

This key is used only if GEOS is run with the /psaved argument. That is, if the user runs GEOS thus:

C>GEOS /psaved

then GEOS will look for the inisaved key rather than the ini key for additional INI files. Similarly, if the user instead runs

C>GEOS /pxxx

GEOS will look for a key called inixxx for the names of the files to be used.

inisaved = net.ini
inisaved = demo.ini net.ini
sharedTokenDatabase
sharedTokenDatabase = <path>

This key defines the location of the shared token database file. This key is most useful in network situations, when many users may be sharing a single token database.

sharedTokenDatabase = N:\NETFILES\TOKEN_DA.000

The INI File: 2.23 Categories in the INI File: [printer]

The printer category defines all the printers configured for the system running GEOS. The printers key within this category (see below) defines the printer names, each of which must then have its own category in the INI file (see the following section under printer device name ).

count
count = <number>

This key indicates the number of printers installed.

count = 0			; no printers installed
count = 2			; two printers installed
defaultPrinter
defaultPrinter = <number>

This key specifies the number of the installed printer that will act as the default device.

defaultPrinter = 2				; printer #2 is the default
numFacsimiles
numFacsimiles = <number>

This key specifies the number of installed print devices which are actually fax drivers rather than printer drivers.

numFacsimiles = 1
numPrinters
numPrinters = <number>

This key specifies the number of installed print devices which are actually printers (as opposed to faxes or other devices).

numPrinters = 2
printers
printers = {<list of devices>}

This key defines all the installed print devices. Each entry in the list is the device name; the list must be a blob, with one printer named per line as in the example below. Each entry in the list must also have its own category defining the driver, device name, port, and type.

printers = { My Printer
         My PostScript to file }

The INI File: 2.24 Categories in the INI File: [<printer device name>]

Each printer defined in the printers key of the printer category (see above) must have its own category. The name of the category must be the same as the printer named in the installed printers list of the printer category. The keys below define the printer's characteristics.

baudRate
baudRate = <number>

This key defines the printer's communication rate, for those printers which communicate via a serial connection.

device
device = <device name>

This key defines the device name of the installed print device.

device = Apple LaserWriter Plus v38.0 (PostScript)
driver
driver = <file name>

This key defines the print driver used for the installed print device.

driver = PostScript driver
handshake
handshake = <hardware, software>

This key indicates the type of handshake used by the printer for those printers which communciate via a serial connection. It must be one of the values specified above.

handshake = hardware
handshake = software
parity
parity = <none, even, odd, mark, space>

This key indicates the printer's parity type for those printers which communicate via a serial connection. It must be one of the values shown above.

parity = none
parity = even
port
port = <port name>

This key specifies the port to which the device is attached. This must be one of the values shown in the following examples.

port = LPT1
port = LPT2
port = LPT3
port = COM1
port = COM2
port = COM3
port = COM4
port = FILE
stopBits
stopBits = <number>

This key specifies the number of stop bits used by the printer for those printers which communciate via a serial connection. This should be 1, 1.5, or 2.

stopBits = 1
stopBits = 1.5
stopBits = 2
type
type = <number>

This key defines the type of device this installed print device is. The number indicates the PrinterDriverType as defined in spool.goh .

type = 0		; PDT_PRINTER
type = 3		; PDT_CAMERA
wordLength
wordLength = <number>

This key indicates the communication word length for those printers which communciate via a serial connection. This should be a number between five and eight inclusive.

wordLength = 8

The INI File: 2.25 Categories in the INI File: [screen 0]

The screen N category is used to define the characteristics of each screen in the system. Currently, GEOS completely supports only one screen, which is called screen 0. This screen's characteristics are defined by the screen 0 category.

device
device = <device name>

This key specifies the full device name of the screen's device. The standard devices are shown in the examples below.

device = Hercules HGC: 720x348 Mono
device = IBM MCGA: 640x480 Mono
device = CGA: 640x200 Mono
device = EGA: 640x350 16-color
device = VGA: 640x480 16-color
driver
driver = <file name>

This key specifies the file name of the driver used to run this screen.

driver = vga.geo
olddevice
olddevice = <device name>

When the user switches between video drivers, the system keeps track of the old device name in case the user made a mistake and wants to switch back. It will store the old value of the "device" field in this field.

olddriver
olddriver = <file name>

When the user switches between video drivers, the system keeps track of the old driver name in case the user made a mistake and wants to switch back. It will store the old value of the "driver" field in this field.

userdevice
userdevice = <device name>

GEOS doesn't use this field. The Debug utility uses this field to store the user's personal video device choice when simulating hardware devices that would not support the choice.

userdriver
userdriver = <file name>

GEOS doesn't use this field. The Debug utility uses this field to store the user's personal video driver choice when simulating hardware devices that would not support the choice.

The INI File: 2.26 Categories in the INI File: [serial]

port <number> = <level>

The serial category defines all the serial ports available on the machine running GEOS. If a port is not defined in this category, GEOS will not recognize its existence. The single key in this category may be used numerous times, once for each available serial port.

The port key defines the hardware interrupt level for the specified port. The normal entries for the four base serial ports are shown in the examples below. If no value is specified, GEOS will try to generate an interrupt for the port and set the value itself. (It only checks levels three and four, though.) Note also that on an AT-class machine, level two for a card is actually level nine specified here.

port 1 = 4
port 2 = 3
port 3 = 4			; may not work if port 1 is in use
port 4 = 3			; may not work if port 2 is in use

The INI File: 2.27 Categories in the INI File: [sound]

The sound category is used by the sound library to determine which sound driver is selected for the system running GEOS.

sampleDriver
sampleDriver = <driver file name>

This key specifies the sound driver that will be used to process all the sampled sounds produced by the system. If this key is not set, the standard sound driver ( standard.geo ) will be used.

sampleDriver = sblaster.geo
synthDriver
synthDriver = <driver file name>

This key specifies the sound driver that will be used for all synthesized sounds (beeps, UI sounds, etc.) If this key is not set, the standard sound driver ( standard.geo ) will be used.

synthDriver = standard.geo

The INI File: 2.28 Categories in the INI File: [spool]

simpleUI = <Boolean>

The spool category has a single key used by the print spooler to configure its user interface. If true, the simpleUI key will display only a simple UI scheme; this is especially useful for small-screen devices because it significantly reduces the size of the print control dialog box. The default is false.

simpleUI = true
simpleUI = false

The INI File: 2.29 Categories in the INI File: [system]

The system category defines system configuration and setup. Most of the keys in this category are set and maintained by the Preferences manager application. These keys, with their formats and possible values, are shown in the following sections.

autoCursorCentering
autoCursorCentering = <Boolean>

If this key is set true, the mouse cursor is re-positioned to the center of the current window whenever the pointer cursor changes and no button is down.

continueSetup
continueSetup = <Boolean>

If this key is set true, GEOS will begin by running the graphical setup program in the proper setup modes. If false, GEOS will bypass the graphical setup. After running, the graphical setup program will reset this field to false. This field overrides the execOnStartup key of the ui category.

continueSetup = true
continueSetup = false
drive
drive <letter> = <number>

This key allows you to override the drive-map initialization done by the primary filesystem driver. You can not make a driver believe a nonexistent drive exists, but you can change the presumed media or make the driver ignore a drive. More than one drive may be remapped.

The letter argument is the drive letter of the drive to be remapped. The number argument defines the new drive definition and is one of the following values:

  -1		fixed disk
   0		ignore the drive
 360		360 K 5.25-inch disk
 720		720 K 3.5-inch disk
1200		1.2 meg 5.25-inch disk
1440		1.44 meg 3.5-inch disk
2880		2.88 meg 3.5-inch disk

Some examples of drive remappings are shown below:

drive d = 0			; ignore drive D:
drive a = 360			; make GEOS think drive A: is 360K
font
font = <driver file names>

This key causes the named font driver to be loaded. If this key doesn't exist, nimbus.geo will automatically be loaded (the default driver). More than one driver may be specified on a single line or in a blob format, as shown in the examples below. (Note, though, that at current only nimbus.geo is recognized.)

font = nimbus.geo otherdrv.geo
font = { nimbus.geo
       otherdrv.geo }
fontid
fontid = <font name>

This key specifies the default font used in the event a requested font does not exist. This font will also be used when putting up system alert boxes (such as Abort/Retry boxes). The only available default font currently is Berkeley; typically, this will be a bitmap font rather than an outline font.

fontid = berkeley
fontmenu
fontmenu = <string of numbers>

This field specifies the order of fonts which should appear in font menus presented by font control objects. This is encoded as a string of numbers, four hex digits for each font, those four digits containing the font ID of the appropriate font. Thus, if the font ID's for the URW Roman and Berkeley fonts are 0x3000 and 0x0202, respectively, then if they are to be the first fonts in the font menu, the fontmenu field would read:

fontmenu = 30000202
fontsize
fontsize = <number>

This key specifies the point size of the default font. If an application requests a font that can't be found, the default point size specified here is used with the font specified with fontid . Berkeley supports 9, 10, 12, 14, and 18, though 18 is normally too large for many applications.

fontsize = 10
fonttool
fontmenu = <string of numbers>

This field specifies the order of fonts which should appear in font pop-up list presented by font control objects. This is encoded as a string of numbers, four hex digits for each font, those four digits containing the font ID of the appropriate font. Thus, if the font ID's for the URW Roman and Berkeley fonts are 0x3000 and 0x0202, respectively, then if they are to be the first fonts in the font pop-up list, the fontool field would read:

fonttool = 30000202
fs
fs = <driver file names>

This key defines the file system drivers to be loaded. The kernel will by default attempt to load the primary IFS driver for the detected version of DOS; if it can not determine the primary IFS driver, the proper driver must be specified in the INI file under this key. Multiple file system drivers may be specified either on a single line or in blob format. The current secondary IFS drivers available are

netware.geo
Used for Novell Netware systems.
msnet.geo
Used for LANtastic and other networks that support the standard DOS device redirection calls.
cdrom.geo
Used for CD-ROM drives accessed through MSCDEX.EXE.
fs = netware.geo
fs = { msnet.geo
     cdrom.geo }
handles
handles = <number>

This key specifies the number of handles GEOS should set as the maximum in the handle table. This should be set to something most likely 2000 or above, and it may be set in the Preferences manager application. If nothing is set in this key, the kernel will assume a default of 1000 handles.

handles = 2000
inkTimeout
inkTimeout = <number>

This key sets the number of ticks (60 ticks in a second) the system will wait after the user has stopped drawing before processing ink input. The default is nine tenths of a second, or 54.

inkTimeout = 54
maxTotalHeapSpace
maxTotalHeapSpace = <size of heap in paragraphs>

This field causes the GeodeLoad() routine to operate in transparent launch mode. The value given represents the overall size of the heap, in paragraphs, excepting system libraries that are always in memory. It should be determined on the target machine itself, by starting up, then running the TCL function "heapspace total". A common value for this field is around 31000.

memory
memory = <driver file names>

This key defines the swap drivers that should be loaded. Swap drivers allow GEOS to use memory above the conventional 640 K. The kernel attempts to determine what type of memory is available and load the appropriate swap driver. This key is settable by the user with the Preferences manager application. The four driver names recognized are

emm.geo
LIM 4.0 standard expanded memory driver. A DOS-level memory driver must be loaded (e.g. EMM.SYS), typically in CONFIG.SYS.
extmem.geo
80286 extended memory driver.
xms.geo
XMS/HIMEM.SYS driver. A DOS-level driver must also be loaded (e.g. HIMEM.SYS), typically in CONFIG.SYS.
disk.geo
Disk swap driver. This should be loaded in all cases where a disk swap file is desired.
memory = disk.geo
memory = { disk.geo
         xms.geo }
noFontDriver
noFontDriver = <Boolean>

If true, this key tells GEOS not to load the font driver; this is useful only when it is known beforehand that outline fonts are not available; it will reduce startup time of GEOS. If the key does not exist, it defaults to false. If used improperly, this key can cause bad things to happen in the system.

noFontDriver = true
noFontDriver = false
notes
notes = <string>

This field isn't used by GEOS proper. The Debug utility will search for this field when looking for text describing a platform which the .ini file simulates.

noVidMem
noVidMem = <Boolean>

If true, this key tells GEOS not to load the vidmem driver, which is used for printing; it will reduce startup time of GEOS and should be used only if it is known beforehand that printing will not be attempted. If the key does not exist, it defaults to false. If used improperly, this key can cause bad things to happen in the system.

noVidMem = true
noVidMem = false
pda
pda = <Boolean>

This field indicates whether GEOS is running on a PDA device. Currently this field is only used by the UI to provide alternate error strings.

penBased
penBased = <Boolean>

If true, this key tells GEOS that it is running on a pen-based system and that some objects will want to receive ink or other pen input. If the key is not set, it defaults to false.

penBased = true
penBased = false
power
power = <driver file name>

This key defines the power management drivers to be loaded, if any. If no driver is specified, the kernel will try to identify whether one is needed and then load it if necessary.

power = casio.geo
serialNumber
serialNumber = <number>

This key holds a predefined serial number for use by developers. This number will be given to you by Geoworks either directly or in the package you receive containing GEOS. Normally, this number is entered by the user when GEOS first finishes its graphical setup program.

setupMode
setupMode = <number>

This key indicates the mode of the graphical setup program. This should be a number from zero to three; for full graphical setup, set it to zero. Other modes are internal in nature and should not be set.

setupMode = 0
splashColor
splashColor = <Color value>

If the splashscreen option has been turned on, this field will determine the background color of any text splash screens shown.

splashscreen
splashscreen = <Boolean>

This permits the GEOS loader to display a message on one of the five simple graphics mode screens while GEOS is loading.

splashText
splashText = <string>

If the splashscreen option has been turned on, this field will determine the text of the message to display.

The INI File: 2.30 Categories in the INI File: [text]

The text category defines various characteristics of GEOS for the text objects, the spelling checker, and localization.

autoCheckSelections
autoCheckSelections = <Boolean>

If true, this key instructs the spelling checker to check the spelling of the selected text automatically when the user brings up the spell-check box. The default is true.

autoCheckSelections = true
autoCheckSelections = false
autoSuggest
autoSuggest = <Boolean>

If true, this key instructs the spelling checker to suggest other spellings automatically if a misspelling is found. The default is false.

autoSuggest = true
autoSuggest = false
dialect
dialect = <dialect code>

This key defines the dialect code used by the dictionary for spelling. Different dictionaries use different dialects within their own language. The Each dialect is represented by a number; the default setting is 128. The different dialects are listed below, by dictionary.

English		 32	IZE British		(realize/colour)
         64	ISE British		(realise/colour)
        128	American		(realize/color)
Dutch		 64	Standard and non-preferred forms
        128	Standard Dutch forms only
French		 64	Accents on uppercase characters
        128	No accents on uppercase characters
German		 64	German Doppel s
        128	German Scharfes s
Norwegian		 64	Nynorsk standard
        128	Bokmal standard
Portuguese		 64	Brazilian Portuguese
        128	Iberian Portuguese

Some examples of setting the dialect are shown below.

dialect = 64
dialect = 128
dictionary
dictionary = <file name>

This key allows the user or the Preferences manager application to set the dictionary used by the spelling checker. The dictionary is set by specifying the file name of the dictionary data file; the default value is that for the English dictionary.

dictionary = IDNF9111.DAT					; Danish
dictionary = IENC9121.DAT					; English
dictionary = IFRF9121.DAT					; French
dictionary = IGRF9112.DAT					; German
dictionary = IITF9110.DAT					; Italian
dictionary = IPOF9110.DAT					; Portuguese
dictionary = ISPF9110.DAT					; Spanish
language
language = <number>

This key specifies the language in use by GEOS. The number is a language code (as shown in the examples below), and the user may set the language with the Preferences manager application. The default is English, 16.

language = 5				; French
language = 6				; German
language = 7				; Swedish
language = 8				; Spanish
language = 9				; Italian
language = 10				; Danish
language = 11				; Dutch
language = 12				; Portuguese
language = 13				; Norwegian
language = 14				; Finnish
language = 15				; Swiss
language = 16				; English
languageName
languageName = <name of language>

This key specifies the name of the language in use; the default is American English. This key is normally set by the Preferences manager application.

languageName = American English
resetSkippedWordsWhenBoxCloses
resetSkippedWordsWhenBoxCloses = <Boolean>

If true, this key instructs the spelling checker to reset its list of skipped words when the user closes the spelling check box. The default is true.

resetSkippedWordsWhenBoxCloses = true
resetSkippedWordsWhenBoxCloses = false
smartQuotes
smartQuotes = <Boolean>

If true, this key instructs the text object to use "smart quotes," quotation marks that curl themselves appropriately to their positions when typed. If this key is false, standard typewriter-style quotation marks will be used. The default is false; this is settable by the user in the Preferences manager application.

smartQuotes = true
smartQuotes = false

The INI File: 2.31 Categories in the INI File: [ui]

autosave
autosave = <Boolean>

If true, this key tells GEOS to turn on the automatic backup feature; this may be set in the Preferences manager application.

autosave = true
autosave = false
autosaveTime
autosaveTime = <number>

This key indicates the number of seconds between autosave operations, if the autosave keyword is set to true. This may be set with the Preferences manager application.

autosaveTime = 300
background
background = <filename>

This key defines the file containing the picture to use as the background graphic. This is normally set by the Preferences manager application.

background = Bricks
backgroundattr
backgroundattr = <t, c, or x>

This key defines how the background picture should be displayed; it is normally set by the Preferences manager application.

t
Tile the picture.
c
Center the picture on the screen.
x
Place picture in upper-left corner of the screen.
backgroundattr = c
backgroundattr = t
backgroundcolor
backgroundcolor = <number>

This key defines the color of the background graphic. This is normally set by the Preferences manager application. The number is the color index of the color to be used.

backgroundcolor = 0
backgroundcolor = 12
deleteStateFilesAfterCrash
deleteStateFilesAfterCrash = <Boolean>

If true, this key tells GEOS to delete state files after every non-clean shutdown. If you set this, you will probably want to set the doNotDisplayResetBox key true as well.

deleteStateFilesAfterCrash = true
deleteStateFilesAfterCrash = false
doNotDisplayResetBox
doNotDisplayResetBox = <Boolean>

If true, this key tells GEOS not to display the system dialog box asking whether the user wants to reset the system or not after a crash. If you set this true, you should also set deleteStateFilesAfterCrash true, or some crashes may allow bad state files to keep GEOS from restarting properly.

doNotDisplayResetBox = true
doNotDisplayResetBox = false
execOnStartup
execOnStartup = <program list>

This key defines applications to be run when the UI is loaded (when GEOS starts up), named for their GEOS long names. The default is not to execute any additional programs.

execOnStartup = {				Lights Out Launcher
                CD Player Application }
generic
generic = <file name>

This key defines the generic UI library that is to be used by GEOS. You will not need to set this; it will default to ui.geo.

generic = ui.geo
generic = uiec.geo
hardIconsLibrary
hardIconsLibrary = <string>

This is the long name of the library which provides the hard icon UI for a PC-based demo of a PDA device.

haveEnvironmentApp
haveEnvironmentApp = <Boolean>

If true, this key indicates that GEOS is using an environment application such as Welcome. If an environment application is being used, that application must be specified in the defaultLauncher key in the uiFeatures category. During debugging, you may set this key false and set the execOnStartup key to the application you're debugging to have GEOS come up directly into your application.

haveEnvironmentApp = true
haveEnvironmentApp = false
hwr
hwr = <file name>

This key indicates the handwriting recognition library to be loaded, if any. If GEOS is not on a pen-based system ( penBased = true in the system category), then no handwriting recognition library will be loaded.

hwr = palm.geo
kbdAcceleratorMode
kbdAcceleratorMode = <Boolean>

If false , this key tells GEOS to ignore keyboard accelerators. By default, this is true and keyboard accelerators are allowed; this is independent of whether the accelerators are drawn or not. See also the uiFeatures category's windowOptions key.

kbdAcceleratorMode = true
kbdAcceleratorMode = false
noClipboard
noClipboard = <Boolean>

If true, this key prevents the UI from opening the clipboard file on startup. This is an optimization used when we want to open the clipboard only later on in a particular application.

noSpooler
noSpooler = <Boolean>

If true, this key prevents the UI from launching the spooler. This can be used to improve startup time if the system running GEOS knows beforehand that the spooler is not required. Very few systems will set this true.

noSpooler = true
noSpooler = false
noTaskSwitcher
noTaskSwitcher = <Boolean>

If true, this key prevents the UI from loading a task switch driver. This may be used to improve startup time if the system running GEOS knows in advance it will never use a task switcher.

noTaskSwitcher = true
noTaskSwitcher = false
noTokenDatabase
noTokenDatabase = <Boolean>

If true, this key prevents the token database from being initialized. This is useful as an optimization when GEOS will not need icons--that is, when GEOS starts up and runs just a single application.

noTokenDatabase = true
noTokenDatabase = false
overstrikeMode
overstrikeMode = <Boolean>

If false , this key prefents the user from switching into overstrike mode; it defaults to true, and it is settable in the Preferences manager application.

overstrikeMode = true
overstrikeMode = false
password
password = <Boolean>

This field turns the system password on or off.

passwordText
passwordText = <string>

Encrypted text of the password string, if any.

penInputDisplayType
penInputDisplayType = <number>

This key defines the PenInputDisplayType to be shown when the PenInputControl object is brought up. The PenInputControl object displays the floating keyboard in one of several display types. See the PenInputDisplayType enumerated type for definitions of its values.

penInputDisplayType = 1					; floating keyboard
penInputDisplayType = 7					; handwriting area
productName
productName = <name>

This key holds the string displayed in the GEOS shutdown dialog box; for example, it will put up a string similar to "Are you sure you want to exit <productName>?"

productName = GEOS
screenBlanker
screenBlanker = <Boolean>

If this field true, then the user wishes to save the screen after an idle time period specified by means of the screenBlankerTimeout field.

screenBlankerTimeout
screenBlankerTimeout = <number of minutes>

If the user has turned on screen blanking, this is the number of minutes the system will stand idle before screen-saving turns on.

showTitleScreen
showTitleScreen = <Boolean>

If true, this key instructs GEOS to put up a title screen. This defaults to false.

showTitleScreen = true
showTitleScreen = false
sound
sound = <Boolean>

If true, this key instructs GEOS to turn sound on. If it's false, sound will be off. This is settable by the Preferences manager application.

sound = true
sound = false
specific
specific = <file name>

This key defines specific UI libraries to be loaded by GEOS. It defaults to motif.geo.

specific = motif.geo
tinyScreen
tinyScreen = <Boolean>

If true, this key tells GEOS that it's running on a small-screened device such as the Zoomer; it defaults to false. You can use this key during development if you're working on applications for a small-screen platform; it affects certain characteristics of the UI.

tinyScreen = true
tinyScreen = false
unbuildControllers
unbuildControllers = <Boolean>

If true, the UI will destroy the child blocks of controllers when the controller's menu/dialog box is closed. The child block will have to be regenerated every time the menu/dialog is openned--this is a memory for time tradeoff.

xScreenSize
xScreenSize = <number>

This key tells GEOS the screen width, in GEOS coordinates. If this key isn't set explicitly, the kernel will set it to the default screen size. This key is used primarily when developing for small-screen platforms such as Zoomer.

xScreenSize = 256
yScreenSize
yScreenSize = <number>

This key tells GEOS the screen height, in GEOS coordinates. If this key isn't set explicitly, the kernel will set it to the default screen size. This key is used primarily when developing for small-screen platforms such as Zoomer.

yScreenSize = 344

The INI File: 2.32 Categories in the INI File: [<specific ui name>]

Each specific UI may have a category with options; this category should be named after the specific UI, e.g. [motif].

fontid
fontid = <font>

This field allows the user to specify a font that the specific UI should use when drawing text monikers for gadgets such as menus and buttons.

fontsize
fontsize = <size in points>

This field allows the user to specify a font size that the specific UI should use when drawing text monikers for gadgets such as menus and buttons.

The INI File: 2.33 Categories in the INI File: [ui features]

The ui features category defines the UI configuration used by the environment application (e.g. Welcome) and all applications on the execOnStartup list in the ui category. On systems with no environment application, this category defines the UI configuration for all applications.

Related categories, uiFeatures - intro , uiFeatures - beginner , and uiFeatures - advanced , support the same keys. Each of these categories defines the configuration for a specific "room" of the Welcome application. Other environment applications will also use these keys for different "rooms."

backupDir
backupDir = <relative path>

This key defines the directory in which the document control object will place quick-backup copies of document files. The default is PRIVDATA\BACKUP.

backupDir = DOCUMENT\BACKUP
defaultLauncher
defaultLauncher = <relative path>

This key defines the directory and application that acts as the default application launcher. This key should always have some application specified; otherwise, no application will start when GEOS loads. The path specified should be relative to the WORLD directory.

defaultLauncher = Utilities\GeoManager
docControlFSLevel
docControlFSLevel = <number>

This key specifies the document control's file selector user level. The file selector box has three different configurations; set the appropriate number (below) to determine which configuration is used.

0	No directories
1	Directories shown, simple UI configuration
2,3	Directories shown, complete UI configuration

An example of setting the file selector level is shown below.

docControlFSLevel = 2
docControlOptions
docControlOptions = <number>

This key turns on or off a number of other features in the document control object. The features are controlled by the bits set or clear in the number given. The five most significant bits of a 16-bit integer are used and have the following meanings, listed from most significant bit:

DCO_BYPASS_BIG_DIALOG (0x8000)
If set, this indicates that the big dialog box normally presented for New File/Open File/Use Template operations should be bypassed (not used). For advanced users, this bit should be clear. For novice users, this bit should be set.
DCO_TRANSPARENT_DOC (0x4000)
If set, this indicates that a "Switch Document" metaphor should be used in place of the New/Open/Close metaphor for document management. This will allow only a single document open at a time and will immediately prompt if no document is open. For introductory and novice users, this bit should be set.
DCO_HAVE_FILE_OPEN (0x2000)
If set, this indicates that there is an Open button in the File menu (subject to specific UI rules). This is typically not set.
DCO_FS_CANNOT_CHANGE (0x1000)
If set, this indicates that the file selector used by the document control object can not change configuration; that is, the file selector will not offer the option of switching between full and simple configurations.
DCO_NAVIGATE_ABOVE_DOC (0x0800)
If set, this indicates that the document control object's file selector will allow the user to navigate directories. If cleared, the user may not navigate above the default document directory.
expressOptions
expressOptions = <number>

This key defines the configuration of the express menu. It sets and clears features based on the least significant 11 bits of a 16-bit number. Each bit, from the most significant used (bit 10) down to the least significant, is detailed below.

UIEO_GEOS_TASKS_LIST
If set, this indicates that the express menu should contain a list of currently-running applications.
UIEO_DESK_ACCESSORY_LIST
If set, this indicates that the express menu should contain a list of applications in the World\Desk Accessories directory.
UIEO_MAIN_APPS_LIST
If set, this indicates that the express menu should contain a list of applications in the World directory.
UIEO_OTHER_APPS_LIST
If set, this indicates that the express menu should contain a hierarchical list of applications in subdirectories of the World directory.
UIEO_CONTROL_PANEL
If set, this indicates that the express menu should contain a control panel area.
UIEO_DOS_TASKS_LIST
If set, this indicates that the express menu should contain a list of available DOS tasks accessible by a task switcher.
UIEO_UTILITIES_PANEL
If set, this indicates that the express menu should contain a utilities panel area.
UIEO_EXIT_TO_DOS
If set, this indicates that the express menu should contain an "Exit to DOS" type of trigger.
UIEO_POSITION
This is a three-bit field indicating where the express menu should appear. Three different values are allowed:
    0	No express menu
    1	In the top of the Primary window
    2	In the lower left (just below the bottom
        of the screen)

Some examples of this key are shown below.

expressOptions = 617					; Introductory
    ;  617 = 0x0269 = 0000 0010 0110 1001
    ; The bits turned on are listed below:
    ; UIEO_DESK_ACCESSORY_LIST
    ; UIEO_CONTROL_PANEL
    ; UIEO_DOS_TASKS_LIST
    ; UIEO_EXIT_TO_DOS
    ; UIEO_POSITION = 1, upper left of window
expressOptions = 889					; Beginner
    ;  889 = 0x0379 = 0000 0011 0111 1001
expressOptions = 2041					; Advanced
    ; 2041 = 0x07F9 = 0000 0111 1111 1001
helpOptions
helpOptions = <number>

This key defines the configuration used by the help controller object. Specifically, it determines whether the help controller will automatically provide help triggers in the GenPrimary and in dialog boxes. The default is to allow help triggers to be created and displayed. Only the least significant bit of a 16-bit number is used, and that bit's significance is shown below.

UIHO_HIDE_HELP_BUTTONS
If set, this indicates that the help controller should not display help triggers in the GenPrimary or in dialog boxes.
helpOptions = 1				; hide help triggers
helpOptions = 0				; display help triggers
interfaceLevel
interfaceLevel = <number>

This key determines the interface level of applications that use the ui features category for their configurations. The four values allowed are shown in the examples below.

interfaceLevel = 0				; Introductory
interfaceLevel = 1				; Beginner
interfaceLevel = 2				; Intermediate
interfaceLevel = 3				; Advanced
interfaceOptions
interfaceOptions = <number>

This key determines two different features of the UI in general. It uses the two most significant bits of a 16-bit integer; the two bits have the following meanings.

UIIO_OPTIONS_MENU
If set, this indicates that an Options menu should exist.
UIIO_DISABLE_POPOUTS
If set, this indicates that the UI should not allow GIV_POPOUT GenInteraction objects to pop in an out.
interfaceOptions = 16384					; No Options menu
interfactOptions = 32768					; Popouts not allowed
launchLevel
launchLevel = <number>

This key controls the interface level of the applications allowed to be launched under the particular field ("room" of the environment application). It allows four values as shown in the examples below.

launchLevel = 0			; Introductory
launchLevel = 1			; Beginner
launchLevel = 2			; Intermediate
launchLevel = 3			; Advanced
launchModel
launchModel = <number>

This key controls how applications are started and exited. It allows four values, each of which defines a different level of user.

launchModel = 0			; Transparent (user does not
            ; realize he is starting an
            ; application
launchModel = 1			; Single instance only
launchModel = 2			; Multiple instances allowed
launchModel = 3			; Advanced features allowed
launchOptions
launchOptions = <number>

This key controls how applications are started and exited; specifically, it has a single flag which determines whether any applications are allowed to be in desk accessory mode. This defaults to true to allow desk accessories. The single flag is the most significant bit of a 16-bit integer.

UILO_DESK_ACCESSORIES
If set, this indicates that desk accessories should be allowed.
launchOptions = 32768				; allow desk accessories
launchOptions = 0				; do not allow them
quitOnClose
quitOnClose = <Boolean>

If true, this key forces the closure of all applications in a room before that room may be exited. This will cause state saving to be turned off. The default for this flag is false. One note: setting quitOnClose = true and launchModel = 0 can result in undesirable behavior.

quitOnClose = true
quitOnClose = false
windowOptions
windowOptions = <number>

This key controls different window system options. The precise interpretation of each flag is up to the specific UI. The high 8 bits form a mask of the bits to affect. The low 8 bits indicated whether the masked bits should be turned on or off. Of the eight bits, the high one is meaningless; the seven flags are listed below. You should not set these, however, unless you are familiar with the workings of the UI and the specific UI.

UIWO_MAXIMIZE_ON_STARTUP
If true, application primary windows will come up maximized. Desk accessory applications may override this behavior.
UIWO_COMBINE_HEADER_AND_MENU_IN_MAXIMIZED_WINDOWS
If true, the title bar and menu bar areas of maximized windows will be combined to save screen space. Only the window gadgetry and menus are retained; title strings are eliminated.
UIWO_PRIMARY_MIN_MAX_RESTORE_CONTROLS
If true, window gadgetry for maximizing, minimizing, and restoring the window will be included on the screen.
UIWO_WINDOW_MENU
If true, a Window menu for keyboard control of minimize, maximize, restore, move, resize, and close operations will be provided. If false, only a "close" button will appear in the menu's place.
UIWO_PINNABLE_MENUS
If true, menus will be pinnable.
UIWO_KBD_NAVIGATION
If true, keyboard accelerators and keyboard navigation will be enabled.
UIWO_POPOUT_MENU_BAR
If true, menu bars will be allowed to pop out to be dialog boxes. This should be used in limited situations because specific UIs may not provide gadgetry to restore the menu bar if the dialog is closed.

The INI File: 2.34 Categories in the INI File: [welcome]

The welcome category defines configuration and usage characteristics of the Welcome environment application. Its keys may be useful to you during development, though you will probably not need them for your applications.

startupRoom
startupRoom = <name of room>

This key defines the room in which Welcome will start when GEOS is run. This is settable in the Preferences manager application; you will probably want to set this to the room most appropriate for your application to speed startup when debugging. The default is no setting, which will cause Welcome to present its title screen.

startupRoom = 1				; Beginner
startupRoom = 2				; Intermediate
startupRoom = 3				; Advanced

This document is a single-page version of a a multi-page document, suitable for easy printing.


Resource Editor

The Resource Editor is a tool created by Geoworks to translate UI elements of an existing GEOS program into another language.

We've made ResEdit usable by non-programmers. Ideally, the only skills one should need to localize an application to a foreign language is knowledge of the source and target languages--the translator shouldn't have to work with the source code. You may wish to give just this piece of documentation to your translator instead of burdening them with a full documentation set.

1 Glossary
2 Getting Started
3 What Needs to be Translated?
4 Creating A Translation File
5 Translating Text
5.1 Translating Via an ASCII File
5.2 Translating With ResEdit
6 Creating a Translated Geode
7 Updating an Executable
8 Testing the Translated Geode
9 Resource Editor Menus
9.1 File Menu
9.2 Edit Menu
9.3 Project Menu
9.4 Filter Menu
9.5 Utilities Menu
9.6 Window Menu

Resource Editor: 1 Glossary

In explaining how to use the Resource Editor, we use some vocabulary to describe certain operations and program constructs.

Geode, Executable
A "geode" or "executable" is a GEOS program: a GEOS application, library, or driver.
Source Executable
The original-language program.
Destination Executable
The local-language executable, which is created by translating the source executable.
Resource
Geodes are divided into memory blocks called resources. Resources may contain code that will be executed, data that will be referred to, or text and bitmaps which will be displayed to users. The Resource Editor will only allow you to edit those resources which contain strings or bitmaps. Each resource has a unique number or extended name.
Chunk
Resources are, in turn, divided into chunks. Inside a resource, there are many different kind of chunks, each containing a unique piece of information, such as a text string, a bitmap, or an object structure. Each chunk has a unique number or name. Your translation will consist of new contents for these chunks.
Driver
A driver is a special sort of executable that performs a very specific function that may not be needed by every user. Many drivers are responsible for interactions with particular kinds of hardware peripherals. In GEOS, there are DOS, video, mouse, communications, and printer drivers, along with a few other types. Most drivers are designed to work with more than one device. For example, the EPSON9.GEO printer driver actually supports more than 50 printers--if you were to localize the driver, you might need to translate the names of those printers.
Localization File
When a geode is created, an extra file to be used by the Resource Editor is created at the same time. This file (which is named like the .geo file but with a .vm extension) contains localization information about the resources and chunks which you will be editing. You cannot localize a geode without this file.
A programmer may create a localization file much as they would compile a geode. Using the GEOS SDK, issue the command "pmake full" to compile a Localization file in addition to a geode.
Translation File
The file that the Resource Editor creates is called a Translation File. The information stored in this file will be merged with the source executable to create the destination executable. Internally, the file holds a copy of the original-language text or bitmap, along with your translation and may contain some translation instructions.
Moniker
Text or graphics which serves as the visual name for objects such as menu items, button labels, and dialog box options. E.g., the File menu's moniker is "File"; an OK button would have moniker "OK".
Text String
Text in the geode which is not a moniker.
Mnemonic
A key that, when pressed with the Alt key, will activate a specific UI gadget which has this letter or symbol underlined in its moniker.
GString
Graphics string. This data structure is used for describing many types of graphics. Most application icons and document icons are GString Monikers.
Bitmap
Bitmap image.
Keyboard Shortcut
A combination of keystrokes than when pressed will cause an object to perform a specific action. Keyboard shortcuts are stored in the chunk containing the object to which they are connected.
Updating
Unfortunately, software often changes. As you make changes to the original-language software, it might become necessary for you to take a Translation File you created with an older version of a particular geode and update it with the newer version. The newest software may contain new strings, may have modified existing strings, or may have eliminated some strings. The Resource Editor is however able to match these two executables and show the newest changes. The process of updating a file will be discussed in more detail later in these instructions.

Resource Editor: 2 Getting Started

Make sure that there are two GEOS installations on your computer: one to hold the original-language version, and one for the translated version. These are often referred to as the source directory and destination directory, respectively.

You will need original-language versions of the geodes you will translate. These should reside in a directory under the source directory. Most applications will be in the WORLD directory. The localization file for each geode you'll translate should be in the same directory as its associated geode.

You'll use ResEdit to generate a foreign-language version of the geode. ResEdit will place the translated geode in the destination directory, at the same relative position as the original language version.

For example, you might have source directory D:\GEOS20.NC and destination directory D:\LATIN. Your original-language application, Connect 4, might reside in D:\GEOS20.NC\WORLD\GAMES. You'd need to copy CONNECT_.VM to D:\GEOS20.NC\WORLD\GAMES. When finished with translating, the resulting translated geode, perhaps named Connect IV, would appear in D:\LATIN\WORLD\GAMES.

To specify the locations of the Source directory or the Destination directory, choose the appropriate item from the Project menu. The Destination directory should not be the same directory as the Source directory.

Resource Editor: 3 What Needs to be Translated?

To create a localized version of your applications, you will need to translate only the applications, libraries, and drivers you have created. All Geoworks software will be translated by Geoworks.

Most applications will use GEOS system libraries whose UI is visible from the application. For example, an application which includes spell checking will have a dialog box containing all the information about the spell checking operation which is included from the Spell library. If the Spell library is not translated, that UI will always appear in its original form. Your application will work with the Spell library, whether or not it has been translated.

At this point you are ready to translate geodes into the target language. The following set of steps can be carried out by a non-programmer running the ResEdit tool. Of course, the translator should have a good idea of the geode's function and use.

Resource Editor: 4 Creating A Translation File

To begin creating new Translation Files, double click on the "Res Edit" icon in the Tools folder of the WORLD directory of the original-language version of Ensemble 2.0.

This brings up a dialog box which gives you three choices:

  • Create New Translation File
  • Open a Translation File
  • Import an ASCII Translation File

Choose the "Create New" option. Another dialog box appears, allowing you to select the localization (.vm) file for the geode you wish to edit.

The first time you start the Resource Editor, it assumes you will be editing geodes in the GEOS20.NC directory, or whatever installation of GEOS you started in. Once you have a Translation File open, you can change the top-level directory by clicking on the "Set source directory" in the Project Menu. This setting is saved in your .ini file, so the next time you start ResEdit, it will automatically set the top-level source directory to what it was when exited.

To create a new Translation File for a geode that is not in the GEOS20 installation (or whichever installation is currently set as the top-level source directory), change the top-level source directory first--because you cannot access the Project menu with no Translation File open, you may need to create a new Translation File and keep it around until you can access the Project menu to change the Source directory.

Resource Editor: 5 Translating Text

There are two main approaches to translating a geode's text. Some people prefer to work with an ASCII text file. Others prefer to use ResEdit's graphical interface.

We will discuss both methods.

Translating Via an ASCII File

Translating With ResEdit

Resource Editor: 5.1 Translating Text: Translating Via an ASCII File

Choose Other from the File menu; in the submenu that appears, choose Export Document. In the dialog that appears, choose a file location and name for the ASCII Translation File (ATF).

If you have already gone through the translation process once, and only wish the ASCII Translation File to contain entries for those chunks which have been translated, select the Export only changed chunks box.

Press the OK button.

You may now edit the ASCII file to translate strings.

The important lines of the file are of the form:

[keyword   ] data

There should always be one space between the keyword and the data. The following keywords appear in the header:

[ source ]
This is the long name of the geode.
[ path ]
This is the geode's path, relative to the Source directory. Applications will typically be in the WORLD path.
[ version ]
The geode's version number.
[ DOS name ]
The geode's DOS filename.

The ASCII Translation File then lists all resources in the geode, by means of [resource ] lines and [endrsrc ] lines. Do not change the text of these lines. If, examining a resource, you realize it contains no chunk entries you wish to change, you may delete it to make it easier to find the resources you do wish to change. To do so, delete the [resource ] line, the [endrsrc ] line, and all lines in between.

Within each resource, there may be one or more chunks containing localizable strings. For each such chunk, there will be five lines:

[chunk     ] chunkname
[original  ] {original text}
[localize  ] {instructions} max, min, type
[translate ] {translated text}
[mnemonic  ] mnemonic [shortcut  ] mods,shortcut

You will leave most of these fields alone, translating only the translated text , mnemonic , mods , and shortcut fields.

chunkname
The chunkname is a unique identifier for the chunk. Do not change this.
original text
The original text in the chunk. Do not change this. The text is delimited by curly braces ("{" and "}"). If any curly braces occur in the string, they will be represented by two curly braces.
instructions
Instructions to the person translating. Do not change this. These instructions are taken from the original-language geode. The geode's programmers specified the instructions by means of the @localize keyword in the source code files.
max
Maximum number of characters you may use in the translated text. Do not change this. The geode's programmers specified this number by means of the @ localize keyword.
min
Minimum number of characters you may use in the translated text. Do not change this. The geode's programmers specified this number by means of the @localize keyword.
type
The text's purpose in the program. Do not change this. A Text Moniker is the visible name of a gadget. A piece of Text appears in the program, but not as the name of a gadget.
translated text
This is how the text will appear in the translated geode. You'll probably want to change this text. The text is delimited by curly braces. If you want the translated string to contain curly braces, you should double them--use "{{" and "}}". E.g., to have translated text "moustache: :-{)", the line would read
    [translate ] {moustache: :-{{)}
If the original text looks like a shortcut key (e.g., "{(Physical Ctrl u)}", then to translate the keyboard shortcut, work with mods and shortcut (described below) and leave this field alone.
mnemonic
This is the character the user will use when navigating the translated geode's UI with keyboard navigation. Only Text Monikers may have mnemonics. The mnemonic should be either a character which occurs in the moniker, the keyword ESC or the keyword NIL (which signals that there is no mnemonic).
You must be careful not to assign the same mnemonic to two monikers whose gadgets are both in the active window at the same time. If there is such a conflict, only one of the two gadgets will be activated by this mnemonic. When you have created the translated executable, you should check that there are no conflicting mnemonics, and change any which overlap with others at the same level.
mods , shortcut
Together, mods and shortcut form a keyboard shortcut which the user may use to activate a UI gadget in the translated geode. For instance, Ctrl-S typically activates the Save item of the File menu. This shortcut would be represented by
    [shortcut] PC,s

If, for some reason, the question mark were to be a shortcut, it would be represented by

    [shortcut	] ,?

Only monikers may have shortcuts. The lack of shortcut is represented by

    [shortcut] ,

Be careful when representing the lack of a shortcut: the carriage return or line feed at the end of the line must occur immediately following the comma. If there is an intervening space, ResEdit will think you want to use the space bar as a shortcut key.

The following mods are available: A (Alt), S (Shift), C (Ctrl), and P ("Physical"--Num Lock and Caps Lock state is disregarded). These modifiers may be combined as in the Ctrl-S example.
The shortcut should be a single character. If this character is a letter, pay attention to its case--it should probably be a lower-case letter.

If you will not need to translate a chunk, you may delete its entry to make it easier to find the chunks you do wish to translate. Delete the [chunk ] line and the [shortcut ] line and all lines in between.

To convert your ASCII Translation File into a regular Translation File, use ResEdit to import the file: either press the Import button in the dialog that appears when ResEdit starts up, or choose New/Open from the File menu, and then choose Import in the dialog that appears. In the dialog that appears, show ResEdit the location of both the ASCII Translation File and the Localization (.vm) File.

Press the OK button.

If your original-language geode has changed since the ASCII Translation File was created, ResEdit may present a warning dialog box at this point. Assuming the geode hasn't changed too much, it's probably safe to use the ASCII Translation File anyway. If you're hesitant, you can abort the import. You might then want to export another ASCII Translation File from the newer version of the geode.

Resource Editor: 5.2 Translating Text: Translating With ResEdit

After selecting a localization (.vm) file to translate, the Resource Editor will then load the information from this file and the corresponding geode into an untitled Translation File saved into the DOCUMENT directory. The file will be displayed in two views. The left portion of the screen is the Source File, while the right portion is the Translation File. When the file is first opened, the two sides look the same, since no translations have yet been made.

The Resource Editor brings up all the strings or bitmaps which need to be translated, regardless of their context. The only way you will be able to be completely sure that the translation is accurate is to perform the translation and then actually run the translated executable (to be discussed later) and see how the translations appear in the program.

Translating a Text String

Click on any text string in the right portion of the screen. The blinking text editing cursor should appear, and you will be able to edit the text in the right screen to reflect the changes necessary for the local language. The text in the left portion of the screen acts as a guide and will not change.

At the bottom of the main screen, there are three fields which may have important information about how you should translate the selected chunk.

  • The Minimum and Maximum Length fields show the size limits on the number of characters that the selected chunk may have.
  • The Instructions field shows special characteristics of the selected chunk, or may give a context where the specific chunk appears.

If there is nothing in these fields, there are no constraints on the size or content of the translation.

Changing a Mnemonic

This is the character the user will use when navigating the translated geode's UI with keyboard navigation. Only Text Monikers may have mnemonics. The mnemonic should be either a character which occurs in the moniker, the keyword ESC or the keyword NIL (which signals that there is no mnemonic).

You must be careful not to assign the same mnemonic to two monikers whose gadgets are both in the active window at the same time. If there is such a conflict, only one of the two gadgets will be activated by this mnemonic. When you have created the translated executable, you should check that there are no conflicting mnemonics, and change any which are found to overlap with others at the same level.

To choose a different mnemonic, use the Mnemonic gadget in the upper-right area of the ResEdit window. Use the up- or down- buttons to choose a letter from the text moniker, or type in your letter of choice. If you choose a letter which does not occur in the text moniker, it will appear in parentheses.

Changing a Keyboard Shortcut

Keyboard shortcuts, also known as keyboard accelerators, are key combinations which may be used to activate a piece of UI gadgetry. For instance, Ctrl-S is a common keyboard shortcut for activating the Save item under the File menu.

In ResEdit, keyboard shortcuts are displayed when there is a chunk of type Object that has a shortcut. The shortcut will appear as something like "(Physical Ctrl p)".

To translate the shortcut, press the Shortcut... button at the top center of the ResEdit window. This will bring up a dialog box allowing you to specify that there should be no shortcut, or to specify a keyboard combination for a translated shortcut.

The following modifier keys are available: Alt, Shift, Ctrl. The Physical option specifies that Num Lock and Caps Lock states are disregarded when matching the shortcut character.

Translating a Graphic

You may wish to translate a graphic. For example, a calendar application written for an English-speaking audience might have an icon depicting a desk calendar displaying the date "Apr 7"--but in a foreign language, "Apr" might not be anything a customer would see on a calendar.

To translate a graphic:

  1. Copy the graphic to be translated--select the displayed graphic in ResEdit, and choose Copy from the Edit menu.
  2. Start up the GeoDraw graphics editor program, creating a new GeoDraw document if necessary.
  3. Paste the graphic into the GeoDraw document--choose Paste from GeoDraw's Edit menu.
  4. Edit the bitmap, being careful not to change the dimensions of the bitmap.
  5. When you are done editing the bitmap, select the bitmap with the arrow tool, and choose Copy from GeoDraw's Edit menu.
  6. Return to the ResEdit application, select the graphic to translate, and choose Paste from ResEdit's Paste menu.

Choosing a Chunk

ResEdit will display the chunks of one resource at a time. To translate all the chunks in a geode, you'll need to work with chunks from several resources.

There are several ways to choose a different resource.

  • Type Ctrl-> (Ctrl-greater) to move to the next resource or Ctrl-< (Ctrl-less) to move to the previous resource.
  • Click on Next Resource/Previous Resource in the Utilities menu.
  • Click on the Resource pop-up list in the upper left corner of the screen and choose any resource.

Within a resource, there are several ways to select a chunk to edit.

  • Use the mouse to click on any chunk.
  • Type Ctrl-.(Ctrl-period) to move to the next chunk or Ctrl-, (Ctrl-comma) to move to the previous chunk.
  • Click on Next Chunk or Previous Chunk in the Utilities menu.
  • Click on the Chunk pop-up list in the upper left corner of the screen and choose any chunk.

Resource Editor: 6 Creating a Translated Geode

After all of the chunks and resources in the selected .vm file have been translated and saved in a Translation File, a new local-language executable can be created in the target installation.

The first step to placing the new executable in the correct place is making sure that the localization target kit has been placed in another directory on your hard drive, for example C:\ESPANOL.

Then select the Destination directory option from the Project menu. It will ask you to choose the top level GEOS directory that will hold the modified executables. In this case, you would choose the C: drive from the Drives list, and choose \ESPANOL in the scrolling list of directories.

You need not have a full GEOS installation in the target directory. However, when new executables are created, they are placed into the same subdirectory as the original geode. For example, if you were to translate GeoWrite, which is in the WORLD directory, and you had the destination directory set to ESPANOL, the directory ESPANOL\WORLD must exist when you try to create the new executable. If it doesn't, then you will get an error message saying that the target could not be created.

To create the translated geode, choose Create New Executable... from the File menu.

This new geode will have the identical original-language long name as the original geode (unless you have changed the name in the File Menu's New Name and User Notes dialog). You may wish to rename several of the geodes, especially those in the WORLD directory and the Screen Savers, to reflect local-language names.If you change the name, the new geode will not overwrite the original one, and you will have to go back and delete the old one to avoid duplicates.

Resource Editor: 7 Updating an Executable

Even after all the translations are complete, there may be a need to replace a certain executable with a newer version containing a bug fix or a feature enhancement. You will be able to do this quickly and effectively by using the Update feature in the File menu.

To make this feature work, you must first put the updated geode and .vm file in the proper source directory, overwriting the original files. Then, by clicking Update Translation File (in the Update submenu of the File menu), the existing Translation File will be compared with the new strings in the geode, showing the new or changed chunks, as well as the ones left unchanged. The deleted chunks will be grouped in a separate resource named "Deleted chunks" at the end of the resource list. You will then be able to make the necessary modifications to this file to bring it up to date with the newest version of the geode.

If the name of the original-language geode has changed, you will need to use the Update Geode Name function (in the Update submenu of the File menu) to tell ResEdit about the new name.

After updating the Translation File, you can then create an updated local-language executable as out lined above.

If there have been no changes to the geode that affect editable chunks (as when code changes for a bug fix), it is also possible to simply rebuild the new geode from the existing Translation File by choosing Create A New Executable from the File menu.

After viewing the updated Translation File and making any necessary changes, you can remove the updating information from the Translation File by choosing Commit the Update trigger from the File/Update menu. This will remove information which marks the chunks as "new" or "changed" as well as remove the Deleted Chunks resource.

Resource Editor: 8 Testing the Translated Geode

You may wish to be able to test your translations as soon as you make them. At any point during a translation, you can create an executable, as described in Creating a Translated Geode .

Change directory to your Destination directory (e.g., \ESPANOL) start that installation, and test your new geode.

If your product will run under several video drivers (e.g., if it runs on desktop PCs), we suggest that you take a look at the localized UI under the CGA video driver to make sure that everything fits. You should also observe it under the EGA video mode with a large UI font size.

Resource Editor: 9 Resource Editor Menus

The quick overview of the menu items below should give you the necessary information to use all of the features.

File Menu

Edit Menu

Project Menu

Filter Menu

Utilities Menu

Window Menu

Resource Editor: 9.1 Resource Editor Menus: File Menu

New/Open :
Brings up the New/Open dialog box, allowing you to create a new Translation File, open an existing one, or import an ASCII Translation File.
Close :
Closes the active Translation File.
Save :
Saves the active Translation File.
Save As :
Saves the active Translation File under a different name.
Backup :
Makes a backup of the active Translation File or restores from a backup file.
Other :
Copy To :
Creates a copy of the Translation File and places it in the directory of your choice.
Move To :
Moves the Translation File to the directory of your choice.
Export Document :
Creates an ASCII Translation File. See Translating Via an ASCII File for details.
Discard Changes :
Discards all changes since last saved version of the Translation File.
Rename :
Gives the Translation File a new name.
Edit Document Notes :
Edits the notes for the Translation File.
Set Document Password :
Creates a document password.
Set Document Type :
Allows the user to choose between a normal, read-only, or public document.
New Name and User Notes :
Allows user to change geode name and make annotations to the file for later reference.
Create a New Executable :
Takes the Translation File, merges it with the source geode, creates a new translated geode, and puts the translated geode in the Destination directory.
Update :
Update Geode Name :
Use this to warn ResEdit that the name of the original-language geode has changed. (Otherwise, ResEdit will not be able to find the original-language geode.)
Update Translation File :
Using a new or updated geode, matches chunks in the old file with those in the new file, creating an updated Translation File which shows new or changed chunks and groups deleted chunks at the end.
Commit the Update :
Deletes intermediate matching information, deleting those chunks which have been marked for deletion, showing only the newest version of the Translation File.
Print :
Allows you to print out a report of the Translation File to paper. You must have configured your printer if this is to work.
Exit :
Exits the Resource Editor.

Resource Editor: 9.2 Resource Editor Menus: Edit Menu

Cut :
Takes highlighted information and moves it to the clipboard.
Copy :
Takes highlighted information and copies it to the clipboard, leaving a copy in place.
Paste :
Pastes information from the clipboard to the location of the cursor.
Undo :
Reverts to previous saved version of individual chunk.
Find and Replace :
Allows user to search for designated words throughout the Translation File and replace text in the Translation File (as displayed in the right view) with local language translation. You can limit a forward or backward search by selecting filters for certain types of chunks from the Filters menu.

Resource Editor: 9.3 Resource Editor Menus: Project Menu

The items in this menu keep track of how your project is organized.

Source directory :
Set the top level directory holding source geodes.
Destination directory :
Set the top level directory holding modified geodes.
Reset Source Geode Path:
Reset the path of the source geode if it moves to a different subdirectory.

Resource Editor: 9.4 Resource Editor Menus: Filter Menu

Each of the "Don't show ..." menu items in the Filter menu is a radio button which allows you to turn on or off a filter. By turning on a filter, you ask that ResEdit not show chunks of a certain type. These options can be helpful when editing updated Translation Files, making it easier to find those items you want to work with.

Don't show Text:
If this filter is on, ResEdit won't display any text monikers or other text chunks.
Don't show Monikers:
If this filter is on, ResEdit won't display any monikers, whether graphic or text.
Don't show GStrings:
If this filter is on, ResEdit won't display any graphic strings. This will filter out graphic monikers.
Don't show Bitmap s:
If this filter is on, ResEdit won't display any bitmap graphics. This will filter out most graphic monikers, since most of these are simple graphics strings displaying bitmaps.
Don't show Objects
ResEdit will normally display the keyboard shortcuts associated with an object so that you may change the shortcut. If this filter is on, then ResEdit won't try to display the objects, and thus no keyboard shortcuts will appear.

Each of the "Show ..." menu items is a radio button which allows you to view specific chunks affected by updating the Translation File. At most one of these may be selected at a time. To show all chunks, make sure that none of these menu items are selected--choose the selected item to un-select it.

Show changed chunks
Show new chunks
Show deleted chunks

Resource Editor: 9.5 Resource Editor Menus: Utilities Menu

The first four of these menu items allow for quick movement between chunks and resources:

Next Chunk
Previous Chunk
Next Resource
Previous Resource

The last item provides a count of words in the original-language geode. It provides a count both of the total number of words and of the number of unique words (i.e., not counting repeated words):

Word Count

Resource Editor: 9.6 Resource Editor Menus: Window Menu

These menu items make manipulation of multiple Translation Files easy, by opening overlapping windows or tiling active windows.

Overlapping :
Choose this item to allow Translation File windows to overlap. This is probably only useful if you are working with more than one such file at a time.
Full-Sized :
Force each Translation File to take up all of Resource Editor's document display area.
Tile :
If you're working with multiple Translation Files, this will lay them out side by side.
This document is a single-page version of a a multi-page document, suitable for easy printing.


Swat Introduction

Most programmers are familiar with the process of debugging code. Many, however, will not be familiar with the issues of debugging programs in an entirely object-oriented, multithreaded system in which memory is often sharable by multiple programs. Because this type of system presents not only incredible power but also a new class of potential bugs, the GEOS tool kit provides a new class of symbolic debugger.

Swat is more than just a debugging program; it is an extensible debugging environment which you can tailor to your needs. Because all Swat commands are programmed in a special language called Tool Command Language (Tcl), you can write your own commands or extend existing commands. Tcl was originally developed by Professor John Ousterhout at the University of California in Berkeley, California. Swat itself was developed during a project headed by professor Ousterhout for a distributed multiprocessing operating system; Geoworks gained permission from the university to use and modify Swat. Since then, we have tailored it for use with GEOS; thus, it is the most appropriate debugger for any GEOS programmer.

Swat is essentially a system debugger, as opposed to an application debugger. This is an important distinction, due to the multithreaded nature of individual GEOS applications--each application may have more than one thread, and a system debugger greatly eases the debugging process for such applications. Swat also has many other features that make it preferable to other debuggers:

  • Ideal for multithreaded environment
    Because Swat was initially designed for debugging multithreaded system software, it is ideal for use on multithreaded applications in the GEOS environment.
  • Extremely flexible
    Nearly every part of Swat you will use is written in Tcl. Swat allows you to create your own commands or extend existing commands by using the Tcl language; you can examine, print, and modify just about any data structure using various Tcl commands. For large applications and projects, the customization this offers can be a tremendous asset.
  • Extensive access to data structures
    You can examine any byte in the test PC's memory while Swat is attached. You can examine any basic or complex data structure. For example, the "pobject" command ("print object") prints the entire instance data of the specified object. You can specify memory locations with symbols, registers, addresses, pointers, or a number of other ways.
  • Easy modification of code and data
    Using Tcl commands, you can change the contents of a register or memory location. You can also create patches to be executed at certain points in your program; this speeds up debugging by reducing the fix-compile-download-attach-debug cycle to a simple fix-debug cycle.
  • Interactive single-step facility
    By using Swat's single-step command, you can execute a single instruction at a time. Swat shows you all the inputs going in to the instruction so you can more easily keep track of what's going on.
  • Facilities for watching messages and objects
    Swat has several "watch" facilities. For example, you can let GEOS continue executing while watching a particular object; Swat will display all messages sent to the object, along with the data passed. You can also watch a particular message; Swat will display the destination and passed data each time the message is sent.
  • On-line help system
    Swat commands are documented on-line. The help facility is available from the basic Swat command prompt, and you can use it either as an interactive help shell or to get help on one particular topic.

Swat also offers many other features that both novices and experts will use. As you gain familiarity with Swat while using Swat with the sample applications and with your own programs, you will discover your own preferred methods of debugging.

1 DOS Command Line Options
2 Notation
3 Address Expressions
4 On-line Help
5 Essential Commands
5.1 Cycle of Development
5.2 Attaching and Detaching
5.3 Breakpoints and Code Stepping
5.4 Examining and Modifying Memory
5.5 Other Important Commands
6 Additional Features

Swat Introduction: 1 DOS Command Line Options

To use Swat, you must have the pccom tool running on the target machine. You may then invoke Swat on the development machine. Swat takes the following command-line flags:

-e
Start up the non-error-checking version of the loader on the target machine.
-C
Disable symbol file path caching.
-h
Displays a usage line.
-k
Use a non-standard kernel file.
-n
Start up the non-error-checking version of GEOS on the target machine.
-r
Start up the error-checking version of GEOS on the target machine.
-s
Start up the error-checking loader on the target machine.
-D
You will only need to use this flag if debugging Swat.

If you have set up your communications incorrectly, Swat will have problems. (Often these problems don't show up when sending or receiving files; Swat demands more from the communications programs than pcsend and pcget.)

One common problem arises when other devices are generating interrupts which are not being successfully masked out. If, for instance, you have a network card which is operating on IRQ 2, you must make sure that either the pccom tool or else Swat is called with the /i:a option. Swat will intercept IRQ level 5 by default. To determine what number to pass with the /i, take the IRQ level, add 8, and express the result in hexadecimal.

If Swat complains that it is "timing out" on some operation, you may have your communication speed set too high. Try changing the baud rate field in PTTY to a lower value.

Normally, Swat insists that any geodes it examines should have their symbolic information in the appropriate subdirectory of your root GEOSSDK development directory--the possible subdirectories are Appl, Library, Driver, and Loader. To ask Swat to look in different places for these sorts of files, change the SWAT.CFG file in your PCGEOS\BIN directory. You may also specify a fifth directory in which to look for geodes. You may specify absolute pathnames in this file; if you give relative paths, they will be assumed to start at the directory specified with your ROOT_DIR variable.

If you add a "default" line to your SWAT.CFG file, that tells Swat where to look for .SYM files as a last resort. To save hard disk space, some developers delete rarely-accessed .SYM files from their hard drive, leave their SDK CD in the CD-ROM drive, and specify the GEOSSDK directory as the default place to look for .SYM files:

default D:\GEOSSDK

Swat Introduction: 2 Notation

The rest of this chapter is devoted to interacting with Swat once you have it running. Most of this is done by means of commands typed at a prompt. Some Swat commands may have subcommands, some may have flag options, and some combine the two. Others may have special options; all, however, are documented with the following conventions.

  • command (alternative1|alternative2|...|alternativeN)
     The parentheses enclose a set of alternatives separated by vertical lines (in C, the bitwise OR operator character). For example, the expression quit (cont|leave) means that either quit cont or quit leave can be used.
  • command <type_of_argument>
     The angled brackets enclose the type of an argument rather than the actual string to be typed. For example, <addr> indicates an address expression and <argument> indicates some sort of argument.
  • command [optional_argument]
     The brackets enclose optional arguments to the command. For example, the command alias [<word>[<command>]] could have zero, one, or two arguments because the command and word arguments are optional. Another example would be the command objwalk [<addr>] , which may take zero arguments if it is meant to use the default address or one argument if the user gives it a particular address to look at.
  • * +
     An asterisk following any of the previous constructs indicates zero or more repetitions of the construct may be typed. A plus sign indicates one or more repetitions of the construct may be used. For example, unalias <word>* can be the unalias command by itself, or it can be followed by a list of words to be unaliased.

Swat Introduction: 3 Address Expressions

Address expressions are used as arguments to any Swat command that accesses memory. For example, the pobject command takes an address expression as an argument and prints out information about the object at that address. An address expression can be a symbol name, which is just the name of a pointer, or a symbol path . A symbol path has the following structure, where scope is the name of a patient, module, function, structure type, union type, enumerated type, or assembly record type:

(<scope>::)+<symbol>

Some of the more common symbol path formats are:

<module>::<symbol>
<patient>::<symbol>
<patient>::<module>::<symbol>
<structure name>::<field name>

The symbol path is used when there is more than one symbol of a given name or if a symbol of a different application is needed. A symbol can be represented in a variety of ways: the name of an object, a field of a structure, a register/number combination, a number from the address history, an element of an array, nested Tcl commands, or a Tcl variable. Array indexing is used as follows:

<addr> [<n>]

which will return the zero-based element n from the given addr , even if addr is not an array.

Another important way of representing the symbol is as a segment:offset pair. In this, the segment is a constant, a register, a module, or a handle ID given as ^h<id> where id is a constant or register.

There are several operators which are used to make memory examination and manipulation easier in Swat. These operators are shown below (in order of highest precedence to lowest):

  • ^h
     The carat-h is used to dereference a memory handle when representing an address as a handle:offset pair (this is also known as a "heap pointer" representation) or when accessing a particular block of memory. It is often used in the situation when a memory handle is in one register (such as BX) and the offset is in another register (such as SI). This is similar to the ^l operator (below), but it requires an offset into the block rather than a chunk handle. The ^h operator is used thus:
    • [hello3:0] 6 => print ^hdx:ALB_appRef
  • .
     The period is used to access a field in a structure. For example, to get the top bound part of a rectangle structure stored as a global variable:
    • [hello3:0] 8 => print theRect.R_top
  • + -
     The addition and subtraction operators are used to add and subtract symbols to and from other symbols and constants. If two symbols in the same segment are subtracted, a constant will be the result.
  • ^l
     The carat-l is used to dereference an optr, a pointer in the form handle:chunk-handle (this is also known as a "local memory pointer"). This is similar to the ^h operator, but ^l requires a chunk handle rather than an offset. If an optr is stored in CX:DX, for example, the ^l operator could be used to dereference it as follows:
    • [hello3:0] 11 => pobj ^lCX:DX
[hello3:0] 12 => pobj ^l0x43d0:0x022
  • :
     The colon is the segment/offset operator, used to separate the segment and offset in a segment:offset pair.
    • [hello3:0] 13 => pobj ^hCX:DX
[hello3:0] 14 => pobj 0x43d0:0x022
[hello3:0] 15 => pobj INTERFACE:HelloView
  • *
     The asterisk is a pointer-dereferencing operator, as in the C programming language:
    • [hello3:0] 16 => print SubliminalTone
@5: SubliminalTone = 7246h
    [hello3:0] 17 => print *(&SubliminalTone)
    @6: *(&SubliminalTone) = 7246h
  • ^v
     The carat-v is the virtual memory operator, used to get to the base of a block that is in a Virtual Memory file given the file handle and VM block handle. The correct usage of the ^v operator is:
    • ^v<file>:<VM_block>

Much of the time the type of data stored at the address given by the address expression is implicit in the expression. Sometimes in ambiguous situations (using code as data), however, the type of data must be explicitly stated in the address expression. This is done by indicating the type of the data followed by a space and then a normal address expression. For example, in the expression

dword ds:14h

the data at ds:14h will be treated as a double word.

Swat Introduction: 4 On-line Help

Swat provides on-line help, both for looking up Swat topics and GEOS reference material.

To get help on a specific Swat command, you simply type the following, where the command is the argument.

[hello3:0] 7 => help <cmd>

To use Swat's interactive, menu-based help system, simply type the following:

[hello3:0] 8 => help

The menu-based system provides a tree of help topics which are organized into subjects. If you are looking for Swat commands having to do with a subject not covered in the help tree, you might try using the apropos command.

To get information about a GEOS topic, use the doc functions.

apropos
apropos [<string>]

The apropos command searches the list of commands and command help for all entries containing <string>. It lists each command and its synopsis. The string may actually be a partial word.

help
help [<command>]

There are two different ways to use the help command. The first is to enter the interactive help mode using the help command with no arguments, and the second is to use the help command with a particular command as an argument.

Interactive help mode
The interactive help mode consists of a tree of commands and topics identified by different numbers. If one of the numbers is typed, information about that particular topic or command is displayed. Some of the topics have their own subtrees (indicated by the ellipses following the topic heading) which follow the same numbering format. The interactive help mode is used when looking for a certain style of command but the name of the command is not known (see The Help Tree ).

The Help Tree

(geos:0) 198 => help

top-most level of the help tree:

 0 FINISH 			6 memory... 			12 step...
 1 advanced... 			7 object... 			13 support...
 2 breakpoint... 			8 print... 			14 swat_navigation...
 3 crash... 			9 running... 			15 window...
 4 file... 			10 source...
 5 heap... 			11 stack...

Type "help" for help, "menu" to redisplay the menu, "0" to exit.
Type a topic (or its number) to display it.
help:top>
help < command >
When help is typed with another command as an argument, information about that command is displayed (the same information as in the interactive help mode). This command is frequently used in order to get fast help on a particular command. (See The help Command .)

The help Command

(geos:0) 200 => help help
Help for help:
Functions for manipulating/accessing the help tree
==============================================================================
This is the user-level access to the on-line help facilities for Swat. If
given a topic (e.g. "brk") as its argument, it will print all help strings
defined for the given topic (there could be more than one if the same name is
used for both a variable and a procedure, for instance). If invoked without
arguments, it will enter a browsing mode, allowing the user to work his/her
way up and down the documentation tree
==============================================================================
(geos:0) 201 =>

Swat Introduction: 5 Essential Commands

This section covers the function and usage of some of the most important Swat commands. These commands fall into the following command groups:

  • Cycle of Development
    Sending down a new copy of a geode and running it.
  • Attaching and Detaching Swat
    The commands used to control the link between Swat and GEOS.
  • Setting Breakpoints and Code Stepping
    The commands used to stop the execution of an application's code at pre-determined points and then examine the code, line by line if necessary.
  • Examination of Memory
    The commands used to examine memory from individual bytes to whole structures such as generic trees and objects.
  • Other Important Commands
    Other commands which are important to know but do not fit into the aforementioned groups.

A complete list of the Swat commands is contained in the Reference chapter.

Cycle of Development

Attaching and Detaching

Breakpoints and Code Stepping

Examining and Modifying Memory

Other Important Commands

Swat Introduction: 5.1 Essential Commands: Cycle of Development

send, run, exit, patient-default

These commands come in handy whenever you've edited and recompiled your application. You'll want to exit the application on the target machine. Use the send command to send down the new, compiled version of your application. Then use the run command to start up the program.

send
send [<geode-name>]

To send the latest compiled version of your program, type "send" followed by the application's patient name (the first part of the field on the "name" line of the .gp file).

run
run [<geode-name>]

To run your geode on the target machine, type "run" followed by the application's patient name (the first part of the field on the "name" line of the .gp file).

exit
exit <geode-name>

To exit a running application, type "exit" followed by the application's patient name. The exit command won't work if your application has encountered a fatal error.

patient-default
patient-default [<geode-name>]

Use this command to set a default patient to use with the send and run commands. The send and run commands will operate on this patient if they are not passed arguments.

Swat Introduction: 5.2 Essential Commands: Attaching and Detaching

attach, att, detach, quit, cont, Ctrl-C

This group of commands controls the state of the connection between Swat and GEOS when Swat is running. The attach and att commands are used to establish the connection while detach and quit are used to sever it. The most frequently used commands in this group are att with the -r flag and detach . Some related commands contained in the Reference are go , istep , sstep , and next .

The following is a typical but simplified debugging cycle using detach and att . It assumes you have already attached for the first time.

  • When a bug is encountered, determine where and what the bug is, then detach Swat by typing detach .
  • Edit the application to fix the bug, recompile it and download it to the target machine.
  • Re-attach Swat to the target PC using the att command by typing Scrolllock-Shift-s on the target machine and typing att on the host machine.
  • Continue debugging the application.
  • Repeat the detach, edit, attach, debug cycle until all of the bugs are fixed.

By themselves, the commands shown below can not do much except open and close the communication lines between GEOS and Swat. More commands to examine and modify the application's code as it runs are needed to start actual debugging.

Swat Display 3-3 Detaching and Attaching

(geos:0) 202 => det cont
PC detached
(loader:0) 203 => att
Re-using patient geos
Re-using patient ms4
Re-using patient vidmem
Re-using patient swap
Re-using patient xms
Re-using patient disk
Re-using patient kbd
Re-using patient nimbus
Re-using patient stream
Re-using patient sound
Re-using patient standard
Re-using patient ui
Re-using patient styles
Re-using patient color
Re-using patient ruler
Re-using patient text
Re-using patient motif
Re-using patient vga
Re-using patient spool
Re-using patient serial
Re-using patient msSer
Re-using patient nonts
Re-using patient welcome
Re-using patient shell
Re-using patient manager
Re-using patient math
Re-using patient borlandc
Re-using patient mess1
Thread 1 created for patient geos
Thread 2 created for patient geos
Thread 0 created for patient ui
Thread 0 created for patient spool
Thread 0 created for patient welcome
Thread 0 created for patient manager
Thread 1 created for patient manager
Thread 0 created for patient mess1
Attached to PC
Stopped in 0070h:0005h, address 0070h:0005h
DOS+773: JMP DOS+2963
(geos:0) 204 =>

In this example, we use the det cont command so that GEOS will keep running. We then re-attach with att . In the intervening time, the two machines are independent, and the serial line is unused. We could have taken advantage of this to send down a new copy of some application (as long as that application was not running on the target machine).

att

att

The att command is similar to the attach command, but has no bootstrap argument (as explained below).

attach

attach [(+b|-b)]

This command is used to attach Swat to the target PC when the Swat stub is already invoked. The -b argument is to bootstrap and the +b argument is not to bootstrap. Bootstrapping means that Swat will search for the symbol files of all of the geodes and threads as they are encountered rather than all at the beginning. This saves some time if you've just detached and need to re-attach, using only a few geodes while debugging. If no argument is given, the most recent bootstrap setting is used. The default bootstrap setting is +b .

cont

cont

The cont command continues the execution of GEOS after it has been stopped for some reason such as at a breakpoint or fatal error or by control-C. This command is often aliased as the letter c .

detach

detach [(cont|leave)]

The detach command will detach Swat from the target PC. By itself, the detach command will detach Swat and exit GEOS. This command is usually used after a bug is encountered and the source code needs to be modified and recompiled (see Detaching and Attaching ).

cont
The cont option will just detach Swat and allow GEOS to continue to run normally. This option is used when the debugging process is finished but GEOS is still needed to do other things (such as word processing) and you may need to re-attach later for further debugging.
leave
The leave option will detach Swat but keep GEOS stopped wherever it was when the detach leave command was given. This command is useful for passing debugging control to someone remotely logged in to the workstation or when Swat can not continue for some reason.

quit

quit [(cont|leave)]

The quit command is only used when Swat needs to be exited for good. It will detach Swat (if necessary), exit from Swat on the development station, and exit from GEOS.

cont
The cont option exits Swat on the development station but allows GEOS to continue running normally on the target PC. This option is used when the debugging process is finished but GEOS is still needed to do other things such as word processing.
leave
The leave option will exit Swat but will keep GEOS stopped wherever it was when the quit leave command was given.

Ctrl-C

Ctrl-C

The Control-C command is the command used to stop the execution of GEOS at any point. This command is executed by holding down the Ctrl key and pressing the c key. It is used to stop GEOS in order to set a breakpoint, examine memory, or to get a command line prompt.

Swat Introduction: 5.3 Essential Commands: Breakpoints and Code Stepping

The commands in this group are used to stop at specified breakpoints in an application's code and then step through the code line by line if necessary. These commands are often used with each other to examine critical areas in the application source code.

Breakpoints

stop, brk, go, cbrk, spawn

The stop , brk and cbrk commands are used to set breakpoints. The breakpoint commands have many subcommands controlling the actions and attributes of a particular breakpoint.

The cbrk command sets breakpoints to be evaluated by the Swat stub; brk sets them to be evaluated by Swat. The Swat stub can evaluate the conditions much faster than Swat, but cbrk has certain limitations: only a limited number of breakpoints can be set using cbrk , and these breakpoints can only compare registers when the breakpoint is hit with a given set of criteria.

stop
stop in <class>::<message> [if <expr>]
stop in <procedure> [if <expr>]
stop in <address-history-token> [if <expr>]
stop at [<file:]<line> [if <expr>]
stop <address> [if <expr>]

This is the main command to use when setting breakpoints in C programs. The "stop in" command will set a breakpoint at the beginning of a procedure, immediately after the procedure's stack frame has been set up. The "stop at" command will set a breakpoint at the first instruction of the given source line. If no <file> is specified, the source file for the current stack frame is used. If a condition is specified by means of an "if <expr>" clause, you should enclose the expression in curly braces to prevent any nested commands, such as a "value fetch" command, from being evaluated until the breakpoint is hit.

brk
brk [<sub-command>]

The brk (breakpoint) command is used for setting nearly all breakpoints in an application's code. The simplest way to use it is to type brk with a single addr argument. The address is usually a routine name for a suspect procedure, and when the breakpoint is reached the code-stepping commands can be used to examine it carefully. The brk command can also create conditional breakpoints which will only be taken if certain conditions are satisfied. Once set, a breakpoint is given an integer number which can be obtained using the list subcommand (see Breakpoints ).

brk <addr> [<command>]
The brk command without any subcommands sets an unconditional breakpoint at the address specified in addr . If the command argument is passed, the given swat command will be carried out when the breakpoint is hit.
brk delete <break>*
Deletes the given breakpoint(s), just as clear , above.
brk enable <break>*
Enables the given breakpoint(s). Has no effect on previously enabled breakpoints. If no breakpoint is given, all breakpoints for the current patient are enabled.
brk disable <break>*
Disables the given breakpoint(s). It has no effect on previously disabled breakpoints. If no breakpoint is given, it disables all breakpoints for the current patient.
brk list [<addr>]
Lists all the breakpoints, whether they are enabled, where they are set, their conditions, and what actions they will take if encountered. If addr is given, it returns the breakpoint numbers of all breakpoints set at the given address.

Breakpoints

(geos:0) 4 => brk list
Num S Address Patient Command/Condition
1   E loader::kcode::LoaderError all echo Loader death due to [penum
                    LoaderStrings [read-reg ax]]
                    expr 1
2   E kcode::FatalError all
                     why
                     assign kdata::errorFlag 0
                     expr 1
3   E kcode::WarningNotice all why-warning
4   E kcode::CWARNINGNOTICE all why-warning
(geos:0) 5 => stop in Mess1Draw
brk5
(geos:0) 6 => brk list
Num S Address Patient Command/Condition
1   E loader::kcode::LoaderError all echo Loader death due to [penum
                    LoaderStrings [read-reg ax]]
                     expr 1
2   E kcode::FatalError all
                     why
                     assign kdata::errorFlag 0
                     expr 1
3   E kcode::WarningNotice all why-warning
4   E kcode::CWARNINGNOTICE all why-warning
5   E <ss1::MESS1_TEXT::Mess1Draw+10 all halt
(geos:0) 7 => brk dis brk4
(geos:0) 8 => brk list
Num S Address Patient Command/Condition
1   E loader::kcode::LoaderError all echo Loader death due to [penum
                    LoaderStrings [read-reg ax]]
                     expr 1
2   E kcode::FatalError all
                     why
                     assign kdata::errorFlag 0
                     expr 1
3   E kcode::WarningNotice all why-warning
4   D kcode::CWARNINGNOTICE all why-warning
5   E <ss1::MESS1_TEXT::Mess1Draw+10 all halt
(geos:0) 9 =>
go
go [<address-expressions>]

The go command sets a one-time breakpoint and resumes execution on the target PC. The net effect of this is to let the target go until it hits a given address, then stop.

cbrk
cbrk [<sub-command>]

The cbrk (conditional breakpoint) command is used to set fast conditional breakpoints. This command is very similar to the brk command above, except that the condition is evaluated by the Swat stub--this increases the speed of the evaluation. There are, however, certain restrictions on the cbrk command: only a limited number of breakpoints can be set (eight), and the scope of the evaluation is limited to comparing word registers (or a single word of memory) to a given set of values.

In the following descriptions, criteria stands for a series of one or more arguments of the form:

<register> <op> <value>
register
One of the machine's registers or "thread," which corresponds to the current thread's handle.
op
One of the following ten comparison operators: = (equal), != (not equal), > (unsigned greater-than), < (unsigned less-than), >= (unsigned greater-or-equal), <= (unsigned less-or-equal), +> (signed greater-than), +< (signed less-than), +>= (signed greater-or-equal), +<= (signed greater-or-equal). These correspond to the 8086 instructions JE, JNE, JA, JB, JAE, JBE, JG, JL, JGE, JLE, respectively.
value
A standard Swat address expression. The resulting offset is the value with which the register will be compared when the breakpoint is hit.
cbrk <addr> <criteria>*
The basic cbrk command sets a fast conditional breakpoint at the address specified in addr .
cbrk cond <break> <criteria>*
Changes the criteria for the breakpoint. If no criteria is given the breakpoint becomes a standard, unconditional breakpoint.
spawn
spawn <patient-name> [<addr>]

The spawn command is used to set a temporary breakpoint in a process or thread which has not yet been created. The arguments are

patient-name
The permanent name, without extension, as specified by the name directive in the .gp file; this is the name of the patient in which to set a temporary breakpoint. A unique abbreviation is sufficient for this argument.
addr
A particular address at which to place the breakpoint. If no address is given, Swat will stop as soon as the given geode is loaded.

This command is used to stop the geode before any of its code can be run, allowing breakpoints to be set in the desired routines. If you could not stop the machine in this manner, the application could hit a buggy routine before a breakpoint could be set in that routine. The spawn command can also be used to catch the spawning of new threads which is useful to keep track of the threads being used by an application (see The spawn Command ).

Swat Display 3-5 The spawn Command

(geos:1) 12 => spawn mess1 Mess1Draw
Re-using patient math
Re-using patient borlandc
Re-using patient mess1
Thread 0 created for patient mess1
Interrupt 3: Breakpoint trap
Stopped in Mess1Draw, line 211, "C:OMNIGO/Appl/SDK_C/MESS1/MESS1.GOC"
Mess1Draw(GStateHandle gstate) 				/* GState to draw to */
(mess1:0) 13 =>

Code Stepping

srcwin, istep, sstep

Once an application is stopped at a breakpoint and you want to examine the code line by line, you can use the commands istep (instruction step) and sstep (source step). These enter the instruction step mode or source step mode to examine and execute the application code line by line.

The subcommands for both istep and sstep are nearly the same and are used for actions including stepping to the next line, skipping the next instruction, or exiting the step mode and continuing the execution of the application. The istep and sstep commands are very similar except that istep is used when stepping through assembly source code (thus stepping through instructions), and sstep is used for stepping through C source code.

srcwin
srcwin <numlines> [view]

The srcwin command will display the source code surrounding the presently executing code any time execution is stopped. The presently executing line will be highlighted. You may set breakpoints with the mouse by clicking on the line numbers which appear to the side. To scroll the srcwin buffer use the arrow keys, the <PgUp> key, and the <PgDn> key.

istep, sstep
istep [<default subcommand>]
sstep [<default subcommand>]

These two commands are used to single-step through code, executing one or more instructions at a time. The default subcommand argument determines the action taken by Swat when the Return key is pressed. For example, the command

[hello3:0] 7 => istep n

will enter instruction step mode, and subsequently pressing the Return key will have the same effect as pressing n . If no default command is given, pressing Return has the same effect as pressing s .

The subcommands to the istep and sstep commands are

s (single step)
Step one instruction. This is the most frequently used subcommand.
n, o (next, over)
Continue to the next instruction but do not display any procedure calls, repeated string instructions, or software interrupts. They will stop when GEOS returns to the same frame as the previous displayed instruction. The frame is the same when the stack pointer and current thread are the same as when the n subcommand was given. o differs from n in that it executes all instructions in a macro without further interpretation and can only be used with istep . If a breakpoint other than for the next instruction is hit, it will take effect as long as the above conditions are met.
N, O (Next, Over)
These are like n and o but will stop whenever a breakpoint is hit even if the frame is different. O will execute all instructions in a macro without further interpretation, and it can only be used with istep . If a breakpoint other than one for the next instruction is hit, it will take effect as long as the above conditions are met.
q, Esc, <space> (quit)
These stop istep/sstep and return to the command level. These subcommands are used when a point in the code is reached where another command needs to be used--to examine memory, for example.
c (continue)
This exits istep and continues the execution of the application. When GEOS next stops, Swat will return to the command prompt.
M (message)
This will continue until the next handled message is received. When the handler is invoked, Swat will return to step mode. This subcommand is often used with the ObjMessage() and ObjCallInstanceNoLock() assembly routines.
F (finish message)
This finishes the current message, stops when execution returns to a frame that is not part of the kernel, and remains in step mode.
f (finish frame)
This finishes the current stack frame, stops, and remains in step mode.
S (skip instruction)
This skips the current instruction, does not execute it, and goes on to the next instruction in step mode.

Swat Introduction: 5.4 Essential Commands: Examining and Modifying Memory

The commands in this section all deal with the examination, manipulation, or modification of the memory used by an application. Memory from individual bytes to complex data structures such as objects can be displayed and examined. These commands fall into the following groups:

  • Simple Memory Examination.
    Examination of bytes, words, and double words with no modification.
  • Complex Memory Examination
    Examination of structures such as objects, generic trees, and handle tables with no modification.
  • Memory Examination with Modification
    Examination of memory with modification if desired. Some commands are used only for memory modification.

The commands in this section are often used with each other and with the code-stepping and breakpoint commands in order to pinpoint bugs in an application's code. Breakpoints can be set, code can be stepped through, and then the memory that the code uses can be examined.

Some related commands defined in the Reference chapter are down , func , handles , hgwalk , impliedgrab , penum , phandle , pinst , piv , precord , skip , systemobj , up , and where .

Simple Memory Examination

bytes, words, dwords, frame, backtrace, why, listi

The commands in this group are used to look at simple blocks of memory without modification. They are defined fully in the entries below.

bytes, words, dwords
bytes [<addr>] [<length>]
words [<addr>] [<length>]
dwords [<addr>] [<length>]

The bytes , words , and dwords commands are essentially the same except that each looks at a different sized piece of memory. These commands will display the memory as a pointer to a dump of bytes, words, or dwords using the given or most recent (if no address is given) address.

The bytes command additionally displays the dump as its ASCII character representation, if any. These three commands are used to examine memory on a very basic level and are useful only if the user knows what the bytes, words, or dwords should or should not be and so can spot any problems. For example, if a certain character string such as "Application" is supposed to be stored at the address given by fileType and the command

[hello3:0] 11 => bytes fileType

dumps the characters "noitacilppA", then there is most likely a problem.

These commands will automatically use the given addr as a pointer to the memory to be examined. If Return is hit many times in a row, the result will be to examine adjacent pieces of memory. (See The words Command .)

Swat Display 3-6 The words Command

(mess1:0) 15 => words themeSongBuf
Addr: +0 +2 +4 +6 +8 +a +c +e
0040h: 0004 0000 0049 0000 0004 0001 0083 0000
(mess1:0) 16 => words
Addr: +0 +2 +4 +6 +8 +a +c +e
004eh: 0000 0006 0004 0028 0000 0001 020b b800
(mess1:0) 17 => !!
words
Addr: +0 +2 +4 +6 +8 +a +c +e
005ch: b800 000c 0020 0002 0001 0000 0001 020b
(mess1:0) 18 => !!
words
Addr: +0 +2 +4 +6 +8 +a +c +e
006ah: 020b b800 000c 0010 0002 0001 000a 0010

Here the words command examines a buffer in memory. When this command is repeated without arguments, it will display memory continuing where the last command left off. Note the use of the !! command to repeat the previous command.

backtrace, frame
backtrace [<frames to list>]
frame <subcommand>

The backtrace and frame commands are used to examine data that has been pushed onto the stack. An application may crash in a routine that is correctly written but has been passed bad data.

The backtrace command prints out a list of all the active frames for the current patient. Then the user can choose a particular frame to examine using one of the frame subcommands. The frame command is used to access frames that have been pushed onto the stack, where a frame is the information for a routine that needs to be saved when it calls another routine.

The frame and backtrace commands can be used together to print the active frames with backtrace and then access data in these frames with frame . However, most of the frame subcommands expect a token for a frame, not the frame number given by the backtrace command. To get this token, the top , cur and next subcommands are used. Then the other frame subcommands can be used with the token to further examine the frame data. See also the reference information for the frame command.

Backtrace and Frame commands

Death due to SOUND_BAD_EVENT_COMMAND
Execution died in patient sound:
SoundHandleTimerEvent+63: MOV AX, 7 (0007h)
*** No explanation available ***
Interrupt 3: Breakpoint trap
Stopped in FatalError, address 1844h:0163h
SoundHandleTimerEvent+63: MOV AX, 7 (0007h)
(mess1:0) 2 => backtrace
  1: near FatalError(), 1844h:0163h
  2: far AppFatalError(), 1844h:0163h
* 3: far SoundHandleTimerEvent(), 2cb2h:003fh
  4: far SoundLibDriverPlaySimpleFM(), 6247h:0062h
  5: far ResourceCallInt(), 1844h:1492h
  6: far SoundLibDriverStrategy(), 2cb2h:0ab2h
  7: near SoundCallLibraryDriverRoutine(), 629ch:00feh
  8: far SoundPlayMusic(), 629ch:0028h
  9: far ResourceCallInt(), 1844h:1492h
  10: far SOUNDPLAYMUSICNOTE(mh = ^h42b0h (at 753ch), priority = 1eh, tempo = 4h
, flags = 80h), 62d6h:00f3h
  11: far ResourceCallInt(), 1844h:1492h
  12: far Mess1Draw(), MESS1.GOC:307
  13: far MESS1PROCESSMETA_EXPOSED(win = 3a60h, message = 69 (invalid), oself =
3ee0h:0000h), MESS1.GOC:362
  14: far ResourceCallInt(), 1844h:1492h
MSG_META_EXPOSED (3a60h 0000h 0000h) sent to Mess1ProcessClass (^l20f0h:0h)
  16: near ObjCallMethodTableSaveBXSI(), 1844h:9ea5h
  17: far SendMessage(), 1844h:9d9bh
  18: far ObjMessage(), 1844h:1d9ch
  19: far MessageDispatchDefaultCallBack(), 1844h:1c72h
  20: far MessageProcess(callBack = 1844h:1c68h (geos::kcode::MessageDispatchDef
aultCallBack)), 1844h:1c15h
  21: far MessageDispatch(), 1844h:1b31h
  22: far ThreadAttachToQueue(), 1844h:bd2ch
(mess1:0) 3 => frame 12
Mess1Draw+302: MOV AX, 100 (0064h)

Complex Memory Examination

print, hwalk, lhwalk, objwalk, pobject, gentree, vistree, vup, gup

The commands in this group are used to examine complex data structures in GEOS.

print
print <expression>

The print command is used to print out the value of the given expression argument. The expression argument is normally some sort of typed address. When there is no type for the expression , then its offset is printed.

The power of this command lies in its ability to print any type at any address; thus, it is used frequently to print out the values of important expressions such as registers or variables. The print command also takes many flags which control the way in which the value of the expression is displayed such as in decimal or hexadecimal. See the Reference chapter for more information on the flags for the print command.

hwalk
hwalk [<patient>]

Use the hwalk (heap walk) command to display blocks on the global heap. Its output can be tailored in various ways according to how the flags are set. If a patient is given, then hwalk will only print the blocks owned by that patient. There are many fields in the listing such as the handle, address, and type of each block. By examining these fields, the user can get an overall sense of how the global heap is being managed, whether any block looks too big or too small, and what the handles of important blocks are. (See The hwalk Command .)

Swat Display 3-8 The hwalk Command

(mess1:0) 6 => hwalk mess1
HANDLE ADDR SIZE FLAGS LOCK OWNER IDLE  OINFO 						TYPE
----------------------------------------------------------------
20f0h 41ebh 2272 FIXED  n/a mess1  n/a     1h 						R#1 (dgroup)
4160h 58eah  448 sDS  a   1 mess1 105eh    1h 						R#2 (MESS1_TEXT)
3a60h 59adh  784 s SL     0 mess1  0:03    1h 						WINDOW
4bb0h 6176h  560 s SL     0 mess1  0:05    1h 						WINDOW
3970h 6232h  336 s SL     0 mess1  0:03    1h 						GSTATE
3ee0h 633ch  160 s S  a   0 mess1  0:05 49c0h 						Geode
4950h 63beh 1280 s SL     0 mess1  0:05 49c0h 						OBJ(mess1:0)
4340h 640eh 1328   SL     0 mess1  0:05 49c0h 						R#3 (INTERFACE)
42b0h 753ch   96 s S      4 mess1 1249h    1h
4bd0h 7542h   96 s S      0 mess1  0:01    1h
41b0h 89d4h  896   SL     0 mess1  0:05 49c0h 						R#4 (APPRESOURCE)
4270h 99e1h   32 s S      0 mess1  0:05     1h

Total bytes allocated: 8288
(mess1:0) 7 =>
lhwalk, objwalk
lhwalk [<addr>]
objwalk [<addr>]

The lhwalk (local heap walk) command is used to display information about a local memory heap, and the objwalk command is used to print out information about an object block. After using hwalk to locate a specific block, lhwalk or objwalk can be used to print out information about that particular block. These commands also print out fields of information which include the local handle, the address, size, and type of data or object. See the Reference chapter for more information on the fields printed by lhwalk and objwalk . (See The objwalk Command .)

Swat Display 3-9 The objwalk Command

(mess1:0) 11 => objwalk ^h4340h

Heap at 640eh:0 (^h4340h), Type = LMEM_TYPE_OBJ_BLOCK
In use count = 3, Block size = 1328, Resource size = 59647 para (192 bytes)

HANDLE ADDRESS 	SIZE FLAGS CLASS (NAME)
------ ------- 	---- ----- ------------
 001ch     56h 		1eh  ---  *flags*
 001eh     76h 		c1h  D RO GenPrimaryClass (Mess1Primary)
 0020h    1aah 		ceh  D RO GenViewClass (Mess1View)
 0022h    166h 		32h ID  O OLGadgetAreaClass
 0024h    19ah 		 eh  I
 0026h    492h 		6bh  D  O GenValueClass
 0028h    2deh 		6bh  D  O GenValueClass
 002ah    37eh 		a6h ID  O GenInteractionClass
 002ch    44ah 		46h ID  O OLMenuBarClass
 002eh    13ah 		 bh ID
 0030h    426h 		22h ID  O OLMenuButtonClass

Free handles = 17, null handles = 0
Objects = 8, 4 of them marked ignoreDirty

(mess1:0) 12 =>
pobject
pobject [<addr>] [<print level>]

The pobject (print object) command (often abbreviated pobj ) is used to print out the entire instance data chunk of an object. You can use gentree , vistree , or hwalk and objwalk to get the handles for an object; once you have them, use pobj with the handles, as follows:

[hello3:0] 7 => pobj ^l0x43d0:0x0022

will print out the instance data chunk specified by that optr.

Any valid address expression, such as a dereferenced object name, may be used as an addr . Additionally, the print level can be changed to print just the headings to each of the master levels and an address history number. The pobject command is used to verify that the object is behaving correctly and that its instance variables (if any) are correct. (See The pobject Command .)

Swat Display 3-10 The pobject Command

[lesink:0] 10 => pobj ^l4710h:0020h
*UpTextView::UpTextViewClass (@7, ^l4710h:0020h)
master part: Gen_offset(123) -- UpTextViewInstance
@8: {UpTextViewInstance (^h18192:622)+123} = {
    MetaBase Gen = {
        ClassStruct _far *MB_class = 360ah:162fh (motif::dgroup::OLPaneClass)
    }
    LinkPart GI_link = {
        dword LP_next = 4710h:001fh
    }
    CompPart GI_comp = {
        dword CP_firstChild = 4710h:002ah
    }
    word GI_visMoniker = 0h
    word GI_kbdAccelerator = 0h
    byte GI_attrs = 2h
    byte GI_states = c0h
    PointDWFixed GVI_origin = {
        DWFixed PDF_x = {0.000000}
        DWFixed PDF_y = {0.000000}
    }
    RectDWord GVI_docBounds = {
        long RD_left = 0
        long RD_top = 0
        long RD_right = +480
        long RD_bottom = +480
    }
    PointDWord GVI_increment = {
        long PD_x = +20
        long PD_y = +15
    }
    PointWWFixed GVI_scaleFactor = {
        WWFixed PF_x = {1.000000}
        WWFixed PF_y = {1.000000}
    }
    ColorQuad GVI_color = {
        CQ_redOrIndex = fh, CQ_info = 0h, CQ_green = 0h, CQ_blue = 0h
    }
    word GVI_attrs = 10h
    byte GVI_horizAttrs = 88h
    byte GVI_vertAttrs = 88h
    byte GVI_inkType = 0h
    dword GVI_content = 4710h:0024h
    dword GVI_horizLink = 0000h:0000h
    dword GVI_vertLink = 0000h:0000h
}
Variable Data:
         *** No Variable Data ***
[lesink:0] 11 =>

In addition to printing information about the object at a given address, pobject can print information about certain objects in the application if passed certain flags:

pobject -i
Prints information about the windowed object under the mouse pointer.
pobject -c
Prints information about the content for the view over which the mouse is located.

There are more flags available, and it is also possible to ask for more or less instance data information. See the full reference for this command for details.

gentree
gentree [<addr>] [<instance field>]

The gentree (generic tree) command prints out a generic tree from the given addr and instance field . The addr must be the address of an object in the generic tree, and the instance field must be the offset into the Generic master part of the instance chunk or any instance data within the Generic master level which is to be printed. This command is used primarily to ensure correct structure of a generic tree and its instance data and to find a particular object in the generic tree. The -i (implied grab) option is used to find an object by placing the mouse over the window in which the object resides and typing the following:

[hello3:0] 7 => gentree -i

The default address that gentree examines is contained in *DS:SI. (See Gentree and Gup .) To examine objects more closely, pass the handles displayed by gentree to the pobject command.

vistree
vistree [<addr>] [<instance field>]

The vistree (visual tree) command prints out a visual tree from the given addr and instance field . The addr must be the address of an object in the visual tree, and the instance field must be the offset into the Vis master part of the object's instance data which is to be printed. This command is primarily used to examine the on-screen layout of the application and to ensure correct structure of the visual tree and its instance data. The vistree command can use the -i option (implied grab), which will use the window that the mouse is over as the first visual object in the printed tree. The default address that vistree examines is contained in *DS:DI. To examine objects more closely, pass the handles displayed by vistree to the pobject command.

gup
gup [<addr>] [<instance field>]

The gup (Generic UPward query) command is used to go up the generic tree from a particular object specified by the addr argument, the default *DS:SI, or the -i option. The -i option (implied grab) uses the windowed object under the mouse as the object from which to start the upward query. This command is used primarily to ensure correct generic class hierarchy and to determine the field of the given object.

Swat Display 3-11 Gentree and Gup

(mess1:0) 19 => gentree -i

GenViewClass (@5, ^l4340h:0020h)
 GenValueClass (@6, ^l4340h:0026h)
 GenValueClass (@7, ^l4340h:0028h)

(mess1:0) 20 => gup @5

GenViewClass (@11, ^l4340h:0020h)
GenPrimaryClass (@12, ^l4340h:001eh) "MESS #1"
GenApplicationClass (@13, ^l41b0h:0024h) *** Is Moniker List ***
GenFieldClass (@14, ^l4080h:001eh)
GenSystemClass (@15, ^l2460h:0020h)

(mess1:0) 21 => gentree ^l4340h:001eh

GenPrimaryClass (@16, ^l4340h:001eh) "MESS #1"
 GenViewClass (@17, ^l4340h:0020h)
 GenValueClass (@18, ^l4340h:0026h)
 GenValueClass (@19, ^l4340h:0028h)
vup
vup [<addr>] [<instance field>]

The vup (Visual UPward query) command is used to examine the visual ancestors of a particular object given by the addr argument, the default *DS:SI, or the -i option. The vup command can be used with the -i option (implied grab) to use the windowed object under the mouse as the object from which to start the upward query. This command is used primarily to ensure correct visual class hierarchy and to determine the field of the given object.

Memory Examination with Modification

assign, imem

The commands in this group are used to modify memory without detaching Swat and editing the application code. They are often used in conjunction with istep , sstep , and pobject to fix any small errors while the code is executing rather than detaching, modifying the actual code, and recompiling. These fixes are temporary, and you must change the source code to enact the real bug fixes.

assign
assign <addr> <value>

The assign command will assign the given value to the given addr , which can only have type byte , word , or dword . Both memory locations and registers may be assigned new values. This command is used to correct minor mistakes or test differing values at run-time without having to recompile.

imem
imem [<addr>] [<mode>]

The imem (inspect memory) command combines examination and modification of memory into one command. It can be used to search through areas of memory and modify problem areas selectively. The command is used to print out memory starting at either the given addr or at the default DS:SI in one of the following modes:

b (bytes)
Displays the memory in terms of bytes.
w (words)
Displays the memory in terms of words.
d (double words)
Displays the memory in terms of double words.
i (instructions)
Displays the memory in terms of instructions.

There are many subcommands to imem which are executed in the same manner as those for istep and sstep . These subcommands are as follows:

b, w, d, i
These will reset the mode to the given letter and redisplay the data in that mode.
n, j, <return> (next, jump)
This will advance to the next piece of data using the appropriate step size (dependent upon the display mode).
p, k, P (previous)
This will retreat to the preceding piece of data. While in instruction mode, if the displayed instruction is wrong, try again with the P subcommand.
<space>
This will clear the data being displayed and allow you to enter a new value in accordance with the current mode. This is exactly like the assign command except for singly and doubly quoted strings. A singly quoted string such as `hello' will have its characters entered into memory starting at the current address with no null byte at the end. A doubly quoted string such as "good-bye" will be entered into memory at the current address with the addition of a null byte at the end of the string. This subcommand may not be used in instruction mode.
q (quit)
Quits the imem mode and returns to the command level.
Ctrl-d
Control-d (down) displays ten successive memory elements in the current display mode.
Ctrl-u
Control-u (up) displays ten of the preceding memory elements in the current display mode.

Swat Introduction: 5.5 Essential Commands: Other Important Commands

alias, mwatch, objwatch, save, switch, sym-default, why

These commands are important to know but do not readily fall into any of the previous categories. This section will discuss each of these commands in relation to the debugging process.

alias
alias [<name> [<body>]]

This command is normally used to abbreviate a long command or series of commands with one single, descriptive command. If no arguments are given, then alias will just give a list of all the aliases and the commands they alias. The alias command is a convenient shortcut for oft used commands or for commands that take a long time to type.

If only one argument is given, then alias will try to match that argument to the command it is aliased to. For example, if the print command is aliased to p , then alias p will return print as a result. If two arguments are given, then alias will cause argument1 to be allowed as an alternative to typing argument2. For example, if the command print were to be aliased as p , the alias command would be used as below:

[hello:0] 5 => alias p print

Typing p will now have the same effect in Swat as typing print .

mwatch
mwatch [<message>+]

The mwatch (message watch) command watches a particular message and displays all deliveries of that message without stopping GEOS. This command can help to verify that a particular message is getting sent to all the right places and is not sent to any of the wrong places. Up to eight messages can be watched at once, and the mwatch command with no arguments clears all watched messages. Note that some message handlers will relay a message on to a superclass' handler; this may make it appear that the message is being delivered again, though this is not the case. (See The mwatch Command .)

Swat Display 3-12 The mwatch Command

(ui:0) 30 => mwatch MSG_VIS_DRAW MSG_META_EXPOSED
(ui:0) 31 => c
MSG_VIS_DRAW, ^l2860h:001eh, GenInteractionClass
 cx = 3f80h, dx = 0000h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:001eh, GenInteractionClass
 cx = 3f80h, dx = 0000h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:001eh, GenInteractionClass
 cx = 3f80h, dx = 0016h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:001eh, GenInteractionClass
 cx = 3f80h, dx = 0016h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:002ch, OLGadgetAreaClass
 cx = 3f80h, dx = 3950h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:002ch, OLGadgetAreaClass
 cx = 3f80h, dx = 3950h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:002ch, OLGadgetAreaClass
 cx = 3f80h, dx = 007ch, bp = 3950h
MSG_VIS_DRAW, ^l2860h:002ch, OLGadgetAreaClass
 cx = 3f80h, dx = 007ch, bp = 3950h
MSG_VIS_DRAW, ^l2860h:0020h, GenTextClass
 cx = 3f80h, dx = 3950h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:0020h, GenTextClass
 cx = 3f80h, dx = 3950h, bp = 3950h
MSG_VIS_DRAW, ^l2860h:0020h, GenTextClass
 cx = 3f80h, dx = 3950h, bp = 3950h
objwatch
objwatch [<addr>]

The objwatch (object watch) command is used for displaying the messages that have reached a particular object. It is useful for verifying that messages are being sent to the object at addr. If no argument is given, then any current objwatch is turned off. (See The objwatch Command .)

Swat Display 3-13 The objwatch Command

mess1:0) 2 => objwatch Mess1View
brk5
(mess1:0) 3 => c
MSG_META_MOUSE_PTR, ^l44a0h:0020h, GenViewClass
 cx = 00afh, dx = 0013h, bp = 0000h
MSG_META_MOUSE_PTR, ^l44a0h:0020h, GenViewClass
 cx = 00afh, dx = 0013h, bp = 0000h
MSG_META_WIN_UPDATE_COMPLETE, ^l44a0h:0020h, GenViewClass
 cx = 4b90h, dx = 0000h, bp = 0000h
MSG_META_MOUSE_PTR, ^l44a0h:0020h, GenViewClass
 cx = 00b0h, dx = 0013h, bp = 0000h
MSG_META_RAW_UNIV_LEAVE, ^l44a0h:0020h, GenViewClass
 cx = 44a0h, dx = 0020h, bp = 4b90h
MSG_VIS_DRAW, ^l44a0h:0020h, GenViewClass
 cx = 4b80h, dx = 23c0h, bp = 23c0h
MSG_VIS_COMP_GET_MARGINS, ^l44a0h:0020h, GenViewClass
 cx = 4b80h, dx = 23c0h, bp = 0000h
MSG_VIS_DRAW, ^l44a0h:0020h, GenViewClass
 cx = 4b80h, dx = 0163h, bp = 23c0h
MSG_META_WIN_UPDATE_COMPLETE, ^l44a0h:0020h, GenViewClass
 cx = 4b90h, dx = 0000h, bp = 0000h
save
save <filename>

The save command, when passed a file name, saves the contents of Swat's main buffer to that file. Thus this command dumps Swat output to a file.

showcalls -L
showcalls -L
showcalls

The showcalls command is, for the most part, only useful under rare circumstances. However, showcalls with the -L option is useful for a fairly common situation: a library is failing to load but you can't find which one.

To find out which libraries an application is trying to load, use showcalls -L . Swat will tell you whenever a library is sought. Whichever library is being sought when the "missing system file" error message appears is the library that's causing your problems: perhaps you need to re-install it.

Use the showcalls command without any arguments to turn off library-loading reporting.

switch
switch [(<patient>:<thread-num>|<threadID>)]

The switch command is used to switch between applications or threads in Swat but does not physically change threads on the target PC. This allows the transfer of debugging control between threads of the same patient. If no argument is given, then switch will change to the thread executing when GEOS was halted. Another way to switch threads is to type the name of the patient on the command line. If a patient has more than one thread, type the name of the patient, a space, and then the thread number. To change thread numbers within a geode, type a colon followed by the thread number to change to (e.g. " :1")

sym-default
sym-default [<patient>]

The sym-default (symbol default) command is used to set the default patient to use when parsing an address expression which is not defined in the current patient. For example, if a breakpoint is hit in the kernel and an object in the application code needs to be examined, Swat will know to use the application as a patient and not the kernel. This command is useful when debugging a single patient, the most common way to debug. If no patient argument is given, then the name of the default patient will be displayed.

This command is normally aliased to sd .

why
why

The why command prints the error code for an occurrence of a fatal error. This command is useful because it can give a good idea of why GEOS crashed.

Swat Introduction: 6 Additional Features

This section covers the features of Swat that make it easier to use when debugging an application.

  • Mouse support
    You can use the mouse to capture and paste text in the main Swat buffer. Capture text by click-dragging with the left mouse button. Pressing the right mouse button pastes the captured text to the Swat prompt line.
  • Navigating the Main Buffer
    To scroll the main buffer, use Ctrl-u (up), Ctrl-d (down), Ctrl-y (back one line), Ctrl-e (forward one line), Ctrl-b (backward page) and Ctrl-f (forward page).
  • Command History
    By pressing Ctrl-p several times, you can call previous commands up to the Swat prompt. If you go past the command that you want, use Ctrl-n to go forward in the history.
    The `!' character followed by a number repeats that command in the command history. (The standard Swat prompt includes a command number which may be used for this.) e.g. !184 will execute the 184th command of this session.
    The `!' character followed by a string will repeat the most recent command whose beginning is the same as the passed string. That is !b might invoke brk list if that was the most recent command that began with "b".
    Typing "!!" will repeat the previous command; "!$" is the last argument of the previous command.
  • Command Correction
    To repeat the previous command, but changing a piece of it, use the ^ command. This comes in handy when you've made a typo trying to enter the previous command.

Swat Display 3-14 Command Correction Using ^

(geos:0) 185 => wurds
Error: invoked "wurds", which isn't a valid command name
(geos:0) 186 => ^u^o
words
Addr:	 	 +0   +2   +4   +6   +8   +a   +c   +e
4b4bh: e800 01b1 0e00 60f6 0016 9800 6e02 a900
(geos:0) 187 => ddwords
Error: invoked "ddwords", which isn't a valid command name
(geos:0) 188 => ^d
dwords
Addr:  +0       +4       +8       +c
4b59h: 1d0aa900 001c400d 294bd000 6c0a8000
  • Address History
    Swat has an address history which is composed of tokens for address expressions previously used by commands such as print or pobj . The elements in the history can be accessed by typing @<number> where the number argument is the number of the item in the history. These elements can replace a full address expression (except constants) and are often used when traversing through fields of a previously printed structure. The default history keeps track of the last 50 items. (See The Address History .)

Swat Display 3-15 The Address History

(geos:0) 8 => gentree -i
GenPrimaryClass (@1, ^l44a0h:001eh) "MESS #1"
 GenViewClass (@2, ^l44a0h:0020h)
 GenValueClass (@3, ^l44a0h:0026h)
 GenValueClass (@4, ^l44a0h:0028h)

(geos:0) 9 => pinst @3
class = ui::dgroup::GenValueClass
master part: Gen_offset(53) -- ui::GenValueInstance
@5: {ui::GenValueInstance (^h17568:1170)+53} = {
 GenInstance GenValue_metaInstance = {
 MetaBase Gen_metaInstance = {
 ClassStruct _far *MB_class = 3573h:1867h (motif::dgroup::OLScrollbarClass)
 }
 LinkPart GI_link = {
 void _optr LP_next = ^l44a0h:0028h (ui::dgroup::GenValueClass@6244h:02deh)
 }
 CompPart GI_comp = {
 void _optr CP_firstChild = null
 }
 void _lptr GI_visMoniker = null
 KeyboardShortcut GI_kbdAccelerator = {
 KS_PHYSICAL = 0
 KS_ALT = 0
 KS_CTRL = 0
 KS_SHIFT = 0
 KS_CHAR_SET = 0
 KS_CHAR = C_NULL
 }
 GenAttrs GI_attrs = {}
 GenStates GI_states = {GS_USABLE, GS_ENABLED}
 }
 WWFixed GVLI_value = {0.000000}
 WWFixed GVLI_minimum = {0.000000}
 WWFixed GVLI_maximum = {0.007324}
 WWFixed GVLI_increment = {0.000229}
 GenValueStateFlags GVLI_stateFlags = {GVSF_INDETERMINATE}
 GenValueDisplayFormat GVLI_displayFormat = GVDF_INTEGER
 void _optr GVLI_destination = ^l44a0h:0020h (ui::dgroup::GenViewClass@6244h:01aah)
 word GVLI_applyMsg = 681ah
}
(geos:0) 10 =>
  • Abbreviations
    Another shortcut available in Swat is the abbreviation feature. Many commands can be specified by their first few characters up to and including the letter that makes them distinct from all other commands. For example, the pobject command can be specified pobj , pob , or even po , but not by just p because there are other commands (such as print ) beginning with the letter p . To get a list of all commands with a given prefix, type the prefix at the Swat prompt, then type Ctrl-D. To automatically complete a command name use the Escape key (if the prefix is unambiguous) or Ctrl-] to scroll through the list of possible command completions.
  • Initialization Files
    If there are certain Swat commands that always need to be executed when Swat is run, then they can be placed in an initialization file. (See An Initialization File srcwin 15 regwin save 500 patient-default mess1 run ) An initialization file contains a list of commands that will be executed just before the first prompt in Swat.
    The initialization file should be called SWAT.RC. Swat will look in the directory from which it was invoked for such a file. If it doesn't find one there, it will look for a file named SWAT.RC in a directory named in the HOME environment variable

Swat Display 3-16 An Initialization File

srcwin 15
regwin
save 500
patient-default mess1
run

This example shows a sample initialization file which sets up windows to display the source code and current register values, set the length of the save buffer to 500 lines, and continue running swat until the mess1 application has been loaded, at which point execution will automatically stop.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Swat Command Reference

 This document is a single-page version of a a multi-page document, suitable for easy printing.

Swat Reference

The descriptions of the Swat commands will follow the following notational conventions:

  • command (alternative1|alternative2|...|alternativeN)
    ()     The parentheses enclose a set of alternatives separated by a vertical line. For example, the expression quit (cont|leave) means that either quit cont or quit leave can be used.
  • command [optional_argument]
    []     The brackets enclose optional arguments to the command. For example, the command alias [<word[<command>]>] could have zero, one, or two arguments because the <command> and <word> arguments are optional. Another example would be the command objwalk [<addr>] , which may take zero arguments if it is meant to use the default address, and one argument if the user gives it a particular address to look at.
  • command <type_of_argument>
    < >     The angled brackets enclose the type of an argument rather than the actual string to be typed. For example, <addr> indicates an address expression and <argument> indicates some sort of argument, but (addr|type) means either the string addr or the string type .
  • * +
     An asterisk following any of the previous constructs indicates zero or more repetitions of the construct may be typed. An addition sign indicates one or more repetitions of the construct may be used. For example, unalias word * can be the unalias command by itself, or it can be followed by a list of words to be unaliased.

_print ...
addr-parse ...
alias ...
assign ...
bits ...
car ...
continue-patient ...
dbrk ...
debug ...
delassoc ...
drivewalk ...
elist ...
fatalerr_auto_explain ...
flagwin ...
fpstack ...
func ...
getenv ...
handsum ...
help ...
hwalk ...
imem ...
intr ...
keyboard ...
load ...
memsize ...
noStructEnum ...
objmessagebrk ...
patchin ...
pdisk ...
pgs ...
pinst ...
pobjmon ...
print-cell ...
printRegions ...
ptext ...
pvismon ...
read-reg ...
ret ...
scope ...
showcalls ...
smatch ...
stdpaths ...
stream ...
symbolCompletion ...
text-fixup ...
twalk ...
varwin ...
wclear ...

_print

 _print <expression>

Examples:

"_print ax-10"
print ax less 10 decimal.

Print the value of an expression.

The difference between this command and the "print" command is a subtle one: if one of the arguments contains square-brackets, the Tcl interpreter will attempt to evaluate the text between the brackets as a command before _print is given the argument. If the text between the brackets is intended to be an array index, the interpreter will generate an error before the Swat expression evaluator has a chance to decide whether the text is a nested Tcl command or an array index.

For this reason, this function is intended primarily for use by Tcl procedures, not by users.

See Also: print, addr-parse.

abort

abort [<frame-number>]
abort [<function>]

Examples:

"abort"
abort executing the current frame.
"abort 3"
abort executing up through the third frame.
"abort ObjMessage"
abort executing up through first ObjMessage.

Abort code execution up through a given frame or routine. By "abort", we me "do not execute". This can be quite dangerous, as semaphores may not be ungrabbed, blocks not unlocked, flags not cleared, etc., leaving the state of objects, and if executing system code, possibly the system itself in a bad state. This command should only be used when the only alternative is to to detach (i.e. in a fatal error) as a way to possibly prolong the usefulness of the debugging session.

  • If no argument is given, code through the current frame is aborted.
  • <frame num> are the numbers that appear at the left of the backtrace.

See Also: finish, backtrace.

abortframe

abortframe <frame-token>

Examples:

"abortframe $cur"
Abort all code execution through the frame whose token is in $cur.

Aborts code execution up through a particular stack frame. As no code is executed, the registers may be in a garbaged state.

  • The argument is a frame token, as returned by the "frame" command.
  • No FULLSTOP event is dispatched when the machine actually aborts executing in the given frame. The caller must dispatch it itself, using the "event" command. For information about FULLSTOP events, see the event Tcl command.
  • The command returns zero if the machine aborted executing in the given frame; non-zero if it was interrupted before that could happen.

addr-parse

addr-parse <addr> [<addr-only>]

Examples:

"addr-parse *ds:si"
Parse the address "*ds:si" into its handle, offset and data-type components. In this case, the data-type will be "nil".
"addr-parse ILLEGAL_HANDLE 0"
Figures the value for the enumerated constant "ILLEGAL_HANDLE". The handle for this non-address will be "value".

This command parses the address expression into its components, returning a list {<handle> <offset> <type> } as its value.

  • This will generate an error if there's an error parsing the <addr>
  • <handle> is the token for the handle in which the address resides, or "nil" if the address is absolute. This token can be given to the "handle" command for further processing.
  • <offset> is a decimal number and is the offset of the address within the block indicated by the <handle> token. If <handle> is "nil", this can be a 32-bit linear address.
  • <type> is a type token for the data at the given address, if any could be determined. For example the address "ds:bx" has no type, as it's just a memory reference, but "ds:bx.VDE_extraData" will have whatever type the structure field "VDE_extraData" possesses. This token can be passed to the "type" or "value" commands for further processing.
  • If the expression doesn't refer to data that can be fetched from the patient (e.g. "foo*3") <handle> will be returned as the string "value" instead of a normal handle token. <offset> is then a value-list for the resulting value, and <type> is the type description by means of which the value list can be interpreted.
  • The optional <addr-only> argument is zero or non-zero to indicate the willingness or unwillingness, respectively, of the caller to receive a value list in return. If <addr-only> is absent or non-zero, any expression that can only be expressed as a value will generate an error. The single exception to this is if the expression involves pointer arithmetic. For example "pself+1" normally would be returned as a value list for a far pointer, as the result cannot be fetched from the PC. When <addr-only> is absent or non-zero, "addr-parse" pretends the expression was "*(pself+1)", allowing simple specification of an address by the user for those commands that just address memory.
  • The <offset> element of the returned list is very useful when you want to allow the user to give you anything, be it a register or a number or an enumerated constant or whatever. You can pass the argument you were given to [index [addr-parse $arg] 1] and end up with an appropriate decimal number. Be sure to pass <addr-only> as 0, however, or else you'll generate an error.

See Also: value, handle, type.

addr-preprocess

 addr-preprocess <addr> <seg-var> <off-var>

Examples:

"addr-preprocess $args s o"
Parse the address expression in $args, storing the segment portion in $s and the offset portion in $o in the current scope.

Preprocesses an address expression into a form that is easier to manipulate and faster to reparse.

  • <seg-var> is the name of a variable in the caller's scope in which the segment of the address is stored. It should be treated as opaque, as it may or may not be numeric.
  • <off-var> is the name of a variable in the caller's scope in which the offset of the address is stored. This will always be numeric.
  • Returns the 3-list returned by addr-parse, in case you have a use for the type token stored in the list.

See Also: addr-parse.

addr-with-obj-flag

addr-with-obj-flag

Examples:

"var addr [addr-with-obj-flag $addr]"
If $addr is "-i", returns the address of the current implied grab.

This is a utility routine that can be used by any command that deals with objects where the user may reasonably want to operate on the leaf object of one of the hierarchies, or the windowed object under the mouse. It can be given one of a set of flags that indicate where to find the address of the object on which to operate.

  • Special values accepted for <address>:
    • -a
    the current patient's application object
    -i
    the current "implied grab": the windowed object over which the mouse is currently located
    -f
    the leaf of the keyboard-focus hierarchy
    -t
    the leaf of the target hierarchy
    -m
    the leaf of the model hierarchy
    -c
    the content for the view over which the mouse is currently located
    -kg
    the leaf of the keyboard-grab hierarchy
    -mg
    the leaf of the mouse-grab hierarchy
  • If <address> is empty, this will return the contents of the local variable "oself" within the current frame, if it has one, or *ds:si
  • If <address> isn't one of the above, this just returns <address>.

See Also: impliedgrab, content, focusobj, targetobj, modelobj, keyboardobj, mouseobj.

alias

alias [<name> [<body>]]

Examples:

"alias p print"
Execute "print" when the user types the command "p". Any arguments to "p" get passed to "print" in the order they were given.
"alias while {for {} $1 {} $2}"
Executes an appropriate "for" loop when the "while" command is executed with its two arguments: a test expression and a body of commands to execute.
"alias"
Prints all the defined aliases.
"alias while"
Prints what the "while" command is aliased to.

This is a short-cut to allow you to make commands you commonly type easier to use, and to define simple new commands quickly.

  • If you give no arguments the current aliases are all displayed.
  • If you give a single argument, the name of an existing alias, the command that will be executed when you use the alias is printed.
  • The <body> string is usually in curly braces, as it usually involves whitespace and can contain newlines for the longer aliases.
  • You can use the pseudo-variables $1, $2, etc. in the <body> to represent the 1st, 2nd, etc. argument given when the alias is invoked. They are pseudo-variables as the "var" command will not operate on them, nor are they available to any procedure invoked by the alias.
  • You can also interpolate a range of the arguments using $<start>-<end>. If you do not give an <end>, then the arguments from <start> to the last one will be interpolated.
  • $* will interpolate all the arguments.
  • $# will interpolate the actual number of arguments.
  • If you do not use any of these pseudo-variables, all the arguments given to the alias will be appended to the <body>.
  • Interpolation of the values for these pseudo-variables occurs regardless of braces in the <body>.
  • It is an error to specify an argument number when there are fewer than that many arguments given to the alias.

See Also: unalias.

alignFields

var alignFields [(0|1)]

Examples:

"var alignFields 1"
Sets the "print" command to align the values for all the fields of a given structure.

Determines whether structure-field values follow immediately after the field name or if all values are indented to the same level. The "print" command and other display commands use this variable when formatting their output.

  • Having all values indented to the same level makes it easier for some people to locate a particular field in a structure. It is not without cost, however, in that Swat must determine the length of the longest field name before it can print anything.
  • The default value for this variable is zero.

See Also: print.

antifreeze

antifreeze <patient>
antifreeze :<n>
antifreeze <patient>:<n>
antifreeze <id>

Examples:

"antifreeze term"
Promotes the application thread for "term" to be the "most-runnable"
"antifreeze :1"
Does likewise for thread #1 of the current patient
"antifreeze 16c0h"
Does likewise the thread whose handle is 16c0h
"antifreeze"
Promotes the current thread to be the "most-runnable."

See Also: freeze.

antithaw

antithaw <patient>
antithaw :<n>
antithaw <patient>:<n>
antithaw <id>

Examples:

"antithaw term"
Allows the application thread for "term" to run normally.
"antithaw :1"
Allows thread #1 of the current patient to run normally.
"antithaw 16c0h"
Allow the thread whose handle is 16c0h to run normally.

See Also: thaw.

appobj

   appobj [<patient>]

Examples:

"pobj [appobj draw]"
prints the GenApplication object for draw.
"pobj [appobj]"
prints the GenApplication object for the current application (equivalent to "pobj -a").

Returns the address of the GenApplication object for the given patient, or the current one if you give no patient.

See Also: impliedgrab.

apropos

apropos [<string>]

Examples:

"apropos vis"
Find all commands related to vis
"apropos text"
Find all commands related to text

Search the list of commands and command help for all entries containing <string>. Lists each command and its synopsis.

<string> may actually be a pattern, as described in the help for the "string" command (under "string match"). It automatically has a leading and following * tacked onto it to cause it to match anywhere within a help string.

See Also: help.

aset

aset <array-name> <index> <value>

Examples:

"aset foo $i $n"
Sets the $i'th element (counting from zero) of the value stored in the variable foo to $n.

Allows you to treat a list stored in a variable as an array, setting arbitrary elements of that array to arbitrary values.

  • <array-name> is the name of the variable, not the value of the variable to be altered.
  • This command returns nothing.
  • The index must be within the bounds of the current value for the variable. If it is out of bounds, aset will generate an error.

See Also: index.

assoc

assoc <list> <key>

Examples:

"assoc $classes GenPrimaryClass"
Examines the sublists of $classes and returns the first one whose first element is the string GenPrimaryClass.

Searches an associative list to find an element with a particular key. The list is itself made up of lists, each of whose first element is a key.

  • A typical associative list is made of key/value pairs, like this:
    {{<key> <value>} {<key> <value>} ...}
  • If an element is found whose <key> matches the passed <key>, the entire element is returned as the result. If no <key> matches, nil is returned.

See Also: car, cdr, range, list, delassoc.

assign

assign <addr> <value>

Examples:

"assign ip ip+2"
Add 2 to the value of IP in the current frame.
"assign {word ds:si} 63h"
Store 63h in the word at ds:si

Performs an assignment to a patient variable or register (but not to an element of the value history). The first argument is the variable or register to be assigned and the second argument is the value to assign to it (which may be a regular address expression). If the first expression doesn't indicate a type, "word" is assumed. Only byte , word or dword types are supported.

  • When assigning to an sptr, the value assigned will be the segment of the block indicated by the <value>, unless <value> is an absolute address (or just a number), in which case the low 16 bits of the offset will be used instead.
  • Similar behavior occurs when assigning to an fptr, except if the <value> is an absolute address, in which case the linear address in the offset portion of the <value> will be decomposed into a segment and an offset.

See Also: imem, value.

att

att [<args>]

Examples:

"att"
attach Swat to GEOS.

Attach Swat to GEOS.

The args argument can be one of the following:

-s
reboot GEOS with error checking, attach, and stop
-sn
reboot GEOS without error checking, attach, and stop
-f
restart GEOS with error checking and attach after a pause
-r
restart GEOS with error checking and attach
-rn
restart GEOS without error checking and attach

See Also: detach, quit.

attach

attach [<boot>]

Examples:

"attach"
attach to the target PC

Attach swat to the target PC.

  • The boot argument is "-b" to bootstrap and "+b" to not. Normally, Swat will try to read symbolic information about all running geodes; bootstrapping specifies that Swat should only read symbolic information for these geodes when it must.
  • If you give no <boot> argument, swat will use the most-recent one.
  • By default, swat will locate the symbols for all geodes and threads active on the PC when it attaches.
  • If any geode has changed since you detached from the PC, its symbols are re-read.

See Also: att, detach, quit.

autoload

autoload <function> <flags> <file> [<class> <docstring>]

Examples:

"autoload cycles 1 timing"
load the file "timing.tcl" when the cycles command is first executed. The user must type the command completely.
"autoload print 2 print"
load the file "print.tcl" when the print command is first executed. The user may abbreviate the command and the Tcl interpreter will not evaluate its arguments.

This command allows the first invocation of a command to automatically force the transparent reading of a file of Tcl commands.

  • autoload takes 3 or 5 arguments: the command, an integer with bit flags telling how the interpreter should invoke the command, the file that should be read to define the command (this may be absolute or on load-path) and an optional help class and string for the command.
  • The help class and string need only be given if the file to be loaded isn't part of the system library (doesn't have its help strings extracted when Swat is built).
  • The <flags> argument has the following bit-flags:
0
User must type the command's name exactly. The command will be defined by "defsubr" or "defdsubr" when <file> is loaded.
1
The interpreter will not evaluate arguments passed to the command. All arguments will be merged into a single string and passed to the command as one argument. The command will use the special "noeval" argument when it is defined.

See Also: defsubr, defcommand, proc.

backtrace

backtrace [-r<reg>* ][<frames to list>]

Examples:

"backtrace"
print all the frames in the patient
"backtrace -rax"
print all the frames and the contents of AX in each one.
"where 5"
print the last five frames
"w 5"
print the last five frames

Print all the active stack frames for the patient.

  • The <frames to list> argument is the number of frames to print. If not specified, then all are printed.
  • If a numeric argument is not passed to backtrace then it attempts to display method calls in the form:
    •         MSG_NAME(cx, dx, bp) => className (^l####h:####h)

    Here <cx>, <dx>, and <bp> are the values passed in these registers. <className> is the name of the class which handled the message. ^l####h:####h is the address of the object (block, chunk handle) handling the message.

  • If a numeric argument is passed to backtrace then the attempt to decode the message is not done and the single line above expands into:
    • 	 far ProcCallModuleRoutine(), geodesResource.asm:476
     near ObjCallMethodTable(), objectClass.asm:1224

    This is generally less useful, but sometimes it's what you need.

See Also: up, down, func.

bin

bin <number> [<field-width>]

Examples:

"bin 5"
returns "0101" (five in binary)
"bin 10h"
returns "0001 0000"
"bin 10h 3"
returns "0000 0001 0000"

Prints numbers in binary.

  • The field width is in nibbles.
  • The default field width is the minium number of nibbles to display the number.

bindings

bindings

Shows all current key bindings

bind-key

bind-key <ascii_value> <function>

Examples:

"bind-key \321 scroll_srcwin_down"
Binds scroll-down key to the scroll_srcwin_down Tcl routine.

Binds an ASCII value to a function.

See Also: alias, unbind-key.

bits

bits [<address>] [<length>] [<type>]

Examples:

"bits"
lists 16 bytes worth of bits at ds:si
"bits ds:di 8"
lists 8 bytes worth of bits at ds:di
"bits ds:di 24 w"
lists 24 words worth of bits at ds:di

Examine memory as a dump of bits in sets of 8, 16, or 32 (byte,word, or dword).

  • The address argument is the address to examine. If not specified, the address after the last examined memory location is used. If no address has been examined then ds:si is used for the address.
  • The length argument is the number of units to examine. It defaults to 16.
  • They type argument is bytes, words or dwords. It defaults to bytes.
  • Pressing return after this command continues the list.

break-taken

 break-taken [<flag>]

Examples:

"break-taken"
Returns 1 if the machine stopped because of a breakpoint.
"break-taken 0"
Specify that no breakpoint was actually taken to stop the machine.

Obscure. This is used to determine if the machine stopped because a breakpoint was hit and taken.

Setting the break-taken flag is a rather obscure operation. It is useful primarily in complex commands that single-step the machine until a particular address is reached, or a breakpoint is taken when a breakpoint must be used to skip over a procedure call, or condense multiple iterations of an instruction with a REP prefix into 1. For an example of this use, refer to the "cycles" command.

See Also: brk, irq.

brkload

brkload [<handle>]

Examples:

"brkload Interface"
Stop the machine when the Interface resource is loaded or swapped in.
"brkload bx"
Stop the machine when the resource whose handle ID is in BX is loaded or swapped in.
"brkload"
Stop watching for the previously-specified resource to be loaded.

Stop the machine when a particular resource is loaded into memory.

  • Only one brkload may be active at a time; registering a second one automatically unregisters the first.
  • If you give no <handle> argument, the previously-set brkload will be unregistered.

See Also: handle.

byteAsChar

var byteAsChar [(0|1)]

Examples:

"var byteAsChar 1"
Print byte variables as characters.

Determines how unsigned character variables are printed: if set non-zero, they are displayed as characters, else they are treated as unsigned integers.

  • If $byteAsChar is 0, $intFormat is used.
  • The default value for this variable is 0.

bytes

bytes [<address>] [<length>]

Examples:

"bytes"
lists 16 bytes at DS:SI
"bytes ds:di 32"
lists 32 bytes at DS:SI

Examine memory as a dump of bytes and characters.

  • The <address> argument is the address to examine. If not specified, the address after the last examined memory location is used. If no address has been examined then DS:SI is used for the address.
  • The <length> argument is the number of bytes to examine. It defaults to 16.
  • Pressing <Return> after this command continues the list.
  • Characters which are not typical ASCII values are displayed as a period.

See Also: words, dwords, imem, assign.

call

 call <function> [<function args>]

Examples: "call MyFunc"
"call MyDraw ax 1 bx 1 cx 10h dx 10h"
"call FindArea box.bottom 5 box.right 5 push box"'

Call a function in the current thread.

  • The <function> argument is the function to call. If it is a NEAR function, the thread must already be executing in the function's segment.
  • The function arguments are in pairs <variable/register> <value>. These pairs are passed to the "assign" command. As a special case, if the variable is "push", the value (a word) is pushed onto the stack and is popped when the call finishes (if it completes successfully).
  • All current registers are preserved and restored when the call is complete. Variables are not.
  • Once the call has completed, you are left in a sub-interpreter to examine the state of the machine. Type "break" to get back to the top level.
  • If the machine stops for any other reason than the call's completion, the saved register state is discarded and you are left wherever the machine stopped. You will not be able to get a stack trace above the called function, but if the call eventually completes, and no registers have actually been modified, things will get back on track.
  • You may not call a function from a thread that has retreated into the kernel. This function also will not allow you to call ThreadExit() . Use the "exit" function to do that.

See Also: assign, call-patient, patch.

call-patient

call-patient <function> ((<reg>|push) <value>)*

Examples:

"call-patient MemLock bx $h"
Locks down the block whose handle ID is in $h.

This is a utility routine, not intended for use from the command line, that will call a routine in the PC after setting registers to or pushing certain values.

  • Returns non-zero if the call completed successfully.
  • If the call is successful, the registers reflect the state of the machine upon return from the called routine. The previous machine state is preserved and can be retrieved, by invoking restore-state, or thrown away, by invoking discard-state. The caller must invoke one of these to clean up.
  • Arguments after <function> are as for "call".
  • If the called routine is in movable memory, this will lock the containing block down before issuing the call, as you'd expect.
  • Calling anything that makes message calls while on the geos:0 thread is a hazardous undertaking at best.

See Also: call.

car

car <list>

Examples:

"car $args"
Returns the first element of $args.

Returns the first element of a list.

This is a lisp-ism for those most comfortable with that language. It can be more-efficiently implemented by saying [index <list> 0]

See Also: cdr.

cdr

cdr <list>

Examples:

"cdr $args"
Returns the remaining arguments yet to be processed.

Returns all but the first element of a list.

This is a lisp-ism for those most comfortable with that language. It can be more-efficiently implemented by saying [range <list> 1 end]

See Also: car.

classes

classes [<patient>]

Examples:

"classes "
Print list of classes in current patient.
"classes myapp"
Print list of classes in myapp patient.

Prints list of classes defined by the given patient.

Remember that "brk" will take address arguments of the form <class>::<message>, so you can use this function and set a breakpoint using "brk MyTextClass:: MSG_MY_TEXT_MESSAGE ". If you need a breakpoint that's limited to one object, use objbrk instead.

clrcc

clrcc <flag> [<value>]

Examples:

"clrcc c"
clear the carry flag

Clear a flag in the target computer.

The first argument is the first letter of the flag to clear. The following is a list of the flags:
t trap
i interrupt enable
d direction
o overflow
s sign
z zero
a auxiliary carry
p parity
c carry

See Also: setcc, compcc, getcc.

columns

columns

Examples:

"columns"
Return the number of columns on the screen.

Retrieves the width of the screen, if known, to allow various commands (most notably "print") to size their output accordingly.

compcc

compcc <flag>

Examples:

"compcc c"
complement the carry flag

Complement a flag in the target computer.

The first argument is the first letter of the flag to complement. The following is a list of the flags:
t trap
i interrupt enable
d direction
o overflow
s sign
z zero
a auxiliary carry
p parity
c carry
This command is handy to insert in a patch to flip a flag bit.

See Also: setcc, clrcc.

completion

completion <list-of-names>

Examples:

"completion {martial marital}"
Returns "mar," the common prefix.

Figures the common prefix from a set of strings. Used for the various forms of completion supported by top-level-read.

condenseSmall

var condenseSmall [(0|1)]

Examples:

"var condenseSpecial 0"
Force even small structures to be printed one field per line.

Controls whether "print" attempts to condense the output by printing small (< 4 bytes) structures (which are usually records in assembly language) as a list of <name> = <int>, where <name> is the field name and <int> is a signed integer.

The default value of this variable is one.

See Also: print, condenseSpecial.

condenseSpecial

var condenseSpecial [(0|1)]

Examples:

"var condenseSpecial 0"
Turns off the special formatting of various types of structures by "print".

Controls the formatting of certain structures in more-intuitive ways than the bare structure fields.

  • The default value of this variable is 1.
  • The current list of structures treated specially are: Semaphore, Rectangle, Output Descriptor, TMatrix, BBFixed, WBFixed, WWFixed, DWFixed, WDFixed, DDFixed, FileDate, FileTime, FloatNum, SpecWinSizeSpec.

See Also: print, condenseSmall.

cont

cont

Examples:

"cont"
continue execution
"c"
continue execution

Continue GEOS.

  • If the global variable waitForPatient is non-zero, this command waits for the machine to stop again before it returns.

See Also: go, istep, step, next, detach, quit.

content

content

Examples:

"vistree [content]"
print the visual tree of the content of the view under the mouse.

Print the address of the content under the view with the current implied grab.

  • This command is normally used with vistree to get the visual tree of a content by placing the mouse on the content's view window and issuing the command in the example.
  • If the pointer is not over a GenView object, this is the same as the "impliedgrab" command.

See Also: systemobj, gentree, impliedgrab.

continue-patient

continue-patient

Examples:

"continue-patient"
Allow the target machine to continue executing GEOS.

Tell the Swat stub to let the target machine continue where it left off.

This command does not wait for the machine to stop again before it returns; once the machine is running, you're free to do whatever you want, whether it's calling "wait" or examining memory periodically.

See Also: step-patient.

cup

cup <class>
cup <object>
cup <flags>

Examples:

"cup ui::GenDocumentControlClass"
Print class hierarchy of named class
"cup ^l2850h:0034h"
Print class hierarchy of object
"cup -f"
Print class hierarchy of focus object
"cup -p"
Print class hierarchy of process

Walks up the class hierarchy, starting at a given class, printing each class encountered. May be passed an object, in which case the class of the object will be used as a starting place.

current-level

current-level

Examples:

"var l [current-level]"
Store the current interpreter nesting level in $l.

Returns the number of invocations of "top-level" (i.e. the main command input loop) currently active.

  • This is currently used only to modify the command prompt to indicate the current nesting level.
  • The top-most command loop is level one.

See Also: top-level.

current-registers

current-registers

Examples:

"current-registers"
Returns a list of the current registers for the current thread.

Returns all the registers for the current thread as a list of decimal numbers.

  • The mapping from element number to register name is contained in the global variable "regnums", which is an assoc-list whose elements contain the name of the register, then the element number.
  • For your own consumption, the list is ordered ax, cx, dx, bx, sp, bp, si, di, es, cs, ss, ds, ip, flags. You should use the "regnums" variable when programming, however, as this may change at some point (e.g. to accommodate the additional registers in the 386).

cvtrecord

cvtrecord <type> <number>

Examples:

"cvtrecord [symbol find type HeapFlags] 36"
Return a value list for the number 36 cast to a HeapFlags record.

Creates a value list for a record from a number, for use in printing out the number as a particular record using fmtval.

  • <type> is a type token for a record (or a structure made up exclusively of bitfields).
  • <number> must be an actual number suitable for the "expr" command. It cannot be a register or variable or some such. Use "getvalue" to obtain an integer from such an expression.
  • Returns a value list suitable for "value store" or for "fmtval".

See Also: value, fmtval, expr, getvalue.

cycles

Count instruction cycles from now until the given address is reached. Prints out each instruction as it is executed, along with the cycles it took. If no address is given, executes until a breakpoint is hit. Takes the following (optional) flags:

-r
Print routines called, the total cycles for each routine, and a running total, not the cycles for each instruction.
-i
Same as -r, but indents to show calling level. Not recommended for counting cycles over deeply nested routines.
-I
Same as -i, except uses (#) to indicate call level
-f
Stop counting when this routine finishes
-n
Does not whine about interrupts being off
-x < routine >
Step over calls to <routine>
-x < routine >=< val >
Step over calls to <routine> and assume that the call takes <val> cycles for timing purposes

See Also: timingProcessor.

dbrk

dbrk <addr> <num> [<command>|default]
dbrk set <break> <num> [<command>|default]
dbrk list [<break>]
dbrk {clear,delete} <break>
dbrk cur <break> <num>
dbrk max <break> <num>
dbrk cmd <break> [<command>]
dbrk reset <break>
dbrk enable <break>
dbrk disable <break>

Examples:

"dbrk ObjMessage 30"
Break at the 30th call to ObjMessage
"dbrk set 5 30"
Break after hitting brk5 30 times.
"dbrk set brk3 10 default"
Break after hitting brk3 10 times; Each time brk3 is hit, perform the default command.
"dbrk cmd brk2 {echo foo}"
Echo a string whenever brk2 is hit.
"dbrk cmd brk2 default"
Use the default command for brk2.
"dbrk disable 2"
Stop counting brk2; it will now always be taken.

This command creates "delayed" breakpoints, creating breakpoints that don't activate until they've been hit a specified nuber of times.

  • break may be a number, or a full breakpoint token (brk<n>) num is a number; and command is a string that will be evaluated. If command may also be the string "default", in which case a default command will be used. The first argument to dbrk may be abbreviated; only the first 3 characters are significant.
  • Deleting a breakpoint with "brk del" removes the dbrk also.
  • With the exception of brk, all the commands that take <break> must be given a breakpoint that has been delayed. The "dbrk list" command will show these.
  • "dbrk list" will show how many times a breakpoint has been hit, the maximum number of times to skip the breakpoint, and the command to evaluate whenever the breakpoint is hit. If no breakpoint is passed, the status of all dbrks will be shown.
  • "dbrk clear" and "dbrk delete" are synonyms. These commands will cause the delayed breakpoint to become normal again, and clear out the global variables used to store the dbrk's state: brk N cur, brk N max, brk N cmd where N is a number.
  • "dbrk cur", "dbrk max", and "dbrk cmd" set the current count, the maximum count, and the command of the specified breakpoint respectively.
  • "dbrk reset break " is a quick way of saying "dbrk cur break 0"
  • "dbrk enable" and "dbrk disable" are used to enable and disable a dbrk. Disabled dbrks will always be taken, and their counter will not be incremented.
  • Since this command is just a front-end to brk cmd, you can use dbrk and brk cond to create more complex breakpoints; for instance, you can "mwatch MSG_VIS_DRAW ", then "dbrk set brk 30 print-method" to break after MSG_VIS_DRAW has been received 30 times.

dcache

dcache bsize <blockSize>
dcache length <numBlocks>
dcache stats
dcache params
dcache (on|off)

Examples:

"dcache bsize 16"
Set the number of bytes fetched at a time to 16.
"dcache length 1024"
Allow 1024 blocks of the current block size to be in the cache at a time.
"dcache off"
Disables the Swat data cache.

Controls the cache Swat uses to hold data read from the target machine while the machine is stopped.

  • Data written while the machine is stopped actually get written to the cache, not the PC, and the modified blocks are written when the machine is continued.
  • The default cache block size is 32 bytes, with a default cache length of 64 blocks.
  • It is a very rare thing to have to turn the data cache off. You might need to do this while examining the changing registers of a memory-mapped I/O device.
  • The <blockSize> must be a power of 2 and no more than 128.
  • Changing the block size causes all cached blocks to be flushed (any modified cache blocks are written to the PC).
  • Changing the cache length will only flush blocks if there are more blocks currently in the cache than are allowed by the new length.
  • The "dcache stats" command prints statistics giving some indication of the efficacy of the data cache. It does not return anything.
  • The "dcache params" command returns a list {<blockSize> <numBlocks>} giving the current parameters of the data cache. There are some operations where you might want to adjust the size of the cache either up or down, but need to reset the parameters when the operation completes. This is what you need to do this.

See Also: cache.

dcall

dcall [<args>]

Examples:

"dcall Dispatch"
Display when the routine Dispatch is called
"dcall none"
stop displaying all routines

Display calls to a routine.

  • The <args> argument normally is the name of the routine to monitor. Whenever a call is made to the routine its name is displayed.
  • If `none' or no argument is passed, then all the routines will stop displaying.
  • Dcall uses breakpoints to display routine names. By looking at the list of breakpoints you can see which routines display their names and you can stop them individually by disabling or deleting their breakpoints.

See Also: showcalls, mwatch.

debug

debug <proc-name>*

Examples:

"debug"
Enter the Tcl debugger immediately.
"debug fooproc"
Enter the Tcl debuffer when the interpreter is about to execute the command "fooproc".

This command is used when debugging Tcl commands. It sets a breakpoint at the start of any Tcl command. Also serves as a breakpoint in the middle of a Tcl procedure, if executed with no argument.

  • The breakpoint for <proc-name> can be removed using the "undebug" command.
  • <proc-name> need not be a Tcl procedure. Setting a breakpoint on a built-in command is not for the faint-of-heart, however, as there are some commands used by the Tcl debugger itself. Setting a breakpoint on such a command will cause instant death.

See Also: undebug.

debugger

var debugger [<command-name>]

Name of the command when things go wrong. The function is passed two arguments: a condition and the current result string from the interpreter. The condition is "enter" if entering a command whose debug flag is set, "exit" if returning from a frame whose debug flag is set, "error" if an error occurred and the "debugOnError" variable is non-zero, "quit" if quit (^ is typed and the "debugOnReset" variable is non-zero, or "other" for some other cause (e.g. "debug" being invoked from within a function).

debugOnError

var debugOnError [(0|1)]

Examples:

"var debugOnError 1"
Turn on debugging when there's a Tcl error.

Enter debug mode when Swat encounters a Tcl error.

  • The 0|1 simply is a false|true to stop and debug upon encountering an error in a Tcl command.
  • If an error is caught with the catch command, Swat will not enter debug mode.

See Also: debugger.

defcmd

defcmd <name> <args> <help-class> <help-string> <body>

Examples: Look at almost any .tcl file in the system library for an example; a complete example set would be too large to give here.

This creates a new Tcl procedure with on-line help whose name the user may abbreviate when invoking.

  • <help-class> is a Tcl list of places in which to store the <help-string>, with the levels in the help tree separated by periods. The leaf node for each path is added by this command and is <name>, so a command "foo" with the <help-class> "prog.tcl" would have its <help-string> stored as "prog.tcl.foo."
  • Because the name you choose for a procedure defined in this manner can have an impact on the unique abbreviation for another command, you should use this sparingly.

See Also: defcommand, proc, help.

defcommand

defcommand <name> <args> <help-class> <help-string> <body>

Examples: Look at almost any .tcl file in the system library for an example.

This creates a new Tcl procedure with on-line help whose name must be given exactly when the user wishes to invoke it.

<help-class> is a Tcl list of places in which to store the <help-string>, with the levels in the help tree separated by periods. The leaf node for each path is added by this command and is <name>, so a command "foo" with the <help-class> "prog.tcl" would have its <help-string> stored as "prog.tcl.foo."

See Also: defcmd, proc, help.

defhelp

defhelp <topic> <help-class> <help-string>

Examples:

"defhelp breakpoint top {Commands relating to the setting of breakpoints}"
Sets the help for "breakpoint" in the "top" category to the given string.

This is used to define the help string for an internal node of the help tree (a node that is used in the path for some other real topic, such as a command or a variable).

  • This cannot override a string that resides in the /pcgeos/tcl/doc file.
  • You only really need this if you have defined your own help-topic category.
  • <help-class> is a Tcl list of places in which to store the <help-string>, with the levels in the help tree separated by periods. The leaf node for each path is added by this command and is <name>, so a command "foo" with the <help-class> "prog.tcl" would have its <help-string> stored as "prog.tcl.foo."

See Also: help.

defvar

defvar <name> <value> [<help-class> <help-string>]

Examples:

"defvar printRegions 0"
Define "printRegions" as a global variable and give it the value zero, if it didn't have a value already.

This command is used in .tcl files to define a global variable and give it an initial value, should the variable not have been defined before.

  • If the variable is one the user may want to change, give it on-line help using the <help-class> and <help-string> arguments.
  • <help-class> is a Tcl list of places in which to store the <help-string>, with the levels in the help tree separated by periods. The leaf node for each path is added by this command and is <name>, so a command "foo" with the <help-class> "variable.output" would have its <help-string> stored as "variable.output.foo."

See Also: var, help.

delassoc

delassoc <list> <key> [<foundvar> [<elvar>]]

Examples:

"delassoc $val murphy"
Returns $val without the sublist whose first element is the string "murphy."

Deletes an entry from an associative list.

  • <foundvar>, if given, is the name of a variable in the caller's scope that is to be set non-zero if an element in <list> was found whose <key> matched the given one. If no such element was found (and therefore deleted), the variable is set zero.
  • <elvar>, if given, is the name of a variable in the caller's scope that receives the element that was deleted from the list. If no element was deleted, the variable remains untouched.

See Also: assoc.

detach

detach [<options>]

Examples:

"detach cont"
continue GEOS and quit swat

Detach swat from the PC.

  • The <option> argument may be one of the following: continue : continue GEOS and detach swat; leave : keep GEOS stopped and detach swat. Anything else causes swat to just detach.

See Also: attach, quit.

dirs

dirs

Prints the directory stack for the current thread.

See Also: pwd, stdpaths.

discard-state

discard-state

Examples:

"discard-state"
Throw away the values for all the thread's registers as saved by the most recent call to save-state .

Throw away the state saved by the most-recent save-state command.

This is usually only used in response to an error that makes it pointless to return to the point where the save-state was performed.

See Also: save-state, restore-state.

diskwalk

diskwalk <drive>

Examples:

"diskwalk F"
Prints the disks registered in drive F.
"diskwalk"
Prints all the disks registered with the system.

Prints out the information on registered disks.

The Flags column is a string of single-character flags with the following meanings:

w
The disk is writable.
V
The disk is always valid, i.e. it's not removable.
S
The disk is stale. This is set if the drive for the disk has been deleted.
u
The disk is unnamed, so the system has made up a name for it.

See Also: drivewalk.

display

display <lines> <command>
display list
display del <num>

Examples:

"display list"
list all the commands displayed
"display 1 {piv Vis VCNI_viewHeight}"
always display the view height
"display del 2"
delete the second display command

Manipulate the display at the bottom of Swat's screen.

  • If you give a numeric <lines> argument, the next argument, <command>, is a standard Tcl command to execute each time the machine halts. The output of the command is directed to a window <lines> lines high, usually located at the bottom of the screen.
  • You can list all the active displays by giving "list" instead of a number as the first argument.
  • If the first argument is "del", you can give the number of a display to delete as the <num> argument. <num> comes either from the value this command returned when the display was created, or from the list of active displays shown by typing "display list".

See Also: wtop, wcreate.

dosMem

dosMem

Examples:

"dosMem"

Traverse DOS' chain of memory blocks, providing information about each.

down

down [<frame offset>]

Examples:

"down"
move the frame one frame down the stack
"down 4"
move the frame four frames down the stack

Move the frame down the stack.

  • The frame offset argument is the number of frames to move down the stack. If no argument is given then the current frame is moved down one frame.
  • This command may be repeated by pressing <Return>.

See Also: backtrace, up.

drivewalk

drivewalk

Examples:

"drivewalk"
Prints the table of drives known to the system.

Prints out all disk drives known to the system, along with their current status.

  • The Flags column is a string of single character flags with the following meanings:
    • L
    The drive is accessible to the local machine only, i.e. it's not visible over a network.
    R
    The drive is read-only.
    F
    Disks may be formatted in the drive.
    A
    The drive is actually an alias for a path on another drive.
    B
    The drive is busy, performing some extended operation, such as formatting or copying a disk.
    r
    The drive uses disks that may be removed by the user.
    n
    The drive is accessed over the network.
  • The Locks column can reflect one of three states:
    • none
    The drive isn't being accessed by any thread.
    Excl
    The drive is locked for exclusive access by a single thread.
    <num>
    The drive is locked for shared access for a particular disk, whose handle is the number. This is followed by the volume name of the disk, in square brackets.

See Also: diskwalk.

dumpstack

dumpstack [<address>] [<length>]

Examples:

"dumpstack"
dump the stack at SS:SP
"ds ds:si 10"
dump ten words starting at DS:SI

Dump the stack and perform some simple interpretation upon it.

  • The <address> argument is the address of the list of words to dump. This defaults to SS:SP.
  • The <length> argument is the number of words to dump. This defaults to 50.
  • This dumps the stack and tries to make symbolic sense of the values, in terms of handles, segments, and routines.
  • After doing a dumpstack, if you just hit return without entering a new command, by default you will see a continuation of the dumpstack.

See Also: backtrace.

dwordIsPtr

var dwordIsPtr [(0|1)]

Examples:

"var dwordIsPtr 1"
Tells "print" to print all double-word variables as if they were far pointers (segment:offset).

Controls whether dword (a.k.a. long) variables are printed as 32-bit unsigned integers or untyped far pointers.

  • For debugging C code, a value of 0 is more appropriate, while 1 is best for debugging assembly language.
  • The default value for this variable is 1.

See Also: intFormat, print.

dwords

dwords [<address>] [<length>]

Examples:

"dwords"
lists 4 double words at DS:SI
"dwords ds:di 8"
lists 8 double words at DS:DI

Examine memory as a dump of double words (32 bit hex numbers).

  • The <address> argument is the address to examine. If not specified, the address after the last examined memory location is used. If no address has be examined then DS:SI is used for the address.
  • The <length> argument is the number of dwords to examine. It defaults to 4.
  • Pressing <Return> after this command continues the list.

See Also: bytes, words, imem, assign.

ec

ec [<args>]

Examples:

"ec"
list the error checking turned on
"ec +vm"
add vmem file structure checking
"ec all"
turn on all error checking (slow)
"ec save none"
save the current error checking and then use none
"ec restore"
use the saved error checking flags

Get or set the error checking level active in the kernel.

  • The following arguments may occur in any combination:
    • < flag >
    turn on <flag>
    +< flag >
    turn on <flag>
    -< flag >
    turn off <flag>
    all
    turn on all error checking flags
    ALL
    turn on all error checking flags
    none
    turn off all error checking flags
    sum < handle >
    turn on checksum checking for the memory block with the given handle ("ec sum bx"). The current contents of the block will be summed and that sum regenerated and checked for changes at strategic points in the system (e.g. when a call between modules occurs).
    - sum
    turn off checksum checking
    save
    save the current error checking
    restore
    restore the saved error checking flags where <flag> may be one of the following:
    analVM
    perform over-aggressive checking of vmem files
    graphics
    graphics checking
    heapFree
    heap free block checking
    lmemFree
    lmem free area checking
    lmemInternal
    internal lmem error checking
    lmemObject
    lmem object checking
    normal
    normal error checking
    region
    region checking segment extensive
    segment
    adds register checking to Swat. If this flag is turned on, Swat will check whether ds and es are valid memory handle at various points in the debugging cycle by calling ECCheckSegment() .
    lmemMove
    force lmem blocks to move whenever possible
    unlockMove
    forces unlocked blocks to move whenever possible. This ensures that your code isn't referencing into an unstable block location. I.e., a block which may move, but usually doesn't , therefore causing non-reproducible (and quite frustrating) errors.
    vm
    vmem file structure checking
    vmemDiscard
    force vmem blocks to be discarded if possible
  • If there isn't an argument, `ec' reports the current error checking flags.
  • Each time GEOS is run the ec flags are cleared. The saved flags are preserved between sessions. The ec flags may be saved and then restored after restarting GEOS so that the flag settings are not lost when restarting GEOS.

See Also: why.

echo

echo [-n] <string>+

Examples:

"echo -n yes?"
Prints "yes?" without a newline.
"echo hi mom"
Prints "hi mom" followed by a newline.

Prints its arguments, separated by spaces.

If the first argument is "-n", no newline is printed after the arguments.

See Also: flush-output.

elist

elist [<patient>]

Examples:

"elist"
list the events for the current thread and patient
"elist ui"
list the events for the last thread of the ui patient
"elist :1"
list the events for the first thread of the current patient
"elist geos:2"
list the events for the second thread of the GEOS patient

Display all events pending for a patient.

The <patient> argument is of the form `patient:thread'. Each part of the patient name is optional, and if nothing is specified then the current patient is listed.

See Also: showcalls.

ensure-swat-attached

ensure-swat-attached

Examples:

"ensure-swat-attached"
Stop if Swat isn't attached to GEOS.

If Swat is not attached to GEOS, display an error and stop a command.

Use this command at the start of any other command that accesses the target PC. Doing so protects the user from the numerous warnings that can result from an attempt to read memory when not attached.

eqfind

eqfind [-p]

Examples:

"eqfind" list all event queues in the system.
"eqfind -p" list and print all event queues in the system.

Display all event queues in the system.

See Also: elist, eqlist, erfind.

eqlist

eqlist <queue handle> <name>

Examples:

"eqlist 8320 geos:2"
show the event list for geos:2

Display all events in a queue.

  • The queue handle argument is the handle to a queue.
  • The name argument is the name of the queue.

See Also: elist.

erfind

erfind [-p]

Examples:

"erfind" list all recorded event handles in the system.
"erfind -p" list and print all recorded event handles in the system.

Display all record event handles in the system. These are events that have been recorded but not necessarily sent anywhere, so they will not appear in the queue of any thread.

See Also: elist, eqlist, eqfind, pevent.

exit

exit <patient>

Examples:

"exit faxmon"
Causes the faxmon application to exit.

Sends messages required to make an application quit.

This command does nothing when you're stopped at FatalError , as it will wait until the machine is idle before attempting to send MSG_META_QUIT ; continuing from FatalError will cause the system to exit.

See Also: run.

exit-thread

exit-thread [<exit-code>]

Examples:

"exit-thread"
Exit the current thread, returning zero to its parent.
"exit-thread 1"
Exit the current thread, returning one to its parent.

Exit the current thread.

  • The exit code argument is the status to return to the current thread's parent, which defaults to zero.
  • Do not invoke this function for an event-driven thread; send it a MSG_META_DETACH instead.

See Also: quit.

explain

explain

Examples: "explain"

Print a more detailed description of why the system crashed, if possible.

  • This must be run from within the frame of the FatalError() function. Sometimes GEOS is not quite there. In this case, step an instruction or two and then try the "why" command again.
  • This simply looks up the enumerated constant for the error code in the AX register in the FatalErrors enumerated type defined by the geode that called FatalError() . For example, if a function in the kernel called FatalError() , AX would be looked up in geos::FatalErrors, while if a function in your application called FatalError() , this function would look it up in the FatalErrors type defined by your application. Each assembly application defines this enumerated type by virtue of having included ec.def .
  • This command also relies on programmers having explained their FatalErrors when defining them.
  • For certain fatal errors, additional information is provided by invoking the command <patient>::<error code name>, if it exists.

explode

explode <string> [<sep-set>]

Examples:

"explode $args"
Breaks the string stored in the variable "args" into a list of its individual letters.
"explode $file /"
Breaks the string stored in the variable "file" into a list of its components, using "/" as the boundary between components when performing the split.

Breaks a string into a list of its component letters, allowing them to be handled quickly via a foreach loop, or the map or mapconcat commands.

This is especially useful for parsing command switches.

See Also: foreach, index, range.

fatalerr_auto_explain

var fatalerr_auto_explain [(0|1)]

Examples:

"var fatalerr_auto_explain 0"
Turn off automatic generation of the explanation for any fatal-error hit.

Determines if the "why" command will automatically provide you with an explanation of any fatal error you encounter. If non-zero, they will be provided whenever FatalError is hit.

  • Explanations are loaded from <patient>.fei files stored in the system Tcl library directory when an error in <patient> is encountered.
  • You can also obtain an explanation of an error via the "explain" command.

fetch-optr

fetch-optr <handle> <offset>

Examples:

"fetch-optr $h $o.GI_comp.CP_firstChild"
Fetch the optr from the GI_comp.CP_firstChild field of the object at ^h$h:$o.

Extracts an optr from memory, coping with the data in the block that holds the optr not having been relocated yet.

  • <offset> is an actual offset, not a chunk handle, while <handle> is a handle ID, not a handle token.
  • Returns a two-list {<handle> <chunk>}, where <handle> is the handle ID from the optr, and <chunk> is the chunk handle (low word) from the optr.
  • We decide whether to relocate the optr ourselves based on the LMF_RELOCATED bit in the LMBH_flags field of the block's header. There are times, e.g. during the call to MSG_META_RELOCATE for an object, when this bit doesn't accurately reflect the state of the class pointer and we will return an error when we should not.

fhandle

fhandle <handle id>

Examples: "fhandle 3290h"

Print out a file handle.

  • The <handle id> argument is the handle number. File handles are listed in the first column of the `fwalk' command.

See Also: fwalk.

field

field <list> <field name>

Examples:

"field [value fetch ds:si MyBox] topLeft"
return the offset of the topLeft field in MyBox

Return the value for the field's offset in the structure.

  • The <list> argument is a structure-value list from the "value" command.
  • The <field name> argument is the field in the structure.

See Also: value, pobject, piv.

fieldwin

fieldwin

Print the address of the target machine's current top-most field window.

find

find [-ir] <string> [<filename>]

Examples:

"find FileRead"
Find next occurrence of string "FileRead" in currently viewed file
"find FI_foo poof.goc"
find first occurrence of string "FI_foo" in file poof.goc.
"find -ir myobject"
case-insensitive reverse search for most recent occurrence of string "myobject" in currently viewed file

Finds a string in a file and brings the line with that string to the middle of Swat's source window.

  • If no file argument is specified, find will find the next instance of the string in the already viewed file starting from the current file position.
  • There must already be a source window displayed for find to work.
  • Possible options to find are:
    • -r
    reverse search
    -i
    case insensitive search

find-opcode

find-opcode <addr> <byte>+

Locates the mnemonic for an opcode and decodesit. Accepts the address from which the opcode bytes were fetched, and one or more opcode bytes as arguments. Returns a list of data from the opcode descriptor:

{name length branch-type args modrm bRead bWritten inst}

length is the length of the instruction.

branch - type is one of:

1
none (flow passes to next instruction)
j
absolute jump
b
pc-relative jump (branch)
r
near return
R
far return
i
interrupt return
I
interrupt instruction

Any argument descriptor that doesn't match is to be taken as a literal. E.g. AX as a descriptor means AX is that operand.

modrm is the modrm byte for the opcode.

bRead is the number of bytes that may be read by the instruction, if one of its operands is in memory.

bWritten is the number of bytes that may be written by the instruction, if one of its operands is in memory.

inst is the decoded form of the instruction. If not enough bytes were given to decode the instruction, inst is returned as empty.

finish

finish [<frame num>]

Examples:

"finish"
finish executing the current frame
"finish 3"
finish executing up to the third frame

Finish the execution of a frame.

  • The <frame num> argument is the number of the frame to finish. If none is specified then the current frame is finished up. The number to use is the number which appears in a backtrace.
  • The machine continues to run until the frame above is reached.

See Also: backtrace.

finishframe

finishframe [<frame-token>]

Examples:

"finishframe $cur"
Run the machine to continue until it has returned from a particular stack frame.

Allows the machine to continue until it has returned from a particular stack frame.

  • No FULLSTOP event is dispatched when the machine actually finishes executing in the given frame. The caller must dispatch it itself, using the "event" command.
  • The command returns zero if the machine finished executing in the given frame; non-zero if it was interrupted before that could happen.
  • The argument is a frame token, as returned by the "frame" command.

See Also: event, frame, finish.

flagwin

flagwin [<on>|off]

Turns on or off a window providing a continuous display of the machine flags (e.g. zero, carry).

See Also: pflags.

flowobj

flowobj

Examples:

"pobject [flowobj]"
print out the flow object.

Prints out address of the uiFlowObj, which is the object which grabs the mouse.

This command is normally used with pobject to print out the object.

flush-output

flush-output

Examples:

"flush-output"
Forces pending output to be displayed.

Flushes any pending output (e.g. waiting for a newline) to the screen.

See Also: echo.

fmtoptr

fmtoptr <handle-id> <chunk>

Examples:

"fmtoptr 3160h o"
Prints a description of the object whose address is ^l3160h:0 (likely a thread/process).

Takes a global and a local handle and prints a description of the object described by that optr.

  • If the global handle is a thread or a process, the thread's name (process thread for a process handle) and the chunk handle (as an additional word of data for the message) are printed.
  • If the global handle is a queue handle, the queue handle and the chunk handle are printed, with a note that the thing's a queue.
  • If Swat can determine the object's class, the optr, full classname, and current far pointer are printed. In addition, if the chunk has its low bit set, the word "parent" is placed before the output, to denote that the optr likely came from a link and is the parent of the object containing the optr.

See Also: print.

fmtval

fmtval <value-list> <type-token> <indent> [<tail> [<one-line>]]

Examples:

"fmtval [value fetch foo] [symbol find type FooStruct] 0"
Prints the value of the variable foo, which is assumed to be of type FooStruct.

This is the primary means of producing nicely-formatted output of data in Swat. It is used by both the "print" and "_print" commands and is helpful if you want to print the value of a variable without entering anything into the value history.

  • <value-list> is the return value from "value fetch". You can, of course, construct one of these if you feel so inclined.
  • <type-token> is the token for the type-description used when fetching the value.
  • <indent> is the base indentation for all output. When "fmtval" calls itself recursively, it increases this by 4 for each recursive call.
  • <tail> is an optional parameter that exists solely for use in formatting nested arrays. It is a string to print after the entire value has been formatted. You will almost always omit it or pass the empty string.
  • <one-line> is another optional parameter used almost exclusively for recursive calls. It indicates if the value being formatted is expected to fit on a single line, and so "fmtval" should not force a newline to be output at the end of the value. The value should be 0 or 1.

See Also: print, _print, fmtoptr, threadname.

focus

focus [<object>]

Examples:

"focus"
print focus hierarchy from the system object down
"focus -i"
print focus hierarchy from implied grab down
"focus ^l4e10h:20h"
print focus hierarchy from ^l4e10h:20h down
"focus [content]"
print focus hierarchy from content under mouse.

Prints the focus hierarchy below an object.

  • If no argument is specified, the system object is used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • Remember that the object you start from may have the focus within its part of the hierarchy, but still not have the focus because something in a different part of the tree has it.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available.

    • This variable defaults to false.

See Also: target, model, mouse, keyboard, pobject.

focusobj

focusobj

Examples:

"focusobj"
print model hierarchy from system object down
"pobj [focusobj]"
Do a pobject on the focus object (equivalent to "pobj -f").

Returns the object with the focus.

See Also: focus, target, model, targetobj, modelobj.

fonts

fonts [<args>]

Examples:

"fonts"
summarize general font usage
"fonts -u"
list fonts currently in use

Print various font info.

  • The <args> argument may be any of the following:
    • -a
    list of fonts available
    -d
    list of font drivers available
    -u
    [<ID>] list of fonts currently in use. Optional font ID to match.
    -s
    summary of above information

    If no argument is specified the default is to show the summary.

  • When using other commands you probably need to pass them the handle in FIUE_dataHandle . When you don't have the font's handle ready, the best way is to use "fonts -u" to find the font at the right point size and then grab the handle from there.

See Also: pfont, pfontinfo, pusage, pfontinfo.

fpstack

fpstack

Examples:

"fpstack"
Prints out the hardware and software floating point stacks for the patient.

Prints out the hardware and software floating point stacks for the patient.

See Also: fpu-state.

fpu-state

fpustate [<mode>]

Examples:

"fpustate"
Print out the state of the coprocessor.
"fpustate w"
Dumps actual words of the numbers.

Prints out the current state of the coprocessor, if any.

See Also: fpstack.

frame

frame top
frame cur
frame get <ss> <sp> <cs> <ip>
frame next <frame>
frame prev <frame>
frame function [<frame>]
frame funcsym [<frame>]
frame scope [<frame>]
frame info [<frame>]
frame patient [<frame>]
frame register <regName> [<frame>]
frame set [<frame>]
frame setreg <regName> <value> [<frame>]
frame +<number>
frame -<number>
frame <number>

Examples:

"var f [frame top]"
Fetches the token for the frame at the top of the current thread's stack and stores it in the variable "f"
"var f [frame next $f]"
Fetches the token for the next frame up the stack (away from the top) from that whose token is in $f
"frame register ax $f"
Returns the value of the AX register in the given frame.
"frame 1"
Sets the current frame for the current thread to be the top-most one.

This command provides access to the stack-decoding functions of swat. Most of the subcommands deal with frame tokens, but a few also handle frame numbers, for the convenience of the user.

  • Subcommands may be abbreviated uniquely.
  • Stack decoding works by a heuristic method, rather than relying on the presence of a created stack frame pointed to by BP in each function. Because of this, it can occasionally get confused.
  • Frame tokens are valid only while the target machine is stopped and are invalidated when it is continued.
  • Each frame records the address on the stack where each register was most-recently pushed (i.e. by the frame closest to it on the way toward the top of the stack). Register pushes are looked for only at the start of a function in what can be considered the function prologue.
  • "frame register" and "frame setreg" allow you to get or set the value held in a register in the given frame. For "setreg", <value> is a standard address expression, only the offset of which is used to set the register.
  • "frame register" returns all registers but "pc" as a decimal number. "pc" is formatted as two hex numbers (each preceded by "0x") separated by a colon.
  • "frame info" prints out information on where the register values for "frame register" and "frame setreg" are coming from/going to for the given or currently-selected frame. Because of the speed that can be gained by only pushing registers when you absolutely have to, there are many functions in GEOS that do not push the registers they save at their start, so Swat does not notice that they are actually saved. It is good to make sure a register value is coming from a reliable source before deciding your program has a bug simply because the value returned by "frame register" is invalid.
  • For any subcommand where the <frame> token is optional, the currently- selected frame will be used if you give no token.
  • "frame cur" returns the token for the currently-selected stack frame.
  • "frame set" is what sets the current frame, when set by a Tcl procedure.
  • "frame +<number>" selects the frame <number> frames up the stack (away from the top) from the current frame. "frame -<number>" goes the other way.
  • "frame <number>" selects the frame with the given number, where the top-most frame is considered frame number 1 and numbers count up from there.
  • "frame funcsym" returns the symbol token for the function active in the given (or current) frame. If no known function is active, you get "nil".
  • "frame scope" returns the full name of the scope that is active in the given (or current) frame. This will be different from the function if, for example, one is in the middle of an "if" that contains variables that are local to it only.
  • "frame function" returns the name of the function active in the given (or current) frame. If no known function is active, you get the CS:IP for the frame, formatted as two hex numbers separated by a colon.
  • "frame patient" returns the token for the patient that owns the function in which the frame is executing.

See Also: addr-parse, switch.

framewin

framewin [del]

Examples:

"framewin"
Creates a single-line window to display info about the current stack frame.
"framewin del"
Deletes the window created by a previous "framewin".

Creates a window in which the current stack frame is always displayed.

  • Only one frame window can be active at a time.

See Also: display, regwin, srcwin.

freeze

freeze [<patient>]
freeze :<n>
freeze <patient>:<n>
freeze <id>

Examples:

"freeze"
Freezes the current thread.
"freeze term"
Freezes the application thread for "term"
"freeze :1"
Freezes thread #1 of the current patient
"freeze 16c0h"
Freezes the thread whose handle is 16c0h.

Freezing a thread prevents a thread from running unless it's the only thread that's runnable in the entire system.

  • A frozen thread is not dead in the water, as it will still run if nothing else is runnable.
  • Freezing a thread is most useful when debugging multi-threaded applications where a bug appears to be caused by a timing problem or race condition between the two threads. Freezing one of the threads ensures a consistent timing relationship between the two threads and allows the bug to be reproduced much more easily.
  • The freezing of a thread is accomplished by setting its base and current priorities to as high a number as possible (255) thereby making the thread the least-favored thread in the system. The previous priority can be restored using the "thaw" command.

See Also: thaw.

fullscreen

fullscreen

Examples:

"fullscreen"

Prints the full screen hierarchy from the system object down.

func

func [<func name>]

Examples:

"func"
return the current function.
"func ObjMessage"
set the frame to the first frame for ObjMessage.

Get the current function or set the frame to the given function.

  • The <func name> argument is the name of a function in the stack frame of the current patient. The frame is set to the first occurrence of the function from the top of the stack.
  • If no <func name> argument is given then `func' returns the current function.

See Also: backtrace, up, down, finish.

fvardata

fvardata <token> [<address>]

Examples:

"fvardata ATTR_VIS_TEXT_STYLE_ARRAY *ds:si"

Locates and returns the value list for the data stored under the given token in the vardata of the given object.

  • If the data are found, returns a list {<token> <data>}, where <data> is a standard value list for the type of data associated with the specified token.
  • Returns an empty list if the object has no vardata entry of the given type.
  • If no <address> is given, the default is *ds:si.

fwalk

fwalk [<patient>]

Examples:

"fwalk"
list all open files.
"fwalk geos"
list all open files owned by the GEOS patient.

Print the list of files open anywhere in the system.

  • The patient argument may be used to restrict the list to a particular patient. The patient may be specified either as the patient name or as the patient's handle.
  • fwalk differs from sysfiles and geosfiles in that it deals primarily with GEOS data structures.
  • The `Other' column shows if there is a VM handle bound to the file.
  • The letters in the `Flags' column mean the following:
    • RW
    deny RW
    R
    deny R
    W
    deny W
    N
    deny none
    rw
    access RW
    r
    access R
    w
    access RW
    O
    override, used to override normal exclusion normally used by FileEnum() to check out file headers.
    E
    exclusive, used to prevent override. This is used by swap.geo

See Also: fhandle, geosfiles, sysfiles.

gc

gc [(off|register|<extensive-heap-checking-flag>]

Implements a simple garbage collector to scavenge unreferenced symbols and types. If given an argument other than "off" or "register," it turns on extensive heap checking, which slows things down enormously but ensures the heap is in good shape. The "gc register" command can be use to register a type created by "type make"as something that is being used for an extended period at the Tcl level, preventing the thing from being garbage-collected.

gentree

gentree [<address>] [<instance field>]

Examples:

"gentree"
print the generic tree starting at *DS:SI
"gentree -i"
print the generic tree under the mouse
"gentree [systemobj]"
print the generic tree starting at the system's root
"gentree @23 GI_states"
print the generic tree with generic states
"gentree *uiSystemObj"
start the generic tree at the root of the system

Print a generic tree.

  • The <address> argument is the address to an object in the generic tree. This defaults to *DS:SI. The `-i' flag for an implied grab may be used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • The <instance field> argument is the offset to any instance data within the GenInstance which should be printed out.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available. This variable defaults to false.

See Also: gup, vistree, impliedgrab, systemobj, pobject.

geosfiles

geosfiles

Examples: "geosfiles"

Print out all the files for which I/O is currently pending in GEOS.

  • This looks at the same dos structure as sysfiles but this prints only those files also listed in GEOS' job file table.

See Also: sysfiles, sftwalk, fwalk.

geos-release

This variable contains the major number of the version of GEOS running on the target PC.

geowatch

geowatch [<object>]

Examples:

"geowatch *MyObj"
Display geometry calls that have reached the object MyObj
"geowatch"
Display geometry calls that have reached *ds:si (asm) or oself (goc)

This displays geometry calls that have reached a particular object. Only one object at a time can be watched in this way.

  • Two conditional breakpoints are used by this function (see cbrk). The tokens for these breakpoints are returned.
  • The special object flags may be used to specify object . For a list of these flags, see pobject.

See Also: objwatch, mwatch, cbrk, pobject.

get-address

Used by the various memory-access commands. Takes one argument, ADDR, being the address argument for the command. Typically, the command is declared as

[defcmd cmd {{addr nil}} ... ]

allowing the address to be unspecified. This function will return the given address if it was, else it will return the last-accessed address (stored in the global lastAddr variable as a 3-tuple from addr-parse) in the form of an address expression. If no address is recorded ( lastAddr is nil), the default-addr argument is used. If it is not specified then CS:IP will be used.

getcc

getcc <flag>

Examples:

"getcc c"
Get the carry flag.

Get a flag from the target machine.

  • The first argument is the first letter of the flag to get. The following is a list of the flags:
    • t
    trap
    i
    interrupt enable
    d
    direction
    o
    overflow
    s
    sign
    z
    zero
    a
    auxiliary carry
    p
    parity
    c
    carry
  • This command is handy to run with a breakpoint to stop if a flag is set.

See Also: setcc, clrcc, compcc.

getenv

getenv <NAME>

Examples:

"getenv PTTY"
Fetches the value of the host machine's PTTY environment variable.

Returns the value for a variable defined in Swat's environment.

If the variable isn't defined, this returns the empty string.

See Also: var, string.

get-key-binding

get-key-binding <char>

Examples:

"get-key-binding c"
Gets key binding for the character c.
"get-key-binding \045"
Gets key binding for the % key.

Gets key binding for given key.

See Also: alias, bind-key, unbind-key.

getvalue

getvalue <expr>

Examples:

"getvalue MSG_META_DETACH "
Returns the integer value of the symbol MSG_META_DETACH.

This is a front-end to the "addr-parse" command that allows you to easily obtain the integer value of any expression. It's most useful for converting something the user might have given you to a decimal integer for further processing.

If the expression you give does not evaluate to an address (whose offset will be returned) or an integer, the results of this function are undefined.

See Also: addr-parse, addr-preprocess.

go

go [<address expressions>]

Examples:

"go"
"go drawLeftLine"

Go until an address is reached.

  • The <address expressions> argument is as many address expressions as desired for breakpoints. Execution is continued until a breakpoint is reached. These breakpoints are then removed when the machine stops and are only active for the current patient.

See Also: break, continue, detach, quit.

gup

gup [<address>] [<instance field>]

Examples:

"gup"
print the generic object at *DS:SI and its ancestors
"gup @23 GI_states"
print the states of object @23 and its ancestors
"gup -i"
print the generic object under the mouse and the object's ancestors

Print a list of the object and all of its generic ancestors.

  • The address argument is the address to an object in the generic tree. This defaults to *DS:SI. The `-i' flag for an implied grab may be used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • The instance field argument is the offset to any instance data within the GenInstance which should be printed out.

See Also: gentree, vup, vistree, impliedgrab.

handles

handles [<flags>] [<patient>]

Examples: "handles"
"handles -f"
"handles ui"

Print all handles in-use.

  • The flags argument is a collection of flags, beginning with `-', from the following set:
    • s
    print summary only.
    e
    events only.
    p
    don't print prevPtr and nextPtr.
    f
    fast print-out - this doesn't try to figure out the block type.
    r
    reverse, i.e. starts at the end of the handle table.
    u
    print only those handles that are in-use.
  • The patient argument is a patient whose blocks are to be selectively printed (either a name or a core-block's handle ID). The default is to print all the blocks on the heap.
  • The following columns can appear in a listing:
    • HANDLE
    The handle of the block
    ADDR
    The segment address of the block
    SIZE
    Size of the block in bytes
    PREV
    The previous block handle (appears with the p flag)
    NEXT
    The next block handle (appears with the p flag)
    FLAGS The following letters appears in the FLAGS column:
    s sharable,
    S swapable,
    D discardable,
    L contains local memory heap,
    d discarded
    (by LMem module: discarded blocks don't appear here),
    a attached (notice given to swat whenever state changes)
    LOCK
    Number of times the block is locked or n/a if FIXED.
    OWNER
    The process which owns the block
    IDLE
    The time since the block has been accessed in minutes:seconds
    OINFO
    The otherInfo field of the handle (block type dependent)
    TYPE
    Type of the block, for example: R#1 (dgroup) resource number one
  • This only prints those handles in memory while `handles' prints all handles used.
  • The handles may be printed with lhwalk and phandle.

See Also: lhwalk, phandle, hgwalk.

handsum

handsum

Examples:

"handsum"
Summarize the use to which the handle table is being put.

This command analyzes the handle table and prints out a list of the number of handles being used by each geode, and for what purpose.

  • The columns of the output are labeled somewhat obscurely, owing to horizontal-space constraints. The headings, and their meanings are:
    • Res
    Resource handles (i.e. handles for data stored in the geode's executable)
    Mem
    Non-resource memory handles
    File
    Open files
    Thds
    Threads
    Evs
    Recorded events
    Qs
    Event queues
    Sems
    Semaphores
    EDat
    Data for recorded events
    Tim
    Timers
    SB
    Saved blocks (handles tracking memory/resource handles whose contents will go to an application's state file)
    VMF
    VM files
  • The "handles" command is good at printing out all the handles for a particular geode, but it's generally too verbose to use for the entire handle table. That's why this command exists.
  • It's a good idea to issue the command "dcache length 4096" before executing this command, as it ensures the entire handle table will end up in Swat's data cache, for quick access if you want to use the "handles" command immediately afterward.

hbrk

    hbrk <address> (byte|word) (match|mismatch) <value>

Examples:

"hbrk scrollTab+10 byte match 0"
print message handlers until a zero is written at scrollTab+10.
"hbrk OLScrollButton+3 word mismatch 0x654f"
Break when the word at OLScrollButton+3 is destroyed.

Break when a memory location changes.

  • The <address> argument is the address to watch for a change.
  • The (byte|word) argument indicates whether to watch a byte or a word for a change.
  • The (match|mismatch) argument indicates whether to break if the value at the address matches or mismatches the value hbrk is called with.
  • hbrk emulates a hardware breakpoint by checking at every message call to see if a location in memory has been written to. If so, swat breaks and tells between which two messages the write occurred. The information and the return stack will hopefully guide you to the offending line of code.
  • The command creates two breakpoints. Remove these to get rid of the hardware breakpoint.

See Also: brk, mwatch, showcalls.

heapspace

heapspace <geode>
heapspace total
heapspace syslib

Examples:

"heapspace geomanager"
print out "heapspace" value for geomanager
"heapspace total"
print out maxTotalHeapSpace
"heapspace syslib"
print out space being used by system libraries.

Prints out how much space the program requires on the heap. This value may then be used in a "heapspace" line of the program's .gp field. This command only determines present usage--to determine the most heapspace your geode will ever use requires that you make it allocate as much space as it ever will. This means pulling down all menus, opening all dialog boxes, and generally building out all UI gadgetry. The value this command prints is roughly the non-discardable heap usage by the app and any transient libraries that it depends on, plus an additional amount for thread activity.

When using this command, you should run your application on the device it is to run on. One or more system libraries may be XIP (running from ROM) on the device, but run from RAM on your target PC's emulator. Thus, to get a more accurate picture of memory usage, run the application on the planned device.

help

help [<command>]

This is the user-level access to the on-line help facilities for Swat. If given a topic (e.g. "brk") as its argument, it will print all help strings defined for the given topic (there could be more than one if the same name is used for both a variable and a procedure, for instance). If invoked without arguments, it will enter a browsing mode, allowing the user to work his/her way up and down the documentation tree.

help-fetch

help-fetch <topic-path>

Examples: "help-fetch top.patient"

Fetches the help string for a given topic path in the help tree.

If there is more than one node with the given path in the help tree, only the string for the first node will be returned.

help-fetch-level

help-fetch-level

Examples:

"help-fetch-level top.prog.obscure"
Returns the topics within the "top.prog.obscure" level of the help tree.

Returns a list of the topics available at a given level in the help tree.

The result is a list of node names without leading path components.

See Also: help-fetch.

help-help

help-help

Provides help about using the help command (q.v.)

See Also: help.

help-is-leaf

help-is-leaf <topic-path>

Examples:

"help-is-leaf top.prog"
See if top.prog is a leaf node in the help tree (i.e. if it has no children).

Determines whether a given path refers to a help topic or a help category.

Returns one if the given path refers to a leaf node, zero if it is not.

See Also: help-fetch, help-fetch-level.

help-minAspect

var help-minAspect [<ratio-times-ten>]

If non-zero, contains the minimum aspect ratio to be maintained when displaying tables in the help browser. The ratio is expressed as the fraction

entries_per_column*10/number_of_columns

E.g. a minimum ratio of 1.5 would be 15. (We multiply by ten because Swat doesn't support floating point numbers.)

help-scan

help-scan <pattern>

Examples:

"help-scan break"
Looks for all nodes at any level of the help tree whose documentation includes the pattern "break".

Scans all nodes in the help tree for those whose documentation matches a given pattern.

  • The result is a list of topic-paths.

See Also: help-fetch.

help-verbose

var help-verbose [0|1)]

If non-zero, performs verbose prompting.

hex

hex <number>

Examples:

"hex 034"
print hex equivalent of octal 34.
"hex 12"
print hex equivalent of decimal 12.

Print hexadecimal equivalent of a number.

hgwalk

hgwalk

Examples:

"hgwalk"
print statistics on all geodes

Print out all geodes and their memory usage.

history

history [<args>]

Examples:

"history 10"
Prints the last 10 commands entered via the "history subst" command.
"history subst $line"
Performs history substitution on the string in $line, enters the result in the history queue and returns the result.
"var n [history cur]"
Stores the number of the next string to be entered via "history subst" in the variable n.
"history set 50"
Limit the number of entries in the queue to 50.
"history fetch 36"
Returns the string entered as command number 36 in the history queue.

This command manipulates the history list. Options are:

<number>
Prints the most-recent <number> commands
set <queue-size>
Sets the number of commands saved
subst <str>
Performs history substitution on <str> and enters it into the history queue.
cur
Returns the current history number. If no argument is given, all saved commands are printed.
fetch <n>
Returns the string entered as command number <n> in the history queue.

hwalk

hwalk [<flags>] [<patient>]

Examples:

"hwalk"
display the heap
"hwalk -e"
display the heap and perform error checking
"hwalk -r ui"
display the heap owned by the ui in reverse order

Print the status of all blocks on the global heap.

  • The <flags> argument is a collection of flags, beginning with `-', from the following set:
    • r
    print heap in reverse order (decreasing order of addresses)
    p
    print prevPtr and nextPtr as well.
    e
    do error-checking on the heap.
    l
    just print out locked blocks
    f
    fast print-out--this doesn't try to figure out the block type
    F
    print out only fixed (or pseudo-fixed) resources.
    c
    Print out only code resources (discardable or fixed non-lmem non-dgroup resources).
    s < num >
    start at block <num>
  • The patient argument is a patient whose blocks are to be selectively printed (either a name or a core-block's handle ID). The default is to print all the blocks on the heap.
  • The following columns can appear in a listing:
    • HANDLE
    The handle of the block
    ADDR
    The segment address of the block
    SIZE
    Size of the block in bytes
    PREV
    The previous block handle (appears with the p flag)
    NEXT
    The next block handle (appears with the p flag)
    FLAGS The following letters appears in the FLAGS column:
    s sharable
    S swapable
    D discardable
    L contains local memory heap
    d discarded
    (by LMem module: discarded blocks don't appear here)
    a attached (notice given to swat whenever state changes)
    LOCK
    Number of times the block is locked or n/a if FIXED.
    OWNER
    The process which owns the block
    IDLE
    The time since the block has been accessed in minutes:seconds
    OINFO
    The otherInfo field of the handle (block type dependent)
    TYPE Type of the block, for example:
    R#1 (dgroup) Resource number one, named "dgroup"
    Geode Internal control block for a geode
    WINDOW, GSTATE,
    Internal structures of the given type
    GSTRING, FONT_BLK, FONT OBJ(write:0)
    Object block run by thread write:0
    VM(3ef0h)...
    VM block from VM file 3ef0h
  • This only prints those handles in memory while `handles' prints all handles used.
  • The handles may be printed with lhwalk and phandle.

See Also: lhwalk, phandle, handles, hgwalk.

iacp

iacp -ac
prints all connections

iacp -l
prints all lists without connections

iacp -d
prints all open documents

iacp <obj>
prints all connections to which <obj> is party

ibrk

Set a breakpoint interactively. At each instruction, you have several options:

q
Quit back to the command level.
n
Go to next instruction (this also happens if you just hit return).
p
Go to previous instruction.
P
Look for a different previous instruction.
^D
Go down a "page" of instructions. The size of the page is controlled by the global variable ibrkPageLen. It defaults to 10.
^U
Go up a "page" of instructions.
b
Set an unconditional breakpoint at the current instruction and go back to command level.
a
Like 'b', but the breakpoint is set for all patients.
t
Like 'b', except the breakpoint is temporary and will be removed the next time the machine stops.
B
Like 'b', but can be followed by a command to execute when the breakpoint is hit.
A
Like 'B', but for all patients.
T
Like 'B', but breakpoint is temporary.

ibrkPageLen

var ibrkPageLen [<number-of-lines>]

Number of instructions to skip when using the ^D and ^U commands of ibrk.

ignerr

ignerr

Examples:

"ignerr" ignore error and continue
"ignerr MyFunc::done" ignore error and continue at MyFunc::done.

Ignore a fatal error and continue.

  • The address argument is the address of where to continue execution. If not specified then CS:IP is taken from the frame.
  • The stack is patched so that execution can continue in the frame above the fatal error handling routine.

See Also: why, backtrace.

imem

imem [<address>] [<mode>]

Examples:

"imem"
enter imem mode at DS:SI
"imem ds:di"
enter imem mode at DS:SI

Examine memory and modify memory interactively.

  • The address argument is the address to examine. If not specified, the address after the last examined memory location is used. If no address has been examined then DS:SI is used for the address.
  • The mode argument determines how the memory is displayed and modified. Each of the four modes display the memory in various appropriate formats. The modes are:
  • The default mode is swat's best guess of what type of object is at the address.
  • imem lets you conveniently examine memory at different locations and assign it different values. imem displays the memory at the current address according to the mode. From there you can move to another memory address or you can assign the memory a value.
  • You may choose from the following single-character commands:
    • b , w , d , i
    Sets the mode to the given one and redisplays the data.
    n , j , < Return >
    Advances to the next data item. The memory address advances by the size of the mode.
    p , k
    Returns to the preceding data item. The memory address decreases by the size of the mode. When displaying instructions, a heuristic is applied to locate the preceding instruction. If it chooses the wrong one, use the `P' command to make it search again.
    < space >
    Clears the data display and allows you to enter a new value appropriate to the current display mode. The "assign" command is used to perform the assignment, so the same rules apply to it, with the exception of `- and "-quoted strings. A string with `s around it (`hi mom') has its characters poked into memory starting at the current address. A string with "s around it ("swat.exe") likewise has its characters poked into memory, with the addition of a null byte at the end. This command is not valid in instruction mode.
    q
    quit imem and return to command level. The last address accessed is recorded for use by the other memory-access commands.
    ^D
    Display a "page" of successive memory elements in the current mode.
    ^U
    Display a "page" of preceding memory elements in the current mode.
    h , ?
    This help list.
    For ^D and ^U, the size of a "page" is kept in the global variable imemPageLen , which defaults to 10.

See Also: bytes, words, dwords, assign.

imemPageLen

var imemPageLen [<numlines>]

Contains the number of elements to display when imem is given the ^D or ^U command.

impliedgrab

impliedgrab

Examples:

"gentree [impliedgrab]"
print the generic tree under the mouse

Print the address of the current implied grab, which is the screen object grabbing the mouse.

  • This command is normally used with gentree to get the generic tree of an application by placing the mouse on application's window and issuing the command.

See Also: systemobj, gentree.

impliedwin

impliedwin
"wintree [impliedwin]"
print the window tree of the window under the mouse

Print the address of the current implied window (the window under the mouse).

  • Note that a window handle is returned.
  • This command is normally used with wintree. One may also use the print command if they properly cast the handle.

int

int [<int level> <state>]

Examples:

"int"
report the interrupt statuses
"int 1:1 on"
allow keyboard interrupt while in swat

Set or print the state of the two interrupt controllers for when then machine is stopped in Swat.

  • If no arguments are given, the current state is printed.
  • The <int level> argument is specified by their names or their numbers with the form <controller>:<number>. <controller> is either 1 or 2, and <number> ranges from 0 to 7. The interrupts and their numbers are:
    • Timer 1:0
    System timer. Probably dangerous to enable.
    Keybd 1:1
    Keyboard input.
    Slave 1:2
    This is how devices on controller 2 interrupt. Disabling this disables them all.
    Com2 1:3
    This is the port usually used by Swat, so it can't be disabled.
    Com1 1:4
    The other serial port -- usually the mouse.
    LPT2 1:5
    The second parallel port
    Floppy 1:6
    Floppy-disk drive
    LPT1 1:7
    First parallel port
    Clock 2:0
    Real-time clock
    Net 2:1
    Network interfaces (?)
    FPA 2:5
    Coprocessor
    HardDisk 2:6
    Hard-disk drive
  • The <state> argument is either on or off.

intFormat

var intFormat [<format-string>]

Examples:

"var intFormat %d"
Sets the default format for printing unsigned integers to decimal.

$intFormat contains the string passed to the "format" command to print an integer.

The default value is {%xh}, which prints the integer in hexadecimal, followed by an "h".

See Also: print, byteAsChar.

intr

Catch, ignore, or deliver an interrupt on the target PC. First argument is the interrupt number. Optional second argument is "catch" to catch delivery of the interrupt, "ignore" to ignore the delivery, or "send" to send the interrupt (the machine will keep going once the interrupt has been handled). If no second argument is given, the interrupt is delivered.

io

io [w] <port> [<value>]

Examples:

"io 21h"
Reads byte-sized I/O port 21h.
"io 20h 10"
Writes decimal 10 to byte-sized I/O port 20h.

Provides access to any I/O port on the PC.

  • If you give the optional first argument "w" then Swat will perform a 16-bit I/O read or write, rather than the default 8-bit access. Be aware that most devices don't handle this too well.
  • <port> must be a number (in any radix); it cannot be a register or other complex expression.
  • If you don't give a <value>, you will be returned the contents of the I/O port (it will not be printed to the screen).

irq

irq
irq (no|yes)
irq (set|clear)

Examples:

"irq"
Returns non-zero if an interrupt is pending.
"irq no"
Disable recognition and acting on a break request from the keyboard.
"irq set"
Pretend the user typed Ctrl-C.

Controls Swat's behavior with respect to interrupt requests from the keyboard.

  • Swat maintains an interrupt-pending flag that is set when you type Ctrl+C (it can also be set or cleared by this command). It delays acting on the interrupt until the start of the next or the completion of the current Tcl command, whichever comes first.
  • When given no arguments, it returns the current state of the interrupt-pending flag. This will only ever be non-zero if Swat is ignoring the flag (since the command wouldn't actually return if the flag were set and being paid attention to, as the interpreter would act on the flag to vault straight back to the command prompt).
  • If given "no" or "yes" as an argument, it causes Swat to ignore or pay attention to the interrupt-pending flag, respectively.
  • You can set or clear the flag by giving "set" or "clear" as an argument.

is-obj-in-class

is-obj-in-class <obj-addr> <class-name>

Examples:

"is-obj-in-class ^l4e10h:1eh GenPrimaryClass"
see if the object at ^l4e10h:1eh is in GenPrimaryClass .

Returns whether a given object is in the specified class.

  • Returns one if the object is in the specified class, zero otherwise. It will return one if the object's class is a subclass of the passed class.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.

See Also: psup.

istep

istep [<default command>]

Examples:

"is"
enter instruction step mode
"istep n"
enter instruction step mode, <ret> does a next command

Step through the execution of the current patient. This is THE command for stepping through assembly code.

  • The default command argument determines what pressing the <Return> key does. By default, <Return> executes a step command. Any other command listed below may be substituted by passing the letter of the command.
  • Istep steps through the patient instruction by instruction, printing where the ip is, what instruction will be executed, and what the instruction arguments contain or reference. Istep waits for the user to type a command which it performs and then prints out again where istep is executing.
  • This is a list of istep commands:
    • q , < Esc >, ` `
    Stops istep and returns to command level.
    b
    Toggles a breakpoint at the current location.
    c
    Stops istep and continues execution.
    n
    Continues to the next instruction, skipping procedure calls, repeated string instructions, and software interrupts. Using this procedure, istep only stops when the machine returns to the right context (i.e. the stack pointer and current thread are the same as they are when the "n" command was given). Routines which change the stack pointer should use "N" instead.
    o
    Like "n" but steps over macros as well.
    l
    Goes to the next library routine.
    N
    Like `n', but stops whenever the breakpoint is hit, whether you're in the same frame or not.
    O
    Like `N' but steps over macros as well.
    m, M
    Goes to the next method called. Doesn't work when the message is not handled anywhere.
    F
    Finishes the current message.
    f
    Finishes out the current stack frame.
    s , < Return >
    Steps one instruction.
    A
    Aborts the current stack frame.
    S
    Skips the current instruction
    B
    Backs up an instruction (opposite of "S").
    J
    Jump on a conditional jump, even when "Will not jump" appears. This does not change the condition codes.
    g
    Executes the `go' command with the rest of the line as arguments.
    e
    Executes a Tcl command and returns to the prompt.
    r
    Lists the registers (uses the regs command)
    R
    References either the function to be called or the function currently executing.
    h , ?
    Displays a help message.
  • If the current patient isn't the actual current thread, istep waits for the patient to wake up before single-stepping it.

See Also: sstep, listi.

keyboard

keyboard [<object>]

Examples:

"keyboard"
print keyboard hierarchy from system object down
"keyboard -i"
print keyboard hierarchy from implied grab down
"keyboard ^l4e10h:20h"
print keyboard hierarchy from ^l4e10h:20h down.

Prints the keyboard hierarchy below an object.

  • If no object is specified, the system object is used.

  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available.

    • This variable defaults to false.

See Also: target, focus, mouse, model, pobject.

keyboardobj

keyboardobj

Examples:

"keyboardobj"
return object with keyboard grab
"pobj [keyboardobj]"
do a pobject on the object with the keyboard grab (equivalent to "pobj -kg".

Returns the object with the keyboard grab.

See Also: target, focus, mouse, keyboard, mouseobj.

lastCommand

$lastCommand

Examples:

"var repeatCommand $lastCommand"
Set the current command as the command to execute next time.

$lastCommand stores the text of the command currently being executed.

This variable is set by top-level-read. Setting it yourself will have no effect, unless you call set-address or some similar routine that looks at it.

See Also: repeatCommand.

lhwalk

lhwalk [<address>]

Examples:

"lhwalk 1581h"
list the lm heap at 1581h:0

Prints out information about a local memory heap.

  • The address argument is the address of the block to print. The default is the block pointed to by DS.

See Also: hwalk, objwalk.

link

link <library> [<patient>]

Examples:

"link motif"
Makes the library "motif" a library of the current patient as far as Swat is concerned.

Allows you to link a patient to act as an imported library of another patient, even though the other patient doesn't actually import the patient. This is useful only for symbol searches.

  • sym-default is a much better way to have Swat locate symbols for libraries that are loaded by GeodeUseLibrary() .
  • Cycles are not allowed. I.e. don't link your application as a library of the UI, as it won't work--or if it does, it will make Swat die.
  • The link persists across detach/attach sequences so long as the <patient> isn't recompiled and downloaded.
  • If you don't give <patient>, then the current patient will be the one made to import <library>
  • Both <library> and <patient> are patient names , not tokens.

See Also: help-fetch.

listi

listi [<address>] [<length>]

Examples:

"l"
disassemble at the current point of execution
"listi geos::Dispatch"
disassemble at the kernel's dispatch routine
"listi DocClip:IsOut"
disassemble at the local label
"listi cs:ip 20"
disassemble 20 instructions from the current point of execution

Disassemble at a memory address.

  • The <address> argument is the address to examine. If not specified, the address after the last examined memory location is used. If no address has be examined then CS:IP is used for the address.
  • The <length> argument is the number of instructions to list. It defaults to 16.
  • Pressing <Return> after this command continues the list.

See Also: istep, sstep, skip.

load

load <file>

Load a file of Tcl commands.

  • If the file cannot be found as given, it is looked for in all the directories mentioned in the "load-path" variable. This variable is initialized from the SWATPATH environment variable, which is in the form <dir1>:<dir2>:...:<dirN>.
  • The Swat library directory is appended to this path so you need not include it yourself. The file need not end in ".tcl".
  • When searching, file , file .tcl, and file .tlc are searched for. If load finds a file .tlc file, that file will be used only if it is more recent than any corresponding file .tcl or file file.

loadapp

Load an application from swat. Single argument is the file name of the application to launch (application must reside in the appl subdirectory of the GEOS tree).

The application is opened in normal application mode. Note that the application will not be loaded until you continue the machine, as the loading is accomplished by sending a message to the UI.

loadgeode

Load a geode from swat. Mandatory first argument is the name of the file to load (with path from top-level GEOS directory, using / instead of \ as the path separator).

Second and third arguments are the data words to pass to the geode. The second argument is passed to the geode in cx, while the third argument is passed in dx.

Both the second and third arguments are optional and default to 0. They likely are unnecessary.

locals

locals [<func>]

Examples:

"locals"
Print the values of all local variables and arguments for the current frame.
"locals WinOpen"
Print the names of all local variables for the given function. No values are printed.

Allows you to quickly find the values or names of all the local variables of a function or stack frame.

See Also: print, frame, info.

localwin

localwin [<numlines>]

Examples:

"localwin"
Display local variables in a 10-line window
"localwin 15"
Display local variables in a 15-line window
"localwin off"
Turn off the local variable display

Turn on or off the continuous display of local variables.

  • Passing an optional numerical argument turns on display of that size. The default size is 10 lines.
  • Only one local variable display may be active at a time.

loop

Simple integer loop procedure. Usage is:
loop <loop-variable> <start>,<end> [step <step>] <body>

<start>, <end>, and <step> are integers. <body> is a string for Tcl to evaluate. If no <step> is given, 1 or -1 (depending as <start> is less than or greater than <end>, respectively) is used. <loop-variable> is any legal Tcl variable name.

map

map <var-list> <data-list>+ <body>

Examples:

"map {i j} {a b} {c d} {list $i $j}"
Executes the command "list $i $j" with i and j assigned to successive elements of the lists {a b} and {c d}, respectively, merging the results into the list {{a c} {b d}}

This applies a command string to the successive elements of one or more lists, binding each element in turn to a variable and evaluating the command string. The results of all the evaluations are merged into a result list.

  • The number of variables given in <var-list> must match the number of <data-list> arguments you give.
  • All the <data-list> arguments must have the same number of elements.
  • You do not specify the result of the <body> with the "return" command. Rather, the result of <body> is the result of the last command executed within <body>.

See Also: foreach, mapconcat.

mapconcat

mapconcat <var-list> <data-list>+ <body>

Examples:

"mapconcat {i j} {a b} {c d} {list $i $j}"
Executes the command "list $i $j" with i and j assigned to successive elements of the lists {a b} and {c d}, respectively, merging the results into a string.

map-method

map-method <number> <object>
map-method <number> <class-name> [<object>]

Examples:

"map-method ax ^lbx:si"
Prints the name of the message in ax, from the object at ^lbx:si's perspective.
"map-method 293 GenClass"
Prints the name of message number 293 from GenClass's perspective.

Maps a message number to a human-readable message name, returning that name. This command is useful both for the user and for a Tcl procedure.

  • When called from a Tcl procedure, the <class-name> argument should be the fullname of the class symbol (usually obtained with the obj-class function), and <object> should be the address of the object for which the mapping is to take place. If no <object> argument is provided, map-method will be unable to resolve messages defined by one of the object's superclasses that lies beyond a variant superclass.
  • If no name can be found, the message number, in decimal, is returned.
  • The result is simply returned, not echoed. You will need to echo the result yourself if you call this function from anywhere but the command line.

See Also: obj-class.

mcount

mcount [<args>]

Examples:

"mcount"
start the method count or print the count
"mcount reset"
restart the method count
"mcount stop"
stop the method count
"mcount MyAppRecalcSize"
count messages handled by MyAppRecalcSize

Keep a count of the methods called.

The args argument may be one of the following:

nothing
start the method count or print the current count
'reset'
reset the count to zero
'stop'
stop the method count and remove it's breakpoint
message handler
start the method count for a particular method

See Also: mwatch, showcalls.

memsize

memsize [<memory size>]

Examples:

"memsize"
"memsize 512"

Change the amount of memory that GEOS thinks that it has.

  • The <memory size> argument is the size to make the heap. If none is specified then the current memory size is returned.
  • Memsize can only be run at startup, before the heap has been initialized. Use this right after an `att -s'.
  • Memsize accounts for the size of the stub.

methods

methods <class>
methods <object>
methods <flags>

Examples:

"methods -p"
Print out methods defined for process
"methods ui::GenDocumentClass"
Print out GenDocumentClass methods
"methods 3ffch:072fh"
Print out methods for class at addr
"methods -a"
Print methods of top class of app obj

Prints out the method table for the class specified, or if an object is passed, for the overall class of the object. Useful for getting a list of candidate locations to breakpoint.

model

model [<object>]

Examples:

"model"
print model hierarchy from system object down
"model -i"
print model hierarchy from implied grab down
"model ^l4e10h:20h"
print model hierarchy from ^l4e10h:20h down.

Prints the model hierarchy below an object.

  • If no object is specified, the system object is used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • Remember that the object you start from may have the model exclusive within its part of the hierarchy, but still not have the exclusive because something in a different part of the tree has it.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available.

    • This variable defaults to false.

See Also: target, focus, mouse, keyboard, pobject.

modelobj

modelobj

Examples:

"modelobj"
print model hierarchy from system object down
"pobj [modelobj]"
Do a pobject on the object with the model grab (the equivalent of a "pobj -m").

Returns the object with the model grab.

See Also: target, focus, model, focusobj, targetobj.

mouse

mouse [<object>]

Examples:

"mouse"
print mouse hierarchy from system object down
"mouse -i"
print mouse hierarchy from implied grab down
"mouse ^l4e10h:20h"
print mouse hierarchy from ^l4e10h:20h down.

Prints the mouse hierarchy below an object.

  • If no object is specified, the system object is used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available.

    • This variable defaults to false.

See Also: target, focus, model, keyboard, pobject.

mouseobj

mouseobj

Examples:

"mouseobj"
return object with mouse grab
"pobj [mouseobj]"
do a pobject on the object with the mouse grab (equivalent to "pobj -mg").

Returns the object with the mouse grab.

See Also: target, focus, mouse, keyboard, keyboardobj.

mwatch

mwatch <msg>+
mwatch add <msg>+
mwatch list
mwatch clear

Examples:

"mwatch MSG_VIS_DRAW MSG_METAQUIT"
watch these messages
"mwatch add MSG_META_START_SELECT "
watch this message also
"mwatch"
clear all message watches

Display all deliveries of a particular message.

  • The msg argument is which messages to watch. Those specified replace any messages watched before. If none are specified then any messages watched will be cleared.
  • You may specify up to eight messages to be watched (fewer if you have other conditional breakpoints active). See cbrk for more information about conditional breakpoints.
  • "mwatch clear" will clear all message watches.
  • "mwatch add" will add the specified messages to the watch list.
  • "mwatch list" will return a list of breakpoints that have been set by previous calls to mwatch.

See Also: objwatch, objbrk, objmessagebrk, procmessagebrk.

next

next

Examples:

"next"
execute the next assembly instruction without entering it
"n"

Execute the patient by a single assembly instruction, skipping over any calls, repeated instructions, or software interrupts.

  • next does not protect against recursion, so when the breakpoint for the next instruction is hit, the frame of execution may be one lower.

See Also: step, istep.

noStructEnum

var noStructEnum [(0|1)]

Examples:

"var noStructEnum 1"
Don't put "struct" or "enum" before the data type for variables that are structures or enumerated types.

Structure fields that are structures or enumerated types normally have "struct" or "enum" as part of their type description. This usually just clutters up the display, however, so this variable shuts off this prepending.

The default value of this variable is one.

See Also: print.

null

null <val>

Examples:

"null $sym"
Sees if the symbol token stored in $sym is the empty string or "nil."

Checks to see if a string is either empty or "nil," special values returned by many commands when something isn't found or doesn't apply. Returns non-zero if <val> is either of these special values.

The notion of "nil" as a value comes from lisp.

See Also: index, range.

objbrk

objbrk [<obj address>] [<message>]

Examples:

"objbrk ds:si MSG_VIS_DRAW "
break when a MSG_VIS_DRAW reaches the object
"objbrk -p"
Break when any message is sent to the process object.

Break when a particular message reaches a particular object.

  • If you do not give a <message> argument after the <obj> argument, the machine will stop when any message is delivered to the object.
  • <obj> is the address of the object to watch.
  • The <objbrk> argument to "objbrk del" is the token/number returned when you set the breakpoint.

See Also: objwatch, objmessagebrk, mwatch.

obj-class

objclass <obj-addr>

Examples:

"var cs [obj-class ^lbx:si]"
Store the symbol token for the class of the object ^lbx:si in the variable $cs.

Figures out the class of an object, coping with unrelocated object blocks and the like.

  • The value return is a symbol token, as one would pass to the "symbol" command. Using "symbol name" or "symbol fullname" you can obtain the actual class name.
  • We decide whether to relocate the class pointer ourselves based on the LMF_RELOCATED bit in the LMBH_flags field of the object block's header. There are times, e.g. during the call to MSG_META_RELOCATE for an object, when this bit doesn't accurately reflect the state of the class pointer and we will return an error when we should not.

See Also: symbol.

objcount

objcount [-q] [-X] [-Y] [-b #] [-o #] [-p #]

Examples:

"objcount"
count all objects
"objcount -p welcome"
count all objects owned by welcome
"objcount -o *desktop::DiskDrives"
count this one object
"objcount -b 0x3270"
count all objects in this block.

Count up instances of various objects on the heap.

  • The first argument specifies the options:
    • q
    quiet operation - no progress output (not applicable with X, Y)
    o #
    check only object #
    b #
    check ONLY block #
    p #
    check only blocks for patient #
    c #
    check only objects of class #
    C #
    check only objects of top-level class #
    X
    show general verbose info
    Y
    show search verbose info
  • Output fields:
    • direct
    number of direct instances of this class
    indirect
    number if indirect instance of this class (i.e object's superclass is this class)
    size
    total size of instance data for this class (excludes instance data inherited from superclass)
  • Status output:
    • .
    processing heap block
    ,
    processing matching object's top-level class
    ;
    processing matching object's non-top-level class

See Also: hwalk, objwalk, lhwalk.

obj-foreach-class

obj-foreach-class <function> <object> [<args>]

Examples:

"obj-foreach-class foo-callback ^lbx:si"
calls foo-callback with each class in turn to which the object ^lbx:si belongs.

Processes all the classes to which an object belongs, calling a callback procedure for each class symbol in turn.

  • <function> is called with the symbol for the current class as its first argument, <object> as its second, and the arguments that follow <object> as its third and subsequent arguments.
  • <function> should return an empty string to continue up the class tree.
  • obj-foreach-class returns whatever <function> returned, if it halted processing before the root of the class tree was reached. It returns the empty string if <function> never returned a non-empty result.

See Also: obj-class.

objmessagebrk

objmessagebrk [<address>]

Examples:

"objmessagebrk MyObj"
break whenever a message is sent to MyObj
"objmessagebrk"
stop intercepting messages

Break whenever a message is sent to a particular object via ObjMessage.

  • The <address> argument is the address to an object to watch for messages being sent to it. If no argument is specified then the watching is stopped.
  • This breaks whenever a message is sent (before they get on the message queue. This enables one to track identical messages to an object which can be removed.

See Also: objwatch, mwatch, procmessagebrk, pobject.

objwalk

objwalk [<address>]

Examples: "objwalk"

Prints out information about an object block.

  • The <address> argument is the address of the block to print. The default is the block pointed at by DS.

See Also: lhwalk, pobject.

objwatch

objwatch [<address>]

Examples:

"objwatch ds:si"
watch the messages which reach the object at DS:SI
"objwatch MyObject"
watch the messages which reach MyObject
"objwatch"
Watch the messages which reach the process object.

Display message calls that have reached a particular object.

  • The <address> argument is the address of the object to watch.
  • This returns the token of the breakpoint being used to watch message deliveries to the object. Use the "brk" command to enable, disable, or turn off the watching of the object.

See Also: brk, mwatch, objmessagebrk, procmessagebrk, pobject.

omfq

omfq <message> <object> <args>*

Examples:

"omfq MSG_META_QUIT *HelloApp"
Sends MSG_META_QUIT to the * HelloApp object.

Forces a message for an object onto its event queue.

  • This command calls ObjMessage, passing it
    di=mask MF_FORCE_QUEUE.
  • <args> is the set of additional parameters to pass to ObjMessage. It consists of <variable/register> <value> pairs, which are passed to the "assign" command. As a special case, if the variable is "push", the value (a word) is pushed onto the stack and is popped when the message has been queued.
  • The registers active before you issued this command are always restored, regardless of whether the call to ObjMessage completes successfully. This is in contrast to the "call" command, which leaves you where the machine stopped with the previous state lost.
  • This command will only work when execution halts in the thread that is to receive the message.

See Also: call.

pappcache

pappcache

Examples:

"pappcache"
Print out current state of the app-cache

Prints out the current state of the system application cache, for systems operating in transparent launch mode.

Specifically, this command prints out:

  • Applications in the cache (First choice for detaching)
  • Top full-screen App (Not detached except by another full screen app)
  • Desk accessories (detached only as last resort)
  • Application geodes in the process of detaching

patch

patch [<addr>]
patch del <addr>*

Patch assists in creating breakpoints that invisibly make small changes to code. This can help the programmer find several bugs without remaking and redownloading.

  • If you give no <addr> when creating a patch, the patch will be placed at the most-recently accessed address, as set by the command that most-recently accessed memory (e.g. bytes, words, listi, imem, etc.)
  • When creating a patch, you are prompted for its contents, each line of which comes from the following command set:

Patch Command Set

Form | Meaning | Example
<reg> = <value> | assign value to reg | ax = bx dl = 5
push <reg>|<value> | push value | push ax push 45
pop <reg>|<value> | pop value | pop ax pop 45
pop | pop nothing (sp=sp+2) | pop
jmp <address> | change ip | jmp UI_Attach+45
scall <address> <regs> | call routine (save) | scall MemLock ax = 3
mcall<address> <regs> | call routine (modify) | mcall MemLock ax = 3
xchg <reg> <reg> | swap two registers | xchg ax bx
set <flag> | set condition flag | set CF set ZF
reset <flag> | reset condition flag | reset CF reset ZF
if <flag> | if flag set then ... | if CF
if !<flag> | if flag reset then ... | if !ZF
if <expr> | if expr then ... | if foo == 4
else |  |
endif |  |
ret | make function return | ret
$ | terminate input |
a | abort |
<other> | tcl command | echo $foo

<flag> is taken from the set TF, IF, DF, OF, SF, ZF, PF, AF, CF and must be in upper-case.

The "scall" command has no effect on the current registers (not even for purposes of return values), while the "mcall" command changes whatever registers the function called modifies. See the "call" documentation for the format of <regs>.

patchin

Patchin undoes the work of patchout.

patchout

This command causes a RET to be placed at the start of a routine.

patient-default

patient-default [<geode-name>]

Examples:

"patient-default hello2"
Makes "hello2" the default patient.
"patient-default"
Prints the names of the current default patient.

Specifies the default patient. The send and run commands will use this as the default patient to operate on if none is passed to them.

pbitmap

pbitmap <address>

Print a bitmap graphically.

  • The address argument is the address of the Bitmap or CBitmap structure.
  • Color bitmaps are printed with a letter representing the color as well. The letters are index from the string (kbgcrvnAaBGCRVYW).

pcarray

pcarray [-eth] [<address>]

Examples:

"pcarray"
Print the chunk array at *DS:SI (header only)
"pcarray es:di"
Print the chunk array at ES:DI (header only)
"pcarray -e"
Print the chunk array at *DS:SI and print the elements in the array
"pcarray -tMyStruct"
Print the chunk array at *DS:SI and print the elements where the elements are of type MyStruct
"pcarry -tMyStruct -TMyExtraStruct"
Like above, but data after MyStruct is printed as an array of MyExtraStruct structures.
"pcarray -e3"
Print the chunk array at *DS:SI and print the third element
"pcarray -hMyHeader"
Print the chunk array at *DS:SI (header only) where the header is of type MyHeader

Print information about a chunk array.

  • The flags argument can be any combination of the flags "e", "t", and "h". The "e" flag prints all elements. If followed by a number (e.g. "-e3"), then only the third element will be printed.
  • The `t' flag specifies the elements' type. It should be followed
    immediately by the element type. You can also use "-tgstring" if
    the elements are GString Elements.
  • The `h' flag specifies the header type. It should be followed immediately by the element type.
  • The `l' flag specifies how many elements to print. It can be used in conjunction with the `e' flag to print a range of element numbers.
  • The `H' flag suppresses printing of the header.
  • All flags are optional and may be combined.
  • The address argument is the address of the chunk array. If not specified then *ds:si is used.

pcbitmap

pcbitmap <address> <width> <height> [<no space flag>]

Examples:

"pcbitmap *ds:si 64 64 t"
print the bitmap without spaces

Print out a one-bit deep packbits-compacted bitmap.

  • The <address> argument is the address to the bitmap data.
  • The <width> argument is the width of the bitmap in pixels.
  • The <height> argument is the height of the bitmap in pixels.
  • The <no space flag> argument removes the space normally printed between the pixels. Anything (like "t") will activate the flag.

See Also: pncbitmap.

pcelldata

pcelldata [<addr>]

Examples:

"pcelldata *es:di"
Print cell data for cell at *es:di.

Prints data for a spreadsheet data.

If no address is given, *es:di is used.

See Also: content, pcelldeps.

pcelldeps

pcelldeps <filehan> [<addr>]

Examples:

"pcelldeps 4be0h *es:di"
print dependencies of cell in file 4be0h.

Prints dependencies for a cell in the spreadsheet.

If no address is given, *es:di is used.

See Also: content, pcelldata.

pclass

pclass [<address>]

Examples:

"pclass"
prints the class of *DS:SI
"pclass ^l4ac0h:001eh"
Print the class of the object at the given address.

Print the object's class.

  • The <address> argument is the address of the object to find the class of. This defaults to *DS:SI.

pdb

Produces useful information about a DBase block. For now, only information about the map block of the DBase file is produced. First arg H is the SIF_FILE or SIG_VM handle's ID. Second arg B is the VM block handle for which information is desired.

pdgroup

pdgroup [<flags>] [<patient>]

Examples:

"pdgroup"
Prints out all dgroup variables of the current patient
"pdgroup -e *lock*"
Prints out all dgroup variables matching expression "*lock*" of the current patient
"pdgroup term"
Prints out all dgroup variables of the patient "term"
"pdgroup -e *lock* term"
Prints out all dgroup variables matching expression "*lock*" of the patient "term"

Prints out the variables defined in dgroup.

  • Flags:
    • -e <expression>
    Only prints out the variables matching <expression>. When -e flag is used, expression argument must be supplied. The expression syntax is the same as the one specified in "string match" command.

See Also: print, pscope.

pdisk

pdisk <disk-handle>

Examples:

"pdisk 00a2h"
Prints information about the disk whose handle is 00a2h.

Prints out information about a registered disk, given its handle.

The Flags column is a string of single-character flags with the following meanings:

w
The disk is writable.
V
The disk is always valid, i.e. it's not removable.
S
The disk is stale. This is set if the drive for the disk has been deleted.
u
The disk is unnamed, so the system has made up a name for it.

See Also: diskwalk, drivewalk.

pdrive

pdrive <drive-handle>
pdrive <drive-name>
pdrive <drive-number>

Examples:

"pdrive si"
Print a description of the drive whose handle is in SI
"pdrive al"
Print a description of the drive whose number is in AL
"pdrive C"
Print a description of drive C

Provides the same information as "drivewalk," but for a single drive, given the offset to its DriveStatusEntry structure in the FSInfoResource.

This is intended for use by implementors of IFS drivers, as no one else is likely to ever see a drive handle.

See Also: drivewalk.

pdw

pdr <register pair>

Examples:

"pdw dxax"
"pdw dx.ax"
"pdw dx:ax"
Prints the dword value of register pair dx:ax

Prints the dword value of a register pair.

The first register is the high register while the second is the low register.

See Also: print.

penum

penum <type> <value>

Examples:

"penum FatalErrors 0"
print the first FatalErrors enumeration

Print an enumeration constant given a numerical value.

  • The <type> argument is the type of the enumeration.
  • The <value> argument is the value of the enumeration in a numerical format.

See Also: print, precord.

pevent

pevent <handle>

Examples:

"pevent 39a0h"
Print event with handle.

Print an event given its handle.

See Also: elist, eqlist, eqfind, erfind.

pflags

pflags

Prints the current machine flags (carry, zero, etc.).

See Also: setcc, getcc.

pfont

pfont [-c] [<address>]

Examples:

"pfont"
print bitmaps of the characters of the font in BX.
"pfont -c ^h1fd0h"
list the characters in the font at ^h1fd0h.

Print all the bitmaps of the characters in a font.

  • The "-c" flag causes pfont to list which characters are in the font and any special status (i.e. NOT BUILT).
  • The <address> argument is the address of the font. If none is specified then ^hbx is used.

See Also: fonts, pusage, pfontinfo.

pfontinfo

pfontinfo <font ID>

Examples: "pfontinfo FID_BERKELEY"

Prints font header information for a font. Also lists all sizes built.

  • The <font ID> argument must be supplied. If not known, use `fonts -u' to list all the fonts with their IDs.

See Also: fonts, pfont.

pgen

pgen <element> [<object>]

Examples:

"pgen GI_states @65"
print the states of object 65
"pgen GI_visMoniker"
print the moniker of the object at *DS:SI
"pgen GI_states -i"
print the states of the object at the implied grab

Print an element of the generic instance data.

  • The <element> argument specifies which element in the object to print
  • The <object> argument is the address to the object to print out. It defaults to *DS:SI and is optional. The `-i' flag for an implied grab may be used.

See Also: gentree, gup, pobject, piv, pvis.

pgs

pgs <address>

Examples:

"pgs"
List the graphics string at DS:SI
"pgs ^hdi"
Lift the graphics string whose handle is in DI, starting at the current position.
"pgs -s ^hdi"
List the graphics string whose handle is in DI, starting at the beginning of the graphics string.
"pgs -l3 ^hdi"
List three elements of the graphics string whose handle is in DI, starting at the current position.

List the contents of a graphics string.

  • The <address> argument is the address of a graphics string. If none is specified then DS:SI is used as a pointer to a graphics string.
  • The passed address may also be the base of a gstate (e.g. "^hdi"). In this case, the gstring that is associated with the gstate will be printed.
  • The -s option can be used to specify that the gstring should be listed from the beginning of the string. By default, gstrings will be listed starting at the current position.
  • The -g option can be used to specify that the passed address is the address of a GrObj (GStringClass) object-- the gstring for that object will be listed.

See Also: pbitmap.

phandle

phandle <handle ID>

Examples:

"phandle 1a8ch"
print the handle 1a8ch

Print out a handle.

  • The <handle ID> argument is just the handle number. Make sure that the proper radix is used.
  • The size is in paragraphs.

See Also: hwalk, lhwalk.

pharray

pharray [<flags>] [<vmfile> <dirblk>]

Examples:

"pharray"
Print the huge array at ^vbx:di (header only)
"pharray dx cx"
Print the huge array at ^vdx:cx (header only)
"pharray -e"
Print the huge array at ^vbx:di and print the elements in the array
"pharray -tMyStruct"
Print the huge array at ^vbx:di and print the elements where the elements are of type MyStruct
"pharray -e3"
Print the huge array at ^vbx:di and print the third element
"pharray -h"
Print the header of the HugeArray at ^vbx:di, using the default header type (HugeArrayDirectory).
"pharray -hMyHeader"
Print the huge array at ^vbx:di (header only) where the header is of type MyHeader
"pharray -d"
Print the directory elements of a HugeArray
"pharray -e5 -l8"
Print 8 HugeArray elements starting with number 5

Print information about a huge array.

  • The flags argument can be any combination of the flags `e', `t', and `h'. The `e' flag prints all elements. If followed by a number "-e3", then only the third element is printed.

    • The `t' flag specifies the elements' type. It should be followed immediately by the element type. You can also use "gstring", in which case the elements will be interpreted as GString Elements.

    The `h' flag specifies the header type. It should be followed immediately by the element type. If no options are specified, then "-hHugeArrayDirectory" is used. If any other options are specified, then the printing of the header is disabled. So, for example, if you want both the header and the third element, use "-h -e3".

    The `d' flag specifies that the HugeArray directory entries should be printed out.

    The `l' flag specified how many elements to print.

    The `s' flag requests that a summary table be printed.

    All flags are optional and may be combined.

  • The address arguments are the VM file handle and the VM block handle for the directory block. If nothing is specified, then bx:di is used

pini

pini [<category>]

Examples:

"pini Lights Out"
Print out the contents of the Lights Out category in each .ini file
"pini"
Print out each currently loaded .ini file.

Provides you with the contents of the .ini files being used by the current GEOS session.

<category> may contain spaces and other such fun things. In fact, if you attempt to quote the argument (e.g. "pini {Lights Out}"), this will not find the category.

pinst

pinst [<address>]

Examples:

"pinst"
print the last master level of instance data of the object at *DS:SI
"pinst *MyObject"
print the last master level of instance data of MyObject.
"pinst -i"
print the last master level of the windowed object at the mouse pointer.

Print out all the instance data to the last level of the object.

  • The <address> argument is the address of the object to examine. If not specified then pinst will use a default address. If you are debugging a C method, then the oself value will be used. Otherwise, *DS:SI is assumed to be an object.
  • This command is useful for classes you've created and you are not interested in the data in the master levels which pobject would display.
  • The following special values are accepted for <address>:
    • -a
    the current patient's application object
    -i
    the current "implied grab": the windowed object over which the mouse is currently located.
    -f
    the leaf of the keyboard-focus hierarchy
    -t
    the leaf of the target hierarchy
    -m
    the leaf of the model hierarchy
    -c
    the content for the view over which the mouse is currently located
    -kg
    the leaf of the keyboard-grab hierarchy
    -mg
    the leaf of the mouse-grab hierarchy
  • pinst prints out the same information as "pobj l".

See Also: pobject, piv.

piv

piv <master> <iv> [<address>]

Examples:

"piv Vis VCNI_viewHeight"
print Vis.VCNI_viewHeight at *DS:SI

This prints out the value of the instance variable specified.

  • The <master> argument expects the name of a master level. The name may be found using pobject to print the levels, and then using the name that appears after "master part:" and before the "_offset".
  • The <iv> argument expects the name of the instance variable to print.
  • The <address> argument is the address of the object to examine. If not specified then *DS:SI assumed to be an object.
  • This command is useful for when you know what instance variable you want to see but don't want to wade through a whole pobject command.

See Also: pobject, pinst.

Prints a keyboard map in assembly-language format.

plines

plines <start> [<obj-address>]

Examples:

"plines 12"
Print lines starting at line 12.
"plines 12 ^l6340h:0020h"
Print lines starting at line 12 of object at given address.

Print information about the lines in a text object.

The printed line-starts are not correct.

See Also: ptext.

plist

Prints out a list of structures stored in an lmem chunk. It takes two arguments, the structure type that makes up the list, and the lmem handle of the chunk. e.g. plist FontsInUseEntry ds:di

pncbitmap

pncbitmap <address> <width> <height> [<no space flag>]

Examples:

"pncbitmap *ds:si 64 64 t"
print the bitmap without spaces

Print out a one-bitdeep noncompacted bitmap.

  • The <address> argument is the address to the bitmap data.
  • The <width> argument is the width of the bitmap in pixels.
  • The <height> argument is the height of the bitmap in pixels.
  • The <no space flag> argument removes the space normally printed between the pixels. Anything (like `t') will activate the flag.

See Also: pcbitmap.

pnormal

pnormal [-v]

Examples:

"pnormal -v"
Print out verbose information about the current normal transfer item.

Prints out information about the current "normal" transfer item on the clipboard.

If you give the "-v" flag, this will print out the contents of the different transfer formats, rather than just an indication of their types.

See Also: pquick, print-clipboard-item.

pobjarray

pobjarray [<address>]

Examples:

"pobjarray"
Print the array of ODs at *ds:si.

Print out an array of objects.

See Also: pbody.

pobject

pobject [<address>] [<detail>]

Examples:

"pobj"
print the object at *ds:si from Gen down if Gen is one of its master levels; else, print all levels
"pobj *MyGenObject"
print MyGenObject from Gen down
"pobj Gen"
print the Gen level for the object at *ds:si
"pobj last"
print the last master level for the object at *ds:si
"pobj *MyObject"
all print all levels of MyObject
"pobj -i sketch"
print the master level headings of the windowed object at the mouse pointer
"pobj *MyObject FI_foo"
print the FI_foo instance variable for MyObject
"pobj HINT_FOO"
print the HINT_FOO variable data entry for the object at *ds:si
"pobj v"
print the variable data for the object at *ds:si

Print all or part of an object's instance and variable data.

  • The <address> argument is the address of the object to examine. If not specified then oself is used, unless the current function is written in assembly, in which case *DS:SI .
  • The following flag values are accepted in lieu of an address:
    • -a
    the current patient's application object
    -i
    the current "implied grab": the windowed object over which the mouse is currently located.
    -f
    the leaf of the keyboard-focus hierarchy
    -t
    the leaf of the target hierarchy
    -m
    the leaf of the model hierarchy
    -c
    the content for the view over which the mouse is currently located
    -kg
    the leaf of the keyboard-grab hierarchy
    -mg
    the leaf of the mouse-grab hierarchy
  • The detail argument specifies what information should be printed out about the object. If none is specified, all levels of the object from the Gen level down will be printed if Gen is one of the object's master levels; else, the whole object will be printed.
  • The following values are accepted for detail :
    • all (or a )
    all master levels
    last (or l )
    last master level only
    sketch (or s )
    master level headings only
    vardata (or v )
    vardata only
    a master level name
    an instance variable name
    a variable data entry name

See Also: pinst, piv, pvardata.

pobjmon

pobjmon [<address>] [<text only>]

Examples:

"pobjmon"
print the VisMoniker from the gentree object at *DS:SI
  • The <address> argument is the address of an object with a VisMoniker. If none is specified then *DS:SI is used.
  • The <text only> argument returns a shortened description of the structure. To set it use something other than `0' for the second argument.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.

See Also: pvismon, pobject, vistree, gup, gentree, impliedgrab, systemobj.

pod

pod <address>

Examples:

"pod ds:si"

Print in output descriptor format (^l<handle>:<chunk>) the address passed.

The address argument is the address of an object.

ppath

ppath (current|docClip|winClip) [<gstate>]

Examples:

"ppath"
print the current path of the GState in ^hdi
"ppath docClip ^hgstate"
print the document clip path of the GState with handle gstate .
"ppath winClip ds"
print the window clip path of the GState in the DS register.

Print the structure of a path.

Unique abbreviations for the path to be printed are allowed.

pquick

pquick [-v]

Examples:

"pquick -v"
Print out verbose information about the current quick transfer item.

Prints out information about the current "quick" transfer item on the clipboard.

If you give the "-v" flag, this will print out the contents of the different transfer formats, rather than just an indication of their types.

See Also: pnormal, print-clipboard-item.

precord

precord <type> <value> [<silent>]

Examples:

"precord GSControl c0h"
print the GSControl record with the top two bits set

Print a record using a certain value.

  • The <type> argument is the type of the record.
  • The <value> argument is the value of the record.
  • The <silent> argument will suppress the text indicating the record type and value. This is done by passing a non zero value like `1'. This is useful when precord is used by other functions.

See Also: print, penum.

preg

preg [-g] <addr>

Examples:

"preg *es:W_appReg"
Prints the application-defined clipping region for the window pointed to by es.
"preg -g ds:si"
Prints a "graphical" representation of the region beginning at ds:si

Decodes a graphics GEOS region and prints it out, either numerically, or as a series of x's and spaces.

  • This command can deal with parameterized regions. When printing a parameterized region with the -g flag, the region is printed as if it were unparameterized, with the offsets from the various PARAM constants used as the coordinates.
  • If no address is given, this will use the last-accessed address (as the "bytes" and "words" commands do). It sets the last-accessed address, for other commands to use, to the first byte after the region definition.

print

print <expression>

Examples:

"print 56h"
print the constant 56h in various formats
"print ax - 10"
print ax less 10 decimal
"print ^l31a0h:001eh"
print the absolute address of the pointer

Print the value of an expression.

  • The <expression> argument is usually an address that has a type or that is given a type by casting and may span multiple arguments. The contents of memory of the given type at that address is what's printed. If the expression has no type, its offset part is printed in both hex and decimal. This is used for printing registers, for example.
  • The first argument may contain the following flags (which start with `-'):
    • x
    integers (bytes, words, dwords if dwordIsPtr false) printed in hex
    d
    integers printed in decimal
    o
    integers printed in octal c bytes printed as characters (byte arrays printed as strings, byte variables/fields printed as character followed by integer equivalent)
    C
    bytes treated as integers
    a
    align structure fields
    A
    Don't align structure fields
    p
    dwords are far pointers
    P
    dwords aren't far pointers
    r
    parse regions
    R
    don't try to parse regions
  • These flags operate on the following Tcl variables:
    • intFormat
    A printf format string for integers.
    bytesAsChar
    Treat bytes as characters if non-zero.
    alignFields
    Align structure fields if non-zero.
    dwordIsPtr
    DWords are far pointers if non-zero.
    noStructEnum
    If non-zero, doesn't print the "struct", "enum" or "record" before the name of a structured/enumerated type -- just gives the type name.
    printRegions
    If non-zero, prints what a Region points to (bounds and so on).
    condenseSpecial
    If non-zero, condense special structures (Rectangles, OutputDescriptors, ObjectDescriptors, TMatrixes and all fixed-point numbers) to one line.
  • This does not print enumerations. Use penum for that.

See Also: precord, penum, _print.

print-cell

print-cell [row column <cfp ds:si>]

Examples:

"print-cell 1 1"
print the cell <1,1>
"print-cell 1 2 *ds:si"
print the cell <1,2> given *DS:SI

Print information about a cell

See Also: print-row, print-row-block, print-cell-params, print-column-element.

print-cell-params

print-cell-params [<address>]

Examples:

"print-cell-params"
print the CellFunctionParameters at ds:si.
"print-cell-params ds:bx"
print the CellFunctionParameters at ds:bx.

Print a CellFunctionParameters block.

See Also: print-row, print-column-element, print-row-block, print-cell.

print-clipboard-item

print-clipboard-item [-v] <vmfile> <vmblock>
print-clipboard-item [-v] <memhandle>
print-clipboard-item [-v] <addr>

Examples:

"print-clipboard-item bx"
Print out info about the transfer item whose memory handle is in the BX register.

Prints out information about a transfer item.

  • If you give the "-v" flag, this will print out the contents of the different transfer formats, rather than just an indication of their types.
  • The -v flag will not work unless the transfer item is in a VM file.

See Also: pnormal, pquick.

print-column-element

print-column-element [<address>]

Examples:

"print-column-element"
Print the ColumnArrayElement at ds:si.
"print-column-element ds:bx" print the ColumnArrayElement at ds:bx

Print a single ColumnArrayElement at a given address.

print-db-group

print-db-group file group

Examples:

"print-db-group ax bx"
print the group at bx/ax.

Print information about a dbase group block.

See Also: print-db-item.

print-db-item

print-db-item file group item

Examples:

"print-db-item bx ax di"
print the item at bx/ax/di

Print information about a single dbase item

See Also: print-db-group.

print-eval-dep-list

print-eval-dep-list [<addr>]

Examples:

"print-eval-dep-list es:0"
Print dependency list at ES:0.

Prints a dependency list used for evaluation.

See Also: content, pcelldeps.

printNamesInObjTrees

var printNamesInObjTrees (0|1)

Examples:

"var printNamesInObjTrees 1"
Sets "gentree," "vistree," etc. commands to print object names (where available).

Determines whether object names are printed (where available) rather than class names when using the following commands: vistree, gentree, focus, target, model, mouse, keyboard.

The default value for this variable is zero.

See Also: gentree, vistree, focus, target, model, mouse, keyboard.

print-obj-and-method

print-obj-and-method <handle> <chunk> <message> [<cx> [<dx>
                                                   [<bp> [<class>]]]]

Examples:

"print-obj-and-method [read-reg bx] [read-reg si]"
Prints a description of the object ^lbx:si with the value stored and a hex representation.
"print-obj-and-method $h $c $m [read-reg cx] [read-reg dx] [read-reg bp]"
Prints a description of the object ^l$h:$c and the name of the message whose number is in $c. This is followed by the three words of data in cx, dx, and bp.

Prints a nicely formatted representation of an object, with option message, register data, label, hex address, & carriage return. The class indication may also be overriden.

  • You may specify anywhere from 0 to 5 arguments after the message number. These are interpreted as the value of the message, the registers CX, DX and BP, and the symbol token of the class to print, respectively.
  • All arguments must be integers, as this is expected to be called by another procedure, not by the user, so the extra time required to call getvalue would normally be wasted. (The user should call pobj , gup , or other such functions for this sort of print out.)

See Also: mwatch, map-method, objwatch.

printRegions

var printRegions [(0|1)]

Examples:

"var printRegions 1"
If a structure contains a pointer to a region, "print" will attempt to determine its bounding box.

Controls whether "print" parses regions to find their bounding rectangle.

The default value for this variable is one.

See Also: print, condenseSpecial.

print-row

print-row [<address *DS:SI>]

Examples:

"print-row"
print the row at *DS:SI
"print-row ds:si"
print the row at DS:SI

Print a single row in the cell file given a pointer to the row.

See Also: print-column-element, print-cell-params, print-row-block, print-cell.

print-row-block

print-row-block [<address ds>]

Examples:

"print-row-block"
print the row-block at DS:0
"print-row-block es"
print the row-block at ES:0

Print a row-block.

See Also: print-row, print-cell-params, print-column-element, print-cell.

printStop

This variable controls how the current machine state is printed each time the machine comes to a complete stop. Possible values:

asm
Print the current assembly-language instruction, complete with the values for the instruction operands.
src
Print the current source line, if it's available. If the source line is not available, the current assembly-language instruction is displayed as above.
why
Print only the reason for the stopping, not the current machine state. "asm" and "src" modes also print this.
nil
Don't print anything.

procmessagebrk

procmessagebrk [<handle>]

Examples:

"procmessagebrk MyObj"
break whenever a message is sent to MyObj
"procmessagebrk"
stop intercepting messages

Break whenever a message is sent to a particular process via ObjMessage.

  • The <handle> argument is the handle to a process to watch for messages being sent to it. If no argument is specified then the watching is stopped. The process' handle may be found by typing "ps -p". The process's handle is the number before the process's name.
  • This command breaks whenever a message is sent (before they get on the message queue. This enables one to track identical messages to a process which can be removed.

See Also: objwatch, mwatch, objmessagebrk, pobject.

ps

ps [<flags>]

Examples:

"ps -t"
list all threads in GEOS.

Print out GEOS' system status.

The flags argument may be one of the following:

-t
Prints out info on all threads. May be followed by a list of patients whose threads are to be displayed.
-p
Prints out info on all patients. May be followed by a list of patients to be displayed.
-h
Prints out info on all handles. May be followed by a list of patients whose handles are to be displayed.
The default is `-p'.

See Also: switch, sym-default.

pscope

pscope [<scope-name> [<sym-class>]]

Examples:

"pscope WinOpen"
Prints out all the local labels, variables, and arguments within the WinOpen() procedure

This prints out all the symbols contained in a particular scope.

  • This can be useful when you want to know just the fields in a structure, and not the fields within those fields, or if you know the segment in which a variable lies, but not its name. Admittedly, this could be overkill.
  • sym-class can be a list of symbol classes to restrict the output. For example, "pscope Filemisc proc" will print out all the procedures within the Filemisc resource.

See Also: whatis, locals.

psize

psize <structure>

Examples: "psize FontsInUseEntry"

Print the size of the passed structure.

pssheet

pssheet [-isSfrcvd] <address>

Examples:

"pssheet -s ^l3ce0h:001eh"
print style attributes.
"pssheet -f -i 94e5h:0057h"
print file info from instance data.

Prints out information about a spreadsheet object.

  • If you are in the middle of debugging a spreadsheet routine and have a pointer to the Spreadsheet instance, the "-i" flag can be used to specify the object using that pointer.
  • If you simply have the OD of the spreadsheet object, use that.
  • Alternatively, you can do:
 pssheet <flags> [targetobj]

See Also: content, targetobj.

psup

psup [<object>]

Examples:

"psup"
print superclasses of object at *ds:si.
"psup -i"
print superclasses of object under mouse.
"psup ^l4e10h:20h"
print superclasses of object at ^l4e10h:20h.

Prints superclasses of an object.

If no object is specified, *ds:si is used.

See Also: is-obj-in-class.

ptext

ptext [<options>] <addr>

Examples:

"ptext"
Prints the text in the object for the method being executed in the current stack frame.

Prints out the text and related structures for a text object

The flag may be one or more of the following:

- c
Print out the char attr structures.
- e
Print out elements in addition to runs.
- f < field >
Print out given field of each element (default is Meta part).
- g
Print out graphics structures.
- l
Print out line and field structures.
- p
Print out para attr structures.
- r
Print out region attr structures.
- s
Print out style structures.
- t
Print out type structures.
- E
Limit printout to just the elements of whatever arrays are requeste. Do not attempt to print out associated text.
- N
Print out associated names.
- R
Print out full region descriptions.

pthread

pthread <id>

Examples:

"pthread 16c0h"
Prints information about the thread whose handle is 16c0h.

Provides various useful pieces of information about a particular thread including its current priority and its current registers.

<id> is the thread's handle ID, as obtained with the "ps -t" or "threadstat" command.

See Also: threadstat.

ptimer

ptimer <handle>

Examples:

"ptimer bx"
Print out information about the timer whose handle is in the BX register.

Prints out information about a timer registered with the system: when it will fire, what it will do when it fires, etc.

<handle> may be a variable, register, or constant.

See Also: twalk, phandle.

ptrans

ptrans [<flags>] [<address>]

Examples:

"ptrans"
print the normal transform for the object at *ds:si.
"ptrans -s"
print the sprite transform for the GrObj object at *ds:si.
"ptrans ^lbx:cx"
print the normal transform for the object whose OD is ^lbx:cx.

Prints the ObjectTransform data structure as specified.

  • The -s flag can be used to print the "sprite" transform (the "sprite" is the shape's outline which is drawn to give feedback to the user when said user is moving/rotating/etc. the GrObj).
  • <address> defaults to *ds:si

See Also: pobject.

ptreg

ptreg <start> [<obj-addr>]

Examples:

"ptreg 12"
Print lines for region 12
"ptreg 12 ^lcx:dx"
Print lines for region 12 of object ^lcx:dx

Print information about the lines in a region.

See Also: ptext.

pusage

pusage [<address>]

Examples:

"pusage"
print the usage of characters in the font

List the characters in a font and when they were last used.

  • The <address> argument is the address of a font. If none is given then ^hbx is used.

See Also: fonts, pfont, pfontinfo, plist.

pvardata

pvardata [<entry>]

Examples:

"pvardata ds:si"
Prints vardata of object at *ds:si
"pvardata -i"
Prints vardata of object with implied grab.
  • The address argument is the address of an object with variable data. The default is *ds:si.

pvardentry

pvardentry <address> <object>

Examples:

"pvardentry ds:bx *ds:si"
  • The address argument is the address of a variable data entry in an object's variable data storage area. The default is ds:bx.
  • The <object> argument is required to determine the name of the tag for the entry, as well as the type of data stored with it.

pvis

pvis <element> [<object>]

Examples:

"pvis VI_bounds @65"
print the bounds of object 65
"pvis VI_optFlags"
print the flags of the object at *DS:SI
"pvis VI_attrs -i"
print the attributes of the object at the implied grab

Print an element of the visual instance data.

  • The <element> argument specifies which element in the object to print
  • The <object> argument is the address to the object to print out. It defaults to *DS:SI and is optional. The `-i' flag for an implied grab may be used.

See Also: vistree, vup, pobject, piv, pgen.

pvismon

pvismon [<address>] [<text only>]

Examples:

"pvismon"
print the moniker at *DS:SI
"pvismon -i 1"
print a short description of the implied grab object.

Print a visual moniker structure at an absolute address.

  • The <address> argument is the address to an object in the visual tree. This defaults to *DS:SI. The `-i' flag for an implied grab may be used.
  • The <text only> argument returns a shortened description of the structure. Pass a non-zero value to turn on this flag.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.

See Also: pobjmon, pobject, vistree, gup, gentree, impliedgrab, systemobj.

pvmb

Prints out the VMBlockHandle for a VM block given the file handle H and the VM block handle B .

pvmt

pvmt [-p] [-a] [-s] [-c] (<handle> | <segment>)

Examples:

"pvmt bx"
Print out all used blocks for the open VM file whose file handle is in BX.
"pvmt -as ds"
Print out all blocks for the open VM file the segment of whose header block is in DS.

Prints out a map of the VM block handles for a VM file.

  • The -p flag will only print out blocks that have the Preserve flag set. Useful for examining object blocks in GeoCalc files, for example.
  • The -a flag causes pvmt to print out all block handles, not just those that have been allocated. The other two types of block handles are "assigned" (meaning they're available for use, but currently are tracking unused space in the file) and "unassigned" (they're available for use).
  • The -s indicates the final argument is a segment, not a file handle. This is used only if you're inside the VM subsystem of the kernel.
  • The -c flag requests a count of the different types of blocks at the end of the printout.
  • The blocks are printed in a table with the following columns:
    • han
    VM block handle (in hex)
    flags D if the block is dirty,
    C if the block is clean,
    - if the block is non-resident,
    L if the block is LMem,
    B if the block has a backup,
    P if the preserve handle bit is set for the block,
    ! if the block is locked
    memhan
    Associated memory handle. Followed by "(d)" if the memory for the block was discarded but the handle retained. Followed by (s) if the memory has been swapped out.
    block type
    The type of block:
    VMBT_USED a normal in-use block,
    VMBT_DUP an in-use block that has been backed up or allocated since the last call to VMSave()
    VMBT_BACKUP a place-holder to keep track of the previous version of a VMBT_DUP block. The uid is the VM block handle to which the file space used to belong.
    VMBT_ZOMBIE a block that has been freed since the last VMSave() . The handle is preserved in case of a VMRevert() (a VMBT_BACKUP block retains the file space).
    uid
    The "used ID" bound to the block.
    size
    Number of bytes allocated for the block in the file.
    pos
    The position of those bytes in the file.

See Also: pgs.

pvsize

pvsize [<object>]

Examples:

"pvsize"
print the dimensions of the visual object at *ds:si.

Print out the dimensions of a visual object.

  • The object argument is the address to the object to print out. It defaults to *ds:si and is optional. The `-i' flag for an implied grab may be used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.

pwd

pwd

Examples: "pwd"

Prints the current working directory for the current thread.

See Also: dirs, stdpaths.

quit

quit [<options>]

Examples:

"quit cont"
continue GEOS and quit swat
"quit det"
detach from the PC and quit swat.

Stop the debugger and exit.

  • The <option> argument may be one of the following:
    continue : continue GEOS and exit swat;
    leave : keep GEOS stopped and exit swat.
  • Anything else causes swat to detach and exit.

See Also: detach.

read-char

read-char [<echo>]

Examples:

"read-char 0"
Read a single character from the user and don't echo it.

Reads a character from the user.

If <echo> is non-zero or absent, the character typed will be echoed.

See Also: read-line.

read-line

read-line [<isTcl> [<initial input> [<special chars>]]]

Examples:

"read-line"
reads a single line of text.
"read-line 1"
reads a Tcl command.
"read-line 1 {go}"
reads a Tcl command that starts with "go "
"read-line 1 {} {\e\4}"
reads a Tcl command, considering escape and control-d cause for immediate return, regardless of whether braces and brackets are balanced

Reads a single line of input from the user. If optional argument is non-zero, the line is interpreted as a Tcl command and will not be returned until all braces/brackets are balanced. The final newline is stripped. Optional second argument is input to be placed in the buffer first. This input must also be on-screen following the prompt, else it will be lost.

  • If <isTcl> is non-zero, the input may span multiple lines, as read-line will not return until all braces and brackets are properly balanced, according to the rules of Tcl. This behavior may be overridden by the <special chars> argument.
  • If <initial input> is given and non-empty, it is taken to be the initial contents of the input line and may be edited by the user just as if s/he had typed it in. The string is not automatically displayed; that is up to the caller.
  • <special chars> is an optional string of characters that will cause this routine to return immediately. The character that caused the immediate return is left as the last character of the string returned. You may use standard backslash escapes to specify the characters. This will return even if the user is entering a multi-line Tcl command whose braces and brackets are not yet balanced.
  • The user's input is returned as a single string with the final newline stripped off.

read-reg

read-reg <register>

Examples:

"read-reg ax"
return the value of AX
"read-reg CC"
return the value of the conditional flags

Return the value of a register in decimal.

  • The <register> argument is the two letter name of a register in either upper or lower case.

See Also: frame, assign, setcc, clrcc.

regs

regs

Print the current registers, flags, and instruction.

See Also: assign, setcc, clrcc, read-reg.

regwin

regwin [off]

Examples: "regwin"
"regwin off"

Turn the continuous display of registers on or off.

  • If you give the optional argument "off", you will turn off any active register display.
  • If you give no argument, the display will be turned on.
  • Only one register display may be active at a time.

See Also: display.

repeatCommand

var repeatCommand <string>

Examples:

"var repeatCommand [list foo nil]"
Execute the command "foo nil" if the user just hits <Enter> at the next command prompt.

This variable holds the command Swat should execute if the user enters an empty command. It is used by all the memory-referencing commands to display the next chunk of memory, and can be used for other purposes as well.

  • repeatCommand is emptied just before top-level-read returns the command the interpreter should execute and must be reset by the repeated command if it wishes to continue to be executed when the user just hits <Enter>.
  • The text of the current command is stored in lastCommand , should you wish to use it when setting up repeatCommand .

See Also: target, focus, mouse, keyboard.

require

require <name> [<file>]

Examples:

"require fmtval print"
Ensure the procedure "fmtval" is defined, loading the file "print.tcl" if it is not.

This ensures that a particular function, not normally invoked by the user but present in some file in the system library, is actually loaded.

If no <file> is given, a file with the same name (possibly suffixed ".tcl") as the function is assumed.

See Also: autoload.

resize

resize <window> <numLines>

Examples:

"resize varwin 5"
Resizes the varwin to 5 lines.
"resize va 5"
Resizes the varwin to 5 lines.

Resizes the source window or a varwin or localwin.

  • The window argument is the name of the window to be resized, or a unique abbreviation of that name. Possible names are: "varwin", "localwin", "view", "doc", and "srcwin"; the last three all refer to the source window.
  • The numLines argument is the desired window size.
  • This command will not resize a flagwin, regwin, or framewin, as the optimal size of those windows does not vary.

restore-state

restore-state

Examples:

"restore-state"
Set all registers for the current thread to the values saved by the most recent save-state.

Pops all the registers for a thread from the internal state stack.

  • This is the companion to the "save-state" command.
  • All the thread's registers are affected by this command.

See Also: save-state.

ret

ret [<function name>]

Examples: "ret"
"ret ObjMessage"

Return from a function and stop.

  • The <function name> argument is the name of a function in the patient's stack after which swat should stop. If none is specified then Swat returns from the current function.
  • The function returned from is the first frame from the top of the stack which calls the function (like the "finish" command).
  • This command does not force a return. The machine continues until it reaches the frame above the function.

See Also: finish, backtrace.

return-to-top-level

return-to-top-level

Examples:

"return-to-top-level"
Returns to the top-level interpreter.

Forces execution to return to the top-level interpreter loop, unwinding intermediate calls (protected commands still have their protected clauses executed, but nothing else is).

See Also: top-level, protect.

rs

rs

Examples:

"rs"
restart GEOS without attaching

Restart GEOS without attaching.

See Also: att, attach.

run

run [<patient-name>]

Examples:

"run uki"
Run the application with patient name "uki".
"run -e uki"
run EC Uki
"run -n uki"
run non-EC Uki
"run -p games\ukiec.geo "
run games\ukiec.geo
"run"
run the default patient, as specified by the patient-default command.

"Runs" an application by loading it via a call to UserLoadApplication() and stopping when the app reaches the GenProcess handler for MSG_META_ATTACH . Return patient created, if any (In the examples shown, this would be "uki").

  • May be used even if stopped inside the loader, in which case GEOS will be allowed to continue starting up, and the specified app run after GEOS is Idle.
  • If the machine stops for any other reason other than the call's completion, you are left wherever the machine stopped.

See Also: patient-default, send, spawn, switch.

rwatch

rwatch [(on|off)]

Examples:

"rwatch on"
Watch text-recalculation as it happens
"rwatch off"
Turn output off
"rwatch"
See what the status is

Displays information about text recalculation. Specifically designed for tracking bugs in the rippling code.

See Also: ptext.

save

save (<#lines>|<filename>)

Examples:

"save 500"
Save the last 500 lines that scroll off the screen.
"save /dumps/puffball"
Save the contents of the entire scroll buffer to the file "puffball".

Controls the scrollback buffer Swat maintains for its main command window.

  • If the argument is numeric, it sets the number of lines to save (the default is 1,000).
  • If the argument is anything else, it's taken to be the name of a file in which the current buffer contents (including the command window) should be saved. If the <filename> is relative, it is taken relative to the directory in which the executable for the patient to which the current stack frame's function belongs is located. If the file already exists, it is overwritten.

save-state

save-state

Examples:

"save-state"
Push the current register state onto the thread's state stack.

Records the state of the current thread (all its registers) for later restoration by "restore-state".

  • Swat maintains an internal state stack for each thread it knows, so calling this has no effect on the target PC.
  • This won't save any memory contents, just the state of the thread's registers.

See Also: restore-state, discard-state.

sbwalk

sbwalk [<patient>]

Examples:

"sbwalk"
list the saved blocks of the current patient.
"sbwalk geos"
list the saved blocks of the GEOS patient.

List all the saved blocks in a patient.

  • The <patient> argument is any GEOS patient. If none is specified then the current patient is used.

scope

scope [<scope-name>]

Examples:

"scope"
Returns the name of the current auxiliary scope.

This changes the auxiliary scope in which Swat looks first when trying to resolve a symbol name in an address expression.

  • This command isn't usually typed by users, but it is the reason you can reference local labels after you've listed a function unrelated to the current one.
  • You most likely want to use the set-address Tcl procedure, rather than this command.
  • If <scope-name> is "..", the auxiliary scope will change to be the lexical parent of the current scope.

See Also: set-address, addr-parse, whatis.

screenwin

screenwin

Print the address of the current top-most screen window.

send

send [-enpr] <geode-name>

Examples:

"send icon"
send EC Icon Editor if running in EC mode; send non-EC Icon Editor if running in non-EC mode.
"send -r icon"
send appropriate icon editor, then run it. (See documentation for "run" above.)
"send -e icon"
send EC Icon editor.
"send -n icon"
send non-EC Icon Editor
"send -p c:/pcgeos/appl/icon/icon.geo"
send c:/pcgeos/appl/icon/icon.geo
"send"
send the default patient (as set by the patient-default command).

Send a geode from the host to target machine.

send-file

send-file <file> <targfilename>

Examples:

"send-file /pcgeos/appl/sdk/mess1/mess1.geo WORLD/soundapp.geo"
Send the mess1.geo file on the host machine to the WORLD directory of the host machine, where it will be called soundapp.geo .

Sends a file from the host machine to the target.

set-address

Set the last-accessed address recorded for memory-access commands. Single argument is an address expression to be used by the next memory-access command (except via <return>).

setcc

setcc <flag> [<value>]

Examples:

"setcc c"
set the carry flag
"setcc z 0"
clear the zero flag

Set a flag in the computer.

  • The first argument is the first letter of the flag to set. The following is a list of the flags:
    • t
    trap
    i
    interrupt enable
    d
    direction
    o
    overflow
    s
    sign
    z
    zero
    a
    auxiliary carry
    p
    parity
    c
    carry
  • The second argument is the value to assign the flag. It defaults to one but may be zero to clear the flag.

See Also: clrcc, compcc.

set-masks

set-masks <mask1> <mask2>

Examples:

"set-masks 0xff 0xff"
Allow no hardware interrupts to be handled while the machine is stopped.

Sets the interrupt masks used while the Swat stub is active. Users should use the "int" command.

  • <mask1> is the mask for the first interrupt controller, with a 1 bit indicating the interrupt should be held until the stub returns the machine to GEOS. <mask2> is the mask for the second interrupt controller.
  • These masks are active only while the machine is executing in the stub, which usually means only while the machine is stopped.

See Also: int.

set-repeat

Sets the command to be repeated using a template string and the lastCommand variable. The variables $0...$n substitute the fields 0...n from the lastCommand variable. The final result is placed in repeatCommand which will be executed should the user type Enter.

set-startup-ec

set-startup-ec [<args>]

Examples:

"set-startup-ec +vm"
turn on VM error checking when starting up
"set-startup-ec none"
turn off all ec code when starting up

Executes the "ec" command upon startup, to allow one to override the default error checking flags.

See Also: ec.

sftwalk

sftwalk

Examples: "sftwalk"

Print the system file table out by blocks.

  • This is different than sysfiles in that it shows less details of the files and instead shows where the SFT blocks are and what files are in them.

See Also: sysfiles, geosfiles, fwalk.

showcalls

showcalls [<flags>] [<args>]

Examples:

"showcalls -o"
show all calls using ObjMessage and ObjCall*
"showcalls -ml"
show all calls changing global and local memory
"showcalls"
stop showing any calls

Display calls to various parts of GEOS.

  • The <flags> argument determines the types of calls displayed. Multiple flags must all be specified in the first argument such as "showcalls -vl". If no flags are passed then showcalls stops watching. The flags may be any of the following:
    • -p
    Modify all other flags to work for the current patient only
    -a
    Show calls associated with attaching, detaching, state-saving, restoring from state
    -b
    Monitors builds of Vis objects
    -d
    Show dispatching of threads
    -e
    Show FOCUS, TARGET, MODAL . exclusive grabs & releases
    -f
    Show certain file operations
    -g
    Show geometry manager resizing things (all sizes in hex)
    -h
    Show all hierarchical exclusive grabs and releases (associated with focus, target, and model).
    -i
    Show call far calls made to movable routines.
    -l
    Show local memory create, destroy, relocate
    -m
    Show global memory alloc, free, realloc
    -o
    Show ObjMessage() and ObjCall ... ()
    -s
    Monitors shutdown: MSG_DETACH , DETACH_COMPLETE , ACK , DETACH_ABORT
    -t
    Show clipboard activity
    -v
    Show video driver calls
    -w
    Show WinOpen() , WinClose() , WinMoveResize() , WinChangePriority() .
    -F
    Show file-change notifications produced by the system.
    -G
    Show GrCreateState() and GrDestroyGState() .
    -H
    Show heap space allocation (using .gp heapspace values, not actual allocation values)
    -I
    Show invalidation mechanism at work.
    -L
    Show library loading calls
    -N
    Show navigation calls (between fields, and between windows)
    -S
    Show stack borrowing activity.
    -T
    Show text-object related information.
    -V
    Show loading and unloading of resources to and from statefiles during shutdown.
  • The<args> argument is used to pass values for some of options.

See Also: mwatch, objwatch.

showMethodNames

var showMethodNames

If this variable is non-zero, Swat prints out the names of the method in the AX register when unassembling a message call.

skip

skip [<number of instructions>]

Examples:

"skip"
skip the current instruction
"skip 6"
skip the next six instructions

Skip one or more instructions.

  • The <number of instructions> argument defaults to one if not specified.

See Also: istep, sstep, patch.

sleep

sleep <seconds>

Examples:

"sleep 5"
Pauses Swat for 5 seconds.

This pauses Tcl execution for the given number of seconds, or until the user types Ctrl-C.

  • Messages from the PC continue to be processed, so a FULLSTOP event will be dispatched if the PC stops, but this command won't return until the given length of time has elapsed.
  • <seconds> is a real number, so "1.5" is a valid argument.
  • Returns non-zero if it slept for the entire time, or 0 if the sleep was interrupted by the user.

slist

slist [<args>]

Examples:

"slist"
list the current point of execution
"slist foo.asm::15"
list foo.asm at line 15
"slist foo.asm::15,45"
list foo.asm from lines 15 to 45

List source file lines in swat.

  • The args argument can be any of the following:
    • <address>
    Lists the 10 lines around the given address
    <line>
    Lists the given line in the current file
    <file>::<line>
    Lists the line in the given file
    <line1>,<line2>
    Lists the lines between line1 and line2, inclusive, in the current file
    <file>::<line1>,<line2>
    Lists the range from <file>
  • The default is to list the source lines around CS:IP.

See Also: listi, istep, regs.

smatch

Look for symbols of a given class by pattern. First argument <pattern> is the pattern for which to search (it's a standard Swat pattern using shell wildcard characters). Optional second argument <class> is the class of symbol for which to search and is given directly to the "symbol match" command. Defaults to "any".

sort

sort [-r] [-n] [-u] <list>

Examples:

"sort -n $ids"
Sorts the list in $ids into ascending numeric order.

This sorts a list into ascending or descending order, lexicographically or numerically.

  • If "-r" is given, the sort will be in descending order.
  • If "-u" is given, duplicate elements will be eliminated.
  • If "-n" is given, the elements are taken to be numbers (with the usual radix specifiers possible) and are sorted accordingly.
  • The sorted list is returned.

See Also: map, foreach, mapconcat.

spawn

spawn <processName> [<addr>]

Set a temporary breakpoint in a not-yet-existent process/thread, waiting for a new one to be created. First argument is the permanent name of the process to watch for. Second argument is an address expression specifying where to place the breakpoint. If no second argument is present, the machine will be stopped and Swat will return to the command level when the new thread is spawned by GEOS.

  • This can also be used to catch the spawning of a new thread.
  • If the machine stops before the breakpoint can be set, you'll have to do this again.

srcwin

srcwin <numLines>

Examples:

"srcwin 6"
Show 6 lines of source context around CS:IP
"srcwin 0"
Show no source lines, i.e. turn the display off.

Set the number of lines of source code to be displayed when the target machine stops.

  • Only one source display may be active at a time.

See Also: display, regwin.

sstep

sstep [<default command>]

Examples:

"ss"
enter source step mode
"sstep n"
enter source step mode, <ret> does a next command

Step through the execution of the current patient by source lines. This is THE command for stepping through high-level (e.g., C) code.

  • The <default> command argument determines what pressing the <Return> key does. By default, <Return> executes a step command. Any other command listed below may be substituted by passing the letter of the command.
  • Sstep steps through the patient line by line, printing where the instruction pointer is and what line is to be executed Sstep waits for the user to type a command which it performs and then prints out again where sstep is executing.
  • This is a list of sstep commands:
    • q , < Esc >,` `
    Stops sstep and returns to command level.
    b
    Toggles a breakpoint at the current location.
    c
    Stops sstep and continues execution.
    n
    Continues to the next source line, skipping procedure calls, repeated string instructions, and software interrupts. Only stops when the machine returns to the right context (i.e. the stack pointer and current thread are the same as they are when the `n' command was given).
    l
    Goes to the next library routine.
    N
    Like n, but stops whenever the breakpoint is hit, whether you're in the same frame or not.
    M
    Goes to the next message called. Doesn't work when the message is not handled anywhere.
    f
    Finishes out the current stack frame.
    s , < Ret >
    Steps one source line
    S
    Skips the current instruction
    J
    Jump on a conditional jump, even when "Will not jump" appears. This does not change the condition codes.
    g
    Executes the `go' command with the rest of the line as arguments.
    e
    Executes a Tcl command and returns to the prompt.
    R
    References either the function to be called or the function currently executing.
    h , ?
    A help message.
  • Emacs will load in the correct file executing and following the lines where sstep is executing if its server is started and if ewatch is on in swat. If ewatch is off emacs will not be updated.
  • If the current patient isn't the actual current thread, sstep waits for the patient to wake up before single-stepping it.

See Also: istep, listi.

stdpaths

stdpaths

Examples: "stdpaths"

Print out all paths set for standard directories

See Also: pwd, dirs.

step

step

Examples:

"step"
execute the next instruction
"s"

Execute the patient by a single machine instruction.

  • If waitForPatient is non-zero, step waits for the machine to stop again.
  • This doesn't do any of the checks for special conditions (XchgTopStack, software interrupts, etc.) performed by the `s' command in istep.

See Also: istep, next.

step-patient

step-patient

Examples:

"step-patient"
Execute a single instruction on the target PC.

Causes the PC to execute a single instruction, returning only when the instruction has been executed.

  • Unlike the continue-patient command, this command will not return until the machine has stopped again.
  • No other thread will be allowed to run, as timer interrupts will be turned off while the instruction is being executed.

See Also: help-fetch.

step-until

step-until expression [byte|word]

Examples:

"step-until ax=0"
Single-step until ax is zero.
"step-until ds:20h!=0 byte"
Single-step until byte at ds:20h is non-zero
"step-until ds:20h!=0 word"
Single-step until word at ds:20h is non-zero
"step-until c=0"
Single-step until the carry is clear
"step-until ax!=ax"
Step forever

This command causes Swat to step until a condition is met.

Useful for tracking memory or register trashing bugs.

stop

stop in <class>::<message> [if <expr>]
stop in <procedure> [if <expr>]
stop in <address-history-token> [if <expr>]
stop at [<file>:]<line> [if <expr>]
stop <address> [if <expr>]

Examples: "stop in main"
"stop in @3"
"stop at /staff/pcgeos/Loader/main.asm:36 if { joe_local ==22}"
"stop at 25"
"stop MemAlloc+3 if {ax==3}"

Specify a place and condition at which the machine should stop executing. This command is intended primarily for setting breakpoints when debugging a geode created in C or another high-level language, but may also be used when debugging assembly-language geodes.

  • "stop in" will set a breakpoint at the beginning of a procedure, immediately after the procedure's stack frame has been set up.
  • "stop at" will set a breakpoint at the first instruction of the given source line. If no <file> is specified, the source file for the current stack frame is used.
  • If a condition is specified, by means of an "if <expr>" clause, you should enclose the expression in {}'s to prevent any nested commands, such as a "value fetch" command, from being evaluated until the break-point is hit.
  • For convenience, "stop in" also allows address-history tokens. This is useful when used in conjunction with the "methods" command.

See Also: brk, ibrk.

stop-catch

stop-catch <body>

Examples:

"stop-catch {go ProcCallModuleRoutine}"
Let machine run until it reaches ProcCallModuleRoutine() , but do not issue a FULLSTOP event when it gets there.

Allows a string of commands to execute without a FULLSTOP event being generated while they execute.

A number of things happen when a FULLSTOP event is dispatched, including notifying the user where the machine stopped. This is inappropriate in something like "istep" or "cycles" that is single-stepping the machine, for example.

See Also: event, continue-patient, step-patient.

stop-patient

stop-patient

Examples:

"stop-patient"
Stops the target PC.

Stops the target PC, in case you continued it and didn't wait for it to stop on its own.

This is different from the "stop" subcommand of the "patient" command.

See Also: continue-patient.

stream

stream open <file> (r|w|a|r+|w+)
stream read (line|list|char) <stream>
stream print <list> <stream>
stream write <string> <stream>
stream rewind <stream>
stream seek (<posn>|+<incr>|-<decr>|end) <stream>
stream state <stream>
stream eof <stream>
stream close <stream>
stream flush <stream>
stream watch <stream> <what> <procName>
stream ignore <stream>

Examples:

"var s [stream open kmap.def w]"
Open the file "kmap.def" for writing, creating it if it wasn't there before, and truncating any existing file.
"stream write $line $s"
Write the string in $line to the open stream.

This allows you to read, write, create, and otherwise manipulate files on the host machine from Swat.

  • Subcommands may be abbreviated uniquely.
  • Streams are a precious resource, so you should be sure to always close them when you are done. This means stream access should usually be performed under the wings of a "protect" command so the stream gets closed even if the user types Ctrl+C.
  • Swat's current directory changes as you change stack frames, with the directory always being the one that holds the executable file for the patient to which the function in the current frame belongs. If the <file> given to "stream open" isn't absolute, it will be affected by this.
  • The global variable file-init-dir contains the absolute path of the directory in which Swat was started. It can be quite useful when forming the <file> argument to "stream open".
  • The second argument to "stream open" is the access mode of the file. The meanings of the 5 possible values are:
    • r
    read-only access. The <file> must already exist.
    w
    write-only access. If <file> doesn't already exist, it will be created. If it does exist, it will be truncated.
    a
    append mode. The file is opened for writing only. If <file> doesn't already exist, it will be created. If it does exist, writing will commence at its end.
    r+
    read/write. The <file> must already exist. A single read/write position is maintained, and it starts out at the start of the file.
    w+
    read/write. If <file> doesn't already exist, it will be created. If it does exist, it will be truncated. A single read/write position is maintained, and it starts out at the start of the file.
  • "stream read" can read data from the stream in one of three formats:
    • line
    Returns all the characters from the current position up to the first newline or the end of the file, whichever comes first. The newline, if seen, is placed at the end of the string as \n. Any other non-printable characters or backslashes are similarly escaped.
    list
    Reads a single list from the stream, following all the usual rules of Tcl list construction. If the character at the current read position is a left brace, this will read to the matching right brace, bringing in newlines and other whitespace. If there is whitespace at the initial read position, it is skipped. Standard Tcl comments before the start of the list are also skipped over (so if the first non-whitespace character encountered is #, the characters up to the following newline or end-of-file will also be skipped).
    char
    This reads a single character from the stream. If the character isn't printable ASCII, it will be returned as one of the regular Tcl backslash escapes.

    If there's nothing left to read, you will get an empty string back.

  • "stream write" writes the string exactly as given, without interpreting backslash escapes. If you want to include a newline or something of the sort in the string, you'll need to use the "format" command to generate the string, or place the whole thing in braces and have the newlines in there literally.
  • While the syntax for "stream print" is the same as for "stream write", there is a subtle difference between the two. "stream write" will write the string as it's given, while "stream print" is intended to write out data to be read back in by "stream read list". Thus the command
    •  stream write {foo biff} $s

    would write the string "foo biff" to the stream. In contrast,

     stream print {foo biff} $s

    would write "{foo biff}" followed by a newline.

  • To ensure that all data you have written has made it to disk, use the "stream flush" command. Nothing is returned.
  • "stream rewind" repositions the read/write position at the start of the stream. "stream seek" gives you finer control over the position. You can set the stream to an absolute position (obtained from a previous call to "stream seek") by passing the byte number as a decimal number. You can also move forward or backward in the file a relative amount by specifying the number of bytes to move, preceded by a "+", for forward, or a "-", for backward. Finally, you can position the pointer at the end of the file by specifying a position of "end".
  • "stream seek" returns the new read/write position, so a call of "stream seek +0 $s" will get you the current position without changing anything. If the seek couldn't be performed, -1 is returned.
  • "stream state" returns one of three strings: "error", if there's been some error accessing the file, "eof" if the read/write position is at the end of the file, or "ok" if everything's fine. "stream eof" is a shortcut for figuring if you've reached the end of the file.
  • "stream close" shuts down the stream. The stream token should never be used again.
  • "stream watch" and "stream ignore" are valid only on UNIX and only make sense if the stream is open to a device or a socket. "stream watch" causes the procedure <procName> to be called whenever the stream is ready for the access indicated by <what>, which is a list of conditions chosen from the following set:
    • read
    the stream has data that may be read.
    write
    the stream has room for data to be written to it.

    When the stream is ready, the procedure is called:

     <procName> <stream> <what>

    where <what> is the list of operations for which the stream is ready.

See Also: protect, source, file.

switch

switch <thread-id>
switch [<patient>] [:<thread-num>]

Examples:

"switch 3730h"
Switches swat's current thread to be the one whose handle ID is 3730h.
"switch :1"
Switches Swat's current thread to be thread number 1 for the current patient.
"switch parallel:2"
Switches Swat's current thread to be thread number 2 for the patient "parallel"
"switch write"
Switches Swat's current thread to be thread number 0 (the process thread) for the patient "write"
"switch"
Switches Swat's current thread to be the current thread on the PC.

Switches between applications/threads.

  • Takes a single argument of the form <patient>:<thread-num> or <threadID>. With the first form, :<thread-num> is optional -- if the patient has threads, the first thread is selected. To switch to another thread of the same patient, give just :<thread-num>. You can also switch to a patient/thread by specifying the thread handle ID. NOTE: The switch doesn't happen on the PC--just inside swat.
  • If you don't give an argument, it switches to the actual current thread in the PC.

symbolCompletion

var symbolCompletion [(0|1)]

Examples:

"var symbolCompletion 1"
Enable symbol completion in the top-level command reader.

This variable controls whether you can ask Swat to complete a symbol for you while you're typing a command. Completion is currently very slow and resource-intensive, so you probably don't want to enable it.

  • Even when symbolCompletion is 0, file-name, variable-name, and command- name completion are always enabled, using the keys described below.
  • When completion is enabled, three keys cause the interpreter to take the text immediately before the cursor and look for all symbols that begin with those characters. The keys are:
    • Ctrl+D
    Produces a list of all possible matches to the prefix.
    Escape
    Completes the command as best possible. If the characters typed so far could be the prefix for more than one command, Swat will fill in as many characters as possible.
    Ctrl+]
    Cycles through the list of possible symbols, in alphabetical order.

sym-default

sym-default [<name>]

Examples:

"sym-default motif"
Make swat look for any unknown symbols in the patient named "motif" once all other usual places have been searched.

Specifies an additional place to search for symbols when all the usual places have been searched to no avail.

  • The named patient need not have been loaded yet when you execute this command.
  • A typical use of this is to make whatever program you're working on be the sym-default in your .swat file so you don't need to worry about whether it's the current one, or reachable from the current one, when the machine stops and you want to examine the patient's state.
  • If you don't give a name, you'll be returned the name of the current sym-default.

sysfiles

sysfiles

Examples:

"sysfiles"
Print out all open files from dos's system file table.

Normally SFT entries that aren't in-use aren't printed. If you give the optional argument "all", however, all SFT entries, including those that aren't in-use, will be printed.

See Also: geosfiles, sftwalk, fwalk.

systemobj

systemobj

Examples:

"gentree [systemobj]"
print the generic tree starting at the system's root
"pobject [systemobj]"
print the system object

Prints out the address of the uiSystemObj, which is the top level of the generic tree.

  • This command is normally used with gentree as shown above to print out the whole generic tree starting from the top.

See Also: gentree, impliedgrab.

target

target [<object>]

Examples:

"target"
print target hierarchy from the system object down
"target -i"
print target hierarchy from implied grab down
"target ^l4e10h:20h"
print target hierarchy from ^l4e10h:20h down
"target [content]"
print target hierarchy from content under mouse.

Prints the target hierarchy below an object.

  • If no argument is specified, the system object is used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • Remember that the object you start from may have the target within its part of the hierarchy, but still not have the target because something in a different part of the tree has it.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available.

    • This variable defaults to false.

See Also: focus, model, mouse, keyboard, pobject.

targetobj

targetobj

Examples:

"targetobj"
return object with target
"pobj [targetobj]"
do a pobject on the target object (equivalent to "pobj -t").

Returns the object with the target.

See Also: target, focus, focusobj, modelobj.

tbrk

tbrk <addr> <condition>*
tbrk del <tbrk>+
tbrk list
tbrk cond <tbrk> <condition>*
tbrk count <tbrk>
tbrk reset <tbrk>
tbrk address <tbrk>

Examples:

"tbrk ObjCallMethodTable"
Count the number of times ObjCallMethodTable() is called.
"tbrk count 2"
Find the number of times tally breakpoint number 2 was hit.
"tbrk reset 2"
Reset the counter for tbrk number 2 to 0.
"tbrk list"
Print a list of the set tally breakpoints and their current counts.

This command manipulates breakpoints that tally the number of times they are hit without stopping execution of the machine--the breakpoint is noted and the machine is immediately continued. Such a breakpoint allows for real-time performance analysis, which is nice.

  • If you specify one or more <condition> arguments when setting the tally breakpoint, only those stops that meet the conditions will be counted.
  • The condition argument is exactly as defined by the "brk" command, q.v..
  • When you've set a tally breakpoint, you will be returned a token of the form "tbrk<n>", where <n> is some number. You use this token, or just the <n>, if you're not a program, wherever <tbrk> appears in the Usage description, above.
  • There are a limited number of tally breakpoints supported by the stub. You'll know when you've set too many.
  • "tbrk address" returns the address at which the tbrk was set, as a symbolic address expression.

See Also: brk, cbrk.

tcl-debug

tcl-debug top
tcl-debug next <tcl-frame>
tcl-debug prev <tcl-frame>
tcl-debug args <tcl-frame>
tcl-debug getf <tcl-frame>
tcl-debug setf <tcl-frame> <flags>
tcl-debug eval <tcl-frame> <expr>
tcl-debug complete <tcl-frame>
tcl-debug next-call

Examples:

"var f [tcl-debug top]"
Sets $f to be the frame at which the debugger was entered.
"var f [tcl-debug next $f]"
Retrieves the next frame down (away from the top) the Tcl call stack from $f.

This provides access to the internals of the Tcl interpreter for the Tcl debugger (which is written in Tcl, not C). It will not function except after the debugger has been entered.

See Also: debug.

text-fixup

  1. Run geos under swat, run swat on the development system
  2. Run GeoWrite
  3. Open the GeoWrite file that needs fixing
  4. Set the breakpoint in swat:
    5.  patch text::CalculateRegions
 => text-fixup

    This will set a breakpoint at the right spot

  5. Turn on the error-checking code in swat:
    6.  ec +text
  6. Enter a <space> into the document. This forces recalculation which will cause CalculateRegions() to be called which will cause text-fixup to be called.

    7. If it worked, this code should patch together the file. If it's not, you'll get a FatalError right now.

  7. Turn off the ec code and disable the fixup breakpoint.
    8.  ec none
     dis <breakpoint number>
     continue
  8. Delete the space and save the file.

To do another file, you can just enable the breakpoint once the new file is open and turn on the ec code.

Helps fix up trashed GeoWrite documents.

thaw

thaw [<patient>]
thaw :<n>
thaw <patient>:<n>
thaw <id>

Examples:

"thaw"
Thaw the current thread.
"thaw term"
Allows the application thread for "term" to run normally
"thaw :1"
Allows thread #1 of the current patient to run normally
"thaw 16c0h"
Allows the thread whose handle is 16c0h to run normally.

Thawing a thread restores its priority to what it was before the thread was frozen.

See Also: freeze.

threadname

threadname <id>

Examples:

"threadname 21c0h"
Returns the name of the thread whose handle id is 21c0h.

Given a thread handle, produces the name of the thread, in the form <patient>:<n>

If the handle is not one of those swat knows to be for a thread, this returns the string "unknown."

See Also: thread, patient.

threadstat

threadstat

Examples:

"threadstat"

Provides information about all threads and various thread queues and synchronization points in the system.

See Also: ps.

timebrk

timebrk <start-addr> <end-addr>+
 timebrk del <timebrk>+
 timebrk list
 timebrk time <timebrk>
 timebrk reset <timebrk>

Examples:

"timebrk LoadResourceData -f"
Calculate the time required to process a call to LoadResourceData() .
"timebrk time 2"
Find the amount of time accumulated for timing breakpoint number 2.
"timebrk reset 2"
Reset the counter for timebrk number two to zero.
"timebrk list"
Print a list of the set timing breakpoints and their current counts and time.

This command manipulates breakpoints that calculate the amount of time executing between their starting point and a specified ending point. The breakpoints also record the number of times their start is hit, so you can figure the average amount of time per hit.

  • You can specify a place at which timing should end either as an address or as "-f". If you use "-f", timing will continue until the finish of the routine at whose start you've placed the breakpoint. Such a breakpoint may only be set at the start of a routine, as the stub hasn't the wherewithal to determine what the return address is at an arbitrary point within the function.
  • You may specify more than one ending point. Timing will stop when execution reaches any of those points.
  • When you've set a timing breakpoint, you will be returned a token of the form "timebrk<n>", where <n> is some number. You use this token, or just the <n>, if you're not a program, wherever <timebrk> appears in the Usage description, above.

See Also: brk, cbrk, tbrk.

timingProcessor

var timingProcessor [i86|i88|i286|V20]

The processor for which to generate cycle counts.

See Also: cycles.

tmem

tmem

Examples:

"tmem"
turn on memory tracing.

Trace memory usage.

The tmem command catches calls to DebugMemory, printing out the parameters passed (move, free, realloc, discard, swapout, swapin, modify).

top-level

top-level

Examples:

"top-level"
Begin reading and interpreting Tcl commands in a nested interpreter.

This is the top-most read-eval-print loop of the Swat Tcl interpreter.

This command will only return if the user issues the "break" command. Otherwise it loops infinitely, reading and executing and printing the results of Tcl commands.

tundocalls

tundocalls [-acPCrR]

Examples:

"tundocalls -a" Print out all text undo calls
"tundocalls -r" Print run undo calls
"tundocalls -R" Print replace undo calls
"tundocalls -c" Print info when undo information is created
"tundocalls -cP" Print info about para attributes only
"tundocalls -cC" Print info about char attributes only
"tundocalls"

Prints out information about each undo call made to the text object.

See Also: ptext, showcalls.

twalk

twalk

Examples:

"twalk"
print all the timers in the system.
"twalk -o ui"
print all the timers in the system for the ui thread.
"twalk -a"
print all the timers with the "real" data for the time for time remaining rather than maintaining a total.

List all the timers in GEOS.

unalias

unalias <name>+

Examples:

"unalias p"
Removes "p" as an alias for print.

This removes an alias.

  • In fact, this actually can be used to delete any command at all, including Tcl procedures and Swat built-in commands. Once they're gone, however, there's no way to get them back.

See Also: alias.

unassemble

unassemble [<addr> [<decode-args>]]

Examples:

"unassemble cs:ip 1"
Disassemble the instruction at CS:IP and return a string that shows the values of the arguments involved.

This decodes data as machine instructions and returns them to you to display as you like. It is not usually typed from the command line.

  • The return value is always a four-element list:
    •  {<symbolic-addr> <instruction> <size> <args>}

    where <symbolic-addr> is the address expressed as an offset from some named symbol, <instruction> is the decoded instruction (without any leading whitespace), <size> is the size of the instruction (in bytes) and <args> is a string displaying the values of the instruction operands, if <decode-args> was given and non-zero (it is the empty string if <decode-args> is missing or 0).

  • If <addr> is missing or "nil", the instruction at the current frame's CS:IP is returned.

See Also: listi.

unbind-key

unbind-key <ascii_value>

Examples:

"unbind-key \321"
Unbinds scroll-down key on host machine.

Unbinds the passed ASCII value.

See Also: alias, bind-key, get-key-binding.

undebug

undebug <proc-name>+

Examples:

"undebug fooproc"
Cease halting execution each time "fooproc" is executing.

Removes a Tcl breakpoint set by a previous "debug" command.

See Also: debug.

up

up [<frame offset>]

Examples:

"up"
move the frame one frame up the stack
"up 4"
move the frame four frames up the stack

Move the frame up the stack.

  • The <frame offset> argument is the number of frame to move up the stack. If none is specified then the current frame is moved up one frame.
  • This command may be repeated by pressing <Return>.

See Also: backtrace, down.

value

value fetch <addr> [<type>]
value store <addr> <value> [<type>]
value hfetch <num>
value hstore <addr-list>
value hset <number-saved>
value log <addr> <stream> [<type>]

Examples:

"value fetch ds:si [type word]"
Fetch a word from ds:si
"value store ds:si 0 [type word]"
Store 0 to the word at ds:si
"value hfetch 36"
Fetch the 36th address list stored in the value history.
"value hstore $a"
Store the address list in $a into the value history.
"value hset 50"
Keep track of up to 50 address lists in the value history.
"value log ds:si $s [type word]"
Fetch a word from ds:si and dump the binary data to stream $s.

This command allows you to fetch and alter values in the target PC. It is also the maintainer of the value history, which you normally access via @<number> terms in address expressions.

  • "value fetch" returns a value list that contains the data at the given address. If the address has an implied data type (it involves a named variable or a structure field), then you do not need to give the <type> argument.

    • All integers and enumerated types are returned in decimal. 32-bit pointers are returned as a single decimal integer whose high 16 bits are the high 16 bits (segment or handle) of the pointer. 16-bit pointers are likewise returned as a single decimal integer.

    Characters are returned as characters, with non-printable characters converted to the appropriate backslash escapes (for example, newline is returned as \n).

    Arrays are returned as a list of value lists, one element per element of the array.

    Structures, unions and records are returned as a list of elements, each of which is a 3-element list: {<field-name> <type> <value>} <field-name> is the name of the field, <type> is the type token for the type of data stored in the field, and <value> is the value list for the data in the field, appropriate to its data type.

  • You will note that the description of value lists is recursive. For example, if a structure has a field that is an array, the <value> element in the list that describes that particular field will be itself a list whose elements are the elements of the array. If that array were an array of structures, each element of that list would again be a list of {<field-name> <type> <value>} lists.
  • The "field" command is very useful when you want to extract the value for a structure field from a value list.
  • As for "value fetch", you do not need to give the <type> argument to
  • "value store" if the <addr> has an implied data type. The <value> argument is a value list appropriate to the type of data being stored, as described above.
  • "value hstore" returns the number assigned to the stored address list. These numbers always increase, starting from 1.
  • If no address list is stored for a given number, "value hfetch" will generate an error.
  • "value hset" controls the maximum number of address lists the value history will hold. The value history is a FIFO queue; if it holds 50 entries, and the 51st entry is added to it, the 1st entry will be thrown out.
  • "value log" has the same functionality as value fetch except that data fetched from the PC are dumped into the stream as raw binary data.

See Also: addr-parse, assign, field.

varwin

varwin <num-lines> <var-name>

view

view [<args>]

Examples:

"view foo.goc"
Bring up foo.goc in the source window.

View a file in Swat.

See Also: view-default, srcwin.

view-default

view-default [patient]

Examples:

"view-default spool"
sets the default view to the spool patient.
"view-default"
turns off the view default.

If the view-default is set the view command will automatically look for source files from that patient. If it's not set then the view command will look for files from the current patient.

See Also: view, srcwin.

vistree

vistree [<address>] [<instance field>]

Examples:

"vistree"
print the visual tree starting at *DS:SI
"vistree -i"
print the visual tree under the mouse
"vistree @23 VI_optFlags"
print the visual tree with opt flags
"vistree *uiSystemObj"
starts the visual tree at the root of the system.

Print out a visual tree.

  • The <address> argument is the address to an object in the generic tree. This defaults to *DS:SI. The `-i' flag for an implied grab may be used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • The <instance field> argument is the offset to any instance data within the VisInstance which should be printed out.
  • The variable "printNamesInObjTrees" can be used to print out the actual app-defined labels for the objects, instead of the class, where available. This variable defaults to false.

See Also: vup, gentree, impliedgrab, pobject.

vup

vup [<address>] [<instance field>]

Examples:

"vup"
print the visual object at *DS:SI and its ancestors
"vup @23 VI_optFlags"
print the states of object @23 and its ancestors
"vup -i"
print the visual object under the mouse and the object's ancestors

Print a list of the object and all of its visual ancestors.

  • The <address> argument is the address to an object in the visual tree. This defaults to *DS:SI. The `-i' flag for an implied grab may be used.
  • The special object flags may be used to specify <object>. For a list of these flags, see pobject.
  • The <instance field> argument is the offset to any instance data within the GenInstance which should be printed out.

See Also: vistree, gup, gentree, impliedgrab, pobject.

wait

wait

Examples:

"wait"
Wait for the target PC to halt.

This is used after the machine has been continued with "continue-patient" to wait for the machine to stop again. Its use is usually hidden by calling "cont" or "next".

  • This returns 0 if the patient halted naturally (because it hit a breakpoint), and 1 if it was interrupted (by the user typing Ctrl+C to Swat).
  • Most procedures won't need to use this function.

See Also: brk, ibrk.

waitForPatient

var waitForPatient [(1|0)]

Examples:

"var waitForPatient 0"
Tells Swat to return to the command prompt after continuing the machine.

Determines whether the command-level patient-continuation commands (step, next, and cont, for example) will wait for the machine to stop before returning.

  • The effect of this is to return to the command prompt immediately after having issued the command. This allows you to periodically examine the state of the machine without actually halting it.
  • The output when the machine does stop (e.g. when it hits a breakpoint) can be somewhat confusing. Furthermore, this isn't fully tested, so it should probably be set to 0 only in somewhat odd circumstances.

See Also: step, next, cont, int.

wakeup

Wait for a given patient/thread to wake up. WHO is of the same form as the argument to the "switch" command, ("help switch" to find out more). Leaves you stopped in the kernel in the desired thread's context unless something else causes the machine to stop before the patient/thread wakes up. WHO defaults to the current thread.

wakeup-thread

Subroutine to actually wake up a thread. Argument WHO is as for the "switch"command. Returns non-zero if the wakeup was successful and zero if the machine stopped for some other reason.

wclear

wclear

Clears the current window.

wcreate

wcreate <height>

Create a window of the given height and return a token for it. The window is placed just above the command window, if there's room. If there aren't that many lines free on the screen, an error is returned.

wdelete

wdelete <window>

Delete the given window. All windows below it move up and the command window enlarges.

whatat

whatat [<address>]

Examples:

"whatat"
name of variable at *DS:SI
"whatat ^l2ef0h:002ah"
name of variable at the specified address

Print the name of the variable at the address.

  • The <address> argument specifies where to find a variable name for. The address defaults to *DS:SI.
  • If no appropriate variable is found for the address, `*nil*' is returned.

See Also: pobject, hwalk, lhwalk.

whatis

whatis (<symbol>|<addr>)

Examples: "whatis WinColorFlags"

This produces a human-readable description of a symbol, giving whatever information is pertinent to its type.

  • For type symbols (e.g. structures and enumerated types), the description of the type is fully displayed, so if a structure has a field with an enumerated type, all the members of the enumerated type will be printed as well. Also all fields of nested structures will be printed. If this level of detail isn't what you need, use the "pscope" command instead.
  • It's not clear why you'd need the ability to find the type of an address-expression, since those types always come from some symbol or other, but if you want to type more, you certainly may.

why

why

Examples: "why"

Print a description of why the system crashed.

  • This must be run from within the frame of the FatalError function. Sometimes GEOS is not quite there. In this case, step an instruction or two and then try the "why" command again.
  • This simply looks up the enumerated constant for the error code in AX in the "FatalErrors" enumerated type defined by the geode that called FatalError. For example, if a function in the kernel called FatalError, AX would be looked up in geos::FatalErrors, while if a function in your application called FatalError, this function would look it up in the FatalErrors type defined by your application. Each application defines this enumerated type by virtue of having included ec.def or ec.goh .
  • For certain fatal errors, additional information is provided by invoking the command <patient>::<error code name>, if it exists.

See Also: regs, backtrace, explain.

wintree

wintree <window handle> [<data field>]

Examples:

"wintree ^hd060h"
print a window tree starting at the handle d060h

Print a window tree starting with the root specified.

  • The <window address> argument is the address to a window.
  • The <data field> argument is the offset to any instance data within a window (like W_ptrFlags).

See Also: vistree, gentree.

winverse

winverse

Sets the inverse-mode of the current window (whether newly-echoed characters are displayed in inverse video) on or off, depending on its argument (1 is on).

wmove

wmove [(+|-)] <x-coord> [(+|-)] <y-coord>

Moves the cursor for the current window. Takes two arguments: the new x position and the new y position. These positions may be absolute or relative (absolute positions begin with + or -). If you attempt to move outside the current window, an error is generated. This command returns the new cursor position as { x y }.

words

words [<address>] [<length>]

Examples:

"words"
lists 8 words at DS:SI
"words ds:di 16"
lists 16 words starting at DS:DI

Examine memory as a dump of words.

  • The <address> argument is the address to examine. If not specified, the address after the last examined memory location is used. If no address has been examined then DS:SI is used for the address.
  • The <length> argument is the number of bytes to examine. It defaults to 8.
  • Pressing <Return> after this command continues the list.

See Also: bytes, dwords, imem, assign.

wpop

wpop

Revert the current window to its previously pushed value.

wpush

wpush <window>

Switch to a new window, saving the old current-window. Use wpop to go back to the previous window. All I/O goes through the current window.

wrefresh

wrefresh

Synchronizes the current window with the screen. This need only be performed if you don't echo a newline, as echoing a newline refreshes the current window.

wtop

wtop <flag>

Sets where windows go. If argument is non-zero, windows go at the top of the screen and work down. Else windows go at the bottom of the screen and work up

This document is a single-page version of a a multi-page document, suitable for easy printing.


Tool Command Language

This chapter is designed to provide information about the Tool Command Language, abbreviated Tcl, (the language in which Swat commands are written) so that new commands can be written and old commands modified. This chapter contains the following main sections:

  • Using This Chapter
    Discussion of the situations warranting the construction of a new command in the Tool Command Language.
  • Background and Description
    Discussion of history of Tool Command Language and general overview of the language.
  • Syntax and Structure
    Description of the syntax and structure of the Tool Command Language.
  • Commands
    List of all built-in commands for the language.
  • Coding
    Descriptions and examples of coding conventions, techniques, and tricks.
  • Installation
    Steps to take in order to be able to use a newly written command to help debug an application.

1 Using This Chapter
2 Copyright Information
3 Background and Description
4 Syntax and Structure
4.1 Basic Command Syntax
4.2 Expressions
4.3 Lists
4.4 Command Results
4.5 Procedures
4.6 Variables
5 Commands
5.1 Notation
5.2 Built-in Commands
6 Coding
6.1 Swat Data Structure Commands
6.2 Examples
7 Using a New Command
7.1 Compilation
7.2 Autoloading
7.3 Explicit Loading

Tool Command Language: 1 Using This Chapter

This chapter provides the information needed to write a new Swat command in Tcl. But, new commands need only be written in certain situations. Some of the situations in which it is advantageous to write a new Swat command in Tcl are:

  • There is complex task that is being repeated often. For example, if one is continually examining a certain piece of data in memory but has to go through many steps to do so, then it is helpful to write a single command to perform all of the needed steps.
  • When a new data-structure is created for an application. For example, if one creates a look-up table for the application, then a Tcl command should be written to examine that table in particular.

The existing Swat commands should take care of the bulk of debugging, but sometimes an extra command can help.

Tool Command Language: 2 Copyright Information

The following sections of this chapter fall under the copyright below: Background and Description, Syntax and Structure, and Commands.

Copyright © 1987 Regents of the University of California

Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The University of California makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.

Tool Command Language: 3 Background and Description

The Tool Command Language is abbreviated as Tcl and is pronounced "tickle". It was developed and written by Professor John Ousterhout at the University of California at Berkeley. Tcl is a combination of two main parts: a language and a library.

Language
The Tcl language is a textual language intended primarily for issuing commands to interactive programs such as text editors, illustrators, shells, and most importantly debuggers. It has a set syntax and is programmable, thus allowing users to create more powerful commands than the built-in command set listed in the Swat Command Reference.
Library
Tcl also includes a library which can be imbedded in an application, as it is in Swat. This library includes a parser for the Tcl language, routines to implement the Tcl built-in commands, and procedures allowing an application to extend Tcl with additional commands.

Tool Command Language: 4 Syntax and Structure

Tcl supports only one type of data: strings . All commands, all arguments to commands, all command results, and all variable values are strings. Where commands require numeric arguments or return numeric results, the arguments and results are passed as strings. Many commands expect their string arguments to have certain formats, but this interpretation is up to the individual commands. For example, arguments often contain Tcl command strings, which may get executed as part of the commands. The easiest way to understand the Tcl interpreter is to remember that everything is just an operation on a string. In many cases Tcl constructs will look similar to more structured constructs from other languages. However, the Tcl constructs are not structured at all; they are just strings of characters, and this gives them a different behavior than the structures they may look like.

Although the exact interpretation of a Tcl string depends on who is doing the interpretation, there are three common forms that strings take: commands , expressions , and lists . This section will have the following main parts:

  • Basic Command Syntax
    Description of the syntax common to all Tcl code: comments, argument grouping, command grouping, variable substitution, backslash substitution.
  • Expressions
    Details on interpretation of expressions by Tcl.
  • Lists
    Details on interpretation of lists by Tcl.
  • Command Results
    What type of results a command can return.
  • Procedures
    The structure and building of procedures in Tcl.
  • Variables
    Variable declaration and description.

Basic Command Syntax

Expressions

Lists

Command Results

Procedures

Variables

Tool Command Language: 4.1 Syntax and Structure: Basic Command Syntax

The Tcl language has syntactic similarities to both Unix and Lisp. However, the interpretation of commands is different in Tcl than in either of those other two systems. A Tcl command string consists of one or more commands separated by newline characters. Each command consists of a collection of fields separated by white space (spaces or tabs). The first field must be the name of a command, and the additional fields, if any, are arguments that will be passed to that command. For example, the command:

var a 22

has three fields: the first, var , is the name of a Tcl command, and the last two, a and 22 , will be passed as arguments to the var command. The command name may refer to a built-in Tcl command, an application specific command, or a command procedure defined with the built-in proc command. Arguments are passed literally as text strings. Individual commands may interpret those strings in any fashion they wish. The var command, for example, will treat its first argument as the name of a variable and its second argument as a string value to assign to that variable. For other commands, arguments may be interpreted as integers, lists, file names, or Tcl commands.

Comments

If the first non-blank character in a command is # (a number sign), then everything from the # up through the next newline character is treated as comment and discarded by the parser.

Argument Grouping

Normally each argument field ends at the next white space (tabs or spaces), but curly braces ("{" and "}") may be used to group arguments in different ways. If an argument field begins with a left brace, then the argument is not terminated by white space; it ends at the matching right brace. Tcl will strip off the outermost layer of braces before passing the argument to the command. For example, in the command:

var a {b c}

the var command will receive two arguments: a and b c . The matching right brace need not be on the same line as the left brace; in this case the newline will be included in the argument field along with any other characters up to the matching right brace. In many cases an argument field to one command consists of a Tcl command string that will be executed later; braces allow complex command structures to be built up without confusion. For example, the eval command takes one argument, which is a command string; eval invokes the Tcl interpreter to execute the command string. The command:

eval {
       var a 22
       var b 33
}

will assign the value 22 to a and 33 to b .

Tcl braces act like quote characters in most other languages, in that they prevent any special interpretation of the characters between the left brace and the matching right brace.

When an argument is in braces, then command, variable, and backslash substitutions do not occur in the normal fashion; all Tcl does is to strip off the outer layer of braces and pass the contents to the command. Braces are only significant in a command field if the first character of the field is a left brace. Otherwise neither left nor right braces in the field will be treated specially (except as part of variable substitution).

Command Grouping

Normally, each command occupies one line (the command is terminated by a newline character). Thus, the string:

var a 22
var b 33

will be interpreted as two separate commands. However, brackets may be used to group commands in ways other than one-command-per-line. If the first character of a command is an open bracket, then the command is not terminated by a newline character; instead, it consists of all the characters up to the matching close bracket. Newline characters inside a bracketed command are treated as white space (they will act as argument separators for arguments that are not enclosed in braces). For example, the string:

[var a
22] [var b 33]

will have the same effect as the previous example.

Command Substitution

If an open bracket occurs in any of the fields of a command, then command substitution occurs. All of the text up to the matching close bracket is treated as a Tcl command and executed immediately. The result of that command is substituted for the bracketed text. For example, consider the command:

var a [var b]

When the var command has only a single argument, it is the name of a variable and var returns the contents of that variable. In this case, if variable b has the value test , then the command above is equivalent to the command:

var a test

Brackets can be used in more complex ways. for example, if the variable b has the value tmp and the variable c has the value val , then the command:

var a test[var b].[var c]

is equivalent to the command:

var a testtmp.val

If a field is enclosed in braces then the brackets and the characters between them are not interpreted specially; they are passed through to the argument verbatim.

Variable Substitution

The dollar sign ($) may be used as a special shorthand form for substituting variables. If $ appears in an argument that is not enclosed in braces then variable substitution will occur. The characters after the $, up to the first character that is not a number, letter, or underscore, are taken as a variable name and the string value of that variable is substituted for the name. Or, if the dollar sign is followed by an open curly brace, then the variable name consists of all the characters up to the next close curly brace. For example, if variable outfile has the value test , then the command:

var a $outfile.c

is equivalent to the command:

var a test.c

and the command:

var a abc${outfile}tmp

is equivalent to the command:

var a abctesttmp

Variable substitution does not occur in arguments that are enclosed in braces: the dollar sign and variable name are passed through to the argument verbatim.

The dollar sign abbreviation is simply a shorthand form. $a is completely equivalent to [var a] ; it is provided as a convenience to reduce typing.

Backslash Substitution

Backslashes may be used to insert non-printing characters into command fields and also to insert braces, brackets, and dollar signs into fields without them being interpreted specially as previously described. The backslash sequences understood by the Tcl interpreter are listed below. In each case, the backslash sequence is replaced by the given character.

For example, in the command:

var a \{x\[\ yz\141

the second argument to var is {x[ yza (note the <space> as part of the argument).

If a backslash is followed by something other than one of the options listed below, then the backslash is transmitted to the argument field without any special processing, and the Tcl scanner continues normal processing with the next character. For example, in the command:

var \*a \\\{test

the first argument will be \*a and the second \{test .

If an argument is enclosed in braces, then backslash sequences inside the argument are parsed but no substitution occurs. In particular, backslashed braces are not counted in locating the matching right brace that terminates the argument. for example, in the command:

var a {\{abc}

the second argument to var will be \{abc .

The backslash mechanism is not sufficient to generate any argument structure; it only covers the most common cases. To produce particularly complicated arguments it will probably be easiest to use the format command along with command substitution.

Tool Command Language: 4.2 Syntax and Structure: Expressions

The second major interpretation applied to strings in Tcl is as expressions . Several commands, such as expr , for , and if , treat some of their arguments as expressions and call the Tcl expression processor (Tcl_Expr) to evaluate them. A Tcl expression has C-like syntax and evaluates to an integer result. Expressions may contain integer values, variable names in $ notation (the variables' values must be integer strings), commands (embedded in brackets) that produce integer string results, parentheses for grouping, and operators. Numeric values, whether they are passed directly or through variable or command substitution, may be specified either in decimal (the normal case), in octal (if the first character of the value of the first character is 0 (zero)), or in hexadecimal (if the first two characters of the value are 0x). The valid operators are listed below, grouped in decreasing order of precedence.

See a C manual for more details on the results produced by each operator. All of the binary operators group left to right within the same precedence level. for example, the expression:

(4*2)<7

evaluates to zero. Evaluating the expression string:

($a+3)<[var b]

will cause the values of the variables a and b to be examined; the result will be 1 if b is greater than a by at least 3; otherwise the result will be 0.

In general it is safest to enclose an expression in braces when entering it in a command; otherwise, if the expression contains any white space then the Tcl interpreter will split it among several arguments. For example, the command:

expr $a + $b

results in three arguments being passed to expr : $a , + , and $b . In addition, if the expression is not in braces then the Tcl interpreter will perform variable and command substitution immediately (it will happen in the command parser rather than in the expression parser). In many cases the expression is being passed to a command that will evaluate the expression later (or even many times if, for example, the expression is to be used to decide when to exit a loop). usually the desired goal is to re-do the variable or command substitutions each time the expression is evaluated, rather than once and for all at the beginning. For an example of a mistake, the command:

for {var i 1} $i<=10 {var i [expr $i+1]} {body...}

is probably intended to iterate over all values of i from 1 to 10. After each iteration of the body of the loop, for will pass its second argument to the expression evaluator to see whether or not to continue processing. Unfortunately, in this case the value of i in the second argument will be substituted once and for all when the for command is parsed. If i was 0 before the for command was invoked then for 's second argument will be 0<=10 which will always evaluate to 1, even though i 's value eventually becomes greater than 10. In the above case the loop will never terminate. By placing the expression in braces, the substitution of i 's value will be delayed; it will be re-done each time the expression is evaluated, which is probably the desired result:

for {var i 1} {$i<=10} {var i [expr $i+1]} {body...}

Tool Command Language: 4.3 Syntax and Structure: Lists

The third major way that strings are interpreted in Tcl is a list . A list is just a string with a list-like structure consisting of fields separated by white space. For example, the string:

Al Sue Anne John

is a list with four elements or fields. Lists have the same basic structure as command strings, except that a newline character in a list is treated as a field separator just like a space or tab. Conventions for braces and backslashes are the same for lists as for commands. For example, the string:

a b\ c {d e {f g h}}

is a list with three elements: a , b c , and d e {f g h} . Note the space between the b and c . Whenever an element is extracted from a list, the same rules about backslashes and braces are applied as for commands. Thus in the above example when the third element is extracted from the list, the result is:

d e {f g h}

(when the field was extracted, all that happened was to strip off the outermost layer of braces). Command substitution is never made on a list (at least, not by the list-processing commands; the list can always be passed to the Tcl interpreter for evaluation).

The Tcl commands concat , foreach , index , length , list , and range allow you to build lists, extract elements from them, search them, and perform other list-related functions.

Tool Command Language: 4.4 Syntax and Structure: Command Results

Each command produces two results: a code and a string . The code indicates whether the command completed successfully or not, and the string gives additional information. The valid codes are defined as follows:

TCL_OK
This is the normal return code, and indicates that the command completed successfully. The string gives the commands's return value.
TCL_ERROR
Indicates that an error occurred; the string gives a message describing the error.
TCL_RETURN
Indicates that the return command has been invoked, and that the current procedure should return immediately. The string gives the return value that procedure should return.
TCL_BREAK
Indicates that the break command has been invoked, so the innermost loop should abort immediately. The string should always be empty.
TCL_CONTINUE
Indicates that the continue command has been invoked, so the innermost loop should go on to the next iteration. The string should always be empty.

Tcl programmers do not normally need to think about return codes, since TCL_OK is almost always returned. If anything else is returned by a command, then the Tcl interpreter immediately stops processing commands and returns to its caller. If there are several nested invocations of the Tcl interpreter in progress, then each nested command will usually return the error to its caller, until eventually the error is reported to the top-level application code. The application will then display the error message for the user.

In a few cases, some commands will handle certain "error" conditions themselves and not return them upwards. For example, the for command checks for the TCL_BREAK code; if it occurs, then for stops executing the body of the loop and returns TCL_OK to its caller. The for command also handles TCL_CONTINUE codes and the procedure interpreter handles TCL_RETURN codes. The catch command allows Tcl programs to catch errors and handle them without aborting command interpretation any further.

Tool Command Language: 4.5 Syntax and Structure: Procedures

Tcl allows one to extend the command interface by defining procedures. A Tcl procedure can be invoked just like any other Tcl command (it has a name and it receives one or more arguments). The only difference is that its body is not a piece of C code linked into the program; it is a string containing one or more other Tcl commands. See the proc command for information on how to define procedures and what happens when they are invoked.

Tool Command Language: 4.6 Syntax and Structure: Variables

Tcl allows the definition of variables and the use of their values either through $-style variable substitution, the var command, or a few other mechanisms. Variables need not be declared: a new variable will automatically be created each time a new variable name is used. Variables may be either global or local. If a variable name is used when a procedure is not being executed, then it automatically refers to a global invocation of the procedure. Local variables are deleted whenever a procedure exits. The global command may be used to request that a name refer to a global variable for the duration of the current procedure (somewhat analogous to extern in C).

Tool Command Language: 5 Commands

The Tcl library provides the following built-in commands, which will be available to any application using Tcl. In addition to these built-in commands, there may be additional commands defined in Swat, plus commands defined as Tcl procedures.

Notation

Built-in Commands

Tool Command Language: 5.1 Commands: Notation

The descriptions of the Tcl commands will follow the following notational conventions:

  • command (alternative1|alternative2|...|alternativeN)
    ()     The parentheses enclose a set of alternatives separated by a vertical line. For example, the expression quit (cont|leave) means that either quit cont or quit leave can be used.
  • command [optional_argument]
    []     The brackets enclose optional arguments to the command. For example, the command alias [<word[<command>]>] could have zero, one, or two arguments because the <command> and <word> arguments are optional. Another example would be the command objwalk [<addr>] , which may take zero arguments if it is meant to use the default address, and one argument if the user gives it a particular address to look at.
  • command <type_of_argument>
    < >     The angled brackets enclose the type of an argument rather than the actual string to be typed. For example, <addr> indicates an address expression and <argument> indicates some sort of argument, but (addr|type) means either the string addr or the string type .
  • * +
     An asterisk following any of the previous constructs indicates zero or more repetitions of the construct may be typed. An addition sign indicates one or more repetitions of the construct may be used. For example, unalias word * can be the unalias command by itself, or it can be followed by a list of words to be unaliased.

Tool Command Language: 5.2 Commands: Built-in Commands

The built-in Tcl commands are as follows:

bc

bc list <proc>
bc disasm <proc>
bc compile <proc>
bc fcompile <file> [<nohelp>]
bc fload <file>
bc fdisasm <file>
bc debug [1|0]

Examples:

"bc compile poof"
Compiles the body of the procedure "poof" and replaces the existing procedure with its compiled form.
"bc fcomp bptutils.tcl"
Creates the file "bptutils.tlc" that contains a stream of compiled Tcl that will do exactly what sourcing bptutils.tcl does, except the resulting procedures will be compiled Tcl, not interpreted Tcl.
"bc fload bptutils.tlc"
Loads a file containing a stream of compiled Tcl code.

The "bc" command allows you to create and examine compiled Tcl code. Compiled Tcl is not nearly as readable or changeable as interpreted Tcl code, but it's 30-50% faster.

The "list" subcommand doesn't work.

See Also: source.

break

break

Examples:

"break"
Break out of the current loop.

Breaks out of the current loop or the current nested interpreter.

  • Only the closest-enclosing loop can be exited via this command.
  • This command may be invoked only inside the body of a loop command such as for or foreach . It returns a TCL_BREAK code to signal the innermost containing loop command to return immediately.
  • If you've entered a nested interpreter, e.g. by calling a function in the patient, use this to exit the interpreter and restore the registers to what they were before you made the call.

See Also: continue, for.

case

case <string> [in] [<pat> <body>]+

Examples:

"[case $c in
{[0-9]} {
# do something with digit
}
default {
# do something with non-digit
}
]"
Do one of two things depending on whether the character in $c is a digit.

Perform one of a set of actions based on whether a string matches one or more patterns.

  • Compares each of the <pattern> arguments to the given <string>, executing <body> following the first <pattern> to match. <pattern> uses shell wildcard characters as for the string match command, but may also contain alternatives, which are separated by a vertical bar, thus allowing a <body> to be executed under one of several circumstances. In addition, if one <pattern> (or element thereof) is the string default, the associated <body> will be executed if none of the other patterns matches. For example, the following:
    • [case $test in
 a|b {return 1}
 {default|[DE]a*} {return 0}
 ?c {return -1}]

    will return 1 if variable test contains a or b , -1 if it contains a two-letter string whose second letter is c , and 0 in all other cases, including the ones where test 's first two letters are either Da or Ea .

  • Each <pat> argument is a list of patterns of the form described for the "string match" command.
  • Each <pat> argument must be accompanied by a <body> to execute.
  • If a <pat> contains the special pattern "default," the associated <body> will be executed if no other pattern matches. The difference between "default" and "*" is a pattern of "*" causes the <body> to be executed regardless of the patterns in the remaining <pat> arguments, while "default" postpones the decision until all the remaining patterns have been checked.
  • You can give the literal "in" argument if you wish to enhance the readability of your code.

See Also: string, if.

catch

catch <command> [<varName>]

Executes a command, retaining control even if the command generates an error (which would otherwise cause execution to unwind completely).

  • The catch command may be used to prevent errors from aborting command interpretation. catch calls the Tcl interpreter recursively to execute <command>, and always returns a TCL_OK code, regardless of any errors that might occur while executing <command>. The return value from catch is a decimal string giving the code returned by the Tcl interpreter after executing <command>. This will be zero (TCL_OK) if there were no errors in command; otherwise it will have a non-zero value corresponding to one of the exceptional return codes. If the <varName> argument is given, then it gives the name of a variable; catch will set the value of the variable to the string returned from command (either a result or an error message).
  • This returns an integer that indicates how <command> completed:
    • 0
    Completed successfully; $<varName> contains the result of the command.
    1
    Generated an error; $<varName> contains the error message.
    2
    Executed "return"; $<varName> contains the argument passed to "return."
    3
    Executed "break"; $<varName> is empty.
    4
    Executed "continue"; $<varName> is empty.

See Also: protect.

concat

concat <arg1>+

Examples:

"concat $list1 $list2"
Merges the lists in $list1 and $list2 into a single list whose elements are the elements of the two lists.

Concatenates multiple list arguments into a single list.

  • This command treats each argument as a list and concatenates them into a single list. It permits any number of arguments. For example, the command
    • concat a b {c d e} {f {g h}}

    will return a b c d e f {g h} as its result.

  • There is a sometimes-subtle difference between this in the "list" command: Given two lists, "concat" will form a list whose n elements are the combined elements of the two component lists, while "list" will form a list whose 2 elements are the two lists. For example,
    •  concat a b {c d e} {f {g h}}

    yields the list

    a b c d e f {g h}

    but

    list a b {c d e} {f {g h}}

    yields

    a b {c d e} {f {g h}}

See Also: list.

continue

continue

Examples:

"continue"
Return to the top of the enclosing loop.

Skips the rest of the commands in the current loop iteration, continuing at the top of the loop.

  • Only the closest-enclosing loop can be continued via this command.
  • The <next> clause of the "for" command is not part of the current iteration, i.e. it will be executed even if you execute this command.
  • This command may be invoked only inside the body of a loop command such as for or foreach . It returns a TCL_CONTINUE code to signal the innermost containing loop command to skip the remainder of the loop's body but continue with the next iteration of the loop.

See Also: break, for.

defsubr

defsubr <name> <args> <body>

Examples:

"defsubr poof {arg1 args} {return [list $arg1 $args]}"
Defines a procedure poof that takes 1 or more arguments and merges them into a list of two elements.

This is the same as the "proc" command, except the new procedure's name may not be abbreviated when it is invoked.

  • Refer to the documentation for proc for more information.

error

error <message>

Examples:

"error {invalid argument}"
Generates an error, giving the not-so-helpful message "invalid argument" to the caller's caller.
  • Unless one of the procedures in the call stack has executed a "catch" command, all procedures on the stack will be terminated with <message> (and an indication of an error) being the result of the final one so terminated.
  • Any commands protected by the "protect" command will be executed.

See Also: return, catch.

eval

eval <body>

Examples:

"eval $mangled_command"
Evaluate the command contained in $mangled_command and return its result.

Evaluates the passed string as a command and returns the result of that evaluation.

  • eval takes one argument, which is a Tcl command (or collection of Tcl commands separated by newlines in the usual way). eval evaluates <body> by passing it to the Tcl interpreter recursively, and returns the result of the last command. If an error occurs inside <body> then eval returns that error.
  • This command is useful when one needs to cobble together a command from arguments or what have you. For example, if one of your arguments is a list of arguments to pass to another command, the only way to accomplish that is to say something like "eval [concat random-command $args]", which will form a list whose first element is the command to be executed, and whose remaining elements are the arguments for the command. "eval" will then execute that list properly.
  • If the executed command generates an error, "eval" will propagate that error just like any other command.

See Also: concat, list.

expr

expr <expression> [float]

Examples:

"expr 36*25"
Multiplies 36 by 25 and returns the result.
"expr $i/6 float"
Divides the number in $i by 6 using floating- point arithmetic; the result is a real number.
"expr 7.2*10 float"
Multiplies 7.2 by 10. Note that though the answer (72) is an integer, we need to pass the "float" keyword to make sure that the expression is interpreted correctly.

Evaluates an arithmetic expression and returns its value.

  • Most C operators are supported with the standard operator precedence.
  • If you use a Tcl variable in the expression, the variable may only contain a number; it may not contain an expression.
  • The result of any Tcl command, in square brackets ("[ ]") must be a number; it may not be an expression.
  • All the C and Esp radix specifiers are allowed.
  • Bitwise and boolean operators (!, &, ^, |, &&, ||, >>, <<, ~) are not permitted when the expression is being evaluated using floating-point arithmetic.

file

file dirname <name>
file exists <name>
file extension <name>
file isdirectory <name>
file isfile <name>
file readable <name>
file rootname <name>
file tail <name>
file writable <name>
file match <pattern>
file newer <name1> <name2>

Examples:

"file match /pcgeos/tcl/*.tcl"
Looks for all files/directories in /pcgeos/tcl whose name ends with ".tcl".
"file isdir $path"
See if the path stored in $path refers to a directory.
"file tail $path"
Return the final component of the path stored in $path

Performs various checks and manipulations of file and directory names.

  • The forward slash is the path separator for this command.
  • The predicate subcommands (executable, exists, isdirectory, isfile, owned, readable, and writable) all return 1 if the path meets the requirements, or 0 if it doesn't.
  • "file match" takes a pattern made from the same components as are described for "string match". It is not the same as the standard DOS wildcarding, where `.' serves to separate the root pattern from the extension pattern. For this command "*.*" would match only files that actually have an extension.
  • "file dirname" returns the directory portion of name . If name has no directory portion, this returns "."
  • "file rootname" returns all leading directory components of name , plus the text before its extension, without the "." that separates the name from the extension.
  • "file tail" returns all of the characters in name after the final forward slash, or name if it contains no forward slashes.
  • "file newer" returns 1 if name1 was modified after name2 . It returns 0 otherwise.

See Also: string.

for

for <start> <test> <next> <body>

Examples:

"for {var i 0} {$i < 10} {var i [expr $i+1]} {echo $i}"
Prints the numbers from 0 to 9.

This is Tcl's main looping construct. It functions similarly to the "for" in C.

  • <start> is a Tcl command string (which may involve multiple commands over multiple lines, if desired) that is executed once at the very start of the loop. It is always executed. If it returns an error, or contains a "break" command, no part of the loop will execute.
  • <test> is an arithmetic expression that is passed to the "expr" command. If the result is non-zero, the <body> is executed.
  • <next> is a Tcl command string (which may involve multiple commands over multiple lines, if desired) that is executed at the end of each iteration before <test> is evaluated again. If it returns an error, or contains a "break" command, no part of the loop will execute.
  • You can exit the loop prematurely by executing the "break" command in any of the three Tcl command strings (<start>, <next>, or <body>).
  • So long as there's no error, "for" always returns the empty string as its result.
  • If a continue command is invoked within <body> then any remaining commands in the current execution of <body> are skipped; processing continues by invoking the Tcl interpreter on <next>, then evaluating <test>, and so on. If a break command is invoked within <body>, then the for command will return immediately. The operation of break and continue are similar to the corresponding statements in C.

See Also: foreach, break, continue.

foreach

foreach <varname> <list> <body>

Examples:

"foreach el $list {echo poof = $el}"
Prints each element of the list $list preceded by the profound words "poof = "

This is a looping construct to easily iterate over all the elements of a list.

  • <body> is evaluated once for each element in <list>. Before each evaluation, the next element is placed in the variable <varName>.
  • You can exit the loop prematurely by executing the "break" command.
  • As long as there's no error, "foreach" always returns the empty string.
  • The break and continue statements may be invoked inside <body>, with the same effect as in the for command.

format

format <formatString> [<arg> ]*

This command generates a formatted string in the same way as the C sprintf procedure (it uses sprintf in its implementation). <formatString> indicates how to format the result, using % fields as in sprintf , and the additional arguments, if any, provide values to be substituted into the result. All of the sprintf options are valid; see the sprintf procedure in a C manual for more details. Each <arg> must match the expected type from the % field in <formatString>; the format command converts each argument to the correct type (floating, integer, etc.) before passing it to sprintf for formatting. The only unusual conversion is for %c; in this case the argument must be a decimal string, which will then be converted to the corresponding ASCII character value. format does backslash substitution on its <formatString> argument, so backslash sequences in <formatString> will be handled correctly even if the argument is in braces. The return value from format is the formatted string.

global

global <varname>+

Examples:

"global attached"
When next the "attached" variable is fetched or set, get it from the global scope, not the local one.

Declares the given variables to be from the global scope.

  • For the duration of the procedure in which this command is executed (but not in any procedure it invokes), the global variable of the given name will be used when the variable is fetched or set.
  • If no global variable of the given name exists, the setting of that variable will define it in the global scope.
  • This command is ignored unless a Tcl procedure is being interpreted. If so, then it declares the given <varname>'s to be global variables rather than local ones. For the duration of the current procedure (and only while executing in the current procedure), any reference to any of the <varname> values will be bound to a global variable instead of a local one.

See Also: var.

if

if <test> [then] <trueBody>

             (elif <test> [(then)] <trueBody>)*

             [[else] <falseBody>]

Examples:

"if {$v > 3} {echo yes} {echo no}"
Prints "yes" if $v is greater than 3, else it prints "no".
"if {$v > 3} then {echo yes} else {echo no}"
Ditto.
"if {$v > 3} then {echo yes} elif {$v == 3} {echo maybe} else {echo no}"

This is Tcl's conditional, as you'd expect from its name.

  • The "then" and "else" keywords are optional, intended to delineate the different sections of the command and make the whole easier to read.
  • The "elif" keyword is mandatory if you want to perform additional tests.
  • The <expr> arguments are normal Tcl expressions. If the result is non-zero, the appropriate <truebody> is executed. If none of the <expr> arguments evaluates non-zero, <falsebody> is executed.
  • If a <truebody> is empty and the test evaluated non-zero, "if" will return the result of the test. Otherwise "if" returns the result from last command executed in whichever <truebody> or <falsebody> argument was finally executed. It returns an empty string if no <expr> evaluated non-zero and no <falsebody> was given.
  • The if command evaluates <test> as an expression in the same way that expr evaluates its argument. If the result is non-zero then <trueBody> is called by passing it to the Tcl interpreter. Otherwise <falseBody> is executed by passing it to the Tcl interpreter. <falseBody> is also optional; if it isn't specified then the command does nothing if <test> evaluates to zero. The return value from if is the value of the last command executed in <trueBody> or <falseBody> or the empty string if <test> evaluates to zero and <falseBody> isn't specified. Alternative test conditions can be added by adding <elif> arguments.

See Also: expr.

index

index <value> <index> [chars]

Examples:

"index {a b c} 1"
Extracts "b" from the list.
"index {hi mom} 3 char"
Extracts "m" from the string.

"index" is used to retrieve a single element or character from a list or string.

  • Elements and characters are numbered from 0.
  • If you request an element or character from beyond the end of the <list> or <string>, you'll receive an empty list or string as a result.
  • If the <chars> keyword isn't specified, then index treats <value> as a list and returns the <index>'th field from it. In extracting the field, index observes the same rules concerning braces and backslashes as the Tcl command interpreter; however, variable substitution and command substitution do not occur. If the <chars> keyword is specified (or any abbreviation of it), then <value> is treated as a string and the command returns the <index>'th character from it (or the empty string if there aren't at least <index>+1 characters in the string).

info

info args <procname> [<pattern>]
info arglist <procname>
info body <procname>
info cmdcount
info commands [<pattern>]
info default <procname> <arg> <varname>
info globals [<pattern>]
info locals [<pattern>]
info procs [<pattern>]
info vars [<pattern>]

Examples:

"info args fmtval"
Retrieves the names of the arguments for the "fmtval" command so you know in what order to pass things.
"info body print-frame"
Retrieves the string that is the body of the "print-frame" Tcl procedure.
"info commands reg "
Retrieves a list of commands whose names contain the string "reg".

This command provides information about a number of data structures maintained by the Tcl interpreter.

  • All the <pattern> arguments are standard wildcard patterns as are used for the "string match" and "case" commands. See "string" for a description of these patterns.
  • "info args" returns the complete list of arguments for a Tcl procedure, or only those matching the <pattern>, if one is given. The arguments are returned in the order in which they must be passed to the procedure.
  • "info arglist" returns the complete list of arguments, and their default values, for a Tcl procedure.
  • "info body" returns the command string that is the body of the given Tcl procedure.
  • "info cmdcount" returns the total number of commands the Tcl interpreter has executed in its lifetime.
  • "info commands" returns the list of all known commands, either built-in or as Tcl procedures, known to the interpreter. You may also specify a pattern to restrict the commands to those whose names match the pattern.
  • "info default" returns non-zero if the argument named <arg> for the given Tcl procedure has a default value. If it does, that default value is stored in the variable whose name is <varname>.
  • "info globals" returns the list of all global variables accessible within the current variable scope (i.e. only those that have been declared global with the "global" command, unless you issue this command from the command-line, which is at the global scope), or those that match the given pattern.
  • "info locals" returns the list of all local variables, or those that match the given pattern.
  • "info procs" returns the list of all known Tcl procedures, or those that match the given pattern.
  • "info vars" returns the list of all known Tcl variables in the current scope, either local or global. You may also give a pattern to restrict the list to only those that match.

See Also: proc, defcmd, defcommand, defsubr.

length

length <value> [<chars>]

Examples:

"length $args"
Returns the number of elements in the list $args
"length $str char"
Returns the number of characters in the string $str

Determines the number of characters in a string, or elements in a list.

  • If (chars) isn't specified, length treats <value> as a list and returns the number of elements in the list. If <chars> is specified (or any abbreviation of it), then length treats <value> as a string and returns the number of characters in it (not including the terminating null character).

See Also: index, range.

list

list <arg>+

Examples:

"list a b {c d e} {f {g h}}"
Returns the list "a b {c d e} {f {g h}}"

Joins any number of arguments into a single list, applying quoting braces and backslashes as necessary to form a valid Tcl list.

  • If you use the "index" command on the result, the 0th element will be the first argument that was passed, the 1st element will be the second argument that was passed, etc.
  • The difference between "list" and "concat" is subtle. Given the above arguments, "concat" would return "a b c d e f {g h}".
  • This command returns a list comprised of all the <args>. It also adds braces and backslashes as necessary, so that the index command may be used on the result to re-extract the original arguments, and also so that eval may be used to execute the resulting list, with <arg1> comprising the command's name and the other <args> comprising its arguments.

See Also: concat, index, range.

proc

proc <name> <args> <body>

Examples:

"proc poof {{arg1 one} args} {return [list $arg1 $args]}"
Defines a procedure poof that takes 0 or more arguments and merges them into a list of two elements. If no argument is given, the result will be the list {one {}}

Defines a new Tcl procedure that can be invoked by typing a unique abbreviation of the procedure name.

  • Any existing procedure or built-in command with the same name is overridden.
  • <name> is the name of the new procedure and can consist of pretty much any character (even a space or tab, if you enclose the argument in braces).
  • <args> is the (possibly empty) list of formal parameters the procedure accepts. Each element of the list can be either the name of local variable, to which the corresponding actual parameter is assigned before the first command of the procedure is executed, or a two-element list, the first element of which is the local variable name, as above, and the second element of which is the value to assign the variable if no actual parameter is given.
  • If the final formal parameter is named "args", the remaining actual parameters from that position on are cobbled into a list and assigned to the local variable $args. This allows a procedure to receive a variable number of arguments (even 0, in which case $args will be the empty list).
  • If the only formal parameter is "noeval", all the actual parameters are merged into a list and assigned to $noeval. Moreover, neither command- nor variable-substitution is performed on the actual parameters.
  • The return value for the procedure is specified by executing the "return" command within the procedure. If no "return" command is executed, the return value for the procedure is the empty string.
  • Whenever the new command is invoked, the contents of <body> will be executed by the Tcl interpreter. <args> specifies the formal arguments to the procedure. It consists of a list, possibly empty, each of whose elements specifies one argument. Braces and backslashes may be used in the usual way to specify complex default values.
  • When <name> (or a unique abbreviation of same) is invoked, a local variable will be created for each of the formal arguments to the procedure; its value will be the value of corresponding argument in the invoking command or the argument's default value. Arguments with default values need not be specified in a procedure invocation. However, there must be enough actual arguments for all the formal arguments that don't have defaults, and there must not be any extra actual arguments (unless the "args" keyword was used).
  • When <body> is being executed, variable names normally refer to local variables, which are created automatically when referenced and deleted when the procedure returns. One local variable is automatically created for each of the procedure's arguments. Global variables can only be accessed by invoking the global command.
  • The proc command itself returns the null string.

See Also: defsubr, return.

protect

protect <body> <cleanup>

Examples:

"protect {
var s [stream open $file w]
# do stuff with the stream
} {

catch {stream close $s}

}"
Perform some random operations on a file making sure the stream gets closed, even if the user types control-C.

Allows one to ensure that clean-up for a sequence of commands will always happen, even if the user types control-C to interrupt the command.

  • Since the interrupt can come at any time during the <body>, the <cleanup> command string should not rely on any particular variables being set. Hence the "catch" command used in the <cleanup> clause of the example.
  • The <cleanup> clause will also be executed if any command in the <body> generates an error.

See Also: catch.

range

range <value> <first> <last> [chars]

Examples:

"range {a b c} 1 end"
Returns {b c} (element 1 to the end)
"range {hi mom} 3 end chars"
Returns "mom"

Extracts a range of characters from a string, or elements from a list.

  • If you give an ending index that is greater than the number of elements in the list (characters in the string), it will be adjusted to be the index of the last element (character).
  • If you give a starting index that is greater than the number of elements in the list (characters in the string), the result will be the empty list (string).
  • You can give <end> as "end" (without the quotation marks, of course) to indicate the extraction should go to the end of the list (string).
  • The range is inclusive, so "range {a b c} 0 0" returns "a".
  • Neither index may be less than 0 or "range" will generate an error.
  • Return a range of fields or characters from value. If the chars keyword, or any abbreviation of it, is specified, then range treats <value> as a character string and returns characters <first> through <last> of it, inclusive. If <last> is less than <first> then an empty string is returned. Note: range value first first does not always produce the same results as index value first (although it often does for simple fields that are not enclosed in braces); it does, however, produce exactly the same results as list [index value first] .

See Also: index.

return

return [<value>]

Examples:

"return $val"
Returns the string in $val as the value for the current Tcl procedure.

Causes an immediate return from the current Tcl procedure, with or without a value.

  • Every Tcl procedure returns a string for a value. If the procedure was called via command substitution (having been placed between square brackets as the argument to another command), the return value takes the place of the command invocation.
  • Execution of the current procedure terminates immediately, though any <cleanup> clause for a containing "protect" command will still be executed.
  • If no "return" command is invoked within a Tcl procedure, the procedure returns the empty string by default.
  • This command may be invoked only when a procedure call is in progress. It causes the current procedure to return immediately. If <value> is specified, it will be the return value from the procedure. Otherwise the current procedure will return the empty string.

See Also: error, proc, defsubr, defcommand, defcmd.

scan

scan <string> <format> [<varname1> ]*

Examples:

"scan $input {my name is %s} name"
Trims the leading string "my name is " from the string in $input and stores the rest of the string within the variable $name

"scan" parses fields from an input string, given the string and a format string that defines the various types of fields. The fields are assigned to variables within the caller's scope.

  • The <format> string consists of literal text, which must be matched explicitly, and field definitions. The <varName> arguments are names of variables to which each successive field value is assigned.
  • A single whitespace character (space or tab) will match any number of whitespace characters in the input string. Fields are specified as for the standard C library routine "sscanf":
    • %c
    A single character. The field value stored is the decimal number of the ASCII code for the character scanned. So if the character were a space, the variable would receive the string "32".
    %d
    A signed decimal integer is parsed and stored.
    %o
    An octal integer is parsed and stored, as a decimal number.
    %x
    A hexadecimal integer is parsed and stored, as a decimal number.
    %i
    A signed integer, following the standard C radix-specification standard, is parsed and stored as a decimal number.
    %f
    A floating-point number is parsed as a "float" and stored without exponent, unless the exponent is less than -4.
    %s
    A whitespace-terminated string is parsed and stored.
    %[<char-class>]
    A string consisting only of the characters in the given character class (see "string match" for details on character classes) is parsed and stored. The normal leading-whitespace skipping is suppressed.
    %%
    Matches a single percent sign in the input.
  • If the % of a field specifier is followed by an *, the field is parsed as usual, consuming characters from the string, but the result is not stored anywhere and you should not specify a variable to receive the value.
  • The maximum length of a field may be specified by giving a decimal number between the % and the field-type character. So "%10s" will extract out a string of at most 10 characters.
  • There is a limit of 5 fields.

See Also: format.

source

source <fileName>

Examples:

"source coolness"
Evaluates all commands within the file "coolness.tcl" in the current directory.

Reads and evaluates commands from a file.

  • If <file> has no extension and doesn't exist, "source" will append ".tcl" to the end and try and read that file.
  • The return value of source is the return value of the last command executed from the file. If an error occurs in executing the contents of the file, then the source command will return that error.

string

string compare<string1> <string2> [no_case]
string first<substring> <string> [no_case]
string last<substring> <string> [no_case]
string match<string> <pattern>
string subst <string> <search> <replace> [global]

Examples:

"if {[string c [index $args 1] all] == 0}"
Do something if the 2nd element of the list in $args is the string "all".
"while {[string m [index $args 0] -*]}"
Loop while the first element of the list in $args begins with a hyphen.

Examine strings in various ways.

  • "string subst" searches <string> for occurrences of <search> and replaces them with <replace>. If 5th argument is given as "global" (it may be abbreviated), then all (non-overlapping) occurrences of <search> will be replaced. If 5th argument is absent, only the first occurrence will be replaced.
  • "string compare" compares the two strings character-by-character. It returns -1, 0, or 1 depending on whether <string1> is lexicographically less than, equal to, or greater than <string2>. If the no_case parameter is passed than it does a case insensitive compare.
  • "string first" searches <string> for the given <substring>. If it finds it, it returns the index of the first character in the first such match. If <substring> isn't part of <string>, it returns -1. If the no_case parameter is passed it does the search ignoring case.
  • "string last" is much like "string first", except it returns the index of the first character of the last match for the <substring> within <string>. If there is no match, it returns -1.
  • "string match" compares <string> against <pattern> and returns 1 if the two match, or 0 if they do not. For the strings to match, their contents must be identical, except that the following special sequences may appear in <pattern> with the following results:
    • *
    Matches any sequence of characters, including none.
    ?
    Matches any single character
    [<char-class>]
    Matches a single character within the given set. The elements of the set are specified as single characters, or as ranges of the form <start>-<end>. Thus [0-9x] matches a single character that is a numeric digit or the letter x.
    [^<char-class>]
    Matches a single character not within the given set.
    \*
    Matches an asterisk.
    \?
    Matches a question mark.
    \[
    Matches an open-bracket.

See Also: case.

uplevel

uplevel <level> <body>

uplevel <function> <body>

Examples:

"uplevel print-frame {var found1}"
Sets $found to 1 within the variables belonging to the nearest invocation of print-frame on the call stack.
"uplevel 0 {var foo-table}"
Retrieves the value of the global variable foo-table.
"uplevel 1 {var found 1}"
Sets $found to 1 within the scope of the procedure that called the one executing the "uplevel" command.

Provides access to the variables of another procedure for fairly specialized purposes.

  • <level> is a signed integer with the following meaning:
    • > 0
    Indicates the number of scopes to go up. For example, if you say "uplevel 1 {var foo 36}", you would modify (or create) the variable "foo" in your caller's scope.
    <= 0
    Indicates the number of scopes to go down from the global one. "uplevel 0 <body>" will execute <body> in the top-most scope, which means that no local variables are involved, and any variables created by the commands in <body> persist as global variables.
  • <function> is the name of a function known to be somewhere on the call stack. If the named function isn't on the call stack anywhere, "uplevel" generates an error.
  • <body> may be spread over multiple arguments, allowing the command to be executed to use variables local to the current procedure as arguments without having to use the "list" command to form the <body>.

See Also: global.

var

var <varname>
var (<name> <value>)+

Examples:

"echo [var poof]"
Prints the value stored in the variable "poof"
"var a b c d"
Assigns the string "b" to the variable "a", and the string "d" to the variable "c".
"var yes $no no $yes"
Exchanges the values of the "yes" and "no" variables

This is the means by which variables are defined in Tcl. Less often, it is also used to retrieve the value of a variable (usually that's done via variable substitution).

  • If you give only one argument, the value of that variable will be returned. If the variable has never been given a value, the variable will be created and assigned the empty string, then the empty string will be returned.
  • You can set the value of a variable by giving the value as the second argument, after the variable name. No value is returned by the "var" command in this case.
  • You can assign values to multiple variables "in parallel" by giving successive name/value pairs.
  • If invoked in a procedure on a variable that has not been declared global (using the "global" command), this applies to the local variable of the given name, even if it has no value yet.

See Also: global.

Tool Command Language: 6 Coding

This section provides information about the features and commands of Tcl that are important to know when using Swat, and the features and commands of Swat that are important to know when using Tcl. These features should be kept in mind while programming in Tcl because, if used properly, they make programming, debugging, and understanding existing commands much easier.This section will contain the following parts:

  • Swat Data Structures
    Descriptions of the major data structures and the commands that access them.
  • Examples

Swat Data Structure Commands

Examples

Tool Command Language: 6.1 Coding: Swat Data Structure Commands

symbol, type, patient, handle, brk, cbrk, event, thread, src, cache, table

This section contains information about Swat's built-in data structures and the commands that access them. These commands examine and modify vital information about the state of GEOS while it is running under Swat.

brk

brk <addr> [<command>]
brk pset <addr> [<command>]
brk aset <addr> [<command>]
brk tset <addr> [<command>]
brk clear <break>*
brk delete <break>*
brk enable <break>*
brk disable <break>*
brk address <break>
brk list [<addr>]
brk debug [<flag>]
brk isset <addr>
brk cond <break> <condition>*
brk cmd <break> [<command>]
brk delcmd <break> [<command>]

Examples:

"brk WinOpen"
Sets the machine to stop unconditionally when any thread calls WinOpen.
"brk pset WinOpen"
Sets the machine to stop when any thread for the current patient calls WinOpen.
"brk tset WinOpen"
Sets the machine to stop when any thread for the current patient calls WinOpen, and deletes the breakpoint when the machine next stops.
"brk enable 1 3-5"
Re-enables breakpoints 1, 3, 4, and 5
"brk clear 2-"
Clears all breakpoints from number 2 onward.
"brk cond 3 cx=42"
Sets breakpoint 3 to be conditional, stopping when the machine reaches the breakpoint's address with CX being 42.
"brk cond 2 (ss:0)!=1b80h"
Sets breakpoint 2 to be conditional, stopping when the machine reaches the breakpoint's address with the word at ss:0 not being 1b80h. Note that the "ss" is the value of the ss register when the "brk cond" command is executed, not when the breakpoint is reached.

Allows you to specify that execution should stop when it reaches a particular point. These breakpoints can be conditional, and can execute an arbitrary Tcl command, which can say whether the machine is to remain stopped, or continue on its way.

  • Once you've set a breakpoint, "brk" will return to you a token for that breakpoint that begins with "brk" and ends with a number. When you refer to the breakpoint, you can use either the full name (as you'll usually do from a Tcl procedure), or just the number.
  • Breakpoints have four attributes: the address at which they are set, the condition set on their being recognized, the Tcl command string to execute when they are recognized, and the Tcl command string to execute when they are deleted.
  • The condition is set either when the breakpoint is set, using the "cbrk" command, or after you've set the breakpoint, by invoking the "brk cond" command.
  • A breakpoint's condition is evaluated (very quickly) on the PC and can check only word registers (the 8 general registers, the three segment registers other than CS, and the current thread; each register may be checked only once in a condition) and a single word of memory. Each <condition> argument is of the form "<reg><op><value>". <reg> is one of the 16-bit machine registers, "thread" (for the current thread), or the address of a word of memory to check, enclosed in parentheses. <op> is a relational operator taken from the following set:
    =
    equal-to
    !=
    not-equal-to
    > < >= <=
    unsigned greater-than, less-than, greater-or-equal, and less-or-equal
    +> +< +>= +<=
    signed greater-than, less-than, greater-or-equal, and less-or-equal

    <value> is a regular Swat address expression. If it is handle-relative, and the <reg> is one of the three non-CS segment registers, the condition will be for the segment of that handle and will change automatically as the handle's memory shifts about on the heap. Similar things will happen if you specify a number as the <value> for a segment register and the number is the current segment of a block on the heap.
  • If you give no <condition> argument to the "brk cond" command, you will remove any condition the breakpoint might have, making it, therefore, unconditional.
  • If a breakpoint is given an associated <command> string, it will be evaluated before the breakpoint is taken. If the result of the evaluation is an error, a non-numeric string, or a numeric string that's non-zero, the breakpoint will be taken. Otherwise, the machine will be allowed to continue (so long as no other breakpoint command or other part of Swat insists that it remain stopped). You can use this to simply print out information when execution reaches the breakpoint address without interrupting the machine's execution.
  • The global variable "breakpoint" contains the name of the breakpoint whose command is being evaluated while that command is being evaluated.
  • You can change the command associated with a breakpoint with the "brk cmd" command. If you give no <command> argument, then no command will be executed and the breakpoint will always be taken, so long as any associated condition is also met.
  • If a breakpoint has both a condition and a command, the command will not be executed until the condition has been met, unless there's another breakpoint at the same address with a different, or no, condition.
  • You can set a breakpoint to last only during the next continuation of the machine by calling "brk tset". The breakpoint thus set will be removed when next the machine comes to a full stop, regardless of why it stopped (i.e. if it hits a different breakpoint, the temporary breakpoint will still be removed). The breakpoint will only be taken if the thread executing when it is hit is owned by the patient that was current when the breakpoint was set.
  • Each <break> argument to the "brk clear", "brk enable" and "brk disable" commands can be either a single breakpoint token (or number), or a range of the form <start>-<end>, where either <start> or <end> may be absent. If <start> is missing, the command affects all breakpoints from number 1 to <end>. If <end> is missing, the command affects all breakpoints from <start> to the last one in existence.
  • If you give no <break> argument to "brk clear", "brk enable" or "brk disable", the command will apply to all breakpoints that are specific to the current patient, i.e. that were set with the "brk pset" command, unless the current patient is the kernel, in which case they will apply to all breakpoints that are specific to no patient (i.e. those set with the "brk aset" or "brk <addr>" commands).
  • "brk address" returns the address expression for where the breakpoint is set. This will usually be of the form ^h<handle-id>:<offset>, with both <handle-id> and <offset> in hex (followed by an "h", of course). If the breakpoint is set at an absolute address, you will get back only a single hex number, being the linear address at which the breakpoint is set.
  • If you type "brk list" with no argument, Swat will print out a listing of the currently-active breakpoints. If you give an <addr> (address expression) argument, however, you'll be returned a list of the breakpoints set at the given address. If there are no breakpoints there, the list will be empty.
  • As a shortcut, you can invoke "brk isset" to see if any breakpoints are set at the given address, if you're not interested in which ones they are.

cache

cache create (lru|fifo) <maxSize> [<flushProc>]
cache destroy <cache> [flush|noflush]
cache lookup <cache> <key>
cache enter <cache> <key>
cache invalone <cache> <entry>
cache invalall <cache> [flush|noflush]
cache key <cache> <entry>
cache size <cache>
cache maxsize <cache>
cache setmaxsize <cache> <maxSize>
cache getval <cache> <entry>
cache setval <cache> <entry> <value>

Examples:

"var cache [cache create lru 10]"
Creates a cache of 10 items that are flushed on a least-recently-used basis. The returned token is saved for later use.
"var entry [cache lookup $cache mom]"
Sees if an entry with the key "mom" is in the cache and saves its entry token if so.
"echo mom=[cache getval $cache $entry]"
Retrieves the value stored in the entry for "mom" and echoes it.
"cache invalone $cache $entry"
Flushes the entry just found from the cache.
"cache destroy $cache"
Destroys the cache.

The cache command, as the name implies, maintains a cache of data that is keyed by strings. When a new entry is added to an already-full cache, an existing entry is automatically flushed based on the usage message with which the cache was created: lru (last recently used) or fifo (first in, first out). If lru , the least-recently-used entry is flushed; if fifo , the oldest entry is flushed.

  • Unlike the "table" command, the "cache" command returns tokens for entries, not their values. This allows entries to be individually flushed or their values altered.
  • If a <flushProc> is specified when the cache is created, the procedure will be called each time an entry is flushed from the cache. It will be called "<flushProc> <cache> <entry>" where <cache> is the token for the cache, and <entry> is the token for the entry being flushed.
  • If the maximum size of a full cache is reduced, entries will be flushed from the cache to bring it down to the new maximum size. The <flushProc> will be called for each of them.
  • If the values stored in the cache entries should not be freed when the cache is destroyed, pass "noflush" to "cache destroy". The default is to flush (and hence call the <flushProc>) all entries from the cache before it is destroyed.
  • If the values stored in the cache entries should not be freed when the cache is flushed, pass "noflush" to "cache invalall". The default is to call the <flushProc> for each entry in the cache before it is actually flushed.
  • If an entry is not found in the cache, "cache lookup" will return an empty string.
  • When an entry is created, "cache enter" returns a 2-list containing the entry token as its first element, and an integer, as its second element, that is either non-zero or 0, to tell if the entry is new or was already present, respectively.

cbrk

cbrk <addr> <condition>*
cbrk aset <addr> <condition>*
cbrk tset <addr> <condition>*
cbrk clear <break>*
cbrk delete <break>*
cbrk enable <break>*
cbrk disable <break>*
cbrk address <break>
cbrk list [<addr>]
cbrk debug [<flag>]
cbrk isset <addr>
cbrk cond <break> <condition>*
cbrk cmd <break> [<command>]
cbrk delcmd <break> [<command>]

Examples:

"cbrk WinOpen di=1b80h"
Stops the machine when execution reaches WinOpen() with di set to 1b80h.

Allows you to set fast conditional breakpoints.

  • All these subcommands function the same as for the "brk" command, with the exception of the "aset" and "tset" commands, which expect the condition for the breakpoint, rather than an associated command.
  • There are a limited number of these sorts of breakpoints that can be set in the PC (currently 8), so they should be used mostly for heavily-travelled areas of code (e.g. inner loops, or functions like ObjCallMethodTable() in the kernel).
  • For more information on the subcommands and the format of arguments, see the documentation for the "brk" command.

event

event <subcommand>

The event command provides access to Swat's internal events. The subcommands are as follows:

handle <eventName> <handler> [<data>]
The <handler> procedure is invoked each time an event of type <eventName> is dispatched. The handler receives two arguments: an event-specific piece of data, and the given <data>. A handler procedure should be declared
proc <handler> {arg data} {<body>}

The handle subcommand returns an <event> for later use in deleting it. The <handler> should return one of event_handled , event_not_handled , or event_stop_handling . If it returns event_stop_handling , the event will not be dispatched to any other handlers of the event.

delete <event>
Deletes the given event handler given by the event handle command.
dispatch <eventName> <arg>
Dispatches the given event with the given <arg> to all handlers of that event. If <eventName> is a pre-defined event type, <arg> will be converted to the appropriate type before being dispatched. Otherwise it is passed as a string.
create
Returns a number that represents a new event type. Handlers may then be defined for and events dispatched of the new type.
list
Lists all Tcl-registered events by event-name and handler function.

The events which are currently defined are:

FULLSTOP
Generated when patient stops for a while. Argument is string telling why the patient stopped.
CONTINUE
Generated just before the patient is continued. The argument is non-zero if going to single-step.
TRACE
Generated when the execution of a source line completes and the patient is in line-trace mode.
START
Generated when a new patient/thread is created. Argument is patient token of the patient involved.
STACK
Current stack frame has changed. The argument is non-zero if the stack change comes from a change in patients/threads or zero if the change comes from actually going up or down the stack in the current patient.
DETACH
Detaching from the PC. The argument is always zero.
RESET
Returning to the top level. The argument is always zero.
ATTACH
Attached to the PC. The argument is always zero.
RELOAD
Kernel was reloaded. The argument is always zero.
CHANGE
Current patient has changed. The argument is the token for the previous patient.
STEP
Machine has stepped a single instruction. The argument is the value to pass to patient stop if you wish the machine to stay stopped.
STOP
Machine has hit a breakpoint. The argument is the value to pass to patient stop if you wish the machine to stay stopped.
INT
Machine has hit some other interrupt that's being caught. The argument is the interrupt number. The machine will remain stopped unless it is continued with continue-patient.

handle

handle lookup <id>
handle find <address>
handle all
handle nointerest <interest-record>
handle interest <handle> <proc> [<data>+]
handle segment <handle>
handle size <handle>
handle state <handle>
handle owner <handle>
handle patient <handle>
handle other <handle>
handle id <handle>
handle isthread <handle>
handle iskernel <handle>
handle isfile <handle>
handle isvm <handle>
handle ismem <handle>

Examples:

"handle lookup [read-reg bx]"
get the handle token for the handle whose ID is in the BX register.
"handle interest $h ob-interest-proc [concat si=$chunk $message]"
call ob-interest-proc, passing the list {si=$chunk $message}, whenever the state of the handle whose token is in $h changes.
"handle patient $h"
get the token for the patient that owns the handle whose token is in $h
"handle all"
get the list of the ID's of all handles currently in Swat's handle table.

The "handle" command provides access to the structures Swat uses to track memory and thread allocation on the PC.

  • As with most other commands that deal with Swat structures, you use this one by calling a lookup function (the "lookup" and "find" subcommands) to obtain a token that you use for further manipulations. A handle token is also returned by a few other commands, such as addr-parse.
  • Handle tokens are valid only until the machine is continued. If you need to keep the token for a while, you will need to register interest in the handle using the "interest" subcommand. Most handles tokens will simply be cached while the machine is stopped and flushed from the cache when the machine continues. Only those handles for which all state changes must be known remain in Swat's handle table. For example, when a conditional breakpoint has been registered with the stub using the segment of a handle, the condition for that breakpoint must be updated immediately should the memory referred to by the handle be moved, swapped or discarded. Keeping the number of tracked handles low reduces the number of calls the stub must make to tell Swat about handle-state changes.
  • The <id> passed to the "lookup" subcommand is an integer. Its default radix is decimal, but you can specify the radix to use in all the usual ways. The value returned is the token to use to obtain further information about the handle.
  • "handle size" returns the number of bytes allocated to the handle.
  • "handle segment" returns the handle's segment (if it's resident) in decimal, as it's intended for use by Tcl programs, not people.
  • "handle owner" returns the token of the handle that owns the given handle, not its ID.
  • "handle all" returns a list of handle ID numbers not a list of handle tokens. The list is only those handles currently known to Swat.
  • "handle interest" tells Swat you wish to be informed when the handle you pass changes state in some way. The procedure <proc> will be called with two or more arguments. The first is the token of the handle whose state has changed, and the second is the state change the handle has undergone, taken from the following set of strings:
    • swapin
    Block swapped in from disk/memory
    load
    Resource freshly loaded from disk
    swapout
    Block swapped to disk/memory
    discard
    Block discarded
    resize
    Block changed size and maybe moved
    move
    Block moved on heap
    free
    Block has been freed
    fchange
    Block's HeapFlags changed

    Any further arguments are taken from the <data>+ arguments provided when you expressed interest in the handle. This subcommand returns a token for an interest record that you pass to "handle nointerest" when you no longer care about the handle. When the block is freed (the state change is "free"), there is no need to call "handle nointerest" as the interest record is automatically deleted.

  • "handle state" returns an integer indicating the state of the handle. The integer is a mask of bits that mean different things:

    • When the integer is AND-ed with the mask for Type (0xf8000), the following values indicate the following types of handles:

  • "handle other" returns the handle's otherInfo field. Note: This isn't necessarily the otherInfo field from the PC. E.g., for resource handles, it's the symbol token of the module for the handle.

patient

patient find <name>
patient name [<patient>]
patient fullname [<patient>]
patient data [<patient>]
patient threads [<patient>]
patient resources [<patient>]
patient libs [<patient>]
patient path [<patient>]
patient all
patient stop [<addr>]

Examples:

"patient find geos"
Returns the patient token for the kernel, if it's been loaded yet.
"patient fullname $p"
Returns the permanent name for the patient whose token is stored in the variable p.
"patient stop $data"
Tells the dispatcher of the STEP event that it should keep the machine stopped when the STEP event has been handled by everyone.

This command provides access to the various pieces of information that are maintained for each patient (geode) loaded by GEOS.

  • Subcommands may be abbreviated uniquely.
  • Swat always has the notion of a "current patient", whose name is displayed in the prompt. It is this patient that is used if you do not provide a token to one of the subcommands that accepts a patient token.
  • "patient name" returns the name of a patient. The name is the non- extension portion of the geode's permanent name. It will have a number added to it if more than one instance of the geode is active on the PC. Thus, if two GeoWrites are active, there will be two patients in Swat: "write" and "write2".
  • "patient fullname" returns the full permanent name of the patient. It is padded with spaces to make up a full 12-character string. This doesn't mean you can obtain the non-extension part by extracting the 0th element of the result with the "index" command, however; you'll have to use the "range" command to get the first 8 characters, then use "index" to trim the trailing spaces off, if you want to.
  • "patient data" returns a three-element list: {<name> <fullname> <thread-number>} <name> and <fullname> are the same as returned by the "name" and "fullname" subcommands. <thread-number> is the number of the current thread for the patient. Each patient has a single thread that is the one the user looked at most recently, and that is its current thread. The current thread of the current patient is, of course, the current thread for the whole debugger.
  • "patient threads" returns a list of tokens, one for each of the patient's threads, whose elements can be passed to the "thread" command to obtain more information about the patient's threads (such as their numbers, handle IDs, and the contents of their registers).
  • "patient resources" returns a list of tokens, one for each of the patient's resources, whose elements can be passed to the "handle" command to obtain more information about the patient's resources (for example, their names and handle IDs).
  • "patient libs" returns a list of patient tokens, one for each of the patient's imported libraries. The kernel has all the loaded device drivers as its "imported" libraries.
  • "patient path" returns the absolute path of the patient's executable.
  • "patient all" returns a list of the tokens of all the patients known to Swat.
  • "patient stop" is used only in STEP, STOP and START event handlers to indicate you want the machine to remain stopped once the event has been dispatched to all interested parties. <addr> is the argument passed in the STEP and STOP events. A START event handler should pass nothing.
  • A number of other commands provide patient tokens. "patient find" isn't the only way to get one.

src

src line <addr>
src read <file> <line>
src cache [<max>]
src addr <file> <line> [<patient>]

Examples:

"src line cs:ip"
Returns a two-list holding the source-line number, and the absolute path of the file in which it lies (not in this order), that encompasses CS:IP.
"src read /pcgeos/appl/sdk_c/hello/hello.goc 64"
Reads the single given source line from the given file.
"src addr icdecode.c 279"
Returns an address-list for the start of the code produced for the given line.
"src cache 10"
Allow 10 source files to be open at a time. This is the default.

The "src" command allows the Tcl programmer to manipulate the source- line maps contained in all the geodes' symbol files.

  • The "src line" commands returns its list as {<file> <line>}, with the <file> being absolute. If no source line can be found, the empty list is returned.
  • The <file> given to the "src read" command must be absolute, as the procedure using this command may well be wrong as to Swat's current directory. Typically this name will come from the return value of a "src line" command, so you needn't worry.
  • The line returned by "src read" contains no tabs and does not include the line terminator for the line (the <lf> for UNIX, or the <cr><lf> pair for MS-DOS).
  • "src addr" returns an address-list, as returned from "addr-parse", not an address expression, as you would pass to "addr-parse". If the <file> and <line> cannot be mapped to an address, the result will be the empty list.
  • The <file> given to "src addr" must be the name that was given to the assembler/compiler. This includes any leading path if the file wasn't in the current directory when the assembler/compiler was run.
  • "src cache" returns the current (or new) number of open files that are cached.

symbol

symbol find <class> <name> [<scope>]
symbol faddr <class> <addr>
symbol match <class> <pattern>
symbol scope <symbol>
symbol name <symbol>
symbol fullname <symbol>
symbol class <symbol>
symbol type <symbol>
symbol get <symbol>
symbol patient <symbol>
symbol tget <symbol>
symbol addr <symbol>
symbol foreach <scope> <class> <callback> [<data>]

Examples:

"symbol find type LMemType"
Locate a type definition named LMemType
"symbol faddr proc cs:ip"
Locate the procedure in which CS:IP lies.
"symbol faddr {proc label} cs:ip"
Locate the procedure or label just before cs:ip.
"symbol fullname $sym"
Fetch the full name of the symbol whose token is in the $sym variable.
"symbol scope $sym"
Fetch the token of the scope containing the passed symbol. This will give the structure containing a structure field, or the procedure containing a local variable, for example.

Provides information on the symbols for all currently-loaded patients. Like many of Swat's commands, this operates by using a lookup function (the "find", "faddr", "match", or "foreach" subcommands) to obtain a token for a piece of data that's internal to Swat. Given this token, you then use the other subcommands (such as "name" or "get") to obtain information about the symbol you looked up.

  • There are many types of symbols that have been grouped into classes that may be manipulated with this command. For a list of the symbol types and their meaning, type "help symbol-types". The type of a symbol can be obtained with the "symbol type" command.
  • The symbol classes are as follows:
    • type
    describes any structured type: typedef, struct, record, etype, union. Symbols of this class may also be used in place of type tokens (see the "type" command).
    field
    describes a field in a structured type: field, bitfield.
    enum
    describes a member of an enumerated type: enum, message.
    const
    a constant defined with EQU: const .
    var
    describes any variable symbol: var, chunk, locvar, class, masterclass, variantclass.
    locvar
    describes any local variable symbol: locvar, locstatic.
    scope
    describes any symbol that holds other symbols within it: module, proc, blockstart, struct, union, record, etype.
    proc
    describes only proc symbols.
    label
    describes any code-related symbol: label, proc, loclabel.
    onstack
    describes only symbols created by the directive.
    module
    describes only segment/group symbols.
    profile
    describes a symbol that marks where profiling code was inserted by a compiler or assembler.
  • The <class> argument for the "find", "faddr" and "match" subcommands may be a single class, or a space-separated list of classes. For example, "symbol faddr {proc label} CS:IP" would find the symbol closest to CS:IP (but whose address is still below or equal to CS:IP) that is either a procedure or a label.
  • The "symbol find" command locates a symbol given its name (which may be a symbol path).
  • The "symbol faddr" command locates a symbol that is closest to the passed address.
  • A symbol's "fullname" is the symbol path, from the current patient, that uniquely identifies the symbol. Thus if a procedure-local variable belongs to the current patient, the fullname would be
    <segment>::<procedure>::<name>
    where <segment> is the segment holding the <procedure>, which is the procedure for which the local variable named <name> is defined.
  • You can force the prepending of the owning patient to the fullname by passing <with-patient> as a non-empty argument ("yes" or "1" are both fine arguments, as is "with-patient").
  • The "symbol get" commands provides different data for each symbol class, as follows:
    • var, locvar, chunk: {<addr> <sclass> <type>}
    <addr> is the symbol's address as for the "addr" subcommand, <sclass> is the storage class of the variable and is one of static (a statically allocated variable), lmem (an lmem chunk), local (a local variable below the frame pointer), param (a local variable above the frame pointer), or reg (a register variable; address is the machine register number -- and index into the list returned by the "current-registers" command).
    object class: {<addr> <sclass> <type> <flag> <super>}
    first three elements same as for other variables. <flag> is "variant" if the class is a variant class, "master" if the class is a master class, or empty if the class is nothing special. <super> is the symbol token of the class's superclass.
    label-class: {<addr> (near|far)}
    <addr> is the symbol's address as for the "addr" subcommand. The second element is "near" or "far" depending on the type of label involved.
    field-class: {<bit-offset> <bit-width> <field-type> <struct-type>}
    <bit-offset> is the offset of the field from the structure/union/record's base expressed in bits. <bit-width> is the width of the field, in bits. <field-type> is the type for the field itself, while <struct-type> is the token for the containing structured type.
    const: {<value>}
    <value> is just the symbol's value.
    enum-class: {<value> <etype>}
    <value> is the symbol's value. <etype> is the enumerated type's symbol.
    blockstart, blockend: {<addr>}
    <addr> is the address bound to the symbol.
    onstack: {<addr> <data>}
    <addr> is the address at which the ON_STACK was declared. <data> is the arguments given to the ON_STACK directive.
    module: {<patient>}
    <patient> is the token for the patient owning the module.
  • A related command, "symbol tget" will fetch the type token for symbols that have data types (var-, field- and enum-class symbols).
  • "symbol addr" can be used to obtain the address of symbols that actually have one (var-, locvar- and label-class symbols). For locvar symbols, the address is an offset from the frame pointer (positive or negative). For var- and label-class symbols (remember that a procedure is a label-class symbols), the returned integer is the offset of the symbol within its segment.
  • "symbol patient" returns the token of the patient to which the symbol belongs.
  • "symbol foreach" will call the <callback> procedure for each symbol in <scope> (a symbol token) that is in one of the classes given in the list <class>. The first argument will be the symbol token itself, while the second argument will be <data>, if given. If <data> wasn't provided, <callback> will receive only 1 argument. <callback> should return 0 to continue iterating, or non-zero to stop. A non-integer return is assumed to mean stop. "symbol foreach" returns whatever the last call to <callback> returned.
  • By default, "symbol scope" will return the physical scope of the symbol. The physical scope of a symbol is the symbol for the segment in which the symbol lies, in contrast to the lexical scope of a symbol, which is where the name of the symbol lies. The two scopes correspond for all symbols but static variables local to a procedure. To obtain the lexical scope of a symbol, pass <lexical> as a non-zero number.

See Also: type.

table

table create [<initBuckets>]
table destroy <table>
table enter <table> <key> <value>
table lookup <table> <key>
table remove <table> <key>

Examples:

"var kitchen [table create 32]"
Create a new table with 32 hash buckets initially.
"table enter $t tbrk3 {1 2 3}"
Enter the value "1 2 3" under the key "tbrk3" in the table whose token is stored in the variable t.
"table lookup $t tbrk4"
Fetch the value, if any, stored under the key "tbrk4" in the table whose token is stored in the variable t.
"table remove $t tbrk3"
Remove the data stored in the table, whose token is stored in the variable t, under the key "tbrk3"
"table destroy $t"
Destroy the table $t and all the data stored in it.
(mess1:0) 159 => var yearTable [table create]
(mess1:0) 160 => table enter $yearTable synclavier 1979
(mess1:0) 161 => table enter $yearTable moog 1966
(mess1:0) 162 => table lookup $yearTable synclavier
1979
(mess1:0) 163 => var yearTable
1403188
(mess1:0) 164 => table lookup 1403188 moog
1966
(mess1:0) 165 => table remove $yearTable synclavier
(mess1:0) 166 => table lookup $yearTable synclavier
nil
(mess1:0) 167 => table destroy $yearTable

The "table" command is used to create, manipulate and destroy hash tables. The entries in the table are keyed on strings and contain strings, as you'd expect from Tcl.

  • The < initBuckets > parameter to "table create" is set based on the number of keys you expect the table to have at any given time. The number of buckets will automatically increase to maintain hashing efficiency, should the need arise, so < initBuckets > isn't a number that need be carefully chosen. It's best to start with the default (16) or perhaps a slightly larger number.
  • If no data are stored in the table under < key >, "table lookup" will return the string "nil", for which you can test with the "null" command.

thread

thread id <thread>
thread register <thread> <regName>
thread handle <thread>
thread endstack <thread>
thread number <thread>
thread all

Examples:

"thread register $t cx"
Fetches the value for the CX register for the given thread.
"thread number $t"
Fetches number swat assigned to thread when it was first encountered.
(mess1:0) 145 => patient threads
2667104
(mess1:0) 146 => thread id 2667104
11184
(mess1:0) 147 => thread all
767532 756068 1348520 1348868 1349216 1349748 1350236 1402096 1079392 2667104
(mess1:0) 148 => thread handle 756068
880428
(mess1:0) 149 => thread number 756068
0

Returns information about a thread, given its thread token. Thread tokens can be obtained via the "patient threads" command, or the "handle other" command applied to a thread handle's token.

  • Subcommands may be abbreviated uniquely.
  • "thread id" returns the handle ID, in decimal, of the thread's handle. This is simply a convenience.
  • "thread register" returns the contents of the given register in the thread when it was suspended. All registers except "pc" are returned as a single decimal number. "pc" is returned as two hexadecimal numbers separated by a colon, being the cs:ip for the thread. Note that GEOS doesn't actually save the AX and BX registers when it suspends a thread, at least not where Swat can consistently locate them. These registers will always hold 0xadeb unless the thread is the current thread for the machine (as opposed to the current thread for swat).
  • "thread handle" returns the token for the thread's handle.
  • "thread endstack" returns the maximum value SP can hold for the thread, when it is operating off its own stack. Swat maintains this value so it knows when to give up trying to decode the stack.
  • "thread number" returns the decimal number Swat assigned the thread when it first encountered it. The first thread for each patient is given the number 0 with successive threads being given the highest thread number known for the patient plus one.
  • "thread all" returns a list of tokens for all the threads known to Swat (for all patients).

type

type <basic-type-name>
type make array <length> <base-type>
type make pstruct (<field> <type>)+
type make struct (<field> <type> <bit-offset> <bit-length>)+
type make union (<field> <type>)+
type make <ptr-type> <base-type>
type delete <type>
type size <type>
type class <type>
type name <type> <var-name> <expand>
type aget <array-type>
type fields <struct-type>
type members <enum-type>
type pget <ptr-type>
type emap <num> <enum-type>
type signed <type>
type field <struct-type> <offset>
type bfget <bitfield-type>

Examples:

"type word"
Returns a type token for a word (2-byte unsigned quantity).
"type make array 10 [type char]"
Returns a type token for a 10-character array.
"type make optr [symbol find type GenBase]"
Returns a type token for an optr (4-byte global/local handle pair) to a "GenBase" structure.

Provides access to the type descriptions by which all PC-based data are manipulated in Swat, and allows a Tcl procedure to obtain information about a type for display to the user, or for its own purposes. As with other Swat commands, this works by calling one subcommand to obtain an opaque "type token", which you then pass to other commands.

  • Type tokens and symbol tokens for type-class symbols may be freely interchanged anywhere in Swat.
  • There are 11 predefined basic types that can be given as the <basic-type-name> argument in "type <basic-type-name>". They are: byte (single-byte unsigned integer), char (single-byte character), double (eight-byte floating-point), dword (four-byte unsigned integer), float (four-byte floating-point), int (two-byte signed integer), long (four-byte signed integer), sbyte (single-byte signed integer), short (two-byte signed integer), void (nothing. useful as the base type for a pointer type), and word (two-byte unsigned integer)
  • Most type tokens are obtained, via the "symbol get" and "symbol tget" commands, from symbols that are defined for a loaded patient. These are known as "external" type descriptions. "Internal" type descriptions are created with the "type make" command and should be deleted, with "type delete" when they are no longer needed.
  • An internal structure type description can be created using either the "pstruct" (packed structure) or "struct" subcommands. Using "pstruct" is simpler, but you have no say in where each field is placed (they are placed at sequential offsets with no padding between fields), and all fields must be a multiple of 8 bits long. The "struct" subcommand is more complex, but does allow you to specify bitfields.
  • "type make pstruct" takes 1 or more pairs of arguments of the form "<field> <type>", where <field> is the name for the field and <type> is a type token giving the data type for the field. All fields must be specified for the structure in this call; fields cannot be appended to an existing type description.
  • "type make struct" takes 1 or more 4-tuples of arguments of the form "<field> <type> <bit-offset> <bit-length>". <field> is the name of the field, and <type> is its data type. <bit-offset> is the offset, in bits, from the start of the structure (starting with 0, as you'd expect). <bit-length> is the length of the field, in bits (starting with 1, as you'd expect). For a bitfield, <type> should be the field within which the bitfield is defined. For example, the C declaration:
    •  struct {
     word a:6;
     word b:10;
     word c;
  }

    would result in the command "type make struct a [type word] 0 6 b [type word] 6 10 c [type word] 16 16", because a and b are defined within a word type, and c is itself a word.

  • "type make union" is similar to "type make pstruct", except all fields start at offset 0. Like "pstruct", this cannot be used to hold bitfields, except by specifying a type created via "type make struct" command as the <type> for one of the fields.
  • "type make array <length> <base-type>" returns a token for an array of <length> elements of the given <base-type>, which may be any valid type token, including another array type.
  • "type make <ptr-type> <base-type>" returns a token for a pointer to the given <base-type>. There are several different classes of pointers in GEOS:
    • nptr
    a near pointer. 16-bits. points to something in the same segment as the pointer itself.
    fptr
    a far pointer. 32-bits. segment in high word, offset in the low.
    sptr
    a segment pointer. 16-bits. contains a segment only.
    lptr
    an lmem pointer. 16-bits. contains a local-memory "chunk handle". data pointed to is assumed to be in the same segment as the lptr itself, but requires two indirections to get to it.
    hptr
    a handle pointer. 16-bits. a GEOS handle.
    optr
    an object pointer. 32-bits. contains a GEOS memory handle in the high word, and a GEOS local-memory chunk handle in the low.
    vptr
    a VM pointer. Its 32 bits contain a GEOS file handle in the high word and a GEOS VM block handle in the low word.
    vfptr
    a virtual far pointer. Its 32 bits contain a virtual segment in the high word and an offset in the low.
  • "type delete" is used to delete a type description created by "type make". You should do this whenever possible to avoid wasting memory.
  • Any type created by the "type make" command is subject to garbage collection unless it is registered with the garbage collector. If you need to keep a type description beyond the end of the command being executed, you must register it. See the "gc" command for details.
  • "type size" returns the size of the passed type, in bytes.
  • "type class" returns the class of a type, a string in the following set:
    • char
    for the basic "char" type only.
    int
    any integer, signed or unsigned.
    struct
    a structure, record, or union.
    enum
    an enumerated type.
    array
    an array, of course,
    pointer
    a pointer to another type.
    void
    nothingness. Often a base for a pointer.
    function
    a function, used solely as a base for a pointer.
    float
    a floating-point number.
  • Each type class has certain data associated with it that can only be obtained by using the proper subcommand.
  • "type aget" applies only to an array-class type token. It returns a four-element list: {<base-type> <low> <high> <index-type>} <base-type> is the type token describing elements of the array. <low> is the lower bound for an index into the array (currently always 0), <high> is the inclusive upper bound for an index into the array, and <index-type> is a token for the data type that indexes the array (currently always [type int]).
  • "type fields" applies only to a struct-class type token. It returns a list of four-tuples {<name> <offset> <length> <type>}, one for each field in the structure. <offset> is the bit offset from the start of the structure, while <length> is the length of the field, again in bits . <type> is the token for the data type of the field, and <name> is, of course, the field's name.
  • "type members" applies only to an enum-class type token. It returns a list of {<name> <value>} pairs for the members of the enumerated type.
  • "type pget" applies only to a pointer-class type token. It returns the type of pointer ("near", "far", "seg", "lmem", "handle", or "object") and the token for the type to which it points.
  • "type bfget" returns a three-list for the given bitfield type: {<offset> <width> <is-signed>}
  • "type signed" returns non-zero if the type is signed. If the <type> is not an int-class type, it is considered unsigned.
  • "type emap" can be used to map an integer to its corresponding enumerated constant. If no member of the enumerated type described by <type> has the value indicated, "nil" is returned, else the name of the matching constant is returned.
  • "type field" maps an offset into the passed struct-class type into a triple of the form {<name> <length> <ftype>}, where <name> can be either a straight field name, or a string of the form <field>.<field>... with as many .<field> clauses as necessary to get to the smallest field in the nested structure <type> that covers the given byte <offset> bytes from the start of the structure. <length> is the bit length of the field, and <ftype> is its type.
  • "type name" produces a printable description of the given type, using C syntax. <varname> is the name of the variable to which the type belongs. It will be placed at the proper point in the resulting string. If <expand> is non-zero, structured types (including enumerated types) are expanded to display their fields (or members, as the case may be).

See Also: gc, symbol, value.

Tool Command Language: 6.2 Coding: Examples

This section will contain a few examples of Tcl code for Swat commands, showing the use of some of included Tcl commands. A good way to view the code for a particular procedure is to type:

info body <procname>

on the Swat command line. This will print out the body of the given <procname> . One thing to watch out for, however, is the case when a procedure has not been loaded into Swat yet (i.e. it has not been used yet). If this is the case, Swat will have no information about the procedure and will thus print nothing. The command must be loaded into Swat either with the load command, or by just typing the command name which will usually autoload the command. (See Using a New Command .) Then the info body <procname> command can be used.

Some code examples:

The Whatat Command

[ defcommand whatat {addr} output
{Given an address, print the name of the variable at that address}
{
  var a [ sym faddr var $addr]
     if {[ null $a]}{
      echo *nil*
    } else {
     echo [sym name $a]
 }
}]

This example shows the code of the whatat command. Note the use of the sym (an abbreviation for symbol ) command to find the address of the given variable <addr> of class <var>.

The Bytes Command

1	 var addr [ get-address $addr ds:si]
2	var base [ index [addr-parse $addr] 1]
3	 echo {Addr: +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f}
4	#fetch the bytes themselves
5	var bytes [ value fetch $addr [  type make array $num [type byte]]]
6	#
    # $s is the index of the first byte to display on this row, $e is the
    # index of the last one. $e can get > $num. The loop handles this case.
    #
    var s 0 e [ expr 16-($base&0xf)-1]
    #
    # $pre can only be non-zero for the first line, so set it once here.
    # We'll set it to zero when done with the first line.
    # $post can be non-zero only for the last line, but we can't just
    # set it to zero and let the loop handle it, as the first may be the
    # last, so...
    #
    var pre [expr 16-($e-$s)-1]
    if {$e > $num} {
    var post [expr $e-($num-1)]
    } else {
    var post 0
    }

    [ for {var start [expr {$base&~0xf}]}
    {$s < $num}
    {var start [expr $start+16]}
    {
28	#extract the bytes we want
29	var bs [ range $bytes $s $e]

30	echo [ format {%04xh: %*s%s%*s "%*s%s%*s"} $start
    [expr $pre*3] {}
    [ map i $bs {format %02x $i}]
    [expr $post*3] {}
    $pre {}
    [ mapconcat i $bs {
    if {$i >= 32 && $i < 127} {
    format %c $i
    } else {
    format .
    }
    }]
    $post {}]
    var s [expr $e+1] e [expr $e+16] pre 0
    if {$e >= $num} {
    var post [expr $e-($num-1)]
    }
    }]
     set-address $addr+$num-1
     set-repeat [format {$0 {%s} $2} $addr+$num]

This example shows the code for the bytes commands. Notice the use of the type command on the fifth line, and the range command on the twenty-ninth line.

Tool Command Language: 7 Using a New Command

Once a new command is written, it needs to be loaded into Swat so that it can be used. Depending on how the command is to be used, you may be interested in any of the following topics:

  • Compilation
  • Autoloading
  • Explicit loading

Compilation

Autoloading

Explicit Loading

Tool Command Language: 7.1 Using a New Command: Compilation

It is possible to byte-compile a Tcl script. The bc Tcl command creates a .TLC file containing compiled Tcl code--this code will run faster than normal Tcl code. When loading, Swat will load a .TLC file instead of a .TCL file where possible. Making changes to compiled Tcl functions involves changing the source code and re-compiling.

Tool Command Language: 7.2 Using a New Command: Autoloading

If the development environment has been set up properly, there should already exist the /pcgeos/Tools/swat/lib directory on the workstation. This directory will contain all of the code files for the built-in Swat commands. To autoload a new command, copy its code file to the /pcgeos/Tools/swat/lib directory and add its name to the autoload. tcl file in the directory. This will load the command into Swat every time Swat is started. For example, say the command blitzburp has just been written to examine a new data structure. First, copy the file containing its code (say blitz. tcl) into the /pcgeos/Tools/swat/lib directory. Next, edit the autoload. tcl file and add one of the following lines:

[autoload blitzburp 0 blitz]
[autoload blitzburp 1 blitz]

This will ensure that blitz.tcl will be loaded when the command blitzburp is first used. The 0 indicates that the command must be typed exactly, and the 1 indicates that the interpreter will not evaluate arguments passed to the command. (Full reference information is available for the autoload command.)

Tool Command Language: 7.3 Using a New Command: Explicit Loading

Another way to load a command into Swat is to use the load command from the Swat command line. This command is simply load <path>/<filename> . If no path is given, then the <file> is loaded from the directories specified in the load-path variable. The load command will load the given file (a Tcl procedure or subroutine) into Swat for subsequent use, and it is mostly used to load infrequently accessed files. (See , for more information on the load command.)

This document is a single-page version of a a multi-page document, suitable for easy printing.


Using Tools

This chapter gives a reference of the tools included in this development kit. This chapter is not intended to teach you how to use the tools; it provides a reference only. To learn how to use most of the tools, see the Tutorial included in the development kit and the First Steps chapter in the Concepts book.

1 Tools Summary
2 Typical Development Session
3 File Types
4 Esp
5 Glue
6 Goc
7 Grev
8 mkmf
9 pccom
9.1 PCCOM Background
9.2 Running PCCOM on the Target
9.3 File Transfer Protocol of PCCOM
10 pcget
11 pcs
12 pcsend
13 pmake
13.1 Copyright Notice and Acknowledgment
13.2 How to Customize pmake
13.3 Command Line Arguments
13.4 Contents of a Makefile
13.5 Advanced pmake Techniques
13.6 The Way Things Work
14 Swat Stub
15 dumpgeo
16 cvtpcx
17 pvm

Using Tools: 1 Tools Summary

The tools provided in this kit are listed below. Each is described in full in the following sections.

pmake
"Make" utility which has knowledge of Goc. The pmake tool manages geode compilation and linking, making appropriate calls to Goc and Glue.
Goc
C preprocessor which is aware of programming constructs specific to GEOS programming. The Goc tool looks for special keywords (such as @class and @object) and creates the proper GEOS data structures so that the geode will work correctly after being compiled and linked. Chances are you won't call Goc directly but will instead use pmake , which will call Goc in turn.
Esp
Assembler with awareness of GEOS programming constructs. Also includes support for creating enumerated types, complicated records, and more. Chances are you won't call Esp directly but will instead use pmake , which will call Esp in turn.
Glue
GEOS linker. This determines how the program's resources should be set up and how those resources can be relocated. It determines file offsets and how to access library functions. As with Goc, you probably won't call Glue directly but will instead use it through pmake .
grev
Revision number generator. This is a handy utility for generating revision numbers for geodes, which may be used to keep track of library protocols. The pmake program uses grev to keep track of incremental changes; if you make large changes to a geode at some point, you can use grev to modify your geode's revision file to reflect your changes.
mkmf
Makefile maker. Creates a MAKEFILE which will in turn be used by pmake to determine how to create the geode's files.
pccom
Communication manager. Sets up communication over serial lines. This manages data transfer and protocols when sending files between machines or debugging.
pcs
Geode downloader. Sends geodes from the development machine to their proper directory on the target machine.
pcsend
File downloader. Sends an arbitrary file from the development machine to an arbitrary directory on the target machine.
pcget
File uploader. Retrieves an arbitrary file on the target machine, transferring it to any directory on the development machine.
Swat
GEOS debugger. This program runs on the development machine, monitoring GEOS programs running on the target machine.

Using Tools: 2 Typical Development Session

The Tutorial shows in detail how to write, compile, download, and debug an application. As a quick guide, however, the following list recaps the steps in a typical development session:

  1. On the development machine, use a text editor (perhaps the one that came with your compiler) to write or edit the source (.c and .goc files, perhaps .asm or .ui files if you have the Esp assembler), header (.h and .goh files, perhaps .def files if you have the Esp assembler), and Glue parameters (.gp files) for your geode.The source files for the geode should be arranged within your development directory.
  2. If you have never compiled the geode before or have added or included new files since the last compilation, you must run mkmf to create or update the MAKEFILE.
  3. If you have added or included new files since the last compilation, you should execute pmake depend so that the compiler will know which files need to be remade if one is altered.
  4. Run pmake . This compiles and links the geode's source files, using directions provided by the MAKEFILE.
  5. To test the geode, either run it on the target machine or run swat from the development machine to debug it.

Using Tools: 3 File Types

You may be curious to know what sorts of files you'll be working with. If you have to work with someone else's code, then being able to find your away around their files (knowing which are sources, which are objects, and which are chaff) can be very useful.

*.goc
These are Goc source files. You will write these files. They contain both standard C code and GEOS constructs (such as objects and messages).
*.goh
These are Goc header files. You will write these files and include others. They provide definitions used by your .goc files (in the same relation as between standard C source and header files). Unlike standard C header files, these are included using the @include Goc keyword.
*.poh , *.ph
These are files generated to optimize the @inclusion of .goh files. Goc will automatically generate these when they don't already exist.
*.doh , *.dh
These are files generated to optimize the @inclusion of .goh files. They contain dependency information.
*.c
These are standard C source files. You may write these as source files, using only standard ANSI C constructions. The Goc preprocessor will create .c files from .goc files. Thus, if you see two files with the same prefix, but one has the .c suffix and the other has the .goc suffix, then you know that the first was created from the second.
Note that Goc will create the generated .c file in the directory where it is invoked. Thus if your development tree contains files PROG\DIR1\CODE.GOC and PROG\DIR2\CODE.GOC, then if you convert these using Goc from the PROG directory, then one of the generated .c files will overwrite the other. Thus, you should never give .goc files the same prefix, even if they are in different directories.
*.h
These are standard C header files. You may write these and include them using the ANSI C #include directive.
*.asm
These are standard GEOS assembly source files, which may be assembled with the Esp assembler, if you have it. They may contain both standard assembly and Esp constructs.
*.def
These are standard GEOS assembly header files, which you may write or include if you have access to the Esp assembler.
*.mk, makefile
These are "makefiles," files which contain scripts which the pmake tool will interpret and use to automatically compile and link your geode. In a source directory there will be a file called MAKEFILE (created with the mkmf tool) and probably a file called DEPENDS.MK (created by calling pmake depend ). If you wish to customize how your geode is made, you will probably write a file called LOCAL.MK, containing your custom makefile script. The INCLUDE directory contains several .mk files, which will be #included by other makefiles.
*.gp
These are "Glue parameter" or "geode parameter" files, which will give the Glue linker information necessary when linking a geode. You will write this file. The pmake program assumes that a geode's .gp file will have name geode.GP, where geode is taken from the name of the directory containing the geode's source (e.g. in the example above, pmake would expect the .gp file to be named PROG.GP).
*.ldf
These are library definition files. Glue uses these files when linking your geode; they determine how your calls to a GEOS library will be encoded. If you are writing a library, then you will create one of these files by means of a pmake lib . The pmake program looks for .ldf files in the INCLUDE\LDF directory.
*.rev
This is a revision file, used to keep track of a program's revision and protocol levels (useful for tracking compatibility). The pmake tool will look for a file with name geode .REV, where geode is taken from the name of the directory containing the geode's source (e.g. in the example above, pmake would expect the .rev file to be named PROG.REV). The pmake program uses grev to create and maintain the .rev file; you should use grev yourself when you need to signal a major revision.
*.rsc
Localization resource file. This file contains information which will be used by the ResEdit localization tool.
*.obj
These are object files. These are files created by a C compiler or Esp which may be linked to form an executable. The pmake program uses Glue to link the object files.
*.ebj
These are error-checking object files. GEOS supports the notion of "error-checking code." When you write your programs, you can mark some commands as "error checking commands." These commands might make sure that a routine is passed valid arguments or perhaps purposefully destroy some information which was not guaranteed preserved by a routine. Such commands may prove time-consuming but are useful for making sure that an application is robust. The pmake program will create two versions of your application--one which includes the EC (Error Checking) code, and one which doesn't. Run the EC program to check for correctness, but use the non-EC version when the program should be fast (i.e. this is the version you should give to your customers).

The .obj files will be linked to form a non-EC executable; .ebj files to form an EC executable.

*.geo
This is a Geode, a GEOS executable (either an application, library, or driver), the end result of your efforts. These are the files containing the code for GEOS programs which the user will interact with. They are created by linking together a number of .obj files, with additional information provided by a .gp file. You will place these files in your GEOS testing directory on your target machine (along with *ec.geo files, described below).
*ec.geo
This is an error checking geode. (See above for quick descriptions of error checking code and geodes.) They are created by linking together a number of .obj and .ebj files, with additional information provided by a .gp file.
*.sym
This is a symbol file, containing symbolic debugging information which the Swat debugger can use to access the geode's data structures.
*ec.sym
This is the symbol file of the error checking version of a geode.
tmp*.*
These are temporary files which pmake will create and destroy while making your executable. The pmake program uses these files to pass arguments to the other tools. Thus, if you see a file of this name in your directory and you didn't create it, you can assume that pmake was interrupted in a recent make and was unable to erase the file (and thus it is safe for you to erase it).
.tcl , .tlc
These are Tcl files, files containing Tool Command Language source code, used by the Swat debugging tool. The .tcl files contain Tcl source code, the .tlc files contain compiled Tcl code. The source code may be edited by any text editor and Swat will interpret it; compiled code runs more quickly, but can only be changed by editing the source code and re-compiling.

If you are writing a GEOS C application, you will write the following types of files:

  • Source files: .goc files, optional .c files.
  • Glue parameters file: geode.GP.
  • Optional header files: .goh files, .h files.
  • Optional custom make file: LOCAL.MK.

After you have made your geode the first time (creating a makefile with mkmf , a dependencies file with pmake depend , and the geode itself with pmake ), your directory should contain the following additional file types:

  • Intermediary C files: .c files (made by transforming .goc files).
  • Makefile: MAKEFILE
  • Dependencies file: DEPEND.MK
  • Revision file: geode.REV.
  • Object files: .obj and .ebj files.
  • Symbol files: .sym files
  • Geodes: .geo files.

Using Tools: 4 Esp

Esp (pronounced "esp") is the GEOS assembler. It creates object files from Esp code--said code using a superset of MASM syntax. These object files may then be linked by means of the Glue tool.

Most users will never call Esp directly, instead going through pmake , which will make the proper calls to Esp for the most common cases.

Esp takes the following options:

-2
Code produced should be DBCS--characters will be two bytes instead of one.
-I directory
Specifies an additional directory in which to look for included files.
-o filename
Name to give to created object file.
-w warntype
- W warntype
Turn warnings off or on
unref
Warn if a symbol that can only be used during this assembly isn't.
field
Warn if structure field used with . operator when lhs isn't of the type that contains the field
shadow
Warn if a local variable or procedure overrides a definition in a larger scope
private
Warn if a private method or instance variable is used outside a method handler or friend function related to the class that defined the thing
unreach Warn about code that cannot be reached. This is very simplistic
unknown Warn if a far call is made to a routine whose segment is unknown
record
Warn if a record initializer doesn't contain all the fields of the record
fall_thru
Warn if a function falls into another one without a .fall_thru directive
inline_data
Warn if a variable is defined where execution can reach
unref_local
Warn if a local label isn't ever referenced
jmp
Warn if out-of-range jumps are transformed into short jumps around near jumps
assume
Warn when override is generated based on segment assumptions
all
Used to turn all warnings on or off.
-M
This assembly is just for the purpose of determining what the source file's dependencies is. Instead of creating an object file, Esp will create a temporary file which the dependencies maker will use to determine the dependencies.
-d
Activate's Esp debugging mode. Useful only when trying to track down a bug in Esp.

Using Tools: 5 Glue

Glue is the GEOS linker. It creates GEOS or DOS executables from object files. (It can also create GEOS VM and font files, if you have the appropriate tools.) These object files may have been created by a C compiler, or by the Esp assembler. To create the executables, Glue must create a combined file, resolve external declarations, determine how to call libraries, and apportion the code and data into resources. Glue will also create a .sym file--a file containing symbolic debugging information which the Swat debugger will use for viewing the geode's data.

Most users will never call Glue directly, instead going through pmake , which will make the proper calls to Glue for the most common cases. If you use the default makefiles, the variable LINKFLAGS will determine which arguments Glue receives; thus, by creating a local makefile that modifies this variable's value, you can pass more flags to glue. See How to Customize pmake for help in creating a local makefile.

The Glue application takes the following arguments:

glue @file
glue <flags> <objFile>+ [-l<objFile>]*
@file
The Glue linker should take its arguments from the file file in addition to those on the command line. This may come in handy if you often use the same long string of options. Since you may need to pass Glue more arguments than may be input on the command line, sometimes this option is necessary.

Note that if you use this option, then all arguments must be included in the file--there should be no others on the command line itself.

The pmake program uses this option to pass arguments to Glue.

-D var
Defines the variable var ; used with ifdef statements within the .gp file to allow for conditional linking (see the GP file keyword reference for the syntax of Glue's ifdef statements).
-L path
Specifies where Glue looks for .ldf (library definition) files. These files are placed in a standard directory by the system makefiles on a " pmake lib¨.
-N string
Specify a copyright notice. This string must be 32 characters or less.
-Oe
Creating DOS executable (".exe") file. Of course, this option is not valid if the object files contain GEOS directives which will not work outside GEOS.
-Oc
Creating DOS command (".com") file. Of course, this option is not valid if the object files contains GEOS directives which will not work outside of GEOS.
-Og file
Creating GEOS executable (".geo") file. You must provide the name of the geode's Glue parameters file (the .GP file). For information about setting up a parameter file, see the Hello World chapter.

When creating a .geo file, you may pass any of the following options:

-E
Link Error checking version of geode.
-R number
Specify release number of geode (e.g. 3.2.1.0).
-P number
Specify protocol number of geode (e.g. 1.0).
-T number
File type.
-l
Creating a library; Glue should create .ldf file.
-Ov
Creating GEOS Virtual Memory (.vm) file. Using this option, you may create .vm storage files as set up by the Esp assembler. You may pass the following options when creating .vm files (if you don't know the meaning of some of these terms, see the Virtual Memory chapter):
-A number
VMAttributes to use for the file.
-C number
Compaction threshold.
-M string
Map block segment name.
-P number
Protocol number (e.g. 2.5.0.3).
-R number
Release number (e.g. 12.3).
-i name
A .geo file from which to get the table of imported libraries. This allows the VM file to be opened by that geode and objects in the file to be used.
-t token
File token
-c token
Creator's manufacturer token.
-l string
File's long name.
-u notes
File's user notes.
-N string
Copyright notice which will be embedded in header of .GEO or .VM files.
-G number
You should never have to use this option. This specifies a non-standard GEOS release number (e.g. 1.3); if this release used a different VM header than GEOS 2.0, Glue will still construct the proper header as long as this option is passed. Since the default release number is 2, developers for 2.X do not need this option.
-Wall
Requests that Glue output all optional warnings.
-Wunref
Requests that Glue output optional unreferenced global symbol warnings.
-d
Dump memory. Normally used only when debugging Glue.
-m
Provide memory map in output. This information gives information about the sizes of various parts of the geode. This information proves especially helpful when making geodes work with small devices.
-nll
Disables the output of line numbers for local memory segments for any application with 163 resources or more.
-o file
Specify name of output file (e.g. WOROPRO.GEO).
-q
Leave the symbolic information behind even if an error was encountered. Normally, this flag is used only when debugging Glue.
-r
Maps segment relocations to non-shared resources to resource IDs. This is normally used only by multi-launchable C applications. When running more than one instance of a multi-launchable application, the system only uses one copy of the read-only portion of the application. The system makes separate copies of the writable data for each instance of the application. This can lead to conflict when the relocation instructions for the read-only data uses the handle or segment of a writable resource; which copy should be responsible for providing these addresses? If you don't pass this flag and Glue detects the above situation, Glue will simply refuse to make the application multi-launchable. This flag instructs Glue to instead use the resource ID of the writable resource where it would normally use the segment or handle of that resource.
If you use this option, make sure that if you use the address of a variable in a resource other than dgroup that you use GeodeGetOptrNS() and lock down or dereference the handle of the returned optr.
-s file
Specify name of symbol file (the .SYM file).
-z
Output localization information.

Using Tools: 6 Goc

Goc is the GEOS C preprocessor, which will turn your .goc file into something a regular C compiler can understand. It will traverse the .goc file, detect Goc keywords (e.g. @class, @object), turn these keywords into appropriate pieces of code, and write the resulting file out to a .c file. Note that Goc acts as a simple filter, and will only make changes where it detects Goc constructs; it won't touch your regular C code at all.

Under normal circumstances, you will not invoke Goc directly. Instead, pmake will make calls to Goc when compiling .goc files.

If for some reason you do need to invoke Goc directly, you may wish to know about its command line arguments:

goc @file
goc [args] <file>
@ file
Command-line arguments are stored in a file--Goc will read this file and treat the contents as its arguments. If you use this option, then all the arguments must be in the file--no others may appear on the command line itself.
-
Redirect output to standard out.
-C segment
Specify segment for class structures to reside.
-D macro
Set variable macro to one (e.g. "-D__GEOS__")
-D macro = value
Set variable macro to value.
-I dir
Specify additional include directory
-I-
Turn off relative inclusion.
-L name
Specify name of library being compiled. Must match argument to @deflib Goc keyword in library's .goh file, if it has one.
-M
Help pmake generate dependency information.
-cb
Generate information for Borland compiler.
-d
Turn on all debugging information.
-dd
Output @default debugging information. (See the GOC Language chapter to find out what @default means).
-dl
Output lexical analyzer debugging information.
-dm
Output message parameter definition debugging information.
-ds
Output symbolic debugging information.
-du
Output Goc-specific debugging information.
-dy
Output parser debugging information.
-l
Do localization work.
-o file
Specify name of output file.
-p
For Geoworks use only.
-X
Ignore @optimize flags

Using Tools: 7 Grev

GEOS supports two version numbers for each geode. The first of these is the release number, used to uniquely identify the release of the geode. The protocol number tracks the external interface of the file. This is used to determine what versions of related geodes can be used together. The kernel will use these numbers to prevent loading of incompatible executable files.

The grev utility generates proper revision numbers. Normally, it is called automatically by pmake , so if you are just making a small change to a file, you need not call it directly. However, you may wish to. When using grev, you must think about how major a change you are making; a large change means that you should change an earlier number of the release number. A change from 1.2.3.4 to 2.0.0.0 should signal a larger step than a change to 1.2.3.5.

The pmake program uses grev to automatically create revision numbers for geodes; it passes these values to Glue, which in turn places the protocol and revision numbers in the .geo and .sym files.

There are three widely used methods for incrementing release numbers with respect to public releases (for which a specific release number is desired for marketing, say "2.0.0"). The problem comes because it is not known until after a release has been built whether it will be the release or not (since bugs may be found).

The first method is to keep separate public release numbers and internal release numbers. This is awkward and confusing and is generally done when it is too late to do anything else.

The second method is to number successive revisions "1.14.0.12", "1.14.0.13", "1.14.0.14" and so on until the final revision is made which is numbered "2.0.0.0". The problem with this is that one never quite knows whether or not a revision is the final one (since bugs may be found).

The third method is to number successive revisions "2.0.0.12", "2.0.0.13", "2.0.0.14" and so on. The released "2.0.0" revision is then the last engineering revision starting with "2.0.0.X". The disadvantage of this method is that it can seem non-obvious at first and requires a little bookkeeping to know the engineering number of the released version.

The protocol number is changed whenever the external interface for the file changes. For the kernel and for libraries the protocol reflects the order as well as the parameters and behavior of external entry points. For applications the protocol reflects the object names, types and attributes. Changes that do not affect the external interface (changing the implementation of a routine, changing the moniker or hints of an object) do not change the protocol number.

The major protocol number reflects incompatible changes in interface, such as rearranging the order of entry points. The minor protocol number reflects upwardly compatible changes in the protocol (such as adding an entry point at the end of a jump table or using a bit formerly marked as "reserved").

Each executable file contains protocol compatibility information (a protocol number) for all other executable files on which it depends. For example, a simple application might be compatible with kernel protocol "34.2" and UI protocol "19.7". Thus the application is compatible with kernels "34.2" through "34.65535" and with UIs "19.7" through "19.65535".

A protocol number is also stored with each state file to determine if the state can be recovered by the currently running application.

The grev tool uses a file (normally marked with a .rev suffix) in the geode's development directory to keep track of the revision number. The file is organized chronologically, with later entries at the beginning of the file. It contains

  • one line for each compilation, denoting the revision number (which is incremented on each compilation), optional user name, the date, and an optional comment. By default, pmake will only change the last part of the release number.
  • one line for each protocol change, denoting the protocol number, optional user name, the date, and an optional comment.

Using the grev utility:

In the Nokia 9000i SDK version 2 (refer to versions), the .rev file may be in the local or in the "Installed" branch. It is suggested that developers keep a local .rev file so that they don't have to make revisions to the branch by hand.

The grev utility uses the following syntax:

     grev <option> <rev filename> [<comment>|-P|-R] -s
It is necessary to pass the -s flag to save the protocol change. There are few circumstances where this flag is not needed.

<options> may be one of the following:

new file [" comment "|-P|-R]
Create a new revision record, listing comment as an initial revision for the base (0.0.0.0 release, 0.0 protocol). This command may only be executed in the geode's development directory. The -P option causes grev to give minimal output, printing only the protocol number. The -R option causes grev to print only the revision number. These last two options are normally used by pmake to extract the relevant numbers.
info file
Print the current release and protocol from the revision file.
getproto file
Print only the current protocol from the revision file.
newprotomajor file [" comment "|-P|-R]
NPM file [" comment "|-P|-R]
Increase the major protocol number by one, setting the minor number to zero. The comment argument is listed as the reason for the change in the file. The -P and -R options work as they do for grev new .
newprotominor file [" comment "|-P|-R]
npm file [" comment "|-P|-R]
Increase the minor protocol number by one. The comment string is listed as the reason for the change in the file. The -P and -R options work as they do for grev new .
newrev file number1.number2.number3 [" comment "|-P|-R]
Increase release number from A.B.C.D to number1.number2.number3.0. The comment is listed as the reason for the change. The -P and -R options work as they do for grev new .
newchange file [" comment "|-P|-R]
Up release number from A.B.C.D to A.B.C+1.0. The comment is listed as the reason for the change. The -P and -R options work as they do for grev new .
neweng file [" comment "|-P|-R]
Increase release number from A.B.C.D to A.B.C.D+1. The comment is listed as the reason for the change. The -P and -R options work as they do for grev new .
<rev filename> is the name of the revision file (ending in .REV).

<comment> is an optional comment embedded in quotes.

-P causes grev to give minimal output, printing only the protocol number. This flag is normally used by pmake to extract to protocol number.

-R causes grev to print only the revision number. pmake uses this flag to extract the version number.

-s, as noted above, causes the change to be saved to the revision file. If this flag is not passed, the change will only be displayed to the screen.

Using Tools: 8 mkmf

The mkmf tool exists to create a file named MAKEFILE. The pmake program will use this file as a sort of script, using it to determine how to compile and link the geode. However, makefiles can get rather complicated, so it is best to create them using mkmf instead of by hand.

For information about customizing this boilerplate makefile, see pmake .

The mkmf tool uses the following rules to build the makefile:

  • The geode name is taken from the name of the directory in which you run mkmf. Among other things, this means that if you change the name of your development directory, you should also run mkmf again.
  • Any file in the current directory or its subdirectories will be considered a source file if it has one of the following suffixes: .asm, .def, .ui, .c, .h, .goc, or .goh (except that .c files which are generated from .goc files will not be considered sources).
  • For each .asm, .c, or .goc file, an .obj file with corresponding name will be added to the makefile variable OBJS.
  • If there are subdirectories, then each subdirectory is considered to hold the source for a module of the program, and mkmf will work with the files in this subdirectory as a unit. The module's name will be added to the CMODULES variable if it contains .c or .goc files; if it contains .asm files, then it will be added to the MODULES list. When using pmake , each module will be considered something that can be made, a sort of intermediate step towards making the whole geode. If you do not wish the files in a subdirectory to be incorporated in the program, create a file in the directory called NO_MKMF. This file need have no contents.

Using Tools: 9 pccom

The pccom tool manages communication between the development and target machines. It assumes that the machines are connected by a single serial line. All I/O is interrupt-driven with XON/XOFF flow control active on the development machine and obeyed on the target machine.

Note that it is possible to use some features of pccom from within GEOS. For more information about this, see the PCCom Library documentation.

PCCOM Background

Running PCCOM on the Target

File Transfer Protocol of PCCOM

Using Tools: 9.1 pccom: PCCOM Background

PCCOM is used in two primary situations. First, it is used by GEOS software developers when transferring files or when debugging an application. In this situation, the developer runs PCCOM on the target machine and then runs PCS, PCSEND, or PCGET, on the host machine. All of these programs know how to interact with PCCOM.

Second, it is used by DOS programs when transferring files to and from Zoomer devices or other devices that require remote file manipulation. In this case, the host machine runs a program which copies escape character sequences to the appropriate serial port, prompting PCCOM to act.

Using Tools: 9.2 pccom: Running PCCOM on the Target

PCCOM is a DOS program that monitors the serial port and responds to commands received on the line. All I/O is interrupt driven with XON/XOFF flow control active on the host machine and obeyed on the target machine.

PCCOM uses the PTTY environment variable of DOS, if it exists. This variable contains communications settings detailing baud rate, COM port, and communications interrupt level. You can override the PTTY settings with command-line options to PCCOM when running it. The following command-line options are allowed:

/b:baud
Specify the baud used for file transfer and serial communications. The baud parameter may be one of the following values: 300, 1200, 2400, 9600, 19200, or 38400. Unambiguous abbreviations may be used (e.g. 9 for 9600 baud or 38 for 38400 baud). The default baud rate is 19200 bps.
/c:port Specify the COM port used for serial communications. The parameter may be one of 1, 2, 3, or 4. The COM ports for Zoomer are
COM 1 the built-in serial port (this is the default).
COM 2 the infrared transceiver.
/i:interrupt
Specify interrupts that should be ignored by PCCOM. This is useful if peripherals share an interrupt number that may confuse PCCOM. The interrupt parameter is one or more numbers of the interrupt(s) to be ignored, in hexadecimal.
/I:irq
Specify the IRQ level for serial line communications. This parameter is rarely required. The irq parameter is the number of the IRQ level to be used.

Quitting PCCOM

PCCOM may be quit either directly or remotely. To quit PCCOM directly, simply hit the Enter key (or the q key) on the machine on which PCCOM is running. If it does not quit on the first keystroke, hit the key again.

To quit PCCOM remotely, issue the quit escape sequence <Esc>EX through the serial line from the host machine. See below for a description of the commands that can be issued remotely.

Remote PCCOM Commands

PCCOM doesn't care what machine originates a remote command; its sole purpose is to evaluate and execute commands received through the serial port it's monitoring. Thus, a command sent by one Zoomer to another will exact the same response as a command sent by a development host machine to a target development machine.

On some computers (depending on BIOS), commands are copied from DOS to the serial port using the "echo" command. (If the computer executing remote commands has a different BIOS, you may get an error like "write fault error writing to device com#"; in this case, you must make sure that the characters sent to the serial port in the end are the same as those shown in the table below.) For example, to send the "quit" command to the remote machine, you could use the DOS command

C:>echo EscEX > com1

where Esc (in italics) represents the Escape character (0x1B).

No matter what method you use to send the character sequences to the serial port, the following commands may be executed remotely. Sending and receiving files remotely is more involved and is therefore discussed in the next section; it is not complicated, however.

All arguments to PCCOM remote commands must end with a delimiter character--an exlamation mark by default. Because of this, file operation commands will not work on files with exclamation points in their names by default. However, there is a PCCom command to use a different delimiter character.

Command | Sequence | Description
Send File | <Esc>XF<Ctrl-A> (<Esc>XF<0x01>) | Send a file from the host to the remote machine using the PCCOM file transfer protocol (see below).
Get File | <Esc>XF<Ctrl-D> (<Esc>XF<0x04>) | Retrieve a file from the remote machine using the PCCOM file transfer protocol (see below).
Turn EchoBack On | <Esc>EBon! | Turn EchoBack on.All text displayed on the target machine will also display on the host machine. Carriage returns are marked by semicolon (";") characters.
Turn EchoBack Off | <Esc>EBoff! | Turn EchoBack off.
Display EchoBack State | <Esc>EB! | Display a string signalling whether EchoBack is turned on. If it's turned on, "Echoback = off" appears on the target machine. If it's turned on, "Echoback = on" appears.
Turn Acknowledge On | <Esc>AKon! | Whenever a transaction is complete, the host will display "Ack Received." or "Nak Recieved." as appropriate.
Turn Acknowledge Off | <Esc>AKoff! | Turn off acknowledgement.
Display Acknowledge State | <Esc>AK! | Display a string signalling whether Acknowledge is turned on. If it's turned on, "ACK/NAK = on" appears on the target machine; otherwise "ACK/NAK = off" appears.
Send Ack Signal | <Esc>ACK | Sends an Ack signal, just as is sent by the target machine when acknowledgements are turned on.
Send NAK Signal | <Esc>NAK | Sends a NAK signal, just as is sent by the target machine when acknowledgements are turned on.
Copy File | <Esc>CPsrc!dest! | Copy the file named in the src argument to the file named in the dest argument. File name arguments may be full or relative paths with or without drive letters. This is equivalent to the DOS COPY command.
Move File | <Esc>MVsrc!dest! | Move the file named in the src argument to the file named in the dest argument. File name arguments may be full or relative paths; no drive letters are allowed. This is equivalent to the DOS MOVE command.
Delete File | <Esc>RFfile! | Remove the named file; the file argument may be a full path or a file in the current directory. This is equivalent to the DOS DEL command.
Change Drive | <Esc>CDdrive:! | Change the working volume to the drive named in the drive argument. This is equivalent to changing the drive in DOS by typing the drive letter followed by a colon (e.g. C:).
Change Directory | <Esc>CDdir! | Change the working directory to that named. The dir argument may be a full or relative path; no drive letter is allowed. This is the equivalent of the DOS CD command.
Show Current Path | <Esc>CD! | Print the current directory's path. This is equivalent to the DOS CD command with no arguments passed.
List Files in Dir | <Esc>LS | List files in the current working directory. This is equivalent to the DOS DIR command with no arguments.
List Files with Details | <Esc>LM | List files in the current working directory, together with the file's DOS attributes, the long name of each GEOS file, and the release number associated with each GEOS file. The DOS attributes are shown with the following flags: a: archive d: sub directory v: volume file s: system h: hidden r: read only
List Files, Full Details | <Esc>LL | List files in the current directory with all information returned by LM, described above, and the token of the creating geode and the document token. Tokens consist of four characters and a ManufacturerID --most ManufacturerID values will appear as a number, but PCCom will translate those values that it knows about into a mnemonic code: GEO: Geoworks APP: Application Local PLM: Palm Computing WIZ: SchoolView CLB: Creative Labs DSL: DOS Launcher AOL: America Online ITU: Intuit SDK: Default SDK AGD: Association of GEOS Developers
Display Free Space | <Esc>FSd:! | Display the free space on the drive named by the d argument.
 | <Esc>FS! | Display the free space on the current drive.
Display Drives | <Esc>AD | Display available drives. The target machine will display one line for each drive, showing the drive's letter and DOS name.
Create Directory | <Esc>MDdir! | Create a new directory according to the dir argument. The dir argument may be a full or relative path. This is the equivalent of the DOS MKDIR command.
Delete Directory | <Esc>RDdir! | Remove the directory named in the dir argument. The dir argument may be a full or relative path; this is equivalent to the DOS RMDIR and RD commands.
Clear Screen | <Esc>cl | Clear the screen of the target machine. This is equivalent to the DOS CLS command.
Display Delimiter | <Esc>DD | Displays the current delimiter character (`!' by default).
Change Delimiter | <Esc>DX <char><old delimiter> | Use a different character as the delimiter. By default, the exclamation mark (`!') is the delimiter character. The character delimiter must lie between the ascii values of 0x21 (exclamation mark) and 0x7e (tilde).
Exit PCCOM | <Esc>EX | Exit PCCOM on the remote machine.

Sending and Receiving Files

If you are using the GEOS SDK, you will do most of your file sending and receiving using the programs PCS, PCSEND, and PCGET. These programs send commands to the serial port, and then follow them by either providing or receiving packaged file data. These three programs are detailed below; following them is a section of the file transfer protocol of PCCOM if you are writing your own remote-access program(s).

PCGET

If PCCOM is running on the target machine, the PCGET program can be executed on the host to retrieve a file from the target. This simple program merely retrieves the file and copies it into the host's working directory under the same name.

PCGET takes the following arguments; only the file name is required. The other arguments are optional and may be used to override the settings in the host machine's PTTY environment variable (see above, under "Running PCCOM on the Target (or on the Zoomer)").

pcget [/b:baud][/c:port][/I:irq] file
/b:baud
Specify the baud used for file transfer. The baud parameter may be one of the following values: 300, 1200, 2400, 9600, 19200, or 38400. Unambiguous abbreviations may be used (e.g. 9 for 9600 baud or 38 for 38400 baud). The default baud rate is 19200 bps.
/c:port Specify the COM port used. The port parameter may be one of 1, 2, 3, or 4. The COM ports for Zoomer are
COM 1 the built-in serial port (this is the default),
COM 2 the infrared transceiver.
/I:irq
Specify the IRQ level for the transfer. This parameter is rarely required. The irq parameter is the number of the IRQ level to be used.
file
Specify the file to be retrieved. The file parameter may be a full or a relative path or a simple file name. The file will be copied from the target to the host into the host's current working directory, with the same name.
PCSEND

If PCCOM is running on the target (remote) machine, PCSEND may be executed on the host machine to download a file to the target. PCSEND will only send a single file, though it may send the file to any directory on the target. To send multiple files, or to download specific geodes to their proper locations in the GEOS 2.0 directory tree, use the PCS program instead.

The command line options of PCSEND are shown below. Only the file to be sent is required; if no other argument is passed, the file will be sent to the target's current working directory.

pcsend [/b:baud][/c:port][/I:irq] file [/d:dest]
/b:baud
Specify the baud used for file transfer. The baud parameter may be one of the following values: 300, 1200, 2400, 9600, 19200, or 38400. Unambiguous abbreviations may be used (e.g. 9 for 9600 baud or 38 for 38400 baud). The default baud rate is 19200 bps.
/c:port Specify the COM port used. The port parameter may be one of 1, 2, 3, or 4. The COM ports for Zoomer are
COM 1 the built-in serial port (this is the default),
COM 2 the infrared transceiver.
/I:irq
Specify the IRQ level for the transfer. This parameter is rarely required. The irq parameter is the number of the IRQ level to be used.
file
Specify the file to be sent. The file parameter may be a full or a relative path or a simple file name. The file will be downloaded to the target machine's current working directory, unless the /d parameter is also passed (see below).
/d:dest
Specify a full or relative destination path for the file.
PCS

If PCCOM is running on the target machine, PCS may be executed on the host machine to send multiple files to predetermined directories on the target. PCS is most often used by GEOS developers using the GEOS development kit, when they are downloading their recently-compiled geodes to the target for debugging.

The PCS program makes use of a list of constraints-tokens and their source and destination files and directories-located in the files ROOT_DIR\INCLUDE\PCS.PAT and ROOT_DIR\INCLUDE\SEND on the SDK host machine. (ROOT_DIR is a DOS environment variable set up by the SDK installation program indicating the top directory into which the SDK files were installed.) The format of these two files is described at the end of this section.

The command-line parameters of PCS are shown below. Note that a file name is not used by PCS; instead, if no token or directory is given, PCS will download all appropriate files in the current working directory. As with PCSEND and PCGET, the baud, COM port, and IRQ level arguments are all optional and may be used to override the settings in the PTTY environment variable.

pcs [/n][/Sf][/t][/b:b][/c:p][/I:i][dir|file|token]
/n
If /n is specified, PCS will send non-EC geodes only. Without this argument, PCS will send only EC geodes.
/Sf
Specify a file containing a list of files to be sent. The file argument is the name of the file.
/t
If the /t argument is used anywhere on the command line, file names specified at the end of the command (see the last argument) will be interpreted as tokens. A token may equate to numerous files as defined in the SEND file.
/b:b
Specify the baud used for file transfer. The baud parameter may be one of the following values: 300, 1200, 2400, 9600, 19200, or 38400. Unambiguous abbreviations may be used (e.g. 9 for 9600 baud or 38 for 38400 baud). The default baud rate is 19200 bps.
/c:p Specify the COM port used. The port parameter may be one of 1, 2, 3, or 4. The COM ports for Zoomer are
COM 1 the built-in serial port (this is the default),
COM 2 the infrared transceiver.
/I:i
Specify the IRQ level for the transfer. This parameter is rarely required. The irq parameter is the number of the IRQ level to be used.
dir|file|token
Specify a directory containing the geodes to be downloaded, the files to be downloaded, or tokens to be interpreted. If no directory, file, or token is specified, PCS will download the appropriate files in the current working directory. If a directory is specified, PCS will download all the appropriate files in that directory. If file names are specified (multiple files and/or directories may be specified), all affected files will be send.

If the /t argument appears anywhere in the command line (see above), this set of arguments will be interpreted as tokens. See directly below for token use and interpretation.

When using a token, PCS looks in the ROOT_DIR\INCLUDE\SEND file for the token to determine which files should be sent. Generally, all executables associated with an application, library, or mechanism are sent when the appropriate token is passed. Look in the SEND file to find out what the accepted tokens are and what they send.

For example, if the SEND file contained the lines

PC	DRIVER/VIDEO/DUMB/HGC/HGC					GEO
PCB	DRIVER/VIDEO/DUMB/HGC/HGC					GEO
PCB	DRIVER/MOUSE/LOGIBUS/LOGIBUS					GEO

then typing

pcs /t pc

would send just the HGCEC.GEO file to the proper directory on the target. Typing

pcs /t pcb

would download both HGCEC.GEO and LOGIBUSE.GEO to their proper directories. A listing of all the supported tokens can be found in the SEND file.

Using Tools: 9.3 pccom: File Transfer Protocol of PCCOM

If you need to create your own file transfer program or module, you can use the basic PCCOM commands and a special transfer protocol to send or receive files over the serial link. This is useful, for example, if you have an existing Windows or DOS program to which you would like the to add the ability to transfer files to or from the Zoomer (or another unit running PCCOM).

Sending a File to the Remote Machine

Sending a file to the remote machine involves the steps below. A file may be sent by any program that can access the serial port.

  1. Notify PCCOM that a file is on its way.
    Send the Send File escape character sequence to the serial port, notifying PCCOM that a file is about to be sent to it. The escape sequence is <Esc>XF<Ctrl-A> (<Esc>XF<0x01>).
  2. Send the destination file name.
    Send the name PCCOM should use for the file when saving it. The name is a string of sequential characters ending with a null byte. If sending the file to the target machine's target directory, this will just be the file's name; if sending to a different directory, this string should instead be the full pathname. Thus, to do the equivalent of "pcsend yuyuhack.sho /d:b:\geoworks\document", this string would consist of "b:\geoworks\document\yuyuhack.sho".
    If a timeout occurs while sending the filename, a NAK_QUIT will be returned and the remote machine will abort the file-send operation.
  3. Wait for acknowledgment of the name transfer.
    PCCOM will send a 0xFF character or a SYNC byte to acknowledge acceptance of the file name. If you do not receive this character, an error has likely occurred.
  4. Send the file size.
    The file size should be encoded as a dword value. Send the low byte first.
  5. Send a packet.
    Once you have received the SYNC or 0xFF byte, you can safely begin sending packets of data to PCCOM. A packet has the following format (sequence of bytes, with the first listed being the first sent):
    6. BLOCK_START				( = 1 )
data
BLOCK_END				( = 2 )
CRC				( checksum value )

    The data between BLOCK_START and the checksum value (CRC) may be up to 1 K. In order to avoid PCCOM confusion between a normal data byte and a BLOCK_START or BLOCK_END, a third element--BLOCK_QUOTE--is used.

    Any time you have a data byte equal to 1, 2, or 3, you must quote it by inserting a BLOCK_QUOTE byte before it and then adding three to its value. Thus, if you had a data sequence consisting of the following

    100, 42, 2, 3, 16

    you would send the following sequence of bytes to transfer the data:

    <Esc>FX1					( alert PCCOM a
                      file is coming )
<null-terminated file name>
<wait for SYNC byte>
BLOCK_START					( = 1 )
100
42
BLOCK_QUOTE					( = 3 )
5					( = 2 + 3 )
BLOCK_QUOTE					( = 3 )
6					( = 3 + 3 )
16
BLOCK_END					( = 2 )
CRC					( checksum value )

    The CRC word is two bytes of checksum value as calculated using the table and code shown in "Calculating Checksum Values," below. The CRC value is based upon the data bytes only. The low byte is transmitted first. You should use this code to ensure that your checksums will match PCCOM's.

  6. Optionally, the first packet may contain the filename and file size.
    In the first (and only the first) packet, it is possible to send a data block containing the file's name. If such a block is received by pccom or the PCCom library, then this filename will take precedence over the filename that was sent before the packets. Since the first filename may have been corrupted by noise, this provides a surer backup. The file size is passed as a dword value, low byte first.

    7. This data block should have the following data:

    "!PCCom File Transfer Filename Block! "  <no NULL>
<null-terminated file name> <null> <dword file size>

    The CRC for this block should be one higher than it would normally be--this signals that this block is of this special format.

  7. Wait for a SYNC byte acknowledgment.
    After each packet, you must make sure PCCOM responds positively with another SYNC byte. If the SYNC is not received, the packet likely failed. If the packet succeeded, continue sending packets as above (waiting for a SYNC after each) until done transferring the data.

    8. If, instead of receiving a SYNC value, you receive a NAK_QUIT value, the target machine has aborted to an unrecoverable error, and there is little point in continuing to send data over the serial line.

    A NAK byte signals that the target machine had an error but that it may be possible to continue the send operation by re-sending the block.

    When sending an optional filename packet, as described in step six, normally one does not re-send the packet it if it fails to send.

  8. Repeat steps five and seven as many times as necessary to transfer the entire file.
  9. Send two zero bytes.
    To make it absolutely clear that the file transfer has finished, send two zero bytes.

Retrieving a File Remotely

Retrieving a file from a machine running PCCOM is straightforward and uses the same file transfer protocol shown above for sending a file. The sequence of commands is different, however, and is listed below.

  1. Notify PCCOM that you're getting a file.
    Send the Get File escape character sequence to the serial port, notifying PCCOM that it should get ready to send a file. The escape sequence is <Esc>XF<Ctrl-D> (<Esc>XF<0x04>).
  2. Send the source file name.
    Send the name of the file to be retrieved. The name is a string of sequential characters ending with a null byte.
  3. Wait for SYNC byte signifying acknowledgment of the name transfer.
    PCCOM will send a 0xFF character or a SYNC byte to acknowledge acceptance of the file name. If you do not receive this character, an error has likely occurred (e.g., the file name wasn't successfully transmitted).
  4. Wait for another SYNC byte signifying file found.
    PCCOM will send a SYNC byte to acknowledge that the desired file was found.
  5. Wait for RECV_COMMAND byte.
    PCCOM will send a RECV_COMMAND byte signifying the file was successfully opened.
  6. Receive destination file name.
    This null-terminated string contains the place to write the file on the host machine. This path string may be up to size PATH_BUFFER_SIZE (defined in file.h).
  7. Send a SYNC byte to acknowledge receipt of the destination file name.
    By sending a SYNC byte, the host acknowledges that it has received the destination file name and is ready to continue the transfer.
  8. Receive the file size.
    The next data received will be the file's size, expressed as a dword, with the low byte sent first.
  9. Receive packets.
    After you receive thefile size from PCCOM, you will begin receiving data packets. Each packet you receive will follow the same format as described above for sending a file. Be aware of the BLOCK_QUOTE requirements when receiving the data (described in sending a file).

    10. You will receive a checksum word at the end of each block's data. If the sent checksum matches the one you calculate from the received data, send a SYNC byte to the serial port (0xFF); otherwise send a NAK value. Calculate all your checksum values with the table and code presented in "Calculating Checksum Values," below.

    When retrieving files, there will never be a file name packet such as described in step six of the file-sending procedure. Thus, you need not check to see whether a block whose CRC is off by one in fact contains a file name; you should not acknowledge this block.

  10. Receive two zero bytes.
    These make it clear that the file transfer has been completed.
  11. Send an ACK byte.
    This acknowledges the end of the file transfer.
  12. Repeat steps 4 through 11 until END_COMMAND byte received.
    If the filename passed to pcget contained wildcard characters and there is more than one matching file, PCCom will send all of them, using the protocol outlined in steps 4 through 11. If in step 4 an END_COMMAND byte is received instead of a SYNC byte, all files have been sent.

Calculating Checksum Values

The CRC word that accompanies each block of transferred data must be calculated using the same code as PCCOM or you will probably have only unsuccessful transmissions. The code used by PCCOM is shown on the next page, and you may include it in your own file transfer program. PCSEND, PCGET, and PCS also use the same checksum calculation code. This checksum should be based only on the data itself; do not include the BLOCK_START, BLOCK_QUOTE, or BLOCK_END characters in your calculations.

Code Display 9-1 PCCOM Checksums

/**********************************************************************
 *		CalcCRC						*
 **********************************************************************
 * SUMMARY:		Calculate the CRC on a block of data.
 * PASS:		char *buf		Pointer to the data buffer
 *		short size		Size of the data buffer
 *		short checksum		Previous checksum (0 at first)
 * RETURN:		CRC value (2 bytes)
 **********************************************************************/
short	crcTable[] = {
    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
    0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
    0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
    0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
    0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
    0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
    0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
    0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
    0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
    0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
    0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
    0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
    0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
    0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
    0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
    0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
    0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
    0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
    0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
    0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
    0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
    0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
    0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
    0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
    0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
    0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
    0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
    0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
    0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
    0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
    0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
    0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
unsigned short		IncCRC(unsigned short crc, char c){
    return ((crc << 8) ^ crcTable[((crc >> 8) ^ c) & 0xff]);
}
short		CalcCRC(char *buf, short size, short checksum){
/* The CRC is for the data part of the packet only.
 * The CRC value is passed low byte first. */
    for (;size > 0; size--){
     checksum = IncCRC(checksum, *buf++);
    }
    return checksum;
}

Testing the Connection

If the other side of a PCCom connection exits, you will get no notification. One way to test the connection to make sure that's still open is to send a <Esc>AKon! command. This requests that the other side of the connection send an <Esc>ACK at completion of each command. Note that the other side will try to acknowledge of the <Esc>AKon!, so that gives you a quick indicator that the other side is active. Assuming you don't want to stay in this mode, send an <Esc>AKoff!.

Using Tools: 10 pcget

Once you have pccom running on the target machine, you can invoke pcget on the development machine to yank a file from the target machine to the development machine.

Normally you would just type

pcget goemon.gif

to retrieve a file from the target machine's current directory or

pcget ..\clipart\jigen.gif

to retrieve it from another directory. To use different speed settings than the pccom tool would normally use (the pccom tool's settings are normally determined by the PTTY environment variable), you may pass /b, /c, and /I flags, as you did when invoking the pccom tool. The /b flag sets the baud rate (e.g. "/b:19400"); the /c: flag sets the com port (e.g. "/c:3"), and the /I flag sets the IRQ level (e.g. "/I:3"). Note that these flags may be indicated with "-" instead of "/").

Using Tools: 11 pcs

Once you have pccom running on the target machine, invoke pcs on the development machine to send your geode's executable to the proper place on the target machine. The pcs tool figures out which files to send (and which directory to send them to) via a number of rules set up in the ROOT_DIR\INCLUDE\PCS.PAT file. Thus it knows to send applications (files that end in .GEO) to the target machine's WORLD directory. (Note that the "/" and "-" characters in the flags below are interchangeable--hyphens are used with the n, S, and t flags by tradition.)

/I: IRQ
Communicate at a different interrupt level than pccom is presently using (this is a dangerous option).
-S file
Send all files mentioned in the file file.
/b: baudRate
Communicate at a different speed than pccom is presently using (this is a dangerous option).
/c: portNum
Communicate via a different COM port than pccom is presently using (this is a dangerous option).
-h
Get help.
-n
Send non-Error Checking version instead of EC.
-t token
Send all files associated with the named token (see below).

When using a token, pcs looks up the token in the INCLUDE\SEND file to determine which files should be sent. Generally all executables associated with an application, library, or mechanism are sent when the appropriate token is passed. Look in the SEND file to find out what the accepted tokens are and what they send. Suppose your send file consisted of the following lines:

PC		DRIVER/VIDEO/DUMB/HGC/HGC 					GEO
HGCAT 		DRIVER/VIDEO/DUMB/HGC/HGC 					GEO
PCB 		DRIVER/VIDEO/DUMB/HGC/HGC 					GEO
PCB 		DRIVER/MOUSE/LOGIBUS/LOGIBUS 					GEO
PCS 		DRIVER/VIDEO/DUMB/HGC/HGC 					GEO
PCS 		DRIVER/MOUSE/LOGISER/LOGISER 					GEO

Typing

pcs -t pc

would send the file DRIVER\VIDEO\DUMB\HGC\HGCEC.GEO (HGC.GEO if sending non-EC). Typing "pcs -S pcb" would send that file, and also DRIVER\MOUSE\LOGIBUS\LOGIBUSE.GEO (or LOGIBUS.GEO).

Using Tools: 12 pcsend

The pcsend tool sends files from the development machine to the target machine (assuming that the target machine is running pccom). Normally you use pcs to send geodes between machines, as that tool has knowledge of where geodes belong; pcsend is for those cases where the pcs tool's automatic behavior is undesirable. With pcsend, you can specify source and destination explicitly.

Normally you will invoke pcsend by typing something like:

pcsend zenigata.pcx

or (to send to a directory other than the target PC's current directory):

pcsend zenigata.pcx /d:..\clipart

In addition to the /d option, you may also pass /b, /c, or /I options to override the speed, port, and/or IRQ values in the PTTY environment variable. (You may use "-" instead of "/" when passing these flags.)

pcsend sendslow.com /b:2400

Using Tools: 13 pmake

The pmake program is a make utility. This means that it takes a directory of sources and a makefile which contains knowledge of how to turn these sources into geodes. If there were no pmake , then you would have to type goc, bcc, and glue each time.

The pmake program will work correctly if you have set up your files correctly. This means that you must have a makefile. You should run mkmf to generate a makefile that knows how to generate geodes. If you are an experienced C programmer, you may have come up with some customizations that you use with your make utility. We still suggest that you work from a standard makefile (which knows about Goc and Glue), but include your customizations in a local.mk file (see How to Customize pmake ).

When you have just added or removed source files from a geode, you will have to generate new dependency information, which pmake will use when doing other makes. You can use pmake to generate this information (it will make an appropriate call to the makedpnd program; it will store the result in depends.mk ):

 pmake depend

Once you have created your source and make files, all you need to do to invoke pmake is type

pmake

The pmake tool can be used to construct a specific target. Thus, if you need to generate an .OBJ file but do not need the rest of the geode, you may type something like:

pmake sdtrk.obj

The system makefiles have been set up with knowledge of some special targets. If you are making a library and wish to install it in the proper directory so that all applications may use it, then signal this to pmake :

 pmake lib

The pmake program does have command-line arguments, but these are used only rarely; they are detailed below.

Copyright Notice and Acknowledgment

How to Customize pmake

Command Line Arguments

Contents of a Makefile

Advanced pmake Techniques

The Way Things Work

Using Tools: 13.1 pmake: Copyright Notice and Acknowledgment

The pmake tool comes under the following copyright notice:

Copyright© 1988, 1989 by the Regents of the University of California
Copyright© 1988, 1989 by Adam de Boor
Copyright© 1989 by Berkeley Softworks

Permission to use, copy, modify, and distribute this software ( pmake ) and its documentation for any non-commercial purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies. The University of California, Berkeley Softworks, and Adam de Boor make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.

The pmake program for the PC uses the SPAWNO routines by Ralf Brown to minimize memory use while shelling to DOS and running other programs.

Using Tools: 13.2 pmake: How to Customize pmake

For most applications, executing mkmf will generate a perfect makefile. However, you may be creating an unusual geode or have some makefile definitions which you want to include. Fortunately, there are ways to customize your make environment without having to build a MAKEFILE from scratch.

Makefiles can #include other makefiles. If you have a file named LOCAL.MK in your make directory, then the standard makefile generated by mkmf will include it; if you wish to customize your makes, you can create a LOCAL.MK file and fill it with appropriate make directives.

Depending on how much customization you need to do, you may wish to read on to find out about makefile syntax. However, there are several simple things you can do without learning too much about makefiles.

# Pass extra flags to Goc:
 GOCFLAGS		+= flag1 flag2
# Pass extra flags to your C compiler:
 CCOMFLAGS		+= flag1 flag2
# Pass extra flags to the Esp assembler (if you have
# that tool):
 ASMFLAGS		+= flag1 flag2
# Pass extra flags to the Glue linker:
 LINKFLAGS 		+= flag1 flag2
# Look somewhere special for .GOC files
# (This pattern applies to any suffix):
.PATH.GOC 		: $(ROOT_DIR)\DIR1 \DIR2
# Specify geode name:
 GEODE 		= NAME
# Set NO_EC variable (which signals that we don't
# want to make an Error Checking version):
 NO_EC 		= 1
# If your preprocessor is not reachable via the
# Path environment variable:
 CPP 		= vol:\path\name
# If your C compiler is not reachable via the Path
# environment variable:
 CCOM 		= vol:\path\name
# Include some other make file
#include "OTHERMF.MK"
# Include the standard makefile directives
# This will include INCLUDE\GEODE.MK:
#include <$(SYSMAKEFILE)>
Sample local.mk Files
---FINGER\LOCAL.MK
# Local Makefile for FPaint
#  FPaint is stored in a directory called FINGER. This would normally confuse
#  pmake, which expects the geode name to be the same as the directory name.
#  Let us, therefore, alert pmake to the geode's real name:
GEODE		= FPAINT
# This was the only thing we wanted to change, so include standard definitions:
#include <$(SYSMAKEFILE)>
---PROMO\LOCAL.MK
# Local Makefile for Promo
#  Promo uses some clip art that isn't in or below its source directory, so we
#  tell pmake where to look for it:
.PATH.GOH 	: $(CSOURCE_PATHS) $(CINCLUDE_DIR) $(ROOT_DIR)\LOGOART
# This program contains no Error Checking code (See
# the GOC Programming chapter for information
# about EC code. So we tell pmake not to bother making an Error Checking
# version:
NO_EC = 1
# Include the standard system makefile:
#include <$(SYSMAKEFILE)>

Using Tools: 13.3 pmake: Command Line Arguments

The pmake program comes with a wide variety of flags to choose from. They must be passed in the following order: flags (if any), variable assignments (if any), target (if any).

pmake [flags] [variables] [target]

The flags are as follows:

-d info
This causes pmake to print out debugging information that may prove useful to you. The info parameter is a string of single characters that tell pmake what aspects you are interested in. Most of these options will make little sense to you unless you've dealt with Make before. Just remember where this table is and come back to it as you read on. The characters and the information they produce are as follows:
*
All debugging information.
c
Conditional evaluation.
d
The searching and caching of directories.
m
The making of each target: what target is being examined; when it was last modified; whether it is out-of-date; etc.
p
Makefile parsing.
r
Remote execution.
s
The application of suffix-transformation rules.
t
The maintenance of the list of targets.
v
Variable assignment.

Of these, the "m" and "s" flags will be most useful.

-f file
Specify a makefile to read different from the default (MAKEFILE). If file is "-", pmake uses the standard input. This is useful for making "quick and dirty" makefiles.
-h
Prints out a summary of the various flags pmake accepts. It can also be used to find out what level of concurrence was compiled into the version of pmake you are using (look at -J and -L) and various other information on how pmake is configured.
-i
If you give this flag, pmake will ignore non-zero status returned by any of its shells. It's like placing a "-" before all the commands in the makefile.
-k
This is similar to -i in that it allows pmake to continue when it sees an error, but unlike -i where pmake continues blithely as if nothing went wrong, -k causes it to recognize the error and only continue work on those things that don't depend on the target, either directly or indirectly (through depending on something that depends on it), whose creation returned the error. (The "k" is for "keep going".)
-n
This flag tells pmake not to execute the commands needed to update the out-of-date targets in the makefile. Rather, pmake will simply print the commands it would have executed and exit. This is particularly useful for checking the correctness of a makefile. If pmake doesn't do what you expect it to, it's a good chance the makefile is wrong.
-p number
You should never have to use this option; it is used for debugging pmake . This causes pmake to print its input in a reasonable form, though not necessarily one that would make immediate sense to anyone but a pmake expert. The number is a bitwise-or of 1 and 2 where 1 means it should print the input before doing any processing and 2 says it should print it after everything has been re-created. Thus "-p 3"would print it twice--once before processing and once after (you might find the difference between the two interesting).
-q
If you give pmake this flag, it will not try to re-create anything. It will just query to see if anything is out-of-date and exit non-zero if so.
-s
This silences pmake , preventing it from printing commands before they're executed. It is the equivalent of putting an "@" before every command in the makefile.
-t
Rather than try to re-create a target, pmake will simply "touch" it so as to make it appear up-to-date. If the target didn't exist before, it will when pmake finishes, but if the target did exist, it will appear to have been updated.
-D var
Allows you to define a variable to have 1 as its value. The variable is a global variable, not a command-line variable. This is useful mostly for people who are used to the C compiler arguments and those using conditionals, which are described in Conditional Statements , below.
-I directory
Tells pmake another place to search for included makefiles.
-W
Suppresses pmake 's warnings. Note that tools which pmake invokes (Goc, Glue, etc.) may still print out warnings of their own.

Using Tools: 13.4 pmake: Contents of a Makefile

The pmake program takes as input a file that tells

  • which files depend on which other files and
  • what to do about files that are "out-of-date."

This file is known as a "makefile" and is usually kept in the top-most directory of the system to be built. While you can call the makefile anything you want, pmake will look for MAKEFILE in the current directory if you don't tell it otherwise. To specify a different makefile, use the -f flag (e.g. "pmake -f program.mk").

A makefile has four different types of lines in it:

  • File dependency specifications
  • Creation commands
  • Variable assignments
  • Comments, include statements and conditional directives

Any line may be continued over multiple lines by ending it with a backslash ("\"). The backslash, following newline and any initial whitespace on the following line are compressed into a single space before the input line is examined by pmake.

Dependency Lines

In any system, there are dependencies between the files that make up the system. For instance, in a program made up of several C source files and one header file, the C files will need to be re-compiled should the header file be changed. For a document of several chapters and one macro file, the chapters will need to be reprocessed if any of the macros changes. These are dependencies and are specified by means of dependency lines in the makefile.

On a dependency line, there are targets and sources , separated by a one- or two-character operator. The targets "depend" on the sources and are usually created from them. Any number of targets and sources may be specified on a dependency line. All the targets in the line are made to depend on all the sources. Targets and sources need not be actual files, but every source must be either an actual file or another target in the makefile. If you run out of room, use a backslash at the end of the line to continue onto the next one.

Any file may be a target and any file may be a source, but the relationship between the two (or however many) is determined by the "operator" that separates them. Three types of operators exist: one specifies that the datedness of a target is determined by the state of its sources, while another specifies other files (the sources) that need to be dealt with before the target can be re-created. The third operator is very similar to the first, with the additional condition that the target is out-of-date if it has no sources. These operations are represented by the colon, the exclamation point and the double-colon, respectively, and are mutually exclusive (to represent a colon in a target, you must precede it with a backslash: "\:"). Their exact semantics are as follows:

:
If a colon is used, a target on the line is considered to be "out-of-date" (and in need of creation) if
  • the target doesn't exist or
  • any of the sources has been modified more recently than the target.

Under this operator, steps will be taken to re-create the target only if it is found to be out-of-date by using these two rules.

!
If an exclamation point is used, the target will always be re-created, but this will not happen until all of its sources have been examined and re-created, if necessary.
::
If a double-colon is used, a target is out-of-date if
  • the target has no sources,
  • any of the sources has been modified more recently than the target, or
  • the target doesn't exist.

If the target is out-of-date according to these rules, it will be re-created. This operator also does something else to the targets, as described in Shell Commands).

Suppose there are three C files ( a.c , b.c and c.c ) each of which includes the file defs.h . The dependencies between the files could then be expressed as follows:

PROGRAM.EXE 			: A.OBJ B.OBJ C.OBJ
A.OBJ B.OBJ C.OBJ 			: DEFS.H
A.OBJ 			: A.C
B.OBJ 			: B.C
C.OBJ 			: C.C

You may be wondering at this point, where A.OBJ, B.OBJ and C.OBJ came in and why they depend on defs.h and the C files don't. The reason is quite simple: PROGRAM.EXE cannot be made by linking together .c files--it must be made from .obj files. Likewise, if you change DEFS.H, it isn't the .c files that need to be re-created, it's the .obj files. If you think of dependencies in these terms--which files (targets) need to be created from which files (sources)-- you should have no problems.

An important thing to notice about the above example is that all the .obj files appear as targets on more than one line. This is perfectly all right: the target is made to depend on all the sources mentioned on all the dependency lines. For example, A.OBJ depends on both DEFS.H and A.C.

The order of the dependency lines in the makefile is important: the first target on the first dependency line in the makefile will be the one that gets made if you don't say otherwise. That's why PROGRAM.EXE comes first in the example makefile, above.

Both targets and sources may contain the standard C-Shell wildcard characters ({, }, *, ?, [, and ]), but the square braces may only appear in the final component (the file portion) of the target or source. The characters mean the following things:

{ }
These enclose a comma-separated list of options and cause the pattern to be expanded once for each element of the list. Each expansion contains a different element. For example,
SRC\{WHIFFLE,BEEP,FISH}.C

expands to the three words "SRC\WHIFFLE.C", "SRC\BEEP.C", and "SRC\FISH.C". These braces may be nested and, unlike the other wildcard characters, the resulting words need not be actual files. All other wildcard characters are expanded using the files that exist when pmake is started.

*
This matches zero or more characters of any sort.
SRC\*.C

will expand to the same three words as above as long as src contains those three files (and no other files that end in .c).

?
Matches any single character.
[ ]
This is known as a character class and contains either a list of single characters, or a series of character ranges ([a-z], for example means all characters between a and z), or both. It matches any single character contained in the list. E.g. [A-Za-z] will match all letters, while [0123456789] will match all numbers.

Shell Commands

At this point, you may be wondering how files are re-created. The re-creation is accomplished by commands you place in the makefile. These commands are passed to the shell to be executed and are expected to do what's necessary to update the target file. (The pmake program doesn't actually check to see if the target was created. It just assumes it's there.)

Shell commands in a makefile look a lot like shell commands you would type, with one important exception: each command in a makefile must be preceded by at least one tab.

Each target has associated with it a shell script made up of one or more of these shell commands. The creation script for a target should immediately follow the dependency line for that target. While any given target may appear on more than one dependency line, only one of these dependency lines may be followed by a creation script, unless the "::" operator was used on the dependency line.

If the double-colon was used, each dependency line for the target may be followed by a shell script. That script will only be executed if the target on the associated dependency line is out-of-date with respect to the sources on that line, according to the rules given earlier.

To expand on the earlier makefile, you might add commands as follows:

PROGRAM.EXE : A.OBJ B.OBJ C.OBJ
    BCC A.OBJ B.OBJ C.OBJ -o PROGRAM.EXE
A.OBJ B.OBJ C.OBJ : DEFS.H
A.OBJ : A.C
    bcc -c A.C
B.OBJ : B.C
    bcc -c B.C
C.OBJ : C.C
    bcc -c C.C

Something you should remember when writing a makefile is that the commands will be executed if the target on the dependency line is out-of-date, not the sources. In this example, the command "bcc -c a.c" will be executed if a.obj is out-of-date. Because of the ":" operator, this means that should a.c or defs.h have been modified more recently than a.obj , the command will be executed ( a.obj will be considered out-of-date).

There is another way in which makefile commands differ from regular shell commands. The first two characters after the initial whitespace are treated specially. If they are any combination of " @" and " -", they cause pmake to do things differently.

In most cases, shell commands are printed before they're actually executed. This is to keep you informed of what's going on. If an "@" appears, however, this echoing is suppressed. In the case of an echo command, perhaps "echo Linking index" it would be rather messy to output

echo Linking index
Linking index

The other special character is the dash ("-"). Shell commands finish with a certain "exit status." This status is made available by the operating system to whatever program invoked the command. Normally this status will be zero if everything went ok and non-zero if something went wrong. For this reason, pmake will consider an error to have occurred if one of the shells it invokes returns a non-zero status. When it detects an error, pmake 's usual action is to abort whatever it's doing and exit with a non-zero status itself. This behavior can be altered, however, by placing a "-" at the front of a command (e.g. "-copy index index.old") . In such a case, the non-zero status is simply ignored and pmake keeps going.

If the system call should be made through the DOS COMMAND.COM, precede the shell command with a backquote (`).

Variables

The pmake program has the ability to save text in variables to be recalled later at your convenience. Variables in pmake are used much like variables in the shell and, by tradition, consist of all upper-case letters. Variables are assigned using lines of the form

VARIABLE  = value

append using lines of the form

VARIABLE  += value

conditionally assigned (if the variable isn't already defined) by using lines of the form

VARIABLE  ?= value

and assigned with expansion (i.e. the value is expanded (see below) before being assigned to the variable--useful for placing a value at the beginning of a variable, or other things) by using lines of the form

VARIABLE  := value

Any whitespace before value is stripped off. When appending, a space is placed between the old value and the values being appended.

The final way a variable may be assigned is using lines of the form

VARIABLE  != shell-command

or, if the shell command requires the use of the command.com interpreter,

VARIABLE !=  `shell-command

In this case, shell-command has all its variables expanded (see below) and is passed off to a shell to execute. The output of the shell is then placed in the variable. Any newlines (other than the final one) are replaced by spaces before the assignment is made. This is typically used to find the current directory via a line like:

CURRENT_DIR != `cd

The value of a variable may be retrieved by enclosing the variable name in parentheses or curly braces and preceding the whole thing with a dollar sign. For example, to set the variable CFLAGS to the string "-I\NIHON\LIB\LIBC -O", you would place a line

CFLAGS = -I\NIHON\LIB\LIBC -O

in the makefile and use the expression

$(CFLAGS)

wherever you would like the string "-I\NIHON\LIB\LIBC -O" to appear. This is called variable expansion.

There are two different times at which variable expansion occurs: When parsing a dependency line, the expansion occurs immediately upon reading the line. Variables in shell commands are expanded when the command is executed. Variables used inside another variable are expanded whenever the outer variable is expanded (the expansion of an inner variable has no effect on the outer variable. That is, if the outer variable is used on a dependency line and in a shell command, and the inner variable changes value between when the dependency line is read and the shell command is executed, two different values will be substituted for the outer variable).

Variables come in four flavors, though they are all expanded the same and all look about the same. They are (in order of expanding scope)

  • local variables.
  • command-line variables.
  • global variables.
  • environment variables.

The classification of variables doesn't matter much, except that the classes are searched from the top (local) to the bottom (environment) when looking up a variable. The first one found wins.

Local Variables

Each target can have as many as seven local variables. These are variables that are only "visible" within that target's shell script and contain such things as the target's name, all of its sources (from all its dependency lines), those sources that were out-of-date, etc. Four local variables are defined for all targets. They are

.TARGET
The name of the target.
.OODATE
The list of the sources for the target that were considered out-of-date. The order in the list is not guaranteed to be the same as the order in which the dependencies were given.
.ALLSRC
The list of all sources for this target in the order in which they were given.
.PREFIX
The target without its suffix and without any leading path. For example, for the target ..\..\LIB\FSREAD.C , this variable would contain FSREAD.

One other local variable, .IMPSRC, is set only for certain targets under special circumstances. It is discussed below.

Two of these variables may be used in sources as well as in shell scripts. These are .TARGET and .PREFIX. The variables in the sources are expanded once for each target on the dependency line, providing what is known as a "dynamic source," allowing you to specify several dependency lines at once. For example,

$(OBJS) : $(.PREFIX).c

will create a dependency between each object file and its corresponding C source file.

Command-line Variables

Command-line variables are set when pmake is first invoked by giving a variable assignment as one of the arguments. For example,

pmake "CFLAGS = -I\NIHON\LIB\LIBC -O"

would make CFLAGS be a command-line variable with the given value. Any assignments to CFLAGS in the makefile will have no effect, because once it is set, there is (almost) nothing you can do to change a command-line variable. Command-line variables may be set using any of the four assignment operators, though only = and ?= behave as you would expect them to, mostly because assignments to command-line variables are performed before the makefile is read, thus the values set in the makefile are unavailable at the time. += is the same as = because the old value of the variable is sought only in the scope in which the assignment is taking place. The := and ?= operators will work if the only variables used are in the environment.

Global Variables

Global variables are those set or appended in the makefile. There are two classes of global variables: those you set and those pmake sets. The ones you set can have any name you want them to have, except they may not contain a colon or an exclamation point. The variables pmake sets (almost) always begin with a period and contain only upper-case letters. The variables are as follows:

.PMAKE
The name by which pmake was invoked is stored in this variable. For compatibility, the name is also stored in the MAKE variable.
.MAKEFLAGS
All the relevant flags with which pmake was invoked. This does not include such things as "-f" or variable assignments. Again for compatibility, this value is stored in the MFLAGS variable as well.

Two other variables, .INCLUDES and .LIBS, are covered in the section on special targets (See Special Targets ).

Global variables may be deleted using lines of the form:

 #undef variable

The "#" must be the first character on the line. Note that this may only be done to global variables.

Environment Variables

Environment variables are passed by the shell that invoked pmake and are given by pmake to each shell it invokes. They are expanded like any other variable, but they cannot be altered in any way.

One special environment variable, PMAKE, is examined by pmake for command-line flags, variable assignments, etc. that it should always use. This variable is examined before the actual arguments to pmake are. In addition, all flags given to pmake , either through the PMAKE variable or on the command line, are placed in this environment variable and exported to each shell pmake executes. Thus recursive invocations of pmake automatically receive the same flags as the top-most one.

Many other standard environment variables are defined and described in the Include\GEOS.MK included Makefile.

Using all these variables, you can compress the sample makefile even more:

OBJS = A.OBJ B.OBJ C.OBJ
PROGRAM.EXE : $(OBJS)
    BCC $(.ALLSRC) -o $(.TARGET)
$(OBJS) : DEFS.H
A.OBJ : A.C
    BCC -c A.C
B.OBJ : B.C
    BCC -c B.C
C.OBJ : C.C
    BCC -c C.C

In addition to variables which pmake will use, you can set environment variables which shell commands may use using the pmake_set directive.

.C.EBJ :
pmake_set CL = $(CCOMFLAGS) /Fo$(.TARGET)
$(CCOM) $(.IMPSRC)

You might use the above sequence to set up an argument list in the CL environment variable if your compiler (invoked with CCOM) needed its arguments in such a variable and was unable to take arguments in a file.

Comments

Comments in a makefile start with a "#" character and extend to the end of the line. They may appear anywhere you want them, except where they might be misinterpreted as a shell command.

Transformation Rules

As you know, a file's name consists of two parts: a base name, which gives some hint as to the contents of the file, and a suffix, which usually indicates the format of the file. Over the years, as DOS has developed, naming conventions, with regard to suffixes, have also developed that have become almost incontrovertible. For example, a file ending in .C is assumed to contain C source code; one with a .OBJ suffix is assumed to be a compiled, relocatable object file that may be linked into any program. One of the best aspects of pmake comes from its understanding of how the suffix of a file pertains to its contents and their ability to do things with a file based solely on its suffix. This ability comes from something known as a transformation rule. A transformation rule specifies how to change a file with one suffix into a file with another suffix.

A transformation rule looks much like a dependency line, except the target is made of two known suffixes stuck together. Suffixes are made known to pmake by placing them as sources on a dependency line whose target is the special target .SUFFIXES. For example:

.SUFFIXES 			: .obj .c
.c.obj 			:
    $(CCOM) $(CFLAGS) -c $(.IMPSRC)

The creation script attached to the target is used to transform a file with the first suffix (in this case, .c) into a file with the second suffix (here, .obj). In addition, the target inherits whatever attributes have been applied to the transformation rule. The simple rule above says that to transform a C source file into an object file, you compile it using your C compiler with the -c flag.

This rule is taken straight from the system makefile. Many transformation rules (and suffixes) are defined there; you should look there for more examples (type "pmake -h" to find out where it is).

There are some things to note about the transformation rule given above:

  1. The .IMPSRC variable. This variable is set to the "implied source" (the file from which the target is being created; the one with the first suffix), which, in this case, is the .c file.
  2. The CFLAGS variable. Almost all of the transformation rules in the system makefile are set up using variables that you can alter in your makefile to tailor the rule to your needs. In this case, if you want all your C files to be compiled with the -g flag, to provide information for Swat or CodeView, you would set the CFLAGS variable to contain -g ("CFLAGS = -g") and pmake would take care of the rest.

To give you a quick example, the makefile could be changed to this:

OBJS = A.OBJ B.OBJ C.OBJ
PROGRAM .EXE			: $(OBJS)
    $(CCOM) -o $(.TARGET) $(.ALLSRC)
$(OBJS) 			: DEFS.H

The transformation rule given above takes the place of the 6 lines.

A.OBJ : A.C
    BCC -c A.C
B.OBJ : B.C
    BCC -c B.C
C.OBJ : C.C
    BCC -c C.C

Now you may be wondering about the dependency between the .obj and .c files--it's not mentioned anywhere in the new makefile. This is because it isn't needed: one of the effects of applying a transformation rule is the target comes to depend on the implied source (hence the name).

For a more detailed example, Suppose you have a makefile like this:

A.EXE 			: A.OBJ B.OBJ
    $(CCOM) $(.ALLSRC)

and a directory set up like this:

total 4

MAKEFILE 		  34	09-07-89 		12:43a
A        C		 119	10-03-89 		 7:39p
A        OBJ 		201	09-07-89 		12:43a
B        C		  69	09-07-89 		12:43a

While just typing " pmake " will do the right thing, it's much more informative to type " pmake -ds " This will show you what pmake is up to as it processes the files. In this case, pmake prints the following:

Suff_FindDeps (A.EXE)
    using existing source A.OBJ
    applying .OBJ -> .EXE to "A.OBJ"
Suff_FindDeps (A.OBJ)
    trying A.C...got it
    applying .C -> .OBJ to "A.C"
Suff_FindDeps (B.OBJ)
    trying B.C...got it
    applying .C -> .OBJ to "B.C"
Suff_FindDeps (A.C)
    trying A.Y...not there
    trying A.L...not there
    trying A.C,V...not there
    trying A.Y,V...not there
    trying A.L,V...not there
Suff_FindDeps (B.C)
    trying B.Y...not there
    trying B.L...not there
    trying B.C,V...not there
    trying B.Y,V...not there
    trying B.L,V...not there
--- A.OBJ ---
bcc -c A.C
--- B.OBJ ---
bcc -c B.C
--- A.EXE ---
bcc A.OBJ B.OBJ

Suff_FindDeps is the name of a function in pmake that is called to check for implied sources for a target using transformation rules. The transformations it tries are, naturally enough, limited to the ones that have been defined (a transformation may be defined multiple times, by the way, but only the most recent one will be used). You will notice, however, that there is a definite order to the suffixes that are tried. This order is set by the relative positions of the suffixes on the .SUFFIXES line--the earlier a suffix appears, the earlier it is checked as the source of a transformation. Once a suffix has been defined, the only way to change its position is to remove all the suffixes (by having a .SUFFIXES dependency line with no sources) and redefine them in the order you want. (Previously-defined transformation rules will be automatically redefined as the suffixes they involve are re-entered.)

Another way to affect the search order is to make the dependency explicit. In the above example, a.exe depends on a.obj and b.obj. Since a transformation exists from .obj to .exe, pmake uses that, as indicated by the "using existing source a.obj" message.

The search for a transformation starts from the suffix of the target and continues through all the defined transformations, in the order dictated by the suffix ranking, until an existing file with the same base (the target name minus the suffix and any leading directories) is found. At that point, one or more transformation rules will have been found to change the one existing file into the target.

For example, ignoring what's in the system makefile for now, say you have a makefile like this:

.SUFFIXES : .EXE .OBJ .C .Y .L
.L.C :
    LEX $(.IMPSRC)
    MOVE LEX.YY.C $(.TARGET)
.Y.C :
    YACC $(.IMPSRC)
    MOVE Y.TAB.C $(.TARGET)
.C.OBJ :
    BCC -L $(.IMPSRC)
.OBJ.EXE :
    BCC -o $(.TARGET) $(.IMPSRC)

and the single file jive.l. If you were to type pmake -rd ms jive.exe , you would get the following output for jive.exe:

Suff_FindDeps (JIVE.EXE)
trying JIVE.OBJ...not there
trying JIVE.C...not there
trying JIVE.Y...not there
trying JIVE.L...got it
applying .L -> .C to "JIVE.L"
applying .C -> .OBJ to "JIVE.C"
applying .OBJ -> .EXE to "JIVE.OBJ"

The pmake tool starts with the target jive.exe, figures out its suffix (.exe) and looks for things it can transform to a .exe file. In this case, it only finds .obj, so it looks for the file JIVE.OBJ.

It fails to find it, so it looks for transformations into a .obj file. Again it has only one choice: .c. So it looks for JIVE.C and fails to find it. At this point it can create the .c file from either a .y file or a .l file. Since .y came first on the .SUFFIXES line, it checks for jive.y first, but can't find it, so it looks for jive.l. At this point, it has defined a transformation path as follows: .l->.c-> .obj-> .exe and applies the transformation rules accordingly. For completeness, and to give you a better idea of what pmake actually did with this three-step transformation, this is what pmake printed for the rest of the process:

Suff_FindDeps (JIVE.OBJ)
using existing source JIVE.C
applying .C -> .OBJ to "JIVE.C"
Suff_FindDeps (JIVE.C)
using existing source JIVE.L
applying .L -> .C to "JIVE.L"
Suff_FindDeps (JIVE.L)
Examining JIVE.L...modified 17:16:01 Oct 4,
 1987...up-to-date
Examining JIVE.C...non-existent...out-of-date
--- JIVE.C ---
LEX JIVE.L
...meaningless lex output deleted...
MV LEX.YY.C JIVE.C
Examining JIVE.OBJ...non-existent...out-of-date
--- JIVE.OBJ ---
bcc -c JIVE.C
Examining JIVE.EXE...non-existent...out-of-date
--- JIVE.EXE ---
bcc -o JIVE.EXE JIVE.OBJ

Including Other Makefiles

Just as for programs, it is often useful to extract certain parts of a makefile into another file and just include it in other makefiles somehow. Many compilers allow you to use something like

#include "defs.h"

to include the contents of defs.h in the source file. The pmake program allows you to do the same thing for makefiles, with the added ability to use variables in the filenames. An include directive in a makefile looks either like this

#include <file>

or like this

#include "file"

The difference between the two is where pmake searches for the file: the first way, pmake will look for the file only in the system makefile directory (to find out what that directory is, give pmake the -h flag).

For files in double-quotes, the search is more complex; pmake will look in the following places in the given order:

  1. The directory of the makefile that's including the file.
  2. The current directory (the one in which you invoked pmake ).
  3. The directories given by you using -I flags, in the order in which you gave them.
  4. Directories given by .PATH dependency lines.
  5. The system makefile directory.

You are free to use pmake variables in the filename-- pmake will expand them before searching for the file. You must specify the searching method with either angle brackets or double-quotes outside of a variable expansion. That is, the following

SYSTEM= <command.mk>
#include $(SYSTEM)

won't work; instead use the following:

SYSTEM= command.mk
#include <$(SYSTEM)>

Saving Commands

There may come a time when you will want to save certain commands to be executed when everything else is done, by inserting an ellipsis "..." in the Makefile. Commands saved in this manner are only executed if pmake manages to re-create everything without an error.

Target Attributes

The pmake tool allows you to give attributes to targets by means of special sources. Like everything else pmake uses, these sources begin with a period and are made up of all upper-case letters. By placing one (or more) of these as a source on a dependency line, you are "marking" the target(s) with that attribute.

Any attributes given as sources for a transformation rule are applied to the target of the transformation rule when the rule is applied.

.DONTCARE
If a target is marked with this attribute and pmake can't figure out how to create it, it will ignore this fact and assume the file isn't really needed or actually exists and pmake just can't find it. This may prove wrong, but the error will be noted later on, not when pmake tries to create the target so marked. This attribute also prevents pmake from attempting to touch the target if given the "-t" flag.
.EXEC
This attribute causes its shell script to be executed while having no effect on targets that depend on it. This makes the target into a sort of subroutine. EXEC sources don't appear in the local variables of targets that depend on them (nor are they touched if pmake is given the -t flag).
.IGNORE
Giving a target the .IGNORE attribute causes pmake to ignore errors from any of the target's commands, as if they all had "-" before them.
.MAKE
The .MAKE attribute marks its target as being a recursive invocation of pmake . This forces pmake to execute the script associated with the target (if it's out-of-date) even if you gave the -n or -t flag. By doing this, you can start at the top of a system and type
pmake -n

and have it descend the directory tree (if your makefiles are set up correctly), printing what it would have executed if you hadn't included the -n flag.

.NOTMAIN
Normally, if you do not specify a target to make in any other way, pmake will take the first target on the first dependency line of a makefile as the target to create. That target is known as the "Main Target" and is labeled as such if you print the dependencies out using the -p flag. Giving a target, this attribute tells pmake that the target is definitely not the Main Target. This allows you to place targets in an included makefile and have pmake create something else by default.
.PRECIOUS
When pmake is interrupted (by someone typing control-C at the keyboard), it will attempt to clean up after itself by removing any half-made targets. If a target has the .PRECIOUS attribute, however, pmake will leave it alone. A side effect of the "::" operator is to mark the targets as .PRECIOUS.
.SILENT
Marking a target with this attribute keeps its commands from being printed when they're executed, just as if they had an "@" in front of them.
.USE
By giving a target this attribute, you turn it into pmake 's equivalent of a macro. When the target is used as a source for another target, the other target acquires the commands, sources and attributes (except .USE) of the source. If the target already has commands, the .USE target's commands are added to the end. If more than one .USE-marked source is given to a target, the rules are applied sequentially.

The typical .USE rule will use the sources of the target to which it is applied (as stored in the .ALLSRC variable for the target) as its "arguments." Several system makefiles (not to be confused with the system makefile) make use of these .USE rules to make developing easier (they're in the default, system makefile directory).

Special Targets

There are certain targets that have special meaning to pmake . When you use one on a dependency line, it is the only target that may appear on the left-hand-side of the operator. As for the attributes and variables, all the special targets begin with a period and consist of upper-case letters only. The targets are as follows:

.BEGIN
Any commands attached to this target are executed before anything else is done. You can use it for any initialization that needs doing.
.DEFAULT
This is sort of a .USE rule for any target (that was used only as a source) that pmake can't figure out any other way to create. It's only "sort of" a .USE rule because only the shell script attached to the .DEFAULT target is used. The .IMPSRC variable of a target that inherits .DEFAULT's commands is set to the target's own name.
.END
This serves a function similar to .BEGIN, in that commands attached to it are executed once everything has been re-created (so long as no errors occurred). It also serves the extra function of being a place on which pmake can hang commands you put off to the end. Thus the script for this target will be executed before any of the commands you save with the ellipsis marker.
.IGNORE
This target marks each of its sources with the .IGNORE attribute. If you don't give it any sources, then it is like giving the -i flag when you invoke pmake --errors are ignored for all commands.
.INCLUDES
The sources for this target are taken to be suffixes that indicate a file that can be included in a program source file. The suffix must already be declared with .SUFFIXES. Any suffix so marked will have the directories on its search path (see .PATH, below) placed in the .INCLUDES variable, each preceded by a "-I" flag. This variable can then be used as an argument for the compiler in the normal fashion. The ".h" suffix is already marked in this way in the system makefile. For example, if you have
.SUFFIXES 			: .PCX
.PATH.PCX 			: \CLIPART
.INCLUDES 			: .PCX

pmake places "-I\CLIPART" in the .INCLUDES variable and you can say

bcc $(.INCLUDES) -c xprogram.c

(Note: the . INCLUDES variable is not actually filled in until the entire makefile has been read.)

.INTERRUPT
When pmake is interrupted, it will execute the commands in the script for this target, if it exists.
.LIBS
This does for libraries what .INCLUDES does for include files, except the flag used is "-L", as required by those linkers that allow you to tell them where to find libraries. The variable used is .LIBS.
.MAIN
If you didn't give a target (or targets) to create when you invoked pmake , it will take the sources of this target as the targets to create.
.MAKEFLAGS
This target provides a way for you to always specify flags for pmake when the makefile is used. The flags are just as they would be typed to the shell (except you can't use shell variables unless they're in the environment), though the -f and -r flags have no effect.
.PATH
If you give sources for this target, pmake will take them as directories in which to search for files it cannot find in the current directory. If you give no sources, it will clear out any directories added to the search path before.
.PATHsuffix
This does a similar thing to .PATH, but it does it only for files with the given suffix. The suffix must have been defined already. Look at Search Paths for more information.
.PRECIOUS
Similar to .IGNORE, this gives the .PRECIOUS attribute to each source on the dependency line, unless there are no sources, in which case the .PRECIOUS attribute is given to every target in the file.
.RECURSIVE
This target applies the .MAKE attribute to all its sources. It does nothing if you don't give it any sources.
.SILENT
When you use .SILENT as a target, it applies the .SILENT attribute to each of its sources. If there are no sources on the dependency line, then it is as if you gave pmake the -s flag and no commands will be echoed.
.SUFFIXES
This is used to give new file suffixes for pmake to handle. Each source is a suffix pmake should recognize. If you give a .SUFFIXES dependency line with no sources, pmake will forget about all the suffixes it knew.

In addition to these targets, a line of the form

attribute : sources

applies the attribute to all the targets listed as sources .

Modifying Variable Expansion

Variables need not always be expanded verbatim. The pmake program defines several modifiers that may be applied to a variable's value before it is expanded. You apply a modifier by placing it after the variable name with a colon between the two, like so:

${VARIABLE:modifier}

Each modifier is a single character followed by something specific to the modifier itself. You may apply as many modifiers as you want--each one is applied to the result of the previous and is separated from the previous by another colon.

There are several ways to modify a variable's expansion:

:M pattern
This is used to select only those words (a word is a series of characters that are neither spaces nor tabs) that match the given pattern. The pattern is a wildcard pattern like that used by the shell, where * means zero or more characters of any sort; ? is any single character; [abcd] matches any single character that is one of "a", "b", "c" or "d" (there may be any number of characters between the brackets); [0-9] matches any single character that is between "0" and "9" (i.e. any digit--this form may be freely mixed with the other bracket form), and `\' is used to escape any of the characters "*", "?", "[" or ":", leaving them as regular characters to match themselves in a word.
Remember that the pattern matcher requires you to prefix certain characters with a backslash, including backslash itself. this can lead to some impressive search strings, because pmake also requires that backslashes be preceded with backslashes:
    #if !empty(CURRENT_DIR:M*\\\\APPL\\\\*)

The above line checks to see if the current directory matches the form "*\APPL\*". (The pattern matcher is passed the string "*\\APPL\\*".)

:N pattern
This is identical to :M except it substitutes all words that don't match the given pattern.
:X pattern
This is like :M except that it returns only part of the matching string. You mark which part of the string you are interested in by enclosing it within backslashed square brackets ("\[" and "\]") (however, due to the backslash rules, you must actually use "\\[" and "\\]".)
    DEVEL_DIR 	:= \
     $(CURRENT_DIR:X \\[*\\\\$(ROOT_DIR:T)\\\\*\\]\\\\*)

The above line returns part of the CURRENT_DIR string, specifically the directory just under the root directory. Free of backslashes, it's searching for [*\$(ROOT_DIR:T)\*]\*. If there is a subdirectory below the development directory, then this will strip off the lower layers.

:S/ search-string / replacement-string /[g]
This causes the first occurrence of search-string in the variable to be replaced by replacement-string, unless the "g" flag is given at the end, in which case all occurrences of the string are replaced. The substitution is performed on each word in the variable in turn. If search-string begins with a caret ("^"), the string must match starting at the beginning of the word. If search-string ends with a dollar sign ("$"), the string must match to the end of the word (these two may be combined to force an exact match). If a backslash precedes these two characters, however, they lose their special meaning. Variable expansion also occurs in the normal fashion inside both the search-string and the replacement-string, except that a backslash is used to prevent the expansion of a "$", not another dollar sign, as is usual. Note that search-string is just a string, not a pattern, so none of the usual regular-expression/wildcard characters have any special meaning save "^" and "$". In the replacement string, the "&" character is replaced by the search-string unless it is preceded by a backslash. Thus, ":S/[A-D]/&&/" will find the string "[A-D]" and replace it with the string "[A-D][A-D]". You are allowed to use any character except colon or exclamation point to separate the two strings. This so-called delimiter character may be placed in either string by preceding it with a backslash.
:T
Replaces each word in the variable expansion by its last component (its "tail"). For example, given
        OBJS = ..\LIB\A.OBJ B \USR\LIB\LIBM.A
        TAILS = $(OBJS:T)

the variable TAILS would expand to "a.obj b libm.a" .

:H
This is similar to :T, except that every word is replaced by everything but the tail (the "head"). Using the same definition of OBJS, the string "$(OBJS:H)" would expand to "..\LIB \USR\LIB" Note that the final slash on the heads is removed and anything without a head is replaced by a single period.
:E
This replaces each word with its suffix ("extension"), so "$(OBJS:E)" would give you ".OBJ .A" .
:R
This replaces each word with everything but the suffix (thus returning the "root" of the word). "$(OBJS:R)" expands to "..\LIB\A B \USR\LIB\LIBM".

In addition, another style of substitution is also supported. This looks like:

$(VARIABLE:search-string=replacement)

It must be the last modifier in the chain. The search is anchored at the end of each word, so only suffixes or whole words may be replaced.

Using Tools: 13.5 pmake: Advanced pmake Techniques

This section is devoted to those facilities in pmake that allow you to do a great deal in a makefile with very little work, as well as do some things you couldn't do in make without a great deal of work (and perhaps the use of other programs). The problem with these features is that they must be handled with care, or you will end up with a mess.

Search Paths

The pmake tool supports the dispersal of files into multiple directories by allowing you to specify places to look for sources with .PATH targets in the makefile. The directories you give as sources for these targets make up a "search path." Only those files used exclusively as sources are actually sought on a search path, the assumption being that anything listed as a target in the makefile can be created by the makefile and thus should be in the current directory.

There are two types of search paths in pmake : one is used for all types of files (including included makefiles) and is specified with a plain .PATH target (e.g. ".PATH : RCS"), while the other is specific to a certain type of file, as indicated by the file's suffix. A specific search path is indicated by immediately following the .PATH with the suffix of the file. For instance

.PATH.H : \GEOSDEV\DEVEL\APPL\WORPRO \GEOSDEV\DEVEL

would tell pmake to look in the directories \GEOSDEV\DEVEL\APPL\WORPRO and \GEOSDEV\DEVEL for any files whose suffix is .H.

The current directory is always consulted first to see if a file exists. Only if it cannot be found are the directories in the specific search path, followed by those in the general search path, consulted.

A search path is also used when expanding wildcard characters. If the pattern has a recognizable suffix on it, the path for that suffix will be used for the expansion. Otherwise the default search path is employed.

When a file is found in some directory other than the current one, all local variables that would have contained the target's name (.ALLSRC and .IMPSRC) will instead contain the path to the file, as found by pmake . Thus if you have a file ..\LIB\MUMBLE.C and a makefile

.PATH.c : ..\LIB
MUMBLE.EXE 			: MUMBLE.C
    $(CCOM) -o $(.TARGET) $(.ALLSRC)

the command executed to create MUMBLE.EXE would be "bcc -o MUMBLE ..\LIB\MUMBLE.C"

If a file exists in two directories on the same search path, the file in the first directory on the path will be the one pmake uses. So if you have a large system spread over many directories, it would behoove you to follow a naming convention that avoids such conflicts.

Something you should know about the way search paths are implemented is that each directory is read, and its contents cached, exactly once--when it is first encountered--so any changes to the directories while pmake is running will not be noted when searching for implicit sources, nor will they be found when pmake attempts to discover when the file was last modified, unless the file was created in the current directory.

Conditional Statements

Like a C compiler, pmake allows you to configure the makefile using conditional statements. A conditional looks like this:

#if <Boolean expression>
<lines>
#elif <another Boolean expression>
<more lines>
#else
<still more lines>
#endif

They may be nested to a depth of 30 and may occur anywhere (except in a comment, of course). The "#" must be the very first character on the line.

Each Boolean expression is made up of terms that look like function calls, the standard C Boolean operators &&, ||, and !, and the standard relational operators ==, !=, >, >=, <, and <=, with == and != being overloaded to allow string comparisons as well. The && operator represents logical AND; || is logical OR and ! is logical NOT. The arithmetic and string operators take precedence over all three of these operators, while NOT takes precedence over AND, which takes precedence over OR. This precedence may be overridden with parentheses, and an expression may be parenthesized to any level. Each Boolean term looks like a call on one of four functions:

make
The syntax is make(target) where target is a target in the makefile. This is true if the given target was specified on the command line or as the source for a .MAIN target (note that the sources for .MAIN are only used if no targets were given on the command line).
defined
The syntax is defined(variable) and is true if variable is defined. Certain variables are defined in the system makefile that identify the system on which pmake is being run.
exists
The syntax is exists(file) and is true if the file can be found on the global search path (i.e. that defined by .PATH targets, not by .PATHsuffix targets).
empty
This syntax is much like the others, except the string inside the parentheses is of the same form as you would put between parentheses when expanding a variable, complete with modifiers. The function returns true if the resulting string is empty. (Note: an undefined variable in this context will cause at the very least a warning message about a malformed conditional, and at the worst will cause the process to stop once it has read the makefile. If you want to check for a variable being defined or empty, use the expression
    !defined(var) || empty(var)
as the definition of || will prevent the empty() from being evaluated and causing an error, if the variable is undefined). This can be used to see if a variable contains a given word, for example:
        #if !empty(var:Mword)

The arithmetic and string operators may only be used to test the value of a variable. The left-hand side must contain the variable expansion, while the right-hand side contains either a string, enclosed in double-quotes, or a number. The standard C numeric conventions (except for specifying an octal number) apply to both sides. For example,

#if $(OS) == 4.3
#if $(MACHINE) == "sun3"
#if $(LOAD_ADDR) < 0xc000

are all valid conditionals. In addition, the numeric value of a variable can be tested as a Boolean as follows:

#if $(LOAD)

would see if LOAD contains a non-zero value and

#if !$(LOAD)

would test if LOAD contains a zero value.

In addition to the bare #if, there are other forms that apply one of the first two functions to each term. They are as follows:

ifdef		defined
ifndef		!defined
ifmake		make
ifnmake		!make

There are also the "else if" forms: elif, elifdef, elifndef, elifmake, and elifnmake.

Using Tools: 13.6 pmake: The Way Things Work

When pmake reads the makefile, it parses sources and targets into nodes in a graph. The graph is directed only in the sense that pmake knows which way is up. Each node contains not only links to all its parents and children (the nodes that depend on it and those on which it depends, respectively), but also a count of the number of its children that have already been processed.

The most important thing to know about how pmake uses this graph is that the traversal is breadth-first and occurs in two passes.

After pmake has parsed the makefile, it begins with the nodes the user has told it to make (either on the command line, or via a .MAIN target, or by the target being the first in the file not labeled with the .NOTMAIN attribute) placed in a queue. It continues to take the node off the front of the queue, mark it as something that needs to be made, pass the node to Suff_FindDeps() (mentioned earlier) to find any implicit sources for the node, and place all the node's children that have yet to be marked at the end of the queue. If any of the children is a .USE rule, its attributes are applied to the parent, then its commands are appended to the parent's list of commands and its children are linked to its parent. The parent's unmade-children counter is then decremented (since the .USE node has been processed). This allows a .USE node to have children that are .USE nodes, and the rules will be applied in sequence. If the node has no children, it is placed at the end of another queue to be examined in the second pass. This process continues until the first queue is empty.

At this point, all the leaves of the graph are in the examination queue; pmake removes the node at the head of the queue and sees if it is out-of-date. If it is, it is passed to a function that will execute the commands for the node asynchronously. When the commands have completed, all the node's parents have their unmade-children counter decremented and, if the counter is then zero, they are placed on the examination queue. Only those parents that were marked on the downward pass are processed in this way. Thus pmake traverses the graph back up to the nodes the user instructed it to create. When the examination queue is empty and no shells are running to create a target, pmake is finished.

Once all targets have been processed, pmake executes the commands attached to the .END target, either explicitly or through the use of an ellipsis in a shell script. If there were no errors during the entire process but there are still some targets unmade ( pmake keeps a running count of how many targets are left to be made), there is a cycle in the graph. The pmake program does a depth-first traversal of the graph to find all the targets that weren't made and prints them out one by one.

Using Tools: 14 Swat Stub

The swat stub runs on the target machine, passing information between a running GEOS session and Swat on the host machine. It has one flag:

/H : hardware
Specify special hardware for sending signals. The only possible argument is P for sending via a serial port in a PCMCIA card.

Using Tools: 15 dumpgeo

Use dumpgeo to display core information about the content of a .GEO file.

Common usage

    dumpgeo mygeode.geo

dumpgeo takes the following optional flags:

-t
Will also print brief information about the library and resource tables in the geode.
-r[id]
Prints detailed relocation tables.
-d[id]
Prints a binary dump of the resources in the geode.

Using Tools: 16 cvtpcx

cvtpcx.exe is an application that converts PCX graphics files to the GEOS icon format, either as a .ui or a .goh file. There are two kinds of possible conversions. The PCX file may be converted to a single icon resource. Or the PCX file may contain the same icon image in a variety of different formats arranged in a predefined grid arrangement. The cvtpcx utility will convert each of these formats into its own icon resource within a single .goh or .ui file. For more information, refer to the bottom of this document.

The source PCX file may have at most a 16-color (4 bit) color lookup table. Use a program like Paint Shop Pro to reduce the number of colors to 2-16 colors and save it in PCX format.

The syntax for cvtpcx is

     cvtpcx <options> <filename>
If no <options> are specified, then this utility will convert the PCX file <filename> to single GEOS icon in a file called "mkr.ui" and display information about the icon.

<options> for either single icons or icons in a grid

These options specify the parameters for converting and saving the icon file.

-o<filename>
The file name for the moniker(s).
-G
Produce .goh output instead of .ui.
-g
Do not put resulting bitmaps in gstrings. The bitmap is still in a chunk, but no gstring opcodes surround it.
-j
Only output the gstring (don't create a moniker or put the gstring in a chunk)
-2
Use 2.0 constants in the resulting gstring/bitmap.
-f
Uses GrFillBitmap instead of GrDrawBitmap for all monochrome bitmaps. Also implies the use of 2.0 constants, since this will not work prior to version 2.0 as GrFillBitmap didn't exist.
-t
Cause the bitmap to be drawn relative to the current pen position if the program decides to optimize the moniker by drawing the bitmap somewhere other than 0,0. All monikers destined for the token database should be created with this flag.
-m<pixel>
Pixel to be masked out. Any pixel containing the color <pixel> (a decimal number) will be given a 0 bit in the mask for the color bitmap. Defaults to none.
-u
Insist the resulting bitmaps remain uncompacted. By default, cvtpcx will determine if it's worthwhile and automatically compact each bitmap for you.

Converting single icons:

A rectangle of the PCX image plane is converted into a single GEOS icon. The resulting file will have no start/end resource directives. The possible options include:

-n<filename>
Name the moniker "<filename>Moniker"
-N
map the colors to the indices for the Nokia device.
-w <width>
width to make GEOS icon bitmap. The input will be trimmed or extended (but masked) as necessary to accommodate this. Defaults to Standard size (48)
-h<height>
height to make GEOS icon bitmap. The input will be trimmed or extended (masked) as necessary to accommodate this. Defaults to Standard size (30)
-S<style>
<style> is one of the defined moniker styles: text, abbrevText, graphicText, icon, or tool. It defaults to icon. (The "-S" option is only for 2.0 and above.)
-s<size>
<size> is one of the defined moniker sizes: large, standard, or tiny. It defaults to standard.
-a<aspect>
<aspect> is one of the defined aspectRatio values: normal (vga), squished (ega) or verySquished (cga). Defaults to squished.
-b<aspect>
Forces creation of a single bitplane (B&W) icon, even if the source is 16 colors. Any pixel that isn't black (pixel 0) is set to 0 in the resulting bitmap. Black pixels are, of course, set to 1.
-B
Similar to -b, but any pixel that isn't white (pixel 15) or the mask (set by "-m") is set to 1.

Icons in a grid:

A grid is a sequence of the same icon in a variety of formats which are laid out from the left to the right. These formats share a common top edge, have a one pixel margin between icons, and a one pixel margin around the grid as a whole. There are 11 formats and three predefined grids.

The 11 predefined icon formats are:
Abbr | Color | Aspect | Size | Style | Width | Height
LC | 4-bit | normal | large | icon | 64 | 40
LM | Mono | normal | large | icon | 64 | 40
SC | 4-bit | normal | standard | icon | 48 | 30
SM | mono | normal | standard | icon | 48 | 30
LCGA | Mono | verySquished | large | icon | 64 | 18
SCGA | Mono | verySquished | tiny | icon | 48 | 14
YC | 4-bit | normal | tiny | icon | 32 | 20
YM | Mono | normal | tiny | icon | 32 | 20
TC | 4-bit | normal | tiny | tool | 15 | 15
TM | Mono | normal | tiny | tool | 15 | 15
TCGA | Mono | verySquished | tiny | tool | 15 | 10

The three grid layouts are:

Grid Flag | Formats within Grid
-l | LC, LM, SC, SM, LCGA, SCGA
-L | LC, LM, SC, SM, LCGA, SCGA, YC, YM, TM, TCGA (the YC and YM icons are used for application icons on handheld devices, while the TM and TCGA icons are used for the Presentation Manager system menu)
-z | TC, TM, TCGA

When the cvtpcx utility converts a grid to a GEOS icon resource file, it creates one file which contains a series of monikers which correspond to the formats in the grid. For example, a PCX file which is a -l grid will be converted to a GEOS header file which has a moniker with a LC format, followed by a moniker in a LM format, followed by a moniker in a SC format... etc. Each moniker is created with its own start/end resource directives.

The following options modify where in the PCX image plane the grid starts, how to name the monikers, which formats to not use, and how the GEOS file will be created.

-x<xoffset>
Specifies the left edge of the grid in the PCX image plane (the X coordinate of the 1-pixel margin to the left of the left-most icon in the grid.)
-y<yoffset>
Specifies the top edge of the grid (the Y coordinate of the 1-pixel margin to the top of the top- most icon in the grid.)
-n<moniker name>
Allows specifying the core name to give the moniker. The name for each moniker in a grid is formed as:
<moniker name><format abbrev>Moniker
For instance -nHello would create "HelloLCMoniker" for the leftmost icon in the "-l" grid.
-d<format(s)>
This flag indicates that the no moniker for the formats in the comma-separated list <format(s)> should be created for that grid.
-r<resource>
Specifies the string (other than the default "App") to begin the name of each resource. The resource names are of the form
<resource><format abbrev>MonikerResource.
The resource name is all uppercase for GOC output.
-R
Don't put out resource start/end directives; just produce the monikers, one after another.

Examples of cvtpcx:

The following converts the single icon "old_icon.pcx" to a .goh icon called "test.goh." The options for this icon specify that this icon uses GEOS 2.0 constants, is defined with GrFillBitmap(), uses Nokia colors, and will be drawn relative to the pen's location.

         cvtpcx -2 -f -t -N -G -otest.goh old_icon.pcx
The following converts the grid "grid_icon.pcx" in the -l grid arrangement into a .goh file with monikers for all the formats in a -l grid, with the exception of the LC format. The "-m11" option specifies that yellow pixels (those with the color 11) will be masked out. The output has the name "mkrconverted_grid_icon.goh".
        cvtpcx -l -2 -f -t -g -G -dLC -m11 -nconverted_grid_icon grid_icon.pcx

Using Tools: 17 pvm

This document is a single-page version of a a multi-page document, suitable for easy printing.


GenGlyph

The GenGlyph object allows you to easily display single lines of text without using a more complex GenText object. A GenGlyph can be thought of as a label in which you can display text but which offers no other functionality.

1 GenGlyph Features
2 GenGlyph Basics
3 Modifying a GenGlyph

GenGlyph: 1 GenGlyph Features

Applications often display simple lines of text. Text might request the user to make a selection. Text can alert the user to a certain application condition. The GenGlyph offers a simple, easy to use text display object.

One other generic object, GenText, provides a powerful means to display text with functions such as word-wrapping, font scalability, and an arbitrarily large buffer size. If you do not need such a versatile and powerful text display capability, however, it is wiser and easier to use a GenGlyph. This object only displays text in the default font and style for your application.

GenGlyph: 2 GenGlyph Basics

The GenGlyph object does not, by itself, offer any instance fields or messages. The functionality of a GenGlyph object is included entirely within the instance fields and messages of its superclass, GenClass .

Unlike text within a GenText, the text of a GenGlyph is taken solely from the object's GI_visMoniker . Therefore, the primary use of a GenGlyph lies in its visual moniker. Any GenClass messages dealing with an object's visual moniker will modify the appearance of a GenGlyph.

A GenGlyph does not allow word-wrapping; if your text may be longer than the object's horizontal constraints, either break up the lines of text into smaller, single-line GenGlyphs or use a GenText object.

Code Display 6-1 Using Hints to Manage GenGlyphs

/* This code will duplicate the display shown above */
@object GenInteractionClass DiskCopyBox = {
    GI_comp = @DiskCopyHeader, @SourceSelection, @DestinationSelection;
    GII_visibility = GIV_DIALOG;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
/* The DiskCopyHeader will be the line of instructional text. */
@object GenGlyphClass DiskCopyHeader = {
    GI_visMoniker = "Select source and destination for disk copy:";
    HINT_CENTER_MONIKER;			/* Centers the moniker horizontally. */
}
/* These objects will form the "Source" line. */
@object GenInteractionClass SourceSelection = {
    GI_comp = @SourceText, @SourceList;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_CENTER_MONIKER;
}
/* This group of objects will form the "Destination" line. */
@object GenInteractionClass DestinationSelection = {
    GI_comp = @DestinationText, @DestinationList;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_CENTER_MONIKER;
}
/* For simplicity, the List objects are not shown. */
@object GenGlyphClass SourceText = {
    GI_visMoniker = "Source:";
}
@object GenGlyphClass DestinationText = {
    GI_visMoniker = "Destination:";
}

GenGlyph: 3 Modifying a GenGlyph

You may wish at some point to change the text in a GenGlyph. Because a GenGlyph's text is entirely within its visual moniker, it is a simple matter to use GenClass messages to change the moniker dynamically. Use MSG_GEN_GET_VIS_MONIKER to return a GenGlyph's current moniker; use MSG_GEN_USE_VIS_MONIKER (or MSG_GEN_REPLACE_VIS_MONIKER ) to set a new moniker for a GenGlyph. Doing so will ensure that the object is visually rebuilt with the new moniker.

Code Display 6-2 A Toggle On/Off Switch Dialog Box

@object GenInteractionClass MyDialogBox = {
    GI_comp = @DialogText, @DialogButton;
    GII_visibility = GIV_DIALOG;
}
/* Monikers for the Instructions (the GenGlyphs). */
@visMoniker OnTextMoniker = "Click the `ON' button";
@visMoniker OffTextMoniker = "Click the `OFF' button";
/* Monikers for the triggers. */
@visMoniker OnButtonMoniker = "ON";
@visMoniker OffButtonMoniker = "OFF";
/* The object begins in the "On" state. */
@object GenGlyphClass DialogText = {
    GI_vismoniker = @OnTextMoniker;
}
/* Whenever the button is pressed, MSG_FLIP_THE_SWITCH will change both the glyph
 * and the trigger monikers. */
@object GenTriggerClass DialogButton = {
    GI_visMoniker = @OnButtonMoniker;
    GTI_actionMsg = MSG_FLIP_THE_SWITCH;
    GTI_destination = process;
}
@method MyProcessClass, MSG_FLIP_THE_SWITCH {
    ChunkHandle testMoniker;				/* Stores the temporary moniker. */
    testMoniker = @call DialogButton::MSG_GEN_GET_VIS_MONIKER;
/* If the moniker is "ON", turn both it and the glyph to the Off monikers.
 * Otherwise (the moniker is "OFF"), turn both it and the glyph to the On
 * monikers. Both visual updates are delayed via the UI queue (and will therefore
 * be updated at the same time rather than separately) to avoid flashing. */
    if (testMoniker == "ON") {
    @call DialogButton::MSG_GEN_USE_VIS_MONIKER(OptrToChunk(@OffButtonMoniker),
                        VUM_DELAYED_VIA_UI_QUEUE);
    @call DialogText::MSG_GEN_USE_VIS_MONIKER(OptrToChunk(@OffTextMoniker),
                            VUM_DELAYED_VIA_UI_QUEUE);
    }
    else {
    @call DialogButton::MSG_GEN_USE_VIS_MONIKER(OptrToChunk(@OnButtonMoniker),
                VUM_DELAYED_VIA_UI_QUEUE);
    @call DialogText::MSG_GEN_USE_VIS_MONIKER(OptrToChunk(@OnTextMoniker),
                VUM_DELAYED_VIA_UI_QUEUE);
    }
    }
This document is a single-page version of a a multi-page document, suitable for easy printing.


Menus and Dialog Boxes

GenInteraction is a high-level object which manages groups of other generic objects. These groups of objects can appear as part of a window, as their own menu or even as a separate dialog box. GenInteractionClass also provides the means to interpret actions of its children and adjust the behavior of the Interaction accordingly. Therefore, it is meaningless for an Interaction to appear without children to manage.

GenInteractionClass is a subclass of GenClass and therefore inherits all of its functionality. Of special interest for GenInteractions is geometry management with hints provided in GenClass . See the GenClass chapterand the Managing UI Geometry chapterfor more information.

You should be familiar with generic objects before reading this chapter. Please see the UI Overviewfor an summary of each generic object in the user interface. You should be particularly familiar with the instance data of and messages handled by GenClass

1 GenInteraction Features
1.1 Sub-Group Interactions
1.2 Popup Interactions (Menus)
1.3 Dialog Boxes
1.4 PopOuts
2 GenInteraction Instance Data
2.1 GenInteraction Visibility
2.2 Standard Interactions (Menus)
2.3 GenInteraction Types
2.4 GenInteraction Attributes
3 GenInteraction Usage
3.1 Visibilities
3.2 Types
4 Supplemental Usage
4.1 Initiating Interactions
4.2 Dismissing Interactions
4.3 Modality for Dialogs
4.4 Managing Input
4.5 Thread Blocking Routines
5 Interaction Commands
5.1 InteractionCommand Types
5.2 Dialog Control
5.3 Standard Response Triggers
5.4 Replacing Default Triggers
5.5 Triggers Completing Interactions

Menus and Dialog Boxes: 1 GenInteraction Features

Because of the nature of GenInteractionClass , objects of this class may appear in a variety of forms. The specific UI will have some control over the visual presentation of GenInteractions, but your application can usually specify in what manner the Interaction should appear. Your application may also add hints to the specific UI to modify the arrangement of objects within the Interaction.

GenInteraction provides three main visual implementations:

  • A sub-group, having no inherent visual representation at all. (The visual implementation depends on the Interaction's parent.)
  • A popup (typically a menu) remaining hidden from the application until activated, and then normally only remaining on-screen for the duration of the selection.
  • An independently displayable window (dialog box) coming on-screen whenever activated and going off-screen whenever instructed to.
  • Popout dialog boxes which can act as sub-group GenInteractions (for example a menu bar) that can be "popped out" into dialog boxes. Note that popouts are not related to pop-ups.

Specifically, a GenInteraction also provides the following features:

  • organization of the spacial arrangement of other objects.
  • the capability to apply properties within an Interaction either immediately (in immediate mode) or at a later time (in delayed mode).
  • the display of progress reports on the state of a process or object.
  • the ability to initiate commands.
  • several default dialog boxes for notification and affirmation responses.
  • modal dialog boxes that block other threads of execution until responded to.

Sub-Group Interactions

Popup Interactions (Menus)

Dialog Boxes

PopOuts

Menus and Dialog Boxes: 1.1 GenInteraction Features: Sub-Group Interactions

A sub-group Interaction is the simplest manifestation of a GenInteraction. A sub-group may only appear as part of a larger window. You will most often use sub-group Interactions to arrange and group other objects (the Interaction's children) within proper places in the UI. This grouping is also sometimes useful to perform geometry actions upon and otherwise manipulate a group of objects as a whole. Using Interactions eliminates the need to provide individual requirements to each child object.

For example, you can use the hint HINT_ORIENT_CHILDREN_VERTICALLY on a single GenInteraction object to display its children in a vertical row. The Interaction's only function in this case is to provide an object for the hint to act upon. Interactions of this type may contain any number and type of children. Because they are not independently displayable, however, their use becomes limited to the constraints of the larger window. If you need the Interaction's children to be hidden from view until needed, you should use a popup or dialog Interaction.

Menus and Dialog Boxes: 1.2 GenInteraction Features: Popup Interactions (Menus)

A popup is an Interaction that you can use to contain commands or selections that should only be displayed when the popup itself is activated. A popup usually conceals its children from view until needed, therefore avoiding cluttering up the UI with dozens of objects at any given time. Popups are most often implemented as menus. Popups usually only remain on-screen until either a selection within the popup is made or the user activates some other object. Menus in OSF/Motif, for example, will only remain on-screen until an object in the menu is selected or until the user clicks somewhere else in the application. (GEOS' implementation of OSF/Motif does allow popups to be pinned, however.)

Popups are not independently displayable; they must appear as part of another window. The popup Interaction's visual moniker usually serves as the only visual cue to the existence of the menu until it is activated. These menu titles usually appear in an appropriate menu bar of the window.

Menus and Dialog Boxes: 1.3 GenInteraction Features: Dialog Boxes

A dialog box is an Interaction that takes the form of an independently displayable window. Typically, a dialog box is a temporary window brought up by the application to request information from the user. Dialogs differ from Primary and Document windows in several ways, however. (For example, most dialogs are not resizable.)

A dialog box can be in either of two conditions: modal or non-modal. By default, dialog boxes are non-modal. A modal dialog box is one in which all other input to the application is blocked until the user responds to the dialog. You will usually need modal dialog boxes when an application must gain some further information before proceeding. For example, many of the System Preferences dialog boxes are modal to prevent the user from changing other aspects of the system while these dialogs are up.

Dialog boxes come on-screen either through direct user activation or through the application's instigation. By default, a dialog box will usually have an associated trigger object placed within the UI. This trigger's only function is to allow the user to bring the dialog box on-screen. You may remove this default activation trigger if your application code should bring up the dialog box instead.

Menus and Dialog Boxes: 1.4 GenInteraction Features: PopOuts

A popout GenInteraction is really just a grouping object that exists as either a simple sub-group or as a dialog box. Popouts generally begin life as sub-groups but can be "popped out" into dialog boxes that can be repositioned wherever the user wishes them. The dialog boxes can also be popped back into their sub-group arrangement.

In general, the GenInteraction is either popped-out or popped-in by double-clicking within the Interaction bounds.

Menus and Dialog Boxes: 2 GenInteraction Instance Data

GenInteraction has a set of instance fields; all are listed in GenInteraction Instance Fields . Any objects of GenInteractionClass or one of its subclasses will contain these instance fields, along with the instance fields of GenClass .

Code Display 7-1 GenInteraction Instance Fields

@instance GenInteractionType GII_type = GIT_ORGANIZATIONAL;
typedef ByteEnum GenInteractionType;
#define GIT_ORGANIZATIONAL 0
#define GIT_PROPERTIES 1
#define GIT_PROGRESS 2
#define GIT_COMMAND 3
#define GIT_NOTIFICATION 4
#define GIT_AFFIRMATION 5
#define GIT_MULTIPLE_RESPONSE 6
@instance GenInteractionVisibility GII_visibility = GIV_SUB_GROUP;
typedef ByteEnum GenInteractionVisibility;
#define GIV_NO_PREFERENCE 0
#define GIV_POPUP 1
#define GIV_SUB_GROUP 2
#define GIV_CONTROL_GROUP 3
#define GIV_DIALOG 4
#define GIV_POPOUT 5
@instance GenInteractionAttrs GII_attrs = 0;
typedef ByteFlags GenInteractionAttrs;
#define GIA_NOT_USER_INITIATABLE					0x80
#define GIA_INITIATED_VIA_USER_DO_DIALOG					0x40
#define GIA_MODAL					0x20
#define GIA_SYS_MODAL					0x10

GII_ type describes the function of the GenInteraction.

GII_ visibility describes the visual implementation of the GenInteraction. Typically, this affects where and how the object and its children are arranged within the UI. A GenInteraction's visibility may also affect the behavior of the children, though this is generally a function of the GII_ type field.

GII_ attrs define attributes that affect how a GenInteraction is initiated, and whether input to other parts of the application (or system) is affected.

Code Display 7-2 GenInteraction Hints

/*
 * Hints that affect Properties GenInteractions. These hints are described in the
 * GIT_PROPERTIES GenInteraction section.
 */
@vardata void HINT_INTERACTION_SINGLE_USAGE;
@vardata void HINT_INTERACTION_COMPLEX_PROPERTIES;
@vardata void HINT_INTERACTION_SIMPLE_PROPERTIES;
@vardata void HINT_INTERACTION_RELATED_PROPERTIES;
@vardata void HINT_INTERACTION_UNRELATED_PROPERTIES;
@vardata void HINT_INTERACTION_SLOW_RESPONSE_PROPERTIES;
@vardata void HINT_INTERACTION_FAST_RESPONSE_PROPERTIES;
@vardata void HINT_INTERACTION_REQUIRES_VALIDATION;
/* Hints that affect all types of GenInteractions. */
@vardata void HINT_INTERACTION_FREQUENT_USAGE;
@vardata void HINT_INTERACTION_INFREQUENT_USAGE;
@vardata void HINT_INTERACTION_MAKE_RESIZABLE;
@vardata void HINT_INTERACTION_CANNOT_BE_DEFAULT;
@vardata void HINT_INTERACTION_MODAL;
@vardata void HINT_INTERACTION_NO_DISTURB;
@vardata void HINT_INTERACTION_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD;
@vardata void HINT_CUSTOM_SYS_MENU;
@vardata void HINT_INTERACTION_MAXIMIZABLE;
@vardata void HINT_INTERACTION_POPOUT_HIDDEN_ON_STARTUP;
@vardata void HINT_INTERACTION_DISCARD_WHEN_CLOSED;

HINT_INTERACTION_FREQUENT_USAGE indicates that the GenInteraction is frequently used, and the specific UI may alter default functionality to reflect this. In most cases, this prevents dialog boxes from being automatically dismissed when the user is done interacting with them.

HINT_INTERACTION_INFREQUENT_USAGE indicates that the GenInteraction is infrequently used. In some specific UIs, this prevents sub-menus from automatically cascading.

HINT_INTERACTION_MAKE_RESIZABLE indicates that this GenInteraction may be resized even if it not normally allowed to do so. This hint usually only applies to dialog boxes.

HINT_INTERACTION_CANNOT_BE_DEFAULT indicates that this GenInteraction should not be activated as a system default. This hint prevents children of this GenInteraction from being activated as system defaults also. You should use this hint to mark groups of objects that may be potentially destructive; the user will be prevented from accidentally activating any objects in the GenInteraction.

HINT_INTERACTION_MODAL indicates that this GenInteraction (a dialog box) should be application modal. This hint should be used in place of GIA_MODAL in cases where modality is not needed for functionality, but is needed to present a cleaner UI to the user. Ideally, you should be able to go back and remove these hints by reworking the UI at some later time.

HINT_INTERACTION_NO_DISTURB indicates that this GenInteraction, if initiated, should be brought on-screen without disturbing the focus of the application. Usually, this hint is placed on notification type dialog boxes (such as new-mail notifications) so that the dialog box doesn't irritate the user by drawing away the focus.

HINT_INTERACTION_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD indicates that the default action for this Interaction group is to navigate to the next field. If something activates the GenInteraction's default action (for example by double-clicking within its confines) objects within the Interaction will not be activated; the focus will travel to the next field.

HINT_CUSTOM_SYS_MENU indicates that this GenInteraction menu is a custom system menu. Depending on the specific UI, the standard system menu will be added as a sub-menu of the custom system menu. This hint takes an integer value that specifies the child position to place the standard system menu (0 being the first position).

HINT_INTERACTION_MAXIMIZABLE indicates that this GenInteraction should be maximizable. This hint also allows the GenInteraction to become restorable to its non-maximizable state.

HINT_INTERACTION_POPOUT_HIDDEN_ON_STARTUP indicates that the GIV_POPOUT GenInteraction should be initially off-screen.

HINT_INTERACTION_DISCARD_WHEN_CLOSED indicates that the GenInteraction object (which must be a dialog box) can be discarded from memory when it is brought off-screen. This is an optimazation designed to free up memory. If the GenInteraction contains this hint, it must also be marked GIA_NOT_USER_INITIATABLE (to prevent it from being referenced), and contain a one-way upward generic link only. The dialog and all of its children must reside in their own block marked nonDetachable and discardable . (There should also be no other objects in that block, of course.)

Code Display 7-3 GenInteraction Optional Attributes

/* Optional Attributes. */
@vardata byte ATTR_GEN_INTERACTION_GROUP_TYPE;						/* GenInteractionGroupType */
typedef ByteEnum GenInteractionGroupType;
#define GIGT_FILE_MENU 0
#define GIGT_EDIT_MENU 1
#define GIGT_VIEW_MENU 2
#define GIGT_OPTIONS_MENU 3
#define GIGT_WINDOW_MENU 4
#define GIGT_HELP_MENU 5
#define GIGT_PRINT_GROUP 6
@vardata void ATTR_GEN_INTERACTION_OVERRIDE_INPUT_RESTRICTIONS;
@vardata void ATTR_GEN_INTERACTION_ABIDE_BY_INPUT_RESTRICTIONS;
@vardata void ATTR_GEN_INTERACTION_POPPED_OUT;
@vardata void ATTR_GEN_INTERACTION_POPOUT_NOT_CLOSABLE;

ATTR_GEN_INTERACTION_GROUP_TYPE indicates that this GenInteraction (typically a menu) is a special group that the specific UI should be aware of. This optional attribute takes a GenInteractionGroupType as its argument. For more information on these standard menus, see Standard Interactions (Menus) .

ATTR_GEN_INTERACTION_OVERRIDE_INPUT_RESTRICTIONS instructs the specific UI to override any input restrictions in place on a modal dialog box. Many dialog types (GIT_PROGRESS, GIT_NOTIFICATION, GIT_AFFIRMATION and GIT_MULTIPLE_RESPONSE) override these input restrictions by default anyway; this hint is for use in cases where this override is not provided.

ATTR_GEN_INTERACTION_ABIDE_BY_INPUT_RESTRICTIONS instructs the specific UI to abide by any input restrictions in place on a modal dialog box. In general, this changes the default behavior for many dialog types (GIT_PROGRESS, GIT_NOTIFICATION, GIT_AFFIRMATION and GIT_MULTIPLE_RESPONSE) which normally override any input restrictions be default.

ATTR_GEN_INTERACTION_POPPED_OUT indicates that a GIV_POPOUT GenInteraction is in its popped-out state. Absence of this optional attribute indicates that the popout is in its popped-in state. This attribute can be set initially, in which case the Popout will appear popped out. This attribute is also set internally by the UI whenever the state of the GIV_POPOUT changes.

ATTR_GEN_INTERACTION_POPOUT_NOT_CLOSABLE indicates that a GIV_POPOUT, when popped out in its dialog box state, will not be closable; you will only be able to pop it back into its sub-group state.

GenInteraction Visibility

Standard Interactions (Menus)

GenInteraction Types

GenInteraction Attributes

Menus and Dialog Boxes: 2.1 GenInteraction Instance Data: GenInteraction Visibility

GII_visibility, MSG_GEN_INTERACTION_GET_VISIBILITY, MSG_GEN_INTERACTION_SET_VISIBILITY

The GII _visibility instance field describes in what manner the user interface will display the Interaction. This is important for GenInteractions because of the wide variety of ways they may appear within an application. In most cases, this attribute will not directly affect the functionality of the children of the Interaction but only the visual implementation and specific UI activations of the objects.

Certain GII_types (see below) may only be meaningful under certain GII_ visibility types, however. For example, GIT_NOTIFICATION, GIT_AFFIRMATION, and GIT_MULTIPLE_RESPONSE gain most of their significant functionality within dialog boxes (visibility GIV_DIALOG). In these cases the visibility will affect the functionality of the Interaction.

Each of the following visibilities is an enumeration of type GenInteractionVisibility ; therefore, you may select one and only one of the following types for your Interaction. By default, a GenInteraction is GIV_SUB_GROUP.

GIV_SUB_GROUP
This specifies that the Interaction serves as a visual grouping of objects within a larger window. This visibility creates an Interaction within the parent window and is therefore not independently displayable. This visibility is the default case.
GIV_POPUP
This specifies that this Interaction is temporary and only appears on-screen for the duration of a single selection. In most specific UIs, the popup will stay up until either an object in the Interaction is activated or until the user clicks somewhere else. Some specific UIs allow a menu to be pinned, however. The most common manifestation of a popup is a menu. Popups typically appear within appropriate "menu bars" of an application but may be independently displayable under different specific UIs.
GIV_DIALOG
This specifies that this Interaction should appear as a dialog box. A dialog box is a temporary window used to display controls or request information from the user. A dialog box can typically contain any number or variety of other generic objects. A dialog box may be either modal or non-modal.
GIV_CONTROL_GROUP
This specifies that this Interaction contains controls and therefore should not appear as a popup Interaction. This prevents the Interaction from disappearing before the user can select an entry in the control group. A control group Interaction may appear as either a sub-group or dialog, depending on the specific UI and its position within the application's generic object tree.
GIV_POPOUT
This specifies that the GenInteraction may act as either a sub-group or as a dialog box, depending on its state. Usually, popouts act as normal sub-group Interactions until the user or the application "pops" them out into a dialog box. Popouts are normally "popped out" into their dialog box state by double-clicking within the confines of the GenInteraction. This popout state is reflected with either the presence or absence of the ATTR_GEN_INTERACTION_POPPED_OUT vardata entry.
GIV_NO_PREFERENCE
This specifies that there is no visual preference for this Interaction. The specific UI will create the Interaction based on hints, the types of children it contains, and its location in the generic tree.

You may also retrieve or set the visibility of any Interaction at run-time. To retrieve the GenInteractionVisibility stored in GII_visibility , send the Interaction a MSG_GEN_INTERACTION_GET_VISIBILITY . You can set the visibility of any Interaction by sending it a MSG_GEN_INTERACTION_SET_VISIBILITY . Make sure that any Interaction you set the visibility for is not currently GS_USABLE or an error will result.

MSG_GEN_INTERACTION_GET_VISIBILITY
byte	MSG_GEN_INTERACTION_GET_VISIBILITY();

This message retrieves the current GenInteractionVisibility stored in the GII_visibility instance field of an Interaction.

Source: Unrestricted.

Destination: Any GenInteraction object.

Parameters: None.

Return: GenInteractionVisibility of the Interaction object.

Interception: Generally not intercepted.

MSG_GEN_INTERACTION_SET_VISIBILITY
void	MSG_GEN_INTERACTION_SET_VISIBILITY(
        byte	visibility);

This message sets the GII_visibility instance data for the GenInteraction. This message must pass a valid GenInteractionVisibility type to the Interaction object. The Interaction must not be GS_USABLE when receiving this message. The new visibility will take effect when the Interaction is next made GS_USABLE.

Source: Unrestricted.

Destination: Any non-usable GenInteraction object.

Parameters: visibility GenInteractionVisibility for interaction.

Return: Nothing.

Interception: Generally not intercepted.

Warnings: Make sure that the object sent this message is not GS_USABLE.

Menus and Dialog Boxes: 2.2 GenInteraction Instance Data: Standard Interactions (Menus)

ATTR_GEN_INTERACTION_GROUP_TYPE, GenInteractionGroupType

Often you will set up standard menus such as the File menu, the Window menu, the Edit menu, and the View menu. You can set these menus up very simply, with the attribute ATTR_GEN_INTERACTION_GROUP_TYPE .

This attribute specifies the type of menu the GenInteraction is via a value of type GenInteractionGroupType . This can be any of the following:

GIGT_FILE_MENU
Creates a standard File menu. Typically has a document control and print control as its children.
GIGT_EDIT_MENU
Creates a standard Edit menu with Cut, Copy, Paste, and Undo as appropriate. Typically has a GenEditControl as its child.
GIGT_VIEW_MENU
Creates a standard View menu; typically has a GenView controller as its child.
GIGT_OPTIONS_MENU
Creates a standard Options menu; typically, this menu has application-specific option entries as well as a GenToolControl as its children.
GIGT_WINDOW_MENU
Creates a standard Window menu; typically has a display controller as its child.
GIGT_HELP_MENU
Creates a standard Help menu; typically has the help objects as its children.
GIGT_PRINT_GROUP
Indicates that the GenInteraction is the print group. Typically contains a print controller and other print commands.

Each of the above types will set up the proper GenInteraction attributes and other generic objects to complete the interactions. Thus, by setting up a GenInteraction like this:

@object GenInteractionClass MyEditMenu = {
    GI_comp = MyEditControl;
    GII_visibility = GIV_POPUP;
    ATTR_GEN_INTERACTION_GROUP_TYPE =
            (GIGT_EDIT_MENU);
}

you can have a complete Edit menu in your program. (Of course, you must also supply the GenEditControl object that would be MyEditControl in the example.)

Menus and Dialog Boxes: 2.3 GenInteraction Instance Data: GenInteraction Types

GII_type, MSG_GEN_INTERACTION_GET_TYPE, MSG_GEN_INTERACTION_SET_TYPE

The GII_type instance field describes the contents of the Interaction. This attribute depends on the makeup of the children within the Interaction. (Note that it would be unusual for an Interaction to appear without children.) In most cases, this will directly affect the functionality of the particular Interaction. Every GenInteraction object should contain children that perform some function within the UI. That function is determined in part by this instance field.

Each of the following types is an enumeration of GenInteractionType ; therefore, you may select one and only one of the following types for your Interaction. By default, a GenInteraction is GIT_ORGANIZATIONAL.

GIT_ORGANIZATIONAL
This specifies that the Interaction is merely organizational. This allows the Interaction to provide geometry management (and not much else) to its children. Any objects within an organizational Interaction should be able to perform their actions independently. Organizational Interactions may appear in any visual form: sub-groups, menus, or dialog boxes.
GIT_PROPERTIES
This specifies that the Interaction contains properties (attributes) that the user can change. Typically, a properties Interaction will operate in one of two modes: immediate or delayed. If the GII_visibility attribute causes this Interaction to appear as a dialog box, the specific UI may create "Apply" and "Reset" triggers. In delayed mode, an apply trigger allows the Interaction to process changes made within the properties Interaction as a group rather than individually; the reset trigger allows the Interaction to reset the properties to their former state. If a popup is GIT_PROPERTIES, it will operate in immediate mode even if hints attempt to override this.
GIT_PROGRESS
This specifies that the Interaction displays a progress report for some operation. For example, a dialog box may appear showing a disk copy operation with the time remaining to its completion. If the GII_visibility attribute causes this Interaction to appear as a dialog box, the specific UI may create a "Stop" trigger that halts the operation in progress and the progress reporting. Your application is responsible for stopping the Interaction from communicating its progress report and may dismiss the Interaction if the specific UI desires.
GIT_COMMAND
This specifies that the Interaction contains commands that the application provides. If the GII_visibility attribute causes this Interaction to appear as a dialog box, the specific UI may create a "Close" trigger to dismiss the dialog. You will have to supply your own specific command triggers within a GIT_COMMAND dialog box.
GIT_NOTIFICATION
This specifies that the Interaction sends notification of some event. If the GII_visibility attribute causes this Interaction to appear as a dialog box, the specific UI may create an "OK" trigger that the user can press to acknowledge the notification.
GIT_AFFIRMATION
This specifies that the Interaction asks for confirmation of an operation. If the GII_visibility attribute causes this Interaction to appear as a dialog box, the specific UI may create "Yes" and "No" triggers. Depending upon the Specific UI, these triggers may dismiss the dialog box.
GIT_MULTIPLE_RESPONSE
This specifies that this Interaction may include multiple items that the user can respond to. (Your application should add custom response triggers using the GenTrigger's vardata ATTR_GEN_TRIGGER_INTERACTION_COMMAND .) If you wish these triggers to appear in a dialog box reply bar, use HINT_SEEK_REPLY_BAR .

You may retrieve or set the type of any Interaction at run-time. To retrieve the GenInteractionType stored in the GII_type instance field, send MSG_GEN_INTERACTION_GET_TYPE . You can set the type of any Interaction by sending it MSG_GEN_INTERACTION_SET_TYPE . Make sure that any Interaction you set the type for is not currently GS_USABLE or an error will result.

MSG_GEN_INTERACTION_GET_TYPE
byte	MSG_GEN_INTERACTION_GET_TYPE();

This message returns the current GenInteractionType stored in the GII_type instance field for the Interaction.

Source: Unrestricted.

Destination: Any GenInteraction object.

Parameters: None.

Return: GenInteractionType of the Interaction.

Interception: Generally not intercepted.

MSG_GEN_INTERACTION_SET_TYPE
void	MSG_GEN_INTERACTION_SET_TYPE(
        byte	type);

This message sets the GII_type instance data for the GenInteraction. This message must pass a valid GenInteractionType enumerated type to the Interaction object. The Interaction must not be GS_USABLE when receiving this message; the new type will take effect when the Interaction is next made GS_USABLE.

Source: Unrestricted.

Destination: Any non-usable GenInteraction object.

Parameters: type GenInteractionType for the Interaction.

Return: Nothing.

Interception: Generally not intercepted.

Warnings: Make sure that the object is not GS_USABLE when sending it this message.

Menus and Dialog Boxes: 2.4 GenInteraction Instance Data: GenInteraction Attributes

GII_attrs, MSG_GEN_INTERACTION_GET_ATTRS, MSG_GEN_INTERACTION_SET_ATTRS

The GII_attrs instance field describes how the Interaction behaves under various circumstances. These attributes only affect the behavior of dialog boxes. If the Interaction given these attributes is not a dialog box, the attributes will have no effect. This field specifies how a GIV_DIALOG Interaction may be initiated and whether input to other parts of the UI is allowed while the Interaction is active. The GII_attrs instance field is a bitfield and therefore any combination of these attributes may be set.

None of these attributes is set by default.

GIA_NOT_USER_INITIATABLE
This attribute specifies that the user cannot bring up the dialog directly. Unless this attribute is set, most specific UIs will place a default activation trigger within a proper place in the UI to initiate the dialog box. This attribute prevents this trigger from appearing. To bring up a dialog box set GIA_NOT_USER_INITIATABLE, your application must send the dialog a MSG_GEN_INTERACTION_INITIATE. Usually, these Interactions should be direct children of either a GenPrimary or GenApplication.
GIA_INITIATED_VIA_USER_DO_DIALOG
This attribute specifies that the application brings up this Interaction through UserDoDialog () . You should use this routine in special cases where you need the user to respond to a dialog box before continuing with your application thread. (This routine blocks the calling thread until the user responds to the dialog box.) Any dialog boxes marked with this attribute should not be initiated with MSG_GEN_INTERACTION_INITIATE. The UI won't include a default activation trigger for dialog boxes with this attribute.
GIA_MODAL
This attribute specifies that while this dialog is up, all other input to the application should be ignored. This attribute will create an application modal dialog box.
GIA_SYS_MODAL
This attribute specifies that while this dialog is up, all other input to the system (including other applications) should be ignored. This attribute will create a system modal dialog box.

You may retrieve or set the GII_attrs of any Interaction at run-time. To retrieve the GenInteractionAttrs , send the Interaction a MSG_GEN_INTERACTION_GET_ATTRS . You can set the GenInteractionAttrs of any Interaction by sending it a MSG_GEN_INTERACTION_SET_ATTRS . Make sure that any Interaction you set these attributes for is not currently GS_USABLE or an error will result.

If your application contains several dialog boxes sitting directly under a GenPrimary (typically with their GIA_NOT_USER_INITIATABLE attribute), the application may take longer to start up. This happens because the system will have to process each of the dialogs individually and determine at that point that a trigger should not be created for them.

You may instead wish to create an organizational Interaction to manage these non-user initiatable Interactions. To do this, create a single GIT_ORGANIZATIONAL GIA_NOT_USER_INITIATABLE dialog box under the GenPrimary. All "real" dialogs should then be placed as children of this "holding" Interaction. When the application starts up, it only needs to process this one Interaction.

Grouping dialogs in this way also allows easier initiation and dismissal of the dialogs all at once. Simply encapsulate MSG_GEN_INTERACTION_INITIATE or MSG_GEN_GUP_INTERACTION_COMMAND with IC_DISMISS and use MSG_GEN_SEND_TO_CHILDREN on the one Interaction.

MSG_GEN_INTERACTION_GET_ATTRS
byte	MSG_GEN_INTERACTION_GET_ATTRS();

This message retrieves the current GII_attrs instance field from the Interaction sent the message.

Source: Unrestricted.

Destination: Any GenInteraction object.

Parameters: None.

Return: GenInteractionAttrs of the Interaction.

Interception: Generally not intercepted.

MSG_GEN_INTERACTION_SET_ATTRS
void	MSG_GEN_INTERACTION_SET_ATTRS(
        byte	setAttrs,
        byte	clearAttrs);

This message sets the GII_attrs instance data for the GenInteraction sent the message. The Interaction must not be GS_USABLE when receiving this message. The new attributes will take effect when the Interaction is next made GS_USABLE.

Source: Unrestricted.

Destination: Any non-usable GenInteraction object.

Parameters: setAttrs GenInteractionAttrs to set.

clearAttrs
GenInteractionAttrs to clear.

Return: Nothing.

Interception: Generally not intercepted.

Warnings: Make sure that the object is not GS_USABLE when sending it this message.

Menus and Dialog Boxes: 3 GenInteraction Usage

You will probably need to create GenInteractions at various points within your application. The GenInteraction object is one of the most flexible generic objects within GEOS. Not only can an Interaction operate as a menu, dialog, or sub-group, but there also exist several pre-defined dialog boxes with default triggers for your use. These dialogs will make writing your application easier and faster. The simplest way to see the wide variety of options available to you is to examine the different types of Interactions available with different instance data values.

Visibilities

Types

Menus and Dialog Boxes: 3.1 GenInteraction Usage: Visibilities

When first creating an Interaction, you must decide in what form to visually present your communication between the application and the user. This concern may be somewhat cosmetic in form but may also affect the functionality of the Interaction itself. For instance, more than one GenInteractionType will support different actions with different visibility types.

GenInteraction Sub-Groups (GIV_SUB_GROUP)

The simplest GenInteraction is one used to arrange the geometry of other objects. To do this, a GenInteraction should be GIV_SUB_GROUP (the default).

You can use hints provided in GenClass to arrange the appearance of objects within the GenInteraction. Interactions usually provide the greatest flexibility for geometry manipulation. For full information, see the Managing UI Geometry chapter.

Note that a GIV_SUB_GROUP Interaction may only appear within a parent window as it is not itself independently displayable.

Code Display 7-4 Arranging a Group of Children

/* This Interaction creates a typical number keypad arrangement. */
@object GenInteractionClass MyKeyPad = {
    /* The Interaction by default is GIV_SUB_GROUP and GIT_ORGANIZATIONAL,
     * so there is no need to set these types. */
    GI_comp = Row789, Row456, Row123;
    /* The Interaction's three children will be arranged vertically. The
     * UI will also draw a border around the Interaction itself. */
    HINT_ORIENT_CHILDREN_VERTICALLY;
    HINT_DRAW_IN_BOX;
}
@object GenInteractionClass Row789= {
    GI_comp = Key7, Key8, Key9;
    /* Each row is then arranged horizontally to gain three rows
     * of three items each. */
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
@object GenInteractionClass Row456= {
    GI_comp = Key4, Key5, Key6;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
@object GenInteractionClass Row123= {
    GI_comp = Key1, Key2, Key3;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
/* Each of the child `keys' will be a GenTrigger with appropriate visual
 * moniker. (For simplicity, only one trigger is shown here.) */
@object GenTriggerClass Key9 = {
    GI_visMoniker = "9";
}

Many hints from GenClass may affect the appearance of GIV_SUB_GROUP Interactions. See the Managing UI Geometry chapter for details.

Of special interest to sub-group Interactions is HINT_DRAW_IN_BOX . This hint will enclose the associated Interaction within a line border. You should use this hint when you wish to distinguish a collection of objects within an Interaction from some other objects.

GenInteractions as Menus (GIV_POPUP)

Most specific UIs implement GIV_POPUP as a menu. Menus are interactions with options that only appear when the menu is activated by the user. Popups make it possible for a user to browse and choose among a variety of commands without cluttering up the UI with dozens of objects. A popup may appear at almost any location within the UI. If a popup is placed as a direct child of a GenPrimary, it may appear in an appropriate menu bar location across the top of the window.

A popup is most often one of GIT_ORGANIZATIONAL, GIT_PROPERTIES, or GIT_COMMAND. (See GenInteraction Types .) What type of popup you select depends on the type of children the Interaction contains. For example, an application may implement a font menu as a GIT_PROPERTIES popup. A GIT_COMMAND popup might issue commands relating to the appearance of text within a text editor. Depending on the GenInteractionType , a popup may appear in forms other than a menu. (The specific UI has final say over the appearance of a popup.)

A GIV_POPUP Interaction must contain a group of children. If the Interaction appears as a menu, these children will become menu items. Most often, these children will be GenTriggers or GenItems within GenItemGroups.

Code Display 7-5 Using a GenInteraction to Create a Menu

/* This example is taken from the Hello World sample application. */
@object GenInteractionClass HelloMenu = {
    GI_visMoniker = 'M', "Menu";					/* The moniker of the menu is used in
                     * the primary window's menu bar (if the
                     * specific UI employs a menu bar). */
    GI_comp = @HelloColorBox;					/* The only child of the menu (the only
                     * item in the menu) is the dialog box. */
    GII_visibility = GIV_POPUP;					/* This attribute designates the
                     * interaction as a menu or a sub-menu. */
}
@object GenInteractionClass HelloColorBox = {
    GI_comp = @HelloBlueTrigger, @HelloGoldTrigger;
    GI_visMoniker = 'C', "Color";
    GII_visibility = GIV_DIALOG;
}
@object GenTriggerClass HelloBlueTrigger = {
    GI_visMoniker = 'B', "Blue";				/* The 'B' indicates the keyboard navigation
                 * character for this trigger. */
    GTI_destination = process;				/* Send the message to the Process object. */
    GTI_actionMsg = MSG_HELLO_CHANGE_TO_BLUE;						/* Send this message. */
}
@object GenTriggerClass HelloGoldTrigger = {
    GI_visMoniker = 'G', "Gold";				/* The 'G' indicates the keyboard navigation
                 * character for this trigger. */
    GTI_destination = process;				/* Send the message to the Process object. */
    GTI_actionMsg = MSG_HELLO_CHANGE_TO_GOLD;						/* Send this message. */
}

If a child of the GIV_POPUP Interaction is another Interaction, the specific UI might change the visual implementation of that child. What change takes place depends on the specific UI. In OSF/Motif, for example, placing a sub-group Interaction within a popup Interaction causes the UI to draw lines separating the sub-group from the other children in the popup. (You may prevent this line by adding HINT_SAME_CATEGORY_AS_PARENT .)

Placing a popup interaction within another popup is a special case and will create nested menus under most specific UIs. To do this, merely create a GIV_POPUP Interaction as a child of another GIV_POPUP Interaction. The specific UI will automatically create the second menu as a sub-menu of the first. The item in the menu bringing up the sub-menu may be labeled with an arrow under certain specific UIs. Menus may be nested to an arbitrary number of levels.

Code Display 7-6 Nested Menus

@object GenInteractionClass TextMenu = {
    GI_comp = @JustificationSubMenu;
    GI_visMoniker = `T', "Text";
    GII_visibility = GIV_POPUP;
}
@object GenInteractionClass JustificationMenu = {
    GI_comp = @JustificationList;
    GI_visMoniker = `J', "Justification";
    GII_visibility = GIV_POPUP;
}
@object GenItemGroupClass JustificationList = {
    GI_comp = @LeftEntry, @RightEntry, @CenterEntry, @FullEntry;
    /* These children are not shown. */
}

List Objects (GenItemGroups and GenBooleanGroups) within a GIV_POPUP Interaction may be used to show object properties in a menu. The menu should be GIT_PROPERTIES in this case. Depending on the Specific UI, a properties menu may be implemented in a different fashion from other menus. (In OSF/Motif, there is no visual difference between a properties menu and other menus.) Other types have no effect on the behavior or appearance of GIV_POPUPs.

GenInteractions as Dialog Boxes (GIV_DIALOG)

The GIV_DIALOG visibility will instruct the specific UI to build the Interaction as a dialog box. A dialog box is an independently-displayable interface element used to display information or other UI objects. Therefore, it is similar to a window although there are some differences. (Most dialog boxes, for example, are not resizable whereas most windows are.)

Dialog boxes also enhance the functionality of other GenInteractionType s. Many GII _types are only significant as dialog boxes. For example, a GIT_NOTIFICATION Interaction will only appear with an "OK" trigger if it is also a dialog box. A GIT_PROPERTIES Interaction may contain properties within any visibility, but only if built as a dialog can it implement delayed mode. (If a GIT_PROPERTIES Interaction is built as a menu or sub-group, it will always operate in immediate mode.) Delayed mode allows the user to change the properties of an object and later apply those changes all at once. Within a dialog box, an "Apply" trigger will make these changes.

Most specific UIs will build a dialog box with an activation trigger present in the UI that brings up the dialog box. These dialog boxes are known as user initiatable. There are ways to override this behavior if you need a non-user initiatable dialog box or a dialog box that blocks the calling thread until answered (forcing the user to respond to the dialog box immediately). See Modality for Dialogs .

By default, dialog boxes are not resizable. HINT_INTERACTION_MAKE_RESIZABLE may be used on a non-modal dialog box to make that dialog resizable. In OSF/Motif, the Specific UI will supply such a dialog with a means to alter its shape. HINT_INTERACTION_MAKE_RESIZABLE is only meaningful for GIV_DIALOG Interactions.

GenInteractions as Popouts (GIV_POPOUTS)

MSG_GEN_INTERACTION_POP_IN, MSG_GEN_INTERACTION_POP_OUT, MSG_GEN_INTERACTION_TOGGLE_POPOUT

Popouts are special GenInteractions that may act as either sub-group Interactions or as dialog boxes, depending on their state. Usually, popouts act as normal sub-group Interactions until the user or the application "pops" them out into a dialog box. Popouts are normally "popped out" into their dialog box state by double-clicking within the confines of the GenInteraction. This popout state is reflected with either the presence or absence of the ATTR_GEN_INTERACTION_POPPED_OUT vardata entry.

You can pop out a GIV_POPOUT GenInteraction in its sub-group state by sending it MSG_GEN_INTERACTION_POP_OUT. Similarly, you can pop a GIV_POPOUT back into its sub-group state from the dialog box state by sending it MSG_GEN_INTERACTION_POP_IN. MSG_GEN_INTERACTION_TOGGLE_POPOUT toggles a popout from whatever state it currently is into its other state.

By default, popouts in their dialog box state contain a special trigger to pop them back into their sub-group state. They may also be popped back in by double-clicking within the popped-out GenInteraction. Popouts also normally provide a close trigger; this trigger dismisses the dialog box but does not return the popout to its sub-group state. You should provide a trigger somewhere within your UI that sends MSG_GEN_INTERACTION_POP_IN or MSG_GEN_INTERACTION_POP_OUT to the popout, so that the user can bring any popout they dismiss back on-screen. Alternatively, you can add ATTR_GEN_INTERACTION_POPOUT_NOT_CLOSABLE on the Popout to prevent the creation of a "Close" trigger.

MSG_GEN_INTERACTION_POP_IN
void	MSG_GEN_INTERACTION_POP_IN();

This message `pops in' a GIV_POPOUT GenInteraction into its default sub-group state. This message has no effect if the GenInteraction is not a GIV_POPOUT or if the GenInteraction is already `popped in.'

Parameters: Nothing.

Return: Nothing.

MSG_GEN_INTERACTION_POP_OUT
void	MSG_GEN_INTERACTION_POP_OUT();

This message `pops out' a GIV_POPOUT GenInteraction into its dialog box state. This message has no effect if the GenInteraction is not a GIV_POPOUT or if the GenInteraction is already `popped out.'

Parameters: Nothing.

Return: Nothing.

MSG_GEN_INTERACTION_TOGGLE_POPOUT
void	MSG_GEN_INTERACTION_TOGGLE_POPOUT();

This message toggles the state of a GIV_POPOUT GenInteraction from its `popped in' state to its `popped out' state (or `popped out' and hidden from view) and vice-versa. The specific UI has control over how this message is implemented.

Parameters: Nothing.

Return: Nothing.

Menus and Dialog Boxes: 3.2 GenInteraction Usage: Types

Possibly more important than how an Interaction is visually displayed is what role that Interaction should play within your UI. That role depends in part on what children the Interaction contains. What the Interaction does with those children is determined by the GenInteractionType of that object. Interactions of different visibility may function in essentially the same manner, but Interactions of different types may differ greatly in their functionality.

For example, if an Interaction contains a GenItemGroup with three GenItem children, it will not affect the basic functionality if that Interaction is built as a dialog, popup, or sub-group. Each item will still perform its default function (such as sending a message). The GII _type of Interaction might affect that functionality, however, by forcing the Items to send out their messages as a group or individually (for example in a GIT_PROPERTIES Interaction).

You should pick the GenInteractionType based on what actions you wish to perform on the children you select for the Interaction. By default, an Interaction is of type GIT_ORGANIZATIONAL. In some cases, the functionality you need will reside entirely with the children; in those cases, a GIT_ORGANIZATIONAL will most likely suffice.

Certain GenInteractionType Interactions, if built as GIV_DIALOG, may create a reply bar with standard response triggers . A reply bar is a special area of a dialog Interaction to place commands for user responses. Standard response triggers perform additional actions for dialog Interactions, apart from any functionality your application provides. You do not need to add these triggers yourself; they are added by the Specific UI.

Standard response triggers perform one of several pre-defined InteractionCommand commands. Depending on the particular type of Interaction, each command may perform different actions. An InteractionCommand is a special data type that the Specific UI recognizes. The Specific UI decides what action to take upon receiving this command; depending on the value of the InteractionCommand , the Specific UI may perform several different actions.

For example, the GIT_NOTIFICATION type, if built as a dialog box, may create an "OK" trigger automatically. The trigger will become one of the dialog box's children. This trigger sends out the InteractionCommand IC_OK when activated. The Specific UI will also decide how to implement that IC_OK message. (In OSF/Motif, for example, IC_OK will dismiss the dialog box as one of its actions.)

You can create your own response triggers within an Interaction. You can also replace any standard response trigger by supplying one of your own. For complete information on how InteractionCommand s and response triggers work, see Interaction Commands .

Organizational Interactions

An organizational interaction is the simplest type of interaction. It is also the default GenInteractionType . An organizational Interaction only functions to contain objects--without indicating any meaning to the makeup of those objects. Because of this, a GIT_ORGANIZATIONAL Interaction should not interpret any InteractionCommand types. See Interaction Commands .

GIT_ORGANIZATIONAL Interactions are only used, therefore, to group their children; there is never any implied meaning to the children they contain. The Interaction may have a certain visual form defined in its GenInteractionVisibility field, but presumably the Interaction should perform its activity under any visible arrangement.

You will most often need an organizational Interaction to arrange objects within another window, whether that "window" is a GenPrimary or a GIV_DIALOG GenInteraction. For example, you may need organizational Interactions solely to arrange a group of generic children in a horizontal or vertical row. There are several hints in GenClass that may affect the visual implementation of an organizational Interaction. See the Managing UI Geometry chapterfor full details.

Organizational Interactions may appear as sub-groups, menus, or dialog boxes; each of its items should already contain all the functionality needed. For example, an edit menu (GIV_POPUP Interaction) with three triggers (Cut, Copy, and Paste) could be organizational if each of those triggers is capable of performing its action purely through the sending of a message (a function of GenTriggerClass ).

No standard response triggers are provided with a GIT_ORGANIZATIONAL Interaction, even if it is built as a dialog box, though you may supply your own. See Standard Response Triggers .

Code Display 7-7 Organizational Interactions

/* This properties dialog box will group a collection of GIT_ORGANIZATIONAL
 * Interactions. */
@object GenInteractionClass MyMasterInteraction = {
    GI_comp = FirstOrganizational, SecondOrganizational;
    GII_visibility = GIV_DIALOG;
    GII_type = GIT_PROPERTIES;
}
@object GenInteractionClass FirstOrganizational = {
/* GenInteractions are GIT_ORGANIZATIONAL by default. */
    GI_comp = One, Two, Three;
}
@object GenInteractionClass SecondOrganizational = {
    GI_comp = Four, Five, Six;
}
@object GenTriggerClass One = {
/* This trigger, after sending out MY_SPECIAL_MESSAGE to the process, will send a
 * MSG_GEN_GUP_INTERACTION_COMMAND with IC_INTERACTION_COMPLETE to the first
 * non-GIT_ORGANIZATIONAL Interaction it encounters up the tree
 * (MyMasterInteraction). This behavior is due to the GenAttribute
 * GA_SIGNAL_INTERACTION_COMPLETE. (See GenClass.) */
    GTI_actionMsg = MY_SPECIAL_MESSAGE;
    GTI_destination = process;
    GI_attrs = GA_SIGNAL_INTERACTION_COMPLETE;
}

Properties Interactions

GIT_PROPERTIES Interactions allow the Interaction to display and set attributes of a specific selected object (the "current selection"). In many cases, this current selection will be the target of the application. (See the Input chapter.) Users can change the UI gadgets in the properties group to change the attributes of the selected object. When the user selects a different object, the UI gadgetry changes to reflect the attributes of the new selection.

For example, a properties dialog may alter text properties within a text editor. If some text is selected, changing properties in the text dialog will change the appearance of the text.

Properties Interactions may appear as sub-groups, popups, or dialog boxes. If a properties Interaction takes the form of a dialog box, the Specific UI may create response triggers with the InteractionCommand types IC_APPLY, IC_RESET and IC_DISMISS. These triggers will either apply changes made in the properties group, reset those changes to their initial state, or close the dialog, respectively. In OSF/Motif these triggers are labeled "Apply" or "OK," "Reset," and "Close" or "Cancel."

Code Display 7-8 GenInteraction as Properties Dialog Box

@object GenInteractionClass MyInteraction = {
    GI_visMoniker = "Dialog Box";
    GI_comp = PropertiesList;
    GII_visibility = GIV_DIALOG;
    GII_type = GIT_PROPERTIES;
}
/* The GenItemGroup below is a list of three properties. */
@object GenItemGroupClass PropertiesList = {
    GI_visMoniker = "Properties List";
    GI_comp = @PropOne, @PropTwo, @PropThree;
    GIGI_behaviorType = GIGBT_NON_EXCLUSIVE;
}
@object GenItemClass PropOne = {
    GI_visMoniker = "A";
    GII_identifier = 1;
}
@object GenItemClass PropTwo = {
    GI_visMoniker = "B";
    GII_identifier = 2;
}
@object GenItemClass PropThree = {
    GI_visMoniker = "C";
    GII_identifier = 3;
}

There are two ways that an application can deal with UI gadgetry in a property dialog. Either the application can work completely off the state change notification messages sent by the UI gadgetry when the properties are changed, or the application may need to do some extra work after the properties are changed. This extra work can include checking for valid settings in the UI gadgetry, querying the UI gadgetry for their states, or dealing with the UI gadgetry as a group rather than individually (i.e. fetching each of their states and using them all at once to make a single change instead of making separate changes for each state).

Delayed and Immediate Mode

Properties dialogs work in one of two modes: immediate and delayed. In immediate mode, the UI gadgetry within a properties box reflects the actual state of those objects. Changing selections within the properties Interaction changes the state of those objects immediately. For example, if a "Fonts" dialog box containing a list of font selections is in immediate mode, selecting each separate font will cause the selected text to immediately display itself in the selected font.

In delayed mode, the UI gadgetry within the properties box reflects a separate "user" state rather than the actual state. The user is free to change settings within the properties Interaction without causing such changes to be made on the object immediately. The UI does this by storing two states for the UI gadgetry: the actual state (which reflects the current state of the objects) and a user state (which reflects the state specified in the dialog box). The user can apply changes made within a delayed mode properties Interaction with use of an "Apply" mechanism. Usually, the Specific UI will provide an "Apply" trigger for such a case.

If either the Specific UI or hints determine that the properties dialog box should be run in delayed mode, an apply trigger will be supplied. In OSF/Motif, an "Apply" trigger will be placed in a reply bar along with a "Close" trigger that dismisses the property box.

Properties Hints

The GIT_PROPERTIES type and GIV_DIALOG visibility indicate to the Specific UI to build a property dialog box. The Specific UI will also determine whether this box should run in immediate or delayed mode. Because the application works completely through its UI gadgets within the dialog, it will support either mode. However, there are good reasons for wanting the properties to work in one mode or another. You can supply additional hints for this purpose.

These hints only affect the behavior of properties dialog boxes (GIV_DIALOG). Therefore, if an Interaction is a GIV_POPUP or GIV_SUB_GROUP, these hints will have no effect. If the Interaction is a GIV_CONTROL_GROUP or GIV_NO_PREFERENCE, these hints may force the object to appear as a dialog box.

HINT_INTERACTION_COMPLEX_PROPERTIES indicates that the properties dialog box contains complex properties and therefore a means to reset the properties to their initial state should be supplied if available. In OSF/Motif, for example, a "Reset" response trigger is supplied.

HINT_INTERACTION_SIMPLE_PROPERTIES indicates that the properties dialog box contains simple properties and therefore a means to reset the properties to their initial state is probably unneeded.

HINT_INTERACTION_RELATED_PROPERTIES indicates that the properties dialog contains properties that are closely related. Therefore, the specific UI should run the objects as a group in delayed mode and provide an "Apply" trigger. This might be useful if you set several graphics area properties (such as color, texture, and dithering) and only wish to send out the changes after the final selection has been made for all the properties.

HINT_INTERACTION_UNRELATED_PROPERTIES indicates that the properties dialog contains unrelated properties that may be set individually. Therefore, the Interaction can be run in immediate mode and no "Apply" trigger is needed.

HINT_INTERACTION_SLOW_RESPONSE_PROPERTIES indicates that the properties dialog contains properties that may take a long time to be reflected in the state of the object. The specific UI may create an "Apply" trigger operating in delayed mode so that the user can set these attributes all at once.

HINT_INTERACTION_FAST_RESPONSE_PROPERTIES indicates that this properties dialog contains properties in which changes can be quickly reflected. The Interaction will be run in immediate mode without any "Apply" and "Reset" triggers.

Modifying Triggers for Custom Properties
HINT_INTERACTION_REQUIRES_VALIDATION

If an application needs to do additional work on a properties dialog box, such as validation of the attributes, you should define a "custom" properties dialog box. You can do this by replacing the default "Apply" and "Reset" triggers with ones of your own design.

HINT_INTERACTION_REQUIRES_VALIDATION indicates that this properties dialog should prompt the user for verification before implementing changes to the properties group. The Interaction will thus be implemented in delayed mode while also allowing a custom "Apply" trigger. The trigger action should be handled in the normal fashion to add any additional functionality to validate before proceeding with the apply.

You can modify the standard IC_APPLY trigger by adding your own apply trigger with ATTR_GEN_TRIGGER_INTERACTION_COMMAND set to IC_APPLY. This trigger will replace the default IC_APPLY trigger that the Specific UI would have supplied otherwise. HINT_SEEK_REPLY_BAR places the trigger in the property dialog's reply bar. In the GenInteraction itself, you might want to include HINT_INTERACTION_REQUIRES_VALIDATION or another more appropriate hint to ensure that the properties dialog will be working in delayed mode.

An application-supplied trigger allows the application to define whatever action output and method it desires. When the user activates this trigger, the action message will be sent out to the action output (as usual for GenTriggers); the default IC_APPLY functionality will not be performed. In the handler for this custom message, the application can do whatever is necessary with the properties dialog (such as validation of the attributes).

When finished, the application can either manually fetch the state of the attributes or send MSG_GEN_GUP_INTERACTION_COMMAND with IC_APPLY to the GenInteraction. (This is the default behavior of an IC_APPLY InteractionCommand .) You may choose not to send out this message if you wish to replace instead of supplement the default behavior.

Finally, you can send MSG_GEN_GUP_INTERACTION_COMMAND with IC_INTERACTION_COMPLETE to notify the GenInteraction that the user has finished a single usage of the Interaction. (You may also mark such a trigger with GA_SIGNAL_INTERACTION_COMPLETE.) Upon this notification, the specific UI will decide if the GenInteraction should be dismissed. Instead, you may send IC_DISMISS to unconditionally dismiss the Interaction.

Code Display 7-9 Replacing the Default IC_APPLY Trigger

@object GenInteraction LineProperties = {
    GI_visMoniker = "Line Properties";
    GI_comp = LineWidth, LineStyle, LinePropertiesApply;
    GII_type = GIT_PROPERTIES;
    GII_visibility = GIV_DIALOG;
    HINT_INTERACTION_REQUIRES_VALIDATION;						/* Forces delayed mode. */
}
@object GenItemGroupClass LineWidth = {
    /* ... */
}
@object GenItemGroupClass LineStyle = {
    /* ... */
}
@object GenTriggerClass LinePropertiesApply = {
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_PROPERTIES_APPLY;
    /* Setting GA_SIGNAL_INTERACTION_COMPLETE will dismiss the dialog
     * after applying the properties. */
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    /* Setting this attribute to IC_APPLY tells the specific UI to replace
    * the standard response trigger with this one. */
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_APPLY };
    HINT_SEEK_REPLY_BAR;
}

The moniker for an application-supplied apply trigger may be set by the application or (preferably) left blank so that the Specific UI can provide an appropriate moniker. The Specific UI uses the IC_APPLY data in the ATTR_GEN_TRIGGER_INTERACTION_COMMAND vardata and any properties hints to determine what moniker to use. (In OSF/Motif this is either "Apply" or "OK.")

If the application wants to alter a moniker but does not need to do any other work, the trigger's output fields should be left blank. Because the trigger has ATTR_GEN_TRIGGER_INTERACTION_COMMAND , if the output fields are null, the trigger will send MSG_GEN_GUP_INTERACTION_COMMAND with the ATTR_GEN_TRIGGER_INTERACTION_COMMAND data to itself. This message will travel up to the appropriate properties GenInteraction where it will be handled, as usual. If doing this, you should make sure to mark any replacement triggers GA_SIGNAL_INTERACTION_COMPLETE so that the Specific UI can determine whether to dismiss the dialog or not when the trigger is activated. (See Replacing the Default IC_APPLY moniker .)

Code Display 7-10 Replacing the Default IC_APPLY moniker

@object GenTriggerClass LinePropertiesApply = {
    GI_visMoniker = "Set Line Properties";
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_APPLY };
    HINT_SEEK_REPLY_BAR;
}

An alternate way to supplement an apply trigger's default behavior is to subclass GenInteractionClass and intercept MSG_GEN_GUP_INTERACTION_COMMAND . If the InteractionCommand sent is IC_APPLY, the application's supplemental apply work may then be done. Then call the superclass to finish with the default apply behavior.

Note that this method cannot be used to override the default GA_SIGNAL_INTERACTION_COMPLETE behavior as that is done separately with MSG_GEN_GUP_INTERACTION_COMMAND and IC_INTERACTION_COMPLETE.

For either default or custom properties dialogs, if the properties dialog contains a large number of attributes, it may be useful to provide a means to reset the properties to their former state. This is often done with a hint such as HINT_INTERACTION_COMPLEX_PROPERTIES .

Just as you may use ATTR_GEN_TRIGGER_INTERACTION_COMMAND with IC_APPLY to provide a custom apply trigger, you may use ATTR_GEN_TRIGGER_INTERACTION_COMMAND with IC_RESET to provide a custom reset trigger. This is useful if the application needs to do some additional work when the user resets the attributes or if the application wishes to override the default reset moniker ("Reset" in OSF/Motif). You may still perform the default reset behavior (sending MSG_GEN_RESET to the UI objects) by sending MSG_GEN_GUP_INTERACTION_COMMAND with IC_RESET to the properties Interaction.

Command Interactions

GIT_COMMAND Interactions allow the user to set up parameters for and issue commands. For example, a "rename file" command in a file manager application needs to request the new name for the file from the user. This can be done with a command dialog that shows the file's current name and has a text entry field for the file's new name. A reply bar with a "Rename" action trigger could be provided to initiate the rename action when the user has finished entering the new name.

Command Interactions can appear as menus, dialogs, or sub-groups within a larger window. If the Interaction is displayed as a dialog, the specific UI may provide a response trigger with the IC_DISMISS InteractionCommand that closes the dialog without executing any commands. In OSF/Motif, this trigger is labeled "Close" or "Cancel." You should supply your own additional command triggers yourself. You may also add custom response triggers with ATTR_GEN_TRIGGER_INTERACTION_COMMAND set to a custom InteractionCommand

Command dialogs provide applications with a place to group related commands. They also provide a convenient place to locate UI gadgetry needed to set up parameters used by commands. A command dialog usually contains one or more command triggers in the dialog reply bar. The Specific UI will usually create a trigger to close the dialog box (with IC_DISMISS) but will not supply any command triggers; you must set up any command triggers or custom response triggers yourself.

Code Display 7-11 Using a Command Dialog Box

/* The Command Dialog created below contains one command trigger (RenameTrigger)
 * which sends MSG_MY_PROCESS_RENAME to the process object. */
@object GenInteractionClass RenameBox = {
    GI_visMoniker = "Rename";
    GI_comp = @RenameSource, @RenameDest, @RenameTrigger;
    GII_type = GIT_COMMAND;
    GII_visibility = GIV_DIALOG;
}
@object GenTextClass RenameSource = {
    /* Single-line text entry object */
}
@object GenTextClass RenameDest = {
    /* Single-line text entry object */
}
@object GenTriggerClass RenameTrigger = {
    GI_visMoniker = "Rename";
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_RENAME;
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    HINT_SEEK_REPLY_BAR;
}

The Rename trigger in the above example is marked HINT_SEEK_REPLY_BAR to place it in the reply bar of the Interaction, though this is not necessary. The GA_SIGNAL_INTERACTION_COMPLETE attribute on the command trigger indicates that when the user activates this trigger, their use of the dialog is complete and the Specific UI may dismiss the dialog, if it desires. By default, non-modal command dialogs in OSF/Motif are not dismissed. You may use hints to override this behavior.

You may wish to modify this closing behavior, however. For example, if the user chooses an invalid destination name, you might not want to close that dialog; instead, the dialog should remain up and the user should be alerted that he has chosen an incorrect destination. In this case, you should remove the GA_SIGNAL_INTERACTION_COMPLETE attribute from the trigger. You should then handle the closing behavior, after validating the destination, in the MSG_MY_PROCESS_RENAME handler. To dismiss the command dialog, send MSG_GEN_GUP_INTERACTION_COMMAND with IC_DISMISS to it. (You may also use IC_INTERACTION_COMPLETE if you wish to defer to the Specific UI whether to close the dialog box.)

The Specific UI will also create a standard response trigger to dismiss the command dialog box. If some action needs to take place when the user attempts to close this dialog box, you should replace the default Close trigger by adding your own trigger to replace the IC_DISMISS InteractionCommand . As with IC_APPLY and IC_RESET, use ATTR_GEN_TRIGGER_INTERACTION_COMMAND with IC_DISMISS to replace the default trigger. You should also mark this trigger HINT_SEEK_REPLY_BAR if you wish it to appear in the Interaction's reply bar.

Code Display 7-12 Replacing the Default Close Trigger

@object GenInteractionClass RenameBox = {
    GI_visMoniker = "Rename";
    GI_comp = RenameSource, RenameDest, RenameTrigger, RenameClose;
    GII_type = GIT_COMMAND;
    GII_visibility = GIV_DIALOG;
}
@object GenTextClass RenameSource = {
    /* Single-line text entry object */
}
@object GenTextClass RenameDest = {
    /* Single-line text entry object */
}
@object GenTriggerClass RenameTrigger = {
    GI_visMoniker = "Rename";
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_RENAME;
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    HINT_SEEK_REPLY_BAR;
}
@object GenTriggerClass RenameClose = {
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_CLOSE_RENAME;
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_DISMISS };
    HINT_SEEK_REPLY_BAR;
}

Progress Interactions

GIT_PROGRESS Interactions report the progress of an operation. You will need progress dialogs most often to show the status of an ongoing operation. Progress Interactions may appear as either a dialog or as part of a larger window. (Typically progress reports should not appear within a popup, as popups will not remain on-screen.)

If a progress Interaction takes the form of a dialog box, the Specific UI may create a response trigger with the InteractionCommand IC_STOP. This trigger will halt the operation (and therefore the progress report) and may close the dialog box if the Specific UI or the application desires this. In OSF/Motif this trigger is labeled "Stop." The Specific UI will not supply a default way to close the dialog other than this IC_STOP trigger. In OSF/Motif this means that a system menu for a non-modal progress dialog will have its "Close" trigger disabled.

For example, you can use a progress dialog to report the progress of a disk format. Gadgets within the progress dialog might reflect the current state of the disk format (for example, a bar indicating the time-percentage of the operation completed). A "Stop" trigger in the reply bar allows the user to abort the disk format. Your application should intercept the InteractionCommand IC_STOP in this case so that you can perform any actions required in the halting operation.

A progress dialog may be either modal or non-modal, depending on the application and the operation in progress. The GenInteractionType GIT_PROGRESS is designed for cases where the application supports stopping an in-progress operation.

While the Specific UI will supply an IC_STOP trigger in the reply bar for halting the on-going operation, the trigger by itself does nothing. You must either replace this IC_STOP trigger with one that performs the required halting operation, or you must provide a subclass of GenInteraction to intercept MSG_GEN_GUP_INTERACTION_COMMAND with IC_STOP. Providing a replacement IC_STOP trigger is the easiest method.

Code Display 7-13 Replacing the Default IC_STOP Trigger

/* This Progress Dialog shows the progress of a data transfer. Its first child
 * shows the number of packets sent in the operation, and the second child is an
 * IC_STOP trigger to halt the transfer operation. */
@object GenInteractionClass FileTransferProgress = {
    GI_visMoniker = "File Transfer Status";
    GI_comp = FileTransferPktCount, FileTransferStop;
    GII_visibility = GIV_DIALOG;
    GII_type = GIT_PROGRESS;
}
/* The GenText object shows the title "Packets Sent" and updates the GTI_text field
 * with the current number of packets sent. */
@object GenTextClass FileTransferPktCount = {
    GI_visMoniker = "Packets sent:";
    GTXI_text = "0";
}
/* When the user activates the "Abort File Transfer" trigger, the trigger sends out
 * MSG_MY_PROCESS_ABORT_FILE_TRANSFER to the process. This action should also
 * close the dialog box. Since this trigger may initiate a destructive action,
 * HINT_TRIGGER_DESTRUCTIVE_ACTION makes sure that the focus does not lie on the
 * dialog box (and therefore the trigger cannot be activated by a default
 * activation method such as hitting RETURN). */
@object GenTriggerClass FileTransferStop = {
    GI_visMoniker = "Abort File Transfer";
    /* OSF/Motif will dismiss this dialog box if marked
     * GA_SIGNAL_INTERACTION_COMPLETE because it is GIT_PROGRESS. */
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    GTI_destination = process;
    /* The handler for MSG_MY_PROCESS_ABORT_FILE_TRANSFER must stop the
     * operation. You may also dismiss the dialog at that time. */
    GTI_actionMsg = MSG_MY_PROCESS_ABORT_FILE_TRANSFER;
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_STOP };
    HINT_SEEK_REPLY_BAR;
    HINT_TRIGGER_DESTRUCTIVE_ACTION;					/* Don't place focus here. */
}

Notification Interactions

GIT_NOTIFICATION Interactions allow your application to send notices to the user. You should use a notification Interaction when you wish to notify the user of an event without needing to prompt the user for further information.

GIT_NOTIFICATION interactions normally appear as dialogs. If so, the Specific UI will create a response trigger with the InteractionCommand IC_OK. This command will close the dialog box in most Specific UIs. In OSF/Motif, this trigger is labeled "OK." The Specific UI will not supply a way to close the dialog without responding with this trigger. In OSF/Motif, this means that the system menu in a non-modal notification dialog will have its "Close" trigger disabled.

A notification Interaction may in theory be of any visibility, but it usually takes the form of a modal dialog box. Therefore, the user should only need to acknowledge the dialog box (usually by activating the "OK" trigger).

You will usually require a notification dialog when you need to let the user know that something has occurred that may or may not require his immediate attention. For example, a notification dialog would be appropriate to notify the user that new e-mail has arrived; a notification dialog would also be appropriate to notify the user that an error has occurred.

Notifications that require immediate attention should be marked GIA_MODAL or HINT_INTERACTION_MODAL , depending on the application's dependence on their modality. Interactions that do not require immediate attention may be marked HINT_INTERACTION_NO_DISTURB so that when brought up, they do not disturb the focus or target of the application. This is especially useful for cases such as e-mail notification, where switching the focus away from another application at each new notice might become irritating to the user.

Code Display 7-14 An E-mail Notification Dialog

/* The Notification dialog is marked HINT_INTERACTION_NO_DISTURB so that it
 * will not grab the focus and target. The notification itself has only one child:
 * the text to display to the user. The Specific UI will also create an IC_OK
 * trigger that is used to acknowledge and dismiss the notification. */
@object GenInteractionClass NewMailNotice = {
    GI_visMoniker = "Mailbox";
    GI_comp = NewMailText;
    GII_type = GIT_NOTIFICATION;
    GII_visibility = GIV_DIALOG;
    HINT_INTERACTION_NO_DISTURB;
}
@object GenGlyphClass NewMailText = {
    GI_visMoniker = "New mail has arrived";
}

By default the IC_OK trigger will just dismiss the dialog. If some other action might need to be taken when the user acknowledges the notification, you should provide a custom IC_OK trigger to replace the specific UI supplied one.

Code Display 7-15 Replacing the IC_OK Trigger

/* This Notification dialog now contains two explicit children: the text to
 * display to the user and a custom trigger to acknowledge the notification. */
@object GenInteractionClass NewMailNotice = {
    GI_visMoniker = "Mailbox";
    GI_comp = NewMailText, NewMailAcknowledge;
    GII_type = GIT_NOTIFICATION;
    GII_visibility = GIV_DIALOG;
    HINT_INTERACTION_NO_DISTURB;
}
@object GenGlyphClass NewMailText = {
    GI_visMoniker = "New mail has arrived";
}
/* When the user clicks on the Acknowledge Trigger, MSG_MY_PROCESS_NEW_MAIL_ACK is
 * sent to the process class. Marking the trigger as IC_OK replaces the default
 * trigger. Note that the Specific UI is still allowed to pick the visual moniker
 * for this trigger as its GI_visMoniker field is left blank. */
@object GenTriggerClass NewMailAcknowledge = {
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_NEW_MAIL_ACK;
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_OK };
    HINT_SEEK_REPLY_BAR;
}

Affirmation Interactions

GIT_AFFIRMATION Interactions allow your application to provide the user with a choice of yes or no. GIT_AFFIRMATIONs normally appear as dialog boxes. If so, the Specific UI will create response triggers with the commands IC_YES and IC_NO. In OSF/Motif, the triggers are labeled "Yes" and "No." The Specific UI will not provide a default manner to close the dialog without responding to one of these. In OSF/Motif, this means that non-modal dialog boxes will have their "Close" triggers disabled.

For example, your application may need to prompt the user with a yes or no question (such as "Do you want to continue?"). Depending on the value returned in the InteractionCommand , the dialog box may continue or abort any impending operations. In most cases, you will want to use an affirmation interaction when you wish to force the user to make a choice.

An affirmation dialog allows the user to make a single yes or no choice, without an option to ignore the choice and close the dialog. In general, this type of dialog is normally modal or used with UserDoDialog() to block the calling thread until the user responds. When the user chooses, the caller will be unblocked and UserDoDialog() will return IC_YES or IC_NO. Alternatively, you can supply replacement IC_YES or IC_NO (or both) triggers which send out custom messages when activated.

Code Display 7-16 An Affirmation Dialog for Deleting a File

/* Rather than using UserDoDialog(), this dialog is simply marked GIA_MODAL.
 * This approach has the advantage of allowing any views that are run by the
 * process thread to be updated if they are exposed by the application. */
/* This dialog keeps the default IC_NO trigger to dismiss the dialog but
 * replaces the default IC_YES trigger. */
@object GenInteractionClass DeleteAffirmation = {
    GI_comp = DeleteAffirmationText, DeleteAffirmationYes;
    GII_type = GIT_AFFIRMATION;
    GII_visibility = GIV_DIALOG;
    GII_attrs = @default | GIA_MODAL;
}
@object GenGlyphClass DeleteAffirmationText = {
    GI_visMoniker = "Are you sure you want to delete this file?";
}
/* This trigger replaces the default IC_YES trigger. */
@object GenTriggerClass DeleteAffirmationYes = {
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_DELETE_FILE;
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = {IC_YES};
    HINT_SEEK_REPLY_BAR;
}

Multiple Response Interactions

GIT_MULTIPLE_RESPONSE Interactions allow the UI to provide the user with a number of choices, one of which must be selected. GIT_MULTIPLE_RESPONSE Interactions normally appear as dialog boxes. The Specific UI itself supplies no standard response triggers; you must add your own response triggers. (See Standard Response Triggers .) There is no default provision to just close or cancel the dialog, ignoring the choice. In OSF/Motif, this means the system menu for a non-modal dialog will have its "Close" trigger disabled.

The GIT_MULTIPLE_RESPONSE type allows you to build a custom dialog box. By default, a multiple-response dialog provides no standard response triggers and no way to close the dialog. You must therefore provide any response triggers you might need (including an IC_DISMISS InteractionCommand ). If not, the dialog will only be dismissable by the application, not by the user. In general, you should use a GIT_MULTIPLE_RESPONSE when you wish to prompt the user with several choices, one of which must be chosen.

Code Display 7-17 A Fragmentation Warning Dialog Box

@object GenInteractionClass DatabaseFragmentWarning = {
    GI_comp = DBFragmentText, DBFragmentCompact, DBFragmentContinue;
    GII_type = GIT_MULTIPLE_RESPONSE;
    GII_visibility = GIV_DIALOG;
    GII_attrs = @default | GIA_MODAL;
}
@object GenGlyphClass DBFragmentText = {
    GI_visMoniker = "Database is getting fragmented."
}
/* As none of the following triggers are standard trigger replacements, there is no
 * need to use ATTR_GEN_TRIGGER_INTERACTION_COMMAND. */
@object GenTriggerClass DBFragmentCompact = {
    GI_visMoniker = "Run Compaction utility.";
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_DB_COMPACT;
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    HINT_SEEK_REPLY_BAR;
}
@object GenTriggerClass DBFragmentContinue = {
    GI_visMoniker = "Continue without Compacting.";
    GTI_destination = process;
    GTI_actionMsg = MSG_MY_PROCESS_DB_CONTINUE;
    GI_attrs = @default | GA_SIGNAL_INTERACTION_COMPLETE;
    HINT_SEEK_REPLY_BAR;
}

Toolboxes

Toolboxes are useful to display small icons for commonly used operations. Toolboxes are normally provided by controllers. (See the Generic UI Controllers chapter.) In many cases toolboxes are non-modal GIT_ORGANIZATIONAL dialog boxes. Any GenInteraction may be marked HINT_TOOLBOX , however. (In some cases, you may wish a toolbox to be GIT_MULTIPLE_RESPONSE.) GeoDraw provides a toolbox to alter the properties of graphics objects. This toolbox appears at appropriate times in the GeoDraw application and will remain on-screen until the user explicitly closes it by selecting "Close" in its system menu.

Toolbox dialogs should never steal the focus or target of the application; this prevents the user from constantly having to reselect current objects. The hint HINT_TOOLBOX provides this functionality. This hint prevents a toolbox Interaction from grabbing the focus whenever an object within the toolbox is activated.

Menus and Dialog Boxes: 4 Supplemental Usage

The previous section described most of what you need to get an Interaction into your application. Much of the behavior of an Interaction is taken care of by the Specific UI. If you wish to modify this behavior, the following section details several ways in which you can alter these defaults.

Initiating Interactions

Dismissing Interactions

Modality for Dialogs

Managing Input

Thread Blocking Routines

Menus and Dialog Boxes: 4.1 Supplemental Usage: Initiating Interactions

MSG_GEN_INTERACTION_INITIATE, MSG_GEN_INTERACTION_INITIATE_NO_DISTURB, MSG_GEN_INTERACTION_DISABLE_DISCARDING

For the most part, the Specific UI decides in what way Interactions should be initiated. For example, a popup built as a menu will create a menu title which, when activated, brings down the menu. A dialog box will create an activation trigger within the UI that, when activated, brings the dialog box on-screen. (Notice that it is meaningless to "initiate" a sub-group Interaction as these objects are not independently-displayable.)

You may wish to modify this behavior, either to prevent the user from directly initiating an Interaction, or to add additional ways to bring these Interactions on-screen.

Although in most cases the user is responsible for activating menus (by activating the menu title), an application may manually open a menu with MSG_GEN_INTERACTION_INITIATE .

A GIV_DIALOG Interaction may be initiated in several ways:

  • In the default case, the specific UI will create an activation trigger whose sole function is to bring up the dialog box. The trigger will appear as a child of the dialog's parent, in the spot within the UI that the dialog would normally reside were it not independently displayable.
  • A dialog box may be set GIA_NOT_USER_INITIATABLE to prevent the building of the default trigger. Any Interaction set non-user initiatable should be sent a MSG_GEN_INTERACTION_INITIATE or a MSG_GEN_INTERACTION_INITIATE_NO_DISTURB to bring the dialog on-screen. You may also put Interactions on the GAGCNLT_WINDOWS GCN list to automatically bring them on-screen when an application is launched. Any Interaction must be fully usable and enabled to be brought up in such a manner. Note: If a dialog box is also marked GIA_INITIATED_VIA_USER_DO_DIALOG, it should be initiated with UserDoDialog() , not MSG_GEN_INTERACTION_INITIATE. These methods are exclusive.
  • A dialog box may be brought up with UserDoDialog() if it is set GIA_INITIATED_VIA_USER_DO_DIALOG. This routine is useful when you wish to block the calling thread until the user responds to the dialog box. A dialog box initiated via UserDoDialog() should also be marked GIA_NOT_USER_INITIATABLE. Note that this dialog box may only be brought up with UserDoDialog() ; it cannot be brought up with MSG_GEN_INTERACTION_INITIATE.

Within most specific UIs a trigger will appear whose sole function is to initiate the dialog. Activating the trigger will bring the dialog box on-screen. You can prevent the display of this default trigger by setting the Interaction GIA_NOT_USER_INITIATABLE in its GII_attrs instance field. (See GenInteraction Attributes ).

Your GenInteraction subclass may intercept MSG_GEN_INTERACTION_INITIATE to determine when a dialog is being brought on-screen and then perform any needed actions at that time. This is useful if you need to update the UI objects within the dialog before building its visual implementation. You should always call the superclass after setting any of this initial data to avoid unnecessary visual updates of the dialog box. MSG_GEN_INTERACTION_INITIATE_NO_DISTURB may also be subclassed, but there is little need to intercept it.

GenInteraction dialog boxes may be marked HINT_INTERACTION_DISCARD_WHEN_CLOSED .This hint informs the system to discard the dialog box's object block when it is no longer being used, but there is a timing problem if the dialog box needs to be initialized before it is brough on-screen. (The system will think it is off-screen and discard it before initialization is complete.)

In that case, send the dialog box MSG_GEN_INTERACTION_DISABLE_DISCARDING to ensure that the dialog box doesn't get discarded between the time that the initialization is performed and the time when the dialog box appears on-screen.

MSG_GEN_INTERACTION_INITIATE
void	MSG_GEN_INTERACTION_INITIATE();

This message brings an Interaction on-screen, giving it focus and target if possible. The Interaction must be both within the generic tree and set GS_USABLE before receiving this message. The Interaction must also be fully usable and enabled before it can be initiated. Interactions marked GIA_INITIATED_VIA_USER_DO_DIALOG should not be sent this message. To initiate those dialogs, you must use UserDoDialog() . (See Thread Blocking Routines .)

You may intercept this message to determine when a dialog is coming on-screen. This is useful if you need to set up UI gadgetry in the dialog to show some initial status. The gadgetry should be set up before calling the superclass to avoid unnecessary visual updating of the object.

Source: Unrestricted.

Destination: Any GS_USABLE GenInteraction, though usually only meaningful for GIV_DIALOG interactions.

Parameters: None.

Return: Nothing.

Interception: May be intercepted to find out when an Interaction is appearing on-screen. This is useful for cases in which the UI gadgetry of a dialog box needs to be set up before being visually built. The superclass should be called after setting up the UI to avoid unnecessary visual changes once the interaction is on-screen.

MSG_GEN_INTERACTION_INITIATE_NO_DISTURB
void	MSG_GEN_INTERACTION_INITIATE_NO_DISTURB();

This message brings an Interaction on-screen but does not give the dialog the focus or target. This message may place the dialog behind other windows. The Interaction must be both within the generic tree and set GS_USABLE before receiving this message. The Interaction must also be fully usable and enabled before it can be initiated. Interactions marked GIA_INITIATED_VIA_USER_DO_DIALOG should not be sent this message. To initiate those dialogs, you must use UserDoDialog() . (See Thread Blocking Routines .)

Source: Unrestricted.

Destination: Any GS_USABLE GenInteraction, though usually only meaningful for GIV_DIALOG Interactions. Not allowed for GIA_MODAL or GIA_INITIATED_VIA_USER_DO_DIALOG Interactions.

Parameters: None.

Return: Nothing.

Interception: May be intercepted to find out when an Interaction is appearing on-screen. This is useful for cases in which the UI gadgetry of a dialog box needs to be set up before being visually built. The superclass should be called after setting up the UI to avoid unnecessary visual changes once the interaction is on-screen.

MSG_GEN_INTERACTION_DISABLE_DISCARDING
void	MSG_GEN_INTERACTION_DISABLE_DISCARDING();

This message informs a Interaction with HINT_INTERACTION_DISCARD_WHEN_CLOSED that it should not be discarded until it has been brought on-screen. This message is used to ensure that a dialog box that needs initialization is not discarded between the time that initialization commences and the time that the dialog finally appears on-screen.

Source: Unrestricted.

Destination: Any GIV_DIALOG GenInteraction that is marked HINT_INTERACTION_DISCARD_WHEN_CLOSED and needs initialization.

Parameters: None.

Return: Nothing.

Menus and Dialog Boxes: 4.2 Supplemental Usage: Dismissing Interactions

You may dismiss dialogs and popups under application control by sending MSG_GEN_GUP_INTERACTION_COMMAND with the InteractionCommand IC_DISMISS to them. This will force dialogs to close even if the user has such a dialog pinned (in Specific UIs that support pinning). Dialogs may also be dismissed when the user activates reply bar response triggers with the GA_SIGNAL_INTERACTION_COMPLETE attribute set. (See the GenClass chapter.)

You may intercept MSG_GEN_GUP_INTERACTION_COMMAND to determine when a dialog is about to be dismissed. The handler for this message should check the incoming InteractionCommand to make sure it is IC_DISMISS before doing whatever work is needed. You should make sure to pass any non-IC_DISMISS InteractionCommand immediately to the superclass. The handler may or may not need to call the superclass for IC_DISMISS, depending on what behavior the handler is adding.

Menus and Dialog Boxes: 4.3 Supplemental Usage: Modality for Dialogs

MSG_GEN_INTERACTION_TEST_INPUT_RESTRICTABILITY

By default, GIV_DIALOG GenInteractions appear as non-modal windows. You may specify a dialog Interaction to appear as modal by setting either the GIA_MODAL attribute in the GII_attrs instance data field or by adding HINT_INTERACTION_MODAL in the object's vardata. When a modal dialog is displayed, input to all other parts of the application will be ignored until the dialog is dismissed. In OSF/Motif, modal dialogs will appear with thick borders.

You should use GIA_MODAL dialogs when the application depends on an Interaction being modal to operate correctly. For example, if a dialog shows information about the current selection but the application cannot update that information if the user were to change the current selection, you should use a GIA_MODAL dialog.

You should use HINT_INTERACTION_MODAL dialogs for cases where the modality of a dialog simplifies the interface between the application and the user but is not needed for the application's correct operation. The Interaction's modality in this case will make the UI clearer and simpler for the user to understand. For example, a dialog for setting obscure options that is brought up from a command dialog might be marked HINT_INTERACTION_MODAL so that the user is forced to set the options before returning to the command dialog. This will prevent the user from using this dialog box in a context where the options are not self-evident. As with all hints, the Specific UI may decide to ignore the hint and implement a HINT_INTERACTION_MODAL dialog with a non-modal dialog.

You may also initiate a modal dialog with UserDoDialog() . Such a dialog must have both the GIA_INITIATED_VIA_USER_DO_DIALOG and GIA_MODAL attributes set. Using this routine not only blocks input to other parts of the application but blocks the calling thread's execution until the dialog is dismissed. UserDoDialog() is useful for displaying notifications where the user's response is required before the application's thread may continue. For more information, see Thread Blocking Routines .

Dialogs displayed with UserStandardDialog() and UserStandardDialogOptr() are also modal. Both will block the calling thread until the dialog is dismissed. (See Thread Blocking Routines .)

Some modal dialogs will have subcomponents such as pop-up lists that must be interactable while the dialog is modal. The GenApplication object running the modal dialog will query the dialog to test both its input restrictions and its interactability in these cases; it uses two messages, described below.

MSG_GEN_INTERACTION_TEST_INPUT_RESTRICTABILITY
Boolean	MSG_GEN_INTERACTION_TEST_INPUT_RESTRICTABILITY(
        optr	obj);

This message is called on a modal window by a GenApplication object to find out whether or not the various input restricting mechanisms (input hold-up, input ignore, busy states, etc.) should be overridden or not.

Source: Part of the input flow/modality mechanism--sent by the GenApplication object to the modal window.

Destination: The modal GenInteraction object.

Parameters: obj The optr of the windowed object that may or may not be restricted.

Return: Will return true if input restrictions should be overridden, false otherwise. A true return value indicates that mouse and keyboard input will flow to the object regardless of hold-up, ignore, or busy states.

Interception: May be intercepted to allow the passed windowed object to override its input restrictions in certain cases.

Menus and Dialog Boxes: 4.4 Supplemental Usage: Managing Input

ATTR_GEN_INTERACTION_OVERRIDE_INPUT_RESTRICTIONS, ATTR_GEN_INTERACTION_ABIDE_BY_INPUT_RESTRICTIONS, HINT_INTERACTION_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD

Typically, specific UIs will determine how input (primarily keyboard input) affects GenInteractions and their components. For example, many UIs will have the Tab key navigate from the active component of a dialog box to the next available component.

HINT_INTERACTION_DEFAULT_ACTION_IS_NAVIGATE_TO_NEXT_FIELD allows you to trade the normal "default action" (the action typically causing the dialog to send its "apply" message) for navigation. For example, if hitting the return key normally invoked an "Apply" trigger, this hint would instead cause it to navigate to the next field in the dialog box.

Two other attributes allow a GenInteraction to either disobey or abide by certain default input restrictions (modality, input hold-up, input ignore, and busy states). ATTR_GEN_INTERACTION_OVERRIDE_INPUT_RESTRICTIONS allows the GenInteraction to override the input restrictions; ATTR_GEN_INTERACTION_ABIDE_BY_INPUT_RESTRICTIONS allows the GenInteraction to override default behavior ignoring those restrictions.

Menus and Dialog Boxes: 4.5 Supplemental Usage: Thread Blocking Routines

Occasionally, you may need a response from the user before continuing with a thread of execution. In these cases, you should use a dialog box to prompt the user for the needed response. However, you also need a means to block the thread of execution until the user responds to this dialog box. The routines UserDoDialog() , UserStandardDialog() , and UserStandardDialogOptr() provide this functionality.

UserDoDialog()

UserDoDialog(), UserCreateDialog(), UserDestroyDialog()

You may bring dialog boxes on-screen with several routines. The most common and easiest to use of these routines is UserDoDialog() . UserDoDialog() only operates on GIV_DIALOG Interactions set both GIA_MODAL and GIA_INITIATED_WITH_USER_DO_DIALOG.

This routine, when passed the optr of a dialog box, will bring the dialog Interaction on-screen. In addition to bringing up a dialog, however, UserDoDialog() will also block the calling thread until a response trigger in the dialog is activated by the user. When this happens, UserDoDialog() returns a value representing the response trigger selected. This return value is usually an InteractionCommand .

The dialog may contain any UI gadgetry but must have response triggers with valid InteractionCommand s to terminate the dialog box. These triggers should have a null output and message and have ATTR_GEN_TRIGGER_INTERACTION_COMMAND vardata set to the proper InteractionCommand s. (See the GenTrigger chapter.) This InteractionCommand will be the response value returned by UserDoDialog() when that trigger is activated. This can be one of the predefined InteractionCommand s or an application-defined one with IC_CUSTOM_START.

The response triggers should also have the attribute GA_SIGNAL_INTERACTION_COMPLETE set to ensure that the dialog will be dismissed when they are activated. (This may be omitted to gain manual control over dismissal over the dialog, but you still must provide a way to dismiss the dialog.) Any response triggers in the dialog should have the hint HINT_SEEK_REPLY_BAR to place the triggers within the dialog's reply bar, but this is not necessary.

Because the calling thread is blocked by UserDoDialog() , views run by that thread will not be updated when exposed.

Code Display 7-18 Using UserDoDialog()

/* This dialog box asks for confirmation before beginning a delete file operation.
 * Therefore, it is advisable to block threads before beginning this operation. The
 * dialog using UserDoDialog() should be marked both GIA_MODAL and
 * GIA_INITIATED_VIA_USER_DO_DIALOG. */
@object GenInteractionClass ConfirmDeleteBox = {
    GI_comp = @ConfirmDeleteText;
    GII_type = GIT_AFFIRMATION;
    GII_visibility = GIV_DIALOG;
    GII_attrs = @default | GIA_INITIATED_VIA_USER_DO_DIALOG | GIA_MODAL;
}
@object GenTextClass ConfirmDeleteText = {
    GTI_text = "Are you sure you want to delete this file?";
}
/* This dialog box is displayed through a normal routine call with
 * the optr of the dialog box as its one argument. */
/* Check for positive response. */
if (UserDoDialog(@ConfirmDeleteBox) == IC_YES) {
    /* delete file. */
}

UserDoDialog() may also return IC_NULL to indicate that the modal dialog has been dismissed by the system, so it is always a good idea to check for and act on positive responses even if only one response is possible (such as in a GIT_NOTIFICATION dialog box).

UserCreateDialog() duplicates a dialog box to initiate later with UserDoDialog() . Typically, the dialog box duplicated is within a template object block; the dialog box must be both not GS_USABLE and not attached to the generic tree when created. The dialog box must also be marked GIA_INITIATED_VIA_USER_DO_DIALOG.

The template block that contains the dialog box and its children must be sharable and read-only. UserCreateDialog() duplicates a template dialog box, attaches the dialog box to the GenApplication object and sets it fully GS_USABLE; it may then be called with UserDoDialog() . When you no longer have a need for the dialog box, send it UserDestroyDialog() .

These routines are useful for conserving memory space; they only take up space when actually being used. In some cases, you may need to use these routines. For example, within libraries, dialog boxes must be duplicated before being used because multiple applications may require their own copy of the dialog box template.

Code Display 7-19 Using UserCreateDialog(), UserDestroyDialog()

/*
 * The template dialog box must not be GS_USABLE. The object must also be marked
 * GIA_INITIATED_VIA_USER_DO_DIALOG. The block must be sharable, read-only, and
 * the top GenInteraction must not be linked into the generic tree.
 */
@object GenInteractionClass MyDialogTemplate = {
    GI_visMoniker = "Template";
    GI_states = @default & ~GS_USABLE;
    GII_visibility = GIV_DIALOG;
    GII_attrs = @default | GIA_INITIATED_VIA_USER_DO_DIALOG |
    GIA_NOT_USER_INITIATABLE | GIA_MODAL;
    GII_type = GIT_NOTIFICATION;
    GI_comp = @NotificationGlyph;
}
@method SomeProcessClass, MSG_BRING_UP_DUPLICATED_DIALOG
{
    optr		newDialog;
    newDialog = UserCreateDialog(@MyDialogTemplate);
    if (UserDoDialog(@newDialog) == IC_OK) {
    /*** code ***/
    }
    UserDestroyDialog(@newDialog);
}

UserStandardDialog()

UserStandardDialog(), UserStandardDialogOptr(), CustomDialogBoxFlags

UserStandardDialog() displays standardized dialog boxes. The dialog is standardized in that it has a text area, an icon glyph representing the type of situation that caused the dialog to be displayed, and one or more response triggers. Like UserDoDialog() , UserStandardDialog() blocks the calling thread until the user activates one of the response triggers. Unlike UserDoDialog() , however, UserStandardDialog() does not need an application-defined dialog box. UserStandardDialog() builds a dialog box at run-time following the specifications passed.

UserStandardDialog() passes a number of parameters:

  • CustomDialogBoxFlags
     This bitfield stores the CustomDialogType (CDBF_DIALOG_TYPE) and the GenInteractionType (CDBF_INTERACTION_TYPE) to display the Interaction with, along with several miscellaneous flags
CDBF_SYSTEM_MODAL indicates that the dialog box brought up by UserDoDialog() should not only be application modal but also system modal.
CDBF_DESTRUCTIVE_ACTION indicates that an affirmative response by the user to the dialog box denotes a destructive action, and thus should not be made the default.
CDBF_DIALOG_TYPE indicates the type of situation creating the dialog box. This type determines the icon glyph that represents what caused the dialog to be displayed. The available types ( CustomDialogType ) are:
CDT_QUESTION: Ask the user a question;
CDT_WARNING: Warn the user of a potential problem;
CDT_NOTIFICATION: Notify the user of some event;
CDT_ERROR: Report an error to the user.
The Specific UI will determine the appropriate icon glyph to use for each dialog type. For example, in OSF/Motif, a CDT_QUESTION icon glyph is a graphic question mark.
The system will also issue a beep when a CDT_ERROR dialog is displayed.
CDBF_INTERACTION_TYPE indicates the type of GenInteraction being initiated. This type specifies what response triggers should be built and is a sub-set of the supplied GenInteractionType enums. The available types are:
GIT_NOTIFICATION
The specific UI will supply a standard response trigger that has the IC_OK response value.
GIT_AFFIRMATION
The specific UI will supply standard response triggers that have the IC_YES and IC_NO response values.
GIT_MULTIPLE_RESPONSE
The application must provide its own trigger monikers and response values. If this value is set, you will have to pass the array of custom triggers.
  • Dialog string (and arg1 and arg2 )
    This is the string to display in the dialog box text area. The string can have up to two parameters. These parameters are other strings that are passed to UserStandardDialog() in arg1 and arg2 . They will replace all occurrences of \001 and \002 in the dialog string. This is useful for including filenames or other variable text in the dialog string.
  • Response trigger monikers and values
    If GIT_MULTIPLE_RESPONSE is used, this indicates how many triggers and what labels and response values the response triggers should have.
  • Help context
    The help context to use for this dialog box layer.

Code Display 7-20 Using UserStandardDialog()

/*
 * This simple example uses no help context, custom triggers, or string arguments.
 */
if ((UserStandardDialog(			(char *)0,
            (char *)0,
            (char *)0,
            (char *)0,
            "Do you wish to continue?",
            ((CDT_QUESTION << CDBF_DIALOG_TYPE_OFFSET) |
            (GIT_AFFIRMATION << CDBF_INTERACTION_TYPE_OFFSET))
            ) == IC_YES)) {
    /* code to perform on a positive response. */
    }
else {
    /* code to perform on a negative response. */
    }

You may also use UserStandardDialogOptr() for cases in which the strings are referenced through optrs rather than pointers.

Code Display 7-21 A ConfirmDeleteBox with explicit monikers

/* For this case, since we want to provide explicit monikers, we must use the
 * GIT_MULTIPLE_RESPONSE interaction type. Using this allows us to pass in the
 * monikers and response values for the response triggers for the dialog. This is
 * done by passing a pointer to a table consisting of the number of triggers in the
 * dialog and a StandardDialogResponseTriggerEntry for each trigger. Each entry
 * contains an optr of the moniker to use and the response value for the trigger.
 * The moniker may be simple text or a graphics string. The response value may be
 * one of the predefined InteractionCommands or an application-defined value based
 * on IC_CUSTOM_START. */
@visMoniker ConfirmYesMoniker = "Delete this file";
@visMoniker ConfirmNoMoniker = "Skip this file";
/* Create a table to hold the trigger data. */
static const StandardDialog2ResponseTriggerTable confirmResponseTable [] = {
    2,				/* SD2RTT_numTriggers */
    /* WRT_trigger1 */
    {ConfirmYesMoniker,				/* SDRTE_moniker */
    IC_YES}, 			/* SDRTE_responseValue */
    /* WRT_trigger2 */
    {ConfirmNoMoniker,				/* SDRTE_moniker */
    IC_NO}			/* SDRTE_responseValue */
};
/* Display the dialog with UserStandardDialog(). */
if (UserStandardDialog(			(char *)0,
            (char *)&confirmResponseTable,
            (char *)0,
            (char *)0,
            "Are you sure you want to delete this file?",
            ((CDT_QUESTION << CDBF_DIALOG_TYPE_OFFSET) |
            /* interaction type - application supplied trigger */
            (GIT_MULTIPLE_RESPONSE << CBDF_INTERACTION_TYPE_OFFSET)))
        == IC_YES) {
    /* delete file */
}

Code Display 7-22 A IC_CUSTOM_START Interaction

#define SAVE_CHANGES				IC_CUSTOM_START+0
#define ABORT_CHANGES				IC_CUSTOM_START+1
#define CANCEL_CLOSE				IC_CUSTOM_START+2
@visMoniker CloseSaveMoniker = "Save Changes";
@visMoniker CloseAbortMoniker = "Abort Changes":
@visMoniker CloseCancelMoniker = "Cancel Close";
static const StandardDialog3ResponseTriggerTable closeResponseTable [] = {
     3, 					/* SD3RTT_numTriggers */
    /* WRT_trigger1 */
    {CloseSaveMoniker,					/* SDRTE_moniker */
    IC_YES},					/* SDRTE_responseValue */
    /* WRT_trigger2 */
    {CloseAbortMoniker,					/* SDRTE_moniker */
    IC_YES},					/* SDRTE_responseValue */
    /* WRT_trigger3 */
    {CloseCancelMoniker,					/* SDRTE_moniker */
    IC_NO}					/* SDRTE_responseValue */
};
closeWithChangesResponse =
    (UserStandardDialog(
        (char *)0,
        (char *)&closeResponseTable,
        (char *)0,
        (char *)0,
        ((CDT_QUESTION << CDBF_DIALOG_TYPE_OFFSET) |
        /* interaction type - application supplied triggers */
        (GIT_MULTIPLE_RESPONSE << CDBF_INTERACTION_TYPE_OFFSET)),
        (char *)0);
switch (closeWithChangesResponse) {
    case SAVE_CHANGES:
    /* save changes */
    case ABORT_CHANGES:
    /* abort changes */
    case IC_CANCEL_CLOSE:
    /* cancel close */
    case IC_NULL:
    /* IC_NULL is always a potential response */
}

Menus and Dialog Boxes: 5 Interaction Commands

Much of the flexibility of GenInteraction lies in its ability to recognize and process a variety of special InteractionCommand s. These commands are data types that the Specific UI and the Interaction can use to decide what actions to take. Depending on the InteractionCommand , the UI may perform several pre-defined actions. Much of this functionality is automatic through the inclusion of standard response triggers within a dialog box.

Some GenInteractionType s provide one or more triggers for your use when the Interaction is built as a dialog box. These default triggers are automatically provided without your needing to create and attach triggers to the Interaction itself. Triggers provided in this manner are known as standard response triggers. Standard response triggers usually appear within an area of the dialog box known as a reply bar.

Each standard response trigger has a specific InteractionCommand associated with it. These triggers perform their actions by sending out their InteractionCommand with the message MSG_GEN_GUP_INTERACTION_COMMAND to themselves when activated. The message will travel up the tree to the first GIV_DIALOG Interaction, where it will be handled. See InteractionCommand Types for the pre-defined InteractionCommand s.

InteractionCommand Types

Dialog Control

Standard Response Triggers

Replacing Default Triggers

Triggers Completing Interactions

Menus and Dialog Boxes: 5.1 Interaction Commands: InteractionCommand Types

Many of these InteractionCommand s are sent out by standard response triggers provided with the Specific UI. Different types of Interactions may provide different standard response triggers with InteractionCommand s. For example, the "OK" trigger for a GIT_NOTIFICATION dialog box in OSF/Motif sends out the InteractionCommand IC_OK.

The InteractionCommand represents the actions that can be performed on an Interaction. The predefined commands are as follows:

  • IC_NULL
    This InteractionCommand is a special case for alerting a UserDoDialog() or UserStandardDialog() dialog box that it is being dismissed because of system shutdown. Because of thread blocking, you should always check for this case to occur in any UserDoDialog() routines. You should never set any custom triggers to this InteractionCommand .
  • IC_DISMISS
    This command instructs the UI to dismiss the Interaction. An Interaction receiving this command will come down whether or not the user has selected the Interaction to remain up (such as by pinning the dialog box).
  • IC_APPLY
    This command instructs the UI to apply properties selected within a GIT_PROPERTIES Interaction. This InteractionCommand causes MSG_GEN_APPLY to be sent to the Interaction's children.
  • IC_RESET
    This command instructs the UI to reset properties within a GIT_PROPERTIES Interaction. This InteractionCommand causes MSG_GEN_RESET to be sent to the Interaction's children.
  • IC_OK
    This command instructs the UI to signal an acknowledgment to a GIT_NOTIFICATION Interaction.
  • IC_YES
    This command instructs the UI to signal a positive response to a GIT_AFFIRMATION Interaction.
  • IC_NO
    This command instructs the UI to signal a negative response to a GIT_AFFIRMATION Interaction.
  • IC_STOP
    This command instructs the UI to halt a GIT_PROGRESS Interaction.
  • IC_EXIT
    This command instructs the UI to exit the application (not just the window). This InteractionCommand is only supported for GenTriggers under GIV_POPUP GenInteractions. This value should only be used with ATTR_GEN_TRIGGER_INTERACTION_COMMAND.
  • IC_HELP
    This command instructs the UI to bring up the proper help context for this dialog. This value should only be used with ATTR_GEN_TRIGGER_INTERACTION_COMMAND.
  • IC_INTERACTION_COMPLETE
    This command notifies the UI that an interaction has been completed. This InteractionCommand is sent out by any trigger with the attribute GA_SIGNAL_INTERACTION_COMPLETE before the trigger's main action has been sent out. Note that this InteractionCommand is always sent out in addition to another IC. You should never set any custom triggers to this InteractionCommand .

IC_DISMISS, IC_APPLY, IC_RESET, IC_OK, IC_YES, IC_NO, and IC_STOP correspond to standard response triggers provided with the Specific UI for dialogs of various GenInteractionType . (See GenInteraction Types .)

You may replace any of these standard response triggers by adding your own triggers with an InteractionCommand . You should do this by setting the trigger's ATTR_GEN_TRIGGER_INTERACTION_COMMAND vardata to the proper InteractionCommand to replace. (See Replacing Default Triggers .)

Adding Custom Response Triggers

In some cases, you may want to add your own response triggers. In many of these cases, a trigger tied to one of the predefined InteractionCommand s will be sufficient for your needs. If a dialog box does not contain one of the pre-defined response triggers by default, you can add any of these yourself.

You are not limited to one of these predefined types, however. UserDoDialog() and GIT_MULTIPLE_RESPONSE UserStandardDialog () allow you to define your own InteractionCommand types. Applications can define their own InteractionCommand by starting an enumeration at the special value IC_CUSTOM_START. This is to avoid conflicting with other predefined values, which still have their usual meaning to the dialog.

Code Display 7-23 Using IC_CUSTOM_START

/* You may define values directly. */
#define IC_SAVE_FILE IC_CUSTOM_START+1;
#define IC_KILL_FILE IC_CUSTOM_START+2;
/* You may also use typedef to enumerate those values. */
typedef enum {
    IC_SAVE_FILE = IC_CUSTOM_START+1,
    IC_KILL_FILE = IC_CUSTOM_START+2
} MyInteractionCommand;
/* Any triggers set up with these InteractionCommands would appear like so: */
@object GenTriggerClass MyTrigger = {
    GI_visMoniker = "Save File";
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_SAVE_FILE };
    HINT_SEEK_REPLY_BAR;
}
/* An Interaction invoked with UserDoDialog() (with MyTrigger as one of its
 * children) will return IC_SAVE_FILE when that trigger is activated. Your
 * application can then perform any required actions. */

Menus and Dialog Boxes: 5.2 Interaction Commands: Dialog Control

MSG_GEN_GUP_INTERACTION_COMMAND, MSG_GEN_INTERACTION_ACTIVATE_COMMAND

There are two ways of delivering an InteractionCommand to an Interaction. The first is through the GenClass message MSG_GEN_GUP_INTERACTION_COMMAND . In most cases, this is the message you will use to deliver commands to your dialog boxes. For example, you may send MSG_GEN_GUP_INTERACTION_COMMAND with IC_DISMISS to dismiss a dialog box. The second involves sending MSG_GEN_INTERACTION_ACTIVATE_COMMAND with one of the specified InteractionCommand types.

The system sends this message whenever the user activates a response trigger with null action fields. The system will pass the command in ATTR_GEN_TRIGGER_INTERACTION_COMMAND vardata with the message up the generic tree until handled by the first GIV_DIALOG Interaction. Handling may include unblocking and returning a UserDoDialog() response value, applying or resetting properties, and dismissing, depending on the InteractionCommand . Note that if an action is specified in the trigger's instance data, MSG_GEN_GUP_INTERACTION_COMMAND will not be sent--the specified action will instead.

In OSF/Motif, dialogs perform the following default activity with their various InteractionCommand s:

IC_DISMISS
This command dismisses the dialog, resets gadgets if GIT_PROPERTIES and modal (i.e. "Cancel" trigger).
IC_APPLY
This command applies gadgets (whether or not GIT_PROPERTIES), then unblocks and returns a UserDoDialog() value if needed.
IC_RESET
This command resets gadgets (whether or not GIT_PROPERTIES), then unblocks and returns a UserDoDialog() value if needed.
IC_INTERACTION_COMPLETE
This command dismisses, depending on dialog context, the dialog box and unblocks and returns a UserDoDialog() value if needed.

All other InteractionCommands will just unblock and return a UserDoDialog() value if needed.

GIV_DIALOG GenInteractions are the most likely recipients of any InteractionCommand s. Popup Interactions only support IC_DISMISS and IC_INTERACTION_COMPLETE and will pass other InteractionCommand s up the generic tree. Any other Interactions (e.g. sub-groups) will always pass MSG_GEN_GUP_INTERACTION_COMMAND up the generic tree.

MSG_GEN_INTERACTION_ACTIVATE_COMMAND is a higher-level function. When sent to a dialog GenInteraction with a relevant InteractionCommand , it will find the standard trigger for that InteractionCommand and activate it. This can be a Specific UI-supplied trigger or a replacement trigger supplied by the application. If no such trigger exists, the message sends MSG_GEN_GUP_INTERACTION_COMMAND to perform the default handling. This message is only supported by dialog Interactions.

MSG_GEN_INTERACTION_ACTIVATE_COMMAND
void	MSG_GEN_INTERACTION_ACTIVATE_COMMAND(
        word	command);

This message activates a GenTrigger having the passed command in its ATTR_GEN_TRIGGER_INTERACTION_COMMAND; if no such trigger exists, this message calls MSG_GEN_GUP_INTERACTION_COMMAND to the Interaction itself with the supplied command value. Note that InteractionCommand type IC_INTERACTION_COMPLETE should not be used as this is a notification rather than a command.

Source: Unrestricted.

Destination: Any non GIT_ORGANIZATIONAL GenInteraction object.

Parameters: command InteractionCommand to activate (except IC_INTERACTION_COMPLETE and IC_NULL).

Return: Nothing.

Interception: May be intercepted to change or supplement default functionality of standard or custom interaction commands. Standard response triggers will use this message, so intercepting this message will allow changing the default behavior of these triggers without having to replace them with your own triggers.

Menus and Dialog Boxes: 5.3 Interaction Commands: Standard Response Triggers

The Specific UI provides standard response triggers for certain GenInteractionType , when built as dialog boxes. These standard response triggers contain an appropriate InteractionCommand and usually a visual moniker chosen by the Specific UI.

The standard response triggers (and their visual monikers under OSF/Motif) for each GII _type are shown in the figure below.

The Specific UI automatically places these standard response triggers in a reply bar within the dialog. In most cases, you will not have to deal directly with any of these standard response triggers; the Specific UI will create and manage them. In other cases, you must either supply additional response triggers or replace the existing default response triggers. Additional triggers must be supplied for GIT_COMMAND and GIT_MULTIPLE_RESPONSE Interactions. You may replace existing triggers to customize their behavior or even just to change their visual monikers.

You should add HINT_SEEK_REPLY_BAR to any additional triggers you supply so that they will be placed in the reply bar area of the Interaction (though this is not necessary). If you add more than one trigger, their order within the Interaction's children will be the order they appear in the reply bar. However, supplemental reply bar triggers will appear to the right of any IC_OK, IC_YES, IC_STOP, or IC_APPLY and to the left of any IC_DISMISS, IC_RESET, or IC_NO trigger.

Certain triggers may act as default triggers in the reply bar. Default triggers do not need to be explicitly activated (i.e. clicked on) to send out their action. Default triggers may also be activated if the user performs a default activation (normally hitting "Return" or double-clicking on an object within the dialog). Normally this default activation trigger will be the first trigger in the reply bar. Default triggers may be highlighted in some manner by the Specific UI. You can override this behavior by marking any reply trigger of your choosing with the hint HINT_DEFAULT_DEFAULT_ACTION .

The Specific UI gives HINT_DEFAULT_DEFAULT_ACTION to the IC_APPLY, IC_OK and IC_YES triggers in any reply bars it creates. (These are normally the first triggers anyway.) For example, the "OK" trigger in a GIT_NOTIFICATION dialog may be activated by either clicking on the trigger or hitting RETURN.

Whenever a dialog is initially brought on-screen, the trigger marked with HINT_DEFAULT_DEFAULT_ACTION will act as the default activation trigger. However, activating any other trigger will make that trigger the default activation trigger the next time the dialog is brought on-screen. You can avoid this by adding HINT_PREVENT_DEFAULT_OVERRIDES on the GenInteraction itself. This will allow the HINT_DEFAULT_DEFAULT_ACTION trigger to always remain the default trigger.

Menus and Dialog Boxes: 5.4 Interaction Commands: Replacing Default Triggers

You may replace a trigger supplied by the Specific UI by adding your own custom GenTrigger (as a child of the GenInteraction) with its ATTR_GEN_TRIGGER_INTERACTION_COMMAND set to the proper InteractionCommand of the trigger to replace. You should also mark such triggers with HINT_SEEK_REPLY_BAR to place them within the reply bar, though this is not required. If there is no such hint, the trigger will appear within the body of the dialog.

You should set the trigger to perform whatever action your application requires (including the function of the trigger being replaced, if applicable). If necessary, the action handler should also perform the default handling of the replaced trigger. A moniker need not be supplied as the Specific UI will choose one based on the InteractionCommand and the context of the dialog. You may supply your own moniker.

In fact, in some cases you might want to merely change the default moniker of a standard response trigger without changing the functionality of the trigger itself. In this case, you should provide an override trigger with ATTR_GEN_TRIGGER_INTERACTION_COMMAND . You should then add your new visual moniker to this replacement but leave the action fields blank. The specific UI will assign any trigger set up with ATTR_GEN_TRIGGER_INTERACTION_COMMAND and a blank action to have the default behavior of the replaced trigger.

Code Display 7-24 Replacing a Standard Response Trigger

/* The GenInteractionType GIT_NOTIFICATION, when built as a dialog, automatically
 * contains a standard response trigger to send out the InteractionCommand IC_OK.
 * You can replace this trigger with one of your own by adding a trigger with that
 * InteractionCommand. Your trigger will override the system default. */
@object GenInteractionClass MyInteraction = {
    GII_type = GIT_NOTIFICATION;
    GII_visibility = GIV_DIALOG;
    GII_attrs = @default | GIA_NOT_USER_INITIATABLE | GIA_MODAL;
    GI_comp = MyGlyph, MyResponseTrigger;
}
@object GenGlyphClass MyGlyph = {
    GI_visMoniker = "New Mail has arrived";
}
/* MyResponseTrigger replaces the IC_OK default trigger for the Notification dialog
 * box. In this case, we will only change the visual moniker of the trigger. By
 * leaving the output fields blank, we cause the system to send
 * MSG_GEN_GUP_INTERACTION_COMMAND with IC_OK. (This is the default behavior.)
 * HINT_SEEK_REPLY_BAR is used to place this trigger within the reply bar of the
 * notification dialog. */
@object GenTriggerClass MyResponseTrigger = {
    GI_visMoniker = "OK, I acknowledge";
    ATTR_GEN_TRIGGER_INTERACTION_COMMAND = { IC_OK };
    HINT_SEEK_REPLY_BAR;
}

ATTR_GEN_TRIGGER_INTERACTION_COMMAND also performs a special function in dialogs displayed with UserDoDialog() . Whenever the user completes an Interaction in a UserDoDialog() dialog box by activating one of the response triggers, the InteractionCommand stored in the ATTR_GEN_TRIGGER_INTERACTION_COMMAND of the activated trigger will be returned. You can use this return value to determine which trigger the user activated. (See Thread Blocking Routines .)

Menus and Dialog Boxes: 5.5 Interaction Commands: Triggers Completing Interactions

The GenClass attribute GA_SIGNAL_INTERACTION_COMPLETE is used to indicate that a response trigger completes a single user interaction within the dialog that contains it. Depending on the context (modality, type, hints) of the dialog, the Specific UI may or may not dismiss the dialog box.

For example, the "Yes" trigger (IC_YES) in a GIT_AFFIRMATION dialog might dismiss a dialog if the trigger is also marked GA_SIGNAL_INTERACTION_COMPLETE because the user has given the dialog final information. An "Apply" trigger (IC_APPLY) marked GA_SIGNAL_INTERACTION_COMPLETE might not dismiss its dialog because it might be useful to use the dialog to repeatedly apply different properties. This behavior depends on the Specific UI.

All standard response triggers provided by the Specific UI with the various GenInteractionType , except for the IC_RESET trigger), have GA_SIGNAL_INTERACTION_COMPLETE set. The IC_RESET trigger normally does not indicate the end of user interaction with the dialog and thus should not dismiss the dialog under any circumstances.

In OSF/Motif, a trigger marked GA_SIGNAL_INTERACTION_COMPLETE will dismiss the dialog containing the trigger if the dialog is not of GenInteractionType GIT_PROPERTIES or GIT_COMMAND, or if the dialog is modal. A non-modal GIT_PROPERTIES or GIT_COMMAND dialog is not dismissed when its response triggers are activated. If you do wish to dismiss the dialog in such cases, you can mark the dialog box with HINT_INTERACTION_SINGLE_USAGE . This hint tells the Specific UI that it expects the user will only use the dialog box once each time it is brought up. You may also want to do this if the dialog box is infrequently used.

Similarly, HINT_INTERACTION_FREQUENT_USAGE allows a dialog to stay up even if one of its GA_SIGNAL_INTERACTION_COMPLETE triggers would normally bring it down. In most cases, however, it is advisable to remove the GA_SIGNAL_INTERACTION_COMPLETE attribute on whichever triggers should not dismiss the Interaction. This is preferred over adding HINT_INTERACTION_FREQUENT_USAGE because the Specific UI may ignore the hint, but it cannot ignore the state of its GA_SIGNAL_INTERACTION_COMPLETE bit.

When using HINT_INTERACTION_FREQUENT_USAGE , you should ensure that the dialog is dismissed. In most cases, this can be done by sending MSG_GEN_GUP_INTERACTION_COMMAND with IC_DISMISS to the Interaction. This can be done in an action handler for a response trigger.

This document is a single-page version of a a multi-page document, suitable for easy printing.


GenView

The GenView provides a means of displaying data on the screen in a scrollable, sizeable, scalable window. This data may be in the form of a graphics document, a generic object tree, or a visible object tree. If your geode requires a means of displaying data visually, you will most likely want to use a view.

This chapter discusses how to create and use the GenView. You should have a good knowledge of the UI in general, and you should understand the GEOS coordinate system.

1 GenView Overview
1.1 GenView Model
1.2 View Features and Goals
1.3 The GenViewControl Object
2 Getting Started: View Basics
2.1 Graphics System Review
2.2 Defining the Basic View
2.3 Handling View Messages
3 Basic View Attributes
3.1 The GVI_attrs Attribute
3.2 Dimensional Attributes
3.3 Setting the Background Color
3.4 The GVI_increment Attribute
4 Advanced Concepts and Uses
4.1 The Life of a View
4.2 Documents in a View
4.3 Drawing the Document
4.4 Document and View Size
4.5 Document Scaling
4.6 Children of the View
4.7 Scrolling
4.8 Monitoring Input
4.9 Linking Views
4.10 Setting the Content
4.11 Internal Utilities
5 The GenViewControl
5.1 GenViewControl Instance Data
5.2 Notification Received
5.3 GenViewControl Example
5.4 Messages Handled

GenView: 1 GenView Overview

The primary goal of the GenView is to display data on the screen. It provides a window into which applications may draw any type of document from a generic object tree to a visible object tree to graphics strings defined by graphics routines. Nearly all document-oriented applications will use a view in some capacity.

GenView Model

View Features and Goals

The GenViewControl Object

GenView: 1.1 GenView Overview: GenView Model

The GenView is simply an object that maintains a window for your geode's use. It must be instructed in how to display your data, although it defaults to a certain display scheme. It receives and sends out messages that define how and when your data is drawn; you must provide an object that can receive the view's messages and respond correctly.

The object that communicates with the view on the application's side is known as the content object. The content may be one of three different classes (see below) depending on the type of data you wish to display. Essentially, what type of object you designate as your content is irrelevant to the view; the view sends and receives the same messages regardless.

There are three basic models you may employ based on the type of data you have to display:

  • Graphic document
    If you are just displaying standard graphics, you will want the content to be an object of ProcessClass . This means that your application's Process object will receive all the messages sent by the view (including MSG_META_EXPOSED , described later) and must know how to draw the graphics document when notified. This model is common.
  • Visible object tree
    If you have a tree of visible objects, you will want to set the view's content as an object of VisContentClass . This object understands how to respond to several of the view's messages and how to draw a visible tree when notified. This approach is also common.
  • Generic object tree
    Though seldom used, it is possible to display generic gadgets in a scrolling view by setting the view's content to be a GenContent object. The GenContent is analogous to the VisContent but works with generic object trees rather than visible object trees. The GenContent is used most often as the superclass of GenDocumentClass (see the Documents chapter).

As described in the Graphics Environment chapter, GEOS uses two different coordinate systems, both of which are based on points (1/72 inch). The default system uses 16-bit coordinates and allows documents up to 113 inches on a side. The large model uses 32-bit coordinates, resulting in documents up to 2,147,483,648 points on a side, or 940 miles square. The GenView uses the large model (32-bit coordinates) by default. This means that all coordinates related to the view will be 32 bits, though you can still use 16 bit coordinates for your documents.

Because the graphic document model described above is the simplest, this chapter will focus on having a ProcessClass object as your view's content.

GenView: 1.2 GenView Overview: View Features and Goals

The GenView is a powerful and flexible object that does most of your display work for you. It is designed to provide not only the functionality required by nearly all applications but also many additional features that more advanced applications will use. The following goals helped shape the GenView:

  • Common functionality
    The view, like all of GEOS, is designed with common application functionality in mind. Those functions common to many applications are integrated as much as possible to relieve programmers of having to code and debug the same features again and again.
  • Consistency of use
    The view, like every generic UI object, allows all GEOS applications to behave and look consistent. Users find this important when using several different applications.
  • Ease of use
    The view, in its simplest form, is almost trivial to implement. Applications can gain a scrolling window by adding very little code (as evidenced in the Hello World program).
  • Expandability
    Using the view's more advanced features requires little more than incremental changes to an application; you can easily and quickly add scaling to or change the background color of your view.

To meet these goals, the view provides several advanced features and makes them easy to use for all applications. All the following features are available to applications with limited effort, and most are expandable to be very powerful.

  • Drawing to the view
    Drawing to the view is simple, as simple as calling the proper graphics routines to draw to a GState. The view will send a MSG_META_EXPOSED to its content object whenever the document must be drawn (for example, when the view scrolls or when a window has uncovered a portion of the view); the content then simply must draw it. The view will handle all necessary clipping, placement, scaling, and translation for the video driver. See Documents in a View .
  • Scrolling
    You can set up your view to be scrollable and all scrolling to be automatic. You can also cause the view to scroll or even change how the view scrolls. See Scrolling .
  • Scaling
    Every view has a scale factor that determines the scale at which the document is drawn. You may leave this at the default (the normal size), or you may dynamically change it. The view simply asks the content object to redraw its document, and the view will handle all the scaling geometry. See Document Scaling .
  • Sizing
    Because most Views are resizable, you can easily alter the sizing behavior of your view. You may also set it to have a certain minimum, maximum, or fixed size. See Document and View Size .
  • Setting the Background Color
    Depending on the view's purpose, you may wish it to be either the same color as its GenPrimary parent or a different color. The View in Hello World, for example, is white; it could easily be any other color. See Setting the Background Color .
  • Handling Input
    Unless your view is display-only, your content will be able to handle either pointer or keyboard events passed through it. You can specify the level of input you want and easily get mouse or typed character events. In addition, the Ink standard for pen input is also supported.
  • Setting the Content Size and Origin
    Every content object has a size associated with it. The size defines where the content sits in the coordinate space and what part of the coordinate space the view has access to for scrolling. Every view also has an origin describing which part of the content is currently visible. See Documents in a View .
  • Changing the Content
    Although most Process objects will hang on to their Views, it is possible to change the view's content object. This is most likely used by applications that use the VisContent rather than the Process object as their content. See Setting the Content .
  • Defining Children of the view
    Every view may have UI objects designated as its children. These objects may appear at any edge of the view. By defining special scroller objects as children of the view, you can alter their placement; for example, you could have the vertical scroller always appear on the left rather than whatever is the default (typically the right). See Children of the View .

GenView: 1.3 GenView Overview: The GenViewControl Object

The GenView, like many objects in the UI, has its own UI controller object to further simplify its use. Though you can implement anything in the controller on your own, it's much easier simply to include a GenViewControl object and let it control the View features for you.

GenViewControlClass is described after the GenView; you must understand the GenView before you can take full advantage of the controller. See The GenViewControl for complete details.

GenView: 2 Getting Started: View Basics

As stated earlier, this chapter will demonstrate how an application would display a graphic document in a scrollable window. You can look at the Hello World and VisSamp sample applications for examples of the view's features and how to manipulate them. The entire code listing of the Hello World application is located in the Hello World chapter.

This section is meant to show you the basic requirements for implementation of a view. It begins with a review of the concepts employed by the GEOS graphics system and then describes how to define a GenView in your Goc code. It also shows how to handle important messages sent by the view.

Graphics System Review

Defining the Basic View

Handling View Messages

GenView: 2.1 Getting Started: View Basics: Graphics System Review

The GEOS graphics system is explained in full in the Graphics Environment chapter. Some of it is reviewed here, although you should read the graphics discussions to fully understand how to draw a graphic document.

The GEOS default document space is represented by signed, 16-bit coordinates relative to a central origin. Coordinates are in 72 points per inch, and documents that use this space can be up to 8192 points, or 113.8 inches, on a side. If your document must be larger than this, you may make use of the "large document" model, which uses 32-bit signed coordinates. Large documents can be over 940 miles on a side.

Every document is drawn to a GState which keeps track of such current state as pen position, color, rotation, and scale. You may apply transformations on the GState to get different rotation and placement within the document space.

To display a graphic document in a view, you must set up the view to have your Process object as its content. This means that your Process object must be able to draw the document, handle messages received from the view, and control the view's behavior as required. The Process object must also know how to support the UI mechanisms with which the view's content object might want to interact (such as the quick-transfer mechanism).

The view defaults to using the large coordinate space for all its operations, though this does not mean only large documents can be displayed in the view. Large documents are represented in a signed, 32-bit (long integer) document coordinate space. This means large documents may be up to 2,147,483,648 points high by 2,147,483,648 points wide (941.5 miles on a side). Because the graphics system is optimized for 16-bit operations, there are special considerations you must take into account when using a view to display large documents.

There are two basic ways to deal with 32-bit coordinates in a 16-bit graphics system. Both involve performing an extended translation on the GState in order to draw in a "local" 16-bit graphics space. The extended translation is performed with GrApplyTranslationDWord() , which takes two 32-bit coordinates and translates the GState's origin to those coordinates. Any 16-bit drawing operations are then automatically done relative to the new origin. All translations are cumulative.

One of the two ways to handle large documents is to apply the extended translation before each drawing operation and untranslate after each operation. This obviously incurs quite a bit of additional overhead.

The other way of handling large documents is to "tile" them, just as the Spooler does when printing large documents on small paper. In this case, you would apply the extended translation once, from then on drawing in 16-bit coordinates relative to the local origin. When you wanted to move to another tile, you would apply another extended translation to the new tile. The drawback to this is that your drawing code has to recognize when the 16-bit local boundaries are being crossed and a transformation is necessary.

GenView: 2.2 Getting Started: View Basics: Defining the Basic View

To use a view to display a document, you should define it in your application's Goc code. The view can legally be a child of GenPrimary, GenDisplay, or GenInteraction. In simple applications, the view will be a child of the application's GenPrimary object. More complex applications will have multiple displays with a view in one or more GenDisplay objects.

The most basic view is resizable and has a white background. You may draw any size document in this view, and it will be clipped properly by the windowing system. At its most basic, the view is not scrollable; however, by setting two attributes as shown in The Basic GenView , the view becomes scrollable in both dimensions.

The most common view is resizable and scrollable with a white background. It generally has boundaries on its scrollable area, called document bounds. The Basic GenView shows this type of view.

Notice that the Goc code for the view is simple; much of the power of the view as a display tool rests in its dynamic flexibility. Once the basic view is defined, you can change its scale, color, scrolling behavior, and document bounds simply by sending messages to it. This dynamic flexibility is also exploited extensively by the GenViewControl.

Code Display 9-1 The Basic GenView

/*
 * The GenView object creates a window in which the application may draw all or
 * portions of its document. Whenever a portion of this window becomes "invalid"
 * (when the window is resized or moved, for example), the view will send
 * MSG_META_EXPOSED to its content (in this case the application's Process object)
 * indicating that a redraw is required. The view keeps track of the clipping
 * boundaries and will automatically display the document properly.
 */
/*
 * The view defined here provides a window with the default white background and
 * normal scrolling and sizing behavior. Its document boundaries are set to 11
 * by 17 inches (the default has all bounds set to zero). The view provides
 * scrollbars automatically, and the application does not need to understand how
 * scrolling is implemented; instead, the view will simply request a redraw of the
 * entire document.
 */
@object GenViewClass MyView = {
    /*
     * The document bounds are set to build an 11 x 17 inch document. Document
     * coordinates are in 1/72 inch (points), and document bounds must also be
     * specified in points. The upper-left corner of the document is placed at
     * (0,0), and the coordinates increase down and right to the far corner
     * (792, 1224).
     */
    GVI_docBounds = {
        0,		/* left bound */
        0,		/* top bound */
        792,		/* right bound */
        1224};		/* bottom bound */
    /*
     * The GVI_horizAttrs and GVI_vertAttrs fields determine the view's
     * scrolling, linking, and sizing behavior in the appropriate dimension.
     * Setting the scrollable attribute in both these fields will make the view
     * create and maintain scrollers (in OSF/Motif, these will appear as
     * scrollbars). All scrolling will happen automatically.
     * These lines are necessary if scrolling is desired.
     */
    GVI_horizAttrs = @default | GVDA_SCROLLABLE;
    GVI_vertAttrs = @default | GVDA_SCROLLABLE;
    /*  Lastly, we must designate which object (our Process object in this case)
     * will receive and handle the message to draw the displayed data
     * (MSG_META_EXPOSED). This object is called the Content object and may
     * be the Process, a GenContent, or a VisContent.
     * Note that no message is defined here as with most action descriptors;
     * the view will send only MSG_META_EXPOSED. You must set this attribute no
     * matter what object is designated as the content; when using a
     * GenContent or a VisContent, the object's name should appear in
     * place of process. */
    GVI_content = process;
}

GenView: 2.3 Getting Started: View Basics: Handling View Messages

After defining the GenView, you must create the method that will handle MSG_META_EXPOSED sent by the view in order to draw in the view's window. This is absolutely necessary, even if your view is not scrollable; the view will send MSG_META_EXPOSED when any part of its display window becomes invalid--this includes when the view is first instantiated and its window is first displayed.

In this simple model, MSG_META_EXPOSED is the only message your application will need to handle relating to the view. If you are managing large documents, then you may also have to handle a few others pertaining primarily to input. A sample handler for MSG_META_EXPOSED --one you can modify and put in your own code--is shown in MSG_META_EXPOSED Handler .

If you are planning on displaying visible objects in the view (as opposed to having your Process object draw graphics), you should instead pay attention to MSG_VIS_DRAW . This message is defined by VisClass and is sent by a VisContent object to itself as the default behavior for MSG_META_EXPOSED . For full information on MSG_VIS_DRAW and visible objects, see the VisClass chapter.

Code Display 9-2 MSG_META_EXPOSED Handler

/* Each time some part of the view window becomes invalid, the view will send a
 * MSG_META_EXPOSED to its content object. When MSG_META_EXPOSED is received,
 * the content must draw the document to a newly-created GState, then pass the
 * GState to the windowing system for drawing to the screen. */
/* The format of this message is
 * 	void MSG_META_EXPOSED(WindowHandle win);
 * The passed value is the window handle of the view window; it will be used in
 * conjunction with the temporary GState for drawing. */
@method	MyProcessClass, MSG_META_EXPOSED {
    /* Set up a temporary GState handle variable for our document. */
    GStateHandle tempGState;
    /* Initialize the GState to the default. */
    tempGState = GrCreateState(win);
    /* Now start a window update. The routine GrBeginUpdate() notifies the window
     * system that we are in the process of drawing in the region in the window
     * that was invalidated. GrBeginUpdate() allows drawing to be clipped to only
     * the region that was invalidated. */
    GrBeginUpdate(tempGState);
    /* Now that we have a GState and have notified the windowing system that we
     * intend to draw, we can draw our document. Here, your method should call
     * whatever routines are appropriate for drawing the entire document.
     * A good hint here relates also to printing: Because printing and drawing to a
     * view are essentially the same operation, you might want to consolidate all
     * your drawing into a single routine that is called by both this method and
     * your printing method. This is what is done here; the routine called
     * MyDrawingRoutine() draws the document we wish displayed. MyDrawingRoutine()
     * will also be called by our printing method with a different GState. */
    MyDrawingRoutine(tempGState);
    /* Now that we have finished drawing the document, we must inform the window
     * system that we are done. Additionally, we must now destroy the temporary
     * GState we used to draw the document. This is essential as not destroying
     * gstates causes small locked blocks to remain on the heap, eroding system
     * performance quickly and eventually making the system run out of handles. */
    GrEndUpdate(tempGState);
    GrDestroyState(tempGState);
}

GenView: 3 Basic View Attributes

The GenView has several instance data fields that it uses to maintain its current status. It also has other attribute fields that determine its scrolling, scaling, and sizing behavior as well as the manner in which it accepts input events. Listed in GenView Instance Data are all the GenView's instance data fields. Each is described later in the chapter. Additionally, each is dynamically changeable with the use of messages defined by GenView.

Code Display 9-3 GenView Instance Data

/* Default settings are shown in the definitions. Other possible settings are
 * shown in a comment after the definition. */
    @instance PointDWFixed				GVI_origin = {{0, 0}, {0,0}};
    @instance RectDWord				GVI_docBounds = {0, 0, 0, 0};
    @instance PointDWord				GVI_increment = {20, 15};
    @instance PointWWFixed				GVI_scaleFactor = {{0, 1}, {0, 1}};
    @instance ColorQuad				GVI_color = {WHITE, 0, 0, 0};
    @instance GenViewAttrs				GVI_attrs = (GVA_FOCUSABLE);
    /* Possible flags for GVI_attrs:
     *	GVA_CONTROLLED			GVA_GENERIC_CONTENTS
     *	GVA_TRACK_SCROLLING			GVA_DRAG_SCROLLING
     *	GVA_NO_WIN_FRAME			GVA_SAME_COLOR_AS_PARENT_WIN
     *	GVA_VIEW_FOLLOWS_CONTENT_GEOMETRY
     *	GVA_WINDOW_COORDINATE_MOUSE_EVENTS
     *	GVA_DONT_SEND_PTR_EVENTS			GVA_DONT_SEND_KBD_RELEASES
     *	GVA_SEND_ALL_KBD_CHARS			GVA_FOCUSABLE
     *	GVA_SCALE_TO_FIT			GVA_ADJUST_FOR_ASPECT_RATIO */
    @instance GenViewDimensionAttrs					GVI_horizAttrs = 0;
    @instance GenViewDimensionAttrs					GVI_vertAttrs = 0;
    /* Possible flags for the dimension attribute records:
     *	GVDA_SCROLLABLE			GVDA_SPLITTABLE
     *	GVDA_TAIL_ORIENTED			GVDA_DONT_DISPLAY_SCROLLBAR
     *	GVDA_NO_LARGER_THAN_CONTENT
     *	GVDA_NO_SMALLER_THAN_CONTENT
     *	GVDA_SIZE_A_MULTIPLE_OF_INCREMENT
     *	GVDA_KEEP_ASPECT_RATIO					*/
    @instance GenViewInkType				GVI_inkType = GVIT_PRESSES_ARE_NOT_INK;
    /* Possible flags for GVI_inkType:
     *	GVIT_PRESSES_ARE_NOT_INK				GVIT_INK_WITH_STANDARD_OVERRIDE
     *	GVIT_PRESSES_ARE_INK				GVIT_QUERY_OUTPUT */
    @instance @optr				GVI_content;
    @instance @optr				GVI_horizLink;
    @instance @optr				GVI_vertLink;
/* The following are the hints and attributes defined for the GenView's vardata.
 * Because GenViewClass is a subclass of GenClass, it inherits all other generic
 * hints as well. */
    @vardata void			HINT_VIEW_LEAVE_ROOM_FOR_VERT_SCROLLER;
    @vardata void			HINT_VIEW_LEAVE_ROOM_FOR_HORIZ_SCROLLER;
    @vardata void			HINT_VIEW_IMMEDIATE_DRAG_UPDATES;
    @vardata void			HINT_VIEW_DELAYED_DRAG_UPDATES;
    @vardata void			HINT_VIEW_REMOVE_SCROLLERS_WHEN_NOT_SCROLLABLE;
    @vardata void			HINT_VIEW_SHOW_SCROLLERS_WHEN_NOT_SCROLLABLE;
    @vardata InkDestinationInfoParams ATTR_GEN_VIEW_INK_DESTINATION_INFO;
    @reloc ATTR_GEN_VIEW_INK_DESTINATION_INFO,
        word_offsetof(InkDestinationInfoParams, IDIP_dest), optr;
@vardata void ATTR_GEN_VIEW_DOES_NOT_ACCEPT_TEXT_INPUT;
    @vardata XYSize			ATTR_GEN_VIEW_PAGE_SIZE;
    @vardata void			ATTR_GEN_VIEW_SCALE_TO_FIT_BASED_ON_X;
    @vardata void			ATTR_GEN_VIEW_SCALE_TO_FIT_BOTH_DIMENSIONS;
    @vardata void			ATTR_GEN_VIEW_DO_NOT_WIN_SCROLL;
/* By default, all GenView objects are targetable. */
    @default GI_attrs = @default | GA_TARGETABLE;

The attributes shown in GenView Instance Data are all dynamically changeable; in fact, many (such as the scale factor) become interesting only when set or changed as a feature of the application (as in the scaling features of GeoWrite and GeoDraw). Each attribute, therefore, is described under its appropriate section of this chapter.

The GVI_attrs Attribute

Dimensional Attributes

Setting the Background Color

The GVI_increment Attribute

GenView: 3.1 Basic View Attributes: The GVI_attrs Attribute

GVI_attrs, MSG_GEN_VIEW_SET_ATTRS, MSG_GEN_VIEW_GET_ATTRS

The GVI_attrs field, as shown in GenView Instance Data , controls many aspects of the view, including several features related to scrolling, input events, and the content object. It is implemented as a bitfield record, and any or all of its attributes may be set at once.

Generally, the attributes specified in this field are not changed at run-time; if your application needs to change or retrieve them, however, it may with the messages MSG_GEN_VIEW_GET_ATTRS and MSG_GEN_VIEW_SET_ATTRS , both of which are shown below.

The attributes that can be set in GVI_attrs are shown in the listing below. The default settings are given in GenView Instance Data .

GVA_CONTROLLED
Set if the GenView is connected to a GenViewControl object and should therefore send out notification as appropriate and add itself to the proper GCN lists.
GVA_GENERIC_CONTENTS
Set if the content is a GenContentClass object. If this bit is set, the mouse grab mode and pointer sending mode are set by the specific UI, overriding whatever is set in the instance data.
GVA_TRACK_SCROLLING
Set if track scrolling is desired. If this is set, the view will send scrolling events to the content object. The content can then alter the scrolling behavior and send the event on to the view.
GVA_DRAG_SCROLLING
Set if drag scrolling is desired. If this bit is set, drag scrolling (when the user drags outside the window bounds, the window scrolls in that direction) is implemented by the view. The content does not have to do anything special; drag scrolling is implemented entirely by the view.
GVA_NO_WIN_FRAME
Set if no frame around the subview window is desired. This is set only if the view is meant to be a display view with its borders obscured (e.g. a text display that has no frame).
GVA_SAME_COLOR_AS_PARENT_WIN
Set if the background color of the view should be whatever color the parent window's background is. This is used in nearly every case where GVA_GENERIC_CONTENTS is also set.
GVA_VIEW_FOLLOWS_CONTENT_GEOMETRY
Set if the view should follow the size of the content in all non-scrollable dimensions. This can only be used if the view and the content are running in the same thread and if the content is not the process object.
GVA_WINDOW_COORDINATE_MOUSE_EVENTS
Set if mouse coordinates passed to the content are in window coordinates (relative to the window's origin) rather than document coordinates. If the document bounds are set outside the 16-bit boundaries of the standard graphics system, then this bit must be set. For full information on mouse event handling, see the Input chapter.

GVA_DONT_SEND_PTR_EVENTS
Set if pointer events should not be sent to the GenView's content. Use this for optimization if pointer events do not need to be sent through the view.

GVA_DONT_SEND_KBD_RELEASES
Set if no keyboard releases should be sent to the content. Applications will set this if they only care about presses, not releases.
GVA_SEND_ALL_KBD_CHARS
Set if the view should pass all keyboard presses and releases to the content before the Specific UI has a chance to interpret them. Normally, the Specific UI will receive keyboard events, filter out the ones it uses (including application-defined accelerators), and then pass the unused events on to the view. This flag will, in essence, override the authority of the Specific UI and therefore is generally highly undesirable. If an application finds a need for this flag, the application is responsible for passing unused characters on to the UI for proper processing. If the unused characters do not get passed on, synchronization problems could result; if an application decides to handle reserved characters, the specific UI's reaction will be unpredictable.
GVA_FOCUSABLE
Set if the view window can gain the input focus. This is set by default and should be set for nearly all views.
GVA_SCALE_TO_FIT
Set if the GenView should operate in "scale to fit" mode. In this mode, the vertical scale factor will automatically be set so the entire document fits vertically within the view window; the horizontal scale factor will then be set based on the vertical. This behavior can be modified by designating a page size (via ATTR_GEN_VIEW_PAGE_SIZE ), by changing the scaling to be based on the horizontal (via the attribute ATTR_GEN_VIEW_SCALE_TO_FIT_BASED_ON_X ), or by setting ATTR_GEN_VIEW_SCALE_TO_FIT_BOTH_DIMENSIONS .
GVA_ADJUST_FOR_ASPECT_RATIO
Set if the GenView should alter its scale factors appropriate to the aspect ratio of the screen.
MSG_GEN_VIEW_SET_ATTRS
void	MSG_GEN_VIEW_SET_ATTRS(
        word		attrsToSet,
        word		attrsToClear,
        VisUpdateMode		updateMode);

This message changes the view's GVI_attrs record to new values.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: attrsToSet A record of GenViewAttrs to set in GVI_attrs .

attrsToClear
A record of GenViewAttrs ; each flag set in this parameter will be cleared in GVI_attrs .
updateMode
A VisUpdateMode indicating when the GenView should be visually updated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_GET_ATTRS
word	MSG_GEN_VIEW_GET_ATTRS();

This message returns a word representing the current GVI_attrs record. To check whether an attribute is set, mask the returned word value against the attribute.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: None.

Return: The current GenViewAttrs record in the GenView's GVI_attrs field.

Interception: Generally not intercepted.

GenView: 3.2 Basic View Attributes: Dimensional Attributes

GVI_horizAttrs, GVI_vertAttrs, MSG_GEN_VIEW_GET_DIMENSION_ATTRS, MSG_GEN_VIEW_SET_DIMENSION_ATTRS

Together, the GVI_horizAttrs and GVI_vertAttrs fields are called the "dimension attributes." They determine the characteristics of the view in each dimension--whether it is scrollable or splittable, whether a scrollbar should be displayed, and whether certain rules should be applied to sizing and scaling.

These attributes, once set, are not frequently changed during execution. Both of these attributes are bitfield records. Each has the same set of possible flags, and these flags are listed below (they are bits in the record GenViewDimensionAttrs ):

GVDA_SCROLLABLE
Set if view is scrollable in the given dimension.
GVDA_SPLITTABLE
Set if the view is splittable in the given dimension. This is not currently implemented but is planned.
GVDA_TAIL_ORIENTED
Set if the document prefers to be displayed at its end. This flag will cause the view to stay at the bottom of the document when the document's length or size is changed--if scrolling to the middle or top, however, the tail orientation will be ignored. Currently, tail orientation is not implemented across threads but may be approximated with track scrolling.
GVDA_DONT_DISPLAY_SCROLLBAR
Set if the scroller for the given dimension should not be displayed even if the dimension is scrollable.
GVDA_NO_LARGER_THAN_CONTENT
Set if the view should never get larger than the content's maximum size in the given dimension (based on the current GVI_docBounds settings). By default, there are no restrictions on the size of the view--it is generally dictated by the view's parent window unless a size hint is applied to the view.
GVDA_NO_SMALLER_THAN_CONTENT
Set if the view should always stay large enough to show the entire content in the given dimension (based on the current GVI_docBounds settings). By default, there are no restrictions on the size of the view.
GVDA_SIZE_A_MULTIPLE_OF_INCREMENT
Set if the view size should always be rounded down to a multiple of the GVI_increment value in the given dimension. If greater control over the sizing behavior is required, a subclass of GenViewClass should be used with an altered handler of MSG_GEN_VIEW_CALC_WIN_SIZE .
GVDA_KEEP_ASPECT_RATIO
Set if the aspect ratio implied by the view's initial length and width should be maintained. If set in GVI_horizAttrs , the view will calculate the width according to the height. If set in GVI_vertAttrs , the view will calculate the height according to the width. This flag is normally used in conjunction with HINT_INITIAL_SIZE . It is an error to set this flag in both dimensions.

If your application must retrieve or set these attributes during execution, it may do so with the messages MSG_GEN_VIEW_GET_DIMENSION_ATTRS and MSG_GEN_VIEW_SET_DIMENSION_ATTRS . Also, to combine and separate the attribute fields, you can use the three macros also shown below.

MAKE_SET_CLEAR_ATTRS
word	MAKE_SET_CLEAR_ATTRS(setAttrs, clearAttrs);
        byte	setAttrs, clearAttrs;

This macro takes two byte values and combines them into a one-word argument. It is used with MSG_GEN_VIEW_SET_DIMENSION_ATTRS , below.

MAKE_HORIZ_ATTRS
byte	MAKE_HORIZ_ATTRS(val);
        word	val;

This macro takes a word-sized value and returns the high byte only (used with MSG_GEN_VIEW_GET_DIMENSION_ATTRS , below, to retrieve the GVI_horizAttrs flag from the returned value).

MAKE_VERT_ATTRS
byte	MAKE_VERT_ATTRS(val);
        word	val;

This macro takes a word and returns the low byte only. It should be used with MSG_GEN_VIEW_GET_DIMENSION_ATTRS , below, to extract the GVI_vertAttrs flag from the returned value.

MSG_GEN_VIEW_SET_DIMENSION_ATTRS
void	MSG_GEN_VIEW_SET_DIMENSION_ATTRS(
        word	horizAttrsToSetClear,
        word	vertAttrsToSetClear,
        VisUpdateMode updateMode);

This message changes the GVI_horizAttrs and GVI_vertAttrs records of the view.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: horizAttrsToSetClear
 A word consisting of two records of GenViewDimensionAttrs flags. The high byte represents the flags to be cleared, and the low byte represents the flags to be set; these flags pertain to the GVI_horizAttrs field.

vertAttrsToSetClear
A word consisting of two records of GenViewDimensionAttrs flags. The high byte represents the flags to be cleared, and the low byte represents the flags to be set; these flags pertain to the GVI_vertAttrs field.
updateMode
A VisUpdateMode type.

Interception: Nothing.

Warnings: Generally not intercepted.

Tips: The macro MAKE_SET_CLEAR_ATTRS is defined above and combines two byte-sized records into one word-sized record for the Goc preprocessor. Its use is shown in the example below.

@call MyView::MSG_GEN_VIEW_SET_DIMENSION_ATTRS(
    MAKE_SET_CLEAR_ATTRS(horizSet, horizClear),
    MAKE_SET_CLEAR_ATTRS(vertSet, vertClear),
    VUM_NOW);
MSG_GEN_VIEW_GET_DIMENSION_ATTRS
word	MSG_GEN_VIEW_GET_DIMENSION_ATTRS();

This message returns the GenView's current dimension attribute records.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: None.

Return: A word consisting of two GenViewDimensionAttrs records. Retrieve the horizontal attributes ( GVI_horizAttrs ) with the macro MAKE_HORIZ_ATTRS and the vertical attributes ( GVI_vertAttrs ) with the macro MAKE_VERT_ATTRS.

Interception: Generally not intercepted.

GenView: 3.3 Basic View Attributes: Setting the Background Color

GVI_color, MSG_GEN_VIEW_SET_COLOR, MSG_GEN_VIEW_GET_COLOR, GVCD_INDEX, GVCD_RED, GVCD_FLAGS, GVCD_BLUE_AND_GREEN, GVCD_BLUE, GVCD_GREEN

A view can have its background color determined by an RGB value, a grayscale value, a CMY value, or as a GEOS color index.

If a view is managing generic objects or is displaying text, it may be most appropriate for the view to appear with the same background color as its parent Primary window. To do this, set the view's GVI_attrs field to the following:

GVI_attrs = @default | GVA_SAME_COLOR_AS_PARENT_WIN;

Otherwise, you will probably want to set the view to something other than the parent's color. To use a standard color index (16-color EGA set), set the GVI_color attribute as follows:

GVI_color = {index, 0, 0, 0};
    /* index is a GEOS color index, one
     * of the Color enumerated type. */

There is no need to set any of the other color fields if you are using an index. The default background color is determined by the specific UI but is normally C_WHITE. (Color indexes are described in the Drawing Shapes chapter.)

If you want to use the RGB scheme for setting the background color, you must know the structure used for defining the color. The view uses the structure ColorQuad to hold all four of the color data fields. This structure is defined as follows:

typedef struct {
    byte			CQ_redOrIndex;
            /* color index or red value */
    ColorFlag			CQ_info;
    byte			CQ_green;		/* green value */
    byte			CQ_blue;		/* blue value */
} ColorQuad;

When using a color index, you only need to set the CQ_info field to the proper index and set the flags to zero. When using other types of value, however, you must set the CQ_info byte to the type and the other fields to their proper values as shown below:

GVI_color = { redVal,					/* red RGB value */
              CF_RGB,					/* RGB flag */
              greenVal,					/* green RGB value */
              blueVal }					/* blue RGB value */

The ColorFlag record determines how the color will appear on the screen. It record may have any of one the following values:

CF_INDEX
This flag indicates the color is a palette index, specified in the CQ_redOrIndex field.
CF_GRAY
This flag indicates the color will be set with a grayscale value as determined by the other three fields.
CF_SAME
This flag indicates the color will not be changed (used for hatch patterns, for example).
CF_CMY
This flag indicates the color is set with CMY values.
CF_RGB
This flag indicates the colors are specified in RGB values in the other three color fields.

As an alternative, you may retrieve, set, or change the view's background color during execution with the messages MSG_GEN_VIEW_SET_COLOR and MSG_GEN_VIEW_GET_COLOR . These messages are shown below. You may also use the macros referenced below to construct and extract color arguments.

MSG_GEN_VIEW_SET_COLOR
void	MSG_GEN_VIEW_SET_COLOR(
        byte		indexOrRed,				/* color index or red value */
        ColorFlag		flags,				/* color flags */
        word		greenBlue);				/* green and blue values */

This message sets the background color of the view. It will not affect the document or printing. The color may be specified either by standard GEOS color index or by RGB value.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: indexOrRed If specifying the color as an index, this is the index value. If specifying as an RGB value, this is the red component of the value.

flags
A record of ColorFlag . If specifying the color as an RGB value, set this to CF_RGB. If specifying the color as a palette index, set this to CF_INDEX.
greenBlue
The green and blue components of the RGB value. If specifying color as an index, set this to zero. If specifying as an RGB value, combine the green and blue components into a single parameter with the macro GVC_GREEN_AND_BLUE.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_GET_COLOR
dword	MSG_GEN_VIEW_GET_COLOR();

This message returns all four fields of the view's background color in a single dword value.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: None.

Return: The four color specifier fields of the GenView's current background color. The individual values may be retrieved with the macros below.

Interception: Generally not intercepted.

GVCD_INDEX
byte	 GVCD_INDEX(val);
        dword	val;

This macro extracts the color index byte from the given dword value. It is intended for use when an index is used.

GVCD_RED
byte	 GVCD_RED(val)
        dword	val;

This macro extracts the red component byte from the given dword value. It is intended for use with RGB values.

GVCD_FLAGS
byte	 GVCD_FLAGS(val)
        dword	val;

This macro extracts the color flags byte from the given dword.

GVCD_GREEN
byte	 GVCD_GREEN(val)
        dword	val;

This macro extracts the green component byte from the given dword.

GVCD_BLUE
byte	 GVCD_BLUE(val)
        dword	val;

This macro extracts the blue component byte from the given dword.

GenView: 3.4 Basic View Attributes: The GVI_increment Attribute

GVI_increment, MSG_GEN_VIEW_GET_INCREMENT, MSG_GEN_VIEW_SET_INCREMENT

Every view has an increment associated with it; this increment may be used in scrolling and sizing the view window. The increment has both a horizontal and a vertical component which are separate from each other. The increment must be specified in document coordinates (points) and is represented by a signed long number. By default, the horizontal increment is set to 20 and the vertical to 15.

The increment values are stored in the GVI_increment field. This field is a structure of type PointDWord . This structure consists of two signed long integers, as follows:

  typedef struct {
    sdword		PD_x;		/* x increment */
    sdword		PD_y;		/* y increment */
} PointDWord;

It is easy to set the increment values in your Goc code, thus:

GVI_increment = {
         horiz,    /* horizontal increment value */
         vert }    /* vertical increment value */

In general, the increment will not change during execution. However, if you find it necessary to do so, you can retrieve and set the increment with MSG_GEN_VIEW_GET_INCREMENT and MSG_GEN_VIEW_SET_INCREMENT .

MSG_GEN_VIEW_SET_INCREMENT
void	MSG_GEN_VIEW_SET_INCREMENT(@stack
        sdword	yIncrement,
        sdword	xIncrement);

This message sets the horizontal and vertical increment values for the view. If the increment had been used before for scrolling, sizing, or scaling, all the same operations will subsequently use the new increment. The two increment values must be in document coordinates (1/72 inch).

Source: Unrestricted.

Destination: Any GenView object.

Parameters: yIncrement The new vertical increment value.

xIncrement
The new horizontal increment value.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_GET_INCREMENT
void	MSG_GEN_VIEW_GET_INCREMENT(
        PointDWord *increment);

This message returns the view's current horizontal and vertical increments in document coordinates.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: increment A pointer to an empty PointDWord structure.

Return: The increment pointer will point to the PointDWord structure containing the horizontal and vertical increment values.

Interception: Generally not intercepted.

GenView: 4 Advanced Concepts and Uses

The GenView's greatest strength is its dynamic flexibility. By sending a message, you can change the scale factor or scroll to any point in a document. You can even define your own scroller objects and objects that can appear at the edges of the view with the scrollers.

This section of this chapter explains the features, messages, and functions of the view that beginners can defer for now; however, the view in general is not complicated and can easily be shaped into what you need.

The Life of a View

Documents in a View

Drawing the Document

Document and View Size

Document Scaling

Children of the View

Scrolling

Monitoring Input

Linking Views

Setting the Content

Internal Utilities

GenView: 4.1 Advanced Concepts and Uses: The Life of a View

When a view is first instantiated, the window system wants to make sure everything happens properly. As a result, the view sends out a series of messages intended to notify the content that the view is opening and that it should be ready to draw its document soon. Most of these messages are intended for the GenContent and VisContent objects, and you will not need to handle them if you designate your Process object as the view's content. Similarly, when the view is shutting down, it notifies its content of the fact that it is closing. Again, the Process does not typically need to handle these messages.

Handling View Messages

MSG_META_CONTENT_SET_VIEW, MSG_META_CONTENT_VIEW_ORIGIN_CHANGED, MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED, MSG_META_CONTENT_VIEW_WIN_OPENED, MSG_META_CONTENT_VIEW_OPENING, MSG_META_CONTENT_VIEW_SIZE_CHANGED

When a GenView is instantiated, the UI takes care of most of the work. However, the view will send several messages to its new content in order to notify it that the view is opening.

The first message sent will be a MSG_META_CONTENT_SET_VIEW , which you may intercept if you need to know the view's object pointer (optr).

The content will then receive a series of other messages indicating the view's initial state. These messages are also sent whenever the view changes its state--for example, when the user moves a scrollbar, the view's origin will change. In general, your Process will not need to handle these. If you are using one of the content objects, you may need them. They are detailed below (for full reference information on these messages, see the VisContent chapter):

  • MSG_META_CONTENT_VIEW_ORIGIN_CHANGED
     This message indicates the initial origin of the view. See Current Origin for more information.
  • MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED
     This message indicates the original scale factor of the view. If you are displaying large documents, you will need to know this in order to handle input events.
  • MSG_META_CONTENT_VIEW_WIN_OPENED
     This message indicates that the view window is being created. Unless you plan on keeping track of several open views, you will not need to handle this message. It is sent before the window is actually opened; this means that the view may or may not be mapped, and you should not draw your document in response to this message. You should instead wait until you receive a MSG_META_EXPOSED . Some applications may intercept this message to get the window handle of the GenView and cache it for future use (until the window is closed).
  • MSG_META_CONTENT_VIEW_OPENING
     This message indicates that the view has received a MSG_VIS_OPEN and that the content will soon be put on the screen. Your Process object will not likely handle this message, though some special VisContents may in order to prepare information needed before drawing.
  • MSG_META_CONTENT_VIEW_SIZE_CHANGED
     This message indicates a change in size of the view--your Process object will not need to intercept this message unless you plan on using the view's width and height for something special.

After the view has been created and opened and is on the screen, it will send a MSG_META_EXPOSED to the content. At this time, the content must make sure the document gets drawn properly (see Drawing the Document ). No special handler is required when a view starts up; the MSG_META_EXPOSED is the same as would be received normally.

When the View Shuts Down

MSG_META_CONTENT_VIEW_CLOSING, MSG_META_CONTENT_VIEW_WIN_CLOSED, MSG_META_CONTENT_SET_VIEW

When a view is about to be closed, a MSG_META_CONTENT_VIEW_CLOSING is sent to the content to notify it of the shutdown. This message is primarily used by VisContentClass so it can send MSG_VIS_CLOSE to itself and all its children.

When the view is being destroyed in addition to being closed, the content will receive a MSG_META_CONTENT_VIEW_WIN_CLOSED . You will only need to handle this message if you have cached the view's window handle. If so, your application should throw out the old window handle to avoid any future drawing to the nonexistent window.

The content will then receive a MSG_META_CONTENT_SET_VIEW again (see above), this time with a null value passed as the view's optr. As with the MSG_META_CONTENT_SET_VIEW received when the view started up, your Process object will not need to intercept this message.

GenView: 4.2 Advanced Concepts and Uses: Documents in a View

The coordinate space is the graphics space owned by your geode for drawing. Your actual data may occupy any portion of it, but the view considers the entire space to be valid. However, you can constrain the view to a certain portion of the coordinate space--for example, if your data only fills 8.5 inches by 11 inches, you can instruct the view to scroll only in that 8.5 by 11 area (or any smaller or larger portion of it).

Document Coordinates

Applications do all their drawing to a device-independent coordinate space. Document coordinates occur in real-world points (72 points per inch). The default graphics system is 8192 points square, with the origin in the middle. The maximum size of a default document is about 113.8 inches on a side. GEOS uses a "large" graphics space as well, represented by 32-bit coordinates that allow documents up to 940 miles on a side. The GenView's coordinates are all given in these 32-bit values to accommodate either the large or the default system.

Your document's data may fill any or all of the coordinate space. Most small documents will have their upper-left corners at the origin and will occupy only a small portion of a 16-bit coordinate space.

Current Origin

GVI_origin, MSG_GEN_VIEW_SET_ORIGIN, MSG_GEN_VIEW_GET_ORIGIN, MSG_GEN_VIEW_SET_ORIGIN_LOW

The GenView must know where in the coordinate space it is located. To do this, it maintains an origin--the upper left coordinates of the window's scrollers. That is, the origin is the upper-left position in the document. The origin is stored in the field GVI_origin , a PointDWFixed structure containing two DWFixed structures. The PointDWFixed structure definition is shown below:

  typedef struct {
    DWFixed		PDF_x;		/* x origin coord */
    DWFixed		PDF_y;		/* y origin coord */
} PointDWFixed;

The view notifies its content each time the origin is changed with the message MSG_META_CONTENT_VIEW_ORIGIN_CHANGED . Typically, the view will also automatically scroll its window to the new origin when the origin changes. You can override this behavior, forcing the view not to scroll on origin changes, by setting the vardata attribute ATTR_GEN_VIEW_DO_NOT_WIN_SCROLL . This attribute, in effect, disconnects the scrollers from the visible position of the document. The notification message MSG_META_CONTENT_VIEW_ORIGIN_CHANGED will still be sent to the content; the window will not scroll, however.

The default origin of the view is (0,0), but this may be set in your Goc code as follows:

GVI_origin = {
    xPosition,			/* x coordinate of origin */
    yPosition			/* y coordinate of origin */
}

You can also change the origin during execution either by causing the view to scroll or by sending a MSG_GEN_VIEW_SET_ORIGIN . To retrieve the view's current origin, use MSG_GEN_VIEW_GET_ORIGIN .

MSG_GEN_VIEW_SET_ORIGIN
void	MSG_GEN_VIEW_SET_ORIGIN(@stack
        sdword	yOrigin,		/* y coordinate of new origin */
        sdword	xOrigin);		/* x coordinate of new origin */

This message changes the view's current origin, causing it to scroll immediately to the new location. The origin represents the upper-left corner of the view in the scrollable document space and is made up of two signed long integers. Each coordinate must be in document coordinates (points).

Source: Unrestricted.

Destination: Any GenView object.

Parameters: yOrigin The new Y coordinate of the origin.

xOrigin
The new X coordinate of the origin.

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_VIEW_SCROLL , MSG_GEN_VIEW_SCROLL_..., MSG_GEN_VIEW_MAKE_RECT_VISIBLE

MSG_GEN_VIEW_GET_ORIGIN
void	MSG_GEN_VIEW_GET_ORIGIN(
        PointDWord *origin);

This message returns a PointDWord structure containing the current X and Y coordinates of the view's origin.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: origin A pointer to an empty PointDWord structure.

Return: The origin pointer will point to a filled structure containing the X and Y coordinates of the current origin. This corresponds to the GenView's GVI_origin field.

Interception: Generally not intercepted.

See Also: MSG_GEN_VIEW_GET_DOC_BOUNDS , MSG_GEN_VIEW_GET_DOC_SIZE

MSG_GEN_VIEW_SET_ORIGIN_LOW
void	MSG_GEN_VIEW_SET_ORIGIN_LOW(@stack
        sdword		yOrigin,
        sdword		xOrigin);

This low-level scroll message is just like MSG_GEN_VIEW_SET_ORIGIN except that it will not propagate to linked views.

Source: Unrestricted--usually encapsulated by MSG_GEN_VIEW_SET_ORIGIN for later dispatch with MSG_GEN_VIEW_SEND_TO_HLINK or MSG_GEN_VIEW_SEND_TO_VLINK handler.

Destination: Any GenView.

Parameters: yOrigin The new vertical origin of the GenView, in document coordinates. For no change, pass GVSOL_NO_CHANGE.

xOrigin
The new horizontal origin of the GenView, in document coordinates. For no change, pass GVSOL_NO_CHANGE.

Return: Nothing.

Interception: Generally not intercepted.

Structures: GVSOL_NO_CHANGE is equal to 0x8000000.

Document Bounds

GVI_docBounds, MSG_GEN_VIEW_SET_DOC_BOUNDS, MSG_GEN_VIEW_GET_DOC_BOUNDS

Because most documents will not actually require the entire document space, the view has an instance data field that defines the view's scrollable boundaries. This field is a RectDWord structure containing four signed dwords, each of which represents a specific boundary of the document. The RectDWord structure is shown below:

  typedef struct {
    sdword		RD_left;		/* left bound */
    sdword		RD_top;		/* top bound */
    sdword		RD_right;		/* right bound */
    sdword		RD_bottom;		/* bottom bound */
} RectDword;

The document bounds are stored in the view's GVI_docBounds instance field. The defaults are shown in the following code; note that each of the values in the field must be in document coordinates.

GVI_docBounds = {0, 0, 0, 0};					/* zero size */

To change the scrollable document bounds at run-time, send the view a message MSG_GEN_VIEW_SET_DOC_BOUNDS . To retrieve the current document bounds, send MSG_GEN_VIEW_GET_DOC_BOUNDS .

MSG_GEN_VIEW_SET_DOC_BOUNDS
void	MSG_GEN_VIEW_SET_DOC_BOUNDS(@stack
        sdword	bottom,		/* new bottom document bound */
        sdword	right,		/* new right document bound */
        sdword	top,		/* new top document bound */
        sdword	left);		/* new left document bound */

This message sets the view's scrollable document bounds to those passed. Each of the four parameters must be given in document coordinates (points), and all are signed long integers. If the new document bounds cause the view to show invalid coordinates, the view will scroll until it is showing valid document space.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: bottom , right , top , left
 As described in the header above.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_GET_DOC_BOUNDS
void	MSG_GEN_VIEW_GET_DOC_BOUNDS(
        RectDWord *bounds);

This message returns the current boundaries of the view's scrollable document space.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: bounds A pointer to an empty RectDWord structure, to be filled in by the method.

Return: The structure pointed to by bounds will be filled with the current scrollable document boundaries of the GenView, as stored in GVI_docBounds .

Interception: Generally not intercepted.

Current Coordinates in the View

MSG_GEN_VIEW_GET_VISIBLE_RECT

To learn what visible rectangle the view is currently displaying, send it a MSG_GEN_VIEW_GET_VISIBLE_RECT . This message will return the current visible rectangle in document coordinates (a RectDWord structure). If the view is not on the screen, the message will return a structure full of zeros.

MSG_GEN_VIEW_GET_VISIBLE_RECT
void	MSG_GEN_VIEW_GET_VISIBLE_RECT(
        RectDWord *rect);

This message returns the rectangle currently visible in the view, in document coordinates.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: rect A pointer to an empty RectDWord structure to be filled in by the method.

Return: The structure pointed to by rect will contain the bounds of the visible portion of the document. If the view window is not visible on the screen, the structure will contain all zeros.

Interception: Generally not intercepted.

Tips: If your document is particularly complex, you might want to call this message and draw only the visible portions upon receiving either MSG_META_EXPOSED or MSG_VIS_DRAW .

GenView: 4.3 Advanced Concepts and Uses: Drawing the Document

MSG_GEN_VIEW_GET_WINDOW, MSG_GEN_VIEW_REDRAW_CONTENT

Displaying your document inside a view is as easy as drawing the document. Any time part of the view becomes invalid (due to scrolling or window movement, for example), the content will receive a MSG_META_EXPOSED. Your content object must have a handler for MSG_META_EXPOSED . See MSG_META_EXPOSED Handler for an example of handling MSG_META_EXPOSED .

You may also cause your application to redraw the window at any time. However, you will need to either save the window handle when the view is created (see MSG_META_CONTENT_VIEW_WIN_OPENED ) or retrieve it with MSG_GEN_VIEW_GET_WINDOW . When your content has the window handle, it can create a GState for that window and draw to it as if a MSG_META_EXPOSED had been sent; drawing the changed portion of the document will change the drawing on the screen. Note that the content should not send itself a MSG_META_EXPOSED --this message should be generated only by the window system because the window system will then know which parts of the screen have become invalid. For a self-generated MSG_META_EXPOSED to work, you must also get the view's visible rectangle (with MSG_GEN_VIEW_GET_VISIBLE_RECT , above) and call GrInvalRect() to invalidate the view's window.

A simpler way to force a redraw is to make the GenView redraw its entire document with MSG_GEN_VIEW_REDRAW_CONTENT . One example of when this might be used is a "Refresh" or "Redraw" option; this causes the window to be redrawn in order to clean up anything extra that may be left on the screen. Note that this is an option provided by the GenViewControl object.

MSG_GEN_VIEW_GET_WINDOW
WindowHandle MSG_GEN_VIEW_GET_WINDOW();

This message returns the window handle of the view window. As an alternative, you can store the view's window handle when MSG_META_CONTENT_VIEW_WIN_OPENED is sent upon view startup (see The Life of a View ) and discard it when you receive MSG_META_CONTENT_VIEW_CLOSING .

Source: Unrestricted.

Destination: Any GenView object.

Parameters: None.

Return: The window handle of the GenView's window.

Interception: Generally not intercepted.

See Also: MSG_META_CONTENT_VIEW_WIN_OPENED , MSG_META_CONTENT_VIEW_CLOSING

MSG_GEN_VIEW_REDRAW_CONTENT
void	MSG_GEN_VIEW_REDRAW_CONTENT();

This message causes the GenView to redraw its entire content. This is essentially the same as exposing the entire view window.

Source: Unrestricted--typically the GenViewControl

Destination: Any GenView object.

Interception: Generally not intercepted.

GenView: 4.4 Advanced Concepts and Uses: Document and View Size

Typically, the document's size will be independent of the view's size. The visual relationship between the two is maintained with the use of scrollers. In these situations, the view will size itself according to the size of its parent Primary window. However, you can change the sizing behavior of the view simply by setting attributes or applying hints.

Setting the View's Size

HINT_INITIAL_SIZE, HINT_MINIMUM_SIZE, HINT_MAXIMUM_SIZE, HINT_FIXED_SIZE

Although the view normally sizes itself to fit the specifications of its parent window, you can change this behavior by applying one of four hints or by setting vertical or horizontal attributes.

  • HINT_INITIAL_SIZE
     This hint sets the initial size of the view to a given width and height. The view window will never shrink smaller than the size set with this hint.
  • HINT_MINIMUM_SIZE
     This hint sets the smallest possible size of the view window. The view's initial size is unaffected by this hint except to ensure that the parent Primary opens large enough to accommodate the view window.
  • HINT_MAXIMUM_SIZE
     This hint sets the maximum allowable size of the view window. This may be used in place of or in addition to the document bounds.
  • HINT_FIXED_SIZE
     This hint has the effect of setting initial, minimum, and maximum sizes at once to set a permanent size for the view window.

All the above hints deal with the structure CompSizeHintArgs . This structure allows specification of size in any of a number of different formats including pixels, percentage of screen size, multiples of character widths, number of text lines, and others.

You may also set GVDA_NO_LARGER_THAN_CONTENT and/or GVDA_NO_SMALLER_THAN_CONTENT in the GVI_horizAttrs and GVI_vertAttrs attribute fields to ensure that the view stays with the content's size. These attributes can be set as follows:

GVI_horizAttrs = @default
                | GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;
GVI_vertAttrs = @default
                | GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;

The above lines will ensure that the view will remain exactly the same size as the content (within screen boundaries).

You can also instruct the view window to snap its width or height to a multiple of the current horizontal or vertical increment. To do this, set the GVI_vertAttrs or GVI_horizAttrs attribute GVDA_SIZE_A_MULTIPLE_OF_INCREMENT as below:

GVI_horizAttrs = @default
                | GVDA_SIZE_A_MULTIPLE_OF_INCREMENT;
GVI_vertAttrs = @default
                | GVDA_SIZE_A_MULTIPLE_OF_INCREMENT;

Leaving Room for Scrollers

HINT_VIEW_LEAVE_ROOM_FOR_VERT_SCROLLER, HINT_VIEW_LEAVE_ROOM_FOR_HORIZ_SCROLLER

Occasionally, you may want to leave extra room for scroller objects even when your view will not have scrollers. You can do so with the hints HINT_VIEW_LEAVE_ROOM_FOR_VERT_SCROLLER and HINT_VIEW_LEAVE_ROOM_FOR_HORIZ_SCROLLER . Although these hints will not affect the size of the view's window, they will affect the view's geometry with respect to its parent window.

Adjusting the view's Size

MSG_GEN_VIEW_CALC_WIN_SIZE

When determining its size initially, the view sends itself the message MSG_GEN_VIEW_CALC_WIN_SIZE . This message, while not useful to applications in and of itself, may be subclassed to get special window sizing behavior. When this message is sent, it includes a suggested window size; by subclassing the GenView and altering the functionality of this message, you can return a different suggested size. The new suggestion will be used to set the view's new size, except at very small sizes--the view will make itself large enough to show scrollers and other children if required.

MSG_GEN_VIEW_CALC_WIN_SIZE
SizeAsDWord MSG_GEN_VIEW_CALC_WIN_SIZE(
        word	width,		/* suggested new width of view window */
        word	height);		/* suggested new height of view window */

This message is sent by the view to itself when it is first being initialized. It takes a suggested size and returns a new suggested size, adjusted for specific conditions.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: width The new suggested width of the view window.

height
The new suggested height of the view window.

Return: A dword representing both the height and the width determined by the method. The width occupies the low word, and the height occupies the high word.

Interception: If you require special sizing behavior from your GenView window, subclass this method. Be sure to call the superclass in the handler.

GenView: 4.5 Advanced Concepts and Uses: Document Scaling

GVI_scaleFactor, MSG_GEN_VIEW_GET_SCALE_FACTOR, MSG_GEN_VIEW_SET_SCALE_FACTOR, MSG_GEN_VIEW_SCALE_LOW, ATTR_GEN_VIEW_PAGE_SIZE, ATTR_GEN_VIEW_SCALE_TO_FIT_BASED_ON_X, ATTR_GEN_VIEW_SCALE_TO_FIT_BOTH_DIMENSIONS

Many applications implement scaling to allow users to view and modify their documents at various scale factors. This scaling behavior, apart from the implementation of the view menu, is almost entirely internal to the view. The application simply needs to instruct the view in what scale factor to use; the view will provide the proper geometry and math involved in displaying, scrolling, and resizing your document.

Every view has a scale factor stored as a PointWWFixed structure in the GVI_scaleFactor instance field. The default scale factor is one, or 100 percent. A scale factor of two means that your document will appear twice as large to the user (200 percent normal size). You can set the scale factor in your Goc code and change it with MSG_GEN_VIEW_SET_SCALE_FACTOR .

The PointWWFixed structure contains two fields, each of which is a WWFixed structure. Both WWFixed and PointWWFixed are shown below.

    typedef struct {
    word		WWF_frac;		/* 16 bits decimal */
    word		WWF_int;		/* 16 bits integral */
} WWFixed;
typedef struct {
    WWFixed		PF_x;		/* x scale factor */
    WWFixed		PF_y;		/* y scale factor */
} PointWWFixed;

A scale factor of 2.5 (250%) in both dimensions would be declared as follows:

GVI_scaleFactor = {{5, 2}, {5, 2}};

A scale factor of 100% in the horizontal and 250% in the vertical would be declared as follows:

GVI_scaleFactor = {{0, 1}, {5, 2}};

To change the scale factor at run-time (for example, when a user clicks on a Zoom button), send MSG_GEN_VIEW_SET_SCALE_FACTOR to the view, passing the proper scale factors. Additionally, you may choose a point on the screen around which to scale. You may also retrieve the current scale factor of a view with the message MSG_GEN_VIEW_GET_SCALE_FACTOR . Note that the GenViewControl has many interactive scaling and zoom features and tools available.

Another way to ensure a certain point is on the screen after scaling is to force it there by causing the view to scroll after scaling. You can suspend the window update, scale the view, then cause the view to scroll with a MSG_GEN_VIEW_MAKE_RECT_VISIBLE . When you then unsuspend the window update, the view will draw with the proper point in the proper place. See Scrolling for an in-depth discussion of scrolling the view.

In addition to the dynamic scaling described above, you can have your view scale itself automatically to fit the entire document in its window. To do so, set the GVI_attrs flag GVA_SCALE_TO_FIT. This will cause the view to scale itself so the entire document fits vertically in the view; the horizontal scale factor will change to correspond with the new vertical scale factor. You can alter this behavior with three different vardata attributes:

ATTR_GEN_VIEW_PAGE_SIZE
This attribute takes an argument of type XYSize (shown below). The view will scale itself to fit this page size rather than its entire document size.
  typedef struct {
    word		XYS_width;		/* width of page */
    word		XYS_height;		/* height of page */
} XYSize;
ATTR_GEN_VIEW_SCALE_TO_FIT_BASED_ON_X
This attribute causes the view to scale itself based on the horizontal size rather than the vertical size. The vertical scale factor will follow the horizontal scale factor.
ATTR_GEN_VIEW_SCALE_TO_FIT_BOTH_DIMENSIONS
This attribute causes the view to scale both dimensions independently when scaling to fit. The horizontal and vertical scale factors may end up different.
MSG_GEN_VIEW_SET_SCALE_FACTOR
void	MSG_GEN_VIEW_SET_SCALE_FACTOR(@stack
        sdword	yOrigin,			/* New Y origin coordinate after scaling */
        sdword	xOrigin,			/* New X origin coordinate after scaling */
        ScaleViewType scaleType,				/* Determines Positioning after scaling */
        WWFixedAsDWord yScaleFactor,						/* New scale factor on Y axis */
        WWFixedAsDWord xScaleFactor);						/* New scale factor on X axis */

This message changes the view's scale factor, causing it to redraw at the new scale. The view will continue to display your document at the new scale until it is either shut down or instructed to change the scale factor again.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: yOrigin New vertical origin of the view window after scaling, if SVT_AROUND_POINT is passed in scaleType . Ignored otherwise.

xOrigin
New horizontal origin of the view window after scaling, if SVT_AROUND_POINT is passed in scaleType . Ignored otherwise.
scaleType
A value of ScaleViewType indicating how the view is to react after scaling. The three allowable values are described below, under "Structures."
yScaleFactor
The new vertical scale factor. To create a WWFixedAsDWord structure, use the macro MakeWWFixed() .
xScaleFactor
The new horizontal scale factor. To create a WWFixedAsDWord structure, use the macro MakeWWFixed() .

Return: Nothing.

Interception: Generally not intercepted. If you want to alter scaling behavior, you may intercept this message to change the parameters. Be sure to call the superclass at the end of the handler.

Structures: The type ScaleViewType defines the way the view will be scaled. It is an enumerated type with three possible values:

SVT_AROUND_UPPER_LEFT
Scale the view around the upper-left corner, and keep the origin the same before and after scaling. The xOrigin and yOrigin parameters will be ignored.
SVT_AROUND_CENTER
Scale the view around its center, keeping the point that is central in the view before scaling in the center after scaling. The xOrigin and yOrigin parameters will be ignored.
SVT_AROUND_POINT
Scale the view around the document point whose coordinates are provided in yOrigin and xOrigin , making sure the new coordinates are set as the view's new origin.
MSG_GEN_VIEW_GET_SCALE_FACTOR
void	MSG_GEN_VIEW_GET_SCALE_FACTOR(
        GetScaleParams *retValue);

This message returns the current scale factors of the view.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: retValue A pointer to an empty GetScaleParams structure to be filled in by the method.

Return: The structure pointed to by retValue will be filled with the current scale factor of the view.

Interception: Generally not intercepted.

Structures: The GetScaleParams structure is made up of two WWFixedAsDWord values. The high word of each represents the fractional (decimal) portion of the scale factor, and the low word represents the integral portion. The GetScaleParams structure definition is shown below:

typedef struct {
    WWFixedAsDWord			GSP_yScaleFactor;			/* y */
    WWFixedAsDWord			GSP_xScaleFactor;			/* x */
} GetScaleParams;
MSG_GEN_VIEW_SCALE_LOW
void	MSG_GEN_VIEW_SCALE_LOW(@stack
        sdword		yOrigin,
        sdword		xOrigin,
        ScaleViewType		scaleType,
        WWFixedAsDWord		yScaleFactor,
        WWFixedAsDWord		xScaleFactor);

This low-level message is similar to MSG_GEN_VIEW_SET_SCALE_FACTOR except that it does not propagate to linked views. It is generally encapsulated by the GenView when another scaling message is received, then dispatched to linked views later.

Source: Sent by the GenView to itself.

Destination: Any GenView object.

Parameters: See MSG_GEN_VIEW_SET_SET_SCALE_FACTOR .

Return: Nothing.

Interception: Should not be intercepted.

GenView: 4.6 Advanced Concepts and Uses: Children of the View

HINT_SEEK_X_SCROLLER_AREA, HINT_SEEK_Y_SCROLLER_AREA, HINT_SEEK_LEFT_OF_VIEW, HINT_SEEK_RIGHT_OF_VIEW, HINT_SEEK_TOP_OF_VIEW, HINT_SEEK_BOTTOM_OF_VIEW

Children of GenView objects always appear at a specified edge of the view; you may decide which children appear on the top, left, right, or bottom, but all children must appear along one of the view's edges. Additionally, you may request that the children be placed next to the view's scrollers; for example, if you wanted a page number to appear at the foot of the view, you might define a GenGlyph that seeks the horizontal scroller.

Each child's placement is determined independently of the others. Each child, therefore, must have a hint that describes where in relation to the view it would like to appear. If the requested configuration is supported by the specific UI, the object will appear where requested. If the specific UI does not support the desired configuration, the child will likely appear below the view. The view may have more than one child. If no hint of location is given for any child, that child will appear with the horizontal scroller.

The location hints (defined by GenClass ) available to a GenView's children include

  • HINT_SEEK_X_SCROLLER_AREA
     The object should be placed with the horizontal scroller object.
  • HINT_SEEK_Y_SCROLLER_AREA
     The object should be placed with the vertical scroller object.
  • HINT_SEEK_LEFT_OF_VIEW
     The object should be forced to the left side of the view.
  • HINT_SEEK_RIGHT_OF_VIEW
     The object should be forced to the right side of the view.
  • HINT_SEEK_TOP_OF_VIEW
     The object should be forced to the top edge of the view.
  • HINT_SEEK_BOTTOM_OF_VIEW
     The object should be forced to the bottom edge of the view.

You may also specify your own scroller objects anywhere below the view in the generic object tree. This is described in the section on Scrolling, below.

GenView: 4.7 Advanced Concepts and Uses: Scrolling

When a document is larger than the view window, the user must have some means of navigation around it. The most common implementation of this is scrolling. In most situations, scrolling will be automatic when you use a view. However, you can modify the view's scrolling behavior.

Removing the Scrollbars

HINT_VIEW_REMOVE_SCROLLERS_WHEN_NOT_SCROLLABLE, HINT_VIEW_SHOW_SCROLLERS_WHEN_NOT_SCROLLABLE

You can determine when scrollbars will be visible on your GVDA_SCROLLABLE view in several ways. One is to set the GVDA_DONT_DISPLAY_SCROLLBAR attribute, either dynamically or in your .goc file, to force a scrollbar not to be displayed. Another is to allow the user to use the GenViewControl to turn the scrollbars on or off.

A third is to allow the GenView to update itself dynamically, by using the hints HINT_VIEW_REMOVE_SCROLLERS_WHEN_NOT_SCROLLABLE and HINT_VIEW_SHOW_SCROLLERS_WHEN_NOT_SCROLLABLE .

The first causes the view to remove the scroller objects when scrolling is not needed (i.e. when the entire content or document fits inside the view's window). The second causes the view to show scrollers in all cases, as long as the view is GVDA_SCROLLABLE and GVDA_DONT_DISPLAY_SCROLLBAR is not set.

Making the View Scrollable

Views, by default, are not scrollable. To make your view scrollable, set the GVDA_SCROLLABLE attribute in both the GVI_horizAttrs and GVI_vertAttrs records. It is easiest to do this in your Goc code, as follows:

GVI_horizAttrs = @default | GVDA_SCROLLABLE;
GVI_vertAttrs = @default | GVDA_SCROLLABLE;

You can also set these attributes during execution by sending the message MSG_GEN_VIEW_SET_DIMENSION_ATTRS to the view. See Dimensional Attributes .

Normal Scrolling

There are three basic ways scrolling can be initiated: First, the user can click on a special scroller object. Second, the user can initiate "drag scrolling" by clicking within the view and dragging outside the window's bounds. Third, the view's content object can request a scrolling action. Only the first of these three is activated by default.

When a scrollable view is first created, scroller objects are also instantiated. This is automatic and for the most part transparent to the geode. The scroller objects may be customized or placed in a certain spot in the generic tree: to learn how to do this, see Customizing the Scrollers .

Scrollers are generic by nature so they can conform to the specific UI in use; in OSF/Motif, for example, scrollers are manifested as scrollbars that position themselves at the right and bottom sides of the view. However, other specific UIs might implement dials, sliders, or some other gadgets for scrolling.

In normal scrolling, the user provides input to a scroller (for example, clicking on the up-arrow of a vertical scrollbar in OSF/Motif). The scroller then sends a message to the view indicating that a scroll should take place. The view calculates the new portion of the document and sends a MSG_META_EXPOSED to its content to indicate that the document must be redrawn. The content then draws its document to the view's window handle, and the view takes care of clipping the document and translating it to the screen.

To take advantage of the normal scrolling features, therefore, you only have to make the view scrollable and respond to MSG_META_EXPOSED .

Drag Scrolling

MSG_GEN_VIEW_INITIATE_DRAG_SCROLL, MSG_GEN_VIEW_SET_DRAG_BOUNDS, HINT_VIEW_IMMEDIATE_DRAG_UPDATES, HINT_VIEW_DELAYED_DRAG_UPDATES

In many cases, applications will want to supplement normal scrolling with drag scrolling, in which a user clicks within the view and drags beyond its edge, causing the view to scroll in that direction. Also, some applications may wish to implement scrolling without the use of scrollers--drag scrolling provides a good way of doing this.

Two hints affect how drag scrolling is implemented. HINT_VIEW_IMMEDIATE_DRAG_UPDATES causes the view to be updated constantly during drags. HINT_VIEW_DELAYED_DRAG_UPDATES causes the view to be updated only at the end of drags. The default behavior is to update periodically during the drag in order to provide visual feedback to the user and to avoid excessive overhead in drawing repeatedly.

To implement drag scrolling, your view must be scrollable. It also must have the GVA_DRAG_SCROLLING attribute of the GVI_attrs record set. You can do this in Goc as follows:

GVI_horizAttrs = @default | GVDA_SCROLLABLE;
GVI_vertAttrs = @default | GVDA_SCROLLABLE;
GVI_attrs = @default | GVA_DRAG_SCROLLING;

You can also set the drag scrolling attribute by sending the view the message MSG_GEN_VIEW_SET_ATTRS (see The GVI_attrs Attribute ).

Normally, drag scrolling works only for the select button (in OSF/Motif, the left mouse button). MSG_GEN_VIEW_INITIATE_DRAG_SCROLL allows you to change this. For example, if you received a MSG_META_START_MOVE_COPY (indicating that the user used the direct-action button), you might want to initiate drag scrolling if your application does not support quick-transfer.

Additionally, if you wanted drag scrolling to work only in a portion of your document (for example, if you wanted the user to be able to move an object on the screen but only move it so far), you could set the temporary drag bounds directly after initiating the drag scrolling. This is done by sending the view a MSG_GEN_VIEW_SET_DRAG_BOUNDS .

MSG_GEN_VIEW_INITIATE_DRAG_SCROLL
void	MSG_GEN_VIEW_INITIATE_DRAG_SCROLL();

This message instructs the view to begin drag scrolling until the pressed mouse button is let go by the user. When drag scrolling is enabled, the select button initiates it; this message allows initiation by any mouse button.

Source: Unrestricted--called by an application on a MSG_META_START_ ... mouse event.

Destination: The GenView in which the mouse button was clicked.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SET_DRAG_BOUNDS
void	MSG_GEN_VIEW_SET_DRAG_BOUNDS(@stack
        sdword	bottom,	/* bottom scrolling bound */
        sdword	right,	/* right scrolling bound */
        sdword	top,	/* top scrolling bound */
        sdword	left);	/* left scrolling bound */

This message sets a temporary rectangle in which drag scrolling can operate if the user should not drag scroll across the entire document.

Source: Unrestricted--called by an application on the initiation of drag scrolling.

Destination: The GenView in which the drag scrolling is underway.

Parameters: bottom , right , top , left
 The bounds (in document coordinates) of the rectangle limiting scrolling. These temporary bounds will be used only during the current drag scroll. The restriction will then be lifted.

Return: Nothing.

Interception: Generally not intercepted.

Modifying Scrolling

Many applications may find a need to scroll the view without user input or to modify the scrolling behavior of the view. For example, if your application has a trigger or menu item that brings a certain portion of the document onto the screen, you will need to instruct the view to scroll to that point, or perhaps your application must scroll by different increments depending on the situation.

Scrolling from the Content
MSG_GEN_VIEW_SCROLL_...

All typical messages sent by scrollers to the view are also available for sending by applications and other geodes. These messages are listed below. Note: With the exception of MSG_GEN_VIEW_SCROLL , these messages work only when the view is visibly initialized. This means that a view that is not on the screen will ignore these messages.

MSG_GEN_VIEW_SCROLL
void	MSG_GEN_VIEW_SCROLL(@stack
        sdword	yOffset,		/* amount to scroll vertically */
        sdword	xOffset);		/* amount to scroll horizontally */

This message causes the view to scroll a given amount in both the X and Y directions.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: yOffset The signed vertical amount to scroll.

xOffset
The signed horizontal amount to scroll.

Return: Nothing.

Interception: Generally not intercepted. If you want to alter scrolling behavior, see Tracking the Scrolling .

MSG_GEN_VIEW_SCROLL_TOP
void	MSG_GEN_VIEW_SCROLL_TOP();

Causes the view to scroll to the top of the document.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_PAGE_UP
void	MSG_GEN_VIEW_SCROLL_PAGE_UP();

Causes the view to scroll up one window height.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_UP
void	MSG_GEN_VIEW_SCROLL_UP();

Causes the view to scroll up one increment.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_SET_Y_ORIGIN
void	MSG_GEN_VIEW_SCROLL_SET_Y_ORIGIN(
        sdword	yOrigin);

Causes the view to scroll to a given vertical point and sets the Y component of the origin to the passed value. It keeps the X component of the origin fixed.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_DOWN
void	MSG_GEN_VIEW_SCROLL_DOWN();

Causes the view to scroll down one increment.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_PAGE_DOWN
void	MSG_GEN_VIEW_SCROLL_PAGE_DOWN();

Causes the view to scroll down one window height.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_BOTTOM
void	MSG_GEN_VIEW_SCROLL_BOTTOM();

Causes the view to scroll to the bottom of the document.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_LEFT_EDGE
void	MSG_GEN_VIEW_SCROLL_LEFT_EDGE();

Causes the view to scroll to the document's left edge.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_PAGE_LEFT
void	MSG_GEN_VIEW_SCROLL_PAGE_LEFT();

Causes the view to scroll left one window width.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_LEFT
void	MSG_GEN_VIEW_SCROLL_LEFT();

Causes the view to scroll left one increment.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_SET_X_ORIGIN
void	MSG_GEN_VIEW_SCROLL_SET_X_ORIGIN(
        sdword	xOrigin);

Causes the view to scroll to a given horizontal point and sets the X component of the origin to the passed value. It keeps the Y component of the origin fixed.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_RIGHT
void	MSG_GEN_VIEW_SCROLL_RIGHT();

Causes the view to scroll right one increment.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_PAGE_RIGHT
void	MSG_GEN_VIEW_SCROLL_PAGE_RIGHT();

Causes the view to scroll right one window width.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SCROLL_RIGHT_EDGE
void	MSG_GEN_VIEW_SCROLL_RIGHT_EDGE();

Causes the view to scroll to the document's right edge.

Source: Unrestricted.

Destination: Any GenView object--views not visible will ignore these messages.

Interception: Generally not intercepted.

Making a Rectangle Visible
MSG_GEN_VIEW_MAKE_RECT_VISIBLE

You can easily make any portion of your document visible with the passage of a single message. For example, if you were displaying a map of the United States and the user clicked on a menu item to show the area around Cincinnati, you could easily determine the coordinates of Cincinnati and instruct the view to scroll there.

To make a certain rectangle of the document visible in the view, pass the message MSG_GEN_VIEW_MAKE_RECT_VISIBLE to the view. You can include a percentage to indicate how far through the rectangle the view should scroll--zero percent causing the rectangle to appear just at the near edge of the view and 100 percent causing the rectangle to appear just at the far edge. Fifty percent causes the object to be centered in the view window.

Normally, this message will not cause any scrolling if the rectangle is already on the screen. However, you can indicate that this message should always cause scrolling (useful if you want to center a rectangle that is already partially on the screen).

MSG_GEN_VIEW_MAKE_RECT_VISIBLE
void	MSG_GEN_VIEW_MAKE_RECT_VISIBLE(@stack
        word	yFlags,	/* MakeRectVisibleFlags for y dimension */
        word	yMargin,	/* percentage to scroll onto screen in y */
        word	xFlags,	/* MakeRectVisibleFlags for x dimension */
        word	xMargin,	/* percentage to scroll onto screen in x */
        sdword	bottom,	/* bottom bound of the target rectangle */
        sdword	right,	/* right bound of the target rectangle */
        sdword	top,	/* top bound of the target rectangle */
        sdword	left);	/* left bound of the target rectangle */

This message causes the view to scroll until a given portion of a passed rectangle is visible in the display window. If the rectangle is already partially visible, the view will not scroll unless instructed to do so by the flags arguments. If the view is not visibly initialized, this message will have no effect.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: yFlags A record of flags indicating when and how scrolling occurs. See below.

yMargin
The amount of the rectangle to be scrolled onto the screen in the vertical dimension. This is a percentage of the screen height and is based from the edge of the screen closest to the rectangle. For example, a yMargin of 50% centers the rectangle vertically; a yMargin of 100% puts the rectangle at the far edge of the screen from where it started. The percentages available are shown below.
xFlags
A record of flags indicating when and how scrolling occurs. See below.
xMargin
The amount of the rectangle to be scrolled onto the screen in the horizontal dimension. This is a percentage of the screen width and is based from the edge of the screen closest to the rectangle. For example, an xMargin of 50% centers the rectangle horizontally; an xMargin of 100% puts the rectangle at the far edge of the screen from where it started. The percentages available are shown below.
bottom , right , top , left
The bounds of the rectangle to be made visible. These should be in document coordinates.

Return: Nothing.

Structures: The yFlags and xFlags parameters are of type MakeRectVisibleFlags ; this type has the following values:

MRVF_ALWAYS_SCROLL
This flag indicates that the view should scroll even when the rectangle is already partially visible.
MRVF_USE_MARGIN_FROM_TOP_LEFT
This flag indicates that margin percentages should be taken relative to the top and left edges of the view rather than the edges closest to the defined rectangle.

The percentages passed in yMargin and xMargin have a special format. You can use a predefined constant, or you can pass custom percentage. To use your own percentage, multiply the decimal representing the desired percentage (e.g. ".50" represents 50 percent) by the hexadecimal number 0xffff; or, instead of doing that math, you can use one of the following system-provided constants:

MRVM_0_PERCENT
MRVM_25_PERCENT
MRVM_50_PERCENT
MRVM_75_PERCENT
MRVM_100_PERCENT

Interception: Generally not intercepted.

Handling Complex Scrolling Operations
MSG_GEN_VIEW_SUSPEND_UPDATE, MSG_GEN_VIEW_UNSUSPEND_UPDATE

If you cause the view to go through several scrolling operations in rapid order, you may want to suspend the view from sending a MSG_META_EXPOSED after each--it is much better to wait until all the updates have been made before the redrawing occurs.

To suspend a view's updates temporarily, send it the message MSG_GEN_VIEW_SUSPEND_UPDATE . After you have finished sending the scrolling messages, unsuspend the view's updates by sending MSG_GEN_VIEW_UNSUSPEND_UPDATE .

MSG_GEN_VIEW_SUSPEND_UPDATE
void	MSG_GEN_VIEW_SUSPEND_UPDATE();

This message causes the view to suspend sending MSG_META_EXPOSED until it receives MSG_GEN_VIEW_UNSUSPEND_UPDATE . Suspend-unsuspend pairs can be nested.

Source: Unrestricted.

Destination: Any GenView object.

Interception: Generally not intercepted.

MSG_GEN_VIEW_UNSUSPEND_UPDATE
void	MSG_GEN_VIEW_UNSUSPEND_UPDATE();

This message allows the view to send a MSG_META_EXPOSED again after being suspended with MSG_GEN_VIEW_SUSPEND_UPDATE .

Source: Unrestricted.

Destination: Any GenView object.

Interception: Generally not intercepted.

Warnings: It is an error to send this message to a non-suspended view.

Tracking the Scrolling

MSG_META_CONTENT_TRACK_SCROLLING, MSG_GEN_VIEW_SETUP_TRACKING_ARGS, MSG_GEN_VIEW_TRACKING_COMPLETE

For flexibility, the view allows applications to track scrolling as it happens and alter it before it gets implemented. When GVA_TRACK_SCROLLING is set in GVI_attrs , all scrolling events will be sent to the content object before being implemented. Because there are often many scrolling events, you should avoid track scrolling unless truly necessary.

If you are going to track the scrolling, your content object must handle the message MSG_META_CONTENT_TRACK_SCROLLING . This message passes a TrackScrollingParams structure containing all the information needed about the upcoming scrolling event. When you receive it, you must do the following three steps:

  1. Send MSG_GEN_VIEW_SETUP_TRACKING_ARGS to the view
    This message fills in all the information of the TrackScrollingParams structure. You must call this routine to ensure that there are no synchronization problems with the scrolling event.
  2. Alter the scrolling event
    If you choose to alter the scrolling event, change the TrackScrollingParams structure to the appropriate values.
  3. Send MSG_GEN_VIEW_TRACKING_COMPLETE to the view
    This message updates the TrackScrollingParams structure based on your changes, then sends a message to the view with the new scrolling information. The view will then scroll according to your changes.

The TrackScrollingParams structure contains complete information about the scrolling event including context information, the view's width and height, the view's document origin, and the action in progress. It also contains a field called TSP_change , which is a structure of type PointDWord and should, after you change the scrolling, contain an offset from the proposed origin to the new origin you desire. See TrackScrollingParams and Associated Structures for the definition of the structure.

Code Display 9-4 TrackScrollingParams and Associated Structures

/*
 * The TrackScrollingParams structure contains all the necessary information about
 * a given scrolling event. It is made up of several substructures, each of which
 * is shown below.
 */
  /*
 * ScrollAction is an enumerated type, each enumeration of which designates a
 * different type of event. The handler should not change these.
 */
typedef ByteEnum ScrollAction;				/* byte-length enumeration */
/*  SA_NOTHING			* No scrolling action
    SA_TO_BEGINNING			* Scrolling to beginning of document
    SA_PAGE_BACK			* Scrolling back one screen height or width
    SA_INC_BACK			* Scrolling back one increment
    SA_INC_FWD			* Scrolling forward one increment
    SA_DRAGGING			* Drag scrolling is underway
    SA_PAGE_FWD			* Scrolling forward one page
    SA_TO_END			* Scrolling to end of document
    SA_SCROLL			* Generic scrolling message sent
    SA_SCROLL_INTO			* Scrolling while keeping a point on screen
    SA_INITIAL_POS			* Indicating the initial scrolling position.
                * Subsequent scroll messages will be relative to
                * this origin.
    SA_SCALE			* Scaling may cause scrolling
    SA_PAN			* Pan-scrolling is underway. Otherwise
                * identical to SA_SCROLL
    SA_DRAG_SCROLL			* Drag-scrolling, otherwise like SA_SCROLL
    SA_SCROLL_FOR_SIZE_CHANGE				* Scrolling because view size changed
*/
  /*
 * ScrollFlags is a byte record of flags used to determine the type of scrolling
 * taking place and the context of the scroll.
 */
typedef ByteFlags ScrollFlags;
#define	SF_VERTICAL		0x80	/* Scrolling is vertical if set. If clear,
                 * scrolling is horizontal. Invalid for
                 * ScrollAction types SA_SCROLL_INTO,
                 * SA_SCROLL, and SA_INITIAL_POS. */
#define	SF_ABSOLUTE		0x40	/* Scrolling is to an absolute point. Set for
                 * ScrollAction types SA_TO_BEGINNING,
                 * SA_TO_END, SA_INITIAL_POS, SA_SCROLL_INTO,
                 * SA_DRAGGING, and some SA_SCROLL. */
#define	SF_DOC_SIZE_CHANGE 0x20			/* Scroll resulted from document size change. */
#define	SF_WINDOW_NOT_SUSPENDED 0x10
                /* Flag used internally only. */
#define	SF_SCALE_TO_FIT		0x08	/* Flag used when the View is in scale-to-fit
                 * mode (often changes scrolling behavior). */
#define	SF_SETUP_HAPPENED  0x04			/* Flag for error checking only. */
/*
 * The TrackScrollingParams structure contains several elements, each of
 * which is described in the comments below. All the fields will be filled by
 * MSG_GEN_VIEW_SETUP_TRACKING_ARGS; however, only certain fields will be filled
 * by MSG_META_CONTENT_TRACK_SCROLLING. Therefore, you should always send
 * MSG_GEN_VIEW_SETUP_TRACKING_ARGS in the handler for this message.
 */
 typedef struct {
    ScrollAction		TSP_action;		/* The type of scrolling underway */
    ScrollFlags		TSP_flags;		/* flags shown above */
    optr		TSP_caller;		/* The sender of the scroll message */
    PointDWord		TSP_change;		/* The relative amount being scrolled */
    PointDWord		TSP_newOrigin;		/* The new absolute origin */
    PointDWord		TSP_oldOrigin;		/* The original origin */
    sword		TSP_viewWidth;		/* Current view width */
    sword		TSP_viewHeight;		/* Current view height */
} TrackScrollingParams;

To set TSP_change , you must first understand how the entire TrackScrollingParams structure is used. When a scrolling event is being tracked, three sets of information are needed:

  • The current origin and size of the view. This is represented by the field TSP_oldOrigin .
  • The scrolling event's proposed change to the view's origin. This is represented by the field TSP_newOrigin .
  • The final change to the view's origin after your adjustments have been added. This is represented by a combination of TSP_oldOrigin and TSP_change . You should set TSP_change understanding that it gets added to TSP_oldOrigin to produce the final origin of the view.

Tracking the scrolling, however, can introduce noticeable lag in scrolling speed. If you do not need to track the scrolling, you probably shouldn't. However, if you need to track the scrolling for simple or few changes, you should consider subclassing GenViewClass and altering the functionality of the method for MSG_META_CONTENT_TRACK_SCROLLING . The GenView's handler for this message simply passes the message on to the content.

If you just need to track changes to the origin, subclass GenViewClass and intercept MSG_META_CONTENT_VIEW_ORIGIN_CHANGED .

Code Display 9-5 Handling Track Scrolling

/* This message is sent by the view to its content object when track scrolling is
 * enabled and a scrolling event is begun. If you plan to track the scrolling, you
 * MUST handle this message. The first thing your handler should do is send
 * MSG_GEN_VIEW_SETUP_TRACKING_ARGS to the view. This message fills in the
 * TrackScrollingParams structure. The last thing the handler must do is send
 * MSG_GEN_VIEW_TRACKING_COMPLETE to the view. This message locks your changes
 * in and directs the view to implement them.
 * The format of this message is
 *	void (TrackScrollingParams *args);								*/
@message		MyProcessClass, MSG_META_CONTENT_TRACK_SCROLLING {
    @call (args->TSP_caller)::MSG_GEN_VIEW_SETUP_TRACKING_ARGS(args);
    /* Here you can do whatever you want to args->TSP_change. */
    @call MyView::MSG_GEN_VIEW_TRACKING_COMPLETE(&args);
}
MSG_GEN_VIEW_SETUP_TRACKING_ARGS
void	MSG_GEN_VIEW_SETUP_TRACKING_ARGS(
        TrackScrollingParams *args);

This message takes the TrackScrollingParams structure and fills in remaining fields not passed by MSG_META_CONTENT_TRACK_SCROLLING .

Source: The content object tracking scrolling (in the handler for MSG_META_CONTENT_TRACK_SCROLLING ).

Destination: The calling object, found in args->TSP_caller .

Parameters: args A pointer to the mostly complete structure of TrackScrollingParams , to be filled in by the method.

Return: The structure pointed to by args will be filled.

Structures: See TrackScrollingParams and Associated Structures .

Interception: Generally not intercepted.

MSG_GEN_VIEW_TRACKING_COMPLETE
void	MSG_GEN_VIEW_TRACKING_COMPLETE(
        TrackScrollingParams *args);

This message sets the altered scrolling arguments and makes them official, instructing the view to implement them.

Source: The content object tracking scrolling (in its handler for MSG_META_CONTENT_TRACK_SCROLLING ).

Destination: The calling object, found in args->TSP_caller .

Parameters: args A pointer to the completed structure of TrackScrollingParams .

Return: Nothing.

Structures: See TrackScrollingParams and Associated Structures .

Interception: Generally not intercepted.

MSG_META_CONTENT_TRACK_SCROLLING
void	MSG_META_CONTENT_TRACK_SCROLLING(
        TrackScrollingParams *args);

This message is sent by the GenView to its content if the content has requested track-scrolling (with GVA_TRACK_SCROLLING set in the GenView's instance data).

Source: Sent by a GenView object to its content when a scrolling event occurs and the view has GVA_TRACK_SCROLLING set.

Destination: The content of the GenView.

Parameters: args A pointer to a TrackScrollingParams structure.

Return: Nothing.

Structures: See TrackScrollingParams and Associated Structures .

Interception: Any content that tracks scrolling must handle this message. It should send MSG_GEN_VIEW_SETUP_TRACKING_ARGS and MSG_GEN_VIEW_TRACKING_COMPLETE to the object specified in TSP_caller .

Customizing the Scrollers

HINT_VALUE_X_SCROLLER, HINT_VALUE_Y_SCROLLER

Although a view's scrollers should remain generic to maintain UI consistency, you may set your own GenValue objects to be the scrollers of your view. This allows special placement of the scrollers not only around the view but also within the generic object tree.

Before creating its own scrollers, the view will search for any objects below it in the generic object tree that have the hint HINT_VALUE_X_SROLLER or HINT_VALUE_Y_SCROLLER . For example, if you wanted to put your vertical scroller on the left of the view rather than on the right, you could add the following code:

@object GenViewClass MyView = {
    /* other view attributes */
    GI_comp = MyViewScroller;
}
@object GenRangeClass MyViewScroller = {
    HINT_Y_SCROLLER;
    HINT_SEEK_LEFT_OF_VIEW;
}

Custom scrollers do not need to be direct children of the view; instead, they may be any number of generations below the view in the generic object tree.

GenView: 4.8 Advanced Concepts and Uses: Monitoring Input

Often, applications will want to receive input--keyboard, mouse, or pen events--through the view. While keyboard events are fairly straightforward, receiving mouse and pen events can be somewhat more complicated. Depending on the types of events you choose to receive, however, input handling can be quite simple.

Mouse Events

Many applications will want to receive mouse events through the view. By default, the view's content will receive all pointer events, whether or not a mouse button has been pressed. However, you can determine which types of events you want to receive by setting the appropriate attributes in the GVI_attrs field:

  • GVA_DONT_SEND_PTR_EVENTS
    This overrides the default and ensures that no mouse (pointer) events are passed on to the content. This is used for optimization when your content will not interact with the user directly.
  • GVA_WINDOW_COORDINATE_MOUSE_EVENTS
    This changes the format of the mouse event as received by the content object and is used with large documents. See below for more information about handling input to large documents.

Both of the above attributes may be set either in your geode's Goc code or with MSG_GEN_VIEW_SET_ATTRS . To retrieve which attributes are set, use MSG_GEN_VIEW_GET_ATTRS . Due to overhead incurred by passing input events, you should only set the attributes you need. (See The GVI_attrs Attribute for more detail on these messages.)

Handling 32-Bit Input Events

Because normal pointer events use the standard GEOS 16-bit coordinate system, large documents must be able to translate these 16-bit events into 32-bit coordinates. The GenView makes this easy for you.

First, set the attribute GVA_WINDOW_COORDINATE_MOUSE_EVENTS in the GVI_attrs record. This will change the format of pointer data passed from the view to the content object. The new format will contain offset coordinates describing the pointer's position relative to the view's screen origin (in screen coordinates).

When you receive one of these events (called "large" mouse events), you can apply the simple formula shown in the figure below to get 32-bit document coordinates. Once you have the coordinates of the pointer event, you can deal with it appropriately.

To keep track of the view's origin and scale factor, you should intercept the messages MSG_META_CONTENT_VIEW_ORIGIN_CHANGED and MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED

Losing Mouse Grabs

During certain operations--for example, quick-transfers--your geode will have the mouse grab. When it loses the grab, it will be notified with a MSG_META_CONTENT_LOST_GADGET_EXCLUSIVE .

Supporting Quick-Transfer
MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER

When using a view and supporting the quick-transfer mechanism, the view will grab the mouse when a quick-transfer operation is invoked. Conclusion of the quick-transfer operation, requires, however, that other objects know when the mouse crosses their bounds; otherwise, no other object in the system could ever become a destination for the transfer.

To relinquish the mouse grab while continuing the transfer operation, you must send MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER to the view. The view will then let other objects get pointer events, thus letting them know when they become a potential destination for the transfer.

MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER
void	MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER();

This message indicates to the view that it should relinquish the mouse grab to allow a quick-transfer operation to continue. Pointer events will then be sent to other objects in the system, allowing them to accept or reject the transfer.

Source: The object with the mouse grab in the view.

Destination: The GenView displaying the caller object.

Interception: Generally not intercepted.

Keyboard Events

By default, all keyboard events not handled by the Specific UI library are passed through to the content when the view has the keyboard focus. Only views with the GVA_FOCUSABLE flags set in GVI_attrs will ever gain the focus (this is the default). Many character strokes are used for keyboard navigation or as mnemonics or accelerators and will therefore be intercepted by the Specific UI. Those that aren't will be passed on to your content.

You can set up your content to receive all keyboard events before the Specific UI has a chance to intercept them. This can have unpredictable results, however, and should be avoided unless absolutely necessary. To set up your content in this manner, set the flag GVA_SEND_ALL_KBD_CHARS in the view's GVI_attrs field. If you handle keyboard events before passing them on, you must be sure to pass every unused MSG_META_KBD_CHAR on to the UI in the form of MSG_META_VUP_KBD_CHAR . If you do not, undesirable synchronization problems could result.

You can indicate that you do not want to receive keyboard releases by setting the attribute GVA_DONT_SEND_KBD_RELEASES in the GVI_attrs record. This is provided as an optimization to improve keyboard response in applications. Most applications need to know when a key has been pressed but do not care when it has been released.

A better way to handle accelerator characters that don't appear in your UI objects is to subclass GenViewClass and intercept MSG_META_KBD_CHAR ; in the handler, take the characters you want and then call the superclass with @callsuper() to make sure unused events get passed on. As an alternative, you can subclass GenApplicationClass and change the handler for MSG_META_VUP_KBD_CHAR ; in the method, take the events you want and then call the superclass with @callsuper() .

Pen Input and Ink

GVI_inkType, MSG_GEN_VIEW_SET_INK_TYPE, MSG_GEN_VIEW_SET_EXTENDED_INK_TYPE, MSG_GEN_VIEW_RESET_EXTENDED_INK_TYPE, ATTR_GEN_VIEW_DOES_NOT_ACCEPT_TEXT_INPUT

It is suggested that you read the Input chapter, before working with Ink in the view. The GenView accepts Ink input events and can pass them to the content either as ink or as mouse events. How the view determines which type of events are desired is mandated by the value of the instance field GVI_inkType .

In addition, some views that get the focus do not accept text input. By default on many pen systems, focusable GenViews will cause an on-screen keyboard to come up on the assumption that the user may wish to enter text to objects within the view. ATTR_GEN_VIEW_DOES_NOT_ACCEPT_TEXT_INPUT allows you to specify that your view does not accept text input and therefore should not cause a floating keyboard to come up. Non-textual keyboard events (e.g. up-arrow used for scrolling) will still be transmitted to the GenView.

The GVI_inkType field will contain one of the following values (each is an enumeration of GenViewInkType ):

GVIT_PRESSES_ARE_NOT_INK
Mouse press events are not Ink input, and Ink input is not expected by the content. This is the default for views.
GVIT_INK_WITH_STANDARD_OVERRIDE
Mouse press events are considered and expected to be Ink events, but they can be overridden by the user in the normal way.
GVIT_PRESSES_ARE_INK
Mouse press events are considered and expected to be Ink.
GVIT_QUERY_OUTPUT
This value indicates that the content will expect Ink events only in certain contexts. Note that if this attribute is set, the content must handle the MSG_META_QUERY_IF_PRESS_IS_INK message.

The GVI_inkType field may be set either in your Goc code or with the message MSG_GEN_VIEW_SET_INK_TYPE , shown below.

Applications that handle Ink input can also set extended information about the Ink. The type of extended information you can set includes the color of the Ink as it's drawn, the thickness of the Ink brush, etc. Use the messages MSG_GEN_VIEW_SET_EXTENDED_INK_TYPE and MSG_GEN_VIEW_RESET_EXTENDED_INK_TYPE , shown below.

You can also pre-set the extended Ink information with the vardata attribute ATTR_GEN_VIEW_INK_DESTINATION_INFO . This attribute has an extra data structure of type InkDestinationInfoParams , shown below.

typedef struct {
    optr		IDIP_dest;
                /* destination object
                 * for Ink output. */
    word		IDIP_brushSize;
                /* Size of Ink brush.
                 * High byte is x size,
                 * Low byte is y size. */
    byte		IDIP_color;		/* Color of Ink */
    Boolean		IDIP_createGState;
                    /* Boolean indicating
                     * if the Ink should
                     * have its own
                     * GState. */
} InkDestinationInfoParams;
MSG_GEN_VIEW_SET_INK_TYPE
void	MSG_GEN_VIEW_SET_INK_TYPE(
        GenViewInkType inkType);				/* value of GenViewInkType */

This message sets the type of Ink input expected by the view's content.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: inkType The type of Ink input expected.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SET_EXTENDED_INK_TYPE
void	MSG_GEN_VIEW_SET_EXTENDED_INK_TYPE(@stack
        Boolean createGState,
        Color	inkColor,
        word	brushSize,
        optr	destObj);

This message sets the extended Ink type for the GenView. The extended Ink type is stored in the vardata ATTR_GEN_VIEW_INK_DESTINATION_INFO .

Source: Unrestricted.

Destination: Any GenView object.

Parameters: createGState A boolean specifying whether the view should create a new GState for the Ink or not.

inkColor
A palette index to use as the color of the Ink.
brushSize
The brush size, in points, to be used for the Ink.
destObj
The optr of the destination object for Ink input to this view. After the Ink is collected, it will be sent to this object.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_RESET_EXTENDED_INK_TYPE
void	MSG_GEN_VIEW_RESET_EXTENDED_INK_TYPE();

This message resets the extended Ink type to default values.

Source: Unrestricted.

Destination: Any GenView object.

Interception: Generally not intercepted.

Target and Focus

MSG_GEN_VIEW_UPDATE_CONTENT_TARGET_INFO

Some applications may draw some portion of their documents differently if they have the target or focus. The view will pass along the following target/focus messages: MSG_META_CONTENT_ENTER , MSG_META_CONTENT_LEAVE , MSG_META_RAW_UNIV_ENTER , MSG_META_RAW_UNIV_LEAVE , and MSG_META_CONTENT_VIEW_LOST_GADGET_EXCLUSIVE .

If you want your geode to be able to grab the focus, you must set the GVA_FOCUSABLE attribute in the GVI_attrs record.

MSG_GEN_VIEW_UPDATE_CONTENT_TARGET_INFO
void	MSG_GEN_VIEW_UPDATE_CONTENT_TARGET_INFO(
        ViewTargetInfo *targetInfo);

This message is sent to the view from VisContentClass contents whenever target information within the view changes. This is done so that MSG_META_GET_TARGET_EXCL may be used to retrieve target information about the portion of the target hierarchy which is within the content itself. (The UI can't directly call objects running in other threads, so this message is required to update it with the latest information.)

Source: A VisContent within the view.

Destination: The GenView associated with the caller.

Parameters: targetInfo A pointer to a ViewTargetInfo structure, shown below.

Return: Nothing.

Structures: The ViewTargetInfo structure has two fields, as shown:

 typedef struct {
     optr		TR_object;		/* optr of object */
    ClassStruct		*TR_class;		/* class of object */
} TargetReference;
 typedef struct {
     TargetReference			VTI_target;
    TargetReference			VTI_content;
} ViewTargetInfo;

The VTI_target field contains the optr and class pointer of the object in the view that currently has the target exclusive. The VTI_content field contains the optr and class pointer of the view's content object.

Interception: Generally not intercepted.

Setting the Pointer Image

MSG_GEN_VIEW_SET_PTR_IMAGE

If you want to change the pointer's image while it is over your view or while the view has the focus, use MSG_GEN_VIEW_SET_PTR_IMAGE . To use this message, you must create the new pointer image in a sharable memory block, lock the block, and pass the pointer to the locked block. To reset your request (return to the default pointer), send the same message with the pointer zeroed. The pointer image structure is detailed in the Input chapter.

MSG_GEN_VIEW_SET_PTR_IMAGE
void	MSG_GEN_VIEW_SET_PTR_IMAGE(
        optr		pointerDef;					/* Optr of pointer definition */
        PtrImageLevel		level);					/* Image level of pointer */

This message causes the view to set the pointer image to a custom image whenever the pointer is over the view.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: pointerDef The optr of a chunk containing the PointerDef32 structure of the new pointer definition for the GenView (this chunk must be in a sharable memory block). Pass NullOptr for no image request. Pass the handle portion of the optr as a NullHandle and the chunk portion of the optr as a PtrImageValue value (see below).

level
A value of PtrImageLevel . This should be either PIL_GADGET, to allow individual gadgets within the view to set the pointer, or PIL_WINDOW, to set the background cursor of the view. Note that if PIV_UPDATE is passed in pointerDef , the level parameter is not used.

Return: Nothing.

Structures: PointerDef16 has the following structure (see the appropriate reference entry for a full description):

  typedef struct {
    byte		PD_width;
    byte		PD_height
    sbyte		PD_hotX;
    sbyte		PD_hotY;
    byte		PD_mask[STANDARD_CURSOR_IMAGE_SIZE];
    byte		PD_image[STANDARD_CURSOR_IMAGE_SIZE];
} PointerDef16;

Interception: Generally not intercepted.

GenView: 4.9 Advanced Concepts and Uses: Linking Views

GVI_horizLink, GVI_vertLink, MSG_GEN_VIEW_SEND_TO_LINKS, MSG_GEN_VIEW_SEND_TO_VLINK, MSG_GEN_VIEW_SEND_TO_HLINK, MSG_GEN_VIEW_CALL_WITHOUT_LINKS

Views may be linked together either in the horizontal or the vertical dimension. Classed events may then be sent to the view's links, causing the events to propagate through all the links. Most scrolling events will be propagated through the links.

Each view may have one horizontal and one vertical link, though no links are required. The links are stored as optrs in the instance fields GVI_horizLink and GVI_vertLink , and links should be circular. In other words, if three views are linked in a single dimension, the links should be as follows: view one points to view two, view two points to view three, and view three points back to view one.

Views linked in the horizontal will share vertical scroll events; those linked in the vertical will share horizontal scroll events.

When one of the linked views receives a scrolling event, the event will automatically propagate to the view's links. Other events can be sent to all the linked views; by using @record to set up an encapsulated event, you can then send that event to the linked views with any of MSG_GEN_VIEW_SEND_TO_LINKS , MSG_GEN_VIEW_SEND_TO_VLINK , or MSG_GEN_VIEW_SEND_TO_HLINK . If you want to send an encapsulated message to a linked view without propagating the event, use MSG_GEN_VIEW_CALL_WITHOUT_LINKS .

MSG_GEN_VIEW_SEND_TO_LINKS
void	MSG_GEN_VIEW_SEND_TO_LINKS(
        EventHandle event,			/* handle of recorded event */
        optr	originator);		/* optr of view first receiving event */

This message sends a recorded event to the receiving view and then to all the view's links. Any linked view will send this message upon receiving a scrolling event.

Source: A linked view when it receives a scrolling event.

Destination: The linked View sends this to itself; it will then dispatch the passed event to itself and all views linked to it.

Parameters: event The EventHandle of the recorded event to be dispatched to all linked views. Typically this is the scrolling event received by the originator.

originator
The optr of the GenView that originally received the scrolling event and sent this message out.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SEND_TO_VLINK
void	MSG_GEN_VIEW_SEND_TO_VLINK(
        EventHandle event,			/* handle of recorded event */
        optr	originator);		/* optr of view first receiving event */

This message sends a recorded event to the receiving view and then to all the view's vertical links. It works the same as MSG_GEN_VIEW_SEND_TO_LINKS except that it restricts itself to vertically-linked views.

Source: A linked view when it receives a scrolling event, or a handler for MSG_GEN_VIEW_SEND_TO_LINKS .

Destination: The linked View sends this to itself; it will then dispatch the passed event to itself and all views linked to it.

Parameters: event The EventHandle of the recorded event to be dispatched to all vertically linked views. Typically this is the scrolling event received by the originator.

originator
The optr of the GenView that originally received the scrolling event and sent this message out.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SEND_TO_HLINK
void	MSG_GEN_VIEW_SEND_TO_HLINK(
        EventHandle event,			/* handle of recorded event */
        optr	originator);		/* optr of view first receiving event */

This message sends a recorded event to the receiving view, then to the view's horizontal links. It works the same as MSG_GEN_VIEW_SEND_TO_LINKS except that it restricts itself to horizontally-linked views.

Source: A linked view when it receives a scrolling event, or a handler for MSG_GEN_VIEW_SEND_TO_LINKS .

Destination: The linked View sends this to itself; it will then dispatch the passed event to itself and all views linked to it.

Parameters: event The EventHandle of the recorded event to be dispatched to all horizontally linked views. Typically this is the scrolling event received by the originator.

originator
The optr of the GenView that originally received the scrolling event and sent this message out.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_CALL_WITHOUT_LINKS
void	MSG_GEN_VIEW_CALL_WITHOUT_LINKS(
        EventHandle		event,					/* handle of recorded event */
        MessageFlags		messageFlags);					/* flags normally sent with
                             * classed event */

This message sends the recorded event to the view and ensures the event does not get passed on to any of the recipient's links. Because the recorded event is sent to only one GenView, it is called immediately and therefore can have return values or pass pointers.

Source: Unrestricted.

Destination: Any linked GenView object

Parameters: event The EventHandle of the recorded event to be sent to only the recipient GenView.

messageFlags
A record of MessageFlags for the recorded event. If the event should return values, be sure to pass the flag MF_CALL in this record.

Return: Nothing.

Interception: Generally not intercepted.

GenView: 4.10 Advanced Concepts and Uses: Setting the Content

MSG_GEN_VIEW_SET_CONTENT, MSG_GEN_VIEW_GET_CONTENT

The GVI_content field in the GenView instance data determines what object is designated as its content. By default, an application's Process object acts as the content. Because the output field defaults to the Process object, you do not have to explicitly set it unless you plan on using a GenContent or VisContent.

Though not usually used when the Process is the content, it is possible to change the content during execution with MSG_GEN_VIEW_SET_CONTENT . Along with this message you must pass the optr of the object that will become the new content. When the view receives this message, it will shut down the current content just as if the view were being shut down. Then it will initialize the new content as if the view were opening for the first time.

To retrieve the optr of the current content object of a view, send the view MSG_GEN_VIEW_GET_CONTENT .

MSG_GEN_VIEW_SET_CONTENT
void	MSG_GEN_VIEW_SET_CONTENT(
        optr	content);

This message sets the view's GVI_content instance data field to a new optr. The current content will receive messages as if the view were shutting down, and the new content will receive messages as if the view were being created anew. (See The Life of a View .)

Source: Unrestricted.

Destination: Any GenView object.

Parameters: content The optr of the new content object.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_GET_CONTENT
optr	MSG_GEN_VIEW_GET_CONTENT();

This message returns the optr of the object currently set as the view's content.

Source: Unrestricted.

Destination: Any GenView object.

Parameters: None.

Return: The optr currently set as the GenView's content.

Interception: Generally not intercepted.

GenView: 4.11 Advanced Concepts and Uses: Internal Utilities

MSG_GEN_VIEW_DETERMINE_VIS_PARENT

Some messages supported by the view may be used as general utilities. Most of these, however, will never be needed by applications.

MSG_GEN_VIEW_DETERMINE_VIS_PARENT
optr	MSG_GEN_VIEW_DETERMINE_VIS_PARENT(
        optr	child);

This message returns the optr of the visible parent for a GenView's child object. This is used by the GenView's children during its specific-UI building and should not be needed by applications.

Source: A generic child of the GenView.

Destination: The GenView being specifically built.

Parameters: child The optr of the child to get the visible parent of.

Return: The optr of the visible parent.

Interception: Should not be intercepted.

MSG_GEN_VIEW_SEND_NOTIFICATION
void	MSG_GEN_VIEW_SEND_NOTIFICATION();

This message is part of the GenViewControl mechanism and is used almost exclusively by the GenView. It sends the special notification from the GenView to the controller, if needed.

Source: Part of GenView control mechanism.

Destination: Any GenView object.

Interception: Generally not intercepted.

MSG_GEN_VIEW_SET_CONTROLLED_ATTRS
void	MSG_GEN_VIEW_SET_CONTROLLED_ATTRS(
        GenViewControlAttrs		controlAttrs,
        word		scaleFactor);

This message sets attributes controlled by the GenViewControl.

Source: Typically a GenViewControl object, automatically.

Destination: A controlled GenView object.

Parameters: controlAttrs A record of GenViewControlAttrs flags indicating the attributes to be set.

scaleFactor
The new scale factor to be set for the view.

Return: Nothing.

Interception: Generally not intercepted.

GenView: 5 The GenViewControl

GenViewControlClass works with GenView objects to provide a number of features to your user interface. It is a subclass of GenControlClass and works with the GenToolControl to implement tool bars and tool boxes for the view.

For the controller to interact with a GenView, the view must have the GVA_CONTROLLED flag set in its GVI_attrs field. The GenViewControl will interact with all views having this flag set.

The GenViewControl features and tools are shown in GenViewControl Features and Tools . All GenViewControl objects must be placed on the MGCNLT_ACTIVE_LIST and GAGCNLT_SELF_LOAD_OPTIONS GCN lists. The view itself should be on the GAGCNLT_CONTROLLED_GEN_VIEW_OBJECTS list.

Code Display 9-6 GenViewControl Features and Tools

/* The GenViewControl supports the following features. */
  typedef		WordFlags GVCFeatures;
#define	GVCF_MAIN_100			0x4000	/* sets scale to 100% from View menu */
#define	GVCF_MAIN_SCALE_TO_FIT			0x2000	/* allows scale-to-fit in View menu */
#define	GVCF_ZOOM_IN			0x1000	/* zooms in on the document */
#define	GVCF_ZOOM_OUT			0x0800	/* zooms out on the document */
#define	GVCF_REDUCE			0x0400	/* reduces the view's scale factor */
#define	GVCF_100			0x0200	/* sets scale to 100% in View submenu */
#define	GVCF_ENLARGE			0x0100	/* enlarges the view's scale factor */
#define	GVCF_BIG_ENLARGE			0x0080	/* enlarges the scale factor twice */
#define	GVCF_SCALE_TO_FIT			0x0040	/* scales the view to fit the document */
#define	GVCF_ADJUST_ASPECT_RATIO			0x0020	/* adjusts scale for aspect ratio */
#define	GVCF_APPLY_TO_ALL			0x0010	/* applies to all active views */
#define	GVCF_SHOW_HORIZONTAL			0x0008	/* shows/hides the horizontal scroller */
#define	GVCF_SHOW_VERTICAL			0x0004	/* shows/hides the vertical scroller */
#define	GVCF_CUSTOM_SCALE			0x0002	/* allows custom scaling */
#define	GVCF_REDRAW			0x0001	/* refreshes the view window */
/* The GenViewControl provides the following tools. */
typedef		WordFlags GVCToolboxFeatures;
 #define	GVCTF_100			0x1000	/* sets the scale factor to 100% */
#define	GVCTF_SCALE_TO_FIT			0x0800	/* scales the view to fit the document */
#define	GVCTF_ZOOM_IN			0x0400	/* zooms in; enlarges the scale factor */
#define	GVCTF_ZOOM_OUT			0x0200	/* zooms out; reduces the scale factor */
#define	GVCTF_REDRAW			0x0100	/* refreshes the view window */
#define	GVCTF_PAGE_LEFT			0x0080	/* scrolls left/back one page width */
#define	GVCTF_PAGE_RIGHT			0x0040	/* scrolls right/forward one page width */
#define	GVCTF_PAGE_UP			0x0020	/* scrolls up/back one page height */
#define	GVCTF_PAGE_DOWN			0x0010	/* scrolls down/forward one page height */
#define	GVCTF_ADJUST_ASPECT_RATIO 0x0008				/* adjusts scale for aspect ratio */
#define	GVCTF_APPLY_TO_ALL			0x0004	/* applies to all active views */
#define	GVCTF_SHOW_HORIZONTAL			0x0002	/* shows/hides the horizontal scroller */
#define	GVCTF_SHOW_VERTICAL			0x0001	/* shows/hides the vertical scroller */
    /* Default features and tools */
#define GVC_DEFAULT_FEATURES				(GVCF_MAIN_100 | GVCF_MAIN_SCALE_TO_FIT |
                 GVCF_REDUCE | GCVF_ENLARGE | GVCF_BIG_ENLARGE |
                 GVCF_SCALE_TO_FIT | GVCF_ADJUST_ASPECT_RATIO |
                 GVCF_APPLY_TO_ALL | GVCF_SHOW_HORIZONTAL |
                 GVCF_SHOW_VERTICAL | GVCF_CUSTOM_SCALE)
#define GVC_DEFAULT_TOOLBOX_FEATURES (GVCTF_100 | GVCTF_ZOOM_IN | GVCTF_ZOOM_OUT)
#define GVC_SUGGESTED_SIMPLE_FEATURES (GVCF_MAIN_100 | GVCF_MAIN_SCALE_TO_FIT |
                 GVCF_ZOOM_IN | GVCF_ZOOM_OUT)
#define GVC_SUGGESTED_INTRODUCTORY_FEATURES (GVCF_MAIN_100 | GVCF_ZOOM_IN |
                 GVCF_ZOOM_OUT)
#define GVC_SUGGESTED_BEGINNING_FEATURES (GVC_SUGGESTED_INTRODUCTORY_FEATURES |
                 GVCF_MAIN_SCALE_TO_FIT)

GenViewControl Instance Data

Notification Received

GenViewControl Example

Messages Handled

GenView: 5.1 The GenViewControl: GenViewControl Instance Data

In addition to the normal instance data of GenControlClass , GenViewControlClass has four other fields. These are shown in GenViewControl Instance Data along with their default values.

Code Display 9-7 GenViewControl Instance Data

/* The GenViewControl has four instance fields in addition to those inherited
 * from GenControlClass. You may wish to set these to determine the controller's
 * initial configuration, but typically you will not need to. */
    /* Constants used for scaling boundaries */
#define DEFAULT_ZOOM_MINIMUM				 25
#define DEFAULT_ZOOM_MAZIMUM				200
    /* GVCI_minZoom determines the minimum percentage zoom allowed. */
    /* GVCI_maxZoom indicates the maximum zoom percentage allowed. */
    /* GVCI_scale indicates the current view scale factor setting. */
    /* GVCI_attrs indicates which attributes the controller should
     * implement on startup. */
    @instance word			GVCI_minZoom =		DEFAULT_ZOOM_MINIMUM;
    @instance word			GVCI_maxZoom =		DEFAULT_ZOOM_MAXIMUM;
    @instance word			GVCI_scale = 		100;
    @instance GenViewControlAttrs GVCI_attrs =						(GVCA_SHOW_HORIZONTAL |
                         GVCA_SHOW_VERTICAL |
                         GVCA_APPLY_TO_ALL);
    /* Possible GenViewControlAttrs flags:
     *	GVCA_ADJUST_ASPECT_RATIO				GVCA_APPLY_TO_ALL
     *	GVCA_SHOW_HORIZONTAL				GVCA_SHOW_VERTICAL */
    @default GI_attrs = (@default | GA_KBD_SEARCH_PATH);
                /* This adds the view controller to the default
                 * keyboard accelerator search path. */

GenView: 5.2 The GenViewControl: Notification Received

When a view's attributes change and the controller will be affected, the view sends notification to the controller. The type of notification handled by the controller is NT_VIEW_STATE_CHANGE. This notification type will carry with it a block containing a structure of type NotifyViewStateChange , shown in The NotifyViewStateChange Structure .

The NotifyViewStateChange structure contains much more information about the view than just those features affected by the controller. Additional information includes background color, increment, document bounds, Ink information, origin, etc. If you want to add functionality to the GenViewControl, you can set up additional UI gadgetry with the GenControlClass attribute ATTR_GEN_CONTROL_APP_UI and have it change and handle the appropriate attributes.

Code Display 9-8 The NotifyViewStateChange Structure

/* This structure is passed to the GenViewControl object when a feature of the
 * active view changes. It is passed with notification NT_VIEW_STATE_CHANGE. */
typedef struct {
    PointDWFixed			NVSC_origin;			/* absolute origin of the view */
    RectDWord			NVSC_docBounds;			/* document bounds of the view */
    PointDWord			NVSC_increment;			/* the view's increment value */
    PointWWFixed			NVSC_scaleFactor;			/* view's current scale factor */
    ColorQuad			NVSC_color;			/* view's background color */
    GenViewAttrs			NVSC_attrs;			/* view's GVI_attrs record */
    GenViewDimensionAttrs				NVSC_horizAttrs;		/* view's GVI_horizAttrs record */
    GenViewDimensionAttrs				NVSC_vertAttrs;		/* view's GVI_vertAttrs record */
    GenViewInkType			NVSC_inkType;			/* view's GVI_inkType value */
  /* The following four fields are used internally by the GenViewControl. */
    XYSize			NVSC_contentSize;
    XYSize			NVSC_contentScreenSize;
    PointDWord			NVSC_originRelative;
    PointDWord			NVSC_documentSize;
} NotifyViewStateChange;

GenView: 5.3 The GenViewControl: GenViewControl Example

An example of the use of a GenViewControl is shown in An Example of GenViewControl . This example is built on the Hello World application, and only the differences between Hello World and the example are shown. As an exercise, try adding a GenToolControl to Hello World as well.

Code Display 9-9 An Example of GenViewControl

/* This display builds on the Hello World sample application. You should make the
 * changes shown here to that program, compile it, and run it to get a solid feel
 * for how the GenViewControl works and the features it provides.
 * Only the alterations to Hello World are shown here; the basic code can be found
 * in the Hello World chapter. */
/* Add the GenViewControl definition file. */
@include <Objects/gViewCC.goh>
/* HelloApp changes
 * Add a new GCN list type to the application object, and add the HelloViewControl
 * object to the list. This list is typical of system-provided controllers. */
@object GenApplicationClass HelloApp = {
    GI_visMoniker = list { @HelloTextMoniker };
    GI_comp = @HelloPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) =							@HelloPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) =									/* add */
                            @HelloViewControl;
}
/* HelloPrimary changes
 * Add a new menu to the Primary's list of children. This new menu is called
 * HelloViewMenu and is declared below. */
@object GenPrimaryClass HelloPrimary = {
    GI_visMoniker = "Hello Sample Application";							/* unchanged */
    GI_comp = @HelloView, @HelloMenu, @HelloViewMenu;							/* add @HelloViewMenu */
    ATTR_GEN_DISPLAY_NOT_MINIMIZABLE;							/* unchanged */
    HINT_SIZE_WINDOW_AS_DESIRED;							/* unchanged */
}
/* HelloViewMenu declaration
 * Declare a new menu, HelloViewMenu. The GenViewControl object will add its
 * features to this menu. */
@object GenInteractionClass HelloViewMenu = {
    GII_visibility = GIV_POPUP;				/* This makes the GenInteraction a menu */
    GI_comp = @HelloViewControl;				/* Add the GenViewControl as the menu's child */
    ATTR_GEN_INTERACTION_GROUP_TYPE = (GIGT_VIEW_MENU);
                /* Give this menu the default characteristics
                 * of the standard View menu. */
}
/* HelloViewControl declaration
 * Declare the GenViewControl object, HelloViewControl. This needs no extra
 * settings; we will use all the defaults. */
@object GenViewControlClass HelloViewControl = {
}
/* HelloView changes
 * The only change necessary to the GenView itself is to mark it controlled.
 * This entails setting the GVA_CONTROLLED flag in GVI_attrs. */
@object GenViewClass HelloView = {
    GVI_attrs = @default | GVA_CONTROLLED;						/* set the controlled attr */
    GVI_horizAttrs = @default | GVDA_SCROLLABLE | GVDA_NO_LARGER_THAN_CONTENT;
                        /* unchanged */
    GVI_vertAttrs = @default | GVDA_SCROLLABLE | GVDA_NO_LARGER_THAN_CONTENT;
                        /* unchanged */
    GVI_content = process;						/* unchanged */
}

GenView: 5.4 The GenViewControl: Messages Handled

MSG_GEN_VIEW_CONTROL_SET_ATTRS, MSG_GEN_VIEW_CONTROL_SET_MINIMUM_SCALE_FACTOR, MSG_GEN_VIEW_CONTROL_SET_MAXIMUM_SCALE_FACTOR

GenViewControlClass handles several messages that allow its characteristics to be set or modified at run-time. It also has several other internal messages which are not documented here.

MSG_GEN_VIEW_CONTROL_SET_ATTRS
void	MSG_GEN_VIEW_CONTROL_SET_ATTRS(
        GenViewControlAttrs		attrsToSet,
        GenViewControlAttrs		attrsToClear);

This message sets the GenViewControlAttrs in GVCI_attrs. Attributes noted in both parameters will be turned off for the controller.

Source: Unrestricted.

Destination: Any GenViewControl object.

Parameters: attrsToSet Attributes to be turned on should be set.

attrsToClear
Attributes to be turned off should be set.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_CONTROL_SET_MINIMUM_SCALE_FACTOR
void	MSG_GEN_VIEW_CONTROL_SET_MINIMUM_SCALE_FACTOR(
        word	minimumScaleFactor);

This message sets the view controller's minimum allowable scale factor.

Source: Unrestricted.

Destination: Any GenViewControl object.

Parameters: minimumScaleFactor
 The new minimum scale factor (a percentage).

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VIEW_CONTROL_SET_MAXIMUM_SCALE_FACTOR
void	MSG_GEN_VIEW_CONTROL_SET_MAXIMUM_SCALE_FACTOR(
         word	maximumScaleFactor);

This message sets the maximum settable scale factor for the view controller.

Source: Unrestricted.

Destination: Any GenViewControl object.

Parameters: maximumScaleFactor
 The maximum settable scale factor (as a percent).

Return: Nothing.

Interception: Generally not intercepted.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Managing UI Geometry

One of the more difficult aspects of programming a graphically oriented application is positioning the user interface components on the screen. Developers often have to manually place buttons, size dialog boxes, and draw frames and titles. The GEOS Geometry Manager lets you ignore most of these issues.

The geometry manager allows you to ignore position, size, and spacing of your generic UI components; if you use visual objects, the geometry manager can calculate object sizes and positions, dynamically changing them as necessary. Because the geometry manager works at run-time, you have to do very little in the way of screen management. Instead, you can set various attributes and hints to fine-tune your UI's appearance.

1 Geometry Manager Overview
1.1 Geometry Manager Features
1.2 How Geometry Is Managed
2 Arranging Your Generic Objects
2.1 General Geometry Rules
2.2 Orienting Children
2.3 Justifying and Centering Children
2.4 Sizing Objects
2.5 Outlining the Composite
2.6 Using Monikers
2.7 Using Custom Child Spacing
2.8 Allowing Children to Wrap
2.9 Object Placement
3 Positioning and Sizing Windows
3.1 Window Positioning
3.2 Determining Initial Size
3.3 On-Screen Behavior
3.4 Window Look and Feel

Managing UI Geometry: 1 Geometry Manager Overview

The GEOS geometry manager is an algorithmic mechanism which interacts directly with the UI to position and size objects of all types. The geometry manager automatically manages all generic objects through interactions with the specific UI library; you can also use it to manage your visible object trees.

The Geometry Manager is what makes the generic UI possible: Since geometry of UI objects (size and position) is determined by the UI at run-time, generic UI objects do not have to be explicitly defined with certain geometry. With this feature, a specific UI library can set up the UI geometry as it wants.

Your first interaction with the geometry manager will be when you build a generic object tree for your UI. Most hints applied to an object are signals to the geometry manager about how the object should be positioned, sized, or otherwise placed on the screen. These hints are all defined in GenClass , the topmost generic object class, so they are available (though not necessarily useful) to all types of generic objects.

This chapter first describes the features of the geometry manager and how the mechanism fits into the GEOS system as a whole (later in this section). Then it discusses how to manage generic UI objects (using a dialog box as an example) in Arranging Your Generic Objects . For the hints you can apply to window objects, see Positioning and Sizing Windows . For descriptions of managing the geometry of visible object trees and composites, see the VisClass chapter.

Geometry Manager Features

How Geometry Is Managed

Managing UI Geometry: 1.1 Geometry Manager Overview: Geometry Manager Features

The geometry manager, used effectively, can take nearly all the work out of displaying, sizing, and positioning objects on the screen. Many of the tasks you normally would have to do to create even a simple dialog box are taken care of automatically by the geometry manager and the specific UI library. Among the functions the geometry manager performs are the following:

  • Window Positioning and Sizing
    The geometry manager interacts directly with the UI to determine where windows should be placed and what their dimensions should be. You have some control over this with hints, messages, and object instance data, and often you will have to choose a hint to determine the window position or size.
  • Object Sizing
    The size of an object can be determined by the combined sizes of its children, by the limits of its parent, or specifically by other constraints on the object. The geometry manager allows all three types of sizing behavior. The first two are implemented automatically in geometry updates; the third depends on how the application wants to manage the "other constraints."
  • Object Positioning
    The position of an object can be determined either by the object or by the geometry manager. The geometry manager can position objects based on several criteria including the spacing of siblings in a composite, the size and/or location of the parent composite, and the justification hints applied to the object. Visible objects can control very closely how and where they are positioned; generic objects can determine how positioning occurs with various hints.
  • Automatic Geometry Updating
    Typically, when a particular object in a tree is resized, repositioned, added, or removed, the object mechanisms will mark the geometry of both that object and its parent invalid. This will invoke the geometry manager, which will traverse the object tree, determine which objects are affected by the potential geometry change, and then update the size and position of all affected objects up to the top of the tree. This entire process is automatic for generic objects; for visual objects, you may or may not have to mark the object invalid yourself, depending on the depth of custom geometry control you use.

With geometry functions covered by the geometry manager and drawing functions covered by the specific UI library, you have little else to do but specify the structure of your object tree and the hints each object should have. The system does all the dirty work of drawing and managing the objects on the screen, leaving you free to work on your application rather than on its graphical screen representation.

Managing UI Geometry: 1.2 Geometry Manager Overview: How Geometry Is Managed

The geometry manager is one part of the system that draws objects on the screen and redraws them when necessary. This system is called the "visual update mechanism."

The visual update mechanism is invoked any time the geometry or image of an object is marked invalid. If only the image is invalid, typically no geometry calculations will be done. If, however, the geometry of an object is marked invalid, the geometry manager is called in to calculate the new size and position of all the affected objects in the object tree.

Normally, you won't have to do any more than set certain hints (for generic objects) or attribute flags (for visible objects) to get the desired geometry behavior. If, however, you plan on modifying how the geometry of a particular set of objects is set, you will want to understand the sequence of events that happen during a geometry update. Typically, only programmers who are building their own specific UI libraries and those who have complex visual object trees will need to understand the geometry calculation sequence.

The geometry manager's calculation can be thought of as a multiple-pass, recursive algorithm. It traverses the object tree, managing geometry within each composite until it reaches the top-most object that is unaffected by the geometry calculations.

The geometry update process is invoked when an object has its geometry marked invalid. This is determined by the object's visual flags (its VI_optFlags field), VOF_GEOMETRY_INVALID and VOF_WINDOW_INVALID. If either of these flags is set, the object's geometry requires recalculation.

The geometry manager sends MSG_VIS_UPDATE_GEOMETRY to the visible object with invalid geometry. This message determines which objects in the tree are potentially affected by the geometry update, and then it recalculates the tree's geometry.

If the object initially marked invalid is a composite, all its children are automatically included in the update. If the object is a child of a composite, the geometry manager will travel up the tree, marking any parents and siblings that also require recalculation. (Recalculation does not cross VTF_IS_WIN_GROUP boundaries, however. A VTF_IS_WIN_GROUP is any windowed object; geometry calculations generally do not cross windowed objects (move into another window layer) unless explicitly instructed to do so.)

The geometry manager then returns to the bottom-most affected object in the tree and sends it a MSG_VIS_RECALC_SIZE . This message passes the object a suggested size, which will be the same as the object's original size. (The original size is passed as an optimization in case the object does not need its geometry changed; many changes will affect many objects but will actually change only a few.)

The object then returns its new desired size based on the geometry change. If this is the original object marked invalid, the returned size will be the direct result of the geometry change. If this object is a descendent of the invalid object, it may not want to change its geometry at all and may therefore return its current size.

The geometry manager collects the returned sizes of all siblings at a given level and calculates the suggested size of their parent composite. The parent composite is then sent MSG_VIS_RECALC_SIZE with the newly calculated size and returns the size it wants to be as a result. (Some composites will have fixed, maximum, or minimum sizes or may have their sizing tied to other factors; these objects may return a different size.) If the returned size is different from the passed size, the geometry manager arbitrates between the objects. Typically, the composite will be grown large enough (even if fixed or maximum size) to fit all its children.

The geometry manager will go up the tree in this manner, calculating the new geometry of each child and then each composite until it reaches an object that is unaffected by the geometry change. (For example, if a child shrinks but other children cause the composite to remain the same size, the geometry update will stop at the composite because objects higher up are not affected.) If all objects in the tree are affected by the change, the geometry update will stop at the topmost object in the branch (the first VTF_IS_WIN_GROUP object).

If any parent objects disagree with their children on the new size, the geometry manager will arbitrate. The geometry manager may make several passes to suggest width and height changes to both the child and parent before deciding on an ultimate geometry. The geometry manager may alter child spacing, wrap the children, grow the composite, or even clip the children if necessary. A result will quickly be reached, however--the geometry manager will not thrash between the parent and children (unless you subclass MSG_VIS_RECALC_SIZE or MSG_VIS_GET_CENTER and do not pay close attention to how the message is handled).

After the geometry is recalculated up to the first VTF_IS_WIN_GROUP object (or up to the top affected object), a MSG_VIS_NOTIFY_GEOMETRY_VALID will be sent to each of the invalid objects that have VGA_NOTIFY_GEOMETRY_VALID set in their VI_attrs field. A visual update will then be started to draw the new geometry to the screen. Once a branch's geometry has been calculated, the branch will remain unaffected until the next time the geometry of one of its objects is marked invalid.

If a recalculation of a branch's geometry causes a VTF_IS_WIN_GROUP object to change its size or position, the window system will determine the effects. For example, if a VisContent object grows as a result of a geometry change, it will send a MSG_GEN_VIEW_SET_DOC_BOUNDS to the GenView; the geometry recalculation mechanism will not scroll the content-view connection. What happens to the view and its parent window then depends on the attributes of the view. For example, if the view is set to follow the size of its content, it will grow, bumping the size of its parent in the process. If the view is scrollable, it likely will stay the same size.

Managing UI Geometry: 2 Arranging Your Generic Objects

The process of arranging user interface elements for a GEOS application differs drastically from that of traditional user interface programming methods. In most graphical systems, you must manually determine the location of each and every user interface element. For instance, to design a dialog box, you have to figure the location of each text string, button, and list entry. Normally, these items would have to be hard-coded into your application, and often different video resolutions would have to be taken into account.

Not so in GEOS. The generic and specific UI libraries work with the geometry manager to automatically arrange your generic UI objects in the most appropriate way. To describe a dialog box in GEOS, for instance, you simply have to define the objects contained in the dialog box and their relative positions in the generic object tree; GEOS does the rest, determining the position and visual effects of each object. You can even fine-tune the appearance of each object with the use of hints.

General Geometry Rules

Orienting Children

Justifying and Centering Children

Sizing Objects

Outlining the Composite

Using Monikers

Using Custom Child Spacing

Allowing Children to Wrap

Object Placement

Managing UI Geometry: 2.1 Arranging Your Generic Objects: General Geometry Rules

You may feel at first that the use of generic UI components limits your control over the UI of your application. Actually, just the opposite is true: Your application becomes immediately available at all supported video resolutions in all available specific UIs. Additionally, you can concentrate on more important issues of your application; the generic UI objects take care of a tremendous amount of the work in positioning and displaying the user interface.

To determine your generic UI tree, you must remember that UI geometry is determined by three things: First and foremost, the overall structure of the UI is governed by the structure and organization of the generic object tree. For example, if GenTriggers A, B, and C are designated as children of a dialog box, they will likely be ordered in the same order they're listed in the dialog's GI_comp field.

Second, an object's attributes can determine its implementation. For example, the GIGI_visibility field of a GenInteraction can determine whether the interaction is implemented as a dialog box, a menu, or a grouping object. The attributes of a generic object determine the basic functionality of the object; that is, even though a menu might be implemented differently in different specific UIs, the functionality of a menu will be implemented in the object. The manner of display of an object is based on its functionality. In this way, the attributes of a generic object take precedence over hints.

Third, an object's behavior can be fine-tuned through the use of hints. Hints may or may not be implemented for each object that has them, and they control less strictly how the object works.

Generic Tree Structure

As stated above, the structure of your application's generic object tree determines how the generic UI objects will be organized. A simple example of a generic tree including a menu, a dialog box, and a GenView can be found in the Hello World chapter.

How Hints Work

Nearly all geometry of generic UI objects is determined by hints. You can position, size, and limit generic objects with different hints. All these hints are defined in GenClass ; inheritance allows all generic objects to have them, though not all hints are applicable to all generic objects.

Hints are described in detail in the GenClass chapter, but the basics are reviewed here for convenience. Hints are implemented as variable data entries; each hint is a different variable-data type. Hints therefore take up instance data space in an object only when the object has that hint.

As stated earlier, not all hints are appropriate for all objects. For example, HINT_CUSTOM_CHILD_SPACING is probably not useful when applied to a GenTrigger object because a GenTrigger can have no children. Hints, by definition, are also not guaranteed to be supported by all specific UI libraries. For example, a specific UI might require all elements of a dialog box to be oriented horizontally; in this case, HINT_ORIENT_CHILDREN_VERTICALLY might be useful but might not be heeded by the specific UI.

Because geometry management of generic objects can be confusing at times, this chapter follows a series of examples accompanied with diagrams. For the most part, you should be able to "plug and play" the examples in the following sections.

Managing UI Geometry: 2.2 Arranging Your Generic Objects: Orienting Children

HINT_ORIENT_CHILDREN_HORIZONTALLY, HINT_ORIENT_CHILDREN_VERTICALLY, HINT_ORIENT_CHILDREN_ALONG_LARGER_DIMENSION, HINT_SAME_ORIENTATION_AS_PARENT

The two hints HINT_ORIENT_CHILDREN_HORIZONTALLY and HINT_ORIENT_CHILDREN_VERTICALLY , when used in a composite object, determine the orientation of the composite's children. A composite object can orient its children either horizontally or vertically. The geometry manager will determine the combined size of the children and any margins or spacing set up (none by default) and will size the composite to the minimum required by its children.

Children oriented horizontally will typically be top-justified, and children oriented vertically will be left-justified. (This is default behavior changeable either with the use of hints or by the specific UI.) The vertical composite will size to the width of the widest child; the horizontal composite will size to the height of the tallest child.

Code Display 12-1 Orienting a Composite

    /* Composite oriented horizontally */
@object GenInteractionClass MyInteraction = {
    GI_comp = @ApplyTrigger, @ResetTrigger, @CancelTrigger;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
    /* Composite oriented vertically */
@object GenInteractionClass MyOtherInteraction = {
    GI_comp = @ApplyTrigger, @ResetTrigger, @CancelTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}

A large example of a complex dialog box is shown in the figure below. This dialog box uses nested GenInteraction objects to achieve a complex grouping of children. Using the many justification, spacing, and orientation hints described throughout the chapter, you can change and fine-tune the appearance of the dialog box. The code that implements this dialog box is shown in A Complex Dialog Box .

Two other hints may be useful for arranging children, especially in toolboxes. These two hints are not shown in the examples, though they are used prominently with tool groups.

HINT_SAME_ORIENTATION_AS_PARENT is used by one windowed object (GenInteraction) when it should orient its children in the same way its parent does. This, too, is useful for toolboxes because you can set an orientation in the toolbox, then set HINT_SAME_ORIENTATION_AS_PARENT in the tool group.

HINT_ORIENT_CHILDREN_ALONG_LARGER_DIMENSION allows you to orient a composite's children along the screen's larger dimension. The hint will work like HINT_ORIENT_CHILDREN_HORIZONTALLY if the screen is wider than it is tall; it will work like HINT_ORIENT_CHILDREN_VERTICALLY if the screen is taller than it is wide.

Code Display 12-2 A Complex Dialog Box

/* This code display shows the basic Goc code that will get the configuration
 * shown in the above figure. */
/* The topmost interaction. It groups the two large interactions vertically. */
@object GenInteractionClass TopInteraction = {
    GI_comp = @ParaInteraction, @ReplyInteraction;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
/* The top of the two large interactions. It groups the three smaller interactions
 * on the top, horizontally. */
@object GenInteractionClass ParaInteraction = {
    GI_comp = @FontInteraction, @JustInteraction, @StyleInteraction;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
/* The interaction containing the Apply, Reset, and Cancel triggers. The
 * triggers are oriented horizontally. */
@object GenInteractionClass ReplyInteraction = {
    GI_comp = @ApplyTrigger, @ResetTrigger, @CancelTrigger;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
/* The three vertically-oriented interactions at the top are similar. Each
 * contains several triggers. All three are defined below. */
@object GenInteractionClass FontInteraction = {
    GI_comp = @RomanTrigger, @SansTrigger, @MonoTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@object GenInteractionClass JustInteraction = {
    GI_comp = @LeftTrigger, @RightTrigger, @CenterTrigger, @FullTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@object GenInteractionClass StyleInteraction = {
    GI_comp = @PlainTrigger, @BoldTrigger, @SuperTrigger, @SubTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
/* Triggers. All the triggers are essentially the same. Each is named as it
 * appears in the GI_comp fields above, and each has the corresponding
 * GI_visMoniker field. The RomanTrigger object is shown as an example. */
@object GenTriggerClass RomanTrigger = {
    GI_visMoniker = "Roman";
}

Managing UI Geometry: 2.3 Arranging Your Generic Objects: Justifying and Centering Children

Although child justification defaults to whatever the specific UI normally uses, you can set the justification of a composite's children with several different hints.

Typically, horizontal composites will have their children top-justified and vertical composites will have left-justified children (as shown in the previous examples).

Edge Justification

HINT_TOP_JUSTIFY_CHILDREN, HINT_BOTTOM_JUSTIFY_CHILDREN, HINT_LEFT_JUSTIFY_CHILDREN, HINT_RIGHT_JUSTIFY_CHILDREN

If you want to line all of a composite's children up on one of their edges, you should use one of these hints. They are self-explanatory; the edge specified in the name of the hint (top, left, etc.) is the edge of the composite along which all the children will appear.

Add the justification hints along with an orientation hint. Examples of justification are shown in the figure below.

Edge Alignment with Other Objects

HINT_ALIGN_LEFT_EDGE_WITH_OBJECT, HINT_ALIGN_TOP_EDGE_WITH_OBJECT, HINT_ALIGN_RIGHT_EDGE_WITH_OBJECT, HINT_ALIGN_BOTTOM_EDGE_WITH_OBJECT

These hints align an object's respective edge with the same edge of another object; the optr of the other object is the hint's argument. The other object does not necessarily have to be a direct sibling or parent, but the result must not cause the object to stray outside its parent's bounds.

Centering Children

HINT_CENTER_CHILDREN_VERTICALLY, HINT_CENTER_CHILDREN_HORIZONTALLY

Often, rather than justifying a composite's children, you will want to center them. These two hints allow you to center the children as shown in the figure below.

Full Justification

HINT_FULL_JUSTIFY_CHILDREN_HORIZONTALLY, HINT_FULL_JUSTIFY_CHILDREN_VERTICALLY, HINT_DONT_FULL_JUSTIFY_CHILDREN, HINT_INCLUDE_ENDS_IN_CHILD_SPACING, HINT_DONT_INCLUDE_ENDS_IN_CHILD_SPACING

Besides justifying the composite's children either on a single edge or on the center line of the composite, you might want to full-justify the children. To do this, you will want to give the composite an orientation first (either vertical or horizontal). The composite may also require a special sizing hint to expand its bounds--see HINT_EXPAND_WIDTH_TO_FIT_PARENT and the other sizing hints in Sizing Objects .

If the full justification hint is in the same dimension as the orientation of the composite (e.g., HINT_FULL_JUSTIFY_CHILDREN_HORIZONTALLY and HINT_ORIENT_CHILDREN_HORIZONTALLY ), then the geometry manager will divide up the entire space of the composite and spread the children out equally along it. If you also specify HINT_INCLUDE_ENDS_IN_CHILD_SPACING , the geometry manager will calculate the spacing as if there were one more child than there actually is; it will then put half the extra space on either end of the children. HINT_DONT_INCLUDE_ENDS_IN_CHILD_SPACING performs the default calculations. Examples of various full justification are shown in the figure below. See Using Full Justification of Children for the code required to implement this full justification behavior.

Typically, full justification is not implemented by default. Some specific UIs may try to full-justify children always; if you want the children non-justified (when possible), apply HINT_DONT_FULL_JUSTIFY_CHILDREN . Added spacing is also typically not turned on by default; you can ensure that added spacing will not be included (where possible) by applying the hint HINT_DONT_INCLUDE_ENDS_IN_CHILD_SPACING to your composite.

Managing UI Geometry: 2.4 Arranging Your Generic Objects: Sizing Objects

Sizing can occur in essentially one of two ways: First, the composite can size itself based on its children. Second, the children can size themselves based on their parent. You can specify additional sizing restrictions in the form of minimum, maximum, and fixed sizes.

Sizing the Parent

HINT_NO_TALLER_THAN_CHILDREN_REQUIRE, HINT_NO_WIDER_THAN_CHILDREN_REQUIRE

A composite can size itself to be only as large as its children require. This may be applied to your primary window or to other organizational composites to keep them from growing into any extra space in their parents. You can set the width and height restrictions independently; to set them both, use both hints in the composite.

Sizing the Children

HINT_EXPAND_HEIGHT_TO_FIT_PARENT, HINT_EXPAND_WIDTH_TO_FIT_PARENT, HINT_DIVIDE_WIDTH_EQUALLY, HINT_DIVIDE_HEIGHT_EQUALLY

Often a composite will have a set size and its children will want to expand themselves to take up all the available space. For example, the interaction with the Apply, Reset, and Cancel triggers in the dialog box (shown in the figure below) must have HINT_EXPAND_WIDTH_TO_FIT_PARENT set for it to expand itself to the far right edge of the dialog box. Another potential situation is a GenView within a GenPrimary; when the user resizes the primary, the view should probably expand itself to take up any extra space in the window.

A third example would be a series of triggers that want to be the same width in a vertical composite. This appears in many menus; each of the triggers expands itself to the width of the parent; the parent composite sizes its width to the largest of the triggers. An example taken from the complex dialog box is shown in the figure above. Code for this example is shown in Using Full Justification of Children .

Some more complex examples of expanding a group and justifying the elements are shown in the figure below. All of the examples use a justification hint combined with an expand-to-fit hint.

Code Display 12-3 Using Full Justification of Children

/* This code display shows the basic Goc code that will get the configuration
 * shown in the figure above. Other attributes are left out of the
 * definitions of the objects. This code display is supplemental to
 *  A Complex Dialog Box; most comments have been removed. */
@object GenInteractionClass TopInteraction = {
    GI_comp = @ParaInteraction, @ReplyInteraction;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@object GenInteractionClass ParaInteraction = {
    GI_comp = @FontInteraction, @JustInteraction, @StyleInteraction;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
}
@object GenInteractionClass ReplyInteraction = {
    GI_comp = @ApplyTrigger, @ResetTrigger, @CancelTrigger;
    HINT_ORIENT_CHILDREN_HORIZONTALLY;
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
    HINT_FULL_JUSTIFY_CHILDREN_HORIZONTALLY;
    HINT_INCLUDE_ENDS_IN_CHILD_SPACING;
}
@object GenInteractionClass FontInteraction = {
    GI_comp = @RomanTrigger, @SansTrigger, @MonoTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@object GenInteractionClass JustInteraction = {
    GI_comp = @LeftTrigger, @RightTrigger, @CenterTrigger, @FullTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@object GenInteractionClass StyleInteraction = {
    GI_comp = @PlainTrigger, @BoldTrigger, @SuperTrigger, @SubTrigger;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
/* Triggers. All the upper triggers are essentially the same. Each is named as it
 * appears in the GI_comp fields above, and each has the corresponding
 * GI_visMoniker field. The RomanTrigger object is shown as an example. */
@object GenTriggerClass RomanTrigger = {
    GI_visMoniker = "Roman";
    HINT_EXPAND_WIDTH_TO_FIT_PARENT;
}
/* The Apply, Reset, and Cancel triggers do not have the hint shown above. */
@object GenTriggerClass ApplyTrigger = {
    GI_visMoniker = "Apply";
}

HINT_DIVIDE_WIDTH_EQUALLY and HINT_DIVIDE_HEIGHT_EQUALLY instruct a composite object to divide up its space along the affected dimension among the usable children. This hint can only suggest sizes for the children. The children themselves will decide if they can expand or contract to fit the requested space. (In general, children will not contract to fit a requested size, but they may expand.) Note: these hints will not work within GenValue objects; if you wish to use them on such an object, place them within a dummy GenInteraction object and use these hints on that object.

Typically, these hints work well in conjunction with HINT_EXPAND_WIDTH_TO_FIT_PARENT and HINT_EXPAND_HEIGHT_TO_FIT_PARENT. If these hints are not also in place (on a GenInteraction, for example) the division of width and/or height may be based on the children's default size; i.e., the parent will size itself based on total size of the children and then attempt to divide up its children's space, rather than the maximum allotted size for the parent, which is probably not desired.

Maximum, Minimum, Fixed, and Initial Sizes

HINT_INITIAL_SIZE, HINT_MAXIMINUM_SIZE, HINT_MINIMUM_SIZE, HINT_FIXED_SIZE

If you know certain sizing restrictions for window or interaction objects, you can set them with the following four hints. Each takes a parameter that details the appropriate height and width in points. The parameters are described for the hints.

HINT_INITIAL_SIZE
This hint sets the initial size of the object, if allowed. Other sizing restrictions may override this hint (such as the children being too large). This is primarily used with window objects (you will likely never use it for anything other than a GenPrimary, GenDisplay, or GenView), and it is effective only for when the object is first displayed. This hint takes an argument of type CompSizeHintArgs , a structure that defines a suggested width, height, and the number of children the object has.
HINT_FIXED_SIZE
This hint sets a fixed size for the object if one is allowed. Other sizing restrictions (such as children growing too large for the window) may override this hint occasionally. This hint can often be used for optimization to inhibit excessive geometry recalculations while on-screen. This hint takes the same parameters as HINT_INITIAL_SIZE , of type CompSizeHintArgs .
HINT_MAXIMUM_SIZE
This hint sets a desired maximum size for the object. Other geometry restrictions may override this hint when necessary. This hint is useful for GenText and GenView objects. It takes the same parameters as HINT_INITIAL_SIZE , of type CompSizeHintArgs .
HINT_MINIMUM_SIZE
This hint sets a desired minimum size for the object. Other geometry restrictions may override this hint when necessary. The hint takes the same parameters as HINT_INITIAL_SIZE , of type CompSizeHintArgs .

The following examples show different ways to set the initial size of an object. The third argument of the hint is the number of children in a particular line of a composite, when the composite is set up to wrap its children.

This example sets the initial size of the composite (probably a GenPrimary) to be half the screen's height and half the screen's width.

HINT_INITIAL_SIZE = {
    SST_PCT_OF_FIELD_WIDTH | PCT_50,
    SST_PCT_OF_FIELD_HEIGHT | PCT_50,
    0 };

This example sets the size of the composite to be 100 pixels high and 200 pixels wide.

HINT_FIXED_SIZE = {
    SST_PIXELS | 200,
    SST_PIXELS | 100,
    0 };

This example sets the composite's minimum size to be 10 average characters wide and 20 percent of the screen height tall.

HINT_MINIMUM_SIZE = {
    SST_AVG_CHAR_WIDTHS | 10,
    SST_PCT_OF_FIELD_HEIGHT | PCT_20,
    0 };

Managing UI Geometry: 2.5 Arranging Your Generic Objects: Outlining the Composite

HINT_DRAW_IN_BOX

Often it is useful to outline a composite, especially if it is used for organizational purposes within another composite. You can use the hint HINT_DRAW_IN_BOX to have the UI automatically draw a box at the object's bounds as shown below.

Managing UI Geometry: 2.6 Arranging Your Generic Objects: Using Monikers

Nearly all generic objects will have visual monikers (usually text labels) that get drawn somewhere in or near the object. The moniker helps the user distinguish between different objects; for example, each trigger should have a text (or graphical) moniker giving some indication of what happens when the trigger is pressed.

Placing Monikers in Relation to Children

HINT_PLACE_MONIKER_ABOVE, HINT_PLACE_MONIKER_BELOW, HINT_PLACE_MONIKER_TO_LEFT, HINT_PLACE_MONIKER_TO_RIGHT, HINT_DO_NOT_USE_MONIKER, HINT_CENTER_MONIKER, HINT_PLACE_MONIKER_ALONG_LARGER_DIMENSION, HINT_ALIGN_LEFT_MONIKER_EDGE_WITH_CHILD

Monikers are extremely useful and dynamic--see the GenClass chapter, for full information on creating, manipulating, and using visual monikers. Monikers may be created or changed at run-time (often causing geometry updates), and all generic objects may be given monikers.

If you place a composite object's moniker to one side of its children, the geometry manager will ensure extra space in the composite gets allotted for the moniker. You can hint that the moniker should be placed above, to the right of, to the left of, or below the children. You can also hint that the moniker should not be used.

Most specific UIs will put the moniker to the left of the object's children by default. You can also center the moniker on the composite's children with HINT_CENTER_MONIKER .

You can also have the UI place the moniker along the object's larger dimension--to the left if the screen is wider than it is tall, or above if the screen is taller than it is wide. To do this, set the hint HINT_PLACE_MONIKER_ALONG_LARGER_DIMENSION .

If a moniker is placed above a group of children, HINT_ALIGN_LEFT_MONIKER_EDGE_WITH_CHILD ensures that left moniker edge coincides with the left edge of the first child below that moniker. This is usually the default behavior anyway, but different specific UIs may have different default behavior.

Justifying or Centering on Monikers

HINT_CENTER_CHILDREN_ON_MONIKERS, HINT_LEFT_JUSTIFY_MONIKERS

When you have several composites within a single composite, you can center the composites based on the position and size of the monikers. Using the hint HINT_CENTER_CHILDREN_ON_MONIKERS has the effect shown in the figure above: It aligns the child composites so their monikers are right-justified at the center. Adding HINT_LEFT_JUSTIFY_MONIKERS has a similar effect, except the monikers are left-justified.

Note that the two hints HINT_CENTER_CHILDREN_ON_MONIKERS and HINT_LEFT_JUSTIFY_MONIKERS are valid only for composites with one or more child composites, all of which have monikers placed to their left.

Removing Moniker Borders

HINT_NO_BORDERS_ON_MONIKERS

This hint removes borders that may appear by default around an object's moniker. For example, GenTriggers under most specific UIs have a rectangular border drawn around their moniker's borders. This hint removes these borders. It may be useful in cases where a custom border should be implemented, but in general should not be used to override the specific UI, as this may confuse the user.

Managing UI Geometry: 2.7 Arranging Your Generic Objects: Using Custom Child Spacing

HINT_CUSTOM_CHILD_SPACING, HINT_CUSTOM_CHILD_SPACING_IF_LIMITED_SPACE, HINT_MINIMIZE_CHILD_SPACING

Normally, spacing of children within a composite is left entirely up to the specific UI. You can customize spacing, however, by using the hint HINT_CUSTOM_CHILD_SPACING . This hint takes a single argument of type SpecSizeSpec , described below.

The SpecSizeSpec structure defines both the spacing between children and how the spacing is determined. It exists as a record with two fields: The first field is a constant of type SpecSizeTypes , used to interpret the second field. The second field is data based on the type specified in the first field.

The different types allowable in the first field are

SST_PIXELS
Data specified in pixels.
SST_PCT_OF_FIELD_WIDTH
Data specified as a percentage of screen width. The data value is a fraction that gets multiplied by the width of the screen. Predefined fraction values to use in the data field are named PCT_ xx , where the xx represents any multiple of five between zero and 100 inclusive. You can get better accuracy by using your own value if desired, however.
SST_PCT_OF_FIELD_HEIGHT
Data specified as a percentage of screen height. As above except uses screen height rather than width.
SST_AVG_CHAR_WIDTHS
Data specified as a number that gets multiplied by the average width of a character in the font being used. The data may be between zero and 1023 inclusive.
SST_WIDE_CHAR_WIDTHS
Data is specified as a number that gets multiplied by the maximum width of the widest character in the font being used. The data may be between zero and 1023 inclusive.
SST_LINES_OF_TEXT
Data is specified as a number of text lines. The number will be multiplied by the height of a line of text in the font being used.

Both fields of the record are defined by using the bitwise-or operator. If you had a composite that wanted to line all its children up exactly next to each other, for example, you would specify the hint as follows:

@object GenInteractionClass @MyComp = {
    GI_comp = @child1, @child2, @child3;
    HINT_CUSTOM_CHILD_SPACING = SST_PIXELS | 0;
}

HINT_CUSTOM_CHILD_SPACING_IF_LIMITED_SPACE also takes a SpecSizeSpec to suggest a custom amount of spacing, but only implements this custom spacing if the specific UI determines that the children are already too tightly arranged. This hint may or may not be helpful, as the specific UI often allocates a minimum amount of spacing independent of this hint.

HINT_MINIMIZE_CHILD_SPACING ensures that child spacing is kept to an absolute minimum, even if this means that object's edges will touch (in color systems) or even overlap (in black and white systems).

Managing UI Geometry: 2.8 Arranging Your Generic Objects: Allowing Children to Wrap

HINT_ALLOW_CHILDREN_TO_WRAP, HINT_WRAP_AFTER_CHILD_COUNT, HINT_DONT_ALLOW_CHILDREN_TO_WRAP, HINT_WRAP_AFTER_CHILD_COUNT_IF_VERTICAL_SCREEN

When a composite grows to fit its children and also must remain small enough to fit within its generic parent, it may wrap the children. This typically happens as a result of the user resizing a window smaller so the children of the composite no longer fit the window's width. Wrapping can occur in both the horizontal and the vertical, depending on the orientation of the composite object.

To allow a composite to wrap its children when necessary, apply the hint HINT_ALLOW_CHILDREN_TO_WRAP to the composite. If you want the children not to wrap, apply HINT_DONT_ALLOW_CHILDREN_TO_WRAP . An example of wrapping children appears below.

When HINT_WRAP_AFTER_CHILD_COUNT is applied and children are allowed to wrap, the composite will cause its children to wrap after a certain number are displayed. This hint takes a single integer as a parameter. For example, if you wanted three children on every line in the composite no matter how many children there were total, you would specify the composite as follows:

@object GenInteractionClass @MyComp = {
    GI_comp = @child1, @child2, @child3, @child4;
    HINT_ALLOW_CHILDREN_TO_WRAP;
    HINT_WRAP_AFTER_CHILD_COUNT = 3;
}

The configuration shown above is demonstrated in the figure below.

A variation, HINT_WRAP_AFTER_CHILD_COUNT_IF_VERTICAL_SCREEN , allows you to allow child wrapping based on whether or not the screen is "vertical." If the screen is taller than it is wide, this hint will have an effect like HINT_WRAP_AFTER_CHILD_COUNT ; if not, the hint will have no effect.

Managing UI Geometry: 2.9 Arranging Your Generic Objects: Object Placement

As we have seen, generic objects can be arranged and distributed in several ways. You may also place generic objects within certain areas of your user interface that are not explicitly defined. For example, in many dialog boxes, a "reply bar" is typically present where reply triggers such as "OK," "Cancel," or "Apply" are located. You may specify objects to generically place themselves within such a reply bar, even though you may not know the exact location of that reply bar as the specific UI creates it.

Reply Bars

HINT_MAKE_REPLY_BAR, HINT_SEEK_REPLY_BAR

One special feature that many dialog boxes have is a reply bar . Typically, any dialog box that allows the user to set a number of options before applying them will have a reply bar. A reply bar usually has triggers in it for "Apply," "Cancel," and perhaps other functions (such as "Reset").

Note that many special dialog box types can have their reply bars built automatically according to the specific UI. See the Menus and Dialog Boxes chapter for full information on reply bars. If you want to create your own, however, you can with the hints in this section.

Although you have to declare each of the triggers that will appear in the reply bar, you can use HINT_MAKE_REPLY_BAR to set up the geometry appropriate for the specific UI. For example, Creating a Reply Bar gives the code that will create a reply bar.

Code Display 12-4 Creating a Reply Bar

/* This reply bar has three triggers, Apply, Reset, and Cancel.
 * The appropriate geometry is not determined until run-time. */
@object GenInteractionClass MyReplyBar = {
    GI_comp = @ApplyTrigger, @ResetTrigger, @CancelTrigger;
    HINT_MAKE_REPLY_BAR;
}
@object GenTriggerClass ApplyTrigger = {
    GI_visMoniker = "Apply"; }
@object GenTriggerClass ResetTrigger = {
    GI_visMoniker = "Reset"; }
@object GenTriggerClass CancelTrigger = {
    GI_visMoniker = "Cancel"; }

HINT_SEEK_REPLY_BAR instructs the generic object (usually a GenTrigger) to seek placement in the dialog box's reply bar. The specific UI will try to place the trigger in the reply bar of the first dialog box GenInteraction it finds that is not GIT_ORGANIZATIONAL.

Placing Objects in Menu Bars

HINT_SEEK_MENU_BAR, HINT_AVOID_MENU_BAR

Usually, it is left up to the specific UI whether it places any given object within a menu bar. You can suggest that objects be placed within the window's menu bar by placing HINT_SEEK_MENU_BAR on that object. Similarly, you can place HINT_AVOID_MENU_BAR on an object to suggest that it not be placed within the window's menu bar.

Placing Objects in Scroller Areas

HINT_SEEK_X_SCROLLER_AREA, HINT_SEEK_Y_SCROLLER_AREA, HINT_SEEK_LEFT_OF_VIEW, HINT_SEEK_TOP_OF_VIEW, HINT_SEEK_RIGHT_OF_VIEW, HINT_SEEK_BOTTOM_OF_VIEW

These hints affect the placement of generic objects within GenViews. The objects must be children of a GenView for the hints to take effect.

HINT_SEEK_X_SCROLLER_AREA and HINT_SEEK_Y_SCROLLER_AREA suggest that a generic object be placed alongside the scrollbar area--either the horizontal scrollbar or the vertical scrollbar, respectively--of the GenView object.

HINT_SEEK_LEFT_OF_VIEW , HINT_SEEK_TOP_OF_VIEW, HINT_SEEK_RIGHT_OF_VIEW and HINT_SEEK_BOTTOM_OF_VIEW suggest that the generic object be placed alongside the respective side of the GenView.

Placing Objects in Window Title Bars

HINT_SEEK_TITLE_BAR_LEFT, HINT_SEEK_TITLE_BAR_RIGHT

These hints suggest the placement of a generic object within a window's title bar. These hints are usually used on GenTriggers or GenInteractions; the objects involved should fit within the title bar area (i.e., be equivalent to tool bar icons). For each window with a title bar, only one object may have each of these hints, and the object must be a direct child of the windowed object.

Placement of Objects Popped Up

HINT_POPS_UP_TO_RIGHT, HINT_POPS_UP_BELOW
HINT_POPS_UP_TO_RIGHT
This hint instructs the specific UI to bring up the object to the right of its activating gadget, if the object is normally popped up below the activating gadget.
HINT_POPS_UP_BELOW
This hint instructs the specific UI to bring up the object below its activating gadget. This is usually the default behavior for objects that are popped up.

Managing UI Geometry: 3 Positioning and Sizing Windows

A generic object that appears as a window, such as an independently-displayable GenInteraction or a GenPrimary, has special needs for positioning and sizing itself, especially when appearing initially. Specific user interfaces may differ in how windows appear on the screen. In Motif, for instance, windows initially appear in one of several staggered positions. Primary and display objects extend by default to the right edge of the screen and to the top edge of the icon area at the bottom of the screen. Other windows are usually only as large as required by their children.

The SpecSizeWinPair structure is often used when specifying window sizes and positions. There are several hints which signal that a window should be positioned or sized depending on the position or size of another window, the size of the screen, or other conditions. The SpecSizeWinSpec structure allows you to specify an offset from another window, either by a number of points (remember that there are 72 points per inch), or by means of a ratio.

typedef struct {
    SpecWinSizeSpec			SWSP_x;
    SpecWinSizeSpec			SWSP_y;
} SpecWinSizePair;
typedef WordFlags SpecWinSizeSpec;
#define SWSS_RATIO 				0x8000
#define SWSS_SIGN 				0x4000
#define SWSS_MANTISSA 				0x3c00
#define SWSS_FRACTION 				0x03ff

The SpecSizeWinPair structure allows you to set up separate parameters for the x and y coordinates. For each coordinate, use the SWSS_RATIO flag to signal whether you are giving coordinates by means of a ratio instead of a constant. If you are using a ratio, use the SWSS_FRACTION, SWSS_MANTISSA, and SWSS_SIGN fields to specify the value of the ratio or use one of the following pre-defined constants to specify a ratio between zero and one:

#define PCT_0			0x000
#define PCT_5			0x033
#define PCT_10			0x066
#define PCT_15			0x099
#define PCT_20 			0x0cc
#define PCT_25 			0x100
#define PCT_30 			0x133
#define PCT_35 			0x166
#define PCT_40 			0x199
#define PCT_45 			0x1cc
#define PCT_50 			0x200
#define PCT_55 			0x233
#define PCT_60 			0x266
#define PCT_65 			0x299
#define PCT_70 			0x2cc
#define PCT_75 			0x300
#define PCT_80 			0x333
#define PCT_85 			0x366
#define PCT_90 			0x399
#define PCT_95 			0x3cc
#define PCT_100			0x3ff

To specify a constant offset, just use that offset, not setting the SWSS_RATIO flag. Not all windows will use the SpecWinSizePair structure.

Window Positioning

Determining Initial Size

On-Screen Behavior

Window Look and Feel

Managing UI Geometry: 3.1 Positioning and Sizing Windows: Window Positioning

HINT_POSITION_WINDOW_AT_RATIO_OF_PARENT, HINT_POSITION_WINDOW_AT_MOUSE, HINT_STAGGER_WINDOW, HINT_CENTER_WINDOW, HINT_TILE_WINDOW, HINT_WINDOW_NO_CONSTRAINTS

GenClass has a number of hints used by the window classes to determine where the window appears initially and how the window is allowed to move about the screen. Specific UIs can override these hints when necessary or when they conflict with the UI's specifications. These hints are listed below.

HINT_POSITION_WINDOW_AT_RATIO_OF_PARENT
This hint suggests an initial position for the window based on its parent window's size and relative to the upper-left corner of the parent window. The suggested position is specified as a fraction of the parent window's size in a SpecWinSizePair structure. This structure consists of two fields, the X position and the Y position of the new window's origin. Predetermined fraction values are names PCT_ xx , where xx is a multiple of five between zero and 100 inclusive. For example, to have the window object's origin start out in the center of its parent, specify the hint as follows:
HINT_POSITION_WINDOW_AT_RATIO_OF_PARENT = {
    SWSS_RATIO | PCT_25, SWSS_RATIO | PCT_60
};
HINT_POSITION_WINDOW_AT_MOUSE
This hint will position the origin of the new window at the mouse pointer.
HINT_STAGGER_WINDOW
This hint indicates that the window's origin should be placed just below and to the right of the origin of the last staggered window.
HINT_CENTER_WINDOW
This hint centers the window in its parent window.
HINT_TILE_WINDOW
This hint tiles the window with other windows that are also marked HINT_TILE_WINDOW within the parent window. This hint is not currently supported.
HINT_WINDOW_NO_CONSTRAINTS
This hint removes all constraints upon the positioning of a window. It should only be used as the final attempt to position the window if other methods do not work to your satisfaction.

Managing UI Geometry: 3.2 Positioning and Sizing Windows: Determining Initial Size

HINT_EXTEND_WINDOW_TO_BOTTOM_RIGHT, HINT_EXTEND_WINDOW_NEAR_BOTTOM_RIGHT, HINT_SIZE_WINDOW_AS_DESIRED, HINT_SIZE_WINDOW_AS_RATIO_OF_PARENT, HINT_SIZE_WINDOW_AS_RATIO_OF_FIELD, HINT_USE_INITIAL_BOUNDS_WHEN_RESTORED

GenClass has a number of hints used by the window classes to determine the size of the window object initially and how the window is allowed to be resized on the screen. Specific UIs can override these hints when necessary or when they conflict with the UI's specifications. These hints are listed below.

HINT_EXTEND_WINDOW_TO_BOTTOM_RIGHT
This hint extends the window's bounds so its lower right corner is located at the lower right corner of its parent window.
HINT_EXTEND_WINDOW_NEAR_BOTTOM_RIGHT
This hint extends the window's bounds so its lower right corner is located near the lower right corner of its parent window. Enough room will be left for any icon area that may be present. Most major applications will apply this hint to their GenPrimary objects.
HINT_SIZE_WINDOW_AS_DESIRED
This hint allows the window to size itself according to the combined size of its children.
HINT_SIZE_WINDOW_AS_RATIO_OF_PARENT
This hint sizes the window to a ratio of its parent window. It takes a single parameter of type SpecWinSizePair , described under HINT_POSITION_WINDOW_AT_RATIO_OF_PARENT . Instead of coordinates, however, the ratios in the structure are width and height.
HINT_SIZE_WINDOW_AS_RATIO_OF_FIELD
This hint sizes the window to a ratio of the field window's size. It takes a SpecWinSizePair parameter indicating the ratio. See above.
HINT_USE_INITIAL_BOUND_WHEN_RESTORED
This hint instructs the window to come on-screen with its original values rather than the state of the window when it was saved to state.

Managing UI Geometry: 3.3 Positioning and Sizing Windows: On-Screen Behavior

HINT_KEEP_INITIALLY_ONSCREEN, HINT_DONT_KEEP_INITIALLY_ONSCREEN, HINT_KEEP_PARTIALLY_ONSCREEN, HINT_DONT_KEEP_PARTIALLY_ONSCREEN, HINT_KEEP_ENTIRELY_ONSCREEN, HINT_KEEP_ENTIRELY_ONSCREEN_WITH_MARGIN, HINT_NOT_MOVABLE
HINT_KEEP_INITIALLY_ONSCREEN
This hint will make the window resize itself to fit entirely within its parent window. Most windows will inherently know how to come up fully on-screen; some specific UIs do not implement it by default for optimization purposes.
HINT_DONT_KEEP_INITIALLY_ONSCREEN
This hint will allow the window to come up whether it will be fully on-screen or not. If the default is to keep the window entirely on-screen and the UI allows this hint, the window will be allowed to open partially off-screen.
HINT_KEEP_PARTIALLY_ONSCREEN
This hint indicates that when the user moves the window, it will be forced to remain partially on-screen.
HINT_DONT_KEEP_PARTIALLY_ONSCREEN
This hint indicates that the user can move the window around the screen at will, even if the window becomes completely obscured under its parent.
HINT_KEEP_ENTIRELY_ONSCREEN
This hint indicates that when the user moves the window, it will be forced to remain entirely on-screen.
HINT_KEEP_ENTIRELY_ONSCREEN_WITH_MARGIN
This hint indicates that when the user moves the window, it will be forced to remain on-screen. An additional margin is added to the edge of the screen to keep the window from touching the edge as well; this margin is defined by the specific UI.
HINT_NOT_MOVABLE
This hint instructs the specific UI to make the windowed object not movable. This hint should be avoided if at all possible.

Managing UI Geometry: 3.4 Positioning and Sizing Windows: Window Look and Feel

HINT_WINDOW_NO_TITLE_BAR, HINT_WINDOW_NO_SYS_MENU, HINT_WINDOW_MINIMIZE_TITLE_BAR, HINT_WINDOW_ALWAYS_DRAW_WITH_FOCUS

HINT_WINDOW_NO_TITLE_BAR instructs the specific UI to remove the title bar from a windowed object (dialog box, GenPrimary or GenDisplay). Use this hint with extreme prejudice as most of a window's functionality will not be accessible without a title bar.

HINT_WINDOW_NO_SYS_MENU instructs the specific UI to remove the system menu (or close button) from a windowed object. Again, do not use this hint unless necessary, as it overrides important features of windowed objects.

HINT_WINDOW_MINIMIZE_TITLE_BAR instructs the specific UI to minimize the area that the window's title bar occupies, if possible. (This hint is designed for use in smaller PDA-style devices.) Note that this hint usually takes away any system menus that may normally be included with the title bar. As a result, make sure that you provide another means to dismiss the window. Also note that if a window containing this hint is disabled, the window will be dismissed by the system.

HINT_WINDOW_ALWAYS_DRAW_WITH_FOCUS instructs the specific UI to draw the window as if it always contains the focus, including "high-lighting" the window's title bar, if possible. Only use this hint if it is absolutely necessary because it may confuse the user and it violates the predominant focus paradigm.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Help Object Library

On-line help is important for many applications, especially in the world of graphical interfaces and consumer devices. Many users will opt for exploration of an application rather than reading through the manual, and on-line help can assist them.

The GEOS on-line help system is an information viewing tool to be used by most if not all GEOS applications. It is automated, and adding it to an application is simple. All you need to do is create your help documents with GeoWrite and then add a few help attributes to your application's generic UI objects.

1 What Is Help?
1.1 Normal Help
1.2 First Aid
1.3 Simple Help
2 Adding Help to Your Application
2.1 Help Contexts and Help Triggers
2.2 Adding Default Normal Help
2.3 Bringing Up Help on the Fly
3 Customizing Help
3.1 Bringing Up Initial Help
3.2 Adding the HelpControl
3.3 Sizing the Help Dialog Box
3.4 Managing Help Types
3.5 Managing Help Files
3.6 Customizing the Pointer Image
3.7 Changing the Help Features
4 Creating Help Files
4.1 Enabling the Help Editor
4.2 Organizing and Writing the Text
4.3 Defining Files and Contexts
4.4 Using Hyperlinks
4.5 Generating the Help Files
5 HelpControlClass Reference

Help Object Library: 1 What Is Help?

GEOS provides four basic flavors of help ranging from simple descriptions to a full-featured information viewer complete with linked text and embedded graphics. GEOS help is both context-sensitive and random-access; a user may get help on the current dialog box, for example, or may invoke your help system's table of contents directly.

How a user gets help depends on the system in use: A standard desktop PC will allow keyboard access with the F1 key as well as help triggers throughout the application's UI. A small-screen device may hide the help triggers and allow only a special "hard icon," special pen gesture, or other means of activation to save screen space. (Such decisions are up to the specific UI.)

Nearly all applications will use "Normal Help," which provides a full-featured help viewer. The four types available are described below.

Normal Help

First Aid

Simple Help

Help Object Library: 1.1 What Is Help?: Normal Help

Most applications will include Normal Help , which provides a full-featured help viewer complete with embedded graphics and hyperlinked text. Normal help allows you to organize your help data in any manner; it has a single Table of Contents (TOC) page, which the user can access at any point in viewing help. Your TOC page should hyperlink to any other pages associated or appropriate (it depends on the information you're presenting). See the figure below for a sample of Normal Help.

Normal Help, like the other help types (see below), is also context-sensitive. You must define the contexts, but the help system will bring up the proper help page for the context. If the user clicks the help trigger on the primary window, for example, the TOC page is presented (by default). If the user clicks the trigger in a dialog box, however, the help page for that dialog box is presented.

Help Object Library: 1.2 What Is Help?: First Aid

First Aid help, like Normal Help, provides a full-featured help viewer. First Aid files are also organized by context, but the information is presented in a different format. Whereas Normal Help is organized around the structure of the application, First Aid is presented in a book format.

When a user clicks the First Aid trigger, the help page for the proper context is displayed; this will typically be taken from the current Focus object (as in Standard Help). From there, the user may navigate throughout the help "book" via both hyperlinks in the text and three buttons at the top of the help window: Contents, Chapter, and Article. An illustration of the First Aid help box is shown below.

To use First Aid, an application needs its own HelpControl object. Simply add one as a child of your GenApplication and set its help type to HT_FIRST_AID . The help controller must also be put on the application's active list.

Help Object Library: 1.3 What Is Help?: Simple Help

Simple Help provides a small scrolling dialog box with help text. This is useful for places where very simple help is required, perhaps only a few lines to illustrate a simple operation. Simple Help may be most useful for appliance-type applications; most applications will use Standard Help or First Aid help. A sample screen of Simple help is shown below.

Simple help, like First Aid, requires that the application have its own HelpControl object and that the help controller be on the active list. Text in simple help does not link, so all help text pertaining to the item must appear on a single page.

Help Object Library: 2 Adding Help to Your Application

For most applications, simply creating help files and adding ATTR_GEN_HELP_CONTEXT to certain generic objects will be sufficient. Other applications, though, such as those that use First Aid help, will require the addition of a HelpControl object (Normal help uses a system-provided and UI-run help controller).

The HelpControl object interacts with the GenClass and MetaClass aspects of generic objects to create help triggers, put up help screens, and determine which help file is used and what help context is displayed. For normal help, you don't need a HelpControl object; the system provides one. You do, however, have to know how to declare help contexts and work with help files.

Help Contexts and Help Triggers

Adding Default Normal Help

Bringing Up Help on the Fly

Help Object Library: 2.1 Adding Help to Your Application: Help Contexts and Help Triggers

 ATTR_GEN_HELP_CONTEXT,  HINT_PRIMARY_NO_HELP_BUTTON

The help controller displays help pages based on an object's context. An object sets its context with ATTR_GEN_HELP_CONTEXT , which holds the name of the context. If an object does not have an assigned context, it will query up the tree until one of its ancestors assigns a context. The help control will display the help page with the same name as the assigned context.

This attribute has some side effects. If placed on a GenPrimary object, it will create the help trigger that appears in the application's title bar (unless other restrictions are applied--see below). If placed on a dialog box GenInteraction, it will create a help trigger in the dialog box (placement of the trigger is subject to the specific UI, but it will normally be on the far right of the reply bar). Both of these triggers invoke help with the context of their objects.

Almost always, the GenPrimary's help context should be "TOC" to bring up the TOC help page. This is the default context, so if you don't specify a context for any object, help will always try to bring up the TOC page.

As stated above, ATTR_GEN_HELP_CONTEXT will by default create help triggers in certain situations. There are two times when creation of these help triggers will be suppressed: First, on small-screen devices or other situations wherein help triggers are not desired. In this situation, the .INI file will have a certain help option turned off. The simplest way to do this is to set

 [uiFeatures]
 helpOptions = 1

in the .INI file. This corresponds to the single help option UIHO_HIDE_HELP_BUTTONS and will suppress the help triggers. Both the F1 key (or equivalent) and MSG_META_BRING_UP_HELP will still invoke help properly.

Second, you can suppress only the GenPrimary's help trigger by adding HINT_PRIMARY_NO_HELP_BUTTON to the GenPrimary. Other help triggers will appear as dictated by the other requirements.

You can create your own help triggers if you like, though there is rarely a need to. Help triggers can be normal GenTrigger objects, with special settings. If the trigger is in a dialog box, it should have IC_HELP as its interaction type ( ATTR_GEN_TRIGGER_INTERACTION_COMMAND ). Otherwise, it should send MSG_META_BRING_UP_HELP to the object that should provide the context for the help. Also, the trigger should have its moniker specified with ATTR_GEN_DEFAULT_MONIKER , with the value GDMT_HELP . This will ensure your help trigger looks like other help triggers.

Help triggers you create, however, will not be suppressed by the system. Thus, if you want your application to be adaptable to small-screen UIs, you should only use system-provided help triggers.

Help Object Library: 2.2 Adding Help to Your Application: Adding Default Normal Help

To add normal help to your application, you only need to add ATTR_GEN_HELP_CONTEXT to the objects that should have help contexts. You can look at the HelpSamp sample application, though that does much more than you need. The code shown in Objects with Help Contexts is taken from HelpSamp and shows a dialog box with a list, in which each entry has its own context.

You will want to put ATTR_GEN_HELP_CONTEXT on any generic object that could conceivably need help text. Nearly all applications will put help only on GenInteractions and GenPrimaries, but it is available for all generic objects. For example, items in a GenItemGroup may have help contexts (as shown in HelpSamp) so that when the user hits the F1 key, help will be invoked for that particular item. Likewise, menus and menu items may have their own help contexts.

Nearly all GenPrimary objects should have the context "TOC" set for them.

Code Display 13-1 Objects with Help Contexts

/*     The TypeDialog box has its own help context: Help Types. This will cause
 * the UI to put a help trigger in the dialog's reply bar and, when that trigger
 * is clicked, will cause the system's help object to bring up the normal help
 * viewer on the "Help Types" page in the current help file.
 *     Each item in the GenItemGroup has its own help context. The list itself
 * does not have a context because if the user presses the F1 key, the help system
 * will bring up the selected item's help. If it were a list in which there could
 * be zero items selected, then the list might have its own context as well. */
@object GenInteractionClass TypeDialog = {
    GI_visMoniker = 'T', "Change Help Type";
    GI_comp = @TypeList;
    GII_visibility = GIV_DIALOG;
    GII_type = GIT_PROPERTIES;
    ATTR_GEN_HELP_CONTEXT = "Help Types";
}
@object GenItemGroupClass TypeList = {
    GI_comp = @NormalItem, @FirstAidItem, @SimpleItem;
    GIGI_numSelections = 1;
    GIGI_selection = HT_NORMAL_HELP;
    GIGI_applyMsg = MSG_HELPSAMP_SET_TYPE;
    GIGI_destination = process;
    HINT_ORIENT_CHILDREN_VERTICALLY;
}
@object GenItemClass NormalItem = {
    GI_visMoniker = "Normal Help";
    GII_identifier = HT_NORMAL_HELP;
    ATTR_GEN_HELP_CONTEXT = "Normal Help";
}
@object GenItemClass FirstAidItem = {
    GI_visMoniker = "First Aid";
    GII_identifier = HT_FIRST_AID;
    ATTR_GEN_HELP_CONTEXT = "First Aid";
}
@object GenItemClass SimpleItem = {
    GI_visMoniker = "Simple Help";
    GII_identifier = HT_SIMPLE_HELP;
    ATTR_GEN_HELP_CONTEXT = "Simple Help";
}

Help Object Library: 2.3 Adding Help to Your Application: Bringing Up Help on the Fly

 HelpSendHelpNotification()

The Help Controller library provides a routine to bring up a help context, or to switch to a help context, at any time. This routine, HelpSendHelpNotification() , generates the proper notification event using the values you pass, then sends that notification to the help control object you've selected. This routine is detailed in the routines reference manual.

This routine becomes most useful when, for example, you want help on visible objects in a GenView or in a document. To call up a help viewer with a specific help file and context, you would have the visible object intercept both your special "help key" (e.g. the user clicks the middle mouse button) and MSG_META_BRING_UP_HELP , then call HelpSendHelpNotification() . In the handler for the input event, the object should not call the superclass. One example of this could be a special diagram of a floorplan, with each room a visible object; each "room" would bring up a different help context describing the characteristics of the room.

Help Object Library: 3 Customizing Help

If you want to use one of the other help types, or if you want to use multiple help types in a single application, you must do more than just apply help contexts to objects. You can customize several things about the help system:

  • Help Type
    You can set or change the type of help offered by your application. You must have one HelpControl object for each type of help offered. You can set different objects to display or use different help types.
  • Help File
    Any object can specify the name of the file from which it draws its help. You can set the help file statically, change it dynamically, or have it defined by the GEOS.INI file.
  • Pointer Image
    The help system lets you define a custom pointer image to be used when the mouse pointer is over a hyperlink. You define the image, and the help controller uses that instead of the standard link pointer.
  • Help Features
    Because the HelpControl object is a controller, it has a certain feature set that may be turned on and off with ATTR_GEN_CONTROL_REQUIRE_UI and ATTR_GEN_CONTROL_PROHIBIT_UI . The features include the buttons presented to the user for navigating the help file and for closing the dialog box.

You can even create special "viewer" applications based on the help object. To make any of the above customizations, however, you must add one or more HelpControl objects to your application.

Bringing Up Initial Help

Adding the HelpControl

Sizing the Help Dialog Box

Managing Help Types

Managing Help Files

Customizing the Pointer Image

Changing the Help Features

Help Object Library: 3.1 Customizing Help: Bringing Up Initial Help

 ATTR_HELP_INITIAL_HELP_FILE,  ATTR_HELP_INITIAL_HELP

An object may use ATTR_HELP_INITIAL_HELP_FILE and ATTR_HELP_INITIAL_HELP to bring up a help context when the object becomes usable. ATTR_HELP_INITIAL_HELP_FILE defines the help file from which the help is to be taken; it is only used if ATTR_HELP_INITIAL_HELP is also present. This one defines the help context to be brought up on startup.

These attributes are used normally only by help-viewer applications, as it is unlikely help will be required on startup. Normally, help is brought up by the user pressing a help trigger.

Help Object Library: 3.2 Customizing Help: Adding the HelpControl

To add a help controller to your application, simply declare an instance of HelpControlClass and add it as a child of your GenApplication, adding it to the active list. The code in Adding Help Controllers is taken from the HelpSamp sample application and illustrates what you have to do to support custom help. The steps are

  • Declare the HelpControl
    Declare an object of HelpControlClass . You must set its HCI_helpType field to the HelpType value it will manage. Because the standard behavior is to have the help window always appear on top of other dialog boxes, you should set the window priority accordingly (as shown). Also, because most controllers will come up disabled, set the GS_ENABLED flag to have the help buttons enabled when help is first invoked.
  • Add the HelpControl to the active list
    The HelpControl, as all controllers, must be put on the active list, the MGCNLT_ACTIVE_LIST GCN list. All of your help controllers must be on this list.
  • Set the help type in the GenApplication
    If you're simply using a different help type, set it in the GenApplication object with ATTR_GEN_HELP_TYPE . The sample code shown does not have this attribute because the application uses multiple types but defaults to Normal Help. The user has the option of changing the help type; when the type is changed, ATTR_GEN_HELP_TYPE is added to the GenApplication dynamically.

Other than the above customizations, using another help type is as simple as using Normal Help. Other considerations must be observed when creating the help files, but the code difference is simple.

Code Display 13-2 Adding Help Controllers

/*    The GenApplication has two help controllers as its children.
 * FirstAidHelpControl manages First Aid help, and SimpleHelpControl manages
 * Simple Help. In addition, the Normal Help controller--provided by the
 * system--manages Normal Help. Both custom help controllers must be put both on
 * the active list and on the GAGCNLT_NOTIFY_HELP_CONTEXT_CHANGE list.
 *    Normally, the GenApplication would have ATTR_GEN_HELP_TYPE declaring the
 * help type used. This application, however, defaults to HT_SYSTEM_HELP and
 * therefore does not need the attribute. */
@object GenApplicationClass HelpSampApp = {
    GI_visMoniker = list { @HelpSampTextMoniker };
    GI_comp = @HelpSampPrimary, @FirstAidHelpControl, @SimpleHelpControl;
    gcnList(MANUFACTURER_ID_GEOWORKS,GAGCNLT_WINDOWS) = @HelpSampPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_NOTIFY_HELP_CONTEXT_CHANGE) =                                     @FirstAidHelpControl, @SimpleHelpControl;
    gcnList(MANUFACTURER_ID_GEOWORKS, MGCNLT_ACTIVE_LIST) =
                @FirstAidHelpControl, @SimpleHelpControl;
}
@visMoniker HelpSampTextMoniker = "C Sample App with Help";
/*    The help controllers may manage at most one HelpType each. Thus, you must
 * set the HCI_helpType field so the controller knows what type of help it
 * manages. */
@object HelpControlClass FirstAidHelpControl = {
    GI_states = @default | GS_ENABLED;
    HCI_helpType = HT_FIRST_AID;
}
@object HelpControlClass SimpleHelpControl = {
    GI_states = @default | GS_ENABLED;
    HCI_helpType = HT_SIMPLE_HELP;
}

Help Object Library: 3.3 Customizing Help: Sizing the Help Dialog Box

 HINT_HELP_TEXT_FIXED_SIZE, HINT_HELP_NOT_RESIZABLE

Depending on what you're using help for, you may want to keep the help dialog box a fixed size, or you may wish to make it not resizable. For both cases, you must use a custom help controller object and set one of the following hints on the controller:

HINT_HELP_TEXT_FIXED_SIZE
This hint is exactly the same as GenClass ' HINT_FIXED_SIZE ; it takes a CompSizeHintArgs structure as its extra data and sets the help controller's dialog box to the fixed size.
HINT_HELP_NOT_RESIZABLE
Normally, help controllers set up as GIV_DIALOG are resizable. This hint will counteract that behavior and make the window not resizable.

Help Object Library: 3.4 Customizing Help: Managing Help Types

 HelpType,  ATTR_GEN_HELP_TYPE,  MSG_META_GET_HELP_TYPE

The brand of help your application provides is defined by its HelpType . This value is set in the HelpControl object's HCI_helpType instance field. For normal help, you don't need to set anything; the type will automatically be HT_SYSTEM_HELP for the system help object (you should not set this type for your own help control objects).

There are five other help types you can use: HT_NORMAL_HELP, HT_FIRST_AID, HT_STATUS_HELP, HT_SIMPLE_HELP, and HT_SYSTEM_MODAL_HELP. You should only explicitly use HT_NORMAL_HELP if you want the normal help setup with some customizations or you are creating a viewer application. The others you must use explicitly if you want to provide those types of help.

(Note: HT_SYSTEM_MODAL_HELP is used for providing help within system-modal dialog boxes only. It is rare that an application will ever use system-modal dialog boxes, but if yours does and you need to provide help within it, use this special type. Such dialog boxes should be given the attribute ATTR_GEN_HELP_TYPE with this type to distinguish them from other dialogs.)

To set an application's help type, you have to first supply a HelpControl object as a child of the GenApplication and set its HCI_helpType field to the proper help type. You also have to put your HelpControl object on the GenApplication's GAGCNLT_NOTIFY_HELP_CONTEXT_CHANGE GCN list. You may have one HelpControl object for each help type supported by your application.

Examples of how to set up the proper objects are shown above in Adding Help Controllers .

Any object may have any help type associated with it. You could have, for example, one dialog box set to Normal Help and another set for Simple Help. This is unusual, and you will usually want to stick to a single help type within one application. To set an object's help type, use ATTR_GEN_HELP_TYPE . You can retrieve the current help type of an object by sending it MSG_META_GET_HELP_TYPE .

Help Object Library: 3.5 Customizing Help: Managing Help Files

 ATTR_GEN_HELP_FILE,  ATTR_GEN_HELP_FILE_FROM_INIT_FILE,  MSG_META_GET_HELP_FILE,  MSG_META_SET_HELP_FILE,  ATTR_GEN_CONTROL_DO_NOT_USE_LIBRARY_NAME_FOR_HELP

The help controller gets the text it displays from help files. Help files are generated by the help editor (a modified GeoWrite) from GeoWrite documents and are located in USERDATA\HELP. An application may use a single help file or multiple help files, and hyperlinks may extend across files.

The help controller figures out the help file to use by looking for ATTR_GEN_HELP_FILE on the generic object that brings up the help. For example, if the user clicks a help trigger in a dialog box, the help controller first gets the context of the dialog box (contexts are described in Help Contexts and Help Triggers ) and will then get its help file. If the dialog box has an ATTR_GEN_HELP_FILE specifying a particular help file, the help controller will try to use that file.

If the dialog box does not have that attribute, the help controller queries up the generic tree until it either finds an ancestor with the attribute or gets to the GenApplication object. If no help file is specified, the help controller will by default use a file with the same name as the geode's permanent name with the extender characters removed. For example, in the sample application HelpSamp, which has the permanent name helpsamp.app (which is defined in the helpsamp.gp file), the default file name would be helpsamp .

An object can use ATTR_GEN_HELP_FILE_FROM_INIT_FILE to specify that its help file should be garnered from the GEOS.INI file. If this attribute is used, the help controller will look in the GEOS.INI file for a category of the same name as the application and a key named "helpfile." Thus, to set the help file to "My Own Help File" for the HelpSamp application, you could add the following to your GEOS.INI file:

[HelpSamp]
helpfile = My Own Help File

If, however, no object has ATTR_GEN_HELP_FILE_FROM_INIT_FILE , this entry will not be noticed by the help controller.

Controller objects, which typically exist within their own libraries, may use the help files provided by the application that includes them rather than the help file normally used by the library. To have a controller do this, set ATTR_GEN_CONTROL_DO_NOT_USE_LIBRARY_NAME_FOR_HELP for the controller in your application. This will have the controller continue querying up the generic tree when searching for the proper help file to use.

The help file used by a given object may also be changed dynamically with MSG_META_SET_HELP_FILE . This message takes a pointer to the new file's name and sets the object's ATTR_GEN_HELP_FILE attribute to the new file. This message is used by the HelpSamp sample application.

The help file can be retrieved with MSG_META_GET_HELP_FILE . This message returns the name of the file being used by the recipient generic object.

Help Object Library: 3.6 Customizing Help: Customizing the Pointer Image

 ATTR_HELP_CUSTOM_POINTER_IMAGE,  MSG_HELP_CONTROL_GET_POINTER_IMAGE

The help controller changes the pointer image when the pointer is over a link in the help text. This is so the user knows where to click and expects a link to be followed. Applications that want custom pointers can set the "link" pointer image with ATTR_HELP_CUSTOM_POINTER_IMAGE . This attribute takes an optr to a custom pointer image definition.

The pointer image is a PointerDef structure and must be stored in a chunk in a sharable, non-duplicated data block. Note also that to use a custom pointer image, you must explicitly declare a HelpControl object, even for Normal Help.

You can retrieve the link pointer image by sending MSG_HELP_CONTROL_GET_POINTER_IMAGE to the HelpControl object. This will return the current image in use, either a custom image or the default. This message is detailed on MSG_HELP_CONTROL_GET_POINTER_IMAGE .

Help Object Library: 3.7 Customizing Help: Changing the Help Features

Because the HelpControl is a subclass of GenControlClass , you can change the features it offers both in your .goc file and dynamically. For complete information, see the Controllers chapter. The features of the help controller are detailed in HelpControlClass Reference .

Help Object Library: 4 Creating Help Files

Perhaps the most involved step of adding help to your application is actually creating the help files. Help files are special, compressed versions of GeoWrite files that reside in the \USERDATA\HELP directory. There are several steps to creating help files:

  1. Enable the Help Editor
    The Help Editor is simply a special version of GeoWrite.
  2. Organize and write the text
    Depending on your application, your help text, and how you wish to present it, this can be simple or complex. You must keep several things in mind when organizing your help.
  3. Define files and contexts
    Each help file must have its contexts named, and each context must be defined. If you are using multiple help files, you must define the help contexts for each file in use, and you must "define" each file being used.
  4. Set contexts
    Each page of help must have its own context name.
  5. Set hyperlinks
    After all contexts are named and set, you may set hyperlinks to them.
  6. Generate the help files
    When the help text is ready, you must generate the help file(s). They will then be available for your application to open and view.

Enabling the Help Editor

Organizing and Writing the Text

Defining Files and Contexts

Using Hyperlinks

Generating the Help Files

Help Object Library: 4.1 Creating Help Files: Enabling the Help Editor

The Help Editor is GeoWrite with the Help Editor option turned on. To turn on this option, manually edit your .INI file to add the category and key as follow:

[configure]
 helpEditor = true

Without the above lines, you can't access the Help Editor. After adding them, start up GeoWrite and change the user level. Choose "Customize" and turn on the Help Editor option at the end of the list. When you apply the change, a new "Help Editor" menu should appear towards the right of the menu bar.

Help Object Library: 4.2 Creating Help Files: Organizing and Writing the Text

If your help text is simple and straightforward, you can probably dive right in and write it. If it is not, though, you should spend some time up front organizing it, naming the contexts and files, and figuring out where hyperlinks will be placed. Defining contexts and links as you're writing can quickly get confusing, even for experienced writers.

Most help text will be configured for Normal Help. This type of help typically has a table of contents page (TOC) and numerous other contexts, many of which are linked to each other. Normally, a help context (page) will be accessible through two means: First, the user could click a help trigger or hit F1 at the right time to bring up the proper context. In this case, the user can not "go back" to the previous context, although he can get to the TOC page by clicking the Contents button. Second, the user could navigate through the help system via hyperlinks and get to the context.

Not all contexts may be available through links, however; some may be accessible only through clicking a help trigger or hitting F1. Other contexts may be accessible only via navigation of hyperlinks in the help document. You should plan this out in advance so you can write effective text and set your links properly.

Organizing the Help

Perhaps the most straightforward help organization is a simple listing of topics, each of which is a hyperlink to another help screen. For example, the "Change File" help screen in the HelpSamp sample application's default file gives a brief description of the Change File dialog box and then three subtopics, each of which links to another context in the help file. (This screen is shown below.)

A First Aid file, however, is designed differently. It has three "levels" of help: Contents, Chapter, and Article. The Contents page is a TOC page, just as in Normal Help. The TOC button allows the user quick access to that level at all times. When the user selects an entry on the TOC page, a Chapter-level context should be brought up. At this point, only the TOC button is enabled, and the Chapter button is enabled and selected.

The Chapter-level context should similarly list subtopics of interest. When the user selects one of these, an Article-level context will be displayed. At this point, both the Contents button and the Chapter button will be enabled, allowing the user to "back up" to one of those previous levels. Also at this point, the Article button will be enabled and selected. The Article-level context (currently viewed) may contain a list of several questions or subtopics. When the user follows one of these links, the Article button will be enabled but deselected, allowing the user to quickly return to any of the three levels traversed to this point.

First Aid, like Normal Help, may have contexts linked to any other contexts in the same or other files; they are not restricted to the TOC-Chapter-Article links mentioned above.

Simple Help normally will not have any links because it offers no way to go back or even to return to a TOC page. If your application offers Simple Help, you should keep it straightforward and not put in any links.

Using Visual Cues and Graphics

Although the help controller will change the pointer image when the pointer is over a hyperlink, you should use graphics or highlighted text whenever possible to call attention to the links. Because the Help Editor is extended GeoWrite, anything you can do to normal GeoWrite text you can put in your help files.

Long pages of text can be difficult to read unless you put in headers or other highlighting. Some suggested highlighting techniques are to use colored or gradient-filled text, use the boxed or button text styles, or underline the text. Using different sizes and fonts is also effective. Keep in mind, however, that many users will have monochrome, CGA-style screens.

You can also embed graphics in the text. This does not include using the "graphic layer" of GeoWrite, however--since the Help Editor uses only the text layer, it will not use graphics pasted to the graphic layer. You must paste them directly into the text.

A useful tip to creating consistent help documents is to pre-define a number of style sheets in GeoWrite and use those styles for all your help. Any time a new style is required, create a new style sheet.

Help Object Library: 4.3 Creating Help Files: Defining Files and Contexts

If all your help resides within single files and links do not cross files (all links are internal to their own files), then defining contexts is simple. However, if links can cross files, you must do a little more work.

When you "define a context," you are notifying the Help Editor that a context with a given name and type exists in the specified file. When all links are internal to a single file, or when you have no links (as with Simple Help), you can simply define contexts for the "same file." However, if you have links crossing files, you must define both the file and context for each external link (similar in function to an "external" definition in your code).

To Define a File

Open the Define File dialog box via the Help Editor menu. Type the name of the file that will have links into it, and click "Add File." You can delete or rename previously defined files.

To Define a Context

Open the Define Context dialog box via the Help Editor menu. Use the upper-left popup list to select which file the new context will be defined for. Use the upper-right popup list to define the type of the file:

Text
A normal text help page. Use this for all contexts in all help types except First Aid as noted below.
TOC
A First Aid TOC page.
Chapter
A First Aid Chapter-level page.
Article
A First Aid Article-level page.

Once you've selected the proper type, name the context and click "Add Context." You can also delete and rename previously-defined contexts.

To Set a Context

Each help page may be recognized as only one context. To set a context

  1. Open the "Set Context" dialog box via the Help Editor menu.
  2. Select some text at the beginning of the help page. Be sure that you have the page's first character selected; otherwise the context name may not "take".
  3. Select the context name from the dialog box, and click "Apply." That page will be set to the selected context.

Pages in the GeoWrite file that do not have help contexts applied will not be generated into the help file. This fact is useful for recording information within the file about the file itself (release dates, draft dates, author name, revision notes, etc.).

Help Object Library: 4.4 Creating Help Files: Using Hyperlinks

You can set any selected text or graphic to be a hyperlink to another context in any help file. To set a hyperlink, open the "Set Hyperlink" dialog box via the Help Editor menu. Select the text that, when clicked on, should cause the new context page to appear. Then select, in the "Set Hyperlink" dialog, the appropriate file and context names. Click "Apply," and the selected text and/or graphic will act as a hyperlink to that file and context.

Once a hyperlink is created, you can follow it in the GeoWrite document by using the "Follow Hyperlink" entry in the Help Editor menu. This option will not be enabled unless the current cursor or selection is a hyperlink.

Help Object Library: 4.5 Creating Help Files: Generating the Help Files

Once the help file is created, all the contexts are defined and set, and all the hyperlinks (if any) are established, you can generate the help file. When the file is generated, it is automatically placed in USERDATA\HELP, and it will have the same name as the GeoWrite document being edited. Thus, if you are editing a file called "helpsamp," the help file would be USERDATA\HELP\ helpsamp .

The help file may be compressed or not. The default is to compress the help data to save disk space. Since help files are read-only, the help controller decompresses the data when loading it, then simply discards it when done. The "Compress Data in File" option in the Help Editor menu allows you to specify whether the help file should be compressed or not (just about the only time it should not be compressed is for Status Help).

To generate the help file, simply select "Generate Help File" in the Help Editor menu. The generation is one-way; you can not edit a help file. You should, therefore, keep your original GeoWrite documents containing the help text available in case they require editing or update .

Help Object Library: 5 HelpControlClass Reference

This section will only be useful if you plan on customizing help for your application. Nearly all of what you need for creating normal help files and adding help to your application is described in the previous sections. The following is detailed reference information for HelpControlClass (shown in HelpControlClass ).

Code Display 13-3 HelpControlClass

    @instance HelpType			 HCI_helpType;			/* See HelpType, below */
    @instance MemHandle			HCI_curFile;			/* Internal */
    @instance MemHandle			HCI_historyBuf;			/* Internal */
    @instance word			HCI_nameArrayVM;			/* Internal */
    @instance GeodeHandle			HCI_compressLib;			/* Internal */
        @default GII_attrs = (@default | GIA_NOT_USER_INITIATABLE);
        @default GII_visibility = GIV_DIALOG;
    @vardata void				 ATTR_HELP_SUPPRESS_INITIATE;
    @vardata char[]				 ATTR_HELP_INITIAL_HELP;
    @vardata optr				 ATTR_HELP_CUSTOM_POINTER_IMAGE;
    @vardata CompSizeHintArgs 				 HINT_HELP_TEXT_FIXED_SIZE;
    @vardata void				 HINT_HELP_NOT_RESIZABLE;
    @vardata char[]				 ATTR_HELP_INITIAL_HELP_FILE;
typedef ByteEnum  HelpType;
    #define HT_NORMAL_HELP				0
    #define HT_FIRST_AID				1
    #define HT_STATUS_HELP				2
    #define HT_SIMPLE_HELP				3
    #define HT_SYSTEM_HELP				4	/* Reserved for system use */
/* Internal Vardata fields--do not use these. */
    @vardata char[]			TEMP_HELP_ERROR_FILENAME;
    @vardata char[]			TEMP_HELP_TOC_FILENAME;
    @vardata void			TEMP_HELP_DETACH_RECEIVED;

HelpControlClass is a subclass of GenControlClass and, as such, inherits the feature management attributes and messages thereof. For complete information on feature and tool sets, see the Controllers chapter. The HelpControl object has features but no tools. The feature set of HelpControlClass is shown in HelpControl Features .

Code Display 13-4 HelpControl Features

/* These features can be turned on or off with ATTR_GEN_CONTROL_REQUIRE_UI and
 * ATTR_GEN_CONTROL_PROHIBIT_UI. They can be turned on or off dynamically with
 * MSG_GEN_CONTROL_ADD_FEATURE and MSG_GEN_CONTROL_REMOVE_FEATURE. */
typedef WordFlags  HPCFeatures;
    #define HPCF_HELP					0x0100	/* has a "help on help" trigger */
    #define HPCF_TEXT					0x0080	/* has text */
    #define HPCF_CONTENTS					0x0040	/* has "Contents" button */
    #define HPCF_HISTORY					0x0020	/* has "History" button */
    #define HPCF_GO_BACK					0x0010	/* has "Go Back" button */
    #define HPCF_CLOSE					0x0008	/* has "Close" button */
    #define HPCF_INSTRUCTIONS					0x0004	/* has "Instructions" button */
    #define HPCF_FIRST_AID_GO_BACK					0x0002	/* has "Go Back" button for
                         * First Aid */
    #define HPCF_FIRST_AID					0x0001	/* has First Aid configuration */

HelpControlClass , as a GenControl subclass, receives notification when the controlled item changes. For example, a change in help context will cause the HelpControl object to display the new context page. The HelpControl object must be put on the GAGCNLT_NOTIFY_HELP_CONTEXT_CHANGE notification list. When a context change occurs, the help controller will receive MSG_META_NOTIFY_WITH_DATA_BLOCK , with the notification type GWNT_HELP_CONTEXT_CHANGE . This notification type passes a data block containing a NotifyHelpContextChange structure, which is shown below.

Code Display 13-5 Help Notification Structure

/* This data structure is passed in MSG_META_NOTIFY_WITH_DATA_BLOCK to the help
 * controller when a change in the help context occurs. This structure can be
 * generated by calling HelpSendHelpNotification(). This structure uses the
 * following constant and type. */
#define MAX_CONTEXT_NAME_SIZE				22
typedef char		ContextName[MAX_CONTEXT_NAME_SIZE];
typedef struct {
    HelpType			NHCC_type;			/* HelpType involved in change */
    ContextName			NHCC_context;			/* New context to link to */
    FileLongName			NHCC_filename;			/* New file name to link to */
    FileLongName			NHCC_filenameTOC;			/* File name to get TOC from */
}  NotifyHelpContextChange;

HelpControlClass has two exported messages that subclasses may intercept. These are not often intercepted by subclasses. Their reference information is presented below.

MSG_HELP_CONTROL_FOLLOW_LINK

Cause the HelpControl object to follow the specified link in the help file.

Source: Unrestricted; typically internal to the controller.

Destination: The HelpControl object to follow the link.

Parameters: link name 16-bit token of the link name.

link file
16-bit token of the link file.

Return: Nothing.

Interception: Not generally intercepted.

MSG_HELP_CONTROL_GET_POINTER_IMAGE

Return the pointer image to be used when the pointer is over a link in the help text. It looks for ATTR_HELP_CUSTOM_POINTER_IMAGE and returns the pointer stored there; if no custom pointer exists, the default is returned.

Source: Unrestricted; typically internal to the controller.

Destination: The HelpControl object using the custom pointer.

Parameters: not over link Pass FALSE if the pointer is not over a link, TRUE if it is. (Must pass TRUE, not just non-zero).

Return: A word of MouseReturnFlags . If MRF_SET_POINTER_IMAGE is set in this record, the optr of the pointer image will also be returned. Otherwise, the optr will be NullOptr.

Interception: A "viewer" application may subclass this and return its own pointer images if they are dynamic. If static pointer images are used, the application should use ATTR_HELP_CUSTOM_POINTER_IMAGE instead. There is no need to call the superclass with this message .

This document is a single-page version of a a multi-page document, suitable for easy printing.


The List Objects

The GenItemGroup and GenBooleanGroup objects manage lists of objects (GenItems or GenBooleans, respectively) that contain user-selectable options. GenDynamicListClass is a subclass of GenItemGroupClass which allows efficient dynamic and scrolling lists. All of the list objects manage children which each represent a particular option. The user can generally select and deselect options in such a group, and in doing so cause an action to take place.

The list groups and list items work together to manage states of objects within your application. These list objects perform all of the complex state management of their children; the selection items themselves are very simple objects.

You should be familiar with generic objects before reading this chapter. Please see the UI Overview for an overview of each generic object in the user interface. You should be particularly familiar with the instance data of and messages handled by GenClass .

  1 List Object Features
  2 Common Behavior
    2.1 Applying the Action
    2.2 State Information
  3 GenItemGroups
    3.1 GenItemGroup Instance Data
    3.2 GenItem Instance Data
    3.3 GenItemGroup Basics
    3.4 Working with Items
    3.5 Scrolling GenItemGroups
    3.6 GenItemGroup Links
    3.7 Limitations of the GenItemGroup
  4 GenDynamicListClass
    4.1 DynamicList Instance Data
    4.2 DynamicList Basics
    4.3 Altering Instance Data
  5 GenBooleanGroups
    5.1 GenBooleanGroup Instance Data
    5.2 GenBooleanGroup Usage
    5.3 Altering Instance Data

The List Objects: 1 List Object Features

The list objects provide a highly flexible way to display and manage user-selectable options. Depending on the Specific UI and the type of list, these objects may be built out in a variety of fashions. In some UIs, BooleanGroups may appear as checkboxes, GenItemGroups may appear as radio buttons, etc.

You may manipulate the grouping objects in much the same way as a GIV_SUB_GROUP GenInteraction. For example, hints may alter the placement and dimensions of the grouping object's children or the list may appear with special dimensions. The items themselves are simple generic objects that can be easily added to your grouping object. The grouping object will manage the geometry and selection state of these children; it will also perform any required actions based on the state of these children.

There are three list management objects.

  • GenItemGroup
     An all-purpose grouping object, this list may be exclusive (only one item may be selected at a time) or non-exclusive and may also have scroll bars. A GenItemGroup may only contain GenItemClass objects as its children.
  • GenBooleanGroup
     This list is designed for entries that are either true of false ("on" or "off") independent of other entries. The BooleanGroup keeps track of its selections with a bitfield representing the boolean state of each of its children. A GenBooleanGroup object may only contain GenBooleanClass objects.
  • GenDynamicList
     This list is designed for very long or changing length lists. In many respects it is similar to a GenItemGroup but also allows efficient dynamic lists. A GenDynamicList does not specify any explicit "children"; instead it contains a list of monikers to display to the user within a scrolling list.

These are the following main list item objects:

  • GenItem
     Objects used as entries in a GenItemGroup. In OSF/Motif, these objects appear in most cases as "diamond" settings.
  • GenBoolean
     Objects used as entries in a GenBooleanGroup representing true or false states. In OSF/Motif, these items appear most often as selectable "boxes."

The GenDynamicList does not specify any generic children, but instead will contain a list of visual monikers to cycle through a scrolling list.

The List Objects: 2 Common Behavior

Each list object acts differently and you will need to carefully review these differences before using them. There are a number of similarities in the functionality of these list objects, however. This next section details these similarities.

Each list group acts as a grouping object for its generic children. The list groups have much of the same geometry capabilities as a GIV_SUB_GROUP Interaction; they can arrange their children in a variety of fashions and can also be manipulated within their parent.

The items themselves may in general be either "on" (selected) or "off" (unselected). Depending on the behavior of the list, selecting an item may cause an action to occur and may affect the other selections. In exclusive lists, for example, selecting any item deselects any other item.

Applying the Action

State Information

The List Objects: 2.1 Common Behavior: Applying the Action

Often, each list object will contain an "apply" message. This message will be sent out when the list's state has changed and the new state should be applied in the application. For example, a list of colors ("Red", "Green", and "Blue") may have a message MSG_GEN_VIEW_SET_COLOR that sets the background color of a GenView. Changing the state of the list (by selecting an item) will cause this notification message to be sent to the view. More typically, though, the apply message will be sent to the application or a controller object.

Alternatively, you could forego including an apply message in the list object's instance data. In this case, whenever the object receives a MSG_GEN_APPLY it would query the state of its children and act upon that state.

When the user makes a selection, the apply action may or may not be sent out immediately, depending on the operating mode of the list. GenItemGroups, GenBooleanGroups, and GenDynamicLists may operate in one of two modes: immediate or delayed. (For a more complete discussion of delayed and immediate modes, see the GenInteractionClass chapter.) Typically, a list object will operate in delayed mode if it is placed within a GIT_PROPERTIES Interaction. Other generic objects such as GenInteractions, GenTexts, and GenValues also utilize delayed and immediate mode.

In immediate mode, any changes made in the list's state are reflected immediately in the application. If the user changes an immediate mode list object concerning a text style change, the text style of the current selection will change immediately. Changing the state of the list will cause the apply action associated with that object to be sent out immediately.

In delayed mode, any changes made in the list's state are not reflected until some later time, when the changes are "applied." This apply may be effected by the user (through an "apply" trigger) or by the application when it deems it appropriate. In delayed mode, the notification message will not be sent on changes to the state of the list but only when the list receives notice to apply the changes.

Occasionally, a list operating in delayed mode may wish to send out a different message whenever the user changes its state, even if that change should not be immediately applied. In this case, you can supply a status message for the object to send to the destination whenever the user makes changes within the list group.

Each list must have a destination to send its apply action and, if needed, status messages to. This destination can be any object or process capable of handling the message.

The List Objects: 2.2 Common Behavior: State Information

In most cases, a selection will be either on or off. In some cases, however, a selection or a list's state will be indeterminate--considered neither completely on or off. For example, a styles list may show the characteristics of the current highlighted text (bold, italic, underlined, etc.). Selecting a whole paragraph of text in which only one word is bold will leave the "bold" entry in the styles list in an indeterminate state--some of the selected text is bold, some is not.

The lists keep track of these indeterminate states themselves. In some cases, an entire list may be indeterminate; in other cases, individual items may be indeterminate. It is up to the specific UI to represent an indeterminate state. (In OSF/Motif settings that are indeterminate are shown unselected.)

If a list object is operating in delayed mode, changes within the list will not be effected until either the user or the application "applies" them. The list objects keep track of changes in state by storing a modified state of either the list or individual items. Whenever an apply action is made on a list group, the group will check whether its list (or its items) have been modified. If nothing has been marked modified, the list will not apply those changes (unless you wish to override this default behavior).

This modified state is independent of the list's mode. In immediate mode, a change in state to a list object will automatically mark that list object modified and simultaneously call MSG_GEN_APPLY on the list object. The list object will recognize that it is modified and will send out the group's apply action. In delayed mode, any changes made to the object will also mark the group modified; at some later point, MSG_GEN_APPLY will be sent to the list; the object will recognize that the group has been modified and will then send out its apply action.

The List Objects: 3 GenItemGroups

The GenItemGroupClass and GenItemClass work together to allow an application to set up a list of options for a user to choose. A GenItemGroup may contain one or more GenItems; each GenItem may represent a particular option in the list. The user may select one or more of the options at any time, depending upon the behavior specified for the list, and the user can generally select or deselect an option by activating the corresponding GenItem.

GenItemClass objects are simple list items. GenItems are used only as entries within a GenItemGroup. In most cases, a GenItem will only need a moniker and a special identifier associated with the selection. The GenItemGroup will use this identifier to refer to the object when it needs to communicate to the items or when it sends out messages.

Your application interacts with GenItems though the GenItemGroup; this group controls the items and keeps track of which are selected. The GenItemGroup object has all of the same geometry features as a grouping type GenInteraction. (See the GenInteractionClass chapter).

GenItemGroup Instance Data

GenItem Instance Data

GenItemGroup Basics

Working with Items

Scrolling GenItemGroups

GenItemGroup Links

Limitations of the GenItemGroup

The List Objects: 3.1 GenItemGroups: GenItemGroup Instance Data

GenItemGroup and GenItem objects contain a variety of information. The bulk of the functionality of the list is contained in the GenItemGroup; the items themselves only contain identifying information that the list uses.

The GenItemGroup contains instance fields relating to the state of its children GenItems. These instance fields are listed in GenItemGroup Instance Data . Any objects of GenItemGroupClass or one of its subclasses will contain these instance fields, along with the instance fields of GenClass .

Code Display 11-1 GenItemGroup Instance Data

    @instance GenItemGroupBehaviorType					GIGI_behaviorType = GIGBT_EXCLUSIVE;
/* The GenItemGroupBehaviorType enumerations are used in the GIGI_behaviorType
 * instance field. */
typedef ByteEnum GenItemGroupBehaviorType;
#define GIGBT_EXCLUSIVE 0
#define GIGBT_EXCLUSIVE_NONE 1
#define GIGBT_EXTENDED_SELECTION 2
#define GIGBT_NON_EXCLUSIVE 3
    @instance GenItemGroupStateFlags					GIGI_stateFlags = 0;
/* The GenItemGroupStateFlags are used in the GIGI_stateFlags instance field. */
typedef ByteFlags GenItemGroupStateFlags;
#define GIGSF_INDETERMINATE 				0x80
#define GIGSF_MODIFIED				0x40
    @instance word					GIGI_selection = GIGS_NONE;
/* The GIGS_NONE constant is used with the GIGI_selection instance field and may
 * also be returned by MSG_GEN_ITEM_GROUP_GET_SELECTION. */
#define GIGS_NONE (0xffff)
    @instance word					GIGI_numSelections = 0;
    @instance optr					GIGI_destination;
    @instance Message					GIGI_applyMsg = 0;

GIGI_ behaviorType describes the selection behavior of the list. The default type is GIGBT_EXCLUSIVE, which creates an exclusive list. A full description of each type is provided in GenItemGroup Behavior .

GIGI_ stateFlags contains flags that affect the state of the GenItemGroup. These flags indicate whether a GenItemGroup is in an indeterminate or a modified state.

GIGI_ selection contains the name of the current GenItem child that is selected. GenItemGroups use the identifying keywords stored in each GenItem's GII_ identifier instance field to reference those selections. If more than one item may be selected at a time (if GIGI_ numSelections is greater than one), this instance field will point to a list of GenItem identifiers rather than store a single identifier.

GIGI_ numSelections contains the number of GenItems currently selected. For exclusive or exclusive-none lists, this number will be either one or zero. For multiple selection lists, this number may be greater than zero. Unless you have an item group that initially appears with more than one selection, you will not need to set this up in your object declaration.

GIGI_ destination contains the optr of the object or process to handle messages sent out by this GenItemGroup. This destination object will receive the notification message in the GIGI_ applyMsg instance field whenever state changes are applied within the GenItemGroup.

GIGI_ applyMsg stores the apply action for the GenItemGroup to send out whenever state changes should be applied, either through an immediate state change or through a delayed mode activation.

The GenItemGroup also has several vardata fields.

Code Display 11-2 GenItemGroup Vardata Instance Fields

@vardata Message 			ATTR_GEN_ITEM_GROUP_STATUS_MSG;
@vardata void			ATTR_GEN_ITEM_GROUP_SET_MODIFIED_ON_REDUNDANT_SELECTION;
@vardata Message			ATTR_GEN_ITEM_GROUP_CUSTOM_DOUBLE_PRESS;
@vardata optr			ATTR_GEN_ITEM_GROUP_LINK;
    @reloc ATTR_GEN_ITEM_GROUP_LINK, 0, optr;
@vardata void			ATTR_GEN_ITEM_GROUP_INIT_FILE_BOOLEAN;
/* Hints */
@vardata void		HINT_ITEM_GROUP_RADIO_BUTTON_STYLE;
@vardata void		HINT_ITEM_GROUP_TOOLBOX_STYLE;
@vardata void		HINT_ITEM_GROUP_SCROLLABLE;
@vardata void		HINT_ITEM_GROUP_MINIMIZE_SIZE;
@vardata void		HINT_ITEM_GROUP_DISPLAY_CURRENT_SELECTION;
@vardata void		HINT_ITEM_GROUP_MINIMIZE_SIZE_IF_VERTICAL_SCREEN;

ATTR_GEN_ITEM_GROUP_STATUS_MSG assigns a status message to your GenItemGroup. A status message allows objects to receive notice of a change in the GenItemGroup's current state without forcing an apply action. This attribute is useful for cases where the state of your GenItemGroup may reflect information in other user interface objects, and should therefore be updated whenever its state changes, regardless of the need to apply those changes.

In exclusive or extended-selection lists, selecting an already-select item will not change the state of the GenItemGroup and will not mark it modified. ATTR_GEN_ITEM_GROUP_SET_MODIFIED_ON_REDUNDANT_SELECTION alters this default behavior for a GenItemGroup. Including this attribute in the object's instance data will cause the group to be marked modified whenever the user makes a redundant selection, thereby forcing an apply action to later take place. Use this attribute for operations you may wish to repeat even if the state of the GenItemGroup has not changed.

ATTR_GEN_ITEM_GROUP_LINK allows two or more GenItemGroups to be linked together and act as one GenItemGroup. Each GenItemGroup may contain one or more items with identifiers unique to all other items within the linked GenItemGroups. The links should be circular, with the last group pointing back to the first. All of these GenItemGroups should have identical states and behavior types so they will work correctly. Use this attribute to circumvent undesirable geometry constraints. For more information, see GenItemGroup Links .

ATTR_GEN_ITEM_GROUP_CUSTOM_DOUBLE_PRESS allows a special activation message to be sent out on double press events; it is used only in exclusive GenItemGroup lists.

ATTR_GEN_ITEM_GROUP_INIT_FILE_BOOLEAN forces the GenItemGroup to use "true" and "false" in the .INI file rather than numerical values.

HINT_ITEM_GROUP_RADIO_BUTTON_STYLE indicates that the Item Group should appear as "radio buttons" if the Specific UI allows such behavior. OSF/Motif does this by default. HINT_ITEM_GROUP_TOOLBOX_STYLE indicates that the item's should be drawn in toolbox style, with a simple box around the visual moniker.

HINT_ITEM_GROUP_MINIMIZE_SIZE and HINT_ITEM_GROUP_MINIMIZE_SIZE_IF_VERTICAL_SCREEN force the item group to use only the smallest space available. In OSF/Motif, for example, this would cause the item group to be a popup menu. The latter of the two hints takes effect only on screens that are taller than they are wide. HINT_ITEM_GROUP_DISPLAY_CURRENT_SELECTION signals that a minimized list should display the currently seleted item's moniker when said list is in its minimized state.

The List Objects: 3.2 GenItemGroups: GenItem Instance Data

GenItemClass is a subclass of GenClass . It has one new instance field:

@instance word GII_identifier;

GII_ identifier sets the indentifying keyword (usually an enumerated type) that uniquely identifies this GenItem. This identifier is used by the GenItemGroup parent to indicate the current selection and otherwise reference the item. Each GenItem identifier within the same GenItemGroup must evaluate to a unique value.

The List Objects: 3.3 GenItemGroups: GenItemGroup Basics

The GenItemGroup manages a group of GenItems. In general, you must decide which behavior type you wish your GenItemGroup to exhibit, the message for the list to send out, and the object (or process) to handle messages sent out by this group. You may also set a number of initial conditions (items selected, whether the list is indeterminate or modified, etc.) for the list.

GenItemGroup Behavior

GIGI_behaviorType, MSG_GEN_ITEM_GROUP_SET_BEHAVIOR_TYPE, MSG_GEN_ITEM_GROUP_GET_BEHAVIOR_TYPE

There are several types of user selection behavior for a GenItemGroup. Items can be set up so that only one is selected at a time, one or none is selected at a time, multiple items can be selected, or multiple items can be selected only when extending the selection (e.g. dragging the mouse).

The GIGI_ behaviorType instance field defines the selection behavior of the list. The instance data must be of the enumerated type GenItemGroupBehaviorType . The default type is GIGBT_EXCLUSIVE, which creates an exclusive list; one and only one selection in an exclusive list may be selected at any time. The available types are listed below.

GIGBT_EXCLUSIVE
This is a list where only one option may be selected at a time. Once a selection has been made, making another selection will deselect the previous item. The user may not deselect a currently selected item. Many specific UIs represent these items with radio buttons; OSF/Motif represents these items with "diamond" settings. This type of list is the default for a GenItemGroup.
GIGBT_EXCLUSIVE_NONE
This list is also exclusive--at most one option may be selected at a time--but the user can also deselect an item, leaving no items selected. GenItemGroups show a "none-selected" state with GIGS_NONE in their GIGI_ selection instance fields.
GIGBT_EXTENDED_SELECTION
This list is normally exclusive, unless the user "extends" the selection. The user can extend the selection either by dragging the mouse through another selection or by clicking on a new selection in conjunction with a special keystroke. In OSF/Motif, clicking on an item while holding the SHIFT key will extend the selection (leaving any other selections unaffected) to the one clicked on. Extending a selection to a currently selected item will deselect that item.
GIGBT_NON_EXCLUSIVE
This list does not exhibit any exclusive behavior. Any or all settings may be on at any time, and selecting any items will not affect the state of other settings. In many case, you may want to use a GenBooleanGroup in place of a non-exclusive GenItemGroup. For example, a non-exclusive GenItemGroup cannot store individual indeterminate or modified states for its children; GenBooleanGroup stores this information.
Exclusive Lists

Exclusive lists are designed for cases in which one and only item should be selected at any given time. For example, a graphics application may want to prompt the user to select a color for drawing lines. The application can display this list of colors within an exclusive list. Only one color may be selected. Once a color is selected, selecting any other color will deselect the first color.

Exclusive lists may (and by default do) appear without any item initially selected. This may lead to problems if the application tries to apply any changes before an item is selected (such as within a delayed-mode dialog box). If your application needs an item to be selected at all times in order to operate correctly, your list should appear with one of the items initially selected (or with the "apply" trigger disabled).

Exclusive-None Lists

Exclusive-None lists are designed for cases in which either one item must be selected or no items should be selected. For example, a word processor may have a "Justifications" menu with several options such as left-justified, right-justified, or center-justified. Selecting any of these options will perform the justification operation on the current paragraph, but the user may also deselect any option, leaving no justification on the paragraph.

Exclusive-None lists by default appear without any items initially selected. Your application should, of course, be able to handle this case of nothing selected; if it cannot, the list should probably not be exclusive-none.

Extended-Selection Lists

Extended-Selection lists are designed for cases in which the application usually needs an exclusive selection but also desires to let the user select multiple items for bulk operations. Multiple selections are stored as lists of items.

For example, a disk copy operation may have an extended-selection list to specify the drive to copy a file to. In most cases, the user will only want to copy to one drive, and the list normally operates in exclusive mode to accommodate this. By extending the selection, however, the user can request the list to copy the file to multiple drives.

It is up to the specific UI how the user may extend a selection. In OSF/Motif, the user may select another item in tandem with a special keystroke (the SHIFT key) to extend the selection from one item to several. Extending a selection to an already selected item will deselect that item, leaving any other items unchanged.

Non-Exclusive Lists

Non-exclusive lists are designed for cases in which any or all of the items may be selected at once. Multiple selections, as in the extended-selection GenItemGroup, are stored as lists of items. Selecting an already selected item will deselect that item. Therefore, there may be a state where nothing is selected within a non-exclusive list.

For example, an interactive tutorial may ask the user if he wants more information on certain topics, giving these topics in a non-exclusive list. The user can freely choose as many or as few items as he wishes, and then select "Print" to print out information on those items.

In most cases, however, you might rather use a GenBooleanGroup object in place of a non-exclusive GenItemGroup. A GenBooleanGroup acts similar to a non-exclusive list but is streamlined to operate efficiently for lists with 16 or fewer items. (GenBooleanGroups represent their children's state through a word-length bitfield; therefore they may only accommodate 16 children.) You may also need to use a GenBooleanGroup if you need to keep track of individual indeterminate and modified states.

MSG_GEN_ITEM_GROUP_GET_BEHAVIOR_TYPE returns the behavior type stored in the list's GIGI_ behaviorType instance field. To set a new behavior type, send the list a MSG_GEN_ITEM_GROUP_SET_BEHAVIOR_TYPE .

MSG_GEN_ITEM_GROUP_GET_BEHAVIOR_TYPE
GenItemGroupBehaviorType MSG_GEN_ITEM_GROUP_GET_BEHAVIOR_TYPE();

This message returns the current behavior type of a GenItemGroup (stored in the GIGI_ behaviorType instance field).

Source: Unrestricted.

Destination: Any GenItemGroup.

Parameters: Nothing.

Return: The GenItemGroupBehaviorType of the GenItemGroup.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_BEHAVIOR_TYPE
void	MSG_GEN_ITEM_GROUP_SET_BEHAVIOR_TYPE(
        GenItemGroupBehaviorType				behaviorType);

This message sets a new behavior type for a GenItemGroup by changing the object's GIGI_ behaviorType instance field. This message should only be sent while the object is not usable.

Source: Unrestricted.

Destination: Any non-usable GenItemGroup object.

Parameters: behaviorType The new GenItemGroupBehaviorType .

Return: Nothing.

Interception: Generally not intercepted.

GenItemGroup Selections

GIGI_selection, GII_identifier, GIGI_numSelections, MSG_GEN_ITEM_GROUP_SET_NONE_SELECTED, MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION, MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS, MSG_GEN_ITEM_GROUP_GET_SELECTION, MSG_GEN_ITEM_GROUP_GET_NUM_SELECTIONS, MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS

Within a GenItemGroup, each GenItem must contain an identifier, often an enumerated value, to uniquely identify that item. The GenItemGroup will use this identifying value whenever referencing the GenItem (or items), either through its instance data or through messages. This value is stored in the GenItem's GII_ identifier instance data.

In most cases, your collection of GenItems within a particular GenItemGroup should be different enumerated values. Each item within a specific GenItemGroup must have a unique identifier to reference each item. To ensure this, it is usually convenient to use an enumerated type. Make sure that you don't use two different enumerated types because they may not contain unique values across types.

In an exclusive or exclusive-none list, the GenItemGroup will store the identifier of the current selection in the GIGI_ selection instance field. If no item is selected, this instance field will instead store the value GIGS_NONE (-1). (For an exclusive list, this case could only arise before an item is initially selected.)

Note that -1 is still a legal identifier for a GenItem. If you do use this identifier you must be sure to check the GIGI_ numSelections instance field to check whether the GIGI_ selection value corresponds to an item or to the GIGS_NONE value.

GenItemGroups by default appear with no items initially set (GIGI_ selection is set to GIGS_NONE). If you wish a GenItemGroup to initially appear with a selection set to one of the GenItems, set GIGI_ selection to the identifier of that GenItem.

Code Display 11-3 Setting Enumerated Types As Identifiers

/* In most cases, each GenItemGroup should have a single set of enumerated types
 * to name its children. In this case, MyCityGroup will have settings all related
 * to the enumerated type USCities, and San Francisco will be the initial
 * selection. */
typedef ByteEnum USCitiesEnum;
#define CITY_CHICAGO 1
#define CITY_NEW_YORK 2
#define CITY_SAN_FRANCISCO 3
#define CITY_LOS_ANGELES 4
@object GenItemGroupClass MyCityGroup = {
    GI_comp = @Chicago, @NewYork, @SanFrancisco, @LosAngeles;
    GIGI_selection = CITY_SAN_FRANCISCO;
}
@object GenItemClass Chicago = {
    GI_visMoniker = `C', "Chicago";
    GII_identifier = CITY_CHICAGO;
}
@object GenItemClass NewYork = {
    GI_visMoniker = `N', "New York";
    GII_identifier = CITY_NEW_YORK;
}
@object GenItemClass SanFrancisco = {
    GI_visMoniker = `S', "San Francisco";
    GII_identifier = CITY_SAN_FRANCISCO;
}
@object GenItemClass LosAngeles = {
    GI_visMoniker = `L', "Los Angeles";
    GII_identifier = CITY_LOS_ANGELES;
}

In an extended-selection or non-exclusive list, more than one item may be selected at a time. In this case, the GIGI_ selection instance field will not store a single identifier but instead will store a ChunkHandle to a list of identifiers. This functionality is internal to the operation of the list.

You may set up a list (if it is extended-selection or non-exclusive) to initially appear with multiple selections. The list of identifiers may be of arbitrary size but must be manually set up in its own chunk. Set GIGI_ selection to the ChunkHandle of the chunk containing this list. In this case, you must also set GIGI_ numSelections to the proper number of selections. (See Setting Multiple Initial Selections .)

For multiple selection lists (GIGBT_EXTENDED_SELECTION or GIGBT_NON_EXCLUSIVE) GIGI_ numSelections may be set greater than 1 if more than one item is selected. In this case, GIGI_ selection will contain a ChunkHandle to a list of identifiers instead of a single identifier.

Code Display 11-4 Setting Multiple Initial Selections

/* To set a GenItemGroup to appear initially with multiple selections, set up a
 * chunk containing the identifiers and a ChunkHandle in the GIGI_selection
 * instance field. Only the new list chunk and the GenItemGroup are shown; other
 * information is in the previous code display. */
@object GenItemGroupClass MyCityGroup = {
    GI_comp = @Chicago, @NewYork, @SanFrancisco, @LosAngeles;
    GIGI_behaviorType = GIGBT_NON_EXCLUSIVE;
    GIGI_numSelections = 2;
    GIGI_selection = (ChunkHandle) ChunkOf(@SelectionList);
}
@chunk word SelectionList[2] = {
    CITY_CHICAGO, CITY_SAN_FRANCISCO
};

In most cases, the user will change the selections of the GenItemGroup by clicking on items. Your application may also retrieve or alter the items selected with the following messages. In many of these messages, you may have to pass an indeterminate state; if you are unsure about what indeterminate state a list object should be in, pass FALSE. Most GenItemGroups do not need to worry about their indeterminate state.

None of the messages which alter the state or number of selections cause an apply or status message to be sent out by the GenItemGroup. If you wish to send out an apply or a status message in these cases, mark the GenItemGroup modified and send the object MSG_GEN_APPLY or MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG .

MSG_GEN_ITEM_GROUP_GET_SELECTION returns the currently selected item for the GenItemGroup. If there are no items currently selected, this message will return GIGS_NONE. If there is more than one item selected, this message will only return the first item selected in that GenItemGroup. (You should use MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS in this case.)

MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS returns the current list of selections for non-exclusive and extended-selection lists. You must pass this message a pointer to a buffer to hold the list of selections, along with the number of items the buffer can hold. Make sure to allocate enough space in this buffer, or the buffer will not be filled in. Also make sure to send this message with @call and not @send , as it passes a pointer. You can check for the number of items currently selected with MSG_GEN_ITEM_GROUP_GET_NUM_SELECTIONS .

MSG_GEN_ITEM_GROUP_SET_NONE_SELECTED deselects any settings currently on so that the list will appear with no items selected. It takes a flag indicating whether the list should be in its indeterminate state. This message will also clear the object's modified flag. If you wish to set this flag, send the GenItemGroup a MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE after sending this message.

MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION sets a single selection for a GenItemGroup. Any old selections will be deselected automatically, regardless of the behavior type of the list. It takes a flag indicating whether the list should be in its indeterminate state. If the GenItemGroup is scrollable and exclusive, this message will usually ensure that the list will automatically scroll to the selected item. This message also clears the object's modified flag.

MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS sets a a group of selections for a GenItemGroup. Any previously selected items will be deselected. The message must pass the number of selections and a pointer to the list of identifiers to set. This message will clear the GenItemGroup's modified flag.

MSG_GEN_ITEM_GROUP_GET_SELECTION
word	MSG_GEN_ITEM_GROUP_GET_SELECTION();

This message returns the current selection identifier for the GenItemGroup. If there is no selection, this message returns GIGS_NONE. If there are multiple selections, this message only returns the first selection. Use MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS for multiple selections. A selection may be returned even if the item is not usable, not enabled, or not in the GenItemGroup.

If you are using a GenItemGroup which may contain a selection of -1, you will want to call MSG_GEN_ITEM_GROUP_GET_NUM_SELECTIONS to differentiate between the item with -1 being selected and GIGS_NONE.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Return: The current selection stored in the GenItemGroup's GIGI_ selection instance field (or GIGS_NONE if there are no selections).

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS
word	MSG_GEN_ITEM_GROUP_GET_MULTIPLE_SELECTIONS(
        word	*selectionList,
        word	maxSelections);

This message returns the current list of selections for non-exclusive and extended-selection GenItemGroups. The caller must allocate a buffer for the entries and pass the size of that buffer. If there is insufficient space in the passed buffer, no entries will be filled in. You should call MSG_GEN_ITEM_GET_NUM_SELECTIONS beforehand in order to allocate the correct maximum size for the buffer.

This message returns the number of selections filled into the buffer. The buffer is filled in with the proper identifiers. The list of identifiers within the buffer will not be null-terminated.

If you are using an exclusive or exclusive-none list, use MSG_GEN_ITEM_GROUP_GET_SELECTION instead.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: selectionList A pointer to the buffer to store the selection entries.

maxSelections
The maximum number of selections that may be returned by the message.

Return: The total number of selections returned in the buffer.

selectionList
The pointer to the buffer containing the identifiers of the selections.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_GET_NUM_SELECTIONS
word	MSG_GEN_ITEM_GROUP_GET_NUM_SELECTIONS();

This message returns the GenItemGroup's current number of selections (stored in GIGI_ numSelections ), or zero if there is no selection.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Return: The number of selections in the GenItemGroup.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_NONE_SELECTED
void	MSG_GEN_ITEM_GROUP_SET_NONE_SELECTED(
        Boolean		indeterminate);

This message sets a GenItemGroup to show no selections; it should not be sent to an exclusive GenItemGroup. This message will also clear the GenItemGroup's modified flag. If you wish to set the modified flag, send the list a MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE .

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: indeterminate Pass TRUE if the GenItemGroup should be marked indeterminate, FALSE otherwise.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION
void	MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION(
        word		identifier,
        Boolean		indeterminate);

This message sets exactly one selection for a GenItemGroup. Any previously-selected items will be deselected, regardless of the GenItemGroup's behavior type. To set items without disturbing other settings, use MSG_GEN_ITEM_GROUP_SET_ITEM_STATE . If the list is on-screen, this message will cause a visual update. If the GenItemGroup is scrollable, this message will usually ensure that the item selected will appear on-screen.

This message clears the list's modified state. If you wish to set the group modified, send it a MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE . You may set a selection that is not usable, not enabled, or even not in the GenItemGroup. (This is useful for linked GenItemGroups.)

Source: Unrestricted.

Destination: Any GenItemGroup.

Parameters: identifier The identifier (enumerated value) of the item to select.

indeterminate
Pass TRUE if the group should be marked indeterminate.

Return: Nothing.

Interception: Generally not intercepted.

Tips: An item with a matching identifier need not be present to become the GenItemGroup's "selection." Using this knowledge, you can set up several "linked" GenItemGroups, each with only one selection. (See GenItemGroup Links for information and examples.)

MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS
void	MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS(
        word	*selectionList,
        word	numSelections);

This message sets multiple selections for a GenItemGroup. The caller must pass a pointer to a list of identifiers that should be selected, along with the number of selections to set. This message will clear the GenItemGroup's modified flag. If you wish to set the group modified send it a MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE .

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: selectionList A pointer to the buffer containing a list of item identifiers.

numSelections
The number of selections in the passed list.

Return: Nothing.

Interception: Generally not intercepted.

GenItemGroup States

GIGI_stateFlags, ATTR_GEN_ITEM_GROUP_SET_MODIFIED_ON_REDUNDANT_SELECTION, MSG_GEN_ITEM_GROUP_SET_INDETERMINATE_STATE, MSG_GEN_ITEM_GROUP_IS_INDETERMINATE, MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE, MSG_GEN_ITEM_GROUP_IS_MODIFIED

The GIGI_ stateFlags contains flags relating to the state of the entire GenItemGroup. The two flags possible are

  • GIGSF_INDETERMINATE
    This flag marks the GenItemGroup indeterminate. Only the group may be indeterminate; individual GenItems may not be marked indeterminate.
  • GIGSF_MODIFIED
    This flag marks the GenItemGroup modified. Only the group may be marked as modified; individual GenItems may not be marked as modified.

A GenItemGroup is in the indeterminate state if its GIGI_ stateFlags contains the GIGSF_INDETERMINATE flag. GenItems cannot be in an indeterminate state by themselves; either the entire list is indeterminate or it is not. If you want individual items to be indeterminate, you may want to use a GenBooleanGroup object instead.

A GenItemGroup is in the modified state if its GIGI_ stateFlags contains the GIGSF_MODIFIED flag. GenItems may not be in a modified state by themselves; either the entire list is modified or it is not. If you want to mark individual items as modified, you may want to use a GenBooleanGroup object instead.

In immediate mode, any user change to the GenItemGroup's state automatically sets the GIGSF_MODIFIED flag and also forces an "apply." Therefore, in immediate mode, any user changes will, by default, cause the apply action to be sent out immediately. In delayed mode, the GIGSF_MODIFIED flag is set, but an apply action will not automatically occur. The apply action will only occur when the user forces it (such as clicking on an "apply" trigger) or when the application forces it (by sending out a MSG_GEN_APPLY ).

You can force an apply by sending the GenItemGroup MSG_GEN_APPLY . You can also override the default behavior, forcing the object to send out its notification message on all applies, by including ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED in the object's instance data. Any time the object receives an "apply," the GenItemGroup will send out the notification message even if the object does not contain any modified GenItem children.

In general, if the user makes redundant changes (such as repeatedly selecting the same item) the group will not be marked modified--the group has not changed state since the last apply, so there is no need to send out a notification message. If, however, you wish for the group to be set modified on redundant changes, add the vardata attribute ATTR_GEN_ITEM_GROUP_SET_MODIFIED_ON_REDUNDANT_SELECTION to the object's instance data. Whenever the user makes any selection, the group will be marked modified.

To check whether a GenItemGroup is indeterminate, send it a MSG_GEN_ITEM_GROUP_IS_INDETERMINATE . The message will return true if the list is indeterminate, false if it is not.

To set a GenItemGroup indeterminate, send it MSG_GEN_ITEM_GROUP_SET_INDETERMINATE_STATE . Note that the messages to set a GenItemGroup's selections already provide an argument to set the GenItemGroup indeterminate.

To check whether a GenItemGroup has been modified, send it MSG_GEN_ITEM_GROUP_IS_MODIFIED . This will return true if the list has been modified, false if it has not. To set a GenItemGroup modified, send it a MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE .

MSG_GEN_ITEM_GROUP_SET_INDETERMINATE_STATE
void	MSG_GEN_ITEM_GROUP_SET_INDETERMINATE_STATE(
        Boolean		indeterminateState);

This message sets the GenItemGroup's GIGSF_INDETERMINATE flag in its GIGI_ stateFlags instance field. Usually this message is used in conjunction with MSG_GEN_ITEM_GROUP_SET_MULTIPLE_SELECTIONS . ( MSG_GEN_ITEM_GROUP_SET_NONE_SELECTED and MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION set the indeterminate state themselves.)

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: indeterminate Pass TRUE to mark the group indeterminate, FALSE to set the group not indeterminate.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_IS_INDETERMINATE
Boolean	MSG_GEN_ITEM_GROUP_IS_INDETERMINATE();

This message checks whether a GenItemGroup is in the indeterminate state; it checks the GIGSF_INDETERMINATE flag in the GenItemGroup's GIGI_ stateFlags .

Source: Unrestricted.

Destination: Any GenItemGroup object.

Return: TRUE if the GenItemGroup is indeterminate, FALSE otherwise.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE
void	MSG_GEN_ITEM_GROUP_SET_MODIFIED_STATE(
        Boolean		modifiedState);

This message marks a GenItemGroup modified by setting the GIGSF_MODIFIED flag in its GIGI_ stateFlags instance field.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: modifiedState TRUE to mark the list modified, FALSE otherwise.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_IS_MODIFIED
Boolean	MSG_GEN_ITEM_GROUP_IS_MODIFIED();

This message checks whether a GenItemGroup has been modified by checking the GIGSF_MODIFIED flag in the GenItemGroup's GIGI_ stateFlags .

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: None.

Return: TRUE if GenItemGroup is marked modified, FALSE otherwise.

Interception: Generally not intercepted.

Sending the Event

GIGI_applyMsg, GIGI_destination, GEN_ITEM_GROUP_APPLY_MSG, ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED, MSG_GEN_ITEM_GROUP_GET_DESTINATION, MSG_GEN_ITEM_GROUP_SET_DESTINATION, MSG_GEN_ITEM_GROUP_GET_APPLY_MSG, MSG_GEN_ITEM_GROUP_SET_APPLY_MSG

When the GenItemGroup receives notice to apply any changes to its state, it will first check its GIGSF_MODIFIED flag. If the list has been modified, the GenItemGroup will send the apply action stored in its GIGI_ applyMsg field to the destination stored in its GIGI_ destination field.

GIGI_ applyMsg stores the apply action you wish the GenItemGroup to send out whenever you make a selection choice within the group. This message should be based on the prototype GEN_ITEM_GROUP_APPLY_MSG so that the proper arguments ( selection , numSelections , and stateFlags ) are automatically passed. GIGI_ destination should store the object or process you wish to handle the message sent out by this group. This object should have a handler for the GIGI_ applyMsg .

You can force a GenItemGroup to apply its changes with MSG_GEN_APPLY . You can also override the default behavior, forcing the object to send out its apply action on all applies, whether modified or not, by including ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED in the object's instance data. Any time the object needs to apply its changes, the GenItemGroup will send out the notification message even if the object is not marked GIGSF_MODIFIED.

To retrieve the object or process stored in the GenItemGroup's GIGI_ destination instance field or the message stored in its GIGI_ applyMsg instance field, send the list MSG_GEN_ITEM_GROUP_GET_DESTINATION or MSG_GEN_ITEM_GROUP_GET_APPLY_MSG .

To set the GenItemGroup's destination, send the list MSG_GEN_ITEM_GROUP_SET_DESTINATION , passing the optr of the new destination. To set the GenItemGroup's notification message, send the list MSG_GEN_ITEM_GROUP_SET_APPLY_MSG , passing it the message to use.

GEN_ITEM_GROUP_APPLY_MSG
void	GEN_ITEM_GROUP_APPLY_MSG(
        word	selection,
        word	numSelections
        byte	stateFlags);

Use this prototype to define the apply message for your GenItemGroup (stored in the GIGI_ applyMsg instance field). This prototype ensures that the message passes the correct parameters to your message handler.

Source: Your GenItemGroup object.

Destination: Destination of your GenItemGroup.

Parameters: selection The current selection of the GenItemGroup.

numSelections
The current number of selections of the GenItemGroup.
stateFlags
The current state flags of the GenItemGroup.

Return: Nothing.

MSG_GEN_ITEM_GROUP_GET_DESTINATION
optr	MSG_GEN_ITEM_GROUP_GET_DESTINATION();

This message returns the optr of the current destination object as specified in the GIGI_ destination instance field.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: Nothing.

Return: The optr of the current destination object.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_DESTINATION
void	MSG_GEN_ITEM_GROUP_SET_DESTINATION(
        optr	dest);

This message sets a new destination object for the list, stored in the list's GIGI_ destination field. The apply and status messages of this GenItemGroup will be sent to this new destination.

Source: Unrestricted.

Destination: Any GenItemGroup.

Parameters: dest The optr of the new destination object to be put in GIGI_ destination .

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_GET_APPLY_MSG
Message	MSG_GEN_ITEM_GROUP_GET_APPLY_MSG();

This message returns the current apply message in the GenItemGroup's GIGI_ applyMsg instance field.

Source: Unrestricted.

Destination: Any GenItemGroup.

Parameters: Nothing.

Return: The current apply message.

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_APPLY_MSG
void	MSG_GEN_ITEM_GROUP_SET_APPLY_MSG(
        Message		message);

This message sets a new apply message in the GenItemGroup's GIGI_ applyMsg instance field.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: message The new notification message.

Return: Nothing.

Interception: Generally not intercepted.

Sending Status Messages

GEN_ITEM_GROUP_STATUS_MSG, ATTR_GEN_ITEM_GROUP_STATUS_MSG, MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG

If you wish your application to receive notice whenever the list's state changes, even if those changes will not be immediately applied, you may set a status message in the object's instance data.

For example, a color selection dialog box provides two means to set the RGB value of a color: one in a GenItemGroup listing the total possible states and another in a GenBooleanGroup with separate toggle states for Red, Green, and Blue. In delayed mode, setting the values in one group should change the display of the other group simultaneously but should not result in an apply action until the user activates the apply trigger.

One list can notify its partner of state changes with a status message. The other list can respond to state changes (and therefore change its corresponding state) without actually applying those changes.

To set up a status message, add a message based on the prototype GEN_ITEM_GROUP_STATUS_MSG. This message will be sent to the list's destination object. Set ATTR_GEN_ITEM_GROUP_STATUS_MSG to the status message.

Code Display 11-5 Status Message in GenItemGroup

/* Use the prototype GEN_ITEM_GROUP_STATUS_MSG to set up a status message. The
 * prototype takes three arguments: selection (the contents of GIGI_selection),
 * numSelections (the contents of GIGI_numSelections), and stateFlags (the contents
 * of GIGI_stateFlags). */
    /* In the destination's class definition: */
@message (GEN_ITEM_GROUP_STATUS_MSG) MSG_COLOR_STATUS_CHANGE;
    /* In the GenItemGroup's object declaration, set
     * ATTR_GEN_ITEM_GROUP_STATUS_MSG to the message you declared. */
@object GenItemGroupClass MyColorGroup = {
    GIGI_applyMsg = MSG_COLOR_CHANGE;
    GIGI_destination = process;
    ATTR_GEN_ITEM_GROUP_STATUS_MSG = MSG_COLOR_STATUS_CHANGE;
}
    /* Finally, in your handler for this status message, do required work. */
@method MyProcessClass, MSG_COLOR_STATUS_CHANGE {
    switch (selection) {
    case C_BLACK:
        /* ...(code)... */
    case C_WHITE:
        /* ...(code)... */
    }
}
GEN_ITEM_GROUP_STATUS_MSG
void	GEN_ITEM_GROUP_STATUS_MSG(
        word	selection,
        word	numSelections,
        byte	stateFlags);

Use this prototype to define a status message for your GenItemGroup (stored in the ATTR_GEN_ITEM_GROUP_STATUS_MSG vardata). This prototype ensures that your status message passes the correct parameters.

Source: Your GenItemGroup object.

Destination: The destination of your GenItemGroup.

Parameters: selection The current selection (enumerated type stored in GIGI_ selection ) of the GenItemGroup.

numSelections
The current number of selections (stored in GIGI_ numSelections ) of the GenItemGroup.
stateFlags
The current state flags (stored in GIGI_ stateFlags ) of the GenItemGroup.

Return: Nothing.

Tips: It is usually convenient to set up a switch statement based on the enumerated value of the selection passed in this message handler.

MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG
void	MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG(
        Boolean modifiedState);

This message causes the GenItemGroup to send out the status message to the destination object. This status message is stored in ATTR_GEN_ITEM_GROUP_STATUS_MSG .

Source: Unrestricted.

Destination: Any GenItemGroup object that contains a status message.

Parameters: modifiedState Pass TRUE if the GenItemGroup should be marked modified.

Return: Nothing.

Interception: Generally not intercepted.

List Focus

MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM, MSG_GEN_ITEM_GROUP_SET_FOCUS_ITEM

A GenItem in a GenItemGroup which has the cursor over it is said to have the focus. Your application may get or set the current focus of any exclusive GenItemGroup.

MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM
word  MSG_GEN_ITEM_GROUP_GET_FOCUS_ITEM();

Gets the focus item of a GenItemGroup.

Source: Anywhere.

Destination: Any GenItemGroup.

Return: The identifier of the item which currently has the focus. If the GenItemGroup is not an exclusive list or if no item has focus, this message will return GIGS_NONE.

Tips: This message differs from MSG_META_GET_FOCUS_EXCL because it works on lists that don't have the focus and it does not suffer from possible synchronization problems.

MSG_GEN_ITEM_GROUP_SET_FOCUS_ITEM
 void   MSG_GEN_ITEM_GROUP_SET_FOCUS_ITEM(word identifier);

This message scrolls the GenItemGroup list so that the specified item will be the visible focus. While this message can be sent to a GenItemGroup at any time, it will only affect item groups which have already been visually built.

Source: Anywhere.

Destination: Any GenItemGroup.

Parameters:

identifier
The ID of the item which will be the focus of the item group. identifier may represent an item that is not usable, not enabled, not in the item group, or it may even be set to GIGS_NONE and represent no item. If identifier cannot be made the focus of the list, then no item in that list will have the exclusive focus.

Return: Nothing.

The List Objects: 3.4 GenItemGroups: Working with Items

With several GenItem and GenItemGroup messages, you can work with individual items. The following sections detail these messages.

Altering the Identifiers

MSG_GEN_ITEM_GET_IDENTIFIER, MSG_GEN_ITEM_SET_IDENTIFIER, MSG_GEN_ITEM_GROUP_GET_UNIQUE_IDENTIFIER

You may change the identifiers of GenItems dynamically. To retrieve a GenItem's identifier, send the GenItem MSG_GEN_ITEM_GET_IDENTIFIER . To set a GenItem's identifier, send it MSG_GEN_ITEM_SET_IDENTIFIER , passing the message the value you wish to set it to. Make sure that this value is unique and not duplicated by any other GenItem in the same GenItemGroup.

To generate a unique identifier for an item in a group, the GenItemGroup may use MSG_GEN_ITEM_GROUP_GET_UNIQUE_IDENTIFIER .

MSG_GEN_ITEM_GET_IDENTIFIER
word	MSG_GEN_ITEM_GET_IDENTIFIER();

This message returns the identifier stored in the item's GII_ identifier instance field.

Source: Unrestricted.

Destination: Any GenItem object.

Return: The identifier of the GenItem object.

Interception: Generally not intercepted.

MSG_GEN_ITEM_SET_IDENTIFIER
void	MSG_GEN_ITEM_SET_IDENTIFIER(
        word	identifier);

This message sets a GenItem's GII_ identifier instance field to a new value. There is no effect on the list itself. The item whose identifier is changed must not be currently selected. It is therefore up to the application to make sure that the item is de-selected before changing its identifier.

Source: Unrestricted.

Destination: Any GenItem object.

Return: Nothing.

Interception: Generally not intercepted.

Warnings: The new enumerated value should be unique among all of the items in the list. Use MSG_GEN_ITEM_GROUP_GET_UNIQUE_IDENTIFIER .

MSG_GEN_ITEM_GROUP_GET_UNIQUE_IDENTIFIER
word	MSG_GEN_ITEM_GROUP_GET_UNIQUE_IDENTIFIER();

This message returns an identifier that is unique among the items in the GenItemGroup's group. It does not check across links, however.

Source: Unrestricted.

Destination: Any non-usable GenItemGroup object.

Parameters: None.

Return: The unique item identifier.

Interception: Generally not intercepted.

Altering the Item's State

MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR, MSG_GEN_ITEM_GROUP_SET_ITEM_STATE, MSG_GEN_ITEM_GROUP_IS_ITEM_SELECTED

You can retrieve the optr of a selection by sending the GenItemGroup a MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR , passing the identifier of the requested item. You may then use this item optr to send messages directly to the individual items or to set items enabled/disabled, usable/not usable, etc. If the item is not found, this message will return null.

You may also set an item selected without altering any other settings in the GenItemGroup. Send the list a MSG_GEN_ITEM_GROUP_SET_ITEM_STATE , passing it the identifier of the item to mark as selected. If the list is exclusive or exclusive-none, any other settings will be deselected; if the list is extended-selection or non-exclusive, the other settings will remain intact.

To check on whether an item is selected, send the GenItemGroup a MSG_GEN_ITEM_GROUP_IS_ITEM_SELECTED , passing it the identifier of the item in question. This message will return true if the item is currently selected, false if it is not.

Send MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBILE to force a scrolling GenItemGroup to scroll to an item. Pass this message the identifier of the requested item. This message has no effect on non-scrolling lists.

MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR
optr	MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR(
        word	identifier);

This message returns the optr of a GenItem within a GenItemGroup. You may then use this optr for directly manipulating the GenItem. If an item with the requested identifier is not found as one of the GenItemGroup's children, this message will return a null optr.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: identifier The identifier of the item to search for (stored in the GenItem's GII_ identifier instance field).

Return: The optr of the GenItem with the requested identifier (or a null optr if none is found).

Interception: Generally not intercepted.

MSG_GEN_ITEM_GROUP_SET_ITEM_STATE
void	MSG_GEN_ITEM_GROUP_SET_ITEM_STATE(
        word		identifier,
        Boolean		state);

This message sets the state (selected or unselected) of a particular GenItem within a GenItemGroup. Other items will be unaffected by the new state. This message also clears the GenItemGroup's modified state. If an identifier is passed which does not match an item within the GenItemGroup (or matches a not usable item), it will still be made the selection.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: identifier The identifier of the item to change state.

state
TRUE to mark item selected, FALSE to mark item unselected.

Return: Nothing.

Interception: Generally not intercepted.

Tips: An item with a matching identifier need not be present to become the GenItemGroup's selection. Using this knowledge, you can set up several "linked" GenItemGroups, each with only one selection. (See GenItemGroup Links for information and examples.)

MSG_GEN_ITEM_GROUP_IS_ITEM_SELECTED
Boolean	MSG_GEN_ITEM_GROUP_IS_ITEM_SELECTED(
        word	identifier);

This message checks whether an item within a GenItemGroup is selected, even if that item is not usable, not enabled, or not within the GenItemGroup. The message returns true if the item is selected, false if it is not selected.

Source: Unrestricted.

Destination: Any GenItemGroup object.

Parameters: identifier The identifier of the item to check for.

Return: Will return true if the item is selected, false if it is not.

Interception: Generally not intercepted.

The List Objects: 3.5 GenItemGroups: Scrolling GenItemGroups

HINT_ITEM_GROUP_SCROLLABLE, MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE

A scrolling list is a special version of a GenItemGroup that allows the user to scroll through a list of items which are not all visible at one time. Scrolling lists usually provide scrollbars or some other means to quickly scan through a list. You may make a GenItemGroup scrollable if the specific UI allows by including the hint HINT_ITEM_GROUP_SCROLLABLE in the list's object declaration.

The list will come up in a standard size determined by the specific UI. You may override this by adding a size hint to the object ( HINT_FIXED_SIZE , HINT_INITIAL_SIZE , etc.). Notification of changes for a scrolling list, as well as setting and getting the selections, works the same for a scrolling list as for a normal GenItemGroup. If an item is selected, the scrolling list will almost always scroll automatically to the selected item.

To force a GenItemGroup to scroll to an item, send MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE . Pass this message the identifier of the item to be made visible in the scrolling list. This message has no effect on a non-scrolling list.

You may wish in some cases for a scrolling list to be for display only. If so, set the scrollable GenItemGroup's GI_ attrs instance data to include the flag GA_READ_ONLY. The user will be unable to interact with a display-only scrolling list (although, of course, the user will still be able to scroll through the list).

If your scrolling list will have many settings or will often be dynamically updated, you should consider using a GenDynamicList instead.

MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE
void	MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE(
        word	identifier);

This message ensures that an item within a scrolling GenItemGroup is visible, by scrolling the list if necessary. This message has no effect if the GenItemGroup is not scrollable or is not usable.

Source: Unrestricted.

Destination: Any scrollable, usable GenItemGroup object.

Parameters: identifier The identifier of the item to be made visible.

Return: Nothing.

Interception: Generally not intercepted.

The List Objects: 3.6 GenItemGroups: GenItemGroup Links

ATTR_GEN_ITEM_GROUP_LINK

In some cases, geometry constraints interfere with functionality. For example, you may wish for "nested" GenItemGroup behavior. Each main selection should act as an exclusive selection within the GenItemGroup, but it should also allow other generic objects "in-between" these exclusive items. Normal geometry constraints prevent this, as GenItemGroups may only contain GenItems as children.

To enable this, you may set up each main section as a separate GenItemGroup with a single selection. Within each GenItemGroup, include a link to the next GenItemGroup with ATTR_GEN_ITEM_GROUP_LINK . Links should be circular; i.e., if three GenItemGroups should be linked, the first group should point to the second, the second to the third, and the third back to the first. The three linked GenItemGroups will act as one entity. If each GenItemGroup is exclusive, one group will select its single item child if selected and the other groups will deselect their children.

These links work because you are allowed to "select" items that are not actually within a GenItemGroup. If an item not within the group is selected, any current selection will therefore be de-selected.

The List Objects: 3.7 GenItemGroups: Limitations of the GenItemGroup

GenItemGroups may only have GenItems as children. Likewise, GenItems must always be generic children of a GenItemGroup object. There also is no capability to allow some items under a GenItemGroup object to be exclusive while others are non-exclusive. This can often be accomplished instead by making the list non-exclusive and clearing other items when necessary to create the exclusive behavior.

In some cases, you may wish to store modified or indeterminate states for individual items rather than for only the whole group. In this case, you should probably use a GenBooleanGroup.

For long lists of items or lists that may require frequent updating, you might wish to use a GenDynamicList instead.

The List Objects: 4 GenDynamicListClass

The GenDynamicListClass is a subclass of GenItemGroupClass that allows an application to pass monikers to the object as needed rather than specify a GenItem for each item in the list. In almost all cases, you will want a GenDynamicList to be a scrolling list so that not all of the list entries will be displayed at any given time. (You can, however, still choose to create a non-scrolling dynamic list.)

The GenDynamicList displays a list of monikers; this list of monikers is usually in the form of a data chunk managed by some other (application-supplied) object. For each moniker the GenDynamicList wishes to display, it will send out a query message and the position of the moniker requested. The object handling the query message will return a pointer to the moniker and will draw the moniker within the GenDynamicList. In this way, monikers for a GenDynamicList are only drawn as needed.

The dynamic list allows the following:

  • Long Lists
    Very long lists can be stored by the application in any way it wants and as efficiently as possible. Because these lists are often scrolling lists, the UI is not cluttered up with a multitude of list entries. Only those within the scroller's bounds will be visually displayed.
  • Dynamic Display of Data
    Applications can easily create a scrolling list from arbitrarily organized data. As long as you provide a means to convert the data into the proper moniker format, you can use any data you wish for moniker entries. You can also dynamically access this data and update the list entries.

An application specifies the number of items in the list and if desired can set up a custom size for the dynamic list. Whenever the object needs to display a moniker, it will query its destination for the visual moniker to display as an item in the list. At any time, the application can remove or add items from the dynamic list or change the total number of items and start with all new items. Because the list only queries for items as they become visible, it is more efficient than having to display a large number of entries in a long list.

Since GenDynamicListClass is a subclass of GenItemGroupClass , all of the same messages that are passed to GenItemGroups can be passed to GenDynamicList (except MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR , which has no meaning for a GenDynamicList). The lists can be exclusive, exclusive-none, extended-selection, or non-exclusive. The user can select and deselect items depending on the behavior type, etc.

DynamicList Instance Data

DynamicList Basics

Altering Instance Data

The List Objects: 4.1 GenDynamicListClass: DynamicList Instance Data

GenDynamicListClass inherits all of the instance fields of GenItemGroupClass . The GenDynamicList object provides two additional instance fields:

@instance word GDLI_numItems = 0;
@instance word GDLI_queryMsg = 0;

GDLI_ numItems stores the number of items in the dynamic list. This field should be set to the initial number of monikers needed within your GenDynamicList. If this value is initially unknown (i.e. is retrieved dynamically) then you should set this value after the number of items becomes known (using MSG_GEN_DYNAMIC_LIST_INITIALIZE ). If this instance field is zero, no items will be drawn.

GDLI_ queryMsg stores the querying message that the dynamic list sends each time it needs to display an entry's moniker. (For example, when initially realized, a scrolling dynamic list may display only four out of 16 entries; this message will be sent out four times to retrieve the monikers for the items initially displayable.)

The query message will be handled by whatever object you set in GIGI_destination . The handler should search a list of data for the visual monikers to display within the list. You will want to set up a custom handler for this message and the list of data to search through. Note that this list of data will serve as the GenDynamicList's "children"; there are no GenItems allowed as children of a GenDynamicList. The query message is based on the prototype GEN_DYNAMIC_LIST_QUERY_MSG .

GEN_DYNAMIC_LIST_QUERY_MSG
void	GEN_DYNAMIC_LIST_QUERY_MSG(
        optr	list,
        word	item);

Use this prototype to define the query message for your GenDynamicList (stored in GDLI _queryMsg ). This prototype ensures that the message passes the correct parameters to your message handler.

Source: Your GenDynamicList object.

Destination: The destination of your GenDynamicList object ( GIGI_destination ).

Parameters: list The optr of the list.

item
The item (position number) needing a moniker.

Return: Nothing.

The List Objects: 4.2 GenDynamicListClass: DynamicList Basics

MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER, MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR, MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT

You will need to perform several preparatory steps in order to use a GenDynamicList object, even in its most basic form. The following steps are required for all dynamic lists.

  • Create a GenDynamicList object. You must include the number of items to appear in your list (or set them dynamically when this number is known) and a query message to handle the extraction and display of monikers from a list of data.
  • Create a list of data to use as entries in the list. For simplicity, this can be a chunk of data containing a list of visual monikers, although the data may come from any source--as long as you provide the means to convert the data into monikers.
  • Create a query message handler to search through the list of data. This handler should convert any data into a proper format (if necessary) and send the moniker to the dynamic list to display the item. You can convert the data into a proper format by using one of several messages provided with GenDynamicListClass . These messages may take text strings, visual monikers, or other forms of data to set the monikers for the dynamic list.

Code Display 11-6 Creating a GenDynamicList object

/* In the definition of your destination object's class, define a message based
 * on the prototype GEN_DYNAMIC_LIST_QUERY. This prototype passes two arguments:
 * list (the optr of the list sending info) and item (the selection enum). */
@message (GEN_DYNAMIC_LIST_QUERY_MSG) MSG_QUERY_COLOR_MONIKER;
/* Set up the selections, just as you would in a normal GenItemGroup. */
typedef enum {
    MC_RED, MC_YELLOW, MC_BLUE, MC_GREEN
} MyColors;
#define NUM_COLORS MC_GREEN+1
/* In the object declaration, include normal instance data for a GenItemGroup,
 * making sure to also fill in GDLI_numItems and GDLI_queryMsg with the proper
 * information. Note that there are no generic children provided for this object.
 * The list will retrieve and display monikers automatically through the query
 * message as they are needed. */
@object GenDynamicListClass MyList = {
    /* Set the initial selection MC_BLUE (and the number of items selected to 1).*/
    GIGI_selection = MC_BLUE;
    GIGI_applyMsg = MSG_NOTIFY_COLOR_CHANGE;
    GIGI_destination = process;
    /* Set numItems to the total number of items to appear in this list. If you
     * will not know this initially, make sure to set this instance data when you
     * do know (with MSG_GEN_DYNAMIC_LIST_INITIALIZE). */
    GDLI_numItems = NUM_COLORS;
    GDLI_queryMsg = MSG_QUERY_COLOR_MONIKER;
    HINT_ITEM_GROUP_SCROLLABLE;
}

When a dynamic list is first built, the specific UI will decide how many items within the scrolling list should be initially displayed. For each of these items, the dynamic list will send out a query message, passing the number (position) of the item to search for. The querying message will search through a list of data (either custom or pre-existing), extracting and converting the data into text, graphics strings, or visual monikers, etc.

Finally, your query message handler should set each moniker in the list using one of the MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER messages. There are three messages to set the moniker of a dynamic list entry:

  • MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER
     This is a general purpose moniker replacement message. It can take a variety of source types (optrs, fptrs, hptrs), data types (visMonikers, text strings, graphics strings, or even geode tokens), and (in the case of graphics strings) the height and width of the moniker. This message also provides a flag allowing a moniker entry to appear visually disabled (grayed out in OSF/Motif).
  • MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR
     This is a simplified version of the above message which takes a visual moniker and displays it within the dynamic list. The message's only arguments are the item in the list to update and the optr of the moniker for that item.
  • MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT
     This is another simplified version of the first message which takes a text string and displays it within the dynamic list. The message's only arguments are the item in the list to update and a pointer to the text string to use.

The simpler messages are useful if your data is a list of complete visual moniker structures or is a list of text strings. If you need to retrieve special data (such as GStrings or geode tokens), or if you need to perform special operations on the moniker (such as bringing it up disabled), then you must use MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER .

The data itself may be placed in several areas of your code. If the data is simple text strings, set up within an array, it may appear within the dynamic list's object block, its own resource block, or within the handler for the query message. If the data is visual monikers, the data must appear within a resource block.

If the data is within a different block from the dynamic list, that block must be locked before its data can be accessed. Make sure to unlock the block after using the data.

Code Display 11-7 Creating the List of Data

/* Create the list of data (in this case within its own data block). This list of
 * data will be in form of text strings. */
@start ItemText, data;
@chunk char *listMonikers[NUM_COLORS] = {
    "Red", "Yellow", "Blue", "Green"
};
@end ItemText
/* Create the query message handler. Each time the GenDynamicList needs to display
 * an item, it will call this querying message with the number (position) of the
 * item it needs to display. In this case, since the monikers are already in the
 * form of text strings, you can use MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT
 * to set the moniker of the list item using the text string.
 * DEFINITION: void MSG_QUERY_COLOR_MONIKER(optr list, word item) */
@method MyProcessClass, MSG_QUERY_COLOR_MONIKER {
char *listString;
    /* The method will retrieve the entry's text moniker from the array of
     * text monikers in listMonikers. It locks itemText, extracts the
     * text string, and uses the REPLACE_ITEM message to set the dynamic list
     * entry. */
    MemLock(HandleOf(@listMonikers));
    listString = listMonikers[item];
    /* Then send off the item string to the dynamic list
     * and unlock the data block. */
    @call listObject::MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT(item, listString);
    MemUnlock(HandleOf(@listMonikers));
}
MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER
void	MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER(@stack
        word			item,
        word			flags,
        word			height,
        word			width,
        word			length,
        VisMonikerDataType			dataType,
        VisMonikerSourceType			sourceType,
        dword			source);

This message sends a moniker to use for a particular list item and is usually sent in response to a moniker query by a dynamic list object. Within your query message handler (GDLI_ queryMsg ), you should access the data for the moniker to use and send off this message with the proper parameters. You may also use this message to change an item's moniker, guaranteeing that it is updated if currently visible.

If you pass this message the flag RIMF_NOT_ENABLED, the moniker for the item will be disabled (usually grayed out).

On the Nokia 9000i Communicator devices, you can use the routine CreateVisMonikerLine() to to arrange the line's contents so that they will form columns when displayed with similarly arranged lines.

This is an all-purpose message that allows many different source types and many ways to reference the data in these sources. If you are using lists of visual monikers or simple text strings and you do not to perform any fancy operations on this data, you may use MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR or MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT .

Source: The object handling a dynamic list's query message.

Destination: The GenDynamicList specified in the query message.

Parameters: item The position of the item needing a moniker.

flags
RIMF_NOT_ENABLED if the item's moniker should be shown disabled.
height
If the item is a GString, the height in points.
width
If the item is a GString, the width in points.
length
Size of moniker data, in bytes. This value is ignored for VMST_OPTR. If the dataType is VMDT_TEXT and length is zero, the text moniker is assumed to be null-terminated. If the dataType is VMDT_GSTRING and the length is zero, the GString is assumed to end with a GR_END_GSTRING element.
dataType
The VisMonikerDataType of the actual moniker.
sourceType
The type of pointer referencing the moniker.
source
A pointer to the moniker data.

Structures: The VisMonikerDataType and VisMonikerSourcetype are defined by VisClass . Their values are shown here for easy reference.

  typedef ByteEnum VisMonikerDataType;
/*	VMDT_NULL
 *	VMDT_VIS_MONIKER
 *	VMDT_TEXT
 *	VMDT_GSTRING
 *	VMDT_TOKEN */
  typedef ByteEnum VisMonikerSourceType;
/*	VMST_FPTR
 *	VMST_OPTR
 *	VMST_HPTR */

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR
void	MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER_OPTR(
        word	item,
        optr	moniker);

This message is a simplified version of MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER . The moniker must be of type VisMoniker and referenced by an optr. No special operations (such as marking the item disabled) are possible with this message. In those cases, use MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER .

Source: The object handling a dynamic list's query message.

Destination: The GenDynamicList object specified by the query message.

Parameters: item The position of the item to display.

optr
The optr of the VisMoniker to use.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT
void	MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_TEXT(
        word	item,
        char	*text);

This message is a simplified version of MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER . The moniker passed must be a null-terminated text string. No special operations (such as marking the item disabled) are possible with this message. In those cases, use MSG_GEN_DYNAMIC_LIST_REPLACE_ITEM_MONIKER .

Source: The object handling a dynamic list's query message.

Destination: The GenDynamicList object specified by the query message.

Parameters: item The position of the item to display.

text
A pointer to the null-terminated text string.

Return: Nothing.

Interception: Generally not intercepted.

The List Objects: 4.3 GenDynamicListClass: Altering Instance Data

You may alter the instance data of a GenDynamicList dynamically. Any of the appropriate GenItemGroup messages are valid for a GenDynamicList object except for MSG_GEN_ITEM_GROUP_GET_ITEM_OPTR .

Altering the Number of Items

MSG_GEN_DYNAMIC_LIST_INITIALIZE, MSG_GEN_DYNAMIC_LIST_GET_NUM_ITEMS

In order to ensure the proper display of monikers in a dynamic list, the list object must know the total number of items that may be displayed. In most cases, you will set this number in the initial instance data for the object. In some cases, however, you will not initially know how many entries may be displayed; the total number of items will only be known after the data block is accessed.

In these cases, you should use MSG_GEN_DYNAMIC_LIST_INITIALIZE , MSG_GEN_DYNAMIC_LIST_ADD_ITEMS or MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS to dynamically set the GDLI_ numItems instance field. You can retrieve the current number of items with MSG_GEN_DYNAMIC_LIST_GET_NUM_ITEMS .

MSG_GEN_DYNAMIC_LIST_INITIALIZE
void	MSG_GEN_DYNAMIC_LIST_INITIALIZE(
        word	numItems);

This message initializes a dynamic list, updating the display to reflect a new number of items or new data within the moniker list. If the list is already usable, this message invalidates all the current items and requests new monikers for all displayable items. The dynamic list will clear all previous selections; it will not scroll to any new selections. Use MSG_GEN_ITEM_GROUP_MAKE_ITEM_VISIBLE to scroll to a selected item.

This message is useful for cases when initially building a dynamic list without knowing the number of items; after you know the number of items, make sure to send the dynamic list this message.

Source: Unrestricted.

Destination: Any GenDynamicList object.

Parameters: numItems The new number of items for the dynamic list or GDLI_NO_CHANGE to keep the current number of selections.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_DYNAMIC_LIST_GET_NUM_ITEMS
word	MSG_GEN_DYNAMIC_LIST_GET_NUM_ITEMS();

This message returns the current number of items for a dynamic list. If no items are within the list, this message returns zero.

Source: Unrestricted.

Destination: Any GenDynamicList object.

Parameters: None.

Return: The number of items in the GenDynamicList (GDLI_ numItems ).

Adding and Removing Items Dynamically

MSG_GEN_DYNAMIC_LIST_ADD_ITEMS, MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS, MSG_GEN_DYNAMIC_LIST_REMOVE_ITEM_LIST

Part of the strength of a dynamic list is its ability to be easily updated with new data. MSG_GEN_DYNAMIC_LIST_ADD_ITEM S adds one or more new entries at any position in a dynamic list. The total number of items (GDLI_ numItems ) will be automatically incremented, and all entries after the added item will have their positions increased by one.

The constants GDLP_FIRST and GDLP_LAST indicate either the first or last item, respectively, and are available for use with these messages.

MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS removes one or more entries from any position within a dynamic list. The total number of items (GDLI_ numItems ) will be decremented, and all entries after the deleted item will have their positions decreased by one.

MSG_GEN_DYNAMIC_LIST_ADD_ITEMS
void	MSG_GEN_DYNAMIC_LIST_ADD_ITEMS(
        word	item,
        word	numItems);

This message adds an item after the position passed. All items that previously appeared at or after the position specified will have their positions incremented, and the total number of items will be incremented by numItems .

Usually, you will send this message after a change in the moniker data occurs. The moniker for the new item will be requested when it is made visible.

Source: Unrestricted.

Destination: Any GenDynamicList object.

Parameters: item The position of the first item to be added to the list. You may pass GDLP_FIRST to add the items at the beginning of the list or GDLP_LAST to add them at the end.

numItems
The number of items to add.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS
void	MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS(
        word	item,
        word	numItems);

This message removes the specified item(s) from a dynamic list at the position passed. All items that previously appeared after those deleted will have their positions decremented, and the total number of items will be decremented by the number of items removed.

Usually, you will send this message after a change in the moniker data. If the moniker for the deleted item is visible, a visual update will occur to reflect the list's new item positions. If the deleted item was selected, it will be deselected before being removed.

Source: Unrestricted.

Destination: Any GenDynamicList object.

Parameters: itemPosition The position of first item to be removed. GDLP_LAST or GDLP_FIRST specify the first or last position in the list.

numItems
The number of items to be removed.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_DYNAMIC_LIST_REMOVE_ITEM_LIST
void	MSG_GEN_DYNAMIC_LIST_REMOVE_ITEM_LIST(
        word	*deletionList,
        word	numItems);

This message removes a list of items from a dynamic list. Therefore, unlike MSG_GEN_DYNAMIC_LIST_REMOVE_ITEMS, this message can remove separate items that occur at different positions in the list. The message updates other item positions appropriately, causing a visual refresh, if necessary, and updates the total number of items.

Source: Unrestricted.

Destination: Any GenDynamic List.

Parameters: deletionList Pointer to buffer storing positions (word values) of the items to be removed.

numItems
Number of items being deleted.

Interception: Generally not intercepted.

The List Objects: 5 GenBooleanGroups

GenBooleanGroupClass provides a list of items (GenBoolean objects) that may be individually set regardless of the state of other items in the list. The GenBooleanGroup is similar in behavior to a non-exclusive GenItemGroup in that each item may be selected independently of other selections. Unlike a non-exclusive GenItemGroup, however, GenBooleanGroup also allows your application to keep track of individual states for each item.

The GenBooleanGroup uses a word of data (16 bits) to keep track of the various states of its GenBoolean children. As a result, the maximum number of children allowed for a GenBooleanGroup object is 16. If you need non-exclusive behavior but have more than 16 children, you should either split the children between several GenBooleanGroups or use a non-exclusive GenItemGroup (or GenDynamicList).

GenBooleanClass is the basic item class for the GenBooleanGroupClass . It contains an entry that is set on or off. For example, a menu of font styles might have many entries such as "bold" or "italic," each of which could be on or off, independent of each other. Each of those entries should be represented as a GenBoolean object.

GenBooleanGroup Instance Data

GenBooleanGroup Usage

Altering Instance Data

The List Objects: 5.1 GenBooleanGroups: GenBooleanGroup Instance Data

GenBooleanGroup and GenBoolean objects contain a variety of information. The bulk of the functionality of the list is contained in the GenBooleanGroup; the items themselves only contain identifying information that the list uses.

GenBooleanGroup Instance Data

The GenBooleanGroup contains instance fields relating to the state of its individual children. These instance fields are listed in GenBooleanGroup Instance Fields .

Code Display 11-8 GenBooleanGroup Instance Fields

    @instance word			GBGI_selectedBooleans = 0;
    @instance word			GBGI_indeterminateBooleans = 0;
    @instance word			GBGI_modifiedBooleans = 0;
    @instance optr			GBGI_destination;
    @instance Message			GBGI_applyMsg = 0;
@vardata Message			ATTR_GEN_BOOLEAN_GROUP_STATUS_MSG;
@vardata optr			ATTR_GEN_BOOLEAN_GROUP_LINK;
    @reloc ATTR_GEN_BOOLEAN_GROUP_LINK, 0, optr;
@vardata void			ATTR_GEN_BOOLEAN_GROUP_INIT_FILE_BOOLEAN;
/* Hints */
@vardata void			HINT_BOOLEAN_GROUP_SCROLLABLE;
@vardata void			HINT_BOOLEAN_GROUP_MINIMIZE_SIZE;
@vardata void			HINT_BOOLEAN_GROUP_CHECKBOX_STYLE;
@vardata void			HINT_BOOLEAN_GROUP_TOOLBOX_STYLE;

GBGI_ selectedBooleans is a record representing the selection state of the GenBooleanGroup's children. If a bit is set, the GenBoolean corresponding to that position is selected. Because of this field's size, no more than 16 GenBooleans may belong to a single GenBooleanGroup.

GBGI_ indeterminateBooleans is a record representing the indeterminate state of the GenBooleanGroup's children. A GenBoolean is indeterminate if it cannot be considered either selected or unselected. If a bit is set, the GenBoolean corresponding to that position is indeterminate.

GBGI_ modifiedBooleans is a record representing the modified state of the GenBooleanGroup's children. A GenBoolean is modified if its state (selected, deselected, or indeterminate) has changed since the last apply. If a bit is set, the GenBoolean corresponding to that position has been marked modified.

GBGI_ destination contains the optr of the object or process that handles messages sent out by the GenBooleanGroup. This destination object will receive the apply action stored in GBGI_ applyMsg whenever the user changes the GenBooleanGroup and that state change is later applied.

GBGI_ applyMsg stores the apply action for the GenBooleanGroup to send out whenever a MSG_GEN_APPLY takes place, either through an immediate state change or through a delayed mode activation.

ATTR_GEN_BOOLEAN_GROUP_STATUS_MSG assigns a status message to your GenBooleanGroup. A status message allows objects to receive notice of a change in the BooleanGroup's current state without forcing an apply. This attribute is useful for cases where the state within your BooleanGroup may reflect information within other user interface objects and should therefore be updated whenever its user changes occur, regardless of the need to apply those changes.

ATTR_GEN_BOOLEAN_GROUP_LINK allows two or more GenBooleanGroups to be linked together and act as one BooleanGroup. Each BooleanGroup may contain one or more booleans with constant values unique to all other booleans within the linked BooleanGroups. The links should also be circular, with the last group pointing back to the first. All of these BooleanGroups should have identical states so they will work correctly.

ATTR_GEN_BOOLEAN_GROUP_INIT_FILE_BOOLEAN forces the GenBooleanGroup to use "true" and "false" values in the GEOS.INI file.

HINT_BOOLEAN_GROUP_SCROLLABLE implements the Boolean Group as a scrollable list, if that feature is supported by the specific UI. HINT_BOOLEAN_GROUP_MINIMIZE_SIZE instructs the UI to minimize the size of the Boolean Group. Depending on the specific UI, the Boolean group may be placed within a popup list or made scrollable.

HINT_BOOLEAN_GROUP_CHECKBOX_STYLE indicates that the Boolean Group should display its Booleans with the "checkbox" style, if that style is supported by the Specific UI. HINT_BOOLEAN_GROUP_TOOLBOX_STYLE indicates that the group should use the toolbox style, with a simple box around the Boolean's visual moniker. This style is the default for toolboxes in OSF/Motif, but not for other Booleans.

GenBoolean Instance Data

GenBooleanClass contains one additional instance field:

@instance word			GBI_identifier;

GBI_ identifier sets the identifying keyword (a 16-bit mask representing a specific bit--or bits--within a word-length bitfield) that uniquely identifies this GenBoolean. This identifier is used by the GenBooleanGroup parent to reference the Boolean. No two Booleans may specify common bits set, although a single Boolean's identifier may have several bits set.

The List Objects: 5.2 GenBooleanGroups: GenBooleanGroup Usage

The GenBooleanGroup manages up to 16 GenBoolean objects. You must define a new (or use a pre-existing) constant to represent each GenBoolean within the GenBooleanGroup. Each constant represents a bit within a word-length record.

The GenBooleanGroup uses 16-bit records to designate its list of items; each bit in the record corresponds to an item. The GenBooleanGroup keeps three such records: GBGI_ selectedBooleans , GBGI_ indeterminateBooleans , and GBGI_ modifiedBooleans to represent the selected state, the indeterminate state, and the modified state of each GenBoolean.

Selected Booleans

GBI_identifier, GBGI_selectedBooleans

GBI_ identifier stores the identifying keyword for a GenBoolean item. The BooleanGroup object will always use this identifier when referring to GenBooleans. These identifiers are set up as constants representing bits in a 16-bit record. (See Setting Up a GenBooleanGroup .)

Code Display 11-9 Setting Up a GenBooleanGroup

/* The identifiers for any particular GenBooleanGroup are
 * constant bits within a word-length bitfield. */
typedef WordFlags USCities;
#define USC_CHICAGO 				0x0001		/* The first bit */
#define USC_NEW_YORK				0x0002		/* The second bit */
#define USC_SAN_FRANCISCO				0x0004		/* The third bit */
@object GenBooleanGroupClass CityList = {
    GI_comp = @Chicago, @NewYork, @SanFrancisco;
    /* The first and second bit in the selectedBooleans record are set to
     * indicate that Chicago and New York are initially selected. */
    GBGI_selectedBooleans = USC_CHICAGO | USC_NEW_YORK;
}
@object GenBooleanClass Chicago = {
    GI_visMoniker = "Chicago";
    GBI_identifier = USC_CHICAGO;
}
@object GenBooleanClass NewYork = {
    GI_visMoniker = "New York";
    GBI_identifier = USC_NEW_YORK;
}
@object GenBooleanClass SanFrancisco = {
    GI_visMoniker = "San Francisco";
    GBI_identifier = USC_SAN_FRANCISCO;
}

GBGI_ selectedBooleans stores a 16-bit record corresponding to the selection state of a GenBooleanGroup's children. Selecting or deselecting the Booleans will alter the bit representing that GenBoolean child in this instance field. By setting selections in this field, you can specify initial selections for the GenBooleanGroup to initially appear set.

Indeterminate and Modified Booleans

GBGI_indeterminateBooleans, GBGI_modifiedBooleans

GBGI_ indeterminateBooleans stores a 16-bit record corresponding to the indeterminate state of a GenBooleanGroup's children. Your BooleanGroup may appear initially with GenBooleans in an indeterminate state. As opposed to a GenItemGroup, where either the entire list is indeterminate or not, individual Booleans may be indeterminate on their own accord.

A GenBoolean may be indeterminate while it is either selected or unselected. Making an item indeterminate will not change its selected state, though the item's visual representation might change. (In OSF/Motif, an indeterminate selection appears "off" whether or not it is also selected.)

GBGI_ modifiedBooleans stores a 16-bit record corresponding to the modified state of a GenBooleanGroup's children. Any GenBooleans that have been modified since the occurrence of the last apply action will be marked modified. In general, you should not need to set any GenBooleans initially modified.

Whenever the GenBooleanGroup receives notice to "apply" any changes, it will check whether any Booleans have been modified. If any have, the GenBooleanGroup will send its apply action to its destination.

In immediate mode, any change to the GenBoolean's state automatically sets the corresponding bit in the BooleanGroup's GBGI_ modifiedBooleans instance data and also sends out MSG_GEN_APPLY . Therefore, in immediate mode, any user changes will cause the apply action to be sent out immediately. In delayed mode, the corresponding bits in the GBGI_ modifiedBooleans are set, but MSG_GEN_APPLY will be sent out at a later time. The apply action will only occur when the user forces it (such as clicking on an "apply" trigger) or when the application forces it (with MSG_GEN_APPLY ).

Destination

GBGI_applyMsg, GBGI_destination, GEN_BOOLEAN_GROUP_APPLY_MSG, ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED

When the GenBooleanGroup receives notice to apply any changes made in the list, it will first determine whether the group has been modified (by checking GBGI_ modifiedBooleans ). If any GenBoolean has been modified, the BooleanGroup will send the apply message stored in GBGI_ applyMsg to the destination stored in GBGI_ destination .

GBGI_ applyMsg should contain the message you wish the GenBooleanGroup to send out whenever changes should be applied. This message should be based on the prototype GEN_BOOLEAN_GROUP_APPLY_MSG so the proper arguments are passed to its handler. GBGI_ destination should contain the object or process that will handle the message sent out by this group.

You can force an apply to occur by marking GenBooleans modified with MSG_GEN_BOOLEAN_GROUP_SET_MODIFIED_STATE and sending MSG_GEN_APPLY to the GenBooleanGroup. You can also override the default behavior, forcing the object to send out its apply action on all applies, with ATTR_GEN_SEND_APPLY_MSG_ON_APPLY_EVEN_IF_NOT_MODIFIED in the object's instance data.

GEN_BOOLEAN_GROUP_APPLY_MSG
void	GEN_BOOLEAN_GROUP_APPLY_MSG(
        word	selectedBooleans,
        word	indeterminateBooleans,
        word	modifiedBooleans);

Use this prototype to define the apply message for your GenBooleanGroup (stored in GBGI_ applyMsg ). This prototype ensures that the message passes the correct parameters to your message handler.

Source: Your GenBooleanGroup object.

Destination: The destination of your GenBooleanGroup.

Parameters: selectedBooleans The currently selected GenBooleans (the contents of the GBGI_ selectedBooleans instance field).

indeterminateBooleans
The current indeterminate GenBooleans (the contents of the GBDI_ indeterminateBooleans instance field).
modifiedBooleans
The current modified GenBooleans (the contents of the GBGI_ modifiedBooleans instance field).

Return: Nothing.

Sending the Status Messages

GEN_BOOLEAN_GROUP_STATUS_MSG, ATTR_GEN_BOOLEAN_GROUP_STATUS_MSG, MSG_GEN_BOOLEAN_GROUP_SEND_STATUS_MSG

In delayed mode, GenBooleanGroups will only send out their apply action if an apply occurs. If you wish your application to receive notice whenever any user changes take place in the list's state, even if those changes should not be "applied," you may set up a status message in the object's instance data.

Set a message based on the prototype GEN_BOOLEAN_GROUP_STATUS_MSG. Set ATTR_GEN_BOOLEAN_GROUP_STATUS_MSG to this message in your object declaration, and provide a handler for this message in the class.

GEN_BOOLEAN_GROUP_STATUS_MSG
void	GEN_BOOLEAN_GROUP_STATUS_MSG(
        word	selectedBooleans,
        word	indeterminateBooleans,
        word	changedBooleans);

Use this prototype to define a status message for your GenBooleanGroup (stored in the ATTR_GEN_BOOLEAN_GROUP_STATUS_MSG vardata). This prototype ensures that the message passes the correct parameters to your message handler.

Source: Your GenBooleanGroup object.

Destination: The destination of your GenBooleanGroup object.

Parameters: selectedBooleans The currently selected GenBooleans (the contents of the GBGI_ selectedBooleans instance field).

indeterminateBooleans
The current indeterminate GenBooleans (the contents of the GBDI_ indeterminateBooleans instance field).
changedBooleans
The GenBooleans that caused the status message to be sent out (not necessarily the contents of the GBGI_ modifiedBooleans instance field).

Return: Nothing.

MSG_GEN_BOOLEAN_GROUP_SEND_STATUS_MSG
void	MSG_GEN_BOOLEAN_GROUP_SEND_STATUS_MSG(
        word	changedBooleans);

This message causes the GenBooleanGroup to send out its status message stored in ATTR_GEN_ITEM_GROUP_STATUS_MSG . The message should include the current state of the GenBooleanGroup. (Base the status message on the prototype GEN_BOOLEAN_GROUP_STATUS_MSG to ensure this.) To mark individual GenBooleans as changed since the last apply action, pass this message a record of the changed GenBooleans.

Source: Unrestricted.

Destination: Any GenBooleanGroup object with a status message.

Parameters: changedBooleans A record representing the GenBooleans that have changed to cause this GenBooleanGroup to send out its status message.

Return: Nothing.

Interception: Generally not intercepted.

BooleanGroup Links

ATTR_GEN_BOOLEAN_GROUP_LINK

Just as you may create links between GenItemGroups, you may also create links between BooleanGroups. For more complete information on the purpose and effects of linked groups, see GenItemGroup Links .

To enable linked BooleanGroups, you may set up small sub-sets of GenBooleans as separate GenBooleanGroups. Within each BooleanGroup, include a link to the next BooleanGroup with ATTR_GEN_BOOLEAN_GROUP_LINK . Links should be circular; i.e., if three BooleanGroups should be linked, the first group should point to the second, the second to the third, and the third to the first. The three linked BooleanGroups will act as one entity.

Scrolling Boolean Groups

HINT_BOOLEAN_GROUP_SCROLLABLE, MSG_GEN_BOOLEAN_GROUP_MAKE_BOOLEAN_VISIBLE

Boolean group lists, like item group lists, can be scrollable. To make a Boolean list scrollable, set HINT_BOOLEAN_GROUP_SCROLLABLE . To make a particular item in a scrollable Boolean list visible, use the message MSG_GEN_BOOLEAN_GROUP_MAKE_BOOLEAN_VISIBLE .

MSG_GEN_BOOLEAN_GROUP_MAKE_BOOLEAN_VISIBLE
void	MSG_GEN_BOOLEAN_GROUP_MAKE_BOOLEAN_VISIBLE(
        word	identifier);

This message, when sent to a scrollable BooleanGroup object, will make the specified GenBoolean visible in the list. If the passed identifier is unknown or is not usable, the BooleanGroup will do nothing.

Source: Unrestricted.

Destination: Any scrollable, usable GenBooleanGroup.

Parameters: identifier The item number of the GenBoolean to be made visible.

Return: Nothing.

Interception: Generally not intercepted.

The List Objects: 5.3 GenBooleanGroups: Altering Instance Data

Both the BooleanGroups and the Booleans themselves may be dynamically changed to alter the behavior of the list objects.

Altering the Identifiers

MSG_GEN_BOOLEAN_GET_IDENTIFIER, MSG_GEN_BOOLEAN_SET_IDENTIFIER

Each GenBoolean object contains an instance field to store the identifier of that object. It is this identifier that the parent GenBooleanGroup object uses when referring to individual GenBoolean items. You may change these identifiers dynamically if you wish.

To get a Boolean's identifier, send it MSG_GEN_BOOLEAN_GET_IDENTIFIER . To change a GenBoolean's identifier, send the object a MSG_GEN_BOOLEAN_SET_IDENTIFIER , passing the new identifier. You can send the GenBoolean object this message at any time, though sending it while the item is still GS_USABLE would be unusual.

MSG_GEN_BOOLEAN_GET_IDENTIFIER
word	MSG_GEN_BOOLEAN_GET_IDENTIFIER();

This message returns the identifier for the GenBoolean item.

Source: Unrestricted.

Destination: Any GenBoolean object.

Parameters: None.

Return: Identifier stored in the object's GBI_identifier instance field.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_SET_IDENTIFIER
void	MSG_GEN_BOOLEAN_SET_IDENTIFIER(
        word	identifier);

This message sets a new identifier for a GenBoolean object. No change is made to the GenBooleanGroup object. You must make sure that you are not referencing the GenBoolean with its old identifier before sending this message.

Source: Unrestricted.

Destination: Any GenBoolean object.

Parameters: identifier A word representing the constant of the new identifier. This should be a mask representing a bit within a 16-bit record.

Return: Nothing.

Interception: Generally not intercepted.

Warnings: The GenBooleanGroup must not be referring to the object when the GenBoolean receives this message. You should also not refer to the GenBoolean by its old identifier from then on.

Altering the Group State

You may alter the state of a GenBooleanGroup's children in two ways: First, you can set the entire group's state by setting the 16-bit records in the GenBooleanGroup's instance data. Second, you may set individual bits representing an item's state individually.

Setting the State Collectively
MSG_GEN_BOOLEAN_GROUP_GET_SELECTED_BOOLEANS, MSG_GEN_BOOLEAN_GROUP_SET_GROUP_STATE, MSG_GEN_BOOLEAN_GROUP_GET_INDETERMINATE_BOOLEANS, MSG_GEN_BOOLEAN_GROUP_SET_GROUP_INDETERMINATE_STATE, MSG_GEN_BOOLEAN_GROUP_GET_MODIFIED_BOOLEANS, MSG_GEN_BOOLEAN_GROUP_SET_GROUP_MODIFIED_STATE

To retrieve the contents of the GBGI_ selectedBooleans instance field, send the BooleanGroup a MSG_GEN_BOOLEAN_GROUP_GET_SELECTED_BOOLEANS . This message returns a 16-bit record. You can set the selection state of a BooleanGroup's children by sending the group a MSG_GEN_BOOLEAN_GROUP_SET_GROUP_STATE .

To retrieve the contents of the GBGI_ indeterminateBooleans instance field, use MSG_GEN_BOOLEAN_GROUP_GET_INDETERMINATE_BOOLEANS . This message returns a 16-bit record. You can set the indeterminate state with MSG_GEN_BOOLEAN_GROUP_SET_GROUP_INDETERMINATE_STATE .

To retrieve the contents of the GBGI_ modifiedBooleans instance field, use MSG_GEN_BOOLEAN_GROUP_GET_MODIFIED_BOOLEANS . This message returns a16-bit record. You can set the selection state of a BooleanGroup's children by sending the group a MSG_GEN_BOOLEAN_GROUP_SET_GROUP_MODIFIED_STATE .

MSG_GEN_BOOLEAN_GROUP_GET_SELECTED_BOOLEANS
word	MSG_GEN_BOOLEAN_GROUP_GET_SELECTED_BOOLEANS();

This message returns the current state of all GenBooleans in a GenBooleanGroup (selected or unselected) by returning the contents of the GBGI_ selectedBooleans instance field.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: None.

Return: A record indicating the GenBooleans selected.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_GROUP_STATE
void	MSG_GEN_BOOLEAN_GROUP_SET_GROUP_STATE(
        word	selectedBooleans,
        word	indeterminateBooleans);

This message sets new selections and indeterminate states for a GenBooleanGroup (altering the contents of the GBGI_ selectedBooleans and GBGI_ indeterminateBooleans instance field). You should pass the records indicating the GenBooleans to select and/or mark indeterminate. Any GenBooleans which are not set will be cleared.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: selectedBooleans The GenBooleans which should be selected.

indeterminateBooleans
The GenBooleans which should be marked indeterminate.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_GET_INDETERMINATE_BOOLEANS
word	MSG_GEN_BOOLEAN_GROUP_GET_INDETERMINATE_BOOLEANS();

This message returns the indeterminate items of a GenBooleanGroup by returning the contents of the GBDI_ indeterminateBooleans instance field.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: None.

Return: A record indicating the current indeterminate booleans, represented by the OR-ed sum of their identifiers.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_GET_MODIFIED_BOOLEANS
word	MSG_GEN_BOOLEAN_GROUP_GET_MODIFIED_BOOLEANS();

This message returns selections marked modified within a GenBooleanGroup since the last apply (by returning the contents of the GBGI_ modifiedBooleans instance field).

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: None.

Return: The current modified Booleans.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_GROUP_MODIFIED_STATE
void	MSG_GEN_BOOLEAN_GROUP_SET_GROUP_MODIFIED_STATE(
        word	setBooleans,
        word	clearBooleans);

GenBooleans are normally marked modified any time their state is altered, and they are marked not modified after these changes have been applied. You may also mark GenBooleans modified (or mark them nor modified) with this message. You should pass this message both the GenBooleans to set and the GenBooleans to clear (in the GBGI _modifiedBooleans instance field).

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: setBooleans A record indicating the GenBooleans to be set modified in GBGI_ modifiedBooleans .

clearBooleans
A record (mask) indicating the GenBooleans to be set not modified (cleared) in GBGI_ modifiedBooleans .

Return: Nothing.

Interception: Generally not intercepted.

Setting the Booleans Individually
MSG_GEN_BOOLEAN_GROUP_GET_BOOLEAN_OPTR, MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_SELECTED, MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_STATE, MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_INDETERMINATE, MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_INDETERMINATE_STATE, MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_MODIFIED, MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_MODIFIED_STATE

You can retrieve the optr of a particular GenBoolean object by sending the BooleanGroup MSG_GEN_BOOLEAN_GROUP_GET_ITEM_OPTR , passing it the identifier of the boolean in question. You can then use that optr to directly communicate with the object for purposes such as enabling/disabling, setting not usable, etc.

To check on whether a GenBoolean is currently selected, send the BooleanGroup MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_SELECTED , passing it the identifier to check. To set the selection state of a Boolean, send the BooleanGroup MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_STATE , passing it the identifier and the selection state (on or off).

To check on the indeterminate state of a GenBoolean, send the BooleanGroup MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_INDETERMINATE , passing the identifier to check. To set the indeterminate state of a Boolean, send MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_INDETERMINATE_STATE , passing it the identifier and the indeterminate state to set the Boolean to.

To check on the modified state of a GenBoolean, send the BooleanGroup MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_MODIFIED , passing the identifier to check. To set the modified state of a Boolean, send MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_MODIFIED_STATE , passing it the identifier and the modified state.

MSG_GEN_BOOLEAN_GROUP_GET_BOOLEAN_OPTR
optr	MSG_GEN_BOOLEAN_GROUP_GET_BOOLEAN_OPTR(
        word	identifier);

This message returns the optr of the requested GenBoolean. You may then use this optr to alter that Boolean's state individually. If the GenBoolean with the matching identifier is not found, a null optr will be returned.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: identifier The identifier of the GenBoolean to look for.

Return: The optr of the requested GenBoolean object (or a null optr if none is found).

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_SELECTED
Boolean	MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_SELECTED(
        word	identifier);

This message checks whether the GenBoolean with the passed identifier is selected.

Source: Unrestricted.

Destination: Any GenBooleanGroup object

Parameters: identifier The identifier of the GenBoolean to check.

Return: Will return true if GenBoolean is selected, false if GenBoolean is unselected.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_STATE
void	MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_STATE(
        word		identifier,
        Boolean		state);

This message sets an individual GenBoolean's selection state, leaving other GenBooleans unaffected. You may alter the selection state of individual GenBooleans even if they are not usable, not enabled, or not even within the BooleanGroup. (This is useful for linked BooleanGroups.)

Source: Unrestricted.

Destination: Any GenBooleanGroup.

Parameters: identifier The identifier of the GenBoolean.

state
TRUE to mark selected, FALSE to mark unselected.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_INDETERMINATE
Boolean	MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_INDETERMINATE(
        word	identifier);

This message checks whether the GenBoolean with the passed identifier is indeterminate (by examining the GBDI_ indeterminateBooleans instance field).

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: identifier The identifier of the GenBoolean to check.

Return: Will return true if the GenBoolean is indeterminate, false if is not.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_INDETERMINATE_STATE
void	MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_INDETERMINATE_STATE(
        word		identifier,
        Boolean		indeterminateState);

This message sets the indeterminate state of an individual GenBoolean without affecting any other GenBooleans. You may mark GenBooleans indeterminate even if they are not usable, not enabled, or not even present within the BooleanGroup.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: identifier The identifier of the GenBoolean.

indeterminateState
Pass TRUE to mark the GenBoolean indeterminate, FALSE to mark it not indeterminate.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_MODIFIED
Boolean	MSG_GEN_BOOLEAN_GROUP_IS_BOOLEAN_MODIFIED(
        word	identifier);

This message checks whether the GenBoolean with the passed identifier has been modified (by examining the GBGI_ modifiedBooleans instance field).

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: identifier The identifier of the GenBoolean to check.

Return: Will return true if boolean has been modified, false if it has not.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_MODIFIED_STATE
void	MSG_GEN_BOOLEAN_GROUP_SET_BOOLEAN_MODIFIED_STATE(
        word		identifier,
        Boolean		modifiedState);

This message sets the modified state of an individual GenBoolean without affecting any other GenBooleans. You may mark GenBooleans modified even if they are not usable, not enabled, or not even present within the BooleanGroup.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: identifier The identifier of the GenBoolean.

modifiedState
TRUE to mark the GenBoolean modified, FALSE to mark it not modified.

Return: Nothing.

Interception: Generally not intercepted.

Altering the Destination

MSG_GEN_BOOLEAN_GROUP_GET_DESTINATION, MSG_GEN_BOOLEAN_GROUP_SET_DESTINATION, MSG_GEN_BOOLEAN_GROUP_GET_APPLY_MSG, MSG_GEN_BOOLEAN_GROUP_SET_APPLY_MSG

To retrieve the GenBooleanGroup's destination stored in its GBGI_ destination instance field or the message stored in its GBGI_ applyMsg instance field, use MSG_GEN_BOOLEAN_GROUP_GET_DESTINATION or MSG_GEN_BOOLEAN_GROUP_GET_APPLY_MSG .

To set the GenBooleanGroup's destination to an object or process, send the list a MSG_GEN_BOOLEAN_GROUP_SET_DESTINATION , passing it the optr of the new destination. This object will thereafter handle all apply actions sent by the GenBooleanGroup.

To set the GenBooleanGroup's apply action to a new message, send the list a MSG_GEN_BOOLEAN_GROUP_SET_APPLY_MSG , passing it the message to use.

MSG_GEN_BOOLEAN_GROUP_GET_DESTINATION
optr	MSG_GEN_BOOLEAN_GROUP_GET_DESTINATION();

This message returns the current destination object for the BooleanGroup, as specified in the GBGI_ destination instance field.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: None.

Return: The optr of the current destination object.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_DESTINATION
void	MSG_GEN_BOOLEAN_GROUP_SET_DESTINATION(
        optr	dest);

This message sets a new destination object for a BooleanGroup. The apply and status messages of this BooleanGroup will be sent to this new destination.

Source: Unrestricted.

Destination: Any GenBooleanGroup.

Parameters: dest The optr of the new destination object; this will be put into GBGI_ destination .

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_GET_APPLY_MSG
Message	MSG_GEN_BOOLEAN_GROUP_GET_APPLY_MSG();

This message returns the current apply action in the GenBooleanGroup's GBGI_ applyMsg instance field.

Source: Unrestricted.

Destination: Any GenBooleanGroup.

Parameters: None.

Return: The current apply message.

Interception: Generally not intercepted.

MSG_GEN_BOOLEAN_GROUP_SET_APPLY_MSG
void	MSG_GEN_BOOLEAN_GROUP_SET_APPLY_MSG(
        Message		message);

This message sets a new apply message in the GenBooleanGroup's GBGI_ applyMsg instance field.

Source: Unrestricted.

Destination: Any GenBooleanGroup object.

Parameters: message The new apply message.

Return: Nothing.

Return: Generally not intercepted.

This document is a single-page version of a a multi-page document, suitable for easy printing.


Pen Object Library

The Pen Library provides routines and object classes which work together to form the backbone of a note-book style database storing pen input.

An Ink object is a visual object and may be used by any application that wants to work with pen input. Though any targetable object may accept ink (see the Input chapter), InkClass has many optimizations for working with ink.

The Ink Database routines provide a front end to the standard GEOS Database (DB) library routines well suited for storing and organizing several small pieces of information. These routines allow the storage of notes within a hierarchical arrangement of folders. Each note may contain one or more pages of textual or ink information.

  1 The Ink Object
    1.1 Instance Data and Messages
    1.2 Storing Ink to DB Items
  2 Working with the Ink DB
    2.1 Getting Started
    2.2 Displaying the Data
    2.3 Titles and Keywords
    2.4 Navigating the Folder Tree
    2.5 Managing Notes and Folders
    2.6 Manipulating Notes
    2.7 Searching and Traversing the Tree
  3 InkControlClass

Pen Object Library: 1 The Ink Object

InkClass provides methods for storing multiple pen inputs in a compact form. It automatically handles all queries about pen input. It handles display, with the power to display the ink in any color, and it allows the use of standard or custom background pictures.

To change the way the Ink class (or any other appropriate class) handles ink, the messages to subclass are MSG_META_NOTIFY_WITH_DATA_BLOCK (with the notification type MANUFACTURER_ID_GEOWORKS, NT_INK), MSG_META_QUERY_IF_PRESS_IS_INK , and MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK .

Instance Data and Messages

Storing Ink to DB Items

Pen Object Library: 1.1 The Ink Object: Instance Data and Messages

When setting up an Ink object, probably the only pieces of instance data the application will be concerned with will be II_flags , II_dirtyOutput , and II_dirtyMsg .

Most of the flags are easy to understand, with the possible exceptions of the IF_HAS_TARGET and IF_DIRTY fields, which should not be set when creating the object in any case.

Code Display 21-1 InkClass Instance Data

/* 	II_flags:
 * 	  This field holds flags governing the object's behavior:
 *		IF_MOUSE_FLAGS,
 * 		IF_SELECTING,
 * 		IF_HAS_TARGET,
 *		IF_HAS_SYS_TARGET,
 *		IF_DIRTY,
 *		IF_ONLY_CHILD_OF_CONTENT,
 *		IF_CONTROLLED, (Set if to be used with an InkControl)
 *		IF_INVALIDATE_ERASURES,
 * 		IF_HAS_UNDO */
@instance 	InkFlags 			II_flags = IF_HAS_UNDO;
/*   	II_tool:
 * 	This field keeps track of how the user is interacting with the Ink. There
 * 	are three possible tools: IT_PENCIL, IT_SELECTOR, and IT_ERASER. */
@instance 	InkTool 		II_tool;
 /* 	II_penColor:
 * 	The color to use when drawing ink. */
@instance 	Color 		II_penColor = C_BLACK;
/* 	II_segments:
 * 	Do not set this field explicitly. This field is a handle to the chunk array
 * 	containing the pen segments. The segments are stored as an array of Point
 * 	structures. The stored coordinates are all positive; any x coordinate with
 * 	its sign bit set belongs to the last point in a gesture. Thus, a small
 * 	cross shape centered at (72, 72) might be stored:
 * 		(0x0048, 0x0046)
 * 		(0x8048, 0x004A) [note sign bit set in x coordinate]
 * 		(0x0046, 0x0048)
 * 		(0x804A, 0x0048)		 [sign bit set in x coordinate]	*/
@instance 	ChunkHandle 		II_segments;
/* 	II_dirtyMsg, II_dirtyOutput:
 * 	Together, these fields form an Action Descriptor. When the Ink processes
 * 	a point of pen information, erases anything, or handles an undo event the
 * 	IF_DIRTY flag will be set. If the flag was not set already, then the Ink
 *	will send the AD's message to the AD's object. The handler for this message
 * 	should probably clear the IF_DIRTY bit. */
@instance optr			II_dirtyOutput;
@instance Message	 		II_dirtyMsg;
@instance Rectangle 			II_selectBounds;			/* Internal */
@instance GStateHandle			II_cachedGState;			/* Internal */
@instance TimerHandle 			II_antTimer;			/* Internal */
@instance word 			II_antTimerID;			/* Internal */
@instance byte			II_antMask;			/* Internal */
/*	ATTR_INK_STROKE_SIZE:
 * 	Size of line used to draw ink. */
@vardata    InkStrokeSize   ATTR_INK_STROKE_SIZE;
typedef struct {
    byte    ISS_width;
    byte    ISS_height;
} InkStrokeSize;
/* 	ATTR_INK_MAX_POINTS:
 * 	Maximum number of points of ink data the object is prepared to store. */
@vardata    word ATTR_INK_MAX_POINTS;

Most of the Ink messages just change or retrieve the values of the instance fields. The exceptions are two messages which help those applications which need to save or transfer the Ink object's pen data. Use MSG_INK_SAVE_TO_DB_ITEM to save the pen data to an arbitrary DB item. If the application changes this information and wishes to pass it back to the ink object, use MSG_INK_LOAD_FROM_DB_ITEM .

MSG_INK_SET_TOOL
void 	MSG_INK_SET_TOOL(
        InkTool 	tool);

This message allows the Ink to switch between pencil and eraser tools, changing the II_tool field.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: tool A tool, either IT_PENCIL or IT_ERASER.

Return: Nothing.

Interception: Unlikely.

MSG_INK_GET_TOOL
InkTool 	MSG_INK_GET_TOOL();

This message returns the Ink's present tool, as stored in II_tool .

Source: Unrestricted.

Destination: Any Ink object.

Parameters: None.

Return: The present tool, either IT_PENCIL or IT_ERASER.

Interception: Unlikely.

MSG_INK_SET_PEN_COLOR
void 	MSG_INK_SET_PEN_COLOR(
        Color 	clr);

This message changes the color used to draw the ink, changing the value in II_penColor .

Source: Unrestricted.

Destination: Any Ink object.

Parameters: clr Index to a palette (e.g. C_RED).

Return: Nothing.

Interception: Unlikely.

MSG_INK_SET_DIRTY_AD
void 	MSG_INK_SET_DIRTY_AD(
        word	 method,
        optr	 object);

This message sets the Action Descriptor to be activated when the user dirties the object, changing the values in II_dirtyMsg and II_dirtyOutput .

Source: Unrestricted.

Destination: Any Ink object.

Parameters: method The message to send when the object is dirty.

object
The object which should receive the above message.

Return: Nothing.

Interception: Unlikely.

MSG_INK_SET_MAX_POINTS
void MSG_INK_SET_MAX_POINTS(
        word 	maxPoints);

Set the maximum number of points of ink data the object is prepared to store.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: maxPoints The message to send when the object is dirty.

Return: Nothing.

Interception: Unlikely.

MSG_INK_SET_FLAGS
void 	MSG_INK_SET_FLAGS(
        InkFlags 	setFlags,
        InkFlags 	clearFlags);

This message changes the value of the II_flags field. Note that something which sets the IF_DIRTY bit should probably also perform the action stored in the II_dirtyMsg and II_dirtyOutput fields.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: setFlags The flags to turn on.

clearFlags
The flags to turn off.

Return: Nothing.

Interception: Unlikely.

MSG_INK_GET_FLAGS
InkFlags 	MSG_INK_GET_FLAGS();

This message gets the value of the II_flags field.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: None.

Return: The present value of the II_flags field.

Interception: Unlikely.

MSG_INK_DELETE_LAST_GESTURE
int MSG_INK_DELETE_LAST_GESTURE();

This message removes the last gesture stored by the ink object; if the object had no ink stored, then nothing happens. The message returns zero if nothing was deleted.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: None.

Return: Zero if nothing deleted.

Interception: Unlikely.

MSG_INK_SET_STROKE_SIZE
void MSG_INK_SET_STROKE_SIZE(
        byte 	width,
        byte 	height);

Set the brush size to use when drawing ink.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: width Brush width.

height
Brush height.

Return: Nothing.

Interception: Unlikely.

MSG_INK_SAVE_TO_DB_ITEM
void	MSG_INK_SAVE_TO_DB_ITEM(
        DBReturn		* RetValue,
        InkDBFrame		* ptr);

This message saves the Ink's pen data into the passed DB item. The pen data will be stored compressed. Calling this message sets the object not dirty.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: ptr A pointer to an InkDBFrame structure, shown below.

RetValue
A pointer to an empty DBReturn structure, to be filled by the handler.

Return: The structure pointed to by RetValue will contain the returned information.

Structures: The InkDBFrame and DBReturn structures are defined below:

  typedef struct {
    Rectangle			IDBF_bounds;
        /* The bounds of the Ink data */
    VMFileHandle			IDBF_VMFile;
        /* VM file to write to*/
    DBGroupAndItem 			IDBF_DBGroupAndItem;
        /* DB Item to save to
         * (or NULL to create a new one) */
    word 			IDBF_DBExtra;
        /* Extra space to skip at start
         * of block */
} InkDBFrame;
  typedef struct {
    word		DBR_group;
    word		DBR_item;
    word		DBR_unused1;
    word		DBR_unused2;
} DBReturn;

Interception: Unlikely.

MSG_INK_LOAD_FROM_DB_ITEM
void	MSG_INK_LOAD_FROM_DB_ITEM(
        InkDBFrame 	*ptr);

This message loads the compressed data into the Ink from the passed DB item. If a NULL handle is passed, then the Ink is cleared. This message marks the Ink as clean.

Source: Unrestricted.

Destination: Any Ink object.

Parameters: ptr A pointer to an InkDBFrame structure.

Return: Nothing.

Structures: For the InkDBFrame structure, see MSG_INK_SAVE_TO_DB_ITEM .

Interception: Unlikely.

Pen Object Library: 1.2 The Ink Object: Storing Ink to DB Items

Pen information comes in as a MSG_META_NOTIFY_WITH_DATA_BLOCK of type NT_INK accompanied by an array containing the coordinates visited by the pen. The pen data keeps track of the coordinates of the pen input. Every time pen input comes in, the ink object notes the coordinates. The ink object is optimized to save space. For instance, the Ink object eliminates collinear points: if three pen events are collinear, it will not record the middle one, recognizing it as redundant.

The non-redundant points are written out to the II_segments field, a chunk array of Point structures. Note that the coordinates are unsigned. If a point's x coordinate's sign bit is set, that does not mean that the x coordinate is negative; if this sign bit is set this is a signal that this point is the last point of a gesture.

When writing pen data to a DB item, the Ink object does some more compression. Applications which work with the items used by MSG_INK_SAVE_TO_DB_ITEM and MSG_INK_LOAD_FROM_DB_ITEM must work with this compression. Since the user is dragging the pen around in a continuous gesture, the pen events tend to occur close together. Thus, it is nice to have a way to record a coordinate as a small offset from another coordinate. Since many strokes are almost horizontal or vertical, quite often the horizontal or vertical offset will be zero or one.

To take advantage of these tendencies, the ink object stores pen input as a bitstream. Coordinates may be recorded either as absolute positions or as offsets from the last coordinate.

When writing out a gesture to a DB item, the first point will always be recorded as an absolute position. Thus, first the x coordinate will be recorded, then the y coordinate. Each coordinate will be marked as absolute by the 1011 bit pattern.

For each subsequent pen point, the algorithm will first make sure that the new point is not collinear with the previous two. If it is, then the algorithm will make the incoming pen event overwrite the previous event's coordinates.

For each event, the algorithm will first write out the x coordinate, then the y coordinate.

  • If the coordinate is at 0 or 1 offset from the previous coordinate, the algorithm will write out the appropriate two-bit code (00, 01, or 11).
  • If the coordinate is at an offset from the previous coordinate between 2 and 8, then the algorithm will write out the appropriate 7 bit code (1000xxx for a positive offset, 1001xxx for a negative offset).
  • If the coordinate is more than 8 points from the previous coordinate, the algorithm writes out 1011 followed by the absolute coordinate, represented as a 15 bit unsigned quantity.

When the input is finished, the algorithm writes a 1000000 bit pattern, signalling the end of the segment.

Decompressing the data is a matter of traversing the bitstream and detecting the appropriate patterns.

As an example of how the algorithm compresses pen input, suppose the Ink object were writing the following gesture to a DB item:

(72, 71)
(82, 74)
(84, 74)
(85, 72)

The first coordinate is 72, so the algorithm will write out:
1011 (signals absolute coordinate) 000000001001000
 The second coordinate is 71, so after handling the second coordinate, the stream will be:
1011 000000001001000 1011 000000001000111
 The x coordinate of the second point is 82, which is 10 points away from the previous x coordinate. Unfortunately, this is too far to express as a short offset, so the algorithm writes another absolute coordinate (the new part of the stream is shown in italics):
1011 000000001001000 1011 000000001000111 1011 000000001010010
The y coordinate of the second point is 74, at a positive 3 offset from the previous y value, so the algorithm will write out the appropriate offset code instead of an absolute position code:
...1011 000000001000111 1011 000000001010010 10 00 010
The third point's x coordinate is 84, at a +2 offset from 82. The y coordinate is 74, the same as the previous point's y coordinate:
...1011 000000001000111 1011 000000001010010 10 00 010 10 00 001 00
The last point's x coordinate is one higher than the previous; its y coordinate is two less.
...1011 000000001010010 10 00 010 10 00 001 00 01 10 01 001
Since it has reached the end of the pen input (this was a suspiciously short gesture, a somewhat contrived example), the algorithm then writes an end-of-segment code:
...1011 000000001010010 10 00 010 10 00 001 00 01 10 01 001 10 00 000
If the Ink object were holding more than one gesture of information, it would write the next gesture's elements starting after the end-of-segment code of the first.

Pen Object Library: 2 Working with the Ink DB

The Ink Database provides a simplified, specialized API to the database library. It allows the user to organize pieces of information on notes stored in a hierarchy of folders. Each note may have one or more pages, with each page corresponding to the contents of an ink or text object. The data stored in each page is a DB item returned by MSG_INK_SAVE_TO_DB_ITEM (for Ink objects) or MSG_VIS_TEXT_GET_ALL_DB_ITEM (for Text objects).

Notes and folders are specified by means of a dword identifier. This identifier has nothing to do with where the note's (or folder's) data is stored, or where it appears in the folder tree. Applications should use the InkLoadPage() and InkSavePage() routines to work with a note's data, and use the routines described below to determine where a note or folder appears in the folder tree.

Getting Started

Displaying the Data

Titles and Keywords

Navigating the Folder Tree

Managing Notes and Folders

Manipulating Notes

Searching and Traversing the Tree

Pen Object Library: 2.1 Working with the Ink DB: Getting Started

InkDBInit()

To create an Ink Database, an application needs a file handle, perhaps the file holding a GenDocument's data. Before calling any other Ink Database functions, call InkDBInit() to set up the file correctly; this routine should be called exactly once per Ink DB. If the database is part of a GenDocument, then this routine should be called within the MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE . Other routines (described below) which might be appropriate when first setting up an Ink Database include InkSetDocPageInfo() and InkSetDocGString() .

Pen Object Library: 2.2 Working with the Ink DB: Displaying the Data

InkNoteLoadPage(), InkNoteSavePage(), InkGetDocPageInfo(), InkSetDocPageInfo(), InkSetDocGString(), InkGetDocGString(), InkNoteGetNoteType()

Assuming that the application is using text and ink objects to display the information held in the Ink DB, use InkNoteLoadPage() and InkNoteSavePage() to transfer information between the Ink object and the Ink DB. InkNoteLoadPage() loads an ink or text object with the data stored within the passed note. Use InkNoteGetNoteType() to determine what sort of data is stored within the note. Once the user has made changes, those changes should be stored to the database. Call InkNoteSavePage() to write the changes.

To find out the document size associated with an Ink Database, call InkGetDocPageInfo() . To change the page size, call InkSetDocPageInfo() .

The Ink DB routines support the notion of a background picture for ink information. There is one background picture for the entire database. To set the background picture, use InkSetDocGString() . To find out the current background picture, call InkGetDocGString() .

The background GString is stored in VM; call GrLoadGString() and GrDrawGString() to draw it.

Pen Object Library: 2.3 Working with the Ink DB: Titles and Keywords

InkNoteSetKeywords(), InkNoteSetKeywordsFromTextObject(), InkNoteGetKeywords(), InkNoteSendKeywordsToTextObject(), InkGetTitle(), InkSendTitleToTextObject(), InkFolderSetTitle(), InkFolderSetTitleFromTextObject(), InkNoteSetTitle(), InkNoteSetTitleFromTextObject()

Each note may have two text strings which are helpful for identification: a title and a set of keywords. These words may be used as the fields for a computed search if the application supports these; regardless, the user will certainly find these fields useful for organizing notes.

To set a note's title, call InkNoteSetTitle() . There is a corresponding InkFolderSetTitle() for setting the title of a folder. Since applications may wish to set the titles of these items based upon the user's entry in a text object, there are two routines InkNoteSetTitleFromTextObject() and InkFolderSetTitleFromTextObject() which take an item's name from a text object. InkGetTitle() gets any item's title, and InkSendTitleToTextObject() is a specialized function used to update the passed text object's text to hold the item's title. The maximum length of any title should be INK_DB_MAX_TITLE_SIZE.

Notes may have keywords: words which should not appear in the title but which are still useful for searches. Folders do not have keywords. To set a note's keywords, use InkNoteSetKeywords() ; to use the contents of a text object as the keywords, use InkNoteSetKeywordsFromTextObject() . To retrieve the keywords, call InkNoteGetKeywords() . InkNoteSendKeywordsToTextObject() replaces a text object's text with the passed note's keywords. The maximum length of any keyword should be INK_DB_MAX_NOTE_KEYWORDS_SIZE .

Pen Object Library: 2.4 Working with the Ink DB: Navigating the Folder Tree

InkDBGetDisplayInfo(), InkDBSetDisplayInfo(), InkDBGetHeadFolder(), InkGetParentFolder(), InkFolderGetContents(), InkFolderGetNumChildren(), InkFolderDisplayChildInList(), InkFolderGetChildInfo(), InkFolderGetChildNumber(), InkNoteGetNumPages()

Assuming the application allows the existence of more than one folder, it must allow some way to move around within the folder tree. If the application allows the user to change the structure of the folder tree, then it will need UI which allows the user to navigate an arbitrary tree. There are routines to find out and change which page is being displayed. For those applications which will need to get information about the folder tree, there are routines to get information about the folder tree.

To find the application's current location within the DB, call InkDBGetDisplayInfo() . This routine returns the current folder ID, the note ID if any is selected, and the page number within the note. To go to a different location, call InkDBSetDisplayInfo() . To use this routine, the application must pass a folder ID, along with a valid note ID and page number if a note is to be selected.

Chances are the user will be maneuvering within the folder tree. To get the ID of the root folder, use InkDBGetHeadFolder() . To find the parent folder of the passed parent or note, call InkGetParentFolder() . InkFolderGetContents() returns two chunk arrays, one containing the double word identifiers of all the folder's subfolders, the other containing the identifiers of the folder's child notes. InkFolderGetNumChildren() returns the number of subfolders and notes within a folder.

To display a note or folder's name in a GenDynamicList, use InkFolderDisplayChildInList() . This routine comes in handy when constructing UI for navigating the folder tree. To copy the icon and folder or note name of a folder or note into the visual moniker of an entry in a list, call InkNoteCopyMoniker() .

To get information about a folder's child, call InkFolderGetChildInfo() . This routine returns a bit specifying whether the child is a folder or note, along with the child's ID number. The InkFolderGetChildNumber() routine returns the passed child's place number within the folder.

Pen Object Library: 2.5 Working with the Ink DB: Managing Notes and Folders

InkFolderCreateSubFolder(), InkFolderMove(), InkFolderDelete(), InkNoteCreate(), InkNoteDelete(), InkNoteMove(), InkNoteCreatePage()

Some Ink DB applications might just create a hierarchy of notes and not allow the user to move or change notes. Applications that will move notes and folders should use the following functions to make changes.

The InkFolderCreateSubFolder() routine creates a new folder as a child of the passed existing folder. Use InkFolderMove() to move a folder to a new parent folder. InkNoteMove() similarly moves a note to a new parent folder. InkFolderDelete() deletes a folder and all subfolders and notes that folder contained. InkNoteCreate() creates a new note. InkNoteDelete() deletes a note. InkNoteCreatePage() adds a new page to a note.

Pen Object Library: 2.6 Working with the Ink DB: Manipulating Notes

InkNoteGetPages(), InkNoteGetNumPages(), InkNoteSetModificationDate(), InkNoteGetModificationDate(), InkNoteGetCreationDate(), InkNoteSetNoteType(), InkNoteGetNoteType()

Normally, the note will store information supplied by an Ink or Text object. However, applications may work with a note's information directly. Call InkNoteGetPages() to get the DB item in which the note's information is stored. The DB item contains a chunk array; each entry of the array contains the information for one page (the DB item associated with an Ink or Text object). To find out how many pages there are in a given note, call InkNoteGetNumPages() .

The note will be expecting either text or ink; call InkNoteSetNoteType() to specify what sort of data will be coming in. The note type is specified by means of a NoteType value: NT_INK or NT_TEXT. To find out a note's type, call InkNoteGetNoteType() .

When writing changes, you may wish to update the note's modification date. Call InkNoteSetModificationDate() to update this information. To find out the date last modified, call InkNoteGetModificationDate() . To find out the date the note was created, call InkNoteGetCreationDate() .

Pen Object Library: 2.7 Working with the Ink DB: Searching and Traversing the Tree

InkNoteFindByTitle(), InkNoteFindByKeywords(), InkFolderDepthFirstTraverse()

Sometimes the user will remember what a note is called, but has lost it in the tree of folders. Sometimes the user will want to find all notes which contain a certain keyword. Use InkNoteFindByTitle() to get a buffer containing IDs of all notes whose titles match the passed string. InkNoteFindByKeywords() similarly returns a buffer containing the IDs of all notes with matching keywords.

For more complicated commands, InkFolderDepthFirstTraverse() allows the application to perform a depth-first traversal of the folder tree, calling the passed routine with all encountered folders.

Pen Object Library: 3 InkControlClass

InkControlClass , a subclass of GenControlClass , provides a menu which allows the user to select an Ink tool for use with an Ink object.

Code Display 21-2 InkControlClass Features

  typedef ByteFlags 	InkControlFeatures;
/* These features may be combined using | and &:
    ICF_PENCIL_TOOL,
    ICF_ERASER_TOOL
    ICF_SELECTION_TOOL */
  typedef ByteFlags 	InkControlToolboxFeatures;
/* These features may be combined using | and &:
    ICTF_PENCIL_TOOL,
    ICTF_ERASER_TOOL
    ICTF_SELECTION_TOOL */
#define  IC_DEFAULT_FEATURES 					(ICF_PENCIL_TOOL | ICF_ERASER_TOOL | \
                     ICF_SELECTION_TOOL)
#define  IC_DEFAULT_TOOLBOX_FEATURES 					(ICTF_PENCIL_TOOL | ICTF_ERASER_TOOL | \
                     ICTF_SELECTION_TOOL)
/* Add this controller to the application's self-load options GCN list. */
This document is a single-page version of a a multi-page document, suitable for easy printing.


The Table Objects

The Table Library allows you to easily create dynamic scrolling tables. By using objects from TableClass , you can create tables with certain characteristics; in particular, tables with many rows, with the data organized by column. For example, you could use TableClass to present a phone number list. One column might have a person's name, the next the phone number, the next the fax number, and so on. Each row would have the same basic format, though it would have different contents. The Table is designed primarily for text tables, though it can display anything you wish to draw.

You can give each column its own characteristics. Users may begin entering data directly into the Table; the TableClass object will automatically present a VisText to let the user enter text, then send a message to itself to replace the cell's contents with the new text; you can subclass that message if you need to check the text the user has entered or perform some operation on the new data.

The Table object does not actually store data; it rather helps you organize data stored elsewhere, and helps you display that data in a tabular format. It also makes it much easier for a user to edit the data.

1 Table Objects
2 TableClass Overview
3 TableClass Instance Data
3.1 Table Attributes
3.2 Defining Columns
3.3 Working with Rows
4 Using a Table Object
4.1 Drawing Cells
4.2 Selecting Cells
4.3 Editing Cells
4.4 Dragging and Dropping
4.5 Custom Scrolling Behavior
4.6 Handling Locator Searches
4.7 Table Headings
4.8 Changing Column Definitions
5 TableContentClass
5.1 TableContent Instance Data
5.2 Altering TableContent Instance Data
6 LocatorClass
6.1 Index Mode
6.2 Text Mode
6.3 LocatorClass Instance Data
6.4 Locator Actions

The Table Objects: 1 Table Objects

There are a number of Table objects available for you to use:

  • TableViewClass
     This subclass of GenViewClass acts as a holder for your table. Its GVI_content should be a TableContent. Specify the main table in the view with the TVI_mainTable instance data field.
  • TableContentClass
     This subclass of VisContentClass acts as an interface between the TableView object and the Table children.
  • TableClass
     This subclass of VisCompClass contains all the main Table functionality. This is where the bulk of the Table object functionality is located. You will typically subclass this object to add your own features.
  • LocatorClass
     This subclass of TableClass isn't really a Table at all. The Locator is a utility object that interacts with the Table to provide Table searching. You typically don't subclass this object; just include one in your application.

The Table Objects: 2 TableClass Overview

TableClass implements a scrolling table. The table should be composed of one or more different columns, and one or more similar rows. Typically, each column would be a different category, and each row would have an entry for every different category.

TableClass is a subclass of VisCompClass . This means that you can group tables together or with other Vis objects. (You can put titles on a scrolling table by grouping two different Table objects together; this is discussed in Table Headings .)

TableClass supports the following features:

  • Redrawing:
    TableClass sends itself messages to instruct itself to redraw cells as needed; you can intercept these messages if you wish to change how cells are drawn. You can instruct a Table object to redraw a range of cells; it will automatically send the appropriate messages to instruct itself to redraw just those cells currently visible on-screen.
  • Pointer Events:
    The TableClass object intercepts pointer events and sends itself appropriate MSG_TABLE_SELECT messages. Applications can subclass these messages if they want to take special actions when cells are selected. When you instantiate a Table object, you specify (by column) what combination of pointer events should act as a selection. TableClass can identify start-select, end-select, double-clicking, clicking and holding, and reselecting (i.e. clicking on an already-selected cell).
  • Selecting Cells
    When cells are selected, a Table object sends itself a MSG_TABLE_SELECT message. You can subclass this message if you wish. If you do not subclass this message, TableClass can handle the selection in a number of different ways: it can select a single cell, a whole row, a rectangular region, etc. The table also lets you specify how selections should be identified (e.g. drawn in reverse video, surrounded by a solid box, etc.).
  • Scrolling:
    If the Table object contains more rows than can be shown on-screen, it will automatically scroll as needed. You can instruct the Table to scroll up or down by sending it messages. You can also set a flag, instructing the Table to scroll automatically when it receives certain pointer events (e.g. when the pointer is dragged off the top or bottom of the table). The Table can also be set to scroll when certain keys are pressed.
  • Editing:
    The table can automatically create a VisText object when the user wants to edit the text in a cell. You can send messages to instruct it to create that VisText ; you can also subclass messages to set the default contents of the VisText , or to process the text entered by the user before it's entered in the cell. This is discussed in Editing Cells .
  • Drag-Drop:
    The Table implements a quick-copy, drag-and-drop mechanism. When you activate quick-copy mode, the user can drag a cell to a new location. At this time, the Table sends itself a message; you can intercept this message, and take an appropriate action (such as copying the data from the source cell over the destination cell). This is discussed in Dragging and Dropping .

There is one function the Table object does not perform. It does not store the data contained in the cells. When it needs to know the contents of a cell, it requests that information; when the user changes a cell, it informs you. You, however, must store the information yourself.

For example, whenever the Table object needs to draw a cell, it sends itself MSG_TABLE_QUERY_DRAW , specifying which cell needs to be drawn. You should intercept this message; your handler should look up the data stored in that cell, and draw its contents. Similarly, when a user edits a cell, you first tell the Table object what the current contents of the cell are (by intercepting MSG_TABLE_START_EDIT_CELL_TEXT ). The Table then brings up a VisText which contains the current contents of the cell. The user can edit or change this text. When the user is finished, the Table object informs the application (via MSG_TABLE_DONE_EDIT_CELL_TEXT ) what the cell's new contents are. The application should intercept the message and store the new contents.

Since the Table refers to its cells by row and column numbers, you may wish to store its data in a GEOS cell file . A cell file is a GEOS VM file which uses special structures and routines to organize DB items so they can be accessed by row and column numbers, instead of by group and item handles. For more information about cell files, consult the "Database" chapter of the Concepts book provided with the SDK.

The Table Objects: 3 TableClass Instance Data

TableClass is a subclass of VisCompClass . This lets you group Tables together with other Tables and other Vis objects. Note, however, that you are not permitted to add children to a Table object. When TableClass creates a VisText object to let users enter data, it makes that VisText a child of the Table ; if you try to add children to the TableClass , you will interfere with that process, and cause many unpredictable problems.

This section describes the instance data fields defined for TableClass . It also describes how to set, examine, and change those fields which are of interest to the application.

The TableClass has the following instance data:

Code Display 5-1 TableClass Instance Data

/* TI_rows specifies the number of rows in the table object. This field can be set
 * dynamically with MSG_TABLE_SET_ROW_COUNT. This height includes the row
 * separators.
 */
    @instance		word		TI_rows;
/* TI_columns specifies the total number of columns in the table. All columns must
 * be visible at once (the table does not scroll horizontally). You must set this
 * field in your source file; it cannot currently be set at run-time.
 */
    @instance		word		TI_columns;
/* TI_visibleRows contains the number of rows currently displayed. You must set
 * this field in your source file; it cannot currently be set at run-time.
 */
    @instance		word		TI_visibleRows;
/* TI_topRow contains the row number of the first visible row. This is maintained
 * automatically by the Table object; applications should not set this field.
 */
    @instance		word		TI_topRow;
/* TI_tableFlags contains a record of TableFlags. This record is described below
 * in  Table Attributes.
 */
    @instance		TableFlags		TI_tableFlags;
/* TI_rowFlags contains a record of TableRowFlags. This record is described below
 * in  Working with Rows.
 */
    @instance		TableRowFlags		TI_rowFlags;
/* TI_columnDefinitions contains the chunk handle of a list of column definitions.
 * Column definitions are discussed in  Defining Columns.
 */
    @instance		ChunkHandle		TI_columnDefinitions;
/* TI_rowHeight specifies the height of each row in "points" (72 points = 1 inch).
 * You must set this in your source code; there is no way to change this
 * dynamically.
 */
    @instance		word		TI_rowHeight;
/* This field specifies the Table object's TableBorderFlags, which specify the
 * border and bounds for the Table object. TableBorderFlags are described
 * below in  Table Attributes.
 */
    @instance		TableBorderFlags			TI_borderFlags;
/* The following instance data fields are for internal use by the Table object.
 * You should not set, inspect, or change any of these fields, either in your
 * source code or at run-time.
 */
    @instance		TableCellLocation			TI_currentSelectionStart =
    { T_NONE_SELECTED, T_NONE_SELECTED };
    @instance		TableCellLocation			TI_currentSelectionEnd =
    { T_NONE_SELECTED, T_NONE_SELECTED };
    @instance		TableCellLocation			TI_lastSelectionStart =
    { T_NONE_SELECTED, T_NONE_SELECTED };
    @instance		TableCellLocation			TI_lastSelectionEnd =
    { T_NONE_SELECTED, T_NONE_SELECTED };
    @instance		Rectangle			TI_bounds = {0, 0, 0, 0};
    @instance		TableRangeInversionType			TI_tableRangeInversion;
    @instance		TableCellLocation			TI_lastCell = {
            T_NONE_SELECTED,
            T_NONE_SELECTED};
    @instance		TableSelectionDrawStyleType 			TI_selectionDrawStyle;
    @instance		TablePrivateFlags			TI_privateFlags;
    @instance		ChunkHandle			TI_textObj;

Table Attributes

Defining Columns

Working with Rows

The Table Objects: 3.1 TableClass Instance Data: Table Attributes

TableFlags, TableBorderFlags, MSG_TABLE_SET_FLAGS, MSG_TABLE_GET_FLAGS, MSG_TABLE_SET_BORDER_FLAGS, MSG_TABLE_GET_BORDER_FLAGS

There are a few attributes which apply to the entire Table . Ordinarily, you would set these up when you define the Table , and leave them unchanged; however, you can get or change these attributes at will, by sending messages.

Every Table has a record of TableFlags , stored in the instance data field TI _tableFlags . This record contains the following flags:

TF_MAIN_TABLE
This flag indicates that this Table is the main (primary) table below the TableContent object. A Table marked with this flag will be the one that is sent scroll messages from the TableView. This is important if you use a separate TableClass object to contain headers for your table. (See Table Headings .)
TF_INTERNAL_DRAG_DROP
This flag should not be modified; it is internally managed. If this flag is set, this indicates that the Table is in drag-and-drop mode from one location in the Table to another location within that same Table; this lets the user copy one cell to another.The user does this by start-selecting the cell he or she wants to copy, and dragging ("hold-selecting") the pointer to the new location, then releasing the pointer ("end-selecting"); the cell's contents will be copied to the new location. If TF_EXIT_DRAG_DROP_UPON_COMPLETION is set, the Table will automatically clear TF_INTERNAL_DRAG_DROP after every drag-drop operation. (See Dragging and Dropping .)
TF_EXTERNAL_DRAG_DROP
This flag should not be modified; it is internally managed. If this flag is set, this indicates that the Table is in drag-and-drop mode from a location outside the Table to another location within the Table.
TF_ENABLE_AUTO_SCROLLING
If this flag is set, the Table will automatically scroll whenever the user clicks the pointer inside the Table and drags it across the top or bottom boundary. This flag does not affect any other scrolling behavior you may have added to your Table (such as scroll buttons in the parent TableView).
TF_SELECTION_ALWAYS_VISIBLE
If this flag is set, a Table's selection will always remain visible when scrolling. Any scrolling that might move the current selection off-screen will result in the movement of the selection (row by row) to stay visible. In other words, this flag does not ensure that the current selection remains visible; it only ensures that a selection bar exists on-screen.
TF_FIXED_SELECTION
This flag is currently unsupported.
TF_EXIT_DRAG_DROP_UPON_COMPLETION
If this flag is set, when the user finishes a drag-and-drop operation, the Table will automatically leave drag-and-drop mode, clearing TF_INTERNAL_DRAG_DROP. (See Dragging and Dropping .) If you do not set this flag, once you begin a drag and drop operation, you will need to manually exit drag and drop by clearing the TF_INTERNAL_DRAG_DROP flag yourself. (This is not recommended.)
TF_TARGETABLE
If this flag is set, the Table object will grab the target whenever it receives a MSG_META_START_SELECT . Unless your Table is display-only, you will probably always want this flag set.
TF_NOTIFY_SELECTION_CHANGED
If this flag is set, the Table object will send itself MSG_TABLE_NOTIFY_SELECTION_CHANGED whenever the stored selection value changes. If you wish to have that message sent (and intercept it) you must set this flag.

You may set these bits in TI _tableFlags when you define the table object in your source file. If you wish to change the TableFlags record of an existing Table object, you must do this by sending the object MSG_TABLE_SET_FLAGS , not by editing TI _tableFlags directly. You can find out the current settings of TI_tableFlags by sending MSG_TABLE_GET_FLAGS to the Table object.

Each table object also has a record of TableBorderFlags , stored in the instance data field TI _borderFlags . This record specifies what borders should be drawn around the Table . This record has the following flags:

TBF_BOX_BORDERS
If this flag is set, a box will be drawn around the entire portion of the Table visible on-screen. Setting this flag is equivalent to setting TBF_TOP_BORDER, TBF_BOTTOM_BORDER, TBF_LEFT_BORDER, and TBF_RIGHT_BORDER.
TBF_TOP_BORDER
If this flag is set, a line will be drawn at the top border of the portion of the Table visible on-screen.
TBF_BOTTOM_BORDER
If this flag is set, a line will be drawn at the bottom border of the portion of the Table visible on-screen.
TBF_LEFT_BORDER
If this flag is set, a line will be drawn at the left border of the portion of the Table visible on-screen.
TBF_RIGHT_BORDER
If this flag is set, a line will be drawn at the right border of the portion of the Table visible on-screen.
TBF_BOX_MARGINS
If this flag is set, margins will be added around the entire portion of the Table visible on-screen. Setting this flag is equivalent to setting TBF_TOP_MARGIN, TBF_BOTTOM_MARGIN, TBF_LEFT_MARGIN, and TBF_RIGHT_MARGIN.
TBF_TOP_MARGIN
If this flag is set, a margin will be added along the top border of the portion of the Table visible on-screen.
TBF_BOTTOM_MARGIN
If this flag is set, a margin will be added along the bottom border of the portion of the Table visible on-screen.
TBF_LEFT_MARGIN
If this flag is set, a margin will be added along the left border of the portion of the Table visible on-screen.
TBF_RIGHT_MARGIN
If this flag is set, a margin will be added along the right border of the portion of the Table visible on-screen.

To find out what TableBorderFlags are currently set, send MSG_TABLE_GET_BORDER_FLAGS to the Table . As with TableFlags , you may set the instance field TI _borderFlags in your source file, but you may not alter it directly at run-time; instead, you can set the field indirectly, by sending MSG_TABLE_SET_BORDER_FLAGS to the Table object.

MSG_TABLE_GET_FLAGS
TableFlags 	MSG_TABLE_GET_FLAGS();

This message returns the current TableFlags settings in the recipient's TI _tableFlags field. To change this field, send MSG_TABLE_SET_FLAGS .

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: None.

Return: The TableFlags record currently stored in the Table object's TI _tableFlags field.

Structures: The TableFlags record (see Every Table has a record of TableFlags, stored in the instance data field TI_tableFlags. This record contains the following flags: ).

MSG_TABLE_SET_FLAGS
void	MSG_TABLE_SET_FLAGS(
        TableFlags		setTableFlags,
        TableFlags		unsetTableFlags);

This message alters the TableFlags settings in the recipient's TI _tableFlags field. To find out what the current settings are, send MSG_TABLE_GET_FLAGS .

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: setTableFlags All the flags in this record will be turned on .

unsetTableFlags
All the flags in this record will be turned off .

Return: Nothing.

Structures: The TableFlags record (see Every Table has a record of TableFlags, stored in the instance data field TI_tableFlags. This record contains the following flags: ).

MSG_TABLE_GET_BORDER_FLAGS
TableBorderFlags 	MSG_TABLE_GET_BORDER_FLAGS();

This message returns the current TableBorderFlags settings in the recipient's TI _borderFlags field. To change this field, send MSG_TABLE_SET_BORDER_FLAGS .

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: None.

Return: The TableBorderFlags record currently stored in the Table object's TI _borderFlags field.

Structures: The TableBorderFlags record (see Each table object also has a record of TableBorderFlags, stored in the instance data field TI_borderFlags. This record specifies what borders should be drawn around the Table. This record has the following flags: ).

MSG_TABLE_SET_BORDER_FLAGS
void	MSG_TABLE_SET_BORDER_FLAGS(
        TableBorderFlags		borderFlags);

This message alters the TableBorderFlags settings in the recipient's TI _borderFlags field. To find out what the current settings are, send MSG_TABLE_GET_FLAGS .

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: borderFlags The current value in TI _borderFlags will be replaced by the value in this record.

Return: Nothing.

Structures: The TableBorderFlags record (see Each table object also has a record of TableBorderFlags, stored in the instance data field TI_borderFlags. This record specifies what borders should be drawn around the Table. This record has the following flags: ).

The Table Objects: 3.2 TableClass Instance Data: Defining Columns

TableColumnDefinition, TableColumnFlags, TableRangeInversionType, MSG_GEN_TABLE_GET_COLUMN_COUNT

When you create a Table object, you must specify what columns the Table has. Typically, each column will represent a different kind of data, and can be of a different length. When you define a Table object, you specify how many columns the table will have; this number is fixed for the table, and cannot be changed later. (It is possible to have your Table switch the column definitions that they use, although it will take a fair amount of work on your part; that is discussed in Changing Column Definitions .) You can find out how many columns a table has by sending it MSG_TABLE_GET_COLUMN_COUNT .

Each column has its own definition--stored in the Table's TI_ columnDefinitions array--specified by a TableColumnDefinition structure that cannot be changed later. This structure has the following definition:

 typedef struct {
    TableColumnFlags			TCD_flags;
    word			TCD_width;
} TableColumnDefinition;
TCD _flags
This is a TableColumnFlags record (described on The TableColumnFlags record has the following flags: below).
TCD _width
This is the width of the column in points (72 points = 1 inch, so 28.3 points ª 1 centimeter).

The TableColumnFlags record has the following flags:

TCF_DRAW_RIGHT_SEPARATOR
If this flag is set, the column will have a solid line drawn on its right border (separating it from the next column to its right). This right-separator will not be drawn if the column is the last (right-most) column in the Table.
TCF_START_SELECT
If this flag is set, then if the user performs a "start-select" anywhere in the column, the Table object will send itself MSG_TABLE_SELECT.
TCF_END_SELECT
If this flag is set, then if the user performs an "end-select" anywhere in the column, the Table object will send itself MSG_TABLE_SELECT.
TCF_DRAG_SELECT
If this flag is set, then if the user performs a pointer drag that crosses cell boundaries within the column, or enters or leaves the column, the Table object will send itself MSG_TABLE_SELECT.
TCF_DOUBLE_SELECT
If this flag is set, then if the user double-clicks anywhere in the column, the Table object will present a VisText , allowing the user to edit the contents of the cell. (See Editing Cells .)
TCF_RESELECT
If this flag is set, then if the user performs a "start-select" in the column on a cell that is already in a range of selected cells, the Table object will send itself MSG_TABLE_SELECT.
TCF_HOLD_SELECT
If this flag is set, then if the user performs a "start-select" in the column on a cell and holds for a certain period of time, the Table will send itself MSG_TABLE_SELECT. This flag also sets the TF_INTERNAL_DRAG_DROP flag.
TCF_TRIT
This is a three-bit field. The value in this field is a member of the TableRangeInversionType enumerated type; it specifies how the Table should display selected cells in this column.

TableRangeInversionType specifies which cells should be drawn as highlighted when the user performs a selection action. Note that each column has its own TableRangeInversionType value. If a selection action crosses from one column into another, the Table uses the TableRangeInversionType of the last column the pointer was in. TableRangeInversionType has the following values:

TRIT_CELL
When the user selects a cell in this column, highlight just that cell.
TRIT_ROW
When the user selects a cell in this column, highlight the entire row containing that cell.
TRIT_COLUMN
When the user selects a cell in this column, highlight the entire visible portion of this column (i.e. every cell in this column that is (at least partially) visible on-screen).
TRIT_MULTI_ROW
Highlight the area bounded by the two cells in which the selection started and ended. The selection is made row by row as if all cells were in a single sequence with the last (right-most) cell of one row being followed by the first (left-most) cell of the next row. (One can think of this selection being analogous to a text selection across several lines.)
TRIT_RECTANGLE
Highlight all the cells in the rectangle pointed by the start-select cell and the end-select cell.
TRIT_MULTI_ROW_FULL
Highlight the row in which the user start-selects, and all rows through which the user drags the selection pointer.
TRIT_MULTI_COL_FULL
Highlight the first column in which the user start-selects, and all columns through which the user drags the selection pointer.
TRIT_NONE
This indicates that the Table should not highlight any selections.

When you create a Table , you must specify how many columns the table has, and what the characteristics of each column are. You specify the number of columns the Table has by setting TI _columns to that number. You cannot change the number of columns in the Table after the Table has been created. To find out how many columns a Table has, send it MSG_TABLE_GET_COLUMN_COUNT .

To specify the attributes of each column, you must create a chunk in the same object block as the Table . That chunk must contain an array of TableColumnDefinition structures, one for each column in the Table . The structures should be in the same order as the columns (i.e. the left-most column would be the first structure in the array).

MSG_GEN_TABLE_GET_COLUMN_COUNT
word	MSG_GEN_TABLE_GET_COLUMN_COUNT();

This message returns the number of columns in a Table object.

Source: Unrestricted.

Destination: Any Table object.

Parameters: None.

Return: The number of columns in the Table .

The Table Objects: 3.3 TableClass Instance Data: Working with Rows

TableRowFlags, MSG_TABLE_GET_ROW_FLAGS, MSG_TABLE_SET_ROW_FLAGS, MSG_TABLE_GET_ROW_COUNT, MSG_TABLE_SET_ROW_COUNT

In a Table object, rows and columns are fundamentally different. Each column has its own properties; columns are generally used to display different kinds of information. Rows, on the other hand, are similar throughout a Table . Each row may contain different data, but all rows have the same basic structure throughout a given Table .

This has some consequences. For example, all columns are usually displayed; the Table should be wide enough to display all visible columns at once.

All the rows, on the other hand, are generally not displayed at once. The Table will display as many rows as it has room for; the user can scroll the table to see other rows. Also, while there are row properties (defined in TableRowFlags ) that are similar in nature to the column properties defined in TableColumnFlags , you cannot set these properties individually for a row; all rows must have the same TableRowFlags .

The instance data field TI _rowFlags specifies what TableRowFlags will be used for every row in the table. TableRowFlags is a word-sized record with the following field:

TRF_DRAW_ROW_SEPARATOR
If this field is set, the Table will draw a line below each column separating the rows. No line will be drawn below the Table's last row.

To find out how TI _rowFlags is set, send MSG_TABLE_GET_ROW_FLAGS to the Table . To change the settings in this field, send MSG_TABLE_SET_ROW_FLAGS.

To find out how many rows a table has, send it MSG_TABLE_GET_ROW_COUNT . You can also add or remove rows at will by sending MSG_TABLE_SET_ROW_COUNT. This message adds rows to, or removes them from, the end of the Table .

MSG_TABLE_GET_ROW_FLAGS
TableRowFlags MSG_TABLE_GET_ROW_FLAGS();

This message returns the current settings of a Table object's TI _rowFlags field. This field holds the TableRowFlags used for every row in the Table .

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: Nothing.

Return: The TableRowFlags used for all rows in the Table .

Structures: TableRowFlags (described on TRF_DRAW_ROW_SEPARATOR If this field is set, the Table will draw a line below each column separating the rows. No line will be drawn below the Table's last row. ).

MSG_TABLE_SET_ROW_FLAGS
void	MSG_TABLE_SET_ROW_FLAGS(
        TableRowFlags		flags);

This message changes current settings of a Table object's TI _rowFlags field. This field holds the TableRowFlags used for every row in the Table .

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: flags This is the new set of TableRowFlags to be used for every row in the Table .

Return: Nothing.

Structures: TableRowFlags (described on TRF_DRAW_ROW_SEPARATOR If this field is set, the Table will draw a line below each column separating the rows. No line will be drawn below the Table's last row. ).

MSG_TABLE_GET_ROW_COUNT
word	MSG_TABLE_GET_ROW_COUNT();

This message returns the total number of rows in a Table object.

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: Nothing.

Return: The number of rows in the Table .

MSG_TABLE_SET_ROW_COUNT
void	MSG_TABLE_SET_ROW_COUNT(
        word	rowCount);

This message changes the Table object's total number of rows. If the new row count is smaller than the old one, the last rows will be truncated.

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: rowCount The new number of rows the Table should contain. Rows are added and removed at the end of the Table .

Return: Nothing.

The Table Objects: 4 Using a Table Object

The Table object is designed to be easy to use. You cannot use a TableClass object directly; you will need to define a subclass of TableClass in order to intercept certain required messages. You will also need to set a few instance fields when you define the Table in your source code.

The Table object must appear within a TableContent object. If it is within a regular VisContent, crashes may result.

When you create a Table object, you will need to set the following instance data fields (the other fields may be left with there default values):

TI _rows
This is the number of rows in the Table . You may set this field to zero. (You can add and remove rows at run-time.) For more information, see Working with Rows .
TI _columns
This is the number of columns in the Table . This must be at least one. You cannot currently add or remove columns at run-time. For more information, see Defining Columns .
TI _visibleRows
This is the number of rows visible on-screen at any one time; thus, this field, together with TI _rowHeight , define the height of a Table object. This field must be at least 1.
TI _columnDefinitions
This is the handle of a chunk containing an array of TableColumnDefinition structures, which specify the properties of the Table 's columns. For more information, see Defining Columns .
TI _rowHeight
This is the height of a row in points (72 points = 1 inch, so 28.3 points ª 1 centimeter). This must be larger than zero.

In order to use a TableClass object, you must define handlers for a few messages. As a general rule, your handlers should call the TableClass handlers after they take their actions; in some cases, your handlers's main role is to add extra data to the message, which will be interpreted by the default handler.

You should define handlers for the following tasks:

  • You must define a handler for MSG_TABLE_QUERY_DRAW , which redraws a specified cell to a GState. (The TableClass object doesn't actually know what the contents of the cells are; it relies on your subclass's code to draw the cells.) For more information, see Drawing Cells .
  • You may wish to define a handler for MSG_TABLE_SELECT . This will let you know when the user has selected a cell; you can perform appropriate actions, and decide whether to call the default handler (which highlights the selection and performs other selection actions). For more information, see Selecting Cells .
  • You should provide handlers for MSG_TABLE_START_EDIT_CELL_TEXT and MSG_TABLE_DONE_EDIT_CELL_TEXT . These messages let the user edit the text in Table cells. By subclassing the messages, you can provide the current cell contents for the user to edit, and store the user's text after he or she has finished entering it. For more information, see Editing Cells .
  • The Table object supports drag-and-drop copying. However, it does not actually know what the cells contain, so it can't perform the copying itself. Instead, when the user performs a drag-and-drop operation, the Table object sends MSG_TABLE_DRAG_DROP_COMPLETE ; the application should intercept the message, and do any appropriate copying of data. For more information, see Dragging and Dropping .
  • You may want to implement your own custom scrolling behavior. ( TableClass supports auto-scrolling with the addition of the TF_ENABLE_AUTO_SCROLLING flag; auto scrolling does not, however, provide any visual UI (such as scroll buttons) to enable scrolling.) If you want custom scrolling behavior, you will need to implement scroll buttons in your TableView and appropriate messages to your Table object (such as MSG_TABLE_SCROLL_SINGLE_UP and MSG_TABLE_SCROLL_SINGLE_DOWN) . For more information, see Custom Scrolling Behavior .
  • If you link a Locator object to your Table (see LocatorClass ) you will want to intercept MSG_TABLE_CHAR_LOCATE and MSG_TABLE_STRING_LOCATE to implement your own search methods. For more information, see Handling Locator Searches .
  • If you want to change your column definitions dynamically, you will need to define additional column definitions and switch which ones you use under the Table. There is no API currently for this behavior; you will need to write the messages and handle them yourself. This is discussed in Changing Column Definitions .

Drawing Cells

Selecting Cells

Editing Cells

Dragging and Dropping

Custom Scrolling Behavior

Handling Locator Searches

Table Headings

Changing Column Definitions

The Table Objects: 4.1 Using a Table Object: Drawing Cells

MSG_TABLE_QUERY_DRAW, MSG_TABLE_REDRAW_TABLE, MSG_TABLE_REDRAW_ROW, MSG_TABLE_REDRAW_COLUMN, MSG_TABLE_REDRAW_CELL, MSG_TABLE_REDRAW_RANGE

The Table object does not actually store the contents of any of the cells. Instead, it helps the application manage and display its data. This means that whenever the Table needs to draw a cell (e.g. when the Table scrolls, or when a cell is covered and then exposed), it has to ask the application to draw it.

Whenever the Table object needs to draw a cell, it sends itself MSG_TABLE_QUERY_DRAW , once for each cell that needs to be redrawn. This message is designed to be subclassed; if you do not subclass it, the default handler will not do anything, and the cell will not be drawn.

The message comes with two arguments:

  • A TableCellLocation structure, specifying which cell needs to be redrawn.
  • A GStateHandle . The GState's clipping region is set to the current cell's boundaries; the current position in the GState is the upper-left corner of the cell.

You should intercept this message and take whatever actions are necessary to draw the contents of the cell. Ordinarily, this means calling a routine such as GrDrawTextAtCP() . The default handler doesn't do anything, so you need not call the superclass. You should not free the passed GState.

For efficiency reasons, the Table object actually uses the same GState for several cells that all need to be redrawn at once. For example, suppose the Table needs to redraw cells (10, 0), (10, 1), (10, 2), and (10, 3). The table will take the following steps:

  1. First, the Table creates a GState.
  2. The Table sets the GState's clipping region to the boundaries of cell (10, 0), and puts the current position in the upper-left corner of that cell.
  3. The Table sends itself MSG_TABLE_QUERY_DRAW, specifying that cell (10, 0) needs to be redrawn.
  4. The application intercepts this message, drawing the contents of cell (10, 0) to the passed GState.
  5. The Table changes the GState's clipping region to the boundaries of cell (10, 1), and moves the current position to the upper-left corner of that cell.
  6. The Table sends itself MSG_TABLE_QUERY_DRAW, specifying that cell (10, 1) needs to be redrawn.
  7. The application intercepts this message, drawing the contents of cell (10, 1) to the passed GState.
  8. The Table repeats steps 5-7 for cells (10, 2) and (10, 3).
  9. Finally, the Table frees the GState.

Code Display 5-2 Handling MSG_TABLE_QUERY_DRAW

/* For simplicity, we previously defined an instance field to hold the optr of the
 * chunk array we are using. We will lock down that data using that instance
 * field. CoffeeTableClass is a subclass of TableClass. */
@method CoffeeTableClass, MSG_TABLE_QUERY_DRAW
{
    char *data;
    word cArrayIndex, size;
    cArrayIndex = (location.TCL_row * TABLE_COLS) + location.TCL_column;
    MemLock(OptrToHandle(pself->CTI_chunkArray));
    data = ChunkArrayElementToPtr((pself->CTI_chunkArray),
                cArrayIndex, &size);
    GrDrawTextAtCP(gstate, data, 0);
    MemUnlock(OptrToHandle(pself->CTI_chunkArray);
}

The Table automatically sends MSG_TABLE_QUERY_DRAW when it knows a part of the Table may be inaccurate, e.g. when the user has edited a cell. You can also instruct the Table to redraw part or all of itself by sending it one of the MSG_TABLE_REDRAW... messages. These messages instruct the Table to send out appropriate MSG_TABLE_QUERY_DRAW messages for one or more visible cells. There are five MSG_TABLE_REDRAW... messages:

MSG_TABLE_REDRAW_TABLE
This instructs the Table to redraw all cells visible on-screen.
MSG_TABLE_REDRAW_ROW
This instructs the Table to redraw a specified row, if it's on-screen.
MSG_TABLE_REDRAW_COLUMN
This instructs the Table to redraw those cells in a specified column which are on-screen.
MSG_TABLE_REDRAW_CELL
This instructs the Table to redraw a specified cell, if it's on-screen.
MSG_TABLE_REDRAW_RANGE
This instructs the Table to redraw the portion of a specified range of cells that is visible on-screen.
MSG_TABLE_QUERY_DRAW
void	MSG_TABLE_QUERY_DRAW(
        TableCellLocation		location,
        GStateHandle		gstate);

The Table object sends itself this message whenever a cell needs to be redrawn. The default handler does nothing; when you use a Table object, you must subclass this message to draw the cell.

Source: A TableClass object.

Destination: The Table sends this message to itself.

Parameters: location The cell which needs to be redrawn.

gstate
A GStateHandle specifying a GState you should draw to. The GState's clipping region is set to the cell boundaries; the current position (CP) is the upper-left corner of the cell.

Return: Nothing. (You should not free the GState.)

Structures: TableCellLocation (see A TableCellLocation structure is used to specify a cell within the Table. It has the following definition: ).

Interception: This message must be intercepted; your handler must draw the cell's contents. You need not call the superclass's handler (which does nothing).

MSG_TABLE_REDRAW_TABLE
void	MSG_TABLE_REDRAW_TABLE();

This message instructs the Table to redraw the entire portion of the Table visible on-screen (creating a GState, sending out appropriate MSG_TABLE_QUERY_DRAW messages, etc.).

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: None.

Return: Nothing.

MSG_TABLE_REDRAW_ROW
void	MSG_TABLE_REDRAW_ROW(
        word	row);

This message instructs the Table to redraw a specified row, if it's visible on-screen (creating a GState, sending out appropriate MSG_TABLE_QUERY_DRAW messages, etc.).

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: row The index of the row to redraw. (The first row has index 0.)

Return: Nothing.

MSG_TABLE_REDRAW_COLUMN
void	MSG_TABLE_REDRAW_COLUMN(
        word	column);

This message instructs the Table to redraw those cells in a specified column that are visible on-screen (creating a GState, sending out appropriate MSG_TABLE_QUERY_DRAW messages, etc.).

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: column The index of the column to redraw. (The first column has index 0.)

Return: Nothing.

MSG_TABLE_REDRAW_CELL
void	MSG_TABLE_REDRAW_CELL(
        TableCellLocation		location);

This message instructs the Table to redraw a specified cell if it is visible on-screen (creating a GState, sending out appropriate MSG_TABLE_QUERY_DRAW messages, etc.).

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: location The cell to redraw.

Return: Nothing.

Structures: The cell is specified with a TableCellLocation structure (described on A TableCellLocation structure is used to specify a cell within the Table. It has the following definition: ).

MSG_TABLE_REDRAW_RANGE
void	MSG_TABLE_REDRAW_RANGE(
        TableCellRange		cellRange);

This message instructs the Table to redraw a range of cells (creating a GState, sending out appropriate MSG_TABLE_QUERY_DRAW messages, etc.).

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: cellRange The range of cells to redraw.

Return: Nothing.

Structures: The cell range to redraw is specified with a TableCellRange structure (described on A TableCellRange structure is used to specify a range of cells. It has the following definition: ).

The Table Objects: 4.2 Using a Table Object: Selecting Cells

TableCellLocation, CellRange, MSG_TABLE_SELECT, MSG_TABLE_GET_CURRENT_SELECTION, MSG_TABLE_SET_CURRENT_SELECTION. MSG_TABLE_SET_ROW_RANGE_SELECTION

You can configure under what circumstances a Table object will select a cell by setting the TableColumnFlags for the various columns in the table. The TableColumnFlags determine what will constitute a "selection event", and what cells will be selected. When a selection event occurs, the Table object will send itself MSG_TABLE_SELECT. This message passes two arguments:

  • A TableCellLocation structure (described below). This structure specifies which cell was selected. If more than one cell was selected, this will specify the cell where the selection action began .
  • A TableColumnFlags record. This record specifies two things. It specifies what pointer event or events caused the selection; and it specifies, in its TCF_TRIT field, which cells should be highlighted as selected.

A TableCellLocation structure is used to specify a cell within the Table . It has the following definition:

typedef struct {
    word	TCL_row;
    word	TCL_column;
} TableCellLocation;
TCL _row
This is the cell's row. (The first row is row number zero, as always.)
TCL _column
This is the cell's column. (The first column is column number zero, as always.)

The default handler for this message causes an area of the table to be highlighted. The area is determined by the TCF_TRIT field of the TableColumnFlags record passed with the message. If you wish, you may intercept the message before it is handled by the default handler, and change the TCF_TRIT field in the passed TableColumnFlags ; this will change which cells will be highlighted by the default handler. For example, if you change the TCF_TRIT field to contain TRIT_NONE, then the default handler will not highlight any rows.

You can find out what cells, if any, are currently selected by sending MSG_TABLE_GET_CURRENT_SELECTION to the Table object. This message takes one argument, a pointer to a TableCellRange structure. The handler will fill in this structure with the currently selected range.

A TableCellRange structure is used to specify a range of cells. It has the following definition:

typedef struct {
    TableCellLocation				TCR_start;
    TableCellLocation				TCR_end;
} TableCellRange;
TCR _start
This TableCellLocation specifies the first cell in the selected range. (The TableCellLocation structure is described on A TableCellLocation structure is used to specify a cell within the Table. It has the following definition: .)
TCR _end
This TableCellLocation specifies the last cell in the selected range. (Note that this cell may be higher up in the Table then TCR _start ; the same range is defined, whichever order TCR _start and TCR _end appear in.)

Note that a TableCellRange forms a rectangle. You can change the selected cells at any time by sending MSG_TABLE_SET_CURRENT_SELECTION . This message is passed one argument, a TableCellRange structure; that structure specifies what the current selection should be. The Table object responds by changing the current selection but does not send a MSG_TABLE_SELECT . To cancel the current selection (and leave the Table with nothing selected), send MSG_TABLE_SET_CURRENT_SELECTION , and put the constant T_NONE_SELECTED in the TCR _start. TCL _column field of the passed TableCellRange structure. (If you do this, the other fields of the TableCellRange will be ignored.) Again, MSG_TABLE_SELECT is not sent in this case.

MSG_TABLE_SELECT
void	MSG_TABLE_SELECT(
        TableCellLocation		location,
        TableColumnFlags		tableColumnFlags);

The Table object sends this message to itself when the user selects one or more cells with a pointer object. (I.e. this message is generated through use of the pointer for Table selection, not when the selection changes through other means.) The TableColumnFlags of the various columns determine whether a particular mouse action is interpreted as a selection. If it is, the Table object sends itself this message, instructing itself to highlight the appropriate cells.

Source: A TableClass object.

Destination: The Table object sends this message to itself.

Parameters: location This is the selected cell; that is, the cell in which the pointer was when the column's selection criteria were met.

tableColumnFlags
This is a record of TableColumnFlags . The flags specify two things: They specify what criteria were used to determine that a selection had happened; and they specify (in the TCF_TRIT field) which cells should be highlighted.

Return: Nothing.

Structures: TableCellLocation (see A TableCellLocation structure is used to specify a cell within the Table. It has the following definition: ) and TableColumnFlags (see The TableColumnFlags record has the following flags: ).

Interception: You may intercept this. If you intercept this message, you can change which cells will be highlighted by changing the tableColumnFlags. TCF_TRIT field.

MSG_TABLE_GET_CURRENT_SELECTION
void	MSG_TABLE_GET_CURRENT_SELECTION(
        TableCellRange *		cellRange);

This message retrieves what cells are currently selected within a Table object

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: cellRange A pointer to a TableCellRange structure.

Return: *cellRange will specify the first and last selected cells.

Structures: TableCellRange (described on A TableCellRange structure is used to specify a range of cells. It has the following definition: ).

MSG_TABLE_SET_CURRENT_SELECTION
void	MSG_TABLE_SET_CURRENT_SELECTION(
        TableCellRange		cellRange);

This message changes what cells are currently selected within a Table object.

Source: Unrestricted.

Destination: Any TableClass object.

Parameters: cellRange A pointer to a TableCellRange structure, specifying what cells should be selected. (If cellRange. TCR _start. TCL _column = T_NONE_SELECTED, no cells will be selected.)

Return: Nothing.

Structures: TableCellRange (described on A TableCellRange structure is used to specify a range of cells. It has the following definition: ).

The Table Objects: 4.3 Using a Table Object: Editing Cells

TableEditCellTextParams, MSG_TABLE_START_EDIT_CELL_TEXT, MSG_TABLE_DONE_EDIT_CELL_TEXT, MSG_TABLE_STOP_CELL_EDIT

The Table objects make it easy for the user to edit cells. If the column has TCF_DOUBLE_SELECT enabled, a user can edit a cell in the column just by double-clicking on it. When the user does this, the Table object will take the following steps:

  1. First, it sends itself MSG_TABLE_START_EDIT_CELL_TEXT . The default handler causes the Table object to create a VisText under the TableContent which the user can enter text in.
  2. When the user has finished entering the text, and closes the VisText (by pressing ENTER ) , the Table object sends itself MSG_TABLE_DONE_EDIT_CELL_TEXT , passing along the text that the user entered in the VisText . The application should store the text wherever it stores the Table 's data.
    If the user finishes editing by clicking in another cell, a MSG_TABLE_STOP_CELL_EDIT is generated and the edit is cancelled. (If your program does not wish to cancel the edit under these circumstances, then intercept the message.)
  3. Finally, the Table sends itself a MSG_TABLE_QUERY_DRAW for the cell which was edited. The application should intercept this message and redraw the cell normally.

Both MSG_TABLE_START_EDIT_CELL_TEXT and MSG_TABLE_DONE_EDIT_CELL_TEXT use a special structure, the TableEditCellTextParams structure; the structure's fields have slightly different meanings for each message. This structure has the following definition:

typedef struct {
    TableCellRange			TECT_cells;
    MemHandle			TECT_text;
    word			TECT_length;
} TableEditCellTextParams;
TECT _cells
This is a TableCellRange structure (described on A TableCellRange structure is used to specify a range of cells. It has the following definition: ), specifying what cells have been selected for editing. If a single cell has been selected for editing, TCR _start will be the same as TCR _end .
TECT _text
This is the handle of a global memory block, containing the text in the cell.
TECT _length
This is the length of the string in TECT _text , or zero if the string is null-terminated.

MSG_TABLE_START_EDIT_CELL_TEXT creates a VisText object (within the TableContent and over the cell to be edited) to allow the user to edit the cell's text. This message passes a set of TableEditCellTextParams containing the cell range being edited (TECT _cells ) and the initial text to display to the user (if any) in TECT _text and TECT _length . This text object, which may be referenced via TI_textObj , can persist between edits. Thus, if you change its instance data, be aware that these changes may or may not linger in future edits. You may wish to update any changes in your MSG_TABLE_START_EDIT_CELL_TEXT handler.

The default handler clears both TECT _text and TECT _length . Your application may subclass the message, and fill in these fields with default text for the VisText (e.g., you may want to retrieve the text currently in the specified cell and display that to be edited). If TECT _text contains a null handle, the VisText will start out blank.

To provide this default text, allocate a global memory block, copy the string into the beginning of the block, and put the block's handle in TECT _text . (You should not store anything else in this block, because the block will be freed by the handler for MSG_TABLE_DONE_EDIT_CELL_TEXT.) If a valid handle was not passed in TECT _text , it allocates a global memory block for the VisText to write to; otherwise, the VisText will simply write to the block specified in TECT _text (overwriting the original text).

Code Display 5-3 Handling MSG_TABLE_START_EDIT_CELL_TEXT

@method CoffeeTableClass, MSG_TABLE_START_EDIT_CELL_TEXT
{
    char *cellData, *textPtr;
    word cArrayIndex, size;
    cArrayIndex = (params.TECT_cells.TCR_start.TCL_row * TABLE_COLS) +
        params.TECT_cells.TCR_start.TCL_column;
    /* The superclass will free this block that we allocate here. */
    params.TECT_text = MemAlloc(CELL_DATA_LENGTH, HF_SWAPABLE, HAF_LOCK);
    textPtr = MemDeref(params.TECT_text);
    /* Retrieve the current data from the cell and stuff it in the memory block. */
    pself = ObjDerefVis(oself);
    MemLock(OptrToHandle(pself->CTI_chunkArray);
    cellData = ChunkArrayElementToPtr((pself->CTI_chunkArray),
                cArrayIndex, &size);
    strcpy(textPtr, cellData);
    MemUnlock(OptrToHandle(pself->CTI_chunkArray));
    params.TECT_length = 0;				/* Zero indicates null-termination. */
    @callsuper();
}

When the user has finished entering text, the VisText object sends the Table object MSG_TABLE_DONE_EDIT_CELL_TEXT. This message takes one argument, a TableEditCellTextParams structure. The TECT _text field will contain the handle of a global memory block, containing the text the user has entered.The application should intercept the message, so it can store the new text; afterwards, it should call the default handler, which will free the memory block specified by TECT _text and perform other bookkeeping chores. The Table object will automatically send an appropriate MSG_TABLE_QUERY_DRAW , so the cell will be redrawn with the new contents.

Note that the Table object supports allowing the user to edit several cells at once. If you choose to allow this, you must decide what the functionality is; that is, in your handler for MSG_TABLE_START_EDIT_CELL_TEXT, you must decide what cell's text to display in the VisText (e.g. the first cell's text, or a composite of all the selected cells's text); and in your handler for MSG_TABLE_DONE_EDIT_CELL_TEXT, you must decide where and how to store the entered text (e.g. whether to overwrite all the edited cells with the same text, divide the entered text amongst the cells, etc.).

Code Display 5-4 Handling MSG_TABLE_DONE_EDIT_CELL_TEXT

@method CoffeeTableClass, MSG_TABLE_DONE_EDIT_CELL_TEXT
{
    char *cellData, *textPtr;
    word cArrayIndex, size;
    cArrayIndex = (params.TECT_cells.TCR_start.TCL_row * TABLE_COLS) +
        params.TECT_cells.TCR_start.TCL_column;
    textPtr = MemLock(params.TECT_text);
    /* Make sure we're null terminated ... */
    textPtr[CELL_DATA_LENGTH -1]; = (char)0;
    /* Lock down the cell and copy the string to it. */
    MemLock(OptrToHandle(pself->CTI_chunkArray));
    cellData = ChunkArrayElementToPtr((pself->CTI_chunkArray),
        cArrayIndex, &size);
    strcpy(cellData, textPtr);
    MemUnlock(OptrToHandle(pself->CTI_chunkArray));
    @callsuper();
}
MSG_TABLE_START_EDIT_CELL_TEXT
void	MSG_TABLE_START_EDIT_CELL_TEXT(
        TableEditCellTextParams			params);

The Table object sends itself this message when the user starts editing a cell. The default handler brings up a VisText for the user to enter text. You should intercept this message, so you can look up and provide the cell's current contents; that way, the contents will be put in the VisText for the user to edit.

Source: Any Table object.

Destination: The Table sends this message to itself.

Parameters: params. TECT _cells
 This field specifies the cell or cells the user is editing.

params. TECT _text
This field contains the handle of a global memory block, containing the text to be displayed in the VisText when it is brought up. When this message is sent, params. TECT _text contains a null handle; your subclass's handler may allocate a global memory block, copy the text into that block, and write the handle here. (Note that the VisText will write to the block you specify, so it should not contain any other data.)
params. TECT _length
This is the length (in bytes) of the string in the block specified by params. TECT _text . If this field is zero, the string is null-terminated.

Return: Nothing. (If params. TECT _text contained a null handle, the default handler will allocate a block.)

Structures: TableEditCellTextParams (see Both MSG_TABLE_START_EDIT_CELL_TEXT and MSG_TABLE_DONE_EDIT_CELL_TEXT use a special structure, the TableEditCellTextParams structure; the structure's fields have slightly different meanings for each message. This structure has the following defi ).

Interception: You should intercept this message to provide the text for the VisText to present for editing (which will usually be the current contents of the cell being edited). After this, you should call the default handler with @callsuper , so the VisText can be created.

Warnings: If you put a memory handle in params. TECT _text , that block will be written to by the VisText , and freed by the handler for MSG_TABLE_DONE_EDIT_CELL_TEXT; so make sure it doesn't contain any other data.

MSG_TABLE_DONE_EDIT_CELL_TEXT
void	MSG_TABLE_DONE_EDIT_CELL_TEXT(
        TableEditCellTextParams				params);

The VisText object sends this message to the Table object after the user has finished entering data. The application should intercept this message to copy data into its storage location; you should also make sure to call the default handler, which will remove the VisText and perform other bookkeeping chores.

Source: This message is sent by a VisText object used by the Table for text editing.

Destination: The VisText sends this message to its parent Table .

Parameters: params. TECT _cells
 This field specifies the cell or cells the user has edited.

params. TECT _text
This field contains the handle of a global memory block, containing the text the user entered in the VisText . The default handler will free this block.
params. TECT _length
This is the length (in bytes) of the string in the block specified by params. TECT _text . If this field is zero, the string is null-terminated.

Return: Nothing. (The default handler will free the block specified by params. TECT _text .)

Structures: TableEditCellTextParams (see Both MSG_TABLE_START_EDIT_CELL_TEXT and MSG_TABLE_DONE_EDIT_CELL_TEXT use a special structure, the TableEditCellTextParams structure; the structure's fields have slightly different meanings for each message. This structure has the following defi ).

Interception: You should intercept this message, so you can copy the new data into your application's storage. Afterwards, you should make sure to call the default handler with @callsuper .

MSG_TABLE_STOP_EDIT_CELL_TEXT
void	MSG_TABLE_STOP_EDIT_CELL_TEXT();

The VisText object sends this message to the Table if the user clicks outside the cell while editing data. This cancels the edit: the Table returns to its normal mode and the user's edits are discarded.

Source: This message is sent by a VisText object used by the Table for text editing.

Destination: The VisText sends this message to its parent Table .

Parameters: None.

Return: Nothing.

Interception: If your program wants to treat a tap outside the edited cell as a signal that the edit is finished, not cancelled, then get the (abandoned) text that the user was editing from the VisText child of the Table. The chunk handle of this VisText child is stored in TI_textObj. You can build an optr to the VisText by using:

textObj = ConstructOptr(OptrToHandle(@YourTable),
                            pself->TI_textObj);

To retrieve the text, use:

@call textObj::MSG_VIS_TEXT_GET_ALL_PTR(buffer);

where "buffer" has been declared as "char buffer[]".

Once you've retrieved the text, the remainder of your method should just save away this data in whatever data structure you've built to hold your table's data -- just like in the method for MSG_TABLE_DONE_EDIT_CELL_TEXT .

The Table Objects: 4.4 Using a Table Object: Dragging and Dropping

TableDragDropCompleteParams, TableDragDropCell, TableDragDropFlags, MSG_TABLE_DRAG_DROP_COMPLETE

The Table object supports the standard GEOS drag-drop functionality. It allows the user to select one or more cells, then drag their contents to other cells in the Table . However, the Table object does not actually keep track of the contents of the cells. Instead, it detects which cells the user dragged and where the user dropped them, and it notifies the application with MSG_TABLE_DRAG_DROP_COMPLETE . The application should intercept this message and use this information to copy data to its appropriate places in its storage.

The Table is put in drag-and-drop mode when the TableFlags bit TF_INTERNAL_DRAG_DROP is set (for example through the sending of MSG_TABLE_SET_FLAGS ). If the Table's column flags contain TCF_HOLD_SELECT, holding for the specified period of time to initiate a "hold" will also set the TF_INTERNAL_DRAG_DROP flag. When the Table is in drag-and-drop mode, the user can copy a cell's contents to another cell by clicking on one cell, dragging the pointer to the other cell, and releasing. If the TableFlags bit TF_EXIT_DRAG_DROP_UPON_COMPLETION is set, the Table will automatically clear TF_INTERNAL_DRAG_DROP after every drag-and-drop.

The Table object sends MSG_TABLE_DRAG_DROP_COMPLETE when the user finishes a drag-and-drop operation. This message passes one argument, a TableDragDropCompleteParams structure. This structure has the following definition:

typedef struct {
    TableDragDropCell				TDDCP_dragDrop;
    TableDragDropFlags				TDDCP_flags;
} TableDragDropCompleteParams;
TDDCP _dragDrop
This structure specifies which cells are being copied, and where they will be copied to. The TableDragDropCell structure is described below.
TDDCP _flags
This field is a record of TableDragDropFlags flags, described below.

The cells being dragged, and the location to which they are being dropped, are specified with a TableDragDropCell structure. This structure has the following definition:

typedef struct {
    TableCellRange			TDDC_from;
    TableCellRange			TDDC_to;
} TableDragDropCell;
TDDC _from
The cell or cells being dragged.
TDDC _to
The cell or cells on which TDDC _from is being dropped.

When MSG_TABLE_DRAG_DROP_COMPLETE is sent, the TDDCP _flags field of the passed TableDragDropCompleteParams is blank. If you intercept this message, you can change this field, setting any TableDragDropFlags bits in this field. Currently, only one flag is defined:

TDDF_DONT_RESCAN_CELLS
If this flag is clear, the Table will redraw all visible cells after a drag-drop operation. If it is set, it will not redraw any cells. (You may wish to set this flag, then force redrawing of a few affected cells with an appropriate MSG_TABLE_REDRAW... message; these messages are described on The Table automatically sends MSG_TABLE_QUERY_DRAW when it knows a part of the Table may be inaccurate, e.g. when the user has edited a cell. You can also instruct the Table to redraw part or all of itself by sending it one of the MSG_TABLE_REDR .)

You should intercept the message, and perform any appropriate data operations, e.g. copying the data from the "dragged" cell to the location where it was "dropped".

If the TF_EXIT_DRAG_DROP_UPON_COMPLETION flag is set, drag and drop mode will be exited upon receipt of MSG_TABLE_DRAG_DROP_COMPLETE . Otherwise, you will need to determine when you wish to exit drag and drop mode and clear the TF_INTERNAL_DRAG_DROP flag manually by sending the Table MSG_TABLE_SET_FLAGS . (You might wish to do this if you want to drag and drop to multiple locations, for example.) Because you are modifying an internal flag in this process, this approach is highly discouraged.

Code Display 5-5 Implementing Drag and Drop Mode

/* This message is sent to the Table whenever it completes a drag and drop
 * operation (by releasing the pen). In your handler, determine the starting and
 * ending point of the operation and copy the data from one location to the other.
 */
@method CoffeeTableClass, MSG_TABLE_DRAG_DROP_COMPLETE
{
    char *fromData, *toData;
    word fromIndex, toIndex, size;
    /* We are storing our data in a chunk array (not shown). We obtain a
     * linear index into this data based on the cell row and column. */
    fromIndex =
    (cellFromTo.TDDCP_dragDrop.TDDC_from.TCR_start.TCL_row * TABLE_COLS) +
    cellFromTo.TDDCP_dragDrop.TDDC_from.TCR_start.TCL_column;
    toIndex =
    (cellFromTo.TDDCP_dragDrop.TDDC_to.TCR_start.TCL_row * TABLE_COLS) +
    cellFromTo.TDDCP_dragDrop.TDDC_to.TCR_start.TCL_column;
    /* Check if this drag and drop is from within the same Table. */
    if (pself->TI_tableFlags & TF_INTERNAL_DRAG_DROP) {
    MemLock(OptrToHandle(pself->CTI_chunkArray);
    fromData = ChunkArrayElementToPtr((pself->CTI_chunkArray),
                    fromIndex, &size);
    toData = ChunkArrayElementToPtr((pself->CTI_chunkArray),
                    toIndex, &Size);
        /* Copy the actual data. *
    strcpy(toData, fromData);
    MemUnlock(OptrToHandle(pself->CTI_chunkArray));
    }
    /* Call the superclass. This will also exit the Table from drag and drop mode
     * if the TF_EXIT_DRAG_DROP_UPON_COMPLETION flag is set. */
    @callsuper();
}
MSG_TABLE_DRAG_DROP_COMPLETE
void	MSG_TABLE_DRAG_DROP_COMPLETE(
        TableDragDropCompleteParams					cellFromTo);

The Table object sends this message to itself when the user has finished performing a drag-and-drop operation. Application writers who wish to perform operations on the data being dragged and dropped must intercept this message.

Source: A TableClass object.

Destination: The Table object sends this message to itself.

Parameters: cellFromTo. TDDCP _dragDrop
 This field specifies what cells are being dragged, and where they are being dropped.

cellFromTo.TDDCP_flags
This field is initially blank. If you intercept the message, you can change the flags before calling the default handler.

Return: Nothing.

Structures: The arguments are passed in a TableDragDropCompleteParams structure (described on The Table object sends MSG_TABLE_DRAG_DROP_COMPLETE when the user finishes a drag-and-drop operation. This message passes one argument, a TableDragDropCompleteParams structure. This structure has the following definition: ).

Interception: Must intercept to perform data operations necessary to implement the drag-and-drop. Afterwards, make sure to call the default handler with @callsuper . The default handler will cause the entire visible portion of the Table to be redrawn, unless you have set the TDDF_DONT_RESCAN_CELLS bit in cellFromTo.TDDCP_flags .

The Table Objects: 4.5 Using a Table Object: Custom Scrolling Behavior

MSG_TABLE_SCROLL, MSG_TABLE_SCROLL_TO_ROW, MSG_TABLE_SCROLL_SINGLE_UP, MSG_TABLE_SCROLL_SINGLE_DOWN

Auto-scrolling (enabled by adding the TF_ENABLE_AUTO_SCROLLING to TI_ tableFlags ) allows the user to scroll within a Table by clicking within a Table and dragging above or below the current Table bounds. The Table will scroll in the direction that the pen was dragged. Auto-scrolling does not provide any UI to scroll the Table, however. If you want scroll buttons, for example, you will need to take the following steps:

  • Make sure you've declared a TableViewClass object. Set its TVI_mainTable field to the Table you wish to scroll.
  • Mark the Table that you want scrolling to occur with the TF_MAIN_TABLE flag.

MSG_TABLE_SCROLL is a generic scrolling message that implements a particular scroll operation (passed in the message's TableScrollType parameter). Possible Table scroll types are:

TST_SCROLL_PAGE_UP
TST_SCROLL_PAGE_DOWN
TST_SCROLL_UP
TST_SCROLL_DOWN
TST_SCROLL_TOP
TST_SCROLL_DOWN

TST_SCROLL_UP scrolls the Table up one row. Similarly, TST_SCROLL_DOWN scrolls the Table down one row.

You may send MSG_TABLE_SCROLL_TO_ROW if you want the Table to scroll to a particular (zero-based) row. The Table will scroll so as to make that row the first visible row, if possible. MSG_TABLE_SCROLL_SINGLE_UP scrolls the Table up one row. Similarly, MSG_TABLE_SCROLL_SINGLE_DOWN scrolls the Table down one row.

The Table Objects: 4.6 Using a Table Object: Handling Locator Searches

MSG_TABLE_CHAR_LOCATE, MSG_TABLE_STRING_LOCATE

The Table object may wish to respond to searches on its data. For this purpose, the Table object is designed to interact with a LocatorClass object; the Locator object is a UI object that the user uses to initiate searches on the Table. The Locator object implements this search by sending the Table object one of two messages. You must intercept these messages to provide searching behavior. TableClass provides no default searching behavior.

The Locator object is discussed in detail in LocatorClass ; however, some background on how the Locator works is illustrative to providing the Table`s searching behavior.

The Locator object can be in one of two modes: index mode or text search mode. The Locator object will appear and act differently depending on which mode it is in.

When the Locator object is in index mode, it takes the form of an action bar, shown below.

Tapping on one of the Locator's dyad buttons (`AB', `EF', etc.) will send the Table object MSG_TABLE_CHAR_LOCATE , passing it the letter to search. Repeatedly clicking on a dyad button will cycle through the two letters to perform the search. For example, clicking once on the `AB' dyad button will send the Table MSG_TABLE_CHAR_LOCATE with the character `A.' Clicking again on that button will send the Table MSG_TABLE_CHAR_LOCATE with the character `B.' Clicking yet again on that button will send the Table MSG_TABLE_CHAR_LOCATE with the character `C.'

If you have a Locator linked to your Table, you will want to intercept MSG_TABLE_CHAR_LOCATE to make sure that you can implement searching behavior in index mode.

When the Locator object is in text search mode, the action bar "morphs" into a text search entry field shown below.

Entering text into the text search field of the Locator will send the linked Table object MSG_TABLE_STRING_LOCATE after each keystroke, passing it the current contents of the text entry field. You will want to intercept this message to provide your searching method for when the Locator object is in text mode.

You may find it convenient to write a generic searching routine that your message handlers for both MSG_TABLE_CHAR_LOCATE and MSG_TABLE_STRING_LOCATE may call. This searching routine will likely want to perform a case-insensitive search on your Table. You may want to use a routine such as LocalCmpStringsNoCase() for this purpose. Note that you must still provide separate message handlers for each of these messages even if you provide a generic searching routine.

Code Display 5-6 Implementing a Search

/* This code implements a single character search initiated by a Locator object. */
@method MyTableClass, MSG_TABLE_CHAR_LOCATE
{
    char *data;			/* Pointer to the table data array. */
    word index =0;			/* A linear index into the Table data array. */
    /* We'll want to send MSG_TABLE_SET_CURRENT_SELECTION, so we'll want
     * to pass a TableCellRange.
     */
    TableCellRange	selection;
    Boolean searchResult;			/* Reports the success or failure of the search. */
    /* We initialize the selection to start searching from the beginning of the
     * Table. */
    selection.TCR_start.TCL_row					= 0;
    selection.TCR_start.TCL_column					= 0;
    selection.TCR_end.TCL_row					= 0;
    selection.TCR_end.TCL_column					= 0;
    /* Lock down our data block. */
    MemLock(OptrToHandle(@cellDataArray));
    /* Perform a case-insensitive search on the data in the array. We do this by
     * searching linearly, multiplying by the total number of columns in the
     * Table, so that we only search the first column. (We could have also
     * performed our search cell by cell in a linear fashion; in that case we
     * would not have multiplied the index by TABLE_COLS in the `step' section of
     * the while statement.) The data in this case is assumed to be stored in a      * linear chunk array. */
    do {
    data = ChunkArrayElementToPtr(@cellDataArray, index, 1);
    } while(
    (LocalCmpStringsNoCase(data, searchText, 1)) &&
    ((++index * TABLE_COLS) < (TABLE_COLS * TABLEROWS)));
    /* We've finished searching; we can free the data block. */
    MemUnlock(OptrToHandle(@cellDataArray));
    /* Determine if search found a match. */
    if (index >= (TABLE_COLS * TABLE_ROWS))
    {
    searchResult = TRUE;			/* Indicates no match was found. */
    }
    else
    {
    /* If successful, set selection to "current" index cell. */
    selection->TCR_start.TCL_row 				= index / TABLE_COLS;
    selection->TCR_start.TCL_column 				= index % TABLE_COLS;
    selection->TCR_end.TCL_row				= selection->TCR_start.TCL_row;
    selection->TCR_end.TCL_column				= selection->TCR_start.TCL_column;
    @call self::MSG_TABLE_SET_CURRENT_SELECTION(selection);
    searchResult = FALSE;			/* Indicates that we found a match. */
    }
    return(searchResult);				/* We don't call the superclass. Default behavior
                 * is to return TRUE indicating no matches were
                 * found. */
}
MSG_TABLE_CHAR_LOCATE
Boolean	MSG_TABLE_CHAR_LOCATE(
        char		searchChar);

This message is sent to the TableClass object by the Locator object instructs a Locator object (in index mode) to locate the entered character within the TableClass object that is in the Locator's LI_ destination field. It does this by sending the Table object MSG_TABLE_CHAR_LOCATE , passing it the search character. You will want to intercept that message within your subclass of TableClass to implement your search criteria.

When in index mode, this message is sent with a single index letter (e.g. `A', or `B' in the "AB" dyad) of the tapped dyad button. Repeatedly tapping on a dyad button will cycle through the indexed letters, sending out this message a separate time for each tap (first with `A,' then with `B,' and then with `A' again). For this reason, make sure that code that implements this message is not run under the UI thread. (Doing so may result in bothersome screen "glitches" as the Table scrolls first to the `A' entries, then to the `B' entries, etc.

Source: The Locator object.

Destination: The Locator's linked Table object.

Parameters: searchChar Character to search. Passed from one of the Locator's dyad buttons.

Return: Non-zero if the search failed. Note that if you do not write a search method for TableClass , this message will always return non-zero.

MSG_TABLE_STRING_LOCATE
Boolean	MSG_TABLE_STRING_LOCATE(
        char		*text);

This message instructs a Locator object (in text mode) to locate the entered string within the TableClass object that is in the Locator's LI_ destination field. It does this by sending the Table object MSG_TABLE_STRING_LOCATE , passing it the pointer to the search string. You will want to intercept that message within your subclass of TableClass to implement your search criteria.

The Locator object enters text search mode whenever the user types a printable character on the keyboard; in that case, the locateString contains the character typed and a search (with this message) is performed. I.e. a search is performed after each successive character is entered. If the search is unsuccessful, the character will not appear.

Source: The Locator object.

Destination: A Locator object.

Parameters: text Pointer to the null-terminated search string to pass to MSG_TABLE_STRING_LOCATE .

Return: Non-zero if the search failed. Note that if you do not write a search method for TableClass, this message will always return non-zero.

The Table Objects: 4.7 Using a Table Object: Table Headings

TableClass does not automatically support the drawing of Table headings. However, it is easy to implement this yourself.

To create a Table with headings, simply create two different Table objects: one for the body of the Table , and one for the headings. Make sure the two Tables have the same left and right margins, and make sure their columns are of the same width; but other than that, you can set up the two tables completely differently. For example, the upper Table (which would have only one line) might well not be user-editable or selectable.

Group the two Tables below the single TableContent object. Once you've set up the two Tables, you may want to experiment with headings and borders to get the look you want. Notice that the two Table s scroll independently. This means that when a user scrolls the lower Table , the same headings will always be visible at the top of the columns. You will want to add the TF_MAIN_TABLE flag to your main Table object so that it does not attempt to scroll the heading table.

Code Display 5-7 Adding a Header Table

/* A Header is simply a Table, though it is usually only one row. */
@object CoffeeTableClass, CoffeeTableHeader = {
    TI_rows = 1;			/* Because this is a header, only one row is needed. */
    TI_columns = MY_TABLE_COLS;				/* Make sure you have the same number of columns
                 * as in the main table. */
    TI_visibleRows = 1;
    TI_rowHeight = 18;			/* Your row height may be different than the row height
             * of the main table. */
    TI_columnDefinitions = @CoffeeTableHeaderColumnDefs;
    TI_borderFlags = TBF_BOX_BORDERS;
    CTI_chunkArray = @CoffeeTableHeaderDataArray;
}
/* The column definition widths should match those of the main table. Headers
 * should not contain any TableColumnFlags, however. Set these to zero. */
@chunk TableColumnDefinition CoffeeTableHeaderColumnDefs[] = {
    0, 25,
    0, 100,
    0, 45,
    0, 70};
/* The Table header data array contains the strings to display in the Table
 * header. */
@chunkArray CellDataStruct CoffeeTableHeaderDataArray = {
    "#","Description","Type","$/lb."
};

The Table Objects: 4.8 Using a Table Object: Changing Column Definitions

You may wish in exceptional circumstances to display different columns than the ones you statically define using TI_ columnDefinitions . In that case, you may want to define alternate column definitions and switch which ones you use at appropriate times in your application. There is a fair amount of overhead in doing this yourself--there currently exists no API to take care of this behavior--so you should make sure you want to take that approach.

The following steps are recommended to get this behavior:

  • Define separate column definitions for each group of columns you want displayed. Each column definition should reside in its own separate chunk. Make sure that each set of column definitions resides within the resource block of the table object. (Because TI_columnDefinitions is a chunk handle, it cannot reference chunks outside of the resource.)
  • Create an instance field in your Table subclass to index which column definitions you are currently using. You will use this instance field to determine what chunk array of data to use.
  • Create separate chunk arrays to hold your individual sets of data and an instance field to hold the correct pointer to that chunk array. Alternatively, you could have one chunk array hold both sets of data and define an offset into the second set of data.
  • Define a message in your Table subclass to change the column definitions, passing it an index number of the column definitions you want to use. Set TI_ columnDefinitions to the appropriate column definitions in your handler for this message. Also change your index number to indicate the new column definitions being used and the pointer to the chunk array of data.
  • Redraw the Table by sending MSG_TABLE_REDRAW_TABLE from this handler. This will dispatch MSG_TABLE_QUERY_DRAW to each of the affected cells.
  • Handle MSG_TABLE_QUERY_DRAW as usual.

Code Display 5-8 Dynamically Changing Column Definitions

/* First, make sure you create a message to change the column definitions and an
 * instance field to indicate which column definitions we are using. */
@class MyTableClass, TableClass;
    @message void MSG_MY_TABLE_CHANGE_COLUMN_DEFINITIONS(int columnIndex);
    @instance int 			MTI_columnIndex = 0;
    @instance optr			MTI_chunkArray = NullOptr;
@endc;
@classdecl MyTableClass;
/* Define your column definitions. Make sure that each set of column definitions
 * adds up to 240 pixels across. Note that if you want headers for each of these
 * column definitions, you will need to handle that yourself.*/
#define MY_COLUMN_FLAGS (TCF_START_SELECT | TCF_DOUBLE_SELECT | TCF_DRAG_SELECT |
            TCF_HOLD_SELECT | TRIT_CELL )
@chunk TableColumnDefinition ColumnSet1[] = {
    MY_COLUMN_FLAGS, 25,
    MY_COLUMN_FLAGS, 100,
    MY_COLUMN_FLAGS, 45,
    MY_COLUMN_FLAGS, 70};
@chunk TableColumnDefinition ColumnSet2[] = {
    MY_COLUMN_FLAGS, 25,
    MY_COLUMN_FLAGS, 100,
    MY_COLUMN_FLAGS, 115};
/* Define our cell data in two separate chunk arrays. */
@chunkArray CellDataStruct myMainData = {
    "0","Viennese","DARK", "$ 8.75",
    "1","Italian","DARK","$ 8.75",
    "2","French","DARK","$ 9.25",
    "3","Sumatra","RICH","$ 8.95"};
@chunkArray CellDataStruct mySecondaryData = {
    "0","Mr. Cringle","1412 Dowdy Ln.",
    "1","Ms. Holworth","23 Abercrombie",
    "2","Jeeves","10 Downing St.",
    "3","Kahlia","345 Cedar St."};
/* Define your method for MSG_MY_TABLE_CHANGE_COLUMN_DEFINITIONS. */
@method MyTableClass, MSG_MY_TABLE_CHANGE_COLUMN_DEFINITIONS
{
    switch(columnIndex) {
    case COLUMN_SET_1:
        pself->TI_columnDefinitions = OptrToHandle(@ColumnSet1);
        pself->MTI_chunkArray = @myMaindata;
        break;
    case COLUMN_SET_2:
        pself->TI_columnDefinitions = OptrToHandle(@ColumnSet2);
        pself->MTI_chunkArray = @mySecondaryData;
        break;
    default:
        break;
    }
    pself->MTI_columnIndex = columnIndex;
    @call self::MSG_TABLE_REDRAW_TABLE;
}
/* Handle MSG_TABLE_QUERY_DRAW. Because we set up the MTI_chunkArray instance
 * field in MSG_MY_TABLE_CHANGE_COLUMN_DEFINITIONS, nothing special is done here.
 */
@method MyTableClass, MSG_TABLE_QUERY_DRAW
{
    char *data;
    word cArrayIndex, offset, size;
    cArrayIndex = (location.TCL_row * TABLE_COLS) + location.TCL_column;
    MemLock(OptrToHandle(pself->MTI_chunkArray));
    data = ChunkArrayElementToPtr((pself->MTI_chunkArray),
                cArrayIndex, &size);
    GrDrawTextAtCP(gstate, data, 0);
    MemUnlock(OptrToHandle(pself->MTI_chunkArray);
}

The Table Objects: 5 TableContentClass

A TableContentClass object manages TableClass objects. The TableContent is a visual grouping object (subclassed off of VisContentClass ) and therefore manages the visual implementation of its children. A TableContent object will contain just two types of children: usually a single TableClass object, possibly an additional TableClass object acting as a set of headings, and a LocatorClass object. ( LocatorClass is discussed later in the chapter.)

The TableContent object manages the dispatch of keyboard events to its Table anTableView object, and acts as the bridge between the generic and visual object hierarchies. The hierarchy of this arrangement is shown below:

TableContent Instance Data

Altering TableContent Instance Data

The Table Objects: 5.1 TableContentClass: TableContent Instance Data

TableContentClass is a subclass of VisContentClass . As such, it inherits instance data and messages from that class. This section describes the instance data fields defined for TableContentClass . It also describes how to set, examine and change those fields which are of interest to developers.

Code Display 5-9 TableContentClass instance data

/*
 * TableContentClass is a subclass of VisContentClass.
 */
@default 		VCI_geoAttrs = 		(@default | VCGA_ORIENT_CHILDREN_VERTICALLY);
@instance 		byte		TCI_kbdSend = 255;
@instance 		optr		TCI_exclDestination = NullOptr;
@instance		word		TCI_childSpacing = 0;
@instance 		word		TCI_childWrapSpacing = 0;

TCI_ kbdSend is an internal field that determines whether keyboard messages will be sent to the TableContent's complement of children. (This is the default behavior.) If you want to send any keyboard events to any of the TableContent's children, this flag must be "on." You should not set or modify this flag directly; it should only be altered by sending the TableContent object MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_ON and MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_OFF.

TCI_ exclDestination is an internal field that determines which specific child object is sent keyboard messages. If TCI_ exclDestination is non-null, then keyboard messages will not be broadcast to all of the TableContent's children, but only to that specific object. This destination should only be altered by sending the TableContent MSG_TABLE_CONTENT_GRAB_KBD_EXCLUSIVE or MSG_TABLE_CONTENT_RELEASE_KBD_EXCLUSIVE (to set the output to null); the field should not be set or modified directly.

TCI_ childSpacing sets the child spacing (in points) for the Table and Locator children of this TableContent object. You should not need to manipulate this field.

TCI_ childWrapSpacing sets the child spacing for children that are forced to wrap (in points) for the children of the TableContent object. You should not need to manipulate this field.

The Table Objects: 5.2 TableContentClass: Altering TableContent Instance Data

MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_ON, MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_OFF, MSG_TABLE_CONTENT_GRAB_KBD_EXCLUSIVE, MSG_TABLE_CONTENT_RELEASE_KBD_EXCLUSIVE

By default, the TableContent broadcasts keyboard events to its children. If this behavior is not desired, you can send the TableContent MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_OFF . Sending the TableContent MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_ON turns keyboard event broadcasting back on.

Keyboard event broadcasting must be on for any child object to receive any keyboard events, even if a child object attempts to grabs the keyboard exclusive on its own. A TableContent's child can grab the keyboard exclusive by sending MSG_TABLE_CONTENT_GRAB_KBD_EXCLUSIVE to its parent TableContent object, passing it the optr of the child object (usually itself) which should be given the keyboard exclusive. If keyboard event broadcasting is not on, or if another child object already has the exclusive, the grab will not be successful.

The child object should also make sure to send its parent MSG_TABLE_CONTENT_RELEASE_KBD_EXCLUSIVE when it can release the keyboard exclusive.

MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_ON
Boolean	MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_ON();

This message instructs the TableContent object to allow keyboard messages to be sent to its children. (This must be enabled if any child object is to grab the keyboard exclusive.) It does this by modifying the TableContent's TCI_ kbdSend instance field. Do not modify this field yourself.

Source: Unrestricted.

Destination: A TableContent object.

Parameters: None.

Return: true (non-zero ) if the TableContent object was not able to turn on keyboard dispatching to its children; false (zero) otherwise.

MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_OFF
Boolean	MSG_TABLE_CONTENT_KBD_SEND_TO_CHILDREN_OFF();

This message instructs the TableContent object to disallow keyboard messages to be sent to its children. (If any child object currently has the keyboard exclusive, this message will not succeed in turning off keyboard event broadcasting.) The TableContent modifies this behavior by modifying the TableContent's TCI_ kbdSend instance field. Do not modify this field yourself.

Source: Unrestricted.

Destination: A TableContent object.

Parameters: None.

Return: true (non-zero) if the TableContent object was not able to turn off keyboard dispatching to its children; false (zero) otherwise.

MSG_TABLE_CONTENT_GRAB_KBD_EXCLUSIVE
Boolean	MSG_TABLE_CONTENT_GRAB_KBD_EXCLUSIVE(
        optr		obj);

This message instructs the TableContent object to give the keyboard grab to the child object whose optr is passed in the message. It does this by modifying the TableContent's TCI_ exclDestination modify this field yourself. Keyboard event broadcasting must be "on" for this grab to succeed.

Source: One of the TableContent's children

Destination: A TableContent object.

Parameters: obj Object to receive the TableContent's keyboard exclusive.

Return: true (non-zero) if the child object was not able to grab the TableContent's keyboard exclusive; false (zero) otherwise.

MSG_TABLE_CONTENT_RELEASE_KBD_EXCLUSIVE
Boolean	MSG_TABLE_CONTENT_RELEASE_KBD_EXCLUSIVE(
        optr		obj);

This message instructs the TableContent object to release the keyboard grab from the child object whose optr is passed in the message. Only a child object which currently has the grab can release it. It does this by modifying the TableContent's TCI _exclDestination instance field, setting it to null. Do not modify this field yourself.

Source: Unrestricted.

Destination: A TableContent object.

Parameters: obj Object releasing the TableContent's keyboard exclusive.

Return: true (non-zero) if the child object was not able to release the TableContent's keyboard exclusive; false (zero) otherwise.

The Table Objects: 6 LocatorClass

The LocatorClass is a subclass of the TableClass . Locator objects do not, however, behave like other Table objects. The Locator object is a specific object designed to be used in tandem with a linked Table object. An action taken on the Locator object will perform a searching action on the linked Table object. In most cases, you will simply want to include a Locator object in your application and link it to your Table. (You implement the actual searching algorithms in your Table object.) You do not need to subclass or add any behavior to the Locator object.

Each Table-Locator pair is placed within a parent TableContent. In the current version of GEOS, multiple tables may not appear under a TableContent (excepting, of course, that LocatorClass is actually a subclass of TableClass itself). The following information mirrors information appearing in Handling Locator Searches . Consult that section for information about implementing a search within your Table object. The information provided here is solely for completeness' sake.

Index Mode

Text Mode

LocatorClass Instance Data

Locator Actions

The Table Objects: 6.1 LocatorClass: Index Mode

MSG_LOCATOR_DO_CHAR_LOCATE

The Locator object can be in one of two modes: index mode or text search mode. The Locator object will appear and act differently depending on it operating mode.

When the Locator object is in index mode, it takes the form of an action bar, shown below:

Tapping on one of the Locator's dyad buttons (`AB', `EF', etc.) sends MSG_LOCATOR_DO_CHAR_LOCATE to the Locator object itself. The Locator responds to this request by sending MSG_TABLE_CHAR_LOCATE to its linked Table. Intercept that message in TableClass to provide your searching behavior.

Repeatedly clicking on a dyad button will cycle through the two letters of the dyad to perform the search. For example, clicking once on the `AB' dyad button will send the aforementioned messages with a character argument of `A.' Clicking again on that button will send these messages with an argument of `B' and clicking yet again on that button will send these messages with an argument of `A' again.

You will probably not want to implement any searching behavior in your Locator object; it is sufficient merely to include the Locator object and intercept MSG_TABLE_CHAR_LOCATE in the Table object itself. This is discussed in Handling Locator Searches .

MSG_LOCATOR_DO_CHAR_LOCATE
Boolean	MSG_LOCATOR_DO_CHAR_LOCATE(
        char		searchChar);

This message instructs a Locator object (in index mode) to locate the entered character within the TableClass object that is in the Locator's LI_ destination field. It does this by sending the Table object MSG_TABLE_CHAR_LOCATE , passing it the search character. You will want to intercept that message within your subclass of TableClass to implement your search criteria.

When in index mode, this message is sent with a single index letter (e.g. `A', or `B' in the "AB" dyad) of the tapped dyad button. Repeatedly tapping on a dyad button will cycle through the indexed letters, sending out this message a separate time for each tap (first with `A,' then with `B,' and then with `A' again). For this reason, make sure that code that implements this message is not run under the UI thread. (Doing so may result in bothersome screen "glitches" as the Table scrolls first to the `A' entries, then to the `B' entries, etc.

Source: The Locator object.

Destination: A Locator object.

Parameters: searchChar Character to pass to MSG_TABLE_CHAR_LOCATE .

Return: Non-zero if the search failed. Note that if you do not write a search method for TableClass, this message will always return non-zero.

The Table Objects: 6.2 LocatorClass: Text Mode

MSG_LOCATOR_DO_STRING_LOCATE

When the Locator object is in text search mode, the action bar "morphs" into a text search entry field shown below:

Entering text into the text search field will send MSG_LOCATOR_DO_STRING_LOCATE to itself. The Locator responds to this message by immediately sending its linked Table MSG_TABLE_STRING_LOCATE . Repeatedly entering valid characters will match succeeding characters.

For example, typing `B' in a Table object will send MSG_TABLE_STRING_LOCATE to the Table with the string "B." Typing `e' after that letter will send MSG_TABLE_STRING_LOCATE passing the string "Be." Whenever the Table can no longer find a match for its character string, MSG_TABLE_STRING_LOCATE will return TRUE (an error condition) and the character will not be allowed into the text entry field. You can complete a text mode search and return to index mode by pressing ENTER.

MSG_LOCATOR_DO_STRING_LOCATE
Boolean	MSG_LOCATOR_DO_STRING_LOCATE(
        char		*locateString);

This message instructs a Locator object (in text mode) to locate the entered string within the TableClass object that is in the Locator's LI_ destination field. It does this by sending the Table object MSG_TABLE_STRING_LOCATE , passing it the pointer to the search string. You will want to intercept that message within your subclass of TableClass to implement your search criteria.

The Locator object enters text search mode whenever the user types a printable character on the keyboard; in that case, the locateString contains the character typed and a search (with this message) is performed. I.e. a search is performed after each successive character is entered. If the search is unsuccessful, the character will not appear.

Source: The Locator object.

Destination: A Locator object.

Parameters: locateString Pointer to the null-terminated search string to pass to MSG_TABLE_STRING_LOCATE .

Return: Non-zero if the search failed. Note that if you do not write a search method for TableClass, this message will always return non-zero.

The Table Objects: 6.3 LocatorClass: LocatorClass Instance Data

LocatorClass contains a number of instance fields that are usually not accessed directly. In most cases, these instance fields are maintained by the system. In some cases, you can send messages to alter these instance fields.

Code Display 5-10 LocatorClass Instance Data

/*
 * LocatorClass is a subclass of TableClass. Do not treat it as a
 * normal subclass of TableClass, however. Many of the instance fields of
 * TableClass undergo special treatment in LocatorClass (especially the column
 * definitions). All of the Locator object's instance fields (except
 * LI_destination) are internal and maintained by the Locator object itself.
 */
@instance 		LocatorStateFlags			LI_state;
@instance 		ChunkHandle			LI_actionBarColDefsHandle = 0;
@instance 		ChunkHandle			LI_textSearchColDefsHandle = 0;
@instance 		TableCellLocation			LI_selectionStart = 0;
@instance 		LocatorSelections			LI_selections = 0;
@instance 		optr			LI_destination = 0;

LI_ state is an internal field that is maintained by the LocatorClass object. This field determines whether the Locator has been initialized, and the status of its current state ( index or text search ). Do not alter this field.

LI_ actionBarColDefsHandle is an internal field; this field contains the chunk handle of the column definitions for the Locator object when it is in index mode. Note that the Locator object contains two separate column definitions, unusual for normal Table objects. Do not alter this field.

LI _textSearchColDefsHandle is an internal field; this field contains the chunk handle of the column definitions for the Locator object when it is in text search mode. Do not alter this field.

LI _selectionStart and LI_ selections are internal fields that manage information about the dyad button's states. Do not alter this field.

LI_ destination contains the optr of the object to receive messages (e.g., MSG_LOCATOR_DO_STRING_LOCATE ) sent out by this Locator object. This is usually the sibling Table object that searches are performed on. This field should be set statically.

The Table Objects: 6.4 LocatorClass: Locator Actions

MSG_LOCATOR_CHANGE_TO_ACTION_BAR, MSG_LOCATOR_CHANGE_TO_TEXT_SEARCH, MSG_LOCATOR_DO_STRING_LOCATE, MSG_LOCATOR_DO_CHAR_LOCATE

Each time the Locator object changes its mode ( index or text search ) it will send itself either MSG_LOCATOR_CHANGE_TO_ACTION_BAR (if it is changing to index mode) or MSG_LOCATOR_CHANGE_TO_TEXT_SEARCH .

For example, if the Locator is in text search mode, it is desirable to revert to index mode whenever it loses the focus (and loses keyboard input). If the Locator is in text search mode and does lose the focus, the Locator's superclass ( TableClass ) senses this, sending MSG_TABLE_STOP_EDIT_CELL_TEXT to itself. In the handler for that message, the Locator sends itself MSG_LOCATOR_CHANGE_TO_ACTION_BAR .

MSG_LOCATOR_CHANGE_TO_ACTION_BAR
void	MSG_LOCATOR_CHANGE_TO_ACTION_BAR();

This message instructs the Locator object to appear as an "action bar" with a row of dyad buttons ("AB", "CD", etc.). This index mode is distinct from the Locator's other mode, that of a text search field. When the Locator object is in index mode, tapping (once) on a dyad button will send MSG_LOCATOR_DO_STRING_LOCATE with the first letter of the dyad tapped on (e.g. "A" in the `AB' dyad; "E" in the `EF' dyad). Repeatedly tapping on that dyad button will send MSG_LOCATOR_DO_STRING_LOCATE with the succeeding characters. The Locator object automatically "wraps" when it performs a search with its second character. I.e. after `B' is sent in the `AB dyad, `A' is sent again.

Source: The Locator object sends this message to itself.

Destination: A Locator object.

Parameters: None.

Return: Nothing.

Interception: May intercept to receive notification when the Locator object is switching into index mode. Make sure to pass this message on to the superclass, however.

MSG_LOCATOR_CHANGE_TO_TEXT_SEARCH
void	MSG_LOCATOR_CHANGE_TO_TEXT_SEARCH();

This message instructs the Locator object to appear as a text search field. When the Locator object is in text search mode any characters entered into the device will appear within the Locator's text search field. Pressing RETURN will send the text string to the Table with MSG_LOCATOR_DO_STRING_LOCATE.

Source: The Locator object.

Destination: A Locator object.

Parameters: None.

Return: Nothing.

Interception: May intercept to receive notification when the Locator is switching into text search mode. Make sure to pass this message on to the superclass, however.

This document is a single-page version of a a multi-page document, suitable for easy printing.


The Text Objects

Text support is one of the integral and most important pieces of the GEOS system software. Many text formatting, editing, publishing, and advanced features are built into the system; therefore, many applications will never need to deal directly with these issues. The text features of GeoWrite, for example, are almost entirely implemented within the text object library.

If your geode will display text, receive text input, or provide any type of text formatting features, you should use one of the text objects, either GenText or VisText. (If you only need a single-line non-editable text field, you may be able to use a simple GenGlyph instead.)

The GenText object is primarily a user interface object, designed to communicate directly with the user and accept keyboard input. The VisText object is a much more versatile (and complex) object designed not only to display text but provide complex formatting and WYSIWYG word processing operations.

Both objects make use of common functionality defined within the Text Object Library. Therefore, it is important to be familiar with the features and standards of that object library before using either GenText or VisText. Additionally, if you plan on using your text object to perform any complex style sheets, you should be familiar with the chunk array mechanisms, as these are widely employed in complex text objects.

1 The Text Objects
1.1 Which Object Should I Use?
1.2 How this Chapter is Organized
2 General Text Features
2.1 Input Management and Filters
2.2 Text Flow Through Regions
2.3 Style Sheets
2.4 Undo
2.5 General Import and Export
2.6 Geometry Management
2.7 Embedded Graphics and Characters
2.8 Search and Replace
2.9 Spell-Checking
2.10 Printing
2.11 Text Controller Objects
3 The Text Object Library
3.1 Character Attribute Definitions
3.2 Paragraph Attribute Definitions
3.3 Storage Flags
4 Text Object Chunks
4.1 The Text
4.2 Lines and Fields
4.3 Character Runs
4.4 Paragraph Runs
5 Using VisText
5.1 VisText Features
5.2 VisText States
5.3 VisText VM File Storage
5.4 Text Filters
5.5 Key Functions
5.6 Setting Text Confines
5.7 Output Messages
5.8 Getting Geometry Information
6 Using GenText
6.1 GenText Instance Data
6.2 GenText Basics
7 The Controllers
7.1 Character Attribute Controllers
7.2 Paragraph Attribute Controllers
7.3 Search and Replace and Spell-Checking

The Text Objects: 1 The Text Objects

Many applications will require some form of typed input from the user. Some will provide text formatting, word processing, or WYSIWYG desktop publishing features, and nearly all will have some text-based information displayed on the screen. Each of these features can be provided easily with either GenText or VisText objects. To use either of these objects, you should make sure to include the text library in your geode.

The VisText and GenText classes have many features in common. Under the hood, both the VisText and GenText objects contain similar instance data, and both are capable of handling messages from the text object library. Which object you use depends on what role you need your text object to assume.

Which Object Should I Use?

How this Chapter is Organized

The Text Objects: 1.1 The Text Objects: Which Object Should I Use?

The VisText object is the more complex of the two objects. In general, a VisText object may exhibit any of the many features present within the text object library; the GenText object may only exhibit a subset of this behavior. The GenText object is not a low-level object, however; in most cases, GenTextClass may use most of the messages provided with VisTextClass . It is only in more complex text operations (such as drawing text to regions or incorporating complex graphics into text) that a GenText object is not as useful.

Typically, geodes that want full-featured word processing will use a VisText object (typically within a scrollable GenView and a VisContent). Simple text editing and input (within a database field, for example) are normally done with an editable GenText object. For other cases, it is up to you. There are advantages to either approach. GenText is easier to use, but VisText is more versatile.

Typically, the makeup of your user interface will decide which text object is more appropriate. GenText objects should be attached to the generic tree; VisText objects should instead be attached to the visible tree. Most of the features listed below are described with respect to VisText objects; in most cases, however, they are equally applicable to the GenText.

The main functional difference between a VisText and a GenText object is the ability of VisText to use what is known as the large text model. The large model allows a text object to define flow regions, provide for text to be stored within a VM file, and offers several other rather complex features. This behavior is essential for providing complex text formatting features. It is usually not required for most text needs, however.

The GenText object does not support this text model. In cases where you feel you need to use functionality of the large model, you should use a VisText object within a visible tree. Luckily, most of these capabilities are automatic when using a VisText object. (You will rarely set up a VisText object already using this model, for instance; instead, whenever the VisText is forced to use operations requiring the large model, the application will force it to convert itself.)

The Text Objects: 1.2 The Text Objects: How this Chapter is Organized

This chapter is organized in a special fashion. Not including this section, the contents of this chapter are:

10.2
A description of some of the more powerful features of the text objects.
10.3
A complete list of the features defined within the text object library, for use with both GenText and VisText.
10.4
An in-depth description of the common ways to change text and attributes within a text object. These are the most commonly used operations for text objects.
10.5
A description in detail of the VisText object.
10.6
A description in detail of the GenText object.
10.7
A description of the controllers specifically tailored for use with the text objects.

If you are using a simple GenText object for getting and setting text, you will probably only need to read the in-depth description of how to alter text and the GenText section. If you need other functionality, you should probably read this chapter straight through.

The Text Objects: 2 General Text Features

The Text objects provide a powerful tool for complex text formatting. This section describes some of the more powerful features of these text objects.

Input Management and Filters

Text Flow Through Regions

Style Sheets

Undo

General Import and Export

Geometry Management

Embedded Graphics and Characters

Search and Replace

Spell-Checking

Printing

Text Controller Objects

The Text Objects: 2.1 General Text Features: Input Management and Filters

A large portion of the text library is devoted to input management and interaction with the UI and the user. Almost every input and display function for text is implemented within the text library, relieving your applications from containing often complex code. In almost all cases, you will not need to know how input is transferred to your text objects.

The text library handles all keyboard and mouse input to text fields, interacting intelligently with the UI to process input events. (This includes pen input supported by the input manager and the pen input mechanisms.) Selection with the mouse and keyboard is handled automatically, as are quick-transfer operations.

Because the text objects understand the focus and target hierarchies of the UI, they can interact directly with the controller objects. Applications using the text objects don't even have to know when a user has applied a new style, changed the paragraph spacing, or set the text color. The user can select text, call up the Style menu, and apply a new style without using a line of application-specific code. If you wish such behavior to happen automatically, you should mark your text object GA_TARGETABLE in its GI_ attrs instance field.

GEOS also provides filters to text objects; these filters allow the text object to selectively intercept and process text as it is being added to a text object (usually a GenText), either from direct keyboard typing or from a more complex operation (such as a clipboard paste). Filters allow the text object to accept or reject text on a number of bases (such as character by character).

The Text Objects: 2.2 General Text Features: Text Flow Through Regions

The text library also allows a continuous stream of text to flow through various connected regions. These regions may be of arbitrary size, shape, and location, and each region occupies one spot in the text object's region list. The text library, once the region list is created, takes care of all text flowing and rippling from one region to the next. GenText objects may not implement regions, though; ideally their geometry should be left up to the specific UI.

Applications can easily set up regions using the graphics system's path code, then add the regions to the text object's region list. These two simple steps can result in powerful publishing features such as flowing text around graphics and flowing text through arbitrarily-shaped regions.

The Text Objects: 2.3 General Text Features: Style Sheets

Many word processors and publishing programs today implement styles . A style is a set of character and paragraph attributes typically associated with a specific name; the user chooses the attributes for the style and then can apply that style to any selected text by choosing the associated name. A style sheet is a set of styles. Styles can be (and normally are) based on other styles.

Normally, the definition and use of styles is left entirely to the application. In GEOS, however, the text library provides a controller object for styles and style sheets, so if your application will use style sheets, it can do so without needing complex code.

The Text Objects: 2.4 General Text Features: Undo

For complex text formatting, the ability to "undo" any changes is nearly a necessity. The GEOS text library provides a single-level undo that will work with all text-edit objects. This undo may be implemented by either the user or the application.

The Text Objects: 2.5 General Text Features: General Import and Export

Through the Impex Library, any application using a text object can import from and export to many popular word processor formats. You don't have to go through the work of creating your own format translator for each supported format; any format supported by the Impex Library will automatically be supported by your application. (See the Impex chapter.) In addition, the Impex Library is designed so users can add new translators to the importer/exporter; these translators are then accessible to your application without recompiling or upgrading.

The Text Objects: 2.6 General Text Features: Geometry Management

The GenText object automatically will interact with its parent generic object (usually a GenInteraction or GenPrimary) to provide the proper sizing behavior. GenText objects automatically implement their own scrolling when necessary, adding horizontal or vertical scrollers as the need arises.

VisText, however, does not provide automatic scrolling. The VisText may only exist as a node in a visible object tree under a VisContent; the content (in concert with its parent GenView) will provide whatever scrolling, scaling, and sizing that is required.

The Text Objects: 2.7 General Text Features: Embedded Graphics and Characters

Every text field with the capability to define regions may contain embedded graphics strings. Each graphic appears as a single character within the text, and graphics may be pasted in by the user.

It is also possible to flow text around graphics. The large object model provides this feature, but the regular text model does not. Thus, to flow text around graphics requires a VisText object set up for large text.

The Text Objects: 2.8 General Text Features: Search and Replace

Global search-and-replace functions are included in the text object. GEOS provides a standard search-and-replace dialog box that can be called up by any text object. The search-and-replace mechanism can work with all text objects in the system; if two applications are both using search-and-replace at the same time, only one dialog box will appear on the screen. The search and replace controller understands which application is the current target of the search operations and interacts intelligently with the active applications.

The Text Objects: 2.9 General Text Features: Spell-Checking

Like search-and-replace, spell-checking is a function of the text library and is available globally to all applications and libraries that use text objects. GEOS uses a licensed Houghton-Mifflin dictionary and spell-checking engine; this engine as well as the common UI are available to each application and library that uses a text object. The spelling checker, like the search-and-replace mechanism, interacts intelligently with the text object and the UI to ensure that only one spell-check dialog box is on the screen.

The simplest and most efficient way to include spell-checking in your application is to include an object of SpellControlClass in your application, linking it to the text object to spell-check. This controller is discussed in Search and Replace and Spell-Checking .

The Text Objects: 2.10 General Text Features: Printing

VisText objects automatically know how to print themselves when sent the proper messages. In nearly all cases where printing text is essential, a VisText will be included in a visual object tree. Then, when printing occurs, the topmost object in the tree (most likely a VisContent) will receive the print message; this message will be passed down the tree until all the visual objects (including any VisTexts) have printed themselves.

The Text Objects: 2.11 General Text Features: Text Controller Objects

As with several other major features of the GEOS user interface, the text library has associated controller classes for many features. Because text formatting has so many options and choices for the user, providing the menus, dialog boxes, and choices can be a lot of work for an application. The text controller objects, however, remove nearly all this work while allowing applications the full support they require.

Nearly every feature of the text library has one corresponding controller object. In addition, master controllers exist to manage other controller objects. Therefore, if you use a master controller, other controllers that may be added to the text object (by Geoworks) will be automatically included in your geode without changing the source code or recompiling.

Using controllers and an editable text object, you can actually create a simple but full-featured word processor without writing any code other than UI object definitions. Controllers typically work on both GenText or VisText objects; whichever text object has the target will receive updates from controllers.

The Text Objects: 3 The Text Object Library

Both Text Objects rely on a single framework; this framework is the Text Object Library. The Text Object Library ( tCommon.h ) contains many definitions and structures that text objects will use. Every definition and structure in this section can be used by both VisText and GenText objects.

VisTextClass is the class that utilizes most of these features directly. GenTextClass also utilizes almost every one of these features through VisTextClass . This is because GenTextClass , as a generic object, is manifested through VisClass by specific UI libraries.

The text library also has data structures and mechanisms defined to support other features that, due to time constraints, have no strict API definition in the library. To provide these "potential features" in your applications, you can directly access the text data structures and set them appropriately. (For example, automatic paragraph numbering is not directly supported, but the paragraph attributes data structures allow defining characters that get prepended to individual paragraphs.)

Many of the features are implemented and controlled automatically by GEOS. For common features (character styles and font sizes, for example), controller objects can handle almost everything. For less common features (automatic paragraph numbering, for example), your application will likely have to provide the UI and the control mechanisms necessary.

Character Attribute Definitions

Paragraph Attribute Definitions

Storage Flags

The Text Objects: 3.1 The Text Object Library: Character Attribute Definitions

The text object library provides a multitude of ways to shape the appearance of individual characters. Each character within a text object may even exhibit its own individual attributes, if the object is defined to use multiple character attributes. The definitions within this section show the full range of possibilities for character attributes.

The VisTextCharAttr Structure

The VisTextCharAttr structure provides a great variety of attributes for characters within text objects. Many of the VisTextCharAttr fields use graphics structures to tailor the appearance of individual characters within the text object. You may wish to review the Graphics Environment chapter to discover the full features of this structure.

Code Display 10-1 The VisTextCharAttr Structure

typedef struct {
    StyleSheetElementHeader				VTCA_meta;
    FontID				VTCA_fontID;
    WBFixed				VTCA_pointSize;
    TextStyle				VTCA_textStyles;
    ColorQuad				VTCA_color;
    sword				VTCA_trackKerning;
    byte				VTCA_fontWeight;
    byte				VTCA_fontWidth;
    VisTextExtendedStyles				VTCA_extendedStyles;
    SystemDrawMask				VTCA_grayScreen;
    GraphicPattern				VTCA_pattern;
    ColorQuad				VTCA_bgColor;
    SystemDrawMask				VTCA_bgGrayScreen;
    GraphicPattern				VTCA_bgPattern;
    byte				VTCA_reserved[7];
} VisTextCharAttr;

VTCA_ meta stores a StyleSheetElementHeader . This structure field allows the VisTextCharAttr structure to act as an element in an element array. For complete information on using character attribute element arrays to implement character style runs, see Character Runs .

VTCA_ fontID stores the FontID of the font to be used for this character. Any current font allowed by the system is valid. There may exist up to 65536 fonts of type FontID .

VTCA_ pointSize stores the point size of the character. This may be any fixed point value between 4 and 792.

VTCA_ textStyles stores the text styles (of type TextStyle ) to be applied to this character. For a list of text styles, see the Drawing Shapes chapter.

VTCA_ color stores the current color (of type ColorQuad ) of the foreground text character.

VTCA_ trackKerning stores the kerning value (either positive or negative) for this character. This value is expressed in points and reflects how much extra space (if positive) or removal of space (if negative) to apply between this character and the next character.

VTCA_ fontWeight stores the font-weight (thickness) of the character. This value is expressed as a FontWeight value, a percentage between 75% and 125%.

VTCA_ fontWidth stores the width of the font, expressed as a FontWidth value, a percentage between 25% and 200%. Several constants are defined in font.h with common font widths, including FWI_CONDENSED and FWI_EXPANDED.

VTCA_ extendedStyles stores any VisTextExtendedStyles (separate from the TextStyle in VTCA_ textStyles ) in use by this character. These styles are defined in the text object library. See VisTextExtendedStyles for a list of extended styles.

VTCA_ grayScreen stores the SystemDrawMask to use when displaying this text. This mask is applied to the text characters but not to the background.

VTCA_ pattern stores the GraphicPattern to use when displaying this text. This pattern is applied to the text characters but not to the background.

VTCA_bgColor stores the background color to apply to the character's background (but not to the character itself).

VTCA_ bgGrayScreen stores the SystemDrawMask to apply to the character's background.

VTCA_ bgPattern stores the GraphicPattern to apply to the character's background.

Code Display 10-2 VisTextExtendedStyles

  /* These flags are stored in the VisTextCharAttr entry VTCA_extendedStyles */
typedef WordFlags VisTextExtendedStyles;
#define VTES_BOXED				0x8000
#define VTES_BUTTON				0x4000
#define VTES_INDEX				0x2000
#define VTES_ALL_CAP				0x1000
#define VTES_SMALL_CAP				0x0800
#define VTES_HIDDEN				0x0400
#define VTES_CHANGE_BAR				0x0200
#define VTES_BACKGROUND_COLOR				0x0100

VTES_BOXED draws the character within a box (a rectangle surrounding the character). If consecutive characters are marked VTES_BOXED they will all be drawn within a single rectangular outline.

VTES_BUTTON frames the text characters within a drop shadow. Consecutive characters marked VTES_BUTTON will be framed within a single drop-shadow.

VTES_INDEX marks the character for indexing purposes. This feature is currently not implemented.

VTES_ALL_CAP indicates that the character should be capitalized in the current point size.

VTES_SMALL_CAP indicates that the character should be capitalized, but at a smaller point size.

VTES_HIDDEN indicates that this character acts as hidden text. Hidden text may exhibit special properties (such as being masked out when printing).

VTES_CHANGE_BAR is currently unimplemented.

VTES_BACKGROUND_COLOR indicates that this character exhibits a special background color.

Default Character Attributes

You may decide that you do not need your characters to exhibit the many attributes available within the VisTextCharAttr structure. If your text object is simple, you may want instead to use the default character attributes provided in the text object library. These default attributes supersede the use of the VisTextCharAttr structure. All of these attributes will fit into a single word record.

The VisTextDefaultCharAttr record allows your text object to exhibit three styles (underline, bold, and italic), 16 color indexes of type Color , eight point sizes, and 32 different fonts.

Code Display 10-3 VisTextDefaultCharAttr

typedef WordFlags VisTextDefaultCharAttr;
#define VTDCA_UNDERLINE			0x8000
#define VTDCA_BOLD			0x4000
#define VTDCA_ITALIC			0x2000
#define VTDCA_COLOR			0x0f00	/* Color */
#define VTDCA_SIZE			0x00e0	/* VisTextDefaultSize */
#define VTDCA_FONT			0x001f	/* VisTextDefaultFont */
#define VTDCA_COLOR_OFFSET				8
#define VTDCA_SIZE_OFFSET				5
#define VTDCA_FONT_OFFSET				0
  typedef ByteEnum VisTextDefaultSize;
    #define		VTDS_8	0		/* Point size of 8 */
    #define		VTDS_9	1		/* Point size of 9 */
    #define		VTDS_10	2		/* Point size of 10 */
    #define		VTDS_12	3		/* Point size of 12 */
    #define		VTDS_14	4		/* Point size of 14 */
    #define		VTDS_18	5		/* Point size of 18 */
    #define		VTDS_24	6		/* Point size of 24 */
    #define		VTDS_36	7		/* Point size of 36 */
  typedef ByteEnum VisTextDefaultFont;
    #define		VTDF_BERKELEY		0		/* Berkeley Font */
    #define		VTDF_CHICAGO		1		/* Chicago Font */
    #define		VTDF_BISON		2		/* Bison Font */
    #define		VTDF_WINDOWS		3		/* Windows Font */
    #define		VTDF_LED		4		/* LED Font */
    #define		VTDF_ROMA		5		/* Roma Font */
    #define		VTDF_UNIVERSITY		6		/* University Font */
    #define		VTDF_URW_ROMAN		7		/* URW Roman Font */
    #define		VTDF_URW_SANS		8		/* URW Sans Font */
    #define		VTDF_URW_MONO		9		/* URW Mono Font */
    #define		VTDF_URW_SYMBOLPS 10				/* URW Symbols Font */
    #define		VTDF_CENTURY_SCHOOLBOOK 11				/* Century Schoolbook Font */
#define  VIS_TEXT_INITIAL_CHAR_ATTR \
    ((VTDS_12 << VTDCA_SIZE_OFFSET) | VTDF_BERKELEY)

Macros for Defining VisTextCharAttr Structures

The text object library also provides several macros to make the definition of the VisTextCharAttr structure simpler. These macros are listed in VisTextCharAttr Macros . In many cases, these macros make the definition of character style runs vastly easier.

Code Display 10-4 VisTextCharAttr Macros

 #define CHAR_ATTR_STYLE_FONT_SIZE_STYLE_COLOR(ref, style, font, psize, tstyle, \ color) { \
    {{{ref, 0}}, style}, font, {0, psize}, tstyle, \
    {color, CF_INDEX, 0, 0}, 0, FWI_MEDIUM, FW_NORMAL, 0, SDM_100, {0}, \
    {C_WHITE, CF_INDEX, 0, 0}, SDM_0, {0}, {0, 0, 0, 0, 0, 0, 0}}
 #define CHAR_ATTR_FONT_SIZE_STYLE(font, psize, tstyle) \
    CHAR_ATTR_STYLE_FONT_SIZE_STYLE_COLOR(2, CA_NULL_ELEMENT, font, \
                        psize, tstyle, C_BLACK)
 #define CHAR_ATTR_FONT_SIZE(font, psize) \
    CHAR_ATTR_STYLE_FONT_SIZE_STYLE_COLOR(2, CA_NULL_ELEMENT, font, \
                        psize, 0, C_BLACK)

The Text Objects: 3.2 The Text Object Library: Paragraph Attribute Definitions

The text object library also provides many ways to shape the appearance of individual paragraphs. Each paragraph within a text object may exhibit its own individual attributes if the text object is set to allow multiple paragraph attributes. The definitions within this section show the full range of possibilities for paragraph attributes.

The VisTextParaAttr Structure

The VisTextParaAttr structure provides the largest set of attributes for use by paragraphs within text objects. Note that although the VisTextParaAttr structure is of a fixed size, the paragraph attributes consist of this structure and a variable number of tabs; therefore, the paragraph attributes within a "run" may be variably sized. These entries are listed in VisTextParaAttr . Comments follow the code display.

Code Display 10-5 VisTextParaAttr

typedef struct {
    StyleSheetElementHeader				VTPA_meta;
    VisTextParaBorderFlags				VTPA_borderFlags;
    ColorQuad				VTPA_borderColor;
    VisTextParaAttrAttributes				VTPA_attributes;
    word				VTPA_leftMargin;
    word				VTPA_rightMargin;
    word				VTPA_paraMargin;
    BBFixedAsWord				VTPA_lineSpacing;
    word				VTPA_leading;
    BBFixedAsWord				VTPA_spaceOnTop;
    BBFixedAsWord				VTPA_spaceOnBottom;
    ColorQuad				VTPA_bgColor;
    byte				VTPA_numberOfTabs;
    byte				VTPA_borderWidth;
    byte				VTPA_borderSpacing;
    byte				VTPA_borderShadow;
    SystemDrawMask				VTPA_borderGrayScreen;
    SystemDrawMask				VTPA_bgGrayScreen;
    HatchPattern				VTPA_borderHatch;
    HatchPattern				VTPA_bgHatch;
    word				VTPA_defaultTabs;
    word				VTPA_startingParaNumber;
    char				VTPA_prependChars[4];
    VisTextHyphenationInfo				VTPA_hyphenationInfo;
    VisTextKeepInfo				VTPA_keepInfo;
    VisTextDropCapInfo				VTPA_dropCapInfo;
    word				VTPA_nextStyle;
    StandardLanguage				VTPA_language;
    byte				VTPA_reserved[15];
} VisTextParaAttr;

VTPA_ meta stores a StyleSheetElementHeader . This structure field allows the VisTextParaAttr structure to act as an element in an element array. For complete information on using paragraph attribute element arrays to implement paragraph style runs, see Paragraph Runs .

VTPA_borderFlags stores the VisTextParaBorderFlags to use in drawing any borders around the text paragraph.

VTPA_ borderColor stores the color (of type ColorQuad ) of the border.

VTPA_ attributes stores the VisTextParaAttrAttributes to use on this paragraph. These attributes specify whether the paragraph uses keep-with, drop caps, automatic hyphenation, or paragraph numbering.

VTPA_ leftMargin stores the left margin of the paragraph. This margin is the distance (offset in points) from the left edge of the text object.

VTPA_ rightMargin stores the right margin of the paragraph. This margin is the distance (offset in points) from the right edge of the text object.

VTPA_ paraMargin stores the paragraph margin. This margin is the additional distance (offset in points from the left margin) to indent the first line of a paragraph.

VTPA_ lineSpacing stores the distance between lines of the paragraph. Single-spacing (a VTPA_ lineSpacing of 1.0) is the default value.

VTPA_ leading stores the leading of the paragraph. Leading is the additional space between all lines of a paragraph, independent of line spacing. This value is expressed in points.

VTPA_ spaceOnTop stores the additional vertical distance (expressed in points) before the first line of the paragraph. VTPA_ spaceOnTop plus VTPA_ spaceOnBottom is the total vertical distance between paragraphs.

VTPA_ spaceOnBottom stores the additional vertical distance (expressed in points) after the last line of the paragraph. VTPA_ spaceOnTop plus VTPA_ spaceOnBottom is the total vertical distance between paragraphs.

VTPA_bgColor stores the background color (of type ColorQuad ) of the paragraph. Note this is different from the background color of an individual character (in VTCA_ bgColor ). The background color of a character, if any, takes precedence over the background color of a paragraph.

VTPA_ numberOfTabs stores the number of tab stops contained in this paragraph. Tabs are added at the end of the VisTextParaAttr structure. Therefore, this structure may be of variable size, depending on the number of tabs.

VTPA_ borderWidth stores the width (in units of eight pixels) of the border, if any, attached to this paragraph.

VTPA_ borderSpacing stores the spacing (in units of eight pixels) between the paragraph border, if any, and the paragraph itself.

VTPA_ borderShadow stores the distance (in units of eight pixels) of the border shadow, if any, from the main border.

VTPA_ borderGrayScreen stores the SystemDrawMask to use when drawing the paragraph border.

VTPA_ bgGrayScreen stores the SystemDrawMask to use when drawing the paragraph background.

VTPA_ borderHatch stores HatchPattern to use when drawing the border.

VTPA_ bgHatch stores the HatchPattern to use when drawing the background.

VTPA_ defaultTabs stores the spacing for default tabs.

VTPA_ startingParaNumber stores the sequential number of this paragraph. This value depends on the VisTextNumberType in VTPA_ attributes .

VTPA_ prependChars stores the characters (up to four) to prepend to the beginning of this paragraph.

VTPA_ hyphenationInfo stores the VisTextHyphenationInfo (see below) to use between lines of text within a paragraph.

VTPA_ keepInfo stores the VisTextKeepInfo (see below) to use between this and other paragraphs.

VTPA_ dropCapInfo stores the VisTextDropCapInfo (see below) to use at the start of this paragraph.

VTPA_nextStyle and VTPA_language represent text attributes that are currently unimplemented but will be added.

VTPA_ reserved reserves fifteen bytes of data at the end of the VisTextParaAttr structure. These bytes are used to designate extra tabs.

Code Display 10-6 VisTextParaBorderFlags

  typedef WordFlags VisTextParaBorderFlags;
#define VTPBF_LEFT				0x8000
#define VTPBF_TOP				0x4000
#define VTPBF_RIGHT				0x2000
#define VTPBF_BOTTOM				0x1000
#define VTPBF_DOUBLE				0x0800
#define VTPBF_DRAW_INNER_LINES				0x0400
#define VTPBF_SHADOW				0x0200
#define VTPBF_ANCHOR				0x0003 /* ShadowAnchor */
#define VTPBF_ANCHOR_OFFSET				0
  typedef ByteEnum ShadowAnchor;
#define	SA_TOP_LEFT		0
#define	SA_TOP_RIGHT		1
#define	SA_BOTTOM_LEFT		2
#define	SA_BOTTOM_RIGHT		3

The VisTextParaBorderFlags specify the manner in which a border should be drawn around the paragraph.

VTPBF_LEFT, VTPBF_TOP, VTPBF_RIGHT, and VTPBF_BOTTOM all specify whether their respective side is drawn with a border. If you wish a border to entirely surround the text object, you should set all of these flags.

VTPBF_DOUBLE specifies that there should be a two-line border around this paragraph. This flag will double any lines set to be drawn with VTPBF_LEFT, VTPBF_TOP, VTPBF_RIGHT, and VTPBF_BOTTOM.

VTPBF_DRAW_INNER_LINES draws lines between bordered paragraphs. If two consecutive paragraphs are marked with this flag, a line border will be drawn between them.

VTPBF_SHADOW specifies that the border should be shadowed. The direction of shadowing is specified in the VTPBF_ANCHOR flag.

VTPBF_ANCHOR specifies the ShadowAnchor to use if drawing a border shadow. The ShadowAnchor specifies the corner of the paragraph to anchor the shadow. The shadowing effect on the paragraph will proceed from this corner towards the opposite corner.

Code Display 10-7 VisTextNumberType

  typedef ByteEnum VisTextNumberType;
#define	VTNT_NUMBER			0
#define	VTNT_LETTER_UPPER_A			1
#define	VTNT_LETTER_LOWER_A			2
#define	VTNT_ROMAN_NUMERAL_UPPER	3
#define	VTNT_ROMAN_NUMERAL_LOWER	4

The VisTextNumberType specifies the paragraph numbering scheme for this paragraph. For each paragraph with a numbering type, the object will number that paragraph sequentially based on the last occurrence of a paragraph with the same type. The text object stores this sequential number in the VTPA_ startingParaNumber entry.

Code Display 10-8 VisTextParaAttrAttributes

  typedef WordFlags VisTextParaAttrAttributes;
#define VTPAA_JUSTIFICATION					0xc000
#define VTPAA_KEEP_PARA_WITH_NEXT					0x2000
#define VTPAA_KEEP_PARA_TOGETHER					0x1000
#define VTPAA_ALLOW_AUTO_HYPHENATION					0x0800
#define VTPAA_DISABLE_WORD_WRAP					0x0400
#define VTPAA_COLUMN_BREAK_BEFORE					0x0200
#define VTPAA_PARA_NUMBER_TYPE					0x01c0
#define VTPAA_DROP_CAP					0x0020
#define VTPAA_KEEP_LINES					0x0010
#define VTPAA_JUSTIFICATION_OFFSET					14
#define VTPAA_PARA_NUMBER_TYPE_OFFSET					5

VTPAA_JUSTIFICATION stores the Justification to use with this paragraph.

VTPAA_KEEP_PARA_WITH_NEXT ensures that this paragraph and the next one will not be split along page breaks.

VTPAA_KEEP_PARA_TOGETHER ensures that the entire paragraph will not be split along page breaks. In most cases, this will cause a paragraph capable of being split to appear at the top of the next page.

VTPAA_ALLOW_AUTO_HYPHENATION allows the paragraph to automatically hyphenate words that cross line breaks. If this flag is set, the entry VTPA_ hyphenationInfo will store the VisTextHyphenationInfo to use when hyphenating words.

VTPAA_DISABLE_WORD_WRAP disables automatic word wrapping. Each line break will immediately wrap to next line without keeping words together.

VTPAA_PARA_NUMBER_TYPE stores the VisTextParaType for this paragraph. The actual paragraph number is stored in the VTPA_ startingParaNumber entry. The paragraph number type in tandem with the paragraph number determines what number heading this paragraph will have.

VTPAA_DROP_CAP signifies that a this paragraph uses Drop Caps. If this flag is set, the VisTextParaAttr entry VTPA_ dropCapInfo stores the VisTextParaDropCapInfo (see below).

VTPAA_KEEP_LINES specifies that this paragraph should make sure that beginning or ending lines of certain lengths should not be allowed to stand alone. If this flag is set, the VisTextParaAttr entry VTCA_ keepInfo stores the VisTextKeepInfo (see below).

Code Display 10-9 VisTextHyphenationInfo

  typedef WordFlags VisTextHyphenationInfo;
#define VTHI_HYPHEN_MAX_LINES					0xf000
#define VTHI_HYPHEN_SHORTEST_WORD					0x0f00
#define VTHI_HYPHEN_SHORTEST_PREFIX					0x00f0
#define VTHI_HYPHEN_SHORTEST_SUFFIX					0x000f
#define VTHI_HYPHEN_MAX_LINES_OFFSET						12
#define VTHI_HYPHEN_SHORTEST_WORD_OFFSET						 8
#define VTHI_HYPHEN_SHORTEST_PREFIX_OFFSET						 4
#define VTHI_HYPHEN_SHORTEST_SUFFIX_OFFSET						 0

If the VTPA_attributes flag VTPAA_ALLOW_AUTO_HYPHENATION is set, VisTextHyphenationInfo stores the criteria to use when hyphenating words in VTPA_ hyphenationInfo . If hyphenation is set, words will be automatically hyphenated using a Houghton-Mifflin engine.

VTHI_SHORTEST_WORD sets the shortest word-length (in character counts) to hyphenate. Words shorter than this character count will be wrapped to the next line.

VTHI_SHORTEST_PREFIX and VTHI_SHORTEST_SUFFIX set the shortest prefix and suffix to leave on a line after hyphenation. Prefixes or suffixes shorter than this character count will be tied to the main word and wrapped, if necessary.

Code Display 10-10 VisTextKeepInfo

  typedef ByteFlags VisTextKeepInfo;
#define VTKI_TOP_LINES				0xf0
#define VTKI_BOTTOM_LINES				0x0f
#define VTKI_TOP_LINES_OFFSET				4
#define VTKI_BOTTOM_LINES_OFFSET				0

If the VTPA_ attributes flag VTPAA_KEEP_LINES is set, VisTextKeepInfo specifies the number of lines at the beginning and end of a paragraph that should not remain across page breaks. This prevents "widows" and "orphans"--single lines either at the end of one page or the beginning of another.

VTKI_TOP_LINES specifies the minimum number of lines at the beginning of a paragraph to be considered able to stand alone and not kept part of the main paragraph along a page break.

VTKI_BOTTOM_LINES specifies the minimum number of lines at the end of a paragraph to be considered able to stand alone and not kept part of the main paragraph along a page break.

For example, if VTKI_BOTTOM_LINES is set to three, then single or double lines will not be able to stand on their own at the top of a page, and either the orphans will be kept with the main paragraph or additional line(s) will be brought over from the preceding page to pad the next page.

Code Display 10-11 VisTextDropCapInfo

  typedef WordFlags VisTextDropCapInfo;
#define VTDCI_CHAR_COUNT				0xf000
#define VTDCI_LINE_COUNT				0x0f00
#define VTDCI_POSITION				0x00f0
#define VTDCI_CHAR_COUNT_OFFSET					12
#define VTDCI_LINE_COUNT_OFFSET					8
#define VTDCI_POSITION_OFFSET					4

If the VTPA_ attributes flag VTPAA_DROP_CAP is set, VisTextDropCapInfo specifies the criteria to use when implementing Drop Caps. Drop Caps are currently not implemented in GEOS though they will be supported for later releases.

VTDCI_CHAR_COUNT sets the number of characters to capitalize at the start of the paragraph before resuming normal capitalization.

VTDCI_LINE_COUNT sets the number of lines to capitalize at the start of a paragraph before resuming normal capitalization.

Code Display 10-12 Tabs

  typedef ByteEnum TabLeader;
#define	TL_NONE 		0
#define	TL_DOT 		1
#define	TL_LINE		2
#define	TL_BULLET		3
#define	TL_GRAY_LINE		4
  typedef ByteEnum TabType;
#define TT_LEFT			0
#define TT_CENTER			1
#define TT_RIGHT			2
#define TT_ANCHORED			3
  typedef ByteFlags TabAttributes;
#define	TA_LEADER		0x1c		/* TabLeader */
#define	TA_TYPE		0x03		/* TabType */
#define TA_LEADER_OFFSET			2
#define TA_TYPE_OFFSET			0
  typedef struct {
    word			T_position;
    TabAttributes			T_attr;		/* TabAttributes */
    SystemDrawMask			T_grayScreen;
    byte			T_lineWidth;
    byte			T_lineSpacing;
    word			T_anchor;
} Tab;
  typedef struct {
    VisTextParaAttr			VTMPA_paraAttr;
    Tab			VTMPA_tabs[VIS_TEXT_MAX_TABS];
} VisTextMaxParaAttr;

The Tab structure allows your paragraph to add custom tab stops to a paragraph. These tabs are in addition to any default tab stops that are defined in VTPA_ defaultTabs . You may add up to 25 custom tabs to each paragraph. These tabs are added at the end of the VisTextParaAttr structure. Therefore, different paragraphs may have paragraph attributes of different lengths depending on the number of custom tabs in use by that paragraph.

Default Paragraph Attributes

You may decide that you do not need many of the features within the VisTextParaAttr structure in your paragraphs. If your text object is simple, you may want to use a set of default paragraph attributes provided in the text object library. These default attributes supersede the use of the VisTextParaAttr structure. All of these attributes will fit into a single word record of type VisTextDefaultParaAttr .

The VisTextDefaultParaAttr structure allows your text object to exhibit a Justification , a subset of default tabs, and left, right, and paragraph margins.

Code Display 10-13 VisTextDefaultParaAttr

typedef WordFlags VisTextDefaultParaAttr;
#define VTDPA_JUSTIFICATION				0xc000 /* Justification */
#define VTDPA_DEFAULT_TABS				0x3000 /* VisTextDefaultTab */
#define VTDPA_LEFT_MARGIN				0x0f00 /* In units of half-inches */
#define VTDPA_PARA_MARGIN				0x00f0 /* In units of half-inches */
#define VTDPA_RIGHT_MARGIN				0x000f /* In units of half-inches */
#define VTDPA_JUSTIFICATION_OFFSET					14
#define VTDPA_DEFAULT_TABS_OFFSET					12
#define VTDPA_LEFT_MARGIN_OFFSET					8
#define VTDPA_PARA_MARGIN_OFFSET					4
#define VTDPA_RIGHT_MARGIN_OFFSET					0
#define VIS_TEXT_INITIAL_PARA_ATTR ( (0*2) << VTDPA_LEFT_MARGIN_OFFSET) | \
                ( (0*2) << VTDPA_PARA_MARGIN_OFFSET) | \
                ( (0*2) << VTDPA_RIGHT_MARGIN_OFFSET) | \
                (VTDDT_INCH << VTDPA_DEFAULT_TABS_OFFSET) | \
                (J_LEFT << VTDPA_JUSTIFICATION_OFFSET)
  typedef ByteEnum VisTextDefaultDefaultTab;
#define VTDDT_NONE				0
#define VTDDT_HALF_INCH				1
#define VTDDT_INCH				2
#define VTDDT_CENTIMETER				3

The VisTextDefaultDefaultTab type is solely for use within a VisTextDefaultParaAttr record. The default tab stops provide either no tab stops (VTDDT_NONE) or tab stops every half-inch, centimeter, or inch. Note that no other application-defined tabs are allowed when using the default paragraph attributes.

Macros for Defining VisTextParaAttr Structures

The text object library also provides a macro to make the definition of the VisTextParaAttr structure simpler; there are also a few macros to aid in setting up justification and tab structures. These macros are listed in VisTextParaAttr Macros . In many cases, these macros make the definition of paragraph style runs vastly easier.

Code Display 10-14 VisTextParaAttr Macros

 #define PARA_ATTR_STYLE_JUST_LEFT_RIGHT_PARA(ref, style, just, left, right, para)\
{ \
    {{{ref, 0}}, style}, 0, {C_BLACK, CF_INDEX, 0, 0}, \
    just << VTPAA_JUSTIFICATION_OFFSET, (left)*PIXELS_PER_INCH, \
    (right)*PIXELS_PER_INCH, (para)*PIXELS_PER_INCH, \
    1<<8, 0, 0, 0, {C_WHITE, CF_INDEX, 0, 0}, \
    0, 1*8, 2*8, 1*8, SDM_100, SDM_0, {0}, {0}, \
    PIXELS_PER_INCH/2*8, VIS_TEXT_DEFAULT_STARTING_NUMBER, "", 0, 0, 0,
    CA_NULL_ELEMENT, SL_ENGLISH, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
#define DEF_PARA_ATTR_JUST_TABS(just, tabs) \
    (( (0*2) << VTDPA_LEFT_MARGIN_OFFSET ) | \
     ( (0*2) << VTDPA_PARA_MARGIN_OFFSET ) | \
     ( (0*2) << VTDPA_RIGHT_MARGIN_OFFSET ) | \
     ( (tabs) << VTDPA_DEFAULT_TABS_OFFSET ) | \
     ( (just) << VTDPA_JUSTIFICATION_OFFSET ))
#define DEF_PARA_ATTR_CENTER DEF_PARA_ATTR_JUST_TABS(J_CENTER, VTDDT_INCH)
#define DEF_PARA_ATTR_RIGHT DEF_PARA_ATTR_JUST_TABS(J_RIGHT, VTDDT_INCH)

The Text Objects: 3.3 The Text Object Library: Storage Flags

As you have seen, the Text Object Library provides a multitude of character and paragraph attributes to use in the display of text. The library also provides a number of different subsets of attributes and ways to store these attributes.

A simple text object may only need a 16-bit record (such as VisTextDefaultCharAttr or VisTextDefaultParaAttr ) to store its text attributes; a highly complex text object may need a chunk array of several 30+ byte elements. The manner in which these attributes are stored is specified in the VisTextStorageFlags of the text object.

Code Display 10-15 VisTextStorageFlags

typedef ByteFlags VisTextStorageFlags;
#define VTSF_LARGE				0x80
#define VTSF_MULTIPLE_CHAR_ATTRS				0x40
#define VTSF_MULTIPLE_PARA_ATTRS				0x20
#define VTSF_TYPES				0x10
#define VTSF_GRAPHICS				0x08
#define VTSF_DEFAULT_CHAR_ATTR				0x04
#define VTSF_DEFAULT_PARA_ATTR				0x02
#define VTSF_STYLES				0x01
VTSF_LARGE
This flag states that this text object is using the large model for its data. In this case, the rest of the settings in VisTextStorageFlags are ignored. The large model is a highly complex text formatting tool; you should avoid it entirely until you are familiar with other aspects of the text object.
VTSF_MULTIPLE_CHAR_ATTRS
This flag specifies that the text may display multiple character attributes. Each character may then display its own character attributes independent of other characters. If this flag is set, VTSF_DEFAULT_CHAR_ATTRS must be not set.
VTSF_MULTIPLE_PARA_ATTRS
This flag specifies that the text may display multiple paragraph attributes. Each paragraph may then display its own attributes independent of other paragraphs. If this flag is set, VTSF_DEFAULT_PARA_ATTRS must not be set.
VTSF_TYPES
This flag specifies that this text object contains a types run.
VTSF_GRAPHICS
This flag specifies that this text object contains a graphics run.
VTSF_DEFAULT_CHAR_ATTRS
This flag specifies that this text object only uses a set of provided default character attributes. These default attributes are represented by a record of type VisTextDefaultCharAttrs . If this flag is not set, the text object will use the larger VisTextCharAttr structure to store its character attributes instead.
VTSF_DEFAULT_PARA_ATTRS
This flag specifies that this text object only uses a set of provided default paragraph attributes. These default attributes are represented by a record of type VisTextDefaultParaAttrs . If this flag is not set, the text object will use the larger VisTextParaAttr structure to store its paragraph attribute.
VTSF_STYLES
This flag specifies that this text object contains a styles run.

The Text Objects: 4 Text Object Chunks

Each text object contains several instance fields that may reference outside chunks. Most non-large text objects will reference the following chunks:

  • A text chunk containing the string of text for that object.
  • A lines/fields chunk containing internal information about the length and position of lines and tab fields.
  • A character attributes chunk, containing either a single VisTextCharAttr structure or (for multiple character attributes) a list of VisTextCharAttr structures.
  • A paragraph attributes chunk, containing either a single VisTextParaAttr structure or (for multiple paragraph attributes) a list of VisTextParaAttr structures.
  • For multiple character attributes, a chunk of character attribute runs, referencing the list of VisTextCharAttr structures above.
  • For multiple paragraph attributes, a chunk of paragraph attribute runs, referencing the list of VisTextParaAttr structures above.

The Text Object Library provides many other features, but these are the most often-used and difficult to master. You should have a complete grasp of these mechanisms before using either VisText or GenText. To make their use easier, both objects use these features in essentially the same manner.

The Text

Lines and Fields

Character Runs

Paragraph Runs

The Text Objects: 4.1 Text Object Chunks: The Text

The most important data associated with a text object is the text itself. The text of all non-large Text Objects is stored within a single chunk; this chunk is located in the same resource as the object itself. Text objects contain an instance field with a ChunkHandle to this text chunk. (In GenText objects, this is the instance field GTXI_ text ; in VisText objects, this is VTI_ text .)

Unless your text object uses the complex large model, it stores all of its text within this chunk. You may set initial text for your text object to appear within its instance data. The text object library automatically handles keyboard input from the user and translates that into characters displayed within the text object, altering the contents of the text chunk at the same time.

The text within the chunk is represented by a null-terminated character string. Each character of the text occupies a zero-based position within the text field. (The first character in the text field is at position zero.) These character positions are useful for setting character and paragraph attributes, marking the current selection, and marking the insertion point for new text.

The text object library provides a number of operations that you can perform to alter the display of text. These messages may take text from a variety of sources and include it within your text object; alternatively, you may retrieve text from your object and send it to other objects or processes.

Though the text within a non-large text object will always reside in a chunk (and may therefore be referenced with an optr) text may come from (and go to) several different sources. If you intend to retrieve text from or send text to a text object, you must know what type of source (or destination) you are dealing with.

Typically, text outside of a text object will reside in one of the following six forms. The first two cases are the most common.

  • A pointer to null terminated text string.
  • An optr to a chunk. (This is the same format the text object itself stores its text.)
  • A handle of a global memory block.
  • A handle of a VM block.
  • A database item.
  • A huge array.

For each operation on a text object, the text object library provides specific messages tailored to the format you are retrieving text from or transferring text to.

Text Ranges

VisTextRange, MSG_VIS_TEXT_GET_RANGE, VisTextRangeContext, MSG_VIS_TEXT_GET_TEXT_SIZE

Frequently, your application may wish to specify a range of text to act on. This range specifies the starting and ending points within the text for the relevant operation. Each of these starting and ending points is a zero-based character position.

typedef struct {
    dword		VTR_start;
            /* starting character position */
    dword		VTR_end;
            /* ending character position */
} VisTextRange;

To select a starting point at the first character, set VTR_ start to zero. To select an ending point at the last character, set VTR_end to the special constant TEXT_ADDRESS_PAST_END.

If you want to pass the current selection as a range in any message that demands a VisTextRange , pass VIS_TEXT_RANGE_SELECTION to indicate that the currently selected area should be used to specify the range. Note that for some operations (such as paragraph attribute changes) the affected area may be larger than the text selection. Pass VIS_TEXT_RANGE_PARAGRAPH_SELECTION if you want the currently selected area to be used after it has been adjusted to reflect its paragraph boundaries.

You can use MSG_VIS_TEXT_GET_RANGE to return an actual VisTextRange of the selection (or paragraph selection). Pass this message a VisTextRangeContext , which specifies whether the range will include just the selection or kick out the range to its paragraph boundaries.The VisTextRange buffer that you also pass must be initially filled in (with VTR_ start as 0 and VTR_ end as TEXT_ADDRESS_PAST_END).

typedef WordFlags VisTextRangeContext;
#define VTRC_PARAGRAPH_CHANGE							0x8000
#define VTRC_CHAR_ATTR_CHANGE							0x4000
#define VTRC_PARA_ATTR_BORDER_CHANGE							0x2000

MSG_VIS_TEXT_GET_TEXT_SIZE returns the total size of the text within the text object.

MSG_VIS_TEXT_GET_RANGE
void	MSG_VIS_TEXT_GET_RANGE(
        VisTextRange		*range,
        word		context);

This message fills in a VisTextRange buffer based on the selection criteria passed in the context argument. The context information specifies whether the range will be used for a character attribute change (in which case the normal selection positions will be used) or whether the range will be used for a paragraph attribute change (in which case the boundaries of the selection will be kicked out to paragraph boundaries).

Source: Unrestricted.

Destination: Any text object.

Parameters: range VisTextRange buffer to hold the range text positions returned from the message handler. This buffer must be initially filled inwith VTR_ start as 0 and VTR_ end as TEXT_ADDRESS_PAST_END.

context
VisTextRangeContext .

Return: The VisTextRange buffer filled in.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_TEXT_SIZE
dword	MSG_VIS_TEXT_GET_TEXT_SIZE();

This message returns the current size of text.

Source: Unrestricted.

Destination: Any text object.

Return: The size of the text within the text object.

Interception: Generally not intercepted.

Replacing Text

MSG_VIS_TEXT_REPLACE_TEXT

There are many messages provided in VisTextClass to replace text within your Text Object. The most generic (and therefore versatile) of these messages is MSG_VIS_TEXT_REPLACE_TEXT. Simpler messages that you can use in special cases are documented in the following section (e.g. if you need to replace all of the text--or all of the text within a selection).

You may replace any range of text displayed within a Text Object by sending MSG_VIS_TEXT_REPLACE_TEXT, passing a pointer to a VisTextReplaceParameters structure. This structure must contain a range of text to replace and a TextReference that defines how the replacement text is referenced and stored. The structure is shown below.

typedef struct {
    VisTextRange         VTRP_range;
    dword                VTRP_insCount;
    TextReference        VTRP_textReference;
    VisTextReplaceFlags  VTRP_flags;
} VisTextReplaceParameters;

VTRP_ range defines the range of text being replaced.

VTRP_ insCount contains the number of characters in the replacement string buffer that is passed in VTRP_ textReference . If you wish to have the Text object calculate this value itself dynamically, you may pass INSERT_COMPUTE_TEXT_LENGTH in the high word of this field (and zero in the low word).

VTRP_ textReference contains a TextReference structure. This structure is explained in more detail below.

VTRP_ flags contains a bitfield of VisTextReplaceFlags . Valid flags are:

VTRF_FILTER
This flag indicates that the passed replacement string should be passed through the Text object's VTI_ filters , if any, before it is inserted into the Text object.
VTRF_KEYBOARD_INPUT
This flag indicates that the text being inserted is coming from the keyboard rather than from some other source.
VTRF_USER_MODIFICATION
This flag indicates that the text being inserted is due to a user modification. (User modifications will mark the Text object dirty; text inserted due to an application-initiated action may not mark the Text object dirty.)
VTRF_UNDO
This flag indicates that the text being inserted is due to an UNDO operation.
VTRF_DO_NOT_SEND_CONTEXT_UPDATE
This flag is internally set. It speeds up context notifications during global replaces, ensuring that the context is only updated after the entire operation has finished. Do not set this flag yourself.

The TextReference structure is shown below:

typedef struct {
    TextReferenceType     TR_type;
    TextReferenceUnion    TR_ref;
} TextReference

The TextReferenceType indicates what structure is referenced within the TextReferenceUnion . For each case, the reference is a structure of different arguments and size. These types are listed below, and correspond to teh indicated matching structure:

TRT_POINTER /* TextReferencePointer */
TRT_SEGMENT_CHUNK /* TextReferenceSegmentChunk */
TRT_OPTR /* TextReferenceBlockChunk */
TRT_BLOCK /* TextReferenceBlock */
TRT_VM_BLOCK /* TextReferenceVMBlock */
TRT_DB_ITEM /* TextReferenceDBItem */
TRT_HUGE_ARRAY /* TextReferenceHugeArray */

The TextReferenceUnion may be any of the following:

typedef union {
    TextReferencePointer       TRU_pointer;
    TextReferenceSegmentChunk  TRU_segChunk;
    TextReferenceBlockChunk    TRU_blockChunk;
    TextReferenceBlock         TRU_block;
    TextReferenceVMBlock       TRU_vmBlock;
    TextReferenceDBItem        TRU_dbItem;
    TextReferenceHugeArray     TRU_hugeArray;
} TextReferenceUnion;

The formats of these individual reference structures are contained within the Structures Reference book.

MSG_VIS_TEXT_REPLACE_TEXT
Boolean	MSG_VIS_TEXT_REPLACE_TEXT(
        VisTextReplaceParameters				*params);

This message replaces the text string within a text object (either a VisText or a GenText object) with the text referenced within the passed structure.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: *params A pointer to a VisTextReplaceParameters structure.

Return: true if an error occurred; false if the Text object's text is replaced.

Interception: Generally not intercepted.

Replacing Text All At Once

MSG_VIS_TEXT_REPLACE_ALL_PTR, MSG_VIS_TEXT_REPLACE_ALL_OPTR, MSG_VIS_TEXT_REPLACE_ALL_BLOCK, MSG_VIS_TEXT_REPLACE_ALL_VM_BLOCK, MSG_VIS_TEXT_REPLACE_ALL_DB_ITEM, MSG_VIS_TEXT_REPLACE_ALL_HUGE_ARRAY

You may replace the text displayed within a Text Object all at once with the following messages. Any previous text within the text object will be replaced with new text. The new text may come from any of the formats previously described. The format of this source determines which message you should use to replace the text.

MSG_VIS_TEXT_REPLACE_ALL_PTR replaces the text with text referenced by a simple pointer to a text string.

MSG_VIS_TEXT_REPLACE_ALL_OPTR replaces the text with text referenced by an optr. (This is the format that non-large text objects store their text.)

MSG_VIS_TEXT_REPLACE_ALL_BLOCK replaces the text with text residing in a global memory block.

MSG_VIS_TEXT_REPLACE_ALL_VM_BLOCK replaces the text with text residing in a VM block. Text objects that use the large model usually store their text within a VM block, so this message is useful for transferring text from one VisText object to another, for example.

MSG_VIS_TEXT_REPLACE_ALL_DB_ITEM replaces the text with text from a database item.

MSG_VIS_TEXT_REPLACE_ALL_HUGE_ARRAY replaces the text with text from a huge array.

MSG_VIS_TEXT_REPLACE_ALL_PTR
void	MSG_VIS_TEXT_REPLACE_ALL_PTR(
        const char 		*text,
        word 		textLen);

This message replaces the text string within a text object (either a VisText or a GenText object) with the text referenced by the passed pointer.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: text A pointer to a text string.

textLen
The text length (in characters) or zero if null-terminated.

Return: Nothing. The Text object's text is replaced.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_ALL_OPTR
void	MSG_VIS_TEXT_REPLACE_ALL_OPTR(
        optr o,
        word textLen);

This message replaces the text string within a text object (either a VisText or a GenText) with the text referenced by the passed optr.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: o The optr of the chunk containing the text.

textLen
Length of the text string or zero if null-terminated.

Return: Nothing. The text object's text is replaced.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_ALL_BLOCK
void	MSG_VIS_TEXT_REPLACE_ALL_BLOCK(
        word block,
        word textLen);

This message replaces the text string within a text object (either a VisText or a GenText) with the text within the passed data block.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: block The handle of the data block containing the text to use in the replacement operation.

textLen
The length of the text or zero if null-terminated.

Return: Nothing. The text object's text is replaced.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_ALL_VM_BLOCK
void	MSG_VIS_TEXT_REPLACE_ALL_VM_BLOCK(
        VMFileHandle		file,
        VMBlockHandle		block,
        word 		textLen);

This message replaces the text string within a text object (either a VisText or a GenText) with the text within the passed data block.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: file The handle of the VM file containing the text.

block
The VM block handle of the block within the VM file.
textLen
The length of the text or zero if null-terminated.

Return: Nothing. The text object's text is replaced.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_ALL_DB_ITEM
void	MSG_VIS_TEXT_REPLACE_ALL_DB_ITEM(
        VMFileHandle 		file,
        DBGroup 		group,
        DBItem		item);

This message replaces the text string within a text object (either a VisText or a GenText) with the text within the passed database item. The text is assumed to be null-terminated.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: file The handle of the database item's associated VM file.

group
The database group containing the text.
item
The DB item containing the text.

Return: Nothing. The text object's text is replaced.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_ALL_HUGE_ARRAY
void	MSG_VIS_TEXT_REPLACE_ALL_HUGE_ARRAY(
        VMFileHandle		file,
        VMBlockHandle		hugeArrayBlock,
        word		textLen);

This message replaces the text string within a text object (either a VisText or a GenText) with the text within the passed HugeArray.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: file The handle of the huge array's associated VM file.

hugeArrayBlock
The VM block handle of the huge array directory block.
textLen
The length of the text or zero if null-terminated.

Return: Nothing. The text object's text is replaced.

Interception: Generally not intercepted.

Replacing the Text Within a Selection

MSG_VIS_TEXT_REPLACE_SELECTION_PTR, MSG_VIS_TEXT_REPLACE_SELECTION_OPTR, MSG_VIS_TEXT_REPLACE_SELECTION_BLOCK, MSG_VIS_TEXT_REPLACE_SELECTION_VM_BLOCK, MSG_VIS_TEXT_REPLACE_SELECTION_DB_ITEM, MSG_VIS_TEXT_REPLACE_SELECTION_HUGE_ARRAY

The text object library automatically allows text selection. By using the mouse (or other device, depending on the specific UI), a user may select any section of text and perform operations on this selected text. The following messages replace any selected text with text from one of the described sources. What message you use depends on what type of source your text comes from.

Note that if no text is selected, these messages will enter text at the current cursor position. This allows these messages either to replace selected text or to "insert" text if none is selected. You may override this insertion behavior by setting paragraph attributes. In those cases, text entered at the current position will overstrike current text.

MSG_VIS_TEXT_REPLACE_SELECTION_PTR
void	MSG_VIS_TEXT_REPLACE_SELECTION_PTR(
        const char		*text,
        word		textLen);

This message replaces the current selection within a text object with the text contained in the passed string pointer. If no text is currently selected, the text will be inserted at the current cursor position.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: text The pointer to the character string.

textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_SELECTION_OPTR
void	MSG_VIS_TEXT_REPLACE_SELECTION_OPTR(
        optr	o,
        word	textLen);

This message replaces the current selection within a text object with the text in the chunk specified by o . If no text is currently selected, the text will be inserted at the current cursor position.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: o The optr of the text chunk.

textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_SELECTION_BLOCK
void	MSG_VIS_TEXT_REPLACE_SELECTION_BLOCK(
        word	block,
        word	textLen);

This message replaces the current selection within a text object with the text contained in the passed block. If no text is currently selected, the text will be inserted at the current cursor position.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: block Handle of the text block.

textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_SELECTION_VM_BLOCK
void	MSG_VIS_TEXT_REPLACE_SELECTION_VM_BLOCK(
        VMFileHandle		file,
        VMBlockHandle		block,
        word		textLen);

This message replaces the current selection within a text object by the text contained in the passed VM block. If no text is currently selected, the text will be inserted at the current cursor position.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: file Handle of the VM file containing the text.

block
Handle of the block within the VM file.
textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_SELECTION_DB_ITEM
void	MSG_VIS_TEXT_REPLACE_SELECTION_DB_ITEM(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This message replaces the current selection within a text object with the text contained in the passed database item. If no text is currently selected, the text will be inserted at the current cursor position. The text is assumed to be null-terminated.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: file The handle of the VM file containing the text.

group
The database group containing the text.
item
The database item.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_REPLACE_SELECTION_HUGE_ARRAY
void	MSG_VIS_TEXT_REPLACE_SELECTION_HUGE_ARRAY(
        VMFileHandle		file,
        VMBlockHandle		hugeArrayBlock,
        word		textLen);

This message replaces the current selection within a text object with the text contained in the passed huge array. If no text is currently selected, the text will be inserted at the current cursor position.

Source: Unrestricted.

Destination: Any GenText or VisText object.

Parameters: file The VM file containing the huge array.

hugeArrayBlock
The VM block handle of the huge array directory block.
textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

Appending the Text

MSG_VIS_TEXT_APPEND_PTR, MSG_VIS_TEXT_APPEND_OPTR, MSG_VIS_TEXT_APPEND_BLOCK, MSG_VIS_TEXT_APPEND_VM_BLOCK, MSG_VIS_TEXT_APPEND_DB_ITEM, MSG_VIS_TEXT_APPEND_HUGE_ARRAY

In many cases, you may not want to replace text but instead add it to the end of the current text. You may use any of the following messages to append text to your text object. Again, the new text may come from any of the formats previously described. The format of this source determines which message you should use to add the text.

MSG_VIS_TEXT_APPEND_PTR
void	MSG_VIS_TEXT_APPEND_PTR(
        const char		*text,
        word		textLen);

This message appends text to a text object; the text is added at the end of the current text. None of the previous text is changed.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: text The pointer to the character string.

textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_APPEND_OPTR
void	MSG_VIS_TEXT_APPEND_OPTR(
        optr	o,
        word	textLen);

This message adds text to a text object; the text is added at the end of the current text. None of the previous text is changed.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: o The optr of the text chunk.

textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_APPEND_BLOCK
void	MSG_VIS_TEXT_APPEND_BLOCK(
        word	block,
        word	textLen);

This message appends text to a text object; the text is added at the end of the current text. None of the previous text is changed.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: block Handle of the text block.

textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_APPEND_VM_BLOCK
void	MSG_VIS_TEXT_APPEND_VM_BLOCK(
        VMFileHandle		file,
        VMBlockHandle		block,
        word		textLen);

This message appends text to a text object; the text is added at the end of the current text. None of the previous text is changed.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file Handle of the VM file containing the text.

block
Handle of the block within the VM file.
textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_APPEND_DB_ITEM
void	MSG_VIS_TEXT_APPEND_DB_ITEM(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This message appends text to a text object; the text is added at the end of the current text. None of the previous text is changed. The text is assumed to be null-terminated.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The handle of the VM file containing the text.

group
The database group containing the text.
item
The database item.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_APPEND_HUGE_ARRAY
void	MSG_VIS_TEXT_APPEND_HUGE_ARRAY(
        VMFileHandle		file,
        VMBlockHandle		hugeArrayBlock,
        word		textLen);

This message appends text to a text object; the text is added at the end of the current text. None of the previous text is changed.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file containing the huge array.

hugeArrayBlock
The block handle of the huge array.
textLen
The length of the text in characters or zero, if null-terminated.

Return: Nothing.

Interception: Generally not intercepted.

Retrieving the Text

MSG_VIS_TEXT_GET_ALL_PTR, MSG_VIS_TEXT_GET_ALL_OPTR, MSG_VIS_TEXT_GET_ALL_BLOCK, MSG_VIS_TEXT_GET_ALL_VM_BLOCK, MSG_VIS_TEXT_GET_ALL_DB_ITEM, MSG_VIS_TEXT_GET_ALL_HUGE_ARRAY

You may also retrieve the text from your text object and place its text into any of the previously mentioned formats. The format of your destination determines which message you should use.

MSG_VIS_TEXT_GET_ALL_PTR
word	MSG_VIS_TEXT_GET_ALL_PTR(
        const char		*text);

This message retrieves the entire text of a text object and copies it to the buffer passed. The text within the text object is unchanged.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: text The pointer to a locked or fixed buffer. The buffer must be large enough to accommodate all the text.

Return: The length of the null-terminated string not counting the null character.

text
The buffer pointed to by text will contain the null-terminated text of the text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_ALL_OPTR
word	MSG_VIS_TEXT_GET_ALL_OPTR(
        optr	o);

This message retrieves the entire text of a text object and copies it into the chunk specified by o . The text within the text object is unchanged.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: o The optr of the chunk into which the text will be copied. Pass a valid memory handle with a null chunk handle to have the message allocate a new chunk.

Return: The chunk handle of the resized (or new) chunk. The chunk will always exist upon return and will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_ALL_BLOCK
word	MSG_VIS_TEXT_GET_ALL_BLOCK(
        word	block);

This message retrieves the entire text of a text object and copies it into the passed data block.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: block Handle of the memory block into which the text will be copied. Pass a null handle to have the message allocate a new block.

Return: The handle of the resized (or new) block. The block will always exist upon return and will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_ALL_VM_BLOCK
word	MSG_VIS_TEXT_GET_ALL_VM_BLOCK(
        VMFileHandle		file,
        VMBlockHandle		block);

This message retrieves the entire text of a text object and copies it into the passed VM block.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file handle of the VM file.

block
The VM block handle of the VM block into which the text will be copied. Pass a null handle to have the message allocate a new VM block in the file.

Return: The VM block handle of the resized (or new) VM block. The VM block will always exist (assuming a valid VM file was passed), and it will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_ALL_DB_ITEM
DBGroupAndItem MSG_VIS_TEXT_GET_ALL_DB_ITEM(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This message retrieves the entire text of a text object and copies it into the passed database item.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file handle of the VM file.

group
The database group number (may be DB_UNGROUPED--if so, a new group will be returned as part of the return value).
item
The item number of the database item into which the text will be copied. Pass zero to allocate a new database item in the specified group .

Return: The DBGroupAndItem representing the resized (or new) DB item into which the text was copied. The item will always exist (assuming a valid VM file was specified) upon return, and it will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_ALL_HUGE_ARRAY
word	MSG_VIS_TEXT_GET_ALL_HUGE_ARRAY(
        VMFileHandle		file,
        VMBlockHandle		hugeArrayBlock);

This message retrieves the entire text of a text object and copies it into the passed huge array block.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file handle of the VM file containing the huge array.

hugeArrayBlock
The VM block handle of the huge array. Pass a null handle to have the message allocate a new huge array.

Return: The VM block handle of the first block of the resized (or new) huge array. The huge array will always exist upon return (assuming a valid VM file was specified), and it will contain at least the terminating null character.

Interception: Generally not intercepted.

Retrieving the Text Within a Selection

MSG_VIS_TEXT_GET_SELECTION_PTR, MSG_VIS_TEXT_GET_SELECTION_OPTR, MSG_VIS_TEXT_GET_SELECTION_BLOCK, MSG_VIS_TEXT_GET_SELECTION_VM_BLOCK, MSG_VIS_TEXT_GET_SELECTION_DB_ITEM, MSG_VIS_TEXT_GET_SELECTION_HUGE_ARRAY

To retrieve the text within the current selection, you may send any of the following messages to your text object. If no text is currently selected no text will be retrieved.

MSG_VIS_TEXT_GET_SELECTION_PTR
word	MSG_VIS_TEXT_GET_SELECTION_PTR(
        const char		*text);

This message retrieves the currently selected text and stores it in the character string referenced by the passed pointer. The text remains selected and intact in the original text object. The passed buffer must be large enough to accommodate the text.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: text The pointer to the character string.

Return: The length of the string not including the terminating null character.

text
The buffer pointed to by text will contain the null-terminated character string.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_SELECTION_OPTR
word	MSG_VIS_TEXT_GET_SELECTION_OPTR(
        optr	o);

This message retrieves the currently selected text and copies it into the passed chunk. The text remains selected and intact in the original text object.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: o The optr of the text chunk into which the text will be copied. Pass a valid memory handle and a null chunk handle to have the message allocate a new chunk in the give block.

Return: The chunk handle of the resized (or new) chunk containing the text. The chunk will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_SELECTION_BLOCK
word	MSG_VIS_TEXT_GET_SELECTION_BLOCK(
        word	block);

This message retrieves the currently selected text and copies it into the passed data block. The text remains selected and intact in the original text object.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: block Handle of the text block to place the text into. Pass a null handle to have the message allocate a new memory block.

Return: The memory handle of the resized (or new) block containing the selected text. The block will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_SELECTION_VM_BLOCK
word	MSG_VIS_TEXT_GET_SELECTION_VM_BLOCK(
        VMFileHandle		file,
        VMBlockHandle		block);

This message retrieves the currently selected text and copies it into the passed VM block. The text remains selected and intact in the original text object.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file handle of the VM file.

block
The VM block handle of the VM block into which the text will be copied. Pass a null block handle to have the message allocate a new VM block in the file.

Return: The VM block handle of the resized (or new) VM block. The VM block will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_SELECTION_DB_ITEM
DBGroupAndItem MSG_VIS_TEXT_GET_SELECTION_DB_ITEM(
        VMFileHandle		file,
        DBGroup		group,
        DBItem		item);

This message retrieves the currently selected text and copies it into the given database item. The text remains selected and intact in the original text object.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file handle of the VM file.

group
The group number of the passed item (if DB_UNGROUPED, a group will be determined).
item
The item number of the database item into which the text will be copied. Pass zero to have the message allocate a new database item in the specified group and file.

Return: The DBGroupAndItem representing the resized (or new) database item. The item will contain at least the terminating null character.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_SELECTION_HUGE_ARRAY
word	MSG_VIS_TEXT_GET_SELECTION_HUGE_ARRAY(
        VMFileHandle		file,
        VMBlockHandle		hugeArrayBlock);

This message retrieves the currently selected text and copies it into the passed huge array. The text remains selected and intact in the original text object.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: file The VM file handle of the VM file.

hugeArrayBlock
The VM block handle of the huge array into which the text will be copied. Pass a null handle to have the message allocate a new huge array in the passed VM file.

Return: The VM block handle of the resized (or new) huge array block. The huge array will contain at least the terminating null character.

Interception: Generally not intercepted.

Other Operations on the Text

MSG_VIS_TEXT_DELETE_ALL, MSG_VIS_TEXT_DELETE_SELECTION

To delete the entire contents of a text object, send it MSG_VIS_TEXT_DELETE_ALL . This message will also resize the text's chunk to its minimum size. To delete only the current selection, send the text object MSG_VIS_TEXT_DELETE_SELECTION . No text will be selected after this message is sent.

MSG_VIS_TEXT_DELETE_ALL
void	MSG_VIS_TEXT_DELETE_ALL();

This message deletes the entire contents of a text object's text chunk. The chunk will be resized to zero.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: None.

Return: Nothing

Interception: Generally not intercepted.

MSG_VIS_TEXT_DELETE_SELECTION
void	MSG_VIS_TEXT_DELETE_SELECTION();

This message deletes the currently selected text. The text after the deletion will be automatically repositioned, and the text chunk will be resized.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

Marking the Text Selection

MSG_VIS_TEXT_GET_SELECTION_RANGE, MAG_VIS_TEXT_SELECT_RANGE, MSG_VIS_TEXT_SELECT_RANGE_SMALL, MSG_VIS_TEXT_SELECT_ALL, MSG_VIS_TEXT_SELECT_START, MSG_VIS_TEXT_SELECT_END, MSG_VIS_TEXT_SELECT_RELATIVE

MSG_VIS_TEXT_GET_SELECTION_RANGE returns the VisTextRange of the current selection.

MSG_VIS_TEXT_SELECT_RANGE selects the selected area of text to the passed range. This message can be used with both large and small model text objects.

MSG_VIS_TEXT_SELECT_RANGE_SMALL selects a range of text. The message must pass the VisTextRange to "select." This message only works with non-large text objects. Any previous selection will be deselected.

MSG_VIS_TEXT_SELECT_ALL selects the recipient's entire text.

MSG_VIS_TEXT_SELECT_START selects the start of the text with a VisTextRange of zero length. This places the cursor at the beginning of the text object and deselects any previous selection.

MSG_VIS_TEXT_SELECT_END selects the end of the text with a VisTextRange of zero length. This places the cursor at the end of the text object and deselects any previous selection.

MSG_VIS_TEXT_SELECT_RELATIVE moves the cursor from its current position to a new location relative to the old position. You may pass this message a relative selection range to move the cursor and select text at the new position.

MSG_VIS_TEXT_GET_SELECTION_RANGE
void	MSG_VIS_TEXT_GET_SELECTION_RANGE(
        VisTextRange		*vtr);

This message returns the range of the text object's current selection. You must pass this message a VisTextRange buffer for the message to fill in with the selection range.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: vtr A pointer to a VisTextRange structure to fill in with the selection range.

Return: The VisTextRange buffer will be filled in.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SELECT_RANGE
void	MSG_VIS_TEXT_SELECT_RANGE(@stack
        dword	end
        dword	start);

This message creates a selection for a text object. The message must pass the starting and ending character positions to mark as the text object's selection. Any previous selection will be deselected. Note that this message uses dword offsets into the text, and can therefore be used with LARGE model text objects.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: start The zero-based character position of the start of the selection.

end
The zero-based character position of the end of the selection. This must be equal to or larger than the start position.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SELECT_RANGE_SMALL
void	MSG_VIS_TEXT_SELECT_RANGE_SMALL(
        word	start,
        word	end);

This message creates a selection for a text object. The message must pass the starting and ending character positions to mark as the text object's selection. Any previous selection will be deselected.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: start The zero-based character position of the start of the selection.

end
The zero-based character position of the end of the selection. This must be equal to or larger than the start position.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SELECT_ALL
void	MSG_VIS_TEXT_SELECT_ALL();

This message selects the entire text of a text object as its selection.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SELECT_START
void	MSG_VIS_TEXT_SELECT_START();

This message places the cursor at the start of the text. Any current selection will be deselected.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SELECT_END
void	MSG_VIS_TEXT_SELECT_END();

This message places the cursor at the end of the text. Any current selection will be deselected.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: None.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SELECT_RELATIVE
void	MSG_VIS_TEXT_SELECT_RELATIVE(
        word	newStart,
        word	newEnd);

This message moves a selection from the current cursor position to a position relative to that position.

Source: Unrestricted.

Destination: Any VisText or GenText object.

Parameters: newStart The number of character positions from the current cursor position to start the new selection.

newEnd
number of character positions from the current cursor position to end the new selection. This value must be equal to or greater than newStart .

Return: Nothing.

Interception: Generally not intercepted.

The Text Objects: 4.2 Text Object Chunks: Lines and Fields

Each text object contains a chunk storing its line and field information. The line and field information is internal; you should not alter it.

The Text Objects: 4.3 Text Object Chunks: Character Runs

The character attributes of your text object may be singular or multiple. Singular character attributes have one common set of character attributes for use by all characters within a text object. Multiple character attributes store their characteristics in "runs" which allow separate characters in the same object to show different character attributes.

Singular Character Attributes

In many cases, your text object will need only one set of attributes for all characters. In those cases, the text object's character attributes instance field (VTI_ charAttrRuns for VisText, ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR or ATTR_GEN_TEXT_CHAR_ATTR for GenText) will store a single set of attributes. This word of data will contain either a 16-bit record or a ChunkHandle to a VisTextCharAttr structure. Those attributes will be exhibited by all characters in that text object.

Unless your VisTextStorageFlags specify VTSF_MULTIPLE_CHAR_ATTRS, your character attributes will be singular. (For GenText, this field is set automatically if using the singular attributes shown above.) All text within the text object will exhibit the same characteristics; also, any changes to the character attributes will affect every character in the entire text. If you need your text object to allow different characters to show different attributes (such as in a word processor), see Multiple Character Attributes .

If your character attributes are singular, you have two choices: use a default set of characteristics or use a more complex (and complete) set of characteristics. For most simple generic text objects, the default set offers enough variety to accomplish most goals. If you need other character styles not offered in the default set, you will have to use the complete set of character attributes.

To use the default character attributes with a VisText, specify VTSF_DEFAULT_CHAR_ATTRS in your VisTextStorageFlags . The default set uses the VisTextDefaultCharAttr record to store the character's attributes. This record offers a simple collection of character fonts, point sizes, and other attributes (see below). If you need other character styles not found in this record, you should not set VTSF_DEFAULT_CHAR_ATTRS. The text object will automatically use the VisTextCharAttr structure.

By default, text objects using the default character attributes will be set to a font of VTDF_BERKELEY, a point size of VTDS_12, and a text color of C_BLACK.

If you do not use the default character attributes record, you must use the VisTextCharAttr structure to store your text object's character attributes. (In this case, do not set the VTSF_DEFAULT_CHAR_ATTRS flag in VisTextStorageFlags .) The text object's character attributes instance field will contain a ChunkHandle to this structure instead of the VisTextDefaultCharAttr record (see Setting Character Attributes ).

Code Display 10-16 Setting Character Attributes

/* This example shows how to set the default character attributes. */
@object GenTextClass MyTextObject = {
    GTXI_text = "";
    ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR = (VTDCA_UNDERLINE |
                (VTDS_18 << VTDCA_SIZE_OFFSET) | VTDF_BERKELEY);
        /* This sets the default character attributes record (of type
         * VisTextDefaultCharAttr) to use underlined Berkeley font in
         * point size 18. */
}
/* This example shows how to use non-default singular character attributes. */
@object GenTextClass MyTextObject = {
    GTXI_text = "";
    ATTR_GEN_TEXT_CHAR_ATTR = (ChunkHandle) @MyTextCharAttrs;
        /* This is a chunk handle to the chunk holding the text
         * attributes (of type VisTextCharAttr). */
}
@chunk VisTextCharAttr MyCharAttrs =
    CHAR_ATTR_FONT_SIZE_STYLE(FID_DTC_URW_ROMAN, 32, TS_UNDERLINE);
        /* This chunk holds the record that will determine the character
         * attributes of all the GenText's text. The text will be
         * 32-point Roman, underlined. */

Multiple Character Attributes

To allow a VisText object to exhibit multiple character attributes, you should set VTSF_MULTIPLE_CHAR_ATTRS and clear VTSF_DEFAULT_CHAR_ATTR flag in the object's VisTextStorageFlags (stored in VTI_storageFlags ). For GenText, you should set ATTR_GEN_TEXT_MULTIPLE_CHAR_ATTR_RUNS . Any multiple character attribute object will not be able to use any of the default character attributes of type VisTextDefaultCharAttr .

Multiple character attributes allow your object's individual characters to exhibit different attributes. One character may be bold and italic; another may be underlined and in a different font. Any character may exhibit any of the attributes allowed within the VisTextCharAttr structure.

Multiple attributes are stored as lists of VisTextCharAttr

Character attributes are specified by "runs." Each character run is used until another run is encountered, at which point the new character attributes are used. Runs are defined by a character position within a chunk array and an associated token element. At the character position, the token corresponds to a VisTextCharAttr element. The character attributes specified in this VisTextCharAttr structure are used until the character position of the next VisTextCharAttr token.

If, for example, a GenText object contains the text "Initial Text Here" with the word "Text" underlined, the chunk array of elements would look as shown in the figure above.

Code Display 10-17 Setting Multiple Character Attribute Runs

@object GenTextClass MultipleCharAttrsObject = {
    ATTR_GEN_TEXT_MULTIPLE_CHAR_ATTR_RUNS = (ChunkHandle) @MyMultCharAttrs;
    GTXI_text = "Initial Text Here";
}
#define MCA_PLAIN			0
#define MCA_UNDERLINE			1
@CHAR_ATTR_ELEMENT_ARRAY_HEADER CharAttrElements = {
    CHAR_ATTR_FONT_SIZE_STYLE(FID_DTC_URW_ROMAN, 12, 0),								 /* Element 0 */
    CHAR_ATTR_FONT_SIZE_STYLE(FID_DTC_URW_ROMAN, 12, TS_UNDERLINE)	/* Element 1 */
};
@RUN_ARRAY_HEADER(CharAttrElements) MyMultCharAttrs = {
    TRAE_ABS(0, MCA_PLAIN),					/* Element 0 */
    TRAE_ABS(8, MCA_UNDERLINE),					/* Element 1 */
    TRAE_END
};

Changing Character Attributes

MSG_VIS_TEXT_GET_CHAR_ATTR,VisTextCharAttrFlags, VisTextCharAttrDiffs, VisTextGetAttrFlags, MSG_VIS_TEXT_SET_CHAR_ATTR_BY_DEFAULT, MSG_VIS_TEXT_SET_CHAR_ATTR, MSG_VIS_TEXT_SET_FONT_ID, MSG_VIS_TEXT_SET_POINT_SIZE, MSG_VIS_TEXT_SET_TEXT_STYLE

There may be cases in which you would like to change the attributes exhibited by certain characters in your Text object. In most cases, you can do this most easily by including one of the controllers mentioned at the end of this chapter. The controllers leave any underlying work up to the specific controller implementation.

If you wish to manually change these character attributes, however, there are several messages to retrieve and set character attributes.

MSG_VIS_TEXT_GET_CHAR_ATTR
word 	MSG_VIS_TEXT_GET_CHAR_ATTR(@stack
        VisTextGetAttrFlags 			flags,
        VisTextCharAttrDiffs 			*diffs,
        VisTextCharAttr 			*attrs,
        dword 			rangeEnd,
        dword 			rangeStart);

This message returns a buffer filled in with the VisTextCharAttr attributes of the given range of text. If VTGAF_MERGE_WITH_PASSED is passed in the VisTextGetAttrFlags , then the passed VisTextCharAttr structure will be compared with the range of text that this message is sent to (and any differences will be pointed out in the passed diffs buffer.(If this flag is not passed, any information initially in that buffer will be ignored.)

Over the passed range, different characters may exhibit different attributes; a pointer to a VisTextCharAttrDiffs structure is also passed to store information about attributes that are different across the range of text.

VisTextCharAttrDiffs contains a list of VisTextCharAttrFlags specifying what attributes are multiply present. The structure also contains the bitfields of the TextStyle and VisTextExtendedStyles present over the range of text. Any attribute differences will produce a mention in one of these records; you can think of this process as XORing the attribute bitfields together.

Possible VisTextCharAttrFlags :
VTCAF_MULTIPLE_FONT_IDS
VTCAF_MULTIPLE_POINT_SIZES
VTCAF_MULTIPLE_COLORS
VTCAF_MULTIPLE_GRAY_SCREENS
VTCAF_MULTIPLE_PATTERNS
VTCAF_MULTIPLE_TRACK_KERNINGS
VTCAF_MULTIPLE_FONT_WEIGHTS
VTCAF_MULTIPLE_FONT_WIDTHS
VTCAF_MULTIPLE_BG_COLORS
VTCAF_MULTIPLE_BG_GRAY_SCREENS
VTCAF_MULTIPLE_BG_PATTERNS
VTCAF_MULTIPLE_STYLES

Structures:

typedef struct {
    VisTextCharAttrFlags 				VTCAD_diffs;
    VisTextExtendedStyles 				VTCAD_extendedStyles;
    TextStyle 				VTCAD_textStyles;
    byte 				VTCAD_unused;
} VisTextCharAttrDiffs;

Source: Unrestricted.

Destination: Any text object.

Parameters: flags VTGAF_MERGE_WITH_PASSED to merge the retrieved text attributes with the text in the passed range.

diffs
Pointer to a VisTextCharAttrDiffs structure to store attribute differences.
attrs
Pointer to a VisTextCharAttr buffer to store the retrieved character attributes. If VTGAF_MERGE_WITH_PASSED is passed in flags, this buffer initially contains attributes to match against the retrieved attributes.
rangeEnd
End of the range (character position).
rangeStart
Beginning of the range (character position).

Return: The token of the specific character attribute run (word value) if the text object is storing runs of VisTextCharAttr structures. The attrs and diffs buffers are also filled with their relevant information.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_CHAR_ATTR_BY_DEFAULT
void	MSG_VIS_TEXT_SET_CHAR_ATTR_BY_DEFAULT(@stack
        VisTextDefaultCharAttr 				defCharAttrs,
        dword 				rangeEnd,
        dword 				rangeStart);

This message sets the character attributes passed in VisTextDefaultCharAttr over the specified range of the text object. If the text object is not in "default" character attribute mode (i.e. it is storing runs of VisTextCharAttr structures) it will translate the default attributes into their matching VisTextCharAttr attributes.

Source: Unrestricted.

Destination: Any Text object.

Parameters: defCharAttrs VisTextDefaultCharAttr record to set over the passed range of text.

rangeEnd
End of the range (Character position).
rangeStart
Start of the range (Character position).

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_CHAR_ATTR
void	MSG_VIS_TEXT_SET_CHAR_ATTR(@stack
        VisTextCharAttr 		*attrs,
        dword 		rangeEnd,
        dword 		rangeStart);

This message sets the character attributes passed in the VisTextCharAttr buffer over the specified range of the text object. If the text object is in "default" character attribute mode, it will force the text object to begin storing runs of VisTextCharAttr structures.

Source: Unrestricted.

Destination: Any Text object.

Parameters: attrs Pointer to a VisTextCharAttr buffer.

rangeEnd
End of the range (Character position).
rangeStart
Start of the range (Character position).

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_FONT_ID
@importMessage MetaTextMessages, void MSG_VIS_TEXT_SET_FONT_ID(@stack
        FontID 		fid,
        dword 		rangeEnd,
        dword 		rangeStart);

This message sets the passed FontID over the specified range of the text object. If the text object currently displays its values in "default" character attributes, it will force the text object to use character attribute runs to store its VisTextCharAttr structures.

Source: Unrestricted.

Destination: Any Text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_POINT_SIZE
@importMessage MetaTextMessages, void MSG_VIS_TEXT_SET_POINT_SIZE(@stack
        WWFixed 		pointSize,
        dword 		rangeEnd,
        dword 		rangeStart);

This message sets the passed point size over the specified range of the text object. If the text object currently displays its values in "default" character attributes, it will force the text object to use character attribute runs to store its VisTextCharAttr structures.

Source: Unrestricted.

Destination: Any Text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_TEXT_STYLE
@importMessage MetaTextMessages, void MSG_VIS_TEXT_SET_TEXT_STYLE(@stack
        word 		extBitsToClear,
        word 		extBitsToSet,
        word 		styleBitsToClear,
        word 		styleBitsToSet,
        dword 		rangeEnd,
        dword 		rangeStart);

This message sets the passed text styles over the specified range of the text object. You must specify the specific style bits to set and clear, both for the VTCA_ styles and the VTCA_ extendedStyles entries.

If the text object currently displays its values in "default" character attributes, it will force the text object to use character attribute runs to store its VisTextCharAttr structures.

Source: Unrestricted.

Destination: Any Text object.

Interception: Generally not intercepted.

The Text Objects: 4.4 Text Object Chunks: Paragraph Runs

The paragraph attributes of your text object may also be singular or multiple. Singular paragraph attributes use one common set of characteristics for all the text in the text object. Multiple paragraph attributes store their characteristics in "runs" which allow separate paragraphs in the same object to use different attributes.

Singular Paragraph Attributes

You may only need your text object to exhibit a singular set of paragraph attributes. In this case, the text object's paragraph attributes instance field (VTI_ paraAttrRuns for VisText, GEN_TEXT_DEFAULT_PARA_ATTR or GEN_TEXT_PARA_ATTR for GenText) will store a single set of attributes. This word of data will contain either a 16-bit record or a ChunkHandle to a chunk of more complex attributes.

Unless your VisTextStorageFlags (in VTI_storageFlags ) specify VTSF_MULTIPLE_PARA_ATTRS, your paragraph attributes will be singular. (This flag will be set automatically for GenTexts using the above attributes.) Initially, all text within the text object will exhibit these same characteristics; also, any changes to the text object's paragraph attributes will affect the entire text. If your text object should allow different paragraphs to exhibit different attributes, see Multiple Paragraph Attributes .

If your paragraph attributes are singular, you have two choices: use a default set of characteristics or use a more complex (and complete) set of characteristics. What you use depends on the variety of choices you wish your application to have over the display of its text. For most simple generic text objects, the default set offers enough variety to accomplish most goals.

To use the default paragraph attributes, specify VTSF_DEFAULT_PARA_ATTRS in the text object's VTI_storageFlags field (for VisText) or set ATTR_GEN_TEXT_DEFAULT_PARA_ATTR (for GenText). The default set uses the VisTextDefaultParaAttr record to store the paragraph's attributes. This record offers a simple collection of paragraph justifications, tab stops and margins, and its definition is given in VisTextDefaultParaAttr . If you need other paragraph styles not found in this record, you should not set VTSF_DEFAULT_PARA_ATTRS.

By default, text objects using the default paragraph attributes will be left-justified, have default tab stops at one inch intervals, and have zero right, left, and paragraph margins.

If you do not use the default paragraph attributes record, you must use the VisTextParaAttr structure to store your text object's paragraph attributes. (In this case, do not set VTSF_DEFAULT_PARA_ATTRS in VTI_storageFlags .) The text object's paragraph attributes instance field will contain a ChunkHandle to this structure instead of the VisTextDefaultParaAttr record.

Code Display 10-18 Setting VisTextParaAttr Attributes

/* This example shows setting the initial default paragraph attributes. */
@object GenTextClass MyTextObject = {
    GTXI_text = "";
    ATTR_GEN_TEXT_DEFAULT_PARA_ATTR = ((J_CENTER << VTDPA_JUSTIFICATION_OFFSET) |
            ( (2*2) << VTDPA_LEFT_MARGIN_OFFSET) |
            ( (1*2) << VTDPA_PARA_MARGIN_OFFSET) |
            ( VTDDT_HALF_INCH << VTDPA_DEFAULT_TABS_OFFSET));
}
/* This example shows setting non-default singular paragraph attributes. */
@object GenTextClass MyTextObject = {
    GTXI_text = "";
    ATTR_GEN_TEXT_PARA_ATTR = (ChunkHandle) @MyParaAttrs;
}
@chunk VisTextParaAttr MyParaAttrs =
    PARA_ATTR_STYLE_JUST_LEFT_RIGHT_PARA(0, 0, J_CENTER, 0, 2, 1);

Multiple Paragraph Attributes

To allow your VisText object to exhibit individual paragraph attributes, you should set VTSF_MULTIPLE_PARA_ATTRS and clear the VTSF_DEFAULT_PARA_ATTR flag in the object's VTI_storageFlags record. For GenText, set the attribute ATTR_GEN_TEXT_MULTIPLE_PARA_ATTR_RUNS . Any multiple paragraph attribute object will not be able to use any of the default paragraph attributes of type VisTextDefaultParaAttr .

Multiple paragraph attributes allow your object's individual paragraphs to exhibit different attributes. One paragraph may be left justified; another may be centered with different margins. Any paragraph may exhibit any of the attributes allowed within the VisTextParaAttr structure separately.

Multiple paragraph attributes are stored in the same manner as multiple character attributes. See Character Runs for a full description of how runs are stored.

Paragraph attributes are specified by runs. Each paragraph run is used until another run is encountered, at which point the new paragraph attributes are applied and used. Runs are defined by the character position within a chunk array and an associated token element; therefore, you must take care when defining a paragraph run so that it coincides with the first character of a new paragraph. At the character position, the token corresponds to a VisTextParaAttr element. The paragraph attributes specified in this VisTextParaAttr structure are used until the next VisTextParaAttr element.

Changing Paragraph Attributes

MSG_VIS_TEXT_GET_PARA_ATTR,VisTextParaAttrFlags, VisTextParaAttrBorderFlags, VisTextParaAttrDiffs, MSG_VIS_TEXT_SET_PARA_ATTR_BY_DEFAULT, MSG_VIS_TEXT_SET_PARA_ATTR, MSG_VIS_TEXT_SET_PARA_ATTRIBUTES, MSG_VIS_TEXT_ADD_PARA_ATTR

There may be cases in which you would like to change the attributes exhibited by certain paragraphs in your Text object. As with character attributes, you can do this most easily by including one of the controllers mentioned at the end of this chapter.

If you wish to manually change these character attributes, however, there are several messages to retrieve and set character attributes.

MSG_VIS_TEXT_GET_PARA_ATTR
word 	MSG_VIS_TEXT_GET_PARA_ATTR(@stack
        VisTextGetAttrFlags 			flags,
        VisTextParaAttrDiffs 			*diffs,
        VisTextParaAttr 			*attrs,
        dword 			rangeEnd,
        dword 			rangeStart);

This message returns a buffer filled in with the VisTextParaAttr attributes of the given range of text. If VTGAF_MERGE_WITH_PASSED is passed in the VisTextGetAttrFlags , then a passed VisTextParaAttr structure will be merged with the range of text that this message is sent to. (If this flag is not passed, any information initially in that buffer will be ignored.)

Over the passed range, different paragraphs may exhibit different attributes; a pointer to a VisTextParaAttrDiffs structure is also passed to store information about attributes that are different across the range of text. This structure contains lists of VisTextParaAttrFlags and VisTextParaAttrBorderFlags specifying what paragraph and paragraph border attributes are multiply present. The structure also contains the bitfields of the VisTextParaAttrAttributes , VisTextHyphenationInfo, VisTextKeepInfo , and VisTextDropCapInfo present over the range of text.

Possible VisTextParaAttrFlags . If any of these are set, the selection exhibits the indicated multiple property over the range of text:
VTPAF_MULTIPLE_LEFT_MARGINS
VTPAF_MULTIPLE_RIGHT_MARGINS
VTPAF_MULTIPLE_PARA_MARGINS
VTPAF_MULTIPLE_LINE_SPACINGS
VTPAF_MULTIPLE_DEFAULT_TABS
VTPAF_MULTIPLE_TOP_SPACING
VTPAF_MULTIPLE_BOTTOM_SPACING
VTPAF_MULTIPLE_LEADINGS
VTPAF_MULTIPLE_BG_COLORS
VTPAF_MULTIPLE_BG_GRAY_SCREENS
VTPAF_MULTIPLE_BG_PATTERNS
VTPAF_MULTIPLE_TAB_LISTS
VTPAF_MULTIPLE_STYLES
VTPAF_MULTIPLE_PREPEND_CHARS
VTPAF_MULTIPLE_PARA_NUMBERS
Possible VisTextParaAttrBorderFlags :
VTPABF_MULTIPLE_BORDER_LEFT
VTPABF_MULTIPLE_BORDER_TOP
VTPABF_MULTIPLE_BORDER_RIGHT
VTPABF_MULTIPLE_BORDER_BOTTOM
VTPABF_MULTIPLE_BORDER_DOUBLES
VTPABF_MULTIPLE_BORDER_DRAW_INNERS
VTPABF_MULTIPLE_BORDER_ANCHORS
VTPABF_MULTIPLE_BORDER_WIDTHS
VTPABF_MULTIPLE_BORDER_SPACINGS VTPABF_MULTIPLE_BORDER_SHADOWS VTPABF_MULTIPLE_BORDER_COLORS VTPABF_MULTIPLE_BORDER_GRAY_SCREENS VTPABF_MULTIPLE_BORDER_PATTERNS

Structures:

typedef struct {
    VisTextParaAttrFlags 					VTPAD_diffs;
    VisTextParaAttrBorderFlags					VTPAD_borderDiffs;
    VisTextParaAttrAttributes					VTPAD_attributes;
    VisTextHyphenationInfo					VTPAD_hyphenationInfo;
    VisTextKeepInfo					VTPAD_keepInfo;
    VisTextDropCapInfo					VTPAD_dropCapInfo;
} VisTextParaAttrDiffs;

Source: Unrestricted.

Destination: Any text object.

Parameters: flags VTGAF_MERGE_WITH_PASSED to merge the retrieved text attributes with the text in the passed range.

diffs
Pointer to a VisTextParaAttrDiffs structure to store attribute differences.
attrs
Pointer to a VisTextParaAttr buffer to store the retrieved paragraph attributes. If VTGAF_MERGE_WITH_PASSED is passed in flags, this buffer initially contains attributes to match against the retrieved attributes.
rangeEnd
End of the range (character position).
rangeStart
Beginning of the range (character position).

Return: The token of the specific paragraph attribute run (word value) if the text object is storing runs of VisTextParaAttr structures. The attrs and diffs buffers are also filled with their relevant information.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_PARA_ATTR_BY_DEFAULT
void	MSG_VIS_TEXT_SET_PARA_ATTR_BY_DEFAULT(@stack
        VisTextDefaultParaAttr 				defParaAttrs,
        dword 				rangeEnd,
        dword 				rangeStart);

This message sets the paragraph attributes passed in VisTextDefaultParaAttr over the specified range of the text object. If the text object is not in "default" paragraph attribute mode (i.e. it is storing runs of VisTextParaAttr structures) it will translate the default attributes into their matching VisTextParaAttr attributes.

Source: Unrestricted.

Destination: Any Text object.

Parameters: defParaAttrs VisTextDefaultParaAttr record to set over the passed range of text.

rangeEnd
End of the range (Character position).
rangeStart
Start of the range (Character position).

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_PARA_ATTR
void	MSG_VIS_TEXT_SET_PARA_ATTR(@stack
        VisTextParaAttr 		*newParaAttrs,
        dword 		rangeEnd,
        dword 		rangeStart);

This message sets the paragraph attributes passed in the VisTextParaAttr buffer over the specified range of the text object. If the text object is in "default" paragraph attribute mode, it will force the text object to begin storing runs of VisTextParaAttr structures.

Source: Unrestricted.

Destination: Any Text object.

Parameters: newParaAttrs Pointer to a VisTextParaAttr buffer.

rangeEnd
End of the range (Character position).
rangeStart
Start of the range (Character position).

Interception: Generally not intercepted.

MSG_VIS_TEXT_ADD_PARA_ATTR
word	MSG_VIS_TEXT_ADD_PARA_ATTR(
        VisTextMaxParaAttr 		*paraAttr);

This message adds a given set of paragraph attributes passed in the VisTextParaAttr buffer to the paragraph "run" for that text object. The text object must not be in "default" paragraph attribute mode; it must already be storing runs of VisTextParaAttr structures.

Source: Unrestricted.

Destination: Any Text object that is storing runs of VisTextParaAttr structures.

Parameters: paraAttr P ointer to a VisTextParaAttr buffer.

Return: Token of the paragraph run entry (word-value).

Interception: Generally not intercepted.

The Text Objects: 5 Using VisText

This section describes how to use a VisText object. At the time of documentation printing, VisTextClass is undergoing improvement and will therefore be covered fully in future documentation releases. This section currently gives much useful information about the class, however.

VisText Instance Fields shows all the instance data fields of VisTextClass . Following the display are descriptions of the individual fields.

Code Display 10-19 VisText Instance Fields

 /* These are all the instance data fields of the VisText. Many of them are used
 * internally by the class and should not be used by applications. Because they
 * are documented as internal, Geoworks reserves the right to change their meaning
 * or use at any time. Those that are internal are clearly noted as such. */
@instance ChunkHandle				VTI_text;
@instance word				VTI_charAttrRuns = VIS_TEXT_INITIAL_CHAR_ATTR;
@instance word				VTI_paraAttrRuns = VIS_TEXT_INITIAL_PARA_ATTR;
@instance VMFileHandle				VTI_vmFile = NullHandle;
@instance word				VTI_lines = 0;				/* INTERNAL */
@instance VisTextStorageFlags				VTI_storageFlags = (VTSF_DEFAULT_CHAR_ATTR |
                         VTSF_DEFAULT_PARA_ATTR);
@instance VisTextFeatures				VTI_features = 0;
@instance VisTextStates				VTI_state = 0;
@instance VisTextIntFlags				VTI_intFlags = 0;				/* INTERNAL */
@instance VisTextIntSelFlags				VTI_intSelFlags = 0;				/* INTERNAL */
@instance GSRefCountAndFlags				VTI_gsRefCount = 0;				/* INTERNAL */
@instance GStateHandle				VTI_gstate = NullHandle;				/* INTERNAL */
@instance word				VTI_gstateRegion = -1;				/* INTERNAL */
@instance dword				VTI_selectStart = 0;
@instance dword				VTI_selectEnd = 0;
@instance PointDWord				VTI_startEventPos = {0,0};				/* INTERNAL */
@instance dword				VTI_selectMinStart = 0;				/* INTERNAL */
@instance dword				VTI_selectMinEnd =0;				/* INTERNAL */
@instance dword				VTI_lastOffset = 0;				/* INTERNAL */
@instance word				VTI_goalPosition = 0;				/* INTERNAL */
@instance Point				VTI_cursorPos = {0,0};				/* INTERNAL */
@instance word				VTI_cursorRegion = 0;				/* INTERNAL */
@instance word				VTI_leftOffset = 0;				/* INTERNAL */
@instance byte				VTI_lrMargin = 0;
@instance byte				VTI_tbMargin = 0;
@instance ColorQuad				VTI_washColor = {
                    C_WHITE,
                    CF_INDEX,
                    0, 0 }
@instance word				VTI_maxLength = 10000;
@instance VisTextFilters				VTI_filters = 0;
@instance optr				VTI_output;
@instance WBFixed				VTI_height = {0,0};				/* INTERNAL */
@instance word				VTI_lastWidth = -1;				/* INTERNAL */
@instance TimerHandle				VTI_timerHandle = NullHandle;				/* INTERNAL */
@instance word				VTI_timerID = 0;				/* INTERNAL */

VTI_ text stores the ChunkHandle of the object's text chunk. This chunk will be stored within a chunk in the same resource as the text object. See The Text for a full discussion of how to manipulate and alter text.

VTI_ storageFlags contains flags related to how a VisText object stores its paragraph and character attributes. The composition of these flags affects the function of VTI_ charAttrRuns and VTI_ paraAttrRuns .

VTI_ charAttrRuns stores the character attributes for the text object. Depending on VTI_ storageFlags , this instance field may contain a 16-bit record, a ChunkHandle to a VisTextCharAttr structure, or a ChunkHandle to a chunk array of character style runs.

VTI_ paraAttrRuns stores the paragraph attributes for the text object. Depending on GTI_ storageFlags , this instance field may contain a 16-bit record, a ChunkHandle to a VisTextParaAttr structure, or a ChunkHandle to a chunk array of paragraph style runs.

VTI_ features stores a VisTextFeatures record to display text within this text object. These features allow such utilities as word wrapping, hyphenation, smart quotes, etc.

VTI_ state stores different states of type VisTextStates that the text object can operate under, such as editable, selectable, targetable, etc.

VTI_ vmFile stores the handle for a text object's associated VM file; this VM file will store the text if the text object incorporates the large model. This instance field is only used for text objects using the large model.

VTI_ selectStart stores the character position of the selection's start. VTI_ selectEnd stores the character position of the selection's end. If both the selection start and selection end coincide, there is no selected text.

VTI_lrMargin stores the left and right margins of the paragraph in points. VTI_ tbMargin stores the top and bottom margins of the paragraph in points.

VTI_ washColor stores the background color (of type ColorQuad ) of the text object.

VTI_ maxLength stores the maximum number of characters allowed in this text object (for non-large objects).

VTI_ filters stores the VisTextFilters in use by this text object. Filters allow your text object to accept or reject certain sets of characters.

VTI_ output stores the destination for actions taken by this text object.

Code Display 10-20 VisText Variable Data

@vardata word		ATTR_VIS_TEXT_TYPE_RUNS;
@vardata word		ATTR_VIS_TEXT_GRAPHICS_RUNS;
@vardata word		ATTR_VIS_TEXT_REGION_ARRAY;
@vardata word		ATTR_VIS_TEXT_STYLE_ARRAY;
@vardata word		ATTR_VIS_TEXT_NAME_ARRAY;
@vardata word		ATTR_VIS_TEXT_SELECTED_TAB;
@vardata void		ATTR_VIS_TEXT_DO_NOT_INTERACT_WITH_SEARCH_CONTROL;
@vardata VisTextExtendedFilterType ATTR_VIS_TEXT_EXTENDED_FILTER;
typedef struct {
    word			VTSD_count;
    VisTextRange			VTSD_recalcRange;
    VisTextRange			VTSD_selectRange;
    dword			VTSD_showSelectionPos;
    WordFlags			VTSD_notifications;
    byte			VTSD_needsRecalc;
} VisTextSuspendData;
@vardata VisTextSuspendData ATTR_VIS_TEXT_SUSPEND_DATA;
@vardata void		ATTR_VIS_TEXT_NOTIFY_CONTENT;
@vardata void		ATTR_VIS_TEXT_DO_NOT_NOTIFY_CONTENT;
@vardata void		ATTR_VIS_TEXT_SEND_CONTEXT_NOTIFICATIONS_EVEN_IF_NOT_FOCUSED;
typedef struct {
    wchar		VTCFD_startOfRange;
    wchar		VTCFD_endOfRange;
} VisTextCustomFilterData;
@vardata ChunkHandle ATTR_VIS_TEXT_CUSTOM_FILTER;
@vardata word		ATTR_VIS_TEXT_CHAR_ATTR_INSERTION_TOKEN;
@vardata word		ATTR_VIS_TEXT_TYPE_INSERTION_TOKEN;
@vardata void		ATTR_VIS_TEXT_UPDATE_VIA_PROCESS;
@vardata void		ATTR_VIS_TEXT_DOES_NOT_ACCEPT_INK;
@vardata WBFixed		ATTR_VIS_TEXT_MINIMUM_SINGLE_LINE_HEIGHT;
@vardata word		ATTR_VIS_TEXT_ADD_SPACE_TO_ALL_LINES;
@vardata void		ATTR_VIS_TEXT_DONT_SHOW_POSITION_ON_GAINED_TARGET_EXCL;
@vardata void		ATTR_VIS_TEXT_SHOW_POSITION_ON_GAINED_TARGET_EXCL;
@vardata void		ATTR_VIS_TEXT_DONT_BEEP_ON_INSERTION_ERROR;

ATTR_VIS_TEXT_TYPE_RUNS specifies the Type runs for this text object. You should not need type runs unless you need hyperlink capabilities. ATTR_VIS_TEXT_GRAPHIC_RUNS specifies a graphics run to use within the text object.

ATTR_VIS_TEXT_REGION_ARRAY specifies the array of regions defined for this text object. Text within the VisText will flow from each of the defined regions to the next region. ATTR_VIS_TEXT_STYLE_ARRAY specifies the name array of styles used in the VisText's style sheets.

ATTR_VIS_TEXT_SELECTED_TAB specifies the position of the currently-selected tab, if any.

ATTR_VIS_TEXT_DO_NOT_INTERACT_WITH_SEARCH_CONTROL instructs the text object not to send the search-control notification.

ATTR_VIS_TEXT_EXTENDED_FILTERS stores the VisTextExtendedFilterType to use instead of the VisTextFilters stored within VTI_ filters . Extended filters offer more powerful (and complex) filtering mechanisms for accepting and rejecting text. Including this attribute will affect the performance of your text object, however.

ATTR_VIS_TEXT_SUSPEND_DATA stores data to be used by the text object during a SUSPEND/UNSUSPEND operation ( VisTextSuspendData ).

ATTR_VIS_TEXT_SEND_CONTEXT_NOTIFICATIONS_EVEN_IF_NOT_FOCUSED sends out context notification even if the object is not focused. Applications must make sure that objects with this attribute do not get modified while another text object has the focus.

ATTR_VIS_TEXT_UPDATE_VIA_PROCESS indicates that status updates must be sent via the process object (usually because there are targetable text objects in multiple threads).

ATTR_VIS_TEXT_DOES_NOT_ACCEPT_INK indicates that the VisText object will not accept ink.

ATTR_VIS_TEXT_MINIMUM_SINGLE_LINE_HEIGHT defines the height of one-line text objects so that custom graphics, etc. can be placed inside the text object.

ATTR_VIS_TEXT_ADD_SPACE_TO_ALL_LINES adds additional spacing above all lines in a text object, and it takes a word argument. This optional attribute is intended to be added to objects that use accent characters that may draw above the top of the text's "font box." This spacing value differs depending on the system that text is implemented on; only use this attribute if redraws are not drawing your text characters properly. This attribute will also affect any text lines that contain borders, adding additional space between the text and the border.

ATTR_VIS_TEXT_DONT_SHOW_POSITION_ON_GAINED_TARGET_EXCL and ATTR_VIS_TEXT_SHOW_POSITION_ON_GAINED_TARGET_EXCL affect the display of the cursor within a text object upon gaining the target exclusive. By default, a VisText object will display the cursor upon gaining the target; a VisLargeText object, on the other hand, will not display this position. You may use either of these hints to override this default behavior.

ATTR_VIS_TEXT_DONT_BEEP_ON_INSERTION_ERROR indicates that the VisText object should not call the default error sound routine in cases where an insertion error occurs.

VisText Features

VisText States

VisText VM File Storage

Text Filters

Key Functions

Setting Text Confines

Output Messages

Getting Geometry Information

The Text Objects: 5.1 Using VisText: VisText Features

VTI_features, VisTextFeatures, MSG_VIS_TEXT_SET_FEATURES, MSG_VIS_TEXT_GET_FEATURES

VTI_ features stores a bitfield of VisTextFeatures specifying whether a particular feature is enabled in the Text object. These flags are listed below.

VTF_NO_WORD_WRAPPING
If set, no word-wrapping is allowed. Characters added after the end of the line will start at the next line.
VTF_AUTO_HYPHENATE
If set, the text object will auto-hyphenate words that span a line break.
VTF_ALLOW_SMART_QUOTES
If set, smart quotes will be allowed if they are enabled.
VTF_ALLOW_UNDO
If set, undo operations will be allowed on the text object. (The text object will handle MSG_META_UNDO.)
VTF_SHOW_HIDDEN_TEXT
If set, hidden text will be displayed. This feature is currently not implemented.
VTF_OUTLINE_MODE
If set, text will be shown in outline mode. This feature is currently not implemented.
VTF_DONT_SHOW_SOFT_PAGE_BREAKS
If set, any soft page breaks (i.e. any page breaks that are not C_PAGE_BREAK) will be ignored.
VTF_DONT_SHOW_GRAPHICS
If set, any graphics in the text object will not be displayed.
VTF_TRANSPARENT
If set, the text object will not do a background wash behind the text after it draws.
VTF_USE_50_PCT_TEXT_MASK
If set, the text object will use a 50% draw mask when drawing, regardless of other character attribute runs. This is used by the GenText object to show a disabled state.
MSG_VIS_TEXT_SET_FEATURES
void	MSG_VIS_TEXT_SET_FEATURES(
        VisTextFeatures		bitsToSet,
        VisTextFeatures		bitsToClear);

This message sets a text object's VisTextFeatures (VTI_ features ).

Source: Unrestricted.

Destination: Any text object.

Parameters: bitsToSet VisTextFeatures to add.

bitsToClear
VisTextFeatures to remove.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_FEATURES
VisTextFeatures MSG_VIS_TEXT_GET_FEATURES();

This message retrieves the text object's VisTextFeatures (VTI_ features ).

Source: Unrestricted.

Destination: Any text object.

Parameters: None.

Return: VisTextFeatures in use by the text object.

Interception: Generally not intercepted.

The Text Objects: 5.2 Using VisText: VisText States

VTI_state, VisTextStates, MSG_VIS_TEXT_GET_STATE, MSG_VIS_TEXT_GET_USER_MODIFIED_STATE, MSG_VIS_TEXT_SET_NOT_USER_MODIFIED, MSG_VIS_TEXT_SET_USER_MODIFIED, MSG_VIS_TEXT_ENTER_OVERSTRIKE_MODE, MSG_VIS_TEXT_ENTER_INSERT_MODE

VTI_state stores a bitfield of VisTextStates that determine the ability of the user to interact with the text object. These flags are listed below.

VTS_EDITABLE
If set, the text object is editable. You must set this flag to allow the user to write text into a text object.
VTS_SELECTABLE
If set, text within the text object may be selected. Text may be selectable without it being editable; it can be copied but not cut in that case.
VTS_TARGETABLE
If set, the text object is capable of acting in the target hierarchy. This flag must be set for text objects that interact with controllers.
VTS_ONE_LINE
If set, this object is a simple one-line text object. The text will not scroll vertically, though it may scroll horizontally.
VTS_SUBCLASS_VIRT_PHYS_TRANSLATION
If set, the mapping of virtual attributes to physical attributes will be effected by sending a message (which can be subclassed) rather than through a routine. Normally, this mapping is done through the faster direct routine call, but this flag allows application-specific operations.
VTS_OVERSTRIKE_MODE
If set, the text object is in overstrike mode, not its default insertion mode. Text entered at the cursor will overwrite following text; it will not insert that text at the cursor position.
VTS_USER_MODIFIED
If set, the text inside the object has been changed since the last time the text object sent out its apply message in GTXI_ applyMsg . You can send the text object MSG_VIS_TEXT_SET_NOT_USER_MODIFIED or MSG_VIS_TEXT_SET_USER_MODIFIED to clear or set this flag.
MSG_VIS_TEXT_GET_STATE
VisTextStates MSG_VIS_TEXT_GET_STATE();

This message retrieves the text object's VisTextStates (VTI_state).

Source: Unrestricted.

Destination: Any text object.

Parameters: None.

Return: VisTextStates in use by the text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_USER_MODIFIED_STATE
Boolean	MSG_VIS_TEXT_GET_USER_MODIFIED_STATE();

This message returns the state of the text object's VTS_MODIFIED flag in its VTI_state entry.

Source: Unrestricted.

Destination: Any text object.

Parameters: None.

Return: true if object has been modified .

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_NOT_USER_MODIFIED
void	MSG_VIS_TEXT_SET_NOT_USER_MODIFIED();

This message clears the text object's VTS_USER_MODIFIED flag.

Source: Unrestricted.

Destination: Any text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_USER_MODIFIED
void	MSG_VIS_TEXT_SET_USER_MODIFIED();

This message sets the text object's VTS_USER_MODIFIED flag.

Source: Unrestricted.

Destination: Any text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_MODIFY_EDITABLE_SELECTABLE
void	MSG_VIS_TEXT_MODIFY_EDITABLE_SELECTABLE(
        VisTextStates		setBits,
        VisTextStates		clearBits);

This message modifies the text object's VTS_EDITABLE and VTS_SELECTABLE states. These are the only VisTextStates you should modify with this message.

Source: Unrestricted.

Destination: Any text object.

Parameters: setBits The VTS_EDITABLE and/or VTS_SELECTABLE states to set.

clearBits
The VTS_EDITABLE and/or VTS_SELECTABLE states to clear.

Interception: Generally not intercepted.

MSG_VIS_TEXT_ENTER_OVERSTRIKE_MODE
void	MSG_VIS_TEXT_ENTER_OVERSTRIKE_MODE(
        Boolean		calledFromTextObject);

This message enters the text object into overstrike mode (setting the VTS_OVERSTRIKE_MODE flag).

Source: Unrestricted.

Destination: Any text object.

Parameters: calledFromTextObject
true if this message was called internally by the text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_ENTER_INSERT_MODE
void	MSG_VIS_TEXT_ENTER_INSERT_MODE(
        Boolean		calledFromTextObject);

This message enters the text object into insert mode (clearing the VTS_OVERSTRIKE_MODE flag).

Source: Unrestricted.

Destination: Any text object.

Parameters: calledFromTextObject
true if this message was called internally by the text object.

Interception: Generally not intercepted.

The Text Objects: 5.3 Using VisText: VisText VM File Storage

VTI_vmFile, MSG_VIS_TEXT_SET_VM_FILE, MSG_VIS_TEXT_SAVE_TO_DB_ITEM, MSG_VIS_TEXT_SAVE_TO_DB_ITEM_WITH_STYLES, MSG_VIS_TEXT_LOAD_FROM_DB_ITEM, MSG_VIS_TEXT_LOAD_FROM_DB_ITEM_WITH_STYLES

VTI_ vmFile stores the VM file handle that is associated with this text object. This file is the default VM file that will be used for operations that require a storage area. You can set this file handle with MSG_VIS_TEXT_SET_VM_FILE.

To save a text object's current text (including attributes) you can send the text object MSG_VIS_TEXT_SAVE_TO_DB_ITEM. This message takes several VisTextSaveDBFlags which specify what aspects of the text to save to the DB item. These flags are listed below:

VTSDBF_TEXT
VTSDBF_CHAR_ATTR
VTSDBF_PARA_ATTR
VTSDBF_TYPE
VTSDBF_GRAPHIC
VTSDBF_STYLE
VTSDBF_REGION
VTSDBF_NAME

To retrieve text saved to a DB item in such a manner, use MSG_VIS_TEXT_LOAD_FROM_DB_ITEM. The text object is able to figure out what features of the text were saved and bring it up with any attributes that were saved previously.

MSG_VIS_TEXT_SET_VM_FILE
void	MSG_VIS_TEXT_SET_VM_FILE(
        VMFileHandle		file);

This message sets the text object's VTI_ vmFile instance field to the passed VM file handle. This file handle will be used as the default for operations that require a VM file.

Source: Unrestricted.

Destination: Any text object.

Parameters: file VM file handle.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SAVE_TO_DB_ITEM
DBGroupAndItem MSG_VIS_TEXT_SAVE_TO_DB_ITEM(
        DBGroupAndItem		item,
        VisTextSaveDBFlags		flags);

This message saves information about the current state of the text object. Data saved with this message can be retrieved with MSG_VIS_TEXT_LOAD_FROM_DB_ITEM.

Source: Unrestricted.

Destination: Any text object.

Parameters: item DB group and item to store the text information. If this is left zero, the info will be saved to the VM file specified in VTI_ vmFile .

flags
VisTextSaveDBFlags specifying what aspect of the text's info to save.

Return: DBItem of saved text info.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SAVE_TO_DB_ITEM_WITH_STYLES
DBGroupAndItem MSG_VIS_TEXT_SAVE_TO_DB_ITEM_WITH_STYLES(
        FileHandle		xferFile,
        VisTextSaveDBFlags		flags,
        DBGroupAndItem		item,
        StyleSheetParams		*params);

This message saves information about the current state of the text object, passing a StyleSheetParams structure.

VTST_RUNS_ONLY should be passed in the VisTextSaveDBFlags entries VTSDBF_CHAR_ATTR and VTSDBF_PARA_ATTR. Do not set VTSDBF_STYLE.

Source: Unrestricted.

Destination: Any text object.

Parameters: xferFile If non-zero, the StyleSheetParams structure is not initialized and this is the file to put the style sheet arrays into.

flags
VisTextSaveDBFlags specifying what aspect of the text's info to save.
item
DB group and item to store the text information. If this is left zero, the info will be saved to the VM file specified in VTI_ vmFile .
* params
Pointer to a StyleSheetParams structure.

Interception: Generally not intercepted.

MSG_VIS_TEXT_LOAD_FROM_DB_ITEM
void	MSG_VIS_TEXT_LOAD_FROM_DB_ITEM(
        DBGroupAndItem		item,
        VMFileHandle		file);

This message loads text saved with MSG_VIS_TEXT_SAVE_TO_DB_ITEM.

Source: Unrestricted.

Destination: Any text object.

Parameters: item DBItem to load.

file
Handle of the VM file to use. If zero, the VM file in VTI_ vmFile will be used.

Interception: Generally not intercepted.

MSG_VIS_TEXT_LOAD_FROM_DB_ITEM_WITH_STYLES
void	MSG_VIS_TEXT_LOAD_FROM_DB_ITEM_WITH_STYLES(
        FileHandle		file,
        DBGroupAndItem		item,
        StyleSheetParams		*params);

This message loads text and style sheets saved with MSG_VIS_TEXT_SAVE_TO_DB_ITEM_WITH_STYLES.

Source: Unrestricted.

Destination: Any text object.

Parameters: file Handle of the VM file to use. If zero, the VM file in VTI_ vmFile will be used.

item
DBItem to load.
* params
Pointer to a StyleSheetParams structure.

Interception: Generally not intercepted.

The Text Objects: 5.4 Using VisText: Text Filters

VTI_filters, VisTextFilters, VisTextFilterClass, ATTR_VIS_TEXT_EXTENDED_FILTER, MSG_VIS_TEXT_SET_FILTER, MSG_VIS_TEXT_GET_FILTER

When the user enters text into the text object, the text object is able to filter out characters that it does not wish to be displayed. This behavior is produced through use of VisTextFilters .

VTI_ filters stores a VisTextFilters record. This record has several entries:

VTF_NO_SPACES filters out any space characters.
VTF_NO_TABS filters out any tab characters.

VTF_UPCASE_CHARS ensures that any alphabetic characters are made uppercase. Other characters are unaffected.

VTF_FILTER_CLASS stores a VisTextFilterClass . This value can be any one of the following:

VTFC_NO_FILTER
VTFC_ALPHA
VTFC_NUMERIC
VTFC_SIGNED_NUMERIC
VTFC_SIGNED_DECIMAL
VTFC_FLOAT_DECIMAL
VTFC_ALPHA_NUMERIC
VTFC_FILENAMES
VTFC_DOS_FILENAMES
VTFC_DOS_PATH
VTFC_DATE
VTFC_TIME
VTFC_DASHED_ALPHA_NUMERIC
VTFC_NORMAL_ASCII
VTFC_DOS_VOLUME_NAMES
VTFC_DOS_CHARACTER_SET
VTFC_ALLOW_COLUMN_BREAKS

For more complex filtering, you can include ATTR_VIS_TEXT_EXTENDED_FILTER in your text object. Each value corresponds to a different message which you can intercept to provide custom filtering. These values are:

VTEFT_REPLACE_PARAMS
This filter generates MSG_VIS_TEXT_FILTER_VIA_REPLACE_PARAMS. This message passes a VisTextReplaceParameters structure that you can either accept or reject as a whole. This will be supported in an upcoming release.
VTEFT_CHARACTER_LEVELER_LEVEL
This filter generates MSG_VIS_TEXT_FILTER_VIA_CHARACTER. This message passes a single character that you can either accept or reject.
VTEFT_BEFORE_AFTER
This filter generates MSG_VIS_TEXT_FILTER_VIA_BEFORE_AFTER. This message passes two buffers containing the current text and the proposed new text. This will be supported in an upcoming release.
MSG_VIS_TEXT_GET_FILTER
byte	MSG_VIS_TEXT_GET_FILTER();

This message retrieves the current VisTextFilters in use by the text object (stored in VTI_ filters ).

Source: Unrestricted.

Destination: Any text object.

Return: VisTextFilters in use by the text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_FILTER
void	MSG_VIS_TEXT_SET_FILTER(
        byte		filter);

This message sets the text object's VisTextFilters (stored in VTI_ filters ) to the passed value. Resetting this filter does not affect any text that already exists in the text object.

Source: Unrestricted.

Destination: Any text object.

Parameters: filter VisTextFilters to set.

Interception: Generally not intercepted.

MSG_VIS_TEXT_FILTER_VIA_CHARACTER
word	MSG_VIS_TEXT_FILTER_VIA_CHARACTER(
        word		charToFilter);

This message allows the text object to filter text on a character by character basis. This message is called for each character that is entered into the text object.

This message will be generated if the text object includes ATTR_VIS_TEXT_EXTENDED_FILTER in its instance data, with its value set to the VisTextExtendedFilterType VTEFT_CHARACTER_LEVELER_LEVEL.

Source: Sent by the system if text object contains ATTR_VIS_TEXT_EXTENDED_FILTER set to VTEFT_CHARACTER_LEVELER_LEVEL.

Destination: Any text object

Parameters: charToFilter Character value that is being filtered. You can pass this value on to the superclass or filter it out.

Interception: Must intercept to provide character by character filtering.

The Text Objects: 5.5 Using VisText: Key Functions

VisTextKeyFunction, MSG_VIS_TEXT_DO_KEY_FUNCTION

The VisText object is able to recognize certain generic functions that operate on text. Most of these functions are initiated through a key sequence entered by the user, but you can mimic such a key sequence by sending the text object MSG_VIS_TEXT_DO_KEY_FUNCTION, passing it a VisTextKeyFunction .

The following VisTextKeyFunction values are recognized by the text object:

VTKF_FORWARD_LINE
VTKF_BACKWARD_LINE
VTKF_SELECT_ADJUST_FORWARD_LINE
VTKF_SELECT_ADJUST_BACKWARD_LINE
VTKF_FORWARD_CHAR
VTKF_BACKWARD_CHAR
VTKF_FORWARD_WORD
VTKF_BACKWARD_WORD
VTKF_FORWARD_PARAGRAPH
VTKF_BACKWARD_PARAGRAPH
VTKF_START_OF_LINE
VTKF_END_OF_LINE
VTKF_START_OF_TEXT
VTKF_END_OF_TEXT
VTKF_SELECT_WORD
VTKF_SELECT_LINE
VTKF_SELECT_PARAGRAPH
VTKF_SELECT_OBJECT
VTKF_SELECT_ADJUST_FORWARD_CHAR
VTKF_SELECT_ADJUST_BACKWARD_CHAR
VTKF_SELECT_ADJUST_FORWARD_WORD
VTKF_SELECT_ADJUST_BACKWARD_WORD
VTKF_SELECT_ADJUST_FORWARD_PARAGRAPH
VTKF_SELECT_ADJUST_BACKWARD_PARAGRAPH
VTKF_SELECT_ADJUST_TO_START
VTKF_SELECT_ADJUST_TO_END
VTKF_SELECT_ADJUST_START_OF_LINE
VTKF_SELECT_ADJUST_END_OF_LINE
VTKF_DELETE_BACKWARD_CHAR
VTKF_DELETE_BACKWARD_WORD
VTKF_DELETE_BACKWARD_LINE
VTKF_DELETE_BACKWARD_PARAGRAPH
VTKF_DELETE_TO_START
VTKF_DELETE_CHAR
VTKF_DELETE_WORD
VTKF_DELETE_LINE
VTKF_DELETE_PARAGRAPH
VTKF_DELETE_TO_END
VTKF_DELETE_EVERYTHING
VTKF_DESELECT
VTKF_TOGGLE_OVERSTRIKE_MODE
VTKF_TOGGLE_SMART_QUOTES
MSG_VIS_TEXT_DO_KEY_FUNCTION
void	MSG_VIS_TEXT_DO_KEY_FUNCTION
        VisTextKeyFunction			func,
        word			data);

This message allows the text object to mimic certain text operations that normally are the result of user keystrokes.

Note: the VisTextKeyFunction types VTKF_SELECT_LINE, VTKF_SELECT_PARAGRAPH, and VTKF_SELECT_OBJECT will be supported in an upcoming release.

Source: Unrestricted.

Destination: Any text object

Parameters: func VisTextKeyFunction to perform.

data
Data for that key function (if needed).

Interception: Generally not intercepted.

The Text Objects: 5.6 Using VisText: Setting Text Confines

MSG_VIS_TEXT_GET_MAX_LENGTH, MSG_VIS_TEXT_SET_MAX_LENGTH, MSG_VIS_TEXT_GET_LR_MARGIN, MSG_VIS_TEXT_SET_LR_MARGIN, MSG_VIS_TEXT_GET_TB_MARGIN, MSG_VIS_TEXT_SET_TB_MARGIN
MSG_VIS_TEXT_GET_MAX_LENGTH
word	MSG_VIS_TEXT_GET_MAX_LENGTH();

This message returns the maximum length of text in the text object (stored in VTI_ maxLength ).

Source: Unrestricted.

Destination: Any text object.

Return: Maximum length of the text, in characters.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_MAX_LENGTH
void	MSG_VIS_TEXT_SET_MAX_LENGTH(
        word		newMaxLength);

This message sets the maximum length of text within a text object (stored in VTI_ maxLength ). If the current text is too long, any excess text is removed.

Source: Unrestricted.

Destination: Any text object (except VisLargeTextClass ).

Parameters: newMaxLength Maximum length of the text object (in characters).

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_LR_MARGIN
byte	MSG_VIS_TEXT_GET_LR_MARGIN();

This message returns the current left/right margins. Both margins are the same and are thus represented by the same returned value.

Source: Unrestricted.

Destination: VisTextClass objects (not GenText objects).

Return: The current left/right margin.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_LR_MARGIN
void	MSG_VIS_TEXT_SET_LR_MARGIN(
        byte		lrMargin);

This message sets the left/right margins of the text object. Both margins will be set to the same value. Do not use this message on GenText objects, as the specific UI is responsible for this behavior.

Source: Unrestricted.

Destination: VisTextClass objects (not GenText objects).

Parameters: lrMargin New left/right margins.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_TB_MARGIN
byte	MSG_VIS_TEXT_GET_TB_MARGIN();

This message returns the current top/bottom margins. Both margins are the same and are thus represented by the same returned value.

Source: Unrestricted.

Destination: VisTextClass objects (not GenText objects).

Return: The current top/bottom margin.

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_TB_MARGIN
void	MSG_VIS_TEXT_SET_TB_MARGIN(
        byte		tbMargin);

This message sets the top/bottom margins of the text object. Both margins will be set to the same value. Do not use this message on GenText objects, as the specific UI is responsible for this behavior.

Source: Unrestricted.

Destination: VisTextClass objects (not GenText objects).

Parameters: tbMargin New top/bottom margins.

Interception: Generally not intercepted.

The Text Objects: 5.7 Using VisText: Output Messages

VTI_output, MSG_VIS_TEXT_GET_OUTPUT, MSG_VIS_TEXT_SET_OUTPUT, MSG_META_TEXT_USER_MODIFIED, MSG_META_TEXT_NOT_USER_MODIFIED,MSG_META_TEXT_CR_FILTERED,MSG_META_TEXT_TAB_FILTERED, MSG_META_TEXT_LOST_FOCUS, MSG_META_TEXT_GAINED_FOCUS, MSG_META_TEXT_LOST_TARGET, MSG_META_TEXT_GAINED_TARGET, MSG_META_TEXT_EMPTY_STATUS_CHANGED

VTI_ output stores the object to receive messages sent out by the text object. A range of messages imported from MetaClass ( MetaTextMessages ) are sent to this destination optr. These messages are listed below:

MSG_META_TEXT_USER_MODIFIED is sent to VTI_ output when the user first modifies text within the object. This message is also sent to the text object itself to set its VTS_MODIFIED state (after it is sent to VTI_ output ). If the text object's VTS_MODIFIED bit is later cleared, it is sent MSG_META_TEXT_NOT_USER_MODIFIED (and may receive MSG_META_TEXT_USER_MODIFIED again).

MSG_META_TEXT_CR_FILTERED is sent when a Carriage Return is filtered out of the text input by the text object's filtering mechanism. This message is first sent to the text object itself.

MSG_META_TEXT_TAB_FILTERED is sent when a Tab is filtered out of the text input by the text object's filtering mechanism. This message is first sent to the text object itself.

MSG_META_TEXT_LOST_FOCUS is sent when the text object loses the focus. MSG_META_TEXT_GAINED_FOCUS is sent when the text object gains the focus.

MSG_META_TEXT_LOST_TARGET is sent when the text object loses the target. MSG_META_TEXT_GAINED_TARGET is sent when the text object gains the target.

MSG_META_TEXT_EMPTY_STATUS_CHANGED is sent when the text object either changes from being empty to being non-empty or vice-versa.

To retrieve the current output of the text object, send it MSG_VIS_TEXT_GET_OUTPUT. To change the current output, send it MSG_VIS_TEXT_SET_OUTPUT.

MSG_META_TEXT_USER_MODIFIED
@importMessage MetaTextMessages, void MSG_META_TEXT_USER_MODIFIED(
        optr		obj);

MSG_META_TEXT_USER_MODIFIED is sent to VTI_ output when the user modifies text within the object. The message is also sent to the text object itself (to mark itself modified) after it is sent to the destination, so the state of the text's VTS_MODIFIED bit may not accurately reflect the state of the text object. If you need to intercept this message and also need to perform an operation using the text object's modified state.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data. The message is also sent to the text object itself.

Parameters: obj Optr of the text object modified.

Interception: May intercept to receive notification of user modification of text. Because modification of the text object's VTS_MODIFIED bit may be asynchronous, you should send any operations that depend on this information (such as clearing the VTS_MODIFIED bit) to the text object using MF_FORCE_QUEUE

MSG_META_TEXT_NOT_USER_MODIFIED
@importMessage MetaTextMessages, void MSG_META_TEXT_NOT_USER_MODIFIED(
        optr		obj);

This message is sent to VTI_ output when the VTS_MODIFIED state of the text object is cleared, either by the system, or by an application inspired action.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data. The message is also sent to the text object itself first.

Parameters: obj Optr of the text object marked not modified.

Interception: Intercept to receive notification of when a text object is marked not modified.

MSG_META_TEXT_CR_FILTERED
@importMessage MetaTextMessages, void MSG_META_TEXT_CR_FILTERED(
        word		character,
        word		flags,
        word		state);

This message is sent to VTI_ output when a Carriage Return is filtered out in the text input stream.

Source: Sent by the system when a CR is filtered out of the text object.

Destination: VTI_ output of the text object's instance data. The message is also sent to the text object itself.

Parameters: character The character value (CR).

flags
ShiftState and CharFlags .
state
The state of the text object (VTI_ state ) at the time of the filtering action.

Interception: Intercept to receive notification that a Carriage Return has been filtered out of the text object.

MSG_META_TEXT_TAB_FILTERED
@importMessage MetaTextMessages, void MSG_META_TEXT_TAB_FILTERED(
        word		character,
        word		flags,
        word		state);

This message is sent to VTI_ output when a Tab is filtered out in the text input stream.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data. The message is also sent to the text object itself.

Parameters: character The character value (Tab).

flags
ShiftState and CharFlags .
state
The state of the text object (VTI_ state ) at the time of the filtering action.

Interception: Intercept to receive notification that a Tab has been filtered out of the text object.

MSG_META_TEXT_LOST_FOCUS
@importMessage MetaTextMessages, void MSG_META_TEXT_LOST_FOCUS(
        optr		obj);

This message is sent when the text object loses the focus of the application.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data.

Parameters: obj Optr of the text object.

Interception: Intercept to receive notification of when the text object loses the focus.

MSG_META_TEXT_GAINED_FOCUS
@importMessage MetaTextMessages, void MSG_META_TEXT_GAINED_FOCUS(
        optr		obj);

This message is sent when the text object gains the focus of the application.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data.

Parameters: obj Optr of the text object.

Interception: Intercept to receive notification of when the text object gains the focus.

MSG_META_TEXT_LOST_TARGET
@importMessage MetaTextMessages, void MSG_META_TEXT_LOST_TARGET(
        optr		obj);

This message is sent when the text object loses the target of an application.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data.

Parameters: obj Optr of the text object.

Interception: Intercept to receive notification of when the text object loses the target.

MSG_META_TEXT_GAINED_TARGET
@importMessage MetaTextMessages, void MSG_META_TEXT_GAINED_TARGET(
        optr		obj);

This message is sent when the text object gains the target of an application.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data.

Parameters: obj Optr of the text object.

Interception: Intercept to receive notification of when the text object gains the target.

MSG_META_TEXT_EMPTY_STATUS_CHANGED
@importMessage MetaTextMessages, void MSG_META_TEXT_EMPTY_STATUS_CHANGED(
        optr		object,
        Boolean		hasTextFlag);

This message is sent out when the text object is either becoming empty or not empty.

Source: Sent by the system when the user modifies the text object.

Destination: VTI_ output of the text object's instance data. The message is also sent to the text object itself first.

Parameters: object Optr of the text object whose empty status is changing.

hasTextFlag
Non-zero if the text object is becoming non-empty.

Interception: Intercept to receive notification when the empty status of the text object is changing.

MSG_VIS_TEXT_GET_OUTPUT
optr	MSG_VIS_TEXT_GET_OUTPUT();

This message returns the current destination (output) for actions taken by the text object.

Source: Unrestricted.

Destination: Any text object.

Return: Optr of the object's destination (in the VTI_ output field).

Interception: Generally not intercepted.

MSG_VIS_TEXT_SET_OUTPUT
void	MSG_VIS_TEXT_SET_OUTPUT(
        optr		newOutput);

This message sets the destination object for actions taken by this text object.

Source: Unrestricted.

Destination: Any text object.

Parameters: newOutput New destination object (placed in object's VTI_ output field).

Interception: Generally not intercepted.

The Text Objects: 5.8 Using VisText: Getting Geometry Information

MSG_VIS_TEXT_GET_MIN_WIDTH, MSG_VIS_TEXT_GET_AVERAGE_CHAR_WIDTH, MSG_VIS_TEXT_GET_LINE_HEIGHT, MSG_VIS_TEXT_RECALC_AND_DRAW, MSG_VIS_TEXT_GET_ONE_LINE_WIDTH, MSG_VIS_TEXT_GET_SIMPLE_MIN_WIDTH
MSG_VIS_TEXT_GET_MIN_WIDTH
word	MSG_VIS_TEXT_GET_MIN_WIDTH();

This message returns the minimum width that can be supported for attributes of the text object.

Source: Unrestricted.

Destination: Any text object.

Return: Minimum width (in points) that can be supported.

MSG_VIS_TEXT_GET_AVERAGE_CHAR_WIDTH
word	MSG_VIS_TEXT_GET_AVERAGE_CHAR_WIDTH();

This message returns the average character width for the first font/character attribute run.

Source: Unrestricted.,

Destination: Any text object.

Return: Average character width (in points).

MSG_VIS_TEXT_GET_LINE_HEIGHT
word	MSG_VIS_TEXT_GET_LINE_HEIGHT();

This message returns the height of a line (in points) for a single-line text object only .

Source: Unrestricted.

Destination: Single-line text objects that are not within document objects.

Return: Height of line (in points).

MSG_VIS_TEXT_RECALC_AND_DRAW
void	MSG_VIS_TEXT_RECALC_AND_DRAW();

This message recalculates and redraws a text object, usually after constraints on either its width or its height is made without its knowledge.

Source: Unrestricted.

Destination: Any text object.

Interception: Generally not intercepted.

MSG_VIS_TEXT_GET_ONE_LINE_WIDTH
word	MSG_VIS_TEXT_GET_ONE_LINE_WIDTH(
        word		charsToCalc);

This message calculates the width of a single-line text object. This width is calculated using the first encountered character attributes.

Source: Unrestricted.

Destination: Any single-line text object.

Parameters: charsToCalc Number of characters (from start of text) to use in the calculation.

Return: Width of the text.

MSG_VIS_TEXT_GET_SIMPLE_MIN_WIDTH
word	MSG_VIS_TEXT_GET_SIMPLE_MIN_WIDTH();

This message returns the minimum width that can be supported for the text object's current text, font and character attributes. This message assumes that the caller is not worried about keeping a width for the largest border possible.

Source: Unrestricted.

Destination: Any text object.

Return: Minimum width which can be supported.

The Text Objects: 6 Using GenText

GenTextClass provides your application with a generic UI object to both display and edit text. The GenText object is a highly functional object, capable of displaying text to the user under a variety of constraints. The object's role is primarily to display text pertaining to user interface operations. The object also provides a wide array of text processing features, however, to display the text in different formats. For more powerful and diverse text formatting (such as word-processing), you should probably use a VisText object instead.

The GenText has many advanced features of the Text Object Library built in. Most of these features may not be useful for cases where you will most likely use a GenText object; GenText is designed for simpler text edit capabilities than incorporating graphics into text, for example. Still, the GenText object provides a great deal of default behavior and, with a little work, a highly powerful and specifically tailored text object.

The GenText object may not only show text to the user but may also allow the user to supply or edit the displayed text. For example, a file copy operation may request the new name to write the file to; a GenText object would allow the user to enter this file name within its text field.

The text object library offers the following key features:

  • Display-only and editable text fields.
  • Automatic keyboard support, allowing the user to immediately begin entering text into the GenText's text field.
  • Automatic word wrapping from one line to the next (or the capability to disable this option).
  • Vertical scrolling of text (providing scrollbars either initially or when needed).
  • Selection of text, usually represented by highlighting.
  • The specification of actions to perform when the user completes text edits.
  • Text character styles to display the text. Sample character styles are bold, underline, strike-through, etc.
  • Paragraph styles (rulers) to display the text. Sample paragraph styles are right-justified, paragraph spacing, hidden text, etc.
  • Fonts to display the text.
  • Ability to store the text in a variety of data structures.
  • Interaction with the text controllers.

GenText inherits most of its capabilities from VisTextClass which in turn uses the text object library. You may wish to peruse the structure of this library if you want to know more details of how the GenText performs its default behavior.

GenText is used primarily in three situations: First, when you need to display text (such as in a "help" window), the GenText object can act as a non-editable text field with its own scrolling and geometry management. Second, when you need to get textual input not related to word processing or text editing (such as a file name), the GenText can act as an editable field of one or more lines. Third, when simple word processing or text editing in the generic tree is required (as in the NotePad application), the GenText can provide a scrollable, sizable text-edit field. If you want, the editable GenText can even support advanced paragraph and text formatting.

VisText is used in most situations where complex or advanced formatting or publishing features are desired. Typically, a GenView and a VisContent will be used to provide the root of the visible tree and its interaction with the UI. The VisText is much more flexible than the GenText and allows complex publishing and page layout features that would be difficult with GenText. VisText also allows you to have the same interface no matter what specific UI is in use since it is a visible object and not subject to the constraints of the specific UI.

GenText Instance Data

GenText Basics

The Text Objects: 6.1 Using GenText: GenText Instance Data

The GenText object has several instance fields which affect the visual presentation of text. These are listed in GenText Instance Data .

Code Display 10-21 GenText Instance Data

 @instance ChunkHandle				GTXI_text;
@instance word				GTXI_maxLength = 32767;
@instance GenTextAttrs				GTXI_attrs = GTA_USE_TAB_FOR_NAVIGATION;
@instance GenTextStateFlags				GTXI_stateFlags = 0;
@instance optr				GTXI_destination;
@instance word				GTXI_applyMsg = 0;
/* GenTextAttrs */
typedef ByteFlags GenTextAttrs;
#define GTA_SINGLE_LINE_TEXT					0x80
#define GTA_USE_TAB_FOR_NAVIGATION					0x40
#define GTA_INIT_SCROLLING					0x20
#define GTA_NO_WORD_WRAPPING					0x10
#define GTA_ALLOW_TEXT_OFF_END					0x08
#define GTA_TAIL_ORIENTED					0x04
#define GTA_DONT_SCROLL_TO_CHANGES					0X02
/* GenTextStateFlags */
typedef ByteFlags GenTextStateFlags
#define GTSF_INDETERMINATE					0x80
#define GTSF_MODIFIED					0x40

GTXI_ text stores the ChunkHandle of the chunk containing the GenText's text. This chunk will be stored in the same resource as the text object. You may alter the text within this chunk with any of the previously mentioned VisTextClass messages. See The Text for a full discussion of how to manipulate and alter text.

GTXI_ maxLength specifies the maximum number of character positions allowed within the GenText. By default, up to 32767 characters may appear within a GenText. If you set GTXI_ maxLength to a lower number, any characters beyond the maximum length will be ignored.

GTXI_ attrs specifies the GenTextAttrs to set for the GenText object. These attributes affect the presentation of the text.

GTXI_stateFlags specifies the GenTextStateFlags to use with this text object. These state flags affect the modified and indeterminate states of the object.

GTXI_ applyMsg sets the message for this object to send out whenever activated. In most cases, your handler for this message may then retrieve the text and perform whatever required operations on that text.

GTXI_ destination sets the object or process to handle apply messages sent out by this object.

Code Display 10-22 GenText VarData Instance Fields

@vardata Message			ATTR_GEN_TEXT_STATUS_MSG;
@vardata void			ATTR_GEN_TEXT_SET_MODIFIED_ON_REDUNDANT_SELECTION;
@vardata void			ATTR_GEN_TEXT_SELECTABLE;
@vardata VisTextDefaultCharAttr ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR;
@vardata VisTextDefaultParaAttr ATTR_GEN_TEXT_DEFAULT_PARA_ATTR;
@vardata ChunkHandle			ATTR_GEN_TEXT_CHAR_ATTR;
@vardata ChunkHandle			ATTR_GEN_TEXT_MULTIPLE_CHAR_ATTR_RUNS;
@vardata ChunkHandle			ATTR_GEN_TEXT_PARA_ATTR;
@vardata ChunkHandle			ATTR_GEN_TEXT_MULTIPLE_PARA_ATTR_RUNS;
@vardata word			ATTR_GEN_TEXT_EXTENDED_FILTER;
@vardata word			ATTR_GEN_TEXT_TYPE_RUNS;
@vardata word			ATTR_GEN_TEXT_GRAPHIC_RUNS;
@vardata word			ATTR_GEN_TEXT_REGION_ARRAY;
@vardata word			ATTR_GEN_TEXT_STYLE_ARRAY;
@vardata word			ATTR_GEN_TEXT_NAME_ARRAY;
@vardata optr			ATTR_GEN_TEXT_RUNS_ITEM_GROUP;
    @reloc		ATTR_GEN_TEXT_RUNS_ITEM_GROUP;
@vardata void			ATTR_GEN_TEXT_DO_NOT_INTERACT_WITH_SEARCH_CONTROL;
@vardata optr			ATTR_GEN_TEXT_SET_OBJECT_ENABLED_WHEN_TEXT_EXISTS;
@vardata void			ATTR_GEN_TEXT_LEGAL_DOS_PATH;
@vardata void			ATTR_GEN_TEXT_DATE;
@vardata void			ATTR_GEN_TEXT_TIME;
@vardata void			ATTR_GEN_TEXT_MAKE_UPPERCASE;
@vardata void			ATTR_GEN_TEXT_ALLOW_COLUMN_BREAKS;
@vardata void			ATTR_GEN_TEXT_UPPERCASE_ALPHA;
@vardata void			ATTR_GEN_TEXT_UPPERCASE_ALPHA_NUMERIC;
@vardata void			ATTR_GEN_TEXT_DASHED_ALPHA_NUMERIC;
@vardata void			ATTR_GEN_TEXT_NORMAL_ASCII;
@vardata void			ATTR_GEN_TEXT_LEGAL_DOS_VOLUME_NAMES;
@vardata void			ATTR_GEN_TEXT_DOS_CHARACTER_SET;
@vardata void			ATTR_GEN_TEXT_NO_SPACES;
@vardata void			ATTR_GEN_TEXT_ALLOW_SPACES;
@vardata void			ATTR_GEN_TEXT_ALPHA;
@vardata void			ATTR_GEN_TEXT_NUMERIC;
@vardata void			ATTR_GEN_TEXT_SIGNED_NUMERIC;
@vardata void			ATTR_GEN_TEXT_SIGNED_DECIMAL;
@vardata void			ATTR_GEN_TEXT_FLOAT_DECIMAL;
@vardata void			ATTR_GEN_TEXT_ALPHA_NUMERIC;
@vardata void			ATTR_GEN_TEXT_LEGAL_FILENAMES;
@vardata void			ATTR_GEN_TEXT_LEGAL_DOS_FILENAMES;
@vardata void			ATTR_GEN_TEXT_NEVER_MAKE_SCROLLABLE;
@vardata void			ATTR_GEN_TEXT_DOES_NOT_ACCEPT_INK;
@vardata void			ATTR_GEN_TEXT_CURSOR_NO_FOCUS;
@vardata ChunkHandle			ATTR_GEN_TEXT_CUSTOM_FILTER;

ATTR_GEN_TEXT_STATUS_MSG sets a status message for this GenText. A status message allows the GenText to send notification other than its apply message.

ATTR_GEN_TEXT_SET_MODIFIED_ON_REDUNDANT_SELECTION sets the text dirty (modified) on any modification, even if it does not change the text or the text's attributes.

ATTR_GEN_TEXT_SELECTABLE should be set if text should be selectable whether or not the text is editable. By default non-editable text is not selectable. Editable text is always selectable.

ATTR_GEN_TEXT_DEFAULT_CHAR_ATTR stores singular default character attributes of type VisTextDefaultCharAttr in a word-length record. ATTR_GEN_TEXT_DEFAULT_PARA_ATTR stores singular default paragraph attributes of type VisTextDefaultParaAttr in a word-length record.

ATTR_GEN_TEXT_CHAR_ATTR stores the singular character attributes for the text object. This vardata field stores the ChunkHandle to a single VisTextCharAttr structure.

ATTR_GEN_TEXT_MULTIPLE_CHAR_ATTR_RUNS stores the multiple character attributes for the text object. This vardata field will store the ChunkHandle to a chunk array of character style runs (each of type VisTextCharAttr ). See Character Runs .

ATTR_GEN_TEXT_PARA_ATTR stores the singular paragraph attributes for the text object. This vardata field stores the ChunkHandle to a single VisTextParaAttr structure.

ATTR_GEN_TEXT_MULTIPLE_PARA_ATTR_RUNS stores the multiple paragraph attributes for the text object. This vardata field will store the ChunkHandle to a chunk array of paragraph style runs (each of type VisTextParaAttr ). See Paragraph Runs .

ATTR_GEN_TEXT_EXTENDED_FILTER stores the VisTextExtendedFilterTypes to use instead of the default VisTextFilters . Extended filters offer more powerful (and complex) filtering mechanisms for accepting and rejecting text into the GenText.

ATTR_GEN_TEXT_TYPE_RUNS specifies the type runs for this text object. You should not need type runs unless you need hyperlink capabilities. ATTR_GEN_TEXT_GRAPHIC_RUNS specifies a graphics run to use within the text object.

ATTR_GEN_TEXT_REGION_ARRAY specifies the array of regions defined for this text object. Text within the GenText will flow from each of the defined regions to the next region. ATTR_GEN_TEXT_STYLE_ARRAY specifies the name array of styles used in the GenText's style sheets.

ATTR_GEN_TEXT_RUNS_ITEM_GROUP indicates that this text object is linked to an item group (list). When the user hits Return (or the equivalent), the text object sends a message to the item group; similarly, the item group will attempt to update the text object on changes.

ATTR_GEN_TEXT_DO_NOT_INTERACT_WITH_SEARCH_CONTROL indicates that the text object should not send notification to the search control object.

ATTR_GEN_TEXT_SET_OBJECT_ENABLED_WHEN_TEXT_EXISTS makes the specified object enabled any time the GenText object has text in it.

ATTR_GEN_TEXT_LEGAL_DOS_PATH makes sure that only legal DOS paths are used.

ATTR_GEN_TEXT_DATE and ATTR_GEN_TEXT_TIME allows short date and time characters and spaces; the text object will be affected by the localization driver.

ATTR_GEN_TEXT_MAKE_UPPERCASE indicates that in addition to any other filters, any alphabetic characters are forced into uppercase.

ATTR_GEN_TEXT_ALLOW_COLUMN_BREAKS allows all characters including column breaks. Column breaks are normally filtered out in a GenText object. The column breaks usually will appear as new lines (like carriage returns) in the text object.

ATTR_GEN_TEXT_UPPERCASE_ALPHA allows alphabetic characters and spaces. ATTR_GEN_TEXT_UPPERCASE_ALPHA_NUMERIC , an allows only uppercase alphabetic characters and spaces. ATTR_GEN_TEXT_DASHED_ALPHA_NUMERIC allows only uppercase alphabetic characters and spaces but also allows dashes.

ATTR_GEN_TEXT_NORMAL_ASCII allows only normal ASCII characters (no extended ASCII characters).

ATTR_GEN_TEXT_LEGAL_DOS_VOLUME_NAMES allows only characters that may appear in a legal DOS volume name.

ATTR_GEN_TEXT_DOS_CHARACTER_SET allows only characters in the legal DOS character set.

ATTR_GEN_TEXT_NO_SPACES and ATTR_GEN_TEXT_ALLOW_SPACES govern whether spaces are allowed in the text object.

ATTR_GEN_TEXT_ALPHA , ATTR_GEN_TEXT_NUMERIC , ATTR_GEN_TEXT_SIGNED_NUMERIC , ATTR_GEN_TEXT_SIGNED_DECIMAL , ATTR_GEN_TEXT_FLOAT_DECIMAL , and ATTR_GEN_TEXT_ALPHA_NUMERIC govern the character types allowed.

ATTR_GEN_TEXT_LEGAL_FILENAMES allows any characters legal to file names.

ATTR_GEN_TEXT_NEVER_MAKE_SCROLLABLE forces the text object to expand larger as the text grows without forcing it into scrollable text.

ATTR_GEN_TEXT_DOES_NOT_ACCEPT_INK indicates that this text object will not accept ink under any circumstances.

ATTR_GEN_TEXT_CURSOR_NO_FOCUS causes the text object to have a cursor whether it has the focus or not.

ATTR_GEN_TEXT_CUSTOM_FILTER contains the ChunkHandle of an array of VisTextCustomFilterData structures. Each of these structures contains a range of character values to filter out. These values are determined from the Chars enumerated type. The text object figures out the number of filters in place by getting the size of the chunk.

Code Display 10-23 GenText Hints

@vardata ColorQuad			 HINT_TEXT_WASH_COLOR
@vardata void			HINT_TEXT_WHITE_WASH_COLOR;
@vardata void			HINT_TEXT_AUTO_HYPHENATE;
@vardata void			HINT_TEXT_SELECT_TEXT;
@vardata void			HINT_TEXT_CURSOR_AT_START;
@vardata void			HINT_TEXT_CURSOR_AT_END;
@vardata void			HINT_TEXT_FRAME;
@vardata void			HINT_TEXT_NO_FRAME;
@vardata void			HINT_TEXT_ALLOW_UNDO;
@vardata void			HINT_TEXT_ALLOW_SMART_QUOTES;
@vardata void			HINT_TEXT_DO_NOT_MAKE_LARGER_ON_PEN_SYSTEMS;

HINT_TEXT_WASH_COLOR indicates the background color for the GenText to exhibit. This hint takes a ColorQuad argument.

HINT_TEXT_WHITE_WASH_COLOR indicates the background color of the GenText should be white.

The Text Objects: 6.2 Using GenText: GenText Basics

The GenText instance fields can be set to specific values in your Goc files. They may also be modified by your application at run-time. These instance fields may be modified by both GenTextClass messages or VisTextClass messages.

GenTextClass always builds out into a completely functioning subclass of VisTextClass . With the exception of geometry management behavior, you can assume that they will behave the same. You may send any VisTextClass message to a GenText object. Messages are only provided in GenTextClass to manipulate and alter behavior solely of GenTextClass origins.

VisText Instance Fields

The chunk handle stored in GTXI_ text is copied to the VisText instance field VTI_ text at run-time. To perform operations on this text, you may use any of the VisTextClass messages in The Text . Similarly, the text object maximum length stored in GTXI_ maxLength is copied to VTI_ maxLength at run-time. To perform operations on this data, use an appropriate message under Using VisText .

GenText Attributes

GTXI_attrs, MSG_GEN_TEXT_SET_ATTRS, MSG_GEN_TEXT_GET_ATTRS

GTXI_ attrs stores the GenTextAttrs of the GenText object. These attribute flags are listed below.

GTA_SINGLE_LINE_TEXT
This flag indicates that this text object is only one line high. Vertical scrolling is disabled. Scrolling may be implemented horizontally if this is set.
GTA_USE_TAB_FOR_NAVIGATION
This flag indicates that the Tab key is used for navigation purposes in your application and should be interpreted to move to the next field rather than inserted into the text object.
GTA_INIT_SCROLLING
This flag indicates that the text object should appear with initial scrollbars.
GTA_NO_WORD_WRAPPING
This flag disables word wrapping.
GTA_ALLOW_TEXT_OFF_END
This flag is set if text may be allowed to overflow past the end of the text box. The text will still be stored in the object's text chunk, but it will not force horizontal (or vertical) scrolling to show the text on-screen.
GTA_TAIL_ORIENTED
This flag is set if you prefer to display the tail end of text rather than the top end. In a scrolling text box, this ensures that the text being added at the end is always displayed.
GTA_DONT_SCROLL_TO_CHANGES
This flag disables the default behavior of scrolling to any changes being made within the text object. Normally, insertion or deletion of text will force a scrolling GenText to scroll to the point of action; this flag will turn this behavior off.

You may alter the contents of a GenText's GTXI_ attrs instance field at run-time by sending the object a MSG_GEN_TEXT_SET_ATTRS . You may only send this message to a non-usable (~GS_USABLE) text object. To retrieve the current GenTextAttrs in use, send the text object MSG_GEN_TEXT_GET_ATTRS .

MSG_GEN_TEXT_SET_ATTRS
void	MSG_GEN_TEXT_SET_ATTRS(
        byte	attrsToSet,
        byte	attrsToClear);

This message sets a GenText object's GenTextAttrs (GTXI_ attrs ). The GenText object must not be GS_USABLE when sent this message.

Source: Unrestricted.

Destination: Any non-usable GenText object.

Parameters: attrsToSet GenTextAttrs to add.

attrsToClear
GenTextAttrs to remove. An attribute set in both parameters will be cleared.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_TEXT_GET_ATTRS
byte	MSG_GEN_TEXT_GET_ATTRS();

This message retrieves the GenText object's GenTextAttrs (GTXI_ attrs ).

Source: Unrestricted.

Destination: Any GenText object.

Parameters: None.

Return: GenTextAttrs in use by the GenText object( GTXI_attrs ).

Interception: Generally not intercepted.

GenTextStates

GTXI_stateFlags, MSG_GEN_TEXT_SET_INDETERMINATE_STATE, MSG_GEN_TEXT_SET_MODIFIED_STATE, MSG_GEN_TEXT_IS_INDETERMINATE, MSG_GEN_TEXT_IS_MODIFIED

GTXI_ stateFlags stores the current state of the GenText object. There are two GenTextStateFlags :

GTSF_INDETERMINATE
This flag specifies that the text within the GenText is indeterminate (may or may not reflect the current state). In most cases, you will not need to set this flag.
GTSF_MODIFIED
This flag specifies that the text within the GenText has changed since it last received a MSG_GEN_APPLY . The handler for MSG_GEN_APPLY checks whether this flag is set before sending out the GenText's GTXI_ applyMsg .

GenText objects should normally be marked by the application as not modified anytime their state is set with an external message. They will automatically be marked modified whenever the user interacts with them and marked not modified after receiving MSG_GEN_APPLY .

You may set a GenText's indeterminate or modified state with MSG_GEN_TEXT_SET_INDETERMINATE_STATE or MSG_GEN_TEXT_SET_MODIFIED_STATE , respectively. To check whether a GenText is indeterminate or modified, use MSG_GEN_TEXT_IS_INDETERMINATE or MSG_GEN_TEXT_IS_MODIFIED .

MSG_GEN_TEXT_SET_INDETERMINATE_STATE
void	MSG_GEN_TEXT_SET_INDETERMINATE_STATE(
        Boolean indeterminateState);

This message sets the GenText object's indeterminate state (the GTSF_INDETERMINATE flag in GTXI_ stateFlags ). This message does not affect the stored text.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: indeterminateState
True to set the text object indeterminate (GTSF_INDETERMINATE); false otherwise.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_TEXT_SET_MODIFIED_STATE
void	MSG_GEN_TEXT_SET_MODIFIED_STATE(
        Boolean modifiedState);

This message allows you to set the modified state of a GenText object (the GTSF_MODIFIED flag in GTXI_ stateFlags ). This message does not affect the stored text.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: modifiedState True to set the text object modified (GTSF_MODIFIED); false otherwise.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_TEXT_IS_INDETERMINATE
Boolean	MSG_GEN_TEXT_IS_INDETERMINATE();

This message checks whether a GenText object is indeterminate in state.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: None.

Return: The indeterminate state of the GenText object ( true if the object is indeterminate, false otherwise).

Interception: Generally not intercepted.

MSG_GEN_TEXT_IS_MODIFIED
Boolean	MSG_GEN_TEXT_IS_MODIFIED();

This message checks whether a GenText object has been modified.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: None.

Return: The modified state of the GenText object ( true if the object has been modified, false otherwise).

Interception: Generally not intercepted.

Sending an Action

GTXI_applyMsg, GTXI_destination, MSG_GEN_TEXT_GET_DESTINATION, MSG_GEN_TEXT_SET_DESTINATION, MSG_GEN_TEXT_GET_APPLY_MSG, MSG_GEN_TEXT_SET_APPLY_MSG

GTXI_ applyMsg holds the message for the GenText to send out whenever it has been modified and needs to apply its changes. Whenever a GenText receives MSG_GEN_APPLY , it checks whether its GTSF_MODIFIED flag has been set; if it has, it will send out its apply message. If a GenText is operating in immediate mode, these actions will happen immediately, resulting in an immediate action.

Use the prototype GEN_TEXT_APPLY_MSG to define your apply message. This ensures that the apply message passes the correct parameters (the current GenTextStateFlags ). GTXI_ destination specifies the destination object (or process) to send the GTXI_ applyMsg to. (This may also be a TravelOption such as TO_APP_FOCUS.) Use of these two fields is shown in Sending an Apply Message .

Code Display 10-24 Sending an Apply Message

    /* Define the apply message using the provided prototype. */
@message (GEN_TEXT_APPLY_MSG) MSG_MY_TEXT_STUFF_TEXT_IN_BUFFER;
    /* In the object declaration, set the destination and the apply message. */
@object GenTextClass MyText = {
    GI_visMoniker = "My Text Object";
    GTXI_text = "Initial Text Here";
    GTXI_maxLength = 99;
    GTXI_applyMsg = MSG_MY_TEXT_STUFF_TEXT_IN_BUFFER;
    GTXI_destination = process;
}
/* Retrieve the current text and place it in the indicated buffer. */
@method MyTextProcessClass, MSG_MY_TEXT_STUFF_TEXT_IN_BUFFER {
    char tempBuffer[100];
/* The GET_ALL_PTR retrieves the current text and stuffs at the location it in the
 * passed pointer. Pass a text length of zero for null-terminated text strings. */
    @call MyText::MSG_VIS_TEXT_GET_ALL_PTR(tempBuffer);
}

To change a GenText's apply message or destination, send it MSG_GEN_TEXT_SET_APPLY_MSG or MSG_GEN_TEXT_SET_DESTINATION , respectively. Use MSG_GEN_TEXT_GET_APPLY_MSG or MSG_GEN_TEXT_GET_DESTINATION to return the current apply message or destination.

MSG_GEN_TEXT_SET_APPLY_MSG
void	MSG_GEN_TEXT_SET_APPLY_MSG(
        Message		message);

This message sets a new apply message (GTXI_ applyMsg ) for the text object.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: message The new apply message.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_TEXT_GET_APPLY_MSG
Message	MSG_GEN_TEXT_GET_APPLY_MSG();

This message retrieves the current apply message (GTXI_ applyMsg ) of a GenText object.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: None.

Return: The apply message of the GenText object.

Interception: Generally not intercepted.

MSG_GEN_TEXT_SET_DESTINATION
void	MSG_GEN_TEXT_SET_DESTINATION(
        optr	dest);

This message sets the destination object or process (GTXI_ destination ) of a GenText object.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: dest The optr of the new destination object or process.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_TEXT_GET_DESTINATION
optr	MSG_GEN_TEXT_GET_DESTINATION();

This message returns the current destination object (GTXI_ destination ) of a GenText object.

Source: Unrestricted.

Destination: Any GenText object.

Parameters: None.

Return: The optr of the GenText's destination object (GTXI_ destination ).

Interception: Generally not intercepted.

GEN_TEXT_APPLY_MSG
void	GEN_TEXT_APPLY_MSG(
        word	stateFlags);

This prototype should be used to define your GenText's apply message (with GTXI_ applyMsg ).

Parameters: stateFlags The text object's GTXI_ stateFlags status.

Status Messages

ATTR_GEN_TEXT_STATUS_MSG, MSG_GEN_TEXT_SEND_STATUS_MSG

If your GenText is operating in delayed mode, there usually occur times when its current state may not reflect the most recent changes. In most cases, this is fine, but in some cases you may wish to notify other UI objects of a change in your GenText's state without sending out an apply message. This can be done with a status message .

A status message allows your GenText object to send out a message whenever the user interacts with the text object, regardless of whether that change will be immediately applied. This is most useful for cases in which two UI objects depend on each other. The status message allows one UI object to inform its counterpart that its state has changed, and that the counterpart should change its state to reflect the new information.

To give a GenText object a status message, include ATTR_GEN_TEXT_STATUS_MSG in the object's declaration. Use the prototype GEN_TEXT_STATUS_MSG to define your status message. This prototype ensures that the status message passes the correct parameters (the current GenTextStateFlags ).

Any user changes that do not result in the sending of the object's apply message will result in the sending of the object's status message. For an object in immediate mode, this attribute will have no effect. You may also manually send an object's status message by sending the GenText object MSG_GEN_TEXT_SEND_STATUS_MSG .

MSG_GEN_TEXT_SEND_STATUS_MSG
void	MSG_GEN_TEXT_SEND_STATUS_MSG(
        Boolean		modifiedState);

This message causes a GenText object to send out its status message, stored in the text object's ATTR_GEN_TEXT_STATUS_MSG vardata field. This message will still function even if the text object is not enabled (or usable).

Source: Unrestricted.

Destination: Any GenText object.

Parameters: modifiedState Non-zero if GVSF_MODIFIED bit should be passed with the status message.

Return: Nothing.

Interception: Generally not intercepted.

GEN_TEXT_STATUS_MSG
void	GEN_TEXT_STATUS_MSG(
        word	stateFlags);

This prototype should be used to define your GenText's status message (with ATTR_GEN_TEXT_STATUS_MSG).

Parameters: stateFlags The text object's GTXI_ stateFlags status.

The Text Objects: 7 The Controllers

This section describes the various text controller classes and how best to use them. Controllers create their UI gadgetry wherever in the generic tree they are placed; this section will give some pointers to designing the text controller UI. For full information on GenControlClass and how controllers work, see the Controllers chapter.

Character Attribute Controllers

Paragraph Attribute Controllers

Search and Replace and Spell-Checking

The Text Objects: 7.1 The Controllers: Character Attribute Controllers

The following controllers affect character attributes within a text object. As such, they each affect the contents of a text object's character attributes instance field (usually the VisTextCharAttr structure).

TextStyleControlClass

This controller works with the style character attributes of the current text selection. The features and tools of this controller are shown in TextStyleControlClass Features .

Code Display 10-25 TextStyleControlClass Features

  typedef WordFlags TSCFeatures;
#define TSCF_PLAIN					0x0800
#define TSCF_BOLD					0x0400
#define TSCF_ITALIC					0x0200
#define TSCF_UNDERLINE					0x0100
#define TSCF_STRIKE_THRU					0x0080
#define TSCF_SUBSCRIPT					0x0040
#define TSCF_SUPERSCRIPT					0x0020
#define TSCF_BOXED					0x0010
#define TSCF_BUTTON					0x0008
#define TSCF_INDEX					0x0004
#define TSCF_ALL_CAP					0x0002
#define TSCF_SMALL_CAP					0x0001
  typedef WordFlags TSCToolboxFeatures;
#define TSCTF_PLAIN					0x0800
#define TSCTF_BOLD					0x0400
#define TSCTF_ITALIC					0x0200
#define TSCTF_UNDERLINE					0x0100
#define TSCTF_STRIKE_THRU					0x0080
#define TSCTF_SUBSCRIPT					0x0040
#define TSCTF_SUPERSCRIPT					0x0020
#define TSCTF_BOXED					0x0010
#define TSCTF_BUTTON					0x0008
#define TSCTF_INDEX					0x0004
#define TSCTF_ALL_CAP					0x0002
#define TSCTF_SMALL_CAP					0x0001
#define  TSC_DEFAULT_FEATURES				(TSCF_PLAIN | TSCF_BOLD | TSCF_ITALIC |
                 TSCF_UNDERLINE | TSCF_SUBSCRIPT |
                 TSCF_SUPERSCRIPT | TSCF_STRIKE_THRU |
                 TSCF_BOXED | TSCF_BUTTON | TSCF_INDEX |
                 TSCF_ALL_CAP | TSCF_SMALL_CAP)
#define  TSC_DEFAULT_TOOLBOX_FEATURES (TSCTF_PLAIN | TSCTF_BOLD | TSCTF_ITALIC |
                TSCTF_UNDERLINE)
FontControlClass

This controller allows the user to change font types of the current text selection. Its features and tools are shown in FontControlClass Features .

Code Display 10-26 FontControlClass Features

  typedef WordFlags FCFeatures;
#define FCF_SHORT_LIST				0x0002
#define FCF_LONG_LIST				0x0001
  typedef WordFlags FCToolboxFeatures;
#define FCTF_ROMAN				0x0004
#define FCTF_SANS				0x0002
#define FCTF_MONO				0x0001
#define  FC_DEFAULT_FEATURES				(FCF_SHORT_LIST | FCF_LONG_LIST)
#define  FC_DEFAULT_TOOLBOX_FEATURES (FCTF_ROMAN | FCTF_SANS | FCTF_MONO)
PointSizeControlClass

This controller controls the point size of the current text selection. Its features and tools are shown in PointSizeControlClass Features .

Code Display 10-27 PointSizeControlClass Features

  typedef WordFlags PSCFeatures;
#define PSCF_9					0x0800
#define PSCF_10					0x0400
#define PSCF_12					0x0200
#define PSCF_14					0x0100
#define PSCF_18					0x0080
#define PSCF_24					0x0040
#define PSCF_36					0x0020
#define PSCF_54					0x0010
#define PSCF_72					0x0008
#define PSCF_SMALLER					0x0004
#define PSCF_LARGER					0x0002
#define PSCF_CUSTOM_SIZE					0x0001
  typedef WordFlags PSCToolboxFeatures;
#define PSCTF_9					0x0400
#define PSCTF_10					0x0200
#define PSCTF_12					0x0100
#define PSCTF_14					0x0080
#define PSCTF_18					0x0040
#define PSCTF_24					0x0020
#define PSCTF_36					0x0010
#define PSCTF_54					0x0008
#define PSCTF_72					0x0004
#define PSCTF_SMALLER					0x0002
#define PSCTF_LARGER					0x0001
#define  PSC_DEFAULT_FEATURES 					(PSCF_9 | PSCF_10 | PSCF_12 | PSCF_14 |
                     PSCF_18 | PSCF_24 | PSCF_36 | PSCF_72 |
                     PSCF_CUSTOM_SIZE | PSCF_SMALLER |
                     PSCF_LARGER)
#define  PSC_DEFAULT_TOOLBOX_FEATURES					(PSCTF_9 | PSCTF_10 | PSCTF_12 |
                     PSCTF_14 | PSCTF_18 | PSCTF_24 |
                     PSCTF_36 | PSCTF_72 | PSCTF_SMALLER |
                     PSCTF_LARGER)
CharFGColorControlClass and CharBGColorControlClass

These controllers handle changes to the foreground (text) and background colors of the current text selection. They are both subclassed from ColorSelectorClass and therefore inherit the feature set of that class. The additional definitions for these classes are given in Character Color Controller Features .

Code Display 10-28 Character Color Controller Features

/* Both CharFGColorControlClass and CharBGColorControlClass are subclasses of
 * ColorSelectorClass. For full details, see that class. */
@class CharFGColorControlClass, ColorSelectorClass;
#define  CFGCC_DEFAULT_FEATURES				(CSF_INDEX | CSF_RGB | CSF_DRAW_MASK |
                 CSF_PATTERN)
    @default GI_states = @default & ~GS_ENABLED;
    @default GCI_output = (TO_APP_TARGET);
@endc
@class CharBGColorControlClass, ColorSelectorClass;
#define  CBGCC_DEFAULT_FEATURES				(CSF_INDEX | CSF_RGB | CSF_DRAW_MASK |
                 CSF_PATTERN)
    @default GI_states = @default & ~GS_ENABLED;
    @default GCI_output = (TO_APP_TARGET);
@endc
FontAttrControlClass

This controller controls the font weight, font width, and track kerning of characters of the current text selection. Its features are shown in FontAttrControlClass Features .

Code Display 10-29 FontAttrControlClass Features

  typedef WordFlags FACFeatures;
#define FACF_FONT_WEIGHT				0x0004
#define FACF_FONT_WIDTH				0x0002
#define FACF_TRACK_KERNING				0x0001
  typedef WordFlags FACToolboxFeatures;
#define  FAC_DEFAULT_FEATURES				(FACF_FONT_WEIGHT | FACF_FONT_WIDTH |
                 FACF_TRACK_KERNING)
#define  FAC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);

The Text Objects: 7.2 The Controllers: Paragraph Attribute Controllers

The following controllers affect paragraph attributes within a text object. As such, they each affect the contents of a text object's paragraph attributes instance field (usually the VisTextParaAttr structure).

JustificationControlClass

This controller allows the user to specify paragraph justification of the current text selection (left, right, center, or full). Its features and tools are shown in JustificationControlClass Features .

Code Display 10-30 JustificationControlClass Features

  typedef WordFlags JCFeatures;
#define JCF_LEFT				0x0008
#define JCF_RIGHT				0x0004
#define JCF_CENTER				0x0002
#define JCF_FULL				0x0001
  typedef WordFlags JCToolboxFeatures;
#define JCTF_LEFT				0x0008
#define JCTF_RIGHT				0x0004
#define JCTF_CENTER				0x0002
#define JCTF_FULL				0x0001
#define  JC_DEFAULT_FEATURES				(JCF_LEFT | JCF_RIGHT | JCF_CENTER | JCF_FULL)
#define  JC_DEFAULT_TOOLBOX_FEATURES (JCTF_LEFT | JCTF_RIGHT | JCTF_CENTER |
                  JCTF_FULL)
    @default GCI_output = (TO_APP_TARGET);
    @default GI_attrs = (@default | GA_KBD_SEARCH_PATH);
ParaSpacingControlClass

This controller controls several paragraph spacing characteristics of the current text selection (such as paragraph spacing on top, spacing on bottom, leading, and line spacing). Its features and tools are shown in ParaSpacingControlClass Features .

Code Display 10-31 ParaSpacingControlClass Features

  typedef WordFlags PASCFeatures;
#define PASCF_SPACE_ON_TOP_BOTTOM					0x0002
#define PASCF_LEADING					0x0001
  typedef WordFlags PASCToolboxFeatures;
#define PASCTF_SINGLE					0x8000
#define PASCTF_ONE_AND_A_HALF					0x4000
#define PASCTF_DOUBLE					0x2000
#define PASCTF_TRIPLE					0x1000
#define  PASC_DEFAULT_FEATURES				(PASCF_SPACE_ON_TOP_BOTTOM | PASCF_LEADING)
#define  PASC_DEFAULT_TOOLBOX_FEATURES (PASCTF_SINGLE | PASCTF_ONE_AND_A_HALF |
                PASCTF_DOUBLE | PASCTF_TRIPLE)
@default GCI_output = (TO_APP_TARGET);
DefaultTabsControlClass and TabControlClass

These controllers set the placement and features of both default and user-defined tabs in the current text selection. The features and tools of both controllers are shown in Tab Controllers .

Code Display 10-32 Tab Controllers

    /* DefaultTabsControlClass */
  typedef WordFlags DTCFeatures;
#define DTCF_LIST				0x0002
#define DTCF_CUSTOM				0x0001
  typedef WordFlags DTCToolboxFeatures;
#define  DTC_DEFAULT_FEATURES				(DTCF_LIST | DTCF_CUSTOM)
#define  DTC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);
     /* TabControlClass */
  typedef WordFlags TCFeatures;
#define TCF_LIST					0x0080
#define TCF_POSITION					0x0040
#define TCF_GRAY_SCREEN					0x0020
#define TCF_TYPE					0x0010
#define TCF_LEADER					0x0008
#define TCF_LINE					0x0004
#define TCF_CLEAR					0x0002
#define TCF_CLEAR_ALL					0x0001
 typedef WordFlags TCToolboxFeatures;
#define  TC_DEFAULT_FEATURES				(TCF_LIST | TCF_POSITION | TCF_GRAY_SCREEN |
                 TCF_TYPE | TCF_LEADER | TCF_LINE | TCF_CLEAR |
                 TCF_CLEAR_ALL)
#define  TC_DEFAULT_TOOLBOX_FEATURES 0
     @instance word TCI_numberOfTabs;
    @instance Tab TCI_tabList[VIS_TEXT_MAX_TABS];
    @instance word TCI_selectedTab;
    @default GCI_output = (TO_APP_TARGET);
ParaBGColorControlClass

This controller controls the background color, draw mask, and pattern of the background color of the current paragraph selections. Since ParaBGColorControlClass is a subclass of ColorSelectorClass , it inherits the functions of that class. Its features and tools are shown in ParaBGColorControlClass Features .

Code Display 10-33 ParaBGColorControlClass Features

#define  PBGCC_DEFAULT_FEATURES				(CSF_INDEX | CSF_RGB | CSF_DRAW_MASK |
                 CSF_PATTERN)
    @default GI_states = @default & ~GS_ENABLED;
    @default GCI_output = (TO_APP_TARGET);
ParaAttrControlClass

This controller the user to set the "keep with," hidden, word wrapping, an widow and orphan attributes of the current paragraph selection. Its features and tools are shown in ParaAttrControlClass Features .

Code Display 10-34 ParaAttrControlClass Features

  typedef WordFlags PACFeatures;
#define PACF_WORD_WRAP					0x0010
#define PACF_HIDDEN_TEXT					0x0008
#define PACF_KEEP_PARA_WITH_NEXT					0x0004
#define PACF_KEEP_PARA_TOGETHER					0x0002
#define PACF_KEEP_LINES					0x0001
 typedef WordFlags PACToolboxFeatures;
#define  PAC_DEFAULT_FEATURES				(PACF_WORD_WRAP | PACF_HIDDEN_TEXT |
                 PACF_KEEP_PARA_WITH_NEXT |
                 PACF_KEEP_PARA_TOGETHER | PACF_KEEP_LINES
#define  PAC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);
BorderControlClass and BorderColorControlClass

This controllers set border color and placement. BorderColorControlClass is a subclass of ColorSelectorClass and therefore inherits the features and tools of that class. The tools and features of both controllers are shown in Border Controller Features .

Code Display 10-35 Border Controller Features

    /* BorderControlClass */
  typedef WordFlags BCFeatures;
#define BCF_LIST				0x0002
#define BCF_CUSTOM				0x0001
 typedef WordFlags BCToolboxFeatures;
#define  BC_DEFAULT_FEATURES				(BCF_LIST | BCF_CUSTOM)
#define  BC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);
    /* BorderColorControlClass */
 typedef WordFlags BCCToolboxFeatures;
#define  BCC_DEFAULT_FEATURES				(CSF_INDEX | CSF_RGB | CSF_DRAW_MASK |
                 CSF_PATTERN)
#define  BCC_DEFAULT_TOOLBOX_FEATURES 0
    @default GI_states = @default & ~GS_ENABLED;
    @default GCI_output = (TO_APP_TARGET);
DropCapControlClass

This controller allows the user to set "drop cap" features of the current paragraph selection. Its features and tools are shown in DropCapControlClass Features .

Code Display 10-36 DropCapControlClass Features

 typedef WordFlags DCCFeatures;
#define  DCCF_DROP_CAP				0x0001
 typedef WordFlags DCCToolboxFeatures;
#define  DCC_DEFAULT_FEATURES				(DCCF_DROP_CAP)
#define  DCC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);
HyphenationControlClass

This controller sets the implementation of automatic word hyphenation of the current paragraph selection. Its features and tools are shown in HyphenationControlClass .

Code Display 10-37 HyphenationControlClass

 typedef WordFlags HCFeatures;
#define  HCF_LIST				0x0001
typedef WordFlags  HCToolboxFeatures;
#define  HC_DEFAULT_FEATURES				(HCF_LIST)
#define  HC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);
MarginControlClass

This controller sets the left, paragraph, and right margins of the current paragraph selection. Its tools and features are shown in MarginControlClass .

Code Display 10-38 MarginControlClass

  typedef WordFlags MCFeatures;
#define MCF_LEFT_MARGIN				0x0004
#define MCF_PARA_MARGIN				0x0002
#define MCF_RIGHT_MARGIN				0x0001
typedef WordFlags  MCToolboxFeatures;
#define  MC_DEFAULT_FEATURES				(MCF_LEFT_MARGIN | MCF_PARA_MARGIN |
                 MCF_RIGHT_MARGIN)
#define  MC_DEFAULT_TOOLBOX_FEATURES 0
    @default GCI_output = (TO_APP_TARGET);
TextRulerControlClass

This controller manages how the TextRulerClass ruler operates, if any text ruler is used. The TextRuler provides a complete ruler for the text object. The features and tools of the TextRulerControl are shown in TextRulerControlClass .

Code Display 10-39 TextRulerControlClass

  typedef WordFlags TRCCFeatures;
#define TRCCF_ROUND					0x0002
#define TRCCF_IGNORE_ORIGIN					0x0001
typedef WordFlags  TRCCToolboxFeatures;
#define  TRCC_DEFAULT_FEATURES				(TRCCF_ROUND | TRCCF_IGNORE_ORIGIN)
#define  TRCC_DEFAULT_TOOLBOX_FEATURES 0
   typedef WordFlags TextRulerControlAttributes;
#define TRCA_ROUND					0x8000
#define TRCA_IGNORE_ORIGIN					0x4000
    @instance TextRulerControlAttributes TRCI_attrs = TRCA_ROUND;
TextStyleSheetControlClass

This controller allows the user to set up and use style sheets with the target text object. It is subclassed off StyleSheetControlClass and inherits all the features and tools of that class. It has no additional tools or features of its own.

The Text Objects: 7.3 The Controllers: Search and Replace and Spell-Checking

TextSearchInString(), TextSearchInHugeArray(), SearchOptions, WildCard

Text searches are not specifically part of VisTextClass , but are commonly used on text objects, and they are discussed here for that reason.

TextSearchInString() searches in a single text chunk for a passed text string. If a match is found, a pointer to that match (and the length of the match) are returned in buffers. TextSearchInHugeArray() performs the same searching operations, but can return the segment and offset of a found match within a huge array.

The following SearchOptions may affect the search procedure:

SO_NO_WILDCARDS
If set, the search mechanism ignores any passed WildCard values and treats them as the control character values (CTRL_P, CTRL_Q and CTRL_R) which they overlap.
SO_IGNORE_SOFT_HYPHENS
If set, soft hyphens in the "searched-in" text are treated as if they do not exist, therefore allowing the text "hyphenation" to match "hyphen-ation" in the destination text. If the match string contains soft hyphens, do not set this flag as strings will never match.
SO_BACKWARD_SEARCH
If set, the search will propagate backwards.
SO_IGNORE_CASE
If set, case will be ignored in the search criteria.
SO_PARTIAL_WORD
If set, partial words will be matched.
SO_PRESERVE_CASE_OF_DOCUMENT_STRING
If set, the case of the match string will be altered to preserve the case of the before

Both routines may take wild cards (type WildCard ) in the place of any character. These wildcards tell the search mechanism to accept any character in the allotted space. Passing WC_MATCH_MULTIPLE_CHAR instructs the search mechanism to accept any number of random characters in that place in the search string. The values of WildCard are:

WC_MATCH_SINGLE_CHAR
WC_MATCH_MULTIPLE_CHAR
WC_MATCH_WHITESPACE_CHAR

You can set higher-level features of your text objects with the SearchReplaceControlClass and SpellControlClass controllers. These controllers allow spell-checking and search and replace. These two controllers are designed to interact with each other and use common messages.

SearchReplaceControlClass

This controller allows the use of the text object's built-in search and replace features. The controller must be placed on the GAGCNLT_SELF_LOAD_OPTIONS Application GCN list. This controller sends out the following messages to its GCI_ output , which may be intercepted to provide custom behavior:

MSG_SEARCH
MSG_REPLACE_CURRENT
MSG_REPLACE_ALL_OCCURENCES
MSG_REPLACE_ALL_OCCURENCES_IN_SELECTION
MSG_META_GET_OBJECT_FOR_SEARCH_SPELL
MSG_META_DISPLAY_OBJECT_FOR_SEARCH_SPELL

Because many of these messages are shared by the SpellControlClass, they are described in Shared Functionality .

The Search & Replace messages make use of a SearchReplaceStruct . This structure is followed by text string(s) that represent the strings to search (and replace if that is the case). These strings may contain WildCard values (unless SO_NO_WILDCARDS is set in params ).The SearchReplaceStruct is shown below:

typedef struct {
    word		searchSize;
    word		replaceSize;
    byte		params;
    optr		replyObject;
    Message		replyMsg;
} SearchReplaceStruct;

searchSize stores the number of characters in the search string, including the null terminator.

replaceSize stores the number of characters in the replace string (if present), including the null terminator.

params stores the SearchOptions in use for this operation.

replyObject stores the optr of the object to send the replyMsg to if the string is not found.

replyMsg stores the message sent to the replyObject above.

The block containing this SearchReplaceStruct will contain either one null-terminated string (if the operation is just a simple search operation) or a null-terminated search string followed by a null-terminated replace string (if the operation is a search & replace).

MSG_SEARCH
@importMessage MetaSearchSpellMessages, void MSG_SEARCH(
        MemHandle		searchInfo);

This message is sent by the SearchReplace controller when an object is starting a search operation. You may intercept this message to find out the nature and specifics of the search.

Source: Search controller.

Destination: GCI_ output of the search controller (usually a text object).

Parameters: searchInfo Handle of block containing the SearchReplaceStruct . The search string immediately follows this structure in the passed block. This block should be freed by the message handler.

Interception: Intercept to find out the nature of the search and replace operation.

The features and tools of this controller are shown in SearchReplaceControlClass Features .

Code Display 10-40 SearchReplaceControlClass Features

  typedef WordFlags SRCFeatures;
#define SRCF_CLOSE					0x200
#define SRCF_FIND_NEXT					0x100
#define SRCF_FIND_PREV					0x80
#define SRCF_REPLACE_CURRENT					0x40
#define SRCF_REPLACE_ALL_IN_SELECTION					0x20
#define SRCF_REPLACE_ALL					0x10
#define SRCF_PARTIAL_WORDS					0x08
#define SRCF_IGNORE_CASE					0x04
#define SRCF_WILDCARDS					0x02
#define SRCF_SPECIAL_CHARS					0x01
  typedef WordFlags SRCToolboxFeatures;
#define SRCTF_SEARCH_REPLACE					0x01
#define  SRC_DEFAULT_FEATURES				(SRCF_FIND_NEXT | SRCF_FIND_PREV |
                 SRCF_REPLACE_CURRENT | SRCF_REPLACE_ALL |
                 SRCF_PARTIAL_WORDS | SRCF_IGNORE_CASE |
                 SRCF_WILDCARDS | SRCF_SPECIAL_CHARS |
                 SRCF_REPLACE_ALL_IN_SELECTION | SRCF_CLOSE)
#define  SRC_DEFAULT_TOOLBOX_FEATURES (SRCTF_SEARCH_REPLACE)
SpellControlClass

This controller allows you to include the advanced spelling-checker features of the text object. The controller must be placed on the GAGCNLT_SELF_LOAD_OPTIONS Application GCN list. The SpellControlClass controller defaults to a state of not enabled; it should be set GS_ENABLED before use.

If the controller will affect a text object within a document object, the SpellControl should be added to the GenDocumentControlClass object's GDCI_ enableDisableList . This will ensure that the spell controller is automatically enabled when text appears within the document.

This controller sends out the following messages, which may be intercepted to provide custom behavior:

MSG_SPELL_CHECK
MSG_REPLACE_CURRENT
MSG_REPLACE_ALL_OCCURENCES
MSG_META_GET_CONTEXT
MSG_META_CONTEXT
MSG_META_GET_OBJECT_FOR_SEARCH_SPELL
MSG_META_DISPLAY_OBJECT_FOR_SEARCH_SPELL
MSG_SPELL_CHECK
@importMessage MetaSearchSpellMessages, void MSG_SPELL_CHECK(@stack
        optr		replyObj,
        dword		numCharsToCheck,
        SpellCheckOptions		options,
        MemHandle		ICbuff);

This message is sent by the Spell controller to continue spell checking from the current position in the document.

Source: Spell controller.

Destination: GCI_ output of the spell controller.

Parameters: replyObj Object to send MSG_SPELL_CHECK_COMPLETED.

numCharsToCheck
Number of characters to check (if options has SCO_CHECK_NUM_CHARS set).
options
SpellCheckOptions in use for this operation.
IC buff
Handle of buffer to pass spell check library.

Interception: Intercept to find out the nature of the spell checking operation.

MSG_META_GET_CONTEXT
@importMessage MetaSearchSpellMessages, void MSG_META_GET_CONTEXT(@stack
        dword		position,
        ContextLocation		location,
        word		numCharsToGet,
        optr		replyObj);

This message is sent by the Spell controller to find out the current context of the text object. Other objects may also send this message to the text object. The text object will return MSG_META_CONTEXT to the requestor.

Source: The Spell controller. (Other objects may want to send this to retrieve the text context displayed in the spell box.)

Destination: GCI_ output of the Spell controller (usually a text object).

Parameters: position Position of the context. The context returned depends on this value and the ContextLocation passed in location .

location
ContextLocation . This may be one of the following values:
CL_STARTING_AT_POSITION
CL_ENDING_AT_POSITION
CL_CENTERED_AROUND_POSITION
CL_CENTERED_AROUND_SELECTION
CL_CENTERED_AROUND_SELECTION_START
CL_SELECTED_WORD
numCharsToGet
Maximum number of characters to return.
replyObj
Optr of the object to reply to via MSG_META_CONTEXT.

Interception: You may intercept to alter the nature of the context request.

MSG_META_CONTEXT
@importMessage MetaSearchSpellMessages, void MSG_META_CONTEXT(
        MemHandle		data);

This message returns the current context to the SpellControl object.

Source: A text object receiving MSG_META_GET_CONTEXT.

Destination: The object that sent the request.

Parameters: data ContextData structure storing information on the context, and the null-terminated context string.

Structures:

typedef struct {
    optr			CD_object;
    dword			CD_numChars;
    dword			CD_startPos;
    VisTextRange			CD_selection;
} ContextData;

CD_ object stores the optr of the object that the context is coming from.

CD_ numChars stores the number of characters in the text object.

CD_ range stores the range of characters that this context represents within the text object.

CD_ selection stores the range of characters that represent the current text selection.

The null-terminated context data follows this structure.

Interception: Generally not intercepted.

Its feature and tool definitions are shown in SpellControlClass .

Code Display 10-41 SpellControlClass

  typedef WordFlags SpellFeatures;
#define SF_CLOSE				0x2000
#define SF_CONTEXT				0x1000
#define SF_SIMPLE_MODAL_BOX				0x0800
#define SF_SUGGESTIONS				0x0400
#define SF_CHECK_ALL				0x0200
#define SF_CHECK_TO_END				0x0100
#define SF_CHECK_SELECTION				0x0080
#define SF_SKIP				0x0040
#define SF_SKIP_ALL				0x0020
#define SF_REPLACE_CURRENT				0x0010
#define SF_REPLACE_ALL				0x0008
#define SF_ADD_TO_USER_DICTIONARY				0x0004
#define SF_EDIT_USER_DICTIONARY				0x0002
#define SF_STATUS				0x0001
  typedef WordFlags SpellToolboxFeatures;
#define STF_SPELL				0x01
#define  SC_DEFAULT_FEATURES					(SF_STATUS | SF_EDIT_USER_DICTIONARY |
        SF_ADD_TO_USER_DICTIONARY | SF_REPLACE_ALL | SF_REPLACE_CURRENT |
        SF_SKIP_ALL | SF_SKIP | SF_CHECK_SELECTION | SF_CHECK_TO_END |
        SF_SUGGESTIONS | SF_CLOSE | SF_CONTEXT)
#define  SC_DEFAULT_TOOLBOX_FEATURES					STF_SPELL
Shared Functionality

The SearchReplace and Spell controllers share a common range of messages ( MetaSearchSpellMessages ) imported from MetaClass . (The messages MSG_SEARCH and MSG_SPELL_CHECK are specific to their respective controllers and were described earlier.) These messages common to both controllers are listed and described here.

MSG_REPLACE_CURRENT
@importMessage MetaSearchSpellMessages, void MSG_REPLACE_CURRENT(
        MemHandle		replaceInfo);

This message is sent by the SearchReplace controller when an object is starting a search operation that should replace the found match with the passed replacement string. This message involves a single replacement operation. To perform a replace-all operation, use MSG_REPLACE_ALL_OCCURENCES.

Source: Search controller.

Destination: GCI_ output of the search controller (usually a text object).

Parameters: replaceInfo Handle of block containing the SearchReplaceStruct . The null-terminated search string immediately follows this structure. The null-terminated replace string follows that string. This block should be freed by the message handler.

Interception: Intercept to find out the nature of the replacement operation.

MSG_REPLACE_ALL_OCCURRENCES
@importMessage MetaSearchSpellMessages, void MSG_REPLACE_ALL_OCCURRENCES(
        MemHandle		replaceInfo,
        Boolean		replaceFromBeginning);

This message is sent by the SearchReplace controller when an object is starting a search operation that should replace all found matches (in the search string) with the passed replacement string.

Source: Search controller.

Destination: GCI_ output of the search controller (usually a text object).

Parameters: replaceInfo Handle of block containing the SearchReplaceStruct . The null-terminated search string immediately follows this structure. The null-terminated replace string follows that string. This block should be freed by the message handler.

replaceFromBeginning
Non-zero if the replace-all operation should proceed from the beginning of the document; normally, the replace-all operation begins after the current position.

Interception: Intercept to find out the nature of the replacement operation.

MSG_REPLACE_ALL_OCCURRENCES_IN_SELECTION
@importMessage MetaSearchSpellMessages, void MSG_REPLACE_ALL_OCCURRENCES_IN_SELECTION(
        MemHandle		replaceInfo,
        Boolean		replaceFromBeginning);

This message is sent by the SearchReplace controller when an object is starting a search operation that should replace all found matches within a selection (in the search string) with the passed replacement string.

Source: Search controller.

Destination: GCI_ output of the search controller (usually a text object).

Parameters: replaceInfo Handle of block containing the SearchReplaceStruct . The null-terminated search string immediately follows this structure. The null-terminated replace string follows that string. This block should be freed by the message handler.

replaceFromBeginning
Non-zero if the replace-all operation should proceed from the beginning of the selection; normally, the replace-all operation begins after the current position.

Interception: Intercept to find out the nature of the replacement operation.

MSG_META_GET_OBJECT_FOR_SEARCH_SPELL
@importMessage MetaSearchSpellMessages, void MSG_META_GET_OBJECT_FOR_SEARCH_SPELL(
        GetSearchSpellObjectOption					option,
        optr					curObject);

This message is sent by a text object to itself when a search reaches the end of the current object and needs to determine where to continue the search.

Source: Any text object.

Destination: The text object sends this to itself.

Parameters: option GetSearchSpellObjectOption specifying the next object to continue the search at. This value may be GSSOT_FIRST_OBJECT, GSSOT_LAST_OBJECT, GSSOT_NEXT_OBJECT, or GSSOT_PREV_OBJECT.

curObject
Optr of the object that is currently being searched through. If you intercept this message, you can use this information to set the option above.

Interception: May intercept if you want to direct where a search or spell check operation should continue to.

MSG_META_DISPLAY_OBJECT_FOR_SEARCH_SPELL
@importMessage MetaSearchSpellMessages, void MSG_META_DISPLAY_OBJECT_FOR_SEARCH_SPELL();

This message is sent by the text object to itself when a matching word (or misspelled word if the controller is a Spell controller) has been found and should be displayed.

Source: The text object.

Destination: The text object sends this to itself.

Interception: You may intercept this message at the text object to receive notification of a match.

This document is a single-page version of a a multi-page document, suitable for easy printing.


GenTrigger

The GenTrigger object allows a user to initiate an action. In most graphical user interfaces, a GenTrigger is represented by a button; clicking on the button initiates the action. This action sends a pre-specified message to a pre-specified object or process in the trigger's instance data. You may also customize a GenTrigger to pass data along with the message.

1 GenTrigger Overview
2 GenTrigger Usage
3 Supplemental GenTrigger Usage
3.1 Passing Data with a GenTrigger
3.2 Interaction Commands
3.3 Interpreting Double-Clicks
3.4 Initiating an Action
3.5 Setting a Trigger As the Default
3.6 Manipulating Instance Data
3.7 Other Hints
4 Customizations

GenTrigger: 1 GenTrigger Overview

Whenever you need to allow the user to initiate a single action, you should probably use a GenTrigger. When activated, a GenTrigger simply sends a message to another object or process. Both the message and its destination can be set up in the GenTrigger's instance data. The trigger can also send data with the message. This is useful if several triggers send the same message (for example MSG_CHOOSE_COLOR) but send different data (for example "Blue," "Red," or "Green").

In some cases, the use of triggers is readily apparent (the "OK" button in a dialog box, for example, which will dismiss the dialog box) while at other times the use of triggers is not as obvious (as menu items, for example). The GenTrigger is a simple and widely used object, appearing in menus, dialog boxes, and primary windows--wherever you need a UI object whose sole action is to send out a message, a GenTrigger will probably suffice.

Because of the trigger's basic role, they cannot have children. Note that this does not mean that activating a trigger may not necessarily bring up an object indirectly (such as a dialog box), but only that an object may not be directly attached below a GenTrigger in a generic object tree.

Your application will choose the function of the trigger but it is left to the Specific UI to choose in what manner a trigger will be activated. (For example, under OSF/Motif, a GenTrigger may be built as a button and will be activated by clicking on the button.)

GenTriggerClass provides two instance fields. These instance fields are listed in GenTrigger Instance Fields .

Code Display 5-1 GenTrigger Instance Fields

/* GenTrigger inherits all the instance fields of GenClass. */
    @instance optr GTI_destination = 0;
    @instance Message GTI_actionMsg = 0;
@vardata void HINT_TRIGGER_BRINGS_UP_WINDOW;
@vardata void ATTR_GEN_TRIGGER_IMMEDIATE_ACTION;
@vardata void HINT_TRIGGER_DESTRUCTIVE_ACTION;
@vardata word ATTR_GEN_TRIGGER_ACTION_DATA;
@vardata word ATTR_GEN_TRIGGER_INTERACTION_COMMAND;
@vardata Message ATTR_GEN_TRIGGER_CUSTOM_DOUBLE_PRESS;
    /* Alias for messages with "void (word a, word b)" */
 typedef struct { word a, b; } TwoWordArgs;
@vardataAlias (ATTR_GEN_TRIGGER_ACTION_DATA)
        TwoWordArgs ATTR_GEN_TRIGGER_ACTION_TWO_WORDS;
    /* Alias for messages with "void (word a, word b, word c)" */
 typedef struct { word a, b, c; } ThreeWordArgs;
@vardataAlias (ATTR_GEN_TRIGGER_ACTION_DATA)
        ThreeWordArgs ATTR_GEN_TRIGGER_ACTION_THREE_WORDS;
    /* Alias for messages with "void (optr output, word a)" */

The GTI _actionMsg instance field sets the message for the trigger to send out upon activation. The activation itself is determined by the specific UI. The trigger will send this message to the output specified in the GTI_ destination instance field.

The GTI _destination instance field sets the object or process for the trigger to send its message to upon activation. The trigger will send the message in the GTI _actionMsg field to this optr. Often this will be a TravelOption or a GenTravelOption .

GenTrigger: 2 GenTrigger Usage

A simple GenTrigger should contain a message to send out upon activation, a destination to send the message to, and a visual moniker to distinguish the trigger from other objects (and describe its function to the user).

You may use the prototype GEN_TRIGGER_ACTION to define the action message. This prototype passes the optr of the sending trigger in the variable name `trigger' and can be accessed within a message handler.

GenTriggers may appear within GenPrimarys, GenViews, or, most often, within a GenInteraction. If a GenTrigger is placed as a child of a GIV_POPUP GenInteraction, the GenTriggers may be represented as menu items.

Code Display 5-2 Code for Basic GenTriggers

/* Use the prototype GEN_TRIGGER_ACTION to define a GenTrigger's action message.
 * Within a method, you can retrieve this optr with the variable name `trigger.' */
@message (GEN_TRIGGER_ACTION) MSG_HELLO_CHANGE_TO_BLUE();
@message (GEN_TRIGGER_ACTION) MSG_HELLO_CHANGE_TO_GOLD();
/* The GenInteraction object will contain the child GenTriggers. */
@object GenInteractionClass HelloColorBox = {
    GI_comp = @HelloBlueTrigger, @HelloGoldTrigger;
    GI_visMoniker = 'C', "Color";
    GII_visibility = GIV_DIALOG;
}
/* GenTriggers
 * Buttons are implemented by GenTriggerClass. When the trigger is pushed by
 * the user (clicked on with the mouse), it will send the specified message
 * to the specified destination object. In both cases below, the trigger will
 * send an application-defined message to the application's Process
 * object. A trigger's moniker is displayed within the trigger. In this
 * case both are text, but any graphics string could also be used to create
 * graphical triggers. */
@object GenTriggerClass HelloBlueTrigger = {
    GI_visMoniker = 'B', "Blue";
    /* Send MSG_HELLO_CHANGE_TO_BLUE to the Process object. */
    GTI_destination = process;
    GTI_actionMsg = MSG_HELLO_CHANGE_TO_BLUE;
}
@object GenTriggerClass HelloGoldTrigger = {
    GI_visMoniker = 'G', "Gold";
    /* Send MSG_HELLO_CHANGE_TO_GOLD to the Process object. */
    GTI_destination = process;
    GTI_actionMsg = MSG_HELLO_CHANGE_TO_GOLD;
}

GenTrigger: 3 Supplemental GenTrigger Usage

GenTriggers may also perform other actions besides the simple sending of a message to an object.

For example, your trigger may pass data along with its message; you may then use this data within a method to perform some additional work. In some cases, this data may be interpreted by a GenInteraction object to provide additional functionality to dialog boxes.

You may also alter the manner in which a trigger is initiated through the implementation of hints and several messages. You can also modify the trigger's instance data dynamically, reassigning its action message and destination.

Passing Data with a GenTrigger

Interaction Commands

Interpreting Double-Clicks

Initiating an Action

Setting a Trigger As the Default

Manipulating Instance Data

Other Hints

GenTrigger: 3.1 Supplemental GenTrigger Usage: Passing Data with a GenTrigger

ATTR_GEN_TRIGGER_ACTION_DATA, ATTR_GEN_TRIGGER_ACTION_TWO_WORDS, ATTR_GEN_TRIGGER_ACTION_THREE_WORDS

A GenTrigger may pass data along with its message. Use ATTR_GEN_TRIGGER_ACTION_DATA to assign a word of data that you wish to pass with the particular message. If you need to pass more than a single word of data, you will have to use @vardataAlias ; this Goc keyword allows ATTR_GEN_TRIGGER_ACTION_DATA to pass a structure instead of a single word.

Several ATTR_GEN_TRIGGER_ACTION_DATA aliases already exist for your use. ATTR_GEN_TRIGGER_ACTION_TWO_WORDS and ATTR_GEN_TRIGGER_ACTION_THREE_WORDS allow you to pass two or three words of data along with the trigger's message .

If you need to pass more than three words of data with a message, you will need to use the stack to pass arguments. To do so, use the @stack parameter within your message definition; because of the implementation of the stack, make sure to set up your structure to pass its arguments in reverse order .

Code Display 5-3 Passing Data from a GenTrigger

/* In a class declaration, you should define a message that passes data. */
@class MyProcessClass, GenProcessClass;
@message void MSG_SET_MY_DATA(int myData);
@endc;
@object GenTriggerClass MyHundredTrigger = {
    /* This trigger will pass 100 to the method for MSG_SET_MY_DATA. */
    GI_visMoniker = "Set My Data to 100";
    GTI_actionMsg = MSG_SET_MY_DATA;
    GTI_destination = process;
    ATTR_GEN_TRIGGER_ACTION_DATA = 100;					/* This object's data is 100. */
}
@object GenTriggerClass MyTenTrigger = {
    /* This trigger will pass `10' to the method for MSG_SET_MY_DATA.*/
    GI_visMoniker = "Set My Data to 10";
    GTI_actionMsg = MSG_SET_MY_DATA;
    GTI_destination = process;
    ATTR_GEN_TRIGGER_ACTION_DATA = 10;					/* This object's data is 10. */
}
@method MyProcessClass, MSG_SET_MY_DATA {
    /* This message passes one parameter (the integer myData). Goc knows
     * that the data is located within the ATTR_GEN_TRIGGER_ACTION_DATA
     * field. */
    UpdateDisplay(myData);				/* Use that data for your own purposes. */
}
/* The following examples show how to pass longer structures than the above. */
    /* Define the message. */
@message void MSG_CUSTOM_MESSAGE(@stack char name[10], optr arg1, int arg2);
    /* Define the structure you wish to pass with the message. If the
     * message will pass parameters on the stack (as in this case), you
     * must define your structure to pass its elements in reverse order. */
typedef struct {
    int		MS_arg2;
    optr		MS_arg1;
    char		MS_name[10]
} MyStruct;
    /* Use @vardataAlias to define your own attribute (ATTR_MY_STRUCT_TO_PASS)
     * to store the custom structure. */
@vardataAlias (ATTR_GEN_TRIGGER_ACTION_DATA) MyStruct ATTR_MY_STRUCT_TO_PASS;
    /* Declare your object and set the arguments to pass. */
@object GenTriggerClass MyTrigger = {
    GI_visMoniker = "Custom Trigger";
    GTI_actionMsg = MSG_CUSTOM_MESSAGE;
    GTI_destination = process;
    ATTR_MY_STRUCT_TO_PASS = { 100, @ListObject, "Zow!" }
}

GenTrigger: 3.2 Supplemental GenTrigger Usage: Interaction Commands

ATTR_GEN_TRIGGER_INTERACTION_COMMAND

A GenTrigger may perform special actions within a GenInteraction object. The ATTR_GEN_TRIGGER_INTERACTION_COMMAND attribute sets the special activity for these types of triggers. This attribute indicates that the GenTrigger is an Interaction command trigger for the parent GenInteraction object. ATTR_GEN_TRIGGER_INTERACTION_COMMAND takes a word of data (of type InteractionCommand ) specifying the command type of the trigger. A trigger with an InteractionCommand can perform one of several standard actions. Most of these actions are specific to dialog boxes.

The InteractionCommand types are

IC_NULL
This command is special, alerting an application that the interaction containing the trigger has been closed because the system is about to shut down. You should never set any triggers to this InteractionCommand .
IC_DISMISS
This command instructs the UI to dismiss the parent GenInteraction. Set this if you want the trigger to dismiss an Interaction or provide a custom "Close" trigger.
IC_INTERACTION_COMPLETE
This command is special, indicating to the UI that a single interaction has been completed (the user has clicked on an InteractionCommand trigger marked with GA_SIGNAL_INTERACTION_COMPLETE). You should never set any triggers to this InteractionCommand . If you wish a trigger to exhibit this behavior, set its GI_ attrs field to include GA_SIGNAL_INTERACTION_COMPLETE. (See the GenClass chapter.)

IC_APPLY This command instructs the UI to apply any properties within a GIT_PROPERTIES Interaction. By default, this trigger will send out a MSG_GEN_APPLY .

IC_RESET This command instructs the UI that this is a "reset" trigger for a properties Interaction; the application is responsible for actual reset behavior. You should provide a method to handle the message sent out by this trigger, set up the state of items to reset, and call MSG_GEN_RESET within that handler.
IC_OK
This command instructs the UI that the user has acknowledged a GIT_NOTIFICATION and should take any appropriate measures.
IC_YES
This command instructs the UI that the user has signalled a positive response from a GIT_AFFIRMATION Interaction.
IC_NO
This command instructs the UI that the user has signalled a negative response from a GIT_AFFIRMATION Interaction.
IC_STOP
This command instructs the UI to halt a GIT_PROGRESS Interaction; (the application is responsible for halting any ongoing operations related to that Interaction).
IC_EXIT
This command is special, causing the trigger to exit the application. Typically, the File menu already has a trigger set to this command.
IC_HELP
This command indicates that this GenTrigger brings up help, searching for the proper help context (ATTR_GEN_HELP_CONTEXT) and bringing up the help dialog box with that context. Any contexts below the trigger's containing dialog are ignored, however; i.e. the lowest possible context is the that on the dialog box itself. For more information on Help, see the Help Library chapter.

In most cases, you should not provide a visual moniker for any triggers with an InteractionCommand ; the specific UI will select an appropriate moniker. (You may supply a moniker if you wish to override the specific UI's selection, however.)

Activating an ATTR_GEN_TRIGGER_INTERACTION_COMMAND trigger will cause the UI to send MSG_GEN_GUP_INTERACTION_COMMAND to the trigger itself. This message will travel up to the appropriate GenInteraction object, where it will be handled.

This activity only occurs if both the trigger's destination and its action message fields are null, however. If these fields are non-null (specifying your own action message and destination), the sending of the InteractionCommand will be overridden. You may want to do this to replace the default functionality of a Specific UI-supplied trigger. For complete usage of an InteractionCommand trigger within a GenInteraction object, see the GenInteractionClass chapter.

GenTrigger: 3.3 Supplemental GenTrigger Usage: Interpreting Double-Clicks

ATTR_GEN_TRIGGER_CUSTOM_DOUBLE_PRESS

Your trigger sends out the message in the GTI_ actionMsg field and data in the ATTR_GEN_TRIGGER_ACTION_DATA field whenever the user clicks on the trigger, whether that click is a single or a double-click. If you wish your trigger to send out an alternate message if the user double-clicks, use ATTR_GEN_TRIGGER_CUSTOM_DOUBLE_PRESS .

If you set a message for this attribute, the trigger may send out two different messages: one for a single click action and another for a double-click action.

Code Display 5-4 A Trigger with a Double-Click Message

@object GenTriggerClass DoubleClickTrigger = {
/* If the user single-clicks on this trigger, MSG_MY_ACTION will be sent along
 * with data1 to the process. If the user double-clicks,
 * MSG_MY_DOUBLE_PRESS_ACTION will be sent instead. */
    GTI_actionMsg = MSG_MY_ACTION;
    GTI_destination = process;
    GI_visMoniker = "Single or Double Click!";
    ATTR_GEN_TRIGGER_ACTION_DATA = data1;
    ATTR_GEN_CUSTOM_DOUBLE_PRESS = MSG_MY_DOUBLE_PRESS_ACTION;
}

GenTrigger: 3.4 Supplemental GenTrigger Usage: Initiating an Action

MSG_GEN_TRIGGER_SEND_ACTION, MSG_GEN_ACTIVATE

In almost all cases, the specific UI initiates the action of the GenTrigger. Once the specific UI determines that a trigger is activated, it will send a MSG_GEN_TRIGGER_SEND_ACTION to the object, which will in turn send the message in GTI_ actionMsg to the object in GTI_ destination . In some rare cases, however, you may wish to manually "trigger" the GenTrigger yourself. This can be done by sending the object the MSG_GEN_TRIGGER_SEND_ACTION yourself.

Note that this message, in effect, skips the specific UI and thus any specific functionality defined for activating a trigger will be ignored. In OSF/Motif, for example, a trigger may "blink" (display itself in reverse video) when activated. To perform the default specific UI behavior, send the object a MSG_GEN_ACTIVATE instead, which will perform the default activation for an object and then call MSG_GEN_TRIGGER_SEND_ACTION .

MSG_GEN_TRIGGER_SEND_ACTION
void	MSG_GEN_TRIGGER_SEND_ACTION(
        Boolean doublePressFlag);

This message causes a GenTrigger to send its action message specified in GTI_actionMsg to the output specified in GTI_ destination if the object is GS_USABLE. This message is sent automatically by the UI as part of the default behavior when a GenTrigger is activated by the specific UI. If you wish to simulate a double-click, sending the message included in an object's ATTR_GEN_TRIGGER_CUSTOM_DOUBLE_PRESS, pass this message with a value of true . Double-press messages may not pass any data in a trigger's ATTR_GEN_TRIGGER_ACTION_DATA.

Slightly more useful is the GenClass message MSG_GEN_ACTIVATE , which activates the trigger at the specific UI's level, performing any default activation behavior (such as making the trigger blink to show that it is being activated).

Source: Unrestricted. Usually the specific UI.

Destination: Any usable GenTrigger object

Parameters: doublePressFlag Pass true to use the message stored in ATTR_GEN_TRIGGER_CUSTOM_DOUBLE_PRESS rather than message in GTI_ actionMsg .

Return: Nothing.

Interception: Generally not intercepted.

See Also: MSG_GEN_ACTIVATE

GenTrigger: 3.5 Supplemental GenTrigger Usage: Setting a Trigger As the Default

MSG_GEN_TRIGGER_MAKE_DEFAULT_ACTION

Often, a dialog box or other windowed generic object will have several triggers. You will likely want one of these triggers to be the default action--the default action is activated when the user double-clicks on some item in the dialog or when the user hits Return, typically. (Specific UIs can determine how the activation occurs.)

To set a trigger the default, use HINT_DEFAULT_DEFAULT_ACTION ; this only works for the first time the trigger is created, though. To set a trigger to be the default action item dynamically, use the message MSG_GEN_TRIGGER_MAKE_DEFAULT_ACTION .

MSG_GEN_TRIGGER_MAKE_DEFAULT_ACTION
void	MSG_GEN_TRIGGER_MAKE_DEFAULT_ACTION();

This message sets the trigger to be the default action in its windowed parent. It is the message equivalent of HINT_DEFAULT_DEFAULT_ACTION , defined in GenClass .

Source: Unrestricted.

Destination: Any GenTrigger object.

Interception: Generally not intercepted.

GenTrigger: 3.6 Supplemental GenTrigger Usage: Manipulating Instance Data

MSG_GEN_TRIGGER_GET_ACTION_MSG, MSG_GEN_TRIGGER_SET_ACTION_MSG, MSG_GEN_TRIGGER_GET_DESTINATION, MSG_GEN_TRIGGER_SET_DESTINATION

A GenTrigger's instance data need not be static. The instance data can be altered dynamically using the following messages of GenTriggerClass .

MSG_GEN_TRIGGER_GET_ACTION_MSG
Message MSG_GEN_TRIGGER_GET_ACTION_MSG();

This message retrieves the GTI_ actionMsg instance data of the trigger.

Source: Unrestricted.

Destination: Any GenTrigger object.

Parameters: None.

Return: Message in GTI_ actionMsg .

Interception: Generally not intercepted.

MSG_GEN_TRIGGER_SET_ACTION_MSG
void	MSG_GEN_TRIGGER_SET_ACTION_MSG(
        Message message);

This message sets the GTI_ actionMsg instance field of a GenTrigger to the message passed.

Source: Unrestricted.

Destination: Any GenTrigger object.

Parameters: message Message to assign GTI_ actionMsg to.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_TRIGGER_GET_DESTINATION
optr	MSG_GEN_TRIGGER_GET_DESTINATION();

This message returns the GTI_ destination instance data field of the GenTrigger.

Source: Unrestricted.

Destination: Any GenTrigger object.

Parameters: Nothing.

Return: The optr of destination object (in GTI_ destination ).

Interception: Generally not intercepted.

MSG_GEN_TRIGGER_SET_DESTINATION
void	MSG_GEN_TRIGGER_SET_DESTINATION(
        optr dest);

This message sets the GTI_ destination instance field of the GenTrigger to the optr passed.

Source: Unrestricted.

Destination: Any GenTrigger object.

Parameters: dest The optr of the new destination.

Return: Nothing.

Interception: Generally not intercepted.

GenTrigger: 3.7 Supplemental GenTrigger Usage: Other Hints

HINT_TRIGGER_BRINGS_UP_WINDOW, ATTR_GEN_TRIGGER_IMMEDIATE_ACTION, HINT_TRIGGER_DESTRUCTIVE_ACTION

Besides the hints discussed in earlier sections and the hints inherited from GenClass , GenTriggerClass provides two others, as well as one additional attribute:

  • HINT_TRIGGER_BRINGS_UP_WINDOW
     Use this hint if your trigger action indirectly brings up a window (such as a GIV_DIALOG GenInteraction or some other interaction window). This hint, depending on the specific UI, may be used to display the button with an ellipsis "..." to symbolize that the trigger leads to some other interaction.
  • ATTR_GEN_TRIGGER_IMMEDIATE_ACTION
     Use this attribute if your trigger is in a menu (GIV_POPUP interaction) and its action should take place immediately before other pending UI events. This will prevent default UI events from occurring before the trigger's action. For example, this attribute is used for the tack trigger in the menu box, to prevent the menu from being closed before it is tacked.
  • HINT_TRIGGER_DESTRUCTIVE_ACTION
    Use this hint if your action may be potentially destructive. This hint prevents the trigger from being recognized as the default trigger.

For more information on other hints not specific to GenTriggers see the Managing UI Geometry chapter and hints defined under GenClass .

GenTrigger: 4 Customizations

You may customize a GenTrigger using all the basic GenClass attributes as shown in the GenClass chapter. Of special significance are the ways in which you can tailor a moniker to print a graphics string on a button, as in a DOS room icon. The moniker should have as its argument a GString. (See the Graphics Environment chapter).

Code Display 5-5 Code for a "Graphic Button" GenTrigger

@start TriggerGraphicsMonikerResource, notDetachable;
@visMoniker TriggerGraphicsMoniker = {
    size = standard;
    color = color4;
    aspectRatio = normal;
    cachedSize = 95, 70;
    gstring {
        GSBeginString
        GSDrawBitmapAtCP <(EndTriggerGraphicsIcon-
                    StartTriggerGraphicsIcon)>
        StartTriggerGraphicsIcon label byte
        <...Bitmap here...>
        EndTriggerGraphicsIcon label byte
        GSEndString
    }
}
@end	TriggerGraphicsMonikerResource;
@object GenTriggerClass GraphicTrigger = {
    GI_visMoniker = list
        {
        TriggerGraphicsMoniker
        }
    GTI_actionMsg = MSG_CUSTOM_MESSAGE;
    GTI_destination = process;
    ATTR_GEN_TRIGGER_ACTION_DATA = {DataToPass};
}

This document is a single-page version of a a multi-page document, suitable for easy printing.


UI Overview

The GEOS user interface (UI) is an integral part of the system software and does an amazing amount of work for applications. The UI is a sophisticated and powerful object library that includes everything from simple buttons to self-scrolling windows to plug-in objects that provide nearly complete word processing.

GEOS also uses a new concept in user interface technology: the generic UI. By using the generic UI, GEOS programs simply declare their UI needs and leave nearly all drawing, screen management, and input management up to GEOS. The generic UI technology also allows a single application executable file to run with several different look-and-feel specifications.

1 The GUI
2 The GEOS User Interface
3 Using the Generic Classes
3.1 The Generic Class Tree
3.2 Creating a Generic Object Tree
4 Using the Visible Classes
4.1 Visible Objects and the GenView
4.2 The Visible Object Document
4.3 Visible Object Abilities
4.4 The Vis Class Tree
4.5 Creating a Visible Object Tree
4.6 Working with Visible Object Trees
5 A UI Example
5.1 What TicTac Illustrates
5.2 What TicTac Does
5.3 The Structure of TicTac
5.4 TicTacBoard Specifics
5.5 TicTacPiece Specifics

UI Overview: 1 The GUI

Ever since the first text-based computer application was created, computer users have looked for easier, more intuitive interfaces to their programs. If the user has to struggle to remember commands or to find the right function, the program is of very little use to him. Thus, the user interface of a program, the program's look and feel, can often be as important as its feature set.

The graphical user interface (GUI) concept has prompted millions of people to begin using computers and has made computer use for millions of others easier. Many corporations and school systems even make GUI a requirement for all their computer acquisitions.

GUIs, however, are not without problems. With so many hundreds of different programs available, even well-designed interfaces can differ significantly from other well-defined interfaces. Although users no longer have to remember whether the "print" command is Ctrl-Alt-P or F4, they may have to search through dozens of menus just to find it. To solve this consistency problem, different manufacturers have created GUI specifications that detail menu structure, color schemes, input management, and many other aspects of the GUI.

GEOS takes this one step further with its Generic User Interface. Application programmers do not have to decide beforehand which GUI specification will be run by their program; nor do they have to write one version for each GUI supported. Instead, they define their generic UI needs and then let GEOS determine the manifestation of those needs at run-time.

Another problem with GUIs is their speed. Without powerful hardware, many GUI-based systems are sluggish because of high overhead associated with geometry management and drawing. GEOS, however, is extremely fast because it is entirely object-oriented and is programmed in assembly language. And, since most of the user interface is managed and drawn by GEOS, applications written for the system automatically take advantage of this speed.

UI Overview: 2 The GEOS User Interface

Most programmers who write applications for GEOS will have worked with other GUI systems. Some will even have worked with object-oriented systems. Using the GEOS UI, however, will be a new experience for nearly every programmer.

The GEOS user interface consists of a number of dynamic libraries containing object classes. Applications include objects of these classes to gain various types of functionality. In general, applications will use two different basic types of objects for their UI: Generic objects provide basic UI functionality including windows, menus, and dialog boxes, and their implementation on the screen is determined by GEOS. Visible objects provide more application-specific functionality and offer complete display control to the application.

Generic object classes are all based on the class GenClass . Applications will never use any objects of GenClass , but they will use many objects of its subclasses. A list of these subclasses is shown below, and each of these classes is described in Using the Generic Classes .

Visible object classes are subclassed from VisClass . VisClass may be used often by applications, as will its subclasses. The subclasses of VisClass are shown below, and each is described in Using the Visible Classes .

When to use the generic or visible objects is subject to many criteria. In order to learn more about each of these components of the UI, you should read the two sections following this one.

The GEOS UI also contains many features that will often be used by applications but which don't fall into either the generic or the visible world. These features are called "mechanisms" and include input management, geometry management, general change notification, and the clipboard and quick-transfer functions. As these mechanisms are not UI-specific but are only implemented within the UI libraries, they are documented elsewhere in this book.

UI Overview: 3 Using the Generic Classes

Most of your application's UI needs will be satisfied by the use of generic UI objects. Many applications may need only the generic classes. Generic UI objects are powerful and easy to use, and they provide a number of services normally left up to application code.

Generic object classes have no inherent visual representation. Rather, each generic object represents a certain set of UI functions instead of UI components. For example, the GenInteraction class implements grouping and organizational functions; it can appear in several forms including menus and dialog boxes. It can also have no visual representation but merely provide geometry management for other generic objects.

What visual form a generic object takes, if any, is determined by two factors: the specific UI library in use, and the instance data of the generic object.

  • A specific UI library is essentially a "UI driver" that translates the generic class into its specific representation. Just as each video card has its own driver, each UI specification (e.g. OSF/Motif or Presentation Manager) has its own specific UI library. This library uses the generic object's instance data to determine the exact form of that object's output and input. A GenValue object, for example, may be implemented as a spinner object in one specific UI but as a slider object in another.

The generic object's instance data determines the features of the object as implemented by the specific UI. Across different GUI specifications, each similar object may appear different or handle input differently. For example, a button in one GUI may invert itself when pressed while the same button in another GUI might simply darken its outline when pressed. However, both buttons will do the same task. Therefore, a generic object's instance data must be categorized into two types: Attributes are instance data that will cause the same results no matter what GUI is used (e.g. the function the button performs). Hints are instance data that suggest particular implementations that may or may not be implemented by the GUI in use.

The way the generic object gets translated by GEOS into a visible representation is a complicated process. All generic classes are subclasses of GenClass , which is defined as a variant class. This means that GenClass has no defined superclass, that the superclass can be changed from time to time. The superclass is determined when the object is instantiated and resolved--when its visual representation is called for. (See the GEOS Programming chapter.)

The specific UI library contains objects subclassed off VisClass . These objects know how to handle input events and have very specific representation on the screen. These specific UI classes connect to the generic classes through the master/variant mechanism; in this way, the superclass of a generic object is assigned to one of the classes in the specific UI library. The generic object thus inherits the specific class' visible representation.

The above process happens entirely at run-time. The application does not have to have any knowledge of which particular GUI is in use; the same application executable code will work with any specific UI library. Because of the generic UI of GEOS, a user can run the same application under several look-and-feel specifications; this is desirable to the programmer because he only has to code the application once to receive the benefits of several different GUIs.

The Generic Class Tree

Creating a Generic Object Tree

UI Overview: 3.1 Using the Generic Classes: The Generic Class Tree

Generic objects have a tremendous amount of built-in functionality. Much of this is built into GenClass , the topmost class in the generic class tree. For full details on GenClass and the other generic classes, see the Object Reference Book.

Among the features offered by all generic classes are

  • Visual Representation
    Through the specific UI library, every generic object provides its own visual representation. The application does not have to do any gadget drawing.
  • Monikers
    Every generic object can have a moniker, which is a name or graphic that gets displayed along with the object.
  • Input Management
    Through the specific UI library, every generic object properly handles mouse and keyboard input.
  • Messaging
    As with all objects in the system, generic objects can receive and send messages. In addition, generic objects can pass messages up or down the generic object tree automatically.
  • Enabled and Usable States
    Generic objects understand their usable and enabled states. If an object is not enabled, it may be visible but the user cannot invoke its action. If an object is not usable, it will not be visible on the screen (in most specific UIs). Entire object trees can be set usable or enabled with one command.
  • State Saving
    Generic objects automatically save their state when the system shuts down. Therefore, when the system comes back up, dialog boxes and windows will automatically be restored to the same state they were left in when shut down.
  • Object Tree Management
    All generic objects must be part of a generic object tree to be displayed. Generic objects inherently understand the tree structures and functions. UI gadgetry can dynamically be added, moved, or removed.

Each of the different generic classes is described in overview depth below. Note that every one of these classes may be subclassed to add, change, or remove functionality. Changing or removing functions from a generic class is not encouraged, however, as it can cause a specific UI library to give unpredictable results.

GenClass

GenClass provides the functionality basic to all generic objects. GenClass is not used directly by any applications and has no visible representation. Rather, all generic classes are subclassed off GenClass . GenClass provides instance fields common to all of its subclasses. Instance fields of special interest include

  • links between parents and children, providing the means of constructing a generic tree.
  • text or graphics strings to serve as an object's visual moniker.
  • a keyboard accelerator to activate an object through keyboard events.
  • a state field relating to the usable state of an object.
  • an attributes field relating to other default behavior, such as how the object handles busy states when an application is waiting for a routine to finish.

GenClass also implements scores of hints that can affect UI geometry, visual representation, data structures, and functions.

GenApplicationClass

GenApplicationClass provides the basic functionality to open and close applications within GEOS. An object of this class serves as the top object in any application for GEOS.

GenPrimaryClass

GenPrimaryClass is a subclass of GenDisplayClass . The GenPrimary is the chief UI grouping object of an application, and it usually appears as the application's primary window. An application's GenPrimary object manages all controls and output areas that are invoked when an application is first launched. You will usually create a GenPrimary as the sole child of your GenApplication object.

GenTriggerClass

A GenTrigger is a simple pushbutton that executes an action when activated by the user. Typically, the trigger will have a moniker displayed within it and will be activated by a mouse click or by a special keystroke sequence. GenTriggers are very common in applications.

GenInteractionClass

GenInteraction objects are essentially grouping mechanisms. GenInteractions are the key objects for creating both menus and dialog boxes, and they can be used to organize the geometry of other generic objects. Typically, a GenInteraction will have a number of children, each of which will appear within the interaction on the screen. The Interaction itself may or may not have a visible representation.

GenViewClass

The GenView object provides a scrollable window in which the application has complete drawing control. Most applications will use a GenView, and many will use it in conjunction with a VisContent object. The View is extremely powerful, providing all clipping, scrolling, resizing, and scaling automatically. A View can even be splittable or linked to other views. The GenView can display either normal graphic documents or hierarchies of visible objects.

List Classes

Together, GenBoolean, GenBooleanGroup, GenItem, GenItemGroup, and GenDynamicList provide many different types of lists. List objects may be used to create lists that are dynamic or static; scrollable or not; exclusive, non-exclusive, or otherwise. List objects may appear within menus or dialog boxes as well as within an application's primary window.

GenValueClass

The GenValue object allows the user to set a value within a particular range. This may be implemented as a slider, a spinner, or a pair of up/down buttons next to the value. Ranges may use scalar or distance values and can have their maximum and minimum values set by the application.

GenTextClass

GenTextClass is tremendously versatile and can be used for text displays or text-edit fields. The GenText is used by nearly every application that either displays text or requires text input. It is so versatile and powerful that it can provide the power of an entire word processor without any additional code in the application.

The GenText object supports selection and control of fonts, point sizes, text color, paragraph color, paragraph borders, margin settings, tab stops, manual leading, and character kerning, as well as several other features. The text library also provides several controllers that work with the GenText to allow the user to set all these features.

Document and Document Control Classes

Together, GenDocumentClass , GenDocumentGroupClass , and GenDocumentControlClass provide all the functions necessary to create, save, open, and edit document files. These classes provide not only the UI menus and tools (the File menu) but also the functions for managing the document files. Applications that use these classes never have to call routines to open, close, or save files--all that is done automatically, including the file selector mechanisms to aid the open and save-as functions.

GenDocument objects are created and managed automatically by the GenDocumentGroup. Each document object represents a single file which has been opened or newly created by the user.

Display and Display Control Classes

Together, the GenDisplay, GenDisplayGroup, and GenDisplayControl provide display windows and the UI gadgetry to manage them. Typically, these objects will be used in conjunction with the document and document control objects to provide one display for each document.

The GenDisplayGroup object creates and manages multiple GenDisplay document windows. The GenDisplayControl object creates and maintains a Window menu to allow the user to operate on the individual displays. If your application will have multiple documents or multiple displays open, you will want to use these objects.

GenControlClass and UI Controllers

GenControlClass is used to create UI controller objects. Applications will most likely not use GenControlClass directly, though some object libraries might. For example, the Text Library uses controller objects for font control, point size control, and style control, among other things. Any application that uses the Text Library can include the font controller object; the user will then be able to select and apply fonts without the application having to do any work to support it.

The UI and various libraries provide many controllers you can use immediately. Some examples are the GenEditControlClass , which creates and maintains the Edit menu and tools; ColorSelectorClass , which creates and displays UI gadgetry to set color information; and GenViewControlClass , which creates and maintains a View menu allowing the user to set scaling and scrolling behavior.

GenToolControlClass

GenToolControlClass lets the user select which of an application's tools are available and where they should be placed (in a toolbox, in a menu, etc.). Tools are provided by UI controllers. Typically, an application that uses controllers will provide several tool areas and a GenToolControl; the GenToolControl will automatically create all the UI gadgetry to let the user select which tools are active and where they will appear.

GenFileSelectorClass

The GenFileSelector provides user interface to allow the user to navigate through his or her file system. It is used most often by the document control objects and is used directly by only some applications.

GenGlyphClass

GenGlyph displays simple text or graphics strings. Text displayed by a Glyph object is not selectable or editable; these objects are typically used for labeling areas or items on the screen.

GenContentClass

GenContentClass is used with a GenView to display other generic UI objects within a scrollable window. The GenContent is rarely used because having some of an application's UI objects not visible can confuse some users. More often, a GenDocument is used as the view's content; GenDocumentClass is subclassed from the GenContent.

UI Overview: 3.2 Using the Generic Classes: Creating a Generic Object Tree

You don't have to understand all the generic object classes to create a complete generic object tree for your application. For insight into and an example of creating a generic tree (including the primary window, a menu, a dialog box, and a scrolling view window), see the Hello World chapter.

UI Overview: 4 Using the Visible Classes

The visible classes in GEOS provide custom objects that can be used for any number of purposes. There are many visible object classes and they are so versatile that everything from spreadsheets to drawing programs to interactive games can be created from them.

Visible Objects and the GenView

The Visible Object Document

Visible Object Abilities

The Vis Class Tree

Creating a Visible Object Tree

Working with Visible Object Trees

UI Overview: 4.1 Using the Visible Classes: Visible Objects and the GenView

Visible objects are designed for flexibility and for interacting with the user. Typically, visible objects will reside in an object tree, the root of which is a VisContent or GenDocument object connected to a GenView (this section assumes the VisContent, though the GenDocument is roughly equivalent for the purposes of overview). The relationship between these objects is simple, yet it accomplishes an immense amount of work for the application.

The GenView provides a window that may be scrollable, scalable, and resizable. The view is directly connected to the VisContent object and works very closely with it to handle input and drawing events. The VisContent acts as a manager for the visible object tree; it passes input events (mouse moves and mouse clicks) on to the proper visible object directly under the mouse pointer, and it makes sure all visible objects in the tree draw themselves at the appropriate time.

The view and VisContent may be connected tightly or very loosely for sizing purposes. For example, the view may be forced to resize itself to the proper size of the VisContent; or, the view might be scrollable and resizable independent of the content's size. The VisContent can choose whether mouse events are expected or required, and the view will notify the VisContent whenever the window has been invalidated and the document needs redrawing.

UI Overview: 4.2 Using the Visible Classes: The Visible Object Document

Visible objects exist in an object tree and draw themselves in the GEOS graphic space. Every visible object knows where in the graphic coordinate space it sits and how big it is. The top object of the object tree, the VisContent, manages the entire tree. Composite objects (of class VisCompClass ) can be used as organizational objects that control other objects.

Using the VisContent, the VisComp, and the standard Vis objects, you can create just about any interactive document you want. (There are other available object libraries such as the spreadsheet and ruler libraries, and they are based on these three building blocks.)

Every visible object knows what its bounds are; that is, each object knows exactly how big it is and where it sits in the graphics document. The bounds are always rectangular in the base VisClass format, though a subclass of VisClass could easily be created to handle more complex shapes. Bounds are in the standard graphics coordinates (i.e. 16-bit numbers based on a 72 dpi grid). Vis objects may also exist in large documents (32-bit coordinates), but the objects must handle the majority of the extra coordinate manipulation on their own.

Visible objects do not inherently know how to draw themselves. However, the GenView, when resized or scrolled (for example), will send MSG_META_EXPOSED to the VisContent. The VisContent object will then send a MSG_VIS_DRAW message to itself and to all its visible object children, directing them to draw themselves in the proper place. For the visible object to draw itself, it must handle MSG_VIS_DRAW . In its handler, it can call any normal graphics commands to draw anything wherever it wants (not just within its bounds).

For examples of visible objects that know their bounds and that handle MSG_VIS_DRAW , see A UI Example .

UI Overview: 4.3 Using the Visible Classes: Visible Object Abilities

Visible objects can be used in innumerable situations; with a little work from the application developer, they provide dozens of useful features including

  • Knowledge of Their Bounds
    Every visible object knows exactly where in the application's document it sits and exactly how big it is. It can easily change its own bounds, either moving itself in the document space or resizing itself.
  • Ability to Draw Themselves
    Every visible object is responsible for drawing itself. When it receives a drawing message ( MSG_VIS_DRAW ), it must recognize the context of the message and draw itself appropriately. VisClass does not have a default handler for MSG_VIS_DRAW ; each subclass must handle this message itself.
  • Input Handling
    Pointer and click events as well as keyboard input can be automatically transmitted through the view to the VisContent if desired. Several combinations of event pass-throughs are available. When the VisContent receives a mouse event, it can intercept and handle it or pass it on to whatever object is under the pointer. If the VisContent does not handle the event (such as MSG_META_PTR ) explicitly, the message will automatically be passed on to the object under the pointer.
  • Geometry Management
    Both the view and the VisContent can interact to provide specific sizing behavior for the window and content. Additional geometry management is available to help a VisComp composite object manage its children.
  • Object Tree Management
    Messages may easily be passed to an object's parent or children. Also, objects may be added to or removed from the visible tree without difficulty.
  • Use of VisMonikers
    In addition to simply drawing itself, a visible object can have a visible moniker that it can draw as well. This simplifies drawing text or handling MSG_VIS_DRAW in that the moniker can be set beforehand and simply drawn with a single call.

Using visible objects allows an application a lot more flexibility in display and input handling than is available with the generic objects. It also requires more programming; however much of the most difficult work is made easy by the view and VisContent objects. The application does not have to worry about clipping or scrolling or even determining what type of input event is taking place. All this is somewhat automatic and can, for the most part, be ignored.

UI Overview: 4.4 Using the Visible Classes: The Vis Class Tree

There are four base visible classes on which the other object libraries are founded. VisClass is the most basic and at the root of the visible class tree. Under it are VisCompClass and VisTextClass . VisContentClass is a subclass of VisCompClass .

VisTextClass is special in that it is rarely subclassed as it already contains nearly all the functionality an application will need from a visible text object.

The other three classes are typically subclassed by applications. None of the three can draw itself; instead, the subclass must handle MSG_VIS_DRAW , the message that indicates the object must draw itself. The three different classes are used as follows:

  • VisClass
    VisClass     is the head of the visible class tree and therefore is the most broad-based in functionality. VisClass objects can not have children and therefore can only exist as the leaves of the object tree.
  • VisCompClass
    VisCompClass     provides a composite VisClass object. Essentially, this class is the same as VisClass except that it can have children. It also includes several special geometry options that allow it to manage and place its children as well as set their bounds.
  • VisContentClass
    VisContentClass     is used only as the content of a GenView. It is a subclass of VisCompClass and therefore can have children and manage them with the same geometry features available to VisComp. In addition, the VisContent interacts with the GenView to handle input and drawing. The content can interact with the view to determine sizing behavior as well as input behavior.

UI Overview: 4.5 Using the Visible Classes: Creating a Visible Object Tree

You can create a visible object tree either in your Goc source code or at run-time. As stated earlier, the visible tree is linked to the generic UI object tree through the GenView object. Without a view object, you will not be able to display your visible objects.

The visible object tree must have a VisContentClass object (or a subclass of VisContentClass ) as its root object. If another class is chosen for the topmost object, that class will have to handle all the messages that would normally be received by a content; otherwise, results are unpredictable and your application will likely not function the way you expect.

VisContent objects are rarely, if ever, used lower in the visible tree than as the top node. For levels further down in the tree, you can use VisCompClass (or, again, a subclass of VisCompClass ). VisComp allows the object to have children and does not incur the same amount of overhead associated with a VisContent. Any object in the visible tree that may have children must be a "composite" object, or a subclass of VisCompClass .

Leaf nodes of the visible tree can be direct subclasses of VisClass . Objects of VisClass can not have children, but they have all the attributes and instance data you need for a leaf node. You must subclass VisClass , however, if your object is ever to be shown on the screen: Since visible objects can have any application-defined visible representation, you must write the MSG_VIS_DRAW handler yourself; this can be done only in a subclass.

UI Overview: 4.6 Using the Visible Classes: Working with Visible Object Trees

Working with visible object trees is quite easy and provides immense flexibility to your applications. Entire groups of objects can be added to, removed from, or moved around in the display with a single command. New objects can be created during execution, and others can be destroyed at will.

Sending Messages in the Tree

Often, an object in the visible tree will need to contact its parent or its children. For example, a child that needs to know the state of its parent must be able to find and contact that parent. Likewise, a parent that must notify all its children to redraw themselves must be able to quickly send out the notification.

An object can send a message to its parent by simply specifying @visParent as the message recipient. That same object could also contact all its children by specifying the recipient as @visChildren . The message sent (only with @send since there are multiple destinations) will go to each of the object's children.

If you need more complicated message passing, you can build this into your Vis subclasses. It is not difficult for an object to retrieve the optr of any of its children or of its parent; it can then send the message appropriately. Each recipient can handle it in the same manner, forwarding it on to the next layer of the tree.

VisClass also employs several messages known as "visual upward queries," or "VUP" messages. These travel up the visible tree until they encounter an object of the proper class, where they will be handled.

Altering the Tree

VisClass and VisCompClass can handle messages that retrieve various information about the visible tree. They also have messages for altering the visible tree's structure.

UI Overview: 5 A UI Example

This section uses the sample application TicTac, a simple Tic Tac Toe board and pieces that can be moved around the board.

What TicTac Illustrates

What TicTac Does

The Structure of TicTac

TicTacBoard Specifics

TicTacPiece Specifics

UI Overview: 5.1 A UI Example: What TicTac Illustrates

The TicTac sample application can teach you several things about how the visible world works and about how to manage visible objects in an application. It is simple but illustrates the following concepts:

  • Drawing Visible Objects
    Every visible object must be able to draw itself. VisClass does not have any inherent code to make visible objects visible; instead, subclasses of VisClass must handle MSG_VIS_DRAW .
  • Changing an Object's Position
    The pieces of the TicTac game allow the user to move them around the screen using the mouse. This entails tracking the mouse and then changing the object's position in the document.
  • Sending Messages Up and Down the Visible Tree
    The pieces must interact with the VisContent object to ensure they stay on the game board, and the VisContent must notify the pieces when the "New Game" button has been pressed. Therefore, messages must be passed both up and down the visible tree.
  • Handling Input Events
    In order for the user to move the pieces around the board, the pieces must be able to receive mouse events. Several different types of mouse events are handled to show the pieces moving around the board and to handle input properly.
  • Drawing Background Graphics
    Because the game board does not change at all, its background is drawn by the content object. The content could change its drawing behavior if necessary, but background graphics are most easily drawn by the content.
  • Visible Tree Structure
    A simple, two-layer visible tree is used in the TicTac application: The VisContent is the root of the tree, and each of the pieces is a leaf.

UI Overview: 5.2 A UI Example: What TicTac Does

The TicTac sample application is extremely simple. It draws a Tic Tac Toe board and its outline, and it has ten pieces which the user can move. Five of these pieces are gray squares, and five are gray circles. Each of the pieces may be moved by clicking and dragging it with the mouse. Any piece may be moved anywhere on the board, but pieces may not be moved off the board.

A Game menu with a "New Game" trigger replaces all the pieces to their original locations. Because this is a simple example, no actual rules of any kind are enforced. Recognition of winning sequences and rules involving turn sequencing or playing against the computer are left as exercises for the reader.

UI Overview: 5.3 A UI Example: The Structure of TicTac

The TicTac sample application is coded in two files: The first, tictac.gp , is the geode parameters file. The other, tictac.goc , contains all the code for objects in the application. The geode parameters file is similar to other .gp files and is not discussed in this section.

The application uses two different object trees, one for its generic UI and one for the game board and game pieces. The first consists of generic UI objects and the second of objects subclassed off VisClass and VisContentClass . The two object trees are diagrammed and described below.

TicTac's Generic Tree

The TicTac application begins with two standard generic objects common to all applications: GenApplication and GenPrimary. These two objects are described in detail in other sections and are not covered here, though their definitions are shown in TicTacApp and TicTacPrimary .

The primary object, TicTacPrimary, has two children. One, TicTacGameMenu, implements the game menu; the other, TicTacView, provides the window through with the user can interact with the visible tree.

Code Display 6-1 TicTacApp and TicTacPrimary

@start AppResource;
    /* The AppResource resource block contains the TicTacApp object only. This
     * object is in its own resource for performance purposes. */
@object GenApplicationClass TicTacApp = {
    GI_visMoniker = list { TicTacTextMoniker }
    GI_comp = TicTacPrimary;
    gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_WINDOWS) = TicTacPrimary;
}
@visMoniker TicTacTextMoniker = "TicTacToe";
@end AppResource
@start Interface;
    /* This is the Primary window of the application. It is not minimizable
     * (since no icon is defined for it). It has two children: The View
     * object and the Menu object. */
@object GenPrimaryClass TicTacPrimary = {
    GI_comp = TicTacView, TicTacGameMenu;
    ATTR_GEN_DISPLAY_NOT_MINIMIZABLE;
    HINT_SIZE_WINDOW_AS_DESIRED;
}
TicTacGameMenu

This object is a standard GenInteraction object set up as a menu. The code for the entire menu (including the TicTacNewTrigger) is shown in The TicTac Game Menu and is heavily commented as to how each attribute is used.

The New Game trigger may be invoked at any time except when a piece is being moved. When a piece is being moved, that piece object has the "mouse grab," meaning that it will receive all mouse and pointer events until it releases the grab. When an object has the mouse grab, no mouse events may be sent to another object, and therefore the menu object can not be clicked on while the piece has the grab.

When the user presses the New Game trigger, the trigger sends its message, MSG_TICTAC_NEW_GAME , to the TicTacBoard object. The TicTacBoard object is the top object in the visible tree and, upon receipt of this message, resets the game board and notifies all the piece objects of the reset. This process is described below in TicTacBoard Specifics .

Code Display 6-2 The TicTac Game Menu

/* The TicTacGameMenu object is the only menu of this application. Its only child
 * and only menu entry is the TicTacNewTrigger object. */
@start Interface;			/* In the same resource block as TicTacPrimary. */
@object GenInteractionClass TicTacGameMenu = {
    GI_visMoniker = "Game";					/* The name of the menu. */
    GI_comp = @TicTacNewTrigger;					/* The only menu item. */
    GII_visibility = GIV_POPUP;					/* This attribute indicates that this
                     * interaction is a menu rather than
                     * a dialog. */
}
@object GenTriggerClass TicTacNewTrigger = {
    GI_visMoniker = "New Game";					/* The name of the menu item. */
    GTI_destination = @TicTacBoard;					/* The object to receive the "New Game"
                     * message: the game board object. */
    GTI_actionMsg = MSG_TICTAC_NEW_GAME;					/* The message to be sent when the trigger
                     * is pressed. */
}
@end Interface			/* End of the Interface resource block */
TicTacView

The TicTacView object is a standard GenView set up to run the TicTacBoard object. The code for TicTacView is shown in The TicTacView Object and is heavily commented to show what each of the view's attributes is used for.

TicTacView's content object is TicTacBoard, the game board object. This means that any appropriate input events as well as all messages sent out by the view will be passed directly to the TicTacBoard object. The sizing attributes and the fact that the view is not marked GVDA_SCROLLABLE in either dimension makes sure the view sizes exactly to the game board's bounds.

A GenView object is necessary in every case where a visible object tree is used. The view not only displays the visible tree but also handles all clipping, scaling, scrolling, and sizing if any is desired (none is used in TicTac). It also takes and passes on any appropriate mouse or keyboard input events. Additionally, it interacts directly with its content object (in this case TicTacBoard) to determine proper geometry and sizing behavior of the content and the view.

This view also provides the background color of the game board, dark blue.

Code Display 6-3 The TicTacView Object

    /* This object provides the window through which the user interacts with the
     * visible object tree. This object communicates with the game board object (a
     * subclass of VisContentClass) to coordinate drawing, clipping, sizing, and
     * even input handling. */
@start Interface;			/* In the same resource block as TicTacPrimary. */
@object GenViewClass TicTacView = {
    GVI_content = @TicTacBoard;				/* The content object of this view is the
                 * TicTacBoard object, the root object of the
                 * visible object tree. */
    GVI_color = { C_BLUE, 0, 0, 0 };					/* The background color of this view
                     * should be dark blue. */
    /* The horizontal attributes of this view set it to the same
     * size as the game board, and the view is not scrollable. */
    GVI_horizAttrs = @default				| GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;
    /* The vertical attributes of this view set it to the same size
     * as the game board, and the view is not scrollable. */

    GVI_vertAttrs = @default			|	 GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT
                | GVDA_KEEP_ASPECT_RATIO;
    /* The user won't need to type anything, so there's no need for
     * a floating keyboard. */
    ATTR_GEN_VIEW_DOES_NOT_ACCEPT_TEXT_INPUT;
}
@end Interface			/* End of the Interface resource block */

TicTac's Visible Tree

The visible tree contains eleven objects. One acts as TicTacView's content and is of TicTacBoardClass , a subclass of VisContentClass . The other ten are all game pieces of class TicTacPieceClass , a subclass of VisClass . Both the class definitions and the object definitions are given in TicTacBoardClass and TicTacPieceClass .

All eleven of the visible objects remain on the screen and in the view during their entire existence. The game board and all its pieces are shown in the figure above; this illustration represents the basic configuration of the game board when the application first starts or when the user presses the "New Game" trigger in the "Game" menu.

The TicTacBoard object draws the border around the board and makes sure the view window sizes itself to the same size as the board. The board is 180 points (2.5 inches) in height and 270 points (3.75 inches) in width; these numbers are stored as the constants BOARD_HEIGHT and BOARD_WIDTH. TicTacBoard also draws the playing field--this consists of the four white lines on the left side of the game board. TicTacBoard's other main function is to ensure that all the children (game pieces) behave properly; it makes sure the child's bounds are on the game board when the piece is moved, and it notifies the game pieces when they must draw themselves due to a view exposure. Finally, TicTacBoard receives the "New Game" message from the Game menu; it then redraws the game board and notifies each of the game pieces that they should return to their initial locations.

Each of the game piece objects knows about its location and status. Each piece knows its initial location, current location, and proposed location (during a move). Every game piece is an instance of TicTacPieceClass . This class is shown in TicTacBoardClass and TicTacPieceClass ; it contains a number of instance data fields for these locations. It also has an instance data field indicating what type of piece (i.e. "box" or "ring") the object is.

The "box" objects are designated by having the value TTPT_BOX in their TTP_pieceType fields; the "ring" objects have TTPT_RING in that field. Both types of objects act and react in the same way to various events; the only difference is in their shape and color.

Code Display 6-4 TicTacBoardClass and TicTacPieceClass

/* The TicTacPieceTypes enumerated type lists the different types of game pieces a
 * particular piece object can be. In this game, a piece is either a "box" (gray
 * square) or a "ring" (light gray circle).									*/
typedef ByteEnum TicTacPieceTypes;
#define TTPT_BOX 0
#define TTPT_RING 1
/***********************************************************************
 * TicTacBoardClass
 * This class is a subclass of VisContentClass and provides the game board
 * for this application. It also manages all the children (piece objects).
 * Because it is a subclass of VisContentClass, it inherits all the instance
 * data fields and messages of that class.
 ***********************************************************************/
@class TicTacBoardClass, VisContentClass;					/* this class is a subclass
                     * of VisContentClass */
    /* Message definitions for this class */
    @message void MSG_TICTAC_NEW_GAME();
    /* This message is sent by the New Game trigger in the Game menu
     * when the user wants to reset the game. It is sent directly to
     * the game board object and causes the board object first to
     * send the "new game" message to each of its children and then
     * to redraw the game board.							*/
    @message Boolean MSG_TICTAC_VALIDATE_BOUNDS(word bottom, word right,
                        word top, word left);
    /* This message is sent by a game piece that is being moved by the
     * user and is about to be set down. The four parameters are the
     * proposed new bounds of the moved piece; if they are within the
     * game board's limit, this message returns TRUE. If they are at
     * all outside the game board, this message returns FALSE.	*/
@endc
/* Declare the class in memory so the method table will be built. */
@classdecl TicTacBoardClass;
/***********************************************************************
 * TicTacPieceClass
 * This class is a subclass of VisClass and provides all the functions
 * necessary for a game piece in this game. Because it is a subclass of
 * VisClass, it inherits all the instance data fields and messages of
 * that class.
 ***********************************************************************/
@class TicTacPieceClass, VisClass;					/* this class is a subclass
                     * of VisClass */
    /* The instance data fields of this class: */
    @instance TicTacPieceTypes TTP_pieceType;
    /* TTP_pieceType defines whether the object of this class is
     * a "box" or a "ring."							*/
    @instance int TTP_vertPos;
    /* TTP_vertPos indicates the current y position of
     * the piece. This does not indicate the piece's actual
     * bounds but rather where its moving outline appears.							*/
    @instance int TTP_horizPos;
    /* TTP_horizPos indicates the current x position of
     * the piece. This does not indicate the piece's actual
     * bounds but rather where its moving outline appears.							*/
    @instance int TTP_origVertPos;
    /* TTP_origVertPos indicates the y position where this
     * piece should return when the New Game trigger is pushed
     * and the piece goes back to its original location.							*/
    @instance int TTP_origHorizPos;
    /* TTP_origHorizPos indicates the x position where this
     * piece should return when the New Game trigger is pushed
     * and the piece goes back to its original location.							*/
    @instance Boolean TTP_dragging;
    /* A flag indicating whether the user is in the process of dragging
     * the game piece around the board. */
    /* Message definitions unique to this class. */
    @message void MSG_PIECE_NEW_GAME();
    /* This message notifies the piece object that the user has pushed *
     * the New Game trigger and that the piece should return to its *
     * original position on the board (the TTP_orig(Horiz/Vert)Pos *
     * fields).							*/
@endc
/* Declare the class in memory so the method table will be built. */
@classdecl TicTacPieceClass;

UI Overview: 5.4 A UI Example: TicTacBoard Specifics

The TicTacBoard object, being of a subclass of VisContentClass , handles many messages specific to content objects. However, only three messages are handled specifically by TicTacBoardClass . These messages are

MSG_VIS_DRAW
This message notifies the object it must draw itself and any accompanying graphics. TicTacBoard responds by drawing the game board (the border and crossed lines) and by passing the MSG_VIS_DRAW on to all of its children.
MSG_TICTAC_NEW_GAME
This message is sent by the New Game trigger in the Game menu. TicTacBoard responds by sending another new game message, MSG_PIECE_NEW_GAME , to each of its children. Each child will position itself and draw itself properly; TicTacBoard will then redraw the game board by sending itself a MSG_VIS_DRAW .
MSG_TICTAC_VALIDATE_BOUNDS
This message is sent by a game piece object when it is being moved. TicTacBoard checks the parameters passed and determines whether they are on the game board or not; if they are it returns TRUE, and if they aren't it returns FALSE.

Each of the methods for the above messages is shown in Methods of TicTacBoardClass . Each is heavily commented and explains the theory behind the method.

Code Display 6-5 Methods of TicTacBoardClass

/***********************************************************************
 *
 * MESSAGE:		MSG_TICTAC_NEW_GAME for TicTacBoardClass
 *
 * DESCRIPTION:		This method notifies each of the visible children that
 * 		a new game is beginning; they should take their places,
 * 		and then the board object will redraw itself
 *
 * PARAMETERS:
 * 	void ()
 *
 ***********************************************************************/
@method TicTacBoardClass, MSG_TICTAC_NEW_GAME {
    WindowHandle win;				/* the window to draw to			*/
    GStateHandle gstate;				/* the gstate of the window */
    /* First notify all the children (game pieces)
     * that a new game is beginning.						*/
    @send @visChildren::MSG_PIECE_NEW_GAME();
    /* Now initiate a new gstate for the view window.
     * Get the window handle from the view, and then
     * create a new gstate for it.						*/
    win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW();
    gstate = GrCreateState(win);
    /* Invalidate the game board rectangle in the document.
     * This will cause the view object to generate a
     * MSG_META_EXPOSED for the rectangle, causing MSG_VIS_DRAW
     * to be sent to this object (TicTacBoard).						*/
    GrInvalRect(gstate, 0, 0, BOARD_WIDTH, BOARD_HEIGHT);
    /* Now destroy the temporary gstate. This is important
     * to keep too many gstate handles from being locked
     * and slowing down the system.						*/
    GrDestroyState(gstate);
}
/***********************************************************************
 *
 * MESSAGE:		MSG_VIS_DRAW for TicTacBoardClass
 *
 * DESCRIPTION:		This method draws the board's outline and the
 *		lines of the playing field. It is sent each time
 *		a portion of the view window becomes invalid (such
 *		as when the primary is moved).
 * PARAMETERS:
 *	void (word drawFlags, GStateHandle gstate)
 *		gstate is the handle of the graphics state associated
 *		with the exposed portion of the view window
 ***********************************************************************/
@method TicTacBoardClass, MSG_VIS_DRAW {
    /* Set up the graphic state properly. The board
     * lines are to be white and three points thick.						*/
    GrSetLineColor(gstate, CF_INDEX, C_WHITE, 0, 0);
    GrSetLineWidth(gstate, 3);
    /* Now draw the border of the game board. It is a
     * rectangle that outlines the entire board.						*/
    GrDrawRect(gstate, 0, 0, BOARD_WIDTH, BOARD_HEIGHT);
    /* Set and draw the Tic Tac Toe playing field. The
     * lines are now set to 4 points thickness, and the
     * lines are drawn with HLine and VLine graphics
     * commands. Ideally, preset constants would be used.*/
    GrSetLineWidth(gstate, 4);
    GrDrawHLine(gstate, 5, 60, 175);
    GrDrawHLine(gstate, 5, 120, 175);
    GrDrawVLine(gstate, 60, 5, 175);
    GrDrawVLine(gstate, 120, 5, 175);
    /* When the MSG_VIS_DRAW is received by the game board,
     * it must pass it on to its visible children. It must
     * also pass on the parameters of the message as passed
     * to ensure all drawing is done properly.						*/
    @send @visChildren::MSG_VIS_DRAW(drawFlags, gstate);
}

/***********************************************************************
 *
 * MESSAGE:		MSG_TICTAC_VALIDATE_BOUNDS for TicTacBoardClass
 *
 * DESCRIPTION:		This method checks to see if the bounds passed
 *		are on the game board. This is invoked when a game
 *		piece is in motion and receives an END_SELECT message
 *		indicating it is being put down. The piece must
 *		determine whether the suggested bounds are on the
 *		game board; the piece should always query the board
 *		object rather than check directly; if the board were
 *		resizable, the piece could be incorrect sometimes.
 *
 * STRATEGY:		Check the four bounds against the board's edges. If
 *		all four are on the board, return TRUE. If any one
 *		of the four is off the board, return FALSE.
 * PARAMETERS:
 *	void (word bottom, word right, word top, word left)
 ***********************************************************************/
@method TicTacBoardClass, MSG_TICTAC_VALIDATE_BOUNDS {
    if (((bottom < BOARD_HEIGHT) && (top > 0))
            && ((right < BOARD_WIDTH) && (left >= 0))) {
    return(TRUE);
    } else {
    return(FALSE);
    }
}

UI Overview: 5.5 A UI Example: TicTacPiece Specifics

TicTacPieceClass contains most of the game's functionality. Since the user interacts directly with each game piece, the piece must know not only how to draw itself but also how to react to user input.

MSG_PIECE_NEW_GAME
This is the only message generated by the game itself that a game piece receives; it is sent by the TicTacBoard object when the user has pressed the New Game trigger. The game piece object responds by resetting its bounds to the original settings. It does not have to redraw or invalidate its old bounds because the TicTacBoard object will send a MSG_VIS_DRAW later and will invalidate the entire board.
MSG_VIS_DRAW
This message notifies the object that it must draw itself and any accompanying graphics. The game piece responds by drawing the proper shape in the proper color in the proper place. Since every VisClass object inherently knows its location and bounds, the object already knows where and how big the shape should appear. Whether a gray box or circle is drawn depends on the TTP_pieceType instance data field.
MSG_META_START_SELECT
This message is sent by the system when the user clicks a mouse button. The UI sends the message to whatever object lies under the pointer. The UI objects then pass the message down the object tree until it gets handled. The progression sends the message to TicTacApp, which passes it to TicTacPrimary, which passes it to TicTacView, which passes it to TicTacBoard, which (by letting the default VisContentClass method handle it) passes it to the proper game piece object (if any) under the pointer. The game piece responds by grabbing the mouse and all subsequent pointer events.
MSG_META_DRAG_SELECT
This message, like MSG_META_START_SELECT , indicates that the user has clicked a mouse button and has initiated a drag event. (Normally, this is used to select ranges or groups of objects; in TicTac, however, it is treated like MSG_META_START_SELECT .)
MSG_META_DRAG
This message is sent after the user has clicked the mouse button and is now moving the mouse pointer (and has not released the button yet).
MSG_META_PTR
This message is sent when the pointer image is over the bounds of the game piece whether or not a mouse button has been pressed. (After the object has grabbed the mouse events by handling MSG_META_START_SELECT , the pointer event is sent whenever the mouse pointer is moved.) The piece determines whether or not it is being dragged around the screen. This is known as a "drag event," and the game piece responds by drawing a piece-shaped outline around the mouse pointer. This outline will follow the pointer around the screen until the user releases the mouse button (causing a MSG_META_END_SELECT , below). The outline is first drawn in either the MSG_META_START_SELECT or MSG_META_DRAG_SELECT handler (whichever is called to start the drag event). MSG_META_PTR and MSG_META_END_SELECT erase the outline before drawing a new one. The game piece will maintain three locations in its instance data: Its VI_bounds field maintains its position when selected. Its TTP_orig(horiz/vert)Pos fields maintain its original position when the game was first started. Its TTP_(horiz/vert)Pos fields maintain the current position of the outline and where the object would relocate to if the move was ended now. If the event is not a drag, the object will not react because it is assumed that no mouse button has been pressed and therefore the user is taking no action.
MSG_META_END_SELECT
This message is sent when the user releases a pressed mouse button, ending the select-and-drag process. The game piece reacts by checking if the location of the pointer is a legal position on the game board. (It does this by sending a verification message to the TicTacBoard object to make sure the proposed new bounds are on the game board.) If the position is legal, the game piece moves itself there, erasing any leftover outlines (from the drag sequence) and its original image on the board. It then causes itself to draw in the new location by sending itself a MSG_VIS_DRAW . If the new location is not legally on the game board, then the object will reset all its instance data and erase any leftover outlines, causing it to revert to its location before the select-and-drag sequence began.

Each of the methods for the above messages is shown in Methods for TicTacPieceClass .

Code Display 6-6 Methods for TicTacPieceClass

/***********************************************************************
 *
 * MESSAGE:		 MSG_PIECE_NEW_GAME for TicTacPieceClass
 *
 * DESCRIPTION:		This message causes the piece to replace itself
 *		to its original position. It is invoked when the
 *		user presses the New Game trigger; the trigger sends
 * 		MSG_TICTAC_NEW_GAME to the TicTacBoard object, and
 * 		the board object sends this message to each of
 * 		the game piece objects.
 *
 * PARAMETERS:
 * 	void ()
 ***********************************************************************/
@method TicTacPieceClass, MSG_PIECE_NEW_GAME {
    /* Set the current (motion) positions to the original positions. */
    pself->TTP_vertPos = pself->TTP_origVertPos;
    pself->TTP_horizPos = pself->TTP_origHorizPos;
    /* Send a MSG_VIS_BOUNDS_CHANGED to ourselves to make
     * sure the old bounds get redrawn. This message will
     * cause an invalidation of the document where the old
     * (passed) bounds were, causing that portion of the
     * window to be redrawn.						*/
    @call self::MSG_VIS_BOUNDS_CHANGED(pself->VI_bounds.R_bottom,
            pself->VI_bounds.R_right, pself->VI_bounds.R_top,
            pself->VI_bounds.R_left);
    /* Set the bounds of the object (VI_bounds) back to
     * their original values. The Rectangle structure
     * contains four fields, each of which must be set.						*/
    pself->VI_bounds.R_left = pself->TTP_origHorizPos;
    pself->VI_bounds.R_top = pself->TTP_origVertPos;
    pself->VI_bounds.R_right = (pself->TTP_origHorizPos + PIECE_WIDTH);
    pself->VI_bounds.R_bottom = (pself->TTP_origVertPos + PIECE_HEIGHT);
    /* This method does not need to invoke a MSG_VIS_DRAW
     * because the TicTacBoard object will do that. The
     * piece object will later receive a MSG_VIS_DRAW that
     * will cause the piece to be redrawn back at its
     * original location (the newly set bounds).						*/
}
/***********************************************************************
 *
 * MESSAGE:		MSG_VIS_DRAW for TicTacPieceClass
 *
 * DESCRIPTION:		Draw the piece at the current location. If the piece
 *		is a "box," draw a gray square. If the piece is a
 *		"ring," draw a gray circle. This message is received
 *		whenever a portion of the view window becomes invalid;
 *		TicTacView will send a MSG_META_EXPOSED to TicTacBoard,
 *		which will send itself (by default) a MSG_VIS_DRAW.
 *		The MSG_VIS_DRAW will be handled and then will be
 *		passed on to each of the game pieces. Then each piece
 *		(in this handler) will draw itself at its own bounds.
 *
 * PARAMETERS:
 *	void (word drawFlags GStateHandle gstate)
 *
 ***********************************************************************/
@method TicTacPieceClass, MSG_VIS_DRAW {
    /* Set the mode to MM_COPY; this means that the image
     * drawn now will be drawn over whatever is there now.*/
    GrSetMixMode(gstate, MM_COPY);
    /* If the type is TTPT_BOX, set the color to gray and
     * draw a rectangle the size of the object's bounds.
     * Otherwise (since there are just two types), set the
     * color to gray and draw an ellipse of that size.						*/
    if (pself->TTP_pieceType == TTPT_BOX) {
    GrSetAreaColor(gstate, CF_INDEX, C_DARK_GRAY, 0, 0);
    GrFillRect(gstate, pself->VI_bounds.R_left, pself->VI_bounds.R_top,
            pself->VI_bounds.R_right, pself->VI_bounds.R_bottom);
    } else {
    GrSetAreaColor(gstate, CF_INDEX, C_LIGHT_GRAY, 0, 0);
    GrFillEllipse(gstate, pself->VI_bounds.R_left, pself->VI_bounds.R_top,
            pself->VI_bounds.R_right, pself->VI_bounds.R_bottom);
    }
    /* After handling the message, call the superclass to
     * ensure that no default behavior has been mucked up.
     * This is actually not necessary in this particular case. */
    @callsuper();
}
/***********************************************************************
 * MESSAGE:		MSG_META_START_SELECT for TicTacPieceClass
 *
 * DESCRIPTION:		Grabs the mouse and calls for future pointer events.
 *		When the user clicks in the view, TicTacView will pass
 *		the click event to TicTacBoard. Since TicTacBoardClass
 *		does not intercept the event, VisContentClass passes
 *		it on to its child object currently under the pointer.
 *
 *		When the piece object receives this message, it means
 *		it has been clicked on by the user and the mouse button
 *		is still down. The piece must grab the mouse so that it
 *		gets all future mouse events, and it must request that
 *		all future mouse events be sent to it. This ensures
 *		that if the pointer leaves the object's bounds while
 *		the button is still pressed, the piece object will still
 *		receive all the pointer events (otherwise they would be
 *		sent to whatever object was under the new pointer
 *		position).
 * PARAMETERS:
 *	void (MouseReturnParams *retVal, word xPosition,
 *			word yPosition, word inputState)
 ***********************************************************************/
@method TicTacPieceClass, MSG_META_START_SELECT {
    /* First grab the gadget exclusive so we're allowed to
     * grab the mouse. Then grab the mouse, so all future
     * pointer events get passed directly to the game piece. */
    @call @visParent::MSG_VIS_TAKE_GADGET_EXCL(oself);
    @call self::MSG_VIS_GRAB_MOUSE();						/* grab mouse */
    /* Finally, return that this particular click
     * event has been processed. If this flag is
     * not returned, the system will send out the
     * click event again.					*/
    retVal->flags = MRF_PROCESSED;						/* this event processed */
}
/***********************************************************************
 *
 * MESSAGE:		MSG_META_DRAG_SELECT for TicTacPieceClass
 *
 * DESCRIPTION:		This message is sent to the piece object when the
 *		select button has been pressed and the mouse has been
 *		moved, resulting in a "drag-select" event.
 *		For event processing from the View, see the header
 *		for MSG_META_START_SELECT.
 *
 * PARAMETERS:
 *	void (MouseReturnParams *retVal, word xPosition,
 *			word yPosition, word inputState)
 ***********************************************************************/
@method TicTacPieceClass, MSG_META_DRAG_SELECT {
    GStateHandle gstate;					/* temporary gstate to draw to */
    WindowHandle win;					/* window handle of view window */
    /* Start off by setting the flag indicating that
     * the piece is being dragged around the screen. */
    pself->TTP_dragging = TRUE;
    /* Next, get the window handle of the view window.
     * Then, create a new, temporary gstate to draw into
     * for that window.						*/
    win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW();
    gstate = GrCreateState(win);
    /* Now, set the current position of the game piece
     * to be centered on the pointer.						*/
    pself->TTP_vertPos = yPosition - (PIECE_HEIGHT/2);
    pself->TTP_horizPos = xPosition - (PIECE_WIDTH/2);
    /* Now, set the drawing mode of the game piece
     * to MM_INVERT to draw a new game piece outline.
     * MM_INVERT is chosen so the outline can be redrawn
     * in invert mode later to erase it and not destroy
     * anything under it.					*/
    GrSetMixMode(gstate, MM_INVERT);
    /* Now draw the outline. If the game piece is of type
     * TTPT_BOX, draw a rectangle outline. Otherwise, draw
     * an ellipse outline.						*/
    if (pself->TTP_pieceType == TTPT_BOX) {
    GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                (pself->TTP_horizPos + PIECE_WIDTH),
                (pself->TTP_vertPos + PIECE_HEIGHT));
    } else {
    GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                (pself->TTP_horizPos + PIECE_WIDTH),
                (pself->TTP_vertPos + PIECE_HEIGHT));
    }
    /* Next, destroy the temporary gstate. This is important
     * to make sure the gstate does not stay in memory and
     * begin to slow down the system as more and more
     * temporary gstates are created but not destroyed.						*/
    GrDestroyState(gstate);
    /* Finally, return that this event has been processed
     * by this method.						*/
    retVal->flags = MRF_PROCESSED;
}
/***********************************************************************
 *
 * MESSAGE:		MSG_META_PTR for TicTacPieceClass
 *
 * DESCRIPTION:		This message is received whenever the pointer passes
 *		over this game piece object's bounds (and another
 *		game piece is not sitting directly on top of it).
 *		See MSG_META_START_SELECT for a description of how the event
 *		gets passed from TicTacView to this object.
 *
 *		This message can be either a drag event or a simple
 *		pointer event. If the latter, we want to do nothing
 *		because no mouse button is pressed. If the latter,
 *		we want to execute the same function as MSG_META_DRAG.
 *
 * PARAMETERS:
 *	void (MouseReturnParams *retVal, word xPosition,
 *			word yPosition, word inputState)
 *
 ***********************************************************************/
@method TicTacPieceClass, MSG_META_PTR {
    GStateHandle gstate;					/* temporary gstate to draw to */
    WindowHandle win;					/* window handle of view window */
    /* First check if this is a drag event. If not, do
     * nothing. If so, then draw a new outline and erase
     * the old outline.					*/
    if (pself->TTP_dragging) {
    /* Get the view's window handle and create a
     * temporary gstate for drawing into.					*/
    win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW();
    gstate = GrCreateState(win);
    /* Set the drawing mode of the game piece to
     * MM_INVERT for outline drawing.					*/
    GrSetMixMode(gstate, MM_INVERT);
    /* Erase the old outline by drawing an inverse
     * outline at the old bounds.					*/
    if (pself->TTP_pieceType == TTPT_BOX) {
        GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                    (pself->TTP_horizPos + PIECE_WIDTH),
                    (pself->TTP_vertPos + PIECE_HEIGHT));
    } else {
        GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                    (pself->TTP_horizPos + PIECE_WIDTH),
                    (pself->TTP_vertPos + PIECE_HEIGHT));
    }
    /* Now set the current motion position to be
     * centered on the pointer.					*/
    pself->TTP_vertPos = yPosition - (PIECE_HEIGHT/2);
    pself->TTP_horizPos = xPosition - (PIECE_WIDTH/2);
    /* Draw the new outline at the current position.*/
    if (pself->TTP_pieceType == TTPT_BOX) {
        GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                    (pself->TTP_horizPos + PIECE_WIDTH),
                    (pself->TTP_vertPos + PIECE_HEIGHT));
    } else {
        GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                    (pself->TTP_horizPos + PIECE_WIDTH),
                    (pself->TTP_vertPos + PIECE_HEIGHT));
    }
    /* Destroy the temporary gstate and return that
     * this event has been processed.						*/
    GrDestroyState(gstate);
    }
    retVal->flags = MRF_PROCESSED;
}
/***********************************************************************
 *
 * MESSAGE:		MSG_META_END_SELECT for TicTacPieceClass
 *
 * DESCRIPTION:		This message is received when the selection button has
 *		been released and this game piece had the mouse grab.
 *		All it does is release the gadget exclusive, which will
 *		cause us to end any dragging in progress and release
 *		the mouse.
 *		When we release the gadget exclusive, the UI will then
 *		sent MSG_VIS_LOST_GADGET_EXCL to this piece, which will
 *		tell us to erase the outline and draw the game piece.
 * PARAMETERS:
 *	void (MouseReturnParams *retVal, word xPosition,
 * 			word yPosition, word inputState);
 ***********************************************************************/
@method TicTacPieceClass, MSG_META_END_SELECT {
    /* Release the gadget exclusive, then return that the
     * event has been processed. */
    @call @visParent::MSG_VIS_RELEASE_GADGET_EXCL(oself);
    retVal->flags = MRF_PROCESSED; /* this event processed */
}
/***********************************************************************
 *
 * MESSAGE:		MSG_VIS_LOST_GADGET_EXCL for TicTacPieceClass
 *
 * DESCRIPTION:		This message is received when the piece lots go of the
 *		gadget exclusive (see MSG_META_END_SELECT, above).
 *		It first checks to see if the new, proposed bounds are
 *		on the game board. If the bounds are valid, then
 *		it sets the objects VI_bounds field to the new values
 *		and causes the object to erase its original drawing
 *		and draw itself at its new bounds. If the bounds are
 *		not on the game board, it will retain the original bounds
 *		and redraw using them.
 *
 * PARAMETERS:
 *	void ()
 *
 ***********************************************************************/
@method TicTacPieceClass, MSG_VIS_LOST_GADGET_EXCL {
    WindowHandle win;					/* window handle of view window */
    GStateHandle gstate;					/* temporary gstate to draw to */
    /* First check if the piece was being dragged.
     * If not, we don't have to do anything.					*/
    if (pself->TTP_dragging) {
    /* Get the window handle of the view window and
     * create a temporary gstate for it to draw to.						*/
    win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW();
    gstate = GrCreateState(win);
    /* Set the mode for drawing the outline.						*/
    GrSetMixMode(gstate, MM_INVERT);
    /* If the game piece type is TTPT_BOX, draw a rectangle
     * outline. Otherwise draw an ellipse outline.						*/
    if (pself->TTP_pieceType == TTPT_BOX) {
        GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                ((pself->TTP_horizPos) + PIECE_WIDTH),
                ((pself->TTP_vertPos) + PIECE_HEIGHT));
    } else {
        GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
                ((pself->TTP_horizPos) + PIECE_WIDTH),
                ((pself->TTP_vertPos) + PIECE_HEIGHT));
    }
    /* Check to see if the new bounds are on the game
     * board. If they are, set the object's bounds to the
     * new values. If they are not, retain the original
     * values and set the values to those last stored.						*/
    if (@call TicTacBoard::MSG_TICTAC_VALIDATE_BOUNDS(
                    ((pself->TTP_vertPos) + PIECE_HEIGHT),
                    ((pself->TTP_horizPos) + PIECE_WIDTH),
                    pself->TTP_vertPos,
                    pself->TTP_horizPos)) {
    /* Invalidate the original drawing of the game piece.
     * Send the VI_bounds rectangle as the parameters
     * because they have not been changed since the
     * START_SELECT. This message is the equivalent of
     * calling GrInvalRect() with the same bounds.						*/
        @call self::MSG_VIS_BOUNDS_CHANGED(pself->VI_bounds.R_bottom,
                    pself->VI_bounds.R_right,
                    pself->VI_bounds.R_top,
                    pself->VI_bounds.R_left);
    /* Now set the current position to be centered
     * on the pointer image.					*/
        pself->TTP_vertPos = yPosition - (PIECE_HEIGHT/2);
        pself->TTP_horizPos = xPosition - (PIECE_WIDTH/2);
    /* Set the game piece object's bounds to
     * the new coordinates.					*/
        pself->VI_bounds.R_left = pself->TTP_horizPos;
        pself->VI_bounds.R_right = (pself->TTP_horizPos) + PIECE_WIDTH;
        pself->VI_bounds.R_top = pself->TTP_vertPos;
        pself->VI_bounds.R_bottom = (pself->TTP_vertPos) + PIECE_HEIGHT;
    } else {
    /* If the bounds are not on the game board, then reset
     * the current positions to be the original bounds. */
        pself->TTP_horizPos = pself->VI_bounds.R_left;
        pself->TTP_vertPos = pself->VI_bounds.R_top;
    }
    /* Now, the game piece must draw itself at its newly-
     * set bounds (will draw itself over its original
     * picture if the new bounds were invalid).						*/
    @call self::MSG_VIS_DRAW(0, gstate);
    /* Destroy the temporary gstate used for drawing. */
    GrDestroyState(gstate);
    /* Finally, clear the dragging flag to indicate that
     * no drag event is in progress. */
    pself->TTP_dragging = FALSE;
    }
    /* Release the mouse grab now that the move has
     * finished. Other objects in the view (other game
     * pieces, for example) may now receive pointer,
     * select, and drag events.						*/
    @call self::MSG_VIS_RELEASE_MOUSE();
}
This document is a single-page version of a a multi-page document, suitable for easy printing.


GenValue

GenValueClass allows the user to retrieve or set a numerical value for use in your application. The GenValue object offers a display for the current value and the means to either increase or decrease this value. The GenValue object also manages the range that this value may fall between and the action to send out when a value has been selected.

Before reading this chapter, you should be familiar with GenClass

1 GenValue Features
2 GenValue Instance Data
3 GenValue Basics
3.1 The Value
3.2 The Minimum and Maximum
3.3 The Increment
3.4 GenValue States
3.5 Display Formats
3.6 Sending an Action
4 Supplemental Usage
4.1 Adjusting the Value Indirectly
4.2 Status Messages
4.3 Retrieving Text
4.4 Using Value Ratios
4.5 Text Filters for the GenValue
4.6 Using Ranges in GenValues

GenValue: 1 GenValue Features

Depending upon the specific UI, the GenValue object can look like one of many things. It may be a spin gadget, a slider, a dial, or some other gadget. A GenValue typically allows the user to set a value within a specified range through keyboard or mouse input. For example, many GenValue objects in OSF/Motif are comprised of a text field showing the current value, an incrementor and a decrementor, and possibly a moniker. (Scroll bars are also GenValues, though they have very different appearance.)

In OSF/Motif, the user can enter the value by keyboard input into the text field or by clicking the mouse on the incrementor or decrementor.

The GenValue provides the following capabilities:

  • A user interface object to enter values into your application.
  • Application defined minimum and maximum values.
  • The increment value to increase or decrease values by.
  • Ability for the value to be represented in a variety of forms, including integer, decimal, or a unit of distance.
  • An action to apply any value to.

The user may change a value in a GenValue in one of two ways. It can be entered with the keyboard by typing directly into the display area, or it can be entered graphically by using the arrows, dials, or sliders that the specific UI provides. To enter a value by typing directly into the text field, first the user clicks on the area, bringing up a cursor. Then a value may be typed into the GenValue. To enter a value graphically, the user clicks on either the incrementor or decrementor, thus changing the value of the GenValue.

Once the value is changed, the value may become set immediately (if the object is operating in immediate mode), and the GenValue will perform its action (if any) every time the value is changed. As an alternative, the GenValue may be operating in delayed mode, and the value will not be set until a MSG_GEN_APPLY is received through an "Apply" or "OK" button. The mode (delayed or immediate) of the GenValue is controlled by the parent. Usually a GenValue will operate in immediate mode unless it is placed as a child of a GIT_PROPERTIES GenInteraction.

GenValue only allows the user to set a value within a specific range; this range consists of a minimum and maximum value that the GenValue may display. These values are set by the application and cannot be directly modified by the user, but they can be changed by your application. If the user enters a value above the maximum, the GenValue simply takes on its maximum possible value. If the user enters a value below the minimum, the GenValue takes on its least possible value.

In addition to the limits upon the range of values entered by the user, the GenValue also has a defined increment value. The increment value is the specific amount to increment or decrement the GenValue's value when using graphical input. The user cannot directly modify this value, but it can be changed by your application.

The GenValue object always displays a numerical value within its text field. The units of that numerical value may be set by your application. By default, any value within a GenValue is an integer, but that value could also be a decimal value, or even a unit of distance such as centimeters, points, or inches. The GenValue automatically provides whatever notation is required by the display units (decimal points, the words "pt" and "cm," or any other distance notation in the text field).

GenValue: 2 GenValue Instance Data

The GenValue contains instance fields that affect the current value, the display units of that value, and the action to take when any value operation has been completed. These instance fields are listed in GenValue Instance Data . Remember, in addition to these instance data variables, there are also the instance fields inherited from the GenValue's superclass, GenClass .

Code Display 8-1 GenValue Instance Data

/* The instance data fields for GenValue are shown below. Those that are records
 * of flags have their default flags shown with other possible flags listed below.
 * Other fields are shown with their default values. */
    @instance WWFixedAsDWord				GVLI_value = MakeWWFixed(0.0);
    @instance WWFixedAsDWord				GVLI_minimum = MakeWWFixed(0.0);
    @instance WWFixedAsDWord				GVLI_maximum = MakeWWFixed(32766);
    @instance WWFixedAsDWord				GVLI_increment = MakeWWFixed(1.0);
    @instance GenValueStateFlags				GVLI_stateFlags = 0;
/* GenValueStateFlags */
    typedef ByteFlags GenValueStateFlags;
    #define GVSF_INDETERMINATE				0x80
    #define GVSF_MODIFIED				0x40
    #define GVSF_OUT_OF_DATE				0x20
    @instance GenValueDisplayFormat 			GVLI_displayFormat = GVDF_INTEGER;
/* GenValueDisplayFormat */
    typedef ByteEnum GenValueDisplayFormat;
    #define GVDF_INTEGER 0
    #define GVDF_DECIMAL 1
    #define GVDF_POINTS 2
    #define GVDF_INCHES 3
    #define GVDF_CENTIMETERS 4
    #define GVDF_MILLIMETERS 5
    #define GVDF_PICAS 6
    #define GVDF_EUR_POINTS 7
    #define GVDF_CICEROS 8
    #define GVDF_POINTS_OR_MILLIMETERS 9
    #define GVDF_POINTS_OR_CENTIMETERS 10
    @instance optr				GVLI_destination;
    @instance Message				GVLI_applyMsg = 0;

GVLI_value is the current numerical value of the GenValue. By default, it is an integer constant defined by the application. Depending on the GVLI_ displayFormat , it can be interpreted as an integer, a decimal, or distance unit.

GVLI_maximum is the maximum possible value that the GenValue may display. This value may be positive or negative. The default value is 32766.

GVLI_minimum is the minimum possible value that the GenValue may display. This value may be positive or negative. The default value is zero.

GVLI_increment is the value to increment (or decrement) the GenValue when its value is changed by UI controls rather than direct text input. The default value is one.

GVLI_ stateFlags specifies the GenValueStateFlags for the GenValue to operate under. These flags affect whether the data within the GenValue is indeterminate (not necessarily true) or modified (changed since the last MSG_GEN_APPLY ).

GVLI_ displayFormat specifies the units of measurement ( GenValueDisplayFormat ) that the numerical value represents. By default, this is an integer, but it could also be a decimal or a unit of measurement (such as inches, points, or centimeters).

GVLI_applyMsg is the message to send out upon GenValue changes (i.e. whenever it receives MSG_GEN_APPLY). There is no default message.

GVLI_destination is the object or process to send the message upon GenValue changes. This can be an optr to an object or a valid TravelOption . See the MetaClass chapter. There is no default destination object.

Code Display 8-2 GenValue Optional Attribute Fields

    @vardata Message			ATTR_GEN_VALUE_STATUS_MSG;
    @vardata word			ATTR_GEN_VALUE_DECIMAL_PLACES;
    @vardata WWFixed			ATTR_GEN_VALUE_METRIC_INCREMENT;
    @vardata void			ATTR_GEN_VALUE_SET_MODIFIED_ON_REDUNDANT_SELECTION;

ATTR_GEN_VALUE_STATUS_MSG sets a status message for a GenValue. A status message allows your object to communicate with other objects when changes occur without sending out its apply message (GVLI_ applyMsg ).

ATTR_GEN_VALUE_DECIMAL_PLACES specifies the number of decimal places to display to the right of the decimal point if the GVLI_ displayFormat allows fractional quantities.

ATTR_GEN_VALUE_METRIC_INCREMENT allows you to specify a particular metric increment to use besides the default if the GVLI_ displayFormat is GVDF_POINTS_OR_MILLIMETERS or GVDF_INCHES_OR_CENTIMETERS.

ATTR_GEN_VALUE_SET_MODIFIED_ON_REDUNDANT_SELECTION specifies that the GenValue should be marked modified whether or not a change in the value has occurred. This will result in that value being applied whenever it receives a MSG_GEN_APPLY . (The default behavior for when no change in state occurs, no message will be sent out.)

Code Display 8-3 GenValue Hints

    @vardata void 			HINT_VALUE_INCREMENTABLE;
    @vardata void 			HINT_VALUE_NOT_INCREMENTABLE;
    @vardata void			HINT_VALUE_NAVIGATE_TO_NEXT_FIELD_ON_RETURN_PRESS;
    @vardata Message			HINT_VALUE_CUSTOM_RETURN_PRESS;
    @vardata WWFixedAsDWord 				HINT_VALUE_DISPLAYS_RANGE;
    @vardata GenValueIntervals				HINT_VALUE_DISPLAY_INTERVALS;
    @vardata void			HINT_VALUE_CONSTRAIN_TO_INTERVALS;
typedef struct {
    word	GVI_numMajorIntervals;
    word	GVI_numMinorIntervals;
} GenValueIntervals;
    @vardata void			HINT_VALUE_SHOW_MIN_AND_MAX;
    @vardata void			HINT_VALUE_IMMEDIATE_DRAG_NOTIFICATION;
    @vardata void			HINT_VALUE_DELAYED_DRAG_NOTIFICATION;
    @vardata void			HINT_VALUE_ORIENT_HORIZONTALLY;
    @vardata void			HINT_VALUE_ORIENT_VERTICALLY;
    @vardata void			HINT_VALUE_ANALOG_DISPLAY;
    @vardata void			HINT_VALUE_DIGITAL_DISPLAY;
    @vardata void			HINT_VALUE_NO_DIGITAL_DISPLAY;
    @vardata void			HINT_VALUE_NO_ANALOG_DISPLAY;
    @vardata void			HINT_VALUE_NOT_DIGITALLY_EDITABLE;
    @vardata void			HINT_VALUE_DO_NOT_MAKE_LARGER_ON_PEN_SYSTEMS;

HINT_VALUE_INCREMENTABLE and HINT_VALUE_NOT_INCREMENTABLE specify whether increment (and decrement) gadgets are appropriate for this GenValue. By default, GenValues are incrementable.

HINT_VALUE_CUSTOM_RETURN_PRESS stores a Message to send out if the user hits return within the textual portion of a GenValue. Normally, this action triggers the interaction default (sending the apply message).

HINT_VALUE_DISPLAYS_RANGE indicates that this GenValue displays a range of values between its minimum and maximum. In most cases, this suggests that the GenValue use sliders or some other UI gadget that is able to show the width of a range. It is up to the specific UI to support range values. If this hint is present, GVLI_ value refers to the starting point of the range of values, whose width is stored in the WWFixedAsDWord value here. The maximum GVLI_ value in this case would be GVLI_ maximum minus the range. If this hint is not present, the range "length" is presumed to be zero, even in gadgets that specify a range length by default.

HINT_VALUE_DISPLAY_INTERVALS indicates that intervals should be displayed along an object's range. This hint is used most often in analog (e.g. slider) type displays. If possible, hash marks will appear along the GenValue's display at the intervals specified by GenValueIntervals . This structure stores entries for both major intervals and minor intervals. In general, minor intervals will have smaller tick marks than major intervals.

typedef struct {
    word GVI_numMajorIntervals;
    word GVI_numMinorIntervals;
} GenValueIntervals;

If either GVI_ numMajorIntervals or GVI_ numMinorIntervals is zero, only one set of marks will appear.

HINT_VALUE_CONSTRAIN_TO_INTERVALS suggests that the value within GVLI_ value constrain itself to the GVI_ numMinorIntervals interval within HINT_VALUE_DISPLAY_INTERVALS.

HINT_VALUE_SHOW_MIN_AND_MAX instructs the GenValue to display its minimum and maximum values, if possible.

HINT_VALUE_IMMEDIATE_DRAG_NOTIFICATION instructs the GenValue to send out its status and/or apply messages constantly during a drag operation (each time the value changes). It is up to the specific UI to support this behavior.

HINT_VALUE_DELAYED_DRAG_NOTIFICATION instructs the GenValue to delay sending out status and/or apply message until the user releases the mouse after the drag operation. It is up to the specific UI to support this behavior.

HINT_VALUE_ORIENT_HORIZONTALLY instructs the specific UI to arrange an analog GenValue, if available, in the horizontal dimension.

HINT_VALUE_ORIENT_VERTICALLY instructs the specific UI to arrange an analog GenValue, if available, in the vertical dimension.

HINT_VALUE_ANALOG_DISPLAY indicates that the GenValue should be displayed in an analog fashion, if applicable. HINT_VALUE_DIGITAL_DISPLAY indicates that the GenValue should be displayed in a digital fashion (i.e. numerically). Similarly, HINT_VALUE_NO_DIGITAL_DISPLAY and HINT_VALUE_NO_ANALOG_DISPLAY indicate that a digital or analog display is not appropriate.

HINT_VALUE_NOT_DIGITALLY_EDITABLE instructs the UI to disallow editing of the text within a numerical GenValue. This hint is only applicable if some other UI means of changing the value is available. To remove a numeric display of values entirely, use HINT_VALUE_NO_DIGITAL_DISPLAY.

HINT_VALUE_DO_NOT_MAKE_LARGER_ON_PEN_SYSTEMS instructs the specific UI to avoid expanding a GenValue object to accept pen input, if possible. By default, text areas of GenValues grow larger under pen systems to allow ink strokes. This hint usually indicates that non-ink means of entering values is available.

GenValue: 3 GenValue Basics

The GenValue instance fields can be set to specific values in your Goc file, and they can also be modified by your application at run-time. This section describes how to set and modify these fields in your Goc file.

The Value

The Minimum and Maximum

The Increment

GenValue States

Display Formats

Sending an Action

GenValue: 3.1 GenValue Basics: The Value

GVLI_value, MSG_GEN_VALUE_SET_VALUE, MSG_GEN_VALUE_SET_INTEGER_VALUE, MSG_GEN_VALUE_GET_VALUE

The GVLI_ value instance field stores the current numerical value of the GenValue. You may set an initial value for the GenValue to appear with by setting this instance field in your Goc file. This value is a fixed point number; use MakeWWFixed to create this fixed point number in your instance data.

Any user changes on the value within the text field will not affect GVLI_ value until MSG_GEN_APPLY applies that value. If the GenValue operates in delayed mode, it will be marked modified in its GVLI_ stateFlags whenever a user change occurs; those changes will be applied when the GenValue receives a MSG_GEN_APPLY . In most cases, however, a GenValue operates in immediate mode, which will result in an immediate change in GVLI_ value .

Code Display 8-4 Setting an Initial Value

/* This GenValue will appear with the initial integer value of two. MakeWWFixed
 * creates a fixed point value. */
@object GenValueClass MyValue = {
    GI_visMoniker = "My Value";
    GVLI_value = MakeWWFixed(2.0);
}

GenValueClass provides several messages to change the value without user control. MSG_GEN_VALUE_SET_VALUE sets this numeric value to a passed fixed point value; this fixed point value may be any integer or decimal value. MSG_GEN_VALUE_SET_INTEGER_VALUE is a simpler message which sets GVLI_ value to an integer value passed. Neither of these messages mark the GenValue modified; you can do this with MSG_GEN_VALUE_SET_MODIFIED_STATE .

MSG_GEN_VALUE_SET_VALUE
void	MSG_GEN_VALUE_SET_VALUE(
        WWFixedAsDWord		value,
        Boolean		indeterminate);

This message sets the GVLI_value field of the GenValue to the passed value . This message clears a GenValue's modified state in its GVLI_ stateFlags . To mark the GenValue modified send MSG_GEN_VALUE_SET_MODIFIED_STATE after sending this message.

Source: Unrestricted. This message is also used internally when responding to user actions.

Destination: Any GenValue object.

Parameters: value The fixed point value to set GVLI_ value to. If you only need an integral value, consider using MSG_GEN_VALUE_SET_INTEGER_VALUE instead.

indeterminate
TRUE to mark the GenValue indeterminate, FALSE to mark it non indeterminate.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_INTEGER_VALUE
void	MSG_GEN_VALUE_SET_INTEGER_VALUE(
        word		value,
        Boolean		indeterminate);

This message sets the GVLI_ value to the passed integer (word-sized) value. GVLI_ value will then contain this value in its high (integral) word and zero in its low (fractional) word. The modified state of the GenValue will be cleared.

Source: Unrestricted. This message is also used internally when responding to user actions.

Destination: Any GenValue object.

Parameters: value The signed integer value to set GVLI_ value to.

indeterminate
TRUE to mark the GenValue indeterminate, FALSE to mark it non indeterminate.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VALUE_GET_VALUE
WWFixedAsDWord MSG_GEN_VALUE_GET_VALUE();

This message returns the value stored in the GenValue's GVLI_value instance field. This returned value will be a fixed point number.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The fixed point numerical value of the GVLI_ value instance field.

Interception: Generally not intercepted.

MSG_GEN_VALUE_GET_INTEGER_VALUE
@alias (MSG_GEN_VALUE_GET_VALUE)
word MSG_GEN_VALUE_GET_INTEGER_VALUE();

This message returns the integral portion of the GenValue's value.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The integral value of the GVLI_ value instance field.

Interception: Generally not intercepted.

GenValue: 3.2 GenValue Basics: The Minimum and Maximum

GVLI_minimum, GVLI_maximum, MSG_GEN_VALUE_GET_MAXIMUM, MSG_GEN_VALUE_SET_MAXIMUM, MSG_GEN_VALUE_GET_MINIMUM, MSG_GEN_VALUE_SET_MINIMUM

The maximum ( GVLI_maximum ) and minimum ( GVLI_minimum ) values of a GenValue constrain the value of GVLI_ value . The maximum is the greatest value that the GenValue's GVLI_ value can have and can be any fixed point number from -32767 up to 32767. The minimum is the least value that the GenValue's GVLI_ value can have (including negative numbers) and can be any fixed point number from 32767 down to -32767. The maximum value must always be greater than or equal to the minimum value; otherwise, an error will occur.

The maximum and minimum values of the GenValue also can be examined and modified by the application. This is helpful if you need to use the same GenValue for two functions with different ranges of values. You can use the GenValue for one function, change the bounds, and use it for another function.

MSG_GEN_VALUE_GET_MINIMUM and MSG_GEN_VALUE_GET_MAXIMUM return the values of GVLI_ minimum and GVLI_ maximum respectively. This value is in fixed point format.

MSG_GEN_VALUE_SET_MINIMUM and MSG_GEN_VALUE_SET_MAXIMUM set the values of GVLI_ minimum and GVLI_ maximum respectively. If this new minimum or maximum places the current GVLI_ value outside the valid value range, the value will be adjusted to fall within the current range.

MSG_GEN_VALUE_GET_MAXIMUM
WWFixedAsDWord MSG_GEN_VALUE_GET_MAXIMUM()

This message returns the fixed point value within the GVLI_maximum instance field of the GenValue.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The fixed point maximum value in GVLI_ maximum .

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_MAXIMUM
void	MSG_GEN_VALUE_SET_MAXIMUM(
        WWFixedAsDWord 		value);

This message sets the GVLI_maximum field of the GenValue to the passed fixed point value. If you set a new maximum that places the current value (in GVLI_ value ) above that maximum, GVLI_ value will be adjusted to this maximum.

Source: Unrestricted. This message is also used internally when the GenValue is being built.

Destination: Any GenValue object.

Parameters: value fixed point value to set GVLI_ maximum to.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VALUE_GET_MINIMUM
WWFixedAsDWord MSG_GEN_VALUE_GET_MINIMUM()

This message returns the fixed point value within the GVLI_minimum instance field of the GenValue.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The fixed point minimum value in GVLI_ minimum .

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_MINIMUM
void	MSG_GEN_VALUE_SET_MINIMUM(
        WWFixedAsDWord		value);

This message sets the GVLI_minimum instance field of the GenValue to the passed fixed point value. If you set a new minimum that places the current value (in GVLI_ value ) below that minimum, GVLI_ value will be adjusted to this minimum.

Source: Unrestricted. This message is also used internally when the GenValue is being built.

Destination: Any GenValue object.

Parameters: value fixed point value to set GVLI_ minimum to.

Return: Nothing.

Interception: Generally not intercepted.

GenValue: 3.3 GenValue Basics: The Increment

GVLI_increment, MSG_GEN_VALUE_GET_INCREMENT, MSG_GEN_VALUE_SET_INCREMENT, HINT_VALUE_INCREMENTABLE, HINT_VALUE_NOT_INCREMENTABLE

The GVLI_increment instance field stores the fixed point increment value for a GenValue. The increment value is the amount by which the current value (in GVLI_ value ) may increase or decrease when that value is changed by UI controls. The increment value can be any positive fixed point value between one and 65535.

If GVLI_ increment is greater than the possible range of the GenValue (the distance between minimum and maximum), then incrementing or decrementing the GenValue will toggle GVLI_ value between its maximum and minimum.

Code Display 8-5 Setting Minimum, Maximum, Increment Values

@object GenValueClass MyValue = {
    GI_visMoniker = "My Value";
    GVLI_value = MakeWWFixed(1.0);
    GVLI_minimum = MakeWWFixed(-100.0);
    GVLI_maximum= MakeWWFixed(100.0);
    GVLI_increment = MakeWWFixed(5.0);
}

You may change the fixed point value of this increment with MSG_GEN_VALUE_SET_INCREMENT . You may also return the current increment stored within GVLI_ increment with MSG_GEN_VALUE_GET_INCREMENT . You may wish to change a GenValue's increment one GenValue is being used for multiple roles and must change its increment value.

By default, all GenValues are incrementable. If you do not wish to have your GenValue provide UI controls for incrementing (or decrementing) its value, add HINT_VALUE_NOT_INCREMENTABLE in its instance data. HINT_VALUE_INCREMENTABLE provides the default behavior. (You may still increment or decrement the value manually with MSG_GEN_VALUE_INCREMENT or MSG_GEN_VALUE_DECREMENT .)

MSG_GEN_VALUE_SET_INCREMENT
void	MSG_GEN_VALUE_SET_INCREMENT (
        WWFixedAsDWord 		value);

This message sets the GVLI_increment field to the passed value .

Source: Unrestricted. This message is also used internally when the GenValue is being built.

Destination: Any GenValue object.

Parameters: value fixed point value to set GVLI_ increment to.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VALUE_GET_INCREMENT
WWFixedAsDWord MSG_GEN_VALUE_GET_INCREMENT();

This message returns the value of the GVLI_increment field of the GenValue.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The fixed point value of GVLI_ increment .

Interception: Generally not intercepted.

GenValue: 3.4 GenValue Basics: GenValue States

GVLI_stateFlags, MSG_GEN_VALUE_SET_INDETERMINATE_STATE, MSG_GEN_VALUE_IS_INDETERMINATE, MSG_GEN_VALUE_SET_MODIFIED_STATE, MSG_GEN_VALUE_IS_MODIFIED, ATTR_GEN_SET_MODIFIED_ON_REDUNDANT_SELECTION

GVLI_ stateFlags stores the current state of the GenValue. There are two GenValueStateFlags :

  • GVSF_INDETERMINATE
    This flag specifies that the value within the GenValue is indeterminate (may or may not be true). In most cases, you will not need to set this flag.
  • GVSF_MODIFIED
    This flag specifies that the value within the GenValue has changed since it last received a MSG_GEN_APPLY . The handler for MSG_GEN_APPLY checks whether this flag is set before sending out the GenValue's GVLI_ applyMsg .
  • GVSF_OUT_OF_DATE
    This flag specifies that the value within the GenValue is out of date with what the user has typed in. This is distinct from the GVSF_MODIFIED state; while the user is typing an a value ("123" for example) the typed value may be temporarily out of range, or incomplete. In this case, the value would be marked GVSF_OUT_OF_DATE and GVSF_MODIFIED. Notice that if the value were incremented or decremented, it would be marked GVSF_MODIFIED, but not GVSF_OUT_OF_DATE because the value is legal and presumable valid for operations. This flag is most useful when telling status messages whether a value should be used.

GenValues are normally marked as not modified anytime their state is set with an external message, marked modified whenever the user interacts with them, and marked not modified after receiving MSG_GEN_APPLY . MSG_GEN_VALUE_SET_MODIFIED_STATE allows you to control the modified state of a GenValue outside of these events.

You may set a GenValue's indeterminate or modified state with MSG_GEN_VALUE_SET_INDETERMINATE_STATE or MSG_GEN_VALUE_SET_MODIFIED_STATE , respectively.

To check whether a GenValue is indeterminate or modified, use MSG_GEN_VALUE_IS_INDETERMINATE or MSG_GEN_VALUE_IS_MODIFIED.

You may also mark a GenValue GVSF_OUT_OF_DATE by sending it MSG_GEN_VALUE_SET_OUT_OF_DATE.

MSG_GEN_VALUE_SET_INDETERMINATE_STATE
void	MSG_GEN_VALUE_SET_INDETERMINATE_STATE(
        Boolean		indeterminateState);

This message sets the indeterminate state for a GenValue. Pass TRUE to mark the GenValue indeterminate, FALSE to mark it not indeterminate. The GenValue will not be marked modified after this message.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: indeterminateState
TRUE to set the GVSF_INDETERMINATE flag in the GenValue's GVLI_ stateFlags ,
FALSE to clear the GVSF_INDETERMINATE flag.

Interception: Generally not intercepted.

MSG_GEN_VALUE_IS_INDETERMINATE
Boolean	MSG_GEN_VALUE_IS_INDETERMINATE();

This message checks whether a GenValue is indeterminate.

Source: Unrestricted.

Destination: Any GenValue object.

Return: TRUE if GenValue is indeterminate, FALSE if it is not.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_MODIFIED_STATE
void	MSG_GEN_VALUE_SET_MODIFIED_STATE(
        Boolean		modifiedState);

This message sets the modified state for a GenValue. Pass TRUE to mark the GenValue modified, FALSE to mark it not modified.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: modifiedState
TRUE to set the GVSF_MODIFIED flag in the GenValue's GVLI_ stateFlags ,
FALSE to clear the GVSF_MODIFIED flag.

Interception: Generally not intercepted.

MSG_GEN_VALUE_IS_MODIFIED
Boolean	MSG_GEN_VALUE_IS_MODIFIED();

This message checks whether a GenValue has been modified since the last MSG_GEN_APPLY .

Source: Unrestricted.

Destination: Any GenValue object.

Return: TRUE if GenValue is modified, FALSE if it is not.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_OUT_OF_DATE
void	MSG_GEN_VALUE_SET_OUT_OF_DATE();

This message sets a GenValue's GVSF_OUT_OF_DATE flag.

Source: Unrestricted.

Destination: Any GenValue object.

Interception: Generally not intercepted.

GenValue: 3.5 GenValue Basics: Display Formats

GVLI_displayFormat, MSG_GEN_VALUE_SET_DISPLAY_FORMAT, MSG_GEN_VALUE_GET_DISPLAY_FORMAT, ATTR_GEN_VALUE_METRIC_EQUIVALENT, ATTR_GEN_VALUE_DECIMAL_PLACES

In addition to displaying an integer numerical value, a GenValue may also display numerical values of several other formats. These formats may be any one of the GenValueDisplayFormat enumerations provided in GenValueClass . The allowed enumerations of type GenValueDisplayFormat are:

GVDF_INTEGER
GVDF_DECIMAL
GVDF_POINTS
GVDF_INCHES
GVDF_CENTIMETERS
GVDF_MILLIMETERS
GVDF_PICAS
GVDF_EUR_POINTS
GVDF_CICEROS
GVDF_POINTS_OR_MILLIMETERS
GVDF_INCHES_OR_CENTIMETERS

GVLI_ displayFormat controls how the values of the GenValue will be represented. For example, if the display format is in centimeters, the display will include the text "cm" after the numerical value; if the display includes a fractional part, a decimal point will be present. These display formats will also convert the values (which are stored as points) into the proper distance units for the textual display.

By default, the value's fractional portion will be displayed using 3 places to the right of the decimal point. You may alter this number of places with ATTR_GEN_VALUE_DECIMAL_PLACES. You may only choose a number of decimal places between zero to four (inclusive), because of the limited text space offered within a GenValue.

Your GenApplication object contains instance data which specifies whether the application is being run under US or metric units. This setting may affect the display of your units depending on the particular GenValueDisplayFormat .

GVDF_INTEGER displays the value as an integer (the high word of the fixed point value) and ignores any fractional part.
GVDF_DECIMAL displays the value as a decimal value.
GVDF_POINTS displays the value in points (1/72 of an inch) regardless of whether metric or US units are specified for the application.
GVDF_INCHES displays the value in inches regardless of whether metric or US units are specified for the application.
GVDF_CENTIMETERS displays the value in centimeters regardless of whether metric or US units are specified for the application.
GVDF_MILLIMETERS displays the value in millimeters regardless of whether metric or US units are specified for the application.
GVDF_PICAS displays the value as a distance in picas. One pica is equal to 12 US Points, or 1/6 of an inch.
GVDF_EUR_POINTS displays the value as a distance in European Points. One european point is about equal to 1.0656 US Points.
GVDF_CICEROS displays the value as a distance in Ciceros. One Cicero is equal to 12 European Points.
GVDF_POINTS_OR_MILLIMETERS and GVDF_INCHES_OR_CENTIMETERS are special cases. These display formats display the value in points (or inches) if US units are specified for the application; the value will be represented in millimeters (or centimeters) if metric units are instead specified.

Importantly, all distance units (inches, picas, centimeters, etc.) store their values as Points (1/72 inch). The system automatically converts these values (in Points) into the proper units of the GenValue's GVLI_ displayFormat when it displays the numerical value within the textual display.

Code Display 8-6 Setting a Distance Display Format

@object GenValueClass MyValue = {
    GI_visMoniker = "My Value";
    GVLI_displayFormat = GVDF_INCHES;
/* For an initial value of 1 inch, the equivalent value in Points is 72. */
    GVLI_value = MakeWWFixed(72.0);
/* For an initial value of 1/2 inch, the equivalent value in Points is 36. */
    GVLI_increment = MakeWWFixed(36.0);
}

For example, if your display format is GVDF_INCHES and you wish to set an initial value of 1 inch and an increment of 1/2 an inch, you should set these values to 72 (points which equals 1 inch) and 36 (points which equals one-half inch) respectively. This is necessary because the system expects these values to be in Points for other system operations. A conversion table is provided below.

Any increments for GVDF_POINTS_OR_MILLIMETERS or GVDF_INCHES_OR_CENTIMETERS are assumed to be in US units. If the application instead operates in metric, the increment will be automatically converted to a metric equivalent by the system; this metric equivalent will usually be rounded to a convenient numerical value. You may specify an ATTR_GEN_VALUE_METRIC_EQUIVALENT to override the default increment that the system calculates, however.

For example, assume GVDF_INCHES_OR_CENTIMETERS is selected and the value and increment are 72 (points which equals 1 inch). If the application is in US units, the display will specify inches and the value and increment will be 72 (1 inch); if the application is instead in metric, the display will specify centimeters and the increment will be 70.866 (2.5 cm). If instead, you choose an ATTR_GEN_VALUE_METRIC_INCREMENT of 52.692 (2 cm), that will be the increment used if the application is metric.

Code Display 8-7 ATTR_GEN_VALUE_METRIC_INCREMENT

/* If the application is US, the initial value will be 1 inch and the increment
 * will be 1 inch. If the application is metric, the initial value will be 2.54 cm
 * (1 inch or 72 points) but the increment will be 2.0 cm (56.692 points). If
 * ATTR_GEN_VALUE_METRIC_INCREMENT were not included, the system would have chosen
 * an increment of 2.5 cm (70.866 points) which is the closest "nice" value to the
 * original increment of 72 points (1 inch). */
@object GenValueClass MyValue = {
    GI_visMoniker = "My Value";
    GVLI_displayFormat = GVDF_INCHES_OR_CENTIMETERS;
    GVLI_value = MakeWWFixed(72.0);
    GVLI_increment = MakeWWFixed(72.0);
    ATTR_GEN_VALUE_METRIC_INCREMENT = MakeWWFixed(56.692);
}

To set a new display format, send MSG_GEN_VALUE_SET_DISPLAY_FORMAT . To retrieve the current display format, send the GenValue MSG_GEN_VALUE_GET_DISPLAY_FORMAT . Note that changing the display format will not change the numerical value of that display. For example, if the display format changes from decimal to integer, the GenValue will round the number down and display only the integer portion of the value.

MSG_GEN_VALUE_GET_DISPLAY_FORMAT
GenValueDisplayFormat MSG_GEN_VALUE_GET_DISPLAY_FORMAT();

This message returns the GVLI_ displayFormat field of the GenValue.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: GenValueDisplayFormat of the GenValue.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_DISPLAY_FORMAT
void	MSG_GEN_VALUE_SET_DISPLAY_FORMAT(
        GenValueDisplayFormat			format);

This message sets the GVLI_displayFormat of the GenValue to the given format. The current value in GVLI_ value is unaffected, but the display will be updated to reflect the new display format.

Source: Unrestricted. This message is also used internally when the GenValue is being built.

Destination: Any GenValue object.

Parameters: format GenValueDisplayFormat to set the GenValue to.

Return: Nothing.

Interception: Generally not intercepted.

GenValue: 3.6 GenValue Basics: Sending an Action

GVLI_applyMsg, GVLI_destination, HINT_VALUE_NAVIGATE_TO_NEXT_FIELD_ON_RETURN_PRESS, HINT_VALUE_CUSTOM_RETURN_PRESS, MSG_GEN_VALUE_GET_APPLY_MSG, MSG_GEN_VALUE_SET_APPLY_MSG, MSG_GEN_VALUE_GET_DESTINATION, MSG_GEN_VALUE_SET_DESTINATION

GVLI_ applyMsg sets the message for the GenValue to send out whenever it has been modified and needs to apply its changes. Whenever a GenValue receives MSG_GEN_APPLY, i t will check whether its GVSF_MODIFIED flag has been set; if it has, it will send out its apply message. If a GenValue is operating in immediate mode, these actions will happen immediately, resulting in an immediate action.

GVLI_ destination specifies the destination object (or process) to send the GVLI_ applyMsg to. (This may also be a TravelOption , such as TO_APP_TARGET.)

Code Display 8-8 Sending an Apply Message

@object GenValueClass MyValue = {
    GI_visMoniker = "My Value";
    GVLI_value = MakeWWFixed(1.0);
    GVLI_applyMsg = MSG_MY_VALUE_DOUBLE_VALUE;
    GVLI_destination = process;
}
/* Retrieve the current value. This value will be a fixed point dword. */
@method MyValueProcessClass, MSG_MY_VALUE_DOUBLE_VALUE {
    WWFixedAsDWord	curValue;
    curValue = @call MyValue::MSG_GEN_VALUE_GET_VALUE();
    curValue = curValue*2;
    @call MyValue::MSG_GEN_VALUE_SET_VALUE(curValue, 0);
}

A GenValue's changes are typically applied when the user hits the Return key and the GenValue has the focus. You can change this behavior, though, with the following hints: HINT_VALUE_CUSTOM_RETURN_PRESS allows a textually-oriented GenValue to send the specified message to the destination object when the Return key is pressed. HINT_VALUE_NAVIGATE_TO_NEXT_FIELD_ON_RETURN_PRESS instructs the GenValue to navigate (via the UI) to the next textually-activated object (as the tab key works in many situations).

To change a GenValue's apply message or destination, send it MSG_GEN_VALUE_SET_APPLY_MSG or MSG_GEN_VALUE_SET_DESTINATION , respectively. Use MSG_GEN_VALUE_GET_APPLY_MSG or MSG_GEN_VALUE_GET_DESTINATION to return the current apply message or destination.

The apply message should be defined on the prototype GEN_VALUE_APPLY_MSG , whose values are shown below.

GEN_VALUE_APPLY_MSG
void	GEN_VALUE_APPLY_MSG(
        WWFixedAsDWord		value,
        word		stateFlags);

This prototype defines the message sent out when the GenValue is "applied." The output of the GenValue should handle a message with these parameters.

Source: GenValue, when "applied."

Destination: The GenValue's output ( GVLI_destination ) object.

Parameters: value The current value of the GenValue.

stateFlags
The GenValueStateFlags stored in GVLI_stateFlags .

Return: Nothing.

Interception: The destination object should handle the apply message with this format.

MSG_GEN_VALUE_GET_APPLY_MSG
Message	MSG_GEN_VALUE_GET_APPLY_MSG();

This message returns the GenValue's GVLI_ applyMsg .

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The apply message of the GenValue.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_APPLY_MSG
void	MSG_GEN_VALUE_SET_APPLY_MSG(
        Message		message);

This message sets the apply message (in GVLI_ applyMsg ) for a GenValue.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: message The apply message to set for the GenValue.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VALUE_GET_DESTINATION
optr	MSG_GEN_VALUE_GET_DESTINATION();

This message returns the current destination object (or process) that the GenValue sends its apply messages to.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The destination optr (GVLI_ destination ) of the GenValue.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_DESTINATION
void	MSG_GEN_VALUE_SET_DESTINATION(
        optr	dest);

This message sets the GVLI_destination field of the range to the passed optr. The object can be a pointer to a specific object in the system (i.e. the GenProcess object) or can be a pointer to a generic location in the system (i.e. a TravelOption ).

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: dest The optr of the new destination object.

Return: Nothing.

Interception: Generally not intercepted.

GenValue: 4 Supplemental Usage

Besides altering instance data, there are several other messages and mechanisms for your use in GenValueClass .

Adjusting the Value Indirectly

Status Messages

Retrieving Text

Using Value Ratios

Text Filters for the GenValue

Using Ranges in GenValues

GenValue: 4.1 Supplemental Usage: Adjusting the Value Indirectly

MSG_GEN_VALUE_INCREMENT, MSG_GEN_VALUE_DECREMENT, MSG_GEN_VALUE_SET_VALUE_TO_MINIMUM, MSG_GEN_VALUE_SET_VALUE_TO_MAXIMUM

To increase the value of GVLI_ value by the increment in GVLI_ increment , send the GenValue MSG_GEN_VALUE_INCREMENT . To decrease the value of GVLI_ value by the increment in GVLI_ increment , send the GenValue MSG_GEN_VALUE_DECREMENT . These messages are equivalent to clicking on the UI controls provided to increment or decrement the GenValue (usually up and down arrows). Both of these messages clear the indeterminate state of the object and do not change its modified state.

To set the value of GVLI_ value to the minimum in GVLI_ minimum , send the GenValue MSG_GEN_VALUE_SET_VALUE_TO_MINIMUM . To set the value of GVLI_ value to the maximum in GVLI_ maximum , send the GenValue MSG_GEN_VALUE_SET_VALUE_TO_MAXIMUM . Both of these messages clear the indeterminate state of the GenValue and do not change its modified state.

Note that HINT_VALUE_NOT_INCREMENTABLE has no effect on these messages; that hint only removes any user controls for incrementing or decrementing a value.

MSG_GEN_VALUE_INCREMENT
void	MSG_GEN_VALUE_INCREMENT();

This message increases the value of GVLI_ value within the GenValue by the increment in GVLI_ increment . This message will clear the indeterminate flag of the GenValue but will not affect its modified flag.

Source: Unrestricted. This message is also used internally when responding to user actions.

Destination: Any GenValue object.

Parameters: None.

Return: Nothing. GVLI_ value will be incremented (or set to the maximum if increasing the value would push it over the maximum).

Interception: Generally not intercepted.

MSG_GEN_VALUE_DECREMENT
void	MSG_GEN_VALUE_DECREMENT();

This message decreases the value of GVLI_ value within the GenValue by the increment in GVLI_ increment . This message will clear the indeterminate flag of the GenValue but will not affect its modified flag.

Source: Unrestricted. This message is also used internally when responding to user actions.

Destination: Any GenValue object.

Parameters: None.

Return: Nothing. GVLI_ value will be decremented (or set to the minimum if decreasing the value would push it below the minimum).

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_VALUE_TO_MINIMUM
void	MSG_GEN_VALUE_SET_VALUE_TO_MINIMUM();

This message sets the value of GVLI_ value to the minimum value in GVLI_ minimum . This message will clear the indeterminate flag of the GenValue but will not affect its modified flag.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: Nothing. GVLI_value will be set to GVLI_ minimum .

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_VALUE_TO_MAXIMUM
void	MSG_GEN_VALUE_SET_VALUE_TO_MAXIMUM();

This message sets the value of GVLI_ value to the maximum value in GVLI_ maximum . This message will clear the indeterminate flag of the GenValue but will not affect its modified flag.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: Nothing. GVLI_value will be set to GVLI_ maximum .

Interception: Generally not intercepted.

GenValue: 4.2 Supplemental Usage: Status Messages

ATTR_GEN_VALUE_STATUS_MSG, MSG_GEN_VALUE_SEND_STATUS_MSG

If your GenValue is operating in delayed mode, there usually occur times when your GenValue's state may not reflect the most recent changes. In most cases this is fine, but in some cases you may wish other UI objects to be notified of a change in your GenValue's state without sending out an apply message. This can be done with a status message.

A status message allows your GenValue to send out a message whenever the user interacts with the GenValue, regardless of whether that change will be immediately applied. This is most useful for cases in which two UI objects are representing information that depends on each other. The status message allows one UI object to inform its friend that its state has changed, and that the friend should change its state to reflect the new information.

To give a GenValue a status message, include ATTR_GEN_VALUE_STATUS_MSG in the object's declaration. Use the prototype GEN_VALUE_STATUS_MSG to define your status message. This prototype ensures that the status message passes the correct parameters (the current value and state flags of the GenValue).

Any user changes that do not result in the sending of the object's apply message will result in the sending of the object's status message. For an object in immediate mode, this ATTR will have no effect. You may also manually send an object's status message by sending the GenValue MSG_GEN_VALUE_SEND_STATUS_MSG .

GEN_VALUE_STATUS_MSG
void	GEN_VALUE_STATUS_MSG(
        WWFixedAsDWord		value,
        word		stateFlags);

This prototype should be used to define the status message of the GenValue.

Source: The GenValue, when its status message is sent.

Destination: The GenValue's destination object ( GVLI_destination ).

Parameters: value The current user value of the GenValue.

stateFlags
The current value of GVLI_stateFlags .

Return: Nothing.

Interception: Must be handled by the output object if the status message is to have any effect.

MSG_GEN_VALUE_SEND_STATUS_MSG
void	MSG_GEN_VALUE_SEND_STATUS_MSG(
        Boolean		modifiedState);

This message sends the status message stored in the object's ATTR_GEN_VALUE_STATUS_MSG instance field. You should pass this message the modified State you wish to send. This modified state may or not reflect the GVSF_MODIFIED flag in the GenValue's GVLI_ stateFlags .

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: modifiedState TRUE if this message should pass the modified bit (GVSF_MODIFIED) set, FALSE if it should pass GVSF_MODIFIED cleared.

Return: Nothing.

Interception: Generally not intercepted.

GenValue: 4.3 Supplemental Usage: Retrieving Text

MSG_GEN_VALUE_GET_VALUE_TEXT, MSG_GEN_VALUE_SET_VALUE_FROM_TEXT, MSG_GEN_VALUE_GET_MAX_TEXT_LEN, MSG_GEN_VALUE_SET_MAX_TEXT_LEN, MSG_GEN_VALUE_SELECT_TEXT

The GenValue's numeric values are displayed within a special text field. In addition to retrieving the numeric value of the GenValue, you may also retrieve the textual representation of that number with MSG_GEN_VALUE_GET_VALUE_TEXT . Similarly, you can set the value of the GenValue from a textual representation with MSG_GEN_VALUE_SET_VALUE_FROM_TEXT .

You must pass these messages a GenValueType which specifies the instance data field you are wishing to set or get. The GenValueType types are:

typedef enum /* word */ {
    GVT_VALUE,			/* GVLI_value */
    GVT_MINIMUM,			/* GVLI_minimum */
    GVT_MAXIMUM,			/* GVLI_maximum */
    GVT_INCREMENT		,	/* GVLI_increment */
    GVT_LONG,			/* Longest value we can
                 * create. */
    GVT_RANGE_LENGTH,			/* End of the displayed
                 * range, if applicable. */
    GVT_RANGE_END,			/* Last value in the range,
                 * if applicable. */
    GVT_VALUE_AS_RATIO_OF_AVAILABLE_RANGE
                /* The current value,
                 * relative to minimum. */
} GenValueType;

Some special subclasses of the GenValue will want to calculate how many characters it will allow the user to type into its text field. By default, the number of characters is determined by the maximum and minimum values, but subclasses can handle MSG_GEN_VALUE_GET_MAX_TEXT_LEN to set it specifically. This message is sent by the range to itself when deciding how big the text field should be. The maximum text length allowed is thirty characters long.

If you wish to select a GenValue's text, send the GenValue MSG_GEN_VALUE_SELECT_TEXT. The specific UI has final say on whether it allows a GenValue to exhibit selectable text.

MSG_GEN_VALUE_GET_VALUE_TEXT
void	MSG_GEN_VALUE_GET_VALUE_TEXT(
        char		*buffer,
        GenValueType		valueType);

This message retrieves a fixed point value (either GVLI_value , GVLI_ minimum , GVLI_ maximum , or GVLI_ increment ) from the GenValue and stores its textual representation (in a null-terminated text string) in the passed buffer. This message is not affected by the indeterminate state of the GenValue.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: buffer The pointer to the buffer to store the null-terminated text string. This buffer must be at least GEN_VALUE_MAX_TEXT_LEN bytes long.

valueType
The GenValueType , specifying the instance data to retrieve and convert into text.

Return: Nothing. The buffer will be filled in with text.

Interception: Can be intercepted by a subclass of GenValue to allow custom text formats to be displayed on the screen. In this case you would also subclass MSG_GEN_VALUE_SET_VALUE_FROM_TEXT .

MSG_GEN_VALUE_SET_VALUE_FROM_TEXT
void	MSG_GEN_VALUE_SET_VALUE_FROM_TEXT(
        char		*text,
        GenValueType		valueType);

This message sets a fixed point value (either GVLI_value , GVLI_ minimum , GVLI_ maximum , or GVLI_ increment ) of the GenValue from a textual representation (in a null-terminated text string) in the passed buffer. This message clears the indeterminate state of the GenValue but does not change its modified state.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: text The pointer to a null-terminated text string to set the GenValue's text to.

valueType
The GenValueType , specifying the instance data to convert into a fixed point value from the passed text.

Return: Nothing. The instance field will be updated.

Interception: Can be intercepted by a subclass of GenValue to allow custom text formats to be displayed on the screen. In this case, you would also subclass MSG_GEN_VALUE_GET_VALUE_TEXT .

MSG_GEN_VALUE_GET_MAX_TEXT_LEN
byte	MSG_GEN_VALUE_GET_MAX_TEXT_LEN();

This message retrieves the maximum number of characters allowed to be typed into a textual GenValue. By default, this number is calculated from the minimums and maximums specified. Subclasses of GenValue may wish to intercept this message to allow different text lengths (such as values represented with floating point or enumerated type instance data).

Source: The GenValue object sends this to itself when it needs to figure out its maximum text length.

Destination: Itself.

Return: Maximum number of allowable characters

Interception: May be intercepted to allow different text lengths.

MSG_GEN_VALUE_SELECT_TEXT
void	MSG_GEN_VALUE_SELECT_TEXT();

This message selects a GenValue's text, if allowed by the specific UI.

Source: Unrestricted.

Destination: Any GenValue object.

Interception: Generally not intercepted.

GenValue: 4.4 Supplemental Usage: Using Value Ratios

MSG_GEN_VALUE_GET_VALUE_RATIO, MSG_GEN_VALUE_SET_VALUE_FROM_RATIO

Instead of setting or getting a specific value, you may want in some cases to set or get a value as a ratio; this ratio is determined as the percentage of the difference between the minimum and maximum. For example, if the minimum of a GenValue is 10 and the maximum is 100, the 50% ratio would be 55 (halfway between 10 and 100).

To retrieve the value of a GenValue as a ratio, send it MSG_GEN_VALUE_GET_VALUE_RATIO . This message will return the ratio as a dword value. You must also pass this message the GenValueType telling it which instance data you wish to retrieve.

To set a value within a GenValue as a ratio, send it MSG_GEN_VALUE_SET_VALUE_RATIO , passing it the ratio and the GenValueType (instance data field) to set. The correct value will be computed and set within your GenValue's instance data.

MSG_GEN_VALUE_GET_VALUE_RATIO
WWFixedAsDWord MSG_GEN_VALUE_GET_VALUE_RATIO(
        GenValueType		valueType);

This message gets the value of a GenValue (GVLI_ value ) as a ratio of its distance between the minimum value and the maximum value. It returns this ratio as a dword (0000.0000h meaning 0%, ffff.ffffh meaning 100%).

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: valueType GenValueType of the instance data field to get the value ratio for.

Return: The ratio as a dword.

Interception: You may intercept subclasses of GenValueClass to allow custom text formats to be displayed on the screen. Someone subclassing this message for this reason would also want to subclass MSG_GEN_VALUE_SET_VALUE_FROM_TEXT to correctly parse text to yield a new value.

MSG_GEN_VALUE_SET_VALUE_RATIO
void	MSG_GEN_VALUE_SET_VALUE_RATIO(
        WWFixed		ratio,
        GenValueType		valueType);

This message sets the value (GVLI_ value ) of a GenValue as a ratio of the distance between its minimum and the maximum values. This ratio should be in the form of a dword (0000.0000h meaning 0%, ffff.ffffh meaning 100%).

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: ratio The dword ratio value.

valueType
The GenValueType of the instance data to set as a ratio.

Return: Nothing.

Interception: Generally not intercepted.

GenValue: 4.5 Supplemental Usage: Text Filters for the GenValue

MSG_GEN_VALUE_GET_TEXT_FILTER

One of the GenValue's components is a text field to enter values into. When this visual component is being built, the GenValue will send MSG_GEN_VALUE_GET_TEXT_FILTER to itself to set up a text filter. A text filter allows a textual object to accept or reject certain characters on a number of bases. The default VisTextFilter provides numeric-only filtering with no spaces and no tabs on GVDF_INTEGER or GVDF_DECIMAL display formats; no tabs on distance unit display formats.

You may set up your own filter by intercepting this message and returning a VisTextFilter of your own choosing.

MSG_GEN_VALUE_GET_TEXT_FILTER
VisTextFilter MSG_GEN_VALUE_GET_TEXT_FILTER();

This message retrieves the text filtering in use on a GenValue object. By default, GenValues use numeric-only filtering with no spaces and no tabs for numbers and a filtering of no tabs for distance units.

Source: Unrestricted. This message is normally sent by a GenValue to itself when building its textual component.

Destination: Any GenValue object.

Parameters: None.

Return: VisTextFilter in use by the textual portion of the GenValue.

Interception: Usually, you will want to intercept this message if you subclass GenValueClass and provide your own custom filtering.

GenValue: 4.6 Supplemental Usage: Using Ranges in GenValues

HINT_VALUE_DISPLAYS_RANGE, MSG_GEN_VALUE_SET_RANGE_LENGTH, MSG_GEN_VALUE_GET_RANGE_LENGTH, MSG_GEN_VALUE_ADD_RANGE_LENGTH, MSG_GEN_VALUE_SUBTRACT_RANGE_LENGTH

Special GenValue objects may actually display ranges rather than individual values within a range--for example, the GEOS scrollbars in OSF/Motif display the percentage of the document visible in the view.

For your GenValue to display a range, use HINT_VALUE_DISPLAYS_RANGE . If this hint is not present, the GenValue is assumed to have a range size of zero. This hint takes an argument of WWFixedAsDWord to indicate the size of the range.

To get the range length, use MSG_GEN_VALUE_GET_RANGE_LENGTH . To set the range, use MSG_GEN_VALUE_SET_RANGE_LENGTH . Two other messages, MSG_GEN_VALUE_ADD_RANGE_LENGTH and MSG_GEN_VALUE_SUBTRACT_RANGE_LENGTH , add or subtract the value of the GenValue by the range length.

MSG_GEN_VALUE_GET_RANGE_LENGTH
WWFixedAsDWord MSG_GEN_VALUE_GET_RANGE_LENGTH();

This message returns the value stored in HINT_VALUE_DISPLAYS_RANGE .

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: None.

Return: The value stored in HINT_VALUE_DISPLAYS_RANGE . If this hint is not set, the return value will be zero.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SET_RANGE_LENGTH
void	MSG_GEN_VALUE_SET_RANGE_LENGTH(
        WWFixedAsDWord value);

This message has the effect of adding or changing the hint HINT_VALUE_DISPLAYS_RANGE for the GenValue. Setting a value of zero will cause the GenValue to act as if it did not have this hint.

Source: Unrestricted.

Destination: Any GenValue object.

Parameters: value The new range length.

Return: Nothing.

Interception: Generally not intercepted.

MSG_GEN_VALUE_ADD_RANGE_LENGTH
void	MSG_GEN_VALUE_ADD_RANGE_LENGTH();

This message adds the range length to the current value of the GenValue object. It has the effect of incrementing the GVLI_value field by the range length.

Source: Unrestricted.

Destination: Any GenValue object.

Interception: Generally not intercepted.

MSG_GEN_VALUE_SUBTRACT_RANGE_LENGTH
void	MSG_GEN_VALUE_SUBTRACT_RANGE_LENGTH();

This message subtracts the range length from the current value of the GenValue object. It has the effect of decrementing the GVLI_value field by the range length.

Source: Unrestricted.

Destination: Any GenValue object.

Interception: Generally not intercepted.

This document is a single-page version of a a multi-page document, suitable for easy printing.


VisClass

The visible classes-- VisClass , VisCompClass , VisContentClass , and VisTextClass --can be extremely useful to application developers. They provide a flexibility of display and use that is unavailable with the generic UI objects. Among other features, visible objects provide the hooks for and basic functionality of custom drawing, custom input handling, and custom geometry management.

VisClass is the parent class of all the visible classes and all visible UI objects. VisClass objects, by themselves, do nothing; VisClass must be subclassed and the objects must be placed in a visible object tree as children of a VisCompClass object. Together, VisClass and VisCompClass provide a large amount of the functionality required for creating a hierarchy of non-overlapping objects.

Many applications will use both VisClass and VisCompClass as well as VisContentClass . Before reading this chapter, however, you should be familiar with the concepts of the generic UI and with basic GEOS applications. You should have created a simple application to get familiar with the GEOS system and Goc, and you should be relatively familiar with the GenView.

You should also become familiar with both VisContentClass and VisCompClass ; both of these classes and their relationship to VisClass

1 Introduction to VisClass
2 The Visible Class Tree
3 VisClass Instance Data
3.1 VI_bounds
3.2 VI_typeFlags
3.3 VI_attrs
3.4 VI_optFlags
3.5 VI_geoAttrs
3.6 VI_specAttrs
3.7 VI_link
4 Using VisClass
4.1 Basic VisClass Rules
4.2 Drawing to the Screen
4.3 Positioning Visible Objects
4.4 Handling Input
5 Working with Visible Object Trees
5.1 Creating and Destroying
5.2 Adding and Removing
5.3 Getting Visible Tree Information
5.4 Sending Messages Through the Tree
5.5 Visible Object Window Operations
6 Visible Layers and 32-Bit Graphics
6.1 Using Visible Document Layers
6.2 Using 16-Bit Drawing Commands
6.3 The 16-Bit Limit on Visual Bounds
6.4 Handling MSG_VIS_DRAW
6.5 Managing 32-Bit Geometry
6.6 Handling Mouse Events
6.7 Setting Up the Objects
7 VisClass Error Checking
8 Creating Specific UIs
9 Basic Summary

VisClass: 1 Introduction to VisClass

VisClass is one of the three visible classes you'll have to have a working knowledge of in order to use visible objects. Both of the others, VisCompClass and VisContentClass , are its subclasses, and both are important in visible object trees. However, VisClass provides the heart of the visible world.

VisClass objects do nothing useful by themselves. When you subclass VisClass and add your own methods, however, the real power of the visible classes can be used without the programmer having to worry about the nuts and bolts of most UI issues. These classes inherently provide the following features:

Knowledge of Visible Bounds
Every visible object has an instance data field that defines the object's bounds in the GEOS graphic coordinate space. These bounds are used for many purposes, including drawing and input handling. See Positioning Visible Objects .
Comprehensive Visual Updating
When a visible object is included in an object tree displayed by a GenView object, the system automatically tells the object when it must redraw itself. For example, when the view scrolls or when part of the window becomes exposed, the system will send a drawing message through the visible tree; the objects then draw themselves at the proper location in the document space, and the view window is updated. See Drawing to the Screen .
Geometry Management
When visible objects are arranged as children of a VisComp or a VisContent object, they can be set up to update their size and position appropriate to the current window and view context. The VisComp and VisContent classes provide extensive and sophisticated child management functions, though custom management is also allowed and is often used. See Positioning Visible Objects , especially the messages MSG_VIS_RECALC_SIZE and MSG_VIS_POSITION_BRANCH in Geometry Management .
Object Tree Manipulation
VisClass objects can not have children, but they can be children of VisCompClass and VisContentClass objects. All of these classes support extensive tree operations including passing messages up the tree, passing messages down the tree, adding branches, removing branches, and moving branches. See Working with Visible Object Trees .
Mouse Event Handling
When an input event occurs over the GenView window in which the visible tree is displayed, that event is immediately passed on to the VisContent object. The event then travels down the visible object tree (automatically) until it is handled by some visible object. Most often, the event is handled by the leaf object with its visible bounds directly under the mouse pointer. Individual Vis objects can also grab the mouse so that they receive input events that occur even outside their bounds. See both Handling Input and the Input chapter.

Keyboard Event Handling
 A visible object can grab the keyboard input stream and receive keyboard events as they occur. Typically, certain keystrokes will be intercepted by the specific UI even if a visible object has the keyboard grab; this ensures the object won't usurp functions expected by the user from his specific UI. (The object can, however, override keystroke interception, though this is strongly discouraged.) See the Input chapter.

Interaction with Input Hierarchies
 VisClass objects have an inherent knowledge of the input hierarchies. Many visible objects may need to interact with the Focus and Target input hierarchies especially, and they can also interact with the Model hierarchy as well. See the Input chapter.

Use of VisMonikers
 Typically, visible objects will draw themselves in their entirety and will not need visual monikers. Monikers are typically labels attached to objects that get displayed either on or near the object. Visual monikers are most frequently used with generic UI objects, but they can be set up and used with visible objects as well. See Using Visual Monikers .

Standard MetaClass Functions
 Because VisClass is a subclass of MetaClass
, it inherits all the basic, standard object functions of that class (e.g., state saving, instantiation and initialization, detaching and destruction, etc.). See the MetaClass chapter.

Many programmers new to object-oriented programming may think that visible objects are used only in certain circumstances. On the contrary, visible objects may be used for nearly any purpose and to provide nearly any application-specific graphical or user interface functions. When used properly, visible objects can relieve you of hours of writing specialized code.

Programmers new to OOP may need to practice with several small applications in order to gain experience using visible objects. You can also study the sample applications, many of which use visible objects extensively.

Visible objects are most useful when you have well-defined items that must appear on the screen or interact with the user. Pieces in a board game provide a good example: Each piece has its own visual representation, and each piece has a special set of behaviors that depend on user actions and game context. Therefore, it makes sense to have each piece be an object of VisClass .

Another, less immediately obvious, example might be a program that represents maps on the screen. The map itself could be stored as a visible composite object ( VisCompClass ), and each of the major subdivisions on the map could be a visible object. For example, in a map of the United States, the country would be a VisComp object, and each state could be a Vis object. Taken one step further, each state could be a composite object, and each county in the state a visible child of the state. Another step turns the counties into composite objects and each town a visible object. This arrangement lends itself to a tree structure, which is how visible objects are stored.

Another example is a spreadsheet application. The spreadsheet itself could be one composite object containing a number of row objects. Each row object is a composite that holds a number of cell objects. Each cell is a visible object with the functions of the cell built into it. The row and spreadsheet composites can use the geometry manager to arrange the cells properly, thereby making the programmer free of ever having to worry where the cells are in the document. The visible object tree will automatically pass input events to the cell under the mouse pointer. Entire rows can be created, destroyed, drawn, erased, or moved with a few simple commands by the programmer.

VisClass: 2 The Visible Class Tree

As stated earlier, the visible class tree consists of four classes.

VisClass
This is the parent class of the visible tree. It contains the basic functionality common to every visible object and is the subject of this chapter.
VisCompClass
A subclass of VisClass , this class has the added ability to have and arrange children in the visible tree. It has sophisticated geometry management functions built in and can be used as an organizational object.
VisContentClass
A subclass of VisCompClass , this class is used only as the content of a GenView. The VisContent has default handlers for many messages sent out by the view, and it interacts with the view window to display the visible object tree. Every visible object tree, to be displayed in a GenView, must have a VisContent as its root object.
VisTextClass
This class, not discussed in this chapter, is a visible version of the GenTextClass . It provides a nearly full-featured word processing object that can be put in the visible tree.

The first three of these classes are used extensively by many applications.

VisClass: 3 VisClass Instance Data

VisClass has seven different instance data fields as shown in VisClass Instance Data . Each of these fields is discussed in detail in the following sections, and each may be set or reset during execution by sending the visible object certain messages.

Several of the instance fields will never be used by the application programmer. Most likely, you will work directly with the VI_bounds field and perhaps the VI_attrs or VI_geoAttrs fields. It's unlikely you will need to use the others unless you're doing something quite unusual.

Code Display 15-1 VisClass Instance Data

/* This is the definition of the VisClass instance data fields with some comments
 * as to their use. */
    /* VI_bounds
     * VI_bounds is a Rectangle structure defining the outermost boundaries of
     * the visible object. */
    @instance Rectangle				 VI_bounds = {0, 0, 0, 0};
    /* VI_typeFlags
     * VI_typeFlags is a record containing eight flags that determine the
     * type of the visible object. None are set by default. */
    @instance  VisTypeFlags				 VI_typeFlags = 0;
    /* Possible flags:
     * VTF_IS_COMPOSITE				VTF_IS_WINDOW
     * VTF_IS_PORTAL				VTF_IS_WIN_GROUP
     * VTF_IS_CONTENT				VTF_IS_INPUT_NODE
     * VTF_IS_GEN				VTF_CHILDREN_OUTSIDE_PORTAL_WIN */
    /* VI_attrs
     * VI_attrs is a record that contains eight attribute flags for the
     * object. Those set by default are shown. */
    @instance  VisAttrs				 VI_attrs = (VA_MANAGED | VA_DRAWABLE |
                       VA_DETECTABLE | VA_FULLY_ENABLED);
    /* Possible flags:
     * VA_VISIBLE				VA_FULLY_ENABLED
     * VA_MANAGED				VA_DRAWABLE
     * VA_DETECTABLE				VA_BRANCH_NOT_MINIMIZABLE
     * VA_OLD_BOUNDS_SAVED				VA_REALIZED */
    /* VI_optFlags
     * VI_optFlags is a record containing eight window update flags.
     * The flags set by default are shown. */
    @instance  VisOptFlags				 VI_optFlags = (VOF_GEOMETRY_INVALID |
                    VOF_GEO_UPDATE_PATH | VOF_WINDOW_INVALID |
                    VOF_WINDOW_UPDATE_PATH |
                    VOF_IMAGE_INVALID |
                    VOF_IMAGE_UPDATE_PATH);
    /* Possible flags:
     * VOF_GEOMETRY_INVALID				VOF_GEO_UPDATE_PATH
     * VOF_IMAGE_INVALID				VOF_IMAGE_UPDATE_PATH
     * VOF_WINDOW_INVALID				VOF_WINDOW_UPDATE_PATH
     * VOF_UPDATE_PENDING				VOF_EC_UPDATING */
    /* VI_geoAttrs
     * VI_geoAttrs is a record of eight geometry management flags. */
    @instance  VisGeoAttrs				 VI_geoAttrs = 0;
    /* Possible flags:
     * VGA_GEOMETRY_CALCULATED				VGA_NO_SIZE_HINTS
     * VGA_NOTIFY_GEOMETRY_VALID				VGA_DONT_CENTER
     * VGA_USE_VIS_SET_POSITION				VGA_USE_VIS_CENTER
     * VGA_ALWAYS_RECALC_SIZE				VGA_ONLY_RECALC_SIZE_WHEN_INVALID
    /* VI_specAttrs
     * VI_specAttrs is a record containing eight flags used when the visible
     * object is part of a specific UI library. This field is rarely if ever
     * used by applications. */
    @instance  SpecAttrs				 VI_specAttrs = 0;
    /* Possible flags:
     * SA_ATTACHED				SA_REALIZABLE
     * SA_BRANCH_MINIMIZED				SA_USES_DUAL_BUILD
     * SA_CUSTOM_VIS_PARENT				SA_SIMPLE_GEN_OBJ
     * SA_CUSTOM_VIS_PARENT_FOR_CHILD
     * SA_TREE_BUILT_BUT_NOT_REALIZED */
    /* VI_link
     * VI_link contains an object pointer to the visible object's next sibling
     * in the tree. Note that VisClass has no corresponding @composite field;
     * if the object is to have children, it must be of class VisCompClass.
    @instance @link				 VI_link;

VI_bounds

VI_typeFlags

VI_attrs

VI_optFlags

VI_geoAttrs

VI_specAttrs

VI_link

VisClass: 3.1 VisClass Instance Data: VI_bounds

The VI_bounds field is a Rectangle structure containing four values: The left, top, right, and bottom outer bounds of the object. The bounds of a visible object describe the object to the rest of the world: the input manager and UI use them to determine when the mouse pointer is over the object, and the object's visible parent (if any) uses them to figure out the total geometry of all its children.

The VI_bounds field is very important to nearly all visible objects. In fact, Positioning Visible Objects is devoted to this field and how it is used. For full information on visible bounds, see that section.

VisClass: 3.2 VisClass Instance Data: VI_typeFlags

VI_typeFlags, MSG_VIS_SET_TYPE_FLAGS, MSG_VIS_GET_TYPE_FLAGS

The VI_typeFlags field is a bitfield record containing eight flags. These flags determine the type of the visible object and are used primarily by the UI and the windowing system. When used at all, these flags are typically set in the object's definition or when the object is first created, and they are rarely changed dynamically by an application. The eight flags in this record are listed below. The record is of type VisTypeFlags .

VTF_IS_COMPOSITE
This flag is set if the object is of VisCompClass rather than VisClass . This flag indicates the object may have children (normal VisClass objects can not have children). Because this is not the only difference between these classes, changing this flag can have unpredictable results.
VTF_IS_WINDOW
This flag is set if the object creates a window via the window system in order to display itself and its children. This flag implies the object must also have VTF_IS_COMPOSITE set, meaning the object must actually be of VisCompClass and not VisClass . If this flag is set, the UI will assume that the VI_bounds field of the object represents the current size of the window. Objects with the VTF_IS_WINDOW flag set are skipped by the normal input and output messages such as MSG_VIS_DRAW and MSG_META_START_SELECT ; these messages will be passed directly to the object's appropriate child. VTF_IS_WINDOW and VTF_IS_PORTAL are mutually exclusive.
VTF_IS_PORTAL
This flag is set if the object is a portal. A portal is an object that has its own window and appears in its parent's window. A portal differs from a window (VTF_IS_WINDOW) in that the window has no part of itself drawn in its parent window. This flag implies that the VTF_IS_COMPOSITE flag must also be set indicating the object may have visible children. All visible children of a portal will appear in the portal's associated window if VTF_CHILDREN_OUTSIDE_PORTAL_WIN is set. The object itself may display portions of itself within its parent's window, though; an example of this is the UI's implementation of the GenDisplayControl object, which manages several child windows within its own window area. Its border is drawn in the parent window, then its window is defined to be one pixel inside this border. VTF_IS_PORTAL and VTF_IS_WINDOW are mutually exclusive.
VTF_IS_WIN_GROUP
This flag is set for the top visible object in a visible branch which makes that branch a realizable entity. This flag implies that both VTF_IS_COMPOSITE and VTF_IS_WINDOW must be set as well. An example would be the specific UI's implementation of a GenPrimary object--if the primary is not visible, none of its children may be visible (with some exceptions).
VTF_IS_CONTENT
This flag is set if the object is the output of a window object. Typically, this will be set only if the object is of VisContentClass (not for VisClass or VisCompClass ). Note that if this is set, both VTF_IS_WINDOW and VTF_IS_WIN_GROUP must also be set.
VTF_IS_INPUT_NODE
This flag is an optimization bit. It is set if the object controls input flow (such as a VisContent) or is a target or focus node that is not a window group (VTF_IS_WIN_GROUP). MSG_VIS_VUP_ALTER_INPUT_FLOW is sent directly to objects with this bit set unless there is a need for them to actually use the VUP mechanism, as is the case with mouse grabs in a 32-bit content. Similarly, MSG_META_MUP_ALTER_FTVMC_EXCL , MSG_VIS_FUP_QUERY_FOCUS_EXCL , and MSG_VIS_VUP_QUERY_FOCUS_EXCL are sent to the first visible object up the tree that either is a window group or has this bit set.
VTF_IS_GEN
This flag is set if the visible object is the specific UI counterpart to a generic object. This flag must be set for the object to handle messages that begin MSG_SPEC . The only objects that will ever have this flag set are those in Specific UI libraries.
VTF_CHILDREN_OUTSIDE_PORTAL_WIN
This flag is set if children of a portal object may appear in the object's parent window rather than in the portal's specially-created window. The VTF_IS_PORTAL flag must also be set for this object. An example of this is the specific UI's representation of a GenView object and its children; the view's children appear along the edges of the view window rather than inside it. The GenDisplayControl, by contrast, has all its children appearing within its window; thus, it does not have this flag set.

Although the VI_typeFlags record will rarely be changed at run-time, you can set new values by sending MSG_VIS_SET_TYPE_FLAGS to the visible object while it is realized. To retrieve the current type flags, send the object a MSG_VIS_GET_TYPE_FLAGS .

MSG_VIS_SET_TYPE_FLAGS
void	MSG_VIS_SET_TYPE_FLAGS(
        VisTypeFlags		flagsToSet,
        VisTypeFlags		flagsToClear);

This message causes the object to set its VI_typeFlags field to the values passed. The flagsToSet parameter will be OR-ed with VI_typeFlags , and the flagsToClear parameter will be logically inverted and then AND-ed with the field. The clear operation will occur after the set. Therefore a flag set in both parameters will be cleared by this message.

Source: Unrestricted.

Destination: Any visible object; however, the object must not be realized when it receives the object.

Parameters: flagsToSet A record of VisTypeFlags indicating which flags are to be set for the object.

flagsToClear
A record of VisTypeFlags indicating which flags are to be cleared for the object (a flag set in flagsToClear will be cleared in VI_typeFlags ).

Return: Nothing.

Interception: Unlikely.

MSG_VIS_GET_TYPE_FLAGS
VisTypeFlags MSG_VIS_GET_TYPE_FLAGS();

This message returns a visible object's current VI_typeFlags field.

Source: Unrestricted.

Destination: Any visible object.

Parameters: None.

Return: A byte of VisTypeFlags representing the object's current VI_typeFlags field.

Interception: Unlikely.

VisClass: 3.3 VisClass Instance Data: VI_attrs

VI_attrs, MSG_VIS_GET_ATTRS, MSG_VIS_SET_ATTRS

The VI_attrs field is a record of eight flags ( VisAttrs )that determine the general visible attributes of the object. These attributes may be changed on the fly and often are. Applications that don't involve visible tree operations (adding, removing, or moving branches) probably will not change these attributes dynamically.

To set the flags after the object has been instantiated, send it a MSG_VIS_SET_ATTRS with the appropriate flags to be set. To retrieve the VI_attrs record, send the object a MSG_VIS_GET_ATTRS . The VA_REALIZED flag may not be changed with MSG_VIS_SET_ATTRS .

The eight flags of the VisAttrs record are listed below:

VA_VISIBLE
This flag is effective only for window group objects (those with VTF_IS_WIN_GROUP set in VI_typeFlags ) and is ignored for other objects. If set, it indicates that the visible branch headed by this object can be made visible as soon as it is linked visibly to another visible branch. For example, a window group object may have this bit set and not be on the screen if its parent window group is not set VA_VISIBLE; as soon as the parent window group becomes VA_VISIBLE, though, the entire branch will become visible.
VA_FULLY_ENABLED
This flag is normally set for visible objects. If it is set, the object is fully enabled when visible on the screen. If not set, the object is not fully enabled--the object will typically be drawn in a 50% pattern (grayed out) and will not receive click events. This flag works even for normal visible objects, though this behavior is seen almost exclusively with generic UI objects.
VA_MANAGED
This flag indicates that the object's location is managed by its parent. If the flag is set, space is set aside in the parent composite by the geometry manager. When the window displaying this object changes, the geometry will have to be calculated. If this flag is not set, the object can alter its own location and bounds without affecting the parent's geometry.
VA_DRAWABLE
This flag indicates whether the object is drawn or invisible. If the flag gets set or reset during execution, the rectangle defined by its VI_bounds field will automatically be marked invalid.
VA_DETECTABLE
This flag, when set, indicates that the object might respond to mouse, pointer, keyboard, or other similar events. If the flag is reset (such as with display-only objects), its parent composite object will not bother passing it these events. This flag must be set if the object is a window composite. If the status of this flag is changed during execution, the object's grab state will not change. For example, if the object has the mouse grab when a MSG_VIS_SET_ATTRS resets the flag, the object will retain the mouse grab until it is released or preempted normally.
VA_BRANCH_NOT_MINIMIZABLE
This flag is used only by the visible counterparts of generic UI objects. If set, it indicates that this branch (e.g., a GenInteraction and its children) stays visible and usable even when the parent primary window is minimized.
VA_OLD_BOUNDS_SAVED
This flag, when set, causes the geometry manager to store the object's old bounds when its VI_bounds field changes. The old bounds are stored in a TEMP_VIS_OLD_BOUNDS vardata field.
VA_REALIZED
This flag indicates whether the object is visibly open on the screen--i.e., whether it is positioned on an open window. If the flag is set, the object is visible on the screen. When the object is no longer visible on the screen, this flag is reset. This flag may not be altered by MSG_VIS_SET_ATTRS ; it may only be changed by MSG_VIS_OPEN (which sets the flag) and MSG_VIS_CLOSE (which resets it).
MSG_VIS_SET_ATTRS
void	MSG_VIS_SET_ATTRS(
        VisAttrs		attrsToSet,
        VisAttrs		attrsToClear,
        VisUpdateMode		updateMode);

This message sets the object's VI_attrs field to the values passed. The attrsToSet parameter will be OR-ed with VI_attrs , and the attrsToClear parameter will be logically inverted and then AND-ed with the field. The clear operation will occur after the set, and therefore a flag set in both parameters will be cleared. The updateMode parameter indicates when the visual update of the object should occur after the new attributes have been set.

Source: Unrestricted.

Destination: Any visible object.

Parameters: attrsToSet A record of VisAttrs indicating which flags are to be set for the object.

attrsToClear
A record of VisAttrs indicating which flags are to be cleared for the object (a flag set in attrsToClear will be cleared in VI_attrs ).
updateMode
A VisUpdateMode indicating when the object should be visually updated on the screen.

Return: Nothing.

Interception: Unlikely.

Warnings: Not all flags in the VI_attrs record may be set with this message.

MSG_VIS_GET_ATTRS
VisAttrs	MSG_VIS_GET_ATTRS();

This message returns a visible object's current VI_attrs field.

Source: Unrestricted.

Destination: Any visible object.

Parameters: None.

Return: A byte of type VisAttrs representing the current flags set in the object's VI_attrs field.

Interception: Unlikely.

VisClass: 3.4 VisClass Instance Data: VI_optFlags

VI_optFlags, MSG_VIS_GET_OPT_FLAGS

The VI_optFlags field is a VisOptFlags record containing eight flags. These flags are set and used by the geometry manager to mark which objects need to be updated and how. The flags can not be set individually by applications; however, they can be retrieved with the message MSG_VIS_GET_OPT_FLAGS . It is unlikely that your code will ever access these flags directly. The eight flags in this field are listed below.

VOF_GEOMETRY_INVALID
This flag indicates that the general geometry of this object has become invalid (with a MSG_VIS_MARK_INVALID ). This is used primarily when an object knows that its bounds must be recalculated or re-verified and that any change must be reflected in its parent window. This flag will be reset when a geometry update resolves the object's bounds. Typically, this will occur with a window update message such as MSG_VIS_UPDATE_WIN_GROUP .
VOF_GEO_UPDATE_PATH
This flag, also set by MSG_VIS_MARK_INVALID , marks a trail of objects that all must have their geometry updated.
VOF_IMAGE_INVALID
This flag indicates that the image of the object or the bounds of a windowed object have changed and therefore need to be updated. This flag is set by MSG_VIS_MARK_INVALID . The image redraw will occur the next time one of the window update messages is used on the object's visual branch. This flag will be reset when a visual update redraws the object. Typically, this will occur with MSG_VIS_UPDATE_WIN_GROUP or another window update message.
VOF_IMAGE_UPDATE_PATH
This flag, also set by MSG_VIS_MARK_INVALID , marks a trail of objects that must have their images updated.
VOF_WINDOW_INVALID
This flag indicates that the window the object resides in needs to be opened or closed, or that the object itself needs to be opened or closed via MSG_VIS_OPEN or MSG_VIS_CLOSE . It is set by MSG_VIS_MARK_INVALID . This flag will be reset when a visual update occurs on the window group. Typically, this will occur with a window update message such as MSG_VIS_VUP_UPDATE_WIN_GROUP . (This message should be called whenever an object is added to a visible tree; it ensures the object will be properly opened.)
VOF_WINDOW_UPDATE_PATH
This flag, also set by MSG_VIS_MARK_INVALID , marks a trail of window objects that all must have their windows and open/closed state updated.
VOF_UPDATE_PENDING
This flag indicates that the object has an unprocessed MSG_VIS_UPDATE_WIN_GROUP in its event queue.
VOF_EC_UPDATING
This flag is used by the VisClass error-checking code only. It indicates that a visible branch is currently being updated; the system checks it to make sure nested updates do not occur.
MSG_VIS_GET_OPT_FLAGS
VisOptFlags MSG_VIS_GET_OPT_FLAGS();

This message returns the current VI_optFlags field of the object.

Source: Unrestricted.

Destination: Any visible object.

Parameters: None.

Return: A byte record of VisOptFlags reflecting the current settings in the object's VI_optFlags field.

Interception: Unlikely.

See Also: MSG_VIS_MARK_INVALID.

VisClass: 3.5 VisClass Instance Data: VI_geoAttrs

VI_geoAttrs, MSG_VIS_SET_GEO_ATTRS, MSG_VIS_GET_GEO_ATTRS

The VI_geoAttrs field is a VisGeoAttrs record of eight flags that determine the state of the object's geometry. They can be set and reset during execution to dynamically change the geometry behavior of the object. None of the flags is set by default. To set the flags, send the object a MSG_VIS_SET_GEO_ATTRS . The flags can be retrieved with a MSG_VIS_GET_GEO_ATTRS . The eight flags in this record are listed below.

VGA_GEOMETRY_CALCULATED
This flag indicates whether the object has ever been calculated. If the flag is clear, the object's geometry is about to be calculated. An object may use this flag if it has a particular desired size. This flag is cleared before a geometry recalculation by MSG_VIS_RESET_TO_INITIAL_SIZE .
VGA_NO_SIZE_HINTS
This flag is used by the Specific UI library to determine whether the object has any sizing hints or not. If the flag is not set, the object has one or more sizing hints (e.g., HINT_INITIAL_SIZE or HINT_FIXED_SIZE ).
VGA_NOTIFY_GEOMETRY_VALID
If this flag is set, the geometry manager will notify the object when its geometry messages have all been handled and the object's geometry is set. Notification will be in the form of MSG_VIS_NOTIFY_GEOMETRY_VALID . This flag may be used by visual objects for calculations or other sizing once geometry is redone but before redrawing occurs.
VGA_DONT_CENTER
This flag, when set, allows the object to individually override its parent's centering behavior. The object will instead appear at either the left edge of a vertical composite or the top edge of a horizontal composite.
VGA_USE_VIS_SET_POSITION
This flag is set to indicate that the object does not use the default handlers for MSG_VIS_SET_POSITION and MSG_VIS_POSITION_BRANCH . This flag provides an optimization that allows static calls to the geometry manager.
VGA_USE_VIS_CENTER
If this flag is set, the geometry manager will use the standard VisClass or VisCompClass center message to calculate the object's center. Unless an object is using some unusual centering behavior, it should have this flag set. This flag allows the geometry manager to speed up calculations for normal center behavior.
VGA_ONLY_RECALC_SIZE_WHEN_INVALID
This flag indicates that the object's size should be recalculated only when its geometry is marked invalid. That is, once the object's geometry is calculated, it will always return the same size until its geometry is once again marked invalid. Because the geometry manager often queries objects for their sizes, most objects should have this flag set. It allows the geometry manager to speed up calculations.
VGA_ALWAYS_RECALC_SIZE
This flag avoids optimizations that do not recalculate the object's size when passed its current size. Though not often used, this flag may be needed in certain situations (e.g., when a composite object must expand to fit and center its children).
MSG_VIS_SET_GEO_ATTRS
void	MSG_VIS_SET_GEO_ATTRS(
        VisGeoAttrs		attrsToSet,
        VisGeoAttrs		attrsToClear,
        VisUpdateMode		updateMode);

This message causes the object to set its VI_geoAttrs field to the values passed. The attrsToSet parameter will be OR-ed with VI_geoAttrs , and the attrsToClear parameter will be logically inverted and then AND-ed with the field. The clear operation will occur after the set, and therefore a flag set in both parameters will end up cleared. Many of the attributes in this record are used only by Specific UI libraries and should not be altered by applications.

Source: Unrestricted.

Destination: Any visible object.

Parameters: attrsToSet A record of VisGeoAttrs indicating which flags are to be set for the object.

attrsToClear
A record of VisGeoAttrs indicating which flags are to be cleared for the object (a flag set in attrsToClear will be cleared in VI_geoAttrs ).
updateMode
A VisUpdateMode indicating when the object should be visually updated on the screen.

Return: Nothing.

Interception: Unlikely.

MSG_VIS_GET_GEO_ATTRS
VisGeoAttrs MSG_VIS_GET_GEO_ATTRS();

This message returns a visible object's current VI_geoAttrs field.

Source: Unrestricted.

Destination: Any visible object.

Parameters: None.

Return: A record of type VisGeoAttrs reflecting the flags currently set in the object's VI_geoAttrs field.

Interception: Unlikely.

VisClass: 3.6 VisClass Instance Data: VI_specAttrs

VI_specAttrs, MSG_SPEC_SET_ATTRS, MSG_SPEC_GET_ATTRS

The VI_specAttrs field is a record of eight flags. This field is accessed only by objects that are part of a Specific UI library. Applications will almost never access this field or use its flags. Documentation on creating specific UI libraries may be published under separate cover.

MSG_SPEC_SET_ATTRS
void	MSG_SPEC_SET_ATTRS(
        SpecAttrs		attrsToSet,
        SpecAttrs		attrsToClear,
        VisUpdateMode		updateMode);

This message sets the current VI_specAttrs field to the values passed. A flag set in both attributes parameters will be cleared.

Source: Unrestricted.

Destination: Any visible object used as a specific UI object.

Parameters: attrsToSet A record of SpecAttrs indicating which flags are to be set in the object's VI_specAttrs field.

attrsToClear
A record of VisAttrs indicating which flags are to be cleared for the object (a flag set in attrsToClear will be cleared in VI_specAttrs ).
updateMode
A VisUpdateMode indicating when the object should be visually updated on the screen.

Return: Nothing.

Interception: Unlikely.

MSG_SPEC_GET_ATTRS
SpecAttrs MSG_SPEC_GET_ATTRS();

This message returns a visible object's current VI_specAttrs field. This field is used only by Specific UI libraries and is meaningful only for objects in these libraries.

Source: Unrestricted.

Destination: Any visible object that is used as a specific UI object.

Parameters: None.

Return: A byte of type SpecAttrs indicating which flags are set in the object's VI_specAttrs field.

Interception: Unlikely.

VisClass: 3.7 VisClass Instance Data: VI_link

VI_link

The VI_link field holds the optr of the visible object's next sibling in the visible object tree. This field may not be accessed directly by applications but may be set or changed with the various visible tree messages. For full information on how to build and manipulate visible object trees, see Working with Visible Object Trees .

VisClass: 4 Using VisClass

As stated earlier, visible objects are nearly useless unless you subclass one of the visible classes and handle certain messages. Although there's a lot of functionality built into VisClass and its subclasses, they must be customized to be of any use. In addition, visible objects must be put into a visible tree and connected to a GenView or other windowed UI object to be displayed.

Basic VisClass Rules

Drawing to the Screen

Positioning Visible Objects

Handling Input

VisClass: 4.1 Using VisClass: Basic VisClass Rules

To use visible objects, you must subclass the Vis classes and create a visible object tree. Otherwise, the visible objects will not be able to display themselves or accept user input, the two things that make visible objects worthwhile.

Nearly all visible objects you use will at least display themselves on the screen. Some may accept mouse or keyboard input; some may move themselves around the screen, resize themselves, or implement custom geometry management functions.

For a subclass of VisClass to display itself on the screen, it must handle the message MSG_VIS_DRAW . This message is sent to all visible objects in a given window when that window is subjected to an exposure event. In addition, the object must be part of a tree of visible objects, the top node of which is connected to a GenView or other windowed object. Displaying a basic visible object is discussed below.

For a visible object to handle mouse input, it will need to handle a subset of the mouse event messages. Typically, a visible object will want to know when the mouse is clicked within the object's bounds ( MSG_META_START_SELECT ) and when the mouse button is released ( MSG_META_END_SELECT ). Other messages dealing with mouse motion or other mouse buttons may also be of interest to the object. In addition, visible objects may request and handle Ink input. Handling of these messages is discussed in Handling Input .

Most applications using visible object trees will add objects to and remove them from the trees. MSG_VIS_ADD_CHILD and MSG_VIS_REMOVE are the two messages used most often for tree management. When an object should be freed, it can be destroyed with MSG_VIS_DESTROY , which will remove it from the tree and then free it.

Some applications will want to use the built-in geometry management features of GEOS. The geometry manager can automatically resize and reposition an entire visible object tree properly according to pre-set constraints. (The constraints can also be changed at run-time.) How the system manages visible object geometry is discussed in Geometry Management .

The geometry manager, though extremely useful for non-overlapping objects, may not be sufficient for all the needs of a complex application. For an object to determine its own size and position, it has to handle some of the messages sent out by the geometry manager. You can also manually set the size and position of each visible object. This is discussed in Positioning Visible Objects .

Often applications may need to change something about a visible object or the tree it's in. When this happens, the application must force a visual update by invalidating either the geometry or the image (or both) of the object and then calling MGS_VIS_VUP_UPDATE_WIN_GROUP . To mark any object invalid, the application must call MSG_VIS_MARK_INVALID .

Many visible objects will have specific functions they perform. For example, if the user presses on a menu item labeled "New Game" or something similar, the visible object may return itself to its original location (as in the TicTac sample application). To get this type of functionality, you must define new messages for your subclass of VisClass and have the object handle them. In the case of the TicTac game pieces, each piece handles the MSG_PIECE_NEW_GAME message by resetting the object's position to its original position.

Visible objects are maintained and managed in a tree structure. The tree has three basic elements: The root (topmost) node must be an object of VisContentClass . Any nodes in the middle of the tree, nodes that are allowed to have children, must be of VisCompClass . Any leaf nodes (guaranteed not to have children) may be of either VisClass or VisCompClass . (Subclasses of the above classes may also be used.)

The visible object tree is displayed normally through a GenView object. The output of the GenView must be tied directly to the top node of the visible object tree; this is why the visible tree must be headed by a VisContent--only the VisContent has the methods to handle GenView messages.

The VisContent object may or may not do things other than manage the visible tree. For example, the content may draw some background graphics to the window before the other visible objects draw themselves, or it may implement some special geometry behavior to arrange its children.

The VisContent can have children. These children can be either standard visible objects or composite visible objects. Composites, like the content, are allowed to have children. For efficiency, standard visible objects can not have children. Therefore, only leaf objects in the tree may be of VisClass .

The visible object tree may have any number of layers of composites. You should pay attention to the functions required in your application and fit the structure of the tree to the application's needs.

These restrictions, combined with the link to the generic object tree through GenView, result in an object tree structure somewhat like that shown in the figure above. Of course, the actual generic and visible tree structure will vary from application to application. Also, note that the generic and visible sections are actually two different object trees; they are not connected through a parent-child relationship anywhere.

VisClass: 4.2 Using VisClass: Drawing to the Screen

One of the main features of visible objects is their ability to draw themselves on the screen. The visible tree does not have to detect when drawing or redrawing should be done; the UI will do this automatically through the window system and the GenView object.

Visual Updates

MSG_VIS_OPEN, MSG_VIS_CLOSE

The visible tree will have to draw or redraw itself when a portion of its GenView window becomes newly exposed. The GenView keeps track of its window and will notify its content when any portion of the window becomes exposed. Exposure can occur from several events: The view window could be newly created; another window could have been moved, exposing the view's window; or the view window could have been scrolled.

Each of these exposure events looks exactly the same to the visible tree. When one of them occurs, the GenView will send a MSG_META_EXPOSED to its content object. The content (a VisContent or GenDocument if you're using a visible tree) will create a GState for the window and translate the MSG_META_EXPOSED into a MSG_VIS_DRAW . It will then send this MSG_VIS_DRAW to itself.

The default handler for MSG_VIS_DRAW in a composite is to simply pass the message on to all of the composite's children. There is no default behavior for MSG_VIS_DRAW in VisClass .

Any object (content, composite, or leaf) that wants to represent itself on the screen must handle MSG_VIS_DRAW . Any composite that subclasses this message must be sure to pass it on to all of its children with the following line:

@send @visChildren:: MSG_VIS_DRAW(drawFlags, gstate);

Redrawing is not initiated only by the view window becoming exposed. It can also be initiated by some portion of the visible tree changing so that it needs to be redrawn (e.g., a child is moved from one composite to another, which may have an effect on the visual representation of the composite). Typically, any change that necessitates a redrawing will automatically generate a MSG_VIS_DRAW to the appropriate objects. These operations, however, will require a VisUpdateMode to be passed along with the other parameters.

All of the above assumes that the visible object is "open" or "realized." If an object is not open, it will not appear on the screen or as part of a composite's geometry calculations. When a window is first created, the window's content is automatically opened with MSG_VIS_OPEN . In the case of a visible tree attached to a GenView, this means that the VisContent object will be opened along with the window. All of the VisContent's children will also be opened automatically (assuming their VOF_WINDOW_INVALID bits are set, which is true the first time a visible object comes up).

If a visible object is added to an already-opened composite, the child will automatically be opened and marked invalid. This will cause the composite's geometry and image to be updated during the next visual update. If a child is added to a composite that is not currently open, the child will not be opened.

Although it is rarely done directly, you can manually open a visible object with the message MSG_VIS_OPEN , and you can "close" an object (remove it from the screen and from the tree's geometry calculations) with MSG_VIS_CLOSE . To open or close an entire branch of the visible tree, you can send the appropriate message to the top composite object in the branch. The message will propagate down through all the visible children.

MSG_VIS_OPEN
void	MSG_VIS_OPEN(
        WindowHandle window);

This message is part of the visual update mechanism and will be sent by the system when an object must be visually updated to appear on the screen. It will be sent to any VOF_WINDOW_INVALID objects in a branch whose VTF_IS_WIN_GROUP object has become VA_VISIBLE. Any object added into a visible tree having VOF_WINDOW_INVALID set will receive a MSG_VIS_OPEN at the next visual update. This message propagates down the visible tree and does not cross over boundaries of window groups. This message is often subclassed by objects that want to initialize some information before being drawn on the screen. If an object subclasses this message, it should be sure to call its superclass somewhere in the handler.

Source: Visual update mechanism.

Destination: Any visible object.

Parameters: window Handle of window in which the object is to appear, or zero if it should appear in the top window object.

Return: Nothing.

Interception: Will be intercepted by objects that want to initialize certain data or take certain actions before being drawn on the screen. Any intercepting objects must call the superclass somewhere in their handler.

MSG_VIS_CLOSE
void	MSG_VIS_CLOSE();

This message is part of the visual update mechanism and is sent to objects that are being taken off the screen. The system will send this message to any objects in a visual branch whose VTF_IS_WIN_GROUP object is set not visible (~VA_VISIBLE). This message closes appropriate windows and propagates to all children to the bottom leaf objects of the visible branch. This message will not cross window group boundaries; if a lower VTF_IS_WIN_GROUP object is encountered during the message propagation, an error will likely occur. Therefore, lower window groups must be closed before higher window groups may be closed.

Source: Visual update mechanism.

Destination: Any visible object.

Interception: Will be intercepted by objects that want to do something as it is being taken off the screen. Any objects that intercept the message must call their superclasses in the handlers.

VisUpdateMode

VisUpdateMode is an enumerated type that determines when and how the visible tree will be visually updated when its image or geometry becomes invalid. This type has four enumerations, described below:

VUM_MANUAL
This mode indicates that the UI should not update at all after this change. Instead, the visual update will be initiated manually later on.
VUM_NOW
This mode indicates that the visual update should occur immediately after the change is effected.
VUM_DELAYED_VIA_UI_QUEUE
This mode indicates that the visual update should occur only when the UI queue is completely empty (indicating no further changes are coming).
VUM_DELAYED_VIA_APP_QUEUE
This mode indicates that the visual update should occur only when the application's queue is empty (indicating that no more changes are coming). This mode is very useful for making several changes that each require an update; it will put the updating off until the very end and will cause only a single update.

A VisUpdateMode is passed as a parameter to certain Vis and Gen messages. These messages typically have some effect on the visual representation of the objects--either on the geometry or on the image itself.

The Initial GState

 MSG_VIS_VUP_CREATE_GSTATE

For any drawing to occur, a graphic state must exist. The graphic state typically is associated with the GenView's window and is created automatically, either when the window is first realized on the screen or when an exposure event occurs.

To create a GState, the system generates a MSG_VIS_VUP_CREATE_GSTATE and sends it to the object that first requires a visual update. This message travels up the visible tree until it gets to a window group (an object with its VTF_IS_WINDOW flag set), and then it creates a GState associated with that window object. Sometimes a content object will subclass this message to set up its own display information before the GenView window can create the new GState. The other time in which this message may be subclassed is by a content that manages a 32-bit graphics space; the content will have to translate the gstate to the proper position in the large document before letting other objects use it.

MSG_VIS_VUP_CREATE_GSTATE
GStateHandle MSG_VIS_VUP_CREATE_GSTATE();

This message travels up the visible object tree until it reaches either the root object or a window group object. It then creates a graphic state associated with that object; this graphic state is then used for the subsequent visual update. You may wish to subclass this message in order to alter the GState created or to set it up to take 32-bit coordinates. If you subclass it, however, be sure to call the superclass somewhere in the handler.

Source: Unrestricted--typically sent by a visible object to itself.

Destination: Any visible object--typically sent by a visible object to itself.

Parameters: None.

Return: The GStateHandle of the newly-created GState.

Interception: Unlikely--VisContent objects may intercept this message to translate the returned GState in a 32-bit graphics space. In this case, the VisContent must first call its superclass, then translate the GState appropriately before returning.

Retrieving the Current Window Handle

MSG_VIS_QUERY_WINDOW

Often a visible object will require the window handle of the window it's currently residing in. Some objects, for example, will want to force visual updates in real-time, and they can do this by using the current window handle, creating a special GState for it, and drawing to that GState. The visible object can either cache the window handle when the window is first opened, or it can retrieve it with MSG_VIS_QUERY_WINDOW .

MSG_VIS_QUERY_WINDOW
WindowHandle MSG_VIS_QUERY_WINDOW();

This message returns the window handle of the window the object currently resides in. In many cases, this will be the window handle of a GenView window.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to retrieve its current window handle.

Parameters: None.

Return: The window handle of the object's current window.

Interception: Unlikely.

See Also: MSG_GEN_VIEW_GET_WINDOW.

Causing Redrawing

MSG_VIS_BOUNDS_CHANGED, MSG_VIS_REDRAW_ENTIRE_OBJECT, MSG_VIS_INVALIDATE, MSG_VIS_MARK_INVALID, MSG_VIS_INVAL_TREE, MSG_VIS_ADD_RECT_TO_UPDATE_REGION

If your visible tree contains a composite that manages its own children, chances are you'll need to force a visual update after certain changes occur. For example, if you have an object that can be moved with the mouse, it will probably want to erase its image and redraw itself somewhere else after the move; since the geometry manager is not involved here, the object must force a redrawing of the affected portion of the visible tree.

There are several ways an object can force a redraw. These all take the form of messages, each of which is described below.

MSG_VIS_DRAW
This message causes the entire visible branch to draw itself. This message may be sent by any object to any other object, including itself. It does not, however, update the screen image; you must call MSG_VIS_VUP_UPDATE_WIN_GROUP .
MSG_VIS_BOUNDS_CHANGED
This message causes the visible object to mark the affected portions invalid, causing the visual update mechanism to redraw that portion during the next visual update. This message is part of the visible update mechanism.
MSG_VIS_REDRAW_ENTIRE_OBJECT
This message causes the visible object to redraw itself entirely (as opposed to just the invalid portion) if it is currently drawable. Essentially, the object will send itself a MSG_VIS_DRAW when it receives this message. This is especially useful if an object needs to completely change its image.
MSG_VIS_INVALIDATE
This message invalidates the entire region inside the visible object's bounds. It will cause the window system to generate a MSG_META_EXPOSED for the affected area, causing a MSG_VIS_DRAW to propagate down the tree to all affected objects. Note that this message must be subclassed to work with scaled windows or large documents.
MSG_VIS_INVAL_TREE
This message invalidates the entire region inside the visible object's bounds, including any child window areas. The window system will generate a MSG_META_EXPOSED for all affected windows.
MSG_VIS_MARK_INVALID
This message allows the caller to set an object's invalid flags so the object will be properly updated later. The object is marked as being invalid, and the default handler ensures a visual update path is created. This method may not be subclassed by applications.
MSG_VIS_ADD_RECT_TO_UPDATE_REGION
This message adds the passed rectangular region to a window's update list. Essentially, the window group associated with the recipient object will ensure that the given rectangle gets marked invalid and included in the next visual update.
MSG_VIS_DRAW
void	MSG_VIS_DRAW(
        DrawFlags		drawFlags,
        GStateHandle		gstate);

This message causes the visible object to draw itself. The default behavior of this message for composite objects is to pass the message on to all children. There is no default behavior for VisClass for this message.

Source: Unrestricted.

Destination: Any visible object.

Parameters: drawFlags A record of type DrawFlags (defined below). The flags in this record describe what type of action initiated the MSG_VIS_DRAW .

gstate
The GStateHandle of the gstate created for this visual update. All drawing done in the handler should be for this GState.

Return: Nothing.

Interception: All visible objects that wish to draw anything to the screen must intercept this message. Composites that intercept it must be sure to send it to their children (with @send @visChildren ).

Structures: The flags in the DrawFlags record are listed below:

DF_EXPOSED
This flag indicates the MSG_VIS_DRAW is a result of a MSG_META_EXPOSED from the window system. This is the standard and most usual form of MSG_VIS_DRAW .
DF_OBJECT_SPECIFIC
This flag is used by various Specific UI objects for object-specific things. Your visible objects should not be concerned with this flag.
DF_PRINT
This flag indicates that the MSG_VIS_DRAW is a result of a MSG_META_EXPOSED_FOR_PRINT , the printing message. The GState passed will be a spooler GState rather than a window GState. If this flag is set, DF_EXPOSED will also be set.
DF_DONT_DRAW_CHILDREN
This flag indicates that composite objects should draw themselves but should not propagate the drawing message on to their children (this is nonstandard behavior).
DF_DISPLAY_TYPE
This is actually an enumerated type indicating the display type being used by the system right now. The display type enumeration is named DisplayClass . For review, the types are listed below:
DC_TEXT, DC_GRAY_1, DC_GRAY_2, DC_GRAY_4, DC_GRAY_8, DC_COLOR_2, DC_COLOR_4, DC_CF_RGB.
MSG_VIS_BOUNDS_CHANGED
void	MSG_VIS_BOUNDS_CHANGED(@stack
        word	bottom,
        word	right,
        word	top,
        word	left);

The geometry manager sends this message when a geometry update changes an object's bounds.

Source: Unrestricted--sent by the geometry manager when a visible object's bounds have changed as the result of a geometry change.

Destination: Any visible object.

Parameters: bottom , right , top , left
 The four parameters correspond to the rectangle defining the object's old bounds. The parameters are passed on the stack.

Return: Nothing.

Interception: Unlikely. If the object subclasses this message, it should invalidate any part of its old bounds that it might have drawn in (the default handler invalidates the entire passed range).

MSG_VIS_REDRAW_ENTIRE_OBJECT
void	MSG_VIS_REDRAW_ENTIRE_OBJECT();

This message causes the object send itself a MSG_VIS_DRAW , creating and destroying a GState for itself.

Source: Unrestricted--typically sent by an object to itself because of a state change, not as part of the visual update mechanism.

Destination: Any visible object--typically sent by an object to itself.

Interception: Unlikely.

MSG_VIS_INVALIDATE
void	MSG_VIS_INVALIDATE();

This message invalidates the entire region within the object's bounds. The message will cause the window system to generate a MSG_META_EXPOSED for the area covered by the bounds, causing a visual update to occur.

Source: Unrestricted.

Destination: Any visible object.

Interception: Must be intercepted if the object is working in a 32-bit document space. Must also be intercepted to work with scaled views.

MSG_VIS_INVAL_TREE
void	MSG_VIS_INVAL_TREE();

This message has the effect of MSG_VIS_INVALIDATE on an entire branch of the visible tree. The window system will generate MSG_META_EXPOSED for each affected window.

Source: Unrestricted.

Destination: Any visible object.

Interception: Unlikely.

MSG_VIS_MARK_INVALID
void	MSG_VIS_MARK_INVALID(
        VisOptFlags		flagsToSet,
        VisUpdateMode		updateMode);

This message allows the caller to set the recipient's VI_optFlags record so the object will get updated properly during the next visual update. The flags that can be set with this message are VOF_GEOMETRY_INVALID, VOF_WINDOW_INVALID, and VOF_IMAGE_INVALID. For more information on these flags, see VI_optFlags .

Source: Unrestricted.

Destination: Any visible object.

Parameters: flagsToSet A record of VisOptFlags indicating which type of invalidation is being caused. The flags allowed with this message are VOF_GEOMETRY_INVALID, VOF_WINDOW_INVALID, and VOF_IMAGE_INVALID.

updateMode
A VisUpdateMode indicating when the visual update caused by this message should occur.

Return: Nothing.

Interception: May not be subclassed. Certain optimizations are made in the default handler, and subclassing may have unpredictable results.

MSG_VIS_ADD_RECT_TO_UPDATE_REGION
void	MSG_VIS_ADD_RECT_TO_UPDATE_REGION(@stack
        byte		unused,		/* for word alignment of parameters */
        VisAddRectFlags		addRectFlags,
        word		bottom,
        word		right,
        word		top,
        word		left);

This message adds the passed rectangular region to the window group's list of regions that require visual update. The handler for this message will ensure that the window group object for this branch of the tree marks the region as invalid so it will be included in the next visual update. The addRectFlags parameter contains either of the flags listed below.

Source: Unrestricted--usually sent by an object to itself.

Destination: Any visible object--usually sent by an object to itself.

Parameters: unused An unused byte for alignment of parameters.

addRectFlags
A record of VisAddRectFlags , defined below.
rectangle coords
The coordinates of the rectangle to be added to the invalidation region.

Return: Nothing.

Interception: Unlikely--a composite object may wish to optimize invalidation by altering the passed bounds and then passing the message on to its superclass.

Structures: The VisAddRectFlags are listed below:

VARF_NOT_IF_ALREADY_INVALID
This flag indicates that the passed rectangle should not be invalidated if any visible object in the hierarchy going up to the window group object is marked invalid.
VARF_ONLY_REDRAW_MARGINS
This flag indicates that the object is invalidating old bounds and may indicate that the system can use a special optimization for invalidation.

Updating Window Groups

MSG_VIS_VUP_UPDATE_WIN_GROUP, MSG_VIS_UPDATE_WIN_GROUP, MSG_VIS_UPDATE_WINDOWS_AND_IMAGE

A window group is an object that has a window in which visible objects are displayed. Typically, you will not create your own window group objects, but you may need to notify a window group when it needs to be updated visually. To be a window group, an object has to have its VTF_IS_WINDOW or VTF_IS_PORTAL flag set in its VI_typeFlags field.

The window group is responsible for keeping track of the regions of its coordinate space that require visual updating. When a region or object gets invalidated, the window group remembers that it is invalid and makes sure it gets updated during the next visual update.

Any object can request that a visual update occur for the entire branch managed by a window group. It can use the three messages described in this section to request or force a visual update for the window group.

MSG_VIS_VUP_UPDATE_WIN_GROUP
Boolean	MSG_VIS_VUP_UPDATE_WIN_GROUP(
        VisUpdateMode updateMode);

This message travels up the visible tree from the recipient object to the first window group encountered; it will then cause that window group to go through a visible update by sending the window group object a MSG_VIS_UPDATE_WIN_GROUP , below.

Source: Unrestricted.

Destination: Any visible object residing in the window group that is to be updated.

Parameters: updateMode A VisUpdateMode indicating when the visual update should occur. VUM_MANUAL has the effect of a "no operation" because VUM_MANUAL does not cause visual updates.

Return: True if the update mechanism was invoked; false if it was not.

Interception: Not allowed.

Warnings: You may not subclass this message.

MSG_VIS_UPDATE_WIN_GROUP
void	MSG_VIS_UPDATE_WIN_GROUP(
        VisUpdateMode updateMode);

This message may only be sent by a window group object to itself. It causes the window group to actually go through a visual update.

Source: Visible update mechanism.

Destination: The window group object that is to be updated.

Parameters: updateMode A VisUpdateMode indicating when the visual update should occur. This is the same as passed to MSG_VIS_VUP_UPDATE_WIN_GROUP.

Return: Nothing.

Interception: Not allowed.

Warnings: You may not subclass this message.

MSG_VIS_UPDATE_WINDOWS_AND_IMAGE
void	MSG_VIS_UPDATE_WINDOWS_AND_IMAGE(
        VisUpdateImageFlags updateImageFlags);

This message is called by a window group during a visual update. It should not be sent or handled by anything other than a window group. It causes both the geometry and image of the affected branch of the visible tree to be updated, and it is used on branches which are already visually realized on the screen.

Source: Visual update mechanism.

Destination: A window group object.

Parameters: updateImageFlags
 This is a record of VisUpdateImageFlags that govern the visual update of the window group.

Return: Nothing.

Interception: Not allowed.

Structures: The flags of VisUpdateImageFlags are listed below:

VUIF_ALREADY_INVALIDATED
An optimization flag to keep an already-invalid composite from invalidating all its children.
VUIF_SEND_TO_ALL_CHILDREN
A flag to ensure that all children get invalidated; it is used when the composite only updates its margins for optimization.

Warnings: You may not subclass this message.

Using Visual Monikers

MSG_VIS_DRAW_MONIKER, MSG_VIS_GET_MONIKER_POS, MSG_VIS_GET_MONIKER_SIZE, MSG_VIS_FIND_MONIKER, MSG_VIS_CREATE_VIS_MONIKER

Although visual monikers are typically used with generic objects (and are, in fact, documented in the GenClass chapter), you can display monikers with visible objects as well. The four messages discussed in this section can be used to draw a visible moniker, get information about a moniker, or locate a particular moniker in the visible object tree.

MSG_VIS_DRAW_MONIKER
void	MSG_VIS_DRAW_MONIKER(@stack
        DrawMonikerFlags		monikerFlags,
        ChunkHandle		visMoniker,
        word		textHeight,
        GStateHandle		gstate,
        word		yMaximum,
        word		xMaximum,
        word		yInset,
        word		xInset);

This message draws a visual moniker for the object. This message may be called by an object on itself in its MSG_VIS_DRAW handler.

Source: Unrestricted.

Destination: Any visible object.

Parameters: monikerFlags A record of DrawMonikerFlags indicating how the moniker should be drawn. These flags are described below.

visMoniker
The chunk handle of the chunk containing the actual moniker to be drawn. This chunk must be in the same block as the object handling the message.
textHeight
A parameter used for optimization--if the caller knows the system text height, it should pass it here. If not, it should pass zero.
gstate
The handle of the GState to use when drawing the moniker. Typically, this is received by MSG_VIS_DRAW , which invokes this message.
yMaximum
The maximum bottom bound of the moniker if DMF_CLIP_TO_MAX_WIDTH is set in monikerFlags . Pass MAX_COORD to avoid clipping.
xMaximum
The maximum right bound of the moniker if DMF_CLIP_TO_MAX_WIDTH is set in monikerFlags . Pass MAX_COORD to avoid clipping.
yInset
The vertical inset at which to begin drawing the moniker, if left or right justified.
xInset
The horizontal inset at which to begin drawing the moniker, if top or bottom justified.

Return: Nothing.

Interception: Unlikely.

Structures: The DrawMonikerFlags structure is defined as follows:

typedef ByteFlags DrawMonikerFlags;
#define DMF_UNDERLINE_ACCELERATOR						0x40
        /* Underlines accelerator key, if any */
#define DMF_CLIP_TO_MAX_WIDTH						0x20
        /* Clips the moniker to the xMaximum
         * parameter */
#define DMF_NONE						0x10
        /* Set to draw the moniker at the
         * current pen position (ignore the
         * xInset and yInset parameters) */
#define DMF_Y_JUST_MASK						0x0c
#define DMF_X_JUST_MASK						0x03
        /* These are two bitfields that
         * determine the justification.
         * Their offsets are below; they
         * are of type Justification. */
#define DMF_Y_JUST_OFFSET					2
#define DMF_X_JUST_OFFSET					0
MSG_VIS_GET_MONIKER_POS
XYValueAsDWord MSG_VIS_GET_MONIKER_POS(@stack
        DrawMonikerFlags		monikerFlags,
        ChunkHandle		visMoniker,
        word		textHeight,
        GStateHandle		gstate,
        word		yMaximum,
        word		xMaximum,
        word		yInset,
        word		xInset);

This message returns the position at which the moniker would appear if it were drawn with MSG_VIS_DRAW_MONIKER . The moniker is not actually drawn by this message.

Source: Unrestricted.

Destination: Any visible object.

Parameters: See MSG_VIS_DRAW_MONIKER above.

Return: A dword value representing the horizontal and vertical positions where the moniker would be drawn if it were drawn with the passed parameters. The horizontal position is returned in the high word; the vertical position is returned in the low word. Use the DWORD_X and DWORD_Y macros to extract the x and y values. These macros can be found in the file graphics.h .

Interception: Unlikely.

MSG_VIS_GET_MONIKER_SIZE
SizeAsDWord MSG_VIS_GET_MONIKER_SIZE(@stack
        DrawMonikerFlags		monikerFlags,
        ChunkHandle		visMoniker,
        word		textHeight,
        GStateHandle		gstate,
        word		yMaximum,
        word		xMaximum,
        word		yInset,
        word		xInset);

This message returns the size of the moniker specified by the parameters. The moniker is not drawn.

Source: Unrestricted.

Destination: Any visible object.

Parameters: See MSG_VIS_DRAW_MONIKER above.

Return: A dword value representing the size of the moniker. The width of the moniker is returned in the high word; the height is returned in the low word. Use the macros DWORD_WIDTH and DWORD_HEIGHT, which can be found in the file visC.goh and also on DWORD_HEIGHT .

Interception: Unlikely.

MSG_VIS_FIND_MONIKER
optr	MSG_VIS_FIND_MONIKER(@stack
        VisMonikerSearchFlags			searchFlags,
        MemHandle			destBlock,
        ChunkHandle			monikerList,
        DisplayType			displayType);

This message locates the given visual moniker list and returns the optr of the moniker most appropriate for the passed display scheme.

Source: Unrestricted.

Destination: Any visible object.

Parameters: searchFlags A record of VisMonikerSearchFlags (described below) indicating the attributes of the moniker that should be returned.

destBlock
Handle of the block into which the moniker chunk should be copied.
monikerList
Chunk handle of the chunk containing the moniker list to search. The moniker list must be in the same object block as the object handling the message.
displayType
The DisplayType of the moniker to be found (see below).

Interception: Unlikely.

Structures: The VisMonikerSearchFlags are listed below.

 typedef WordFlags VisMonikerSearchFlags;
#define VMSF_STYLE					0xf000
    /* Four bits defining the preferred style of
     * the moniker. These bits are of type
     * VMStyle, which is defined below. */
#define VMSF_COPY_CHUNK					0x0400
    /* Set if the moniker should be copied into
     * the block specified if not already in
     * that block. */
#define VMSF_REPLACE_LIST					0x0200
    /* Set if the moniker list chunk containing
     * the VisMoniker should be replaced. The
     * chunk handle of the list will then point
     * to the moniker rather than the list. */
#define VMSF_GSTRING					0x0100
    /* Set if a GString moniker is expected,
     * clear if a text moniker is expected. */
    /* The remaining bits of this record are
     * reserved for internal use. */
#define VMSF_STYLE_OFFSET 12
typedef ByteEnum  VMStyle;
#define VMS_TEXT					0
            /* Normal text moniker */
#define VMS_ABBREV_TEXT					1
            /* short text abbreviation */
#define VMS_GRAPHIC_TEXT					2
            /* textual GString */
#define VMS_ICON					3
            /* normal GString moniker */
#define VMS_TOOL					4
            /* tool moniker, normally smaller
             * than a standard moniker */

The DisplayType flags are listed below and can be found in win.h:

typedef ByteFlags  DisplayType;
#define DT_DISP_SIZE						0xc0
        /*   Two bits indicating the size of the
         * display; a DisplaySize value, one of
         * DS_TINY (CGA, or 256 x 320),
         * DS_STANDARD (EGA, VGA, HGC, MCGA),
         * DS_LARGE (800 x 600 SVGA), or
         * DS_HUGE (huge screens). */
#define DT_DISP_ASPECT_RATIO						0x30
        /* Two bits indicating the aspect
         * ratio of the screen; a value of
         * DisplayAspectRatio, one of
         * DAR_NORMAL (VGA or MCGA),
         * DAR_SQUISHED (EGA or HGC), or
         * DAR_VERY_SQUISHED (CGA) */
#define DT_DISP_CLASS						0x0f
        /* Four bits indicating the class of
         * the display driver (or closest
         * match); A DisplayClass value, one of
         * DC_TEXT (char only, not implemented),
         * DC_GRAY_1 (1 bit/pixel gray scale),
         * DC_GRAY_2 (2 bit/pixel gray scale),
         * DC_GRAY_4 (4 bit/pixel gray scale),
         * DC_GRAY_8 (8 bit/pixel gray scale),
         * DC_COLOR_2 (2 bit/pixel color index),
         * DC_COLOR_4 (4 bit/pixel color index),
         * DC_COLOR_8 (8 bit/pixel color index),
         * DC_CF_RGB (color with RGB values) */
MSG_VIS_CREATE_VIS_MONIKER
ChunkHandle MSG_VIS_CREATE_VIS_MONIKER(@stack
        CreateVisMonikerFlags			flags,
        word			height,
        word			width
        word			length,
        VisMonikerDataType			dataType,
        VisMonikerSourceType			sourceType,
        dword			source);

This message creates a new chunk for a visual moniker within the recipient's object block. The new moniker can be created from an already existing visual moniker, a visual moniker list, a text string, a GString, or a token from the token database. The source may be defined by a far pointer, a global memory handle, or an optr. If the source is a text string or GString, a visual moniker structure will be created for the string. The newly-created chunk is marked dirty if the CVMF_DIRTY flag is passed.

If a moniker list is passed, the entire list will be copied into the destination object block. You must make sure that all the monikers in the list will still exist when the list is used.

Source: Unrestricted.

Destination: Any visible object.

Parameters: flags A record of CreateVisMonikerFlags . Currently only one may be passed: CVMF_DIRTY, which indicates that the chunk should be marked dirty.

height
The height of the moniker.
width
The width of the moniker.
length
The length of the moniker, if a text string.
dataType
The type of moniker, an enumeration of the type VisMonikerDataType . This parameter determines the type of moniker to be created.
sourceType
The type of source used to create the new moniker, an enumeration of VisMonikerSourceType .
source
This parameter can be a pointer to the source, the global handle of the block containing the source, or an optr pointing to the chunk containing the source, depending on the value in sourceType .

Return: The chunk handle of the new visual moniker. The visual moniker chunk resides in the object block of the object receiving the message. If the flag CVMF_DIRTY is passed, the chunk will be marked dirty.

Interception: Unlikely--custom UI gadgets may intercept in some cases.

Structures: VisMonikerSourceType and VisMonikerDataType are defined below. Both can be found in visC.goh .

typedef ByteEnum  VisMonikerSourceType;
#define VMST_FPTR					0
        /* Source is referenced by a pointer.
         * CVMF_source is a far pointer. */
#define VMST_OPTR					1
        /* Source is referenced by an optr.
         * CVMF_source is an optr. */
#define VMST_HPTR					2
        /* Source is referenced by a combination
         * memory handle and offset into the
         * memory block (as opposed to an optr
         * in which the low word is actually a
         * chunk handle, not an offset). */
typedef ByteEnum  VisMonikerDataType;
#define VMDT_NULL					0
        /* There is no source. Not valid for
         * MSG_VIS_CREATE_VIS_MONIKER. */
#define VMDT_VIS_MONIKER					1
        /* Source is a complete VisMoniker
         * structure. CVMF_length indicates the
         * size of a complete VisMoniker
         * structure; CVMF_width and
         * CVMF_height are unused. */
#define VMDT_TEXT					2
        /* Source is a text string. If the
         * string is null-terminated,
         * CVMF_length should be zero.
         * Otherwise, CVMF_length is the length
         * of the string; A VisMoniker
         * structure will be created for the
         * string. CVMF_width and CVMF_height
         * are unused. */
#define VMDT_GSTRING					3
        /* Source is a GString. If CVMF_length
         * is zero, the GString length is
         * determined by scanning for
         * GR_END_STRING. Otherwise, CVMF_length
         * indicates the length of the GString.
         * CVMF_width and CVMF_height indicate
         * the width and height of the GString.
         * If either is zero, the dimension will
         * be calculated by examining the
         * string. A VisMoniker structure will
         * be created for the GString. */
#define VMDT_TOKEN					4
        /* Source is a GeodeToken. CVMF_length,
         * CVMF_width, and CVMF_height are
         * unused. The destination must be able
         * to use this data type because the
         * specific UI must decide which moniker
         * to choose from the token in the token
         * database. */
DWORD_HEIGHT
word	 DWORD_HEIGHT(val);
        dword	val;

This macro extracts the height from a SizeAsDWord structure (dword).

DWORD_WIDTH
word	 DWORD_WIDTH(val);
        dword	val;

This macro extracts the width from a SizeAsDWord structure (dword).

MAKE_SIZE_DWORD
SizeAsDWord  MAKE_SIZE_DWORD(width, height);
        word	width, height;

This macro takes a width and height and creates a SizeAsDWord value.

Cached GStates

MSG_VIS_CREATE_CACHED_GSTATES, MSG_VIS_RECREATE_CACHED_GSTATES, MSG_VIS_DESTROY_CACHED_GSTATES

Many complex visible objects (such as VisText and VisSpline) have both a cached GState and a reference count. When the reference count goes from zero to one, the Vis object creates and caches a GState; when the reference count goes from one to zero, the cached GState is destroyed. VisClass has three messages to create, update, and destroy the cached GStates. None of these messages has any default behavior; they are provided for complex objects to handle should they need them.

MSG_VIS_CREATE_CACHED_GSTATES
void	MSG_VIS_CREATE_CACHED_GSTATES();

This message may be used to create and cache a GState, typically to avoid having a complex Vis object update several times for several simple operations (such as pointer events).

Source: Unrestricted.

Destination: Any visible object.

Interception: Must be intercepted to have any effect; there is no default behavior.

MSG_VIS_RECREATE_CACHED_GSTATES
void	MSG_VIS_RECREATE_CACHED_GSTATES();

This message may be used to have the Vis object destroy and recreate any cached GStates it has. For example, cached GStates of a complex visible object that gets moved or resized will no longer map to the proper place in the object's document. After the move or resize, this message may be used to update those GStates.

Source: Unrestricted.

Destination: Any visible object.

Interception: Must be intercepted to have any effect; there is no default behavior.

MSG_VIS_DESTROY_CACHED_GSTATES
void	MSG_VIS_DESTROY_CACHED_GSTATES();

This message may be used to destroy any cached GStates the visible object may have.

Source: Unrestricted.

Destination: Any visible object.

Interception: Must be intercepted to have any effect; there is no default behavior.

VisClass: 4.3 Using VisClass: Positioning Visible Objects

Every visible object has an instance data field called VI_bounds that records the object's visible bounds. Visible bounds are the left, top, right, and bottom bounds of a rectangle that define the maximum area covered by the visible object. The bounds are used primarily for determining the object's size and location.

The VI_bounds field is a Rectangle structure, provided by the graphics system. This structure has four components, as shown below:

  typedef struct {
    sword   R_left;    /* x of upper-left corner */
    sword   R_top;     /* y of upper-left corner */
    sword   R_right;   /* x of lower-right corner */
    sword   R_bottom;  /* y of lower-right corner */
} Rectangle;

The bounds of non-window objects are stored in document coordinates relative to the document displayed by the window in which they appear. The left and top bounds constitute the position of the object. The difference between the right and left bounds is the object's width; the difference between bottom and top is its height. Together, the width and height constitute the object's size.

The bounds of a window or portal object (VTF_IS_WINDOW or VTF_IS_PORTAL) represent the location of the window, in document coordinates, within its parent window. The coordinate system within the window object is independent from that of its parent.

Unless an object's bounds are set in the Goc code, they will be set at zero and must be initialized to some values before the object can be drawn. This initialization can be done with the messages that set position and size, or it can be done automatically by the geometry manager (if a composite is managing its children).

The VI_bounds instance field exists in VisClass and therefore occurs also in VisCompClass and VisContentClass and all their subclasses. Any object can directly set any or all of its bounds; other objects (such as a parent VisComp) can direct the object to set its bounds to certain coordinates as a result of either geometry management or some user action. For example, a VisComp that has ten children may recalculate their positions if one of those children gets resized; the composite must then notify all the children of their new positions. To learn how a composite object can manage its children in different ways, see the VisComp chapter.

Object Position and Bounds

MSG_VIS_GET_POSITION, MSG_VIS_SET_POSITION, MSG_VIS_GET_BOUNDS

An object's bounds determine both the location and the size of the object in the document space. The object may reposition itself or resize itself at will; for example, it may allow the user to move or resize it with the mouse. The object can also receive direction from other objects that it should change its bounds. Note that none of these messages by themselves will cause the object to redraw; you must also send a MSG_VIS_MARK_INVALID to the object to get either its geometry or its image to be marked invalid.

An object's position is represented by the upper-left corner of its bounds, defined by VI_bounds . R_top and VI_bounds . R_left . You can retrieve an object's position by sending it MSG_VIS_GET_POSITION , or you can set it with MSG_VIS_SET_POSITION . You can also use MSG_VIS_GET_BOUNDS to get all four components of the object's bounds.

An object can access its own bounds field with a simple pointer. Since every message has pself as an implicit parameter (a pointer to the object's instance data structure), any object can look at or change its bounds directly. (See the example in Grabbing and Releasing the Mouse .)

Using Swat, you can see a visible object's bounds with the command pobj .

If you have a composite object managing several children, you can arrange to have their positions determined automatically. This is discussed in Geometry Management .

MSG_VIS_SET_POSITION
void	MSG_VIS_SET_POSITION(
        int	xOrigin,
        int	yOrigin);

This message causes the object to set its VI_bounds to the new location specified, retaining its current size. The left bound ( VI_bounds . R_left ) is set to the value of xOrigin , and the top bound ( VI_bounds . R_top ) is set to that of yOrigin . The right and bottom bounds are set to match the new origin and the width and height of the object.

Source: Unrestricted--usually the visible parent or geometry manager.

Destination: Any visible object.

Parameters: xOrigin New horizontal coordinate of the object's position.

yOrigin
New vertical coordinate of the object's position.

Return: Nothing.

Interception: Unlikely. It's much more likely that the object will subclass MSG_VIS_POSITION_BRANCH .

Warnings: This message will not cause an image invalidation. You must mark the object invalid with MSG_VIS_MARK_INVALID .

See Also: MSG_VIS_POSITION_BRANCH , MSG_VIS_MARK_INVALID .

MSG_VIS_GET_POSITION
XYValueAsDWord MSG_VIS_GET_POSITION();

This message returns the current origin of the object. The origin returned consists of the left and top object bounds ( VI_bounds . R_left and VI_bounds . R_top , respectively).

Source: Unrestricted.

Destination: Any visible object.

Parameters: None.

Return: A dword value representing the left bound in the low word and the top bound in the high word. Use the macros DWORD_X and DWORD_Y to extract the bounds from the return value (see the file graphics.h ).

Interception: Unlikely.

MSG_VIS_GET_BOUNDS
void	MSG_VIS_GET_BOUNDS(
        Rectangle *retValue);

This message returns the current rectangle structure stored in the object's VI_bounds field. This rectangle represents both the position and the size of the object by giving its left, top, right, and bottom bounds.

Source: Unrestricted.

Destination: Any visible object.

Parameters: retValue A pointer to an empty Rectangle structure. The values in the object's VI_bounds field will be returned in the structure pointed to.

Return: The retValue pointer will be returned with the bounds filled into the Rectangle structure.

Interception: Unlikely.

Object Size

MSG_VIS_GET_SIZE, MSG_VIS_SET_SIZE

A visible object's size is defined as its width and height. The width is the difference between the object's left and right bounds; the height is the difference between the object's top and bottom bounds.

You can retrieve a visible object's size with the message MSG_VIS_GET_SIZE , and you can set it with MSG_VIS_SET_SIZE . MSG_VIS_SET_SIZE will set the object's right and bottom bounds without recalculating the object's position. If geometry is being managed automatically, you may want to subclass other messages; these are described in Geometry Management .

In Swat, you can see the object's size indirectly with the command pobj . This command will show, among other things, the VI_bounds field of the object. You can manually calculate the size from that information.

MSG_VIS_SET_SIZE
void	MSG_VIS_SET_SIZE(
        int	width,
        int	height);

This message sets the object's height and width to the passed values, retaining the object's current position. The object's position will not be recalculated as a result of the size change.

Source: Unrestricted--usually the geometry manager or the object's visible parent calculating geometry.

Destination: Any visible object.

Parameters: width The new width of the object. width will be added to the object's left bound to get the object's new right bound.

height
The new height of the object. height will be added to the object's top bound to get the object's new bottom bound.

Return: Nothing.

Interception: Unlikely.

Warnings: This message will not cause an image invalidation. You must mark the object invalid with MSG_VIS_MARK_INVALID .

MSG_VIS_GET_SIZE
SizeAsDWord MSG_VIS_GET_SIZE();

This message returns the current size (width and height) of the object. Both values are word-sized integers and are returned in a single dword value. The high word of this value is the object's width, and the low word is the object's height.

Source: Unrestricted.

Destination: Any visible object.

Parameters: None.

Return: A dword value representing the object's width in the low word and height in the high word. Use the macros DWORD_WIDTH and DWORD_HEIGHT to extract the width and height; see DWORD_HEIGHT .

Interception: Unlikely.

Geometry Management

MSG_VIS_UPDATE_GEOMETRY, MSG_VIS_NOTIFY_GEOMETRY_VALID, MSG_VIS_RECALC_SIZE, MSG_VIS_GET_CENTER, MSG_VIS_RECALC_SIZE_AND_INVAL_IF_NEEDED, MSG_VIS_RESET_TO_INITIAL_SIZE, MSG_VIS_POSITION_BRANCH, MSG_VIS_POSITION_AND_INVAL_IF_NEEDED, MSG_VIS_INVAL_ALL_GEOMETRY

Geometry management consists of positioning and sizing visible objects properly. This can be done manually by the programmer or automatically by GEOS. If you wish to manually control your visible tree's geometry, set the flag VCGA_CUSTOM_MANAGE_CHILDREN in the top composite's VCI_geoAttrs field (see the VisComp chapter). You can then use any messages, either native to VisClass or defined by your subclasses, for object positioning and sizing.

Automatic geometry management occurs when the geometry of the visible tree is marked invalid somewhere and a visual update is initiated. The tree's geometry can be marked invalid in several ways--an object can be added to or removed from the tree, an object can be manually marked invalid (with MSG_VIS_MARK_INVALID ), or an object can be opened or closed ( MSG_VIS_OPEN or MSG_VIS_CLOSE ).

When automatic geometry management is invoked, the geometry manager will recalculate the entire affected branch's geometry. It will calculate geometry for each affected window group, from the topmost affected window object down to the bottommost affected object. If the geometry manager encounters window groups below the topmost window, it will check first to see if the lower window's size is controlled by its children (e.g., a GenView that follows the geometry of its content). If the lower window is scrollable or otherwise not subject to its children's geometry, the geometry manager will finish calculating the geometry above the lower window before proceeding.

Calculating geometry is an involved, complex task. The process involves composites and their children negotiating on desired sizes. How the geometry is calculated depends primarily on the relationships between the objects and how their flags are set.

For example, if a GenView is set up to be exactly the same size as its VisContent object, the calculations will be different than if the GenView were scrollable. The same is true for visible composites that manage their children: If the composite resizes itself large enough to hold all its children, the children must be resized first.

The negotiation between parent and child in the visible tree takes the form of a single message: MSG_VIS_RECALC_SIZE . This is the single most important message in calculating the tree's geometry, though others are also significant. This message is sent by a composite to each of its children in turn; it passes a suggested size, and the child returns its desired size.

The composite collects all the desired sizes of its children and compares that to the size it thinks it should be. If necessary, it makes another pass through the children, or it passes on its desired size to its parent composite. Depending on the situation and the disparity between parent and children sizes, the geometry calculation may take a single pass or several. If the sizes can not be resolved through this negotiation, the geometry manager will make a decision after several passes, typically expanding the parent as much as possible to fit all the children.

After a visible object has calculated its size, its size will be set either by itself or by its parent. Often this is done with MSG_VIS_SET_SIZE . Once all sizes have been determined and set, the objects set their positions with MSG_VIS_POSITION_BRANCH , which propagates down the tree. It sets the position of the topmost composite and then sets each of its children's positions appropriately.

When geometry calculations are complete, the geometry manager will send a MSG_VIS_NOTIFY_GEOMETRY_VALID to all visible objects with the flag VGA_NOTIFY_GEOMETRY_VALID set in its VI_geoAttrs field.

Several other messages may be used during geometry updates. Some of them may be intercepted to alter the behavior of the default method. These messages are listed below. Others can be used to invalidate a visible object's geometry or to cause a geometry update on the tree.

MSG_VIS_UPDATE_GEOMETRY
This message is typically called by the UI during a window group update. It calculates the geometry of the entire branch receiving it. It's not likely you'll subclass this method--altering its behavior can be used for certain optimizations, however.
MSG_VIS_NOTIFY_GEOMETRY_VALID
This message will be sent to an object when all its geometry messages have been handled and its geometry has been fully resolved (at least for the moment). This message will be sent only if VGA_NOTIFY_GEOMETRY_VALID is set in the object's VI_geoAttrs field. There is no default behavior for this message; it should be intercepted only if the object needs to do calculations after the geometry has settled down.
MSG_VIS_RECALC_SIZE
This message is normally sent by the geometry manager to a composite object. The composite is expected to send it to all its children; the children will then determine their sizes, and the composite will figure the total size and return. A composite that manages its own children can also use this message to query its children for their desired sizes.
MSG_VIS_RECALC_SIZE_AND_INVAL_IF_NEEDED
This message is an optimized MSG_VIS_RECALC_SIZE . Composites should use this version when sending a recalculation message to their children. This message only causes recalculation if needed and will invalidate the object if the size gets recalculated. It invalidates the object's image at its old bounds and only invalidates its geometry if the bounds change. It is highly unlikely that you'll subclass this method.
MSG_VIS_GET_CENTER
This message returns the amount of space on each side the object wants reserved for itself. It does not, as the name suggests, return the coordinates of the object's center. Instead, it returns the distance in each direction the object requires (you may think of it as external margins).
MSG_VIS_RESET_TO_INITIAL_SIZE
This message propagates down the visible tree and causes every object that receives it to reset itself to its original size. It does this by clearing VGA_GEOMETRY_CALCULATED in the object's VI_geoAttrs field, causing the object to believe it is being put on-screen for the first time. This message will also set the object's flags to indicate that its geometry has become invalid. It's highly unlikely your application will subclass this method.
MSG_VIS_POSITION_BRANCH
This message is sent by the geometry manager when an entire visible branch should be repositioned. If an application wants to do something special, it should subclass the handler for this message. Typically, though, only subclasses of VisCompClass and perhaps VisContentClass will change this method.
MSG_VIS_POSITION_AND_INVAL_IF_NEEDED
This message is an optimized MSG_VIS_POSITION_BRANCH that causes a branch reposition only if it's necessary. It will also invalidate both the object's and the parent's original images and, if the position changes, the geometry as well. It is highly unlikely that you'll subclass this method.
MSG_VIS_INVAL_ALL_GEOMETRY
This message invalidates all geometry under the recipient object, forcing invalidation of all children. This is a brute-force message used only when absolutely necessary.
MSG_VIS_UPDATE_GEOMETRY
void	MSG_VIS_UPDATE_GEOMETRY();

This message is sent during a visual update to cause objects to recalculate their geometry. Applications can use this message to ensure that geometry gets updated for a visual branch even if the branch is not currently drawn on the screen.

Source: Visual update mechanism.

Destination: Any visible object.

Interception: May be intercepted for optimizations. A thorough knowledge of the geometry update mechanism and the specifics of the visible tree's geometry is strongly recommended, however.

MSG_VIS_NOTIFY_GEOMETRY_VALID
void	MSG_VIS_NOTIFY_GEOMETRY_VALID();

This message is sent by the geometry manager to objects that have VGA_NOTIFY_GEOMETRY_VALID set. The message is sent only after all geometry messages have been handled for the object. It has no default behavior.

Source: Visible update mechanism.

Destination: Any visible object--typically a child of the object sending the message.

Interception: This message has no default behavior. It should be intercepted by any object that needs to do something after its geometry has been made valid.

MSG_VIS_RECALC_SIZE
SizeAsDWord MSG_VIS_RECALC_SIZE(
        int	width,
        int	height);

This message takes the suggested height and width of the object and recalculates the object's desired size based on them. Composite objects are expected to pass this message to their children and then calculate their sizes based on those of their children. The default behavior of the handler for this is to return the current size no matter what the suggested size is.

Source: Visible update mechanism.

Destination: Any visible object.

Parameters: width Suggested new width of the object as determined by the object's parent.

height
Suggested new height of the object as determined by the object's parent.

Return: A dword value representing the object's desired size as calculated by the object. The high word of the return value represents the width and the low word the height. Use the macros DWORD_WIDTH and DWORD_HEIGHT to extract the width and height; see DWORD_HEIGHT .

Interception: Default behavior is to return the object's current size no matter what the passed width and height are. Any object that wants special sizing behavior based either on the object's children or on the suggested size should subclass the method.

MSG_VIS_RECALC_SIZE_AND_INVAL_IF_NEEDED
SizeAsDWord MSG_VIS_RECALC_SIZE_AND_INVAL_IF_NEEDED(
        int	width,
        int	height);

This message is the same as MSG_VIS_CALC_RESIZE except that it is somewhat optimized. It will recalculate only if certain flags are set for the object, and it will mark the geometry invalid only if recalculation is done.

Source: Visible update mechanism.

Destination: Any visible object.

Parameters: width Suggested new width of the object as determined by the object's parent.

height
Suggested new height of the object as determined by the object's parent.

Return: A dword value representing the object's desired size as calculated by the object. The low word of the return value represents the width and the high word the height.

Interception: Unlikely--Default behavior is to return the object's current size no matter what the passed width and height are. Any object that wants special sizing behavior based either on the object's children or on the suggested size should subclass the method.

MSG_VIS_GET_CENTER
void	MSG_VIS_GET_CENTER(
        GetCenterParams *retValue);

This message returns the amount of space needed by the object in each direction from its center. It does not, as the name suggests, return the coordinates of the center of the object. Instead, the object must determine the amount of space it requires from its center to each edge for geometry management and return them in the structure passed.

Source: Visible update mechanism.

Destination: Any visible object

Parameters: retValue A pointer to an empty GetCenterParams structure, defined below.

Return: The method must fill the GetCenterParams structure pointed to by the retValue parameter.

Interception: Any object that wants to effect a different centering than the default should subclass this method. For example, if a set of objects can be overlapped, or if they should have extra space between them, they should subclass this message and alter the returned values appropriately.

Structures: The GetCenterParams structure is defined as follows:

typedef struct {
    word   GCP_aboveCenter;    /* space above */
    word   GCP_belowCenter;    /* space below */
    word   GCP_leftOfCenter;   /* space left */
    word   GCP_rightOfCenter;  /* space right */
}  GetCenterParams;
MSG_VIS_RESET_TO_INITIAL_SIZE
void	MSG_VIS_RESET_TO_INITIAL_SIZE(
        VisUpdateMode updateMode);

This message propagates down an entire visual branch, causing the objects in it to reset their sizes to their original width and height. First it invalidates the object's geometry, then it recalculates the geometry starting at the top.

Source: Visible update mechanism.

Destination: Any visible object.

Parameters: updateMode A VisUpdateMode indicating when the visual update of the tree should occur.

Return: Nothing.

Interception: Unlikely.

MSG_VIS_POSITION_BRANCH
void	MSG_VIS_POSITION_BRANCH(
        word	xOrigin,
        word	yOrigin);

This message repositions an entire visible branch. It propagates down the branch, causing each visible object to reposition itself based on the new origins passed. Composites must pass the appropriate altered positions to their children.

Source: Visible update mechanism.

Destination: Any visible object.

Parameters: xOrigin The new horizontal position of the object relative to the document or window it's in. This value is set into the object's VI_bounds . R_left bound, and the right bound is set according to the object's size.

yOrigin
The new vertical position of the object relative to the document or window it's in. This value is set into the object's VI_bounds . R_top bound, and the bottom bound is set according to the object's size.

Return: Nothing.

Interception: The default behavior is to set the object to the passed position. Composite objects that want to position their children a special way should subclass this (and not call the superclass) method. Non-composites will rarely, if ever, subclass this message.

MSG_VIS_POSITION_AND_INVAL_IF_NEEDED
Boolean	MSG_VIS_POSITION_AND_INVAL_IF_NEEDED(
        word	xPosition,
        word	yPosition);

This message is an optimized version of MSG_VIS_POSITION_BRANCH . It repositions the branch only if necessary and will mark the image invalid only if the position was changed. It returns a flag indicating whether or not the object was repositioned.

Source: Visible update mechanism.

Destination: Any visible object--typically sent by a composite to its children.

Parameters: xPosition The new horizontal position of the object relative to the document or window it's in. A negative value means that the object should choose its own position as best it can.

yPosition
The new vertical position of the object relative to the document or window it's in. A negative value means that the object should choose its own position as best it can.

Return: If the object was repositioned, the message will return true . Otherwise, it will return false .

Interception: Unlikely.

MSG_VIS_INVAL_ALL_GEOMETRY
void	 MSG_VIS_INVAL_ALL_GEOMETRY(
        VisUpdateMode		updateMode);

This is a brute-force, desperation message used to invalidate all geometry in the recipient's tree; that is, all geometry of the recipient and all objects under it in the tree will be invalidated. You should only use this message when the whole tree should be recalculated.

Source: Unrestricted.

Destination: The object requiring geometry recalculation.

Parameters: updateMode A visual update mode indicating when the visual update of the recalculation should occur.

Return: Nothing.

Interception: Generally not intercepted.

VisClass: 4.4 Using VisClass: Handling Input

Input events, what they mean, and how they are generated are discussed fully in the Input chapter. The visible world has some default behavior for handling input messages such as MSG_META_START_SELECT , MSG_META_END_SELECT , and MSG_META_PTR . For the most part, however, visible objects that want to respond to input must subclass and handle the input messages that interest them.

Composite objects will automatically pass mouse messages on to the proper child directly under the mouse pointer. Non-composite objects (direct subclasses of VisClass ) have no special default behavior for input handling.

Note that VisClass does not intelligently handle layering of objects. That is, if one object is visibly on top of another object, it will not necessarily receive the input events--the events may be passed to the partially hidden object. This is due to the order in which drawing and mouse events are handled by a visible object's children.

Take the example of a visible composite with two children. Each child draws a filled rectangle covering its bounds; the first child has a width and height of 40, and the second child has a width and height of 20. The composite's declaration shows

    ...
    VCI_comp = LargeChild, SmallChild;
    ...

Because drawing occurs in the order the children are listed, the small child will be drawn over the large child, as shown in the figure below. If each object can intercept and handle mouse events (e.g., MSG_META_START_SELECT ), clicks will never reach the small object without special behavior added to the composite. This is because input events are handled in the same order as drawing; the input event will be given to the first child whose bounds include the event's coordinates.

So, a click event on (10, 10), which is in the middle of the small square, would first be passed to the LargeChild object. The large child would see that the click was within its bounds, and it would take the event. The event would never be handled by the small object.

Therefore, if you need to have overlapping objects which handle user input, you will have to add special features for bounds detection. In the example of the two squares, if neither were to move, the composite could simply pass the event to the small child first and then to the large child, assuming the small child is always on top. If the objects could be moved forward and back so they hide each other, the composite (or the objects themselves) will need some other, more complex, detection behavior.

Mouse Events

MSG_META_PTR, MSG_META_START_SELECT, MSG_META_END_SELECT, MSG_META_DRAG_SELECT, MSG_META_DRAG, MSG_META_START_MOVE_COPY, MSG_META_DRAG_MOVE_COPY, MSG_META_START_OTHER, MSG_META_END_OTHER, MSG_META_DRAG_OTHER

Composite objects will automatically pass mouse messages on to the first child directly under the mouse pointer. Non-composite objects (direct subclasses of VisClass ) have no special default behavior for input handling.

Typically, a visible object that needs to handle mouse input will subclass MSG_META_START_SELECT (or one of the other press events such as MSG_META_START_MOVE_COPY ). This message is sent when the user presses on the "select" mouse button while the pointer is over the object. In this method, the object should grab the mouse (as shown below). When it has the mouse grab, it will receive MSG_META_PTR each time the mouse is moved; the object should subclass this message to provide the desired reaction to mouse moves. Finally, when the user releases the select button, the object will receive a MSG_META_END_SELECT . At this point, it should release the mouse grab.

Typically, mouse events will arrive with coordinates specified in 16 bits. This is the default graphics space, and it represents a coordinate system more than 25 yards on a side. Nearly all applications will find this graphic system big enough for their uses. However, if you need larger coordinates, you can use the large document space with 32-bit coordinates. To do this, you must set the GVA_WINDOW_COORDINATE_MOUSE_EVENTS in the GVI_attrs field of your GenView object and set up your visible objects correctly. For more information on supporting 32-bit graphic spaces with visible objects, see Visible Layers and 32-Bit Graphics .

Grabbing the Mouse
MSG_VIS_GRAB_MOUSE, MSG_VIS_FORCE_GRAB_MOUSE, MSG_VIS_GRAB_LARGE_MOUSE, MSG_VIS_FORCE_GRAB_LARGE_MOUSE, MSG_VIS_RELEASE_MOUSE

When an object "grabs the mouse," it requests that the UI send all future mouse events directly to that object. Normally, mouse events will travel down the visible object tree until they reach either the leaf object under the pointer or an object under the pointer that handles them. When an object has the mouse grab, the mouse events will go to the object with the grab regardless of the location of the pointer.

Typically, if an object wants to receive mouse events, it will subclass MSG_META_START_SELECT . This message is sent to the object when the pointer is over the object's bounds and the user presses the select button. In this handler, the object grabs the UI's gadget exclusive and then grabs the mouse. A good example of this can be found in the TicTac sample application's handler for MSG_META_START_SELECT .

After an object grabs the mouse, it receives all pointer and drag events that occur. These are primarily MSG_META_PTR messages, and the object must subclass MSG_META_PTR to achieve the proper behavior it wants. When the user lets go of the select button, the object will receive a MSG_META_END_SELECT . If it grabbed the mouse (which it probably did if it receives this message), it must relinquish both the gadget exclusive and its mouse grab.

An example of getting and releasing the mouse grab (taken from the TicTac sample application) is shown in Grabbing and Releasing the Mouse . The object subclasses these messages to let the user drag it around the screen.

The UI allows the visible object to get the normal mouse grab by using four different messages. Each of these is detailed below. If you're using a large document (32-bit coordinates), you will use the "large" mouse grabs. Otherwise, the normal mouse grab messages will do.

MSG_VIS_GRAB_MOUSE
void	MSG_VIS_GRAB_MOUSE();

A visible object sends itself this message (with @call ) when it wants to acquire the normal mouse grab. The message MSG_VIS_RELEASE_MOUSE must be used to relinquish the grab.

Source: Unrestricted.

Destination: Any visible object--typically sent by the object to itself.

Interception: Highly unlikely.

MSG_VIS_FORCE_GRAB_MOUSE
void	MSG_VIS_FORCE_GRAB_MOUSE();

A visible object sends itself this message (with @call ) when it wants to forcibly grab the mouse input. The object will acquire the mouse grab even if another object currently has it; the other object will receive a MSG_VIS_LOST_GADGET_EXCL when it loses the mouse by a forced grab. The object must release the grab later with MSG_VIS_RELEASE_MOUSE .

Source: Unrestricted.

Destination: Any visible object--typically sent by the object to itself.

Interception: Highly unlikely.

MSG_VIS_GRAB_LARGE_MOUSE
void	MSG_VIS_GRAB_LARGE_MOUSE();

A visible object sends itself this message (with @call ) when it wants to acquire the mouse grab and when it wants 32-bit coordinates. If the object wants 16-bit coordinates, it should use MSG_VIS_GRAB_MOUSE , above. The object must relinquish the mouse grab with MSG_VIS_RELEASE_MOUSE .

Source: Unrestricted.

Destination: Any visible object--typically sent by the object to itself.

Interception: Highly unlikely.

MSG_VIS_FORCE_GRAB_LARGE_MOUSE
void	MSG_VIS_FORCE_GRAB_LARGE_MOUSE();

A visible object sends itself this message (with @call ) when it wants to forcibly grab the mouse input and when it wants large (32-bit) input coordinates. If it wants normal (16-bit) coordinates, it should use MSG_VIS_FORCE_GRAB_MOUSE , above. The object will acquire the mouse grab even if another object currently has it; the other object will receive a MSG_VIS_LOST_GADGET_EXCL when it loses the mouse by a forced grab. The object must release the grab later with MSG_VIS_RELEASE_MOUSE .

Source: Unrestricted.

Destination: Any visible object--typically sent by the object to itself.

Interception: Highly unlikely.

MSG_VIS_VUP_ALTER_INPUT_FLOW
void	MSG_VIS_VUP_ALTER_INPUT_FLOW(@stack
        PointDWord		translation,
        WindowHandle		window,
        optr		object,
        word		grapTypeAndFlags);

This message is the primitive employed by the grab/release messages shown above. Objects will rarely use this unless they are large objects (using 32-bit coordinates). Large objects and composites should subclass this message. In the case of a mouse grab, they should add in their 32-bit offset amounts to the translation structure before passing the event on to their superclasses. This will result in large mouse events being properly translated for the duration of the mouse grab.

Source: Unrestricted.

Destination: Any visible object--typically a VisContent which uses a 32-bit graphics space for its children.

Parameters: translation The offsets to the origin of the "local" 16-bit graphics space.

window
The handle of the view window associated with this input flow alteration.
object
The optr of the object altering the input flow.
grabTypeAndFlags
The type and flags for the input flow grab affected by this alteration. The high byte contains a record of VisInputFlowGrabType , described below; the low byte contains a VisInputFlowGrabFlags record, also described below.

Return: Nothing.

Interception: Any VisContent object that uses a large graphics space should subclass this method. In the handler, it should apply the stated translation, then call its superclass. Each subsequent mouse event will have the translation applied before being passed on to the composite's children.

Structures: The types of VisInputFlowGrabType are listed below:

VIFGT_ACTIVE
The affected grab type is the active grab.
VIFGT_PRE_PASSIVE
The affected grab type is the pre-passive grab.
VIFGT_POST_PASSIVE
The affected grab type is the post-passive grab.
The flags of VisInputFlowGrabFlags are listed below:
VIFGF_NOT_HERE
Flag set to indicate that the message should not be handled in this method but rather passed up the tree with this flag cleared. It is used to distinguish between requests made by other objects and requests made by an object sent to itself. It also allows an object to redirect the message to another node not in the visible hierarchy by handling the message and passing it in a direction other than up the visible tree. This overrides all the other flags in this record.
VIFGF_FORCE
Flag set if the grab should be forced. Note that both VIFGF_GRAB and VIFGT_ACTIVE must also be specified.
VIFGF_GRAB
Flag set if the grab is being made, clear if the grab is being released. In either case, object must be passed--if a release, the grab will not be released unless the objects match. Note also that only one object may have the active grab, but many objects may have either passive grab at a given time.
VIFGF_KBD
Flag set if action is a keyboard grab or release.
VIFGF_MOUSE
Flag set if action is a mouse grab or release.
VIFGF_LARGE
Flag set if large mouse events are affected by the flow alteration. VIFGF_MOUSE must also be set.
VIFGF_PTR
Flag set if mouse grab and pointer events are requested. If flag clear, only button-related events will be sent. VIFGF_MOUSE must also be set.
MSG_VIS_RELEASE_MOUSE
void	MSG_VIS_RELEASE_MOUSE();

A visible object sends itself this message if it has the mouse grab and wants to release it. Typically, this message will be called in the object's MSG_META_END_SELECT handler. It works for both large and small grabs.

Source: Any visible object.

Destination: Any visible object--typically sent by the object to itself.

Interception: Unlikely.

Code Display 15-2 Grabbing and Releasing the Mouse

/* This code display shows the  MSG_META_START_SELECT, MSG_META_END_SELECT,
 * and MSG_VIS_LOST_GADGET_EXCL handlers from the TicTac sample application.
 *
 * The sequence of input events and the messages they generate follow the
 * following basic patterns:
 * 1.	User presses select button
 *	MSG_META_START_SELECT generated, sent to object under mouse pointer
 *	If object under pointer is a game piece,
 *		Game piece grabs gadget exclusive
 *		Game piece grabs mouse
 * 2.	User holds button and drags mouse
 *	MSG_META_DRAG_SELECT generated, sent to object with mouse grab
 *		Game piece sets internal "dragging" flag
 *		Game piece draws outline
 *	MSG_META_PTR messages generated during drag, sent to object with mouse grab
 *		Game piece erases previous outline
 *		Game piece draws new outline at new pointer position
 * 3.	User releases button
 *	MSG_META_END_SELECT generated, sent to object with mouse grab
 *		Game piece releases gadget exclusive (MSG_VIS_RELEASE_GADGET_EXCL)
 *		MSG_VIS_LOST_GADGET_EXCL generated, sent to mouse grab (game piece)
 *		Game piece erases old outline, if any
 *		Game piece moves itself, erases old bounds, draws new bounds
 *		Game piece releases mouse grab (MSG_VIS_RELEASE_MOUSE)
 */
/***********************************************************************
 *		MSG_META_START_SELECT for TicTacPieceClass
 ***********************************************************************
 * SYNOPSIS:		Grabs the mouse and calls for future pointer events.
 * 		When the user clickes in the view, TicTacView will pass
 * 		the click event to TicTacBoard. Since TicTacBoardClass
 * 		does not intercept the event, VisContentClass passes
 * 		it on to its child object currently under the pointer.
 * PARAMETERS:
 * 	void	(MouseReturnParams *retVal,
 *		 word xPosition, word yPosition, word inputState);
 *
 * STRATEGY:		When the piece object receives this message, it means
 * 		it has been clicked on by the user and the mouse button
 * 		is still down. The piece must grab the mouse so that it
 * 		gets all future mouse events, and it must request that
 * 		all future mouse events be sent to it. This ensures
 * 		that if the pointer leaves the object's bounds while
 * 		the button is still pressed, the piece object will still
 * 		receive all the pointer events (otherwise they would be
 * 		sent to whatever object was under the new pointer position).
 ***********************************************************************/
@method TicTacPieceClass, MSG_META_START_SELECT {
    /* Grab the gadget exclusive so we're allowed to grab the mouse. */
    @call @visParent::MSG_VIS_TAKE_GADGET_EXCL(oself);
    /* Grab the mouse. This requests that all future pointer
     * events be passed directly to this game piece. */
    @call self::MSG_VIS_GRAB_MOUSE();
    /* Finally, return that this particular click event has been processed.
     * If this flag is not returned, the system will send out the click
     * event again. */
    retVal->flags = MRF_PROCESSED;					 /* this event processed */
}
/***********************************************************************
 *		MSG_META_END_SELECT for TicTacPieceClass
 ***********************************************************************
 * SYNOPSIS:		This message is received when the selection button has
 * 		been released and this game piece had the mouse grab.
 * 		All it does is release the gadget exclusive.
 * PARAMETERS:
 * 	void	(MouseReturnParams *retVal,
 * 		 word xPosition, word yPosition, word inputState);
 ***********************************************************************/
@method TicTacPieceClass, MSG_META_END_SELECT {
    @call @visParent::MSG_VIS_RELEASE_GADGET_EXCL(oself);
    retVal->flags = MRF_PROCESSED;					/* this event processed */
}
/***********************************************************************
 *		MSG_VIS_LOST_GADGET_EXCL for TicTacPieceClass
 ***********************************************************************
 * SYNOPSIS:		This message is received when the selection button has
 * 		been released and this game piece had the mouse grab.
 * 		It first checks to see if the new, proposed bounds are
 * 		on the game board. If the bounds are valid, then
 * 		it sets the objects VI_bounds field to the new values
 * 		and causes the object to erase its original drawing
 * 		and draw itself at its new bounds. If the bounds are
 * 		not on the game board, it will retain the original bounds
 * 		and redraw using them.
 * PARAMETERS:		void ();
 ***********************************************************************/
@method TicTacPieceClass, MSG_VIS_LOST_GADGET_EXCL {
    WindowHandle win;				/* window handle of view window */
    GStateHandle gstate;				/* temporary gstate to draw to */
    /* See if piece was being dragged or not. If so, clear the outline. */
    if (pself->TTP_dragging) {
        /* Get the window handle of the view window and create a
         * temporary gstate for it to draw to. */
    win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW();
    gstate = GrCreateState(win);
        /* Set the line color and mode for drawing the outline. */
    GrSetLineColor(gstate, CF_INDEX, C_BLACK, 0, 0);
    GrSetMixMode(gstate, MM_INVERT);
        /* Erase outline on screen. */
        /* If the game piece type is TTPT_BOX, draw a rectangle
         * outline. Otherwise draw an ellipse outline. */
    if (pself->TTP_pieceType == TTPT_BOX) {
        GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
             ((pself->TTP_horizPos) + PIECE_WIDTH),
             ((pself->TTP_vertPos) + PIECE_HEIGHT));
    } else {
        GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
             ((pself->TTP_horizPos) + PIECE_WIDTH),
             ((pself->TTP_vertPos) + PIECE_HEIGHT));
    }
        /* Check to see if the new bounds are on the game board. If
         * they are, set the object's bounds to the new values. If
         * the are not, retain the original values and set the values
         * to those last stored. */
    if (@call TicTacBoard::MSG_TICTAC_VALIDATE_BOUNDS(
            ((pself->TTP_vertPos) + PIECE_HEIGHT),
            ((pself->TTP_horizPos) + PIECE_WIDTH),
            pself->TTP_vertPos, pself->TTP_horizPos)) {
        /* Invalidate the original drawing of the game piece. Send
         * the VI_bounds rectangle as the parameters because they have
         * not been changed since the START_SELECT. This message is
         * the equivalent of calling GrInvalRect() with the same bounds */
        @call self::MSG_VIS_BOUNDS_CHANGED(pself->VI_bounds.R_bottom,
                    pself->VI_bounds.R_right,
                    pself->VI_bounds.R_top,
                    pself->VI_bounds.R_left);
        /* Set the game piece object's bounds to the new coordinates. */
        pself->VI_bounds.R_left = pself->TTP_horizPos;
        pself->VI_bounds.R_right = (pself->TTP_horizPos) + PIECE_WIDTH;
        pself->VI_bounds.R_top = pself->TTP_vertPos;
        pself->VI_bounds.R_bottom = (pself->TTP_vertPos) + PIECE_HEIGHT;
    } else {
        /* If the bounds are not on the game board, then reset the
         * current positions to be the original bounds. */
        pself->TTP_horizPos = pself->VI_bounds.R_left;
        pself->TTP_vertPos = pself->VI_bounds.R_top;
    }
        /* Now, the game piece must draw itself at its newly-set
         * bounds (will draw itself over its original picture if the
         * new bounds were invalid). */
    @call self::MSG_VIS_DRAW(0, gstate);
        /* Finally, destroy the temporary gstate used for drawing. */
    GrDestroyState(gstate);
        /* Clear flag to indicate we are no longer dragging. */
    pself->TTP_dragging = FALSE;
    }
    /* Release the mouse grab now that the move has finished.
     * Other objects in the view (other game pieces, for example)
     * may now receive pointer, select, and drag events. */
    @call self::MSG_VIS_RELEASE_MOUSE();
}
Passive Grabs
MSG_VIS_ADD_BUTTON_PRE_PASSIVE, MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE, MSG_VIS_ADD_BUTTON_POST_PASSIVE, MSG_VIS_REMOVE_BUTTON_POST_PASSIVE

The type of mouse grab described in the previous section is also known as an "active" mouse grab. An object can also have two other types of mouse grabs: pre-passive and post-passive . While only one object may have the active mouse grab at any given time, any number of objects may have pre- or post-passive grabs on the mouse.

An object with a pre-passive mouse grab receives a copy of the input event before the event goes to its true destination. The copy will be a message of the form MSG_META_PRE_PASSIVE_... (e.g., if the event is MSG_META_PTR , the pre-passive event is MSG_META_PRE_PASSIVE_PTR ). All pre-passive events will be handled before the true event is delivered to the object with the active grab. If any of the pre-passive copies of the event is returned with the flag MRF_PREVENT_PASS_THROUGH, the active event will not be sent to its destination. You can use this feature to set up input filters, having an object receive all mouse events pre-passive and filtering out those that should not be delivered. The message MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE removes any pre-passive mouse grab from the object.

A post-passive mouse grab is similar to a pre-passive except that the post-passive object receives the event after it is sent to its normal destination. The post-passive object will receive a MSG_META_POST_PASSIVE_... with the same flags and position information as sent with the input event. An object that has the post-passive grab can release it by sending itself a MSG_VIS_REMOVE_BUTTON_POST_PASSIVE .

MSG_VIS_ADD_BUTTON_PRE_PASSIVE
void	MSG_VIS_ADD_BUTTON_PRE_PASSIVE();

A visible object sends itself this message (with @call ) when it wants to gain a pre-passive mouse grab. The object will receive all subsequent mouse events in the form of MSG_META_PRE_PASSIVE_... before the event's destination object receives them; this message will carry the same input information as passed with the input event. The object should handle MSG_META_PRE_PASSIVE_... and, if the event is not to be sent normally to its destination, should return the flag MRF_PREVENT_PASS_THROUGH from that handler. The pre-passive grab will be released when the object sends itself a MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE .

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself when it wants to gain a pre-passive mouse grab.

Interception: Unlikely--Note that the object must, however, intercept any MSG_META_PRE_PASSIVE... events it is interested in.

MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE
void	MSG_VIS_REMOVE_BUTTON_PRE_PASSIVE();

This message removes the pre-passive grab from a visible object.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself when it has a pre-passive grab and wants to release it.

Interception: Unlikely.

MSG_VIS_ADD_BUTTON_POST_PASSIVE
void	MSG_VIS_ADD_BUTTON_POST_PASSIVE();

A visible object sends itself this message (with @call ) when it wants to gain the post-passive mouse grab. The object will receive all subsequent mouse events in the form of MSG_META_POST_PASSIVE_... after the event's destination object receives them; this message will carry the same input information as passed with the input event. The object should handle the appropriate MSG_META_POST_PASSIVE_... to properly receive the events. The post-passive grab will be released when the object receives a MSG_VIS_REMOVE_BUTTON_POST_PASSIVE .

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself when it wants to receive post-passive input events.

Interception: Unlikely--Note that the object must, however, handle any MSG_META_POST_PASSIVE_... events it is interested in.

MSG_VIS_REMOVE_BUTTON_POST_PASSIVE
void	MSG_VIS_REMOVE_BUTTON_POST_PASSIVE();

This message removes any post-passive mouse grab from the object.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to release a post-passive mouse grab.

Interception: Unlikely.

The Gadget Exclusive and Focus Hierarchy
MSG_VIS_TAKE_GADGET_EXCL, MSG_VIS_RELEASE_GADGET_EXCL, MSG_VIS_LOST_GADGET_EXCL, MSG_VIS_VUP_QUERY_FOCUS_EXCL, MSG_VIS_FUP_QUERY_FOCUS_EXCL, MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER

The gadget exclusive is a marker in the system used by the UI to indicate which object has exclusive access to input gadget functionality. Only one gadget at a time may have the gadget exclusive, and the gadget exclusive may be forcibly taken from an object by another object (for example, when a system-modal dialog comes up during a drag operation).

At times, an object will force the mouse input to go to a different object from that which has the current active grab. This is done almost exclusively by the Specific UI library and is rarely, if ever, done by applications. Three messages can be used in determining which object has the current "gadget exclusive," or active mouse grab. These are shown below.

In addition, individual objects can get information about the current state of the Focus hierarchy with MSG_VIS_VUP_QUERY_FOCUS_EXCL and MSG_VIS_FUP_QUERY_FOCUS_EXCL , also shown below.

To deal with a quick-transfer operation, a visible object may also have to use the message MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER . This message may be used to remove the mouse grab from the GenView in which the visible object resides, thereby allowing mouse events to reach other objects outside the view window.

MSG_VIS_TAKE_GADGET_EXCL
void	MSG_VIS_TAKE_GADGET_EXCL(
        optr	child);

This message causes the passed object to be given the gadget exclusive. Any object currently having the gadget exclusive will subsequently receive a MSG_VIS_LOST_GADGET_EXCL . This is used primarily by the Specific UI.

Source: Unrestricted. Typically sent by a visible object that will next grab the mouse.

Destination: Any visible object--normally sent by an object to its visible parent.

Parameters: child The optr of the object to which the gadget exclusive will be given (normally the calling object).

Return: Nothing.

Interception: Unlikely.

See Also: MSG_VIS_GRAB_MOUSE , MSG_VIS_LOST_GADGET_EXCL

MSG_VIS_RELEASE_GADGET_EXCL
void	MSG_VIS_RELEASE_GADGET_EXCL(
        optr	child);

This message causes the passed object to release the gadget exclusive. The object specified by child will then receive a MSG_VIS_LOST_GADGET_EXCL .

Source: Unrestricted. Typically sent by an object that has a mouse grab and expects to release it.

Destination: Any visible object--normally sent by the object to it's parent.

Parameters: child The optr of the object which is to lose the gadget exclusive (normally the calling object).

Return: Nothing.

Interception: Unlikely.

See Also: MSG_VIS_RELEASE_MOUSE , MSG_VIS_LOST_GADGET_EXCL

MSG_VIS_LOST_GADGET_EXCL
void	MSG_VIS_LOST_GADGET_EXCL();

This message, when received, indicates that the recipient has lost its hold on the gadget exclusive. When an object receives this, it should release the active mouse grab, if appropriate.

Source: Unrestricted--typically sent by the UI.

Destination: The visible object losing the gadget exclusive.

Interception: Mouse grabs should be released in this handler.

MSG_VIS_VUP_QUERY_FOCUS_EXCL
void	MSG_VIS_VUP_QUERY_FOCUS_EXCL(
        ObjectAndGrabParams *retValue);

This message queries up the visible hierarchy to find the object having the current focus in the current window. The current window is taken to be the window in which the object receiving the message resides.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to find the focus optr in its window.

Parameters: retValue A pointer to an empty ObjectAndGrabParams structure, shown below.

Return: The ObjectAndGrabParams structure pointed to by retVal will be filled with the appropriate data.

Interception: Unlikely.

Structures: The structure of the ObjectAndGrabParams is shown below:

typedef struct {
    word		OAGP_grabFlags;  /* flags */
    word		OAGP_unused;     /* reserved */
    optr		OAGP_object;     /* object with focus */
}  ObjectAndGrabParams;

The OAGP_grabFlags field contains a record of HierarchicalGrabFlags

, defined in the Input chapter. The allowable flags are

HGF_SYS_EXCL
Indicates this object has the system exclusive.
HGF_APP_EXCL
Indicates this object has the application's exclusive.
HGF_GRAB
Indicates this object wishes to grab the exclusive (if clear, the object wishes to release), when passed as a parameter. In the structure, it merely indicates that the object in OAGP_object has the exclusive.
HGF_OTHER_INFO
Twelve bits used by the specific UI depending on the type of hierarchical grab.
MSG_VIS_FUP_QUERY_FOCUS_EXCL
void	MSG_VIS_FUP_QUERY_FOCUS_EXCL(
        ObjectAndGrabParams *retValue);

This message queries the focus hierarchy to see which object has the current focus. The current focus object does not have to be in the caller's visible tree.

Source: Unrestricted.

Destination: Any visible object.

Parameters: retValue A pointer to an empty ObjectAndGrabParams structure, shown above in the entry for MSG_VIS_VUP_QUERY_FOCUS_EXCL .

Return: The ObjectAndGrabParams structure pointed to by retVal will be filled with the appropriate data.

Interception: Unlikely. This message is not deliverd to all visible objects, rather only those marked as VTF_WIN_GROUP or VTF_IS_INPUT_NODE. If you are implementing a new focus node, and need this message, and are not a window group, you will need to set the VTF_IS_INPUT_NODE bit in your init routine.

Structures: The structure of the ObjectAndGrabParams is shown in the entry for MSG_VIS_VUP_QUERY_FOCUS_EXCL .

MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER
void	MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER();

This message is sent by a visible object to itself when a quick-transfer operation is underway and the pointer has to be allowed to leave the bounds of the object's view window. This message will travel up to the content object, which will indicate to the GenView that the pointer events must be allowed to go to other windows in the system.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself when the global transfer must be allowed outside its window.

Interception: Unlikely.

The Mou se Status
MSG_VIS_VUP_SET_MOUSE_INTERACTION_BOUNDS, MSG_VIS_VUP_GET_MOUSE_STATUS, MSG_VIS_VUP_TERMINATE_ACTIVE_MOUSE_FUNCTION, MSG_VIS_VUP_BUMP_MOUSE

The UI can control the mouse image and events as they occur. Because the UI is based on the visible classes, the messages that control the mouse status are also available to you, though they will probably be of very little utility. These messages are given below.

MSG_VIS_VUP_SET_MOUSE_INTERACTION_BOUNDS
void	MSG_VIS_VUP_SET_MOUSE_INTERACTION_BOUNDS(@stack
        int	bottom,
        int	right,
        int	top,
        int	left);

A visible object requesting the mouse grab sends this message up the visible tree. This message will be handled by a content object. It indicates a new set of draggable bounds in case the user initiates a drag-scrolling operation. This message is sent automatically when the visible object requests the grab.

Source: A visible object requesting a mouse grab.

Destination: The sender sends this message to itself.

Parameters: bottom, right, top, left
 The four bounds of the new bounding rectangle. Default is the bounds of the object sending the message.

Return: Nothing.

Interception: Unlikely.

MSG_VIS_VUP_GET_MOUSE_STATUS
word	MSG_VIS_VUP_GET_MOUSE_STATUS();

This message is rarely used by any objects outside the UI library. It returns the information passed with the last mouse event. The word return value contains two byte-sized sets of flags. The high byte is a list of the active UI functions, and the low byte represents the current button information.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself when it can not locally store the latest mouse information.

Parameters: None.

Return: A word containing two values: The high word is a record of type UIFunctionsActive , and the low word is a word of ButtonInfo .

Interception: Unlikely.

MSG_VIS_VUP_TERMINATE_ACTIVE_MOUSE_FUNCTION
void	MSG_VIS_VUP_TERMINATE_ACTIVE_MOUSE_FUNCTION();

This message is sent by a visible object to itself to terminate any active mouse function, forcing it to be a function of type "other." This message is used only by the Specific UI library in cases where input synchronization problems occur in specific places. Applications should generally not use this message.

Source: A visible object handling active input events.

Destination: Sent by the visible object to itself.

Interception: This message should not be subclassed.

MSG_VIS_VUP_BUMP_MOUSE
void	MSG_VIS_VUP_BUMP_MOUSE(
        int	xBump,
        int	yBump);

This message causes the UI to bump the pointer image on the screen by the passed offsets as if the user had moved the mouse. It's unlikely your objects will subclass this message; you may, however, use it to bump the mouse. It is most often used by specific UI objects (menus, scrollers) that make the mouse follow their movements.

Source: Unrestricted.

Destination: Any visible object.

Parameters: xBump The horizontal amount to bump the mouse.

yBump
The vertical amount to bump the mouse.

Return: Nothing.

Interception: Unlikely.

Keyboard Events

MSG_META_GRAB_KBD, MSG_META_FORCE_GRAB_KBD, MSG_META_RELEASE_KBD

The keyboard, like the mouse, may be grabbed by visible objects. Keyboard events arrive in the form MSG_META_KBD_CHAR ; a single message represents all different types of keyboard events (unlike mouse events, which can be much more diverse).

To grab the keyboard, the visible object should send itself the message MSG_META_GRAB_KBD . If the grab should be made in any circumstances, the object should use MSG_META_FORCE_GRAB_KBD . To release the keyboard grab, the object should send itself MSG_META_RELEASE_KBD .

MSG_META_GRAB_KBD
void	MSG_META_GRAB_KBD();

A visible object will send itself this message (using @call ) when it wants to gain the keyboard grab. To release the grab, it must send itself a MSG_META_RELEASE_KBD . If it wants to gain the exclusive regardless of whether another object has it, it should use MSG_META_FORCE_GRAB_KBD .

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to gain the keyboard grab.

Interception: Unlikely--Note that the object must, however, intercept MSG_META_KBD_CHAR to receive the subsequent keyboard events.

MSG_META_FORCE_GRAB_KBD
void	MSG_META_FORCE_GRAB_KBD();

A visible object will send itself this message (using @call ) when it wants to gain the keyboard grab whether or not another object currently has it. To release the grab, the object must later send itself MSG_META_RELEASE_KBD .

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to force a keyboard grab.

Interception: Unlikely.

MSG_META_RELEASE_KBD
void	MSG_META_RELEASE_KBD();

A visible object that has the keyboard grab must send itself this message to release the grab.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to release its keyboard grab.

Interception: Unlikely.

Ink and P en Events

MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK

If a visible object expects and wants Ink input, it should subclass the message MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK . This message queries the visible object, which should return specific values based on the current input state and object bounds.

This message has two default handlers; the default VisClass handler always returns false , indicating that the visible object does not expect Ink input. You can, however, substitute a second default handler for the VisClass handler by adding the following line to your Goc file after the object's class definition:

@method  VisObjectHandlesInkReply, <YourClass>,
                MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK;

This will, in essence, substitute the system-provided routine VisObjectHandlesInkReply() for a subclassed message handler. This routine will do the right thing for the object in interacting with the input manager. All subsequent input events will be considered Ink events.

MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK
void	MSG_VIS_QUERY_IF_OBJECT_HANDLES_INK(
        VisCallChildrenInBoundsFrame					*data);

This message is subclassed by objects that handle Ink input. See above for directions on handling this message. The parameters are five data words that may be modified and the bounds of a rectangle. This message will be sent to all objects whose bounds fall within the passed rectangle.

Source: Unrestricted.

Destination: Any visible object.

Parameters: data A pointer to a VisCallChildrenInBoundsFrame structure containing the bounding rectangle and five words of data. This structure is described below.

Return: Three fields of the VisCallChildrenInBoundsFrame structure pointed to by data will have meaning on return. See the Interception paragraph below to determine whether the return values should be set or not in a subclassed handler.

VCCIBF_data1
Memory handle of the top object desiring ink.
VCCIBF_data2
Chunk handle of the top object desiring ink.
VCCIBF_data3
Upper bound of the object desiring ink.

Interception: If an object wants to receive ink messages, it should do the following things in its handler for this message: First compare the passed VCCIBF_data3 value with the top bound of the object. Then, if the upper bound of the object is less than the VCCIBF_data3 value (or if the value in VCCIBF_data1 is zero), then the object should return the values indicated in the Return paragraph above. Otherwise, if these conditions are not met, it should return with the structures and registers unchanged.

Structures: The VisCallChildrenInBoundsFrame structure is shown below:

typedef struct {
    word		VCCIBF_data1;
    word		VCCIBF_data2;
    word		VCCIBF_data3;
    word		VCCIBF_data4;
    word		VCCIBF_data5;
        /* Five data words as described above */
    Rectangle		VCCIBF_bounds;
        /* bounds must be in the coordinate
         * system of the parent of the caller */
} VisCallChildrenInBoundsFrame;

VisClass: 5 Working with Visible Object Trees

Visible objects can not be used unless they are placed within object trees. The top object of the tree must be of a subclass of VisContentClass . Other objects in the tree can be of any visible object class. Keep in mind, however, that VisClass objects can not have children; only the composite classes ( VisCompClass and VisContentClass ) may have children. Therefore, VisClass is useful only for leaf objects.

Each visible object has a VI_link instance field. This field specifies the optr of that object's next sibling in the tree. Since this is a field of VisClass , both composites and noncomposites have it. Composite objects also have a VCI_comp field which contains the optr of the composite's first child. Thus, visible trees use the same format as normal object trees.

Creating and Destroying

Adding and Removing

Getting Visible Tree Information

Sending Messages Through the Tree

Visible Object Window Operations

VisClass: 5.1 Working with Visible Object Trees: Creating and Destroying

MSG_VIS_DESTROY

Visible objects are just like any other objects in the ways they are created and destroyed. You can create visible objects in many ways; the easiest is to set them up in a resource and simply load or duplicate the resource block at run-time. You can also use the ObjInstantiate() routine to instantiate new visible objects on the fly from your predefined subclass of VisClass , VisCompClass , or VisContentClass . the GEOS Programming chapter details how to instantiate a new object of a given class.

Destroying visible objects is very simple. You only need to use the message MSG_VIS_DESTROY , which works on a visible object and all its children (if it has any). This message removes the visible branch from the screen if necessary, detaches the visible objects from the visible tree, and then destroys the object (freeing its instance chunk). The only thing the object must do is clean up after itself--in its MSG_META_DETACH or MSG_META_FINAL_OBJ_FREE handler, it should free any memory that it allocated explicitly for its own use, for example.

MSG_VIS_DESTROY also marks the destroyed object's parent invalid both in geometry and image so the tree will be updated properly.

MSG_VIS_DESTROY
void	MSG_VIS_DESTROY(
        VisUpdateMode updateMode);

This message is the high level routine for destroying branches of visible trees. This message will close and destroy the entire branch, unlinking any visible objects in the branch under the recipient object. The parent of the removed branch will be marked invalid for later visual update. This message may be subclassed to replace the geometry update handling, but this is not easily done and is rare.

Source: Unrestricted.

Destination: Any visible object (or head of a visible branch).

Parameters: updateMode A VisUpdateMode that determines when the visible parent of the destroyed object/branch will be updated on the screen.

Return: Nothing.

Interception: Unlikely--may be subclassed if the parent is not to be marked invalid. This is extremely rare.

VisClass: 5.2 Working with Visible Object Trees: Adding and Removing

MSG_VIS_ADD_CHILD, MSG_VIS_REMOVE, MSG_VIS_REMOVE_CHILD, MSG_VIS_MOVE_CHILD, MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD, MSG_VIS_REMOVE_NON_DISCARDABLE_VM_CHILD, MSG_VIS_REMOVE_NON_DISCARDABLE

Once you have objects instantiated, you can connect them together into an object tree. If you know the structure of your tree beforehand, you can create the tree explicitly in your .goc file, just as you would create your generic object tree.

For many purposes, however, a dynamic object tree is much more useful. To use it, you must be able to add and remove objects easily as well as move them within the tree easily.

Adding and Removing Normally

To add an object to a branch of a visible tree, use the message MSG_VIS_ADD_CHILD . This message adds a visible object as the child of a composite; if the new child is a composite with its own children, the entire branch is added to the tree. You can add a child at any position in the composite's child list (e.g., as the first child, as the second child, as the last child, etc.), and you can mark the child dirty after addition if you want.

For example, if you have a composite visible object with two children that is currently unattached to any visible tree, you can attach it to a tree with MSG_VIS_ADD_CHILD . The figure above shows this process and the line of code used to add the composite as the second child of an object in the tree.

Note that the child in the example is added as child one. The child list of a composite is zero-based, so the first child is referred to as number zero, the second is number one, and so on. The last child may always be referred to as CCO_LAST; likewise, the first child may always be referred to as CCO_FIRST.

To remove an object or branch from a visible tree, you can use either MSG_VIS_REMOVE or MSG_VIS_REMOVE_CHILD . MSG_VIS_DESTROY may also be used, but only if the entire branch should be destroyed after being removed from the tree. Both MSG_VIS_REMOVE and MSG_VIS_REMOVE_CHILD detach the object from the visible tree and visually update the parent immediately. MSG_VIS_REMOVE , however, should be sent directly to the object being removed, while MSG_VIS_REMOVE_CHILD is sent to the parent of the object being removed. When removing a child from its parent, you can specify the child by position number (e.g., remove the last child or the first child).

You can move a child within its child list with MSG_VIS_MOVE_CHILD . This message is sent to the parent and simply moves the child within the parent's child list. To move a child from one parent to another, you must first remove it and then add it to the other parent with MSG_VIS_REMOVE_CHILD and MSG_VIS_ADD_CHILD . This message, as the others, will move the child's entire branch with it.

Adding and Removing Objects Not Saved to Documents

In many cases, you may save visible objects directly to VM documents using GenDocument objects and the document controllers. In cases like this, the objects you save to the file must not be discarded by the UI if the visible object is taken off the tree or removed. These objects are known as "non-discardable" visible objects stored in a VM file, and unless they're handled specially, they can cause your documents to crash your application.

Any top-level object you save directly to a file must be treated as non-discardable. That is, any object that gets dynamically added to or removed from a visible tree (such as a vis object that gets added as the child of a GenDocument when the document is opened) must be added, removed, and managed as a non-discardable object. There are three messages that deal specifically with non-discardable visible objects; these are detailed below and are MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD , MSG_VIS_REMOVE_NON_DISCARDABLE_VM_CHILD , and MSG_VIS_REMOVE_NON_DISCARDABLE .

MSG_VIS_ADD_CHILD
void	MSG_VIS_ADD_CHILD(
        optr		child,
        CompChildFlags		flags);

This message attaches the passed object as a child of the composite handling the message. If the parent is already opened and on the screen, you must invalidate the child with MSG_VIS_MARK_INVALID , passing the invalidation VOF_WINDOW_INVALID; the composite and child will be updated appropriately according to the VisUpdateMode passed with the invalidation. If, however, the parent is not opened, the child will automatically be opened when the parent is opened.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: child The optr of the visible object to be added as a child.

flags
A structure of CompChildFlags , described below.

Return: Nothing.

Interception: You should not subclass this message.

Structures: The CompChildFlags structure contains one one-bit flag and one 15-bit unsigned numerical field. These are described below and can be found in metaC.goh .

 typedef WordFlags  CompChildFlags;
#define CCF_MARK_DIRTY					0x8000
#define CCF_REFERENCE					0x7fff
#define CCO_FIRST					0x0000
#define CCO_LAST					0x7FFF
#define CCF_REFERENCE_OFFSET					     0
CCF_MARK_DIRTY
If this flag is set, the object will be marked dirty when added to the tree.
CCF_REFERENCE
The zero-based position of the new child. Other children will be shuffled forward in the child list as necessary. If greater than the number of children, it will be taken to be CCO_LAST.
Two special numbers may be used in the CCF_REFERENCE field:
CCO_FIRST
Add as the first child.
CCO_LAST
Add as the last child.

Warnings: Do not pass the optr of an object that is already the child of another object. The results are unpredictable and will likely result in an error.

MSG_VIS_REMOVE
void	MSG_VIS_REMOVE(
        VisUpdateMode updateMode);

This is the high-level message that closes (if necessary) and removes a visible branch from the object tree. The parent of the branch is marked invalid for visual update according to the passed VisUpdateMode .

Unlike MSG_VIS_DESTROY , this message does not destroy the visible branch but only unlinks it from the tree. This message may not be subclassed by any object. This message is useful for hiding visible branches which may be added again later with MSG_VIS_ADD_CHILD .

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: updateMode The VisUpdateMode used to update the composite from which the child is removed. VUM_MANUAL is not allowed.

Return: Nothing.

Interception: Do not subclass this message.

Warnings: This message will not allow VUM_MANUAL to be passed as the updateMode .

MSG_VIS_REMOVE_CHILD
void	MSG_VIS_REMOVE_CHILD(
        optr		child,
        CompChildFlags		flags);

This message removes the specified child from the object tree. This message should be rarely used and used with care; it does not close the visible branch but simply removes it. Consider using the higher-level MSG_VIS_REMOVE instead. Note that all grabs (focus, gadget, mouse, etc.) must be released by the child and all its children before the branch can be removed safely.

Source: Unrestricted.

Destination: The parent composite of the passed object.

Parameters: child The optr of the child to be removed. If this optr is not among the recipient's children, an error will likely occur.

flags
The CompChildFlags as described in MSG_VIS_ADD_CHILD on typedef WordFlags CompChildFlags; #define CCF_MARK_DIRTY 0x8000 #define CCF_REFERENCE 0x7fff .

Return: Nothing.

Interception: You should not subclass this message.

Warnings: Most likely you should use MSG_VIS_REMOVE instead of this message. MSG_VIS_REMOVE takes care of extra bookkeeping automatically that can be difficult.

MSG_VIS_MOVE_CHILD
void	MSG_VIS_MOVE_CHILD(
        optr		child,
        CompChildFlags		flags);

This message moves a child of the recipient to another location among its siblings. It essentially removes the child from the branch and then re-adds it in the same manner as MSG_VIS_ADD_CHILD . This message does not move the child from one parent to another; you must use a combination of MSG_VIS_REMOVE and MSG_VIS_ADD_CHILD to achieve that.

Source: Unrestricted.

Destination: The parent composite of the passed object.

Parameters: child The optr of the child to be removed. If this optr is not among the recipient's children, an error will likely occur.

flags
The CompChildFlags as described in MSG_VIS_ADD_CHILD on typedef WordFlags CompChildFlags; #define CCF_MARK_DIRTY 0x8000 #define CCF_REFERENCE 0x7fff .

Return: Nothing.

Interception: You should not subclass this message.

MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD
void	MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD(
        optr		child,
        CompChildFlags		flags);

This message performs exactly the same as MSG_VIS_ADD_CHILD except that it also increments the object's in-use count so it will never be discarded. This is used on top objects in visible sub-trees that are saved to document files: The object is added as a non-discardable file when the document is opened, and it is removed as a non-discardable child (with MSG_VIS_REMOVE_NON_DISCARDABLE_VM_CHILD or MSG_VIS_REMOVE_NON_DISCARDABLE ) when the document is closed.

Source: Unrestricted--typically a document object when opening the file.

Destination: The new parent of the visible object being added to the tree.

Parameters: child The optr of the new child being added to the tree.

flags
A record of CompChildFlags indicating where the child should be added. CCF_MARK_DIRTY is ignored in this record.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_REMOVE_NON_DISCARDABLE_VM_CHILD
void	MSG_VIS_REMOVE_NON_DISCARDABLE_VM_CHILD(
        optr	child);

This message performs exactly the same as MSG_VIS_REMOVE_CHILD except that it is used with MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD rather than with MSG_VIS_ADD_CHILD . This message decrements the in-use count before removing the child, thereby undoing the extra increment performed by MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD .

Source: Unrestricted--typically a document object when closing the file.

Destination: The parent of the visible object being removed from the tree.

Parameters: child The optr of the child being removed.

Return: Nothing.

Interception: Generally not intercepted.

MSG_VIS_REMOVE_NON_DISCARDABLE
void	MSG_VIS_REMOVE_NON_DISCARDABLE(
        VisUpdateMode		updateMode)

This message performs exactly the same as MSG_VIS_REMOVE except that it decrements the object's in-use count before removing it. This message should therefore be used in conjunction with the adding message MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD and should not be used with MSG_VIS_ADD_CHILD. Likewise, MSG_VIS_REMOVE should not be used with this MSG_VIS_ADD_NON_DISCARDABLE_VM_CHILD.

Source: Unrestricted--typically a document object when closing the file.

Destination: The visible object being removed from the tree.

Parameters: updateMode A VisUpdateMode value indicating when the object should be visibly removed from the tree. VUM_MANUAL is not allowed.

Return: Nothing.

Interception: Generally not intercepted.

VisClass: 5.3 Working with Visible Object Trees: Getting Visible Tree Information

 MSG_VIS_FIND_CHILD, MSG_VIS_FIND_CHILD_AT_POSITION, MSG_VIS_COUNT_CHILDREN, MSG_VIS_FIND_PARENT

When an application manages a visible object tree, it will most likely need to find children, count children, or get the optr of a particular object in the tree. VisClass offers four messages to provide this bookkeeping information. These four messages are listed below.

MSG_VIS_FIND_CHILD
word	MSG_VIS_FIND_CHILD(
        optr	object);

This message returns the zero-based position of the specified child among the parent's children.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: object The optr of the child to be found.

Return: The zero-based position of the child in the parent's child list. (E.g., the first child returns zero, the second child returns one, the third child returns two, etc.) If object is not the optr of one of the composite's children, the value -1 will be returned. If sent to a non-composite object, the message will also return -1.

Interception: Unlikely.

MSG_VIS_FIND_CHILD_AT_POSITION
optr	MSG_VIS_FIND_CHILD_AT_POSITION(
        word	position);

This message returns the optr of the child occupying the position specified among the recipient's children.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: position The zero-based position of the child whose optr is to be returned.

Return: The optr of the child occupying the passed position . If no child occupies that position, or if the message is sent to a non-composite object, the retuned value will be NullOptr.

Interception: Unlikely.

MSG_VIS_COUNT_CHILDREN
word	MSG_VIS_COUNT_CHILDREN();

This message returns the number of children the recipient object has. The count is not zero-based; if a composite has five children, this message will return five.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: None.

Return: The number of children the object has. If the object has no children (includes non-composite objects), the return will be zero.

Interception: Unlikely.

MSG_VIS_FIND_PARENT
optr	MSG_VIS_FIND_PARENT();

This message returns the optr of the recipient's parent composite.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: None.

Return: The optr of the object's parent composite. If the message is sent to the top node of a visible tree, the return value will be NullOptr.

Interception: Unlikely.

Tips: If you want to send a message to your parent, use the @visParent macro defined in the next section.

VisClass: 5.4 Working with Visible Object Trees: Sending Messages Through the Tree

Often an object will need to contact its parent or children but will not know their precise optrs. Other times, you may need to send a message to a particular object in the tree but don't know where in the tree the object is. GEOS provides several ways to dispatch messages to parents and children and even to objects of a given class.

Contacting Parents and Children Directly

 @visParent,  @visChildren, MSG_VIS_CALL_PARENT, MSG_VIS_SEND_TO_PARENT, MSG_VIS_SEND_TO_CHILDREN

To send a given message to an object's parent (to check valid bounds, to check the parent's state, etc.), you can use the macro @visParent . To do this, substitute the macro in place of the destination object's name, as follows:

kids = @call @visParent::MSG_VIS_COUNT_CHILDREN();

The above call is more efficient than using two message calls for the same thing, as follows:

myParent = @call self::MSG_VIS_FIND_PARENT();
kids = @call myParent::MSG_VIS_COUNT_CHILDREN();

Note that either @call or @send may be used with the @visParent macro.

A similar macro, @visChildren , is offered for sending messages to a composite's children. Note, however, that you can only use the @send keyword to send messages with this macro; there is no way for a single call to collect return values from many different objects. So, if you need to pass pointers to your children, you will have to get each child's optr in turn and dispatch the message to it individually. (Recall that pointers should not be passed with @send because they may be invalidated by the time the message is handled.) The format for using the @visChildren macro is as follows:

@send @visChildren::MSG_VIS_INVALIDATE();

This usage is much more efficient than counting the composite's children and using a loop to get the child's optr and send the child the message.

In addition to providing the above macros, VisClass lets you pass recorded events to a visible object's parent or children. This can be useful, for example, if you need to send a message to another object's parent; you might otherwise first have to find the optr of the object's parent and then send the message directly. Instead, you can use the VisClass messages MSG_VIS_CALL_PARENT and MSG_VIS_SEND_TO_PARENT to pass recorded (encapsulated) events to the recipient's parent.

To send a recorded event to a visible object's children use the message MSG_VIS_SEND_TO_CHILDREN . The recorded event will not be allowed to return anything for the same reason the @visChildren macro can not.

MSG_VIS_CALL_PARENT
void	MSG_VIS_CALL_PARENT(
        EventHandle event);

This message delivers the passed recorded event to the parent of the recipient object. It acts as a call, executing immediately even across threads.

Source: Unrestricted.

Destination: Any visible object.

Parameters: event The event handle of the prerecorded event to be delivered.

Return: Nothing.

Interception: Unlikely.

Tips: In Goc, an object should not send this message to itself; instead, it should use the @visParent macro described above.

MSG_VIS_SEND_TO_PARENT
void	MSG_VIS_SEND_TO_PARENT(
        EventHandle event);

This message delivers the passed recorded event to the parent of the recipient object. It acts as a send, allowing the caller to continue execution without waiting for the event to be handled.

Source: Unrestricted.

Destination: Any visible object.

Parameters: event The event handle of the recorded event to be delivered.

Return: Nothing.

Interception: Unlikely.

Tips: In Goc, an object should not send this message to itself; instead, it should use the @visParent macro described above.

MSG_VIS_SEND_TO_CHILDREN
void	MSG_VIS_SEND_TO_CHILDREN(
        EventHandle event);

This message delivers the passed recorded event to each of the recipient's children. It acts as a message send and can not return values; there is no way for a single message to call several objects and return values from each with a single call.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: event The event handle of the recorded event to be delivered.

Return: Nothing.

Structures: In Goc, an object should not send this message to itself; instead, it should use the @visChildren macro described above.

Warnings: Do not pass pointers in the recorded event's parameters because the message may not be handled before the pointers are invalidated.

Sending Classed Events Up the Tree

MSG_VIS_VUP_FIND_OBJECT_OF_CLASS, MSG_VIS_VUP_CALL_OBJECT_OF_CLASS, MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS, MSG_VIS_VUP_TEST_FOR_OBJECT_OF_CLASS

VisClass has several messages that automatically travel up the visible object tree until they get to an object that meets the proper criteria; they then deliver themselves to that object. This is accomplished by the default handler in VisClass recognizing whether the given object meets the criteria; if it does not, the handler passes the message up to the object's parent.

To find an object of a given class in the tree, use the message MSG_VIS_VUP_FIND_OBJECT_OF_CLASS . This message travels up the tree starting at the recipient until it reaches the first object of that class. So if you are looking for an object of VisCompClass , at the most the message will be passed up a single level.

If you need to send a message to an object of a given class in the visible tree, you can either find it and send it the message or send the message directly with the visible upward messages MSG_VIS_VUP_CALL_OBJECT_OF_CLASS and MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS . These messages search up the tree in the same was as MSG_VIS_VUP_FIND_OBJECT_OF_CLASS , and they then deliver the passed event to the resulting object.

To see simply whether an object of a given class exists in the visible tree, use MSG_VIS_VUP_TEST_FOR_OBJECT_OF_CLASS . This is useful if you are going to use MSG_VIS_VUP_CALL_OBJECT_OF_CLASS (or its counterpart). If no object of the given class exists, you can skip sending the recorded message.

To send classed messages to an object in the tree that is of a specific class, you can use TravelOption with certain MetaClass messages. VisClass defines a travel option TO_VIS_PARENT, which sends the message to the first object up the tree that is of the specified class.

MSG_VIS_VUP_FIND_OBJECT_OF_CLASS
optr	MSG_VIS_VUP_FIND_OBJECT_OF_CLASS(
        ClassStruct *class);

This message searches up the visible object tree until it encounters an object of the specified class. It then returns the optr of that object.

Source: Unrestricted.

Destination: Any visible object--typically sent by a visible object to itself to find an object of the given class above it in the visible object tree.

Parameters: class A pointer to the ClassStruct structure of the class to be searched for.

Return: The optr of the object of the passed class. If multiple objects of this class exist in the tree, the first encountered will be returned. If no objects of this class are in the tree, a NullOptr will be returned.

Interception: Unlikely--only objects that masquerade as objects of a different class would subclass this message. This practice is highly discouraged.

MSG_VIS_VUP_TEST_FOR_OBJECT_OF_CLASS
Boolean	MSG_VIS_VUP_TEST_FOR_OBJECT_OF_CLASS(
        ClassStruct *class);

This message searches up the visible tree and determines whether an object of the given class is in the tree.

Source: Unrestricted.

Destination: Any visible object--typically sent before MSG_VIS_VUP_CALL_OBJECT_OF_CLASS or MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS to ensure that a recipient for the message exists.

Parameters: class A pointer to the ClassStruct structure of the class to be searched for.

Return: True if the class is found; false if it is not.

Interception: Unlikely.

MSG_VIS_VUP_CALL_OBJECT_OF_CLASS
void	MSG_VIS_VUP_CALL_OBJECT_OF_CLASS(
        EventHandle event);

This message searches up the visible tree until it encounters an object of the proper class for the recorded event. The class is specified within the passed event . When the first such object is found, it will be called with the classed event as if an @call had been used with the event directly.

Source: Unrestricted.

Destination: Any visible object--The passed event will be delivered to the first object of the appropriate class, not necessarily to the recipient of the MSG_VIS_VUP_CALL_OBJECT_OF_CLASS .

Parameters: event The event handle of a recorded event.

Return: The classed event delivered by this message may return values as determined by the event. This message returns nothing.

Interception: Unlikely.

MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS
void	MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS(
        EventHandle event);

This message searches up the visible tree until it encounters an object of the proper class for the recorded event. The class is specified within the passed event . When the first such object is found, the message will be delivered to that object as if an @send had been used with the event directly.

Source: Unrestricted.

Destination: Any visible object--The passed event will be delivered to the first object of the appropriate class, not necessarily to the object that received the MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS .

Parameters: event The event handle of a recorded event.

Return: Nothing. The recorded event may not return anything.

Interception: Unlikely.

Warnings: The recorded event should not pass pointers among its parameters.

Visible Upward Queries

MSG_VIS_VUP_QUERY

You can create your own messages that get passed up the visible tree by creating aliases of MSG_VIS_VUP_QUERY . MSG_VIS_VUP_QUERY , by itself, does nothing useful; it simply gets passed up the visible tree without ever being handled.

You can create your own visible queries to return different types of values from objects higher up in the object tree. Take, for example, a music-teaching application which uses note objects as distant children of a sheet music object. If the note objects needed to know what key they were supposed to be in, they would query up the tree with a special upward query message. If we assume the sheet music object kept this information, it would want to respond to the query message with the appropriate value. Thus, the sheet music class would have a line similar to the following:

@alias(MSG_VIS_VUP_QUERY)
        MusicKeyType MSG_VIS_VUP_QUERY_MUSIC_KEY();

This line creates an alias for the general-purpose upward query. The sheet music object should have a handler for MSG_VIS_VUP_QUERY_MUSIC_KEY in which it figures out and returns the appropriate key. This type of upward query can be invaluable in many different situations.

MSG_VIS_VUP_QUERY
void	MSG_VIS_VUP_QUERY();

This message simply queries up the tree until it is handled. It is extensible so visible objects can implement their own upward queries without adding handlers to every class in between it and the query handler.

It is extremely rare that any object would send or handle MSG_VIS_VUP_QUERY on its own; many classes, however, may alias this message and create their own versions of the upward query.

Source: Unrestricted--see note above.

Destination: Any visible object--see note above. Typically sent by a visible object either to itself or directly to its visible parent.

Parameters: None--an alias of this message may have parameters.

Return: Nothing--an alias of this message may have return values.

Interception: MSG_VIS_VUP_QUERY itself should not be intercepted. Its aliases, however, should be intercepted by the appropriate classes that will handle them.

Sending Messages to Window Groups

MSG_VIS_VUP_CALL_WIN_GROUP, MSG_VIS_VUP_SEND_TO_WIN_GROUP

VisClass offers two specific messages for contacting window group objects in a visible tree. These are similar to MSG_VIS_VUP_CALL_OBJECT_OF_CLASS and MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS , except they find the window group of the recipient and deliver the event to the window object. These two messages are detailed below.

MSG_VIS_VUP_CALL_WIN_GROUP
void	MSG_VIS_VUP_CALL_WIN_GROUP(
        EventHandle event);

This message searches up the visible object tree until it encounters a window group object. When the first window group is found, it will be called with the classed event as if an @call had been used.

Source: Unrestricted--typically sent by a visible object to itself to deliver the event to its window.

Destination: Any visible object--the passed event will be delivered to a window object, not necessarily to the object that receives the MSG_VIS_VUP_CALL_WIN_GROUP .

Parameters: event The event handle of a recorded event to be delivered to the window object.

Return: The passed event may return its own values, but MSG_VIS_VUP_CALL_WIN_GROUP returns nothing.

Interception: Unlikely.

See Also: MSG_VIS_VUP_CALL_OBJECT_OF_CLASS.

MSG_VIS_VUP_SEND_TO_WIN_GROUP
void	MSG_VIS_VUP_SEND_TO_WIN_GROUP(
        EventHandle event);

This message searches up the visible object tree until it encounters a window group object. When the first window group is found, the passed event will be delivered to that object as if an @send had been used directly.

Source: Unrestricted--typically sent by a visible object to itself to deliver the event to its window.

Destination: Any visible object--the passed event will be delivered to a window object, not necessarily to the object that receives the MSG_VIS_VUP_CALL_WIN_GROUP .

Parameters: event The event handle of a recorded event to be delivered to the window object.

Return: Nothing. The recorded event may not return values.

Interception: Unlikely.

Warnings: The recorded event may not pass pointers among its parameters.

See Also: MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS.

VisClass: 5.5 Working with Visible Object Trees: Visible Object Window Operations

MSG_VIS_OPEN_WIN, MSG_VIS_CLOSE_WIN, MSG_VIS_WIN_ABOUT_TO_BE_CLOSED, MSG_VIS_MOVE_RESIZE_WIN

Typically, windows will be managed entirely by generic UI objects and the Specific UI library currently in use. You can, however, manage your own VisClass -based window objects by using messages normally used only by Specific UI objects. This is a difficult and complex task, however, and it is not recommended. The four messages used for this purpose are detailed below.

MSG_VIS_OPEN_WIN
void	MSG_VIS_OPEN_WIN(
        WindowHandle parentWindow);

This message is sent to a window or portal object to open its window. The graphics window will be opened and drawn on the screen, and a visual update will propagate down the displayed visible tree.

Source: Visual update mechanism.

Destination: A window group (VTF_IS_WINDOW) or portal (VTF_IS_PORTAL) object.

Parameters: parentWindow The window handle of the open window in which the new window will be created.

Return: Nothing.

Interception: The window group or portal must intercept in order to open the new graphics window and set initial parameters ( VCI_window ).

MSG_VIS_CLOSE_WIN
void	MSG_VIS_CLOSE_WIN();

This message is sent to a window or portal object that has its graphics window currently on the screen. The window and its visible branch will be closed (taken off the screen).

Source: Visual update mechanism.

Destination: A window group (VTF_IS_WINDOW) or portal (VTF_IS_PORTAL) object.

Interception: The window group or portal may intercept if more needs to be done than simply closing the graphics window.

MSG_VIS_WIN_ABOUT_TO_BE_CLOSED
void	MSG_VIS_WIN_ABOUT_TO_BE_CLOSED();

This message notifies the recipient that the window it's displayed in is about to be closed. The default handler for this will remove the visible branch of the window off the screen so no redraws will occur before the window closes.

Source: Visual update mechanism.

Destination: The window group (VTF_IS_WINDOW) or portal (VTF_IS_PORTAL) object that is being closed; the message will then propagate down the visible tree.

Interception: May be intercepted by either the window group or the VisContent object if the default behavior is not appropriate for the object.

MSG_VIS_MOVE_RESIZE_WIN
void	MSG_VIS_MOVE_RESIZE_WIN();

This message causes the window object to move and/or resize itself based on the bounds of its content object. This message is used only by window or portal objects and is rarely, if ever, used by objects in your applications.

Source: Part of the visual update mechanism--typically called by the window object on itself in its MSG_VIS_UPDATE_WINDOWS_AND_IMAGE handler.

Destination: The window group (VTF_IS_WINDOW) or portal (VTF_IS_PORTAL) object to be resized or moved.

Interception: The window object may intercept if it does not want to resize to the bounds of its content object.

VisClass: 6 Visible Layers and 32-Bit Graphics

MSG_VIS_LAYER_SET_DOC_BOUNDS, MSG_VIS_LAYER_GET_DOC_BOUNDS, MSG_VIS_LAYER_INSERT_OR_DELETE_SPACE

The GEOS graphic space is built on a basis of 16-bit coordinates. A few applications, however, will require much more space for their documents. GEOS also supports the special use of 32-bit coordinates; applications using these coordinates are said to be using the "large document" model.

The visible object library was originally designed with 16-bit coordinates in the default GEOS graphics space. All instance data, message parameters and return values, and coordinates are assumed to be word-sized integers. A large-document visible object library is not provided primarily because there is no single visible model that works well with large coordinates, and also because no single large model works efficiently for all applications.

It is possible, however, for applications to subclass the standard visible classes in order to get them to support the large document model. In order to do this, however, you must understand the basic issues involved with the large model and with using 32-bit coordinates.

As the application developer, you should read the following sections if you will be using the large document model. Each one deals with a different problem facing applications that want large documents.

In addition, some complex applications may offer several different visible layers in the program. For example, a spreadsheet might want to include a graphic object layer for graphics and charts. The content object of the visible tree can be set up to have several different children, each of which is a "layer" that manages its own objects in the window.

The content in charge of the layers must occasionally set and retrieve the document bounds of its children. For example, if the user wants to set the page size of the spreadsheet that has a graphical layer, the content object must set the new size for all its children. To retrieve the current document bounds of a particular object, send it a MSG_VIS_LAYER_GET_DOC_BOUNDS . To set the document bounds, send a MSG_VIS_LAYER_SET_DOC_BOUNDS . In addition, the application may add or delete space into a layer with MSG_VIS_LAYER_INSERT_OR_DELETE_SPACE ; for example, if a spreadsheet needs to resize a column, it must send this message to the graphic layer to ensure the layers handle the sizing properly.

MSG_VIS_LAYER_SET_DOC_BOUNDS
void	MSG_VIS_LAYER_SET_DOC_BOUNDS(@stack
        sdword	bottom,
        sdword	right,
        sdword	top,
        sdword	left);

This message sets the 32-bit document bounds for a particular layer object. This message is typically sent by a VisContent object to its children, which are assumed to be layer objects.

Source: Unrestricted--typically sent by a VisContent to its large children.

Destination: Any large (32-bit) visible object or composite.

Parameters: all The four parameters describe the new bounds of the large document. Since layer objects typically cover the entire document, the layer object will set its bounds to these values.

Return: Nothing.

Interception: All layer objects and large objects must subclass this message. There is no default handler.

MSG_VIS_LAYER_GET_DOC_BOUNDS
void	MSG_VIS_LAYER_GET_DOC_BOUNDS(
        RectDWord *bounds);

This message returns the 32-bit document bounds of a particular large visible object. Typically, a VisContent will query its large children with this message when it needs to know their bounds.

Source: Unrestricted--typically sent by a VisContent to its large children.

Destination: Any large (32-bit) visible object or composite.

Parameters: bounds A pointer to an empty RectDWord structure that describes a set of large bounds.

Return: The pointer to the filled RectDWord structure. The structure should, upon return, contain the 32-bit bounds of the large visible object.

Interception: All layer objects and large objects must subclass this message. There is no default handler.

Structures: The RectDWord structure is show below for convenience:

typedef struct {
    sdword   RD_left;
    sdword   RD_top;
    sdword   RD_right;
    sdword   RD_bottom;
}  RectDWord;
MSG_VIS_LAYER_INSERT_OR_DELETE_SPACE
void	MSG_VIS_LAYER_INSERT_OR_DELETE_SPACE(
        InsertDeleteSpaceTypes 			type,
        DWFixed 			spaceY,
        DWFixed 			spaceX,
        DWFixed 			positionY,
        DWFixed 			positionX)

This message should be sent by one layer to another when it changes sizes. An example would be a spreadsheet application with a graphic layer; when a column in the spreadsheet resizes, the graphic layer must resize accordingly.

Source: Any visible layer object.

Destination: The visible layer object requiring resizing.

Parameters: type InsertDeleteSpaceType specifying what sort of operation is taking place.

spaceX , spaceY
Change in space for the x and y directions.
positionX , positionY
Change in x and y position.

Return: Nothing.

Interception: This message must be intercepted; it has no default behavior.

Using Visible Document Layers

Using 16-Bit Drawing Commands

The 16-Bit Limit on Visual Bounds

Handling MSG_VIS_DRAW

Managing 32-Bit Geometry

Handling Mouse Events

Setting Up the Objects

VisClass: 6.1 Visible Layers and 32-Bit Graphics: Using Visible Document Layers

The first issue with large documents is the basic structure of the visible tree. The topmost object of a large visible tree must be able to keep track of information about the current translations and coordinates being used. Typically, the topmost object would be a subclass of VisContentClass ; this does not change with large trees, though another consideration has to be taken care of.

A content object should not contain any information about the document itself; the content object should not be stored along with other document information. Thus, the object that acts as the content can not be the object that also manages the 32-bit document space. Instead, it is easiest to use a VisCompClass object to manage the document space and to place this object as a child of the content.

In addition, you can set up several different composite children of the content, each one managing its own large document space. Each one of these composites is called a "layer object" and manages a "layer" of the document. This way, you can have several different layers (such as a spreadsheet layer and a graphic layer) in the same document. The layer object should manage the calculations and translations necessary for supporting the large document space.

VisClass: 6.2 Visible Layers and 32-Bit Graphics: Using 16-Bit Drawing Commands

All the GEOS drawing commands are based on 16-bit coordinates. Drawing may only occur within a 16-bit graphics space. The graphics system does, however, support extended translations of the 16-bit graphics space within 32-bit coordinates.

This means that you can think of the large document space as a number of smaller, 16-bit "local" graphics spaces. When you need to draw anything, you first use an extended translation to get near your drawing point in the large document. Then you can draw using 16-bit offsets from the point to which you translated.

The application using the extended translation can do so in two different ways: It can store a 32-bit translation with each visible object, or it can store a 32-bit translation in a visible composite, thereby making the entire visible branch under the composite in a 32-bit graphics space. Visible trees implementing layer objects must use the second option; the layer must contain the translation for the entire visible branch that it manages. If a simple large visible tree does not need layers, it may wish to use the first solution.

The first of the two solutions is simple but takes up additional memory and time; each object, when it draws, must translate the coordinates, draw, then translate back. Two translations for each object could significantly affect drawing performance unless visual updates are handled carefully.

The other solution, storing a translation in the composite object, requires just two translations for the entire branch headed by the composite. Whenever the composite receives MSG_VIS_DRAW or MSG_VIS_VUP_CREATE_GSTATE , it executes the translation on the GState. As long as the translation is always relative to the GState's current transformation (done using GrApplyTranslationDWord() ), and as long as the translation is undone before the GState is returned to the caller, drawing will be done properly.

VisClass: 6.3 Visible Layers and 32-Bit Graphics: The 16-Bit Limit on Visual Bounds

Individual visual objects have 16-bit bounds. If the rules of drawing in the 32-bit graphics space are followed, this is not a problem. Individual objects are constrained to 16-bit width and 16-bit height, however.

The only objects for which having 16-bit bounds is a problem are the tree's content object and all the layer objects. This is because these two types of objects must have width and height greater than 16 bits. This problem is handled differently for each object:

For the content object, the VI_bounds field is set to all zeros and is not used. Normally, the content uses its bounds to set the document size in the GenView; the responsibility for setting the GenView's document size then falls to the application.

For the layer object, the VI_bounds field is set to all zeros and is not used. If the bounds of the layer object are important to the application, the layer object must be given new instance fields to hold the 32-bit coordinates of the bounds. If not, nothing needs to be done; the bounds can simply be ignored.

VisClass: 6.4 Visible Layers and 32-Bit Graphics: Handling MSG_VIS_DRAW

In normal-sized visible trees, MSG_VIS_DRAW gets sent to all objects in the tree. This draws the entire visible tree, no matter what portions are visible on the screen. For large documents, this can quickly get unwieldy and inefficient.

A better solution is for the layer object of each particular layer to intercept MSG_VIS_DRAW and pass it on only to those objects in the affected portion of the document. For this to work, of course, the layer object must know something of the structure and composition of the visible branch below it. This is the primary reason the visible object library does not offer a standard visible layer class--in essence, it must be defined specifically for each application.

VisClass: 6.5 Visible Layers and 32-Bit Graphics: Managing 32-Bit Geometry

Because visible objects and composites maintain their 16-bit bounds in a large document, they can still use the 16-bit geometry manager. Layer objects, however, can not use the geometry manager. All geometry management on the layer level (child positioning, resizing, etc.) must be done by the layer object itself in conjunction with its child composites.

VisClass: 6.6 Visible Layers and 32-Bit Graphics: Handling Mouse Events

The GenView is built to support large documents, though its default is normal-sized documents. Thus, you can request the view to send large mouse events, which carry with them 32 bits of integer and 16 bits of fraction in each dimension. The visible classes have no default handlers for these messages, so your own classes will have to handle them appropriately.

The large mouse events are the equivalent of their 16-bit counterparts. They just carry different data to represent the coordinates of the pointer. All the mouse events, both large and normal, are described in the Input chapter.

Normal composite objects will not be affected by large mouse events; the composite will continue to send the event on to the proper child as if it were in a 16-bit document. Layer and content objects, however, must process these messages differently.

If no object within the visible tree has the mouse grabbed, the content of the tree must decide which layer object should receive the mouse events. How the content and layer objects negotiate this is up to the particular application; some applications may consider that one layer is "on top of" the others, and other applications may determine which layer is the most appropriate based on the event context. By default, the large mouse events are passed by VisContentClass straight on to the first layer child. If you want a more complex scheme, you will have to subclass VisContentClass and intercept these messages.

VisClass: 6.7 Visible Layers and 32-Bit Graphics: Setting Up the Objects

Finally, you should be aware of what attributes to set in both the GenView and the VisContent objects of your visible tree to support the large document. You could easily find this out by looking through the various attributes of those classes (and you are encouraged to do so), but the following guide is provided for convenience:

In the GenView object, set GVA_WINDOW_COORDINATE_MOUSE_EVENTS in the GVI_attrs instance data field. This will pass mouse events in terms of offsets from the upper-left corner of the view window rather than in absolute document coordinates. VisContentClass handles these events and automatically turns them into document coordinates.

In the VisContent, set VCNA_WINDOW_COORDINATE_MOUSE_EVENTS and VCNA_LARGE_DOCUMENT_MODEL in the VCNI_attrs field. The first of these tells the content that it will receive input events in window coordinates rather than in document coordinates. The second will have the effect of making the content object ignore its bounds field and geometry management. It will also tell the content that all its children are layer objects. Setting the large document model will cause the content to send all the following messages on to all its children rather than handle them directly: MSG_VIS_DRAW , MSG_VIS_CONTENT_VIEW_ORIGIN_CHANGED , MSG_VIS_CONTENT_VIEW_SCALE_FACTOR_CHANGED , and MSG_VIS_CONTENT_VIEW_SIZE_CHANGED .

You do not have to create all the objects you may need; be aware that the Graphic Object Library provides its own layer object that supports a 32-bit graphic object layer. The spreadsheet object also exports its own layer object.

Requirements of Layer Objects

Layer objects will need to handle MSG_VIS_LAYER_SET_DOC_BOUNDS , which will be sent to all layers when the document bounds change.

In essence the layer object's job is to isolate its children and their children from the fact that they are in a 32-bit document rather than a 16-bit document. In order to do this, layer objects must do several things:

  • Store the translation
    This is a set of 32-bit translation offsets. These offsets represent the current location, if any, where the layer is being drawn in the 32-bit coordinate space.
  • Intercept MSG_VIS_DRAW
     In its handler, the layer should apply any 32-bit translations (using GrApplyTranslationDWord() ) on the GState provided by MSG_VIS_DRAW . It should then intelligently select which of its children are affected by the drawing event and pass the message on to them with the translated GState. The handler should then untranslate the GState before returning.
  • Intercept MSG_VIS_VUP_CREATE_GSTATE
     In the handler, the layer must first call its superclass (to create the GState normally), then apply the necessary translation using GrApplyTranslationDWord() . It can then return the translated GState.
  • Intercept and handle large mouse events
    Each large mouse event that may be interesting to the layer must be handled by the layer object. This is to ensure that mouse events that occur when no object has the mouse grab get handled. If the layer has 16-bit visible children, it should translate the mouse event into small mouse events before passing it to its superclass. If the layer has 32-bit visible children, it should first translate both mouse coordinates and then decide which of its large children should receive the event.

Requirements of 32-bit Visible Objects

Large visible objects in the visible tree should be equipped to handle both large and small mouse events. This is because the parent object may have both 32-bit and 16-bit children. If it does, it will have to send out only small mouse events; otherwise, the 16-bit objects would be confused by the large mouse events.

Requirements of 16-bit Visible Objects

Because 16-bit objects are the norm and not the exception, there are few requirements for them. However, 16-bit objects in 32-bit documents must do one thing: they must prevent the system from allowing the user to select the object and then drag-scroll too far. To prevent this from happening, the visible object must call MSG_VIS_VUP_SET_MOUSE_INTERACTION_BOUNDS to set a temporary boundary on drag scrolling.

VisClass: 7 VisClass Error Checking

MSG_VIS_VUP_EC_ENSURE_WINDOW_NOT_REFERENCED, MSG_VIS_VUP_EC_ENSURE_OBJ_BLOCK_NOT_REFERENCED, MSG_VIS_VUP_EC_ENSURE_OD_NOT_REFERENCED

VisClass provides three messages you can use for special error checking. These three messages are provided especially to make sure that all objects being destroyed or removed will be destroyed cleanly. The default handlers for these messages should cause a fatal error if their criteria are not met.

MSG_VIS_VUP_EC_ENSURE_WINDOW_NOT_REFERENCED
void	MSG_VIS_VUP_EC_ENSURE_WINDOW_NOT_REFERENCED(
        WindowHandle window);

This message travels up the visible object tree, ensuring that the window handle passed is not stored anywhere that it might cause trouble. This message may be used when a window is being destroyed and its handle is about to be freed.

Source: Unrestricted.

Destination: Any visible object in the affected visible tree.

Parameters: window The handle of the window being destroyed.

Return: Nothing--if it returns, no error was encountered.

Interception: Do not intercept this message.

MSG_VIS_VUP_EC_ENSURE_OBJ_BLOCK_NOT_REFERENCED
void	MSG_VIS_VUP_EC_ENSURE_OBJ_BLOCK_NOT_REFERENCED(
        MemHandle objBlock);

This message travels up the visible object tree, ensuring that the memory handle (the handle of an object block) passed is not stored anywhere that it might cause trouble. This message may be used just before the object block's handle is freed to ensure that no stale references will remain.

Source: Unrestricted.

Destination: Any visible object in the affected visible tree.

Parameters: objBlock The handle of the block being freed.

Return: Nothing--if it returns, no error was encountered.

Interception: Do not intercept this message.

MSG_VIS_VUP_EC_ENSURE_OD_NOT_REFERENCED
void	MSG_VIS_VUP_EC_ENSURE_OD_NOT_REFERENCED(
        optr	object);

This message travels up the visible object tree, ensuring that the optr passed is not stored by any objects in the tree. This message may be sent when the object is being destroyed; any objects methods encountering a match between a stored optr and the passed optr should force a fatal error.

Source: Unrestricted.

Destination: Any visible object in the affected visible tree.

Parameters: object The handle of the object being destroyed.

Return: Nothing--if it returns, no error was encountered.

Interception: Do not intercept this message.

VisClass: 8 Creating Specific UIs

It is possible, using visible object classes, to create your own specific UIs. It is a complex and difficult task, however, and only very few developers will expect to do this. Documentation on creating a custom specific UI may appear either in a future release of this documentation or under separate cover. Contact Geoworks Developer Products for more information.

VisClass: 9 Basic Summary

Visible objects are involved in several basic operations, each of which has its own primary messages:

  • Drawing
    Vis objects must handle MSG_VIS_DRAW ( MSG_VIS_DRAW ) if they are to draw after their window has been exposed. Objects wishing to redraw themselves outside of exposure events can call MSG_VIS_VUP_CREATE_GSTATE ( MSG_VIS_VUP_CREATE_GSTATE ) to create a GState in their view windows for drawing.
  • Being added to an object tree
    When an object is added to a visible tree, it must then force a redraw of the tree. It can be added to the tree with MSG_VIS_ADD_CHILD ( MSG_VIS_ADD_CHILD ), and the tree can be updated with MSG_VIS_VUP_UPDATE_WIN_GROUP ( MSG_VIS_VUP_UPDATE_WIN_GROUP ).
  • Being removed from an object tree
    A visible object may be removed from a visible tree with MSG_VIS_REMOVE ( MSG_VIS_REMOVE ). This message will close the object (take it off the screen) and then segregate it from its parent.
  • Undergoing visual update
    The visual update mechanism is automatic for the most part. An object that requires visual update, however, can use MSG_VIS_MARK_INVALID ( MSG_VIS_MARK_INVALID ) in conjunction with MSG_VIS_VUP_UPDATE_WIN_GROUP ( MSG_VIS_VUP_UPDATE_WIN_GROUP ) to cause the update.
  • Geometry management
    Geometry management of visible objects can be as simple as setting a few flags in the instance data or as complex as manually setting position and size of the objects. For a full description of geometry management of visible objects, see Positioning Visible Objects .
  • Being Destroyed
    Visible objects may be destroyed even when they are part of a visible tree that is on the screen. MSG_VIS_DESTROY ( MSG_VIS_DESTROY ) first removes the object from the tree (with MSG_VIS_REMOVE ) and then destroys it (through the MetaClass detach and destroy mechanisms).
This document is a single-page version of a a multi-page document, suitable for easy printing.


VisComp

VisCompClass allows visible objects to have children. VisComp objects are the grouping elements within visible object trees.

If you need to have a visible object tree with several levels, or if you need to manage the geometry of visible objects, you will want to read this chapter. You should already be familiar with VisClass

1 VisCompClass Features
2 VisCompClass Instance Data
2.1 VCI_comp
2.2 VCI_gadgetExcl
2.3 VCI_window
2.4 VCI_geoAttrs
2.5 VCI_geoDimensionAttrs
2.6 Managing Instance Data
3 Using VisCompClass
3.1 Managing Geometry
3.2 Managing Graphic Windows

VisComp: 1 VisCompClass Features

Composite visible objects provide several features and functions that normal visible objects can not. VisCompClass has several instance data fields above and beyond those found in VisClass (since VisCompClass is a subclass of VisClass , it inherits all its instance data and methods). Although it only handles a few messages not defined in VisClass , it provides much more functionality. Some of the main features of composite objects are listed below:

  • They can have children.
    Normal visible objects can exist only as leaves of a visible object tree. VisCompClass can have children, allowing the tree to be built to any number of levels. See the VisClass chapterfor full information on visible trees.
  • They can manage their children's geometry.
    A composite's children can be managed arbitrarily by the composite, or the composite can use the geometry manager to automatically position and size its children. Since a composite can have other composites as its children, this sizing can descend recursively throughout the visible tree, making the entire tree's geometry completely self-managed. See Managing Geometry for information on geometry management.
  • They can create and manage their own windows.
    Composite objects can create their own graphics windows. Though this is not typically done (except in Specific UI libraries), it is possible. See Managing Graphic Windows for more information on windows.
  • They support other VisClass functions.
    VisCompClass inherits all the instance data fields and messages of VisClass . As such, it can do everything normal VisClass objects can do. See the VisClass chapterfor full information on VisClass .

VisComp: 2 VisCompClass Instance Data

As stated above, VisCompClass inherits all the instance data fields from VisClass . All of those fields may be set and reset as they could be for an object of that class. Composite objects also have four other instance fields, shown in VisCompClass Instance Fields .

Code Display 16-1 VisCompClass Instance Fields

/* The VisCompClass instance data fields are shown below and are discussed in
 * detail throughout the chapter. */
    /* VCI_comp
     * VCI_comp contains the link to the composite object's first child. */
    @instance @composite				 VCI_comp = VI_link;
    /* VCI_gadgetExcl
     * VCI_gadgetExcl is an optr to the object that currently has the gadget
     * exclusive. This field is rarely used directly by applications. */
    @instance optr				 VCI_gadgetExcl;
    /* VCI_window
     * VCI_window contains the window handle of the graphics window associated
     * with this object. This field is rarely accessed directly by
     * applications; it is set by the visual update mechanism. */
    @instance WindowHandle				 VCI_window = NullHandle;
    /* VCI_geoAttrs
     * VCI_geoAttrs is a record that defines some of the geometry management
     * information for the composite. None of its values are set by default;
     * the possible flags in this record are shown after the definition. */
    @instance VisCompGeoAttrs				 VCI_geoAttrs = 0;
    /* Possible flags:
     *	VCGA_ORIENT_CHILDREN_VERTICALLY					0x80
     *	VCGA_INCLUDE_ENDS_IN_CHILD_SPACING					0x40
     *	VCGA_ALLOW_CHILDREN_TO_WRAP					0x20
     *	VCGA_ONE_PASS_OPTIMIZATION					0x10
     *	VCGA_CUSTOM_MANAGE_CHILDREN					0x08
     *	VCGA_HAS_MINIMUM_SIZE					0x04
     *	VCGA_WRAP_AFTER_CHILD_COUNT					0x02
     *	VCGA_ONLY_DRAWS_IN_MARGINS 					0x01
     */
    /* VCI_geoDimensionAttrs
     * VCI_geoDimensionAttrs is a record that contains additional information
     * about the composite's geometry. This field contains two two-bit fields
     * among its other flags. The default settings are shown in the definition;
     * all possible flags and settings are shown following. */
    @instance  VisCompGeoDimensionAttrs					 VCI_geoDimensionAttrs = 0;
    /* Possible flags:
      * Width Justification flags (mutually exclusive):
     * VCGDA_WIDTH_JUSTIFICATION						0xc0
     *	WJ_LEFT_JUSTIFY_CHILDREN					0x00
     *	WJ_RIGHT_JUSTIFY_CHILDREN					0x40
     *	WJ_CENTER_CHILDREN_HORIZONTALLY					0x80
     *	WJ_FULL_JUSTIFY_CHILDREN_HORIZONTALLY					0xc0
     *
     * Height Justification flags (mutually exclusive):
     * VCGDA_HEIGHT_JUSTIFICATION 						0x0c
     *	HJ_TOP_JUSTIFY_CHILDREN					0x00
     *	HJ_BOTTOM_JUSTIFY_CHILDREN,					0x04
     *	HJ_CENTER_CHILDREN_VERTICALLY					0x08
     *	HJ_FULL_JUSTIFY_CHILDREN_VERTICALLY					0x0c
     *
     * Other flags:
     *	VCGDA_EXPAND_WIDTH_TO_FIT_PARENT					0x20
     *	VCGDA_DIVIDE_WIDTH_EQUALLY					0x10
     *	VCGDA_EXPAND_HEIGHT_TO_FIT_PARENT					0x02
     *	VCGDA_DIVIDE_HEIGHT_EQUALLY					0x01
     */
    @default  VI_typeFlags = VTF_IS_COMPOSITE;

VCI_comp

VCI_gadgetExcl

VCI_window

VCI_geoAttrs

VCI_geoDimensionAttrs

Managing Instance Data

VisComp: 2.1 VisCompClass Instance Data: VCI_comp

Normal visible objects have just one instance field for tree construction: the VI_link field that points to the object's next sibling. For an object to have children, it must also have an instance field declared with the keyword @composite ; this field, in VisCompClass , is VCI_comp . The composite field contains the optr of the object's first child; the link field contains a pointer to either the object's next sibling or its parent.

Applications should never access the VI_link or VCI_comp fields directly at run-time; instead, these fields can be altered or queried with the following messages (all defined in VisClass ):

MSG_VIS_ADD_CHILD
This message adds a new child object to the composite.
MSG_VIS_REMOVE_CHILD
This message removes a child from the composite.
MSG_VIS_MOVE_CHILD
This message moves a child within the composite's list of children.
MSG_VIS_GET_PARENT
This message returns a pointer to the object's parent.

Several other messages may also be used to change or query any visible object's VI_link and VCI_comp fields. These are discussed in the VisClass chapter.

VisComp: 2.2 VisCompClass Instance Data: VCI_gadgetExcl

Since the composite object must manage several other objects, it must also keep track of certain hierarchies used by the UI; the gadget exclusive is one of these. Within each branch of the visible object tree, only one visible object may have the gadget exclusive at a time. The gadget exclusive is kept track of via a path of pointers from the top of the tree down to the object having the exclusive.

The VCI_gadgetExcl field determines which child of the composite has the gadget exclusive. The child indicated in this field may or may not actually have the gadget exclusive for the entire visible tree; if the branch does not have the exclusive, neither will the child object. The gadget exclusive is similar to the other hierarchies of the input manager and acts the same way.

Applications never access the VCI_gadgetExcl field of a composite directly. Instead, this field may be set with the following three VisClass messages:

MSG_VIS_TAKE_GADGET_EXCL
This message causes a visible object to set itself as having the gadget exclusive. The object's parent composite will set its VCI_gadgetExcl field appropriately.
MSG_VIS_RELEASE_GADGET_EXCL
This message causes a visible object to relinquish its hold on the gadget exclusive. The object's parent composite will set its VCI_gadgetExcl field appropriately.
MSG_VIS_LOST_GADGET_EXCL
This message is sent to the object having the gadget exclusive for that branch when it has lost the exclusive.

The above messages are detailed in the VisClass chapter.

VisComp: 2.3 VisCompClass Instance Data: VCI_window

Every visible object has a window associated with it in which it will be drawn. Normal VisClass objects have no control over what window they're associated with; they must be in the same window as their composite parents. Composites may need to appear in different windows from their parents, however. Thus, VisCompClass has the VCI_window field, which contains the window handle of the window in which the composite's branch will be drawn.

Applications will not access this field directly. Typically, composites will be drawn in the window associated with their parent composites or contents. Sometimes, however, an application will want a visible branch to appear in a window different from that of the rest of the visible tree. The composite then must use other VisClass messages for creating and associating a window. This process is discussed in more detail in Managing Graphic Windows .

VisComp: 2.4 VisCompClass Instance Data: VCI_geoAttrs

The most-used feature of composite objects is their ability to manage the sizing and placement of their children. This is known as managing the composite's geometry.

The geometry management behavior implemented by a particular composite object is determined by its VCI_geoAttrs and VCI_geoDimensionAttrs fields. Both of these fields may be set and altered by applications as their geometry needs change. The VCI_geoAttrs field, specifically, determines the type of geometry management employed by the composite.

The VCI_geoAttrs field may contain any or all of the following eight flags:

VCGA_ORIENT_CHILDREN_VERTICALLY
This flag indicates that the composite's children should be arranged vertically rather than the default (horizontally).
VCGA_INCLUDE_ENDS_IN_CHILD_SPACING
When the composite is using full justification (see VCI_geoDimensionAttrs ), this flag indicates that there should be as much space before the first child and after the last child as there is between the children. If this flag is not set, there will be no space outside the first and last children.
VCGA_ALLOW_CHILDREN_TO_WRAP
This flag will allow the children to wrap if their combined lengths won't allow them to fit within the composite's bounds. The composite will keep within the bounds of its parent, and its children will wrap as necessary. If this flag is not set, the composite will try to grow to be as large as necessary to fit all children.
VCGA_ONE_PASS_OPTIMIZATION
This flag makes the geometry manager make only one pass at managing the children. It should only be set if the children can be guaranteed not to wrap or resize.
VCGA_CUSTOM_MANAGE_CHILDREN
This flag indicates that the composite will manage its children without using the geometry manager. This allows the composite or its children to manually determine their positions and sizes. If this flag is set, the composite will keep its own bounds in its VI_bounds field, just as other Vis objects. This flag in a composite indicates custom management for the composite's entire branch, not just its children.
VCGA_HAS_MINIMUM_SIZE
This flag indicates that the composite has a minimum size. The geometry manager will query the composite for this minimum and will ensure the object never gets smaller than that regardless of the size of its children. The minimum size must be returned by a custom MSG_VIS_COMP_GET_MINIMUM_SIZE handler.
VCGA_WRAP_AFTER_CHILD_COUNT
Used in conjunction with VCGA_ALLOW_CHILDREN_TO_WRAP, this flag will cause child wrapping after a certain number of children. This can cause wrapping based on the number of children rather than on child size. The geometry manager will query the composite with MSG_VIS_COMP_GET_WRAP_COUNT .
VCGA_ONLY_DRAWS_IN_MARGINS
This flag is used for optimized visual updates. It causes only the margins of the composite to be drawn when its image is marked invalid; all children of the composite must have their own images marked invalid if they are to be redrawn as well.

VisComp: 2.5 VisCompClass Instance Data: VCI_geoDimensionAttrs

The VCI_geoDimensionAttrs field determines how the composite manages its children in each dimension. It provides the geometry information not given in VCI_geoAttrs such as child justification and certain sizing behavior.

It contains three fields for each dimension (horizontal and vertical): The first field represents the justification of the children in that dimension. This field is two bits and can be one of four different enumerations. The second and third fields are sizing flags. These fields are listed below:

VCGDA_WIDTH_JUSTIFICATION
This is a two-bit field that can be set to any one of four possible width justifications. If the name of this field is used in place of one of the four values, full justification will be used. The justification can be set as a normal flag. The four different values are
WJ_LEFT_JUSTIFY_CHILDREN
Left justify the children.
WJ_RIGHT_JUSTIFY_CHILDREN
Right justify the children.
WJ_CENTER_CHILDREN_HORIZONTALLY
Center the children horizontally.
WJ_FULL_JUSTIFY_CHILDREN_HORIZONTALLY
Full justify the children. Horizontal full justification is only meaningful if the children are oriented horizontally (by clearing VCGA_ORIENT_CHILDREN_VERTICALLY in VCI_geoAttrs ).
VCGDA_EXPAND_WIDTH_TO_FIT_PARENT
If this flag is set, the composite will try to expand to fill the available width of its parent. By default, this flag is not set; the composite will be only as wide as its children require.
VCGDA_DIVIDE_WIDTH_EQUALLY
If this flag is set, the composite will try to divide space equally between all its manageable, horizontally-oriented children. The composite will only suggest sizes--the children may or may not cooperate.
VCGDA_HEIGHT_JUSTIFICATION
This is a two-bit field that can be set to any one of four height justifications. If the name of this field is used in place of one of the four values, full justification will be used. The justification can be set as a normal flag. The four different values are
HJ_TOP_JUSTIFY_CHILDREN
Justify the children to the composite's top bound.
HJ_BOTTOM_JUSTIFY_CHILDREN
Justify the children to the composite's bottom bound.
HJ_CENTER_CHILDREN_VERTICALLY
Center the children vertically.
HJ_FULL_JUSTIFY_CHILDREN_VERTICALLY
Full justify the children. Vertical full justification is only meaningful if the children are oriented vertically (by setting VCGA_ORIENT_CHILDREN_VERTICALLY in VCI_geoAttrs ).
VCGDA_EXPAND_HEIGHT_TO_FIT_PARENT
If this flag is set, the composite will try to expand to fill the available height of its parent. By default, this flag is not set; the composite will be only as tall as its children require.
VCGDA_DIVIDE_HEIGHT_EQUALLY
If this flag is set, the composite will try to divide space equally between all its manageable, vertically-oriented children. The composite will only suggest sizes--the children may or may not cooperate .

VisComp: 2.6 VisCompClass Instance Data: Managing Instance Data

MSG_VIS_COMP_GET_GEO_ATTRS, MSG_VIS_COMP_SET_GEO_ATTRS

To retrieve the flags currently set in both VCI_geoAttrs and VCI_geoDimensionAttrs , use MSG_VIS_COMP_GET_GEO_ATTRS . To set the attributes in either or both fields, use MSG_VIS_COMP_SET_GEO_ATTRS . both of these messages are detailed below.

MSG_VIS_COMP_SET_GEO_ATTRS
void	MSG_VIS_COMP_SET_GEO_ATTRS(
        word	attrsToSet,
        word	attrsToClear);

This message sets the flags in the composite object's VCI_geoAttrs and VCI_geoDimensionAttrs fields. The high byte of each parameter represents the dimension attributes, and the low byte represents the geometry attributes. This message does not invalidate or update the object's geometry.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: attrsToSet A word of attributes that should be set for the object. The high byte is a record of VisCompGeoDimensionAttrs , and the low byte is a record of VisCompGeoAttrs . The attributes set in this parameter will be set for the object.

attrsToClear
A word of attributes to be cleared from the object's instance data. It has the same form as attrsToSet , above. Any attribute set in this parameter will be cleared in the instance fields.

Return: Nothing.

Interception: Unlikely.

MSG_VIS_COMP_GET_GEO_ATTRS
word	MSG_VIS_COMP_GET_GEO_ATTRS();

This message retrieves the flags set in the object's VCI_geoAttrs and VCI_geoDimensionAttrs fields. The high byte of the return value represents the dimension attributes, and the low byte represents the geometry attributes. This message does not invalidate or update the object's geometry.

Source: Unrestricted.

Destination: Any visible composite object.

Parameters: None.

Return: A word of flags. The high byte is a record of type VisCompGeoDimensionAttrs ; the low byte is a record of type VisCompGeoAttrs . The high byte represents the attributes set in the object's VCI_geoDimensionAttrs field, and the low byte represents the attributes set in the object's VCI_geoAttrs field.

Interception: Unlikely.

VisComp: 3 Using VisCompClass

With the exception of the geometry flags mentioned above and the management of children, the use of VisCompClass is little different from the use of VisClass . In fact, most of the functionality designed for VisCompClass is built directly into VisClass ; for example, nearly all geometry management and object tree management messages are defined in VisClass . Some reminders are listed below, however.

Not all composite objects will have something to draw. Some composites will be used simply as grouping objects to manage visible children. In these cases, you will not have to subclass VisCompClass but can instead use the class directly.

Many composite objects will have something to draw, though. For example, a composite may want to draw a box around its children or wash a different background color behind them. If this is the case, the object must be a subclass of VisCompClass and must handle MSG_VIS_DRAW .

One of the primary functions of VisCompClass is to pass input events and other messages down and up the tree to the proper objects. This is done automatically. You can, however, change this behavior by subclassing VisCompClass and intercepting the messages in which the object will be interested.

Managing Geometry

Managing Graphic Windows

VisComp: 3.1 Using VisCompClass: Managing Geometry

MSG_VIS_COMP_GET_CHILD_SPACING, MSG_VIS_COMP_GET_MINIMUM_SIZE, MSG_VIS_COMP_GET_MARGINS, MSG_VIS_COMP_GET_WRAP_COUNT

A special feature of VisCompClass , and one that can be used in many ways, is its ability to automatically manage its children. By setting various flags in the composite's instance fields, you can have it control its children's sizing and position without additional code in your application.

Most of the flags you can set in VCI_geoAttrs and VCI_geoDimensionAttrs are explained fully in VisCompClass Instance Data . You should especially be aware that if you do not want to use the geometry management capabilities of VisCompClass , you should set VCGA_CUSTOM_MANAGE_CHILDREN in VCI_geoAttrs .

In addition to the messages provided in VisClass for geometry management, VisCompClass has four that return information about its current geometry. These are necessary because composites may be children of other composites, and therefore they may be managed. These messages are detailed below.

Most of the issues of geometry management are discussed in the VisClass chapter.

DWORD_CHILD_SPACING
word	DWORD_CHILD_SPACING(val);
        SpacingAsDWord val;

This macro extracts the child spacing from the given SpacingAsDWord value. Use it with MSG_VIS_COMP_GET_CHILD_SPACING .

DWORD_WRAP_SPACING
word	DWORD_WRAP_SPACING(val);
        SpacingAsDWord val;

This macro extracts the wrap spacing from the given SpacingAsDWord value. Use it with MSG_VIS_COMP_GET_CHILD_SPACING .

MAKE_SPACING_DWORD
SpacingAsDWord MAKE_SPACING_DWORD(child, wrap);
        word	child;
        word	wrap;

This macro creates a SpacingAsDWord dword from the two given arguments. The child argument is the child spacing, and the wrap argument is the wrap spacing. Use this macro in your handler (if any) for the message MSG_VIS_COMP_GET_CHILD_SPACING .

MSG_VIS_COMP_GET_CHILD_SPACING
SpacingAsDWord MSG_VIS_COMP_GET_CHILD_SPACING();

This message returns the child spacing used by the composite. The high word of the return value is the spacing between lines of wrapped children; the low word is the horizontal spacing between children.

Source: Unrestricted.

Destination: Any visible composite object--typically sent by a composite to itself during geometry calculations.

Parameters: None.

Return: A dword containing the child spacing used by the composite. The dword contains two values: The child spacing--the amount of spacing placed between the composite's children--can be extracted from the return value with the macro DWORD_CHILD_SPACING . The wrap spacing--the amount of space placed between lines of wrapped children--can be extracted from the return value with the macro DWORD_WRAP_SPACING .

Interception: If a composite wants special child or wrap spacing other than the default, it should subclass this message and return the desired values. There is no need to call the superclass in the method.

Tips: In your handler, you can use the macro MAKE_SPACING_DWORD to form the return value from the two spacing values.

MSG_VIS_COMP_GET_MINIMUM_SIZE
SizeAsDWord MSG_VIS_COMP_GET_MINIMUM_SIZE();

This message returns the minimum size of the composite. It is used by the geometry manager if the composite has VCGDA_HAS_MINIMUM_SIZE set. This message does not invalidate or update the object's geometry.

Source: Unrestricted.

Destination: Any visible composite object--typically sent by a composite to itself during geometry calculations.

Parameters: None.

Return: A dword containing the minimum size of the composite. The high word is the width, and the low word is the height. Use the macros DWORD_WIDTH and DWORD_HEIGHT to extract the individual values from the SizeAsDWord structure.

Interception: Any composite that wants a minimum size should subclass this message and return its desired size. There is no need to call the superclass in your handler.

Tips: In your handler, use the macro MAKE_SIZE_DWORD to create the SizeAsDWord return value from the width and height. This macro has the same format as MAKE_SPACING_DWORD .

MSG_VIS_COMP_GET_MARGINS
void	MSG_VIS_COMP_GET_MARGINS(
        Rectangle *retValue);

This message returns the margins the composite should use when recalculating its child spacing. If you want a special left, top, right, or bottom margin around the composite's children, intercept this message and return the margin(s) in the appropriate field(s) of the Rectangle structure.

Source: Unrestricted.

Destination: Any visible composite object--typically sent by a composite to itself during geometry calculations.

Parameters: retValue A pointer to an empty Rectangle structure that will be filled with the composite's desired margins.

Return: The pointer to the filled Rectangle structure is preserved. The structure contains the four margins desired by the object outside of its bounds (e.g. if retValue->R_top is 100 upon return, the composite is requesting 100 points of extra "margin" spacing below its top bound before its children are placed).

Interception: Any composite that wants extra margin space added to its bounds when geometry is calculated should subclass this message and return its desired margins. There is no need to call the superclass in your handler.

MSG_VIS_COMP_GET_WRAP_COUNT
word	MSG_VIS_COMP_GET_WRAP_COUNT();

This message returns the number of children to be counted before wrapping if the composite has VCGA_WRAP_AFTER_CHILD_COUNT set.

Source: Unrestricted.

Destination: Any visible composite object--typically sent by a composite to itself during geometry calculations.

Parameters: None.

Return: The number of children that will be allowed before the composite wraps additional children to a new line.

Interception: Any composite that wants to wrap after a certain number of children should subclass this message and return the proper number of children. There is no need to call the superclass in your handler .

VisComp: 3.2 Using VisCompClass: Managing Graphic Windows

It is very rare that a visible object will want to create its own window without using a generic object. This practice is highly discouraged as well because it will almost certainly violate some principles of most specific UI specifications. You can, however, set up a visible object to have its own window with the following steps:

  1. Set up the VisComp object as a window group.
    Either instantiate a new VisComp object or set it up and load it in, off-screen. Then set the flags VTF_IS_WINDOW and VTF_IS_WIN_GROUP in the composite to make it a window and the top of the visible tree.
  2. Add visible children to the window object.
    Using MSG_VIS_ADD_CHILD , add any visible object children to the window group as you need. If some of the children had possibly been removed from the visible tree earlier, you may have to mark them invalid.
  3. Add your window to the field window.
    Determine the handle of the parent window you need, then set your window group object as a child of the parent.
  4. Set the top object in your window group visible.
    Set VA_VISIBLE using MSG_VIS_SET_ATTR to mark the window group visible. The window will be opened, and it will receive a MSG_META_EXPOSED indicating that it and all its children should draw themselves.
This document is a single-page version of a a multi-page document, suitable for easy printing.


VisContent

VisContentClass provides objects that can be used to head visible trees and interact with the GenView. The VisContent is a necessary class for any application that plans on displaying visible objects in a view.

You should be familiar with visible objects, visible object trees, and the GenView class. These topics can be found in the VisClass chapter and the GenView chapter.

Because VisContentClass is a subclass of VisCompClass , it inherits all the instance data fields and messages of both that class as well as VisClass . You will likely want to read about both of those classes before reading this chapter. You can just read Basic VisContent Usage if you need a basic VisContent object--that section describes the most basic ways to use the VisContent without understanding its inner workings.

1 VisContent Instance Data
1.1 The VCNI_attrs Field
1.2 Fields That Affect the View
1.3 Fields That Affect the Document
1.4 Fields That Affect Input Events
2 Basic VisContent Usage
2.1 Setting Up Sizing Behavior
2.2 Messages Received from the View

VisContent: 1 VisContent Instance Data

The VisContent has many instance data fields above and beyond those defined in VisClass and VisCompClass . These new instance fields deal with such topics as content attributes, the way the content interacts with the view, the document described by the content, and how the content handles input.

All the instance fields defined by VisContentClass and their definitions are shown in VisContent Instance Data Fields . Each field is described later in this section.

Code Display 17-1 VisContent Instance Data Fields

/* All the instance fields defined in VisContentClass are described below. You
 * will likely use very few of them, if any; see the later sections of this
 * chapter for specific information about each field. */
    /* The following fields deal with the GenView and the view window. */
    @instance optr				 VCNI_view = 0;
    @instance WindowHandle				 VCNI_window = 0;
    @instance word				 VCNI_viewHeight = 0;
    @instance word				 VCNI_viewWidth = 0;
    /* VCNI_attrs determines the content's attributes. */
    @instance  VisContentAttrs				 VCNI_attrs = 0;
    /* The possible flags for VCNI_attrs are
     *	VCNA_SAME_WIDTH_AS_VIEW						0x80
     *	VCNA_SAME_HEIGHT_AS_VIEW						0x40
     *	VCNA_LARGE_DOCUMENT_MODEL						0x20
     *	VCNA_WINDOW_COORDINATE_MOUSE_EVENTS						0x10
     *	VCNA_ACTIVE_MOUSE_GRAB_REQUIRES_LARGE_EVENTS						0x08
     *	VCNA_VIEW_DOC_BOUNDS_SET_MANUALLY						0x04	 */
    /* The following fields determine features of the content's document. */
    @instance PointDWord				 VCNI_docOrigin = {0, 0};
    @instance PointWWFixed				 VCNI_scaleFactor = {{0, 1}, {0, 1}};
     /* The following fields deal with how the content handles input. */
    @instance ChunkHandle				VCNI_prePassiveMouseGrabList = 0;
    @instance VisMouseGrab				VCNI_impliedMouseGrab =
                    {0, 0, {0, 0}, (VIFGF_MOUSE | VIFGF_PTR), 0};
    @instance VisMouseGrab				VCNI_activeMouseGrab =
                    {0, 0, {0, 0}, 0, 0};
    @instance ChunkHandle				VCNI_postPassiveMouseGrabList = 0;
    @instance KbdGrab				VCNI_kbdGrab = {0, 0};
    @instance FTVMCGrab				VCNI_focusExcl = {0, MAEF_FOCUS};
    @instance FTVMCGrab				VCNI_targetExcl = {0, MAEF_TARGET};
    @instance Handle				VCNI_holdUpInputQueue = 0;
    @instance word				VCNI_holdUpInputCount = 0;
    @instance byte				VCNI_holdUpInputFlags = 0;
    /* The type flags of the content are special and should not be altered. */
    @default		VI_typeFlags =		VTF_IS_COMPOSITE | VTF_IS_WINDOW |
                VTF_IS_CONTENT | VTF_IS_WIN_GROUP |
                VTF_IS_INPUT_NODE;

The VCNI_attrs Field

Fields That Affect the View

Fields That Affect the Document

Fields That Affect Input Events

VisContent: 1.1 VisContent Instance Data: The VCNI_attrs Field

VCNI_attrs, MSG_VIS_CONTENT_SET_ATTRS, MSG_VIS_CONTENT_GET_ATTRS

The VCNI_attrs field is a record of VisContentAttrs that contains several attributes which affect how the content object interacts with the view and with the visible object tree. You can set these attributes with MSG_VIS_CONTENT_SET_ATTRS dynamically, and you can retrieve them with MSG_VIS_CONTENT_GET_ATTRS .

The flags in this field are shown below. None of them is set by default.

VCNA_SAME_WIDTH_AS_VIEW
This flag indicates that the content's width should follow the width of the view window, if possible. Most likely, you will want to set this if the view is not horizontally scrollable.
VCNA_SAME_HEIGHT_AS_VIEW
This flag indicates that the content's height should follow the height of the view window, if possible. Most likely you will want to set this flag if the view is not vertically scrollable.
VCNA_LARGE_DOCUMENT_MODEL
This flag indicates that the content object manages a large document (32-bit coordinates rather than the standard 16-bit coordinates). For information on how this affects the content and its children, see the VisClass chapter.

VCNA_WINDOW_COORDINATE_MOUSE_EVENTS
This flag must be set if VCNA_LARGE_DOCUMENT_MODEL is set. It indicates that the associated GenView will pass input events with window coordinates rather than document coordinates. The default handlers in the content object will then automatically translate the events into document coordinates.

VCNA_ACTIVE_MOUSE_GRAB_REQUIRES_LARGE_EVENTS
This flag indicates that the object that currently has the active mouse grab requires mouse input events to carry large document coordinates rather than the standard document coordinates. This flag is set and reset with the message MSG_VIS_VUP_ALTER_INPUT_FLOW . Large content objects should not set this flag in their Goc declarations.
VCNA_VIEW_DOC_BOUNDS_SET_MANUALLY
Not often used, this flag indicates that the content should not send its document bounds off to the view during a geometry update. The GenView's document bounds must be set manually, most likely with the GenView message MSG_GEN_VIEW_SET_DOC_BOUNDS .
VCNA_VIEW_DOES_NOT_WIN_SCROLL
This flag indicates that the view does not scroll but instead sends MSG_META_CONTENT_VIEW_ORIGIN_CHANGED to the content when the user interacts with the scroller. The UI will use this flag to invalidate the correct region of the content. This should be set when ATTR_GEN_VIEW_DO_NOT_WIN_SCROLL is set in the GenView. See the GenView chapter for full information on view scrolling.
MSG_VIS_CONTENT_SET_ATTRS
void	MSG_VIS_CONTENT_SET_ATTRS(
        VisContentAttrs attrsToSet,
        VisContentAttrs attrsToClear);

This message sets the VCNI_attrs field of the content object according to the passed values.

Source: Unrestricted.

Destination: Any VisContent object.

Parameters: attrsToSet This is a record of VisContentAttrs to set. A flag set in this field will be set in the object's VCNI_attrs field.

attrsToClear This is a record of VisContentAttrs to clear. A flag set in this field will be cleared in the object's VCNI_attrs field. This parameter takes precedence over attrsToSet ; that is, if a flag is set in both parameters, it will end up cleared.

Return: Nothing.

Interception: Unlikely.

MSG_VIS_CONTENT_GET_ATTRS
VisContentAttrs MSG_VIS_CONTENT_GET_ATTRS();

This message returns the current contents of the object's VCNI_attrs field, a record of VisContentAttrs .

Source: Unrestricted.

Destination: Any VisContent object.

Parameters: None.

Return: A record of VisContentAttrs reflecting the flags currently set in the object's VCNI_attrs field.

Interception: Unlikely.

VisContent: 1.2 VisContent Instance Data: Fields That Affect the View

VCNI_view, VCNI_viewHeight, VCNI_viewWidth, VCNI_window, MSG_VIS_CONTENT_GET_WIN_SIZE

Because the content must interact directly with the view, it must maintain some information about both the GenView object and its associated window. VisContentClass defines four fields that deal exclusively with view-related information. Each of these fields is described below.

VCNI_view
This field contains the optr of the GenView object. The content should not ever actually use this field for the view's optr; if you need to contact the view, you should instead use the messages MSG_VIS_VUP_SEND_TO_OBJECT_OF_CLASS and MSG_VIS_VUP_CALL_OBJECT_OF_CLASS . The field will be set automatically by the GenView when the view is opened; the view will send a MSG_META_CONTENT_VIEW_OPENING .
VCNI_viewHeight
This field contains the height, in document coordinates, of the view window. This will be set by the view object and should not be altered by the application; it can be retrieved with the message MSG_VIS_CONTENT_GET_WIN_SIZE (below). The view automatically notifies the content each time the window size changes with MSG_META_CONTENT_VIEW_SIZE_CHANGED .
VCNI_viewWidth
This field contains the width, in document coordinates, of the view window. This will be set by the view object and should not be altered by the application; it can be retrieved with the message MSG_VIS_CONTENT_GET_WIN_SIZE (below). The view automatically notifies the content on each window resize with MSG_META_CONTENT_VIEW_SIZE_CHANGED .
VCNI_window
This field contains the window handle of the view's window. This field is set by the view object and should not be accessed by the application. If an object under the content needs to get the window handle, it should use MSG_VIS_QUERY_WINDOW . This field will be set automatically by the GenView when the view's window is first opened. The view will send the messages MSG_META_CONTENT_VIEW_WIN_OPENED and MSG_META_CONTENT_VIEW_OPENING to set the window handle. When the view window closes, the view will send MSG_META_CONTENT_VIEW_CLOSING and MSG_META_CONTENT_VIEW_WIN_CLOSED .

The messages shown above that are sent by the view to the content are detailed in Messages Received from the View .

MSG_VIS_CONTENT_GET_WIN_SIZE
SizeAsDWord MSG_VIS_CONTENT_GET_WIN_SIZE();

This message returns the size of the content object's associated window in terms of width and height.

Source: Unrestricted.

Destination: Any VisContent object.

Parameters: None.

Return: A SizeAsDWord value with the window's width in the high word and the window's height in the low word. Use the DWORD_HEIGHT and DWORD_WIDTH macros to extract the proper values.

Interception: Unlikely.

VisContent: 1.3 VisContent Instance Data: Fields That Affect the Document

VCNI_docOrigin, VCNI_scaleFactor, MSG_VIS_CONTENT_SET_DOC_BOUNDS, MSG_VIS_CONTENT_RECALC_SIZE_BASED_ON_VIEW

The GenView object maintains quite a bit of information about the document as managed by the content object. The content must also keep information about the document and how the view is displaying it. This information is stored in two fields, VCNI_docOrigin and VCNI_scaleFactor , both detailed below.

The document bounds of the content are typically equal to the bounds of the VisContent itself. The content's bounds are stored in the VI_bounds field inherited from VisClass . When a content is managing layers or large documents, however, its bounds are set to zero. It then manages its document bounds within the layer objects and the GenView. When the document bounds change, a MSG_VIS_CONTENT_SET_DOC_BOUNDS should be sent to the content to get it to notify all its layer children and the GenView of the new bounds. This message is shown at the end of this section.

If the content is not a large document and is set up to follow the GenView's geometry (it has either or both of VCNA_SAME_WIDTH_AS_VIEW or VCNA_SAME_HEIGHT_AS_VIEW set), it will be affected by changes in the view's geometry. During geometry updates, the view will send it a MSG_VIS_CONTENT_RECALC_SIZE_BASED_ON_VIEW . This message sets the content's width and/or height and therefore affects its VI_bounds field. This message is also shown at the end of this section.

VCNI_docOrigin
This field contains the current origin of the view window. The origin is the location of the view's upper left corner in the document (where the scrollers are). This field is of type PointDWord , which has the following structure:
typedef struct {
    sdword   PD_x;    /* x coordinate of origin */
    sdword   PD_y;    /* y coordinate of origin */
} PointDWord;
Normally, this field is set when the view is first opened or when the view is scrolled, scaled, or otherwise changed in document position. The view will send the message MSG_META_CONTENT_VIEW_ORIGIN_CHANGED to indicate the origin is different from its current setting.
VCNI_scaleFactor
This field contains the current scale factor the view is displaying. Scaling is implemented almost entirely in the GenView object; some content objects, however, will want to react in a special way when the scale factor is changed. The scale factor is stored in a PointWWFixed structure, as follows:
typedef struct {
    WWFixed    PF_x;   /* horizontal scale factor */
    WWFixed    PF_y;   /* vertical scale factor */
} PointWWFixed;
The WWFixed structures that determine the scale factor in each dimension consist of two elements. This structure is shown below:
typedef struct {
    word    WWF_frac;   /* fractional portion */
    word    WWF_int;    /* integral portion */
} WWFixed;
The VCNI_scaleFactor field in the content is never set directly by the application; instead, it is set with MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED whenever the view's scale factor changes.

Messages that set these fields are discussed in Messages Received from the View .

MSG_VIS_CONTENT_SET_DOC_BOUNDS
void	MSG_VIS_CONTENT_SET_DOC_BOUNDS(@stack
        sdword	bottom,
        sdword	right,
        sdword	top,
        sdword	left);

This message is used to set the document bounds of a content's large document. The content will send MSG_GEN_VIEW_SET_DOC_BOUNDS to its view and MSG_VIS_LAYER_SET_DOC_BOUNDS to each of its children with the new bounds. The recipient content must have the flag VCNA_LARGE_DOCUMENT_MODEL set in VCNI_attrs .

Source: Unrestricted.

Destination: Any large VisContent object--if the content is not using the large document model, an error will result.

Parameters: bottom, right, top, left
 The new document bounds in 32-bit document coordinates.

Return: Nothing.

Interception: Unlikely.

Warnings: You may only send this message to a content that is using the large document model. All other contents will not handle this message but will result in a fatal error.

MSG_VIS_CONTENT_RECALC_SIZE_BASED_ON_VIEW
SizeAsDWord MSG_VIS_CONTENT_RECALC_SIZE_BASED_ON_VIEW();

This message causes the content to recalculate its size based on the view's geometry. It will try to set its width if it has VCNA_SAME_WIDTH_AS_VIEW set, and it will try to set its height if it has VCNA_SAME_HEIGHT_AS_VIEW set.

Source: Unrestricted--typically sent by the view or by the content to itself during geometry updates.

Destination: Any VisContent object.

Parameters: None.

Return: A SizeAsDWord value indicating the new size of the content. Use the macros DWORD_HEIGHT and DWORD_WIDTH to extract the appropriate values.

Interception: Unlikely .

VisContent: 1.4 VisContent Instance Data: Fields That Affect Input Events

One of the main features of VisContentClass is its ability to handle, manage, and pass on input events sent through the GenView. The content has a large amount of functionality built into it to provide these features.

Mouse and Keyboard Grabs

VCNI_prePassiveMouseGrabList, VCNI_impliedMouseGrab, VCNI_activeMouseGrab, VCNI_postPassiveMouseGrabList, VCNI_kbdGrab, MSG_VIS_CONTENT_UNWANTED_MOUSE_EVENT, MSG_VIS_CONTENT_UNWANTED_KBD_EVENT, MSG_VIS_CONTENT_TEST_IF_ACTIVE_OR_IMPLIED_WIN

VisContentClass , as the head of the visible tree displayed in the view, keeps track of which object in its tree has each different type of input grab. With this information, the content can simply pass the input event directly to the object that has the grab. It can also easily send the event to both the prepassive and postpassive grab objects, if any.

To do this, it uses five instance fields that applications can not directly access. These fields are altered by the messages that allow an object to gain and release the subject grab. (These messages are all detailed in the VisClass chapter.) The instance fields are listed below:

VCNI_prePassiveMouseGrabList
This field is a pointer to a chunkarray containing the list of objects that currently have the prepassive mouse grab.
VCNI_impliedMouseGrab
This field is a VisMouseGrab (described below) structure that contains details about the object which has the implied mouse grab.
VCNI_activeMouseGrab
This field is a VisMouseGrab (described below) structure that contains details about the object which has the active mouse grab.
VCNI_postPassiveMouseGrabList
This field is a pointer to a chunkarray containing the list of objects that currently have the postpassive mouse grab.
VCNI_kbdGrab
This field is a KbdGrab structure that contains details about the object which currently has the keyboard grab.

You will probably not ever have to know the structure of these fields, how to set them, or the information they contain. However the two structures VisMouseGrab and KbdGrab are shown in Grab Data Structures for your information.

Code Display 17-2 Grab Data Structures

/* These structures are obscure, and you will likely never have to use them. */
  /* The VisMouseGrab structure contains information about the object that
 * currently has the mouse grab. */
typedef struct {
    optr		VMG_object;		/* The optr of the object that has the grab.
                 * If no object has the grab, this is zero. */
    WindowHandle		VMG_gWin;		/* The window handle of the window with the object
                 * having the grab, if it's different from
                 * the content's window. If it's in the content's
                 * window, this field contains zero.	*/
    PointDWord		VMG_translation;		/* The 32-bit translation applied to mouse events
                 * if the large document model is in use. This is
                 * set with a previous message call. */
    VisInputFlowGrabFlags VMG_flags; /* A record of VisInputFlowGrabFlags,
                   * described below. */
    byte		VMG_unused;		/* Reserved byte. */
} VisMouseGrab;
/* The VisInputFlowGrabFlags determine the type and context of the grab. These
 * flags are not listed here for simplicity. You do not have to know these flags;
 * they are set with MSG_VIS_VUP_ALTER_INPUT_FLOW. */
  /* The KbdGrab structure contains information about the object that currently
 * has the keyboard grab. */
typedef struct {
    optr		KG_OD;		/* The optr of the object that has the
                 * keyboard grab. */
    word		KG_unused;		/* Reserved word. */
} KbdGrab;

In addition, the VisContent has the following messages that are affected by the input grab fields:

MSG_VIS_CONTENT_TEST_IF_ACTIVE_OR_IMPLIED_WIN
This message returns true if the passed window handle is the same as the active or implied window currently translating mouse events. It returns false otherwise. This is used by specific UI objects.
MSG_VIS_CONTENT_UNWANTED_MOUSE_EVENT
This message is sent by the content to itself if a mouse event has arrived and there is no implied or active grab in the content's visible tree. The default reaction is to beep if the event is a button press. It's highly unlikely that you'll send or intercept this message.
MSG_VIS_CONTENT_UNWANTED_KBD_EVENT
This message is sent by the content to itself if a keyboard event has arrived and there is no keyboard grab set up. It is highly unlikely that you will ever send or intercept this message.
MSG_VIS_CONTENT_TEST_IF_ACTIVE_OR_IMPLIED_WIN
Boolean	MSG_VIS_CONTENT_TEST_IF_ACTIVE_OR_IMPLIED_WIN(
        WindowHandle window);

This message checks to see if the passed window handle is the same as the window of the object having either the implied or active mouse grab. This is typically used by objects in a Specific UI library to determine if the mouse event was actually within the window or directly on the window's border.

Source: Unrestricted.

Destination: Any VisContent object.

Parameters: window The window handle to be checked.

Return: True if the window handle is the same as either the active or the implied window, false otherwise.

Interception: Unlikely.

MSG_VIS_CONTENT_UNWANTED_MOUSE_EVENT
void	MSG_VIS_CONTENT_UNWANTED_MOUSE_EVENT(
        VisMouseGrab *mouseGrab,
        word	inputState);

This message is received by the content if a mouse event was received and there was no active or implied grab. This is most frequently encountered when the user presses a mouse button outside a modal dialog box. The default action of this handler is to beep (on presses only, not releases) and get rid of the event as if it had been handled.

Source: Input flow mechanism.

Destination: The affected VisContent object.

Parameters: mouseGrab A pointer to the appropriate VisMouseGrab structure.

inputState
Same as passed with the actual event.

Return: Nothing.

Interception: Unlikely.

MSG_VIS_CONTENT_NOTIFY_ACTIVE_MOUSE_GRAB_WIN_CHANGED
void 	MSG_VIS_CONTENT_NOTIFY_ACTIVE_MOUSE_GRAB_WIN_CHANGED();

A message we send ourselves from within our MSG_VIS_VUP_ALTER_INPUT_FLOW handler in the case that VCNI_activeMouseGrab . VMG_gWin changes. Used by the specific UI's implementation of GenApplicationClass to keep track of the currently active window within the application.

Source: See above.

Destination: Self.

Parameters: None

Return: Nothing.

Interception: Intercepted by specific UI's implementation of GenApplication for the purpose of finding out a change in the active window within the application has occurred. It will in turn update the window system so that mouse grabs and pointer image changes work properly.

MSG_VIS_CONTENT_UNWANTED_KBD_EVENT
void	MSG_VIS_CONTENT_UNWANTED_KBD_EVENT(
        word	character,
        word	flags,
        word	state);

This message will be received by the content if a keyboard event was sent and there was no keyboard grab set up. The default action of the content is to beep (on presses only, not releases) and return as if the event had been processed.

Source: Input flow mechanism.

Destination: The affected VisContent object.

Parameters: character The keyboard character pressed.

flags
A word of flags: The low byte is a CharFlags record, and the high byte is a ShiftState record. Both are the same as passed with the original MSG_META_KBD_CHAR .
state
A word containing two values: The low byte is a record of ToggleState , and the high byte is the scan code. Both are the same as passed with the original MSG_META_KBD_CHAR .

Return: Nothing.

Interception: Unlikely.

Focus and Target

VCNI_focusExcl, VCNI_targetExcl, MSG_META_CONTENT_APPLY_DEFAULT_FOCUS

In addition to keeping track of which of its children have the mouse and keyboard grabs, the content also keeps track of which objects have the focus and target input exclusives. Both VCNI_focusExcl and VCNI_targetExcl contain a structure of type FTVMCGrab that describes the object that has the subject exclusive. The messages sent by the GenView that set these fields are described in Messages Received from the View .

Input Flow Control

VCNI_holdUpInputQueue, VCNI_holdUpInputCount, VCNI_holdUpInputFlags, MSG_VIS_CONTENT_HOLD_UP_INPUT_FLOW, MSG_VIS_CONTENT_RESUME_INPUT_FLOW, MSG_VIS_CONTENT_DISABLE_HOLD_UP, MSG_VIS_CONTENT_ENABLE_HOLD_UP

GEOS allows a visible tree to hold up input--that is, input will be stored elsewhere while the visible tree is doing something else. This can be useful if complex tree operations are going on and you don't want input to go to the wrong object.

VisContentClass provides three instance fields that define the input holdup mechanism. These three fields are

VCNI_holdUpInputQueue
This field contains the queue handle of the queue where held-up input will be temporarily stored. Input events will go into this event queue until they are allowed to be handled again; then they will be sent to their proper recipients.
VCNI_holdUpInputCount
This field contains a count of the number of objects that have requested that input be held up. If this count is positive, input will be held up and input events will go into the hold-up queue.
VCNI_holdUpInputFlags
This field contains a record of flags which determine the state of the hold-up mechanism. The following two flags are allowed:
HUIF_FLUSHING_QUEUE
This flag indicates that the hold-up queue is currently being flushed.
HUIF_HOLD_UP_MODE_DISABLED
This flag forces input events to flow normally. It is used primarily by GEOS to ensure that the user can interact with a system-modal dialog box.

VisContentClass has four messages that it sends to itself to set the state of information hold-up. These messages are detailed below.

MSG_VIS_CONTENT_HOLD_UP_INPUT_FLOW
void	MSG_VIS_CONTENT_HOLD_UP_INPUT_FLOW();

This message increments the count in VCNI_holdUpInputCount . If this count is nonzero and HUIF_HOLD_UP_MODE_DISABLED is clear, subsequent input events will be sent into the hold-up queue until either the flag is set or the count once more drops to zero.

Source: Unrestricted.

Destination: Any VisContent object.

Interception: Unlikely.

Warnings: Do not forget to resume input with a later use of the message MSG_VIS_CONTENT_RESUME_INPUT_FLOW .

MSG_VIS_CONTENT_RESUME_INPUT_FLOW
void	MSG_VIS_CONTENT_RESUME_INPUT_FLOW();

This message decrements the count in VCNI_holdUpInputCount . If the count becomes zero with this call, the hold-up event queue is flushed and all the events in it are "played back." If the count goes below zero, GEOS will give an error. Therefore, do not use this message unless it is preceded with a MSG_VIS_CONTENT_HOLD_UP_INPUT_FLOW .

Source: Unrestricted.

Destination: Any VisContent object.

Interception: Unlikely.

Warnings: If this message is used without first holding up input with MSG_VIS_CONTENT_HOLD_UP_INPUT_FLOW , an error will be the likely result. The error condition is the VCNI_holdUpInputCount field going below zero.

MSG_VIS_CONTENT_DISABLE_HOLD_UP
void	MSG_VIS_CONTENT_DISABLE_HOLD_UP();

This message sets the HUIF_HOLD_UP_MODE_DISABLED flag, forcing all input events to flow normally until the flag is cleared. In essence, it turns off the hold-up mechanism.

Source: Unrestricted.

Destination: Any VisContent object.

Interception: Unlikely.

MSG_VIS_CONTENT_ENABLE_HOLD_UP
void	MSG_VIS_CONTENT_ENABLE_HOLD_UP();

This message clears the HUIF_HOLD_UP_MODE_DISABLED flag, allowing input events to be held up in the hold-up event queue.

Source: Unrestricted.

Destination: Any VisContent object.

Interception: Unlikely.

VisContent: 2 Basic VisContent Usage

You will probably not have to subclass VisContentClass unless you are using a large document or you want the content to draw something in response to a MSG_VIS_DRAW . In most other cases, you can declare your content object directly on VisContentClass .

Most VisContent behavior is inherited directly from VisCompClass . Some additional behavior is detailed in the following sections.

If you are planning on using the document control objects with GenViews, you will not need VisContent objects. The GenDocument resolves at run-time into a subclass of VisContent and therefore replaces the need for an explicitly-defined VisContent object. See the Documents chapter for full information on GenDocuments.

Setting Up Sizing Behavior

Messages Received from the View

VisContent: 2.1 Basic VisContent Usage: Setting Up Sizing Behavior

Most geometry behavior revolves around whether the GenView is scrollable or not. You can set up several different sizing behaviors depending on the attributes you set in the GenView. Some are listed below with the attributes you should set in both the GenView and the VisContent.

Typically, setting the view's attributes will involve either setting or not setting the GVDA_SCROLLABLE flag in the appropriate GenView dimension; also, you will want to be aware of the GVDA_NO_LARGER_THAN_CONTENT and GVDA_NO_SMALLER_THAN_CONTENT flags.

If the Content Is Fixed Size

Typically, if the content object is a fixed size, the view will either conform to the content's size or scroll in one or both dimensions. This behavior is determined entirely within the GenView's instance data. Three types of this behavior are shown in Sizing the View with a Fixed Content .

Code Display 17-3 Sizing the View with a Fixed Content

/* This code display shows three different types of sizing behavior of the GenView
 * if its VisContent object is of a fixed size. Note that if the content is
 * managing its geometry, its bounds (and therefore the view's) will be determined
 * by the content's children. */
/* The view window is scrollable in both dimensions. This will result in the view
 * being sizable and scrollable in both dimensions. */
@object GenViewClass MyView = {
    GVI_content = @MyVisContent;
    GVI_horizAttrs = @default | GVDA_SCROLLABLE;
    GVI_vertAttrs = @default | GVDA_SCROLLABLE;
};
/* The view window is scrollable in only the vertical dimension. It follows the
 * width of the VisContent object and therefore does not scroll vertically. The
 * VisContent's VI_bounds field should be set by the content. */
@object GenViewClass MyView = {
    GVI_content = @MyVisContent;
    GVDI_horizAttrs = @default				| GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;
    GVDI_vertAttrs = @default				| GVDA_SCROLLABLE;
};
/* The view window sizes itself exactly to the size of the VisContent's bounds.
 * The VisContent's VI_bounds field should be set appropriately by the content.
 * Note that this is not a valid combination for VisContents that display large
 * documents or layer objects. */
@object GenViewClass MyView = {
    GVI_content = @MyVisContent;
    GVDI_horizAttrs = @default				| GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;
    GVDI_vertAttrs = @default				| GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;
}

Another type of behavior with fixed-size contents is called "keeping the aspect ratio." The view and content can work together to allow the user to resize the view while automatically setting the view's scale factor to keep the entire content in the window. This might be used, for example, for a game board; the game could be resized, and the entire game board would stay in the view.

In this situation, the content calculates its new size based on one of the view's dimensions. For example, when the user resizes the view, the content may keep the width but calculate the height based on the width. The proper scale factor is then set, and the content does not have to do anything special beyond that. To gain this behavior, set up your view and content as shown in Keeping the View Aspect Ratio .

Code Display 17-4 Keeping the View Aspect Ratio

/* This example shows a view and its content. The content object is of a fixed
 * size, and the view is resizable. The content/view pair will keep the aspect
 * ratio to automatically figure the view's height based on its width and then
 * scale the image to keep the entire bounds of the content within the view
 * window. */
@object GenViewClass MyView = {
    GVI_content = @MyVisContent;
    GVDI_horizAttrs = @default				| GVDA_NO_LARGER_THAN_CONTENT
                | GVDA_NO_SMALLER_THAN_CONTENT;
    GVDI_vertAttrs = @default				| GVDA_KEEP_ASPECT_RATIO;
};
@object VisContentClass MyVisContent = {
    VI_bounds =		{0,	/* left bound */
         0,	/* top bound */
         250,	/* right bound */
         250};	/* bottom bound */
    VCI_comp =			/* put any children here */;
    VCI_geoAttrs = VCGA_CUSTOM_MANAGE_CHILDREN;
        /* This is set because typically a content's bounds are determined
         * by its children. If we want to set our own bounds, we should
         * custom manage our geometry. This is true of contents used with
         * the views in the previous example. */
};

If the Content Is Variable Size

Many visible trees will have contents that are of variable size. How the content determines its size differs from use to use; some contents will adjust their geometries to those of their view objects, and some will resize themselves based on the geometry of their children.

Typically, if the content resizes itself based on its children's geometry, the view either will be scrollable or will adjust its size to that of its content.

If the content rearranges its children to meet the size of the view, the view will not be scrollable and will not adjust its size to the content at all. Instead, the content will have the VCNA_SAME_WIDTH_AS_VIEW and VCNA_SAME_HEIGHT_AS_VIEW flags set in its VCNI_attrs field .

VisContent: 2.2 Basic VisContent Usage: Messages Received from the View

MSG_META_CONTENT_SET_VIEW, MSG_META_CONTENT_VIEW_ORIGIN_CHANGED, MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED, MSG_META_CONTENT_VIEW_OPENING, MSG_META_CONTENT_VIEW_WIN_OPENED, MSG_META_CONTENT_VIEW_SIZE_CHANGED, MSG_META_CONTENT_VIEW_CLOSING, MSG_META_CONTENT_VIEW_WIN_CLOSED

A s detailed in the discussions on GenView, the view sends a sequence of messages to its content when the view is first opening and when it is closing. These messages set up the content's visible instance data and prime the visible tree to be drawn on the screen. The messages are handled by the default handlers in VisContentClass , and you do not need to add anything to them to make them work.

The messages sent to the content when the view is first created are

  1. MSG_META_CONTENT_SET_VIEW
     This message passes the view's optr to the content, setting the VCNI_view field properly.
  2. MSG_META_CONTENT_VIEW_ORIGIN_CHANGED
     This message passes the view's initial origin (which may be set other than the default) to the content.
  3. MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED
     This message passes the view's initial scale factor (which may be set other than the default) to the content.
  4. MSG_META_CONTENT_VIEW_OPENING
     This message is sent to the content when the view receives a MSG_VIS_OPEN . This notifies the content that the view is about to be put on the screen and that it should prepare itself to be drawn.
  5. MSG_META_CONTENT_VIEW_WIN_OPENED
     This message passes the window handle of the newly created view window so the content can record it in VCNI_window .
  6. MSG_META_CONTENT_VIEW_SIZE_CHANGED
     This message passes the view window's size (height and width) to the content so the content can determine its proper size and geometry.
  7. MSG_META_EXPOSED
     This message is sent when the view's window is finally on the screen. It signifies that the content should draw itself and then send MSG_VIS_DRAW s to all its children.

The view will also send certain messages to the content when different things happen to change the content's instance data:

  • MSG_META_CONTENT_VIEW_SIZE_CHANGED
     This message is passed whenever the view window's size changes for any reason.
  • MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED
     This message is passed whenever the view's scale factor is changed, usually due to the user setting it from a menu.
  • MSG_META_CONTENT_VIEW_ORIGIN_CHANGED
     This message is passed whenever the view's origin is changed, usually when the view is scrolled.
  • MSG_META_EXPOSED
     This message is passed to the content whenever a portion of the view window becomes exposed and must be drawn. The content automatically translates this into a MSG_VIS_DRAW .

When the view is shutting down, it will send the following three messages to the content to set the proper data:

  1. MSG_META_CONTENT_VIEW_CLOSING
     This message is sent to the content when the view receives a MSG_VIS_CLOSE . It indicates that the view is being taken off the screen and that all the visible objects in the content's tree should remove themselves from the screen.
  2. MSG_META_CONTENT_VIEW_WIN_CLOSED
     This message is sent when the view's window is finally destroyed. The copy of the window handle in VCNI_window will be thrown out now so no drawing to the stale window handle will be done.
  3. MSG_META_CONTENT_SET_VIEW
     This message is sent to set the content's VCNI_view field to a null handle. When the view is finally taken off the screen, it no longer should have a content associated with it since it is meaningless to work with a visible tree that is not on the screen. If the view is opened again later, the content will receive another MSG_META_CONTENT_VIEW_OPENING and will once again be passed the view's optr.
MSG_META_CONTENT_SET_VIEW
void	MSG_META_CONTENT_SET_VIEW(
        optr	view);

This message passes the optr of the GenView object that will display this content object. The default handler will set the content's VCNI_view field to the passed optr. This message is also used when the view has been shut down; the passed optr will be null.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: view The optr of the GenView using this object as its content.

Return: Nothing.

Interception: Unlikely.

MSG_META_CONTENT_VIEW_ORIGIN_CHANGED
void	MSG_META_CONTENT_VIEW_ORIGIN_CHANGED(@stack
        WindowHandle	 viewWindow,
        sdword		xOrigin,
        sdword		yOrigin);

This message notifies the content that the view's origin has changed. The content will set its VCNI_docOrigin field to the passed values.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: viewWindow The window handle of the GenView's window.

xOrigin
The new horizontal origin of the view.
yOrigin
The new vertical origin of the view.

Return: Nothing.

Interception: Any content that is managing large documents will probably need to subclass this message and apply the proper translations for the 32-bit coordinates.

MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED
void	MSG_META_CONTENT_VIEW_SCALE_FACTOR_CHANGED(@stack
        WindowHandle viewWindow,
        WWFixedAsDWord		yScaleFactor,
        WWFixedAsDWord		xScaleFactor);

This message notifies the content that the view window's scale factor has changed. The content will set its VCNI_scaleFactor field to the passed values.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: viewWindow The window handle of the GenView's window.

yScaleFactor
The new vertical scale factor.
xScaleFactor
The new horizontal scale factor.

Return: Nothing.

Interception: Any content that is managing large documents will probably need to subclass this message and apply the proper translations for the 32-bit coordinates.

MSG_META_CONTENT_VIEW_WIN_OPENED
void	MSG_META_CONTENT_VIEW_WIN_OPENED(
        word		viewWidth,
        word		viewHeight,
        WindowHandle		viewWindow);

This message notifies the content that the view's window has been created and is being put on the screen. This message will be followed by MSG_META_EXPOSED , so the content should not draw anything here.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: viewWidth The new window's initial width.

viewHeight
The new window's initial height.
viewWindow
The window handle of the GenView's window.

Return: Nothing.

Interception: A content may wish to subclass this message if it needs to initialize data before the view's window is actually on the screen.

MSG_META_CONTENT_VIEW_OPENING
void	MSG_META_CONTENT_VIEW_OPENING(
        optr	view);

This message notifies the content that the view window is being put on the screen. Although the window will usually be fully realized by the time the content handles this message, the content should not draw anything in this handler. Because the view and content are often in different threads, a context switch could have occurred and the window might not be fully realized. This message will be followed by a MSG_META_EXPOSED indicating that the visible tree can be drawn and that the window is fully opened.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: view The optr of the GenView.

Return: Nothing.

Interception: A content may wish to subclass this message to initialize data before the view window is fully opened.

MSG_META_CONTENT_VIEW_SIZE_CHANGED
void	MSG_META_CONTENT_VIEW_SIZE_CHANGED(
        word		viewWidth,
        word		viewHeight,
        WindowHandle		viewWindow);

This message is sent to the content whenever the view's size changes for any reason. The passed height and width will be stored in the content's VCNI_viewHeight and VCNI_viewWidth fields.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: viewWidth The new width of the view window.

viewHeight
The new height of the view window.
viewWindow
The window handle of the GenView's window.

Return: Nothing.

Interception: Any content that is managing large documents may need to subclass this message to apply translations for 32-bit coordinates.

MSG_META_CONTENT_VIEW_CLOSING
void	MSG_META_CONTENT_VIEW_CLOSING();

This message indicates to the content that the view window is being shut down. The content should remove the visible tree from the screen and should prepare itself for the window to be closed.

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Interception: A content may subclass this message if it wants to do additional things when the view is taken off the screen.

MSG_META_CONTENT_VIEW_WIN_CLOSED
void	MSG_META_CONTENT_VIEW_WIN_CLOSED(
        WindowHandle viewWindow);

This message indicates that the view's window has been shut down, taken off the screen, and destroyed. The content responds to this message by discarding the window handle stored in its VCNI_window field. The content should already have removed itself from the screen when it received an earlier MSG_VIS_CLOSE .

Source: Unrestricted--typically sent by a GenView to its content object.

Destination: Any VisContent or Process object acting as the content of a GenView.

Parameters: viewWindow The window handle of the GenView's window.

Return: Nothing.

Interception: A content may subclass this message to clean up after the view window is closed (e.g. if the content cached the view's window handle to a global variable, it will need to zero that handle now).

This document is a single-page version of a a multi-page document, suitable for easy printing.